From 26ee630756ebdd7c96bccede0881a8c8b98e8f2b Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sat, 11 Feb 2006 22:45:04 +0000 Subject: Moved engines to the new engines/ directory svn-id: r20582 --- engines/sky/logic.cpp | 2570 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2570 insertions(+) create mode 100644 engines/sky/logic.cpp (limited to 'engines/sky/logic.cpp') diff --git a/engines/sky/logic.cpp b/engines/sky/logic.cpp new file mode 100644 index 0000000000..9ea560a5bf --- /dev/null +++ b/engines/sky/logic.cpp @@ -0,0 +1,2570 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2003-2006 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. + * + * $URL$ + * $Id$ + * + */ + +#include "common/stdafx.h" +#include "sky/autoroute.h" +#include "sky/compact.h" +#include "sky/control.h" +#include "sky/debug.h" +#include "sky/disk.h" +#include "sky/grid.h" +#include "sky/logic.h" +#include "sky/mouse.h" +#include "sky/music/musicbase.h" +#include "sky/text.h" +#include "sky/screen.h" +#include "sky/sky.h" +#include "sky/sound.h" +#include "sky/struc.h" + +namespace Sky { + +uint32 Logic::_scriptVariables[NUM_SKY_SCRIPTVARS]; + +void Logic::setupLogicTable() { + static const LogicTable logicTable[] = { + &Logic::nop, + &Logic::logicScript, // 1 script processor + &Logic::autoRoute, // 2 Make a route + &Logic::arAnim, // 3 Follow a route + &Logic::arTurn, // 4 Mega turns araound + &Logic::alt, // 5 Set up new get-to script + &Logic::anim, // 6 Follow a sequence + &Logic::turn, // 7 Mega turning + &Logic::cursor, // 8 id tracks the pointer + &Logic::talk, // 9 count down and animate + &Logic::listen, // 10 player waits for talking id + &Logic::stopped, // 11 wait for id to move + &Logic::choose, // 12 wait for player to click + &Logic::frames, // 13 animate just frames + &Logic::pause, // 14 Count down to 0 and go + &Logic::waitSync, // 15 Set to l_script when sync!=0 + &Logic::simpleAnim, // 16 Module anim without x,y's + }; + + _logicTable = logicTable; +} + +Logic::Logic(SkyCompact *skyCompact, Screen *skyScreen, Disk *skyDisk, Text *skyText, MusicBase *skyMusic, Mouse *skyMouse, Sound *skySound) { + _skyCompact = skyCompact; + _skyScreen = skyScreen; + _skyDisk = skyDisk; + _skyText = skyText; + _skyMusic = skyMusic; + _skySound = skySound; + _skyMouse = skyMouse; + _skyGrid = new Grid(_skyDisk, _skyCompact); + _skyAutoRoute = new AutoRoute(_skyGrid, _skyCompact); + + setupLogicTable(); + setupMcodeTable(); + + memset(_objectList, 0, 30 * sizeof(uint32)); + + for (int i = 0; i < ARRAYSIZE(_moduleList); i++) + _moduleList[i] = 0; + _stackPtr = 0; + + _currentSection = 0xFF; //force music & sound reload + initScriptVariables(); +} + +Logic::~Logic(void) { + delete _skyGrid; + delete _skyAutoRoute; + + for (int i = 0; i < ARRAYSIZE(_moduleList); i++) + if (_moduleList[i]) + free(_moduleList[i]); +} + +void Logic::initScreen0(void) { + fnEnterSection(0, 0, 0); + _skyMusic->startMusic(2); + SkyEngine::_systemVars.currentMusic = 2; +} + +void Logic::parseSaveData(uint32 *data) { + if (!SkyEngine::isDemo()) + fnLeaveSection(_scriptVariables[CUR_SECTION], 0, 0); + for (uint16 cnt = 0; cnt < NUM_SKY_SCRIPTVARS; cnt++) + _scriptVariables[cnt] = READ_LE_UINT32(data++); + fnEnterSection(_scriptVariables[CUR_SECTION], 0, 0); +} + +bool Logic::checkProtection(void) { + if (_scriptVariables[ENTER_DIGITS]) { + if (_scriptVariables[CONSOLE_TYPE] == 5) // reactor code + _scriptVariables[FS_COMMAND] = 240; + else // copy protection + _scriptVariables[FS_COMMAND] = 337; + _scriptVariables[ENTER_DIGITS] = 0; + return true; + } else + return false; +} + +void Logic::engine() { + do { + uint16 *logicList = (uint16 *)_skyCompact->fetchCpt(_scriptVariables[LOGIC_LIST_NO]); + + while (uint16 id = *logicList++) { // 0 means end of list + if (id == 0xffff) { + // Change logic data address + logicList = (uint16 *)_skyCompact->fetchCpt(*logicList); + continue; + } + + _scriptVariables[CUR_ID] = id; + _compact = _skyCompact->fetchCpt(id); + + // check the id actually wishes to be processed + if (!(_compact->status & (1 << 6))) + continue; + + // ok, here we process the logic bit system + + if (_compact->status & (1 << 7)) + _skyGrid->removeObjectFromWalk(_compact); + + Debug::logic(_compact->logic); + (this->*_logicTable[_compact->logic]) (); + + if (_compact->status & (1 << 7)) + _skyGrid->objectToWalk(_compact); + + // a sync sent to the compact is available for one cycle + // only. that cycle has just ended so remove the sync. + // presumably the mega has just reacted to it. + _compact->sync = 0; + } + // usually this loop is run only once, it'll only be run a second time if the game + // script just asked the user to enter a copy protection code. + // this is done to prevent the copy protection screen from flashing up. + // (otherwise it would be visible for 1/50 second) + } while (checkProtection()); +} + +void Logic::nop() {} + +/** + * This function is basicly a wrapper around the real script engine. It runs + * the script engine until a script has finished. + * @see script() + */ +void Logic::logicScript() { + /// Process the current mega's script + /// If the script finishes then drop back a level + + for (;;) { + uint16 mode = _compact->mode; // get pointer to current script + uint16 *scriptNo = SkyCompact::getSub(_compact, mode); + uint16 *offset = SkyCompact::getSub(_compact, mode + 2); + + *offset = script(*scriptNo, *offset); + + if (!*offset) // script finished + _compact->mode -= 4; + else if (_compact->mode == mode) + return; + } +} + +void Logic::autoRoute() { + + _compact->downFlag = _skyAutoRoute->autoRoute(_compact); + if ((_compact->downFlag == 2) && _skyCompact->cptIsId(_compact, CPT_JOEY) && + (_compact->mode == 0) && (_compact->baseSub == JOEY_OUT_OF_LIFT)) { + // workaround for script bug #1064113. Details unclear... + _compact->downFlag = 0; + } + if (_compact->downFlag != 1) { // route ok + _compact->grafixProgId = _compact->animScratchId; + _compact->grafixProgPos = 0; + } + + _compact->logic = L_SCRIPT; // continue the script + + logicScript(); + return; +} + +void Logic::arAnim() { + /// Follow a route + /// Mega should be in getToMode + + // only check collisions on character boundaries + if ((_compact->xcood & 7) || (_compact->ycood & 7)) { + mainAnim(); + return; + } + + // On character boundary. Have we been told to wait? + // if not - are WE colliding? + + if (_compact->waitingFor == 0xffff) { // 1st cycle of re-route does + mainAnim(); + return; + } + + if (_compact->waitingFor) { + // ok, we've been told we've hit someone + // we will wait until we are no longer colliding + // with them. here we check to see if we are (still) colliding. + // if we are then run the stop script. if not clear the flag + // and continue. + + // remember - this could be the first ar cycle for some time, + // we might have been told to wait months ago. if we are + // waiting for one person then another hits us then + // c_waiting_for will be replaced by the new mega - this is + // fine because the later collision will almost certainly + // take longer to clear than the earlier one. + + if (collide(_skyCompact->fetchCpt(_compact->waitingFor))) { + stopAndWait(); + return; + } + + // we are not in fact hitting this person so clr & continue + // it must have registered some time ago + + _compact->waitingFor = 0; // clear id flag + } + + // ok, our turn to check for collisions + + uint16 *logicList = (uint16 *)_skyCompact->fetchCpt(_scriptVariables[LOGIC_LIST_NO]); + Compact *cpt = 0; + + while (uint16 id = *logicList++) { // get an id + + if (id == 0xffff) { // address change? + logicList = (uint16 *)_skyCompact->fetchCpt(*logicList); // get new logic list + continue; + } + + if (id == (uint16)(_scriptVariables[CUR_ID] & 0xffff)) // is it us? + continue; + + _scriptVariables[HIT_ID] = id; // save target id for any possible c_mini_bump + cpt = _skyCompact->fetchCpt(id); // let's have a closer look + + if (!(cpt->status & (1 << ST_COLLISION_BIT))) // can it collide? + continue; + + if (cpt->screen != _compact->screen) // is it on our screen? + continue; + + if (collide(cpt)) { // check for a hit + // ok, we've hit a mega + // is it moving... or something else? + + if (cpt->logic != L_AR_ANIM) { // check for following route + // it is doing something else + // we restart our get-to script + // first tell it to wait for us - in case it starts moving + // ( *it may have already hit us and stopped to wait ) + + _compact->waitingFor = 0xffff; // effect 1 cycle collision skip + // tell it it is waiting for us + cpt->waitingFor = (uint16)(_scriptVariables[CUR_ID] & 0xffff); + // restart current script + *SkyCompact::getSub(_compact, _compact->mode + 2) = 0; + _compact->logic = L_SCRIPT; + logicScript(); + return; + } + + script(_compact->miniBump, 0); + return; + } + } + + // ok, there was no collisions + // now check for interaction request + // *note: the interaction is always set up as an action script + + if (_compact->request) { + _compact->mode = C_ACTION_MODE; // put into action mode + _compact->actionSub = _compact->request; + _compact->actionSub_off = 0; + _compact->request = 0; // trash request + _compact->logic = L_SCRIPT; + logicScript(); + return; + } + + // any flag? - or any change? + // if change then re-run the current script, which must be + // a position independent get-to ---- + + if (!_compact->atWatch) { // any flag set? + mainAnim(); + return; + } + + // ok, there is an at watch - see if it's changed + + if (_compact->atWas == _scriptVariables[_compact->atWatch/4]) { // still the same? + mainAnim(); + return; + } + + // changed so restart the current script + // *not suitable for base initiated ARing + *SkyCompact::getSub(_compact, _compact->mode + 2) = 0; + + _compact->logic = L_SCRIPT; + logicScript(); +} + +void Logic::mainAnim() { + /// Extension of arAnim() + _compact->waitingFor = 0; // clear possible zero-zero skip + + uint16 *sequence = _skyCompact->getGrafixPtr(_compact); + if (!*sequence) { + // ok, move to new anim segment + sequence += 2; + _compact->grafixProgPos += 2; + if (!*sequence) { // end of route? + // ok, sequence has finished + + // will start afresh if new sequence continues in last direction + _compact->arAnimIndex = 0; + + _compact->downFlag = 0; // pass back ok to script + _compact->logic = L_SCRIPT; + logicScript(); + return; + } + + _compact->arAnimIndex = 0; // reset position + } + + uint16 dir; + while ((dir = _compact->dir) != *(sequence + 1)) { + // ok, setup turning + _compact->dir = *(sequence + 1); + + uint16 *tt = _skyCompact->getTurnTable(_compact, dir); + if (tt[_compact->dir]) { + _compact->turnProgId = tt[_compact->dir]; + _compact->turnProgPos = 0; + _compact->logic = L_AR_TURNING; + arTurn(); + return; + } + }; + + uint16 animId = *(uint16*)_skyCompact->getCompactElem(_compact, C_ANIM_UP + _compact->megaSet + dir * 4); + uint16 *animList = (uint16*)_skyCompact->fetchCpt(animId); + + uint16 arAnimIndex = _compact->arAnimIndex; + if (!animList[arAnimIndex / 2]) { + arAnimIndex = 0; + _compact->arAnimIndex = 0; // reset + } + + _compact->arAnimIndex += S_LENGTH; + + *sequence -= animList[(S_COUNT + arAnimIndex)/2]; // reduce the distance to travel + _compact->frame = animList[(S_FRAME + arAnimIndex)/2]; // new graphic frame + _compact->xcood += animList[(S_AR_X + arAnimIndex)/2]; // update x coordinate + _compact->ycood += animList[(S_AR_Y + arAnimIndex)/2]; // update y coordinate +} + +void Logic::arTurn() { + uint16 *turnData = (uint16*)_skyCompact->fetchCpt(_compact->turnProgId) + _compact->turnProgPos; + _compact->frame = *turnData++; + _compact->turnProgPos++; + + if (!*turnData) { // turn done? + // Back to ar mode + _compact->arAnimIndex = 0; + _compact->logic = L_AR_ANIM; + } +} + +void Logic::alt() { + /// change the current script + _compact->logic = L_SCRIPT; + *SkyCompact::getSub(_compact, _compact->mode) = _compact->alt; + *SkyCompact::getSub(_compact, _compact->mode + 2) = 0; + logicScript(); +} + +void Logic::anim() { + /// Follow an animation sequence + uint16 *grafixProg = _skyCompact->getGrafixPtr(_compact); + + while (*grafixProg) { + _compact->grafixProgPos += 3; // all types are 3 words. + if (*grafixProg == LF_START_FX) { // do fx + grafixProg++; + uint16 sound = *grafixProg++; + uint16 volume = *grafixProg++; + + // channel 0 + fnStartFx(sound, 0, volume); + } else if (*grafixProg >= LF_START_FX) { // do sync + grafixProg++; + + Compact *cpt = _skyCompact->fetchCpt(*grafixProg++); + + cpt->sync = *grafixProg++; + } else { // put coordinates and frame in + _compact->xcood = *grafixProg++; + _compact->ycood = *grafixProg++; + + _compact->frame = *grafixProg++ | _compact->offset; + return; + } + } + + _compact->downFlag = 0; + _compact->logic = L_SCRIPT; + logicScript(); +} + +void Logic::turn() { + uint16 *turnData = (uint16*)_skyCompact->fetchCpt(_compact->turnProgId) + _compact->turnProgPos; + if (*turnData) { + _compact->frame = *turnData; + _compact->turnProgPos++; + return; + } + + // turn_to_script: + _compact->arAnimIndex = 0; + _compact->logic = L_SCRIPT; + + logicScript(); +} + +void Logic::cursor() { + _skyText->logicCursor(_compact, _skyMouse->giveMouseX(), _skyMouse->giveMouseY()); +} + +static uint16 clickTable[46] = { + ID_FOSTER, + ID_JOEY, + ID_JOBS, + ID_LAMB, + ID_ANITA, + ID_SON, + ID_DAD, + ID_MONITOR, + ID_SHADES, + MINI_SS, + FULL_SS, + ID_FOREMAN, + ID_RADMAN, + ID_GALLAGER_BEL, + ID_BURKE, + ID_BODY, + ID_HOLO, + ID_TREVOR, + ID_ANCHOR, + ID_WRECK_GUARD, + ID_SKORL_GUARD, + + // BASE LEVEL + ID_SC30_HENRI, + ID_SC31_GUARD, + ID_SC32_VINCENT, + ID_SC32_GARDENER, + ID_SC32_BUZZER, + ID_SC36_BABS, + ID_SC36_BARMAN, + ID_SC36_COLSTON, + ID_SC36_GALLAGHER, + ID_SC36_JUKEBOX, + ID_DANIELLE, + ID_SC42_JUDGE, + ID_SC42_CLERK, + ID_SC42_PROSECUTION, + ID_SC42_JOBSWORTH, + + // UNDERWORLD + ID_MEDI, + ID_WITNESS, + ID_GALLAGHER, + ID_KEN, + ID_SC76_ANDROID_2, + ID_SC76_ANDROID_3, + ID_SC81_FATHER, + ID_SC82_JOBSWORTH, + + // LINC WORLD + ID_HOLOGRAM_B, + 12289 +}; + +void Logic::talk() { + // first count through the frames + // just frames - nothing tweeky + // the speech finishes when the timer runs out & + // not when the animation finishes + // this routine is very task specific + + // TODO: Check for mouse clicking + + // Are we allowed to click + + if (_skyMouse->wasClicked()) + for (int i = 0; i < ARRAYSIZE(clickTable); i++) + if (clickTable[i] == (uint16)_scriptVariables[CUR_ID]) { + if ((SkyEngine::_systemVars.systemFlags & SF_ALLOW_SPEECH) && (!_skySound->speechFinished())) + _skySound->stopSpeech(); + if ((_compact->spTextId > 0) && + (_compact->spTextId < 0xFFFF)) { + + _skyCompact->fetchCpt(_compact->spTextId)->status = 0; + } + if (_skyCompact->getGrafixPtr(_compact)) { + _compact->frame = _compact->getToFlag; // set character to stand + _compact->grafixProgId = 0; + } + + _compact->logic = L_SCRIPT; + logicScript(); + return; + } + + // If speech is allowed then check for it to finish before finishing animations + + if ((_compact->spTextId == 0xFFFF) && // is this a voc file? + (_skySound->speechFinished())) { // finished? + + _compact->logic = L_SCRIPT; // restart character control + + if (_skyCompact->getGrafixPtr(_compact)) { + _compact->frame = _compact->getToFlag; // set character to stand + _compact->grafixProgId = 0; + } + + logicScript(); + return; + } + + uint16 *graphixProg = _skyCompact->getGrafixPtr(_compact); + if (graphixProg) { + if ((*graphixProg) && ((_compact->spTime != 3) || (!_skySound->speechFinished()))) { + // we will force the animation to finish 3 game cycles + // before the speech actually finishes - because it looks good. + + _compact->frame = *(graphixProg + 2) + _compact->offset; + graphixProg += 3; + _compact->grafixProgPos += 3; + } else { + // we ran out of frames or finished speech, let actor stand still. + _compact->frame = _compact->getToFlag; + _compact->grafixProgId = 0; + } + } + + if (_skySound->speechFinished()) _compact->spTime--; + + if (_compact->spTime == 0) { + + // ok, speech has finished + + if (_compact->spTextId) { + Compact *cpt = _skyCompact->fetchCpt(_compact->spTextId); // get text id to kill + cpt->status = 0; // kill the text + } + + _compact->logic = L_SCRIPT; + logicScript(); + } +} + +void Logic::listen() { + /// Stay in this mode until id in getToFlag leaves L_TALK mode + + Compact *cpt = _skyCompact->fetchCpt(_compact->flag); + + if (cpt->logic == L_TALK) + return; + + _compact->logic = L_SCRIPT; + logicScript(); +} + +void Logic::stopped() { + /// waiting for another mega to move or give-up trying + /// + /// this mode will always be set up from a special script + /// that will be one level higher than the script we + /// would wish to restart from + + Compact *cpt = _skyCompact->fetchCpt(_compact->waitingFor); + + if (cpt) + if (!cpt->mood && collide(cpt)) + return; + + // we are free, continue processing the script + + // restart script one level below + *SkyCompact::getSub(_compact, _compact->mode - 2) = 0; + _compact->waitingFor = 0xffff; + + _compact->logic = L_SCRIPT; + logicScript(); +} + +void Logic::choose() { + // Remain in this mode until player selects some text + if (!_scriptVariables[THE_CHOSEN_ONE]) + return; + + fnNoHuman(0, 0, 0); // kill mouse again + + SkyEngine::_systemVars.systemFlags &= ~SF_CHOOSING; // restore save/restore + + _compact->logic = L_SCRIPT; // and continue script + logicScript(); +} + +void Logic::frames() { + if (!_compact->sync) + simpleAnim(); + else { + _compact->downFlag = 0; // return 'ok' to script + _compact->logic = L_SCRIPT; + logicScript(); + } +} + +void Logic::pause() { + if (--_compact->flag) + return; + + _compact->logic = L_SCRIPT; + logicScript(); + return; +} + +void Logic::waitSync() { + /// checks c_sync, when its non 0 + /// the id is put back into script mode + // use this instead of loops in the script + + if (!_compact->sync) + return; + + _compact->logic = L_SCRIPT; + logicScript(); +} + +void Logic::simpleAnim() { + /// follow an animation sequence module whilst ignoring the coordinate data + + uint16 *grafixProg = _skyCompact->getGrafixPtr(_compact); + + // *grafix_prog: command + while (*grafixProg) { + _compact->grafixProgPos += 3; + if (*grafixProg != SEND_SYNC) { + grafixProg++; + grafixProg++; // skip coordinates + + // *grafix_prog: frame + if (*grafixProg >= 64) + _compact->frame = *grafixProg; + else + _compact->frame = *grafixProg + _compact->offset; + + return; + } + + grafixProg++; + // *grafix_prog: id to sync + Compact *compact2 = _skyCompact->fetchCpt(*grafixProg); + grafixProg++; + + // *grafix_prog: sync + compact2->sync = *grafixProg; + grafixProg++; + } + + _compact->downFlag = 0; // return 'ok' to script + _compact->logic = L_SCRIPT; + logicScript(); +} + +bool Logic::collide(Compact *cpt) { + MegaSet *m1 = SkyCompact::getMegaSet(_compact); + MegaSet *m2 = SkyCompact::getMegaSet(cpt); + + // target's base coordinates + uint16 x = cpt->xcood & 0xfff8; + uint16 y = cpt->ycood & 0xfff8; + + // The collision is direction dependent + switch (_compact->dir) { + case 0: // looking up + x -= m1->colOffset; // compensate for inner x offsets + x += m2->colOffset; + + if ((x + m2->colWidth) < _compact->xcood) // their rightmost + return false; + + x -= m1->colWidth; // our left, their right + if (x >= _compact->xcood) + return false; + + y += 8; // bring them down a line + if (y == _compact->ycood) + return true; + + y += 8; // bring them down a line + if (y == _compact->ycood) + return true; + + return false; + case 1: // looking down + x -= m1->colOffset; // compensate for inner x offsets + x += m2->colOffset; + + if ((x + m2->colWidth) < _compact->xcood) // their rightmoast + return false; + + x -= m1->colWidth; // our left, their right + if (x >= _compact->xcood) + return false; + + y -= 8; // bring them up a line + if (y == _compact->ycood) + return true; + + y -= 8; // bring them up a line + if (y == _compact->ycood) + return true; + + return false; + case 2: // looking left + + if (y != _compact->ycood) + return false; + + x += m2->lastChr; + if (x == _compact->xcood) + return true; + + x -= 8; // out another one + if (x == _compact->xcood) + return true; + + return false; + case 3: // looking right + case 4: // talking (not sure if this makes sense...) + + if (y != _compact->ycood) + return false; + + x -= m1->lastChr; // last block + if (x == _compact->xcood) + return true; + + x -= 8; // out another block + if (x != _compact->xcood) + return false; + + return true; + default: + error("Unknown Direction: %d", _compact->dir); + } +} + +void Logic::runGetOff() { + uint32 getOff = _scriptVariables[GET_OFF]; + _scriptVariables[GET_OFF] = 0; + if (getOff) + script((uint16)(getOff & 0xffff), (uint16)(getOff >> 16)); +} + +void Logic::stopAndWait() { + _compact->mode += 4; + + uint16 *scriptNo = SkyCompact::getSub(_compact, _compact->mode); + uint16 *offset = SkyCompact::getSub(_compact, _compact->mode + 2); + + *scriptNo = _compact->stopScript; + *offset = 0; + + _compact->logic = L_SCRIPT; + logicScript(); +} + +void Logic::checkModuleLoaded(uint16 moduleNo) { + if (!_moduleList[moduleNo]) + _moduleList[moduleNo] = (uint16 *)_skyDisk->loadFile((uint16)moduleNo + F_MODULE_0); +} + +void Logic::push(uint32 a) { + if (_stackPtr > ARRAYSIZE(_stack) - 2) + error("Stack overflow"); + _stack[_stackPtr++] = a; +} + +uint32 Logic::pop() { + if (_stackPtr < 1 || _stackPtr > ARRAYSIZE(_stack) - 1) + error("No items on Stack to pop"); + return _stack[--_stackPtr]; +} + +void Logic::setupMcodeTable() { + static const McodeTable mcodeTable[] = { + &Logic::fnCacheChip, + &Logic::fnCacheFast, + &Logic::fnDrawScreen, + &Logic::fnAr, + &Logic::fnArAnimate, + &Logic::fnIdle, + &Logic::fnInteract, + &Logic::fnStartSub, + &Logic::fnTheyStartSub, + &Logic::fnAssignBase, + &Logic::fnDiskMouse, + &Logic::fnNormalMouse, + &Logic::fnBlankMouse, + &Logic::fnCrossMouse, + &Logic::fnCursorRight, + &Logic::fnCursorLeft, + &Logic::fnCursorDown, + &Logic::fnOpenHand, + &Logic::fnCloseHand, + &Logic::fnGetTo, + &Logic::fnSetToStand, + &Logic::fnTurnTo, + &Logic::fnArrived, + &Logic::fnLeaving, + &Logic::fnSetAlternate, + &Logic::fnAltSetAlternate, + &Logic::fnKillId, + &Logic::fnNoHuman, + &Logic::fnAddHuman, + &Logic::fnAddButtons, + &Logic::fnNoButtons, + &Logic::fnSetStop, + &Logic::fnClearStop, + &Logic::fnPointerText, + &Logic::fnQuit, + &Logic::fnSpeakMe, + &Logic::fnSpeakMeDir, + &Logic::fnSpeakWait, + &Logic::fnSpeakWaitDir, + &Logic::fnChooser, + &Logic::fnHighlight, + &Logic::fnTextKill, + &Logic::fnStopMode, + &Logic::fnWeWait, + &Logic::fnSendSync, + &Logic::fnSendFastSync, + &Logic::fnSendRequest, + &Logic::fnClearRequest, + &Logic::fnCheckRequest, + &Logic::fnStartMenu, + &Logic::fnUnhighlight, + &Logic::fnFaceId, + &Logic::fnForeground, + &Logic::fnBackground, + &Logic::fnNewBackground, + &Logic::fnSort, + &Logic::fnNoSpriteEngine, + &Logic::fnNoSpritesA6, + &Logic::fnResetId, + &Logic::fnToggleGrid, + &Logic::fnPause, + &Logic::fnRunAnimMod, + &Logic::fnSimpleMod, + &Logic::fnRunFrames, + &Logic::fnAwaitSync, + &Logic::fnIncMegaSet, + &Logic::fnDecMegaSet, + &Logic::fnSetMegaSet, + &Logic::fnMoveItems, + &Logic::fnNewList, + &Logic::fnAskThis, + &Logic::fnRandom, + &Logic::fnPersonHere, + &Logic::fnToggleMouse, + &Logic::fnMouseOn, + &Logic::fnMouseOff, + &Logic::fnFetchX, + &Logic::fnFetchY, + &Logic::fnTestList, + &Logic::fnFetchPlace, + &Logic::fnCustomJoey, + &Logic::fnSetPalette, + &Logic::fnTextModule, + &Logic::fnChangeName, + &Logic::fnMiniLoad, + &Logic::fnFlushBuffers, + &Logic::fnFlushChip, + &Logic::fnSaveCoods, + &Logic::fnPlotGrid, + &Logic::fnRemoveGrid, + &Logic::fnEyeball, + &Logic::fnCursorUp, + &Logic::fnLeaveSection, + &Logic::fnEnterSection, + &Logic::fnRestoreGame, + &Logic::fnRestartGame, + &Logic::fnNewSwingSeq, + &Logic::fnWaitSwingEnd, + &Logic::fnSkipIntroCode, + &Logic::fnBlankScreen, + &Logic::fnPrintCredit, + &Logic::fnLookAt, + &Logic::fnLincTextModule, + &Logic::fnTextKill2, + &Logic::fnSetFont, + &Logic::fnStartFx, + &Logic::fnStopFx, + &Logic::fnStartMusic, + &Logic::fnStopMusic, + &Logic::fnFadeDown, + &Logic::fnFadeUp, + &Logic::fnQuitToDos, + &Logic::fnPauseFx, + &Logic::fnUnPauseFx, + &Logic::fnPrintf + }; + + _mcodeTable = mcodeTable; +} + +static const uint32 forwardList1b[] = { + JOBS_SPEECH, + JOBS_S4, + JOBS_ALARMED, + JOEY_RECYCLE, + SHOUT_SSS, + JOEY_MISSION, + TRANS_MISSION, + SLOT_MISSION, + CORNER_MISSION, + JOEY_LOGIC, + GORDON_SPEECH, + JOEY_BUTTON_MISSION, + LOB_DAD_SPEECH, + LOB_SON_SPEECH, + GUARD_SPEECH, + MANTRACH_SPEECH, + WRECK_SPEECH, + ANITA_SPEECH, + LAMB_FACTORY, + FORE_SPEECH, + JOEY_42_MISS, + JOEY_JUNCTION_MISS, + WELDER_MISSION, + JOEY_WELD_MISSION, + RADMAN_SPEECH, + LINK_7_29, + LINK_29_7, + LAMB_TO_3, + LAMB_TO_2, + BURKE_SPEECH, + BURKE_1, + BURKE_2, + DR_BURKE_1, + JASON_SPEECH, + JOEY_BELLEVUE, + ANCHOR_SPEECH, + ANCHOR_MISSION, + JOEY_PC_MISSION, + HOOK_MISSION, + TREVOR_SPEECH, + JOEY_FACTORY, + HELGA_SPEECH, + JOEY_HELGA_MISSION, + GALL_BELLEVUE, + GLASS_MISSION, + LAMB_FACT_RETURN, + LAMB_LEAVE_GARDEN, + LAMB_START_29, + LAMB_BELLEVUE, + CABLE_MISSION, + FOSTER_TOUR, + LAMB_TOUR, + FOREMAN_LOGIC, + LAMB_LEAVE_FACTORY, + LAMB_BELL_LOGIC, + LAMB_FACT_2, + START90, + 0, + 0, + LINK_28_31, + LINK_31_28, + EXIT_LINC, + DEATH_SCRIPT +}; + +static uint32 forwardList1b288[] = { + JOBS_SPEECH, + JOBS_S4, + JOBS_ALARMED, + JOEY_RECYCLE, + SHOUT_SSS, + JOEY_MISSION, + TRANS_MISSION, + SLOT_MISSION, + CORNER_MISSION, + JOEY_LOGIC, + GORDON_SPEECH, + JOEY_BUTTON_MISSION, + LOB_DAD_SPEECH, + LOB_SON_SPEECH, + GUARD_SPEECH, + 0x68, + WRECK_SPEECH, + ANITA_SPEECH, + LAMB_FACTORY, + FORE_SPEECH, + JOEY_42_MISS, + JOEY_JUNCTION_MISS, + WELDER_MISSION, + JOEY_WELD_MISSION, + RADMAN_SPEECH, + LINK_7_29, + LINK_29_7, + LAMB_TO_3, + LAMB_TO_2, + 0x3147, + 0x3100, + 0x3101, + 0x3102, + 0x3148, + 0x3149, + 0x314A, + 0x30C5, + 0x30C6, + 0x30CB, + 0x314B, + JOEY_FACTORY, + 0x314C, + 0x30E2, + 0x314D, + 0x310C, + LAMB_FACT_RETURN, + 0x3139, + 0x313A, + 0x004F, + CABLE_MISSION, + FOSTER_TOUR, + LAMB_TOUR, + FOREMAN_LOGIC, + LAMB_LEAVE_FACTORY, + 0x3138, + LAMB_FACT_2, + 0x004D, + 0, + 0, + LINK_28_31, + LINK_31_28, + 0x004E, + DEATH_SCRIPT +}; + +static const uint32 forwardList2b[] = { + STD_ON, + STD_EXIT_LEFT_ON, + STD_EXIT_RIGHT_ON, + ADVISOR_188, + SHOUT_ACTION, + MEGA_CLICK, + MEGA_ACTION +}; + +static const uint32 forwardList3b[] = { + DANI_SPEECH, + DANIELLE_GO_HOME, + SPUNKY_GO_HOME, + HENRI_SPEECH, + BUZZER_SPEECH, + FOSTER_VISIT_DANI, + DANIELLE_LOGIC, + JUKEBOX_SPEECH, + VINCENT_SPEECH, + EDDIE_SPEECH, + BLUNT_SPEECH, + DANI_ANSWER_PHONE, + SPUNKY_SEE_VIDEO, + SPUNKY_BARK_AT_FOSTER, + SPUNKY_SMELLS_FOOD, + BARRY_SPEECH, + COLSTON_SPEECH, + GALL_SPEECH, + BABS_SPEECH, + CHUTNEY_SPEECH, + FOSTER_ENTER_COURT +}; + +static const uint32 forwardList4b[] = { + WALTER_SPEECH, + JOEY_MEDIC, + JOEY_MED_LOGIC, + JOEY_MED_MISSION72, + KEN_LOGIC, + KEN_SPEECH, + KEN_MISSION_HAND, + SC70_IRIS_OPENED, + SC70_IRIS_CLOSED, + FOSTER_ENTER_BOARDROOM, + BORED_ROOM, + FOSTER_ENTER_NEW_BOARDROOM, + HOBS_END, + SC82_JOBS_SSS +}; + +static const uint32 forwardList5b[] = { + SET_UP_INFO_WINDOW, + SLAB_ON, + UP_MOUSE, + DOWN_MOUSE, + LEFT_MOUSE, + RIGHT_MOUSE, + DISCONNECT_FOSTER +}; + +void Logic::fnExec(uint16 num, uint32 a, uint32 b, uint32 c) { + (this->*_mcodeTable[num])(a, b, c); +} + +void Logic::initScriptVariables() { + for (int i = 0; i < ARRAYSIZE(_scriptVariables); i++) + _scriptVariables[i] = 0; + + _scriptVariables[LOGIC_LIST_NO] = 141; + _scriptVariables[LAMB_GREET] = 62; + _scriptVariables[JOEY_SECTION] = 1; + _scriptVariables[LAMB_SECTION] = 2; + _scriptVariables[S15_FLOOR] = 8371; + _scriptVariables[GUARDIAN_THERE] = 1; + _scriptVariables[DOOR_67_68_FLAG] = 1; + _scriptVariables[SC70_IRIS_FLAG] = 3; + _scriptVariables[DOOR_73_75_FLAG] = 1; + _scriptVariables[SC76_CABINET1_FLAG] = 1; + _scriptVariables[SC76_CABINET2_FLAG] = 1; + _scriptVariables[SC76_CABINET3_FLAG] = 1; + _scriptVariables[DOOR_77_78_FLAG] = 1; + _scriptVariables[SC80_EXIT_FLAG] = 1; + _scriptVariables[SC31_LIFT_FLAG] = 1; + _scriptVariables[SC32_LIFT_FLAG] = 1; + _scriptVariables[SC33_SHED_DOOR_FLAG] = 1; + _scriptVariables[BAND_PLAYING] = 1; + _scriptVariables[COLSTON_AT_TABLE] = 1; + _scriptVariables[SC36_NEXT_DEALER] = 16731; + _scriptVariables[SC36_DOOR_FLAG] = 1; + _scriptVariables[SC37_DOOR_FLAG] = 2; + _scriptVariables[SC40_LOCKER_1_FLAG] = 1; + _scriptVariables[SC40_LOCKER_2_FLAG] = 1; + _scriptVariables[SC40_LOCKER_3_FLAG] = 1; + _scriptVariables[SC40_LOCKER_4_FLAG] = 1; + _scriptVariables[SC40_LOCKER_5_FLAG] = 1; + + if (SkyEngine::_systemVars.gameVersion == 288) + memcpy(_scriptVariables + 352, forwardList1b288, sizeof(forwardList1b288)); + else + memcpy(_scriptVariables + 352, forwardList1b, sizeof(forwardList1b)); + + memcpy(_scriptVariables + 656, forwardList2b, sizeof(forwardList2b)); + memcpy(_scriptVariables + 721, forwardList3b, sizeof(forwardList3b)); + memcpy(_scriptVariables + 663, forwardList4b, sizeof(forwardList4b)); + memcpy(_scriptVariables + 505, forwardList5b, sizeof(forwardList5b)); +} + +uint16 Logic::mouseScript(uint32 scrNum, Compact *scriptComp) { + + Compact *tmpComp = _compact; + _compact = scriptComp; + uint16 retVal = script((uint16)(scrNum & 0xFFFF), (uint16)(scrNum >> 16)); + _compact = tmpComp; + return retVal; +} + +/** + * This is the actual script engine. It interprets script \a scriptNo starting at \a offset + * + * @param scriptNo The script to interpret. + * @arg Bits 0-11 - Script number + * @arg Bits 12-15 - Module number + * @param offset At which offset to start interpreting the script. + * + * @return 0 if script finished. Else offset where to continue. + */ +uint16 Logic::script(uint16 scriptNo, uint16 offset) { +script: + /// process a script + /// low level interface to interpreter + + uint16 moduleNo = scriptNo >> 12; + debug(3, "Doing Script %x", (offset << 16) | scriptNo); + uint16 *scriptData = _moduleList[moduleNo]; // get module address + + if (!scriptData) { // We need to load the script module + _moduleList[moduleNo] = _skyDisk->loadScriptFile(moduleNo + F_MODULE_0); + scriptData = _moduleList[moduleNo]; // module has been loaded + } + + uint16 *moduleStart = scriptData; + + // Check whether we have an offset or what + if (offset) + scriptData = moduleStart + offset; + else + scriptData += scriptData[scriptNo & 0x0fff]; + + uint32 a = 0, b = 0, c = 0; + uint16 command, s; + + for (;;) { + command = *scriptData++; // get a command + Debug::script(command, scriptData); + + switch (command) { + case 0: // push_variable + push( _scriptVariables[*scriptData++ / 4] ); + break; + case 1: // less_than + a = pop(); + b = pop(); + if (a > b) + push(1); + else + push(0); + break; + case 2: // push_number + push(*scriptData++); + break; + case 3: // not_equal + a = pop(); + b = pop(); + if (a != b) + push(1); + else + push(0); + break; + case 4: // if_and + a = pop(); + b = pop(); + if (a && b) + push(1); + else + push(0); + break; + case 5: // skip_zero + s = *scriptData++; + + a = pop(); + if (!a) + scriptData += s / 2; + break; + case 6: // pop_var + b = _scriptVariables[*scriptData++ / 4] = pop(); + break; + case 7: // minus + a = pop(); + b = pop(); + push(b-a); + break; + case 8: // plus + a = pop(); + b = pop(); + push(b+a); + break; + case 9: // skip_always + s = *scriptData++; + scriptData += s / 2; + break; + case 10: // if_or + a = pop(); + b = pop(); + if (a || b) + push(1); + else + push(0); + break; + case 11: // call_mcode + { + a = *scriptData++; + assert(a <= 3); + // No, I did not forget the "break"s + switch (a) { + case 3: + c = pop(); + case 2: + b = pop(); + case 1: + a = pop(); + } + + uint16 mcode = *scriptData++ / 4; // get mcode number + Debug::mcode(mcode, a, b, c); + + Compact *saveCpt = _compact; + bool ret = (this->*_mcodeTable[mcode]) (a, b, c); + _compact = saveCpt; + + if (!ret) + return (scriptData - moduleStart); + } + break; + case 12: // more_than + a = pop(); + b = pop(); + if (a < b) + push(1); + else + push(0); + break; + case 14: // switch + c = s = *scriptData++; // get number of cases + + a = pop(); // and value to switch on + + do { + if (a == *scriptData) { + scriptData += scriptData[1] / 2; + scriptData++; + break; + } + scriptData += 2; + } while (--s); + + if (s == 0) + scriptData += *scriptData / 2; // use the default + break; + case 15: // push_offset + push( *(uint16 *)_skyCompact->getCompactElem(_compact, *scriptData++) ); + break; + case 16: // pop_offset + // pop a value into a compact + *(uint16 *)_skyCompact->getCompactElem(_compact, *scriptData++) = (uint16)pop(); + break; + case 17: // is_equal + a = pop(); + b = pop(); + if (a == b) + push(1); + else + push(0); + break; + case 18: { // skip_nz + int16 t = *scriptData++; + a = pop(); + if (a) + scriptData += t / 2; + break; + } + case 13: + case 19: // script_exit + return 0; + case 20: // restart_script + offset = 0; + goto script; + default: + error("Unknown script command: %d", command); + } + } +} + +bool Logic::fnCacheChip(uint32 a, uint32 b, uint32 c) { + _skySound->fnStopFx(); + _skyDisk->fnCacheChip((uint16*)_skyCompact->fetchCpt((uint16)a)); + return true; +} + +bool Logic::fnCacheFast(uint32 a, uint32 b, uint32 c) { + _skyDisk->fnCacheFast((uint16*)_skyCompact->fetchCpt((uint16)a)); + return true; +} + +bool Logic::fnDrawScreen(uint32 a, uint32 b, uint32 c) { + debug(5, "Call: fnDrawScreen(%X, %X)",a,b); + SkyEngine::_systemVars.currentPalette = a; + _skyScreen->fnDrawScreen(a, b); + + if (Logic::_scriptVariables[SCREEN] == 32) { + /* workaround for script bug #786482 + Under certain circumstances, which never got completely cleared, + the gardener can get stuck in an animation, waiting for a sync + signal from foster. + This is most probably caused by foster leaving the screen before + sending the sync. + To work around that, we simply send a sync to the gardener every time + we enter the screen. If he isn't stuck (and thus not waiting for sync) + it will be ignored anyways */ + + debug(1, "sending gardener sync"); + fnSendSync(ID_SC32_GARDENER, 1, 0); + } + return true; +} + +bool Logic::fnAr(uint32 x, uint32 y, uint32 c) { + _compact->downFlag = 1; // assume failure in-case logic is interupted by speech (esp Joey) + + _compact->arTargetX = (uint16)x; + _compact->arTargetY = (uint16)y; + _compact->logic = L_AR; // Set to AR mode + + _compact->xcood &= 0xfff8; + _compact->ycood &= 0xfff8; + + return false; // drop out of script +} + +bool Logic::fnArAnimate(uint32 a, uint32 b, uint32 c) { + _compact->mood = 0; // high level 'not stood still' + _compact->logic = L_AR_ANIM; + return false; // drop out of script +} + +bool Logic::fnIdle(uint32 a, uint32 b, uint32 c) { + // set the player idling + _compact->logic = 0; + return true; +} + +bool Logic::fnInteract(uint32 targetId, uint32 b, uint32 c) { + _compact->mode += 4; // next level up + _compact->logic = L_SCRIPT; + Compact *cpt = _skyCompact->fetchCpt(targetId); + + *SkyCompact::getSub(_compact, _compact->mode) = cpt->actionScript; + *SkyCompact::getSub(_compact, _compact->mode + 2) = 0; + + return false; +} + +bool Logic::fnStartSub(uint32 scr, uint32 b, uint32 c) { + _compact->mode += 4; + *SkyCompact::getSub(_compact, _compact->mode) = (uint16)(scr & 0xffff); + *SkyCompact::getSub(_compact, _compact->mode + 2) = (uint16)(scr >> 16); + return false; +} + +bool Logic::fnTheyStartSub(uint32 mega, uint32 scr, uint32 c) { + Compact *cpt = _skyCompact->fetchCpt(mega); + cpt->mode += 4; + *SkyCompact::getSub(cpt, cpt->mode) = (uint16)(scr & 0xffff); + *SkyCompact::getSub(cpt, cpt->mode + 2) = (uint16)(scr >> 16); + return true; +} + +bool Logic::fnAssignBase(uint32 id, uint32 scr, uint32 c) { + Compact *cpt = _skyCompact->fetchCpt(id); + cpt->mode = C_BASE_MODE; + cpt->logic = L_SCRIPT; + cpt->baseSub = (uint16)(scr & 0xffff); + cpt->baseSub_off = (uint16)(scr >> 16); + return true; +} + +bool Logic::fnDiskMouse(uint32 a, uint32 b, uint32 c) { + _skyMouse->spriteMouse(MOUSE_DISK, 11, 11); + return true; +} + +bool Logic::fnNormalMouse(uint32 a, uint32 b, uint32 c) { + _skyMouse->spriteMouse(MOUSE_NORMAL, 0, 0); + return true; +} + +bool Logic::fnBlankMouse(uint32 a, uint32 b, uint32 c) { + _skyMouse->spriteMouse(MOUSE_BLANK, 0, 0); + return true; +} + +bool Logic::fnCrossMouse(uint32 a, uint32 b, uint32 c) { + if (_scriptVariables[OBJECT_HELD]) + _skyMouse->fnOpenCloseHand(false); + else + _skyMouse->spriteMouse(MOUSE_CROSS, 4, 4); + return true; +} + +bool Logic::fnCursorRight(uint32 a, uint32 b, uint32 c) { + _skyMouse->spriteMouse(MOUSE_RIGHT, 9, 4); + return true; +} + +bool Logic::fnCursorLeft(uint32 a, uint32 b, uint32 c) { + _skyMouse->spriteMouse(MOUSE_LEFT, 0, 5); + return true; +} + +bool Logic::fnCursorDown(uint32 a, uint32 b, uint32 c) { + _skyMouse->spriteMouse(MOUSE_DOWN, 9, 4); + return true; +} + +bool Logic::fnCursorUp(uint32 a, uint32 b, uint32 c) { + _skyMouse->spriteMouse(MOUSE_UP, 9, 4); + return true; +} + +bool Logic::fnOpenHand(uint32 a, uint32 b, uint32 c) { + _skyMouse->fnOpenCloseHand(true); + return true; +} + +bool Logic::fnCloseHand(uint32 a, uint32 b, uint32 c) { + _skyMouse->fnOpenCloseHand(false); + return true; +} + +bool Logic::fnGetTo(uint32 targetPlaceId, uint32 mode, uint32 c) { + _compact->upFlag = (uint16)mode; // save mode for action script + _compact->mode += 4; // next level up + Compact *cpt = _skyCompact->fetchCpt(_compact->place); + if (!cpt) { + warning("can't find _compact's getToTable. Place compact is NULL"); + return false; + } + uint16 *getToTable = (uint16*)_skyCompact->fetchCpt(cpt->getToTableId); + if (!getToTable) { + warning("Place compact's getToTable is NULL!"); + return false; + } + + while (*getToTable != targetPlaceId) + getToTable += 2; + + // get new script + *SkyCompact::getSub(_compact, _compact->mode) = *(getToTable + 1); + *SkyCompact::getSub(_compact, _compact->mode + 2) = 0; + + return false; // drop out of script +} + +bool Logic::fnSetToStand(uint32 a, uint32 b, uint32 c) { + _compact->mood = 1; // high level stood still + + _compact->grafixProgId = *(uint16*)_skyCompact->getCompactElem(_compact, C_STAND_UP + _compact->megaSet + _compact->dir * 4); + _compact->grafixProgPos = 0; + + uint16 *standList = _skyCompact->getGrafixPtr(_compact); + + _compact->offset = *standList; // get frames offset + _compact->logic = L_SIMPLE_MOD; + _compact->grafixProgPos++; + simpleAnim(); + return false; // drop out of script +} + +bool Logic::fnTurnTo(uint32 dir, uint32 b, uint32 c) { + /// turn compact to direction dir + + uint16 curDir = _compact->dir; // get current direction + _compact->dir = (uint16)(dir & 0xffff); // set new direction + + uint16 *tt = _skyCompact->getTurnTable(_compact, curDir); + + if (!tt[dir]) + return true; // keep going + + _compact->turnProgId = tt[dir]; // put turn program in + _compact->turnProgPos = 0; + _compact->logic = L_TURNING; + + turn(); + + return false; // drop out of script +} + +bool Logic::fnArrived(uint32 scriptVar, uint32 b, uint32 c) { + _compact->leaving = (uint16)(scriptVar & 0xffff); + _scriptVariables[scriptVar/4]++; + return true; +} + +bool Logic::fnLeaving(uint32 a, uint32 b, uint32 c) { + _compact->atWatch = 0; + + if (_compact->leaving) { + _scriptVariables[_compact->leaving/4]--; + _compact->leaving = 0; // I shall do this only once + } + + return true; // keep going +} + +bool Logic::fnSetAlternate(uint32 scr, uint32 b, uint32 c) { + _compact->alt = (uint16)(scr & 0xffff); + _compact->logic = L_ALT; + return false; +} + +bool Logic::fnAltSetAlternate(uint32 target, uint32 scr, uint32 c) { + Compact *cpt = _skyCompact->fetchCpt(target); + cpt->alt = (uint16)(scr & 0xffff); + cpt->logic = L_ALT; + return false; +} + +bool Logic::fnKillId(uint32 id, uint32 b, uint32 c) { + if (id) { + Compact *cpt = _skyCompact->fetchCpt(id); + if (cpt->status & (1 << 7)) + _skyGrid->removeObjectFromWalk(cpt); + cpt->status = 0; + } + return true; +} + +bool Logic::fnNoHuman(uint32 a, uint32 b, uint32 c) { + if (!_scriptVariables[MOUSE_STOP]) { + _scriptVariables[MOUSE_STATUS] &= 1; + runGetOff(); + fnBlankMouse(0, 0, 0); + } + return true; +} + +bool Logic::fnAddHuman(uint32 a, uint32 b, uint32 c) { + return _skyMouse->fnAddHuman(); +} + +bool Logic::fnAddButtons(uint32 a, uint32 b, uint32 c) { + _scriptVariables[MOUSE_STATUS] |= 4; + return true; +} + +bool Logic::fnNoButtons(uint32 a, uint32 b, uint32 c) { + //remove the mouse buttons + _scriptVariables[MOUSE_STATUS] &= 0xFFFFFFFB; + return true; +} + +bool Logic::fnSetStop(uint32 a, uint32 b, uint32 c) { + _scriptVariables[MOUSE_STOP] |= 1; + return true; +} + +bool Logic::fnClearStop(uint32 a, uint32 b, uint32 c) { + _scriptVariables[MOUSE_STOP] = 0; + return true; +} + +bool Logic::fnPointerText(uint32 a, uint32 b, uint32 c) { + _skyText->fnPointerText(a, _skyMouse->giveMouseX(), _skyMouse->giveMouseY()); + return true; +} + +bool Logic::fnQuit(uint32 a, uint32 b, uint32 c) { + return false; +} + +bool Logic::fnSpeakMe(uint32 targetId, uint32 mesgNum, uint32 animNum) { + stdSpeak(_skyCompact->fetchCpt(targetId), mesgNum, animNum, 0); + return false; //drop out of script +} + +bool Logic::fnSpeakMeDir(uint32 targetId, uint32 mesgNum, uint32 animNum) { + //must be player so don't cause script to drop out + //this function sets the directional option whereby + //the anim chosen is linked to c_dir + animNum += _compact->dir << 1; //2 sizes (large and small) + return fnSpeakMe(targetId, mesgNum, animNum); +} + +bool Logic::fnSpeakWait(uint32 id, uint32 message, uint32 animation) { + // non player mega char speaks + // player will wait for it to finish before continuing script processing + _compact->flag = (uint16)(id & 0xffff); + _compact->logic = L_LISTEN; + return fnSpeakMe(id, message, animation); +} + +bool Logic::fnSpeakWaitDir(uint32 a, uint32 b, uint32 c) { + /* non player mega chr$ speaks S2(20Jan93tw) + the player will wait for it to finish + before continuing script processing + this function sets the directional option whereby + the anim chosen is linked to c_dir - + + _compact is player + a is ID to speak (not us) + b is text message number + c is base of mini table within anim_talk_table */ + +#ifdef __DC__ + __builtin_alloca(4); // Works around a gcc bug (wrong-code/11736) +#endif + + _compact->flag = (uint16)a; + _compact->logic = L_LISTEN; + + Compact *speaker = _skyCompact->fetchCpt(a); + if (c) { + c += speaker->dir << 1; + stdSpeak(speaker, b, c, speaker->dir << 1); + } else + stdSpeak(speaker, b, c, 0); + + return false; +} + +bool Logic::fnChooser(uint32 a, uint32 b, uint32 c) { + + // setup the text questions to be clicked on + // read from TEXT1 until 0 + + SkyEngine::_systemVars.systemFlags |= SF_CHOOSING; // can't save/restore while choosing + + _scriptVariables[THE_CHOSEN_ONE] = 0; // clear result + + uint32 *p = _scriptVariables + TEXT1; + uint16 ycood = TOP_LEFT_Y; // rolling coordinate + + while (*p) { + uint32 textNum = *p++; + + struct lowTextManager_t lowText = _skyText->lowTextManager(textNum, GAME_SCREEN_WIDTH, 0, 241, 0); + + uint8 *data = lowText.textData; + + // stipple the text + + uint32 size = ((dataFileHeader *)data)->s_height * ((dataFileHeader *)data)->s_width; + uint32 index = 0; + uint32 width = ((dataFileHeader *)data)->s_width; + + data += sizeof(dataFileHeader); + + while (index < size) { + if (index % width <= 1) + index ^= 1; //index++; + if (!data[index]) + data[index] = 1; + index += 2; + } + + Compact *textCompact = _skyCompact->fetchCpt(lowText.compactNum); + + textCompact->getToFlag = (uint16)textNum; + textCompact->downFlag = (uint16)*p++; // get animation number + + textCompact->status |= ST_MOUSE; // mouse detects + + textCompact->xcood = TOP_LEFT_X; // set coordinates + textCompact->ycood = ycood; + ycood += 12; + } + + if (p == _scriptVariables + TEXT1) + return true; + + _compact->logic = L_CHOOSE; // player frozen until choice made + fnAddHuman(0, 0, 0); // bring back mouse + + return false; +} + +bool Logic::fnHighlight(uint32 itemNo, uint32 pen, uint32 c) { + pen -= 11; + pen ^= 1; + pen += 241; + Compact *textCompact = _skyCompact->fetchCpt(itemNo); + uint8 *sprData = (uint8 *)SkyEngine::fetchItem(textCompact->flag); + _skyText->changeTextSpriteColour(sprData, (uint8)pen); + return true; +} + +bool Logic::fnTextKill(uint32 a, uint32 b, uint32 c) { + /// Kill of text items that are mouse detectable + + uint32 id = FIRST_TEXT_COMPACT; + + for (int i = 10; i > 0; i--) { + Compact *cpt = _skyCompact->fetchCpt(id); + if (cpt->status & (1 << 4)) + cpt->status = 0; + id++; + } + return true; +} + +bool Logic::fnStopMode(uint32 a, uint32 b, uint32 c) { + _compact->logic = L_STOPPED; + return false; +} + +bool Logic::fnWeWait(uint32 id, uint32 b, uint32 c) { + /// We have hit another mega + /// we are going to wait for it to move + + _compact->waitingFor = (uint16) id; + stopAndWait(); + return true; // not sure about this +} + +bool Logic::fnSendSync(uint32 mega, uint32 sync, uint32 c) { + Compact *cpt = _skyCompact->fetchCpt(mega); + cpt->sync = (uint16)(sync & 0xffff); + return false; +} + +bool Logic::fnSendFastSync(uint32 mega, uint32 sync, uint32 c) { + Compact *cpt = _skyCompact->fetchCpt(mega); + cpt->sync = (uint16)(sync & 0xffff); + return true; +} + +bool Logic::fnSendRequest(uint32 target, uint32 scr, uint32 c) { + Compact *cpt = _skyCompact->fetchCpt(target); + cpt->request = (uint16)(scr & 0xffff); + return false; +} + +bool Logic::fnClearRequest(uint32 target, uint32 b, uint32 c) { + Compact *cpt = _skyCompact->fetchCpt(target); + cpt->request = 0; + return true; +} + +bool Logic::fnCheckRequest(uint32 a, uint32 b, uint32 c) { + /// check for interaction request + + if (!_compact->request) + return true; + + _compact->mode = C_ACTION_MODE; // into action mode + + _compact->actionSub = _compact->request; + _compact->actionSub_off = 0; + + _compact->request = 0; // trash request + return false; // drop from script +} + +bool Logic::fnStartMenu(uint32 firstObject, uint32 b, uint32 c) { + /// initialise the top menu bar + // firstObject is o0 for game menu, k0 for linc + + uint i; + firstObject /= 4; + + // (1) FIRST, SET UP THE 2 ARROWS SO THEY APPEAR ON SCREEN + + Compact *cpt = _skyCompact->fetchCpt(47); + cpt->status = ST_MOUSE + ST_FOREGROUND + ST_LOGIC + ST_RECREATE; + cpt->screen = (uint16)(_scriptVariables[SCREEN] & 0xffff); + + cpt = _skyCompact->fetchCpt(48); + cpt->status = ST_MOUSE + ST_FOREGROUND + ST_LOGIC + ST_RECREATE; + cpt->screen = (uint16)(_scriptVariables[SCREEN] & 0xffff); + + // (2) COPY OBJECTS FROM NON-ZERO INVENTORY VARIABLES INTO OBJECT DISPLAY LIST (& COUNT THEM) + + // sort the objects and pad with blanks + + uint32 menuLength = 0; + for (i = firstObject; i < firstObject + ARRAYSIZE(_objectList); i++) { + if ( _scriptVariables[i] ) + _objectList[menuLength++] = _scriptVariables[i]; + } + _scriptVariables[MENU_LENGTH] = menuLength; + + // (3) OK, NOW TOP UP THE LIST WITH THE REQUIRED NO. OF BLANK OBJECTS (for min display length 11) + + uint32 blankID = 51; + for (i = menuLength; i < 11; i++) + _objectList[i] = blankID++; + + // (4) KILL ID's OF ALL 20 OBJECTS SO UNWANTED ICONS (SCROLLED OFF) DON'T REMAIN ON SCREEN + // (There should be a better way of doing this - only kill id of 12th item when menu has scrolled right) + + for (i = 0; i < ARRAYSIZE(_objectList); i++) { + if (_objectList[i]) + (_skyCompact->fetchCpt(_objectList[i]))->status = ST_LOGIC; + else break; + } + + // (5) NOW FIND OUT WHICH OBJECT TO START THE DISPLAY FROM (depending on scroll offset) + + if (menuLength < 11) // check we can scroll + _scriptVariables[SCROLL_OFFSET] = 0; + else if (menuLength < _scriptVariables[SCROLL_OFFSET] + 11) + _scriptVariables[SCROLL_OFFSET] = menuLength - 11; + + // (6) AND FINALLY, INITIALISE THE 11 OBJECTS SO THEY APPEAR ON SCREEEN + + uint16 rollingX = TOP_LEFT_X + 28; + for (i = 0; i < 11; i++) { + cpt = _skyCompact->fetchCpt( + _objectList[_scriptVariables[SCROLL_OFFSET] + i]); + + cpt->status = ST_MOUSE + ST_FOREGROUND + ST_LOGIC + ST_RECREATE; + cpt->screen = (uint16)(_scriptVariables[SCREEN] & 0xffff); + + cpt->xcood = rollingX; + rollingX += 24; + + if (_scriptVariables[MENU] == 2) + cpt->ycood = 136; + else + cpt->ycood = 112; + } + + return true; +} + +bool Logic::fnUnhighlight(uint32 item, uint32 b, uint32 c) { + Compact *cpt = _skyCompact->fetchCpt(item); + cpt->frame--; + cpt->getToFlag = 0; + return true; +} + +bool Logic::fnFaceId(uint32 otherId, uint32 b, uint32 c) { + /// return the direction to turn to face another id + /// pass back result in c_just_flag + + Compact *cpt = _skyCompact->fetchCpt(otherId); + + int16 x = _compact->xcood - cpt->xcood; + + if (x < 0) { // we're to the left + x = -x; + _compact->getToFlag = 3; + } else { // it's to the left + _compact->getToFlag = 2; + } + + // now check y + + // we must find the true bottom of the sprite + // it is not enough to use y coord because changing + // sprite offsets can ruin the formula - instead we + // will use the bottom of the mouse collision area + + int16 y = _compact->ycood - (cpt->ycood + cpt->mouseRelY + cpt->mouseSizeY); + + if (y < 0) { // it's below + y = -y; + if (y >= x) + _compact->getToFlag = 1; + } else { // it's above + if (y >= x) + _compact->getToFlag = 0; + } + return true; +} + +bool Logic::fnForeground(uint32 sprite, uint32 b, uint32 c) { + /// Make sprite a foreground sprite + Compact *cpt = _skyCompact->fetchCpt(sprite); + cpt->status &= 0xfff8; + cpt->status |= ST_FOREGROUND; + return true; +} + +bool Logic::fnBackground(uint32 a, uint32 b, uint32 c) { + /// Make us a background sprite + _compact->status &= 0xfff8; + _compact->status |= ST_BACKGROUND; + return true; +} + +bool Logic::fnNewBackground(uint32 sprite, uint32 b, uint32 c) { + /// Make sprite a background sprite + Compact *cpt = _skyCompact->fetchCpt(sprite); + cpt->status &= 0xfff8; + cpt->status |= ST_BACKGROUND; + return true; +} + +bool Logic::fnSort(uint32 mega, uint32 b, uint32 c) { + Compact *cpt = _skyCompact->fetchCpt(mega); + cpt->status &= 0xfff8; + cpt->status |= ST_SORT; + return true; +} + +bool Logic::fnNoSpriteEngine(uint32 a, uint32 b, uint32 c) { + /// stop the compact printing + /// remove foreground, background & sort + _compact->status &= 0xfff8; + return true; +} + +bool Logic::fnNoSpritesA6(uint32 us, uint32 b, uint32 c) { + /// stop the compact printing + /// remove foreground, background & sort + Compact *cpt = _skyCompact->fetchCpt(us); + cpt->status &= 0xfff8; + return true; +} + +bool Logic::fnResetId(uint32 id, uint32 resetBlock, uint32 c) { + /// used when a mega is to be restarted + /// eg - when a smaller mega turn to larger + /// - a mega changes rooms... + + Compact *cpt = _skyCompact->fetchCpt(id); + uint16 *rst = (uint16 *)_skyCompact->fetchCpt(resetBlock); + + if (!cpt) { + warning("fnResetId(): Compact %d (id) == NULL", id); + return true; + } + if (!rst) { + warning("fnResetId(): Compact %d (resetBlock) == NULL", resetBlock); + return true; + } + + uint16 off; + while ((off = *rst++) != 0xffff) + *(uint16 *)_skyCompact->getCompactElem(cpt, off) = *rst++; + return true; +} + +bool Logic::fnToggleGrid(uint32 a, uint32 b, uint32 c) { + /// Toggle a mega's grid plotting + _compact->status ^= ST_GRID_PLOT; + return true; +} + +bool Logic::fnPause(uint32 cycles, uint32 b, uint32 c) { + /// Set mega to L_PAUSE + _compact->flag = (uint16)(cycles & 0xffff); + _compact->logic = L_PAUSE; + return false; // drop out of script +} + +bool Logic::fnRunAnimMod(uint32 animNo, uint32 b, uint32 c) { + _compact->grafixProgId = animNo; + _compact->grafixProgPos = 0; + + _compact->offset = *_skyCompact->getGrafixPtr(_compact); + _compact->grafixProgPos++; + _compact->logic = L_MOD_ANIMATE; + anim(); + return false; // drop from script +} + +bool Logic::fnSimpleMod(uint32 animSeqNo, uint32 b, uint32 c) { + _compact->grafixProgId = animSeqNo; + _compact->grafixProgPos = 0; + + _compact->logic = L_SIMPLE_MOD; + _compact->offset = *_skyCompact->getGrafixPtr(_compact); + _compact->grafixProgPos++; + simpleAnim(); + return false; +} + +bool Logic::fnRunFrames(uint32 sequenceNo, uint32 b, uint32 c) { + _compact->grafixProgId = sequenceNo; + _compact->grafixProgPos = 0; + + _compact->logic = L_FRAMES; + _compact->offset = *_skyCompact->getGrafixPtr(_compact); + _compact->grafixProgPos++; + simpleAnim(); + return false; +} + +bool Logic::fnAwaitSync(uint32 a, uint32 b, uint32 c) { + if (_compact->sync) + return true; + + _compact->logic = L_WAIT_SYNC; + return false; +} + +bool Logic::fnIncMegaSet(uint32 a, uint32 b, uint32 c) { + _compact->megaSet += NEXT_MEGA_SET; + return true; +} + +bool Logic::fnDecMegaSet(uint32 a, uint32 b, uint32 c) { + _compact->megaSet -= NEXT_MEGA_SET; + return true; +} + +bool Logic::fnSetMegaSet(uint32 mega, uint32 setNo, uint32 c) { + Compact *cpt = _skyCompact->fetchCpt(mega); + cpt->megaSet = (uint16) (setNo * NEXT_MEGA_SET); + return true; +} + +bool Logic::fnMoveItems(uint32 listNo, uint32 screenNo, uint32 c) { + // Move a list of id's to another screen + uint16 *p = (uint16*)_skyCompact->fetchCpt(CPT_MOVE_LIST); + p = (uint16*)_skyCompact->fetchCpt(p[listNo]); + for (int i = 0; i < 2; i++) { + if (!*p) + return true; + Compact *cpt = _skyCompact->fetchCpt(*p++); + cpt->screen = (uint16)(screenNo & 0xffff); + } + return true; +} + +bool Logic::fnNewList(uint32 a, uint32 b, uint32 c) { + /// Reset the chooser list + for (int i = 0; i < 16; i++) + _scriptVariables[TEXT1 + i] = 0; + return true; +} + +bool Logic::fnAskThis(uint32 textNo, uint32 animNo, uint32 c) { + // find first free position + uint32 *p = _scriptVariables + TEXT1; + while (*p) + p += 2; + *p++ = textNo; + *p = animNo; + return true; +} + +bool Logic::fnRandom(uint32 a, uint32 b, uint32 c) { + _scriptVariables[RND] = _rnd.getRandomNumber(65536) & a; + return true; +} + +bool Logic::fnPersonHere(uint32 id, uint32 room, uint32 c) { + Compact *cpt = _skyCompact->fetchCpt(id); + _scriptVariables[RESULT] = cpt->screen == room ? 1 : 0; + return true; +} + +bool Logic::fnToggleMouse(uint32 a, uint32 b, uint32 c) { + + _skyCompact->fetchCpt(a)->status ^= ST_MOUSE; + return true; +} + +bool Logic::fnMouseOn(uint32 a, uint32 b, uint32 c) { + //switch on the mouse highlight + Compact *cpt = _skyCompact->fetchCpt(a); + cpt->status |= ST_MOUSE; + return true; +} + +bool Logic::fnMouseOff(uint32 a, uint32 b, uint32 c) { + //switch off the mouse highlight + Compact *cpt = _skyCompact->fetchCpt(a); + cpt->status &= ~ST_MOUSE; + return true; +} + +bool Logic::fnFetchX(uint32 id, uint32 b, uint32 c) { + Compact *cpt = _skyCompact->fetchCpt(id); + _scriptVariables[RESULT] = cpt->xcood; + return true; +} + +bool Logic::fnFetchY(uint32 id, uint32 b, uint32 c) { + Compact *cpt = _skyCompact->fetchCpt(id); + _scriptVariables[RESULT] = cpt->ycood; + return true; +} + +bool Logic::fnTestList(uint32 id, uint32 x, uint32 y) { + _scriptVariables[RESULT] = 0; // assume fail + uint16 *list = (uint16 *)_skyCompact->fetchCpt(id); + + while (*list) { + if ((x >= list[0]) && (x < list[1]) && (y >= list[2]) && (y < list[3])) + _scriptVariables[RESULT] = list[4]; + list += 5; + } + return true; +} + +bool Logic::fnFetchPlace(uint32 id, uint32 b, uint32 c) { + Compact *cpt = _skyCompact->fetchCpt(id); + _scriptVariables[RESULT] = cpt->place; + return true; +} + +bool Logic::fnCustomJoey(uint32 id, uint32 b, uint32 c) { + /// return id's x & y coordinate & c_mood (i.e. stood still yes/no) + /// used by Joey-Logic - done in code like this because scripts can't + /// get access to another megas compact as easily + + Compact *cpt = _skyCompact->fetchCpt(id); + + _scriptVariables[PLAYER_X] = cpt->xcood; + _scriptVariables[PLAYER_Y] = cpt->ycood; + _scriptVariables[PLAYER_MOOD] = cpt->mood; + _scriptVariables[PLAYER_SCREEN] = cpt->screen; + return true; +} + +bool Logic::fnSetPalette(uint32 a, uint32 b, uint32 c) { + _skyScreen->setPaletteEndian((uint8 *)_skyCompact->fetchCpt(a)); + SkyEngine::_systemVars.currentPalette = a; + return true; +} + +bool Logic::fnTextModule(uint32 a, uint32 b, uint32 c) { + _skyText->fnTextModule(a, b); + return true; +} + +bool Logic::fnChangeName(uint32 id, uint32 textNo, uint32 c) { + Compact *cpt = _skyCompact->fetchCpt(id); + cpt->cursorText = (uint16) textNo; + return true; +} + +bool Logic::fnMiniLoad(uint32 a, uint32 b, uint32 c) { + _skyDisk->fnMiniLoad((uint16)a); + return true; +} + +bool Logic::fnFlushBuffers(uint32 a, uint32 b, uint32 c) { + _skyDisk->fnFlushBuffers(); + return true; +} + +bool Logic::fnFlushChip(uint32 a, uint32 b, uint32 c) { + // this should be done automatically + return true; +} + +bool Logic::fnSaveCoods(uint32 a, uint32 b, uint32 c) { + _skyMouse->fnSaveCoods(); + return true; +} + +bool Logic::fnPlotGrid(uint32 x, uint32 y, uint32 width) { + _skyGrid->plotGrid(x, y, width, _compact); + return true; +} + +bool Logic::fnRemoveGrid(uint32 x, uint32 y, uint32 width) { + _skyGrid->removeGrid(x, y, width, _compact); + return true; +} + +bool Logic::fnEyeball(uint32 id, uint32 b, uint32 c) { + // set 'result' to frame no. pointing to foster, according to table used + // eg. FN_eyeball (id_eye_90_table); + + uint16 *eyeTable = (uint16 *)_skyCompact->fetchCpt(id); + Compact *cpt = _skyCompact->fetchCpt(ID_BLUE_FOSTER); + + uint32 x = cpt->xcood; // 168 < x < 416 + x -= 168; + x >>= 3; + + uint32 y = cpt->ycood; // 256 < y < 296 + y -= 256; + y <<= 2; + + _scriptVariables[RESULT] = eyeTable[x + y] + S91; + return true; +} + +bool Logic::fnLeaveSection(uint32 sectionNo, uint32 b, uint32 c) { + if (SkyEngine::isDemo()) + _skyControl->showGameQuitMsg(); + + if (sectionNo == 5) //linc section - has different mouse icons + _skyMouse->replaceMouseCursors(60301); + + return true; +} + +bool Logic::fnEnterSection(uint32 sectionNo, uint32 b, uint32 c) { + + if (SkyEngine::isDemo() && (sectionNo > 2)) + _skyControl->showGameQuitMsg(); + + _scriptVariables[CUR_SECTION] = sectionNo; + SkyEngine::_systemVars.currentMusic = 0; + + if (sectionNo == 5) //linc section - has different mouse icons + _skyMouse->replaceMouseCursors(60302); + + if ((sectionNo != _currentSection) || (SkyEngine::_systemVars.systemFlags & SF_GAME_RESTORED)) { + _currentSection = sectionNo; + + sectionNo++; + _skyMusic->loadSection((byte)sectionNo); + _skySound->loadSection((byte)sectionNo); + _skyGrid->loadGrids(); + SkyEngine::_systemVars.systemFlags &= ~SF_GAME_RESTORED; + } + + return true; +} + +bool Logic::fnRestoreGame(uint32 a, uint32 b, uint32 c) { + _skyControl->doLoadSavePanel(); + return false; +} + +bool Logic::fnRestartGame(uint32 a, uint32 b, uint32 c) { + _skyControl->restartGame(); + return false; +} + +bool Logic::fnNewSwingSeq(uint32 a, uint32 b, uint32 c) { + // only certain files work on pc. (huh?! something we should take care of?) + if ((a == 85) || (a == 106) || (a == 75) || (a == 15)) { + _skyScreen->startSequenceItem((uint16)a); + } else { + debug(1,"Logic::fnNewSwingSeq: ignored seq %d",a); + } + return true; +} + +bool Logic::fnWaitSwingEnd(uint32 a, uint32 b, uint32 c) { + _skyScreen->waitForSequence(); + return true; +} + +bool Logic::fnSkipIntroCode(uint32 a, uint32 b, uint32 c) { + SkyEngine::_systemVars.pastIntro = true; + return true; +} + +bool Logic::fnBlankScreen(uint32 a, uint32 b, uint32 c) { + _skyScreen->clearScreen(); + return true; +} + +bool Logic::fnPrintCredit(uint32 a, uint32 b, uint32 c) { + + lowTextManager_t creditText = _skyText->lowTextManager(a, 240, 0, 248, true); + Compact *credCompact = _skyCompact->fetchCpt(creditText.compactNum); + credCompact->xcood = 168; + if ((a == 558) && (c == 215)) + credCompact->ycood = 211; + else + credCompact->ycood = (uint16)c; + _scriptVariables[RESULT] = creditText.compactNum; + return true; +} + +bool Logic::fnLookAt(uint32 a, uint32 b, uint32 c) { + + struct lowTextManager_t textInfo = _skyText->lowTextManager(a, 240, 0, 248, true); + Compact *textCpt = _skyCompact->fetchCpt(textInfo.compactNum); + textCpt->xcood = 168; + textCpt->ycood = (uint16)c; + + _skyScreen->recreate(); + _skyScreen->spriteEngine(); + _skyScreen->flip(); + + fnNoHuman(0, 0, 0); + _skyMouse->lockMouse(); + + _skyMouse->waitMouseNotPressed(); + + _skyMouse->unlockMouse(); + fnAddHuman(0, 0, 0); + textCpt->status = 0; + + return true; +} + +bool Logic::fnLincTextModule(uint32 textPos, uint32 textNo, uint32 buttonAction) { + + uint16 cnt; + if (buttonAction & 0x8000) + for (cnt = LINC_DIGIT_0; cnt <= LINC_DIGIT_9; cnt++) + _scriptVariables[cnt] = 0; + buttonAction &= 0x7FFF; + if (buttonAction < 10) + _scriptVariables[LINC_DIGIT_0 + buttonAction] = textNo; + + lowTextManager_t text = _skyText->lowTextManager(textNo, 220, 0, 215, false); + + Compact *textCpt = _skyCompact->fetchCpt(text.compactNum); + + if (textPos < 20) { // line number (for text) + textCpt->xcood = 152; + textCpt->ycood = (uint16)textPos * 13 + 170; + } else if (textPos > 20) { // x coordinate (for numbers) + textCpt->xcood = (uint16)textPos; + textCpt->ycood = 214; + } else warning("::fnLincTextModule: textPos == 20"); + textCpt->getToFlag = (uint16)textNo; + return true; +} + +bool Logic::fnTextKill2(uint32 a, uint32 b, uint32 c) { + /// Kill all text items + + uint32 id = FIRST_TEXT_COMPACT; + + for (int i = 10; i > 0; i--) { + Compact *cpt = _skyCompact->fetchCpt(id); + cpt->status = 0; + id++; + } + return true; +} + +bool Logic::fnSetFont(uint32 font, uint32 b, uint32 c) { + _skyText->fnSetFont(font); + return true; +} + +bool Logic::fnStartFx(uint32 sound, uint32 b, uint32 c) { + _skySound->fnStartFx(sound, (uint8)(b & 1)); + return true; +} + +bool Logic::fnStopFx(uint32 a, uint32 b, uint32 c) { + _skySound->fnStopFx(); + return true; +} + +bool Logic::fnStartMusic(uint32 a, uint32 b, uint32 c) { + if (!(SkyEngine::_systemVars.systemFlags & SF_MUS_OFF)) + _skyMusic->startMusic((uint16)a); + SkyEngine::_systemVars.currentMusic = (uint16)a; + return true; +} + +bool Logic::fnStopMusic(uint32 a, uint32 b, uint32 c) { + _skyMusic->startMusic(0); + SkyEngine::_systemVars.currentMusic = 0; + return true; +} + +bool Logic::fnFadeDown(uint32 a, uint32 b, uint32 c) { + _skyScreen->fnFadeDown(a); + return true; +} + +bool Logic::fnFadeUp(uint32 a, uint32 b, uint32 c) { + SkyEngine::_systemVars.currentPalette = a; + _skyScreen->fnFadeUp(a,b); + return true; +} + +bool Logic::fnQuitToDos(uint32 a, uint32 b, uint32 c) { + SkyEngine::_systemVars.quitGame = true; + return false; +} + +bool Logic::fnPauseFx(uint32 a, uint32 b, uint32 c) { + _skySound->fnPauseFx(); + return true; +} + +bool Logic::fnUnPauseFx(uint32 a, uint32 b, uint32 c) { + _skySound->fnUnPauseFx(); + return true; +} + +bool Logic::fnPrintf(uint32 a, uint32 b, uint32 c) { + printf("fnPrintf: %d\n", a); + return true; +} + +void Logic::stdSpeak(Compact *target, uint32 textNum, uint32 animNum, uint32 base) { + + animNum += target->megaSet / NEXT_MEGA_SET; + animNum &= 0xFF; + + uint16 *talkTable = (uint16*)_skyCompact->fetchCpt(CPT_TALK_TABLE_LIST); + target->grafixProgId = talkTable[animNum]; + target->grafixProgPos = 0; + uint16 *animPtr = _skyCompact->getGrafixPtr(target); + + if (animPtr) { + target->offset = *animPtr++; + target->getToFlag = *animPtr++; + target->grafixProgPos += 2; + } else + target->grafixProgId = 0; + + bool speechFileFound = false; + if (SkyEngine::isCDVersion()) + speechFileFound = _skySound->startSpeech((uint16)textNum); + + if ((SkyEngine::_systemVars.systemFlags & SF_ALLOW_TEXT) || !speechFileFound) { + // form the text sprite, if player wants subtitles or + // if we couldn't find the speech file + struct lowTextManager_t textInfo; + textInfo = _skyText->lowTextManager(textNum, FIXED_TEXT_WIDTH, 0, (uint8)target->spColour, true); + Compact *textCompact = _skyCompact->fetchCpt(textInfo.compactNum); + target->spTextId = textInfo.compactNum; //So we know what text to kill + byte *textGfx = textInfo.textData; + + textCompact->screen = target->screen; //put it on our screen + + if (_scriptVariables[SCREEN] == target->screen) { // Only use coordinates if we are on the current screen + //talking on-screen + //create the x coordinate for the speech text + //we need the talkers sprite information + byte *targetGfx = (byte *)SkyEngine::fetchItem(target->frame >> 6); + uint16 xPos = target->xcood + ((struct dataFileHeader *)targetGfx)->s_offset_x; + uint16 width = (((struct dataFileHeader *)targetGfx)->s_width >> 1); + + xPos += width - (FIXED_TEXT_WIDTH / 2); //middle of talker + + if (xPos < TOP_LEFT_X) + xPos = TOP_LEFT_X; + + width = xPos + FIXED_TEXT_WIDTH; + if ((TOP_LEFT_X + FULL_SCREEN_WIDTH) <= width) { + xPos = TOP_LEFT_X + FULL_SCREEN_WIDTH; + xPos -= FIXED_TEXT_WIDTH; + } + + textCompact->xcood = xPos; + uint16 yPos = target->ycood + ((struct dataFileHeader *)targetGfx)->s_offset_y - 6 - ((struct dataFileHeader *)textGfx)->s_height; + + if (yPos < TOP_LEFT_Y) + yPos = TOP_LEFT_Y; + + textCompact->ycood = yPos; + + } else { + //talking off-screen + target->spTextId = 0; //don't kill any text 'cos none was made + textCompact->status = 0; //don't display text + } + // In CD version, we're doing the timing by checking when the VOC has stopped playing. + // Setting spTime to 10 thus means that we're doing a pause of 10 gamecycles between + // each sentence. + if (speechFileFound) + target->spTime = 10; + else + target->spTime = (uint16)_skyText->_numLetters + 5; + } else { + target->spTime = 10; + target->spTextId = 0; + } + target->logic = L_TALK; +} + +} // End of namespace Sky -- cgit v1.2.3