Game.cpp 38 KB

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