aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sword2/anims.cpp262
-rw-r--r--sword2/build_display.cpp83
-rw-r--r--sword2/console.cpp12
-rw-r--r--sword2/controls.cpp6
-rw-r--r--sword2/driver/animation.cpp310
-rw-r--r--sword2/driver/animation.h7
-rw-r--r--sword2/driver/d_sound.cpp472
-rw-r--r--sword2/driver/d_sound.h128
-rw-r--r--sword2/events.cpp90
-rw-r--r--sword2/function.cpp2859
-rw-r--r--sword2/icons.cpp27
-rw-r--r--sword2/layers.cpp12
-rw-r--r--sword2/logic.cpp89
-rw-r--r--sword2/logic.h27
-rw-r--r--sword2/mouse.cpp257
-rw-r--r--sword2/resman.cpp5
-rw-r--r--sword2/save_rest.cpp74
-rw-r--r--sword2/scroll.cpp37
-rw-r--r--sword2/sound.cpp373
-rw-r--r--sword2/sound.h192
-rw-r--r--sword2/speech.cpp1015
-rw-r--r--sword2/startup.cpp61
-rw-r--r--sword2/startup.h30
-rw-r--r--sword2/sword2.cpp12
-rw-r--r--sword2/sword2.h44
-rw-r--r--sword2/sync.cpp54
-rw-r--r--sword2/walker.cpp639
27 files changed, 3435 insertions, 3742 deletions
diff --git a/sword2/anims.cpp b/sword2/anims.cpp
index fe6d2ee303..385ff1885b 100644
--- a/sword2/anims.cpp
+++ b/sword2/anims.cpp
@@ -34,50 +34,12 @@
#include "sword2/maketext.h"
#include "sword2/memory.h"
#include "sword2/resman.h"
+#include "sword2/sound.h"
#include "sword2/driver/animation.h"
#include "sword2/driver/d_draw.h"
-#include "sword2/driver/d_sound.h"
namespace Sword2 {
-int32 Logic::fnAnim(int32 *params) {
- // params: 0 pointer to object's logic structure
- // 1 pointer to object's graphic structure
- // 2 resource id of animation file
-
- // 0 means normal forward anim
- return animate(params, false);
-}
-
-int32 Logic::fnReverseAnim(int32 *params) {
- // params: 0 pointer to object's logic structure
- // 1 pointer to object's graphic structure
- // 2 resource id of animation file
-
- // 1 means reverse anim
- return animate(params, true);
-}
-
-int32 Logic::fnMegaTableAnim(int32 *params) {
- // params: 0 pointer to object's logic structure
- // 1 pointer to object's graphic structure
- // 2 pointer to object's mega structure
- // 3 pointer to animation table
-
- // 0 means normal forward anim
- return megaTableAnimate(params, false);
-}
-
-int32 Logic::fnReverseMegaTableAnim(int32 *params) {
- // params: 0 pointer to object's logic structure
- // 1 pointer to object's graphic structure
- // 2 pointer to object's mega structure
- // 3 pointer to animation table
-
- // 1 means reverse anim
- return megaTableAnimate(params, true);
-}
-
int32 Logic::animate(int32 *params, bool reverse) {
// params: 0 pointer to object's logic structure
// 1 pointer to object's graphic structure
@@ -217,34 +179,6 @@ int32 Logic::megaTableAnimate(int32 *params, bool reverse) {
return animate(pars, reverse);
}
-int32 Logic::fnSetFrame(int32 *params) {
- // params: 0 pointer to object's graphic structure
- // 1 resource id of animation file
- // 2 frame flag (0=first 1=last)
-
- int32 res = params[1];
- assert(res);
-
- // open the resource (& check it's valid)
- byte *anim_file = _vm->_resman->openResource(res);
-
- StandardHeader *head = (StandardHeader *) anim_file;
- assert(head->fileType == ANIMATION_FILE);
-
- // set up pointer to the animation header
- AnimHeader *anim_head = _vm->fetchAnimHeader(anim_file);
-
- // set up anim resource in graphic object
- ObjectGraphic *ob_graphic = (ObjectGraphic *) _vm->_memory->decodePtr(params[0]);
-
- ob_graphic->anim_resource = res;
- ob_graphic->anim_pc = params[2] ? anim_head->noAnimFrames - 1 : 0;
-
- // Close the anim file and drop out of script
- _vm->_resman->closeResource(ob_graphic->anim_resource);
- return IR_CONT;
-}
-
void Logic::setSpriteStatus(uint32 sprite, uint32 type) {
ObjectGraphic *ob_graphic = (ObjectGraphic *) _vm->_memory->decodePtr(sprite);
@@ -261,80 +195,6 @@ void Logic::setSpriteShading(uint32 sprite, uint32 type) {
ob_graphic->type = (ob_graphic->type & 0x0000ffff) | type;
}
-int32 Logic::fnNoSprite(int32 *params) {
- // params: 0 pointer to object's graphic structure
- setSpriteStatus(params[0], NO_SPRITE);
- return IR_CONT;
-}
-
-int32 Logic::fnBackPar0Sprite(int32 *params) {
- // params: 0 pointer to object's graphic structure
- setSpriteStatus(params[0], BGP0_SPRITE);
- return IR_CONT;
-}
-
-int32 Logic::fnBackPar1Sprite(int32 *params) {
- // params: 0 pointer to object's graphic structure
- setSpriteStatus(params[0], BGP1_SPRITE);
- return IR_CONT;
-}
-
-int32 Logic::fnBackSprite(int32 *params) {
- // params: 0 pointer to object's graphic structure
- setSpriteStatus(params[0], BACK_SPRITE);
- return IR_CONT;
-}
-
-int32 Logic::fnSortSprite(int32 *params) {
- // params: 0 pointer to object's graphic structure
- setSpriteStatus(params[0], SORT_SPRITE);
- return IR_CONT;
-}
-
-int32 Logic::fnForeSprite(int32 *params) {
- // params: 0 pointer to object's graphic structure
- setSpriteStatus(params[0], FORE_SPRITE);
- return IR_CONT;
-}
-
-int32 Logic::fnForePar0Sprite(int32 *params) {
- // params: 0 pointer to object's graphic structure
- setSpriteStatus(params[0], FGP0_SPRITE);
- return IR_CONT;
-}
-
-int32 Logic::fnForePar1Sprite(int32 *params) {
- // params: 0 pointer to object's graphic structure
- setSpriteStatus(params[0], FGP1_SPRITE);
- return IR_CONT;
-}
-
-int32 Logic::fnShadedSprite(int32 *params) {
- // params: 0 pointer to object's graphic structure
- setSpriteShading(params[0], SHADED_SPRITE);
- return IR_CONT;
-}
-
-int32 Logic::fnUnshadedSprite(int32 *params) {
- // params: 0 pointer to object's graphic structure
- setSpriteShading(params[0], UNSHADED_SPRITE);
- return IR_CONT;
-}
-
-int32 Logic::fnAddSequenceText(int32 *params) {
- // params: 0 text number
- // 1 frame number to start the text displaying
- // 2 frame number to stop the text dispalying
-
- assert(_sequenceTextLines < MAX_SEQUENCE_TEXT_LINES);
-
- _sequenceTextList[_sequenceTextLines].textNumber = params[0];
- _sequenceTextList[_sequenceTextLines].startFrame = params[1];
- _sequenceTextList[_sequenceTextLines].endFrame = params[2];
- _sequenceTextLines++;
- return IR_CONT;
-}
-
void Logic::createSequenceSpeech(MovieTextObject *sequenceText[]) {
uint32 line;
FrameHeader *frame;
@@ -462,124 +322,4 @@ void Logic::clearSequenceSpeech(MovieTextObject *sequenceText[]) {
_sequenceTextLines = 0;
}
-int32 Logic::fnSmackerLeadIn(int32 *params) {
- // params: 0 id of lead-in music
- byte *leadIn = _vm->_resman->openResource(params[0]);
-
- StandardHeader *header = (StandardHeader *) leadIn;
- assert(header->fileType == WAV_FILE);
-
- leadIn += sizeof(StandardHeader);
-
- uint32 len = _vm->_resman->fetchLen(params[0]) - sizeof(StandardHeader);
- uint32 rv = _vm->_sound->playFx(0, len, leadIn, 0, 0, RDSE_FXLEADIN);
-
- if (rv)
- debug(5, "SFX ERROR: playFx() returned %.8x", rv);
-
- _vm->_resman->closeResource(params[0]);
-
- // fade out any music that is currently playing
- fnStopMusic(NULL);
- return IR_CONT;
-}
-
-int32 Logic::fnSmackerLeadOut(int32 *params) {
- // params: 0 id of lead-out music
-
- // ready for use in fnPlaySequence
- _smackerLeadOut = params[0];
- return IR_CONT;
-}
-
-int32 Logic::fnPlaySequence(int32 *params) {
- // params: 0 pointer to null-terminated ascii filename
- // 1 number of frames in the sequence, used for PSX.
-
- char filename[30];
- MovieTextObject *sequenceSpeechArray[MAX_SEQUENCE_TEXT_LINES + 1];
- byte *leadOut = NULL;
- uint32 leadOutLen = 0;
-
- // The original code had some #ifdef blocks for skipping or muting the
- // cutscenes - fondly described as "the biggest fudge in the history
- // of computer games" - but at the very least we want to show the
- // cutscene subtitles, so I removed them.
-
- debug(5, "fnPlaySequence(\"%s\");", (const char *) _vm->_memory->decodePtr(params[0]));
-
- // add the appropriate file extension & play it
-
- strcpy(filename, (const char *) _vm->_memory->decodePtr(params[0]));
-
- // Write to walkthrough file (zebug0.txt)
- debug(5, "PLAYING SEQUENCE \"%s\"", filename);
-
- // now create the text sprites, if any
-
- if (_sequenceTextLines)
- createSequenceSpeech(sequenceSpeechArray);
-
- // open the lead-out music resource, if there is one
-
- if (_smackerLeadOut) {
- leadOut = _vm->_resman->openResource(_smackerLeadOut);
-
- StandardHeader *header = (StandardHeader *) leadOut;
- assert(header->fileType == WAV_FILE);
-
- leadOut += sizeof(StandardHeader);
-
- leadOutLen = _vm->_resman->fetchLen(_smackerLeadOut) - sizeof(StandardHeader);
- }
-
- // don't want to carry on streaming game music when smacker starts!
- fnStopMusic(NULL);
-
- // pause sfx during sequence, except the one used for lead-in music
- _vm->_sound->pauseFxForSequence();
-
- MoviePlayer player(_vm);
- uint32 rv;
-
- if (_sequenceTextLines && !_scriptVars[DEMO])
- rv = player.play(filename, sequenceSpeechArray, leadOut, leadOutLen);
- else
- rv = player.play(filename, NULL, leadOut, leadOutLen);
-
- // check the error return-value
- if (rv)
- debug(5, "MoviePlayer.play(\"%s\") returned 0x%.8x", filename, rv);
-
- // unpause sound fx again, in case we're staying in same location
- _vm->_sound->unpauseFx();
-
- // close the lead-out music resource
-
- if (_smackerLeadOut) {
- _vm->_resman->closeResource(_smackerLeadOut);
- _smackerLeadOut = 0;
- }
-
- // now clear the text sprites, if any
-
- if (_sequenceTextLines)
- clearSequenceSpeech(sequenceSpeechArray);
-
- // now clear the screen in case the Sequence was quitted (using ESC)
- // rather than fading down to black
-
- _vm->_graphics->clearScene();
-
- // zero the entire palette in case we're about to fade up!
-
- byte pal[4 * 256];
-
- memset(pal, 0, sizeof(pal));
- _vm->_graphics->setPalette(0, 256, pal, RDPAL_INSTANT);
-
- debug(5, "fnPlaySequence FINISHED");
- return IR_CONT;
-}
-
} // End of namespace Sword2
diff --git a/sword2/build_display.cpp b/sword2/build_display.cpp
index 1a0bc5cbf1..98f738f75b 100644
--- a/sword2/build_display.cpp
+++ b/sword2/build_display.cpp
@@ -557,18 +557,6 @@ void Sword2Engine::registerFrame(int32 *params, BuildUnit *build_unit) {
_resman->closeResource(ob_graph->anim_resource);
}
-int32 Logic::fnRegisterFrame(int32 *params) {
- // this call would be made from an objects service script 0
-
- // params: 0 pointer to mouse structure or NULL for no write to
- // mouse list (non-zero means write sprite-shape to
- // mouse list)
- // 1 pointer to graphic structure
- // 2 pointer to mega structure or NULL if not a mega
-
- return _vm->registerFrame(params);
-}
-
int32 Sword2Engine::registerFrame(int32 *params) {
ObjectGraphic *ob_graph = (ObjectGraphic *) _memory->decodePtr(params[1]);
@@ -640,58 +628,6 @@ void Sword2Engine::startNewPalette(void) {
_thisScreen.new_palette = 0;
}
-int32 Logic::fnUpdatePlayerStats(int32 *params) {
- // engine needs to know certain info about the player
-
- // params: 0 pointer to mega structure
-
- ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[0]);
-
- _vm->_thisScreen.player_feet_x = ob_mega->feet_x;
- _vm->_thisScreen.player_feet_y = ob_mega->feet_y;
-
- // for the script
- _scriptVars[PLAYER_FEET_X] = ob_mega->feet_x;
- _scriptVars[PLAYER_FEET_Y] = ob_mega->feet_y;
- _scriptVars[PLAYER_CUR_DIR] = ob_mega->current_dir;
- _scriptVars[SCROLL_OFFSET_X] = _vm->_thisScreen.scroll_offset_x;
-
- debug(5, "fnUpdatePlayerStats: %d %d", ob_mega->feet_x, ob_mega->feet_y);
-
- return IR_CONT;
-}
-
-int32 Logic::fnFadeDown(int32 *params) {
- // NONE means up! can only be called when screen is fully faded up -
- // multiple calls wont have strange effects
-
- // params: none
-
- if (_vm->_graphics->getFadeStatus() == RDFADE_NONE)
- _vm->_graphics->fadeDown();
-
- return IR_CONT;
-}
-
-int32 Logic::fnFadeUp(int32 *params) {
- // params: none
-
- _vm->_graphics->waitForFade();
-
- if (_vm->_graphics->getFadeStatus() == RDFADE_BLACK)
- _vm->_graphics->fadeUp();
-
- return IR_CONT;
-}
-
-int32 Logic::fnSetPalette(int32 *params) {
- // params: 0 resource number of palette file, or 0 if it's to be
- // the palette from the current screen
-
- _vm->setFullPalette(params[0]);
- return IR_CONT;
-}
-
void Sword2Engine::setFullPalette(int32 palRes) {
// fudge for hut interior
// - unpausing should restore last palette as normal (could be screen
@@ -763,23 +699,4 @@ void Sword2Engine::setFullPalette(int32 palRes) {
_lastPaletteRes = palRes;
}
-int32 Logic::fnRestoreGame(int32 *params) {
- // params: none
- return IR_CONT;
-}
-
-int32 Logic::fnChangeShadows(int32 *params) {
- // params: none
-
- // if last screen was using a shading mask (see below)
- if (_vm->_thisScreen.mask_flag) {
- uint32 rv = _vm->_graphics->closeLightMask();
- if (rv)
- error("Driver Error %.8x", rv);
- _vm->_thisScreen.mask_flag = false;
- }
-
- return IR_CONT;
-}
-
} // End of namespace Sword2
diff --git a/sword2/console.cpp b/sword2/console.cpp
index 038f986bf0..4c0ab2f550 100644
--- a/sword2/console.cpp
+++ b/sword2/console.cpp
@@ -26,8 +26,8 @@
#include "sword2/maketext.h"
#include "sword2/memory.h"
#include "sword2/resman.h"
+#include "sword2/sound.h"
#include "sword2/driver/d_draw.h"
-#include "sword2/driver/d_sound.h"
#include "common/debugger.cpp"
@@ -206,7 +206,7 @@ bool Debugger::Cmd_ResList(int argc, const char **argv) {
}
bool Debugger::Cmd_Starts(int argc, const char **argv) {
- _vm->_logic->conPrintStartMenu();
+ _vm->conPrintStartMenu();
return true;
}
@@ -218,7 +218,7 @@ bool Debugger::Cmd_Start(int argc, const char **argv) {
return true;
}
- _vm->_logic->conStart(atoi(argv[1]));
+ _vm->conStart(atoi(argv[1]));
_vm->_graphics->setPalette(187, 1, pal, RDPAL_INSTANT);
return true;
}
@@ -466,7 +466,7 @@ bool Debugger::Cmd_AnimTest(int argc, const char **argv) {
}
// Automatically do "s 32" to run the animation testing start script
- _vm->_logic->conStart(32);
+ _vm->conStart(32);
// Same as typing "VAR 912 <value>" at the console
varSet(912, atoi(argv[1]));
@@ -482,7 +482,7 @@ bool Debugger::Cmd_TextTest(int argc, const char **argv) {
}
// Automatically do "s 33" to run the text/speech testing start script
- _vm->_logic->conStart(33);
+ _vm->conStart(33);
// Same as typing "VAR 1230 <value>" at the console
varSet(1230, atoi(argv[1]));
@@ -501,7 +501,7 @@ bool Debugger::Cmd_LineTest(int argc, const char **argv) {
}
// Automatically do "s 33" to run the text/speech testing start script
- _vm->_logic->conStart(33);
+ _vm->conStart(33);
// Same as typing "VAR 1230 <value>" at the console
varSet(1230, atoi(argv[1]));
diff --git a/sword2/controls.cpp b/sword2/controls.cpp
index 095d814ff6..5d2936b006 100644
--- a/sword2/controls.cpp
+++ b/sword2/controls.cpp
@@ -28,8 +28,8 @@
#include "sword2/logic.h"
#include "sword2/resman.h"
#include "sword2/router.h"
+#include "sword2/sound.h"
#include "sword2/driver/d_draw.h"
-#include "sword2/driver/d_sound.h"
#define MAX_STRING_LEN 64 // 20 was too low; better to be safe ;)
#define CHARACTER_OVERLAP 2 // overlap characters by 3 pixels
@@ -1029,7 +1029,6 @@ public:
_mixer->setVolumeForSoundType(SoundMixer::kMusicAudioDataType, _musicSlider->getValue());
_mixer->setVolumeForSoundType(SoundMixer::kSpeechAudioDataType, _speechSlider->getValue());
_mixer->setVolumeForSoundType(SoundMixer::kSFXAudioDataType, _fxSlider->getValue());
- _gui->_vm->_sound->buildPanTable(_gui->_stereoReversed);
_gui->updateGraphicsLevel(_gfxSlider->getValue());
@@ -1515,7 +1514,6 @@ void Gui::readOptionSettings(void) {
_vm->_sound->muteMusic(ConfMan.getBool("music_mute"));
_vm->_sound->muteSpeech(ConfMan.getBool("speech_mute"));
_vm->_sound->muteFx(ConfMan.getBool("sfx_mute"));
- _vm->_sound->buildPanTable(_stereoReversed);
}
void Gui::writeOptionSettings(void) {
@@ -1588,7 +1586,7 @@ void Gui::restartControl(void) {
// Restart the game. To do this, we must...
// Stop music instantly!
- _vm->killMusic();
+ _vm->_sound->stopMusic();
// In case we were dead - well we're not anymore!
Logic::_scriptVars[DEAD] = 0;
diff --git a/sword2/driver/animation.cpp b/sword2/driver/animation.cpp
index 7777524dc1..ae1d6cc8ef 100644
--- a/sword2/driver/animation.cpp
+++ b/sword2/driver/animation.cpp
@@ -28,9 +28,10 @@
#include "sword2/sword2.h"
#include "sword2/maketext.h"
+#include "sword2/resman.h"
+#include "sword2/sound.h"
#include "sword2/driver/animation.h"
#include "sword2/driver/d_draw.h"
-#include "sword2/driver/d_sound.h"
#include "sword2/driver/menu.h"
#include "sword2/driver/render.h"
@@ -43,7 +44,6 @@ AnimationState::AnimationState(Sword2Engine *vm)
AnimationState::~AnimationState() {
}
-
#ifdef BACKEND_8BIT
void AnimationState::setPalette(byte *pal) {
@@ -168,12 +168,63 @@ void MoviePlayer::drawTextObject(AnimationState *anim, MovieTextObject *obj) {
* @param musicOut lead-out music
*/
-int32 MoviePlayer::play(const char *filename, MovieTextObject *text[], byte *musicOut, uint32 musicOutLen) {
+int32 MoviePlayer::play(const char *filename, MovieTextObject *text[], int32 leadInRes, int32 leadOutRes) {
+ PlayingSoundHandle leadInHandle;
+
// This happens if the user quits during the "eye" smacker
if (_vm->_quit)
return RD_OK;
+ if (leadInRes) {
+ byte *leadIn = _vm->_resman->openResource(leadInRes);
+ uint32 leadInLen = _vm->_resman->fetchLen(leadInRes) - sizeof(StandardHeader);
+ StandardHeader *header = (StandardHeader *) leadIn;
+
+ assert(header->fileType == WAV_FILE);
+
+ leadIn += sizeof(StandardHeader);
+
+ _vm->_sound->playFx(&leadInHandle, leadIn, leadInLen, SoundMixer::kMaxChannelVolume, 0, false, SoundMixer::kMusicAudioDataType);
+ }
+
+ byte *leadOut = NULL;
+ uint32 leadOutLen = 0;
+
+ if (leadOutRes) {
+ leadOut = _vm->_resman->openResource(leadOutRes);
+ leadOutLen = _vm->_resman->fetchLen(leadOutRes) - sizeof(StandardHeader);
+ StandardHeader *header = (StandardHeader *) leadOut;
+
+ assert(header->fileType == WAV_FILE);
+
+ leadOut += sizeof(StandardHeader);
+ }
+
#ifdef USE_MPEG2
+ playMPEG(filename, text, leadOut, leadOutLen);
+#else
+ // No MPEG2? Use the old 'Narration Only' hack
+ playDummy(filename, text, leadOut);
+#endif
+
+ _vm->_mixer->stopHandle(leadInHandle);
+
+ // Wait for the lead-out to stop, if there is any.
+ while (_leadOutHandle.isActive()) {
+ _vm->_graphics->updateDisplay();
+ _vm->_system->delayMillis(30);
+ }
+
+ if (leadInRes)
+ _vm->_resman->closeResource(leadInRes);
+
+ if (leadOutRes)
+ _vm->_resman->closeResource(leadOutRes);
+
+ return RD_OK;
+}
+
+void MoviePlayer::playMPEG(const char *filename, MovieTextObject *text[], byte *leadOut, uint32 leadOutLen) {
uint frameCounter = 0, textCounter = 0;
PlayingSoundHandle handle;
bool skipCutscene = false, textVisible = false;
@@ -188,8 +239,8 @@ int32 MoviePlayer::play(const char *filename, MovieTextObject *text[], byte *mus
if (!anim->init(filename)) {
delete anim;
// Missing Files? Use the old 'Narration Only' hack
- playDummy(filename, text, musicOut, musicOutLen);
- return RD_OK;
+ playDummy(filename, text, leadOut, leadOutLen);
+ return;
}
#ifndef BACKEND_8BIT
@@ -249,11 +300,10 @@ int32 MoviePlayer::play(const char *filename, MovieTextObject *text[], byte *mus
}
anim->updateScreen();
-
frameCounter++;
- if (frameCounter == leadOutFrame && musicOut)
- _vm->_sound->playFx(0, musicOutLen, musicOut, 0, 0, RDSE_FXLEADOUT);
+ if (frameCounter == leadOutFrame && leadOut)
+ _vm->_sound->playFx(&_leadOutHandle, leadOut, leadOutLen, SoundMixer::kMaxChannelVolume, 0, false, SoundMixer::kMusicAudioDataType);
OSystem::Event event;
while (_sys->pollEvent(event)) {
@@ -299,10 +349,9 @@ int32 MoviePlayer::play(const char *filename, MovieTextObject *text[], byte *mus
anim->updateScreen();
- // Wait for the voice to stop playing. This is to make sure
- // that we don't cut off the speech in mid-sentence, and - even
- // more importantly - that we don't free the sound buffer while
- // it's in use.
+ // Wait for the voice to stop playing. This is to make sure that we
+ // don't cut off the speech in mid-sentence, and - even more
+ // importantly - that we don't free the sound buffer while it's in use.
if (skipCutscene)
_snd->stopHandle(handle);
@@ -313,30 +362,12 @@ int32 MoviePlayer::play(const char *filename, MovieTextObject *text[], byte *mus
}
// Clear the screen again
-
anim->clearScreen();
anim->updateScreen();
_vm->_graphics->setPalette(0, 256, oldPal, RDPAL_INSTANT);
delete anim;
-
- // Wait for the lead-out to stop, if there is any.
- _vm->_sound->waitForLeadOut();
-
- // Lead-in and lead-out music are, as far as I can tell, only used for
- // the animated cut-scenes, so this seems like a good place to close
- // both of them.
-
- _vm->_sound->stopFx(-1);
- _vm->_sound->stopFx(-2);
-
- return RD_OK;
-#else
- // No MPEG2? Use the old 'Narration Only' hack
- playDummy(filename, text, musicOut, musicOutLen);
- return RD_OK;
-#endif
}
/**
@@ -344,161 +375,152 @@ int32 MoviePlayer::play(const char *filename, MovieTextObject *text[], byte *mus
* are missing.
*/
-int32 MoviePlayer::playDummy(const char *filename, MovieTextObject *text[], byte *musicOut, uint32 musicOutLen) {
+void MoviePlayer::playDummy(const char *filename, MovieTextObject *text[], byte *leadOut, uint32 leadOutLen) {
+ if (!text)
+ return;
+
int frameCounter = 0, textCounter = 0;
- if (text) {
- byte oldPal[256 * 4];
- byte tmpPal[256 * 4];
- _vm->_graphics->clearScene();
+ byte oldPal[256 * 4];
+ byte tmpPal[256 * 4];
+
+ _vm->_graphics->clearScene();
- // HACK: Draw instructions
- //
- // I'm using the the menu area, because that's unlikely to be
- // touched by anything else during the cutscene.
+ // HACK: Draw instructions
+ //
+ // I'm using the the menu area, because that's unlikely to be touched
+ // by anything else during the cutscene.
- memset(_vm->_graphics->_buffer, 0, _vm->_graphics->_screenWide * MENUDEEP);
+ memset(_vm->_graphics->_buffer, 0, _vm->_graphics->_screenWide * MENUDEEP);
- byte *data;
+ byte *data;
- // Russian version substituted latin characters with cyrillic. That's why
- // it renders completely unreadable with default message
- if (Common::parseLanguage(ConfMan.get("language")) == Common::RU_RUS) {
- byte msg[] = "Po\344uk - to\344\345ko pev\345: hagmute k\344abuwy Ucke\343n, u\344u nocetute ca\343t npoekta u ckava\343te budeo po\344uku";
- data = _vm->_fontRenderer->makeTextSprite(msg, RENDERWIDE, 255, _vm->_speechFontId);
- } else {
- // TODO: Translate message to other languages?
+ // Russian version substituted latin characters with cyrillic. That's
+ // why it renders completely unreadable with default message
+ if (Common::parseLanguage(ConfMan.get("language")) == Common::RU_RUS) {
+ byte msg[] = "Po\344uk - to\344\345ko pev\345: hagmute k\344abuwy Ucke\343n, u\344u nocetute ca\343t npoekta u ckava\343te budeo po\344uku";
+ data = _vm->_fontRenderer->makeTextSprite(msg, RENDERWIDE, 255, _vm->_speechFontId);
+ } else {
+ // TODO: Translate message to other languages?
#ifdef USE_MPEG2
- byte msg[] = "Cutscene - Narration Only: Press ESC to exit, or visit www.scummvm.org to download cutscene videos";
+ byte msg[] = "Cutscene - Narration Only: Press ESC to exit, or visit www.scummvm.org to download cutscene videos";
#else
- byte msg[] = "Cutscene - Narration Only: Press ESC to exit, or recompile ScummVM with MPEG2 support";
+ byte msg[] = "Cutscene - Narration Only: Press ESC to exit, or recompile ScummVM with MPEG2 support";
#endif
- data = _vm->_fontRenderer->makeTextSprite(msg, RENDERWIDE, 255, _vm->_speechFontId);
- }
- FrameHeader *frame = (FrameHeader *) data;
- SpriteInfo msgSprite;
- byte *msgSurface;
+ data = _vm->_fontRenderer->makeTextSprite(msg, RENDERWIDE, 255, _vm->_speechFontId);
+ }
+
+ FrameHeader *frame = (FrameHeader *) data;
+ SpriteInfo msgSprite;
+ byte *msgSurface;
- msgSprite.x = _vm->_graphics->_screenWide / 2 - frame->width / 2;
- msgSprite.y = RDMENU_MENUDEEP / 2 - frame->height / 2;
- msgSprite.w = frame->width;
- msgSprite.h = frame->height;
- msgSprite.type = RDSPR_NOCOMPRESSION;
- msgSprite.data = data + sizeof(FrameHeader);
+ msgSprite.x = _vm->_graphics->_screenWide / 2 - frame->width / 2;
+ msgSprite.y = RDMENU_MENUDEEP / 2 - frame->height / 2;
+ msgSprite.w = frame->width;
+ msgSprite.h = frame->height;
+ msgSprite.type = RDSPR_NOCOMPRESSION;
+ msgSprite.data = data + sizeof(FrameHeader);
- _vm->_graphics->createSurface(&msgSprite, &msgSurface);
- _vm->_graphics->drawSurface(&msgSprite, msgSurface);
- _vm->_graphics->deleteSurface(msgSurface);
- free(data);
+ _vm->_graphics->createSurface(&msgSprite, &msgSurface);
+ _vm->_graphics->drawSurface(&msgSprite, msgSurface);
+ _vm->_graphics->deleteSurface(msgSurface);
- // In case the cutscene has a long lead-in, start just before
- // the first line of text.
+ free(data);
- frameCounter = text[0]->startFrame - 12;
+ // In case the cutscene has a long lead-in, start just before the first
+ // line of text.
- // Fake a palette that will hopefully make the text visible.
- // In the opening cutscene it seems to use colours 1 (black?)
- // and 255 (white?).
+ frameCounter = text[0]->startFrame - 12;
- memcpy(oldPal, _vm->_graphics->_palette, sizeof(oldPal));
- memset(tmpPal, 0, sizeof(tmpPal));
- tmpPal[255 * 4 + 0] = 255;
- tmpPal[255 * 4 + 1] = 255;
- tmpPal[255 * 4 + 2] = 255;
- _vm->_graphics->setPalette(0, 256, tmpPal, RDPAL_INSTANT);
+ // Fake a palette that will hopefully make the text visible. In the
+ // opening cutscene it seems to use colours 1 (black) and 255 (white).
- PlayingSoundHandle handle;
+ memcpy(oldPal, _vm->_graphics->_palette, sizeof(oldPal));
+ memset(tmpPal, 0, sizeof(tmpPal));
+ tmpPal[255 * 4 + 0] = 255;
+ tmpPal[255 * 4 + 1] = 255;
+ tmpPal[255 * 4 + 2] = 255;
+ _vm->_graphics->setPalette(0, 256, tmpPal, RDPAL_INSTANT);
- bool skipCutscene = false;
+ PlayingSoundHandle handle;
- uint32 flags = SoundMixer::FLAG_16BITS;
+ bool skipCutscene = false;
+
+ uint32 flags = SoundMixer::FLAG_16BITS;
#ifndef SCUMM_BIG_ENDIAN
- flags |= SoundMixer::FLAG_LITTLE_ENDIAN;
+ flags |= SoundMixer::FLAG_LITTLE_ENDIAN;
#endif
- while (1) {
- if (!text[textCounter])
- break;
+ while (1) {
+ if (!text[textCounter])
+ break;
- if (frameCounter == text[textCounter]->startFrame) {
- _vm->_graphics->clearScene();
- openTextObject(text[textCounter]);
- drawTextObject(NULL, text[textCounter]);
- if (text[textCounter]->speech) {
- _snd->playRaw(&handle, text[textCounter]->speech, text[textCounter]->speechBufferSize, 22050, flags);
- }
- }
- if (frameCounter == text[textCounter]->endFrame) {
- closeTextObject(text[textCounter]);
- _vm->_graphics->clearScene();
- _vm->_graphics->setNeedFullRedraw();
- textCounter++;
+ if (frameCounter == text[textCounter]->startFrame) {
+ _vm->_graphics->clearScene();
+ openTextObject(text[textCounter]);
+ drawTextObject(NULL, text[textCounter]);
+ if (text[textCounter]->speech) {
+ _snd->playRaw(&handle, text[textCounter]->speech, text[textCounter]->speechBufferSize, 22050, flags);
}
+ }
- frameCounter++;
-
- _vm->_graphics->updateDisplay();
-
- KeyboardEvent *ke = _vm->keyboardEvent();
+ if (frameCounter == text[textCounter]->endFrame) {
+ closeTextObject(text[textCounter]);
+ _vm->_graphics->clearScene();
+ _vm->_graphics->setNeedFullRedraw();
+ textCounter++;
+ }
- if ((ke && ke->keycode == 27) || _vm->_quit) {
- _snd->stopHandle(handle);
- skipCutscene = true;
- break;
- }
+ frameCounter++;
+ _vm->_graphics->updateDisplay();
- // Simulate ~12 frames per second. I don't know what
- // frame rate the original movies had, or even if it
- // was constant, but this seems to work reasonably.
+ KeyboardEvent *ke = _vm->keyboardEvent();
- _sys->delayMillis(90);
+ if ((ke && ke->keycode == 27) || _vm->_quit) {
+ _snd->stopHandle(handle);
+ skipCutscene = true;
+ break;
}
- // Wait for the voice to stop playing. This is to make sure
- // that we don't cut off the speech in mid-sentence, and - even
- // more importantly - that we don't free the sound buffer while
- // it's in use.
+ // Simulate ~12 frames per second. I don't know what frame rate
+ // the original movies had, or even if it was constant, but
+ // this seems to work reasonably.
- while (handle.isActive()) {
- _vm->_graphics->updateDisplay(false);
- _sys->delayMillis(100);
- }
-
- closeTextObject(text[textCounter]);
+ _sys->delayMillis(90);
+ }
- _vm->_graphics->clearScene();
- _vm->_graphics->setNeedFullRedraw();
+ // Wait for the voice to stop playing. This is to make sure that we
+ // don't cut off the speech in mid-sentence, and - even more
+ // importantly - that we don't free the sound buffer while it's in use.
- // HACK: Remove the instructions created above
- Common::Rect r;
+ while (handle.isActive()) {
+ _vm->_graphics->updateDisplay(false);
+ _sys->delayMillis(100);
+ }
- memset(_vm->_graphics->_buffer, 0, _vm->_graphics->_screenWide * MENUDEEP);
- r.left = r.top = 0;
- r.right = _vm->_graphics->_screenWide;
- r.bottom = MENUDEEP;
- _vm->_graphics->updateRect(&r);
+ closeTextObject(text[textCounter]);
- // FIXME: For now, only play the lead-out music for cutscenes
- // that have subtitles.
+ _vm->_graphics->clearScene();
+ _vm->_graphics->setNeedFullRedraw();
- if (!skipCutscene && musicOut) {
- _vm->_sound->playFx(0, musicOutLen, musicOut, 0, 0, RDSE_FXLEADOUT);
- _vm->_sound->waitForLeadOut();
- }
+ // HACK: Remove the instructions created above
+ Common::Rect r;
- _vm->_graphics->setPalette(0, 256, oldPal, RDPAL_INSTANT);
- }
+ memset(_vm->_graphics->_buffer, 0, _vm->_graphics->_screenWide * MENUDEEP);
+ r.left = r.top = 0;
+ r.right = _vm->_graphics->_screenWide;
+ r.bottom = MENUDEEP;
+ _vm->_graphics->updateRect(&r);
- // Lead-in and lead-out music are, as far as I can tell, only used for
- // the animated cut-scenes, so this seems like a good place to close
- // both of them.
+ // FIXME: For now, only play the lead-out music for cutscenes that have
+ // subtitles.
- _vm->_sound->stopFx(-1);
- _vm->_sound->stopFx(-2);
+ if (!skipCutscene && leadOut)
+ _vm->_sound->playFx(&_leadOutHandle, leadOut, leadOutLen, SoundMixer::kMaxChannelVolume, 0, false, SoundMixer::kMusicAudioDataType);
- return RD_OK;
+ _vm->_graphics->setPalette(0, 256, oldPal, RDPAL_INSTANT);
}
} // End of namespace Sword2
diff --git a/sword2/driver/animation.h b/sword2/driver/animation.h
index a49f785004..f34d8c8b06 100644
--- a/sword2/driver/animation.h
+++ b/sword2/driver/animation.h
@@ -65,17 +65,20 @@ private:
byte *_textSurface;
+ PlayingSoundHandle _leadOutHandle;
+
static struct MovieInfo _movies[];
void openTextObject(MovieTextObject *obj);
void closeTextObject(MovieTextObject *obj);
void drawTextObject(AnimationState *anim, MovieTextObject *obj);
- int32 playDummy(const char *filename, MovieTextObject *text[], byte *musicOut, uint32 musicOutLen);
+ void playMPEG(const char *filename, MovieTextObject *text[], byte *leadOut, uint32 leadOutLen);
+ void playDummy(const char *filename, MovieTextObject *text[], byte *leadOut, uint32 leadOutLen);
public:
MoviePlayer(Sword2Engine *vm);
- int32 play(const char *filename, MovieTextObject *text[], byte *musicOut, uint32 musicOutLen);
+ int32 play(const char *filename, MovieTextObject *text[], int32 leadInRes, int32 leadOutRes);
};
} // End of namespace Sword2
diff --git a/sword2/driver/d_sound.cpp b/sword2/driver/d_sound.cpp
index 4424842c4b..66baf797d5 100644
--- a/sword2/driver/d_sound.cpp
+++ b/sword2/driver/d_sound.cpp
@@ -34,8 +34,8 @@
#include "sound/wave.h"
#include "sword2/sword2.h"
#include "sword2/resman.h"
+#include "sword2/sound.h"
#include "sword2/driver/d_draw.h"
-#include "sword2/driver/d_sound.h"
namespace Sword2 {
@@ -138,8 +138,6 @@ static AudioStream *getAudioStream(File *fp, const char *base, int cd, uint32 id
}
}
-#define BUFFER_SIZE 4096
-
// ----------------------------------------------------------------------------
// Custom AudioStream class to handle Broken Sword II's audio compression.
// ----------------------------------------------------------------------------
@@ -148,32 +146,6 @@ static AudioStream *getAudioStream(File *fp, const char *base, int cd, uint32 id
#define GetCompressedSign(n) (((n) >> 3) & 1)
#define GetCompressedAmplitude(n) ((n) & 7)
-class CLUInputStream : public AudioStream {
-private:
- File *_file;
- bool _firstTime;
- uint32 _file_pos;
- uint32 _end_pos;
- int16 _outbuf[BUFFER_SIZE];
- byte _inbuf[BUFFER_SIZE];
- const int16 *_bufferEnd;
- const int16 *_pos;
-
- uint16 _prev;
-
- void refill();
- inline bool eosIntern() const;
-public:
- CLUInputStream(File *file, int size);
- ~CLUInputStream();
-
- int readBuffer(int16 *buffer, const int numSamples);
-
- bool endOfData() const { return eosIntern(); }
- bool isStereo() const { return false; }
- int getRate() const { return 22050; }
-};
-
CLUInputStream::CLUInputStream(File *file, int size)
: _file(file), _firstTime(true), _bufferEnd(_outbuf + BUFFER_SIZE) {
@@ -191,10 +163,6 @@ CLUInputStream::~CLUInputStream() {
_file->decRef();
}
-inline bool CLUInputStream::eosIntern() const {
- return _pos >= _bufferEnd;
-}
-
int CLUInputStream::readBuffer(int16 *buffer, const int numSamples) {
int samples = 0;
while (samples < numSamples && !eosIntern()) {
@@ -260,44 +228,6 @@ AudioStream *makeCLUStream(File *file, int size) {
// The length of a fade-in/out, in milliseconds.
#define FADE_LENGTH 3000
-class MusicInputStream : public AudioStream {
-private:
- int _cd;
- uint32 _musicId;
- AudioStream *_decoder;
- int16 _buffer[BUFFER_SIZE];
- const int16 *_bufferEnd;
- const int16 *_pos;
- bool _remove;
- uint32 _numSamples;
- uint32 _samplesLeft;
- bool _looping;
- int32 _fading;
- int32 _fadeSamples;
- bool _paused;
-
- void refill();
- inline bool eosIntern() const;
-public:
- MusicInputStream(int cd, uint32 musicId, bool looping);
- ~MusicInputStream();
-
- int readBuffer(int16 *buffer, const int numSamples);
-
- bool endOfData() const { return eosIntern(); }
- bool isStereo() const { return _decoder->isStereo(); }
- int getRate() const { return _decoder->getRate(); }
-
- void fadeUp();
- void fadeDown();
-
- bool isReady() { return _decoder != NULL; }
- int32 isFading() { return _fading; }
-
- bool readyToRemove();
- int32 getTimeRemaining();
-};
-
MusicInputStream::MusicInputStream(int cd, uint32 musicId, bool looping)
: _cd(cd), _musicId(musicId), _bufferEnd(_buffer + BUFFER_SIZE),
_remove(false), _looping(looping), _fading(0) {
@@ -316,12 +246,6 @@ MusicInputStream::~MusicInputStream() {
delete _decoder;
}
-inline bool MusicInputStream::eosIntern() const {
- if (_looping)
- return false;
- return _remove || _pos >= _bufferEnd;
-}
-
int MusicInputStream::readBuffer(int16 *buffer, const int numSamples) {
if (!_decoder)
return 0;
@@ -462,58 +386,13 @@ int32 MusicInputStream::getTimeRemaining() {
// Main sound class
// ----------------------------------------------------------------------------
-Sound::Sound(Sword2Engine *vm) {
- _vm = vm;
- _mutex = _vm->_system->createMutex();
-
- memset(_fx, 0, sizeof(_fx));
-
- _soundOn = true;
-
- _speechPaused = false;
- _speechMuted = false;
-
- _fxPaused = false;
- _fxMuted = false;
-
- _musicPaused = false;
- _musicMuted = false;
-
- _mixBuffer = NULL;
- _mixBufferLen = 0;
-
- for (int i = 0; i < MAXMUS; i++)
- _music[i] = NULL;
-
- _vm->_mixer->setupPremix(this, SoundMixer::kMusicAudioDataType);
-}
-
-Sound::~Sound() {
- int i;
-
- _vm->_mixer->setupPremix(0);
-
- for (i = 0; i < MAXMUS; i++)
- delete _music[i];
-
- free(_mixBuffer);
-
- for (i = 0; i < MAXFX; i++)
- stopFxHandle(i);
-
- stopSpeech();
-
- if (_mutex)
- _vm->_system->deleteMutex(_mutex);
-}
-
// AudioStream API
int Sound::readBuffer(int16 *buffer, const int numSamples) {
Common::StackLock lock(_mutex);
int i;
- if (!_soundOn || _musicPaused)
+ if (_musicPaused)
return 0;
for (i = 0; i < MAXMUS; i++) {
@@ -560,60 +439,18 @@ bool Sound::isStereo() const { return false; }
bool Sound::endOfData() const { return !fpMus.isOpen(); }
int Sound::getRate() const { return 22050; }
-
-/**
- * This function creates the pan table.
- */
-
-void Sound::buildPanTable(bool reverse) {
- int i;
-
- for (i = 0; i < 16; i++) {
- _panTable[i] = -127 + i * 8;
- _panTable[i + 17] = (i + 1) * 8 - 1;
- }
-
- _panTable[16] = 0;
-
- if (reverse) {
- for (i = 0; i < 33; i++)
- _panTable[i] = -_panTable[i];
- }
-}
-
// ----------------------------------------------------------------------------
// MUSIC
// ----------------------------------------------------------------------------
/**
- * Mutes/Unmutes the music.
- * @param mute If mute is false, restore the volume to the last set master
- * level. Otherwise the music is muted (volume 0).
- */
-
-void Sound::muteMusic(bool mute) {
- _musicMuted = mute;
-}
-
-/**
- * @return the music's mute state, true if mute, false if not mute
- */
-
-bool Sound::isMusicMute(void) {
- return _musicMuted;
-}
-
-/**
* Stops the music dead in its tracks. Any music that is currently being
* streamed is paused.
*/
-void Sound::pauseMusic(void) {
+void Sound::pauseMusic() {
Common::StackLock lock(_mutex);
- if (!_soundOn)
- return;
-
_musicPaused = true;
}
@@ -621,12 +458,9 @@ void Sound::pauseMusic(void) {
* Restarts the music from where it was stopped.
*/
-void Sound::unpauseMusic(void) {
+void Sound::unpauseMusic() {
Common::StackLock lock(_mutex);
- if (!_soundOn)
- return;
-
_musicPaused = false;
}
@@ -634,35 +468,30 @@ void Sound::unpauseMusic(void) {
* Fades out and stops the music.
*/
-void Sound::stopMusic(void) {
+void Sound::stopMusic() {
Common::StackLock lock(_mutex);
+ _loopingMusicId = 0;
+
for (int i = 0; i < MAXMUS; i++)
if (_music[i])
_music[i]->fadeDown();
}
-void Sound::waitForLeadOut(void) {
- int i = getFxIndex(-1);
-
- if (i == MAXFX)
- return;
-
- while (_fx[i]._handle.isActive()) {
- _vm->_graphics->updateDisplay();
- _vm->_system->delayMillis(30);
- }
-}
-
/**
* Streams music from a cluster file.
* @param musicId the id of the music to stream
* @param looping true if the music is to loop back to the start
* @return RD_OK or an error code
*/
-int32 Sound::streamCompMusic(uint32 musicId, bool looping) {
+int32 Sound::streamCompMusic(uint32 musicId, bool loop) {
Common::StackLock lock(_mutex);
+ if (loop)
+ _loopingMusicId = musicId;
+ else
+ _loopingMusicId = 0;
+
int primary = -1;
int secondary = -1;
@@ -716,7 +545,7 @@ int32 Sound::streamCompMusic(uint32 musicId, bool looping) {
if (secondary != -1)
_music[secondary]->fadeDown();
- _music[primary] = new MusicInputStream(_vm->_resman->whichCd(), musicId, looping);
+ _music[primary] = new MusicInputStream(_vm->_resman->whichCd(), musicId, loop);
if (!_music[primary]->isReady()) {
delete _music[primary];
@@ -763,14 +592,6 @@ void Sound::muteSpeech(bool mute) {
}
/**
- * @return the speech's mute state, true if mute, false if not mute
- */
-
-bool Sound::isSpeechMute(void) {
- return _speechMuted;
-}
-
-/**
* Stops the speech dead in its tracks.
*/
@@ -792,14 +613,12 @@ void Sound::unpauseSpeech(void) {
* Stops the speech from playing.
*/
-int32 Sound::stopSpeech(void) {
- if (!_soundOn)
- return RD_OK;
-
+int32 Sound::stopSpeech() {
if (_soundHandleSpeech.isActive()) {
_vm->_mixer->stopHandle(_soundHandleSpeech);
return RD_OK;
}
+
return RDERR_SPEECHNOTPLAYING;
}
@@ -807,7 +626,7 @@ int32 Sound::stopSpeech(void) {
* @return Either RDSE_SAMPLEPLAYING or RDSE_SAMPLEFINISHED
*/
-int32 Sound::getSpeechStatus(void) {
+int32 Sound::getSpeechStatus() {
return _soundHandleSpeech.isActive() ? RDSE_SAMPLEPLAYING : RDSE_SAMPLEFINISHED;
}
@@ -815,7 +634,7 @@ int32 Sound::getSpeechStatus(void) {
* Returns either RDSE_QUIET or RDSE_SPEAKING
*/
-int32 Sound::amISpeaking(void) {
+int32 Sound::amISpeaking() {
if (!_speechMuted && !_speechPaused && _soundHandleSpeech.isActive())
return RDSE_SPEAKING;
@@ -826,15 +645,15 @@ int32 Sound::amISpeaking(void) {
* This function loads and decompresses a list of speech from a cluster, but
* does not play it. This is used for cutscene voice-overs, presumably to
* avoid having to read from more than one file on the CD during playback.
- * @param speechid the text line id used to reference the speech
+ * @param speechId the text line id used to reference the speech
* @param buf a pointer to the buffer that will be allocated for the sound
*/
-uint32 Sound::preFetchCompSpeech(uint32 speechid, uint16 **buf) {
+uint32 Sound::preFetchCompSpeech(uint32 speechId, uint16 **buf) {
File fp;
uint32 numSamples;
- AudioStream *input = getAudioStream(&fp, "speech", _vm->_resman->whichCd(), speechid, &numSamples);
+ AudioStream *input = getAudioStream(&fp, "speech", _vm->_resman->whichCd(), speechId, &numSamples);
*buf = NULL;
@@ -860,12 +679,12 @@ uint32 Sound::preFetchCompSpeech(uint32 speechid, uint16 **buf) {
/**
* This function loads, decompresses and plays a line of speech. An error
* occurs if speech is already playing.
- * @param speechid the text line id used to reference the speech
+ * @param speechId the text line id used to reference the speech
* @param vol volume, 0 (minimum) to 16 (maximum)
* @param pan panning, -16 (full left) to 16 (full right)
*/
-int32 Sound::playCompSpeech(uint32 speechid, uint8 vol, int8 pan) {
+int32 Sound::playCompSpeech(uint32 speechId, uint8 vol, int8 pan) {
if (_speechMuted)
return RD_OK;
@@ -873,7 +692,7 @@ int32 Sound::playCompSpeech(uint32 speechid, uint8 vol, int8 pan) {
return RDERR_SPEECHPLAYING;
File *fp = new File;
- AudioStream *input = getAudioStream(fp, "speech", _vm->_resman->whichCd(), speechid, NULL);
+ AudioStream *input = getAudioStream(fp, "speech", _vm->_resman->whichCd(), speechId, NULL);
// Make the AudioStream object the sole owner of the file so that it
// will die along with the AudioStream when the speech has finished.
@@ -885,7 +704,10 @@ int32 Sound::playCompSpeech(uint32 speechid, uint8 vol, int8 pan) {
// Modify the volume according to the master volume
byte volume = _speechMuted ? 0 : vol * SoundMixer::kMaxChannelVolume / 16;
- int8 p = _panTable[pan + 16];
+ int8 p = (pan * 127) / 16;
+
+ if (isReverseStereo())
+ p = -p;
// Start the speech playing
_vm->_mixer->playInputStream(SoundMixer::kSpeechAudioDataType, &_soundHandleSpeech, input, -1, volume, p);
@@ -897,19 +719,6 @@ int32 Sound::playCompSpeech(uint32 speechid, uint8 vol, int8 pan) {
// ----------------------------------------------------------------------------
/**
- * @return the index of the sound effect with the ID passed in.
- */
-
-int32 Sound::getFxIndex(int32 id) {
- for (int i = 0; i < MAXFX; i++) {
- if (_fx[i]._id == id)
- return i;
- }
-
- return MAXFX;
-}
-
-/**
* Mutes/Unmutes the sound effects.
* @param mute If mute is false, restore the volume to the last set master
* level. Otherwise the sound effects are muted (volume 0).
@@ -919,241 +728,62 @@ void Sound::muteFx(bool mute) {
_fxMuted = mute;
// Now update the volume of any fxs playing
- for (int i = 0; i < MAXFX; i++) {
- if (_fx[i]._id) {
- byte volume = mute ? 0 : _fx[i]._volume * SoundMixer::kMaxChannelVolume / 16;
-
- _vm->_mixer->setChannelVolume(_fx[i]._handle, volume);
+ for (int i = 0; i < FXQ_LENGTH; i++) {
+ if (_fxQueue[i].resource) {
+ _vm->_mixer->setChannelVolume(_fxQueue[i].handle, mute ? 0 : _fxQueue[i].volume);
}
}
}
/**
- * @return the sound effects's mute state, true if mute, false if not mute
- */
-
-bool Sound::isFxMute(void) {
- return _fxMuted;
-}
-
-/**
* Sets the volume and pan of the sample which is currently playing
* @param id the id of the sample
* @param vol volume
* @param pan panning
*/
-int32 Sound::setFxIdVolumePan(int32 id, uint8 vol, int8 pan) {
- int32 i = getFxIndex(id);
-
- if (i == MAXFX)
- return RDERR_FXNOTOPEN;
-
- if (vol > 14)
- vol = 14;
-
- _fx[i]._volume = vol;
-
- if (!_fxMuted) {
- _vm->_mixer->setChannelVolume(_fx[i]._handle, _fx[i]._volume * SoundMixer::kMaxChannelVolume / 16);
- _vm->_mixer->setChannelBalance(_fx[i]._handle, _panTable[pan + 16]);
- }
-
- return RD_OK;
-}
-
-int32 Sound::setFxIdVolume(int32 id, uint8 vol) {
- int32 i = getFxIndex(id);
-
- if (i == MAXFX)
+int32 Sound::setFxIdVolumePan(int32 i, int vol, int pan) {
+ if (!_fxQueue[i].resource)
return RDERR_FXNOTOPEN;
- _fx[i]._volume = vol;
+ if (vol > 16)
+ vol = 16;
- if (!_fxMuted)
- _vm->_mixer->setChannelVolume(_fx[i]._handle, vol * SoundMixer::kMaxChannelVolume / 16);
+ _fxQueue[i].volume = (vol * SoundMixer::kMaxChannelVolume) / 16;
- return RD_OK;
-}
+ if (pan != -1)
+ _fxQueue[i].pan = (pan * 127) / 16;
-
-void Sound::pauseFx(void) {
- if (_fxPaused)
- return;
-
- for (int i = 0; i < MAXFX; i++) {
- if (_fx[i]._id) {
- _vm->_mixer->pauseHandle(_fx[i]._handle, true);
- _fx[i]._paused = true;
- } else
- _fx[i]._paused = false;
+ if (!_fxMuted && _fxQueue[i].handle.isActive()) {
+ _vm->_mixer->setChannelVolume(_fxQueue[i].handle, _fxQueue[i].volume);
+ if (pan != -1)
+ _vm->_mixer->setChannelBalance(_fxQueue[i].handle, _fxQueue[i].pan);
}
- _fxPaused = true;
+ return RD_OK;
}
-void Sound::pauseFxForSequence(void) {
+void Sound::pauseFx() {
if (_fxPaused)
return;
- for (int i = 0; i < MAXFX; i++) {
- if (_fx[i]._id && _fx[i]._id != -2) {
- _vm->_mixer->pauseHandle(_fx[i]._handle, true);
- _fx[i]._paused = true;
- } else
- _fx[i]._paused = false;
+ for (int i = 0; i < FXQ_LENGTH; i++) {
+ if (_fxQueue[i].resource)
+ _vm->_mixer->pauseHandle(_fxQueue[i].handle, true);
}
_fxPaused = true;
}
-void Sound::unpauseFx(void) {
+void Sound::unpauseFx() {
if (!_fxPaused)
return;
- for (int i = 0; i < MAXFX; i++)
- if (_fx[i]._paused && _fx[i]._id)
- _vm->_mixer->pauseHandle(_fx[i]._handle, false);
+ for (int i = 0; i < FXQ_LENGTH; i++)
+ if (_fxQueue[i].resource)
+ _vm->_mixer->pauseHandle(_fxQueue[i].handle, false);
_fxPaused = false;
}
-bool Sound::isFxPlaying(int32 id) {
- int i;
-
- i = getFxIndex(id);
- if (i == MAXFX)
- return false;
-
- return _fx[i]._handle.isActive();
-}
-
-/**
- * This function closes a sound effect which has been previously opened for
- * playing. Sound effects must be closed when they are finished with, otherwise
- * you will run out of sound effect buffers.
- * @param id the id of the sound to close
- */
-
-int32 Sound::stopFx(int32 id) {
- int i;
-
- if (!_soundOn)
- return RD_OK;
-
- i = getFxIndex(id);
-
- if (i == MAXFX)
- return RDERR_FXNOTOPEN;
-
- stopFxHandle(i);
- return RD_OK;
-}
-
-/**
- * This function plays a sound effect. If the effect has already been opened
- * then 'data' should be NULL, and the sound effect will simply be obtained
- * from the id passed in. If the effect has not been opened, then the WAV data
- * should be passed in 'data'. The sound effect will be closed when it has
- * finished playing.
- * @param id the sound id
- * @param data either NULL or the WAV data
- * @param vol volume, 0 (minimum) to 16 (maximum)
- * @param pan panning, -16 (full left) to 16 (full right)
- * @param type either RDSE_FXSPOT or RDSE_FXLOOP
- * @warning Zero is not a valid id
- */
-
-int32 Sound::playFx(int32 id, uint32 len, byte *data, uint8 vol, int8 pan, uint8 type) {
- if (!_soundOn)
- return RD_OK;
-
- byte volume = _fxMuted ? 0 : vol * SoundMixer::kMaxChannelVolume / 16;
- SoundMixer::SoundType soundType = SoundMixer::kSFXAudioDataType;
-
- // All lead-ins and lead-outs I've heard are music, so we use
- // the music volume setting for them.
-
- if (type == RDSE_FXLEADIN || type == RDSE_FXLEADOUT) {
- id = (type == RDSE_FXLEADIN) ? -2 : -1;
- volume = _musicMuted ? 0 : SoundMixer::kMaxChannelVolume;
- soundType = SoundMixer::kMusicAudioDataType;
- }
-
- Common::MemoryReadStream stream(data, len);
- int rate, size;
- byte flags;
-
- if (!loadWAVFromStream(stream, size, rate, flags)) {
- warning("playFx: Not a valid WAV file");
- return RDERR_INVALIDWAV;
- }
-
- int32 fxi = getFxIndex(id);
-
- if (fxi == MAXFX) {
- // Find a free slot
- fxi = getFxIndex(0);
-
- if (fxi == MAXFX) {
- warning("openFx: Running out of sound slots");
-
- // There aren't any free sound handles available. This
- // usually shouldn't happen, but if it does we expire
- // the first sound effect that isn't currently playing.
-
- for (fxi = 0; fxi < MAXFX; fxi++)
- if (!_fx[fxi]._handle.isActive())
- break;
-
- // Still no dice? I give up!
-
- if (fxi == MAXFX) {
- warning("openFx: No free sound slots");
- return RDERR_NOFREEBUFFERS;
- }
- }
-
- _fx[fxi]._id = id;
- }
-
- if (_fx[fxi]._handle.isActive())
- return RDERR_FXALREADYOPEN;
-
- if (type == RDSE_FXLOOP)
- flags |= SoundMixer::FLAG_LOOP;
- else
- flags &= ~SoundMixer::FLAG_LOOP;
-
- _fx[fxi]._volume = vol;
-
- int8 p = _panTable[pan + 16];
-
- _vm->_mixer->playRaw(&_fx[fxi]._handle, data + stream.pos(), size, rate, flags, -1, volume, p, 0, 0, soundType);
-
- return RD_OK;
-}
-
-void Sound::stopFxHandle(int i) {
- if (_fx[i]._id) {
- _vm->_mixer->stopHandle(_fx[i]._handle);
- _fx[i]._id = 0;
- _fx[i]._paused = false;
- }
-}
-
-/**
- * This function clears all of the sound effects which are currently open or
- * playing, irrespective of type.
- */
-
-void Sound::clearAllFx(void) {
- if (!_soundOn)
- return;
-
- for (int i = 0; i < MAXFX; i++)
- if (_fx[i]._id && _fx[i]._id != -1 && _fx[i]._id != -2)
- stopFxHandle(i);
-}
-
} // End of namespace Sword2
diff --git a/sword2/driver/d_sound.h b/sword2/driver/d_sound.h
deleted file mode 100644
index 33517fae61..0000000000
--- a/sword2/driver/d_sound.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/* Copyright (C) 1994-1998 Revolution Software Ltd.
- * Copyright (C) 2003-2005 The ScummVM project
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * $Header$
- */
-
-#ifndef D_SOUND_H
-#define D_SOUND_H
-
-#include "common/file.h"
-#include "sound/audiostream.h"
-#include "sound/mixer.h"
-
-namespace Sword2 {
-
-class MusicInputStream;
-
-// Max number of sound fx
-#define MAXFX 16
-#define MAXMUS 2
-
-enum {
- kCLUMode = 1,
- kMP3Mode,
- kVorbisMode,
- kFlacMode
-};
-
-extern void sword2_sound_handler(void *refCon);
-
-struct FxHandle {
- int32 _id;
- bool _paused;
- int8 _volume;
- PlayingSoundHandle _handle;
-};
-
-class Sound : public AudioStream {
-private:
- Sword2Engine *_vm;
-
- Common::MutexRef _mutex;
-
- int32 _panTable[33];
- bool _soundOn;
-
- MusicInputStream *_music[MAXMUS];
- int16 *_mixBuffer;
- int _mixBufferLen;
-
- bool _musicPaused;
- bool _musicMuted;
-
- PlayingSoundHandle _soundHandleSpeech;
- bool _speechPaused;
- bool _speechMuted;
-
- FxHandle _fx[MAXFX];
- bool _fxPaused;
- bool _fxMuted;
-
- int32 getFxIndex(int32 id);
- void stopFxHandle(int i);
-
-public:
- Sound(Sword2Engine *vm);
- ~Sound();
-
- // AudioStream API
-
- int readBuffer(int16 *buffer, const int numSamples);
- bool isStereo() const;
- bool endOfData() const;
- int getRate() const;
-
- // End of AudioStream API
-
- void buildPanTable(bool reverse);
-
- void muteMusic(bool mute);
- bool isMusicMute(void);
- void pauseMusic(void);
- void unpauseMusic(void);
- void stopMusic(void);
- void waitForLeadOut(void);
- int32 streamCompMusic(uint32 musicId, bool looping);
- int32 musicTimeRemaining(void);
-
- void muteSpeech(bool mute);
- bool isSpeechMute(void);
- void pauseSpeech(void);
- void unpauseSpeech(void);
- int32 stopSpeech(void);
- int32 getSpeechStatus(void);
- int32 amISpeaking(void);
- uint32 preFetchCompSpeech(uint32 speechid, uint16 **buf);
- int32 playCompSpeech(uint32 speechid, uint8 vol, int8 pan);
-
- void muteFx(bool mute);
- bool isFxMute(void);
- int32 setFxIdVolumePan(int32 id, uint8 vol, int8 pan);
- int32 setFxIdVolume(int32 id, uint8 vol);
- void pauseFx(void);
- void pauseFxForSequence(void);
- void unpauseFx(void);
- bool isFxPlaying(int32 id);
- int32 playFx(int32 id, uint32 len, uint8 *data, uint8 vol, int8 pan, uint8 type);
- int32 stopFx(int32 id);
- void clearAllFx(void);
-};
-
-} // End of namespace Sword2
-
-#endif
diff --git a/sword2/events.cpp b/sword2/events.cpp
index ec84350bda..b56f999f20 100644
--- a/sword2/events.cpp
+++ b/sword2/events.cpp
@@ -88,96 +88,6 @@ void Logic::killAllIdsEvents(uint32 id) {
}
}
-int32 Logic::fnRequestSpeech(int32 *params) {
- // change current script - must be followed by a TERMINATE script
- // directive
-
- // params: 0 id of target to catch the event and startup speech
- // servicing
-
- // Full script id to interact with - megas run their own 7th script
- sendEvent(params[0], (params[0] << 16) | 6);
- return IR_CONT;
-}
-
-int32 Logic::fnSetPlayerActionEvent(int32 *params) {
- // we want to intercept the player character and have him interact
- // with an object - from script this code is the same as the mouse
- // engine calls when you click on an object - here, a third party
- // does the clicking IYSWIM
-
- // note - this routine used CUR_PLAYER_ID as the target
-
- // params: 0 id to interact with
-
- setPlayerActionEvent(CUR_PLAYER_ID, params[0]);
- return IR_CONT;
-}
-
-int32 Logic::fnSendEvent(int32 *params) {
- // we want to intercept the player character and have him interact
- // with an object - from script
-
- // params: 0 id to receive event
- // 1 script to run
-
- sendEvent(params[0], params[1]);
- return IR_CONT;
-}
-
-int32 Logic::fnCheckEventWaiting(int32 *params) {
- // params: none
-
- _scriptVars[RESULT] = checkEventWaiting();
- return IR_CONT;
-}
-
-// like fnCheckEventWaiting, but starts the event rather than setting RESULT
-// to 1
-
-int32 Logic::fnCheckForEvent(int32 *params) {
- // params: none
-
- if (checkEventWaiting()) {
- startEvent();
- return IR_TERMINATE;
- }
-
- return IR_CONT;
-}
-
-// combination of fnPause and fnCheckForEvent
-// - ie. does a pause, but also checks for event each cycle
-
-int32 Logic::fnPauseForEvent(int32 *params) {
- // params: 0 pointer to object's logic structure
- // 1 number of game-cycles to pause
-
- ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
-
- if (checkEventWaiting()) {
- ob_logic->looping = 0;
- startEvent();
- return IR_TERMINATE;
- }
-
- return fnPause(params);
-}
-
-int32 Logic::fnClearEvent(int32 *params) {
- // params: none
-
- clearEvent(_scriptVars[ID]);
- return IR_CONT;
-}
-
-int32 Logic::fnStartEvent(int32 *params) {
- // params: none
-
- startEvent();
- return IR_TERMINATE;
-}
-
// For the debugger
uint32 Logic::countEvents(void) {
diff --git a/sword2/function.cpp b/sword2/function.cpp
index d7a2b1663f..9aa800bb8b 100644
--- a/sword2/function.cpp
+++ b/sword2/function.cpp
@@ -23,14 +23,17 @@
#include "common/system.h"
#include "sword2/sword2.h"
#include "sword2/defs.h"
+#include "sword2/console.h"
+#include "sword2/controls.h"
#include "sword2/interpreter.h"
#include "sword2/logic.h"
#include "sword2/maketext.h"
#include "sword2/memory.h"
#include "sword2/resman.h"
+#include "sword2/router.h"
#include "sword2/sound.h"
+#include "sword2/driver/animation.h"
#include "sword2/driver/d_draw.h"
-#include "sword2/driver/d_sound.h"
#include "sword2/driver/render.h"
namespace Sword2 {
@@ -45,39 +48,83 @@ int32 Logic::fnTestFlags(int32 *params) {
return IR_CONT;
}
-int32 Logic::fnGosub(int32 *params) {
- // params: 0 id of script
+int32 Logic::fnRegisterStartPoint(int32 *params) {
+ // params: 0 id of startup script to call - key
+ // 1 pointer to ascii message
- // Hurray, script subroutines. Logic goes up - pc is saved for current
- // level.
- logicUp(params[0]);
- return IR_GOSUB;
+ int32 key = params[0];
+ char *name = (char *) _vm->_memory->decodePtr(params[1]);
+
+ _vm->registerStartPoint(key, name);
+ return IR_CONT;
}
-int32 Logic::fnNewScript(int32 *params) {
- // change current script - must be followed by a TERMINATE script
- // directive
+int32 Logic::fnInitBackground(int32 *params) {
+ // this screen defines the size of the back buffer
- // params: 0 id of script
+ // params: 0 res id of normal background layer - cannot be 0
+ // 1 1 yes 0 no for a new palette
- _scriptVars[PLAYER_ACTION] = 0; // must clear this
- logicReplace(params[0]);
- return IR_TERMINATE;
+ return _vm->initBackground(params[0], params[1]);
}
-int32 Logic::fnInteract(int32 *params) {
- // Run targets action on a subroutine. Called by player on his base
- // level 0 idle, for example.
+/**
+ * This function is used by start scripts.
+ */
- // params: 0 id of target from which we derive action script
- // reference
+int32 Logic::fnSetSession(int32 *params) {
+ // params: 0 id of new run list
- _scriptVars[PLAYER_ACTION] = 0; // must clear this
- logicUp((params[0] << 16) | 2); // 3rd script of clicked on id
+ expressChangeSession(params[0]);
+ return IR_CONT;
+}
- // Out, up and around again - pc is saved for current level to be
- // returned to.
- return IR_GOSUB;
+int32 Logic::fnBackSprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ setSpriteStatus(params[0], BACK_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnSortSprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ setSpriteStatus(params[0], SORT_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnForeSprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ setSpriteStatus(params[0], FORE_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnRegisterMouse(int32 *params) {
+ // this call would be made from an objects service script 0
+ // the object would be one with no graphic but with a mouse - i.e. a
+ // floor or one whose mouse area is manually defined rather than
+ // intended to fit sprite shape
+
+ // params: 0 pointer to ObjectMouse or 0 for no write to mouse
+ // list
+
+ _vm->registerMouse((ObjectMouse *) _vm->_memory->decodePtr(params[0]));
+ return IR_CONT;
+}
+
+int32 Logic::fnAnim(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 resource id of animation file
+
+ // 0 means normal forward anim
+ return animate(params, false);
+}
+
+int32 Logic::fnRandom(int32 *params) {
+ // params: 0 min
+ // 1 max
+
+ _scriptVars[RESULT] = _vm->_rnd.getRandomNumberRng(params[0], params[1]);
+ return IR_CONT;
}
int32 Logic::fnPreLoad(int32 *params) {
@@ -93,42 +140,518 @@ int32 Logic::fnPreLoad(int32 *params) {
return IR_CONT;
}
-int32 Logic::fnPreFetch(int32 *params) {
- // Go fetch resource in the background.
+int32 Logic::fnAddSubject(int32 *params) {
+ // params: 0 id
+ // 1 daves reference number
- // params: 0 resource to fetch [guess]
+ if (_scriptVars[IN_SUBJECT] == 0) {
+ // This is the start of the new subject list. Set the default
+ // repsonse id to zero in case we're never passed one.
+ _defaultResponseId = 0;
+ }
+
+ if (params[0] == -1) {
+ // Id -1 is used for setting the default response, i.e. the
+ // response when someone uses an object on a person and he
+ // doesn't know anything about it. See fnChoose() below.
+
+ _defaultResponseId = params[1];
+ } else {
+ debug(5, "fnAddSubject res %d, uid %d", params[0], params[1]);
+ _subjectList[_scriptVars[IN_SUBJECT]].res = params[0];
+ _subjectList[_scriptVars[IN_SUBJECT]].ref = params[1];
+ _scriptVars[IN_SUBJECT]++;
+ }
return IR_CONT;
}
-int32 Logic::fnFetchWait(int32 *params) {
- // Fetches a resource in the background but prevents the script from
- // continuing until the resource is in memory.
+int32 Logic::fnInteract(int32 *params) {
+ // Run targets action on a subroutine. Called by player on his base
+ // level 0 idle, for example.
- // params: 0 resource to fetch [guess]
+ // params: 0 id of target from which we derive action script
+ // reference
- return IR_CONT;
+ _scriptVars[PLAYER_ACTION] = 0; // must clear this
+ logicUp((params[0] << 16) | 2); // 3rd script of clicked on id
+
+ // Out, up and around again - pc is saved for current level to be
+ // returned to.
+ return IR_GOSUB;
}
-int32 Logic::fnRelease(int32 *params) {
- // Releases a resource from memory. Used for freeing memory for
- // sprites that have just been used and will not be used again.
- // Sometimes it is better to kick out a sprite straight away so that
- // the memory can be used for more frequent animations.
+int32 Logic::fnChoose(int32 *params) {
+ // params: none
- // params: 0 resource to release [guess]
+ // This opcode is used to open the conversation menu. The human is
+ // switched off so there will be no normal mouse engine.
- return IR_CONT;
+ // The player's choice is piggy-backed on the standard opcode return
+ // values, to be used with the CP_JUMP_ON_RETURNED opcode. As far as I
+ // can tell, this is the only function that uses that feature.
+
+ uint i;
+
+ _scriptVars[AUTO_SELECTED] = 0;
+
+ if (_scriptVars[OBJECT_HELD]) {
+ // The player used an object on a person. In this case it
+ // triggered a conversation menu. Act as if the user tried to
+ // talk to the person about that object. If the person doesn't
+ // know anything about it, use the default response.
+
+ uint32 response = _defaultResponseId;
+
+ for (i = 0; i < _scriptVars[IN_SUBJECT]; i++) {
+ if (_subjectList[i].res == _scriptVars[OBJECT_HELD]) {
+ response = _subjectList[i].ref;
+ break;
+ }
+ }
+
+ // The user won't be holding the object any more, and the
+ // conversation menu will be closed.
+
+ _scriptVars[OBJECT_HELD] = 0;
+ _scriptVars[IN_SUBJECT] = 0;
+ return IR_CONT | (response << 3);
+ }
+
+ if (_scriptVars[CHOOSER_COUNT_FLAG] == 0 && _scriptVars[IN_SUBJECT] == 1 && _subjectList[0].res == EXIT_ICON) {
+ // This is the first time the chooser is coming up in this
+ // conversation, there is only one subject and that's the
+ // EXIT icon.
+ //
+ // In other words, the player doesn't have anything to talk
+ // about. Skip it.
+
+ // The conversation menu will be closed. We set AUTO_SELECTED
+ // because the speech script depends on it.
+
+ _scriptVars[AUTO_SELECTED] = 1;
+ _scriptVars[IN_SUBJECT] = 0;
+ return IR_CONT | (_subjectList[0].ref << 3);
+ }
+
+ byte *icon;
+
+ if (!_choosing) {
+ // This is a new conversation menu.
+
+ if (!_scriptVars[IN_SUBJECT])
+ error("fnChoose with no subjects");
+
+ for (i = 0; i < _scriptVars[IN_SUBJECT]; i++) {
+ icon = _vm->_resman->openResource(_subjectList[i].res) + sizeof(StandardHeader) + RDMENU_ICONWIDE * RDMENU_ICONDEEP;
+ _vm->_graphics->setMenuIcon(RDMENU_BOTTOM, i, icon);
+ _vm->_resman->closeResource(_subjectList[i].res);
+ }
+
+ for (; i < 15; i++)
+ _vm->_graphics->setMenuIcon(RDMENU_BOTTOM, (uint8) i, NULL);
+
+ _vm->_graphics->showMenu(RDMENU_BOTTOM);
+ _vm->setMouse(NORMAL_MOUSE_ID);
+ _choosing = true;
+ return IR_REPEAT;
+ }
+
+ // The menu is there - we're just waiting for a click. We only care
+ // about left clicks.
+
+ MouseEvent *me = _vm->mouseEvent();
+
+ if (!me || !(me->buttons & RD_LEFTBUTTONDOWN) || _vm->_mouseY < 400)
+ return IR_REPEAT;
+
+ // Check for click on a menu.
+
+ int hit = _vm->menuClick(_scriptVars[IN_SUBJECT]);
+ if (hit < 0)
+ return IR_REPEAT;
+
+ // Hilight the clicked icon by greying the others.
+
+ for (i = 0; i < _scriptVars[IN_SUBJECT]; i++) {
+ if ((int) i != hit) {
+ icon = _vm->_resman->openResource(_subjectList[i].res) + sizeof(StandardHeader);
+ _vm->_graphics->setMenuIcon(RDMENU_BOTTOM, i, icon);
+ _vm->_resman->closeResource(_subjectList[i].res);
+ }
+ }
+
+ // For non-speech scripts that manually call the chooser
+ _scriptVars[RESULT] = _subjectList[hit].res;
+
+ // The conversation menu will be closed
+
+ _choosing = false;
+ _scriptVars[IN_SUBJECT] = 0;
+ _vm->setMouse(0);
+
+ return IR_CONT | (_subjectList[hit].ref << 3);
}
-int32 Logic::fnRandom(int32 *params) {
- // params: 0 min
- // 1 max
+/**
+ * Walk mega to (x,y,dir). Set RESULT to 0 if it succeeded. Otherwise, set
+ * RESULT to 1.
+ */
+
+int32 Logic::fnWalk(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+ // 3 pointer to object's walkdata structure
+ // 4 target x-coord
+ // 5 target y-coord
+ // 6 target direction (8 means end walk on ANY direction)
+
+ ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
+ ObjectGraphic *ob_graph = (ObjectGraphic *) _vm->_memory->decodePtr(params[1]);
+ ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[2]);
+
+ int16 target_x = (int16) params[4];
+ int16 target_y = (int16) params[5];
+ uint8 target_dir = (uint8) params[6];
+
+ ObjectWalkdata *ob_walkdata;
+
+ // If this is the start of the walk, calculate the route.
+
+ if (!ob_logic->looping) {
+ // If we're already there, don't even bother allocating
+ // memory and calling the router, just quit back & continue
+ // the script! This avoids an embarassing mega stand frame
+ // appearing for one cycle when we're already in position for
+ // an anim eg. repeatedly clicking on same object to repeat
+ // an anim - no mega frame will appear in between runs of the
+ // anim.
+
+ if (ob_mega->feet_x == target_x && ob_mega->feet_y == target_y && ob_mega->current_dir == target_dir) {
+ _scriptVars[RESULT] = 0;
+ return IR_CONT;
+ }
+
+ assert(params[6] >= 0 && params[6] <= 8);
+
+ ob_walkdata = (ObjectWalkdata *) _vm->_memory->decodePtr(params[3]);
+
+ ob_mega->walk_pc = 0;
+
+ // Set up mem for _walkData in route_slots[] & set mega's
+ // 'route_slot_id' accordingly
+
+ _router->allocateRouteMem();
+
+ int32 route = _router->routeFinder(ob_mega, ob_walkdata, target_x, target_y, target_dir);
+
+ // 0 = can't make route to target
+ // 1 = created route
+ // 2 = zero route but may need to turn
+
+ if (route == 1 || route == 2) {
+ // so script fnWalk loop continues until end of
+ // walk-anim
+
+ ob_logic->looping = 1;
+
+ // need to animate the route now, so don't set result
+ // or return yet!
+
+ // started walk
+ ob_mega->currently_walking = 1;
+
+ // (see fnGetPlayerSaveData() in save_rest.cpp
+ } else {
+ _router->freeRouteMem();
+ _scriptVars[RESULT] = 1;
+ return IR_CONT;
+ }
+
+ // Walk is about to start, so set the mega's graphic resource
+ ob_graph->anim_resource = ob_mega->megaset_res;
+ } else if (_scriptVars[EXIT_FADING] && _vm->_graphics->getFadeStatus() == RDFADE_BLACK) {
+ // Double clicked an exit so quit the walk when screen is black
+ // ok, thats it - back to script and change screen
+
+ ob_logic->looping = 0;
+ _router->freeRouteMem();
+
+ // Must clear in-case on the new screen there's a walk
+ // instruction (which would get cut short)
+ _scriptVars[EXIT_CLICK_ID] = 0;
+
+ // finished walk
+ ob_mega->currently_walking = 0;
+
+ // see fnGetPlayerSaveData() in save_rest.cpp
+
+ _scriptVars[RESULT] = 0;
+
+ // continue the script so that RESULT can be checked!
+ return IR_CONT;
+ }
+
+ // get pointer to walkanim & current frame position
+
+ WalkData *walkAnim = _router->getRouteMem();
+ int32 walk_pc = ob_mega->walk_pc;
+
+ // If stopping the walk early, overwrite the next step with a
+ // slow-out, then finish
+
+ if (checkEventWaiting()) {
+ if (walkAnim[walk_pc].step == 0 && walkAnim[walk_pc + 1].step == 1) {
+ // At the beginning of a step
+ ob_walkdata = (ObjectWalkdata *) _vm->_memory->decodePtr(params[3]);
+ _router->earlySlowOut(ob_mega, ob_walkdata);
+ }
+ }
+
+ // Get new frame of walk
+
+ ob_graph->anim_pc = walkAnim[walk_pc].frame;
+ ob_mega->current_dir = walkAnim[walk_pc].dir;
+ ob_mega->feet_x = walkAnim[walk_pc].x;
+ ob_mega->feet_y = walkAnim[walk_pc].y;
+
+ // Check if NEXT frame is in fact the end-marker of the walk sequence
+ // so we can return to script just as the final (stand) frame of the
+ // walk is set - so that if followed by an anim, the anim's first
+ // frame replaces the final stand-frame of the walk (see below)
+
+ // '512' is end-marker
+ if (walkAnim[walk_pc + 1].frame == 512) {
+ ob_logic->looping = 0;
+ _router->freeRouteMem();
+
+ // finished walk
+ ob_mega->currently_walking = 0;
+
+ // (see fnGetPlayerSaveData() in save_rest.cpp
+
+ // if George's walk has been interrupted to run a new action
+ // script for instance or Nico's walk has been interrupted by
+ // player clicking on her to talk
+
+ // There used to be code here for checking if two megas were
+ // colliding, but that code had been commented out, and it
+ // was only run if a function that always returned zero
+ // returned non-zero.
+
+ if (checkEventWaiting()) {
+ startEvent();
+ _scriptVars[RESULT] = 1;
+ return IR_TERMINATE;
+ } else {
+ _scriptVars[RESULT] = 0;
+
+ // CONTINUE the script so that RESULT can be checked!
+ // Also, if an anim command follows the fnWalk command,
+ // the 1st frame of the anim (which is always a stand
+ // frame itself) can replace the final stand frame of
+ // the walk, to hide the slight difference between the
+ // shrinking on the mega frames and the pre-shrunk anim
+ // start-frame.
+
+ return IR_CONT;
+ }
+ }
+
+ // Increment the walkanim frame number and come back next cycle
+
+ ob_mega->walk_pc++;
+ return IR_REPEAT;
+}
+
+/**
+ * Walk mega to start position of anim
+ */
+
+int32 Logic::fnWalkToAnim(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+ // 3 pointer to object's walkdata structure
+ // 4 anim resource id
+
+ int32 pars[7];
+
+ // Walkdata is needed for earlySlowOut if player clicks elsewhere
+ // during the walk.
+
+ pars[0] = params[0];
+ pars[1] = params[1];
+ pars[2] = params[2];
+ pars[3] = params[3];
+
+ ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
+
+ // If this is the start of the walk, read anim file to get start coords
+
+ if (!ob_logic->looping) {
+ byte *anim_file = _vm->_resman->openResource(params[4]);
+ AnimHeader *anim_head = _vm->fetchAnimHeader( anim_file );
+
+ pars[4] = anim_head->feetStartX;
+ pars[5] = anim_head->feetStartY;
+ pars[6] = anim_head->feetStartDir;
+
+ _vm->_resman->closeResource(params[4]);
+
+ // If start coords not yet set in anim header, use the standby
+ // coords (which should be set beforehand in the script).
+
+ if (pars[4] == 0 && pars[5] == 0) {
+ byte buf[NAME_LEN];
+
+ pars[4] = _standbyX;
+ pars[5] = _standbyY;
+ pars[6] = _standbyDir;
+
+ debug(3, "WARNING: fnWalkToAnim(%s) used standby coords", _vm->fetchObjectName(params[4], buf));
+ }
+
+ assert(pars[6] >= 0 && pars[6] <= 7);
+ }
+
+ return fnWalk(pars);
+}
+
+/**
+ * Turn mega to the specified direction. Just needs to call fnWalk() with
+ * current feet coords, so router can produce anim of turn frames.
+ */
+
+int32 Logic::fnTurn(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+ // 3 pointer to object's walkdata structure
+ // 4 target direction
+
+ int32 pars[7];
+
+ pars[0] = params[0];
+ pars[1] = params[1];
+ pars[2] = params[2];
+ pars[3] = params[3];
+
+ ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
+
+ // If this is the start of the turn, get the mega's current feet
+ // coords + the required direction
+
+ if (!ob_logic->looping) {
+ assert(params[4] >= 0 && params[4] <= 7);
+
+ ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[2]);
+
+ pars[4] = ob_mega->feet_x;
+ pars[5] = ob_mega->feet_y;
+ pars[6] = params[4];
+ }
+
+ return fnWalk(pars);
+}
+
+/**
+ * Stand mega at (x,y,dir)
+ * Sets up the graphic object, but also needs to set the new 'current_dir' in
+ * the mega object, so the router knows in future
+ */
+
+int32 Logic::fnStandAt(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ // 1 pointer to object's mega structure
+ // 2 target x-coord
+ // 3 target y-coord
+ // 4 target direction
+
+ assert(params[4] >= 0 && params[4] <= 7);
+
+ ObjectGraphic *ob_graph = (ObjectGraphic *) _vm->_memory->decodePtr(params[0]);
+ ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[1]);
+
+ // set up the stand frame & set the mega's new direction
+
+ ob_mega->feet_x = params[2];
+ ob_mega->feet_y = params[3];
+ ob_mega->current_dir = params[4];
+
+ // mega-set animation file
+ ob_graph->anim_resource = ob_mega->megaset_res;
+
+ // dir + first stand frame (always frame 96)
+ ob_graph->anim_pc = params[4] + 96;
- _scriptVars[RESULT] = _vm->_rnd.getRandomNumberRng(params[0], params[1]);
return IR_CONT;
}
+/**
+ * Stand mega into the specified direction at current feet coords.
+ * Just needs to call fnStandAt() with current feet coords.
+ */
+
+int32 Logic::fnStand(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ // 1 pointer to object's mega structure
+ // 2 target direction
+
+ ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[1]);
+
+ int32 pars[5];
+
+ pars[0] = params[0];
+ pars[1] = params[1];
+ pars[2] = ob_mega->feet_x;
+ pars[3] = ob_mega->feet_y;
+ pars[4] = params[2];
+
+ return fnStandAt(pars);
+}
+
+/**
+ * stand mega at end position of anim
+ */
+
+int32 Logic::fnStandAfterAnim(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ // 1 pointer to object's mega structure
+ // 2 anim resource id
+
+ byte *anim_file = _vm->_resman->openResource(params[2]);
+ AnimHeader *anim_head = _vm->fetchAnimHeader(anim_file);
+
+ int32 pars[5];
+
+ pars[0] = params[0];
+ pars[1] = params[1];
+
+ pars[2] = anim_head->feetEndX;
+ pars[3] = anim_head->feetEndY;
+ pars[4] = anim_head->feetEndDir;
+
+ // If start coords not available either use the standby coords (which
+ // should be set beforehand in the script)
+
+ if (pars[2] == 0 && pars[3] == 0) {
+ byte buf[NAME_LEN];
+
+ pars[2] = _standbyX;
+ pars[3] = _standbyY;
+ pars[4] = _standbyDir;
+
+ debug(3, "WARNING: fnStandAfterAnim(%s) used standby coords", _vm->fetchObjectName(params[2], buf));
+ }
+
+ assert(pars[4] >= 0 && pars[4] <= 7);
+
+ _vm->_resman->closeResource(params[2]);
+ return fnStandAt(pars);
+}
+
int32 Logic::fnPause(int32 *params) {
// params: 0 pointer to object's logic structure
// 1 number of game-cycles to pause
@@ -152,6 +675,92 @@ int32 Logic::fnPause(int32 *params) {
return IR_CONT;
}
+int32 Logic::fnMegaTableAnim(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+ // 3 pointer to animation table
+
+ // 0 means normal forward anim
+ return megaTableAnimate(params, false);
+}
+
+int32 Logic::fnAddMenuObject(int32 *params) {
+ // params: 0 pointer to a MenuObject structure to copy down
+
+ _vm->addMenuObject((MenuObject *) _vm->_memory->decodePtr(params[0]));
+ return IR_CONT;
+}
+
+/**
+ * Start a conversation.
+ *
+ * Note that fnStartConversation() might accidentally be called every time the
+ * script loops back for another chooser, but we only want to reset the chooser
+ * count flag the first time this function is called, i.e. when the talk flag
+ * is zero.
+ */
+
+int32 Logic::fnStartConversation(int32 *params) {
+ // params: none
+
+ if (_scriptVars[TALK_FLAG] == 0) {
+ // See fnChooser & speech scripts
+ _scriptVars[CHOOSER_COUNT_FLAG] = 0;
+ }
+
+ fnNoHuman(params);
+ return IR_CONT;
+}
+
+/**
+ * End a conversation.
+ */
+
+int32 Logic::fnEndConversation(int32 *params) {
+ // params: none
+
+ _vm->_graphics->hideMenu(RDMENU_BOTTOM);
+
+ if (_vm->_mouseY > 399) {
+ // Will wait for cursor to move off the bottom menu
+ _vm->_mouseMode = MOUSE_holding;
+ }
+
+ // In case DC forgets
+ _scriptVars[TALK_FLAG] = 0;
+
+ return IR_CONT;
+}
+
+int32 Logic::fnSetFrame(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ // 1 resource id of animation file
+ // 2 frame flag (0=first 1=last)
+
+ int32 res = params[1];
+ assert(res);
+
+ // open the resource (& check it's valid)
+ byte *anim_file = _vm->_resman->openResource(res);
+
+ StandardHeader *head = (StandardHeader *) anim_file;
+ assert(head->fileType == ANIMATION_FILE);
+
+ // set up pointer to the animation header
+ AnimHeader *anim_head = _vm->fetchAnimHeader(anim_file);
+
+ // set up anim resource in graphic object
+ ObjectGraphic *ob_graphic = (ObjectGraphic *) _vm->_memory->decodePtr(params[0]);
+
+ ob_graphic->anim_resource = res;
+ ob_graphic->anim_pc = params[2] ? anim_head->noAnimFrames - 1 : 0;
+
+ // Close the anim file and drop out of script
+ _vm->_resman->closeResource(ob_graphic->anim_resource);
+ return IR_CONT;
+}
+
int32 Logic::fnRandomPause(int32 *params) {
// params: 0 pointer to object's logic structure
// 1 minimum number of game-cycles to pause
@@ -171,6 +780,65 @@ int32 Logic::fnRandomPause(int32 *params) {
return fnPause(pars);
}
+int32 Logic::fnRegisterFrame(int32 *params) {
+ // this call would be made from an objects service script 0
+
+ // params: 0 pointer to mouse structure or NULL for no write to
+ // mouse list (non-zero means write sprite-shape to
+ // mouse list)
+ // 1 pointer to graphic structure
+ // 2 pointer to mega structure or NULL if not a mega
+
+ return _vm->registerFrame(params);
+}
+
+int32 Logic::fnNoSprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ setSpriteStatus(params[0], NO_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnSendSync(int32 *params) {
+ // params: 0 sync's recipient
+ // 1 sync value
+
+ for (int i = 0; i < MAX_syncs; i++) {
+ if (_syncList[i].id == 0) {
+ debug(5, "%d sends sync %d to %d", _scriptVars[ID], params[1], params[0]);
+ _syncList[i].id = params[0];
+ _syncList[i].sync = params[1];
+ return IR_CONT;
+ }
+ }
+
+ // The original code didn't even check for this condition, so maybe
+ // it should be a fatal error?
+
+ warning("No free sync slot");
+ return IR_CONT;
+}
+
+int32 Logic::fnUpdatePlayerStats(int32 *params) {
+ // engine needs to know certain info about the player
+
+ // params: 0 pointer to mega structure
+
+ ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[0]);
+
+ _vm->_thisScreen.player_feet_x = ob_mega->feet_x;
+ _vm->_thisScreen.player_feet_y = ob_mega->feet_y;
+
+ // for the script
+ _scriptVars[PLAYER_FEET_X] = ob_mega->feet_x;
+ _scriptVars[PLAYER_FEET_Y] = ob_mega->feet_y;
+ _scriptVars[PLAYER_CUR_DIR] = ob_mega->current_dir;
+ _scriptVars[SCROLL_OFFSET_X] = _vm->_thisScreen.scroll_offset_x;
+
+ debug(5, "fnUpdatePlayerStats: %d %d", ob_mega->feet_x, ob_mega->feet_y);
+
+ return IR_CONT;
+}
+
int32 Logic::fnPassGraph(int32 *params) {
// makes an engine local copy of passed ObjectGraphic - run script 4
// of an object to request this used by fnTurnTo(id) etc
@@ -184,6 +852,22 @@ int32 Logic::fnPassGraph(int32 *params) {
return IR_CONT;
}
+int32 Logic::fnInitFloorMouse(int32 *params) {
+ // params: 0 pointer to object's mouse structure
+
+ ObjectMouse *ob_mouse = (ObjectMouse *) _vm->_memory->decodePtr(params[0]);
+
+ // floor is always lowest priority
+
+ ob_mouse->x1 = 0;
+ ob_mouse->y1 = 0;
+ ob_mouse->x2 = _vm->_thisScreen.screen_wide - 1;
+ ob_mouse->y2 = _vm->_thisScreen.screen_deep - 1;
+ ob_mouse->priority = 9;
+ ob_mouse->pointer = NORMAL_MOUSE_ID;
+ return IR_CONT;
+}
+
int32 Logic::fnPassMega(int32 *params) {
// makes an engine local copy of passed graphic_structure and
// mega_structure - run script 4 of an object to request this
@@ -198,6 +882,1223 @@ int32 Logic::fnPassMega(int32 *params) {
return IR_CONT;
}
+/**
+ * Turn mega to face point (x,y) on the floor
+ * Just needs to call fnWalk() with current feet coords & direction computed
+ * by whatTarget()
+ */
+
+int32 Logic::fnFaceXY(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+ // 3 pointer to object's walkdata structure
+ // 4 target x-coord
+ // 5 target y-coord
+
+ int32 pars[7];
+
+ pars[0] = params[0];
+ pars[1] = params[1];
+ pars[2] = params[2];
+ pars[3] = params[3];
+
+ ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
+
+ // If this is the start of the turn, get the mega's current feet
+ // coords + the required direction
+
+ if (!ob_logic->looping) {
+ ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[2]);
+
+ pars[4] = ob_mega->feet_x;
+ pars[5] = ob_mega->feet_y;
+ pars[6] = whatTarget(ob_mega->feet_x, ob_mega->feet_y, params[4], params[5]);
+ }
+
+ return fnWalk(pars);
+}
+
+/**
+ * Causes no more objects in this logic loop to be processed. The logic engine
+ * will restart at the beginning of the new list. The current screen will not
+ * be drawn!
+ */
+
+int32 Logic::fnEndSession(int32 *params) {
+ // params: 0 id of new run-list
+
+ // terminate current and change to next run-list
+ expressChangeSession(params[0]);
+
+ // stop the script - logic engine will now go around and the new
+ // screen will begin
+ return IR_STOP;
+}
+
+int32 Logic::fnNoHuman(int32 *params) {
+ // params: none
+
+ _vm->noHuman();
+ _vm->clearPointerText();
+
+ // must be normal mouse situation or a largely neutral situation -
+ // special menus use noHuman
+
+ // dont hide menu in conversations
+ if (_scriptVars[TALK_FLAG] == 0)
+ _vm->_graphics->hideMenu(RDMENU_BOTTOM);
+
+ if (_vm->_mouseMode == MOUSE_system_menu) {
+ // close menu
+ _vm->_mouseMode = MOUSE_normal;
+ _vm->_graphics->hideMenu(RDMENU_TOP);
+ }
+
+ return IR_CONT;
+}
+
+int32 Logic::fnAddHuman(int32 *params) {
+ // params: none
+
+ // for logic scripts
+ _scriptVars[MOUSE_AVAILABLE] = 1;
+
+ // off
+ if (_vm->_mouseStatus) {
+ _vm->_mouseStatus = false; // on
+ _vm->_mouseTouching = 1; // forces engine to choose a cursor
+ }
+
+ // clear this to reset no-second-click system
+ _scriptVars[CLICKED_ID] = 0;
+
+ // this is now done outside the OBJECT_HELD check in case it's set to
+ // zero before now!
+
+ // unlock the mouse from possible large object lock situtations - see
+ // syphon in rm 3
+
+ _vm->_mouseModeLocked = false;
+
+ if (_scriptVars[OBJECT_HELD]) {
+ // was dragging something around
+ // need to clear this again
+ _scriptVars[OBJECT_HELD] = 0;
+
+ // and these may also need clearing, just in case
+ _vm->_examiningMenuIcon = false;
+ Logic::_scriptVars[COMBINE_BASE] = 0;
+
+ _vm->setLuggage(0);
+ }
+
+ // if mouse is over menu area
+ if (_vm->_mouseY > 399) {
+ if (_vm->_mouseMode != MOUSE_holding) {
+ // VITAL - reset things & rebuild the menu
+ _vm->_mouseMode = MOUSE_normal;
+ _vm->setMouse(NORMAL_MOUSE_ID);
+ } else
+ _vm->setMouse(NORMAL_MOUSE_ID);
+ }
+
+ // enabled/disabled from console; status printed with on-screen debug
+ // info
+
+ if (_vm->_debugger->_testingSnR) {
+ uint8 black[4] = { 0, 0, 0, 0 };
+ uint8 white[4] = { 255, 255, 255, 0 };
+
+ // testing logic scripts by simulating an instant Save &
+ // Restore
+
+ _vm->_graphics->setPalette(0, 1, white, RDPAL_INSTANT);
+
+ // stops all fx & clears the queue - eg. when leaving a
+ // location
+
+ _vm->_sound->clearFxQueue();
+
+ // Trash all object resources so they load in fresh & restart
+ // their logic scripts
+
+ _vm->_resman->killAllObjects(false);
+
+ _vm->_graphics->setPalette(0, 1, black, RDPAL_INSTANT);
+ }
+
+ return IR_CONT;
+}
+
+/**
+ * Wait for a target to become waiting, i.e. not busy.
+ */
+
+int32 Logic::fnWeWait(int32 *params) {
+ // params: 0 target
+
+ StandardHeader *head = (StandardHeader *) _vm->_resman->openResource(params[0]);
+ assert(head->fileType == GAME_OBJECT);
+
+ // Run the target's get-speech-state script
+
+ int32 target = params[0];
+ char *raw_script_ad = (char *) head;
+ uint32 null_pc = 5;
+
+ runScript(raw_script_ad, raw_script_ad, &null_pc);
+
+ _vm->_resman->closeResource(target);
+
+ if (_scriptVars[RESULT] == 0) {
+ // The target is busy. Try again.
+ _vm->_debugger->_speechScriptWaiting = target;
+ return IR_REPEAT;
+ }
+
+ // The target is waiting, i.e. not busy.
+
+ _vm->_debugger->_speechScriptWaiting = 0;
+ return IR_CONT;
+}
+
+/**
+ * Wait for a target to become waiting, i.e. not busy, send a command to it,
+ * then wait for it to finish.
+ */
+
+int32 Logic::fnTheyDoWeWait(int32 *params) {
+ // params: 0 pointer to ob_logic
+ // 1 target
+ // 2 command
+ // 3 ins1
+ // 4 ins2
+ // 5 ins3
+ // 6 ins4
+ // 7 ins5
+
+ StandardHeader *head = (StandardHeader *) _vm->_resman->openResource(params[1]);
+ assert(head->fileType == GAME_OBJECT);
+
+ // Run the target's get-speech-state script
+
+ int32 target = params[1];
+ char *raw_script_ad = (char *) head;
+ uint32 null_pc = 5;
+
+ runScript(raw_script_ad, raw_script_ad, &null_pc);
+
+ _vm->_resman->closeResource(target);
+
+ ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
+
+ if (_scriptVars[RESULT] == 1 && !_scriptVars[INS_COMMAND] && ob_logic->looping == 0) {
+ // The target is waiting, i.e. not busy, and there is no other
+ // command queued. We haven't sent the command yet, so do it.
+
+ debug(5, "fnTheyDoWeWait: sending command to %d", target);
+
+ _vm->_debugger->_speechScriptWaiting = target;
+ ob_logic->looping = 1;
+
+ _scriptVars[SPEECH_ID] = params[1];
+ _scriptVars[INS_COMMAND] = params[2];
+ _scriptVars[INS1] = params[3];
+ _scriptVars[INS2] = params[4];
+ _scriptVars[INS3] = params[5];
+ _scriptVars[INS4] = params[6];
+ _scriptVars[INS5] = params[7];
+
+ return IR_REPEAT;
+ }
+
+ if (ob_logic->looping == 0) {
+ // The command has not been sent yet. Keep waiting.
+ _vm->_debugger->_speechScriptWaiting = target;
+ return IR_REPEAT;
+ }
+
+ if (_scriptVars[RESULT] == 0) {
+ // The command has been sent, and the target is busy doing it.
+ // Wait for it to finish.
+
+ debug(5, "fnTheyDoWeWait: Waiting for %d to finish", target);
+
+ _vm->_debugger->_speechScriptWaiting = target;
+ return IR_REPEAT;
+ }
+
+ debug(5, "fnTheyDoWeWait: %d finished", target);
+
+ ob_logic->looping = 0;
+ _vm->_debugger->_speechScriptWaiting = 0;
+ return IR_CONT;
+}
+
+/**
+ * Wait for a target to become waiting, i.e. not busy, then send a command to
+ * it.
+ */
+
+int32 Logic::fnTheyDo(int32 *params) {
+ // params: 0 target
+ // 1 command
+ // 2 ins1
+ // 3 ins2
+ // 4 ins3
+ // 5 ins4
+ // 6 ins5
+
+ StandardHeader *head = (StandardHeader *) _vm->_resman->openResource(params[0]);
+ assert (head->fileType == GAME_OBJECT);
+
+ // Run the target's get-speech-state script
+
+ int32 target = params[0];
+ char *raw_script_ad = (char *) head;
+ uint32 null_pc = 5;
+
+ runScript(raw_script_ad, raw_script_ad, &null_pc);
+
+ _vm->_resman->closeResource(target);
+
+ if (_scriptVars[RESULT] == 1 && !_scriptVars[INS_COMMAND]) {
+ // The target is waiting, i.e. not busy, and there is no other
+ // command queued. Send the command.
+
+ debug(5, "fnTheyDo: sending command to %d", target);
+
+ _vm->_debugger->_speechScriptWaiting = 0;
+
+ _scriptVars[SPEECH_ID] = params[0];
+ _scriptVars[INS_COMMAND] = params[1];
+ _scriptVars[INS1] = params[2];
+ _scriptVars[INS2] = params[3];
+ _scriptVars[INS3] = params[4];
+ _scriptVars[INS4] = params[5];
+ _scriptVars[INS5] = params[6];
+
+ return IR_CONT;
+ }
+
+ // The target is busy. Come back again next cycle.
+
+ _vm->_debugger->_speechScriptWaiting = target;
+ return IR_REPEAT;
+}
+
+/**
+ * Route to the left or right hand side of target id, if possible.
+ */
+
+int32 Logic::fnWalkToTalkToMega(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+ // 3 pointer to object's walkdata structure
+ // 4 id of target mega to face
+ // 5 distance
+
+ int32 pars[7];
+
+ pars[0] = params[0];
+ pars[1] = params[1];
+ pars[2] = params[2];
+ pars[3] = params[3];
+
+ ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
+
+ // If this is the start of the walk, calculate the route.
+
+ if (!ob_logic->looping) {
+ StandardHeader *head = (StandardHeader *) _vm->_resman->openResource(params[4]);
+
+ assert(head->fileType == GAME_OBJECT);
+
+ // Call the base script. This is the graphic/mouse service
+ // call, and will set _engineMega to the ObjectMega of mega we
+ // want to route to.
+
+ char *raw_script_ad = (char *) head;
+ uint32 null_pc = 3;
+
+ runScript(raw_script_ad, raw_script_ad, &null_pc);
+
+ _vm->_resman->closeResource(params[4]);
+
+ // Stand exactly beside the mega, ie. at same y-coord
+ pars[5] = _engineMega.feet_y;
+
+ ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[2]);
+
+ // Apply scale factor to walk distance. Ay+B gives 256 * scale
+ // ie. 256 * 256 * true_scale for even better accuracy, ie.
+ // scale = (Ay + B) / 256
+
+ int scale = (ob_mega->scale_a * ob_mega->feet_y + ob_mega->scale_b) / 256;
+ int mega_separation = (params[5] * scale) / 256;
+
+ debug(4, "Target is at (%d, %d), separation %d", _engineMega.feet_x, _engineMega.feet_y, mega_separation);
+
+ if (_engineMega.feet_x < ob_mega->feet_x) {
+ // Target is left of us, so aim to stand to their
+ // right. Face down_left
+
+ pars[4] = _engineMega.feet_x + mega_separation;
+ pars[6] = 5;
+ } else {
+ // Ok, must be right of us so aim to stand to their
+ // left. Face down_right.
+
+ pars[4] = _engineMega.feet_x - mega_separation;
+ pars[6] = 3;
+ }
+ }
+
+ return fnWalk(pars);
+}
+
+int32 Logic::fnFadeDown(int32 *params) {
+ // NONE means up! can only be called when screen is fully faded up -
+ // multiple calls wont have strange effects
+
+ // params: none
+
+ if (_vm->_graphics->getFadeStatus() == RDFADE_NONE)
+ _vm->_graphics->fadeDown();
+
+ return IR_CONT;
+}
+
+enum {
+ S_OB_GRAPHIC = 0,
+ S_OB_SPEECH = 1,
+ S_OB_LOGIC = 2,
+ S_OB_MEGA = 3,
+
+ S_TEXT = 4,
+ S_WAV = 5,
+ S_ANIM = 6,
+ S_DIR_TABLE = 7,
+ S_ANIM_MODE = 8
+};
+
+/**
+ * It's the super versatile fnSpeak. Text and wavs can be selected in any
+ * combination.
+ *
+ * @note We can assume no human - there should be no human, at least!
+ */
+
+int32 Logic::fnISpeak(int32 *params) {
+ // params: 0 pointer to ob_graphic
+ // 1 pointer to ob_speech
+ // 2 pointer to ob_logic
+ // 3 pointer to ob_mega
+ // 4 encoded text number
+ // 5 wav res id
+ // 6 anim res id
+ // 7 anim table res id
+ // 8 animation mode 0 lip synced,
+ // 1 just straight animation
+
+ static bool cycle_skip = false;
+ static bool speechRunning;
+
+ // Set up the pointers which we know we'll always need
+
+ ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[S_OB_LOGIC]);
+ ObjectGraphic *ob_graphic = (ObjectGraphic *) _vm->_memory->decodePtr(params[S_OB_GRAPHIC]);
+
+ // FIRST TIME ONLY: create the text, load the wav, set up the anim,
+ // etc.
+
+ if (!ob_logic->looping) {
+ // New fudge to wait for smacker samples to finish
+ // since they can over-run into the game
+
+ if (_vm->_sound->getSpeechStatus() != RDSE_SAMPLEFINISHED)
+ return IR_REPEAT;
+
+ // New fudge for 'fx' subtitles: If subtitles switched off, and
+ // we don't want to use a wav for this line either, then just
+ // quit back to script right now!
+
+ if (!_vm->_gui->_subtitles && !wantSpeechForLine(params[S_WAV]))
+ return IR_CONT;
+
+ // Drop out for 1st cycle to allow walks/anims to end and
+ // display last frame before system locks while speech loaded
+
+ if (!cycle_skip) {
+ cycle_skip = true;
+ return IR_REPEAT;
+ }
+
+ cycle_skip = false;
+
+ _vm->_debugger->_textNumber = params[S_TEXT];
+
+ // Pull out the text line to get the official text number
+ // (for wav id). Once the wav id's go into all script text
+ // commands, we'll only need this for debugging.
+
+ uint32 text_res = params[S_TEXT] / SIZE;
+ uint32 local_text = params[S_TEXT] & 0xffff;
+
+ // For testing all text & speech!
+ //
+ // A script loop can send any text number to fnISpeak and it
+ // will only run the valid ones or return with 'result' equal
+ // to '1' or '2' to mean 'invalid text resource' and 'text
+ // number out of range' respectively
+ //
+ // See 'testing_routines' object in George's Player Character
+ // section of linc
+
+ if (_scriptVars[SYSTEM_TESTING_TEXT]) {
+ if (!_vm->_resman->checkValid(text_res)) {
+ // Not a valid resource number - invalid (null
+ // resource)
+ _scriptVars[RESULT] = 1;
+ return IR_CONT;
+ }
+
+ StandardHeader *head = (StandardHeader *) _vm->_resman->openResource(text_res);
+
+ if (head->fileType != TEXT_FILE) {
+ // Invalid - not a text resource
+ _vm->_resman->closeResource(text_res);
+ _scriptVars[RESULT] = 1;
+ return IR_CONT;
+ }
+
+ if (!_vm->checkTextLine((byte *) head, local_text)) {
+ // Line number out of range
+ _vm->_resman->closeResource(text_res);
+ _scriptVars[RESULT] = 2;
+ return IR_CONT;
+ }
+
+ _vm->_resman->closeResource(text_res);
+ _scriptVars[RESULT] = 0;
+ }
+
+ byte *text = _vm->fetchTextLine(_vm->_resman->openResource(text_res), local_text);
+ _officialTextNumber = READ_LE_UINT16(text);
+ _vm->_resman->closeResource(text_res);
+
+ // Prevent dud lines from appearing while testing text & speech
+ // since these will not occur in the game anyway
+
+ if (_scriptVars[SYSTEM_TESTING_TEXT]) {
+ // If actor number is 0 and text line is just a 'dash'
+ // character
+ if (_officialTextNumber == 0 && text[2] == '-' && text[3] == 0) {
+ _scriptVars[RESULT] = 3;
+ return IR_CONT;
+ }
+ }
+
+ // Set the 'looping_flag' and the text-click-delays. We can
+ // left-click past the text after half a second, and
+ // right-click past it after a quarter of a second.
+
+ ob_logic->looping = 1;
+ _leftClickDelay = 6;
+ _rightClickDelay = 3;
+
+ if (_scriptVars[PLAYER_ID] != CUR_PLAYER_ID)
+ debug(5, "(%d) Nico: %s", _officialTextNumber, text + 2);
+ else {
+ byte buf[NAME_LEN];
+
+ debug(5, "(%d) %s: %s", _officialTextNumber, _vm->fetchObjectName(_scriptVars[ID], buf), text + 2);
+ }
+
+ // Set up the speech animation
+
+ if (params[S_ANIM]) {
+ // Just a straight anim.
+ _animId = params[6];
+ } else if (params[S_DIR_TABLE]) {
+ // Use this direction table to derive the anim
+ // NB. ASSUMES WE HAVE A MEGA OBJECT!!
+
+ ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[S_OB_MEGA]);
+ int32 *anim_table = (int32 *) _vm->_memory->decodePtr(params[S_DIR_TABLE]);
+
+ _animId = anim_table[ob_mega->current_dir];
+ } else {
+ // No animation choosen
+ _animId = 0;
+ }
+
+ if (_animId) {
+ // Set the talker's graphic to the first frame of this
+ // speech anim for now.
+
+ _speechAnimType = _scriptVars[SPEECHANIMFLAG];
+ ob_graphic->anim_resource = _animId;
+ ob_graphic->anim_pc = 0;
+ }
+
+ // Default back to looped lip synced anims.
+ _scriptVars[SPEECHANIMFLAG] = 0;
+
+ // Set up _textX and _textY for speech panning and/or text
+ // sprite position.
+
+ locateTalker(params);
+
+ // Is it to be speech or subtitles or both?
+
+ // Assume not running until know otherwise
+ speechRunning = false;
+
+ // New fudge for 'fx' subtitles: If speech is selected, and
+ // this line is allowed speech (not if it's an fx subtitle!)
+
+ if (!_vm->_sound->isSpeechMute() && wantSpeechForLine(_officialTextNumber)) {
+ // If the wavId parameter is zero because not yet
+ // compiled into speech command, we can still get it
+ // from the 1st 2 chars of the text line.
+
+ if (!params[S_WAV])
+ params[S_WAV] = (int32) _officialTextNumber;
+
+ // Panning goes from -16 (left) to 16 (right)
+ int8 speech_pan = ((_textX - 320) * 16) / 320;
+
+ if (speech_pan < -16)
+ speech_pan = -16;
+ else if (speech_pan > 16)
+ speech_pan = 16;
+
+ uint32 rv = _vm->_sound->playCompSpeech(params[S_WAV], 16, speech_pan);
+
+ if (rv == RD_OK) {
+ // Ok, we've got something to play. Set it
+ // playing now. (We might want to do this the
+ // next cycle, don't know yet.)
+
+ speechRunning = true;
+ _vm->_sound->unpauseSpeech();
+ } else {
+ debug(5, "ERROR: PlayCompSpeech(wav=%d (res=%d pos=%d)) returned %.8x", params[S_WAV], text_res, local_text, rv);
+ }
+ }
+
+ if (_vm->_gui->_subtitles || !speechRunning) {
+ // We want subtitles, or the speech failed to load.
+ // Either way, we're going to show the text so create
+ // the text sprite.
+
+ formText(params);
+ }
+ }
+
+ // EVERY TIME: run a cycle of animation, if there is one
+
+ if (_animId) {
+ // There is an animation - Increment the anim frame number.
+ ob_graphic->anim_pc++;
+
+ byte *anim_file = _vm->_resman->openResource(ob_graphic->anim_resource);
+ AnimHeader *anim_head = _vm->fetchAnimHeader(anim_file);
+
+ if (!_speechAnimType) {
+ // ANIM IS TO BE LIP-SYNC'ED & REPEATING
+
+ if (ob_graphic->anim_pc == (int32) (anim_head->noAnimFrames)) {
+ // End of animation - restart from frame 0
+ ob_graphic->anim_pc = 0;
+ } else if (speechRunning && _vm->_sound->amISpeaking() == RDSE_QUIET) {
+ // The speech is running, but we're at a quiet
+ // bit. Restart from frame 0 (closed mouth).
+ ob_graphic->anim_pc = 0;
+ }
+ } else {
+ // ANIM IS TO PLAY ONCE ONLY
+ if (ob_graphic->anim_pc == (int32) (anim_head->noAnimFrames) - 1) {
+ // Reached the last frame of the anim. Hold
+ // anim on this last frame
+ _animId = 0;
+ }
+ }
+
+ _vm->_resman->closeResource(ob_graphic->anim_resource);
+ } else if (_speechAnimType) {
+ // Placed here so we actually display the last frame of the
+ // anim.
+ _speechAnimType = 0;
+ }
+
+ // EVERY TIME: FIND OUT IF WE NEED TO STOP THE SPEECH NOW...
+
+ // If there is a wav then we're using that to end the speech naturally
+
+ bool speechFinished = false;
+
+ // If playing a sample
+
+ if (speechRunning) {
+ // Has it finished?
+ if (_vm->_sound->getSpeechStatus() == RDSE_SAMPLEFINISHED)
+ speechFinished = true;
+ } else if (!speechRunning && _speechTime) {
+ // Counting down text time because there is no sample - this
+ // ends the speech
+
+ // if no sample then we're using _speechTime to end speech
+ // naturally
+
+ _speechTime--;
+ if (!_speechTime)
+ speechFinished = true;
+ }
+
+ // Ok, all is running along smoothly - but a click means stop
+ // unnaturally
+
+ // So that we can go to the options panel while text & speech is
+ // being tested
+ if (_scriptVars[SYSTEM_TESTING_TEXT] == 0 || _vm->_mouseY > 0) {
+ MouseEvent *me = _vm->mouseEvent();
+
+ // Note that we now have TWO click-delays - one for LEFT
+ // button, one for RIGHT BUTTON
+
+ if ((!_leftClickDelay && me && (me->buttons & RD_LEFTBUTTONDOWN)) ||
+ (!_rightClickDelay && me && (me->buttons & RD_RIGHTBUTTONDOWN))) {
+ // Mouse click, after click_delay has expired -> end
+ // the speech.
+
+ // if testing text & speech
+ if (_scriptVars[SYSTEM_TESTING_TEXT]) {
+ // and RB used to click past text
+ if (me->buttons & RD_RIGHTBUTTONDOWN) {
+ // then we want the previous line again
+ _scriptVars[SYSTEM_WANT_PREVIOUS_LINE] = 1;
+ } else {
+ // LB just want next line again
+ _scriptVars[SYSTEM_WANT_PREVIOUS_LINE] = 0;
+ }
+ }
+
+ speechFinished = true;
+
+ // if speech sample playing, halt it prematurely
+ if (speechRunning)
+ _vm->_sound->stopSpeech();
+ }
+ }
+
+ // If we are finishing the speech this cycle, do the business
+
+ // !speechAnimType, as we want an anim which is playing once to have
+ // finished.
+
+ if (speechFinished && !_speechAnimType) {
+ // If there is text, kill it
+ if (_speechTextBlocNo) {
+ _vm->_fontRenderer->killTextBloc(_speechTextBlocNo);
+ _speechTextBlocNo = 0;
+ }
+
+ // if there is a speech anim, end it on closed mouth frame
+ if (_animId) {
+ _animId = 0;
+ ob_graphic->anim_pc = 0;
+ }
+
+ speechRunning = false;
+
+ // no longer in a script function loop
+ ob_logic->looping = 0;
+
+ _vm->_debugger->_textNumber = 0;
+
+ // reset to zero, in case text line not even extracted (since
+ // this number comes from the text line)
+ _officialTextNumber = 0;
+
+ _scriptVars[RESULT] = 0;
+ return IR_CONT;
+ }
+
+ // Speech still going, so decrement the click_delay if it's still
+ // active
+
+ if (_leftClickDelay)
+ _leftClickDelay--;
+
+ if (_rightClickDelay)
+ _rightClickDelay--;
+
+ return IR_REPEAT;
+}
+
+/**
+ * Reset the object and restart script 1 on level 0
+ */
+
+#define LEVEL (_curObjectHub->logic_level)
+
+int32 Logic::fnTotalRestart(int32 *params) {
+ // mega runs this to restart its base logic again - like being cached
+ // in again
+
+ // params: none
+
+ LEVEL = 0;
+ _curObjectHub->script_pc[0] = 1;
+ return IR_TERMINATE;
+}
+
+int32 Logic::fnSetWalkGrid(int32 *params) {
+ // params: none
+
+ warning("fnSetWalkGrid() is no longer a valid opcode");
+ return IR_CONT;
+}
+
+/**
+ * Receive and sequence the commands sent from the conversation script. We have
+ * to do this in a slightly tweeky manner as we can no longer have generic
+ * scripts.
+ */
+
+enum {
+ INS_talk = 1,
+ INS_anim = 2,
+ INS_reverse_anim = 3,
+ INS_walk = 4,
+ INS_turn = 5,
+ INS_face = 6,
+ INS_trace = 7,
+ INS_no_sprite = 8,
+ INS_sort = 9,
+ INS_foreground = 10,
+ INS_background = 11,
+ INS_table_anim = 12,
+ INS_reverse_table_anim = 13,
+ INS_walk_to_anim = 14,
+ INS_set_frame = 15,
+ INS_stand_after_anim = 16,
+ INS_quit = 42
+};
+
+int32 Logic::fnSpeechProcess(int32 *params) {
+ // params: 0 pointer to ob_graphic
+ // 1 pointer to ob_speech
+ // 2 pointer to ob_logic
+ // 3 pointer to ob_mega
+ // 4 pointer to ob_walkdata
+
+ ObjectSpeech *ob_speech = (ObjectSpeech *) _vm->_memory->decodePtr(params[1]);
+
+ while (1) {
+ int32 pars[9];
+
+ // Check which command we're waiting for, and call the
+ // appropriate function. Once we're done, clear the command
+ // and set wait_state to 1.
+ //
+ // Note: we could save a var and ditch wait_state and check
+ // 'command' for non zero means busy
+ //
+ // Note: I can't see that we ever check the value of wait_state
+ // but perhaps it accesses that memory location directly?
+
+ switch (ob_speech->command) {
+ case 0:
+ break;
+ case INS_talk:
+ pars[0] = params[0]; // ob_graphic
+ pars[1] = params[1]; // ob_speech
+ pars[2] = params[2]; // ob_logic
+ pars[3] = params[3]; // ob_mega
+ pars[4] = ob_speech->ins1; // encoded text number
+ pars[5] = ob_speech->ins2; // wav res id
+ pars[6] = ob_speech->ins3; // anim res id
+ pars[7] = ob_speech->ins4; // anim table res id
+ pars[8] = ob_speech->ins5; // animation mode - 0 lip synced, 1 just straight animation
+
+ if (fnISpeak(pars) != IR_REPEAT) {
+ ob_speech->command = 0;
+ ob_speech->wait_state = 1;
+ }
+
+ return IR_REPEAT;
+ case INS_turn:
+ pars[0] = params[2]; // ob_logic
+ pars[1] = params[0]; // ob_graphic
+ pars[2] = params[3]; // ob_mega
+ pars[3] = params[4]; // ob_walkdata
+ pars[4] = ob_speech->ins1; // direction to turn to
+
+ if (fnTurn(pars) != IR_REPEAT) {
+ ob_speech->command = 0;
+ ob_speech->wait_state = 1;
+ }
+
+ return IR_REPEAT;
+ case INS_face:
+ pars[0] = params[2]; // ob_logic
+ pars[1] = params[0]; // ob_graphic
+ pars[2] = params[3]; // ob_mega
+ pars[3] = params[4]; // ob_walkdata
+ pars[4] = ob_speech->ins1; // target
+
+ if (fnFaceMega(pars) != IR_REPEAT) {
+ ob_speech->command = 0;
+ ob_speech->wait_state = 1;
+ }
+
+ return IR_REPEAT;
+ case INS_anim:
+ pars[0] = params[2]; // ob_logic
+ pars[1] = params[0]; // ob_graphic
+ pars[2] = ob_speech->ins1; // anim res
+
+ if (fnAnim(pars) != IR_REPEAT) {
+ ob_speech->command = 0;
+ ob_speech->wait_state = 1;
+ }
+
+ return IR_REPEAT;
+ case INS_reverse_anim:
+ pars[0] = params[2]; // ob_logic
+ pars[1] = params[0]; // ob_graphic
+ pars[2] = ob_speech->ins1; // anim res
+
+ if (fnReverseAnim(pars) != IR_REPEAT) {
+ ob_speech->command = 0;
+ ob_speech->wait_state = 1;
+ }
+
+ return IR_REPEAT;
+ case INS_table_anim:
+ pars[0] = params[2]; // ob_logic
+ pars[1] = params[0]; // ob_graphic
+ pars[2] = params[3]; // ob_mega
+ pars[3] = ob_speech->ins1; // pointer to anim table
+
+ if (fnMegaTableAnim(pars) != IR_REPEAT) {
+ ob_speech->command = 0;
+ ob_speech->wait_state = 1;
+ }
+
+ return IR_REPEAT;
+ case INS_reverse_table_anim:
+ pars[0] = params[2]; // ob_logic
+ pars[1] = params[0]; // ob_graphic
+ pars[2] = params[3]; // ob_mega
+ pars[3] = ob_speech->ins1; // pointer to anim table
+
+ if (fnReverseMegaTableAnim(pars) != IR_REPEAT) {
+ ob_speech->command = 0;
+ ob_speech->wait_state = 1;
+ }
+
+ return IR_REPEAT;
+ case INS_no_sprite:
+ fnNoSprite(params); // ob_graphic
+
+ ob_speech->command = 0;
+ ob_speech->wait_state = 1;
+ return IR_REPEAT ;
+ case INS_sort:
+ fnSortSprite(params); // ob_graphic
+
+ ob_speech->command = 0;
+ ob_speech->wait_state = 1;
+ return IR_REPEAT;
+ case INS_foreground:
+ fnForeSprite(params); // ob_graphic
+
+ ob_speech->command = 0;
+ ob_speech->wait_state = 1;
+ return IR_REPEAT;
+ case INS_background:
+ fnBackSprite(params); // ob_graphic
+
+ ob_speech->command = 0;
+ ob_speech->wait_state = 1;
+ return IR_REPEAT;
+ case INS_walk:
+ pars[0] = params[2]; // ob_logic
+ pars[1] = params[0]; // ob_graphic
+ pars[2] = params[3]; // ob_mega
+ pars[3] = params[4]; // ob_walkdata
+ pars[4] = ob_speech->ins1; // target x
+ pars[5] = ob_speech->ins2; // target y
+ pars[6] = ob_speech->ins3; // target direction
+
+ if (fnWalk(pars) != IR_REPEAT) {
+ ob_speech->command = 0;
+ ob_speech->wait_state = 1;
+ }
+
+ return IR_REPEAT;
+ case INS_walk_to_anim:
+ pars[0] = params[2]; // ob_logic
+ pars[1] = params[0]; // ob_graphic
+ pars[2] = params[3]; // ob_mega
+ pars[3] = params[4]; // ob_walkdata
+ pars[4] = ob_speech->ins1; // anim resource
+
+ if (fnWalkToAnim(pars) != IR_REPEAT) {
+ ob_speech->command = 0;
+ ob_speech->wait_state = 1;
+ }
+
+ return IR_REPEAT;
+ case INS_stand_after_anim:
+ pars[0] = params[0]; // ob_graphic
+ pars[1] = params[3]; // ob_mega
+ pars[2] = ob_speech->ins1; // anim resource
+
+ fnStandAfterAnim(pars);
+
+ ob_speech->command = 0;
+ ob_speech->wait_state = 1;
+ return IR_REPEAT;
+ case INS_set_frame:
+ pars[0] = params[0]; // ob_graphic
+ pars[1] = ob_speech->ins1; // anim_resource
+ pars[2] = ob_speech->ins2; // FIRST_FRAME or LAST_FRAME
+ fnSetFrame(pars);
+
+ ob_speech->command = 0;
+ ob_speech->wait_state = 1;
+ return IR_REPEAT;
+ case INS_quit:
+ // That's it - we're finished with this
+ ob_speech->command = 0;
+ // ob_speech->wait_state = 0;
+ return IR_CONT;
+ default:
+ // Unimplemented command - just cancel
+ ob_speech->command = 0;
+ ob_speech->wait_state = 1;
+ break;
+ }
+
+ if (_scriptVars[SPEECH_ID] == _scriptVars[ID]) {
+ // There's a new command for us! Grab the command -
+ // potentially we only have this cycle to do this - and
+ // set things up so that the command will be picked up
+ // on the next iteration of the while loop.
+
+ debug(5, "fnSpeechProcess: Received new command %d", _scriptVars[INS_COMMAND]);
+
+ _scriptVars[SPEECH_ID] = 0;
+
+ ob_speech->command = _scriptVars[INS_COMMAND];
+ ob_speech->ins1 = _scriptVars[INS1];
+ ob_speech->ins2 = _scriptVars[INS2];
+ ob_speech->ins3 = _scriptVars[INS3];
+ ob_speech->ins4 = _scriptVars[INS4];
+ ob_speech->ins5 = _scriptVars[INS5];
+ ob_speech->wait_state = 0;
+
+ _scriptVars[INS_COMMAND] = 0;
+ } else {
+ // No new command. We could run a blink anim (or
+ // something) here.
+
+ ob_speech->wait_state = 1;
+ return IR_REPEAT;
+ }
+ }
+}
+
+int32 Logic::fnSetScaling(int32 *params) {
+ // params: 0 pointer to object's mega structure
+ // 1 scale constant A
+ // 2 scale constant B
+
+ // 256 * s = A * y + B
+
+ // Where s is system scale, which itself is (256 * actual_scale) ie.
+ // s == 128 is half size
+
+ ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[0]);
+
+ ob_mega->scale_a = params[1];
+ ob_mega->scale_b = params[2];
+
+ return IR_CONT;
+}
+
+int32 Logic::fnStartEvent(int32 *params) {
+ // params: none
+
+ startEvent();
+ return IR_TERMINATE;
+}
+
+int32 Logic::fnCheckEventWaiting(int32 *params) {
+ // params: none
+
+ _scriptVars[RESULT] = checkEventWaiting();
+ return IR_CONT;
+}
+
+int32 Logic::fnRequestSpeech(int32 *params) {
+ // change current script - must be followed by a TERMINATE script
+ // directive
+
+ // params: 0 id of target to catch the event and startup speech
+ // servicing
+
+ // Full script id to interact with - megas run their own 7th script
+ sendEvent(params[0], (params[0] << 16) | 6);
+ return IR_CONT;
+}
+
+int32 Logic::fnGosub(int32 *params) {
+ // params: 0 id of script
+
+ // Hurray, script subroutines. Logic goes up - pc is saved for current
+ // level.
+ logicUp(params[0]);
+ return IR_GOSUB;
+}
+
+/**
+ * Wait for a target to become waiting, i.e. not busy, or until we time out.
+ * This is useful when clicking on a target to talk to it, and it doesn't
+ * reply. This way, we won't lock up.
+ *
+ * If the target becomes waiting, RESULT is set to 0. If we time out, RESULT is
+ * set to 1.
+ */
+
+int32 Logic::fnTimedWait(int32 *params) {
+ // params: 0 ob_logic
+ // 1 target
+ // 2 number of cycles before give up
+
+ StandardHeader *head = (StandardHeader *) _vm->_resman->openResource(params[1]);
+ assert(head->fileType == GAME_OBJECT);
+
+ ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
+
+ if (!ob_logic->looping) {
+ // This is the first time, so set up the time-out.
+ ob_logic->looping = params[2];
+ }
+
+ // Run the target's get-speech-state script
+
+ int32 target = params[1];
+ char *raw_script_ad = (char *) head;
+ uint32 null_pc = 5;
+
+ runScript(raw_script_ad, raw_script_ad, &null_pc);
+
+ _vm->_resman->closeResource(target);
+
+ if (_scriptVars[RESULT] == 1) {
+ // The target is waiting, i.e. not busy
+
+ _vm->_debugger->_speechScriptWaiting = 0;
+
+ ob_logic->looping = 0;
+ _scriptVars[RESULT] = 0;
+ return IR_CONT;
+ }
+
+ ob_logic->looping--;
+
+ if (!ob_logic->looping) {
+ // Time's up.
+
+ debug(5, "fnTimedWait: Timed out waiting for %d", target);
+ _vm->_debugger->_speechScriptWaiting = 0;
+
+ // Clear the event that hasn't been picked up - in theory,
+ // none of this should ever happen.
+
+ killAllIdsEvents(target);
+ _scriptVars[RESULT] = 1;
+ return IR_CONT;
+ }
+
+ // Target is busy. Keep trying.
+
+ _vm->_debugger->_speechScriptWaiting = target;
+ return IR_REPEAT;
+}
+
+int32 Logic::fnPlayFx(int32 *params) {
+ // params: 0 sample resource id
+ // 1 type (FX_SPOT, FX_RANDOM, FX_LOOP)
+ // 2 delay (0..65535)
+ // 3 volume (0..16)
+ // 4 pan (-16..16)
+
+ // example script:
+ // fnPlayFx (FXWATER, FX_LOOP, 0, 10, 15);
+ // // fx_water is just a local script flag
+ // fx_water = result;
+ // .
+ // .
+ // .
+ // fnStopFx (fx_water);
+
+ int32 res = params[0];
+ int32 type = params[1];
+ int32 delay = params[2];
+ int32 volume = params[3];
+ int32 pan = params[4];
+
+ _vm->_sound->queueFx(res, type, delay, volume, pan);
+ return IR_CONT;
+}
+
+int32 Logic::fnStopFx(int32 *params) {
+ // params: 0 position in queue
+ if (_vm->_sound->stopFx(params[0]) != RD_OK)
+ debug(5, "SFX ERROR: Trying to stop an inactive sound slot");
+
+ return IR_CONT;
+}
+
+/**
+ * Start a tune playing, to play once or to loop until stopped or next one
+ * played.
+ */
+
+int32 Logic::fnPlayMusic(int32 *params) {
+ // params: 0 tune id
+ // 1 loop flag (0 or 1)
+
+ char filename[128];
+ bool loopFlag;
+ uint32 rv;
+
+ loopFlag = (params[1] == FX_LOOP);
+
+ rv = _vm->_sound->streamCompMusic(params[0], loopFlag);
+
+ if (rv)
+ debug(5, "ERROR: streamCompMusic(%s, %d, %d) returned error 0x%.8x", filename, params[0], loopFlag, rv);
+
+ return IR_CONT;
+}
+
+int32 Logic::fnStopMusic(int32 *params) {
+ // params: none
+
+ _vm->_sound->stopMusic();
+ return IR_CONT;
+}
+
int32 Logic::fnSetValue(int32 *params) {
// temp. function!
@@ -213,6 +2114,317 @@ int32 Logic::fnSetValue(int32 *params) {
return IR_CONT;
}
+int32 Logic::fnNewScript(int32 *params) {
+ // change current script - must be followed by a TERMINATE script
+ // directive
+
+ // params: 0 id of script
+
+ _scriptVars[PLAYER_ACTION] = 0; // must clear this
+ logicReplace(params[0]);
+ return IR_TERMINATE;
+}
+
+/**
+ * Like getSync(), but called from scripts. Sets the RESULT variable to
+ * the sync value, or 0 if none is found.
+ */
+
+int32 Logic::fnGetSync(int32 *params) {
+ // params: none
+
+ int slot = getSync();
+
+ _scriptVars[RESULT] = (slot != -1) ? _syncList[slot].sync : 0;
+ return IR_CONT;
+}
+
+/**
+ * Wait for sync to happen. Sets the RESULT variable to the sync value, once
+ * it has been found.
+ */
+
+int32 Logic::fnWaitSync(int32 *params) {
+ // params: none
+
+ debug(6, "fnWaitSync: %d waits", _scriptVars[ID]);
+
+ int slot = getSync();
+
+ if (slot == -1)
+ return IR_REPEAT;
+
+ debug(5, "fnWaitSync: %d got sync %d", _scriptVars[ID], _syncList[slot].sync);
+ _scriptVars[RESULT] = _syncList[slot].sync;
+ return IR_CONT;
+}
+
+int32 Logic::fnRegisterWalkGrid(int32 *params) {
+ // params: none
+
+ warning("fnRegisterWalkGrid() is no longer a valid opcode");
+ return IR_CONT;
+}
+
+int32 Logic::fnReverseMegaTableAnim(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+ // 3 pointer to animation table
+
+ // 1 means reverse anim
+ return megaTableAnimate(params, true);
+}
+
+int32 Logic::fnReverseAnim(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 resource id of animation file
+
+ // 1 means reverse anim
+ return animate(params, true);
+}
+
+/**
+ * Mark this object for killing - to be killed when player leaves this screen.
+ * Object reloads and script restarts upon re-entry to screen, which causes
+ * this object's startup logic to be re-run every time we enter the screen.
+ * "Which is nice."
+ *
+ * @note Call ONCE from object's logic script, i.e. in startup code, so not
+ * re-called every time script frops off and restarts!
+ */
+
+int32 Logic::fnAddToKillList(int32 *params) {
+ // params: none
+
+ // DON'T EVER KILL GEORGE!
+ if (_scriptVars[ID] == CUR_PLAYER_ID)
+ return IR_CONT;
+
+ // Scan the list to see if it's already included
+
+ for (uint32 i = 0; i < _kills; i++) {
+ if (_objectKillList[i] == _scriptVars[ID])
+ return IR_CONT;
+ }
+
+ assert(_kills < OBJECT_KILL_LIST_SIZE); // no room at the inn
+
+ _objectKillList[_kills++] = _scriptVars[ID];
+
+ // "another one bites the dust"
+
+ // When we leave the screen, all these object resources are to be
+ // cleaned out of memory and the kill list emptied by doing
+ // '_kills = 0', ensuring that all resources are in fact still in
+ // memory and, more importantly, closed before killing!
+
+ return IR_CONT;
+}
+
+/**
+ * Set the standby walk coords to be used by fnWalkToAnim() and
+ * fnStandAfterAnim() when the anim header's start/end coords are zero.
+ * Useful during development; can stay in final game anyway.
+ */
+
+int32 Logic::fnSetStandbyCoords(int32 *params) {
+ // params: 0 x-coord
+ // 1 y-coord
+ // 2 direction (0..7)
+
+ assert(params[2] >= 0 && params[2] <= 7);
+
+ _standbyX = (int16) params[0];
+ _standbyY = (int16) params[1];
+ _standbyDir = (uint8) params[2];
+
+ return IR_CONT;
+}
+
+int32 Logic::fnBackPar0Sprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ setSpriteStatus(params[0], BGP0_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnBackPar1Sprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ setSpriteStatus(params[0], BGP1_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnForePar0Sprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ setSpriteStatus(params[0], FGP0_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnForePar1Sprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ setSpriteStatus(params[0], FGP1_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnSetPlayerActionEvent(int32 *params) {
+ // we want to intercept the player character and have him interact
+ // with an object - from script this code is the same as the mouse
+ // engine calls when you click on an object - here, a third party
+ // does the clicking IYSWIM
+
+ // note - this routine used CUR_PLAYER_ID as the target
+
+ // params: 0 id to interact with
+
+ setPlayerActionEvent(CUR_PLAYER_ID, params[0]);
+ return IR_CONT;
+}
+
+/**
+ * Set the special scroll offset variables
+ *
+ * Call when starting screens and to change the camera within screens
+ *
+ * call AFTER fnInitBackground() to override the defaults
+ */
+
+int32 Logic::fnSetScrollCoordinate(int32 *params) {
+ // params: 0 feet_x value
+ // 1 feet_y value
+
+ // Called feet_x and feet_y to retain intellectual compatibility with
+ // Sword1!
+ //
+ // feet_x & feet_y refer to the physical screen coords where the
+ // system will try to maintain George's feet
+
+ _vm->_thisScreen.feet_x = params[0];
+ _vm->_thisScreen.feet_y = params[1];
+ return IR_CONT;
+}
+
+/**
+ * Stand mega at start position of anim
+ */
+
+int32 Logic::fnStandAtAnim(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ // 1 pointer to object's mega structure
+ // 2 anim resource id
+
+ byte *anim_file = _vm->_resman->openResource(params[2]);
+ AnimHeader *anim_head = _vm->fetchAnimHeader(anim_file);
+
+ int32 pars[5];
+
+ pars[0] = params[0];
+ pars[1] = params[1];
+
+ pars[2] = anim_head->feetStartX;
+ pars[3] = anim_head->feetStartY;
+ pars[4] = anim_head->feetStartDir;
+
+ // If start coords not available use the standby coords (which should
+ // be set beforehand in the script)
+
+ if (pars[2] == 0 && pars[3] == 0) {
+ byte buf[NAME_LEN];
+
+ pars[2] = _standbyX;
+ pars[3] = _standbyY;
+ pars[4] = _standbyDir;
+
+ debug(3, "WARNING: fnStandAtAnim(%s) used standby coords", _vm->fetchObjectName(params[2], buf));
+ }
+
+ assert(pars[4] >= 0 && pars[4] <= 7);
+
+ _vm->_resman->closeResource(params[2]);
+ return fnStandAt(pars);
+}
+
+#define SCROLL_MOUSE_WIDTH 20
+
+int32 Logic::fnSetScrollLeftMouse(int32 *params) {
+ // params: 0 pointer to object's mouse structure
+
+ ObjectMouse *ob_mouse = (ObjectMouse *) _vm->_memory->decodePtr(params[0]);
+
+ // Highest priority
+
+ ob_mouse->x1 = 0;
+ ob_mouse->y1 = 0;
+ ob_mouse->x2 = _vm->_thisScreen.scroll_offset_x + SCROLL_MOUSE_WIDTH;
+ ob_mouse->y2 = _vm->_thisScreen.screen_deep - 1;
+ ob_mouse->priority = 0;
+
+ if (_vm->_thisScreen.scroll_offset_x > 0) {
+ // not fully scrolled to the left
+ ob_mouse->pointer = SCROLL_LEFT_MOUSE_ID;
+ } else {
+ // so the mouse area doesn't get registered
+ ob_mouse->pointer = 0;
+ }
+
+ return IR_CONT;
+}
+
+int32 Logic::fnSetScrollRightMouse(int32 *params) {
+ // params: 0 pointer to object's mouse structure
+
+ ObjectMouse *ob_mouse = (ObjectMouse *) _vm->_memory->decodePtr(params[0]);
+
+ // Highest priority
+
+ ob_mouse->x1 = _vm->_thisScreen.scroll_offset_x + _vm->_graphics->_screenWide - SCROLL_MOUSE_WIDTH;
+ ob_mouse->y1 = 0;
+ ob_mouse->x2 = _vm->_thisScreen.screen_wide - 1;
+ ob_mouse->y2 = _vm->_thisScreen.screen_deep - 1;
+ ob_mouse->priority = 0;
+
+ if (_vm->_thisScreen.scroll_offset_x < _vm->_thisScreen.max_scroll_offset_x) {
+ // not fully scrolled to the right
+ ob_mouse->pointer = SCROLL_RIGHT_MOUSE_ID;
+ } else {
+ // so the mouse area doesn't get registered
+ ob_mouse->pointer = 0;
+ }
+
+ return IR_CONT;
+}
+
+int32 Logic::fnColour(int32 *params) {
+ // set border colour - useful during script development
+ // eg. set to colour during a timer situation, then black when timed
+ // out
+
+ // params 0: colour (see defines above)
+
+#ifdef SWORD2_DEBUG
+ // what colour?
+ switch (params[0]) {
+ case BLACK:
+ _vm->_graphics->setPalette(0, 1, black, RDPAL_INSTANT);
+ break;
+ case WHITE:
+ _vm->_graphics->setPalette(0, 1, white, RDPAL_INSTANT);
+ break;
+ case RED:
+ _vm->_graphics->setPalette(0, 1, red, RDPAL_INSTANT);
+ break;
+ case GREEN:
+ _vm->_graphics->setPalette(0, 1, green, RDPAL_INSTANT);
+ break;
+ case BLUE:
+ _vm->_graphics->setPalette(0, 1, blue, RDPAL_INSTANT);
+ break;
+ }
+#endif
+
+ return IR_CONT;
+}
+
#ifdef SWORD2_DEBUG
#define BLACK 0
#define WHITE 1
@@ -262,34 +2474,300 @@ int32 Logic::fnFlash(int32 *params) {
return IR_CONT;
}
+int32 Logic::fnPreFetch(int32 *params) {
+ // Go fetch resource in the background.
-int32 Logic::fnColour(int32 *params) {
- // set border colour - useful during script development
- // eg. set to colour during a timer situation, then black when timed
- // out
+ // params: 0 resource to fetch [guess]
- // params 0: colour (see defines above)
+ return IR_CONT;
+}
-#ifdef SWORD2_DEBUG
- // what colour?
- switch (params[0]) {
- case BLACK:
- _vm->_graphics->setPalette(0, 1, black, RDPAL_INSTANT);
- break;
- case WHITE:
- _vm->_graphics->setPalette(0, 1, white, RDPAL_INSTANT);
- break;
- case RED:
- _vm->_graphics->setPalette(0, 1, red, RDPAL_INSTANT);
- break;
- case GREEN:
- _vm->_graphics->setPalette(0, 1, green, RDPAL_INSTANT);
- break;
- case BLUE:
- _vm->_graphics->setPalette(0, 1, blue, RDPAL_INSTANT);
- break;
+/**
+ * Reverse of fnPassPlayerSaveData() - run script 8 of player object.
+ */
+
+int32 Logic::fnGetPlayerSaveData(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+
+ byte *logic_ptr = _vm->_memory->decodePtr(params[0]);
+ byte *graphic_ptr = _vm->_memory->decodePtr(params[1]);
+ byte *mega_ptr = _vm->_memory->decodePtr(params[2]);
+
+ // Copy from savegame header to player object
+
+ memcpy(logic_ptr, &_vm->_saveGameHeader.logic, sizeof(ObjectLogic));
+ memcpy(graphic_ptr, &_vm->_saveGameHeader.graphic, sizeof(ObjectGraphic));
+ memcpy(mega_ptr, &_vm->_saveGameHeader.mega, sizeof(ObjectMega));
+
+ // Any walk-data must be cleared - the player will be set to stand if
+ // he was walking when saved.
+
+ ObjectMega *ob_mega = (ObjectMega *) mega_ptr;
+
+ if (ob_mega->currently_walking) {
+ ob_mega->currently_walking = 0;
+
+ int32 pars[3];
+
+ pars[0] = params[1]; // ob_graphic;
+ pars[1] = params[2]; // ob_mega
+ pars[2] = ob_mega->current_dir;
+
+ fnStand(pars);
+
+ // Reset looping flag (which would have been 1 during fnWalk)
+ ObjectLogic *ob_logic = (ObjectLogic *) logic_ptr;
+
+ ob_logic->looping = 0;
}
-#endif
+
+ return IR_CONT;
+}
+
+/**
+ * Copies the 4 essential player structures into the savegame header - run
+ * script 7 of player object to request this.
+ *
+ * Remember, we cannot simply read a compact any longer but instead must
+ * request it from the object itself.
+ */
+
+int32 Logic::fnPassPlayerSaveData(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+
+ // Copy from player object to savegame header
+
+ memcpy(&_vm->_saveGameHeader.logic, _vm->_memory->decodePtr(params[0]), sizeof(ObjectLogic));
+ memcpy(&_vm->_saveGameHeader.graphic, _vm->_memory->decodePtr(params[1]), sizeof(ObjectGraphic));
+ memcpy(&_vm->_saveGameHeader.mega, _vm->_memory->decodePtr(params[2]), sizeof(ObjectMega));
+
+ return IR_CONT;
+}
+
+int32 Logic::fnSendEvent(int32 *params) {
+ // we want to intercept the player character and have him interact
+ // with an object - from script
+
+ // params: 0 id to receive event
+ // 1 script to run
+
+ sendEvent(params[0], params[1]);
+ return IR_CONT;
+}
+
+/**
+ * Add this walkgrid resource to the list of those used for routing in this
+ * location. Note that this is ignored if the resource is already in the list.
+ */
+
+int32 Logic::fnAddWalkGrid(int32 *params) {
+ // params: 0 id of walkgrid resource
+
+ // All objects that add walkgrids must be restarted whenever we
+ // re-enter a location.
+
+ // DON'T EVER KILL GEORGE!
+ if (_scriptVars[ID] != 8) {
+ // Need to call this in case it wasn't called in script!
+ fnAddToKillList(NULL);
+ }
+
+ _router->addWalkGrid(params[0]);
+ fnPreLoad(params);
+ return IR_CONT;
+}
+
+/**
+ * Remove this walkgrid resource from the list of those used for routing in
+ * this location. Note that this is ignored if the resource isn't actually
+ * in the list.
+ */
+
+int32 Logic::fnRemoveWalkGrid(int32 *params) {
+ // params: 0 id of walkgrid resource
+
+ _router->removeWalkGrid(params[0]);
+ return IR_CONT;
+}
+
+// like fnCheckEventWaiting, but starts the event rather than setting RESULT
+// to 1
+
+int32 Logic::fnCheckForEvent(int32 *params) {
+ // params: none
+
+ if (checkEventWaiting()) {
+ startEvent();
+ return IR_TERMINATE;
+ }
+
+ return IR_CONT;
+}
+
+// combination of fnPause and fnCheckForEvent
+// - ie. does a pause, but also checks for event each cycle
+
+int32 Logic::fnPauseForEvent(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 number of game-cycles to pause
+
+ ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
+
+ if (checkEventWaiting()) {
+ ob_logic->looping = 0;
+ startEvent();
+ return IR_TERMINATE;
+ }
+
+ return fnPause(params);
+}
+
+int32 Logic::fnClearEvent(int32 *params) {
+ // params: none
+
+ clearEvent(_scriptVars[ID]);
+ return IR_CONT;
+}
+
+int32 Logic::fnFaceMega(int32 *params) {
+ // params: 0 pointer to object's logic structure
+ // 1 pointer to object's graphic structure
+ // 2 pointer to object's mega structure
+ // 3 pointer to object's walkdata structure
+ // 4 id of target mega to face
+
+ int32 pars[7];
+
+ pars[0] = params[0];
+ pars[1] = params[1];
+ pars[2] = params[2];
+ pars[3] = params[3];
+
+ ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
+
+ // If this is the start of the walk, decide where to walk to.
+
+ if (!ob_logic->looping) {
+ StandardHeader *head = (StandardHeader *) _vm->_resman->openResource(params[4]);
+
+ assert(head->fileType == GAME_OBJECT);
+
+ // Call the base script. This is the graphic/mouse service
+ // call, and will set _engineMega to the ObjectMega of mega we
+ // want to turn to face.
+
+ char *raw_script_ad = (char *) head;
+ uint32 null_pc = 3;
+
+ runScript(raw_script_ad, raw_script_ad, &null_pc);
+
+ _vm->_resman->closeResource(params[4]);
+
+ ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[2]);
+
+ pars[3] = params[3];
+ pars[4] = ob_mega->feet_x;
+ pars[5] = ob_mega->feet_y;
+ pars[6] = whatTarget(ob_mega->feet_x, ob_mega->feet_y, _engineMega.feet_x, _engineMega.feet_y);
+ }
+
+ return fnWalk(pars);
+}
+
+int32 Logic::fnPlaySequence(int32 *params) {
+ // params: 0 pointer to null-terminated ascii filename
+ // 1 number of frames in the sequence, used for PSX.
+
+ char filename[30];
+ MovieTextObject *sequenceSpeechArray[MAX_SEQUENCE_TEXT_LINES + 1];
+
+ // The original code had some #ifdef blocks for skipping or muting the
+ // cutscenes - fondly described as "the biggest fudge in the history
+ // of computer games" - but at the very least we want to show the
+ // cutscene subtitles, so I removed them.
+
+ debug(5, "fnPlaySequence(\"%s\");", (const char *) _vm->_memory->decodePtr(params[0]));
+
+ // add the appropriate file extension & play it
+
+ strcpy(filename, (const char *) _vm->_memory->decodePtr(params[0]));
+
+ // Write to walkthrough file (zebug0.txt)
+ debug(5, "PLAYING SEQUENCE \"%s\"", filename);
+
+ // now create the text sprites, if any
+
+ if (_sequenceTextLines)
+ createSequenceSpeech(sequenceSpeechArray);
+
+ // don't want to carry on streaming game music when smacker starts!
+ fnStopMusic(NULL);
+
+ // pause sfx during sequence
+ _vm->_sound->pauseFx();
+
+ MoviePlayer player(_vm);
+ uint32 rv;
+
+ if (_sequenceTextLines && !_scriptVars[DEMO])
+ rv = player.play(filename, sequenceSpeechArray, _smackerLeadIn, _smackerLeadOut);
+ else
+ rv = player.play(filename, NULL, _smackerLeadIn, _smackerLeadOut);
+
+ // check the error return-value
+ if (rv)
+ debug(5, "MoviePlayer.play(\"%s\") returned 0x%.8x", filename, rv);
+
+ // unpause sound fx again, in case we're staying in same location
+ _vm->_sound->unpauseFx();
+
+ _smackerLeadIn = 0;
+ _smackerLeadOut = 0;
+
+ // now clear the text sprites, if any
+
+ if (_sequenceTextLines)
+ clearSequenceSpeech(sequenceSpeechArray);
+
+ // now clear the screen in case the Sequence was quitted (using ESC)
+ // rather than fading down to black
+
+ _vm->_graphics->clearScene();
+
+ // zero the entire palette in case we're about to fade up!
+
+ byte pal[4 * 256];
+
+ memset(pal, 0, sizeof(pal));
+ _vm->_graphics->setPalette(0, 256, pal, RDPAL_INSTANT);
+
+ debug(5, "fnPlaySequence FINISHED");
+ return IR_CONT;
+}
+
+int32 Logic::fnShadedSprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ setSpriteShading(params[0], SHADED_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnUnshadedSprite(int32 *params) {
+ // params: 0 pointer to object's graphic structure
+ setSpriteShading(params[0], UNSHADED_SPRITE);
+ return IR_CONT;
+}
+
+int32 Logic::fnFadeUp(int32 *params) {
+ // params: none
+
+ _vm->_graphics->waitForFade();
+
+ if (_vm->_graphics->getFadeStatus() == RDFADE_BLACK)
+ _vm->_graphics->fadeUp();
return IR_CONT;
}
@@ -313,6 +2791,33 @@ int32 Logic::fnDisplayMsg(int32 *params) {
return IR_CONT;
}
+int32 Logic::fnSetObjectHeld(int32 *params) {
+ // params: 0 luggage icon to set
+
+ _vm->setLuggage(params[0]);
+
+ _scriptVars[OBJECT_HELD] = params[0];
+ _vm->_currentLuggageResource = params[0];
+
+ // mode locked - no menu available
+ _vm->_mouseModeLocked = true;
+ return IR_CONT;
+}
+
+int32 Logic::fnAddSequenceText(int32 *params) {
+ // params: 0 text number
+ // 1 frame number to start the text displaying
+ // 2 frame number to stop the text dispalying
+
+ assert(_sequenceTextLines < MAX_SEQUENCE_TEXT_LINES);
+
+ _sequenceTextList[_sequenceTextLines].textNumber = params[0];
+ _sequenceTextList[_sequenceTextLines].startFrame = params[1];
+ _sequenceTextList[_sequenceTextLines].endFrame = params[2];
+ _sequenceTextLines++;
+ return IR_CONT;
+}
+
int32 Logic::fnResetGlobals(int32 *params) {
// fnResetGlobals is used by the demo - so it can loop back & restart
// itself
@@ -346,6 +2851,132 @@ int32 Logic::fnResetGlobals(int32 *params) {
return IR_CONT;
}
+int32 Logic::fnSetPalette(int32 *params) {
+ // params: 0 resource number of palette file, or 0 if it's to be
+ // the palette from the current screen
+
+ _vm->setFullPalette(params[0]);
+ return IR_CONT;
+}
+
+// use this in the object's service script prior to registering the mouse area
+// ie. before fnRegisterMouse or fnRegisterFrame
+// - best if kept at very top of service script
+
+int32 Logic::fnRegisterPointerText(int32 *params) {
+ // params: 0 local id of text line to use as pointer text
+
+ assert(_vm->_curMouse < TOTAL_mouse_list);
+
+ // current object id - used for checking pointer_text when mouse area
+ // registered (in fnRegisterMouse and fnRegisterFrame)
+
+ _vm->_mouseList[_vm->_curMouse].id = _scriptVars[ID];
+ _vm->_mouseList[_vm->_curMouse].pointer_text = params[0];
+ return IR_CONT;
+}
+
+int32 Logic::fnFetchWait(int32 *params) {
+ // Fetches a resource in the background but prevents the script from
+ // continuing until the resource is in memory.
+
+ // params: 0 resource to fetch [guess]
+
+ return IR_CONT;
+}
+
+int32 Logic::fnRelease(int32 *params) {
+ // Releases a resource from memory. Used for freeing memory for
+ // sprites that have just been used and will not be used again.
+ // Sometimes it is better to kick out a sprite straight away so that
+ // the memory can be used for more frequent animations.
+
+ // params: 0 resource to release [guess]
+
+ return IR_CONT;
+}
+
+int32 Logic::fnPrepareMusic(int32 *params) {
+ // params: 1 id of music to prepare [guess]
+ return IR_CONT;
+}
+
+int32 Logic::fnSoundFetch(int32 *params) {
+ // params: 0 id of sound to fetch [guess]
+ return IR_CONT;
+}
+
+int32 Logic::fnSmackerLeadIn(int32 *params) {
+ // params: 0 id of lead-in music
+
+ // ready for use in fnPlaySequence
+ _smackerLeadIn = params[0];
+ return IR_CONT;
+}
+
+int32 Logic::fnSmackerLeadOut(int32 *params) {
+ // params: 0 id of lead-out music
+
+ // ready for use in fnPlaySequence
+ _smackerLeadOut = params[0];
+ return IR_CONT;
+}
+
+/**
+ * Stops all FX and clears the entire FX queue.
+ */
+
+int32 Logic::fnStopAllFx(int32 *params) {
+ // params: none
+
+ _vm->_sound->clearFxQueue();
+ return IR_CONT;
+}
+
+int32 Logic::fnCheckPlayerActivity(int32 *params) {
+ // Used to decide when to trigger music cues described as "no player
+ // activity for a while"
+
+ // params: 0 threshold delay in seconds, ie. what we want to
+ // check the actual delay against
+
+ uint32 threshold = params[0] * 12; // in game cycles
+
+ // if the actual delay is at or above the given threshold
+ if (_vm->_playerActivityDelay >= threshold) {
+ // reset activity delay counter, now that we've got a
+ // positive check
+
+ _vm->_playerActivityDelay = 0;
+ _scriptVars[RESULT] = 1;
+ } else
+ _scriptVars[RESULT] = 0;
+
+ return IR_CONT;
+}
+
+int32 Logic::fnResetPlayerActivityDelay(int32 *params) {
+ // Use if you want to deliberately reset the "no player activity"
+ // counter for any reason
+
+ // params: none
+
+ _vm->_playerActivityDelay = 0;
+ return IR_CONT;
+}
+
+int32 Logic::fnCheckMusicPlaying(int32 *params) {
+ // params: none
+
+ // sets result to no. of seconds of current tune remaining
+ // or 0 if no music playing
+
+ // in seconds, rounded up to the nearest second
+ _scriptVars[RESULT] = _vm->_sound->musicTimeRemaining();
+
+ return IR_CONT;
+}
+
// FIXME:
//
// The original credits used a different font. I think it's stored in the
@@ -374,7 +3005,7 @@ struct CreditsLine {
#define CREDITS_LINE_SPACING 20
int32 Logic::fnPlayCredits(int32 *params) {
- uint32 loopingMusicId = _vm->_loopingMusicId;
+ uint32 loopingMusicId = _vm->_sound->getLoopingMusicId();
// This function just quits the game if this is the playable demo, ie.
// credits are NOT played in the demo any more!
@@ -723,4 +3354,96 @@ int32 Logic::fnPlayCredits(int32 *params) {
return IR_CONT;
}
+int32 Logic::fnSetScrollSpeedNormal(int32 *params) {
+ // params: none
+
+ _vm->_scrollFraction = 16;
+ return IR_CONT;
+}
+
+int32 Logic::fnSetScrollSpeedSlow(int32 *params) {
+ // params: none
+
+ _vm->_scrollFraction = 32;
+ return IR_CONT;
+}
+
+// called from speech scripts to remove the chooser bar when it's not
+// appropriate to keep it displayed
+
+int32 Logic::fnRemoveChooser(int32 *params) {
+ // params: none
+
+ _vm->_graphics->hideMenu(RDMENU_BOTTOM);
+ return IR_CONT;
+}
+
+/**
+ * Alter the volume and pan of a currently playing FX
+ */
+
+int32 Logic::fnSetFxVolAndPan(int32 *params) {
+ // params: 0 id of fx (ie. the id returned in 'result' from
+ // fnPlayFx
+ // 1 new volume (0..16)
+ // 2 new pan (-16..16)
+
+ debug(5, "fnSetFxVolAndPan(%d, %d, %d)", params[0], params[1], params[2]);
+
+ _vm->_sound->setFxIdVolumePan(params[0], params[1], params[2]);
+ return IR_CONT;
+}
+
+/**
+ * Alter the volume of a currently playing FX
+ */
+
+int32 Logic::fnSetFxVol(int32 *params) {
+ // params: 0 id of fx (ie. the id returned in 'result' from
+ // fnPlayFx
+ // 1 new volume (0..16)
+
+ _vm->_sound->setFxIdVolumePan(params[0], params[1]);
+ return IR_CONT;
+}
+
+int32 Logic::fnRestoreGame(int32 *params) {
+ // params: none
+ return IR_CONT;
+}
+
+int32 Logic::fnRefreshInventory(int32 *params) {
+ // called from 'menu_look_or_combine' script in 'menu_master' object
+ // to update the menu to display a combined object while George runs
+ // voice-over. Note that 'object_held' must be set to the graphic of
+ // the combined object
+
+ // params: none
+
+ // can reset this now
+ _scriptVars[COMBINE_BASE] = 0;
+
+ // so that the icon in 'object_held' is coloured while the rest are
+ // grey
+ _vm->_examiningMenuIcon = true;
+ _vm->buildMenu();
+ _vm->_examiningMenuIcon = false;
+
+ return IR_CONT;
+}
+
+int32 Logic::fnChangeShadows(int32 *params) {
+ // params: none
+
+ // if last screen was using a shading mask (see below)
+ if (_vm->_thisScreen.mask_flag) {
+ uint32 rv = _vm->_graphics->closeLightMask();
+ if (rv)
+ error("Driver Error %.8x", rv);
+ _vm->_thisScreen.mask_flag = false;
+ }
+
+ return IR_CONT;
+}
+
} // End of namespace Sword2
diff --git a/sword2/icons.cpp b/sword2/icons.cpp
index 5157842b13..7146743d8a 100644
--- a/sword2/icons.cpp
+++ b/sword2/icons.cpp
@@ -29,33 +29,6 @@
namespace Sword2 {
-int32 Logic::fnAddMenuObject(int32 *params) {
- // params: 0 pointer to a MenuObject structure to copy down
-
- _vm->addMenuObject((MenuObject *) _vm->_memory->decodePtr(params[0]));
- return IR_CONT;
-}
-
-int32 Logic::fnRefreshInventory(int32 *params) {
- // called from 'menu_look_or_combine' script in 'menu_master' object
- // to update the menu to display a combined object while George runs
- // voice-over. Note that 'object_held' must be set to the graphic of
- // the combined object
-
- // params: none
-
- // can reset this now
- _scriptVars[COMBINE_BASE] = 0;
-
- // so that the icon in 'object_held' is coloured while the rest are
- // grey
- _vm->_examiningMenuIcon = true;
- _vm->buildMenu();
- _vm->_examiningMenuIcon = false;
-
- return IR_CONT;
-}
-
void Sword2Engine::addMenuObject(MenuObject *obj) {
assert(_totalTemp < TOTAL_engine_pockets);
memcpy(&_tempList[_totalTemp], obj, sizeof(MenuObject));
diff --git a/sword2/layers.cpp b/sword2/layers.cpp
index 67c1238c12..86debbd8c3 100644
--- a/sword2/layers.cpp
+++ b/sword2/layers.cpp
@@ -31,19 +31,11 @@
#include "sword2/interpreter.h"
#include "sword2/logic.h"
#include "sword2/resman.h"
+#include "sword2/sound.h"
#include "sword2/driver/d_draw.h"
namespace Sword2 {
-int32 Logic::fnInitBackground(int32 *params) {
- // this screen defines the size of the back buffer
-
- // params: 0 res id of normal background layer - cannot be 0
- // 1 1 yes 0 no for a new palette
-
- return _vm->initBackground(params[0], params[1]);
-}
-
/**
* This function is called when entering a new room.
* @param res resource id of the normal background layer
@@ -60,7 +52,7 @@ int32 Sword2Engine::initBackground(int32 res, int32 new_palette) {
_resman->passTime();
_resman->expireOldResources();
- clearFxQueue();
+ _sound->clearFxQueue();
_graphics->waitForFade();
debug(1, "CHANGED TO LOCATION \"%s\"", fetchObjectName(res, buf));
diff --git a/sword2/logic.cpp b/sword2/logic.cpp
index a2c59934fb..db739c348f 100644
--- a/sword2/logic.cpp
+++ b/sword2/logic.cpp
@@ -26,6 +26,7 @@
#include "sword2/logic.h"
#include "sword2/resman.h"
#include "sword2/router.h"
+#include "sword2/sound.h"
#define LEVEL (_curObjectHub->logic_level)
@@ -36,15 +37,14 @@ namespace Sword2 {
Logic::Logic(Sword2Engine *vm) :
_vm(vm), _kills(0), _smackerLeadOut(0), _sequenceTextLines(0),
_speechTime(0), _animId(0), _speechAnimType(0), _leftClickDelay(0),
- _rightClickDelay(0), _defaultResponseId(0), _totalStartups(0),
- _totalScreenManagers(0), _officialTextNumber(0), _speechTextBlocNo(0),
+ _rightClickDelay(0), _defaultResponseId(0), _officialTextNumber(0),
+ _speechTextBlocNo(0),
_choosing(false) {
_scriptVars = NULL;
memset(_subjectList, 0, sizeof(_subjectList));
memset(_eventList, 0, sizeof(_eventList));
memset(_syncList, 0, sizeof(_syncList));
_router = new Router(_vm);
- initStartMenu();
}
Logic::~Logic() {
@@ -212,7 +212,7 @@ void Logic::expressChangeSession(uint32 sesh_id) {
// Various clean-ups
_router->clearWalkGridList();
- _vm->clearFxQueue();
+ _vm->_sound->clearFxQueue();
_router->freeAllRouteMem();
}
@@ -225,34 +225,6 @@ uint32 Logic::getRunList(void) {
}
/**
- * This function is by start scripts.
- */
-
-int32 Logic::fnSetSession(int32 *params) {
- // params: 0 id of new run list
-
- expressChangeSession(params[0]);
- return IR_CONT;
-}
-
-/**
- * Causes no more objects in this logic loop to be processed. The logic engine
- * will restart at the beginning of the new list. The current screen will not
- * be drawn!
- */
-
-int32 Logic::fnEndSession(int32 *params) {
- // params: 0 id of new run-list
-
- // terminate current and change to next run-list
- expressChangeSession(params[0]);
-
- // stop the script - logic engine will now go around and the new
- // screen will begin
- return IR_STOP;
-}
-
-/**
* Move the current object up a level. Called by fnGosub command. Remember:
* only the logic object has access to _curObjectHub.
*/
@@ -307,59 +279,6 @@ void Logic::examineRunList(void) {
Debug_Printf("No run list set\n");
}
-/**
- * Reset the object and restart script 1 on level 0
- */
-
-int32 Logic::fnTotalRestart(int32 *params) {
- // mega runs this to restart its base logic again - like being cached
- // in again
-
- // params: none
-
- LEVEL = 0;
- _curObjectHub->script_pc[0] = 1;
- return IR_TERMINATE;
-}
-
-/**
- * Mark this object for killing - to be killed when player leaves this screen.
- * Object reloads and script restarts upon re-entry to screen, which causes
- * this object's startup logic to be re-run every time we enter the screen.
- * "Which is nice."
- *
- * @note Call ONCE from object's logic script, i.e. in startup code, so not
- * re-called every time script frops off and restarts!
- */
-
-int32 Logic::fnAddToKillList(int32 *params) {
- // params: none
-
- // DON'T EVER KILL GEORGE!
- if (_scriptVars[ID] == CUR_PLAYER_ID)
- return IR_CONT;
-
- // Scan the list to see if it's already included
-
- for (uint32 i = 0; i < _kills; i++) {
- if (_objectKillList[i] == _scriptVars[ID])
- return IR_CONT;
- }
-
- assert(_kills < OBJECT_KILL_LIST_SIZE); // no room at the inn
-
- _objectKillList[_kills++] = _scriptVars[ID];
-
- // "another one bites the dust"
-
- // When we leave the screen, all these object resources are to be
- // cleaned out of memory and the kill list emptied by doing
- // '_kills = 0', ensuring that all resources are in fact still in
- // memory and, more importantly, closed before killing!
-
- return IR_CONT;
-}
-
void Logic::resetKillList(void) {
_kills = 0;
}
diff --git a/sword2/logic.h b/sword2/logic.h
index dcbdf547f0..f535ac4581 100644
--- a/sword2/logic.h
+++ b/sword2/logic.h
@@ -24,7 +24,6 @@
#define _LOGIC
#include "sword2/speech.h"
-#include "sword2/startup.h"
namespace Sword2 {
@@ -68,7 +67,8 @@ private:
EventUnit _eventList[MAX_events];
- // Stores resource id of the wav to use as lead-out from smacker
+ // Resource id of the wav to use as lead-in/lead-out from smacker
+ uint32 _smackerLeadIn;
uint32 _smackerLeadOut;
int32 animate(int32 *params, bool reverse);
@@ -133,26 +133,6 @@ private:
void formText(int32 *params);
bool wantSpeechForLine(uint32 wavId);
- uint32 _totalStartups;
- uint32 _totalScreenManagers;
- uint32 _startRes;
-
- struct StartUp {
- char description[MAX_description];
-
- // id of screen manager object
- uint32 start_res_id;
-
- // tell the manager which startup you want (if there are more
- // than 1) (i.e more than 1 entrance to a screen and/or
- // separate game boots)
- uint32 key;
- };
-
- StartUp _startList[MAX_starts];
-
- bool initStartMenu(void);
-
int16 _standbyX; // see fnSetStandbyCoords()
int16 _standbyY;
int16 _standbyDir;
@@ -180,9 +160,6 @@ public:
// could alternately use logic->looping of course
bool _choosing;
- void conPrintStartMenu(void);
- void conStart(int start);
-
int runScript(char *scriptData, char *objectData, uint32 *offset);
void sendEvent(uint32 id, uint32 interact_id);
diff --git a/sword2/mouse.cpp b/sword2/mouse.cpp
index bc08005389..c0062ba635 100644
--- a/sword2/mouse.cpp
+++ b/sword2/mouse.cpp
@@ -30,7 +30,6 @@
#include "sword2/resman.h"
#include "sword2/sound.h"
#include "sword2/driver/d_draw.h"
-#include "sword2/driver/d_sound.h"
namespace Sword2 {
@@ -199,14 +198,14 @@ void Sword2Engine::systemMenuMouse(void) {
// playing when returning from control panels because control panel
// music will overwrite it!
- safe_looping_music_id = _loopingMusicId;
+ safe_looping_music_id = _sound->getLoopingMusicId();
pars[0] = 221;
pars[1] = FX_LOOP;
_logic->fnPlayMusic(pars);
- // restore proper looping_music_id
- _loopingMusicId = safe_looping_music_id;
+ // HACK: Restore proper looping_music_id
+ _sound->setLoopingMusicId(safe_looping_music_id);
_graphics->processMenu();
@@ -262,8 +261,8 @@ void Sword2Engine::systemMenuMouse(void) {
// then restart it! NB. If a game has been restored the music will be
// restarted twice, but this shouldn't cause any harm.
- if (_loopingMusicId) {
- pars[0] = _loopingMusicId;
+ if (_sound->getLoopingMusicId()) {
+ pars[0] = _sound->getLoopingMusicId();
pars[1] = FX_LOOP;
_logic->fnPlayMusic(pars);
} else
@@ -1081,250 +1080,4 @@ void Sword2Engine::monitorPlayerActivity(void) {
}
}
-int32 Logic::fnNoHuman(int32 *params) {
- // params: none
-
- _vm->noHuman();
- _vm->clearPointerText();
-
- // must be normal mouse situation or a largely neutral situation -
- // special menus use noHuman
-
- // dont hide menu in conversations
- if (_scriptVars[TALK_FLAG] == 0)
- _vm->_graphics->hideMenu(RDMENU_BOTTOM);
-
- if (_vm->_mouseMode == MOUSE_system_menu) {
- // close menu
- _vm->_mouseMode = MOUSE_normal;
- _vm->_graphics->hideMenu(RDMENU_TOP);
- }
-
- return IR_CONT;
-}
-
-int32 Logic::fnAddHuman(int32 *params) {
- // params: none
-
- // for logic scripts
- _scriptVars[MOUSE_AVAILABLE] = 1;
-
- // off
- if (_vm->_mouseStatus) {
- _vm->_mouseStatus = false; // on
- _vm->_mouseTouching = 1; // forces engine to choose a cursor
- }
-
- // clear this to reset no-second-click system
- _scriptVars[CLICKED_ID] = 0;
-
- // this is now done outside the OBJECT_HELD check in case it's set to
- // zero before now!
-
- // unlock the mouse from possible large object lock situtations - see
- // syphon in rm 3
-
- _vm->_mouseModeLocked = false;
-
- if (_scriptVars[OBJECT_HELD]) {
- // was dragging something around
- // need to clear this again
- _scriptVars[OBJECT_HELD] = 0;
-
- // and these may also need clearing, just in case
- _vm->_examiningMenuIcon = false;
- Logic::_scriptVars[COMBINE_BASE] = 0;
-
- _vm->setLuggage(0);
- }
-
- // if mouse is over menu area
- if (_vm->_mouseY > 399) {
- if (_vm->_mouseMode != MOUSE_holding) {
- // VITAL - reset things & rebuild the menu
- _vm->_mouseMode = MOUSE_normal;
- _vm->setMouse(NORMAL_MOUSE_ID);
- } else
- _vm->setMouse(NORMAL_MOUSE_ID);
- }
-
- // enabled/disabled from console; status printed with on-screen debug
- // info
-
- if (_vm->_debugger->_testingSnR) {
- uint8 black[4] = { 0, 0, 0, 0 };
- uint8 white[4] = { 255, 255, 255, 0 };
-
- // testing logic scripts by simulating an instant Save &
- // Restore
-
- _vm->_graphics->setPalette(0, 1, white, RDPAL_INSTANT);
-
- // stops all fx & clears the queue - eg. when leaving a
- // location
-
- _vm->clearFxQueue();
-
- // Trash all object resources so they load in fresh & restart
- // their logic scripts
-
- _vm->_resman->killAllObjects(false);
-
- _vm->_graphics->setPalette(0, 1, black, RDPAL_INSTANT);
- }
-
- return IR_CONT;
-}
-
-int32 Logic::fnRegisterMouse(int32 *params) {
- // this call would be made from an objects service script 0
- // the object would be one with no graphic but with a mouse - i.e. a
- // floor or one whose mouse area is manually defined rather than
- // intended to fit sprite shape
-
- // params: 0 pointer to ObjectMouse or 0 for no write to mouse
- // list
-
- _vm->registerMouse((ObjectMouse *) _vm->_memory->decodePtr(params[0]));
- return IR_CONT;
-}
-
-// use this in the object's service script prior to registering the mouse area
-// ie. before fnRegisterMouse or fnRegisterFrame
-// - best if kept at very top of service script
-
-int32 Logic::fnRegisterPointerText(int32 *params) {
- // params: 0 local id of text line to use as pointer text
-
- assert(_vm->_curMouse < TOTAL_mouse_list);
-
- // current object id - used for checking pointer_text when mouse area
- // registered (in fnRegisterMouse and fnRegisterFrame)
-
- _vm->_mouseList[_vm->_curMouse].id = _scriptVars[ID];
- _vm->_mouseList[_vm->_curMouse].pointer_text = params[0];
- return IR_CONT;
-}
-
-int32 Logic::fnInitFloorMouse(int32 *params) {
- // params: 0 pointer to object's mouse structure
-
- ObjectMouse *ob_mouse = (ObjectMouse *) _vm->_memory->decodePtr(params[0]);
-
- // floor is always lowest priority
-
- ob_mouse->x1 = 0;
- ob_mouse->y1 = 0;
- ob_mouse->x2 = _vm->_thisScreen.screen_wide - 1;
- ob_mouse->y2 = _vm->_thisScreen.screen_deep - 1;
- ob_mouse->priority = 9;
- ob_mouse->pointer = NORMAL_MOUSE_ID;
- return IR_CONT;
-}
-
-#define SCROLL_MOUSE_WIDTH 20
-
-int32 Logic::fnSetScrollLeftMouse(int32 *params) {
- // params: 0 pointer to object's mouse structure
-
- ObjectMouse *ob_mouse = (ObjectMouse *) _vm->_memory->decodePtr(params[0]);
-
- // Highest priority
-
- ob_mouse->x1 = 0;
- ob_mouse->y1 = 0;
- ob_mouse->x2 = _vm->_thisScreen.scroll_offset_x + SCROLL_MOUSE_WIDTH;
- ob_mouse->y2 = _vm->_thisScreen.screen_deep - 1;
- ob_mouse->priority = 0;
-
- if (_vm->_thisScreen.scroll_offset_x > 0) {
- // not fully scrolled to the left
- ob_mouse->pointer = SCROLL_LEFT_MOUSE_ID;
- } else {
- // so the mouse area doesn't get registered
- ob_mouse->pointer = 0;
- }
-
- return IR_CONT;
-}
-
-int32 Logic::fnSetScrollRightMouse(int32 *params) {
- // params: 0 pointer to object's mouse structure
-
- ObjectMouse *ob_mouse = (ObjectMouse *) _vm->_memory->decodePtr(params[0]);
-
- // Highest priority
-
- ob_mouse->x1 = _vm->_thisScreen.scroll_offset_x + _vm->_graphics->_screenWide - SCROLL_MOUSE_WIDTH;
- ob_mouse->y1 = 0;
- ob_mouse->x2 = _vm->_thisScreen.screen_wide - 1;
- ob_mouse->y2 = _vm->_thisScreen.screen_deep - 1;
- ob_mouse->priority = 0;
-
- if (_vm->_thisScreen.scroll_offset_x < _vm->_thisScreen.max_scroll_offset_x) {
- // not fully scrolled to the right
- ob_mouse->pointer = SCROLL_RIGHT_MOUSE_ID;
- } else {
- // so the mouse area doesn't get registered
- ob_mouse->pointer = 0;
- }
-
- return IR_CONT;
-}
-
-int32 Logic::fnSetObjectHeld(int32 *params) {
- // params: 0 luggage icon to set
-
- _vm->setLuggage(params[0]);
-
- _scriptVars[OBJECT_HELD] = params[0];
- _vm->_currentLuggageResource = params[0];
-
- // mode locked - no menu available
- _vm->_mouseModeLocked = true;
- return IR_CONT;
-}
-
-// called from speech scripts to remove the chooser bar when it's not
-// appropriate to keep it displayed
-
-int32 Logic::fnRemoveChooser(int32 *params) {
- // params: none
-
- _vm->_graphics->hideMenu(RDMENU_BOTTOM);
- return IR_CONT;
-}
-
-int32 Logic::fnCheckPlayerActivity(int32 *params) {
- // Used to decide when to trigger music cues described as "no player
- // activity for a while"
-
- // params: 0 threshold delay in seconds, ie. what we want to
- // check the actual delay against
-
- uint32 threshold = params[0] * 12; // in game cycles
-
- // if the actual delay is at or above the given threshold
- if (_vm->_playerActivityDelay >= threshold) {
- // reset activity delay counter, now that we've got a
- // positive check
-
- _vm->_playerActivityDelay = 0;
- _scriptVars[RESULT] = 1;
- } else
- _scriptVars[RESULT] = 0;
-
- return IR_CONT;
-}
-
-int32 Logic::fnResetPlayerActivityDelay(int32 *params) {
- // Use if you want to deliberately reset the "no player activity"
- // counter for any reason
-
- // params: none
-
- _vm->_playerActivityDelay = 0;
- return IR_CONT;
-}
-
} // End of namespace Sword2
diff --git a/sword2/resman.cpp b/sword2/resman.cpp
index 0ac49fdb9a..ad1292ea38 100644
--- a/sword2/resman.cpp
+++ b/sword2/resman.cpp
@@ -27,6 +27,7 @@
#include "sword2/memory.h"
#include "sword2/resman.h"
#include "sword2/router.h"
+#include "sword2/sound.h"
#include "sword2/driver/d_draw.h"
#define Debug_Printf _vm->_debugger->DebugPrintf
@@ -786,7 +787,7 @@ void ResourceManager::removeAll(void) {
// will still believe that the sound resources are in memory, and that
// it's ok to close them.
- _vm->clearFxQueue();
+ _vm->_sound->clearFxQueue();
for (uint i = 0; i < _totalResFiles; i++)
remove(i);
@@ -803,7 +804,7 @@ void ResourceManager::killAll(bool wantInfo) {
// will still believe that the sound resources are in memory, and that
// it's ok to close them.
- _vm->clearFxQueue();
+ _vm->_sound->clearFxQueue();
for (uint i = 0; i < _totalResFiles; i++) {
// Don't nuke the global variables or the player object!
diff --git a/sword2/save_rest.cpp b/sword2/save_rest.cpp
index 6f2e1ed6fb..d3bd0035ff 100644
--- a/sword2/save_rest.cpp
+++ b/sword2/save_rest.cpp
@@ -122,7 +122,7 @@ void Sword2Engine::fillSaveBuffer(byte *buffer, uint32 size, byte *desc) {
_saveGameHeader.runListId = _logic->getRunList();
_saveGameHeader.feet_x = _thisScreen.feet_x;
_saveGameHeader.feet_y = _thisScreen.feet_y;
- _saveGameHeader.music_id = _loopingMusicId;
+ _saveGameHeader.music_id = _sound->getLoopingMusicId();
memcpy(&_saveGameHeader.player_hub, _resman->openResource(CUR_PLAYER_ID) + sizeof(StandardHeader), sizeof(ObjectHub));
@@ -334,14 +334,12 @@ uint32 Sword2Engine::restoreFromBuffer(byte *buffer, uint32 size) {
// Any music required will be started after we've returned from
// restoreControl() - see systemMenuMouse() in mouse.cpp!
- _loopingMusicId = _saveGameHeader.music_id;
-
// Restart any looping music. Originally this was - and still is - done
// in systemMenuMouse(), but with ScummVM we have other ways of
// restoring savegames so it's easier to put it here as well.
- if (_loopingMusicId) {
- pars[0] = _loopingMusicId;
+ if (_saveGameHeader.music_id) {
+ pars[0] = _saveGameHeader.music_id;
pars[1] = FX_LOOP;
_logic->fnPlayMusic(pars);
} else
@@ -470,70 +468,4 @@ uint32 Sword2Engine::calcChecksum(byte *buffer, uint32 size) {
return total;
}
-/**
- * Copies the 4 essential player structures into the savegame header - run
- * script 7 of player object to request this.
- *
- * Remember, we cannot simply read a compact any longer but instead must
- * request it from the object itself.
- */
-
-int32 Logic::fnPassPlayerSaveData(int32 *params) {
- // params: 0 pointer to object's logic structure
- // 1 pointer to object's graphic structure
- // 2 pointer to object's mega structure
-
- // Copy from player object to savegame header
-
- memcpy(&_vm->_saveGameHeader.logic, _vm->_memory->decodePtr(params[0]), sizeof(ObjectLogic));
- memcpy(&_vm->_saveGameHeader.graphic, _vm->_memory->decodePtr(params[1]), sizeof(ObjectGraphic));
- memcpy(&_vm->_saveGameHeader.mega, _vm->_memory->decodePtr(params[2]), sizeof(ObjectMega));
-
- return IR_CONT;
-}
-
-/**
- * Reverse of fnPassPlayerSaveData() - run script 8 of player object.
- */
-
-int32 Logic::fnGetPlayerSaveData(int32 *params) {
- // params: 0 pointer to object's logic structure
- // 1 pointer to object's graphic structure
- // 2 pointer to object's mega structure
-
- byte *logic_ptr = _vm->_memory->decodePtr(params[0]);
- byte *graphic_ptr = _vm->_memory->decodePtr(params[1]);
- byte *mega_ptr = _vm->_memory->decodePtr(params[2]);
-
- // Copy from savegame header to player object
-
- memcpy(logic_ptr, &_vm->_saveGameHeader.logic, sizeof(ObjectLogic));
- memcpy(graphic_ptr, &_vm->_saveGameHeader.graphic, sizeof(ObjectGraphic));
- memcpy(mega_ptr, &_vm->_saveGameHeader.mega, sizeof(ObjectMega));
-
- // Any walk-data must be cleared - the player will be set to stand if
- // he was walking when saved.
-
- ObjectMega *ob_mega = (ObjectMega *) mega_ptr;
-
- if (ob_mega->currently_walking) {
- ob_mega->currently_walking = 0;
-
- int32 pars[3];
-
- pars[0] = params[1]; // ob_graphic;
- pars[1] = params[2]; // ob_mega
- pars[2] = ob_mega->current_dir;
-
- fnStand(pars);
-
- // Reset looping flag (which would have been 1 during fnWalk)
- ObjectLogic *ob_logic = (ObjectLogic *) logic_ptr;
-
- ob_logic->looping = 0;
- }
-
- return IR_CONT;
-}
-
} // End of namespace Sword2
diff --git a/sword2/scroll.cpp b/sword2/scroll.cpp
index 890253ffe8..82fa0fa504 100644
--- a/sword2/scroll.cpp
+++ b/sword2/scroll.cpp
@@ -135,41 +135,4 @@ void Sword2Engine::setScrolling(void) {
}
}
-/**
- * Set the special scroll offset variables
- *
- * Call when starting screens and to change the camera within screens
- *
- * call AFTER fnInitBackground() to override the defaults
- */
-
-int32 Logic::fnSetScrollCoordinate(int32 *params) {
- // params: 0 feet_x value
- // 1 feet_y value
-
- // Called feet_x and feet_y to retain intellectual compatibility with
- // Sword1!
- //
- // feet_x & feet_y refer to the physical screen coords where the
- // system will try to maintain George's feet
-
- _vm->_thisScreen.feet_x = params[0];
- _vm->_thisScreen.feet_y = params[1];
- return IR_CONT;
-}
-
-int32 Logic::fnSetScrollSpeedNormal(int32 *params) {
- // params: none
-
- _vm->_scrollFraction = 16;
- return IR_CONT;
-}
-
-int32 Logic::fnSetScrollSpeedSlow(int32 *params) {
- // params: none
-
- _vm->_scrollFraction = 32;
- return IR_CONT;
-}
-
} // End of namespace Sword2
diff --git a/sword2/sound.cpp b/sword2/sound.cpp
index 343ec997d5..b50735ccc2 100644
--- a/sword2/sound.cpp
+++ b/sword2/sound.cpp
@@ -30,87 +30,104 @@
#include "common/stdafx.h"
#include "common/file.h"
+#include "common/system.h"
+
#include "sword2/sword2.h"
#include "sword2/defs.h"
-#include "sword2/interpreter.h"
#include "sword2/logic.h"
#include "sword2/resman.h"
#include "sword2/sound.h"
-#include "sword2/driver/d_sound.h"
+
+#include "sound/wave.h"
namespace Sword2 {
-struct FxQueueEntry {
- uint32 resource; // resource id of sample
- byte *data; // pointer to WAV data
- uint16 delay; // cycles to wait before playing (or 'random chance' if FX_RANDOM)
- uint8 volume; // 0..16
- int8 pan; // -16..16
- uint8 type; // FX_SPOT, FX_RANDOM or FX_LOOP
-};
+Sound::Sound(Sword2Engine *vm) {
+ int i;
-// FIXME: Should be in one of the classes, I guess...
+ _vm = vm;
+ _mutex = _vm->_system->createMutex();
-static FxQueueEntry fxQueue[FXQ_LENGTH];
+ for (i = 0; i < FXQ_LENGTH; i++)
+ _fxQueue[i].resource = 0;
-/**
- * Initialise the FX queue by clearing all the entries. This is only used at
- * the start of the game. Later when we need to clear the queue we must also
- * stop the sound and close the resource.
- */
+ for (i = 0; i < MAXMUS; i++)
+ _music[i] = NULL;
-void Sword2Engine::initFxQueue(void) {
- for (int i = 0; i < FXQ_LENGTH; i++)
- fxQueue[i].resource = 0;
+ _speechPaused = false;
+ _musicPaused = false;
+ _fxPaused = false;
+
+ _speechMuted = false;
+ _musicMuted = false;
+ _fxMuted = false;
+
+ _mixBuffer = NULL;
+ _mixBufferLen = 0;
+
+ _vm->_mixer->setupPremix(this, SoundMixer::kMusicAudioDataType);
+}
+
+Sound::~Sound() {
+ _vm->_mixer->setupPremix(0);
+
+ clearFxQueue();
+ stopMusic();
+ stopSpeech();
+
+ for (int i = 0; i < MAXMUS; i++)
+ delete _music[i];
+
+ free(_mixBuffer);
+
+ if (_mutex)
+ _vm->_system->deleteMutex(_mutex);
}
/**
* Stop all sounds, close their resources and clear the FX queue.
*/
-void Sword2Engine::clearFxQueue(void) {
+void Sound::clearFxQueue() {
for (int i = 0; i < FXQ_LENGTH; i++) {
- if (fxQueue[i].resource) {
- _sound->stopFx(i + 1);
- _resman->closeResource(fxQueue[i].resource);
- fxQueue[i].resource = 0;
+ if (_fxQueue[i].resource) {
+ stopFx(i);
}
}
}
/**
- * Process the FX queue once every game cycle
+ * Process the FX queue. This function is called once every game cycle.
*/
-void Sword2Engine::processFxQueue(void) {
+void Sound::processFxQueue() {
for (int i = 0; i < FXQ_LENGTH; i++) {
- if (!fxQueue[i].resource)
+ if (!_fxQueue[i].resource)
continue;
- switch (fxQueue[i].type) {
+ switch (_fxQueue[i].type) {
case FX_RANDOM:
// 1 in 'delay' chance of this fx occurring
- if (_rnd.getRandomNumber(fxQueue[i].delay) == 0)
- triggerFx(i);
+ if (_vm->_rnd.getRandomNumber(_fxQueue[i].delay) == 0)
+ playFx(&_fxQueue[i]);
break;
case FX_SPOT:
- if (fxQueue[i].delay)
- fxQueue[i].delay--;
+ if (_fxQueue[i].delay)
+ _fxQueue[i].delay--;
else {
- triggerFx(i);
- fxQueue[i].type = FX_SPOT2;
+ playFx(&_fxQueue[i]);
+ _fxQueue[i].type = FX_SPOT2;
}
break;
case FX_LOOP:
- triggerFx(i);
- fxQueue[i].type = FX_LOOPING;
+ playFx(&_fxQueue[i]);
+ _fxQueue[i].type = FX_LOOPING;
break;
case FX_SPOT2:
// Once the FX has finished remove it from the queue.
- if (!_sound->isFxPlaying(i + 1)) {
- _sound->stopFx(i + 1);
- _resman->closeResource(fxQueue[i].resource);
- fxQueue[i].resource = 0;
+ if (!_fxQueue[i].handle.isActive()) {
+ _vm->_resman->closeResource(_fxQueue[i].resource);
+ _fxQueue[i].resource = 0;
}
break;
case FX_LOOPING:
@@ -121,241 +138,139 @@ void Sword2Engine::processFxQueue(void) {
}
}
-void Sword2Engine::triggerFx(uint8 i) {
- int type;
-
- if (fxQueue[i].type == FX_LOOP)
- type = RDSE_FXLOOP;
- else
- type = RDSE_FXSPOT;
-
- uint32 len = _resman->fetchLen(fxQueue[i].resource) - sizeof(StandardHeader);
- uint32 rv = _sound->playFx(i + 1, len, fxQueue[i].data, fxQueue[i].volume, fxQueue[i].pan, type);
-
- if (rv)
- debug(5, "SFX ERROR: playFx() returned %.8x", rv);
-}
-
-void Sword2Engine::killMusic(void) {
- _loopingMusicId = 0; // clear the 'looping' flag
- _sound->stopMusic();
-}
-
-void Sword2Engine::pauseAllSound(void) {
- _sound->pauseMusic();
- _sound->pauseSpeech();
- _sound->pauseFx();
-}
-
-void Sword2Engine::unpauseAllSound(void) {
- _sound->unpauseMusic();
- _sound->unpauseSpeech();
- _sound->unpauseFx();
-}
-
-int32 Logic::fnPlayFx(int32 *params) {
- // params: 0 sample resource id
- // 1 type (FX_SPOT, FX_RANDOM, FX_LOOP)
- // 2 delay (0..65535)
- // 3 volume (0..16)
- // 4 pan (-16..16)
-
- // example script:
- // fnPlayFx (FXWATER, FX_LOOP, 0, 10, 15);
- // // fx_water is just a local script flag
- // fx_water = result;
- // .
- // .
- // .
- // fnStopFx (fx_water);
+/**
+ * Queue a sound effect for playing later.
+ * @param res the sound resource number
+ * @param type the type of sound effect
+ * @param delay when to play the sound effect
+ * @param volume the sound effect volume (0 through 16)
+ * @param pan the sound effect panning (-16 through 16)
+ */
+void Sound::queueFx(int32 res, int32 type, int32 delay, int32 volume, int32 pan) {
if (_vm->_wantSfxDebug) {
- char type[10];
+ const char *typeStr;
- switch (params[1]) {
+ switch (type) {
case FX_SPOT:
- strcpy(type, "SPOT");
+ typeStr = "SPOT";
break;
case FX_LOOP:
- strcpy(type, "LOOPED");
+ typeStr = "LOOPED";
break;
case FX_RANDOM:
- strcpy(type, "RANDOM");
+ typeStr = "RANDOM";
break;
default:
- strcpy(type, "INVALID");
+ typeStr = "INVALID";
break;
}
byte buf[NAME_LEN];
- debug(0, "SFX (sample=\"%s\", vol=%d, pan=%d, delay=%d, type=%s)", _vm->fetchObjectName(params[0], buf), params[3], params[4], params[2], type);
- }
-
- int i;
-
- // Find a free slot in the FX queue
-
- for (i = 0; i < FXQ_LENGTH; i++) {
- if (!fxQueue[i].resource)
- break;
- }
-
- if (i == FXQ_LENGTH) {
- warning("No free slot in FX queue");
- return IR_CONT;
+ debug(0, "SFX (sample=\"%s\", vol=%d, pan=%d, delay=%d, type=%s)", _vm->fetchObjectName(res, buf), volume, pan, delay, typeStr);
}
- fxQueue[i].resource = params[0];
- fxQueue[i].type = params[1];
- fxQueue[i].delay = params[2];
-
- if (fxQueue[i].type == FX_RANDOM) {
- // For spot effects and loops the dela is the number of frames
- // to wait. For random effects, however, it's the average
- // number of seconds between playing the sound, so we have to
- // multiply by the frame rate.
- fxQueue[i].delay *= 12;
- }
-
- fxQueue[i].volume = params[3];
- fxQueue[i].pan = params[4];
+ for (int i = 0; i < FXQ_LENGTH; i++) {
+ if (!_fxQueue[i].resource) {
+ byte *data = _vm->_resman->openResource(res);
+ StandardHeader *header = (StandardHeader *) data;
- byte *data = _vm->_resman->openResource(params[0]);
- StandardHeader *header = (StandardHeader *) data;
+ assert(header->fileType == WAV_FILE);
- assert(header->fileType == WAV_FILE);
+ uint32 len = _vm->_resman->fetchLen(res) - sizeof(StandardHeader);
- fxQueue[i].data = data + sizeof(StandardHeader);
+ if (type == FX_RANDOM) {
+ // For spot effects and loops the delay is the
+ // number of frames to wait. For random
+ // effects, however, it's the average number of
+ // seconds between playing the sound, so we
+ // have to multiply by the frame rate.
+ delay *= 12;
+ }
- // Keep track of the index in the loop so that fnStopFx() can be used
- // later to kill this sound. Mainly for FX_LOOP and FX_RANDOM.
+ volume = (volume * SoundMixer::kMaxChannelVolume) / 16;
+ pan = (pan * 127) / 16;
- _scriptVars[RESULT] = i;
- return IR_CONT;
-}
+ _fxQueue[i].resource = res;
+ _fxQueue[i].data = data + sizeof(StandardHeader);
+ _fxQueue[i].len = len;
+ _fxQueue[i].delay = delay;
+ _fxQueue[i].volume = volume;
+ _fxQueue[i].pan = pan;
+ _fxQueue[i].type = type;
-int32 Logic::fnSoundFetch(int32 *params) {
- // params: 0 id of sound to fetch [guess]
- return IR_CONT;
-}
+ // Keep track of the index in the loop so that
+ // fnStopFx() can be used later to kill this sound.
+ // Mainly for FX_LOOP and FX_RANDOM.
-/**
- * Alter the volume and pan of a currently playing FX
- */
-
-int32 Logic::fnSetFxVolAndPan(int32 *params) {
- // params: 0 id of fx (ie. the id returned in 'result' from
- // fnPlayFx
- // 1 new volume (0..16)
- // 2 new pan (-16..16)
-
- debug(5, "fnSetFxVolAndPan(%d, %d, %d)", params[0], params[1], params[2]);
+ Logic::_scriptVars[RESULT] = i;
+ return;
+ }
+ }
- _vm->_sound->setFxIdVolumePan(params[0] + 1, params[1], params[2]);
- return IR_CONT;
+ warning("No free slot in FX queue");
}
-/**
- * Alter the volume of a currently playing FX
- */
-
-int32 Logic::fnSetFxVol(int32 *params) {
- // params: 0 id of fx (ie. the id returned in 'result' from
- // fnPlayFx
- // 1 new volume (0..16)
-
- _vm->_sound->setFxIdVolume(params[0] + 1, params[1]);
- return IR_CONT;
+int32 Sound::playFx(FxQueueEntry *fx) {
+ return playFx(&fx->handle, fx->data, fx->len, fx->volume, fx->pan, (fx->type == FX_LOOP), SoundMixer::kSFXAudioDataType);
}
-int32 Logic::fnStopFx(int32 *params) {
- // params: 0 position in queue
+int32 Sound::playFx(PlayingSoundHandle *handle, byte *data, uint32 len, uint8 vol, int8 pan, bool loop, SoundMixer::SoundType soundType) {
+ if (_fxMuted)
+ return RD_OK;
- int32 i = params[0];
- uint32 rv = _vm->_sound->stopFx(i + 1);
+ if (handle->isActive())
+ return RDERR_FXALREADYOPEN;
- if (rv)
- debug(5, "SFX ERROR: closeFx() returned %.8x", rv);
+ Common::MemoryReadStream stream(data, len);
+ int rate, size;
+ byte flags;
- // Remove from queue
- if (fxQueue[i].resource) {
- _vm->_resman->closeResource(fxQueue[i].resource);
- fxQueue[i].resource = 0;
+ if (!loadWAVFromStream(stream, size, rate, flags)) {
+ warning("playFX: Not a valid WAV file");
+ return RDERR_INVALIDWAV;
}
- return IR_CONT;
-}
-
-/**
- * Stops all FX and clears the entire FX queue.
- */
+ if (isReverseStereo())
+ flags |= SoundMixer::FLAG_REVERSE_STEREO;
-int32 Logic::fnStopAllFx(int32 *params) {
- // params: none
+ if (loop)
+ flags |= SoundMixer::FLAG_LOOP;
- _vm->clearFxQueue();
- return IR_CONT;
-}
-
-int32 Logic::fnPrepareMusic(int32 *params) {
- // params: 1 id of music to prepare [guess]
- return IR_CONT;
+ _vm->_mixer->playRaw(handle, data + stream.pos(), size, rate, flags, -1, vol, pan, 0, 0, soundType);
+ return RD_OK;
}
/**
- * Start a tune playing, to play once or to loop until stopped or next one
- * played.
+ * This function closes a sound effect which has been previously opened for
+ * playing. Sound effects must be closed when they are finished with, otherwise
+ * you will run out of sound effect buffers.
+ * @param i the index of the sound to close
*/
-int32 Logic::fnPlayMusic(int32 *params) {
- // params: 0 tune id
- // 1 loop flag (0 or 1)
-
- char filename[128];
- bool loopFlag;
- uint32 rv;
-
- if (params[1] == FX_LOOP) {
- loopFlag = true;
+int32 Sound::stopFx(int32 i) {
+ if (!_fxQueue[i].resource)
+ return RDERR_FXNOTOPEN;
- // keep a note of the id, for restarting after an
- // interruption to gameplay
- _vm->_loopingMusicId = params[0];
- } else {
- loopFlag = false;
+ if (_fxQueue[i].handle.isActive())
+ _vm->_mixer->stopHandle(_fxQueue[i].handle);
- // don't need to restart this tune after control panel or
- // restore
- _vm->_loopingMusicId = 0;
- }
-
- rv = _vm->_sound->streamCompMusic(params[0], loopFlag);
-
- if (rv)
- debug(5, "ERROR: streamCompMusic(%s, %d, %d) returned error 0x%.8x", filename, params[0], loopFlag, rv);
-
- return IR_CONT;
+ _vm->_resman->closeResource(_fxQueue[i].resource);
+ _fxQueue[i].resource = 0;
+ return RD_OK;
}
-int32 Logic::fnStopMusic(int32 *params) {
- // params: none
-
- _vm->_loopingMusicId = 0; // clear the 'looping' flag
- _vm->_sound->stopMusic();
- return IR_CONT;
+void Sound::pauseAllSound() {
+ pauseMusic();
+ pauseSpeech();
+ pauseFx();
}
-int32 Logic::fnCheckMusicPlaying(int32 *params) {
- // params: none
-
- // sets result to no. of seconds of current tune remaining
- // or 0 if no music playing
-
- // in seconds, rounded up to the nearest second
- _scriptVars[RESULT] = _vm->_sound->musicTimeRemaining();
-
- return IR_CONT;
+void Sound::unpauseAllSound() {
+ unpauseMusic();
+ unpauseSpeech();
+ unpauseFx();
}
} // End of namespace Sword2
diff --git a/sword2/sound.h b/sword2/sound.h
index 7649a725c8..d37ebeb450 100644
--- a/sword2/sound.h
+++ b/sword2/sound.h
@@ -31,12 +31,25 @@
#ifndef SOUND_H
#define SOUND_H
-// max number of fx in queue at once [DO NOT EXCEED 255]
+#include "sound/audiostream.h"
+#include "sound/mixer.h"
+
+// Max number of sound fx
+#define MAXMUS 2
+
+// Max number of fx in queue at once
#define FXQ_LENGTH 32
+#define BUFFER_SIZE 4096
+
namespace Sword2 {
-// fx types
+enum {
+ kCLUMode = 1,
+ kMP3Mode,
+ kVorbisMode,
+ kFlacMode
+};
enum {
// These three types correspond to types set by the scripts
@@ -49,6 +62,181 @@ enum {
FX_LOOPING = 4
};
+extern void sword2_sound_handler(void *refCon);
+
+class CLUInputStream : public AudioStream {
+private:
+ File *_file;
+ bool _firstTime;
+ uint32 _file_pos;
+ uint32 _end_pos;
+ int16 _outbuf[BUFFER_SIZE];
+ byte _inbuf[BUFFER_SIZE];
+ const int16 *_bufferEnd;
+ const int16 *_pos;
+
+ uint16 _prev;
+
+ void refill();
+
+ inline bool eosIntern() const {
+ return _pos >= _bufferEnd;
+ }
+
+public:
+ CLUInputStream(File *file, int size);
+ ~CLUInputStream();
+
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool endOfData() const { return eosIntern(); }
+ bool isStereo() const { return false; }
+ int getRate() const { return 22050; }
+};
+
+class MusicInputStream : public AudioStream {
+private:
+ int _cd;
+ uint32 _musicId;
+ AudioStream *_decoder;
+ int16 _buffer[BUFFER_SIZE];
+ const int16 *_bufferEnd;
+ const int16 *_pos;
+ bool _remove;
+ uint32 _numSamples;
+ uint32 _samplesLeft;
+ bool _looping;
+ int32 _fading;
+ int32 _fadeSamples;
+ bool _paused;
+
+ void refill();
+
+ inline bool eosIntern() const {
+ if (_looping)
+ return false;
+ return _remove || _pos >= _bufferEnd;
+ }
+
+public:
+ MusicInputStream(int cd, uint32 musicId, bool looping);
+ ~MusicInputStream();
+
+ int readBuffer(int16 *buffer, const int numSamples);
+
+ bool endOfData() const { return eosIntern(); }
+ bool isStereo() const { return _decoder->isStereo(); }
+ int getRate() const { return _decoder->getRate(); }
+
+ void fadeUp();
+ void fadeDown();
+
+ bool isReady() { return _decoder != NULL; }
+ int32 isFading() { return _fading; }
+
+ bool readyToRemove();
+ int32 getTimeRemaining();
+};
+
+class Sound : public AudioStream {
+private:
+ Sword2Engine *_vm;
+
+ Common::MutexRef _mutex;
+
+ struct FxQueueEntry {
+ PlayingSoundHandle handle; // sound handle
+ uint32 resource; // resource id of sample
+ byte *data; // pointer to WAV data
+ uint32 len; // WAV data length
+ uint16 delay; // cycles to wait before playing (or 'random chance' if FX_RANDOM)
+ uint8 volume; // sound volume
+ int8 pan; // sound panning
+ uint8 type; // FX_SPOT, FX_RANDOM, FX_LOOP
+ };
+
+ FxQueueEntry _fxQueue[FXQ_LENGTH];
+
+ void triggerFx(uint8 i);
+
+ bool _reverseStereo;
+
+ bool _speechMuted;
+ bool _fxMuted;
+ bool _musicMuted;
+
+ bool _speechPaused;
+ bool _fxPaused;
+ bool _musicPaused;
+
+ int32 _loopingMusicId;
+
+ PlayingSoundHandle _soundHandleSpeech;
+
+ MusicInputStream *_music[MAXMUS];
+ int16 *_mixBuffer;
+ int _mixBufferLen;
+
+public:
+ Sound(Sword2Engine *vm);
+ ~Sound();
+
+ // AudioStream API
+
+ int readBuffer(int16 *buffer, const int numSamples);
+ bool isStereo() const;
+ bool endOfData() const;
+ int getRate() const;
+
+ // End of AudioStream API
+
+ void clearFxQueue();
+ void processFxQueue();
+
+ void setReverseStereo(bool reverse) { _reverseStereo = reverse; }
+ bool isReverseStereo() const { return _reverseStereo; }
+
+ void muteSpeech(bool mute);
+ bool isSpeechMute() const { return _speechMuted; }
+
+ void muteFx(bool mute);
+ bool isFxMute() const { return _fxMuted; }
+
+ void muteMusic(bool mute) { _musicMuted = mute; }
+ bool isMusicMute() const { return _musicMuted; }
+
+ void setLoopingMusicId(int32 id) { _loopingMusicId = id; }
+ int32 getLoopingMusicId() const { return _loopingMusicId; }
+
+ void pauseSpeech();
+ void unpauseSpeech();
+
+ void pauseFx();
+ void unpauseFx();
+
+ void pauseMusic();
+ void unpauseMusic();
+
+ void pauseAllSound();
+ void unpauseAllSound();
+
+ void queueFx(int32 res, int32 type, int32 delay, int32 volume, int32 pan);
+ int32 playFx(FxQueueEntry *fx);
+ int32 playFx(PlayingSoundHandle *handle, byte *data, uint32 len, uint8 vol, int8 pan, bool loop, SoundMixer::SoundType soundType);
+ int32 stopFx(int32 i);
+ int32 setFxIdVolumePan(int32 id, int vol, int pan = -1);
+
+ int32 getSpeechStatus();
+ int32 amISpeaking();
+ int32 playCompSpeech(uint32 speechId, uint8 vol, int8 pan);
+ uint32 preFetchCompSpeech(uint32 speechId, uint16 **buf);
+ int32 stopSpeech();
+
+ int32 streamCompMusic(uint32 musicId, bool loop);
+ void stopMusic();
+ int32 musicTimeRemaining();
+};
+
} // End of namespace Sword2
#endif
diff --git a/sword2/speech.cpp b/sword2/speech.cpp
index de1b335d04..dcd3651a9e 100644
--- a/sword2/speech.cpp
+++ b/sword2/speech.cpp
@@ -31,667 +31,15 @@
#include "sword2/memory.h"
#include "sword2/resman.h"
#include "sword2/driver/d_draw.h"
-#include "sword2/driver/d_sound.h"
namespace Sword2 {
-int32 Logic::fnAddSubject(int32 *params) {
- // params: 0 id
- // 1 daves reference number
-
- if (_scriptVars[IN_SUBJECT] == 0) {
- // This is the start of the new subject list. Set the default
- // repsonse id to zero in case we're never passed one.
- _defaultResponseId = 0;
- }
-
- if (params[0] == -1) {
- // Id -1 is used for setting the default response, i.e. the
- // response when someone uses an object on a person and he
- // doesn't know anything about it. See fnChoose() below.
-
- _defaultResponseId = params[1];
- } else {
- debug(5, "fnAddSubject res %d, uid %d", params[0], params[1]);
- _subjectList[_scriptVars[IN_SUBJECT]].res = params[0];
- _subjectList[_scriptVars[IN_SUBJECT]].ref = params[1];
- _scriptVars[IN_SUBJECT]++;
- }
-
- return IR_CONT;
-}
-
-int32 Logic::fnChoose(int32 *params) {
- // params: none
-
- // This opcode is used to open the conversation menu. The human is
- // switched off so there will be no normal mouse engine.
-
- // The player's choice is piggy-backed on the standard opcode return
- // values, to be used with the CP_JUMP_ON_RETURNED opcode. As far as I
- // can tell, this is the only function that uses that feature.
-
- uint i;
-
- _scriptVars[AUTO_SELECTED] = 0;
-
- if (_scriptVars[OBJECT_HELD]) {
- // The player used an object on a person. In this case it
- // triggered a conversation menu. Act as if the user tried to
- // talk to the person about that object. If the person doesn't
- // know anything about it, use the default response.
-
- uint32 response = _defaultResponseId;
-
- for (i = 0; i < _scriptVars[IN_SUBJECT]; i++) {
- if (_subjectList[i].res == _scriptVars[OBJECT_HELD]) {
- response = _subjectList[i].ref;
- break;
- }
- }
-
- // The user won't be holding the object any more, and the
- // conversation menu will be closed.
-
- _scriptVars[OBJECT_HELD] = 0;
- _scriptVars[IN_SUBJECT] = 0;
- return IR_CONT | (response << 3);
- }
-
- if (_scriptVars[CHOOSER_COUNT_FLAG] == 0 && _scriptVars[IN_SUBJECT] == 1 && _subjectList[0].res == EXIT_ICON) {
- // This is the first time the chooser is coming up in this
- // conversation, there is only one subject and that's the
- // EXIT icon.
- //
- // In other words, the player doesn't have anything to talk
- // about. Skip it.
-
- // The conversation menu will be closed. We set AUTO_SELECTED
- // because the speech script depends on it.
-
- _scriptVars[AUTO_SELECTED] = 1;
- _scriptVars[IN_SUBJECT] = 0;
- return IR_CONT | (_subjectList[0].ref << 3);
- }
-
- byte *icon;
-
- if (!_choosing) {
- // This is a new conversation menu.
-
- if (!_scriptVars[IN_SUBJECT])
- error("fnChoose with no subjects");
-
- for (i = 0; i < _scriptVars[IN_SUBJECT]; i++) {
- icon = _vm->_resman->openResource(_subjectList[i].res) + sizeof(StandardHeader) + RDMENU_ICONWIDE * RDMENU_ICONDEEP;
- _vm->_graphics->setMenuIcon(RDMENU_BOTTOM, i, icon);
- _vm->_resman->closeResource(_subjectList[i].res);
- }
-
- for (; i < 15; i++)
- _vm->_graphics->setMenuIcon(RDMENU_BOTTOM, (uint8) i, NULL);
-
- _vm->_graphics->showMenu(RDMENU_BOTTOM);
- _vm->setMouse(NORMAL_MOUSE_ID);
- _choosing = true;
- return IR_REPEAT;
- }
-
- // The menu is there - we're just waiting for a click. We only care
- // about left clicks.
-
- MouseEvent *me = _vm->mouseEvent();
-
- if (!me || !(me->buttons & RD_LEFTBUTTONDOWN) || _vm->_mouseY < 400)
- return IR_REPEAT;
-
- // Check for click on a menu.
-
- int hit = _vm->menuClick(_scriptVars[IN_SUBJECT]);
- if (hit < 0)
- return IR_REPEAT;
-
- // Hilight the clicked icon by greying the others.
-
- for (i = 0; i < _scriptVars[IN_SUBJECT]; i++) {
- if ((int) i != hit) {
- icon = _vm->_resman->openResource(_subjectList[i].res) + sizeof(StandardHeader);
- _vm->_graphics->setMenuIcon(RDMENU_BOTTOM, i, icon);
- _vm->_resman->closeResource(_subjectList[i].res);
- }
- }
-
- // For non-speech scripts that manually call the chooser
- _scriptVars[RESULT] = _subjectList[hit].res;
-
- // The conversation menu will be closed
-
- _choosing = false;
- _scriptVars[IN_SUBJECT] = 0;
- _vm->setMouse(0);
-
- return IR_CONT | (_subjectList[hit].ref << 3);
-}
-
-/**
- * Start a conversation.
- *
- * Note that fnStartConversation() might accidentally be called every time the
- * script loops back for another chooser, but we only want to reset the chooser
- * count flag the first time this function is called, i.e. when the talk flag
- * is zero.
- */
-
-int32 Logic::fnStartConversation(int32 *params) {
- // params: none
-
- if (_scriptVars[TALK_FLAG] == 0) {
- // See fnChooser & speech scripts
- _scriptVars[CHOOSER_COUNT_FLAG] = 0;
- }
-
- fnNoHuman(params);
- return IR_CONT;
-}
-
-/**
- * End a conversation.
- */
-
-int32 Logic::fnEndConversation(int32 *params) {
- // params: none
-
- _vm->_graphics->hideMenu(RDMENU_BOTTOM);
-
- if (_vm->_mouseY > 399) {
- // Will wait for cursor to move off the bottom menu
- _vm->_mouseMode = MOUSE_holding;
- }
-
- // In case DC forgets
- _scriptVars[TALK_FLAG] = 0;
-
- return IR_CONT;
-}
-
// To request the status of a target, we run its 4th script, get-speech-state.
// This will cause RESULT to be set to either 1 (target is waiting) or 0
// (target is busy).
-/**
- * Wait for a target to become waiting, i.e. not busy, then send a command to
- * it.
- */
-
-int32 Logic::fnTheyDo(int32 *params) {
- // params: 0 target
- // 1 command
- // 2 ins1
- // 3 ins2
- // 4 ins3
- // 5 ins4
- // 6 ins5
-
- StandardHeader *head = (StandardHeader *) _vm->_resman->openResource(params[0]);
- assert (head->fileType == GAME_OBJECT);
-
- // Run the target's get-speech-state script
-
- int32 target = params[0];
- char *raw_script_ad = (char *) head;
- uint32 null_pc = 5;
-
- runScript(raw_script_ad, raw_script_ad, &null_pc);
-
- _vm->_resman->closeResource(target);
-
- if (_scriptVars[RESULT] == 1 && !_scriptVars[INS_COMMAND]) {
- // The target is waiting, i.e. not busy, and there is no other
- // command queued. Send the command.
-
- debug(5, "fnTheyDo: sending command to %d", target);
-
- _vm->_debugger->_speechScriptWaiting = 0;
-
- _scriptVars[SPEECH_ID] = params[0];
- _scriptVars[INS_COMMAND] = params[1];
- _scriptVars[INS1] = params[2];
- _scriptVars[INS2] = params[3];
- _scriptVars[INS3] = params[4];
- _scriptVars[INS4] = params[5];
- _scriptVars[INS5] = params[6];
-
- return IR_CONT;
- }
-
- // The target is busy. Come back again next cycle.
-
- _vm->_debugger->_speechScriptWaiting = target;
- return IR_REPEAT;
-}
-
-/**
- * Wait for a target to become waiting, i.e. not busy, send a command to it,
- * then wait for it to finish.
- */
-
-int32 Logic::fnTheyDoWeWait(int32 *params) {
- // params: 0 pointer to ob_logic
- // 1 target
- // 2 command
- // 3 ins1
- // 4 ins2
- // 5 ins3
- // 6 ins4
- // 7 ins5
-
- StandardHeader *head = (StandardHeader *) _vm->_resman->openResource(params[1]);
- assert(head->fileType == GAME_OBJECT);
-
- // Run the target's get-speech-state script
-
- int32 target = params[1];
- char *raw_script_ad = (char *) head;
- uint32 null_pc = 5;
-
- runScript(raw_script_ad, raw_script_ad, &null_pc);
-
- _vm->_resman->closeResource(target);
-
- ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
-
- if (_scriptVars[RESULT] == 1 && !_scriptVars[INS_COMMAND] && ob_logic->looping == 0) {
- // The target is waiting, i.e. not busy, and there is no other
- // command queued. We haven't sent the command yet, so do it.
-
- debug(5, "fnTheyDoWeWait: sending command to %d", target);
-
- _vm->_debugger->_speechScriptWaiting = target;
- ob_logic->looping = 1;
-
- _scriptVars[SPEECH_ID] = params[1];
- _scriptVars[INS_COMMAND] = params[2];
- _scriptVars[INS1] = params[3];
- _scriptVars[INS2] = params[4];
- _scriptVars[INS3] = params[5];
- _scriptVars[INS4] = params[6];
- _scriptVars[INS5] = params[7];
-
- return IR_REPEAT;
- }
-
- if (ob_logic->looping == 0) {
- // The command has not been sent yet. Keep waiting.
- _vm->_debugger->_speechScriptWaiting = target;
- return IR_REPEAT;
- }
-
- if (_scriptVars[RESULT] == 0) {
- // The command has been sent, and the target is busy doing it.
- // Wait for it to finish.
-
- debug(5, "fnTheyDoWeWait: Waiting for %d to finish", target);
-
- _vm->_debugger->_speechScriptWaiting = target;
- return IR_REPEAT;
- }
-
- debug(5, "fnTheyDoWeWait: %d finished", target);
-
- ob_logic->looping = 0;
- _vm->_debugger->_speechScriptWaiting = 0;
- return IR_CONT;
-}
-
-/**
- * Wait for a target to become waiting, i.e. not busy.
- */
-
-int32 Logic::fnWeWait(int32 *params) {
- // params: 0 target
-
- StandardHeader *head = (StandardHeader *) _vm->_resman->openResource(params[0]);
- assert(head->fileType == GAME_OBJECT);
-
- // Run the target's get-speech-state script
-
- int32 target = params[0];
- char *raw_script_ad = (char *) head;
- uint32 null_pc = 5;
-
- runScript(raw_script_ad, raw_script_ad, &null_pc);
-
- _vm->_resman->closeResource(target);
-
- if (_scriptVars[RESULT] == 0) {
- // The target is busy. Try again.
- _vm->_debugger->_speechScriptWaiting = target;
- return IR_REPEAT;
- }
-
- // The target is waiting, i.e. not busy.
-
- _vm->_debugger->_speechScriptWaiting = 0;
- return IR_CONT;
-}
-
-/**
- * Wait for a target to become waiting, i.e. not busy, or until we time out.
- * This is useful when clicking on a target to talk to it, and it doesn't
- * reply. This way, we won't lock up.
- *
- * If the target becomes waiting, RESULT is set to 0. If we time out, RESULT is
- * set to 1.
- */
-
-int32 Logic::fnTimedWait(int32 *params) {
- // params: 0 ob_logic
- // 1 target
- // 2 number of cycles before give up
-
- StandardHeader *head = (StandardHeader *) _vm->_resman->openResource(params[1]);
- assert(head->fileType == GAME_OBJECT);
-
- ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
-
- if (!ob_logic->looping) {
- // This is the first time, so set up the time-out.
- ob_logic->looping = params[2];
- }
-
- // Run the target's get-speech-state script
-
- int32 target = params[1];
- char *raw_script_ad = (char *) head;
- uint32 null_pc = 5;
-
- runScript(raw_script_ad, raw_script_ad, &null_pc);
-
- _vm->_resman->closeResource(target);
-
- if (_scriptVars[RESULT] == 1) {
- // The target is waiting, i.e. not busy
-
- _vm->_debugger->_speechScriptWaiting = 0;
-
- ob_logic->looping = 0;
- _scriptVars[RESULT] = 0;
- return IR_CONT;
- }
-
- ob_logic->looping--;
-
- if (!ob_logic->looping) {
- // Time's up.
-
- debug(5, "fnTimedWait: Timed out waiting for %d", target);
- _vm->_debugger->_speechScriptWaiting = 0;
-
- // Clear the event that hasn't been picked up - in theory,
- // none of this should ever happen.
-
- killAllIdsEvents(target);
- _scriptVars[RESULT] = 1;
- return IR_CONT;
- }
-
- // Target is busy. Keep trying.
-
- _vm->_debugger->_speechScriptWaiting = target;
- return IR_REPEAT;
-}
-
-enum {
- INS_talk = 1,
- INS_anim = 2,
- INS_reverse_anim = 3,
- INS_walk = 4,
- INS_turn = 5,
- INS_face = 6,
- INS_trace = 7,
- INS_no_sprite = 8,
- INS_sort = 9,
- INS_foreground = 10,
- INS_background = 11,
- INS_table_anim = 12,
- INS_reverse_table_anim = 13,
- INS_walk_to_anim = 14,
- INS_set_frame = 15,
- INS_stand_after_anim = 16,
- INS_quit = 42
-};
-
-/**
- * Receive and sequence the commands sent from the conversation script. We have
- * to do this in a slightly tweeky manner as we can no longer have generic
- * scripts.
- */
-
-int32 Logic::fnSpeechProcess(int32 *params) {
- // params: 0 pointer to ob_graphic
- // 1 pointer to ob_speech
- // 2 pointer to ob_logic
- // 3 pointer to ob_mega
- // 4 pointer to ob_walkdata
-
- ObjectSpeech *ob_speech = (ObjectSpeech *) _vm->_memory->decodePtr(params[1]);
-
- while (1) {
- int32 pars[9];
-
- // Check which command we're waiting for, and call the
- // appropriate function. Once we're done, clear the command
- // and set wait_state to 1.
- //
- // Note: we could save a var and ditch wait_state and check
- // 'command' for non zero means busy
- //
- // Note: I can't see that we ever check the value of wait_state
- // but perhaps it accesses that memory location directly?
-
- switch (ob_speech->command) {
- case 0:
- break;
- case INS_talk:
- pars[0] = params[0]; // ob_graphic
- pars[1] = params[1]; // ob_speech
- pars[2] = params[2]; // ob_logic
- pars[3] = params[3]; // ob_mega
- pars[4] = ob_speech->ins1; // encoded text number
- pars[5] = ob_speech->ins2; // wav res id
- pars[6] = ob_speech->ins3; // anim res id
- pars[7] = ob_speech->ins4; // anim table res id
- pars[8] = ob_speech->ins5; // animation mode - 0 lip synced, 1 just straight animation
-
- if (fnISpeak(pars) != IR_REPEAT) {
- ob_speech->command = 0;
- ob_speech->wait_state = 1;
- }
-
- return IR_REPEAT;
- case INS_turn:
- pars[0] = params[2]; // ob_logic
- pars[1] = params[0]; // ob_graphic
- pars[2] = params[3]; // ob_mega
- pars[3] = params[4]; // ob_walkdata
- pars[4] = ob_speech->ins1; // direction to turn to
-
- if (fnTurn(pars) != IR_REPEAT) {
- ob_speech->command = 0;
- ob_speech->wait_state = 1;
- }
-
- return IR_REPEAT;
- case INS_face:
- pars[0] = params[2]; // ob_logic
- pars[1] = params[0]; // ob_graphic
- pars[2] = params[3]; // ob_mega
- pars[3] = params[4]; // ob_walkdata
- pars[4] = ob_speech->ins1; // target
-
- if (fnFaceMega(pars) != IR_REPEAT) {
- ob_speech->command = 0;
- ob_speech->wait_state = 1;
- }
-
- return IR_REPEAT;
- case INS_anim:
- pars[0] = params[2]; // ob_logic
- pars[1] = params[0]; // ob_graphic
- pars[2] = ob_speech->ins1; // anim res
-
- if (fnAnim(pars) != IR_REPEAT) {
- ob_speech->command = 0;
- ob_speech->wait_state = 1;
- }
-
- return IR_REPEAT;
- case INS_reverse_anim:
- pars[0] = params[2]; // ob_logic
- pars[1] = params[0]; // ob_graphic
- pars[2] = ob_speech->ins1; // anim res
-
- if (fnReverseAnim(pars) != IR_REPEAT) {
- ob_speech->command = 0;
- ob_speech->wait_state = 1;
- }
-
- return IR_REPEAT;
- case INS_table_anim:
- pars[0] = params[2]; // ob_logic
- pars[1] = params[0]; // ob_graphic
- pars[2] = params[3]; // ob_mega
- pars[3] = ob_speech->ins1; // pointer to anim table
-
- if (fnMegaTableAnim(pars) != IR_REPEAT) {
- ob_speech->command = 0;
- ob_speech->wait_state = 1;
- }
-
- return IR_REPEAT;
- case INS_reverse_table_anim:
- pars[0] = params[2]; // ob_logic
- pars[1] = params[0]; // ob_graphic
- pars[2] = params[3]; // ob_mega
- pars[3] = ob_speech->ins1; // pointer to anim table
-
- if (fnReverseMegaTableAnim(pars) != IR_REPEAT) {
- ob_speech->command = 0;
- ob_speech->wait_state = 1;
- }
-
- return IR_REPEAT;
- case INS_no_sprite:
- fnNoSprite(params); // ob_graphic
-
- ob_speech->command = 0;
- ob_speech->wait_state = 1;
- return IR_REPEAT ;
- case INS_sort:
- fnSortSprite(params); // ob_graphic
-
- ob_speech->command = 0;
- ob_speech->wait_state = 1;
- return IR_REPEAT;
- case INS_foreground:
- fnForeSprite(params); // ob_graphic
-
- ob_speech->command = 0;
- ob_speech->wait_state = 1;
- return IR_REPEAT;
- case INS_background:
- fnBackSprite(params); // ob_graphic
-
- ob_speech->command = 0;
- ob_speech->wait_state = 1;
- return IR_REPEAT;
- case INS_walk:
- pars[0] = params[2]; // ob_logic
- pars[1] = params[0]; // ob_graphic
- pars[2] = params[3]; // ob_mega
- pars[3] = params[4]; // ob_walkdata
- pars[4] = ob_speech->ins1; // target x
- pars[5] = ob_speech->ins2; // target y
- pars[6] = ob_speech->ins3; // target direction
-
- if (fnWalk(pars) != IR_REPEAT) {
- ob_speech->command = 0;
- ob_speech->wait_state = 1;
- }
-
- return IR_REPEAT;
- case INS_walk_to_anim:
- pars[0] = params[2]; // ob_logic
- pars[1] = params[0]; // ob_graphic
- pars[2] = params[3]; // ob_mega
- pars[3] = params[4]; // ob_walkdata
- pars[4] = ob_speech->ins1; // anim resource
-
- if (fnWalkToAnim(pars) != IR_REPEAT) {
- ob_speech->command = 0;
- ob_speech->wait_state = 1;
- }
-
- return IR_REPEAT;
- case INS_stand_after_anim:
- pars[0] = params[0]; // ob_graphic
- pars[1] = params[3]; // ob_mega
- pars[2] = ob_speech->ins1; // anim resource
-
- fnStandAfterAnim(pars);
-
- ob_speech->command = 0;
- ob_speech->wait_state = 1;
- return IR_REPEAT;
- case INS_set_frame:
- pars[0] = params[0]; // ob_graphic
- pars[1] = ob_speech->ins1; // anim_resource
- pars[2] = ob_speech->ins2; // FIRST_FRAME or LAST_FRAME
- fnSetFrame(pars);
-
- ob_speech->command = 0;
- ob_speech->wait_state = 1;
- return IR_REPEAT;
- case INS_quit:
- // That's it - we're finished with this
- ob_speech->command = 0;
- // ob_speech->wait_state = 0;
- return IR_CONT;
- default:
- // Unimplemented command - just cancel
- ob_speech->command = 0;
- ob_speech->wait_state = 1;
- break;
- }
-
- if (_scriptVars[SPEECH_ID] == _scriptVars[ID]) {
- // There's a new command for us! Grab the command -
- // potentially we only have this cycle to do this - and
- // set things up so that the command will be picked up
- // on the next iteration of the while loop.
-
- debug(5, "fnSpeechProcess: Received new command %d", _scriptVars[INS_COMMAND]);
-
- _scriptVars[SPEECH_ID] = 0;
-
- ob_speech->command = _scriptVars[INS_COMMAND];
- ob_speech->ins1 = _scriptVars[INS1];
- ob_speech->ins2 = _scriptVars[INS2];
- ob_speech->ins3 = _scriptVars[INS3];
- ob_speech->ins4 = _scriptVars[INS4];
- ob_speech->ins5 = _scriptVars[INS5];
- ob_speech->wait_state = 0;
-
- _scriptVars[INS_COMMAND] = 0;
- } else {
- // No new command. We could run a blink anim (or
- // something) here.
-
- ob_speech->wait_state = 1;
- return IR_REPEAT;
- }
- }
-}
+// Distance kept above talking sprite
+#define GAP_ABOVE_HEAD 20
enum {
S_OB_GRAPHIC = 0,
@@ -707,365 +55,6 @@ enum {
};
/**
- * It's the super versatile fnSpeak. Text and wavs can be selected in any
- * combination.
- *
- * @note We can assume no human - there should be no human, at least!
- */
-
-int32 Logic::fnISpeak(int32 *params) {
- // params: 0 pointer to ob_graphic
- // 1 pointer to ob_speech
- // 2 pointer to ob_logic
- // 3 pointer to ob_mega
- // 4 encoded text number
- // 5 wav res id
- // 6 anim res id
- // 7 anim table res id
- // 8 animation mode 0 lip synced,
- // 1 just straight animation
-
- static bool cycle_skip = false;
- static bool speechRunning;
-
- // Set up the pointers which we know we'll always need
-
- ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[S_OB_LOGIC]);
- ObjectGraphic *ob_graphic = (ObjectGraphic *) _vm->_memory->decodePtr(params[S_OB_GRAPHIC]);
-
- // FIRST TIME ONLY: create the text, load the wav, set up the anim,
- // etc.
-
- if (!ob_logic->looping) {
- // New fudge to wait for smacker samples to finish
- // since they can over-run into the game
-
- if (_vm->_sound->getSpeechStatus() != RDSE_SAMPLEFINISHED)
- return IR_REPEAT;
-
- // New fudge for 'fx' subtitles: If subtitles switched off, and
- // we don't want to use a wav for this line either, then just
- // quit back to script right now!
-
- if (!_vm->_gui->_subtitles && !wantSpeechForLine(params[S_WAV]))
- return IR_CONT;
-
- // Drop out for 1st cycle to allow walks/anims to end and
- // display last frame before system locks while speech loaded
-
- if (!cycle_skip) {
- cycle_skip = true;
- return IR_REPEAT;
- }
-
- cycle_skip = false;
-
- _vm->_debugger->_textNumber = params[S_TEXT];
-
- // Pull out the text line to get the official text number
- // (for wav id). Once the wav id's go into all script text
- // commands, we'll only need this for debugging.
-
- uint32 text_res = params[S_TEXT] / SIZE;
- uint32 local_text = params[S_TEXT] & 0xffff;
-
- // For testing all text & speech!
- //
- // A script loop can send any text number to fnISpeak and it
- // will only run the valid ones or return with 'result' equal
- // to '1' or '2' to mean 'invalid text resource' and 'text
- // number out of range' respectively
- //
- // See 'testing_routines' object in George's Player Character
- // section of linc
-
- if (_scriptVars[SYSTEM_TESTING_TEXT]) {
- if (!_vm->_resman->checkValid(text_res)) {
- // Not a valid resource number - invalid (null
- // resource)
- _scriptVars[RESULT] = 1;
- return IR_CONT;
- }
-
- StandardHeader *head = (StandardHeader *) _vm->_resman->openResource(text_res);
-
- if (head->fileType != TEXT_FILE) {
- // Invalid - not a text resource
- _vm->_resman->closeResource(text_res);
- _scriptVars[RESULT] = 1;
- return IR_CONT;
- }
-
- if (!_vm->checkTextLine((byte *) head, local_text)) {
- // Line number out of range
- _vm->_resman->closeResource(text_res);
- _scriptVars[RESULT] = 2;
- return IR_CONT;
- }
-
- _vm->_resman->closeResource(text_res);
- _scriptVars[RESULT] = 0;
- }
-
- byte *text = _vm->fetchTextLine(_vm->_resman->openResource(text_res), local_text);
- _officialTextNumber = READ_LE_UINT16(text);
- _vm->_resman->closeResource(text_res);
-
- // Prevent dud lines from appearing while testing text & speech
- // since these will not occur in the game anyway
-
- if (_scriptVars[SYSTEM_TESTING_TEXT]) {
- // If actor number is 0 and text line is just a 'dash'
- // character
- if (_officialTextNumber == 0 && text[2] == '-' && text[3] == 0) {
- _scriptVars[RESULT] = 3;
- return IR_CONT;
- }
- }
-
- // Set the 'looping_flag' and the text-click-delays. We can
- // left-click past the text after half a second, and
- // right-click past it after a quarter of a second.
-
- ob_logic->looping = 1;
- _leftClickDelay = 6;
- _rightClickDelay = 3;
-
- if (_scriptVars[PLAYER_ID] != CUR_PLAYER_ID)
- debug(5, "(%d) Nico: %s", _officialTextNumber, text + 2);
- else {
- byte buf[NAME_LEN];
-
- debug(5, "(%d) %s: %s", _officialTextNumber, _vm->fetchObjectName(_scriptVars[ID], buf), text + 2);
- }
-
- // Set up the speech animation
-
- if (params[S_ANIM]) {
- // Just a straight anim.
- _animId = params[6];
- } else if (params[S_DIR_TABLE]) {
- // Use this direction table to derive the anim
- // NB. ASSUMES WE HAVE A MEGA OBJECT!!
-
- ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[S_OB_MEGA]);
- int32 *anim_table = (int32 *) _vm->_memory->decodePtr(params[S_DIR_TABLE]);
-
- _animId = anim_table[ob_mega->current_dir];
- } else {
- // No animation choosen
- _animId = 0;
- }
-
- if (_animId) {
- // Set the talker's graphic to the first frame of this
- // speech anim for now.
-
- _speechAnimType = _scriptVars[SPEECHANIMFLAG];
- ob_graphic->anim_resource = _animId;
- ob_graphic->anim_pc = 0;
- }
-
- // Default back to looped lip synced anims.
- _scriptVars[SPEECHANIMFLAG] = 0;
-
- // Set up _textX and _textY for speech panning and/or text
- // sprite position.
-
- locateTalker(params);
-
- // Is it to be speech or subtitles or both?
-
- // Assume not running until know otherwise
- speechRunning = false;
-
- // New fudge for 'fx' subtitles: If speech is selected, and
- // this line is allowed speech (not if it's an fx subtitle!)
-
- if (!_vm->_sound->isSpeechMute() && wantSpeechForLine(_officialTextNumber)) {
- // If the wavId parameter is zero because not yet
- // compiled into speech command, we can still get it
- // from the 1st 2 chars of the text line.
-
- if (!params[S_WAV])
- params[S_WAV] = (int32) _officialTextNumber;
-
- // Panning goes from -16 (left) to 16 (right)
- int8 speech_pan = ((_textX - 320) * 16) / 320;
-
- if (speech_pan < -16)
- speech_pan = -16;
- else if (speech_pan > 16)
- speech_pan = 16;
-
- uint32 rv = _vm->_sound->playCompSpeech(params[S_WAV], 16, speech_pan);
-
- if (rv == RD_OK) {
- // Ok, we've got something to play. Set it
- // playing now. (We might want to do this the
- // next cycle, don't know yet.)
-
- speechRunning = true;
- _vm->_sound->unpauseSpeech();
- } else {
- debug(5, "ERROR: PlayCompSpeech(wav=%d (res=%d pos=%d)) returned %.8x", params[S_WAV], text_res, local_text, rv);
- }
- }
-
- if (_vm->_gui->_subtitles || !speechRunning) {
- // We want subtitles, or the speech failed to load.
- // Either way, we're going to show the text so create
- // the text sprite.
-
- formText(params);
- }
- }
-
- // EVERY TIME: run a cycle of animation, if there is one
-
- if (_animId) {
- // There is an animation - Increment the anim frame number.
- ob_graphic->anim_pc++;
-
- byte *anim_file = _vm->_resman->openResource(ob_graphic->anim_resource);
- AnimHeader *anim_head = _vm->fetchAnimHeader(anim_file);
-
- if (!_speechAnimType) {
- // ANIM IS TO BE LIP-SYNC'ED & REPEATING
-
- if (ob_graphic->anim_pc == (int32) (anim_head->noAnimFrames)) {
- // End of animation - restart from frame 0
- ob_graphic->anim_pc = 0;
- } else if (speechRunning && _vm->_sound->amISpeaking() == RDSE_QUIET) {
- // The speech is running, but we're at a quiet
- // bit. Restart from frame 0 (closed mouth).
- ob_graphic->anim_pc = 0;
- }
- } else {
- // ANIM IS TO PLAY ONCE ONLY
- if (ob_graphic->anim_pc == (int32) (anim_head->noAnimFrames) - 1) {
- // Reached the last frame of the anim. Hold
- // anim on this last frame
- _animId = 0;
- }
- }
-
- _vm->_resman->closeResource(ob_graphic->anim_resource);
- } else if (_speechAnimType) {
- // Placed here so we actually display the last frame of the
- // anim.
- _speechAnimType = 0;
- }
-
- // EVERY TIME: FIND OUT IF WE NEED TO STOP THE SPEECH NOW...
-
- // If there is a wav then we're using that to end the speech naturally
-
- bool speechFinished = false;
-
- // If playing a sample
-
- if (speechRunning) {
- // Has it finished?
- if (_vm->_sound->getSpeechStatus() == RDSE_SAMPLEFINISHED)
- speechFinished = true;
- } else if (!speechRunning && _speechTime) {
- // Counting down text time because there is no sample - this
- // ends the speech
-
- // if no sample then we're using _speechTime to end speech
- // naturally
-
- _speechTime--;
- if (!_speechTime)
- speechFinished = true;
- }
-
- // Ok, all is running along smoothly - but a click means stop
- // unnaturally
-
- // So that we can go to the options panel while text & speech is
- // being tested
- if (_scriptVars[SYSTEM_TESTING_TEXT] == 0 || _vm->_mouseY > 0) {
- MouseEvent *me = _vm->mouseEvent();
-
- // Note that we now have TWO click-delays - one for LEFT
- // button, one for RIGHT BUTTON
-
- if ((!_leftClickDelay && me && (me->buttons & RD_LEFTBUTTONDOWN)) ||
- (!_rightClickDelay && me && (me->buttons & RD_RIGHTBUTTONDOWN))) {
- // Mouse click, after click_delay has expired -> end
- // the speech.
-
- // if testing text & speech
- if (_scriptVars[SYSTEM_TESTING_TEXT]) {
- // and RB used to click past text
- if (me->buttons & RD_RIGHTBUTTONDOWN) {
- // then we want the previous line again
- _scriptVars[SYSTEM_WANT_PREVIOUS_LINE] = 1;
- } else {
- // LB just want next line again
- _scriptVars[SYSTEM_WANT_PREVIOUS_LINE] = 0;
- }
- }
-
- speechFinished = true;
-
- // if speech sample playing, halt it prematurely
- if (speechRunning)
- _vm->_sound->stopSpeech();
- }
- }
-
- // If we are finishing the speech this cycle, do the business
-
- // !speechAnimType, as we want an anim which is playing once to have
- // finished.
-
- if (speechFinished && !_speechAnimType) {
- // If there is text, kill it
- if (_speechTextBlocNo) {
- _vm->_fontRenderer->killTextBloc(_speechTextBlocNo);
- _speechTextBlocNo = 0;
- }
-
- // if there is a speech anim, end it on closed mouth frame
- if (_animId) {
- _animId = 0;
- ob_graphic->anim_pc = 0;
- }
-
- speechRunning = false;
-
- // no longer in a script function loop
- ob_logic->looping = 0;
-
- _vm->_debugger->_textNumber = 0;
-
- // reset to zero, in case text line not even extracted (since
- // this number comes from the text line)
- _officialTextNumber = 0;
-
- _scriptVars[RESULT] = 0;
- return IR_CONT;
- }
-
- // Speech still going, so decrement the click_delay if it's still
- // active
-
- if (_leftClickDelay)
- _leftClickDelay--;
-
- if (_rightClickDelay)
- _rightClickDelay--;
-
- return IR_REPEAT;
-}
-
-// Distance kept above talking sprite
-#define GAP_ABOVE_HEAD 20
-
-/**
* Sets _textX and _textY for position of text sprite. Note that _textX is
* also used to calculate speech pan.
*/
diff --git a/sword2/startup.cpp b/sword2/startup.cpp
index 1c11bbefca..d7ae9419ea 100644
--- a/sword2/startup.cpp
+++ b/sword2/startup.cpp
@@ -30,13 +30,13 @@
#include "sword2/memory.h"
#include "sword2/resman.h"
#include "sword2/router.h"
-#include "sword2/driver/d_sound.h"
+#include "sword2/sound.h"
-#define Debug_Printf _vm->_debugger->DebugPrintf
+#define Debug_Printf _debugger->DebugPrintf
namespace Sword2 {
-bool Logic::initStartMenu(void) {
+bool Sword2Engine::initStartMenu() {
// Print out a list of all the start points available.
// There should be a linc produced file called startup.txt.
// This file should contain ascii numbers of all the resource game
@@ -49,6 +49,7 @@ bool Logic::initStartMenu(void) {
// ok, load in the master screen manager file
_totalStartups = 0;
+ _totalScreenManagers = 0;
if (!fp.open("startup.inf")) {
warning("Cannot open startup.inf - the debugger won't have a start menu");
@@ -119,12 +120,12 @@ bool Logic::initStartMenu(void) {
// - need to check in case un-built sections included in
// start list
- if (_vm->_resman->checkValid(_startRes)) {
- char *raw_script = (char *) _vm->_resman->openResource(_startRes);
+ if (_resman->checkValid(_startRes)) {
+ char *raw_script = (char *) _resman->openResource(_startRes);
uint32 null_pc = 0;
- runScript(raw_script, raw_script, &null_pc);
- _vm->_resman->closeResource(_startRes);
+ _logic->runScript(raw_script, raw_script, &null_pc);
+ _resman->closeResource(_startRes);
} else
warning("Start menu resource %d invalid", _startRes);
}
@@ -132,22 +133,16 @@ bool Logic::initStartMenu(void) {
return 1;
}
-int32 Logic::fnRegisterStartPoint(int32 *params) {
- // params: 0 id of startup script to call - key
- // 1 pointer to ascii message
-
+void Sword2Engine::registerStartPoint(int32 key, char *name) {
assert(_totalStartups < MAX_starts);
- char *name = (char *) _vm->_memory->decodePtr(params[1]);
-
_startList[_totalStartups].start_res_id = _startRes;
- _startList[_totalStartups].key = params[0];
+ _startList[_totalStartups].key = key;
strncpy(_startList[_totalStartups].description, name, MAX_description);
_startList[_totalStartups].description[MAX_description - 1] = 0;
_totalStartups++;
- return IR_CONT;
}
/**
@@ -155,7 +150,7 @@ int32 Logic::fnRegisterStartPoint(int32 *params) {
* start points in the game.
*/
-void Logic::conPrintStartMenu(void) {
+void Sword2Engine::conPrintStartMenu() {
if (!_totalStartups) {
Debug_Printf("Sorry - no startup positions registered?\n");
@@ -170,7 +165,7 @@ void Logic::conPrintStartMenu(void) {
Debug_Printf("%d (%s)\n", i, _startList[i].description);
}
-void Logic::conStart(int start) {
+void Sword2Engine::conStart(int start) {
if (!_totalStartups) {
Debug_Printf("Sorry - there are no startups!\n");
return;
@@ -183,44 +178,44 @@ void Logic::conStart(int start) {
// Restarting - stop sfx, music & speech!
- _vm->clearFxQueue();
- fnStopMusic(NULL);
- _vm->_sound->unpauseSpeech();
- _vm->_sound->stopSpeech();
+ _sound->clearFxQueue();
+ _logic->fnStopMusic(NULL);
+ _sound->unpauseSpeech();
+ _sound->stopSpeech();
// Remove all resources from memory, including player object and global
// variables
- _vm->_resman->removeAll();
+ _resman->removeAll();
// Reopen global variables resource and player object
- _vm->setupPersistentResources();
+ setupPersistentResources();
// Free all the route memory blocks from previous game
- _router->freeAllRouteMem();
+ _logic->_router->freeAllRouteMem();
// If there was speech text, kill the text block
- if (_speechTextBlocNo) {
- _vm->_fontRenderer->killTextBloc(_speechTextBlocNo);
- _speechTextBlocNo = 0;
+ if (_logic->_speechTextBlocNo) {
+ _fontRenderer->killTextBloc(_logic->_speechTextBlocNo);
+ _logic->_speechTextBlocNo = 0;
}
// Open George
- char *raw_data_ad = (char *) _vm->_resman->openResource(CUR_PLAYER_ID);
- char *raw_script = (char *) _vm->_resman->openResource(_startList[start].start_res_id);
+ char *raw_data_ad = (char *) _resman->openResource(CUR_PLAYER_ID);
+ char *raw_script = (char *) _resman->openResource(_startList[start].start_res_id);
// Denotes script to run
uint32 null_pc = _startList[start].key & 0xffff;
Debug_Printf("Running start %d\n", start);
- runScript(raw_script, raw_data_ad, &null_pc);
+ _logic->runScript(raw_script, raw_data_ad, &null_pc);
- _vm->_resman->closeResource(_startList[start].start_res_id);
- _vm->_resman->closeResource(CUR_PLAYER_ID);
+ _resman->closeResource(_startList[start].start_res_id);
+ _resman->closeResource(CUR_PLAYER_ID);
// Make sure there's a mouse, in case restarting while mouse not
// available
- fnAddHuman(NULL);
+ _logic->fnAddHuman(NULL);
}
} // End of namespace Sword2
diff --git a/sword2/startup.h b/sword2/startup.h
deleted file mode 100644
index 8486d81a63..0000000000
--- a/sword2/startup.h
+++ /dev/null
@@ -1,30 +0,0 @@
-/* Copyright (C) 1994-1998 Revolution Software Ltd.
- * Copyright (C) 2003-2005 The ScummVM project
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- * $Header$
- */
-
-#ifndef _STARTUP
-#define _STARTUP
-
-#define MAX_starts 100
-#define MAX_description 100
-
-namespace Sword2 {
-} // End of namespace Sword2
-
-#endif
diff --git a/sword2/sword2.cpp b/sword2/sword2.cpp
index 0afc0b3141..9bc65fcb38 100644
--- a/sword2/sword2.cpp
+++ b/sword2/sword2.cpp
@@ -39,7 +39,6 @@
#include "sword2/resman.h"
#include "sword2/sound.h"
#include "sword2/driver/d_draw.h"
-#include "sword2/driver/d_sound.h"
#ifdef _WIN32_WCE
extern bool isSmartphone(void);
@@ -190,8 +189,6 @@ Sword2Engine::Sword2Engine(GameDetector *detector, OSystem *syst) : Engine(syst)
}
Sword2Engine::~Sword2Engine() {
- killMusic();
-
delete _debugger;
delete _graphics;
delete _sound;
@@ -261,13 +258,14 @@ int Sword2Engine::init(GameDetector &detector) {
_mixer->setVolumeForSoundType(SoundMixer::kSpeechAudioDataType, ConfMan.getInt("speech_volume"));
_mixer->setVolumeForSoundType(SoundMixer::kSFXAudioDataType, ConfMan.getInt("sfx_volume"));
+ initStartMenu();
+
// During normal gameplay, we care neither about mouse button releases
// nor the scroll wheel.
setEventFilter(RD_LEFTBUTTONUP | RD_RIGHTBUTTONUP | RD_WHEELUP | RD_WHEELDOWN);
setupPersistentResources();
initialiseFontResourceFlags();
- initFxQueue();
if (_features & GF_DEMO)
Logic::_scriptVars[DEMO] = 1;
@@ -511,7 +509,7 @@ void Sword2Engine::gameCycle() {
setScrolling();
mouseEngine();
- processFxQueue();
+ _sound->processFxQueue();
}
void Sword2Engine::startGame() {
@@ -564,7 +562,7 @@ void Sword2Engine::pauseGame() {
if (_graphics->getFadeStatus() != RDFADE_NONE)
return;
- pauseAllSound();
+ _sound->pauseAllSound();
// Make the mouse cursor normal. This is the only place where we are
// allowed to clear the luggage this way.
@@ -599,7 +597,7 @@ void Sword2Engine::unpauseGame() {
if (Logic::_scriptVars[OBJECT_HELD] && _realLuggageItem)
setLuggage(_realLuggageItem);
- unpauseAllSound();
+ _sound->unpauseAllSound();
// Put back game screen palette; see build_display.cpp
setFullPalette(-1);
diff --git a/sword2/sword2.h b/sword2/sword2.h
index cac3119f55..c51202b447 100644
--- a/sword2/sword2.h
+++ b/sword2/sword2.h
@@ -41,6 +41,9 @@
#include "sword2/object.h"
#include "sword2/save_rest.h"
+#define MAX_starts 100
+#define MAX_description 100
+
class GameDetector;
class OSystem;
@@ -171,6 +174,24 @@ private:
MenuObject _masterMenuList[TOTAL_engine_pockets];
uint32 _totalMasters;
+ uint32 _totalStartups;
+ uint32 _totalScreenManagers;
+ uint32 _startRes;
+
+ struct StartUp {
+ char description[MAX_description];
+
+ // id of screen manager object
+ uint32 start_res_id;
+
+ // tell the manager which startup you want (if there are more
+ // than 1) (i.e more than 1 entrance to a screen and/or
+ // separate game boots)
+ uint32 key;
+ };
+
+ StartUp _startList[MAX_starts];
+
public:
Sword2Engine(GameDetector *detector, OSystem *syst);
~Sword2Engine();
@@ -378,25 +399,6 @@ public:
void setScrolling();
- // used to store id of tunes that loop, for save & restore
- uint32 _loopingMusicId;
-
- // to be called during system initialisation
- void initFxQueue();
-
- // to be called from the main loop, once per cycle
- void processFxQueue();
-
- // stops all fx & clears the queue - eg. when leaving a location
- void clearFxQueue();
-
- void pauseAllSound();
- void unpauseAllSound();
-
- void killMusic();
-
- void triggerFx(uint8 j);
-
bool _gamePaused;
bool _graphicsLevelFudged;
@@ -410,6 +412,10 @@ public:
void initialiseFontResourceFlags();
void initialiseFontResourceFlags(uint8 language);
+ bool initStartMenu();
+ void registerStartPoint(int32 key, char *name);
+ void conPrintStartMenu();
+ void conStart(int start);
// Convenience alias for OSystem::getMillis().
// This is a bit hackish, of course :-).
diff --git a/sword2/sync.cpp b/sword2/sync.cpp
index bfb5aa7edd..473bc93217 100644
--- a/sword2/sync.cpp
+++ b/sword2/sync.cpp
@@ -26,26 +26,6 @@
namespace Sword2 {
-int32 Logic::fnSendSync(int32 *params) {
- // params: 0 sync's recipient
- // 1 sync value
-
- for (int i = 0; i < MAX_syncs; i++) {
- if (_syncList[i].id == 0) {
- debug(5, "%d sends sync %d to %d", _scriptVars[ID], params[1], params[0]);
- _syncList[i].id = params[0];
- _syncList[i].sync = params[1];
- return IR_CONT;
- }
- }
-
- // The original code didn't even check for this condition, so maybe
- // it should be a fatal error?
-
- warning("No free sync slot");
- return IR_CONT;
-}
-
/**
* Clear any syncs registered for this id. Call this just after the id has been
* processed. Theoretically there could be more than one sync waiting for us,
@@ -75,38 +55,4 @@ int Logic::getSync(void) {
return -1;
}
-/**
- * Like getSync(), but called from scripts. Sets the RESULT variable to
- * the sync value, or 0 if none is found.
- */
-
-int32 Logic::fnGetSync(int32 *params) {
- // params: none
-
- int slot = getSync();
-
- _scriptVars[RESULT] = (slot != -1) ? _syncList[slot].sync : 0;
- return IR_CONT;
-}
-
-/**
- * Wait for sync to happen. Sets the RESULT variable to the sync value, once
- * it has been found.
- */
-
-int32 Logic::fnWaitSync(int32 *params) {
- // params: none
-
- debug(6, "fnWaitSync: %d waits", _scriptVars[ID]);
-
- int slot = getSync();
-
- if (slot == -1)
- return IR_REPEAT;
-
- debug(5, "fnWaitSync: %d got sync %d", _scriptVars[ID], _syncList[slot].sync);
- _scriptVars[RESULT] = _syncList[slot].sync;
- return IR_CONT;
-}
-
} // End of namespace Sword2
diff --git a/sword2/walker.cpp b/sword2/walker.cpp
index da1c2b5057..d0238b4df7 100644
--- a/sword2/walker.cpp
+++ b/sword2/walker.cpp
@@ -36,405 +36,6 @@
namespace Sword2 {
/**
- * Walk mega to (x,y,dir). Set RESULT to 0 if it succeeded. Otherwise, set
- * RESULT to 1.
- */
-
-int32 Logic::fnWalk(int32 *params) {
- // params: 0 pointer to object's logic structure
- // 1 pointer to object's graphic structure
- // 2 pointer to object's mega structure
- // 3 pointer to object's walkdata structure
- // 4 target x-coord
- // 5 target y-coord
- // 6 target direction (8 means end walk on ANY direction)
-
- ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
- ObjectGraphic *ob_graph = (ObjectGraphic *) _vm->_memory->decodePtr(params[1]);
- ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[2]);
-
- int16 target_x = (int16) params[4];
- int16 target_y = (int16) params[5];
- uint8 target_dir = (uint8) params[6];
-
- ObjectWalkdata *ob_walkdata;
-
- // If this is the start of the walk, calculate the route.
-
- if (!ob_logic->looping) {
- // If we're already there, don't even bother allocating
- // memory and calling the router, just quit back & continue
- // the script! This avoids an embarassing mega stand frame
- // appearing for one cycle when we're already in position for
- // an anim eg. repeatedly clicking on same object to repeat
- // an anim - no mega frame will appear in between runs of the
- // anim.
-
- if (ob_mega->feet_x == target_x && ob_mega->feet_y == target_y && ob_mega->current_dir == target_dir) {
- _scriptVars[RESULT] = 0;
- return IR_CONT;
- }
-
- assert(params[6] >= 0 && params[6] <= 8);
-
- ob_walkdata = (ObjectWalkdata *) _vm->_memory->decodePtr(params[3]);
-
- ob_mega->walk_pc = 0;
-
- // Set up mem for _walkData in route_slots[] & set mega's
- // 'route_slot_id' accordingly
-
- _router->allocateRouteMem();
-
- int32 route = _router->routeFinder(ob_mega, ob_walkdata, target_x, target_y, target_dir);
-
- // 0 = can't make route to target
- // 1 = created route
- // 2 = zero route but may need to turn
-
- if (route == 1 || route == 2) {
- // so script fnWalk loop continues until end of
- // walk-anim
-
- ob_logic->looping = 1;
-
- // need to animate the route now, so don't set result
- // or return yet!
-
- // started walk
- ob_mega->currently_walking = 1;
-
- // (see fnGetPlayerSaveData() in save_rest.cpp
- } else {
- _router->freeRouteMem();
- _scriptVars[RESULT] = 1;
- return IR_CONT;
- }
-
- // Walk is about to start, so set the mega's graphic resource
- ob_graph->anim_resource = ob_mega->megaset_res;
- } else if (_scriptVars[EXIT_FADING] && _vm->_graphics->getFadeStatus() == RDFADE_BLACK) {
- // Double clicked an exit so quit the walk when screen is black
- // ok, thats it - back to script and change screen
-
- ob_logic->looping = 0;
- _router->freeRouteMem();
-
- // Must clear in-case on the new screen there's a walk
- // instruction (which would get cut short)
- _scriptVars[EXIT_CLICK_ID] = 0;
-
- // finished walk
- ob_mega->currently_walking = 0;
-
- // see fnGetPlayerSaveData() in save_rest.cpp
-
- _scriptVars[RESULT] = 0;
-
- // continue the script so that RESULT can be checked!
- return IR_CONT;
- }
-
- // get pointer to walkanim & current frame position
-
- WalkData *walkAnim = _router->getRouteMem();
- int32 walk_pc = ob_mega->walk_pc;
-
- // If stopping the walk early, overwrite the next step with a
- // slow-out, then finish
-
- if (checkEventWaiting()) {
- if (walkAnim[walk_pc].step == 0 && walkAnim[walk_pc + 1].step == 1) {
- // At the beginning of a step
- ob_walkdata = (ObjectWalkdata *) _vm->_memory->decodePtr(params[3]);
- _router->earlySlowOut(ob_mega, ob_walkdata);
- }
- }
-
- // Get new frame of walk
-
- ob_graph->anim_pc = walkAnim[walk_pc].frame;
- ob_mega->current_dir = walkAnim[walk_pc].dir;
- ob_mega->feet_x = walkAnim[walk_pc].x;
- ob_mega->feet_y = walkAnim[walk_pc].y;
-
- // Check if NEXT frame is in fact the end-marker of the walk sequence
- // so we can return to script just as the final (stand) frame of the
- // walk is set - so that if followed by an anim, the anim's first
- // frame replaces the final stand-frame of the walk (see below)
-
- // '512' is end-marker
- if (walkAnim[walk_pc + 1].frame == 512) {
- ob_logic->looping = 0;
- _router->freeRouteMem();
-
- // finished walk
- ob_mega->currently_walking = 0;
-
- // (see fnGetPlayerSaveData() in save_rest.cpp
-
- // if George's walk has been interrupted to run a new action
- // script for instance or Nico's walk has been interrupted by
- // player clicking on her to talk
-
- // There used to be code here for checking if two megas were
- // colliding, but that code had been commented out, and it
- // was only run if a function that always returned zero
- // returned non-zero.
-
- if (checkEventWaiting()) {
- startEvent();
- _scriptVars[RESULT] = 1;
- return IR_TERMINATE;
- } else {
- _scriptVars[RESULT] = 0;
-
- // CONTINUE the script so that RESULT can be checked!
- // Also, if an anim command follows the fnWalk command,
- // the 1st frame of the anim (which is always a stand
- // frame itself) can replace the final stand frame of
- // the walk, to hide the slight difference between the
- // shrinking on the mega frames and the pre-shrunk anim
- // start-frame.
-
- return IR_CONT;
- }
- }
-
- // Increment the walkanim frame number and come back next cycle
-
- ob_mega->walk_pc++;
- return IR_REPEAT;
-}
-
-/**
- * Walk mega to start position of anim
- */
-
-int32 Logic::fnWalkToAnim(int32 *params) {
- // params: 0 pointer to object's logic structure
- // 1 pointer to object's graphic structure
- // 2 pointer to object's mega structure
- // 3 pointer to object's walkdata structure
- // 4 anim resource id
-
- int32 pars[7];
-
- // Walkdata is needed for earlySlowOut if player clicks elsewhere
- // during the walk.
-
- pars[0] = params[0];
- pars[1] = params[1];
- pars[2] = params[2];
- pars[3] = params[3];
-
- ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
-
- // If this is the start of the walk, read anim file to get start coords
-
- if (!ob_logic->looping) {
- byte *anim_file = _vm->_resman->openResource(params[4]);
- AnimHeader *anim_head = _vm->fetchAnimHeader( anim_file );
-
- pars[4] = anim_head->feetStartX;
- pars[5] = anim_head->feetStartY;
- pars[6] = anim_head->feetStartDir;
-
- _vm->_resman->closeResource(params[4]);
-
- // If start coords not yet set in anim header, use the standby
- // coords (which should be set beforehand in the script).
-
- if (pars[4] == 0 && pars[5] == 0) {
- byte buf[NAME_LEN];
-
- pars[4] = _standbyX;
- pars[5] = _standbyY;
- pars[6] = _standbyDir;
-
- debug(3, "WARNING: fnWalkToAnim(%s) used standby coords", _vm->fetchObjectName(params[4], buf));
- }
-
- assert(pars[6] >= 0 && pars[6] <= 7);
- }
-
- return fnWalk(pars);
-}
-
-/**
- * Turn mega to the specified direction. Just needs to call fnWalk() with
- * current feet coords, so router can produce anim of turn frames.
- */
-
-int32 Logic::fnTurn(int32 *params) {
- // params: 0 pointer to object's logic structure
- // 1 pointer to object's graphic structure
- // 2 pointer to object's mega structure
- // 3 pointer to object's walkdata structure
- // 4 target direction
-
- int32 pars[7];
-
- pars[0] = params[0];
- pars[1] = params[1];
- pars[2] = params[2];
- pars[3] = params[3];
-
- ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
-
- // If this is the start of the turn, get the mega's current feet
- // coords + the required direction
-
- if (!ob_logic->looping) {
- assert(params[4] >= 0 && params[4] <= 7);
-
- ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[2]);
-
- pars[4] = ob_mega->feet_x;
- pars[5] = ob_mega->feet_y;
- pars[6] = params[4];
- }
-
- return fnWalk(pars);
-}
-
-/**
- * Stand mega at (x,y,dir)
- * Sets up the graphic object, but also needs to set the new 'current_dir' in
- * the mega object, so the router knows in future
- */
-
-int32 Logic::fnStandAt(int32 *params) {
- // params: 0 pointer to object's graphic structure
- // 1 pointer to object's mega structure
- // 2 target x-coord
- // 3 target y-coord
- // 4 target direction
-
- assert(params[4] >= 0 && params[4] <= 7);
-
- ObjectGraphic *ob_graph = (ObjectGraphic *) _vm->_memory->decodePtr(params[0]);
- ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[1]);
-
- // set up the stand frame & set the mega's new direction
-
- ob_mega->feet_x = params[2];
- ob_mega->feet_y = params[3];
- ob_mega->current_dir = params[4];
-
- // mega-set animation file
- ob_graph->anim_resource = ob_mega->megaset_res;
-
- // dir + first stand frame (always frame 96)
- ob_graph->anim_pc = params[4] + 96;
-
- return IR_CONT;
-}
-
-/**
- * Stand mega into the specified direction at current feet coords.
- * Just needs to call fnStandAt() with current feet coords.
- */
-
-int32 Logic::fnStand(int32 *params) {
- // params: 0 pointer to object's graphic structure
- // 1 pointer to object's mega structure
- // 2 target direction
-
- ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[1]);
-
- int32 pars[5];
-
- pars[0] = params[0];
- pars[1] = params[1];
- pars[2] = ob_mega->feet_x;
- pars[3] = ob_mega->feet_y;
- pars[4] = params[2];
-
- return fnStandAt(pars);
-}
-
-/**
- * stand mega at end position of anim
- */
-
-int32 Logic::fnStandAfterAnim(int32 *params) {
- // params: 0 pointer to object's graphic structure
- // 1 pointer to object's mega structure
- // 2 anim resource id
-
- byte *anim_file = _vm->_resman->openResource(params[2]);
- AnimHeader *anim_head = _vm->fetchAnimHeader(anim_file);
-
- int32 pars[5];
-
- pars[0] = params[0];
- pars[1] = params[1];
-
- pars[2] = anim_head->feetEndX;
- pars[3] = anim_head->feetEndY;
- pars[4] = anim_head->feetEndDir;
-
- // If start coords not available either use the standby coords (which
- // should be set beforehand in the script)
-
- if (pars[2] == 0 && pars[3] == 0) {
- byte buf[NAME_LEN];
-
- pars[2] = _standbyX;
- pars[3] = _standbyY;
- pars[4] = _standbyDir;
-
- debug(3, "WARNING: fnStandAfterAnim(%s) used standby coords", _vm->fetchObjectName(params[2], buf));
- }
-
- assert(pars[4] >= 0 && pars[4] <= 7);
-
- _vm->_resman->closeResource(params[2]);
- return fnStandAt(pars);
-}
-
-/**
- * Stand mega at start position of anim
- */
-
-int32 Logic::fnStandAtAnim(int32 *params) {
- // params: 0 pointer to object's graphic structure
- // 1 pointer to object's mega structure
- // 2 anim resource id
-
- byte *anim_file = _vm->_resman->openResource(params[2]);
- AnimHeader *anim_head = _vm->fetchAnimHeader(anim_file);
-
- int32 pars[5];
-
- pars[0] = params[0];
- pars[1] = params[1];
-
- pars[2] = anim_head->feetStartX;
- pars[3] = anim_head->feetStartY;
- pars[4] = anim_head->feetStartDir;
-
- // If start coords not available use the standby coords (which should
- // be set beforehand in the script)
-
- if (pars[2] == 0 && pars[3] == 0) {
- byte buf[NAME_LEN];
-
- pars[2] = _standbyX;
- pars[3] = _standbyY;
- pars[4] = _standbyDir;
-
- debug(3, "WARNING: fnStandAtAnim(%s) used standby coords", _vm->fetchObjectName(params[2], buf));
- }
-
- assert(pars[4] >= 0 && pars[4] <= 7);
-
- _vm->_resman->closeResource(params[2]);
- return fnStandAt(pars);
-}
-
-/**
* Work out direction from start to dest.
*/
@@ -468,244 +69,4 @@ int Logic::whatTarget(int startX, int startY, int destX, int destY) {
return (deltaY > 0) ? 5 : 7;
}
-/**
- * Turn mega to face point (x,y) on the floor
- * Just needs to call fnWalk() with current feet coords & direction computed
- * by whatTarget()
- */
-
-int32 Logic::fnFaceXY(int32 *params) {
- // params: 0 pointer to object's logic structure
- // 1 pointer to object's graphic structure
- // 2 pointer to object's mega structure
- // 3 pointer to object's walkdata structure
- // 4 target x-coord
- // 5 target y-coord
-
- int32 pars[7];
-
- pars[0] = params[0];
- pars[1] = params[1];
- pars[2] = params[2];
- pars[3] = params[3];
-
- ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
-
- // If this is the start of the turn, get the mega's current feet
- // coords + the required direction
-
- if (!ob_logic->looping) {
- ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[2]);
-
- pars[4] = ob_mega->feet_x;
- pars[5] = ob_mega->feet_y;
- pars[6] = whatTarget(ob_mega->feet_x, ob_mega->feet_y, params[4], params[5]);
- }
-
- return fnWalk(pars);
-}
-
-int32 Logic::fnFaceMega(int32 *params) {
- // params: 0 pointer to object's logic structure
- // 1 pointer to object's graphic structure
- // 2 pointer to object's mega structure
- // 3 pointer to object's walkdata structure
- // 4 id of target mega to face
-
- int32 pars[7];
-
- pars[0] = params[0];
- pars[1] = params[1];
- pars[2] = params[2];
- pars[3] = params[3];
-
- ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
-
- // If this is the start of the walk, decide where to walk to.
-
- if (!ob_logic->looping) {
- StandardHeader *head = (StandardHeader *) _vm->_resman->openResource(params[4]);
-
- assert(head->fileType == GAME_OBJECT);
-
- // Call the base script. This is the graphic/mouse service
- // call, and will set _engineMega to the ObjectMega of mega we
- // want to turn to face.
-
- char *raw_script_ad = (char *) head;
- uint32 null_pc = 3;
-
- runScript(raw_script_ad, raw_script_ad, &null_pc);
-
- _vm->_resman->closeResource(params[4]);
-
- ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[2]);
-
- pars[3] = params[3];
- pars[4] = ob_mega->feet_x;
- pars[5] = ob_mega->feet_y;
- pars[6] = whatTarget(ob_mega->feet_x, ob_mega->feet_y, _engineMega.feet_x, _engineMega.feet_y);
- }
-
- return fnWalk(pars);
-}
-
-/**
- * Route to the left or right hand side of target id, if possible.
- */
-
-int32 Logic::fnWalkToTalkToMega(int32 *params) {
- // params: 0 pointer to object's logic structure
- // 1 pointer to object's graphic structure
- // 2 pointer to object's mega structure
- // 3 pointer to object's walkdata structure
- // 4 id of target mega to face
- // 5 distance
-
- int32 pars[7];
-
- pars[0] = params[0];
- pars[1] = params[1];
- pars[2] = params[2];
- pars[3] = params[3];
-
- ObjectLogic *ob_logic = (ObjectLogic *) _vm->_memory->decodePtr(params[0]);
-
- // If this is the start of the walk, calculate the route.
-
- if (!ob_logic->looping) {
- StandardHeader *head = (StandardHeader *) _vm->_resman->openResource(params[4]);
-
- assert(head->fileType == GAME_OBJECT);
-
- // Call the base script. This is the graphic/mouse service
- // call, and will set _engineMega to the ObjectMega of mega we
- // want to route to.
-
- char *raw_script_ad = (char *) head;
- uint32 null_pc = 3;
-
- runScript(raw_script_ad, raw_script_ad, &null_pc);
-
- _vm->_resman->closeResource(params[4]);
-
- // Stand exactly beside the mega, ie. at same y-coord
- pars[5] = _engineMega.feet_y;
-
- ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[2]);
-
- // Apply scale factor to walk distance. Ay+B gives 256 * scale
- // ie. 256 * 256 * true_scale for even better accuracy, ie.
- // scale = (Ay + B) / 256
-
- int scale = (ob_mega->scale_a * ob_mega->feet_y + ob_mega->scale_b) / 256;
- int mega_separation = (params[5] * scale) / 256;
-
- debug(4, "Target is at (%d, %d), separation %d", _engineMega.feet_x, _engineMega.feet_y, mega_separation);
-
- if (_engineMega.feet_x < ob_mega->feet_x) {
- // Target is left of us, so aim to stand to their
- // right. Face down_left
-
- pars[4] = _engineMega.feet_x + mega_separation;
- pars[6] = 5;
- } else {
- // Ok, must be right of us so aim to stand to their
- // left. Face down_right.
-
- pars[4] = _engineMega.feet_x - mega_separation;
- pars[6] = 3;
- }
- }
-
- return fnWalk(pars);
-}
-
-int32 Logic::fnSetWalkGrid(int32 *params) {
- // params: none
-
- error("fnSetWalkGrid() is no longer a valid opcode");
- return IR_CONT;
-}
-
-/**
- * Add this walkgrid resource to the list of those used for routing in this
- * location. Note that this is ignored if the resource is already in the list.
- */
-
-int32 Logic::fnAddWalkGrid(int32 *params) {
- // params: 0 id of walkgrid resource
-
- // All objects that add walkgrids must be restarted whenever we
- // re-enter a location.
-
- // DON'T EVER KILL GEORGE!
- if (_scriptVars[ID] != 8) {
- // Need to call this in case it wasn't called in script!
- fnAddToKillList(NULL);
- }
-
- _router->addWalkGrid(params[0]);
- fnPreLoad(params);
- return IR_CONT;
-}
-
-/**
- * Remove this walkgrid resource from the list of those used for routing in
- * this location. Note that this is ignored if the resource isn't actually
- * in the list.
- */
-
-int32 Logic::fnRemoveWalkGrid(int32 *params) {
- // params: 0 id of walkgrid resource
-
- _router->removeWalkGrid(params[0]);
- return IR_CONT;
-}
-
-int32 Logic::fnRegisterWalkGrid(int32 *params) {
- // params: none
-
- error("fnRegisterWalkGrid() is no longer a valid opcode");
- return IR_CONT;
-}
-
-int32 Logic::fnSetScaling(int32 *params) {
- // params: 0 pointer to object's mega structure
- // 1 scale constant A
- // 2 scale constant B
-
- // 256 * s = A * y + B
-
- // Where s is system scale, which itself is (256 * actual_scale) ie.
- // s == 128 is half size
-
- ObjectMega *ob_mega = (ObjectMega *) _vm->_memory->decodePtr(params[0]);
-
- ob_mega->scale_a = params[1];
- ob_mega->scale_b = params[2];
-
- return IR_CONT;
-}
-
-/**
- * Set the standby walk coords to be used by fnWalkToAnim() and
- * fnStandAfterAnim() when the anim header's start/end coords are zero.
- * Useful during development; can stay in final game anyway.
- */
-
-int32 Logic::fnSetStandbyCoords(int32 *params) {
- // params: 0 x-coord
- // 1 y-coord
- // 2 direction (0..7)
-
- assert(params[2] >= 0 && params[2] <= 7);
-
- _standbyX = (int16) params[0];
- _standbyY = (int16) params[1];
- _standbyDir = (uint8) params[2];
-
- return IR_CONT;
-}
-
} // End of namespace Sword2