2004-04-12 21:40:49 +00:00
|
|
|
/* ScummVM - Scumm Interpreter
|
2005-01-01 16:20:17 +00:00
|
|
|
* Copyright (C) 2004-2005 The ScummVM project
|
2004-04-12 21:40:49 +00:00
|
|
|
*
|
|
|
|
* The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
|
|
|
|
*
|
|
|
|
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*
|
|
|
|
* $Header$
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Scripting module: Script resource handling functions
|
2004-08-02 16:20:35 +00:00
|
|
|
#include "saga/saga.h"
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-02 16:20:35 +00:00
|
|
|
#include "saga/gfx.h"
|
|
|
|
#include "saga/rscfile_mod.h"
|
2004-08-10 18:31:33 +00:00
|
|
|
#include "saga/console.h"
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-02 16:20:35 +00:00
|
|
|
#include "saga/script.h"
|
2004-12-15 00:24:12 +00:00
|
|
|
#include "saga/stream.h"
|
2005-01-08 20:30:07 +00:00
|
|
|
#include "saga/interface.h"
|
2005-01-15 20:12:49 +00:00
|
|
|
#include "saga/actordata.h"
|
|
|
|
#include "saga/scene.h"
|
|
|
|
#include "saga/events.h"
|
|
|
|
#include "saga/actor.h"
|
|
|
|
#include "saga/objectdata.h"
|
|
|
|
#include "saga/objectmap.h"
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
namespace Saga {
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Initializes the scripting module.
|
|
|
|
// Loads script resource look-up table, initializes script data system
|
2004-08-01 22:48:40 +00:00
|
|
|
Script::Script() {
|
2004-10-27 21:32:28 +00:00
|
|
|
RSCFILE_CONTEXT *s_lut_ctxt;
|
2005-01-08 20:30:07 +00:00
|
|
|
RSCFILE_CONTEXT *resourceContext;
|
2004-04-30 23:02:23 +00:00
|
|
|
byte *rsc_ptr;
|
2004-04-12 21:40:49 +00:00
|
|
|
size_t rsc_len;
|
2004-05-04 03:33:03 +00:00
|
|
|
int prevTell;
|
2004-04-12 21:40:49 +00:00
|
|
|
int result;
|
2004-05-04 03:33:03 +00:00
|
|
|
int i, j;
|
2005-01-08 20:30:07 +00:00
|
|
|
byte *stringsPointer;
|
|
|
|
size_t stringsLength;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-01 22:48:40 +00:00
|
|
|
//initialize member variables
|
2004-10-21 06:49:11 +00:00
|
|
|
_dbg_thread = 0;
|
2004-12-29 16:05:12 +00:00
|
|
|
_dbg_singlestep = 0;
|
2004-08-01 23:47:19 +00:00
|
|
|
_scriptContext = 0;
|
|
|
|
_voiceLUTPresent = false;
|
|
|
|
_scriptLUTEntryLen = 0;
|
|
|
|
_currentScript = 0;
|
2004-10-27 02:27:54 +00:00
|
|
|
_abortEnabled = true;
|
|
|
|
_skipSpeeches = false;
|
2005-01-17 20:17:06 +00:00
|
|
|
_conversingThread = NULL;
|
2004-12-22 21:04:50 +00:00
|
|
|
|
2005-01-17 07:21:08 +00:00
|
|
|
_firstObjectSet = false;
|
|
|
|
_secondObjectNeeded = false;
|
|
|
|
_pendingVerb = kVerbNone;
|
2005-01-08 20:30:07 +00:00
|
|
|
_currentVerb = kVerbNone;
|
|
|
|
_stickyVerb = kVerbWalkTo;
|
|
|
|
_leftButtonVerb = kVerbNone;
|
|
|
|
_rightButtonVerb = kVerbNone;
|
2005-01-15 20:12:49 +00:00
|
|
|
_pointerObject = 0;
|
2005-01-08 20:30:07 +00:00
|
|
|
|
2004-12-22 21:04:50 +00:00
|
|
|
_dataBuf[0].data = _dataBuf[1].data = (ScriptDataWord *)calloc(SCRIPT_DATABUF_LEN, sizeof(ScriptDataWord));;
|
|
|
|
_dataBuf[0].length = _dataBuf[1].length = SCRIPT_DATABUF_LEN;
|
|
|
|
|
|
|
|
for (i = 2; i < SCRIPT_DATABUF_NUM; i++) {
|
|
|
|
_dataBuf[i].length = 0;
|
|
|
|
_dataBuf[i].data = NULL;
|
|
|
|
}
|
2004-11-25 07:20:50 +00:00
|
|
|
|
2004-08-01 22:48:40 +00:00
|
|
|
|
|
|
|
debug(0, "Initializing scripting subsystem");
|
2004-05-01 14:05:10 +00:00
|
|
|
// Load script resource file context
|
2004-12-22 13:09:47 +00:00
|
|
|
_scriptContext = _vm->getFileContext(GAME_SCRIPTFILE, 0);
|
2004-11-15 03:03:48 +00:00
|
|
|
if (_scriptContext == NULL) {
|
2004-08-01 22:48:40 +00:00
|
|
|
error("Couldn't get script file context");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Load script LUT resource
|
2004-12-22 13:09:47 +00:00
|
|
|
s_lut_ctxt = _vm->getFileContext(GAME_RESOURCEFILE, 0);
|
2004-11-15 03:03:48 +00:00
|
|
|
if (s_lut_ctxt == NULL) {
|
2004-08-01 22:48:40 +00:00
|
|
|
error("Couldn't get resource file context");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2005-01-09 23:41:22 +00:00
|
|
|
debug(0, "Loading script LUT from resource %u.", _vm->getResourceDescription()->script_lut_rn);
|
|
|
|
result = RSC_LoadResource(s_lut_ctxt, _vm->getResourceDescription()->script_lut_rn, &rsc_ptr, &rsc_len);
|
2004-10-27 21:32:28 +00:00
|
|
|
if (result != SUCCESS) {
|
2004-08-01 22:48:40 +00:00
|
|
|
error("Error: Couldn't load script resource look-up table");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Create logical script LUT from resource
|
2004-10-27 21:32:28 +00:00
|
|
|
if (rsc_len % S_LUT_ENTRYLEN_ITECD == 0) {
|
|
|
|
_scriptLUTEntryLen = S_LUT_ENTRYLEN_ITECD;
|
|
|
|
} else if (rsc_len % S_LUT_ENTRYLEN_ITEDISK == 0) {
|
|
|
|
_scriptLUTEntryLen = S_LUT_ENTRYLEN_ITEDISK;
|
2004-04-12 21:40:49 +00:00
|
|
|
} else {
|
2004-12-08 03:15:36 +00:00
|
|
|
error("Error: Invalid script lookup table length (%d)", rsc_len);
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Calculate number of entries
|
2004-08-01 23:47:19 +00:00
|
|
|
_scriptLUTMax = rsc_len / _scriptLUTEntryLen;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-11-28 18:26:09 +00:00
|
|
|
debug(0, "LUT has %d entries.", _scriptLUTMax);
|
2005-01-03 21:17:32 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Allocate space for logical LUT
|
2004-10-27 21:32:28 +00:00
|
|
|
_scriptLUT = (SCRIPT_LUT_ENTRY *)malloc(_scriptLUTMax * sizeof(SCRIPT_LUT_ENTRY));
|
2004-08-01 23:47:19 +00:00
|
|
|
if (_scriptLUT == NULL) {
|
2004-08-01 22:48:40 +00:00
|
|
|
error("Error: Couldn't allocate memory for script resource look-up table");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Convert LUT resource to logical LUT
|
2004-12-15 00:24:12 +00:00
|
|
|
MemoryReadStreamEndian scriptS(rsc_ptr, rsc_len, IS_BIG_ENDIAN);
|
2004-08-01 23:47:19 +00:00
|
|
|
for (i = 0; i < _scriptLUTMax; i++) {
|
2004-08-22 18:28:42 +00:00
|
|
|
prevTell = scriptS.pos();
|
2004-12-15 00:24:12 +00:00
|
|
|
_scriptLUT[i].script_rn = scriptS.readUint16();
|
|
|
|
_scriptLUT[i].diag_list_rn = scriptS.readUint16();
|
|
|
|
_scriptLUT[i].voice_lut_rn = scriptS.readUint16();
|
2004-08-01 23:47:19 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Skip the unused portion of the structure
|
2004-08-22 18:28:42 +00:00
|
|
|
for (j = scriptS.pos(); j < prevTell + _scriptLUTEntryLen; j++) {
|
|
|
|
if (scriptS.readByte() != 0)
|
2004-10-09 07:39:46 +00:00
|
|
|
warning("Unused scriptLUT part isn't really unused for LUT %d (pos: %d)", i, j);
|
2004-08-22 18:28:42 +00:00
|
|
|
}
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
RSC_FreeResource(rsc_ptr);
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Any voice lookup table resources present?
|
2004-08-01 23:47:19 +00:00
|
|
|
for (i = 0; i < _scriptLUTMax; i++) {
|
|
|
|
if (_scriptLUT[i].voice_lut_rn) {
|
|
|
|
_voiceLUTPresent = true;
|
2004-04-12 21:40:49 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-08-12 23:57:45 +00:00
|
|
|
setupScriptFuncList();
|
|
|
|
|
2005-01-08 20:30:07 +00:00
|
|
|
resourceContext = _vm->getFileContext(GAME_RESOURCEFILE, 0);
|
|
|
|
|
|
|
|
result = RSC_LoadResource(resourceContext, RID_ITE_MAIN_STRINGS, &stringsPointer, &stringsLength); // fixme: IHNM
|
|
|
|
if ((result != SUCCESS) || (stringsLength == 0)) {
|
|
|
|
error("Error loading strings list resource");
|
|
|
|
}
|
|
|
|
|
|
|
|
_vm->loadStrings(_mainStrings, stringsPointer, stringsLength);
|
|
|
|
RSC_FreeResource(stringsPointer);
|
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
_initialized = true;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Shut down script module gracefully; free all allocated module resources
|
2004-08-01 22:48:40 +00:00
|
|
|
Script::~Script() {
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-01 22:48:40 +00:00
|
|
|
if (!_initialized) {
|
|
|
|
error("Script not initialized");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-05 13:05:45 +00:00
|
|
|
debug(0, "Shutting down scripting subsystem.");
|
2005-01-08 20:30:07 +00:00
|
|
|
|
|
|
|
_mainStrings.freeMem();
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Free script lookup table
|
2004-08-01 23:47:19 +00:00
|
|
|
free(_scriptLUT);
|
2004-12-15 00:24:12 +00:00
|
|
|
|
2004-12-22 21:04:50 +00:00
|
|
|
free(_dataBuf[0].data);
|
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
_initialized = false;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-12-22 21:04:50 +00:00
|
|
|
int Script::getWord(int bufNumber, int wordNumber, ScriptDataWord *data) {
|
|
|
|
if ((bufNumber < 0) || (bufNumber >= SCRIPT_DATABUF_NUM)) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((wordNumber < 0) || (wordNumber >= _dataBuf[bufNumber].length)) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data == NULL) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
*data = _dataBuf[bufNumber].data[wordNumber];
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Script::putWord(int bufNumber, int wordNumber, ScriptDataWord data) {
|
|
|
|
if ((bufNumber < 0) || (bufNumber >= SCRIPT_DATABUF_NUM)) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((wordNumber < 0) || (wordNumber >= _dataBuf[bufNumber].length)) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
_dataBuf[bufNumber].data[wordNumber] = data;
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Script::setBit(int bufNumber, ScriptDataWord bitNumber, int bitState) {
|
|
|
|
int wordNumber;
|
|
|
|
int bitPos;
|
|
|
|
|
|
|
|
ScriptDataWord bitPattern = 0x01;
|
|
|
|
|
|
|
|
if ((bufNumber < 0) || (bufNumber >= SCRIPT_DATABUF_NUM)) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bitNumber >= (unsigned long)_dataBuf[bufNumber].length * (sizeof(ScriptDataWord) * CHAR_BIT)) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
wordNumber = bitNumber / (sizeof(ScriptDataWord) * CHAR_BIT);
|
|
|
|
bitPos = bitNumber % (sizeof(ScriptDataWord) * CHAR_BIT);
|
|
|
|
|
|
|
|
bitPattern <<= ((sizeof(ScriptDataWord) * CHAR_BIT) - (bitPos + 1));
|
|
|
|
|
|
|
|
if (bitState) {
|
|
|
|
_dataBuf[bufNumber].data[wordNumber] |= bitPattern;
|
|
|
|
} else {
|
|
|
|
_dataBuf[bufNumber].data[wordNumber] &= ~bitPattern;
|
|
|
|
}
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Script::getBit(int bufNumber, ScriptDataWord bitNumber, int *bitState) {
|
|
|
|
int wordNumber;
|
|
|
|
int bitPos;
|
|
|
|
|
|
|
|
ScriptDataWord bitPattern = 0x01;
|
|
|
|
|
|
|
|
if ((bufNumber < 0) || (bufNumber >= SCRIPT_DATABUF_NUM)) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bitNumber >= (unsigned long)_dataBuf[bufNumber].length * (sizeof(ScriptDataWord) * CHAR_BIT)) {
|
|
|
|
return FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
wordNumber = bitNumber / (sizeof(ScriptDataWord) * CHAR_BIT);
|
|
|
|
bitPos = bitNumber % (sizeof(ScriptDataWord) * CHAR_BIT);
|
|
|
|
|
|
|
|
bitPattern <<= ((sizeof(ScriptDataWord) * CHAR_BIT) - (bitPos + 1));
|
|
|
|
|
|
|
|
|
|
|
|
*bitState = (_dataBuf[bufNumber].data[wordNumber] & bitPattern) ? 1 : 0;
|
|
|
|
|
|
|
|
return SUCCESS;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Loads a script; including script bytecode and dialogue list
|
2005-01-15 20:12:49 +00:00
|
|
|
int Script::loadScript(int scriptModuleNumber) {
|
2005-01-08 20:30:07 +00:00
|
|
|
ScriptData *script_data;
|
2004-04-30 23:02:23 +00:00
|
|
|
byte *bytecode_p;
|
2004-04-12 21:40:49 +00:00
|
|
|
size_t bytecode_len;
|
2004-04-30 23:02:23 +00:00
|
|
|
uint32 scriptl_rn;
|
2004-12-21 06:49:07 +00:00
|
|
|
byte *stringsPointer;
|
|
|
|
size_t stringsLength;
|
|
|
|
uint32 stringsResourceId;
|
2004-04-30 23:02:23 +00:00
|
|
|
byte *voicelut_p;
|
2004-04-12 21:40:49 +00:00
|
|
|
size_t voicelut_len;
|
2004-04-30 23:02:23 +00:00
|
|
|
uint32 voicelut_rn;
|
2004-04-12 21:40:49 +00:00
|
|
|
int result;
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Validate script number
|
2005-01-15 20:12:49 +00:00
|
|
|
if ((scriptModuleNumber < 0) || (scriptModuleNumber > _scriptLUTMax)) {
|
|
|
|
warning("Script::loadScript(): Invalid script module number");
|
2004-10-27 21:32:28 +00:00
|
|
|
return FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Release old script data if present
|
2004-08-01 22:48:40 +00:00
|
|
|
freeScript();
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Initialize script data structure
|
2005-01-15 20:12:49 +00:00
|
|
|
debug(0, "Loading script data for script module #%d", scriptModuleNumber);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2005-01-08 20:30:07 +00:00
|
|
|
script_data = (ScriptData *)malloc(sizeof(*script_data));
|
2004-04-12 21:40:49 +00:00
|
|
|
if (script_data == NULL) {
|
2004-12-21 06:49:07 +00:00
|
|
|
error("Memory allocation failed");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
script_data->loaded = 0;
|
|
|
|
|
2005-01-08 20:30:07 +00:00
|
|
|
// Initialize script pointers
|
2004-04-12 21:40:49 +00:00
|
|
|
script_data->bytecode = NULL;
|
|
|
|
script_data->voice = NULL;
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Load script bytecode
|
2005-01-15 20:12:49 +00:00
|
|
|
scriptl_rn = _scriptLUT[scriptModuleNumber].script_rn;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
result = RSC_LoadResource(_scriptContext, scriptl_rn, &bytecode_p, &bytecode_len);
|
2004-10-27 21:32:28 +00:00
|
|
|
if (result != SUCCESS) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Error loading script bytecode resource");
|
2004-04-12 21:40:49 +00:00
|
|
|
free(script_data);
|
2004-10-27 21:32:28 +00:00
|
|
|
return FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-08-01 22:48:40 +00:00
|
|
|
script_data->bytecode = loadBytecode(bytecode_p, bytecode_len);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
if (script_data->bytecode == NULL) {
|
2004-12-21 06:49:07 +00:00
|
|
|
error("Error interpreting script bytecode resource");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-12-21 06:49:07 +00:00
|
|
|
// Load script strings list
|
2005-01-15 20:12:49 +00:00
|
|
|
stringsResourceId = _scriptLUT[scriptModuleNumber].diag_list_rn;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-12-21 06:49:07 +00:00
|
|
|
// Load strings list resource
|
|
|
|
result = RSC_LoadResource(_scriptContext, stringsResourceId, &stringsPointer, &stringsLength);
|
|
|
|
if ((result != SUCCESS) || (stringsLength == 0)) {
|
|
|
|
error("Error loading strings list resource");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
2004-12-21 06:49:07 +00:00
|
|
|
|
|
|
|
// Convert strings list resource to logical strings list
|
2005-01-08 20:30:07 +00:00
|
|
|
_vm->loadStrings(script_data->strings, stringsPointer, stringsLength);
|
|
|
|
RSC_FreeResource(stringsPointer);
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Load voice resource lookup table
|
2004-08-01 23:47:19 +00:00
|
|
|
if (_voiceLUTPresent) {
|
2005-01-15 20:12:49 +00:00
|
|
|
voicelut_rn = _scriptLUT[scriptModuleNumber].voice_lut_rn;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Load voice LUT resource
|
2004-08-01 23:47:19 +00:00
|
|
|
result = RSC_LoadResource(_scriptContext, voicelut_rn, &voicelut_p, &voicelut_len);
|
2004-10-27 21:32:28 +00:00
|
|
|
if (result != SUCCESS) {
|
2004-12-21 06:49:07 +00:00
|
|
|
error("Error loading voice LUT resource");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Convert voice LUT resource to logical voice LUT
|
2004-08-01 22:48:40 +00:00
|
|
|
script_data->voice = loadVoiceLUT(voicelut_p, voicelut_len, script_data);
|
2004-04-12 21:40:49 +00:00
|
|
|
if (script_data->voice == NULL) {
|
2004-12-21 06:49:07 +00:00
|
|
|
error("Error interpreting voice LUT resource");
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Finish initialization
|
2004-04-12 21:40:49 +00:00
|
|
|
script_data->loaded = 1;
|
2004-08-01 23:47:19 +00:00
|
|
|
_currentScript = script_data;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
return SUCCESS;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Frees all resources associated with current script.
|
2004-08-01 22:48:40 +00:00
|
|
|
int Script::freeScript() {
|
2004-08-01 23:47:19 +00:00
|
|
|
if (_currentScript == NULL) {
|
2004-10-27 21:32:28 +00:00
|
|
|
return FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
if (!_currentScript->loaded) {
|
2004-10-27 21:32:28 +00:00
|
|
|
return FAILURE;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-05 13:05:45 +00:00
|
|
|
debug(0, "Releasing script data.");
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Finish initialization
|
2005-01-08 20:30:07 +00:00
|
|
|
_currentScript->strings.freeMem();
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
if (_currentScript->bytecode != NULL) {
|
|
|
|
free(_currentScript->bytecode->entrypoints);
|
|
|
|
RSC_FreeResource(_currentScript->bytecode->bytecode_p);
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
free(_currentScript->bytecode);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
if (_voiceLUTPresent) {
|
|
|
|
free(_currentScript->voice->voices);
|
|
|
|
free(_currentScript->voice);
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
free(_currentScript);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-01 23:47:19 +00:00
|
|
|
_currentScript = NULL;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
return SUCCESS;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Reads the entrypoint table from a script bytecode resource in memory.
|
|
|
|
// Returns NULL on failure.
|
2004-10-27 21:32:28 +00:00
|
|
|
SCRIPT_BYTECODE *Script::loadBytecode(byte *bytecode_p, size_t bytecode_len) {
|
|
|
|
PROC_TBLENTRY *bc_ep_tbl = NULL;
|
|
|
|
SCRIPT_BYTECODE *bc_new_data = NULL;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
unsigned long n_entrypoints; // Number of entrypoints
|
|
|
|
size_t ep_tbl_offset; // Offset of bytecode entrypoint table
|
2004-04-12 21:40:49 +00:00
|
|
|
unsigned long i;
|
|
|
|
|
2004-05-05 13:05:45 +00:00
|
|
|
debug(0, "Loading script bytecode...");
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-12-15 00:24:12 +00:00
|
|
|
MemoryReadStreamEndian scriptS(bytecode_p, bytecode_len, IS_BIG_ENDIAN);
|
2004-05-04 03:33:03 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// The first two uint32 values are the number of entrypoints, and the
|
|
|
|
// offset to the entrypoint table, respectively.
|
2004-12-15 00:24:12 +00:00
|
|
|
n_entrypoints = scriptS.readUint16();
|
|
|
|
scriptS.readUint16(); //skip
|
|
|
|
ep_tbl_offset = scriptS.readUint16();
|
|
|
|
scriptS.readUint16(); //skip
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Check that the entrypoint table offset is valid.
|
2004-10-27 21:32:28 +00:00
|
|
|
if ((bytecode_len - ep_tbl_offset) < (n_entrypoints * SCRIPT_TBLENTRY_LEN)) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Invalid table offset");
|
2004-04-12 21:40:49 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-10-27 21:32:28 +00:00
|
|
|
if (n_entrypoints > SCRIPT_MAX) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Script limit exceeded");
|
2004-04-12 21:40:49 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Allocate a new bytecode resource information structure and table of
|
|
|
|
// entrypoints
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2005-01-02 14:52:11 +00:00
|
|
|
bc_new_data = (SCRIPT_BYTECODE *)malloc(sizeof(*bc_new_data));
|
2004-04-12 21:40:49 +00:00
|
|
|
if (bc_new_data == NULL) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Memory allocation failure loading script bytecode");
|
2004-04-12 21:40:49 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-01-02 14:52:11 +00:00
|
|
|
bc_ep_tbl = (PROC_TBLENTRY *)malloc(n_entrypoints * sizeof(*bc_ep_tbl));
|
2004-04-12 21:40:49 +00:00
|
|
|
if (bc_ep_tbl == NULL) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Memory allocation failure creating script entrypoint table");
|
2004-04-12 21:40:49 +00:00
|
|
|
free(bc_new_data);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Read in the entrypoint table
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-22 18:28:42 +00:00
|
|
|
while (scriptS.pos() < ep_tbl_offset)
|
|
|
|
scriptS.readByte();
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
for (i = 0; i < n_entrypoints; i++) {
|
2004-05-01 14:05:10 +00:00
|
|
|
// First uint16 is the offset of the entrypoint name from the start
|
|
|
|
// of the bytecode resource, second uint16 is the offset of the
|
|
|
|
// bytecode itself for said entrypoint
|
2004-12-15 00:24:12 +00:00
|
|
|
bc_ep_tbl[i].name_offset = scriptS.readUint16();
|
|
|
|
bc_ep_tbl[i].offset = scriptS.readUint16();
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Perform a simple range check on offset values
|
|
|
|
if ((bc_ep_tbl[i].name_offset > bytecode_len) || (bc_ep_tbl[i].offset > bytecode_len)) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Invalid offset encountered in script entrypoint table");
|
2004-04-12 21:40:49 +00:00
|
|
|
free(bc_new_data);
|
|
|
|
free(bc_ep_tbl);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-04-30 23:02:23 +00:00
|
|
|
bc_new_data->bytecode_p = (byte *) bytecode_p;
|
2004-04-12 21:40:49 +00:00
|
|
|
bc_new_data->bytecode_len = bytecode_len;
|
|
|
|
|
|
|
|
bc_new_data->n_entrypoints = n_entrypoints;
|
|
|
|
bc_new_data->entrypoints = bc_ep_tbl;
|
|
|
|
bc_new_data->ep_tbl_offset = ep_tbl_offset;
|
|
|
|
|
|
|
|
return bc_new_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-05-01 14:05:10 +00:00
|
|
|
// Reads a logical voice LUT from a voice LUT resource in memory.
|
|
|
|
// Returns NULL on failure.
|
2005-01-08 20:30:07 +00:00
|
|
|
VOICE_LUT *Script::loadVoiceLUT(const byte *voicelut_p, size_t voicelut_len, ScriptData *script) {
|
2004-10-27 21:32:28 +00:00
|
|
|
VOICE_LUT *voice_lut;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-04-30 23:02:23 +00:00
|
|
|
uint16 i;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2005-01-02 14:52:11 +00:00
|
|
|
voice_lut = (VOICE_LUT *)malloc(sizeof(*voice_lut));
|
2004-04-12 21:40:49 +00:00
|
|
|
if (voice_lut == NULL) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-01-02 14:30:50 +00:00
|
|
|
voice_lut->n_voices = voicelut_len / 2;
|
2005-01-08 20:30:07 +00:00
|
|
|
if (voice_lut->n_voices != script->strings.stringsCount) {
|
2004-05-05 13:05:45 +00:00
|
|
|
warning("Error: Voice LUT entries do not match dialogue entries");
|
2004-04-12 21:40:49 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2005-01-02 14:52:11 +00:00
|
|
|
voice_lut->voices = (int *)malloc(voice_lut->n_voices * sizeof(*voice_lut->voices));
|
2004-04-12 21:40:49 +00:00
|
|
|
if (voice_lut->voices == NULL) {
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2004-12-15 00:24:12 +00:00
|
|
|
MemoryReadStreamEndian scriptS(voicelut_p, voicelut_len, IS_BIG_ENDIAN);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2005-01-02 14:30:50 +00:00
|
|
|
for (i = 0; i < voice_lut->n_voices; i++) {
|
2004-12-15 00:24:12 +00:00
|
|
|
voice_lut->voices[i] = scriptS.readUint16();
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return voice_lut;
|
|
|
|
}
|
|
|
|
|
2005-01-15 20:12:49 +00:00
|
|
|
void Script::scriptError(ScriptThread *thread, const char *format, ...) {
|
2005-01-03 21:17:32 +00:00
|
|
|
char buf[STRINGBUFLEN];
|
|
|
|
va_list argptr;
|
|
|
|
|
|
|
|
va_start(argptr, format);
|
|
|
|
vsprintf(buf, format, argptr);
|
|
|
|
va_end (argptr);
|
|
|
|
|
|
|
|
thread->flags |= kTFlagAborted;
|
2005-01-15 20:12:49 +00:00
|
|
|
debug(0, "Script::scriptError %X: %s", thread->instructionOffset, buf);
|
|
|
|
_vm->_console->DebugPrintf("Script::scriptError %X: %s", thread->instructionOffset, buf);
|
2005-01-03 21:17:32 +00:00
|
|
|
}
|
|
|
|
|
2004-12-03 19:15:44 +00:00
|
|
|
void Script::scriptInfo() {
|
2004-04-30 23:02:23 +00:00
|
|
|
uint32 n_entrypoints;
|
|
|
|
uint32 i;
|
2004-04-12 21:40:49 +00:00
|
|
|
char *name_ptr;
|
|
|
|
|
2004-08-11 00:27:43 +00:00
|
|
|
if (currentScript() == NULL) {
|
2004-04-12 21:40:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-08-11 00:27:43 +00:00
|
|
|
if (!currentScript()->loaded) {
|
2004-04-12 21:40:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-08-11 00:27:43 +00:00
|
|
|
n_entrypoints = currentScript()->bytecode->n_entrypoints;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-12-03 19:15:44 +00:00
|
|
|
_vm->_console->DebugPrintf("Current script contains %d entrypoints:\n", n_entrypoints);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
|
|
|
for (i = 0; i < n_entrypoints; i++) {
|
2004-08-11 00:27:43 +00:00
|
|
|
name_ptr = (char *)currentScript()->bytecode->bytecode_p +
|
|
|
|
currentScript()->bytecode->entrypoints[i].name_offset;
|
2004-12-03 19:15:44 +00:00
|
|
|
_vm->_console->DebugPrintf("%lu: %s\n", i, name_ptr);
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-12-03 19:15:44 +00:00
|
|
|
void Script::scriptExec(int argc, const char **argv) {
|
2004-04-30 23:02:23 +00:00
|
|
|
uint16 ep_num;
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-12-03 19:15:44 +00:00
|
|
|
ep_num = atoi(argv[1]);
|
2004-04-12 21:40:49 +00:00
|
|
|
|
2004-08-11 00:27:43 +00:00
|
|
|
if (_dbg_thread == NULL) {
|
2004-12-03 19:15:44 +00:00
|
|
|
_vm->_console->DebugPrintf("Creating debug thread...\n");
|
2005-01-15 20:12:49 +00:00
|
|
|
_dbg_thread = createThread();
|
2004-08-11 00:27:43 +00:00
|
|
|
if (_dbg_thread == NULL) {
|
2004-12-03 19:15:44 +00:00
|
|
|
_vm->_console->DebugPrintf("Thread creation failed.\n");
|
2004-04-12 21:40:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-08-11 00:27:43 +00:00
|
|
|
if (ep_num >= currentScript()->bytecode->n_entrypoints) {
|
2004-12-03 19:15:44 +00:00
|
|
|
_vm->_console->DebugPrintf("Invalid entrypoint.\n");
|
2004-04-12 21:40:49 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-01-15 20:12:49 +00:00
|
|
|
executeThread(_dbg_thread, ep_num);
|
2004-08-11 00:27:43 +00:00
|
|
|
}
|
|
|
|
|
2005-01-08 20:30:07 +00:00
|
|
|
// verb
|
|
|
|
void Script::showVerb() {
|
|
|
|
const char *verbName;
|
|
|
|
const char *object1Name;
|
|
|
|
const char *object2Name;
|
|
|
|
char statusString[STATUS_TEXT_LEN];
|
|
|
|
|
|
|
|
|
|
|
|
if (_leftButtonVerb == kVerbNone) {
|
|
|
|
_vm->_interface->setStatusText("");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
verbName = _mainStrings.getString(_leftButtonVerb - 1);
|
|
|
|
|
2005-01-18 15:01:21 +00:00
|
|
|
if (objectTypeId(_currentObject[0]) == kGameObjectNone) {
|
2005-01-08 20:30:07 +00:00
|
|
|
_vm->_interface->setStatusText(verbName);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
object1Name = _vm->getObjectName(_currentObject[0]);
|
|
|
|
|
|
|
|
if (!_secondObjectNeeded) {
|
|
|
|
snprintf(statusString, STATUS_TEXT_LEN, "%s %s", verbName, object1Name);
|
|
|
|
_vm->_interface->setStatusText(statusString);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2005-01-18 15:01:21 +00:00
|
|
|
if (objectTypeId(_currentObject[1]) != kGameObjectNone) {
|
2005-01-08 20:30:07 +00:00
|
|
|
object2Name = _vm->getObjectName(_currentObject[1]);
|
|
|
|
} else {
|
|
|
|
object2Name = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_leftButtonVerb == kVerbGive) {
|
|
|
|
snprintf(statusString, STATUS_TEXT_LEN, "Give %s to %s", object1Name, object2Name);
|
|
|
|
_vm->_interface->setStatusText(statusString);
|
|
|
|
} else {
|
|
|
|
if (_leftButtonVerb == kVerbUse) {
|
|
|
|
snprintf(statusString, STATUS_TEXT_LEN, "Use %s with %s", object1Name, object2Name);
|
|
|
|
_vm->_interface->setStatusText(statusString);
|
|
|
|
} else {
|
|
|
|
snprintf(statusString, STATUS_TEXT_LEN, "%s %s", verbName, object1Name);
|
|
|
|
_vm->_interface->setStatusText(statusString);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Script::setVerb(int verb) {
|
|
|
|
_pendingObject[0] = ID_NOTHING;
|
|
|
|
_currentObject[0] = ID_NOTHING;
|
|
|
|
_pendingObject[1] = ID_NOTHING;
|
|
|
|
_currentObject[1] = ID_NOTHING;
|
|
|
|
_firstObjectSet = false;
|
|
|
|
_secondObjectNeeded = false;
|
|
|
|
|
|
|
|
setLeftButtonVerb( verb );
|
|
|
|
showVerb();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Script::setLeftButtonVerb(int verb) {
|
2005-01-13 22:42:49 +00:00
|
|
|
int oldVerb = _currentVerb;
|
|
|
|
|
|
|
|
_currentVerb = _leftButtonVerb = verb;
|
|
|
|
|
|
|
|
if ((_currentVerb != oldVerb) && (_vm->_interface->getMode() == kPanelMain)){
|
|
|
|
if (oldVerb > kVerbNone)
|
|
|
|
_vm->_interface->drawVerb(oldVerb, 2);
|
|
|
|
|
|
|
|
if (_currentVerb > kVerbNone)
|
|
|
|
_vm->_interface->drawVerb(_currentVerb, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Script::setRightButtonVerb(int verb) {
|
2005-01-18 15:01:21 +00:00
|
|
|
int oldVerb = _rightButtonVerb;
|
2005-01-13 22:42:49 +00:00
|
|
|
|
|
|
|
_rightButtonVerb = verb;
|
|
|
|
|
|
|
|
if ((_rightButtonVerb != oldVerb) && (_vm->_interface->getMode() == kPanelMain)){
|
|
|
|
if (oldVerb > kVerbNone)
|
|
|
|
_vm->_interface->drawVerb(oldVerb, 2);
|
|
|
|
|
|
|
|
if (_rightButtonVerb > kVerbNone)
|
|
|
|
_vm->_interface->drawVerb(_rightButtonVerb, 2);
|
|
|
|
}
|
2005-01-08 20:30:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void Script::doVerb() {
|
2005-01-15 20:12:49 +00:00
|
|
|
int scriptEntrypointNumber = 0;
|
|
|
|
int scriptModuleNumber = 0;
|
|
|
|
int objectType;
|
|
|
|
EVENT event;
|
|
|
|
const char *excuseText;
|
|
|
|
int excuseSampleResourceId;
|
2005-01-18 15:01:21 +00:00
|
|
|
const HitZone *hitZone;
|
2005-01-15 20:12:49 +00:00
|
|
|
|
2005-01-18 15:01:21 +00:00
|
|
|
objectType = objectTypeId(_pendingObject[0]);
|
2005-01-15 20:12:49 +00:00
|
|
|
|
|
|
|
if (_pendingVerb == kVerbGive) {
|
|
|
|
scriptEntrypointNumber = _vm->getObjectScriptEntrypointNumber(_pendingObject[1]);
|
|
|
|
if (_vm->getObjectFlags(_pendingObject[1]) & (kFollower|kProtagonist|kExtended)) {
|
|
|
|
scriptModuleNumber = 0;
|
|
|
|
} else {
|
|
|
|
scriptModuleNumber = _vm->_scene->getScriptModuleNumber();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (_pendingVerb == kVerbUse) {
|
2005-01-18 15:01:21 +00:00
|
|
|
if ((objectTypeId(_pendingObject[1]) > kGameObjectNone) && (objectType < objectTypeId(_pendingObject[1]))) {
|
2005-01-15 20:12:49 +00:00
|
|
|
SWAP(_pendingObject[0], _pendingObject[1]);
|
2005-01-18 15:01:21 +00:00
|
|
|
objectType = objectTypeId(_pendingObject[0]);
|
2005-01-15 20:12:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (objectType == kGameObjectHitZone) {
|
|
|
|
scriptModuleNumber = _vm->_scene->getScriptModuleNumber();
|
2005-01-18 15:01:21 +00:00
|
|
|
hitZone = _vm->_scene->_objectMap->getHitZone(objectIdToIndex(_pendingObject[0]));
|
|
|
|
if ((hitZone->getFlags() & kHitZoneExit) == 0) {
|
|
|
|
scriptEntrypointNumber = hitZone->getScriptNumber();
|
|
|
|
}
|
|
|
|
|
2005-01-15 20:12:49 +00:00
|
|
|
} else {
|
|
|
|
if (objectType & (kGameObjectActor | kGameObjectObject)) {
|
|
|
|
scriptEntrypointNumber = _vm->getObjectScriptEntrypointNumber(_pendingObject[0]);
|
|
|
|
|
|
|
|
if ((objectType == kGameObjectActor) && !(_vm->getObjectFlags(_pendingObject[0]) & (kFollower|kProtagonist|kExtended))) {
|
|
|
|
scriptModuleNumber = _vm->_scene->getScriptModuleNumber();
|
|
|
|
} else {
|
|
|
|
scriptModuleNumber = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (scriptEntrypointNumber > 0) {
|
|
|
|
if (scriptModuleNumber != _vm->_scene->getScriptModuleNumber()) {
|
|
|
|
warning("scriptModuleNumber != _vm->_scene->getScriptModuleNumber()");
|
|
|
|
}
|
|
|
|
|
|
|
|
event.type = ONESHOT_EVENT;
|
|
|
|
event.code = SCRIPT_EVENT;
|
|
|
|
event.op = EVENT_EXEC_NONBLOCKING;
|
|
|
|
event.time = 0;
|
|
|
|
event.param = scriptEntrypointNumber;
|
|
|
|
event.param2 = _pendingVerb; // Action
|
|
|
|
event.param3 = _pendingObject[0]; // Object
|
|
|
|
event.param4 = _pendingObject[1]; // With Object
|
|
|
|
event.param5 = (objectType == kGameObjectActor) ? _pendingObject[0] : ID_PROTAG; // Actor
|
|
|
|
|
|
|
|
_vm->_events->queue(&event);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
_vm->getExcuseInfo(_pendingVerb, excuseText, excuseSampleResourceId);
|
|
|
|
if (excuseText)
|
|
|
|
_vm->_actor->actorSpeech(ID_PROTAG, &excuseText, 1, excuseSampleResourceId, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((_currentVerb == kVerbWalkTo) || (_currentVerb == kVerbLookAt)) {
|
|
|
|
_stickyVerb = _currentVerb;
|
|
|
|
}
|
|
|
|
|
|
|
|
_pendingVerb = kVerbNone;
|
|
|
|
_currentObject[0] = _currentObject[1] = ID_NOTHING;
|
|
|
|
setLeftButtonVerb(_stickyVerb);
|
|
|
|
|
|
|
|
setPointerVerb();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Script::setPointerVerb() {
|
|
|
|
Point mousePoint;
|
|
|
|
mousePoint = _vm->getMousePos();
|
|
|
|
if (_vm->_interface->isActive()) {
|
|
|
|
_pointerObject = ID_PROTAG;
|
|
|
|
whichObject(mousePoint);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-01-15 23:46:43 +00:00
|
|
|
void Script::hitObject(bool leftButton) {
|
|
|
|
int verb;
|
|
|
|
verb = leftButton ? _leftButtonVerb : _rightButtonVerb;
|
|
|
|
|
|
|
|
if (verb > kVerbNone) {
|
|
|
|
if (_firstObjectSet) {
|
|
|
|
if (_secondObjectNeeded) {
|
|
|
|
_pendingObject[0] = _currentObject[0];
|
|
|
|
_pendingObject[1] = _currentObject[1];
|
|
|
|
_pendingVerb = verb;
|
|
|
|
|
|
|
|
_leftButtonVerb = verb;
|
|
|
|
if (_pendingVerb > kVerbNone) {
|
|
|
|
// statusColor = BRIGHT_WHITE;
|
|
|
|
}
|
|
|
|
showVerb();
|
|
|
|
/*statusColor = GREEN_BA;*/
|
|
|
|
|
|
|
|
_secondObjectNeeded = false;
|
|
|
|
_firstObjectSet = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (verb == kVerbGive) {
|
|
|
|
_secondObjectNeeded = true;
|
|
|
|
} else {
|
|
|
|
if (verb == kVerbUse) {
|
|
|
|
|
|
|
|
if (_currentObjectFlags[0] & kObjUseWith) {
|
|
|
|
_secondObjectNeeded = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!_secondObjectNeeded) {
|
|
|
|
_pendingObject[0] = _currentObject[0];
|
|
|
|
_pendingObject[1] = ID_NOTHING;
|
|
|
|
_pendingVerb = verb;
|
|
|
|
|
|
|
|
_secondObjectNeeded = false;
|
|
|
|
_firstObjectSet = false;
|
|
|
|
} else {
|
|
|
|
_firstObjectSet = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_leftButtonVerb = verb;
|
|
|
|
if (_pendingVerb > kVerbNone) {
|
|
|
|
// statusColor = BRIGHT_WHITE;
|
|
|
|
}
|
|
|
|
showVerb();
|
|
|
|
//statusColor = GREEN_BA;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Script::playfieldClick(const Point& mousePoint, bool leftButton) {
|
|
|
|
Location pickLocation;
|
|
|
|
const HitZone *hitZone;
|
2005-01-18 15:01:21 +00:00
|
|
|
Point specialPoint;
|
2005-01-15 23:46:43 +00:00
|
|
|
|
|
|
|
_vm->_actor->abortSpeech();
|
|
|
|
|
|
|
|
if ((_vm->_actor->_protagonist->currentAction != kActionWait) &&
|
|
|
|
(_vm->_actor->_protagonist->currentAction != kActionFreeze) &&
|
|
|
|
(_vm->_actor->_protagonist->currentAction != kActionWalkToLink) &&
|
|
|
|
(_vm->_actor->_protagonist->currentAction != kActionWalkToPoint)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (_pendingVerb > kVerbNone) {
|
|
|
|
setLeftButtonVerb(kVerbWalkTo);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_pointerObject != ID_NOTHING) {
|
|
|
|
hitObject( leftButton );
|
|
|
|
} else {
|
|
|
|
_pendingObject[0] = ID_NOTHING;
|
|
|
|
_pendingObject[1] = ID_NOTHING;
|
|
|
|
_pendingVerb = kVerbWalkTo;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// tiled stuff
|
|
|
|
if (_vm->_scene->getFlags() & kSceneFlagISO) {
|
|
|
|
//todo: it
|
|
|
|
} else {
|
|
|
|
pickLocation.fromScreenPoint(mousePoint);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
hitZone = NULL;
|
|
|
|
|
2005-01-18 15:01:21 +00:00
|
|
|
if (objectTypeId(_pendingObject[0]) == kGameObjectHitZone) {
|
|
|
|
hitZone = _vm->_scene->_objectMap->getHitZone(objectIdToIndex(_pendingObject[0]));
|
2005-01-15 23:46:43 +00:00
|
|
|
} else {
|
2005-01-18 15:01:21 +00:00
|
|
|
if ((_pendingVerb == kVerbUse) && (objectTypeId(_pendingObject[1]) == kGameObjectHitZone)) {
|
|
|
|
hitZone = _vm->_scene->_objectMap->getHitZone(objectIdToIndex(_pendingObject[1]));
|
2005-01-15 23:46:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hitZone != NULL) {
|
|
|
|
if (hitZone->getFlags() & kHitZoneNoWalk) {
|
|
|
|
_vm->_actor->actorFaceTowardsPoint(ID_PROTAG, pickLocation);
|
|
|
|
doVerb();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hitZone->getFlags() & kHitZoneProject) {
|
2005-01-18 15:01:21 +00:00
|
|
|
if (!hitZone->getSpecialPoint(specialPoint)) {
|
|
|
|
error("Script::playfieldClick SpecialPoint not found");
|
|
|
|
}
|
|
|
|
|
|
|
|
// tiled stuff
|
|
|
|
if (_vm->_scene->getFlags() & kSceneFlagISO) {
|
|
|
|
//todo: it
|
|
|
|
} else {
|
|
|
|
pickLocation.fromScreenPoint(specialPoint);
|
|
|
|
}
|
2005-01-15 23:46:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (_pendingVerb) {
|
|
|
|
case kVerbWalkTo:
|
|
|
|
case kVerbPickUp:
|
|
|
|
case kVerbOpen:
|
|
|
|
case kVerbClose:
|
|
|
|
case kVerbUse:
|
|
|
|
_vm->_actor->actorWalkTo(ID_PROTAG, pickLocation);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kVerbLookAt:
|
2005-01-18 15:01:21 +00:00
|
|
|
if (objectTypeId(_pendingObject[0]) != kGameObjectActor ) {
|
2005-01-15 23:46:43 +00:00
|
|
|
_vm->_actor->actorWalkTo(ID_PROTAG, pickLocation);
|
|
|
|
} else {
|
|
|
|
doVerb();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kVerbTalkTo:
|
|
|
|
case kVerbGive:
|
|
|
|
doVerb();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Script::whichObject(const Point& mousePoint) {
|
2005-01-15 20:12:49 +00:00
|
|
|
uint16 objectId;
|
|
|
|
int16 objectFlags;
|
|
|
|
int newRightButtonVerb;
|
|
|
|
uint16 newObjectId;
|
|
|
|
ActorData *actor;
|
|
|
|
Location pickLocation;
|
2005-01-18 15:01:21 +00:00
|
|
|
int hitZoneIndex;
|
|
|
|
const HitZone * hitZone;
|
2005-01-15 20:12:49 +00:00
|
|
|
|
|
|
|
objectId = ID_NOTHING;
|
|
|
|
objectFlags = 0;
|
|
|
|
_leftButtonVerb = _currentVerb;
|
|
|
|
newRightButtonVerb = kVerbNone;
|
|
|
|
|
|
|
|
if (_vm->_actor->_protagonist->currentAction == kActionWalkDir) {
|
|
|
|
} else {
|
2005-01-15 23:46:43 +00:00
|
|
|
newObjectId = _vm->_actor->testHit(mousePoint);
|
2005-01-15 20:12:49 +00:00
|
|
|
|
|
|
|
if (newObjectId != ID_NOTHING) {
|
2005-01-18 15:01:21 +00:00
|
|
|
if (objectTypeId(newObjectId) == kGameObjectObject) {
|
2005-01-15 20:12:49 +00:00
|
|
|
objectId = newObjectId;
|
|
|
|
objectFlags = 0;
|
|
|
|
newRightButtonVerb = kVerbLookAt;
|
|
|
|
|
|
|
|
if ((_currentVerb == kVerbTalkTo) || ((_currentVerb == kVerbGive) && _firstObjectSet)) {
|
|
|
|
objectId = ID_NOTHING;
|
|
|
|
newObjectId = ID_NOTHING;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
actor = _vm->_actor->getActor(newObjectId);
|
|
|
|
objectId = newObjectId;
|
|
|
|
objectFlags = kObjUseWith;
|
|
|
|
newRightButtonVerb = kVerbTalkTo;
|
|
|
|
|
|
|
|
if ((_currentVerb == kVerbPickUp) ||
|
|
|
|
(_currentVerb == kVerbOpen) ||
|
|
|
|
(_currentVerb == kVerbClose) ||
|
|
|
|
((_currentVerb == kVerbGive) && !_firstObjectSet) ||
|
|
|
|
((_currentVerb == kVerbUse) && !(actor->flags & kFollower))) {
|
|
|
|
objectId = ID_NOTHING;
|
|
|
|
newObjectId = ID_NOTHING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-01-18 15:01:21 +00:00
|
|
|
if (newObjectId == ID_NOTHING) {
|
2005-01-15 20:12:49 +00:00
|
|
|
if (_vm->_scene->getFlags() & kSceneFlagISO) {
|
|
|
|
//todo: it
|
|
|
|
} else {
|
2005-01-15 23:46:43 +00:00
|
|
|
pickLocation.x = mousePoint.x;
|
|
|
|
pickLocation.y = mousePoint.y;
|
2005-01-15 20:12:49 +00:00
|
|
|
pickLocation.z = 0;
|
|
|
|
}
|
|
|
|
|
2005-01-18 15:01:21 +00:00
|
|
|
hitZoneIndex = _vm->_scene->_objectMap->hitTest(mousePoint);
|
2005-01-15 20:12:49 +00:00
|
|
|
|
2005-01-18 15:01:21 +00:00
|
|
|
if ((hitZoneIndex != -1)) {
|
|
|
|
hitZone = _vm->_scene->_objectMap->getHitZone(hitZoneIndex);
|
|
|
|
objectId = hitZone->getHitZoneId();
|
2005-01-15 20:12:49 +00:00
|
|
|
objectFlags = 0;
|
|
|
|
newRightButtonVerb = hitZone->getRightButtonVerb() & 0x7f;
|
|
|
|
|
|
|
|
if (newRightButtonVerb == kVerbWalkOnly) {
|
|
|
|
if (_firstObjectSet) {
|
|
|
|
objectId = ID_NOTHING;
|
|
|
|
} else {
|
|
|
|
newRightButtonVerb = _leftButtonVerb = kVerbWalkTo;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (_firstObjectSet) {
|
|
|
|
objectId = ID_NOTHING;
|
|
|
|
} else {
|
|
|
|
newRightButtonVerb = _leftButtonVerb = kVerbLookAt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newRightButtonVerb >= kVerbOptions) {
|
|
|
|
newRightButtonVerb = kVerbNone;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((_currentVerb == kVerbTalkTo) || ((_currentVerb == kVerbGive) && !_firstObjectSet)) {
|
|
|
|
objectId = ID_NOTHING;
|
|
|
|
newObjectId = ID_NOTHING;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((_leftButtonVerb == kVerbUse) && (hitZone->getRightButtonVerb() & 0x80)) {
|
|
|
|
objectFlags = kObjUseWith;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (objectId != _pointerObject) {
|
|
|
|
_pointerObject = objectId;
|
|
|
|
_currentObject[_firstObjectSet ? 1 : 0] = objectId;
|
|
|
|
_currentObjectFlags[_firstObjectSet ? 1 : 0] = objectFlags;
|
|
|
|
if (_pendingVerb == kVerbNone) {
|
|
|
|
showVerb();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (newRightButtonVerb != _rightButtonVerb) {
|
|
|
|
setRightButtonVerb(newRightButtonVerb);
|
|
|
|
}
|
2005-01-08 20:30:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// console wrappers
|
2004-12-03 19:15:44 +00:00
|
|
|
void Script::CF_script_togglestep() {
|
|
|
|
_dbg_singlestep = !_dbg_singlestep;
|
2004-04-12 21:40:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
} // End of namespace Saga
|