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