Tower shots start at the tower top (arrow battlements, cannon muzzle, magic orb)
Effects now carry start/end heights; leftover 2D height offsets that moved projectiles and hit sparks sideways on the map are removed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
4d1bf30586
commit
6f037294c0
2 files changed
+54
-23
No files matched your search
+50
-15
@@ -268,6 +268,8 @@ pub struct Fx {
|
||||
pub t: f32,
|
||||
pub max: f32,
|
||||
pub col: u32,
|
||||
pub h: f32, // height of the start point (3D)
|
||||
pub h2: f32, // height of the end point (projectiles)
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
@@ -587,8 +589,24 @@ impl Game {
|
||||
}
|
||||
|
||||
pub fn fx(&mut self, kind: u8, x: f32, y: f32, x2: f32, y2: f32, max: f32, col: u32) {
|
||||
let h = if kind == 0 { 0.45 } else { 0.4 };
|
||||
if self.fx.len() < 400 {
|
||||
self.fx.push(Fx { kind, x, y, x2, y2, t: 0.0, max, col });
|
||||
self.fx.push(Fx { kind, x, y, x2, y2, t: 0.0, max, col, h, h2: 0.45 });
|
||||
}
|
||||
}
|
||||
|
||||
// projectile from (x, y) at height h to (x2, y2) at height h2
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn shot(&mut self, x: f32, y: f32, h: f32, x2: f32, y2: f32, h2: f32, max: f32, col: u32) {
|
||||
if self.fx.len() < 400 {
|
||||
self.fx.push(Fx { kind: 0, x, y, x2, y2, t: 0.0, max, col, h, h2 });
|
||||
}
|
||||
}
|
||||
|
||||
// effect at a given height
|
||||
pub fn fx_at(&mut self, kind: u8, x: f32, y: f32, h: f32, max: f32, col: u32) {
|
||||
if self.fx.len() < 400 {
|
||||
self.fx.push(Fx { kind, x, y, x2: 0.0, y2: 0.0, t: 0.0, max, col, h, h2: h });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -915,7 +933,7 @@ impl Game {
|
||||
let (gx, gy) = self.gate_center();
|
||||
if s.range > 1.5 {
|
||||
let col = if e.kind == EK::Mage { 0xffff44cc } else { 0xff70b0d0 };
|
||||
self.fx(0, e.x, e.y - 0.3, gx, gy, 0.25, col);
|
||||
self.fx(0, e.x, e.y, gx, gy, 0.25, col);
|
||||
} else {
|
||||
let fx = (e.x + gx) * 0.5;
|
||||
let fy = (e.y + gy) * 0.5;
|
||||
@@ -1111,7 +1129,7 @@ impl Game {
|
||||
let (dx, dy) = norm(tx - e.x, ty - e.y);
|
||||
let (nx, ny) = (e.x + dx * 0.9, e.y + dy * 0.9);
|
||||
if self.map.walk(nx as i32, ny as i32) {
|
||||
self.fx(0, tx, ty - 0.3, e.x, e.y - 0.3, 0.3, 0xff8090a0);
|
||||
self.fx(0, tx, ty, e.x, e.y, 0.3, 0xff8090a0);
|
||||
self.sfx(Sfx::Capture, e.x, e.y);
|
||||
let t = &mut self.ents[j];
|
||||
t.x = nx;
|
||||
@@ -1162,10 +1180,10 @@ impl Game {
|
||||
EK::Priestess => 0xff3080ff,
|
||||
_ => 0xff70b0d0,
|
||||
};
|
||||
self.fx(0, e.x, e.y - 0.3, tx, ty - 0.3, 0.25, col);
|
||||
self.fx(0, e.x, e.y, tx, ty, 0.25, col);
|
||||
self.sfx(Sfx::Shoot, e.x, e.y);
|
||||
} else {
|
||||
self.fx(2, tx, ty - 0.2, 0.0, 0.0, 0.25, 0xffffffff);
|
||||
self.fx(2, tx, ty, 0.0, 0.0, 0.25, 0xffffffff);
|
||||
self.sfx(Sfx::MeleeHit, tx, ty);
|
||||
}
|
||||
e.xp += dmg * 0.6;
|
||||
@@ -1180,7 +1198,7 @@ impl Game {
|
||||
.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.fx(2, ox, oy, 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 {
|
||||
@@ -2603,14 +2621,21 @@ impl Game {
|
||||
self.towers[k].flash = 0.15;
|
||||
let (px, py) = t.pos();
|
||||
let (ex, ey) = (self.ents[j].x, self.ents[j].y);
|
||||
self.towers[k].aim = (ey - py).atan2(ex - px);
|
||||
let aim = (ey - py).atan2(ex - px);
|
||||
self.towers[k].aim = aim;
|
||||
// where the shot leaves the tower (see the tower models in render3d.rs)
|
||||
let (mx, my, mh) = match t.kind {
|
||||
TowerKind::Arrow => (px, py, t.top() + 0.25),
|
||||
TowerKind::Cannon => (px + aim.cos() * 0.55, py + aim.sin() * 0.55, t.top() + 0.2),
|
||||
TowerKind::Magic => (px, py, t.top() + 0.45),
|
||||
};
|
||||
let dmg = t.dmg();
|
||||
match t.kind {
|
||||
TowerKind::Arrow => {
|
||||
// the arrow pierces up to two more heroes behind the target
|
||||
let dir = norm(ex - px, ey - py);
|
||||
let end = (px + dir.0 * t.range(), py + dir.1 * t.range());
|
||||
self.fx(0, px, py - 0.6, end.0, end.1, 0.3, 0xffd0e0f0);
|
||||
self.shot(mx, my, mh, end.0, end.1, 0.45, 0.3, 0xffd0e0f0);
|
||||
self.sfx(Sfx::Shoot, px, py);
|
||||
let mut hits = vec![(j, dmg)];
|
||||
for (o, e) in self.ents.iter().enumerate() {
|
||||
@@ -2632,9 +2657,9 @@ impl Game {
|
||||
}
|
||||
TowerKind::Cannon => {
|
||||
// splash damage and knockback around the impact
|
||||
self.fx(0, px, py - 0.8, ex, ey, 0.35, 0xff303030);
|
||||
self.shot(mx, my, mh, ex, ey, 0.4, 0.35, 0xff303030);
|
||||
self.fx(3, ex, ey, 0.0, 0.0, 0.6, 0xff4070a0);
|
||||
self.fx(2, ex, ey - 0.2, 0.0, 0.0, 0.3, 0xff60c0ff);
|
||||
self.fx(2, ex, ey, 0.0, 0.0, 0.3, 0xff60c0ff);
|
||||
self.sfx(Sfx::Cannon, px, py);
|
||||
let mut hits = Vec::new();
|
||||
for (o, e) in self.ents.iter().enumerate() {
|
||||
@@ -2665,7 +2690,7 @@ impl Game {
|
||||
}
|
||||
TowerKind::Magic => {
|
||||
// sure hit, slows, jumps to a second hero
|
||||
self.fx(0, px, py - 1.0, ex, ey, 0.2, 0xffff60d0);
|
||||
self.shot(mx, my, mh, ex, ey, 0.45, 0.2, 0xffff60d0);
|
||||
self.sfx(Sfx::Magic, px, py);
|
||||
self.ents[j].slow = 2.0;
|
||||
let chain = self
|
||||
@@ -2697,6 +2722,7 @@ impl Game {
|
||||
fn attack_tower(&mut self, e: &mut Ent, id: u32, dt: f32) -> bool {
|
||||
let Some(k) = self.towers.iter().position(|t| t.id == id) else { return false };
|
||||
let (tx, ty) = self.towers[k].pos();
|
||||
let hit_h = self.towers[k].top() * 0.6;
|
||||
let s = st(e.kind);
|
||||
let d = ((tx - e.x).powi(2) + (ty - e.y).powi(2)).sqrt();
|
||||
if d <= s.range + 0.75 && (s.range < 1.5 || self.los(e.x, e.y, tx, ty)) {
|
||||
@@ -2707,9 +2733,9 @@ impl Game {
|
||||
e.cd = s.interval;
|
||||
let dmg = e.atk() * (0.85 + self.rng.f() * 0.3);
|
||||
if s.range > 1.5 {
|
||||
self.fx(0, e.x, e.y - 0.3, tx, ty - 0.5, 0.25, 0xff70b0d0);
|
||||
self.shot(e.x, e.y, 0.45, tx, ty, hit_h, 0.25, 0xff70b0d0);
|
||||
} else {
|
||||
self.fx(2, tx, ty - 0.4, 0.0, 0.0, 0.25, 0xffffffff);
|
||||
self.fx_at(2, tx, ty, hit_h, 0.25, 0xffffffff);
|
||||
}
|
||||
self.sfx(Sfx::MeleeHit, tx, ty);
|
||||
let t = &mut self.towers[k];
|
||||
@@ -2836,7 +2862,7 @@ impl Game {
|
||||
if matches!(e.job, Job::Sleep(_)) {
|
||||
e.job = Job::None; // woken up
|
||||
}
|
||||
self.fx(2, x, y - 0.3, 0.0, 0.0, 0.3, 0xffffffff);
|
||||
self.fx(2, x, y, 0.0, 0.0, 0.3, 0xffffffff);
|
||||
self.sfx(Sfx::Slap, x, y);
|
||||
return 1;
|
||||
}
|
||||
@@ -2853,7 +2879,7 @@ impl Game {
|
||||
}
|
||||
self.punch_ready = self.time + 0.5;
|
||||
self.ents[j].stun = 1.0;
|
||||
self.fx(2, x, y - 0.3, 0.0, 0.0, 0.35, 0xff40e0ff);
|
||||
self.fx(2, x, y, 0.0, 0.0, 0.35, 0xff40e0ff);
|
||||
self.fx(7, x, y, 0.0, 0.0, 0.4, 0xff40e0ff);
|
||||
self.sfx(Sfx::Punch, x, y);
|
||||
self.tower_damage(j, 15.0);
|
||||
@@ -2987,6 +3013,15 @@ impl Tower {
|
||||
let (x, y) = xy(self.tile);
|
||||
(x as f32 + 0.5, y as f32 + 0.5)
|
||||
}
|
||||
|
||||
// height of the tower body (3D units); grows with the level
|
||||
pub fn top(&self) -> f32 {
|
||||
match self.kind {
|
||||
TowerKind::Arrow => 1.0 + 0.3 * (self.level - 1) as f32,
|
||||
TowerKind::Cannon => 0.6 + 0.15 * (self.level - 1) as f32,
|
||||
TowerKind::Magic => 1.2 + 0.3 * (self.level - 1) as f32,
|
||||
}
|
||||
}
|
||||
// gold spent on this tower so far
|
||||
pub fn invested(&self) -> i32 {
|
||||
tower_stats(self.kind).cost[..self.level as usize].iter().sum()
|
||||
|
||||
Reference in new issue
Block a user