battle.c 41 KB

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