use bevy_ecs::prelude::*;
use bevy_entity_ptr::{EntityHandle, EntityPtr, WorldExt};
#[derive(Component)]
struct Name(&'static str);
#[derive(Component)]
struct Health(i32);
#[derive(Component)]
struct Target(Option<EntityHandle>);
#[derive(Component)]
struct Inventory(Vec<EntityHandle>);
#[derive(Component)]
struct Equipped {
weapon: Option<EntityHandle>,
armor: Option<EntityHandle>,
}
#[derive(Component)]
struct Weight(f32);
#[derive(Component)]
struct Damage(i32);
#[derive(Component)]
struct Defense(i32);
fn total_weight(character: EntityPtr) -> f32 {
character
.get::<Inventory>()
.map(|inv| {
inv.0
.iter()
.filter_map(|h| character.follow_handle(*h).get::<Weight>())
.map(|w| w.0)
.sum()
})
.unwrap_or(0.0)
}
fn equipped_damage(character: EntityPtr) -> Option<i32> {
character
.get::<Equipped>()
.and_then(|e| e.weapon)
.and_then(|h| character.follow_handle(h).get::<Damage>())
.map(|d| d.0)
}
fn equipped_defense(character: EntityPtr) -> Option<i32> {
character
.get::<Equipped>()
.and_then(|e| e.armor)
.and_then(|h| character.follow_handle(h).get::<Defense>())
.map(|d| d.0)
}
fn target_info(character: EntityPtr) -> Option<(&'static str, i32)> {
character
.follow_opt::<Target, _>(|t| t.0)
.and_then(|target| {
let name = target.get::<Name>()?.0;
let health = target.get::<Health>()?.0;
Some((name, health))
})
}
fn find_attackers(world: &World, candidates: &[Entity], target_entity: Entity) -> Vec<EntityPtr> {
candidates
.iter()
.filter_map(|&entity| {
let ptr = world.entity_ptr(entity);
ptr.get::<Target>()
.and_then(|t| t.0.filter(|h| h.entity() == target_entity).map(|_| ptr))
})
.collect()
}
fn main() {
let mut world = World::new();
println!("Setting up game entities...\n");
let sword = world
.spawn((Name("Iron Sword"), Weight(5.0), Damage(15)))
.id();
let shield = world
.spawn((Name("Wooden Shield"), Weight(8.0), Defense(10)))
.id();
let potion = world.spawn((Name("Health Potion"), Weight(0.5))).id();
let gold = world.spawn((Name("Gold Coins"), Weight(1.0))).id();
let bow = world
.spawn((Name("Short Bow"), Weight(3.0), Damage(10)))
.id();
let hero = world
.spawn((
Name("Hero"),
Health(100),
Target(None), Inventory(vec![
EntityHandle::new(sword),
EntityHandle::new(shield),
EntityHandle::new(potion),
EntityHandle::new(gold),
]),
Equipped {
weapon: Some(EntityHandle::new(sword)),
armor: Some(EntityHandle::new(shield)),
},
))
.id();
let goblin = world
.spawn((
Name("Goblin"),
Health(30),
Target(Some(EntityHandle::new(hero))),
Inventory(vec![EntityHandle::new(bow)]),
Equipped {
weapon: Some(EntityHandle::new(bow)),
armor: None,
},
))
.id();
let orc = world
.spawn((
Name("Orc"),
Health(50),
Target(Some(EntityHandle::new(hero))),
))
.id();
let hero_ptr = world.entity_ptr(hero);
let goblin_ptr = world.entity_ptr(goblin);
let weight = total_weight(hero_ptr);
println!("Hero's inventory weight: {} lbs", weight);
println!(" Expected: 5.0 + 8.0 + 0.5 + 1.0 = 14.5");
assert!((weight - 14.5).abs() < 0.01);
let damage = equipped_damage(hero_ptr);
println!("\nHero's equipped weapon damage: {:?}", damage);
assert_eq!(damage, Some(15));
let defense = equipped_defense(hero_ptr);
println!("Hero's equipped armor defense: {:?}", defense);
assert_eq!(defense, Some(10));
let goblin_damage = equipped_damage(goblin_ptr);
println!("\nGoblin's equipped weapon damage: {:?}", goblin_damage);
assert_eq!(goblin_damage, Some(10));
let goblin_defense = equipped_defense(goblin_ptr);
println!("Goblin's equipped armor defense: {:?}", goblin_defense);
assert_eq!(goblin_defense, None);
let hero_target = target_info(hero_ptr);
println!("\nHero's target: {:?}", hero_target);
assert_eq!(hero_target, None);
let goblin_target = target_info(goblin_ptr);
println!("Goblin's target: {:?}", goblin_target);
assert_eq!(goblin_target, Some(("Hero", 100)));
let characters = [hero, goblin, orc];
let attackers = find_attackers(&world, &characters, hero);
let attacker_names: Vec<_> = attackers
.iter()
.filter_map(|p| p.get::<Name>())
.map(|n| n.0)
.collect();
println!("\nEntities targeting the hero: {:?}", attacker_names);
assert_eq!(attacker_names.len(), 2);
assert!(attacker_names.contains(&"Goblin"));
assert!(attacker_names.contains(&"Orc"));
world
.entity_mut(hero)
.insert(Target(Some(EntityHandle::new(goblin))));
let hero_ptr = world.entity_ptr(hero);
let hero_target = target_info(hero_ptr);
println!("\nAfter targeting goblin:");
println!("Hero's target: {:?}", hero_target);
assert_eq!(hero_target, Some(("Goblin", 30)));
println!("\nAll assertions passed!");
}