use crate::border_panel::{INNER_BORDER, draw_border_panel}; use crate::{Currency, bar::Bar, game::State, row::Row}; use servicepoint::{Bitmap, CharGrid, CharGridMutExt, TILE_SIZE, WindowMut}; use std::{ collections::VecDeque, fmt::{Debug, Formatter}, ops::Mul, }; #[derive(Debug)] pub struct UnlockSystem { bought: usize, last_unlock: Option, unlock_queue: VecDeque, all_unlocks_bar: Bar, next_unlock_bar: Bar, total_unlock_count: usize, } pub struct Unlock { pub name: String, pub cost: Currency, pub apply: Box, } impl Debug for Unlock { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_fmt(format_args!( "Upgrade {{ name: {:?}, cost: {:?} }}", self.name, self.cost )) } } impl UnlockSystem { pub fn new(start_state: &State) -> Self { let mut upgrades = Self::gen_bar_enable_unlocks(&start_state.rows) .chain(Self::gen_global_speed_unlocks()) .chain(Self::gen_global_productivity_unlocks()) .chain(Self::gen_bar_speed_unlocks(&start_state.rows)) .chain(Self::gen_bar_productivity_unlocks(&start_state.rows)) .collect::>(); upgrades.sort_by(|l, r| l.cost.total_cmp(&r.cost)); Self { total_unlock_count: upgrades.len(), unlock_queue: upgrades.into(), bought: 0, last_unlock: None, all_unlocks_bar: Bar::new(), next_unlock_bar: Bar::new(), } } pub fn peek_next(&self) -> Option<&Unlock> { self.unlock_queue.front() } pub fn progress(&mut self, state: &mut State) { if let Some(next_upgrade) = self.peek_next() { if next_upgrade.cost <= state.currency { self.buy_next(state); self.next_unlock_bar.progress = 0.0; } else { self.next_unlock_bar.progress = state.currency / next_upgrade.cost; } } } pub fn draw( &self, chars: &mut WindowMut, bitmap: &mut WindowMut, ) { let title = format!(" Stage {}/{} ", self.bought, self.total_unlock_count); let (mut chars, mut bitmap) = draw_border_panel(chars, bitmap, &title, INNER_BORDER); let (all_unlocks_pixels, mut bitmap) = bitmap.split_vertical_mut(TILE_SIZE).unwrap(); self.all_unlocks_bar.draw(all_unlocks_pixels); let (next_unlocks_pixels, _) = bitmap.split_vertical_mut(TILE_SIZE).unwrap(); self.next_unlock_bar.draw(next_unlocks_pixels); if let Some(next) = self.peek_next() { let (_, mut next_unlock_chars) = chars.split_vertical_mut(2).unwrap(); next_unlock_chars .set_row_str(0, &format!("Loading: {}", next.name)) .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; self.all_unlocks_bar.progress = self.bought as f64 / self.total_unlock_count as f64; } fn gen_bar_enable_unlocks(bars: &[Row]) -> impl Iterator { bars.iter().enumerate().map(|(index, bar)| Unlock { name: format!("Start {}", bar.name()), cost: 10f64.powi(index as i32), apply: Box::new(move |game| { game.rows[index].enabled = true; }), }) } fn gen_global_speed_unlocks() -> impl Iterator { (1..32).map_while(|level| { Some(Unlock { name: format!("The answer {}", level), cost: 42f64.mul(2f64.powi(level)), apply: Box::new(move |game| { game.speed *= 1.15; }), }) }) } fn gen_global_productivity_unlocks() -> impl Iterator { (1..32).map(|level| Unlock { name: format!("??? {}", level), cost: 23f64.mul(2f64.powi(level)), apply: Box::new(move |game| { game.productivity *= 1.15; }), }) } fn gen_bar_productivity_unlocks(bars: &[Row]) -> impl Iterator { bars.iter().enumerate().flat_map(|(index, bar)| { (1..=20).map(move |level| Unlock { name: format!("{} productivity {level}", bar.name()), cost: 10f64.powi(index as i32).mul(3f64.powi(level)), apply: Box::new(move |game| { game.rows[index].productivity *= 1.5; }), }) }) } fn gen_bar_speed_unlocks(bars: &[Row]) -> impl Iterator { bars.iter().enumerate().flat_map(|(index, bar)| { (1..=15).map(move |level| Unlock { name: format!("{} speed {level}", bar.name()), cost: 10f64.powi(index as i32).mul(5f64.powi(level)), apply: Box::new(move |game| { game.rows[index].speed *= 1.5; }), }) }) } }