ball.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright 2016 RWS Inc, All Rights Reserved
  4. //
  5. // This program is free software; you can redistribute it and/or modify
  6. // it under the terms of version 2 of the GNU General Public License as published by
  7. // the Free Software Foundation
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License along
  15. // with this program; if not, write to the Free Software Foundation, Inc.,
  16. // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. //
  18. // ball.cpp
  19. // Project: Nostril (aka Postal)
  20. //
  21. // This module impliments the CBall class, which will hopefully become a model
  22. // for most other game objects.
  23. //
  24. // History:
  25. // 12/18/96 MJR Started.
  26. //
  27. // 01/19/97 JMI Now EditNew() actually loads and puts up a dialog.
  28. //
  29. // 01/23/97 JMI The positioning in Render() for Y was + sY / 2. Changed to
  30. // - sY.
  31. //
  32. // 01/27/97 JMI Added override for EditRect() to make this object clickable.
  33. //
  34. // 01/29/97 JMI Now Load() and Save() call the base class versions.
  35. //
  36. // 02/02/97 JMI Added EditHotSpot().
  37. //
  38. // 02/04/97 JMI Changed LoadDib() call to Load() (which now supports
  39. // loading of DIBs).
  40. //
  41. // 02/06/97 JMI Made this from a 2D object to a 3D one.
  42. //
  43. // 02/07/97 JMI Removed m_pipeline and associated members and setup since
  44. // the CScene now owns the pipeline.
  45. //
  46. // 02/11/97 JMI Fixed bug in EditHotSpot.
  47. //
  48. // 02/23/97 JMI In progress, do not use.
  49. //
  50. // 02/23/97 JMI Brought up to date so we can continue to use this as a
  51. // test object.
  52. //
  53. // 02/24/97 JMI For the gravity bounce, I was reversing the current
  54. // velocity but setting the last known position. This caused
  55. // one extra iteration of acceleration. Now I use both the
  56. // old position and the old velocity.
  57. //
  58. // 02/24/97 JMI AdjustPosVel() now uses default param (gravity).
  59. //
  60. // 02/24/97 JMI No longer sets the m_type member of the m_sprite b/c it
  61. // is set by m_sprite's constructor.
  62. //
  63. // 03/06/97 JMI Upgraded to current rspMod360 usage.
  64. //
  65. // 03/13/97 JMI Load now takes a version number.
  66. //
  67. // 04/10/97 BRH Changed ball to use new multi layer attribute maps and the
  68. // helper functions that go with them.
  69. //
  70. // 05/29/97 JMI Got rid of 'old way' of using attributes and put in the
  71. // new.
  72. //
  73. // 06/29/97 JMI Converted EditRect(), EditRender(), and/or Render() to
  74. // use Map3Dto2D().
  75. // Also, fixed priority.
  76. //
  77. // 07/01/97 JMI Replaced use of GetTerrainAttributes() with
  78. // GetHeightAndNoWalk().
  79. //
  80. // 08/05/97 JMI Changed priority to use Z position rather than 2D
  81. // projected Y position.
  82. //
  83. ////////////////////////////////////////////////////////////////////////////////
  84. #define BALL_CPP
  85. #include "RSPiX.h"
  86. #include <string.h>
  87. #include "ball.h"
  88. #include "hood.h"
  89. #include "game.h"
  90. #include "reality.h"
  91. ////////////////////////////////////////////////////////////////////////////////
  92. // Macros/types/etc.
  93. ////////////////////////////////////////////////////////////////////////////////
  94. #define BALL_SOP_FILE "3d/ball.sop"
  95. #define BALL_MSH_FILE "3d/ball.msh"
  96. #define BALL_TEX_FILE "3d/ball.tex"
  97. #define BALL_3D_FILE "3d/ball.dat"
  98. #define BALL_GUI_FILE "res/editor/ball.gui"
  99. // GUI IDs.
  100. #define GUI_ID_OK 1
  101. #define GUI_ID_CANCEL 2
  102. #define GUI_ID_X_OFFSET 3
  103. #define GUI_ID_Y_OFFSET 4
  104. #define GUI_ID_Z_OFFSET 5
  105. ////////////////////////////////////////////////////////////////////////////////
  106. // Variables/data
  107. ////////////////////////////////////////////////////////////////////////////////
  108. short CBall::ms_sFileCount;
  109. /// Standing Animation Files ////////////////////////////////////////////////////
  110. // An array of pointers to res names (one for each animation component).
  111. static char* ms_apszAnimNames[] =
  112. {
  113. "3d/main_bobbing.sop",
  114. "3d/main_bobbing.mesh",
  115. "3d/main_bobbing.tex",
  116. "3d/main_bobbing.hot",
  117. "3d/main_bobbing.bounds",
  118. "3d/main_bobbing.floor",
  119. NULL, // No rigid body for this anim.
  120. NULL // For safety, this should ensure a crash if referenced
  121. // beyond useful portion of array.
  122. };
  123. ////////////////////////////////////////////////////////////////////////////////
  124. // Load object (should call base class version!)
  125. ////////////////////////////////////////////////////////////////////////////////
  126. short CBall::Load( // Returns 0 if successfull, non-zero otherwise
  127. RFile* pFile, // In: File to load from
  128. bool bEditMode, // In: True for edit mode, false otherwise
  129. short sFileCount, // In: File count (unique per file, never 0)
  130. ULONG ulFileVersion) // In: Version of file format to load.
  131. {
  132. short sResult = 0;
  133. // In most cases, the base class Load() should be called.
  134. sResult = CThing::Load(pFile, bEditMode, sFileCount, ulFileVersion);
  135. if (sResult == 0)
  136. {
  137. // Load common data just once per file (not with each object)
  138. if (ms_sFileCount != sFileCount)
  139. {
  140. ms_sFileCount = sFileCount;
  141. // Load static data
  142. switch (ulFileVersion)
  143. {
  144. default:
  145. case 1:
  146. // pFile->Read(ms_acImageName);
  147. break;
  148. }
  149. }
  150. // Load object data
  151. switch (ulFileVersion)
  152. {
  153. default:
  154. case 1:
  155. pFile->Read(&m_dX);
  156. pFile->Read(&m_dY);
  157. pFile->Read(&m_dZ);
  158. pFile->Read(&m_dDX);
  159. pFile->Read(&m_dDY);
  160. pFile->Read(&m_dDZ);
  161. break;
  162. }
  163. // Make sure there were no file errors or format errors . . .
  164. if (!pFile->Error() && sResult == 0)
  165. {
  166. sResult = GetResources();
  167. }
  168. else
  169. {
  170. sResult = -1;
  171. TRACE("CBall::Load(): Error reading from file!\n");
  172. }
  173. }
  174. else
  175. {
  176. TRACE("CBall::Load(): CThing::Load() failed.\n");
  177. }
  178. return sResult;
  179. }
  180. ////////////////////////////////////////////////////////////////////////////////
  181. // Save object (should call base class version!)
  182. ////////////////////////////////////////////////////////////////////////////////
  183. short CBall::Save( // Returns 0 if successfull, non-zero otherwise
  184. RFile* pFile, // In: File to save to
  185. short sFileCount) // In: File count (unique per file, never 0)
  186. {
  187. short sResult = 0;
  188. // In most cases, the base class Save() should be called.
  189. sResult = CThing::Save(pFile, sFileCount);
  190. if (sResult == 0)
  191. {
  192. // Save common data just once per file (not with each object)
  193. if (ms_sFileCount != sFileCount)
  194. {
  195. ms_sFileCount = sFileCount;
  196. // Save static data
  197. // pFile->Write(ms_acImageName);
  198. }
  199. // Save object data
  200. pFile->Write(&m_dX);
  201. pFile->Write(&m_dY);
  202. pFile->Write(&m_dZ);
  203. pFile->Write(&m_dDX);
  204. pFile->Write(&m_dDY);
  205. pFile->Write(&m_dDZ);
  206. sResult = pFile->Error();
  207. }
  208. else
  209. {
  210. TRACE("CBall::Save(): CThing::Save() failed.\n");
  211. }
  212. return sResult;
  213. }
  214. ////////////////////////////////////////////////////////////////////////////////
  215. // Startup object
  216. ////////////////////////////////////////////////////////////////////////////////
  217. short CBall::Startup(void) // Returns 0 if successfull, non-zero otherwise
  218. {
  219. short sResult = 0; // Assume success.
  220. // At this point we can assume the CHood was loaded, so we init our height
  221. m_sPrevHeight = m_pRealm->GetHeight(m_dX, m_dZ);
  222. // HARD-WIRED CODE ALERT!
  223. // Eventually, this should be set via the bounding sphere radius.
  224. m_sCurRadius = 64;
  225. m_lPrevTime = m_pRealm->m_time.GetGameTime();
  226. return sResult;
  227. }
  228. ////////////////////////////////////////////////////////////////////////////////
  229. // Shutdown object
  230. ////////////////////////////////////////////////////////////////////////////////
  231. short CBall::Shutdown(void) // Returns 0 if successfull, non-zero otherwise
  232. {
  233. short sResult = 0;
  234. m_trans.Make1();
  235. return sResult;
  236. }
  237. ////////////////////////////////////////////////////////////////////////////////
  238. // Suspend object
  239. ////////////////////////////////////////////////////////////////////////////////
  240. void CBall::Suspend(void)
  241. {
  242. m_sSuspend++;
  243. }
  244. ////////////////////////////////////////////////////////////////////////////////
  245. // Resume object
  246. ////////////////////////////////////////////////////////////////////////////////
  247. void CBall::Resume(void)
  248. {
  249. m_sSuspend--;
  250. }
  251. ////////////////////////////////////////////////////////////////////////////////
  252. // Update object
  253. ////////////////////////////////////////////////////////////////////////////////
  254. void CBall::Update(void)
  255. {
  256. if (!m_sSuspend)
  257. {
  258. long lCurTime = m_pRealm->m_time.GetGameTime();
  259. double dDeltaSeconds = (lCurTime - m_lPrevTime) / 1000.0;
  260. // Adjust vertical velocity and calculate new position.
  261. double dNewY = m_dY;
  262. double dNewDY = m_dDY;
  263. AdjustPosVel(&dNewY, &dNewDY, dDeltaSeconds);
  264. // Calculate new position.
  265. double dNewX = m_dX + m_dDX;
  266. double dNewZ = m_dZ + m_dDZ;
  267. // Bounce off edges of world.
  268. // Get height and 'no walk' status at new position.
  269. bool bNoWalk;
  270. short sHeight = m_pRealm->GetHeightAndNoWalk(dNewX, dNewY, &bNoWalk);
  271. // If new Y position is less than terrain height or 'no walk' zone . . .
  272. if (dNewY < sHeight || bNoWalk == true)
  273. {
  274. // If at the last position we would be above the terrain . . .
  275. if (dNewY > m_sPrevHeight)
  276. {
  277. // We've hit a wall.
  278. // Reverse both directions (this is cheesy, but it's just a demo object!)
  279. m_dDX = -m_dDX;
  280. m_dDZ = -m_dDZ;
  281. // Restore previous position to avoid getting embedded in anything
  282. dNewX = m_dX;
  283. dNewZ = m_dZ;
  284. }
  285. else
  286. {
  287. // We've hit flat terrain.
  288. // Use previous velocity. Otherwise, we'd have accelerated past the
  289. // ground.
  290. dNewDY = -m_dDY;
  291. // Restore previous position to avoid getting embedded in anything
  292. dNewY = m_dY;
  293. }
  294. }
  295. // Save new height
  296. m_sPrevHeight = sHeight;
  297. // Update position
  298. m_dX = dNewX;
  299. m_dY = dNewY;
  300. m_dZ = dNewZ;
  301. // Update velocities.
  302. m_dDY = dNewDY;
  303. // Store time.
  304. m_lPrevTime = lCurTime;
  305. }
  306. }
  307. ////////////////////////////////////////////////////////////////////////////////
  308. // Render object
  309. ////////////////////////////////////////////////////////////////////////////////
  310. void CBall::Render(void)
  311. {
  312. // No special flags
  313. m_sprite.m_sInFlags = 0;
  314. // Map from 3d to 2d coords
  315. Map3Dto2D(
  316. m_dX,
  317. m_dY,
  318. m_dZ,
  319. &(m_sprite.m_sX2),
  320. &(m_sprite.m_sY2) );
  321. // Priority is based on 3D hotspot which is where we're drawn.
  322. m_sprite.m_sPriority = m_dZ;
  323. m_sprite.m_sLayer = CRealm::GetLayerViaAttrib(
  324. m_pRealm->GetLayer((short) m_dX, (short) m_dZ));
  325. // Cheese festival rotation.
  326. m_trans.Ry(rspMod360(m_dDX));
  327. m_trans.Rz(rspMod360(m_dDZ));
  328. long lTime = m_pRealm->m_time.GetGameTime();
  329. m_sprite.m_pmesh = (RMesh*)m_anim.m_pmeshes->GetAtTime(lTime);
  330. m_sprite.m_psop = (RSop*)m_anim.m_psops->GetAtTime(lTime);
  331. m_sprite.m_ptex = (RTexture*)m_anim.m_ptextures->GetAtTime(lTime);
  332. m_sprite.m_psphere = (RP3d*)m_anim.m_pbounds->GetAtTime(lTime);
  333. m_sprite.m_ptrans = &m_trans;
  334. m_sprite.m_sRadius = m_sCurRadius;
  335. // Update sprite in scene
  336. m_pRealm->m_scene.UpdateSprite(&m_sprite);
  337. }
  338. ////////////////////////////////////////////////////////////////////////////////
  339. // Called by editor to init new object at specified position
  340. ////////////////////////////////////////////////////////////////////////////////
  341. short CBall::EditNew( // Returns 0 if successfull, non-zero otherwise
  342. short sX, // In: New x coord
  343. short sY, // In: New y coord
  344. short sZ) // In: New z coord
  345. {
  346. short sResult = 0;
  347. // Use specified position
  348. m_dX = sX;
  349. m_dY = sY;
  350. m_dZ = sZ;
  351. // Load resources.
  352. sResult = GetResources();
  353. if (sResult == 0)
  354. {
  355. sResult = Shutdown();
  356. if (sResult == 0)
  357. {
  358. // Attempt to startup as if in a real play . . .
  359. sResult = Startup();
  360. }
  361. }
  362. return sResult;
  363. }
  364. ////////////////////////////////////////////////////////////////////////////////
  365. // Called by editor to modify object
  366. ////////////////////////////////////////////////////////////////////////////////
  367. short CBall::EditModify(void)
  368. {
  369. short sResult = 0;
  370. // Load GUI . . .
  371. RGuiItem* pguiRoot = RGuiItem::LoadInstantiate(FullPath(GAME_PATH_VD, BALL_GUI_FILE) );
  372. if (pguiRoot != NULL)
  373. {
  374. // Modal loop.
  375. rspClearAllInputEvents();
  376. // Get the two items that can cause donage.
  377. RGuiItem* pguiOk = pguiRoot->GetItemFromId(GUI_ID_OK);
  378. RGuiItem* pguiCancel = pguiRoot->GetItemFromId(GUI_ID_CANCEL);
  379. if (pguiOk != NULL && pguiCancel != NULL)
  380. {
  381. // Prepare values.
  382. // These should definitely check to make sure they exist.
  383. // A nice inline helper function that takes varargs would do.
  384. RGuiItem* pguiEditX = pguiRoot->GetItemFromId(GUI_ID_X_OFFSET);
  385. if (pguiEditX != NULL)
  386. {
  387. pguiEditX->SetText("%g", m_dDX);
  388. // Compose with new text.
  389. pguiEditX->Compose();
  390. }
  391. RGuiItem* pguiEditY = pguiRoot->GetItemFromId(GUI_ID_Y_OFFSET);
  392. if (pguiEditY != NULL)
  393. {
  394. pguiEditY->SetText("%g", m_dDY);
  395. // Compose with new text.
  396. pguiEditY->Compose();
  397. }
  398. RGuiItem* pguiEditZ = pguiRoot->GetItemFromId(GUI_ID_Z_OFFSET);
  399. if (pguiEditZ != NULL)
  400. {
  401. pguiEditZ->SetText("%g", m_dDZ);
  402. // Compose with new text.
  403. pguiEditZ->Compose();
  404. }
  405. if (DoGui(pguiRoot) == GUI_ID_OK)
  406. {
  407. // Free any existing resources.
  408. FreeResources();
  409. // Set values.
  410. if (pguiEditX != NULL)
  411. {
  412. m_dDX = strtod(pguiEditX->m_szText, NULL);
  413. }
  414. if (pguiEditY != NULL)
  415. {
  416. m_dDY = strtod(pguiEditY->m_szText, NULL);
  417. }
  418. if (pguiEditZ != NULL)
  419. {
  420. m_dDZ = strtod(pguiEditZ->m_szText, NULL);
  421. }
  422. // Load resources.
  423. sResult = GetResources();
  424. if (sResult == 0)
  425. {
  426. sResult = Shutdown();
  427. if (sResult == 0)
  428. {
  429. // Attempt to startup as if in a real play . . .
  430. sResult = Startup();
  431. }
  432. }
  433. }
  434. else
  435. {
  436. // User aborted.
  437. sResult = 3;
  438. }
  439. rspClearAllInputEvents();
  440. }
  441. else
  442. {
  443. TRACE("EditNew(): GUI missing item(s) with ID %d or %d.\n",
  444. GUI_ID_OK,
  445. GUI_ID_CANCEL);
  446. sResult = 2;
  447. }
  448. // Done with GUIs.
  449. delete pguiRoot;
  450. }
  451. else
  452. {
  453. TRACE("EditNew(): Failed to load GUI file \"%s\".\n", BALL_GUI_FILE);
  454. sResult = 1;
  455. }
  456. return sResult;
  457. }
  458. ////////////////////////////////////////////////////////////////////////////////
  459. // Called by editor to move object to specified position
  460. ////////////////////////////////////////////////////////////////////////////////
  461. short CBall::EditMove( // Returns 0 if successfull, non-zero otherwise
  462. short sX, // In: New x coord
  463. short sY, // In: New y coord
  464. short sZ) // In: New z coord
  465. {
  466. m_dX = sX;
  467. m_dY = sY;
  468. m_dZ = sZ;
  469. return 0;
  470. }
  471. ////////////////////////////////////////////////////////////////////////////////
  472. // Called by editor to update object
  473. ////////////////////////////////////////////////////////////////////////////////
  474. void CBall::EditUpdate(void)
  475. {
  476. }
  477. ////////////////////////////////////////////////////////////////////////////////
  478. // Called by editor to render object
  479. ////////////////////////////////////////////////////////////////////////////////
  480. void CBall::EditRender(void)
  481. {
  482. // In some cases, object's might need to do a special-case render in edit
  483. // mode because Startup() isn't called. In this case it doesn't matter, so
  484. // we can call the normal Render().
  485. Render();
  486. }
  487. ////////////////////////////////////////////////////////////////////////////////
  488. // Called by editor to get the clickable pos/area of an object.
  489. // (virtual (Overridden here)).
  490. ////////////////////////////////////////////////////////////////////////////////
  491. void CBall::EditRect( // Returns nothiing.
  492. RRect* prc) // Out: Clickable pos/area of object.
  493. {
  494. Map3Dto2D(
  495. m_dX,
  496. m_dY,
  497. m_dZ,
  498. &(prc->sX),
  499. &(prc->sY) );
  500. prc->sX -= m_sCurRadius;
  501. prc->sY -= m_sCurRadius;
  502. prc->sW = m_sCurRadius * 2;
  503. prc->sH = m_sCurRadius * 2;
  504. }
  505. ////////////////////////////////////////////////////////////////////////////////
  506. // Called by editor to get the hotspot of an object in 2D.
  507. // (virtual (Overridden here)).
  508. ////////////////////////////////////////////////////////////////////////////////
  509. void CBall::EditHotSpot( // Returns nothiing.
  510. short* psX, // Out: X coord of 2D hotspot relative to
  511. // EditRect() pos.
  512. short* psY) // Out: Y coord of 2D hotspot relative to
  513. // EditRect() pos.
  514. {
  515. *psX = m_sCurRadius;
  516. *psY = m_sCurRadius;
  517. }
  518. ////////////////////////////////////////////////////////////////////////////////
  519. // Get all required resources
  520. ////////////////////////////////////////////////////////////////////////////////
  521. short CBall::GetResources(void) // Returns 0 if successfull, non-zero otherwise
  522. {
  523. short sResult = 0;
  524. sResult = m_anim.Get(
  525. ms_apszAnimNames,
  526. RChannel_LoopAtStart | RChannel_LoopAtEnd);
  527. return sResult;
  528. }
  529. ////////////////////////////////////////////////////////////////////////////////
  530. // Free all resources
  531. ////////////////////////////////////////////////////////////////////////////////
  532. short CBall::FreeResources(void) // Returns 0 if successfull, non-zero otherwise
  533. {
  534. short sResult = 0;
  535. m_anim.Release();
  536. return sResult;
  537. }
  538. ////////////////////////////////////////////////////////////////////////////////
  539. // EOF
  540. ////////////////////////////////////////////////////////////////////////////////