Files
scummvm-cursorfix/engines/awe/script.cpp
2026-02-02 04:50:13 +01:00

928 lines
24 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 "awe/gfx.h"
#include "awe/script.h"
#include "awe/resource.h"
#include "awe/video.h"
#include "awe/sfx_player.h"
#include "awe/sound.h"
#include "awe/system_stub.h"
namespace Awe {
Script::Script(Sound *snd, Resource *res, SfxPlayer *ply, Video *vid)
: _sound(snd), _res(res), _ply(ply), _vid(vid), _stub(nullptr) {
}
void Script::init() {
memset(_scriptVars, 0, sizeof(_scriptVars));
_fastMode = false;
_ply->_syncVar = &_scriptVars[VAR_MUSIC_SYNC];
_scriptPtr.byteSwap = _is3DO = (_res->getDataType() == DT_3DO);
if (_is3DO) {
_scriptVars[0xDB] = 1;
_scriptVars[0xE2] = 1;
_scriptVars[0xF2] = 6000;
} else if (_res->getDataType() != DT_15TH_EDITION && _res->getDataType() != DT_20TH_EDITION) {
_scriptVars[VAR_RANDOM_SEED] = 0; // time(0);
if (!_res->_copyProtection) {
// these 3 variables are set by the game code
_scriptVars[0xBC] = 0x10;
_scriptVars[0xC6] = 0x80;
_scriptVars[0xF2] = (_res->getDataType() == DT_AMIGA || _res->getDataType() == DT_ATARI) ? 6000 : 4000;
// these 2 variables are set by the engine executable
_scriptVars[0xDC] = 33;
}
if (_res->getDataType() == DT_DOS || _res->getDataType() == DT_WIN31) {
_scriptVars[0xE4] = 20;
}
}
}
void Script::op_movConst() {
const uint8 i = _scriptPtr.fetchByte();
const int16 n = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_movConst(0x%02X, %d)", i, n);
_scriptVars[i] = n;
}
void Script::op_mov() {
const uint8 i = _scriptPtr.fetchByte();
const uint8 j = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_mov(0x%02X, 0x%02X)", i, j);
_scriptVars[i] = _scriptVars[j];
}
void Script::op_add() {
const uint8 i = _scriptPtr.fetchByte();
const uint8 j = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_add(0x%02X, 0x%02X)", i, j);
_scriptVars[i] += _scriptVars[j];
}
void Script::op_addConst() {
if (_res->getDataType() == DT_DOS || _res->getDataType() == DT_AMIGA || _res->getDataType() == DT_ATARI) {
if (_res->_currentPart == 16006 && _scriptPtr.pc == _res->_segCode + 0x6D48) {
warning("Script::op_addConst() workaround for infinite looping gun sound");
// The script 0x27 slot 0x17 doesn't stop the gun sound from looping.
// This is a bug in the original game code, confirmed by Eric Chahi and
// addressed with the anniversary editions.
// For older releases (DOS, Amiga), we play the 'stop' sound like it is
// done in other part of the game code.
//
// 6D43: jmp(0x6CE5)
// 6D46: break
// 6D47: VAR(0x06) -= 50
//
snd_playSound(0x5B, 1, 64, 1);
}
}
const uint8 i = _scriptPtr.fetchByte();
const int16 n = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_addConst(0x%02X, %d)", i, n);
_scriptVars[i] += n;
}
void Script::op_call() {
const uint16 off = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_call(0x%X)", off);
if (_stackPtr == 0x40) {
error("Script::op_call() ec=0x%X stack overflow", 0x8F);
}
_scriptStackCalls[_stackPtr] = _scriptPtr.pc - _res->_segCode;
++_stackPtr;
_scriptPtr.pc = _res->_segCode + off;
}
void Script::op_ret() {
debugC(kDebugScript, "Script::op_ret()");
if (_stackPtr == 0) {
error("Script::op_ret() ec=0x%X stack underflow", 0x8F);
}
--_stackPtr;
_scriptPtr.pc = _res->_segCode + _scriptStackCalls[_stackPtr];
}
void Script::op_yieldTask() {
debugC(kDebugScript, "Script::op_yieldTask()");
_scriptPaused = true;
}
void Script::op_jmp() {
const uint16 off = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_jmp(0x%02X)", off);
_scriptPtr.pc = _res->_segCode + off;
}
void Script::op_installTask() {
const uint8 i = _scriptPtr.fetchByte();
const uint16 n = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_installTask(0x%X, 0x%X)", i, n);
assert(i < 0x40);
_scriptTasks[1][i] = n;
}
void Script::op_jmpIfVar() {
const uint8 i = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_jmpIfVar(0x%02X)", i);
--_scriptVars[i];
if (_scriptVars[i] != 0) {
op_jmp();
} else {
_scriptPtr.fetchWord();
}
}
void Script::op_condJmp() {
// Script patch the original interpreter triggers
// if an incorrect code is entered at the start
if (_res->_currentPart == kPartCopyProtection &&
_res->_dataType == DT_DOS &&
(_scriptPtr.pc - _res->_segCode) == 0xc4c) {
byte *script = _scriptPtr.pc;
*script = 0x81;
WRITE_BE_UINT16(script + 3, 0xcb7);
WRITE_BE_UINT16(script + 153, 0xced);
}
const uint8 op = _scriptPtr.fetchByte();
const uint8 var = _scriptPtr.fetchByte();
const int16 b = _scriptVars[var];
int16 a;
if (op & 0x80) {
a = _scriptVars[_scriptPtr.fetchByte()];
} else if (op & 0x40) {
a = _scriptPtr.fetchWord();
} else {
a = _scriptPtr.fetchByte();
}
debugC(kDebugScript, "Script::op_condJmp(%d, 0x%02X, 0x%02X) var=0x%02X", op, b, a, var);
bool expr = false;
switch (op & 7) {
case 0:
expr = (b == a);
if (!_res->_copyProtection) {
if (_res->_currentPart == kPartCopyProtection) {
//
// 0CB8: jmpIf(VAR(0x29) == VAR(0x1E), @0CD3)
// ...
//
if (var == 0x29 && (op & 0x80) != 0) {
// 4 symbols
_scriptVars[0x29] = _scriptVars[0x1E];
_scriptVars[0x2A] = _scriptVars[0x1F];
_scriptVars[0x2B] = _scriptVars[0x20];
_scriptVars[0x2C] = _scriptVars[0x21];
// counters
_scriptVars[0x32] = 6;
_scriptVars[0x64] = 20;
warning("Script::op_condJmp() bypassing protection");
expr = true;
}
}
}
break;
case 1:
expr = (b != a);
break;
case 2:
expr = (b > a);
break;
case 3:
expr = (b >= a);
break;
case 4:
expr = (b < a);
break;
case 5:
expr = (b <= a);
break;
default:
warning("Script::op_condJmp() invalid condition %d", (op & 7));
break;
}
if (expr) {
op_jmp();
if (!_is3DO && var == VAR_SCREEN_NUM && _screenNum != _scriptVars[VAR_SCREEN_NUM]) {
fixUpPalette_changeScreen(_res->_currentPart, _scriptVars[VAR_SCREEN_NUM]);
_screenNum = _scriptVars[VAR_SCREEN_NUM];
}
} else {
_scriptPtr.fetchWord();
}
}
void Script::op_setPalette() {
const uint16 i = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_changePalette(%d)", i);
const int num = i >> 8;
if (_vid->_graphics->_fixUpPalette == FIXUP_PALETTE_REDRAW) {
if (_res->_currentPart == 16001) {
if (num == 10 || num == 16) {
return;
}
}
_vid->_nextPal = num;
} else {
_vid->_nextPal = num;
}
}
void Script::op_changeTasksState() {
uint8 start = _scriptPtr.fetchByte();
const uint8 end = _scriptPtr.fetchByte();
if (end < start) {
warning("Script::op_changeTasksState() ec=0x%X (end < start)", 0x880);
return;
}
const uint8 state = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_changeTasksState(%d, %d, %d)", start, end, state);
if (state == 2) {
for (; start <= end; ++start) {
_scriptTasks[1][start] = 0xFFFE;
}
} else if (state < 2) {
for (; start <= end; ++start) {
_scriptStates[1][start] = state;
}
}
}
void Script::op_selectPage() {
const uint8 i = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_selectPage(%d)", i);
_vid->setWorkPagePtr(i);
}
void Script::op_fillPage() {
const uint8 i = _scriptPtr.fetchByte();
const uint8 color = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_fillPage(%d, %d)", i, color);
_vid->fillPage(i, color);
}
void Script::op_copyPage() {
const uint8 i = _scriptPtr.fetchByte();
const uint8 j = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_copyPage(%d, %d)", i, j);
_vid->copyPage(i, j, _scriptVars[VAR_SCROLL_Y]);
}
void Script::op_updateDisplay() {
const uint8 page = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_updateDisplay(%d)", page);
inp_handleSpecialKeys();
if (_res->_copyProtection) {
// entered protection symbols match the expected values
if (_res->_currentPart == kPartCopyProtection && _scriptVars[0x67] == 1) {
_scriptVars[0xDC] = 33;
}
}
const int frameHz = _is3DO ? 60 : 50;
if (!_fastMode && _scriptVars[VAR_PAUSE_SLICES] != 0) {
const int delay = _stub->getTimeStamp() - _timeStamp;
const int pause = _scriptVars[VAR_PAUSE_SLICES] * 1000 / frameHz - delay;
if (pause > 0) {
_stub->sleep(pause);
}
}
_timeStamp = _stub->getTimeStamp();
if (_is3DO) {
_scriptVars[0xF7] = (_timeStamp - _startTime) * frameHz / 1000;
} else {
_scriptVars[0xF7] = 0;
}
_vid->_displayHead = !((_res->_currentPart == 16004 && _screenNum == 37) || (_res->_currentPart == 16006 && _screenNum == 202));
_vid->updateDisplay(page, _stub);
}
void Script::op_removeTask() {
debugC(kDebugScript, "Script::op_removeTask()");
_scriptPtr.pc = _res->_segCode + 0xFFFF;
_scriptPaused = true;
}
void Script::op_drawString() {
const uint16 strId = _scriptPtr.fetchWord();
const uint16 x = _scriptPtr.fetchByte();
const uint16 y = _scriptPtr.fetchByte();
const uint16 col = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_drawString(0x%03X, %d, %d, %d)", strId, x, y, col);
_vid->drawString(col, x, y, strId);
}
void Script::op_sub() {
const uint8 i = _scriptPtr.fetchByte();
const uint8 j = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_sub(0x%02X, 0x%02X)", i, j);
_scriptVars[i] -= _scriptVars[j];
}
void Script::op_and() {
const uint8 i = _scriptPtr.fetchByte();
const uint16 n = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_and(0x%02X, %d)", i, n);
_scriptVars[i] = (uint16)_scriptVars[i] & n;
}
void Script::op_or() {
const uint8 i = _scriptPtr.fetchByte();
const uint16 n = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_or(0x%02X, %d)", i, n);
_scriptVars[i] = (uint16)_scriptVars[i] | n;
}
void Script::op_shl() {
const uint8 i = _scriptPtr.fetchByte();
const uint16 n = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_shl(0x%02X, %d)", i, n);
_scriptVars[i] = (uint16)_scriptVars[i] << n;
}
void Script::op_shr() {
const uint8 i = _scriptPtr.fetchByte();
const uint16 n = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_shr(0x%02X, %d)", i, n);
_scriptVars[i] = (uint16)_scriptVars[i] >> n;
}
void Script::op_playSound() {
const uint16 resNum = _scriptPtr.fetchWord();
const uint8 freq = _scriptPtr.fetchByte();
const uint8 vol = _scriptPtr.fetchByte();
const uint8 channel = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_playSound(0x%X, %d, %d, %d)", resNum, freq, vol, channel);
snd_playSound(resNum, freq, vol, channel);
}
static void preloadSoundCb(void *userdata, int soundNum, const uint8 *data) {
((Script *)userdata)->snd_preloadSound(soundNum, data);
}
void Script::op_updateResources() {
const uint16 num = _scriptPtr.fetchWord();
debugC(kDebugScript, "Script::op_updateResources(%d)", num);
if (num == 0) {
_ply->stop();
_sound->stopAll();
_res->invalidateRes();
} else {
_res->update(num, preloadSoundCb, this);
}
}
void Script::op_playMusic() {
const uint16 resNum = _scriptPtr.fetchWord();
const uint16 delay = _scriptPtr.fetchWord();
const uint8 pos = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op_playMusic(0x%X, %d, %d)", resNum, delay, pos);
snd_playMusic(resNum, delay, pos);
}
void Script::restartAt(int part, int pos) {
_ply->stop();
_sound->stopAll();
if (_res->getDataType() == DT_20TH_EDITION) {
_scriptVars[0xBF] = _difficulty; // difficulty (0 to 2)
// _scriptVars[0xDB] = 1; // preload sounds (resnum >= 2000)
_scriptVars[0xDE] = _useRemasteredAudio ? 1 : 0; // playback remastered sounds (resnum >= 146)
}
if (_res->getDataType() == DT_DOS && part == kPartCopyProtection) {
// VAR(0x54) indicates if the "Out of this World" title screen should be presented
//
// 0084: jmpIf(VAR(0x54) < 128, @00C4)
// ..
// 008D: setPalette(num=0)
// 0090: updateResources(res=18)
// ...
// 00C4: setPalette(num=23)
// 00CA: updateResources(res=71)
// Use "Out of this World" title screen if playing the USA release
const bool awTitleScreen = !(_res->_lang == Common::EN_USA);
_scriptVars[0x54] = awTitleScreen ? 0x1 : 0x81;
}
_res->setupPart(part);
memset(_scriptTasks, 0xFF, sizeof(_scriptTasks));
memset(_scriptStates, 0, sizeof(_scriptStates));
_scriptTasks[0][0] = 0;
_screenNum = -1;
if (pos >= 0) {
_scriptVars[0] = pos;
}
_startTime = _timeStamp = _stub->getTimeStamp();
if (part == kPartWater) {
if (_res->_demo3Joy.start()) {
memset(_scriptVars, 0, sizeof(_scriptVars));
}
}
}
void Script::setupTasks() {
if (_res->_nextPart != 0) {
restartAt(_res->_nextPart);
_res->_nextPart = 0;
}
for (int i = 0; i < 0x40; ++i) {
_scriptStates[0][i] = _scriptStates[1][i];
const uint16 n = _scriptTasks[1][i];
if (n != 0xFFFF) {
_scriptTasks[0][i] = (n == 0xFFFE) ? 0xFFFF : n;
_scriptTasks[1][i] = 0xFFFF;
}
}
}
void Script::runTasks() {
for (int i = 0; i < 0x40 && !_stub->_pi.quit; ++i) {
if (_scriptStates[0][i] == 0) {
const uint16 n = _scriptTasks[0][i];
if (n != 0xFFFF) {
_scriptPtr.pc = _res->_segCode + n;
_stackPtr = 0;
_scriptPaused = false;
debugC(kDebugScript, "Script::runTasks() i=0x%02X n=0x%02X", i, n);
executeTask();
_scriptTasks[0][i] = _scriptPtr.pc - _res->_segCode;
debugC(kDebugScript, "Script::runTasks() i=0x%02X pos=0x%X", i, _scriptTasks[0][i]);
}
}
}
}
void Script::executeTask() {
while (!_scriptPaused) {
const uint8 opcode = _scriptPtr.fetchByte();
if (opcode & 0x80) {
const uint16 off = ((opcode << 8) | _scriptPtr.fetchByte()) << 1;
_res->_useSegVideo2 = false;
Point pt;
pt.x = _scriptPtr.fetchByte();
pt.y = _scriptPtr.fetchByte();
const int16 h = pt.y - 199;
if (h > 0) {
pt.y = 199;
pt.x += h;
}
debugC(kDebugVideo, "vid_opcd_0x80 : opcode=0x%X off=0x%X x=%d y=%d", opcode, off, pt.x, pt.y);
_vid->setDataBuffer(_res->_segVideo1, off);
if (_is3DO) {
_vid->drawShape3DO(0xFF, 64, &pt);
} else {
_vid->drawShape(0xFF, 64, &pt);
}
} else if (opcode & 0x40) {
Point pt;
const uint8 offsetHi = _scriptPtr.fetchByte();
const uint16 off = ((offsetHi << 8) | _scriptPtr.fetchByte()) << 1;
pt.x = _scriptPtr.fetchByte();
_res->_useSegVideo2 = false;
if (!(opcode & 0x20)) {
if (!(opcode & 0x10)) {
pt.x = (pt.x << 8) | _scriptPtr.fetchByte();
} else {
pt.x = _scriptVars[pt.x];
}
} else {
if (opcode & 0x10) {
pt.x += 0x100;
}
}
pt.y = _scriptPtr.fetchByte();
if (!(opcode & 8)) {
if (!(opcode & 4)) {
pt.y = (pt.y << 8) | _scriptPtr.fetchByte();
} else {
pt.y = _scriptVars[pt.y];
}
}
uint16 zoom = 64;
if (!(opcode & 2)) {
if (opcode & 1) {
zoom = _scriptVars[_scriptPtr.fetchByte()];
}
} else {
if (opcode & 1) {
_res->_useSegVideo2 = true;
} else {
zoom = _scriptPtr.fetchByte();
}
}
debugC(kDebugVideo, "vid_opcd_0x40 : off=0x%X x=%d y=%d", off, pt.x, pt.y);
_vid->setDataBuffer(_res->_useSegVideo2 ? _res->_segVideo2 : _res->_segVideo1, off);
if (_is3DO) {
_vid->drawShape3DO(0xFF, zoom, &pt);
} else {
_vid->drawShape(0xFF, zoom, &pt);
}
} else {
if (_is3DO) {
switch (opcode) {
case 11:
{
const int num = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op11() setPalette %d", num);
_vid->changePal(num);
}
continue;
case 22:
{
const int var = _scriptPtr.fetchByte();
const int shift = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op22() VAR(0x%02X) <<= %d", var, shift);
_scriptVars[var] = (uint16)_scriptVars[var] << shift;
}
continue;
case 23:
{
const int var = _scriptPtr.fetchByte();
const int shift = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op23() VAR(0x%02X) >>= %d", var, shift);
_scriptVars[var] = (uint16)_scriptVars[var] >> shift;
}
continue;
case 26:
{
const int num = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op26() playMusic %d", num);
snd_playMusic(num, 0, 0);
}
continue;
case 27:
{
const int num = _scriptPtr.fetchWord();
const int x = _scriptVars[_scriptPtr.fetchByte()];
const int y = _scriptVars[_scriptPtr.fetchByte()];
const int color = _scriptPtr.fetchByte();
_vid->drawString(color, x, y, num);
}
continue;
case 28:
{
const uint8 var = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op28() jmpIf(VAR(0x%02x) == 0)", var);
if (_scriptVars[var] == 0) {
op_jmp();
} else {
_scriptPtr.fetchWord();
}
}
continue;
case 29:
{
const uint8 var = _scriptPtr.fetchByte();
debugC(kDebugScript, "Script::op29() jmpIf(VAR(0x%02x) != 0)", var);
if (_scriptVars[var] != 0) {
op_jmp();
} else {
_scriptPtr.fetchWord();
}
}
continue;
case 30:
{
::debug("Time = %d", _scriptVars[0xF7]);
}
continue;
default:
break;
}
}
if (opcode > 0x1A)
error("Script::executeTask() ec=0x%X invalid opcode=0x%X", 0xFFF, opcode);
(this->*OPCODE_TABLE[opcode])();
}
}
}
void Script::updateInput() {
_stub->processEvents();
if (_res->_currentPart == kPartPassword) {
const char c = _stub->_pi.lastChar;
if (c == 8 || /*c == 0xD ||*/ c == 0 || (c >= 'a' && c <= 'z')) {
_scriptVars[VAR_LAST_KEYCHAR] = c & ~0x20;
_stub->_pi.lastChar = 0;
}
}
int16 lr = 0;
int16 m = 0;
int16 ud = 0;
int16 jd = 0;
if (_stub->_pi.dirMask & PlayerInput::DIR_RIGHT) {
lr = 1;
m |= 1;
}
if (_stub->_pi.dirMask & PlayerInput::DIR_LEFT) {
lr = -1;
m |= 2;
}
if (_stub->_pi.dirMask & PlayerInput::DIR_DOWN) {
ud = jd = 1;
m |= 4; // crouch
}
if (_is3DO) { // This could be enabled to any later version than Amiga, Atari and DOS demo
if (_stub->_pi.dirMask & PlayerInput::DIR_UP) {
ud = -1;
}
if (_stub->_pi.jump) {
jd = -1;
m |= 8; // jump
}
} else {
if (_stub->_pi.dirMask & PlayerInput::DIR_UP) {
ud = jd = -1;
m |= 8; // jump
}
}
if (!(_res->getDataType() == DT_AMIGA || _res->getDataType() == DT_ATARI)) {
_scriptVars[VAR_HERO_POS_UP_DOWN] = ud;
}
_scriptVars[VAR_HERO_POS_JUMP_DOWN] = jd;
_scriptVars[VAR_HERO_POS_LEFT_RIGHT] = lr;
_scriptVars[VAR_HERO_POS_MASK] = m;
int16 action = 0;
if (_stub->_pi.action) {
action = 1;
m |= 0x80;
}
_scriptVars[VAR_HERO_ACTION] = action;
_scriptVars[VAR_HERO_ACTION_POS_MASK] = m;
if (_res->_currentPart == kPartWater) {
const uint8 mask = _res->_demo3Joy.update();
if (mask != 0) {
_scriptVars[VAR_HERO_ACTION_POS_MASK] = mask;
_scriptVars[VAR_HERO_POS_MASK] = mask & 15;
_scriptVars[VAR_HERO_POS_LEFT_RIGHT] = 0;
if (mask & 1) {
_scriptVars[VAR_HERO_POS_LEFT_RIGHT] = 1;
}
if (mask & 2) {
_scriptVars[VAR_HERO_POS_LEFT_RIGHT] = -1;
}
_scriptVars[VAR_HERO_POS_JUMP_DOWN] = 0;
if (mask & 4) {
_scriptVars[VAR_HERO_POS_JUMP_DOWN] = 1;
}
if (mask & 8) {
_scriptVars[VAR_HERO_POS_JUMP_DOWN] = -1;
}
_scriptVars[VAR_HERO_ACTION] = (mask >> 7);
}
}
}
void Script::inp_handleSpecialKeys() {
if (_stub->_pi.pause) {
if (_res->_currentPart != kPartCopyProtection && _res->_currentPart != kPartIntro) {
_stub->_pi.pause = false;
if (_is3DO) {
_vid->drawBitmap3DO("PauseShape", _stub);
}
while (!_stub->_pi.pause && !_stub->_pi.quit) {
_stub->processEvents();
_stub->sleep(50);
}
}
_stub->_pi.pause = false;
}
if (_stub->_pi.back) {
_stub->_pi.back = false;
if (_is3DO) {
static const char *names[] = { "EndShape1", "EndShape2" };
int current = 0;
_vid->drawBitmap3DO(names[current], _stub);
while (!_stub->_pi.quit) {
_stub->processEvents();
_stub->sleep(50);
if (_stub->_pi.dirMask & PlayerInput::DIR_LEFT) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_LEFT;
if (current != 0) {
current = 0;
_vid->drawBitmap3DO(names[current], _stub);
}
}
if (_stub->_pi.dirMask & PlayerInput::DIR_RIGHT) {
_stub->_pi.dirMask &= ~PlayerInput::DIR_RIGHT;
if (current != 1) {
current = 1;
_vid->drawBitmap3DO(names[current], _stub);
}
}
if (_stub->_pi.action) {
_stub->_pi.action = false;
if (current == 0) {
_res->_nextPart = 16000;
}
break;
}
}
}
}
if (_stub->_pi.code) {
_stub->_pi.code = false;
if (_res->_hasPasswordScreen) {
if (_res->_currentPart != kPartPassword && _res->_currentPart != kPartCopyProtection) {
_res->_nextPart = kPartPassword;
}
}
}
}
static uint8 getWavLooping(uint16 resNum) {
switch (resNum) {
case 1:
case 3:
case 8:
case 16:
case 89:
case 97:
case 102:
case 104:
case 106:
case 132:
case 139:
return 1;
default:
break;
}
return 0;
}
static int getSoundFreq(uint8 period) {
if (period > 39) {
warning("Script::getSoundFreq() invalid period %d", period);
period = 39;
}
return kPaulaFreq / (Script::PERIOD_TABLE[period] * 2);
}
void Script::snd_playSound(uint16 resNum, uint8 freq, uint8 vol, uint8 channel) {
debugC(kDebugSound, "snd_playSound(0x%X, %d, %d, %d)", resNum, freq, vol, channel);
if (vol == 0) {
_sound->stopSound(channel);
return;
}
if (vol > 63) {
vol = 63;
}
if (freq > 39) {
freq = 39;
}
channel &= 3;
switch (_res->getDataType()) {
case DT_20TH_EDITION:
if (freq != 0) {
--freq;
}
// fall-through
case DT_15TH_EDITION:
if (freq >= 32) {
// Anniversary editions do not have the 170 period
//
// [31] dos=19886 20th=19886 amiga=19886 (period 180)
// [32] dos=21056 20th=22372 amiga=21056 (period 170)
// [33] dos=22372 20th=23704 amiga=22372 (period 160)
++freq;
}
// fall-through
case DT_WIN31: {
uint32 size = 0;
uint8 *buf = _res->loadWav(resNum, &size);
if (buf) {
_sound->playSoundWav(channel, buf, size,
getSoundFreq(freq), vol, getWavLooping(resNum));
}
break;
}
case DT_3DO:
_sound->playSoundAiff(channel, resNum, vol);
break;
case DT_AMIGA:
case DT_ATARI:
case DT_ATARI_DEMO:
case DT_DOS: {
MemEntry *me = &_res->_memList[resNum];
if (me->status == Resource::STATUS_LOADED) {
_sound->playSoundRaw(channel, me->bufPtr, me->unpackedSize, getSoundFreq(freq), vol);
}
break;
}
default:
break;
}
}
void Script::snd_playMusic(uint16 resNum, uint16 delay, uint8 pos) {
debugC(kDebugSound, "snd_playMusic(0x%X, %d, %d)", resNum, delay, pos);
uint8 loop = 0;
switch (_res->getDataType()) {
case DT_20TH_EDITION:
if (resNum == 5000) {
_sound->stopMusic();
break;
}
if (resNum >= 5001 && resNum <= 5010) {
loop = 1;
}
// fall-through
case DT_15TH_EDITION:
case DT_WIN31:
if (resNum != 0) {
char path[MAXPATHLEN];
const char *p = _res->getMusicPath(resNum, path, sizeof(path));
if (p) {
_sound->playMusic(p, loop);
}
}
break;
case DT_3DO:
if (resNum == 0) {
_sound->stopAifcMusic();
} else {
uint32 offset = 0;
char path[MAXPATHLEN];
const char *p = _res->getMusicPath(resNum, path, sizeof(path), &offset);
if (p) {
_sound->playAifcMusic(p, offset);
}
}
break;
default: // DT_AMIGA, DT_ATARI, DT_DOS
if (resNum != 0) {
_ply->loadSfxModule(resNum, delay, pos);
_ply->start();
_sound->playSfxMusic(resNum);
} else if (delay != 0) {
_ply->setEventsDelay(delay);
} else {
_sound->stopSfxMusic();
}
break;
}
}
void Script::snd_preloadSound(uint16 resNum, const uint8 *data) {
if (_res->getDataType() == DT_3DO) {
_sound->preloadSoundAiff(resNum, data);
}
}
void Script::fixUpPalette_changeScreen(int part, int screen) {
int pal = -1;
switch (part) {
case 16004:
if (screen == 0x47) { // bitmap resource #68
pal = 8;
}
break;
case 16006:
if (screen == 0x4A) { // bitmap resources #144, #145
pal = 1;
}
break;
default:
break;
}
if (pal != -1) {
debugC(kDebugScript, "Setting palette %d for part %d screen %d", pal, part, screen);
_vid->changePal(pal);
}
}
} // namespace Awe