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.
409 lines
9.5 KiB
C++
409 lines
9.5 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/>.
|
|
*
|
|
*/
|
|
|
|
// Video script opcodes for Simon1/Simon2
|
|
|
|
|
|
#include "agos/agos.h"
|
|
#include "agos/intern.h"
|
|
#include "agos/sound.h"
|
|
|
|
#include "common/endian.h"
|
|
#include "common/system.h"
|
|
|
|
#include "graphics/surface.h"
|
|
#include "graphics/palette.h"
|
|
|
|
namespace AGOS {
|
|
|
|
void AGOSEngine_Elvira2::setupVideoOpcodes(VgaOpcodeProc *op) {
|
|
AGOSEngine::setupVideoOpcodes(op);
|
|
|
|
op[17] = &AGOSEngine::vc17_waitEnd;
|
|
op[19] = &AGOSEngine::vc19_loop;
|
|
op[22] = &AGOSEngine::vc22_setPalette;
|
|
op[28] = &AGOSEngine::vc28_playSFX;
|
|
op[32] = &AGOSEngine::vc32_saveScreen;
|
|
op[37] = &AGOSEngine::vc37_pokePalette;
|
|
op[45] = &AGOSEngine::vc45_setWindowPalette;
|
|
op[46] = &AGOSEngine::vc46_setPaletteSlot1;
|
|
op[47] = &AGOSEngine::vc47_setPaletteSlot2;
|
|
op[48] = &AGOSEngine::vc48_setPaletteSlot3;
|
|
op[53] = &AGOSEngine::vc53_dissolveIn;
|
|
op[54] = &AGOSEngine::vc54_dissolveOut;
|
|
op[57] = &AGOSEngine::vc57_blackPalette;
|
|
op[56] = &AGOSEngine::vc56_fullScreen;
|
|
op[58] = &AGOSEngine::vc58_checkCodeWheel;
|
|
op[59] = &AGOSEngine::vc59_ifEGA;
|
|
}
|
|
|
|
void AGOSEngine::vc43_ifBitSet() {
|
|
if (!getBitFlag(vcReadNextWord())) {
|
|
vcSkipNextInstruction();
|
|
}
|
|
}
|
|
|
|
void AGOSEngine::vc44_ifBitClear() {
|
|
if (getBitFlag(vcReadNextWord())) {
|
|
vcSkipNextInstruction();
|
|
}
|
|
}
|
|
|
|
void AGOSEngine::vc45_setWindowPalette() {
|
|
uint16 num = vcReadNextWord();
|
|
uint16 color = vcReadNextWord();
|
|
|
|
const uint16 *vlut = &_videoWindows[num * 4];
|
|
uint8 width = vlut[2] * 8;
|
|
uint8 height = vlut[3];
|
|
|
|
if (num == 4) {
|
|
byte *dst = (byte *)_window4BackScn->getPixels();
|
|
|
|
for (uint8 h = 0; h < height; h++) {
|
|
for (uint8 w = 0; w < width; w++) {
|
|
uint16 val = READ_LE_UINT16(dst + w * 2);
|
|
val &= 0xF0F;
|
|
val |= color * 16;
|
|
WRITE_LE_UINT16(dst + w * 2, val);
|
|
}
|
|
dst += width * 2;
|
|
}
|
|
} else {
|
|
Graphics::Surface *screen = getBackendSurface();
|
|
byte *dst = (byte *)screen->getBasePtr(vlut[0] * 16, vlut[1]);
|
|
|
|
if (getGameType() == GType_ELVIRA2 && num == 7) {
|
|
dst -= 8;
|
|
width += 4;
|
|
}
|
|
|
|
for (uint8 h = 0; h < height; h++) {
|
|
for (uint8 w = 0; w < width; w++) {
|
|
uint16 val = READ_LE_UINT16(dst + w * 2);
|
|
val &= 0xF0F;
|
|
val |= color * 16;
|
|
WRITE_LE_UINT16(dst + w * 2, val);
|
|
}
|
|
dst += screen->pitch;
|
|
}
|
|
|
|
updateBackendSurface();
|
|
}
|
|
}
|
|
|
|
void AGOSEngine::setPaletteSlot(uint16 srcOffs, uint8 dstOffs) {
|
|
byte *offs, *palptr, *src;
|
|
uint16 num;
|
|
|
|
palptr = _displayPalette + dstOffs * 3 * 16;
|
|
offs = _curVgaFile1 + READ_BE_UINT16(_curVgaFile1 + 6);
|
|
src = offs + srcOffs * 32;
|
|
num = 16;
|
|
|
|
do {
|
|
uint16 color = READ_BE_UINT16(src);
|
|
palptr[0] = ((color & 0xf00) >> 8) * 32;
|
|
palptr[1] = ((color & 0x0f0) >> 4) * 32;
|
|
palptr[2] = ((color & 0x00f) >> 0) * 32;
|
|
|
|
palptr += 3;
|
|
src += 2;
|
|
} while (--num);
|
|
|
|
_paletteFlag = 2;
|
|
}
|
|
|
|
void AGOSEngine::vc46_setPaletteSlot1() {
|
|
uint16 srcOffs = vcReadNextWord();
|
|
setPaletteSlot(srcOffs, 1);
|
|
}
|
|
|
|
void AGOSEngine::vc47_setPaletteSlot2() {
|
|
uint16 srcOffs = vcReadNextWord();
|
|
setPaletteSlot(srcOffs, 2);
|
|
}
|
|
|
|
void AGOSEngine::vc48_setPaletteSlot3() {
|
|
uint16 srcOffs = vcReadNextWord();
|
|
setPaletteSlot(srcOffs, 3);
|
|
}
|
|
|
|
void AGOSEngine::vc49_setBit() {
|
|
uint16 bit = vcReadNextWord();
|
|
if (getGameType() == GType_FF && bit == 82) {
|
|
_variableArrayPtr = _variableArray2;
|
|
}
|
|
setBitFlag(bit, true);
|
|
}
|
|
|
|
void AGOSEngine::vc50_clearBit() {
|
|
uint16 bit = vcReadNextWord();
|
|
if (getGameType() == GType_FF && bit == 82) {
|
|
_variableArrayPtr = _variableArray;
|
|
}
|
|
setBitFlag(bit, false);
|
|
}
|
|
|
|
void AGOSEngine::vc51_enableBox() {
|
|
enableBox(vcReadNextWord());
|
|
}
|
|
|
|
void AGOSEngine::vc52_playSound() {
|
|
bool ambient = false;
|
|
|
|
uint16 sound = vcReadNextWord();
|
|
if (sound >= 0x8000) {
|
|
ambient = true;
|
|
sound = -sound;
|
|
}
|
|
|
|
if (getGameType() == GType_FF || getGameType() == GType_PP) {
|
|
int16 pan = vcReadNextWord();
|
|
int16 vol = vcReadNextWord();
|
|
|
|
if (ambient)
|
|
loadSound(sound, pan, vol, Sound::TYPE_AMBIENT);
|
|
else
|
|
loadSound(sound, pan, vol, Sound::TYPE_SFX);
|
|
} else if (getGameType() == GType_SIMON2) {
|
|
if (ambient)
|
|
_sound->playAmbient(sound);
|
|
else
|
|
_sound->playEffects(sound);
|
|
} else if (getFeatures() & GF_TALKIE) {
|
|
_sound->playEffects(sound);
|
|
} else {
|
|
// Waxworks uses 2 opcodes to play SFX: vc28 for digital SFX and vc52
|
|
// for MIDI SFX. If a sound effect has both a MIDI and a digital
|
|
// version, both opcodes are triggered. Only one of them should play
|
|
// a sound effect.
|
|
playSfx(sound, 0, 0, false, getGameType() == GType_WW);
|
|
}
|
|
}
|
|
|
|
void AGOSEngine::vc53_dissolveIn() {
|
|
uint16 num = vcReadNextWord();
|
|
uint16 speed = vcReadNextWord() + 1;
|
|
|
|
byte *src, *dst, *srcOffs, *srcOffs2, *dstOffs, *dstOffs2;
|
|
int16 xoffs, yoffs;
|
|
uint8 color = 0;
|
|
|
|
// Only uses Video Window 4
|
|
num = 4;
|
|
|
|
uint16 dissolveX = _videoWindows[num * 4 + 2] * 8;
|
|
uint16 dissolveY = (_videoWindows[num * 4 + 3] + 1) / 2;
|
|
uint16 dissolveCheck = dissolveY * dissolveX * 4;
|
|
uint16 dissolveDelay = dissolveCheck * 2 / speed;
|
|
uint16 dissolveCount = dissolveCheck * 2 / speed;
|
|
|
|
int16 x = _videoWindows[num * 4 + 0] * 16;
|
|
int16 y = _videoWindows[num * 4 + 1];
|
|
|
|
uint16 count = dissolveCheck * 2;
|
|
while (count--) {
|
|
Graphics::Surface *screen = getBackendSurface();
|
|
byte *dstPtr = (byte *)screen->getBasePtr(x, y);
|
|
|
|
yoffs = _rnd.getRandomNumber(dissolveY);
|
|
dst = dstPtr + yoffs * screen->pitch;
|
|
src = (byte *)_window4BackScn->getBasePtr(0, yoffs);
|
|
|
|
xoffs = _rnd.getRandomNumber(dissolveX);
|
|
dst += xoffs;
|
|
src += xoffs;
|
|
|
|
*dst &= color;
|
|
*dst |= *src & 0xF;
|
|
|
|
dstOffs = dst;
|
|
srcOffs = src;
|
|
|
|
xoffs = dissolveX * 2 - 1 - (xoffs * 2);
|
|
dst += xoffs;
|
|
src += xoffs;
|
|
|
|
*dst &= color;
|
|
*dst |= *src & 0xF;
|
|
|
|
srcOffs2 = src;
|
|
dstOffs2 = dst;
|
|
|
|
yoffs = (dissolveY - 1) * 2 - (yoffs * 2);
|
|
src = srcOffs + yoffs * _window4BackScn->pitch;
|
|
dst = dstOffs + yoffs * screen->pitch;
|
|
|
|
color = 0xF0;
|
|
*dst &= color;
|
|
*dst |= *src & 0xF;
|
|
|
|
dst = dstOffs2 + yoffs * screen->pitch;
|
|
src = srcOffs2 + yoffs * _window4BackScn->pitch;
|
|
|
|
*dst &= color;
|
|
*dst |= *src & 0xF;
|
|
|
|
updateBackendSurface();
|
|
|
|
dissolveCount--;
|
|
if (!dissolveCount) {
|
|
if (count >= dissolveCheck)
|
|
dissolveDelay++;
|
|
|
|
dissolveCount = dissolveDelay;
|
|
delay(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AGOSEngine::vc54_dissolveOut() {
|
|
uint16 num = vcReadNextWord();
|
|
uint16 color = vcReadNextWord();
|
|
uint16 speed = vcReadNextWord() + 1;
|
|
|
|
byte *dst, *dstOffs;
|
|
int16 xoffs, yoffs;
|
|
|
|
uint16 dissolveX = _videoWindows[num * 4 + 2] * 8;
|
|
uint16 dissolveY = (_videoWindows[num * 4 + 3] + 1) / 2;
|
|
uint16 dissolveCheck = dissolveY * dissolveX * 4;
|
|
uint16 dissolveDelay = dissolveCheck * 2 / speed;
|
|
uint16 dissolveCount = dissolveCheck * 2 / speed;
|
|
|
|
int16 x = _videoWindows[num * 4 + 0] * 16;
|
|
int16 y = _videoWindows[num * 4 + 1];
|
|
|
|
uint16 count = dissolveCheck * 2;
|
|
while (count--) {
|
|
Graphics::Surface *screen = getBackendSurface();
|
|
byte *dstPtr = (byte *)screen->getBasePtr(x, y);
|
|
color |= dstPtr[0] & 0xF0;
|
|
|
|
yoffs = _rnd.getRandomNumber(dissolveY);
|
|
xoffs = _rnd.getRandomNumber(dissolveX);
|
|
dst = dstPtr + xoffs + yoffs * screen->pitch;
|
|
*dst = color;
|
|
|
|
dstOffs = dst;
|
|
|
|
xoffs = dissolveX * 2 - 1 - (xoffs * 2);
|
|
dst += xoffs;
|
|
*dst = color;
|
|
|
|
yoffs = (dissolveY - 1) * 2 - (yoffs * 2);
|
|
dst = dstOffs + yoffs * screen->pitch;
|
|
*dst = color;
|
|
|
|
dst += xoffs;
|
|
*dst = color;
|
|
|
|
updateBackendSurface();
|
|
|
|
dissolveCount--;
|
|
if (!dissolveCount) {
|
|
if (count >= dissolveCheck)
|
|
dissolveDelay++;
|
|
|
|
dissolveCount = dissolveDelay;
|
|
delay(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void AGOSEngine::vc55_moveBox() {
|
|
HitArea *ha = _hitAreas;
|
|
uint count = ARRAYSIZE(_hitAreas);
|
|
uint16 id = vcReadNextWord();
|
|
int16 x = vcReadNextWord();
|
|
int16 y = vcReadNextWord();
|
|
|
|
for (;;) {
|
|
if (ha->id == id) {
|
|
ha->x += x;
|
|
ha->y += y;
|
|
break;
|
|
}
|
|
ha++;
|
|
if (!--count)
|
|
break;
|
|
}
|
|
|
|
_needHitAreaRecalc++;
|
|
}
|
|
|
|
void AGOSEngine::fullFade() {
|
|
uint8 *srcPal, *dstPal;
|
|
int c, p;
|
|
|
|
for (c = 64; c != 0; c --) {
|
|
srcPal = _curVgaFile2 + 32;
|
|
dstPal = _currentPalette;
|
|
for (p = 768; p !=0; p -= 3) {
|
|
uint8 r = srcPal[0] * 4;
|
|
if (dstPal[0] != r)
|
|
dstPal[0] += 4;
|
|
uint8 g = srcPal[1] * 4;
|
|
if (dstPal[1] != g)
|
|
dstPal[1] += 4;
|
|
uint8 b = srcPal[2] * 4;
|
|
if (dstPal[2] != b)
|
|
dstPal[2] += 4;
|
|
srcPal += 3;
|
|
dstPal += 3;
|
|
}
|
|
_system->getPaletteManager()->setPalette(_currentPalette, 0, 256);
|
|
delay(5);
|
|
}
|
|
}
|
|
|
|
void AGOSEngine::vc56_fullScreen() {
|
|
Graphics::Surface *screen = getBackendSurface();
|
|
byte *dst = (byte *)screen->getPixels();
|
|
byte *src = _curVgaFile2 + 800;
|
|
|
|
for (int i = 0; i < _screenHeight; i++) {
|
|
memcpy(dst, src, _screenWidth);
|
|
src += 320;
|
|
dst += screen->pitch;
|
|
}
|
|
updateBackendSurface();
|
|
|
|
fullFade();
|
|
}
|
|
|
|
void AGOSEngine::vc57_blackPalette() {
|
|
memset(_currentPalette, 0, sizeof(_currentPalette));
|
|
_system->getPaletteManager()->setPalette(_currentPalette, 0, 256);
|
|
}
|
|
|
|
void AGOSEngine::vc58_checkCodeWheel() {
|
|
_variableArray[0] = 0;
|
|
}
|
|
|
|
void AGOSEngine::vc59_ifEGA() {
|
|
// Skip if not EGA
|
|
vcSkipNextInstruction();
|
|
}
|
|
|
|
} // End of namespace AGOS
|