survivor

graphical action game for the linux terminal
git clone https://git.ce9e.org/survivor.git

commit
f98a4e78aed835501c2e76956cbbc539fffa4966
parent
ce5bf10697339c254969f135e1525895f6f183bd
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2023-02-18 23:30
run rustfmt

Diffstat

M src/input.rs 2 +-
M src/main.rs 197 ++++++++++++++++++++++++++++++++++++++-----------------------
M src/random.rs 4 +++-
M src/term.rs 9 ++++++---

4 files changed, 134 insertions, 78 deletions


diff --git a/src/input.rs b/src/input.rs

@@ -29,7 +29,7 @@ impl Input {
   29    29 
   30    30     fn cbreak(&self) {
   31    31         let mut t = self.termios.clone();
   32    -1         t.c_lflag &= !(libc::ICANON|libc::ECHO);
   -1    32         t.c_lflag &= !(libc::ICANON | libc::ECHO);
   33    33         t.c_cc[libc::VMIN] = 0;
   34    34         t.c_cc[libc::VTIME] = 0;
   35    35         unsafe {

diff --git a/src/main.rs b/src/main.rs

@@ -1,13 +1,13 @@
    1    -1 mod term;
   -1     1 mod enemies;
    2     2 mod input;
    3     3 mod random;
    4    -1 mod enemies;
   -1     4 mod term;
    5     5 
    6     6 extern crate libc;
    7     7 extern crate sprites;
    8     8 
    9    -1 use std::{thread, time};
   10     9 use std::sync::atomic::{AtomicBool, Ordering};
   -1    10 use std::{thread, time};
   11    11 
   12    12 const TICK: time::Duration = time::Duration::from_millis(33);
   13    13 const MAX_ENEMIES: usize = 100;
@@ -23,7 +23,13 @@ const PERK_ATTRACT: usize = 6;
   23    23 static RUNNING: AtomicBool = AtomicBool::new(true);
   24    24 
   25    25 #[derive(PartialEq)]
   26    -1 enum Dir { Up, Right, Down, Left, Stop }
   -1    26 enum Dir {
   -1    27     Up,
   -1    28     Right,
   -1    29     Down,
   -1    30     Left,
   -1    31     Stop,
   -1    32 }
   27    33 
   28    34 fn quit(_sig: i32) {
   29    35     RUNNING.fetch_and(false, Ordering::Relaxed);
@@ -73,10 +79,16 @@ fn sprite(screen: &mut term::Screen, cx: f32, cy: f32, sprite: &sprites::Sprite,
   73    79 fn circle(screen: &mut term::Screen, cx: f32, cy: f32, r: f32, color: [u8; 3]) {
   74    80     let r2 = r * r;
   75    81 
   76    -1     let y0 = screen.convert_y(cy - r).max(0).min(screen.height as i64 - 1) as usize;
   -1    82     let y0 = screen
   -1    83         .convert_y(cy - r)
   -1    84         .max(0)
   -1    85         .min(screen.height as i64 - 1) as usize;
   77    86     let x0 = screen.convert_x(cx - r).max(0).min(screen.width as i64 - 1) as usize;
   78    87 
   79    -1     let y1 = screen.convert_y(cy + r).max(0).min(screen.height as i64 - 1) as usize;
   -1    88     let y1 = screen
   -1    89         .convert_y(cy + r)
   -1    90         .max(0)
   -1    91         .min(screen.height as i64 - 1) as usize;
   80    92     let x1 = screen.convert_x(cx + r).max(0).min(screen.width as i64 - 1) as usize;
   81    93 
   82    94     for y in y0..=y1 {
@@ -124,7 +136,6 @@ struct Player {
  124   136     pub xp: usize,
  125   137     pub last_level: usize,
  126   138     pub next_level: usize,
  127    -1 
  128   139 }
  129   140 
  130   141 fn main() {
@@ -168,22 +179,28 @@ fn main() {
  168   179         let dt = (time1 - time0).as_secs_f32();
  169   180 
  170   181         match input.getch() {
  171    -1             Some(b'w' | b'A') => { player.dir = Dir::Up },
  172    -1             Some(b'a' | b'D') => { player.dir = Dir::Left; player.face = Dir::Left },
  173    -1             Some(b's' | b'B') => { player.dir = Dir::Down },
  174    -1             Some(b'd' | b'C') => { player.dir = Dir::Right; player.face = Dir::Right },
  175    -1             Some(b' ') => { player.dir = Dir::Stop },
  176    -1             Some(b'q') => { quit(0) },
  177    -1             _ => {},
   -1   182             Some(b'w' | b'A') => player.dir = Dir::Up,
   -1   183             Some(b'a' | b'D') => {
   -1   184                 player.dir = Dir::Left;
   -1   185                 player.face = Dir::Left
   -1   186             }
   -1   187             Some(b's' | b'B') => player.dir = Dir::Down,
   -1   188             Some(b'd' | b'C') => {
   -1   189                 player.dir = Dir::Right;
   -1   190                 player.face = Dir::Right
   -1   191             }
   -1   192             Some(b' ') => player.dir = Dir::Stop,
   -1   193             Some(b'q') => quit(0),
   -1   194             _ => {}
  178   195         }
  179   196 
  180   197         // move
  181   198         match player.dir {
  182    -1             Dir::Up => { player.y -= player.speed * dt },
  183    -1             Dir::Right => { player.x += player.speed * dt },
  184    -1             Dir::Down => { player.y += player.speed * dt },
  185    -1             Dir::Left => { player.x -= player.speed * dt },
  186    -1             Dir::Stop => {},
   -1   199             Dir::Up => player.y -= player.speed * dt,
   -1   200             Dir::Right => player.x += player.speed * dt,
   -1   201             Dir::Down => player.y += player.speed * dt,
   -1   202             Dir::Left => player.x -= player.speed * dt,
   -1   203             Dir::Stop => {}
  187   204         }
  188   205 
  189   206         for i in 0..enemies.len() {
@@ -224,10 +241,10 @@ fn main() {
  224   241         player.health = (player.health + player.health_recover * dt).min(player.health_max);
  225   242 
  226   243         // despawn
  227    -1         enemies = enemies.into_iter().filter(|e| {
  228    -1             (e.y - player.y).abs() < height
  229    -1             && (e.x - player.x).abs() < width
  230    -1         }).collect();
   -1   244         enemies = enemies
   -1   245             .into_iter()
   -1   246             .filter(|e| (e.y - player.y).abs() < height && (e.x - player.x).abs() < width)
   -1   247             .collect();
  231   248 
  232   249         // interact with enemies
  233   250         for enemy in enemies.iter_mut() {
@@ -244,17 +261,20 @@ fn main() {
  244   261             }
  245   262         }
  246   263 
  247    -1         enemies = enemies.into_iter().filter(|enemy| {
  248    -1             if enemy.health <= 0.0 {
  249    -1                 diamonds.push(Diamond {
  250    -1                     x: enemy.x,
  251    -1                     y: enemy.y,
  252    -1                 });
  253    -1                 return false;
  254    -1             } else {
  255    -1                 return true;
  256    -1             }
  257    -1         }).collect();
   -1   264         enemies = enemies
   -1   265             .into_iter()
   -1   266             .filter(|enemy| {
   -1   267                 if enemy.health <= 0.0 {
   -1   268                     diamonds.push(Diamond {
   -1   269                         x: enemy.x,
   -1   270                         y: enemy.y,
   -1   271                     });
   -1   272                     return false;
   -1   273                 } else {
   -1   274                     return true;
   -1   275                 }
   -1   276             })
   -1   277             .collect();
  258   278 
  259   279         if player.health < 0.0 {
  260   280             println!("\nyou died (score: {})", player.xp);
@@ -262,30 +282,45 @@ fn main() {
  262   282         }
  263   283 
  264   284         // interact with diamonds
  265    -1         diamonds = diamonds.into_iter().filter(|diamond| {
  266    -1             let dx = player.x - diamond.x;
  267    -1             let dy = player.y - diamond.y;
  268    -1             let d = dx * dx + dy * dy;
  269    -1             if d < player.diamond_radius * player.diamond_radius {
  270    -1                 player.xp += 1;
  271    -1                 return false;
  272    -1             } else{
  273    -1                 return true;
  274    -1             }
  275    -1         }).collect();
   -1   285         diamonds = diamonds
   -1   286             .into_iter()
   -1   287             .filter(|diamond| {
   -1   288                 let dx = player.x - diamond.x;
   -1   289                 let dy = player.y - diamond.y;
   -1   290                 let d = dx * dx + dy * dy;
   -1   291                 if d < player.diamond_radius * player.diamond_radius {
   -1   292                     player.xp += 1;
   -1   293                     return false;
   -1   294                 } else {
   -1   295                     return true;
   -1   296                 }
   -1   297             })
   -1   298             .collect();
  276   299 
  277   300         while player.xp >= player.next_level {
  278   301             player.last_level = player.next_level;
  279   302             player.next_level *= 2;
  280   303 
  281   304             match rng.gen_range(0, 7) {
  282    -1                 PERK_POWER => { player.power *= 1.1; },
  283    -1                 PERK_HEALTH => { player.health_max *= 1.1; },
  284    -1                 PERK_SPEED => { player.speed *= 1.1; },
  285    -1                 PERK_RADIUS => { player.damage_radius *= 1.1; },
  286    -1                 PERK_HEAL => { player.health = player.health_max; },
  287    -1                 PERK_RECOVER => { player.health_recover += 0.2 },
  288    -1                 PERK_ATTRACT => { player.diamond_radius *= 1.1; },
   -1   305                 PERK_POWER => {
   -1   306                     player.power *= 1.1;
   -1   307                 }
   -1   308                 PERK_HEALTH => {
   -1   309                     player.health_max *= 1.1;
   -1   310                 }
   -1   311                 PERK_SPEED => {
   -1   312                     player.speed *= 1.1;
   -1   313                 }
   -1   314                 PERK_RADIUS => {
   -1   315                     player.damage_radius *= 1.1;
   -1   316                 }
   -1   317                 PERK_HEAL => {
   -1   318                     player.health = player.health_max;
   -1   319                 }
   -1   320                 PERK_RECOVER => player.health_recover += 0.2,
   -1   321                 PERK_ATTRACT => {
   -1   322                     player.diamond_radius *= 1.1;
   -1   323                 }
  289   324                 _ => unreachable!(),
  290   325             }
  291   326         }
@@ -293,22 +328,10 @@ fn main() {
  293   328         // spawn
  294   329         if enemies.len() < MAX_ENEMIES && rng.gen_f32() < dt * 2.0 {
  295   330             let (spawn_x, spawn_y) = match rng.gen_range(0, 4) {
  296    -1                 0 => (
  297    -1                     rng.gen_f32() * width,
  298    -1                     -sprite_height,
  299    -1                 ),
  300    -1                 1 => (
  301    -1                     width + sprite_width,
  302    -1                     rng.gen_f32() * height,
  303    -1                 ),
  304    -1                 2 => (
  305    -1                     rng.gen_f32() * width,
  306    -1                     height + sprite_height,
  307    -1                 ),
  308    -1                 3 => (
  309    -1                     -sprite_width,
  310    -1                     rng.gen_f32() * height,
  311    -1                 ),
   -1   331                 0 => (rng.gen_f32() * width, -sprite_height),
   -1   332                 1 => (width + sprite_width, rng.gen_f32() * height),
   -1   333                 2 => (rng.gen_f32() * width, height + sprite_height),
   -1   334                 3 => (-sprite_width, rng.gen_f32() * height),
  312   335                 _ => unreachable!(),
  313   336             };
  314   337 
@@ -322,7 +345,13 @@ fn main() {
  322   345 
  323   346         // render
  324   347         clear(&mut screen);
  325    -1         circle(&mut screen, width / 2.0, height / 2.0, player.damage_radius, [0x00, 0xff, 0x00]);
   -1   348         circle(
   -1   349             &mut screen,
   -1   350             width / 2.0,
   -1   351             height / 2.0,
   -1   352             player.damage_radius,
   -1   353             [0x00, 0xff, 0x00],
   -1   354         );
  326   355 
  327   356         for diamond in diamonds.iter() {
  328   357             let sx = diamond.x - player.x + width / 2.0;
@@ -334,7 +363,13 @@ fn main() {
  334   363         let mut player_rendered = false;
  335   364         for enemy in enemies.iter() {
  336   365             if !player_rendered && enemy.y > player.y {
  337    -1                 sprite(&mut screen, width / 2.0, height / 2.0, &sprites::HERO, player.face == Dir::Left);
   -1   366                 sprite(
   -1   367                     &mut screen,
   -1   368                     width / 2.0,
   -1   369                     height / 2.0,
   -1   370                     &sprites::HERO,
   -1   371                     player.face == Dir::Left,
   -1   372                 );
  338   373                 player_rendered = true;
  339   374             }
  340   375 
@@ -343,12 +378,28 @@ fn main() {
  343   378             sprite(&mut screen, sx, sy, enemy.t.sprite, enemy.x > player.x);
  344   379         }
  345   380         if !player_rendered {
  346    -1             sprite(&mut screen, width / 2.0, height / 2.0, &sprites::HERO, player.face == Dir::Left);
   -1   381             sprite(
   -1   382                 &mut screen,
   -1   383                 width / 2.0,
   -1   384                 height / 2.0,
   -1   385                 &sprites::HERO,
   -1   386                 player.face == Dir::Left,
   -1   387             );
  347   388         }
  348   389 
  349    -1         bar(&mut screen, 0, (player.xp - player.last_level) as f32 / (player.next_level - player.last_level) as f32, [0x00, 0x00, 0xff]);
   -1   390         bar(
   -1   391             &mut screen,
   -1   392             0,
   -1   393             (player.xp - player.last_level) as f32 / (player.next_level - player.last_level) as f32,
   -1   394             [0x00, 0x00, 0xff],
   -1   395         );
  350   396         let h = screen.height;
  351    -1         bar(&mut screen, h - 3, player.health / player.health_max, [0xff, 0x00, 0x00]);
   -1   397         bar(
   -1   398             &mut screen,
   -1   399             h - 3,
   -1   400             player.health / player.health_max,
   -1   401             [0xff, 0x00, 0x00],
   -1   402         );
  352   403 
  353   404         screen.render();
  354   405 

diff --git a/src/random.rs b/src/random.rs

@@ -10,7 +10,9 @@ impl Rng {
   10    10         unsafe {
   11    11             libc::getrandom(bytes.as_mut_ptr(), 8, 0x0001);
   12    12         }
   13    -1         return Self { state: u64::from_ne_bytes(bytes) };
   -1    13         return Self {
   -1    14             state: u64::from_ne_bytes(bytes),
   -1    15         };
   14    16     }
   15    17 
   16    18     fn gen(&mut self) -> usize {

diff --git a/src/term.rs b/src/term.rs

@@ -28,7 +28,7 @@ fn set_fg(color: [u8; 3]) {
   28    28 
   29    29 fn block6(block: u32) -> char {
   30    30     if block == 0b000000 {
   31    -1         return ' '
   -1    31         return ' ';
   32    32     } else if block < 0b010101 {
   33    33         return char::from_u32(0x1FB00 + block - 1).unwrap();
   34    34     } else if block == 0b010101 {
@@ -61,7 +61,10 @@ fn get_block(colors: [[u8; 3]; 6]) -> (u32, [u8; 3], [u8; 3]) {
   61    61     let mut lights = vec![];
   62    62     let mut darks = vec![];
   63    63 
   64    -1     let lightness: Vec<u8> = colors.iter().map(|c| (c[0] >> 3) + (c[1] >> 1) + (c[2] >> 5)).collect();
   -1    64     let lightness: Vec<u8> = colors
   -1    65         .iter()
   -1    66         .map(|c| (c[0] >> 3) + (c[1] >> 1) + (c[2] >> 5))
   -1    67         .collect();
   65    68     let mean: u8 = lightness.iter().map(|l| l / 6).sum();
   66    69 
   67    70     for i in 0..6 {
@@ -97,7 +100,7 @@ impl Screen {
   97   100             width: width,
   98   101             height: height,
   99   102             pixels: vec![vec![[0, 0, 0]; width]; height],
  100    -1         }
   -1   103         };
  101   104     }
  102   105 
  103   106     pub fn set(&mut self, x: usize, y: usize, color: [u8; 3]) {