ref: b821d86c911e230575d3d3d9e45f1690de5636a1
dir: /src/Game.cpp/
// THIS IS DECOMPILED PROPRIETARY CODE - USE AT YOUR OWN RISK. // // The original code belongs to Daisuke "Pixel" Amaya. // // Modifications and custom code are under the MIT licence. // See LICENCE.txt for details. #include "Game.h" #include <stddef.h> #include "WindowsWrapper.h" #include "Backends/Misc.h" #include "ArmsItem.h" #include "Back.h" #include "Boss.h" #include "BossLife.h" #include "BulHit.h" #include "Bullet.h" #include "Caret.h" #include "CommonDefines.h" #include "Draw.h" #include "Ending.h" #include "Escape.h" #include "Fade.h" #include "Flags.h" #include "Flash.h" #include "Frame.h" #include "Generic.h" #include "GenericLoad.h" #include "KeyControl.h" #include "Main.h" #include "Map.h" #include "MapName.h" #include "MiniMap.h" #include "MyChar.h" #include "MycHit.h" #include "MycParam.h" #include "NpChar.h" #include "NpcHit.h" #include "NpcTbl.h" #include "Profile.h" #include "Random.h" #include "SelStage.h" #include "Shoot.h" #include "Sound.h" #include "Stage.h" #include "Star.h" #include "TextScr.h" #include "ValueView.h" int g_GameFlags; int gCounter; static BOOL bContinue; int Random(int min, int max) { const int range = max - min + 1; return (msvc_rand() % range) + min; } void PutNumber4(int x, int y, int value, BOOL bZero) { // Define rects RECT rcClient = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT}; RECT rect[10] = { {0, 56, 8, 64}, {8, 56, 16, 64}, {16, 56, 24, 64}, {24, 56, 32, 64}, {32, 56, 40, 64}, {40, 56, 48, 64}, {48, 56, 56, 64}, {56, 56, 64, 64}, {64, 56, 72, 64}, {72, 56, 80, 64}, }; // Digits int tbl[4] = {1000, 100, 10, 1}; int a; int sw; int offset; // Limit value if (value > 9999) value = 9999; // Go through number and draw digits offset = 0; sw = 0; while (offset < 4) { // Get the digit that this is a = 0; while (value >= tbl[offset]) { value -= tbl[offset]; ++a; ++sw; } // Draw digit if ((bZero && offset == 2) || sw != 0 || offset == 3) PutBitmap3(&rcClient, x + 8 * offset, y, &rect[a], SURFACE_ID_TEXT_BOX); // Go to next digit ++offset; } } static int ModeOpening(void) { int frame_x; int frame_y; unsigned int wait; InitNpChar(); InitCaret(); InitStar(); InitFade(); InitFlash(); InitBossLife(); ChangeMusic(MUS_SILENCE); TransferStage(72, 100, 3, 3); SetFrameTargetMyChar(16); SetFadeMask(); // Reset cliprect and flags grcGame.left = 0; #if WINDOW_WIDTH != 320 || WINDOW_HEIGHT != 240 // Non-vanilla: these three lines are widescreen-related grcGame.top = 0; grcGame.right = WINDOW_WIDTH; grcGame.bottom = WINDOW_HEIGHT; #endif g_GameFlags = 3; CutNoise(); wait = 0; while (wait < 500) { // Increase timer ++wait; // Get pressed keys GetTrg(); // Escape menu if (gKey & KEY_ESCAPE) { switch (Call_Escape()) { case enum_ESCRETURN_exit: return 0; case enum_ESCRETURN_restart: return 1; } } // Skip intro if OK is pressed if (gKey & gKeyOk) break; // Update everything ActNpChar(); ActBossChar(); ActBack(); ResetMyCharFlag(); HitMyCharMap(); HitMyCharNpChar(); HitMyCharBoss(); HitNpCharMap(); HitBossMap(); HitBossBullet(); ActCaret(); MoveFrame3(); ProcFade(); // Draw everything CortBox(&grcFull, 0x000000); GetFramePosition(&frame_x, &frame_y); PutBack(frame_x, frame_y); PutStage_Back(frame_x, frame_y); PutBossChar(frame_x, frame_y); PutNpChar(frame_x, frame_y); PutMapDataVector(frame_x, frame_y); PutStage_Front(frame_x, frame_y); PutFront(frame_x, frame_y); PutCaret(frame_x, frame_y); PutFade(); // Update Text Script switch (TextScriptProc()) { case enum_ESCRETURN_exit: return 0; case enum_ESCRETURN_restart: return 1; } PutMapName(FALSE); PutTextScript(); PutFramePerSecound(); if (!Flip_SystemTask()) return 0; ++gCounter; } wait = Backend_GetTicks(); while (Backend_GetTicks() < wait + 500) { CortBox(&grcGame, 0x000000); PutFramePerSecound(); if (!Flip_SystemTask()) return 0; } return 2; } static int ModeTitle(void) { // Set rects RECT rcTitle = {0, 0, 144, 40}; RECT rcPixel = {0, 0, 160, 16}; RECT rcNew = {144, 0, 192, 16}; RECT rcContinue = {144, 16, 192, 32}; RECT rcVersion = {152, 80, 208, 88}; RECT rcPeriod = {152, 88, 208, 96}; // Character rects RECT rcMyChar[4] = { {0, 16, 16, 32}, {16, 16, 32, 32}, {0, 16, 16, 32}, {32, 16, 48, 32}, }; RECT rcCurly[4] = { {0, 112, 16, 128}, {16, 112, 32, 128}, {0, 112, 16, 128}, {32, 112, 48, 128}, }; RECT rcToroko[4] = { {64, 80, 80, 96}, {80, 80, 96, 96}, {64, 80, 80, 96}, {96, 80, 112, 96}, }; RECT rcKing[4] = { {224, 48, 240, 64}, {288, 48, 304, 64}, {224, 48, 240, 64}, {304, 48, 320, 64}, }; RECT rcSu[4] = { {0, 16, 16, 32}, {32, 16, 48, 32}, {0, 16, 16, 32}, {48, 16, 64, 32}, }; unsigned int wait; int anime; int v1, v2, v3, v4; RECT char_rc; int char_type; int time_counter; int char_y; SurfaceID char_surf; unsigned long back_color; // Reset everything InitCaret(); InitStar(); CutNoise(); // Create variables anime = 0; char_type = 0; time_counter = 0; back_color = GetCortBoxColor(RGB(0x20, 0x20, 0x20)); GetCompileVersion(&v1, &v2, &v3, &v4); // Set state if (IsProfile()) bContinue = TRUE; else bContinue = FALSE; // Set character time_counter = LoadTimeCounter(); if (time_counter && time_counter < 6 * 60 * 50) // 6 minutes char_type = 1; if (time_counter && time_counter < 5 * 60 * 50) // 5 minutes char_type = 2; if (time_counter && time_counter < 4 * 60 * 50) // 4 minutes char_type = 3; if (time_counter && time_counter < 3 * 60 * 50) // 3 minutes char_type = 4; // Set music to character's specific music if (char_type == 1) ChangeMusic(MUS_RUNNING_HELL); else if (char_type == 2) ChangeMusic(MUS_TOROKOS_THEME); else if (char_type == 3) ChangeMusic(MUS_WHITE); else if (char_type == 4) ChangeMusic(MUS_SAFETY); else ChangeMusic(MUS_CAVE_STORY); // Reset cliprect, flags, and give the player the Nikumaru counter grcGame.left = 0; #if WINDOW_WIDTH != 320 || WINDOW_HEIGHT != 240 // Non-vanilla: these three lines are widescreen-related grcGame.top = 0; grcGame.right = WINDOW_WIDTH; grcGame.bottom = WINDOW_HEIGHT; #endif g_GameFlags = 0; gMC.equip |= EQUIP_NIKUMARU_COUNTER; // Give the player the Nikumaru Counter so the timer appears on-screen // Start loop wait = 0; while (1) { // Don't accept selection for 10 frames if (wait < 10) ++wait; // Get pressed keys GetTrg(); // Quit when OK is pressed if (wait >= 10) { if (gKeyTrg & gKeyOk) { PlaySoundObject(18, SOUND_MODE_PLAY); break; } } if (gKey & KEY_ESCAPE) { switch (Call_Escape()) { case enum_ESCRETURN_exit: return 0; case enum_ESCRETURN_restart: return 1; } } // Move cursor if (gKeyTrg & (gKeyUp | gKeyDown)) { PlaySoundObject(1, SOUND_MODE_PLAY); if (bContinue) bContinue = FALSE; else bContinue = TRUE; } // Update carets ActCaret(); // Animate character cursor if (++anime >= 40) anime = 0; // Draw title CortBox(&grcGame, back_color); // Draw version PutBitmap3(&grcGame, (WINDOW_WIDTH / 2) - 60, WINDOW_HEIGHT - 24, &rcVersion, SURFACE_ID_TEXT_BOX); PutBitmap3(&grcGame, (WINDOW_WIDTH / 2) - 4, WINDOW_HEIGHT - 24, &rcPeriod, SURFACE_ID_TEXT_BOX); PutNumber4((WINDOW_WIDTH / 2) - 20, WINDOW_HEIGHT - 24, v1, FALSE); PutNumber4((WINDOW_WIDTH / 2) - 4, WINDOW_HEIGHT - 24, v2, FALSE); PutNumber4((WINDOW_WIDTH / 2) + 12, WINDOW_HEIGHT - 24, v3, FALSE); PutNumber4((WINDOW_WIDTH / 2) + 28, WINDOW_HEIGHT - 24, v4, FALSE); // Draw main title PutBitmap3(&grcGame, (WINDOW_WIDTH / 2) - 72, 40, &rcTitle, SURFACE_ID_TITLE); PutBitmap3(&grcGame, (WINDOW_WIDTH / 2) - 24, (WINDOW_HEIGHT / 2) + 8, &rcNew, SURFACE_ID_TITLE); PutBitmap3(&grcGame, (WINDOW_WIDTH / 2) - 24, (WINDOW_HEIGHT / 2) + 28, &rcContinue, SURFACE_ID_TITLE); PutBitmap3(&grcGame, (WINDOW_WIDTH / 2) - 80, WINDOW_HEIGHT - 48, &rcPixel, SURFACE_ID_PIXEL); // Draw character cursor switch (char_type) { case 0: char_rc = rcMyChar[anime / 10 % 4]; char_surf = SURFACE_ID_MY_CHAR; break; case 1: char_rc = rcCurly[anime / 10 % 4]; char_surf = SURFACE_ID_NPC_REGU; break; case 2: char_rc = rcToroko[anime / 10 % 4]; char_surf = SURFACE_ID_NPC_REGU; break; case 3: char_rc = rcKing[anime / 10 % 4]; char_surf = SURFACE_ID_NPC_REGU; break; case 4: char_rc = rcSu[anime / 10 % 4]; char_surf = SURFACE_ID_NPC_REGU; break; } if (!bContinue) char_y = (WINDOW_HEIGHT / 2) + 7; else char_y = (WINDOW_HEIGHT / 2) + 27; // Pixel being redundant if (!bContinue) PutBitmap3(&grcGame, (WINDOW_WIDTH / 2) - 44, char_y, &char_rc, char_surf); else PutBitmap3(&grcGame, (WINDOW_WIDTH / 2) - 44, char_y, &char_rc, char_surf); // Draw carets PutCaret(0, 0); if (time_counter) PutTimeCounter(16, 8); PutFramePerSecound(); if (!Flip_SystemTask()) return 0; } ChangeMusic(MUS_SILENCE); // Black screen when option is selected wait = Backend_GetTicks(); while (Backend_GetTicks() < wait + 1000) { CortBox(&grcGame, 0); PutFramePerSecound(); if (!Flip_SystemTask()) return 0; } return 3; } static int ModeAction(void) { int frame_x; int frame_y; unsigned int swPlay; unsigned long color = GetCortBoxColor(RGB(0, 0, 0x20)); swPlay = 1; // Reset stuff gCounter = 0; grcGame.left = 0; #if WINDOW_WIDTH != 320 || WINDOW_HEIGHT != 240 // Non-vanilla: these three lines are widescreen-related grcGame.top = 0; grcGame.right = WINDOW_WIDTH; grcGame.bottom = WINDOW_HEIGHT; #endif g_GameFlags = 3; // Initialize everything InitMyChar(); InitNpChar(); InitBullet(); InitCaret(); InitStar(); InitFade(); InitFlash(); ClearArmsData(); ClearItemData(); ClearPermitStage(); StartMapping(); InitFlags(); InitBossLife(); if (bContinue) { if (!LoadProfile(NULL) && !InitializeGame()) return 0; } else { if (!InitializeGame()) return 0; } while (1) { // Get pressed keys GetTrg(); // Escape menu if (gKey & KEY_ESCAPE) { switch (Call_Escape()) { case enum_ESCRETURN_exit: return 0; case enum_ESCRETURN_restart: return 1; } } if (swPlay % 2 && g_GameFlags & 1) // The "swPlay % 2" part is always true { if (g_GameFlags & 2) ActMyChar(TRUE); else ActMyChar(FALSE); ActStar(); ActNpChar(); ActBossChar(); ActValueView(); ActBack(); ResetMyCharFlag(); HitMyCharMap(); HitMyCharNpChar(); HitMyCharBoss(); HitNpCharMap(); HitBossMap(); HitBulletMap(); HitNpCharBullet(); HitBossBullet(); if (g_GameFlags & 2) ShootBullet(); ActBullet(); ActCaret(); MoveFrame3(); #ifdef FIX_MAJOR_BUGS // ActFlash uses frame_x and frame_y uninitialised GetFramePosition(&frame_x, &frame_y); #endif ActFlash(frame_x, frame_y); if (g_GameFlags & 2) AnimationMyChar(TRUE); else AnimationMyChar(FALSE); } if (g_GameFlags & 8) { ActionCredit(); ActionIllust(); ActionStripper(); } ProcFade(); CortBox(&grcFull, color); GetFramePosition(&frame_x, &frame_y); PutBack(frame_x, frame_y); PutStage_Back(frame_x, frame_y); PutBossChar(frame_x, frame_y); PutNpChar(frame_x, frame_y); PutBullet(frame_x, frame_y); PutMyChar(frame_x, frame_y); PutStar(frame_x, frame_y); PutMapDataVector(frame_x, frame_y); PutStage_Front(frame_x, frame_y); PutFront(frame_x, frame_y); PutFlash(); PutCaret(frame_x, frame_y); PutValueView(frame_x, frame_y); PutBossLife(); PutFade(); if (!(g_GameFlags & 4)) { // Open inventory if (gKeyTrg & gKeyItem) { BackupSurface(SURFACE_ID_SCREEN_GRAB, &grcGame); switch (CampLoop()) { case enum_ESCRETURN_exit: return 0; case enum_ESCRETURN_restart: return 1; } gMC.cond &= ~1; } else if (gMC.equip & EQUIP_MAP && gKeyTrg & gKeyMap) { BackupSurface(SURFACE_ID_SCREEN_GRAB, &grcGame); switch (MiniMapLoop()) { case enum_ESCRETURN_exit: return 0; case enum_ESCRETURN_restart: return 1; } } } if (g_GameFlags & 2) { if (gKeyTrg & gKeyArms) RotationArms(); else if (gKeyTrg & gKeyArmsRev) RotationArmsRev(); } if (swPlay % 2) // This is always true { switch (TextScriptProc()) { case enum_ESCRETURN_exit: return 0; case enum_ESCRETURN_restart: return 1; } } PutMapName(FALSE); PutTimeCounter(16, 8); if (g_GameFlags & 2) { PutMyLife(TRUE); PutArmsEnergy(TRUE); PutMyAir((WINDOW_WIDTH / 2) - 40, (WINDOW_HEIGHT / 2) - 16); PutActiveArmsList(); } if (g_GameFlags & 8) { PutIllust(); PutStripper(); } PutTextScript(); PutFramePerSecound(); if (!Flip_SystemTask()) return 0; ++gCounter; } return 0; } BOOL Game(void) { int mode; if (!LoadGenericData()) { #if !defined(JAPANESE) && defined(FIX_BUGS) // The Aeon Genesis translation didn't translate this Backend_ShowMessageBox("Error", "Couldn't read general purpose files"); #else Backend_ShowMessageBox("エラー", "汎用ファイルが読めない"); #endif return FALSE; } PlaySoundObject(7, SOUND_MODE_PLAY_LOOP); char path[128]; snprint(path, sizeof path, "%s/npc.tbl", gDataPath); if (!LoadNpcTable(path)) { #if !defined(JAPANESE) && defined(FIX_BUGS) // The Aeon Genesis translation didn't translate this Backend_ShowMessageBox("Error", "Couldn't read the NPC table"); #else Backend_ShowMessageBox("エラー", "NPCテーブルが読めない"); #endif return FALSE; } InitTextScript2(); InitSkipFlags(); InitMapData2(); InitCreditScript(); mode = 1; while (mode) { if (mode == 1) mode = ModeOpening(); if (mode == 2) mode = ModeTitle(); if (mode == 3) mode = ModeAction(); } PlaySoundObject(7, SOUND_MODE_STOP); EndMapData(); EndTextScript(); ReleaseNpcTable(); ReleaseCreditScript(); return TRUE; }