1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 * Save and restore scene and game.
22 */
23
24
25 #include "tinsel/actors.h"
26 #include "tinsel/background.h"
27 #include "tinsel/config.h"
28 #include "tinsel/drives.h"
29 #include "tinsel/dw.h"
30 #include "tinsel/faders.h" // FadeOutFast()
31 #include "tinsel/graphics.h" // ClearScreen()
32 #include "tinsel/handle.h"
33 #include "tinsel/dialogs.h"
34 #include "tinsel/music.h"
35 #include "tinsel/pid.h"
36 #include "tinsel/play.h"
37 #include "tinsel/polygons.h"
38 #include "tinsel/rince.h"
39 #include "tinsel/savescn.h"
40 #include "tinsel/scene.h"
41 #include "tinsel/sched.h"
42 #include "tinsel/scroll.h"
43 #include "tinsel/sound.h"
44 #include "tinsel/sysvar.h"
45 #include "tinsel/tinlib.h"
46 #include "tinsel/token.h"
47
48 #include "common/textconsole.h"
49
50 namespace Tinsel {
51
52 //----------------- EXTERN FUNCTIONS --------------------
53
54 // in BG.C
55 extern SCNHANDLE GetBgroundHandle();
56 extern void SetDoFadeIn(bool tf);
57
58 // In DOS_DW.C
59 void RestoreMasterProcess(INT_CONTEXT *pic);
60
61 // in EVENTS.C (declared here and not in events.h because of strange goings-on)
62 void RestoreProcess(INT_CONTEXT *pic);
63
64 // in SCENE.C
65 extern SCNHANDLE GetSceneHandle();
66
67
68 //----------------- LOCAL DEFINES --------------------
69
70 enum {
71 RS_COUNT = 5, // Restore scene count
72
73 MAX_NEST = 4
74 };
75
76 //----------------- EXTERNAL GLOBAL DATA --------------------
77
78 extern int g_thingHeld;
79 extern int g_restoreCD;
80 extern SRSTATE g_SRstate;
81
82 //----------------- LOCAL GLOBAL DATA --------------------
83
84 // FIXME: Avoid non-const global vars
85
86 bool g_ASceneIsSaved = false;
87
88 static int g_savedSceneCount = 0;
89
90 static bool g_bNotDoneYet = false;
91
92 //static SAVED_DATA ssData[MAX_NEST];
93 static SAVED_DATA *g_ssData = NULL;
94 static SAVED_DATA g_sgData;
95
96 static SAVED_DATA *g_rsd = 0;
97
98 static int g_RestoreSceneCount = 0;
99
100 static bool g_bNoFade = false;
101
102 //----------------- FORWARD REFERENCES --------------------
103
104 /**
105 * Save current scene.
106 * @param sd Pointer to the scene data
107 */
DoSaveScene(SAVED_DATA * sd)108 void DoSaveScene(SAVED_DATA *sd) {
109 sd->SavedSceneHandle = GetSceneHandle();
110 sd->SavedBgroundHandle = GetBgroundHandle();
111 SaveMovers(sd->SavedMoverInfo);
112 sd->NumSavedActors = SaveActors(sd->SavedActorInfo);
113 PlayfieldGetPos(FIELD_WORLD, &sd->SavedLoffset, &sd->SavedToffset);
114 SaveInterpretContexts(sd->SavedICInfo);
115 sd->SavedControl = ControlIsOn();
116 sd->SavedNoBlocking = GetNoBlocking();
117 GetNoScrollData(&sd->SavedNoScrollData);
118
119 if (TinselV2) {
120 // Tinsel 2 specific data save
121 SaveActorZ(sd->savedActorZ);
122 SaveZpositions(sd->zPositions);
123 SavePolygonStuff(sd->SavedPolygonStuff);
124 _vm->_pcmMusic->getTunePlaying(sd->SavedTune, sizeof(sd->SavedTune));
125 sd->bTinselDim = _vm->_pcmMusic->getMusicTinselDimmed();
126 sd->SavedScrollFocus = GetScrollFocus();
127 SaveSysVars(sd->SavedSystemVars);
128 SaveSoundReels(sd->SavedSoundReels);
129
130 } else {
131 // Tinsel 1 specific data save
132 SaveDeadPolys(sd->SavedDeadPolys);
133 CurrentMidiFacts(&sd->SavedMidi, &sd->SavedLoop);
134 }
135
136 g_ASceneIsSaved = true;
137 }
138
139 /**
140 * Initiate restoration of the saved scene.
141 * @param sd Pointer to the scene data
142 * @param bFadeOut Flag to perform a fade out
143 */
DoRestoreScene(SAVED_DATA * sd,bool bFadeOut)144 void DoRestoreScene(SAVED_DATA *sd, bool bFadeOut) {
145 g_rsd = sd;
146
147 if (bFadeOut)
148 g_RestoreSceneCount = RS_COUNT + COUNTOUT_COUNT; // Set restore scene count
149 else
150 g_RestoreSceneCount = RS_COUNT; // Set restore scene count
151 }
152
InitializeSaveScenes()153 void InitializeSaveScenes() {
154 if (g_ssData == NULL) {
155 g_ssData = (SAVED_DATA *)calloc(MAX_NEST, sizeof(SAVED_DATA));
156 if (g_ssData == NULL) {
157 error("Cannot allocate memory for scene changes");
158 }
159 } else {
160 // Re-initialize - no scenes saved
161 g_savedSceneCount = 0;
162 }
163 }
164
FreeSaveScenes()165 void FreeSaveScenes() {
166 free(g_ssData);
167 g_ssData = NULL;
168 }
169
170 /**
171 * Checks that all non-moving actors are playing the same reel as when
172 * the scene was saved.
173 * Also 'stand' all the moving actors at their saved positions.
174 */
sortActors(SAVED_DATA * sd)175 void sortActors(SAVED_DATA *sd) {
176 assert(!TinselV2);
177 for (int i = 0; i < sd->NumSavedActors; i++) {
178 ActorsLife(sd->SavedActorInfo[i].actorID, sd->SavedActorInfo[i].bAlive);
179
180 // Should be playing the same reel.
181 if (sd->SavedActorInfo[i].presFilm != 0) {
182 if (!actorAlive(sd->SavedActorInfo[i].actorID))
183 continue;
184
185 RestoreActorReels(sd->SavedActorInfo[i].presFilm, sd->SavedActorInfo[i].presRnum, sd->SavedActorInfo[i].zFactor,
186 sd->SavedActorInfo[i].presPlayX, sd->SavedActorInfo[i].presPlayY);
187 }
188 }
189
190 RestoreAuxScales(sd->SavedMoverInfo);
191 for (int i = 0; i < MAX_MOVERS; i++) {
192 if (sd->SavedMoverInfo[i].bActive)
193 Stand(Common::nullContext, sd->SavedMoverInfo[i].actorID, sd->SavedMoverInfo[i].objX,
194 sd->SavedMoverInfo[i].objY, sd->SavedMoverInfo[i].hLastfilm);
195 }
196 }
197
198 /**
199 * Stand all the moving actors at their saved positions.
200 * Not called from the foreground.
201 */
SortMAProcess(CORO_PARAM,const void *)202 static void SortMAProcess(CORO_PARAM, const void *) {
203 CORO_BEGIN_CONTEXT;
204 int i;
205 int viaActor;
206 CORO_END_CONTEXT(_ctx);
207
208
209 CORO_BEGIN_CODE(_ctx);
210
211 // Disable via actor for the stands
212 _ctx->viaActor = SysVar(ISV_DIVERT_ACTOR);
213 SetSysVar(ISV_DIVERT_ACTOR, 0);
214
215 RestoreAuxScales(g_rsd->SavedMoverInfo);
216
217 for (_ctx->i = 0; _ctx->i < MAX_MOVERS; _ctx->i++) {
218 if (g_rsd->SavedMoverInfo[_ctx->i].bActive) {
219 CORO_INVOKE_ARGS(Stand, (CORO_SUBCTX, g_rsd->SavedMoverInfo[_ctx->i].actorID,
220 g_rsd->SavedMoverInfo[_ctx->i].objX, g_rsd->SavedMoverInfo[_ctx->i].objY,
221 g_rsd->SavedMoverInfo[_ctx->i].hLastfilm));
222
223 if (g_rsd->SavedMoverInfo[_ctx->i].bHidden)
224 HideMover(GetMover(g_rsd->SavedMoverInfo[_ctx->i].actorID));
225 }
226
227 ActorPalette(g_rsd->SavedMoverInfo[_ctx->i].actorID,
228 g_rsd->SavedMoverInfo[_ctx->i].startColor, g_rsd->SavedMoverInfo[_ctx->i].paletteLength);
229
230 if (g_rsd->SavedMoverInfo[_ctx->i].brightness != BOGUS_BRIGHTNESS)
231 ActorBrightness(g_rsd->SavedMoverInfo[_ctx->i].actorID, g_rsd->SavedMoverInfo[_ctx->i].brightness);
232 }
233
234 // Restore via actor
235 SetSysVar(ISV_DIVERT_ACTOR, _ctx->viaActor);
236
237 g_bNotDoneYet = false;
238
239 CORO_END_CODE;
240 }
241
242
243 //---------------------------------------------------------------------------
244
ResumeInterprets()245 void ResumeInterprets() {
246 // Master script only affected on restore game, not restore scene
247 if (!TinselV2 && (g_rsd == &g_sgData)) {
248 CoroScheduler.killMatchingProcess(PID_MASTER_SCR, -1);
249 FreeMasterInterpretContext();
250 }
251
252 for (int i = 0; i < NUM_INTERPRET; i++) {
253 switch (g_rsd->SavedICInfo[i].GSort) {
254 case GS_NONE:
255 break;
256
257 case GS_INVENTORY:
258 if (g_rsd->SavedICInfo[i].event != POINTED) {
259 RestoreProcess(&g_rsd->SavedICInfo[i]);
260 }
261 break;
262
263 case GS_MASTER:
264 // Master script only affected on restore game, not restore scene
265 if (g_rsd == &g_sgData)
266 RestoreMasterProcess(&g_rsd->SavedICInfo[i]);
267 break;
268
269 case GS_PROCESS:
270 // Tinsel 2 process
271 RestoreSceneProcess(&g_rsd->SavedICInfo[i]);
272 break;
273
274 case GS_GPROCESS:
275 // Tinsel 2 Global processes only affected on restore game, not restore scene
276 if (g_rsd == &g_sgData)
277 RestoreGlobalProcess(&g_rsd->SavedICInfo[i]);
278 break;
279
280 case GS_ACTOR:
281 if (TinselV2)
282 RestoreProcess(&g_rsd->SavedICInfo[i]);
283 else
284 RestoreActorProcess(g_rsd->SavedICInfo[i].idActor, &g_rsd->SavedICInfo[i], g_rsd == &g_sgData);
285 break;
286
287 case GS_POLYGON:
288 case GS_SCENE:
289 RestoreProcess(&g_rsd->SavedICInfo[i]);
290 break;
291
292 default:
293 warning("Unhandled GSort in ResumeInterprets");
294 }
295 }
296 }
297
298 /**
299 * Do restore scene
300 * @param n Id
301 */
DoRestoreSceneFrame(SAVED_DATA * sd,int n)302 static int DoRestoreSceneFrame(SAVED_DATA *sd, int n) {
303 switch (n) {
304 case RS_COUNT + COUNTOUT_COUNT:
305 // Trigger pre-load and fade and start countdown
306 FadeOutFast();
307 break;
308
309 case RS_COUNT:
310 _vm->_sound->stopAllSamples();
311 ClearScreen();
312
313 if (TinselV2) {
314
315 // Master script only affected on restore game, not restore scene
316 if (sd == &g_sgData) {
317 CoroScheduler.killMatchingProcess(PID_MASTER_SCR);
318 KillGlobalProcesses();
319 FreeMasterInterpretContext();
320 }
321
322 RestorePolygonStuff(sd->SavedPolygonStuff);
323
324 // Abandon temporarily if different CD
325 if (sd == &g_sgData && g_restoreCD != GetCurrentCD()) {
326 g_SRstate = SR_IDLE;
327
328 EndScene();
329 SetNextCD(g_restoreCD);
330 CDChangeForRestore(g_restoreCD);
331
332 return 0;
333 }
334 } else {
335 RestoreDeadPolys(sd->SavedDeadPolys);
336 }
337
338 // Start up the scene
339 StartNewScene(sd->SavedSceneHandle, NO_ENTRY_NUM);
340
341 SetDoFadeIn(!g_bNoFade);
342 g_bNoFade = false;
343 StartupBackground(Common::nullContext, sd->SavedBgroundHandle);
344
345 if (TinselV2) {
346 Offset(EX_USEXY, sd->SavedLoffset, sd->SavedToffset);
347 } else {
348 KillScroll();
349 PlayfieldSetPos(FIELD_WORLD, sd->SavedLoffset, sd->SavedToffset);
350 SetNoBlocking(sd->SavedNoBlocking);
351 }
352
353 RestoreNoScrollData(&sd->SavedNoScrollData);
354
355 if (TinselV2) {
356 // create process to sort out the moving actors
357 CoroScheduler.createProcess(PID_MOVER, SortMAProcess, NULL, 0);
358 g_bNotDoneYet = true;
359
360 RestoreActorZ(sd->savedActorZ);
361 RestoreZpositions(sd->zPositions);
362 RestoreSysVars(sd->SavedSystemVars);
363 RestoreActors(sd->NumSavedActors, sd->SavedActorInfo);
364 RestoreSoundReels(sd->SavedSoundReels);
365 return 1;
366 }
367
368 sortActors(sd);
369 break;
370
371 case 2:
372 break;
373
374 case 1:
375 if (TinselV2) {
376 if (g_bNotDoneYet)
377 return n;
378
379 if (sd == &g_sgData)
380 HoldItem(g_thingHeld, true);
381 if (sd->bTinselDim)
382 _vm->_pcmMusic->dim(true);
383 _vm->_pcmMusic->restoreThatTune(sd->SavedTune);
384 ScrollFocus(sd->SavedScrollFocus);
385 } else {
386 RestoreMidiFacts(sd->SavedMidi, sd->SavedLoop);
387 }
388
389 if (sd->SavedControl)
390 ControlOn(); // Control was on
391 ResumeInterprets();
392 break;
393
394 default:
395 break;
396 }
397
398 return n - 1;
399 }
400
401 /**
402 * Restore game
403 * @param num num
404 */
RestoreGame(int num)405 void RestoreGame(int num) {
406 KillInventory();
407
408 RequestRestoreGame(num, &g_sgData, &g_savedSceneCount, g_ssData);
409
410 // Actual restoring is performed by ProcessSRQueue
411 }
412
413 /**
414 * Save game
415 * @param name Name of savegame
416 * @param desc Description of savegame
417 */
SaveGame(char * name,char * desc)418 void SaveGame(char *name, char *desc) {
419 // Get current scene data
420 DoSaveScene(&g_sgData);
421
422 RequestSaveGame(name, desc, &g_sgData, &g_savedSceneCount, g_ssData);
423
424 // Actual saving is performed by ProcessSRQueue
425 }
426
427
428 //---------------------------------------------------------------------------------
429
IsRestoringScene()430 bool IsRestoringScene() {
431 if (g_RestoreSceneCount) {
432 g_RestoreSceneCount = DoRestoreSceneFrame(g_rsd, g_RestoreSceneCount);
433 }
434
435 return g_RestoreSceneCount ? true : false;
436 }
437
438 /**
439 * Restores Scene
440 */
TinselRestoreScene(bool bFade)441 void TinselRestoreScene(bool bFade) {
442 // only called by restore_scene PCODE
443 if (g_RestoreSceneCount == 0) {
444 assert(g_savedSceneCount >= 1); // No saved scene to restore
445
446 if (g_ASceneIsSaved)
447 DoRestoreScene(&g_ssData[--g_savedSceneCount], bFade);
448 if (!bFade)
449 g_bNoFade = true;
450 }
451 }
452
453 /**
454 * Please Save Scene
455 */
TinselSaveScene(CORO_PARAM)456 void TinselSaveScene(CORO_PARAM) {
457 // only called by save_scene PCODE
458 CORO_BEGIN_CONTEXT;
459 CORO_END_CONTEXT(_ctx);
460
461 CORO_BEGIN_CODE(_ctx);
462
463 assert(g_savedSceneCount < MAX_NEST); // nesting limit reached
464
465 // Don't save the same thing multiple times!
466 // FIXME/TODO: Maybe this can be changed to an assert?
467 if (g_savedSceneCount && g_ssData[g_savedSceneCount-1].SavedSceneHandle == GetSceneHandle())
468 CORO_KILL_SELF();
469
470 DoSaveScene(&g_ssData[g_savedSceneCount++]);
471
472 CORO_END_CODE;
473 }
474
475 } // End of namespace Tinsel
476