Logo Search packages:      
Sourcecode: scummvm version File versions

logic.cpp

/* ScummVM - Scumm Interpreter
 * Copyright (C) 2003-2004 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * $Header: /cvsroot/scummvm/scummvm/sky/logic.cpp,v 1.148 2004/11/11 15:40:48 lavosspawn Exp $
 *
 */

#include "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"
#include "sky/talks.h"
/*
#include "sky/skydefs.h"
#include "base/gameDetector.h"
*/

namespace Sky {

uint32 Logic::_scriptVariables[838];

typedef void (Logic::*LogicTable) ();
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
};

Logic::Logic(Screen *skyScreen, Disk *skyDisk, Text *skyText, MusicBase *skyMusic, Mouse *skyMouse, Sound *skySound) {
      _skyScreen = skyScreen;
      _skyDisk = skyDisk;
      _skyText = skyText;
      _skyMusic = skyMusic;
      _skySound = skySound;
      _skyMouse = skyMouse;
      _skyGrid = new Grid(_skyDisk);
      _skyAutoRoute = new AutoRoute(_skyGrid);

      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();
}

bool Logic::checkProtection(void) {

      if (!_scriptVariables[ENTER_DIGITS]) return false;
      if (_scriptVariables[CONSOLE_TYPE] == 5) { // reactor code
            _scriptVariables[FS_COMMAND] = 240;
            _scriptVariables[ENTER_DIGITS] = 0;
            return true;
      } else {                                                 // copy protection
            _scriptVariables[FS_COMMAND] = 337;
            _scriptVariables[ENTER_DIGITS] = 0;
            return true;
      }
}

void Logic::engine() {
      uint16 *logicList = (uint16 *)SkyEngine::fetchCompact(_scriptVariables[LOGIC_LIST_NO]);

      while (uint16 id = *logicList++) { // 0 means end of list
            if (id == 0xffff) {
                  // Change logic data address
                  logicList = (uint16 *)SkyEngine::fetchCompact(*logicList);
                  continue;
            }

            _scriptVariables[CUR_ID] = id;
            _compact = SkyEngine::fetchCompact(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;
      }
}

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) && (_compact == &SkyCompact::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->grafixProg.pos = 0;
            _compact->grafixProg.ptrTarget = 0;
            _compact->grafixProg.ptrType = AUTOROUTE;
      }

      _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->extCompact->waitingFor == 0xffff) { // 1st cycle of re-route does
            mainAnim();
            return;
      }

      if (_compact->extCompact->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(SkyEngine::fetchCompact(_compact->extCompact->waitingFor))) {
                  stopAndWait();
                  return;
            }

            // we are not in fact hitting this person so clr & continue
            // it must have registered some time ago

            _compact->extCompact->waitingFor = 0; // clear id flag
      }

      // ok, our turn to check for collisions

      uint16 *logicList = (uint16 *)SkyEngine::fetchCompact(_scriptVariables[LOGIC_LIST_NO]);
      Compact *cpt = 0;

      while (uint16 id = *logicList++) { // get an id

            if (id == 0xffff) { // address change?
                  logicList = (uint16 *)SkyEngine::fetchCompact(*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 = SkyEngine::fetchCompact(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->extCompact->waitingFor = 0xffff; // effect 1 cycle collision skip
                        // tell it it is waiting for us
                        cpt->extCompact->waitingFor = (uint16)(_scriptVariables[CUR_ID] & 0xffff);
                        // restart current script
                        *SkyCompact::getSub(_compact, _compact->mode + 2) = 0;
                        _compact->logic = L_SCRIPT;
                        logicScript();
                        return;
                  }

                  script(_compact->extCompact->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->extCompact->request) {
            _compact->mode = C_ACTION_MODE; // put into action mode
            _compact->extCompact->actionSub = _compact->extCompact->request;
            _compact->extCompact->actionSub_off = 0;
            _compact->extCompact->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->extCompact->atWatch) { // any flag set?
            mainAnim();
            return;
      }

      // ok, there is an at watch - see if it's changed

      if (_compact->extCompact->atWas == _scriptVariables[_compact->extCompact->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->extCompact->waitingFor = 0; // clear possible zero-zero skip

      //uint16 *sequence = _compact->grafixProg;
      uint16 *sequence = SkyCompact::getGrafixPtr(_compact);
      if (!*sequence) {
            // ok, move to new anim segment
            sequence += 2;
            _compact->grafixProg.pos += 2;
            if (!*sequence) { // end of route?
                  // ok, sequence has finished

                  // will start afresh if new sequence continues in last direction
                  _compact->extCompact->arAnimIndex = 0;

                  _compact->downFlag = 0; // pass back ok to script
                  _compact->logic = L_SCRIPT;
                  logicScript();
                  return;
            }

            _compact->extCompact->arAnimIndex = 0; // reset position
      }

      uint16 dir;
      while ((dir = _compact->extCompact->dir) != *(sequence + 1)) {
            // ok, setup turning
            _compact->extCompact->dir = *(sequence + 1);

            uint16 **tt = SkyCompact::getTurnTable(_compact,_compact->extCompact->megaSet, dir);
            if (tt[_compact->extCompact->dir]) {
                  _compact->extCompact->turnProg = tt[_compact->extCompact->dir];
                  _compact->logic = L_AR_TURNING;
                  arTurn();
                  return;
            }
      };

      uint16 *animList = *(uint16 **)SkyCompact::getCompactElem(_compact,
                  C_ANIM_UP + _compact->extCompact->megaSet + dir * 4);

      uint16 arAnimIndex = _compact->extCompact->arAnimIndex;
      if (!animList[arAnimIndex / 2]) {
             arAnimIndex = 0;
            _compact->extCompact->arAnimIndex = 0; // reset
      }

      _compact->extCompact->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() {
      _compact->frame = *_compact->extCompact->turnProg++;

      if (!*_compact->extCompact->turnProg) { // turn done?
            // Back to ar mode
            _compact->extCompact->arAnimIndex = 0;
            _compact->logic = L_AR_ANIM;
      }
}

void Logic::alt() {
      /// change the current script
      _compact->logic = L_SCRIPT;
      *SkyCompact::getSub(_compact, _compact->mode) = _compact->extCompact->alt;
      *SkyCompact::getSub(_compact, _compact->mode + 2) = 0;
      logicScript();
}

void Logic::anim() {
      /// Follow an animation sequence

      //uint16 *grafixProg = _compact->grafixProg;
      uint16 *grafixProg = SkyCompact::getGrafixPtr(_compact);

      while (*grafixProg) {
            _compact->grafixProg.pos += 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 = SkyEngine::fetchCompact(*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() {
      if (*_compact->extCompact->turnProg) {
            _compact->frame = *_compact->extCompact->turnProg++;
            return;
      }

      // turn_to_script:
      _compact->extCompact->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 ((SkyEngine::_systemVars.systemFlags & SF_ALLOW_TEXT) &&
                              (_compact->extCompact->spTextId > 0) &&
                              (_compact->extCompact->spTextId < 0xFFFF)) {
                              
                              SkyEngine::fetchCompact(_compact->extCompact->spTextId)->status = 0;
                        }
                        if (SkyCompact::getGrafixPtr(_compact)) {
                              _compact->frame = _compact->getToFlag; // set character to stand
                              _compact->grafixProg.ptrType = PTR_NULL;
                        }

                        _compact->logic = L_SCRIPT;
                        logicScript();
                        return;
                  }

      // If speech is allowed then check for it to finish before finishing animations

      if ((_compact->extCompact->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->grafixProg.ptrType = PTR_NULL;
            }

            logicScript();
            return;
      }

      //uint16 *graphixProg = _compact->grafixProg; // no anim file?
      uint16 *graphixProg = SkyCompact::getGrafixPtr(_compact);
      if (graphixProg) {
            if ((*graphixProg) && ((_compact->extCompact->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->grafixProg.pos += 3;
            } else {
                  // we ran out of frames or finished speech, let actor stand still.
                  _compact->frame = _compact->getToFlag;
                  _compact->grafixProg.ptrType = PTR_NULL;
            }
      }

      if (_skySound->speechFinished()) _compact->extCompact->spTime--;

      if (_compact->extCompact->spTime == 0) {

            // ok, speech has finished

            if (_compact->extCompact->spTextId) {
                  Compact *cpt = SkyEngine::fetchCompact(_compact->extCompact->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 = SkyEngine::fetchCompact(_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 = SkyEngine::fetchCompact(_compact->extCompact->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->extCompact->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->grafixProg.pos += 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 = SkyEngine::fetchCompact(*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, _compact->extCompact->megaSet);
      MegaSet *m2 = SkyCompact::getMegaSet(cpt, cpt->extCompact->megaSet);

      // target's base coordinates
      uint16 x = cpt->xcood & 0xfff8;
      uint16 y = cpt->ycood & 0xfff8;

      // The collision is direction dependent
      switch (_compact->extCompact->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->extCompact->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 = (uint16)(_compact->extCompact->stopScript & 0xffff);
      *offset   = 0; //stopScript is uint16, after right shift is zero
//    *offset   = (uint16)(_compact->extCompact->stopScript >> 16);

      _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];
}

typedef bool (Logic::*McodeTable) (uint32, uint32, uint32);
static  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
};

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 = (uint16)((scriptNo & 0xff00) >> 12);
      debug(3, "Doing Script %x", (offset << 16) | scriptNo);
      uint16 *scriptData = _moduleList[moduleNo]; // get module address

      if (!scriptData) { // The module has not been loaded
            scriptData = (uint16 *)_skyDisk->loadFile(moduleNo + F_MODULE_0);
            _moduleList[moduleNo] = scriptData; // module has been loaded
      }

      uint16 *moduleStart = scriptData;

      // Check whether we have an offset or what
      if (offset)
            scriptData = moduleStart + offset;
      else
            scriptData += READ_LE_UINT16(scriptData + (scriptNo & 0x0fff));

      uint32 a = 0, b = 0, c = 0;
      uint16 command, s;

      for (;;) {
            command = READ_LE_UINT16(scriptData++); // get a command
            Debug::script(command, scriptData);

            switch (command) {
            case 0: // push_variable
                  push( _scriptVariables[READ_LE_UINT16(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(READ_LE_UINT16(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 = READ_LE_UINT16(scriptData++);

                  a = pop();
                  if (!a)
                        scriptData += s / 2;
                  break;
            case 6: // pop_var
                  b = _scriptVariables[READ_LE_UINT16(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 = READ_LE_UINT16(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 = READ_LE_UINT16(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 = READ_LE_UINT16(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 = READ_LE_UINT16(scriptData++); // get number of cases

                  a = pop(); // and value to switch on

                  do {
                        if (a == READ_LE_UINT16(scriptData)) {
                              scriptData += READ_LE_UINT16(scriptData + 1) / 2;
                              scriptData++;
                              break;
                        }
                        scriptData += 2;
                  } while (--s);

                  if (s == 0)
                        scriptData += READ_LE_UINT16(scriptData++)/2 - 1; // use the default
                  break;
            case 15: // push_offset
                  push( *(uint16 *)SkyCompact::getCompactElem(_compact, READ_LE_UINT16(scriptData++)) );
                  break;
            case 16: // pop_offset
                  // pop a value into a compact
                  *(uint16 *)SkyCompact::getCompactElem(_compact, READ_LE_UINT16(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 = READ_LE_UINT16(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(a);
      return true;
}

bool Logic::fnCacheFast(uint32 a, uint32 b, uint32 c) {
      _skyDisk->fnCacheFast(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->extCompact->arTargetX = (uint16)x;
      _compact->extCompact->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 = SkyEngine::fetchCompact(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 = SkyEngine::fetchCompact(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 = SkyEngine::fetchCompact(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 = SkyEngine::fetchCompact(_compact->place);
      if (!cpt) { 
            warning("can't find _compact's getToTable. Place compact is NULL");
            return false; 
      }
      uint16 *getToTable = cpt->getToTable;
      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->grafixProg.ptrType = COMPACTELEM;
      _compact->grafixProg.pos = 0;
      _compact->grafixProg.ptrTarget = 
             C_STAND_UP + _compact->extCompact->megaSet + _compact->extCompact->dir * 4;

      uint16 *standList = SkyCompact::getGrafixPtr(_compact);

      _compact->offset = *standList; // get frames offset
      _compact->logic = L_SIMPLE_MOD;
      _compact->grafixProg.pos++;
      simpleAnim();
      return false; // drop out of script
}

bool Logic::fnTurnTo(uint32 dir, uint32 b, uint32 c) {
      /// turn compact to direction dir

      uint16 curDir = _compact->extCompact->dir; // get current direction
      _compact->extCompact->dir = (uint16)(dir & 0xffff); // set new direction

      uint16 **tt = SkyCompact::getTurnTable(_compact, _compact->extCompact->megaSet, curDir);

      if (!tt[dir])
            return true; // keep going

      _compact->extCompact->turnProg = tt[dir]; // put turn program in
      _compact->logic = L_TURNING;

      turn();

      return false; // drop out of script
}

bool Logic::fnArrived(uint32 scriptVar, uint32 b, uint32 c) {
      _compact->extCompact->leaving = (uint16)(scriptVar & 0xffff);
      _scriptVariables[scriptVar/4]++;
      return true;
}

bool Logic::fnLeaving(uint32 a, uint32 b, uint32 c) {
      _compact->extCompact->atWatch = 0;

      if (_compact->extCompact->leaving) {
            _scriptVariables[_compact->extCompact->leaving/4]--;
            _compact->extCompact->leaving = 0; // I shall do this only once
      }

      return true; // keep going
}

bool Logic::fnSetAlternate(uint32 scr, uint32 b, uint32 c) {
      _compact->extCompact->alt = (uint16)(scr & 0xffff);
      _compact->logic = L_ALT;
      return false;
}

bool Logic::fnAltSetAlternate(uint32 target, uint32 scr, uint32 c) {
      Compact *cpt = SkyEngine::fetchCompact(target);
      cpt->extCompact->alt = (uint16)(scr & 0xffff);
      cpt->logic = L_ALT;
      return false;
}

bool Logic::fnKillId(uint32 id, uint32 b, uint32 c) {
      if (id) {
            Compact *cpt = SkyEngine::fetchCompact(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(SkyEngine::fetchCompact(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->extCompact->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 = SkyEngine::fetchCompact(a);
      if (c) {
            c += speaker->extCompact->dir << 1;
            stdSpeak(speaker, b, c, speaker->extCompact->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 = SkyEngine::fetchCompact(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 = SkyEngine::fetchCompact(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 = SkyEngine::fetchCompact(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->extCompact->waitingFor = (uint16) id;
      stopAndWait();
      return true; // not sure about this
}

bool Logic::fnSendSync(uint32 mega, uint32 sync, uint32 c) {
      Compact *cpt = SkyEngine::fetchCompact(mega);
      cpt->sync = (uint16)(sync & 0xffff);
      return false;
}

bool Logic::fnSendFastSync(uint32 mega, uint32 sync, uint32 c) {
      Compact *cpt = SkyEngine::fetchCompact(mega);
      cpt->sync = (uint16)(sync & 0xffff);
      return true;
}

bool Logic::fnSendRequest(uint32 target, uint32 scr, uint32 c) {
      Compact *cpt = SkyEngine::fetchCompact(target);
      cpt->extCompact->request = (uint16)(scr & 0xffff);
      return false;
}

bool Logic::fnClearRequest(uint32 target, uint32 b, uint32 c) {
      Compact *cpt = SkyEngine::fetchCompact(target);
      cpt->extCompact->request = 0;
      return true;
}

bool Logic::fnCheckRequest(uint32 a, uint32 b, uint32 c) {
      /// check for interaction request
      
      if (!_compact->extCompact->request)
            return true;

      _compact->mode = C_ACTION_MODE; // into action mode

      _compact->extCompact->actionSub = _compact->extCompact->request;
      _compact->extCompact->actionSub_off = 0;

      _compact->extCompact->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 = SkyEngine::fetchCompact(47);
      cpt->status = ST_MOUSE + ST_FOREGROUND + ST_LOGIC + ST_RECREATE;
      cpt->screen = (uint16)(_scriptVariables[SCREEN] & 0xffff);

      cpt = SkyEngine::fetchCompact(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])
                  (SkyEngine::fetchCompact(_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 = SkyEngine::fetchCompact(
                        _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 = SkyEngine::fetchCompact(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 = SkyEngine::fetchCompact(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 = SkyEngine::fetchCompact(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 = SkyEngine::fetchCompact(sprite);
      cpt->status &= 0xfff8;
      cpt->status |= ST_BACKGROUND;
      return true;
}

bool Logic::fnSort(uint32 mega, uint32 b, uint32 c) {
      Compact *cpt = SkyEngine::fetchCompact(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 = SkyEngine::fetchCompact(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 = SkyEngine::fetchCompact(id);
      uint16 *rst = (uint16 *)SkyEngine::fetchCompact(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->grafixProg.ptrType = COMPACT;
      _compact->grafixProg.ptrTarget = animNo;
      _compact->grafixProg.pos = 0;
      
      //uint16 *animation = (uint16 *)SkyEngine::fetchCompact(animNo);
      //uint16 sprite = *animation++; // get sprite set
      //_compact->offset = sprite;
      _compact->offset = *SkyCompact::getGrafixPtr(_compact);
      //_compact->grafixProg = animation;
      _compact->grafixProg.pos++;
      _compact->logic = L_MOD_ANIMATE;
      anim();
      return false; // drop from script
}

bool Logic::fnSimpleMod(uint32 animSeqNo, uint32 b, uint32 c) {

      _compact->grafixProg.ptrType = COMPACT;
      _compact->grafixProg.ptrTarget = animSeqNo;
      _compact->grafixProg.pos = 0;
      //uint16 *animSeq = (uint16 *)SkyEngine::fetchCompact(animSeqNo);
      //_compact->offset = *animSeq++;
      //assert(*animSeq != 0);
      _compact->offset = *SkyCompact::getGrafixPtr(_compact);
      //_compact->grafixProg = animSeq;
      _compact->grafixProg.pos++;
      _compact->logic = L_SIMPLE_MOD;
      simpleAnim();
      return false;
}

bool Logic::fnRunFrames(uint32 sequenceNo, uint32 b, uint32 c) {
      _compact->grafixProg.ptrType = COMPACT;
      _compact->grafixProg.ptrTarget = sequenceNo;
      _compact->grafixProg.pos = 0;
      //uint16 *sequence = (uint16 *)SkyEngine::fetchCompact(sequenceNo);

      _compact->logic = L_FRAMES;
      //_compact->offset = *sequence++;
      _compact->offset = *SkyCompact::getGrafixPtr(_compact);
      _compact->grafixProg.pos++;
      //_compact->grafixProg = sequence;
      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->extCompact->megaSet += NEXT_MEGA_SET;
      return true;
}

bool Logic::fnDecMegaSet(uint32 a, uint32 b, uint32 c) {
      _compact->extCompact->megaSet -= NEXT_MEGA_SET;
      return true;
}

bool Logic::fnSetMegaSet(uint32 mega, uint32 setNo, uint32 c) {
      Compact *cpt = SkyEngine::fetchCompact(mega);
      cpt->extCompact->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 = SkyCompact::move_list[listNo];
      for (int i = 0; i < 2; i++) {
            if (!*p)
                  return true;
            Compact *cpt = SkyEngine::fetchCompact(*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 = SkyEngine::fetchCompact(id);
      _scriptVariables[RESULT] = cpt->screen == room ? 1 : 0;
      return true;
}

bool Logic::fnToggleMouse(uint32 a, uint32 b, uint32 c) {
      
      SkyEngine::fetchCompact(a)->status ^= ST_MOUSE;
      return true;
}

bool Logic::fnMouseOn(uint32 a, uint32 b, uint32 c) {
      //switch on the mouse highlight
      Compact *cpt = SkyEngine::fetchCompact(a);
      cpt->status |= ST_MOUSE;
      return true;
}

bool Logic::fnMouseOff(uint32 a, uint32 b, uint32 c) {
      //switch on (off??) the mouse highlight
      Compact *cpt = SkyEngine::fetchCompact(a);
      cpt->status &= ~ST_MOUSE;
      return true;
}

bool Logic::fnFetchX(uint32 id, uint32 b, uint32 c) {
      Compact *cpt = SkyEngine::fetchCompact(id);
      _scriptVariables[RESULT] = cpt->xcood;
      return true;
}

bool Logic::fnFetchY(uint32 id, uint32 b, uint32 c) {
      Compact *cpt = SkyEngine::fetchCompact(id);
      _scriptVariables[RESULT] = cpt->ycood;
      return true;
}

bool Logic::fnTestList(uint32 id, uint32 x, uint32 y) {
      _scriptVariables[RESULT] = 0; // assume fail
      uint16 *list = (uint16 *)SkyEngine::fetchCompact(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 = SkyEngine::fetchCompact(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 = SkyEngine::fetchCompact(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 *)SkyEngine::fetchCompact(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 = SkyEngine::fetchCompact(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 *)SkyEngine::fetchCompact(id);
      Compact *cpt = SkyEngine::fetchCompact(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);

      //_currentSection = 0xFF; // force music-, sound- and gridreload

      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 = SkyEngine::fetchCompact(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 = SkyEngine::fetchCompact(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 = SkyEngine::fetchCompact(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 = SkyEngine::fetchCompact(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) {
      _skyControl->showGameQuitMsg();  // calls _system->quit()
      return true;
}

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) {

      uint16 *animPtr;

      animNum += target->extCompact->megaSet / NEXT_MEGA_SET;
      animNum &= 0xFF;
      if (TalkAnims::animTalkTableIsPointer[animNum]) { //is it a pointer? 
            //animPtr = (uint16 *)TalkAnims::animTalkTablePtr[animNum];
            target->grafixProg.ptrType = TALKTABLE;
            target->grafixProg.ptrTarget = animNum;
      } else {    //then it must be a value
            //animPtr = (uint16 *)SkyEngine::fetchCompact(TalkAnims::animTalkTableVal[animNum]);
            target->grafixProg.ptrType = COMPACT;
            target->grafixProg.ptrTarget = TalkAnims::animTalkTableVal[animNum];
      }
      target->grafixProg.pos = 0;
      animPtr = SkyCompact::getGrafixPtr(target);
      
      if (animPtr) {
            target->offset = *animPtr++;
            target->getToFlag = *animPtr++;
            target->grafixProg.pos += 2;
      } else {
            target->grafixProg.ptrType = PTR_NULL;
      }

      bool speechUsed = false;
      // startSpeech returns false if no speech file exists for that text
      if (SkyEngine::isCDVersion())
            speechUsed = _skySound->startSpeech((uint16)textNum);

      // if sky is configured to speech-only return now - except if we're running another
      // language than english
      if (speechUsed && (!(SkyEngine::_systemVars.systemFlags & SF_ALLOW_TEXT))) {
            target->extCompact->spTime = 10;
            target->logic = L_TALK; 
            return ;
      }

      //now form the text sprite
      struct lowTextManager_t textInfo;
      textInfo = _skyText->lowTextManager(textNum, FIXED_TEXT_WIDTH, 0, (uint8)target->extCompact->spColour, true);
      Compact *textCompact = SkyEngine::fetchCompact(textInfo.compactNum);
      target->extCompact->spTextId = textInfo.compactNum;   //So we know what text to kill
      byte *textGfx = textInfo.textData;

      //create the x coordinate for the speech text
      //we need the talkers sprite information
      textCompact->screen = target->screen;     //put our screen in

      if (_scriptVariables[SCREEN] == target->screen) { // Only use coordinates if we are on the current screen 
            //talking on-screen
            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->extCompact->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 (speechUsed) target->extCompact->spTime = 10;
      else target->extCompact->spTime = (uint16)_skyText->_dtLetters + 5;
      target->logic = L_TALK; 
}

} // End of namespace Sky

Generated by  Doxygen 1.6.0   Back to index