Chainbreaker: rare, very strong original creature (twin-axe cleave, restless without fights)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
05ba88359e
commit
c5b7896609
7 files changed
+111
-7
No files matched your search
+76
@@ -94,6 +94,7 @@ pub enum EK {
|
||||
Paladin,
|
||||
Jailer, // attracted by the torture chamber
|
||||
Priestess, // attracted by the temple
|
||||
Chainbreaker, // rare and very strong
|
||||
}
|
||||
|
||||
pub struct St {
|
||||
@@ -123,6 +124,7 @@ pub fn st(k: EK) -> St {
|
||||
EK::Paladin => s("unit.paladin", 700.0, 26.0, 1.0, 1.7, 1.2, 220, 1000),
|
||||
EK::Jailer => s("unit.jailer", 260.0, 16.0, 1.0, 1.6, 1.3, 140, 0),
|
||||
EK::Priestess => s("unit.priestess", 70.0, 14.0, 5.0, 2.0, 1.6, 120, 0),
|
||||
EK::Chainbreaker => s("unit.chainbreaker", 600.0, 36.0, 1.1, 2.3, 1.1, 400, 0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -720,6 +722,13 @@ impl Game {
|
||||
if self.counts[6] >= 6 {
|
||||
opts.push((EK::Priestess, 2));
|
||||
}
|
||||
// the Chainbreaker only visits big dungeons with experienced creatures and a rich treasury
|
||||
let room_tiles: usize = self.counts.iter().sum();
|
||||
let veteran = self.ents.iter().any(|e| e.alive && e.creature() && e.level >= 5);
|
||||
let has_one = self.ents.iter().any(|e| e.alive && e.kind == EK::Chainbreaker && !e.hero);
|
||||
if room_tiles >= 40 && veteran && self.gold >= 4000.0 && !has_one {
|
||||
opts.push((EK::Chainbreaker, 1));
|
||||
}
|
||||
let total: u32 = opts.iter().map(|o| o.1).sum();
|
||||
let mut r = self.rng.next() % total;
|
||||
let mut kind = EK::Kobold;
|
||||
@@ -1160,6 +1169,25 @@ impl Game {
|
||||
self.sfx(Sfx::MeleeHit, tx, ty);
|
||||
}
|
||||
e.xp += dmg * 0.6;
|
||||
if e.kind == EK::Chainbreaker {
|
||||
// twin axes: a second enemy next to the Chainbreaker takes half damage
|
||||
let side = !e.hero;
|
||||
let second = self
|
||||
.ents
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(o, x)| *o != j && x.alive && !x.held && x.hero == side && x.captive == 0 && (x.x - e.x).powi(2) + (x.y - e.y).powi(2) < 2.6)
|
||||
.map(|(o, _)| o);
|
||||
if let Some(o) = second {
|
||||
let (ox, oy) = (self.ents[o].x, self.ents[o].y);
|
||||
self.fx(2, ox, oy - 0.2, 0.0, 0.0, 0.25, 0xff40a0ff);
|
||||
self.ents[o].hp -= dmg * 0.5;
|
||||
self.ents[o].flash = 0.15;
|
||||
if self.ents[o].hp <= 0.0 {
|
||||
self.kill(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
if e.kind == EK::Priestess {
|
||||
// fireball: half damage to enemies around the target
|
||||
self.fx(3, tx, ty, 0.0, 0.0, 0.5, 0xff3080ff);
|
||||
@@ -1617,6 +1645,15 @@ impl Game {
|
||||
}
|
||||
}
|
||||
}
|
||||
if e.kind == EK::Chainbreaker {
|
||||
// restless: without a fight for two minutes its mood sinks (skill_cd counts down as idle time)
|
||||
if matches!(e.job, Job::Attack(_)) {
|
||||
e.skill_cd = 0.0;
|
||||
} else if e.skill_cd < -120.0 {
|
||||
e.happy -= 0.005 * dt;
|
||||
self.hint(20, "hint.chainbreaker_restless");
|
||||
}
|
||||
}
|
||||
if let Job::Attack(id) = e.job {
|
||||
if !self.attack_job(e, id, dt) {
|
||||
e.job = Job::None;
|
||||
@@ -3270,5 +3307,44 @@ mod tests {
|
||||
assert!(g.pickup(Some(imp)));
|
||||
assert!(!g.drop(40.5, 40.5) || g.map.t[idx(40, 40)].owner == Owner::Player);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn chainbreaker_visits_only_great_dungeons() {
|
||||
let mut g = Game::new(8, 64, 64);
|
||||
g.gold = 9000.0;
|
||||
// conditions not met: no Chainbreaker among many portal visits
|
||||
let (px, py) = g.map.portals[0];
|
||||
for y in 0..3 {
|
||||
for x in 0..3 {
|
||||
g.map.t[idx(px + x, py + y)].owner = Owner::Player;
|
||||
}
|
||||
}
|
||||
let (cx, cy) = g.map.core;
|
||||
g.build_rect(1, cx + 4, cy - 3, cx + 5, cy + 5); // lair 18
|
||||
g.build_rect(2, cx - 3, cy - 3, cx - 2, cy + 5); // farm 18
|
||||
g.recount();
|
||||
for _ in 0..60 {
|
||||
g.try_portal();
|
||||
}
|
||||
assert!(!g.ents.iter().any(|e| e.kind == EK::Chainbreaker));
|
||||
// a veteran creature makes it possible (rooms 16 ring + 36 = 52 tiles, gold 9000)
|
||||
if let Some(e) = g.ents.iter_mut().find(|e| e.creature()) {
|
||||
e.level = 5;
|
||||
}
|
||||
for e in g.ents.iter_mut().filter(|e| e.creature()) {
|
||||
e.alive = false; // free beds
|
||||
}
|
||||
g.ents.retain(|e| e.alive);
|
||||
g.spawn(EK::Kobold, false, cx as f32, cy as f32, 6);
|
||||
let mut seen = false;
|
||||
for _ in 0..400 {
|
||||
g.gold = 9000.0;
|
||||
g.try_portal();
|
||||
seen |= g.ents.iter().any(|e| e.kind == EK::Chainbreaker);
|
||||
g.ents.retain(|e| !e.creature() || e.kind == EK::Chainbreaker || e.level >= 5);
|
||||
}
|
||||
assert!(seen);
|
||||
assert!(g.ents.iter().filter(|e| e.kind == EK::Chainbreaker).count() <= 1);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user