105 lines
3.9 KiB
Rust
105 lines
3.9 KiB
Rust
// 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);
|
|
}
|
|
}
|