VOID PAL_DrawASCIICharOnSurface( BYTE bChar, SDL_Surface *lpSurface, PAL_POS pos, BYTE bColor ) /*++ Purpose: Draw a ASCII character on a surface. Parameters: [IN] bChar - the character to be drawn. [OUT] lpSurface - the destination surface. [IN] pos - the destination location of the surface. [IN] bColor - the color of the character. Return value: None. --*/ { int i, j, dx; int x = PAL_X(pos), y = PAL_Y(pos); LPBYTE pChar = &iso_font[(int)(bChar & ~128) * 15]; // // Check for NULL pointer. // if (lpSurface == NULL) { return; } // // Draw the character to the surface. // y *= lpSurface->pitch; for (i = 0; i < 15; i++) { dx = x; for (j = 0; j < 8; j++) { if (pChar[i] & (1 << j)) { ((LPBYTE)(lpSurface->pixels))[y + dx] = bColor; } dx++; } y += lpSurface->pitch; } }
VOID PAL_MakeScene( VOID ) /*++ Purpose: Draw the scene of the current frame to the screen. Both the map and the sprites are handled here. Parameters: None. Return value: None. --*/ { static SDL_Rect rect = {0, 0, 320, 200}; // // Step 1: Draw the complete map, for both of the layers. // rect.x = PAL_X(gpGlobals->viewport); rect.y = PAL_Y(gpGlobals->viewport); PAL_MapBlitToSurface(PAL_GetCurrentMap(), gpScreen, &rect, 0); PAL_MapBlitToSurface(PAL_GetCurrentMap(), gpScreen, &rect, 1); // // Step 2: Apply screen waving effects. // PAL_ApplyWave(gpScreen); // // Step 3: Draw all the sprites. // PAL_SceneDrawSprites(); // // Check if we need to fade in. // if (gpGlobals->fNeedToFadeIn) { VIDEO_UpdateScreen(NULL); PAL_FadeIn(gpGlobals->wNumPalette, gpGlobals->fNightPalette, 1); gpGlobals->fNeedToFadeIn = FALSE; } }
VOID PAL_DeleteBox( LPBOX lpBox ) /*++ Purpose: Delete a box and restore the saved part of the screen. Parameters: [IN] lpBox - pointer to the BOX struct. Return value: None. --*/ { SDL_Rect rect; // // Check for NULL pointer. // if (lpBox == NULL) { return; } // // Restore the saved screen part // rect.x = PAL_X(lpBox->pos); rect.y = PAL_Y(lpBox->pos); rect.w = lpBox->wWidth; rect.h = lpBox->wHeight; SDL_BlitSurface(lpBox->lpSavedArea, NULL, gpScreen, &rect); // // Free the memory used by the box // SDL_FreeSurface(lpBox->lpSavedArea); free(lpBox); }
/*++ Enemy flee the battle. --*/ VOID PAL_BattleEnemyEscape(void) { int j, x, y, w; BOOL f = TRUE; SOUND_Play(45); // // Show the animation // while (f) { f = FALSE; for (j = 0; j <= g_Battle.wMaxEnemyIndex; j++) { if (g_Battle.rgEnemy[j].wObjectID == 0) { continue; } x = PAL_X(g_Battle.rgEnemy[j].pos) - 5; y = PAL_Y(g_Battle.rgEnemy[j].pos); g_Battle.rgEnemy[j].pos = PAL_XY(x, y); w = PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgEnemy[j].lpSprite, 0)); if (x + w > 0) { f = TRUE; } } PAL_BattleMakeScene(); SDL_BlitSurface(g_Battle.lpSceneBuf, NULL, gpScreen, NULL); VIDEO_UpdateScreen(NULL); UTIL_Delay(10); } UTIL_Delay(500); g_Battle.BattleResult = kBattleResultTerminated; }
VOID PAL_BattleUIShowNum( WORD wNum, PAL_POS pos, NUMCOLOR color ) /*++ Purpose: Show a number on battle screen (indicates HP/MP change). Parameters: [IN] wNum - number to be shown. [IN] pos - position of the number on the screen. [IN] color - color of the number. Return value: None. --*/ { int i; for (i = 0; i < BATTLEUI_MAX_SHOWNUM; i++) { if (g_Battle.UI.rgShowNum[i].wNum == 0) { g_Battle.UI.rgShowNum[i].wNum = wNum; g_Battle.UI.rgShowNum[i].pos = PAL_XY(PAL_X(pos) - 15, PAL_Y(pos)); g_Battle.UI.rgShowNum[i].color = color; g_Battle.UI.rgShowNum[i].dwTime = SDL_GetTicks(); break; } } }
VOID PAL_UpdateParty( VOID ) /*++ Purpose: Update the location and walking gesture of all the party members. Parameters: None. Return value: None. --*/ { int xSource, ySource, xTarget, yTarget, xOffset, yOffset, i; // // Has user pressed one of the arrow keys? // if (g_InputState.dir != kDirUnknown) { xOffset = ((g_InputState.dir == kDirWest || g_InputState.dir == kDirSouth) ? -16 : 16); yOffset = ((g_InputState.dir == kDirWest || g_InputState.dir == kDirNorth) ? -8 : 8); xSource = PAL_X(gpGlobals->viewport) + PAL_X(gpGlobals->partyoffset); ySource = PAL_Y(gpGlobals->viewport) + PAL_Y(gpGlobals->partyoffset); xTarget = xSource + xOffset; yTarget = ySource + yOffset; gpGlobals->wPartyDirection = g_InputState.dir; // // Check for obstacles on the destination location // if (!PAL_CheckObstacle(PAL_XY(xTarget, yTarget), TRUE, 0)) { // // Player will actually be moved. Store trail. // for (i = 3; i >= 0; i--) { gpGlobals->rgTrail[i + 1] = gpGlobals->rgTrail[i]; } gpGlobals->rgTrail[0].wDirection = g_InputState.dir; gpGlobals->rgTrail[0].x = xSource; gpGlobals->rgTrail[0].y = ySource; // // Move the viewport // gpGlobals->viewport = PAL_XY(PAL_X(gpGlobals->viewport) + xOffset, PAL_Y(gpGlobals->viewport) + yOffset); // // Update gestures // PAL_UpdatePartyGestures(TRUE); return; // don't go further } } PAL_UpdatePartyGestures(FALSE); }
VOID PAL_SaveGame( LPCSTR szFileName, WORD wSavedTimes ) /*++ Purpose: Save the current game state to file. Parameters: [IN] szFileName - file name of saved game. Return value: None. --*/ { FILE *fp; static SAVEDGAME s; UINT32 i; // // Put all the data to the saved game struct. // s.wViewportX = PAL_X(gpGlobals->viewport); s.wViewportY = PAL_Y(gpGlobals->viewport); s.nPartyMember = gpGlobals->wMaxPartyMemberIndex; s.wNumScene = gpGlobals->wNumScene; s.wPaletteOffset = (gpGlobals->fNightPalette ? 0x180 : 0); s.wPartyDirection = gpGlobals->wPartyDirection; s.wNumMusic = gpGlobals->wNumMusic; s.wNumBattleMusic = gpGlobals->wNumBattleMusic; s.wNumBattleField = gpGlobals->wNumBattleField; s.wScreenWave = gpGlobals->wScreenWave; s.wCollectValue = gpGlobals->wCollectValue; s.wLayer = gpGlobals->wLayer; s.wChaseRange = gpGlobals->wChaseRange; s.wChasespeedChangeCycles = gpGlobals->wChasespeedChangeCycles; s.nFollower = gpGlobals->nFollower; s.dwCash = gpGlobals->dwCash; #ifndef PAL_CLASSIC s.wBattleSpeed = gpGlobals->bBattleSpeed; #else s.wBattleSpeed = 2; #endif memcpy(s.rgParty, gpGlobals->rgParty, sizeof(gpGlobals->rgParty)); memcpy(s.rgTrail, gpGlobals->rgTrail, sizeof(gpGlobals->rgTrail)); s.Exp = gpGlobals->Exp; s.PlayerRoles = gpGlobals->g.PlayerRoles; memcpy(s.rgPoisonStatus, gpGlobals->rgPoisonStatus, sizeof(gpGlobals->rgPoisonStatus)); memcpy(s.rgInventory, gpGlobals->rgInventory, sizeof(gpGlobals->rgInventory)); memcpy(s.rgScene, gpGlobals->g.rgScene, sizeof(gpGlobals->g.rgScene)); memcpy(s.rgObject, gpGlobals->g.rgObject, sizeof(gpGlobals->g.rgObject)); memcpy(s.rgEventObject, gpGlobals->g.lprgEventObject, sizeof(EVENTOBJECT) * gpGlobals->g.nEventObject); s.wSavedTimes = wSavedTimes; // // Adjust endianness // DO_BYTESWAP(&s, sizeof(SAVEDGAME)); // // Cash amount is in DWORD, so do a wordswap in Big-Endian. // #if SDL_BYTEORDER == SDL_BIG_ENDIAN s.dwCash = ((s.dwCash >> 16) | (s.dwCash << 16)); #endif // // Try writing to file // fp = fopen(szFileName, "wb"); if (fp == NULL) { return; } i = PAL_MKFGetChunkSize(0, gpGlobals->f.fpSSS); i += sizeof(SAVEDGAME) - sizeof(EVENTOBJECT) * MAX_EVENT_OBJECTS; fwrite(&s, i, 1, fp); fclose(fp); }
static BOOL PAL_BattleDisplayStatChange( VOID ) /*++ Purpose: Display the HP and MP changes of all players and enemies. Parameters: None. Return value: TRUE if there are any number displayed, FALSE if not. --*/ { int i, x, y; SHORT sDamage; WORD wPlayerRole; BOOL f = FALSE; for (i = 0; i <= g_Battle.wMaxEnemyIndex; i++) { if (g_Battle.rgEnemy[i].wObjectID == 0) { continue; } if (g_Battle.rgEnemy[i].wPrevHP != g_Battle.rgEnemy[i].e.wHealth) { // // Show the number of damage // sDamage = g_Battle.rgEnemy[i].e.wHealth - g_Battle.rgEnemy[i].wPrevHP; x = PAL_X(g_Battle.rgEnemy[i].pos); y = PAL_Y(g_Battle.rgEnemy[i].pos) - 70; x += PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame)) / 2; y += PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame)) / 2; if (y < 10) { y = 10; } if (sDamage < 0) { PAL_BattleUIShowNum((WORD)(-sDamage), PAL_XY(x, y), kNumColorBlue); } else { PAL_BattleUIShowNum((WORD)(sDamage), PAL_XY(x, y), kNumColorYellow); } f = TRUE; } } for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++) { wPlayerRole = gpGlobals->rgParty[i].wPlayerRole; if (g_Battle.rgPlayer[i].wPrevHP != gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole]) { sDamage = gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] - g_Battle.rgPlayer[i].wPrevHP; x = PAL_X(g_Battle.rgPlayer[i].pos); y = PAL_Y(g_Battle.rgPlayer[i].pos) - 70; x += PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame)) / 2; y += PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame)); if (y < 10) { y = 10; } if (sDamage < 0) { PAL_BattleUIShowNum((WORD)(-sDamage), PAL_XY(x, y), kNumColorBlue); } else { PAL_BattleUIShowNum((WORD)(sDamage), PAL_XY(x, y), kNumColorYellow); } f = TRUE; } if (g_Battle.rgPlayer[i].wPrevMP != gpGlobals->g.PlayerRoles.rgwMP[wPlayerRole]) { sDamage = gpGlobals->g.PlayerRoles.rgwMP[wPlayerRole] - g_Battle.rgPlayer[i].wPrevMP; x = PAL_X(g_Battle.rgPlayer[i].pos); y = PAL_Y(g_Battle.rgPlayer[i].pos) - 62; x += PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame)) / 2; y += PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame)); if (y < 10) { y = 10; } if (sDamage < 0) { PAL_BattleUIShowNum((WORD)(-sDamage), PAL_XY(x, y), kNumColorBlue); } else { PAL_BattleUIShowNum((WORD)(sDamage), PAL_XY(x, y), kNumColorCyan); } f = TRUE; } } return f; }
static VOID PAL_CalcCoverTiles( SPRITE_TO_DRAW *lpSpriteToDraw ) /*++ Purpose: Calculate all the tiles which may cover the specified sprite. Add the tiles into our list as well. Parameters: [IN] lpSpriteToDraw - pointer to SPRITE_TO_DRAW struct. Return value: None. --*/ { int x, y, i, l, iTileHeight; LPCBITMAPRLE lpTile; const int sx = PAL_X(gpGlobals->viewport) + PAL_X(lpSpriteToDraw->pos); const int sy = PAL_Y(gpGlobals->viewport) + PAL_Y(lpSpriteToDraw->pos); const int sh = ((sx % 32) ? 1 : 0); const int width = PAL_RLEGetWidth(lpSpriteToDraw->lpSpriteFrame); const int height = PAL_RLEGetHeight(lpSpriteToDraw->lpSpriteFrame); int dx = 0; int dy = 0; int dh = 0; // // Loop through all the tiles in the area of the sprite. // for (y = (sy - height - 15) / 16; y <= sy / 16; y++) { for (x = (sx - width / 2) / 32; x <= (sx + width / 2) / 32; x++) { for (i = ((x == (sx - width / 2) / 32) ? 0 : 3); i < 5; i++) { // // Scan tiles in the following form (* = to scan): // // . . . * * * . . . // . . . * * . . . . // switch (i) { case 0: dx = x; dy = y; dh = sh; break; case 1: dx = x - 1; break; case 2: dx = (sh ? x : (x - 1)); dy = (sh ? (y + 1) : y); dh = 1 - sh; break; case 3: dx = x + 1; dy = y; dh = sh; break; case 4: dx = (sh ? (x + 1) : x); dy = (sh ? (y + 1) : y); dh = 1 - sh; break; } for (l = 0; l < 2; l++) { lpTile = PAL_MapGetTileBitmap(dx, dy, dh, l, PAL_GetCurrentMap()); iTileHeight = (signed char)PAL_MapGetTileHeight(dx, dy, dh, l, PAL_GetCurrentMap()); // // Check if this tile may cover the sprites // if (lpTile != NULL && iTileHeight > 0 && (dy + iTileHeight) * 16 + dh * 8 >= sy) { // // This tile may cover the sprite // PAL_AddSpriteToDraw(lpTile, dx * 32 + dh * 16 - 16 - PAL_X(gpGlobals->viewport), dy * 16 + dh * 8 + 7 + l + iTileHeight * 8 - PAL_Y(gpGlobals->viewport), iTileHeight * 8 + l); } } } } } }
VOID PAL_BattleUIUpdate( VOID ) /*++ Purpose: Update the status of battle UI. Parameters: None. Return value: None. --*/ { int i, j, x, y; WORD wPlayerRole, w; static int s_iFrame = 0; s_iFrame++; if (g_Battle.UI.fAutoAttack && !gpGlobals->fAutoBattle) { // // Draw the "auto attack" message if in the autoattack mode. // if (g_InputState.dwKeyPress & kKeyMenu) { g_Battle.UI.fAutoAttack = FALSE; } else { PAL_DrawText(PAL_GetWord(BATTLEUI_LABEL_AUTO), PAL_XY(280, 10), MENUITEM_COLOR_CONFIRMED, TRUE, FALSE); } } if (gpGlobals->fAutoBattle) { PAL_BattlePlayerCheckReady(); for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++) { if (g_Battle.rgPlayer[i].state == kFighterCom) { PAL_BattleUIPlayerReady(i); break; } } if (g_Battle.UI.state != kBattleUIWait) { w = PAL_BattleUIPickAutoMagic(gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole, 9999); if (w == 0) { g_Battle.UI.wActionType = kBattleActionAttack; g_Battle.UI.wSelectedIndex = PAL_BattleSelectAutoTarget(); } else { g_Battle.UI.wActionType = kBattleActionMagic; g_Battle.UI.wObjectID = w; if (gpGlobals->g.rgObject[w].magic.wFlags & kMagicFlagApplyToAll) { g_Battle.UI.wSelectedIndex = -1; } else { g_Battle.UI.wSelectedIndex = PAL_BattleSelectAutoTarget(); } } PAL_BattleCommitAction(FALSE); } goto end; } if (g_InputState.dwKeyPress & kKeyAuto) { g_Battle.UI.fAutoAttack = !g_Battle.UI.fAutoAttack; g_Battle.UI.MenuState = kBattleMenuMain; } #ifdef PAL_CLASSIC if (g_Battle.Phase == kBattlePhasePerformAction) { goto end; } if (!g_Battle.UI.fAutoAttack) #endif { // // Draw the player info boxes. // for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++) { wPlayerRole = gpGlobals->rgParty[i].wPlayerRole; w = F2int(g_Battle.rgPlayer[i].flTimeMeter); j = TIMEMETER_COLOR_DEFAULT; #ifndef PAL_CLASSIC if (gpGlobals->rgPlayerStatus[wPlayerRole][kStatusHaste] > 0) { j = TIMEMETER_COLOR_HASTE; } else if (gpGlobals->rgPlayerStatus[wPlayerRole][kStatusSlow] > 0) { j = TIMEMETER_COLOR_SLOW; } #endif if (gpGlobals->rgPlayerStatus[wPlayerRole][kStatusSleep] != 0 || gpGlobals->rgPlayerStatus[wPlayerRole][kStatusConfused] != 0 || gpGlobals->rgPlayerStatus[wPlayerRole][kStatusPuppet] != 0) { w = 0; } PAL_PlayerInfoBox(PAL_XY(91 + 77 * i, 165), wPlayerRole, w, j, FALSE); } } if (g_InputState.dwKeyPress & kKeyStatus) { PAL_PlayerStatus(); goto end; } if (g_Battle.UI.state != kBattleUIWait) { wPlayerRole = gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole; if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] == 0 && gpGlobals->rgPlayerStatus[wPlayerRole][kStatusPuppet]) { g_Battle.UI.wActionType = kBattleActionAttack; if (PAL_PlayerCanAttackAll(gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole)) { g_Battle.UI.wSelectedIndex = -1; } else { g_Battle.UI.wSelectedIndex = PAL_BattleSelectAutoTarget(); } PAL_BattleCommitAction(FALSE); goto end; // don't go further } // // Cancel any actions if player is dead or sleeping. // if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] == 0 || gpGlobals->rgPlayerStatus[wPlayerRole][kStatusSleep] != 0 || gpGlobals->rgPlayerStatus[wPlayerRole][kStatusParalyzed] != 0) { g_Battle.UI.wActionType = kBattleActionPass; PAL_BattleCommitAction(FALSE); goto end; // don't go further } if (gpGlobals->rgPlayerStatus[wPlayerRole][kStatusConfused] != 0) { g_Battle.UI.wActionType = kBattleActionAttackMate; PAL_BattleCommitAction(FALSE); goto end; // don't go further } if (g_Battle.UI.fAutoAttack) { g_Battle.UI.wActionType = kBattleActionAttack; if (PAL_PlayerCanAttackAll(gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole)) { g_Battle.UI.wSelectedIndex = -1; } else { g_Battle.UI.wSelectedIndex = PAL_BattleSelectAutoTarget(); } PAL_BattleCommitAction(FALSE); goto end; // don't go further } // // Draw the arrow on the player's head. // i = SPRITENUM_BATTLE_ARROW_CURRENTPLAYER_RED; if (s_iFrame & 1) { i = SPRITENUM_BATTLE_ARROW_CURRENTPLAYER; } x = g_rgPlayerPos[gpGlobals->wMaxPartyMemberIndex][g_Battle.UI.wCurPlayerIndex][0] - 8; y = g_rgPlayerPos[gpGlobals->wMaxPartyMemberIndex][g_Battle.UI.wCurPlayerIndex][1] - 74; PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, i), gpScreen, PAL_XY(x, y)); } switch (g_Battle.UI.state) { case kBattleUIWait: if (!g_Battle.fEnemyCleared) { PAL_BattlePlayerCheckReady(); for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++) { if (g_Battle.rgPlayer[i].state == kFighterCom) { PAL_BattleUIPlayerReady(i); break; } } } break; case kBattleUISelectMove: // // Draw the icons // { struct { int iSpriteNum; PAL_POS pos; BATTLEUIACTION action; } rgItems[] = { {SPRITENUM_BATTLEICON_ATTACK, PAL_XY(27, 140), kBattleUIActionAttack}, {SPRITENUM_BATTLEICON_MAGIC, PAL_XY(0, 155), kBattleUIActionMagic}, {SPRITENUM_BATTLEICON_COOPMAGIC, PAL_XY(54, 155), kBattleUIActionCoopMagic}, {SPRITENUM_BATTLEICON_MISCMENU, PAL_XY(27, 170), kBattleUIActionMisc} }; if (g_Battle.UI.MenuState == kBattleMenuMain) { if (g_InputState.dir == kDirNorth) { g_Battle.UI.wSelectedAction = 0; } else if (g_InputState.dir == kDirSouth) { g_Battle.UI.wSelectedAction = 3; } else if (g_InputState.dir == kDirWest) { if (PAL_BattleUIIsActionValid(kBattleUIActionMagic)) { g_Battle.UI.wSelectedAction = 1; } } else if (g_InputState.dir == kDirEast) { if (PAL_BattleUIIsActionValid(kBattleUIActionCoopMagic)) { g_Battle.UI.wSelectedAction = 2; } } } if (!PAL_BattleUIIsActionValid(rgItems[g_Battle.UI.wSelectedAction].action)) { g_Battle.UI.wSelectedAction = 0; } for (i = 0; i < 4; i++) { if (g_Battle.UI.wSelectedAction == i) { PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, rgItems[i].iSpriteNum), gpScreen, rgItems[i].pos); } else if (PAL_BattleUIIsActionValid(rgItems[i].action)) { PAL_RLEBlitMonoColor(PAL_SpriteGetFrame(gpSpriteUI, rgItems[i].iSpriteNum), gpScreen, rgItems[i].pos, 0, -4); } else { PAL_RLEBlitMonoColor(PAL_SpriteGetFrame(gpSpriteUI, rgItems[i].iSpriteNum), gpScreen, rgItems[i].pos, 0x10, -4); } } switch (g_Battle.UI.MenuState) { case kBattleMenuMain: if (g_InputState.dwKeyPress & kKeySearch) { switch (g_Battle.UI.wSelectedAction) { case 0: // // Attack // g_Battle.UI.wActionType = kBattleActionAttack; if (PAL_PlayerCanAttackAll(gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole)) { g_Battle.UI.state = kBattleUISelectTargetEnemyAll; } else { g_Battle.UI.wSelectedIndex = g_Battle.UI.wPrevEnemyTarget; g_Battle.UI.state = kBattleUISelectTargetEnemy; } break; case 1: // // Magic // g_Battle.UI.MenuState = kBattleMenuMagicSelect; PAL_MagicSelectionMenuInit(wPlayerRole, TRUE, 0); break; case 2: // // Cooperative magic // w = gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole; w = PAL_GetPlayerCooperativeMagic(w); g_Battle.UI.wActionType = kBattleActionCoopMagic; g_Battle.UI.wObjectID = w; if (gpGlobals->g.rgObject[w].magic.wFlags & kMagicFlagUsableToEnemy) { if (gpGlobals->g.rgObject[w].magic.wFlags & kMagicFlagApplyToAll) { g_Battle.UI.state = kBattleUISelectTargetEnemyAll; } else { g_Battle.UI.wSelectedIndex = g_Battle.UI.wPrevEnemyTarget; g_Battle.UI.state = kBattleUISelectTargetEnemy; } } else { if (gpGlobals->g.rgObject[w].magic.wFlags & kMagicFlagApplyToAll) { g_Battle.UI.state = kBattleUISelectTargetPlayerAll; } else { #ifdef PAL_CLASSIC g_Battle.UI.wSelectedIndex = 0; #else g_Battle.UI.wSelectedIndex = g_Battle.UI.wCurPlayerIndex; #endif g_Battle.UI.state = kBattleUISelectTargetPlayer; } } break; case 3: // // Misc menu // g_Battle.UI.MenuState = kBattleMenuMisc; g_iCurMiscMenuItem = 0; break; } } else if (g_InputState.dwKeyPress & kKeyDefend) { g_Battle.UI.wActionType = kBattleActionDefend; PAL_BattleCommitAction(FALSE); } else if (g_InputState.dwKeyPress & kKeyForce) { w = PAL_BattleUIPickAutoMagic(gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole, 60); if (w == 0) { g_Battle.UI.wActionType = kBattleActionAttack; if (PAL_PlayerCanAttackAll(gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole)) { g_Battle.UI.wSelectedIndex = -1; } else { g_Battle.UI.wSelectedIndex = PAL_BattleSelectAutoTarget(); } } else { g_Battle.UI.wActionType = kBattleActionMagic; g_Battle.UI.wObjectID = w; if (gpGlobals->g.rgObject[w].magic.wFlags & kMagicFlagApplyToAll) { g_Battle.UI.wSelectedIndex = -1; } else { g_Battle.UI.wSelectedIndex = PAL_BattleSelectAutoTarget(); } } PAL_BattleCommitAction(FALSE); } else if (g_InputState.dwKeyPress & kKeyFlee) { g_Battle.UI.wActionType = kBattleActionFlee; PAL_BattleCommitAction(FALSE); } else if (g_InputState.dwKeyPress & kKeyUseItem) { g_Battle.UI.MenuState = kBattleMenuUseItemSelect; PAL_ItemSelectMenuInit(kItemFlagUsable); } else if (g_InputState.dwKeyPress & kKeyThrowItem) { g_Battle.UI.MenuState = kBattleMenuThrowItemSelect; PAL_ItemSelectMenuInit(kItemFlagThrowable); } else if (g_InputState.dwKeyPress & kKeyRepeat) { PAL_BattleCommitAction(TRUE); } #ifdef PAL_CLASSIC else if (g_InputState.dwKeyPress & kKeyMenu) { g_Battle.rgPlayer[g_Battle.UI.wCurPlayerIndex].state = kFighterWait; g_Battle.UI.state = kBattleUIWait; if (g_Battle.UI.wCurPlayerIndex > 0) { // // Revert to the previous player // do { g_Battle.rgPlayer[--g_Battle.UI.wCurPlayerIndex].state = kFighterWait; if (g_Battle.rgPlayer[g_Battle.UI.wCurPlayerIndex].action.ActionType == kBattleActionThrowItem) { for (i = 0; i < MAX_INVENTORY; i++) { if (gpGlobals->rgInventory[i].wItem == g_Battle.rgPlayer[g_Battle.UI.wCurPlayerIndex].action.wActionID) { gpGlobals->rgInventory[i].nAmountInUse--; break; } } } else if (g_Battle.rgPlayer[g_Battle.UI.wCurPlayerIndex].action.ActionType == kBattleActionUseItem) { if (gpGlobals->g.rgObject[g_Battle.rgPlayer[g_Battle.UI.wCurPlayerIndex].action.wActionID].item.wFlags & kItemFlagConsuming) { for (i = 0; i < MAX_INVENTORY; i++) { if (gpGlobals->rgInventory[i].wItem == g_Battle.rgPlayer[g_Battle.UI.wCurPlayerIndex].action.wActionID) { gpGlobals->rgInventory[i].nAmountInUse--; break; } } } } } while (g_Battle.UI.wCurPlayerIndex > 0 && (gpGlobals->g.PlayerRoles.rgwHP[gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole] == 0 || gpGlobals->rgPlayerStatus[gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole][kStatusConfused] > 0 || gpGlobals->rgPlayerStatus[gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole][kStatusSleep] > 0 || gpGlobals->rgPlayerStatus[gpGlobals->rgParty[g_Battle.UI.wCurPlayerIndex].wPlayerRole][kStatusParalyzed] > 0)); } } #else else if (g_InputState.dwKeyPress & kKeyMenu) { FLOAT flMin = int2F(-1); j = -1; for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++) { if (g_Battle.rgPlayer[i].flTimeMeter >= 100) { g_Battle.rgPlayer[i].flTimeMeter += 100; // HACKHACK: Prevent the time meter from going below 100 if ((g_Battle.rgPlayer[i].flTimeMeter < flMin || flMin < 0) && i != (int)g_Battle.UI.wCurPlayerIndex && g_Battle.rgPlayer[i].state == kFighterWait) { flMin = g_Battle.rgPlayer[i].flTimeMeter; j = i; } } } if (j != -1) { g_Battle.rgPlayer[g_Battle.UI.wCurPlayerIndex].flTimeMeter = flMin - int2F(99); g_Battle.rgPlayer[g_Battle.UI.wCurPlayerIndex].state = kFighterWait; g_Battle.UI.state = kBattleUIWait; } } #endif break; case kBattleMenuMagicSelect: w = PAL_MagicSelectionMenuUpdate(); if (w != 0xFFFF) { g_Battle.UI.MenuState = kBattleMenuMain; if (w != 0) { g_Battle.UI.wActionType = kBattleActionMagic; g_Battle.UI.wObjectID = w; if (gpGlobals->g.rgObject[w].magic.wFlags & kMagicFlagUsableToEnemy) { if (gpGlobals->g.rgObject[w].magic.wFlags & kMagicFlagApplyToAll) { g_Battle.UI.state = kBattleUISelectTargetEnemyAll; } else { g_Battle.UI.wSelectedIndex = g_Battle.UI.wPrevEnemyTarget; g_Battle.UI.state = kBattleUISelectTargetEnemy; } } else { if (gpGlobals->g.rgObject[w].magic.wFlags & kMagicFlagApplyToAll) { g_Battle.UI.state = kBattleUISelectTargetPlayerAll; } else { #ifdef PAL_CLASSIC g_Battle.UI.wSelectedIndex = 0; #else g_Battle.UI.wSelectedIndex = g_Battle.UI.wCurPlayerIndex; #endif g_Battle.UI.state = kBattleUISelectTargetPlayer; } } } } break; case kBattleMenuUseItemSelect: PAL_BattleUIUseItem(); break; case kBattleMenuThrowItemSelect: PAL_BattleUIThrowItem(); break; case kBattleMenuMisc: w = PAL_BattleUIMiscMenuUpdate(); if (w != 0xFFFF) { g_Battle.UI.MenuState = kBattleMenuMain; switch (w) { #ifdef PAL_CLASSIC case 2: // item #else case 1: // item #endif g_Battle.UI.MenuState = kBattleMenuMiscItemSubMenu; g_iCurSubMenuItem = 0; break; #ifdef PAL_CLASSIC case 3: // defend #else case 2: // defend #endif g_Battle.UI.wActionType = kBattleActionDefend; PAL_BattleCommitAction(FALSE); break; #ifdef PAL_CLASSIC case 1: // auto #else case 3: // auto #endif g_Battle.UI.fAutoAttack = TRUE; break; case 4: // flee g_Battle.UI.wActionType = kBattleActionFlee; PAL_BattleCommitAction(FALSE); break; case 5: // status PAL_PlayerStatus(); break; } } break; case kBattleMenuMiscItemSubMenu: w = PAL_BattleUIMiscItemSubMenuUpdate(); if (w != 0xFFFF) { g_Battle.UI.MenuState = kBattleMenuMain; switch (w) { case 1: // use g_Battle.UI.MenuState = kBattleMenuUseItemSelect; PAL_ItemSelectMenuInit(kItemFlagUsable); break; case 2: // throw g_Battle.UI.MenuState = kBattleMenuThrowItemSelect; PAL_ItemSelectMenuInit(kItemFlagThrowable); break; } } break; } } break; case kBattleUISelectTargetEnemy: x = -1; y = 0; for (i = 0; i <= g_Battle.wMaxEnemyIndex; i++) { if (g_Battle.rgEnemy[i].wObjectID != 0) { x = i; y++; } } if (x == -1) { g_Battle.UI.state = kBattleUISelectMove; break; } if (g_Battle.UI.wActionType == kBattleActionCoopMagic) { if (!PAL_BattleUIIsActionValid(kBattleActionCoopMagic)) { g_Battle.UI.state = kBattleUISelectMove; break; } } #ifdef PAL_CLASSIC // // Don't bother selecting when only 1 enemy left // if (y == 1) { g_Battle.UI.wPrevEnemyTarget = (WORD)x; PAL_BattleCommitAction(FALSE); break; } #endif if (g_Battle.UI.wSelectedIndex > x) { g_Battle.UI.wSelectedIndex = x; } for (i = 0; i <= x; i++) { if (g_Battle.rgEnemy[g_Battle.UI.wSelectedIndex].wObjectID != 0) { break; } g_Battle.UI.wSelectedIndex++; g_Battle.UI.wSelectedIndex %= x + 1; } // // Highlight the selected enemy // if (s_iFrame & 1) { i = g_Battle.UI.wSelectedIndex; x = PAL_X(g_Battle.rgEnemy[i].pos); y = PAL_Y(g_Battle.rgEnemy[i].pos); x -= PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame)) / 2; y -= PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame)); PAL_RLEBlitWithColorShift(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame), gpScreen, PAL_XY(x, y), 7); } if (g_InputState.dwKeyPress & kKeyMenu) { g_Battle.UI.state = kBattleUISelectMove; } else if (g_InputState.dwKeyPress & kKeySearch) { g_Battle.UI.wPrevEnemyTarget = g_Battle.UI.wSelectedIndex; PAL_BattleCommitAction(FALSE); } else if (g_InputState.dwKeyPress & (kKeyLeft | kKeyDown)) { if (g_Battle.UI.wSelectedIndex != 0) { g_Battle.UI.wSelectedIndex--; while (g_Battle.UI.wSelectedIndex != 0 && g_Battle.rgEnemy[g_Battle.UI.wSelectedIndex].wObjectID == 0) { g_Battle.UI.wSelectedIndex--; } } } else if (g_InputState.dwKeyPress & (kKeyRight | kKeyUp)) { if (g_Battle.UI.wSelectedIndex < x) { g_Battle.UI.wSelectedIndex++; while (g_Battle.UI.wSelectedIndex < x && g_Battle.rgEnemy[g_Battle.UI.wSelectedIndex].wObjectID == 0) { g_Battle.UI.wSelectedIndex++; } } } break; case kBattleUISelectTargetPlayer: #ifdef PAL_CLASSIC // // Don't bother selecting when only 1 player is in the party // if (gpGlobals->wMaxPartyMemberIndex == 0) { g_Battle.UI.wSelectedIndex = 0; PAL_BattleCommitAction(FALSE); } #endif j = SPRITENUM_BATTLE_ARROW_SELECTEDPLAYER; if (s_iFrame & 1) { j = SPRITENUM_BATTLE_ARROW_SELECTEDPLAYER_RED; } // // Draw arrows on the selected player // x = g_rgPlayerPos[gpGlobals->wMaxPartyMemberIndex][g_Battle.UI.wSelectedIndex][0] - 8; y = g_rgPlayerPos[gpGlobals->wMaxPartyMemberIndex][g_Battle.UI.wSelectedIndex][1] - 67; PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, j), gpScreen, PAL_XY(x, y)); if (g_InputState.dwKeyPress & kKeyMenu) { g_Battle.UI.state = kBattleUISelectMove; } else if (g_InputState.dwKeyPress & kKeySearch) { PAL_BattleCommitAction(FALSE); } else if (g_InputState.dwKeyPress & (kKeyLeft | kKeyDown)) { if (g_Battle.UI.wSelectedIndex != 0) { g_Battle.UI.wSelectedIndex--; } else { g_Battle.UI.wSelectedIndex = gpGlobals->wMaxPartyMemberIndex; } } else if (g_InputState.dwKeyPress & (kKeyRight | kKeyUp)) { if (g_Battle.UI.wSelectedIndex < gpGlobals->wMaxPartyMemberIndex) { g_Battle.UI.wSelectedIndex++; } else { g_Battle.UI.wSelectedIndex = 0; } } break; case kBattleUISelectTargetEnemyAll: #ifdef PAL_CLASSIC // // Don't bother selecting // g_Battle.UI.wSelectedIndex = (WORD)-1; PAL_BattleCommitAction(FALSE); #else if (g_Battle.UI.wActionType == kBattleActionCoopMagic) { if (!PAL_BattleUIIsActionValid(kBattleActionCoopMagic)) { g_Battle.UI.state = kBattleUISelectMove; break; } } if (s_iFrame & 1) { // // Highlight all enemies // for (i = g_Battle.wMaxEnemyIndex; i >= 0; i--) { if (g_Battle.rgEnemy[i].wObjectID == 0) { continue; } x = PAL_X(g_Battle.rgEnemy[i].pos); y = PAL_Y(g_Battle.rgEnemy[i].pos); x -= PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame)) / 2; y -= PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame)); PAL_RLEBlitWithColorShift(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame), gpScreen, PAL_XY(x, y), 7); } } if (g_InputState.dwKeyPress & kKeyMenu) { g_Battle.UI.state = kBattleUISelectMove; } else if (g_InputState.dwKeyPress & kKeySearch) { g_Battle.UI.wSelectedIndex = (WORD)-1; PAL_BattleCommitAction(FALSE); } #endif break; case kBattleUISelectTargetPlayerAll: #ifdef PAL_CLASSIC // // Don't bother selecting // g_Battle.UI.wSelectedIndex = (WORD)-1; PAL_BattleCommitAction(FALSE); #else j = SPRITENUM_BATTLE_ARROW_SELECTEDPLAYER; if (s_iFrame & 1) { j = SPRITENUM_BATTLE_ARROW_SELECTEDPLAYER_RED; } for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++) { if (g_Battle.UI.wActionType == kBattleActionMagic) { w = gpGlobals->g.rgObject[g_Battle.UI.wObjectID].magic.wMagicNumber; if (gpGlobals->g.lprgMagic[w].wType == kMagicTypeTrance) { if (i != g_Battle.UI.wCurPlayerIndex) continue; } } // // Draw arrows on all players, despite of dead or not // x = g_rgPlayerPos[gpGlobals->wMaxPartyMemberIndex][i][0] - 8; y = g_rgPlayerPos[gpGlobals->wMaxPartyMemberIndex][i][1] - 67; PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, j), gpScreen, PAL_XY(x, y)); } if (g_InputState.dwKeyPress & kKeyMenu) { g_Battle.UI.state = kBattleUISelectMove; } else if (g_InputState.dwKeyPress & kKeySearch) { g_Battle.UI.wSelectedIndex = (WORD)-1; PAL_BattleCommitAction(FALSE); } #endif break; } end: // // Show the text message if there is one. // #ifndef PAL_CLASSIC if (SDL_GetTicks() < g_Battle.UI.dwMsgShowTime) { // // The text should be shown in a small window at the center of the screen // PAL_POS pos; int len = strlen(g_Battle.UI.szMsg); // // Create the window box // pos = PAL_XY(160 - len * 4, 40); PAL_CreateSingleLineBox(pos, (len + 1) / 2, FALSE); // // Show the text on the screen // pos = PAL_XY(PAL_X(pos) + 8 + ((len & 1) << 2), PAL_Y(pos) + 10); PAL_DrawText(g_Battle.UI.szMsg, pos, 0, FALSE, FALSE); } else if (g_Battle.UI.szNextMsg[0] != '\0') { strcpy(g_Battle.UI.szMsg, g_Battle.UI.szNextMsg); g_Battle.UI.dwMsgShowTime = SDL_GetTicks() + g_Battle.UI.wNextMsgDuration; g_Battle.UI.szNextMsg[0] = '\0'; } #endif // // Draw the numbers // for (i = 0; i < BATTLEUI_MAX_SHOWNUM; i++) { if (g_Battle.UI.rgShowNum[i].wNum > 0) { if ((SDL_GetTicks() - g_Battle.UI.rgShowNum[i].dwTime) / BATTLE_FRAME_TIME > 10) { g_Battle.UI.rgShowNum[i].wNum = 0; } else { PAL_DrawNumber(g_Battle.UI.rgShowNum[i].wNum, 5, 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), g_Battle.UI.rgShowNum[i].color, kNumAlignRight); } } } PAL_ClearKeyState(); }
VOID PAL_DrawText( LPCSTR lpszText, PAL_POS pos, BYTE bColor, BOOL fShadow, BOOL fUpdate ) /*++ Purpose: Draw text on the screen. Parameters: [IN] lpszText - the text to be drawn. [IN] pos - Position of the text. [IN] bColor - Color of the text. [IN] fShadow - TRUE if the text is shadowed or not. [IN] fUpdate - TRUE if update the screen area. Return value: None. --*/ { SDL_Rect rect, urect; WORD wChar; rect.x = PAL_X(pos); rect.y = PAL_Y(pos); urect.x = rect.x; urect.y = rect.y; urect.h = 16; urect.w = 0; while (*lpszText) { // // Draw the character // if (*lpszText & 0x80) { // // BIG-5 Chinese Character // wChar = SWAP16(((LPBYTE)lpszText)[0] | (((LPBYTE)lpszText)[1] << 8)); if (fShadow) { PAL_DrawCharOnSurface(wChar, gpScreen, PAL_XY(rect.x + 1, rect.y + 1), 0); PAL_DrawCharOnSurface(wChar, gpScreen, PAL_XY(rect.x + 1, rect.y), 0); } PAL_DrawCharOnSurface(wChar, gpScreen, PAL_XY(rect.x, rect.y), bColor); lpszText += 2; rect.x += 16; urect.w += 16; } else { // // ASCII character // if (fShadow) { PAL_DrawASCIICharOnSurface(*lpszText, gpScreen, PAL_XY(rect.x + 1, rect.y + 1), 0); PAL_DrawASCIICharOnSurface(*lpszText, gpScreen, PAL_XY(rect.x + 1, rect.y), 0); } PAL_DrawASCIICharOnSurface(*lpszText, gpScreen, PAL_XY(rect.x, rect.y), bColor); lpszText++; rect.x += 8; urect.w += 8; } } // // Update the screen area // if (fUpdate && urect.w > 0) { VIDEO_UpdateScreen(&urect); } }
VOID PAL_BattleEnemyEscape( VOID ) /*++ Purpose: Enemy flee the battle. Parameters: None. Return value: None. --*/ { int j, x, y, w; BOOL f = TRUE; UTIL_WriteLog(LOG_DEBUG, "[0x%08x][%s][%s] - %s", (long)PAL_BattleEnemyEscape, "PAL_BattleEnemyEscape", __FILE__, "start"); SOUND_Play(45); // // Show the animation // while (f) { f = FALSE; for (j = 0; j <= g_Battle.wMaxEnemyIndex; j++) { if (g_Battle.rgEnemy[j].wObjectID == 0) { continue; } x = PAL_X(g_Battle.rgEnemy[j].pos) - 5; y = PAL_Y(g_Battle.rgEnemy[j].pos); g_Battle.rgEnemy[j].pos = PAL_XY(x, y); w = PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgEnemy[j].lpSprite, 0)); if (x + w > 0) { f = TRUE; } } PAL_BattleMakeScene(); SDL_BlitSurface(g_Battle.lpSceneBuf, NULL, gpScreen, NULL); VIDEO_UpdateScreen(NULL); UTIL_Delay(10); } UTIL_Delay(500); g_Battle.BattleResult = kBattleResultTerminated; UTIL_WriteLog(LOG_DEBUG, "[0x%08x][%s][%s] - %s", (long)PAL_BattleEnemyEscape, "PAL_BattleEnemyEscape", __FILE__, "end"); }
static VOID PAL_BattleShowPlayerAttackAnim( WORD wPlayerIndex, BOOL fCritical ) /*++ Purpose: Show the physical attack effect for player. Parameters: [IN] wPlayerIndex - the index of the player. [IN] fCritical - TRUE if this is a critical hit. Return value: None. --*/ { WORD wPlayerRole = gpGlobals->rgParty[wPlayerIndex].wPlayerRole; SHORT sTarget = g_Battle.rgPlayer[wPlayerIndex].action.sTarget; int index, i, j; int enemy_x = 0, enemy_y = 0, enemy_h = 0, x, y, dist = 0; int w, h; DWORD dwTime; if (sTarget != -1) { enemy_x = PAL_X(g_Battle.rgEnemy[sTarget].pos); enemy_y = PAL_Y(g_Battle.rgEnemy[sTarget].pos); enemy_x += PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgEnemy[sTarget].lpSprite, g_Battle.rgEnemy[sTarget].wCurrentFrame)) / 2; enemy_h = PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgEnemy[sTarget].lpSprite, g_Battle.rgEnemy[sTarget].wCurrentFrame)); enemy_y += enemy_h; if (sTarget >= 3) { dist = (sTarget - wPlayerIndex) * 8; } } else { enemy_x = 150; enemy_y = 100; } index = gpGlobals->g.rgwBattleEffectIndex[gpGlobals->g.PlayerRoles.rgwSpriteNumInBattle[wPlayerRole]][1]; index *= 3; // // Play the attack voice // if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] > 0) { if (!fCritical) { SOUND_Play(gpGlobals->g.PlayerRoles.rgwAttackSound[wPlayerRole]); } else { SOUND_Play(gpGlobals->g.PlayerRoles.rgwCriticalSound[wPlayerRole]); } } // // Show the animation // x = enemy_x - dist + 64; y = enemy_y + dist + 20; g_Battle.rgPlayer[wPlayerIndex].wCurrentFrame = 8; w = PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgPlayer[wPlayerIndex].lpSprite, 8)); h = PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgPlayer[wPlayerIndex].lpSprite, 8)); g_Battle.rgPlayer[wPlayerIndex].pos = PAL_XY(x - w / 2, y - h); PAL_BattleDelay(2, 0); x -= 10; y -= 2; g_Battle.rgPlayer[wPlayerIndex].pos = PAL_XY(x - w / 2, y - h); PAL_BattleDelay(1, 0); g_Battle.rgPlayer[wPlayerIndex].wCurrentFrame = 9; x -= 16; y -= 4; SOUND_Play(gpGlobals->g.PlayerRoles.rgwWeaponSound[wPlayerRole]); x = enemy_x; y = enemy_y - enemy_h / 3 + 10; dwTime = SDL_GetTicks() + BATTLE_FRAME_TIME; for (i = 0; i < 3; i++) { LPCBITMAPRLE b = PAL_SpriteGetFrame(g_Battle.lpEffectSprite, index++); // // Clear the input state of previous frame. // PAL_ClearKeyState(); // // Wait for the time of one frame. Accept input here. // PAL_ProcessEvent(); while (SDL_GetTicks() <= dwTime) { PAL_ProcessEvent(); SDL_Delay(1); } // // Set the time of the next frame. // dwTime = SDL_GetTicks() + BATTLE_FRAME_TIME; // // Update the gesture of enemies. // for (j = 0; j <= g_Battle.wMaxEnemyIndex; j++) { if (g_Battle.rgEnemy[j].wObjectID == 0) { continue; } if (--g_Battle.rgEnemy[j].e.wIdleAnimSpeed == 0) { g_Battle.rgEnemy[j].wCurrentFrame++; g_Battle.rgEnemy[j].e.wIdleAnimSpeed = gpGlobals->g.lprgEnemy[gpGlobals->g.rgObject[g_Battle.rgEnemy[j].wObjectID].enemy.wEnemyID].wIdleAnimSpeed; } if (g_Battle.rgEnemy[j].wCurrentFrame >= g_Battle.rgEnemy[j].e.wIdleFrames) { g_Battle.rgEnemy[j].wCurrentFrame = 0; } } PAL_BattleMakeScene(); SDL_BlitSurface(g_Battle.lpSceneBuf, NULL, gpScreen, NULL); PAL_RLEBlitToSurface(b, gpScreen, PAL_XY(x - PAL_RLEGetWidth(b) / 2, y - PAL_RLEGetHeight(b))); x -= 16; y += 16; PAL_BattleUIUpdate(); if (i == 0) { if (sTarget == -1) { for (j = 0; j <= g_Battle.wMaxEnemyIndex; j++) { g_Battle.rgEnemy[j].iColorShift = 6; } } else { g_Battle.rgEnemy[sTarget].iColorShift = 6; } // // Flash the screen if it's a critical hit // if (fCritical) { SDL_FillRect(gpScreen, NULL, 15); } } VIDEO_UpdateScreen(NULL); if (i == 1) { g_Battle.rgPlayer[wPlayerIndex].pos = PAL_XY(PAL_X(g_Battle.rgPlayer[wPlayerIndex].pos) + 2, PAL_Y(g_Battle.rgPlayer[wPlayerIndex].pos) + 1); } } dist = 8; for (i = 0; i <= g_Battle.wMaxEnemyIndex; i++) { g_Battle.rgEnemy[i].iColorShift = 0; } if (sTarget == -1) { for (i = 0; i < 3; i++) { for (j = 0; j <= g_Battle.wMaxEnemyIndex; j++) { x = PAL_X(g_Battle.rgEnemy[j].pos); y = PAL_Y(g_Battle.rgEnemy[j].pos); x -= dist; y -= dist / 2; g_Battle.rgEnemy[j].pos = PAL_XY(x, y); } PAL_BattleDelay(1, 0); dist /= -2; } } else { x = PAL_X(g_Battle.rgEnemy[sTarget].pos); y = PAL_Y(g_Battle.rgEnemy[sTarget].pos); for (i = 0; i < 3; i++) { x -= dist; dist /= -2; y += dist; g_Battle.rgEnemy[sTarget].pos = PAL_XY(x, y); PAL_BattleDelay(1, 0); } } }
LPBOX PAL_CreateBox( PAL_POS pos, INT nRows, INT nColumns, INT iStyle, BOOL fSaveScreen ) /*++ Purpose: Create a box on the screen. Parameters: [IN] pos - position of the box. [IN] nRows - number of rows of the box. [IN] nColumns - number of columns of the box. [IN] iStyle - style of the box (0 or 1). [IN] fSaveScreen - whether save the used screen area or not. Return value: Pointer to a BOX structure. NULL if failed. If fSaveScreen is false, then always returns NULL. --*/ { int i, j, x, m, n; LPCBITMAPRLE rglpBorderBitmap[3][3]; LPBOX lpBox = NULL; SDL_Surface *save; SDL_Rect rect; // // Get the bitmaps // for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { rglpBorderBitmap[i][j] = PAL_SpriteGetFrame(gpSpriteUI, i * 3 + j + iStyle * 9); } } rect.x = PAL_X(pos); rect.y = PAL_Y(pos); rect.w = 0; rect.h = 0; // // Get the total width and total height of the box // for (i = 0; i < 3; i++) { if (i == 1) { rect.w += PAL_RLEGetWidth(rglpBorderBitmap[0][i]) * nColumns; rect.h += PAL_RLEGetHeight(rglpBorderBitmap[i][0]) * nRows; } else { rect.w += PAL_RLEGetWidth(rglpBorderBitmap[0][i]); rect.h += PAL_RLEGetHeight(rglpBorderBitmap[i][0]); } } if (fSaveScreen) { // // Save the used part of the screen // save = SDL_CreateRGBSurface(gpScreen->flags, rect.w, rect.h, 8, gpScreen->format->Rmask, gpScreen->format->Gmask, gpScreen->format->Bmask, gpScreen->format->Amask); if (save == NULL) { return NULL; } lpBox = (LPBOX)calloc(1, sizeof(BOX)); if (lpBox == NULL) { SDL_FreeSurface(save); return NULL; } SDL_BlitSurface(gpScreen, &rect, save, NULL); lpBox->lpSavedArea = save; lpBox->pos = pos; lpBox->wWidth = rect.w; lpBox->wHeight = rect.h; } // // Border takes 2 additional rows and columns... // nRows += 2; nColumns += 2; // // Draw the box // for (i = 0; i < nRows; i++) { x = rect.x; m = (i == 0) ? 0 : ((i == nRows - 1) ? 2 : 1); for (j = 0; j < nColumns; j++) { n = (j == 0) ? 0 : ((j == nColumns - 1) ? 2 : 1); PAL_RLEBlitToSurface(rglpBorderBitmap[m][n], gpScreen, PAL_XY(x, rect.y)); x += PAL_RLEGetWidth(rglpBorderBitmap[m][n]); } rect.y += PAL_RLEGetHeight(rglpBorderBitmap[m][0]); } return lpBox; }
VOID PAL_BattlePlayerEscape( VOID ) /*++ Purpose: Player flee the battle. Parameters: None. Return value: None. --*/ { int i, j; WORD wPlayerRole; UTIL_WriteLog(LOG_DEBUG, "[0x%08x][%s][%s] - %s", (long)PAL_BattlePlayerEscape, "PAL_BattlePlayerEscape", __FILE__, "start"); SOUND_Play(45); PAL_BattleUpdateFighters(); for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++) { wPlayerRole = gpGlobals->rgParty[i].wPlayerRole; if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] > 0) { g_Battle.rgPlayer[i].wCurrentFrame = 0; } } for (i = 0; i < 16; i++) { for (j = 0; j <= gpGlobals->wMaxPartyMemberIndex; j++) { wPlayerRole = gpGlobals->rgParty[j].wPlayerRole; if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] > 0) { // // TODO: This is still not the same as the original game // switch (j) { case 0: if (gpGlobals->wMaxPartyMemberIndex > 0) { g_Battle.rgPlayer[j].pos = PAL_XY(PAL_X(g_Battle.rgPlayer[j].pos) + 4, PAL_Y(g_Battle.rgPlayer[j].pos) + 6); break; } case 1: g_Battle.rgPlayer[j].pos = PAL_XY(PAL_X(g_Battle.rgPlayer[j].pos) + 4, PAL_Y(g_Battle.rgPlayer[j].pos) + 4); break; case 2: g_Battle.rgPlayer[j].pos = PAL_XY(PAL_X(g_Battle.rgPlayer[j].pos) + 6, PAL_Y(g_Battle.rgPlayer[j].pos) + 3); break; default: assert(FALSE); // Not possible break; } } } PAL_BattleDelay(1, 0, FALSE); } // // Remove all players from the screen // for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++) { g_Battle.rgPlayer[i].pos = PAL_XY(9999, 9999); } PAL_BattleDelay(1, 0, FALSE); g_Battle.BattleResult = kBattleResultFleed; UTIL_WriteLog(LOG_DEBUG, "[0x%08x][%s][%s] - %s", (long)PAL_BattlePlayerEscape, "PAL_BattlePlayerEscape", __FILE__, "end"); }
VOID PAL_DrawNumber( UINT iNum, UINT nLength, PAL_POS pos, NUMCOLOR color, NUMALIGN align ) /*++ Purpose: Draw the specified number with the bitmaps in the UI sprite. Parameters: [IN] iNum - the number to be drawn. [IN] nLength - max. length of the number. [IN] pos - position on the screen. [IN] color - color of the number (yellow or blue). [IN] align - align mode of the number. Return value: None. --*/ { UINT nActualLength, i; int x, y; LPCBITMAPRLE rglpBitmap[10]; // // Get the bitmaps. Blue starts from 29, Cyan from 56, Yellow from 19. // x = (color == kNumColorBlue) ? 29 : ((color == kNumColorCyan) ? 56 : 19); for (i = 0; i < 10; i++) { rglpBitmap[i] = PAL_SpriteGetFrame(gpSpriteUI, (UINT)x + i); } i = iNum; nActualLength = 0; // // Calculate the actual length of the number. // while (i > 0) { i /= 10; nActualLength++; } if (nActualLength > nLength) { nActualLength = nLength; } else if (nActualLength == 0) { nActualLength = 1; } x = PAL_X(pos) - 6; y = PAL_Y(pos); switch (align) { case kNumAlignLeft: x += 6 * nActualLength; break; case kNumAlignMid: x += 3 * (nLength + nActualLength); break; case kNumAlignRight: x += 6 * nLength; break; } // // Draw the number. // while (nActualLength-- > 0) { PAL_RLEBlitToSurface(rglpBitmap[iNum % 10], gpScreen, PAL_XY(x, y)); x -= 6; iNum /= 10; } }
static VOID PAL_BattleShowPlayerUseItemAnim( WORD wPlayerIndex, WORD wObjectID, SHORT sTarget ) /*++ Purpose: Show the "use item" effect for player. Parameters: [IN] wPlayerIndex - the index of the player. [IN] wObjectID - the object ID of the item to be used. [IN] sTarget - the target player of the action. Return value: None. --*/ { int i, j; PAL_BattleDelay(4, 0); g_Battle.rgPlayer[wPlayerIndex].pos = PAL_XY(PAL_X(g_Battle.rgPlayer[wPlayerIndex].pos) - 15, PAL_Y(g_Battle.rgPlayer[wPlayerIndex].pos) - 7); g_Battle.rgPlayer[wPlayerIndex].wCurrentFrame = 5; SOUND_Play(28); for (i = 0; i <= 6; i++) { if (sTarget == -1) { for (j = 0; j <= gpGlobals->wMaxPartyMemberIndex; j++) { g_Battle.rgPlayer[j].iColorShift = i; } } else { g_Battle.rgPlayer[sTarget].iColorShift = i; } PAL_BattleDelay(1, wObjectID); } for (i = 5; i >= 0; i--) { if (sTarget == -1) { for (j = 0; j <= gpGlobals->wMaxPartyMemberIndex; j++) { g_Battle.rgPlayer[j].iColorShift = i; } } else { g_Battle.rgPlayer[sTarget].iColorShift = i; } PAL_BattleDelay(1, wObjectID); } }
LPBOX PAL_CreateSingleLineBox( PAL_POS pos, INT nLen, BOOL fSaveScreen ) /*++ Purpose: Create a single-line box on the screen. Parameters: [IN] pos - position of the box. [IN] nLen - length of the box. [IN] fSaveScreen - whether save the used screen area or not. Return value: Pointer to a BOX structure. NULL if failed. If fSaveScreen is false, then always returns NULL. --*/ { static const int iNumLeftSprite = 44; static const int iNumMidSprite = 45; static const int iNumRightSprite = 46; LPCBITMAPRLE lpBitmapLeft; LPCBITMAPRLE lpBitmapMid; LPCBITMAPRLE lpBitmapRight; SDL_Surface *save; SDL_Rect rect; LPBOX lpBox = NULL; int i; // // Get the bitmaps // lpBitmapLeft = PAL_SpriteGetFrame(gpSpriteUI, iNumLeftSprite); lpBitmapMid = PAL_SpriteGetFrame(gpSpriteUI, iNumMidSprite); lpBitmapRight = PAL_SpriteGetFrame(gpSpriteUI, iNumRightSprite); rect.x = PAL_X(pos); rect.y = PAL_Y(pos); // // Get the total width and total height of the box // rect.w = PAL_RLEGetWidth(lpBitmapLeft) + PAL_RLEGetWidth(lpBitmapRight); rect.w += PAL_RLEGetWidth(lpBitmapMid) * nLen; rect.h = PAL_RLEGetHeight(lpBitmapLeft); if (fSaveScreen) { // // Save the used part of the screen // save = SDL_CreateRGBSurface(gpScreen->flags, rect.w, rect.h, 8, gpScreen->format->Rmask, gpScreen->format->Gmask, gpScreen->format->Bmask, gpScreen->format->Amask); if (save == NULL) { return NULL; } lpBox = (LPBOX)calloc(1, sizeof(BOX)); if (lpBox == NULL) { SDL_FreeSurface(gpScreen); return NULL; } SDL_BlitSurface(gpScreen, &rect, save, NULL); lpBox->pos = pos; lpBox->lpSavedArea = save; lpBox->wHeight = (WORD)rect.w; lpBox->wWidth = (WORD)rect.h; } // // Draw the box // PAL_RLEBlitToSurface(lpBitmapLeft, gpScreen, pos); rect.x += PAL_RLEGetWidth(lpBitmapLeft); for (i = 0; i < nLen; i++) { PAL_RLEBlitToSurface(lpBitmapMid, gpScreen, PAL_XY(rect.x, rect.y)); rect.x += PAL_RLEGetWidth(lpBitmapMid); } PAL_RLEBlitToSurface(lpBitmapRight, gpScreen, PAL_XY(rect.x, rect.y)); return lpBox; }
VOID PAL_DrawCharOnSurface( WORD wChar, SDL_Surface *lpSurface, PAL_POS pos, BYTE bColor ) /*++ Purpose: Draw a BIG-5 Chinese character on a surface. Parameters: [IN] wChar - the character to be drawn (in GB2312/BIG5). [OUT] lpSurface - the destination surface. [IN] pos - the destination location of the surface. [IN] bColor - the color of the character. Return value: None. --*/ { int i, j, dx; int x = PAL_X(pos), y = PAL_Y(pos); LPBYTE pChar; BYTE ch1, ch2; // // Check for NULL pointer. // if (lpSurface == NULL) { return; } // // Locate for this character in the font lib. // ch1 = wChar & 0xff; ch2 = wChar >> 8; if (fIsBig5) { if (ch2 < 0xa1) pChar = &big5font[((ch1 - 0xA1) * 157 + ch2 - 0x40) << 5] + 8; else pChar = &big5font[((ch1 - 0xA1) * 157 + 63 + ch2 - 0xA1) << 5] + 8; } else { if (!is_gb(ch1, ch2)) { return; } pChar = &gbfont[((ch1 - 0xa1) * 94 + (ch2 - 0xa1)) * 32]; } if (pChar == NULL) return; // // Draw the character to the surface. // if (y >= lpSurface->h) return; y *= lpSurface->pitch; for (i = 0; i < 32; i++) { dx = x + ((i & 1) << 3); for (j = 0; j < 8; j++) { if (pChar[i] & (1 << (7 - j))) { if (dx < lpSurface->w) { ((LPBYTE)(lpSurface->pixels))[y + dx] = bColor; } else { break; } } dx++; } y += (i & 1) * lpSurface->pitch; if (y / lpSurface->pitch >= lpSurface->h) { break; } } }
static VOID PAL_SceneDrawSprites( VOID ) /*++ Purpose: Draw all the sprites to scene. Parameters: None. Return value: None. --*/ { int i, x, y, vy; g_nSpriteToDraw = 0; // // Put all the sprites to be drawn into our array. // // // Players // for (i = 0; i <= (short)gpGlobals->wMaxPartyMemberIndex + gpGlobals->nFollower; i++) { LPCBITMAPRLE lpBitmap = PAL_SpriteGetFrame(PAL_GetPlayerSprite((BYTE)i), gpGlobals->rgParty[i].wFrame); if (lpBitmap == NULL) { continue; } // // Add it to our array // PAL_AddSpriteToDraw(lpBitmap, gpGlobals->rgParty[i].x - PAL_RLEGetWidth(lpBitmap) / 2, gpGlobals->rgParty[i].y + gpGlobals->wLayer + 10, gpGlobals->wLayer + 6); // // Calculate covering tiles on the map // PAL_CalcCoverTiles(&g_rgSpriteToDraw[g_nSpriteToDraw - 1]); } // // Event Objects (Monsters/NPCs/others) // for (i = gpGlobals->g.rgScene[gpGlobals->wNumScene - 1].wEventObjectIndex; i < gpGlobals->g.rgScene[gpGlobals->wNumScene].wEventObjectIndex; i++) { LPCBITMAPRLE lpFrame; LPCSPRITE lpSprite; LPEVENTOBJECT lpEvtObj = &(gpGlobals->g.lprgEventObject[i]); int iFrame; if (lpEvtObj->sState == kObjStateHidden || lpEvtObj->sVanishTime > 0 || lpEvtObj->sState < 0) { continue; } // // Get the sprite // lpSprite = PAL_GetEventObjectSprite((WORD)i + 1); if (lpSprite == NULL) { continue; } iFrame = lpEvtObj->wCurrentFrameNum; if (lpEvtObj->nSpriteFrames == 3) { // // walking character // if (iFrame == 2) { iFrame = 0; } if (iFrame == 3) { iFrame = 2; } } lpFrame = PAL_SpriteGetFrame(lpSprite, lpEvtObj->wDirection * lpEvtObj->nSpriteFrames + iFrame); if (lpFrame == NULL) { continue; } // // Calculate the coordinate and check if outside the screen // x = (SHORT)lpEvtObj->x - PAL_X(gpGlobals->viewport); x -= PAL_RLEGetWidth(lpFrame) / 2; if (x >= 320 || x < -(int)PAL_RLEGetWidth(lpFrame)) { // // outside the screen; skip it // continue; } y = (SHORT)lpEvtObj->y - PAL_Y(gpGlobals->viewport); y += lpEvtObj->sLayer * 8 + 9; vy = y - PAL_RLEGetHeight(lpFrame) - lpEvtObj->sLayer * 8 + 2; if (vy >= 200 || vy < -(int)PAL_RLEGetHeight(lpFrame)) { // // outside the screen; skip it // continue; } // // Add it into the array // PAL_AddSpriteToDraw(lpFrame, x, y, lpEvtObj->sLayer * 8 + 2); // // Calculate covering map tiles // PAL_CalcCoverTiles(&g_rgSpriteToDraw[g_nSpriteToDraw - 1]); } // // All sprites are now in our array; sort them by their vertical positions. // for (x = 0; x < g_nSpriteToDraw - 1; x++) { SPRITE_TO_DRAW tmp; BOOL fSwap = FALSE; for (y = 0; y < g_nSpriteToDraw - 1 - x; y++) { if (PAL_Y(g_rgSpriteToDraw[y].pos) > PAL_Y(g_rgSpriteToDraw[y + 1].pos)) { fSwap = TRUE; tmp = g_rgSpriteToDraw[y]; g_rgSpriteToDraw[y] = g_rgSpriteToDraw[y + 1]; g_rgSpriteToDraw[y + 1] = tmp; } } if (!fSwap) { break; } } // // Draw all the sprites to the screen. // for (i = 0; i < g_nSpriteToDraw; i++) { SPRITE_TO_DRAW *p = &g_rgSpriteToDraw[i]; x = PAL_X(p->pos); y = PAL_Y(p->pos) - PAL_RLEGetHeight(p->lpSpriteFrame) - p->iLayer; PAL_RLEBlitToSurface(p->lpSpriteFrame, gpScreen, PAL_XY(x, y)); } }
VOID PAL_DrawCharOnSurface( WORD wChar, SDL_Surface *lpSurface, PAL_POS pos, BYTE bColor ) /*++ Purpose: Draw a BIG-5 Chinese character on a surface. Parameters: [IN] wChar - the character to be drawn (in BIG-5). [OUT] lpSurface - the destination surface. [IN] pos - the destination location of the surface. [IN] bColor - the color of the character. Return value: None. --*/ { int i, j, dx; int x = PAL_X(pos), y = PAL_Y(pos); LPBYTE pChar; // // Check for NULL pointer. // if (lpSurface == NULL || gpFont == NULL) { return; } // // Locate for this character in the font lib. // for (i = 0; i < gpFont->nChar; i++) { if (gpFont->lpBufChar[i] == wChar) { break; } } if (i >= gpFont->nChar) { // // This character does not exist in the font lib. // return; } pChar = gpFont->lpBufGlyph + i * 30; // // Draw the character to the surface. // y *= lpSurface->pitch; for (i = 0; i < 30; i++) { dx = x + ((i & 1) << 3); for (j = 0; j < 8; j++) { if (pChar[i] & (1 << (7 - j))) { ((LPBYTE)(lpSurface->pixels))[y + dx] = bColor; } dx++; } y += (i & 1) * lpSurface->pitch; } }
VOID PAL_ShowDialogText( LPCSTR lpszText ) /*++ Purpose: Show one line of the dialog text. Parameters: [IN] lpszText - the text to be shown. Return value: None. --*/ { SDL_Rect rect; int x, y, len = strlen(lpszText); PAL_ClearKeyState(); g_TextLib.bIcon = 0; if (gpGlobals->fInBattle && !g_fUpdatedInBattle) { // // Update the screen in battle, or the graphics may seem messed up // VIDEO_UpdateScreen(NULL); g_fUpdatedInBattle = TRUE; } if (g_TextLib.nCurrentDialogLine > 3) { // // The rest dialogs should be shown in the next page. // PAL_DialogWaitForKey(); g_TextLib.nCurrentDialogLine = 0; VIDEO_RestoreScreen(); VIDEO_UpdateScreen(NULL); } x = PAL_X(g_TextLib.posDialogText); y = PAL_Y(g_TextLib.posDialogText) + g_TextLib.nCurrentDialogLine * 18; if (g_TextLib.bDialogPosition == kDialogCenterWindow) { // // The text should be shown in a small window at the center of the screen // #ifndef PAL_CLASSIC if (gpGlobals->fInBattle && g_Battle.BattleResult == kBattleResultOnGoing) { PAL_BattleUIShowText(lpszText, 1400); } else #endif { PAL_POS pos; LPBOX lpBox; // // Create the window box // pos = PAL_XY(PAL_X(g_TextLib.posDialogText) - len * 4, PAL_Y(g_TextLib.posDialogText)); lpBox = PAL_CreateSingleLineBox(pos, (len + 1) / 2, TRUE); rect.x = PAL_X(pos); rect.y = PAL_Y(pos); rect.w = 320 - rect.x * 2 + 32; rect.h = 64; // // Show the text on the screen // pos = PAL_XY(PAL_X(pos) + 8 + ((len & 1) << 2), PAL_Y(pos) + 10); PAL_DrawText(lpszText, pos, 0, FALSE, FALSE); VIDEO_UpdateScreen(&rect); PAL_DialogWaitForKey(); // // Delete the box // PAL_DeleteBox(lpBox); VIDEO_UpdateScreen(&rect); PAL_EndDialog(); } } else { if (g_TextLib.nCurrentDialogLine == 0 && g_TextLib.bDialogPosition != kDialogCenter && (BYTE)lpszText[len - 1] == 0x47 && (BYTE)lpszText[len - 2] == 0xA1) { // // name of character // PAL_DrawText(lpszText, g_TextLib.posDialogTitle, FONT_COLOR_CYAN_ALT, TRUE, TRUE); } else { // // normal texts // char text[3]; if (!g_TextLib.fPlayingRNG && g_TextLib.nCurrentDialogLine == 0) { // // Save the screen before we show the first line of dialog // VIDEO_BackupScreen(); } while (lpszText != NULL && *lpszText != '\0') { switch (*lpszText) { case '-': // // Set the font color to Cyan // if (g_TextLib.bCurrentFontColor == FONT_COLOR_CYAN) { g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT; } else { g_TextLib.bCurrentFontColor = FONT_COLOR_CYAN; } lpszText++; break; case '\'': // // Set the font color to Red // if (g_TextLib.bCurrentFontColor == FONT_COLOR_RED) { g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT; } else { g_TextLib.bCurrentFontColor = FONT_COLOR_RED; } lpszText++; break; case '\"': // // Set the font color to Yellow // if (g_TextLib.bCurrentFontColor == FONT_COLOR_YELLOW) { g_TextLib.bCurrentFontColor = FONT_COLOR_DEFAULT; } else { g_TextLib.bCurrentFontColor = FONT_COLOR_YELLOW; } lpszText++; break; case '$': // // Set the delay time of text-displaying // g_TextLib.iDelayTime = atoi(lpszText + 1) * 10 / 7; lpszText += 3; break; case '~': // // Delay for a period and quit // UTIL_Delay(atoi(lpszText + 1) * 80 / 7); g_TextLib.nCurrentDialogLine = 0; g_TextLib.fUserSkip = FALSE; return; // don't go further case ')': // // Set the waiting icon // g_TextLib.bIcon = 1; lpszText++; break; case '(': // // Set the waiting icon // g_TextLib.bIcon = 2; lpszText++; break; case '\\': lpszText++; default: if (*lpszText & 0x80) { text[0] = lpszText[0]; text[1] = lpszText[1]; text[2] = '\0'; lpszText += 2; } else { text[0] = *lpszText; text[1] = '\0'; lpszText++; } PAL_DrawText(text, PAL_XY(x, y), g_TextLib.bCurrentFontColor, TRUE, TRUE); x += ((text[0] & 0x80) ? 16 : 8); if (!g_TextLib.fUserSkip) { PAL_ClearKeyState(); UTIL_Delay(g_TextLib.iDelayTime * 8); if (g_InputState.dwKeyPress & (kKeySearch | kKeyMenu)) { // // User pressed a key to skip the dialog // g_TextLib.fUserSkip = TRUE; } } } } g_TextLib.posIcon = PAL_XY(x, y); g_TextLib.nCurrentDialogLine++; } } }
/*++ Generate the battle scene into the scene buffer. --*/ VOID PAL_BattleMakeScene(void) { int i; PAL_POS pos; LPBYTE pSrc, pDst; BYTE b; // // Draw the background // pSrc = g_Battle.lpBackground->pixels; pDst = g_Battle.lpSceneBuf->pixels; for (i = 0; i < g_Battle.lpSceneBuf->pitch * g_Battle.lpSceneBuf->h; i++) { b = (*pSrc & 0x0F); b += g_Battle.sBackgroundColorShift; if (b & 0x80) { b = 0; } else if (b & 0x70) { b = 0x0F; } *pDst = (b | (*pSrc & 0xF0)); ++pSrc; ++pDst; } PAL_ApplyWave(g_Battle.lpSceneBuf); // // Draw the enemies // for (i = g_Battle.wMaxEnemyIndex; i >= 0; i--) { pos = g_Battle.rgEnemy[i].pos; if (g_Battle.rgEnemy[i].rgwStatus[kStatusConfused] > 0 && g_Battle.rgEnemy[i].rgwStatus[kStatusSleep] == 0 && g_Battle.rgEnemy[i].rgwStatus[kStatusParalyzed] == 0) { // // Enemy is confused // pos = PAL_XY(PAL_X(pos) + RandomLong(-1, 1), PAL_Y(pos)); } pos = PAL_XY(PAL_X(pos) - PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame)) / 2, PAL_Y(pos) - PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame))); if (g_Battle.rgEnemy[i].wObjectID != 0) { if (g_Battle.rgEnemy[i].iColorShift) { PAL_RLEBlitWithColorShift(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame), g_Battle.lpSceneBuf, pos, g_Battle.rgEnemy[i].iColorShift); } else { PAL_RLEBlitToSurface(PAL_SpriteGetFrame(g_Battle.rgEnemy[i].lpSprite, g_Battle.rgEnemy[i].wCurrentFrame), g_Battle.lpSceneBuf, pos); } } } if (g_Battle.lpSummonSprite != NULL) { // // Draw the summoned god // pos = PAL_XY(PAL_X(g_Battle.posSummon) - PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.lpSummonSprite, g_Battle.iSummonFrame)) / 2, PAL_Y(g_Battle.posSummon) - PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.lpSummonSprite, g_Battle.iSummonFrame))); PAL_RLEBlitToSurface(PAL_SpriteGetFrame(g_Battle.lpSummonSprite, g_Battle.iSummonFrame), g_Battle.lpSceneBuf, pos); } else { // // Draw the players // for (i = gpGlobals->wMaxPartyMemberIndex; i >= 0; i--) { pos = g_Battle.rgPlayer[i].pos; if (gpGlobals->rgPlayerStatus[gpGlobals->rgParty[i].wPlayerRole][kStatusConfused] != 0 && gpGlobals->rgPlayerStatus[gpGlobals->rgParty[i].wPlayerRole][kStatusSleep] == 0 && gpGlobals->rgPlayerStatus[gpGlobals->rgParty[i].wPlayerRole][kStatusParalyzed] == 0 && gpGlobals->g.PlayerRoles.rgwHP[gpGlobals->rgParty[i].wPlayerRole] > 0) { // // Player is confused // continue; } pos = PAL_XY(PAL_X(pos) - PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame)) / 2, PAL_Y(pos) - PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame))); if (g_Battle.rgPlayer[i].iColorShift != 0) { PAL_RLEBlitWithColorShift(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame), g_Battle.lpSceneBuf, pos, g_Battle.rgPlayer[i].iColorShift); } else if (g_Battle.iHidingTime == 0) { PAL_RLEBlitToSurface(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame), g_Battle.lpSceneBuf, pos); } } // // Confused players should be drawn on top of normal players // for (i = gpGlobals->wMaxPartyMemberIndex; i >= 0; i--) { if (gpGlobals->rgPlayerStatus[gpGlobals->rgParty[i].wPlayerRole][kStatusConfused] != 0 && gpGlobals->rgPlayerStatus[gpGlobals->rgParty[i].wPlayerRole][kStatusSleep] == 0 && gpGlobals->rgPlayerStatus[gpGlobals->rgParty[i].wPlayerRole][kStatusParalyzed] == 0 && gpGlobals->g.PlayerRoles.rgwHP[gpGlobals->rgParty[i].wPlayerRole] > 0) { // // Player is confused // pos = PAL_XY(PAL_X(g_Battle.rgPlayer[i].pos), PAL_Y(g_Battle.rgPlayer[i].pos) + RandomLong(-1, 1)); pos = PAL_XY(PAL_X(pos) - PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame)) / 2, PAL_Y(pos) - PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame))); if (g_Battle.rgPlayer[i].iColorShift != 0) { PAL_RLEBlitWithColorShift(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame), g_Battle.lpSceneBuf, pos, g_Battle.rgPlayer[i].iColorShift); } else if (g_Battle.iHidingTime == 0) { PAL_RLEBlitToSurface(PAL_SpriteGetFrame(g_Battle.rgPlayer[i].lpSprite, g_Battle.rgPlayer[i].wCurrentFrame), g_Battle.lpSceneBuf, pos); } } } } }
VOID PAL_PlayerInfoBox( PAL_POS pos, WORD wPlayerRole, INT iTimeMeter, BYTE bTimeMeterColor, BOOL fUpdate ) /*++ Purpose: Show the player info box. Parameters: [IN] pos - the top-left corner position of the box. [IN] wPlayerRole - the player role ID to be shown. [IN] iTimeMeter - the value of time meter. 0 = empty, 100 = full. [IN] bTimeMeterColor - the color of time meter. [IN] fUpdate - whether to update the screen area or not. Return value: None. --*/ { SDL_Rect rect; BYTE bPoisonColor; int i, iPartyIndex; WORD wMaxLevel, w; const BYTE rgStatusPos[kStatusAll][2] = { {35, 19}, // confused {0, 0}, // slow {54, 1}, // sleep {55, 20}, // silence {0, 0}, // puppet {0, 0}, // bravery {0, 0}, // protect {0, 0}, // haste {0, 0}, // dualattack }; const WORD rgwStatusWord[kStatusAll] = { 0x1D, // confused 0x00, // slow 0x1C, // sleep 0x1A, // silence 0x00, // puppet 0x00, // bravery 0x00, // protect 0x00, // haste 0x00, // dualattack }; const BYTE rgbStatusColor[kStatusAll] = { 0x5F, // confused 0x00, // slow 0x0E, // sleep 0x3C, // silence 0x00, // puppet 0x00, // bravery 0x00, // protect 0x00, // haste 0x00, // dualattack }; // // Draw the box // PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_PLAYERINFOBOX), gpScreen, pos); // // Draw the player face // wMaxLevel = 0; bPoisonColor = 0xFF; for (iPartyIndex = 0; iPartyIndex <= gpGlobals->wMaxPartyMemberIndex; iPartyIndex++) { if (gpGlobals->rgParty[iPartyIndex].wPlayerRole == wPlayerRole) { break; } } if (iPartyIndex <= gpGlobals->wMaxPartyMemberIndex) { for (i = 0; i < MAX_POISONS; i++) { w = gpGlobals->rgPoisonStatus[i][iPartyIndex].wPoisonID; if (w != 0 && gpGlobals->g.rgObject[w].poison.wPoisonLevel <= 3) { if (gpGlobals->g.rgObject[w].poison.wPoisonLevel >= wMaxLevel) { wMaxLevel = gpGlobals->g.rgObject[w].poison.wPoisonLevel; bPoisonColor = (BYTE)(gpGlobals->g.rgObject[w].poison.wColor); } } } } if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] == 0) { // // Always use the black/white color for dead players // and do not use the time meter // bPoisonColor = 0; iTimeMeter = 0; } if (bPoisonColor == 0xFF) { PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_PLAYERFACE_FIRST + wPlayerRole), gpScreen, PAL_XY(PAL_X(pos) - 2, PAL_Y(pos) - 4)); } else { PAL_RLEBlitMonoColor(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_PLAYERFACE_FIRST + wPlayerRole), gpScreen, PAL_XY(PAL_X(pos) - 2, PAL_Y(pos) - 4), bPoisonColor, 0); } #ifndef PAL_CLASSIC // // Draw a border for the Time Meter // rect.x = PAL_X(pos) + 31; rect.y = PAL_Y(pos) + 4; rect.w = 1; rect.h = 6; SDL_FillRect(gpScreen, &rect, 0xBD); rect.x += 39; SDL_FillRect(gpScreen, &rect, 0xBD); rect.x = PAL_X(pos) + 32; rect.y = PAL_Y(pos) + 3; rect.w = 38; rect.h = 1; SDL_FillRect(gpScreen, &rect, 0xBD); rect.y += 7; SDL_FillRect(gpScreen, &rect, 0xBD); // // Draw the Time meter bar // if (iTimeMeter >= 100) { rect.x = PAL_X(pos) + 33; rect.y = PAL_Y(pos) + 6; rect.w = 36; rect.h = 2; SDL_FillRect(gpScreen, &rect, 0x2C); } else if (iTimeMeter > 0) { rect.x = PAL_X(pos) + 33; rect.y = PAL_Y(pos) + 5; rect.w = iTimeMeter * 36 / 100; rect.h = 4; SDL_FillRect(gpScreen, &rect, bTimeMeterColor); } #endif // // Draw the HP and MP value // #ifdef PAL_CLASSIC PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_SLASH), gpScreen, PAL_XY(PAL_X(pos) + 49, PAL_Y(pos) + 6)); PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwMaxHP[wPlayerRole], 4, PAL_XY(PAL_X(pos) + 47, PAL_Y(pos) + 8), kNumColorYellow, kNumAlignRight); PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole], 4, PAL_XY(PAL_X(pos) + 26, PAL_Y(pos) + 5), kNumColorYellow, kNumAlignRight); PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_SLASH), gpScreen, PAL_XY(PAL_X(pos) + 49, PAL_Y(pos) + 22)); PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwMaxMP[wPlayerRole], 4, PAL_XY(PAL_X(pos) + 47, PAL_Y(pos) + 24), kNumColorCyan, kNumAlignRight); PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwMP[wPlayerRole], 4, PAL_XY(PAL_X(pos) + 26, PAL_Y(pos) + 21), kNumColorCyan, kNumAlignRight); #else PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_SLASH), gpScreen, PAL_XY(PAL_X(pos) + 49, PAL_Y(pos) + 14)); PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwMaxHP[wPlayerRole], 4, PAL_XY(PAL_X(pos) + 47, PAL_Y(pos) + 16), kNumColorYellow, kNumAlignRight); PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole], 4, PAL_XY(PAL_X(pos) + 26, PAL_Y(pos) + 13), kNumColorYellow, kNumAlignRight); PAL_RLEBlitToSurface(PAL_SpriteGetFrame(gpSpriteUI, SPRITENUM_SLASH), gpScreen, PAL_XY(PAL_X(pos) + 49, PAL_Y(pos) + 24)); PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwMaxMP[wPlayerRole], 4, PAL_XY(PAL_X(pos) + 47, PAL_Y(pos) + 26), kNumColorCyan, kNumAlignRight); PAL_DrawNumber(gpGlobals->g.PlayerRoles.rgwMP[wPlayerRole], 4, PAL_XY(PAL_X(pos) + 26, PAL_Y(pos) + 23), kNumColorCyan, kNumAlignRight); #endif // // Draw Statuses // if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] > 0) { for (i = 0; i < kStatusAll; i++) { if (gpGlobals->rgPlayerStatus[wPlayerRole][i] > 0 && rgwStatusWord[i] != 0) { PAL_DrawText(PAL_GetWord(rgwStatusWord[i]), PAL_XY(PAL_X(pos) + rgStatusPos[i][0], PAL_Y(pos) + rgStatusPos[i][1]), rgbStatusColor[i], TRUE, FALSE); } } } // // Update the screen area if needed // if (fUpdate) { rect.x = PAL_X(pos) - 2; rect.y = PAL_Y(pos) - 4; rect.w = 77; rect.h = 39; VIDEO_UpdateScreen(&rect); } }
/*++ Player flee the battle. --*/ VOID PAL_BattlePlayerEscape(void) { int i, j; WORD wPlayerRole; SOUND_Play(45); PAL_BattleUpdateFighters(); for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++) { wPlayerRole = gpGlobals->rgParty[i].wPlayerRole; if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] > 0) { g_Battle.rgPlayer[i].wCurrentFrame = 0; } } for (i = 0; i < 16; i++) { for (j = 0; j <= gpGlobals->wMaxPartyMemberIndex; j++) { wPlayerRole = gpGlobals->rgParty[j].wPlayerRole; if (gpGlobals->g.PlayerRoles.rgwHP[wPlayerRole] > 0) { // // TODO: This is still not the same as the original game // switch (j) { case 0: if (gpGlobals->wMaxPartyMemberIndex > 0) { g_Battle.rgPlayer[j].pos = PAL_XY(PAL_X(g_Battle.rgPlayer[j].pos) + 4, PAL_Y(g_Battle.rgPlayer[j].pos) + 6); break; } case 1: g_Battle.rgPlayer[j].pos = PAL_XY(PAL_X(g_Battle.rgPlayer[j].pos) + 4, PAL_Y(g_Battle.rgPlayer[j].pos) + 4); break; case 2: g_Battle.rgPlayer[j].pos = PAL_XY(PAL_X(g_Battle.rgPlayer[j].pos) + 6, PAL_Y(g_Battle.rgPlayer[j].pos) + 3); break; // @@@ - extra 4th role: case 3: g_Battle.rgPlayer[j].pos = PAL_XY(PAL_X(g_Battle.rgPlayer[j].pos) + 6, PAL_Y(g_Battle.rgPlayer[j].pos) + 3); break; default: assert(FALSE); // Not possible break; } } } PAL_BattleDelay(1, 0, FALSE); } // // Remove all players from the screen // for (i = 0; i <= gpGlobals->wMaxPartyMemberIndex; i++) { g_Battle.rgPlayer[i].pos = PAL_XY(9999, 9999); } PAL_BattleDelay(1, 0, FALSE); g_Battle.BattleResult = kBattleResultFleed; }
VOID PAL_UpdatePartyGestures( BOOL fWalking ) /*++ Purpose: Update the gestures of all the party members. Parameters: [IN] fWalking - whether the party is walking or not. Return value: None. --*/ { static int s_iThisStepFrame = 0; int iStepFrameFollower = 0, iStepFrameLeader = 0; int i; if (fWalking) { // // Update the gesture for party leader // s_iThisStepFrame = (s_iThisStepFrame + 1) % 4; if (s_iThisStepFrame & 1) { iStepFrameLeader = (s_iThisStepFrame + 1) / 2; iStepFrameFollower = 3 - iStepFrameLeader; } else { iStepFrameLeader = 0; iStepFrameFollower = 0; } gpGlobals->rgParty[0].x = PAL_X(gpGlobals->partyoffset); gpGlobals->rgParty[0].y = PAL_Y(gpGlobals->partyoffset); if (gpGlobals->g.PlayerRoles.rgwWalkFrames[gpGlobals->rgParty[0].wPlayerRole] == 4) { gpGlobals->rgParty[0].wFrame = gpGlobals->wPartyDirection * 4 + s_iThisStepFrame; } else { gpGlobals->rgParty[0].wFrame = gpGlobals->wPartyDirection * 3 + iStepFrameLeader; } // // Update the gestures and positions for other party members // for (i = 1; i <= (short)gpGlobals->wMaxPartyMemberIndex; i++) { gpGlobals->rgParty[i].x = gpGlobals->rgTrail[1].x - PAL_X(gpGlobals->viewport); gpGlobals->rgParty[i].y = gpGlobals->rgTrail[1].y - PAL_Y(gpGlobals->viewport); if (i == 2) { gpGlobals->rgParty[i].x += (gpGlobals->rgTrail[1].wDirection == kDirEast || gpGlobals->rgTrail[1].wDirection == kDirWest) ? -16 : 16; gpGlobals->rgParty[i].y += 8; } else { gpGlobals->rgParty[i].x += ((gpGlobals->rgTrail[1].wDirection == kDirWest || gpGlobals->rgTrail[1].wDirection == kDirSouth) ? 16 : -16); gpGlobals->rgParty[i].y += ((gpGlobals->rgTrail[1].wDirection == kDirWest || gpGlobals->rgTrail[1].wDirection == kDirNorth) ? 8 : -8); } // // Adjust the position if there is obstacle // if (PAL_CheckObstacle(PAL_XY(gpGlobals->rgParty[i].x + PAL_X(gpGlobals->viewport), gpGlobals->rgParty[i].y + PAL_Y(gpGlobals->viewport)), TRUE, 0)) { gpGlobals->rgParty[i].x = gpGlobals->rgTrail[1].x - PAL_X(gpGlobals->viewport); gpGlobals->rgParty[i].y = gpGlobals->rgTrail[1].y - PAL_Y(gpGlobals->viewport); } // // Update gesture for this party member // if (gpGlobals->g.PlayerRoles.rgwWalkFrames[gpGlobals->rgParty[i].wPlayerRole] == 4) { gpGlobals->rgParty[i].wFrame = gpGlobals->rgTrail[2].wDirection * 4 + s_iThisStepFrame; } else { gpGlobals->rgParty[i].wFrame = gpGlobals->rgTrail[2].wDirection * 3 + iStepFrameLeader; } } if (gpGlobals->nFollower > 0) { // // Update the position and gesture for the follower // gpGlobals->rgParty[gpGlobals->wMaxPartyMemberIndex + 1].x = gpGlobals->rgTrail[3].x - PAL_X(gpGlobals->viewport); gpGlobals->rgParty[gpGlobals->wMaxPartyMemberIndex + 1].y = gpGlobals->rgTrail[3].y - PAL_Y(gpGlobals->viewport); gpGlobals->rgParty[gpGlobals->wMaxPartyMemberIndex + 1].wFrame = gpGlobals->rgTrail[3].wDirection * 3 + iStepFrameFollower; } } else { // // Player is not moved. Use the "standing" gesture instead of "walking" one. // i = gpGlobals->g.PlayerRoles.rgwWalkFrames[gpGlobals->rgParty[0].wPlayerRole]; if (i == 0) { i = 3; } gpGlobals->rgParty[0].wFrame = gpGlobals->wPartyDirection * i; for (i = 1; i <= (short)gpGlobals->wMaxPartyMemberIndex; i++) { int f = gpGlobals->g.PlayerRoles.rgwWalkFrames[gpGlobals->rgParty[i].wPlayerRole]; if (f == 0) { f = 3; } gpGlobals->rgParty[i].wFrame = gpGlobals->rgTrail[2].wDirection * f; } if (gpGlobals->nFollower > 0) { gpGlobals->rgParty[gpGlobals->wMaxPartyMemberIndex + 1].wFrame = gpGlobals->rgTrail[3].wDirection * 3; } s_iThisStepFrame &= 2; s_iThisStepFrame ^= 2; } }
static VOID PAL_DialogWaitForKey( VOID ) /*++ Purpose: Wait for player to press a key after showing a dialog. Parameters: None. Return value: None. --*/ { PAL_LARGE SDL_Color palette[256]; SDL_Color *pCurrentPalette, t; int i; // // get the current palette // pCurrentPalette = PAL_GetPalette(gpGlobals->wNumPalette, gpGlobals->fNightPalette); memcpy(palette, pCurrentPalette, sizeof(palette)); if (g_TextLib.bDialogPosition != kDialogCenterWindow && g_TextLib.bDialogPosition != kDialogCenter) { // // show the icon // LPCBITMAPRLE p = PAL_SpriteGetFrame(g_TextLib.bufDialogIcons, g_TextLib.bIcon); if (p != NULL) { SDL_Rect rect; rect.x = PAL_X(g_TextLib.posIcon); rect.y = PAL_Y(g_TextLib.posIcon); rect.w = 16; rect.h = 16; PAL_RLEBlitToSurface(p, gpScreen, g_TextLib.posIcon); VIDEO_UpdateScreen(&rect); } } PAL_ClearKeyState(); while (TRUE) { UTIL_Delay(100); if (g_TextLib.bDialogPosition != kDialogCenterWindow && g_TextLib.bDialogPosition != kDialogCenter) { // // palette shift // t = palette[0xF9]; for (i = 0xF9; i < 0xFE; i++) { palette[i] = palette[i + 1]; } palette[0xFE] = t; VIDEO_SetPalette(palette); } if (g_InputState.dwKeyPress != 0) { break; } } if (g_TextLib.bDialogPosition != kDialogCenterWindow && g_TextLib.bDialogPosition != kDialogCenter) { PAL_SetPalette(gpGlobals->wNumPalette, gpGlobals->fNightPalette); } PAL_ClearKeyState(); g_TextLib.fUserSkip = FALSE; }
VOID PAL_BattlePlayerPerformAction( WORD wPlayerIndex ) /*++ Purpose: Perform the selected action for a player. Parameters: [IN] wPlayerIndex - the index of the player. Return value: None. --*/ { // TODO SHORT sDamage; WORD wPlayerRole = gpGlobals->rgParty[wPlayerIndex].wPlayerRole; SHORT sTarget = g_Battle.rgPlayer[wPlayerIndex].action.sTarget; int x, y; int i, t; WORD str, def, res, wObject; BOOL fCritical, fPoisoned; PAL_BattlePlayerValidateAction(wPlayerIndex); PAL_BattleBackupStat(); switch (g_Battle.rgPlayer[wPlayerIndex].action.ActionType) { case kBattleActionAttack: if (sTarget != -1) { // // Attack one enemy // if (g_Battle.rgEnemy[sTarget].wObjectID == 0) { sTarget = PAL_BattleSelectAutoTarget(); g_Battle.rgPlayer[wPlayerIndex].action.sTarget = sTarget; } for (t = 0; t < (gpGlobals->rgPlayerStatus[wPlayerRole][kStatusDualAttack] ? 2 : 1); t++) { str = PAL_GetPlayerAttackStrength(wPlayerRole); def = g_Battle.rgEnemy[sTarget].e.wDefense; def += (g_Battle.rgEnemy[sTarget].e.wLevel + 6) * 4; res = g_Battle.rgEnemy[sTarget].e.wAttackResistance; fCritical = FALSE; sDamage = PAL_CalcPhysicalAttackDamage(str, def, res); if (RandomLong(0, 5) == 0) { // // Critical Hit // sDamage *= 3; fCritical = TRUE; } if (wPlayerRole == 0 && RandomLong(0, 11) == 0) { // // Bonus hit for Li Xiaoyao // sDamage *= 2; fCritical = TRUE; } if (sDamage <= 0) { sDamage = 1; } if (g_Battle.rgEnemy[sTarget].e.wHealth > (WORD)sDamage) { g_Battle.rgEnemy[sTarget].e.wHealth -= sDamage; } else { g_Battle.rgEnemy[sTarget].e.wHealth = 0; } if (t == 0) { g_Battle.rgPlayer[wPlayerIndex].wCurrentFrame = 7; PAL_BattleDelay(4, 0); } PAL_BattleShowPlayerAttackAnim(wPlayerIndex, fCritical); // // Show the number of damage // x = PAL_X(g_Battle.rgEnemy[sTarget].pos); y = PAL_Y(g_Battle.rgEnemy[sTarget].pos) - 70; x += PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgEnemy[sTarget].lpSprite, g_Battle.rgEnemy[sTarget].wCurrentFrame)) / 2; y += PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgEnemy[sTarget].lpSprite, g_Battle.rgEnemy[sTarget].wCurrentFrame)) / 2; if (y < 10) { y = 10; } PAL_BattleUIShowNum((WORD)sDamage, PAL_XY(x, y), kNumColorBlue); } } else { // // Attack all enemies // for (t = 0; t < (gpGlobals->rgPlayerStatus[wPlayerRole][kStatusDualAttack] ? 2 : 1); t++) { int division = 1; const int index[MAX_ENEMIES_IN_TEAM] = {2, 1, 0, 4, 3}; fCritical = (RandomLong(0, 5) == 0); if (t == 0) { g_Battle.rgPlayer[wPlayerIndex].wCurrentFrame = 7; PAL_BattleDelay(4, 0); } PAL_BattleShowPlayerAttackAnim(wPlayerIndex, fCritical); for (i = 0; i < MAX_ENEMIES_IN_TEAM; i++) { if (g_Battle.rgEnemy[index[i]].wObjectID == 0 || index[i] > g_Battle.wMaxEnemyIndex) { continue; } str = PAL_GetPlayerAttackStrength(wPlayerRole); def = g_Battle.rgEnemy[index[i]].e.wDefense; def += (g_Battle.rgEnemy[index[i]].e.wLevel + 6) * 4; res = g_Battle.rgEnemy[index[i]].e.wAttackResistance; sDamage = PAL_CalcPhysicalAttackDamage(str, def, res); if (fCritical) { // // Critical Hit // sDamage *= 3; } sDamage /= division; if (sDamage <= 0) { sDamage = 1; } if (g_Battle.rgEnemy[index[i]].e.wHealth > (WORD)sDamage) { g_Battle.rgEnemy[index[i]].e.wHealth -= sDamage; } else { g_Battle.rgEnemy[index[i]].e.wHealth = 0; } // // Show the number of damage // x = PAL_X(g_Battle.rgEnemy[index[i]].pos); y = PAL_Y(g_Battle.rgEnemy[index[i]].pos) - 70; x += PAL_RLEGetWidth(PAL_SpriteGetFrame(g_Battle.rgEnemy[index[i]].lpSprite, g_Battle.rgEnemy[index[i]].wCurrentFrame)) / 2; y += PAL_RLEGetHeight(PAL_SpriteGetFrame(g_Battle.rgEnemy[index[i]].lpSprite, g_Battle.rgEnemy[index[i]].wCurrentFrame)) / 2; if (y < 10) { y = 10; } PAL_BattleUIShowNum((WORD)sDamage, PAL_XY(x, y), kNumColorBlue); division++; if (division > 3) { division = 3; } } } } PAL_BattleUpdateFighters(); PAL_BattleMakeScene(); PAL_BattleDelay(5, 0); break; case kBattleActionAttackMate: break; case kBattleActionCoopMagic: break; case kBattleActionDefend: g_Battle.rgPlayer[wPlayerIndex].fDefending = TRUE; break; case kBattleActionFlee: break; case kBattleActionMagic: break; case kBattleActionThrowItem: break; case kBattleActionUseItem: wObject = g_Battle.rgPlayer[wPlayerIndex].action.wActionID; PAL_BattleShowPlayerUseItemAnim(wPlayerIndex, wObject, sTarget); // // Run the script // gpGlobals->g.rgObject[wObject].item.wScriptOnUse = PAL_RunTriggerScript(gpGlobals->g.rgObject[wObject].item.wScriptOnUse, (sTarget == -1) ? 0xFFFF : gpGlobals->rgParty[sTarget].wPlayerRole); // // Remove the item if the item is consuming and the script succeeded // if ((gpGlobals->g.rgObject[wObject].item.wFlags & kItemFlagConsuming) && g_fScriptSuccess) { PAL_AddItemToInventory(wObject, -1); } PAL_BattleUpdateFighters(); PAL_BattleDisplayStatChange(); PAL_BattleDelay(8, 0); break; case kBattleActionPass: break; } PAL_BattlePostActionCheck(FALSE); // // Check for poisons // fPoisoned = FALSE; PAL_BattleBackupStat(); for (i = 0; i < MAX_POISONS; i++) { wObject = gpGlobals->rgPoisonStatus[i][wPlayerIndex].wPoisonID; if (wObject != 0) { fPoisoned = TRUE; gpGlobals->rgPoisonStatus[i][wPlayerIndex].wPoisonScript = PAL_RunTriggerScript(gpGlobals->rgPoisonStatus[i][wPlayerIndex].wPoisonScript, wPlayerRole); } } if (fPoisoned) { PAL_BattleDelay(3, 0); PAL_BattleUpdateFighters(); if (PAL_BattleDisplayStatChange()) { PAL_BattleDelay(12, 0); } } // // Update statuses // for (i = 0; i < kStatusAll; i++) { if (gpGlobals->rgPlayerStatus[wPlayerRole][i] > 0) { gpGlobals->rgPlayerStatus[wPlayerRole][i]--; } } }
INT PAL_RLEBlitMonoColor( LPCBITMAPRLE lpBitmapRLE, SDL_Surface *lpDstSurface, PAL_POS pos, BYTE bColor, INT iColorShift ) /*++ Purpose: Blit an RLE-compressed bitmap to an SDL surface in mono-color form. NOTE: Assume the surface is already locked, and the surface is a 8-bit one. Parameters: [IN] lpBitmapRLE - pointer to the RLE-compressed bitmap to be decoded. [OUT] lpDstSurface - pointer to the destination SDL surface. [IN] pos - position of the destination area. [IN] bColor - the color to be used while drawing. [IN] iColorShift - shift the color by this value. Return value: 0 = success, -1 = error. --*/ { UINT i, j; INT x, y; UINT uiLen = 0; UINT uiWidth = 0; UINT uiHeight = 0; BYTE T, b; INT dx = PAL_X(pos); INT dy = PAL_Y(pos); // // Check for NULL pointer. // if (lpBitmapRLE == NULL || lpDstSurface == NULL) { return -1; } // // Skip the 0x00000002 in the file header. // if (lpBitmapRLE[0] == 0x02 && lpBitmapRLE[1] == 0x00 && lpBitmapRLE[2] == 0x00 && lpBitmapRLE[3] == 0x00) { lpBitmapRLE += 4; } // // Get the width and height of the bitmap. // uiWidth = lpBitmapRLE[0] | (lpBitmapRLE[1] << 8); uiHeight = lpBitmapRLE[2] | (lpBitmapRLE[3] << 8); // // Calculate the total length of the bitmap. // The bitmap is 8-bpp, each pixel will use 1 byte. // uiLen = uiWidth * uiHeight; // // Start decoding and blitting the bitmap. // lpBitmapRLE += 4; bColor &= 0xF0; for (i = 0; i < uiLen;) { T = *lpBitmapRLE++; if ((T & 0x80) && T <= 0x80 + uiWidth) { i += T - 0x80; } else { for (j = 0; j < T; j++) { // // Calculate the destination coordination. // FIXME: This could be optimized // y = (i + j) / uiWidth + dy; x = (i + j) % uiWidth + dx; // // Skip the points which are out of the surface. // if (x < 0) { j += -x - 1; continue; } else if (x >= lpDstSurface->w) { j += x - lpDstSurface->w; continue; } if (y < 0) { j += -y * uiWidth - 1; continue; } else if (y >= lpDstSurface->h) { goto end; // No more pixels needed, break out } // // Put the pixel onto the surface (FIXME: inefficient). // b = lpBitmapRLE[j] & 0x0F; if ((INT)b + iColorShift > 0x0F) { b = 0x0F; } else if ((INT)b + iColorShift < 0) { b = 0; } else { b += iColorShift; } ((LPBYTE)lpDstSurface->pixels)[y * lpDstSurface->pitch + x] = (b | bColor); } lpBitmapRLE += T; i += T; } } end: // // Success // return 0; }
BOOL PAL_CheckObstacle( PAL_POS pos, BOOL fCheckEventObjects, WORD wSelfObject ) /*++ Purpose: Check if the specified location has obstacle or not. Parameters: [IN] pos - the position to check. [IN] fCheckEventObjects - TRUE if check for event objects, FALSE if only check for the map. [IN] wSelfObject - the event object which will be skipped. Return value: TRUE if the location is obstacle, FALSE if not. --*/ { int x, y, h, xr, yr; if (PAL_X(pos) < 0 || PAL_X(pos) >= 2048 || PAL_Y(pos) < 0 || PAL_Y(pos) >= 2048) { return TRUE; } // // Check if the map tile at the specified position is blocking // x = PAL_X(pos) / 32; y = PAL_Y(pos) / 16; h = 0; xr = PAL_X(pos) % 32; yr = PAL_Y(pos) % 16; if (xr + yr * 2 >= 16) { if (xr + yr * 2 >= 48) { x++; y++; } else if (32 - xr + yr * 2 < 16) { x++; } else if (32 - xr + yr * 2 < 48) { h = 1; } else { y++; } } if (PAL_MapTileIsBlocked(x, y, h, PAL_GetCurrentMap())) { return TRUE; } if (fCheckEventObjects) { // // Loop through all event objects in the current scene // int i; for (i = gpGlobals->g.rgScene[gpGlobals->wNumScene - 1].wEventObjectIndex; i < gpGlobals->g.rgScene[gpGlobals->wNumScene].wEventObjectIndex; i++) { LPEVENTOBJECT p = &(gpGlobals->g.lprgEventObject[i]); if (i == wSelfObject - 1) { // // Skip myself // continue; } // // Is this object a blocking one? // if (p->sState >= kObjStateBlocker) { // // Check for collision // if (abs(p->x - PAL_X(pos)) + abs(p->y - PAL_Y(pos)) * 2 < 16) { return TRUE; } } } } return FALSE; }