The 11th Hour has bugged scripts that sometimes don't set the variables for the GameBook thumbnails correctly, which causes even the original Win95 exe to crash. This happens occasionallyafter watching an end of chapter cutscene. Changing this to a warning means you don't crash, and the missing thumbnail is a minor compromise.
2460 lines
67 KiB
C++
2460 lines
67 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 2
|
|
* 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, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include <limits.h>
|
|
|
|
#include "audio/mididrv.h"
|
|
|
|
#include "groovie/script.h"
|
|
#include "groovie/cursor.h"
|
|
#include "groovie/graphics.h"
|
|
#include "groovie/music.h"
|
|
#include "groovie/video/player.h"
|
|
#include "groovie/resource.h"
|
|
#include "groovie/saveload.h"
|
|
#include "groovie/logic/cell.h"
|
|
#include "groovie/logic/tlcgame.h"
|
|
|
|
#include "gui/saveload.h"
|
|
|
|
#include "common/archive.h"
|
|
#include "common/config-manager.h"
|
|
#include "common/debug-channels.h"
|
|
#include "common/events.h"
|
|
#include "common/file.h"
|
|
#include "common/macresman.h"
|
|
#include "common/translation.h"
|
|
|
|
#include "gui/message.h"
|
|
|
|
const uint NUM_OPCODES = 91;
|
|
|
|
namespace Groovie {
|
|
|
|
// Adapted from SCRIPT.GRV
|
|
const byte t7gMidiInitScript[] = {
|
|
0x1A, 0x00, 0x01, 0xB1, 0x12, 0x00, // strcmpnejmp (if (var 0100 != 01) jmp 0012)
|
|
0x02, 0x46, 0x4C, // playsong 4C46 (GM init)
|
|
0x03, // bf9on (fade-in)
|
|
0x09, 0x60, 0x24, // videofromref 2460 (GM init video)
|
|
0x09, 0x60, 0x24, // videofromref 2460 (GM init video)
|
|
0x04, // palfadeout
|
|
0x29, // stopmidi
|
|
0x1A, 0x00, 0x01, 0xB2, 0x21, 0x00, // :0012 - strcmpnejmp (if (var 0100 != 02) jmp 0021)
|
|
0x02, 0x45, 0x4C, // playsong 4C45 (MT-32 init)
|
|
0x03, // bf9on (fade-in)
|
|
0x09, 0x61, 0x24, // videofromref 2461 (MT-32 init video)
|
|
0x04, // palfadeout
|
|
0x29, // stopmidi
|
|
0x31, 0x63, 0x00, 0x00, 0x00, // :0021 - midivolume 0063, 0000
|
|
0x3C, // checkvalidsaves
|
|
0x43, 0x00 // returnscript 00
|
|
};
|
|
|
|
enum kSpecialVariableTypes {
|
|
kVarTypeArray = 0x23,
|
|
kVarType2DArray = 0x7C
|
|
};
|
|
|
|
Script::Script(GroovieEngine *vm, EngineVersion version) :
|
|
_code(nullptr), _savedCode(nullptr), _stacktop(0), _debugger(nullptr), _vm(vm),
|
|
_videoFile(nullptr), _videoRef(UINT_MAX), _cellGame(nullptr), _lastCursor(0xff),
|
|
#ifdef ENABLE_GROOVIE2
|
|
_beehive(ConfMan.getBool("easier_ai")), _cake(ConfMan.getBool("easier_ai")), _gallery(ConfMan.getBool("easier_ai")),
|
|
_mouseTrap(ConfMan.getBool("easier_ai")), _othello(ConfMan.getBool("easier_ai")), _pente(ConfMan.getBool("easier_ai")),
|
|
#endif
|
|
_version(version), _random("GroovieScripts"), _tlcGame(nullptr)
|
|
{
|
|
|
|
// Initialize the opcode set depending on the engine version
|
|
if (version == kGroovieT7G) {
|
|
_opcodes = _opcodesT7G;
|
|
} else {
|
|
_opcodes = _opcodesV2;
|
|
}
|
|
|
|
// Prepare the variables
|
|
_bitflags = 0;
|
|
for (int i = 0; i < 0x400; i++) {
|
|
setVariable(i, 0);
|
|
}
|
|
|
|
// Initialize the music type variable
|
|
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
|
|
if (MidiDriver::getMusicType(dev) == MT_ADLIB) {
|
|
// MIDI through AdLib
|
|
setVariable(0x100, 0);
|
|
} else if ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32")) {
|
|
// MT-32
|
|
setVariable(0x100, 2);
|
|
} else {
|
|
// GM
|
|
setVariable(0x100, 1);
|
|
}
|
|
|
|
_hotspotTopAction = 0;
|
|
_hotspotBottomAction = 0;
|
|
_hotspotRightAction = 0;
|
|
_hotspotLeftAction = 0;
|
|
_hotspotSlot = (uint16)-1;
|
|
|
|
_oldInstruction = (uint16)-1;
|
|
_videoSkipAddress = 0;
|
|
}
|
|
|
|
Script::~Script() {
|
|
delete[] _code;
|
|
delete[] _savedCode;
|
|
delete _videoFile;
|
|
delete _cellGame;
|
|
delete _tlcGame;
|
|
}
|
|
|
|
void Script::setVariable(uint16 variablenum, byte value) {
|
|
_variables[variablenum] = value;
|
|
debugC(1, kDebugScriptvars, "script variable[0x%03X] = %d (0x%04X)", variablenum, value, value);
|
|
}
|
|
|
|
void Script::setBitFlag(int bitnum, bool value) {
|
|
if (value) {
|
|
_bitflags |= (1 << bitnum);
|
|
} else {
|
|
_bitflags &= ~(1 << bitnum);
|
|
}
|
|
}
|
|
|
|
bool Script::getBitFlag(int bitnum) {
|
|
return _bitflags & (1 << bitnum);
|
|
}
|
|
|
|
void Script::setDebugger(Debugger *debugger) {
|
|
_debugger = debugger;
|
|
}
|
|
|
|
void Script::timerTick() {
|
|
setVariable(0x103, _variables[0x103] + 1);
|
|
}
|
|
|
|
bool Script::loadScript(Common::String filename) {
|
|
Common::SeekableReadStream *scriptfile = nullptr;
|
|
|
|
if (_vm->_macResFork) {
|
|
// Try to open the script file from the resource fork
|
|
scriptfile = _vm->_macResFork->getResource(filename);
|
|
} else {
|
|
// Try to open the script file
|
|
scriptfile = SearchMan.createReadStreamForMember(filename);
|
|
}
|
|
|
|
if (!scriptfile)
|
|
return false;
|
|
|
|
// Save the script filename
|
|
_scriptFile = filename;
|
|
|
|
// Load the code
|
|
_codeSize = scriptfile->size();
|
|
_code = new byte[_codeSize];
|
|
if (!_code)
|
|
return false;
|
|
scriptfile->read(_code, _codeSize);
|
|
delete scriptfile;
|
|
|
|
// Patch the loaded code for known script bugs
|
|
if (filename.equals("dr.grv")) {
|
|
// WORKAROUND for the cake puzzle glitch (bug #4050): Lowering the
|
|
// piece on the first column and second row updates the wrong script
|
|
// variable
|
|
assert(_codeSize == 5546);
|
|
_code[0x03C2] = 0x38;
|
|
} else if (filename.equals("maze.grv")) {
|
|
// GRAPHICS ENHANCEMENT - Leave a skeleton in the maze.
|
|
// Replaces one normal T intersection with the unused(?)
|
|
// skeleton T intersection graphics.
|
|
assert(_codeSize == 3652);
|
|
|
|
// Terminating T branch
|
|
_code[0x0769] = 0x46;
|
|
_code[0x0774] = 0x3E;
|
|
_code[0x077A] = 0x42;
|
|
|
|
// T with branch on right
|
|
_code[0x08E2] = 0x43;
|
|
_code[0x08D7] = 0x44;
|
|
_code[0x08E8] = 0x45;
|
|
|
|
// T with branch on left
|
|
_code[0x0795] = 0x41;
|
|
_code[0x078A] = 0x40;
|
|
_code[0x079B] = 0x3F;
|
|
}
|
|
|
|
// Initialize the script
|
|
_currentInstruction = 0;
|
|
|
|
return true;
|
|
}
|
|
|
|
void Script::directGameLoad(int slot) {
|
|
// Reject invalid slots
|
|
if (slot < 0 || slot > MAX_SAVES - 1) {
|
|
return;
|
|
}
|
|
|
|
// Return to the main script if required
|
|
if (_savedCode) {
|
|
// Returning the correct spot, dealing with _savedVariables, etc
|
|
// is not needed as game state is getting nuked anyway
|
|
delete[] _code;
|
|
_code = _savedCode;
|
|
_codeSize = _savedCodeSize;
|
|
_savedCode = nullptr;
|
|
}
|
|
|
|
uint16 targetInstruction = 0;
|
|
const byte *midiInitScript = nullptr;
|
|
uint8 midiInitScriptSize = 0;
|
|
|
|
// HACK: We set the slot to load in the appropriate variable, and set the
|
|
// current instruction to the one that actually loads the saved game
|
|
// specified in that variable. This differs depending on the game and its
|
|
// version.
|
|
if (_version == kGroovieT7G) {
|
|
// 7th Guest
|
|
setVariable(0x19, slot);
|
|
targetInstruction = 0x287;
|
|
// TODO Not sure if this works on or is necessary for Mac or iOS
|
|
// versions. Disabling it to prevent breaking game loading.
|
|
if (_vm->getPlatform() == Common::kPlatformDOS) {
|
|
midiInitScript = t7gMidiInitScript;
|
|
midiInitScriptSize = sizeof(t7gMidiInitScript);
|
|
}
|
|
} else if (_version == kGroovieT11H) {
|
|
setVariable(0xF, slot);
|
|
_currentInstruction = 0xE78D;
|
|
return;
|
|
} else if (_version == kGroovieCDY) {
|
|
setVariable(0x1, slot);
|
|
_currentInstruction = 0x9EBF;
|
|
return;
|
|
} else if (_version == kGroovieUHP) {
|
|
setVariable(0x19, slot);
|
|
_currentInstruction = 0x23B4;
|
|
return;
|
|
}
|
|
|
|
if (midiInitScript && !_vm->_musicPlayer->isMidiInit()) {
|
|
// Run the MIDI init script as a subscript.
|
|
|
|
// Backup the current script state
|
|
_savedCode = _code;
|
|
_savedCodeSize = _codeSize;
|
|
_savedStacktop = _stacktop;
|
|
_savedScriptFile = _scriptFile;
|
|
// Set the game load instruction as the backup instruction. This
|
|
// will run when the subscript returns.
|
|
_savedInstruction = targetInstruction;
|
|
|
|
// Set the MIDI init script as the current script.
|
|
_codeSize = midiInitScriptSize;
|
|
_code = new byte[_codeSize];
|
|
memcpy(_code, midiInitScript, _codeSize);
|
|
_stacktop = 0;
|
|
_currentInstruction = 0;
|
|
} else {
|
|
// No MIDI initialization necessary. Just jump to the game load
|
|
// instruction.
|
|
_currentInstruction = targetInstruction;
|
|
// Due to HACK above, the call to check valid save slots is not run.
|
|
// As this is where we load save names, manually call it here.
|
|
o_checkvalidsaves();
|
|
}
|
|
}
|
|
|
|
void Script::step() {
|
|
// Prepare the base debug string
|
|
_debugString = _scriptFile + Common::String::format("@0x%04X: ", _currentInstruction);
|
|
|
|
// Get the current opcode
|
|
byte opcode = readScript8bits();
|
|
_firstbit = ((opcode & 0x80) != 0);
|
|
opcode = opcode & 0x7F;
|
|
|
|
// Show the opcode debug string
|
|
_debugString += Common::String::format("op 0x%02X: ", opcode);
|
|
|
|
// Only output if we're not re-doing the previous instruction
|
|
if (_currentInstruction != _oldInstruction) {
|
|
debugCN(1, kDebugScript, "%s", _debugString.c_str());
|
|
|
|
_oldInstruction = _currentInstruction;
|
|
}
|
|
|
|
// Detect invalid opcodes
|
|
if (opcode >= NUM_OPCODES) {
|
|
o_invalid();
|
|
return;
|
|
}
|
|
|
|
// Execute the current opcode
|
|
OpcodeFunc op = _opcodes[opcode];
|
|
(this->*op)();
|
|
}
|
|
|
|
void Script::setMouseClick(uint8 button) {
|
|
_eventMouseClicked = button;
|
|
}
|
|
|
|
void Script::setKbdChar(uint8 c) {
|
|
_eventKbdChar = c;
|
|
}
|
|
|
|
Common::String &Script::getContext() {
|
|
return _debugString;
|
|
}
|
|
|
|
uint8 Script::getCodeByte(uint16 address) {
|
|
if (address >= _codeSize)
|
|
error("Trying to read a script byte at address 0x%04X, while the "
|
|
"script is just 0x%04X bytes long", address, _codeSize);
|
|
return _code[address];
|
|
}
|
|
|
|
uint8 Script::readScript8bits() {
|
|
uint8 data = getCodeByte(_currentInstruction);
|
|
_currentInstruction++;
|
|
return data;
|
|
}
|
|
|
|
uint8 Script::readScriptVar() {
|
|
uint8 data = _variables[readScript8or16bits()];
|
|
return data;
|
|
}
|
|
|
|
uint16 Script::readScript16bits() {
|
|
uint8 lower = readScript8bits();
|
|
uint8 upper = readScript8bits();
|
|
return lower | (upper << 8);
|
|
}
|
|
|
|
uint32 Script::readScript32bits() {
|
|
uint16 lower = readScript16bits();
|
|
uint16 upper = readScript16bits();
|
|
return lower | (upper << 16);
|
|
}
|
|
|
|
uint16 Script::readScript8or16bits() {
|
|
if (_firstbit) {
|
|
return readScript8bits();
|
|
} else {
|
|
return readScript16bits();
|
|
}
|
|
}
|
|
|
|
uint8 Script::readScriptChar(bool allow7C, bool limitVal, bool limitVar) {
|
|
uint8 result;
|
|
uint8 data = readScript8bits();
|
|
|
|
if (limitVal) {
|
|
data &= 0x7F;
|
|
}
|
|
|
|
if (allow7C && (data == kVarType2DArray)) {
|
|
// Index a bidimensional array
|
|
uint8 parta, partb;
|
|
parta = readScriptChar(false, false, false);
|
|
partb = readScriptChar(false, true, true);
|
|
result = _variables[0x0A * parta + partb + 0x19];
|
|
debugC(7, kDebugScript, "readScriptChar got | for var %d with value %d", (int)(0x0A * parta + partb + 0x19), (int)result);
|
|
} else if (data == kVarTypeArray) {
|
|
// Index an array
|
|
data = readScript8bits();
|
|
if (limitVar) {
|
|
data &= 0x7F;
|
|
}
|
|
result = _variables[data - 0x61];
|
|
debugC(7, kDebugScript, "readScriptChar got # for var %d with value %d", (int)(data - 0x61), (int)result);
|
|
} else {
|
|
// Immediate value
|
|
result = data - 0x30;
|
|
debugC(7, kDebugScript, "readScriptChar got %d", (int)result);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void Script::readScriptString(Common::String &str) {
|
|
byte c;
|
|
Common::String orig;
|
|
|
|
debugC(5, kDebugScript, "readScriptString start");
|
|
|
|
while ((c = readScript8bits())) {
|
|
orig += c;
|
|
switch (c) {
|
|
case kVarTypeArray:
|
|
c = readScript8bits();
|
|
orig += Common::String::format("%d", (int)(c - 0x61));
|
|
c = _variables[c - 0x61] + 0x30;
|
|
if (_version == kGroovieT7G) {
|
|
if (c >= 0x41 && c <= 0x5A) {
|
|
c += ' ';
|
|
}
|
|
}
|
|
break;
|
|
case kVarType2DArray:
|
|
uint8 parta, partb;
|
|
parta = readScriptChar(false, false, false);
|
|
partb = readScriptChar(false, false, false);
|
|
orig += Common::String::format("%d", (int)(0x0A * parta + partb + 0x19));
|
|
c = _variables[0x0A * parta + partb + 0x19] + 0x30;
|
|
break;
|
|
default:
|
|
if (_version == kGroovieT7G) {
|
|
if (c >= 0x41 && c <= 0x5A) {
|
|
c += ' ';
|
|
}
|
|
}
|
|
}
|
|
// Append the current character at the end of the string
|
|
str += c;
|
|
}
|
|
|
|
debugC(5, kDebugScript, "readScriptString orig: %s, ret: %s", orig.c_str(), str.c_str());
|
|
}
|
|
|
|
uint32 Script::getVideoRefString(Common::String &resName) {
|
|
// Read String from Script (includes variable values)
|
|
readScriptString(resName);
|
|
|
|
// Add a trailing dot
|
|
resName += '.';
|
|
|
|
debugCN(0, kDebugScript, "%s", resName.c_str());
|
|
|
|
// Get the fileref of the resource
|
|
return _vm->_resMan->getRef(resName);
|
|
}
|
|
|
|
void Script::executeInputAction(uint16 address) {
|
|
debugC(1, kDebugScript, "Groovie::Script: executeInputAction 0x%04X", (uint)address);
|
|
|
|
// Jump to the planned address
|
|
_currentInstruction = address;
|
|
|
|
// Exit the input loop
|
|
_inputLoopAddress = 0;
|
|
|
|
// Force immediate hiding of the mouse cursor (required when the next video just contains audio)
|
|
_vm->_grvCursorMan->show(false);
|
|
_vm->_graphicsMan->change();
|
|
}
|
|
|
|
bool Script::hotspot(Common::Rect rect, uint16 address, uint8 cursor) {
|
|
// Test if the current mouse position is contained in the specified rectangle
|
|
Common::Point mousepos = _vm->_system->getEventManager()->getMousePos();
|
|
bool contained = rect.contains(mousepos);
|
|
|
|
// Show hotspots when debugging
|
|
if (DebugMan.isDebugChannelEnabled(kDebugHotspots)) {
|
|
if (_vm->_graphicsMan->isFullScreen())
|
|
rect.translate(0, -80);
|
|
Graphics::Surface *gamescreen = _vm->_system->lockScreen();
|
|
gamescreen->frameRect(rect, 0xcc2338ff);
|
|
_vm->_system->unlockScreen();
|
|
_vm->_system->updateScreen();
|
|
}
|
|
|
|
if (contained) {
|
|
// Change the mouse cursor
|
|
if (_newCursorStyle == 5) {
|
|
_newCursorStyle = cursor;
|
|
}
|
|
|
|
// If clicked with the mouse, jump to the specified address
|
|
if (_mouseClicked) {
|
|
_lastCursor = cursor;
|
|
executeInputAction(address);
|
|
}
|
|
}
|
|
|
|
return contained;
|
|
}
|
|
|
|
void Script::loadgame(uint slot) {
|
|
// The 11th Hour uses slot 0 for the Open House savegame. It loads this
|
|
// savegame before showing the load/restart dialog during the intro. The
|
|
// music should not be stopped in this case.
|
|
if (!(_vm->getEngineVersion() == kGroovieT11H && slot == 0))
|
|
_vm->_musicPlayer->stop();
|
|
|
|
Common::InSaveFile *file = SaveLoad::openForLoading(ConfMan.getActiveDomainName(), slot);
|
|
|
|
// Loading the variables. It is endian safe because they're byte variables
|
|
file->read(_variables, 0x400);
|
|
|
|
delete file;
|
|
|
|
// Hide the mouse cursor
|
|
_vm->_grvCursorMan->show(false);
|
|
}
|
|
|
|
bool Script::preview_loadgame(uint slot) { // used by Clandestiny for the photos
|
|
Common::InSaveFile *file = SaveLoad::openForLoading(ConfMan.getActiveDomainName(), slot);
|
|
|
|
if (!file)
|
|
return false;
|
|
|
|
// Loading the variables. It is endian safe because they're byte variables
|
|
uint32 size = 21;
|
|
uint32 bytes_read = file->read(_variables, size);
|
|
delete file;
|
|
|
|
if (bytes_read < size)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Script::canDirectSave() const {
|
|
// Disallow when running a subscript (puzzle)
|
|
if (_savedCode == nullptr) {
|
|
// UHP appears not to use "room" variables(?)
|
|
// 11H uses room plus 'scene' variable. 8D is set to 1 at launch, but scene is left '0' until first nav
|
|
// T7G and Clan only use room vars
|
|
// TLC uses room variables to indicate question progress
|
|
if (_version == kGroovieUHP)
|
|
return true;
|
|
else if (_version == kGroovieT11H)
|
|
return _variables[0x8C] != 0 || _variables[0x8D] != 1 || _variables[0x8E] != 0;
|
|
else
|
|
return _variables[0x8C] != 0 || _variables[0x8D] != 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Script::directGameSave(int slot, const Common::String &desc) {
|
|
if (slot < 0 || slot > MAX_SAVES - 1) {
|
|
return;
|
|
}
|
|
const char *saveName = desc.c_str();
|
|
for (int i = 0; i < 15; i++) {
|
|
_variables[i] = saveName[i] - 0x30;
|
|
}
|
|
savegame(slot);
|
|
}
|
|
|
|
void Script::savegame(uint slot) {
|
|
char save[15];
|
|
char newchar;
|
|
Common::OutSaveFile *file = SaveLoad::openForSaving(ConfMan.getActiveDomainName(), slot);
|
|
|
|
if (!file) {
|
|
debugC(9, kDebugScript, "Save file pointer is null");
|
|
GUI::MessageDialog dialog(_("Failed to save game"), _("OK"));
|
|
dialog.runModal();
|
|
return;
|
|
}
|
|
|
|
// Saving the variables. It is endian safe because they're byte variables
|
|
file->write(_variables, 0x400);
|
|
delete file;
|
|
|
|
// Cache the saved name
|
|
for (int i = 0; i < 15; i++) {
|
|
newchar = _variables[i] + 0x30;
|
|
if ((newchar < 0x30 || newchar > 0x39) && (newchar < 0x41 || newchar > 0x7A) && newchar != 0x2E) {
|
|
save[i] = '\0';
|
|
break;
|
|
} else if (newchar == 0x2E) { // '.', generated when space is pressed
|
|
save[i] = ' ';
|
|
} else {
|
|
save[i] = newchar;
|
|
}
|
|
}
|
|
_saveNames[slot] = save;
|
|
}
|
|
|
|
void Script::printString(Graphics::Surface *surface, const char *str) {
|
|
char message[15];
|
|
memset(message, 0, 15);
|
|
|
|
// Preprocess the string
|
|
for (int i = 0; i < 14; i++) {
|
|
if (str[i] <= 0x00 || str[i] == 0x24)
|
|
break;
|
|
message[i] = str[i];
|
|
}
|
|
Common::rtrim(message);
|
|
|
|
// Draw the string
|
|
if (_version == kGroovieT7G) {
|
|
_vm->_font->drawString(surface, message, 0, 16, 640, 0xE2, Graphics::kTextAlignCenter);
|
|
} else {
|
|
_vm->_font->drawString(surface, message, 190, 190, 640, _vm->_pixelFormat.RGBToColor(0xff, 0x0A, 0x0A), Graphics::kTextAlignLeft);
|
|
}
|
|
}
|
|
|
|
// OPCODES
|
|
|
|
void Script::o_invalid() {
|
|
error("Groovie::Script: Invalid opcode");
|
|
}
|
|
|
|
void Script::o_nop() {
|
|
debugC(1, kDebugScript, "Groovie::Script: NOP");
|
|
}
|
|
|
|
void Script::o_nop8() {
|
|
uint8 tmp = readScript8bits();
|
|
debugC(1, kDebugScript, "Groovie::Script: NOP8: 0x%02X", tmp);
|
|
}
|
|
|
|
void Script::o_nop16() {
|
|
uint16 tmp = readScript16bits();
|
|
debugC(1, kDebugScript, "Groovie::Script: NOP16: 0x%04X", tmp);
|
|
}
|
|
|
|
void Script::o_nop32() {
|
|
uint32 tmp = readScript32bits();
|
|
debugC(1, kDebugScript, "Groovie::Script: NOP32: 0x%08X", tmp);
|
|
}
|
|
|
|
void Script::o_nop8or16() {
|
|
uint16 tmp = readScript8or16bits();
|
|
debugC(1, kDebugScript, "Groovie::Script: NOP8OR16: 0x%04X", tmp);
|
|
}
|
|
|
|
void Script::o_playsong() { // 0x02
|
|
uint16 fileref = readScript16bits();
|
|
debugC(1, kDebugScript, "Groovie::Script: PlaySong(0x%04X): Play xmidi file", fileref);
|
|
if (fileref == 0x4C17) {
|
|
warning("Groovie::Script: this song is special somehow");
|
|
// don't save the reference?
|
|
}
|
|
_vm->_musicPlayer->playSong(fileref);
|
|
}
|
|
|
|
void Script::o_bf9on() { // 0x03
|
|
debugC(1, kDebugScript, "Groovie::Script: BF9ON: bitflag 9 turned on");
|
|
_bitflags |= 1 << 9;
|
|
}
|
|
|
|
void Script::o_palfadeout() {
|
|
debugC(1, kDebugScript, "Groovie::Script: PALFADEOUT");
|
|
debugC(2, kDebugVideo, "Groovie::Script: PALFADEOUT");
|
|
_vm->_graphicsMan->fadeOut();
|
|
}
|
|
|
|
void Script::o_bf8on() { // 0x05
|
|
debugC(1, kDebugScript, "Groovie::Script: BF8ON: bitflag 8 turned on");
|
|
_bitflags |= 1 << 8;
|
|
}
|
|
|
|
void Script::o_bf6on() { // 0x06
|
|
debugC(1, kDebugScript, "Groovie::Script: BF6ON: bitflag 6 turned on");
|
|
_bitflags |= 1 << 6;
|
|
}
|
|
|
|
void Script::o_bf7on() { // 0x07
|
|
debugC(1, kDebugScript, "Groovie::Script: BF7ON: bitflag 7 turned on");
|
|
_bitflags |= 1 << 7;
|
|
}
|
|
|
|
void Script::o2_bf0on() { // v2 0x0A
|
|
debugC(1, kDebugScript, "Groovie::Script: BF0ON: bitflag 0 turned on");
|
|
_bitflags |= 1;
|
|
}
|
|
|
|
void Script::o_setbackgroundsong() { // 0x08
|
|
uint16 fileref = readScript16bits();
|
|
debugC(1, kDebugScript, "Groovie::Script: SetBackgroundSong(0x%04X)", fileref);
|
|
_vm->_musicPlayer->setBackgroundSong(fileref);
|
|
}
|
|
|
|
void Script::o_videofromref() { // 0x09
|
|
uint16 fileref = readScript16bits();
|
|
|
|
// Show the debug information just when starting the playback
|
|
if (fileref != _videoRef) {
|
|
debugC(1, kDebugScript, "Groovie::Script: VIDEOFROMREF(0x%04X) (Not fully imp): Play video file from ref", fileref);
|
|
debugC(2, kDebugVideo, "\nGroovie::Script: @0x%04X: Playing video %d via 0x09 (VideoFromRef)", _currentInstruction-3, fileref);
|
|
}
|
|
switch (fileref) {
|
|
case 0x1C03: // Trilobyte logo
|
|
case 0x1C04: // Virgin logo
|
|
case 0x1C05: // Credits
|
|
if (fileref != _videoRef) {
|
|
debugC(1, kDebugScript, "Groovie::Script: Use external file if available");
|
|
}
|
|
break;
|
|
|
|
case 0x400D: // floating objects in music room
|
|
case 0x5060: // a sound from gamwav?
|
|
case 0x5098: // a sound from gamwav?
|
|
case 0x2402: // House becomes book in intro?
|
|
case 0x1426: // Turn to face front in hall: played after intro
|
|
case 0x206D: // Cards on table puzzle (bedroom)
|
|
case 0x2001: // Coins on table puzzle (bedroom)
|
|
if (fileref != _videoRef) {
|
|
debugCN(1, kDebugScript, "Groovie::Script: (This video is special somehow!)");
|
|
warning("Groovie::Script: (This video (0x%04X) is special somehow!)", fileref);
|
|
}
|
|
break;
|
|
|
|
case 0x2420: // load from the main menu
|
|
if (_version == kGroovieT7G && !ConfMan.getBool("originalsaveload")) {
|
|
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
|
|
int slot = dialog->runModalWithCurrentTarget();
|
|
delete dialog;
|
|
|
|
if (slot >= 0) {
|
|
_currentInstruction = 0x287;
|
|
_bitflags = 0;
|
|
setVariable(0x19, slot);
|
|
} else {
|
|
_currentInstruction = 0x016; // back to main menu (load game / new game)
|
|
}
|
|
|
|
return;
|
|
}
|
|
break;
|
|
|
|
case 0x2422: // save from the in-game menu
|
|
if (_version == kGroovieT7G && !ConfMan.getBool("originalsaveload")) {
|
|
GUI::MessageDialog saveOrLoad(_("Would you like to save or restore a game?"), _("Save"), _("Restore"));
|
|
|
|
int choice = saveOrLoad.runModal();
|
|
if (choice == GUI::kMessageOK) {
|
|
// Save
|
|
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
|
|
int slot = dialog->runModalWithCurrentTarget();
|
|
Common::String saveName = dialog->getResultString();
|
|
delete dialog;
|
|
|
|
if (slot >= 0) {
|
|
directGameSave(slot, saveName);
|
|
}
|
|
|
|
_currentInstruction = 0x17C8; // back to game menu
|
|
} else {
|
|
// Restore
|
|
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
|
|
int slot = dialog->runModalWithCurrentTarget();
|
|
delete dialog;
|
|
|
|
if (slot >= 0) {
|
|
_currentInstruction = 0x287;
|
|
_bitflags = 0;
|
|
setVariable(0x19, slot);
|
|
} else {
|
|
_currentInstruction = 0x17C8; // back to game menu
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
if (fileref != _videoRef) {
|
|
debugCN(1, kDebugScript, "\n");
|
|
}
|
|
|
|
// Determine if the MT-32 or GM initialization video is being played
|
|
const bool enhancedMusicTracksExist = _version == kGroovieT7G && Common::File::exists("gu16.ogg");
|
|
const bool gmInitVideo = _version == kGroovieT7G && fileref == 0x2460 && !enhancedMusicTracksExist;
|
|
const bool mt32InitVideo = _version == kGroovieT7G && fileref == 0x2461 && !enhancedMusicTracksExist;
|
|
// Play the video
|
|
// If a MIDI init video is being played, loop it until the "audio"
|
|
// (init commands) has finished playing
|
|
if (!playvideofromref(fileref, gmInitVideo || mt32InitVideo)) {
|
|
// Move _currentInstruction back
|
|
_currentInstruction -= 3;
|
|
} else if (gmInitVideo || mt32InitVideo) {
|
|
// MIDI initialization has completed. Set this on the music player,
|
|
// so that MIDI init will not be done again on game load.
|
|
_vm->_musicPlayer->setMidiInit(true);
|
|
if (gmInitVideo)
|
|
// The script plays the GM init video twice to give the "audio"
|
|
// enough time to play. It has just looped until the audio finished,
|
|
// so the second play is no longer necessary.
|
|
// Skip the next instruction.
|
|
_currentInstruction += 3;
|
|
}
|
|
}
|
|
|
|
bool Script::playvideofromref(uint32 fileref, bool loopUntilAudioDone) {
|
|
// It isn't the current video, open it
|
|
if (fileref != _videoRef) {
|
|
|
|
// Debug bitflags
|
|
debugCN(1, kDebugScript, "Groovie::Script: Play video 0x%04X (bitflags:", fileref);
|
|
for (int i = 15; i >= 0; i--) {
|
|
debugCN(1, kDebugScript, "%d", _bitflags & (1 << i)? 1 : 0);
|
|
if (i % 4 == 0) {
|
|
debugCN(1, kDebugScript, " ");
|
|
}
|
|
}
|
|
debugC(1, kDebugScript, " <- 0)");
|
|
|
|
// Close the previous video file
|
|
if (_videoFile) {
|
|
_videoRef = UINT_MAX;
|
|
delete _videoFile;
|
|
}
|
|
|
|
if (fileref == UINT_MAX)
|
|
return true;
|
|
|
|
// Try to open the new file
|
|
ResInfo resInfo;
|
|
if (!_vm->_resMan->getResInfo(fileref, resInfo)) {
|
|
error("Groovie::Script: Couldn't find resource info for fileref %d", fileref);
|
|
return true;
|
|
}
|
|
|
|
_videoFile = _vm->_resMan->open(resInfo);
|
|
|
|
if (_videoFile) {
|
|
_videoRef = fileref;
|
|
// If teeth or mask cursor, and in main script, mark video prefer low-speed.
|
|
// Filename check as sometimes teeth used for puzzle movements (bishops)
|
|
if (_version == kGroovieT7G && (_lastCursor == 7 || _lastCursor == 4) && _scriptFile == "script.grv")
|
|
_bitflags |= (1 << 15);
|
|
// act, door and trailer use a variation of motion blocks in the ROQ decoder.
|
|
// Original clan engine specifically references these files by name to set the flag
|
|
else if (_version == kGroovieCDY && (resInfo.filename.hasPrefix("act") || resInfo.filename.hasPrefix("door")))
|
|
_bitflags |= (1 << 14);
|
|
_vm->_videoPlayer->load(_videoFile, _bitflags);
|
|
} else {
|
|
error("Groovie::Script: Couldn't open file");
|
|
return true;
|
|
}
|
|
|
|
// Reset the clicked mouse events
|
|
_eventMouseClicked = 0;
|
|
}
|
|
|
|
// Check if the user wants to skip the video
|
|
if ((_eventMouseClicked == 2) && (_videoSkipAddress != 0)) {
|
|
// Jump to the given address
|
|
_currentInstruction = _videoSkipAddress;
|
|
|
|
// Reset the skip address
|
|
_videoSkipAddress = 0;
|
|
|
|
_bitflags = 0;
|
|
|
|
// End the playback
|
|
return true;
|
|
} else if (_eventMouseClicked == 2) {
|
|
_vm->_videoPlayer->fastForward();
|
|
_eventMouseClicked = 0;
|
|
}
|
|
|
|
// Video available, play one frame
|
|
if (_videoFile) {
|
|
bool endVideo = _vm->_videoPlayer->playFrame();
|
|
_vm->_musicPlayer->frameTick();
|
|
|
|
if (endVideo && loopUntilAudioDone && _vm->_musicPlayer->isPlaying()) {
|
|
// The video has ended, but the audio hasn't. Loop the video.
|
|
_videoFile->seek(0);
|
|
// Clear bit flag 9 (fade-in)
|
|
_vm->_videoPlayer->load(_videoFile, _bitflags & ~(1 << 9));
|
|
return false;
|
|
}
|
|
|
|
if (endVideo || (loopUntilAudioDone && !_vm->_musicPlayer->isPlaying())) {
|
|
// The video has ended, or it was being looped and the audio has ended.
|
|
|
|
// Close the file
|
|
delete _videoFile;
|
|
_videoFile = nullptr;
|
|
_videoRef = UINT_MAX;
|
|
|
|
// Clear the input events while playing the video
|
|
_eventMouseClicked = 0;
|
|
_eventKbdChar = 0;
|
|
|
|
// Newline
|
|
debugCN(1, kDebugScript, "\n");
|
|
|
|
_bitflags = 0;
|
|
|
|
// Let the caller know if the video has ended
|
|
return true;
|
|
}
|
|
|
|
// The video has not ended yet.
|
|
return false;
|
|
}
|
|
|
|
// If the file is closed, finish the playback
|
|
_bitflags = 0;
|
|
return true;
|
|
}
|
|
|
|
bool Script::playBackgroundSound(uint32 fileref, uint32 loops) {
|
|
if (fileref == UINT_MAX) {
|
|
return false;
|
|
}
|
|
|
|
// Try to open the new file
|
|
Common::SeekableReadStream *_soundFile = _vm->_resMan->open(fileref);
|
|
|
|
if (_soundFile) {
|
|
_vm->_soundQueue.queue(_soundFile, loops);
|
|
} else {
|
|
warning("Groovie::Script: Couldn't open file");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void Script::o_bf5on() { // 0x0A
|
|
debugC(1, kDebugScript, "Groovie::Script: BF5ON: bitflag 5 turned on");
|
|
_bitflags |= 1 << 5;
|
|
}
|
|
|
|
void Script::o_inputloopstart() { //0x0B
|
|
debugC(5, kDebugScript, "Groovie::Script: Input loop start");
|
|
|
|
// For TLC the regions for many questions are in an extra database. Reset internal region counters
|
|
if (_version == kGroovieTLC && _tlcGame != nullptr) {
|
|
#ifdef ENABLE_GROOVIE2
|
|
_tlcGame->getRegionRewind();
|
|
#endif
|
|
}
|
|
|
|
// Reset the mouse cursor
|
|
_newCursorStyle = 5;
|
|
|
|
// Save the input loop address
|
|
_inputLoopAddress = _currentInstruction - 1;
|
|
|
|
// Save the current mouse state for the whole loop
|
|
_mouseClicked = (_eventMouseClicked == 1);
|
|
_eventMouseClicked = 0;
|
|
|
|
// Save the current pressed character for the whole loop
|
|
_kbdChar = _eventKbdChar;
|
|
_eventKbdChar = 0;
|
|
}
|
|
|
|
void Script::o_keyboardaction() {
|
|
uint8 val = readScript8bits();
|
|
uint16 address = readScript16bits();
|
|
|
|
// Check the typed key
|
|
if (_kbdChar == val) {
|
|
debugC(5, kDebugScript, "Groovie::Script: Test key == 0x%02X @0x%04X - match", val, address);
|
|
|
|
executeInputAction(address);
|
|
} else {
|
|
debugC(5, kDebugScript, "Groovie::Script: Test key == 0x%02X @0x%04X", val, address);
|
|
}
|
|
}
|
|
|
|
void Script::o_hotspot_rect() {
|
|
uint16 left = readScript16bits();
|
|
uint16 top = readScript16bits();
|
|
uint16 right = readScript16bits();
|
|
uint16 bottom = readScript16bits();
|
|
uint16 address = readScript16bits();
|
|
uint8 cursor = readScript8bits();
|
|
|
|
// TLC: The regions for many questions are in an extra database
|
|
if (_version == kGroovieTLC && left == 0 && top == 0 && right == 0 && bottom == 0 && _tlcGame != nullptr) {
|
|
#ifdef ENABLE_GROOVIE2
|
|
if (_tlcGame->getRegionNext(left, top, right, bottom) < 0) {
|
|
debugC(5, kDebugScript, "Groovie::Script: HOTSPOT-RECT(%d,%d,%d,%d) @0x%04X cursor=%d SKIPPED", left, top, right, bottom, address, cursor);
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
debugC(5, kDebugScript, "Groovie::Script: HOTSPOT-RECT(%d,%d,%d,%d) @0x%04X cursor=%d", left, top, right, bottom, address, cursor);
|
|
|
|
// Mark the specified rectangle
|
|
Common::Rect rect(left, top, right, bottom);
|
|
hotspot(rect, address, cursor);
|
|
}
|
|
|
|
void Script::o_hotspot_left() {
|
|
uint16 address = readScript16bits();
|
|
|
|
debugC(5, kDebugScript, "Groovie::Script: HOTSPOT-LEFT @0x%04X", address);
|
|
|
|
// Mark the leftmost 100 pixels of the game area
|
|
Common::Rect rect(0, 80, 100, 400);
|
|
hotspot(rect, address, 1);
|
|
}
|
|
|
|
void Script::o_hotspot_right() {
|
|
uint16 address = readScript16bits();
|
|
|
|
debugC(5, kDebugScript, "Groovie::Script: HOTSPOT-RIGHT @0x%04X", address);
|
|
|
|
// Mark the rightmost 100 pixels of the game area
|
|
Common::Rect rect(540, 80, 640, 400);
|
|
hotspot(rect, address, 2);
|
|
}
|
|
|
|
void Script::o_hotspot_center() {
|
|
uint16 address = readScript16bits();
|
|
|
|
debugC(5, kDebugScript, "Groovie::Script: HOTSPOT-CENTER @0x%04X", address);
|
|
|
|
// Mark the centermost 240 pixels of the game area
|
|
Common::Rect rect(200, 80, 440, 400);
|
|
hotspot(rect, address, 0);
|
|
}
|
|
|
|
void Script::o_hotspot_current() {
|
|
uint16 address = readScript16bits();
|
|
|
|
debugC(5, kDebugScript, "Groovie::Script: HOTSPOT-CURRENT @0x%04X", address);
|
|
|
|
// The original interpreter doesn't check the position, so accept the
|
|
// whole screen
|
|
Common::Rect rect(0, 0, 640, 480);
|
|
hotspot(rect, address, 0);
|
|
}
|
|
|
|
void Script::o_inputloopend() {
|
|
debugC(5, kDebugScript, "Groovie::Script: Input loop end");
|
|
|
|
// Handle the predefined hotspots
|
|
if (_hotspotTopAction) {
|
|
Common::Rect rect(0, 0, 640, 80);
|
|
hotspot(rect, _hotspotTopAction, _hotspotTopCursor);
|
|
}
|
|
if (_hotspotBottomAction) {
|
|
Common::Rect rect(0, 400, 640, 480);
|
|
hotspot(rect, _hotspotBottomAction, _hotspotBottomCursor);
|
|
}
|
|
if (_hotspotRightAction) {
|
|
Common::Rect rect(560, 0, 640, 480);
|
|
hotspot(rect, _hotspotRightAction, 2);
|
|
}
|
|
if (_hotspotLeftAction) {
|
|
Common::Rect rect(0, 0, 80, 480);
|
|
hotspot(rect, _hotspotLeftAction, 1);
|
|
}
|
|
|
|
// Nothing to do
|
|
if (_inputLoopAddress) {
|
|
if (_newCursorStyle != _vm->_grvCursorMan->getStyle()) {
|
|
_vm->_grvCursorMan->setStyle(_newCursorStyle);
|
|
}
|
|
_vm->_grvCursorMan->show(true);
|
|
|
|
// Go back to the begining of the loop
|
|
_currentInstruction = _inputLoopAddress;
|
|
|
|
// There's nothing to do until we get some input
|
|
_vm->waitForInput();
|
|
}
|
|
}
|
|
|
|
void Script::o_random() {
|
|
uint16 varnum = readScript8or16bits();
|
|
uint8 maxnum = readScript8bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: RANDOM: var[0x%04X] = rand(%d)", varnum, maxnum);
|
|
|
|
// TODO: Check if this is really different between the Engines
|
|
if (_version == kGroovieT7G) {
|
|
setVariable(varnum, _random.getRandomNumber(maxnum));
|
|
} else {
|
|
setVariable(varnum, _random.getRandomNumber(maxnum - 1));
|
|
}
|
|
}
|
|
|
|
void Script::o_jmp() {
|
|
uint16 address = readScript16bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: JMP @0x%04X", address);
|
|
|
|
// Set the current address
|
|
_currentInstruction = address;
|
|
}
|
|
|
|
void Script::o_loadstring() {
|
|
uint16 varnum = readScript8or16bits();
|
|
|
|
debugCN(1, kDebugScript, "Groovie::Script: LOADSTRING var[0x%04X..] =", varnum);
|
|
do {
|
|
setVariable(varnum++, readScriptChar(true, true, true));
|
|
debugCN(1, kDebugScript, " 0x%02X", _variables[varnum - 1]);
|
|
} while (!(getCodeByte(_currentInstruction - 1) & 0x80));
|
|
debugCN(1, kDebugScript, "\n");
|
|
}
|
|
|
|
void Script::o_ret() {
|
|
uint8 val = readScript8bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: RET %d", val);
|
|
|
|
// Set the return value
|
|
setVariable(0x102, val);
|
|
|
|
// Get the return address
|
|
if (_stacktop > 0) {
|
|
_stacktop--;
|
|
_currentInstruction = _stack[_stacktop];
|
|
} else {
|
|
error("Groovie::Script: Return: Stack is empty");
|
|
}
|
|
}
|
|
|
|
void Script::o_call() {
|
|
uint16 address = readScript16bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: CALL @0x%04X", address);
|
|
|
|
// Save return address in the call stack
|
|
_stack[_stacktop] = _currentInstruction;
|
|
_stacktop++;
|
|
|
|
// Change the current instruction
|
|
_currentInstruction = address;
|
|
}
|
|
|
|
void Script::o_sleep() {
|
|
uint16 time = readScript16bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: SLEEP 0x%04X (%d ms)", time, time*3);
|
|
|
|
uint32 endTime = _vm->_system->getMillis() + time * 3;
|
|
|
|
Common::Event ev;
|
|
while (_vm->_system->getMillis() < endTime) {
|
|
_vm->_system->getEventManager()->pollEvent(ev);
|
|
_vm->_system->updateScreen();
|
|
_vm->_system->delayMillis(10);
|
|
}
|
|
}
|
|
|
|
void Script::o_strcmpnejmp() { // 0x1A
|
|
uint16 varnum = readScript8or16bits();
|
|
uint8 result = 1;
|
|
|
|
debugCN(1, kDebugScript, "Groovie::Script: STRCMP-NEJMP: var[0x%04X..],", varnum);
|
|
|
|
do {
|
|
uint8 val = readScriptChar(true, true, true);
|
|
|
|
if (_variables[varnum] != val) {
|
|
result = 0;
|
|
}
|
|
varnum++;
|
|
debugCN(1, kDebugScript, " 0x%02X", val);
|
|
} while (!(getCodeByte(_currentInstruction - 1) & 0x80));
|
|
|
|
uint16 address = readScript16bits();
|
|
if (!result) {
|
|
debugC(1, kDebugScript, " jumping to @0x%04X", address);
|
|
_currentInstruction = address;
|
|
} else {
|
|
debugC(1, kDebugScript, " not jumping");
|
|
}
|
|
}
|
|
|
|
void Script::o_xor_obfuscate() {
|
|
uint16 varnum = readScript8or16bits();
|
|
|
|
debugCN(1, kDebugScript, "Groovie::Script: XOR OBFUSCATE: var[0x%04X..] = ", varnum);
|
|
do {
|
|
uint8 val = readScript8bits();
|
|
_firstbit = ((val & 0x80) != 0);
|
|
val &= 0x4F;
|
|
|
|
setVariable(varnum, _variables[varnum] ^ val);
|
|
debugCN(1, kDebugScript, "%c", _variables[varnum]);
|
|
|
|
varnum++;
|
|
} while (!_firstbit);
|
|
debugCN(1, kDebugScript, "\n");
|
|
}
|
|
|
|
void Script::o_vdxtransition() { // 0x1C
|
|
uint16 fileref = readScript16bits();
|
|
|
|
// Show the debug information just when starting the playback
|
|
if (fileref != _videoRef) {
|
|
debugC(1, kDebugScript, "Groovie::Script: VDX transition fileref = 0x%04X", fileref);
|
|
debugC(2, kDebugVideo, "\nGroovie::Script: @0x%04X: Playing video %d via 0x1C (VdxTransition)", _currentInstruction-3, fileref);
|
|
}
|
|
|
|
// Set bit 1
|
|
_bitflags |= 1 << 1;
|
|
|
|
// Clear bit 7
|
|
_bitflags &= ~(1 << 7);
|
|
|
|
// Set bit 2 if _firstbit
|
|
if (_firstbit) {
|
|
_bitflags |= 1 << 2;
|
|
}
|
|
|
|
// Play the video
|
|
if (!playvideofromref(fileref)) {
|
|
// Move _currentInstruction back
|
|
_currentInstruction -= 3;
|
|
}
|
|
}
|
|
|
|
void Script::o_swap() {
|
|
uint16 varnum1 = readScript8or16bits();
|
|
uint16 varnum2 = readScript16bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: SWAP var[0x%04X] <-> var[0x%04X]", varnum1, varnum2);
|
|
|
|
uint8 tmp = _variables[varnum1];
|
|
setVariable(varnum1, _variables[varnum2]);
|
|
setVariable(varnum2, tmp);
|
|
}
|
|
|
|
void Script::o_inc() {
|
|
uint16 varnum = readScript8or16bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: INC var[0x%04X]", varnum);
|
|
|
|
setVariable(varnum, _variables[varnum] + 1);
|
|
}
|
|
|
|
void Script::o_dec() {
|
|
uint16 varnum = readScript8or16bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: DEC var[0x%04X]", varnum);
|
|
|
|
setVariable(varnum, _variables[varnum] - 1);
|
|
}
|
|
|
|
void Script::o_strcmpnejmp_var() { // 0x21
|
|
uint16 data = readScriptVar();
|
|
|
|
if (data > 9) {
|
|
data -= 7;
|
|
}
|
|
data = _variables[data + 0x19];
|
|
bool stringsmatch = 1;
|
|
do {
|
|
if (_variables[data++] != readScriptChar(true, true, true)) {
|
|
stringsmatch = 0;
|
|
}
|
|
} while (!(getCodeByte(_currentInstruction - 1) & 0x80));
|
|
|
|
uint16 offset = readScript16bits();
|
|
if (!stringsmatch) {
|
|
_currentInstruction = offset;
|
|
}
|
|
}
|
|
|
|
void Script::o_copybgtofg() { // 0x22
|
|
debugC(1, kDebugScript, "Groovie::Script: COPY_BG_TO_FG");
|
|
debugC(2, kDebugVideo, "Groovie::Script: @0x%04X: COPY_BG_TO_FG", _currentInstruction - 1);
|
|
size_t len = _vm->_graphicsMan->_foreground.pitch * _vm->_graphicsMan->_foreground.h;
|
|
memcpy(_vm->_graphicsMan->_foreground.getPixels(), _vm->_graphicsMan->_background.getPixels(), len);
|
|
}
|
|
|
|
void Script::o2_copybgtofg() { // 0x22
|
|
debugC(1, kDebugScript, "Groovie::Script: COPY_SCREEN_TO_BG");
|
|
debugC(2, kDebugVideo, "Groovie::Script: @0x%04X: COPY_SCREEN_TO_BG", _currentInstruction - 1);
|
|
|
|
Graphics::Surface *screen = _vm->_system->lockScreen();
|
|
if (_vm->_graphicsMan->isFullScreen()) {
|
|
_vm->_graphicsMan->_foreground.copyFrom(screen->getSubArea(Common::Rect(0, 0, 640, 480)));
|
|
} else {
|
|
_vm->_graphicsMan->_foreground.copyFrom(screen->getSubArea(Common::Rect(0, 80, 640, 400)));
|
|
}
|
|
_vm->_system->unlockScreen();
|
|
}
|
|
|
|
void Script::o_strcmpeqjmp() { // 0x23
|
|
uint16 varnum = readScript8or16bits();
|
|
uint8 result = 1;
|
|
|
|
debugCN(1, kDebugScript, "Groovie::Script: STRCMP-EQJMP: var[0x%04X..],", varnum);
|
|
do {
|
|
uint8 val = readScriptChar(true, true, true);
|
|
|
|
if (_variables[varnum] != val) {
|
|
result = 0;
|
|
}
|
|
varnum++;
|
|
debugCN(1, kDebugScript, " 0x%02X", val);
|
|
} while (!(getCodeByte(_currentInstruction - 1) & 0x80));
|
|
|
|
uint16 address = readScript16bits();
|
|
if (result) {
|
|
debugC(1, kDebugScript, " jumping to @0x%04X", address);
|
|
_currentInstruction = address;
|
|
} else {
|
|
debugC(1, kDebugScript, " not jumping");
|
|
}
|
|
}
|
|
|
|
void Script::o_mov() {
|
|
uint16 varnum1 = readScript8or16bits();
|
|
uint16 varnum2 = readScript16bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: MOV var[0x%04X] = var[0x%04X]", varnum1, varnum2);
|
|
|
|
setVariable(varnum1, _variables[varnum2]);
|
|
}
|
|
|
|
void Script::o_add() {
|
|
uint16 varnum1 = readScript8or16bits();
|
|
uint16 varnum2 = readScript16bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: ADD var[0x%04X] += var[0x%04X]", varnum1, varnum2);
|
|
|
|
setVariable(varnum1, _variables[varnum1] + _variables[varnum2]);
|
|
}
|
|
|
|
void Script::o_videofromstring1() {
|
|
Common::String vidName;
|
|
uint16 instStart = _currentInstruction;
|
|
uint32 fileref = getVideoRefString(vidName);
|
|
|
|
// Show the debug information just when starting the playback
|
|
if (fileref != _videoRef) {
|
|
debugC(0, kDebugScript, "Groovie::Script: VIDEOFROMSTRING1 %d ('%s')", fileref, vidName.c_str());
|
|
debugC(2, kDebugVideo, "\nGroovie::Script: @0x%04X: Playing video %d ('%s') via 0x26 (VideoFromString1)", instStart-1, fileref, vidName.c_str());
|
|
}
|
|
|
|
if (_version != kGroovieT7G) {
|
|
// Clear bit 1
|
|
_bitflags &= ~(1 << 1);
|
|
}
|
|
|
|
// Play the video
|
|
if (!playvideofromref(fileref)) {
|
|
// Move _currentInstruction back
|
|
_currentInstruction = instStart - 1;
|
|
}
|
|
}
|
|
|
|
void Script::o_videofromstring2() {
|
|
Common::String vidName;
|
|
uint16 instStart = _currentInstruction;
|
|
uint32 fileref = getVideoRefString(vidName);
|
|
|
|
// Show the debug information just when starting the playback
|
|
if (fileref != _videoRef) {
|
|
debugC(0, kDebugScript, "Groovie::Script: VIDEOFROMSTRING2 %d ('%s')", fileref, vidName.c_str());
|
|
debugC(2, kDebugVideo, "\nGroovie::Script: @0x%04X: Playing video %d ('%s') via 0x27 (VideoFromString2)", instStart-1, fileref, vidName.c_str());
|
|
}
|
|
|
|
// Set bit 1
|
|
_bitflags |= 1 << 1;
|
|
|
|
// Set bit 2 if _firstbit
|
|
if (_firstbit) {
|
|
_bitflags |= 1 << 2;
|
|
}
|
|
|
|
// Play the video
|
|
if (!playvideofromref(fileref)) {
|
|
// Move _currentInstruction back
|
|
_currentInstruction = instStart - 1;
|
|
}
|
|
}
|
|
|
|
void Script::o_stopmidi() {
|
|
debugC(1, kDebugScript, "Groovie::Script: STOPMIDI (TODO)");
|
|
}
|
|
|
|
void Script::o_endscript() {
|
|
debugC(1, kDebugScript, "Groovie::Script: END OF SCRIPT");
|
|
_vm->quitGame();
|
|
}
|
|
|
|
void Script::o_sethotspottop() {
|
|
uint16 address = readScript16bits();
|
|
uint8 cursor = readScript8bits();
|
|
|
|
debugC(5, kDebugScript, "Groovie::Script: SETHOTSPOTTOP @0x%04X cursor=%d", address, cursor);
|
|
|
|
_hotspotTopAction = address;
|
|
_hotspotTopCursor = cursor;
|
|
}
|
|
|
|
void Script::o_sethotspotbottom() {
|
|
uint16 address = readScript16bits();
|
|
uint8 cursor = readScript8bits();
|
|
|
|
debugC(5, kDebugScript, "Groovie::Script: SETHOTSPOTBOTTOM @0x%04X cursor=%d", address, cursor);
|
|
|
|
_hotspotBottomAction = address;
|
|
_hotspotBottomCursor = cursor;
|
|
}
|
|
|
|
void Script::o_loadgame() {
|
|
uint16 varnum = readScript8or16bits();
|
|
uint8 slot = _variables[varnum];
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: LOADGAME var[0x%04X] -> slot=%d (TODO)", varnum, slot);
|
|
|
|
loadgame(slot);
|
|
if (_version == kGroovieT7G) {
|
|
_vm->_system->fillScreen(0);
|
|
}
|
|
}
|
|
|
|
void Script::o_savegame() {
|
|
uint16 varnum = readScript8or16bits();
|
|
uint8 slot = _variables[varnum];
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: SAVEGAME var[0x%04X] -> slot=%d (TODO)", varnum, slot);
|
|
|
|
savegame(slot);
|
|
}
|
|
|
|
void Script::o_hotspotbottom_4() { //0x30
|
|
uint16 address = readScript16bits();
|
|
|
|
debugC(5, kDebugScript, "Groovie::Script: HOTSPOT-BOTTOM @0x%04X", address);
|
|
|
|
// Mark the 80 pixels under the game area
|
|
Common::Rect rect(0, 400, 640, 480);
|
|
hotspot(rect, address, 4);
|
|
}
|
|
|
|
void Script::o_midivolume() {
|
|
uint16 arg1 = readScript16bits();
|
|
uint16 arg2 = readScript16bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: MIDI volume: %d %d", arg1, arg2);
|
|
_vm->_musicPlayer->setGameVolume(arg1, arg2);
|
|
}
|
|
|
|
void Script::o_jne() {
|
|
int16 varnum1 = readScript8or16bits();
|
|
uint16 varnum2 = readScript16bits();
|
|
uint16 address = readScript16bits();
|
|
|
|
debugCN(1, kDebugScript, "Groovie::Script: JNE: var[var[0x%04X] - 0x31] != var[0x%04X] @0x%04X", varnum1, varnum2, address);
|
|
|
|
if (_variables[_variables[varnum1] - 0x31] != _variables[varnum2]) {
|
|
_currentInstruction = address;
|
|
debugC(1, kDebugScript, " jumping to @0x%04X", address);
|
|
} else {
|
|
debugC(1, kDebugScript, " not jumping");
|
|
}
|
|
}
|
|
|
|
void Script::o_loadstringvar() {
|
|
uint16 varnum = readScript8or16bits();
|
|
|
|
varnum = _variables[varnum] - 0x31;
|
|
debugCN(1, kDebugScript, "Groovie::Script: LOADSTRINGVAR var[0x%04X..] =", varnum);
|
|
do {
|
|
setVariable(varnum++, readScriptChar(true, true, true));
|
|
debugCN(1, kDebugScript, " 0x%02X", _variables[varnum - 1]);
|
|
} while (!(getCodeByte(_currentInstruction - 1) & 0x80));
|
|
debugCN(1, kDebugScript, "\n");
|
|
}
|
|
|
|
void Script::o_chargreatjmp() {
|
|
uint16 varnum = readScript8or16bits();
|
|
uint8 result = 0;
|
|
|
|
debugCN(1, kDebugScript, "Groovie::Script: CHARGREAT-JMP: var[0x%04X..],", varnum);
|
|
do {
|
|
uint8 val = readScriptChar(true, true, true);
|
|
|
|
if (val < _variables[varnum]) {
|
|
result = 1;
|
|
}
|
|
varnum++;
|
|
debugCN(1, kDebugScript, " 0x%02X", val);
|
|
} while (!(getCodeByte(_currentInstruction - 1) & 0x80));
|
|
|
|
uint16 address = readScript16bits();
|
|
if (result) {
|
|
debugC(1, kDebugScript, " jumping to @0x%04X", address);
|
|
_currentInstruction = address;
|
|
} else {
|
|
debugC(1, kDebugScript, " not jumping");
|
|
}
|
|
}
|
|
|
|
void Script::o_bf7off() {
|
|
debugC(1, kDebugScript, "Groovie::Script: BF7OFF: bitflag 7 turned off");
|
|
_bitflags &= ~(1 << 7);
|
|
}
|
|
|
|
void Script::o_charlessjmp() {
|
|
uint16 varnum = readScript8or16bits();
|
|
uint8 result = 0;
|
|
|
|
debugCN(1, kDebugScript, "Groovie::Script: CHARLESS-JMP: var[0x%04X..],", varnum);
|
|
do {
|
|
uint8 val = readScriptChar(true, true, true);
|
|
|
|
if (val > _variables[varnum]) {
|
|
result = 1;
|
|
}
|
|
varnum++;
|
|
debugCN(1, kDebugScript, " 0x%02X", val);
|
|
} while (!(getCodeByte(_currentInstruction - 1) & 0x80));
|
|
|
|
uint16 address = readScript16bits();
|
|
if (result) {
|
|
debugC(1, kDebugScript, " jumping to @0x%04X", address);
|
|
_currentInstruction = address;
|
|
} else {
|
|
debugC(1, kDebugScript, " not jumping");
|
|
}
|
|
}
|
|
|
|
void Script::o_copyrecttobg() { // 0x37
|
|
uint16 left = readScript16bits();
|
|
uint16 top = readScript16bits();
|
|
uint16 right = readScript16bits();
|
|
uint16 bottom = readScript16bits();
|
|
uint16 baseTop = (!_vm->_graphicsMan->isFullScreen()) ? 80 : 0;
|
|
|
|
// Sanity checks to prevent bad pointer access crashes
|
|
if (left > right) {
|
|
warning("Groovie::Script: COPYRECT left:%d > right:%d", left, right);
|
|
// swap over left and right parameters
|
|
uint16 j;
|
|
j = right;
|
|
right = left;
|
|
left = j;
|
|
}
|
|
if (top > bottom) {
|
|
warning("Groovie::Script: COPYRECT top:%d > bottom:%d", top, bottom);
|
|
// swap over top and bottom parameters
|
|
uint16 j;
|
|
j = bottom;
|
|
bottom = top;
|
|
top = j;
|
|
}
|
|
if (top < baseTop) {
|
|
warning("Groovie::Script: COPYRECT top < baseTop... clamping");
|
|
top = baseTop;
|
|
}
|
|
if (top >= 480) {
|
|
warning("Groovie::Script: COPYRECT top >= 480... clamping");
|
|
top = 480 - 1;
|
|
}
|
|
if (bottom >= 480) {
|
|
warning("Groovie::Script: COPYRECT bottom >= 480... clamping");
|
|
bottom = 480 - 1;
|
|
}
|
|
if (left >= 640) {
|
|
warning("Groovie::Script: COPYRECT left >= 640... clamping");
|
|
left = 640 - 1;
|
|
}
|
|
if (right >= 640) {
|
|
warning("Groovie::Script: COPYRECT right >= 640... clamping");
|
|
right = 640 - 1;
|
|
}
|
|
|
|
uint16 width = right - left, height = bottom - top;
|
|
uint32 offset = 0;
|
|
uint32 pitch = _vm->_graphicsMan->_foreground.pitch;
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: COPYRECT((%d,%d)->(%d,%d))", left, top, right, bottom);
|
|
debugC(2, kDebugVideo, "Groovie::Script: @0x%04X: COPYRECT((%d,%d)->(%d,%d))",_currentInstruction-9, left, top, right, bottom);
|
|
|
|
byte *fg = (byte *)_vm->_graphicsMan->_foreground.getBasePtr(left, top - baseTop);
|
|
byte *bg = (byte *)_vm->_graphicsMan->_background.getBasePtr(left, top - baseTop);
|
|
for (uint16 i = 0; i < height; i++) {
|
|
memcpy(bg + offset, fg + offset, width * _vm->_graphicsMan->_foreground.format.bytesPerPixel);
|
|
offset += pitch;
|
|
}
|
|
|
|
_vm->_system->copyRectToScreen(bg, pitch, left, top, width, height);
|
|
_vm->_graphicsMan->change();
|
|
}
|
|
|
|
void Script::o_restorestkpnt() {
|
|
debugC(1, kDebugScript, "Groovie::Script: Restore stack pointer from saved (TODO)");
|
|
}
|
|
|
|
void Script::o_obscureswap() {
|
|
uint16 var1, var2, tmp;
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: OBSCSWAP");
|
|
|
|
// Read the first variable
|
|
var1 = readScriptChar(false, true, true) * 10;
|
|
var1 += readScriptChar(false, true, true) + 0x19;
|
|
|
|
// Read the second variable
|
|
var2 = readScriptChar(false, true, true) * 10;
|
|
var2 += readScriptChar(false, true, true) + 0x19;
|
|
|
|
// Swap the values
|
|
tmp = _variables[var1];
|
|
setVariable(var1, _variables[var2]);
|
|
setVariable(var2, tmp);
|
|
}
|
|
|
|
void Script::o_printstring() {
|
|
char stringstorage[15];
|
|
uint8 counter = 0;
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: PRINTSTRING");
|
|
debugC(2, kDebugVideo, "Groovie::Script: @0x%04X: PRINTSTRING", _currentInstruction - 1);
|
|
|
|
memset(stringstorage, 0, 15);
|
|
do {
|
|
char newchar = readScriptChar(true, true, true) + 0x30;
|
|
if (newchar < 0x30 || newchar > 0x39) { // If character is invalid, chuck a space in
|
|
if (newchar < 0x41 || newchar > 0x7A) {
|
|
newchar = 0x20;
|
|
}
|
|
}
|
|
|
|
stringstorage[counter] = newchar;
|
|
counter++;
|
|
} while (!(getCodeByte(_currentInstruction - 1) & 0x80));
|
|
|
|
stringstorage[counter] = 0;
|
|
|
|
Common::Rect topbar(640, 80);
|
|
Graphics::Surface *gamescreen = _vm->_system->lockScreen();
|
|
|
|
// Clear the top bar
|
|
gamescreen->fillRect(topbar, 0);
|
|
|
|
// Draw the string
|
|
printString(gamescreen, stringstorage);
|
|
|
|
_vm->_system->unlockScreen();
|
|
}
|
|
|
|
void Script::o_hotspot_slot() {
|
|
uint16 slot = readScript8bits();
|
|
uint16 left = readScript16bits();
|
|
uint16 top = readScript16bits();
|
|
uint16 right = readScript16bits();
|
|
uint16 bottom = readScript16bits();
|
|
uint16 address = readScript16bits();
|
|
uint16 cursor = readScript8bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: HOTSPOT-SLOT %d (%d,%d,%d,%d) @0x%04X cursor=%d (TODO)", slot, left, top, right, bottom, address, cursor);
|
|
|
|
// Set rectangle according to the used engine. To remove the previously written text an the screen.
|
|
Common::Rect removeText;
|
|
if (_version == kGroovieT7G) {
|
|
removeText.left = 0;
|
|
removeText.top = 0;
|
|
removeText.right = 640;
|
|
removeText.bottom = 80;
|
|
} else {
|
|
// Only tested for 11th hour. TLC does not use this command.
|
|
removeText.left = 120;
|
|
removeText.top = 185;
|
|
removeText.right = 400;
|
|
removeText.bottom = 215;
|
|
}
|
|
|
|
Common::Rect rect(left, top, right, bottom);
|
|
if (hotspot(rect, address, cursor)) {
|
|
if (_hotspotSlot == slot) {
|
|
return;
|
|
}
|
|
|
|
Graphics::Surface *gamescreen = _vm->_system->lockScreen();
|
|
|
|
// Clear the top bar
|
|
gamescreen->fillRect(removeText, 0); // 0 works for both color formats (Groovie V1 and V2)
|
|
|
|
printString(gamescreen, _saveNames[slot].c_str());
|
|
|
|
_vm->_system->unlockScreen();
|
|
|
|
// Save the currently highlighted slot
|
|
_hotspotSlot = slot;
|
|
_vm->_graphicsMan->change();
|
|
|
|
} else {
|
|
if (_hotspotSlot == slot) {
|
|
Graphics::Surface *gamescreen;
|
|
gamescreen = _vm->_system->lockScreen();
|
|
|
|
gamescreen->fillRect(removeText, 0); // 0 works for both color formats (Groovie V1 and V2)
|
|
|
|
_vm->_system->unlockScreen();
|
|
|
|
// Removing the slot highlight
|
|
_hotspotSlot = (uint16)-1;
|
|
|
|
_vm->_graphicsMan->change();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Checks valid save games. Even for TLC (uses only 4 user save games) the function
|
|
// checks for 10 save games.
|
|
void Script::o_checkvalidsaves() {
|
|
debugC(1, kDebugScript, "Groovie::Script: CHECKVALIDSAVES");
|
|
|
|
// Reset the array of valid saves and the savegame names cache
|
|
for (int i = 0; i < MAX_SAVES; i++) {
|
|
setVariable(i, 0);
|
|
_saveNames[i] = "E M P T Y";
|
|
}
|
|
|
|
// Get the list of savefiles
|
|
SaveStateList list = SaveLoad::listValidSaves(ConfMan.getActiveDomainName());
|
|
|
|
// Mark the existing savefiles as valid
|
|
uint count = 0;
|
|
SaveStateList::iterator it = list.begin();
|
|
while (it != list.end()) {
|
|
int8 slot = it->getSaveSlot();
|
|
if (SaveLoad::isSlotValid(slot)) {
|
|
debugC(2, kDebugScript, "Groovie::Script: Found valid savegame: %s", it->getDescription().encode().c_str());
|
|
|
|
// Mark this slot as used
|
|
setVariable(slot, 1);
|
|
|
|
// Cache this slot's description
|
|
_saveNames[slot] = it->getDescription();
|
|
count++;
|
|
}
|
|
it++;
|
|
}
|
|
|
|
// Save the number of valid saves
|
|
setVariable(0x104, count);
|
|
debugC(1, kDebugScript, "Groovie::Script: Found %d valid savegames", count);
|
|
}
|
|
|
|
void Script::o_resetvars() {
|
|
debugC(1, kDebugScript, "RESETVARS");
|
|
for (int i = 0; i < 0x100; i++) {
|
|
setVariable(i, 0);
|
|
}
|
|
}
|
|
|
|
void Script::o_mod() {
|
|
uint16 varnum = readScript8or16bits();
|
|
uint8 val = readScript8bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: MOD var[0x%04X] %%= %d", varnum, val);
|
|
|
|
setVariable(varnum, _variables[varnum] % val);
|
|
}
|
|
|
|
void Script::o_loadscript() {
|
|
Common::String filename;
|
|
char c;
|
|
|
|
while ((c = readScript8bits())) {
|
|
filename += c;
|
|
}
|
|
debugC(1, kDebugScript, "Groovie::Script: LOADSCRIPT %s", filename.c_str());
|
|
|
|
// Just 1 level of sub-scripts are allowed
|
|
if (_savedCode) {
|
|
error("Groovie::Script: Tried to load a level 2 sub-script");
|
|
}
|
|
|
|
// Save the current code
|
|
_savedCode = _code;
|
|
_savedCodeSize = _codeSize;
|
|
_savedInstruction = _currentInstruction;
|
|
|
|
// Save the filename of the current script
|
|
_savedScriptFile = _scriptFile;
|
|
|
|
// Load the sub-script
|
|
if (!loadScript(filename)) {
|
|
error("Groovie::Script: Couldn't load sub-script %s", filename.c_str());
|
|
}
|
|
|
|
// Save the current stack top
|
|
_savedStacktop = _stacktop;
|
|
|
|
// Save the variables
|
|
memcpy(_savedVariables, _variables + 0x107, 0x180);
|
|
}
|
|
|
|
void Script::o_setvideoorigin() {
|
|
// Read the two offset arguments
|
|
int16 origX = readScript16bits();
|
|
int16 origY = readScript16bits();
|
|
|
|
// Set bitflag 7
|
|
_bitflags |= 1 << 7;
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: SetVideoOrigin(0x%04X,0x%04X) (%d, %d)", origX, origY, origX, origY);
|
|
_vm->_videoPlayer->setOrigin(origX, origY);
|
|
}
|
|
|
|
void Script::o_sub() {
|
|
uint16 varnum1 = readScript8or16bits();
|
|
uint16 varnum2 = readScript16bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: SUB var[0x%04X] -= var[0x%04X]", varnum1, varnum2);
|
|
|
|
setVariable(varnum1, _variables[varnum1] - _variables[varnum2]);
|
|
}
|
|
|
|
void Script::o_returnscript() {
|
|
uint8 val = readScript8bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: RETURNSCRIPT @0x%02X", val);
|
|
|
|
// Are we returning from a sub-script?
|
|
if (!_savedCode) {
|
|
error("Groovie::Script: Tried to return from the main script");
|
|
}
|
|
|
|
// Set the return value
|
|
setVariable(0x102, val);
|
|
|
|
// Restore the code
|
|
delete[] _code;
|
|
_code = _savedCode;
|
|
_codeSize = _savedCodeSize;
|
|
_savedCode = nullptr;
|
|
_currentInstruction = _savedInstruction;
|
|
|
|
// Restore the stack
|
|
_stacktop = _savedStacktop;
|
|
|
|
// Restore the variables
|
|
memcpy(_variables + 0x107, _savedVariables, 0x180);
|
|
|
|
// Restore the filename of the script
|
|
_scriptFile = _savedScriptFile;
|
|
|
|
_vm->_videoPlayer->resetFlags();
|
|
_vm->_videoPlayer->setOrigin(0, 0);
|
|
}
|
|
|
|
void Script::o_sethotspotright() {
|
|
uint16 address = readScript16bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: SETHOTSPOTRIGHT @0x%04X", address);
|
|
|
|
_hotspotRightAction = address;
|
|
}
|
|
|
|
void Script::o_sethotspotleft() {
|
|
uint16 address = readScript16bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: SETHOTSPOTLEFT @0x%04X", address);
|
|
|
|
_hotspotLeftAction = address;
|
|
}
|
|
|
|
void Script::o_getcd() {
|
|
debugC(1, kDebugScript, "Groovie::Script: GETCD");
|
|
|
|
// By default set it to no CD available
|
|
int8 cd = -1;
|
|
|
|
// Try to open one file from each CD
|
|
Common::File cdfile;
|
|
if (cdfile.open("b.gjd")) {
|
|
cdfile.close();
|
|
cd = 1;
|
|
}
|
|
if (cdfile.open("at.gjd")) {
|
|
cdfile.close();
|
|
if (cd == 1) {
|
|
// Both CDs are available
|
|
cd = 0;
|
|
} else {
|
|
cd = 2;
|
|
}
|
|
}
|
|
|
|
setVariable(0x106, cd);
|
|
}
|
|
|
|
void Script::o_playcd() {
|
|
uint8 val = readScript8bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: PLAYCD %d", val);
|
|
|
|
if (val == 2) {
|
|
// TODO: Play the alternative logo
|
|
}
|
|
|
|
_vm->_musicPlayer->playCD(val);
|
|
}
|
|
|
|
void Script::o_musicdelay() {
|
|
uint16 delay = readScript16bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: MUSICDELAY %d", delay);
|
|
|
|
_vm->_musicPlayer->setBackgroundDelay(delay);
|
|
}
|
|
|
|
void Script::o_hotspot_outrect() {
|
|
uint16 left = readScript16bits();
|
|
uint16 top = readScript16bits();
|
|
uint16 right = readScript16bits();
|
|
uint16 bottom = readScript16bits();
|
|
uint16 address = readScript16bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: HOTSPOT-OUTRECT(%d,%d,%d,%d) @0x%04X (TODO)", left, top, right, bottom, address);
|
|
|
|
// Test if the current mouse position is outside the specified rectangle
|
|
Common::Rect rect(left, top, right, bottom);
|
|
Common::Point mousepos = _vm->_system->getEventManager()->getMousePos();
|
|
bool contained = rect.contains(mousepos);
|
|
|
|
if (!contained) {
|
|
_currentInstruction = address;
|
|
}
|
|
}
|
|
|
|
void Script::o_stub56() {
|
|
uint32 val1 = readScript32bits();
|
|
uint8 val2 = readScript8bits();
|
|
uint8 val3 = readScript8bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: STUB56: 0x%08X 0x%02X 0x%02X", val1, val2, val3);
|
|
}
|
|
|
|
void Script::o_stub59() {
|
|
uint16 val1 = readScript8or16bits();
|
|
uint8 val2 = readScript8bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: STUB59: 0x%04X 0x%02X", val1, val2);
|
|
}
|
|
|
|
void Script::o2_printstring() {
|
|
uint16 posx = readScript16bits();
|
|
uint16 posy = readScript16bits();
|
|
uint8 colr = readScript8bits();
|
|
uint8 colg = readScript8bits();
|
|
uint8 colb = readScript8bits();
|
|
uint32 col = _vm->_pixelFormat.RGBToColor(colr, colg, colb);
|
|
Common::String text;
|
|
|
|
// Read string from Script
|
|
readScriptString(text);
|
|
debugC(1, kDebugScript, "Groovie::Script: PRINTSTRING (%d, %d): %s", posx, posy, text.c_str());
|
|
|
|
Graphics::Surface *gamescreen = _vm->_system->lockScreen();
|
|
_vm->_font->drawString(gamescreen, text.c_str(), posx, posy, 640, col, Graphics::kTextAlignLeft);
|
|
_vm->_system->unlockScreen();
|
|
_vm->_graphicsMan->change(); // Force Update screen after step
|
|
}
|
|
|
|
void Script::o2_playsong() {
|
|
uint32 fileref = readScript32bits();
|
|
debugC(1, kDebugScript, "Groovie::Script: PlaySong(0x%08X): Play xmidi file", fileref);
|
|
_vm->_musicPlayer->playSong(fileref);
|
|
}
|
|
|
|
void Script::o2_midicontrol() {
|
|
uint16 arg1 = readScript16bits();
|
|
uint16 arg2 = readScript16bits();
|
|
|
|
switch (arg1) {
|
|
case 0:
|
|
// Stop Playback
|
|
debugC(1, kDebugScript, "Groovie::Script: MIDI %d:Stop: %d", arg1, arg2);
|
|
_vm->_musicPlayer->stop();
|
|
_vm->_soundQueue.stopAll();
|
|
break;
|
|
|
|
case 1:
|
|
// Play song from index
|
|
debugC(1, kDebugScript, "Groovie::Script: MIDI %d: Play song %d", arg1, arg2);
|
|
_vm->_musicPlayer->playSong(arg2);
|
|
break;
|
|
|
|
case 3:
|
|
// TODO: Set Volume? Or is it some kind of fade in / out
|
|
debugC(1, kDebugScript, "Groovie::Script: MIDI %d: Set volume/time: %d", arg1, arg2);
|
|
//_vm->_musicPlayer->setGameVolume(arg2, 0);
|
|
break;
|
|
}
|
|
}
|
|
void Script::o2_setbackgroundsong() {
|
|
uint32 fileref = readScript32bits();
|
|
debugC(1, kDebugScript, "Groovie::Script: SetBackgroundSong(0x%08X)", fileref);
|
|
_vm->_musicPlayer->setBackgroundSong(fileref);
|
|
}
|
|
|
|
void Script::o2_videofromref() {
|
|
uint32 fileref = readScript32bits();
|
|
|
|
// Skip the 11th Hour intro videos on right mouse click, instead of
|
|
// fast-forwarding them. This has the same effect as pressing 'p' twice in
|
|
// the skulls screen after the Groovie logo
|
|
if (_version == kGroovieT11H && fileref == 4926 && fileref != _videoRef)
|
|
_videoSkipAddress = 1417;
|
|
|
|
if (_version == kGroovieT11H && fileref == 4337 && !ConfMan.getBool("originalsaveload")) {
|
|
if (_currentInstruction == 0xE50A) {
|
|
// Load from the main menu
|
|
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Restore game:"), _("Restore"), false);
|
|
int slot = dialog->runModalWithCurrentTarget();
|
|
delete dialog;
|
|
|
|
if (slot >= 0) {
|
|
_currentInstruction = 0xE790;
|
|
loadgame(slot);
|
|
return;
|
|
} else {
|
|
_currentInstruction = 0xBF37; // main menu
|
|
}
|
|
} else if (_currentInstruction == 0xE955) {
|
|
// Save from the main menu
|
|
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser(_("Save game:"), _("Save"), true);
|
|
int slot = dialog->runModalWithCurrentTarget();
|
|
Common::String saveName = dialog->getResultString();
|
|
delete dialog;
|
|
|
|
if (slot >= 0) {
|
|
directGameSave(slot, saveName);
|
|
}
|
|
|
|
_currentInstruction = 0xBF37; // main menu
|
|
}
|
|
}
|
|
|
|
// Show the debug information just when starting the playback
|
|
if (fileref != _videoRef) {
|
|
debugC(1, kDebugScript, "Groovie::Script: VIDEOFROMREF(0x%08X) (Not fully imp): Play video file from ref", fileref);
|
|
debugC(2, kDebugVideo, "\nGroovie::Script: @0x%04X: Playing video %d via 0x09", _currentInstruction-5, fileref);
|
|
}
|
|
|
|
// Clear bit 1
|
|
_bitflags &= ~(1 << 1);
|
|
|
|
// Play the video
|
|
if (!playvideofromref(fileref)) {
|
|
// Move _currentInstruction back
|
|
_currentInstruction -= 5;
|
|
}
|
|
}
|
|
|
|
void Script::o2_vdxtransition() {
|
|
uint32 fileref = readScript32bits();
|
|
|
|
// Show the debug information just when starting the playback
|
|
if (fileref != _videoRef) {
|
|
debugC(1, kDebugScript, "Groovie::Script: VDX transition fileref = 0x%08X", fileref);
|
|
debugC(2, kDebugVideo, "\nGroovie::Script: @0x%04X: Playing video %d with transition via 0x1C", _currentInstruction-5, fileref);
|
|
}
|
|
|
|
// Set bit 1
|
|
_bitflags |= 1 << 1;
|
|
|
|
// Set bit 2 if _firstbit
|
|
if (_firstbit) {
|
|
_bitflags |= 1 << 2;
|
|
}
|
|
|
|
// Play the video
|
|
if (!playvideofromref(fileref)) {
|
|
// Move _currentInstruction back
|
|
_currentInstruction -= 5;
|
|
}
|
|
}
|
|
|
|
void Script::o2_savescreen() {
|
|
uint16 val = readScript16bits();
|
|
|
|
// TODO: Parameter
|
|
if (val)
|
|
warning("Groovie::Script: o2_copyscreentobg: Param is %d", val);
|
|
|
|
_vm->_graphicsMan->saveScreen();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: CopyScreenToBG3: 0x%04X", val);
|
|
debugC(2, kDebugVideo, "Groovie::Script: @0x%04X: CopyScreenToBG3: 0x%04X", _currentInstruction-3, val);
|
|
}
|
|
|
|
void Script::o2_restorescreen() {
|
|
uint16 val = readScript16bits();
|
|
|
|
// TODO: Parameter
|
|
if (val)
|
|
warning("Groovie::Script: o2_copybgtoscreen: Param is %d", val);
|
|
|
|
_vm->_graphicsMan->restoreScreen();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: CopyBG3ToScreen: 0x%04X", val);
|
|
debugC(2, kDebugVideo, "Groovie::Script: @0x%04X: CopyBG3ToScreen: 0x%04X", _currentInstruction-3, val);
|
|
}
|
|
|
|
void Script::o2_setvideoskip() {
|
|
_videoSkipAddress = readScript16bits();
|
|
debugC(1, kDebugScript, "Groovie::Script: SetVideoSkip (0x%04X)", _videoSkipAddress);
|
|
}
|
|
|
|
// This function depends on the actual game played. There was an initial version
|
|
// for T7G, and then it kept being expanded in newer games (11H, Clan, UHP). This
|
|
// means that newer games contained logic used in older ones (e.g. Clandestiny
|
|
// and UHP include the hardcoded puzzle logic of 11H).
|
|
void Script::o_gamelogic() {
|
|
uint8 param = readScript8bits();
|
|
debugC(1, kDebugScript, "Groovie::Script: Mini game logic, param %d", param);
|
|
|
|
switch (_version) {
|
|
case kGroovieT7G:
|
|
if (!_cellGame)
|
|
_cellGame = new CellGame(ConfMan.getBool("easier_ai"));
|
|
|
|
_cellGame->run(param, &_variables[0x19]);
|
|
|
|
// Set the movement origin
|
|
setVariable(0, _cellGame->getStartY()); // y
|
|
setVariable(1, _cellGame->getStartX()); // x
|
|
// Set the movement destination
|
|
setVariable(2, _cellGame->getEndY());
|
|
setVariable(3, _cellGame->getEndX());
|
|
break;
|
|
|
|
#ifdef ENABLE_GROOVIE2
|
|
case kGroovieT11H:
|
|
case kGroovieCDY:
|
|
case kGroovieUHP:
|
|
switch (param) {
|
|
case 1: // 11H Cake puzzle in the dining room (tb.grv)
|
|
_cake.run(_variables);
|
|
break;
|
|
case 2: // 11H/UHP Beehive puzzle in the top room (hs.grv)
|
|
_beehive.run(_variables);
|
|
break;
|
|
case 3: // 11H Gallery puzzle in the modern art painting (bs.grv)
|
|
_gallery.run(_variables);
|
|
break;
|
|
case 4: // 11H Triangle puzzle in the chapel (tx.grv)
|
|
_triangle.run(_variables);
|
|
break;
|
|
case 5: // 11H/UHP Mouse trap puzzle in the lab (al.grv)
|
|
_mouseTrap.run(_variables);
|
|
break;
|
|
case 6: // 11H Pente puzzle at the end of the game (pt.grv)
|
|
_pente.run(_variables);
|
|
break;
|
|
case 7: // Clan Wine rack puzzle
|
|
_wineRack.run(_variables);
|
|
break;
|
|
case 8: // Clan/UHP Othello/Reversi puzzle
|
|
_othello.run(_variables);
|
|
break;
|
|
default:
|
|
debugC(1, kDebugScript, "Groovie::Script: Op42 (0x%02X): Invalid -> NOP", param);
|
|
}
|
|
break;
|
|
|
|
case kGroovieTLC:
|
|
if (!_tlcGame)
|
|
_tlcGame = new TlcGame(_variables);
|
|
|
|
_tlcGame->handleOp(param);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
warning("Groovie::Script: OpCode 0x42 (param %d) for current game is not implemented yet.", param);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Script::o2_copyfgtobg() {
|
|
uint8 arg = readScript8bits();
|
|
debugC(1, kDebugScript, "Groovie::Script: o2_copyfgtobg (0x%02X)", arg);
|
|
debugC(2, kDebugVideo, "Groovie::Script: @0x%04X: o2_copyfgtobg (0x%02X)", _currentInstruction-2, arg);
|
|
// return;
|
|
|
|
_vm->_graphicsMan->_background.copyFrom(_vm->_graphicsMan->_foreground);
|
|
_vm->_graphicsMan->updateScreen(&_vm->_graphicsMan->_background);
|
|
}
|
|
|
|
void Script::o2_setscriptend() {
|
|
uint16 arg = readScript16bits();
|
|
debugC(1, kDebugScript, "Groovie::Script: SetScriptEnd (0x%04X)", arg);
|
|
}
|
|
|
|
void Script::o2_playsound() {
|
|
uint32 fileref = readScript32bits();
|
|
uint8 loops = readScript8bits();// 0 means loop forever, 1 means play once
|
|
uint8 val3 = readScript8bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: o2_playsound: 0x%08X 0x%02X 0x%02X", fileref, loops, val3);
|
|
|
|
if (fileref == 0 && loops == 0) {
|
|
_vm->_soundQueue.stopAll();
|
|
return;
|
|
}
|
|
|
|
playBackgroundSound(fileref, loops);
|
|
}
|
|
|
|
void Script::o_wipemaskfromstring58() {
|
|
// used in pente when pieces are captured
|
|
Common::String vidName;
|
|
uint16 instStart = _currentInstruction;
|
|
uint32 fileref = getVideoRefString(vidName);
|
|
setBitFlag(10, true);
|
|
|
|
// Show the debug information just when starting the playback
|
|
if (fileref != _videoRef) {
|
|
debugC(0, kDebugScript, "Groovie::Script: WIPEMASKFROMSTRING58 %d ('%s')", fileref, vidName.c_str());
|
|
debugC(2, kDebugVideo, "\nGroovie::Script: @0x%04X: Playing mask video %d ('%s') via 0x58 (o_wipemaskfromstring58)", instStart - 1, fileref, vidName.c_str());
|
|
}
|
|
|
|
// Play the video
|
|
if (!playvideofromref(fileref)) {
|
|
// Move _currentInstruction back
|
|
_currentInstruction = instStart - 1;
|
|
}
|
|
}
|
|
|
|
void Script::o2_check_sounds_overlays() {
|
|
uint16 val1 = readScript8or16bits();
|
|
uint8 val2 = readScript8bits();
|
|
|
|
debugC(1, kDebugScript, "Groovie::Script: STUB59: 0x%04X 0x%02X", val1, val2);
|
|
|
|
// bitflag 0 is set by background sounds (clock chimes, wind, heart, drip in the kitchen)
|
|
// bitflag 2 is set by overlay videos
|
|
// this instruction is notably used at the end of the game when you have until midnight to choose a door
|
|
_variables[val1] = getBitFlag(0) || getBitFlag(2);
|
|
}
|
|
|
|
void Script::o2_preview_loadgame() {
|
|
uint8 save_slot = readScript8bits();
|
|
|
|
if (preview_loadgame(save_slot))
|
|
return;
|
|
|
|
for (int i = 0; i < 15; i++) {
|
|
_variables[i] = 0xf0;
|
|
}
|
|
|
|
for (int i = 15; i < 22; i++) {
|
|
_variables[i] = 0x4a;
|
|
}
|
|
}
|
|
|
|
Script::OpcodeFunc Script::_opcodesT7G[NUM_OPCODES] = {
|
|
&Script::o_nop, // 0x00
|
|
&Script::o_nop,
|
|
&Script::o_playsong,
|
|
&Script::o_bf9on,
|
|
&Script::o_palfadeout, // 0x04
|
|
&Script::o_bf8on,
|
|
&Script::o_bf6on,
|
|
&Script::o_bf7on,
|
|
&Script::o_setbackgroundsong, // 0x08
|
|
&Script::o_videofromref,
|
|
&Script::o_bf5on,
|
|
&Script::o_inputloopstart,
|
|
&Script::o_keyboardaction, // 0x0C
|
|
&Script::o_hotspot_rect,
|
|
&Script::o_hotspot_left,
|
|
&Script::o_hotspot_right,
|
|
&Script::o_hotspot_center, // 0x10
|
|
&Script::o_hotspot_center,
|
|
&Script::o_hotspot_current,
|
|
&Script::o_inputloopend,
|
|
&Script::o_random, // 0x14
|
|
&Script::o_jmp,
|
|
&Script::o_loadstring,
|
|
&Script::o_ret,
|
|
&Script::o_call, // 0x18
|
|
&Script::o_sleep,
|
|
&Script::o_strcmpnejmp,
|
|
&Script::o_xor_obfuscate,
|
|
&Script::o_vdxtransition, // 0x1C
|
|
&Script::o_swap,
|
|
&Script::o_nop8,
|
|
&Script::o_inc,
|
|
&Script::o_dec, // 0x20
|
|
&Script::o_strcmpnejmp_var,
|
|
&Script::o_copybgtofg,
|
|
&Script::o_strcmpeqjmp,
|
|
&Script::o_mov, // 0x24
|
|
&Script::o_add,
|
|
&Script::o_videofromstring1, // Reads a string and then does stuff: used by book in library
|
|
&Script::o_videofromstring2, // play vdx file from string, after setting 1 (and 2 if firstbit)
|
|
&Script::o_nop16, // 0x28
|
|
&Script::o_stopmidi,
|
|
&Script::o_endscript,
|
|
&Script::o_nop,
|
|
&Script::o_sethotspottop, // 0x2C
|
|
&Script::o_sethotspotbottom,
|
|
&Script::o_loadgame,
|
|
&Script::o_savegame,
|
|
&Script::o_hotspotbottom_4, // 0x30
|
|
&Script::o_midivolume,
|
|
&Script::o_jne,
|
|
&Script::o_loadstringvar,
|
|
&Script::o_chargreatjmp, // 0x34
|
|
&Script::o_bf7off,
|
|
&Script::o_charlessjmp,
|
|
&Script::o_copyrecttobg,
|
|
&Script::o_restorestkpnt, // 0x38
|
|
&Script::o_obscureswap,
|
|
&Script::o_printstring,
|
|
&Script::o_hotspot_slot,
|
|
&Script::o_checkvalidsaves, // 0x3C
|
|
&Script::o_resetvars,
|
|
&Script::o_mod,
|
|
&Script::o_loadscript,
|
|
&Script::o_setvideoorigin, // 0x40
|
|
&Script::o_sub,
|
|
&Script::o_gamelogic,
|
|
&Script::o_returnscript,
|
|
&Script::o_sethotspotright, // 0x44
|
|
&Script::o_sethotspotleft,
|
|
&Script::o_nop,
|
|
&Script::o_nop,
|
|
&Script::o_nop8, // 0x48
|
|
&Script::o_nop,
|
|
&Script::o_nop16,
|
|
&Script::o_nop8,
|
|
&Script::o_getcd, // 0x4C
|
|
&Script::o_playcd,
|
|
&Script::o_musicdelay,
|
|
&Script::o_nop16,
|
|
&Script::o_nop16, // 0x50
|
|
&Script::o_nop16,
|
|
//&Script::o_nop8,
|
|
&Script::o_invalid, // Do loads with game area, maybe draw dirty areas?
|
|
&Script::o_hotspot_outrect,
|
|
&Script::o_nop, // 0x54
|
|
&Script::o_nop16,
|
|
&Script::o_stub56,
|
|
//&Script::o_nop32,
|
|
&Script::o_invalid, // completely unimplemented, plays vdx in some way
|
|
//&Script::o_nop, // 0x58
|
|
&Script::o_invalid, // 0x58 // like above, but plays from string not ref
|
|
&Script::o_stub59,
|
|
&Script::o_invalid
|
|
};
|
|
|
|
Script::OpcodeFunc Script::_opcodesV2[NUM_OPCODES] = {
|
|
&Script::o_invalid, // 0x00
|
|
&Script::o_nop,
|
|
&Script::o2_playsong,
|
|
&Script::o_nop,
|
|
&Script::o_nop, // 0x04
|
|
&Script::o_nop,
|
|
&Script::o_nop,
|
|
&Script::o_nop,
|
|
&Script::o2_setbackgroundsong, // 0x08
|
|
&Script::o2_videofromref,
|
|
&Script::o2_bf0on,
|
|
&Script::o_inputloopstart,
|
|
&Script::o_keyboardaction, // 0x0C
|
|
&Script::o_hotspot_rect,
|
|
&Script::o_hotspot_left,
|
|
&Script::o_hotspot_right,
|
|
&Script::o_hotspot_center, // 0x10
|
|
&Script::o_hotspot_center,
|
|
&Script::o_hotspot_current,
|
|
&Script::o_inputloopend,
|
|
&Script::o_random, // 0x14
|
|
&Script::o_jmp,
|
|
&Script::o_loadstring,
|
|
&Script::o_ret,
|
|
&Script::o_call, // 0x18
|
|
&Script::o_sleep,
|
|
&Script::o_strcmpnejmp,
|
|
&Script::o_xor_obfuscate,
|
|
&Script::o2_vdxtransition, // 0x1C
|
|
&Script::o_swap,
|
|
&Script::o_invalid,
|
|
&Script::o_inc,
|
|
&Script::o_dec, // 0x20
|
|
&Script::o_strcmpnejmp_var,
|
|
&Script::o2_copybgtofg,
|
|
&Script::o_strcmpeqjmp,
|
|
&Script::o_mov, // 0x24
|
|
&Script::o_add,
|
|
&Script::o_videofromstring1,
|
|
&Script::o_videofromstring2,
|
|
&Script::o_invalid, // 0x28
|
|
&Script::o_nop,
|
|
&Script::o_endscript,
|
|
&Script::o_invalid,
|
|
&Script::o_sethotspottop, // 0x2C
|
|
&Script::o_sethotspotbottom,
|
|
&Script::o_loadgame,
|
|
&Script::o_savegame,
|
|
&Script::o_hotspotbottom_4, // 0x30
|
|
&Script::o2_midicontrol,
|
|
&Script::o_jne,
|
|
&Script::o_loadstringvar,
|
|
&Script::o_chargreatjmp, // 0x34
|
|
&Script::o_bf7off,
|
|
&Script::o_charlessjmp,
|
|
&Script::o_copyrecttobg,
|
|
&Script::o_restorestkpnt, // 0x38
|
|
&Script::o_obscureswap,
|
|
&Script::o2_printstring,
|
|
&Script::o_hotspot_slot,
|
|
&Script::o_checkvalidsaves, // 0x3C
|
|
&Script::o_resetvars,
|
|
&Script::o_mod,
|
|
&Script::o_loadscript,
|
|
&Script::o_setvideoorigin, // 0x40
|
|
&Script::o_sub,
|
|
&Script::o_gamelogic,
|
|
&Script::o_returnscript,
|
|
&Script::o_sethotspotright, // 0x44
|
|
&Script::o_sethotspotleft,
|
|
&Script::o_invalid,
|
|
&Script::o_invalid,
|
|
&Script::o_invalid, // 0x48
|
|
&Script::o_invalid,
|
|
&Script::o_nop16,
|
|
&Script::o_invalid,
|
|
&Script::o_invalid, // 0x4C
|
|
&Script::o_invalid,
|
|
&Script::o_invalid,
|
|
&Script::o2_savescreen,
|
|
&Script::o2_restorescreen, // 0x50
|
|
&Script::o2_setvideoskip,
|
|
&Script::o2_copyfgtobg,
|
|
&Script::o_hotspot_outrect,
|
|
&Script::o_invalid, // 0x54
|
|
&Script::o2_setscriptend,
|
|
&Script::o2_playsound,
|
|
&Script::o_invalid,
|
|
&Script::o_wipemaskfromstring58, // 0x58
|
|
&Script::o2_check_sounds_overlays,
|
|
&Script::o2_preview_loadgame
|
|
};
|
|
|
|
} // End of Groovie namespace
|