mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
Waxworks DOS has both digital and limited MIDI SFX. The game scripts use two different opcodes for trigging digital and MIDI SFX; if a sound effect has both a digital and a MIDI version, both opcodes are triggered. When digital SFX are active, ScummVM would try to handle both the digital and the MIDI opcode by playing a digital SFX. This would crash the game if the MIDI SFX ID was invalid as a digital SFX ID. Strangely enough, this bug also seems to affect the Amiga version. Apparently both opcodes are still in the scripts of this version, even though it does not support MIDI at all AFAIK. This commit fixes the issue by only playing the MIDI SFX if digital SFX are turned off and ignoring them otherwise.
577 lines
14 KiB
C++
577 lines
14 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#include "common/system.h"
|
|
#include "common/localization.h"
|
|
|
|
#include "graphics/palette.h"
|
|
|
|
#include "agos/agos.h"
|
|
#include "agos/intern.h"
|
|
#include "agos/sound.h"
|
|
|
|
namespace AGOS {
|
|
|
|
#define OPCODE(x) _OPCODE(AGOSEngine_Simon1, x)
|
|
|
|
void AGOSEngine_Simon1::setupOpcodes() {
|
|
static const OpcodeEntrySimon1 opcodes[] = {
|
|
/* 00 */
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_at),
|
|
OPCODE(o_notAt),
|
|
OPCODE(o_invalid),
|
|
/* 04 */
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_carried),
|
|
OPCODE(o_notCarried),
|
|
OPCODE(o_isAt),
|
|
/* 08 */
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_zero),
|
|
/* 12 */
|
|
OPCODE(o_notZero),
|
|
OPCODE(o_eq),
|
|
OPCODE(o_notEq),
|
|
OPCODE(o_gt),
|
|
/* 16 */
|
|
OPCODE(o_lt),
|
|
OPCODE(o_eqf),
|
|
OPCODE(o_notEqf),
|
|
OPCODE(o_ltf),
|
|
/* 20 */
|
|
OPCODE(o_gtf),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_chance),
|
|
/* 24 */
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_isRoom),
|
|
OPCODE(o_isObject),
|
|
OPCODE(o_state),
|
|
/* 28 */
|
|
OPCODE(o_oflag),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_destroy),
|
|
/* 32 */
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_place),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
/* 36 */
|
|
OPCODE(o_copyff),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
/* 40 */
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_clear),
|
|
OPCODE(o_let),
|
|
OPCODE(o_add),
|
|
/* 44 */
|
|
OPCODE(o_sub),
|
|
OPCODE(o_addf),
|
|
OPCODE(o_subf),
|
|
OPCODE(o_mul),
|
|
/* 48 */
|
|
OPCODE(o_div),
|
|
OPCODE(o_mulf),
|
|
OPCODE(o_divf),
|
|
OPCODE(o_mod),
|
|
/* 52 */
|
|
OPCODE(o_modf),
|
|
OPCODE(o_random),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_goto),
|
|
/* 56 */
|
|
OPCODE(o_oset),
|
|
OPCODE(o_oclear),
|
|
OPCODE(o_putBy),
|
|
OPCODE(o_inc),
|
|
/* 60 */
|
|
OPCODE(o_dec),
|
|
OPCODE(o_setState),
|
|
OPCODE(o_print),
|
|
OPCODE(o_message),
|
|
/* 64 */
|
|
OPCODE(o_msg),
|
|
OPCODE(oww_addTextBox),
|
|
OPCODE(oww_setShortText),
|
|
OPCODE(oww_setLongText),
|
|
/* 68 */
|
|
OPCODE(o_end),
|
|
OPCODE(o_done),
|
|
OPCODE(oww_printLongText),
|
|
OPCODE(o_process),
|
|
/* 72 */
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
/* 76 */
|
|
OPCODE(o_when),
|
|
OPCODE(o_if1),
|
|
OPCODE(o_if2),
|
|
OPCODE(o_isCalled),
|
|
/* 80 */
|
|
OPCODE(o_is),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_debug),
|
|
OPCODE(oe1_rescan),
|
|
/* 84 */
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_comment),
|
|
/* 88 */
|
|
OPCODE(o_haltAnimation),
|
|
OPCODE(o_restartAnimation),
|
|
OPCODE(o_getParent),
|
|
OPCODE(o_getNext),
|
|
/* 92 */
|
|
OPCODE(o_getChildren),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
/* 96 */
|
|
OPCODE(o_picture),
|
|
OPCODE(o_loadZone),
|
|
OPCODE(os1_animate),
|
|
OPCODE(oe1_stopAnimate),
|
|
/* 100 */
|
|
OPCODE(o_killAnimate),
|
|
OPCODE(o_defWindow),
|
|
OPCODE(o_window),
|
|
OPCODE(o_cls),
|
|
/* 104 */
|
|
OPCODE(o_closeWindow),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_addBox),
|
|
/* 108 */
|
|
OPCODE(o_delBox),
|
|
OPCODE(o_enableBox),
|
|
OPCODE(o_disableBox),
|
|
OPCODE(o_moveBox),
|
|
/* 112 */
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_doIcons),
|
|
OPCODE(o_isClass),
|
|
/* 116 */
|
|
OPCODE(o_setClass),
|
|
OPCODE(o_unsetClass),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_waitSync),
|
|
/* 120 */
|
|
OPCODE(o_sync),
|
|
OPCODE(o_defObj),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
/* 124 */
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_here),
|
|
OPCODE(o_doClassIcons),
|
|
OPCODE(o_playTune),
|
|
/* 128 */
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_setAdjNoun),
|
|
OPCODE(o_invalid),
|
|
/* 132 */
|
|
OPCODE(o_saveUserGame),
|
|
OPCODE(o_loadUserGame),
|
|
OPCODE(o_invalid),
|
|
OPCODE(os1_pauseGame),
|
|
/* 136 */
|
|
OPCODE(o_copysf),
|
|
OPCODE(o_restoreIcons),
|
|
OPCODE(o_freezeZones),
|
|
OPCODE(o_placeNoIcons),
|
|
/* 140 */
|
|
OPCODE(o_clearTimers),
|
|
OPCODE(o_setDollar),
|
|
OPCODE(o_isBox),
|
|
OPCODE(oe2_doTable),
|
|
/* 144 */
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
/* 148 */
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(oe2_storeItem),
|
|
/* 152 */
|
|
OPCODE(oe2_getItem),
|
|
OPCODE(oe2_bSet),
|
|
OPCODE(oe2_bClear),
|
|
OPCODE(oe2_bZero),
|
|
/* 156 */
|
|
OPCODE(oe2_bNotZero),
|
|
OPCODE(oe2_getOValue),
|
|
OPCODE(oe2_setOValue),
|
|
OPCODE(o_invalid),
|
|
/* 160 */
|
|
OPCODE(oe2_ink),
|
|
OPCODE(os1_screenTextBox),
|
|
OPCODE(os1_screenTextMsg),
|
|
OPCODE(os1_playEffect),
|
|
/* 164 */
|
|
OPCODE(oe2_getDollar2),
|
|
OPCODE(oe2_isAdjNoun),
|
|
OPCODE(oe2_b2Set),
|
|
OPCODE(oe2_b2Clear),
|
|
/* 168 */
|
|
OPCODE(oe2_b2Zero),
|
|
OPCODE(oe2_b2NotZero),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
/* 172 */
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(o_invalid),
|
|
OPCODE(oww_lockZones),
|
|
/* 176 */
|
|
OPCODE(oww_unlockZones),
|
|
OPCODE(os1_screenTextPObj),
|
|
OPCODE(os1_getPathPosn),
|
|
OPCODE(os1_scnTxtLongText),
|
|
/* 180 */
|
|
OPCODE(os1_mouseOn),
|
|
OPCODE(os1_mouseOff),
|
|
OPCODE(os1_loadBeard),
|
|
OPCODE(os1_unloadBeard),
|
|
/* 184 */
|
|
OPCODE(os1_unloadZone),
|
|
OPCODE(os1_loadStrings),
|
|
OPCODE(os1_unfreezeZones),
|
|
OPCODE(os1_specialFade),
|
|
};
|
|
|
|
_opcodesSimon1 = opcodes;
|
|
_numOpcodes = 188;
|
|
}
|
|
|
|
void AGOSEngine_Simon1::executeOpcode(int opcode) {
|
|
OpcodeProcSimon1 op = _opcodesSimon1[opcode].proc;
|
|
(this->*op) ();
|
|
}
|
|
|
|
// -----------------------------------------------------------------------
|
|
// Simon 1 Opcodes
|
|
// -----------------------------------------------------------------------
|
|
|
|
void AGOSEngine_Simon1::os1_animate() {
|
|
// 98: animate
|
|
uint16 vgaSpriteId = getVarOrWord();
|
|
uint16 windowNum = getVarOrByte();
|
|
int16 x = getVarOrWord();
|
|
int16 y = getVarOrWord();
|
|
uint16 palette = (getVarOrWord() & 15);
|
|
|
|
if (getFeatures() & GF_TALKIE && vgaSpriteId >= 400) {
|
|
_lastVgaWaitFor = 0;
|
|
}
|
|
|
|
_videoLockOut |= 0x40;
|
|
animate(windowNum, vgaSpriteId / 100, vgaSpriteId, x, y, palette);
|
|
_videoLockOut &= ~0x40;
|
|
}
|
|
|
|
void AGOSEngine_Simon1::os1_pauseGame() {
|
|
// 135: pause game
|
|
_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
|
|
|
|
Common::KeyCode keyYes, keyNo;
|
|
|
|
Common::getLanguageYesNo(_language, keyYes, keyNo);
|
|
|
|
while (!shouldQuit()) {
|
|
delay(1);
|
|
if (_keyPressed.keycode == keyYes)
|
|
quitGame();
|
|
else if (_keyPressed.keycode == keyNo)
|
|
break;
|
|
}
|
|
|
|
_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
|
|
}
|
|
|
|
void AGOSEngine_Simon1::os1_screenTextBox() {
|
|
// 161: setup text
|
|
TextLocation *tl = getTextLocation(getVarOrByte());
|
|
|
|
tl->x = getVarOrWord();
|
|
tl->y = getVarOrByte();
|
|
tl->width = getVarOrWord();
|
|
}
|
|
|
|
void AGOSEngine_Simon1::os1_screenTextMsg() {
|
|
// 162: print string
|
|
uint vgaSpriteId = getVarOrByte();
|
|
uint color = getVarOrByte();
|
|
uint stringId = getNextStringID();
|
|
const byte *stringPtr = nullptr;
|
|
uint speechId = 0;
|
|
TextLocation *tl;
|
|
|
|
if (stringId != 0xFFFF)
|
|
stringPtr = getStringPtrByID(stringId);
|
|
|
|
if (getFeatures() & GF_TALKIE) {
|
|
if (getGameType() == GType_FF || getGameType() == GType_PP)
|
|
speechId = (uint16)getVarOrWord();
|
|
else
|
|
speechId = (uint16)getNextWord();
|
|
}
|
|
|
|
if (getGameType() == GType_FF || getGameType() == GType_PP)
|
|
vgaSpriteId = 1;
|
|
|
|
tl = getTextLocation(vgaSpriteId);
|
|
if (_speech && speechId != 0)
|
|
playSpeech(speechId, vgaSpriteId);
|
|
if (((getGameType() == GType_SIMON2 && (getFeatures() & GF_TALKIE)) || getGameType() == GType_FF) &&
|
|
speechId == 0) {
|
|
stopAnimateSimon2(2, vgaSpriteId + 2);
|
|
}
|
|
|
|
// WORKAROUND: Several strings in the French version of Simon the Sorcerer 1 set the incorrect width,
|
|
// causing crashes, or glitches in subtitles. See bug #6014 for example.
|
|
if (getGameType() == GType_SIMON1 && _language == Common::FR_FRA) {
|
|
if ((getFeatures() & GF_TALKIE) && stringId == 33219)
|
|
tl->width = 96;
|
|
if (!(getFeatures() & GF_TALKIE) && stringId == 33245)
|
|
tl->width = 96;
|
|
}
|
|
|
|
if (stringPtr != nullptr && stringPtr[0] != 0 && (speechId == 0 || _subtitles))
|
|
printScreenText(vgaSpriteId, color, (const char *)stringPtr, tl->x, tl->y, tl->width);
|
|
|
|
}
|
|
|
|
void AGOSEngine_Simon1::os1_playEffect() {
|
|
// 163: play sound
|
|
uint16 soundId = getVarOrWord();
|
|
|
|
if (getGameId() == GID_SIMON1DOS)
|
|
playSfx(soundId, 0, 0);
|
|
else
|
|
_sound->playEffects(soundId);
|
|
}
|
|
|
|
void AGOSEngine_Simon1::os1_screenTextPObj() {
|
|
// 177: inventory descriptions
|
|
uint vgaSpriteId = getVarOrByte();
|
|
uint color = getVarOrByte();
|
|
|
|
SubObject *subObject = (SubObject *)findChildOfType(getNextItemPtr(), kObjectType);
|
|
if (getFeatures() & GF_TALKIE) {
|
|
if (subObject != nullptr && subObject->objectFlags & kOFVoice) {
|
|
uint offs = getOffsetOfChild2Param(subObject, kOFVoice);
|
|
playSpeech(subObject->objectFlagValue[offs], vgaSpriteId);
|
|
} else if (subObject != nullptr && subObject->objectFlags & kOFNumber) {
|
|
uint offs = getOffsetOfChild2Param(subObject, kOFNumber);
|
|
playSpeech(subObject->objectFlagValue[offs] + 3550, vgaSpriteId);
|
|
}
|
|
}
|
|
|
|
if (subObject != nullptr && subObject->objectFlags & kOFText && _subtitles) {
|
|
const char *stringPtr = (const char *)getStringPtrByID(subObject->objectFlagValue[0]);
|
|
TextLocation *tl = getTextLocation(vgaSpriteId);
|
|
char buf[256];
|
|
int j, k;
|
|
|
|
if (subObject->objectFlags & kOFNumber) {
|
|
if (_language == Common::HE_ISR) {
|
|
j = subObject->objectFlagValue[getOffsetOfChild2Param(subObject, kOFNumber)];
|
|
k = (j % 10) * 10;
|
|
k += j / 10;
|
|
if (!(j % 10))
|
|
sprintf(buf,"0%d%s", k, stringPtr);
|
|
else
|
|
sprintf(buf,"%d%s", k, stringPtr);
|
|
} else {
|
|
sprintf(buf,"%d%s", subObject->objectFlagValue[getOffsetOfChild2Param(subObject, kOFNumber)], stringPtr);
|
|
}
|
|
stringPtr = buf;
|
|
}
|
|
if (stringPtr != nullptr && stringPtr[0] != 0)
|
|
printScreenText(vgaSpriteId, color, stringPtr, tl->x, tl->y, tl->width);
|
|
}
|
|
}
|
|
|
|
void AGOSEngine_Simon1::os1_getPathPosn() {
|
|
// 178: path find
|
|
uint x = getVarOrWord();
|
|
uint y = getVarOrWord();
|
|
uint var_1 = getVarOrByte();
|
|
uint var_2 = getVarOrByte();
|
|
|
|
const uint16 *p;
|
|
uint i, j;
|
|
uint prev_i;
|
|
uint x_diff, y_diff;
|
|
uint best_i = 0, best_j = 0, best_dist = 0xFFFFFFFF;
|
|
uint maxPath = (getGameType() == GType_FF || getGameType() == GType_PP) ? 100 : 20;
|
|
|
|
if (getGameType() == GType_FF || getGameType() == GType_PP) {
|
|
x += _scrollX;
|
|
y += _scrollY;
|
|
} else if (getGameType() == GType_SIMON2) {
|
|
x += _scrollX * 8;
|
|
}
|
|
|
|
int end = (getGameType() == GType_FF) ? 9999 : 999;
|
|
prev_i = maxPath + 1 - readVariable(12);
|
|
for (i = maxPath; i != 0; --i) {
|
|
p = (const uint16 *)_pathFindArray[maxPath - i];
|
|
if (!p)
|
|
continue;
|
|
for (j = 0; readUint16Wrapper(&p[0]) != end; j++, p += 2) {
|
|
x_diff = ABS((int16)(readUint16Wrapper(&p[0]) - x));
|
|
y_diff = ABS((int16)(readUint16Wrapper(&p[1]) - 12 - y));
|
|
|
|
if (x_diff < y_diff) {
|
|
x_diff /= 4;
|
|
y_diff *= 4;
|
|
}
|
|
x_diff += y_diff /= 4;
|
|
|
|
if ((x_diff < best_dist) || ((x_diff == best_dist) && (prev_i == i))) {
|
|
best_dist = x_diff;
|
|
best_i = maxPath + 1 - i;
|
|
best_j = j;
|
|
}
|
|
}
|
|
}
|
|
|
|
writeVariable(var_1, best_i);
|
|
writeVariable(var_2, best_j);
|
|
}
|
|
|
|
void AGOSEngine_Simon1::os1_scnTxtLongText() {
|
|
// 179: conversation responses and room descriptions
|
|
uint vgaSpriteId = getVarOrByte();
|
|
uint color = getVarOrByte();
|
|
uint stringId = getVarOrByte();
|
|
uint speechId = 0;
|
|
TextLocation *tl;
|
|
|
|
const char *stringPtr = (const char *)getStringPtrByID(_longText[stringId]);
|
|
if (getFeatures() & GF_TALKIE)
|
|
speechId = _longSound[stringId];
|
|
|
|
if (getGameType() == GType_FF || getGameType() == GType_PP)
|
|
vgaSpriteId = 1;
|
|
tl = getTextLocation(vgaSpriteId);
|
|
|
|
if (_speech && speechId != 0)
|
|
playSpeech(speechId, vgaSpriteId);
|
|
if (stringPtr != nullptr && stringPtr[0] != 0 && _subtitles)
|
|
printScreenText(vgaSpriteId, color, stringPtr, tl->x, tl->y, tl->width);
|
|
}
|
|
|
|
void AGOSEngine_Simon1::os1_mouseOn() {
|
|
// 180: force mouseOn
|
|
_mouseHideCount = 0;
|
|
}
|
|
|
|
void AGOSEngine_Simon1::os1_mouseOff() {
|
|
// 181: force mouseOff
|
|
scriptMouseOff();
|
|
}
|
|
|
|
void AGOSEngine_Simon1::os1_loadBeard() {
|
|
// 182: load beard
|
|
if (_beardLoaded == false) {
|
|
_beardLoaded = true;
|
|
_videoLockOut |= 0x8000;
|
|
loadVGABeardFile(328);
|
|
_videoLockOut &= ~0x8000;
|
|
}
|
|
}
|
|
|
|
void AGOSEngine_Simon1::os1_unloadBeard() {
|
|
// 183: unload beard
|
|
if (_beardLoaded == true) {
|
|
_beardLoaded = false;
|
|
_videoLockOut |= 0x8000;
|
|
loadVGABeardFile(23);
|
|
_videoLockOut &= ~0x8000;
|
|
}
|
|
}
|
|
|
|
void AGOSEngine_Simon1::os1_unloadZone() {
|
|
// 184: unload zone
|
|
uint a = getVarOrWord();
|
|
VgaPointersEntry *vpe = &_vgaBufferPointers[a];
|
|
|
|
vpe->sfxFile = nullptr;
|
|
vpe->vgaFile1 = nullptr;
|
|
vpe->vgaFile2 = nullptr;
|
|
}
|
|
|
|
void AGOSEngine_Simon1::os1_loadStrings() {
|
|
// 185: load sound files
|
|
_soundFileId = getVarOrWord();
|
|
if (getPlatform() == Common::kPlatformAmiga && (getFeatures() & GF_TALKIE)) {
|
|
char buf[13];
|
|
sprintf(buf, "%d%s", _soundFileId, "Effects");
|
|
_sound->readSfxFile(buf);
|
|
sprintf(buf, "%d%s", _soundFileId, "simon");
|
|
_sound->readVoiceFile(buf);
|
|
}
|
|
}
|
|
|
|
void AGOSEngine_Simon1::os1_unfreezeZones() {
|
|
// 186: freeze zone
|
|
unfreezeBottom();
|
|
}
|
|
|
|
void AGOSEngine_Simon1::os1_specialFade() {
|
|
// 187: fade to black
|
|
uint i;
|
|
|
|
for (i = 32; i != 0; --i) {
|
|
paletteFadeOut(_currentPalette, 32, 8);
|
|
paletteFadeOut(_currentPalette + 3 * 48, 144, 8);
|
|
paletteFadeOut(_currentPalette + 3 * 208, 48, 8);
|
|
_system->getPaletteManager()->setPalette(_currentPalette, 0, 256);
|
|
delay(5);
|
|
}
|
|
|
|
memcpy(_displayPalette, _currentPalette, sizeof(_currentPalette));
|
|
}
|
|
|
|
void AGOSEngine::scriptMouseOff() {
|
|
_videoLockOut |= 0x8000;
|
|
vc34_setMouseOff();
|
|
_videoLockOut &= ~0x8000;
|
|
}
|
|
|
|
} // End of namespace AGOS
|