summaryrefslogtreecommitdiffstats
path: root/src/renderer.rs
diff options
context:
space:
mode:
authorsalmonik <salmonik@tuta.io>2026-07-11 15:30:04 +0200
committersalmonik <salmonik@tuta.io>2026-07-11 15:30:04 +0200
commit723c2fe0982f80438b75098fa79d1933efc13d0a (patch)
tree78bb8c9089eb351f4889affd74eb0d4de65f0a67 /src/renderer.rs
parentend game screen, fix occlusion (diff)
downloaddungeon-723c2fe0982f80438b75098fa79d1933efc13d0a.tar.gz
dungeon-723c2fe0982f80438b75098fa79d1933efc13d0a.zip
add enemy AI: chase, patrol, attack, and movement animation
Diffstat (limited to 'src/renderer.rs')
-rw-r--r--src/renderer.rs117
1 files changed, 69 insertions, 48 deletions
diff --git a/src/renderer.rs b/src/renderer.rs
index aa87b54..885e5de 100644
--- a/src/renderer.rs
+++ b/src/renderer.rs
@@ -74,6 +74,18 @@ pub struct Renderer {
74 floor_dist_lut: Vec<f64>, 74 floor_dist_lut: Vec<f64>,
75} 75}
76 76
77// ---- entity sprites ---------------------------------------------------
78
79/// Screen-space info for an entity sprite to render.
80struct Sprite {
81 sx: isize,
82 sy: isize,
83 scale: f64,
84 ch: char,
85 color: u32,
86 cam_z: f64,
87}
88
77impl Renderer { 89impl Renderer {
78 pub fn new(screen_w: usize, screen_h: usize) -> Self { 90 pub fn new(screen_w: usize, screen_h: usize) -> Self {
79 let half_h = screen_h as f64 / 2.0; 91 let half_h = screen_h as f64 / 2.0;
@@ -306,19 +318,9 @@ impl Renderer {
306 ) { 318 ) {
307 let mw = game.maze.width(); 319 let mw = game.maze.width();
308 let mh = game.maze.height(); 320 let mh = game.maze.height();
309
310 // Collect visible entities with their screen info.
311 struct Sprite {
312 sx: isize,
313 sy: isize,
314 scale: f64,
315 ch: char,
316 color: u32,
317 cam_z: f64,
318 }
319
320 let mut sprites: Vec<Sprite> = Vec::new(); 321 let mut sprites: Vec<Sprite> = Vec::new();
321 322
323 // Static entities on the grid.
322 for y in 0..mh { 324 for y in 0..mh {
323 for x in 0..mw { 325 for x in 0..mw {
324 let entity = match game.maze.entity_at(x, y) { 326 let entity = match game.maze.entity_at(x, y) {
@@ -329,46 +331,15 @@ impl Renderer {
329 let ex = x as f64 + 0.5; 331 let ex = x as f64 + 0.5;
330 let ey = y as f64 + 0.5; 332 let ey = y as f64 + 0.5;
331 333
332 let dx = ex - game.player.x; 334 self.push_entity_sprite(game, ex, ey, entity, fov, half_h, &mut sprites);
333 let dy = ey - game.player.y;
334
335 let fx = game.player.angle.cos();
336 let fy = game.player.angle.sin();
337 let rx = -fy;
338 let ry = fx;
339
340 let cam_z = dx * fx + dy * fy;
341 if cam_z <= 0.1 {
342 continue;
343 }
344
345 let cam_x = dx * rx + dy * ry;
346
347 let half_fov_tan = (fov / 2.0).tan();
348 let screen_x_norm = cam_x / (cam_z * half_fov_tan);
349 if screen_x_norm < -1.0 || screen_x_norm > 1.0 {
350 continue;
351 }
352
353 let sx = ((screen_x_norm + 1.0) * 0.5 * self.screen_w as f64) as isize;
354
355 let entity_h = (self.screen_h as f64 / cam_z * 0.4) as isize;
356 let sy = (half_h - entity_h as f64 * 0.3) as isize;
357
358 let scale = (entity_h.max(6) as f64 / LETTER_H as f64).max(0.5);
359 let (ch, color) = entity_char_and_color(entity);
360
361 sprites.push(Sprite {
362 sx,
363 sy,
364 scale,
365 ch,
366 color,
367 cam_z,
368 });
369 } 335 }
370 } 336 }
371 337
338 // Animating enemies (mid-move, not on the grid).
339 for (wx, wy, entity) in game.enemy_render_entities() {
340 self.push_entity_sprite(game, wx, wy, &entity, fov, half_h, &mut sprites);
341 }
342
372 // Sort farthest first so nearby entities draw on top. 343 // Sort farthest first so nearby entities draw on top.
373 sprites.sort_by(|a, b| b.cam_z.partial_cmp(&a.cam_z).unwrap()); 344 sprites.sort_by(|a, b| b.cam_z.partial_cmp(&a.cam_z).unwrap());
374 345
@@ -387,6 +358,56 @@ impl Renderer {
387 } 358 }
388 } 359 }
389 360
361 /// Project a world-space entity position and push a Sprite if on screen.
362 fn push_entity_sprite(
363 &self,
364 game: &Game,
365 ex: f64,
366 ey: f64,
367 entity: &Entity,
368 fov: f64,
369 half_h: f64,
370 sprites: &mut Vec<Sprite>,
371 ) {
372 let dx = ex - game.player.x;
373 let dy = ey - game.player.y;
374
375 let fx = game.player.angle.cos();
376 let fy = game.player.angle.sin();
377 let rx = -fy;
378 let ry = fx;
379
380 let cam_z = dx * fx + dy * fy;
381 if cam_z <= 0.1 {
382 return;
383 }
384
385 let cam_x = dx * rx + dy * ry;
386
387 let half_fov_tan = (fov / 2.0).tan();
388 let screen_x_norm = cam_x / (cam_z * half_fov_tan);
389 if screen_x_norm < -1.0 || screen_x_norm > 1.0 {
390 return;
391 }
392
393 let sx = ((screen_x_norm + 1.0) * 0.5 * self.screen_w as f64) as isize;
394
395 let entity_h = (self.screen_h as f64 / cam_z * 0.4) as isize;
396 let sy = (half_h - entity_h as f64 * 0.3) as isize;
397
398 let scale = (entity_h.max(6) as f64 / LETTER_H as f64).max(0.5);
399 let (ch, color) = entity_char_and_color(entity);
400
401 sprites.push(Sprite {
402 sx,
403 sy,
404 scale,
405 ch,
406 color,
407 cam_z,
408 });
409 }
410
390 fn draw_char_scaled( 411 fn draw_char_scaled(
391 &self, 412 &self,
392 buffer: &mut [u32], 413 buffer: &mut [u32],