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/str.h"
25 #include "common/translation.h"
26 
27 #include "gui/message.h"
28 
29 #include "gob/gob.h"
30 #include "gob/inter.h"
31 #include "gob/global.h"
32 #include "gob/util.h"
33 #include "gob/dataio.h"
34 #include "gob/draw.h"
35 #include "gob/game.h"
36 #include "gob/expression.h"
37 #include "gob/script.h"
38 #include "gob/resources.h"
39 #include "gob/hotspots.h"
40 #include "gob/goblin.h"
41 #include "gob/map.h"
42 #include "gob/mult.h"
43 #include "gob/scenery.h"
44 #include "gob/video.h"
45 #include "gob/save/saveload.h"
46 #include "gob/videoplayer.h"
47 #include "gob/sound/sound.h"
48 
49 namespace Gob {
50 
51 #define OPCODEVER Inter_v2
52 #define OPCODEDRAW(i, x)  _opcodesDraw[i]._OPCODEDRAW(OPCODEVER, x)
53 #define OPCODEFUNC(i, x)  _opcodesFunc[i]._OPCODEFUNC(OPCODEVER, x)
54 #define OPCODEGOB(i, x)   _opcodesGob[i]._OPCODEGOB(OPCODEVER, x)
55 
Inter_v2(GobEngine * vm)56 Inter_v2::Inter_v2(GobEngine *vm) : Inter_v1(vm) {
57 }
58 
setupOpcodesDraw()59 void Inter_v2::setupOpcodesDraw() {
60 	Inter_v1::setupOpcodesDraw();
61 
62 	OPCODEDRAW(0x01, o2_playMult);
63 	OPCODEDRAW(0x02, o2_freeMultKeys);
64 
65 	OPCODEDRAW(0x0A, o2_setRenderFlags);
66 
67 	OPCODEDRAW(0x13, o2_multSub);
68 
69 	OPCODEDRAW(0x14, o2_initMult);
70 
71 	OPCODEDRAW(0x17, o2_loadMultObject);
72 
73 	OPCODEDRAW(0x1C, o2_renderStatic);
74 	OPCODEDRAW(0x1D, o2_loadCurLayer);
75 
76 	OPCODEDRAW(0x20, o2_playCDTrack);
77 	OPCODEDRAW(0x21, o2_waitCDTrackEnd);
78 	OPCODEDRAW(0x22, o2_stopCD);
79 	OPCODEDRAW(0x23, o2_readLIC);
80 
81 	OPCODEDRAW(0x24, o2_freeLIC);
82 	OPCODEDRAW(0x25, o2_getCDTrackPos);
83 
84 	OPCODEDRAW(0x30, o2_loadFontToSprite);
85 
86 	OPCODEDRAW(0x40, o2_totSub);
87 	OPCODEDRAW(0x41, o2_switchTotSub);
88 	OPCODEDRAW(0x42, o2_pushVars);
89 	OPCODEDRAW(0x43, o2_popVars);
90 
91 	OPCODEDRAW(0x50, o2_loadMapObjects);
92 	OPCODEDRAW(0x51, o2_freeGoblins);
93 	OPCODEDRAW(0x52, o2_moveGoblin);
94 	OPCODEDRAW(0x53, o2_writeGoblinPos);
95 
96 	OPCODEDRAW(0x54, o2_stopGoblin);
97 	OPCODEDRAW(0x55, o2_setGoblinState);
98 	OPCODEDRAW(0x56, o2_placeGoblin);
99 
100 	OPCODEDRAW(0x80, o2_initScreen);
101 	OPCODEDRAW(0x81, o2_scroll);
102 	OPCODEDRAW(0x82, o2_setScrollOffset);
103 	OPCODEDRAW(0x83, o2_playImd);
104 
105 	OPCODEDRAW(0x84, o2_getImdInfo);
106 	OPCODEDRAW(0x85, o2_openItk);
107 	OPCODEDRAW(0x86, o2_closeItk);
108 	OPCODEDRAW(0x87, o2_setImdFrontSurf);
109 
110 	OPCODEDRAW(0x88, o2_resetImdFrontSurf);
111 }
112 
setupOpcodesFunc()113 void Inter_v2::setupOpcodesFunc() {
114 	Inter_v1::setupOpcodesFunc();
115 
116 	OPCODEFUNC(0x09, o2_assign);
117 
118 	OPCODEFUNC(0x11, o2_printText);
119 
120 	OPCODEFUNC(0x17, o2_animPalInit);
121 
122 	OPCODEFUNC(0x18, o2_addHotspot);
123 	OPCODEFUNC(0x19, o2_removeHotspot);
124 	OPCODEFUNC(0x1A, o2_getTotTextItemPart);
125 
126 	OPCODEFUNC(0x25, o2_goblinFunc);
127 
128 	OPCODEFUNC(0x39, o2_stopSound);
129 	OPCODEFUNC(0x3A, o2_loadSound);
130 
131 	OPCODEFUNC(0x3E, o2_getFreeMem);
132 	OPCODEFUNC(0x3F, o2_checkData);
133 
134 	OPCODEFUNC(0x4D, o2_readData);
135 	OPCODEFUNC(0x4E, o2_writeData);
136 }
137 
setupOpcodesGob()138 void Inter_v2::setupOpcodesGob() {
139 	OPCODEGOB(  0, o2_loadInfogramesIns);
140 	OPCODEGOB(  1, o2_startInfogrames);
141 	OPCODEGOB(  2, o2_stopInfogrames);
142 
143 	OPCODEGOB( 10, o2_playInfogrames);
144 
145 	OPCODEGOB(100, o2_handleGoblins);
146 
147 	OPCODEGOB(500, o2_playProtracker);
148 	OPCODEGOB(501, o2_stopProtracker);
149 }
150 
checkSwitchTable(uint32 & offset)151 void Inter_v2::checkSwitchTable(uint32 &offset) {
152 	byte type;
153 	int16 len;
154 	int32 value;
155 	bool found;
156 
157 	found = false;
158 	offset = 0;
159 
160 	type = _vm->_game->_script->peekByte();
161 
162 	value = (uint16) _vm->_game->_script->readVarIndex();
163 
164 	switch (type) {
165 	case TYPE_VAR_INT8:
166 	case TYPE_ARRAY_INT8:
167 		value = (int8) READ_VARO_UINT8(value);
168 		break;
169 
170 	case TYPE_VAR_INT32:
171 	case TYPE_ARRAY_INT32:
172 		value = READ_VARO_UINT32(value);
173 		break;
174 
175 	default:
176 		value = (int16) READ_VARO_UINT16(value);
177 		break;
178 	}
179 
180 	if (_terminate)
181 		return;
182 
183 	len = _vm->_game->_script->readInt8();
184 	while (len != -5) {
185 		for (int i = 0; i < len; i++) {
186 			type = _vm->_game->_script->peekByte();
187 
188 			switch (type) {
189 			case TYPE_IMM_INT32:
190 				_vm->_game->_script->skip(1);
191 				if (!found &&
192 						(value == _vm->_game->_script->peekInt32()))
193 					found = true;
194 				_vm->_game->_script->skip(5);
195 				break;
196 
197 			case TYPE_IMM_INT16:
198 				_vm->_game->_script->skip(1);
199 				if (!found &&
200 						(value == _vm->_game->_script->peekInt16()))
201 					found = true;
202 				_vm->_game->_script->skip(3);
203 				break;
204 
205 			case TYPE_IMM_INT8:
206 				_vm->_game->_script->skip(1);
207 				if (!found && (value == _vm->_game->_script->peekInt8()))
208 					found = true;
209 				_vm->_game->_script->skip(2);
210 				break;
211 
212 			default:
213 				if (!found) {
214 					_vm->_game->_script->evalExpr(0);
215 					if (value == _vm->_game->_script->getResultInt())
216 						found = true;
217 				} else
218 					_vm->_game->_script->skipExpr(99);
219 				break;
220 			}
221 		}
222 
223 		if (found && (offset == 0))
224 			offset = _vm->_game->_script->pos();
225 
226 		_vm->_game->_script->skip(_vm->_game->_script->peekUint16(2) + 2);
227 		len = _vm->_game->_script->readInt8();
228 	}
229 
230 	if ((_vm->_game->_script->peekByte() >> 4) != 4)
231 		return;
232 
233 	_vm->_game->_script->skip(1);
234 	if (offset == 0)
235 		offset = _vm->_game->_script->pos();
236 
237 	_vm->_game->_script->skip(_vm->_game->_script->peekUint16(2) + 2);
238 }
239 
o2_playMult()240 void Inter_v2::o2_playMult() {
241 	int16 checkEscape;
242 
243 	checkEscape = _vm->_game->_script->readInt16();
244 
245 	_vm->_mult->setMultData(checkEscape >> 1);
246 	_vm->_mult->playMult(VAR(57), -1, checkEscape & 0x1, 0);
247 }
248 
o2_freeMultKeys()249 void Inter_v2::o2_freeMultKeys() {
250 	uint16 index = _vm->_game->_script->readUint16();
251 
252 	if (!_vm->_mult->hasMultData(index))
253 		return;
254 
255 	_vm->_mult->setMultData(index);
256 	_vm->_mult->freeMultKeys();
257 	_vm->_mult->zeroMultData(index);
258 }
259 
o2_setRenderFlags()260 void Inter_v2::o2_setRenderFlags() {
261 	int16 expr;
262 
263 	expr = _vm->_game->_script->readValExpr();
264 
265 	if (expr & 0x8000) {
266 		_vm->_draw->_renderFlags |= expr & 0x3FFF;
267 	} else {
268 		if (expr & 0x4000)
269 			_vm->_draw->_renderFlags &= expr & 0x3FFF;
270 		else
271 			_vm->_draw->_renderFlags = expr;
272 	}
273 }
274 
o2_multSub()275 void Inter_v2::o2_multSub() {
276 	_vm->_mult->multSub(_vm->_game->_script->readValExpr());
277 }
278 
o2_initMult()279 void Inter_v2::o2_initMult() {
280 	int16 oldAnimHeight;
281 	int16 oldAnimWidth;
282 	int16 oldObjCount;
283 	uint16 posXVar;
284 	uint16 posYVar;
285 	uint16 animDataVar;
286 
287 	oldAnimWidth = _vm->_mult->_animWidth;
288 	oldAnimHeight = _vm->_mult->_animHeight;
289 	oldObjCount = _vm->_mult->_objCount;
290 
291 	_vm->_mult->_animLeft = _vm->_game->_script->readInt16();
292 	_vm->_mult->_animTop = _vm->_game->_script->readInt16();
293 	_vm->_mult->_animWidth = _vm->_game->_script->readInt16();
294 	_vm->_mult->_animHeight = _vm->_game->_script->readInt16();
295 	_vm->_mult->_objCount = _vm->_game->_script->readInt16();
296 	posXVar = _vm->_game->_script->readVarIndex();
297 	posYVar = _vm->_game->_script->readVarIndex();
298 	animDataVar = _vm->_game->_script->readVarIndex();
299 
300 	if (_vm->_mult->_objects && (oldObjCount != _vm->_mult->_objCount)) {
301 		warning("Initializing new objects without having "
302 				"cleaned up the old ones at first");
303 
304 		_vm->_mult->clearObjectVideos();
305 
306 		for (int i = 0; i < _vm->_mult->_objCount; i++) {
307 			delete _vm->_mult->_objects[i].pPosX;
308 			delete _vm->_mult->_objects[i].pPosY;
309 		}
310 
311 		delete[] _vm->_mult->_objects;
312 		delete[] _vm->_mult->_renderObjs;
313 		delete[] _vm->_mult->_orderArray;
314 
315 		_vm->_mult->_objects = 0;
316 		_vm->_mult->_renderObjs = 0;
317 		_vm->_mult->_orderArray = 0;
318 	}
319 
320 	if (_vm->_mult->_objects == 0) {
321 		_vm->_mult->_renderObjs = new Mult::Mult_Object*[_vm->_mult->_objCount];
322 		memset(_vm->_mult->_renderObjs, 0,
323 				_vm->_mult->_objCount * sizeof(Mult::Mult_Object*));
324 
325 		if (_terminate)
326 			return;
327 
328 		_vm->_mult->_orderArray = new int8[_vm->_mult->_objCount];
329 		memset(_vm->_mult->_orderArray, 0, _vm->_mult->_objCount * sizeof(int8));
330 		_vm->_mult->_objects = new Mult::Mult_Object[_vm->_mult->_objCount];
331 		memset(_vm->_mult->_objects, 0,
332 				_vm->_mult->_objCount * sizeof(Mult::Mult_Object));
333 
334 		for (int i = 0; i < _vm->_mult->_objCount; i++) {
335 			uint32 offPosX = i * 4 + (posXVar / 4) * 4;
336 			uint32 offPosY = i * 4 + (posYVar / 4) * 4;
337 			uint32 offAnim = animDataVar + i * 4 * _vm->_global->_inter_animDataSize;
338 
339 			_vm->_mult->_objects[i].pPosX = new VariableReference(*_vm->_inter->_variables, offPosX);
340 			_vm->_mult->_objects[i].pPosY = new VariableReference(*_vm->_inter->_variables, offPosY);
341 
342 			_vm->_mult->_objects[i].pAnimData =
343 				(Mult::Mult_AnimData *)_variables->getAddressOff8(offAnim);
344 
345 			_vm->_mult->_objects[i].pAnimData->isStatic = 1;
346 			_vm->_mult->_objects[i].tick = 0;
347 			_vm->_mult->_objects[i].lastLeft = -1;
348 			_vm->_mult->_objects[i].lastRight = -1;
349 			_vm->_mult->_objects[i].lastTop = -1;
350 			_vm->_mult->_objects[i].lastBottom = -1;
351 			_vm->_mult->_objects[i].goblinX = 1;
352 			_vm->_mult->_objects[i].goblinY = 1;
353 		}
354 	}
355 
356 	if (_vm->_mult->_animSurf &&
357 	    ((oldAnimWidth != _vm->_mult->_animWidth) ||
358 			 (oldAnimHeight != _vm->_mult->_animHeight))) {
359 		_vm->_draw->freeSprite(Draw::kAnimSurface);
360 		_vm->_mult->_animSurf.reset();
361 	}
362 
363 	_vm->_draw->adjustCoords(0,
364 			&_vm->_mult->_animWidth, &_vm->_mult->_animHeight);
365 	if (!_vm->_mult->_animSurf) {
366 		_vm->_draw->initSpriteSurf(Draw::kAnimSurface, _vm->_mult->_animWidth,
367 				_vm->_mult->_animHeight, 0);
368 		_vm->_mult->_animSurf = _vm->_draw->_spritesArray[Draw::kAnimSurface];
369 		if (_terminate)
370 			return;
371 	}
372 
373 	_vm->_draw->adjustCoords(1,
374 			&_vm->_mult->_animWidth, &_vm->_mult->_animHeight);
375 	_vm->_draw->_sourceSurface = Draw::kBackSurface;
376 	_vm->_draw->_destSurface = Draw::kAnimSurface;
377 	_vm->_draw->_spriteLeft = _vm->_mult->_animLeft;
378 	_vm->_draw->_spriteTop = _vm->_mult->_animTop;
379 	_vm->_draw->_spriteRight = _vm->_mult->_animWidth;
380 	_vm->_draw->_spriteBottom = _vm->_mult->_animHeight;
381 	_vm->_draw->_destSpriteX = 0;
382 	_vm->_draw->_destSpriteY = 0;
383 	_vm->_draw->spriteOperation(0);
384 
385 	debugC(4, kDebugGraphics, "o2_initMult: x = %d, y = %d, w = %d, h = %d",
386 		  _vm->_mult->_animLeft, _vm->_mult->_animTop,
387 			_vm->_mult->_animWidth, _vm->_mult->_animHeight);
388 	debugC(4, kDebugGraphics, "    _vm->_mult->_objCount = %d, "
389 			"animation data size = %d", _vm->_mult->_objCount,
390 			_vm->_global->_inter_animDataSize);
391 }
392 
o2_loadMultObject()393 void Inter_v2::o2_loadMultObject() {
394 	assert(_vm->_mult->_objects);
395 
396 	uint16 objIndex = _vm->_game->_script->readValExpr();
397 
398 	debugC(4, kDebugGameFlow, "Loading mult object %d", objIndex);
399 
400 	Mult::Mult_Object &obj = _vm->_mult->_objects[objIndex];
401 	Mult::Mult_AnimData &objAnim = *(obj.pAnimData);
402 
403 	*obj.pPosX = _vm->_game->_script->readValExpr();
404 	*obj.pPosY = _vm->_game->_script->readValExpr();
405 
406 	byte *multData = (byte *) &objAnim;
407 	for (int i = 0; i < 11; i++) {
408 		if (_vm->_game->_script->peekByte() != 99)
409 			multData[i] = _vm->_game->_script->readValExpr();
410 		else
411 			_vm->_game->_script->skip(1);
412 	}
413 
414 	if ((objAnim.animType == 100) && (objIndex < _vm->_goblin->_gobsCount)) {
415 
416 		uint8 posX   = *(obj.pPosX) % 256;
417 		obj.destX    = posX;
418 		obj.gobDestX = posX;
419 		obj.goblinX  = posX;
420 
421 		uint8 posY   = *(obj.pPosY) % 256;
422 		obj.destY    = posY;
423 		obj.gobDestY = posY;
424 		obj.goblinY  = posY;
425 
426 		*(obj.pPosX) *= _vm->_map->getTilesWidth();
427 
428 		int16 layer     = objAnim.layer;
429 		int16 animation = obj.goblinStates[layer][0].animation;
430 
431 		objAnim.framesLeft    = objAnim.maxFrame;
432 		objAnim.nextState     = -1;
433 		objAnim.newState      = -1;
434 		objAnim.pathExistence = 0;
435 		objAnim.isBusy        = 0;
436 		objAnim.state         = layer;
437 		objAnim.layer         = obj.goblinStates[objAnim.state][0].layer;
438 		objAnim.animation     = animation;
439 
440 		_vm->_scenery->updateAnim(layer, 0, animation, 0,
441 				*(obj.pPosX), *(obj.pPosY), 0);
442 
443 		if (!_vm->_map->hasBigTiles())
444 			*(obj.pPosY) = (obj.goblinY + 1) * _vm->_map->getTilesHeight()
445 				- (_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
446 		else
447 			*(obj.pPosY) = ((obj.goblinY + 1) * _vm->_map->getTilesHeight()) -
448 				(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) -
449 				((obj.goblinY + 1) / 2);
450 		*(obj.pPosX) = obj.goblinX * _vm->_map->getTilesWidth();
451 
452 	} else if ((objAnim.animType == 101) && (objIndex < _vm->_goblin->_gobsCount)) {
453 
454 		int16 layer = objAnim.layer;
455 		int16 animation = obj.goblinStates[layer][0].animation;
456 
457 		objAnim.nextState = -1;
458 		objAnim.newState  = -1;
459 		objAnim.state     = layer;
460 		objAnim.layer     = obj.goblinStates[objAnim.state][0].layer;
461 		objAnim.animation = animation;
462 
463 		if ((*(obj.pPosX) == 1000) && (*(obj.pPosY) == 1000)) {
464 			Scenery::AnimLayer *animLayer =
465 				_vm->_scenery->getAnimLayer(animation, objAnim.layer);
466 
467 			*(obj.pPosX) = animLayer->posX;
468 			*(obj.pPosY) = animLayer->posY;
469 		}
470 		_vm->_scenery->updateAnim(layer, 0, animation, 0,
471 				*(obj.pPosX), *(obj.pPosY), 0);
472 
473 	} else if ((objAnim.animType != 100) && (objAnim.animType != 101)) {
474 
475 		if ((((int32) *(obj.pPosX)) == -1234) && (((int32) *(obj.pPosY)) == -4321)) {
476 
477 			if (obj.videoSlot > 0)
478 				_vm->_vidPlayer->closeVideo(obj.videoSlot - 1);
479 
480 			obj.videoSlot  = 0;
481 			obj.lastLeft   = -1;
482 			obj.lastTop    = -1;
483 			obj.lastBottom = -1;
484 			obj.lastRight  = -1;
485 		}
486 
487 	}
488 }
489 
o2_renderStatic()490 void Inter_v2::o2_renderStatic() {
491 	int16 layer;
492 	int16 index;
493 
494 	index = _vm->_game->_script->readValExpr();
495 	layer = _vm->_game->_script->readValExpr();
496 	_vm->_scenery->renderStatic(index, layer);
497 }
498 
o2_loadCurLayer()499 void Inter_v2::o2_loadCurLayer() {
500 	_vm->_scenery->_curStatic = _vm->_game->_script->readValExpr();
501 	_vm->_scenery->_curStaticLayer = _vm->_game->_script->readValExpr();
502 }
503 
o2_playCDTrack()504 void Inter_v2::o2_playCDTrack() {
505 	if (!(_vm->_draw->_renderFlags & RENDERFLAG_NOBLITINVALIDATED))
506 		_vm->_draw->blitInvalidated();
507 
508 	_vm->_sound->cdPlay(_vm->_game->_script->evalString());
509 }
510 
o2_waitCDTrackEnd()511 void Inter_v2::o2_waitCDTrackEnd() {
512 	debugC(1, kDebugSound, "CDROM: Waiting for playback to end");
513 
514 	while (_vm->_sound->cdGetTrackPos() >= 0)
515 		_vm->_util->longDelay(1);
516 }
517 
o2_stopCD()518 void Inter_v2::o2_stopCD() {
519 	_vm->_sound->cdStop();
520 }
521 
o2_readLIC()522 void Inter_v2::o2_readLIC() {
523 	Common::String file = _vm->_game->_script->evalString();
524 	file += ".LIC";
525 
526 	_vm->_sound->cdLoadLIC(file.c_str());
527 }
528 
o2_freeLIC()529 void Inter_v2::o2_freeLIC() {
530 	_vm->_sound->cdUnloadLIC();
531 }
532 
o2_getCDTrackPos()533 void Inter_v2::o2_getCDTrackPos() {
534 	int16 varPos;
535 	int16 varName;
536 
537 	_vm->_util->longDelay(1);
538 
539 	varPos = _vm->_game->_script->readVarIndex();
540 	varName = _vm->_game->_script->readVarIndex();
541 
542 	WRITE_VAR_OFFSET(varPos, _vm->_sound->cdGetTrackPos(GET_VARO_STR(varName)));
543 	WRITE_VARO_STR(varName, _vm->_sound->cdGetCurrentTrack());
544 }
545 
o2_loadFontToSprite()546 void Inter_v2::o2_loadFontToSprite() {
547 	int16 i = _vm->_game->_script->readInt16();
548 
549 	_vm->_draw->_fontToSprite[i].sprite = _vm->_game->_script->readByte();
550 	_vm->_game->_script->skip(1);
551 	_vm->_draw->_fontToSprite[i].base = _vm->_game->_script->readByte();
552 	_vm->_game->_script->skip(1);
553 	_vm->_draw->_fontToSprite[i].width = _vm->_game->_script->readByte();
554 	_vm->_game->_script->skip(1);
555 	_vm->_draw->_fontToSprite[i].height = _vm->_game->_script->readByte();
556 	_vm->_game->_script->skip(1);
557 }
558 
o2_totSub()559 void Inter_v2::o2_totSub() {
560 	uint8 length = _vm->_game->_script->readByte();
561 	if ((length & 0x7F) > 13)
562 		error("Length in o2_totSub is greater than 13 (%d)", length);
563 
564 	Common::String totFile;
565 	if (length & 0x80)
566 		totFile = _vm->_game->_script->evalString();
567 	else
568 		for (uint8 i = 0; i < length; i++)
569 			totFile += _vm->_game->_script->readChar();
570 
571 	// WORKAROUND: There is a race condition in the script when opening the notepad
572 	if (!totFile.equalsIgnoreCase("edit"))
573 		_vm->_util->forceMouseUp();
574 
575 	// WORKAROUND: For some reason, the variable indicating which TOT to load next
576 	// is overwritten in the guard house card game in Woodruff
577 	if ((_vm->getGameType() == kGameTypeWoodruff) && (totFile == "6"))
578 		totFile = "EMAP2011";
579 
580 	uint8 flags = _vm->_game->_script->readByte();
581 
582 	_vm->_game->totSub(flags, totFile);
583 }
584 
o2_switchTotSub()585 void Inter_v2::o2_switchTotSub() {
586 	int16 index;
587 	int16 function;
588 
589 	index = _vm->_game->_script->readInt16();
590 	function = _vm->_game->_script->readInt16();
591 
592 	_vm->_game->switchTotSub(index, function);
593 }
594 
o2_pushVars()595 void Inter_v2::o2_pushVars() {
596 	uint8 count = _vm->_game->_script->readByte();
597 	for (int i = 0; i < count; i++) {
598 		if ((_vm->_game->_script->peekByte() == 25) ||
599 				(_vm->_game->_script->peekByte() == 28)) {
600 
601 			int16 varOff = _vm->_game->_script->readVarIndex();
602 			_vm->_game->_script->skip(1);
603 
604 			_varStack.pushData(*_variables, varOff, _vm->_global->_inter_animDataSize * 4);
605 
606 		} else {
607 			int16 value;
608 
609 			if (_vm->_game->_script->evalExpr(&value) != 20)
610 				value = 0;
611 
612 			_varStack.pushInt((uint16)value);
613 		}
614 	}
615 }
616 
o2_popVars()617 void Inter_v2::o2_popVars() {
618 	uint8 count = _vm->_game->_script->readByte();
619 	for (int i = 0; i < count; i++) {
620 		int16 varOff = _vm->_game->_script->readVarIndex();
621 
622 		_varStack.pop(*_variables, varOff);
623 	}
624 }
625 
o2_loadMapObjects()626 void Inter_v2::o2_loadMapObjects() {
627 	_vm->_map->loadMapObjects(0);
628 }
629 
o2_freeGoblins()630 void Inter_v2::o2_freeGoblins() {
631 	_vm->_goblin->freeObjects();
632 }
633 
o2_moveGoblin()634 void Inter_v2::o2_moveGoblin() {
635 	int16 destX, destY;
636 	int16 index;
637 
638 	destX = _vm->_game->_script->readValExpr();
639 	destY = _vm->_game->_script->readValExpr();
640 	index = _vm->_game->_script->readValExpr();
641 	_vm->_goblin->move(destX, destY, index);
642 }
643 
o2_writeGoblinPos()644 void Inter_v2::o2_writeGoblinPos() {
645 	int16 varX, varY;
646 	int16 index;
647 
648 	varX = _vm->_game->_script->readVarIndex();
649 	varY = _vm->_game->_script->readVarIndex();
650 	index = _vm->_game->_script->readValExpr();
651 	WRITE_VAR_OFFSET(varX, _vm->_mult->_objects[index].goblinX);
652 	WRITE_VAR_OFFSET(varY, _vm->_mult->_objects[index].goblinY);
653 }
654 
o2_stopGoblin()655 void Inter_v2::o2_stopGoblin() {
656 	int16 index = _vm->_game->_script->readValExpr();
657 
658 	_vm->_mult->_objects[index].pAnimData->pathExistence = 4;
659 }
660 
o2_setGoblinState()661 void Inter_v2::o2_setGoblinState() {
662 	int16 index;
663 	int16 state;
664 	int16 type;
665 	int16 layer;
666 	int16 animation;
667 	int16 deltaX, deltaY;
668 	int16 deltaWidth, deltaHeight;
669 
670 	index = _vm->_game->_script->readValExpr();
671 	state = _vm->_game->_script->readValExpr();
672 	type = _vm->_game->_script->readValExpr();
673 
674 	Mult::Mult_Object &obj = _vm->_mult->_objects[index];
675 	Mult::Mult_AnimData &objAnim = *(obj.pAnimData);
676 
677 	objAnim.stateType = type;
678 	if (!obj.goblinStates || !obj.goblinStates[state])
679 		return;
680 
681 	Scenery::AnimLayer *animLayer;
682 	switch (type) {
683 	case 0:
684 		objAnim.frame = 0;
685 		layer = obj.goblinStates[state][0].layer;
686 		animation = obj.goblinStates[state][0].animation;
687 		objAnim.state = state;
688 		objAnim.layer = layer;
689 		objAnim.animation = animation;
690 
691 		animLayer = _vm->_scenery->getAnimLayer(animation, layer);
692 		*(obj.pPosX) = animLayer->posX;
693 		*(obj.pPosY) = animLayer->posY;
694 		objAnim.isPaused = 0;
695 		objAnim.isStatic = 0;
696 		objAnim.newCycle = animLayer->framesCount;
697 		break;
698 
699 	case 1:
700 	case 4:
701 	case 6:
702 		layer = obj.goblinStates[objAnim.state][0].layer;
703 		animation = obj.goblinStates[objAnim.state][0].animation;
704 		_vm->_scenery->updateAnim(layer, 0, animation, 0,
705 				*(obj.pPosX), *(obj.pPosY), 0);
706 
707 		deltaHeight = _vm->_scenery->_animBottom - _vm->_scenery->_animTop;
708 		deltaWidth = _vm->_scenery->_animRight - _vm->_scenery->_animLeft;
709 
710 		animLayer =
711 			_vm->_scenery->getAnimLayer(objAnim.animation, objAnim.layer);
712 		deltaX = animLayer->animDeltaX;
713 		deltaY = animLayer->animDeltaY;
714 
715 		layer = obj.goblinStates[state][0].layer;
716 		animation = obj.goblinStates[state][0].animation;
717 		objAnim.state = state;
718 		objAnim.layer = layer;
719 		objAnim.animation = animation;
720 		objAnim.frame = 0;
721 		objAnim.isPaused = 0;
722 		objAnim.isStatic = 0;
723 
724 		animLayer = _vm->_scenery->getAnimLayer(animation, layer);
725 		objAnim.newCycle = animLayer->framesCount;
726 
727 		_vm->_scenery->updateAnim(layer, 0, animation, 0,
728 				*(obj.pPosX), *(obj.pPosY), 0);
729 
730 		deltaHeight -= _vm->_scenery->_animBottom - _vm->_scenery->_animTop;
731 		deltaWidth -= _vm->_scenery->_animRight - _vm->_scenery->_animLeft;
732 		*(obj.pPosX) += deltaWidth + deltaX;
733 		*(obj.pPosY) += deltaHeight + deltaY;
734 		break;
735 
736 	case 11:
737 		layer = obj.goblinStates[state][0].layer;
738 		animation = obj.goblinStates[state][0].animation;
739 		objAnim.state = state;
740 		objAnim.layer = layer;
741 		objAnim.animation = animation;
742 		objAnim.frame = 0;
743 		objAnim.isPaused = 0;
744 		objAnim.isStatic = 0;
745 
746 		animLayer = _vm->_scenery->getAnimLayer(animation, layer);
747 		objAnim.newCycle = animLayer->framesCount;
748 		_vm->_scenery->updateAnim(layer, 0, animation, 0,
749 				*(obj.pPosX), *(obj.pPosY), 0);
750 
751 		if (_vm->_map->hasBigTiles())
752 			*(obj.pPosY) = ((obj.goblinY + 1) * _vm->_map->getTilesHeight()) -
753 				(_vm->_scenery->_animBottom - _vm->_scenery->_animTop) -
754 				((obj.goblinY + 1) / 2);
755 		else
756 			*(obj.pPosY) = ((obj.goblinY + 1) * _vm->_map->getTilesHeight()) -
757 				(_vm->_scenery->_animBottom - _vm->_scenery->_animTop);
758 		*(obj.pPosX) = obj.goblinX * _vm->_map->getTilesWidth();
759 		break;
760 
761 	default:
762 		break;
763 	}
764 }
765 
o2_placeGoblin()766 void Inter_v2::o2_placeGoblin() {
767 	int16 index;
768 	int16 x, y;
769 	int16 state;
770 
771 	index = _vm->_game->_script->readValExpr();
772 	x = _vm->_game->_script->readValExpr();
773 	y = _vm->_game->_script->readValExpr();
774 	state = _vm->_game->_script->readValExpr();
775 
776 	_vm->_goblin->placeObject(0, 0, index, x, y, state);
777 }
778 
o2_initScreen()779 void Inter_v2::o2_initScreen() {
780 	int16 offY;
781 	int16 videoMode;
782 	int16 width, height;
783 
784 	offY = _vm->_game->_script->readInt16();
785 
786 	videoMode = offY & 0xFF;
787 	offY = (offY >> 8) & 0xFF;
788 
789 	width = _vm->_game->_script->readValExpr();
790 	height = _vm->_game->_script->readValExpr();
791 
792 	_vm->_video->clearScreen();
793 
794 	// Lost in Time switches to 640x400x16 when showing the title screen
795 	if (_vm->getGameType() == kGameTypeLostInTime) {
796 
797 		if (videoMode == 0x10) {
798 
799 			width = _vm->_width = 640;
800 			height = _vm->_height = 400;
801 			_vm->_global->_colorCount = 16;
802 
803 			_vm->_video->setSize();
804 
805 		} else if (_vm->_global->_videoMode == 0x10) {
806 
807 			if (width == -1)
808 				width = 320;
809 			if (height == -1)
810 				height = 200;
811 
812 			_vm->_width = 320;
813 			_vm->_height = 200;
814 			_vm->_global->_colorCount = 256;
815 
816 			_vm->_video->setSize();
817 
818 		}
819 	}
820 
821 	_vm->_global->_fakeVideoMode = videoMode;
822 
823 	// Some versions require this
824 	if (videoMode == 0xD)
825 		videoMode = _vm->_mode;
826 
827 	if ((videoMode == _vm->_global->_videoMode) && (width == -1))
828 		return;
829 
830 	if (width > 0)
831 		_vm->_video->_surfWidth = width;
832 	if (height > 0)
833 		_vm->_video->_surfHeight = height;
834 
835 	_vm->_video->_splitHeight1 = MIN<int16>(_vm->_height, _vm->_video->_surfHeight - offY);
836 	_vm->_video->_splitHeight2 = offY;
837 	_vm->_video->_splitStart = _vm->_video->_surfHeight - offY;
838 
839 	_vm->_video->_screenDeltaX = 0;
840 	_vm->_video->_screenDeltaY = 0;
841 
842 	_vm->_global->_mouseMinX = 0;
843 	_vm->_global->_mouseMinY = 0;
844 	_vm->_global->_mouseMaxX = _vm->_width;
845 	_vm->_global->_mouseMaxY = _vm->_height - _vm->_video->_splitHeight2 - 1;
846 
847 	_vm->_draw->closeScreen();
848 	_vm->_util->clearPalette();
849 	memset(_vm->_global->_redPalette, 0, 256);
850 	memset(_vm->_global->_greenPalette, 0, 256);
851 	memset(_vm->_global->_bluePalette, 0, 256);
852 
853 	_vm->_global->_videoMode = videoMode;
854 	_vm->_video->initPrimary(videoMode);
855 	WRITE_VAR(15, _vm->_global->_fakeVideoMode);
856 
857 	_vm->_global->_setAllPalette = true;
858 
859 	_vm->_util->setMousePos(_vm->_global->_inter_mouseX,
860 			_vm->_global->_inter_mouseY);
861 	_vm->_util->clearPalette();
862 
863 	_vm->_draw->initScreen();
864 
865 	_vm->_util->setScrollOffset();
866 }
867 
o2_scroll()868 void Inter_v2::o2_scroll() {
869 	int16 startX;
870 	int16 startY;
871 	int16 endX;
872 	int16 endY;
873 	int16 stepX;
874 	int16 stepY;
875 	int16 curX;
876 	int16 curY;
877 
878 	startX = CLIP((int)_vm->_game->_script->readValExpr(), 0,
879 			_vm->_video->_surfWidth - _vm->_width);
880 	startY = CLIP((int)_vm->_game->_script->readValExpr(), 0,
881 			_vm->_video->_surfHeight - _vm->_height);
882 	endX = CLIP((int)_vm->_game->_script->readValExpr(), 0,
883 			_vm->_video->_surfWidth - _vm->_width);
884 	endY = CLIP((int)_vm->_game->_script->readValExpr(), 0,
885 			_vm->_video->_surfHeight - _vm->_height);
886 	stepX = _vm->_game->_script->readValExpr();
887 	stepY = _vm->_game->_script->readValExpr();
888 
889 	curX = startX;
890 	curY = startY;
891 	while (!_vm->shouldQuit() && ((curX != endX) || (curY != endY))) {
892 		curX = stepX > 0 ? MIN(curX + stepX, (int)endX) :
893 			MAX(curX + stepX, (int)endX);
894 		curY = stepY > 0 ? MIN(curY + stepY, (int)endY) :
895 			MAX(curY + stepY, (int)endY);
896 
897 		_vm->_draw->_scrollOffsetX = curX;
898 		_vm->_draw->_scrollOffsetY = curY;
899 		_vm->_util->setScrollOffset();
900 		_vm->_video->dirtyRectsAll();
901 	}
902 }
903 
o2_setScrollOffset()904 void Inter_v2::o2_setScrollOffset() {
905 	int16 offsetX, offsetY;
906 
907 	offsetX = _vm->_game->_script->readValExpr();
908 	offsetY = _vm->_game->_script->readValExpr();
909 
910 	if (offsetX == -1) {
911 		_vm->_game->_preventScroll = !_vm->_game->_preventScroll;
912 
913 		WRITE_VAR(2, _vm->_draw->_scrollOffsetX);
914 		WRITE_VAR(3, _vm->_draw->_scrollOffsetY);
915 	} else {
916 		int16 screenW = _vm->_video->_surfWidth;
917 		int16 screenH = _vm->_video->_surfHeight;
918 
919 		if (screenW > _vm->_width)
920 			screenW -= _vm->_width;
921 		if (screenH > _vm->_height)
922 			screenH -= _vm->_height;
923 
924 		_vm->_draw->_scrollOffsetX = CLIP<int16>(offsetX, 0, screenW);
925 		_vm->_draw->_scrollOffsetY = CLIP<int16>(offsetY, 0, screenH);
926 		_vm->_video->dirtyRectsAll();
927 	}
928 
929 	_vm->_util->setScrollOffset();
930 	_noBusyWait = true;
931 }
932 
o2_playImd()933 void Inter_v2::o2_playImd() {
934 	VideoPlayer::Properties props;
935 
936 	Common::String imd = _vm->_game->_script->evalString();
937 	if (imd.size() > 8)
938 		imd = Common::String(imd.c_str(), 8);
939 
940 	props.x          = _vm->_game->_script->readValExpr();
941 	props.y          = _vm->_game->_script->readValExpr();
942 	props.startFrame = _vm->_game->_script->readValExpr();
943 	props.lastFrame  = _vm->_game->_script->readValExpr();
944 	props.breakKey   = _vm->_game->_script->readValExpr();
945 	props.flags      = _vm->_game->_script->readValExpr();
946 	props.palStart   = _vm->_game->_script->readValExpr();
947 	props.palEnd     = _vm->_game->_script->readValExpr();
948 	props.palCmd     = 1 << (props.flags & 0x3F);
949 
950 	debugC(1, kDebugVideo, "Playing video \"%s\" @ %d+%d, frames %d - %d, "
951 			"paletteCmd %d (%d - %d), flags %X", imd.c_str(),
952 			props.x, props.y, props.startFrame, props.lastFrame,
953 			props.palCmd, props.palStart, props.palEnd, props.flags);
954 
955 	int slot = 0;
956 	if (!imd.empty()) {
957 		_vm->_vidPlayer->evaluateFlags(props);
958 		if ((slot = _vm->_vidPlayer->openVideo(true, imd, props)) < 0) {
959 			WRITE_VAR(11, (uint32) -1);
960 			return;
961 		}
962 	}
963 
964 	bool close = (props.lastFrame == -1);
965 	if (props.startFrame == -2) {
966 		props.startFrame = 0;
967 		props.lastFrame  = 0;
968 		close = false;
969 	}
970 
971 	if (props.startFrame >= 0)
972 		_vm->_vidPlayer->play(slot, props);
973 
974 	if (close)
975 		_vm->_vidPlayer->closeVideo(slot);
976 }
977 
o2_getImdInfo()978 void Inter_v2::o2_getImdInfo() {
979 	Common::String imd = _vm->_game->_script->evalString();
980 
981 	int16 varX      = _vm->_game->_script->readVarIndex();
982 	int16 varY      = _vm->_game->_script->readVarIndex();
983 	int16 varFrames = _vm->_game->_script->readVarIndex();
984 	int16 varWidth  = _vm->_game->_script->readVarIndex();
985 	int16 varHeight = _vm->_game->_script->readVarIndex();
986 
987 	// WORKAROUND: The nut rolling animation in the administration center
988 	// in Woodruff is called "noixroul", but the scripts think it's "noixroule".
989 	if ((_vm->getGameType() == kGameTypeWoodruff) &&
990 	    imd.equalsIgnoreCase("noixroule"))
991 		imd = "noixroul";
992 
993 	_vm->_vidPlayer->writeVideoInfo(imd, varX, varY, varFrames, varWidth, varHeight);
994 }
995 
o2_openItk()996 void Inter_v2::o2_openItk() {
997 	Common::String file = _vm->_game->_script->evalString();
998 	if (!file.contains('.'))
999 		file += ".ITK";
1000 
1001 	_vm->_dataIO->openArchive(file, false);
1002 }
1003 
o2_closeItk()1004 void Inter_v2::o2_closeItk() {
1005 	_vm->_dataIO->closeArchive(false);
1006 
1007 	// NOTE: Lost in Time might close a data file without explicitely closing a video in it.
1008 	//       So we make sure that all open videos are still available.
1009 		_vm->_vidPlayer->reopenAll();
1010 }
1011 
o2_setImdFrontSurf()1012 void Inter_v2::o2_setImdFrontSurf() {
1013 }
1014 
o2_resetImdFrontSurf()1015 void Inter_v2::o2_resetImdFrontSurf() {
1016 }
1017 
o2_assign(OpFuncParams & params)1018 void Inter_v2::o2_assign(OpFuncParams &params) {
1019 	byte destType = _vm->_game->_script->peekByte();
1020 	int16 dest = _vm->_game->_script->readVarIndex();
1021 
1022 	byte loopCount;
1023 	if (_vm->_game->_script->peekByte() == 99) {
1024 		_vm->_game->_script->skip(1);
1025 		loopCount = _vm->_game->_script->readByte();
1026 	} else
1027 		loopCount = 1;
1028 
1029 	for (int i = 0; i < loopCount; i++) {
1030 		int16 result;
1031 		int16 srcType = _vm->_game->_script->evalExpr(&result);
1032 
1033 		switch (destType) {
1034 		case TYPE_VAR_INT8:
1035 		case TYPE_ARRAY_INT8:
1036 			WRITE_VARO_UINT8(dest + i, _vm->_game->_script->getResultInt());
1037 			break;
1038 
1039 		case TYPE_VAR_INT16:
1040 		case TYPE_ARRAY_INT16:
1041 			WRITE_VARO_UINT16(dest + i * 2, _vm->_game->_script->getResultInt());
1042 			break;
1043 
1044 		case TYPE_VAR_INT32:
1045 		case TYPE_ARRAY_INT32:
1046 			WRITE_VAR_OFFSET(dest + i * 4, _vm->_game->_script->getResultInt());
1047 			break;
1048 
1049 		case TYPE_VAR_INT32_AS_INT16:
1050 			WRITE_VARO_UINT16(dest + i * 4, _vm->_game->_script->getResultInt());
1051 			break;
1052 
1053 		case TYPE_VAR_STR:
1054 		case TYPE_ARRAY_STR:
1055 			if (srcType == TYPE_IMM_INT16)
1056 				WRITE_VARO_UINT8(dest, result);
1057 			else
1058 				WRITE_VARO_STR(dest, _vm->_game->_script->getResultStr());
1059 			break;
1060 
1061 		default:
1062 			break;
1063 		}
1064 	}
1065 }
1066 
o2_printText(OpFuncParams & params)1067 void Inter_v2::o2_printText(OpFuncParams &params) {
1068 	char buf[60];
1069 	int i;
1070 
1071 	_vm->_draw->_destSpriteX = _vm->_game->_script->readValExpr();
1072 	_vm->_draw->_destSpriteY = _vm->_game->_script->readValExpr();
1073 
1074 	_vm->_draw->_backColor = _vm->_game->_script->readValExpr();
1075 	_vm->_draw->_frontColor = _vm->_game->_script->readValExpr();
1076 	_vm->_draw->_fontIndex = _vm->_game->_script->readValExpr();
1077 	_vm->_draw->_destSurface = Draw::kBackSurface;
1078 	_vm->_draw->_textToPrint = buf;
1079 	_vm->_draw->_transparency = 0;
1080 
1081 	SurfacePtr surface = _vm->_draw->_spritesArray[_vm->_draw->_destSurface];
1082 	uint16 destWidth  = surface ? surface->getWidth()  : 0;
1083 	uint16 destHeight = surface ? surface->getHeight() : 0;
1084 
1085 	if (_vm->_draw->_backColor == 16) {
1086 		_vm->_draw->_backColor = 0;
1087 		_vm->_draw->_transparency = 1;
1088 	}
1089 
1090 	do {
1091 		for (i = 0; (_vm->_game->_script->peekChar() != '.') &&
1092 				(_vm->_game->_script->peekByte() != 200); i++) {
1093 			buf[i] = _vm->_game->_script->readChar();
1094 		}
1095 
1096 		if (_vm->_game->_script->peekByte() != 200) {
1097 			_vm->_game->_script->skip(1);
1098 			switch (_vm->_game->_script->peekByte()) {
1099 			case TYPE_VAR_INT8:
1100 			case TYPE_ARRAY_INT8:
1101 				sprintf(buf + i, "%d",
1102 						(int8) READ_VARO_UINT8(_vm->_game->_script->readVarIndex()));
1103 				break;
1104 
1105 			case TYPE_VAR_INT16:
1106 			case TYPE_VAR_INT32_AS_INT16:
1107 			case TYPE_ARRAY_INT16:
1108 				sprintf(buf + i, "%d",
1109 						(int16) READ_VARO_UINT16(_vm->_game->_script->readVarIndex()));
1110 				break;
1111 
1112 			case TYPE_VAR_INT32:
1113 			case TYPE_ARRAY_INT32:
1114 				sprintf(buf + i, "%d",
1115 						(int32)VAR_OFFSET(_vm->_game->_script->readVarIndex()));
1116 				break;
1117 
1118 			case TYPE_VAR_STR:
1119 			case TYPE_ARRAY_STR:
1120 				sprintf(buf + i, "%s",
1121 						GET_VARO_STR(_vm->_game->_script->readVarIndex()));
1122 				break;
1123 
1124 			default:
1125 				break;
1126 			}
1127 			_vm->_game->_script->skip(1);
1128 		} else
1129 			buf[i] = 0;
1130 
1131 		if ((_vm->_draw->_destSpriteX < destWidth) &&
1132 		    (_vm->_draw->_destSpriteY < destHeight))
1133 			_vm->_draw->spriteOperation(DRAW_PRINTTEXT);
1134 
1135 	} while (_vm->_game->_script->peekByte() != 200);
1136 
1137 	_vm->_game->_script->skip(1);
1138 }
1139 
o2_animPalInit(OpFuncParams & params)1140 void Inter_v2::o2_animPalInit(OpFuncParams &params) {
1141 	int16 index;
1142 
1143 	index = _vm->_game->_script->readInt16();
1144 	if (index > 0) {
1145 		index--;
1146 		_animPalLowIndex[index] = _vm->_game->_script->readValExpr();
1147 		_animPalHighIndex[index] = _vm->_game->_script->readValExpr();
1148 		_animPalDir[index] = 1;
1149 	} else if (index == 0) {
1150 		memset(_animPalDir, 0, 8 * sizeof(int16));
1151 		_vm->_game->_script->readValExpr();
1152 		_vm->_game->_script->readValExpr();
1153 	} else {
1154 		index = -index - 1;
1155 		_animPalLowIndex[index] = _vm->_game->_script->readValExpr();
1156 		_animPalHighIndex[index] = _vm->_game->_script->readValExpr();
1157 		_animPalDir[index] = -1;
1158 	}
1159 }
1160 
o2_addHotspot(OpFuncParams & params)1161 void Inter_v2::o2_addHotspot(OpFuncParams &params) {
1162 	int16 id       = _vm->_game->_script->readValExpr();
1163 	uint16 funcPos = _vm->_game->_script->pos();
1164 	int16 left     = _vm->_game->_script->readValExpr();
1165 	int16 top      = _vm->_game->_script->readValExpr();
1166 	uint16 width   = _vm->_game->_script->readValExpr();
1167 	uint16 height  = _vm->_game->_script->readValExpr();
1168 	uint16 flags   = _vm->_game->_script->readValExpr();
1169 	uint16 key     = _vm->_game->_script->readInt16();
1170 
1171 	if (key == 0)
1172 		key = ABS(id) + 41960;
1173 
1174 	if (left < 0) {
1175 		width += left;
1176 		left   = 0;
1177 	}
1178 
1179 	if (top < 0) {
1180 		height += top;
1181 		top     = 0;
1182 	}
1183 
1184 	if (id < 0)
1185 		_vm->_game->_hotspots->add(0xD000 - id, left & 0xFFFC, top & 0xFFFC,
1186 				left + width + 3, top + height + 3, flags, key, 0, 0, funcPos);
1187 	else
1188 		_vm->_game->_hotspots->add(0xE000 + id, left, top,
1189 				left + width - 1, top + height - 1, flags, key, 0, 0, funcPos);
1190 }
1191 
o2_removeHotspot(OpFuncParams & params)1192 void Inter_v2::o2_removeHotspot(OpFuncParams &params) {
1193 	int16 id = _vm->_game->_script->readValExpr();
1194 	uint8 stateType1 = Hotspots::kStateFilledDisabled | Hotspots::kStateType1;
1195 	uint8 stateType2 = Hotspots::kStateFilledDisabled | Hotspots::kStateType2;
1196 
1197 	if      (id == -2)
1198 		_vm->_game->_hotspots->removeState(stateType1);
1199 	else if (id == -1)
1200 		_vm->_game->_hotspots->removeState(stateType2);
1201 	else
1202 		_vm->_game->_hotspots->remove((stateType2 << 12) + id);
1203 }
1204 
o2_getTotTextItemPart(OpFuncParams & params)1205 void Inter_v2::o2_getTotTextItemPart(OpFuncParams &params) {
1206 	byte *totData;
1207 	int16 totTextItem;
1208 	int16 part, curPart = 0;
1209 	int16 offX = 0, offY = 0;
1210 	int16 collId = 0, collCmd;
1211 	uint32 stringStartVar, stringVar;
1212 	bool end;
1213 
1214 	totTextItem = _vm->_game->_script->readInt16();
1215 	stringStartVar = _vm->_game->_script->readVarIndex();
1216 	part = _vm->_game->_script->readValExpr();
1217 
1218 	stringVar = stringStartVar;
1219 	if (part == -1) {
1220 		warning("o2_getTotTextItemPart, part == -1");
1221 		_vm->_draw->_hotspotText = GET_VARO_STR(stringVar);
1222 	}
1223 
1224 	WRITE_VARO_UINT8(stringVar, 0);
1225 
1226 	TextItem *textItem = _vm->_game->_resources->getTextItem(totTextItem);
1227 	if (!textItem)
1228 		return;
1229 
1230 	totData = textItem->getData();
1231 
1232 	// Skip background rectangles
1233 	while (((int16) READ_LE_UINT16(totData)) != -1)
1234 		totData += 9;
1235 	totData += 2;
1236 
1237 	while (*totData != 1) {
1238 		switch (*totData) {
1239 		case 2:
1240 		case 5:
1241 			totData++;
1242 			offX = READ_LE_UINT16(totData);
1243 			offY = READ_LE_UINT16(totData + 2);
1244 			totData += 4;
1245 			break;
1246 
1247 		case 3:
1248 		case 4:
1249 			totData += 2;
1250 			break;
1251 
1252 		case 6:
1253 			totData++;
1254 
1255 			collCmd = *totData++;
1256 			if (collCmd & 0x80) {
1257 				collId = READ_LE_UINT16(totData);
1258 				totData += 2;
1259 			}
1260 
1261 			// Skip collision coordinates
1262 			if (collCmd & 0x40)
1263 				totData += 8;
1264 
1265 			if ((collCmd & 0x8F) && ((-collId - 1) == part)) {
1266 				int n = 0;
1267 
1268 				while (1) {
1269 					if ((*totData < 1) || (*totData > 7)) {
1270 						if (*totData >= 32) {
1271 							WRITE_VARO_UINT8(stringVar++, *totData++);
1272 							n++;
1273 						} else
1274 							totData++;
1275 						continue;
1276 					}
1277 
1278 					if ((n != 0) || (*totData == 1) ||
1279 							(*totData == 6) || (*totData == 7)) {
1280 						WRITE_VARO_UINT8(stringVar, 0);
1281 						delete textItem;
1282 						return;
1283 					}
1284 
1285 					switch (*totData) {
1286 					case 2:
1287 					case 5:
1288 						totData += 5;
1289 						break;
1290 
1291 					case 3:
1292 					case 4:
1293 						totData += 2;
1294 						break;
1295 
1296 					default:
1297 						break;
1298 					}
1299 				}
1300 
1301 			}
1302 			break;
1303 
1304 		case 7:
1305 		case 8:
1306 		case 9:
1307 			totData++;
1308 			break;
1309 
1310 		case 10:
1311 			if (curPart == part) {
1312 				WRITE_VARO_UINT8(stringVar++, 0xFF);
1313 				WRITE_VARO_UINT16(stringVar, offX);
1314 				WRITE_VARO_UINT16(stringVar + 2, offY);
1315 				WRITE_VARO_UINT16(stringVar + 4,
1316 						totData - _vm->_game->_resources->getTexts());
1317 				WRITE_VARO_UINT8(stringVar + 6, 0);
1318 				delete textItem;
1319 				return;
1320 			}
1321 
1322 			end = false;
1323 			while (!end) {
1324 				switch (*totData) {
1325 				case 2:
1326 				case 5:
1327 					if (ABS(offY - READ_LE_UINT16(totData + 3)) > 1)
1328 						end = true;
1329 					else
1330 						totData += 5;
1331 					break;
1332 
1333 				case 3:
1334 					totData += 2;
1335 					break;
1336 
1337 				case 10:
1338 					totData += totData[1] * 2 + 2;
1339 					break;
1340 
1341 				default:
1342 					if (*totData < 32)
1343 						end = true;
1344 					while (*totData >= 32)
1345 						totData++;
1346 					break;
1347 				}
1348 			}
1349 
1350 			if (part >= 0)
1351 				curPart++;
1352 			break;
1353 
1354 		default:
1355 			while (1) {
1356 
1357 				while (*totData >= 32)
1358 					WRITE_VARO_UINT8(stringVar++, *totData++);
1359 				WRITE_VARO_UINT8(stringVar, 0);
1360 
1361 				if (((*totData != 2) && (*totData != 5)) ||
1362 						(ABS(offY - READ_LE_UINT16(totData + 3)) > 1)) {
1363 
1364 					if (curPart == part) {
1365 						delete textItem;
1366 						return;
1367 					}
1368 
1369 					stringVar = stringStartVar;
1370 					WRITE_VARO_UINT8(stringVar, 0);
1371 
1372 					while (*totData >= 32)
1373 						totData++;
1374 
1375 					if (part >= 0)
1376 						curPart++;
1377 					break;
1378 
1379 				} else
1380 					totData += 5;
1381 
1382 			}
1383 			break;
1384 		}
1385 	}
1386 
1387 	delete textItem;
1388 }
1389 
o2_goblinFunc(OpFuncParams & params)1390 void Inter_v2::o2_goblinFunc(OpFuncParams &params) {
1391 	OpGobParams gobParams;
1392 	int16 cmd;
1393 
1394 	cmd = _vm->_game->_script->readInt16();
1395 
1396 	gobParams.paramCount = _vm->_game->_script->readInt16();
1397 	gobParams.extraData = cmd;
1398 
1399 	if (cmd != 101)
1400 		executeOpcodeGob(cmd, gobParams);
1401 }
1402 
o2_stopSound(OpFuncParams & params)1403 void Inter_v2::o2_stopSound(OpFuncParams &params) {
1404 	int16 expr;
1405 
1406 	expr = _vm->_game->_script->readValExpr();
1407 
1408 	if (expr < 0) {
1409 		_vm->_sound->adlibStop();
1410 	} else
1411 		_vm->_sound->blasterStop(expr);
1412 
1413 	_soundEndTimeKey = 0;
1414 }
1415 
o2_loadSound(OpFuncParams & params)1416 void Inter_v2::o2_loadSound(OpFuncParams &params) {
1417 	loadSound(0);
1418 }
1419 
o2_getFreeMem(OpFuncParams & params)1420 void Inter_v2::o2_getFreeMem(OpFuncParams &params) {
1421 	uint16 freeVar;
1422 	uint16 maxFreeVar;
1423 
1424 	freeVar    = _vm->_game->_script->readVarIndex();
1425 	maxFreeVar = _vm->_game->_script->readVarIndex();
1426 
1427 	// HACK
1428 	WRITE_VAR_OFFSET(freeVar   , 1000000);
1429 	WRITE_VAR_OFFSET(maxFreeVar, 1000000);
1430 	WRITE_VAR(16, _vm->_game->_script->getVariablesCount() * 4);
1431 }
1432 
o2_checkData(OpFuncParams & params)1433 void Inter_v2::o2_checkData(OpFuncParams &params) {
1434 	Common::String file = _vm->_game->_script->evalString();
1435 	int16 varOff = _vm->_game->_script->readVarIndex();
1436 
1437 	// WORKAROUND: For some reason, the variable indicating which TOT to load next
1438 	// is overwritten in the guard house card game in Woodruff.
1439 	if ((_vm->getGameType() == kGameTypeWoodruff) && file.equalsIgnoreCase("6.tot"))
1440 		file = "EMAP2011.TOT";
1441 
1442 	int32 size = -1;
1443 	SaveLoad::SaveMode mode = _vm->_saveLoad ? _vm->_saveLoad->getSaveMode(file.c_str()) : SaveLoad::kSaveModeNone;
1444 	if (mode == SaveLoad::kSaveModeNone) {
1445 
1446 		size = _vm->_dataIO->fileSize(file);
1447 		if (size == -1)
1448 			warning("File \"%s\" not found", file.c_str());
1449 
1450 	} else if (mode == SaveLoad::kSaveModeSave)
1451 		size = _vm->_saveLoad->getSize(file.c_str());
1452 	else if (mode == SaveLoad::kSaveModeExists)
1453 		size = 23;
1454 
1455 	debugC(2, kDebugFileIO, "Requested size of file \"%s\": %d", file.c_str(), size);
1456 
1457 	WRITE_VAR_OFFSET(varOff, (size == -1) ? -1 : 50);
1458 	WRITE_VAR(16, (uint32) size);
1459 }
1460 
o2_readData(OpFuncParams & params)1461 void Inter_v2::o2_readData(OpFuncParams &params) {
1462 	const char *file = _vm->_game->_script->evalString();
1463 
1464 	uint16 dataVar = _vm->_game->_script->readVarIndex();
1465 	int32  size    = _vm->_game->_script->readValExpr();
1466 	int32  offset  = _vm->_game->_script->evalInt();
1467 	int32  retSize = 0;
1468 
1469 	debugC(2, kDebugFileIO, "Read from file \"%s\" (%d, %d bytes at %d)",
1470 			file, dataVar, size, offset);
1471 
1472 	SaveLoad::SaveMode mode = _vm->_saveLoad ? _vm->_saveLoad->getSaveMode(file) : SaveLoad::kSaveModeNone;
1473 	if (mode == SaveLoad::kSaveModeSave) {
1474 
1475 		WRITE_VAR(1, 1);
1476 
1477 		if (!_vm->_saveLoad->load(file, dataVar, size, offset)) {
1478 
1479 			GUI::MessageDialog dialog(_("Failed to load saved game from file."));
1480 			dialog.runModal();
1481 
1482 		} else
1483 			WRITE_VAR(1, 0);
1484 
1485 		return;
1486 
1487 	} else if (mode == SaveLoad::kSaveModeIgnore)
1488 		return;
1489 
1490 	if (size < 0) {
1491 		warning("Attempted to read a raw sprite from file \"%s\"",
1492 				file);
1493 		return;
1494 	} else if (size == 0) {
1495 		dataVar = 0;
1496 		size = _vm->_game->_script->getVariablesCount() * 4;
1497 	}
1498 
1499 	byte *buf = _variables->getAddressOff8(dataVar);
1500 
1501 	if (file[0] == 0) {
1502 		WRITE_VAR(1, size);
1503 		return;
1504 	}
1505 
1506 	WRITE_VAR(1, 1);
1507 	Common::SeekableReadStream *stream = _vm->_dataIO->getFile(file);
1508 	if (!stream)
1509 		return;
1510 
1511 	_vm->_draw->animateCursor(4);
1512 	if (offset < 0)
1513 		stream->seek(offset + 1, SEEK_END);
1514 	else
1515 		stream->seek(offset);
1516 
1517 	if (((dataVar >> 2) == 59) && (size == 4)) {
1518 		WRITE_VAR(59, stream->readUint32LE());
1519 		// The scripts in some versions divide through 256^3 then,
1520 		// effectively doing a LE->BE conversion
1521 		if ((_vm->getPlatform() != Common::kPlatformDOS) && (VAR(59) < 256))
1522 			WRITE_VAR(59, SWAP_BYTES_32(VAR(59)));
1523 	} else
1524 		retSize = stream->read(buf, size);
1525 
1526 	if (retSize == size)
1527 		WRITE_VAR(1, 0);
1528 
1529 	delete stream;
1530 }
1531 
o2_writeData(OpFuncParams & params)1532 void Inter_v2::o2_writeData(OpFuncParams &params) {
1533 	const char *file = _vm->_game->_script->evalString();
1534 
1535 	int16 dataVar = _vm->_game->_script->readVarIndex();
1536 	int32 size    = _vm->_game->_script->readValExpr();
1537 	int32 offset  = _vm->_game->_script->evalInt();
1538 
1539 	debugC(2, kDebugFileIO, "Write to file \"%s\" (%d, %d bytes at %d)",
1540 			file, dataVar, size, offset);
1541 
1542 	WRITE_VAR(1, 1);
1543 
1544 	SaveLoad::SaveMode mode = _vm->_saveLoad ? _vm->_saveLoad->getSaveMode(file) : SaveLoad::kSaveModeNone;
1545 	if (mode == SaveLoad::kSaveModeSave) {
1546 
1547 		if (!_vm->_saveLoad->save(file, dataVar, size, offset)) {
1548 
1549 			GUI::MessageDialog dialog(_("Failed to save game to file."));
1550 			dialog.runModal();
1551 
1552 		} else
1553 			WRITE_VAR(1, 0);
1554 
1555 	} else if (mode == SaveLoad::kSaveModeIgnore)
1556 		return;
1557 	else if (mode == SaveLoad::kSaveModeNone)
1558 		warning("Attempted to write to file \"%s\"", file);
1559 }
1560 
o2_loadInfogramesIns(OpGobParams & params)1561 void Inter_v2::o2_loadInfogramesIns(OpGobParams &params) {
1562 	int16 varName;
1563 	char fileName[20];
1564 
1565 	varName = _vm->_game->_script->readInt16();
1566 
1567 	Common::strlcpy(fileName, GET_VAR_STR(varName), 16);
1568 	strcat(fileName, ".INS");
1569 
1570 	_vm->_sound->infogramesLoadInstruments(fileName);
1571 }
1572 
o2_playInfogrames(OpGobParams & params)1573 void Inter_v2::o2_playInfogrames(OpGobParams &params) {
1574 	int16 varName;
1575 	char fileName[20];
1576 
1577 	varName = _vm->_game->_script->readInt16();
1578 
1579 	Common::strlcpy(fileName, GET_VAR_STR(varName), 16);
1580 	strcat(fileName, ".DUM");
1581 
1582 	_vm->_sound->infogramesLoadSong(fileName);
1583 	_vm->_sound->infogramesPlay();
1584 }
1585 
o2_startInfogrames(OpGobParams & params)1586 void Inter_v2::o2_startInfogrames(OpGobParams &params) {
1587 	_vm->_game->_script->readInt16();
1588 
1589 	_vm->_sound->infogramesPlay();
1590 }
1591 
o2_stopInfogrames(OpGobParams & params)1592 void Inter_v2::o2_stopInfogrames(OpGobParams &params) {
1593 	_vm->_game->_script->readInt16();
1594 
1595 	_vm->_sound->infogramesStop();
1596 }
1597 
o2_playProtracker(OpGobParams & params)1598 void Inter_v2::o2_playProtracker(OpGobParams &params) {
1599 	_vm->_sound->protrackerPlay("mod.babayaga");
1600 }
1601 
o2_stopProtracker(OpGobParams & params)1602 void Inter_v2::o2_stopProtracker(OpGobParams &params) {
1603 	_vm->_sound->protrackerStop();
1604 }
1605 
o2_handleGoblins(OpGobParams & params)1606 void Inter_v2::o2_handleGoblins(OpGobParams &params) {
1607 	_vm->_goblin->_gob1NoTurn = VAR(_vm->_game->_script->readInt16()) != 0;
1608 	_vm->_goblin->_gob2NoTurn = VAR(_vm->_game->_script->readInt16()) != 0;
1609 	_vm->_goblin->_gob1RelaxTimeVar = _vm->_game->_script->readInt16();
1610 	_vm->_goblin->_gob2RelaxTimeVar = _vm->_game->_script->readInt16();
1611 	_vm->_goblin->_gob1Busy = VAR(_vm->_game->_script->readInt16()) != 0;
1612 	_vm->_goblin->_gob2Busy = VAR(_vm->_game->_script->readInt16()) != 0;
1613 	_vm->_goblin->handleGoblins();
1614 }
1615 
loadSound(int16 search)1616 int16 Inter_v2::loadSound(int16 search) {
1617 	int16 id;
1618 	int16 slot;
1619 	uint16 slotIdMask;
1620 	SoundType type;
1621 
1622 	type = SOUND_SND;
1623 	slotIdMask = 0;
1624 
1625 	if (!search) {
1626 		slot = _vm->_game->_script->readValExpr();
1627 		if (slot < 0) {
1628 			type = SOUND_ADL;
1629 			slot = -slot;
1630 		}
1631 		id = _vm->_game->_script->readInt16();
1632 	} else {
1633 		id = _vm->_game->_script->readInt16();
1634 
1635 		for (slot = 0; slot < Sound::kSoundsCount; slot++)
1636 			if (_vm->_sound->sampleGetBySlot(slot)->isId(id)) {
1637 				slotIdMask = 0x8000;
1638 				break;
1639 			}
1640 
1641 		if (slot == Sound::kSoundsCount) {
1642 			for (slot = (Sound::kSoundsCount - 1); slot >= 0; slot--) {
1643 				if (_vm->_sound->sampleGetBySlot(slot)->empty())
1644 					break;
1645 			}
1646 
1647 			if (slot == -1) {
1648 				warning("Inter_v2::loadSound(): No free slot to load sound "
1649 						"(id = %d)", id);
1650 				return 0;
1651 			}
1652 		}
1653 	}
1654 
1655 	SoundDesc *sample = _vm->_sound->sampleGetBySlot(slot);
1656 
1657 	_vm->_sound->sampleFree(sample, true, slot);
1658 
1659 	if (id == -1) {
1660 		char sndfile[14];
1661 
1662 		Common::strlcpy(sndfile, _vm->_game->_script->readString(9), 10);
1663 
1664 		if (type == SOUND_ADL)
1665 			strcat(sndfile, ".ADL");
1666 		else
1667 			strcat(sndfile, ".SND");
1668 
1669 		int32 dataSize;
1670 		byte *dataPtr = _vm->_dataIO->getFile(sndfile, dataSize);
1671 		if (!dataPtr)
1672 			return 0;
1673 
1674 		if (!sample->load(type, dataPtr, dataSize)) {
1675 			delete[] dataPtr;
1676 			return 0;
1677 		}
1678 
1679 		sample->_id = id;
1680 		return slot | slotIdMask;
1681 	}
1682 
1683 	Resource *resource = _vm->_game->_resources->getResource(id);
1684 	if (!resource)
1685 		return 0;
1686 
1687 	if (!sample->load(type, resource)) {
1688 		delete resource;
1689 		return 0;
1690 	}
1691 
1692 	sample->_id = id;
1693 	return slot | slotIdMask;
1694 }
1695 
animPalette()1696 void Inter_v2::animPalette() {
1697 	int16 i;
1698 	int16 j;
1699 	Video::Color col;
1700 	bool first;
1701 
1702 	first = true;
1703 	for (j = 0; j < 8; j ++) {
1704 		if (_animPalDir[j] == 0)
1705 			continue;
1706 
1707 		if (first) {
1708 			_vm->_video->waitRetrace();
1709 			first = false;
1710 		}
1711 
1712 		if (_animPalDir[j] == -1) {
1713 			col = _vm->_global->_pPaletteDesc->vgaPal[_animPalLowIndex[j]];
1714 
1715 			for (i = _animPalLowIndex[j]; i < _animPalHighIndex[j]; i++)
1716 				_vm->_draw->_vgaPalette[i] = _vm->_draw->_vgaPalette[i + 1];
1717 
1718 			_vm->_global->_pPaletteDesc->vgaPal[_animPalHighIndex[j]] = col;
1719 		} else {
1720 			col = _vm->_global->_pPaletteDesc->vgaPal[_animPalHighIndex[j]];
1721 			for (i = _animPalHighIndex[j]; i > _animPalLowIndex[j]; i--)
1722 				_vm->_draw->_vgaPalette[i] = _vm->_draw->_vgaPalette[i - 1];
1723 
1724 			_vm->_global->_pPaletteDesc->vgaPal[_animPalLowIndex[j]] = col;
1725 		}
1726 		_vm->_global->_pPaletteDesc->vgaPal = _vm->_draw->_vgaPalette;
1727 	}
1728 	if (!first)
1729 		_vm->_video->setFullPalette(_vm->_global->_pPaletteDesc);
1730 }
1731 
1732 } // End of namespace Gob
1733