play.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. /* -*- mode: c; tab-width: 4; c-basic-offset: 4; c-file-style: "linux" -*- */
  2. //
  3. // Copyright (c) 2009-2011, Wei Mingzhi <whistler_wmz@users.sf.net>.
  4. // Copyright (c) 2011-2017, SDLPAL development team.
  5. // All rights reserved.
  6. //
  7. // This file is part of SDLPAL.
  8. //
  9. // SDLPAL is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU General Public License as published by
  11. // the Free Software Foundation, either version 3 of the License, or
  12. // (at your option) any later version.
  13. //
  14. // This program is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. // GNU General Public License for more details.
  18. //
  19. // You should have received a copy of the GNU General Public License
  20. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. //
  22. #include "main.h"
  23. VOID
  24. PAL_GameUpdate(
  25. BOOL fTrigger
  26. )
  27. /*++
  28. Purpose:
  29. The main game logic routine. Update the status of everything.
  30. Parameters:
  31. [IN] fTrigger - whether to process trigger events or not.
  32. Return value:
  33. None.
  34. --*/
  35. {
  36. WORD wEventObjectID, wDir;
  37. int i;
  38. LPEVENTOBJECT p;
  39. //
  40. // Check for trigger events
  41. //
  42. if (fTrigger)
  43. {
  44. //
  45. // Check if we are entering a new scene
  46. //
  47. if (gpGlobals->fEnteringScene)
  48. {
  49. //
  50. // Run the script for entering the scene
  51. //
  52. gpGlobals->fEnteringScene = FALSE;
  53. i = gpGlobals->wNumScene - 1;
  54. gpGlobals->g.rgScene[i].wScriptOnEnter =
  55. PAL_RunTriggerScript(gpGlobals->g.rgScene[i].wScriptOnEnter, 0xFFFF);
  56. if (gpGlobals->fEnteringScene || gpGlobals->fGameStart)
  57. {
  58. //
  59. // Don't go further as we're switching to another scene
  60. //
  61. return;
  62. }
  63. PAL_ClearKeyState();
  64. PAL_MakeScene();
  65. }
  66. //
  67. // Update the vanish time for all event objects
  68. //
  69. for (wEventObjectID = 0; wEventObjectID < gpGlobals->g.nEventObject; wEventObjectID++)
  70. {
  71. p = &gpGlobals->g.lprgEventObject[wEventObjectID];
  72. if (p->sVanishTime != 0)
  73. {
  74. p->sVanishTime += ((p->sVanishTime < 0) ? 1 : -1);
  75. }
  76. }
  77. //
  78. // Loop through all event objects in the current scene
  79. //
  80. for (wEventObjectID = gpGlobals->g.rgScene[gpGlobals->wNumScene - 1].wEventObjectIndex + 1;
  81. wEventObjectID <= gpGlobals->g.rgScene[gpGlobals->wNumScene].wEventObjectIndex;
  82. wEventObjectID++)
  83. {
  84. p = &gpGlobals->g.lprgEventObject[wEventObjectID - 1];
  85. if (p->sVanishTime != 0)
  86. {
  87. continue;
  88. }
  89. if (p->sState < 0)
  90. {
  91. if (p->x < PAL_X(gpGlobals->viewport) ||
  92. p->x > PAL_X(gpGlobals->viewport) + 320 ||
  93. p->y < PAL_Y(gpGlobals->viewport) ||
  94. p->y > PAL_Y(gpGlobals->viewport) + 320)
  95. {
  96. p->sState = abs(p->sState);
  97. p->wCurrentFrameNum = 0;
  98. }
  99. }
  100. else if (p->sState > 0 && p->wTriggerMode >= kTriggerTouchNear)
  101. {
  102. //
  103. // This event object can be triggered without manually exploring
  104. //
  105. if (abs(PAL_X(gpGlobals->viewport) + PAL_X(gpGlobals->partyoffset) - p->x) +
  106. abs(PAL_Y(gpGlobals->viewport) + PAL_Y(gpGlobals->partyoffset) - p->y) * 2 <
  107. (p->wTriggerMode - kTriggerTouchNear) * 32 + 16)
  108. {
  109. //
  110. // Player is in the trigger zone.
  111. //
  112. if (p->nSpriteFrames)
  113. {
  114. //
  115. // The sprite has multiple frames. Try to adjust the direction.
  116. //
  117. int xOffset, yOffset;
  118. p->wCurrentFrameNum = 0;
  119. xOffset = PAL_X(gpGlobals->viewport) + PAL_X(gpGlobals->partyoffset) - p->x;
  120. yOffset = PAL_Y(gpGlobals->viewport) + PAL_Y(gpGlobals->partyoffset) - p->y;
  121. if (xOffset > 0)
  122. {
  123. p->wDirection = ((yOffset > 0) ? kDirEast : kDirNorth);
  124. }
  125. else
  126. {
  127. p->wDirection = ((yOffset > 0) ? kDirSouth : kDirWest);
  128. }
  129. //
  130. // Redraw the scene
  131. //
  132. PAL_UpdatePartyGestures(FALSE);
  133. PAL_MakeScene();
  134. VIDEO_UpdateScreen(NULL);
  135. }
  136. //
  137. // Execute the script.
  138. //
  139. p->wTriggerScript = PAL_RunTriggerScript(p->wTriggerScript, wEventObjectID);
  140. PAL_ClearKeyState();
  141. if (gpGlobals->fEnteringScene || gpGlobals->fGameStart)
  142. {
  143. //
  144. // Don't go further on scene switching
  145. //
  146. return;
  147. }
  148. }
  149. }
  150. }
  151. }
  152. //
  153. // Run autoscript for each event objects
  154. //
  155. for (wEventObjectID = gpGlobals->g.rgScene[gpGlobals->wNumScene - 1].wEventObjectIndex + 1;
  156. wEventObjectID <= gpGlobals->g.rgScene[gpGlobals->wNumScene].wEventObjectIndex;
  157. wEventObjectID++)
  158. {
  159. p = &gpGlobals->g.lprgEventObject[wEventObjectID - 1];
  160. if (p->sState > 0 && p->sVanishTime == 0)
  161. {
  162. WORD wScriptEntry = p->wAutoScript;
  163. if (wScriptEntry != 0)
  164. {
  165. p->wAutoScript = PAL_RunAutoScript(wScriptEntry, wEventObjectID);
  166. if (gpGlobals->fEnteringScene || gpGlobals->fGameStart)
  167. {
  168. //
  169. // Don't go further on scene switching
  170. //
  171. return;
  172. }
  173. }
  174. }
  175. //
  176. // Check if the player is in the way
  177. //
  178. if (fTrigger && p->sState >= kObjStateBlocker && p->wSpriteNum != 0 &&
  179. abs(p->x - PAL_X(gpGlobals->viewport) - PAL_X(gpGlobals->partyoffset)) +
  180. abs(p->y - PAL_Y(gpGlobals->viewport) - PAL_Y(gpGlobals->partyoffset)) * 2 <= 12)
  181. {
  182. //
  183. // Player is in the way, try to move a step
  184. //
  185. wDir = (p->wDirection + 1) % 4;
  186. for (i = 0; i < 4; i++)
  187. {
  188. int x, y;
  189. PAL_POS pos;
  190. x = PAL_X(gpGlobals->viewport) + PAL_X(gpGlobals->partyoffset);
  191. y = PAL_Y(gpGlobals->viewport) + PAL_Y(gpGlobals->partyoffset);
  192. x += ((wDir == kDirWest || wDir == kDirSouth) ? -16 : 16);
  193. y += ((wDir == kDirWest || wDir == kDirNorth) ? -8 : 8);
  194. pos = PAL_XY(x, y);
  195. if (!PAL_CheckObstacle(pos, TRUE, 0))
  196. {
  197. //
  198. // move here
  199. //
  200. gpGlobals->viewport = PAL_XY(
  201. PAL_X(pos) - PAL_X(gpGlobals->partyoffset),
  202. PAL_Y(pos) - PAL_Y(gpGlobals->partyoffset));
  203. break;
  204. }
  205. wDir = (wDir + 1) % 4;
  206. }
  207. }
  208. }
  209. gpGlobals->dwFrameNum++;
  210. }
  211. VOID
  212. PAL_GameUseItem(
  213. VOID
  214. )
  215. /*++
  216. Purpose:
  217. Allow player use an item in the game.
  218. Parameters:
  219. None.
  220. Return value:
  221. None.
  222. --*/
  223. {
  224. WORD wObject;
  225. while (TRUE)
  226. {
  227. wObject = PAL_ItemSelectMenu(NULL, kItemFlagUsable);
  228. if (wObject == 0)
  229. {
  230. return;
  231. }
  232. if (!(gpGlobals->g.rgObject[wObject].item.wFlags & kItemFlagApplyToAll))
  233. {
  234. //
  235. // Select the player to use the item on
  236. //
  237. WORD wPlayer = 0;
  238. while (TRUE)
  239. {
  240. wPlayer = PAL_ItemUseMenu(wObject);
  241. if (wPlayer == MENUITEM_VALUE_CANCELLED)
  242. {
  243. break;
  244. }
  245. //
  246. // Run the script
  247. //
  248. gpGlobals->g.rgObject[wObject].item.wScriptOnUse =
  249. PAL_RunTriggerScript(gpGlobals->g.rgObject[wObject].item.wScriptOnUse, wPlayer);
  250. //
  251. // Remove the item if the item is consuming and the script succeeded
  252. //
  253. if ((gpGlobals->g.rgObject[wObject].item.wFlags & kItemFlagConsuming) &&
  254. g_fScriptSuccess)
  255. {
  256. PAL_AddItemToInventory(wObject, -1);
  257. }
  258. }
  259. }
  260. else
  261. {
  262. //
  263. // Run the script
  264. //
  265. gpGlobals->g.rgObject[wObject].item.wScriptOnUse =
  266. PAL_RunTriggerScript(gpGlobals->g.rgObject[wObject].item.wScriptOnUse, 0xFFFF);
  267. //
  268. // Remove the item if the item is consuming and the script succeeded
  269. //
  270. if ((gpGlobals->g.rgObject[wObject].item.wFlags & kItemFlagConsuming) &&
  271. g_fScriptSuccess)
  272. {
  273. PAL_AddItemToInventory(wObject, -1);
  274. }
  275. return;
  276. }
  277. }
  278. }
  279. VOID
  280. PAL_GameEquipItem(
  281. VOID
  282. )
  283. /*++
  284. Purpose:
  285. Allow player equip an item in the game.
  286. Parameters:
  287. None.
  288. Return value:
  289. None.
  290. --*/
  291. {
  292. WORD wObject;
  293. while (TRUE)
  294. {
  295. wObject = PAL_ItemSelectMenu(NULL, kItemFlagEquipable);
  296. if (wObject == 0)
  297. {
  298. return;
  299. }
  300. PAL_EquipItemMenu(wObject);
  301. }
  302. }
  303. VOID
  304. PAL_Search(
  305. VOID
  306. )
  307. /*++
  308. Purpose:
  309. Process searching trigger events.
  310. Parameters:
  311. None.
  312. Return value:
  313. None.
  314. --*/
  315. {
  316. int x, y, xOffset, yOffset, dx, dy, dh, ex, ey, eh, i, k, l;
  317. LPEVENTOBJECT p;
  318. PAL_POS rgPos[13];
  319. //
  320. // Get the party location
  321. //
  322. x = PAL_X(gpGlobals->viewport) + PAL_X(gpGlobals->partyoffset);
  323. y = PAL_Y(gpGlobals->viewport) + PAL_Y(gpGlobals->partyoffset);
  324. if (gpGlobals->wPartyDirection == kDirNorth || gpGlobals->wPartyDirection == kDirEast)
  325. {
  326. xOffset = 16;
  327. }
  328. else
  329. {
  330. xOffset = -16;
  331. }
  332. if (gpGlobals->wPartyDirection == kDirEast || gpGlobals->wPartyDirection == kDirSouth)
  333. {
  334. yOffset = 8;
  335. }
  336. else
  337. {
  338. yOffset = -8;
  339. }
  340. rgPos[0] = PAL_XY(x, y);
  341. for (i = 0; i < 4; i++)
  342. {
  343. rgPos[i * 3 + 1] = PAL_XY(x + xOffset, y + yOffset);
  344. rgPos[i * 3 + 2] = PAL_XY(x, y + yOffset * 2);
  345. rgPos[i * 3 + 3] = PAL_XY(x + xOffset, y);
  346. x += xOffset;
  347. y += yOffset;
  348. }
  349. for (i = 0; i < 13; i++)
  350. {
  351. //
  352. // Convert to map location
  353. //
  354. dh = ((PAL_X(rgPos[i]) % 32) ? 1 : 0);
  355. dx = PAL_X(rgPos[i]) / 32;
  356. dy = PAL_Y(rgPos[i]) / 16;
  357. //
  358. // Loop through all event objects
  359. //
  360. for (k = gpGlobals->g.rgScene[gpGlobals->wNumScene - 1].wEventObjectIndex;
  361. k < gpGlobals->g.rgScene[gpGlobals->wNumScene].wEventObjectIndex; k++)
  362. {
  363. p = &(gpGlobals->g.lprgEventObject[k]);
  364. ex = p->x / 32;
  365. ey = p->y / 16;
  366. eh = ((p->x % 32) ? 1 : 0);
  367. if (p->sState <= 0 || p->wTriggerMode >= kTriggerTouchNear ||
  368. p->wTriggerMode * 6 - 4 < i || dx != ex || dy != ey || dh != eh)
  369. {
  370. continue;
  371. }
  372. //
  373. // Adjust direction/gesture for party members and the event object
  374. //
  375. if (p->nSpriteFrames * 4 > p->wCurrentFrameNum)
  376. {
  377. p->wCurrentFrameNum = 0; // use standing gesture
  378. p->wDirection = (gpGlobals->wPartyDirection + 2) % 4; // face the party
  379. for (l = 0; l <= gpGlobals->wMaxPartyMemberIndex; l++)
  380. {
  381. //
  382. // All party members should face the event object
  383. //
  384. gpGlobals->rgParty[l].wFrame = gpGlobals->wPartyDirection * 3;
  385. }
  386. //
  387. // Redraw everything
  388. //
  389. PAL_MakeScene();
  390. VIDEO_UpdateScreen(NULL);
  391. }
  392. //
  393. // Execute the script
  394. //
  395. p->wTriggerScript = PAL_RunTriggerScript(p->wTriggerScript, k + 1);
  396. //
  397. // Clear inputs and delay for a short time
  398. //
  399. UTIL_Delay(50);
  400. PAL_ClearKeyState();
  401. return; // don't go further
  402. }
  403. }
  404. }
  405. VOID
  406. PAL_StartFrame(
  407. VOID
  408. )
  409. /*++
  410. Purpose:
  411. Starts a video frame. Called once per video frame.
  412. Parameters:
  413. None.
  414. Return value:
  415. None.
  416. --*/
  417. {
  418. //
  419. // Run the game logic of one frame
  420. //
  421. PAL_GameUpdate(TRUE);
  422. if (gpGlobals->fEnteringScene)
  423. {
  424. return;
  425. }
  426. //
  427. // Update the positions and gestures of party members
  428. //
  429. PAL_UpdateParty();
  430. //
  431. // Update the scene
  432. //
  433. PAL_MakeScene();
  434. VIDEO_UpdateScreen(NULL);
  435. if (g_InputState.dwKeyPress & kKeyMenu)
  436. {
  437. //
  438. // Show the in-game menu
  439. //
  440. PAL_InGameMenu();
  441. }
  442. else if (g_InputState.dwKeyPress & kKeyUseItem)
  443. {
  444. //
  445. // Show the use item menu
  446. //
  447. PAL_GameUseItem();
  448. }
  449. else if (g_InputState.dwKeyPress & kKeyThrowItem)
  450. {
  451. //
  452. // Show the equipment menu
  453. //
  454. PAL_GameEquipItem();
  455. }
  456. else if (g_InputState.dwKeyPress & kKeyForce)
  457. {
  458. //
  459. // Show the magic menu
  460. //
  461. PAL_InGameMagicMenu();
  462. }
  463. else if (g_InputState.dwKeyPress & kKeyStatus)
  464. {
  465. //
  466. // Show the player status
  467. //
  468. PAL_PlayerStatus();
  469. }
  470. else if (g_InputState.dwKeyPress & kKeySearch)
  471. {
  472. //
  473. // Process search events
  474. //
  475. PAL_Search();
  476. }
  477. else if (g_InputState.dwKeyPress & kKeyFlee)
  478. {
  479. //
  480. // Quit Game
  481. //
  482. PAL_QuitGame();
  483. }
  484. if (--gpGlobals->wChasespeedChangeCycles == 0)
  485. {
  486. gpGlobals->wChaseRange = 1;
  487. }
  488. }
  489. VOID
  490. PAL_WaitForKey(
  491. WORD wTimeOut
  492. )
  493. /*++
  494. Purpose:
  495. Wait for any key.
  496. Parameters:
  497. [IN] wTimeOut - the maximum time of the waiting. 0 = wait forever.
  498. Return value:
  499. None.
  500. --*/
  501. {
  502. DWORD dwTimeOut = SDL_GetTicks() + wTimeOut;
  503. PAL_ClearKeyState();
  504. while (wTimeOut == 0 || !SDL_TICKS_PASSED(SDL_GetTicks(), dwTimeOut))
  505. {
  506. UTIL_Delay(5);
  507. if (g_InputState.dwKeyPress & (kKeySearch | kKeyMenu))
  508. {
  509. break;
  510. }
  511. }
  512. }