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  */
22 
23 #include "common/scummsys.h"
24 
25 #include "zvision/scripting/script_manager.h"
26 
27 #include "zvision/zvision.h"
28 #include "zvision/graphics/render_manager.h"
29 #include "zvision/graphics/cursors/cursor_manager.h"
30 #include "zvision/file/save_manager.h"
31 #include "zvision/scripting/actions.h"
32 #include "zvision/scripting/menu.h"
33 #include "zvision/scripting/effects/timer_effect.h"
34 
35 #include "common/algorithm.h"
36 #include "common/hashmap.h"
37 #include "common/debug.h"
38 #include "common/stream.h"
39 #include "common/config-manager.h"
40 
41 namespace ZVision {
42 
ScriptManager(ZVision * engine)43 ScriptManager::ScriptManager(ZVision *engine)
44 	: _engine(engine),
45 	  _currentlyFocusedControl(0),
46 	  _activeControls(NULL) {
47 }
48 
~ScriptManager()49 ScriptManager::~ScriptManager() {
50 	cleanScriptScope(universe);
51 	cleanScriptScope(world);
52 	cleanScriptScope(room);
53 	cleanScriptScope(nodeview);
54 	_controlEvents.clear();
55 }
56 
initialize()57 void ScriptManager::initialize() {
58 	cleanScriptScope(universe);
59 	cleanScriptScope(world);
60 	cleanScriptScope(room);
61 	cleanScriptScope(nodeview);
62 
63 	_currentLocation.node = 0;
64 	_currentLocation.world = 0;
65 	_currentLocation.room = 0;
66 	_currentLocation.view = 0;
67 
68 	_changeLocationDelayCycles = 0;
69 
70 	parseScrFile("universe.scr", universe);
71 	changeLocation('g', 'a', 'r', 'y', 0);
72 
73 	_controlEvents.clear();
74 }
75 
update(uint deltaTimeMillis)76 void ScriptManager::update(uint deltaTimeMillis) {
77 	if (_currentLocation != _nextLocation) {
78 		// The location is changing. The script that did that may have
79 		// triggered other scripts, so give them all one extra cycle to
80 		// run. This fixes some missing scoring in ZGI, and quite
81 		// possibly other minor glitches as well.
82 		//
83 		// Another idea would be to change if there are pending scripts
84 		// in the exec queues, but that could cause this to hang
85 		// indefinitely.
86 		if (_changeLocationDelayCycles-- <= 0) {
87 			ChangeLocationReal(false);
88 		}
89 	}
90 
91 	updateNodes(deltaTimeMillis);
92 	if (!execScope(nodeview)) {
93 		return;
94 	}
95 	if (!execScope(room)) {
96 		return;
97 	}
98 	if (!execScope(world)) {
99 		return;
100 	}
101 	if (!execScope(universe)) {
102 		return;
103 	}
104 	updateControls(deltaTimeMillis);
105 }
106 
execScope(ScriptScope & scope)107 bool ScriptManager::execScope(ScriptScope &scope) {
108 	// Swap queues
109 	PuzzleList *tmp = scope.execQueue;
110 	scope.execQueue = scope.scopeQueue;
111 	scope.scopeQueue = tmp;
112 	scope.scopeQueue->clear();
113 
114 	for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) {
115 		(*PuzzleIter)->addedBySetState = false;
116 	}
117 
118 	if (scope.procCount < 2 || getStateValue(StateKey_ExecScopeStyle)) {
119 		for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) {
120 			if (!checkPuzzleCriteria(*PuzzleIter, scope.procCount)) {
121 				return false;
122 			}
123 		}
124 	} else {
125 		for (PuzzleList::iterator PuzzleIter = scope.execQueue->begin(); PuzzleIter != scope.execQueue->end(); ++PuzzleIter) {
126 			if (!checkPuzzleCriteria(*PuzzleIter, scope.procCount)) {
127 				return false;
128 			}
129 		}
130 	}
131 
132 	if (scope.procCount < 2) {
133 		scope.procCount++;
134 	}
135 	return true;
136 }
137 
referenceTableAddPuzzle(uint32 key,PuzzleRef ref)138 void ScriptManager::referenceTableAddPuzzle(uint32 key, PuzzleRef ref) {
139 	if (_referenceTable.contains(key)) {
140 		Common::Array<PuzzleRef> *arr = &_referenceTable[key];
141 		for (uint32 i = 0; i < arr->size(); i++) {
142 			if ((*arr)[i].puz == ref.puz) {
143 				return;
144 			}
145 		}
146 	}
147 
148 	_referenceTable[key].push_back(ref);
149 }
150 
addPuzzlesToReferenceTable(ScriptScope & scope)151 void ScriptManager::addPuzzlesToReferenceTable(ScriptScope &scope) {
152 	// Iterate through each local Puzzle
153 	for (PuzzleList::iterator PuzzleIter = scope.puzzles.begin(); PuzzleIter != scope.puzzles.end(); ++PuzzleIter) {
154 		Puzzle *puzzlePtr = (*PuzzleIter);
155 
156 		PuzzleRef ref;
157 		ref.scope = &scope;
158 		ref.puz = puzzlePtr;
159 
160 		referenceTableAddPuzzle(puzzlePtr->key, ref);
161 
162 		// Iterate through each CriteriaEntry and add a reference from the criteria key to the Puzzle
163 		for (Common::List<Common::List<Puzzle::CriteriaEntry> >::iterator criteriaIter = (*PuzzleIter)->criteriaList.begin(); criteriaIter != (*PuzzleIter)->criteriaList.end(); ++criteriaIter) {
164 			for (Common::List<Puzzle::CriteriaEntry>::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) {
165 				referenceTableAddPuzzle(entryIter->key, ref);
166 			}
167 		}
168 	}
169 }
170 
updateNodes(uint deltaTimeMillis)171 void ScriptManager::updateNodes(uint deltaTimeMillis) {
172 	// If process() returns true, it means the node can be deleted
173 	for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end();) {
174 		if ((*iter)->process(deltaTimeMillis)) {
175 			delete(*iter);
176 			// Remove the node
177 			iter = _activeSideFx.erase(iter);
178 		} else {
179 			++iter;
180 		}
181 	}
182 }
183 
updateControls(uint deltaTimeMillis)184 void ScriptManager::updateControls(uint deltaTimeMillis) {
185 	if (!_activeControls) {
186 		return;
187 	}
188 
189 	// Process only one event
190 	if (!_controlEvents.empty()) {
191 		Common::Event _event = _controlEvents.front();
192 		Common::Point imageCoord;
193 		switch (_event.type) {
194 		case Common::EVENT_LBUTTONDOWN:
195 			imageCoord = _engine->getRenderManager()->screenSpaceToImageSpace(_event.mouse);
196 			onMouseDown(_event.mouse, imageCoord);
197 			break;
198 		case Common::EVENT_LBUTTONUP:
199 			imageCoord = _engine->getRenderManager()->screenSpaceToImageSpace(_event.mouse);
200 			onMouseUp(_event.mouse, imageCoord);
201 			break;
202 		case Common::EVENT_KEYDOWN:
203 			onKeyDown(_event.kbd);
204 			break;
205 		case Common::EVENT_KEYUP:
206 			onKeyUp(_event.kbd);
207 			break;
208 		default:
209 			break;
210 		}
211 		_controlEvents.pop_front();
212 	}
213 
214 	for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); iter++) {
215 		if ((*iter)->process(deltaTimeMillis)) {
216 			break;
217 		}
218 	}
219 }
220 
checkPuzzleCriteria(Puzzle * puzzle,uint counter)221 bool ScriptManager::checkPuzzleCriteria(Puzzle *puzzle, uint counter) {
222 	// Check if the puzzle is already finished
223 	// Also check that the puzzle isn't disabled
224 	if (getStateValue(puzzle->key) == 1 || (getStateFlag(puzzle->key) & Puzzle::DISABLED)) {
225 		return true;
226 	}
227 
228 	// Check each Criteria
229 	if (counter == 0 && (getStateFlag(puzzle->key) & Puzzle::DO_ME_NOW) == 0) {
230 		return true;
231 	}
232 
233 	bool criteriaMet = false;
234 	for (Common::List<Common::List<Puzzle::CriteriaEntry> >::iterator criteriaIter = puzzle->criteriaList.begin(); criteriaIter != puzzle->criteriaList.end(); ++criteriaIter) {
235 		criteriaMet = false;
236 
237 		for (Common::List<Puzzle::CriteriaEntry>::iterator entryIter = criteriaIter->begin(); entryIter != criteriaIter->end(); ++entryIter) {
238 			// Get the value to compare against
239 			int argumentValue;
240 			if (entryIter->argumentIsAKey) {
241 				argumentValue = getStateValue(entryIter->argument);
242 			} else {
243 				argumentValue = entryIter->argument;
244 			}
245 
246 			// Do the comparison
247 			switch (entryIter->criteriaOperator) {
248 			case Puzzle::EQUAL_TO:
249 				criteriaMet = getStateValue(entryIter->key) == argumentValue;
250 				break;
251 			case Puzzle::NOT_EQUAL_TO:
252 				criteriaMet = getStateValue(entryIter->key) != argumentValue;
253 				break;
254 			case Puzzle::GREATER_THAN:
255 				criteriaMet = getStateValue(entryIter->key) > argumentValue;
256 				break;
257 			case Puzzle::LESS_THAN:
258 				criteriaMet = getStateValue(entryIter->key) < argumentValue;
259 				break;
260 			default:
261 				break;
262 			}
263 
264 			// If one check returns false, don't keep checking
265 			if (!criteriaMet) {
266 				break;
267 			}
268 		}
269 
270 		// If any of the Criteria are *fully* met, then execute the results
271 		if (criteriaMet) {
272 			break;
273 		}
274 	}
275 
276 	// criteriaList can be empty. Aka, the puzzle should be executed immediately
277 	if (puzzle->criteriaList.empty() || criteriaMet) {
278 		debug(1, "Puzzle %u criteria passed. Executing its ResultActions", puzzle->key);
279 
280 		// Set the puzzle as completed
281 		setStateValue(puzzle->key, 1);
282 
283 		for (Common::List<ResultAction *>::iterator resultIter = puzzle->resultActions.begin(); resultIter != puzzle->resultActions.end(); ++resultIter) {
284 			if (!(*resultIter)->execute()) {
285 				return false;
286 			}
287 		}
288 	}
289 
290 	return true;
291 }
292 
cleanStateTable()293 void ScriptManager::cleanStateTable() {
294 	for (StateMap::iterator iter = _globalState.begin(); iter != _globalState.end(); ++iter) {
295 		// If the value is equal to zero, we can purge it since getStateValue()
296 		// will return zero if _globalState doesn't contain a key
297 		if (iter->_value == 0) {
298 			// Remove the node
299 			_globalState.erase(iter);
300 		}
301 	}
302 }
303 
cleanScriptScope(ScriptScope & scope)304 void ScriptManager::cleanScriptScope(ScriptScope &scope) {
305 	scope.privQueueOne.clear();
306 	scope.privQueueTwo.clear();
307 	scope.scopeQueue = &scope.privQueueOne;
308 	scope.execQueue = &scope.privQueueTwo;
309 	for (PuzzleList::iterator iter = scope.puzzles.begin(); iter != scope.puzzles.end(); ++iter) {
310 		delete(*iter);
311 	}
312 
313 	scope.puzzles.clear();
314 
315 	for (ControlList::iterator iter = scope.controls.begin(); iter != scope.controls.end(); ++iter) {
316 		delete(*iter);
317 	}
318 
319 	scope.controls.clear();
320 
321 	scope.procCount = 0;
322 }
323 
getStateValue(uint32 key)324 int ScriptManager::getStateValue(uint32 key) {
325 	if (_globalState.contains(key)) {
326 		return _globalState[key];
327 	} else {
328 		return 0;
329 	}
330 }
331 
queuePuzzles(uint32 key)332 void ScriptManager::queuePuzzles(uint32 key) {
333 	if (_referenceTable.contains(key)) {
334 		Common::Array<PuzzleRef> *arr = &_referenceTable[key];
335 		for (int32 i = arr->size() - 1; i >= 0; i--) {
336 			if (!(*arr)[i].puz->addedBySetState) {
337 				(*arr)[i].scope->scopeQueue->push_back((*arr)[i].puz);
338 				(*arr)[i].puz->addedBySetState = true;
339 			}
340 		}
341 	}
342 }
343 
setStateValue(uint32 key,int value)344 void ScriptManager::setStateValue(uint32 key, int value) {
345 	if (value == 0) {
346 		_globalState.erase(key);
347 	} else {
348 		_globalState[key] = value;
349 	}
350 
351 	queuePuzzles(key);
352 }
353 
setStateValueSilent(uint32 key,int value)354 void ScriptManager::setStateValueSilent(uint32 key, int value) {
355 	if (value == 0) {
356 		_globalState.erase(key);
357 	} else {
358 		_globalState[key] = value;
359 	}
360 }
361 
getStateFlag(uint32 key)362 uint ScriptManager::getStateFlag(uint32 key) {
363 	if (_globalStateFlags.contains(key)) {
364 		return _globalStateFlags[key];
365 	} else {
366 		return 0;
367 	}
368 }
369 
setStateFlag(uint32 key,uint value)370 void ScriptManager::setStateFlag(uint32 key, uint value) {
371 	queuePuzzles(key);
372 
373 	_globalStateFlags[key] |= value;
374 }
375 
setStateFlagSilent(uint32 key,uint value)376 void ScriptManager::setStateFlagSilent(uint32 key, uint value) {
377 	if (value == 0) {
378 		_globalStateFlags.erase(key);
379 	} else {
380 		_globalStateFlags[key] = value;
381 	}
382 }
383 
unsetStateFlag(uint32 key,uint value)384 void ScriptManager::unsetStateFlag(uint32 key, uint value) {
385 	queuePuzzles(key);
386 
387 	if (_globalStateFlags.contains(key)) {
388 		_globalStateFlags[key] &= ~value;
389 
390 		if (_globalStateFlags[key] == 0) {
391 			_globalStateFlags.erase(key);
392 		}
393 	}
394 }
395 
getControl(uint32 key)396 Control *ScriptManager::getControl(uint32 key) {
397 	for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) {
398 		if ((*iter)->getKey() == key) {
399 			return *iter;
400 		}
401 	}
402 
403 	return nullptr;
404 }
405 
focusControl(uint32 key)406 void ScriptManager::focusControl(uint32 key) {
407 	if (!_activeControls) {
408 		return;
409 	}
410 	if (_currentlyFocusedControl == key) {
411 		return;
412 	}
413 	for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) {
414 		uint32 controlKey = (*iter)->getKey();
415 
416 		if (controlKey == key) {
417 			(*iter)->focus();
418 		} else if (controlKey == _currentlyFocusedControl) {
419 			(*iter)->unfocus();
420 		}
421 	}
422 
423 	_currentlyFocusedControl = key;
424 }
425 
setFocusControlKey(uint32 key)426 void ScriptManager::setFocusControlKey(uint32 key) {
427 	_currentlyFocusedControl = key;
428 }
429 
addSideFX(ScriptingEffect * fx)430 void ScriptManager::addSideFX(ScriptingEffect *fx) {
431 	_activeSideFx.push_back(fx);
432 }
433 
getSideFX(uint32 key)434 ScriptingEffect *ScriptManager::getSideFX(uint32 key) {
435 	for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) {
436 		if ((*iter)->getKey() == key) {
437 			return (*iter);
438 		}
439 	}
440 
441 	return nullptr;
442 }
443 
deleteSideFx(uint32 key)444 void ScriptManager::deleteSideFx(uint32 key) {
445 	for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) {
446 		if ((*iter)->getKey() == key) {
447 			delete(*iter);
448 			_activeSideFx.erase(iter);
449 			break;
450 		}
451 	}
452 }
453 
stopSideFx(uint32 key)454 void ScriptManager::stopSideFx(uint32 key) {
455 	for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) {
456 		if ((*iter)->getKey() == key) {
457 			bool ret = (*iter)->stop();
458 			if (ret) {
459 				delete(*iter);
460 				_activeSideFx.erase(iter);
461 			}
462 			break;
463 		}
464 	}
465 }
466 
killSideFx(uint32 key)467 void ScriptManager::killSideFx(uint32 key) {
468 	for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) {
469 		if ((*iter)->getKey() == key) {
470 			(*iter)->kill();
471 			delete(*iter);
472 			_activeSideFx.erase(iter);
473 			break;
474 		}
475 	}
476 }
477 
killSideFxType(ScriptingEffect::ScriptingEffectType type)478 void ScriptManager::killSideFxType(ScriptingEffect::ScriptingEffectType type) {
479 	for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end();) {
480 		if ((*iter)->getType() & type) {
481 			(*iter)->kill();
482 			delete(*iter);
483 			iter = _activeSideFx.erase(iter);
484 		} else {
485 			++iter;
486 		}
487 	}
488 }
489 
onMouseDown(const Common::Point & screenSpacePos,const Common::Point & backgroundImageSpacePos)490 void ScriptManager::onMouseDown(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
491 	if (!_activeControls) {
492 		return;
493 	}
494 	for (ControlList::iterator iter = _activeControls->reverse_begin(); iter != _activeControls->end(); iter--) {
495 		if ((*iter)->onMouseDown(screenSpacePos, backgroundImageSpacePos)) {
496 			return;
497 		}
498 	}
499 }
500 
onMouseUp(const Common::Point & screenSpacePos,const Common::Point & backgroundImageSpacePos)501 void ScriptManager::onMouseUp(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
502 	if (!_activeControls) {
503 		return;
504 	}
505 	for (ControlList::iterator iter = _activeControls->reverse_begin(); iter != _activeControls->end(); iter--) {
506 		if ((*iter)->onMouseUp(screenSpacePos, backgroundImageSpacePos)) {
507 			return;
508 		}
509 	}
510 }
511 
onMouseMove(const Common::Point & screenSpacePos,const Common::Point & backgroundImageSpacePos)512 bool ScriptManager::onMouseMove(const Common::Point &screenSpacePos, const Common::Point &backgroundImageSpacePos) {
513 	if (!_activeControls) {
514 		return false;
515 	}
516 
517 	for (ControlList::iterator iter = _activeControls->reverse_begin(); iter != _activeControls->end(); iter--) {
518 		if ((*iter)->onMouseMove(screenSpacePos, backgroundImageSpacePos)) {
519 			return true;
520 		}
521 	}
522 
523 	return false;
524 }
525 
onKeyDown(Common::KeyState keyState)526 void ScriptManager::onKeyDown(Common::KeyState keyState) {
527 	if (!_activeControls) {
528 		return;
529 	}
530 	for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) {
531 		if ((*iter)->onKeyDown(keyState)) {
532 			return;
533 		}
534 	}
535 }
536 
onKeyUp(Common::KeyState keyState)537 void ScriptManager::onKeyUp(Common::KeyState keyState) {
538 	if (!_activeControls) {
539 		return;
540 	}
541 	for (ControlList::iterator iter = _activeControls->begin(); iter != _activeControls->end(); ++iter) {
542 		if ((*iter)->onKeyUp(keyState)) {
543 			return;
544 		}
545 	}
546 }
547 
changeLocation(const Location & _newLocation)548 void ScriptManager::changeLocation(const Location &_newLocation) {
549 	changeLocation(_newLocation.world, _newLocation.room, _newLocation.node, _newLocation.view, _newLocation.offset);
550 }
551 
changeLocation(char _world,char _room,char _node,char _view,uint32 offset)552 void ScriptManager::changeLocation(char _world, char _room, char _node, char _view, uint32 offset) {
553 	_changeLocationDelayCycles = 1;
554 
555 	_nextLocation.world = _world;
556 	_nextLocation.room = _room;
557 	_nextLocation.node = _node;
558 	_nextLocation.view = _view;
559 	_nextLocation.offset = offset;
560 	// If next location is 0000, return to the previous location.
561 	if (_nextLocation == "0000") {
562 		if (getStateValue(StateKey_World) != 'g' || getStateValue(StateKey_Room) != 'j') {
563 			_nextLocation.world = getStateValue(StateKey_LastWorld);
564 			_nextLocation.room = getStateValue(StateKey_LastRoom);
565 			_nextLocation.node = getStateValue(StateKey_LastNode);
566 			_nextLocation.view = getStateValue(StateKey_LastView);
567 			_nextLocation.offset = getStateValue(StateKey_LastViewPos);
568 		} else {
569 			_nextLocation.world = getStateValue(StateKey_Menu_LastWorld);
570 			_nextLocation.room = getStateValue(StateKey_Menu_LastRoom);
571 			_nextLocation.node = getStateValue(StateKey_Menu_LastNode);
572 			_nextLocation.view = getStateValue(StateKey_Menu_LastView);
573 			_nextLocation.offset = getStateValue(StateKey_Menu_LastViewPos);
574 		}
575 	}
576 }
577 
ChangeLocationReal(bool isLoading)578 void ScriptManager::ChangeLocationReal(bool isLoading) {
579 	assert(_nextLocation.world != 0);
580 	debug(1, "Changing location to: %c %c %c %c %u", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view, _nextLocation.offset);
581 
582 	const bool enteringMenu = (_nextLocation.world == 'g' && _nextLocation.room == 'j');
583 	const bool leavingMenu = (_currentLocation.world == 'g' && _currentLocation.room == 'j');
584 	const bool isSaveScreen = (enteringMenu && _nextLocation.node == 's' && _nextLocation.view == 'e');
585 	const bool isRestoreScreen = (enteringMenu && _nextLocation.node == 'r' && _nextLocation.view == 'e');
586 
587 	if (enteringMenu && !ConfMan.getBool("originalsaveload")) {
588 		if (isSaveScreen || isRestoreScreen) {
589 			// Hook up the ScummVM save/restore dialog
590 			bool gameSavedOrLoaded = _engine->getSaveManager()->scummVMSaveLoadDialog(isSaveScreen);
591 			if (!gameSavedOrLoaded || isSaveScreen) {
592 				// Reload the current room
593 				_nextLocation.world = _currentLocation.world;
594 				_nextLocation.room = _currentLocation.room;
595 				_nextLocation.node = _currentLocation.node;
596 				_nextLocation.view = _currentLocation.view;
597 				_nextLocation.offset = _currentLocation.offset;
598 
599 				return;
600 			} else {
601 				_currentLocation.world = 'g';
602 				_currentLocation.room = '0';
603 				_currentLocation.node = '0';
604 				_currentLocation.view = '0';
605 				_currentLocation.offset = 0;
606 			}
607 		}
608 	}
609 
610 	_engine->setRenderDelay(2);
611 
612 	if (!leavingMenu) {
613 		if (!isLoading && !enteringMenu) {
614 			setStateValue(StateKey_LastWorld, getStateValue(StateKey_World));
615 			setStateValue(StateKey_LastRoom, getStateValue(StateKey_Room));
616 			setStateValue(StateKey_LastNode, getStateValue(StateKey_Node));
617 			setStateValue(StateKey_LastView, getStateValue(StateKey_View));
618 			setStateValue(StateKey_LastViewPos, getStateValue(StateKey_ViewPos));
619 		} else {
620 			setStateValue(StateKey_Menu_LastWorld, getStateValue(StateKey_World));
621 			setStateValue(StateKey_Menu_LastRoom, getStateValue(StateKey_Room));
622 			setStateValue(StateKey_Menu_LastNode, getStateValue(StateKey_Node));
623 			setStateValue(StateKey_Menu_LastView, getStateValue(StateKey_View));
624 			setStateValue(StateKey_Menu_LastViewPos, getStateValue(StateKey_ViewPos));
625 		}
626 	}
627 
628 	if (enteringMenu) {
629 		if (isSaveScreen && !leavingMenu) {
630 			_engine->getSaveManager()->prepareSaveBuffer();
631 		}
632 	} else {
633 		if (leavingMenu) {
634 			_engine->getSaveManager()->flushSaveBuffer();
635 		}
636 	}
637 
638 	setStateValue(StateKey_World, _nextLocation.world);
639 	setStateValue(StateKey_Room, _nextLocation.room);
640 	setStateValue(StateKey_Node, _nextLocation.node);
641 	setStateValue(StateKey_View, _nextLocation.view);
642 	setStateValue(StateKey_ViewPos, _nextLocation.offset);
643 
644 	_referenceTable.clear();
645 	addPuzzlesToReferenceTable(universe);
646 
647 	_engine->getMenuHandler()->setEnable(0xFFFF);
648 
649 	if (_nextLocation.world != _currentLocation.world) {
650 		cleanScriptScope(nodeview);
651 		cleanScriptScope(room);
652 		cleanScriptScope(world);
653 
654 		Common::String fileName = Common::String::format("%c%c%c%c.scr", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view);
655 		parseScrFile(fileName, nodeview);
656 		addPuzzlesToReferenceTable(nodeview);
657 
658 		fileName = Common::String::format("%c%c.scr", _nextLocation.world, _nextLocation.room);
659 		parseScrFile(fileName, room);
660 		addPuzzlesToReferenceTable(room);
661 
662 		fileName = Common::String::format("%c.scr", _nextLocation.world);
663 		parseScrFile(fileName, world);
664 		addPuzzlesToReferenceTable(world);
665 	} else if (_nextLocation.room != _currentLocation.room) {
666 		cleanScriptScope(nodeview);
667 		cleanScriptScope(room);
668 
669 		addPuzzlesToReferenceTable(world);
670 
671 		Common::String fileName = Common::String::format("%c%c%c%c.scr", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view);
672 		parseScrFile(fileName, nodeview);
673 		addPuzzlesToReferenceTable(nodeview);
674 
675 		fileName = Common::String::format("%c%c.scr", _nextLocation.world, _nextLocation.room);
676 		parseScrFile(fileName, room);
677 		addPuzzlesToReferenceTable(room);
678 
679 	} else if (_nextLocation.node != _currentLocation.node || _nextLocation.view != _currentLocation.view) {
680 		cleanScriptScope(nodeview);
681 
682 		addPuzzlesToReferenceTable(room);
683 		addPuzzlesToReferenceTable(world);
684 
685 		Common::String fileName = Common::String::format("%c%c%c%c.scr", _nextLocation.world, _nextLocation.room, _nextLocation.node, _nextLocation.view);
686 		parseScrFile(fileName, nodeview);
687 		addPuzzlesToReferenceTable(nodeview);
688 	}
689 
690 	_activeControls = &nodeview.controls;
691 
692 	// Revert to the idle cursor
693 	_engine->getCursorManager()->changeCursor(CursorIndex_Idle);
694 
695 	// Change the background position
696 	_engine->getRenderManager()->setBackgroundPosition(_nextLocation.offset);
697 
698 	if (_currentLocation == "0000") {
699 		_currentLocation = _nextLocation;
700 		execScope(world);
701 		execScope(room);
702 		execScope(nodeview);
703 	} else if (_nextLocation.world != _currentLocation.world) {
704 		_currentLocation = _nextLocation;
705 		execScope(room);
706 		execScope(nodeview);
707 	} else if (_nextLocation.room != _currentLocation.room) {
708 		_currentLocation = _nextLocation;
709 		execScope(room);
710 		execScope(nodeview);
711 	} else if (_nextLocation.node != _currentLocation.node || _nextLocation.view != _currentLocation.view) {
712 		_currentLocation = _nextLocation;
713 		execScope(nodeview);
714 	}
715 
716 	_engine->getRenderManager()->checkBorders();
717 }
718 
serialize(Common::WriteStream * stream)719 void ScriptManager::serialize(Common::WriteStream *stream) {
720 	stream->writeUint32BE(MKTAG('Z', 'N', 'S', 'G'));
721 	stream->writeUint32LE(4);
722 	stream->writeUint32LE(0);
723 	stream->writeUint32BE(MKTAG('L', 'O', 'C', ' '));
724 	stream->writeUint32LE(8);
725 	stream->writeByte(getStateValue(StateKey_World));
726 	stream->writeByte(getStateValue(StateKey_Room));
727 	stream->writeByte(getStateValue(StateKey_Node));
728 	stream->writeByte(getStateValue(StateKey_View));
729 	stream->writeUint32LE(getStateValue(StateKey_ViewPos));
730 
731 	for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); ++iter) {
732 		(*iter)->serialize(stream);
733 	}
734 
735 	stream->writeUint32BE(MKTAG('F', 'L', 'A', 'G'));
736 
737 	int32 slots = 20000;
738 	if (_engine->getGameId() == GID_NEMESIS) {
739 		slots = 30000;
740 	}
741 
742 	stream->writeUint32LE(slots * 2);
743 
744 	for (int32 i = 0; i < slots; i++) {
745 		stream->writeUint16LE(getStateFlag(i));
746 	}
747 
748 	stream->writeUint32BE(MKTAG('P', 'U', 'Z', 'Z'));
749 
750 	stream->writeUint32LE(slots * 2);
751 
752 	for (int32 i = 0; i < slots; i++) {
753 		stream->writeSint16LE(getStateValue(i));
754 	}
755 }
756 
deserialize(Common::SeekableReadStream * stream)757 void ScriptManager::deserialize(Common::SeekableReadStream *stream) {
758 	// Clear out the current table values
759 	_globalState.clear();
760 	_globalStateFlags.clear();
761 
762 	cleanScriptScope(nodeview);
763 	cleanScriptScope(room);
764 	cleanScriptScope(world);
765 
766 	_currentLocation.node = 0;
767 	_currentLocation.world = 0;
768 	_currentLocation.room = 0;
769 	_currentLocation.view = 0;
770 
771 	for (SideFXList::iterator iter = _activeSideFx.begin(); iter != _activeSideFx.end(); iter++) {
772 		delete(*iter);
773 	}
774 
775 	_activeSideFx.clear();
776 
777 	_referenceTable.clear();
778 
779 	if (stream->readUint32BE() != MKTAG('Z', 'N', 'S', 'G') || stream->readUint32LE() != 4) {
780 		changeLocation('g', 'a', 'r', 'y', 0);
781 		return;
782 	}
783 
784 	stream->seek(4, SEEK_CUR);
785 
786 	if (stream->readUint32BE() != MKTAG('L', 'O', 'C', ' ') || stream->readUint32LE() != 8) {
787 		changeLocation('g', 'a', 'r', 'y', 0);
788 		return;
789 	}
790 
791 	Location nextLocation;
792 
793 	nextLocation.world = stream->readByte();
794 	nextLocation.room = stream->readByte();
795 	nextLocation.node = stream->readByte();
796 	nextLocation.view = stream->readByte();
797 	nextLocation.offset = stream->readUint32LE() & 0x0000FFFF;
798 
799 	while (stream->pos() < stream->size()) {
800 		uint32 tag = stream->readUint32BE();
801 		uint32 tagSize = stream->readUint32LE();
802 		switch (tag) {
803 		case MKTAG('T', 'I', 'M', 'R'): {
804 			uint32 key = stream->readUint32LE();
805 			uint32 time = stream->readUint32LE();
806 			if (_engine->getGameId() == GID_GRANDINQUISITOR) {
807 				time /= 100;
808 			} else if (_engine->getGameId() == GID_NEMESIS) {
809 				time /= 1000;
810 			}
811 			addSideFX(new TimerNode(_engine, key, time));
812 		}
813 		break;
814 		case MKTAG('F', 'L', 'A', 'G'):
815 			for (uint32 i = 0; i < tagSize / 2; i++) {
816 				setStateFlagSilent(i, stream->readUint16LE());
817 			}
818 			break;
819 		case MKTAG('P', 'U', 'Z', 'Z'):
820 			for (uint32 i = 0; i < tagSize / 2; i++) {
821 				setStateValueSilent(i, stream->readUint16LE());
822 			}
823 			break;
824 		default:
825 			stream->seek(tagSize, SEEK_CUR);
826 		}
827 	}
828 
829 	_nextLocation = nextLocation;
830 
831 	ChangeLocationReal(true);
832 
833 	_engine->setRenderDelay(10);
834 	setStateValue(StateKey_RestoreFlag, 1);
835 
836 	_engine->loadSettings();
837 }
838 
getCurrentLocation() const839 Location ScriptManager::getCurrentLocation() const {
840 	Location location = _currentLocation;
841 	location.offset = _engine->getRenderManager()->getCurrentBackgroundOffset();
842 
843 	return location;
844 }
845 
getLastLocation()846 Location ScriptManager::getLastLocation() {
847 	Location location;
848 	location.world = getStateValue(StateKey_LastWorld);
849 	location.room = getStateValue(StateKey_LastRoom);
850 	location.node = getStateValue(StateKey_LastNode);
851 	location.view = getStateValue(StateKey_LastView);
852 	location.offset = getStateValue(StateKey_LastViewPos);
853 
854 	return location;
855 }
856 
getLastMenuLocation()857 Location ScriptManager::getLastMenuLocation() {
858 	Location location;
859 	location.world = getStateValue(StateKey_Menu_LastWorld);
860 	location.room = getStateValue(StateKey_Menu_LastRoom);
861 	location.node = getStateValue(StateKey_Menu_LastNode);
862 	location.view = getStateValue(StateKey_Menu_LastView);
863 	location.offset = getStateValue(StateKey_Menu_LastViewPos);
864 
865 	return location;
866 }
867 
addEvent(Common::Event event)868 void ScriptManager::addEvent(Common::Event event) {
869 	_controlEvents.push_back(event);
870 }
871 
flushEvent(Common::EventType type)872 void ScriptManager::flushEvent(Common::EventType type) {
873 	EventList::iterator it = _controlEvents.begin();
874 	while (it != _controlEvents.end()) {
875 
876 		if ((*it).type == type) {
877 			it = _controlEvents.erase(it);
878 		} else {
879 			it++;
880 		}
881 	}
882 }
883 
trimCommentsAndWhiteSpace(Common::String * string) const884 void ScriptManager::trimCommentsAndWhiteSpace(Common::String *string) const {
885 	for (int i = string->size() - 1; i >= 0; i--) {
886 		if ((*string)[i] == '#') {
887 			string->erase(i);
888 		}
889 	}
890 
891 	string->trim();
892 }
893 
ValueSlot(ScriptManager * scriptManager,const char * slotValue)894 ValueSlot::ValueSlot(ScriptManager *scriptManager, const char *slotValue):
895 	_scriptManager(scriptManager) {
896 	value = 0;
897 	slot = false;
898 	const char *isSlot = strstr(slotValue, "[");
899 	if (isSlot) {
900 		slot = true;
901 		value = atoi(isSlot + 1);
902 	} else {
903 		slot = false;
904 		value = atoi(slotValue);
905 	}
906 }
getValue()907 int16 ValueSlot::getValue() {
908 	if (slot) {
909 		if (value >= 0) {
910 			return _scriptManager->getStateValue(value);
911 		}
912 		else {
913 			return 0;
914 		}
915 	} else {
916 		return value;
917 	}
918 }
919 
920 } // End of namespace ZVision
921