mirror of
https://github.com/scummvm/scummvm.git
synced 2026-06-20 05:45:29 +00:00
Returning to launcher via ScummVM popup menu would retain the values of _systemVars This would cause relaunching the game (after returning back to launcher) to have wrong value for eg. pastIntro, thus allowing "Save" from the ScummVM popup menu during the intro sequence.
2591 lines
65 KiB
C++
2591 lines
65 KiB
C++
/* 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.
|
|
*
|
|
*/
|
|
|
|
#include "common/endian.h"
|
|
#include "common/rect.h"
|
|
#include "common/textconsole.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)
|
|
: _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() {
|
|
delete _skyGrid;
|
|
delete _skyAutoRoute;
|
|
|
|
for (int i = 0; i < ARRAYSIZE(_moduleList); i++)
|
|
if (_moduleList[i])
|
|
free(_moduleList[i]);
|
|
}
|
|
|
|
void Logic::initScreen0() {
|
|
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() {
|
|
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);
|
|
SkyCompact::setSub(_compact, mode + 2, 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 (isCollision(_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 (isCollision(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::setSub(_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::setSub(_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::setSub(_compact, _compact->mode, _compact->alt);
|
|
SkyCompact::setSub(_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 && isCollision(cpt))
|
|
return;
|
|
|
|
// we are free, continue processing the script
|
|
|
|
// restart script one level below
|
|
SkyCompact::setSub(_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();
|
|
}
|
|
|
|
/** Checks if the currently processed object in _compact collides
|
|
with the one given as parameter */
|
|
bool Logic::isCollision(Compact *other) {
|
|
MegaSet *thisMegaSet = SkyCompact::getMegaSet(_compact);
|
|
MegaSet *otherMegaSet = SkyCompact::getMegaSet(other);
|
|
|
|
// target's base coordinates
|
|
uint16 otherX = other->xcood & ~7;
|
|
uint16 otherY = other->ycood & ~7;
|
|
if ((_compact->dir == UPY) || (_compact->dir == DOWNY)) { // If we're looking up or down...
|
|
otherX -= thisMegaSet->colOffset; // ...then compensate inner otherX offsets
|
|
otherX += otherMegaSet->colOffset;
|
|
}
|
|
|
|
if ((_compact->dir == UPY) || (_compact->dir == DOWNY)) {
|
|
// Check X coordinate, same for facing up or down
|
|
if (otherX + otherMegaSet->colWidth < _compact->xcood) // their rightmost
|
|
return false; // other is left of us
|
|
|
|
if (otherX - thisMegaSet->colWidth >= _compact->xcood) // our left, their right
|
|
return false; // other is right of us
|
|
// Check Y coordinate according to actual direction
|
|
if (_compact->dir == UPY) {
|
|
if (otherY + 8 == _compact->ycood)
|
|
return true;
|
|
if (otherY + 16 == _compact->ycood)
|
|
return true;
|
|
} else {
|
|
if (otherY - 8 == _compact->ycood)
|
|
return true;
|
|
if (otherY - 16 == _compact->ycood)
|
|
return true;
|
|
}
|
|
} else {
|
|
// Facing left, right (or talking, which probably never happens)
|
|
if (otherY != _compact->ycood)
|
|
return false;
|
|
if (_compact->dir == LEFTY) { // looking left
|
|
if (otherX + otherMegaSet->lastChr == _compact->xcood)
|
|
return true;
|
|
if (otherX + otherMegaSet->lastChr - 8 == _compact->xcood)
|
|
return true;
|
|
} else {
|
|
if (otherX - thisMegaSet->lastChr == _compact->xcood)
|
|
return true;
|
|
if (otherX - thisMegaSet->lastChr - 8 == _compact->xcood)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
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;
|
|
|
|
SkyCompact::setSub(_compact, _compact->mode, _compact->stopScript);
|
|
SkyCompact::setSub(_compact, _compact->mode + 2, 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;
|
|
|
|
if (scrNum == MENU_SELECT || (scrNum >= LINC_MENU_SELECT && scrNum <= DOC_MENU_SELECT)) {
|
|
// HACK: See patch #1689516 for details. The short story:
|
|
// The user has clicked on an inventory item. We update the
|
|
// mouse cursor instead of waiting for the script to update it.
|
|
// In the original game the cursor is just updated when the mouse
|
|
// moves away the item, but it's unintuitive.
|
|
fnCrossMouse(0, 0, 0);
|
|
}
|
|
|
|
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) {
|
|
do {
|
|
bool restartScript = false;
|
|
|
|
/// process a script
|
|
/// low level interface to interpreter
|
|
|
|
uint16 moduleNo = scriptNo >> 12;
|
|
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;
|
|
|
|
debug(3, "Doing Script: %d:%d:%x", moduleNo, scriptNo & 0xFFF, offset ? (offset - moduleStart[scriptNo & 0xFFF]) : 0);
|
|
|
|
// WORKAROUND for bug #3149412: "Invalid Mode when giving shades to travel agent"
|
|
// Using the dark glasses on Trevor (travel agent) multiple times in succession would
|
|
// wreck the trevor compact's mode, as the script in question doesn't account for using
|
|
// this item at this point in the game (you will only have it here if you play the game
|
|
// in an unusual way) and thus would loop indefinitely / never drop out.
|
|
// To prevent this, we trigger the generic response by pretending we're using an item
|
|
// which the script /does/ handle.
|
|
if (scriptNo == TREVOR_SPEECH && _scriptVariables[OBJECT_HELD] == IDO_SHADES)
|
|
_scriptVariables[OBJECT_HELD] = IDO_GLASS;
|
|
|
|
|
|
// 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;
|
|
|
|
while(!restartScript) {
|
|
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();
|
|
// fall through
|
|
case 2:
|
|
b = pop();
|
|
// fall through
|
|
case 1:
|
|
a = pop();
|
|
// fall through
|
|
default:
|
|
break;
|
|
}
|
|
|
|
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;
|
|
restartScript = true;
|
|
break;
|
|
default:
|
|
error("Unknown script command: %d", command);
|
|
}
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
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::setSub(_compact, _compact->mode, cpt->actionScript);
|
|
SkyCompact::setSub(_compact, _compact->mode + 2, 0);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Logic::fnStartSub(uint32 scr, uint32 b, uint32 c) {
|
|
_compact->mode += 4;
|
|
SkyCompact::setSub(_compact, _compact->mode, scr & 0xffff);
|
|
SkyCompact::setSub(_compact, _compact->mode + 2, scr >> 16);
|
|
return false;
|
|
}
|
|
|
|
bool Logic::fnTheyStartSub(uint32 mega, uint32 scr, uint32 c) {
|
|
Compact *cpt = _skyCompact->fetchCpt(mega);
|
|
cpt->mode += 4;
|
|
SkyCompact::setSub(cpt, cpt->mode, scr & 0xffff);
|
|
SkyCompact::setSub(cpt, cpt->mode + 2, 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::setSub(_compact, _compact->mode, *(getToTable + 1));
|
|
SkyCompact::setSub(_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) {
|
|
/* WORKAROUND for #2687172: When Mrs. Piermont is talking
|
|
on the phone in her apartment, ignore her fnSpeakMe calls
|
|
on other screens, as the lack of speech files for these lines
|
|
will cause Foster's speech to be aborted if the timing is bad.
|
|
*/
|
|
if (targetId == ID_DANIELLE && animNum == 0x9B && Logic::_scriptVariables[SCREEN] != 38) {
|
|
return false;
|
|
}
|
|
|
|
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++;
|
|
|
|
DisplayedText 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;
|
|
uint32 height = ((DataFileHeader *)data)->s_height;
|
|
|
|
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 += height;
|
|
}
|
|
|
|
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->changeTextSpriteColor(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) {
|
|
/// initialize 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, INITIALIZE 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())
|
|
Engine::quitGame();
|
|
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* This function is called by the script logic
|
|
* even when the intro sequence has completed without skipping it.
|
|
* This means that it (also) gets called at the point after the intro
|
|
* when Foster in on the top level of the factory and finished his monologue.
|
|
*/
|
|
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) {
|
|
DisplayedText 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) {
|
|
DisplayedText 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;
|
|
|
|
DisplayedText 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) {
|
|
Engine::quitGame();
|
|
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) {
|
|
debug("fnPrintf(%d, %d, %d)", a, b, c);
|
|
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
|
|
DisplayedText textInfo;
|
|
textInfo = _skyText->lowTextManager(textNum, FIXED_TEXT_WIDTH, 0, (uint8)target->spColor, 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 + ((DataFileHeader *)targetGfx)->s_offset_x;
|
|
uint16 width = (((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 + ((DataFileHeader *)targetGfx)->s_offset_y - 6 - ((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
|