diff --git a/lang/de.lang b/lang/de.lang index a1f1ab4..74685f9 100644 --- a/lang/de.lang +++ b/lang/de.lang @@ -502,3 +502,4 @@ camp.m19.story = Drei Festen hüten die letzten Splitter vor der Zitadelle. Der camp.m20.title = Der Großmeister camp.m20.story = Großmeister Valerian Hellschild erwartet dich in seiner Zitadelle jenseits des großen Flusses, mit dem letzten Splitter und dem Tor des Ordens. Hundert Jahre lang hat er gebetet, poliert und sich vorbereitet. | Zerstöre sein Tor, hol dir den Splitter und mach deinen Kern wieder ganz. Ich bin direkt hinter dir. Ein gutes Stück hinter dir. camp.epilogue = Der letzte Splitter versinkt im Kern, und die Dunkelheit atmet wieder. Irgendwo dort oben schreibt der Orden der Lichtwacht einen sehr langen Bericht. | Krummfinger hat Tee gekocht. Du bist wieder ganz, Meister. Das Unterreich gehört dir. +lair.arrivals = {0} freie Plätze: Neue Kreaturen kommen mit {1}% Tempo diff --git a/lang/en.lang b/lang/en.lang index 637f51f..7e33d92 100644 --- a/lang/en.lang +++ b/lang/en.lang @@ -503,3 +503,4 @@ camp.m19.story = Three keeps hold the last shards before the citadel. The Order camp.m20.title = The Grand Master camp.m20.story = Grand Master Valerian Brightshield waits in his citadel beyond the great river, with the last shard and the gate of the order. He has prayed, polished and prepared for a hundred years. | Destroy his gate, take back the shard and make your core whole again. I will be right behind you. Well behind you. camp.epilogue = The last shard sinks into the core, and the darkness breathes again. Somewhere above, the Order of the Lightwatch is writing a very long report. | Crookfinger has made tea. You are whole, master. The underworld is yours. +lair.arrivals = {0} free beds: new creatures arrive at {1}% speed diff --git a/src/game.rs b/src/game.rs index 584b5a6..eeac51f 100644 --- a/src/game.rs +++ b/src/game.rs @@ -834,7 +834,7 @@ impl Game { if owned > 0 { self.portal_timer -= dt; if self.portal_timer <= 0.0 { - self.portal_timer = (20.0 + self.rng.f() * 14.0) / (owned as f32).sqrt(); + self.portal_timer = (20.0 + self.rng.f() * 14.0) / (owned as f32).sqrt() * self.lair_factor(); self.try_portal(); } } @@ -938,6 +938,15 @@ impl Game { } } + // 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()) + } + + pub fn lair_factor(&self) -> f32 { + (8.0 / (self.free_beds() as f32 + 2.0)).clamp(0.35, 3.0) + } + fn try_portal(&mut self) { let creatures = self.creature_count(); let lair = self.counts[1]; @@ -4052,6 +4061,23 @@ mod tests { assert!(g.gate_hp < GATE_HP, "the gate is shot at"); } + #[test] + fn more_lair_room_brings_creatures_faster() { + let mut g = Game::new(3, 64, 64); + g.counts[1] = 4; + let tight = g.lair_factor(); + g.counts[1] = 12; + let normal = g.lair_factor(); + g.counts[1] = 30; + let roomy = g.lair_factor(); + assert!(tight > normal && normal > roomy, "{} {} {}", tight, normal, roomy); + // creatures fill the lair: arrivals slow down again + for _ in 0..20 { + g.spawn(EK::Kobold, false, 10.0, 10.0, 1); + } + assert!(g.lair_factor() > roomy); + } + #[test] fn vaults_are_claimed_and_looted() { let (mut g, _) = fort_game(|x, y| crate::fortress::FortSpec { vaults: 2, vault_value: 1800, ..spec(x, y) }); diff --git a/src/ui.rs b/src/ui.rs index 1f3fed9..3b26256 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1119,7 +1119,11 @@ pub fn hover_info(g: &Game, ent: Option, tile: Option<(i32, i32)>) -> Vec let what = ["res.gold", "res.silver", "res.ruby", "res.diamond"][(t.res & 3) as usize]; out.push((trf("treasury.content", &[t.gold.to_string(), tr(what)]), GOLD)); } - Kind::Lair => out.push((tr(if t.bed != 0 { "lair.taken" } else { "lair.free" }), DIM)), + Kind::Lair => { + out.push((tr(if t.bed != 0 { "lair.taken" } else { "lair.free" }), DIM)); + let speed = (100.0 / g.lair_factor()).round() as i32; + out.push((trf("lair.arrivals", &[g.free_beds().to_string(), speed.to_string()]), if speed >= 100 { GREEN } else { GOLD })); + } Kind::Torture => { let used = g.ents.iter().filter(|e| e.alive && e.hero && e.captive != 0).count(); out.push((trf("info.prison", &[used.to_string(), (g.counts[5] / 2).to_string()]), DIM));