uibattle.c 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777
  1. //
  2. // Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
  3. // All rights reserved.
  4. //
  5. // This file is part of SDLPAL.
  6. //
  7. // SDLPAL is free software: you can redistribute it and/or modify
  8. // it under the terms of the GNU General Public License as published by
  9. // the Free Software Foundation, either version 3 of the License, or
  10. // (at your option) any later version.
  11. //
  12. // This program is distributed in the hope that it will be useful,
  13. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. // GNU General Public License for more details.
  16. //
  17. // You should have received a copy of the GNU General Public License
  18. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. //
  20. #include "main.h"
  21. extern WORD g_rgPlayerPos[3][3][2];
  22. static int g_iCurMiscMenuItem = 0;
  23. static int g_iCurSubMenuItem = 0;
  24. VOID
  25. PAL_PlayerInfoBox(
  26. PAL_POS pos,
  27. WORD wPlayerRole,
  28. INT iTimeMeter,
  29. BYTE bTimeMeterColor,
  30. BOOL fUpdate
  31. )
  32. /*++
  33. Purpose:
  34. Show the player info box.
  35. Parameters:
  36. [IN] pos - the top-left corner position of the box.
  37. [IN] wPlayerRole - the player role ID to be shown.
  38. [IN] iTimeMeter - the value of time meter. 0 = empty, 100 = full.
  39. [IN] bTimeMeterColor - the color of time meter.
  40. [IN] fUpdate - whether to update the screen area or not.
  41. Return value:
  42. None.
  43. --*/
  44. {
  45. SDL_Rect rect;
  46. BYTE bPoisonColor;
  47. int i, iPartyIndex;
  48. WORD wMaxLevel, w;
  49. const BYTE rgStatusPos[kStatusAll][2] =
  50. {
  51. {35, 19}, // confused
  52. {0, 0}, // slow
  53. {54, 1}, // sleep
  54. {55, 20}, // silence
  55. {0, 0}, // puppet
  56. {0, 0}, // bravery
  57. {0, 0}, // protect
  58. {0, 0}, // haste
  59. {0, 0}, // dualattack
  60. };
  61. const WORD rgwStatusWord[kStatusAll] =
  62. {
  63. 0x1D, // confused
  64. 0x00, // slow
  65. 0x1C, // sleep
  66. 0x1A, // silence
  67. 0x00, // puppet
  68. 0x00, // bravery
  69. 0x00, // protect
  70. 0x00, // haste
  71. 0x00, // dualattack
  72. };
  73. const BYTE rgbStatusColor[kStatusAll] =
  74. {
  75. 0x5F, // confused
  76. 0x00, // slow
  77. 0x0E, // sleep
  78. 0x3C, // silence
  79. 0x00, // puppet
  80. 0x00, // bravery
  81. 0x00, // protect
  82. 0x00, // haste
  83. 0x00, // dualattack
  84. };
  85. //
  86. // Draw the box
  87. //
  88. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_PLAYERINFOBOX),
  89. gpScreen, pos);
  90. //
  91. // Draw the player face
  92. //
  93. wMaxLevel = 0;
  94. bPoisonColor = 0xFF;
  95. for (iPartyIndex = 0; iPartyIndex <= gpGlobals->wMaxPartyMemberIndex; iPartyIndex++)
  96. {
  97. if (gpGlobals->rgParty[iPartyIndex].wPlayerRole == wPlayerRole)
  98. {
  99. break;
  100. }
  101. }
  102. if (iPartyIndex <= gpGlobals->wMaxPartyMemberIndex)
  103. {
  104. for (i = 0; i < MAX_POISONS; i++)
  105. {
  106. w = gpGlobals->rgPoisonStatus[i][iPartyIndex].wPoisonID;
  107. if (w != 0 &&
  108. gpGlobals->g.rgObject[w].poison.wPoisonLevel <= 3)
  109. {
  110. if (gpGlobals->g.rgObject[w].poison.wPoisonLevel >= wMaxLevel)
  111. {
  112. wMaxLevel = gpGlobals->g.rgObject[w].poison.wPoisonLevel;
  113. bPoisonColor = (BYTE)(gpGlobals->g.rgObject[w].poison.wColor);
  114. }
  115. }
  116. }
  117. }
  118. if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] == 0)
  119. {
  120. //
  121. // Always use the black/white color for dead players
  122. // and do not use the time meter
  123. //
  124. bPoisonColor = 0;
  125. iTimeMeter = 0;
  126. }
  127. if (bPoisonColor == 0xFF)
  128. {
  129. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_PLAYERFACE_FIRST + wPlayerRole),
  130. gpScreen, PAL_XY(PAL_X(pos) - 2, PAL_Y(pos) - 4));
  131. }
  132. else
  133. {
  134. PAL_RLEBlitMonoColor(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_PLAYERFACE_FIRST + wPlayerRole),
  135. gpScreen, PAL_XY(PAL_X(pos) - 2, PAL_Y(pos) - 4), bPoisonColor, 0);
  136. }
  137. #ifndef PAL_CLASSIC
  138. //
  139. // Draw a border for the Time Meter
  140. //
  141. rect.x = PAL_X(pos) + 31;
  142. rect.y = PAL_Y(pos) + 4;
  143. rect.w = 1;
  144. rect.h = 6;
  145. SDL_FillRect(gpScreen, &rect, 0xBD);
  146. rect.x += 39;
  147. SDL_FillRect(gpScreen, &rect, 0xBD);
  148. rect.x = PAL_X(pos) + 32;
  149. rect.y = PAL_Y(pos) + 3;
  150. rect.w = 38;
  151. rect.h = 1;
  152. SDL_FillRect(gpScreen, &rect, 0xBD);
  153. rect.y += 7;
  154. SDL_FillRect(gpScreen, &rect, 0xBD);
  155. //
  156. // Draw the Time meter bar
  157. //
  158. if (iTimeMeter >= 100)
  159. {
  160. rect.x = PAL_X(pos) + 33;
  161. rect.y = PAL_Y(pos) + 6;
  162. rect.w = 36;
  163. rect.h = 2;
  164. SDL_FillRect(gpScreen, &rect, 0x2C);
  165. }
  166. else if (iTimeMeter > 0)
  167. {
  168. rect.x = PAL_X(pos) + 33;
  169. rect.y = PAL_Y(pos) + 5;
  170. rect.w = iTimeMeter * 36 / 100;
  171. rect.h = 4;
  172. SDL_FillRect(gpScreen, &rect, bTimeMeterColor);
  173. }
  174. #endif
  175. //
  176. // Draw the HP and MP value
  177. //
  178. #ifdef PAL_CLASSIC
  179. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_SLASH), gpScreen,
  180. PAL_XY(PAL_X(pos) + 49, PAL_Y(pos) + 6));
  181. PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwMaxHP[wPlayerRole], 4,
  182. PAL_XY(PAL_X(pos) + 47, PAL_Y(pos) + 8), kNumColorYellow, kNumAlignRight);
  183. PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole], 4,
  184. PAL_XY(PAL_X(pos) + 26, PAL_Y(pos) + 5), kNumColorYellow, kNumAlignRight);
  185. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_SLASH), gpScreen,
  186. PAL_XY(PAL_X(pos) + 49, PAL_Y(pos) + 22));
  187. PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwMaxMP[wPlayerRole], 4,
  188. PAL_XY(PAL_X(pos) + 47, PAL_Y(pos) + 24), kNumColorCyan, kNumAlignRight);
  189. PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwMP[wPlayerRole], 4,
  190. PAL_XY(PAL_X(pos) + 26, PAL_Y(pos) + 21), kNumColorCyan, kNumAlignRight);
  191. #else
  192. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_SLASH), gpScreen,
  193. PAL_XY(PAL_X(pos) + 49, PAL_Y(pos) + 14));
  194. PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwMaxHP[wPlayerRole], 4,
  195. PAL_XY(PAL_X(pos) + 47, PAL_Y(pos) + 16), kNumColorYellow, kNumAlignRight);
  196. PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole], 4,
  197. PAL_XY(PAL_X(pos) + 26, PAL_Y(pos) + 13), kNumColorYellow, kNumAlignRight);
  198. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_SLASH), gpScreen,
  199. PAL_XY(PAL_X(pos) + 49, PAL_Y(pos) + 24));
  200. PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwMaxMP[wPlayerRole], 4,
  201. PAL_XY(PAL_X(pos) + 47, PAL_Y(pos) + 26), kNumColorCyan, kNumAlignRight);
  202. PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwMP[wPlayerRole], 4,
  203. PAL_XY(PAL_X(pos) + 26, PAL_Y(pos) + 23), kNumColorCyan, kNumAlignRight);
  204. #endif
  205. //
  206. // Draw Statuses
  207. //
  208. if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] > 0)
  209. {
  210. for (i = 0; i < kStatusAll; i++)
  211. {
  212. if (gpGlobals->rgPlayerStatus[wPlayerRole][i] > 0 &&
  213. rgwStatusWord[i] != 0)
  214. {
  215. PAL_DrawText(PAL_GetWord(rgwStatusWord[i]),
  216. PAL_XY(PAL_X(pos) + rgStatusPos[i][0], PAL_Y(pos) + rgStatusPos[i][1]),
  217. rgbStatusColor[i], TRUE, FALSE);
  218. }
  219. }
  220. }
  221. //
  222. // Update the screen area if needed
  223. //
  224. if (fUpdate)
  225. {
  226. rect.x = PAL_X(pos) - 2;
  227. rect.y = PAL_Y(pos) - 4;
  228. rect.w = 77;
  229. rect.h = 39;
  230. VIDEO_UpdateScreen(&rect);
  231. }
  232. }
  233. static BOOL
  234. PAL_BattleUIIsActionValid(
  235. BATTLEUIACTION ActionType
  236. )
  237. /*++
  238. Purpose:
  239. Check if the specified action is valid.
  240. Parameters:
  241. [IN] ActionType - the type of the action.
  242. Return value:
  243. TRUE if the action is valid, FALSE if not.
  244. --*/
  245. {
  246. WORD wPlayerRole, w;
  247. int i;
  248. wPlayerRole = gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole;
  249. switch (ActionType)
  250. {
  251. case kBattleUIActionAttack:
  252. case kBattleUIActionMisc:
  253. break;
  254. case kBattleUIActionMagic:
  255. if (gpGlobals->rgPlayerStatus[wPlayerRole][kStatusSilence] != 0)
  256. {
  257. return FALSE;
  258. }
  259. break;
  260. case kBattleUIActionCoopMagic:
  261. if (gpGlobals->wMaxPartyMemberIndex == 0)
  262. {
  263. return FALSE;
  264. }
  265. for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
  266. {
  267. w = gpGlobals->rgParty[i].wPlayerRole;
  268. #ifndef PAL_CLASSIC
  269. if (gpGlobals->g.PlayerRoles.rgwHP[w] < gpGlobals->g.PlayerRoles.rgwMaxHP[w] / 5 ||
  270. gpGlobals->rgPlayerStatus[w][kStatusSleep] != 0 ||
  271. gpGlobals->rgPlayerStatus[w][kStatusConfused] != 0 ||
  272. gpGlobals->rgPlayerStatus[w][kStatusSilence] != 0 ||
  273. g_Battle.rgPlayer[i].flTimeMeter < 100 ||
  274. g_Battle.rgPlayer[i].state == kFighterAct)
  275. #else
  276. if (gpGlobals->g.PlayerRoles.rgwHP[w] < gpGlobals->g.PlayerRoles.rgwMaxHP[w] / 5 ||
  277. gpGlobals->rgPlayerStatus[w][kStatusSleep] != 0 ||
  278. gpGlobals->rgPlayerStatus[w][kStatusConfused] != 0 ||
  279. gpGlobals->rgPlayerStatus[w][kStatusSilence] != 0)
  280. #endif
  281. {
  282. return FALSE;
  283. }
  284. }
  285. break;
  286. }
  287. return TRUE;
  288. }
  289. static VOID
  290. PAL_BattleUIDrawMiscMenu(
  291. WORD wCurrentItem,
  292. BOOL fConfirmed
  293. )
  294. /*++
  295. Purpose:
  296. Draw the misc menu.
  297. Parameters:
  298. [IN] wCurrentItem - the current selected menu item.
  299. [IN] fConfirmed - TRUE if confirmed, FALSE if not.
  300. Return value:
  301. None.
  302. --*/
  303. {
  304. int i;
  305. BYTE bColor;
  306. #ifdef PAL_CLASSIC
  307. MENUITEM rgMenuItem[] = {
  308. // value label enabled position
  309. { 0, BATTLEUI_LABEL_AUTO, TRUE, PAL_XY(16, 32) },
  310. { 1, BATTLEUI_LABEL_INVENTORY, TRUE, PAL_XY(16, 50) },
  311. { 2, BATTLEUI_LABEL_DEFEND, TRUE, PAL_XY(16, 68) },
  312. { 3, BATTLEUI_LABEL_FLEE, TRUE, PAL_XY(16, 86) },
  313. { 4, BATTLEUI_LABEL_STATUS, TRUE, PAL_XY(16, 104) }
  314. };
  315. #else
  316. MENUITEM rgMenuItem[] = {
  317. // value label enabled position
  318. { 0, BATTLEUI_LABEL_ITEM, TRUE, PAL_XY(16, 32) },
  319. { 1, BATTLEUI_LABEL_DEFEND, TRUE, PAL_XY(16, 50) },
  320. { 2, BATTLEUI_LABEL_AUTO, TRUE, PAL_XY(16, 68) },
  321. { 3, BATTLEUI_LABEL_FLEE, TRUE, PAL_XY(16, 86) },
  322. { 4, BATTLEUI_LABEL_STATUS, TRUE, PAL_XY(16, 104) }
  323. };
  324. #endif
  325. //
  326. // Draw the box
  327. //
  328. PAL_CreateBox(PAL_XY(2, 20), 4, 1, 0, FALSE);
  329. //
  330. // Draw the menu items
  331. //
  332. for (i = 0; i < 5; i++)
  333. {
  334. bColor = MENUITEM_COLOR;
  335. if (i == wCurrentItem)
  336. {
  337. if (fConfirmed)
  338. {
  339. bColor = MENUITEM_COLOR_CONFIRMED;
  340. }
  341. else
  342. {
  343. bColor = MENUITEM_COLOR_SELECTED;
  344. }
  345. }
  346. PAL_DrawText(PAL_GetWord(rgMenuItem[i].wNumWord), rgMenuItem[i].pos, bColor,
  347. TRUE, FALSE);
  348. }
  349. }
  350. static WORD
  351. PAL_BattleUIMiscMenuUpdate(
  352. VOID
  353. )
  354. /*++
  355. Purpose:
  356. Update the misc menu.
  357. Parameters:
  358. None.
  359. Return value:
  360. The selected item number. 0 if cancelled, 0xFFFF if not confirmed.
  361. --*/
  362. {
  363. //
  364. // Draw the menu
  365. //
  366. PAL_BattleUIDrawMiscMenu(g_iCurMiscMenuItem, FALSE);
  367. //
  368. // Process inputs
  369. //
  370. if (g_InputState.dwKeyPress & (kKeyUp | kKeyLeft))
  371. {
  372. g_iCurMiscMenuItem--;
  373. if (g_iCurMiscMenuItem < 0)
  374. {
  375. g_iCurMiscMenuItem = 4;
  376. }
  377. }
  378. else if (g_InputState.dwKeyPress & (kKeyDown | kKeyRight))
  379. {
  380. g_iCurMiscMenuItem++;
  381. if (g_iCurMiscMenuItem > 4)
  382. {
  383. g_iCurMiscMenuItem = 0;
  384. }
  385. }
  386. else if (g_InputState.dwKeyPress & kKeySearch)
  387. {
  388. return g_iCurMiscMenuItem + 1;
  389. }
  390. else if (g_InputState.dwKeyPress & kKeyMenu)
  391. {
  392. return 0;
  393. }
  394. return 0xFFFF;
  395. }
  396. static WORD
  397. PAL_BattleUIMiscItemSubMenuUpdate(
  398. VOID
  399. )
  400. /*++
  401. Purpose:
  402. Update the item sub menu of the misc menu.
  403. Parameters:
  404. None.
  405. Return value:
  406. The selected item number. 0 if cancelled, 0xFFFF if not confirmed.
  407. --*/
  408. {
  409. int i;
  410. BYTE bColor;
  411. MENUITEM rgMenuItem[] = {
  412. // value label enabled position
  413. { 0, BATTLEUI_LABEL_USEITEM, TRUE, PAL_XY(44, 62) },
  414. { 1, BATTLEUI_LABEL_THROWITEM, TRUE, PAL_XY(44, 80) },
  415. };
  416. //
  417. // Draw the menu
  418. //
  419. #ifdef PAL_CLASSIC
  420. PAL_BattleUIDrawMiscMenu(1, TRUE);
  421. #else
  422. PAL_BattleUIDrawMiscMenu(0, TRUE);
  423. #endif
  424. PAL_CreateBox(PAL_XY(30, 50), 1, 1, 0, FALSE);
  425. //
  426. // Draw the menu items
  427. //
  428. for (i = 0; i < 2; i++)
  429. {
  430. bColor = MENUITEM_COLOR;
  431. if (i == g_iCurSubMenuItem)
  432. {
  433. bColor = MENUITEM_COLOR_SELECTED;
  434. }
  435. PAL_DrawText(PAL_GetWord(rgMenuItem[i].wNumWord), rgMenuItem[i].pos, bColor,
  436. TRUE, FALSE);
  437. }
  438. //
  439. // Process inputs
  440. //
  441. if (g_InputState.dwKeyPress & (kKeyUp | kKeyLeft))
  442. {
  443. g_iCurSubMenuItem = 0;
  444. }
  445. else if (g_InputState.dwKeyPress & (kKeyDown | kKeyRight))
  446. {
  447. g_iCurSubMenuItem = 1;
  448. }
  449. else if (g_InputState.dwKeyPress & kKeySearch)
  450. {
  451. return g_iCurSubMenuItem + 1;
  452. }
  453. else if (g_InputState.dwKeyPress & kKeyMenu)
  454. {
  455. return 0;
  456. }
  457. return 0xFFFF;
  458. }
  459. VOID
  460. PAL_BattleUIShowText(
  461. LPCSTR lpszText,
  462. WORD wDuration
  463. )
  464. /*++
  465. Purpose:
  466. Show a text message in the battle.
  467. Parameters:
  468. [IN] lpszText - the text message to be shown.
  469. [IN] wDuration - the duration of the message, in milliseconds.
  470. Return value:
  471. None.
  472. --*/
  473. {
  474. if (SDL_GetTicks() < g_Battle.UI.dwMsgShowTime)
  475. {
  476. strcpy(g_Battle.UI.szNextMsg, lpszText);
  477. g_Battle.UI.wNextMsgDuration = wDuration;
  478. }
  479. else
  480. {
  481. strcpy(g_Battle.UI.szMsg, lpszText);
  482. g_Battle.UI.dwMsgShowTime = SDL_GetTicks() + wDuration;
  483. }
  484. }
  485. VOID
  486. PAL_BattleUIPlayerReady(
  487. WORD wPlayerIndex
  488. )
  489. /*++
  490. Purpose:
  491. Start the action selection menu of the specified player.
  492. Parameters:
  493. [IN] wPlayerIndex - the player index.
  494. Return value:
  495. None.
  496. --*/
  497. {
  498. #ifndef PAL_CLASSIC
  499. WORD w = gpGlobals->rgParty[wPlayerIndex].wPlayerRole;
  500. #endif
  501. g_Battle.UI.wCurPlayerIndex = wPlayerIndex;
  502. g_Battle.UI.state = kBattleUISelectMove;
  503. g_Battle.UI.wSelectedAction = 0;
  504. g_Battle.UI.MenuState = kBattleMenuMain;
  505. #ifndef PAL_CLASSIC
  506. //
  507. // Play a sound which indicates the player is ready
  508. //
  509. if (gpGlobals->rgPlayerStatus[w][kStatusPuppet] == 0 &&
  510. gpGlobals->rgPlayerStatus[w][kStatusSleep] == 0 &&
  511. gpGlobals->rgPlayerStatus[w][kStatusConfused] == 0 &&
  512. !g_Battle.UI.fAutoAttack && !gpGlobals->fAutoBattle)
  513. {
  514. SOUND_PlayChannel(78, 1);
  515. }
  516. #endif
  517. }
  518. static VOID
  519. PAL_BattleUIUseItem(
  520. VOID
  521. )
  522. /*++
  523. Purpose:
  524. Use an item in the battle UI.
  525. Parameters:
  526. None.
  527. Return value:
  528. None.
  529. --*/
  530. {
  531. WORD wSelectedItem;
  532. wSelectedItem = PAL_ItemSelectMenuUpdate();
  533. if (wSelectedItem != 0xFFFF)
  534. {
  535. if (wSelectedItem != 0)
  536. {
  537. g_Battle.UI.wActionType = kBattleActionUseItem;
  538. g_Battle.UI.wObjectID = wSelectedItem;
  539. if (gpGlobals->g.rgObject[wSelectedItem].item.wFlags & kItemFlagApplyToAll)
  540. {
  541. g_Battle.UI.state = kBattleUISelectTargetPlayerAll;
  542. }
  543. else
  544. {
  545. #ifdef PAL_CLASSIC
  546. g_Battle.UI.wSelectedIndex = 0;
  547. #else
  548. g_Battle.UI.wSelectedIndex = g_Battle.UI.wCurPlayerIndex;
  549. #endif
  550. g_Battle.UI.state = kBattleUISelectTargetPlayer;
  551. }
  552. }
  553. else
  554. {
  555. g_Battle.UI.MenuState = kBattleMenuMain;
  556. }
  557. }
  558. }
  559. static VOID
  560. PAL_BattleUIThrowItem(
  561. VOID
  562. )
  563. /*++
  564. Purpose:
  565. Throw an item in the battle UI.
  566. Parameters:
  567. None.
  568. Return value:
  569. None.
  570. --*/
  571. {
  572. WORD wSelectedItem = PAL_ItemSelectMenuUpdate();
  573. if (wSelectedItem != 0xFFFF)
  574. {
  575. if (wSelectedItem != 0)
  576. {
  577. g_Battle.UI.wActionType = kBattleActionThrowItem;
  578. g_Battle.UI.wObjectID = wSelectedItem;
  579. if (gpGlobals->g.rgObject[wSelectedItem].item.wFlags & kItemFlagApplyToAll)
  580. {
  581. g_Battle.UI.state = kBattleUISelectTargetEnemyAll;
  582. }
  583. else
  584. {
  585. g_Battle.UI.wSelectedIndex = g_Battle.UI.wPrevEnemyTarget;
  586. g_Battle.UI.state = kBattleUISelectTargetEnemy;
  587. }
  588. }
  589. else
  590. {
  591. g_Battle.UI.MenuState = kBattleMenuMain;
  592. }
  593. }
  594. }
  595. static WORD
  596. PAL_BattleUIPickAutoMagic(
  597. WORD wPlayerRole,
  598. WORD wRandomRange
  599. )
  600. /*++
  601. Purpose:
  602. Pick a magic for the specified player for automatic usage.
  603. Parameters:
  604. [IN] wPlayerRole - the player role ID.
  605. [IN] wRandomRange - the range of the magic power.
  606. Return value:
  607. The object ID of the selected magic. 0 for physical attack.
  608. --*/
  609. {
  610. WORD wMagic = 0, w, wMagicNum;
  611. int i, iMaxPower = 0, iPower;
  612. if (gpGlobals->rgPlayerStatus[wPlayerRole][kStatusSilence] != 0)
  613. {
  614. return 0;
  615. }
  616. for (i = 0; i < MAX_PLAYER_MAGICS; i++)
  617. {
  618. w = gpGlobals->g.PlayerRoles.rgwMagic[i][wPlayerRole];
  619. if (w == 0)
  620. {
  621. continue;
  622. }
  623. wMagicNum = gpGlobals->g.rgObject[w].magic.wMagicNumber;
  624. //
  625. // skip if the magic is an ultimate move or not enough MP
  626. //
  627. if (gpGlobals->g.lprgMagic[wMagicNum].wCostMP == 1 ||
  628. gpGlobals->g.lprgMagic[wMagicNum].wCostMP > gpGlobals->g.PlayerRoles.rgwMP[wPlayerRole] ||
  629. (SHORT)(gpGlobals->g.lprgMagic[wMagicNum].wBaseDamage) <= 0)
  630. {
  631. continue;
  632. }
  633. iPower = (SHORT)(gpGlobals->g.lprgMagic[wMagicNum].wBaseDamage) +
  634. RandomLong(0, wRandomRange);
  635. if (iPower > iMaxPower)
  636. {
  637. iMaxPower = iPower;
  638. wMagic = w;
  639. }
  640. }
  641. return wMagic;
  642. }
  643. VOID
  644. PAL_BattleUIUpdate(
  645. VOID
  646. )
  647. /*++
  648. Purpose:
  649. Update the status of battle UI.
  650. Parameters:
  651. None.
  652. Return value:
  653. None.
  654. --*/
  655. {
  656. int i, j, x, y;
  657. WORD wPlayerRole, w;
  658. static int s_iFrame = 0;
  659. s_iFrame++;
  660. if (g_Battle.UI.fAutoAttack && !gpGlobals->fAutoBattle)
  661. {
  662. //
  663. // Draw the "auto attack" message if in the autoattack mode.
  664. //
  665. if (g_InputState.dwKeyPress & kKeyMenu)
  666. {
  667. g_Battle.UI.fAutoAttack = FALSE;
  668. }
  669. else
  670. {
  671. PAL_DrawText(PAL_GetWord(BATTLEUI_LABEL_AUTO), PAL_XY(280, 10),
  672. MENUITEM_COLOR_CONFIRMED, TRUE, FALSE);
  673. }
  674. }
  675. if (gpGlobals->fAutoBattle)
  676. {
  677. PAL_BattlePlayerCheckReady();
  678. for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
  679. {
  680. if (g_Battle.rgPlayer[i].state == kFighterCom)
  681. {
  682. PAL_BattleUIPlayerReady(i);
  683. break;
  684. }
  685. }
  686. if (g_Battle.UI.state != kBattleUIWait)
  687. {
  688. w = PAL_BattleUIPickAutoMagic(gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole, 9999);
  689. if (w == 0)
  690. {
  691. g_Battle.UI.wActionType = kBattleActionAttack;
  692. g_Battle.UI.wSelectedIndex = PAL_BattleSelectAutoTarget();
  693. }
  694. else
  695. {
  696. g_Battle.UI.wActionType = kBattleActionMagic;
  697. g_Battle.UI.wObjectID = w;
  698. if (gpGlobals->g.rgObject[w].magic.wFlags & kMagicFlagApplyToAll)
  699. {
  700. g_Battle.UI.wSelectedIndex = -1;
  701. }
  702. else
  703. {
  704. g_Battle.UI.wSelectedIndex = PAL_BattleSelectAutoTarget();
  705. }
  706. }
  707. PAL_BattleCommitAction(FALSE);
  708. }
  709. goto end;
  710. }
  711. if (g_InputState.dwKeyPress & kKeyAuto)
  712. {
  713. g_Battle.UI.fAutoAttack = !g_Battle.UI.fAutoAttack;
  714. g_Battle.UI.MenuState = kBattleMenuMain;
  715. }
  716. #ifdef PAL_CLASSIC
  717. if (g_Battle.Phase == kBattlePhasePerformAction)
  718. {
  719. goto end;
  720. }
  721. if (!g_Battle.UI.fAutoAttack)
  722. #endif
  723. {
  724. //
  725. // Draw the player info boxes.
  726. //
  727. for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
  728. {
  729. wPlayerRole = gpGlobals->rgParty[i].wPlayerRole;
  730. w = (WORD)(g_Battle.rgPlayer[i].flTimeMeter);
  731. j = TIMEMETER_COLOR_DEFAULT;
  732. #ifndef PAL_CLASSIC
  733. if (gpGlobals->rgPlayerStatus[wPlayerRole][kStatusHaste] > 0)
  734. {
  735. j = TIMEMETER_COLOR_HASTE;
  736. }
  737. else if (gpGlobals->rgPlayerStatus[wPlayerRole][kStatusSlow] > 0)
  738. {
  739. j = TIMEMETER_COLOR_SLOW;
  740. }
  741. #endif
  742. if (gpGlobals->rgPlayerStatus[wPlayerRole][kStatusSleep] != 0 ||
  743. gpGlobals->rgPlayerStatus[wPlayerRole][kStatusConfused] != 0 ||
  744. gpGlobals->rgPlayerStatus[wPlayerRole][kStatusPuppet] != 0)
  745. {
  746. w = 0;
  747. }
  748. PAL_PlayerInfoBox(PAL_XY(91 + 77 * i, 165), wPlayerRole,
  749. w, j, FALSE);
  750. }
  751. }
  752. if (g_InputState.dwKeyPress & kKeyStatus)
  753. {
  754. PAL_PlayerStatus();
  755. goto end;
  756. }
  757. if (g_Battle.UI.state != kBattleUIWait)
  758. {
  759. wPlayerRole = gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole;
  760. if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] == 0 &&
  761. gpGlobals->rgPlayerStatus[wPlayerRole][kStatusPuppet])
  762. {
  763. g_Battle.UI.wActionType = kBattleActionAttack;
  764. if (PAL_PlayerCanAttackAll(gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole))
  765. {
  766. g_Battle.UI.wSelectedIndex = -1;
  767. }
  768. else
  769. {
  770. g_Battle.UI.wSelectedIndex = PAL_BattleSelectAutoTarget();
  771. }
  772. PAL_BattleCommitAction(FALSE);
  773. goto end; // don't go further
  774. }
  775. //
  776. // Cancel any actions if player is dead or sleeping.
  777. //
  778. if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] == 0 ||
  779. gpGlobals->rgPlayerStatus[wPlayerRole][kStatusSleep] != 0 ||
  780. gpGlobals->rgPlayerStatus[wPlayerRole][kStatusParalyzed] != 0)
  781. {
  782. g_Battle.UI.wActionType = kBattleActionPass;
  783. PAL_BattleCommitAction(FALSE);
  784. goto end; // don't go further
  785. }
  786. if (gpGlobals->rgPlayerStatus[wPlayerRole][kStatusConfused] != 0)
  787. {
  788. g_Battle.UI.wActionType = kBattleActionAttackMate;
  789. PAL_BattleCommitAction(FALSE);
  790. goto end; // don't go further
  791. }
  792. if (g_Battle.UI.fAutoAttack)
  793. {
  794. g_Battle.UI.wActionType = kBattleActionAttack;
  795. if (PAL_PlayerCanAttackAll(gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole))
  796. {
  797. g_Battle.UI.wSelectedIndex = -1;
  798. }
  799. else
  800. {
  801. g_Battle.UI.wSelectedIndex = PAL_BattleSelectAutoTarget();
  802. }
  803. PAL_BattleCommitAction(FALSE);
  804. goto end; // don't go further
  805. }
  806. //
  807. // Draw the arrow on the player's head.
  808. //
  809. i = SPRITENUM_BATTLE_ARROW_CURRENTPLAYER_RED;
  810. if (s_iFrame & 1)
  811. {
  812. i = SPRITENUM_BATTLE_ARROW_CURRENTPLAYER;
  813. }
  814. x = g_rgPlayerPos[gpGlobals->wMaxPartyMemberIndex][g_Battle.UI.wCurPlayerIndex][0] - 8;
  815. y = g_rgPlayerPos[gpGlobals->wMaxPartyMemberIndex][g_Battle.UI.wCurPlayerIndex][1] - 74;
  816. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, i), gpScreen, PAL_XY(x, y));
  817. }
  818. switch (g_Battle.UI.state)
  819. {
  820. case kBattleUIWait:
  821. if (!g_Battle.fEnemyCleared)
  822. {
  823. PAL_BattlePlayerCheckReady();
  824. for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
  825. {
  826. if (g_Battle.rgPlayer[i].state == kFighterCom)
  827. {
  828. PAL_BattleUIPlayerReady(i);
  829. break;
  830. }
  831. }
  832. }
  833. break;
  834. case kBattleUISelectMove:
  835. //
  836. // Draw the icons
  837. //
  838. {
  839. struct {
  840. int iSpriteNum;
  841. PAL_POS pos;
  842. BATTLEUIACTION action;
  843. } rgItems[] =
  844. {
  845. {SPRITENUM_BATTLEICON_ATTACK, PAL_XY(27, 140), kBattleUIActionAttack},
  846. {SPRITENUM_BATTLEICON_MAGIC, PAL_XY(0, 155), kBattleUIActionMagic},
  847. {SPRITENUM_BATTLEICON_COOPMAGIC, PAL_XY(54, 155), kBattleUIActionCoopMagic},
  848. {SPRITENUM_BATTLEICON_MISCMENU, PAL_XY(27, 170), kBattleUIActionMisc}
  849. };
  850. if (g_Battle.UI.MenuState == kBattleMenuMain)
  851. {
  852. if (g_InputState.dir == kDirNorth)
  853. {
  854. g_Battle.UI.wSelectedAction = 0;
  855. }
  856. else if (g_InputState.dir == kDirSouth)
  857. {
  858. g_Battle.UI.wSelectedAction = 3;
  859. }
  860. else if (g_InputState.dir == kDirWest)
  861. {
  862. if (PAL_BattleUIIsActionValid(kBattleUIActionMagic))
  863. {
  864. g_Battle.UI.wSelectedAction = 1;
  865. }
  866. }
  867. else if (g_InputState.dir == kDirEast)
  868. {
  869. if (PAL_BattleUIIsActionValid(kBattleUIActionCoopMagic))
  870. {
  871. g_Battle.UI.wSelectedAction = 2;
  872. }
  873. }
  874. }
  875. if (!PAL_BattleUIIsActionValid(rgItems[g_Battle.UI.wSelectedAction].action))
  876. {
  877. g_Battle.UI.wSelectedAction = 0;
  878. }
  879. for (i = 0; i < 4; i++)
  880. {
  881. if (g_Battle.UI.wSelectedAction == i)
  882. {
  883. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, rgItems[i].iSpriteNum),
  884. gpScreen, rgItems[i].pos);
  885. }
  886. else if (PAL_BattleUIIsActionValid(rgItems[i].action))
  887. {
  888. PAL_RLEBlitMonoColor(PAL_SpriteGetFrame(gpSpriteUI, rgItems[i].iSpriteNum),
  889. gpScreen, rgItems[i].pos, 0, -4);
  890. }
  891. else
  892. {
  893. PAL_RLEBlitMonoColor(PAL_SpriteGetFrame(gpSpriteUI, rgItems[i].iSpriteNum),
  894. gpScreen, rgItems[i].pos, 0x10, -4);
  895. }
  896. }
  897. switch (g_Battle.UI.MenuState)
  898. {
  899. case kBattleMenuMain:
  900. if (g_InputState.dwKeyPress & kKeySearch)
  901. {
  902. switch (g_Battle.UI.wSelectedAction)
  903. {
  904. case 0:
  905. //
  906. // Attack
  907. //
  908. g_Battle.UI.wActionType = kBattleActionAttack;
  909. if (PAL_PlayerCanAttackAll(gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole))
  910. {
  911. g_Battle.UI.state = kBattleUISelectTargetEnemyAll;
  912. }
  913. else
  914. {
  915. g_Battle.UI.wSelectedIndex = g_Battle.UI.wPrevEnemyTarget;
  916. g_Battle.UI.state = kBattleUISelectTargetEnemy;
  917. }
  918. break;
  919. case 1:
  920. //
  921. // Magic
  922. //
  923. g_Battle.UI.MenuState = kBattleMenuMagicSelect;
  924. PAL_MagicSelectionMenuInit(wPlayerRole, TRUE, 0);
  925. break;
  926. case 2:
  927. //
  928. // Cooperative magic
  929. //
  930. w = gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole;
  931. w = PAL_GetPlayerCooperativeMagic(w);
  932. g_Battle.UI.wActionType = kBattleActionCoopMagic;
  933. g_Battle.UI.wObjectID = w;
  934. if (gpGlobals->g.rgObject[w].magic.wFlags & kMagicFlagUsableToEnemy)
  935. {
  936. if (gpGlobals->g.rgObject[w].magic.wFlags & kMagicFlagApplyToAll)
  937. {
  938. g_Battle.UI.state = kBattleUISelectTargetEnemyAll;
  939. }
  940. else
  941. {
  942. g_Battle.UI.wSelectedIndex = g_Battle.UI.wPrevEnemyTarget;
  943. g_Battle.UI.state = kBattleUISelectTargetEnemy;
  944. }
  945. }
  946. else
  947. {
  948. if (gpGlobals->g.rgObject[w].magic.wFlags & kMagicFlagApplyToAll)
  949. {
  950. g_Battle.UI.state = kBattleUISelectTargetPlayerAll;
  951. }
  952. else
  953. {
  954. #ifdef PAL_CLASSIC
  955. g_Battle.UI.wSelectedIndex = 0;
  956. #else
  957. g_Battle.UI.wSelectedIndex = g_Battle.UI.wCurPlayerIndex;
  958. #endif
  959. g_Battle.UI.state = kBattleUISelectTargetPlayer;
  960. }
  961. }
  962. break;
  963. case 3:
  964. //
  965. // Misc menu
  966. //
  967. g_Battle.UI.MenuState = kBattleMenuMisc;
  968. g_iCurMiscMenuItem = 0;
  969. break;
  970. }
  971. }
  972. else if (g_InputState.dwKeyPress & kKeyDefend)
  973. {
  974. g_Battle.UI.wActionType = kBattleActionDefend;
  975. PAL_BattleCommitAction(FALSE);
  976. }
  977. else if (g_InputState.dwKeyPress & kKeyForce)
  978. {
  979. w = PAL_BattleUIPickAutoMagic(gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole, 60);
  980. if (w == 0)
  981. {
  982. g_Battle.UI.wActionType = kBattleActionAttack;
  983. if (PAL_PlayerCanAttackAll(gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole))
  984. {
  985. g_Battle.UI.wSelectedIndex = -1;
  986. }
  987. else
  988. {
  989. g_Battle.UI.wSelectedIndex = PAL_BattleSelectAutoTarget();
  990. }
  991. }
  992. else
  993. {
  994. g_Battle.UI.wActionType = kBattleActionMagic;
  995. g_Battle.UI.wObjectID = w;
  996. if (gpGlobals->g.rgObject[w].magic.wFlags & kMagicFlagApplyToAll)
  997. {
  998. g_Battle.UI.wSelectedIndex = -1;
  999. }
  1000. else
  1001. {
  1002. g_Battle.UI.wSelectedIndex = PAL_BattleSelectAutoTarget();
  1003. }
  1004. }
  1005. PAL_BattleCommitAction(FALSE);
  1006. }
  1007. else if (g_InputState.dwKeyPress & kKeyFlee)
  1008. {
  1009. g_Battle.UI.wActionType = kBattleActionFlee;
  1010. PAL_BattleCommitAction(FALSE);
  1011. }
  1012. else if (g_InputState.dwKeyPress & kKeyUseItem)
  1013. {
  1014. g_Battle.UI.MenuState = kBattleMenuUseItemSelect;
  1015. PAL_ItemSelectMenuInit(kItemFlagUsable);
  1016. }
  1017. else if (g_InputState.dwKeyPress & kKeyThrowItem)
  1018. {
  1019. g_Battle.UI.MenuState = kBattleMenuThrowItemSelect;
  1020. PAL_ItemSelectMenuInit(kItemFlagThrowable);
  1021. }
  1022. else if (g_InputState.dwKeyPress & kKeyRepeat)
  1023. {
  1024. PAL_BattleCommitAction(TRUE);
  1025. }
  1026. #ifdef PAL_CLASSIC
  1027. else if (g_InputState.dwKeyPress & kKeyMenu)
  1028. {
  1029. g_Battle.rgPlayer[g_Battle.UI.wCurPlayerIndex].state = kFighterWait;
  1030. g_Battle.UI.state = kBattleUIWait;
  1031. if (g_Battle.UI.wCurPlayerIndex > 0)
  1032. {
  1033. //
  1034. // Revert to the previous player
  1035. //
  1036. do
  1037. {
  1038. g_Battle.rgPlayer[--g_Battle.UI.wCurPlayerIndex].state = kFighterWait;
  1039. if (g_Battle.rgPlayer[g_Battle.UI.wCurPlayerIndex].action.ActionType == kBattleActionThrowItem)
  1040. {
  1041. for (i = 0; i < MAX_INVENTORY; i++)
  1042. {
  1043. if (gpGlobals->rgInventory[i].wItem ==
  1044. g_Battle.rgPlayer[g_Battle.UI.wCurPlayerIndex].action.wActionID)
  1045. {
  1046. gpGlobals->rgInventory[i].nAmountInUse--;
  1047. break;
  1048. }
  1049. }
  1050. }
  1051. else if (g_Battle.rgPlayer[g_Battle.UI.wCurPlayerIndex].action.ActionType == kBattleActionUseItem)
  1052. {
  1053. if (gpGlobals->g.rgObject[g_Battle.rgPlayer[g_Battle.UI.wCurPlayerIndex].action.wActionID].item.wFlags & kItemFlagConsuming)
  1054. {
  1055. for (i = 0; i < MAX_INVENTORY; i++)
  1056. {
  1057. if (gpGlobals->rgInventory[i].wItem ==
  1058. g_Battle.rgPlayer[g_Battle.UI.wCurPlayerIndex].action.wActionID)
  1059. {
  1060. gpGlobals->rgInventory[i].nAmountInUse--;
  1061. break;
  1062. }
  1063. }
  1064. }
  1065. }
  1066. } while (g_Battle.UI.wCurPlayerIndex > 0 &&
  1067. (gpGlobals->g.PlayerRoles.rgwHP[gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole] == 0 ||
  1068. gpGlobals->rgPlayerStatus[gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole][kStatusConfused] > 0 ||
  1069. gpGlobals->rgPlayerStatus[gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole][kStatusSleep] > 0 ||
  1070. gpGlobals->rgPlayerStatus[gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole][kStatusParalyzed] > 0));
  1071. }
  1072. }
  1073. #else
  1074. else if (g_InputState.dwKeyPress & kKeyMenu)
  1075. {
  1076. float flMin = -1;
  1077. j = -1;
  1078. for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
  1079. {
  1080. if (g_Battle.rgPlayer[i].flTimeMeter >= 100)
  1081. {
  1082. g_Battle.rgPlayer[i].flTimeMeter += 100; // HACKHACK: Prevent the time meter from going below 100
  1083. if ((g_Battle.rgPlayer[i].flTimeMeter < flMin || flMin < 0) &&
  1084. i != (int)g_Battle.UI.wCurPlayerIndex &&
  1085. g_Battle.rgPlayer[i].state == kFighterWait)
  1086. {
  1087. flMin = g_Battle.rgPlayer[i].flTimeMeter;
  1088. j = i;
  1089. }
  1090. }
  1091. }
  1092. if (j != -1)
  1093. {
  1094. g_Battle.rgPlayer[g_Battle.UI.wCurPlayerIndex].flTimeMeter = flMin - 99;
  1095. g_Battle.rgPlayer[g_Battle.UI.wCurPlayerIndex].state = kFighterWait;
  1096. g_Battle.UI.state = kBattleUIWait;
  1097. }
  1098. }
  1099. #endif
  1100. break;
  1101. case kBattleMenuMagicSelect:
  1102. w = PAL_MagicSelectionMenuUpdate();
  1103. if (w != 0xFFFF)
  1104. {
  1105. g_Battle.UI.MenuState = kBattleMenuMain;
  1106. if (w != 0)
  1107. {
  1108. g_Battle.UI.wActionType = kBattleActionMagic;
  1109. g_Battle.UI.wObjectID = w;
  1110. if (gpGlobals->g.rgObject[w].magic.wFlags & kMagicFlagUsableToEnemy)
  1111. {
  1112. if (gpGlobals->g.rgObject[w].magic.wFlags & kMagicFlagApplyToAll)
  1113. {
  1114. g_Battle.UI.state = kBattleUISelectTargetEnemyAll;
  1115. }
  1116. else
  1117. {
  1118. g_Battle.UI.wSelectedIndex = g_Battle.UI.wPrevEnemyTarget;
  1119. g_Battle.UI.state = kBattleUISelectTargetEnemy;
  1120. }
  1121. }
  1122. else
  1123. {
  1124. if (gpGlobals->g.rgObject[w].magic.wFlags & kMagicFlagApplyToAll)
  1125. {
  1126. g_Battle.UI.state = kBattleUISelectTargetPlayerAll;
  1127. }
  1128. else
  1129. {
  1130. #ifdef PAL_CLASSIC
  1131. g_Battle.UI.wSelectedIndex = 0;
  1132. #else
  1133. g_Battle.UI.wSelectedIndex = g_Battle.UI.wCurPlayerIndex;
  1134. #endif
  1135. g_Battle.UI.state = kBattleUISelectTargetPlayer;
  1136. }
  1137. }
  1138. }
  1139. }
  1140. break;
  1141. case kBattleMenuUseItemSelect:
  1142. PAL_BattleUIUseItem();
  1143. break;
  1144. case kBattleMenuThrowItemSelect:
  1145. PAL_BattleUIThrowItem();
  1146. break;
  1147. case kBattleMenuMisc:
  1148. w = PAL_BattleUIMiscMenuUpdate();
  1149. if (w != 0xFFFF)
  1150. {
  1151. g_Battle.UI.MenuState = kBattleMenuMain;
  1152. switch (w)
  1153. {
  1154. #ifdef PAL_CLASSIC
  1155. case 2: // item
  1156. #else
  1157. case 1: // item
  1158. #endif
  1159. g_Battle.UI.MenuState = kBattleMenuMiscItemSubMenu;
  1160. g_iCurSubMenuItem = 0;
  1161. break;
  1162. #ifdef PAL_CLASSIC
  1163. case 3: // defend
  1164. #else
  1165. case 2: // defend
  1166. #endif
  1167. g_Battle.UI.wActionType = kBattleActionDefend;
  1168. PAL_BattleCommitAction(FALSE);
  1169. break;
  1170. #ifdef PAL_CLASSIC
  1171. case 1: // auto
  1172. #else
  1173. case 3: // auto
  1174. #endif
  1175. g_Battle.UI.fAutoAttack = TRUE;
  1176. break;
  1177. case 4: // flee
  1178. g_Battle.UI.wActionType = kBattleActionFlee;
  1179. PAL_BattleCommitAction(FALSE);
  1180. break;
  1181. case 5: // status
  1182. PAL_PlayerStatus();
  1183. break;
  1184. }
  1185. }
  1186. break;
  1187. case kBattleMenuMiscItemSubMenu:
  1188. w = PAL_BattleUIMiscItemSubMenuUpdate();
  1189. if (w != 0xFFFF)
  1190. {
  1191. g_Battle.UI.MenuState = kBattleMenuMain;
  1192. switch (w)
  1193. {
  1194. case 1: // use
  1195. g_Battle.UI.MenuState = kBattleMenuUseItemSelect;
  1196. PAL_ItemSelectMenuInit(kItemFlagUsable);
  1197. break;
  1198. case 2: // throw
  1199. g_Battle.UI.MenuState = kBattleMenuThrowItemSelect;
  1200. PAL_ItemSelectMenuInit(kItemFlagThrowable);
  1201. break;
  1202. }
  1203. }
  1204. break;
  1205. }
  1206. }
  1207. break;
  1208. case kBattleUISelectTargetEnemy:
  1209. x = -1;
  1210. y = 0;
  1211. for (i = 0; i <= g_Battle.wMaxEnemyIndex; i++)
  1212. {
  1213. if (g_Battle.rgEnemy[i].wObjectID != 0)
  1214. {
  1215. x = i;
  1216. y++;
  1217. }
  1218. }
  1219. if (x == -1)
  1220. {
  1221. g_Battle.UI.state = kBattleUISelectMove;
  1222. break;
  1223. }
  1224. if (g_Battle.UI.wActionType == kBattleActionCoopMagic)
  1225. {
  1226. if (!PAL_BattleUIIsActionValid(kBattleActionCoopMagic))
  1227. {
  1228. g_Battle.UI.state = kBattleUISelectMove;
  1229. break;
  1230. }
  1231. }
  1232. #ifdef PAL_CLASSIC
  1233. //
  1234. // Don't bother selecting when only 1 enemy left
  1235. //
  1236. if (y == 1)
  1237. {
  1238. g_Battle.UI.wPrevEnemyTarget = (WORD)x;
  1239. PAL_BattleCommitAction(FALSE);
  1240. break;
  1241. }
  1242. #endif
  1243. if (g_Battle.UI.wSelectedIndex > x)
  1244. {
  1245. g_Battle.UI.wSelectedIndex = x;
  1246. }
  1247. for (i = 0; i <= x; i++)
  1248. {
  1249. if (g_Battle.rgEnemy[g_Battle.UI.wSelectedIndex].wObjectID != 0)
  1250. {
  1251. break;
  1252. }
  1253. g_Battle.UI.wSelectedIndex++;
  1254. g_Battle.UI.wSelectedIndex %= x + 1;
  1255. }
  1256. //
  1257. // Highlight the selected enemy
  1258. //
  1259. if (s_iFrame & 1)
  1260. {
  1261. i = g_Battle.UI.wSelectedIndex;
  1262. x = PAL_X(g_Battle.rgEnemy[i].pos);
  1263. y = PAL_Y(g_Battle.rgEnemy[i].pos);
  1264. x -= PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame)) / 2;
  1265. y -= PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame));
  1266. PAL_RLEBlitWithColorShift(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame),
  1267. gpScreen, PAL_XY(x, y), 7);
  1268. }
  1269. if (g_InputState.dwKeyPress & kKeyMenu)
  1270. {
  1271. g_Battle.UI.state = kBattleUISelectMove;
  1272. }
  1273. else if (g_InputState.dwKeyPress & kKeySearch)
  1274. {
  1275. g_Battle.UI.wPrevEnemyTarget = g_Battle.UI.wSelectedIndex;
  1276. PAL_BattleCommitAction(FALSE);
  1277. }
  1278. else if (g_InputState.dwKeyPress & (kKeyLeft | kKeyDown))
  1279. {
  1280. if (g_Battle.UI.wSelectedIndex != 0)
  1281. {
  1282. g_Battle.UI.wSelectedIndex--;
  1283. while (g_Battle.UI.wSelectedIndex != 0 &&
  1284. g_Battle.rgEnemy[g_Battle.UI.wSelectedIndex].wObjectID == 0)
  1285. {
  1286. g_Battle.UI.wSelectedIndex--;
  1287. }
  1288. }
  1289. }
  1290. else if (g_InputState.dwKeyPress & (kKeyRight | kKeyUp))
  1291. {
  1292. if (g_Battle.UI.wSelectedIndex < x)
  1293. {
  1294. g_Battle.UI.wSelectedIndex++;
  1295. while (g_Battle.UI.wSelectedIndex < x &&
  1296. g_Battle.rgEnemy[g_Battle.UI.wSelectedIndex].wObjectID == 0)
  1297. {
  1298. g_Battle.UI.wSelectedIndex++;
  1299. }
  1300. }
  1301. }
  1302. break;
  1303. case kBattleUISelectTargetPlayer:
  1304. #ifdef PAL_CLASSIC
  1305. //
  1306. // Don't bother selecting when only 1 player is in the party
  1307. //
  1308. if (gpGlobals->wMaxPartyMemberIndex == 0)
  1309. {
  1310. g_Battle.UI.wSelectedIndex = 0;
  1311. PAL_BattleCommitAction(FALSE);
  1312. }
  1313. #endif
  1314. j = SPRITENUM_BATTLE_ARROW_SELECTEDPLAYER;
  1315. if (s_iFrame & 1)
  1316. {
  1317. j = SPRITENUM_BATTLE_ARROW_SELECTEDPLAYER_RED;
  1318. }
  1319. //
  1320. // Draw arrows on the selected player
  1321. //
  1322. x = g_rgPlayerPos[gpGlobals->wMaxPartyMemberIndex][g_Battle.UI.wSelectedIndex][0] - 8;
  1323. y = g_rgPlayerPos[gpGlobals->wMaxPartyMemberIndex][g_Battle.UI.wSelectedIndex][1] - 67;
  1324. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, j), gpScreen, PAL_XY(x, y));
  1325. if (g_InputState.dwKeyPress & kKeyMenu)
  1326. {
  1327. g_Battle.UI.state = kBattleUISelectMove;
  1328. }
  1329. else if (g_InputState.dwKeyPress & kKeySearch)
  1330. {
  1331. PAL_BattleCommitAction(FALSE);
  1332. }
  1333. else if (g_InputState.dwKeyPress & (kKeyLeft | kKeyDown))
  1334. {
  1335. if (g_Battle.UI.wSelectedIndex != 0)
  1336. {
  1337. g_Battle.UI.wSelectedIndex--;
  1338. }
  1339. else
  1340. {
  1341. g_Battle.UI.wSelectedIndex = gpGlobals->wMaxPartyMemberIndex;
  1342. }
  1343. }
  1344. else if (g_InputState.dwKeyPress & (kKeyRight | kKeyUp))
  1345. {
  1346. if (g_Battle.UI.wSelectedIndex < gpGlobals->wMaxPartyMemberIndex)
  1347. {
  1348. g_Battle.UI.wSelectedIndex++;
  1349. }
  1350. else
  1351. {
  1352. g_Battle.UI.wSelectedIndex = 0;
  1353. }
  1354. }
  1355. break;
  1356. case kBattleUISelectTargetEnemyAll:
  1357. #ifdef PAL_CLASSIC
  1358. //
  1359. // Don't bother selecting
  1360. //
  1361. g_Battle.UI.wSelectedIndex = (WORD)-1;
  1362. PAL_BattleCommitAction(FALSE);
  1363. #else
  1364. if (g_Battle.UI.wActionType == kBattleActionCoopMagic)
  1365. {
  1366. if (!PAL_BattleUIIsActionValid(kBattleActionCoopMagic))
  1367. {
  1368. g_Battle.UI.state = kBattleUISelectMove;
  1369. break;
  1370. }
  1371. }
  1372. if (s_iFrame & 1)
  1373. {
  1374. //
  1375. // Highlight all enemies
  1376. //
  1377. for (i = g_Battle.wMaxEnemyIndex; i >= 0; i--)
  1378. {
  1379. if (g_Battle.rgEnemy[i].wObjectID == 0)
  1380. {
  1381. continue;
  1382. }
  1383. x = PAL_X(g_Battle.rgEnemy[i].pos);
  1384. y = PAL_Y(g_Battle.rgEnemy[i].pos);
  1385. x -= PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame)) / 2;
  1386. y -= PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame));
  1387. PAL_RLEBlitWithColorShift(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame),
  1388. gpScreen, PAL_XY(x, y), 7);
  1389. }
  1390. }
  1391. if (g_InputState.dwKeyPress & kKeyMenu)
  1392. {
  1393. g_Battle.UI.state = kBattleUISelectMove;
  1394. }
  1395. else if (g_InputState.dwKeyPress & kKeySearch)
  1396. {
  1397. g_Battle.UI.wSelectedIndex = (WORD)-1;
  1398. PAL_BattleCommitAction(FALSE);
  1399. }
  1400. #endif
  1401. break;
  1402. case kBattleUISelectTargetPlayerAll:
  1403. #ifdef PAL_CLASSIC
  1404. //
  1405. // Don't bother selecting
  1406. //
  1407. g_Battle.UI.wSelectedIndex = (WORD)-1;
  1408. PAL_BattleCommitAction(FALSE);
  1409. #else
  1410. j = SPRITENUM_BATTLE_ARROW_SELECTEDPLAYER;
  1411. if (s_iFrame & 1)
  1412. {
  1413. j = SPRITENUM_BATTLE_ARROW_SELECTEDPLAYER_RED;
  1414. }
  1415. for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++)
  1416. {
  1417. if (g_Battle.UI.wActionType == kBattleActionMagic)
  1418. {
  1419. w = gpGlobals->g.rgObject[g_Battle.UI.wObjectID].magic.wMagicNumber;
  1420. if (gpGlobals->g.lprgMagic[w].wType == kMagicTypeTrance)
  1421. {
  1422. if (i != g_Battle.UI.wCurPlayerIndex)
  1423. continue;
  1424. }
  1425. }
  1426. //
  1427. // Draw arrows on all players, despite of dead or not
  1428. //
  1429. x = g_rgPlayerPos[gpGlobals->wMaxPartyMemberIndex][i][0] - 8;
  1430. y = g_rgPlayerPos[gpGlobals->wMaxPartyMemberIndex][i][1] - 67;
  1431. PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, j), gpScreen, PAL_XY(x, y));
  1432. }
  1433. if (g_InputState.dwKeyPress & kKeyMenu)
  1434. {
  1435. g_Battle.UI.state = kBattleUISelectMove;
  1436. }
  1437. else if (g_InputState.dwKeyPress & kKeySearch)
  1438. {
  1439. g_Battle.UI.wSelectedIndex = (WORD)-1;
  1440. PAL_BattleCommitAction(FALSE);
  1441. }
  1442. #endif
  1443. break;
  1444. }
  1445. end:
  1446. //
  1447. // Show the text message if there is one.
  1448. //
  1449. #ifndef PAL_CLASSIC
  1450. if (SDL_GetTicks() < g_Battle.UI.dwMsgShowTime)
  1451. {
  1452. //
  1453. // The text should be shown in a small window at the center of the screen
  1454. //
  1455. PAL_POS pos;
  1456. int len = strlen(g_Battle.UI.szMsg);
  1457. //
  1458. // Create the window box
  1459. //
  1460. pos = PAL_XY(160 - len * 4, 40);
  1461. PAL_CreateSingleLineBox(pos, (len + 1) / 2, FALSE);
  1462. //
  1463. // Show the text on the screen
  1464. //
  1465. pos = PAL_XY(PAL_X(pos) + 8 + ((len & 1) << 2), PAL_Y(pos) + 10);
  1466. PAL_DrawText(g_Battle.UI.szMsg, pos, 0, FALSE, FALSE);
  1467. }
  1468. else if (g_Battle.UI.szNextMsg[0] != '\0')
  1469. {
  1470. strcpy(g_Battle.UI.szMsg, g_Battle.UI.szNextMsg);
  1471. g_Battle.UI.dwMsgShowTime = SDL_GetTicks() + g_Battle.UI.wNextMsgDuration;
  1472. g_Battle.UI.szNextMsg[0] = '\0';
  1473. }
  1474. #endif
  1475. //
  1476. // Draw the numbers
  1477. //
  1478. for (i = 0; i < BATTLEUI_MAX_SHOWNUM; i++)
  1479. {
  1480. if (g_Battle.UI.rgShowNum[i].wNum > 0)
  1481. {
  1482. if ((SDL_GetTicks() - g_Battle.UI.rgShowNum[i].dwTime) / BATTLE_FRAME_TIME > 10)
  1483. {
  1484. g_Battle.UI.rgShowNum[i].wNum = 0;
  1485. }
  1486. else
  1487. {
  1488. PAL_DrawNumber(g_Battle.UI.rgShowNum[i].wNum, 5,
  1489. PAL_XY(PAL_X(g_Battle.UI.rgShowNum[i].pos), PAL_Y(g_Battle.UI.rgShowNum[i].pos) - (SDL_GetTicks() - g_Battle.UI.rgShowNum[i].dwTime) / BATTLE_FRAME_TIME),
  1490. g_Battle.UI.rgShowNum[i].color, kNumAlignRight);
  1491. }
  1492. }
  1493. }
  1494. PAL_ClearKeyState();
  1495. }
  1496. VOID
  1497. PAL_BattleUIShowNum(
  1498. WORD wNum,
  1499. PAL_POS pos,
  1500. NUMCOLOR color
  1501. )
  1502. /*++
  1503. Purpose:
  1504. Show a number on battle screen (indicates HP/MP change).
  1505. Parameters:
  1506. [IN] wNum - number to be shown.
  1507. [IN] pos - position of the number on the screen.
  1508. [IN] color - color of the number.
  1509. Return value:
  1510. None.
  1511. --*/
  1512. {
  1513. int i;
  1514. for (i = 0; i < BATTLEUI_MAX_SHOWNUM; i++)
  1515. {
  1516. if (g_Battle.UI.rgShowNum[i].wNum == 0)
  1517. {
  1518. g_Battle.UI.rgShowNum[i].wNum = wNum;
  1519. g_Battle.UI.rgShowNum[i].pos = PAL_XY(PAL_X(pos) - 15, PAL_Y(pos));
  1520. g_Battle.UI.rgShowNum[i].color = color;
  1521. g_Battle.UI.rgShowNum[i].dwTime = SDL_GetTicks();
  1522. break;
  1523. }
  1524. }
  1525. }