battle.c 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491
  1. /* -*- mode: c; tab-width: 4; c-basic-offset: 3; c-file-style: "linux" -*- */
  2. //
  3. // Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
  4. // All rights reserved.
  5. //
  6. // This file is part of SDLPAL.
  7. //
  8. // SDLPAL is free software: you can redistribute it and/or modify
  9. // it under the terms of the GNU General Public License as published by
  10. // the Free Software Foundation, either version 3 of the License, or
  11. // (at your option) any later version.
  12. //
  13. // This program is distributed in the hope that it will be useful,
  14. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. // GNU General Public License for more details.
  17. //
  18. // You should have received a copy of the GNU General Public License
  19. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  20. //
  21. #include "main.h"
  22. BATTLE g_Battle;
  23. WORD
  24. g_rgPlayerPos[3][3][2] = {
  25. {{240, 170}}, // one player
  26. {{200, 176}, {256, 152}}, // two players
  27. {{180, 180}, {234, 170}, {270, 146}} // three players
  28. };
  29. VOID
  30. PAL_BattleMakeScene(
  31. VOID
  32. )
  33. /*++
  34. Purpose:
  35. Generate the battle scene into the scene buffer.
  36. Parameters:
  37. None.
  38. Return value:
  39. None.
  40. --*/
  41. {
  42. int i;
  43. PAL_POS pos;
  44. LPBYTE pSrc, pDst;
  45. BYTE b;
  46. //
  47. // Draw the background
  48. //
  49. pSrc = g_Battle.lpBackground->pixels;
  50. pDst = g_Battle.lpSceneBuf->pixels;
  51. for (i = 0; i < g_Battle.lpSceneBuf->pitch * g_Battle.lpSceneBuf->h; i++)
  52. {
  53. b = (*pSrc & 0x0F);
  54. b += g_Battle.sBackgroundColorShift;
  55. if (b & 0x80)
  56. {
  57. b = 0;
  58. }
  59. else if (b & 0x70)
  60. {
  61. b = 0x0F;
  62. }
  63. *pDst = (b | (*pSrc & 0xF0));
  64. ++pSrc;
  65. ++pDst;
  66. }
  67. PAL_ApplyWave(g_Battle.lpSceneBuf);
  68. //
  69. // Draw the enemies
  70. //
  71. for (i = g_Battle.wMaxEnemyIndex; i >= 0; i--)
  72. {
  73. pos = g_Battle.rgEnemy[i].pos;
  74. if (PAL_CheckRoleStatus(
  75. g_Battle.rgEnemy[i].rgwStatus, 3, TRUE, TRUE,
  76. kStatusConfused, TRUE,
  77. kStatusSleep, FALSE,
  78. kStatusParalyzedOrSlow, FALSE))
  79. {
  80. //
  81. // Enemy is confused
  82. //
  83. pos = PAL_XY(PAL_X(pos) + RandomLong(-1, 1), PAL_Y(pos));
  84. }
  85. pos = PAL_XY(PAL_X(pos) - PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame)) / 2,
  86. PAL_Y(pos) - PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame)));
  87. if (g_Battle.rgEnemy[i].wObjectID != 0)
  88. {
  89. if (g_Battle.rgEnemy[i].iColorShift)
  90. {
  91. PAL_RLEBlitWithColorShift(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame),
  92. g_Battle.lpSceneBuf, pos, g_Battle.rgEnemy[i].iColorShift);
  93. }
  94. else
  95. {
  96. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame),
  97. g_Battle.lpSceneBuf, pos);
  98. }
  99. }
  100. }
  101. if (g_Battle.lpSummonSprite != NULL)
  102. {
  103. //
  104. // Draw the summoned god
  105. //
  106. pos = PAL_XY(PAL_X(g_Battle.posSummon) - PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.lpSummonSprite, g_Battle.iSummonFrame)) / 2,
  107. PAL_Y(g_Battle.posSummon) - PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.lpSummonSprite, g_Battle.iSummonFrame)));
  108. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(g_Battle.lpSummonSprite, g_Battle.iSummonFrame),
  109. g_Battle.lpSceneBuf, pos);
  110. }
  111. else
  112. {
  113. //
  114. // Draw the players
  115. //
  116. for (i = gpGlobals->wMaxPartyMemberIndex; i >= 0; i--)
  117. {
  118. pos = g_Battle.rgPlayer[i].pos;
  119. if (PAL_CheckRoleStatus(
  120. gpGlobals->rgPlayerStatus[gpGlobals->rgParty[i].wPlayerRole], 3, TRUE, TRUE,
  121. kStatusConfused, TRUE, kStatusSleep, FALSE, kStatusParalyzedOrSlow, FALSE) &&
  122. gpGlobals->g.PlayerRoles.rgwHP[gpGlobals->rgParty[i].wPlayerRole] > 0)
  123. {
  124. //
  125. // Player is confused
  126. //
  127. continue;
  128. }
  129. pos = PAL_XY(PAL_X(pos) - PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame)) / 2,
  130. PAL_Y(pos) - PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame)));
  131. if (g_Battle.rgPlayer[i].iColorShift != 0)
  132. {
  133. PAL_RLEBlitWithColorShift(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame),
  134. g_Battle.lpSceneBuf, pos, g_Battle.rgPlayer[i].iColorShift);
  135. }
  136. else if (g_Battle.iHidingTime == 0)
  137. {
  138. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame),
  139. g_Battle.lpSceneBuf, pos);
  140. }
  141. }
  142. //
  143. // Confused players should be drawn on top of normal players
  144. //
  145. for (i = gpGlobals->wMaxPartyMemberIndex; i >= 0; i--)
  146. {
  147. if (PAL_CheckRoleStatus(
  148. gpGlobals->rgPlayerStatus[gpGlobals->rgParty[i].wPlayerRole], 3, TRUE, TRUE,
  149. kStatusConfused, TRUE, kStatusSleep, FALSE, kStatusParalyzedOrSlow, FALSE) &&
  150. gpGlobals->g.PlayerRoles.rgwHP[gpGlobals->rgParty[i].wPlayerRole] > 0)
  151. {
  152. //
  153. // Player is confused
  154. //
  155. pos = PAL_XY(PAL_X(g_Battle.rgPlayer[i].pos), PAL_Y(g_Battle.rgPlayer[i].pos) + RandomLong(-1, 1));
  156. pos = PAL_XY(PAL_X(pos) - PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame)) / 2,
  157. PAL_Y(pos) - PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame)));
  158. if (g_Battle.rgPlayer[i].iColorShift != 0)
  159. {
  160. PAL_RLEBlitWithColorShift(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame),
  161. g_Battle.lpSceneBuf, pos, g_Battle.rgPlayer[i].iColorShift);
  162. }
  163. else if (g_Battle.iHidingTime == 0)
  164. {
  165. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame),
  166. g_Battle.lpSceneBuf, pos);
  167. }
  168. }
  169. }
  170. }
  171. }
  172. VOID
  173. PAL_BattleBackupScene(
  174. VOID
  175. )
  176. /*++
  177. Purpose:
  178. Backup the scene buffer.
  179. Parameters:
  180. None.
  181. Return value:
  182. None.
  183. --*/
  184. {
  185. SDL_BlitSurface(g_Battle.lpSceneBuf, NULL, gpScreenBak, NULL);
  186. }
  187. VOID
  188. PAL_BattleFadeScene(
  189. VOID
  190. )
  191. /*++
  192. Purpose:
  193. Fade in the scene of battle.
  194. Parameters:
  195. None.
  196. Return value:
  197. None.
  198. --*/
  199. {
  200. int i, j, k;
  201. DWORD time;
  202. BYTE a, b;
  203. const int rgIndex[6] = {0, 3, 1, 5, 2, 4};
  204. time = SDL_GetTicks();
  205. for (i = 0; i < 12; i++)
  206. {
  207. for (j = 0; j < 6; j++)
  208. {
  209. PAL_DelayUntil(time);
  210. time = SDL_GetTicks() + 16;
  211. //
  212. // Blend the pixels in the 2 buffers, and put the result into the
  213. // backup buffer
  214. //
  215. for (k = rgIndex[j]; k < gpScreen->pitch * gpScreen->h; k += 6)
  216. {
  217. a = ((LPBYTE)(g_Battle.lpSceneBuf->pixels))[k];
  218. b = ((LPBYTE)(gpScreenBak->pixels))[k];
  219. if (i > 0)
  220. {
  221. if ((a & 0x0F) > (b & 0x0F))
  222. {
  223. b++;
  224. }
  225. else if ((a & 0x0F) < (b & 0x0F))
  226. {
  227. b--;
  228. }
  229. }
  230. ((LPBYTE)(gpScreenBak->pixels))[k] = ((a & 0xF0) | (b & 0x0F));
  231. }
  232. //
  233. // Draw the backup buffer to the screen
  234. //
  235. SDL_BlitSurface(gpScreenBak, NULL, gpScreen, NULL);
  236. PAL_BattleUIUpdate();
  237. VIDEO_UpdateScreen(NULL);
  238. }
  239. }
  240. //
  241. // Draw the result buffer to the screen as the final step
  242. //
  243. SDL_BlitSurface(g_Battle.lpSceneBuf, NULL, gpScreen, NULL);
  244. PAL_BattleUIUpdate();
  245. VIDEO_UpdateScreen(NULL);
  246. }
  247. static BATTLERESULT
  248. PAL_BattleMain(
  249. VOID
  250. )
  251. /*++
  252. Purpose:
  253. The main battle routine.
  254. Parameters:
  255. None.
  256. Return value:
  257. The result of the battle.
  258. --*/
  259. {
  260. int i;
  261. DWORD dwTime;
  262. VIDEO_BackupScreen();
  263. //
  264. // Generate the scene and draw the scene to the screen buffer
  265. //
  266. PAL_BattleMakeScene();
  267. SDL_BlitSurface(g_Battle.lpSceneBuf, NULL, gpScreen, NULL);
  268. //
  269. // Fade out the music and delay for a while
  270. //
  271. AUDIO_PlayMusic(0, FALSE, 1);
  272. UTIL_Delay(200);
  273. //
  274. // Switch the screen
  275. //
  276. VIDEO_SwitchScreen(5);
  277. //
  278. // Play the battle music
  279. //
  280. AUDIO_PlayMusic(gpGlobals->wNumBattleMusic, TRUE, 0);
  281. //
  282. // Fade in the screen when needed
  283. //
  284. if (gpGlobals->fNeedToFadeIn)
  285. {
  286. PAL_FadeIn(gpGlobals->wNumPalette, gpGlobals->fNightPalette, 1);
  287. gpGlobals->fNeedToFadeIn = FALSE;
  288. }
  289. //
  290. // Run the pre-battle scripts for each enemies
  291. //
  292. for (i = 0; i <= g_Battle.wMaxEnemyIndex; i++)
  293. {
  294. g_Battle.rgEnemy[i].wScriptOnTurnStart =
  295. PAL_RunTriggerScript(g_Battle.rgEnemy[i].wScriptOnTurnStart, i);
  296. if (g_Battle.BattleResult != kBattleResultPreBattle)
  297. {
  298. break;
  299. }
  300. }
  301. if (g_Battle.BattleResult == kBattleResultPreBattle)
  302. {
  303. g_Battle.BattleResult = kBattleResultOnGoing;
  304. }
  305. if (!PAL_IsClassicBattle())
  306. {
  307. PAL_UpdateTimeChargingUnit();
  308. }
  309. dwTime = SDL_GetTicks();
  310. PAL_ClearKeyState();
  311. //
  312. // Run the main battle loop.
  313. //
  314. while (TRUE)
  315. {
  316. //
  317. // Break out if the battle ended.
  318. //
  319. if (g_Battle.BattleResult != kBattleResultOnGoing)
  320. {
  321. break;
  322. }
  323. //
  324. // Wait for the time of one frame. Accept input here.
  325. //
  326. PAL_DelayUntil(dwTime);
  327. //
  328. // Set the time of the next frame.
  329. //
  330. dwTime = SDL_GetTicks() + BATTLE_FRAME_TIME;
  331. //
  332. // Run the main frame routine.
  333. //
  334. PAL_BattleStartFrame();
  335. //
  336. // Update the screen.
  337. //
  338. VIDEO_UpdateScreen(NULL);
  339. }
  340. //
  341. // Return the battle result
  342. //
  343. return g_Battle.BattleResult;
  344. }
  345. static VOID
  346. PAL_FreeBattleSprites(
  347. VOID
  348. )
  349. /*++
  350. Purpose:
  351. Free all the loaded sprites.
  352. Parameters:
  353. None.
  354. Return value:
  355. None.
  356. --*/
  357. {
  358. int i;
  359. //
  360. // Free all the loaded sprites
  361. //
  362. for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
  363. {
  364. if (g_Battle.rgPlayer[i].lpSprite != NULL)
  365. {
  366. free(g_Battle.rgPlayer[i].lpSprite);
  367. }
  368. g_Battle.rgPlayer[i].lpSprite = NULL;
  369. }
  370. for (i = 0; i <= g_Battle.wMaxEnemyIndex; i++)
  371. {
  372. if (g_Battle.rgEnemy[i].lpSprite != NULL)
  373. {
  374. free(g_Battle.rgEnemy[i].lpSprite);
  375. }
  376. g_Battle.rgEnemy[i].lpSprite = NULL;
  377. }
  378. if (g_Battle.lpSummonSprite != NULL)
  379. {
  380. free(g_Battle.lpSummonSprite);
  381. }
  382. g_Battle.lpSummonSprite = NULL;
  383. }
  384. VOID
  385. PAL_LoadBattleSprites(
  386. VOID
  387. )
  388. /*++
  389. Purpose:
  390. Load all the loaded sprites.
  391. Parameters:
  392. None.
  393. Return value:
  394. None.
  395. --*/
  396. {
  397. int i, l, x, y, s;
  398. FILE *fp;
  399. PAL_FreeBattleSprites();
  400. fp = UTIL_OpenRequiredFile("abc.mkf");
  401. //
  402. // Load battle sprites for players
  403. //
  404. for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
  405. {
  406. s = PAL_GetPlayerBattleSprite(gpGlobals->rgParty[i].wPlayerRole);
  407. l = PAL_MKFGetDecompressedSize(s, gpGlobals->f.fpF);
  408. if (l <= 0)
  409. {
  410. continue;
  411. }
  412. g_Battle.rgPlayer[i].lpSprite = UTIL_calloc(l, 1);
  413. PAL_MKFDecompressChunk(g_Battle.rgPlayer[i].lpSprite, l,
  414. s, gpGlobals->f.fpF);
  415. //
  416. // Set the default position for this player
  417. //
  418. x = g_rgPlayerPos[gpGlobals->wMaxPartyMemberIndex][i][0];
  419. y = g_rgPlayerPos[gpGlobals->wMaxPartyMemberIndex][i][1];
  420. g_Battle.rgPlayer[i].posOriginal = PAL_XY(x, y);
  421. g_Battle.rgPlayer[i].pos = PAL_XY(x, y);
  422. }
  423. //
  424. // Load battle sprites for enemies
  425. //
  426. for (i = 0; i < MAX_ENEMIES_IN_TEAM; i++)
  427. {
  428. if (g_Battle.rgEnemy[i].wObjectID == 0)
  429. {
  430. continue;
  431. }
  432. l = PAL_MKFGetDecompressedSize(
  433. gpGlobals->g.rgObject[g_Battle.rgEnemy[i].wObjectID].enemy.wEnemyID, fp);
  434. if (l <= 0)
  435. {
  436. continue;
  437. }
  438. g_Battle.rgEnemy[i].lpSprite = UTIL_calloc(l, 1);
  439. PAL_MKFDecompressChunk(g_Battle.rgEnemy[i].lpSprite, l,
  440. gpGlobals->g.rgObject[g_Battle.rgEnemy[i].wObjectID].enemy.wEnemyID, fp);
  441. //
  442. // Set the default position for this enemy
  443. //
  444. x = gpGlobals->g.EnemyPos.pos[i][g_Battle.wMaxEnemyIndex].x;
  445. y = gpGlobals->g.EnemyPos.pos[i][g_Battle.wMaxEnemyIndex].y;
  446. y += g_Battle.rgEnemy[i].e.wYPosOffset;
  447. g_Battle.rgEnemy[i].posOriginal = PAL_XY(x, y);
  448. g_Battle.rgEnemy[i].pos = PAL_XY(x, y);
  449. }
  450. fclose(fp);
  451. }
  452. static VOID
  453. PAL_LoadBattleBackground(
  454. VOID
  455. )
  456. /*++
  457. Purpose:
  458. Load the screen background picture of the battle.
  459. Parameters:
  460. None.
  461. Return value:
  462. None.
  463. --*/
  464. {
  465. PAL_LARGE BYTE buf[320 * 200];
  466. //
  467. // Create the surface
  468. //
  469. g_Battle.lpBackground =
  470. SDL_CreateRGBSurface(gpScreen->flags & ~SDL_HWSURFACE, 320, 200, 8,
  471. gpScreen->format->Rmask, gpScreen->format->Gmask,
  472. gpScreen->format->Bmask, gpScreen->format->Amask);
  473. if (g_Battle.lpBackground == NULL)
  474. {
  475. TerminateOnError("PAL_LoadBattleBackground(): failed to create surface!");
  476. }
  477. #if SDL_VERSION_ATLEAST(2, 0, 0)
  478. SDL_SetSurfacePalette(g_Battle.lpBackground, gpScreen->format->palette);
  479. #else
  480. SDL_SetPalette(g_Battle.lpBackground, SDL_PHYSPAL | SDL_LOGPAL, VIDEO_GetPalette(), 0, 256);
  481. #endif
  482. //
  483. // Load the picture
  484. //
  485. PAL_MKFDecompressChunk(buf, 320 * 200, gpGlobals->wNumBattleField, gpGlobals->f.fpFBP);
  486. //
  487. // Draw the picture to the surface.
  488. //
  489. PAL_FBPBlitToSurface(buf, g_Battle.lpBackground);
  490. }
  491. static VOID
  492. PAL_BattleWon(
  493. VOID
  494. )
  495. /*++
  496. Purpose:
  497. Show the "you win" message and add the experience points for players.
  498. Parameters:
  499. None.
  500. Return value:
  501. None.
  502. --*/
  503. {
  504. const SDL_Rect rect = {0, 60, 320, 100};
  505. SDL_Rect rect1 = {80, 0, 180, 200};
  506. int i, j, iTotalCount;
  507. DWORD dwExp;
  508. WORD w;
  509. BOOL fLevelUp;
  510. PLAYERROLES OrigPlayerRoles;
  511. //
  512. // Backup the initial player stats
  513. //
  514. OrigPlayerRoles = gpGlobals->g.PlayerRoles;
  515. VIDEO_BackupScreen();
  516. if (g_Battle.iExpGained > 0)
  517. {
  518. int w1 = PAL_WordWidth(BATTLEWIN_GETEXP_LABEL) + 3;
  519. int ww1 = (w1 - 8) << 3;
  520. //
  521. // Play the "battle win" music
  522. //
  523. AUDIO_PlayMusic(g_Battle.fIsBoss ? 2 : 3, FALSE, 0);
  524. //
  525. // Show the message about the total number of exp. and cash gained
  526. //
  527. PAL_CreateSingleLineBox(PAL_XY(83 - ww1, 60), w1, FALSE);
  528. PAL_CreateSingleLineBox(PAL_XY(65, 105), 10, FALSE);
  529. PAL_DrawText(PAL_GetWord(BATTLEWIN_GETEXP_LABEL), PAL_XY(95 - ww1, 70), 0, FALSE, FALSE, FALSE);
  530. PAL_DrawText(PAL_GetWord(BATTLEWIN_BEATENEMY_LABEL), PAL_XY(77, 115), 0, FALSE, FALSE, FALSE);
  531. PAL_DrawText(PAL_GetWord(BATTLEWIN_DOLLAR_LABEL), PAL_XY(197, 115), 0, FALSE, FALSE, FALSE);
  532. PAL_DrawNumber(g_Battle.iExpGained, 5, PAL_XY(182 + ww1, 74), kNumColorYellow, kNumAlignRight);
  533. PAL_DrawNumber(g_Battle.iCashGained, 5, PAL_XY(162, 119), kNumColorYellow, kNumAlignMid);
  534. VIDEO_UpdateScreen(&rect);
  535. PAL_WaitForKey(g_Battle.fIsBoss ? 5500 : 3000);
  536. }
  537. //
  538. // Add the cash value
  539. //
  540. gpGlobals->dwCash += g_Battle.iCashGained;
  541. const MENUITEM rgFakeMenuItem[] =
  542. {
  543. // value label enabled pos
  544. { 1, gpGlobals->g.PlayerRoles.rgwName[0], TRUE, PAL_XY(0, 0) },
  545. { 2, gpGlobals->g.PlayerRoles.rgwName[1], TRUE, PAL_XY(0, 0) },
  546. { 3, gpGlobals->g.PlayerRoles.rgwName[2], TRUE, PAL_XY(0, 0) },
  547. { 4, gpGlobals->g.PlayerRoles.rgwName[3], TRUE, PAL_XY(0, 0) },
  548. { 5, gpGlobals->g.PlayerRoles.rgwName[4], TRUE, PAL_XY(0, 0) },
  549. { 6, gpGlobals->g.PlayerRoles.rgwName[5], TRUE, PAL_XY(0, 0) },
  550. };
  551. int maxNameWidth = PAL_MenuTextMaxWidth(rgFakeMenuItem, sizeof(rgFakeMenuItem) / sizeof(MENUITEM));
  552. const MENUITEM rgFakeMenuItem2[] =
  553. {
  554. // value label enabled pos
  555. { 1, STATUS_LABEL_LEVEL, TRUE, PAL_XY(0, 0) },
  556. { 2, STATUS_LABEL_HP, TRUE, PAL_XY(0, 0) },
  557. { 3, STATUS_LABEL_MP, TRUE, PAL_XY(0, 0) },
  558. { 4, STATUS_LABEL_ATTACKPOWER, TRUE, PAL_XY(0, 0) },
  559. { 5, STATUS_LABEL_MAGICPOWER, TRUE, PAL_XY(0, 0) },
  560. { 6, STATUS_LABEL_RESISTANCE, TRUE, PAL_XY(0, 0) },
  561. { 7, STATUS_LABEL_DEXTERITY, TRUE, PAL_XY(0, 0) },
  562. { 8, STATUS_LABEL_FLEERATE, TRUE, PAL_XY(0, 0) },
  563. };
  564. int maxPropertyWidth = PAL_MenuTextMaxWidth(rgFakeMenuItem2, sizeof(rgFakeMenuItem2) / sizeof(MENUITEM)) - 1;
  565. int propertyLength = maxPropertyWidth - 1;
  566. int offsetX = -8*propertyLength;
  567. rect1.x += offsetX;
  568. rect1.w -= 2*offsetX;
  569. //
  570. // Add the experience points for each players
  571. //
  572. for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
  573. {
  574. fLevelUp = FALSE;
  575. w = gpGlobals->rgParty[i].wPlayerRole;
  576. if (gpGlobals->g.PlayerRoles.rgwHP[w] == 0)
  577. {
  578. continue; // don't care about dead players
  579. }
  580. dwExp = gpGlobals->Exp.rgPrimaryExp[w].wExp;
  581. dwExp += g_Battle.iExpGained;
  582. if (gpGlobals->g.PlayerRoles.rgwLevel[w] > MAX_LEVELS)
  583. {
  584. gpGlobals->g.PlayerRoles.rgwLevel[w] = MAX_LEVELS;
  585. }
  586. while (dwExp >= gpGlobals->g.rgLevelUpExp[gpGlobals->g.PlayerRoles.rgwLevel[w]])
  587. {
  588. dwExp -= gpGlobals->g.rgLevelUpExp[gpGlobals->g.PlayerRoles.rgwLevel[w]];
  589. if (gpGlobals->g.PlayerRoles.rgwLevel[w] < MAX_LEVELS)
  590. {
  591. fLevelUp = TRUE;
  592. PAL_PlayerLevelUp(w, 1);
  593. gpGlobals->g.PlayerRoles.rgwHP[w] = gpGlobals->g.PlayerRoles.rgwMaxHP[w];
  594. gpGlobals->g.PlayerRoles.rgwMP[w] = gpGlobals->g.PlayerRoles.rgwMaxMP[w];
  595. }
  596. }
  597. gpGlobals->Exp.rgPrimaryExp[w].wExp = (WORD)dwExp;
  598. if (fLevelUp)
  599. {
  600. VIDEO_RestoreScreen();
  601. //
  602. // Player has gained a level. Show the message
  603. //
  604. PAL_CreateSingleLineBox(PAL_XY(offsetX+80, 0), propertyLength+10, FALSE);
  605. PAL_CreateBox(PAL_XY(offsetX+82, 32), 7, propertyLength+8, 1, FALSE);
  606. WCHAR buffer[256] = L"";
  607. PAL_swprintf(buffer, sizeof(buffer) / sizeof(WCHAR), L"%ls%ls%ls", PAL_GetWord(gpGlobals->g.PlayerRoles.rgwName[w]), PAL_GetWord(STATUS_LABEL_LEVEL), PAL_GetWord(BATTLEWIN_LEVELUP_LABEL));
  608. PAL_DrawText(buffer, PAL_XY(110, 10), 0, FALSE, FALSE, FALSE);
  609. for (j = 0; j < 8; j++)
  610. {
  611. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_ARROW),
  612. gpScreen, PAL_XY(-offsetX+180, 48 + 18 * j));
  613. }
  614. PAL_DrawText(PAL_GetWord(STATUS_LABEL_LEVEL), PAL_XY(offsetX+100, 44), BATTLEWIN_LEVELUP_LABEL_COLOR, TRUE, FALSE, FALSE);
  615. PAL_DrawText(PAL_GetWord(STATUS_LABEL_HP), PAL_XY(offsetX+100, 62), BATTLEWIN_LEVELUP_LABEL_COLOR, TRUE, FALSE, FALSE);
  616. PAL_DrawText(PAL_GetWord(STATUS_LABEL_MP), PAL_XY(offsetX+100, 80), BATTLEWIN_LEVELUP_LABEL_COLOR, TRUE, FALSE, FALSE);
  617. PAL_DrawText(PAL_GetWord(STATUS_LABEL_ATTACKPOWER), PAL_XY(offsetX+100, 98), BATTLEWIN_LEVELUP_LABEL_COLOR, TRUE, FALSE, FALSE);
  618. PAL_DrawText(PAL_GetWord(STATUS_LABEL_MAGICPOWER), PAL_XY(offsetX+100, 116), BATTLEWIN_LEVELUP_LABEL_COLOR, TRUE, FALSE, FALSE);
  619. PAL_DrawText(PAL_GetWord(STATUS_LABEL_RESISTANCE), PAL_XY(offsetX+100, 134), BATTLEWIN_LEVELUP_LABEL_COLOR, TRUE, FALSE, FALSE);
  620. PAL_DrawText(PAL_GetWord(STATUS_LABEL_DEXTERITY), PAL_XY(offsetX+100, 152), BATTLEWIN_LEVELUP_LABEL_COLOR, TRUE, FALSE, FALSE);
  621. PAL_DrawText(PAL_GetWord(STATUS_LABEL_FLEERATE), PAL_XY(offsetX+100, 170), BATTLEWIN_LEVELUP_LABEL_COLOR, TRUE, FALSE, FALSE);
  622. //
  623. // Draw the original stats and stats after level up
  624. //
  625. PAL_DrawNumber(OrigPlayerRoles.rgwLevel[w], 4, PAL_XY(-offsetX+133, 47),
  626. kNumColorYellow, kNumAlignRight);
  627. PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwLevel[w], 4, PAL_XY(-offsetX+195, 47),
  628. kNumColorYellow, kNumAlignRight);
  629. PAL_DrawNumber(OrigPlayerRoles.rgwHP[w], 4, PAL_XY(-offsetX+133, 64),
  630. kNumColorYellow, kNumAlignRight);
  631. PAL_DrawNumber(OrigPlayerRoles.rgwMaxHP[w], 4, PAL_XY(-offsetX+154, 68),
  632. kNumColorBlue, kNumAlignRight);
  633. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_SLASH), gpScreen,
  634. PAL_XY(-offsetX+156, 66));
  635. PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwHP[w], 4, PAL_XY(-offsetX+195, 64),
  636. kNumColorYellow, kNumAlignRight);
  637. PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwMaxHP[w], 4, PAL_XY(-offsetX+216, 68),
  638. kNumColorBlue, kNumAlignRight);
  639. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_SLASH), gpScreen,
  640. PAL_XY(-offsetX+218, 66));
  641. PAL_DrawNumber(OrigPlayerRoles.rgwMP[w], 4, PAL_XY(-offsetX+133, 82),
  642. kNumColorYellow, kNumAlignRight);
  643. PAL_DrawNumber(OrigPlayerRoles.rgwMaxMP[w], 4, PAL_XY(-offsetX+154, 86),
  644. kNumColorBlue, kNumAlignRight);
  645. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_SLASH), gpScreen,
  646. PAL_XY(-offsetX+156, 84));
  647. PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwMP[w], 4, PAL_XY(-offsetX+195, 82),
  648. kNumColorYellow, kNumAlignRight);
  649. PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwMaxMP[w], 4, PAL_XY(-offsetX+216, 86),
  650. kNumColorBlue, kNumAlignRight);
  651. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_SLASH), gpScreen,
  652. PAL_XY(-offsetX+218, 84));
  653. PAL_DrawNumber(OrigPlayerRoles.rgwAttackStrength[w] + PAL_GetPlayerAttackStrength(w) -
  654. gpGlobals->g.PlayerRoles.rgwAttackStrength[w],
  655. 4, PAL_XY(-offsetX+133, 101), kNumColorYellow, kNumAlignRight);
  656. PAL_DrawNumber(PAL_GetPlayerAttackStrength(w), 4, PAL_XY(-offsetX+195, 101),
  657. kNumColorYellow, kNumAlignRight);
  658. PAL_DrawNumber(OrigPlayerRoles.rgwMagicStrength[w] + PAL_GetPlayerMagicStrength(w) -
  659. gpGlobals->g.PlayerRoles.rgwMagicStrength[w],
  660. 4, PAL_XY(-offsetX+133, 119), kNumColorYellow, kNumAlignRight);
  661. PAL_DrawNumber(PAL_GetPlayerMagicStrength(w), 4, PAL_XY(-offsetX+195, 119),
  662. kNumColorYellow, kNumAlignRight);
  663. PAL_DrawNumber(OrigPlayerRoles.rgwDefense[w] + PAL_GetPlayerDefense(w) -
  664. gpGlobals->g.PlayerRoles.rgwDefense[w],
  665. 4, PAL_XY(-offsetX+133, 137), kNumColorYellow, kNumAlignRight);
  666. PAL_DrawNumber(PAL_GetPlayerDefense(w), 4, PAL_XY(-offsetX+195, 137),
  667. kNumColorYellow, kNumAlignRight);
  668. PAL_DrawNumber(OrigPlayerRoles.rgwDexterity[w] + PAL_GetPlayerDexterity(w) -
  669. gpGlobals->g.PlayerRoles.rgwDexterity[w],
  670. 4, PAL_XY(-offsetX+133, 155), kNumColorYellow, kNumAlignRight);
  671. PAL_DrawNumber(PAL_GetPlayerDexterity(w), 4, PAL_XY(-offsetX+195, 155),
  672. kNumColorYellow, kNumAlignRight);
  673. PAL_DrawNumber(OrigPlayerRoles.rgwFleeRate[w] + PAL_GetPlayerFleeRate(w) -
  674. gpGlobals->g.PlayerRoles.rgwFleeRate[w],
  675. 4, PAL_XY(-offsetX+133, 173), kNumColorYellow, kNumAlignRight);
  676. PAL_DrawNumber(PAL_GetPlayerFleeRate(w), 4, PAL_XY(-offsetX+195, 173),
  677. kNumColorYellow, kNumAlignRight);
  678. //
  679. // Update the screen and wait for key
  680. //
  681. VIDEO_UpdateScreen(&rect1);
  682. PAL_WaitForKey(3000);
  683. OrigPlayerRoles = gpGlobals->g.PlayerRoles;
  684. }
  685. //
  686. // Increasing of other hidden levels
  687. //
  688. iTotalCount = 0;
  689. iTotalCount += gpGlobals->Exp.rgAttackExp[w].wCount;
  690. iTotalCount += gpGlobals->Exp.rgDefenseExp[w].wCount;
  691. iTotalCount += gpGlobals->Exp.rgDexterityExp[w].wCount;
  692. iTotalCount += gpGlobals->Exp.rgFleeExp[w].wCount;
  693. iTotalCount += gpGlobals->Exp.rgHealthExp[w].wCount;
  694. iTotalCount += gpGlobals->Exp.rgMagicExp[w].wCount;
  695. iTotalCount += gpGlobals->Exp.rgMagicPowerExp[w].wCount;
  696. if (iTotalCount > 0)
  697. {
  698. #define CHECK_HIDDEN_EXP(expname, statname, label) \
  699. { \
  700. dwExp = g_Battle.iExpGained; \
  701. dwExp *= gpGlobals->Exp.expname[w].wCount; \
  702. dwExp /= iTotalCount; \
  703. dwExp *= 2; \
  704. \
  705. dwExp += gpGlobals->Exp.expname[w].wExp; \
  706. \
  707. if (gpGlobals->Exp.expname[w].wLevel > MAX_LEVELS) \
  708. { \
  709. gpGlobals->Exp.expname[w].wLevel = MAX_LEVELS; \
  710. } \
  711. \
  712. while (dwExp >= gpGlobals->g.rgLevelUpExp[gpGlobals->Exp.expname[w].wLevel]) \
  713. { \
  714. dwExp -= gpGlobals->g.rgLevelUpExp[gpGlobals->Exp.expname[w].wLevel]; \
  715. gpGlobals->g.PlayerRoles.statname[w] += RandomLong(1, 2); \
  716. if (gpGlobals->Exp.expname[w].wLevel < MAX_LEVELS) \
  717. { \
  718. gpGlobals->Exp.expname[w].wLevel++; \
  719. } \
  720. } \
  721. \
  722. gpGlobals->Exp.expname[w].wExp = (WORD)dwExp; \
  723. \
  724. if (gpGlobals->g.PlayerRoles.statname[w] != OrigPlayerRoles.statname[w]) \
  725. { \
  726. WCHAR buffer[256] = L""; \
  727. PAL_swprintf(buffer, sizeof(buffer) / sizeof(WCHAR), L"%ls%ls%ls", PAL_GetWord(gpGlobals->g.PlayerRoles.rgwName[w]), PAL_GetWord(label), PAL_GetWord(BATTLEWIN_LEVELUP_LABEL)); \
  728. PAL_CreateSingleLineBox(PAL_XY(offsetX+78, 60), maxNameWidth+maxPropertyWidth+PAL_TextWidth(PAL_GetWord(BATTLEWIN_LEVELUP_LABEL))/32+4, FALSE); \
  729. PAL_DrawText(buffer, PAL_XY(offsetX+90, 70), 0, FALSE, FALSE, FALSE); \
  730. PAL_DrawNumber(gpGlobals->g.PlayerRoles.statname[w] - OrigPlayerRoles.statname[w], 5, PAL_XY(183+(maxNameWidth+maxPropertyWidth-3)*8, 74), kNumColorYellow, kNumAlignRight); \
  731. VIDEO_UpdateScreen(&rect); \
  732. PAL_WaitForKey(3000); \
  733. } \
  734. }
  735. CHECK_HIDDEN_EXP(rgHealthExp, rgwMaxHP, STATUS_LABEL_HP);
  736. CHECK_HIDDEN_EXP(rgMagicExp, rgwMaxMP, STATUS_LABEL_MP);
  737. CHECK_HIDDEN_EXP(rgAttackExp, rgwAttackStrength, STATUS_LABEL_ATTACKPOWER);
  738. CHECK_HIDDEN_EXP(rgMagicPowerExp, rgwMagicStrength, STATUS_LABEL_MAGICPOWER);
  739. CHECK_HIDDEN_EXP(rgDefenseExp, rgwDefense, STATUS_LABEL_RESISTANCE);
  740. CHECK_HIDDEN_EXP(rgDexterityExp, rgwDexterity, STATUS_LABEL_DEXTERITY);
  741. CHECK_HIDDEN_EXP(rgFleeExp, rgwFleeRate, STATUS_LABEL_FLEERATE);
  742. #undef CHECK_HIDDEN_EXP
  743. }
  744. //
  745. // Learn all magics at the current level
  746. //
  747. j = 0;
  748. while (j < gpGlobals->g.nLevelUpMagic)
  749. {
  750. if (gpGlobals->g.lprgLevelUpMagic[j].m[w].wMagic == 0 ||
  751. gpGlobals->g.lprgLevelUpMagic[j].m[w].wLevel > gpGlobals->g.PlayerRoles.rgwLevel[w])
  752. {
  753. j++;
  754. continue;
  755. }
  756. if (PAL_AddMagic(w, gpGlobals->g.lprgLevelUpMagic[j].m[w].wMagic))
  757. {
  758. int ww;
  759. int w1 = (ww = PAL_WordWidth(gpGlobals->g.PlayerRoles.rgwName[w])) > 3 ? ww : 3;
  760. int w2 = (ww = PAL_WordWidth(BATTLEWIN_ADDMAGIC_LABEL)) > 2 ? ww : 2;
  761. int w3 = (ww = PAL_WordWidth(gpGlobals->g.lprgLevelUpMagic[j].m[w].wMagic)) > 5 ? ww : 5;
  762. ww = (w1 + w2 + w3 - 10) << 3;
  763. PAL_CreateSingleLineBox(PAL_XY(65 - ww, 105), w1 + w2 + w3, FALSE);
  764. PAL_DrawText(PAL_GetWord(gpGlobals->g.PlayerRoles.rgwName[w]), PAL_XY(75 - ww, 115), 0, FALSE, FALSE, FALSE);
  765. PAL_DrawText(PAL_GetWord(BATTLEWIN_ADDMAGIC_LABEL), PAL_XY(75 + 16 * w1 - ww, 115), 0, FALSE, FALSE, FALSE);
  766. PAL_DrawText(PAL_GetWord(gpGlobals->g.lprgLevelUpMagic[j].m[w].wMagic), PAL_XY(75 + 16 * (w1 + w2) - ww, 115), 0x1B, FALSE, FALSE, FALSE);
  767. VIDEO_UpdateScreen(&rect);
  768. PAL_WaitForKey(3000);
  769. }
  770. j++;
  771. }
  772. }
  773. //
  774. // Run the post-battle scripts
  775. //
  776. for (i = 0; i <= g_Battle.wMaxEnemyIndex; i++)
  777. {
  778. PAL_RunTriggerScript(g_Battle.rgEnemy[i].wScriptOnBattleEnd, i);
  779. }
  780. //
  781. // Recover automatically after each battle
  782. //
  783. for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
  784. {
  785. w = gpGlobals->rgParty[i].wPlayerRole;
  786. #if 1//def PAL_CLASSIC
  787. gpGlobals->g.PlayerRoles.rgwHP[w] +=
  788. (gpGlobals->g.PlayerRoles.rgwMaxHP[w] - gpGlobals->g.PlayerRoles.rgwHP[w]) / 2;
  789. gpGlobals->g.PlayerRoles.rgwMP[w] +=
  790. (gpGlobals->g.PlayerRoles.rgwMaxMP[w] - gpGlobals->g.PlayerRoles.rgwMP[w]) / 2;
  791. #else
  792. if (gpGlobals->g.PlayerRoles.rgwHP[w] == 0)
  793. {
  794. gpGlobals->g.PlayerRoles.rgwHP[w] = 1;
  795. }
  796. else if (g_Battle.iExpGained > 0)
  797. {
  798. FLOAT f =
  799. (gpGlobals->g.rgLevelUpExp[gpGlobals->g.PlayerRoles.rgwLevel[w]] / 5.0f) / g_Battle.iExpGained;
  800. if (f < 2)
  801. {
  802. f = 2;
  803. }
  804. gpGlobals->g.PlayerRoles.rgwHP[w] +=
  805. (gpGlobals->g.PlayerRoles.rgwMaxHP[w] - gpGlobals->g.PlayerRoles.rgwHP[w]) / f;
  806. gpGlobals->g.PlayerRoles.rgwMP[w] +=
  807. (gpGlobals->g.PlayerRoles.rgwMaxMP[w] - gpGlobals->g.PlayerRoles.rgwMP[w]) / f / 1.2;
  808. }
  809. #endif
  810. }
  811. }
  812. VOID
  813. PAL_BattleEnemyEscape(
  814. VOID
  815. )
  816. /*++
  817. Purpose:
  818. Enemy flee the battle.
  819. Parameters:
  820. None.
  821. Return value:
  822. None.
  823. --*/
  824. {
  825. int j, x, y, w;
  826. BOOL f = TRUE;
  827. AUDIO_PlaySound(45);
  828. //
  829. // Show the animation
  830. //
  831. while (f)
  832. {
  833. f = FALSE;
  834. for (j = 0; j <= g_Battle.wMaxEnemyIndex; j++)
  835. {
  836. if (g_Battle.rgEnemy[j].wObjectID == 0)
  837. {
  838. continue;
  839. }
  840. x = PAL_X(g_Battle.rgEnemy[j].pos) - 5;
  841. y = PAL_Y(g_Battle.rgEnemy[j].pos);
  842. g_Battle.rgEnemy[j].pos = PAL_XY(x, y);
  843. w = PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgEnemy[j].lpSprite, 0));
  844. if (x + w > 0)
  845. {
  846. f = TRUE;
  847. }
  848. }
  849. PAL_BattleMakeScene();
  850. SDL_BlitSurface(g_Battle.lpSceneBuf, NULL, gpScreen, NULL);
  851. VIDEO_UpdateScreen(NULL);
  852. UTIL_Delay(10);
  853. }
  854. UTIL_Delay(500);
  855. g_Battle.BattleResult = kBattleResultTerminated;
  856. }
  857. VOID
  858. PAL_BattlePlayerEscape(
  859. VOID
  860. )
  861. /*++
  862. Purpose:
  863. Player flee the battle.
  864. Parameters:
  865. None.
  866. Return value:
  867. None.
  868. --*/
  869. {
  870. int i, j;
  871. WORD wPlayerRole;
  872. AUDIO_PlaySound(45);
  873. PAL_BattleUpdateFighters();
  874. for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
  875. {
  876. wPlayerRole = gpGlobals->rgParty[i].wPlayerRole;
  877. if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] > 0)
  878. {
  879. g_Battle.rgPlayer[i].wCurrentFrame = 0;
  880. }
  881. }
  882. for (i = 0; i < 16; i++)
  883. {
  884. for (j = 0; j <= gpGlobals->wMaxPartyMemberIndex; j++)
  885. {
  886. wPlayerRole = gpGlobals->rgParty[j].wPlayerRole;
  887. if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] > 0)
  888. {
  889. //
  890. // TODO: This is still not the same as the original game
  891. //
  892. switch (j)
  893. {
  894. case 0:
  895. if (gpGlobals->wMaxPartyMemberIndex > 0)
  896. {
  897. g_Battle.rgPlayer[j].pos =
  898. PAL_XY(PAL_X(g_Battle.rgPlayer[j].pos) + 4,
  899. PAL_Y(g_Battle.rgPlayer[j].pos) + 6);
  900. break;
  901. }
  902. case 1:
  903. g_Battle.rgPlayer[j].pos =
  904. PAL_XY(PAL_X(g_Battle.rgPlayer[j].pos) + 4,
  905. PAL_Y(g_Battle.rgPlayer[j].pos) + 4);
  906. break;
  907. case 2:
  908. g_Battle.rgPlayer[j].pos =
  909. PAL_XY(PAL_X(g_Battle.rgPlayer[j].pos) + 6,
  910. PAL_Y(g_Battle.rgPlayer[j].pos) + 3);
  911. break;
  912. default:
  913. assert(FALSE); // Not possible
  914. break;
  915. }
  916. }
  917. }
  918. PAL_BattleDelay(1, 0, FALSE);
  919. }
  920. //
  921. // Remove all players from the screen
  922. //
  923. for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
  924. {
  925. g_Battle.rgPlayer[i].pos = PAL_XY(9999, 9999);
  926. }
  927. PAL_BattleDelay(1, 0, FALSE);
  928. g_Battle.BattleResult = kBattleResultFleed;
  929. }
  930. BATTLERESULT
  931. PAL_StartBattle(
  932. WORD wEnemyTeam,
  933. BOOL fIsBoss
  934. )
  935. /*++
  936. Purpose:
  937. Start a battle.
  938. Parameters:
  939. [IN] wEnemyTeam - the number of the enemy team.
  940. [IN] fIsBoss - TRUE for boss fight (not allowed to flee).
  941. Return value:
  942. The result of the battle.
  943. --*/
  944. {
  945. int i;
  946. WORD w, wPrevWaveLevel;
  947. SHORT sPrevWaveProgression;
  948. //
  949. // Set the screen waving effects
  950. //
  951. wPrevWaveLevel = gpGlobals->wScreenWave;
  952. sPrevWaveProgression = gpGlobals->sWaveProgression;
  953. gpGlobals->sWaveProgression = 0;
  954. gpGlobals->wScreenWave = gpGlobals->g.lprgBattleField[gpGlobals->wNumBattleField].wScreenWave;
  955. //
  956. // Make sure everyone in the party is alive, also clear all hidden
  957. // EXP count records
  958. //
  959. for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
  960. {
  961. w = gpGlobals->rgParty[i].wPlayerRole;
  962. if (gpGlobals->g.PlayerRoles.rgwHP[w] == 0)
  963. {
  964. gpGlobals->g.PlayerRoles.rgwHP[w] = 1;
  965. gpGlobals->rgPlayerStatus[w][kStatusPuppet] = 0;
  966. }
  967. gpGlobals->Exp.rgHealthExp[w].wCount = 0;
  968. gpGlobals->Exp.rgMagicExp[w].wCount = 0;
  969. gpGlobals->Exp.rgAttackExp[w].wCount = 0;
  970. gpGlobals->Exp.rgMagicPowerExp[w].wCount = 0;
  971. gpGlobals->Exp.rgDefenseExp[w].wCount = 0;
  972. gpGlobals->Exp.rgDexterityExp[w].wCount = 0;
  973. gpGlobals->Exp.rgFleeExp[w].wCount = 0;
  974. }
  975. //
  976. // Clear all item-using records
  977. //
  978. for (i = 0; i < MAX_INVENTORY; i++)
  979. {
  980. gpGlobals->rgInventory[i].nAmountInUse = 0;
  981. }
  982. //
  983. // Store all enemies
  984. //
  985. for (i = 0; i < MAX_ENEMIES_IN_TEAM; i++)
  986. {
  987. memset(&(g_Battle.rgEnemy[i]), 0, sizeof(BATTLEENEMY));
  988. w = gpGlobals->g.lprgEnemyTeam[wEnemyTeam].rgwEnemy[i];
  989. if (w == 0xFFFF)
  990. {
  991. break;
  992. }
  993. if (w != 0)
  994. {
  995. g_Battle.rgEnemy[i].e = gpGlobals->g.lprgEnemy[gpGlobals->g.rgObject[w].enemy.wEnemyID];
  996. g_Battle.rgEnemy[i].wObjectID = w;
  997. g_Battle.rgEnemy[i].state = kFighterWait;
  998. g_Battle.rgEnemy[i].wScriptOnTurnStart = gpGlobals->g.rgObject[w].enemy.wScriptOnTurnStart;
  999. g_Battle.rgEnemy[i].wScriptOnBattleEnd = gpGlobals->g.rgObject[w].enemy.wScriptOnBattleEnd;
  1000. g_Battle.rgEnemy[i].wScriptOnReady = gpGlobals->g.rgObject[w].enemy.wScriptOnReady;
  1001. g_Battle.rgEnemy[i].iColorShift = 0;
  1002. if (!PAL_IsClassicBattle())
  1003. {
  1004. g_Battle.rgEnemy[i].flTimeMeter = 50;
  1005. //
  1006. // HACK: Otherwise the black thief lady will be too hard to beat
  1007. //
  1008. if (g_Battle.rgEnemy[i].e.wDexterity == 164)
  1009. {
  1010. g_Battle.rgEnemy[i].e.wDexterity /= ((gpGlobals->wMaxPartyMemberIndex == 0) ? 6 : 3);
  1011. }
  1012. //
  1013. // HACK: Heal up automatically for final boss
  1014. //
  1015. if (g_Battle.rgEnemy[i].e.wHealth == 32760)
  1016. {
  1017. for (w = 0; w < MAX_PLAYER_ROLES; w++)
  1018. {
  1019. gpGlobals->g.PlayerRoles.rgwHP[w] = gpGlobals->g.PlayerRoles.rgwMaxHP[w];
  1020. gpGlobals->g.PlayerRoles.rgwMP[w] = gpGlobals->g.PlayerRoles.rgwMaxMP[w];
  1021. }
  1022. }
  1023. //
  1024. // Yet another HACKs
  1025. //
  1026. if ((SHORT)g_Battle.rgEnemy[i].e.wDexterity == -32)
  1027. {
  1028. g_Battle.rgEnemy[i].e.wDexterity = 0; // for Grandma Knife
  1029. }
  1030. else if (g_Battle.rgEnemy[i].e.wDexterity == 20)
  1031. {
  1032. //
  1033. // for Fox Demon
  1034. //
  1035. if (gpGlobals->g.PlayerRoles.rgwLevel[0] < 15)
  1036. {
  1037. g_Battle.rgEnemy[i].e.wDexterity = 8;
  1038. }
  1039. else if (gpGlobals->g.PlayerRoles.rgwLevel[4] > 28 ||
  1040. gpGlobals->Exp.rgPrimaryExp[4].wExp > 0)
  1041. {
  1042. g_Battle.rgEnemy[i].e.wDexterity = 60;
  1043. }
  1044. }
  1045. else if (g_Battle.rgEnemy[i].e.wExp == 250 &&
  1046. g_Battle.rgEnemy[i].e.wCash == 1100)
  1047. {
  1048. g_Battle.rgEnemy[i].e.wDexterity += 12; // for Snake Demon
  1049. }
  1050. else if ((SHORT)g_Battle.rgEnemy[i].e.wDexterity == -60)
  1051. {
  1052. g_Battle.rgEnemy[i].e.wDexterity = 15; // for Spider
  1053. }
  1054. else if ((SHORT)g_Battle.rgEnemy[i].e.wDexterity == -30)
  1055. {
  1056. g_Battle.rgEnemy[i].e.wDexterity = (WORD)-10; // for Stone Head
  1057. }
  1058. else if ((SHORT)g_Battle.rgEnemy[i].e.wDexterity == -16)
  1059. {
  1060. g_Battle.rgEnemy[i].e.wDexterity = 0; // for Zombie
  1061. }
  1062. else if ((SHORT)g_Battle.rgEnemy[i].e.wDexterity == -20)
  1063. {
  1064. g_Battle.rgEnemy[i].e.wDexterity = -8; // for Flower Demon
  1065. }
  1066. else if (g_Battle.rgEnemy[i].e.wLevel < 20 &&
  1067. gpGlobals->wNumScene >= 0xD8 && gpGlobals->wNumScene <= 0xE2)
  1068. {
  1069. //
  1070. // for low-level monsters in the Cave of Trial
  1071. //
  1072. g_Battle.rgEnemy[i].e.wLevel += 15;
  1073. g_Battle.rgEnemy[i].e.wDexterity += 25;
  1074. }
  1075. else if (gpGlobals->wNumScene == 0x90)
  1076. {
  1077. g_Battle.rgEnemy[i].e.wDexterity += 25; // for Tower Dragons
  1078. }
  1079. else if (g_Battle.rgEnemy[i].e.wLevel == 2 &&
  1080. g_Battle.rgEnemy[i].e.wCash == 48)
  1081. {
  1082. g_Battle.rgEnemy[i].e.wDexterity += 8; // for Miao Fists
  1083. }
  1084. else if (g_Battle.rgEnemy[i].e.wLevel == 4 &&
  1085. g_Battle.rgEnemy[i].e.wCash == 240)
  1086. {
  1087. g_Battle.rgEnemy[i].e.wDexterity += 18; // for Fat Miao
  1088. }
  1089. else if (g_Battle.rgEnemy[i].e.wLevel == 16 &&
  1090. g_Battle.rgEnemy[i].e.wMagicRate == 4 &&
  1091. g_Battle.rgEnemy[i].e.wAttackEquivItemRate == 4)
  1092. {
  1093. g_Battle.rgEnemy[i].e.wDexterity += 50; // for Black Spider
  1094. }
  1095. }
  1096. }
  1097. }
  1098. g_Battle.wMaxEnemyIndex = i - 1;
  1099. //
  1100. // Store all players
  1101. //
  1102. for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
  1103. {
  1104. g_Battle.rgPlayer[i].flTimeMeter = 15.0f;
  1105. if (!PAL_IsClassicBattle())
  1106. {
  1107. g_Battle.rgPlayer[i].flTimeSpeedModifier = 2.0f;
  1108. g_Battle.rgPlayer[i].sTurnOrder = -1;
  1109. }
  1110. g_Battle.rgPlayer[i].wHidingTime = 0;
  1111. g_Battle.rgPlayer[i].state = kFighterWait;
  1112. g_Battle.rgPlayer[i].action.sTarget = -1;
  1113. g_Battle.rgPlayer[i].fDefending = FALSE;
  1114. g_Battle.rgPlayer[i].wCurrentFrame = 0;
  1115. g_Battle.rgPlayer[i].iColorShift = FALSE;
  1116. }
  1117. //
  1118. // Load sprites and background
  1119. //
  1120. PAL_LoadBattleSprites();
  1121. PAL_LoadBattleBackground();
  1122. //
  1123. // Create the surface for scene buffer
  1124. //
  1125. g_Battle.lpSceneBuf =
  1126. SDL_CreateRGBSurface(gpScreen->flags & ~SDL_HWSURFACE, 320, 200, 8,
  1127. gpScreen->format->Rmask, gpScreen->format->Gmask,
  1128. gpScreen->format->Bmask, gpScreen->format->Amask);
  1129. if (g_Battle.lpSceneBuf == NULL)
  1130. {
  1131. TerminateOnError("PAL_StartBattle(): creating surface for scene buffer failed!");
  1132. }
  1133. #if SDL_VERSION_ATLEAST(2, 0, 0)
  1134. SDL_SetSurfacePalette(g_Battle.lpSceneBuf, gpScreen->format->palette);
  1135. #else
  1136. SDL_SetPalette(g_Battle.lpSceneBuf, SDL_PHYSPAL | SDL_LOGPAL, VIDEO_GetPalette(), 0, 256);
  1137. #endif
  1138. PAL_UpdateEquipments();
  1139. g_Battle.iExpGained = 0;
  1140. g_Battle.iCashGained = 0;
  1141. g_Battle.fIsBoss = fIsBoss;
  1142. g_Battle.fEnemyCleared = FALSE;
  1143. g_Battle.fEnemyMoving = FALSE;
  1144. g_Battle.iHidingTime = 0;
  1145. g_Battle.wMovingPlayerIndex = 0;
  1146. g_Battle.UI.szMsg[0] = '\0';
  1147. g_Battle.UI.szNextMsg[0] = '\0';
  1148. g_Battle.UI.dwMsgShowTime = 0;
  1149. g_Battle.UI.state = kBattleUIWait;
  1150. g_Battle.UI.fAutoAttack = FALSE;
  1151. g_Battle.UI.wSelectedIndex = 0;
  1152. g_Battle.UI.wPrevEnemyTarget = 0;
  1153. memset(g_Battle.UI.rgShowNum, 0, sizeof(g_Battle.UI.rgShowNum));
  1154. g_Battle.lpSummonSprite = NULL;
  1155. g_Battle.sBackgroundColorShift = 0;
  1156. gpGlobals->fInBattle = TRUE;
  1157. g_Battle.BattleResult = kBattleResultPreBattle;
  1158. PAL_BattleUpdateFighters();
  1159. //
  1160. // Load the battle effect sprite.
  1161. //
  1162. i = PAL_MKFGetChunkSize(10, gpGlobals->f.fpDATA);
  1163. g_Battle.lpEffectSprite = UTIL_malloc(i);
  1164. PAL_MKFReadChunk(g_Battle.lpEffectSprite, i, 10, gpGlobals->f.fpDATA);
  1165. if (PAL_IsClassicBattle())
  1166. {
  1167. g_Battle.Phase = kBattlePhaseSelectAction;
  1168. g_Battle.fRepeat = FALSE;
  1169. g_Battle.fForce = FALSE;
  1170. g_Battle.fFlee = FALSE;
  1171. }
  1172. #ifdef PAL_ALLOW_KEYREPEAT
  1173. SDL_EnableKeyRepeat(120, 75);
  1174. #endif
  1175. //
  1176. // Run the main battle routine.
  1177. //
  1178. i = PAL_BattleMain();
  1179. #ifdef PAL_ALLOW_KEYREPEAT
  1180. SDL_EnableKeyRepeat(0, 0);
  1181. PAL_ClearKeyState();
  1182. g_InputState.prevdir = kDirUnknown;
  1183. #endif
  1184. if (i == kBattleResultWon)
  1185. {
  1186. //
  1187. // Player won the battle. Add the Experience points.
  1188. //
  1189. PAL_BattleWon();
  1190. }
  1191. //
  1192. // Clear all item-using records
  1193. //
  1194. for (w = 0; w < MAX_INVENTORY; w++)
  1195. {
  1196. gpGlobals->rgInventory[w].nAmountInUse = 0;
  1197. }
  1198. //
  1199. // Clear all player status, poisons and temporary effects
  1200. //
  1201. PAL_ClearAllPlayerStatus();
  1202. for (w = 0; w < MAX_PLAYER_ROLES; w++)
  1203. {
  1204. PAL_CurePoisonByLevel(w, 3);
  1205. PAL_RemoveEquipmentEffect(w, kBodyPartExtra);
  1206. }
  1207. //
  1208. // Free all the battle sprites
  1209. //
  1210. PAL_FreeBattleSprites();
  1211. free(g_Battle.lpEffectSprite);
  1212. //
  1213. // Free the surfaces for the background picture and scene buffer
  1214. //
  1215. SDL_FreeSurface(g_Battle.lpBackground);
  1216. SDL_FreeSurface(g_Battle.lpSceneBuf);
  1217. g_Battle.lpBackground = NULL;
  1218. g_Battle.lpSceneBuf = NULL;
  1219. gpGlobals->fInBattle = FALSE;
  1220. AUDIO_PlayMusic(gpGlobals->wNumMusic, TRUE, 1);
  1221. //
  1222. // Restore the screen waving effects
  1223. //
  1224. gpGlobals->sWaveProgression = sPrevWaveProgression;
  1225. gpGlobals->wScreenWave = wPrevWaveLevel;
  1226. return i;
  1227. }