battle.c 41 KB

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