use beet::prelude::*;
use std::io;
use std::io::Write;
fn main() {
run_app();
loop {
println!("Press Enter to try again...");
io::stdout().flush().unwrap();
io::stdin()
.read_line(&mut String::new())
.expect("Failed to read line");
run_app();
}
}
fn run_app() {
println!("👩\tMalenia says: {INTRO}");
let mut app = App::new();
app.add_plugins((MinimalPlugins, ControlFlowPlugin::default()))
.add_systems(Update, health_handler)
.init_resource::<RandomSource>();
app.world_mut()
.spawn((Name::new("Elden Lord"), Health::default()));
app.world_mut()
.spawn((
Name::new("Malenia"),
Health::default(),
HealingPotions(2),
Fallback::default(),
Repeat::default(),
))
.with_children(|root| {
root.spawn((Name::new("Try Heal Self"), TryHealSelf));
root.spawn((Name::new("Attack"), HighestScore::default()))
.with_child((
Name::new("Waterfoul Dance"),
RandomScoreProvider::default(),
AttackPlayer {
max_damage: 15.0,
max_recoil: 30.0,
},
EndWith(Outcome::Pass),
))
.with_child((
Name::new("Scarlet Aeonia"),
EndWith(Score(0.05)),
AttackPlayer {
max_damage: 10_000.0,
max_recoil: 10.0,
},
EndWith(Outcome::Pass),
));
})
.trigger_target(GetOutcome);
app.run();
}
#[action(attack_player)]
#[derive(Component)]
struct AttackPlayer {
max_damage: f32,
max_recoil: f32,
}
fn attack_player(
ev: On<GetOutcome>,
attacks: Query<(&AttackPlayer, &Name)>,
mut query: Query<(&mut Health, &Name)>,
mut random_source: ResMut<RandomSource>,
) -> Result {
let (attack, attack_name) = attacks.get(ev.action())?;
println!("🔪 \tMalenia attacks with {}", attack_name);
for (mut health, name) in query.iter_mut() {
if name.as_str() == "Malenia" {
let damage: f32 =
random_source.random_range(0.0..attack.max_recoil).round();
health.0 -= damage;
println!(
"❗ \tMalenia takes {} recoil damage, current health: {}",
damage, health.0
);
} else {
let damage: f32 =
random_source.random_range(0.0..attack.max_damage).round();
health.0 -= damage;
println!(
"❗ \tPlayer takes {} damage, current health: {}",
damage, health.0
);
}
}
println!();
Ok(())
}
fn health_handler(
query: Populated<(&Health, &Name), Changed<Health>>,
mut exit: MessageWriter<AppExit>,
) {
for (health, name) in query.iter() {
if health.0 > 0. {
continue;
} else if name.as_str() == "Malenia" {
println!(
"👩\tMalenia says: 'Your strength, extraordinary...'\n✅\tYou win!"
);
} else {
println!(
"👩\tMalenia says: 'I am Malenia. Blade of Miquella'\n❌\tYou lose"
);
}
exit.write(AppExit::Success);
}
}
#[derive(Component)]
struct Health(f32);
#[derive(Component)]
struct HealingPotions(usize);
impl Default for Health {
fn default() -> Self { Self(100.0) }
}
#[action(provide_random_score)]
#[derive(Component, Reflect)]
struct RandomScoreProvider {
pub scalar: f32,
pub offset: f32,
}
impl Default for RandomScoreProvider {
fn default() -> Self {
Self {
scalar: 1.0,
offset: 0.0,
}
}
}
fn provide_random_score(
ev: On<GetScore>,
mut commands: Commands,
mut random_source: ResMut<RandomSource>,
query: Query<&RandomScoreProvider>,
) -> Result {
let score_provider = query.get(ev.action())?;
let rnd: f32 = random_source.random();
commands.entity(ev.action()).trigger_target(Score(
rnd * score_provider.scalar + score_provider.offset,
));
Ok(())
}
#[action(try_heal_self)]
#[derive(Component, Reflect)]
struct TryHealSelf;
fn try_heal_self(
ev: On<GetOutcome>,
mut commands: Commands,
mut query: AgentQuery<(&mut Health, &mut HealingPotions)>,
) -> Result {
let (mut health, mut potions) = query.get_mut(ev.action())?;
if health.0 < 50.0 && potions.0 > 0 {
health.0 += 30.;
potions.0 -= 1;
println!("💊\tMalenia heals herself, current health: {}\n", health.0);
commands.entity(ev.action()).trigger_target(Outcome::Pass);
} else {
commands.entity(ev.action()).trigger_target(Outcome::Fail);
}
Ok(())
}
const INTRO: &str = r#"
I dreamt for so long.
My flesh was dull gold...and my blood, rotted.
Corpse after corpse, left in my wake...
As I awaited... his return.
... Heed my words.
I am Malenia. Blade of Miquella.
And I have never known defeat.
"#;