Add bitmap font, UI primitives and a HUD with message log
Ports the CP437 font renderer from wds to the framebuffer: proportional advance from measured ink bounds, luminance-keyed mask, word wrap. The right 80 px strip becomes a HUD with name, HP bar, population and a log fed from sim events; Attacked/Died now carry the type ids the log needs, since the victim is gone from the world by the time events are read. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+14
-12
@@ -15,11 +15,13 @@ const SIGHT_RANGE: i32 = 8;
|
||||
/// back — they are an output channel, drained with `Sim::take_events`.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum Event {
|
||||
/// `by` bumped into `target` and dealt `damage`.
|
||||
Attacked { by: u32, target: u32, damage: u16 },
|
||||
/// An entity's hp reached zero; it has already been removed from the world. `at`
|
||||
/// is where it stood, since the entity itself is gone.
|
||||
Died { id: u32, at: (i16, i16) },
|
||||
/// `by` bumped into `target` and dealt `damage`. `target_type` is carried so a
|
||||
/// listener can still name a target that died of this hit.
|
||||
Attacked { by: u32, target: u32, target_type: u16, damage: u16 },
|
||||
/// An entity's hp reached zero and it has been removed from the world. Carries
|
||||
/// what listeners need, since the entity itself is gone: what it was, where it
|
||||
/// stood, who killed it.
|
||||
Died { id: u32, type_id: u16, at: (i16, i16), by: u32 },
|
||||
}
|
||||
|
||||
pub struct Sim {
|
||||
@@ -174,11 +176,11 @@ impl Sim {
|
||||
let damage = self.world.entities[&by].attack;
|
||||
let t = self.world.entities.get_mut(&target).unwrap();
|
||||
t.hp = t.hp.saturating_sub(damage);
|
||||
let (hp, at) = (t.hp, t.pos);
|
||||
self.events.push(Event::Attacked { by, target, damage });
|
||||
let (hp, type_id, at) = (t.hp, t.type_id, t.pos);
|
||||
self.events.push(Event::Attacked { by, target, target_type: type_id, damage });
|
||||
if hp == 0 {
|
||||
self.world.remove_entity(target);
|
||||
self.events.push(Event::Died { id: target, at });
|
||||
self.events.push(Event::Died { id: target, type_id, at, by });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -290,7 +292,7 @@ mod tests {
|
||||
sim.set_action(p, player_action::SOUTH);
|
||||
step_window(&mut sim);
|
||||
assert_eq!(sim.world.entities[&n].hp, 2);
|
||||
assert_eq!(sim.take_events(), vec![Event::Attacked { by: p, target: n, damage: 3 }]);
|
||||
assert_eq!(sim.take_events(), vec![Event::Attacked { by: p, target: n, target_type: 1, damage: 3 }]);
|
||||
assert!(sim.take_events().is_empty()); // drained
|
||||
|
||||
sim.set_action(p, player_action::SOUTH);
|
||||
@@ -298,8 +300,8 @@ mod tests {
|
||||
assert!(!sim.world.entities.contains_key(&n));
|
||||
assert!(!sim.world.blocked(4, 5));
|
||||
assert_eq!(sim.take_events(), vec![
|
||||
Event::Attacked { by: p, target: n, damage: 3 },
|
||||
Event::Died { id: n, at: (4, 5) },
|
||||
Event::Attacked { by: p, target: n, target_type: 1, damage: 3 },
|
||||
Event::Died { id: n, type_id: 1, at: (4, 5), by: p },
|
||||
]);
|
||||
|
||||
// The tile is free again: the same intent now moves.
|
||||
@@ -346,7 +348,7 @@ mod tests {
|
||||
assert_eq!(sim.world.entities[&hen].hp, 5);
|
||||
step_window(&mut sim);
|
||||
assert_eq!(sim.world.entities[&hen].hp, 3);
|
||||
assert_eq!(sim.take_events(), vec![Event::Attacked { by: fox, target: hen, damage: 2 }]);
|
||||
assert_eq!(sim.take_events(), vec![Event::Attacked { by: fox, target: hen, target_type: 1, damage: 2 }]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user