Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

logic.cpp

/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-11-1/engines/sky/logic.cpp $
 * $Id: logic.cpp 30944 2008-02-23 22:50:18Z sev $
 *
 */


#include "common/endian.h"
#include "common/rect.h"
#include "common/events.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) {
      g_system->getEventManager()->registerRandomSource(_rnd, "sky");

      _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 not require collision checks
            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(800);

      _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);


      // Set the focus region to that area
      // Calculate the point where the character is
      int x = target->xcood - TOP_LEFT_X;
      int y = target->ycood - TOP_LEFT_Y;
      // TODO: Make the box size change based on the object that has the focus
      _skyScreen->setFocusRectangle(Common::Rect::center(x, y, 192, 128));


      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

Generated by  Doxygen 1.6.0   Back to index