From 74d146242b9d672b867d1697a7dea2c8beb429be Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Sun, 6 Jul 2025 11:46:32 +0200 Subject: [PATCH] generated upgrades and bars --- src/bar.rs | 8 +-- src/game.rs | 125 ++++++++++++++++++++------------ src/main.rs | 2 +- src/unlocks.rs | 189 ++++++++++++++++++------------------------------- 4 files changed, 155 insertions(+), 169 deletions(-) diff --git a/src/bar.rs b/src/bar.rs index e4ddaac..e2c8dab 100644 --- a/src/bar.rs +++ b/src/bar.rs @@ -21,9 +21,11 @@ impl Bar { progress: 0f64, } } -} -impl Bar { + pub(crate) fn name(&self) -> &'static str { + self.name + } + pub(crate) fn progress(&mut self, delta: Duration) -> Currency { if !self.enabled { return 0.0; @@ -36,9 +38,7 @@ impl Bar { completions * self.productivity } -} -impl Bar { pub fn draw, P: GridMut>(&self, chars: &mut C, bitmap: &mut P) { if !self.enabled { return; diff --git a/src/game.rs b/src/game.rs index e07d752..203abe3 100644 --- a/src/game.rs +++ b/src/game.rs @@ -1,4 +1,4 @@ -use crate::unlocks::UnlockSystem; +use crate::unlocks::{Unlock, UnlockSystem}; use crate::{Currency, bar::Bar}; use servicepoint::{Bitmap, CharGrid, CharGridMutExt, TILE_SIZE, WindowMut}; use std::time::Duration; @@ -11,36 +11,50 @@ pub struct Game { } #[derive(Debug)] -pub struct State { +pub struct State { pub(crate) currency: Currency, pub(crate) speed: f64, pub(crate) productivity: f64, - pub(crate) bars: [Bar; STEP_COUNT], + pub(crate) bars: Vec, } impl Game { + const BAR_NAMES: [&'static str; 11] = [ + "Powering infrastructure", + "Dusting ServicePoint", + "Activating colorful lights", + "Dimming darkroom", + "Refilling Matemat", + "Pre-heating convectiomat", + "Resetting chair heights", + "Untangling 'block chain'", + "Refilling sticker box", + "Setting room to public", + "Welcoming creatures", + ]; + pub fn new() -> Self { + let bars = Self::BAR_NAMES + .iter() + .enumerate() + .map(|(index, name)| { + Bar::new( + 2usize.pow(index as u32) as f64, + 1.0 * (0.5f64.powi(index as i32)), + *name, + ) + }) + .collect(); + let state = State { + bars, + currency: 1f64, + speed: 1f64, + productivity: 1f64, + }; Self { total_currency: 0f64, - unlocks: UnlockSystem::new(), - state: State { - currency: 0f64, - speed: 1f64, - productivity: 1f64, - bars: [ - Bar::new(1f64, 1.0, "Powering infrastructure"), - Bar::new(2f64, 0.5, "Dusting ServicePoint"), - Bar::new(4f64, 0.25, "Activating colorful lights"), - Bar::new(8f64, 0.125, "Dimming darkroom"), - Bar::new(16f64, 0.0625, "Refilling Matemat"), - Bar::new(32f64, 0.03125, "Pre-heating convectiomat"), - Bar::new(64f64, 0.015625, "Resetting chair heights"), - Bar::new(128f64, 0.0078125, "Untangling 'block chain'"), - Bar::new(256f64, 0.00390625, "Refilling sticker box"), - Bar::new(512f64, 0.001953125, "Setting room to public"), - Bar::new(1024f64, 0.000976562, "Welcoming creatures"), - ], - }, + unlocks: UnlockSystem::new(&state), + state, } } @@ -67,34 +81,33 @@ impl Game { text_layer .set_row_str(0, "Discordia Boot Procedure") .unwrap(); - let middle = text_layer.width() / 2; - text_layer - .window_mut(middle, 0, middle, 1) - .unwrap() - .set_row_str(0, &format!(" Hacks: {}", self.state.currency.floor())) - .unwrap(); + self.draw_bars(text_layer, pixel_layer, 1); - for (index, bar) in self.state.bars.iter().enumerate() { - let row = 2 + index; - let mut bar_window = pixel_layer - .window_mut(0, row * TILE_SIZE, pixel_layer.width(), TILE_SIZE) - .unwrap(); - let mut label_window = text_layer - .window_mut(0, row, text_layer.width(), 1) - .unwrap(); - bar.draw(&mut label_window, &mut bar_window); - } + let mut row = text_layer.height() - 1; + self.draw_stats(text_layer, row); + row -= 1; if let Some(next_upgrade) = self.unlocks.peek_next() { - text_layer - .window_mut(0, text_layer.height() - 2, text_layer.width(), 1) - .unwrap() - .set_row_str(0, &format!("Next unlock for {} Hacks", next_upgrade.cost)) - .unwrap(); + 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); + } + } + + fn draw_next_upgrade(&self, text_layer: &mut WindowMut, row: usize, next_upgrade: &Unlock) { text_layer - .window_mut(0, text_layer.height() - 1, text_layer.width() / 2, 1) + .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, 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)) .unwrap(); @@ -103,7 +116,7 @@ impl Game { text_layer .window_mut( text_layer.width() / 2, - text_layer.height() - 1, + row, text_layer.width() / 2, 1, ) @@ -112,4 +125,26 @@ impl Game { .unwrap(); } } + + fn draw_bars(&self, text_layer: &mut WindowMut, pixel_layer: &mut WindowMut, mut row: usize) { + for bar in self.state.bars.iter() { + let mut bar_window = pixel_layer + .window_mut(0, row * TILE_SIZE, pixel_layer.width(), TILE_SIZE) + .unwrap(); + let mut label_window = text_layer + .window_mut(0, row, text_layer.width(), 1) + .unwrap(); + bar.draw(&mut label_window, &mut bar_window); + + row += 1; + } + } + + fn draw_prev_unlock(&self, chars: &mut WindowMut, row: usize, prev: &String) { + chars + .window_mut(0, row, chars.width(), 1) + .unwrap() + .set_row_str(0, &format!("Unlocked: {}", prev)) + .unwrap(); + } } diff --git a/src/main.rs b/src/main.rs index 447ed05..b1129d4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,6 +55,6 @@ fn main() { }) .unwrap(); - sleep(FRAME_PACING); + sleep(FRAME_PACING / 2); } } diff --git a/src/unlocks.rs b/src/unlocks.rs index bbf5497..c4205a6 100644 --- a/src/unlocks.rs +++ b/src/unlocks.rs @@ -1,18 +1,20 @@ use crate::Currency; use crate::game::{Game, State}; use std::collections::VecDeque; -use std::fmt::{Debug, Formatter}; +use std::fmt::{Debug, Formatter, format}; +use std::ops::Mul; #[derive(Debug)] pub(crate) struct UnlockSystem { bought: usize, + last_unlock: Option, unlock_queue: VecDeque, } pub(crate) struct Unlock { - pub(crate) name: &'static str, + pub(crate) name: String, pub(crate) cost: Currency, - pub(crate) apply: fn(&mut State), + pub(crate) apply: Box, } impl Debug for Unlock { @@ -25,124 +27,68 @@ impl Debug for Unlock { } impl UnlockSystem { - pub(crate) fn new() -> Self { + 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; + }), + }, + ) + ) + .collect::>(); + upgrades.sort_by(|l, r| l.cost.total_cmp(&r.cost)); + + let unlock_queue = upgrades.into(); + Self { + unlock_queue, bought: 0, - unlock_queue: vec![ - Unlock { - name: "Start Powering infrastructure", - cost: 0f64, - apply: |game| { - game.bars[0].enabled = true; - }, - }, - Unlock { - name: "More power", - cost: 10f64, - apply: |game| { - game.bars[0].productivity *= 2.0; - }, - }, - Unlock { - name: "", - cost: 23f64, - apply: |game| { - game.productivity *= 1.1; - }, - }, - Unlock { - name: "The answer", - cost: 42f64, - apply: |game| { - game.speed *= 1.1; - }, - }, - Unlock { - name: "Start Dusting ServicePoint", - cost: 64f64, - apply: |game| { - game.bars[1].enabled = true; - }, - }, - Unlock { - name: "Dust filters", - cost: 100f64, - apply: |game| { - game.bars[1].productivity *= 2.0; - }, - }, - Unlock { - name: "HASS automation", - cost: 128f64, - apply: |game| { - game.bars[0].speed *= 1.5; - }, - }, - Unlock { - name: "Start Activating colorful lights", - cost: 256f64, - apply: |game| { - game.bars[2].enabled = true; - }, - }, - Unlock { - name: "Start Dimming darkroom", - cost: 1024f64, - apply: |game| { - game.bars[3].enabled = true; - }, - }, - Unlock { - name: "Start Refilling Matemat", - cost: 4096f64, - apply: |game| { - game.bars[4].enabled = true; - }, - }, - Unlock { - name: "Start Pre-heating convectiomat", - cost: 16384f64, - apply: |game| { - game.bars[5].enabled = true; - }, - }, - Unlock { - name: "Start Resetting chair heights", - cost: 65536f64, - apply: |game| { - game.bars[6].enabled = true; - }, - }, - Unlock { - name: "Start Untangling 'block chain'", - cost: 262144f64, - apply: |game| { - game.bars[7].enabled = true; - }, - }, - Unlock { - name: "Start Refilling sticker box", - cost: 1048576f64, - apply: |game| { - game.bars[8].enabled = true; - }, - }, - Unlock { - name: "Start Setting room to public", - cost: 4194304f64, - apply: |game| { - game.bars[9].enabled = true; - }, - }, - Unlock { - name: "Start Welcoming creatures", - cost: 16777216f64, - apply: |game| { - game.bars[10].enabled = true; - }, - }, - ] - .into(), + last_unlock: None } } @@ -157,6 +103,7 @@ impl UnlockSystem { 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; } } @@ -165,4 +112,8 @@ impl UnlockSystem { pub(crate) fn bought(&self) -> usize { self.bought } + + pub(crate) fn previous(&self) -> Option<&String> { + self.last_unlock.as_ref() + } }