aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTorbjörn Andersson2005-01-28 16:33:14 +0000
committerTorbjörn Andersson2005-01-28 16:33:14 +0000
commitc0a3816e1790182b7b4b3854846f01247b00ebbe (patch)
tree65e6fd93a7dcff1bb2111c802f283ff511b8b027
parent6feb7ae90f38c11b85868c54fb5b328d50844856 (diff)
downloadscummvm-rg350-c0a3816e1790182b7b4b3854846f01247b00ebbe.tar.gz
scummvm-rg350-c0a3816e1790182b7b4b3854846f01247b00ebbe.tar.bz2
scummvm-rg350-c0a3816e1790182b7b4b3854846f01247b00ebbe.zip
Began what I hope is the final major restructuring of the BS2 engine.
In this first step, I have moved all opcode functions into functions.cpp, instead of having them scattered all over the place. To get things to compile again, I had to rewrite the overly complicated sound effects handling. It's much simpler now. The next step will be to move any non-trivial code out of the opcode functions and into the appropriate object. This, I hope, will make it easier to create well-separated objects, instead of the current mess. I also want to tear down the artificial boundary between the main directory and the "driver" directory. We already have a cross-platform layer; there's no need to have yet another one. (Actually, the rewriting of the sound effects code took one first step in this direction.) At the final stage, I'd like to get rid of the "drivers" directory completely, but I'll probably need some help with that if I want to preserve the CVS history of the code. Things will probably be a bit bumpy along the way, but I seem to have reached a point of relative stability again, which is why I'm commiting this now. svn-id: r16668
-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