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 "made/scriptfuncs.h"
24 #include "made/made.h"
25 #include "made/screen.h"
26 #include "made/music.h"
27 #include "made/database.h"
28 #include "made/pmvplayer.h"
29 
30 #include "audio/softsynth/pcspk.h"
31 
32 #include "backends/audiocd/audiocd.h"
33 
34 #include "graphics/cursorman.h"
35 #include "graphics/surface.h"
36 
37 namespace Made {
38 
ScriptFunctions(MadeEngine * vm)39 ScriptFunctions::ScriptFunctions(MadeEngine *vm) : _vm(vm), _soundStarted(false) {
40 	// Initialize the two tone generators
41 	_pcSpeaker1 = new Audio::PCSpeaker();
42 	_pcSpeaker2 = new Audio::PCSpeaker();
43 	_vm->_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_pcSpeakerHandle1, _pcSpeaker1);
44 	_vm->_system->getMixer()->playStream(Audio::Mixer::kMusicSoundType, &_pcSpeakerHandle2, _pcSpeaker2);
45 	_soundResource = nullptr;
46 	_musicRes = nullptr;
47 }
48 
~ScriptFunctions()49 ScriptFunctions::~ScriptFunctions() {
50 	for (uint i = 0; i < _externalFuncs.size(); ++i)
51 			delete _externalFuncs[i];
52 
53 	_vm->_system->getMixer()->stopHandle(_pcSpeakerHandle1);
54 	_vm->_system->getMixer()->stopHandle(_pcSpeakerHandle2);
55 }
56 
57 typedef Common::Functor2Mem<int16, int16*, int16, ScriptFunctions> ExternalScriptFunc;
58 #define External(x) \
59 	_externalFuncs.push_back(new ExternalScriptFunc(this, &ScriptFunctions::x));  \
60 	_externalFuncNames.push_back(#x);
setupExternalsTable()61 void ScriptFunctions::setupExternalsTable() {
62 
63 	External(sfSystemCall);
64 	External(sfInitGraf);
65 	External(sfRestoreGraf);
66 	External(sfDrawPicture);
67 	External(sfClearScreen);
68 	External(sfShowPage);
69 	External(sfPollEvent);
70 	External(sfGetMouseX);
71 	External(sfGetMouseY);
72 	External(sfGetKey);
73 	External(sfSetVisualEffect);
74 	External(sfPlaySound);
75 	External(sfPlayMusic);
76 	External(sfStopMusic);
77 	External(sfIsMusicPlaying);
78 	External(sfSetTextPos);
79 	External(sfFlashScreen);
80 	External(sfPlayNote);
81 	External(sfStopNote);
82 	External(sfPlayTele);
83 	External(sfStopTele);
84 	External(sfHideMouseCursor);
85 	External(sfShowMouseCursor);
86 	External(sfGetMusicBeat);
87 	External(sfSetScreenLock);
88 	External(sfAddSprite);
89 	External(sfFreeAnim);
90 	External(sfDrawSprite);
91 	External(sfEraseSprites);
92 	External(sfUpdateSprites);
93 	External(sfGetTimer);
94 	External(sfSetTimer);
95 	External(sfResetTimer);
96 	External(sfAllocTimer);
97 	External(sfFreeTimer);
98 	External(sfSetPaletteLock);
99 	External(sfSetFont);
100 	External(sfDrawText);
101 	External(sfHomeText);
102 	External(sfSetTextRect);
103 	External(sfSetTextXY);
104 	External(sfSetFontDropShadow);
105 	External(sfSetFontColor);
106 	External(sfSetFontOutline);
107 	External(sfLoadMouseCursor);
108 	External(sfSetSpriteGround);
109 	External(sfLoadResText);
110 
111 	if (_vm->getGameID() == GID_MANHOLE || _vm->getGameID() == GID_LGOP2 || _vm->getGameID() == GID_RODNEY) {
112 		External(sfAddScreenMask);
113 		External(sfSetSpriteMask);
114 	} else if (_vm->getGameID() == GID_RTZ) {
115 		External(sfSetClipArea);
116 		External(sfSetSpriteClip);
117 	}
118 
119 	External(sfSoundPlaying);
120 	External(sfStopSound);
121 	External(sfPlayVoice);
122 
123 	if (_vm->getGameID() == GID_MANHOLE || _vm->getGameID() == GID_RTZ || _vm->getGameID() == GID_RODNEY) {
124 		External(sfPlayCd);
125 		External(sfStopCd);
126 		External(sfGetCdStatus);
127 		External(sfGetCdTime);
128 		External(sfPlayCdSegment);
129 	}
130 
131 	if (_vm->getGameID() == GID_RTZ) {
132 		External(sfPrintf);
133 		External(sfClearMono);
134 		External(sfGetSoundEnergy);
135 		External(sfClearText);
136 		External(sfAnimText);
137 		External(sfGetTextWidth);
138 		External(sfPlayMovie);
139 		External(sfLoadSound);
140 		External(sfLoadMusic);
141 		External(sfLoadPicture);
142 		External(sfSetMusicVolume);
143 		External(sfRestartEvents);
144 		External(sfPlaceSprite);
145 		External(sfPlaceText);
146 		External(sfDeleteChannel);
147 		External(sfGetChannelType);
148 		External(sfSetChannelState);
149 		External(sfSetChannelLocation);
150 		External(sfSetChannelContent);
151 		External(sfSetExcludeArea);
152 		External(sfSetSpriteExclude);
153 		External(sfGetChannelState);
154 		External(sfPlaceAnim);
155 		External(sfSetAnimFrame);
156 		External(sfGetAnimFrame);
157 		External(sfGetAnimFrameCount);
158 		External(sfGetPictureWidth);
159 		External(sfGetPictureHeight);
160 		External(sfSetSoundRate);
161 		External(sfDrawAnimPic);
162 		External(sfLoadAnim);
163 		External(sfReadText);
164 		External(sfReadMenu);
165 		External(sfDrawMenu);
166 		External(sfGetMenuCount);
167 		External(sfSaveGame);
168 		External(sfLoadGame);
169 		External(sfGetGameDescription);
170 		External(sfShakeScreen);
171 		External(sfPlaceMenu);
172 		External(sfSetSoundVolume);
173 		External(sfGetSynthType);
174 		External(sfIsSlowSystem);
175 	}
176 
177 }
178 #undef External
179 
sfSystemCall(int16 argc,int16 * argv)180 int16 ScriptFunctions::sfSystemCall(int16 argc, int16 *argv) {
181 	// This opcode is empty.
182 	return 0;
183 }
184 
sfInitGraf(int16 argc,int16 * argv)185 int16 ScriptFunctions::sfInitGraf(int16 argc, int16 *argv) {
186 	// This opcode is empty.
187 	return 0;
188 }
189 
sfRestoreGraf(int16 argc,int16 * argv)190 int16 ScriptFunctions::sfRestoreGraf(int16 argc, int16 *argv) {
191 	// This opcode is empty.
192 	return 0;
193 }
194 
sfDrawPicture(int16 argc,int16 * argv)195 int16 ScriptFunctions::sfDrawPicture(int16 argc, int16 *argv) {
196 	return _vm->_screen->drawPic(argv[4], argv[3], argv[2], argv[1], argv[0]);
197 }
198 
sfClearScreen(int16 argc,int16 * argv)199 int16 ScriptFunctions::sfClearScreen(int16 argc, int16 *argv) {
200 	if (_vm->_screen->isScreenLocked())
201 		return 0;
202 	if (_vm->_autoStopSound) {
203 		stopSound();
204 		_vm->_autoStopSound = false;
205 	}
206 	_vm->_screen->clearScreen();
207 	return 0;
208 }
209 
sfShowPage(int16 argc,int16 * argv)210 int16 ScriptFunctions::sfShowPage(int16 argc, int16 *argv) {
211 	_vm->_screen->show();
212 	// NOTE: We need to return something != 0 here or some game scripts won't
213 	//       work correctly. The actual meaning of this value is unknown to me.
214 	//       0x38 was found out by analyzing debug output of the original engine.
215 	return 0x38;
216 }
217 
sfPollEvent(int16 argc,int16 * argv)218 int16 ScriptFunctions::sfPollEvent(int16 argc, int16 *argv) {
219 	_vm->handleEvents();
220 	_vm->_screen->updateScreenAndWait(10);
221 
222 	int16 eventNum = _vm->_eventNum;
223 	_vm->_eventNum = 0;
224 
225 	return eventNum;
226 }
227 
sfGetMouseX(int16 argc,int16 * argv)228 int16 ScriptFunctions::sfGetMouseX(int16 argc, int16 *argv) {
229 	return _vm->_eventMouseX;
230 }
231 
sfGetMouseY(int16 argc,int16 * argv)232 int16 ScriptFunctions::sfGetMouseY(int16 argc, int16 *argv) {
233 	return _vm->_eventMouseY;
234 }
235 
sfGetKey(int16 argc,int16 * argv)236 int16 ScriptFunctions::sfGetKey(int16 argc, int16 *argv) {
237 	return _vm->_eventKey;
238 }
239 
sfSetVisualEffect(int16 argc,int16 * argv)240 int16 ScriptFunctions::sfSetVisualEffect(int16 argc, int16 *argv) {
241 	_vm->_screen->setVisualEffectNum(argv[0]);
242 	return 0;
243 }
244 
sfPlaySound(int16 argc,int16 * argv)245 int16 ScriptFunctions::sfPlaySound(int16 argc, int16 *argv) {
246 	int16 soundNum = argv[0];
247 	_vm->_autoStopSound = false;
248 	stopSound();
249 	if (argc > 1) {
250 		soundNum = argv[1];
251 		_vm->_autoStopSound = (argv[0] == 1);
252 	}
253 	if (soundNum > 0) {
254 		SoundResource *soundRes = _vm->_res->getSound(soundNum);
255 		_vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_audioStreamHandle,
256 			soundRes->getAudioStream(_vm->_soundRate, false));
257 		_vm->_soundEnergyArray = soundRes->getSoundEnergyArray();
258 		_vm->_soundEnergyIndex = 0;
259 		_soundStarted = true;
260 		_soundResource = soundRes;
261 	}
262 	return 0;
263 }
264 
sfPlayMusic(int16 argc,int16 * argv)265 int16 ScriptFunctions::sfPlayMusic(int16 argc, int16 *argv) {
266 	int16 musicNum = argv[0];
267 
268 	_vm->_musicBeatStart = _vm->_system->getMillis();
269 
270 	if (_vm->getGameID() == GID_RTZ) {
271 		if (musicNum > 0) {
272 			_musicRes = _vm->_res->getXmidi(musicNum);
273 			if (_musicRes)
274 				_vm->_music->playXMIDI(_musicRes);
275 		}
276 	} else {
277 		// HACK: music number 2 in LGOP2 is file MT32SET.TON, which
278 		// is used to set the MT32 instruments. This is not loaded
279 		// correctly and the game freezes, and since we don't support
280 		// MT32 music yet, we ignore it here
281 		// FIXME: Remove this hack and handle this file properly
282 		if (_vm->getGameID() == GID_LGOP2 && musicNum == 2)
283 			return 0;
284 		if (musicNum > 0) {
285 			_musicRes = _vm->_res->getMidi(musicNum);
286 			if (_musicRes)
287 				_vm->_music->playSMF(_musicRes);
288 		}
289 	}
290 
291 	return 0;
292 }
293 
sfStopMusic(int16 argc,int16 * argv)294 int16 ScriptFunctions::sfStopMusic(int16 argc, int16 *argv) {
295 	if (_vm->_music->isPlaying() && _musicRes) {
296 		_vm->_music->stop();
297 		_vm->_res->freeResource(_musicRes);
298 		_musicRes = NULL;
299 	}
300 	return 0;
301 }
302 
sfIsMusicPlaying(int16 argc,int16 * argv)303 int16 ScriptFunctions::sfIsMusicPlaying(int16 argc, int16 *argv) {
304 	if (_vm->_music->isPlaying())
305 		return 1;
306 	else
307 		return 0;
308 }
309 
sfSetTextPos(int16 argc,int16 * argv)310 int16 ScriptFunctions::sfSetTextPos(int16 argc, int16 *argv) {
311 	// Used in Manhole:NE
312 	// This seems to be some kind of low-level opcode.
313 	// The original engine calls int 10h to set the VGA cursor position.
314 	// Since this seems to be used for debugging purposes only it's left out.
315 	return 0;
316 }
317 
sfFlashScreen(int16 argc,int16 * argv)318 int16 ScriptFunctions::sfFlashScreen(int16 argc, int16 *argv) {
319 	_vm->_screen->flash(argv[0]);
320 	return 0;
321 }
322 
sfPlayNote(int16 argc,int16 * argv)323 int16 ScriptFunctions::sfPlayNote(int16 argc, int16 *argv) {
324 	// This is used when using the piano in the desk screen inside the ship
325 	// in The Manhole (EGA/NE).
326 
327 	// It takes 2 parameters:
328 	// The first parameter is the note number of the key pressed + 1
329 	// The second parameter is some sort of modifier (volume, perhaps?),
330 	// depending on which of the 3 keys on the right has been pressed.
331 	// This value seems to be [12, 14] in NE and [1, 3] in EGA.
332 
333 	// Note frequencies based on http://www.phy.mtu.edu/~suits/notefreqs.html
334 	static const int freqTable[] = {
335 		16, 17, 18, 19, 21, 22, 23, 24, 26, 28, 29,
336 		30, 32, 35, 37, 39, 41, 44, 46, 49, 52, 55,
337 		58, 62, 65, 69, 73, 77, 82, 87, 93, 98, 104,
338 		110, 117, 123, 131, 139, 147, 156, 165, 175, 195,
339 		196, 208, 220, 233, 247, 262, 277, 294, 311, 330,
340 		349, 370, 392, 415, 440, 466, 494, 523, 554, 587,
341 		622, 659, 698, 740, 784, 831, 880, 932, 988, 1047,
342 		1109, 1175, 1245, 1319, 1397, 1480, 1568, 1661, 1760,
343 		1865, 1976, 2093, 2217, 2349, 2489, 2637, 2794, 2960,
344 		3136, 3322, 3529, 3729, 3951, 4186, 4435, 4697, 4978
345 	};
346 
347 	debug(4, "sfPlayNote: Note = %d, Volume(?) = %d", argv[0] - 1, argv[1]);
348 
349 	_pcSpeaker1->play(Audio::PCSpeaker::kWaveFormSine, freqTable[argv[0] - 1], -1);
350 
351 	// TODO: Figure out what to do with the second parameter
352 	//_pcSpeaker1->setVolume(argv[1]);
353 
354 	return 0;
355 }
356 
sfStopNote(int16 argc,int16 * argv)357 int16 ScriptFunctions::sfStopNote(int16 argc, int16 *argv) {
358 	// Used in the same place as sfPlayNote, with the same parameters
359 	// We just stop the wave generator here
360 	_pcSpeaker1->stop();
361 	return 0;
362 }
363 
sfPlayTele(int16 argc,int16 * argv)364 int16 ScriptFunctions::sfPlayTele(int16 argc, int16 *argv) {
365 	// This is used when pressing the phone keys while using the phone in
366 	// the desk screen inside the ship in The Manhole (EGA/NE).
367 	// It takes 1 parameter, the key pressed (0-9, 10 for asterisk, 11 for hash)
368 
369 	// A telephone keypad uses a two tones for each key.
370 	// See http://en.wikipedia.org/wiki/Telephone_keypad for more info
371 
372 	static const int freqTable1[] = {
373 		1336, 1209, 1336, 1477,
374 		1209, 1336, 1477, 1209,
375 		1336, 1477, 1209, 1477
376 	};
377 
378 	static const int freqTable2[] = {
379 		941, 697, 697, 697,
380 		770, 770, 770, 852,
381 		852, 852, 941, 941
382 	};
383 
384 	debug(4, "sfPlayTele: Button = %d", argv[0]);
385 
386 	_pcSpeaker1->play(Audio::PCSpeaker::kWaveFormSine, freqTable1[argv[0]], -1);
387 	_pcSpeaker2->play(Audio::PCSpeaker::kWaveFormSine, freqTable2[argv[0]], -1);
388 	return 0;
389 }
390 
sfStopTele(int16 argc,int16 * argv)391 int16 ScriptFunctions::sfStopTele(int16 argc, int16 *argv) {
392 	// Used in the same place as sfPlayTele, with the same parameters
393 	// We just stop both wave generators here
394 	_pcSpeaker1->stop();
395 	_pcSpeaker2->stop();
396 	return 0;
397 }
398 
sfHideMouseCursor(int16 argc,int16 * argv)399 int16 ScriptFunctions::sfHideMouseCursor(int16 argc, int16 *argv) {
400 	_vm->_system->showMouse(false);
401 	return 0;
402 }
403 
sfShowMouseCursor(int16 argc,int16 * argv)404 int16 ScriptFunctions::sfShowMouseCursor(int16 argc, int16 *argv) {
405 	_vm->_system->showMouse(true);
406 	return 0;
407 }
408 
sfGetMusicBeat(int16 argc,int16 * argv)409 int16 ScriptFunctions::sfGetMusicBeat(int16 argc, int16 *argv) {
410 	// This is used as timer in some games
411 	return (_vm->_system->getMillis() - _vm->_musicBeatStart) / 360;
412 }
413 
sfSetScreenLock(int16 argc,int16 * argv)414 int16 ScriptFunctions::sfSetScreenLock(int16 argc, int16 *argv) {
415 	_vm->_screen->setScreenLock(argv[0] != 0);
416 	return 0;
417 }
418 
sfAddSprite(int16 argc,int16 * argv)419 int16 ScriptFunctions::sfAddSprite(int16 argc, int16 *argv) {
420 	if (_vm->getGameID() == GID_RTZ) {
421 		// Unused in RTZ
422 		return 0;
423 	} if (_vm->getGameID() == GID_LGOP2 || _vm->getGameID() == GID_MANHOLE || _vm->getGameID() == GID_RODNEY) {
424 		return _vm->_screen->addToSpriteList(argv[2], argv[1], argv[0]);
425 	} else {
426 		return 0;
427 	}
428 }
429 
sfFreeAnim(int16 argc,int16 * argv)430 int16 ScriptFunctions::sfFreeAnim(int16 argc, int16 *argv) {
431 	_vm->_screen->clearChannels();
432 	if (_vm->getGameID() == GID_LGOP2 || _vm->getGameID() == GID_MANHOLE || _vm->getGameID() == GID_RODNEY) {
433 		_vm->_screen->clearSpriteList();
434 	}
435 	return 0;
436 }
437 
sfDrawSprite(int16 argc,int16 * argv)438 int16 ScriptFunctions::sfDrawSprite(int16 argc, int16 *argv) {
439 	if (_vm->getGameID() == GID_RTZ) {
440 		return _vm->_screen->drawSprite(argv[2], argv[1], argv[0]);
441 	} if (_vm->getGameID() == GID_LGOP2 || _vm->getGameID() == GID_MANHOLE || _vm->getGameID() == GID_RODNEY) {
442 		SpriteListItem item = _vm->_screen->getFromSpriteList(argv[2]);
443 		int16 channelIndex = _vm->_screen->drawSprite(item.index, argv[1] - item.xofs, argv[0] - item.yofs);
444 		_vm->_screen->setChannelUseMask(channelIndex);
445 		return 0;
446 	} else {
447 		return 0;
448 	}
449 }
450 
sfEraseSprites(int16 argc,int16 * argv)451 int16 ScriptFunctions::sfEraseSprites(int16 argc, int16 *argv) {
452 	_vm->_screen->clearChannels();
453 	return 0;
454 }
455 
sfUpdateSprites(int16 argc,int16 * argv)456 int16 ScriptFunctions::sfUpdateSprites(int16 argc, int16 *argv) {
457 	_vm->_screen->updateSprites();
458 	return 0;
459 }
460 
sfGetTimer(int16 argc,int16 * argv)461 int16 ScriptFunctions::sfGetTimer(int16 argc, int16 *argv) {
462 	return _vm->getTimer(argv[0]);
463 }
464 
sfSetTimer(int16 argc,int16 * argv)465 int16 ScriptFunctions::sfSetTimer(int16 argc, int16 *argv) {
466 	_vm->setTimer(argv[1], argv[0]);
467 	return 0;
468 }
469 
sfResetTimer(int16 argc,int16 * argv)470 int16 ScriptFunctions::sfResetTimer(int16 argc, int16 *argv) {
471 	_vm->resetTimer(argv[0]);
472 	return 0;
473 }
474 
sfAllocTimer(int16 argc,int16 * argv)475 int16 ScriptFunctions::sfAllocTimer(int16 argc, int16 *argv) {
476 	return _vm->allocTimer();
477 }
478 
sfFreeTimer(int16 argc,int16 * argv)479 int16 ScriptFunctions::sfFreeTimer(int16 argc, int16 *argv) {
480 	_vm->freeTimer(argv[0]);
481 	return 0;
482 }
483 
sfSetPaletteLock(int16 argc,int16 * argv)484 int16 ScriptFunctions::sfSetPaletteLock(int16 argc, int16 *argv) {
485 	_vm->_screen->setPaletteLock(argv[0] != 0);
486 	return 0;
487 }
488 
sfSetFont(int16 argc,int16 * argv)489 int16 ScriptFunctions::sfSetFont(int16 argc, int16 *argv) {
490 	_vm->_screen->setFont(argv[0]);
491 	return 0;
492 }
493 
sfDrawText(int16 argc,int16 * argv)494 int16 ScriptFunctions::sfDrawText(int16 argc, int16 *argv) {
495 
496 	const char *text = NULL;
497 
498 	if (_vm->getGameID() == GID_RTZ) {
499 		text = _vm->_dat->getObjectString(argv[argc - 1]);
500 	} if (_vm->getGameID() == GID_LGOP2 || _vm->getGameID() == GID_MANHOLE || _vm->getGameID() == GID_RODNEY) {
501 		text = _vm->_dat->getString(argv[argc - 1]);
502 	}
503 
504 	if (text) {
505 		Common::String finalText;
506 		switch (argc) {
507 		case 1:
508 			finalText = text;
509 			break;
510 		case 2:
511 			finalText = Common::String::format(text, argv[0]);
512 			break;
513 		case 3:
514 			finalText = Common::String::format(text, argv[1], argv[0]);
515 			break;
516 		case 4:
517 			finalText = Common::String::format(text, argv[2], argv[1], argv[0]);
518 			break;
519 		case 5:
520 			finalText = Common::String::format(text, argv[3], argv[2], argv[1], argv[0]);
521 			break;
522 		default:
523 			// Leave it empty
524 			break;
525 		}
526 		_vm->_screen->printText(finalText.c_str());
527 	}
528 
529 	return 0;
530 }
531 
sfHomeText(int16 argc,int16 * argv)532 int16 ScriptFunctions::sfHomeText(int16 argc, int16 *argv) {
533 	_vm->_screen->homeText();
534 	return 0;
535 }
536 
sfSetTextRect(int16 argc,int16 * argv)537 int16 ScriptFunctions::sfSetTextRect(int16 argc, int16 *argv) {
538 	// Used in the save/load screens of RtZ, and perhaps other places as well
539 	int16 x1 = CLIP<int16>(argv[4], 1, 318);
540 	int16 y1 = CLIP<int16>(argv[3], 1, 198);
541 	int16 x2 = CLIP<int16>(argv[2], 1, 318);
542 	int16 y2 = CLIP<int16>(argv[1], 1, 198);
543 	//int16 textValue = argv[0];	// looks to be unused
544 
545 	_vm->_screen->setTextRect(Common::Rect(x1, y1, x2, y2));
546 	return 0;
547 }
548 
sfSetTextXY(int16 argc,int16 * argv)549 int16 ScriptFunctions::sfSetTextXY(int16 argc, int16 *argv) {
550 	int16 x = CLIP<int16>(argv[1], 1, 318);
551 	int16 y = CLIP<int16>(argv[0], 1, 198);
552 	_vm->_screen->setTextXY(x, y);
553 	return 0;
554 }
555 
sfSetFontDropShadow(int16 argc,int16 * argv)556 int16 ScriptFunctions::sfSetFontDropShadow(int16 argc, int16 *argv) {
557 	// if the drop shadow color is -1, then text drop shadow is disabled
558 	// when font drop shadow is enabled, outline is disabled
559 	_vm->_screen->setDropShadowColor(argv[0]);
560 	return 0;
561 }
562 
sfSetFontColor(int16 argc,int16 * argv)563 int16 ScriptFunctions::sfSetFontColor(int16 argc, int16 *argv) {
564 	_vm->_screen->setTextColor(argv[0]);
565 	return 0;
566 }
567 
sfSetFontOutline(int16 argc,int16 * argv)568 int16 ScriptFunctions::sfSetFontOutline(int16 argc, int16 *argv) {
569 	// if the outline color is -1, then text outline is disabled
570 	// when font outline is enabled, drop shadow is disabled
571 	_vm->_screen->setOutlineColor(argv[0]);
572 	return 0;
573 }
574 
sfLoadMouseCursor(int16 argc,int16 * argv)575 int16 ScriptFunctions::sfLoadMouseCursor(int16 argc, int16 *argv) {
576 	PictureResource *flex = _vm->_res->getPicture(argv[2]);
577 	if (flex) {
578 		Graphics::Surface *surf = flex->getPicture();
579 		CursorMan.replaceCursor(surf->getPixels(), surf->w, surf->h, argv[1], argv[0], 0);
580 		_vm->_res->freeResource(flex);
581 	}
582 	return 0;
583 }
584 
sfSetSpriteGround(int16 argc,int16 * argv)585 int16 ScriptFunctions::sfSetSpriteGround(int16 argc, int16 *argv) {
586 	_vm->_screen->setGround(argv[0]);
587 	return 0;
588 }
589 
sfLoadResText(int16 argc,int16 * argv)590 int16 ScriptFunctions::sfLoadResText(int16 argc, int16 *argv) {
591 	// Never used in LGOP2, RTZ, Manhole:NE, Rodney
592 	warning("Unimplemented opcode: sfLoadResText");
593 	return 0;
594 }
595 
sfSetClipArea(int16 argc,int16 * argv)596 int16 ScriptFunctions::sfSetClipArea(int16 argc, int16 *argv) {
597 	_vm->_screen->setClipArea(argv[3], argv[2], argv[1], argv[0]);
598 	return 0;
599 }
600 
sfSetSpriteClip(int16 argc,int16 * argv)601 int16 ScriptFunctions::sfSetSpriteClip(int16 argc, int16 *argv) {
602 	_vm->_screen->setClip(argv[0]);
603 	return 0;
604 }
605 
sfAddScreenMask(int16 argc,int16 * argv)606 int16 ScriptFunctions::sfAddScreenMask(int16 argc, int16 *argv) {
607 	_vm->_screen->drawMask(argv[2], argv[1], argv[0]);
608 	return 0;
609 }
610 
sfSetSpriteMask(int16 argc,int16 * argv)611 int16 ScriptFunctions::sfSetSpriteMask(int16 argc, int16 *argv) {
612 	_vm->_screen->setMask(argv[0]);
613 	return 0;
614 }
615 
sfSoundPlaying(int16 argc,int16 * argv)616 int16 ScriptFunctions::sfSoundPlaying(int16 argc, int16 *argv) {
617 	if (_vm->_mixer->isSoundHandleActive(_audioStreamHandle))
618 		return 1;
619 	else
620 		return 0;
621 }
622 
stopSound()623 void ScriptFunctions::stopSound() {
624 	_vm->_mixer->stopHandle(_audioStreamHandle);
625 	if (_soundStarted) {
626 		_vm->_res->freeResource(_soundResource);
627 		_soundStarted = false;
628 	}
629 
630 }
631 
632 
sfStopSound(int16 argc,int16 * argv)633 int16 ScriptFunctions::sfStopSound(int16 argc, int16 *argv) {
634 	stopSound();
635 	_vm->_autoStopSound = false;
636 	return 0;
637 }
638 
sfPlayVoice(int16 argc,int16 * argv)639 int16 ScriptFunctions::sfPlayVoice(int16 argc, int16 *argv) {
640 	int16 soundNum = argv[0];
641 	stopSound();
642 	if (soundNum > 0) {
643 		_soundResource = _vm->_res->getSound(soundNum);
644 		_vm->_mixer->playStream(Audio::Mixer::kPlainSoundType, &_audioStreamHandle,
645 			_soundResource->getAudioStream(_vm->_soundRate, false));
646 		_vm->_autoStopSound = true;
647 		_soundStarted = true;
648 	}
649 	return 0;
650 }
651 
sfPlayCd(int16 argc,int16 * argv)652 int16 ScriptFunctions::sfPlayCd(int16 argc, int16 *argv) {
653 	g_system->getAudioCDManager()->play(argv[0] - 1, 1, 0, 0);
654 	_vm->_cdTimeStart = _vm->_system->getMillis();
655 	if (g_system->getAudioCDManager()->isPlaying()) {
656 		return 1;
657 	} else {
658 		return 0;
659 	}
660 }
661 
sfStopCd(int16 argc,int16 * argv)662 int16 ScriptFunctions::sfStopCd(int16 argc, int16 *argv) {
663 	if (g_system->getAudioCDManager()->isPlaying()) {
664 		g_system->getAudioCDManager()->stop();
665 		return 1;
666 	} else {
667 		return 0;
668 	}
669 }
670 
sfGetCdStatus(int16 argc,int16 * argv)671 int16 ScriptFunctions::sfGetCdStatus(int16 argc, int16 *argv) {
672 	return g_system->getAudioCDManager()->isPlaying() ? 1 : 0;
673 }
674 
sfGetCdTime(int16 argc,int16 * argv)675 int16 ScriptFunctions::sfGetCdTime(int16 argc, int16 *argv) {
676 	if (g_system->getAudioCDManager()->isPlaying()) {
677 		uint32 deltaTime = _vm->_system->getMillis() - _vm->_cdTimeStart;
678 		// This basically converts the time from milliseconds to MSF format to MADE's format
679 		return (deltaTime / 1000 * 30) + (deltaTime % 1000 / 75 * 30 / 75);
680 	} else {
681 		return 32000;
682 	}
683 }
684 
sfPlayCdSegment(int16 argc,int16 * argv)685 int16 ScriptFunctions::sfPlayCdSegment(int16 argc, int16 *argv) {
686 	// Never used in LGOP2, RTZ, Manhole:NE, Rodney
687 	warning("Unimplemented opcode: sfPlayCdSegment");
688 	return 0;
689 }
690 
sfPrintf(int16 argc,int16 * argv)691 int16 ScriptFunctions::sfPrintf(int16 argc, int16 *argv) {
692 	const char *text = _vm->_dat->getObjectString(argv[argc - 1]);
693 	debug(4, "--> text = %s", text);
694 	return 0;
695 }
696 
sfClearMono(int16 argc,int16 * argv)697 int16 ScriptFunctions::sfClearMono(int16 argc, int16 *argv) {
698 	// Never used in LGOP2, RTZ, Manhole:NE
699 	warning("Unimplemented opcode: sfClearMono");
700 	return 0;
701 }
702 
sfGetSoundEnergy(int16 argc,int16 * argv)703 int16 ScriptFunctions::sfGetSoundEnergy(int16 argc, int16 *argv) {
704 	// This is called while in-game voices are played to animate
705 	// mouths when NPCs are talking
706 	int result = 0;
707 	if (_vm->_mixer->isSoundHandleActive(_audioStreamHandle) && _vm->_soundEnergyArray) {
708 		while (_vm->_soundEnergyIndex < _vm->_soundEnergyArray->size()) {
709 			SoundEnergyItem *soundEnergyItem = &(*_vm->_soundEnergyArray)[_vm->_soundEnergyIndex];
710 			const Audio::Timestamp ts = _vm->_mixer->getElapsedTime(_audioStreamHandle);
711 			if (ts.convertToFramerate(_vm->_soundRate).totalNumberOfFrames() < (int)soundEnergyItem->position) {
712 				result = soundEnergyItem->energy;
713 				break;
714 			}
715 			_vm->_soundEnergyIndex++;
716 		}
717 		if (_vm->_soundEnergyIndex >= _vm->_soundEnergyArray->size())
718 			result = 0;
719 	}
720 	return result;
721 }
722 
sfClearText(int16 argc,int16 * argv)723 int16 ScriptFunctions::sfClearText(int16 argc, int16 *argv) {
724 	// Never used in LGOP2, RTZ, Manhole:NE
725 	warning("Unimplemented opcode: sfClearText");
726 	return 1;
727 }
728 
sfAnimText(int16 argc,int16 * argv)729 int16 ScriptFunctions::sfAnimText(int16 argc, int16 *argv) {
730 	// Never used in LGOP2, RTZ, Manhole:NE
731 	warning("Unimplemented opcode: sfAnimText");
732 	return 0;
733 }
734 
sfGetTextWidth(int16 argc,int16 * argv)735 int16 ScriptFunctions::sfGetTextWidth(int16 argc, int16 *argv) {
736 	int16 width = 0;
737 	if (argv[1] > 0) {
738 		const char *text = _vm->_dat->getObjectString(argv[1]);
739 		width = _vm->_screen->getTextWidth(argv[0], text);
740 	}
741 	return width;
742 }
743 
sfPlayMovie(int16 argc,int16 * argv)744 int16 ScriptFunctions::sfPlayMovie(int16 argc, int16 *argv) {
745 	const char *movieName = _vm->_dat->getObjectString(argv[1]);
746 	_vm->_system->showMouse(false);
747 	bool completed = _vm->_pmvPlayer->play(movieName);
748 	_vm->_system->showMouse(true);
749 	// Return true/false according to if the movie was canceled or not
750 	return completed ? -1 : 0;
751 }
752 
sfLoadSound(int16 argc,int16 * argv)753 int16 ScriptFunctions::sfLoadSound(int16 argc, int16 *argv) {
754 	SoundResource *sound = _vm->_res->getSound(argv[0]);
755 	if (sound) {
756 		_vm->_res->freeResource(sound);
757 		return 1;
758 	}
759 	return 0;
760 }
761 
sfLoadMusic(int16 argc,int16 * argv)762 int16 ScriptFunctions::sfLoadMusic(int16 argc, int16 *argv) {
763 	GenericResource *xmidi = _vm->_res->getXmidi(argv[0]);
764 	if (xmidi) {
765 		_vm->_res->freeResource(xmidi);
766 		return 1;
767 	}
768 	return 0;
769 }
770 
sfLoadPicture(int16 argc,int16 * argv)771 int16 ScriptFunctions::sfLoadPicture(int16 argc, int16 *argv) {
772 	PictureResource *flex = _vm->_res->getPicture(argv[0]);
773 	if (flex) {
774 		_vm->_res->freeResource(flex);
775 		return 1;
776 	}
777 	return 0;
778 }
779 
sfSetMusicVolume(int16 argc,int16 * argv)780 int16 ScriptFunctions::sfSetMusicVolume(int16 argc, int16 *argv) {
781 	// Never used in LGOP2, RTZ, Manhole:NE
782 	warning("Unimplemented opcode: sfSetMusicVolume");
783 	return 0;
784 }
785 
sfRestartEvents(int16 argc,int16 * argv)786 int16 ScriptFunctions::sfRestartEvents(int16 argc, int16 *argv) {
787 	// Used in RTZ
788 	//warning("Unimplemented opcode: sfRestartEvents");
789 	// This is used to reset the event recording/queue.
790 	// Since we don't use either it's left out.
791 	return 0;
792 }
793 
sfPlaceSprite(int16 argc,int16 * argv)794 int16 ScriptFunctions::sfPlaceSprite(int16 argc, int16 *argv) {
795 	return _vm->_screen->placeSprite(argv[3], argv[2], argv[1], argv[0]);
796 }
797 
sfPlaceText(int16 argc,int16 * argv)798 int16 ScriptFunctions::sfPlaceText(int16 argc, int16 *argv) {
799 	return _vm->_screen->placeText(argv[6], argv[5], argv[4], argv[3], argv[2], argv[1], argv[0]);
800 }
801 
sfDeleteChannel(int16 argc,int16 * argv)802 int16 ScriptFunctions::sfDeleteChannel(int16 argc, int16 *argv) {
803 	_vm->_screen->deleteChannel(argv[0]);
804 	return 0;
805 }
806 
sfGetChannelType(int16 argc,int16 * argv)807 int16 ScriptFunctions::sfGetChannelType(int16 argc, int16 *argv) {
808 	return _vm->_screen->getChannelType(argv[0]);
809 }
810 
sfSetChannelState(int16 argc,int16 * argv)811 int16 ScriptFunctions::sfSetChannelState(int16 argc, int16 *argv) {
812 	_vm->_screen->setChannelState(argv[1], argv[0]);
813 	return 0;
814 }
815 
sfSetChannelLocation(int16 argc,int16 * argv)816 int16 ScriptFunctions::sfSetChannelLocation(int16 argc, int16 *argv) {
817 	// Never used in LGOP2, RTZ, Manhole:NE
818 	warning("Unimplemented opcode: sfSetChannelLocation");
819 	return 0;
820 }
821 
sfSetChannelContent(int16 argc,int16 * argv)822 int16 ScriptFunctions::sfSetChannelContent(int16 argc, int16 *argv) {
823 	// Never used in LGOP2, RTZ, Manhole:NE
824 	warning("Unimplemented opcode: sfSetChannelContent");
825 	return 0;
826 }
827 
sfSetExcludeArea(int16 argc,int16 * argv)828 int16 ScriptFunctions::sfSetExcludeArea(int16 argc, int16 *argv) {
829 	_vm->_screen->setExcludeArea(argv[3], argv[2], argv[1], argv[0]);
830 	return 0;
831 }
832 
sfSetSpriteExclude(int16 argc,int16 * argv)833 int16 ScriptFunctions::sfSetSpriteExclude(int16 argc, int16 *argv) {
834 	_vm->_screen->setExclude(argv[0]);
835 	return 0;
836 }
837 
sfGetChannelState(int16 argc,int16 * argv)838 int16 ScriptFunctions::sfGetChannelState(int16 argc, int16 *argv) {
839 	return _vm->_screen->getChannelState(argv[0]);
840 }
841 
sfPlaceAnim(int16 argc,int16 * argv)842 int16 ScriptFunctions::sfPlaceAnim(int16 argc, int16 *argv) {
843 	return _vm->_screen->placeAnim(argv[4], argv[3], argv[2], argv[1], argv[0]);
844 }
845 
sfSetAnimFrame(int16 argc,int16 * argv)846 int16 ScriptFunctions::sfSetAnimFrame(int16 argc, int16 *argv) {
847 	_vm->_screen->setAnimFrame(argv[1], argv[0]);
848 	return 0;
849 }
850 
sfGetAnimFrame(int16 argc,int16 * argv)851 int16 ScriptFunctions::sfGetAnimFrame(int16 argc, int16 *argv) {
852 	return _vm->_screen->getAnimFrame(argv[0]);
853 }
854 
sfGetAnimFrameCount(int16 argc,int16 * argv)855 int16 ScriptFunctions::sfGetAnimFrameCount(int16 argc, int16 *argv) {
856 	int16 frameCount = 0;
857 	AnimationResource *anim = _vm->_res->getAnimation(argv[0]);
858 	if (anim) {
859 		frameCount = anim->getCount();
860 		_vm->_res->freeResource(anim);
861 	}
862 	return frameCount;
863 }
864 
sfGetPictureWidth(int16 argc,int16 * argv)865 int16 ScriptFunctions::sfGetPictureWidth(int16 argc, int16 *argv) {
866 	int16 width = 0;
867 	PictureResource *flex = _vm->_res->getPicture(argv[0]);
868 	if (flex) {
869 		width = flex->getPicture()->w;
870 		_vm->_res->freeResource(flex);
871 	}
872 	return width;
873 }
874 
sfGetPictureHeight(int16 argc,int16 * argv)875 int16 ScriptFunctions::sfGetPictureHeight(int16 argc, int16 *argv) {
876 	int16 height = 0;
877 	PictureResource *flex = _vm->_res->getPicture(argv[0]);
878 	if (flex) {
879 		height = flex->getPicture()->h;
880 		_vm->_res->freeResource(flex);
881 	}
882 	return height;
883 }
884 
sfSetSoundRate(int16 argc,int16 * argv)885 int16 ScriptFunctions::sfSetSoundRate(int16 argc, int16 *argv) {
886 	_vm->_soundRate = argv[0];
887 	return 1;
888 }
889 
sfDrawAnimPic(int16 argc,int16 * argv)890 int16 ScriptFunctions::sfDrawAnimPic(int16 argc, int16 *argv) {
891 	return _vm->_screen->drawAnimPic(argv[5], argv[4], argv[3], argv[2], argv[1], argv[0]);
892 }
893 
sfLoadAnim(int16 argc,int16 * argv)894 int16 ScriptFunctions::sfLoadAnim(int16 argc, int16 *argv) {
895 	AnimationResource *anim = _vm->_res->getAnimation(argv[0]);
896 	if (anim) {
897 		_vm->_res->freeResource(anim);
898 		return 1;
899 	}
900 	return 0;
901 }
902 
sfReadText(int16 argc,int16 * argv)903 int16 ScriptFunctions::sfReadText(int16 argc, int16 *argv) {
904 	// Never used in LGOP2, RTZ, Manhole:NE
905 	warning("Unimplemented opcode: sfReadText");
906 	return 0;
907 }
908 
sfReadMenu(int16 argc,int16 * argv)909 int16 ScriptFunctions::sfReadMenu(int16 argc, int16 *argv) {
910 	int16 objectIndex = argv[2];
911 	int16 menuIndex = argv[1];
912 	int16 textIndex = argv[0];
913 	int16 length = 0;
914 	MenuResource *menu = _vm->_res->getMenu(menuIndex);
915 	if (menu) {
916 		const char *text = menu->getString(textIndex);
917 		debug(4, "objectIndex = %04X; text = %s\n", objectIndex, text);
918 		_vm->_dat->setObjectString(objectIndex, text);
919 		_vm->_res->freeResource(menu);
920 		if (text)
921 			length = strlen(text);
922 	} else {
923 		_vm->_dat->setObjectString(objectIndex, "");
924 	}
925 	return length;
926 }
927 
sfDrawMenu(int16 argc,int16 * argv)928 int16 ScriptFunctions::sfDrawMenu(int16 argc, int16 *argv) {
929 	int16 menuIndex = argv[1];
930 	int16 textIndex = argv[0];
931 	MenuResource *menu = _vm->_res->getMenu(menuIndex);
932 	if (menu) {
933 		const char *text = menu->getString(textIndex);
934 		if (text)
935 			_vm->_screen->printText(text);
936 
937 		_vm->_res->freeResource(menu);
938 	}
939 	return 0;
940 }
941 
sfGetMenuCount(int16 argc,int16 * argv)942 int16 ScriptFunctions::sfGetMenuCount(int16 argc, int16 *argv) {
943 	int16 menuIndex = argv[0];
944 	int16 count = 0;
945 	MenuResource *menu = _vm->_res->getMenu(menuIndex);
946 	if (menu) {
947 		count = menu->getCount();
948 		_vm->_res->freeResource(menu);
949 	}
950 	return count;
951 }
952 
sfSaveGame(int16 argc,int16 * argv)953 int16 ScriptFunctions::sfSaveGame(int16 argc, int16 *argv) {
954 
955 	int16 saveNum = argv[2];
956 	int16 descObjectIndex = argv[1];
957 	int16 version = argv[0];
958 
959 	if (saveNum > 999)
960 		return 6;
961 
962 	const char *description = _vm->_dat->getObjectString(descObjectIndex);
963 	Common::String filename = _vm->getSavegameFilename(saveNum);
964 	return _vm->_dat->savegame(filename.c_str(), description, version);
965 
966 }
967 
sfLoadGame(int16 argc,int16 * argv)968 int16 ScriptFunctions::sfLoadGame(int16 argc, int16 *argv) {
969 
970 	int16 saveNum = argv[1];
971 	int16 version = argv[0];
972 
973 	if (saveNum > 999)
974 		return 1;
975 
976 	Common::String filename = _vm->getSavegameFilename(saveNum);
977 	return _vm->_dat->loadgame(filename.c_str(), version);
978 
979 }
980 
sfGetGameDescription(int16 argc,int16 * argv)981 int16 ScriptFunctions::sfGetGameDescription(int16 argc, int16 *argv) {
982 
983 	int16 descObjectIndex = argv[2];
984 	int16 saveNum = argv[1];
985 	int16 version = argv[0];
986 	Common::String description;
987 
988 	if (saveNum > 999)
989 		return 1;
990 
991 	Common::String filename = _vm->getSavegameFilename(saveNum);
992 
993 	if (_vm->_dat->getSavegameDescription(filename.c_str(), description, version)) {
994 		_vm->_dat->setObjectString(descObjectIndex, description.c_str());
995 		return 0;
996 	} else {
997 		_vm->_dat->setObjectString(descObjectIndex, "");
998 		return 1;
999 	}
1000 
1001 }
1002 
sfShakeScreen(int16 argc,int16 * argv)1003 int16 ScriptFunctions::sfShakeScreen(int16 argc, int16 *argv) {
1004 	// TODO: Used in RTZ
1005 	warning("Unimplemented opcode: sfShakeScreen");
1006 	return 0;
1007 }
1008 
sfPlaceMenu(int16 argc,int16 * argv)1009 int16 ScriptFunctions::sfPlaceMenu(int16 argc, int16 *argv) {
1010 	// Never used in LGOP2, RTZ, Manhole:NE
1011 	warning("Unimplemented opcode: sfPlaceMenu");
1012 	return 0;
1013 }
1014 
sfSetSoundVolume(int16 argc,int16 * argv)1015 int16 ScriptFunctions::sfSetSoundVolume(int16 argc, int16 *argv) {
1016 	_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, argv[0] * 25);
1017 	_vm->_mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, argv[0] * 25);
1018 	return 0;
1019 }
1020 
sfGetSynthType(int16 argc,int16 * argv)1021 int16 ScriptFunctions::sfGetSynthType(int16 argc, int16 *argv) {
1022 	// 0 = Default
1023 	// 1 = PCSPKR
1024 	// 2 = SBFM/ADLIB
1025 	// 3 = ADLIBG
1026 	// 4 = MT32MPU
1027 
1028 	// There doesn't seem to be any difference in the music no matter what this returns
1029 
1030 	//warning("Unimplemented opcode: sfGetSynthType");
1031 	return 0;
1032 }
1033 
sfIsSlowSystem(int16 argc,int16 * argv)1034 int16 ScriptFunctions::sfIsSlowSystem(int16 argc, int16 *argv) {
1035 	//warning("Unimplemented opcode: sfIsSlowSystem");
1036 	// NOTE: In the original engine this value is set via a command-line parameter
1037 	// I don't think it's needed here
1038 	// Update: I believe this is used to determine which version of the intro/ending to show
1039 	// There are 2 versions of each video: one with sound, and one without
1040 	// An example is FINTRO00.PMV (with sound) and FINTRO01.PMV (without sound)
1041 	// One could maybe think about returning 1 here on actually slower systems.
1042 	return 0;
1043 }
1044 
1045 } // End of namespace Made
1046