CSE2-tweaks/src/Ending.cpp

601 lines
12 KiB
C++

// 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 "Ending.h"
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include "WindowsWrapper.h"
#include "Draw.h"
#include "Escape.h"
#include "Flags.h"
#include "Generic.h"
#include "KeyControl.h"
#include "Main.h"
#include "MycParam.h"
#ifdef EXTRA_SOUND_FORMATS
#include "ExtraSoundFormats.h"
#endif
#include "Organya.h"
#include "Pause.h"
#include "Stage.h"
#include "TextScr.h"
enum CREDIT_MODE
{
CREDIT_MODE_STOP,
CREDIT_MODE_SCROLL_READ,
CREDIT_MODE_SCROLL_WAIT
};
enum ILLUSTRATION_ACTION
{
ILLUSTRATION_ACTION_IDLE,
ILLUSTRATION_ACTION_SLIDE_IN,
ILLUSTRATION_ACTION_SLIDE_OUT
};
struct CREDIT
{
long size;
char *pData;
int offset;
int wait;
CREDIT_MODE mode;
int start_x;
};
struct STRIP
{
int flag;
int x;
int y;
int cast;
char str[0x40];
};
struct ILLUSTRATION
{
ILLUSTRATION_ACTION act_no;
int x;
};
struct ISLAND_SPRITE
{
int x;
int y;
};
static CREDIT Credit;
static STRIP Strip[MAX_STRIP];
static ILLUSTRATION Illust;
// Update casts
void ActionStripper(void)
{
int s;
for (s = 0; s < MAX_STRIP; ++s)
{
// Move up
if (Strip[s].flag & 0x80 && Credit.mode != CREDIT_MODE_STOP)
Strip[s].y -= 0x100;
// Get removed when off-screen
if (Strip[s].y <= -16 * 0x200)
Strip[s].flag = 0;
}
}
// Draw casts
void PutStripper(void)
{
int s;
RECT rc;
for (s = 0; s < MAX_STRIP; ++s)
{
if (Strip[s].flag & 0x80)
{
// Draw text
rc.left = 0;
rc.right = 320;
rc.top = s * 16;
rc.bottom = rc.top + 16;
PutBitmap3(&grcFull, SubpixelToScreenCoord(Strip[s].x) + PixelToScreenCoord((WINDOW_WIDTH - 320) / 2), SubpixelToScreenCoord(Strip[s].y), &rc, SURFACE_ID_CREDIT_CAST);
// Draw character
rc.left = (Strip[s].cast % 13) * 24;
rc.right = rc.left + 24;
rc.top = (Strip[s].cast / 13) * 24;
rc.bottom = rc.top + 24;
PutBitmap3(&grcFull, SubpixelToScreenCoord(Strip[s].x) + PixelToScreenCoord(((WINDOW_WIDTH - 320) / 2) - 24), SubpixelToScreenCoord(Strip[s].y) - PixelToScreenCoord(8), &rc, SURFACE_ID_CASTS);
}
}
}
// Create a cast object
void SetStripper(int x, int y, const char *text, int cast)
{
int s;
RECT rc;
for (s = 0; s < MAX_STRIP; ++s)
if (!(Strip[s].flag & 0x80))
break;
if (s == MAX_STRIP)
return;
// Initialize cast property
Strip[s].flag = 0x80;
Strip[s].x = x;
Strip[s].y = y;
Strip[s].cast = cast;
strcpy(Strip[s].str, text);
// Draw text
rc.left = 0;
rc.right = 320;
rc.top = s * 16;
rc.bottom = rc.top + 16;
CortBox2(&rc, 0, SURFACE_ID_CREDIT_CAST);
PutText2(0, rc.top, text, RGB(0xFF, 0xFF, 0xFE), SURFACE_ID_CREDIT_CAST);
}
// Regenerate cast text
void RestoreStripper(void)
{
int s;
RECT rc;
for (s = 0; s < MAX_STRIP; ++s)
{
if (Strip[s].flag & 0x80)
{
rc.left = 0;
rc.right = 320;
rc.top = s * 16;
rc.bottom = rc.top + 16;
CortBox2(&rc, 0, SURFACE_ID_CREDIT_CAST);
PutText2(0, rc.top, Strip[s].str, RGB(0xFF, 0xFF, 0xFE), SURFACE_ID_CREDIT_CAST);
}
}
}
// Handle the illustration
void ActionIllust(void)
{
switch (Illust.act_no)
{
case ILLUSTRATION_ACTION_IDLE: // Off-screen to the left
Illust.x = -160 * 0x200;
break;
case ILLUSTRATION_ACTION_SLIDE_IN: // Move in from the left
Illust.x += 40 * 0x200;
if (Illust.x > 0)
Illust.x = 0;
break;
case ILLUSTRATION_ACTION_SLIDE_OUT: // Move out from the right
Illust.x -= 40 * 0x200;
if (Illust.x < -160 * 0x200)
Illust.x = -160 * 0x200;
break;
}
}
// Draw illustration
void PutIllust(void)
{
RECT rcIllust = {0, 0, 160, 240};
#if WINDOW_WIDTH != 320 || WINDOW_HEIGHT != 240 // TODO - Move this to CSE2EX
// Widescreen edit
RECT rcClip = {(WINDOW_WIDTH - 320) / 2, 0, WINDOW_WIDTH, WINDOW_HEIGHT};
PutBitmap3(&rcClip, SubpixelToScreenCoord(Illust.x) + PixelToScreenCoord((WINDOW_WIDTH - 320) / 2), PixelToScreenCoord((WINDOW_HEIGHT - 240) / 2), &rcIllust, SURFACE_ID_CREDITS_IMAGE);
#else
PutBitmap3(&grcFull, SubpixelToScreenCoord(Illust.x) + PixelToScreenCoord((WINDOW_WIDTH - 320) / 2), PixelToScreenCoord((WINDOW_HEIGHT - 240) / 2), &rcIllust, SURFACE_ID_CREDITS_IMAGE);
#endif
}
// Load illustration
void ReloadIllust(int a)
{
char name[16];
sprintf(name, "CREDIT%02d", a);
std::string path = std::string{"Resource/BITMAP/"} + name;
ReloadBitmap_File(path.c_str(), SURFACE_ID_CREDITS_IMAGE, true, false);
}
const char *credit_script = "Credit.tsc";
// Initialize and release credits
void InitCreditScript(void)
{
// Clear script state and casts
memset(&Credit, 0, sizeof(CREDIT));
memset(Strip, 0, sizeof(Strip));
}
void ReleaseCreditScript(void)
{
if (Credit.pData != NULL)
{
// Free script data
free(Credit.pData);
Credit.pData = NULL;
}
}
// Start playing credits
BOOL StartCreditScript(void)
{
FILE *fp;
std::string path;
// Clear previously existing credits data
if (Credit.pData != NULL)
{
free(Credit.pData);
Credit.pData = NULL;
}
// Open file
path = gDataPath + '/' + credit_script;
Credit.size = GetFileSizeLong(path.c_str());
if (Credit.size == -1)
return FALSE;
// Allocate buffer data
Credit.pData = (char*)malloc(Credit.size);
if (Credit.pData == NULL)
return FALSE;
fp = fopen(path.c_str(), "rb");
if (fp == NULL)
{
free(Credit.pData);
return FALSE;
}
// Read data
fread(Credit.pData, 1, Credit.size, fp);
#ifdef FIX_MAJOR_BUGS
// The original game forgot to close the file
fclose(fp);
#endif
EncryptionBinaryData2((unsigned char*)Credit.pData, Credit.size);
// Reset credits
Credit.offset = 0;
Credit.wait = 0;
Credit.mode = CREDIT_MODE_SCROLL_READ;
Illust.x = -160 * 0x200;
Illust.act_no = ILLUSTRATION_ACTION_IDLE;
// Modify cliprect
grcGame.left = WINDOW_WIDTH / 2;
#if WINDOW_WIDTH != 320 || WINDOW_HEIGHT != 240 // TODO - Move to CSE2EX
// These three are non-vanilla: for wide/tallscreen support
grcGame.right = ((WINDOW_WIDTH - 320) / 2) + 320;
grcGame.top = (WINDOW_HEIGHT - 240) / 2;
grcGame.bottom = ((WINDOW_HEIGHT - 240) / 2) + 240;
#endif
// Reload casts
if (!ReloadBitmap_File("casts", SURFACE_ID_CASTS, false, false))
return FALSE;
// Clear casts
memset(Strip, 0, sizeof(Strip));
return TRUE;
}
// Get number from text (4 digit)
static int GetScriptNumber(const char *text)
{
return (text[0] - '0') * 1000
+ (text[1] - '0') * 100
+ (text[2] - '0') * 10
+ (text[3] - '0') * 1;
}
// Parse credits
static void ActionCredit_Read(void)
{
int a, b, len;
char text[40];
while (1)
{
if (Credit.offset >= Credit.size)
break;
switch (Credit.pData[Credit.offset])
{
case '[': // Create cast
// Get the range for the cast text
Credit.offset += 1;
a = Credit.offset;
while (Credit.pData[a] != ']')
{
if (IsShiftJIS(Credit.pData[a]))
a += 2;
else
a += 1;
}
len = a - Credit.offset;
// Copy the text to the cast text
memcpy(text, &Credit.pData[Credit.offset], len);
text[len] = '\0';
// Get cast ID
Credit.offset = a;
Credit.offset += 1;
len = GetScriptNumber(&Credit.pData[Credit.offset]);
// Create cast object
SetStripper(Credit.start_x, (WINDOW_HEIGHT + 8) * 0x200, text, len);
// Change offset
Credit.offset += 4;
return;
case '-': // Wait for X amount of frames
Credit.offset += 1;
Credit.wait = GetScriptNumber(&Credit.pData[Credit.offset]);
Credit.offset += 4;
Credit.mode = CREDIT_MODE_SCROLL_WAIT;
return;
case '+': // Change casts x-position
Credit.offset += 1;
Credit.start_x = GetScriptNumber(&Credit.pData[Credit.offset]) * 0x200;
Credit.offset += 4;
return;
case '/': // Stop credits
Credit.mode = CREDIT_MODE_STOP;
return;
case '!': // Change music
Credit.offset += 1;
a = GetScriptNumber(&Credit.pData[Credit.offset]);
Credit.offset += 4;
ChangeMusic((MusicID)a);
return;
case '~': // Start fading out music
Credit.offset += 1;
SetOrganyaFadeout();
#ifdef EXTRA_SOUND_FORMATS
ExtraSound_FadeOutMusic();
#endif
return;
case 'j': // Jump to label
Credit.offset += 1;
// Get number
b = GetScriptNumber(&Credit.pData[Credit.offset]);
// Change offset
Credit.offset += 4;
// Jump to specific label
if (1) // This appears to be a hacked-up duplicate of some code from the below 'f' condition
{
while (Credit.offset < Credit.size)
{
if (Credit.pData[Credit.offset] == 'l')
{
Credit.offset += 1;
a = GetScriptNumber(&Credit.pData[Credit.offset]);
Credit.offset += 4;
if (b == a)
break;
}
else
{
if (IsShiftJIS(Credit.pData[Credit.offset]))
Credit.offset += 2;
else
Credit.offset += 1;
}
}
}
return;
case 'f': // Flag jump
Credit.offset += 1;
// Read numbers XXXX:YYYY
a = GetScriptNumber(&Credit.pData[Credit.offset]);
Credit.offset += 5;
b = GetScriptNumber(&Credit.pData[Credit.offset]);
Credit.offset += 4;
// If flag is set
if (GetNPCFlag(a))
{
// Jump to label
while (Credit.offset < Credit.size)
{
if (Credit.pData[Credit.offset] == 'l')
{
Credit.offset += 1;
a = GetScriptNumber(&Credit.pData[Credit.offset]);
Credit.offset += 4;
if (b == a)
break;
}
else
{
if (IsShiftJIS(Credit.pData[Credit.offset]))
Credit.offset += 2;
else
Credit.offset += 1;
}
}
}
return;
default:
// Progress through file
Credit.offset += 1;
break;
}
}
}
// Update credits
void ActionCredit(void)
{
if (Credit.offset >= Credit.size)
return;
// Update script, or if waiting, decrement the wait value
switch (Credit.mode)
{
case CREDIT_MODE_SCROLL_READ:
ActionCredit_Read();
break;
case CREDIT_MODE_SCROLL_WAIT:
if (--Credit.wait <= 0)
Credit.mode = CREDIT_MODE_SCROLL_READ;
break;
}
}
// Change illustration
void SetCreditIllust(int a)
{
ReloadIllust(a);
Illust.act_no = ILLUSTRATION_ACTION_SLIDE_IN;
}
// Slide illustration off-screen
void CutCreditIllust(void)
{
Illust.act_no = ILLUSTRATION_ACTION_SLIDE_OUT;
}
// Scene of the island falling
int Scene_DownIsland(int mode)
{
ISLAND_SPRITE sprite;
int wait;
// Setup background
RECT rc_frame = {(WINDOW_WIDTH / 2) - 80, (WINDOW_HEIGHT / 2) - 40, (WINDOW_WIDTH / 2) + 80, (WINDOW_HEIGHT / 2) + 40};
RECT rc_sky = {0, 0, 160, 80};
RECT rc_ground = {160, 48, 320, 80};
// Setup island
RECT rc_sprite = {160, 0, 200, 24};
sprite.x = 168 * 0x200;
sprite.y = 64 * 0x200;
for (wait = 0; wait < 900; ++wait)
{
// Get pressed keys
GetTrg();
if (gKey & KEY_PAUSE)
{
switch (Call_Pause(1))
{
case enum_ESCRETURN_exit:
return enum_ESCRETURN_exit;
case enum_ESCRETURN_restart:
return enum_ESCRETURN_restart;
}
}
// Escape menu
if (gKey & KEY_ESCAPE)
{
switch (Call_Escape())
{
case enum_ESCRETURN_exit:
return enum_ESCRETURN_exit;
case enum_ESCRETURN_restart:
return enum_ESCRETURN_restart;
}
}
switch (mode)
{
case 0:
// Move down
sprite.y += 0x200 / 10;
break;
case 1:
if (wait < 350)
{
// Move down at normal speed
sprite.y += 0x200 / 10;
}
else if (wait < 500)
{
// Move down slower
sprite.y += 0x200 / 20;
}
else if (wait < 600)
{
// Move down slow
sprite.y += 0x200 / 40;
}
else if (wait == 750)
{
// End scene
wait = 900;
}
break;
}
// Draw scene
CortBox(&grcFull, 0);
PutBitmap3(&rc_frame, PixelToScreenCoord(80 + ((WINDOW_WIDTH - 320) / 2)), PixelToScreenCoord(80 + ((WINDOW_HEIGHT - 240) / 2)), &rc_sky, SURFACE_ID_LEVEL_SPRITESET_1);
PutBitmap3(&rc_frame, SubpixelToScreenCoord(sprite.x) - PixelToScreenCoord(20) + PixelToScreenCoord((WINDOW_WIDTH - 320) / 2), SubpixelToScreenCoord(sprite.y) - PixelToScreenCoord(12) + PixelToScreenCoord((WINDOW_HEIGHT - 240) / 2), &rc_sprite, SURFACE_ID_LEVEL_SPRITESET_1);
PutBitmap3(&rc_frame, PixelToScreenCoord(80 + ((WINDOW_WIDTH - 320) / 2)), PixelToScreenCoord(128 + ((WINDOW_HEIGHT - 240) / 2)), &rc_ground, SURFACE_ID_LEVEL_SPRITESET_1);
PutTimeCounter(16, 8);
// Draw window
PutFramePerSecound();
if (!Flip_SystemTask())
return enum_ESCRETURN_exit;
}
return enum_ESCRETURN_continue;
}