Idle imps help: up to five imps at one task, +25% speed per helper (max 200%)
Imps with nothing else to do (no digging, delivering, claiming, repairing or reinforcing of their own) walk to a task of another imp and help; every 1.5 s they check for own work again. Claiming floor keeps its priority before helping. Test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a0f2660eae
commit
05d9cf8824
3 files changed
+103
-4
No files matched your search
+101
-4
@@ -9,7 +9,8 @@ pub const PAYDAY: f32 = 150.0;
|
||||
pub const PAYDAY_WARN: f32 = 20.0; // the payday is announced this many seconds before
|
||||
pub const TREASURY_TILE: f32 = 500.0; // value one treasury tile holds
|
||||
pub const CORE_STORE: f32 = 2000.0; // value the soul core holds without any treasury
|
||||
pub const TRAIN_COST: f32 = 1.5; // gold per second of training (billed on payday)
|
||||
pub const TRAIN_COST: f32 = 1.5;
|
||||
pub const HELPERS_MAX: usize = 4; // up to five imps at one task (one worker + four helpers) // gold per second of training (billed on payday)
|
||||
pub const FIRST_WAVE: f32 = 300.0;
|
||||
pub const WAVE_GAP: f32 = 180.0;
|
||||
pub const MAX_SQUAD: usize = 18;
|
||||
@@ -154,6 +155,7 @@ pub enum Job {
|
||||
Pray(usize), // temple tile
|
||||
HealCore, // flame priestess heals the soul core
|
||||
TearBridge(usize), // knight tears down a bridge of the player
|
||||
Help(usize), // imp with nothing else to do helps another imp at this tile
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
@@ -942,6 +944,51 @@ impl Game {
|
||||
}
|
||||
}
|
||||
|
||||
// the imp working on tile t (dig, claim or reinforce)
|
||||
fn task_worker(&self, t: usize) -> Option<usize> {
|
||||
self.ents.iter().position(|o| o.alive && !o.hero && o.kind == EK::Worker && matches!(o.job, Job::Dig(x) | Job::Claim(x) | Job::Reinforce(x) if x == t))
|
||||
}
|
||||
|
||||
// imps helping at tile t (standing next to it)
|
||||
fn helpers(&self, t: usize) -> usize {
|
||||
let (tx, ty) = xy(t);
|
||||
self.ents
|
||||
.iter()
|
||||
.filter(|o| o.alive && o.kind == EK::Worker && o.job == Job::Help(t) && o.path.is_empty() && (o.x - tx as f32 - 0.5).abs() < 1.6 && (o.y - ty as f32 - 0.5).abs() < 1.6)
|
||||
.count()
|
||||
}
|
||||
|
||||
// working speed at a tile: +25% per helper, at most 200% (five imps)
|
||||
fn help_speed(&self, t: usize) -> f32 {
|
||||
1.0 + 0.25 * self.helpers(t).min(HELPERS_MAX) as f32
|
||||
}
|
||||
|
||||
// an idle imp walks to the nearest task with room for another helper
|
||||
fn start_help(&mut self, e: &mut Ent) -> bool {
|
||||
let tasks: Vec<usize> = self
|
||||
.ents
|
||||
.iter()
|
||||
.filter(|o| o.alive && !o.hero && o.kind == EK::Worker)
|
||||
.filter_map(|o| match o.job {
|
||||
Job::Dig(t) | Job::Claim(t) | Job::Reinforce(t) => Some(t),
|
||||
_ => None,
|
||||
})
|
||||
.filter(|&t| self.ents.iter().filter(|o| o.alive && o.job == Job::Help(t)).count() < HELPERS_MAX)
|
||||
.collect();
|
||||
if tasks.is_empty() {
|
||||
return false;
|
||||
}
|
||||
let cur = e.tile();
|
||||
let near = |i: usize| tasks.iter().copied().find(|&t| i != t && adj4(i, t) || (i == t && self.map.t[t].walkable()));
|
||||
let Some(p) = self.map.search(cur, self.map.walk_cost(), |i| near(i).is_some(), true) else { return false };
|
||||
let end = p.last().copied().unwrap_or(cur);
|
||||
let Some(t) = near(end) else { return false };
|
||||
e.path = p;
|
||||
e.job = Job::Help(t);
|
||||
e.think = 1.5;
|
||||
true
|
||||
}
|
||||
|
||||
// free beds in the lairs: much room lets creatures arrive faster, little room slower
|
||||
pub fn free_beds(&self) -> usize {
|
||||
self.counts[1].min(30).saturating_sub(self.creature_count())
|
||||
@@ -1948,7 +1995,7 @@ impl Game {
|
||||
let (tx, ty) = xy(t);
|
||||
e.face = sgn(tx as f32 + 0.5 - e.x);
|
||||
e.working = true;
|
||||
e.timer += dt;
|
||||
e.timer += dt * self.help_speed(t);
|
||||
if e.timer >= 0.45 {
|
||||
e.timer = 0.0;
|
||||
let fxx = (e.x + tx as f32 + 0.5) * 0.5;
|
||||
@@ -2004,6 +2051,21 @@ impl Game {
|
||||
self.sfx(Sfx::Reinforce, tx, ty);
|
||||
}
|
||||
}
|
||||
Job::Help(t) => {
|
||||
// the task is done or nobody works on it any more: back to thinking
|
||||
if self.task_worker(t).is_none() || self.helpers(t) > HELPERS_MAX {
|
||||
e.job = Job::None;
|
||||
e.think = 0.2;
|
||||
return;
|
||||
}
|
||||
let (tx, _) = xy(t);
|
||||
e.face = sgn(tx as f32 + 0.5 - e.x);
|
||||
e.working = true;
|
||||
if e.think <= 0.0 {
|
||||
// every 1.5 s: is there own work again? (if not, the imp comes back to help)
|
||||
e.job = Job::None;
|
||||
}
|
||||
}
|
||||
Job::Reinforce(w) => {
|
||||
let t = self.map.t[w];
|
||||
if !adj4(cur, w) || t.kind != Kind::Earth || t.reinforced || t.marked {
|
||||
@@ -2014,7 +2076,7 @@ impl Game {
|
||||
let (wx, wy) = xy(w);
|
||||
e.face = sgn(wx as f32 + 0.5 - e.x);
|
||||
e.working = true;
|
||||
e.timer += dt;
|
||||
e.timer += dt * self.help_speed(w);
|
||||
if e.timer >= 2.5 {
|
||||
self.unreserve(me);
|
||||
e.job = Job::None;
|
||||
@@ -2032,7 +2094,7 @@ impl Game {
|
||||
return;
|
||||
}
|
||||
e.working = true;
|
||||
e.timer += dt;
|
||||
e.timer += dt * self.help_speed(t);
|
||||
// knight ground takes longer
|
||||
let knight = self.map.t[t].owner == Owner::Hero;
|
||||
if e.timer >= if knight { 3.0 } else { 1.0 } {
|
||||
@@ -2159,6 +2221,10 @@ impl Game {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// nothing else to do: help another imp (up to five at one task)
|
||||
if self.start_help(e) {
|
||||
return;
|
||||
}
|
||||
e.think = 1.0;
|
||||
self.wander(e, 3);
|
||||
}
|
||||
@@ -3813,6 +3879,7 @@ pub fn job_name(e: &Ent) -> &'static str {
|
||||
Job::Study(_) => "job.study",
|
||||
Job::Attack(_) => "job.fight",
|
||||
Job::TearBridge(_) => "job.tear_bridge",
|
||||
Job::Help(_) => "job.help",
|
||||
Job::AttackCore => {
|
||||
if e.working {
|
||||
"job.core_attack"
|
||||
@@ -4231,6 +4298,36 @@ mod tests {
|
||||
assert!(!g.map.t[idx(x0 - 1, y0)].reinforced && !g.map.t[idx(x0, y0 - 1)].reinforced && !g.map.t[idx(x0 - 1, y0 - 1)].reinforced);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn idle_imps_help_and_speed_up_work() {
|
||||
let mut g = Game::new(3, 64, 64);
|
||||
let (cx, cy) = g.map.core;
|
||||
for _ in 0..6 {
|
||||
g.spawn(EK::Worker, false, cx as f32 + 4.5, cy as f32 + 1.5, 1);
|
||||
}
|
||||
// a single earth tile to dig, next to the start area: one digs, the others help
|
||||
let (dx, dy) = (cx + 6, (cy - 3..=cy + 5).find(|&y| g.map.t[idx(cx + 6, y)].kind == Kind::Earth).expect("earth next to the start area"));
|
||||
// no other work: everything around is claimed already, no walls to reinforce
|
||||
for t in g.map.t.iter_mut() {
|
||||
if t.kind == Kind::Earth {
|
||||
t.reinforced = true;
|
||||
}
|
||||
}
|
||||
g.map.t[idx(dx, dy)].reinforced = false;
|
||||
g.map.t[idx(dx, dy)].marked = true;
|
||||
let mut best = 0.0f32;
|
||||
for _ in 0..200 {
|
||||
g.update(0.05);
|
||||
best = best.max(g.help_speed(idx(dx, dy)));
|
||||
if g.map.t[idx(dx, dy)].kind != Kind::Earth {
|
||||
break;
|
||||
}
|
||||
}
|
||||
println!("best working speed {}", best);
|
||||
assert!(best >= 1.75, "helpers join: {}", best);
|
||||
assert!(best <= 2.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn imps_reinforce_walls() {
|
||||
let mut g = Game::new(3, 64, 64);
|
||||
|
||||
Reference in new issue
Block a user