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