Advertisement
PirateHearts

Super Win the Game main player class

Mar 5th, 2018
692
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 138.06 KB | None | 0 0
  1. #include "Player.h"
  2. #include "ValkyrieSprite.h"
  3. #include "SpriteMaterial.h"
  4. #include "PlayerSprite.h"
  5. #include "NBaseGame.h"
  6. #include "ValkyrieGame.h"
  7. #include "NControlConfig.h"
  8. #include "NBasingComponent.h"
  9. #include "NFacingComponent.h"
  10. #include "Transition.h"
  11. #include "DoorTransition.h"
  12. #include "NSerialComponent.h"
  13. #include "NNameComponent.h"
  14. #include "DoorComponent.h"
  15. #include "NHealthComponent.h"
  16. #include "NPusherComponent.h"
  17. #include "AnaglyphComponent.h"
  18. #include "DemoIdleMenu.h"
  19. #include "SpeedrunComponent.h"
  20. #include "CoffeeComponent.h"
  21. #include "MinimapComponent.h"
  22. #include "TeleportComponent.h"
  23.  
  24. #ifndef _PLAT_WIN
  25. #include <ctime>
  26. #endif
  27.  
  28. static const float BGMUSIC_SWITCH_DELAY = 0.35f;
  29.  
  30. static const int THUMB_SIZE = 128;
  31.  
  32. static const float DEMO_TIMEOUT = 45.0f;
  33. static const float DEMO_TIMEOUT_PAUSED = 15.0f;
  34.  
  35. static const float OVERWORLD_DELAY = 0.2f;
  36.  
  37. static const float WALL_GRIP_TIMER = 0.15f;
  38.  
  39. // For some reason, Windows NFML builds choke on this. (Constant conditional expression warning.)
  40. static /*const*/ bool OVERWORLD_MOTION = false;//true;
  41. static /*const*/ bool SWIMMING_MOTION = false;
  42. static /*const*/ bool ICY_MOTION = false;
  43. static const float OVERWORLD_TILE_SIZE = float(TILE_SIZE) * 2.0f;
  44.  
  45. static const float TELEPORT_ENTRY_DURATION = 0.3f;
  46.  
  47. static const int CURRENT_SAVE_FILE_VERSION = 3;
  48. // Version 2 removes Volver cruft. A lot of this will be replaced with the more generic serial component "resource" concept of a save game.
  49. // Version 3 adds respawn map.
  50.  
  51. static const float ASPECT_RATIO_FIXUP_X = (256.0f / 320.0f);
  52. static const float ASPECT_RATIO_FIXUP_Y = (224.0f / 200.0f);
  53. static const float SIZE_FIXUP_Y = 1.38f;// arbitrarily chosen to round well for jumps. (clears 6 and 10 cleanly) (18.0f / 14.0f); // bottom of foot to top of hat, ignoring border pixels
  54. //static const float SIZE_FIXUP_X = 1.0f;//SIZE_FIXUP_Y; // because it doesn't feel right changing the arc of jumps
  55. //static const float JUMP_TIME_FIXUP = SIZE_FIXUP_Y; // Cleaner way to do the same
  56. // ...or do a little bit of each.
  57. static const float SIZE_FIXUP_X = 1.15f;
  58. static const float JUMP_TIME_FIXUP = SIZE_FIXUP_Y / SIZE_FIXUP_X;
  59.  
  60. static const int NUM_JUMPS = 2;
  61. static const float MOVEMENT_SPEED = (12.0f * 8.0f) * ASPECT_RATIO_FIXUP_X * SIZE_FIXUP_X;
  62. static const float SWIMMING_MOVEMENT_SPEED = MOVEMENT_SPEED * 0.5f;
  63.  
  64. static const float JUMP_TIME = 0.325f * JUMP_TIME_FIXUP;
  65.  
  66. static const uint HAS_PATH_TOP = 0x01;
  67. static const uint HAS_PATH_BOTTOM = 0x02;
  68. static const uint HAS_PATH_LEFT = 0x04;
  69. static const uint HAS_PATH_RIGHT = 0x08;
  70.  
  71. void Player::ClearStatsForNewGame()
  72. {
  73. ClearAllData();
  74.  
  75. //bCaptureThumbnail = false;
  76.  
  77. bFacingLeft = false;
  78. WalkingFrame = 0.0f;
  79. LandedTime = 0.0f;
  80. DeadedTime = 0.0f;
  81. IdleTime = 0.0f;
  82. DemoIdleTime = 0.0f;
  83. OverworldTime = 0.0f;
  84.  
  85. bLookingDown = false;
  86. DownJumpTime = 0.0f;
  87.  
  88. OverworldFacing = NVec2(0,1);
  89. PrevOverworldFacing = OverworldFacing;
  90. OverworldState = OMS_Idle;
  91. OverWalk_Duration = 0.25f; // Based on Zelda 2 rates.
  92. OverWalk_Elapsed = 0.0f;
  93.  
  94. TeleportEntry_Duration = TELEPORT_ENTRY_DURATION;
  95. TeleportEntry_Remaining = -1.0f;
  96. TeleportEntry_Direction = NVec2();
  97.  
  98. SetUpPhysComp();
  99.  
  100. NDialogConsumerComponent* Dialog = GetComponent<NDialogConsumerComponent>();
  101. if (Dialog)
  102. {
  103. Dialog->CloseDialog();
  104. }
  105.  
  106. SpeedrunComponent* Speed = GetComponent<SpeedrunComponent>();
  107. if (Speed)
  108. {
  109. Speed->DebugReset();
  110. }
  111.  
  112. CurrentBGMusicName = "";
  113.  
  114. bCurrentSafeSpace = false;
  115.  
  116. bLineDefUnderwater = false;
  117. bLineDefToxic = false;
  118. bRegionUnderwater = false;
  119. bRegionToxic = false;
  120.  
  121. // I Guess?
  122. bVulnerable = false;
  123.  
  124. BGMusicSwitchDelay = -1.0f;
  125. //BGMusicSwitchName = "";
  126.  
  127. WallGripTimer = WALL_GRIP_TIMER;
  128. }
  129.  
  130. //void Player::ClearStatsForLostGame()
  131. //{
  132. // //bHasDoubleJump = false;
  133. // //bHasWallJump = false;
  134. //
  135. // ClearAllData();
  136. //
  137. // SetUpPhysComp();
  138. //}
  139. //
  140. //void Player::ClearStatsForWonGame()
  141. //{
  142. //}
  143.  
  144. Player::Player()
  145. {
  146. // Hey, tickable priority is a thing!
  147. // Lower value means it ticks first.
  148. // Ticking either before or after a parent base (assumed priority ~0) ensures correct camera behavior.
  149. // For the sake of whatever, I'll say the player ticks last, after everything else has moved.
  150. Priority = 100;
  151.  
  152. bEnteredCode = false;
  153.  
  154. NextBubbleTime = -1.0f;
  155.  
  156. // I Guess?
  157. bVulnerable = false;
  158.  
  159. //ThumbImgFull.init(CRT_WIDTH,CRT_HEIGHT,3);
  160. //ThumbImgSm.init(THUMB_SIZE,THUMB_SIZE,3);
  161. //bCaptureThumbnail = false;
  162.  
  163. AddComponent<NDataComponent>();
  164. GetComponent<NDataComponent>()->SetWriteCallback(OnDataWritten);
  165.  
  166. AddComponent<SpeedrunComponent>();
  167. AddComponent<CoffeeComponent>();
  168. AddComponent<MinimapComponent>();
  169.  
  170. Sprite = AddComponent<NRenderableComponent>();
  171. Space = AddComponent<NSpatialComponent>();
  172.  
  173. PhysComp = AddComponent<NPhysicsComponent>();
  174.  
  175. Camera = AddComponent<NCameraComponent>();
  176. Collision = AddComponent<NCollisionComponent>();
  177. Base = AddComponent<NBasingComponent>();
  178. Base->bCanBeBasingChild = true;
  179.  
  180. Base->SetOnBaseAboutToMoveCallback(OnBaseAboutToMove);
  181. Base->SetOnBaseMovedCallback(OnBaseMoved);
  182.  
  183. Controller = AddComponent<NPlayerControllerComponent>();
  184.  
  185. NRenderableInst* Instance = new PlayerSprite();
  186. Instance->SetTemplate(NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->SpriteTemplate);
  187. Instance->SetMaterial(NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->SpriteMat);
  188. Instance->SetDeferredSPI(this);
  189. NRenderer::GetInstance()->AddRenderableToRenderGroup(Instance, NHashedString("World Sprites Group"));
  190.  
  191. Camera->Camera = NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->GameCamera;
  192. Sprite->AddRenderable(Instance);
  193.  
  194. POINT DefRoom;
  195. DefRoom.x = 0;
  196. DefRoom.y = 0;
  197.  
  198. SetCheckpointPosition(NVec2((float)HALF_CRT_WIDTH, (float)HALF_CRT_HEIGHT), false);
  199. SetCheckpointRoom(DefRoom, false);
  200. SetCheckpointMap(gNullStr, false);
  201. SetCheckpointCampaign(gNullStr, false);
  202. SetCheckpointPosition(NVec2((float)HALF_CRT_WIDTH, (float)HALF_CRT_HEIGHT), true);
  203. SetCheckpointRoom(DefRoom, true);
  204. SetCheckpointMap(gNullStr, true);
  205. SetCheckpointCampaign(gNullStr, true);
  206.  
  207. Space->Game_Curr.Position = GetCheckpointPosition(true);
  208. Space->Game_Curr.Position.z = 0.0f; // Just to be safe.
  209. Space->Init();
  210. Space->SetCollisionCallback(OnCollision);
  211.  
  212. //NPhysicsGlobals::SetSlideAlphaHorizontal(0.0f);
  213. //NPhysicsGlobals::SetSlideAlphaVertical(1.0f); // Slide vertically in order to preserve landing velocity. Proooobably not a very good solution! I guess this came from Vanguard?
  214.  
  215. PhysComp->SetJumpCallback(OnJump);
  216. PhysComp->SetLandCallback(OnLand);
  217. PhysComp->SetUngroundCallback(OnUnground);
  218. PhysComp->SetWallHugCallback(OnWallHug);
  219. PhysComp->SetCanWallHugCallback(CanWallHug);
  220.  
  221.  
  222.  
  223. NCollisionBox* Bounds = new NCollisionBox();
  224. //Bounds->Extents = NVec3(4.0f, 6.0f, 1.0f);
  225. Bounds->SetController(Space);
  226. Bounds->SetBlocksCallback(BlocksOther);
  227. Bounds->SetRespondsCallback(PingsOther);
  228. Bounds->SetCollisionFilterType(CFT_Player);
  229. Collision->AddPrimitive(Bounds);
  230.  
  231. Collision->SetSweepFilter(CFT_All & ~(CFT_NonPlayerBlocker | CFT_BulletTrap | CFT_NonQuadBlocker));
  232.  
  233.  
  234.  
  235. bFacingLeft = false;
  236. WalkingFrame = 0.0f;
  237. LandedTime = 0.0f;
  238. DeadedTime = 0.0f;
  239.  
  240. OverworldFacing = NVec2(0,1);
  241. PrevOverworldFacing = OverworldFacing;
  242. OverworldState = OMS_Idle;
  243. OverWalk_Duration = 0.25f; // Based on Zelda 2 rates.
  244. OverWalk_Elapsed = 0.0f;
  245.  
  246. TeleportEntry_Duration = TELEPORT_ENTRY_DURATION;
  247. TeleportEntry_Remaining = -1.0f;
  248. TeleportEntry_Direction = NVec2();
  249.  
  250. bLookingDown = false;
  251. DownJumpTime = 0.0f;
  252.  
  253.  
  254.  
  255. LastCheckpointTime = -1.0f;
  256.  
  257.  
  258.  
  259. //Deaths = 0;
  260. //bHasDoubleJump = false;
  261. //bHasWallJump = false;
  262. //PlayTime = 0.0f;
  263.  
  264. ClearAllData();
  265.  
  266.  
  267.  
  268. SetUpPhysComp();
  269.  
  270. //InitCamera();
  271.  
  272. //////////////////////////////////////////////////////////////////////////
  273. // Done at new game or load game time.
  274. //NSerialResource::GetInstance()->LoadFromDisk("saevgaem0001.nsd");
  275. //////////////////////////////////////////////////////////////////////////
  276.  
  277. //////////////////////////////////////////////////////////////////////////
  278. // Done at entity creation time.
  279. //NSerialComponent* Serial = AddComponent<NSerialComponent>();
  280. //Serial->SetNotifyCallback(TEST_DeltaChanged);
  281. //Serial->SetResource(NSerialResource::GetInstance());
  282. //Serial->SetGUID(RESERVED_GUID | 0x00000001); // TODO: Define reserved GUIDs somewhere common. Most entity instances will already have a GUID, but things like the player or other one-off game concepts will need their own.
  283. //Serial->LoadFromResource();
  284. //Serial->SetDelta(NHashedString("alive"), NString("false"));
  285. //Serial->SaveToResource(); // redundant, done on deletion
  286. //////////////////////////////////////////////////////////////////////////
  287.  
  288. //////////////////////////////////////////////////////////////////////////
  289. // Done at entity deletion time.
  290. //RemoveComponent<NSerialComponent>();
  291. //////////////////////////////////////////////////////////////////////////
  292.  
  293. //////////////////////////////////////////////////////////////////////////
  294. // Done at save game time.
  295. //NSerialResource::GetInstance()->SaveToDisk("saevgaem0001.nsd");
  296. //////////////////////////////////////////////////////////////////////////
  297.  
  298. // More better actual path?
  299. AddComponent<NGUIDComponent>();
  300. GetComponent<NGUIDComponent>()->SetGUID(GUID_Player);
  301.  
  302.  
  303.  
  304. AddComponent<NScriptComponent>();
  305.  
  306.  
  307.  
  308. AddComponent<NDialogConsumerComponent>();
  309. GetComponent<NDialogConsumerComponent>()->SetClosedCallback(OnDialogClosed);
  310.  
  311. // Heyo! This will be the status screen, probably.
  312. AddComponent<NDialogProviderComponent>();
  313. StatusProviderFile.LoadCooked("Status.ndd");
  314. StatusProviderFile_Speedrun.LoadCooked("StatusSpeedrun.ndd");
  315. StatusProviderFile_FullMap.LoadCooked("StatusFullMap.ndd");
  316. MAKESTRING(DialogProvider);
  317. //GetComponent<NDialogProviderComponent>()->ApplyDefinitionData(StatusProviderFile.Contents.GetChildByName(DialogProvider));
  318.  
  319. AddComponent<NCheckpointComponent>();
  320.  
  321.  
  322.  
  323. AddComponent<NFrobComponent>();
  324. //GetComponent<NFrobComponent>()->SetCanFrobCallback(CanFrob);
  325. GetComponent<NFrobComponent>()->TraceDistance = 20.0f;
  326. GetComponent<NFrobComponent>()->PrimitiveName = NHashedString("FrobSweep");
  327. GetComponent<NFrobComponent>()->SetFilter(CFT_Frobbable);
  328.  
  329.  
  330.  
  331. AddComponent<DoorComponent>();
  332. //GetComponent<DoorComponent>()->SetCanFrobCallback(CanFrob);
  333. GetComponent<DoorComponent>()->PrimitiveName = NHashedString("FrobSweep");
  334. GetComponent<DoorComponent>()->SetFilter(CFT_Door);
  335.  
  336.  
  337.  
  338. NCollisionPoint* FrobBounds = new NCollisionPoint();
  339. GetComponent<NCollisionComponent>()->AddPrimitive(FrobBounds, NHashedString("FrobSweep"));
  340. FrobBounds->SetController(Space); // Must have a controller so it can early out of intersection tests against the player's collision box.
  341. FrobBounds->SetCollisionFilterType(CFT_None);
  342. //NCollisionBox* FrobBounds = new NCollisionBox();
  343. //FrobBounds->Extents = NVec3(5.0f, 8.0f, 1.0f);
  344. //GetComponent<NCollisionComponent>()->AddPrimitive(FrobBounds, NHashedString("FrobSweep"));
  345.  
  346.  
  347.  
  348. AddComponent<NFacingComponent>();
  349. GetComponent<NFacingComponent>()->SetFacing(FD_Right);
  350.  
  351.  
  352.  
  353. AddComponent<NHealthComponent>();
  354. GetComponent<NHealthComponent>()->SetHealthDamagedCallback(OnHealthDamaged);
  355.  
  356.  
  357.  
  358. AddComponent<NPusherComponent>();
  359. GetComponent<NPusherComponent>()->SetCanBePushed(true);
  360. //GetComponent<NPusherComponent>()->SetCanPush(true); // Nah. As much fun as pushing blocks could be, it creates problems.
  361.  
  362.  
  363.  
  364. AddComponent<AnaglyphComponent>();
  365. GetComponent<AnaglyphComponent>()->SetDepth(0.0f);
  366.  
  367.  
  368.  
  369. TeleportComponent* SpeedrunTele = AddComponent<TeleportComponent>();
  370. {
  371. SpeedrunTele->AddDestination(NHashedString("SpeedrunHall"), NHashedString("Town Interiors"), 13, 0, NHashedString("FromSpeedrun"), false, true);
  372.  
  373. SpeedrunTele->AddDestination(NHashedString("OldArcadia"), NHashedString("Speedrun Maps"), 0, 0, NHashedString("SpeedrunIntro"), false, true);
  374. SpeedrunTele->AddDestination(NHashedString("Bonehoard"), NHashedString("Speedrun Maps"), -1, 6, NHashedString("BonehoardIntro"), false, true);
  375. SpeedrunTele->AddDestination(NHashedString("Dreamscape"), NHashedString("Speedrun Maps"), 7, -6, NHashedString("DreamEntry"), false, true);
  376. SpeedrunTele->AddDestination(NHashedString("LegacyCaves"), NHashedString("Speedrun Maps"), 12, 6, NHashedString("LegacyCavesIntro"), false, true);
  377. SpeedrunTele->AddDestination(NHashedString("Gallery"), NHashedString("Speedrun Maps"), 14, 3, NHashedString("GalleryIntro"), false, true);
  378. }
  379.  
  380.  
  381.  
  382. // Also sets swimming props if necessary.
  383. SetOverworldProps();
  384.  
  385.  
  386.  
  387. bExtendedCamera = false;
  388.  
  389.  
  390.  
  391. OverworldDelay = -1.0f;
  392.  
  393. bAllowRejump = false;
  394.  
  395.  
  396.  
  397. BGMusicSwitchDelay = -1.0f;
  398. //BGMusicSwitchName = "";
  399. }
  400.  
  401. void Player::TEST_DeltaChanged(NEntity* OwnerEntity, const NHashedString& Key, const NString& Value)
  402. {
  403. OwnerEntity, Key, Value;
  404. int Foo = 0;
  405. Foo++;
  406. }
  407.  
  408. Player::~Player()
  409. {
  410. //NRenderableInterface* Instance = Sprite->GetRenderableByName();
  411. //NRenderer::GetInstance()->RemoveRenderableFromRenderGroup(Instance, NHashedString("World Sprites Group"));
  412. //SafeDelete(Instance);
  413. Sprite->DeleteRenderables();
  414.  
  415. Collision->DeletePrimitives();
  416. }
  417.  
  418.  
  419. void Player::Tick_CalcUpdate(float DeltaTime, ETickGroup TickGroup)
  420. {
  421. DeltaTime;
  422. TickGroup;
  423.  
  424. MAKESTRING(Gameplay);
  425. MAKESTRING(Overworld);
  426.  
  427. MAKESTRING(MoveRight);
  428. MAKESTRING(MoveLeft);
  429. MAKESTRING(Jump);
  430. MAKESTRING(JumpDown);
  431. MAKESTRING(Interact);
  432. MAKESTRING(Enter);
  433. MAKESTRING(Status);
  434. MAKESTRING(FullMap);
  435.  
  436. MAKESTRING(MoveNorth);
  437. MAKESTRING(MoveSouth);
  438. MAKESTRING(MoveEast);
  439. MAKESTRING(MoveWest);
  440.  
  441. MAKESTRING(Menus);
  442. MAKESTRING(Accept);
  443. MAKESTRING(Click);
  444. MAKESTRING(Cancel);
  445.  
  446. MAKESTRING(NavUp);
  447. MAKESTRING(NavDown);
  448.  
  449. if (TickGroup & ETG_Game)
  450. {
  451. //if (bCaptureThumbnail)
  452. //{
  453. // bCaptureThumbnail = false;
  454. // CaptureThumbnail();
  455. //}
  456.  
  457. if (!IsIndisposed())
  458. {
  459. // hax, not a shipping solution on account of happening during the game tick!
  460. DemoIdleTime += NBaseGame::GetInstance()->engDeltaTime;
  461.  
  462. // Might as well run this every tick. It contains all its own logic.
  463. TryBubble(DeltaTime);
  464.  
  465. NDialogConsumerComponent* Dialog = GetComponent<NDialogConsumerComponent>();
  466. if (Dialog && Dialog->HasDialog())
  467. {
  468. if (Controller->IsRepeating(Menus, NavUp))
  469. {
  470. // TODO: AUDIO
  471. DemoIdleTime = 0.0f;
  472. if (Dialog->MoveSelectedOption(-1))
  473. {
  474. NBaseGame::GetInstance()->PlayDialogAudio(DAC_NavUp);
  475. }
  476. }
  477. else if (Controller->IsRepeating(Menus, NavDown))
  478. {
  479. // TODO: AUDIO
  480. DemoIdleTime = 0.0f;
  481. if (Dialog->MoveSelectedOption(+1))
  482. {
  483. NBaseGame::GetInstance()->PlayDialogAudio(DAC_NavDown);
  484. }
  485. }
  486. else if (Controller->IsDownEdge(Menus, Cancel))
  487. {
  488. DemoIdleTime = 0.0f;
  489. // Close dialog box instantly. Do not take any follow-up actions.
  490. if (Dialog->CanCloseDialog())
  491. {
  492. // TODO: AUDIO
  493. NBaseGame::GetInstance()->PlayDialogAudio(DAC_Close);
  494.  
  495. Dialog->CloseDialog();
  496.  
  497. // If we pressed Escape, we want to suppress the pause menu.
  498. NBaseGame::GetInstance()->bSuppressPause = true;
  499. }
  500. else
  501. {
  502. // If the dialog is set to no-close, then it's totally okay to bring up the pause menu over it.
  503. }
  504. }
  505. // maaaaaaaaybe?
  506. else if (Controller->IsDownEdge(Gameplay, Jump) ||
  507. Controller->IsDownEdge(Gameplay, Enter) ||
  508. //Controller->IsDownEdge(Gameplay, Primary) ||
  509. Controller->IsDownEdge(Gameplay, Interact) || // Bad for some reason?? (Same as cancel button by default [IN VANGUARD]. Could be user-defined collisions here though!!)
  510. Controller->IsDownEdge(Gameplay, Status) ||
  511. Controller->IsDownEdge(Gameplay, FullMap) || // Sigh. Sure.
  512. Controller->IsDownEdge(Menus, Accept)// ||
  513. //Controller->IsDownEdge(Menus, Cancel) ||
  514. /*Controller->IsDownEdge(Menus, Click)*/) // Probably not this one...
  515. {
  516. // TODO: AUDIO
  517.  
  518. DemoIdleTime = 0.0f;
  519. NHandle SavedProvider = Dialog->GetProvider();
  520.  
  521. if (Dialog->CanAdvanceDialog())
  522. {
  523. bool bOpen = Dialog->AdvanceDialog();
  524.  
  525. if (bOpen)
  526. {
  527. NBaseGame::GetInstance()->PlayDialogAudio(DAC_Advance);
  528. }
  529. else
  530. {
  531. NBaseGame::GetInstance()->PlayDialogAudio(DAC_Close);
  532. }
  533. }
  534. }
  535. }
  536. else
  537. {
  538. // Kick off frob logic. This class handles everything else and will return the current best result on demand.
  539. GetComponent<NFrobComponent>()->Update();
  540. GetComponent<DoorComponent>()->Update();
  541.  
  542.  
  543.  
  544. if (!OVERWORLD_MOTION &&
  545. Controller->IsDownEdge(Gameplay, Status))
  546. {
  547. DemoIdleTime = 0.0f;
  548. IdleTime = 0.0f;
  549.  
  550. ShowStatusScreen();
  551. }
  552.  
  553. if (!OVERWORLD_MOTION &&
  554. ValkyrieGame::GetInstance()->bHasMinimap &&
  555. Controller->IsDownEdge(Gameplay, FullMap))
  556. {
  557. DemoIdleTime = 0.0f;
  558. IdleTime = 0.0f;
  559.  
  560. ShowFullMap();
  561. }
  562.  
  563.  
  564.  
  565. if (Controller->IsDownEdge(Gameplay, Interact))
  566. {
  567. DemoIdleTime = 0.0f;
  568. IdleTime = 0.0f;
  569.  
  570. if (!GetComponent<NFrobComponent>()->Frob())
  571. {
  572. // Frob failed; try something else?
  573. }
  574. }
  575.  
  576. bool bSuppressJump = false;
  577. if (Controller->IsDownEdge(Gameplay, Enter))
  578. {
  579. DemoIdleTime = 0.0f;
  580. IdleTime = 0.0f;
  581.  
  582. if (!GetComponent<DoorComponent>()->Frob())
  583. {
  584. // Frob failed; try something else?
  585. }
  586. else
  587. {
  588. // Suppress jumping if we just went through a door.
  589. bSuppressJump = true;
  590. }
  591. }
  592.  
  593.  
  594.  
  595. // Suppress jumping for a split second.
  596. bSuppressJump = bSuppressJump || ((TeleportEntry_Remaining - (TeleportEntry_Duration * 0.25f)) > 0.0f);
  597.  
  598.  
  599.  
  600. float MovementLateral = 0.0f;
  601. float MovementLongitudinal = 0.0f;
  602.  
  603. if (!OVERWORLD_MOTION)
  604. {
  605. MovementLateral =
  606. Controller->GetValue(Gameplay, MoveRight) -
  607. Controller->GetValue(Gameplay, MoveLeft);
  608.  
  609. // Prevent mouse cheating
  610. MovementLateral = Clamp(MovementLateral, -1.0f, 1.0f);
  611.  
  612. // Extra dead zone because analog stick feels bad when looking down.
  613. // For Valkyrie, I guess I should do the same for pushing up to enter doors too.
  614. // It's irritating to change directions when pushing up.
  615. const float EXTRA_DEAD_ZONE = Min(0.9f, Max(Controller->GetValue(Gameplay, JumpDown), Controller->GetValue(Gameplay, Enter)));
  616. MovementLateral = sign(MovementLateral) * (Max(0.0, ((float)fabs(MovementLateral) - EXTRA_DEAD_ZONE)) / (1.0f - EXTRA_DEAD_ZONE));
  617.  
  618.  
  619. }
  620. else
  621. {
  622. // Use the other controls for the overworld
  623.  
  624. MovementLateral =
  625. Controller->GetValue(Overworld, MoveEast) -
  626. Controller->GetValue(Overworld, MoveWest);
  627.  
  628. MovementLongitudinal =
  629. Controller->GetValue(Overworld, MoveSouth) -
  630. Controller->GetValue(Overworld, MoveNorth);
  631.  
  632. MovementLateral = Clamp(MovementLateral, -1.0f, 1.0f);
  633. MovementLongitudinal = Clamp(MovementLongitudinal, -1.0f, 1.0f);
  634.  
  635. if (OverworldDelay > 0.0f)
  636. {
  637. MovementLateral = 0.0f;
  638. MovementLongitudinal = 0.0f;
  639. }
  640. }
  641.  
  642. OverworldDelay -= DeltaTime;
  643.  
  644.  
  645.  
  646. if (!OVERWORLD_MOTION)
  647. {
  648. if (TeleportEntry_Remaining >= 0.0f)
  649. {
  650. float EntryAlpha = (TeleportEntry_Remaining / TeleportEntry_Duration);
  651. EntryAlpha = pow(EntryAlpha, 0.5f);
  652.  
  653. TeleportEntry_Remaining -= DeltaTime;
  654.  
  655. MovementLateral = Lerp(MovementLateral, TeleportEntry_Direction.x, EntryAlpha);
  656. }
  657. }
  658.  
  659.  
  660.  
  661. // Super Meat Boy wall gripping.
  662. if (!OVERWORLD_MOTION)
  663. {
  664. // This is actually asking whether we were wall sliding LAST tick, including gripping.
  665. if (PhysComp->IsWallSliding())
  666. {
  667. float HugNorm = PhysComp->GetWallHugNormal().x;
  668.  
  669. // Now the question is whether we're currently TRYING to slide (in which case we do nothing but reset the grip timer),
  670. // or whether we're opposing or ignoring the slide, in which case we push against the wall and decrement the timer.
  671. if ((MovementLateral > 0.0f && HugNorm < 0.0f) ||
  672. (MovementLateral < 0.0f && HugNorm > 0.0f))
  673. {
  674. WallGripTimer = WALL_GRIP_TIMER;
  675. }
  676. else if (WallGripTimer > 0.0f)
  677. {
  678. WallGripTimer -= DeltaTime;
  679. MovementLateral = -HugNorm;
  680. }
  681. }
  682. }
  683.  
  684.  
  685.  
  686. {
  687. // Not sure whether I should base this on MovementLateral (keep walking into walls)
  688. // or velocity.x / TILE_SIZE (stop at walls)...
  689. float SpeedScalar = 9.0f * Max((float)fabs(MovementLateral), (float)fabs(MovementLongitudinal));
  690.  
  691. if (SWIMMING_MOTION)
  692. {
  693. SpeedScalar *= (SWIMMING_MOVEMENT_SPEED / MOVEMENT_SPEED);
  694. }
  695.  
  696. WalkingFrame += DeltaTime * SpeedScalar;
  697. while (WalkingFrame >= 6.0f)
  698. {
  699. WalkingFrame -= 6.0f;
  700. }
  701.  
  702. if (MovementLateral > 0.01f)
  703. {
  704. bFacingLeft = false;
  705. GetComponent<NFacingComponent>()->SetFacing(FD_Right);
  706. }
  707. else if (MovementLateral < -0.01f)
  708. {
  709. bFacingLeft = true;
  710. GetComponent<NFacingComponent>()->SetFacing(FD_Left);
  711. }
  712. else if (!OVERWORLD_MOTION || (float)fabs(MovementLongitudinal) < 0.01f)
  713. {
  714. WalkingFrame = 0.0f;
  715. }
  716.  
  717. if (LandedTime > 0.0f)
  718. {
  719. LandedTime = Max(0.0f, LandedTime - DeltaTime);
  720. }
  721. }
  722.  
  723. bool bAltDownJump = NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->ALT_DOWN_JUMP;
  724.  
  725. bLookingDown = (!OVERWORLD_MOTION && PhysComp && PhysComp->IsGrounded() && Controller->IsDown(Gameplay, JumpDown));
  726.  
  727. bool bJumping = (!OVERWORLD_MOTION && (!bLookingDown || !bAltDownJump) && !bSuppressJump && Controller->IsDownEdge(Gameplay, Jump));
  728.  
  729. if (bAllowRejump)
  730. {
  731. if (Controller->IsDown(Gameplay, Jump))
  732. {
  733. bJumping = true;
  734. }
  735. bAllowRejump = false;
  736. }
  737.  
  738. DownJumpTime = (Max(0.0, DownJumpTime - DeltaTime));
  739.  
  740. bool bDownJumpTest = false;
  741. if (!bAltDownJump)
  742. {
  743. bDownJumpTest = !OVERWORLD_MOTION && Controller->IsDownEdge(Gameplay, JumpDown);
  744. }
  745. else
  746. {
  747. bDownJumpTest = !OVERWORLD_MOTION && Controller->IsDown(Gameplay, JumpDown) && Controller->IsDownEdge(Gameplay, Jump);
  748. }
  749.  
  750. if (bDownJumpTest)
  751. {
  752. DownJumpTime = 0.1f;
  753. }
  754.  
  755.  
  756.  
  757.  
  758.  
  759. if ((((float)fabs(MovementLateral) + (float)fabs(MovementLongitudinal)) <= 0.0001f &&
  760. !bJumping && !bLookingDown))
  761. {
  762. IdleTime += DeltaTime;
  763. while (IdleTime > 8.0f)
  764. {
  765. IdleTime -= 8.0f;
  766. }
  767. }
  768. else
  769. {
  770. IdleTime = 0.0f;
  771. DemoIdleTime = 0.0f;
  772. }
  773.  
  774. if (OVERWORLD_MOTION)
  775. {
  776. OverworldTime += DeltaTime;
  777. while (OverworldTime > 8.0f)
  778. {
  779. OverworldTime -= 8.0f;
  780. }
  781. }
  782.  
  783.  
  784.  
  785.  
  786.  
  787. NVec3 DesiredMoveVector(0,0,0);
  788. DesiredMoveVector += (MovementLateral * NVec3(1,0,0));
  789. DesiredMoveVector += (MovementLongitudinal * NVec3(0,1,0));
  790.  
  791. if (!OVERWORLD_MOTION)
  792. {
  793. if (!SWIMMING_MOTION)
  794. {
  795. DesiredMoveVector *= MOVEMENT_SPEED;
  796. }
  797. else
  798. {
  799. DesiredMoveVector *= SWIMMING_MOVEMENT_SPEED;
  800. }
  801.  
  802. // Coffee cup hack here.
  803. CoffeeComponent* Coffee = GetComponent<CoffeeComponent>();
  804. if (Coffee)
  805. {
  806. Coffee->ModifySpeed(DesiredMoveVector);
  807. }
  808. }
  809. else
  810. {
  811. HandleOverworldMovement(DeltaTime, TickGroup, DesiredMoveVector);
  812. }
  813.  
  814. {
  815. // We still want to maintain slippery velocity while in the air,
  816. // but we only apply it while we're on the ground.
  817. IcyVelocity.x = Smooth(IcyVelocity.x, DesiredMoveVector.x, ValkyrieGame::GetInstance()->ICY_RAMP_SPEED, DeltaTime);
  818.  
  819. // Might want a special rule for when we land while moving and our momentum
  820. // is actually slowing us down or trying to reverse us. That feels not good.
  821. // What would a good fix be? Maybe just ramp up faster than we ramp down?
  822.  
  823. if (fabs(IcyVelocity.x) < 4.0f && fabs(DesiredMoveVector.x) < 0.01f) // Once we stop scrolling smoothly, kill the momentum.
  824. {
  825. IcyVelocity.x = 0.0f;
  826. }
  827. if (ICY_MOTION)
  828. {
  829. if (PhysComp->IsGrounded())
  830. {
  831. DesiredMoveVector.x = IcyVelocity.x;
  832. }
  833. }
  834. }
  835.  
  836. bool bHoldingJump = !OVERWORLD_MOTION && Controller->IsDown(Gameplay, Jump);
  837. if (PhysComp)
  838. {
  839. PhysComp->Function1_NeedsName(bHoldingJump);
  840. PhysComp->Function1_5_LOL_ApplyMoveAttempt(DesiredMoveVector, bJumping, bHoldingJump);
  841. PhysComp->Function2_NeedsName(bJumping);
  842. }
  843.  
  844. // 07/02/2012: Originally calculated gravitional acceleration here.
  845. // Moved to after DoJump_Outer() call per Kabuto's notes.
  846. // DoJump_Outer() will alter GravityAlpha, so the acceleration will be off if we do this first.
  847.  
  848. // bGrounded has the opportunity to change in the commit phase but is guaranteed to not change during this function.
  849. // So this can be done whenever. Except that Function2_NeedsName() can ALSO change velocity because that's where the jump actually starts
  850. // (and that probably needs to be refactored as well), so this should be done before that, I guess.
  851.  
  852. // TODO: Use a getter function for bGrounded.
  853. }
  854. }
  855. else if (IsDying())
  856. {
  857. DeadedTime -= DeltaTime;
  858.  
  859. if (DeadedTime < 0.0f)
  860. {
  861. DeadedTime = 0.0f;
  862.  
  863. GetComponent<NHealthComponent>()->ResetHealth();
  864.  
  865. // Record a ghost event BEFORE respawning as well as after.
  866. SpeedrunComponent* Speed = GetComponent<SpeedrunComponent>();
  867. if (Speed)
  868. {
  869. Speed->TryRecordGhostEvent(true);
  870. }
  871.  
  872. // Respawn
  873. Space->Game_Curr.Velocity = NVec3(0.0f);
  874. Space->Game_Curr.Position = GetCheckpointPosition(false);
  875. Space->Game_Curr.Position.z = 0.0f; // Just to be safe.
  876. Space->Init();
  877.  
  878. // TODO: Move to an OnDied() sort of function?
  879. // What is this doing, in a general sense?
  880. if (PhysComp)
  881. {
  882. PhysComp->OnSpawn(true);
  883. }
  884.  
  885. Base->TryUnbase();
  886.  
  887. // Hopefully we aren't trying to change campaigns here! :O
  888. opt_assert(GetCheckpointCampaign(false) == NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.ActiveCampaign);
  889.  
  890. POINT ActiveRoom = NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.ActiveRoom;
  891. NHashedString ActiveMap = NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.ActiveMap;
  892. if (GetCheckpointRoom(false).x != ActiveRoom.x ||
  893. GetCheckpointRoom(false).y != ActiveRoom.y ||
  894. GetCheckpointMap(false) != ActiveMap)
  895. {
  896. if (GetCheckpointMap(false) != ActiveMap)
  897. {
  898. NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->LoadMap(NString(GetCheckpointMap(false).getString()));
  899. }
  900.  
  901. NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.ActiveRoom = GetCheckpointRoom(false);
  902. NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->CompileRoom();
  903.  
  904. ValkyrieGame::GetInstance()->PostTransitionCreateEntities(false);
  905. }
  906. else
  907. {
  908. // We didn't change rooms, but we need to reprop the region flags because they get cleared on death!
  909. // Or...just don't clear those. If we changed rooms, they'll get set by CompileRoom().
  910. }
  911.  
  912. bVulnerable = true;
  913.  
  914. // Maybe this will fix the ice world bug and not introduce any new ones?
  915. TryGround();
  916.  
  917. InitCamera();
  918. InitUnderwater();
  919.  
  920. CheckpointWith(GetHandle());
  921.  
  922. //if (NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->IsYOLOMode())
  923. //{
  924. // ClearStatsForNewGame();
  925. //}
  926.  
  927. //SpeedrunComponent* Speed = GetComponent<SpeedrunComponent>();
  928. if (Speed)
  929. {
  930. Speed->TryRecordGhostEvent(true);
  931. }
  932. }
  933. }
  934. else
  935. {
  936. // Quick hack for demo pausing.
  937. NGameMenu* TopMenu = NMenuMgr::GetInstance()->GetTopmostMenu();
  938. if (TopMenu && TopMenu->AllowDemoReset())
  939. {
  940. DemoIdleTime += NBaseGame::GetInstance()->engDeltaTime;
  941. }
  942. }
  943. }
  944. }
  945.  
  946. void Player::HandleOverworldMovement(float DeltaTime, ETickGroup TickGroup, NVec3& DesiredMoveVector)
  947. {
  948. DeltaTime;
  949. if (TickGroup & ETG_Game)
  950. {
  951. //////////////////////////////////////////////////////////////////////////
  952. // Begin overworld movement
  953. //////////////////////////////////////////////////////////////////////////
  954.  
  955. if (OverworldState == OMS_Walking &&
  956. OverWalk_Elapsed >= OverWalk_Duration)
  957. {
  958. OverworldState = OMS_Idle;
  959.  
  960. // TODO: Do intersection test against anything we might've just stepped on.
  961. // This is an easy way to do intersection tests with all the existing callbacks.
  962. GetComponent<NCollisionComponent>()->SetCollision(true);
  963.  
  964. // TODO: Attempt transition if we left the scroll region.
  965. {
  966. POINT RoomMoveDir;
  967. RoomMoveDir.x = 0;
  968. RoomMoveDir.y = 0;
  969. NVec3 NewPlayerPos = Space->Game_Curr.Position;
  970. NVec3 NewPlayerVel = Space->Game_Curr.Velocity;
  971.  
  972. bool bCrossedEdge = false;
  973. EdgeTransition TheCrossedEdge;
  974.  
  975. {
  976. RoomMoveDir.x = int(floor(NewPlayerPos.x / (float)GAME_SCREEN_WIDTH));
  977. RoomMoveDir.y = int(floor(NewPlayerPos.y / (float)GAME_SCREEN_HEIGHT));
  978.  
  979. if (NewPlayerPos.y > ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Bottom)
  980. {
  981. bCrossedEdge = true;
  982. POINT ThisRoom = ValkyrieGame::GetInstance()->TheWorld.ActiveRoom;
  983. POINT TransRoom = ThisRoom;
  984. TransRoom.x += RoomMoveDir.x;
  985. TransRoom.y += RoomMoveDir.y - 1;
  986. TheCrossedEdge = ValkyrieGame::GetInstance()->TheWorld.Rooms[TransRoom.x][TransRoom.y].Edges[1];
  987. }
  988. else if (NewPlayerPos.y < ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Top)
  989. {
  990. bCrossedEdge = true;
  991. POINT ThisRoom = ValkyrieGame::GetInstance()->TheWorld.ActiveRoom;
  992. POINT TransRoom = ThisRoom;
  993. TransRoom.x += RoomMoveDir.x;
  994. TransRoom.y += RoomMoveDir.y + 1;
  995. TheCrossedEdge = ValkyrieGame::GetInstance()->TheWorld.Rooms[TransRoom.x][TransRoom.y].Edges[0];
  996. }
  997.  
  998. if (NewPlayerPos.x > ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Right)
  999. {
  1000. bCrossedEdge = true;
  1001. POINT ThisRoom = ValkyrieGame::GetInstance()->TheWorld.ActiveRoom;
  1002. POINT TransRoom = ThisRoom;
  1003. TransRoom.x += RoomMoveDir.x - 1;
  1004. TransRoom.y += RoomMoveDir.y;
  1005. TheCrossedEdge = ValkyrieGame::GetInstance()->TheWorld.Rooms[TransRoom.x][TransRoom.y].Edges[3];
  1006. }
  1007. else if (NewPlayerPos.x < ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Left)
  1008. {
  1009. bCrossedEdge = true;
  1010. POINT ThisRoom = ValkyrieGame::GetInstance()->TheWorld.ActiveRoom;
  1011. POINT TransRoom = ThisRoom;
  1012. TransRoom.x += RoomMoveDir.x + 1;
  1013. TransRoom.y += RoomMoveDir.y;
  1014. TheCrossedEdge = ValkyrieGame::GetInstance()->TheWorld.Rooms[TransRoom.x][TransRoom.y].Edges[2];
  1015. }
  1016. }
  1017.  
  1018. opt_assert(!bCrossedEdge || RoomMoveDir.x != 0 || RoomMoveDir.y != 0);
  1019.  
  1020. if (bCrossedEdge &&
  1021. (RoomMoveDir.x != 0 || RoomMoveDir.y != 0))
  1022. {
  1023. POINT CurrRoom = NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.ActiveRoom;
  1024. POINT NewRoomCoords = NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.GetNextRoomOver(CurrRoom.x, CurrRoom.y, RoomMoveDir.x, RoomMoveDir.y);
  1025.  
  1026. while (NewPlayerPos.x < 0.0f)
  1027. {
  1028. NewPlayerPos.x += (float)GAME_SCREEN_WIDTH;
  1029. }
  1030. while (NewPlayerPos.x > (float)GAME_SCREEN_WIDTH)
  1031. {
  1032. NewPlayerPos.x -= (float)GAME_SCREEN_WIDTH;
  1033. }
  1034.  
  1035. while (NewPlayerPos.y < 0.0f)
  1036. {
  1037. NewPlayerPos.y += (float)GAME_SCREEN_HEIGHT;
  1038. }
  1039. while (NewPlayerPos.y > (float)GAME_SCREEN_HEIGHT)
  1040. {
  1041. NewPlayerPos.y -= (float)GAME_SCREEN_HEIGHT;
  1042. }
  1043.  
  1044. TransitionRooms(NewRoomCoords, RoomMoveDir, NewPlayerPos, NewPlayerVel, TheCrossedEdge, true);
  1045. }
  1046. }
  1047. }
  1048.  
  1049. if (OverworldState == OMS_Idle)
  1050. {
  1051. if (DesiredMoveVector.lengthSquared2D() > 0.0)
  1052. {
  1053. // Take input and maybe start move
  1054.  
  1055. // Axis selection from old movement code.
  1056. {
  1057. NVec3 BestAxis = NVec3(0,0,0);
  1058. float BestDot = 0.0f;
  1059. float TestDot = 0.0f;
  1060.  
  1061. //if (bCanMoveVert)
  1062. {
  1063. TestDot = Dot(NVec3(0,1,0), DesiredMoveVector);
  1064. if (TestDot > BestDot)
  1065. {
  1066. BestDot = TestDot;
  1067. BestAxis = NVec3(0,1,0);
  1068. }
  1069.  
  1070. TestDot = Dot(NVec3(0,-1,0), DesiredMoveVector);
  1071. if (TestDot > BestDot)
  1072. {
  1073. BestDot = TestDot;
  1074. BestAxis = NVec3(0,-1,0);
  1075. }
  1076. }
  1077.  
  1078. //if (bCanMoveHori)
  1079. {
  1080. TestDot = Dot(NVec3(1,0,0), DesiredMoveVector);
  1081. if (TestDot > BestDot)
  1082. {
  1083. BestDot = TestDot;
  1084. BestAxis = NVec3(1,0,0);
  1085. }
  1086.  
  1087. TestDot = Dot(NVec3(-1,0,0), DesiredMoveVector);
  1088. if (TestDot > BestDot)
  1089. {
  1090. BestDot = TestDot;
  1091. BestAxis = NVec3(-1,0,0);
  1092. }
  1093. }
  1094.  
  1095. OverworldDesiredMove = BestAxis;
  1096.  
  1097. if (OverworldDesiredMove.length2D() > 0.0f)
  1098. {
  1099. NVec2 NewOverworldFacing = Normalize(OverworldDesiredMove);
  1100. if (Dot(OverworldFacing, NewOverworldFacing) < 0.707f)
  1101. {
  1102. // If we're changing direction, cache off the old value.
  1103. PrevOverworldFacing = OverworldFacing;
  1104. }
  1105. OverworldFacing = NewOverworldFacing;
  1106.  
  1107.  
  1108. }
  1109. else
  1110. {
  1111.  
  1112. }
  1113. }
  1114.  
  1115. // Should only need to look ahead 8, but 10 shouldn't hurt
  1116. NVec3 Lookahead = Normalize(OverworldDesiredMove) * 12.5f;
  1117.  
  1118. bool bMoveFree = true;
  1119. {
  1120. static NUtilVector<NCollisionResult> Results;
  1121. Results.setNeverDealloc(true);
  1122. Results.clear();
  1123.  
  1124. NCollisionInterface* Primitive = Collision->GetPrimitiveByName();
  1125.  
  1126. if (Primitive)
  1127. {
  1128. // TODO: MAKE SURE THIS DOESN'T TRIGGER ANY TOUCH EVENTS!!
  1129. // That should only happen as part of the swept move code path.
  1130. bMoveFree =
  1131. NGameOctree::GetInstance()->Sweep(
  1132. Results, Primitive,
  1133. Space->Game_Curr.Position,
  1134. Space->Game_Curr.Position + Lookahead,
  1135. 0, Collision->GetSweepFilter() & ~(CFT_MapDoor)); // Don't sweep against map doors for the purposes of movement.
  1136. }
  1137. }
  1138.  
  1139. if (bMoveFree)
  1140. {
  1141. // Set up a move.
  1142. OverworldState = OMS_Walking;
  1143. OverWalk_Elapsed = 0.0f;
  1144. OverWalk_Start = Space->Game_Curr.Position;
  1145. OverWalk_End = Space->Game_Curr.Position + (Normalize(OverworldDesiredMove) * 16.0f);
  1146.  
  1147. // Just in case, make sure we're aligned to 16x16 blocks.
  1148. OverWalk_End.x = ((floor(((OverWalk_End.x - 8.0f) / 16.0f) + 0.5f) * 16.0f) + 8.0f);
  1149. OverWalk_End.y = ((floor(((OverWalk_End.y - 8.0f) / 16.0f) + 0.5f) * 16.0f) + 8.0f);
  1150. }
  1151. }
  1152. }
  1153.  
  1154. if (OverworldState == OMS_Walking)
  1155. {
  1156. // Continue move
  1157. OverWalk_Elapsed += DeltaTime;
  1158. float WalkAlpha = Clamp(OverWalk_Elapsed / OverWalk_Duration, 0.0f, 1.0f);
  1159. Space->Game_Curr.Position = Lerp(OverWalk_Start, OverWalk_End, WalkAlpha);
  1160. }
  1161. //else
  1162. {
  1163. // ALWAYS clear this. We don't use it for overworld movement after this!
  1164. DesiredMoveVector = NVec3(0);
  1165. }
  1166.  
  1167. /*
  1168.  
  1169. // Cover four 16x16 tiles in one second. Based on Zelda 2 rates.
  1170. // Slightly slower than normal movement speed (~88 vs. 64).
  1171. DesiredMoveVector *= (8.0f * 8.0f);
  1172.  
  1173. // Now do clamping!
  1174. // We'll take the magnitude of the desired move vector and apply it to whatever direction we end up moving (as opposed to projecting);
  1175. // we just need to decide what direction that actually is.
  1176.  
  1177. //NVec3 ActualDirection(1,0,0);
  1178. //DesiredMoveVector = ActualDirection * DesiredMoveVector.length2D();
  1179.  
  1180. float PrevX = floor(Space->Game_Curr.Position.x / (float)OVERWORLD_TILE_SIZE);
  1181. float NextX = ceil(Space->Game_Curr.Position.x / (float)OVERWORLD_TILE_SIZE);
  1182. float PrevY = floor(Space->Game_Curr.Position.y / (float)OVERWORLD_TILE_SIZE);
  1183. float NextY = ceil(Space->Game_Curr.Position.y / (float)OVERWORLD_TILE_SIZE);
  1184.  
  1185. bool bCanMoveHori = ((NextY - PrevY) <= 0.01f);
  1186. bool bCanMoveVert = ((NextX - PrevX) <= 0.01f);
  1187.  
  1188. // If we're inside a block somehow, allow moves in either direction.
  1189. //if (!bCanMoveHori && !bCanMoveVert)
  1190. //{
  1191. // bCanMoveHori = true;
  1192. // bCanMoveVert = true;
  1193. //}
  1194.  
  1195. NVec3 AllowedMove;
  1196. if (bCanMoveHori) AllowedMove += NVec3(1,0,0);
  1197. if (bCanMoveVert) AllowedMove += NVec3(0,1,0);
  1198.  
  1199. // But we still have to choose a best axis, and we also want to turn at corners.
  1200. // To turn a corner, we look at whether we would cross the axis in this move, and if we would,
  1201. // we snap to that axis and move the remainder of the distance.
  1202.  
  1203. NVec3 BestAxis = NVec3(0,0,0);
  1204. float BestDot = 0.0f;
  1205. float TestDot = 0.0f;
  1206.  
  1207. //if (bCanMoveVert)
  1208. {
  1209. TestDot = Dot(NVec3(0,1,0), DesiredMoveVector);
  1210. if (TestDot > BestDot)
  1211. {
  1212. BestDot = TestDot;
  1213. BestAxis = NVec3(0,1,0);
  1214. }
  1215.  
  1216. TestDot = Dot(NVec3(0,-1,0), DesiredMoveVector);
  1217. if (TestDot > BestDot)
  1218. {
  1219. BestDot = TestDot;
  1220. BestAxis = NVec3(0,-1,0);
  1221. }
  1222. }
  1223.  
  1224. //if (bCanMoveHori)
  1225. {
  1226. TestDot = Dot(NVec3(1,0,0), DesiredMoveVector);
  1227. if (TestDot > BestDot)
  1228. {
  1229. BestDot = TestDot;
  1230. BestAxis = NVec3(1,0,0);
  1231. }
  1232.  
  1233. TestDot = Dot(NVec3(-1,0,0), DesiredMoveVector);
  1234. if (TestDot > BestDot)
  1235. {
  1236. BestDot = TestDot;
  1237. BestAxis = NVec3(-1,0,0);
  1238. }
  1239. }
  1240.  
  1241. OverworldDesiredMove = BestAxis;
  1242.  
  1243. if (OverworldDesiredMove.length2D() > 0.0f)
  1244. {
  1245. NVec2 NewOverworldFacing = Normalize(OverworldDesiredMove);
  1246. if (Dot(OverworldFacing, NewOverworldFacing) < 0.707f)
  1247. {
  1248. // If we're changing direction, cache off the old value.
  1249. PrevOverworldFacing = OverworldFacing;
  1250. }
  1251. OverworldFacing = NewOverworldFacing;
  1252.  
  1253.  
  1254. }
  1255. else
  1256. {
  1257.  
  1258. }
  1259.  
  1260. BestAxis = BestAxis * AllowedMove;
  1261.  
  1262. float PreMag = (DesiredMoveVector.length2D());
  1263. DesiredMoveVector = BestAxis * DesiredMoveVector.length2D();
  1264. float PostMag = (DesiredMoveVector.length2D());
  1265.  
  1266. if (PreMag > 0.0f && PostMag <= 0.0f)
  1267. {
  1268. // Oops, we killed our movement!
  1269. // (Probably trying to move solely in an unallowed direction.)
  1270. // Let's try to patch it up.
  1271.  
  1272. float AlphaX = Normalize(Space->Game_Curr.Position.x / (float)OVERWORLD_TILE_SIZE, PrevX, NextX);
  1273. float AlphaY = Normalize(Space->Game_Curr.Position.y / (float)OVERWORLD_TILE_SIZE, PrevY, NextY);
  1274. AlphaX, AlphaY;
  1275.  
  1276. if (!bCanMoveHori)
  1277. {
  1278. //if (AlphaY < 0.5f)
  1279. if (PrevOverworldFacing.y < 0.0f)
  1280. {
  1281. DesiredMoveVector = NVec3(0,-1,0) * PreMag;
  1282. }
  1283. else
  1284. {
  1285. DesiredMoveVector = NVec3(0,1,0) * PreMag;
  1286. }
  1287. }
  1288. else if (!bCanMoveVert)
  1289. {
  1290. //if (AlphaX < 0.5f)
  1291. if (PrevOverworldFacing.x < 0.0f)
  1292. {
  1293. DesiredMoveVector = NVec3(-1,0,0) * PreMag;
  1294. }
  1295. else
  1296. {
  1297. DesiredMoveVector = NVec3(1,0,0) * PreMag;
  1298. }
  1299. }
  1300. }
  1301.  
  1302. */
  1303.  
  1304. //////////////////////////////////////////////////////////////////////////
  1305. // End overworld movement
  1306. //////////////////////////////////////////////////////////////////////////
  1307. }
  1308. }
  1309.  
  1310. void Player::Tick_CommitUpdate(float DeltaTime, ETickGroup TickGroup)
  1311. {
  1312. TickGroup;
  1313.  
  1314. if (TickGroup & ETG_Game)
  1315. {
  1316. // This should be done before integration because it adjusts velocity for wall jumping.
  1317. if (PhysComp)
  1318. {
  1319. PhysComp->Function3_Commit_Upkeep(DeltaTime);
  1320. }
  1321.  
  1322. // TODO: Can THIS be moved into the jump component? SHOULD it?
  1323. if (!IsIndisposed())
  1324. {
  1325. // May change jump component's bGrounded.
  1326. // This is the ONLY code that may change bGrounded.
  1327. Space->Integrate(DeltaTime);
  1328.  
  1329. /*
  1330. if (OVERWORLD_MOTION)
  1331. {
  1332. // Turn corners as necessary
  1333. OverworldDesiredMove;
  1334.  
  1335. float PrevPrevX = floor(Space->Game_Prev.Position.x / (float)OVERWORLD_TILE_SIZE);
  1336. float PrevNextX = ceil(Space->Game_Prev.Position.x / (float)OVERWORLD_TILE_SIZE);
  1337. float PrevPrevY = floor(Space->Game_Prev.Position.y / (float)OVERWORLD_TILE_SIZE);
  1338. float PrevNextY = ceil(Space->Game_Prev.Position.y / (float)OVERWORLD_TILE_SIZE);
  1339.  
  1340. float CurrPrevX = floor(Space->Game_Curr.Position.x / (float)OVERWORLD_TILE_SIZE);
  1341. float CurrNextX = ceil(Space->Game_Curr.Position.x / (float)OVERWORLD_TILE_SIZE);
  1342. float CurrPrevY = floor(Space->Game_Curr.Position.y / (float)OVERWORLD_TILE_SIZE);
  1343. float CurrNextY = ceil(Space->Game_Curr.Position.y / (float)OVERWORLD_TILE_SIZE);
  1344.  
  1345. if (Space->Game_Curr.Velocity.lengthSquared2D() > 0.0f &&
  1346. OverworldDesiredMove.lengthSquared2D() > 0.0f &&
  1347. Dot(Space->Game_Curr.Velocity, OverworldDesiredMove) < 0.707f)
  1348. {
  1349. // We moved in a direction we didn't want to. We want to take a corner.
  1350.  
  1351. if (PrevPrevX != CurrPrevX ||
  1352. PrevPrevY != CurrPrevY ||
  1353. PrevNextX != CurrNextX ||
  1354. PrevNextY != CurrNextY)
  1355. {
  1356. // We crossed a threshold. Now we just need to figure out which one.
  1357.  
  1358. opt_assert(
  1359. (PrevPrevX == PrevNextX && CurrPrevX == CurrNextX && PrevPrevX == CurrPrevX && PrevNextX == CurrNextX) ||
  1360. (PrevPrevY == PrevNextY && CurrPrevY == CurrNextY && PrevPrevY == CurrPrevY && PrevNextY == CurrNextY));
  1361.  
  1362. if (CurrPrevX != CurrNextX)
  1363. {
  1364. if (CurrPrevX > PrevPrevX)
  1365. {
  1366. // We're moving right, so clamp to the axis left of us (CurrPrevX)
  1367. float NewAxis = CurrPrevX * OVERWORLD_TILE_SIZE;
  1368. Space->Game_Curr.Position.x = NewAxis;
  1369. }
  1370. else if (CurrPrevX < PrevPrevX)
  1371. {
  1372. // We're moving left, so clamp to the axis right of us (CurrNextX)
  1373. float NewAxis = CurrNextX * OVERWORLD_TILE_SIZE;
  1374. Space->Game_Curr.Position.x = NewAxis;
  1375. }
  1376. else
  1377. {
  1378. opt_assert(false);
  1379. }
  1380. }
  1381.  
  1382. if (CurrPrevY != CurrNextY)
  1383. {
  1384. if (CurrPrevY > PrevPrevY)
  1385. {
  1386. // We're moving down, so clamp to the axis above us (CurrPrevY)
  1387. float NewAxis = CurrPrevY * OVERWORLD_TILE_SIZE;
  1388. Space->Game_Curr.Position.y = NewAxis;
  1389. }
  1390. else if (CurrPrevY < PrevPrevY)
  1391. {
  1392. // We're moving up, so clamp to the axis below us (CurrNextY)
  1393. float NewAxis = CurrNextY * OVERWORLD_TILE_SIZE;
  1394. Space->Game_Curr.Position.y = NewAxis;
  1395. }
  1396. else
  1397. {
  1398. opt_assert(false);
  1399. }
  1400. }
  1401. }
  1402. }
  1403. }
  1404. */
  1405. }
  1406.  
  1407. if (PhysComp)
  1408. {
  1409. PhysComp->Function4_Apply_Terminal_Velocity();
  1410.  
  1411. // 07/26/2013: Moved this above the switch-rooms stuff
  1412. PhysComp->Function5_TickGracePeriods(DeltaTime);
  1413. }
  1414.  
  1415. //// Keep camera centered on player!
  1416. //// This will need more rules for clamping to scroll region, transitioning, etc.
  1417. //if (!IsTransitioning())
  1418. //{
  1419. // // This is a little hard to read, but we use the current value of bExtendedCamera to specify whether we allow it to be extended,
  1420. // // and we write the new value depending on whether it were extended.
  1421. // Camera->Camera->setPos(GetCameraPosition(bExtendedCamera, bExtendedCamera));
  1422. //}
  1423. //else
  1424. //{
  1425. // // Always allow the camera to extend when transitioning.
  1426. // bExtendedCamera = true;
  1427. //}
  1428.  
  1429. SpeedrunComponent* Speed = GetComponent<SpeedrunComponent>();
  1430. if (Speed)
  1431. {
  1432. Speed->TryRecordGhostEvent();
  1433. }
  1434.  
  1435. CheckTransitionRooms();
  1436.  
  1437. // Is this gonna be too slow to do every tick?
  1438. // TODO: Use actual delta time? This will be zero when dialogs are open.
  1439. // HAXHAXHAX not safe if we're using a fixed time step!!!
  1440. SetPlayTime(GetPlayTime() + NBaseGame::GetInstance()->engDeltaTime);
  1441.  
  1442. TryCacheSafePlace(false);
  1443.  
  1444. if (NBaseGame::GetInstance()->DEMO_MODE)
  1445. {
  1446. if (NBaseGame::GetInstance()->GamePaused())
  1447. {
  1448. if (DemoIdleTime > DEMO_TIMEOUT_PAUSED)
  1449. {
  1450. DemoIdleTime = 0.0f;
  1451. NMenuMgr::GetInstance()->OpenMenu(new DemoIdleMenu());
  1452. }
  1453. }
  1454. else
  1455. {
  1456. if (DemoIdleTime > DEMO_TIMEOUT)
  1457. {
  1458. DemoIdleTime = 0.0f;
  1459. NMenuMgr::GetInstance()->OpenMenu(new DemoIdleMenu());
  1460. }
  1461. }
  1462. }
  1463. }
  1464.  
  1465. if (TickGroup & ETG_Render)
  1466. {
  1467. TickCommitBGMusicChange(DeltaTime);
  1468. }
  1469.  
  1470. if (TickGroup & ETG_Render)
  1471. {
  1472. if (!IsIndisposed())
  1473. {
  1474. Space->Interpolate(NBaseGame::GetInstance()->gameAlpha);
  1475. }
  1476.  
  1477. // Keep camera centered on player!
  1478. // This will need more rules for clamping to scroll region, transitioning, etc.
  1479. if (!IsTransitioning())
  1480. {
  1481. // This is a little hard to read, but we use the current value of bExtendedCamera to specify whether we allow it to be extended,
  1482. // and we write the new value depending on whether it were extended.
  1483. Camera->Camera->setPos(GetCameraPosition(bExtendedCamera, bExtendedCamera));
  1484. }
  1485. else
  1486. {
  1487. // Always allow the camera to extend when transitioning.
  1488. bExtendedCamera = true;
  1489. }
  1490.  
  1491. NDialogConsumerComponent* Dialog = GetComponent<NDialogConsumerComponent>();
  1492. if (Dialog)
  1493. {
  1494. DialogAnchor Anchor = DA_Top;
  1495.  
  1496. // TODO: Always top-anchor the status screen?
  1497. if (Resolve<NEntity>(Dialog->GetProvider()) != this)
  1498. {
  1499. if (Space && Camera && Camera->Camera)
  1500. {
  1501. NVec3 Diff = Space->Render_Curr.Position - Camera->Camera->getPos();
  1502. if (Diff.y < -0.0f)
  1503. {
  1504. Anchor = DA_Bottom;
  1505. }
  1506. }
  1507. }
  1508.  
  1509. Dialog->Tick();
  1510. Dialog->Render(Anchor);
  1511. }
  1512.  
  1513. if (ValkyrieGame::GetInstance()->SHOW_PROMPTS)
  1514. {
  1515. NFrobComponent* Frob = GetComponent<NFrobComponent>();
  1516. if (Frob && (!Dialog || !Dialog->HasDialog()))
  1517. {
  1518. Frob->Render();
  1519. }
  1520.  
  1521. DoorComponent* Door = GetComponent<DoorComponent>();
  1522. if (Door && (!Dialog || !Dialog->HasDialog()))
  1523. {
  1524. Door->Render();
  1525. }
  1526. }
  1527.  
  1528. NPopupComponent* Popup = GetComponent<NPopupComponent>();
  1529. if (Popup)
  1530. {
  1531. Popup->Tick(DeltaTime);
  1532. Popup->Render();
  1533. }
  1534. }
  1535.  
  1536. SpeedrunComponent* Speed = GetComponent<SpeedrunComponent>();
  1537. if (Speed)
  1538. {
  1539. Speed->Tick(TickGroup, DeltaTime);
  1540. }
  1541. }
  1542.  
  1543. void Player::CheckTransitionRooms()
  1544. {
  1545. // Never do this if we're doing a wipe.
  1546. if (NSubroutineMgr::GetInstance()->IsSubroutineRunning(NHashedString("Door Transition")))
  1547. {
  1548. return;
  1549. }
  1550.  
  1551. // Switch rooms
  1552. // TODO: For Valkyrie, these deltas may be greater than one!
  1553. // As much of a kludge as it sounds, the camera position might be the best way to determine where we're actually trying to go,
  1554. // since it will lerp to the center point on the appropriate axis when we transition.
  1555. // This also might mean being stricter about only transitioning one direction or the other.
  1556. NVec3 MoveVec = (Space->Game_Curr.Position - Space->Game_Prev.Position);
  1557.  
  1558. float Alpha = 1.0f;
  1559. POINT RoomMoveDir;
  1560. RoomMoveDir.x = 0;
  1561. RoomMoveDir.y = 0;
  1562. NVec3 NewPlayerPos = Space->Game_Curr.Position;
  1563. NVec3 NewPlayerVel = Space->Game_Curr.Velocity;
  1564.  
  1565. bool bCrossedEdge = false;
  1566. EdgeTransition TheCrossedEdge;
  1567.  
  1568. bool bSkipWrapAroundLogic = false;
  1569.  
  1570. bool bAllowCheckpoint = true;
  1571.  
  1572. if (MoveVec.lengthSquared2D() > 0.0f)
  1573. {
  1574. RoomMoveDir.x = int(floor(NewPlayerPos.x / (float)GAME_SCREEN_WIDTH));
  1575. RoomMoveDir.y = int(floor(NewPlayerPos.y / (float)GAME_SCREEN_HEIGHT));
  1576.  
  1577. //while (NewPlayerPos.x < 0.0f)//ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Left)
  1578. //{
  1579. // NewPlayerPos.x += (float)GAME_SCREEN_WIDTH;
  1580. // RoomMoveDir.x -= 1;
  1581. //}
  1582. //while (NewPlayerPos.x > (float)GAME_SCREEN_WIDTH)//ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Right)
  1583. //{
  1584. // NewPlayerPos.x -= (float)GAME_SCREEN_WIDTH;
  1585. // RoomMoveDir.x += 1;
  1586. //}
  1587.  
  1588. //while (NewPlayerPos.y < 0.0f)//ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Top)
  1589. //{
  1590. // NewPlayerPos.y += (float)GAME_SCREEN_HEIGHT;
  1591. // RoomMoveDir.y -= 1;
  1592. //}
  1593. //while (NewPlayerPos.y > (float)GAME_SCREEN_HEIGHT)//ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Bottom)
  1594. //{
  1595. // NewPlayerPos.y -= (float)GAME_SCREEN_HEIGHT;
  1596. // RoomMoveDir.y += 1;
  1597. //}
  1598.  
  1599. if ((float)fabs(MoveVec.y) > 0.0f)
  1600. {
  1601. if (NewPlayerPos.y > ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Bottom)
  1602. {
  1603. // Don't allow checkpointing on vertical transitions.
  1604. bAllowCheckpoint = false;
  1605.  
  1606. bCrossedEdge = true;
  1607. POINT ThisRoom = ValkyrieGame::GetInstance()->TheWorld.ActiveRoom;
  1608. POINT TransRoom = ThisRoom;
  1609. TransRoom.x += RoomMoveDir.x;
  1610. TransRoom.y += RoomMoveDir.y - 1;
  1611. TheCrossedEdge = ValkyrieGame::GetInstance()->TheWorld.Rooms[TransRoom.x][TransRoom.y].Edges[1];
  1612.  
  1613. Alpha = (ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Bottom - Space->Game_Prev.Position.y) / MoveVec.y;
  1614. NewPlayerPos = Lerp(Space->Game_Prev.Position, NewPlayerPos, Alpha);
  1615. NewPlayerVel = Lerp(Space->Game_Prev.Velocity, NewPlayerVel, Alpha);
  1616. DebugLog(Text("CROSSED EDGE Bottom edge. Alpha is %f.", Alpha));
  1617. DebugLog(Text("CROSSED EDGE Interp position is ( %f, %f ).", NewPlayerPos.x, NewPlayerPos.y));
  1618. NewPlayerPos.y = 0.0f;
  1619. DebugLog(Text("CROSSED EDGE Fixed position is ( %f, %f ).", NewPlayerPos.x, NewPlayerPos.y));
  1620. //Space->Init();
  1621.  
  1622. // uh?
  1623. RoomMoveDir.x = int(floor(NewPlayerPos.x / (float)GAME_SCREEN_WIDTH));
  1624. }
  1625. else if (NewPlayerPos.y < ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Top)
  1626. {
  1627. // Don't allow checkpointing on vertical transitions.
  1628. bAllowCheckpoint = false;
  1629.  
  1630. bCrossedEdge = true;
  1631. POINT ThisRoom = ValkyrieGame::GetInstance()->TheWorld.ActiveRoom;
  1632. POINT TransRoom = ThisRoom;
  1633. TransRoom.x += RoomMoveDir.x;
  1634. TransRoom.y += RoomMoveDir.y + 1;
  1635. TheCrossedEdge = ValkyrieGame::GetInstance()->TheWorld.Rooms[TransRoom.x][TransRoom.y].Edges[0];
  1636.  
  1637. // For the top edge ONLY, cancel the transition if there's nothing up there.
  1638. if ((!TheCrossedEdge.bHasRoomCoords && !TheCrossedEdge.bHasMapCoords) &&
  1639. (!ValkyrieGame::GetInstance()->TheWorld.Rooms.exists(TransRoom.x) ||
  1640. !ValkyrieGame::GetInstance()->TheWorld.Rooms[TransRoom.x].exists(TransRoom.y - 1)))
  1641. {
  1642. bCrossedEdge = false;
  1643. }
  1644. else
  1645. {
  1646. Alpha = (ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Top - Space->Game_Prev.Position.y) / MoveVec.y;
  1647. NewPlayerPos = Lerp(Space->Game_Prev.Position, NewPlayerPos, Alpha);
  1648. NewPlayerVel = Lerp(Space->Game_Prev.Velocity, NewPlayerVel, Alpha);
  1649. DebugLog(Text("CROSSED EDGE Top edge. Alpha is %f.", Alpha));
  1650. DebugLog(Text("CROSSED EDGE Interp position is ( %f, %f ).", NewPlayerPos.x, NewPlayerPos.y));
  1651. NewPlayerPos.y = (float)GAME_SCREEN_HEIGHT;
  1652. DebugLog(Text("CROSSED EDGE Fixed position is ( %f, %f ).", NewPlayerPos.x, NewPlayerPos.y));
  1653. //Space->Init();
  1654.  
  1655. // uh?
  1656. RoomMoveDir.x = int(floor(NewPlayerPos.x / (float)GAME_SCREEN_WIDTH));
  1657. }
  1658. }
  1659. }
  1660.  
  1661. if ((float)fabs(MoveVec.x) > 0.0f)
  1662. {
  1663. if (NewPlayerPos.x > ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Right)
  1664. {
  1665. // Make sure we aren't indexing outside the world on account of jumping above the top scroll region.
  1666. if (NewPlayerPos.y < ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Top &&
  1667. !bCrossedEdge)
  1668. {
  1669. DebugLog("Adjusted transition delta for having jumped out of bounds.");
  1670. RoomMoveDir.y++;
  1671. bSkipWrapAroundLogic = true;
  1672. }
  1673.  
  1674. bCrossedEdge = true;
  1675. POINT ThisRoom = ValkyrieGame::GetInstance()->TheWorld.ActiveRoom;
  1676. POINT TransRoom = ThisRoom;
  1677. TransRoom.x += RoomMoveDir.x - 1;
  1678. TransRoom.y += RoomMoveDir.y;
  1679. TheCrossedEdge = ValkyrieGame::GetInstance()->TheWorld.Rooms[TransRoom.x][TransRoom.y].Edges[3];
  1680.  
  1681. Alpha = (ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Right - Space->Game_Prev.Position.x) / MoveVec.x;
  1682. NewPlayerPos = Lerp(Space->Game_Prev.Position, NewPlayerPos, Alpha);
  1683. NewPlayerVel = Lerp(Space->Game_Prev.Velocity, NewPlayerVel, Alpha);
  1684. DebugLog(Text("CROSSED EDGE Right edge. Alpha is %f.", Alpha));
  1685. DebugLog(Text("CROSSED EDGE Interp position is ( %f, %f ).", NewPlayerPos.x, NewPlayerPos.y));
  1686. NewPlayerPos.x = 0.0f;
  1687. DebugLog(Text("CROSSED EDGE Fixed position is ( %f, %f ).", NewPlayerPos.x, NewPlayerPos.y));
  1688. //Space->Init();
  1689.  
  1690. // uh?
  1691. RoomMoveDir.y = int(floor(NewPlayerPos.y / (float)GAME_SCREEN_HEIGHT));
  1692. if (bSkipWrapAroundLogic) {RoomMoveDir.y++;} // I guess?
  1693. }
  1694. else if (NewPlayerPos.x < ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Left)
  1695. {
  1696. // Make sure we aren't indexing outside the world on account of jumping above the top scroll region.
  1697. if (NewPlayerPos.y < ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Top &&
  1698. !bCrossedEdge)
  1699. {
  1700. DebugLog("Adjusted transition delta for having jumped out of bounds.");
  1701. RoomMoveDir.y++;
  1702. bSkipWrapAroundLogic = true;
  1703. }
  1704.  
  1705. bCrossedEdge = true;
  1706. POINT ThisRoom = ValkyrieGame::GetInstance()->TheWorld.ActiveRoom;
  1707. POINT TransRoom = ThisRoom;
  1708. TransRoom.x += RoomMoveDir.x + 1;
  1709. TransRoom.y += RoomMoveDir.y;
  1710. TheCrossedEdge = ValkyrieGame::GetInstance()->TheWorld.Rooms[TransRoom.x][TransRoom.y].Edges[2];
  1711.  
  1712. Alpha = (ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Left - Space->Game_Prev.Position.x) / MoveVec.x;
  1713. NewPlayerPos = Lerp(Space->Game_Prev.Position, NewPlayerPos, Alpha);
  1714. NewPlayerVel = Lerp(Space->Game_Prev.Velocity, NewPlayerVel, Alpha);
  1715. DebugLog(Text("CROSSED EDGE Left edge. Alpha is %f.", Alpha));
  1716. DebugLog(Text("CROSSED EDGE Interp position is ( %f, %f ).", NewPlayerPos.x, NewPlayerPos.y));
  1717. NewPlayerPos.x = (float)GAME_SCREEN_WIDTH;
  1718. DebugLog(Text("CROSSED EDGE Fixed position is ( %f, %f ).", NewPlayerPos.x, NewPlayerPos.y));
  1719. //Space->Init();
  1720.  
  1721. // uh?
  1722. RoomMoveDir.y = int(floor(NewPlayerPos.y / (float)GAME_SCREEN_HEIGHT));
  1723. if (bSkipWrapAroundLogic) {RoomMoveDir.y++;} // I guess?
  1724. }
  1725. }
  1726. }
  1727.  
  1728. opt_assert(!bCrossedEdge || RoomMoveDir.x != 0 || RoomMoveDir.y != 0);
  1729.  
  1730. if (bCrossedEdge &&
  1731. (RoomMoveDir.x != 0 || RoomMoveDir.y != 0))
  1732. {
  1733. if (Base->Parent)
  1734. {
  1735. Base->TryUnbase(true);
  1736.  
  1737. Space->Game_Curr = Space->Game_Prev;
  1738. }
  1739. else
  1740. {
  1741. POINT CurrRoom = NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.ActiveRoom;
  1742. POINT NewRoomCoords = NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.GetNextRoomOver(CurrRoom.x, CurrRoom.y, RoomMoveDir.x, RoomMoveDir.y);
  1743.  
  1744. DebugLog(Text(">>>> RT >>>> >>>> RT >>>> >>>> RT >>>> >>>> RT >>>> >>>> RT >>>> >>>> RT >>>> >>>> RT >>>> >>>> RT >>>>"));
  1745. DebugLog(Text(">>>> RT >>>> Transitioning from room [ %d, %d ] to room [ %d, %d ].", CurrRoom.x, CurrRoom.y, NewRoomCoords.x, NewRoomCoords.y));
  1746. DebugLog(Text(">>>> RT >>>> Position was ( %f, %f ).", Space->Game_Prev.Position.x, Space->Game_Prev.Position.y));
  1747. DebugLog(Text(">>>> RT >>>> Position is ( %f, %f ).", Space->Game_Curr.Position.x, Space->Game_Curr.Position.y));
  1748. DebugLog(Text(">>>> RT >>>> Position will be ( %f, %f ).", NewPlayerPos.x, NewPlayerPos.y));
  1749.  
  1750. while (NewPlayerPos.x < 0.0f)//ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Left)
  1751. {
  1752. NewPlayerPos.x += (float)GAME_SCREEN_WIDTH;
  1753. }
  1754. while (NewPlayerPos.x > (float)GAME_SCREEN_WIDTH)//ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Right)
  1755. {
  1756. NewPlayerPos.x -= (float)GAME_SCREEN_WIDTH;
  1757. }
  1758.  
  1759. if (!bSkipWrapAroundLogic)
  1760. {
  1761. while (NewPlayerPos.y < 0.0f)//ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Top)
  1762. {
  1763. NewPlayerPos.y += (float)GAME_SCREEN_HEIGHT;
  1764. }
  1765. while (NewPlayerPos.y > (float)GAME_SCREEN_HEIGHT)//ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Bottom)
  1766. {
  1767. NewPlayerPos.y -= (float)GAME_SCREEN_HEIGHT;
  1768. }
  1769. }
  1770. else
  1771. {
  1772. ANCHOR;
  1773. }
  1774.  
  1775. DebugLog(Text(">>>> RT >>>> Position will actually be ( %f, %f ).", NewPlayerPos.x, NewPlayerPos.y));
  1776.  
  1777. TransitionRooms(NewRoomCoords, RoomMoveDir, NewPlayerPos, NewPlayerVel, TheCrossedEdge, bAllowCheckpoint);
  1778. }
  1779. }
  1780. }
  1781.  
  1782. void Player::OnDataWritten(NEntity* OwnerEntity, NHashedString Key)
  1783. {
  1784. OwnerEntity, Key;
  1785.  
  1786. //MAKESTRING(PlayTimeSeconds);
  1787. //if (Key != PlayTimeSeconds)
  1788. //{
  1789. // if (strstr(Key.getString(), "Checkpoint") == NULL)
  1790. // {
  1791. // Cast<Player>(OwnerEntity)->CheckpointWith(Cast<Player>(OwnerEntity)->GetHandle());
  1792. // }
  1793. //}
  1794. }
  1795.  
  1796. void Player::TryCacheSafePlace(bool bForce)
  1797. {
  1798. if (bForce ||
  1799. (bCurrentSafeSpace && (PhysComp->IsGrounded() || OVERWORLD_MOTION)))
  1800. {
  1801. const ValkyrieWorld& TheWorld = ValkyrieGame::GetInstance()->TheWorld;
  1802.  
  1803. LastSafePlace.Campaign = TheWorld.ActiveCampaign;
  1804. LastSafePlace.Map = TheWorld.ActiveMap;
  1805. LastSafePlace.Room = TheWorld.ActiveRoom;
  1806. LastSafePlace.Position = Space->Game_Curr.Position;
  1807. LastSafePlace.Time = NBaseGame::GetInstance()->engAbsTime;
  1808. }
  1809. }
  1810.  
  1811. NVec3 Player::GetCameraPosition(bool bExtend, bool& bExtended)
  1812. {
  1813. NVec3 RetVal = Camera->Camera->getPos();
  1814.  
  1815. NVec2 Extents;
  1816.  
  1817. if (!OVERWORLD_MOTION)
  1818. {
  1819. Extents = NVec2(TILE_SIZE * 1.5f, TILE_SIZE * 3.5f);
  1820. }
  1821. else
  1822. {
  1823. Extents = NVec2(TILE_SIZE * 1.5f, TILE_SIZE * 1.5f);
  1824. }
  1825.  
  1826. if (bExtend)
  1827. {
  1828. float ExtendedX = (float)fabs(RetVal.x - Space->Render_Prev.Position.x);
  1829. float ExtendedY = (float)fabs(RetVal.y - Space->Render_Prev.Position.y);
  1830.  
  1831. bExtended = (ExtendedX > Extents.x) || (ExtendedY > Extents.y);
  1832.  
  1833. // Allow extents to encompass the distance between the previous camera position and the previous player position.
  1834. Extents.x = Max(Extents.x, (float)fabs(RetVal.x - Space->Render_Prev.Position.x));
  1835. Extents.y = Max(Extents.y, (float)fabs(RetVal.y - Space->Render_Prev.Position.y));
  1836.  
  1837. // Clamp to integer to prevent jitter?
  1838. // Nope, not exactly...
  1839. // This seems to work, but I can't explain why...
  1840. Extents.x = floor(Extents.x + 0.999f);
  1841. Extents.y = floor(Extents.y + 0.999f);
  1842. }
  1843.  
  1844. {
  1845. float NewCamX = RetVal.x;
  1846. float NewCamY = RetVal.y;
  1847.  
  1848. //if (!bExtend)
  1849. {
  1850. NewCamX = Clamp(NewCamX,
  1851. Space->Render_Curr.Position.x - Extents.x,
  1852. Space->Render_Curr.Position.x + Extents.x);
  1853. NewCamY = Clamp(NewCamY,
  1854. Space->Render_Curr.Position.y - Extents.y,
  1855. Space->Render_Curr.Position.y + Extents.y);
  1856. }
  1857.  
  1858. NewCamX = Clamp(NewCamX,
  1859. ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Left + (float(GAME_SCREEN_WIDTH) * 0.5f),
  1860. ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Right - (float(GAME_SCREEN_WIDTH) * 0.5f));
  1861. NewCamY = Clamp(NewCamY,
  1862. ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Top + (float(GAME_SCREEN_HEIGHT) * 0.5f),
  1863. ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Bottom - (float(GAME_SCREEN_HEIGHT) * 0.5f));
  1864.  
  1865. RetVal.x = NewCamX;
  1866. RetVal.y = NewCamY;
  1867. }
  1868.  
  1869. return RetVal;
  1870. }
  1871.  
  1872. NVec3 Player::GetDeltaToNearestAxis(const NVec3& FromPos, bool bForLeavingRoom, TransitionType InType)
  1873. {
  1874. // Choose the nearest axis to the player's position, then find the delta from the camera position to that axis.
  1875. // This may not necessarily be the nearest axis to the camera position, and that's fine.
  1876. // We want the one nearer the player in the event of a disparity.
  1877.  
  1878. NVec3 RelativeCamPos = FromPos;
  1879. NVec3 TestPos = bForLeavingRoom ? PreTransitionPosition : Space->Game_Curr.Position;
  1880.  
  1881. while (TestPos.x < 0.0f)
  1882. {
  1883. TestPos.x += float(GAME_SCREEN_WIDTH);
  1884. RelativeCamPos.x += float(GAME_SCREEN_WIDTH);
  1885. }
  1886. while (TestPos.x > float(GAME_SCREEN_WIDTH))
  1887. {
  1888. TestPos.x -= float(GAME_SCREEN_WIDTH);
  1889. RelativeCamPos.x -= float(GAME_SCREEN_WIDTH);
  1890. }
  1891. while (TestPos.y < 0.0f)
  1892. {
  1893. TestPos.y += float(GAME_SCREEN_HEIGHT);
  1894. RelativeCamPos.y += float(GAME_SCREEN_HEIGHT);
  1895. }
  1896. while (TestPos.y > float(GAME_SCREEN_HEIGHT))
  1897. {
  1898. TestPos.y -= float(GAME_SCREEN_HEIGHT);
  1899. RelativeCamPos.y -= float(GAME_SCREEN_HEIGHT);
  1900. }
  1901.  
  1902. // TestPos is now within [0,dims] on both axes.
  1903. // RelativeCamPos may not be.
  1904.  
  1905. NVec3 RetVal = RelativeCamPos;
  1906. RetVal.x = (float(GAME_SCREEN_WIDTH) * 0.5f) - RelativeCamPos.x;
  1907. RetVal.y = (float(GAME_SCREEN_HEIGHT) * 0.5f) - RelativeCamPos.y;
  1908.  
  1909. if (/*OVERWORLD_MOTION &&*/ InType == TT_JumpCut)
  1910. {
  1911. // I don't think I want axis alignment for the overworld.
  1912. // Welllllllll..... I do, but not all the time.
  1913.  
  1914. // Also maybe I can get away with not doing this for any jump cut...
  1915.  
  1916. // IF THIS CAUSES PROBLEMS IN THE FUTURE, A BETTER SOLUTION WOULD BE TO ONLY DO THIS WHEN THE JUMP CUT INVOLVES A SWITCH TO ANOTHER MAP / ENTITY / WHATEVER.
  1917. // (Also I would want to update the related code in TransitionRooms() even though it may be unnecessary. Search for "/*OVERWORLD_MOTION &&*/".)
  1918.  
  1919. RetVal = NVec3(0);
  1920. }
  1921.  
  1922. return RetVal;
  1923. }
  1924.  
  1925. void Player::OnCollision(NEntity* OwnerEntity, const NCollisionResult& ThisHitRes)
  1926. {
  1927. if (ThisHitRes.HitPrimitive)
  1928. {
  1929. CollisionLineDef* LineDef = Cast<CollisionLineDef>(ThisHitRes.HitPrimitive);
  1930. Player* ThisPlayer = Cast<Player>(OwnerEntity);
  1931.  
  1932. opt_assert(ThisPlayer);
  1933.  
  1934. if (LineDef)
  1935. {
  1936. if (LineDef->Type == VTT_Deadly)
  1937. {
  1938. ThisPlayer->Die();
  1939. }
  1940.  
  1941. if (LineDef->Type == VTT_Water ||
  1942. LineDef->Type == VTT_Toxic)
  1943. {
  1944. ThisPlayer->SetSwimmingLineDef(true, (LineDef->Type == VTT_Toxic), true, true);
  1945. }
  1946.  
  1947. if (LineDef->Type == VTT_Water_Exit)
  1948. {
  1949. ThisPlayer->SetSwimmingLineDef(false, false, true, false);
  1950. }
  1951.  
  1952. if (ThisHitRes.HitNormal.y < -0.9f)
  1953. {
  1954. if (LineDef->Type == VTT_Ice)
  1955. {
  1956. ICY_MOTION = true;
  1957. }
  1958. else if (LineDef->Type == VTT_Solid ||
  1959. LineDef->Type == VTT_Ledge ||
  1960. LineDef->Type == VTT_NoClimb ||
  1961. LineDef->Type == VTT_NoEffects)
  1962. {
  1963. ICY_MOTION = false;
  1964. }
  1965. }
  1966. else if (fabs(ThisHitRes.HitNormal.x) > 0.9f)
  1967. {
  1968. // Kill icy momentum on wall collisions.
  1969. ThisPlayer->IcyVelocity.x = 0.0f;
  1970. }
  1971. }
  1972. else
  1973. {
  1974. // Hit non-world thing.
  1975.  
  1976. if (//ThisHitRes.HitPrimitive->BlocksOther(ThisPlayer->Collision->GetPrimitiveByName()) &&
  1977. ThisHitRes.HitPrimitive->BlocksOtherResult(ThisPlayer->Collision->GetPrimitiveByName(), ThisHitRes))
  1978. {
  1979. if (ThisHitRes.HitNormal.y < -0.9f)
  1980. {
  1981. ICY_MOTION = false;
  1982. }
  1983. else if (fabs(ThisHitRes.HitNormal.x) > 0.9f)
  1984. {
  1985. // Kill icy momentum on wall collisions.
  1986. ThisPlayer->IcyVelocity.x = 0.0f;
  1987. }
  1988. }
  1989.  
  1990. // TODO: Figure out if the other thing we hit is associated with an entity,
  1991. // and if that entity has a basing component, and if we should get based on it
  1992. // or maybe vice-versa too.
  1993.  
  1994. //NCollisionInstance* CollInst = Cast<NCollisionInstance>(ThisHitRes.HitPrimitive);
  1995. //if (CollInst && CollInst->GetController())
  1996. //{
  1997. // NSpatialComponent* OtherSpace = Cast<NSpatialComponent>(CollInst->GetController()->GetCollisionOwner());
  1998. // if (OtherSpace)
  1999. // {
  2000. // if (ThisHitRes.HitNormal.y < -0.8f)
  2001. // {
  2002. // ThisPlayer->Base->TryBaseOn(OtherSpace->GetHandle());
  2003. // }
  2004. // }
  2005. //}
  2006. }
  2007. }
  2008. }
  2009.  
  2010. void Player::OnLand(NEntity* OwnerEntity, const NCollisionResult& ThisHitRes, bool bEffectsReady)
  2011. {
  2012. bool bPlayEffects = true;
  2013.  
  2014. CollisionLineDef* TestPrim = Cast<CollisionLineDef>(ThisHitRes.HitPrimitive);
  2015. if (TestPrim)
  2016. {
  2017. if (TestPrim->Type == VTT_NoEffects)
  2018. {
  2019. bPlayEffects = false;
  2020. }
  2021. }
  2022.  
  2023. ThisHitRes, bEffectsReady;
  2024.  
  2025. Player* ThisPlayer = Cast<Player>(OwnerEntity);
  2026. opt_assert(ThisPlayer);
  2027.  
  2028. ThisPlayer->LandedTime = 0.2f;
  2029.  
  2030. if (bPlayEffects)
  2031. {
  2032. ValkyrieGame::GetInstance()->MultiSynth->PlayTune(NMSTune("landing.nms", AUDPRI_LANDING, NMS_CHAN_SOUNDFX), gNullStr, NMSTU_SFX);
  2033.  
  2034.  
  2035.  
  2036. {
  2037. ValkEntity* NewEntity = NEntityFactory::BuildEntity<ValkEntity>(NHashedString("Landing.ndd"));
  2038.  
  2039. // Oops, this may still be opted-out!
  2040. if (NewEntity)
  2041. {
  2042. // TODO: Allow the game to make additional updates (e.g., for positioning Valkyrie entities relative to room coordinates).
  2043. NBaseGame::GetInstance()->GamePostBuildEntity(NewEntity);
  2044.  
  2045. NBaseGame::GetInstance()->DynamicEntities.push(NewEntity);
  2046.  
  2047. // I guess update its position to the context's position?
  2048. NSpatialComponent* ContextSpace = Resolve<NSpatialComponent>(ThisPlayer);
  2049. NSpatialComponent* EffectSpace = Resolve<NSpatialComponent>(NewEntity);
  2050. if (ContextSpace && EffectSpace)
  2051. {
  2052. // Don't alter Z.
  2053. EffectSpace->Game_Curr.Position.x = ContextSpace->Game_Curr.Position.x;
  2054. EffectSpace->Game_Curr.Position.y = ContextSpace->Game_Curr.Position.y + 6.0f;
  2055. EffectSpace->Init();
  2056. }
  2057. }
  2058. }
  2059. }
  2060.  
  2061. SpeedrunComponent* Speed = OwnerEntity->GetComponent<SpeedrunComponent>();
  2062. if (Speed)
  2063. {
  2064. Speed->TryRecordGhostEvent(true);
  2065. }
  2066. }
  2067.  
  2068. // This happens when the player initiated a jump and it was successful.
  2069. // By contrast, OnUnground() happens when the player leaves the ground and loses a jump count, even if it's by walking off a ledge.
  2070. void Player::OnJump(NEntity* OwnerEntity, int JumpNum)
  2071. {
  2072. OwnerEntity;
  2073.  
  2074. // Aaaaaaaand play SOUNDS!!
  2075. if (JumpNum == 0)
  2076. {
  2077. ValkyrieGame::GetInstance()->MultiSynth->PlayTune(NMSTune("jump.nms", AUDPRI_JUMP, NMS_CHAN_SOUNDFX), gNullStr, NMSTU_SFX);
  2078. }
  2079. else
  2080. {
  2081. ValkyrieGame::GetInstance()->MultiSynth->PlayTune(NMSTune("dbljump.nms", AUDPRI_DBLJUMP, NMS_CHAN_SOUNDFX), gNullStr, NMSTU_SFX);
  2082. }
  2083.  
  2084. SpeedrunComponent* Speed = OwnerEntity->GetComponent<SpeedrunComponent>();
  2085. if (Speed)
  2086. {
  2087. Speed->TryRecordGhostEvent(true);
  2088. }
  2089. }
  2090.  
  2091. // This happens when the player leaves the ground and loses a jump count, even if it's by walking off a ledge.
  2092. // By contrast, OnJump() happens when the player initiated a jump and it was successful.
  2093. void Player::OnUnground(NEntity* OwnerEntity)
  2094. {
  2095. // This means we either just jumped or we walked off a ledge.
  2096. // If we walked off a ledge (how do we test this? velocity?),
  2097. // set a grace period to allow us to use our first jump.
  2098.  
  2099. OwnerEntity;
  2100.  
  2101. DebugLog("############## OnUnground");
  2102.  
  2103. SpeedrunComponent* Speed = OwnerEntity->GetComponent<SpeedrunComponent>();
  2104. if (Speed)
  2105. {
  2106. Speed->TryRecordGhostEvent(true);
  2107. }
  2108. }
  2109.  
  2110. void Player::OnWallHug(NEntity* OwnerEntity, const NCollisionResult& ThisHitRes)
  2111. {
  2112. OwnerEntity;
  2113.  
  2114. bool bPlayEffects = true;
  2115.  
  2116. CollisionLineDef* TestPrim = Cast<CollisionLineDef>(ThisHitRes.HitPrimitive);
  2117. if (TestPrim)
  2118. {
  2119. if (TestPrim->Type == VTT_NoEffects)
  2120. {
  2121. bPlayEffects = false;
  2122. }
  2123. }
  2124.  
  2125. if (bPlayEffects)
  2126. {
  2127. ValkyrieGame::GetInstance()->MultiSynth->PlayTune(NMSTune("wallhug.nms", AUDPRI_WALLHUG, NMS_CHAN_SOUNDFX), gNullStr, NMSTU_SFX);
  2128. }
  2129.  
  2130. SpeedrunComponent* Speed = OwnerEntity->GetComponent<SpeedrunComponent>();
  2131. if (Speed)
  2132. {
  2133. Speed->TryRecordGhostEvent(true);
  2134. }
  2135. }
  2136.  
  2137. bool Player::CanWallHug(NEntity* OwnerEntity, const NCollisionResult& ThisHitRes)
  2138. {
  2139. OwnerEntity;
  2140.  
  2141. bool bRetVal = true;
  2142.  
  2143. CollisionLineDef* LineDef = Cast<CollisionLineDef>(ThisHitRes.HitPrimitive);
  2144. if (LineDef && LineDef->Type == VTT_NoClimb)
  2145. {
  2146. bRetVal = false;
  2147. }
  2148.  
  2149. return bRetVal;
  2150. }
  2151.  
  2152. bool Player::BlocksOther(NCollisionInterface* Self, NCollisionInterface* Other)
  2153. {
  2154. Self, Other;
  2155. return false;
  2156. }
  2157.  
  2158. bool Player::PingsOther(NCollisionInterface* Self, NCollisionInterface* Other)
  2159. {
  2160. Self;
  2161. return BlocksOther(Self, Other) || true;
  2162. }
  2163.  
  2164. bool Player::SetParam(NShaderParamMapping* Param, NHashedString ActivePass, NShaderParamInformant* Informant)
  2165. {
  2166. bool RetVal = true;
  2167.  
  2168. MAKESTRING(wvpMat);
  2169. MAKESTRING(uvMat);
  2170. MAKESTRING(AnaglyphDistance);
  2171. MAKESTRING(AnaglyphDistanceOffset);
  2172. MAKESTRING(AnaglyphColor);
  2173. MAKESTRING(AnaglyphSilhFix);
  2174. MAKESTRING(AnaglyphDesat);
  2175. MAKESTRING(AnaglyphGamma);
  2176.  
  2177. if (Param->myParamName == AnaglyphDistance)
  2178. {
  2179. float Depth = 0.0f;
  2180. AnaglyphComponent* AC = Resolve<AnaglyphComponent>(this);
  2181. if (AC) {Depth = AC->GetDepth();}
  2182. Param->SetFloatValue(0.5f * Depth * NBaseGame::GetInstance()->HACK_GetAnaglyphDistance());
  2183. }
  2184. else if (Param->myParamName == AnaglyphDistanceOffset)
  2185. {
  2186. Param->SetFloatValue(NBaseGame::GetInstance()->HACK_GetAnaglyphDistanceOffset());
  2187. }
  2188. else if (Param->myParamName == AnaglyphColor)
  2189. {
  2190. Param->SetVectorValue(NBaseGame::GetInstance()->HACK_GetAnaglyphColor());
  2191. }
  2192. else if (Param->myParamName == AnaglyphSilhFix)
  2193. {
  2194. Param->SetFloatValue(NBaseGame::GetInstance()->HACK_GetAnaglyphSilhFix() ? 1.0f : 0.0f);
  2195. }
  2196. else if (Param->myParamName == AnaglyphDesat)
  2197. {
  2198. Param->SetFloatValue(NBaseGame::GetInstance()->HACK_GetAnaglyphDesat());
  2199. }
  2200. else if (Param->myParamName == AnaglyphGamma)
  2201. {
  2202. Param->SetVectorValue(NBaseGame::GetInstance()->HACK_GetAnaglyphGamma());
  2203. }
  2204. else if (Param->myParamName == wvpMat)
  2205. {
  2206. NVec3 RelativePos = Camera->Camera->getPos();
  2207.  
  2208. Param->SetMatrixValue(Space->GetWorldMatrixQuantized(true, RelativePos) *
  2209. #ifndef _NFML
  2210. CreateTranslationMatrix(NVec3(-0.5f, -0.5f, 0.0f)) *
  2211. #endif
  2212. Camera->GetModifiedViewProj());
  2213. }
  2214. else if (Param->myParamName == uvMat)
  2215. {
  2216. NVec2 AnimPos;
  2217.  
  2218. if (!OVERWORLD_MOTION)
  2219. {
  2220. if (IsDying())
  2221. {
  2222. int Blink = 0;
  2223.  
  2224. if (DeadedTime <= 0.3f)
  2225. {
  2226. Blink = int(DeadedTime * 30.0f) % 2;
  2227. }
  2228. else
  2229. {
  2230. Blink = int(DeadedTime * 15.0f) % 2;
  2231. }
  2232.  
  2233. AnimPos.y = (16.0f / 128.0f) * 0.75f;
  2234. AnimPos.x = (float(Blink) * 16.0f / 128.0f);
  2235. }
  2236. else
  2237. {
  2238. if (OVERWORLD_MOTION || PhysComp->IsGrounded())
  2239. {
  2240. if (LandedTime > 0.0f)
  2241. {
  2242. AnimPos.y = (48.0f / 128.0f) * 0.75f;
  2243. AnimPos.x = (48.0f / 128.0f);
  2244. }
  2245. else
  2246. {
  2247. if ((!OVERWORLD_MOTION && (float)fabs(Space->Game_Curr.Velocity.x) > 0.01f) ||
  2248. (OVERWORLD_MOTION && (float)fabs(Space->Game_Curr.Velocity.lengthSquared2D()) > 0.01f))
  2249. {
  2250. AnimPos.y = (32.0f / 128.0f) * 0.75f;
  2251. AnimPos.x = (16.0f / 128.0f) * float(int(WalkingFrame) % 6);
  2252. }
  2253. else if (bLookingDown)
  2254. {
  2255. AnimPos.x = (16.0f / 128.0f);
  2256. }
  2257. else
  2258. {
  2259. // Idle. Leave zeroed.
  2260.  
  2261. if (IdleTime > 4.0f)
  2262. {
  2263. int IdleFrame = int(IdleTime * 2.0f) % 8;
  2264.  
  2265. if (IdleFrame == 6)
  2266. {
  2267. // Or go to landed:
  2268. AnimPos.y = (48.0f / 128.0f) * 0.75f;
  2269. AnimPos.x = (48.0f / 128.0f);
  2270. }
  2271. else if (IdleFrame % 2 == 0)
  2272. {
  2273. // Or to first walking frame:
  2274. AnimPos.y = (32.0f / 128.0f) * 0.75f;
  2275. AnimPos.x = 0.0f;
  2276. }
  2277. }
  2278. }
  2279. }
  2280. }
  2281. else
  2282. {
  2283. // TODO: Add a WallSliding() function?)
  2284. // (Also TODO: clarify vocabulary. Wall hugging, wall sliding, and wall jumping are three different things.)
  2285. if (PhysComp && PhysComp->IsWallSliding())
  2286. {
  2287. AnimPos.x = (32.0f / 128.0f);
  2288. AnimPos.y = (16.0f / 128.0f) * 0.75f;
  2289. }
  2290. else
  2291. {
  2292. AnimPos.y = (48.0f / 128.0f) * 0.75f;
  2293.  
  2294. float JumpScalar = 0.1f * (Space->Game_Curr.Velocity.y / float(TILE_SIZE));
  2295. float JumpFrame = 0.0f;
  2296.  
  2297. if (JumpScalar < -1.0f)
  2298. {
  2299. JumpFrame = 1.0f;
  2300. }
  2301. else if (JumpScalar > 1.0f)
  2302. {
  2303. JumpFrame = 2.0f;
  2304. }
  2305.  
  2306. AnimPos.x = (16.0f / 128.0f) * JumpFrame;
  2307. }
  2308. }
  2309. }
  2310.  
  2311. if (bFacingLeft)
  2312. {
  2313. AnimPos.y += 0.5f;
  2314. }
  2315.  
  2316. NMat4 TextureMatrix;
  2317. TextureMatrix =
  2318. CreateScaleMatrix(NVec3((16.0f / 128.0f), (16.0f / 128.0f) * 0.75f, 1.0f)) *
  2319. CreateTranslationMatrix(NVec3(AnimPos.x, AnimPos.y, 0.0f));
  2320. Param->SetMatrixValue(TextureMatrix);
  2321. }
  2322. else // OVERWORLD_MOTION
  2323. {
  2324. AnimPos.x = 0.0f;
  2325.  
  2326. if (OverworldFacing.y <= -0.707f) AnimPos.x = 0.75f;
  2327. else if (OverworldFacing.y >= 0.707f) AnimPos.x = 0.0f;
  2328. else if (OverworldFacing.x <= -0.707f) AnimPos.x = 0.5f;
  2329. else if (OverworldFacing.x >= 0.707f) AnimPos.x = 0.25f;
  2330.  
  2331. if (int(OverworldTime * 4.0f) % 2 == 0)
  2332. {
  2333. AnimPos.y += 0.5f;
  2334. }
  2335.  
  2336. NMat4 TextureMatrix;
  2337. TextureMatrix =
  2338. CreateScaleMatrix(NVec3((16.0f / 64.0f), (16.0f / 32.0f), 1.0f)) *
  2339. CreateTranslationMatrix(NVec3(AnimPos.x, AnimPos.y, 0.0f));
  2340. Param->SetMatrixValue(TextureMatrix);
  2341. }
  2342. }
  2343. else
  2344. {
  2345. RetVal = NShaderParamInformant::SetParam(Param, ActivePass, Informant);
  2346. }
  2347.  
  2348. return RetVal;
  2349. }
  2350.  
  2351. void Player::Die()
  2352. {
  2353. if (bVulnerable)
  2354. {
  2355. if (!IsDying())
  2356. {
  2357. DeadedTime = 0.6f;
  2358. SetDeaths(GetDeaths() + 1);
  2359.  
  2360. {
  2361. ValkyrieGame::GetInstance()->MultiSynth->PlayTune(NMSTune("death.nms", AUDPRI_DEATH, NMS_CHAN_SOUNDFX), gNullStr, NMSTU_SFX);
  2362. }
  2363.  
  2364. // I guess? I mean these should get repropped when we respawn if we actually want them.
  2365. bLineDefUnderwater = false;
  2366. bLineDefToxic = false;
  2367.  
  2368. // And probably these too?
  2369. //bRegionUnderwater = false;
  2370. //bRegionToxic = false;
  2371.  
  2372. IcyVelocity.x = 0.0f;
  2373.  
  2374. SpeedrunComponent* Speed = GetComponent<SpeedrunComponent>();
  2375. if (Speed)
  2376. {
  2377. Speed->IncrementDeaths();
  2378. Speed->TryRecordGhostEvent(true);
  2379. }
  2380. }
  2381. }
  2382. }
  2383.  
  2384. void Player::TeleportWith(NEntity* Other)
  2385. {
  2386. // Might be dangerous to transition mid-collision. Hrm.
  2387. // Maybe set a flag saying we want to transition next frame instead?
  2388.  
  2389. opt_assert(Other);
  2390.  
  2391. //TransitionRooms(Other->DestRoom, Other->DestLocation, Space->Game_Curr.Velocity);
  2392. }
  2393.  
  2394. void Player::TransitionRooms(POINT NewRoomCoords, POINT RoomMoveDir, const NVec3& NewPlayerPos, const NVec3& NewPlayerVel, const EdgeTransition& EdgeTrans, bool bAllowCheckpoint)
  2395. {
  2396. SpeedrunComponent* Speed = GetComponent<SpeedrunComponent>();
  2397. if (Speed)
  2398. {
  2399. Speed->TryRecordGhostEvent(true);
  2400. }
  2401.  
  2402. if (EdgeTrans.bWipeTransition &&
  2403. EdgeTrans.bHasMapName &&
  2404. EdgeTrans.bHasMapCoords &&
  2405. EdgeTrans.bHasEntityName)
  2406. {
  2407. if (EdgeTrans.bWipeAudio)
  2408. {
  2409. ValkyrieGame::GetInstance()->MultiSynth->PlayTune(NMSTune("footsteps.nms", AUDPRI_BELL /* (120) */, NMS_CHAN_SOUNDFX), gNullStr, NMSTU_SFX);
  2410. }
  2411.  
  2412. ValkyrieGame::GetInstance()->ClearDynamicEntities();
  2413. ValkyrieGame::GetInstance()->ThePlayer->GetComponent<NRenderableComponent>()->SetVisibility(false);
  2414. NSubroutineMgr::GetInstance()->FinishSubroutine(NHashedString("Door Transition"));
  2415. NSubroutineMgr::GetInstance()->RunSubroutine(new DoorTransition(EdgeTrans.MapName, EdgeTrans.MapCoords, EdgeTrans.EntityName, bAllowCheckpoint), NHashedString("Door Transition"), 0);
  2416. }
  2417. else
  2418. {
  2419. //PreTransitionPosition = Space->Game_Curr.Position;
  2420. PreTransitionPosition = Space->Game_Prev.Position;
  2421. bExtendedCamera = EdgeTrans.Type == TT_ScrollCut;//true;
  2422.  
  2423. if (EdgeTrans.bHasMapName)
  2424. {
  2425. // Load that map!
  2426. ValkyrieGame::GetInstance()->LoadMap(EdgeTrans.MapName);
  2427. }
  2428.  
  2429. POINT OrigCoords = NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.ActiveRoom;
  2430. POINT RemapFixup;
  2431. RemapFixup.x = 0;
  2432. RemapFixup.y = 0;
  2433.  
  2434. if (EdgeTrans.bHasMapCoords)
  2435. {
  2436. // Stomp whatever we expected.
  2437. NewRoomCoords = EdgeTrans.MapCoords;
  2438.  
  2439. // But we'll also need to fix up our EffCoords if we're scrolling...which we won't be. This should be fine.
  2440. //opt_assert(EdgeTrans.Type == TT_JumpCut);
  2441.  
  2442. // Hacky last-minute feature add! Definitely won't break anything! :D
  2443. if (EdgeTrans.Type == TT_ScrollCut)
  2444. {
  2445. ANCHOR;
  2446. RemapFixup.x = NewRoomCoords.x - OrigCoords.x;
  2447. RemapFixup.y = NewRoomCoords.y - OrigCoords.y;
  2448.  
  2449. RemapFixup.x -= RoomMoveDir.x;
  2450. RemapFixup.y -= RoomMoveDir.y;
  2451. }
  2452. }
  2453.  
  2454.  
  2455.  
  2456. // EffCoords is "where the new room thinks we're coming from,"
  2457. // after compensating for scroll regions and maybe scroll cuts to other rooms.
  2458. POINT EffCoords;
  2459. EffCoords.x = NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.ActiveRoom.x;
  2460. EffCoords.y = NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.ActiveRoom.y;
  2461.  
  2462. NVec3 EffPos = Space->Game_Prev.Position;
  2463.  
  2464. // Myeh, whatever.
  2465. // (I shouldn't need this fudge if I use the previous position. What I'm trying to do is figure out which room I'm actually leaving,
  2466. // since the ActiveRoom will be whichever part of the scroll region I first entered and not necessarily the one I'm in now.)
  2467. // (Still need it for crossing back from room to room unless I push the player in a bit more, which I still might.)
  2468. const float FUDGE = 0.01f;
  2469.  
  2470. EffPos.x = Clamp(EffPos.x,
  2471. ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Left + FUDGE,
  2472. ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Right - FUDGE);
  2473. EffPos.y = Clamp(EffPos.y,
  2474. ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Top + FUDGE,
  2475. ValkyrieGame::GetInstance()->TheWorld.CurrentScrollBounds.Bottom - FUDGE);
  2476.  
  2477. int dx = int(floor(EffPos.x / float(GAME_SCREEN_WIDTH)));
  2478. int dy = int(floor(EffPos.y / float(GAME_SCREEN_HEIGHT)));
  2479.  
  2480. EffCoords.x += dx;
  2481. EffCoords.y += dy;
  2482.  
  2483. EffCoords.x += RemapFixup.x;
  2484. EffCoords.y += RemapFixup.y;
  2485.  
  2486. POINT ApparentDelta;
  2487. ApparentDelta.x = NewRoomCoords.x - EffCoords.x;
  2488. ApparentDelta.y = NewRoomCoords.y - EffCoords.y;
  2489.  
  2490. DebugLog(Text(">>>> RT >>>> Clamped position is ( %f, %f ).", EffPos.x, EffPos.y));
  2491. DebugLog(Text(">>>> RT >>>> Offset on current room coords due to position in scroll region is [ %d, %d ].", dx, dy));
  2492. DebugLog(Text(">>>> RT >>>> Current room with offset is [ %d, %d ].", EffCoords, EffCoords));
  2493. DebugLog(Text(">>>> RT >>>> Apparent delta is [ %d, %d ].", ApparentDelta, ApparentDelta));
  2494.  
  2495. Space->Game_Curr.Position = NewPlayerPos;
  2496. Space->Game_Curr.Velocity = NewPlayerVel;
  2497. Space->Game_Curr.Position.z = 0.0f; // Just to be safe.
  2498. Space->Init();
  2499.  
  2500. // Note that MoveDelta doesn't correspond to camera motion because of scroll regions!
  2501. //InitialCamPos.x = float(NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.ActiveRoom.x - NewRoomCoords.x) * GAME_SCREEN_WIDTH;
  2502. //InitialCamPos.y = float(NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.ActiveRoom.y - NewRoomCoords.y) * GAME_SCREEN_HEIGHT;
  2503.  
  2504. bool bChangedRooms =
  2505. (NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.ActiveRoom.x != NewRoomCoords.x ||
  2506. NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.ActiveRoom.y != NewRoomCoords.y);
  2507.  
  2508. NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.ActiveRoom.x = NewRoomCoords.x;
  2509. NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.ActiveRoom.y = NewRoomCoords.y;
  2510.  
  2511.  
  2512.  
  2513. NVec3 CamDeltaFixup;
  2514. CamDeltaFixup.x = float((OrigCoords.x - NewRoomCoords.x) * GAME_SCREEN_WIDTH);
  2515. CamDeltaFixup.y = float((OrigCoords.y - NewRoomCoords.y) * GAME_SCREEN_HEIGHT);
  2516.  
  2517.  
  2518.  
  2519. if (EdgeTrans.bHasMapName)
  2520. {
  2521. bChangedRooms = true;
  2522. }
  2523.  
  2524. if (bChangedRooms)
  2525. {
  2526. // Might run into problems if we do the transition (which calls EndCycleMeshes()) without doing this first...
  2527. ValkyrieGame::GetInstance()->TheWorldEntity->CycleMeshes();
  2528. ValkyrieGame::GetInstance()->TheDynWorldEntity->CycleMeshes();
  2529.  
  2530. ValkyrieGame::GetInstance()->GameCamera_Alt->setPos(ValkyrieGame::GetInstance()->GameCamera->getPos());
  2531.  
  2532. NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->CompileRoom();
  2533.  
  2534. // This is kind of a dumb hack, but if we're teleporting to another room or map, we need to get its entities created NOW
  2535. // so that we can find our target.
  2536. if (EdgeTrans.bHasEntityName)
  2537. {
  2538. ValkyrieGame::GetInstance()->PostTransitionCreateEntities(true);
  2539. }
  2540. }
  2541.  
  2542.  
  2543.  
  2544. if (EdgeTrans.bHasEntityName)
  2545. {
  2546. // Find that entity and jump there.
  2547. // (Once the map's compiled and entities exist.)
  2548. for (int i = 0; i < NBaseGame::GetInstance()->DynamicEntities.size(); ++i)
  2549. {
  2550. NNameComponent* EntName = NBaseGame::GetInstance()->DynamicEntities[i]->GetComponent<NNameComponent>();
  2551. if (EntName && EntName->MyName == EdgeTrans.EntityName)
  2552. {
  2553. NSpatialComponent* EntSpace = NBaseGame::GetInstance()->DynamicEntities[i]->GetComponent<NSpatialComponent>();
  2554. if (EntSpace)
  2555. {
  2556. Space->Game_Curr.Position = EntSpace->Game_Curr.Position;
  2557. Space->Game_Curr.Position.z = 0.0f; // derp
  2558. Space->Init();
  2559.  
  2560. // Aaaaand if we've just teleported, try to ground the player.
  2561. // Probably generalize this so I can do it on game start (if I want)
  2562. // or really whenever.
  2563. TryGround();
  2564. }
  2565. }
  2566. }
  2567. }
  2568. else if (EdgeTrans.bHasRoomCoords)
  2569. {
  2570. // Move to those coords.
  2571. }
  2572.  
  2573.  
  2574.  
  2575. // This is a REALLY gross hack, but to suppress a one-frame flicker when going to the overworld, do this.
  2576. // (And hope this won't mess up other overworld transitions!)
  2577. // Actually, maybe I want this for any jump cut. :/
  2578. if (/*OVERWORLD_MOTION &&*/ EdgeTrans.Type == TT_JumpCut)
  2579. {
  2580. // Actually, not sure this is necessary at all, but it doesn't seem to hurt...
  2581.  
  2582. InitCamera();
  2583. //Camera->Camera->setPos(GetCameraPosition(bExtendedCamera, bExtendedCamera));
  2584. }
  2585.  
  2586.  
  2587.  
  2588. NVec3 EffCamDeltaFixup;
  2589. if (ApparentDelta.x == 0)
  2590. {
  2591. EffCamDeltaFixup += (CamDeltaFixup * NVec3(1.0f, 0.0f, 0.0f));
  2592. }
  2593. if (ApparentDelta.y == 0)
  2594. {
  2595. EffCamDeltaFixup += (CamDeltaFixup * NVec3(0.0f, 1.0f, 0.0f));
  2596. }
  2597. Camera->Camera->setPos(Camera->Camera->getPos() + EffCamDeltaFixup);
  2598.  
  2599.  
  2600.  
  2601. // Just in case...
  2602. NSubroutineMgr::GetInstance()->FinishSubroutine(NHashedString("Room Transition"));
  2603.  
  2604. // I guess always do the transition even if we didn't change rooms, just in case we want to do
  2605. // the Zelda "Lost Woods" trick of scrolling back into the room we were just in.
  2606. NSubroutineMgr::GetInstance()->RunSubroutine(new Transition(EdgeTrans.Type, ApparentDelta, EffCamDeltaFixup, bAllowCheckpoint), NHashedString("Room Transition"), 0);
  2607.  
  2608. // Maybe?
  2609. // TODO: Test first to make sure we're not saving on a vertical transition. Or at least not an upward one. Or if we are, save our velocity.
  2610. if (bAllowCheckpoint)
  2611. {
  2612. CheckpointWith(GetHandle(), false/*, true*/);
  2613. }
  2614. }
  2615. }
  2616. //
  2617. //void Player::SaveGame()
  2618. //{
  2619. // //if (!NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->IsYOLOMode())
  2620. // {
  2621. // // No support for anything but an autosave currently.
  2622. // // Not sure if care.
  2623. // char SaveGamePath[MAX_PATH];
  2624. // GetLocalAppDataPath(SaveGamePath, NCore::GetInstance()->GetLocalAppDataPathName(), "Saves" FOLDER_SEPARATOR);
  2625. // strcat_s(SaveGamePath, MAX_PATH, "autosave.vsg");
  2626. //
  2627. //
  2628. // FILE* File;
  2629. // fopen_s(&File, SaveGamePath, "wb");
  2630. //
  2631. //
  2632. //
  2633. // fwrite(&CURRENT_SAVE_FILE_VERSION, sizeof(int), 1, File);
  2634. //
  2635. //
  2636. //
  2637. // SYSTEMTIME LocalTime;
  2638. //
  2639. //#ifdef _PLAT_WIN
  2640. // GetLocalTime(&LocalTime);
  2641. //#else
  2642. // // TODO: NFML TIME. Generalize this conversion (both directions) maybe?
  2643. // std::time_t StdTime = std::time(0);
  2644. // std::tm* StdLocalTime = std::localtime(&StdTime);
  2645. // StdLocalTime;
  2646. //
  2647. // LocalTime.wYear = StdLocalTime->tm_year + 1900;
  2648. // LocalTime.wMonth = StdLocalTime->tm_mon;
  2649. // LocalTime.wDayOfWeek = StdLocalTime->tm_wday;
  2650. // LocalTime.wDay = StdLocalTime->tm_mday;
  2651. // LocalTime.wHour = StdLocalTime->tm_hour;
  2652. // LocalTime.wMinute = StdLocalTime->tm_min;
  2653. // LocalTime.wSecond = StdLocalTime->tm_sec;
  2654. // LocalTime.wMilliseconds = 0;
  2655. //#endif
  2656. //
  2657. // fwrite(&LocalTime, sizeof(SYSTEMTIME), 1, File);
  2658. //
  2659. // NString CampaignFile = NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->TheWorld.CampaignFilename;
  2660. //
  2661. // int CampFileLen = CampaignFile.Length() + 1;
  2662. // fwrite(&CampFileLen, sizeof(int), 1, File);
  2663. // fwrite(*CampaignFile, sizeof(char) * CampFileLen, 1, File);
  2664. //
  2665. //
  2666. //
  2667. // fwrite(&PlayTime, sizeof(float), 1, File);
  2668. // fwrite(&Deaths, sizeof(int), 1, File);
  2669. //
  2670. // int MapStrLen = (int)strlen(RespawnMap.getString()) + 1;
  2671. // fwrite(&MapStrLen, sizeof(int), 1, File);
  2672. // fwrite(RespawnMap.getString(), sizeof(char), MapStrLen, File);
  2673. //
  2674. // fwrite(&RespawnRoom, sizeof(POINT), 1, File);
  2675. // fwrite(&RespawnPoint, sizeof(NVec3), 1, File);
  2676. // //fwrite(&bHasDoubleJump, sizeof(bool), 1, File);
  2677. // //fwrite(&bHasWallJump, sizeof(bool), 1, File);
  2678. //
  2679. // nfclose(File);
  2680. //
  2681. //
  2682. //
  2683. //
  2684. //
  2685. //
  2686. //
  2687. // // Aaaaand spawn the save effect.
  2688. // /*
  2689. // if (NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->SHOW_SAVE_ICON)
  2690. // {
  2691. // Effect* NewEffect = new Effect();
  2692. // NewEffect->Space->Game_Curr.Position = NVec3((float)(CRT_WIDTH) - 16.0f, 16.0f, 0.0f);
  2693. //
  2694. // ((NSprite*)(NewEffect->Sprite->GetRenderableByName()))->LoadTex(NString("savedisk.bmp"), NHashedString("Texture_SaveDisk"));
  2695. //
  2696. // NewEffect->Space->Game_Curr.Scale = NVec3(16.0f, 16.0f, 1.0f);
  2697. //
  2698. // NewEffect->Space->Init();
  2699. //
  2700. // NewEffect->Anim->AnimFrames = 8;
  2701. // NewEffect->Anim->AnimDuration = 0.6f;
  2702. //
  2703. // NBaseGame::GetInstance()->DynamicEntities.push(NewEffect);
  2704. // }
  2705. // */
  2706. // }
  2707. //}
  2708. //
  2709. //void Player::LoadGame()
  2710. //{
  2711. // // Always disable this when loading!!
  2712. // //NBaseGame::GetInstance()->GetGameType<ValkyrieGame>()->SetYOLOMode(false);
  2713. //
  2714. // // No support for anything but an autosave currently.
  2715. // // Not sure if care.
  2716. // char SaveGamePath[MAX_PATH];
  2717. // GetLocalAppDataPath(SaveGamePath, NCore::GetInstance()->GetLocalAppDataPathName(), "Saves" FOLDER_SEPARATOR);
  2718. // strcat_s(SaveGamePath, MAX_PATH, "autosave.vsg");
  2719. //
  2720. // if (FileExists(SaveGamePath))
  2721. // {
  2722. // FILE* File;
  2723. // fopen_s(&File, SaveGamePath, "rb");
  2724. //
  2725. // // IF THIS CHANGES, ValkyrieTitleScreenMenu::GetSaveGameInfo MAY NEED TO CHANGE TOO!!
  2726. //
  2727. //
  2728. //
  2729. // int SaveVersion = 0;
  2730. // fread_s(&SaveVersion, sizeof(int), sizeof(int), 1, File);
  2731. //
  2732. //
  2733. //
  2734. //// if (SaveVersion != CURRENT_SAVE_FILE_VERSION)
  2735. //// {
  2736. //// NMessageBox::Show("Save file version does not match expected value.", "Warning", MBS_Warning);
  2737. //// nfclose(File);
  2738. //// ClearStatsForNewGame(); // I guess?
  2739. //// return;
  2740. //// }
  2741. //
  2742. //
  2743. //
  2744. // SYSTEMTIME LocalTime;
  2745. // fread_s(&LocalTime, sizeof(SYSTEMTIME), sizeof(SYSTEMTIME), 1, File);
  2746. //
  2747. // int CampFileLen = -1;
  2748. // fread_s(&CampFileLen, sizeof(int), sizeof(int), 1, File);
  2749. // opt_assert(CampFileLen >= 0);
  2750. // char* buffer = new char[CampFileLen];
  2751. // fread_s(buffer, sizeof(char) * CampFileLen, sizeof(char), CampFileLen, File);
  2752. // NString CampaignFile = buffer;
  2753. // SafeDeleteArray(buffer);
  2754. //
  2755. //
  2756. //
  2757. // fread_s(&PlayTime, sizeof(float), sizeof(float), 1, File);
  2758. // fread_s(&Deaths, sizeof(int), sizeof(int), 1, File);
  2759. //
  2760. // if (SaveVersion > 2)
  2761. // {
  2762. // int MapStrLen = -1;
  2763. // fread_s(&MapStrLen, sizeof(int), sizeof(int), 1, File);
  2764. // static char MapStrBuffer[256] = {0};
  2765. // fread_s(MapStrBuffer, sizeof(char) * MapStrLen, sizeof(char), MapStrLen, File);
  2766. // RespawnMap = NHashedString(MapStrBuffer);
  2767. // }
  2768. //
  2769. // fread_s(&RespawnRoom, sizeof(POINT), sizeof(POINT), 1, File);
  2770. // fread_s(&RespawnPoint, sizeof(NVec3), sizeof(NVec3), 1, File);
  2771. // //fread_s(&bHasDoubleJump, sizeof(bool), sizeof(bool), 1, File);
  2772. // //fread_s(&bHasWallJump, sizeof(bool), sizeof(bool), 1, File);
  2773. //
  2774. // nfclose(File);
  2775. //
  2776. //
  2777. //
  2778. // SetUpPhysComp();
  2779. // }
  2780. //}
  2781.  
  2782. void Player::ClearAllData()
  2783. {
  2784. NDataComponent* DataComp = GetComponent<NDataComponent>();
  2785. if (DataComp)
  2786. {
  2787. DataComp->Clear();
  2788. }
  2789.  
  2790. MinimapComponent* MiniComp = GetComponent<MinimapComponent>();
  2791. if (MiniComp)
  2792. {
  2793. MiniComp->ClearData();
  2794. }
  2795.  
  2796. // And initialize powerups to suppress missing data asserts.
  2797. SetHasAllPowerups(false);
  2798.  
  2799.  
  2800.  
  2801. // And make sure this default stuff exists too.
  2802. MAKESTRING(CheckpointCampaign);
  2803. MAKESTRING(CheckpointMap);
  2804. MAKESTRING(CheckpointRoomX);
  2805. MAKESTRING(CheckpointRoomY);
  2806. MAKESTRING(CheckpointPositionX);
  2807. MAKESTRING(CheckpointPositionY);
  2808. MAKESTRING(PlayTimeSeconds);
  2809. MAKESTRING(DeathsCount);
  2810. DataComp->CreateMappedValue(CheckpointCampaign);
  2811. DataComp->CreateMappedValue(CheckpointMap);
  2812. DataComp->CreateMappedValue(CheckpointRoomX);
  2813. DataComp->CreateMappedValue(CheckpointRoomY);
  2814. DataComp->CreateMappedValue(CheckpointPositionX);
  2815. DataComp->CreateMappedValue(CheckpointPositionY);
  2816. DataComp->CreateMappedValue(PlayTimeSeconds);
  2817. DataComp->CreateMappedValue(DeathsCount);
  2818. SetCheckpointPosition(NVec2((float)HALF_CRT_WIDTH, (float)HALF_CRT_HEIGHT), false);
  2819. SetCheckpointPosition(NVec2((float)HALF_CRT_WIDTH, (float)HALF_CRT_HEIGHT), true);
  2820. POINT DefRoom;
  2821. DefRoom.x = 0;
  2822. DefRoom.y = 0;
  2823. SetCheckpointRoom(DefRoom, false);
  2824. SetCheckpointMap(gNullStr, false);
  2825. SetCheckpointCampaign(gNullStr, false);
  2826. SetCheckpointRoom(DefRoom, true);
  2827. SetCheckpointMap(gNullStr, true);
  2828. SetCheckpointCampaign(gNullStr, true);
  2829. SetDeaths(0);
  2830. SetPlayTime(0.0f);
  2831. SetEnteredCode(bEnteredCode || GetEnteredCode());
  2832. }
  2833.  
  2834. void Player::SetUpPhysComp()
  2835. {
  2836. if (PhysComp)
  2837. {
  2838. // Does this make sense to generalize for other games?
  2839.  
  2840. // TODO: Make these go through setter functions.
  2841.  
  2842. int CurrMaxJumps = PhysComp->GetMaxJumps();
  2843.  
  2844. PhysComp->SetMaxJumps(HasPowerup(VPU_Boots_DoubleJump) ? 2 : 1);
  2845. PhysComp->SetCanWallJump(HasPowerup(VPU_Gloves_Climbing));
  2846.  
  2847. int JumpsDiff = PhysComp->GetMaxJumps() - CurrMaxJumps;
  2848. for (int i = 0; i < JumpsDiff; ++i)
  2849. {
  2850. PhysComp->IncrementJumpsRemaining();
  2851. }
  2852. }
  2853. }
  2854.  
  2855. bool Player::IsTransitioning()
  2856. {
  2857. MAKESTRINGWITH(RoomTransSubName, "Room Transition");
  2858. MAKESTRINGWITH(DoorTransSubName, "Door Transition");
  2859. return
  2860. NSubroutineMgr::GetInstance()->IsSubroutineRunning(RoomTransSubName) ||
  2861. NSubroutineMgr::GetInstance()->IsSubroutineRunning(DoorTransSubName);
  2862. }
  2863.  
  2864. bool Player::IsIntroShowing()
  2865. {
  2866. MAKESTRINGWITH(SubName, "Intro Splash Cinematic");
  2867. return NSubroutineMgr::GetInstance()->IsSubroutineRunning(SubName);
  2868. }
  2869.  
  2870. bool Player::IsIndisposed()
  2871. {
  2872. return (
  2873. IsDying() ||
  2874. IsTransitioning() ||
  2875. IsIntroShowing() ||
  2876. (NMenuMgr::GetInstance()->IsAnyMenuOpen())
  2877. );
  2878. }
  2879.  
  2880. void Player::InitCamera()
  2881. {
  2882. bool bDummy = false;
  2883. Camera->Camera->setPos((Space->Render_Curr.Position * NVec3(1.0f,1.0f,0.0f)) + NVec3(0,0,-17.0f));
  2884. Camera->Camera->setPos(GetCameraPosition(false, bDummy));
  2885. bExtendedCamera = false;
  2886. }
  2887.  
  2888. void Player::InitUnderwater()
  2889. {
  2890. // See whether we spawned underwater!
  2891. // (Could probably actually use the player's current location instead of these respawn values, but whatever.)
  2892. // SHOULD probably use the player's current location; these values will be out of date when switching maps!!
  2893.  
  2894. //ValkyrieTileType SpawnTile =
  2895. // ValkyrieGame::GetInstance()->TheWorld.GetCellType(
  2896. // GetCheckpointRoom().x, GetCheckpointRoom().y,
  2897. // (int)(GetCheckpointPosition().x / (float)TILE_SIZE),
  2898. // (int)(GetCheckpointPosition().y / (float)TILE_SIZE));
  2899.  
  2900. const ValkyrieWorld& TheWorld = ValkyrieGame::GetInstance()->TheWorld;
  2901.  
  2902. ValkyrieTileType SpawnTile =
  2903. ValkyrieGame::GetInstance()->TheWorld.GetCellType(
  2904. TheWorld.ActiveRoom.x, TheWorld.ActiveRoom.y,
  2905. (int)(Space->Game_Curr.Position.x / (float)TILE_SIZE),
  2906. (int)(Space->Game_Curr.Position.y / (float)TILE_SIZE));
  2907.  
  2908. bool bUnderwater = (SpawnTile == VTT_Water || SpawnTile == VTT_Toxic);
  2909. bool bToxic = (SpawnTile == VTT_Toxic);
  2910. SetSwimmingLineDef(bUnderwater, bToxic);
  2911. }
  2912.  
  2913. void Player::SetOverworld(bool bNewOverworld)
  2914. {
  2915. if (bNewOverworld != OVERWORLD_MOTION)
  2916. {
  2917. OVERWORLD_MOTION = bNewOverworld;
  2918.  
  2919. SetOverworldProps();
  2920. }
  2921. }
  2922.  
  2923. void Player::SetOverworldProps()
  2924. {
  2925. if (!OVERWORLD_MOTION)
  2926. {
  2927. Space->Game_Curr.Scale = NVec3(16.0f, 24.0f, 1.0f);
  2928. }
  2929. else
  2930. {
  2931. Space->Game_Curr.Scale = NVec3(16.0f, 16.0f, 1.0f);
  2932. }
  2933.  
  2934. if (!OVERWORLD_MOTION)
  2935. {
  2936. NPhysicsGlobals::SetWorldUp(NVec3(0,-1,0));
  2937. }
  2938. else
  2939. {
  2940. // Gravity is "into the screen" on the overworld.
  2941. NPhysicsGlobals::SetWorldUp(NVec3(0,0,1));
  2942. }
  2943.  
  2944. if (!OVERWORLD_MOTION)
  2945. {
  2946. SetSwimmingProps();
  2947. }
  2948. else
  2949. {
  2950. PhysComp->InitConstants(MOVEMENT_SPEED, NULL, 0, 0.0f, 0.0f, 0.0f);
  2951. }
  2952.  
  2953. // Maybe?
  2954. PhysComp->OnSpawn(false);
  2955.  
  2956. NCollisionBox* Bounds = Cast<NCollisionBox>(Collision->GetPrimitiveByName());
  2957. if (Bounds)
  2958. {
  2959. if (!OVERWORLD_MOTION)
  2960. {
  2961. Bounds->Extents = NVec3(5.0f, 8.0f, 1.0f);
  2962. }
  2963. else
  2964. {
  2965. // Can't make it an even 8 because of whatever.
  2966. //Bounds->Extents = NVec3(7.95f, 7.95f, 1.0f);
  2967.  
  2968. // For tile-at-a-time movement, make this smaller.
  2969. Bounds->Extents = NVec3(4.0f, 4.0f, 1.0f);
  2970. }
  2971. }
  2972.  
  2973. PlayerSprite* TheSprite = ((PlayerSprite*)(Sprite->GetRenderableByName()));
  2974. if (TheSprite)
  2975. {
  2976. if (!OVERWORLD_MOTION)
  2977. {
  2978. TheSprite->SetSprite("capmansheetredux.bmp", NHashedString("Texture_PlayerSprite_Default"));
  2979. }
  2980. else
  2981. {
  2982. TheSprite->SetSprite("capmanmapsheet.bmp", NHashedString("Texture_PlayerSprite_Overworld"));
  2983. }
  2984. }
  2985.  
  2986. OverworldFacing = NVec2(0,1);
  2987. PrevOverworldFacing = OverworldFacing;
  2988. OverworldState = OMS_Idle;
  2989. OverWalk_Duration = 0.25f; // Based on Zelda 2 rates.
  2990. OverWalk_Elapsed = 0.0f;
  2991.  
  2992. if (OVERWORLD_MOTION)
  2993. {
  2994. // I think this is right...
  2995. //InitCamera();
  2996. }
  2997.  
  2998. if (OVERWORLD_MOTION)
  2999. {
  3000. // Set a brief delay to prevent walking right after going to the map.
  3001. OverworldDelay = OVERWORLD_DELAY;
  3002. }
  3003. else
  3004. {
  3005. OverworldDelay = -1.0f;
  3006. }
  3007.  
  3008. if (OVERWORLD_MOTION)
  3009. {
  3010. IcyVelocity.x = 0.0f;
  3011. }
  3012. }
  3013.  
  3014. void Player::SetSwimmingLineDef(bool bSwimming, bool bToxic, bool bEffects, bool bEntering)
  3015. {
  3016. bLineDefUnderwater = bSwimming;
  3017. bLineDefToxic = bSwimming ? bToxic : false;
  3018.  
  3019. SetSwimming(bEffects, bEntering);
  3020. }
  3021.  
  3022. void Player::SetSwimmingRegion(bool bSwimming, bool bToxic)
  3023. {
  3024. bRegionUnderwater = bSwimming;
  3025. bRegionToxic = bSwimming ? bToxic : false;
  3026.  
  3027. SetSwimming();
  3028. }
  3029.  
  3030. void Player::SetSwimming(bool bEffects, bool bEntering)
  3031. {
  3032. bool bSwimming = bLineDefUnderwater || bRegionUnderwater;
  3033. bool bToxic = bLineDefToxic || bRegionToxic;
  3034.  
  3035. if (bSwimming != SWIMMING_MOTION)
  3036. {
  3037. SWIMMING_MOTION = bSwimming;
  3038. SetSwimmingProps();
  3039.  
  3040. if (bEffects)
  3041. {
  3042. PlaySplashEffects(bEntering);
  3043. }
  3044. }
  3045.  
  3046. if ((bSwimming && !HasPowerup(VPU_Suit_Swimming)) ||
  3047. (bToxic && !HasPowerup(VPU_Suit_Toxic)))
  3048. {
  3049. Die();
  3050. }
  3051. }
  3052.  
  3053. void Player::SetSwimmingProps()
  3054. {
  3055. // 1.12x multiplier means jumps now reach:
  3056. // First jump: 4.704
  3057. // Second jump: 3.024
  3058. // Total jump: 7.728
  3059. // That's probably okay?
  3060.  
  3061. // With 1.5x on top of that,
  3062. // First: 7.056
  3063. // Second: 4.536
  3064. // Total: 11.592
  3065.  
  3066. float JumpHeights[NUM_JUMPS] =
  3067. {(4.20f * 8.0f) * ASPECT_RATIO_FIXUP_Y * SIZE_FIXUP_Y,
  3068. (2.70f * 8.0f) * ASPECT_RATIO_FIXUP_Y * SIZE_FIXUP_Y};
  3069.  
  3070. float SwimmingJumpHeights[NUM_JUMPS] =
  3071. {(4.20f * 8.0f) * ASPECT_RATIO_FIXUP_Y * SIZE_FIXUP_Y * 2.0f,
  3072. (2.70f * 8.0f) * ASPECT_RATIO_FIXUP_Y * SIZE_FIXUP_Y * 2.0f};
  3073.  
  3074. float PreChangeVertVel = Space->Game_Curr.Velocity.y;
  3075. float PreChangeTermVel = PhysComp->GetTerminalVelocity();
  3076.  
  3077. if (!OVERWORLD_MOTION)
  3078. {
  3079. if (SWIMMING_MOTION)
  3080. {
  3081. PhysComp->InitConstants(SWIMMING_MOVEMENT_SPEED, SwimmingJumpHeights, NUM_JUMPS, JUMP_TIME * 4.0f, 0.25f, 0.0f);
  3082. }
  3083. else
  3084. {
  3085. PhysComp->InitConstants(MOVEMENT_SPEED, JumpHeights, NUM_JUMPS, JUMP_TIME, 0.5f, 0.0f);
  3086. }
  3087. }
  3088.  
  3089. float PostChangeVertVel = Space->Game_Curr.Velocity.y;
  3090. float PostChangeTermVel = PhysComp->GetTerminalVelocity();
  3091.  
  3092. float NewVertVel = PreChangeVertVel * PostChangeTermVel;
  3093. if (fabs(PreChangeTermVel) > 0.0f)
  3094. {
  3095. NewVertVel = NewVertVel / PreChangeTermVel;
  3096. }
  3097. PostChangeVertVel;
  3098. NewVertVel;
  3099.  
  3100. //if (PreChangeVertVel < 0.0 && NewVertVel < 0.0f && NewVertVel < PreChangeVertVel)
  3101. {
  3102. // Increase speed jumping out of water.
  3103. const float CHANGE_PERCENT = 0.65f;
  3104. Space->Game_Curr.Velocity.y = Lerp(PostChangeVertVel, NewVertVel, CHANGE_PERCENT);
  3105. //Space->Init();
  3106. }
  3107.  
  3108. ANCHOR;
  3109. }
  3110.  
  3111. // Don't actually start playing the tune here.
  3112. // Instead (assuming the song changed), silence the music channel for a brief time, THEN play the song.
  3113. void Player::SetBGMusic(const NString& BGMusicName)
  3114. {
  3115. // TODO: Don't restart the song if we're attempting to play the same one that's currently playing!
  3116. if (BGMusicName != CurrentBGMusicName)
  3117. {
  3118. BGMusicSwitchDelay = BGMUSIC_SWITCH_DELAY;
  3119. CurrentBGMusicName = BGMusicName;
  3120.  
  3121. //ValkyrieGame::GetInstance()->MultiSynth->ClearTunes();
  3122.  
  3123. MAKESTRING(BGMusicTune);
  3124. ValkyrieGame::GetInstance()->MultiSynth->StopTune(BGMusicTune);
  3125. }
  3126. }
  3127.  
  3128. void Player::SetSpeedrun(bool bSpeedrun, bool bSpeedrunActive, NHashedString CourseID)
  3129. {
  3130. bSpeedrun, bSpeedrunActive, CourseID;
  3131.  
  3132. // bSpeedrun controls whether we show the speedrun status menu, the timer, etc.
  3133. // bSpeedrunActive controls whether the timer is running.
  3134.  
  3135. SpeedrunComponent* Speed = GetComponent<SpeedrunComponent>();
  3136. if (Speed)
  3137. {
  3138. Speed->SetProps(bSpeedrun, bSpeedrunActive, CourseID);
  3139. }
  3140. }
  3141.  
  3142. void Player::SetBuildMinimap(bool bBuildMinimap, const NString& MinimapID)
  3143. {
  3144. bBuildMinimap; // Unused now.
  3145.  
  3146. bBuildMinimap, MinimapID;
  3147.  
  3148. MinimapComponent* MiniComp = GetComponent<MinimapComponent>();
  3149. if (MiniComp)
  3150. {
  3151. if (ValkyrieGame::GetInstance()->TheMinimapBackMesh &&
  3152. ValkyrieGame::GetInstance()->TheMinimapMesh &&
  3153. ValkyrieGame::GetInstance()->TheMinimapIndicatorMesh)
  3154. {
  3155. ValkyrieGame::GetInstance()->TheMinimapBackMesh->visible = false;
  3156. ValkyrieGame::GetInstance()->TheMinimapMesh->visible = false;
  3157. ValkyrieGame::GetInstance()->TheMinimapIndicatorMesh->visible = false;
  3158. ValkyrieGame::GetInstance()->bHasMinimap = false;
  3159. }
  3160.  
  3161. MiniComp->InvalidateID();
  3162.  
  3163. MAKESTRINGWITH(MinimapsDef, "Minimaps.ndd");
  3164. const NDefFileNode& TopNode = NDefMgr::GetInstance()->GetDefMapRef(MinimapsDef);
  3165. TopNode;
  3166.  
  3167. if (!TopNode.bIsDummy)
  3168. {
  3169. MAKESTRING(Collections);
  3170. MAKESTRING(Collection);
  3171. MAKESTRING(ID);
  3172. MAKESTRING(LocFile);
  3173. MAKESTRING(LocSection);
  3174. MAKESTRING(LocKey);
  3175. MAKESTRING(Minimaps);
  3176. MAKESTRING(Minimap);
  3177. MAKESTRING(Name);
  3178. MAKESTRING(CollID);
  3179. MAKESTRING(OX);
  3180. MAKESTRING(OY);
  3181. MAKESTRING(W);
  3182. MAKESTRING(H);
  3183. MAKESTRING(PX);
  3184. MAKESTRING(PY);
  3185. MAKESTRING(Base);
  3186. MAKESTRING(Overlay);
  3187.  
  3188. //MiniComp->MapIDToCollID.clear();
  3189. for (NDefFileNodeIterator It = TopNode.GetChildByName(Collections).IterBegin(Collection); It(); It.Advance())
  3190. {
  3191. // TODO: Load collections
  3192. }
  3193.  
  3194. for (NDefFileNodeIterator It = TopNode.GetChildByName(Minimaps).IterBegin(Minimap); It(); It.Advance())
  3195. {
  3196. if (It.Get().KeyValues.exists(Name) &&
  3197. It.Get().KeyValues[Name] ^ MinimapID)
  3198. {
  3199.  
  3200.  
  3201.  
  3202. //if ((It.Get().KeyValues.exists(Name))
  3203. //{
  3204. // NHashedString(*(It.Get().KeyValues[CollID]))
  3205. // MiniComp->MapIDToCollID[] = ;
  3206. //}
  3207.  
  3208.  
  3209.  
  3210.  
  3211. if (It.Get().KeyValues.exists(OX) &&
  3212. It.Get().KeyValues.exists(OY) &&
  3213. It.Get().KeyValues.exists(Base) &&
  3214. It.Get().KeyValues.exists(Overlay))
  3215. {
  3216. NString BaseBMP = It.Get().KeyValues[Base];
  3217. NString OverlayBMP = It.Get().KeyValues[Overlay];
  3218. int OffsetX = It.Get().KeyValues[OX].GetInt();
  3219. int OffsetY = It.Get().KeyValues[OY].GetInt();
  3220.  
  3221. int Width = -1;
  3222. int Height = -1;
  3223. int PosX = -1;
  3224. int PosY = -1;
  3225. if (It.Get().KeyValues.exists(W) &&
  3226. It.Get().KeyValues.exists(H) &&
  3227. It.Get().KeyValues.exists(PX) &&
  3228. It.Get().KeyValues.exists(PY))
  3229. {
  3230. Width = It.Get().KeyValues[W].GetInt();
  3231. Height = It.Get().KeyValues[H].GetInt();
  3232. PosX = It.Get().KeyValues[PX].GetInt();
  3233. PosY = It.Get().KeyValues[PY].GetInt();
  3234. }
  3235.  
  3236. // TODO: Load collection ID.
  3237.  
  3238. MiniComp->Init(BaseBMP, OverlayBMP, OffsetX, OffsetY, Width, Height, PosX, PosY, NHashedString(*MinimapID));
  3239.  
  3240. if (ValkyrieGame::GetInstance()->TheMinimapBackMesh &&
  3241. ValkyrieGame::GetInstance()->TheMinimapMesh &&
  3242. ValkyrieGame::GetInstance()->TheMinimapIndicatorMesh)
  3243. {
  3244. ValkyrieGame::GetInstance()->TheMinimapBackMesh->visible = ValkyrieGame::GetInstance()->MINIMAP_VISIBLE;
  3245. ValkyrieGame::GetInstance()->TheMinimapMesh->visible = ValkyrieGame::GetInstance()->MINIMAP_VISIBLE;
  3246. ValkyrieGame::GetInstance()->TheMinimapIndicatorMesh->visible = ValkyrieGame::GetInstance()->MINIMAP_VISIBLE;
  3247.  
  3248. ValkyrieGame::GetInstance()->bHasMinimap = true;
  3249. }
  3250. }
  3251. }
  3252. }
  3253. }
  3254. }
  3255.  
  3256. /*
  3257. NString MapName = ValkyrieGame::GetInstance()->TheWorld.CurrentMapName;
  3258. MapName;
  3259.  
  3260. //ValkyrieGame::GetInstance()->TheWorld.ActiveMap;
  3261. POINT Room = ValkyrieGame::GetInstance()->TheWorld.ActiveRoom;
  3262. Room;
  3263.  
  3264. ANCHOR;
  3265.  
  3266. //if (bBuildMinimap)
  3267. {
  3268. // We need to walk the rooms and build a map.
  3269.  
  3270. ANCHOR;
  3271.  
  3272. // Really just a set.
  3273. //NUtilMap<NUtilMap<uint, int>, int> WalkedRooms;
  3274.  
  3275. //RecBuildMinimap(WalkedRooms, Room);
  3276.  
  3277. int MinX = 1000;
  3278. int MaxX = -1000;
  3279. int MinY = 1000;
  3280. int MaxY = -1000;
  3281.  
  3282. const ValkyrieWorld& TheWorld = ValkyrieGame::GetInstance()->TheWorld;
  3283. const NUtilMap<NUtilMap<ValkyrieRoom, int>, int>& Rooms = TheWorld.Rooms;
  3284.  
  3285. //for (NUtilMapIter<NUtilMap<uint, int>, int> It1 = WalkedRooms.first(); It1(); It1.next())
  3286. for (NUtilMapIter<NUtilMap<ValkyrieRoom, int>, int> It1 = Rooms.first(); It1(); It1.next())
  3287. {
  3288. MinX = Min(MinX, It1.getKey());
  3289. MaxX = Max(MaxX, It1.getKey());
  3290. for (NUtilMapIter<ValkyrieRoom, int> It2 = (*It1).first(); It2(); It2.next())
  3291. {
  3292. MinY = Min(MinY, It2.getKey());
  3293. MaxY = Max(MaxY, It2.getKey());
  3294. }
  3295. }
  3296.  
  3297. int CenterX = (MinX + MaxX) / 2;
  3298. int CenterY = (MinY + MaxY) / 2;
  3299.  
  3300. int Width = (MaxX - MinX) + 1;
  3301. int Height = (MaxY - MinY) + 1;
  3302.  
  3303. CenterX, CenterY, Width, Height;
  3304.  
  3305. int X1 = -(Width / 2);
  3306. int Y1 = -(Height / 2);
  3307. int X2 = X1 + Width - 1;
  3308. int Y2 = Y1 + Height - 1;
  3309.  
  3310. X1, X2, Y1, Y2;
  3311.  
  3312. NImage MinimapImage;
  3313. NImage GuideImage;
  3314. MinimapImage.init(Width * 4 + 8, Height * 4 + 8, 3);
  3315. GuideImage.init(Width * 8 + 16, Height * 8 + 16, 3);
  3316.  
  3317. opt_assert(Width <= 24);
  3318. opt_assert(Height <= 20);
  3319.  
  3320. for (int x = X1; x <= X2; ++x)
  3321. {
  3322. for (int y = Y1; y <= Y2; ++y)
  3323. {
  3324. //int PixelX1 = (MinimapImage.getWidth() / 2) + (x * 8);
  3325. //int PixelY1 = (MinimapImage.getHeight() / 2) + (y * 8);
  3326. int PixelX1 = ((x - X1) * 8) + 8;
  3327. int PixelY1 = ((y - Y1) * 8) + 8;
  3328. int PixelX2 = PixelX1 + 7;
  3329. int PixelY2 = PixelY1 + 7;
  3330.  
  3331. int RoomX = x - X1 + MinX;
  3332. int RoomY = y - Y1 + MinY;
  3333.  
  3334. RoomX, RoomY;
  3335.  
  3336. if (Rooms.exists(RoomX) &&
  3337. Rooms[RoomX].exists(RoomY))
  3338. {
  3339. //uint Paths = Rooms[RoomX][RoomY];
  3340. for (int px = PixelX1; px <= PixelX2; ++px)
  3341. {
  3342. for (int py = PixelY1; py <= PixelY2; ++py)
  3343. {
  3344. int Count = 0;
  3345. int SolidCount = 0;
  3346. for (int xx = 0; xx < 4; ++xx)
  3347. {
  3348. for (int yy = 0; yy < 4; ++yy)
  3349. {
  3350. Count++;
  3351.  
  3352. int bx = ((px - PixelX1) * 4) + xx;
  3353. int by = ((py - PixelY1) * 4) + yy;
  3354.  
  3355. static NUtilVector<ValkyrieTileType> SolidTiles;
  3356. if (SolidTiles.empty())
  3357. {
  3358. SolidTiles.push(VTT_Solid);
  3359. SolidTiles.push(VTT_Ice);
  3360. SolidTiles.push(VTT_Deadly);
  3361. SolidTiles.push(VTT_NoClimb);
  3362. SolidTiles.push(VTT_NoEffects);
  3363. }
  3364.  
  3365. if (SolidTiles.findFirst(TheWorld.GetCellType(RoomX,RoomY,bx,by)) != -1)
  3366. {
  3367. SolidCount++;
  3368. }
  3369. }
  3370. }
  3371.  
  3372. uchar Brightness = uchar(128.0f * (float(SolidCount) / float(Count)));
  3373. GuideImage.setPixel(px, py, NColor8(Brightness));
  3374. }
  3375. }
  3376.  
  3377.  
  3378. //if ((Paths & HAS_PATH_TOP) == 0)
  3379. {
  3380. for (int px = PixelX1; px <= PixelX2 + 1; ++px)
  3381. {
  3382. //MinimapImage.setPixel(px, PixelY1, NColor8(255));
  3383. }
  3384. }
  3385.  
  3386. //if ((Paths & HAS_PATH_BOTTOM) == 0)
  3387. {
  3388. for (int px = PixelX1; px <= PixelX2 + 1; ++px)
  3389. {
  3390. //MinimapImage.setPixel(px, PixelY2 + 1, NColor8(255));
  3391. }
  3392. }
  3393.  
  3394. //if ((Paths & HAS_PATH_LEFT) == 0)
  3395. {
  3396. for (int py = PixelY1; py <= PixelY2 + 1; ++py)
  3397. {
  3398. //MinimapImage.setPixel(PixelX1, py, NColor8(255));
  3399. }
  3400. }
  3401.  
  3402. //if ((Paths & HAS_PATH_RIGHT) == 0)
  3403. {
  3404. for (int py = PixelY1; py <= PixelY2 + 1; ++py)
  3405. {
  3406. //MinimapImage.setPixel(PixelX2 + 1, py, NColor8(255));
  3407. }
  3408. }
  3409. }
  3410. }
  3411. }
  3412.  
  3413. for (int x = X1; x <= X2; ++x)
  3414. {
  3415. for (int y = Y1; y <= Y2; ++y)
  3416. {
  3417. //int PixelX1 = (MinimapImage.getWidth() / 2) + (x * 8);
  3418. //int PixelY1 = (MinimapImage.getHeight() / 2) + (y * 8);
  3419. int PixelX1 = ((x - X1) * 4) + 4;
  3420. int PixelY1 = ((y - Y1) * 4) + 4;
  3421. int PixelX2 = PixelX1 + 4;
  3422. int PixelY2 = PixelY1 + 4;
  3423.  
  3424. int RoomX = x - X1 + MinX;
  3425. int RoomY = y - Y1 + MinY;
  3426.  
  3427. RoomX, RoomY;
  3428.  
  3429. if (Rooms.exists(RoomX) &&
  3430. Rooms[RoomX].exists(RoomY))
  3431. {
  3432. bool bInSubRegion = false;
  3433.  
  3434. for (int i = 0; i < TheWorld.ScrollRegions.size() && !bInSubRegion; ++i)
  3435. {
  3436. const RECT& Region = TheWorld.ScrollRegions[i].Region;
  3437. if (RoomX >= Region.left &&
  3438. RoomX < Region.right &&
  3439. RoomY >= Region.top &&
  3440. RoomY < Region.bottom)
  3441. {
  3442. bInSubRegion = true;
  3443. }
  3444. }
  3445.  
  3446. if (!bInSubRegion)
  3447. {
  3448. for (int px = PixelX1 + 1; px <= PixelX2 - 1; ++px)
  3449. {
  3450. for (int py = PixelY1 + 1; py <= PixelY2 - 1; ++py)
  3451. {
  3452. MinimapImage.setPixel(px, py, NColor8(0, 120, 248));
  3453. }
  3454. }
  3455.  
  3456. for (int px = PixelX1; px <= PixelX2; ++px)
  3457. {
  3458. MinimapImage.setPixel(px, PixelY1, NColor8(255));
  3459. MinimapImage.setPixel(px, PixelY2, NColor8(255));
  3460. }
  3461.  
  3462. for (int py = PixelY1; py <= PixelY2; ++py)
  3463. {
  3464. MinimapImage.setPixel(PixelX1, py, NColor8(255));
  3465. MinimapImage.setPixel(PixelX2, py, NColor8(255));
  3466. }
  3467. }
  3468. }
  3469. }
  3470. }
  3471.  
  3472. for (int i = 0; i < TheWorld.ScrollRegions.size(); ++i)
  3473. {
  3474. //for (int x = TheWorld.ScrollRegions[i].Region.left; x < TheWorld.ScrollRegions[i].Region.right; ++x)
  3475. //{
  3476. // for (int y = TheWorld.ScrollRegions[i].Region.top; y < TheWorld.ScrollRegions[i].Region.bottom; ++y)
  3477. // {
  3478. // }
  3479. //}
  3480.  
  3481. const RECT& Region = TheWorld.ScrollRegions[i].Region;
  3482.  
  3483. int PixelX1 = ((Region.left - MinX) * 4) + 4;
  3484. int PixelY1 = ((Region.top - MinY) * 4 + 4);
  3485. int PixelX2 = ((Region.right - MinX) * 4) + 4;
  3486. int PixelY2 = ((Region.bottom - MinY) * 4) + 4;
  3487.  
  3488. for (int px = PixelX1 + 1; px <= PixelX2 - 1; ++px)
  3489. {
  3490. for (int py = PixelY1 + 1; py <= PixelY2 - 1; ++py)
  3491. {
  3492. MinimapImage.setPixel(px, py, NColor8(0, 120, 248));
  3493. }
  3494. }
  3495.  
  3496. for (int px = PixelX1; px <= PixelX2; ++px)
  3497. {
  3498. MinimapImage.setPixel(px, PixelY1, NColor8(255));
  3499. MinimapImage.setPixel(px, PixelY2, NColor8(255));
  3500. }
  3501.  
  3502. for (int py = PixelY1; py <= PixelY2; ++py)
  3503. {
  3504. MinimapImage.setPixel(PixelX1, py, NColor8(255));
  3505. MinimapImage.setPixel(PixelX2, py, NColor8(255));
  3506. }
  3507. }
  3508.  
  3509. ANCHOR;
  3510.  
  3511. //MinimapImage.saveBMP(Text("MINIMAP_%s.bmp", MapName));
  3512. //GuideImage.saveBMP(Text("MINIMAP_GUIDE_%s.bmp", MapName));
  3513.  
  3514. ANCHOR;
  3515. }
  3516. //else
  3517. {
  3518. // We want to hide the minimap in this case.
  3519.  
  3520. ANCHOR;
  3521. }
  3522.  
  3523.  
  3524.  
  3525.  
  3526.  
  3527.  
  3528.  
  3529.  
  3530.  
  3531. // Let's try something different.
  3532.  
  3533. {
  3534.  
  3535. }
  3536. */
  3537. }
  3538.  
  3539. void Player::RecBuildMinimap(NUtilMap<NUtilMap<uint, int>, int>& Set, POINT CurRoom)
  3540. {
  3541. if (!Set.exists(CurRoom.x) ||
  3542. !Set[CurRoom.x].exists(CurRoom.y))
  3543. {
  3544. // We haven't added this room yet.
  3545.  
  3546. // See whether a path exists to an adjacent room.
  3547. // Also have to see whether there's an edge remap,
  3548. // whether the adjacent room is part of a scroll region, and so on.
  3549.  
  3550. const ValkyrieWorld& TheWorld = ValkyrieGame::GetInstance()->TheWorld;
  3551.  
  3552. if (TheWorld.Rooms.exists(CurRoom.x) &&
  3553. TheWorld.Rooms[CurRoom.x].exists(CurRoom.y))
  3554. {
  3555. uint PATHS = 0;
  3556.  
  3557. Set[CurRoom.x][CurRoom.y] = PATHS;
  3558.  
  3559. const ValkyrieRoom& ThisRoom = TheWorld.Rooms[CurRoom.x][CurRoom.y];
  3560.  
  3561. bool bHasTopPath = false;
  3562. bool bHasBottomPath = false;
  3563. bool bHasLeftPath = false;
  3564. bool bHasRightPath = false;
  3565.  
  3566. static NUtilVector<ValkyrieTileType> PathTypes;
  3567. if (PathTypes.empty())
  3568. {
  3569. PathTypes.push(VTT_None);
  3570. PathTypes.push(VTT_Ledge);
  3571. PathTypes.push(VTT_Water);
  3572. PathTypes.push(VTT_Toxic);
  3573. }
  3574.  
  3575. for (int i = 1; i < TILES_WIDTH - 1; ++i)
  3576. {
  3577. bHasTopPath = bHasTopPath || (PathTypes.findFirst(TheWorld.GetCellType(CurRoom.x, CurRoom.y, i, 0)) != -1);
  3578. bHasBottomPath = bHasBottomPath || (PathTypes.findFirst(TheWorld.GetCellType(CurRoom.x, CurRoom.y, i, TILES_HEIGHT - 1)) != -1);
  3579. }
  3580.  
  3581. for (int i = 1; i < TILES_HEIGHT - 1; ++i)
  3582. {
  3583. bHasLeftPath = bHasLeftPath || (PathTypes.findFirst(TheWorld.GetCellType(CurRoom.x, CurRoom.y, 0, i)) != -1);
  3584. bHasRightPath = bHasRightPath || (PathTypes.findFirst(TheWorld.GetCellType(CurRoom.x, CurRoom.y, TILES_WIDTH - 1, i)) != -1);
  3585. }
  3586.  
  3587. // See whether we have a jump cut or a remap, as this will affect how or whether we recurse.
  3588. // Not yet sure where scroll regions will go.
  3589. if (bHasTopPath)
  3590. {
  3591. bool bRecurse = true;
  3592. POINT RecRoom;
  3593. RecRoom.x = CurRoom.x;
  3594. RecRoom.y = CurRoom.y - 1;
  3595.  
  3596. if (!TheWorld.Rooms.exists(RecRoom.x) || !TheWorld.Rooms[RecRoom.x].exists(RecRoom.y)) {bRecurse = false;}
  3597. if (ThisRoom.Edges[0].bHasMapName && ThisRoom.Edges[0].MapName != TheWorld.ActiveMap) {bRecurse = false;}
  3598. if (ThisRoom.Edges[0].bHasMapCoords) {RecRoom = ThisRoom.Edges[0].MapCoords;}
  3599. if (bRecurse) {RecBuildMinimap(Set, RecRoom); PATHS |= HAS_PATH_TOP;}
  3600. }
  3601. if (bHasBottomPath)
  3602. {
  3603. bool bRecurse = true;
  3604. POINT RecRoom;
  3605. RecRoom.x = CurRoom.x;
  3606. RecRoom.y = CurRoom.y + 1;
  3607.  
  3608. if (!TheWorld.Rooms.exists(RecRoom.x) || !TheWorld.Rooms[RecRoom.x].exists(RecRoom.y)) {bRecurse = false;}
  3609. if (ThisRoom.Edges[1].bHasMapName && ThisRoom.Edges[1].MapName != TheWorld.ActiveMap) {bRecurse = false;}
  3610. if (ThisRoom.Edges[1].bHasMapCoords) {RecRoom = ThisRoom.Edges[1].MapCoords;}
  3611. if (bRecurse) {RecBuildMinimap(Set, RecRoom); PATHS |= HAS_PATH_BOTTOM;}
  3612. }
  3613. if (bHasLeftPath)
  3614. {
  3615. bool bRecurse = true;
  3616. POINT RecRoom;
  3617. RecRoom.x = CurRoom.x - 1;
  3618. RecRoom.y = CurRoom.y;
  3619.  
  3620. if (!TheWorld.Rooms.exists(RecRoom.x) || !TheWorld.Rooms[RecRoom.x].exists(RecRoom.y)) {bRecurse = false;}
  3621. if (ThisRoom.Edges[2].bHasMapName && ThisRoom.Edges[2].MapName != TheWorld.ActiveMap) {bRecurse = false;}
  3622. if (ThisRoom.Edges[2].bHasMapCoords) {RecRoom = ThisRoom.Edges[2].MapCoords;}
  3623. if (bRecurse) {RecBuildMinimap(Set, RecRoom); PATHS |= HAS_PATH_LEFT;}
  3624. }
  3625. if (bHasRightPath)
  3626. {
  3627. bool bRecurse = true;
  3628. POINT RecRoom;
  3629. RecRoom.x = CurRoom.x + 1;
  3630. RecRoom.y = CurRoom.y;
  3631.  
  3632. if (!TheWorld.Rooms.exists(RecRoom.x) || !TheWorld.Rooms[RecRoom.x].exists(RecRoom.y)) {bRecurse = false;}
  3633. if (ThisRoom.Edges[3].bHasMapName && ThisRoom.Edges[3].MapName != TheWorld.ActiveMap) {bRecurse = false;}
  3634. if (ThisRoom.Edges[3].bHasMapCoords) {RecRoom = ThisRoom.Edges[3].MapCoords;}
  3635. if (bRecurse) {RecBuildMinimap(Set, RecRoom); PATHS |= HAS_PATH_RIGHT;}
  3636. }
  3637.  
  3638. Set[CurRoom.x][CurRoom.y] = PATHS;
  3639. }
  3640. }
  3641. }
  3642.  
  3643. void Player::TickCommitBGMusicChange(float DeltaTime)
  3644. {
  3645. if (BGMusicSwitchDelay > 0.0f)
  3646. {
  3647. BGMusicSwitchDelay -= DeltaTime;
  3648. if (BGMusicSwitchDelay <= 0.0f)
  3649. {
  3650. //CurrentBGMusicName = BGMusicSwitchName;
  3651.  
  3652. // At least for starters, we'll clear ALL tunes (including sound effects) when changing BG music.
  3653. // This will likely be crap and not a shipping solution.
  3654.  
  3655. if (CurrentBGMusicName.Length() > 0)
  3656. {
  3657. // TODO: Need to save off the campaign's prefix for use here!
  3658.  
  3659. MAKESTRING(BGMusicTune);
  3660. ValkyrieGame::GetInstance()->MultiSynth->PlayTune(NMSTune(*(ValkyrieGame::GetInstance()->ActiveCampaignPrefix + CurrentBGMusicName), AUDPRI_BGMUSIC, NMS_CHAN_BGMUSIC), BGMusicTune, NMSTU_BGM);
  3661. }
  3662. }
  3663. }
  3664. }
  3665.  
  3666. void Player::SetSprite(const NString& SpriteName)
  3667. {
  3668. PlayerSprite* TheSprite = ((PlayerSprite*)(Sprite->GetRenderableByName()));
  3669. if (TheSprite)
  3670. {
  3671. if (SpriteName.Length() > 0)
  3672. {
  3673. TheSprite->SetSprite(*SpriteName, NHashedString(*(NString("Texture_PlayerSprite_Alternate_") + SpriteName)));
  3674. }
  3675. else
  3676. {
  3677. if (!OVERWORLD_MOTION)
  3678. {
  3679. TheSprite->SetSprite("capmansheetredux.bmp", NHashedString("Texture_PlayerSprite_Default"));
  3680. }
  3681. else
  3682. {
  3683. TheSprite->SetSprite("capmanmapsheet.bmp", NHashedString("Texture_PlayerSprite_Overworld"));
  3684. }
  3685. }
  3686. }
  3687. }
  3688.  
  3689. void Player::SetSafeSpace(bool bSafe)
  3690. {
  3691. bCurrentSafeSpace = bSafe;
  3692. }
  3693.  
  3694. void Player::OnBaseAboutToMove(NEntity* Owner, NEntity* Parent)
  3695. {
  3696. if (Owner && Parent)
  3697. {
  3698. }
  3699. }
  3700.  
  3701. void Player::OnBaseMoved(NEntity* Owner, NEntity* Parent)
  3702. {
  3703. if (Owner && Parent)
  3704. {
  3705. Player* ThePlayer = Cast<Player>(Owner);
  3706. ThePlayer->Camera->Camera->setPos(ThePlayer->GetCameraPosition(ThePlayer->bExtendedCamera, ThePlayer->bExtendedCamera));
  3707.  
  3708. // Should fix the "whoops I rode a donut lift off the screen" bug.
  3709. ThePlayer->CheckTransitionRooms();
  3710. }
  3711. }
  3712.  
  3713. void Player::OnHealthDamaged(NEntity* OwnerEntity, NHandle Causer)
  3714. {
  3715. if (OwnerEntity)
  3716. {
  3717. Player* ThePlayer = Cast<Player>(OwnerEntity);
  3718. ThePlayer->Die();
  3719. }
  3720. }
  3721.  
  3722. bool Player::HasPowerup(ValkPowerups Powerup, bool bOmitSpeedrunPowerups)
  3723. {
  3724. bool bRetVal = false;
  3725. NDataComponent* DataComp = GetComponent<NDataComponent>();
  3726. static char NameBuffer[64] = {0};
  3727. sprintf_s(NameBuffer, "Powerup_%08x", (int)Powerup);
  3728. NHashedString PowerupName(NameBuffer);
  3729. if (DataComp)
  3730. {
  3731. if (DataComp->HasValue(PowerupName))
  3732. {
  3733. GenericData PowerupValue;
  3734. if (DataComp->GetValue(PowerupName, PowerupValue))
  3735. {
  3736. if (PowerupValue.Type == GDT_Bool)
  3737. {
  3738. if (PowerupValue.Data.Data_Bool == true)
  3739. {
  3740. bRetVal = true;
  3741. }
  3742. }
  3743. }
  3744. }
  3745. }
  3746.  
  3747. if (!bOmitSpeedrunPowerups)
  3748. {
  3749. SpeedrunComponent* Speed = GetComponent<SpeedrunComponent>();
  3750. if (Speed)
  3751. {
  3752. Speed->OverridePowerup(bRetVal, Powerup);
  3753. }
  3754. }
  3755.  
  3756. return bRetVal;
  3757. }
  3758.  
  3759. void Player::SetHasPowerup(ValkPowerups Powerup, bool bHas)
  3760. {
  3761. NDataComponent* DataComp = GetComponent<NDataComponent>();
  3762. static char NameBuffer[64] = {0};
  3763. sprintf_s(NameBuffer, "Powerup_%08x", (int)Powerup);
  3764. NHashedString PowerupName(NameBuffer);
  3765. if (DataComp)
  3766. {
  3767. GenericData PowerupValue(bHas);
  3768. DataComp->CreateMappedValue(PowerupName);
  3769. DataComp->SetValue(PowerupName, PowerupValue);
  3770. }
  3771.  
  3772. // And fix up the legacy properties until I kill them off...
  3773. if (Powerup == VPU_Boots_DoubleJump)
  3774. {
  3775. //bHasDoubleJump = bHas;
  3776. }
  3777. else if (Powerup == VPU_Gloves_Climbing)
  3778. {
  3779. //bHasWallJump = bHas;
  3780. }
  3781.  
  3782. // TODO: Figure out the appropriate times to do these things.
  3783. SetUpPhysComp();
  3784.  
  3785. if (PhysComp)
  3786. {
  3787. PhysComp->IncrementJumpsRemaining();
  3788. }
  3789.  
  3790. // THIS IS VERY DANGEROUS. IT CAN DELETE STUFF OUT FROM UNDER US.
  3791. //ValkyrieGame::GetInstance()->CompileRoom();
  3792. }
  3793.  
  3794. void Player::SetHasAllPowerups(bool bHas)
  3795. {
  3796. NUtilVector<ValkPowerups> DefPowers;
  3797. DefPowers.push(VPU_Aura_Red);
  3798. DefPowers.push(VPU_Aura_Blue);
  3799. //DefPowers.push(VPU_Aura_Green);
  3800. //DefPowers.push(VPU_Aura_Yellow);
  3801. DefPowers.push(VPU_Boots_HighJump);
  3802. DefPowers.push(VPU_Boots_DoubleJump);
  3803. DefPowers.push(VPU_Boots_Spiked);
  3804. DefPowers.push(VPU_Gloves_Climbing);
  3805. DefPowers.push(VPU_Mask_Hidden);
  3806. DefPowers.push(VPU_Badge_Pass);
  3807. DefPowers.push(VPU_Suit_Swimming);
  3808. DefPowers.push(VPU_Suit_Toxic);
  3809. DefPowers.push(VPU_Key_Master);
  3810. DefPowers.push(VPU_Pass_Arc);
  3811. DefPowers.push(VPU_Pass_Styx);
  3812. DefPowers.push(VPU_Trinket_Necklace);
  3813. for (int i = 0; i < DefPowers.size(); ++i)
  3814. {
  3815. SetHasPowerup(DefPowers[i], bHas);
  3816. }
  3817. }
  3818.  
  3819. void Player::CheckpointWith(NHandle Checkpoint, bool bUseLastSafePlace/*, bool bAllowThumbnail*/)
  3820. {
  3821. bool bIsSelf = (Checkpoint == GetHandle());
  3822. bool bDoWrite = true;
  3823.  
  3824. if (bUseLastSafePlace && bIsSelf)
  3825. {
  3826. if (LastSafePlace.Time > LastCheckpointTime)
  3827. {
  3828. SetCheckpointPosition(LastSafePlace.Position, bIsSelf);
  3829. SetCheckpointCampaign(LastSafePlace.Campaign, bIsSelf);
  3830. SetCheckpointMap(LastSafePlace.Map, bIsSelf);
  3831. SetCheckpointRoom(LastSafePlace.Room, bIsSelf);
  3832. }
  3833. else
  3834. {
  3835. bDoWrite = false;
  3836. }
  3837. }
  3838. else
  3839. {
  3840. NSpatialComponent* OtherSpace = Resolve<NSpatialComponent>(Checkpoint);
  3841. if (Space)
  3842. {
  3843. SetCheckpointPosition(NVec2(OtherSpace->Game_Curr.Position.x, OtherSpace->Game_Curr.Position.y), bIsSelf);
  3844. }
  3845.  
  3846. const ValkyrieWorld& TheWorld = ValkyrieGame::GetInstance()->TheWorld;
  3847.  
  3848. SetCheckpointCampaign(TheWorld.ActiveCampaign, bIsSelf);
  3849. SetCheckpointMap(TheWorld.ActiveMap, bIsSelf);
  3850. POINT NewRoom;
  3851. NewRoom.x = TheWorld.ActiveRoom.x;
  3852. NewRoom.y = TheWorld.ActiveRoom.y;
  3853. SetCheckpointRoom(NewRoom, bIsSelf);
  3854. }
  3855.  
  3856. if (!bIsSelf)
  3857. {
  3858. // If we're trying to save at a bell, do a quicksave afterwards. This will also do the actual write to disk.
  3859. CheckpointWith(GetHandle(), false/*, bAllowThumbnail*/);
  3860.  
  3861. bDoWrite = false;
  3862. }
  3863.  
  3864. if (bDoWrite)
  3865. {
  3866. // Save our own data to the serial resource.
  3867. NEntityFactory::SaveEntity(this);
  3868.  
  3869. // Hrm. I need a better plan for these things.
  3870. NEntityFactory::SaveEntity(ValkyrieGame::GetInstance()->TheGroupMgr);
  3871.  
  3872.  
  3873.  
  3874. // TODO: Save ALL dynamic entities!!
  3875. for (int i = 0; i < NBaseGame::GetInstance()->DynamicEntities.size(); ++i)
  3876. {
  3877. NEntityFactory::SaveEntity(NBaseGame::GetInstance()->DynamicEntities[i]);
  3878. }
  3879.  
  3880. //if (bAllowThumbnail)
  3881. //{
  3882. // // Super hacks.
  3883. // if (bUseLastSafePlace)
  3884. // {
  3885. // CaptureThumbnail();
  3886. // }
  3887. // else
  3888. // {
  3889. // QueueThumbnail();
  3890. // }
  3891. //}
  3892.  
  3893. // Do I want to commit changes to disk here?
  3894. // Is there any reason not to?
  3895. // When I end the game, I'll flush any unsaved data and reload from disk.
  3896. char SaveGamePath[MAX_PATH];
  3897. GetLocalAppDataPath(SaveGamePath, ValkyrieGame::GetInstance()->GetLocalAppDataPathName(), "Saves" FOLDER_SEPARATOR);
  3898. strcat_s(SaveGamePath, MAX_PATH, "autosave.nsd");
  3899. NSerialResource::GetInstance()->SaveToDisk(SaveGamePath);
  3900.  
  3901. LastCheckpointTime = NBaseGame::GetInstance()->engAbsTime;
  3902. }
  3903. }
  3904.  
  3905. //void Player::QueueThumbnail()
  3906. //{
  3907. // bCaptureThumbnail = true;
  3908. //}
  3909. //
  3910. //void Player::CaptureThumbnail()
  3911. //{
  3912. // NColor8* CleanBuffer = new NColor8[CRT_WIDTH*CRT_HEIGHT];
  3913. //
  3914. // NColor8* pBuffer = CleanBuffer;
  3915. // for (int y = 0; y < CRT_HEIGHT; ++y)
  3916. // {
  3917. // for (int x = 0; x < CRT_WIDTH; ++x)
  3918. // {
  3919. // *pBuffer = NColor8(0,0,0,255);
  3920. // pBuffer++;
  3921. // }
  3922. // }
  3923. //
  3924. // // Somehow it looks like the pRenderSurface in this RTT can be NULL and that's the cause of the thumbnail crash.
  3925. // // Seems unlikely (because how would the game work at all then), but shrug?
  3926. // if (ValkyrieGame::GetInstance()->CleanFrame &&
  3927. // ValkyrieGame::GetInstance()->CleanFrame->getSurface())
  3928. // {
  3929. // ValkyrieGame::GetInstance()->CleanFrame->CopyToBuffer(CleanBuffer, sizeof(NColor8) * CRT_WIDTH * CRT_HEIGHT);
  3930. // }
  3931. //
  3932. // //MAKESTRINGWITH(QuadPassName, "Composite Pass");
  3933. // //NRenderer::GetInstance()->GetRenderPass(QuadPassName)->targets[0]->CopyToBuffer(CleanBuffer, sizeof(NColor8) * CRT_WIDTH * CRT_HEIGHT);
  3934. //
  3935. // pBuffer = CleanBuffer;
  3936. //#ifndef _NFML
  3937. // for (int y = 0; y < CRT_HEIGHT; ++y)
  3938. //#else
  3939. // for (int y = CRT_HEIGHT - 1; y >=0 ; --y)
  3940. //#endif
  3941. // {
  3942. // for (int x = 0; x < CRT_WIDTH; ++x)
  3943. // {
  3944. // ThumbImgFull.setPixel(x, y, *pBuffer);
  3945. // pBuffer++;
  3946. // }
  3947. // }
  3948. // SafeDeleteArray(CleanBuffer);
  3949. //
  3950. // // Ugh, sure.
  3951. // SaveThumbnail();
  3952. //}
  3953. //
  3954. //void Player::SaveThumbnail()
  3955. //{
  3956. // char ThumbPath[MAX_PATH];
  3957. // GetLocalAppDataPath(ThumbPath, ValkyrieGame::GetInstance()->GetLocalAppDataPathName(), "Saves" FOLDER_SEPARATOR);
  3958. // strcat_s(ThumbPath, MAX_PATH, "autosave.bmp");
  3959. //
  3960. //
  3961. //
  3962. // //int ScaledHeight = int(float(CRT_HEIGHT) * (float(THUMB_SIZE) / float(CRT_WIDTH)));
  3963. // //NBlitter::ScaleDownBlit(ThumbImgSm, ThumbImgFull, 0, (THUMB_SIZE - ScaledHeight) / 2, 0, 0, THUMB_SIZE, ScaledHeight, CRT_WIDTH, CRT_HEIGHT, false, true);
  3964. //
  3965. // //ThumbImgSm.saveBMP(ThumbPath);
  3966. //
  3967. //
  3968. //
  3969. // if (!FileExists(ThumbPath) ||
  3970. // !FileLocked(ThumbPath))
  3971. // {
  3972. // ThumbImgFull.saveBMP(ThumbPath);
  3973. // }
  3974. //}
  3975.  
  3976. void Player::PostApplySerialData()
  3977. {
  3978. GetComponent<NDataComponent>()->SetCallbackOwner(GetHandle());
  3979. GetComponent<NDataComponent>()->SetHasCallback(HACK_HasData);
  3980. GetComponent<NDataComponent>()->SetGetCallback(HACK_GetData);
  3981. GetComponent<NDataComponent>()->SetSetCallback(HACK_SetData);
  3982. GetComponent<NDataComponent>()->SetIncCallback(HACK_IncData);
  3983. GetComponent<NDataComponent>()->SetDecCallback(HACK_DecData);
  3984.  
  3985. SetUpPhysComp();
  3986.  
  3987. // And I guess always apply this after loading...
  3988. SetEnteredCode(bEnteredCode || GetEnteredCode());
  3989. }
  3990.  
  3991. void Player::ApplyCheckpointData()
  3992. {
  3993. NDataComponent* DataComp = GetComponent<NDataComponent>();
  3994. if (DataComp)
  3995. {
  3996. // Probably this doesn't need to do anything now.
  3997. }
  3998. }
  3999.  
  4000.  
  4001.  
  4002. void Player::SetCheckpointPosition(NVec2 Position, bool bQuickSave)
  4003. {
  4004. NDataComponent* DataComp = GetComponent<NDataComponent>();
  4005. if (DataComp)
  4006. {
  4007. MAKESTRING(CheckpointPositionX);
  4008. MAKESTRING(CheckpointPositionY);
  4009. MAKESTRING(CheckpointPositionX_QuickSave);
  4010. MAKESTRING(CheckpointPositionY_QuickSave);
  4011.  
  4012. NHashedString StrX = bQuickSave ? CheckpointPositionX_QuickSave : CheckpointPositionX;
  4013. NHashedString StrY = bQuickSave ? CheckpointPositionY_QuickSave : CheckpointPositionY;
  4014.  
  4015. DataComp->CreateMappedValue(StrX);
  4016. DataComp->CreateMappedValue(StrY);
  4017. DataComp->SetValue(StrX, GenericData(Position.x));
  4018. DataComp->SetValue(StrY, GenericData(Position.y));
  4019. }
  4020. }
  4021.  
  4022. void Player::SetCheckpointRoom(POINT Coords, bool bQuickSave)
  4023. {
  4024. NDataComponent* DataComp = GetComponent<NDataComponent>();
  4025. if (DataComp)
  4026. {
  4027. MAKESTRING(CheckpointRoomX);
  4028. MAKESTRING(CheckpointRoomY);
  4029. MAKESTRING(CheckpointRoomX_QuickSave);
  4030. MAKESTRING(CheckpointRoomY_QuickSave);
  4031.  
  4032. NHashedString StrX = bQuickSave ? CheckpointRoomX_QuickSave : CheckpointRoomX;
  4033. NHashedString StrY = bQuickSave ? CheckpointRoomY_QuickSave : CheckpointRoomY;
  4034.  
  4035. DataComp->CreateMappedValue(StrX);
  4036. DataComp->CreateMappedValue(StrY);
  4037. DataComp->SetValue(StrX, GenericData(Coords.x));
  4038. DataComp->SetValue(StrY, GenericData(Coords.y));
  4039. }
  4040. }
  4041.  
  4042. void Player::SetCheckpointMap(NHashedString MapName, bool bQuickSave)
  4043. {
  4044. NDataComponent* DataComp = GetComponent<NDataComponent>();
  4045. if (DataComp)
  4046. {
  4047. MAKESTRING(CheckpointMap);
  4048. MAKESTRING(CheckpointMap_QuickSave);
  4049.  
  4050. NHashedString Str = bQuickSave ? CheckpointMap_QuickSave : CheckpointMap;
  4051.  
  4052. DataComp->CreateMappedValue(Str);
  4053. DataComp->SetValue(Str, GenericData(MapName));
  4054. }
  4055. }
  4056.  
  4057. void Player::SetCheckpointCampaign(NHashedString CampaignName, bool bQuickSave)
  4058. {
  4059. NDataComponent* DataComp = GetComponent<NDataComponent>();
  4060. if (DataComp)
  4061. {
  4062. MAKESTRING(CheckpointCampaign);
  4063. MAKESTRING(CheckpointCampaign_QuickSave);
  4064.  
  4065. NHashedString Str = bQuickSave ? CheckpointCampaign_QuickSave : CheckpointCampaign;
  4066.  
  4067. DataComp->CreateMappedValue(Str);
  4068. DataComp->SetValue(Str, GenericData(CampaignName));
  4069. }
  4070. }
  4071.  
  4072. void Player::SetDeaths(int Deaths)
  4073. {
  4074. NDataComponent* DataComp = GetComponent<NDataComponent>();
  4075. if (DataComp)
  4076. {
  4077. MAKESTRING(DeathsCount);
  4078. DataComp->CreateMappedValue(DeathsCount);
  4079. DataComp->SetValue(DeathsCount, GenericData(Deaths));
  4080. }
  4081.  
  4082. ValkyrieGame::GetInstance()->CheckAchievements("");
  4083. }
  4084.  
  4085. void Player::SetPlayTime(float PlayTime)
  4086. {
  4087. NDataComponent* DataComp = GetComponent<NDataComponent>();
  4088. if (DataComp)
  4089. {
  4090. MAKESTRING(PlayTimeSeconds);
  4091. DataComp->CreateMappedValue(PlayTimeSeconds);
  4092. DataComp->SetValue(PlayTimeSeconds, GenericData(PlayTime));
  4093. }
  4094. }
  4095.  
  4096. void Player::SetEnteredCode(bool bEnteredCode)
  4097. {
  4098. NDataComponent* DataComp = GetComponent<NDataComponent>();
  4099. if (DataComp)
  4100. {
  4101. MAKESTRING(EnteredCode);
  4102. DataComp->CreateMappedValue(EnteredCode);
  4103. DataComp->SetValue(EnteredCode, GenericData(bEnteredCode));
  4104. }
  4105.  
  4106. ValkyrieGame::GetInstance()->CheckAchievements(NString("AnyCampaign"));
  4107. }
  4108.  
  4109.  
  4110.  
  4111. NVec2 Player::GetCheckpointPosition(bool bQuickSave)
  4112. {
  4113. NVec2 RetVal = NVec2((float)HALF_CRT_WIDTH, (float)HALF_CRT_HEIGHT);
  4114.  
  4115. NDataComponent* DataComp = GetComponent<NDataComponent>();
  4116. if (DataComp)
  4117. {
  4118. GenericData OutData;
  4119.  
  4120. MAKESTRING(CheckpointPositionX);
  4121. MAKESTRING(CheckpointPositionY);
  4122. MAKESTRING(CheckpointPositionX_QuickSave);
  4123. MAKESTRING(CheckpointPositionY_QuickSave);
  4124.  
  4125. NHashedString StrX = bQuickSave ? CheckpointPositionX_QuickSave : CheckpointPositionX;
  4126. NHashedString StrY = bQuickSave ? CheckpointPositionY_QuickSave : CheckpointPositionY;
  4127.  
  4128. if (DataComp->GetValue(StrX, OutData))
  4129. {
  4130. opt_assert(OutData.Type == GDT_Float);
  4131. RetVal.x = OutData.Data.Data_Float;
  4132. }
  4133.  
  4134. if (DataComp->GetValue(StrY, OutData))
  4135. {
  4136. opt_assert(OutData.Type == GDT_Float);
  4137. RetVal.y = OutData.Data.Data_Float;
  4138. }
  4139. }
  4140.  
  4141. return RetVal;
  4142. }
  4143.  
  4144. POINT Player::GetCheckpointRoom(bool bQuickSave)
  4145. {
  4146. POINT RetVal;
  4147. RetVal.x = 0;
  4148. RetVal.y = 0;
  4149.  
  4150. NDataComponent* DataComp = GetComponent<NDataComponent>();
  4151. if (DataComp)
  4152. {
  4153. GenericData OutData;
  4154.  
  4155. MAKESTRING(CheckpointRoomX);
  4156. MAKESTRING(CheckpointRoomY);
  4157. MAKESTRING(CheckpointRoomX_QuickSave);
  4158. MAKESTRING(CheckpointRoomY_QuickSave);
  4159.  
  4160. NHashedString StrX = bQuickSave ? CheckpointRoomX_QuickSave : CheckpointRoomX;
  4161. NHashedString StrY = bQuickSave ? CheckpointRoomY_QuickSave : CheckpointRoomY;
  4162.  
  4163. if (DataComp->GetValue(StrX, OutData))
  4164. {
  4165. opt_assert(OutData.Type == GDT_Int);
  4166. RetVal.x = OutData.Data.Data_Int;
  4167. }
  4168.  
  4169. if (DataComp->GetValue(StrY, OutData))
  4170. {
  4171. opt_assert(OutData.Type == GDT_Int);
  4172. RetVal.y = OutData.Data.Data_Int;
  4173. }
  4174. }
  4175.  
  4176. return RetVal;
  4177. }
  4178.  
  4179. NHashedString Player::GetCheckpointMap(bool bQuickSave)
  4180. {
  4181. NHashedString RetVal;
  4182.  
  4183. NDataComponent* DataComp = GetComponent<NDataComponent>();
  4184. if (DataComp)
  4185. {
  4186. GenericData OutData;
  4187.  
  4188. MAKESTRING(CheckpointMap);
  4189. MAKESTRING(CheckpointMap_QuickSave);
  4190.  
  4191. NHashedString Str = bQuickSave ? CheckpointMap_QuickSave : CheckpointMap;
  4192.  
  4193. if (DataComp->GetValue(Str, OutData))
  4194. {
  4195. opt_assert(OutData.Type == GDT_HashedString);
  4196. RetVal = OutData.Data.Data_HashedString;
  4197. }
  4198. }
  4199.  
  4200. return RetVal;
  4201. }
  4202.  
  4203. NHashedString Player::GetCheckpointCampaign(bool bQuickSave)
  4204. {
  4205. NHashedString RetVal;
  4206.  
  4207. NDataComponent* DataComp = GetComponent<NDataComponent>();
  4208. if (DataComp)
  4209. {
  4210. GenericData OutData;
  4211.  
  4212. MAKESTRING(CheckpointCampaign);
  4213. MAKESTRING(CheckpointCampaign_QuickSave);
  4214.  
  4215. NHashedString Str = bQuickSave ? CheckpointCampaign_QuickSave : CheckpointCampaign;
  4216.  
  4217. if (DataComp->GetValue(Str, OutData))
  4218. {
  4219. opt_assert(OutData.Type == GDT_HashedString);
  4220. RetVal = OutData.Data.Data_HashedString;
  4221. }
  4222. }
  4223.  
  4224. return RetVal;
  4225. }
  4226.  
  4227. int Player::GetDeaths()
  4228. {
  4229. int RetVal = 0;
  4230.  
  4231. NDataComponent* DataComp = GetComponent<NDataComponent>();
  4232. if (DataComp)
  4233. {
  4234. GenericData OutData;
  4235.  
  4236. MAKESTRING(DeathsCount);
  4237.  
  4238. if (DataComp->GetValue(DeathsCount, OutData))
  4239. {
  4240. opt_assert(OutData.Type == GDT_Int);
  4241. RetVal = OutData.Data.Data_Int;
  4242. }
  4243. }
  4244.  
  4245. return RetVal;
  4246. }
  4247.  
  4248. float Player::GetPlayTime()
  4249. {
  4250. float RetVal = 0;
  4251.  
  4252. NDataComponent* DataComp = GetComponent<NDataComponent>();
  4253. if (DataComp)
  4254. {
  4255. GenericData OutData;
  4256.  
  4257. MAKESTRING(PlayTimeSeconds);
  4258.  
  4259. if (DataComp->GetValue(PlayTimeSeconds, OutData))
  4260. {
  4261. opt_assert(OutData.Type == GDT_Float);
  4262. RetVal = OutData.Data.Data_Float;
  4263. }
  4264. }
  4265.  
  4266. return RetVal;
  4267. }
  4268.  
  4269. bool Player::GetEnteredCode()
  4270. {
  4271. bool RetVal = false;
  4272.  
  4273. NDataComponent* DataComp = GetComponent<NDataComponent>();
  4274. if (DataComp)
  4275. {
  4276. GenericData OutData;
  4277.  
  4278. MAKESTRING(EnteredCode);
  4279.  
  4280. if (DataComp->GetValue(EnteredCode, OutData))
  4281. {
  4282. opt_assert(OutData.Type == GDT_Bool);
  4283. RetVal = OutData.Data.Data_Bool;
  4284. }
  4285. }
  4286.  
  4287. return RetVal;
  4288. }
  4289.  
  4290.  
  4291.  
  4292. void Player::TryGround(float LiftDist, float DropDist)
  4293. {
  4294. if (Space && Collision && !OVERWORLD_MOTION)
  4295. {
  4296. NCore::GetInstance()->DebuggerHook();
  4297.  
  4298. // Lift without regard to collision, probably.
  4299. // Can change this if it causes problems later.
  4300. Space->Game_Curr.Position.y -= LiftDist;
  4301.  
  4302. // Then drop and collide.
  4303. // This does not go through the normal spatial component sweep path.
  4304. {
  4305. static NUtilVector<NCollisionResult> Results;
  4306. Results.setNeverDealloc(true);
  4307. Results.clear();
  4308.  
  4309. NCollisionInterface* Primitive = Collision->GetPrimitiveByName();
  4310.  
  4311. if (Primitive)
  4312. {
  4313. if (!NGameOctree::GetInstance()->Sweep(
  4314. Results, Primitive,
  4315. Space->Game_Curr.Position,
  4316. Space->Game_Curr.Position + NVec3(0, DropDist, 0),
  4317. 0, Collision->GetSweepFilter()))
  4318. {
  4319. // We hit something.
  4320. // Update our position.
  4321. Space->Game_Curr.Position = Results[0].HitPosition;
  4322. Space->Game_Curr.Position.z = 0.0f; // Just to be safe.
  4323.  
  4324. // Suuuuuuuuper hacks.
  4325. if (GetComponent<NPhysicsComponent>())
  4326. {
  4327. GetComponent<NPhysicsComponent>()->OnSpawn(false);
  4328. GetComponent<NPhysicsComponent>()->Land(Results[0]);
  4329.  
  4330. // Apply terminal velocity immediately so we stay grounded.
  4331. Space->Game_Curr.Velocity = NPhysicsGlobals::GetWorldUp() * -PhysComp->GetTerminalVelocity();
  4332.  
  4333. // This initializes acceleration.
  4334. PhysComp->Function2_NeedsName(false);
  4335. }
  4336.  
  4337. // Suppress animation.
  4338. LandedTime = 0.0f;
  4339. }
  4340. else
  4341. {
  4342. // We didn't hit anything.
  4343. // Undo the lift.
  4344. Space->Game_Curr.Position.y += LiftDist;
  4345. }
  4346. }
  4347. }
  4348.  
  4349. Space->Init();
  4350. }
  4351.  
  4352. SpeedrunComponent* Speed = GetComponent<SpeedrunComponent>();
  4353. if (Speed)
  4354. {
  4355. Speed->TryRecordGhostEvent(true);
  4356. }
  4357. }
  4358.  
  4359. void Player::TryEnterRoomFromTeleport()
  4360. {
  4361. // Just a random bit of polish.
  4362. // Automate walking and suppress jumping for a short duration. Another bit stolen from Zelda 2.
  4363. if (!OVERWORLD_MOTION)
  4364. {
  4365. const ValkyrieWorld& TheWorld = ValkyrieGame::GetInstance()->TheWorld;
  4366.  
  4367. if (Space->Game_Curr.Position.x <= (TheWorld.CurrentScrollBounds.Left) + (TILE_SIZE * 2.0f))
  4368. {
  4369. TeleportEntry_Direction = NVec3(1.0f, 0.0f, 0.0f);
  4370. TeleportEntry_Remaining = TeleportEntry_Duration;
  4371. }
  4372. else if (Space->Game_Curr.Position.x >= (TheWorld.CurrentScrollBounds.Right) - (TILE_SIZE * 2.0f))
  4373. {
  4374. TeleportEntry_Direction = NVec3(-1.0f, 0.0f, 0.0f);
  4375. TeleportEntry_Remaining = TeleportEntry_Duration;
  4376. }
  4377. }
  4378. }
  4379.  
  4380. void Player::ShowStatusScreen()
  4381. {
  4382. NDialogConsumerComponent* MyDialogConsumer = GetComponent<NDialogConsumerComponent>();
  4383. NDialogProviderComponent* MyDialogProvider = GetComponent<NDialogProviderComponent>();
  4384.  
  4385. if (MyDialogConsumer && MyDialogProvider)
  4386. {
  4387. // TODO: AUDIO
  4388. NBaseGame::GetInstance()->PlayDialogAudio(DAC_Open);
  4389.  
  4390. ValkyrieGame::GetInstance()->TheGameFont_Screen->LeadingScale = 1.0f;
  4391.  
  4392. MAKESTRING(DialogProvider);
  4393.  
  4394. SpeedrunComponent* Speed = GetComponent<SpeedrunComponent>();
  4395. if (!Speed || !Speed->bOnSpeedrunCourse)
  4396. {
  4397. MyDialogProvider->ClearTree();
  4398. MyDialogProvider->ApplyDefinitionData(StatusProviderFile.Contents.GetChildByName(DialogProvider));
  4399.  
  4400. MyDialogProvider->SetTextSpeed(100000.0f); // Basically immediate.
  4401. MyDialogProvider->SetMaxLines(10); // This is probably the best fit. Actually takes 23 lines, or 184 pixels of about 224 visible (16 above and 24 below).
  4402. MyDialogProvider->SetMinLines(10); // This is probably the best fit. Actually takes 23 lines, or 184 pixels of about 224 visible (16 above and 24 below).
  4403. MyDialogProvider->SetWidth(15);
  4404. }
  4405. else
  4406. {
  4407. MyDialogProvider->ClearTree();
  4408. MyDialogProvider->ApplyDefinitionData(StatusProviderFile_Speedrun.Contents.GetChildByName(DialogProvider));
  4409.  
  4410. MyDialogProvider->SetTextSpeed(100000.0f);
  4411. MyDialogProvider->SetMaxLines(7);
  4412. MyDialogProvider->SetMinLines(7);
  4413. MyDialogProvider->SetWidth(18);
  4414. }
  4415.  
  4416. if (ValkyrieGame::GetInstance()->bHasMinimap)
  4417. {
  4418. //GetComponent<NDialogConsumerComponent>()->SetClosedCallback(OnDialogClosed_ToFullMap);
  4419. }
  4420.  
  4421. MyDialogProvider->Consumer = MyDialogConsumer->GetHandle();
  4422.  
  4423. MyDialogProvider->TryInitFromTemplate();
  4424.  
  4425. MyDialogConsumer->SetProvider(MyDialogProvider);
  4426. }
  4427. }
  4428.  
  4429. void Player::ShowFullMap()
  4430. {
  4431. NDialogConsumerComponent* MyDialogConsumer = GetComponent<NDialogConsumerComponent>();
  4432. NDialogProviderComponent* MyDialogProvider = GetComponent<NDialogProviderComponent>();
  4433.  
  4434. if (MyDialogConsumer && MyDialogProvider)
  4435. {
  4436. // TODO: AUDIO
  4437. NBaseGame::GetInstance()->PlayDialogAudio(DAC_Open);
  4438.  
  4439. ValkyrieGame::GetInstance()->TheGameFont_Screen->LeadingScale = 1.0f;
  4440.  
  4441. MAKESTRING(DialogProvider);
  4442. {
  4443. MyDialogProvider->ClearTree();
  4444. MyDialogProvider->ApplyDefinitionData(StatusProviderFile_FullMap.Contents.GetChildByName(DialogProvider));
  4445.  
  4446. MyDialogProvider->SetTextSpeed(100000.0f);
  4447. MyDialogProvider->SetMaxLines(10);
  4448. MyDialogProvider->SetMinLines(10);
  4449. MyDialogProvider->SetWidth(26);
  4450. }
  4451.  
  4452. MyDialogProvider->Consumer = MyDialogConsumer->GetHandle();
  4453.  
  4454. MyDialogProvider->TryInitFromTemplate();
  4455.  
  4456. MyDialogConsumer->SetProvider(MyDialogProvider);
  4457. }
  4458. }
  4459.  
  4460. void Player::OnDialogClosed(NEntity* OwnerEntity)
  4461. {
  4462. OwnerEntity;
  4463.  
  4464. // Always reset this in case the dialog was the status screen.
  4465. ValkyrieGame::GetInstance()->TheGameFont_Screen->LeadingScale = 2.0f;
  4466.  
  4467. // Sure. Whatever.
  4468. // Nah, still exploitable because of status screen.
  4469. //Cast<Player>(OwnerEntity)->CheckpointWith(Cast<Player>(OwnerEntity)->GetHandle());
  4470.  
  4471. ValkyrieGame::GetInstance()->TheFullMapMesh->visible = false;
  4472. ValkyrieGame::GetInstance()->TheFullMapIndicatorMesh->visible = false;
  4473. }
  4474.  
  4475. /*
  4476. void Player::OnDialogClosed_ToFullMap(NEntity* OwnerEntity)
  4477. {
  4478. OwnerEntity->GetComponent<NDialogConsumerComponent>()->SetClosedCallback(OnDialogClosed);
  4479.  
  4480. NDialogConsumerComponent* MyDialogConsumer = OwnerEntity->GetComponent<NDialogConsumerComponent>();
  4481. NDialogProviderComponent* MyDialogProvider = OwnerEntity->GetComponent<NDialogProviderComponent>();
  4482.  
  4483. if (MyDialogConsumer && MyDialogProvider)
  4484. {
  4485. // TODO: AUDIO
  4486. NBaseGame::GetInstance()->PlayDialogAudio(DAC_Open);
  4487.  
  4488. ValkyrieGame::GetInstance()->TheGameFont_Screen->LeadingScale = 1.0f;
  4489.  
  4490. MAKESTRING(DialogProvider);
  4491. {
  4492. MyDialogProvider->ClearTree();
  4493. MyDialogProvider->ApplyDefinitionData(Cast<Player>(OwnerEntity)->StatusProviderFile_FullMap.Contents.GetChildByName(DialogProvider));
  4494.  
  4495. MyDialogProvider->SetTextSpeed(100000.0f);
  4496. MyDialogProvider->SetMaxLines(10);
  4497. MyDialogProvider->SetMinLines(10);
  4498. MyDialogProvider->SetWidth(18);
  4499. }
  4500.  
  4501. MyDialogProvider->Consumer = MyDialogConsumer->GetHandle();
  4502.  
  4503. MyDialogProvider->TryInitFromTemplate();
  4504.  
  4505. MyDialogConsumer->SetProvider(MyDialogProvider);
  4506. }
  4507. }
  4508. */
  4509.  
  4510. void Player::PlaySplashEffects(bool bEntering)
  4511. {
  4512. DebugLog("Playing splash effects.");
  4513.  
  4514. float Offset = bEntering ? -2.0f : 0.0f;
  4515. Offset += 4.0f;
  4516.  
  4517. if (bEntering)
  4518. {
  4519. ValkyrieGame::GetInstance()->MultiSynth->PlayTune(NMSTune("splashdown.nms", AUDPRI_LANDING, NMS_CHAN_SOUNDFX), gNullStr, NMSTU_SFX);
  4520. }
  4521. else
  4522. {
  4523. ValkyrieGame::GetInstance()->MultiSynth->PlayTune(NMSTune("splash.nms", AUDPRI_LANDING, NMS_CHAN_SOUNDFX), gNullStr, NMSTU_SFX);
  4524. }
  4525.  
  4526. {
  4527. ValkEntity* NewEntity = NEntityFactory::BuildEntity<ValkEntity>(NHashedString("Splash.ndd"));
  4528.  
  4529. // Oops, this may still be opted-out!
  4530. if (NewEntity)
  4531. {
  4532. // TODO: Allow the game to make additional updates (e.g., for positioning Valkyrie entities relative to room coordinates).
  4533. NBaseGame::GetInstance()->GamePostBuildEntity(NewEntity);
  4534.  
  4535. NBaseGame::GetInstance()->DynamicEntities.push(NewEntity);
  4536.  
  4537. // I guess update its position to the context's position?
  4538. NSpatialComponent* ContextSpace = Resolve<NSpatialComponent>(this);
  4539. NSpatialComponent* EffectSpace = Resolve<NSpatialComponent>(NewEntity);
  4540. if (ContextSpace && EffectSpace)
  4541. {
  4542. // Don't alter Z.
  4543. EffectSpace->Game_Curr.Position.x = ContextSpace->Game_Curr.Position.x;
  4544. EffectSpace->Game_Curr.Position.y = ContextSpace->Game_Curr.Position.y + Offset;
  4545. EffectSpace->Init();
  4546. }
  4547. }
  4548. }
  4549. }
  4550.  
  4551. void Player::MadSwag()
  4552. {
  4553. NDataComponent* DataComp = GetComponent<NDataComponent>();
  4554. if (DataComp)
  4555. {
  4556. DataComp->IncValue(NHashedString("smcubes"), GenericData(200));
  4557. DataComp->IncValue(NHashedString("smcubes_total"), GenericData(200));
  4558. DataComp->IncValue(NHashedString("bgcubes"), GenericData(10));
  4559. }
  4560. }
  4561.  
  4562. void Player::AllowRejump()
  4563. {
  4564. bAllowRejump = true;
  4565. }
  4566.  
  4567. void Player::TryBubble(float DeltaTime)
  4568. {
  4569. if (SWIMMING_MOTION)
  4570. {
  4571. NextBubbleTime -= DeltaTime;
  4572. if (NextBubbleTime <= 0.0f)
  4573. {
  4574. NextBubbleTime = (random_fl() * 2.0f) + 0.5f;
  4575.  
  4576. NSpatialComponent* ContextSpace = Resolve<NSpatialComponent>(this);
  4577. NVec3 SpawnPos = ContextSpace->Game_Curr.Position;
  4578. SpawnPos.y -= 2.0f;
  4579.  
  4580.  
  4581. const ValkyrieWorld& TheWorld = ValkyrieGame::GetInstance()->TheWorld;
  4582.  
  4583. ValkyrieTileType SpawnTile =
  4584. ValkyrieGame::GetInstance()->TheWorld.GetCellType(
  4585. TheWorld.ActiveRoom.x, TheWorld.ActiveRoom.y,
  4586. (int)(SpawnPos.x / (float)TILE_SIZE),
  4587. (int)((SpawnPos.y - 8.0f) / (float)TILE_SIZE)); // Test higher to provide buffer space to float up.
  4588.  
  4589. bool bUnderwater =
  4590. ((SpawnTile == VTT_Water || SpawnTile == VTT_Toxic) ||
  4591. (bRegionUnderwater && (SpawnTile == VTT_None || SpawnTile == VTT_Ledge)));
  4592.  
  4593. if (bUnderwater)
  4594. {
  4595. ValkEntity* NewEntity = NEntityFactory::BuildEntity<ValkEntity>(NHashedString("Bubble.ndd"));
  4596.  
  4597. // Oops, this may still be opted-out!
  4598. if (NewEntity)
  4599. {
  4600. // TODO: Allow the game to make additional updates (e.g., for positioning Valkyrie entities relative to room coordinates).
  4601. NBaseGame::GetInstance()->GamePostBuildEntity(NewEntity);
  4602.  
  4603. NBaseGame::GetInstance()->DynamicEntities.push(NewEntity);
  4604.  
  4605. // I guess update its position to the context's position?
  4606. NSpatialComponent* EffectSpace = Resolve<NSpatialComponent>(NewEntity);
  4607. if (ContextSpace && EffectSpace)
  4608. {
  4609. // Don't alter Z.
  4610. EffectSpace->Game_Curr.Position.x = SpawnPos.x;
  4611. EffectSpace->Game_Curr.Position.y = SpawnPos.y;
  4612. EffectSpace->Init();
  4613. }
  4614. }
  4615. }
  4616. }
  4617. }
  4618. }
  4619.  
  4620. bool Player::HACK_HasData(NHandle CallbackOwner, NHashedString Key)
  4621. {
  4622. bool bRetVal = false;
  4623.  
  4624. SpeedrunComponent* Speed = Resolve<SpeedrunComponent>(CallbackOwner);
  4625. if (Speed)
  4626. {
  4627. bRetVal = Speed->HasData(Key);
  4628. }
  4629.  
  4630. return bRetVal;
  4631. }
  4632.  
  4633. bool Player::HACK_GetData(NHandle CallbackOwner, NHashedString Key, GenericData& OutData)
  4634. {
  4635. bool bRetVal = false;
  4636.  
  4637. SpeedrunComponent* Speed = Resolve<SpeedrunComponent>(CallbackOwner);
  4638. if (Speed)
  4639. {
  4640. bRetVal = Speed->GetData(Key, OutData);
  4641.  
  4642. if (!bRetVal)
  4643. {
  4644. MinimapComponent* Mini = Resolve<MinimapComponent>(CallbackOwner);
  4645. if (Mini)
  4646. {
  4647. MAKESTRING(smcubes_loc_coll);
  4648. MAKESTRING(smcubes_loc_total);
  4649. MAKESTRING(loc_title);
  4650.  
  4651. if (Key == smcubes_loc_coll)
  4652. {
  4653. bRetVal = true;
  4654.  
  4655. //OutData = GenericData(Mini->CurrentDeets.CollectedGems);
  4656.  
  4657. int OutCollectedGems = 0;
  4658. int OutTotalGems = 0;
  4659. Mini->CountGemsInCollection(OutCollectedGems, OutTotalGems, Mini->MapIDToCollID[Mini->LastMap]);
  4660. OutData = GenericData(OutCollectedGems);
  4661. }
  4662. else if (Key == smcubes_loc_total)
  4663. {
  4664. bRetVal = true;
  4665.  
  4666. //OutData = GenericData(Mini->CurrentDeets.TotalGems);
  4667.  
  4668. int OutCollectedGems = 0;
  4669. int OutTotalGems = 0;
  4670. Mini->CountGemsInCollection(OutCollectedGems, OutTotalGems, Mini->MapIDToCollID[Mini->LastMap]);
  4671. OutData = GenericData(OutTotalGems);
  4672. }
  4673. else if (Key == loc_title)
  4674. {
  4675. bRetVal = true;
  4676.  
  4677. MAKESTRINGWITH(LocFile, "ValkyrieGame");
  4678. MAKESTRINGWITH(LocSection, "Collections");
  4679.  
  4680. NHashedString LocKey = Mini->CollIDToLocKey[Mini->MapIDToCollID[Mini->LastMap]];
  4681. NString TitleString = NLocMgr::GetInstance()->GetString(LocFile, LocSection, LocKey);
  4682. opt_assert(TitleString.PercLen() <= 24);
  4683. int NumSpaces = Max(0, (24 - TitleString.PercLen()) / 2);
  4684. for (int i = 0; i < NumSpaces; ++i)
  4685. {
  4686. TitleString = NString(" ") + TitleString;
  4687. }
  4688. OutData = GenericData(TitleString);
  4689. }
  4690. }
  4691. }
  4692. }
  4693.  
  4694. return bRetVal;
  4695. }
  4696.  
  4697. bool Player::HACK_SetData(NHandle CallbackOwner, NHashedString Key, const GenericData& InData)
  4698. {
  4699. bool bRetVal = false;
  4700.  
  4701. SpeedrunComponent* Speed = Resolve<SpeedrunComponent>(CallbackOwner);
  4702. if (Speed)
  4703. {
  4704. bRetVal = Speed->SetData(Key, InData);
  4705. }
  4706.  
  4707. return bRetVal;
  4708. }
  4709.  
  4710. bool Player::HACK_IncData(NHandle CallbackOwner, NHashedString Key, const GenericData& DeltaData)
  4711. {
  4712. bool bRetVal = false;
  4713.  
  4714. SpeedrunComponent* Speed = Resolve<SpeedrunComponent>(CallbackOwner);
  4715. if (Speed)
  4716. {
  4717. bRetVal = Speed->IncData(Key, DeltaData);
  4718. }
  4719.  
  4720. return bRetVal;
  4721. }
  4722.  
  4723. bool Player::HACK_DecData(NHandle CallbackOwner, NHashedString Key, const GenericData& DeltaData)
  4724. {
  4725. bool bRetVal = false;
  4726.  
  4727. SpeedrunComponent* Speed = Resolve<SpeedrunComponent>(CallbackOwner);
  4728. if (Speed)
  4729. {
  4730. bRetVal = Speed->DecData(Key, DeltaData);
  4731. }
  4732.  
  4733. return bRetVal;
  4734. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement