Skip to content

Several dialog & subtitle improvements in the way Scenes / Cutscenes work in a party - #854

Merged
miredirex merged 3 commits into
tiltedphoques:devfrom
rfortier:fix/Scene-dialog-subtitle-sync
Oct 16, 2026
Merged

miredirex merged 3 commits into
tiltedphoques:devfrom
rfortier:fix/Scene-dialog-subtitle-sync

Conversation

@rfortier

@rfortier rfortier commented Feb 5, 2026 •

Copy link
Copy Markdown
Contributor

…work in a party.

Updated PR with a new approach that syncs better. Turned down all the logging for release.

If the cutscene design is that all party members (or all inrange) play the scene, the Leader's dialog sync would step on and cancel the copies running in the party members. This would mess up the scene phase timing, causing the members to race ahead and abort actions of the Leader, potentially hanging the scene. Found with the Finn/Carbos scene in Enderal.

If the cutscene design is only the person(s) who trigger the scene play the scene, and the scene is triggered by a non-Leader, no one except the scene-initiator got any dialog. This is an uncommon case; usually the start of a scene triggers a quest stage change that brings everyone into the scene, or the scene is just random encounter / idle chatter that is just fine to sync to just the triggering player.

Because scenes that don't trigger quest stage changes but also would be desirable to sync are rare, and because there is no good way to distinguish them from scenes where syncing the audio will cause dialog cancels and timing problems, this case is left as unsolved, have to live with it.

There is some logic in the quest sync PR #848 to trigger scenes in the rest of the party when one starts the scene. This solves some cases. In others, there are scene start conditions that just stop the scene again. Best known example is the Companions Brotherhood quest stage C02. Unless the entire party stands on the tiny hotspot where the initiator triggers the scene, it just stops again.

It's not perfect, but the selected (surviving experiment) solution is:

  1. Try to trigger the scene for the entire party, which often works.
  2. Each party member plays the scene to themselves, generally with decent audio sync. The party Leader is still controlling the NPCs and motion / animation sync handles most cases.
  3. If the scene triggers TaskDialogues (those NPC dialogues where you answer with a menu pick), that dialog is individual to the SceneMaster (they who triggered), so that is synced to party (PlayersInRange).
  4. Only flaw known is AI packages.

Subtitle sync follows the same rules as Dialogue sync.

AI package handling is left for a future iteration, since it also is tricky. Current state: AI packages only run on Leader, so if Member triggers a scene, the AI packages doesn't run. If it is anything of consequence (more than "look at player", say, having to do something and then interact with player), the scene will get stuck and Leader will have to take over. If Leader is close, NPC will just switch to Leader. If farther away or not in scene, Leader will have to get closer, possibly having to unlock the Leader with the EPC (EnablePlayerControls) console command.

For the next round, the two big options I see for fixing AI is for

  1. SceneMaster to take ownership of NPCs in-scene. They can be released at end of scene, or just wait for a cell change. This will solve the scene getting stuck problem, but not the dialog sync problem.
  2. Find a way to reactivate AI on "remote" NPCs for the duration of the scene, This sounds more fragile, but since everyone is playing the scene again, dialogue will sync.
  3. Switch model to "everyone listens to SceneMaster." This combines the first option with changing the dialog sync so the scene ONLY plays on the SceneMaster, and diaglog / motion / animation is synced as usual from the NPC owner. This one is fragile because the way Skyrim works the scene is often started on all players, and trying to stop / interrupt it is sure to cause new issues.

Thanks to @miredirex and @powerof3 CommonLibSSE for help with some of the RE:: trickery.

This was extracted from a bigger PR #848 in the works, so draft until we get a bit more mileage using this version.

@rfortier
rfortier force-pushed the fix/Scene-dialog-subtitle-sync branch 2 times, most recently from b92c4dc to 46034d1 Compare February 7, 2026 02:00
@rfortier

rfortier commented Feb 7, 2026

Copy link
Copy Markdown
Contributor Author

Updated with improved dialogue and subtitle sync.

In particular, there is an engine bug or something in our hooking / RE that causes HookSpeakSoundFunction() to be invoked twice for most spoken lines, about 5-10ms apart and on separate threads. Looks like it is probably a Bethesda bug.

But it caused us to send DialogueEvent twice, too, trying to sync twice when relevant. Added a non-pretty fix to watch for duplicates within 20ms, and drop the duplicate send of DialogueEvent. That's the only functional change. It still calls RealSpeakSoundFunction both times; didn't want to change that behavior because the engine might actually depend on it.

@rfortier
rfortier marked this pull request as ready for review February 7, 2026 02:06
@rfortier
rfortier force-pushed the fix/Scene-dialog-subtitle-sync branch 2 times, most recently from 5437e9a to 2191f82 Compare February 7, 2026 15:20
@rfortier

rfortier commented Feb 7, 2026

Copy link
Copy Markdown
Contributor Author

Force-pushed to fix log(s) that should have been spdlog::debug for release, and delete some dead debug code no longer needed.

@rfortier

Copy link
Copy Markdown
Contributor Author

Returning this to draft, I probably have a better algorithm incoming.

@rfortier
rfortier marked this pull request as draft February 15, 2026 15:29
@rfortier
rfortier force-pushed the fix/Scene-dialog-subtitle-sync branch 2 times, most recently from 7f42bee to 525c5c5 Compare February 17, 2026 21:28
@rfortier

Copy link
Copy Markdown
Contributor Author

Clean this up (again), found a syncing model that works properly more often and supports a way to get "unstuck" when an AI package requires the Leader to take over a scene to unstick the NPCs.

@rfortier
rfortier marked this pull request as ready for review February 17, 2026 21:40
@rfortier
rfortier force-pushed the fix/Scene-dialog-subtitle-sync branch from 525c5c5 to b45b630 Compare February 20, 2026 20:22
absol89 added a commit to absol89/TiltedEvolutionScriptFixes that referenced this pull request Jul 8, 2026
sfedev added a commit to sfedev/TiltedEvolution that referenced this pull request Sep 13, 2026
… quest progression PR tiltedphoques#769, includes tiltedphoques#854)

# Conflicts:
#	Code/client/Games/Misc/SubtitleManager.cpp
#	Code/client/Games/Skyrim/Actor.cpp
#	Code/client/Games/Skyrim/Forms/TESQuest.cpp
#	Code/client/Games/Skyrim/Forms/TESQuest.h
#	Code/client/Services/Generic/CharacterService.cpp
#	Code/client/Services/Generic/QuestService.cpp
#	Code/encoding/Messages/ClientMessageFactory.h
#	Code/encoding/Messages/ServerMessageFactory.h
#	Code/server/Services/QuestService.cpp
sfedev added a commit to sfedev/TiltedEvolution that referenced this pull request Sep 16, 2026
Scenes, dialogue and subtitle improvements (tiltedphoques#854, rfortier), validated in game
by Sergio on 17 September 2026. Conflicts in Actor.cpp, SubtitleManager.cpp
and CharacterService.cpp resolved as in fix/810-weapon-state-restore (ea5d875).
sfedev added a commit to sfedev/TiltedEvolution that referenced this pull request Sep 16, 2026
…work in a party.

If the cutscene design is that all party members (or all inrange) play the scene, the Leader's dialog actions would step on and cancel the copies running in the party members. This would mess up the scene phase timing, causing the members to race ahead and abort actions of the Leader, potentially hanging the scene. Found with the Finn/Carbos scene in Enderal.

If the cutscene design is only the person(s) who trigger the scene play the scene, and the scene is triggered by a non-Leader, no one except the scene-initiator got any dialog. This is an uncommon case; usually the start of a scene triggers a quest stage change that brings everyone into the scene, or the scene is just random encounter / idle chatter that is just fine to sync to just the triggering player.

Because scenes that don't trigger quest stage changes but also would be desirable to sync are rare, and because there is no good way to distinguish them from scenes where syncing the audio will cause dialog cancels and timing problems, this case is left as unsolved, have to live with it.

There is some logic to trigger scenes in the rest of the party when one starts the scene. This solves some cases. In others, there are scene start conditions that just stop the scene again. Best known example is the Companions Brotherhood quest stage C02. Unless the entire party stands on the tiny hotspot where the initiator triggers the scene, it just stops again.

It's not perfect, but the selected (surviving experiment) solution is:
1) Try to trigger the scene for  the entire party, which often works.
2) Each party member plays the scene to themselves, generally with decent audio sync. The party Leader is still controlling the NPCs and motion / animation sync handles most cases.
3) If the scene triggers TaskDialogues (those NPC dialogues where you answer with a menu pick), that dialog is individual to the SceneMaster (they who triggered), so that is synced to party (PlayersInRange).
4) Only flaw known is AI packages.

Subtitle sync follows the same rules as Dialogue sync.

AI package handling is left for a future iteration, since it also is tricky. Current state: AI packages only run on Leader, so if Member triggers a scene, the AI packages doesn't run. If it is anything of consequence (more than "look at player", say, having to do something and then interact with player), the scene will get stuck and Leader will have to take over. If Leader is close, NPC will just switch to Leader. If farther away or not in scene, Leader will have to get closer, possibly having to unlock the Leader with the EPC (EnablePlayerControls) console command.

For the next round, the two big options I see for fixing AI is for
1. SceneMaster to take ownership of NPCs in-scene. They can be released at end of scene, or just wait for a cell change.
2. Find a way to reactivate AI on "remote" NPCs for the duration of the scene, This sounds more fragile.
…nes.

Getting the failed attempt out of the code, but keeping the commit so you can it was attempted, but there's not enough info.
@miredirex
miredirex force-pushed the fix/Scene-dialog-subtitle-sync branch from a8c6472 to c62f8f5 Compare October 16, 2026 15:27
Every client plays a quest's scenes itself, so relaying the owner's
scene lines made other clients stop their own copy of the line,
ending the scene phase early and letting members race ahead or hang.
Dialogue and subtitle messages now carry the `GameId` of the scene a
line belongs to, and receivers skip lines from a scene they are
playing too. Lines from the player dialogue menu, or from a scene
that isn't playing on the receiver, still play. Subtitles needed the
same skip: only the speaker's own line ending hides a subtitle, so a
remote one arriving after that would stay on screen.
@miredirex
miredirex force-pushed the fix/Scene-dialog-subtitle-sync branch from c62f8f5 to 7a9b48a Compare October 16, 2026 15:39
@miredirex miredirex changed the title Several dialog & subtitle improvements in the way Scenes / Cutscenes … Several dialog & subtitle improvements in the way Scenes / Cutscenes work in a party Oct 16, 2026
@miredirex
miredirex merged commit ea0f6c3 into tiltedphoques:dev Oct 16, 2026
3 checks passed
absol89 pushed a commit to absol89/TiltedEvolutionScriptFixes that referenced this pull request Oct 17, 2026
…oques#854)

* Several dialog & subtitle improvements in the way Scenes / Cutscenes work in a party.

If the cutscene design is that all party members (or all inrange) play the scene, the Leader's dialog actions would step on and cancel the copies running in the party members. This would mess up the scene phase timing, causing the members to race ahead and abort actions of the Leader, potentially hanging the scene. Found with the Finn/Carbos scene in Enderal.

If the cutscene design is only the person(s) who trigger the scene play the scene, and the scene is triggered by a non-Leader, no one except the scene-initiator got any dialog. This is an uncommon case; usually the start of a scene triggers a quest stage change that brings everyone into the scene, or the scene is just random encounter / idle chatter that is just fine to sync to just the triggering player.

Because scenes that don't trigger quest stage changes but also would be desirable to sync are rare, and because there is no good way to distinguish them from scenes where syncing the audio will cause dialog cancels and timing problems, this case is left as unsolved, have to live with it.

There is some logic to trigger scenes in the rest of the party when one starts the scene. This solves some cases. In others, there are scene start conditions that just stop the scene again. Best known example is the Companions Brotherhood quest stage C02. Unless the entire party stands on the tiny hotspot where the initiator triggers the scene, it just stops again.

It's not perfect, but the selected (surviving experiment) solution is:
1) Try to trigger the scene for  the entire party, which often works.
2) Each party member plays the scene to themselves, generally with decent audio sync. The party Leader is still controlling the NPCs and motion / animation sync handles most cases.
3) If the scene triggers TaskDialogues (those NPC dialogues where you answer with a menu pick), that dialog is individual to the SceneMaster (they who triggered), so that is synced to party (PlayersInRange).
4) Only flaw known is AI packages.

Subtitle sync follows the same rules as Dialogue sync.

AI package handling is left for a future iteration, since it also is tricky. Current state: AI packages only run on Leader, so if Member triggers a scene, the AI packages doesn't run. If it is anything of consequence (more than "look at player", say, having to do something and then interact with player), the scene will get stuck and Leader will have to take over. If Leader is close, NPC will just switch to Leader. If farther away or not in scene, Leader will have to get closer, possibly having to unlock the Leader with the EPC (EnablePlayerControls) console command.

For the next round, the two big options I see for fixing AI is for
1. SceneMaster to take ownership of NPCs in-scene. They can be released at end of scene, or just wait for a cell change.
2. Find a way to reactivate AI on "remote" NPCs for the duration of the scene, This sounds more fragile.

* Delete #if 0 code, failed attempt to handle TaskDialogue "goodbye" lines.

Getting the failed attempt out of the code, but keeping the commit so you can it was attempted, but there's not enough info.

* Skip remote voice lines and subtitles for scenes played locally

This is a new approach: every client plays a quest's scenes itself, so relaying the owner's
scene lines made other clients stop their own copy of the line,
ending the scene phase early and letting members race ahead or hang.
Dialogue and subtitle messages now carry the `GameId` of the scene a
line belongs to, and receivers skip lines from a scene they are
playing too. Lines from the player dialogue menu, or from a scene
that isn't playing on the receiver, still play. Subtitles needed the
same skip: only the speaker's own line ending hides a subtitle, so a
remote one arriving after that would stay on screen.

---------

Co-authored-by: Daniil Zakharov <miredirex@gmail.com>
(cherry picked from commit ea0f6c3 on tiltedphoques/TiltedEvolution dev)

Ported onto 1.8-playtest. Conflict resolution: this branch already had the
earlier willSync version of tiltedphoques#854 (b45b630); took the merged upstream
approach (always send SceneId, receiver skips lines for a scene it plays
locally) for OnDialogueEvent/OnNotifyDialogue/OnSubtitleEvent and
BGSSceneAction, kept this branch's logging and BGSScene script helpers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Mdy6FmrJTc1m7KuVM1n3H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants