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/endian.h"
24 #include "common/util.h"
25 #include "common/textconsole.h"
26 #include "common/translation.h"
27 
28 #include "sword1/logic.h"
29 #include "sword1/text.h"
30 #include "sword1/sound.h"
31 #include "sword1/eventman.h"
32 #include "sword1/menu.h"
33 #include "sword1/router.h"
34 #include "sword1/screen.h"
35 #include "sword1/mouse.h"
36 #include "sword1/sword1.h"
37 #include "sword1/music.h"
38 #include "sword1/swordres.h"
39 #include "sword1/animation.h"
40 
41 #include "sword1/debug.h"
42 
43 #include "gui/message.h"
44 
45 namespace Sword1 {
46 
47 #define MAX_STACK_SIZE 10
48 #define SCRIPT_VERSION  13
49 #define LAST_FRAME 999
50 
51 uint32 Logic::_scriptVars[NUM_SCRIPT_VARS];
52 
Logic(SwordEngine * vm,ObjectMan * pObjMan,ResMan * resMan,Screen * pScreen,Mouse * pMouse,Sound * pSound,Music * pMusic,Menu * pMenu,OSystem * system,Audio::Mixer * mixer)53 Logic::Logic(SwordEngine *vm, ObjectMan *pObjMan, ResMan *resMan, Screen *pScreen, Mouse *pMouse, Sound *pSound, Music *pMusic, Menu *pMenu, OSystem *system, Audio::Mixer *mixer)
54 	: _rnd("sword1") {
55 
56 	_vm = vm;
57 	_objMan = pObjMan;
58 	_resMan = resMan;
59 	_screen = pScreen;
60 	_mouse = pMouse;
61 	_music = pMusic;
62 	_sound = pSound;
63 	_menu = pMenu;
64 	_textMan = NULL;
65 	_screen->useTextManager(_textMan);
66 	_router = new Router(_objMan, _resMan);
67 	_eventMan = NULL;
68 	_system = system;
69 	_mixer = mixer;
70 
71 	setupMcodeTable();
72 }
73 
~Logic()74 Logic::~Logic() {
75 	delete _textMan;
76 	delete _router;
77 	delete _eventMan;
78 }
79 
initialize()80 void Logic::initialize() {
81 	memset(_scriptVars, 0, NUM_SCRIPT_VARS * sizeof(uint32));
82 	for (uint8 cnt = 0; cnt < NON_ZERO_SCRIPT_VARS; cnt++)
83 		_scriptVars[_scriptVarInit[cnt][0]] = _scriptVarInit[cnt][1];
84 	if (SwordEngine::_systemVars.isDemo)
85 		_scriptVars[PLAYINGDEMO] = 1;
86 
87 	delete _eventMan;
88 	_eventMan = new EventManager();
89 
90 	delete _textMan;
91 	_textMan = new Text(_objMan, _resMan,
92 	                    (SwordEngine::_systemVars.language == BS1_CZECH) ? true : false);
93 	_screen->useTextManager(_textMan);
94 	_textRunning = _speechRunning = false;
95 	_speechFinished = true;
96 }
97 
newScreen(uint32 screen)98 void Logic::newScreen(uint32 screen) {
99 	Object *compact = (Object *)_objMan->fetchObject(PLAYER);
100 
101 	// work around script bug #911508
102 	if (((screen == 25) || (_scriptVars[SCREEN] == 25)) && (_scriptVars[SAND_FLAG] == 4)) {
103 		Object *cpt = _objMan->fetchObject(SAND_25);
104 		Object *george = _objMan->fetchObject(PLAYER);
105 		if (george->o_place == HOLDING_REPLICA_25) // is george holding the replica in his hands?
106 			fnFullSetFrame(cpt, SAND_25, IMPFLRCDT, IMPFLR, 0, 0, 0, 0); // empty impression in floor
107 		else
108 			fnFullSetFrame(cpt, SAND_25, IMPPLSCDT, IMPPLS, 0, 0, 0, 0); // impression filled with plaster
109 	}
110 
111 	// work around, at screen 69 in psx version TOP menu gets stuck at disabled, fix it at next screen (71)
112 	if ((screen == 71) && (SwordEngine::isPsx()))
113 		_scriptVars[TOP_MENU_DISABLED] = 0;
114 
115 	if (SwordEngine::_systemVars.justRestoredGame) { // if we've just restored a game - we want George to be exactly as saved
116 		fnAddHuman(NULL, 0, 0, 0, 0, 0, 0, 0);
117 		if (_scriptVars[GEORGE_WALKING]) { // except that if George was walking when we saveed the game
118 			fnStandAt(compact, PLAYER, _scriptVars[CHANGE_X], _scriptVars[CHANGE_Y], _scriptVars[CHANGE_DIR], _scriptVars[CHANGE_STANCE], 0, 0);
119 			fnIdle(compact, PLAYER, 0, 0, 0, 0, 0, 0);
120 			_scriptVars[GEORGE_WALKING] = 0;
121 		}
122 		SwordEngine::_systemVars.justRestoredGame = 0;
123 		_music->startMusic(_scriptVars[CURRENT_MUSIC], 1);
124 	} else { // if we haven't just restored a game, set George to stand, etc
125 		compact->o_screen = _scriptVars[NEW_SCREEN]; //move the mega/player at this point between screens
126 		fnStandAt(compact, PLAYER, _scriptVars[CHANGE_X], _scriptVars[CHANGE_Y], _scriptVars[CHANGE_DIR], _scriptVars[CHANGE_STANCE], 0, 0);
127 		fnChangeFloor(compact, PLAYER, _scriptVars[CHANGE_PLACE], 0, 0, 0, 0, 0);
128 	}
129 }
130 
engine()131 void Logic::engine() {
132 	debug(8, "\n\nNext logic cycle");
133 	_eventMan->serviceGlobalEventList();
134 
135 	for (uint16 sectCnt = 0; sectCnt < TOTAL_SECTIONS; sectCnt++) {
136 		if (_objMan->sectionAlive(sectCnt)) {
137 			uint32 numCpts = _objMan->fetchNoObjects(sectCnt);
138 			for (uint32 cptCnt = 0; cptCnt < numCpts; cptCnt++) {
139 				uint32 currentId = sectCnt * ITM_PER_SEC + cptCnt;
140 				Object *compact = _objMan->fetchObject(currentId);
141 
142 				if (compact->o_status & STAT_LOGIC) { // does the object want to be processed?
143 					if (compact->o_status & STAT_EVENTS) {
144 						//subscribed to the global-event-switcher? and in logic mode
145 						switch (compact->o_logic) {
146 						case LOGIC_pause_for_event:
147 						case LOGIC_idle:
148 						case LOGIC_AR_animate:
149 							_eventMan->checkForEvent(compact);
150 							break;
151 						}
152 					}
153 					debug(7, "Logic::engine: handling compact %d (%X)", currentId, currentId);
154 					processLogic(compact, currentId);
155 					compact->o_sync = 0; // syncs are only available for 1 cycle.
156 				}
157 
158 				if ((uint32)compact->o_screen == _scriptVars[SCREEN]) {
159 					if (compact->o_status & STAT_FORE)
160 						_screen->addToGraphicList(0, currentId);
161 					if (compact->o_status & STAT_SORT)
162 						_screen->addToGraphicList(1, currentId);
163 					if (compact->o_status & STAT_BACK)
164 						_screen->addToGraphicList(2, currentId);
165 
166 					if (compact->o_status & STAT_MOUSE)
167 						_mouse->addToList(currentId, compact);
168 				}
169 			}
170 		}
171 	}
172 	//_collision->checkCollisions();
173 
174 }
175 
processLogic(Object * compact,uint32 id)176 void Logic::processLogic(Object *compact, uint32 id) {
177 	int logicRet;
178 	do {
179 		switch (compact->o_logic) {
180 		case LOGIC_idle:
181 			logicRet = 0;
182 			break;
183 		case LOGIC_pause:
184 		case LOGIC_pause_for_event:
185 			if (compact->o_pause) {
186 				compact->o_pause--;
187 				logicRet = 0;
188 			} else {
189 				compact->o_logic = LOGIC_script;
190 				logicRet = 1;
191 			}
192 			break;
193 		case LOGIC_quit:
194 			compact->o_logic = LOGIC_script;
195 			logicRet = 0;
196 			break;
197 		case LOGIC_wait_for_sync:
198 			if (compact->o_sync) {
199 				logicRet = 1;
200 				compact->o_logic = LOGIC_script;
201 			} else
202 				logicRet = 0;
203 			break;
204 		case LOGIC_choose:
205 			_scriptVars[CUR_ID] = id;
206 			logicRet = _menu->logicChooser(compact);
207 			break;
208 		case LOGIC_wait_for_talk:
209 			logicRet = logicWaitTalk(compact);
210 			break;
211 		case LOGIC_start_talk:
212 			logicRet = logicStartTalk(compact);
213 			break;
214 		case LOGIC_script:
215 			_scriptVars[CUR_ID] = id;
216 			logicRet = scriptManager(compact, id);
217 			break;
218 		case LOGIC_new_script:
219 			compact->o_tree.o_script_pc[compact->o_tree.o_script_level] = _newScript;
220 			compact->o_tree.o_script_id[compact->o_tree.o_script_level] = _newScript;
221 			compact->o_logic = LOGIC_script;
222 			logicRet = 1;
223 			break;
224 		case LOGIC_AR_animate:
225 			logicRet = logicArAnimate(compact, id);
226 			break;
227 		case LOGIC_restart:
228 			compact->o_tree.o_script_pc[compact->o_tree.o_script_level] = compact->o_tree.o_script_id[compact->o_tree.o_script_level];
229 			compact->o_logic = LOGIC_script;
230 			logicRet = 1;
231 			break;
232 		case LOGIC_bookmark:
233 			memcpy(&(compact->o_tree.o_script_level), &(compact->o_bookmark.o_script_level), sizeof(ScriptTree));
234 			if (id == GMASTER_79) {
235 				// workaround for ending script.
236 				// GMASTER_79 is not prepared for mega_interact receiving INS_quit
237 				fnSuicide(compact, id, 0, 0, 0, 0, 0, 0);
238 				logicRet = 0;
239 			} else {
240 				compact->o_logic = LOGIC_script;
241 				logicRet = 1;
242 			}
243 			break;
244 		case LOGIC_speech:
245 			logicRet = speechDriver(compact);
246 			break;
247 		case LOGIC_full_anim:
248 			logicRet = fullAnimDriver(compact);
249 			break;
250 		case LOGIC_anim:
251 			logicRet = animDriver(compact);
252 			break;
253 
254 		default:
255 			error("Fatal error: compact %d's logic == %X", id, compact->o_logic);
256 			break;
257 		}
258 	} while (logicRet);
259 }
260 
logicWaitTalk(Object * compact)261 int Logic::logicWaitTalk(Object *compact) {
262 	Object *target = _objMan->fetchObject(compact->o_down_flag);
263 
264 	if (target->o_status & STAT_TALK_WAIT) {
265 		compact->o_logic = LOGIC_script;
266 		return 1;
267 	} else {
268 		return 0;
269 	}
270 }
271 
logicStartTalk(Object * compact)272 int Logic::logicStartTalk(Object *compact) {
273 	Object *target = _objMan->fetchObject(compact->o_down_flag); //holds id of person we're waiting for
274 	if (target->o_status & STAT_TALK_WAIT) { //response?
275 		compact->o_logic = LOGIC_script; //back to script again
276 		return SCRIPT_CONT;
277 	}
278 	if (_eventMan->eventValid(compact->o_down_flag))
279 		return SCRIPT_STOP; //event still valid - keep waiting
280 	//the event has gone - so back to script with error code
281 	compact->o_down_flag = 0;
282 	compact->o_logic = LOGIC_script;
283 	return SCRIPT_CONT;
284 }
285 
logicArAnimate(Object * compact,uint32 id)286 int Logic::logicArAnimate(Object *compact, uint32 id) {
287 	WalkData *route;
288 	int32 walkPc;
289 	if ((_scriptVars[GEORGE_WALKING] == 0) && (id == PLAYER))
290 		_scriptVars[GEORGE_WALKING] = 1;
291 
292 	compact->o_resource = compact->o_walk_resource;
293 	compact->o_status |= STAT_SHRINK;
294 	route = compact->o_route;
295 
296 	walkPc            = compact->o_walk_pc;
297 	compact->o_frame  = route[walkPc].frame;
298 	compact->o_dir    = route[walkPc].dir;
299 	compact->o_xcoord = route[walkPc].x;
300 	compact->o_ycoord = route[walkPc].y;
301 	compact->o_anim_x = compact->o_xcoord;
302 	compact->o_anim_y = compact->o_ycoord;
303 
304 	if (((_scriptVars[GEORGE_WALKING] == 2) && (walkPc > 5) && (id == PLAYER) &&
305 	        (route[walkPc - 1].step == 5) && (route[walkPc].step == 0)) ||
306 	        ((_scriptVars[GEORGE_WALKING] == 3) && (id == PLAYER))) {
307 
308 		compact->o_frame = 96 + compact->o_dir;                     //reset
309 		if ((compact->o_dir != 2) && (compact->o_dir != 6)) {  // on verticals and diagonals stand where george is
310 			compact->o_xcoord = route[walkPc - 1].x;
311 			compact->o_ycoord = route[walkPc - 1].y;
312 			compact->o_anim_x = compact->o_xcoord;
313 			compact->o_anim_y = compact->o_ycoord;
314 		}
315 		compact->o_logic = LOGIC_script;
316 		compact->o_down_flag = 0;       //0 means error
317 		_scriptVars[GEORGE_WALKING] = 0;
318 		route[compact->o_walk_pc + 1].frame = 512;                  //end of sequence
319 		if (_scriptVars[MEGA_ON_GRID] == 2)
320 			_scriptVars[MEGA_ON_GRID] = 0;
321 	}
322 	compact->o_walk_pc++;
323 
324 	if (route[compact->o_walk_pc].frame == 512) {                   //end of sequence
325 		compact->o_logic = LOGIC_script;
326 		if (((_scriptVars[GEORGE_WALKING] == 2) || (_scriptVars[GEORGE_WALKING] == 1)) &&
327 		        (id == PLAYER)) {
328 			_scriptVars[GEORGE_WALKING] = 0;
329 			if (_scriptVars[MEGA_ON_GRID] == 2)
330 				_scriptVars[MEGA_ON_GRID] = 0;
331 		}
332 	}
333 	return 0;
334 }
335 
speechDriver(Object * compact)336 int Logic::speechDriver(Object *compact) {
337 	if ((!_speechClickDelay) && (_mouse->testEvent() & BS1L_BUTTON_DOWN))
338 		_speechFinished = true;
339 	if (_speechClickDelay)
340 		_speechClickDelay--;
341 
342 	if (_speechRunning) {
343 		if (_sound->speechFinished())
344 			_speechFinished = true;
345 	} else {
346 		if (!compact->o_speech_time)
347 			_speechFinished = true;
348 		else
349 			compact->o_speech_time--;
350 	}
351 	if (_speechFinished) {
352 		if (_speechRunning)
353 			_sound->stopSpeech();
354 		compact->o_logic = LOGIC_script;
355 		if (_textRunning) {
356 			_textMan->releaseText(compact->o_text_id);
357 			_objMan->fetchObject(compact->o_text_id)->o_status = 0; // kill compact linking text sprite
358 		}
359 		_speechRunning = _textRunning = false;
360 		_speechFinished = true;
361 	}
362 	if (compact->o_anim_resource) {
363 		uint8 *animData = ((uint8 *)_resMan->openFetchRes(compact->o_anim_resource)) + sizeof(Header);
364 		int32 numFrames = _resMan->readUint32(animData);
365 		animData += 4;
366 		compact->o_anim_pc++; // go to next frame of anim
367 
368 		if (_speechFinished || (compact->o_anim_pc >= numFrames) ||
369 		        (_speechRunning && (_sound->amISpeaking() == 0)))
370 			compact->o_anim_pc = 0; //set to frame 0, closed mouth
371 
372 		AnimUnit *animPtr = (AnimUnit *)(animData + sizeof(AnimUnit) * compact->o_anim_pc);
373 		if (!(compact->o_status & STAT_SHRINK)) {
374 			compact->o_anim_x = _resMan->getUint32(animPtr->animX);
375 			compact->o_anim_y = _resMan->getUint32(animPtr->animY);
376 		}
377 		compact->o_frame = _resMan->getUint32(animPtr->animFrame);
378 		_resMan->resClose(compact->o_anim_resource);
379 	}
380 	return 0;
381 }
382 
fullAnimDriver(Object * compact)383 int Logic::fullAnimDriver(Object *compact) {
384 	if (compact->o_sync) { // return to script immediately if we've received a sync
385 		compact->o_logic = LOGIC_script;
386 		return 1;
387 	}
388 	uint8 *data = ((uint8 *)_resMan->openFetchRes(compact->o_anim_resource)) + sizeof(Header);
389 	uint32 numFrames = _resMan->readUint32(data);
390 	data += 4;
391 	AnimUnit *animPtr = (AnimUnit *)(data + compact->o_anim_pc * sizeof(AnimUnit));
392 
393 	compact->o_anim_x = compact->o_xcoord = _resMan->getUint32(animPtr->animX);
394 	compact->o_anim_y = compact->o_ycoord = _resMan->getUint32(animPtr->animY);
395 	compact->o_frame = _resMan->getUint32(animPtr->animFrame);
396 
397 	compact->o_anim_pc++;
398 	if (compact->o_anim_pc == (int)numFrames)
399 		compact->o_logic = LOGIC_script;
400 
401 	_resMan->resClose(compact->o_anim_resource);
402 	return 0;
403 }
404 
animDriver(Object * compact)405 int Logic::animDriver(Object *compact) {
406 	if (compact->o_sync) {
407 		compact->o_logic = LOGIC_script;
408 		return 1;
409 	}
410 	uint8 *data = ((uint8 *)_resMan->openFetchRes(compact->o_anim_resource)) + sizeof(Header);
411 	uint32 numFrames = _resMan->readUint32(data);
412 	AnimUnit *animPtr = (AnimUnit *)(data + 4 + compact->o_anim_pc * sizeof(AnimUnit));
413 
414 	if (!(compact->o_status & STAT_SHRINK)) {
415 		compact->o_anim_x = _resMan->getUint32(animPtr->animX);
416 		compact->o_anim_y = _resMan->getUint32(animPtr->animY);
417 	}
418 
419 	compact->o_frame = _resMan->getUint32(animPtr->animFrame);
420 	compact->o_anim_pc++;
421 	if (compact->o_anim_pc == (int)numFrames)
422 		compact->o_logic = LOGIC_script;
423 
424 	_resMan->resClose(compact->o_anim_resource);
425 	return 0;
426 }
427 
updateScreenParams()428 void Logic::updateScreenParams() {
429 	Object *compact = (Object *)_objMan->fetchObject(PLAYER);
430 	_screen->setScrolling((int16)(compact->o_xcoord - _scriptVars[FEET_X]),
431 	                      (int16)(compact->o_ycoord - _scriptVars[FEET_Y]));
432 }
433 
scriptManager(Object * compact,uint32 id)434 int Logic::scriptManager(Object *compact, uint32 id) {
435 	int ret;
436 	do {
437 		uint32 level = compact->o_tree.o_script_level;
438 		uint32 script = compact->o_tree.o_script_id[level];
439 		Debug::interpretScript(id, level, script, compact->o_tree.o_script_pc[level] & ITM_ID);
440 		ret = interpretScript(compact, id, _resMan->lockScript(script), script, compact->o_tree.o_script_pc[level] & ITM_ID);
441 		_resMan->unlockScript(script);
442 		if (!ret) {
443 			if (compact->o_tree.o_script_level)
444 				compact->o_tree.o_script_level--;
445 			else
446 				error("ScriptManager: basescript %d for cpt %d ended", script, id);
447 		} else
448 			compact->o_tree.o_script_pc[level] = ret;
449 	} while (!ret);
450 	return 1;
451 	//Logic continues - but the script must have changed logic mode
452 	//this is a radical change from S2.0 where once a script finished there
453 	//was no more processing for that object on that cycle - the Logic_engine terminated.
454 	//This meant that new logics that needed immediate action got a first call from the
455 	//setup function. This was a bit tweeky. This technique ensures that the script is a
456 	//totally seamless concept that takes up zero cycle time. The only downside is that
457 	//an FN_quit becomes slightly more convoluted, but so what you might ask.
458 }
459 
runMouseScript(Object * cpt,int32 scriptId)460 void Logic::runMouseScript(Object *cpt, int32 scriptId) {
461 	Header *script = _resMan->lockScript(scriptId);
462 	debug(9, "running mouse script %d", scriptId);
463 	interpretScript(cpt, _scriptVars[SPECIAL_ITEM], script, scriptId, scriptId);
464 	_resMan->unlockScript(scriptId);
465 }
466 
interpretScript(Object * compact,int id,Header * scriptModule,int scriptBase,int scriptNum)467 int Logic::interpretScript(Object *compact, int id, Header *scriptModule, int scriptBase, int scriptNum) {
468 	int32 *scriptCode = (int32 *)(((uint8 *)scriptModule) + sizeof(Header));
469 	int32 stack[MAX_STACK_SIZE];
470 	int32 stackIdx = 0;
471 	int32 offset;
472 	int32 pc;
473 	if (memcmp(scriptModule->type, "Script", 6))
474 		error("Invalid script module");
475 	if (scriptModule->version != SCRIPT_VERSION)
476 		error("Illegal script version");
477 	if (scriptNum < 0)
478 		error("negative script number");
479 	if ((uint32)scriptNum >= scriptModule->decomp_length)
480 		error("Script number out of bounds");
481 
482 	if (scriptNum < scriptCode[0])
483 		pc = scriptCode[scriptNum + 1];
484 	else
485 		pc = scriptNum;
486 	int32 startOfScript = scriptCode[(scriptBase & ITM_ID) + 1];
487 
488 	int32 a, b, c, d, e, f;
489 	int mCodeReturn = 0;
490 	int32 mCodeNumber = 0, mCodeArguments = 0;
491 	uint32 varNum = 0;
492 	while (1) {
493 		assert((stackIdx >= 0) && (stackIdx <= MAX_STACK_SIZE));
494 		switch (scriptCode[pc++]) {
495 		case IT_MCODE:
496 			a = b = c = d = e = f = 0;
497 			mCodeNumber = scriptCode[pc++];
498 			mCodeArguments = scriptCode[pc++];
499 			switch (mCodeArguments) {
500 			case 6: f = stack[--stackIdx]; // fall through
501 			case 5: e = stack[--stackIdx]; // fall through
502 			case 4: d = stack[--stackIdx]; // fall through
503 			case 3: c = stack[--stackIdx]; // fall through
504 			case 2: b = stack[--stackIdx]; // fall through
505 			case 1: a = stack[--stackIdx]; // fall through
506 			case 0:
507 				Debug::callMCode(mCodeNumber, mCodeArguments, a, b, c, d, e, f);
508 				mCodeReturn = (this->*_mcodeTable[mCodeNumber])(compact, id, a, b, c, d, e, f);
509 				break;
510 			default:
511 				warning("mcode[%d]: too many arguments(%d)", mCodeNumber, mCodeArguments);
512 			}
513 			if (mCodeReturn == 0)
514 				return pc;
515 			break;
516 		case IT_PUSHNUMBER:
517 			debug(9, "IT_PUSH: %d", scriptCode[pc]);
518 			stack[stackIdx++] = scriptCode[pc++];
519 			break;
520 		case IT_PUSHVARIABLE:
521 			debug(9, "IT_PUSHVARIABLE: ScriptVar[%d] => %d", scriptCode[pc], _scriptVars[scriptCode[pc]]);
522 			varNum = scriptCode[pc++];
523 			if (SwordEngine::_systemVars.isDemo && SwordEngine::isWindows()) {
524 				if (varNum >= 397) // BS1 Demo has different number of script variables
525 					varNum++;
526 				if (varNum >= 699)
527 					varNum++;
528 			}
529 			stack[stackIdx++] = _scriptVars[varNum];
530 			break;
531 		case IT_NOTEQUAL:
532 			stackIdx--;
533 			debug(9, "IT_NOTEQUAL: RESULT = %d", stack[stackIdx - 1] != stack[stackIdx]);
534 			stack[stackIdx - 1] = (stack[stackIdx - 1] != stack[stackIdx]);
535 			break;
536 		case IT_ISEQUAL:
537 			stackIdx--;
538 			debug(9, "IT_ISEQUAL: RESULT = %d", stack[stackIdx - 1] == stack[stackIdx]);
539 			stack[stackIdx - 1] = (stack[stackIdx - 1] == stack[stackIdx]);
540 			break;
541 		case IT_PLUS:
542 			stackIdx--;
543 			debug(9, "IT_PLUS: RESULT = %d", stack[stackIdx - 1] + stack[stackIdx]);
544 			stack[stackIdx - 1] = (stack[stackIdx - 1] + stack[stackIdx]);
545 			break;
546 		case IT_TIMES:
547 			stackIdx--;
548 			debug(9, "IT_TIMES: RESULT = %d", stack[stackIdx - 1] * stack[stackIdx]);
549 			stack[stackIdx - 1] = (stack[stackIdx - 1] * stack[stackIdx]);
550 			break;
551 		case IT_ANDAND:
552 			stackIdx--;
553 			debug(9, "IT_ANDAND: RESULT = %d", stack[stackIdx - 1] && stack[stackIdx]);
554 			stack[stackIdx - 1] = (stack[stackIdx - 1] && stack[stackIdx]);
555 			break;
556 		case IT_OROR:           // ||
557 			stackIdx--;
558 			debug(9, "IT_OROR: RESULT = %d", stack[stackIdx - 1] || stack[stackIdx]);
559 			stack[stackIdx - 1] = (stack[stackIdx - 1] || stack[stackIdx]);
560 			break;
561 		case IT_LESSTHAN:
562 			stackIdx--;
563 			debug(9, "IT_LESSTHAN: RESULT = %d", stack[stackIdx - 1] < stack[stackIdx]);
564 			stack[stackIdx - 1] = (stack[stackIdx - 1] < stack[stackIdx]);
565 			break;
566 		case IT_NOT:
567 			debug(9, "IT_NOT: RESULT = %d", stack[stackIdx - 1] ? 0 : 1);
568 			if (stack[stackIdx - 1])
569 				stack[stackIdx - 1] = 0;
570 			else
571 				stack[stackIdx - 1] = 1;
572 			break;
573 		case IT_MINUS:
574 			stackIdx--;
575 			debug(9, "IT_MINUS: RESULT = %d", stack[stackIdx - 1] - stack[stackIdx]);
576 			stack[stackIdx - 1] = (stack[stackIdx - 1] - stack[stackIdx]);
577 			break;
578 		case IT_AND:
579 			stackIdx--;
580 			debug(9, "IT_AND: RESULT = %d", stack[stackIdx - 1] & stack[stackIdx]);
581 			stack[stackIdx - 1] = (stack[stackIdx - 1] & stack[stackIdx]);
582 			break;
583 		case IT_OR:
584 			stackIdx--;
585 			debug(9, "IT_OR: RESULT = %d", stack[stackIdx - 1] | stack[stackIdx]);
586 			stack[stackIdx - 1] = (stack[stackIdx - 1] | stack[stackIdx]);
587 			break;
588 		case IT_GTE:
589 			stackIdx--;
590 			debug(9, "IT_GTE: RESULT = %d", stack[stackIdx - 1] >= stack[stackIdx]);
591 			stack[stackIdx - 1] = (stack[stackIdx - 1] >= stack[stackIdx]);
592 			break;
593 		case IT_LTE:
594 			stackIdx--;
595 			debug(9, "IT_LTE: RESULT = %d", stack[stackIdx - 1] <= stack[stackIdx]);
596 			stack[stackIdx - 1] = (stack[stackIdx - 1] <= stack[stackIdx]);
597 			break;
598 		case IT_DEVIDE:
599 			stackIdx--;
600 			debug(9, "IT_DEVIDE: RESULT = %d", stack[stackIdx - 1] / stack[stackIdx]);
601 			stack[stackIdx - 1] = (stack[stackIdx - 1] / stack[stackIdx]);
602 			break;
603 		case IT_GT:
604 			stackIdx--;
605 			debug(9, "IT_GT: RESULT = %d", stack[stackIdx - 1] > stack[stackIdx]);
606 			stack[stackIdx - 1] = (stack[stackIdx - 1] > stack[stackIdx]);
607 			break;
608 		case IT_SCRIPTEND:
609 			debug(9, "IT_SCRIPTEND");
610 			return 0;
611 		case IT_POPVAR:         // pop a variable
612 			debug(9, "IT_POPVAR: ScriptVars[%d] = %d", scriptCode[pc], stack[stackIdx - 1]);
613 			varNum = scriptCode[pc++];
614 			if (SwordEngine::_systemVars.isDemo && SwordEngine::isWindows()) {
615 				if (varNum >= 397) // BS1 Demo has different number of script variables
616 					varNum++;
617 				if (varNum >= 699)
618 					varNum++;
619 			}
620 			_scriptVars[varNum] = stack[--stackIdx];
621 			break;
622 		case IT_POPLONGOFFSET:
623 			offset = scriptCode[pc++];
624 			debug(9, "IT_POPLONGOFFSET: Cpt[%d] = %d", offset, stack[stackIdx - 1]);
625 			*((int32 *)((uint8 *)compact + offset)) = stack[--stackIdx];
626 			break;
627 		case IT_PUSHLONGOFFSET:
628 			offset = scriptCode[pc++];
629 			debug(9, "IT_PUSHLONGOFFSET: PUSH Cpt[%d] (==%d)", offset, *((int32 *)((uint8 *)compact + offset)));
630 			stack[stackIdx++] = *((int32 *)((uint8 *)compact + offset));
631 			break;
632 		case IT_SKIPONFALSE:
633 			debug(9, "IT_SKIPONFALSE: %d (%s)", scriptCode[pc], (stack[stackIdx - 1] ? "IS TRUE (NOT SKIPPED)" : "IS FALSE (SKIPPED)"));
634 			if (stack[--stackIdx])
635 				pc++;
636 			else
637 				pc += scriptCode[pc];
638 			break;
639 		case IT_SKIP:
640 			debug(9, "IT_SKIP: %d", scriptCode[pc]);
641 			pc += scriptCode[pc];
642 			break;
643 		case IT_SWITCH:         // The mega switch statement
644 			debug(9, "IT_SWITCH: [SORRY, NO DEBUG INFO]");
645 			{
646 				int switchValue = stack[--stackIdx];
647 				int switchCount = scriptCode[pc++];
648 				int doneSwitch = 0;
649 
650 				for (int cnt = 0; (cnt < switchCount) && (doneSwitch == 0); cnt++) {
651 					if (switchValue == scriptCode[pc]) {
652 						pc += scriptCode[pc + 1];
653 						doneSwitch = 1;
654 					} else
655 						pc += 2;
656 				}
657 				if (doneSwitch == 0)
658 					pc += scriptCode[pc];
659 			}
660 			break;
661 		case IT_SKIPONTRUE:     // skip if expression true
662 			debug(9, "IT_SKIPONTRUE: %d (%s)", scriptCode[pc], (stack[stackIdx - 1] ? "IS TRUE (SKIPPED)" : "IS FALSE (NOT SKIPPED)"));
663 			stackIdx--;
664 			if (stack[stackIdx])
665 				pc += scriptCode[pc];
666 			else
667 				pc++;
668 			break;
669 		case IT_PRINTF:
670 			debug(0, "IT_PRINTF(%d)", stack[stackIdx]);
671 			break;
672 		case IT_RESTARTSCRIPT:
673 			debug(9, "IT_RESTARTSCRIPT");
674 			pc = startOfScript;
675 			break;
676 		case IT_POPWORDOFFSET:
677 			offset = scriptCode[pc++];
678 			debug(9, "IT_POPWORDOFFSET: Cpt[%d] = %d", offset, stack[stackIdx - 1] & 0xFFFF);
679 			*((int32 *)((uint8 *)compact + offset)) = stack[--stackIdx] & 0xffff;
680 			break;
681 		case IT_PUSHWORDOFFSET:
682 			offset = scriptCode[pc++];
683 			debug(9, "IT_PUSHWORDOFFSET: PUSH Cpt[%d] == %d", offset, (*((int32 *)((uint8 *)compact + offset))) & 0xffff);
684 			stack[stackIdx++] = (*((int32 *)((uint8 *)compact + offset))) & 0xffff;
685 			break;
686 		default:
687 			error("Invalid operator %d", scriptCode[pc - 1]);
688 			return 0;   // for compilers that don't support NORETURN
689 		}
690 	}
691 }
692 
setupMcodeTable()693 void Logic::setupMcodeTable() {
694 	static const BSMcodeTable mcodeTable[100] = {
695 		&Logic::fnBackground,
696 		&Logic::fnForeground,
697 		&Logic::fnSort,
698 		&Logic::fnNoSprite,
699 		&Logic::fnMegaSet,
700 		&Logic::fnAnim,
701 		&Logic::fnSetFrame,
702 		&Logic::fnFullAnim,
703 		&Logic::fnFullSetFrame,
704 		&Logic::fnFadeDown,
705 		&Logic::fnFadeUp,
706 		&Logic::fnCheckFade,
707 		&Logic::fnSetSpritePalette,
708 		&Logic::fnSetWholePalette,
709 		&Logic::fnSetFadeTargetPalette,
710 		&Logic::fnSetPaletteToFade,
711 		&Logic::fnSetPaletteToCut,
712 		&Logic::fnPlaySequence,
713 		&Logic::fnIdle,
714 		&Logic::fnPause,
715 		&Logic::fnPauseSeconds,
716 		&Logic::fnQuit,
717 		&Logic::fnKillId,
718 		&Logic::fnSuicide,
719 		&Logic::fnNewScript,
720 		&Logic::fnSubScript,
721 		&Logic::fnRestartScript,
722 		&Logic::fnSetBookmark,
723 		&Logic::fnGotoBookmark,
724 		&Logic::fnSendSync,
725 		&Logic::fnWaitSync,
726 		&Logic::cfnClickInteract,
727 		&Logic::cfnSetScript,
728 		&Logic::cfnPresetScript,
729 		&Logic::fnInteract,
730 		&Logic::fnIssueEvent,
731 		&Logic::fnCheckForEvent,
732 		&Logic::fnWipeHands,
733 		&Logic::fnISpeak,
734 		&Logic::fnTheyDo,
735 		&Logic::fnTheyDoWeWait,
736 		&Logic::fnWeWait,
737 		&Logic::fnChangeSpeechText,
738 		&Logic::fnTalkError,
739 		&Logic::fnStartTalk,
740 		&Logic::fnCheckForTextLine,
741 		&Logic::fnAddTalkWaitStatusBit,
742 		&Logic::fnRemoveTalkWaitStatusBit,
743 		&Logic::fnNoHuman,
744 		&Logic::fnAddHuman,
745 		&Logic::fnBlankMouse,
746 		&Logic::fnNormalMouse,
747 		&Logic::fnLockMouse,
748 		&Logic::fnUnlockMouse,
749 		&Logic::fnSetMousePointer,
750 		&Logic::fnSetMouseLuggage,
751 		&Logic::fnMouseOn,
752 		&Logic::fnMouseOff,
753 		&Logic::fnChooser,
754 		&Logic::fnEndChooser,
755 		&Logic::fnStartMenu,
756 		&Logic::fnEndMenu,
757 		&Logic::cfnReleaseMenu,
758 		&Logic::fnAddSubject,
759 		&Logic::fnAddObject,
760 		&Logic::fnRemoveObject,
761 		&Logic::fnEnterSection,
762 		&Logic::fnLeaveSection,
763 		&Logic::fnChangeFloor,
764 		&Logic::fnWalk,
765 		&Logic::fnTurn,
766 		&Logic::fnStand,
767 		&Logic::fnStandAt,
768 		&Logic::fnFace,
769 		&Logic::fnFaceXy,
770 		&Logic::fnIsFacing,
771 		&Logic::fnGetTo,
772 		&Logic::fnGetToError,
773 		&Logic::fnGetPos,
774 		&Logic::fnGetGamepadXy,
775 		&Logic::fnPlayFx,
776 		&Logic::fnStopFx,
777 		&Logic::fnPlayMusic,
778 		&Logic::fnStopMusic,
779 		&Logic::fnInnerSpace,
780 		&Logic::fnRandom,
781 		&Logic::fnSetScreen,
782 		&Logic::fnPreload,
783 		&Logic::fnCheckCD,
784 		&Logic::fnRestartGame,
785 		&Logic::fnQuitGame,
786 		&Logic::fnDeathScreen,
787 		&Logic::fnSetParallax,
788 		&Logic::fnTdebug,
789 		&Logic::fnRedFlash,
790 		&Logic::fnBlueFlash,
791 		&Logic::fnYellow,
792 		&Logic::fnGreen,
793 		&Logic::fnPurple,
794 		&Logic::fnBlack
795 	};
796 
797 	_mcodeTable = mcodeTable;
798 }
799 
fnBackground(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)800 int Logic::fnBackground(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
801 
802 	cpt->o_status &= ~(STAT_FORE | STAT_SORT);
803 	cpt->o_status |= STAT_BACK;
804 	return SCRIPT_CONT;
805 }
806 
fnForeground(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)807 int Logic::fnForeground(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
808 	cpt->o_status &= ~(STAT_BACK | STAT_SORT);
809 	cpt->o_status |= STAT_FORE;
810 	return SCRIPT_CONT;
811 }
812 
fnSort(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)813 int Logic::fnSort(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
814 	cpt->o_status &= ~(STAT_BACK | STAT_FORE);
815 	cpt->o_status |= STAT_SORT;
816 	return SCRIPT_CONT;
817 }
818 
fnNoSprite(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)819 int Logic::fnNoSprite(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
820 	cpt->o_status &= ~(STAT_BACK | STAT_FORE | STAT_SORT);
821 	return SCRIPT_CONT;
822 }
823 
fnMegaSet(Object * cpt,int32 id,int32 walk_data,int32 spr,int32 e,int32 f,int32 z,int32 x)824 int Logic::fnMegaSet(Object *cpt, int32 id, int32 walk_data, int32 spr, int32 e, int32 f, int32 z, int32 x) {
825 	cpt->o_mega_resource = walk_data;
826 	cpt->o_walk_resource = spr;
827 	return SCRIPT_CONT;
828 }
829 
fnAnim(Object * cpt,int32 id,int32 cdt,int32 spr,int32 e,int32 f,int32 z,int32 x)830 int Logic::fnAnim(Object *cpt, int32 id, int32 cdt, int32 spr, int32 e, int32 f, int32 z, int32 x) {
831 	AnimSet *animTab;
832 
833 	if (cdt && (!spr)) {
834 		animTab = (AnimSet *)((uint8 *)_resMan->openFetchRes(cdt) + sizeof(Header));
835 		animTab += cpt->o_dir;
836 
837 		cpt->o_anim_resource = _resMan->getUint32(animTab->cdt);
838 		cpt->o_resource = _resMan->getUint32(animTab->spr);
839 		_resMan->resClose(cdt);
840 	} else {
841 		cpt->o_anim_resource = cdt;
842 		cpt->o_resource = spr;
843 	}
844 	if ((cpt->o_anim_resource == 0) || (cpt->o_resource == 0))
845 		error("fnAnim called width (%d/%d) => (%d/%d)", cdt, spr, cpt->o_anim_resource, cpt->o_resource);
846 
847 	FrameHeader *frameHead = _resMan->fetchFrame(_resMan->openFetchRes(cpt->o_resource), 0);
848 	if (frameHead->offsetX || frameHead->offsetY) { // boxed mega anim?
849 		cpt->o_status |= STAT_SHRINK;
850 		cpt->o_anim_x = cpt->o_xcoord; // set anim coords to 'feet' coords - only need to do this once
851 		cpt->o_anim_y = cpt->o_ycoord;
852 	} else {
853 		// Anim_driver sets anim coords to cdt coords for every frame of a loose anim
854 		cpt->o_status &= ~STAT_SHRINK;
855 	}
856 	_resMan->resClose(cpt->o_resource);
857 
858 	cpt->o_logic = LOGIC_anim;
859 	cpt->o_anim_pc = 0;
860 	cpt->o_sync = 0;
861 	return SCRIPT_STOP;
862 }
863 
fnSetFrame(Object * cpt,int32 id,int32 cdt,int32 spr,int32 frameNo,int32 f,int32 z,int32 x)864 int Logic::fnSetFrame(Object *cpt, int32 id, int32 cdt, int32 spr, int32 frameNo, int32 f, int32 z, int32 x) {
865 
866 	AnimUnit   *animPtr;
867 
868 	uint8 *data = (uint8 *)_resMan->openFetchRes(cdt);
869 	data += sizeof(Header);
870 	if (frameNo == LAST_FRAME)
871 		frameNo = _resMan->readUint32(data) - 1;
872 
873 	data += 4;
874 	animPtr = (AnimUnit *)(data + frameNo * sizeof(AnimUnit));
875 
876 	cpt->o_anim_x = _resMan->getUint32(animPtr->animX);
877 	cpt->o_anim_y = _resMan->getUint32(animPtr->animY);
878 	cpt->o_frame = _resMan->getUint32(animPtr->animFrame);
879 
880 	cpt->o_resource = spr;
881 	cpt->o_status &= ~STAT_SHRINK;
882 	_resMan->resClose(cdt);
883 	return SCRIPT_CONT;
884 }
885 
fnFullAnim(Object * cpt,int32 id,int32 anim,int32 graphic,int32 e,int32 f,int32 z,int32 x)886 int Logic::fnFullAnim(Object *cpt, int32 id, int32 anim, int32 graphic, int32 e, int32 f, int32 z, int32 x) {
887 	cpt->o_logic = LOGIC_full_anim;
888 
889 	cpt->o_anim_pc = 0;
890 	cpt->o_anim_resource = anim;
891 	cpt->o_resource = graphic;
892 	cpt->o_status &= ~STAT_SHRINK;
893 	cpt->o_sync = 0;
894 	return SCRIPT_STOP;
895 }
896 
fnFullSetFrame(Object * cpt,int32 id,int32 cdt,int32 spr,int32 frameNo,int32 f,int32 z,int32 x)897 int Logic::fnFullSetFrame(Object *cpt, int32 id, int32 cdt, int32 spr, int32 frameNo, int32 f, int32 z, int32 x) {
898 	uint8 *data = (uint8 *)_resMan->openFetchRes(cdt) + sizeof(Header);
899 
900 	if (frameNo == LAST_FRAME)
901 		frameNo = _resMan->readUint32(data) - 1;
902 	data += 4;
903 
904 	AnimUnit *animPtr = (AnimUnit *)(data + sizeof(AnimUnit) * frameNo);
905 	cpt->o_anim_x = cpt->o_xcoord = _resMan->getUint32(animPtr->animX);
906 	cpt->o_anim_y = cpt->o_ycoord = _resMan->getUint32(animPtr->animY);
907 	cpt->o_frame = _resMan->getUint32(animPtr->animFrame);
908 
909 	cpt->o_resource = spr;
910 	cpt->o_status &= ~STAT_SHRINK;
911 
912 	_resMan->resClose(cdt);
913 	return SCRIPT_CONT;
914 }
915 
fnFadeDown(Object * cpt,int32 id,int32 speed,int32 d,int32 e,int32 f,int32 z,int32 x)916 int Logic::fnFadeDown(Object *cpt, int32 id, int32 speed, int32 d, int32 e, int32 f, int32 z, int32 x) {
917 	_screen->fadeDownPalette();
918 	return SCRIPT_CONT;
919 }
920 
fnFadeUp(Object * cpt,int32 id,int32 speed,int32 d,int32 e,int32 f,int32 z,int32 x)921 int Logic::fnFadeUp(Object *cpt, int32 id, int32 speed, int32 d, int32 e, int32 f, int32 z, int32 x) {
922 	_screen->fadeUpPalette();
923 	return SCRIPT_CONT;
924 }
925 
fnCheckFade(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)926 int Logic::fnCheckFade(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
927 	_scriptVars[RETURN_VALUE] = (uint8)_screen->stillFading();
928 	return SCRIPT_CONT;
929 }
930 
fnSetSpritePalette(Object * cpt,int32 id,int32 spritePal,int32 d,int32 e,int32 f,int32 z,int32 x)931 int Logic::fnSetSpritePalette(Object *cpt, int32 id, int32 spritePal, int32 d, int32 e, int32 f, int32 z, int32 x) {
932 	_screen->fnSetPalette(184, 72, spritePal, false);
933 	return SCRIPT_CONT;
934 }
935 
fnSetWholePalette(Object * cpt,int32 id,int32 spritePal,int32 d,int32 e,int32 f,int32 z,int32 x)936 int Logic::fnSetWholePalette(Object *cpt, int32 id, int32 spritePal, int32 d, int32 e, int32 f, int32 z, int32 x) {
937 	_screen->fnSetPalette(0, 256, spritePal, false);
938 	return SCRIPT_CONT;
939 }
940 
fnSetFadeTargetPalette(Object * cpt,int32 id,int32 spritePal,int32 d,int32 e,int32 f,int32 z,int32 x)941 int Logic::fnSetFadeTargetPalette(Object *cpt, int32 id, int32 spritePal, int32 d, int32 e, int32 f, int32 z, int32 x) {
942 	_screen->fnSetPalette(0, 184, spritePal, true);
943 	return SCRIPT_CONT;
944 }
945 
fnSetPaletteToFade(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)946 int Logic::fnSetPaletteToFade(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
947 	SwordEngine::_systemVars.wantFade = true;
948 	return SCRIPT_CONT;
949 }
950 
fnSetPaletteToCut(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)951 int Logic::fnSetPaletteToCut(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
952 	SwordEngine::_systemVars.wantFade = false;
953 	return SCRIPT_CONT;
954 }
955 
fnPlaySequence(Object * cpt,int32 id,int32 sequenceId,int32 d,int32 e,int32 f,int32 z,int32 x)956 int Logic::fnPlaySequence(Object *cpt, int32 id, int32 sequenceId, int32 d, int32 e, int32 f, int32 z, int32 x) {
957 
958 	// A cutscene usually (always?) means the room will change. In the
959 	// meantime, we don't want any looping sound effects still playing.
960 	_sound->quitScreen();
961 
962 	MoviePlayer *player = makeMoviePlayer(sequenceId, _vm, _textMan, _resMan, _system);
963 	if (player) {
964 		_screen->clearScreen();
965 		if (player->load(sequenceId))
966 			player->play();
967 		delete player;
968 	}
969 	return SCRIPT_CONT;
970 }
971 
fnIdle(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)972 int Logic::fnIdle(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
973 	cpt->o_tree.o_script_level = 0; // force to level 0
974 	cpt->o_logic = LOGIC_idle;
975 	return SCRIPT_STOP;
976 }
977 
fnPause(Object * cpt,int32 id,int32 pause,int32 d,int32 e,int32 f,int32 z,int32 x)978 int Logic::fnPause(Object *cpt, int32 id, int32 pause, int32 d, int32 e, int32 f, int32 z, int32 x) {
979 	cpt->o_pause = pause;
980 	cpt->o_logic = LOGIC_pause;
981 	return SCRIPT_STOP;
982 }
983 
fnPauseSeconds(Object * cpt,int32 id,int32 pause,int32 d,int32 e,int32 f,int32 z,int32 x)984 int Logic::fnPauseSeconds(Object *cpt, int32 id, int32 pause, int32 d, int32 e, int32 f, int32 z, int32 x) {
985 	cpt->o_pause = pause * FRAME_RATE;
986 	cpt->o_logic = LOGIC_pause;
987 	return SCRIPT_STOP;
988 }
989 
fnQuit(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)990 int Logic::fnQuit(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
991 	cpt->o_logic = LOGIC_quit;
992 	return SCRIPT_STOP;
993 }
994 
fnKillId(Object * cpt,int32 id,int32 target,int32 d,int32 e,int32 f,int32 z,int32 x)995 int Logic::fnKillId(Object *cpt, int32 id, int32 target, int32 d, int32 e, int32 f, int32 z, int32 x) {
996 	Object *targetObj = _objMan->fetchObject(target);
997 	targetObj->o_status = 0;
998 	return SCRIPT_CONT;
999 }
1000 
fnSuicide(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1001 int Logic::fnSuicide(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1002 	cpt->o_status = 0;
1003 	cpt->o_logic = LOGIC_quit;
1004 	return SCRIPT_STOP;
1005 }
1006 
fnNewScript(Object * cpt,int32 id,int32 script,int32 d,int32 e,int32 f,int32 z,int32 x)1007 int Logic::fnNewScript(Object *cpt, int32 id, int32 script, int32 d, int32 e, int32 f, int32 z, int32 x) {
1008 	cpt->o_logic = LOGIC_new_script;
1009 	_newScript = script;
1010 	return SCRIPT_STOP;
1011 }
1012 
fnSubScript(Object * cpt,int32 id,int32 script,int32 d,int32 e,int32 f,int32 z,int32 x)1013 int Logic::fnSubScript(Object *cpt, int32 id, int32 script, int32 d, int32 e, int32 f, int32 z, int32 x) {
1014 	cpt->o_tree.o_script_level++;
1015 	if (cpt->o_tree.o_script_level == TOTAL_script_levels)
1016 		error("Compact %d: script level exceeded in fnSubScript", id);
1017 	cpt->o_tree.o_script_pc[cpt->o_tree.o_script_level] = script;
1018 	cpt->o_tree.o_script_id[cpt->o_tree.o_script_level] = script;
1019 	return SCRIPT_STOP;
1020 }
1021 
fnRestartScript(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1022 int Logic::fnRestartScript(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1023 	cpt->o_logic = LOGIC_restart;
1024 	return SCRIPT_STOP;
1025 }
1026 
fnSetBookmark(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1027 int Logic::fnSetBookmark(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1028 	memcpy(&cpt->o_bookmark.o_script_level, &cpt->o_tree.o_script_level, sizeof(ScriptTree));
1029 	return SCRIPT_CONT;
1030 }
1031 
fnGotoBookmark(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1032 int Logic::fnGotoBookmark(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1033 	cpt->o_logic = LOGIC_bookmark;
1034 	return SCRIPT_STOP;
1035 }
1036 
fnSendSync(Object * cpt,int32 id,int32 sendId,int32 syncValue,int32 e,int32 f,int32 z,int32 x)1037 int Logic::fnSendSync(Object *cpt, int32 id, int32 sendId, int32 syncValue, int32 e, int32 f, int32 z, int32 x) {
1038 	Object *target = _objMan->fetchObject(sendId);
1039 	target->o_sync = syncValue;
1040 	return SCRIPT_CONT;
1041 }
1042 
fnWaitSync(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1043 int Logic::fnWaitSync(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1044 	cpt->o_logic = LOGIC_wait_for_sync;
1045 	return SCRIPT_STOP;
1046 }
1047 
cfnClickInteract(Object * cpt,int32 id,int32 target,int32 d,int32 e,int32 f,int32 z,int32 x)1048 int Logic::cfnClickInteract(Object *cpt, int32 id, int32 target, int32 d, int32 e, int32 f, int32 z, int32 x) {
1049 	Object *tar = _objMan->fetchObject(target);
1050 	cpt = _objMan->fetchObject(PLAYER);
1051 	cpt->o_tree.o_script_level = 0;
1052 	cpt->o_tree.o_script_pc[0] = tar->o_interact;
1053 	cpt->o_tree.o_script_id[0] = tar->o_interact;
1054 	cpt->o_logic = LOGIC_script;
1055 	return SCRIPT_STOP;
1056 }
1057 
cfnSetScript(Object * cpt,int32 id,int32 target,int32 script,int32 e,int32 f,int32 z,int32 x)1058 int Logic::cfnSetScript(Object *cpt, int32 id, int32 target, int32 script, int32 e, int32 f, int32 z, int32 x) {
1059 	Object *tar = _objMan->fetchObject(target);
1060 	tar->o_tree.o_script_level = 0;
1061 	tar->o_tree.o_script_pc[0] = script;
1062 	tar->o_tree.o_script_id[0] = script;
1063 	tar->o_logic = LOGIC_script;
1064 	return SCRIPT_CONT;
1065 }
1066 
cfnPresetScript(Object * cpt,int32 id,int32 target,int32 script,int32 e,int32 f,int32 z,int32 x)1067 int Logic::cfnPresetScript(Object *cpt, int32 id, int32 target, int32 script, int32 e, int32 f, int32 z, int32 x) {
1068 	Object *tar = _objMan->fetchObject(target);
1069 	tar->o_tree.o_script_level = 0;
1070 	tar->o_tree.o_script_pc[0] = script;
1071 	tar->o_tree.o_script_id[0] = script;
1072 	if (tar->o_logic == LOGIC_idle)
1073 		tar->o_logic = LOGIC_script;
1074 	return SCRIPT_CONT;
1075 }
1076 
fnInteract(Object * cpt,int32 id,int32 target,int32 d,int32 e,int32 f,int32 z,int32 x)1077 int Logic::fnInteract(Object *cpt, int32 id, int32 target, int32 d, int32 e, int32 f, int32 z, int32 x) {
1078 	Object *tar = _objMan->fetchObject(target);
1079 	cpt->o_place = tar->o_place;
1080 
1081 	Object *floorObject = _objMan->fetchObject(tar->o_place);
1082 	cpt->o_scale_a = floorObject->o_scale_a;
1083 	cpt->o_scale_b = floorObject->o_scale_b;
1084 
1085 	cpt->o_tree.o_script_level++;
1086 	cpt->o_tree.o_script_pc[cpt->o_tree.o_script_level] = tar->o_interact;
1087 	cpt->o_tree.o_script_id[cpt->o_tree.o_script_level] = tar->o_interact;
1088 
1089 	return SCRIPT_STOP;
1090 }
1091 
fnIssueEvent(Object * cpt,int32 id,int32 event,int32 delay,int32 e,int32 f,int32 z,int32 x)1092 int Logic::fnIssueEvent(Object *cpt, int32 id, int32 event, int32 delay, int32 e, int32 f, int32 z, int32 x) {
1093 	_eventMan->fnIssueEvent(cpt, id, event, delay);
1094 	return SCRIPT_CONT;
1095 }
1096 
fnCheckForEvent(Object * cpt,int32 id,int32 pause,int32 d,int32 e,int32 f,int32 z,int32 x)1097 int Logic::fnCheckForEvent(Object *cpt, int32 id, int32 pause, int32 d, int32 e, int32 f, int32 z, int32 x) {
1098 	return _eventMan->fnCheckForEvent(cpt, id, pause);
1099 }
1100 
fnWipeHands(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1101 int Logic::fnWipeHands(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1102 	_scriptVars[OBJECT_HELD] = 0;
1103 	_mouse->setLuggage(0, 0);
1104 	_menu->refresh(MENU_TOP);
1105 	return SCRIPT_CONT;
1106 }
1107 
fnISpeak(Object * cpt,int32 id,int32 cdt,int32 textNo,int32 spr,int32 f,int32 z,int32 x)1108 int Logic::fnISpeak(Object *cpt, int32 id, int32 cdt, int32 textNo, int32 spr, int32 f, int32 z, int32 x) {
1109 	_speechClickDelay = 3;
1110 	if (((textNo & ~1) == 0x3f0012) && (!cdt) && (!spr)) {
1111 		cdt = GEOSTDLCDT; // workaround for missing animation when examining
1112 		spr = GEOSTDL;    // the conductor on the train roof
1113 	}
1114 	cpt->o_logic = LOGIC_speech;
1115 
1116 	// first setup the talk animation
1117 	if (cdt && (!spr)) { // if 'cdt' is non-zero but 'spr' is zero - 'cdt' is an anim table tag
1118 		AnimSet *animTab = (AnimSet *)((uint8 *)_resMan->openFetchRes(cdt) + sizeof(Header));
1119 		animTab += cpt->o_dir;
1120 
1121 		cpt->o_anim_resource = _resMan->getUint32(animTab->cdt);
1122 		if (animTab->cdt)
1123 			cpt->o_resource = _resMan->getUint32(animTab->spr);
1124 		_resMan->resClose(cdt);
1125 	} else {
1126 		cpt->o_anim_resource = cdt;
1127 		if (cdt)
1128 			cpt->o_resource = spr;
1129 	}
1130 	cpt->o_anim_pc = 0; // start anim from first frame
1131 	if (cpt->o_anim_resource) {
1132 		if (!cpt->o_resource)
1133 			error("ID %d: Can't run anim with cdt=%d, spr=%d", id, cdt, spr);
1134 
1135 		FrameHeader *frameHead = _resMan->fetchFrame(_resMan->openFetchRes(cpt->o_resource), 0);
1136 		if (frameHead->offsetX && frameHead->offsetY) { // is this a boxed mega?
1137 			cpt->o_status |= STAT_SHRINK;
1138 			cpt->o_anim_x = cpt->o_xcoord;
1139 			cpt->o_anim_y = cpt->o_ycoord;
1140 		} else
1141 			cpt->o_status &= ~STAT_SHRINK;
1142 
1143 		_resMan->resClose(cpt->o_resource);
1144 	}
1145 	if (SwordEngine::_systemVars.playSpeech)
1146 		_speechRunning = _sound->startSpeech(textNo >> 16, textNo & 0xFFFF);
1147 	else
1148 		_speechRunning = false;
1149 	_speechFinished = false;
1150 	if (SwordEngine::_systemVars.showText || (!_speechRunning)) {
1151 		_textRunning = true;
1152 
1153 		char *text = _objMan->lockText(textNo);
1154 		cpt->o_speech_time = strlen(text) + 5;
1155 		uint32 textCptId = _textMan->lowTextManager((uint8 *)text, cpt->o_speech_width, (uint8)cpt->o_speech_pen);
1156 		_objMan->unlockText(textNo);
1157 
1158 		Object *textCpt = _objMan->fetchObject(textCptId);
1159 		textCpt->o_screen = cpt->o_screen;
1160 		textCpt->o_target = textCptId;
1161 
1162 		// the graphic is a property of Text, so we don't lock/unlock it.
1163 		uint16 textSpriteWidth  = _resMan->getUint16(_textMan->giveSpriteData(textCpt->o_target)->width);
1164 		uint16 textSpriteHeight = _resMan->getUint16(_textMan->giveSpriteData(textCpt->o_target)->height);
1165 
1166 		cpt->o_text_id = textCptId;
1167 
1168 		// now set text coords, above the player, usually
1169 
1170 #define TEXT_MARGIN 3 // distance kept from edges of screen
1171 #define ABOVE_HEAD 20 // distance kept above talking sprite
1172 		uint16 textX, textY;
1173 		if (((id == GEORGE) || ((id == NICO) && (_scriptVars[SCREEN] == 10))) && (!cpt->o_anim_resource)) {
1174 			// if George is doing Voice-Over text (centered at the bottom of the screen)
1175 			textX = _scriptVars[SCROLL_OFFSET_X] + 128 + (640 / 2) - textSpriteWidth / 2;
1176 			textY = _scriptVars[SCROLL_OFFSET_Y] + 128 + 400;
1177 		} else {
1178 			if ((id == GEORGE) && (_scriptVars[SCREEN] == 79))
1179 				textX = cpt->o_mouse_x2; // move it off george's head
1180 			else
1181 				textX = (cpt->o_mouse_x1 + cpt->o_mouse_x2) / 2 - textSpriteWidth / 2;
1182 
1183 			textY = cpt->o_mouse_y1 - textSpriteHeight - ABOVE_HEAD;
1184 		}
1185 		// now ensure text is within visible screen
1186 		uint16 textLeftMargin, textRightMargin, textTopMargin, textBottomMargin;
1187 		textLeftMargin   = SCREEN_LEFT_EDGE   + TEXT_MARGIN + _scriptVars[SCROLL_OFFSET_X];
1188 		textRightMargin  = SCREEN_RIGHT_EDGE  - TEXT_MARGIN + _scriptVars[SCROLL_OFFSET_X] - textSpriteWidth;
1189 		textTopMargin    = SCREEN_TOP_EDGE    + TEXT_MARGIN + _scriptVars[SCROLL_OFFSET_Y];
1190 		textBottomMargin = SCREEN_BOTTOM_EDGE - TEXT_MARGIN + _scriptVars[SCROLL_OFFSET_Y] - textSpriteHeight;
1191 
1192 		textCpt->o_anim_x = textCpt->o_xcoord = CLIP<uint16>(textX, textLeftMargin, textRightMargin);
1193 		textCpt->o_anim_y = textCpt->o_ycoord = CLIP<uint16>(textY, textTopMargin, textBottomMargin);
1194 	}
1195 	return SCRIPT_STOP;
1196 }
1197 
1198 //send instructions to mega in conversation with player
1199 //the instruction is interpreted by the script mega_interact
fnTheyDo(Object * cpt,int32 id,int32 tar,int32 instruc,int32 param1,int32 param2,int32 param3,int32 x)1200 int Logic::fnTheyDo(Object *cpt, int32 id, int32 tar, int32 instruc, int32 param1, int32 param2, int32 param3, int32 x) {
1201 	Object *target;
1202 	target = _objMan->fetchObject(tar);
1203 	target->o_down_flag = instruc; // instruction for the mega
1204 	target->o_ins1 = param1;
1205 	target->o_ins2 = param2;
1206 	target->o_ins3 = param3;
1207 	return SCRIPT_CONT;
1208 }
1209 
1210 //send an instruction to mega we're talking to and wait
1211 //until it has finished before returning to script
fnTheyDoWeWait(Object * cpt,int32 id,int32 tar,int32 instruc,int32 param1,int32 param2,int32 param3,int32 x)1212 int Logic::fnTheyDoWeWait(Object *cpt, int32 id, int32 tar, int32 instruc, int32 param1, int32 param2, int32 param3, int32 x) {
1213 	// workaround for scriptbug #928791: Freeze at hospital
1214 	// in at least one game version, a script forgets to set sam_returning back to zero
1215 	if ((tar == SAM) && (instruc == INS_talk) && (param2 == 2162856))
1216 		_scriptVars[SAM_RETURNING] = 0;
1217 	Object *target = _objMan->fetchObject(tar);
1218 	target->o_down_flag = instruc; // instruction for the mega
1219 	target->o_ins1 = param1;
1220 	target->o_ins2 = param2;
1221 	target->o_ins3 = param3;
1222 	target->o_status &= ~STAT_TALK_WAIT;
1223 
1224 	cpt->o_logic = LOGIC_wait_for_talk;
1225 	cpt->o_down_flag = tar;
1226 	return SCRIPT_STOP;
1227 }
1228 
fnWeWait(Object * cpt,int32 id,int32 tar,int32 d,int32 e,int32 f,int32 z,int32 x)1229 int Logic::fnWeWait(Object *cpt, int32 id, int32 tar, int32 d, int32 e, int32 f, int32 z, int32 x) {
1230 	Object *target = _objMan->fetchObject(tar);
1231 	target->o_status &= ~STAT_TALK_WAIT;
1232 
1233 	cpt->o_logic = LOGIC_wait_for_talk;
1234 	cpt->o_down_flag = tar;
1235 
1236 	return SCRIPT_STOP;
1237 }
1238 
fnChangeSpeechText(Object * cpt,int32 id,int32 tar,int32 width,int32 pen,int32 f,int32 z,int32 x)1239 int Logic::fnChangeSpeechText(Object *cpt, int32 id, int32 tar, int32 width, int32 pen, int32 f, int32 z, int32 x) {
1240 	Object *target = _objMan->fetchObject(tar);
1241 	target->o_speech_width = width;
1242 	target->o_speech_pen = pen;
1243 	return SCRIPT_STOP;
1244 }
1245 
1246 //mega_interact has received an instruction it does not understand -
1247 //The game is halted for debugging. Maybe we'll remove this later.
fnTalkError(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1248 int Logic::fnTalkError(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1249 	error("fnTalkError for id %d, instruction %d", id, cpt->o_down_flag);
1250 	return SCRIPT_STOP; // for compilers that don't support NORETURN
1251 }
1252 
fnStartTalk(Object * cpt,int32 id,int32 target,int32 d,int32 e,int32 f,int32 z,int32 x)1253 int Logic::fnStartTalk(Object *cpt, int32 id, int32 target, int32 d, int32 e, int32 f, int32 z, int32 x) {
1254 	cpt->o_down_flag = target;
1255 	cpt->o_logic = LOGIC_start_talk;
1256 	return SCRIPT_STOP;
1257 }
1258 
fnCheckForTextLine(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1259 int Logic::fnCheckForTextLine(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1260 	_scriptVars[RETURN_VALUE] = _objMan->fnCheckForTextLine(id);
1261 	return SCRIPT_CONT;
1262 }
1263 
fnAddTalkWaitStatusBit(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1264 int Logic::fnAddTalkWaitStatusBit(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1265 	cpt->o_status |= STAT_TALK_WAIT;
1266 	return SCRIPT_CONT;
1267 }
1268 
fnRemoveTalkWaitStatusBit(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1269 int Logic::fnRemoveTalkWaitStatusBit(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1270 	cpt->o_status &= ~STAT_TALK_WAIT;
1271 	return SCRIPT_CONT;
1272 }
1273 
fnNoHuman(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1274 int Logic::fnNoHuman(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1275 	_mouse->fnNoHuman();
1276 	return SCRIPT_CONT;
1277 }
1278 
fnAddHuman(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1279 int Logic::fnAddHuman(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1280 	_mouse->fnAddHuman();
1281 	return SCRIPT_CONT;
1282 }
1283 
fnBlankMouse(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1284 int Logic::fnBlankMouse(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1285 	_mouse->fnBlankMouse();
1286 	return SCRIPT_CONT;
1287 }
1288 
fnNormalMouse(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1289 int Logic::fnNormalMouse(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1290 	_mouse->fnNormalMouse();
1291 	return SCRIPT_CONT;
1292 }
1293 
fnLockMouse(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1294 int Logic::fnLockMouse(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1295 	_mouse->fnLockMouse();
1296 	return SCRIPT_CONT;
1297 }
1298 
fnUnlockMouse(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1299 int Logic::fnUnlockMouse(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1300 	_mouse->fnUnlockMouse();
1301 	return SCRIPT_CONT;
1302 }
1303 
fnSetMousePointer(Object * cpt,int32 id,int32 tag,int32 rate,int32 e,int32 f,int32 z,int32 x)1304 int Logic::fnSetMousePointer(Object *cpt, int32 id, int32 tag, int32 rate, int32 e, int32 f, int32 z, int32 x) {
1305 	_mouse->setPointer(tag, rate);
1306 	return SCRIPT_CONT;
1307 }
1308 
fnSetMouseLuggage(Object * cpt,int32 id,int32 tag,int32 rate,int32 e,int32 f,int32 z,int32 x)1309 int Logic::fnSetMouseLuggage(Object *cpt, int32 id, int32 tag, int32 rate, int32 e, int32 f, int32 z, int32 x) {
1310 	_mouse->setLuggage(tag, rate);
1311 	return SCRIPT_CONT;
1312 }
1313 
fnMouseOn(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1314 int Logic::fnMouseOn(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1315 	cpt->o_status |= STAT_MOUSE;
1316 	return SCRIPT_CONT;
1317 }
1318 
fnMouseOff(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1319 int Logic::fnMouseOff(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1320 	cpt->o_status &= ~STAT_MOUSE;
1321 	return SCRIPT_CONT;
1322 }
1323 
fnChooser(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1324 int Logic::fnChooser(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1325 	_menu->fnChooser(cpt);
1326 	return SCRIPT_STOP;
1327 }
1328 
fnEndChooser(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1329 int Logic::fnEndChooser(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1330 	_menu->fnEndChooser();
1331 	return SCRIPT_CONT;
1332 }
1333 
fnStartMenu(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1334 int Logic::fnStartMenu(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1335 	_menu->fnStartMenu();
1336 	return SCRIPT_CONT;
1337 }
1338 
fnEndMenu(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1339 int Logic::fnEndMenu(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1340 	_menu->fnEndMenu();
1341 	return SCRIPT_CONT;
1342 }
1343 
cfnReleaseMenu(Object * cpt,int32 id,int32 c,int32 d,int32 e,int32 f,int32 z,int32 x)1344 int Logic::cfnReleaseMenu(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
1345 	_menu->cfnReleaseMenu();
1346 	return SCRIPT_STOP;
1347 }
1348 
fnAddSubject(Object * cpt,int32 id,int32 sub,int32 d,int32 e,int32 f,int32 z,int32 x)1349 int Logic::fnAddSubject(Object *cpt, int32 id, int32 sub, int32 d, int32 e, int32 f, int32 z, int32 x) {
1350 	_menu->fnAddSubject(sub);
1351 	return SCRIPT_CONT;
1352 }
1353 
fnAddObject(Object * cpt,int32 id,int32 objectNo,int32 d,int32 e,int32 f,int32 z,int32 x)1354 int Logic::fnAddObject(Object *cpt, int32 id, int32 objectNo, int32 d, int32 e, int32 f, int32 z, int32 x) {
1355 	_scriptVars[POCKET_1 + objectNo - 1] = 1; // basically means: carrying object objectNo = true;
1356 	return SCRIPT_CONT;
1357 }
1358 
fnRemoveObject(Object * cpt,int32 id,int32 objectNo,int32 d,int32 e,int32 f,int32 z,int32 x)1359 int Logic::fnRemoveObject(Object *cpt, int32 id, int32 objectNo, int32 d, int32 e, int32 f, int32 z, int32 x) {
1360 	_scriptVars[POCKET_1 + objectNo - 1] = 0;
1361 	return SCRIPT_CONT;
1362 }
1363 
fnEnterSection(Object * cpt,int32 id,int32 screen,int32 d,int32 e,int32 f,int32 z,int32 x)1364 int Logic::fnEnterSection(Object *cpt, int32 id, int32 screen, int32 d, int32 e, int32 f, int32 z, int32 x) {
1365 	if (screen >= TOTAL_SECTIONS)
1366 		error("mega %d tried entering section %d", id, screen);
1367 
1368 	/* if (cpt->o_type == TYPE_PLAYER)
1369 	   ^= this was the original condition from the game sourcecode.
1370 	   not sure why it doesn't work*/
1371 	if (id == PLAYER)
1372 		_scriptVars[NEW_SCREEN] = screen;
1373 	else
1374 		cpt->o_screen = screen; // move the mega
1375 	_objMan->megaEntering(screen);
1376 	return SCRIPT_CONT;
1377 }
1378 
fnLeaveSection(Object * cpt,int32 id,int32 oldScreen,int32 d,int32 e,int32 f,int32 z,int32 x)1379 int Logic::fnLeaveSection(Object *cpt, int32 id, int32 oldScreen, int32 d, int32 e, int32 f, int32 z, int32 x) {
1380 	if (oldScreen >= TOTAL_SECTIONS)
1381 		error("mega %d leaving section %d", id, oldScreen);
1382 	_objMan->megaLeaving(oldScreen, id);
1383 	return SCRIPT_CONT;
1384 }
1385 
fnChangeFloor(Object * cpt,int32 id,int32 floor,int32 d,int32 e,int32 f,int32 z,int32 x)1386 int Logic::fnChangeFloor(Object *cpt, int32 id, int32 floor, int32 d, int32 e, int32 f, int32 z, int32 x) {
1387 	cpt->o_place = floor;
1388 	Object *floorCpt = _objMan->fetchObject(floor);
1389 	cpt->o_scale_a = floorCpt->o_scale_a;
1390 	cpt->o_scale_b = floorCpt->o_scale_b;
1391 	return SCRIPT_CONT;
1392 }
1393 
fnWalk(Object * cpt,int32 id,int32 x,int32 y,int32 dir,int32 stance,int32 a,int32 b)1394 int Logic::fnWalk(Object *cpt, int32 id, int32 x, int32 y, int32 dir, int32 stance, int32 a, int32 b) {
1395 	if (stance > 0)
1396 		dir = 9;
1397 	cpt->o_walk_pc = 0;
1398 	cpt->o_route[1].frame = 512; // end of sequence
1399 	if (id == PLAYER)
1400 		_router->setPlayerTarget(x, y, dir, stance);
1401 
1402 	int32 routeRes = _router->routeFinder(id, cpt, x, y, dir);
1403 
1404 	if (id == PLAYER) {
1405 		if ((routeRes == 1) || (routeRes == 2)) {
1406 			_scriptVars[MEGA_ON_GRID] = 0;
1407 			_scriptVars[REROUTE_GEORGE] = 0;
1408 		}
1409 	}
1410 	if ((routeRes == 1) || (routeRes == 2)) {
1411 		cpt->o_down_flag = 1; // 1 means okay.
1412 		// if both mouse buttons were pressed on an exit => skip george's walk
1413 		if ((id == GEORGE) && (_mouse->testEvent() == MOUSE_BOTH_BUTTONS)) {
1414 			int32 target = _scriptVars[CLICK_ID];
1415 			// exceptions: compacts that use hand pointers but are not actually exits
1416 			if ((target != LEFT_SCROLL_POINTER) && (target != RIGHT_SCROLL_POINTER) &&
1417 			        (target != FLOOR_63) && (target != ROOF_63) && (target != GUARD_ROOF_63) &&
1418 			        (target != LEFT_TREE_POINTER_71) && (target != RIGHT_TREE_POINTER_71)) {
1419 
1420 				target = _objMan->fetchObject(_scriptVars[CLICK_ID])->o_mouse_on;
1421 				if ((target >= SCR_exit0) && (target <= SCR_exit9)) {
1422 					fnStandAt(cpt, id, x, y, dir, stance, 0, 0);
1423 					return SCRIPT_STOP;
1424 				}
1425 			}
1426 		}
1427 		cpt->o_logic = LOGIC_AR_animate;
1428 		return SCRIPT_STOP;
1429 	} else if (routeRes == 3)
1430 		cpt->o_down_flag = 1; // pretend it was successful
1431 	else
1432 		cpt->o_down_flag = 0; // 0 means error
1433 
1434 	return SCRIPT_CONT;
1435 }
1436 
fnTurn(Object * cpt,int32 id,int32 dir,int32 stance,int32 c,int32 d,int32 a,int32 b)1437 int Logic::fnTurn(Object *cpt, int32 id, int32 dir, int32 stance, int32 c, int32 d, int32 a, int32 b) {
1438 	if (stance > 0)
1439 		dir = 9;
1440 	int route = _router->routeFinder(id, cpt, cpt->o_xcoord, cpt->o_ycoord, dir);
1441 
1442 	if (route)
1443 		cpt->o_down_flag = 1;       //1 means ok
1444 	else
1445 		cpt->o_down_flag = 0;       //0 means error
1446 
1447 	cpt->o_logic = LOGIC_AR_animate;
1448 	cpt->o_walk_pc = 0;                     //reset
1449 
1450 	return SCRIPT_STOP;
1451 }
1452 
fnStand(Object * cpt,int32 id,int32 dir,int32 stance,int32 c,int32 d,int32 a,int32 b)1453 int Logic::fnStand(Object *cpt, int32 id, int32 dir, int32 stance, int32 c, int32 d, int32 a, int32 b) {
1454 	if ((dir < 0) || (dir > 8)) {
1455 		warning("fnStand:: invalid direction %d", dir);
1456 		return SCRIPT_CONT;
1457 	}
1458 	if (dir == 8)
1459 		dir = cpt->o_dir;
1460 	cpt->o_resource = cpt->o_walk_resource;
1461 	cpt->o_status |= STAT_SHRINK;
1462 	cpt->o_anim_x = cpt->o_xcoord;
1463 	cpt->o_anim_y = cpt->o_ycoord;
1464 	cpt->o_frame = 96 + dir;
1465 	cpt->o_dir = dir;
1466 	return SCRIPT_STOP;
1467 }
1468 
fnStandAt(Object * cpt,int32 id,int32 x,int32 y,int32 dir,int32 stance,int32 a,int32 b)1469 int Logic::fnStandAt(Object *cpt, int32 id, int32 x, int32 y, int32 dir, int32 stance, int32 a, int32 b) {
1470 	if ((dir < 0) || (dir > 8)) {
1471 		warning("fnStandAt:: invalid direction %d", dir);
1472 		return SCRIPT_CONT;
1473 	}
1474 	if (dir == 8)
1475 		dir = cpt->o_dir;
1476 	cpt->o_xcoord = x;
1477 	cpt->o_ycoord = y;
1478 	return fnStand(cpt, id, dir, stance, 0, 0, 0, 0);
1479 }
1480 
fnFace(Object * cpt,int32 id,int32 targetId,int32 b,int32 c,int32 d,int32 a,int32 z)1481 int Logic::fnFace(Object *cpt, int32 id, int32 targetId, int32 b, int32 c, int32 d, int32 a, int32 z) {
1482 	Object *target = _objMan->fetchObject(targetId);
1483 	int32 x, y;
1484 	if ((target->o_type == TYPE_MEGA) || (target->o_type == TYPE_PLAYER)) {
1485 		x = target->o_xcoord;
1486 		y = target->o_ycoord;
1487 	} else {
1488 		x = (target->o_mouse_x1 + target->o_mouse_x2) / 2;
1489 		y = target->o_mouse_y2;
1490 	}
1491 	int32 megaTarDir = whatTarget(cpt->o_xcoord, cpt->o_ycoord, x, y);
1492 	fnTurn(cpt, id, megaTarDir, 0, 0, 0, 0, 0);
1493 	return SCRIPT_STOP;
1494 }
1495 
fnFaceXy(Object * cpt,int32 id,int32 x,int32 y,int32 c,int32 d,int32 a,int32 b)1496 int Logic::fnFaceXy(Object *cpt, int32 id, int32 x, int32 y, int32 c, int32 d, int32 a, int32 b) {
1497 	int megaTarDir = whatTarget(cpt->o_xcoord, cpt->o_ycoord, x, y);
1498 	fnTurn(cpt, id, megaTarDir, 0, 0, 0, 0, 0);
1499 	return SCRIPT_STOP;
1500 }
1501 
fnIsFacing(Object * cpt,int32 id,int32 targetId,int32 b,int32 c,int32 d,int32 a,int32 z)1502 int Logic::fnIsFacing(Object *cpt, int32 id, int32 targetId, int32 b, int32 c, int32 d, int32 a, int32 z) {
1503 	Object *target = _objMan->fetchObject(targetId);
1504 	int32 x, y, dir;
1505 	if ((target->o_type == TYPE_MEGA) || (target->o_type == TYPE_PLAYER)) {
1506 		x = target->o_xcoord;
1507 		y = target->o_ycoord;
1508 		dir = target->o_dir;
1509 	} else
1510 		error("fnIsFacing:: Target isn't a mega");
1511 
1512 	int32 lookDir = whatTarget(x, y, cpt->o_xcoord, cpt->o_ycoord);
1513 	lookDir -= dir;
1514 	lookDir = ABS(lookDir);
1515 
1516 	if (lookDir > 4)
1517 		lookDir = 8 - lookDir;
1518 
1519 	_scriptVars[RETURN_VALUE] = lookDir;
1520 	return SCRIPT_STOP;
1521 }
1522 
fnGetTo(Object * cpt,int32 id,int32 a,int32 b,int32 c,int32 d,int32 z,int32 x)1523 int Logic::fnGetTo(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
1524 	Object *place = _objMan->fetchObject(cpt->o_place);
1525 
1526 	cpt->o_tree.o_script_level++;
1527 	cpt->o_tree.o_script_pc[cpt->o_tree.o_script_level] = place->o_get_to_script;
1528 	cpt->o_tree.o_script_id[cpt->o_tree.o_script_level] = place->o_get_to_script;
1529 	return SCRIPT_STOP;
1530 }
1531 
fnGetToError(Object * cpt,int32 id,int32 a,int32 b,int32 c,int32 d,int32 z,int32 x)1532 int Logic::fnGetToError(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
1533 	debug(1, "fnGetToError: compact %d at place %d no get-to for target %d, click_id %d\n", id, cpt->o_place, cpt->o_target, _scriptVars[CLICK_ID]);
1534 	return SCRIPT_CONT;
1535 }
1536 
fnRandom(Object * compact,int32 id,int32 min,int32 max,int32 e,int32 f,int32 z,int32 x)1537 int Logic::fnRandom(Object *compact, int32 id, int32 min, int32 max, int32 e, int32 f, int32 z, int32 x) {
1538 	_scriptVars[RETURN_VALUE] = _rnd.getRandomNumberRng(min, max);
1539 	return SCRIPT_CONT;
1540 }
1541 
fnGetPos(Object * cpt,int32 id,int32 targetId,int32 b,int32 c,int32 d,int32 z,int32 x)1542 int Logic::fnGetPos(Object *cpt, int32 id, int32 targetId, int32 b, int32 c, int32 d, int32 z, int32 x) {
1543 	Object *target = _objMan->fetchObject(targetId);
1544 	if ((target->o_type == TYPE_MEGA) || (target->o_type == TYPE_PLAYER)) {
1545 		_scriptVars[RETURN_VALUE]   = target->o_xcoord;
1546 		_scriptVars[RETURN_VALUE_2] = target->o_ycoord;
1547 	} else {
1548 		_scriptVars[RETURN_VALUE]   = (target->o_mouse_x1 + target->o_mouse_x2) / 2;
1549 		_scriptVars[RETURN_VALUE_2] = target->o_mouse_y2;
1550 	}
1551 	_scriptVars[RETURN_VALUE_3] = target->o_dir;
1552 
1553 	int32 megaSeperation;
1554 	if (targetId == DUANE)
1555 		megaSeperation = 70; // George & Duane stand with feet 70 pixels apart when at full scale
1556 	else if (targetId == BENOIR)
1557 		megaSeperation = 61; // George & Benoir
1558 	else
1559 		megaSeperation = 42; // George & Nico/Goinfre stand with feet 42 pixels apart when at full scale
1560 
1561 	if (target->o_status & STAT_SHRINK) {
1562 		int32 scale = (target->o_scale_a * target->o_ycoord + target->o_scale_b) / 256;
1563 		_scriptVars[RETURN_VALUE_4] = (megaSeperation * scale) / 256;
1564 	} else
1565 		_scriptVars[RETURN_VALUE_4] = megaSeperation;
1566 	return SCRIPT_CONT;
1567 }
1568 
fnGetGamepadXy(Object * cpt,int32 id,int32 a,int32 b,int32 c,int32 d,int32 z,int32 x)1569 int Logic::fnGetGamepadXy(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
1570 	// playstation only
1571 	return SCRIPT_CONT;
1572 }
1573 
fnPlayFx(Object * cpt,int32 id,int32 fxNo,int32 b,int32 c,int32 d,int32 z,int32 x)1574 int Logic::fnPlayFx(Object *cpt, int32 id, int32 fxNo, int32 b, int32 c, int32 d, int32 z, int32 x) {
1575 	_scriptVars[RETURN_VALUE] = _sound->addToQueue(fxNo);
1576 	return SCRIPT_CONT;
1577 }
1578 
fnStopFx(Object * cpt,int32 id,int32 fxNo,int32 b,int32 c,int32 d,int32 z,int32 x)1579 int Logic::fnStopFx(Object *cpt, int32 id, int32 fxNo, int32 b, int32 c, int32 d, int32 z, int32 x) {
1580 	_sound->fnStopFx(fxNo);
1581 	//_sound->removeFromQueue(fxNo);
1582 	return SCRIPT_CONT;
1583 }
1584 
fnPlayMusic(Object * cpt,int32 id,int32 tuneId,int32 loopFlag,int32 c,int32 d,int32 z,int32 x)1585 int Logic::fnPlayMusic(Object *cpt, int32 id, int32 tuneId, int32 loopFlag, int32 c, int32 d, int32 z, int32 x) {
1586 	if (tuneId == 153)
1587 		return SCRIPT_CONT;
1588 	if (loopFlag == LOOPED)
1589 		_scriptVars[CURRENT_MUSIC] = tuneId; // so it gets restarted when saving & reloading
1590 	else
1591 		_scriptVars[CURRENT_MUSIC] = 0;
1592 
1593 	_music->startMusic(tuneId, loopFlag);
1594 	return SCRIPT_CONT;
1595 }
1596 
fnStopMusic(Object * cpt,int32 id,int32 a,int32 b,int32 c,int32 d,int32 z,int32 x)1597 int Logic::fnStopMusic(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
1598 	_scriptVars[CURRENT_MUSIC] = 0;
1599 	_music->fadeDown();
1600 	return SCRIPT_CONT;
1601 }
1602 
fnInnerSpace(Object * cpt,int32 id,int32 a,int32 b,int32 c,int32 d,int32 z,int32 x)1603 int Logic::fnInnerSpace(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
1604 	error("fnInnerSpace() not working");
1605 	return SCRIPT_STOP; // for compilers that don't support NORETURN
1606 }
1607 
fnSetScreen(Object * cpt,int32 id,int32 target,int32 screen,int32 c,int32 d,int32 z,int32 x)1608 int Logic::fnSetScreen(Object *cpt, int32 id, int32 target, int32 screen, int32 c, int32 d, int32 z, int32 x) {
1609 	_objMan->fetchObject(target)->o_screen = screen;
1610 	return SCRIPT_CONT;
1611 }
1612 
fnPreload(Object * cpt,int32 id,int32 resId,int32 b,int32 c,int32 d,int32 z,int32 x)1613 int Logic::fnPreload(Object *cpt, int32 id, int32 resId, int32 b, int32 c, int32 d, int32 z, int32 x) {
1614 	_resMan->resOpen(resId);
1615 	_resMan->resClose(resId);
1616 	return SCRIPT_CONT;
1617 }
1618 
fnCheckCD(Object * cpt,int32 id,int32 screen,int32 b,int32 c,int32 d,int32 z,int32 x)1619 int Logic::fnCheckCD(Object *cpt, int32 id, int32 screen, int32 b, int32 c, int32 d, int32 z, int32 x) {
1620 	// only a dummy, here.
1621 	// the check is done in the mainloop
1622 	return SCRIPT_CONT;
1623 }
1624 
fnRestartGame(Object * cpt,int32 id,int32 a,int32 b,int32 c,int32 d,int32 z,int32 x)1625 int Logic::fnRestartGame(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
1626 	SwordEngine::_systemVars.forceRestart = true;
1627 	cpt->o_logic = LOGIC_quit;
1628 	return SCRIPT_STOP;
1629 }
1630 
fnQuitGame(Object * cpt,int32 id,int32 a,int32 b,int32 c,int32 d,int32 z,int32 x)1631 int Logic::fnQuitGame(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
1632 	if (SwordEngine::_systemVars.isDemo) {
1633 		GUI::MessageDialog dialog(_("This is the end of the Broken Sword 1 Demo"), _("OK"), NULL);
1634 		dialog.runModal();
1635 		Engine::quitGame();
1636 	} else
1637 		error("fnQuitGame() called");
1638 	return fnQuit(cpt, id, 0, 0, 0, 0, 0, 0);
1639 }
1640 
fnDeathScreen(Object * cpt,int32 id,int32 a,int32 b,int32 c,int32 d,int32 z,int32 x)1641 int Logic::fnDeathScreen(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
1642 
1643 	if (_scriptVars[FINALE_OPTION_FLAG] == 4) // successful end of game!
1644 		SwordEngine::_systemVars.controlPanelMode = CP_THEEND;
1645 	else
1646 		SwordEngine::_systemVars.controlPanelMode = CP_DEATHSCREEN;
1647 
1648 	cpt->o_logic = LOGIC_quit;
1649 	return SCRIPT_STOP;
1650 }
1651 
fnSetParallax(Object * cpt,int32 id,int32 screen,int32 resId,int32 c,int32 d,int32 z,int32 x)1652 int Logic::fnSetParallax(Object *cpt, int32 id, int32 screen, int32 resId, int32 c, int32 d, int32 z, int32 x) {
1653 	_screen->fnSetParallax(screen, resId);
1654 	return SCRIPT_CONT;
1655 }
1656 
fnTdebug(Object * cpt,int32 id,int32 a,int32 b,int32 c,int32 d,int32 z,int32 x)1657 int Logic::fnTdebug(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
1658 	debug(1, "Script TDebug id %d code %d, %d", id, a, b);
1659 	return SCRIPT_CONT;
1660 }
1661 
fnRedFlash(Object * cpt,int32 id,int32 a,int32 b,int32 c,int32 d,int32 z,int32 x)1662 int Logic::fnRedFlash(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
1663 	_screen->fnFlash(FLASH_RED);
1664 	return SCRIPT_CONT;
1665 }
1666 
fnBlueFlash(Object * cpt,int32 id,int32 a,int32 b,int32 c,int32 d,int32 z,int32 x)1667 int Logic::fnBlueFlash(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
1668 	_screen->fnFlash(FLASH_BLUE);
1669 	return SCRIPT_CONT;
1670 }
1671 
fnYellow(Object * cpt,int32 id,int32 a,int32 b,int32 c,int32 d,int32 z,int32 x)1672 int Logic::fnYellow(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
1673 	_screen->fnFlash(BORDER_YELLOW);
1674 	return SCRIPT_CONT;
1675 }
1676 
fnGreen(Object * cpt,int32 id,int32 a,int32 b,int32 c,int32 d,int32 z,int32 x)1677 int Logic::fnGreen(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
1678 	_screen->fnFlash(BORDER_GREEN);
1679 	return SCRIPT_CONT;
1680 }
1681 
fnPurple(Object * cpt,int32 id,int32 a,int32 b,int32 c,int32 d,int32 z,int32 x)1682 int Logic::fnPurple(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
1683 	_screen->fnFlash(BORDER_PURPLE);
1684 	return SCRIPT_CONT;
1685 }
1686 
fnBlack(Object * cpt,int32 id,int32 a,int32 b,int32 c,int32 d,int32 z,int32 x)1687 int Logic::fnBlack(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
1688 	_screen->fnFlash(BORDER_BLACK);
1689 	return SCRIPT_CONT;
1690 }
1691 
startPosCallFn(uint8 fnId,uint32 param1,uint32 param2,uint32 param3)1692 void Logic::startPosCallFn(uint8 fnId, uint32 param1, uint32 param2, uint32 param3) {
1693 	Object *obj = NULL;
1694 	switch (fnId) {
1695 	case opcPlaySequence:
1696 		fnPlaySequence(NULL, 0, param1, 0, 0, 0, 0, 0);
1697 		break;
1698 	case opcAddObject:
1699 		fnAddObject(NULL, 0, param1, 0, 0, 0, 0, 0);
1700 		break;
1701 	case opcRemoveObject:
1702 		fnRemoveObject(NULL, 0, param1, 0, 0, 0, 0, 0);
1703 		break;
1704 	case opcMegaSet:
1705 		obj = _objMan->fetchObject(param1);
1706 		fnMegaSet(obj, param1, param2, param3, 0, 0, 0, 0);
1707 		break;
1708 	case opcNoSprite:
1709 		obj = _objMan->fetchObject(param1);
1710 		fnNoSprite(obj, param1, param2, param3, 0, 0, 0, 0);
1711 		break;
1712 	default:
1713 		error("Illegal fnCallfn argument %d", fnId);
1714 	}
1715 }
1716 
runStartScript(const uint8 * data)1717 void Logic::runStartScript(const uint8 *data) {
1718 	// Here data is a static resource defined in staticres.cpp
1719 	// It is always in little endian
1720 	uint16 varId = 0;
1721 	uint8 fnId = 0;
1722 	uint32 param1 = 0;
1723 	while (*data != opcSeqEnd) {
1724 		switch (*data++) {
1725 		case opcCallFn:
1726 			fnId = *data++;
1727 			param1 = *data++;
1728 			startPosCallFn(fnId, param1, 0, 0);
1729 			break;
1730 		case opcCallFnLong:
1731 			fnId = *data++;
1732 			startPosCallFn(fnId, READ_LE_UINT32(data), READ_LE_UINT32(data + 4), READ_LE_UINT32(data + 8));
1733 			data += 12;
1734 			break;
1735 		case opcSetVar8:
1736 			varId = READ_LE_UINT16(data);
1737 			_scriptVars[varId] = data[2];
1738 			data += 3;
1739 			break;
1740 		case opcSetVar16:
1741 			varId = READ_LE_UINT16(data);
1742 			_scriptVars[varId] = READ_LE_UINT16(data + 2);
1743 			data += 4;
1744 			break;
1745 		case opcSetVar32:
1746 			varId = READ_LE_UINT16(data);
1747 			_scriptVars[varId] = READ_LE_UINT32(data + 2);
1748 			data += 6;
1749 			break;
1750 		case opcGeorge:
1751 			_scriptVars[CHANGE_X]     = READ_LE_UINT16(data + 0);
1752 			_scriptVars[CHANGE_Y]     = READ_LE_UINT16(data + 2);
1753 			_scriptVars[CHANGE_DIR]   = data[4];
1754 			_scriptVars[CHANGE_PLACE] = READ_LE_UINT24(data + 5);
1755 			data += 8;
1756 			break;
1757 		case opcRunStart:
1758 			data = _startData[*data];
1759 			break;
1760 		case opcRunHelper:
1761 			data = _helperData[*data];
1762 			break;
1763 		default:
1764 			error("Unexpected opcode in StartScript");
1765 		}
1766 	}
1767 }
1768 
startPositions(uint32 pos)1769 void Logic::startPositions(uint32 pos) {
1770 	bool spainVisit2 = false;
1771 	if ((pos >= 956) && (pos <= 962)) {
1772 		spainVisit2 = true;
1773 		pos -= 900;
1774 	}
1775 	if ((pos > 80) || (_startData[pos] == NULL))
1776 		error("Starting in Section %d is not supported", pos);
1777 
1778 	Logic::_scriptVars[CHANGE_STANCE] = STAND;
1779 	Logic::_scriptVars[GEORGE_CDT_FLAG] = GEO_TLK_TABLE;
1780 
1781 	runStartScript(_startData[pos]);
1782 	if (spainVisit2)
1783 		runStartScript(_helperData[HELP_SPAIN2]);
1784 
1785 	if (pos == 0)
1786 		pos = 1;
1787 	Object *compact = _objMan->fetchObject(PLAYER);
1788 	fnEnterSection(compact, PLAYER, pos, 0, 0, 0, 0, 0);    // (automatically opens the compact resource for that section)
1789 	SwordEngine::_systemVars.controlPanelMode = CP_NORMAL;
1790 	SwordEngine::_systemVars.wantFade = true;
1791 }
1792 
1793 const uint32 Logic::_scriptVarInit[NON_ZERO_SCRIPT_VARS][2] = {
1794 	{  42,  448}, {  43,  378}, {  51,    1}, {  92,    1}, { 147,   71}, { 201,   1},
1795 	{ 209,    1}, { 215,    1}, { 242,    2}, { 244,    1}, { 246,    3}, { 247,   1},
1796 	{ 253,    1}, { 297,    1}, { 398,    1}, { 508,    1}, { 605,    1}, { 606,   1},
1797 	{ 701,    1}, { 709,    1}, { 773,    1}, { 843,    1}, { 907,    1}, { 923,   1},
1798 	{ 966,    1}, { 988,    2}, {1058,    1}, {1059,    2}, {1060,    3}, {1061,   4},
1799 	{1062,    5}, {1063,    6}, {1064,    7}, {1065,    8}, {1066,    9}, {1067,  10},
1800 	{1068,   11}, {1069,   12}, {1070,   13}, {1071,   14}, {1072,   15}, {1073,  16},
1801 	{1074,   17}, {1075,   18}, {1076,   19}, {1077,   20}, {1078,   21}, {1079,  22},
1802 	{1080,   23}, {1081,   24}, {1082,   25}, {1083,   26}, {1084,   27}, {1085,  28},
1803 	{1086,   29}, {1087,   30}, {1088,   31}, {1089,   32}, {1090,   33}, {1091,  34},
1804 	{1092,   35}, {1093,   36}, {1094,   37}, {1095,   38}, {1096,   39}, {1097,  40},
1805 	{1098,   41}, {1099,   42}, {1100,   43}, {1101,   44}, {1102,   48}, {1103,  45},
1806 	{1104,   47}, {1105,   49}, {1106,   50}, {1107,   52}, {1108,   54}, {1109,  56},
1807 	{1110,   57}, {1111,   58}, {1112,   59}, {1113,   60}, {1114,   61}, {1115,  62},
1808 	{1116,   63}, {1117,   64}, {1118,   65}, {1119,   66}, {1120,   67}, {1121,  68},
1809 	{1122,   69}, {1123,   71}, {1124,   72}, {1125,   73}, {1126,   74}
1810 };
1811 
1812 } // End of namespace Sword1
1813