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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
1417 loadSound(0);
1418 }
1419
o2_getFreeMem(OpFuncParams & params)1420 void Inter_v2::o2_getFreeMem(OpFuncParams ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
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 ¶ms) {
1587 _vm->_game->_script->readInt16();
1588
1589 _vm->_sound->infogramesPlay();
1590 }
1591
o2_stopInfogrames(OpGobParams & params)1592 void Inter_v2::o2_stopInfogrames(OpGobParams ¶ms) {
1593 _vm->_game->_script->readInt16();
1594
1595 _vm->_sound->infogramesStop();
1596 }
1597
o2_playProtracker(OpGobParams & params)1598 void Inter_v2::o2_playProtracker(OpGobParams ¶ms) {
1599 _vm->_sound->protrackerPlay("mod.babayaga");
1600 }
1601
o2_stopProtracker(OpGobParams & params)1602 void Inter_v2::o2_stopProtracker(OpGobParams ¶ms) {
1603 _vm->_sound->protrackerStop();
1604 }
1605
o2_handleGoblins(OpGobParams & params)1606 void Inter_v2::o2_handleGoblins(OpGobParams ¶ms) {
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