feat: bigger characters (16 sub-bodies), wider terminal viewport
- Entity: 16 sub-bodies (was 7), radius 0.7 (was 0.4), full humanoid shape: head, shoulders, torso, arms, hips, legs, feet - Terminal: viewport up to 250x80 (was 120x50), uses full terminal size - Camera: uses renderer viewport dimensions instead of hardcoded values - Physics: constraint correction clamped to 0.5 cells max, 8 substeps (was 4), gravity 0.04 (was 0.08), collision pass after each constraint iteration to prevent tunneling with larger bodies - Player: move_speed 0.3, jump_force 1.2 for bigger body - Spawn: 6 cells above surface (was 4) for taller body
This commit is contained in:
+57
-17
@@ -91,33 +91,73 @@ impl Entity {
|
||||
self.bodies.clear();
|
||||
self.constraints.clear();
|
||||
|
||||
let r = 0.4;
|
||||
let r = 0.7;
|
||||
let mat = match self.kind {
|
||||
EntityKind::Player => MaterialId::Flesh,
|
||||
EntityKind::Goblin => MaterialId::Flesh,
|
||||
EntityKind::Corpse => MaterialId::Flesh,
|
||||
};
|
||||
|
||||
self.bodies.push(SubBody::new(cx, cy - 2.0, r, mat));
|
||||
self.bodies.push(SubBody::new(cx, cy - 1.0, r, mat));
|
||||
self.bodies.push(SubBody::new(cx - 0.8, cy - 1.0, r, mat));
|
||||
self.bodies.push(SubBody::new(cx + 0.8, cy - 1.0, r, mat));
|
||||
self.bodies.push(SubBody::new(cx - 0.5, cy, r, mat));
|
||||
self.bodies.push(SubBody::new(cx + 0.5, cy, r, mat));
|
||||
self.bodies.push(SubBody::new(cx, cy + 1.0, r, MaterialId::Bone));
|
||||
// Head
|
||||
self.bodies.push(SubBody::new(cx, cy - 3.5, r, mat)); // 0: head top
|
||||
self.bodies.push(SubBody::new(cx - 0.8, cy - 3.0, r, mat)); // 1: head left
|
||||
self.bodies.push(SubBody::new(cx + 0.8, cy - 3.0, r, mat)); // 2: head right
|
||||
// Shoulders & torso
|
||||
self.bodies.push(SubBody::new(cx - 1.8, cy - 1.8, r, mat)); // 3: left shoulder
|
||||
self.bodies.push(SubBody::new(cx, cy - 1.8, r, mat)); // 4: center torso
|
||||
self.bodies.push(SubBody::new(cx + 1.8, cy - 1.8, r, mat)); // 5: right shoulder
|
||||
// Lower torso
|
||||
self.bodies.push(SubBody::new(cx - 1.0, cy - 0.5, r, mat)); // 6: left torso
|
||||
self.bodies.push(SubBody::new(cx + 1.0, cy - 0.5, r, mat)); // 7: right torso
|
||||
// Arms
|
||||
self.bodies.push(SubBody::new(cx - 3.0, cy - 1.0, r, mat)); // 8: left hand
|
||||
self.bodies.push(SubBody::new(cx + 3.0, cy - 1.0, r, mat)); // 9: right hand
|
||||
// Hips
|
||||
self.bodies.push(SubBody::new(cx - 1.2, cy + 0.8, r, mat)); // 10: left hip
|
||||
self.bodies.push(SubBody::new(cx + 1.2, cy + 0.8, r, mat)); // 11: right hip
|
||||
// Legs
|
||||
self.bodies.push(SubBody::new(cx - 1.2, cy + 2.2, r, mat)); // 12: left leg
|
||||
self.bodies.push(SubBody::new(cx + 1.2, cy + 2.2, r, mat)); // 13: right leg
|
||||
// Feet
|
||||
self.bodies.push(SubBody::new(cx - 1.5, cy + 3.5, r, MaterialId::Bone)); // 14: left foot
|
||||
self.bodies.push(SubBody::new(cx + 1.5, cy + 3.5, r, MaterialId::Bone)); // 15: right foot
|
||||
|
||||
let s = self.constraint_stiffness;
|
||||
let mk = |a: usize, b: usize, len: f32| Constraint::new(a, b, len, s);
|
||||
|
||||
self.constraints.push(mk(0, 1, 1.0));
|
||||
self.constraints.push(mk(1, 2, 0.9));
|
||||
self.constraints.push(mk(1, 3, 0.9));
|
||||
self.constraints.push(mk(2, 4, 0.9));
|
||||
self.constraints.push(mk(3, 5, 0.9));
|
||||
self.constraints.push(mk(1, 6, 2.0));
|
||||
self.constraints.push(mk(4, 5, 1.0));
|
||||
self.constraints.push(mk(4, 6, 1.1));
|
||||
self.constraints.push(mk(5, 6, 1.1));
|
||||
// Head internal
|
||||
self.constraints.push(mk(0, 1, 0.9));
|
||||
self.constraints.push(mk(0, 2, 0.9));
|
||||
self.constraints.push(mk(1, 2, 1.6));
|
||||
// Head to shoulders
|
||||
self.constraints.push(mk(1, 3, 1.3));
|
||||
self.constraints.push(mk(2, 5, 1.3));
|
||||
// Shoulders to torso
|
||||
self.constraints.push(mk(3, 4, 1.8));
|
||||
self.constraints.push(mk(4, 5, 1.8));
|
||||
self.constraints.push(mk(3, 6, 1.6));
|
||||
self.constraints.push(mk(5, 7, 1.6));
|
||||
// Torso center
|
||||
self.constraints.push(mk(4, 6, 1.3));
|
||||
self.constraints.push(mk(4, 7, 1.3));
|
||||
self.constraints.push(mk(6, 7, 2.0));
|
||||
// Arms
|
||||
self.constraints.push(mk(3, 8, 1.5));
|
||||
self.constraints.push(mk(5, 9, 1.5));
|
||||
// Hips
|
||||
self.constraints.push(mk(6, 10, 1.5));
|
||||
self.constraints.push(mk(7, 11, 1.5));
|
||||
self.constraints.push(mk(10, 11, 2.4));
|
||||
// Legs
|
||||
self.constraints.push(mk(10, 12, 1.4));
|
||||
self.constraints.push(mk(11, 13, 1.4));
|
||||
// Feet
|
||||
self.constraints.push(mk(12, 14, 1.5));
|
||||
self.constraints.push(mk(13, 15, 1.5));
|
||||
// Cross-bracing for stability
|
||||
self.constraints.push(mk(0, 4, 1.7));
|
||||
self.constraints.push(mk(6, 10, 1.3));
|
||||
self.constraints.push(mk(7, 11, 1.3));
|
||||
}
|
||||
|
||||
pub fn apply_fire_damage(&mut self) {
|
||||
|
||||
@@ -11,8 +11,8 @@ impl Player {
|
||||
let id = manager.spawn(EntityKind::Player);
|
||||
Self {
|
||||
entity_id: id,
|
||||
move_speed: 0.15,
|
||||
jump_force: 0.8,
|
||||
move_speed: 0.3,
|
||||
jump_force: 1.2,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+15
-11
@@ -79,7 +79,7 @@ impl Game {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let cy = (surface_y as f32) - 4.0;
|
||||
let cy = (surface_y as f32) - 6.0;
|
||||
self.player.spawn_at(&mut self.entities, cx, cy);
|
||||
|
||||
let (px, py) = self.player.center(&self.entities);
|
||||
@@ -87,8 +87,8 @@ impl Game {
|
||||
}
|
||||
|
||||
pub fn center_camera_on(&mut self, px: f32, py: f32) {
|
||||
self.cam_x = px as i32 - 40;
|
||||
self.cam_y = py as i32 - 12;
|
||||
self.cam_x = px as i32 - 60;
|
||||
self.cam_y = py as i32 - 20;
|
||||
}
|
||||
|
||||
pub fn run<R: Renderer>(&mut self, renderer: &mut R) {
|
||||
@@ -108,12 +108,15 @@ impl Game {
|
||||
self.accumulator -= self.fixed_dt;
|
||||
}
|
||||
|
||||
let vw = renderer.viewport_w();
|
||||
let vh = renderer.viewport_h();
|
||||
let (px, py) = self.player.center(&self.entities);
|
||||
self.center_camera_on(px, py);
|
||||
self.cam_x = px as i32 - (vw as i32 / 2);
|
||||
self.cam_y = py as i32 - (vh as i32 / 2);
|
||||
|
||||
let _ = renderer.render(&self.grid, &self.entities, self.cam_x, self.cam_y);
|
||||
|
||||
self.handle_input(renderer.viewport_w(), renderer.viewport_h());
|
||||
self.handle_input(vw, vh);
|
||||
}
|
||||
|
||||
let _ = renderer.shutdown();
|
||||
@@ -246,13 +249,14 @@ impl Game {
|
||||
}
|
||||
}
|
||||
|
||||
solver.solve_constraints(&mut bodies, &constraints, 2);
|
||||
|
||||
for b in &mut bodies {
|
||||
if !b.alive {
|
||||
continue;
|
||||
for _ci in 0..4 {
|
||||
solver.solve_constraints(&mut bodies, &constraints, 1);
|
||||
for b in &mut bodies {
|
||||
if !b.alive {
|
||||
continue;
|
||||
}
|
||||
resolve_grid_collision(grid, b);
|
||||
}
|
||||
resolve_grid_collision(grid, b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-6
@@ -94,11 +94,11 @@ pub struct VerletSolver {
|
||||
impl VerletSolver {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
gravity: 0.08,
|
||||
damping: 0.98,
|
||||
gravity: 0.04,
|
||||
damping: 0.97,
|
||||
dt: 1.0,
|
||||
max_vel: 0.8,
|
||||
substeps: 4,
|
||||
max_vel: 1.0,
|
||||
substeps: 8,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,7 @@ impl VerletSolver {
|
||||
}
|
||||
|
||||
pub fn solve_constraints(&self, bodies: &mut [SubBody], constraints: &[Constraint], iterations: u32) {
|
||||
const MAX_CORRECTION: f32 = 0.5;
|
||||
for _ in 0..iterations {
|
||||
for c in constraints {
|
||||
let (ba, bb) = if c.a < bodies.len() && c.b < bodies.len() {
|
||||
@@ -143,8 +144,14 @@ impl VerletSolver {
|
||||
continue;
|
||||
}
|
||||
let diff = (dist - c.rest_length) / dist;
|
||||
let sx = dx * 0.5 * diff * c.stiffness;
|
||||
let sy = dy * 0.5 * diff * c.stiffness;
|
||||
let mut sx = dx * 0.5 * diff * c.stiffness;
|
||||
let mut sy = dy * 0.5 * diff * c.stiffness;
|
||||
let corr_mag = (sx * sx + sy * sy).sqrt();
|
||||
if corr_mag > MAX_CORRECTION {
|
||||
let scale = MAX_CORRECTION / corr_mag;
|
||||
sx *= scale;
|
||||
sy *= scale;
|
||||
}
|
||||
bodies[c.a].x += sx;
|
||||
bodies[c.a].y += sy;
|
||||
bodies[c.b].x -= sx;
|
||||
|
||||
@@ -31,9 +31,9 @@ impl TerminalRenderer {
|
||||
}
|
||||
|
||||
fn detect_size(&mut self) -> io::Result<()> {
|
||||
let (w, h) = term_size().unwrap_or((80, 25));
|
||||
self.width = (w as usize).min(120).max(40);
|
||||
self.height = (h as usize).min(50).max(15);
|
||||
let (w, h) = term_size().unwrap_or((120, 40));
|
||||
self.width = (w as usize).min(250).max(60);
|
||||
self.height = (h as usize).min(80).max(20);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user