Badly hurt creatures may flee to their lair or the core and heal
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
5701124c4f
commit
535800c7dc
1 file changed
+43
-13
+43
-13
@@ -3132,14 +3132,20 @@ impl Game {
|
|||||||
e.timer = 2.0 + self.rng.f() * 3.0;
|
e.timer = 2.0 + self.rng.f() * 3.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// badly hurt flyers flee to the aerie and rest until they are healed to 75%
|
// badly hurt creatures flee to their bed (lair, flyers: aerie), or else next to the soul
|
||||||
|
// core, and rest there until they are healed to 75%
|
||||||
fn start_rest(&mut self, e: &mut Ent) -> bool {
|
fn start_rest(&mut self, e: &mut Ent) -> bool {
|
||||||
let cur = e.tile();
|
let cur = e.tile();
|
||||||
let target = e.bed.filter(|&b| self.map.t[b].kind == Kind::Aerie).or_else(|| {
|
let fly = flies(e.kind);
|
||||||
self.map.search(cur, self.map.fly_cost(), |i| self.map.t[i].kind == Kind::Aerie, true).map(|p| p.last().copied().unwrap_or(cur))
|
let bed_kind = if fly { Kind::Aerie } else { Kind::Lair };
|
||||||
});
|
let me = self.me;
|
||||||
|
let target = e
|
||||||
|
.bed
|
||||||
|
.filter(|&b| self.map.t[b].kind == bed_kind)
|
||||||
|
.or_else(|| self.map.search(cur, self.map.creature_cost(fly), |i| self.map.t[i].kind == bed_kind && self.map.t[i].owner == me, true).map(|p| p.last().copied().unwrap_or(cur)))
|
||||||
|
.or_else(|| self.map.search(cur, self.map.creature_cost(fly), |i| self.map.adj_core_of(i, me), true).map(|p| p.last().copied().unwrap_or(cur)));
|
||||||
let Some(b) = target else { return false };
|
let Some(b) = target else { return false };
|
||||||
e.path = self.map.fly_path_to(cur, b).unwrap_or_default();
|
e.path = self.map.creature_path(fly, cur, b).unwrap_or_default();
|
||||||
e.job = Job::Rest(b);
|
e.job = Job::Rest(b);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -3511,8 +3517,9 @@ impl Game {
|
|||||||
self.fx(8, bx as f32 + 0.5, by as f32 + 0.5, 0.0, 0.0, 0.6, 0xff40d0ff);
|
self.fx(8, bx as f32 + 0.5, by as f32 + 0.5, 0.0, 0.0, 0.6, 0xff40d0ff);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// flyers flee when badly hurt and come back at 75%
|
// badly hurt creatures may flee before they die (not all of them, and not at once)
|
||||||
if flies(e.kind) && e.hp < e.max_hp * 0.25 && !matches!(e.job, Job::Rest(_) | Job::Leave) {
|
// and come back at 75%
|
||||||
|
if e.hp < e.max_hp * 0.25 && !matches!(e.job, Job::Rest(_) | Job::Leave) && self.rng.f() < 0.3 {
|
||||||
self.unreserve(me);
|
self.unreserve(me);
|
||||||
if self.start_rest(e) {
|
if self.start_rest(e) {
|
||||||
return;
|
return;
|
||||||
@@ -3570,17 +3577,18 @@ impl Game {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let (bx, by) = xy(b);
|
let (bx, by) = xy(b);
|
||||||
let at_aerie = (e.x - bx as f32 - 0.5).abs() < 1.5 && (e.y - by as f32 - 0.5).abs() < 1.5;
|
let there = (e.x - bx as f32 - 0.5).abs() < 1.5 && (e.y - by as f32 - 0.5).abs() < 1.5;
|
||||||
if !at_aerie {
|
if !there {
|
||||||
// pushed away on the way: fly on, or give up if there is no way
|
// pushed away on the way: go on, or give up if there is no way
|
||||||
match self.map.fly_path_to(e.tile(), b) {
|
match self.map.creature_path(flies(e.kind), e.tile(), b) {
|
||||||
Some(p) if !p.is_empty() => e.path = p,
|
Some(p) if !p.is_empty() => e.path = p,
|
||||||
_ => e.job = Job::None,
|
_ => e.job = Job::None,
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// resting in the aerie heals quickly, back to work at 75%
|
// resting heals: quickly in a bed room, slower next to the core; back to work at 75%
|
||||||
e.hp = (e.hp + e.max_hp * dt / 12.0).min(e.max_hp);
|
let bed_room = matches!(self.map.t[b].kind, Kind::Lair | Kind::Aerie);
|
||||||
|
e.hp = (e.hp + e.max_hp * dt / if bed_room { 12.0 } else { 24.0 }).min(e.max_hp);
|
||||||
e.energy = (e.energy + dt / 14.0).min(1.0);
|
e.energy = (e.energy + dt / 14.0).min(1.0);
|
||||||
if e.hp >= e.max_hp * 0.75 {
|
if e.hp >= e.max_hp * 0.75 {
|
||||||
e.job = Job::None;
|
e.job = Job::None;
|
||||||
@@ -6045,6 +6053,28 @@ mod tests {
|
|||||||
assert!(!g.torches.iter().any(|t| t.stand && t.wall == s0), "a stand on lost ground is removed");
|
assert!(!g.torches.iter().any(|t| t.stand && t.wall == s0), "a stand on lost ground is removed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn badly_hurt_creatures_flee_to_the_lair_and_heal() {
|
||||||
|
let mut g = Game::new(5, 64, 64);
|
||||||
|
let (cx, cy) = g.map.core;
|
||||||
|
g.gold = 5000.0;
|
||||||
|
g.build_rect(1, cx - 3, cy - 3, cx - 2, cy + 2);
|
||||||
|
assert!(g.counts[1] >= 8, "a lair");
|
||||||
|
let id = g.spawn(EK::Kobold, false, cx as f32 + 5.5, cy as f32 + 0.5, 2);
|
||||||
|
let j = g.ents.iter().position(|e| e.id == id).unwrap();
|
||||||
|
g.ents[j].hp = g.ents[j].max_hp * 0.2;
|
||||||
|
let mut fled = false;
|
||||||
|
for _ in 0..(20 * 6) {
|
||||||
|
g.update(0.05);
|
||||||
|
fled |= matches!(g.ents[j].job, Job::Rest(_));
|
||||||
|
}
|
||||||
|
assert!(fled, "it fled to rest");
|
||||||
|
for _ in 0..(20 * 20) {
|
||||||
|
g.update(0.05);
|
||||||
|
}
|
||||||
|
assert!(g.ents[j].hp > g.ents[j].max_hp * 0.5, "and healed: {} of {}", g.ents[j].hp, g.ents[j].max_hp);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn flyers_take_every_way_across_rivers() {
|
fn flyers_take_every_way_across_rivers() {
|
||||||
let (mut g, rx) = river_game(false);
|
let (mut g, rx) = river_game(false);
|
||||||
|
|||||||
Reference in new issue
Block a user