Game.cpp 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159
  1. //
  2. // Urho3D Engine
  3. // Copyright (c) 2008-2011 Lasse Öörni
  4. //
  5. // Permission is hereby granted, free of charge, to any person obtaining a copy
  6. // of this software and associated documentation files (the "Software"), to deal
  7. // in the Software without restriction, including without limitation the rights
  8. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. // copies of the Software, and to permit persons to whom the Software is
  10. // furnished to do so, subject to the following conditions:
  11. //
  12. // The above copyright notice and this permission notice shall be included in
  13. // all copies or substantial portions of the Software.
  14. //
  15. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. // THE SOFTWARE.
  22. //
  23. #include "Precompiled.h"
  24. #include "AIController.h"
  25. #include "Audio.h"
  26. #include "BorderImage.h"
  27. #include "Camera.h"
  28. #include "Client.h"
  29. #include "CollisionShape.h"
  30. #include "Connection.h"
  31. #include "DebugHud.h"
  32. #include "Engine.h"
  33. #include "EngineEvents.h"
  34. #include "Exception.h"
  35. #include "File.h"
  36. #include "Font.h"
  37. #include "Game.h"
  38. #include "GameConfig.h"
  39. #include "GameEvents.h"
  40. #include "GameObjectFactory.h"
  41. #include "Input.h"
  42. #include "InputEvents.h"
  43. #include "Light.h"
  44. #include "Log.h"
  45. #include "Material.h"
  46. #include "MemoryBuffer.h"
  47. #include "Ninja.h"
  48. #include "PackageFile.h"
  49. #include "PositionalChannel.h"
  50. #include "Potion.h"
  51. #include "PhysicsEvents.h"
  52. #include "PhysicsWorld.h"
  53. #include "Pipeline.h"
  54. #include "ProcessUtils.h"
  55. #include "Profiler.h"
  56. #include "ProtocolEvents.h"
  57. #include "Renderer.h"
  58. #include "ResourceCache.h"
  59. #include "RigidBody.h"
  60. #include "Scene.h"
  61. #include "SceneEvents.h"
  62. #include "ScriptEngine.h"
  63. #include "ScriptFile.h"
  64. #include "Server.h"
  65. #include "Skybox.h"
  66. #include "SnowBall.h"
  67. #include "SnowCrate.h"
  68. #include "StaticModel.h"
  69. #include "StringUtils.h"
  70. #include "Text.h"
  71. #include "Texture2D.h"
  72. #include "UI.h"
  73. #include "UIEvents.h"
  74. #include "XM.h"
  75. #include "XMLFile.h"
  76. #include "Zone.h"
  77. #include <algorithm>
  78. #include "DebugNew.h"
  79. int simulatePacketLoss = 0;
  80. int simulateLatency = 0;
  81. std::string applicationDir;
  82. std::string downloadDir;
  83. Game::Game() :
  84. mTimeScale(1.0f),
  85. mCameraMinDist(0.0f),
  86. mCameraMaxDist(0.0f),
  87. mCameraSafetyDist(0.0f),
  88. mCameraRayLength(0.0f),
  89. mGameOn(false),
  90. mFirstFrame(false),
  91. mPaused(false),
  92. mClientEntityID(0)
  93. {
  94. std::string userDir = getUserDocumentsDirectory();
  95. applicationDir = userDir + "NinjaSnowWar";
  96. // Test the "allowed path" feature. Access outside the working directory, and these paths, should cause an exception
  97. registerDirectory("Data");
  98. registerDirectory(getSystemFontDirectory());
  99. registerDirectory(applicationDir);
  100. createDirectory(applicationDir);
  101. applicationDir = fixPath(applicationDir); // Add the slash at the end
  102. downloadDir = applicationDir + "Cache";
  103. createDirectory(downloadDir); // Create package download cache
  104. subscribeToEvent(EVENT_UPDATE, EVENT_HANDLER(Game, handleUpdate));
  105. subscribeToEvent(EVENT_POSTUPDATE, EVENT_HANDLER(Game, handlePostUpdate));
  106. subscribeToEvent(EVENT_PHYSICSPRESTEP, EVENT_HANDLER(Game, handlePreStep));
  107. subscribeToEvent(EVENT_DIE, EVENT_HANDLER(Game, handleGameEvent));
  108. subscribeToEvent(EVENT_REMOVE, EVENT_HANDLER(Game, handleGameEvent));
  109. subscribeToEvent(EVENT_CLIENTIDENTITY, EVENT_HANDLER(Game, handleClientIdentity));
  110. subscribeToEvent(EVENT_JOINEDSCENE, EVENT_HANDLER(Game, handleJoinedScene));
  111. subscribeToEvent(EVENT_CONTROLSUPDATE, EVENT_HANDLER(Game, handleControlsUpdate));
  112. subscribeToEvent(EVENT_CONTROLSPLAYBACK, EVENT_HANDLER(Game, handleControlsPlayback));
  113. subscribeToEvent(EVENT_CLIENTJOINEDSCENE, EVENT_HANDLER(Game, handleClientJoined));
  114. subscribeToEvent(EVENT_CLIENTLEFTSCENE, EVENT_HANDLER(Game, handleClientLeft));
  115. subscribeToEvent(EVENT_PLAYERSPAWNED, EVENT_HANDLER(Game, handlePlayerSpawned));
  116. registerRemoteEvent(EVENT_PLAYERSPAWNED);
  117. }
  118. Game::~Game()
  119. {
  120. }
  121. void Game::run()
  122. {
  123. init();
  124. while (!mEngine->isExiting())
  125. {
  126. mEngine->runFrame(mScene, mCamera, !mPaused);
  127. // Save/load/exit
  128. // Check these outside the frame update, so that the engine does not render a black screen
  129. // for one frame after loading a game (the old camera has been destroyed at that point)
  130. Input* input = mEngine->getInput();
  131. if (input->getKeyPress(KEY_F5))
  132. saveGame();
  133. if (input->getKeyPress(KEY_F7))
  134. loadGame();
  135. if (input->getKeyPress(KEY_ESC))
  136. mEngine->exit();
  137. }
  138. }
  139. void Game::init()
  140. {
  141. PROFILE(Game_Init);
  142. bool runServer = false;
  143. bool runClient = false;
  144. std::string address;
  145. std::string logName = "NinjaSnowWar.log";
  146. // Force forward rendering
  147. std::vector<std::string> arguments = getArguments();
  148. arguments.insert(arguments.begin(), "-forward");
  149. for (unsigned i = 0; i < arguments.size(); ++i)
  150. {
  151. if (toLower(arguments[i]) == "server")
  152. {
  153. logName = "Server.log";
  154. runServer = true;
  155. runClient = false;
  156. }
  157. else
  158. {
  159. if ((arguments[i][0] != '-') && (!runClient))
  160. {
  161. logName = "Client.log";
  162. runServer = false;
  163. runClient = true;
  164. address = arguments[i];
  165. if ((arguments.size() > i + 1) && (arguments[i + 1][0] != '-'))
  166. mUserName = arguments[i + 1];
  167. }
  168. }
  169. }
  170. // Initialize the engine. If running the server, use headless mode
  171. mEngine = new Engine("NinjaSnowWar", logName, runServer);
  172. // Add the resources as a package if available
  173. mCache = mEngine->getResourceCache();
  174. if (fileExists("Data.pak"))
  175. mCache->addPackageFile(new PackageFile("Data.pak"));
  176. else
  177. mCache->addResourcePath("Data");
  178. mEngine->init(arguments);
  179. mCache->addResourcePath(getSystemFontDirectory());
  180. DebugHud* debugHud = mEngine->createDebugHud();
  181. debugHud->setStyle(mCache->getResource<XMLFile>("UI/DefaultStyle.xml"));
  182. if (runServer)
  183. {
  184. mServer = mEngine->createServer();
  185. mServer->setNetFps(25); // Set FPS to match an even amount of physics updates
  186. }
  187. if (runClient)
  188. mClient = mEngine->createClient(downloadDir);
  189. // Setup sound. Play music in singleplayer only
  190. Audio* audio = mEngine->getAudio();
  191. audio->setMasterGain(CHANNEL_MASTER, 0.75f);
  192. audio->setMasterGain(CHANNEL_MUSIC, 0.75f);
  193. if ((!runServer) && (!runClient))
  194. {
  195. XM* song = mCache->getResource<XM>("Music/NinjaGods.xm");
  196. song->play();
  197. }
  198. setupOptions();
  199. createOverlays();
  200. createScene();
  201. // If running a server, add the scene to the server, and all packages to required packages
  202. if (mServer)
  203. {
  204. mServer->addScene(mScene);
  205. const std::vector<SharedPtr<PackageFile> >& packages = mCache->getPackageFiles();
  206. for (unsigned i = 0; i < packages.size(); ++i)
  207. mScene->addRequiredPackageFile(packages[i]);
  208. mServer->start(1234);
  209. }
  210. // If running a client, try connecting
  211. if (mClient)
  212. {
  213. mClient->setScene(mScene);
  214. mClient->connect(address, 1234, mUserName);
  215. }
  216. startGame();
  217. }
  218. void Game::setupOptions()
  219. {
  220. GameConfig::load("NinjaSnowWar.xml", mCache);
  221. mSensitivity = GameConfig::getReal("Controls/MouseSensitivity");
  222. setupCamera();
  223. Ninja::setup();
  224. SnowBall::setup();
  225. SnowCrate::setup();
  226. Potion::setup();
  227. }
  228. void Game::createOverlays()
  229. {
  230. UIElement* uiRoot = mEngine->getUIRoot();
  231. if (!uiRoot)
  232. return;
  233. Renderer* renderer = mEngine->getRenderer();
  234. mSight = new BorderImage();
  235. mSight->setTexture(mCache->getResource<Texture2D>("Textures/Sight.png"));
  236. mSight->setAlignment(HA_CENTER, VA_CENTER);
  237. int height = min(renderer->getHeight() / 22, 64);
  238. mSight->setSize(height, height);
  239. uiRoot->addChild(mSight);
  240. mScoreText = new Text();
  241. mScoreText->setFont(mCache->getResource<Font>("Fonts/BlueHighway.ttf"), 17);
  242. mScoreText->setAlignment(HA_LEFT, VA_TOP);
  243. mScoreText->setPosition(5, 5);
  244. mScoreText->setColor(C_BOTTOMLEFT, Color(1.0f, 1.0f, 0.25f));
  245. mScoreText->setColor(C_BOTTOMRIGHT, Color(1.0f, 1.0f, 0.25f));
  246. uiRoot->addChild(mScoreText);
  247. mHiScoreText = new Text();
  248. mHiScoreText->setFont(mCache->getResource<Font>("Fonts/BlueHighway.ttf"), 17);
  249. mHiScoreText->setAlignment(HA_RIGHT, VA_TOP);
  250. mHiScoreText->setPosition(-5, 5);
  251. mHiScoreText->setColor(C_BOTTOMLEFT, Color(1.0f, 1.0f, 0.25f));
  252. mHiScoreText->setColor(C_BOTTOMRIGHT, Color(1.0f, 1.0f, 0.25f));
  253. uiRoot->addChild(mHiScoreText);
  254. mMessage = new Text();
  255. mMessage->setFont(mCache->getResource<Font>("Fonts/BlueHighway.ttf"), 17);
  256. mMessage->setColor(Color(1.0f, 0.0f, 0.0f));
  257. mMessage->setAlignment(HA_CENTER, VA_CENTER);
  258. mMessage->setPosition(0, -height * 2);
  259. uiRoot->addChild(mMessage);
  260. mHealthBar = new BorderImage();
  261. mHealthBar->setTexture(mCache->getResource<Texture2D>("Textures/HealthBarBorder.png"));
  262. mHealthBar->setAlignment(HA_CENTER, VA_TOP);
  263. mHealthBar->setPosition(0, 8);
  264. mHealthBar->setSize(120, 20);
  265. SharedPtr<BorderImage> healthInside(new BorderImage());
  266. healthInside->setTexture(mCache->getResource<Texture2D>("Textures/HealthBarInside.png"));
  267. healthInside->setPosition(2, 2);
  268. healthInside->setSize(116, 16);
  269. mHealthBar->addChild(healthInside);
  270. uiRoot->addChild(mHealthBar);
  271. }
  272. void Game::createScene()
  273. {
  274. mScene = mEngine->createScene("NinjaSnowWar", BoundingBox(-100000.0f, 100000.f));
  275. mScene->addComponentFactory(new GameObjectFactory());
  276. // If not a multiplayer client, load the scene contents from file
  277. if (!mClient)
  278. {
  279. SharedPtr<File> sceneFile = mCache->getFile(GameConfig::get("Game/LevelName"));
  280. mScene->loadXML(*sceneFile);
  281. // Create the camera if not server
  282. if (!mServer)
  283. createCamera();
  284. }
  285. }
  286. void Game::createCamera()
  287. {
  288. // Note: camera is local, so it will not be affected by net replication
  289. Entity* cameraEntity = mScene->createEntity("Camera", true);
  290. mCamera = cameraEntity->createComponent<Camera>();
  291. Camera* camera = mCamera.getPtr();
  292. Renderer* renderer = mEngine->getRenderer();
  293. if (renderer)
  294. camera->setAspectRatio((float)renderer->getWidth() / (float)renderer->getHeight());
  295. // View distance is optional
  296. try
  297. {
  298. camera->setNearClip(GameConfig::getReal("Engine/ViewStart"));
  299. camera->setFarClip(GameConfig::getReal("Engine/ViewEnd"));
  300. }
  301. catch (...) {}
  302. }
  303. void Game::startGame()
  304. {
  305. // Do not start game on your own in client mode
  306. if (mClient)
  307. return;
  308. // Set gameon flag
  309. mGameOn = true;
  310. // Reset game parameters
  311. mEnemies = GameConfig::getInt("Game/Enemies");
  312. mPowerUps = GameConfig::getInt("Game/PowerUps");
  313. mEnemySpawnTime = GameConfig::getReal("Game/EnemySpawnRate");
  314. mPowerUpSpawnTime = GameConfig::getReal("Game/PowerUpSpawnRate");
  315. mIncrementCount = GameConfig::getInt("Game/IncrementEach");
  316. // Reset AI difficulty
  317. AIController::setup();
  318. // Clear all previous game objects
  319. std::map<EntityID, SharedPtr<Entity> > entities = mScene->getEntities();
  320. for (std::map<EntityID, SharedPtr<Entity> >::iterator i = entities.begin(); i != entities.end(); ++i)
  321. {
  322. if (i->second->getName().find("Obj") != std::string::npos)
  323. mScene->removeEntity(i->second->getID());
  324. }
  325. mPlayers.clear();
  326. // In singleplayer, create the lone player ninja & player structure, as well as a hiscore entry
  327. if (!mServer)
  328. {
  329. mFirstFrame = true;
  330. setMessage("");
  331. Entity* entity = mScene->createEntity("ObjPlayer");
  332. Ninja* ninja = entity->createComponent<Ninja>();
  333. ninja->setMaxHealth(GameConfig::getInt("Game/PlayerHealth"));
  334. ninja->create(GameConfig::getVector3("Game/PlayerStart"));
  335. ninja->setSide(SIDE_PLAYER);
  336. Player newPlayer;
  337. newPlayer.mScore = 0;
  338. newPlayer.mEntity = entity;
  339. mPlayers.push_back(newPlayer);
  340. HiScore newHiScore;
  341. newHiScore.mScore = 0;
  342. mHiScores.push_back(newHiScore);
  343. }
  344. }
  345. void Game::endGame()
  346. {
  347. mGameOn = false;
  348. setMessage("Press Fire or Jump to restart!");
  349. }
  350. void Game::loadGame()
  351. {
  352. // Do not load game in multiplayer mode
  353. if ((mClient) || (mServer))
  354. return;
  355. if (mPaused)
  356. return;
  357. if (!fileExists(applicationDir + "Save.dat"))
  358. return;
  359. // Load the scene
  360. File saveFile(applicationDir + "Save.dat", FILE_READ);
  361. mScene->load(saveFile);
  362. // Load global game state
  363. mPlayers.clear();
  364. Player newPlayer;
  365. newPlayer.mScore = saveFile.readInt();
  366. mGameOn = saveFile.readBool();
  367. mFirstFrame = saveFile.readBool();
  368. mMessage->setText(saveFile.readString());
  369. mEnemies = saveFile.readInt();
  370. mPowerUps = saveFile.readInt();
  371. mEnemySpawnTime = saveFile.readFloat();
  372. mPowerUpSpawnTime = saveFile.readFloat();
  373. mIncrementCount = saveFile.readInt();
  374. mControls.read(saveFile);
  375. AIController::load(saveFile);
  376. mPrevControls = mControls;
  377. // Reacquire the player entity if possible
  378. Entity* playerEntity = mScene->getEntity("ObjPlayer");
  379. newPlayer.mEntity = playerEntity;
  380. mPlayers.push_back(newPlayer);
  381. // Check singleplayer hiscore
  382. if (mPlayers[0].mScore > mHiScores[0].mScore)
  383. mHiScores[0].mScore = mPlayers[0].mScore;
  384. // Update the camera position
  385. updateCamera();
  386. }
  387. void Game::saveGame()
  388. {
  389. // Do not save game in multiplayer mode
  390. if ((mClient) || (mServer))
  391. return;
  392. if ((mPaused) || (!mGameOn))
  393. return;
  394. // Save the scene
  395. File saveFile(applicationDir + "Save.dat", FILE_WRITE);
  396. mScene->save(saveFile);
  397. // Save global game state
  398. saveFile.writeInt(mPlayers[0].mScore);
  399. saveFile.writeBool(mGameOn);
  400. saveFile.writeBool(mFirstFrame);
  401. saveFile.writeString(mMessage->getText());
  402. saveFile.writeInt(mEnemies);
  403. saveFile.writeInt(mPowerUps);
  404. saveFile.writeFloat(mEnemySpawnTime);
  405. saveFile.writeFloat(mPowerUpSpawnTime);
  406. saveFile.writeInt(mIncrementCount);
  407. mControls.write(saveFile);
  408. AIController::save(saveFile);
  409. setMessage("GAME SAVED");
  410. mSaveMessageTime = 0.0f;
  411. }
  412. void Game::handleUpdate(StringHash eventType, VariantMap& eventData)
  413. {
  414. using namespace Update;
  415. float timeStep = eventData[P_TIMESTEP].getFloat();
  416. // Read input
  417. getControls();
  418. }
  419. void Game::handlePostUpdate(StringHash eventType, VariantMap& eventData)
  420. {
  421. using namespace Update;
  422. float timeStep = eventData[P_TIMESTEP].getFloat();
  423. // Update camera to player position
  424. updateCamera();
  425. // Update status panel
  426. updateStatus(timeStep);
  427. }
  428. void Game::handlePreStep(StringHash eventType, VariantMap& eventData)
  429. {
  430. using namespace PhysicsPreStep;
  431. float timeStep = eventData[P_TIMESTEP].getFloat();
  432. // Create new objects
  433. spawnObjects(timeStep);
  434. // Singleplayer: set controls
  435. // (multiplayer client sets controls through the SetControls & ControlsPlayback events only)
  436. if ((!mServer) && (!mClient))
  437. {
  438. // Check new game start
  439. if (!mGameOn)
  440. {
  441. if ((mControls.isPressed(CTRL_FIRE, mPrevControls)) || (mControls.isPressed(CTRL_JUMP, mPrevControls)))
  442. startGame();
  443. }
  444. Entity* playerEntity = mPlayers[0].mEntity.getPtr();
  445. if (playerEntity)
  446. {
  447. Ninja* player = playerEntity->getComponent<Ninja>();
  448. if (player)
  449. {
  450. // First frame shoot/jump prevention hack
  451. if (mFirstFrame)
  452. {
  453. player->mPrevControls = mControls;
  454. mFirstFrame = false;
  455. }
  456. player->mControls = mControls;
  457. }
  458. }
  459. }
  460. mPrevControls = mControls;
  461. // Multiplayer server: set controls for clients and handle respawn
  462. if (mServer)
  463. {
  464. for (unsigned i = 0; i < mPlayers.size(); ++i)
  465. {
  466. Connection* connection = mPlayers[i].mConnection;
  467. if (mPlayers[i].mEntity.isExpired())
  468. {
  469. if (connection->getControls().isPressed(CTRL_FIRE, mPlayers[i].mPrevControls))
  470. {
  471. mPlayers[i].mScore = 0;
  472. mPlayers[i].mEntity = spawnRemotePlayer(connection, false);
  473. }
  474. }
  475. else
  476. {
  477. Entity* entity = mPlayers[i].mEntity.getPtr();
  478. Ninja* player = entity->getComponent<Ninja>();
  479. if (player)
  480. player->mControls = connection->getControls();
  481. }
  482. mPlayers[i].mPrevControls = connection->getControls();
  483. }
  484. }
  485. }
  486. void Game::handleGameEvent(StringHash eventType, VariantMap& eventData)
  487. {
  488. using namespace Die;
  489. GameObject* obj = static_cast<GameObject*>(eventData[P_OBJECT].getPtr());
  490. if (eventType == EVENT_DIE)
  491. {
  492. if (obj->getSide() != SIDE_PLAYER)
  493. {
  494. EntityID damageOrigin = obj->getLastDamageOrigin();
  495. Entity* entity = mScene->getEntity(damageOrigin);
  496. if (entity)
  497. {
  498. // Check who of the players gets points, if any
  499. for (unsigned i = 0; i < mPlayers.size(); ++i)
  500. {
  501. if (mPlayers[i].mEntity.getPtr() == entity)
  502. {
  503. mPlayers[i].mScore += obj->getPoints();
  504. // Check for updating hiscore, or adding new hiscore entry
  505. bool hiScoreFound = false;
  506. for (unsigned j = 0; j < mHiScores.size(); ++j)
  507. {
  508. if (mHiScores[j].mName == mPlayers[i].mName)
  509. {
  510. if (mPlayers[i].mScore > mHiScores[j].mScore)
  511. mHiScores[j].mScore = mPlayers[i].mScore;
  512. hiScoreFound = true;
  513. break;
  514. }
  515. }
  516. if (!hiScoreFound)
  517. {
  518. HiScore newHiScore;
  519. newHiScore.mName = mPlayers[i].mName;
  520. newHiScore.mScore = mPlayers[i].mScore;
  521. mHiScores.push_back(newHiScore);
  522. }
  523. std::sort(mHiScores.begin(), mHiScores.end(), compareHiScores);
  524. // Multiplayer: put score & hiscores to synced entity properties for replication
  525. if (mServer)
  526. {
  527. entity->setProperty(PROP_SCORE, Variant(mPlayers[i].mScore), true);
  528. VectorBuffer hiScoreBuffer;
  529. hiScoreBuffer.writeVLE(mHiScores.size());
  530. for (unsigned j = 0; j < mHiScores.size(); ++j)
  531. {
  532. hiScoreBuffer.writeString(mHiScores[j].mName);
  533. hiScoreBuffer.writeInt(mHiScores[j].mScore);
  534. }
  535. Entity* hiScoreEntity = mScene->getEntity("HiScores");
  536. if (!hiScoreEntity)
  537. hiScoreEntity = mScene->createEntity("HiScores");
  538. hiScoreEntity->setProperty(PROP_HISCORES, Variant(hiScoreBuffer.getBuffer()), true);
  539. }
  540. break;
  541. }
  542. }
  543. }
  544. if (obj->getType() == Ninja::getTypeStatic())
  545. makeHarder();
  546. }
  547. }
  548. // End of singleplayer game (note: multiplayer never ends)
  549. if (eventType == EVENT_REMOVE)
  550. {
  551. if ((!mClient) && (!mServer))
  552. {
  553. if (obj->getEntity() == mPlayers[0].mEntity.getPtr())
  554. endGame();
  555. }
  556. }
  557. }
  558. void Game::handleClientIdentity(StringHash eventType, VariantMap& eventData)
  559. {
  560. // When a client joins and tells identity, assign to the NinjaSnowWar scene
  561. using namespace ClientIdentity;
  562. Connection* connection = static_cast<Connection*>(eventData[P_CONNECTION].getPtr());
  563. mServer->setClientScene(connection, mScene);
  564. }
  565. void Game::handleJoinedScene(StringHash eventType, VariantMap& eventData)
  566. {
  567. // The multiplayer scene does not include camera, so create it now
  568. createCamera();
  569. }
  570. void Game::handleControlsUpdate(StringHash eventType, VariantMap& eventData)
  571. {
  572. // Set controls to be sent over network
  573. mClient->setControls(mControls);
  574. // Also set the controls immediately for predicted game logic
  575. Entity* playerEntity = mScene->getEntity(mClientEntityID);
  576. if (playerEntity)
  577. {
  578. Ninja* player = playerEntity->getComponent<Ninja>();
  579. RigidBody* body = playerEntity->getComponent<RigidBody>();
  580. if (player)
  581. player->mControls = mControls;
  582. // Set player position for entity relevancy calculations
  583. if (body)
  584. mClient->setPosition(body->getPosition());
  585. }
  586. }
  587. void Game::handleControlsPlayback(StringHash eventType, VariantMap& eventData)
  588. {
  589. using namespace ControlsPlayback;
  590. Entity* playerEntity = mScene->getEntity(mClientEntityID);
  591. if (playerEntity)
  592. {
  593. Ninja* player = playerEntity->getComponent<Ninja>();
  594. if (player)
  595. {
  596. player->mPrevControls = player->mControls;
  597. player->mControls.mButtons = eventData[P_BUTTONS].getInt();
  598. player->mControls.mYaw = eventData[P_YAW].getFloat();
  599. player->mControls.mPitch = eventData[P_PITCH].getFloat();
  600. }
  601. }
  602. }
  603. void Game::handleClientJoined(StringHash eventType, VariantMap& eventData)
  604. {
  605. if (!mServer)
  606. return;
  607. // If beginning with one player, reset the game
  608. if (mPlayers.empty())
  609. {
  610. LOGINFO("Resetting the game");
  611. startGame();
  612. }
  613. // Create ninja for new player and send back spawn event
  614. using namespace ClientJoinedScene;
  615. Connection* connection = static_cast<Connection*>(eventData[P_CONNECTION].getPtr());
  616. spawnRemotePlayer(connection, true);
  617. }
  618. void Game::handleClientLeft(StringHash eventType, VariantMap& eventData)
  619. {
  620. if (!mServer)
  621. return;
  622. // Remove player's ninja if exists
  623. using namespace ClientLeftScene;
  624. Connection* connection = static_cast<Connection*>(eventData[P_CONNECTION].getPtr());
  625. for (unsigned i = 0; i < mPlayers.size(); ++i)
  626. {
  627. if (mPlayers[i].mConnection == connection)
  628. {
  629. Entity* entity = mPlayers[i].mEntity.getPtr();
  630. if (entity)
  631. {
  632. // Perform seppuku (?)
  633. Ninja* ninja = entity->getComponent<Ninja>();
  634. if (ninja)
  635. ninja->setHealth(0);
  636. }
  637. mPlayers.erase(mPlayers.begin() + i);
  638. break;
  639. }
  640. }
  641. }
  642. void Game::handlePlayerSpawned(StringHash eventType, VariantMap& eventData)
  643. {
  644. if (!mClient)
  645. return;
  646. using namespace PlayerSpawned;
  647. mClientEntityID = eventData[P_ENTITYID].getInt();
  648. }
  649. void Game::getControls()
  650. {
  651. Input* input = mEngine->getInput();
  652. if (input->getKeyPress(KEY_F1))
  653. toggleDebugOverlay();
  654. if (input->getKeyPress(KEY_F2))
  655. toggleDebugGeometry();
  656. // Toggle edge filter (deferred only)
  657. if (input->getKeyPress('F'))
  658. {
  659. Pipeline* pipeline = mEngine->getPipeline();
  660. EdgeFilterParameters params = pipeline->getEdgeFilter();
  661. if (params.mMaxFilter > 0.0f)
  662. params.mMaxFilter = 0.0f;
  663. else
  664. params.mMaxFilter = 1.0f;
  665. pipeline->setEdgeFilter(params);
  666. }
  667. // Game control keys
  668. if ((input->getKeyPress('P')) || (input->getKeyPress(KEY_PAUSE)))
  669. togglePause();
  670. // Packet loss test
  671. if ((input->getKeyPress('X')) && (mClient) && (mClient->getServerConnection()))
  672. {
  673. static const float lossRatios[3] =
  674. {
  675. 0.0f, 0.33f, 0.66f
  676. };
  677. ++simulatePacketLoss;
  678. if (simulatePacketLoss > 2)
  679. simulatePacketLoss = 0;
  680. mClient->getServerConnection()->getPeer()->setSimulatedPacketLoss(lossRatios[simulatePacketLoss]);
  681. }
  682. // Latency test
  683. if ((input->getKeyPress('Z')) && (mClient) && (mClient->getServerConnection()))
  684. {
  685. static const unsigned latencyValues[3] =
  686. {
  687. 0, 100, 250
  688. };
  689. ++simulateLatency;
  690. if (simulateLatency > 2)
  691. simulateLatency = 0;
  692. mClient->getServerConnection()->getPeer()->setSimulatedLatency(latencyValues[simulateLatency]);
  693. }
  694. // Player controls
  695. if (!mServer)
  696. {
  697. mControls.set(CTRL_ALL, false);
  698. if ((!mFirstFrame) && (!mPaused))
  699. {
  700. if (input->getKeyDown('W')) mControls.set(CTRL_UP);
  701. if (input->getKeyDown('S')) mControls.set(CTRL_DOWN);
  702. if (input->getKeyDown('A')) mControls.set(CTRL_LEFT);
  703. if (input->getKeyDown('D')) mControls.set(CTRL_RIGHT);
  704. if (input->getKeyDown(KEY_CTRL)) mControls.set(CTRL_FIRE);
  705. if (input->getKeyDown(' ')) mControls.set(CTRL_JUMP);
  706. if (input->getMouseButtonDown(MOUSEB_LEFT)) mControls.set(CTRL_FIRE);
  707. if (input->getMouseButtonDown(MOUSEB_RIGHT)) mControls.set(CTRL_JUMP);
  708. mControls.mYaw += mSensitivity * (float)input->getMouseMoveX();
  709. mControls.mPitch += mSensitivity * (float)input->getMouseMoveY();
  710. mControls.mPitch = clamp(mControls.mPitch, -60.0f, 60.0f);
  711. }
  712. }
  713. }
  714. void Game::toggleDebugOverlay()
  715. {
  716. mEngine->getDebugHud()->toggleAll();
  717. }
  718. void Game::toggleDebugGeometry()
  719. {
  720. mEngine->setDebugDrawMode(mEngine->getDebugDrawMode() ^ DEBUGDRAW_PHYSICS);
  721. }
  722. void Game::togglePause()
  723. {
  724. // Can not pause in multiplayer
  725. if ((mClient) || (mServer))
  726. return;
  727. mPaused = !mPaused;
  728. if (mGameOn)
  729. {
  730. if (mPaused)
  731. setMessage("PAUSED");
  732. else
  733. setMessage("");
  734. }
  735. }
  736. void Game::makeHarder()
  737. {
  738. AIController::makeHarder();
  739. // After certain amount of kills, increase number of simultaneous enemies
  740. mIncrementCount--;
  741. if (mIncrementCount <= 0)
  742. {
  743. mIncrementCount = GameConfig::getInt("Game/IncrementEach");
  744. if (mEnemies < GameConfig::getInt("Game/MaxEnemies"))
  745. mEnemies++;
  746. }
  747. }
  748. void Game::spawnObjects(float timeStep)
  749. {
  750. // No spawning after game has ended, or if this is a multiplayer client
  751. if ((!mGameOn) || (mClient))
  752. return;
  753. mEnemySpawnTime -= timeStep;
  754. mPowerUpSpawnTime -= timeStep;
  755. // Time to spawn a new powerup?
  756. if (mPowerUpSpawnTime <= 0)
  757. {
  758. mPowerUpSpawnTime = GameConfig::getReal("Game/PowerUpSpawnRate");
  759. // Check for combined amount of crates & potions
  760. if ((getObjectCount(SnowCrate::getTypeStatic()) + getObjectCount(Potion::getTypeStatic())) < mPowerUps)
  761. {
  762. float maxOffset = GameConfig::getReal("Game/PowerUpSpawnOffset");
  763. float xOffset = random(maxOffset * 2.0f) - maxOffset;
  764. float zOffset = random(maxOffset * 2.0f) - maxOffset;
  765. Vector3 position(xOffset, GameConfig::getReal("Game/PowerUpSpawnHeight"), zOffset);
  766. Entity* entity = mScene->createEntity("ObjCrate");
  767. GameObject* obj = entity->createComponent<SnowCrate>();
  768. obj->create(position);
  769. }
  770. }
  771. // Time to spawn a new enemy ninja?
  772. if (mEnemySpawnTime <= 0)
  773. {
  774. mEnemySpawnTime = GameConfig::getReal("Game/EnemySpawnRate");
  775. if (getObjectCount(Ninja::getTypeStatic(), SIDE_ENEMY) < mEnemies)
  776. {
  777. float maxOffset = GameConfig::getReal("Game/EnemySpawnOffset");
  778. float offset = random(maxOffset * 2.0f) - maxOffset;
  779. // Random north/east/south/west direction
  780. int dir = rand() & 3;
  781. dir *= 90;
  782. Quaternion q((float)dir, Vector3::sUp);
  783. Vector3 position(q * (GameConfig::getVector3("Game/EnemySpawnPosition") + Vector3(offset, 0, 0)));
  784. Entity* entity = mScene->createEntity("ObjEnemy");
  785. GameObject* obj = entity->createComponent<Ninja>();
  786. obj->create(position, q);
  787. obj->setSide(SIDE_ENEMY);
  788. obj->getBody()->setLinearVelocity(q * GameConfig::getVector3("Game/EnemySpawnVelocity"));
  789. }
  790. }
  791. }
  792. Entity* Game::spawnRemotePlayer(Connection* connection, bool newPlayer)
  793. {
  794. Entity* entity = mScene->createEntity("ObjRemotePlayer");
  795. entity->setOwner(connection);
  796. Ninja* ninja = entity->createComponent<Ninja>();
  797. ninja->setMaxHealth(GameConfig::getInt("Game/PlayerHealth"));
  798. Vector3 startPos = GameConfig::getVector3("Game/PlayerStart");
  799. startPos.mX += random(1000.0f) - 500.0f;
  800. startPos.mZ += random(1000.0f) - 500.0f;
  801. ninja->create(startPos);
  802. ninja->setSide(SIDE_PLAYER);
  803. if (newPlayer)
  804. {
  805. Player newPlayer;
  806. newPlayer.mScore = 0;
  807. newPlayer.mEntity = entity;
  808. newPlayer.mConnection = connection;
  809. newPlayer.mName = connection->getUserName();
  810. mPlayers.push_back(newPlayer);
  811. }
  812. else
  813. {
  814. // Respawn: set the firebutton held down in the controls to prevent first frame shoot
  815. ninja->mControls.mButtons = CTRL_FIRE;
  816. ninja->mPrevControls.mButtons = CTRL_FIRE;
  817. }
  818. using namespace PlayerSpawned;
  819. VariantMap eventData;
  820. eventData[P_ENTITYID] = entity->getID();
  821. mScene->sendRemoteEvent(EVENT_PLAYERSPAWNED, eventData, connection);
  822. return entity;
  823. }
  824. void Game::setupCamera()
  825. {
  826. GameConfig::setSection("Camera");
  827. mCameraMinDist = GameConfig::getReal("MinDistance");
  828. mCameraMaxDist = GameConfig::getReal("MaxDistance");
  829. mCameraSafetyDist = GameConfig::getReal("SafetyDistance");
  830. mCameraRayLength = GameConfig::getReal("RayLength");
  831. GameConfig::setSection();
  832. }
  833. void Game::setMessage(const std::string& text)
  834. {
  835. if (mMessage)
  836. mMessage->setText(text);
  837. }
  838. void Game::updateStatus(float timeStep)
  839. {
  840. if ((!mScene) || (!mScoreText) || (!mHiScoreText) || (!mHealthBar))
  841. return;
  842. // Singleplayer status update
  843. if ((!mClient) && (!mServer))
  844. {
  845. mScoreText->setText("Score " + toString(mPlayers[0].mScore));
  846. if (mHiScores.size())
  847. mHiScoreText->setText("Hiscore " + toString(mHiScores[0].mScore));
  848. Entity* playerEntity = mPlayers[0].mEntity.getPtr();
  849. if (playerEntity)
  850. {
  851. GameObject* player = playerEntity->getDerivedComponent<GameObject>();
  852. if (player)
  853. mHealthBar->getChild(0)->setWidth((116 * player->getHealth()) / player->getMaxHealth());
  854. }
  855. }
  856. else
  857. {
  858. if (mClient)
  859. {
  860. // Get score/health/maxhealth from the player entity, if it exists
  861. Entity* playerEntity = mScene->getEntity(mClientEntityID);
  862. if (playerEntity)
  863. {
  864. int clientScore = playerEntity->getProperty(PROP_SCORE).getInt();
  865. mScoreText->setText("Score " + toString(clientScore));
  866. GameObject* player = playerEntity->getDerivedComponent<GameObject>();
  867. if (player)
  868. mHealthBar->getChild(0)->setWidth((116 * player->getHealth()) / player->getMaxHealth());
  869. }
  870. // Get the hiscores from the hiscores entity, if it exists
  871. //! \todo It is wasteful to do this each frame even if the hiscores have not changed
  872. Entity* hiScoresEntity = mScene->getEntity("HiScores");
  873. if ((hiScoresEntity) && (hiScoresEntity->hasProperty(PROP_HISCORES)))
  874. {
  875. std::string text;
  876. const std::vector<unsigned char>& hiScoreVector = hiScoresEntity->getProperty(PROP_HISCORES).getBuffer();
  877. if (hiScoreVector.size())
  878. {
  879. MemoryBuffer hiScoreBuffer(&hiScoreVector[0], hiScoreVector.size());
  880. unsigned numHiScores = hiScoreBuffer.readVLE();
  881. mHiScores.resize(numHiScores);
  882. for (unsigned i = 0; i < numHiScores; ++i)
  883. {
  884. mHiScores[i].mName = hiScoreBuffer.readString();
  885. mHiScores[i].mScore = hiScoreBuffer.readInt();
  886. text += mHiScores[i].mName + " " + toString(mHiScores[i].mScore) + "\n";
  887. }
  888. mHiScoreText->setText(text);
  889. }
  890. }
  891. static float fileTransferAcc = 0.0f;
  892. fileTransferAcc += timeStep;
  893. if (fileTransferAcc > 0.1f)
  894. {
  895. fileTransferAcc -= 0.1f;
  896. mMessage->setText(mClient->getFileTransferStatus());
  897. }
  898. }
  899. }
  900. // If the save message is being displayed, remove it after a set time
  901. if (mMessage->getText() == "GAME SAVED")
  902. {
  903. mSaveMessageTime += timeStep;
  904. if (mSaveMessageTime > 1.0f)
  905. setMessage("");
  906. }
  907. }
  908. void Game::updateCamera()
  909. {
  910. if (!mScene)
  911. return;
  912. if (mServer)
  913. return;
  914. Camera* camera = 0;
  915. if (mCamera.isExpired())
  916. {
  917. // Reacquire the camera pointer if necessary
  918. Entity* cameraEntity = mScene->getEntity("Camera");
  919. if (cameraEntity)
  920. {
  921. mCamera = cameraEntity->getComponent<Camera>();
  922. camera = mCamera.getPtr();
  923. if (camera)
  924. {
  925. // Reset aspect ratio, might have changed
  926. Renderer* renderer = mEngine->getRenderer();
  927. if (renderer)
  928. camera->setAspectRatio((float)renderer->getWidth() / (float)renderer->getHeight());
  929. }
  930. }
  931. }
  932. else
  933. camera = mCamera.getPtr();
  934. if (!camera)
  935. return;
  936. // Player tracking
  937. Entity* playerEntity = 0;
  938. if ((!mClient) && (!mServer))
  939. playerEntity = mPlayers[0].mEntity.getPtr();
  940. else
  941. playerEntity = mScene->getEntity(mClientEntityID);
  942. if (!playerEntity)
  943. return;
  944. RigidBody* body = playerEntity->getComponent<RigidBody>();
  945. Ninja* player = playerEntity->getComponent<Ninja>();
  946. if ((!body) || (!player))
  947. return;
  948. Vector3 pos = body->getWorldPosition();
  949. // Use yaw & pitch from own controls for immediate response
  950. Quaternion dir = Quaternion::sIdentity;
  951. dir = dir * Quaternion(mControls.mYaw, Vector3::sUp);
  952. dir = dir * Quaternion(mControls.mPitch, Vector3::sRight);
  953. // Force the player character rotation, so that there is no stuttering
  954. if (mClient)
  955. {
  956. bool alive = player->getHealth() > 0;
  957. if (alive)
  958. {
  959. Quaternion rot(mControls.mYaw, Vector3::sUp);
  960. body->setRotation(rot);
  961. body->Node::setRotation(rot); // This disables client-side rotation smoothing
  962. }
  963. }
  964. Vector3 aimPoint = pos + Vector3(0,100,0);
  965. Vector3 minDist = aimPoint + dir * Vector3(0, 0, -mCameraMinDist);
  966. Vector3 maxDist = aimPoint + dir * Vector3(0, 0, -mCameraMaxDist);
  967. // Collide camera ray with static objects
  968. Vector3 rayDir = (maxDist - minDist).getNormalized();
  969. float rayDistance = mCameraMaxDist - mCameraMinDist + mCameraSafetyDist;
  970. std::vector<PhysicsRaycastResult> result;
  971. mScene->getExtension<PhysicsWorld>()->raycast(Ray(minDist, rayDir), result, rayDistance, 2);
  972. if (result.size())
  973. rayDistance = min(rayDistance, result[0].mDistance - mCameraSafetyDist);
  974. camera->setPosition(minDist + rayDir * rayDistance);
  975. camera->setRotation(dir);
  976. Audio* audio = mEngine->getAudio();
  977. if (audio)
  978. {
  979. audio->setListenerPosition(pos);
  980. audio->setListenerRotation(dir);
  981. }
  982. }
  983. int Game::getObjectCount(ShortStringHash type, int side)
  984. {
  985. int count = 0;
  986. const std::map<EntityID, SharedPtr<Entity> >& entities = mScene->getEntities();
  987. for (std::map<EntityID, SharedPtr<Entity> >::const_iterator i = entities.begin(); i != entities.end(); ++i)
  988. {
  989. GameObject* object = i->second->getDerivedComponent<GameObject>();
  990. if ((object) && (object->getType() == type) && ((side == SIDE_UNDEFINED) || (object->getSide() == side)))
  991. ++count;
  992. }
  993. return count;
  994. }