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/system.h"
24 #include "common/localization.h"
25
26 #include "graphics/palette.h"
27
28 #include "agos/agos.h"
29 #include "agos/intern.h"
30 #include "agos/sound.h"
31
32 namespace AGOS {
33
34 #define OPCODE(x) _OPCODE(AGOSEngine_Simon1, x)
35
setupOpcodes()36 void AGOSEngine_Simon1::setupOpcodes() {
37 static const OpcodeEntrySimon1 opcodes[] = {
38 /* 00 */
39 OPCODE(o_invalid),
40 OPCODE(o_at),
41 OPCODE(o_notAt),
42 OPCODE(o_invalid),
43 /* 04 */
44 OPCODE(o_invalid),
45 OPCODE(o_carried),
46 OPCODE(o_notCarried),
47 OPCODE(o_isAt),
48 /* 08 */
49 OPCODE(o_invalid),
50 OPCODE(o_invalid),
51 OPCODE(o_invalid),
52 OPCODE(o_zero),
53 /* 12 */
54 OPCODE(o_notZero),
55 OPCODE(o_eq),
56 OPCODE(o_notEq),
57 OPCODE(o_gt),
58 /* 16 */
59 OPCODE(o_lt),
60 OPCODE(o_eqf),
61 OPCODE(o_notEqf),
62 OPCODE(o_ltf),
63 /* 20 */
64 OPCODE(o_gtf),
65 OPCODE(o_invalid),
66 OPCODE(o_invalid),
67 OPCODE(o_chance),
68 /* 24 */
69 OPCODE(o_invalid),
70 OPCODE(o_isRoom),
71 OPCODE(o_isObject),
72 OPCODE(o_state),
73 /* 28 */
74 OPCODE(o_oflag),
75 OPCODE(o_invalid),
76 OPCODE(o_invalid),
77 OPCODE(o_destroy),
78 /* 32 */
79 OPCODE(o_invalid),
80 OPCODE(o_place),
81 OPCODE(o_invalid),
82 OPCODE(o_invalid),
83 /* 36 */
84 OPCODE(o_copyff),
85 OPCODE(o_invalid),
86 OPCODE(o_invalid),
87 OPCODE(o_invalid),
88 /* 40 */
89 OPCODE(o_invalid),
90 OPCODE(o_clear),
91 OPCODE(o_let),
92 OPCODE(o_add),
93 /* 44 */
94 OPCODE(o_sub),
95 OPCODE(o_addf),
96 OPCODE(o_subf),
97 OPCODE(o_mul),
98 /* 48 */
99 OPCODE(o_div),
100 OPCODE(o_mulf),
101 OPCODE(o_divf),
102 OPCODE(o_mod),
103 /* 52 */
104 OPCODE(o_modf),
105 OPCODE(o_random),
106 OPCODE(o_invalid),
107 OPCODE(o_goto),
108 /* 56 */
109 OPCODE(o_oset),
110 OPCODE(o_oclear),
111 OPCODE(o_putBy),
112 OPCODE(o_inc),
113 /* 60 */
114 OPCODE(o_dec),
115 OPCODE(o_setState),
116 OPCODE(o_print),
117 OPCODE(o_message),
118 /* 64 */
119 OPCODE(o_msg),
120 OPCODE(oww_addTextBox),
121 OPCODE(oww_setShortText),
122 OPCODE(oww_setLongText),
123 /* 68 */
124 OPCODE(o_end),
125 OPCODE(o_done),
126 OPCODE(oww_printLongText),
127 OPCODE(o_process),
128 /* 72 */
129 OPCODE(o_invalid),
130 OPCODE(o_invalid),
131 OPCODE(o_invalid),
132 OPCODE(o_invalid),
133 /* 76 */
134 OPCODE(o_when),
135 OPCODE(o_if1),
136 OPCODE(o_if2),
137 OPCODE(o_isCalled),
138 /* 80 */
139 OPCODE(o_is),
140 OPCODE(o_invalid),
141 OPCODE(o_debug),
142 OPCODE(oe1_rescan),
143 /* 84 */
144 OPCODE(o_invalid),
145 OPCODE(o_invalid),
146 OPCODE(o_invalid),
147 OPCODE(o_comment),
148 /* 88 */
149 OPCODE(o_haltAnimation),
150 OPCODE(o_restartAnimation),
151 OPCODE(o_getParent),
152 OPCODE(o_getNext),
153 /* 92 */
154 OPCODE(o_getChildren),
155 OPCODE(o_invalid),
156 OPCODE(o_invalid),
157 OPCODE(o_invalid),
158 /* 96 */
159 OPCODE(o_picture),
160 OPCODE(o_loadZone),
161 OPCODE(os1_animate),
162 OPCODE(oe1_stopAnimate),
163 /* 100 */
164 OPCODE(o_killAnimate),
165 OPCODE(o_defWindow),
166 OPCODE(o_window),
167 OPCODE(o_cls),
168 /* 104 */
169 OPCODE(o_closeWindow),
170 OPCODE(o_invalid),
171 OPCODE(o_invalid),
172 OPCODE(o_addBox),
173 /* 108 */
174 OPCODE(o_delBox),
175 OPCODE(o_enableBox),
176 OPCODE(o_disableBox),
177 OPCODE(o_moveBox),
178 /* 112 */
179 OPCODE(o_invalid),
180 OPCODE(o_invalid),
181 OPCODE(o_doIcons),
182 OPCODE(o_isClass),
183 /* 116 */
184 OPCODE(o_setClass),
185 OPCODE(o_unsetClass),
186 OPCODE(o_invalid),
187 OPCODE(o_waitSync),
188 /* 120 */
189 OPCODE(o_sync),
190 OPCODE(o_defObj),
191 OPCODE(o_invalid),
192 OPCODE(o_invalid),
193 /* 124 */
194 OPCODE(o_invalid),
195 OPCODE(o_here),
196 OPCODE(o_doClassIcons),
197 OPCODE(o_playTune),
198 /* 128 */
199 OPCODE(o_invalid),
200 OPCODE(o_invalid),
201 OPCODE(o_setAdjNoun),
202 OPCODE(o_invalid),
203 /* 132 */
204 OPCODE(o_saveUserGame),
205 OPCODE(o_loadUserGame),
206 OPCODE(o_invalid),
207 OPCODE(os1_pauseGame),
208 /* 136 */
209 OPCODE(o_copysf),
210 OPCODE(o_restoreIcons),
211 OPCODE(o_freezeZones),
212 OPCODE(o_placeNoIcons),
213 /* 140 */
214 OPCODE(o_clearTimers),
215 OPCODE(o_setDollar),
216 OPCODE(o_isBox),
217 OPCODE(oe2_doTable),
218 /* 144 */
219 OPCODE(o_invalid),
220 OPCODE(o_invalid),
221 OPCODE(o_invalid),
222 OPCODE(o_invalid),
223 /* 148 */
224 OPCODE(o_invalid),
225 OPCODE(o_invalid),
226 OPCODE(o_invalid),
227 OPCODE(oe2_storeItem),
228 /* 152 */
229 OPCODE(oe2_getItem),
230 OPCODE(oe2_bSet),
231 OPCODE(oe2_bClear),
232 OPCODE(oe2_bZero),
233 /* 156 */
234 OPCODE(oe2_bNotZero),
235 OPCODE(oe2_getOValue),
236 OPCODE(oe2_setOValue),
237 OPCODE(o_invalid),
238 /* 160 */
239 OPCODE(oe2_ink),
240 OPCODE(os1_screenTextBox),
241 OPCODE(os1_screenTextMsg),
242 OPCODE(os1_playEffect),
243 /* 164 */
244 OPCODE(oe2_getDollar2),
245 OPCODE(oe2_isAdjNoun),
246 OPCODE(oe2_b2Set),
247 OPCODE(oe2_b2Clear),
248 /* 168 */
249 OPCODE(oe2_b2Zero),
250 OPCODE(oe2_b2NotZero),
251 OPCODE(o_invalid),
252 OPCODE(o_invalid),
253 /* 172 */
254 OPCODE(o_invalid),
255 OPCODE(o_invalid),
256 OPCODE(o_invalid),
257 OPCODE(oww_lockZones),
258 /* 176 */
259 OPCODE(oww_unlockZones),
260 OPCODE(os1_screenTextPObj),
261 OPCODE(os1_getPathPosn),
262 OPCODE(os1_scnTxtLongText),
263 /* 180 */
264 OPCODE(os1_mouseOn),
265 OPCODE(os1_mouseOff),
266 OPCODE(os1_loadBeard),
267 OPCODE(os1_unloadBeard),
268 /* 184 */
269 OPCODE(os1_unloadZone),
270 OPCODE(os1_loadStrings),
271 OPCODE(os1_unfreezeZones),
272 OPCODE(os1_specialFade),
273 };
274
275 _opcodesSimon1 = opcodes;
276 _numOpcodes = 188;
277 }
278
executeOpcode(int opcode)279 void AGOSEngine_Simon1::executeOpcode(int opcode) {
280 OpcodeProcSimon1 op = _opcodesSimon1[opcode].proc;
281 (this->*op) ();
282 }
283
284 // -----------------------------------------------------------------------
285 // Simon 1 Opcodes
286 // -----------------------------------------------------------------------
287
os1_animate()288 void AGOSEngine_Simon1::os1_animate() {
289 // 98: animate
290 uint16 vgaSpriteId = getVarOrWord();
291 uint16 windowNum = getVarOrByte();
292 int16 x = getVarOrWord();
293 int16 y = getVarOrWord();
294 uint16 palette = (getVarOrWord() & 15);
295
296 if (getFeatures() & GF_TALKIE && vgaSpriteId >= 400) {
297 _lastVgaWaitFor = 0;
298 }
299
300 _videoLockOut |= 0x40;
301 animate(windowNum, vgaSpriteId / 100, vgaSpriteId, x, y, palette);
302 _videoLockOut &= ~0x40;
303 }
304
os1_pauseGame()305 void AGOSEngine_Simon1::os1_pauseGame() {
306 // 135: pause game
307 _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
308
309 Common::KeyCode keyYes, keyNo;
310
311 Common::getLanguageYesNo(_language, keyYes, keyNo);
312
313 while (!shouldQuit()) {
314 delay(1);
315 if (_keyPressed.keycode == keyYes)
316 quitGame();
317 else if (_keyPressed.keycode == keyNo)
318 break;
319 }
320
321 _system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
322 }
323
os1_screenTextBox()324 void AGOSEngine_Simon1::os1_screenTextBox() {
325 // 161: setup text
326 TextLocation *tl = getTextLocation(getVarOrByte());
327
328 tl->x = getVarOrWord();
329 tl->y = getVarOrByte();
330 tl->width = getVarOrWord();
331 }
332
os1_screenTextMsg()333 void AGOSEngine_Simon1::os1_screenTextMsg() {
334 // 162: print string
335 uint vgaSpriteId = getVarOrByte();
336 uint color = getVarOrByte();
337 uint stringId = getNextStringID();
338 const byte *stringPtr = NULL;
339 uint speechId = 0;
340 TextLocation *tl;
341
342 if (stringId != 0xFFFF)
343 stringPtr = getStringPtrByID(stringId);
344
345 if (getFeatures() & GF_TALKIE) {
346 if (getGameType() == GType_FF || getGameType() == GType_PP)
347 speechId = (uint16)getVarOrWord();
348 else
349 speechId = (uint16)getNextWord();
350 }
351
352 if (getGameType() == GType_FF || getGameType() == GType_PP)
353 vgaSpriteId = 1;
354
355 tl = getTextLocation(vgaSpriteId);
356 if (_speech && speechId != 0)
357 playSpeech(speechId, vgaSpriteId);
358 if (((getGameType() == GType_SIMON2 && (getFeatures() & GF_TALKIE)) || getGameType() == GType_FF) &&
359 speechId == 0) {
360 stopAnimateSimon2(2, vgaSpriteId + 2);
361 }
362
363 // WORKAROUND: Several strings in the French version of Simon the Sorcerer 1 set the incorrect width,
364 // causing crashes, or glitches in subtitles. See bug #3512776 for example.
365 if (getGameType() == GType_SIMON1 && _language == Common::FR_FRA) {
366 if ((getFeatures() & GF_TALKIE) && stringId == 33219)
367 tl->width = 96;
368 if (!(getFeatures() & GF_TALKIE) && stringId == 33245)
369 tl->width = 96;
370 }
371
372 if (stringPtr != NULL && stringPtr[0] != 0 && (speechId == 0 || _subtitles))
373 printScreenText(vgaSpriteId, color, (const char *)stringPtr, tl->x, tl->y, tl->width);
374
375 }
376
os1_playEffect()377 void AGOSEngine_Simon1::os1_playEffect() {
378 // 163: play sound
379 uint16 soundId = getVarOrWord();
380
381 if (getGameId() == GID_SIMON1DOS)
382 playSting(soundId);
383 else
384 _sound->playEffects(soundId);
385 }
386
os1_screenTextPObj()387 void AGOSEngine_Simon1::os1_screenTextPObj() {
388 // 177: inventory descriptions
389 uint vgaSpriteId = getVarOrByte();
390 uint color = getVarOrByte();
391
392 SubObject *subObject = (SubObject *)findChildOfType(getNextItemPtr(), kObjectType);
393 if (getFeatures() & GF_TALKIE) {
394 if (subObject != NULL && subObject->objectFlags & kOFVoice) {
395 uint offs = getOffsetOfChild2Param(subObject, kOFVoice);
396 playSpeech(subObject->objectFlagValue[offs], vgaSpriteId);
397 } else if (subObject != NULL && subObject->objectFlags & kOFNumber) {
398 uint offs = getOffsetOfChild2Param(subObject, kOFNumber);
399 playSpeech(subObject->objectFlagValue[offs] + 3550, vgaSpriteId);
400 }
401 }
402
403 if (subObject != NULL && subObject->objectFlags & kOFText && _subtitles) {
404 const char *stringPtr = (const char *)getStringPtrByID(subObject->objectFlagValue[0]);
405 TextLocation *tl = getTextLocation(vgaSpriteId);
406 char buf[256];
407 int j, k;
408
409 if (subObject->objectFlags & kOFNumber) {
410 if (_language == Common::HE_ISR) {
411 j = subObject->objectFlagValue[getOffsetOfChild2Param(subObject, kOFNumber)];
412 k = (j % 10) * 10;
413 k += j / 10;
414 if (!(j % 10))
415 sprintf(buf,"0%d%s", k, stringPtr);
416 else
417 sprintf(buf,"%d%s", k, stringPtr);
418 } else {
419 sprintf(buf,"%d%s", subObject->objectFlagValue[getOffsetOfChild2Param(subObject, kOFNumber)], stringPtr);
420 }
421 stringPtr = buf;
422 }
423 if (stringPtr != NULL && stringPtr[0] != 0)
424 printScreenText(vgaSpriteId, color, stringPtr, tl->x, tl->y, tl->width);
425 }
426 }
427
os1_getPathPosn()428 void AGOSEngine_Simon1::os1_getPathPosn() {
429 // 178: path find
430 uint x = getVarOrWord();
431 uint y = getVarOrWord();
432 uint var_1 = getVarOrByte();
433 uint var_2 = getVarOrByte();
434
435 const uint16 *p;
436 uint i, j;
437 uint prev_i;
438 uint x_diff, y_diff;
439 uint best_i = 0, best_j = 0, best_dist = 0xFFFFFFFF;
440 uint maxPath = (getGameType() == GType_FF || getGameType() == GType_PP) ? 100 : 20;
441
442 if (getGameType() == GType_FF || getGameType() == GType_PP) {
443 x += _scrollX;
444 y += _scrollY;
445 } else if (getGameType() == GType_SIMON2) {
446 x += _scrollX * 8;
447 }
448
449 int end = (getGameType() == GType_FF) ? 9999 : 999;
450 prev_i = maxPath + 1 - readVariable(12);
451 for (i = maxPath; i != 0; --i) {
452 p = (const uint16 *)_pathFindArray[maxPath - i];
453 if (!p)
454 continue;
455 for (j = 0; readUint16Wrapper(&p[0]) != end; j++, p += 2) {
456 x_diff = ABS((int16)(readUint16Wrapper(&p[0]) - x));
457 y_diff = ABS((int16)(readUint16Wrapper(&p[1]) - 12 - y));
458
459 if (x_diff < y_diff) {
460 x_diff /= 4;
461 y_diff *= 4;
462 }
463 x_diff += y_diff /= 4;
464
465 if ((x_diff < best_dist) || ((x_diff == best_dist) && (prev_i == i))) {
466 best_dist = x_diff;
467 best_i = maxPath + 1 - i;
468 best_j = j;
469 }
470 }
471 }
472
473 writeVariable(var_1, best_i);
474 writeVariable(var_2, best_j);
475 }
476
os1_scnTxtLongText()477 void AGOSEngine_Simon1::os1_scnTxtLongText() {
478 // 179: conversation responses and room descriptions
479 uint vgaSpriteId = getVarOrByte();
480 uint color = getVarOrByte();
481 uint stringId = getVarOrByte();
482 uint speechId = 0;
483 TextLocation *tl;
484
485 const char *stringPtr = (const char *)getStringPtrByID(_longText[stringId]);
486 if (getFeatures() & GF_TALKIE)
487 speechId = _longSound[stringId];
488
489 if (getGameType() == GType_FF || getGameType() == GType_PP)
490 vgaSpriteId = 1;
491 tl = getTextLocation(vgaSpriteId);
492
493 if (_speech && speechId != 0)
494 playSpeech(speechId, vgaSpriteId);
495 if (stringPtr != NULL && stringPtr[0] != 0 && _subtitles)
496 printScreenText(vgaSpriteId, color, stringPtr, tl->x, tl->y, tl->width);
497 }
498
os1_mouseOn()499 void AGOSEngine_Simon1::os1_mouseOn() {
500 // 180: force mouseOn
501 _mouseHideCount = 0;
502 }
503
os1_mouseOff()504 void AGOSEngine_Simon1::os1_mouseOff() {
505 // 181: force mouseOff
506 scriptMouseOff();
507 }
508
os1_loadBeard()509 void AGOSEngine_Simon1::os1_loadBeard() {
510 // 182: load beard
511 if (_beardLoaded == false) {
512 _beardLoaded = true;
513 _videoLockOut |= 0x8000;
514 loadVGABeardFile(328);
515 _videoLockOut &= ~0x8000;
516 }
517 }
518
os1_unloadBeard()519 void AGOSEngine_Simon1::os1_unloadBeard() {
520 // 183: unload beard
521 if (_beardLoaded == true) {
522 _beardLoaded = false;
523 _videoLockOut |= 0x8000;
524 loadVGABeardFile(23);
525 _videoLockOut &= ~0x8000;
526 }
527 }
528
os1_unloadZone()529 void AGOSEngine_Simon1::os1_unloadZone() {
530 // 184: unload zone
531 uint a = getVarOrWord();
532 VgaPointersEntry *vpe = &_vgaBufferPointers[a];
533
534 vpe->sfxFile = NULL;
535 vpe->vgaFile1 = NULL;
536 vpe->vgaFile2 = NULL;
537 }
538
os1_loadStrings()539 void AGOSEngine_Simon1::os1_loadStrings() {
540 // 185: load sound files
541 _soundFileId = getVarOrWord();
542 if (getPlatform() == Common::kPlatformAmiga && (getFeatures() & GF_TALKIE)) {
543 char buf[10];
544 sprintf(buf, "%d%s", _soundFileId, "Effects");
545 _sound->readSfxFile(buf);
546 sprintf(buf, "%d%s", _soundFileId, "simon");
547 _sound->readVoiceFile(buf);
548 }
549 }
550
os1_unfreezeZones()551 void AGOSEngine_Simon1::os1_unfreezeZones() {
552 // 186: freeze zone
553 unfreezeBottom();
554 }
555
os1_specialFade()556 void AGOSEngine_Simon1::os1_specialFade() {
557 // 187: fade to black
558 uint i;
559
560 for (i = 32; i != 0; --i) {
561 paletteFadeOut(_currentPalette, 32, 8);
562 paletteFadeOut(_currentPalette + 3 * 48, 144, 8);
563 paletteFadeOut(_currentPalette + 3 * 208, 48, 8);
564 _system->getPaletteManager()->setPalette(_currentPalette, 0, 256);
565 delay(5);
566 }
567
568 memcpy(_displayPalette, _currentPalette, sizeof(_currentPalette));
569 }
570
scriptMouseOff()571 void AGOSEngine::scriptMouseOff() {
572 _videoLockOut |= 0x8000;
573 vc34_setMouseOff();
574 _videoLockOut &= ~0x8000;
575 }
576
577 } // End of namespace AGOS
578