mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-24 11:36:22 +00:00
934fe91620
herald a change of scene, so it should be ok. And it keeps the crackling fire at the end from playing over the end credits and the End of Game dialog afterwards. (If we change the sound engine to do looping manually, instead of letting the mixer handle it, the looping will not be seamless and we'll still get the crackling fire over the dialog after the credits.) This change is probably safe for 0.8.1, assuming we make one. svn-id: r19387
1803 lines
58 KiB
C++
1803 lines
58 KiB
C++
/* ScummVM - Scumm Interpreter
|
|
* Copyright (C) 2003-2005 The ScummVM project
|
|
*
|
|
* 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.
|
|
*
|
|
* $Header$
|
|
*
|
|
*/
|
|
|
|
#include "common/stdafx.h"
|
|
#include "common/util.h"
|
|
|
|
#include "sword1/logic.h"
|
|
#include "sword1/text.h"
|
|
#include "sword1/sound.h"
|
|
#include "sword1/eventman.h"
|
|
#include "sword1/menu.h"
|
|
#include "sword1/router.h"
|
|
#include "sword1/screen.h"
|
|
#include "sword1/mouse.h"
|
|
#include "sword1/sword1.h"
|
|
#include "sword1/music.h"
|
|
#include "sword1/swordres.h"
|
|
#include "sword1/animation.h"
|
|
#include "sword1/credits.h"
|
|
|
|
#include "sword1/debug.h"
|
|
|
|
#include "gui/message.h"
|
|
|
|
namespace Sword1 {
|
|
|
|
#define MAX_STACK_SIZE 10
|
|
#define SCRIPT_VERSION 13
|
|
#define LAST_FRAME 999
|
|
|
|
uint32 Logic::_scriptVars[NUM_SCRIPT_VARS];
|
|
|
|
Logic::Logic(ObjectMan *pObjMan, ResMan *resMan, Screen *pScreen, Mouse *pMouse, Sound *pSound, Music *pMusic, Menu *pMenu, OSystem *system, Audio::Mixer *mixer) {
|
|
_objMan = pObjMan;
|
|
_resMan = resMan;
|
|
_screen = pScreen;
|
|
_mouse = pMouse;
|
|
_music = pMusic;
|
|
_sound = pSound;
|
|
_menu = pMenu;
|
|
_textMan = NULL;
|
|
_screen->useTextManager(_textMan);
|
|
_router = new Router(_objMan, _resMan);
|
|
_eventMan = NULL;
|
|
_system = system;
|
|
_mixer = mixer;
|
|
}
|
|
|
|
Logic::~Logic(void) {
|
|
delete _textMan;
|
|
delete _router;
|
|
delete _eventMan;
|
|
}
|
|
|
|
void Logic::initialize(void) {
|
|
memset(_scriptVars, 0, NUM_SCRIPT_VARS * sizeof(uint32));
|
|
for (uint8 cnt = 0; cnt < NON_ZERO_SCRIPT_VARS; cnt++)
|
|
_scriptVars[_scriptVarInit[cnt][0]] = _scriptVarInit[cnt][1];
|
|
if (SwordEngine::_systemVars.isDemo)
|
|
_scriptVars[PLAYINGDEMO] = 1;
|
|
|
|
delete _eventMan;
|
|
_eventMan = new EventManager();
|
|
|
|
delete _textMan;
|
|
_textMan = new Text(_objMan, _resMan,
|
|
(SwordEngine::_systemVars.language == BS1_CZECH) ? true : false);
|
|
_screen->useTextManager(_textMan);
|
|
_textRunning = _speechRunning = false;
|
|
_speechFinished = true;
|
|
_router->resetExtraData();
|
|
}
|
|
|
|
void Logic::newScreen(uint32 screen) {
|
|
Object *compact = (Object*)_objMan->fetchObject(PLAYER);
|
|
|
|
// work around script bug #911508
|
|
if (((screen == 25) || (_scriptVars[SCREEN] == 25)) && (_scriptVars[SAND_FLAG] == 4)) {
|
|
Object *cpt = _objMan->fetchObject(SAND_25);
|
|
Object *george = _objMan->fetchObject(PLAYER);
|
|
if (george->o_place == HOLDING_REPLICA_25) // is george holding the replica in his hands?
|
|
fnFullSetFrame(cpt, SAND_25, IMPFLRCDT, IMPFLR, 0, 0, 0, 0); // empty impression in floor
|
|
else
|
|
fnFullSetFrame(cpt, SAND_25, IMPPLSCDT, IMPPLS, 0, 0, 0, 0); // impression filled with plaster
|
|
}
|
|
|
|
if (SwordEngine::_systemVars.justRestoredGame) { // if we've just restored a game - we want George to be exactly as saved
|
|
fnAddHuman(NULL, 0, 0, 0, 0, 0, 0, 0);
|
|
if (_scriptVars[GEORGE_WALKING]) { // except that if George was walking when we saveed the game
|
|
fnStandAt(compact, PLAYER, _scriptVars[CHANGE_X], _scriptVars[CHANGE_Y], _scriptVars[CHANGE_DIR], _scriptVars[CHANGE_STANCE], 0,0);
|
|
fnIdle(compact,PLAYER,0,0,0,0,0,0);
|
|
_scriptVars[GEORGE_WALKING] = 0;
|
|
}
|
|
SwordEngine::_systemVars.justRestoredGame = 0;
|
|
_music->startMusic(_scriptVars[CURRENT_MUSIC], 1);
|
|
} else { // if we haven't just restored a game, set George to stand, etc
|
|
compact->o_screen = _scriptVars[NEW_SCREEN]; //move the mega/player at this point between screens
|
|
fnStandAt(compact, PLAYER, _scriptVars[CHANGE_X], _scriptVars[CHANGE_Y], _scriptVars[CHANGE_DIR], _scriptVars[CHANGE_STANCE], 0,0);
|
|
fnChangeFloor(compact, PLAYER, _scriptVars[CHANGE_PLACE], 0, 0, 0, 0, 0);
|
|
}
|
|
}
|
|
|
|
void Logic::engine(void) {
|
|
debug(8, "\n\nNext logic cycle");
|
|
_eventMan->serviceGlobalEventList();
|
|
|
|
for (uint16 sectCnt = 0; sectCnt < TOTAL_SECTIONS; sectCnt++) {
|
|
if (_objMan->sectionAlive(sectCnt)) {
|
|
uint32 numCpts = _objMan->fetchNoObjects(sectCnt);
|
|
for (uint32 cptCnt = 0; cptCnt < numCpts; cptCnt++) {
|
|
uint32 currentId = sectCnt * ITM_PER_SEC + cptCnt;
|
|
Object *compact = _objMan->fetchObject(currentId);
|
|
|
|
if (compact->o_status & STAT_LOGIC) { // does the object want to be processed?
|
|
if (compact->o_status & STAT_EVENTS) {
|
|
//subscribed to the global-event-switcher? and in logic mode
|
|
switch (compact->o_logic) {
|
|
case LOGIC_pause_for_event:
|
|
case LOGIC_idle:
|
|
case LOGIC_AR_animate:
|
|
_eventMan->checkForEvent(compact);
|
|
break;
|
|
}
|
|
}
|
|
debug(7, "Logic::engine: handling compact %d (%X)", currentId, currentId);
|
|
processLogic(compact, currentId);
|
|
compact->o_sync = 0; // syncs are only available for 1 cycle.
|
|
}
|
|
|
|
if ((uint32)compact->o_screen == _scriptVars[SCREEN]) {
|
|
if (compact->o_status & STAT_FORE)
|
|
_screen->addToGraphicList(0, currentId);
|
|
if (compact->o_status & STAT_SORT)
|
|
_screen->addToGraphicList(1, currentId);
|
|
if (compact->o_status & STAT_BACK)
|
|
_screen->addToGraphicList(2, currentId);
|
|
|
|
if (compact->o_status & STAT_MOUSE)
|
|
_mouse->addToList(currentId, compact);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//_collision->checkCollisions();
|
|
|
|
}
|
|
|
|
void Logic::processLogic(Object *compact, uint32 id) {
|
|
int logicRet;
|
|
do {
|
|
switch (compact->o_logic) {
|
|
case LOGIC_idle:
|
|
logicRet = 0;
|
|
break;
|
|
case LOGIC_pause:
|
|
case LOGIC_pause_for_event:
|
|
if (compact->o_pause) {
|
|
compact->o_pause--;
|
|
logicRet = 0;
|
|
} else {
|
|
compact->o_logic = LOGIC_script;
|
|
logicRet = 1;
|
|
}
|
|
break;
|
|
case LOGIC_quit:
|
|
compact->o_logic = LOGIC_script;
|
|
logicRet = 0;
|
|
break;
|
|
case LOGIC_wait_for_sync:
|
|
if (compact->o_sync) {
|
|
logicRet = 1;
|
|
compact->o_logic = LOGIC_script;
|
|
} else
|
|
logicRet = 0;
|
|
break;
|
|
case LOGIC_choose:
|
|
_scriptVars[CUR_ID] = id;
|
|
logicRet = _menu->logicChooser(compact);
|
|
break;
|
|
case LOGIC_wait_for_talk:
|
|
logicRet = logicWaitTalk(compact);
|
|
break;
|
|
case LOGIC_start_talk:
|
|
logicRet = logicStartTalk(compact);
|
|
break;
|
|
case LOGIC_script:
|
|
_scriptVars[CUR_ID] = id;
|
|
logicRet = scriptManager(compact, id);
|
|
break;
|
|
case LOGIC_new_script:
|
|
compact->o_tree.o_script_pc[compact->o_tree.o_script_level] = _newScript;
|
|
compact->o_tree.o_script_id[compact->o_tree.o_script_level] = _newScript;
|
|
compact->o_logic = LOGIC_script;
|
|
logicRet = 1;
|
|
break;
|
|
case LOGIC_AR_animate:
|
|
logicRet = logicArAnimate(compact, id);
|
|
break;
|
|
case LOGIC_restart:
|
|
compact->o_tree.o_script_pc[compact->o_tree.o_script_level] = compact->o_tree.o_script_id[compact->o_tree.o_script_level];
|
|
compact->o_logic = LOGIC_script;
|
|
logicRet=1;
|
|
break;
|
|
case LOGIC_bookmark:
|
|
memcpy(&(compact->o_tree.o_script_level), &(compact->o_bookmark.o_script_level), sizeof(ScriptTree));
|
|
if (id == GMASTER_79) {
|
|
// workaround for ending script.
|
|
// GMASTER_79 is not prepared for mega_interact receiving INS_quit
|
|
fnSuicide(compact, id, 0, 0, 0, 0, 0, 0);
|
|
logicRet = 0;
|
|
} else {
|
|
compact->o_logic = LOGIC_script;
|
|
logicRet = 1;
|
|
}
|
|
break;
|
|
case LOGIC_speech:
|
|
logicRet = speechDriver(compact);
|
|
break;
|
|
case LOGIC_full_anim:
|
|
logicRet = fullAnimDriver(compact);
|
|
break;
|
|
case LOGIC_anim:
|
|
logicRet = animDriver(compact);
|
|
break;
|
|
|
|
default:
|
|
error("Fatal error: compact %d's logic == %X!", id, compact->o_logic);
|
|
break;
|
|
}
|
|
} while (logicRet);
|
|
}
|
|
|
|
int Logic::logicWaitTalk(Object *compact) {
|
|
Object *target = _objMan->fetchObject(compact->o_down_flag);
|
|
|
|
if (target->o_status & STAT_TALK_WAIT) {
|
|
compact->o_logic = LOGIC_script;
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int Logic::logicStartTalk(Object *compact) {
|
|
Object *target = _objMan->fetchObject(compact->o_down_flag); //holds id of person we're waiting for
|
|
if (target->o_status & STAT_TALK_WAIT) { //response?
|
|
compact->o_logic = LOGIC_script; //back to script again
|
|
return SCRIPT_CONT;
|
|
}
|
|
if (_eventMan->eventValid(compact->o_down_flag))
|
|
return SCRIPT_STOP; //event still valid - keep waiting
|
|
//the event has gone - so back to script with error code
|
|
compact->o_down_flag = 0;
|
|
compact->o_logic = LOGIC_script;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::logicArAnimate(Object *compact, uint32 id) {
|
|
WalkData *route;
|
|
int32 walkPc;
|
|
if ((_scriptVars[GEORGE_WALKING] == 0) && (id == PLAYER))
|
|
_scriptVars[GEORGE_WALKING] = 1;
|
|
|
|
compact->o_resource = compact->o_walk_resource;
|
|
compact->o_status |= STAT_SHRINK;
|
|
route = compact->o_route;
|
|
|
|
walkPc =compact->o_walk_pc;
|
|
compact->o_frame =route[walkPc].frame;
|
|
compact->o_dir =route[walkPc].dir;
|
|
compact->o_xcoord =route[walkPc].x;
|
|
compact->o_ycoord =route[walkPc].y;
|
|
compact->o_anim_x =compact->o_xcoord;
|
|
compact->o_anim_y =compact->o_ycoord;
|
|
|
|
if (((_scriptVars[GEORGE_WALKING] == 2) && (walkPc > 5) && (id == PLAYER) &&
|
|
(route[walkPc - 1].step == 5) && (route[walkPc].step == 0)) ||
|
|
((_scriptVars[GEORGE_WALKING] == 3) && (id == PLAYER))) {
|
|
|
|
compact->o_frame = 96 + compact->o_dir; //reset
|
|
if ((compact->o_dir != 2) && (compact->o_dir != 6)) { // on verticals and diagonals stand where george is
|
|
compact->o_xcoord = route[walkPc - 1].x;
|
|
compact->o_ycoord = route[walkPc - 1].y;
|
|
compact->o_anim_x = compact->o_xcoord;
|
|
compact->o_anim_y = compact->o_ycoord;
|
|
}
|
|
compact->o_logic = LOGIC_script;
|
|
compact->o_down_flag = 0; //0 means error
|
|
_scriptVars[GEORGE_WALKING] = 0;
|
|
route[compact->o_walk_pc+1].frame = 512; //end of sequence
|
|
if (_scriptVars[MEGA_ON_GRID] == 2)
|
|
_scriptVars[MEGA_ON_GRID] = 0;
|
|
}
|
|
compact->o_walk_pc++;
|
|
|
|
if (route[compact->o_walk_pc].frame == 512) //end of sequence
|
|
{
|
|
compact->o_logic = LOGIC_script;
|
|
if (((_scriptVars[GEORGE_WALKING] == 2) || (_scriptVars[GEORGE_WALKING] == 1)) &&
|
|
(id == PLAYER)) {
|
|
_scriptVars[GEORGE_WALKING] = 0;
|
|
if (_scriptVars[MEGA_ON_GRID] == 2)
|
|
_scriptVars[MEGA_ON_GRID] = 0;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int Logic::speechDriver(Object *compact) {
|
|
if ((!_speechClickDelay) && (_mouse->testEvent() & BS1L_BUTTON_DOWN))
|
|
_speechFinished = true;
|
|
if (_speechClickDelay)
|
|
_speechClickDelay--;
|
|
|
|
if (_speechRunning) {
|
|
if (_sound->speechFinished())
|
|
_speechFinished = true;
|
|
} else {
|
|
if (!compact->o_speech_time)
|
|
_speechFinished = true;
|
|
else
|
|
compact->o_speech_time--;
|
|
}
|
|
if (_speechFinished) {
|
|
if (_speechRunning)
|
|
_sound->stopSpeech();
|
|
compact->o_logic = LOGIC_script;
|
|
if (_textRunning) {
|
|
_textMan->releaseText(compact->o_text_id);
|
|
_objMan->fetchObject(compact->o_text_id)->o_status = 0; // kill compact linking text sprite
|
|
}
|
|
_speechRunning = _textRunning = false;
|
|
_speechFinished = true;
|
|
}
|
|
if (compact->o_anim_resource) {
|
|
uint8 *animData = ((uint8*)_resMan->openFetchRes(compact->o_anim_resource)) + sizeof(Header);
|
|
int32 numFrames = READ_LE_UINT32(animData);
|
|
animData += 4;
|
|
compact->o_anim_pc++; // go to next frame of anim
|
|
|
|
if (_speechFinished || (compact->o_anim_pc >= numFrames) ||
|
|
(_speechRunning && (_sound->amISpeaking() == 0)))
|
|
compact->o_anim_pc = 0; //set to frame 0, closed mouth
|
|
|
|
AnimUnit *animPtr = (AnimUnit*)(animData + sizeof(AnimUnit) * compact->o_anim_pc);
|
|
if (!(compact->o_status & STAT_SHRINK)) {
|
|
compact->o_anim_x = FROM_LE_32(animPtr->animX);
|
|
compact->o_anim_y = FROM_LE_32(animPtr->animY);
|
|
}
|
|
compact->o_frame = FROM_LE_32(animPtr->animFrame);
|
|
_resMan->resClose(compact->o_anim_resource);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int Logic::fullAnimDriver(Object *compact) {
|
|
if (compact->o_sync) { // return to script immediately if we've received a sync
|
|
compact->o_logic = LOGIC_script;
|
|
return 1;
|
|
}
|
|
uint8 *data = ((uint8*)_resMan->openFetchRes(compact->o_anim_resource)) + sizeof(Header);
|
|
uint32 numFrames = READ_LE_UINT32(data);
|
|
data += 4;
|
|
AnimUnit *animPtr = (AnimUnit*)(data + compact->o_anim_pc * sizeof(AnimUnit));
|
|
|
|
compact->o_anim_x = compact->o_xcoord = FROM_LE_32(animPtr->animX);
|
|
compact->o_anim_y = compact->o_ycoord = FROM_LE_32(animPtr->animY);
|
|
compact->o_frame = FROM_LE_32(animPtr->animFrame);
|
|
|
|
compact->o_anim_pc++;
|
|
if (compact->o_anim_pc == (int)numFrames)
|
|
compact->o_logic = LOGIC_script;
|
|
|
|
_resMan->resClose(compact->o_anim_resource);
|
|
return 0;
|
|
}
|
|
|
|
int Logic::animDriver(Object *compact) {
|
|
if (compact->o_sync) {
|
|
compact->o_logic = LOGIC_script;
|
|
return 1;
|
|
}
|
|
uint8 *data = ((uint8*)_resMan->openFetchRes(compact->o_anim_resource)) + sizeof(Header);
|
|
uint32 numFrames = READ_LE_UINT32(data);
|
|
AnimUnit *animPtr = (AnimUnit*)(data + 4 + compact->o_anim_pc * sizeof(AnimUnit));
|
|
|
|
if (!(compact->o_status & STAT_SHRINK)) {
|
|
compact->o_anim_x = FROM_LE_32(animPtr->animX);
|
|
compact->o_anim_y = FROM_LE_32(animPtr->animY);
|
|
}
|
|
|
|
compact->o_frame = FROM_LE_32(animPtr->animFrame);
|
|
compact->o_anim_pc++;
|
|
if (compact->o_anim_pc == (int)numFrames)
|
|
compact->o_logic = LOGIC_script;
|
|
|
|
_resMan->resClose(compact->o_anim_resource);
|
|
return 0;
|
|
}
|
|
|
|
void Logic::updateScreenParams(void) {
|
|
Object *compact = (Object*)_objMan->fetchObject(PLAYER);
|
|
_screen->setScrolling((int16)(compact->o_xcoord - _scriptVars[FEET_X]),
|
|
(int16)(compact->o_ycoord - _scriptVars[FEET_Y]));
|
|
}
|
|
|
|
int Logic::scriptManager(Object *compact, uint32 id) {
|
|
int ret;
|
|
do {
|
|
uint32 level = compact->o_tree.o_script_level;
|
|
uint32 script = compact->o_tree.o_script_id[level];
|
|
Debug::interpretScript(id, level, script, compact->o_tree.o_script_pc[level] & ITM_ID);
|
|
ret = interpretScript(compact, id, _resMan->lockScript(script), script, compact->o_tree.o_script_pc[level] & ITM_ID);
|
|
_resMan->unlockScript(script);
|
|
if (!ret) {
|
|
if (compact->o_tree.o_script_level)
|
|
compact->o_tree.o_script_level--;
|
|
else
|
|
error("ScriptManager: basescript %d for cpt %d ended!", script, id);
|
|
} else
|
|
compact->o_tree.o_script_pc[level] = ret;
|
|
} while (!ret);
|
|
return 1;
|
|
//Logic continues - but the script must have changed logic mode
|
|
//this is a radical change from S2.0 where once a script finished there
|
|
//was no more processing for that object on that cycle - the Logic_engine terminated.
|
|
//This meant that new logics that needed immediate action got a first call from the
|
|
//setup function. This was a bit tweeky. This technique ensures that the script is a
|
|
//totally seamless concept that takes up zero cycle time. The only downside is that
|
|
//an FN_quit becomes slightly more convoluted, but so what you might ask.
|
|
}
|
|
|
|
void Logic::runMouseScript(Object *cpt, int32 scriptId) {
|
|
Header *script = _resMan->lockScript(scriptId);
|
|
debug(9, "running mouse script %d", scriptId);
|
|
interpretScript(cpt, _scriptVars[SPECIAL_ITEM], script, scriptId, scriptId);
|
|
_resMan->unlockScript(scriptId);
|
|
}
|
|
|
|
int Logic::interpretScript(Object *compact, int id, Header *scriptModule, int scriptBase, int scriptNum) {
|
|
int32 *scriptCode = (int32*)(((uint8*)scriptModule) + sizeof(Header));
|
|
int32 stack[MAX_STACK_SIZE];
|
|
int32 stackIdx = 0;
|
|
int32 offset;
|
|
int32 pc;
|
|
if (memcmp(scriptModule->type, "Script", 6))
|
|
error("Invalid script module!");
|
|
if (scriptModule->version != SCRIPT_VERSION)
|
|
error("Illegal script version!");
|
|
if (scriptNum < 0)
|
|
error("negative script number");
|
|
if ((uint32)scriptNum >= scriptModule->decomp_length)
|
|
error("Script number out of bounds");
|
|
|
|
if (scriptNum < scriptCode[0])
|
|
pc = scriptCode[scriptNum + 1];
|
|
else
|
|
pc = scriptNum;
|
|
int32 startOfScript = scriptCode[(scriptBase & ITM_ID) + 1];
|
|
|
|
int32 a, b, c, d, e, f;
|
|
int mCodeReturn = 0;
|
|
int32 mCodeNumber = 0, mCodeArguments = 0;
|
|
uint32 varNum = 0;
|
|
while (1) {
|
|
assert((stackIdx >= 0) && (stackIdx <= MAX_STACK_SIZE));
|
|
switch (scriptCode[pc++]) {
|
|
case IT_MCODE:
|
|
a = b = c = d = e = f = 0;
|
|
mCodeNumber = scriptCode[pc++];
|
|
mCodeArguments = scriptCode[pc++];
|
|
switch (mCodeArguments) {
|
|
case 6: f = stack[--stackIdx];
|
|
case 5: e = stack[--stackIdx];
|
|
case 4: d = stack[--stackIdx];
|
|
case 3: c = stack[--stackIdx];
|
|
case 2: b = stack[--stackIdx];
|
|
case 1: a = stack[--stackIdx];
|
|
case 0:
|
|
Debug::callMCode(mCodeNumber, mCodeArguments, a, b, c, d, e, f);
|
|
mCodeReturn = (this->*_mcodeTable[mCodeNumber])(compact, id, a, b, c, d, e, f);
|
|
break;
|
|
default:
|
|
warning("mcode[%d]: too many arguments(%d)", mCodeNumber, mCodeArguments);
|
|
}
|
|
if (mCodeReturn == 0)
|
|
return pc;
|
|
break;
|
|
case IT_PUSHNUMBER:
|
|
debug(9, "IT_PUSH: %d", scriptCode[pc]);
|
|
stack[stackIdx++] = scriptCode[pc++];
|
|
break;
|
|
case IT_PUSHVARIABLE:
|
|
debug(9, "IT_PUSHVARIABLE: ScriptVar[%d] => %d", scriptCode[pc], _scriptVars[scriptCode[pc]]);
|
|
varNum = scriptCode[pc++];
|
|
if (SwordEngine::_systemVars.isDemo) {
|
|
if (varNum >= 397) // BS1 Demo has different number of script variables
|
|
varNum++;
|
|
if (varNum >= 699)
|
|
varNum++;
|
|
}
|
|
stack[stackIdx++] = _scriptVars[varNum];
|
|
break;
|
|
case IT_NOTEQUAL:
|
|
stackIdx--;
|
|
debug(9, "IT_NOTEQUAL: RESULT = %d", stack[stackIdx - 1] != stack[stackIdx]);
|
|
stack[stackIdx - 1] = (stack[stackIdx - 1] != stack[stackIdx]);
|
|
break;
|
|
case IT_ISEQUAL:
|
|
stackIdx--;
|
|
debug(9, "IT_ISEQUAL: RESULT = %d", stack[stackIdx - 1] == stack[stackIdx]);
|
|
stack[stackIdx - 1] = (stack[stackIdx - 1] == stack[stackIdx]);
|
|
break;
|
|
case IT_PLUS:
|
|
stackIdx--;
|
|
debug(9, "IT_PLUS: RESULT = %d", stack[stackIdx - 1] + stack[stackIdx]);
|
|
stack[stackIdx - 1] = (stack[stackIdx - 1] + stack[stackIdx]);
|
|
break;
|
|
case IT_TIMES:
|
|
stackIdx--;
|
|
debug(9, "IT_TIMES: RESULT = %d", stack[stackIdx - 1] * stack[stackIdx]);
|
|
stack[stackIdx - 1] = (stack[stackIdx - 1] * stack[stackIdx]);
|
|
break;
|
|
case IT_ANDAND:
|
|
stackIdx--;
|
|
debug(9, "IT_ANDAND: RESULT = %d", stack[stackIdx - 1] && stack[stackIdx]);
|
|
stack[stackIdx - 1] = (stack[stackIdx - 1] && stack[stackIdx]);
|
|
break;
|
|
case IT_OROR: // ||
|
|
stackIdx--;
|
|
debug(9, "IT_OROR: RESULT = %d", stack[stackIdx - 1] || stack[stackIdx]);
|
|
stack[stackIdx - 1] = (stack[stackIdx - 1] || stack[stackIdx]);
|
|
break;
|
|
case IT_LESSTHAN:
|
|
stackIdx--;
|
|
debug(9, "IT_LESSTHAN: RESULT = %d", stack[stackIdx - 1] < stack[stackIdx]);
|
|
stack[stackIdx - 1] = (stack[stackIdx - 1] < stack[stackIdx]);
|
|
break;
|
|
case IT_NOT:
|
|
debug(9, "IT_NOT: RESULT = %d", stack[stackIdx - 1] ? 0 : 1);
|
|
if (stack[stackIdx - 1])
|
|
stack[stackIdx - 1] = 0;
|
|
else
|
|
stack[stackIdx - 1] = 1;
|
|
break;
|
|
case IT_MINUS:
|
|
stackIdx--;
|
|
debug(9, "IT_MINUS: RESULT = %d", stack[stackIdx - 1] - stack[stackIdx]);
|
|
stack[stackIdx - 1] = (stack[stackIdx - 1] - stack[stackIdx]);
|
|
break;
|
|
case IT_AND:
|
|
stackIdx--;
|
|
debug(9, "IT_AND: RESULT = %d", stack[stackIdx - 1] & stack[stackIdx]);
|
|
stack[stackIdx - 1] = (stack[stackIdx - 1] & stack[stackIdx]);
|
|
break;
|
|
case IT_OR:
|
|
stackIdx--;
|
|
debug(9, "IT_OR: RESULT = %d", stack[stackIdx - 1] | stack[stackIdx]);
|
|
stack[stackIdx - 1] = (stack[stackIdx - 1] | stack[stackIdx]);
|
|
break;
|
|
case IT_GTE:
|
|
stackIdx--;
|
|
debug(9, "IT_GTE: RESULT = %d", stack[stackIdx - 1] >= stack[stackIdx]);
|
|
stack[stackIdx - 1] = (stack[stackIdx - 1] >= stack[stackIdx]);
|
|
break;
|
|
case IT_LTE:
|
|
stackIdx--;
|
|
debug(9, "IT_LTE: RESULT = %d", stack[stackIdx - 1] <= stack[stackIdx]);
|
|
stack[stackIdx - 1] = (stack[stackIdx - 1] <= stack[stackIdx]);
|
|
break;
|
|
case IT_DEVIDE:
|
|
stackIdx--;
|
|
debug(9, "IT_DEVIDE: RESULT = %d", stack[stackIdx - 1] / stack[stackIdx]);
|
|
stack[stackIdx - 1] = (stack[stackIdx - 1] / stack[stackIdx]);
|
|
break;
|
|
case IT_GT:
|
|
stackIdx--;
|
|
debug(9, "IT_GT: RESULT = %d", stack[stackIdx - 1] > stack[stackIdx]);
|
|
stack[stackIdx - 1] = (stack[stackIdx - 1] > stack[stackIdx]);
|
|
break;
|
|
case IT_SCRIPTEND:
|
|
debug(9, "IT_SCRIPTEND");
|
|
return 0;
|
|
case IT_POPVAR: // pop a variable
|
|
debug(9, "IT_POPVAR: ScriptVars[%d] = %d", scriptCode[pc], stack[stackIdx-1]);
|
|
varNum = scriptCode[pc++];
|
|
if (SwordEngine::_systemVars.isDemo) {
|
|
if (varNum >= 397) // BS1 Demo has different number of script variables
|
|
varNum++;
|
|
if (varNum >= 699)
|
|
varNum++;
|
|
}
|
|
_scriptVars[varNum] = stack[--stackIdx];
|
|
break;
|
|
case IT_POPLONGOFFSET:
|
|
offset = scriptCode[pc++];
|
|
debug(9, "IT_POPLONGOFFSET: Cpt[%d] = %d", offset, stack[stackIdx - 1]);
|
|
*((int32 *)((uint8*)compact + offset)) = stack[--stackIdx];
|
|
break;
|
|
case IT_PUSHLONGOFFSET:
|
|
offset = scriptCode[pc++];
|
|
debug(9, "IT_PUSHLONGOFFSET: PUSH Cpt[%d] (==%d)", offset, *((int32 *)((uint8*)compact + offset)));
|
|
stack[stackIdx++] = *((int32 *)((uint8*)compact + offset));
|
|
break;
|
|
case IT_SKIPONFALSE:
|
|
debug(9, "IT_SKIPONFALSE: %d (%s)", scriptCode[pc], (stack[stackIdx-1] ? "IS TRUE (NOT SKIPPED)" : "IS FALSE (SKIPPED)"));
|
|
if (stack[--stackIdx])
|
|
pc++;
|
|
else
|
|
pc += scriptCode[pc];
|
|
break;
|
|
case IT_SKIP:
|
|
debug(9, "IT_SKIP: %d", scriptCode[pc]);
|
|
pc += scriptCode[pc];
|
|
break;
|
|
case IT_SWITCH: // The mega switch statement
|
|
debug(9, "IT_SWITCH: [SORRY, NO DEBUG INFO]");
|
|
{
|
|
int switchValue = stack[--stackIdx];
|
|
int switchCount = scriptCode[pc++];
|
|
int doneSwitch=0;
|
|
|
|
for (int cnt = 0; (cnt < switchCount) && (doneSwitch==0); cnt++) {
|
|
if (switchValue == scriptCode[pc]) {
|
|
pc += scriptCode[pc+1];
|
|
doneSwitch=1;
|
|
} else
|
|
pc += 2;
|
|
}
|
|
if (doneSwitch == 0)
|
|
pc += scriptCode[pc];
|
|
}
|
|
break;
|
|
case IT_SKIPONTRUE: // skip if expression true
|
|
debug(9, "IT_SKIPONTRUE: %d (%s)", scriptCode[pc], (stack[stackIdx-1] ? "IS TRUE (SKIPPED)" : "IS FALSE (NOT SKIPPED)"));
|
|
stackIdx--;
|
|
if (stack[stackIdx])
|
|
pc += scriptCode[pc];
|
|
else
|
|
pc++;
|
|
break;
|
|
case IT_PRINTF:
|
|
debug(0, "IT_PRINTF(%d)",stack[stackIdx]);
|
|
break;
|
|
case IT_RESTARTSCRIPT:
|
|
debug(9, "IT_RESTARTSCRIPT");
|
|
pc = startOfScript;
|
|
break;
|
|
case IT_POPWORDOFFSET:
|
|
offset = scriptCode[pc++];
|
|
debug(9, "IT_POPWORDOFFSET: Cpt[%d] = %d", offset, stack[stackIdx - 1] & 0xFFFF);
|
|
*((int32 *)((uint8*)compact + offset)) = stack[--stackIdx] & 0xffff;
|
|
break;
|
|
case IT_PUSHWORDOFFSET:
|
|
offset = scriptCode[pc++];
|
|
debug(9, "IT_PUSHWORDOFFSET: PUSH Cpt[%d] == %d", offset, (*((int32 *)((uint8*)compact + offset))) & 0xffff);
|
|
stack[stackIdx++] = (*((int32 *)((uint8*)compact + offset))) & 0xffff;
|
|
break;
|
|
default:
|
|
error("Invalid operator %d",scriptCode[pc-1]);
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
BSMcodeTable Logic::_mcodeTable[100] = {
|
|
&Logic::fnBackground,
|
|
&Logic::fnForeground,
|
|
&Logic::fnSort,
|
|
&Logic::fnNoSprite,
|
|
&Logic::fnMegaSet,
|
|
&Logic::fnAnim,
|
|
&Logic::fnSetFrame,
|
|
&Logic::fnFullAnim,
|
|
&Logic::fnFullSetFrame,
|
|
&Logic::fnFadeDown,
|
|
&Logic::fnFadeUp,
|
|
&Logic::fnCheckFade,
|
|
&Logic::fnSetSpritePalette,
|
|
&Logic::fnSetWholePalette,
|
|
&Logic::fnSetFadeTargetPalette,
|
|
&Logic::fnSetPaletteToFade,
|
|
&Logic::fnSetPaletteToCut,
|
|
&Logic::fnPlaySequence,
|
|
&Logic::fnIdle,
|
|
&Logic::fnPause,
|
|
&Logic::fnPauseSeconds,
|
|
&Logic::fnQuit,
|
|
&Logic::fnKillId,
|
|
&Logic::fnSuicide,
|
|
&Logic::fnNewScript,
|
|
&Logic::fnSubScript,
|
|
&Logic::fnRestartScript,
|
|
&Logic::fnSetBookmark,
|
|
&Logic::fnGotoBookmark,
|
|
&Logic::fnSendSync,
|
|
&Logic::fnWaitSync,
|
|
&Logic::cfnClickInteract,
|
|
&Logic::cfnSetScript,
|
|
&Logic::cfnPresetScript,
|
|
&Logic::fnInteract,
|
|
&Logic::fnIssueEvent,
|
|
&Logic::fnCheckForEvent,
|
|
&Logic::fnWipeHands,
|
|
&Logic::fnISpeak,
|
|
&Logic::fnTheyDo,
|
|
&Logic::fnTheyDoWeWait,
|
|
&Logic::fnWeWait,
|
|
&Logic::fnChangeSpeechText,
|
|
&Logic::fnTalkError,
|
|
&Logic::fnStartTalk,
|
|
&Logic::fnCheckForTextLine,
|
|
&Logic::fnAddTalkWaitStatusBit,
|
|
&Logic::fnRemoveTalkWaitStatusBit,
|
|
&Logic::fnNoHuman,
|
|
&Logic::fnAddHuman,
|
|
&Logic::fnBlankMouse,
|
|
&Logic::fnNormalMouse,
|
|
&Logic::fnLockMouse,
|
|
&Logic::fnUnlockMouse,
|
|
&Logic::fnSetMousePointer,
|
|
&Logic::fnSetMouseLuggage,
|
|
&Logic::fnMouseOn,
|
|
&Logic::fnMouseOff,
|
|
&Logic::fnChooser,
|
|
&Logic::fnEndChooser,
|
|
&Logic::fnStartMenu,
|
|
&Logic::fnEndMenu,
|
|
&Logic::cfnReleaseMenu,
|
|
&Logic::fnAddSubject,
|
|
&Logic::fnAddObject,
|
|
&Logic::fnRemoveObject,
|
|
&Logic::fnEnterSection,
|
|
&Logic::fnLeaveSection,
|
|
&Logic::fnChangeFloor,
|
|
&Logic::fnWalk,
|
|
&Logic::fnTurn,
|
|
&Logic::fnStand,
|
|
&Logic::fnStandAt,
|
|
&Logic::fnFace,
|
|
&Logic::fnFaceXy,
|
|
&Logic::fnIsFacing,
|
|
&Logic::fnGetTo,
|
|
&Logic::fnGetToError,
|
|
&Logic::fnGetPos,
|
|
&Logic::fnGetGamepadXy,
|
|
&Logic::fnPlayFx,
|
|
&Logic::fnStopFx,
|
|
&Logic::fnPlayMusic,
|
|
&Logic::fnStopMusic,
|
|
&Logic::fnInnerSpace,
|
|
&Logic::fnRandom,
|
|
&Logic::fnSetScreen,
|
|
&Logic::fnPreload,
|
|
&Logic::fnCheckCD,
|
|
&Logic::fnRestartGame,
|
|
&Logic::fnQuitGame,
|
|
&Logic::fnDeathScreen,
|
|
&Logic::fnSetParallax,
|
|
&Logic::fnTdebug,
|
|
&Logic::fnRedFlash,
|
|
&Logic::fnBlueFlash,
|
|
&Logic::fnYellow,
|
|
&Logic::fnGreen,
|
|
&Logic::fnPurple,
|
|
&Logic::fnBlack
|
|
};
|
|
|
|
int Logic::fnBackground(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
|
|
cpt->o_status &= ~(STAT_FORE | STAT_SORT);
|
|
cpt->o_status |= STAT_BACK;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnForeground(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_status &= ~(STAT_BACK | STAT_SORT);
|
|
cpt->o_status |= STAT_FORE;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnSort(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_status &= ~(STAT_BACK | STAT_FORE);
|
|
cpt->o_status |= STAT_SORT;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnNoSprite(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_status &= ~(STAT_BACK | STAT_FORE | STAT_SORT);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnMegaSet(Object *cpt, int32 id, int32 walk_data, int32 spr, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_mega_resource = walk_data;
|
|
cpt->o_walk_resource = spr;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnAnim(Object *cpt, int32 id, int32 cdt, int32 spr, int32 e, int32 f, int32 z, int32 x) {
|
|
AnimSet *animTab;
|
|
|
|
if (cdt && (!spr)) {
|
|
animTab = (AnimSet*)((uint8*)_resMan->openFetchRes(cdt) + sizeof(Header));
|
|
animTab += cpt->o_dir;
|
|
|
|
cpt->o_anim_resource = FROM_LE_32(animTab->cdt);
|
|
cpt->o_resource = FROM_LE_32(animTab->spr);
|
|
_resMan->resClose(cdt);
|
|
} else {
|
|
cpt->o_anim_resource = cdt;
|
|
cpt->o_resource = spr;
|
|
}
|
|
if ((cpt->o_anim_resource == 0) || (cpt->o_resource == 0))
|
|
error("fnAnim called width (%d/%d) => (%d/%d)", cdt, spr, cpt->o_anim_resource, cpt->o_resource);
|
|
|
|
FrameHeader *frameHead = _resMan->fetchFrame(_resMan->openFetchRes(cpt->o_resource), 0);
|
|
if (frameHead->offsetX || frameHead->offsetY) { // boxed mega anim?
|
|
cpt->o_status |= STAT_SHRINK;
|
|
cpt->o_anim_x = cpt->o_xcoord; // set anim coords to 'feet' coords - only need to do this once
|
|
cpt->o_anim_y = cpt->o_ycoord;
|
|
} else {
|
|
// Anim_driver sets anim coords to cdt coords for every frame of a loose anim
|
|
cpt->o_status &= ~STAT_SHRINK;
|
|
}
|
|
_resMan->resClose(cpt->o_resource);
|
|
|
|
cpt->o_logic = LOGIC_anim;
|
|
cpt->o_anim_pc = 0;
|
|
cpt->o_sync = 0;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnSetFrame(Object *cpt, int32 id, int32 cdt, int32 spr, int32 frameNo, int32 f, int32 z, int32 x) {
|
|
|
|
AnimUnit *animPtr;
|
|
|
|
uint8 *data = (uint8*)_resMan->openFetchRes(cdt);
|
|
data += sizeof(Header);
|
|
if (frameNo == LAST_FRAME)
|
|
frameNo = READ_LE_UINT32(data) - 1;
|
|
|
|
data += 4;
|
|
animPtr = (AnimUnit*)(data + frameNo * sizeof(AnimUnit));
|
|
|
|
cpt->o_anim_x = FROM_LE_32(animPtr->animX);
|
|
cpt->o_anim_y = FROM_LE_32(animPtr->animY);
|
|
cpt->o_frame = FROM_LE_32(animPtr->animFrame);
|
|
|
|
cpt->o_resource = spr;
|
|
cpt->o_status &= ~STAT_SHRINK;
|
|
_resMan->resClose(cdt);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnFullAnim(Object *cpt, int32 id, int32 anim, int32 graphic, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_logic = LOGIC_full_anim;
|
|
|
|
cpt->o_anim_pc = 0;
|
|
cpt->o_anim_resource = anim;
|
|
cpt->o_resource = graphic;
|
|
cpt->o_status &= ~STAT_SHRINK;
|
|
cpt->o_sync = 0;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnFullSetFrame(Object *cpt, int32 id, int32 cdt, int32 spr, int32 frameNo, int32 f, int32 z, int32 x) {
|
|
uint8 *data = (uint8*)_resMan->openFetchRes(cdt) + sizeof(Header);
|
|
|
|
if (frameNo == LAST_FRAME)
|
|
frameNo = READ_LE_UINT32(data) - 1;
|
|
data += 4;
|
|
|
|
AnimUnit *animPtr = (AnimUnit*)(data + sizeof(AnimUnit) * frameNo);
|
|
cpt->o_anim_x = cpt->o_xcoord = FROM_LE_32(animPtr->animX);
|
|
cpt->o_anim_y = cpt->o_ycoord = FROM_LE_32(animPtr->animY);
|
|
cpt->o_frame = FROM_LE_32(animPtr->animFrame);
|
|
|
|
cpt->o_resource = spr;
|
|
cpt->o_status &= ~STAT_SHRINK;
|
|
|
|
_resMan->resClose(cdt);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnFadeDown(Object *cpt, int32 id, int32 speed, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_screen->fadeDownPalette();
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnFadeUp(Object *cpt, int32 id, int32 speed, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_screen->fadeUpPalette();
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnCheckFade(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_scriptVars[RETURN_VALUE] = (uint8)_screen->stillFading();
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnSetSpritePalette(Object *cpt, int32 id, int32 spritePal, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_screen->fnSetPalette(184, 72, spritePal, false);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnSetWholePalette(Object *cpt, int32 id, int32 spritePal, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_screen->fnSetPalette(0, 256, spritePal, false);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnSetFadeTargetPalette(Object *cpt, int32 id, int32 spritePal, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_screen->fnSetPalette(0, 184, spritePal, true);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnSetPaletteToFade(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
SwordEngine::_systemVars.wantFade = true;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnSetPaletteToCut(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
SwordEngine::_systemVars.wantFade = false;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnPlaySequence(Object *cpt, int32 id, int32 sequenceId, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
|
|
// A cutscene usually (always?) means the room will change. In the
|
|
// meantime, we don't want any looping sound effects still playing.
|
|
_sound->quitScreen();
|
|
|
|
if ((SwordEngine::_systemVars.cutscenePackVersion == 1) && (sequenceId == SEQ_CREDITS)) {
|
|
CreditsPlayer player(_system, _mixer);
|
|
player.play();
|
|
} else {
|
|
MoviePlayer player(_screen, _mixer, _system);
|
|
player.play(sequenceId);
|
|
}
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnIdle(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_tree.o_script_level = 0; // force to level 0
|
|
cpt->o_logic = LOGIC_idle;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnPause(Object *cpt, int32 id, int32 pause, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_pause = pause;
|
|
cpt->o_logic = LOGIC_pause;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnPauseSeconds(Object *cpt, int32 id, int32 pause, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_pause = pause * FRAME_RATE;
|
|
cpt->o_logic = LOGIC_pause;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnQuit(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_logic = LOGIC_quit;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnKillId(Object *cpt, int32 id, int32 target, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
Object *targetObj = _objMan->fetchObject(target);
|
|
targetObj->o_status = 0;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnSuicide(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_status = 0;
|
|
cpt->o_logic = LOGIC_quit;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnNewScript(Object *cpt, int32 id, int32 script, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_logic = LOGIC_new_script;
|
|
_newScript = script;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnSubScript(Object *cpt, int32 id, int32 script, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_tree.o_script_level++;
|
|
if (cpt->o_tree.o_script_level == TOTAL_script_levels)
|
|
error("Compact %d: script level exceeded in fnSubScript.", id);
|
|
cpt->o_tree.o_script_pc[cpt->o_tree.o_script_level] = script;
|
|
cpt->o_tree.o_script_id[cpt->o_tree.o_script_level] = script;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnRestartScript(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_logic = LOGIC_restart;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnSetBookmark(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
memcpy(&cpt->o_bookmark.o_script_level, &cpt->o_tree.o_script_level, sizeof(ScriptTree));
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnGotoBookmark(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_logic = LOGIC_bookmark;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnSendSync(Object *cpt, int32 id, int32 sendId, int32 syncValue, int32 e, int32 f, int32 z, int32 x) {
|
|
Object *target = _objMan->fetchObject(sendId);
|
|
target->o_sync = syncValue;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnWaitSync(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_logic = LOGIC_wait_for_sync;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::cfnClickInteract(Object *cpt, int32 id, int32 target, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
Object *tar = _objMan->fetchObject(target);
|
|
cpt = _objMan->fetchObject(PLAYER);
|
|
cpt->o_tree.o_script_level = 0;
|
|
cpt->o_tree.o_script_pc[0] = tar->o_interact;
|
|
cpt->o_tree.o_script_id[0] = tar->o_interact;
|
|
cpt->o_logic = LOGIC_script;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::cfnSetScript(Object *cpt, int32 id, int32 target, int32 script, int32 e, int32 f, int32 z, int32 x) {
|
|
Object *tar = _objMan->fetchObject(target);
|
|
tar->o_tree.o_script_level = 0;
|
|
tar->o_tree.o_script_pc[0] = script;
|
|
tar->o_tree.o_script_id[0] = script;
|
|
tar->o_logic = LOGIC_script;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::cfnPresetScript(Object *cpt, int32 id, int32 target, int32 script, int32 e, int32 f, int32 z, int32 x) {
|
|
Object *tar = _objMan->fetchObject(target);
|
|
tar->o_tree.o_script_level = 0;
|
|
tar->o_tree.o_script_pc[0] = script;
|
|
tar->o_tree.o_script_id[0] = script;
|
|
if (tar->o_logic == LOGIC_idle)
|
|
tar->o_logic = LOGIC_script;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnInteract(Object *cpt, int32 id, int32 target, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
Object *tar = _objMan->fetchObject(target);
|
|
cpt->o_place = tar->o_place;
|
|
|
|
Object *floorObject = _objMan->fetchObject(tar->o_place);
|
|
cpt->o_scale_a = floorObject->o_scale_a;
|
|
cpt->o_scale_b = floorObject->o_scale_b;
|
|
|
|
cpt->o_tree.o_script_level++;
|
|
cpt->o_tree.o_script_pc[cpt->o_tree.o_script_level] = tar->o_interact;
|
|
cpt->o_tree.o_script_id[cpt->o_tree.o_script_level] = tar->o_interact;
|
|
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnIssueEvent(Object *cpt, int32 id, int32 event, int32 delay, int32 e, int32 f, int32 z, int32 x) {
|
|
_eventMan->fnIssueEvent(cpt, id, event, delay);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnCheckForEvent(Object *cpt, int32 id, int32 pause, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
return _eventMan->fnCheckForEvent(cpt, id, pause);
|
|
}
|
|
|
|
int Logic::fnWipeHands(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_scriptVars[OBJECT_HELD] = 0;
|
|
_mouse->setLuggage(0, 0);
|
|
_menu->refresh(MENU_TOP);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnISpeak(Object *cpt, int32 id, int32 cdt, int32 textNo, int32 spr, int32 f, int32 z, int32 x) {
|
|
_speechClickDelay = 3;
|
|
if (((textNo & ~1) == 0x3f0012) && (!cdt) && (!spr)) {
|
|
cdt = GEOSTDLCDT; // workaround for missing animation when examining
|
|
spr = GEOSTDL; // the conductor on the train roof
|
|
}
|
|
cpt->o_logic = LOGIC_speech;
|
|
|
|
// first setup the talk animation
|
|
if (cdt && (!spr)) { // if 'cdt' is non-zero but 'spr' is zero - 'cdt' is an anim table tag
|
|
AnimSet *animTab = (AnimSet*)((uint8*)_resMan->openFetchRes(cdt) + sizeof(Header));
|
|
animTab += cpt->o_dir;
|
|
|
|
cpt->o_anim_resource = FROM_LE_32(animTab->cdt);
|
|
if (animTab->cdt)
|
|
cpt->o_resource = FROM_LE_32(animTab->spr);
|
|
_resMan->resClose(cdt);
|
|
} else {
|
|
cpt->o_anim_resource = cdt;
|
|
if (cdt)
|
|
cpt->o_resource = spr;
|
|
}
|
|
cpt->o_anim_pc = 0; // start anim from first frame
|
|
if (cpt->o_anim_resource) {
|
|
if (!cpt->o_resource)
|
|
error("ID %d: Can't run anim with cdt=%d, spr=%d", id, cdt, spr);
|
|
|
|
FrameHeader *frameHead = _resMan->fetchFrame(_resMan->openFetchRes(cpt->o_resource), 0);
|
|
if (frameHead->offsetX && frameHead->offsetY) { // is this a boxed mega?
|
|
cpt->o_status |= STAT_SHRINK;
|
|
cpt->o_anim_x = cpt->o_xcoord;
|
|
cpt->o_anim_y = cpt->o_ycoord;
|
|
} else
|
|
cpt->o_status &= ~STAT_SHRINK;
|
|
|
|
_resMan->resClose(cpt->o_resource);
|
|
}
|
|
if (SwordEngine::_systemVars.playSpeech)
|
|
_speechRunning = _sound->startSpeech(textNo >> 16, textNo & 0xFFFF);
|
|
else
|
|
_speechRunning = false;
|
|
_speechFinished = false;
|
|
if (SwordEngine::_systemVars.showText || (!_speechRunning)) {
|
|
_textRunning = true;
|
|
|
|
char *text = _objMan->lockText(textNo);
|
|
cpt->o_speech_time = strlen(text) + 5;
|
|
uint32 textCptId = _textMan->lowTextManager((uint8*)text, cpt->o_speech_width, (uint8)cpt->o_speech_pen);
|
|
_objMan->unlockText(textNo);
|
|
|
|
Object * textCpt = _objMan->fetchObject(textCptId);
|
|
textCpt->o_screen = cpt->o_screen;
|
|
textCpt->o_target = textCptId;
|
|
|
|
// the graphic is a property of Text, so we don't lock/unlock it.
|
|
uint16 textSpriteWidth = FROM_LE_16(_textMan->giveSpriteData(textCpt->o_target)->width);
|
|
uint16 textSpriteHeight = FROM_LE_16(_textMan->giveSpriteData(textCpt->o_target)->height);
|
|
|
|
cpt->o_text_id = textCptId;
|
|
|
|
// now set text coords, above the player, usually
|
|
|
|
#define TEXT_MARGIN 3 // distance kept from edges of screen
|
|
#define ABOVE_HEAD 20 // distance kept above talking sprite
|
|
uint16 textX, textY;
|
|
if (((id == GEORGE) || ((id == NICO) && (_scriptVars[SCREEN] == 10))) && (!cpt->o_anim_resource)) {
|
|
// if George is doing Voice-Over text (centered at the bottom of the screen)
|
|
textX = _scriptVars[SCROLL_OFFSET_X] + 128 + (640 / 2) - textSpriteWidth / 2;
|
|
textY = _scriptVars[SCROLL_OFFSET_Y] + 128 + 400;
|
|
} else {
|
|
if ((id == GEORGE) && (_scriptVars[SCREEN] == 79))
|
|
textX = cpt->o_mouse_x2; // move it off george's head
|
|
else
|
|
textX = (cpt->o_mouse_x1 + cpt->o_mouse_x2) / 2 - textSpriteWidth / 2;
|
|
|
|
textY = cpt->o_mouse_y1 - textSpriteHeight - ABOVE_HEAD;
|
|
}
|
|
// now ensure text is within visible screen
|
|
uint16 textLeftMargin, textRightMargin, textTopMargin, textBottomMargin;
|
|
textLeftMargin = SCREEN_LEFT_EDGE + TEXT_MARGIN + _scriptVars[SCROLL_OFFSET_X];
|
|
textRightMargin = SCREEN_RIGHT_EDGE - TEXT_MARGIN + _scriptVars[SCROLL_OFFSET_X] - textSpriteWidth;
|
|
textTopMargin = SCREEN_TOP_EDGE + TEXT_MARGIN + _scriptVars[SCROLL_OFFSET_Y];
|
|
textBottomMargin = SCREEN_BOTTOM_EDGE - TEXT_MARGIN + _scriptVars[SCROLL_OFFSET_Y] - textSpriteHeight;
|
|
|
|
textCpt->o_anim_x = textCpt->o_xcoord = inRange(textLeftMargin, textX, textRightMargin);
|
|
textCpt->o_anim_y = textCpt->o_ycoord = inRange(textTopMargin, textY, textBottomMargin);
|
|
}
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
//send instructions to mega in conversation with player
|
|
//the instruction is interpreted by the script mega_interact
|
|
int Logic::fnTheyDo(Object *cpt, int32 id, int32 tar, int32 instruc, int32 param1, int32 param2, int32 param3, int32 x) {
|
|
Object *target;
|
|
target = _objMan->fetchObject(tar);
|
|
target->o_down_flag = instruc; // instruction for the mega
|
|
target->o_ins1 = param1;
|
|
target->o_ins2 = param2;
|
|
target->o_ins3 = param3;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
//send an instruction to mega we're talking to and wait
|
|
//until it has finished before returning to script
|
|
int Logic::fnTheyDoWeWait(Object *cpt, int32 id, int32 tar, int32 instruc, int32 param1, int32 param2, int32 param3, int32 x) {
|
|
// workaround for scriptbug #928791: Freeze at hospital
|
|
// in at least one game version, a script forgets to set sam_returning back to zero
|
|
if ((tar == SAM) && (instruc == INS_talk) && (param2 == 2162856))
|
|
_scriptVars[SAM_RETURNING] = 0;
|
|
Object *target = _objMan->fetchObject(tar);
|
|
target->o_down_flag = instruc; // instruction for the mega
|
|
target->o_ins1 = param1;
|
|
target->o_ins2 = param2;
|
|
target->o_ins3 = param3;
|
|
target->o_status &= ~STAT_TALK_WAIT;
|
|
|
|
cpt->o_logic = LOGIC_wait_for_talk;
|
|
cpt->o_down_flag = tar;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnWeWait(Object *cpt, int32 id, int32 tar, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
Object *target = _objMan->fetchObject(tar);
|
|
target->o_status &= ~STAT_TALK_WAIT;
|
|
|
|
cpt->o_logic = LOGIC_wait_for_talk;
|
|
cpt->o_down_flag = tar;
|
|
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnChangeSpeechText(Object *cpt, int32 id, int32 tar, int32 width, int32 pen, int32 f, int32 z, int32 x) {
|
|
Object *target = _objMan->fetchObject(tar);
|
|
target->o_speech_width = width;
|
|
target->o_speech_pen = pen;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
//mega_interact has received an instruction it does not understand -
|
|
//The game is halted for debugging. Maybe we'll remove this later.
|
|
int Logic::fnTalkError(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
error("fnTalkError for id %d, instruction %d", id, cpt->o_down_flag);
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnStartTalk(Object *cpt, int32 id, int32 target, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_down_flag = target;
|
|
cpt->o_logic = LOGIC_start_talk;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnCheckForTextLine(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_scriptVars[RETURN_VALUE] = _objMan->fnCheckForTextLine(id);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnAddTalkWaitStatusBit(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_status |= STAT_TALK_WAIT;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnRemoveTalkWaitStatusBit(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_status &= ~STAT_TALK_WAIT;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnNoHuman(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_mouse->fnNoHuman();
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnAddHuman(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_mouse->fnAddHuman();
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnBlankMouse(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_mouse->fnBlankMouse();
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnNormalMouse(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_mouse->fnNormalMouse();
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnLockMouse(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_mouse->fnLockMouse();
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnUnlockMouse(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_mouse->fnUnlockMouse();
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnSetMousePointer(Object *cpt, int32 id, int32 tag, int32 rate, int32 e, int32 f, int32 z, int32 x) {
|
|
_mouse->setPointer(tag, rate);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnSetMouseLuggage(Object *cpt, int32 id, int32 tag, int32 rate, int32 e, int32 f, int32 z, int32 x) {
|
|
_mouse->setLuggage(tag, rate);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnMouseOn(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_status |= STAT_MOUSE;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnMouseOff(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_status &= ~STAT_MOUSE;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnChooser(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_menu->fnChooser(cpt);
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnEndChooser(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_menu->fnEndChooser();
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnStartMenu(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_menu->fnStartMenu();
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnEndMenu(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_menu->fnEndMenu();
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::cfnReleaseMenu(Object *cpt, int32 id, int32 c, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_menu->cfnReleaseMenu();
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnAddSubject(Object *cpt, int32 id, int32 sub, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_menu->fnAddSubject(sub);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnAddObject(Object *cpt, int32 id, int32 objectNo, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_scriptVars[POCKET_1 + objectNo - 1] = 1; // basically means: carrying object objectNo = true;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnRemoveObject(Object *cpt, int32 id, int32 objectNo, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
_scriptVars[POCKET_1 + objectNo - 1] = 0;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnEnterSection(Object *cpt, int32 id, int32 screen, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
if (screen >= TOTAL_SECTIONS)
|
|
error("mega %d tried entering section %d", id, screen);
|
|
|
|
/* if (cpt->o_type == TYPE_PLAYER)
|
|
^= this was the original condition from the game sourcecode.
|
|
not sure why it doesn't work*/
|
|
if (id == PLAYER)
|
|
_scriptVars[NEW_SCREEN] = screen;
|
|
else
|
|
cpt->o_screen = screen; // move the mega
|
|
_objMan->megaEntering(screen);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnLeaveSection(Object *cpt, int32 id, int32 oldScreen, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
if (oldScreen >= TOTAL_SECTIONS)
|
|
error("mega %d leaving section %d", id, oldScreen);
|
|
_objMan->megaLeaving(oldScreen, id);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnChangeFloor(Object *cpt, int32 id, int32 floor, int32 d, int32 e, int32 f, int32 z, int32 x) {
|
|
cpt->o_place = floor;
|
|
Object *floorCpt = _objMan->fetchObject(floor);
|
|
cpt->o_scale_a = floorCpt->o_scale_a;
|
|
cpt->o_scale_b = floorCpt->o_scale_b;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnWalk(Object *cpt, int32 id, int32 x, int32 y, int32 dir, int32 stance, int32 a, int32 b) {
|
|
if (stance > 0)
|
|
dir = 9;
|
|
cpt->o_walk_pc = 0;
|
|
cpt->o_route[1].frame = 512; // end of sequence
|
|
if (id == PLAYER)
|
|
_router->setPlayerTarget(x, y, dir, stance);
|
|
|
|
int32 routeRes = _router->routeFinder(id, cpt, x, y, dir);
|
|
|
|
if (id == PLAYER) {
|
|
_router->resetExtraData();
|
|
if ((routeRes == 1) || (routeRes == 2)) {
|
|
_scriptVars[MEGA_ON_GRID] = 0;
|
|
_scriptVars[REROUTE_GEORGE] = 0;
|
|
}
|
|
}
|
|
if ((routeRes == 1) || (routeRes == 2)) {
|
|
cpt->o_down_flag = 1; // 1 means okay.
|
|
// if both mouse buttons were pressed on an exit => skip george's walk
|
|
if ((id == GEORGE) && (_mouse->testEvent() == MOUSE_BOTH_BUTTONS)) {
|
|
int32 target = _scriptVars[CLICK_ID];
|
|
// exceptions: compacts that use hand pointers but are not actually exits
|
|
if ((target != LEFT_SCROLL_POINTER) && (target != RIGHT_SCROLL_POINTER) &&
|
|
(target != FLOOR_63) && (target != ROOF_63) && (target != GUARD_ROOF_63) &&
|
|
(target != LEFT_TREE_POINTER_71) && (target != RIGHT_TREE_POINTER_71)) {
|
|
|
|
target = _objMan->fetchObject(_scriptVars[CLICK_ID])->o_mouse_on;
|
|
if ((target >= SCR_exit0) && (target <= SCR_exit9)) {
|
|
fnStandAt(cpt,id,x,y,dir,stance,0,0);
|
|
return SCRIPT_STOP;
|
|
}
|
|
}
|
|
}
|
|
cpt->o_logic = LOGIC_AR_animate;
|
|
return SCRIPT_STOP;
|
|
} else if (routeRes == 3)
|
|
cpt->o_down_flag = 1; // pretend it was successful
|
|
else
|
|
cpt->o_down_flag = 0; // 0 means error
|
|
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnTurn(Object *cpt, int32 id, int32 dir, int32 stance, int32 c, int32 d, int32 a, int32 b) {
|
|
if (stance > 0)
|
|
dir = 9;
|
|
int route = _router->routeFinder(id, cpt, cpt->o_xcoord, cpt->o_ycoord, dir);
|
|
|
|
if (route)
|
|
cpt->o_down_flag = 1; //1 means ok
|
|
else
|
|
cpt->o_down_flag = 0; //0 means error
|
|
|
|
cpt->o_logic = LOGIC_AR_animate;
|
|
cpt->o_walk_pc = 0; //reset
|
|
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnStand(Object *cpt, int32 id, int32 dir, int32 stance, int32 c, int32 d, int32 a, int32 b) {
|
|
if ((dir < 0) || (dir > 8)) {
|
|
warning("fnStand:: invalid direction %d", dir);
|
|
return SCRIPT_CONT;
|
|
}
|
|
if (dir == 8)
|
|
dir = cpt->o_dir;
|
|
cpt->o_resource = cpt->o_walk_resource;
|
|
cpt->o_status |= STAT_SHRINK;
|
|
cpt->o_anim_x = cpt->o_xcoord;
|
|
cpt->o_anim_y = cpt->o_ycoord;
|
|
cpt->o_frame = 96 + dir;
|
|
cpt->o_dir = dir;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnStandAt(Object *cpt, int32 id, int32 x, int32 y, int32 dir, int32 stance, int32 a, int32 b) {
|
|
if ((dir < 0) || (dir > 8)) {
|
|
warning("fnStandAt:: invalid direction %d", dir);
|
|
return SCRIPT_CONT;
|
|
}
|
|
if (dir == 8)
|
|
dir = cpt->o_dir;
|
|
cpt->o_xcoord = x;
|
|
cpt->o_ycoord = y;
|
|
return fnStand(cpt, id, dir, stance, 0, 0, 0, 0);
|
|
}
|
|
|
|
int Logic::fnFace(Object *cpt, int32 id, int32 targetId, int32 b, int32 c, int32 d, int32 a, int32 z) {
|
|
Object *target = _objMan->fetchObject(targetId);
|
|
int32 x, y;
|
|
if ((target->o_type == TYPE_MEGA) || (target->o_type == TYPE_PLAYER)) {
|
|
x = target->o_xcoord;
|
|
y = target->o_ycoord;
|
|
} else {
|
|
x = (target->o_mouse_x1 + target->o_mouse_x2) / 2;
|
|
y = target->o_mouse_y2;
|
|
}
|
|
int32 megaTarDir = whatTarget(cpt->o_xcoord, cpt->o_ycoord, x, y);
|
|
fnTurn(cpt, id, megaTarDir, 0, 0, 0, 0, 0);
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnFaceXy(Object *cpt, int32 id, int32 x, int32 y, int32 c, int32 d, int32 a, int32 b) {
|
|
int megaTarDir = whatTarget(cpt->o_xcoord, cpt->o_ycoord, x, y);
|
|
fnTurn(cpt, id, megaTarDir, 0, 0, 0, 0, 0);
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnIsFacing(Object *cpt, int32 id, int32 targetId, int32 b, int32 c, int32 d, int32 a, int32 z) {
|
|
Object *target = _objMan->fetchObject(targetId);
|
|
int32 x, y, dir;
|
|
if ((target->o_type == TYPE_MEGA) || (target->o_type == TYPE_PLAYER)) {
|
|
x = target->o_xcoord;
|
|
y = target->o_ycoord;
|
|
dir = target->o_dir;
|
|
} else
|
|
error("fnIsFacing:: Target isn't a mega!");
|
|
|
|
int32 lookDir = whatTarget(x, y, cpt->o_xcoord, cpt->o_ycoord);
|
|
lookDir -= dir;
|
|
lookDir = ABS(lookDir);
|
|
|
|
if (lookDir > 4)
|
|
lookDir = 8 - lookDir;
|
|
|
|
_scriptVars[RETURN_VALUE] = lookDir;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnGetTo(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
Object *place = _objMan->fetchObject(cpt->o_place);
|
|
|
|
cpt->o_tree.o_script_level++;
|
|
cpt->o_tree.o_script_pc[cpt->o_tree.o_script_level] = place->o_get_to_script;
|
|
cpt->o_tree.o_script_id[cpt->o_tree.o_script_level] = place->o_get_to_script;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnGetToError(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
debug(1, "fnGetToError: compact %d at place %d no get-to for target %d, click_id %d\n", id, cpt->o_place, cpt->o_target, _scriptVars[CLICK_ID]);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnRandom(Object *compact, int32 id, int32 min, int32 max, int32 e, int32 f, int32 z, int32 x) {
|
|
_scriptVars[RETURN_VALUE] = _rnd.getRandomNumberRng(min, max);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnGetPos(Object *cpt, int32 id, int32 targetId, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
Object *target = _objMan->fetchObject(targetId);
|
|
if ((target->o_type == TYPE_MEGA) || (target->o_type == TYPE_PLAYER)) {
|
|
_scriptVars[RETURN_VALUE] = target->o_xcoord;
|
|
_scriptVars[RETURN_VALUE_2] = target->o_ycoord;
|
|
} else {
|
|
_scriptVars[RETURN_VALUE] = (target->o_mouse_x1 + target->o_mouse_x2) / 2;
|
|
_scriptVars[RETURN_VALUE_2] = target->o_mouse_y2;
|
|
}
|
|
_scriptVars[RETURN_VALUE_3] = target->o_dir;
|
|
|
|
int32 megaSeperation;
|
|
if (targetId == DUANE)
|
|
megaSeperation = 70; // George & Duane stand with feet 70 pixels apart when at full scale
|
|
else if (targetId == BENOIR)
|
|
megaSeperation = 61; // George & Benoir
|
|
else
|
|
megaSeperation = 42; // George & Nico/Goinfre stand with feet 42 pixels apart when at full scale
|
|
|
|
if (target->o_status & STAT_SHRINK) {
|
|
int32 scale = (target->o_scale_a * target->o_ycoord + target->o_scale_b) / 256;
|
|
_scriptVars[RETURN_VALUE_4] = (megaSeperation * scale) / 256;
|
|
} else
|
|
_scriptVars[RETURN_VALUE_4] = megaSeperation;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnGetGamepadXy(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
// playstation only
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnPlayFx(Object *cpt, int32 id, int32 fxNo, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
_scriptVars[RETURN_VALUE] = _sound->addToQueue(fxNo);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnStopFx(Object *cpt, int32 id, int32 fxNo, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
_sound->fnStopFx(fxNo);
|
|
//_sound->removeFromQueue(fxNo);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnPlayMusic(Object *cpt, int32 id, int32 tuneId, int32 loopFlag, int32 c, int32 d, int32 z, int32 x) {
|
|
if (tuneId == 153)
|
|
return SCRIPT_CONT;
|
|
if (loopFlag == LOOPED)
|
|
_scriptVars[CURRENT_MUSIC] = tuneId; // so it gets restarted when saving & reloading
|
|
else
|
|
_scriptVars[CURRENT_MUSIC] = 0;
|
|
|
|
_music->startMusic(tuneId, loopFlag);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnStopMusic(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
_scriptVars[CURRENT_MUSIC] = 0;
|
|
_music->fadeDown();
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnInnerSpace(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
error("fnInnerSpace() not working.");
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnSetScreen(Object *cpt, int32 id, int32 target, int32 screen, int32 c, int32 d, int32 z, int32 x) {
|
|
_objMan->fetchObject(target)->o_screen = screen;
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnPreload(Object *cpt, int32 id, int32 resId, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
_resMan->resOpen(resId);
|
|
_resMan->resClose(resId);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnCheckCD(Object *cpt, int32 id, int32 screen, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
// only a dummy, here.
|
|
// the check is done in the mainloop
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnRestartGame(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
SwordEngine::_systemVars.forceRestart = true;
|
|
cpt->o_logic = LOGIC_quit;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnQuitGame(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
if (SwordEngine::_systemVars.isDemo) {
|
|
GUI::MessageDialog dialog("This is the end of the Broken Sword 1 Demo", "OK", NULL);
|
|
dialog.runModal();
|
|
SwordEngine::_systemVars.engineQuit = true;
|
|
} else
|
|
error("fnQuitGame() called");
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnDeathScreen(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
|
|
if (_scriptVars[FINALE_OPTION_FLAG] == 4) // successful end of game!
|
|
SwordEngine::_systemVars.controlPanelMode = CP_THEEND;
|
|
else
|
|
SwordEngine::_systemVars.controlPanelMode = CP_DEATHSCREEN;
|
|
|
|
cpt->o_logic = LOGIC_quit;
|
|
return SCRIPT_STOP;
|
|
}
|
|
|
|
int Logic::fnSetParallax(Object *cpt, int32 id, int32 screen, int32 resId, int32 c, int32 d, int32 z, int32 x) {
|
|
_screen->fnSetParallax(screen, resId);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnTdebug(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
debug(1, "Script TDebug id %d code %d, %d", id, a, b);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnRedFlash(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
_screen->fnFlash(FLASH_RED);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnBlueFlash(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
_screen->fnFlash(FLASH_BLUE);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnYellow(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
_screen->fnFlash(BORDER_YELLOW);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnGreen(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
_screen->fnFlash(BORDER_GREEN);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnPurple(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
_screen->fnFlash(BORDER_PURPLE);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
int Logic::fnBlack(Object *cpt, int32 id, int32 a, int32 b, int32 c, int32 d, int32 z, int32 x) {
|
|
_screen->fnFlash(BORDER_BLACK);
|
|
return SCRIPT_CONT;
|
|
}
|
|
|
|
uint16 Logic::inRange(uint16 a, uint16 b, uint16 c) {
|
|
return (a > b)? a : (((b > c) ? c : b));
|
|
}
|
|
|
|
void Logic::startPosCallFn(uint8 fnId, uint32 param1, uint32 param2, uint32 param3) {
|
|
Object *obj = NULL;
|
|
switch (fnId) {
|
|
case opcPlaySequence:
|
|
fnPlaySequence(NULL, 0, param1, 0, 0, 0, 0, 0);
|
|
break;
|
|
case opcAddObject:
|
|
fnAddObject(NULL, 0, param1, 0, 0, 0, 0, 0);
|
|
break;
|
|
case opcRemoveObject:
|
|
fnRemoveObject(NULL, 0, param1, 0, 0, 0, 0, 0);
|
|
break;
|
|
case opcMegaSet:
|
|
obj = _objMan->fetchObject(param1);
|
|
fnMegaSet(obj, param1, param2, param3, 0, 0, 0, 0);
|
|
break;
|
|
case opcNoSprite:
|
|
obj = _objMan->fetchObject(param1);
|
|
fnNoSprite(obj, param1, param2, param3, 0, 0, 0, 0);
|
|
break;
|
|
default:
|
|
error("Illegal fnCallfn argument %d", fnId);
|
|
}
|
|
}
|
|
|
|
void Logic::runStartScript(const uint8 *data) {
|
|
uint16 varId = 0;
|
|
uint8 fnId = 0;
|
|
uint32 param1 = 0;
|
|
while (*data != opcSeqEnd) {
|
|
switch (*data++) {
|
|
case opcCallFn:
|
|
fnId = *data++;
|
|
param1 = *data++;
|
|
startPosCallFn(fnId, param1, 0, 0);
|
|
break;
|
|
case opcCallFnLong:
|
|
fnId = *data++;
|
|
startPosCallFn(fnId, READ_LE_UINT32(data), READ_LE_UINT32(data + 4), READ_LE_UINT32(data + 8));
|
|
data += 12;
|
|
break;
|
|
case opcSetVar8:
|
|
varId = READ_LE_UINT16(data);
|
|
_scriptVars[varId] = data[2];
|
|
data += 3;
|
|
break;
|
|
case opcSetVar16:
|
|
varId = READ_LE_UINT16(data);
|
|
_scriptVars[varId] = READ_LE_UINT16(data + 2);
|
|
data += 4;
|
|
break;
|
|
case opcSetVar32:
|
|
varId = READ_LE_UINT16(data);
|
|
_scriptVars[varId] = READ_LE_UINT32(data + 2);
|
|
data += 6;
|
|
break;
|
|
case opcGeorge:
|
|
_scriptVars[CHANGE_X] = READ_LE_UINT16(data + 0);
|
|
_scriptVars[CHANGE_Y] = READ_LE_UINT16(data + 2);
|
|
_scriptVars[CHANGE_DIR] = data[4];
|
|
_scriptVars[CHANGE_PLACE] = READ_LE_UINT24(data + 5);
|
|
data += 8;
|
|
break;
|
|
case opcRunStart:
|
|
data = _startData[*data];
|
|
break;
|
|
case opcRunHelper:
|
|
data = _helperData[*data];
|
|
break;
|
|
default:
|
|
error("Unexpected opcode in StartScript");
|
|
}
|
|
}
|
|
}
|
|
|
|
void Logic::startPositions(uint32 pos) {
|
|
bool spainVisit2 = false;
|
|
if ((pos >= 956) && (pos <= 962)) {
|
|
spainVisit2 = true;
|
|
pos -= 900;
|
|
}
|
|
if ((pos > 80) || (_startData[pos] == NULL))
|
|
error("Starting in Section %d is not supported", pos);
|
|
|
|
Logic::_scriptVars[CHANGE_STANCE] = STAND;
|
|
Logic::_scriptVars[GEORGE_CDT_FLAG] = GEO_TLK_TABLE;
|
|
|
|
runStartScript(_startData[pos]);
|
|
if (spainVisit2)
|
|
runStartScript(_helperData[HELP_SPAIN2]);
|
|
|
|
if (pos == 0)
|
|
pos = 1;
|
|
Object *compact = _objMan->fetchObject(PLAYER);
|
|
fnEnterSection(compact, PLAYER, pos, 0, 0, 0, 0, 0); // (automatically opens the compact resource for that section)
|
|
SwordEngine::_systemVars.controlPanelMode = CP_NORMAL;
|
|
SwordEngine::_systemVars.wantFade = true;
|
|
}
|
|
|
|
const uint32 Logic::_scriptVarInit[NON_ZERO_SCRIPT_VARS][2] = {
|
|
{ 42, 448}, { 43, 378}, { 51, 1}, { 92, 1}, { 147, 71}, { 201, 1},
|
|
{ 209, 1}, { 215, 1}, { 242, 2}, { 244, 1}, { 246, 3}, { 247, 1},
|
|
{ 253, 1}, { 297, 1}, { 398, 1}, { 508, 1}, { 605, 1}, { 606, 1},
|
|
{ 701, 1}, { 709, 1}, { 773, 1}, { 843, 1}, { 907, 1}, { 923, 1},
|
|
{ 966, 1}, { 988, 2}, {1058, 1}, {1059, 2}, {1060, 3}, {1061, 4},
|
|
{1062, 5}, {1063, 6}, {1064, 7}, {1065, 8}, {1066, 9}, {1067, 10},
|
|
{1068, 11}, {1069, 12}, {1070, 13}, {1071, 14}, {1072, 15}, {1073, 16},
|
|
{1074, 17}, {1075, 18}, {1076, 19}, {1077, 20}, {1078, 21}, {1079, 22},
|
|
{1080, 23}, {1081, 24}, {1082, 25}, {1083, 26}, {1084, 27}, {1085, 28},
|
|
{1086, 29}, {1087, 30}, {1088, 31}, {1089, 32}, {1090, 33}, {1091, 34},
|
|
{1092, 35}, {1093, 36}, {1094, 37}, {1095, 38}, {1096, 39}, {1097, 40},
|
|
{1098, 41}, {1099, 42}, {1100, 43}, {1101, 44}, {1102, 48}, {1103, 45},
|
|
{1104, 47}, {1105, 49}, {1106, 50}, {1107, 52}, {1108, 54}, {1109, 56},
|
|
{1110, 57}, {1111, 58}, {1112, 59}, {1113, 60}, {1114, 61}, {1115, 62},
|
|
{1116, 63}, {1117, 64}, {1118, 65}, {1119, 66}, {1120, 67}, {1121, 68},
|
|
{1122, 69}, {1123, 71}, {1124, 72}, {1125, 73}, {1126, 74}
|
|
};
|
|
|
|
} // End of namespace Sword1
|