aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Kurushin2004-12-21 06:49:07 +0000
committerAndrew Kurushin2004-12-21 06:49:07 +0000
commitea3b0d11003dbf297b99d22905363df0b24c2f85 (patch)
tree325c5e37fdd246302d529af0d7dcab0671ab4508
parentad0b8f1de57e0c217831a60c6d0c93f2d22b2cde (diff)
downloadscummvm-rg350-ea3b0d11003dbf297b99d22905363df0b24c2f85.tar.gz
scummvm-rg350-ea3b0d11003dbf297b99d22905363df0b24c2f85.tar.bz2
scummvm-rg350-ea3b0d11003dbf297b99d22905363df0b24c2f85.zip
- 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
-rw-r--r--saga/actor.cpp392
-rw-r--r--saga/actor.h107
-rw-r--r--saga/console.cpp15
-rw-r--r--saga/console.h2
-rw-r--r--saga/input.cpp4
-rw-r--r--saga/resnames.h5
-rw-r--r--saga/saga.cpp2
-rw-r--r--saga/saga.h9
-rw-r--r--saga/script.cpp132
-rw-r--r--saga/script.h57
-rw-r--r--saga/sdebug.cpp20
-rw-r--r--saga/sfuncs.cpp9
-rw-r--r--saga/sthread.cpp160
-rw-r--r--saga/xref.txt4
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<ACTORDIALOGUE> 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<ACTORINTENT> 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::~Console() {
}
+int Console::DebugPrintf(const char *format, ...) {
+ int count;
+ va_list argptr;
+
+ va_start(argptr, format);
+
+ debug(1, format, argptr);
+ count = Common::Debugger<Console>::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
======