mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-24 02:36:27 +00:00
477 lines
12 KiB
C++
477 lines
12 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Save and restore scene and game.
|
|
*/
|
|
|
|
|
|
#include "tinsel/actors.h"
|
|
#include "tinsel/background.h"
|
|
#include "tinsel/config.h"
|
|
#include "tinsel/drives.h"
|
|
#include "tinsel/dw.h"
|
|
#include "tinsel/faders.h" // FadeOutFast()
|
|
#include "tinsel/graphics.h" // ClearScreen()
|
|
#include "tinsel/handle.h"
|
|
#include "tinsel/dialogs.h"
|
|
#include "tinsel/music.h"
|
|
#include "tinsel/pid.h"
|
|
#include "tinsel/play.h"
|
|
#include "tinsel/polygons.h"
|
|
#include "tinsel/rince.h"
|
|
#include "tinsel/savescn.h"
|
|
#include "tinsel/scene.h"
|
|
#include "tinsel/sched.h"
|
|
#include "tinsel/scroll.h"
|
|
#include "tinsel/sound.h"
|
|
#include "tinsel/sysvar.h"
|
|
#include "tinsel/tinlib.h"
|
|
#include "tinsel/token.h"
|
|
|
|
#include "common/textconsole.h"
|
|
|
|
namespace Tinsel {
|
|
|
|
//----------------- EXTERN FUNCTIONS --------------------
|
|
|
|
// in BG.C
|
|
extern SCNHANDLE GetBgroundHandle();
|
|
extern void SetDoFadeIn(bool tf);
|
|
|
|
// In DOS_DW.C
|
|
void RestoreMasterProcess(INT_CONTEXT *pic);
|
|
|
|
// in EVENTS.C (declared here and not in events.h because of strange goings-on)
|
|
void RestoreProcess(INT_CONTEXT *pic);
|
|
|
|
// in SCENE.C
|
|
extern SCNHANDLE GetSceneHandle();
|
|
|
|
|
|
//----------------- LOCAL DEFINES --------------------
|
|
|
|
enum {
|
|
RS_COUNT = 5, // Restore scene count
|
|
|
|
MAX_NEST = 4
|
|
};
|
|
|
|
//----------------- EXTERNAL GLOBAL DATA --------------------
|
|
|
|
extern int g_thingHeld;
|
|
extern int g_restoreCD;
|
|
extern SRSTATE g_SRstate;
|
|
|
|
//----------------- LOCAL GLOBAL DATA --------------------
|
|
|
|
// FIXME: Avoid non-const global vars
|
|
|
|
bool g_ASceneIsSaved = false;
|
|
|
|
static int g_savedSceneCount = 0;
|
|
|
|
static bool g_bNotDoneYet = false;
|
|
|
|
//static SAVED_DATA ssData[MAX_NEST];
|
|
static SAVED_DATA *g_ssData = NULL;
|
|
static SAVED_DATA g_sgData;
|
|
|
|
static SAVED_DATA *g_rsd = 0;
|
|
|
|
static int g_RestoreSceneCount = 0;
|
|
|
|
static bool g_bNoFade = false;
|
|
|
|
//----------------- FORWARD REFERENCES --------------------
|
|
|
|
/**
|
|
* Save current scene.
|
|
* @param sd Pointer to the scene data
|
|
*/
|
|
void DoSaveScene(SAVED_DATA *sd) {
|
|
sd->SavedSceneHandle = GetSceneHandle();
|
|
sd->SavedBgroundHandle = GetBgroundHandle();
|
|
SaveMovers(sd->SavedMoverInfo);
|
|
sd->NumSavedActors = SaveActors(sd->SavedActorInfo);
|
|
PlayfieldGetPos(FIELD_WORLD, &sd->SavedLoffset, &sd->SavedToffset);
|
|
SaveInterpretContexts(sd->SavedICInfo);
|
|
sd->SavedControl = ControlIsOn();
|
|
sd->SavedNoBlocking = GetNoBlocking();
|
|
GetNoScrollData(&sd->SavedNoScrollData);
|
|
|
|
if (TinselV2) {
|
|
// Tinsel 2 specific data save
|
|
SaveActorZ(sd->savedActorZ);
|
|
SaveZpositions(sd->zPositions);
|
|
SavePolygonStuff(sd->SavedPolygonStuff);
|
|
_vm->_pcmMusic->getTunePlaying(sd->SavedTune, sizeof(sd->SavedTune));
|
|
sd->bTinselDim = _vm->_pcmMusic->getMusicTinselDimmed();
|
|
sd->SavedScrollFocus = GetScrollFocus();
|
|
SaveSysVars(sd->SavedSystemVars);
|
|
SaveSoundReels(sd->SavedSoundReels);
|
|
|
|
} else {
|
|
// Tinsel 1 specific data save
|
|
SaveDeadPolys(sd->SavedDeadPolys);
|
|
CurrentMidiFacts(&sd->SavedMidi, &sd->SavedLoop);
|
|
}
|
|
|
|
g_ASceneIsSaved = true;
|
|
}
|
|
|
|
/**
|
|
* Initiate restoration of the saved scene.
|
|
* @param sd Pointer to the scene data
|
|
* @param bFadeOut Flag to perform a fade out
|
|
*/
|
|
void DoRestoreScene(SAVED_DATA *sd, bool bFadeOut) {
|
|
g_rsd = sd;
|
|
|
|
if (bFadeOut)
|
|
g_RestoreSceneCount = RS_COUNT + COUNTOUT_COUNT; // Set restore scene count
|
|
else
|
|
g_RestoreSceneCount = RS_COUNT; // Set restore scene count
|
|
}
|
|
|
|
void InitializeSaveScenes() {
|
|
if (g_ssData == NULL) {
|
|
g_ssData = (SAVED_DATA *)calloc(MAX_NEST, sizeof(SAVED_DATA));
|
|
if (g_ssData == NULL) {
|
|
error("Cannot allocate memory for scene changes");
|
|
}
|
|
} else {
|
|
// Re-initialize - no scenes saved
|
|
g_savedSceneCount = 0;
|
|
}
|
|
}
|
|
|
|
void FreeSaveScenes() {
|
|
free(g_ssData);
|
|
g_ssData = NULL;
|
|
}
|
|
|
|
/**
|
|
* Checks that all non-moving actors are playing the same reel as when
|
|
* the scene was saved.
|
|
* Also 'stand' all the moving actors at their saved positions.
|
|
*/
|
|
void sortActors(SAVED_DATA *sd) {
|
|
assert(!TinselV2);
|
|
for (int i = 0; i < sd->NumSavedActors; i++) {
|
|
ActorsLife(sd->SavedActorInfo[i].actorID, sd->SavedActorInfo[i].bAlive);
|
|
|
|
// Should be playing the same reel.
|
|
if (sd->SavedActorInfo[i].presFilm != 0) {
|
|
if (!actorAlive(sd->SavedActorInfo[i].actorID))
|
|
continue;
|
|
|
|
RestoreActorReels(sd->SavedActorInfo[i].presFilm, sd->SavedActorInfo[i].presRnum, sd->SavedActorInfo[i].zFactor,
|
|
sd->SavedActorInfo[i].presPlayX, sd->SavedActorInfo[i].presPlayY);
|
|
}
|
|
}
|
|
|
|
RestoreAuxScales(sd->SavedMoverInfo);
|
|
for (int i = 0; i < MAX_MOVERS; i++) {
|
|
if (sd->SavedMoverInfo[i].bActive)
|
|
Stand(Common::nullContext, sd->SavedMoverInfo[i].actorID, sd->SavedMoverInfo[i].objX,
|
|
sd->SavedMoverInfo[i].objY, sd->SavedMoverInfo[i].hLastfilm);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Stand all the moving actors at their saved positions.
|
|
* Not called from the foreground.
|
|
*/
|
|
static void SortMAProcess(CORO_PARAM, const void *) {
|
|
CORO_BEGIN_CONTEXT;
|
|
int i;
|
|
int viaActor;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
// Disable via actor for the stands
|
|
_ctx->viaActor = SysVar(ISV_DIVERT_ACTOR);
|
|
SetSysVar(ISV_DIVERT_ACTOR, 0);
|
|
|
|
RestoreAuxScales(g_rsd->SavedMoverInfo);
|
|
|
|
for (_ctx->i = 0; _ctx->i < MAX_MOVERS; _ctx->i++) {
|
|
if (g_rsd->SavedMoverInfo[_ctx->i].bActive) {
|
|
CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, g_rsd->SavedMoverInfo[_ctx->i].actorID,
|
|
g_rsd->SavedMoverInfo[_ctx->i].objX, g_rsd->SavedMoverInfo[_ctx->i].objY,
|
|
g_rsd->SavedMoverInfo[_ctx->i].hLastfilm));
|
|
|
|
if (g_rsd->SavedMoverInfo[_ctx->i].bHidden)
|
|
HideMover(GetMover(g_rsd->SavedMoverInfo[_ctx->i].actorID));
|
|
}
|
|
|
|
ActorPalette(g_rsd->SavedMoverInfo[_ctx->i].actorID,
|
|
g_rsd->SavedMoverInfo[_ctx->i].startColor, g_rsd->SavedMoverInfo[_ctx->i].paletteLength);
|
|
|
|
if (g_rsd->SavedMoverInfo[_ctx->i].brightness != BOGUS_BRIGHTNESS)
|
|
ActorBrightness(g_rsd->SavedMoverInfo[_ctx->i].actorID, g_rsd->SavedMoverInfo[_ctx->i].brightness);
|
|
}
|
|
|
|
// Restore via actor
|
|
SetSysVar(ISV_DIVERT_ACTOR, _ctx->viaActor);
|
|
|
|
g_bNotDoneYet = false;
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
void ResumeInterprets() {
|
|
// Master script only affected on restore game, not restore scene
|
|
if (!TinselV2 && (g_rsd == &g_sgData)) {
|
|
CoroScheduler.killMatchingProcess(PID_MASTER_SCR, -1);
|
|
FreeMasterInterpretContext();
|
|
}
|
|
|
|
for (int i = 0; i < NUM_INTERPRET; i++) {
|
|
switch (g_rsd->SavedICInfo[i].GSort) {
|
|
case GS_NONE:
|
|
break;
|
|
|
|
case GS_INVENTORY:
|
|
if (g_rsd->SavedICInfo[i].event != POINTED) {
|
|
RestoreProcess(&g_rsd->SavedICInfo[i]);
|
|
}
|
|
break;
|
|
|
|
case GS_MASTER:
|
|
// Master script only affected on restore game, not restore scene
|
|
if (g_rsd == &g_sgData)
|
|
RestoreMasterProcess(&g_rsd->SavedICInfo[i]);
|
|
break;
|
|
|
|
case GS_PROCESS:
|
|
// Tinsel 2 process
|
|
RestoreSceneProcess(&g_rsd->SavedICInfo[i]);
|
|
break;
|
|
|
|
case GS_GPROCESS:
|
|
// Tinsel 2 Global processes only affected on restore game, not restore scene
|
|
if (g_rsd == &g_sgData)
|
|
RestoreGlobalProcess(&g_rsd->SavedICInfo[i]);
|
|
break;
|
|
|
|
case GS_ACTOR:
|
|
if (TinselV2)
|
|
RestoreProcess(&g_rsd->SavedICInfo[i]);
|
|
else
|
|
RestoreActorProcess(g_rsd->SavedICInfo[i].idActor, &g_rsd->SavedICInfo[i], g_rsd == &g_sgData);
|
|
break;
|
|
|
|
case GS_POLYGON:
|
|
case GS_SCENE:
|
|
RestoreProcess(&g_rsd->SavedICInfo[i]);
|
|
break;
|
|
|
|
default:
|
|
warning("Unhandled GSort in ResumeInterprets");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Do restore scene
|
|
* @param n Id
|
|
*/
|
|
static int DoRestoreSceneFrame(SAVED_DATA *sd, int n) {
|
|
switch (n) {
|
|
case RS_COUNT + COUNTOUT_COUNT:
|
|
// Trigger pre-load and fade and start countdown
|
|
FadeOutFast(NULL);
|
|
break;
|
|
|
|
case RS_COUNT:
|
|
_vm->_sound->stopAllSamples();
|
|
ClearScreen();
|
|
|
|
if (TinselV2) {
|
|
|
|
// Master script only affected on restore game, not restore scene
|
|
if (sd == &g_sgData) {
|
|
CoroScheduler.killMatchingProcess(PID_MASTER_SCR);
|
|
KillGlobalProcesses();
|
|
FreeMasterInterpretContext();
|
|
}
|
|
|
|
RestorePolygonStuff(sd->SavedPolygonStuff);
|
|
|
|
// Abandon temporarily if different CD
|
|
if (sd == &g_sgData && g_restoreCD != GetCurrentCD()) {
|
|
g_SRstate = SR_IDLE;
|
|
|
|
EndScene();
|
|
SetNextCD(g_restoreCD);
|
|
CDChangeForRestore(g_restoreCD);
|
|
|
|
return 0;
|
|
}
|
|
} else {
|
|
RestoreDeadPolys(sd->SavedDeadPolys);
|
|
}
|
|
|
|
// Start up the scene
|
|
StartNewScene(sd->SavedSceneHandle, NO_ENTRY_NUM);
|
|
|
|
SetDoFadeIn(!g_bNoFade);
|
|
g_bNoFade = false;
|
|
StartupBackground(Common::nullContext, sd->SavedBgroundHandle);
|
|
|
|
if (TinselV2) {
|
|
Offset(EX_USEXY, sd->SavedLoffset, sd->SavedToffset);
|
|
} else {
|
|
KillScroll();
|
|
PlayfieldSetPos(FIELD_WORLD, sd->SavedLoffset, sd->SavedToffset);
|
|
SetNoBlocking(sd->SavedNoBlocking);
|
|
}
|
|
|
|
RestoreNoScrollData(&sd->SavedNoScrollData);
|
|
|
|
if (TinselV2) {
|
|
// create process to sort out the moving actors
|
|
CoroScheduler.createProcess(PID_MOVER, SortMAProcess, NULL, 0);
|
|
g_bNotDoneYet = true;
|
|
|
|
RestoreActorZ(sd->savedActorZ);
|
|
RestoreZpositions(sd->zPositions);
|
|
RestoreSysVars(sd->SavedSystemVars);
|
|
CreateGhostPalette(BgPal());
|
|
RestoreActors(sd->NumSavedActors, sd->SavedActorInfo);
|
|
RestoreSoundReels(sd->SavedSoundReels);
|
|
return 1;
|
|
}
|
|
|
|
sortActors(sd);
|
|
break;
|
|
|
|
case 2:
|
|
break;
|
|
|
|
case 1:
|
|
if (TinselV2) {
|
|
if (g_bNotDoneYet)
|
|
return n;
|
|
|
|
if (sd == &g_sgData)
|
|
HoldItem(g_thingHeld, true);
|
|
if (sd->bTinselDim)
|
|
_vm->_pcmMusic->dim(true);
|
|
_vm->_pcmMusic->restoreThatTune(sd->SavedTune);
|
|
ScrollFocus(sd->SavedScrollFocus);
|
|
} else {
|
|
RestoreMidiFacts(sd->SavedMidi, sd->SavedLoop);
|
|
}
|
|
|
|
if (sd->SavedControl)
|
|
ControlOn(); // Control was on
|
|
ResumeInterprets();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return n - 1;
|
|
}
|
|
|
|
/**
|
|
* Restore game
|
|
* @param num num
|
|
*/
|
|
void RestoreGame(int num) {
|
|
KillInventory();
|
|
|
|
RequestRestoreGame(num, &g_sgData, &g_savedSceneCount, g_ssData);
|
|
|
|
// Actual restoring is performed by ProcessSRQueue
|
|
}
|
|
|
|
/**
|
|
* Save game
|
|
* @param name Name of savegame
|
|
* @param desc Description of savegame
|
|
*/
|
|
void SaveGame(char *name, char *desc) {
|
|
// Get current scene data
|
|
DoSaveScene(&g_sgData);
|
|
|
|
RequestSaveGame(name, desc, &g_sgData, &g_savedSceneCount, g_ssData);
|
|
|
|
// Actual saving is performed by ProcessSRQueue
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------------
|
|
|
|
bool IsRestoringScene() {
|
|
if (g_RestoreSceneCount) {
|
|
g_RestoreSceneCount = DoRestoreSceneFrame(g_rsd, g_RestoreSceneCount);
|
|
}
|
|
|
|
return g_RestoreSceneCount ? true : false;
|
|
}
|
|
|
|
/**
|
|
* Restores Scene
|
|
*/
|
|
void TinselRestoreScene(bool bFade) {
|
|
// only called by restore_scene PCODE
|
|
if (g_RestoreSceneCount == 0) {
|
|
assert(g_savedSceneCount >= 1); // No saved scene to restore
|
|
|
|
if (g_ASceneIsSaved)
|
|
DoRestoreScene(&g_ssData[--g_savedSceneCount], bFade);
|
|
if (!bFade)
|
|
g_bNoFade = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Please Save Scene
|
|
*/
|
|
void TinselSaveScene(CORO_PARAM) {
|
|
// only called by save_scene PCODE
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
assert(g_savedSceneCount < MAX_NEST); // nesting limit reached
|
|
|
|
// Don't save the same thing multiple times!
|
|
// FIXME/TODO: Maybe this can be changed to an assert?
|
|
if (g_savedSceneCount && g_ssData[g_savedSceneCount-1].SavedSceneHandle == GetSceneHandle())
|
|
CORO_KILL_SELF();
|
|
|
|
DoSaveScene(&g_ssData[g_savedSceneCount++]);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
} // End of namespace Tinsel
|