battle.c 41 KB

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