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:
1 parent
75232d95ee
commit
52d49b0243
1 file changed
+53
-10
+53
-10
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user