battle.c 42 KB

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