Delivered treasure fills the treasury tiles nearest to where the imp put it down

Imps already walk to the nearest treasury; the treasury display now places the delivery there
(nearest tiles that can take it) instead of the first free tiles anywhere. Test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-03 12:35:44 +01:00
1 parent 75232d95ee
commit 52d49b0243
1 file changed
+53 -10
+53 -10
View File
@@ -354,7 +354,7 @@ pub struct Game {
#[serde(default)]
pub reserve: f32, // value stored in the soul core (the rest lies in the treasury tiles)
#[serde(skip)]
pub incoming: Vec<(u8, f32)>, // treasures added since the last treasury update
pub incoming: Vec<(u8, f32, Option<usize>)>, // treasures added since the last treasury update (kind, value, delivered at)
#[serde(default)]
pub training_bill: f32, // training costs, paid on the next payday
#[serde(default)]
@@ -651,11 +651,16 @@ impl Game {
// adds treasure of one kind (as far as there is room); returns the value accepted
pub fn add_res(&mut self, amount: f32, res: u8) -> f32 {
self.add_res_at(amount, res, None)
}
// treasure delivered at a tile: it fills the treasury tiles nearest to that place
pub fn add_res_at(&mut self, amount: f32, res: u8, at: Option<usize>) -> f32 {
let cap = self.gold_cap();
let acc = amount.min((cap - self.gold).max(0.0));
self.gold += acc;
if acc > 0.0 {
self.incoming.push((res, acc));
self.incoming.push((res, acc, at));
}
acc
}
@@ -700,22 +705,37 @@ impl Game {
self.reserve = (self.reserve - need.max(0.0)).max(0.0);
} else if diff > 0.5 {
let mut left = diff;
let mut parts: Vec<(u8, f32)> = incoming;
let mut parts: Vec<(u8, f32, Option<usize>)> = incoming;
let typed: f32 = parts.iter().map(|p| p.1).sum();
if typed < left {
parts.push((RES_GOLD, left - typed));
parts.push((RES_GOLD, left - typed, None));
}
for (res, amount) in parts {
for (res, amount, at) in parts {
let mut a = amount.min(left);
left -= a;
// tiles holding this treasure first, then empty tiles
for pass in 0..2 {
for &i in &tiles {
// delivered treasure goes to the treasury tiles nearest to the place of delivery
let mut order = tiles.clone();
if let Some(at) = at {
let (ax, ay) = xy(at);
order.sort_by_key(|&i| {
let (x, y) = xy(i);
(x - ax).pow(2) + (y - ay).pow(2)
});
}
// without a place: tiles holding this treasure first, then empty tiles;
// with a place: strictly the nearest tiles that can take it
let passes: &[u8] = if at.is_some() { &[2] } else { &[0, 1] };
for &pass in passes {
for &i in &order {
if a <= 0.5 {
break;
}
let t = &mut self.map.t[i];
let fits = if pass == 0 { t.gold > 0 && t.res == res } else { t.gold <= 0 };
let fits = match pass {
0 => t.gold > 0 && t.res == res,
1 => t.gold <= 0,
_ => (t.gold > 0 && t.res == res && (t.gold as f32) < TREASURY_TILE) || t.gold <= 0,
};
if !fits {
continue;
}
@@ -2126,7 +2146,7 @@ impl Game {
Job::Deliver => {
if self.is_deposit(cur) {
let amount = e.carry as f32;
let got = self.add_res(amount, e.carry_res);
let got = self.add_res_at(amount, e.carry_res, Some(cur));
if got < amount {
self.hint(3, "hint.treasury_full");
}
@@ -4087,6 +4107,29 @@ mod tests {
assert!(g.reserve > 0.0 && g.reserve <= CORE_STORE + 1.0);
}
#[test]
fn treasure_lands_in_the_nearest_treasury() {
let mut g = Game::new(3, 64, 64);
let (cx, cy) = g.map.core;
// a second treasury far from the core ring
for y in cy - 9..=cy - 7 {
for x in cx + 10..=cx + 12 {
let t = &mut g.map.t[idx(x, y)];
t.kind = Kind::Treasury;
t.owner = Owner::Player;
t.gold = 0;
}
}
g.recount();
g.update(0.01);
let far = |g: &Game| -> i32 { (cy - 9..=cy - 7).flat_map(|y| (cx + 10..=cx + 12).map(move |x| (x, y))).map(|(x, y)| g.map.t[idx(x, y)].gold).sum() };
let before = far(&g);
let at = idx(cx + 11, cy - 8);
g.add_res_at(900.0, 2, Some(at));
g.update(0.01);
assert_eq!(far(&g) - before, 900, "the rubies lie where they were delivered");
}
#[test]
fn training_is_billed_on_payday() {
let mut g = Game::new(3, 64, 64);