uibattle.c 46 KB

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