add borders around panels
This commit is contained in:
		
							parent
							
								
									74d146242b
								
							
						
					
					
						commit
						12f48497ae
					
				
					 6 changed files with 368 additions and 197 deletions
				
			
		
							
								
								
									
										55
									
								
								src/bar.rs
									
										
									
									
									
								
							
							
						
						
									
										55
									
								
								src/bar.rs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,60 +1,15 @@
 | 
			
		|||
use crate::Currency;
 | 
			
		||||
use servicepoint::{CharGridMutExt, GridMut, WindowMut};
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
use servicepoint::GridMut;
 | 
			
		||||
#[derive(Debug, Clone, Copy)]
 | 
			
		||||
pub struct Bar {
 | 
			
		||||
    name: &'static str,
 | 
			
		||||
    progress: f64,
 | 
			
		||||
    pub(crate) enabled: bool,
 | 
			
		||||
    pub(crate) speed: f64,
 | 
			
		||||
    pub(crate) productivity: f64,
 | 
			
		||||
    pub progress: f64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Bar {
 | 
			
		||||
    pub(crate) fn new(productivity: f64, speed: f64, name: &'static str) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            name,
 | 
			
		||||
            productivity,
 | 
			
		||||
            speed,
 | 
			
		||||
            enabled: false,
 | 
			
		||||
            progress: 0f64,
 | 
			
		||||
        }
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        Self { progress: 0.0 }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn name(&self) -> &'static str {
 | 
			
		||||
        self.name
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn progress(&mut self, delta: Duration) -> Currency {
 | 
			
		||||
        if !self.enabled {
 | 
			
		||||
            return 0.0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let extra_progress = delta.mul_f64(self.speed).as_secs_f64();
 | 
			
		||||
        let progress = self.progress + extra_progress;
 | 
			
		||||
        let completions = progress.floor();
 | 
			
		||||
        self.progress = progress - completions;
 | 
			
		||||
 | 
			
		||||
        completions * self.productivity
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn draw<C: GridMut<char>, P: GridMut<bool>>(&self, chars: &mut C, bitmap: &mut P) {
 | 
			
		||||
        if !self.enabled {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut bitmap = WindowMut::new(bitmap, 0, 0, bitmap.width() / 2, bitmap.height()).unwrap();
 | 
			
		||||
        let mut chars = WindowMut::new(
 | 
			
		||||
            chars,
 | 
			
		||||
            chars.width() / 2 + 1,
 | 
			
		||||
            0,
 | 
			
		||||
            chars.width() / 2 - 1,
 | 
			
		||||
            chars.height(),
 | 
			
		||||
        )
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
        chars.set_row_str(0, self.name).unwrap();
 | 
			
		||||
    pub fn draw<P: GridMut<bool>>(&self, bitmap: &mut P) {
 | 
			
		||||
        bitmap.fill(false);
 | 
			
		||||
 | 
			
		||||
        let margin = 1;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										97
									
								
								src/border_panel.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/border_panel.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,97 @@
 | 
			
		|||
use servicepoint::{CharGridMutExt, Grid, GridMut, TILE_SIZE, WindowMut};
 | 
			
		||||
use std::io::BufRead;
 | 
			
		||||
 | 
			
		||||
pub type BorderPattern = [bool; TILE_SIZE];
 | 
			
		||||
 | 
			
		||||
pub const OUTER_BORDER: BorderPattern = [false, false, false, true, true, false, true, false];
 | 
			
		||||
pub const INNER_BORDER: BorderPattern = [false, false, false, true, true, false, false, false];
 | 
			
		||||
 | 
			
		||||
pub fn draw_border_panel<'t, C: GridMut<char>, P: GridMut<bool>>(
 | 
			
		||||
    mut chars: WindowMut<'t, char, C>,
 | 
			
		||||
    mut bitmap: WindowMut<'t, bool, P>,
 | 
			
		||||
    label: &'static str,
 | 
			
		||||
    border_pattern: BorderPattern,
 | 
			
		||||
) -> (WindowMut<'t, char, C>, WindowMut<'t, bool, P>) {
 | 
			
		||||
    let tile_width = chars.width();
 | 
			
		||||
    let tile_height = chars.height();
 | 
			
		||||
    let pixel_width = bitmap.width();
 | 
			
		||||
    let pixel_height = bitmap.height();
 | 
			
		||||
 | 
			
		||||
    chars
 | 
			
		||||
        .window_mut(2, 0, tile_width - 4, 1)
 | 
			
		||||
        .unwrap()
 | 
			
		||||
        .set_row_str(0, label)
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
    // border top+bottom
 | 
			
		||||
    for tile_x in 1..tile_width - 1 {
 | 
			
		||||
        for inner_x in 0..TILE_SIZE {
 | 
			
		||||
            let x = tile_x * TILE_SIZE + inner_x;
 | 
			
		||||
            let draw_top = tile_x <= 1 || tile_x >= label.chars().count() + 2;
 | 
			
		||||
 | 
			
		||||
            for inner_y in 0..TILE_SIZE {
 | 
			
		||||
                let val = border_pattern[inner_y];
 | 
			
		||||
                if draw_top {
 | 
			
		||||
                    bitmap.set(x, inner_y, val);
 | 
			
		||||
                }
 | 
			
		||||
                bitmap.set(x, pixel_height - inner_y - 1, val);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // border left + right
 | 
			
		||||
    for tile_y in 1..tile_height - 1 {
 | 
			
		||||
        for inner_y in 0..TILE_SIZE {
 | 
			
		||||
            let y = tile_y * TILE_SIZE + inner_y;
 | 
			
		||||
            for inner_x in 0..TILE_SIZE {
 | 
			
		||||
                let val = border_pattern[inner_x];
 | 
			
		||||
 | 
			
		||||
                bitmap.set(inner_x, y, val);
 | 
			
		||||
                bitmap.set(pixel_width - inner_x - 1, y, val);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // edges
 | 
			
		||||
    for pat_index in 0..TILE_SIZE {
 | 
			
		||||
        if !border_pattern[pat_index] {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        for extend in 0..=TILE_SIZE - pat_index {
 | 
			
		||||
            bitmap.set(TILE_SIZE - extend, pat_index, true);
 | 
			
		||||
            bitmap.set(pixel_width - 1 - TILE_SIZE + extend, pat_index, true);
 | 
			
		||||
 | 
			
		||||
            bitmap.set(pat_index, TILE_SIZE - extend, true);
 | 
			
		||||
            bitmap.set(pat_index, pixel_height - 1 - TILE_SIZE + extend, true);
 | 
			
		||||
 | 
			
		||||
            bitmap.set(TILE_SIZE - extend, pixel_height - pat_index - 1, true);
 | 
			
		||||
            bitmap.set(
 | 
			
		||||
                pixel_width - 1 - TILE_SIZE + extend,
 | 
			
		||||
                pixel_height - pat_index - 1,
 | 
			
		||||
                true,
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
            bitmap.set(pixel_width - pat_index - 1, TILE_SIZE - extend, true);
 | 
			
		||||
            bitmap.set(
 | 
			
		||||
                pixel_width - pat_index - 1,
 | 
			
		||||
                pixel_height - 1 - TILE_SIZE + extend,
 | 
			
		||||
                true,
 | 
			
		||||
            );
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let (chars, _) = chars.split_horizontal_mut(tile_width - 1).unwrap();
 | 
			
		||||
    let (_, chars) = chars.split_horizontal_mut(1).unwrap();
 | 
			
		||||
    let (chars, _) = chars.split_vertical_mut(tile_height - 1).unwrap();
 | 
			
		||||
    let (_, chars) = chars.split_vertical_mut(1).unwrap();
 | 
			
		||||
 | 
			
		||||
    let (bitmap, _) = bitmap
 | 
			
		||||
        .split_horizontal_mut(pixel_width - TILE_SIZE)
 | 
			
		||||
        .unwrap();
 | 
			
		||||
    let (_, bitmap) = bitmap.split_horizontal_mut(TILE_SIZE).unwrap();
 | 
			
		||||
    let (bitmap, _) = bitmap.split_vertical_mut(pixel_height - TILE_SIZE).unwrap();
 | 
			
		||||
    let (_, bitmap) = bitmap.split_vertical_mut(TILE_SIZE).unwrap();
 | 
			
		||||
 | 
			
		||||
    (chars, bitmap)
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										125
									
								
								src/game.rs
									
										
									
									
									
								
							
							
						
						
									
										125
									
								
								src/game.rs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,6 +1,10 @@
 | 
			
		|||
use crate::unlocks::{Unlock, UnlockSystem};
 | 
			
		||||
use crate::{Currency, bar::Bar};
 | 
			
		||||
use servicepoint::{Bitmap, CharGrid, CharGridMutExt, TILE_SIZE, WindowMut};
 | 
			
		||||
use crate::border_panel::{INNER_BORDER, OUTER_BORDER, draw_border_panel};
 | 
			
		||||
use crate::row::Row;
 | 
			
		||||
use crate::{
 | 
			
		||||
    Currency,
 | 
			
		||||
    unlocks::{Unlock, UnlockSystem},
 | 
			
		||||
};
 | 
			
		||||
use servicepoint::{Bitmap, CharGrid, CharGridMutExt, GridMut, TILE_SIZE, WindowMut};
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
| 
						 | 
				
			
			@ -12,14 +16,14 @@ pub struct Game {
 | 
			
		|||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct State {
 | 
			
		||||
    pub(crate) currency: Currency,
 | 
			
		||||
    pub(crate) speed: f64,
 | 
			
		||||
    pub(crate) productivity: f64,
 | 
			
		||||
    pub(crate) bars: Vec<Bar>,
 | 
			
		||||
    pub currency: Currency,
 | 
			
		||||
    pub speed: f64,
 | 
			
		||||
    pub productivity: f64,
 | 
			
		||||
    pub rows: Vec<Row>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Game {
 | 
			
		||||
    const BAR_NAMES: [&'static str; 11] = [
 | 
			
		||||
    const BAR_NAMES: [&'static str; 10] = [
 | 
			
		||||
        "Powering infrastructure",
 | 
			
		||||
        "Dusting ServicePoint",
 | 
			
		||||
        "Activating colorful lights",
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +34,7 @@ impl Game {
 | 
			
		|||
        "Untangling 'block chain'",
 | 
			
		||||
        "Refilling sticker box",
 | 
			
		||||
        "Setting room to public",
 | 
			
		||||
        "Welcoming creatures",
 | 
			
		||||
        // "Welcoming creatures",
 | 
			
		||||
    ];
 | 
			
		||||
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
| 
						 | 
				
			
			@ -38,7 +42,7 @@ impl Game {
 | 
			
		|||
            .iter()
 | 
			
		||||
            .enumerate()
 | 
			
		||||
            .map(|(index, name)| {
 | 
			
		||||
                Bar::new(
 | 
			
		||||
                Row::new(
 | 
			
		||||
                    2usize.pow(index as u32) as f64,
 | 
			
		||||
                    1.0 * (0.5f64.powi(index as i32)),
 | 
			
		||||
                    *name,
 | 
			
		||||
| 
						 | 
				
			
			@ -46,7 +50,7 @@ impl Game {
 | 
			
		|||
            })
 | 
			
		||||
            .collect();
 | 
			
		||||
        let state = State {
 | 
			
		||||
            bars,
 | 
			
		||||
            rows: bars,
 | 
			
		||||
            currency: 1f64,
 | 
			
		||||
            speed: 1f64,
 | 
			
		||||
            productivity: 1f64,
 | 
			
		||||
| 
						 | 
				
			
			@ -58,93 +62,80 @@ impl Game {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn progress(&mut self, delta: Duration) {
 | 
			
		||||
    pub fn progress(&mut self, delta: Duration) {
 | 
			
		||||
        let adjusted_delta = delta.mul_f64(self.state.speed);
 | 
			
		||||
 | 
			
		||||
        let extra_currency = self.state.productivity
 | 
			
		||||
            * self
 | 
			
		||||
                .state
 | 
			
		||||
                .bars
 | 
			
		||||
                .rows
 | 
			
		||||
                .iter_mut()
 | 
			
		||||
                .map(|bar| bar.progress(adjusted_delta))
 | 
			
		||||
                .sum::<Currency>();
 | 
			
		||||
        self.state.currency += extra_currency;
 | 
			
		||||
        self.total_currency += extra_currency;
 | 
			
		||||
        self.unlocks.check_next(&mut self.state);
 | 
			
		||||
        self.unlocks.progress(&mut self.state);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn draw(
 | 
			
		||||
    pub fn draw(
 | 
			
		||||
        &self,
 | 
			
		||||
        text_layer: &mut WindowMut<char, CharGrid>,
 | 
			
		||||
        pixel_layer: &mut WindowMut<bool, Bitmap>,
 | 
			
		||||
        text_layer: WindowMut<char, CharGrid>,
 | 
			
		||||
        pixel_layer: WindowMut<bool, Bitmap>,
 | 
			
		||||
    ) {
 | 
			
		||||
        text_layer
 | 
			
		||||
            .set_row_str(0, "Discordia Boot Procedure")
 | 
			
		||||
        let (text_layer, pixel_layer) = draw_border_panel(
 | 
			
		||||
            text_layer,
 | 
			
		||||
            pixel_layer,
 | 
			
		||||
            " Discordia Boot Procedure ",
 | 
			
		||||
            OUTER_BORDER,
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
        let unlocks_height = 3 + 2;
 | 
			
		||||
        let (unlocks_text, text_layer) = text_layer.split_vertical_mut(unlocks_height).unwrap();
 | 
			
		||||
        let (unlocks_pixel, pixel_layer) = pixel_layer
 | 
			
		||||
            .split_vertical_mut(unlocks_height * TILE_SIZE)
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        self.draw_bars(text_layer, pixel_layer, 1);
 | 
			
		||||
        self.unlocks.draw(unlocks_text, unlocks_pixel);
 | 
			
		||||
 | 
			
		||||
        let mut row = text_layer.height() - 1;
 | 
			
		||||
        self.draw_stats(text_layer, row);
 | 
			
		||||
        row -= 1;
 | 
			
		||||
        let bars_height = self.state.rows.len() + 2;
 | 
			
		||||
        let (bars_text, text_layer) = text_layer.split_vertical_mut(bars_height).unwrap();
 | 
			
		||||
        let (bars_pixel, _) = pixel_layer
 | 
			
		||||
            .split_vertical_mut(bars_height * TILE_SIZE)
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        self.draw_bars(bars_text, bars_pixel);
 | 
			
		||||
 | 
			
		||||
        if let Some(next_upgrade) = self.unlocks.peek_next() {
 | 
			
		||||
            self.draw_next_upgrade(text_layer, row, next_upgrade);
 | 
			
		||||
            row -= 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(prev_unlock) = self.unlocks.previous() {
 | 
			
		||||
            self.draw_prev_unlock(text_layer, row, prev_unlock);
 | 
			
		||||
        }
 | 
			
		||||
        let free_row_bottom = text_layer.height() - 1;
 | 
			
		||||
        self.draw_stats(text_layer, free_row_bottom);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn draw_next_upgrade(&self, text_layer: &mut WindowMut<char, CharGrid>, row: usize, next_upgrade: &Unlock) {
 | 
			
		||||
        text_layer
 | 
			
		||||
            .window_mut(0, row, text_layer.width(), 1)
 | 
			
		||||
            .unwrap()
 | 
			
		||||
            .set_row_str(0, &format!("Next unlock: {:.0}/{} Hacks", self.state.currency, next_upgrade.cost))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn draw_stats(&self, text_layer: &mut WindowMut<char, CharGrid>, row: usize) {
 | 
			
		||||
        text_layer
 | 
			
		||||
            .window_mut(0, row, text_layer.width() / 2, 1)
 | 
			
		||||
            .unwrap()
 | 
			
		||||
            .set_row_str(0, &format!("Hack Score: {:.2}", self.total_currency))
 | 
			
		||||
    fn draw_stats<C: GridMut<char>>(&self, text_layer: WindowMut<char, C>, row: usize) {
 | 
			
		||||
        let middle = text_layer.width() / 2;
 | 
			
		||||
        let (mut left, mut right) = text_layer.split_horizontal_mut(middle).unwrap();
 | 
			
		||||
        left.set_row_str(0, &format!("Hack Score: {:.2}", self.total_currency))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
 | 
			
		||||
        if self.unlocks.bought() > 0 {
 | 
			
		||||
            text_layer
 | 
			
		||||
                .window_mut(
 | 
			
		||||
                    text_layer.width() / 2,
 | 
			
		||||
                    row,
 | 
			
		||||
                    text_layer.width() / 2,
 | 
			
		||||
                    1,
 | 
			
		||||
                )
 | 
			
		||||
                .unwrap()
 | 
			
		||||
            right
 | 
			
		||||
                .set_row_str(0, &format!(" Unlocks: {}", self.unlocks.bought()))
 | 
			
		||||
                .unwrap();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn draw_bars(&self, text_layer: &mut WindowMut<char, CharGrid>, pixel_layer: &mut WindowMut<bool, Bitmap>, mut row: usize) {
 | 
			
		||||
        for bar in self.state.bars.iter() {
 | 
			
		||||
    fn draw_bars<C: GridMut<char>, P: GridMut<bool>>(
 | 
			
		||||
        &self,
 | 
			
		||||
        text_layer: WindowMut<char, C>,
 | 
			
		||||
        pixel_layer: WindowMut<bool, P>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let (mut text_layer, mut pixel_layer) =
 | 
			
		||||
            draw_border_panel(text_layer, pixel_layer, " Processes ", INNER_BORDER);
 | 
			
		||||
 | 
			
		||||
        for (index, row) in self.state.rows.iter().enumerate() {
 | 
			
		||||
            let mut bar_window = pixel_layer
 | 
			
		||||
                .window_mut(0, row * TILE_SIZE, pixel_layer.width(), TILE_SIZE)
 | 
			
		||||
                .window_mut(0, index * TILE_SIZE, pixel_layer.width(), TILE_SIZE)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
            let mut label_window = text_layer
 | 
			
		||||
                .window_mut(0, row, text_layer.width(), 1)
 | 
			
		||||
                .window_mut(0, index, text_layer.width(), 1)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
            bar.draw(&mut label_window, &mut bar_window);
 | 
			
		||||
 | 
			
		||||
            row += 1;
 | 
			
		||||
            row.draw(&mut label_window, &mut bar_window);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn draw_prev_unlock(&self, chars: &mut WindowMut<char, CharGrid>, row: usize, prev: &String) {
 | 
			
		||||
        chars
 | 
			
		||||
            .window_mut(0, row, chars.width(), 1)
 | 
			
		||||
            .unwrap()
 | 
			
		||||
            .set_row_str(0, &format!("Unlocked: {}", prev))
 | 
			
		||||
            .unwrap();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,9 @@ use servicepoint::{
 | 
			
		|||
use std::{net::UdpSocket, thread::sleep, time::Instant};
 | 
			
		||||
 | 
			
		||||
mod bar;
 | 
			
		||||
mod border_panel;
 | 
			
		||||
mod game;
 | 
			
		||||
mod row;
 | 
			
		||||
mod unlocks;
 | 
			
		||||
 | 
			
		||||
type Currency = f64;
 | 
			
		||||
| 
						 | 
				
			
			@ -35,13 +37,13 @@ fn main() {
 | 
			
		|||
        chars.fill(' ');
 | 
			
		||||
        pixels.fill(false);
 | 
			
		||||
 | 
			
		||||
        let mut chars_view = chars
 | 
			
		||||
        let chars_view = chars
 | 
			
		||||
            .window_mut(0, 0, chars.width(), chars.height())
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        let mut pixels_view = pixels
 | 
			
		||||
        let pixels_view = pixels
 | 
			
		||||
            .window_mut(0, 0, pixels.width(), pixels.height())
 | 
			
		||||
            .unwrap();
 | 
			
		||||
        state.draw(&mut chars_view, &mut pixels_view);
 | 
			
		||||
        state.draw(chars_view, pixels_view);
 | 
			
		||||
 | 
			
		||||
        connection
 | 
			
		||||
            .send_command(CharGridCommand::from(chars.clone()))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										66
									
								
								src/row.rs
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/row.rs
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,66 @@
 | 
			
		|||
use crate::{Currency, bar::Bar};
 | 
			
		||||
use servicepoint::{CharGridMutExt, GridMut, WindowMut};
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Copy)]
 | 
			
		||||
pub struct Row {
 | 
			
		||||
    name: &'static str,
 | 
			
		||||
    pub enabled: bool,
 | 
			
		||||
    pub speed: f64,
 | 
			
		||||
    pub productivity: f64,
 | 
			
		||||
    bar: Bar,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Row {
 | 
			
		||||
    pub fn new(productivity: f64, speed: f64, name: &'static str) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            name,
 | 
			
		||||
            productivity,
 | 
			
		||||
            speed,
 | 
			
		||||
            enabled: false,
 | 
			
		||||
            bar: Bar { progress: 0f64 },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn name(&self) -> &'static str {
 | 
			
		||||
        self.name
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn progress(&mut self, delta: Duration) -> Currency {
 | 
			
		||||
        if !self.enabled {
 | 
			
		||||
            return 0.0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let extra_progress = delta.mul_f64(self.speed).as_secs_f64();
 | 
			
		||||
        let progress = self.bar.progress + extra_progress;
 | 
			
		||||
        let completions = progress.floor();
 | 
			
		||||
        self.bar.progress = progress - completions;
 | 
			
		||||
 | 
			
		||||
        completions * self.productivity
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn draw<C: GridMut<char>, P: GridMut<bool>>(&self, chars: &mut C, bitmap: &mut P) {
 | 
			
		||||
        if !self.enabled {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let mut bitmap = WindowMut::new(bitmap, 0, 0, bitmap.width() / 2, bitmap.height()).unwrap();
 | 
			
		||||
        self.bar.draw(&mut bitmap);
 | 
			
		||||
 | 
			
		||||
        let mut chars = WindowMut::new(
 | 
			
		||||
            chars,
 | 
			
		||||
            chars.width() / 2 + 1,
 | 
			
		||||
            0,
 | 
			
		||||
            chars.width() / 2 - 1,
 | 
			
		||||
            chars.height(),
 | 
			
		||||
        )
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
        chars
 | 
			
		||||
            .set_row_str(
 | 
			
		||||
                0,
 | 
			
		||||
                &*self.name.chars().take(chars.width()).collect::<String>(),
 | 
			
		||||
            )
 | 
			
		||||
            .unwrap();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										214
									
								
								src/unlocks.rs
									
										
									
									
									
								
							
							
						
						
									
										214
									
								
								src/unlocks.rs
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1,20 +1,26 @@
 | 
			
		|||
use crate::Currency;
 | 
			
		||||
use crate::game::{Game, State};
 | 
			
		||||
use std::collections::VecDeque;
 | 
			
		||||
use std::fmt::{Debug, Formatter, format};
 | 
			
		||||
use std::ops::Mul;
 | 
			
		||||
use crate::border_panel::{INNER_BORDER, draw_border_panel};
 | 
			
		||||
use crate::{Currency, bar::Bar, game::State, row::Row};
 | 
			
		||||
use servicepoint::{CharGridMutExt, GridMut, TILE_SIZE, WindowMut};
 | 
			
		||||
use std::{
 | 
			
		||||
    collections::VecDeque,
 | 
			
		||||
    fmt::{Debug, Formatter},
 | 
			
		||||
    iter::Map,
 | 
			
		||||
    ops::{Mul, Range},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub(crate) struct UnlockSystem {
 | 
			
		||||
pub struct UnlockSystem {
 | 
			
		||||
    bought: usize,
 | 
			
		||||
    last_unlock: Option<String>,
 | 
			
		||||
    unlock_queue: VecDeque<Unlock>,
 | 
			
		||||
    all_unlocks_bar: Bar,
 | 
			
		||||
    next_unlock_bar: Bar,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub(crate) struct Unlock {
 | 
			
		||||
    pub(crate) name: String,
 | 
			
		||||
    pub(crate) cost: Currency,
 | 
			
		||||
    pub(crate) apply: Box<dyn Fn(&mut State)>,
 | 
			
		||||
pub struct Unlock {
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub cost: Currency,
 | 
			
		||||
    pub apply: Box<dyn Fn(&mut State)>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Debug for Unlock {
 | 
			
		||||
| 
						 | 
				
			
			@ -27,93 +33,147 @@ impl Debug for Unlock {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
impl UnlockSystem {
 | 
			
		||||
    pub(crate) fn new(start_state: &State) -> Self {
 | 
			
		||||
        let mut upgrades = start_state
 | 
			
		||||
            .bars
 | 
			
		||||
            .iter()
 | 
			
		||||
            .enumerate()
 | 
			
		||||
            .flat_map(|(index, bar)| {
 | 
			
		||||
                let initial_cost = 10usize.pow(index as u32);
 | 
			
		||||
                [
 | 
			
		||||
                    Unlock {
 | 
			
		||||
                        name: format!("Start {}", bar.name()),
 | 
			
		||||
                        cost: initial_cost as f64,
 | 
			
		||||
                        apply: Box::new(move |game| {
 | 
			
		||||
                            game.bars[index].enabled = true;
 | 
			
		||||
                        }),
 | 
			
		||||
                    },
 | 
			
		||||
                    Unlock {
 | 
			
		||||
                        name: format!("{} productivity", bar.name()),
 | 
			
		||||
                        cost: initial_cost.mul(2) as f64,
 | 
			
		||||
                        apply: Box::new(move |game| {
 | 
			
		||||
                            game.bars[index].productivity *= 2.0;
 | 
			
		||||
                        }),
 | 
			
		||||
                    },
 | 
			
		||||
                    Unlock {
 | 
			
		||||
                        name: format!("{} speed", bar.name()),
 | 
			
		||||
                        cost: initial_cost.mul(20) as f64,
 | 
			
		||||
                        apply: Box::new(move |game| {
 | 
			
		||||
                            game.bars[index].speed *= 1.5;
 | 
			
		||||
                        }),
 | 
			
		||||
                    },
 | 
			
		||||
                ]
 | 
			
		||||
            })
 | 
			
		||||
            .chain(
 | 
			
		||||
                (1..10).map(|level|
 | 
			
		||||
                    Unlock {
 | 
			
		||||
                        name: format!("The answer {}", level),
 | 
			
		||||
                        cost: (42usize * 10usize.pow(level)) as f64,
 | 
			
		||||
                        apply: Box::new(move |game| {
 | 
			
		||||
                            game.speed *= 1.1;
 | 
			
		||||
                        }),
 | 
			
		||||
                    }
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
            .chain(
 | 
			
		||||
                (1..10).map(|level|
 | 
			
		||||
                    Unlock {
 | 
			
		||||
                        name: format!("??? {}", level),
 | 
			
		||||
                        cost: (23usize * 10usize.pow(level)) as f64,
 | 
			
		||||
                        apply: Box::new(move |game| {
 | 
			
		||||
                            game.productivity *= 1.1;
 | 
			
		||||
                        }),
 | 
			
		||||
                    },
 | 
			
		||||
                )
 | 
			
		||||
            )
 | 
			
		||||
    pub fn new(start_state: &State) -> Self {
 | 
			
		||||
        let mut upgrades = Self::gen_bar_upgrades(&start_state.rows)
 | 
			
		||||
            .chain(Self::gen_speed_upgrades())
 | 
			
		||||
            .chain(Self::gen_productivity_upgrades())
 | 
			
		||||
            .collect::<Vec<_>>();
 | 
			
		||||
        upgrades.sort_by(|l, r| l.cost.total_cmp(&r.cost));
 | 
			
		||||
 | 
			
		||||
        let unlock_queue = upgrades.into();
 | 
			
		||||
 | 
			
		||||
        Self {
 | 
			
		||||
            unlock_queue,
 | 
			
		||||
            unlock_queue: upgrades.into(),
 | 
			
		||||
            bought: 0,
 | 
			
		||||
            last_unlock: None
 | 
			
		||||
            last_unlock: None,
 | 
			
		||||
            all_unlocks_bar: Bar::new(),
 | 
			
		||||
            next_unlock_bar: Bar::new(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn peek_next(&self) -> Option<&Unlock> {
 | 
			
		||||
    pub fn peek_next(&self) -> Option<&Unlock> {
 | 
			
		||||
        self.unlock_queue.front()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn check_next(&mut self, state: &mut State) {
 | 
			
		||||
    pub fn progress(&mut self, state: &mut State) {
 | 
			
		||||
        if let Some(next_upgrade) = self.peek_next() {
 | 
			
		||||
            if next_upgrade.cost <= state.currency {
 | 
			
		||||
                log::info!("Applying upgrade {:?}", next_upgrade);
 | 
			
		||||
                let next_upgrade = self.unlock_queue.pop_front().unwrap();
 | 
			
		||||
                state.currency -= next_upgrade.cost;
 | 
			
		||||
                (next_upgrade.apply)(state);
 | 
			
		||||
                self.last_unlock = Some(next_upgrade.name);
 | 
			
		||||
                self.bought += 1;
 | 
			
		||||
                self.buy_next(state);
 | 
			
		||||
                self.next_unlock_bar.progress = 0.0;
 | 
			
		||||
            } else {
 | 
			
		||||
                self.next_unlock_bar.progress = state.currency / next_upgrade.cost;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn bought(&self) -> usize {
 | 
			
		||||
    pub fn bought(&self) -> usize {
 | 
			
		||||
        self.bought
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub(crate) fn previous(&self) -> Option<&String> {
 | 
			
		||||
    pub fn previous(&self) -> Option<&String> {
 | 
			
		||||
        self.last_unlock.as_ref()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn draw<C: GridMut<char>, P: GridMut<bool>>(
 | 
			
		||||
        &self,
 | 
			
		||||
        chars: WindowMut<char, C>,
 | 
			
		||||
        bitmap: WindowMut<bool, P>,
 | 
			
		||||
    ) {
 | 
			
		||||
        let (mut chars, mut bitmap) = draw_border_panel(chars, bitmap, " Unlocks ", INNER_BORDER);
 | 
			
		||||
        let mut free_row_bottom = chars.height() - 1;
 | 
			
		||||
 | 
			
		||||
        let pixel_width = bitmap.width();
 | 
			
		||||
        self.all_unlocks_bar.draw(
 | 
			
		||||
            &mut WindowMut::new(
 | 
			
		||||
                &mut bitmap,
 | 
			
		||||
                0,
 | 
			
		||||
                free_row_bottom * TILE_SIZE,
 | 
			
		||||
                pixel_width,
 | 
			
		||||
                TILE_SIZE,
 | 
			
		||||
            )
 | 
			
		||||
            .unwrap(),
 | 
			
		||||
        );
 | 
			
		||||
        free_row_bottom -= 1;
 | 
			
		||||
 | 
			
		||||
        self.next_unlock_bar.draw(
 | 
			
		||||
            &mut WindowMut::new(
 | 
			
		||||
                &mut bitmap,
 | 
			
		||||
                0,
 | 
			
		||||
                free_row_bottom * TILE_SIZE,
 | 
			
		||||
                pixel_width,
 | 
			
		||||
                TILE_SIZE,
 | 
			
		||||
            )
 | 
			
		||||
            .unwrap(),
 | 
			
		||||
        );
 | 
			
		||||
        free_row_bottom -= 1;
 | 
			
		||||
 | 
			
		||||
        if let Some(prev_unlock) = self.previous() {
 | 
			
		||||
            let char_width = chars.width();
 | 
			
		||||
            WindowMut::new(&mut chars, 0, free_row_bottom, char_width, 1)
 | 
			
		||||
                .unwrap()
 | 
			
		||||
                .set_row_str(0, &format!("Unlocked: {}", prev_unlock))
 | 
			
		||||
                .unwrap();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn buy_next(&mut self, state: &mut State) {
 | 
			
		||||
        let next_upgrade = self.unlock_queue.pop_front().unwrap();
 | 
			
		||||
        log::info!("Applying upgrade {:?}", &next_upgrade);
 | 
			
		||||
 | 
			
		||||
        state.currency -= next_upgrade.cost;
 | 
			
		||||
        (next_upgrade.apply)(state);
 | 
			
		||||
 | 
			
		||||
        self.last_unlock = Some(next_upgrade.name);
 | 
			
		||||
        self.bought += 1;
 | 
			
		||||
 | 
			
		||||
        let total = self.bought + self.unlock_queue.len();
 | 
			
		||||
        self.all_unlocks_bar.progress = self.bought as f64 / total as f64;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn gen_bar_upgrades(bars: &Vec<Row>) -> impl Iterator<Item = Unlock> {
 | 
			
		||||
        bars.iter().enumerate().flat_map(|(index, bar)| {
 | 
			
		||||
            let initial_cost = 10usize.pow(index as u32);
 | 
			
		||||
            [
 | 
			
		||||
                Unlock {
 | 
			
		||||
                    name: format!("Start {}", bar.name()),
 | 
			
		||||
                    cost: initial_cost as f64,
 | 
			
		||||
                    apply: Box::new(move |game| {
 | 
			
		||||
                        game.rows[index].enabled = true;
 | 
			
		||||
                    }),
 | 
			
		||||
                },
 | 
			
		||||
                Unlock {
 | 
			
		||||
                    name: format!("{} productivity", bar.name()),
 | 
			
		||||
                    cost: initial_cost.mul(2) as f64,
 | 
			
		||||
                    apply: Box::new(move |game| {
 | 
			
		||||
                        game.rows[index].productivity *= 2.0;
 | 
			
		||||
                    }),
 | 
			
		||||
                },
 | 
			
		||||
                Unlock {
 | 
			
		||||
                    name: format!("{} speed", bar.name()),
 | 
			
		||||
                    cost: initial_cost.mul(20) as f64,
 | 
			
		||||
                    apply: Box::new(move |game| {
 | 
			
		||||
                        game.rows[index].speed *= 1.5;
 | 
			
		||||
                    }),
 | 
			
		||||
                },
 | 
			
		||||
            ]
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn gen_speed_upgrades() -> impl Iterator<Item = Unlock> {
 | 
			
		||||
        (1..10).map(|level| Unlock {
 | 
			
		||||
            name: format!("The answer {}", level),
 | 
			
		||||
            cost: (42usize * 10usize.pow(level)) as f64,
 | 
			
		||||
            apply: Box::new(move |game| {
 | 
			
		||||
                game.speed *= 1.1;
 | 
			
		||||
            }),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn gen_productivity_upgrades() -> Map<Range<u32>, fn(u32) -> Unlock> {
 | 
			
		||||
        (1..10).map(|level| Unlock {
 | 
			
		||||
            name: format!("??? {}", level),
 | 
			
		||||
            cost: (23usize * 10usize.pow(level)) as f64,
 | 
			
		||||
            apply: Box::new(move |game| {
 | 
			
		||||
                game.productivity *= 1.1;
 | 
			
		||||
            }),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue