Post-Effekte über Action::SetPost statt Engine-Zustand
Die PostChain lag als engine::post im Kern, nur damit die Konsole
`post` durch den exec-Trichter schalten konnte — Zustand, der allein
das Render-Frontend angeht und nichts mit der Simulation zu tun hat.
Jetzt kennt der Kern Post-Effekte nur als deferred Action (wie
play_sound): das `post <name> on|off`-Verb queued Action::SetPost,
die Session reicht sie als take_post()-Queue durch, das Fenster wendet
sie auf seine eigene PostChain an. Damit schalten Trigger und Ink-Tags
Effekte, die vorher nur der Konsolenbefehl erreichte.
- engine/post.rs entfällt; PostChain wandert nach render::post
- Session verliert das post-Feld, bekommt pending_post + take_post
- der `post`-Konsolenbefehl ist ein Shim wie `mode`; Ketten-Listing
und nackter Toggle fallen weg (letzterer bewusst, vgl. 8bee832)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0196AM4UyAJEXVtPvp5DdKr8
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
b3d08d4a8c
commit
247f7ef9fe
+48
-50
@@ -3,7 +3,6 @@ use crate::engine::collision::CollisionWorld;
|
||||
use crate::engine::game::{Action, Game, ModeTarget};
|
||||
use crate::engine::ink::StoryState;
|
||||
use crate::engine::player::{self, Player};
|
||||
use crate::engine::post::PostChain;
|
||||
use crate::engine::trigger::Triggers;
|
||||
use crate::engine::{kv, model, signals, story_ctrl};
|
||||
|
||||
@@ -44,13 +43,14 @@ pub struct Session {
|
||||
/// Ambient-Emitter aus den Blender-Modellen (`sound`-Property auf
|
||||
/// Empties, siehe engine::audio).
|
||||
emitters: Emitters,
|
||||
/// An/Aus der Render-Post-Effekte. Das Fenster-Frontend meldet seine Kette
|
||||
/// beim Start an und liest sie jeden Frame; die CLI lässt sie leer.
|
||||
pub post: PostChain,
|
||||
/// Von `Action::PlaySound` aufgelaufene Einmal-SFX — das Fenster-Frontend
|
||||
/// zieht sie per [`Session::take_sounds`]; der CLI reicht die
|
||||
/// `[action]`-Textzeile.
|
||||
pending_sounds: Vec<String>,
|
||||
/// Von `Action::SetPost` aufgelaufene Post-Effekt-Schaltungen (Name,
|
||||
/// Zielzustand) — das Fenster zieht sie per [`Session::take_post`] und
|
||||
/// wendet sie auf seine Kette an; der CLI reicht die `[action]`-Textzeile.
|
||||
pending_post: Vec<(String, bool)>,
|
||||
signals_path: String,
|
||||
/// Aufgelaufene, noch nicht simulierte Zeit (s) für den Fixed-Timestep.
|
||||
sim_accum: f32,
|
||||
@@ -97,8 +97,8 @@ impl Session {
|
||||
collision: CollisionWorld::empty(),
|
||||
triggers: Triggers::new(),
|
||||
emitters: Emitters::new(),
|
||||
post: PostChain::new(),
|
||||
pending_sounds: Vec::new(),
|
||||
pending_post: Vec::new(),
|
||||
signals_path,
|
||||
sim_accum: 0.0,
|
||||
pending_jump: false,
|
||||
@@ -138,6 +138,13 @@ impl Session {
|
||||
std::mem::take(&mut self.pending_sounds)
|
||||
}
|
||||
|
||||
/// Aufgelaufene Post-Effekt-Schaltungen (`SetPost`-Actions) abholen — jeden
|
||||
/// Frame vom Fenster-Frontend, das sie auf seine Kette anwendet. Headless
|
||||
/// bleibt die Queue liegen (die CLI hat keine Kette) und wird nie gezogen.
|
||||
pub fn take_post(&mut self) -> Vec<(String, bool)> {
|
||||
std::mem::take(&mut self.pending_post)
|
||||
}
|
||||
|
||||
/// Einen Simulationsschritt treiben — das kontinuierliche Gegenstück zu
|
||||
/// `exec`. Im Spielmodus läuft die Physik, im Noclip fliegt derselbe
|
||||
/// Spieler frei; Menü und Dialog pausieren die Welt. Rückgabe:
|
||||
@@ -255,7 +262,16 @@ impl Session {
|
||||
}
|
||||
None => ExecResult::lines(vec!["Modus: play | free | menu".into()]),
|
||||
},
|
||||
("post", args) => ExecResult::lines(self.post_cmd(args)),
|
||||
// Post-Effekt über dieselbe Action wie das `post`-Verb aus
|
||||
// signals.toml/Ink-Tags. Das Fenster löst den Namen gegen seine
|
||||
// Kette auf; headless bleibt es bei der `[action]`-Zeile.
|
||||
("post", args) => match signals::parse_post_arg(args) {
|
||||
Some((name, on)) => {
|
||||
self.game.actions.push(Action::SetPost { name, on });
|
||||
ExecResult::lines(self.drain_actions())
|
||||
}
|
||||
None => ExecResult::lines(vec!["post <name> on|off".into()]),
|
||||
},
|
||||
("signal", sig) => ExecResult::lines(self.fire(sig, None)),
|
||||
// Objekt-Interaktion (Klick wie Konsole): Klick-Ziele feuern ihre
|
||||
// `signal`-Property, sonst den suffix-befreiten Objektnamen.
|
||||
@@ -272,32 +288,6 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
/// `post` listet die Kette, `post <name> [on|off]` schaltet sie (ohne
|
||||
/// Zustand: toggle). Die Namen kommen vom Frontend — die Session validiert
|
||||
/// nur gegen das, was angemeldet wurde.
|
||||
fn post_cmd(&mut self, args: &str) -> Vec<String> {
|
||||
if args.is_empty() {
|
||||
let lines: Vec<String> = self.post.list()
|
||||
.map(|(n, on)| format!(" {n:<10} {}", on_off(on)))
|
||||
.collect();
|
||||
return match lines.is_empty() {
|
||||
true => vec!["keine Post-Effekte (nur im Fenster-Frontend)".into()],
|
||||
false => lines,
|
||||
};
|
||||
}
|
||||
let (name, state) = args.split_once(char::is_whitespace).unwrap_or((args, ""));
|
||||
let on = match state.trim() {
|
||||
"" => None,
|
||||
"on" | "an" => Some(true),
|
||||
"off" | "aus" => Some(false),
|
||||
_ => return vec!["post <name> [on|off]".into()],
|
||||
};
|
||||
match self.post.set(name, on) {
|
||||
Some(now) => vec![format!("{name}: {}", on_off(now))],
|
||||
None => vec![format!("unbekannter Effekt: {name:?} — `post` listet sie")],
|
||||
}
|
||||
}
|
||||
|
||||
fn dialog_input(&mut self, line: &str) -> ExecResult {
|
||||
let Mode::Dialog(d) = &self.mode else { return ExecResult::empty(); };
|
||||
let sel = if d.choices.is_empty() {
|
||||
@@ -378,6 +368,10 @@ impl Session {
|
||||
self.set_mode(t);
|
||||
out.push(format!("[action] mode {}", t.label()));
|
||||
}
|
||||
Action::SetPost { name, on } => {
|
||||
out.push(format!("[action] post {name} {}", if on { "on" } else { "off" }));
|
||||
self.pending_post.push((name, on));
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
@@ -404,10 +398,6 @@ impl Session {
|
||||
}
|
||||
}
|
||||
|
||||
fn on_off(on: bool) -> &'static str {
|
||||
if on { "an" } else { "aus" }
|
||||
}
|
||||
|
||||
fn help() -> Vec<String> {
|
||||
vec![
|
||||
" signal <s> Signal feuern oder Action direkt ausführen".into(),
|
||||
@@ -417,7 +407,7 @@ fn help() -> Vec<String> {
|
||||
" kv KV-Store anzeigen".into(),
|
||||
" menu Menü öffnen / ins Spiel zurück".into(),
|
||||
" mode <m> Modus setzen: play | free (Noclip) | menu".into(),
|
||||
" post [<e> on|off] Post-Effekte anzeigen / schalten".into(),
|
||||
" post <e> on|off Post-Effekt schalten (Fenster-Frontend)".into(),
|
||||
" reload signals.toml neu laden".into(),
|
||||
" quit beenden".into(),
|
||||
]
|
||||
@@ -643,21 +633,29 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn post_command_switches_registered_effects() {
|
||||
fn post_command_queues_setpost_for_the_frontend() {
|
||||
let mut s = empty_session();
|
||||
// Ohne Frontend ist die Kette leer — der Befehl sagt das, statt zu lügen.
|
||||
assert_eq!(s.exec("post").output.len(), 1);
|
||||
assert!(!s.post.enabled("dither"));
|
||||
s.mode = Mode::Free;
|
||||
// Der Befehl kennt die Kette nicht — er reiht die Schaltung ein, das
|
||||
// Fenster zieht sie per take_post().
|
||||
let r = s.exec("post outline off");
|
||||
assert_eq!(r.output, vec!["[action] post outline off"]);
|
||||
assert_eq!(s.take_post(), vec![("outline".to_string(), false)]);
|
||||
assert!(s.take_post().is_empty(), "abgeholt = geleert");
|
||||
|
||||
s.post.register("dither", true);
|
||||
assert_eq!(s.exec("post").output.len(), 1, "eine Zeile je Effekt");
|
||||
s.exec("post dither off");
|
||||
assert!(!s.post.enabled("dither"));
|
||||
s.exec("post dither");
|
||||
assert!(s.post.enabled("dither"), "ohne Zustand togglet es");
|
||||
// Unbekannter Effekt meldet sich und lässt die Kette in Ruhe.
|
||||
assert_eq!(s.exec("post bloom on").output.len(), 1);
|
||||
assert!(s.post.enabled("dither"));
|
||||
// Fehlendes/ungültiges on|off → Nutzungshinweis, nichts eingereiht.
|
||||
assert_eq!(s.exec("post outline").output, vec!["post <name> on|off"]);
|
||||
assert_eq!(s.exec("post outline wat").output, vec!["post <name> on|off"]);
|
||||
assert!(s.take_post().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn post_signal_from_table_queues_setpost() {
|
||||
let mut s = empty_session();
|
||||
s.mode = Mode::Free;
|
||||
// Ink-Tag-/signals.toml-Pfad: `post`-Verb direkt als Action.
|
||||
s.exec("signal post dither on");
|
||||
assert_eq!(s.take_post(), vec![("dither".to_string(), true)]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user