please trust me when i say it is not feasible to do that.
anyway, i dug into my disassembly of dromed v1.27, and located a number of the relevant functions and their arguments, then attached visual studio's debugger to it with breakpoints configured to print out messages and keep running. here is the result; the window in the top right is the output from the breakpoint messages; the complete log is below:
calls correspond to the output you see from ai_sound_watch where it requests a concept to play—only i did not have the various "did not speak because..." bits labelled. "speakerObjID 3" is the archer.
are the sound api, showing the schema id, the sample number (if the schema has multiple samples), and the actual chosen sample (e.g. wav) file name.
are the callbacks that the face motions code uses to know a speech line starts and stops. the "Talking" flag in the FaceState property is set and cleared by these callbacks.
lines and those immediately following are from looking up facepos.str to find the animation string.
lines are from the function that runs every frame to set the face bitmap, when it reaches the end of the facepos string.
Code:
[Archer clears his throat]
at 10: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 10: SchemaIDPlay( schemaID:-4291, ... )
at 10: sampleNum=10
at 10: SchemaSamplePlay( schemaID:-4291, num:10, ... )
at 10: sampleName=0x0d00eb70 "sg4a0th2"
[the start callback fires successfully]
at 10: SpeechStartCB( speakerID:3, hSchema:216219160, schemaID:-4291 )
at 10: FetchMouthString( 0x0cfdd904 "sg4a0th2" )
at 10: got string.
at 10: do we have facepos str?
at 10: found facepos str: 0x140ee8a0 "22000220222100000002210000"
at 2011: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 4013: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
[The sound finishes; FaceState->Talking is turned off]
at 5645: end of string.
[the end callback fails to fire!]
at 6027: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 8030: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 10038: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 12039: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 14045: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
[Archer mutters to himself]
at 16056: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 17168: SchemaSamplePlay( schemaID:-4291, num:7, ... )
at 17168: sampleName=0x0d00e6a8 "sg4a0mu3"
[the start callback fails to fire! (probably because the end callback above didnt happen)]
at 18060: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 20077: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 22092: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 24099: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 26100: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 28115: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 30122: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 32128: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 34135: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 36152: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 38157: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 40169: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 42184: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
[I enable ai_aware_of_player, and the archer sees me]
at 43147: SpeechSpeak( speakerObjID:3, pConcept:0x00a05308 "spotplayer", ... )
[now that the idle broadcasts are ending, the end callback fires at last? but why only now? ...]
at 43147: SpeechEndCB( speakerID:3, hSchema:216219160, schemaID:-4291 )
at 43147: SchemaIDPlay( schemaID:-3961, ... )
at 43147: sampleNum=9
at 43147: SchemaSamplePlay( schemaID:-3961, num:9, ... )
at 43147: sampleName=0x0cf80fc8 "sg4a3na5"
[...and now that the end callback fired just above, we get a start callback again]
at 43147: SpeechStartCB( speakerID:3, hSchema:216219368, schemaID:-3961 )
at 43147: FetchMouthString( 0x0cfddafc "sg4a3na5" )
at 43147: got string.
at 43147: do we have facepos str?
at 43147: found facepos str: 0x140f2180 "242422112214211221112212210000"
at 45025: end of string.
[...and when this line ends, we *do* get the end callback firing]
at 45224: SpeechEndCB( speakerID:3, hSchema:216219368, schemaID:-3961 )
[we see the same pattern with this combat line: successful start and end callbacks.]
at 45485: SpeechSpeak( speakerObjID:3, pConcept:0x00a053a8 "reactshoot", ... )
at 45485: SchemaIDPlay( schemaID:-3973, ... )
at 45485: sampleNum=0
at 45485: SchemaSamplePlay( schemaID:-3973, num:0, ... )
at 45485: sampleName=0x0ce4cd98 "sg4atb_1"
at 45485: SpeechStartCB( speakerID:3, hSchema:216219160, schemaID:-3973 )
at 45485: FetchMouthString( 0x0cfde18c "sg4atb_1" )
at 45485: got string.
at 45485: do we have facepos str?
at 45485: found facepos str: 0x142342e8 "2114441244244111421121442210000"
at 45485: SchemaIDPlay( schemaID:-6602, ... )
at 45485: sampleNum=1
at 45485: SchemaSamplePlay( schemaID:-6602, num:1, ... )
at 45485: sampleName=0x0a57c368 "bow_ai2"
at 46262: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 47434: end of string.
at 47718: SpeechEndCB( speakerID:3, hSchema:216219160, schemaID:-3973 )
[at this point i disabled ai_aware_of_player and made the ai forget]
at 48269: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 48269: SchemaIDPlay( schemaID:-4291, ... )
at 48269: sampleNum=5
at 48269: SchemaSamplePlay( schemaID:-4291, num:5, ... )
at 48269: sampleName=0x0d00e678 "sg4a0mu1"
[just like right at the start, we get a start callback for the idle broadcast]
at 48269: SpeechStartCB( speakerID:3, hSchema:216219160, schemaID:-4291 )
at 48269: FetchMouthString( 0x0cfde4d4 "sg4a0mu1" )
at 48269: got string.
at 48269: do we have facepos str?
at 48269: found facepos str: 0x0cf743d0 "02222122111122111000002112110000000000000000011222222222111000000111222111102021222022124211111212222222211110000011220222011210222211111111111221112211000000000000012020111122222201212122222222222222...
at 50280: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 52290: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
[here the idle broadcast is interrupted by the guard's awareness timing out]
at 52516: SpeechSpeak( speakerObjID:3, pConcept:0x00a05358 "lostcontact", ... )
[the end callback for the idle broadcast fires successfully]
at 52516: SpeechEndCB( speakerID:3, hSchema:216219160, schemaID:-4291 )
at 52516: SchemaIDPlay( schemaID:-3965, ... )
at 52516: sampleNum=3
at 52516: SchemaSamplePlay( schemaID:-3965, num:3, ... )
at 52516: sampleName=0x0ce2a720 "sg4los_4"
[the start callback fires successfully]
at 52516: SpeechStartCB( speakerID:3, hSchema:216219160, schemaID:-3965 )
at 52516: FetchMouthString( 0x0cfddf94 "sg4los_4" )
at 52516: got string.
at 52516: do we have facepos str?
at 52516: found facepos str: 0x1419a0d0 "144441114411411110000"
at 53833: end of string.
[the end callback fires successfully when the lostcontact line finishes]
at 54014: SpeechEndCB( speakerID:3, hSchema:216219160, schemaID:-3965 )
[and we are back to idle broadcasts. the guard clears his throat]
at 54296: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 54296: SchemaIDPlay( schemaID:-4291, ... )
at 54296: sampleNum=13
at 54296: SchemaSamplePlay( schemaID:-4291, num:13, ... )
at 54296: sampleName=0x0d00e930 "sg4a0th5"
[just like at the very start, we get a start callback...]
at 54296: SpeechStartCB( speakerID:3, hSchema:216219576, schemaID:-4291 )
at 54296: FetchMouthString( 0x0cfde4d4 "sg4a0th5" )
at 54296: got string.
at 54296: do we have facepos str?
at 54296: found facepos str: 0x1419a1b0 "4412444241111100000"
at 56298: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 58309: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 59488: end of string.
[...but not an end callback! this is suspicious]
at 60322: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 62334: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 64338: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 66347: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 68358: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 70366: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 72376: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 74383: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 76395: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
[...and the next idle broadcast that happens has no callbacks at all, again just like the muttering near the start]
at 76589: SchemaSamplePlay( schemaID:-4291, num:2, ... )
at 76589: sampleName=0x0d00e618 "sg4a0wh3"
at 78399: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 80402: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 82407: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 84413: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
at 86427: SpeechSpeak( speakerObjID:3, pConcept:0x00a051f0 "atlevelzero", ... )
[finally, when i end game mode, the speech end callback fires!]
at 0: SpeechEndCB( speakerID:3, hSchema:216219576, schemaID:-4291 )
note that this debugging session did not show me anything about the mouth-flapping; it did not happen once in my testing. i did try a few runs with two guards, in case that was related, but it still did not happen; and after that i reverted to a single guard so the log above would not be too confusing. if i can find a reliable way to get the mouth-flapping happening, i will do another debug session to investigate that.
it is possible i never saw the mouth-flapping because of how i generated facepos.str: i had added the Face Motions property to the "Schema" archetype so that every schema-driven sound, speech or not, was in the file. facepos.str immediately after generating had 7947 lines; but after i removed duplicates, it was down to 6459 lines. but it is also simply possible i did not encounter the conditions in which it happens; notably i do not have any conversations in this test missions.
as for the log above: what is happening here with the mouth not moving has suddenly become clear to me while i wrote up this post! see if you can spot it. here is a clue, the schema definition for -4291:
anyway, i will take that sudden realisation and dig into the code some more to get a sense of how fixable this might be.