survivor

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

commit
8b457fc6faf4ac2869eb286d1bc8decd34e5fbab
parent
e1c50a4021ca58af2a6ab58d22a71afb5e345ec5
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2023-02-19 16:17
split game logic into separate module

Diffstat

A src/game.rs 298 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
M src/main.rs 298 +++++--------------------------------------------------------
M src/term.rs 18 ------------------
M src/win.rs 34 ++++++++++++++++++++++++++--------

4 files changed, 345 insertions, 303 deletions


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

@@ -0,0 +1,298 @@
   -1     1 use crate::enemies;
   -1     2 use crate::random;
   -1     3 use crate::sprites;
   -1     4 use crate::term;
   -1     5 use crate::win;
   -1     6 
   -1     7 const MAX_ENEMIES: usize = 100;
   -1     8 
   -1     9 const PERK_POWER: usize = 0;
   -1    10 const PERK_HEALTH: usize = 1;
   -1    11 const PERK_SPEED: usize = 2;
   -1    12 const PERK_RADIUS: usize = 3;
   -1    13 const PERK_HEAL: usize = 4;
   -1    14 const PERK_RECOVER: usize = 5;
   -1    15 const PERK_ATTRACT: usize = 6;
   -1    16 
   -1    17 #[derive(PartialEq)]
   -1    18 pub enum Dir {
   -1    19     Up,
   -1    20     Right,
   -1    21     Down,
   -1    22     Left,
   -1    23     Stop,
   -1    24 }
   -1    25 
   -1    26 pub struct Diamond {
   -1    27     pub x: f32,
   -1    28     pub y: f32,
   -1    29 }
   -1    30 
   -1    31 pub struct Player {
   -1    32     pub x: f32,
   -1    33     pub y: f32,
   -1    34     pub dir: Dir,
   -1    35     pub face: Dir,
   -1    36     pub speed: f32,
   -1    37     pub size: f32,
   -1    38     pub health: f32,
   -1    39     pub health_max: f32,
   -1    40     pub health_recover: f32,
   -1    41     pub power: f32,
   -1    42     pub damage_radius: f32,
   -1    43     pub diamond_radius: f32,
   -1    44     pub xp: usize,
   -1    45     pub last_level: usize,
   -1    46     pub next_level: usize,
   -1    47 }
   -1    48 
   -1    49 impl Player {
   -1    50     pub fn new() -> Self {
   -1    51         return Self {
   -1    52             x: 0.0,
   -1    53             y: 0.0,
   -1    54             dir: Dir::Stop,
   -1    55             face: Dir::Right,
   -1    56             speed: 30.0,
   -1    57             size: 9.0,
   -1    58             health: 50.0,
   -1    59             health_max: 50.0,
   -1    60             health_recover: 0.0,
   -1    61             power: 10.0,
   -1    62             damage_radius: 30.0,
   -1    63             diamond_radius: 15.0,
   -1    64             xp: 0,
   -1    65             last_level: 0,
   -1    66             next_level: 10,
   -1    67         };
   -1    68     }
   -1    69 }
   -1    70 
   -1    71 pub struct Game {
   -1    72     pub player: Player,
   -1    73     pub diamonds: Vec<Diamond>,
   -1    74     pub enemies: Vec<enemies::Enemy>,
   -1    75     pub i_enemy: usize,
   -1    76     pub win: win::Window,
   -1    77     rng: random::Rng,
   -1    78 }
   -1    79 
   -1    80 impl Game {
   -1    81     pub fn new(screen: &term::Screen) -> Self {
   -1    82         return Self {
   -1    83             enemies: vec![],
   -1    84             diamonds: vec![],
   -1    85             i_enemy: 0,
   -1    86             player: Player::new(),
   -1    87             win: win::Window {
   -1    88                 width: screen.width,
   -1    89                 height: screen.height - 6,
   -1    90                 dx: 0,
   -1    91                 dy: 3,
   -1    92             },
   -1    93             rng: random::Rng::new(),
   -1    94         };
   -1    95     }
   -1    96 
   -1    97     pub fn step(&mut self, dt: f32) {
   -1    98         let height = win::iconvert_y(self.win.height);
   -1    99         let width = win::iconvert_x(self.win.width);
   -1   100         let sprite_height = win::iconvert_y(sprites::HEIGHT);
   -1   101         let sprite_width = win::iconvert_x(sprites::WIDTH);
   -1   102 
   -1   103         // move
   -1   104         match self.player.dir {
   -1   105             Dir::Up => self.player.y -= self.player.speed * dt,
   -1   106             Dir::Right => self.player.x += self.player.speed * dt,
   -1   107             Dir::Down => self.player.y += self.player.speed * dt,
   -1   108             Dir::Left => self.player.x -= self.player.speed * dt,
   -1   109             Dir::Stop => {}
   -1   110         }
   -1   111 
   -1   112         for i in 0..self.enemies.len() {
   -1   113             let enemy = &self.enemies[i];
   -1   114 
   -1   115             let dxp = self.player.x - enemy.x;
   -1   116             let dyp = self.player.y - enemy.y;
   -1   117             let dp = (dxp * dxp + dyp * dyp).sqrt();
   -1   118 
   -1   119             let mut dx = dxp / dp;
   -1   120             let mut dy = dyp / dp;
   -1   121 
   -1   122             for j in 0..self.enemies.len() {
   -1   123                 if i != j {
   -1   124                     let other = &self.enemies[j];
   -1   125 
   -1   126                     let dxm = other.x - enemy.x;
   -1   127                     let dym = other.y - enemy.y;
   -1   128                     let dm = (dxm * dxm + dym * dym).sqrt();
   -1   129 
   -1   130                     if dm < enemy.t.size + other.t.size {
   -1   131                         dx -= dxm / dm;
   -1   132                         dy -= dym / dm;
   -1   133                     }
   -1   134                 }
   -1   135             }
   -1   136 
   -1   137             let d = (dx * dx + dy * dy).sqrt();
   -1   138             dx /= d;
   -1   139             dy /= d;
   -1   140 
   -1   141             let mut enemy = &mut self.enemies[i];
   -1   142             enemy.x += dx * enemy.t.speed * dt;
   -1   143             enemy.y += dy * enemy.t.speed * dt;
   -1   144         }
   -1   145 
   -1   146         // recover
   -1   147         self.player.health =
   -1   148             (self.player.health + self.player.health_recover * dt).min(self.player.health_max);
   -1   149 
   -1   150         // despawn
   -1   151         self.enemies = std::mem::take(&mut self.enemies)
   -1   152             .into_iter()
   -1   153             .filter(|e| (e.y - self.player.y).abs() < height && (e.x - self.player.x).abs() < width)
   -1   154             .collect();
   -1   155 
   -1   156         // interact with enemies
   -1   157         for enemy in self.enemies.iter_mut() {
   -1   158             let dx = self.player.x - enemy.x;
   -1   159             let dy = self.player.y - enemy.y;
   -1   160 
   -1   161             let size = enemy.t.size + self.player.size;
   -1   162             if dx * dx + dy * 2.0 * dy * 2.0 < size * size {
   -1   163                 self.player.health -= enemy.t.power * dt;
   -1   164             }
   -1   165 
   -1   166             if dx * dx + dy * dy < self.player.damage_radius * self.player.damage_radius {
   -1   167                 enemy.health -= self.player.power * dt;
   -1   168             }
   -1   169         }
   -1   170 
   -1   171         self.enemies = std::mem::take(&mut self.enemies)
   -1   172             .into_iter()
   -1   173             .filter(|enemy| {
   -1   174                 if enemy.health <= 0.0 {
   -1   175                     self.diamonds.push(Diamond {
   -1   176                         x: enemy.x,
   -1   177                         y: enemy.y,
   -1   178                     });
   -1   179                     return false;
   -1   180                 } else {
   -1   181                     return true;
   -1   182                 }
   -1   183             })
   -1   184             .collect();
   -1   185 
   -1   186         // interact with diamonds
   -1   187         self.diamonds = std::mem::take(&mut self.diamonds)
   -1   188             .into_iter()
   -1   189             .filter(|diamond| {
   -1   190                 let dx = self.player.x - diamond.x;
   -1   191                 let dy = self.player.y - diamond.y;
   -1   192                 let d = dx * dx + dy * dy;
   -1   193                 if d < self.player.diamond_radius * self.player.diamond_radius {
   -1   194                     self.player.xp += 1;
   -1   195                     return false;
   -1   196                 } else {
   -1   197                     return true;
   -1   198                 }
   -1   199             })
   -1   200             .collect();
   -1   201 
   -1   202         while self.player.xp >= self.player.next_level {
   -1   203             self.player.last_level = self.player.next_level;
   -1   204             self.player.next_level *= 2;
   -1   205 
   -1   206             match self.rng.gen_range(0, 7) {
   -1   207                 PERK_POWER => {
   -1   208                     self.player.power *= 1.1;
   -1   209                 }
   -1   210                 PERK_HEALTH => {
   -1   211                     self.player.health_max *= 1.1;
   -1   212                 }
   -1   213                 PERK_SPEED => {
   -1   214                     self.player.speed *= 1.1;
   -1   215                 }
   -1   216                 PERK_RADIUS => {
   -1   217                     self.player.damage_radius *= 1.1;
   -1   218                 }
   -1   219                 PERK_HEAL => {
   -1   220                     self.player.health = self.player.health_max;
   -1   221                 }
   -1   222                 PERK_RECOVER => self.player.health_recover += 0.2,
   -1   223                 PERK_ATTRACT => {
   -1   224                     self.player.diamond_radius *= 1.1;
   -1   225                 }
   -1   226                 _ => unreachable!(),
   -1   227             }
   -1   228         }
   -1   229 
   -1   230         // spawn
   -1   231         if self.enemies.len() < MAX_ENEMIES && self.rng.gen_f32() < dt * 2.0 {
   -1   232             let (spawn_x, spawn_y) = match self.rng.gen_range(0, 4) {
   -1   233                 0 => (self.rng.gen_f32() * width, -sprite_height),
   -1   234                 1 => (width + sprite_width, self.rng.gen_f32() * height),
   -1   235                 2 => (self.rng.gen_f32() * width, height + sprite_height),
   -1   236                 3 => (-sprite_width, self.rng.gen_f32() * height),
   -1   237                 _ => unreachable!(),
   -1   238             };
   -1   239 
   -1   240             self.enemies.push(enemies::get_enemy(
   -1   241                 spawn_x + self.player.x - width / 2.0,
   -1   242                 spawn_y + self.player.y - height / 2.0,
   -1   243                 self.i_enemy,
   -1   244             ));
   -1   245             self.i_enemy += 1;
   -1   246         }
   -1   247         self.enemies.sort_unstable_by_key(|e| e.y as i32);
   -1   248     }
   -1   249 
   -1   250     pub fn render(&self, screen: &mut term::Screen) {
   -1   251         let height = win::iconvert_y(self.win.height);
   -1   252         let width = win::iconvert_x(self.win.width);
   -1   253 
   -1   254         // render
   -1   255         self.win.fill(screen, [0x33, 0x88, 0x22]);
   -1   256         self.win.circle(
   -1   257             screen,
   -1   258             width / 2.0,
   -1   259             height / 2.0,
   -1   260             self.player.damage_radius,
   -1   261             [0x00, 0xff, 0x00],
   -1   262         );
   -1   263 
   -1   264         for diamond in self.diamonds.iter() {
   -1   265             let sx = diamond.x - self.player.x + width / 2.0;
   -1   266             let sy = diamond.y - self.player.y + height / 2.0;
   -1   267             self.win.sprite(screen, sx, sy, &sprites::DIAMOND, false);
   -1   268         }
   -1   269 
   -1   270         let mut player_rendered = false;
   -1   271         for enemy in self.enemies.iter() {
   -1   272             if !player_rendered && enemy.y > self.player.y {
   -1   273                 self.win.sprite(
   -1   274                     screen,
   -1   275                     width / 2.0,
   -1   276                     height / 2.0,
   -1   277                     &sprites::PLAYER,
   -1   278                     self.player.face == Dir::Left,
   -1   279                 );
   -1   280                 player_rendered = true;
   -1   281             }
   -1   282 
   -1   283             let sx = enemy.x - self.player.x + width / 2.0;
   -1   284             let sy = enemy.y - self.player.y + height / 2.0;
   -1   285             self.win
   -1   286                 .sprite(screen, sx, sy, enemy.t.sprite, enemy.x > self.player.x);
   -1   287         }
   -1   288         if !player_rendered {
   -1   289             self.win.sprite(
   -1   290                 screen,
   -1   291                 width / 2.0,
   -1   292                 height / 2.0,
   -1   293                 &sprites::PLAYER,
   -1   294                 self.player.face == Dir::Left,
   -1   295             );
   -1   296         }
   -1   297     }
   -1   298 }

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

@@ -1,6 +1,7 @@
    1     1 extern crate libc;
    2     2 
    3     3 mod enemies;
   -1     4 mod game;
    4     5 mod input;
    5     6 mod random;
    6     7 mod sprites;
@@ -11,94 +12,21 @@ use std::sync::atomic::{AtomicBool, Ordering};
   11    12 use std::{thread, time};
   12    13 
   13    14 const TICK: time::Duration = time::Duration::from_millis(33);
   14    -1 const MAX_ENEMIES: usize = 100;
   15    -1 
   16    -1 const PERK_POWER: usize = 0;
   17    -1 const PERK_HEALTH: usize = 1;
   18    -1 const PERK_SPEED: usize = 2;
   19    -1 const PERK_RADIUS: usize = 3;
   20    -1 const PERK_HEAL: usize = 4;
   21    -1 const PERK_RECOVER: usize = 5;
   22    -1 const PERK_ATTRACT: usize = 6;
   23    15 
   24    16 const BLACK: [u8; 3] = [0x00, 0x00, 0x00];
   25    17 const RED: [u8; 3] = [0xff, 0x00, 0x00];
   26    -1 const GREEN: [u8; 3] = [0x00, 0xff, 0x00];
   27    18 const BLUE: [u8; 3] = [0x00, 0x00, 0xff];
   28    19 
   29    20 static RUNNING: AtomicBool = AtomicBool::new(true);
   30    21 
   31    -1 #[derive(PartialEq)]
   32    -1 enum Dir {
   33    -1     Up,
   34    -1     Right,
   35    -1     Down,
   36    -1     Left,
   37    -1     Stop,
   38    -1 }
   39    -1 
   40    22 fn quit(_sig: i32) {
   41    23     RUNNING.fetch_and(false, Ordering::Relaxed);
   42    24 }
   43    25 
   44    -1 struct Diamond {
   45    -1     pub x: f32,
   46    -1     pub y: f32,
   47    -1 }
   48    -1 
   49    -1 struct Player {
   50    -1     pub x: f32,
   51    -1     pub y: f32,
   52    -1     pub dir: Dir,
   53    -1     pub face: Dir,
   54    -1     pub speed: f32,
   55    -1     pub size: f32,
   56    -1     pub health: f32,
   57    -1     pub health_max: f32,
   58    -1     pub health_recover: f32,
   59    -1     pub power: f32,
   60    -1     pub damage_radius: f32,
   61    -1     pub diamond_radius: f32,
   62    -1     pub xp: usize,
   63    -1     pub last_level: usize,
   64    -1     pub next_level: usize,
   65    -1 }
   66    -1 
   67    26 fn main() {
   68    27     let input = input::Input::new();
   69    28     let mut screen = term::Screen::new();
   70    -1     let win = win::Window {
   71    -1         width: screen.width,
   72    -1         height: screen.height - 6,
   73    -1         dx: 0,
   74    -1         dy: 3,
   75    -1     };
   76    -1     let mut rng = random::Rng::new();
   77    -1     let width = screen.iconvert_x(win.width);
   78    -1     let height = screen.iconvert_y(win.height);
   79    -1     let sprite_width = screen.iconvert_x(sprites::WIDTH);
   80    -1     let sprite_height = screen.iconvert_y(sprites::HEIGHT);
   81    -1     let mut enemies: Vec<enemies::Enemy> = vec![];
   82    -1     let mut diamonds: Vec<Diamond> = vec![];
   83    -1     let mut i_enemy = 0;
   84    -1 
   85    -1     let mut player = Player {
   86    -1         x: 0.0,
   87    -1         y: 0.0,
   88    -1         dir: Dir::Stop,
   89    -1         face: Dir::Right,
   90    -1         speed: 30.0,
   91    -1         size: 9.0,
   92    -1         health: 50.0,
   93    -1         health_max: 50.0,
   94    -1         health_recover: 0.0,
   95    -1         power: 10.0,
   96    -1         damage_radius: 30.0,
   97    -1         diamond_radius: 15.0,
   98    -1         xp: 0,
   99    -1         last_level: 0,
  100    -1         next_level: 10,
  101    -1     };
   -1    29     let mut game = game::Game::new(&screen);
  102    30 
  103    31     unsafe {
  104    32         libc::signal(libc::SIGINT, quit as usize);
@@ -108,219 +36,29 @@ fn main() {
  108    36 
  109    37     while RUNNING.load(Ordering::Relaxed) {
  110    38         let time1 = time::Instant::now();
  111    -1         let dt = (time1 - time0).as_secs_f32();
  112    39 
  113    40         match input.getch() {
  114    -1             Some(b'w' | b'A') => player.dir = Dir::Up,
   -1    41             Some(b'w' | b'A') => game.player.dir = game::Dir::Up,
  115    42             Some(b'a' | b'D') => {
  116    -1                 player.dir = Dir::Left;
  117    -1                 player.face = Dir::Left
   -1    43                 game.player.dir = game::Dir::Left;
   -1    44                 game.player.face = game::Dir::Left
  118    45             }
  119    -1             Some(b's' | b'B') => player.dir = Dir::Down,
   -1    46             Some(b's' | b'B') => game.player.dir = game::Dir::Down,
  120    47             Some(b'd' | b'C') => {
  121    -1                 player.dir = Dir::Right;
  122    -1                 player.face = Dir::Right
   -1    48                 game.player.dir = game::Dir::Right;
   -1    49                 game.player.face = game::Dir::Right
  123    50             }
  124    -1             Some(b' ') => player.dir = Dir::Stop,
   -1    51             Some(b' ') => game.player.dir = game::Dir::Stop,
  125    52             Some(b'q') => quit(0),
  126    53             _ => {}
  127    54         }
  128    55 
  129    -1         // move
  130    -1         match player.dir {
  131    -1             Dir::Up => player.y -= player.speed * dt,
  132    -1             Dir::Right => player.x += player.speed * dt,
  133    -1             Dir::Down => player.y += player.speed * dt,
  134    -1             Dir::Left => player.x -= player.speed * dt,
  135    -1             Dir::Stop => {}
  136    -1         }
  137    -1 
  138    -1         for i in 0..enemies.len() {
  139    -1             let enemy = &enemies[i];
  140    -1 
  141    -1             let dxp = player.x - enemy.x;
  142    -1             let dyp = player.y - enemy.y;
  143    -1             let dp = (dxp * dxp + dyp * dyp).sqrt();
  144    -1 
  145    -1             let mut dx = dxp / dp;
  146    -1             let mut dy = dyp / dp;
  147    -1 
  148    -1             for j in 0..enemies.len() {
  149    -1                 if i != j {
  150    -1                     let other = &enemies[j];
  151    -1 
  152    -1                     let dxm = other.x - enemy.x;
  153    -1                     let dym = other.y - enemy.y;
  154    -1                     let dm = (dxm * dxm + dym * dym).sqrt();
  155    -1 
  156    -1                     if dm < enemy.t.size + other.t.size {
  157    -1                         dx -= dxm / dm;
  158    -1                         dy -= dym / dm;
  159    -1                     }
  160    -1                 }
  161    -1             }
  162    -1 
  163    -1             let d = (dx * dx + dy * dy).sqrt();
  164    -1             dx /= d;
  165    -1             dy /= d;
  166    -1 
  167    -1             let mut enemy = &mut enemies[i];
  168    -1             enemy.x += dx * enemy.t.speed * dt;
  169    -1             enemy.y += dy * enemy.t.speed * dt;
  170    -1         }
  171    -1 
  172    -1         // recover
  173    -1         player.health = (player.health + player.health_recover * dt).min(player.health_max);
  174    -1 
  175    -1         // despawn
  176    -1         enemies = enemies
  177    -1             .into_iter()
  178    -1             .filter(|e| (e.y - player.y).abs() < height && (e.x - player.x).abs() < width)
  179    -1             .collect();
  180    -1 
  181    -1         // interact with enemies
  182    -1         for enemy in enemies.iter_mut() {
  183    -1             let dx = player.x - enemy.x;
  184    -1             let dy = player.y - enemy.y;
  185    -1 
  186    -1             let size = enemy.t.size + player.size;
  187    -1             if dx * dx + dy * 2.0 * dy * 2.0 < size * size {
  188    -1                 player.health -= enemy.t.power * dt;
  189    -1             }
  190    -1 
  191    -1             if dx * dx + dy * dy < player.damage_radius * player.damage_radius {
  192    -1                 enemy.health -= player.power * dt;
  193    -1             }
  194    -1         }
  195    -1 
  196    -1         enemies = enemies
  197    -1             .into_iter()
  198    -1             .filter(|enemy| {
  199    -1                 if enemy.health <= 0.0 {
  200    -1                     diamonds.push(Diamond {
  201    -1                         x: enemy.x,
  202    -1                         y: enemy.y,
  203    -1                     });
  204    -1                     return false;
  205    -1                 } else {
  206    -1                     return true;
  207    -1                 }
  208    -1             })
  209    -1             .collect();
  210    -1 
  211    -1         if player.health < 0.0 {
  212    -1             println!("\nyou died (score: {})", player.xp);
  213    -1             break;
  214    -1         }
  215    -1 
  216    -1         // interact with diamonds
  217    -1         diamonds = diamonds
  218    -1             .into_iter()
  219    -1             .filter(|diamond| {
  220    -1                 let dx = player.x - diamond.x;
  221    -1                 let dy = player.y - diamond.y;
  222    -1                 let d = dx * dx + dy * dy;
  223    -1                 if d < player.diamond_radius * player.diamond_radius {
  224    -1                     player.xp += 1;
  225    -1                     return false;
  226    -1                 } else {
  227    -1                     return true;
  228    -1                 }
  229    -1             })
  230    -1             .collect();
   -1    56         game.step((time1 - time0).as_secs_f32());
   -1    57         game.render(&mut screen);
  231    58 
  232    -1         while player.xp >= player.next_level {
  233    -1             player.last_level = player.next_level;
  234    -1             player.next_level *= 2;
  235    -1 
  236    -1             match rng.gen_range(0, 7) {
  237    -1                 PERK_POWER => {
  238    -1                     player.power *= 1.1;
  239    -1                 }
  240    -1                 PERK_HEALTH => {
  241    -1                     player.health_max *= 1.1;
  242    -1                 }
  243    -1                 PERK_SPEED => {
  244    -1                     player.speed *= 1.1;
  245    -1                 }
  246    -1                 PERK_RADIUS => {
  247    -1                     player.damage_radius *= 1.1;
  248    -1                 }
  249    -1                 PERK_HEAL => {
  250    -1                     player.health = player.health_max;
  251    -1                 }
  252    -1                 PERK_RECOVER => player.health_recover += 0.2,
  253    -1                 PERK_ATTRACT => {
  254    -1                     player.diamond_radius *= 1.1;
  255    -1                 }
  256    -1                 _ => unreachable!(),
  257    -1             }
  258    -1         }
  259    -1 
  260    -1         // spawn
  261    -1         if enemies.len() < MAX_ENEMIES && rng.gen_f32() < dt * 2.0 {
  262    -1             let (spawn_x, spawn_y) = match rng.gen_range(0, 4) {
  263    -1                 0 => (rng.gen_f32() * width, -sprite_height),
  264    -1                 1 => (width + sprite_width, rng.gen_f32() * height),
  265    -1                 2 => (rng.gen_f32() * width, height + sprite_height),
  266    -1                 3 => (-sprite_width, rng.gen_f32() * height),
  267    -1                 _ => unreachable!(),
  268    -1             };
  269    -1 
  270    -1             enemies.push(enemies::get_enemy(
  271    -1                 spawn_x + player.x - width / 2.0,
  272    -1                 spawn_y + player.y - height / 2.0,
  273    -1                 i_enemy,
  274    -1             ));
  275    -1             i_enemy += 1;
  276    -1         }
  277    -1 
  278    -1         // render
  279    -1         win.fill(&mut screen, [0x33, 0x88, 0x22]);
  280    -1         win.circle(
  281    -1             &mut screen,
  282    -1             width / 2.0,
  283    -1             height / 2.0,
  284    -1             player.damage_radius,
  285    -1             GREEN,
  286    -1         );
  287    -1 
  288    -1         for diamond in diamonds.iter() {
  289    -1             let sx = diamond.x - player.x + width / 2.0;
  290    -1             let sy = diamond.y - player.y + height / 2.0;
  291    -1             win.sprite(&mut screen, sx, sy, &sprites::DIAMOND, false);
  292    -1         }
  293    -1 
  294    -1         enemies.sort_unstable_by_key(|e| e.y as i32);
  295    -1         let mut player_rendered = false;
  296    -1         for enemy in enemies.iter() {
  297    -1             if !player_rendered && enemy.y > player.y {
  298    -1                 win.sprite(
  299    -1                     &mut screen,
  300    -1                     width / 2.0,
  301    -1                     height / 2.0,
  302    -1                     &sprites::PLAYER,
  303    -1                     player.face == Dir::Left,
  304    -1                 );
  305    -1                 player_rendered = true;
  306    -1             }
  307    -1 
  308    -1             let sx = enemy.x - player.x + width / 2.0;
  309    -1             let sy = enemy.y - player.y + height / 2.0;
  310    -1             win.sprite(&mut screen, sx, sy, enemy.t.sprite, enemy.x > player.x);
  311    -1         }
  312    -1         if !player_rendered {
  313    -1             win.sprite(
  314    -1                 &mut screen,
  315    -1                 width / 2.0,
  316    -1                 height / 2.0,
  317    -1                 &sprites::PLAYER,
  318    -1                 player.face == Dir::Left,
  319    -1             );
  320    -1         }
  321    -1 
  322    -1         let xp_bar = (screen.width as f32 * (player.xp - player.last_level) as f32
  323    -1             / (player.next_level - player.last_level) as f32) as usize;
   -1    59         let xp_bar = (screen.width as f32 * (game.player.xp - game.player.last_level) as f32
   -1    60             / (game.player.next_level - game.player.last_level) as f32)
   -1    61             as usize;
  324    62         for x in 0..screen.width {
  325    63             let c = if x <= xp_bar { BLUE } else { BLACK };
  326    64             for y in 0..3 {
@@ -328,7 +66,8 @@ fn main() {
  328    66             }
  329    67         }
  330    68 
  331    -1         let health_bar = (screen.width as f32 * player.health / player.health_max) as usize;
   -1    69         let health_bar =
   -1    70             (screen.width as f32 * game.player.health / game.player.health_max) as usize;
  332    71         for x in 0..screen.width {
  333    72             let c = if x <= health_bar { RED } else { BLACK };
  334    73             for y in (screen.height - 3)..screen.height {
@@ -338,6 +77,11 @@ fn main() {
  338    77 
  339    78         screen.render();
  340    79 
   -1    80         if game.player.health < 0.0 {
   -1    81             println!("\nyou died (score: {})", game.player.xp);
   -1    82             break;
   -1    83         }
   -1    84 
  341    85         // sleep
  342    86         let time2 = time::Instant::now();
  343    87         if TICK > time2 - time1 {

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

@@ -1,7 +1,5 @@
    1     1 extern crate libc;
    2     2 
    3    -1 const Y_FACTOR: f32 = 1.4;
    4    -1 
    5     3 fn get_terminal_size() -> (usize, usize) {
    6     4     let w = [0u16; 4];
    7     5     unsafe {
@@ -146,20 +144,4 @@ impl Screen {
  146   144 
  147   145         ti::sgr0();
  148   146     }
  149    -1 
  150    -1     pub fn convert_x(&self, x: f32) -> i64 {
  151    -1         return x as i64;
  152    -1     }
  153    -1 
  154    -1     pub fn convert_y(&self, y: f32) -> i64 {
  155    -1         return (y / Y_FACTOR) as i64;
  156    -1     }
  157    -1 
  158    -1     pub fn iconvert_x(&self, x: usize) -> f32 {
  159    -1         return x as f32;
  160    -1     }
  161    -1 
  162    -1     pub fn iconvert_y(&self, y: usize) -> f32 {
  163    -1         return y as f32 * Y_FACTOR;
  164    -1     }
  165   147 }

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

@@ -1,6 +1,24 @@
    1     1 use crate::sprites;
    2     2 use crate::term::Screen;
    3     3 
   -1     4 const Y_FACTOR: f32 = 1.4;
   -1     5 
   -1     6 pub fn convert_x(x: f32) -> i64 {
   -1     7     return x as i64;
   -1     8 }
   -1     9 
   -1    10 pub fn convert_y(y: f32) -> i64 {
   -1    11     return (y / Y_FACTOR) as i64;
   -1    12 }
   -1    13 
   -1    14 pub fn iconvert_x(x: usize) -> f32 {
   -1    15     return x as f32;
   -1    16 }
   -1    17 
   -1    18 pub fn iconvert_y(y: usize) -> f32 {
   -1    19     return y as f32 * Y_FACTOR;
   -1    20 }
   -1    21 
    4    22 pub struct Window {
    5    23     pub height: usize,
    6    24     pub width: usize,
@@ -29,8 +47,8 @@ impl Window {
   29    47         sprite: &sprites::Sprite,
   30    48         invert: bool,
   31    49     ) {
   32    -1         let x0 = screen.convert_x(cx) - sprites::WIDTH as i64 / 2;
   33    -1         let y0 = screen.convert_y(cy) + sprites::WIDTH as i64 / 2 - sprites::HEIGHT as i64;
   -1    50         let x0 = convert_x(cx) - sprites::WIDTH as i64 / 2;
   -1    51         let y0 = convert_y(cy) + sprites::WIDTH as i64 / 2 - sprites::HEIGHT as i64;
   34    52 
   35    53         for dy in 0..sprites::HEIGHT {
   36    54             let y = y0 + dy as i64;
@@ -60,17 +78,17 @@ impl Window {
   60    78     pub fn circle(&self, screen: &mut Screen, cx: f32, cy: f32, r: f32, color: [u8; 3]) {
   61    79         let r2 = r * r;
   62    80 
   63    -1         let y0 = screen.convert_y(cy - r).max(0).min(self.height as i64 - 1) as usize;
   64    -1         let x0 = screen.convert_x(cx - r).max(0).min(self.width as i64 - 1) as usize;
   -1    81         let y0 = convert_y(cy - r).max(0).min(self.height as i64 - 1) as usize;
   -1    82         let x0 = convert_x(cx - r).max(0).min(self.width as i64 - 1) as usize;
   65    83 
   66    -1         let y1 = screen.convert_y(cy + r).max(0).min(self.height as i64 - 1) as usize;
   67    -1         let x1 = screen.convert_x(cx + r).max(0).min(self.width as i64) as usize;
   -1    84         let y1 = convert_y(cy + r).max(0).min(self.height as i64 - 1) as usize;
   -1    85         let x1 = convert_x(cx + r).max(0).min(self.width as i64) as usize;
   68    86 
   69    87         for y in y0..=y1 {
   70    -1             let dy = screen.iconvert_y(y) - cy;
   -1    88             let dy = iconvert_y(y) - cy;
   71    89             let y2 = dy * dy;
   72    90             for x in x0..=x1 {
   73    -1                 let dx = screen.iconvert_x(x) - cx;
   -1    91                 let dx = iconvert_x(x) - cx;
   74    92                 if dx * dx + y2 <= r2 {
   75    93                     self.set(screen, x, y, color);
   76    94                 }