// Creature equipment: when no trap or door is ordered, the forge makes armour and weapons in // three tiers - iron, steel (once steel walls are known) and rune (once the magic door, the // last discovery of the library, is known). Every finished piece goes to the strongest creature without anything that good yet: // more health and harder blows. The tier shows as a little coloured mark at the health bar. use crate::game::*; // tier 1..3: forge work, gold, health and attack factor pub const GEAR_WORK: [f32; 3] = [40.0, 70.0, 110.0]; pub const GEAR_GOLD: [f32; 3] = [60.0, 140.0, 280.0]; pub const GEAR_FACTOR: [f32; 4] = [1.0, 1.1, 1.22, 1.38]; pub fn gear_factor(gear: u8) -> f32 { GEAR_FACTOR[(gear as usize).min(3)] } impl Game { // the best tier the forge can make now (1..3) pub fn gear_tier(&self) -> u8 { if self.known(31) { 3 } else if self.known(19) { 2 } else { 1 } } // is a creature waiting for a piece of the best tier (and none in stock)? pub fn gear_wanted(&self) -> bool { let tier = self.gear_tier(); self.gear_stock.get(tier as usize - 1).copied().unwrap_or(0) == 0 && self.ents.iter().any(|e| e.alive && e.creature() && e.gear < tier) } // forge work without an order: the next piece of equipment pub(crate) fn forge_gear(&mut self, work: f32) { if !self.gear_wanted() { return; } let tier = self.gear_tier(); let k = tier as usize - 1; self.gear_work += work; if self.gear_work >= GEAR_WORK[k] && self.gold >= GEAR_GOLD[k] { self.gear_work -= GEAR_WORK[k]; self.gold -= GEAR_GOLD[k]; if self.gear_stock.len() < 3 { self.gear_stock.resize(3, 0); } self.gear_stock[k] += 1; } self.equip_from_stock(); } // finished pieces go to the strongest creatures that have nothing that good pub(crate) fn equip_from_stock(&mut self) { for k in (0..self.gear_stock.len().min(3)).rev() { while self.gear_stock[k] > 0 { let tier = k as u8 + 1; let best = (0..self.ents.len()) .filter(|&j| { let e = &self.ents[j]; e.alive && e.creature() && e.gear < tier }) .max_by(|&a, &b| { let (ea, eb) = (&self.ents[a], &self.ents[b]); (ea.level, ea.max_hp as i32).cmp(&(eb.level, eb.max_hp as i32)) }); let Some(j) = best else { break }; self.gear_stock[k] -= 1; let e = &mut self.ents[j]; let f = gear_factor(tier) / gear_factor(e.gear); e.max_hp *= f; e.hp *= f; e.gear = tier; let (x, y, kind) = (e.x, e.y, e.kind); self.fx(7, x, y, 0.0, 0.0, 0.6, 0xff40c0ff); self.msg("msg.gear_given", vec![format!("@gear.{}", tier), format!("@{}", st(kind).name)]); } } } } #[cfg(test)] mod tests { use super::*; #[test] fn the_forge_equips_the_strongest_creature() { let mut g = Game::new(4, 64, 64); let (cx, cy) = g.map.core; let weak = g.spawn(EK::Kobold, false, cx as f32 + 4.5, cy as f32 + 1.5, 1); let strong = g.spawn(EK::Ogre, false, cx as f32 + 5.5, cy as f32 + 1.5, 5); g.gold = 1000.0; assert!(g.gear_wanted()); let hp0 = g.ents.iter().find(|e| e.id == strong).unwrap().max_hp; g.forge_gear(GEAR_WORK[0] + 1.0); let s = g.ents.iter().find(|e| e.id == strong).unwrap(); let w = g.ents.iter().find(|e| e.id == weak).unwrap(); assert_eq!(s.gear, 1, "the ogre got the first piece"); assert_eq!(w.gear, 0); assert!(s.max_hp > hp0 && s.atk() > st(EK::Ogre).atk * (1.0 + 0.12 * 4.0)); assert!(g.gold < 1000.0); } }