global.c 50 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273
  1. /* -*- mode: c; tab-width: 4; c-basic-offset: 4; c-file-style: "linux" -*- */
  2. //
  3. // Copyright (c) 2009-2011, Wei Mingzhi <whistler_wmz@users.sf.net>.
  4. // Copyright (c) 2011-2017, SDLPAL development team.
  5. // All rights reserved.
  6. //
  7. // This file is part of SDLPAL.
  8. //
  9. // SDLPAL is free software: you can redistribute it and/or modify
  10. // it under the terms of the GNU General Public License as published by
  11. // the Free Software Foundation, either version 3 of the License, or
  12. // (at your option) any later version.
  13. //
  14. // This program is distributed in the hope that it will be useful,
  15. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. // GNU General Public License for more details.
  18. //
  19. // You should have received a copy of the GNU General Public License
  20. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  21. //
  22. #include "main.h"
  23. #include "resampler.h"
  24. #include "palcfg.h"
  25. static GLOBALVARS _gGlobals;
  26. GLOBALVARS * const gpGlobals = &_gGlobals;
  27. CONFIGURATION gConfig;
  28. #if SDL_BYTEORDER == SDL_LIL_ENDIAN
  29. #define DO_BYTESWAP(buf, size)
  30. #else
  31. #define DO_BYTESWAP(buf, size) \
  32. do { \
  33. int i; \
  34. for (i = 0; i < (size) / 2; i++) \
  35. { \
  36. ((LPWORD)(buf))[i] = SDL_SwapLE16(((LPWORD)(buf))[i]); \
  37. } \
  38. } while(0)
  39. #endif
  40. #define LOAD_DATA(buf, size, chunknum, fp) \
  41. do { \
  42. PAL_MKFReadChunk((LPBYTE)(buf), (size), (chunknum), (fp)); \
  43. DO_BYTESWAP(buf, size); \
  44. } while(0)
  45. BOOL
  46. PAL_IsWINVersion(
  47. BOOL *pfIsWIN95
  48. )
  49. {
  50. FILE *fps[] = { UTIL_OpenRequiredFile("abc.mkf"), UTIL_OpenRequiredFile("map.mkf"), gpGlobals->f.fpF, gpGlobals->f.fpFBP, gpGlobals->f.fpFIRE, gpGlobals->f.fpMGO };
  51. uint8_t *data = NULL;
  52. int data_size = 0, dos_score = 0, win_score = 0;
  53. BOOL result = FALSE;
  54. for (int i = 0; i < sizeof(fps) / sizeof(FILE *); i++)
  55. {
  56. //
  57. // Find the first non-empty sub-file
  58. //
  59. int count = PAL_MKFGetChunkCount(fps[i]), j = 0, size;
  60. while (j < count && (size = PAL_MKFGetChunkSize(j, fps[i])) < 4) j++;
  61. if (j >= count) goto PAL_IsWINVersion_Exit;
  62. //
  63. // Read the content and check the compression signature
  64. // Note that this check is not 100% correct, however in incorrect situations,
  65. // the sub-file will be over 784MB if uncompressed, which is highly unlikely.
  66. //
  67. if (data_size < size) data = (uint8_t *)realloc(data, data_size = size);
  68. PAL_MKFReadChunk(data, data_size, j, fps[i]);
  69. if (data[0] == 'Y' && data[1] == 'J' && data[2] == '_' && data[3] == '1')
  70. {
  71. if (win_score > 0)
  72. goto PAL_IsWINVersion_Exit;
  73. else
  74. dos_score++;
  75. }
  76. else
  77. {
  78. if (dos_score > 0)
  79. goto PAL_IsWINVersion_Exit;
  80. else
  81. win_score++;
  82. }
  83. }
  84. //
  85. // Finally check the size of object definition
  86. //
  87. data_size = PAL_MKFGetChunkSize(2, gpGlobals->f.fpSSS);
  88. if (data_size % sizeof(OBJECT) == 0 && data_size % sizeof(OBJECT_DOS) != 0 && dos_score > 0) goto PAL_IsWINVersion_Exit;
  89. if (data_size % sizeof(OBJECT_DOS) == 0 && data_size % sizeof(OBJECT) != 0 && win_score > 0) goto PAL_IsWINVersion_Exit;
  90. if (pfIsWIN95) *pfIsWIN95 = (win_score == sizeof(fps) / sizeof(FILE *)) ? TRUE : FALSE;
  91. result = TRUE;
  92. PAL_IsWINVersion_Exit:
  93. free(data);
  94. fclose(fps[1]);
  95. fclose(fps[0]);
  96. return result;
  97. }
  98. CODEPAGE
  99. PAL_DetectCodePage(
  100. const char * filename
  101. )
  102. {
  103. FILE *fp;
  104. char *word_buf = NULL;
  105. long word_len = 0;
  106. CODEPAGE cp = CP_BIG5;
  107. if (NULL != (fp = UTIL_OpenFile(filename)))
  108. {
  109. fseek(fp, 0, SEEK_END);
  110. word_len = ftell(fp);
  111. word_buf = (char *)malloc(word_len);
  112. fseek(fp, 0, SEEK_SET);
  113. word_len = fread(word_buf, 1, word_len, fp);
  114. UTIL_CloseFile(fp);
  115. // Eliminates null characters so that PAL_MultiByteToWideCharCP works properly
  116. for (char *ptr = word_buf; ptr < word_buf + word_len; ptr++)
  117. {
  118. if (!*ptr)
  119. *ptr = ' ';
  120. }
  121. }
  122. if (word_buf)
  123. {
  124. int probability;
  125. cp = PAL_DetectCodePageForString(word_buf, (int)word_len, cp, &probability);
  126. free(word_buf);
  127. if (probability == 100)
  128. UTIL_LogOutput(LOGLEVEL_INFO, "PAL_DetectCodePage detected code page '%s' for %s\n", cp ? "GBK" : "BIG5", filename);
  129. else
  130. UTIL_LogOutput(LOGLEVEL_WARNING, "PAL_DetectCodePage detected the most possible (%d) code page '%s' for %s\n", probability, cp ? "GBK" : "BIG5", filename);
  131. }
  132. return cp;
  133. }
  134. INT
  135. PAL_InitGlobals(
  136. VOID
  137. )
  138. /*++
  139. Purpose:
  140. Initialize global data.
  141. Parameters:
  142. None.
  143. Return value:
  144. 0 = success, -1 = error.
  145. --*/
  146. {
  147. //
  148. // Open files
  149. //
  150. gpGlobals->f.fpFBP = UTIL_OpenRequiredFile("fbp.mkf");
  151. gpGlobals->f.fpMGO = UTIL_OpenRequiredFile("mgo.mkf");
  152. gpGlobals->f.fpBALL = UTIL_OpenRequiredFile("ball.mkf");
  153. gpGlobals->f.fpDATA = UTIL_OpenRequiredFile("data.mkf");
  154. gpGlobals->f.fpF = UTIL_OpenRequiredFile("f.mkf");
  155. gpGlobals->f.fpFIRE = UTIL_OpenRequiredFile("fire.mkf");
  156. gpGlobals->f.fpRGM = UTIL_OpenRequiredFile("rgm.mkf");
  157. gpGlobals->f.fpSSS = UTIL_OpenRequiredFile("sss.mkf");
  158. //
  159. // Retrieve game resource version
  160. //
  161. if (!PAL_IsWINVersion(&gConfig.fIsWIN95)) return -1;
  162. //
  163. // Enable AVI playing only when the resource is WIN95
  164. //
  165. gConfig.fEnableAviPlay = gConfig.fEnableAviPlay && gConfig.fIsWIN95;
  166. //
  167. // Detect game language only when no message file specified
  168. //
  169. if (!gConfig.pszMsgFile) PAL_SetCodePage(PAL_DetectCodePage("word.dat"));
  170. //
  171. // Set decompress function
  172. //
  173. Decompress = gConfig.fIsWIN95 ? YJ2_Decompress : YJ1_Decompress;
  174. gpGlobals->lpObjectDesc = gConfig.fIsWIN95 ? NULL : PAL_LoadObjectDesc(va("%s%s", gConfig.pszGamePath, "desc.dat"));
  175. gpGlobals->bCurrentSaveSlot = 1;
  176. return 0;
  177. }
  178. VOID
  179. PAL_FreeGlobals(
  180. VOID
  181. )
  182. /*++
  183. Purpose:
  184. Free global data.
  185. Parameters:
  186. None.
  187. Return value:
  188. None.
  189. --*/
  190. {
  191. //
  192. // Close all opened files
  193. //
  194. UTIL_CloseFile(gpGlobals->f.fpFBP);
  195. UTIL_CloseFile(gpGlobals->f.fpMGO);
  196. UTIL_CloseFile(gpGlobals->f.fpBALL);
  197. UTIL_CloseFile(gpGlobals->f.fpDATA);
  198. UTIL_CloseFile(gpGlobals->f.fpF);
  199. UTIL_CloseFile(gpGlobals->f.fpFIRE);
  200. UTIL_CloseFile(gpGlobals->f.fpRGM);
  201. UTIL_CloseFile(gpGlobals->f.fpSSS);
  202. //
  203. // Free the game data
  204. //
  205. free(gpGlobals->g.lprgEventObject);
  206. free(gpGlobals->g.lprgScriptEntry);
  207. free(gpGlobals->g.lprgStore);
  208. free(gpGlobals->g.lprgEnemy);
  209. free(gpGlobals->g.lprgEnemyTeam);
  210. free(gpGlobals->g.lprgMagic);
  211. free(gpGlobals->g.lprgBattleField);
  212. free(gpGlobals->g.lprgLevelUpMagic);
  213. //
  214. // Free the object description data
  215. //
  216. if (!gConfig.fIsWIN95)
  217. PAL_FreeObjectDesc(gpGlobals->lpObjectDesc);
  218. //
  219. // Clear the instance
  220. //
  221. memset(gpGlobals, 0, sizeof(GLOBALVARS));
  222. PAL_FreeConfig();
  223. }
  224. static VOID
  225. PAL_ReadGlobalGameData(
  226. VOID
  227. )
  228. /*++
  229. Purpose:
  230. Read global game data from data files.
  231. Parameters:
  232. None.
  233. Return value:
  234. None.
  235. --*/
  236. {
  237. const GAMEDATA *p = &gpGlobals->g;
  238. LOAD_DATA(p->lprgScriptEntry, p->nScriptEntry * sizeof(SCRIPTENTRY),
  239. 4, gpGlobals->f.fpSSS);
  240. LOAD_DATA(p->lprgStore, p->nStore * sizeof(STORE), 0, gpGlobals->f.fpDATA);
  241. LOAD_DATA(p->lprgEnemy, p->nEnemy * sizeof(ENEMY), 1, gpGlobals->f.fpDATA);
  242. LOAD_DATA(p->lprgEnemyTeam, p->nEnemyTeam * sizeof(ENEMYTEAM),
  243. 2, gpGlobals->f.fpDATA);
  244. LOAD_DATA(p->lprgMagic, p->nMagic * sizeof(MAGIC), 4, gpGlobals->f.fpDATA);
  245. LOAD_DATA(p->lprgBattleField, p->nBattleField * sizeof(BATTLEFIELD),
  246. 5, gpGlobals->f.fpDATA);
  247. LOAD_DATA(p->lprgLevelUpMagic, p->nLevelUpMagic * sizeof(LEVELUPMAGIC_ALL),
  248. 6, gpGlobals->f.fpDATA);
  249. LOAD_DATA(p->rgwBattleEffectIndex, sizeof(p->rgwBattleEffectIndex),
  250. 11, gpGlobals->f.fpDATA);
  251. PAL_MKFReadChunk((LPBYTE)&(p->EnemyPos), sizeof(p->EnemyPos),
  252. 13, gpGlobals->f.fpDATA);
  253. DO_BYTESWAP(&(p->EnemyPos), sizeof(p->EnemyPos));
  254. PAL_MKFReadChunk((LPBYTE)(p->rgLevelUpExp), sizeof(p->rgLevelUpExp),
  255. 14, gpGlobals->f.fpDATA);
  256. DO_BYTESWAP(p->rgLevelUpExp, sizeof(p->rgLevelUpExp));
  257. }
  258. static VOID
  259. PAL_InitGlobalGameData(
  260. VOID
  261. )
  262. /*++
  263. Purpose:
  264. Initialize global game data.
  265. Parameters:
  266. None.
  267. Return value:
  268. None.
  269. --*/
  270. {
  271. int len;
  272. #define PAL_DOALLOCATE(fp, num, type, lptype, ptr, n) \
  273. { \
  274. len = PAL_MKFGetChunkSize(num, fp); \
  275. ptr = (lptype)malloc(len); \
  276. n = len / sizeof(type); \
  277. if (ptr == NULL) \
  278. { \
  279. TerminateOnError("PAL_InitGlobalGameData(): Memory allocation error!"); \
  280. } \
  281. }
  282. //
  283. // If the memory has not been allocated, allocate first.
  284. //
  285. if (gpGlobals->g.lprgEventObject == NULL)
  286. {
  287. PAL_DOALLOCATE(gpGlobals->f.fpSSS, 0, EVENTOBJECT, LPEVENTOBJECT,
  288. gpGlobals->g.lprgEventObject, gpGlobals->g.nEventObject);
  289. PAL_DOALLOCATE(gpGlobals->f.fpSSS, 4, SCRIPTENTRY, LPSCRIPTENTRY,
  290. gpGlobals->g.lprgScriptEntry, gpGlobals->g.nScriptEntry);
  291. PAL_DOALLOCATE(gpGlobals->f.fpDATA, 0, STORE, LPSTORE,
  292. gpGlobals->g.lprgStore, gpGlobals->g.nStore);
  293. PAL_DOALLOCATE(gpGlobals->f.fpDATA, 1, ENEMY, LPENEMY,
  294. gpGlobals->g.lprgEnemy, gpGlobals->g.nEnemy);
  295. PAL_DOALLOCATE(gpGlobals->f.fpDATA, 2, ENEMYTEAM, LPENEMYTEAM,
  296. gpGlobals->g.lprgEnemyTeam, gpGlobals->g.nEnemyTeam);
  297. PAL_DOALLOCATE(gpGlobals->f.fpDATA, 4, MAGIC, LPMAGIC,
  298. gpGlobals->g.lprgMagic, gpGlobals->g.nMagic);
  299. PAL_DOALLOCATE(gpGlobals->f.fpDATA, 5, BATTLEFIELD, LPBATTLEFIELD,
  300. gpGlobals->g.lprgBattleField, gpGlobals->g.nBattleField);
  301. PAL_DOALLOCATE(gpGlobals->f.fpDATA, 6, LEVELUPMAGIC_ALL, LPLEVELUPMAGIC_ALL,
  302. gpGlobals->g.lprgLevelUpMagic, gpGlobals->g.nLevelUpMagic);
  303. PAL_ReadGlobalGameData();
  304. }
  305. #undef PAL_DOALLOCATE
  306. }
  307. static VOID
  308. PAL_LoadDefaultGame(
  309. VOID
  310. )
  311. /*++
  312. Purpose:
  313. Load the default game data.
  314. Parameters:
  315. None.
  316. Return value:
  317. None.
  318. --*/
  319. {
  320. GAMEDATA *p = &gpGlobals->g;
  321. UINT32 i;
  322. //
  323. // Load the default data from the game data files.
  324. //
  325. LOAD_DATA(p->lprgEventObject, p->nEventObject * sizeof(EVENTOBJECT),
  326. 0, gpGlobals->f.fpSSS);
  327. PAL_MKFReadChunk((LPBYTE)(p->rgScene), sizeof(p->rgScene), 1, gpGlobals->f.fpSSS);
  328. DO_BYTESWAP(p->rgScene, sizeof(p->rgScene));
  329. if (gConfig.fIsWIN95)
  330. {
  331. PAL_MKFReadChunk((LPBYTE)(p->rgObject), sizeof(p->rgObject), 2, gpGlobals->f.fpSSS);
  332. DO_BYTESWAP(p->rgObject, sizeof(p->rgObject));
  333. }
  334. else
  335. {
  336. OBJECT_DOS objects[MAX_OBJECTS];
  337. PAL_MKFReadChunk((LPBYTE)(objects), sizeof(objects), 2, gpGlobals->f.fpSSS);
  338. DO_BYTESWAP(objects, sizeof(objects));
  339. //
  340. // Convert the DOS-style data structure to WIN-style data structure
  341. //
  342. for (i = 0; i < MAX_OBJECTS; i++)
  343. {
  344. memcpy(&p->rgObject[i], &objects[i], sizeof(OBJECT_DOS));
  345. if (i >= OBJECT_ITEM_START && i <= OBJECT_MAGIC_END)
  346. {
  347. p->rgObject[i].rgwData[6] = objects[i].rgwData[5]; // wFlags
  348. p->rgObject[i].rgwData[5] = 0; // wScriptDesc or wReserved2
  349. }
  350. else
  351. {
  352. p->rgObject[i].rgwData[6] = 0;
  353. }
  354. }
  355. }
  356. PAL_MKFReadChunk((LPBYTE)(&(p->PlayerRoles)), sizeof(PLAYERROLES),
  357. 3, gpGlobals->f.fpDATA);
  358. DO_BYTESWAP(&(p->PlayerRoles), sizeof(PLAYERROLES));
  359. //
  360. // Set some other default data.
  361. //
  362. gpGlobals->dwCash = 0;
  363. gpGlobals->wNumMusic = 0;
  364. gpGlobals->wNumPalette = 0;
  365. gpGlobals->wNumScene = 1;
  366. gpGlobals->wCollectValue = 0;
  367. gpGlobals->fNightPalette = FALSE;
  368. gpGlobals->wMaxPartyMemberIndex = 0;
  369. gpGlobals->viewport = PAL_XY(0, 0);
  370. gpGlobals->wLayer = 0;
  371. gpGlobals->wChaseRange = 1;
  372. #ifndef PAL_CLASSIC
  373. gpGlobals->bBattleSpeed = 2;
  374. #endif
  375. memset(gpGlobals->rgInventory, 0, sizeof(gpGlobals->rgInventory));
  376. memset(gpGlobals->rgPoisonStatus, 0, sizeof(gpGlobals->rgPoisonStatus));
  377. memset(gpGlobals->rgParty, 0, sizeof(gpGlobals->rgParty));
  378. memset(gpGlobals->rgTrail, 0, sizeof(gpGlobals->rgTrail));
  379. memset(&(gpGlobals->Exp), 0, sizeof(gpGlobals->Exp));
  380. for (i = 0; i < MAX_PLAYER_ROLES; i++)
  381. {
  382. gpGlobals->Exp.rgPrimaryExp[i].wLevel = p->PlayerRoles.rgwLevel[i];
  383. gpGlobals->Exp.rgHealthExp[i].wLevel = p->PlayerRoles.rgwLevel[i];
  384. gpGlobals->Exp.rgMagicExp[i].wLevel = p->PlayerRoles.rgwLevel[i];
  385. gpGlobals->Exp.rgAttackExp[i].wLevel = p->PlayerRoles.rgwLevel[i];
  386. gpGlobals->Exp.rgMagicPowerExp[i].wLevel = p->PlayerRoles.rgwLevel[i];
  387. gpGlobals->Exp.rgDefenseExp[i].wLevel = p->PlayerRoles.rgwLevel[i];
  388. gpGlobals->Exp.rgDexterityExp[i].wLevel = p->PlayerRoles.rgwLevel[i];
  389. gpGlobals->Exp.rgFleeExp[i].wLevel = p->PlayerRoles.rgwLevel[i];
  390. }
  391. gpGlobals->fEnteringScene = TRUE;
  392. }
  393. typedef struct tagSAVEDGAME_COMMON
  394. {
  395. WORD wSavedTimes; // saved times
  396. WORD wViewportX, wViewportY; // viewport location
  397. WORD nPartyMember; // number of members in party
  398. WORD wNumScene; // scene number
  399. WORD wPaletteOffset;
  400. WORD wPartyDirection; // party direction
  401. WORD wNumMusic; // music number
  402. WORD wNumBattleMusic; // battle music number
  403. WORD wNumBattleField; // battle field number
  404. WORD wScreenWave; // level of screen waving
  405. WORD wBattleSpeed; // battle speed
  406. WORD wCollectValue; // value of "collected" items
  407. WORD wLayer;
  408. WORD wChaseRange;
  409. WORD wChasespeedChangeCycles;
  410. WORD nFollower;
  411. WORD rgwReserved2[3]; // unused
  412. DWORD dwCash; // amount of cash
  413. PARTY rgParty[MAX_PLAYABLE_PLAYER_ROLES]; // player party
  414. TRAIL rgTrail[MAX_PLAYABLE_PLAYER_ROLES]; // player trail
  415. ALLEXPERIENCE Exp; // experience data
  416. PLAYERROLES PlayerRoles;
  417. POISONSTATUS rgPoisonStatus[MAX_POISONS][MAX_PLAYABLE_PLAYER_ROLES]; // poison status
  418. INVENTORY rgInventory[MAX_INVENTORY]; // inventory status
  419. SCENE rgScene[MAX_SCENES];
  420. } SAVEDGAME_COMMON, *LPSAVEDGAME_COMMON;
  421. typedef struct tagSAVEDGAME_DOS
  422. {
  423. WORD wSavedTimes; // saved times
  424. WORD wViewportX, wViewportY; // viewport location
  425. WORD nPartyMember; // number of members in party
  426. WORD wNumScene; // scene number
  427. WORD wPaletteOffset;
  428. WORD wPartyDirection; // party direction
  429. WORD wNumMusic; // music number
  430. WORD wNumBattleMusic; // battle music number
  431. WORD wNumBattleField; // battle field number
  432. WORD wScreenWave; // level of screen waving
  433. WORD wBattleSpeed; // battle speed
  434. WORD wCollectValue; // value of "collected" items
  435. WORD wLayer;
  436. WORD wChaseRange;
  437. WORD wChasespeedChangeCycles;
  438. WORD nFollower;
  439. WORD rgwReserved2[3]; // unused
  440. DWORD dwCash; // amount of cash
  441. PARTY rgParty[MAX_PLAYABLE_PLAYER_ROLES]; // player party
  442. TRAIL rgTrail[MAX_PLAYABLE_PLAYER_ROLES]; // player trail
  443. ALLEXPERIENCE Exp; // experience data
  444. PLAYERROLES PlayerRoles;
  445. POISONSTATUS rgPoisonStatus[MAX_POISONS][MAX_PLAYABLE_PLAYER_ROLES]; // poison status
  446. INVENTORY rgInventory[MAX_INVENTORY]; // inventory status
  447. SCENE rgScene[MAX_SCENES];
  448. OBJECT_DOS rgObject[MAX_OBJECTS];
  449. EVENTOBJECT rgEventObject[MAX_EVENT_OBJECTS];
  450. } SAVEDGAME_DOS, *LPSAVEDGAME_DOS;
  451. typedef struct tagSAVEDGAME_WIN
  452. {
  453. WORD wSavedTimes; // saved times
  454. WORD wViewportX, wViewportY; // viewport location
  455. WORD nPartyMember; // number of members in party
  456. WORD wNumScene; // scene number
  457. WORD wPaletteOffset;
  458. WORD wPartyDirection; // party direction
  459. WORD wNumMusic; // music number
  460. WORD wNumBattleMusic; // battle music number
  461. WORD wNumBattleField; // battle field number
  462. WORD wScreenWave; // level of screen waving
  463. WORD wBattleSpeed; // battle speed
  464. WORD wCollectValue; // value of "collected" items
  465. WORD wLayer;
  466. WORD wChaseRange;
  467. WORD wChasespeedChangeCycles;
  468. WORD nFollower;
  469. WORD rgwReserved2[3]; // unused
  470. DWORD dwCash; // amount of cash
  471. PARTY rgParty[MAX_PLAYABLE_PLAYER_ROLES]; // player party
  472. TRAIL rgTrail[MAX_PLAYABLE_PLAYER_ROLES]; // player trail
  473. ALLEXPERIENCE Exp; // experience data
  474. PLAYERROLES PlayerRoles;
  475. POISONSTATUS rgPoisonStatus[MAX_POISONS][MAX_PLAYABLE_PLAYER_ROLES]; // poison status
  476. INVENTORY rgInventory[MAX_INVENTORY]; // inventory status
  477. SCENE rgScene[MAX_SCENES];
  478. OBJECT rgObject[MAX_OBJECTS];
  479. EVENTOBJECT rgEventObject[MAX_EVENT_OBJECTS];
  480. } SAVEDGAME_WIN, *LPSAVEDGAME_WIN;
  481. static BOOL
  482. PAL_LoadGame_Common(
  483. const char *szFileName,
  484. LPSAVEDGAME_COMMON s,
  485. size_t size
  486. )
  487. {
  488. //
  489. // Try to open the specified file
  490. //
  491. FILE *fp = UTIL_OpenRequiredFileForMode(szFileName, "rb");
  492. //
  493. // Read all data from the file and close.
  494. //
  495. size_t n = fp ? fread(s, 1, size, fp) : 0;
  496. if (fp != NULL)
  497. {
  498. fclose(fp);
  499. }
  500. if (n < size - sizeof(EVENTOBJECT) * MAX_EVENT_OBJECTS)
  501. {
  502. return FALSE;
  503. }
  504. //
  505. // Adjust endianness
  506. //
  507. DO_BYTESWAP(&s, size);
  508. //
  509. // Cash amount is in DWORD, so do a wordswap in Big-Endian.
  510. //
  511. #if SDL_BYTEORDER == SDL_BIG_ENDIAN
  512. s->dwCash = ((s->dwCash >> 16) | (s->dwCash << 16));
  513. #endif
  514. //
  515. // Get common data from the saved game struct.
  516. //
  517. gpGlobals->viewport = PAL_XY(s->wViewportX, s->wViewportY);
  518. gpGlobals->wMaxPartyMemberIndex = s->nPartyMember;
  519. gpGlobals->wNumScene = s->wNumScene;
  520. gpGlobals->fNightPalette = (s->wPaletteOffset != 0);
  521. gpGlobals->wPartyDirection = s->wPartyDirection;
  522. gpGlobals->wNumMusic = s->wNumMusic;
  523. gpGlobals->wNumBattleMusic = s->wNumBattleMusic;
  524. gpGlobals->wNumBattleField = s->wNumBattleField;
  525. gpGlobals->wScreenWave = s->wScreenWave;
  526. gpGlobals->sWaveProgression = 0;
  527. gpGlobals->wCollectValue = s->wCollectValue;
  528. gpGlobals->wLayer = s->wLayer;
  529. gpGlobals->wChaseRange = s->wChaseRange;
  530. gpGlobals->wChasespeedChangeCycles = s->wChasespeedChangeCycles;
  531. gpGlobals->nFollower = s->nFollower;
  532. gpGlobals->dwCash = s->dwCash;
  533. #ifndef PAL_CLASSIC
  534. gpGlobals->bBattleSpeed = s->wBattleSpeed;
  535. if (gpGlobals->bBattleSpeed > 5 || gpGlobals->bBattleSpeed == 0)
  536. {
  537. gpGlobals->bBattleSpeed = 2;
  538. }
  539. #endif
  540. memcpy(gpGlobals->rgParty, s->rgParty, sizeof(gpGlobals->rgParty));
  541. memcpy(gpGlobals->rgTrail, s->rgTrail, sizeof(gpGlobals->rgTrail));
  542. gpGlobals->Exp = s->Exp;
  543. gpGlobals->g.PlayerRoles = s->PlayerRoles;
  544. memset(gpGlobals->rgPoisonStatus, 0, sizeof(gpGlobals->rgPoisonStatus));
  545. memcpy(gpGlobals->rgInventory, s->rgInventory, sizeof(gpGlobals->rgInventory));
  546. memcpy(gpGlobals->g.rgScene, s->rgScene, sizeof(gpGlobals->g.rgScene));
  547. gpGlobals->fEnteringScene = FALSE;
  548. PAL_CompressInventory();
  549. return TRUE;
  550. }
  551. static INT
  552. PAL_LoadGame_DOS(
  553. LPCSTR szFileName
  554. )
  555. /*++
  556. Purpose:
  557. Load a saved game.
  558. Parameters:
  559. [IN] szFileName - file name of saved game.
  560. Return value:
  561. 0 if success, -1 if failed.
  562. --*/
  563. {
  564. PAL_LARGE SAVEDGAME_DOS s;
  565. int i;
  566. //
  567. // Get all the data from the saved game struct.
  568. //
  569. if (!PAL_LoadGame_Common(szFileName, (LPSAVEDGAME_COMMON)&s, sizeof(SAVEDGAME_DOS)))
  570. return -1;
  571. //
  572. // Convert the DOS-style data structure to WIN-style data structure
  573. //
  574. for (i = 0; i < MAX_OBJECTS; i++)
  575. {
  576. memcpy(&gpGlobals->g.rgObject[i], &s.rgObject[i], sizeof(OBJECT_DOS));
  577. if (i >= OBJECT_ITEM_START && i <= OBJECT_MAGIC_END)
  578. {
  579. gpGlobals->g.rgObject[i].rgwData[6] = s.rgObject[i].rgwData[5]; // wFlags
  580. gpGlobals->g.rgObject[i].rgwData[5] = 0; // wScriptDesc or wReserved2
  581. }
  582. else
  583. {
  584. gpGlobals->g.rgObject[i].rgwData[6] = 0;
  585. }
  586. }
  587. memcpy(gpGlobals->g.lprgEventObject, s.rgEventObject, sizeof(EVENTOBJECT) * gpGlobals->g.nEventObject);
  588. //
  589. // Success
  590. //
  591. return 0;
  592. }
  593. static INT
  594. PAL_LoadGame_WIN(
  595. LPCSTR szFileName
  596. )
  597. /*++
  598. Purpose:
  599. Load a saved game.
  600. Parameters:
  601. [IN] szFileName - file name of saved game.
  602. Return value:
  603. 0 if success, -1 if failed.
  604. --*/
  605. {
  606. PAL_LARGE SAVEDGAME_WIN s;
  607. //
  608. // Get all the data from the saved game struct.
  609. //
  610. if (!PAL_LoadGame_Common(szFileName, (LPSAVEDGAME_COMMON)&s, sizeof(SAVEDGAME_WIN)))
  611. return -1;
  612. memcpy(gpGlobals->g.rgObject, s.rgObject, sizeof(gpGlobals->g.rgObject));
  613. memcpy(gpGlobals->g.lprgEventObject, s.rgEventObject, sizeof(EVENTOBJECT) * gpGlobals->g.nEventObject);
  614. //
  615. // Success
  616. //
  617. return 0;
  618. }
  619. static INT
  620. PAL_LoadGame(
  621. LPCSTR szFileName
  622. )
  623. {
  624. return gConfig.fIsWIN95 ? PAL_LoadGame_WIN(szFileName) : PAL_LoadGame_DOS(szFileName);
  625. }
  626. static VOID
  627. PAL_SaveGame_Common(
  628. LPCSTR szFileName,
  629. WORD wSavedTimes,
  630. LPSAVEDGAME_COMMON s,
  631. size_t size
  632. )
  633. {
  634. FILE *fp;
  635. int i;
  636. s->wSavedTimes = wSavedTimes;
  637. s->wViewportX = PAL_X(gpGlobals->viewport);
  638. s->wViewportY = PAL_Y(gpGlobals->viewport);
  639. s->nPartyMember = gpGlobals->wMaxPartyMemberIndex;
  640. s->wNumScene = gpGlobals->wNumScene;
  641. s->wPaletteOffset = (gpGlobals->fNightPalette ? 0x180 : 0);
  642. s->wPartyDirection = gpGlobals->wPartyDirection;
  643. s->wNumMusic = gpGlobals->wNumMusic;
  644. s->wNumBattleMusic = gpGlobals->wNumBattleMusic;
  645. s->wNumBattleField = gpGlobals->wNumBattleField;
  646. s->wScreenWave = gpGlobals->wScreenWave;
  647. s->wCollectValue = gpGlobals->wCollectValue;
  648. s->wLayer = gpGlobals->wLayer;
  649. s->wChaseRange = gpGlobals->wChaseRange;
  650. s->wChasespeedChangeCycles = gpGlobals->wChasespeedChangeCycles;
  651. s->nFollower = gpGlobals->nFollower;
  652. s->dwCash = gpGlobals->dwCash;
  653. #ifndef PAL_CLASSIC
  654. s->wBattleSpeed = gpGlobals->bBattleSpeed;
  655. #else
  656. s->wBattleSpeed = 2;
  657. #endif
  658. memcpy(s->rgParty, gpGlobals->rgParty, sizeof(gpGlobals->rgParty));
  659. memcpy(s->rgTrail, gpGlobals->rgTrail, sizeof(gpGlobals->rgTrail));
  660. s->Exp = gpGlobals->Exp;
  661. s->PlayerRoles = gpGlobals->g.PlayerRoles;
  662. memcpy(s->rgPoisonStatus, gpGlobals->rgPoisonStatus, sizeof(gpGlobals->rgPoisonStatus));
  663. memcpy(s->rgInventory, gpGlobals->rgInventory, sizeof(gpGlobals->rgInventory));
  664. memcpy(s->rgScene, gpGlobals->g.rgScene, sizeof(gpGlobals->g.rgScene));
  665. //
  666. // Adjust endianness
  667. //
  668. DO_BYTESWAP(&s, size);
  669. //
  670. // Cash amount is in DWORD, so do a wordswap in Big-Endian.
  671. //
  672. #if SDL_BYTEORDER == SDL_BIG_ENDIAN
  673. s->dwCash = ((s->dwCash >> 16) | (s->dwCash << 16));
  674. #endif
  675. //
  676. // Try writing to file
  677. //
  678. if ((fp = fopen(szFileName, "wb")) == NULL)
  679. {
  680. return;
  681. }
  682. i = PAL_MKFGetChunkSize(0, gpGlobals->f.fpSSS);
  683. i += size - sizeof(EVENTOBJECT) * MAX_EVENT_OBJECTS;
  684. fwrite(s, i, 1, fp);
  685. fclose(fp);
  686. }
  687. static VOID
  688. PAL_SaveGame_DOS(
  689. LPCSTR szFileName,
  690. WORD wSavedTimes
  691. )
  692. /*++
  693. Purpose:
  694. Save the current game state to file.
  695. Parameters:
  696. [IN] szFileName - file name of saved game.
  697. Return value:
  698. None.
  699. --*/
  700. {
  701. PAL_LARGE SAVEDGAME_DOS s;
  702. UINT32 i;
  703. //
  704. // Convert the WIN-style data structure to DOS-style data structure
  705. //
  706. for (i = 0; i < MAX_OBJECTS; i++)
  707. {
  708. memcpy(&s.rgObject[i], &gpGlobals->g.rgObject[i], sizeof(OBJECT_DOS));
  709. if (i >= OBJECT_ITEM_START && i <= OBJECT_MAGIC_END)
  710. {
  711. s.rgObject[i].rgwData[5] = gpGlobals->g.rgObject[i].rgwData[6]; // wFlags
  712. }
  713. }
  714. memcpy(s.rgEventObject, gpGlobals->g.lprgEventObject, sizeof(EVENTOBJECT) * gpGlobals->g.nEventObject);
  715. //
  716. // Put all the data to the saved game struct.
  717. //
  718. PAL_SaveGame_Common(szFileName, wSavedTimes, (LPSAVEDGAME_COMMON)&s, sizeof(SAVEDGAME_DOS));
  719. }
  720. static VOID
  721. PAL_SaveGame_WIN(
  722. LPCSTR szFileName,
  723. WORD wSavedTimes
  724. )
  725. /*++
  726. Purpose:
  727. Save the current game state to file.
  728. Parameters:
  729. [IN] szFileName - file name of saved game.
  730. Return value:
  731. None.
  732. --*/
  733. {
  734. PAL_LARGE SAVEDGAME_WIN s;
  735. //
  736. // Put all the data to the saved game struct.
  737. //
  738. memcpy(s.rgObject, gpGlobals->g.rgObject, sizeof(gpGlobals->g.rgObject));
  739. memcpy(s.rgEventObject, gpGlobals->g.lprgEventObject, sizeof(EVENTOBJECT) * gpGlobals->g.nEventObject);
  740. PAL_SaveGame_Common(szFileName, wSavedTimes, (LPSAVEDGAME_COMMON)&s, sizeof(SAVEDGAME_WIN));
  741. }
  742. VOID
  743. PAL_SaveGame(
  744. LPCSTR szFileName,
  745. WORD wSavedTimes
  746. )
  747. {
  748. if (gConfig.fIsWIN95)
  749. PAL_SaveGame_WIN(szFileName, wSavedTimes);
  750. else
  751. PAL_SaveGame_DOS(szFileName, wSavedTimes);
  752. }
  753. VOID
  754. PAL_InitGameData(
  755. INT iSaveSlot
  756. )
  757. /*++
  758. Purpose:
  759. Initialize the game data (used when starting a new game or loading a saved game).
  760. Parameters:
  761. [IN] iSaveSlot - Slot of saved game.
  762. Return value:
  763. None.
  764. --*/
  765. {
  766. PAL_InitGlobalGameData();
  767. gpGlobals->bCurrentSaveSlot = (BYTE)iSaveSlot;
  768. //
  769. // try loading from the saved game file.
  770. //
  771. char saveFileName[PAL_MAX_PATH];
  772. snprintf(saveFileName, PAL_MAX_PATH, "%d%s", iSaveSlot, ".rpg");
  773. if (iSaveSlot == 0 || PAL_LoadGame(saveFileName) != 0)
  774. {
  775. //
  776. // Cannot load the saved game file. Load the defaults.
  777. //
  778. PAL_LoadDefaultGame();
  779. }
  780. gpGlobals->fGameStart = TRUE;
  781. gpGlobals->fNeedToFadeIn = FALSE;
  782. gpGlobals->iCurInvMenuItem = 0;
  783. gpGlobals->fInBattle = FALSE;
  784. memset(gpGlobals->rgPlayerStatus, 0, sizeof(gpGlobals->rgPlayerStatus));
  785. PAL_UpdateEquipments();
  786. }
  787. BOOL
  788. PAL_AddItemToInventory(
  789. WORD wObjectID,
  790. INT iNum
  791. )
  792. /*++
  793. Purpose:
  794. Add or remove the specified kind of item in the inventory.
  795. Parameters:
  796. [IN] wObjectID - object number of the item.
  797. [IN] iNum - number to be added (positive value) or removed (negative value).
  798. Return value:
  799. TRUE if succeeded, FALSE if failed.
  800. --*/
  801. {
  802. int index;
  803. BOOL fFound;
  804. if (wObjectID == 0)
  805. {
  806. return FALSE;
  807. }
  808. if (iNum == 0)
  809. {
  810. iNum = 1;
  811. }
  812. index = 0;
  813. fFound = FALSE;
  814. //
  815. // Search for the specified item in the inventory
  816. //
  817. while (index < MAX_INVENTORY)
  818. {
  819. if (gpGlobals->rgInventory[index].wItem == wObjectID)
  820. {
  821. fFound = TRUE;
  822. break;
  823. }
  824. else if (gpGlobals->rgInventory[index].wItem == 0)
  825. {
  826. break;
  827. }
  828. index++;
  829. }
  830. if (iNum > 0)
  831. {
  832. //
  833. // Add item
  834. //
  835. if (index >= MAX_INVENTORY)
  836. {
  837. //
  838. // inventory is full. cannot add item
  839. //
  840. return FALSE;
  841. }
  842. if (fFound)
  843. {
  844. gpGlobals->rgInventory[index].nAmount += iNum;
  845. if (gpGlobals->rgInventory[index].nAmount > 99)
  846. {
  847. //
  848. // Maximum number is 99
  849. //
  850. gpGlobals->rgInventory[index].nAmount = 99;
  851. }
  852. }
  853. else
  854. {
  855. gpGlobals->rgInventory[index].wItem = wObjectID;
  856. if (iNum > 99)
  857. {
  858. iNum = 99;
  859. }
  860. gpGlobals->rgInventory[index].nAmount = iNum;
  861. }
  862. return TRUE;
  863. }
  864. else
  865. {
  866. //
  867. // Remove item
  868. //
  869. if (fFound)
  870. {
  871. iNum *= -1;
  872. if (gpGlobals->rgInventory[index].nAmount < iNum)
  873. {
  874. //
  875. // This item has been run out
  876. //
  877. gpGlobals->rgInventory[index].nAmount = 0;
  878. return FALSE;
  879. }
  880. gpGlobals->rgInventory[index].nAmount -= iNum;
  881. return TRUE;
  882. }
  883. return FALSE;
  884. }
  885. }
  886. INT
  887. PAL_GetItemAmount(
  888. WORD wItem
  889. )
  890. /*++
  891. Purpose:
  892. Get the amount of the specified item in the inventory.
  893. Parameters:
  894. [IN] wItem - the object ID of the item.
  895. Return value:
  896. The amount of the item in the inventory.
  897. --*/
  898. {
  899. int i;
  900. for (i = 0; i < MAX_INVENTORY; i++)
  901. {
  902. if (gpGlobals->rgInventory[i].wItem == 0)
  903. {
  904. break;
  905. }
  906. if (gpGlobals->rgInventory[i].wItem == wItem)
  907. {
  908. return gpGlobals->rgInventory[i].nAmount;
  909. }
  910. }
  911. return 0;
  912. }
  913. VOID
  914. PAL_CompressInventory(
  915. VOID
  916. )
  917. /*++
  918. Purpose:
  919. Remove all the items in inventory which has a number of zero.
  920. Parameters:
  921. None.
  922. Return value:
  923. None.
  924. --*/
  925. {
  926. int i, j;
  927. j = 0;
  928. for (i = 0; i < MAX_INVENTORY; i++)
  929. {
  930. if (gpGlobals->rgInventory[i].wItem == 0)
  931. {
  932. break;
  933. }
  934. if (gpGlobals->rgInventory[i].nAmount > 0)
  935. {
  936. gpGlobals->rgInventory[j] = gpGlobals->rgInventory[i];
  937. j++;
  938. }
  939. }
  940. for (; j < MAX_INVENTORY; j++)
  941. {
  942. gpGlobals->rgInventory[j].nAmount = 0;
  943. gpGlobals->rgInventory[j].nAmountInUse = 0;
  944. gpGlobals->rgInventory[j].wItem = 0;
  945. }
  946. }
  947. BOOL
  948. PAL_IncreaseHPMP(
  949. WORD wPlayerRole,
  950. SHORT sHP,
  951. SHORT sMP
  952. )
  953. /*++
  954. Purpose:
  955. Increase or decrease player's HP and/or MP.
  956. Parameters:
  957. [IN] wPlayerRole - the number of player role.
  958. [IN] sHP - number of HP to be increased (positive value) or decrased
  959. (negative value).
  960. [IN] sMP - number of MP to be increased (positive value) or decrased
  961. (negative value).
  962. Return value:
  963. TRUE if the operation is succeeded, FALSE if not.
  964. --*/
  965. {
  966. BOOL fSuccess = FALSE;
  967. //
  968. // Only care about alive players
  969. //
  970. if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] > 0)
  971. {
  972. //
  973. // change HP
  974. //
  975. gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] += sHP;
  976. if ((SHORT)(gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole]) < 0)
  977. {
  978. gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] = 0;
  979. }
  980. else if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] >
  981. gpGlobals->g.PlayerRoles.rgwMaxHP[wPlayerRole])
  982. {
  983. gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] =
  984. gpGlobals->g.PlayerRoles.rgwMaxHP[wPlayerRole];
  985. }
  986. //
  987. // Change MP
  988. //
  989. gpGlobals->g.PlayerRoles.rgwMP[wPlayerRole] += sMP;
  990. if ((SHORT)(gpGlobals->g.PlayerRoles.rgwMP[wPlayerRole]) < 0)
  991. {
  992. gpGlobals->g.PlayerRoles.rgwMP[wPlayerRole] = 0;
  993. }
  994. else if (gpGlobals->g.PlayerRoles.rgwMP[wPlayerRole] >
  995. gpGlobals->g.PlayerRoles.rgwMaxMP[wPlayerRole])
  996. {
  997. gpGlobals->g.PlayerRoles.rgwMP[wPlayerRole] =
  998. gpGlobals->g.PlayerRoles.rgwMaxMP[wPlayerRole];
  999. }
  1000. fSuccess = TRUE;
  1001. }
  1002. return fSuccess;
  1003. }
  1004. VOID
  1005. PAL_UpdateEquipments(
  1006. VOID
  1007. )
  1008. /*++
  1009. Purpose:
  1010. Update the effects of all equipped items for all players.
  1011. Parameters:
  1012. None.
  1013. Return value:
  1014. None.
  1015. --*/
  1016. {
  1017. int i, j;
  1018. WORD w;
  1019. memset(&(gpGlobals->rgEquipmentEffect), 0, sizeof(gpGlobals->rgEquipmentEffect));
  1020. for (i = 0; i < MAX_PLAYER_ROLES; i++)
  1021. {
  1022. for (j = 0; j < MAX_PLAYER_EQUIPMENTS; j++)
  1023. {
  1024. w = gpGlobals->g.PlayerRoles.rgwEquipment[j][i];
  1025. if (w != 0)
  1026. {
  1027. gpGlobals->g.rgObject[w].item.wScriptOnEquip =
  1028. PAL_RunTriggerScript(gpGlobals->g.rgObject[w].item.wScriptOnEquip, (WORD)i);
  1029. }
  1030. }
  1031. }
  1032. }
  1033. VOID
  1034. PAL_RemoveEquipmentEffect(
  1035. WORD wPlayerRole,
  1036. WORD wEquipPart
  1037. )
  1038. /*++
  1039. Purpose:
  1040. Remove all the effects of the equipment for the player.
  1041. Parameters:
  1042. [IN] wPlayerRole - the player role.
  1043. [IN] wEquipPart - the part of the equipment.
  1044. Return value:
  1045. None.
  1046. --*/
  1047. {
  1048. WORD *p;
  1049. int i, j;
  1050. p = (WORD *)(&gpGlobals->rgEquipmentEffect[wEquipPart]); // HACKHACK
  1051. for (i = 0; i < sizeof(PLAYERROLES) / sizeof(PLAYERS); i++)
  1052. {
  1053. p[i * MAX_PLAYER_ROLES + wPlayerRole] = 0;
  1054. }
  1055. //
  1056. // Reset some parameters to default when appropriate
  1057. //
  1058. if (wEquipPart == kBodyPartHand)
  1059. {
  1060. //
  1061. // reset the dual attack status
  1062. //
  1063. gpGlobals->rgPlayerStatus[wPlayerRole][kStatusDualAttack] = 0;
  1064. }
  1065. else if (wEquipPart == kBodyPartWear)
  1066. {
  1067. //
  1068. // Remove all poisons leveled 99
  1069. //
  1070. for (i = 0; i <= (short)gpGlobals->wMaxPartyMemberIndex; i++)
  1071. {
  1072. if (gpGlobals->rgParty[i].wPlayerRole == wPlayerRole)
  1073. {
  1074. wPlayerRole = i;
  1075. break;
  1076. }
  1077. }
  1078. if (i <= (short)gpGlobals->wMaxPartyMemberIndex)
  1079. {
  1080. j = 0;
  1081. for (i = 0; i < MAX_POISONS; i++)
  1082. {
  1083. WORD w = gpGlobals->rgPoisonStatus[i][wPlayerRole].wPoisonID;
  1084. if (w == 0)
  1085. {
  1086. break;
  1087. }
  1088. if (gpGlobals->g.rgObject[w].poison.wPoisonLevel < 99)
  1089. {
  1090. gpGlobals->rgPoisonStatus[j][wPlayerRole] =
  1091. gpGlobals->rgPoisonStatus[i][wPlayerRole];
  1092. j++;
  1093. }
  1094. }
  1095. while (j < MAX_POISONS)
  1096. {
  1097. gpGlobals->rgPoisonStatus[j][wPlayerRole].wPoisonID = 0;
  1098. gpGlobals->rgPoisonStatus[j][wPlayerRole].wPoisonScript = 0;
  1099. j++;
  1100. }
  1101. }
  1102. }
  1103. }
  1104. VOID
  1105. PAL_AddPoisonForPlayer(
  1106. WORD wPlayerRole,
  1107. WORD wPoisonID
  1108. )
  1109. /*++
  1110. Purpose:
  1111. Add the specified poison to the player.
  1112. Parameters:
  1113. [IN] wPlayerRole - the player role ID.
  1114. [IN] wPoisonID - the poison to be added.
  1115. Return value:
  1116. None.
  1117. --*/
  1118. {
  1119. int i, index;
  1120. WORD w;
  1121. for (index = 0; index <= gpGlobals->wMaxPartyMemberIndex; index++)
  1122. {
  1123. if (gpGlobals->rgParty[index].wPlayerRole == wPlayerRole)
  1124. {
  1125. break;
  1126. }
  1127. }
  1128. if (index > gpGlobals->wMaxPartyMemberIndex)
  1129. {
  1130. return; // don't go further
  1131. }
  1132. for (i = 0; i < MAX_POISONS; i++)
  1133. {
  1134. w = gpGlobals->rgPoisonStatus[i][index].wPoisonID;
  1135. if (w == 0)
  1136. {
  1137. break;
  1138. }
  1139. if (w == wPoisonID)
  1140. {
  1141. return; // already poisoned
  1142. }
  1143. }
  1144. if (i < MAX_POISONS)
  1145. {
  1146. gpGlobals->rgPoisonStatus[i][index].wPoisonID = wPoisonID;
  1147. gpGlobals->rgPoisonStatus[i][index].wPoisonScript =
  1148. gpGlobals->g.rgObject[wPoisonID].poison.wPlayerScript;
  1149. }
  1150. }
  1151. VOID
  1152. PAL_CurePoisonByKind(
  1153. WORD wPlayerRole,
  1154. WORD wPoisonID
  1155. )
  1156. /*++
  1157. Purpose:
  1158. Remove the specified poison from the player.
  1159. Parameters:
  1160. [IN] wPlayerRole - the player role ID.
  1161. [IN] wPoisonID - the poison to be removed.
  1162. Return value:
  1163. None.
  1164. --*/
  1165. {
  1166. int i, index;
  1167. for (index = 0; index <= gpGlobals->wMaxPartyMemberIndex; index++)
  1168. {
  1169. if (gpGlobals->rgParty[index].wPlayerRole == wPlayerRole)
  1170. {
  1171. break;
  1172. }
  1173. }
  1174. if (index > gpGlobals->wMaxPartyMemberIndex)
  1175. {
  1176. return; // don't go further
  1177. }
  1178. for (i = 0; i < MAX_POISONS; i++)
  1179. {
  1180. if (gpGlobals->rgPoisonStatus[i][index].wPoisonID == wPoisonID)
  1181. {
  1182. gpGlobals->rgPoisonStatus[i][index].wPoisonID = 0;
  1183. gpGlobals->rgPoisonStatus[i][index].wPoisonScript = 0;
  1184. }
  1185. }
  1186. }
  1187. VOID
  1188. PAL_CurePoisonByLevel(
  1189. WORD wPlayerRole,
  1190. WORD wMaxLevel
  1191. )
  1192. /*++
  1193. Purpose:
  1194. Remove the poisons which have a maximum level of wMaxLevel from the player.
  1195. Parameters:
  1196. [IN] wPlayerRole - the player role ID.
  1197. [IN] wMaxLevel - the maximum level of poisons to be removed.
  1198. Return value:
  1199. None.
  1200. --*/
  1201. {
  1202. int i, index;
  1203. WORD w;
  1204. for (index = 0; index <= gpGlobals->wMaxPartyMemberIndex; index++)
  1205. {
  1206. if (gpGlobals->rgParty[index].wPlayerRole == wPlayerRole)
  1207. {
  1208. break;
  1209. }
  1210. }
  1211. if (index > gpGlobals->wMaxPartyMemberIndex)
  1212. {
  1213. return; // don't go further
  1214. }
  1215. for (i = 0; i < MAX_POISONS; i++)
  1216. {
  1217. w = gpGlobals->rgPoisonStatus[i][index].wPoisonID;
  1218. if (gpGlobals->g.rgObject[w].poison.wPoisonLevel <= wMaxLevel)
  1219. {
  1220. gpGlobals->rgPoisonStatus[i][index].wPoisonID = 0;
  1221. gpGlobals->rgPoisonStatus[i][index].wPoisonScript = 0;
  1222. }
  1223. }
  1224. }
  1225. BOOL
  1226. PAL_IsPlayerPoisonedByLevel(
  1227. WORD wPlayerRole,
  1228. WORD wMinLevel
  1229. )
  1230. /*++
  1231. Purpose:
  1232. Check if the player is poisoned by poisons at a minimum level of wMinLevel.
  1233. Parameters:
  1234. [IN] wPlayerRole - the player role ID.
  1235. [IN] wMinLevel - the minimum level of poison.
  1236. Return value:
  1237. TRUE if the player is poisoned by poisons at a minimum level of wMinLevel;
  1238. FALSE if not.
  1239. --*/
  1240. {
  1241. int i, index;
  1242. WORD w;
  1243. for (index = 0; index <= gpGlobals->wMaxPartyMemberIndex; index++)
  1244. {
  1245. if (gpGlobals->rgParty[index].wPlayerRole == wPlayerRole)
  1246. {
  1247. break;
  1248. }
  1249. }
  1250. if (index > gpGlobals->wMaxPartyMemberIndex)
  1251. {
  1252. return FALSE; // don't go further
  1253. }
  1254. for (i = 0; i < MAX_POISONS; i++)
  1255. {
  1256. w = gpGlobals->rgPoisonStatus[i][index].wPoisonID;
  1257. w = gpGlobals->g.rgObject[w].poison.wPoisonLevel;
  1258. if (w >= 99)
  1259. {
  1260. //
  1261. // Ignore poisons which has a level of 99 (usually effect of equipment)
  1262. //
  1263. continue;
  1264. }
  1265. if (w >= wMinLevel)
  1266. {
  1267. return TRUE;
  1268. }
  1269. }
  1270. return FALSE;
  1271. }
  1272. BOOL
  1273. PAL_IsPlayerPoisonedByKind(
  1274. WORD wPlayerRole,
  1275. WORD wPoisonID
  1276. )
  1277. /*++
  1278. Purpose:
  1279. Check if the player is poisoned by the specified poison.
  1280. Parameters:
  1281. [IN] wPlayerRole - the player role ID.
  1282. [IN] wPoisonID - the poison to be checked.
  1283. Return value:
  1284. TRUE if player is poisoned by the specified poison;
  1285. FALSE if not.
  1286. --*/
  1287. {
  1288. int i, index;
  1289. for (index = 0; index <= gpGlobals->wMaxPartyMemberIndex; index++)
  1290. {
  1291. if (gpGlobals->rgParty[index].wPlayerRole == wPlayerRole)
  1292. {
  1293. break;
  1294. }
  1295. }
  1296. if (index > gpGlobals->wMaxPartyMemberIndex)
  1297. {
  1298. return FALSE; // don't go further
  1299. }
  1300. for (i = 0; i < MAX_POISONS; i++)
  1301. {
  1302. if (gpGlobals->rgPoisonStatus[i][index].wPoisonID == wPoisonID)
  1303. {
  1304. return TRUE;
  1305. }
  1306. }
  1307. return FALSE;
  1308. }
  1309. WORD
  1310. PAL_GetPlayerAttackStrength(
  1311. WORD wPlayerRole
  1312. )
  1313. /*++
  1314. Purpose:
  1315. Get the player's attack strength, count in the effect of equipments.
  1316. Parameters:
  1317. [IN] wPlayerRole - the player role ID.
  1318. Return value:
  1319. The total attack strength of the player.
  1320. --*/
  1321. {
  1322. WORD w;
  1323. int i;
  1324. w = gpGlobals->g.PlayerRoles.rgwAttackStrength[wPlayerRole];
  1325. for (i = 0; i <= MAX_PLAYER_EQUIPMENTS; i++)
  1326. {
  1327. w += gpGlobals->rgEquipmentEffect[i].rgwAttackStrength[wPlayerRole];
  1328. }
  1329. return w;
  1330. }
  1331. WORD
  1332. PAL_GetPlayerMagicStrength(
  1333. WORD wPlayerRole
  1334. )
  1335. /*++
  1336. Purpose:
  1337. Get the player's magic strength, count in the effect of equipments.
  1338. Parameters:
  1339. [IN] wPlayerRole - the player role ID.
  1340. Return value:
  1341. The total magic strength of the player.
  1342. --*/
  1343. {
  1344. WORD w;
  1345. int i;
  1346. w = gpGlobals->g.PlayerRoles.rgwMagicStrength[wPlayerRole];
  1347. for (i = 0; i <= MAX_PLAYER_EQUIPMENTS; i++)
  1348. {
  1349. w += gpGlobals->rgEquipmentEffect[i].rgwMagicStrength[wPlayerRole];
  1350. }
  1351. return w;
  1352. }
  1353. WORD
  1354. PAL_GetPlayerDefense(
  1355. WORD wPlayerRole
  1356. )
  1357. /*++
  1358. Purpose:
  1359. Get the player's defense value, count in the effect of equipments.
  1360. Parameters:
  1361. [IN] wPlayerRole - the player role ID.
  1362. Return value:
  1363. The total defense value of the player.
  1364. --*/
  1365. {
  1366. WORD w;
  1367. int i;
  1368. w = gpGlobals->g.PlayerRoles.rgwDefense[wPlayerRole];
  1369. for (i = 0; i <= MAX_PLAYER_EQUIPMENTS; i++)
  1370. {
  1371. w += gpGlobals->rgEquipmentEffect[i].rgwDefense[wPlayerRole];
  1372. }
  1373. return w;
  1374. }
  1375. WORD
  1376. PAL_GetPlayerDexterity(
  1377. WORD wPlayerRole
  1378. )
  1379. /*++
  1380. Purpose:
  1381. Get the player's dexterity, count in the effect of equipments.
  1382. Parameters:
  1383. [IN] wPlayerRole - the player role ID.
  1384. Return value:
  1385. The total dexterity of the player.
  1386. --*/
  1387. {
  1388. WORD w;
  1389. int i;
  1390. w = gpGlobals->g.PlayerRoles.rgwDexterity[wPlayerRole];
  1391. #ifdef PAL_CLASSIC
  1392. for (i = 0; i <= MAX_PLAYER_EQUIPMENTS; i++)
  1393. #else
  1394. for (i = 0; i <= MAX_PLAYER_EQUIPMENTS - 1; i++)
  1395. #endif
  1396. {
  1397. w += gpGlobals->rgEquipmentEffect[i].rgwDexterity[wPlayerRole];
  1398. }
  1399. return w;
  1400. }
  1401. WORD
  1402. PAL_GetPlayerFleeRate(
  1403. WORD wPlayerRole
  1404. )
  1405. /*++
  1406. Purpose:
  1407. Get the player's flee rate, count in the effect of equipments.
  1408. Parameters:
  1409. [IN] wPlayerRole - the player role ID.
  1410. Return value:
  1411. The total flee rate of the player.
  1412. --*/
  1413. {
  1414. WORD w;
  1415. int i;
  1416. w = gpGlobals->g.PlayerRoles.rgwFleeRate[wPlayerRole];
  1417. for (i = 0; i <= MAX_PLAYER_EQUIPMENTS; i++)
  1418. {
  1419. w += gpGlobals->rgEquipmentEffect[i].rgwFleeRate[wPlayerRole];
  1420. }
  1421. return w;
  1422. }
  1423. WORD
  1424. PAL_GetPlayerPoisonResistance(
  1425. WORD wPlayerRole
  1426. )
  1427. /*++
  1428. Purpose:
  1429. Get the player's resistance to poisons, count in the effect of equipments.
  1430. Parameters:
  1431. [IN] wPlayerRole - the player role ID.
  1432. Return value:
  1433. The total resistance to poisons of the player.
  1434. --*/
  1435. {
  1436. WORD w;
  1437. int i;
  1438. w = gpGlobals->g.PlayerRoles.rgwPoisonResistance[wPlayerRole];
  1439. for (i = 0; i <= MAX_PLAYER_EQUIPMENTS; i++)
  1440. {
  1441. w += gpGlobals->rgEquipmentEffect[i].rgwPoisonResistance[wPlayerRole];
  1442. }
  1443. if (w > 100)
  1444. {
  1445. w = 100;
  1446. }
  1447. return w;
  1448. }
  1449. WORD
  1450. PAL_GetPlayerElementalResistance(
  1451. WORD wPlayerRole,
  1452. INT iAttrib
  1453. )
  1454. /*++
  1455. Purpose:
  1456. Get the player's resistance to attributed magics, count in the effect
  1457. of equipments.
  1458. Parameters:
  1459. [IN] wPlayerRole - the player role ID.
  1460. [IN] iAttrib - the attribute of magics.
  1461. Return value:
  1462. The total resistance to the attributed magics of the player.
  1463. --*/
  1464. {
  1465. WORD w;
  1466. int i;
  1467. w = gpGlobals->g.PlayerRoles.rgwElementalResistance[iAttrib][wPlayerRole];
  1468. for (i = 0; i <= MAX_PLAYER_EQUIPMENTS; i++)
  1469. {
  1470. w += gpGlobals->rgEquipmentEffect[i].rgwElementalResistance[iAttrib][wPlayerRole];
  1471. }
  1472. if (w > 100)
  1473. {
  1474. w = 100;
  1475. }
  1476. return w;
  1477. }
  1478. WORD
  1479. PAL_GetPlayerBattleSprite(
  1480. WORD wPlayerRole
  1481. )
  1482. /*++
  1483. Purpose:
  1484. Get player's battle sprite.
  1485. Parameters:
  1486. [IN] wPlayerRole - the player role ID.
  1487. Return value:
  1488. Number of the player's battle sprite.
  1489. --*/
  1490. {
  1491. int i;
  1492. WORD w;
  1493. w = gpGlobals->g.PlayerRoles.rgwSpriteNumInBattle[wPlayerRole];
  1494. for (i = 0; i <= MAX_PLAYER_EQUIPMENTS; i++)
  1495. {
  1496. if (gpGlobals->rgEquipmentEffect[i].rgwSpriteNumInBattle[wPlayerRole] != 0)
  1497. {
  1498. w = gpGlobals->rgEquipmentEffect[i].rgwSpriteNumInBattle[wPlayerRole];
  1499. }
  1500. }
  1501. return w;
  1502. }
  1503. WORD
  1504. PAL_GetPlayerCooperativeMagic(
  1505. WORD wPlayerRole
  1506. )
  1507. /*++
  1508. Purpose:
  1509. Get player's cooperative magic.
  1510. Parameters:
  1511. [IN] wPlayerRole - the player role ID.
  1512. Return value:
  1513. Object ID of the player's cooperative magic.
  1514. --*/
  1515. {
  1516. int i;
  1517. WORD w;
  1518. w = gpGlobals->g.PlayerRoles.rgwCooperativeMagic[wPlayerRole];
  1519. for (i = 0; i <= MAX_PLAYER_EQUIPMENTS; i++)
  1520. {
  1521. if (gpGlobals->rgEquipmentEffect[i].rgwCooperativeMagic[wPlayerRole] != 0)
  1522. {
  1523. w = gpGlobals->rgEquipmentEffect[i].rgwCooperativeMagic[wPlayerRole];
  1524. }
  1525. }
  1526. return w;
  1527. }
  1528. BOOL
  1529. PAL_PlayerCanAttackAll(
  1530. WORD wPlayerRole
  1531. )
  1532. /*++
  1533. Purpose:
  1534. Check if the player can attack all of the enemies in one move.
  1535. Parameters:
  1536. [IN] wPlayerRole - the player role ID.
  1537. Return value:
  1538. TRUE if player can attack all of the enemies in one move, FALSE if not.
  1539. --*/
  1540. {
  1541. int i;
  1542. BOOL f;
  1543. f = FALSE;
  1544. for (i = 0; i <= MAX_PLAYER_EQUIPMENTS; i++)
  1545. {
  1546. if (gpGlobals->rgEquipmentEffect[i].rgwAttackAll[wPlayerRole] != 0)
  1547. {
  1548. f = TRUE;
  1549. break;
  1550. }
  1551. }
  1552. return f;
  1553. }
  1554. BOOL
  1555. PAL_AddMagic(
  1556. WORD wPlayerRole,
  1557. WORD wMagic
  1558. )
  1559. /*++
  1560. Purpose:
  1561. Add a magic to the player.
  1562. Parameters:
  1563. [IN] wPlayerRole - the player role ID.
  1564. [IN] wMagic - the object ID of the magic.
  1565. Return value:
  1566. TRUE if succeeded, FALSE if failed.
  1567. --*/
  1568. {
  1569. int i;
  1570. for (i = 0; i < MAX_PLAYER_MAGICS; i++)
  1571. {
  1572. if (gpGlobals->g.PlayerRoles.rgwMagic[i][wPlayerRole] == wMagic)
  1573. {
  1574. //
  1575. // already have this magic
  1576. //
  1577. return FALSE;
  1578. }
  1579. }
  1580. for (i = 0; i < MAX_PLAYER_MAGICS; i++)
  1581. {
  1582. if (gpGlobals->g.PlayerRoles.rgwMagic[i][wPlayerRole] == 0)
  1583. {
  1584. break;
  1585. }
  1586. }
  1587. if (i >= MAX_PLAYER_MAGICS)
  1588. {
  1589. //
  1590. // Not enough slots
  1591. //
  1592. return FALSE;
  1593. }
  1594. gpGlobals->g.PlayerRoles.rgwMagic[i][wPlayerRole] = wMagic;
  1595. return TRUE;
  1596. }
  1597. VOID
  1598. PAL_RemoveMagic(
  1599. WORD wPlayerRole,
  1600. WORD wMagic
  1601. )
  1602. /*++
  1603. Purpose:
  1604. Remove a magic to the player.
  1605. Parameters:
  1606. [IN] wPlayerRole - the player role ID.
  1607. [IN] wMagic - the object ID of the magic.
  1608. Return value:
  1609. None.
  1610. --*/
  1611. {
  1612. int i;
  1613. for (i = 0; i < MAX_PLAYER_MAGICS; i++)
  1614. {
  1615. if (gpGlobals->g.PlayerRoles.rgwMagic[i][wPlayerRole] == wMagic)
  1616. {
  1617. gpGlobals->g.PlayerRoles.rgwMagic[i][wPlayerRole] = 0;
  1618. break;
  1619. }
  1620. }
  1621. }
  1622. VOID
  1623. PAL_SetPlayerStatus(
  1624. WORD wPlayerRole,
  1625. WORD wStatusID,
  1626. WORD wNumRound
  1627. )
  1628. /*++
  1629. Purpose:
  1630. Set one of the statuses for the player.
  1631. Parameters:
  1632. [IN] wPlayerRole - the player ID.
  1633. [IN] wStatusID - the status to be set.
  1634. [IN] wNumRound - the effective rounds of the status.
  1635. Return value:
  1636. None.
  1637. --*/
  1638. {
  1639. #ifndef PAL_CLASSIC
  1640. if (wStatusID == kStatusSlow &&
  1641. gpGlobals->rgPlayerStatus[wPlayerRole][kStatusHaste] > 0)
  1642. {
  1643. //
  1644. // Remove the haste status
  1645. //
  1646. PAL_RemovePlayerStatus(wPlayerRole, kStatusHaste);
  1647. return;
  1648. }
  1649. if (wStatusID == kStatusHaste &&
  1650. gpGlobals->rgPlayerStatus[wPlayerRole][kStatusSlow] > 0)
  1651. {
  1652. //
  1653. // Remove the slow status
  1654. //
  1655. PAL_RemovePlayerStatus(wPlayerRole, kStatusSlow);
  1656. return;
  1657. }
  1658. #endif
  1659. switch (wStatusID)
  1660. {
  1661. case kStatusConfused:
  1662. case kStatusSleep:
  1663. case kStatusSilence:
  1664. #ifdef PAL_CLASSIC
  1665. case kStatusParalyzed:
  1666. #else
  1667. case kStatusSlow:
  1668. #endif
  1669. //
  1670. // for "bad" statuses, don't set the status when we already have it
  1671. //
  1672. if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] != 0 &&
  1673. gpGlobals->rgPlayerStatus[wPlayerRole][wStatusID] == 0)
  1674. {
  1675. gpGlobals->rgPlayerStatus[wPlayerRole][wStatusID] = wNumRound;
  1676. }
  1677. break;
  1678. case kStatusPuppet:
  1679. //
  1680. // only allow dead players for "puppet" status
  1681. //
  1682. if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] == 0 &&
  1683. gpGlobals->rgPlayerStatus[wPlayerRole][wStatusID] < wNumRound)
  1684. {
  1685. gpGlobals->rgPlayerStatus[wPlayerRole][wStatusID] = wNumRound;
  1686. }
  1687. break;
  1688. case kStatusBravery:
  1689. case kStatusProtect:
  1690. case kStatusDualAttack:
  1691. case kStatusHaste:
  1692. //
  1693. // for "good" statuses, reset the status if the status to be set lasts longer
  1694. //
  1695. if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] != 0 &&
  1696. gpGlobals->rgPlayerStatus[wPlayerRole][wStatusID] < wNumRound)
  1697. {
  1698. gpGlobals->rgPlayerStatus[wPlayerRole][wStatusID] = wNumRound;
  1699. }
  1700. break;
  1701. default:
  1702. assert(FALSE);
  1703. break;
  1704. }
  1705. }
  1706. VOID
  1707. PAL_RemovePlayerStatus(
  1708. WORD wPlayerRole,
  1709. WORD wStatusID
  1710. )
  1711. /*++
  1712. Purpose:
  1713. Remove one of the status for player.
  1714. Parameters:
  1715. [IN] wPlayerRole - the player ID.
  1716. [IN] wStatusID - the status to be set.
  1717. Return value:
  1718. None.
  1719. --*/
  1720. {
  1721. //
  1722. // Don't remove effects of equipments
  1723. //
  1724. if (gpGlobals->rgPlayerStatus[wPlayerRole][wStatusID] <= 999)
  1725. {
  1726. gpGlobals->rgPlayerStatus[wPlayerRole][wStatusID] = 0;
  1727. }
  1728. }
  1729. VOID
  1730. PAL_ClearAllPlayerStatus(
  1731. VOID
  1732. )
  1733. /*++
  1734. Purpose:
  1735. Clear all player status.
  1736. Parameters:
  1737. None.
  1738. Return value:
  1739. None.
  1740. --*/
  1741. {
  1742. int i, j;
  1743. for (i = 0; i < MAX_PLAYER_ROLES; i++)
  1744. {
  1745. for (j = 0; j < kStatusAll; j++)
  1746. {
  1747. //
  1748. // Don't remove effects of equipments
  1749. //
  1750. if (gpGlobals->rgPlayerStatus[i][j] <= 999)
  1751. {
  1752. gpGlobals->rgPlayerStatus[i][j] = 0;
  1753. }
  1754. }
  1755. }
  1756. }
  1757. VOID
  1758. PAL_PlayerLevelUp(
  1759. WORD wPlayerRole,
  1760. WORD wNumLevel
  1761. )
  1762. /*++
  1763. Purpose:
  1764. Increase the player's level by wLevels.
  1765. Parameters:
  1766. [IN] wPlayerRole - player role ID.
  1767. [IN] wNumLevel - number of levels to be increased.
  1768. Return value:
  1769. None.
  1770. --*/
  1771. {
  1772. WORD i;
  1773. //
  1774. // Add the level
  1775. //
  1776. gpGlobals->g.PlayerRoles.rgwLevel[wPlayerRole] += wNumLevel;
  1777. if (gpGlobals->g.PlayerRoles.rgwLevel[wPlayerRole] > MAX_LEVELS)
  1778. {
  1779. gpGlobals->g.PlayerRoles.rgwLevel[wPlayerRole] = MAX_LEVELS;
  1780. }
  1781. for (i = 0; i < wNumLevel; i++)
  1782. {
  1783. //
  1784. // Increase player's stats
  1785. //
  1786. gpGlobals->g.PlayerRoles.rgwMaxHP[wPlayerRole] += 10 + RandomLong(0, 8);
  1787. gpGlobals->g.PlayerRoles.rgwMaxMP[wPlayerRole] += 8 + RandomLong(0, 6);
  1788. gpGlobals->g.PlayerRoles.rgwAttackStrength[wPlayerRole] += 4 + RandomLong(0, 1);
  1789. gpGlobals->g.PlayerRoles.rgwMagicStrength[wPlayerRole] += 4 + RandomLong(0, 1);
  1790. gpGlobals->g.PlayerRoles.rgwDefense[wPlayerRole] += 2 + RandomLong(0, 1);
  1791. gpGlobals->g.PlayerRoles.rgwDexterity[wPlayerRole] += 2 + RandomLong(0, 1);
  1792. gpGlobals->g.PlayerRoles.rgwFleeRate[wPlayerRole] += 2;
  1793. }
  1794. #define STAT_LIMIT(t) { if ((t) > 999) (t) = 999; }
  1795. STAT_LIMIT(gpGlobals->g.PlayerRoles.rgwMaxHP[wPlayerRole]);
  1796. STAT_LIMIT(gpGlobals->g.PlayerRoles.rgwMaxMP[wPlayerRole]);
  1797. STAT_LIMIT(gpGlobals->g.PlayerRoles.rgwAttackStrength[wPlayerRole]);
  1798. STAT_LIMIT(gpGlobals->g.PlayerRoles.rgwMagicStrength[wPlayerRole]);
  1799. STAT_LIMIT(gpGlobals->g.PlayerRoles.rgwDefense[wPlayerRole]);
  1800. STAT_LIMIT(gpGlobals->g.PlayerRoles.rgwDexterity[wPlayerRole]);
  1801. STAT_LIMIT(gpGlobals->g.PlayerRoles.rgwFleeRate[wPlayerRole]);
  1802. #undef STAT_LIMIT
  1803. //
  1804. // Reset experience points to zero
  1805. //
  1806. gpGlobals->Exp.rgPrimaryExp[wPlayerRole].wExp = 0;
  1807. gpGlobals->Exp.rgPrimaryExp[wPlayerRole].wLevel =
  1808. gpGlobals->g.PlayerRoles.rgwLevel[wPlayerRole];
  1809. }