Creature personality: favourite rooms, hatreds and brawls, abilities learned with the level
- every kind loves one room (working there lifts the mood) and cannot stand certain other kinds; close by they sour each other's mood and now and then brawl (they stop at 30% health) - learned abilities: imps dig faster, kobold dash, spider web, mage shadow step, ogre stomp, jailer shackle, priestess healing pulse, chainbreaker whirlwind, bat shriek, gargoyle stone form, owl volley, wyrm inferno, cogling bomb - bestiary lists likes, hatreds and the ability (highlighted from its level on) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
240e89d9cb
commit
2c8db33a69
7 files changed
+403
-4
No files matched your search
+43
-4
@@ -183,6 +183,7 @@ pub enum Job {
|
||||
Help(usize), // imp with nothing else to do helps another imp at this tile
|
||||
Forge(usize), // creature works in the forge (traps and doors)
|
||||
Install(u32), // imp carries a finished trap or door (id) to its spot and installs it
|
||||
Brawl(u32), // two creatures that cannot stand each other scuffle (never to the death)
|
||||
}
|
||||
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
@@ -242,6 +243,10 @@ pub struct Ent {
|
||||
pub invis: f32, // spell: seconds of invisibility left (until it attacks)
|
||||
#[serde(default)]
|
||||
pub plague: f32, // spell: seconds of plague left (poison that spreads)
|
||||
#[serde(default)]
|
||||
pub ab_cd: f32, // cooldown of the learned ability
|
||||
#[serde(default)]
|
||||
pub mood_t: f32, // seconds until the next mood check (likes and hatreds)
|
||||
pub guard: bool, // stays at its home (gate defenders, treasure guards)
|
||||
#[serde(default)]
|
||||
pub home: Option<(f32, f32)>, // guard post
|
||||
@@ -311,6 +316,8 @@ impl Ent {
|
||||
shield: 0.0,
|
||||
invis: 0.0,
|
||||
plague: 0.0,
|
||||
ab_cd: 0.0,
|
||||
mood_t: 0.0,
|
||||
guard: false,
|
||||
home: None,
|
||||
patrol: Vec::new(),
|
||||
@@ -1732,7 +1739,7 @@ impl Game {
|
||||
};
|
||||
let target = match e.job {
|
||||
Job::Dig(t) | Job::Reinforce(t) | Job::Help(t) | Job::TearBridge(t) => tile(t),
|
||||
Job::Attack(id) | Job::Torment(id) | Job::Carry(id) => self.find(id).map(|j| (self.ents[j].x, self.ents[j].y)),
|
||||
Job::Attack(id) | Job::Torment(id) | Job::Carry(id) | Job::Brawl(id) => self.find(id).map(|j| (self.ents[j].x, self.ents[j].y)),
|
||||
Job::Repair(id) | Job::AttackTower(id) => self.towers.iter().find(|t| t.id == id).map(|t| t.pos()),
|
||||
_ => None,
|
||||
};
|
||||
@@ -1855,7 +1862,7 @@ impl Game {
|
||||
}
|
||||
}
|
||||
|
||||
fn los(&self, x0: f32, y0: f32, x1: f32, y1: f32) -> bool {
|
||||
pub(crate) fn los(&self, x0: f32, y0: f32, x1: f32, y1: f32) -> bool {
|
||||
let dx = x1 - x0;
|
||||
let dy = y1 - y0;
|
||||
let d = (dx * dx + dy * dy).sqrt();
|
||||
@@ -1918,6 +1925,9 @@ impl Game {
|
||||
e.path.clear();
|
||||
e.face = sgn(tx - e.x);
|
||||
e.working = true;
|
||||
if e.cd <= 0.0 && self.use_ability(e, j) {
|
||||
return true;
|
||||
}
|
||||
if e.cd <= 0.0 {
|
||||
e.cd = s.interval;
|
||||
self.hit(e, j);
|
||||
@@ -2007,7 +2017,7 @@ impl Game {
|
||||
.ents
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter(|(o, x)| *o != j && x.alive && !x.held && x.side() != me && x.captive == 0 && (x.x - tx).powi(2) + (x.y - ty).powi(2) < 1.44)
|
||||
.filter(|(o, x)| *o != j && x.alive && !x.held && x.side() != me && x.captive == 0 && (x.x - tx).powi(2) + (x.y - ty).powi(2) < if crate::personality::knows(e, "inferno") { 6.25 } else { 1.44 })
|
||||
.map(|(o, _)| o)
|
||||
.collect();
|
||||
for o in splash {
|
||||
@@ -2023,6 +2033,27 @@ impl Game {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// learned abilities that come with a blow
|
||||
if crate::personality::knows(e, "web") && self.rng.f() < 0.3 {
|
||||
self.ents[j].slow = self.ents[j].slow.max(3.0);
|
||||
}
|
||||
if crate::personality::knows(e, "shackle") && self.rng.f() < 0.2 {
|
||||
self.ents[j].stun = self.ents[j].stun.max(2.0);
|
||||
}
|
||||
if crate::personality::knows(e, "volley") {
|
||||
let me = e.side();
|
||||
let second = self.ents.iter().enumerate().find(|(o, x)| *o != j && x.alive && !x.held && x.side() != me && x.captive == 0 && (x.x - tx).powi(2) + (x.y - ty).powi(2) < 6.0).map(|(o, _)| o);
|
||||
if let Some(o) = second {
|
||||
let (ox, oy) = (self.ents[o].x, self.ents[o].y);
|
||||
self.shot(e.x, e.y, 0.8, ox, oy, 0.45, 0.25, 0xffffa060);
|
||||
self.style_last_fx(crate::particles::ST_ARCANE);
|
||||
self.ents[o].hp -= dmg * 0.6;
|
||||
self.ents[o].flash = 0.15;
|
||||
if self.ents[o].hp <= 0.0 {
|
||||
self.kill(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
let my_id = e.id;
|
||||
e.invis = 0.0; // attacking breaks invisibility
|
||||
let t = &mut self.ents[j];
|
||||
@@ -2605,7 +2636,8 @@ impl Game {
|
||||
let (tx, ty) = xy(t);
|
||||
e.face = sgn(tx as f32 + 0.5 - e.x);
|
||||
e.working = true;
|
||||
e.timer += dt * self.help_speed(t);
|
||||
let skilled = if crate::personality::knows(e, "dig") { 1.25 } else { 1.0 };
|
||||
e.timer += dt * self.help_speed(t) * skilled;
|
||||
if e.timer >= 0.45 {
|
||||
e.timer = 0.0;
|
||||
let fxx = (e.x + tx as f32 + 0.5) * 0.5;
|
||||
@@ -3008,6 +3040,12 @@ impl Game {
|
||||
self.hint(20, "hint.chainbreaker_restless");
|
||||
}
|
||||
}
|
||||
self.mood_check(e, dt);
|
||||
self.passive_abilities(e);
|
||||
if let Job::Brawl(id) = e.job {
|
||||
self.brawl(e, id, dt);
|
||||
return;
|
||||
}
|
||||
if let Job::Attack(id) = e.job {
|
||||
if !self.attack_job(e, id, dt) {
|
||||
e.job = Job::None;
|
||||
@@ -4751,6 +4789,7 @@ pub fn job_name(e: &Ent) -> &'static str {
|
||||
Job::Eat(_) => "job.eat",
|
||||
Job::Train(_) => "job.train",
|
||||
Job::Forge(_) => "job.forge",
|
||||
Job::Brawl(_) => "job.brawl",
|
||||
Job::Install(_) => "job.install",
|
||||
Job::Study(_) => "job.study",
|
||||
Job::Attack(_) => "job.fight",
|
||||
|
||||
Reference in new issue
Block a user