implement level completion, score storing and a dismissable 'level complete' popup
This commit is contained in:
parent
4aa5ed9eec
commit
f9b8dba019
7 changed files with 130 additions and 18 deletions
|
@ -5,11 +5,14 @@ logic mostly like https://git.crispypin.cc/CrispyPin/marble
|
|||
|
||||
## todo
|
||||
cleanup: unpowered texture names x_off -> x
|
||||
input/output display
|
||||
grow grid automatically while editing
|
||||
sim/speed control gui
|
||||
(option) display input as numbers
|
||||
scroll output
|
||||
default i/o text modes specified per level
|
||||
grow grid automatically while editing
|
||||
make marble movement not order-dependent (`>ooo <` does not behave symmetrically)
|
||||
blueprints
|
||||
scroll level list
|
||||
|
||||
decide on marble data size (u32 or byte?)
|
||||
blueprint rotation
|
||||
|
|
104
src/editor.rs
104
src/editor.rs
|
@ -10,7 +10,7 @@ use crate::{
|
|||
Machine,
|
||||
},
|
||||
simple_button,
|
||||
solution::Solution,
|
||||
solution::{Score, Solution},
|
||||
text_input, texture_option_button, Textures,
|
||||
};
|
||||
|
||||
|
@ -38,6 +38,16 @@ pub struct Editor {
|
|||
time_since_step: f32,
|
||||
exit_state: ExitState,
|
||||
exit_menu: bool,
|
||||
complete_popup: Popup,
|
||||
fail_popup: Popup,
|
||||
score: Option<Score>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Popup {
|
||||
Start,
|
||||
Visible,
|
||||
Dismissed,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -90,6 +100,9 @@ impl Editor {
|
|||
level,
|
||||
exit_state: ExitState::Dont,
|
||||
exit_menu: false,
|
||||
complete_popup: Popup::Start,
|
||||
fail_popup: Popup::Start,
|
||||
score: solution.score,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -105,11 +118,29 @@ impl Editor {
|
|||
&self.source_board
|
||||
}
|
||||
|
||||
pub fn score(&self) -> Option<Score> {
|
||||
self.score.clone()
|
||||
}
|
||||
|
||||
fn start_sim(&mut self) {
|
||||
self.machine.reset();
|
||||
self.machine.set_board(self.source_board.clone());
|
||||
}
|
||||
|
||||
fn step(&mut self) {
|
||||
self.machine.step();
|
||||
if self.level.outputs() == self.machine.output() && self.complete_popup == Popup::Start {
|
||||
self.complete_popup = Popup::Visible;
|
||||
self.exit_state = ExitState::Save;
|
||||
self.sim_state = SimState::Stepping;
|
||||
self.score = Some(Score {
|
||||
cycles: self.machine.step_count(),
|
||||
tiles: self.source_board.count_tiles(),
|
||||
area: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn rotate_tool(&mut self, shift: bool) {
|
||||
match &self.active_tool {
|
||||
Tool::Math => {
|
||||
|
@ -155,25 +186,26 @@ impl Editor {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn input(&mut self, rl: &RaylibHandle) {
|
||||
pub fn update(&mut self, rl: &RaylibHandle) {
|
||||
if rl.is_key_pressed(KeyboardKey::KEY_ESCAPE) {
|
||||
self.sim_state = SimState::Editing
|
||||
self.sim_state = SimState::Editing;
|
||||
self.complete_popup = Popup::Start;
|
||||
}
|
||||
if self.sim_state == SimState::Running {
|
||||
self.time_since_step += rl.get_frame_time();
|
||||
if self.time_since_step > 1. / self.sim_speed {
|
||||
self.time_since_step = 0.;
|
||||
self.machine.step();
|
||||
self.step();
|
||||
}
|
||||
}
|
||||
if rl.is_key_pressed(KeyboardKey::KEY_SPACE) {
|
||||
match self.sim_state {
|
||||
SimState::Editing => {
|
||||
self.start_sim();
|
||||
self.machine.step();
|
||||
self.step();
|
||||
}
|
||||
SimState::Running => (),
|
||||
SimState::Stepping => self.machine.step(),
|
||||
SimState::Stepping => self.step(),
|
||||
}
|
||||
self.sim_state = SimState::Stepping;
|
||||
}
|
||||
|
@ -183,7 +215,10 @@ impl Editor {
|
|||
self.start_sim();
|
||||
self.sim_state = SimState::Running;
|
||||
}
|
||||
SimState::Running => self.sim_state = SimState::Editing,
|
||||
SimState::Running => {
|
||||
self.sim_state = SimState::Editing;
|
||||
self.complete_popup = Popup::Start;
|
||||
}
|
||||
SimState::Stepping => self.sim_state = SimState::Running,
|
||||
}
|
||||
}
|
||||
|
@ -241,6 +276,61 @@ impl Editor {
|
|||
self.board_overlay(d, textures);
|
||||
self.draw_bottom_bar(d, textures);
|
||||
self.draw_top_bar(d, textures);
|
||||
|
||||
if self.complete_popup == Popup::Visible {
|
||||
let width = 310;
|
||||
let height = 165;
|
||||
let x = d.get_screen_width() / 2 - width / 2;
|
||||
let y = d.get_screen_height() / 2 - height / 2;
|
||||
d.draw_rectangle(x, y, width, height, Color::DIMGRAY);
|
||||
d.draw_text("Level Complete!", x + 10, y + 10, 30, Color::LIME);
|
||||
if let Some(score) = &self.score {
|
||||
d.draw_text("cycles", x + 15, y + 45, 20, Color::WHITE);
|
||||
d.draw_rectangle(x + 10, y + 70, 90, 30, Color::DARKGREEN);
|
||||
d.draw_text(
|
||||
&format!("{}", score.cycles),
|
||||
x + 15,
|
||||
y + 75,
|
||||
20,
|
||||
Color::WHITE,
|
||||
);
|
||||
|
||||
d.draw_text("tiles", x + 115, y + 45, 20, Color::WHITE);
|
||||
d.draw_rectangle(x + 110, y + 70, 90, 30, Color::DARKGREEN);
|
||||
d.draw_text(
|
||||
&format!("{}", score.tiles),
|
||||
x + 115,
|
||||
y + 75,
|
||||
20,
|
||||
Color::WHITE,
|
||||
);
|
||||
|
||||
d.draw_text("area", x + 215, y + 45, 20, Color::WHITE);
|
||||
d.draw_rectangle(x + 210, y + 70, 90, 30, Color::DARKGREEN);
|
||||
d.draw_text(
|
||||
&format!("{}", score.area),
|
||||
x + 215,
|
||||
y + 75,
|
||||
20,
|
||||
Color::WHITE,
|
||||
);
|
||||
}
|
||||
if simple_button(d, x + 10, y + 110, 140, 45) {
|
||||
self.complete_popup = Popup::Dismissed;
|
||||
}
|
||||
d.draw_text("continue\nediting", x + 15, y + 115, 20, Color::WHITE);
|
||||
|
||||
if simple_button(d, x + width / 2 + 5, y + 110, 140, 45) {
|
||||
self.exit_state = ExitState::ExitAndSave;
|
||||
}
|
||||
d.draw_text(
|
||||
"return to\nlevel list",
|
||||
x + width / 2 + 10,
|
||||
y + 115,
|
||||
20,
|
||||
Color::WHITE,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_top_bar(&mut self, d: &mut RaylibDrawHandle, _textures: &Textures) {
|
||||
|
|
|
@ -62,7 +62,7 @@ impl Game {
|
|||
while !rl.window_should_close() {
|
||||
let mut d = rl.begin_drawing(&thread);
|
||||
if let Some(editor) = &mut self.open_editor {
|
||||
editor.input(&d);
|
||||
editor.update(&d);
|
||||
editor.draw(&mut d, &self.textures);
|
||||
match editor.get_exit_state() {
|
||||
ExitState::Dont => (),
|
||||
|
@ -70,6 +70,7 @@ impl Game {
|
|||
let solution = &mut self.solutions.get_mut(editor.level_id()).unwrap()
|
||||
[self.selected_solution];
|
||||
solution.board = editor.source_board().to_string();
|
||||
solution.score = editor.score();
|
||||
solution.save();
|
||||
self.open_editor = None;
|
||||
}
|
||||
|
@ -77,6 +78,7 @@ impl Game {
|
|||
let solution = &mut self.solutions.get_mut(editor.level_id()).unwrap()
|
||||
[self.selected_solution];
|
||||
solution.board = editor.source_board().to_string();
|
||||
solution.score = editor.score();
|
||||
solution.save();
|
||||
}
|
||||
ExitState::ExitNoSave => self.open_editor = None,
|
||||
|
|
|
@ -58,6 +58,10 @@ impl Machine {
|
|||
&self.input
|
||||
}
|
||||
|
||||
pub fn step_count(&self) -> usize {
|
||||
self.steps
|
||||
}
|
||||
|
||||
pub fn set_input(&mut self, bytes: Vec<u8>) {
|
||||
self.input_index = self.input_index.min(bytes.len());
|
||||
self.input = bytes;
|
||||
|
|
|
@ -91,6 +91,19 @@ impl Board {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn count_tiles(&self) -> usize {
|
||||
let mut sum = 0;
|
||||
for row in &self.rows {
|
||||
for tile in row {
|
||||
match tile {
|
||||
Tile::Blank | Tile::Block => (),
|
||||
_ => sum += 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
pub fn in_bounds(&self, p: Pos) -> bool {
|
||||
p.x >= 0 && p.y >= 0 && p.x < self.width as isize && p.y < self.height as isize
|
||||
}
|
||||
|
|
|
@ -19,9 +19,9 @@ pub struct Solution {
|
|||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Score {
|
||||
pub cycles: u32,
|
||||
pub tiles: u32,
|
||||
pub area: u32,
|
||||
pub cycles: usize,
|
||||
pub tiles: usize,
|
||||
pub area: usize,
|
||||
}
|
||||
|
||||
impl Solution {
|
||||
|
|
Loading…
Reference in a new issue