#![allow(clippy::print_stdout)]
use bevy::prelude::*;
use bevy_brink::{
BrinkBindings, BrinkBindingsAppExt, BrinkChoicesPresented, BrinkContext, BrinkFlow,
BrinkFlowRequest, BrinkGlobals, BrinkLocale, BrinkPlugin, BrinkProgram, BrinkTranscript,
Choice, FlowStart, LineTablesAsset, ProgramAsset, RuntimeError, Scope, Value, WorldPolicy,
digit_key_to_choice_index, flow_context_view,
};
fn main() {
let mut app = App::new();
app.add_plugins((AssetPlugin::default(), BrinkPlugin::<()>::default()));
app.bind_brink_fn::<(), _, _>("noop", |_args| Value::Int(0));
register_choice_logger(&mut app);
app.add_systems(Update, drive);
app.add_systems(Update, request_flow.run_if(|| false));
app.update();
}
#[expect(
clippy::needless_pass_by_value,
reason = "bevy systems take Res by value"
)]
fn request_flow(mut commands: Commands, assets: Res<AssetServer>) {
commands.spawn(
BrinkFlowRequest::<()>::builder()
.story(assets.load("dialogue.inkb"))
.start(FlowStart::Address("intro_scene".into())) .build(),
);
}
#[expect(
dead_code,
reason = "included verbatim into the Bevy book; not exercised here"
)]
fn app_with_policy() -> App {
let mut policy = WorldPolicy::default(); policy.overrides.insert("mood".to_string(), Scope::Local); policy
.overrides
.insert("greeting".to_string(), Scope::Local);
let mut app = App::new();
app.add_plugins((
AssetPlugin::default(),
BrinkPlugin::<()>::default().with_policy(policy),
));
app
}
#[expect(
clippy::needless_pass_by_value,
clippy::type_complexity,
reason = "bevy systems take Res by value and have complex query tuples"
)]
fn drive(
mut flows: Query<(
Entity,
&mut BrinkFlow<()>,
&mut BrinkContext<()>,
&BrinkProgram<()>,
&BrinkLocale<()>,
)>,
globals: Option<ResMut<BrinkGlobals<()>>>,
programs: Res<Assets<ProgramAsset>>,
tables: Res<Assets<LineTablesAsset>>,
bindings: Res<BrinkBindings<()>>,
mut commands: Commands,
) {
let Some(mut globals) = globals else {
return; };
for (entity, mut flow, mut ctx, prog, loc) in &mut flows {
if flow.inner.has_pending_external() {
continue;
} let (Some(p), Some(t)) = (programs.get(&prog.handle), tables.get(&loc.handle)) else {
continue;
};
let handler = bindings.handler();
let mut view = flow_context_view(&mut globals, &mut ctx);
let _ = flow.advance_until_terminal(
&p.program,
&t.tables,
&mut view,
&handler,
entity,
&mut commands,
);
handler.flush(&mut commands); }
}
#[expect(
dead_code,
reason = "included verbatim into the Bevy book; not exercised here"
)]
fn pick(
flow: &mut BrinkFlow<()>,
globals: &mut BrinkGlobals<()>,
ctx: &mut BrinkContext<()>,
index: usize,
) -> Result<(), RuntimeError> {
let mut view = flow_context_view(globals, ctx);
flow.choose(&mut view, index)?;
Ok(())
}
#[expect(
clippy::needless_pass_by_value,
dead_code,
reason = "included verbatim into the Bevy book; not exercised here"
)]
fn keyboard_pick(
keys: Res<ButtonInput<KeyCode>>,
choices: &[Choice],
flow: &mut BrinkFlow<()>,
globals: &mut BrinkGlobals<()>,
ctx: &mut BrinkContext<()>,
) -> Result<(), RuntimeError> {
if let Some(idx) = digit_key_to_choice_index(&keys, choices.len()) {
let mut view = flow_context_view(globals, ctx);
flow.choose(&mut view, idx)?;
}
Ok(())
}
fn register_choice_logger(app: &mut App) {
app.add_observer(|on: On<BrinkChoicesPresented<()>>| {
for (i, choice) in on.event().choices.iter().enumerate() {
println!(" [{}] {}", i + 1, choice.text);
}
});
}
#[expect(
dead_code,
reason = "included verbatim into the Bevy book; not exercised here"
)]
fn show_transcript(mut commands: Commands, flow: Entity, transcript: &BrinkTranscript<()>) {
commands
.entity(flow)
.insert(BrinkTranscript::<()>::default());
let text = transcript.text(); let lines = &transcript.lines; render_conversation(&text, lines);
}
fn render_conversation(_text: &str, _lines: &[(String, Vec<String>)]) {}