From ea3b0d11003dbf297b99d22905363df0b24c2f85 Mon Sep 17 00:00:00 2001 From: Andrew Kurushin Date: Tue, 21 Dec 2004 06:49:07 +0000 Subject: - rewritten actors speech engine : 1) there are three types of speech: one acor, multiple actor, non actor 2) slow speech implemented 3) uses native engine flags (async,noanimate...) instead of semaphores - proper timings implemented svn-id: r16237 --- saga/actor.cpp | 392 +++++++++++++++++++++++++++---------------------------- saga/actor.h | 107 ++++++++++----- saga/console.cpp | 15 +++ saga/console.h | 2 + saga/input.cpp | 4 +- saga/resnames.h | 5 + saga/saga.cpp | 2 +- saga/saga.h | 9 +- saga/script.cpp | 132 +++++++------------ saga/script.h | 57 ++++---- saga/sdebug.cpp | 20 +-- saga/sfuncs.cpp | 9 +- saga/sthread.cpp | 160 ++++++++++++++--------- saga/xref.txt | 4 +- 14 files changed, 496 insertions(+), 422 deletions(-) diff --git a/saga/actor.cpp b/saga/actor.cpp index ebe812c52a..100abe4de2 100644 --- a/saga/actor.cpp +++ b/saga/actor.cpp @@ -40,6 +40,7 @@ #include "saga/actordata.h" #include "saga/stream.h" #include "saga/interface.h" +#include "common/config-manager.h" namespace Saga { @@ -62,6 +63,7 @@ ACTIONTIMES ActionTDeltas[] = { Actor::Actor(SagaEngine *vm) : _vm(vm) { int i; ActorData *actor; + debug(9, "Actor::Actor()"); // Get actor resource file context _actorContext = GAME_GetFileContext(GAME_RESOURCEFILE, 0); @@ -73,7 +75,7 @@ Actor::Actor(SagaEngine *vm) : _vm(vm) { actor = &_actors[i]; actor->actorId = ACTOR_INDEX_TO_ID(i); actor->index = i; - debug(0, "init actorId=0x%X index=0x%X", actor->actorId, actor->index); + debug(9, "init actorId=0x%X index=0x%X", actor->actorId, actor->index); actor->nameIndex = ActorTable[i].nameIndex; actor->spriteListResourceId = ActorTable[i].spriteListResourceId; actor->frameListResourceId = ActorTable[i].frameListResourceId; @@ -100,7 +102,7 @@ Actor::~Actor() { int i; ActorData *actor; - debug(0, "Actor::~Actor()"); + debug(9, "Actor::~Actor()"); //release resources for (i = 0; i < ACTORCOUNT; i++) { actor = &_actors[i]; @@ -118,7 +120,7 @@ bool Actor::loadActorResources(ActorData * actor) { int i, orient; int result; - debug(0, "Loading frame resource id 0x%X", actor->frameListResourceId); + debug(9, "Loading frame resource id 0x%X", actor->frameListResourceId); result = RSC_LoadResource(_actorContext, actor->frameListResourceId, &resourcePointer, &resourceLength); if (result != SUCCESS) { warning("Couldn't load sprite action index resource"); @@ -126,7 +128,7 @@ bool Actor::loadActorResources(ActorData * actor) { } framesCount = resourceLength / 16; - debug(0, "Frame resource contains %d frames", framesCount); + debug(9, "Frame resource contains %d frames", framesCount); framesPointer = (ActorFrame *)malloc(sizeof(ActorFrame) * framesCount); if (framesPointer == NULL) { @@ -154,7 +156,7 @@ bool Actor::loadActorResources(ActorData * actor) { actor->framesCount = framesCount; - debug(0, "Loading sprite resource id 0x%X", actor->spriteListResourceId); + debug(9, "Loading sprite resource id 0x%X", actor->spriteListResourceId); if (_vm->_sprite->loadList(actor->spriteListResourceId, &actor->spriteList) != SUCCESS) { warning("Unable to load sprite list"); return false; @@ -163,7 +165,7 @@ bool Actor::loadActorResources(ActorData * actor) { i = _vm->_sprite->getListLen(actor->spriteList); if ( (lastFrame >= i)) { - debug(0, "Appending to sprite list 0x%X", actor->spriteListResourceId); + debug(9, "Appending to sprite list 0x%X", actor->spriteListResourceId); if (_vm->_sprite->appendList(actor->spriteListResourceId + 1, actor->spriteList) != SUCCESS) { warning("Unable append sprite list"); return false; @@ -199,6 +201,87 @@ void Actor::updateActorsScene() { } } +void Actor::handleSpeech(int msec) { + int stringLength; + int sampleLength; + bool removeFirst; + int i; + int talkspeed; + ActorData *actor; + + if (!isSpeaking()) return; + + stringLength = strlen(_activeSpeech.strings[0]); + + if (stringLength == 0) + error("Empty strings not allowed"); + + if (_vm->_script->_skipSpeeches) { + _activeSpeech.stringsCount = 0; + _vm->_sound->stopVoice(); + _vm->_script->wakeUpThreads(kWaitTypeSpeech); + return; + } + + if (!_activeSpeech.playing) { // just added + talkspeed = ConfMan.getInt("talkspeed"); + if (_activeSpeech.speechFlags & kSpeakSlow) { + if (_activeSpeech.slowModeCharIndex >= stringLength) + error("Wrong string index"); + + debug(0 , "Slow string!"); + _activeSpeech.playingTime = 10 * talkspeed; + // 10 - fix it + + } else { + sampleLength = _vm->_sndRes->getVoiceLength(_activeSpeech.sampleResourceId); //fixme - too fast + + if (sampleLength < 0) { + _activeSpeech.playingTime = stringLength * talkspeed; + } else { + _activeSpeech.playingTime = sampleLength; + } + } + + if (_activeSpeech.sampleResourceId != -1) + _vm->_sndRes->playVoice(_activeSpeech.sampleResourceId); + + if (_activeSpeech.actorIds[0] != 0) { + actor = getActor(_activeSpeech.actorIds[0]); + if (!(_activeSpeech.speechFlags & kSpeakNoAnimate)) { + actor->currentAction = kActionSpeak; + //a->actionCycle = rand() % 64; todo + } + } + _activeSpeech.playing = true; + return; + } + + + _activeSpeech.playingTime -= msec; + + removeFirst = false; + if (_activeSpeech.playingTime <= 0) { + if (_activeSpeech.speechFlags & kSpeakSlow) { + _activeSpeech.slowModeCharIndex++; + if (_activeSpeech.slowModeCharIndex >= stringLength) + removeFirst = true; + } else { + removeFirst = true; + } + } + + if (removeFirst) { + for (i = 1; i < _activeSpeech.stringsCount; i++) { + _activeSpeech.strings[i - 1] = _activeSpeech.strings[i]; + } + _activeSpeech.stringsCount--; + } + + if (!isSpeaking()) + _vm->_script->wakeUpThreadsDelayed(kWaitTypeSpeech, ticksToMSec(kScriptTimeTicksPerSecond / 3)); +} + int Actor::direct(int msec) { int i; ActorData *actor; @@ -228,13 +311,6 @@ int Actor::direct(int msec) { handleWalkIntent(actor, &a_intent->walkIntent, &a_intent->a_idone, msec); } break; - case INTENT_SPEAK: - // Actor wants to blab - { - handleSpeakIntent(actor, a_intent, &a_intent->a_idone, msec); - } - break; - default: break; } @@ -275,6 +351,8 @@ int Actor::direct(int msec) { } } +//process speech + handleSpeech(msec); return SUCCESS; } @@ -284,7 +362,7 @@ void Actor::createDrawOrderList() { ActorData *actor; _drawOrderList.clear(); - for (i = 0;i < ACTORCOUNT;i++) { + for (i = 0; i < ACTORCOUNT; i++) { actor = &_actors[i]; if (actor->disabled) continue; if (actor->sceneNumber != _vm->_scene->currentSceneNumber()) continue; @@ -318,16 +396,10 @@ int Actor::drawActors() { ActorOrderList::iterator actorDrawOrderIterator; ActorData *actor; - ActorIntentList::iterator actorIntentIterator; - ACTORINTENT *a_intent; - - ActorDialogList::iterator actorDialogIterator; - ACTORDIALOGUE *a_dialogue; int o_idx; //Orientation index int sprite_num; - int diag_x, diag_y; // dialog coordinates SURFACE *back_buf; @@ -350,198 +422,54 @@ int Actor::drawActors() { continue; } _vm->_sprite->drawOccluded(back_buf, actor->spriteList, sprite_num, actor->screenPosition, actor->screenScale, actor->screenDepth); - - // If actor's current intent is to speak, oblige him by - // displaying his dialogue - actorIntentIterator = actor->a_intentlist.begin(); - if (actorIntentIterator != actor->a_intentlist.end()) { - a_intent = actorIntentIterator.operator->(); - if (a_intent->a_itype == INTENT_SPEAK) { - actorDialogIterator = a_intent->si_diaglist.begin(); - if (actorDialogIterator != a_intent->si_diaglist.end()) { - a_dialogue = actorDialogIterator.operator->(); - diag_x = actor->screenPosition.x; - diag_y = actor->screenPosition.y; - diag_y -= ACTOR_DIALOGUE_HEIGHT; - _vm->textDraw(MEDIUM_FONT_ID, back_buf, a_dialogue->d_string, diag_x, diag_y, actor->speechColor, 0, - FONT_OUTLINE | FONT_CENTERED); - } - } - } } - return SUCCESS; -} - -// Called if the user wishes to skip a line of dialogue (spacebar in the -// original game). Will find all actors currently talking and remove one -// dialogue entry if there is a current speak intent present. +// draw speeches + if (isSpeaking() && !_vm->_script->_skipSpeeches) { + int i; + int textDrawFlags, speechColor; + Point speechCoord; + char oneChar[2]; + oneChar[1] = 0; + const char *outputString; -int Actor::skipDialogue() { - int i; - ActorData *actor; - - ActorIntentList::iterator actorIntentIterator; - ACTORINTENT *a_intent; - - ActorDialogList::iterator actorDialogIterator; - ACTORDIALOGUE *a_dialogue; - - for (i = 0; i < ACTORCOUNT; i++) { - actor = &_actors[i]; - if (actor->disabled) continue; - // Check the actor's current intent for a speak intent - actorIntentIterator = actor->a_intentlist.begin(); - if (actorIntentIterator != actor->a_intentlist.end()) { - a_intent = actorIntentIterator.operator->(); - if (a_intent->a_itype == INTENT_SPEAK) { - // Okay, found a speak intent. Remove one dialogue entry - // from it, releasing any semaphore */ - actorDialogIterator = a_intent->si_diaglist.begin(); - if (actorDialogIterator != a_intent->si_diaglist.end()) { - a_dialogue = actorDialogIterator.operator->(); - if (a_dialogue->d_sem != NULL) { - _vm->_script->SThreadReleaseSem(a_dialogue->d_sem); - } - a_intent->si_diaglist.erase(actorDialogIterator); - // And stop any currently playing voices - _vm->_sound->stopVoice(); - } - } - } - } - - return SUCCESS; -} - -void Actor::speak(uint16 actorId, const char *d_string, uint16 d_voice_rn, SEMAPHORE *sem) { - ActorData *actor; - ActorIntentList::iterator actorIntentIterator; - ACTORINTENT *a_intent_p = NULL; - ACTORINTENT a_intent; - int use_existing_ai = 0; - ACTORDIALOGUE a_dialogue; - - a_dialogue.d_string = d_string; - a_dialogue.d_voice_rn = d_voice_rn; - a_dialogue.d_time = getSpeechTime(d_string, d_voice_rn); - a_dialogue.d_sem_held = 1; - a_dialogue.d_sem = sem; - - actor = getActor(actorId); - - // If actor's last registered intent is to speak, we can queue the - // requested dialogue on that intent context; so examine the last - // intent - - actorIntentIterator = actor->a_intentlist.end(); - --actorIntentIterator; - if (actorIntentIterator != actor->a_intentlist.end()) { - a_intent_p = actorIntentIterator.operator->(); - if (a_intent_p->a_itype == INTENT_SPEAK) { - use_existing_ai = 1; + if (_activeSpeech.speechFlags & kSpeakSlow) { + outputString = oneChar; + oneChar[0] = _activeSpeech.strings[0][_activeSpeech.slowModeCharIndex]; + } else { + outputString = _activeSpeech.strings[0]; } - } - - if (use_existing_ai) { - // Store the current dialogue off the existing actor intent - a_intent_p->si_diaglist.push_back(a_dialogue); - } else { - // Create a new actor intent - a_intent.a_itype = INTENT_SPEAK; - a_intent.a_idone = 0; - a_intent.a_iflags = 0; - - a_intent.si_last_action = actor->action; - a_intent.si_diaglist.push_back(a_dialogue); - actor->a_intentlist.push_back(a_intent); - } - - if (sem != NULL) { - _vm->_script->SThreadHoldSem(sem); - } -} - -int Actor::handleSpeakIntent(ActorData *actor, ACTORINTENT *a_aintent, int *complete_p, int msec) { - ActorDialogList::iterator actorDialogIterator; - ActorDialogList::iterator nextActorDialogIterator; - ACTORDIALOGUE *a_dialogue; - ACTORDIALOGUE *a_dialogue2; - long carry_time; - int intent_complete = 0; - - if (!a_aintent->si_init) { - // Initialize speak intent by setting up action - actor->action = ACTION_SPEAK; - actor->action_frame = 0; - actor->action_time = 0; - actor->action_flags = ACTION_LOOP; - a_aintent->si_init = 1; - } - - // Process actor dialogue list - actorDialogIterator = a_aintent->si_diaglist.begin(); - if (actorDialogIterator != a_aintent->si_diaglist.end()) { - a_dialogue = actorDialogIterator.operator->(); - if (!a_dialogue->d_playing) { - // Dialogue voice hasn't played yet - play it now - _vm->_sndRes->playVoice(a_dialogue->d_voice_rn); - a_dialogue->d_playing = 1; + textDrawFlags = FONT_CENTERED; + if (_activeSpeech.outlineColor != 0) { + textDrawFlags |= FONT_OUTLINE; } - a_dialogue->d_time -= msec; - if (a_dialogue->d_time <= 0) { - // Dialogue time has expired; carry negative time to next - // dialogue entry if present, release any semaphores and - // delete the expired entry - - //actor->action = ACTION_IDLE; - - if (a_dialogue->d_sem != NULL) { - _vm->_script->SThreadReleaseSem(a_dialogue->d_sem); - } - - carry_time = a_dialogue->d_time; - - nextActorDialogIterator = actorDialogIterator; - ++nextActorDialogIterator; - if (nextActorDialogIterator != a_aintent->si_diaglist.end()) { - a_dialogue2 = nextActorDialogIterator.operator->(); - a_dialogue2->d_time -= carry_time; + if (_activeSpeech.actorIds[0] != 0) { + + for (i = 0; i < _activeSpeech.actorsCount; i++){ + actor = getActor(_activeSpeech.actorIds[i]); + speechCoord.x = actor->screenPosition.x; + speechCoord.y = actor->screenPosition.y; + speechCoord.y -= ACTOR_DIALOGUE_HEIGHT; + if (_activeSpeech.actorsCount > 1) + speechColor = actor->speechColor; + else + speechColor = _activeSpeech.speechColor; + + _vm->textDraw(MEDIUM_FONT_ID, back_buf, outputString, speechCoord.x, speechCoord.y, speechColor, _activeSpeech.outlineColor, textDrawFlags); } - // Check if there are any dialogue nodes left. If not, - // flag this speech intent as complete - - actorDialogIterator = a_aintent->si_diaglist.erase(actorDialogIterator); - if (actorDialogIterator == a_aintent->si_diaglist.end()) { - intent_complete = 1; - } + } else { // non actors speech + warning("non actors speech occures"); + //todo: write it } - } else { - intent_complete = 1; - } - if (intent_complete) { - *complete_p = 1; } return SUCCESS; } -int Actor::getSpeechTime(const char *d_string, uint16 d_voice_rn) { - int voice_len; - - voice_len = _vm->_sndRes->getVoiceLength(d_voice_rn); - - if (voice_len < 0) { - voice_len = strlen(d_string) * ACTOR_DIALOGUE_LETTERTIME; - } - - return voice_len; -} - void Actor::setOrientation(uint16 actorId, int orient) { ActorData *actor; @@ -605,7 +533,7 @@ void Actor::walkTo(uint16 actorId, const Point *walk_pt, uint16 flags, SEMAPHORE actor->a_intentlist.push_back(actor_intent); int is = actor->a_intentlist.size(); - debug(0, "actor->a_intentlist.size() %i", is); + debug(9, "actor->a_intentlist.size() %i", is); if (sem != NULL) { _vm->_script->SThreadHoldSem(sem); @@ -794,12 +722,80 @@ void Actor::moveRelative(uint16 actorId, const Point &movePoint) { actor->actorY += movePoint.y; } - void Actor::StoA(Point &actorPoint, const Point &screenPoint) { actorPoint.x = (screenPoint.x * ACTOR_LMULT); actorPoint.y = (screenPoint.y * ACTOR_LMULT); } +void Actor::actorSpeech(uint16 actorId, const char **strings, int stringsCount, uint16 sampleResourceId, int speechFlags) { + ActorData *actor; + int i; + + actor = getActor(actorId); + for (i = 0; i < stringsCount; i++) { + _activeSpeech.strings[i] = strings[i]; + } + _activeSpeech.stringsCount = stringsCount; + _activeSpeech.speechFlags = speechFlags; + _activeSpeech.actorsCount = 1; + _activeSpeech.actorIds[0] = actorId; + _activeSpeech.speechColor = actor->speechColor; + _activeSpeech.outlineColor = 15; // fixme - BLACK + _activeSpeech.sampleResourceId = sampleResourceId; + _activeSpeech.playing = false; + _activeSpeech.slowModeCharIndex = 0; +} + +void Actor::nonActorSpeech(const char **strings, int stringsCount, int speechFlags) { + int i; + + _vm->_script->wakeUpThreads(kWaitTypeSpeech); + + for (i = 0; i < stringsCount; i++) { + _activeSpeech.strings[i] = strings[i]; + } + _activeSpeech.stringsCount = stringsCount; + _activeSpeech.speechFlags = speechFlags; + _activeSpeech.actorsCount = 1; + _activeSpeech.actorIds[0] = 0; + //_activeSpeech.speechColor = ; + //_activeSpeech.outlineColor = ; + _activeSpeech.sampleResourceId = -1; + _activeSpeech.playing = false; + _activeSpeech.slowModeCharIndex = 0; +} + +void Actor::simulSpeech(const char *string, uint16 *actorIds, int actorIdsCount, int speechFlags) { + int i; + + for (i = 0; i < actorIdsCount; i++) { + _activeSpeech.actorIds[i] = actorIds[i]; + } + _activeSpeech.strings[0] = string; + _activeSpeech.stringsCount = 1; + _activeSpeech.speechFlags = speechFlags; + //_activeSpeech.speechColor = ; // get's from every actor + _activeSpeech.outlineColor = 0; // disable outline + _activeSpeech.sampleResourceId = -1; + _activeSpeech.playing = false; + _activeSpeech.slowModeCharIndex = 0; + + // caller should call thread->wait(kWaitTypeSpeech) by itself +} + +void Actor::abortAllSpeeches() { + if (_vm->_script->_abortEnabled) + _vm->_script->_skipSpeeches = true; + + for (int i = 0; i < 10; i++) + _vm->_script->executeThreads(0); +} + +void Actor::abortSpeech() { + _vm->_sound->stopVoice(); + _activeSpeech.playingTime = 0; +} + // Console wrappers - must be safe to run // TODO - checkup ALL arguments, cause wrong arguments may fall function with "error" diff --git a/saga/actor.h b/saga/actor.h index bb75e54f10..61f57662df 100644 --- a/saga/actor.h +++ b/saga/actor.h @@ -39,22 +39,46 @@ namespace Saga { #define ACTOR_ACTIONTIME 80 -#define ACTOR_DIALOGUE_LETTERTIME 50 #define ACTOR_DIALOGUE_HEIGHT 100 #define ACTOR_LMULT 4 #define ACTOR_ORIENTATION_COUNT 4 +#define ACTOR_SPEECH_STRING_MAX 16 +#define ACTOR_SPEECH_ACTORS_MAX 8 + #define IS_VALID_ACTOR_INDEX(index) ((index >= 0) && (index < ACTORCOUNT)) #define IS_VALID_ACTOR_ID(id) ((id == 1) || (id >= 0x2000) && (id < (0x2000 | ACTORCOUNT))) #define ACTOR_ID_TO_INDEX(id) ((((uint16)id) == 1 ) ? 0 : (int)(((uint16)id) & ~0x2000)) #define ACTOR_INDEX_TO_ID(index) ((((int)index) == 0 ) ? 1 : (uint16)(((int)index) | 0x2000)) + +enum ActorActions { + kActionWait = 0, + kActionWalkToPoint = 1, + kActionWalkToLink = 2, + kActionWalkDir = 3, + kActionSpeak = 4, + kActionAccept = 5, + kActionStoop = 6, + kActionLook = 7, + kActionCycleFrames = 8, + kActionPongFrames = 9, + kActionFreeze = 10, + kActionFall = 11, + kActionClimb = 12 +}; + +enum SpeechFlags { + kSpeakNoAnimate = 1, + kSpeakAsync = 2, + kSpeakSlow = 4 +}; + enum ACTOR_INTENTS { INTENT_NONE = 0, - INTENT_PATH = 1, - INTENT_SPEAK = 2 + INTENT_PATH = 1 }; enum ACTOR_WALKFLAGS { @@ -136,17 +160,6 @@ struct WALKINTENT { }; -struct ACTORDIALOGUE { - int d_playing; - const char *d_string; - uint16 d_voice_rn; - long d_time; - int d_sem_held; - SEMAPHORE *d_sem; - ACTORDIALOGUE() { memset(this, 0, sizeof(*this)); } -}; - -typedef Common::List ActorDialogList; struct ACTORINTENT { @@ -154,34 +167,27 @@ struct ACTORINTENT { uint16 a_iflags; int a_idone; - int si_init; - uint16 si_flags; - int si_last_action; - ActorDialogList si_diaglist; /* Actor dialogue list */ - WALKINTENT walkIntent; ACTORINTENT() { a_itype = 0; a_iflags = 0; a_idone = 0; - - si_init = 0; - si_flags = 0; - si_last_action = 0; } }; typedef Common::List ActorIntentList; + struct ActorData { bool disabled; // Actor disabled in init section int index; // Actor index uint16 actorId; // Actor id int nameIndex; // Actor's index in actor name string list byte speechColor; // Actor dialogue color - uint16 flags; // Actor flags + uint16 flags; // Actor initial flags + int sceneNumber; // scene of actor int actorX; // Actor's logical coordinates int actorY; // @@ -191,9 +197,12 @@ struct ActorData { int screenDepth; // int screenScale; // - int currentAction; - int facingDirection; + uint16 actorFlags; // dynamic flags + int currentAction; // ActorActions type + int facingDirection; // orientation int actionDirection; + int actionCycle; + int frameNumber; // current actor frame number SPRITELIST *spriteList; // Actor's sprite list data int spriteListResourceId; // Actor's sprite list resource id @@ -233,9 +242,6 @@ struct ActorData { index = 0; actorId = 0; nameIndex = 0; - currentAction = 0; - facingDirection = 0; - actionDirection = 0; speechColor = 0; frames = NULL; framesCount = 0; @@ -248,6 +254,11 @@ struct ActorData { actorY = 0; actorZ = 0; screenDepth = 0; + + actorFlags = 0; + currentAction = 0; + facingDirection = 0; + actionDirection = 0; idle_time = 0; orient = 0; @@ -270,6 +281,24 @@ struct ACTIONTIMES { int time; }; +struct SpeechData { + int speechColor; + int outlineColor; + int speechFlags; + const char *strings[ACTOR_SPEECH_STRING_MAX]; + int stringsCount; + int slowModeCharIndex; + uint16 actorIds[ACTOR_SPEECH_ACTORS_MAX]; + int actorsCount; + int sampleResourceId; + bool playing; + int playingTime; + + SpeechData() { + memset(this, 0, sizeof(*this)); + } +}; + class Actor { public: Actor(SagaEngine *vm); @@ -284,7 +313,6 @@ public: int drawActors(); void updateActorsScene(); // calls from scene loading to update Actors info - void AtoS(Point &screenPoint, const Point &actorPoint); void StoA(Point &actorPoint, const Point &screenPoint); void move(uint16 actorId, const Point &movePoint); @@ -292,15 +320,25 @@ public: void walkTo(uint16 actorId, const Point *walk_pt, uint16 flags, SEMAPHORE *sem); - void speak(uint16 actorId, const char *d_string, uint16 d_voice_rn, SEMAPHORE *sem); - int skipDialogue(); - int getSpeechTime(const char *d_string, uint16 d_voice_rn); void setOrientation(uint16 actorId, int orient); void setAction(uint16 actorId, int action_n, uint16 action_flags); void setDefaultAction(uint16 actorId, int action_n, uint16 action_flags); +// speech + void actorSpeech(uint16 actorId, const char **strings, int stringsCount, uint16 sampleResourceId, int speechFlags); + void nonActorSpeech(const char **strings, int stringsCount, int speechFlags); + void simulSpeech(const char *string, uint16 *actorIds, int actorIdsCount, int speechFlags); + void setSpeechColor(int speechColor, int outlineColor) { + _activeSpeech.speechColor = speechColor; + _activeSpeech.outlineColor = outlineColor; + } + void abortAllSpeeches(); + void abortSpeech(); + bool isSpeaking() { + return _activeSpeech.stringsCount > 0; + } private: int handleWalkIntent(ActorData *actor, WALKINTENT *a_walk_int, int *complete_p, int msec); @@ -308,15 +346,16 @@ private: int setPathNode(WALKINTENT *walk_int, const Point &src_pt, Point *dst_pt, SEMAPHORE *sem); ActorData *getActor(uint16 actorId); - bool loadActorResources(ActorData * actor); void createDrawOrderList(); + void handleSpeech(int msec); SagaEngine *_vm; RSCFILE_CONTEXT *_actorContext; ActorOrderList _drawOrderList; ActorData _actors[ACTORCOUNT]; + SpeechData _activeSpeech; }; } // End of namespace Saga diff --git a/saga/console.cpp b/saga/console.cpp index a70fb77108..b5ada851e1 100644 --- a/saga/console.cpp +++ b/saga/console.cpp @@ -86,6 +86,21 @@ Console::Console(SagaEngine *vm) : Common::Debugger() { Console::~Console() { } +int Console::DebugPrintf(const char *format, ...) { + int count; + va_list argptr; + + va_start(argptr, format); + + debug(1, format, argptr); + count = Common::Debugger::DebugPrintf(format); + + va_end (argptr); + + return count; +} + + void Console::preEnter() { } diff --git a/saga/console.h b/saga/console.h index 3dec60ef9a..c4b1ea34ed 100644 --- a/saga/console.h +++ b/saga/console.h @@ -35,6 +35,8 @@ public: Console(SagaEngine *vm); ~Console(void); + int DebugPrintf(const char *format, ...); + protected: virtual void preEnter(); virtual void postEnter(); diff --git a/saga/input.cpp b/saga/input.cpp index 77786a4ea7..0a162649c7 100644 --- a/saga/input.cpp +++ b/saga/input.cpp @@ -71,7 +71,7 @@ int SagaEngine::processInput() { // Actual game keys case 32: // space - _actor->skipDialogue(); + _actor->abortSpeech(); break; case 19: // pause case 'p': @@ -80,7 +80,7 @@ int SagaEngine::processInput() { case 27: // Esc // Skip to next scene skip target if (!_interface->getMode() == kPanelNone) // FIXME: hack - _script->SThreadAbortAll(); + _actor->abortAllSpeeches(); else _scene->skipScene(); break; diff --git a/saga/resnames.h b/saga/resnames.h index fb4bc0f3d3..c6b9b5977f 100644 --- a/saga/resnames.h +++ b/saga/resnames.h @@ -104,6 +104,11 @@ namespace Saga { #define CAVE_VOICE_12 12 #define CAVE_VOICE_13 13 +#define SCENE1_VOICE_009 57 +//TODO: fill it +#define SCENE1_VOICE_138 186 + + // MUSIC #define MUSIC_1 9 #define MUSIC_2 10 diff --git a/saga/saga.cpp b/saga/saga.cpp index 3e04e64a76..bb43f53451 100644 --- a/saga/saga.cpp +++ b/saga/saga.cpp @@ -248,7 +248,7 @@ int SagaEngine::go() { } _actor->direct(msec); _events->handleEvents(msec); - _script->SThreadExecThreads(msec); + _script->executeThreads(msec); } // Per frame processing _render->drawScene(); diff --git a/saga/saga.h b/saga/saga.h index 04b301eaba..1d89fb422b 100644 --- a/saga/saga.h +++ b/saga/saga.h @@ -74,15 +74,22 @@ enum ERRORCODE { enum SAGAGameId { GID_ITE, - GID_ITECD, GID_IHNM }; +enum scriptTimings { + kScriptTimeTicksPerSecond = (728L/10L), +}; + struct CLICKAREA { int n_points; Point *points; }; +inline int ticksToMSec(int tick) { + return tick * 1000 / kScriptTimeTicksPerSecond; +} + class SagaEngine : public Engine { void errorString(const char *buf_input, char *buf_output); diff --git a/saga/script.cpp b/saga/script.cpp index fe4efa1fe4..a8f6c11764 100644 --- a/saga/script.cpp +++ b/saga/script.cpp @@ -148,9 +148,9 @@ int Script::loadScript(int script_num) { byte *bytecode_p; size_t bytecode_len; uint32 scriptl_rn; - byte *diagl_p; - size_t diagl_len; - uint32 diagl_rn; + byte *stringsPointer; + size_t stringsLength; + uint32 stringsResourceId; byte *voicelut_p; size_t voicelut_len; uint32 voicelut_rn; @@ -174,14 +174,13 @@ int Script::loadScript(int script_num) { script_data = (SCRIPTDATA *)malloc(sizeof *script_data); if (script_data == NULL) { - warning("Memory allocation failed"); - return MEM; + error("Memory allocation failed"); } script_data->loaded = 0; // Initialize script pointers - script_data->diag = NULL; + script_data->strings = NULL; script_data->bytecode = NULL; script_data->voice = NULL; @@ -198,34 +197,21 @@ int Script::loadScript(int script_num) { script_data->bytecode = loadBytecode(bytecode_p, bytecode_len); if (script_data->bytecode == NULL) { - warning("Error interpreting script bytecode resource"); - free(script_data); - RSC_FreeResource(bytecode_p); - return FAILURE; + error("Error interpreting script bytecode resource"); } - // Load script dialogue list - diagl_rn = _scriptLUT[script_num].diag_list_rn; - - // Load dialogue list resource - result = RSC_LoadResource(_scriptContext, diagl_rn, &diagl_p, &diagl_len); - if (result != SUCCESS) { - warning("Error loading dialogue list resource"); - free(script_data); - RSC_FreeResource(bytecode_p); - return FAILURE; - } + // Load script strings list + stringsResourceId = _scriptLUT[script_num].diag_list_rn; - // Convert dialogue list resource to logical dialogue list - script_data->diag = loadDialogue(diagl_p, diagl_len); - if (script_data->diag == NULL) { - warning("Error interpreting dialogue list resource"); - free(script_data); - RSC_FreeResource(bytecode_p); - RSC_FreeResource(diagl_p); - return FAILURE; + // Load strings list resource + result = RSC_LoadResource(_scriptContext, stringsResourceId, &stringsPointer, &stringsLength); + if ((result != SUCCESS) || (stringsLength == 0)) { + error("Error loading strings list resource"); } - + + // Convert strings list resource to logical strings list + loadStrings(stringsPointer, stringsLength, script_data->strings); + // Load voice resource lookup table if (_voiceLUTPresent) { voicelut_rn = _scriptLUT[script_num].voice_lut_rn; @@ -233,22 +219,13 @@ int Script::loadScript(int script_num) { // Load voice LUT resource result = RSC_LoadResource(_scriptContext, voicelut_rn, &voicelut_p, &voicelut_len); if (result != SUCCESS) { - warning("Error loading voice LUT resource"); - free(script_data); - RSC_FreeResource(bytecode_p); - RSC_FreeResource(diagl_p); - return FAILURE; + error("Error loading voice LUT resource"); } // Convert voice LUT resource to logical voice LUT script_data->voice = loadVoiceLUT(voicelut_p, voicelut_len, script_data); if (script_data->voice == NULL) { - warning("Error interpreting voice LUT resource"); - free(script_data); - RSC_FreeResource(bytecode_p); - RSC_FreeResource(diagl_p); - RSC_FreeResource(voicelut_p); - return FAILURE; + error("Error interpreting voice LUT resource"); } } @@ -272,11 +249,10 @@ int Script::freeScript() { debug(0, "Releasing script data."); // Finish initialization - if (_currentScript->diag != NULL) { - free(_currentScript->diag->str); - free(_currentScript->diag->str_off); + if (_currentScript->strings != NULL) { + free(_currentScript->strings->strings); } - free(_currentScript->diag); + free(_currentScript->strings); if (_currentScript->bytecode != NULL) { free(_currentScript->bytecode->entrypoints); @@ -376,66 +352,54 @@ SCRIPT_BYTECODE *Script::loadBytecode(byte *bytecode_p, size_t bytecode_len) { return bc_new_data; } -// Reads a logical dialogue list from a dialogue list resource in memory. +const char *Script::getString(int index) { + if (_currentScript->strings->stringsCount <= index) + error("Script::getString wrong index 0x%X", index); + return _currentScript->strings->strings[index]; +} + +// Reads a logical strings list from a strings list resource in memory. // Returns NULL on failure. -DIALOGUE_LIST *Script::loadDialogue(const byte *dialogue_p, size_t dialogue_len) { - DIALOGUE_LIST *dialogue_list; - uint16 n_dialogue; +void Script::loadStrings(const byte *stringsPointer, size_t stringsLength, StringsList *&strings) { + uint16 stringsCount; uint16 i; size_t offset; - debug(0, "Loading dialogue list..."); + debug(9, "Loading strings list..."); // Allocate dialogue list structure - dialogue_list = (DIALOGUE_LIST *)malloc(sizeof *dialogue_list); - if (dialogue_list == NULL) { - return NULL; + strings = (StringsList *)malloc(sizeof *strings); + if (strings == NULL) { + error("No enough memory for strings list"); } - MemoryReadStreamEndian scriptS(dialogue_p, dialogue_len, IS_BIG_ENDIAN); + MemoryReadStreamEndian scriptS(stringsPointer, stringsLength, IS_BIG_ENDIAN); // First uint16 is the offset of the first string offset = scriptS.readUint16(); - if (offset > dialogue_len) { - warning("Error, invalid string offset"); - return NULL; + if (offset > stringsLength) { + error("Invalid string offset"); } // Calculate table length - n_dialogue = offset / 2; - dialogue_list->n_dialogue = n_dialogue; + stringsCount = offset / 2; + strings->stringsCount = stringsCount; // Allocate table of string pointers - dialogue_list->str = (const char **)malloc(n_dialogue * sizeof(const char *)); - if (dialogue_list->str == NULL) { - free(dialogue_list); - return NULL; - } - - // Allocate table of string offsets - dialogue_list->str_off = (size_t *)malloc(n_dialogue * sizeof(size_t)); - if (dialogue_list->str_off == NULL) { - free(dialogue_list->str); - free(dialogue_list); - return NULL; + strings->strings = (const char **)malloc(stringsCount * sizeof(const char *)); + if (strings->strings == NULL) { + error("No enough memory for strings list"); } - + // Read in tables from dialogue list resource scriptS.seek(0); - for (i = 0; i < n_dialogue; i++) { + for (i = 0; i < stringsCount; i++) { offset = scriptS.readUint16(); - if (offset > dialogue_len) { - warning("Error, invalid string offset"); - free(dialogue_list->str); - free(dialogue_list->str_off); - free(dialogue_list); - return NULL; + if (offset > stringsLength) { + error("invalid string offset"); } - dialogue_list->str[i] = (const char *)dialogue_p + offset; - dialogue_list->str_off[i] = offset; + strings->strings[i] = (const char *)stringsPointer + offset; } - - return dialogue_list; } // Reads a logical voice LUT from a voice LUT resource in memory. @@ -452,7 +416,7 @@ VOICE_LUT *Script::loadVoiceLUT(const byte *voicelut_p, size_t voicelut_len, SCR } n_voices = voicelut_len / 2; - if (n_voices != script->diag->n_dialogue) { + if (n_voices != script->strings->stringsCount) { warning("Error: Voice LUT entries do not match dialogue entries"); return NULL; } diff --git a/saga/script.h b/saga/script.h index 2e36dd87cb..0019c6a5a8 100644 --- a/saga/script.h +++ b/saga/script.h @@ -75,7 +75,7 @@ enum { kVarActor }; -enum { +enum ThreadFlags { kTFlagNone = 0, kTFlagWaiting = 1, // wait for even denoted in waitType kTFlagFinished = 2, @@ -83,20 +83,24 @@ enum { kTFlagAsleep = 7 // Combination of all flags which can halt a thread }; -enum { - kTWaitNone = 0, // waiting for nothing - kTWaitDelay, // waiting for a timer - kTWaitSpeech, // waiting for speech to finish - kTWaitDialogEnd, // waiting for my dialog to finish - kTWaitDialogBegin, // waiting for other dialog to finish - kTWaitWalk, // waiting to finish walking - kTWaitRequest, // a request is up - kTWaitPause +enum ThreadWaitTypes { + kWaitTypeNone = 0, // waiting for nothing + kWaitTypeDelay = 1, // waiting for a timer + kWaitTypeSpeech = 2, // waiting for speech to finish + kWaitTypeDialogEnd = 3, // waiting for my dialog to finish + kWaitTypeDialogBegin = 4, // waiting for other dialog to finish + kWaitTypeWalk = 5, // waiting to finish walking + kWaitTypeRequest = 6, // a request is up + kWaitTypePause = 7 +}; + +enum OpCodes { + opSpeak = 0x53 }; struct SCRIPT_THREAD { - int flags; - int waitType; + int flags; // ThreadFlags + int waitType; // ThreadWaitTypes uint sleepTime; int ep_num; // Entrypoint number @@ -135,6 +139,11 @@ struct SCRIPT_THREAD { assert(stackPtr < ARRAYSIZE(stackBuf)); return stackBuf[stackPtr++]; } + + void wait(int aWaitType) { + waitType = aWaitType; + flags |= kTFlagWaiting; + } SCRIPT_THREAD() { memset(this, 0, sizeof(*this)); } }; @@ -154,10 +163,9 @@ struct SCRIPT_BYTECODE { PROC_TBLENTRY *entrypoints; }; -struct DIALOGUE_LIST { - unsigned int n_dialogue; - const char **str; - size_t *str_off; +struct StringsList { + int stringsCount; + const char **strings; }; struct VOICE_LUT { @@ -168,7 +176,7 @@ struct VOICE_LUT { struct SCRIPTDATA { int loaded; SCRIPT_BYTECODE *bytecode; - DIALOGUE_LIST *diag; + StringsList *strings; VOICE_LUT *voice; }; @@ -195,16 +203,16 @@ public: int loadScript(int scriptNum); int freeScript(); SCRIPT_BYTECODE *loadBytecode(byte *bytecode_p, size_t bytecode_len); - DIALOGUE_LIST *loadDialogue(const byte *dialogue_p, size_t dialogue_len); + void loadStrings(const byte *stringsList, size_t stringsLength, StringsList *&strings); VOICE_LUT *loadVoiceLUT(const byte *voicelut_p, size_t voicelut_len, SCRIPTDATA *script); - int disassemble(SCRIPT_BYTECODE *script_list, DIALOGUE_LIST *diag_list); + int disassemble(SCRIPT_BYTECODE *script_list, StringsList *strings); bool isInitialized() const { return _initialized; } bool isVoiceLUTPresent() const { return _voiceLUTPresent; } SCRIPTDATA *currentScript() { return _currentScript; } void setBuffer(int idx, SCRIPT_DATABUF *ptr) { _dataBuf[idx] = ptr; } SCRIPT_DATABUF *dataBuffer(int idx) { return _dataBuf[idx]; } -// YS_DL_LIST *threadList() { return _threadList; } + const char *getString(int index); void scriptInfo(); void scriptExec(int argc, const char **argv); @@ -220,10 +228,11 @@ protected: SCRIPT_DATABUF *_dataBuf[SCRIPT_DATABUF_NUM]; ScriptThreadList _threadList; + +public: bool _skipSpeeches; bool _abortEnabled; -public: int _dbg_singlestep; int _dbg_dostep; SCRIPT_THREAD *_dbg_thread; @@ -232,13 +241,15 @@ public: public: SCRIPT_THREAD *SThreadCreate(); int SThreadExecute(SCRIPT_THREAD *thread, int ep_num); - int SThreadExecThreads(uint msec); + int executeThreads(uint msec); int SThreadHoldSem(SEMAPHORE *sem); int SThreadReleaseSem(SEMAPHORE *sem); int SThreadDebugStep(); void SThreadCompleteThread(void); int SThreadDestroy(SCRIPT_THREAD *thread); - void SThreadAbortAll(void); + + void wakeUpThreads(int waitType); + void wakeUpThreadsDelayed(int waitType, int sleepTime); private: void setFramePtr(SCRIPT_THREAD *thread, int newPtr); diff --git a/saga/sdebug.cpp b/saga/sdebug.cpp index 51ddab4305..14c2e3654a 100644 --- a/saga/sdebug.cpp +++ b/saga/sdebug.cpp @@ -453,19 +453,19 @@ int Script::SDebugPrintInstr(SCRIPT_THREAD *thread) { case 0x46: SD_ADDTXT("LXOR |"); break; - case 0x53: + case opSpeak: { - int n_voices; - int param1; - int param2; - - SD_ADDTXT("DLGP | "); - n_voices = readS.readByte(); - param1 = readS.readUint16LE(); - param2 = readS.readByte(); + int stringsCount; + uint16 actorId; + int speechFlags; + + SD_ADDTXT("opSpeak | "); + stringsCount = readS.readByte(); + actorId = readS.readUint16LE(); + speechFlags = readS.readByte(); // ignored ? readS.readUint16LE(); - sprintf(tmp_buf, "%02X %04X %02X", n_voices, param1, param2); + sprintf(tmp_buf, "%02X %04X %02X", stringsCount, actorId, speechFlags); SD_ADDTXT(tmp_buf); } break; diff --git a/saga/sfuncs.cpp b/saga/sfuncs.cpp index d00ab6e8a8..2636214677 100644 --- a/saga/sfuncs.cpp +++ b/saga/sfuncs.cpp @@ -133,7 +133,7 @@ void Script::setupScriptFuncList(void) { int Script::SF_putString(SCRIPTFUNC_PARAMS) { SDataWord_T param = thread->pop(); - debug(1, currentScript()->diag->str[param]); + _vm->_console->DebugPrintf(getString(param)); return SUCCESS; } @@ -146,9 +146,8 @@ int Script::SF_sleep(SCRIPTFUNC_PARAMS) { if (!_skipSpeeches) { time_param = thread->pop(); time = _vm->_sdata->readWordU(time_param); - time = time * 10; // 72.8 ticks per second - thread->flags |= kTFlagWaiting; // put thread to sleep - thread->waitType = kTWaitDelay; + thread->wait(kWaitTypeDelay); // put thread to sleep + thread->sleepTime = ticksToMSec(time); } return SUCCESS; } @@ -191,7 +190,7 @@ int Script::SF_objectIsCarried(SCRIPTFUNC_PARAMS) { int Script::SF_setStatusText(SCRIPTFUNC_PARAMS) { SDataWord_T param = thread->pop(); - return _vm->_interface->setStatusText(currentScript()->diag->str[param]); + return _vm->_interface->setStatusText(getString(param)); } // Script function #5 (0x05) diff --git a/saga/sthread.cpp b/saga/sthread.cpp index cca11ec39e..2eebfb0c08 100644 --- a/saga/sthread.cpp +++ b/saga/sthread.cpp @@ -33,6 +33,8 @@ #include "saga/sdata.h" #include "saga/game_mod.h" #include "saga/stream.h" +#include "saga/scene.h" +#include "saga/resnames.h" namespace Saga { @@ -55,7 +57,7 @@ SCRIPT_THREAD *Script::SThreadCreate() { setFramePtr(new_thread, new_thread->stackPtr); new_thread->flags = kTFlagWaiting; - new_thread->waitType = kTWaitPause; + new_thread->waitType = kWaitTypePause; dataBuffer(4)->len = ARRAYSIZE(new_thread->threadVars); dataBuffer(4)->data = new_thread->threadVars; @@ -63,46 +65,67 @@ SCRIPT_THREAD *Script::SThreadCreate() { return new_thread; } +void Script::wakeUpThreads(int waitType) { + SCRIPT_THREAD *thread; + ScriptThreadList::iterator threadIterator; + + for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) { + thread = threadIterator.operator->(); + if ((thread->flags & kTFlagWaiting) && (thread->waitType == waitType)) { + thread->flags &= ~kTFlagWaiting; + } + } +} -int Script::SThreadExecThreads(uint msec) { +void Script::wakeUpThreadsDelayed(int waitType, int sleepTime) { SCRIPT_THREAD *thread; + ScriptThreadList::iterator threadIterator; + + for (threadIterator = _threadList.begin(); threadIterator != _threadList.end(); ++threadIterator) { + thread = threadIterator.operator->(); + if ((thread->flags & kTFlagWaiting) && (thread->waitType == waitType)) { + thread->waitType = kWaitTypeDelay; + thread->sleepTime = sleepTime; + } + } +} + +int Script::executeThreads(uint msec) { + SCRIPT_THREAD *thread; + ScriptThreadList::iterator threadIterator; if (!isInitialized()) { return FAILURE; } - ScriptThreadList::iterator threadi = _threadList.begin(); + threadIterator = _threadList.begin(); - while (threadi != _threadList.end()) { - thread = (SCRIPT_THREAD *)threadi.operator->(); + while (threadIterator != _threadList.end()) { + thread = threadIterator.operator->(); if (thread->flags & (kTFlagFinished | kTFlagAborted)) { //if (thread->flags & kTFlagFinished) // FIXME. Missing function - threadi = _threadList.erase(threadi); + threadIterator = _threadList.erase(threadIterator); continue; } - if (thread->flags & kTFlagWaiting) { - switch(thread->waitType) { - case kTWaitDelay: - if (thread->sleepTime < msec) { - thread->sleepTime = 0; - } else { - thread->sleepTime -= msec; - } - - if (thread->sleepTime == 0) - thread->flags &= ~kTFlagWaiting; - break; + if ((thread->flags & kTFlagWaiting) && (thread->waitType == kWaitTypeDelay)) { + if (thread->sleepTime < msec) { + thread->sleepTime = 0; + } else { + thread->sleepTime -= msec; } + + if (thread->sleepTime == 0) + thread->flags &= ~kTFlagWaiting; } if (!(thread->flags & kTFlagWaiting)) SThreadRun(thread, STHREAD_TIMESLICE); - ++threadi; + ++threadIterator; } return SUCCESS; @@ -110,7 +133,7 @@ int Script::SThreadExecThreads(uint msec) { void Script::SThreadCompleteThread(void) { for (int i = 0; i < 40 && !_threadList.isEmpty() ; i++) - SThreadExecThreads(0); + executeThreads(0); } int Script::SThreadSetEntrypoint(SCRIPT_THREAD *thread, int ep_num) { @@ -147,15 +170,7 @@ int Script::SThreadExecute(SCRIPT_THREAD *thread, int ep_num) { return SUCCESS; } -void Script::SThreadAbortAll(void) { - // TODO: stop current speech - if (_abortEnabled) - _skipSpeeches = true; - - for (int i = 0; i < 10; i++) - _vm->_script->SThreadExecThreads(0); -} unsigned char *Script::SThreadGetReadPtr(SCRIPT_THREAD *thread) { return currentScript()->bytecode->bytecode_p + thread->i_offset; @@ -215,7 +230,7 @@ int Script::SThreadRun(SCRIPT_THREAD *thread, int instr_limit) { int debug_print = 0; int n_buf; int bitstate; - int in_char; + int operandChar; int i; int unhandled = 0; @@ -239,14 +254,17 @@ int Script::SThreadRun(SCRIPT_THREAD *thread, int instr_limit) { scriptS.seek(thread->i_offset); for (instr_count = 0; instr_count < instr_limit; instr_count++) { - if (thread->sem.hold_count) + if (thread->flags & kTFlagWaiting) + break; + + if (thread->sem.hold_count) //TODO: remove it break; saved_offset = thread->i_offset; - in_char = scriptS.readByte(); + operandChar = scriptS.readByte(); - debug(2, "Executing thread offset: %lu (%x) stack: %d", thread->i_offset, in_char, thread->stackSize()); - switch (in_char) { + debug(2, "Executing thread offset: %lu (%x) stack: %d", thread->i_offset, operandChar, thread->stackSize()); + switch (operandChar) { case 0x01: // nextblock // Some sort of "jump to the start of the next memory // page" instruction, I think. @@ -355,7 +373,6 @@ int Script::SThreadRun(SCRIPT_THREAD *thread, int instr_limit) { if (func_num >= SFUNC_NUM) { _vm->_console->DebugPrintf(S_ERROR_PREFIX "Invalid script function number: (%X)\n", func_num); thread->flags |= kTFlagAborted; - debug( 9, "Invalid script function number: (%X)\n", func_num); break; } @@ -363,7 +380,6 @@ int Script::SThreadRun(SCRIPT_THREAD *thread, int instr_limit) { sfuncRetVal = (this->*sfunc)(thread, n_args); if (sfuncRetVal != SUCCESS) { _vm->_console->DebugPrintf(S_WARN_PREFIX "%X: Script function %d failed.\n", thread->i_offset, func_num); - debug( 9, "%X: Script function %d failed.\n", thread->i_offset, func_num); } if (func_num == 16) { // SF_gotoScene @@ -371,7 +387,7 @@ int Script::SThreadRun(SCRIPT_THREAD *thread, int instr_limit) { break; } - if (in_char == 0x18) // CALL function + if (operandChar == 0x18) // CALL function thread->push(thread->retVal); if (thread->flags & kTFlagAsleep) @@ -393,11 +409,10 @@ int Script::SThreadRun(SCRIPT_THREAD *thread, int instr_limit) { if (thread->stackSize() == 0) { _vm->_console->DebugPrintf("Script execution complete.\n"); thread->flags |= kTFlagFinished; - debug( 9, "Script execution complete.\n"); } else { thread->i_offset = thread->pop(); /* int n_args = */ thread->pop(); - if (in_char == 0x1B) + if (operandChar == 0x1B) thread->push(scriptRetVal); } break; @@ -493,7 +508,6 @@ int Script::SThreadRun(SCRIPT_THREAD *thread, int instr_limit) { } if (!branch_found) { _vm->_console->DebugPrintf(S_ERROR_PREFIX "%X: Random jump target out of bounds.\n", thread->i_offset); - debug(9, "%X: Random jump target out of bounds.\n", thread->i_offset); } } break; @@ -716,29 +730,55 @@ int Script::SThreadRun(SCRIPT_THREAD *thread, int instr_limit) { // GAME INSTRUCTIONS - // (DLGP): Play Character Dialogue - case 0x53: + // (opSpeak): Play Character Speech + case opSpeak: { - int n_voices; + int stringsCount; uint16 actorId; - int voice_rn; - - n_voices = scriptS.readByte(); - param1 = (SDataWord_T) scriptS.readUint16LE(); - // ignored ? - scriptS.readByte(); - scriptS.readUint16LE(); + int speechFlags; + int sampleResourceId = -1; + int first; + const char *strings[ACTOR_SPEECH_STRING_MAX]; + + if (_vm->_actor->isSpeaking()) { + thread->wait(kWaitTypeSpeech); + return SUCCESS; + } - actorId = param1; + stringsCount = scriptS.readByte(); + actorId = scriptS.readUint16LE(); + speechFlags = scriptS.readByte(); + scriptS.readUint16LE(); // x,y skip + + if (stringsCount == 0) + error("opSpeak stringsCount == 0"); + + if (stringsCount >= ACTOR_SPEECH_STRING_MAX) + error("opSpeak stringsCount=0x%X exceed ACTOR_SPEECH_STRING_MAX", stringsCount); + + data = first = thread->stackTop(); + for (i = 0; i < stringsCount; i++) { + data = thread->pop(); + strings[i] = getString(data); + } + // now data contains last string index - for (i = 0; i < n_voices; i++) { - data = thread->pop(); - if (!isVoiceLUTPresent()) { - voice_rn = -1; - } else { - voice_rn = currentScript()->voice->voices[data]; + if (GAME_GetGame() == GAME_ITE_DISK) { // special ITE dos + if ((_vm->_scene->currentSceneNumber() == ITE_DEFAULT_SCENE) && (data >= 288) && (data <= (SCENE1_VOICE_138 - SCENE1_VOICE_009 + 288))) { + sampleResourceId = SCENE1_VOICE_009 + data - 288; + } + } else { + if (isVoiceLUTPresent()) { + if (currentScript()->voice->n_voices > first) { + sampleResourceId = currentScript()->voice->voices[first]; + } } - _vm->_actor->speak(actorId, currentScript()->diag->str[data], voice_rn, &thread->sem); + } + + _vm->_actor->actorSpeech(actorId, strings, stringsCount, sampleResourceId, speechFlags); + + if (!(speechFlags & kSpeakAsync)) { + thread->wait(kWaitTypeSpeech); } } break; @@ -772,10 +812,8 @@ int Script::SThreadRun(SCRIPT_THREAD *thread, int instr_limit) { // End instruction list default: - - _vm->_console->DebugPrintf(S_ERROR_PREFIX "%X: Invalid opcode encountered: (%X).\n", thread->i_offset, in_char); + _vm->_console->DebugPrintf(S_ERROR_PREFIX "%X: Invalid opcode encountered: (%X).\n", thread->i_offset, operandChar); thread->flags |= kTFlagAborted; - debug(9, "%X: Invalid opcode encountered: (%X).\n", thread->i_offset, in_char); break; } @@ -786,7 +824,6 @@ int Script::SThreadRun(SCRIPT_THREAD *thread, int instr_limit) { if (thread->i_offset >= scriptS.size()) { _vm->_console->DebugPrintf("Out of range script execution at %x size %x\n", thread->i_offset, scriptS.size()); thread->flags |= kTFlagFinished; - debug(9, "Out of range script execution at %x size %x\n", thread->i_offset, scriptS.size()); } else scriptS.seek(thread->i_offset); @@ -794,7 +831,6 @@ int Script::SThreadRun(SCRIPT_THREAD *thread, int instr_limit) { if (unhandled) { _vm->_console->DebugPrintf(S_ERROR_PREFIX "%X: Unhandled opcode.\n", thread->i_offset); thread->flags |= kTFlagAborted; - debug(9, "%X: Unhandled opcode.\n", thread->i_offset); } if ((thread->flags == kTFlagNone) && debug_print) { SDebugPrintInstr(thread); diff --git a/saga/xref.txt b/saga/xref.txt index 6f236ab4d2..ae55958ae4 100644 --- a/saga/xref.txt +++ b/saga/xref.txt @@ -68,7 +68,7 @@ Scene.c Interp.c ======== - dispatchThreads() SThreadExecThreads() + dispatchThreads() executeThreads() runThread() SThreadCompleteThread() moduleList _scriptLUT ModuleEntry->codeID _scriptLUT->script_rn @@ -90,7 +90,7 @@ Actor.h Actor.c ======= - abortAllSpeeches() SThreadAbortAll() + abortAllSpeeches() abortAllSpeeches() Main.c ====== -- cgit v1.2.3