Game.cpp 38 KB

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