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