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