Units face eight directions and animate their actions
- five sprite views per kind and gear tier (front, front three-quarter, profile, back three-quarter, back), mirrored for the other three directions; chosen from the unit's facing and the camera - units face where they walk, dig, repair or fight - animations: imps stomp while claiming (squash on landing), swing while digging, hammer while repairing; lunges in combat, punches in training, nodding while eating, swaying while reading or praying, breathing when idle, leaning back with loads, lying in bed, chained creatures pulling at their chains, flyers flapping Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1016ee5d6c
commit
d06ce2edb5
3 files changed
+234
-21
No files matched your search
+82
-4
@@ -1085,6 +1085,9 @@ impl R3d {
|
||||
0.5
|
||||
}
|
||||
};
|
||||
// the camera's right and viewing direction on the ground (for the eight views)
|
||||
let rg = vec2(r.x, r.z).normalize_or_zero();
|
||||
let tg = vec2(toward.x, toward.z).normalize_or_zero();
|
||||
let mut mb = MB::new();
|
||||
let sw = SPRITE_TEX_W;
|
||||
let sh = SPRITE_TEX_H;
|
||||
@@ -1095,12 +1098,81 @@ impl R3d {
|
||||
let w = sprite_size(e.kind);
|
||||
let h = w;
|
||||
let sleeping = matches!(e.job, Job::Sleep(_)) && !e.moving;
|
||||
// which of the eight directions the unit shows (five views, three of them mirrored)
|
||||
let d = vec2(e.dir.cos(), e.dir.sin());
|
||||
let ang = d.dot(rg).atan2(d.dot(tg));
|
||||
let view = ((ang / std::f32::consts::FRAC_PI_4).round().abs() as u32).min(4);
|
||||
let mirror = ang < 0.0;
|
||||
let fwd = if mirror { -1.0 } else { 1.0 }; // screen direction the unit faces
|
||||
let ph = t * 10.0 + e.id as f32 * 1.7;
|
||||
let (mut sx, mut sy, mut lunge) = (1.0f32, 1.0f32, 0.0f32);
|
||||
let mut bob = if e.moving { (e.anim * 12.0).sin().abs() * 0.06 } else { 0.0 };
|
||||
let mut rot = 0.0;
|
||||
if e.moving {
|
||||
// a little sway while walking; loads are carried leaning back
|
||||
rot = (e.anim * 12.0).sin() * 0.05;
|
||||
if e.carry > 0 || matches!(e.job, Job::Carry(_)) {
|
||||
rot += 0.12 * fwd;
|
||||
bob *= 0.6;
|
||||
}
|
||||
} else if e.working && !sleeping {
|
||||
match e.job {
|
||||
// claiming: stomping the ground, squashed on landing
|
||||
Job::Claim(_) | Job::Help(_) => {
|
||||
let k = (ph * 1.1).sin();
|
||||
bob = k.max(0.0) * 0.13;
|
||||
if k < 0.0 {
|
||||
sy = 1.0 + k * 0.14;
|
||||
sx = 1.0 - k * 0.1;
|
||||
}
|
||||
}
|
||||
// digging: a swing at the wall
|
||||
Job::Dig(_) | Job::TearBridge(_) => {
|
||||
let k = (ph * 1.3).sin();
|
||||
rot = -(k * 0.35).max(-0.12) * fwd;
|
||||
lunge = k.max(0.0) * 0.07;
|
||||
}
|
||||
// hammering
|
||||
Job::Reinforce(_) | Job::Repair(_) => {
|
||||
let k = (ph * 2.2).sin();
|
||||
rot = -k.max(0.0) * 0.25 * fwd;
|
||||
bob = (ph * 2.2).cos().max(0.0) * 0.03;
|
||||
}
|
||||
// fighting: lunging at the enemy
|
||||
Job::Attack(_) | Job::AttackTower(_) | Job::AttackCore | Job::Torment(_) => {
|
||||
let k = (ph * 0.9).sin().max(0.0).powi(2);
|
||||
lunge = k * 0.14;
|
||||
rot = -k * 0.2 * fwd;
|
||||
}
|
||||
// training: quick punches
|
||||
Job::Train(_) => {
|
||||
let k = (ph * 1.6).sin();
|
||||
lunge = k.max(0.0) * 0.08;
|
||||
rot = -k * 0.12 * fwd;
|
||||
bob = (ph * 3.2).sin().abs() * 0.03;
|
||||
}
|
||||
// eating: nodding over the food
|
||||
Job::Eat(_) => {
|
||||
sy = 1.0 - (ph * 0.8).sin().abs() * 0.1;
|
||||
rot = -(ph * 0.8).sin().abs() * 0.12 * fwd;
|
||||
}
|
||||
// reading and praying: a slow sway
|
||||
Job::Study(_) | Job::Pray(_) | Job::HealCore => rot = (t * 1.8 + e.id as f32).sin() * 0.07,
|
||||
_ => rot = (ph * 1.4).sin() * 0.15,
|
||||
}
|
||||
} else if !sleeping {
|
||||
// breathing
|
||||
sy = 1.0 + (t * 2.4 + e.id as f32).sin() * 0.025;
|
||||
}
|
||||
// flying creatures hover and flap (on their perch while sleeping)
|
||||
if flies(e.kind) && !sleeping {
|
||||
bob = 0.45 + (t * 7.0 + e.anim * 3.0).sin() * 0.06;
|
||||
sx *= 1.0 + (t * 14.0 + e.id as f32).sin() * 0.06;
|
||||
} else if sleeping {
|
||||
// asleep: lying in bed
|
||||
rot = std::f32::consts::FRAC_PI_2 * 0.9 * fwd;
|
||||
bob = 0.04;
|
||||
}
|
||||
let mut rot = if e.working && !sleeping { (t * 14.0 + e.anim).sin() * 0.15 } else { 0.0 };
|
||||
// prisoners: lying unconscious, carried above an imp, or shaking in the torture chamber
|
||||
match e.captive {
|
||||
1 => rot = std::f32::consts::FRAC_PI_2,
|
||||
@@ -1111,11 +1183,16 @@ impl R3d {
|
||||
3 => rot = (t * 25.0 + e.anim).sin() * 0.08,
|
||||
_ => {}
|
||||
}
|
||||
if e.chained {
|
||||
// pulling at the chains
|
||||
rot = (t * 3.0 + e.id as f32).sin() * 0.1;
|
||||
lunge = 0.0;
|
||||
}
|
||||
let white = e.flash > 0.07;
|
||||
let src = Gfx::sprite_cell(e.kind, white, e.level);
|
||||
let src = Gfx::sprite_cell_view(e.kind, white, e.level, if e.captive != 0 { 0 } else { view });
|
||||
let (mut u0, v0) = (src.x / sw, src.y / sh);
|
||||
let (mut u1, v1) = ((src.x + src.w) / sw, (src.y + src.h) / sh);
|
||||
if e.face < 0.0 {
|
||||
if mirror {
|
||||
std::mem::swap(&mut u0, &mut u1);
|
||||
}
|
||||
let a = amb(e.x, e.y) * if sleeping || e.captive == 1 { 0.6 } else { 1.0 };
|
||||
@@ -1128,9 +1205,10 @@ impl R3d {
|
||||
};
|
||||
// pulled towards the camera: unchanged on screen (orthographic), but walls behind
|
||||
// the unit no longer cut into the sprite
|
||||
let base = vec3(e.x, bob, e.y) + toward * 0.45;
|
||||
let base = vec3(e.x, bob, e.y) + toward * 0.45 + r * (lunge * fwd);
|
||||
let (sr, cr) = rot.sin_cos();
|
||||
let corner = |dx: f32, dy: f32| {
|
||||
let (dx, dy) = (dx * sx, dy * sy);
|
||||
let x = dx * cr - dy * sr;
|
||||
let y = dx * sr + dy * cr;
|
||||
base + r * x + Vec3::Y * y
|
||||
|
||||
Reference in new issue
Block a user