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
|
## todo
|
||||||
cleanup: unpowered texture names x_off -> x
|
cleanup: unpowered texture names x_off -> x
|
||||||
input/output display
|
|
||||||
grow grid automatically while editing
|
|
||||||
sim/speed control gui
|
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)
|
make marble movement not order-dependent (`>ooo <` does not behave symmetrically)
|
||||||
blueprints
|
blueprints
|
||||||
|
scroll level list
|
||||||
|
|
||||||
decide on marble data size (u32 or byte?)
|
decide on marble data size (u32 or byte?)
|
||||||
blueprint rotation
|
blueprint rotation
|
||||||
|
|
104
src/editor.rs
104
src/editor.rs
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
Machine,
|
Machine,
|
||||||
},
|
},
|
||||||
simple_button,
|
simple_button,
|
||||||
solution::Solution,
|
solution::{Score, Solution},
|
||||||
text_input, texture_option_button, Textures,
|
text_input, texture_option_button, Textures,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,6 +38,16 @@ pub struct Editor {
|
||||||
time_since_step: f32,
|
time_since_step: f32,
|
||||||
exit_state: ExitState,
|
exit_state: ExitState,
|
||||||
exit_menu: bool,
|
exit_menu: bool,
|
||||||
|
complete_popup: Popup,
|
||||||
|
fail_popup: Popup,
|
||||||
|
score: Option<Score>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
enum Popup {
|
||||||
|
Start,
|
||||||
|
Visible,
|
||||||
|
Dismissed,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -90,6 +100,9 @@ impl Editor {
|
||||||
level,
|
level,
|
||||||
exit_state: ExitState::Dont,
|
exit_state: ExitState::Dont,
|
||||||
exit_menu: false,
|
exit_menu: false,
|
||||||
|
complete_popup: Popup::Start,
|
||||||
|
fail_popup: Popup::Start,
|
||||||
|
score: solution.score,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,11 +118,29 @@ impl Editor {
|
||||||
&self.source_board
|
&self.source_board
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn score(&self) -> Option<Score> {
|
||||||
|
self.score.clone()
|
||||||
|
}
|
||||||
|
|
||||||
fn start_sim(&mut self) {
|
fn start_sim(&mut self) {
|
||||||
self.machine.reset();
|
self.machine.reset();
|
||||||
self.machine.set_board(self.source_board.clone());
|
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) {
|
fn rotate_tool(&mut self, shift: bool) {
|
||||||
match &self.active_tool {
|
match &self.active_tool {
|
||||||
Tool::Math => {
|
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) {
|
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 {
|
if self.sim_state == SimState::Running {
|
||||||
self.time_since_step += rl.get_frame_time();
|
self.time_since_step += rl.get_frame_time();
|
||||||
if self.time_since_step > 1. / self.sim_speed {
|
if self.time_since_step > 1. / self.sim_speed {
|
||||||
self.time_since_step = 0.;
|
self.time_since_step = 0.;
|
||||||
self.machine.step();
|
self.step();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if rl.is_key_pressed(KeyboardKey::KEY_SPACE) {
|
if rl.is_key_pressed(KeyboardKey::KEY_SPACE) {
|
||||||
match self.sim_state {
|
match self.sim_state {
|
||||||
SimState::Editing => {
|
SimState::Editing => {
|
||||||
self.start_sim();
|
self.start_sim();
|
||||||
self.machine.step();
|
self.step();
|
||||||
}
|
}
|
||||||
SimState::Running => (),
|
SimState::Running => (),
|
||||||
SimState::Stepping => self.machine.step(),
|
SimState::Stepping => self.step(),
|
||||||
}
|
}
|
||||||
self.sim_state = SimState::Stepping;
|
self.sim_state = SimState::Stepping;
|
||||||
}
|
}
|
||||||
|
@ -183,7 +215,10 @@ impl Editor {
|
||||||
self.start_sim();
|
self.start_sim();
|
||||||
self.sim_state = SimState::Running;
|
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,
|
SimState::Stepping => self.sim_state = SimState::Running,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -241,6 +276,61 @@ impl Editor {
|
||||||
self.board_overlay(d, textures);
|
self.board_overlay(d, textures);
|
||||||
self.draw_bottom_bar(d, textures);
|
self.draw_bottom_bar(d, textures);
|
||||||
self.draw_top_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) {
|
fn draw_top_bar(&mut self, d: &mut RaylibDrawHandle, _textures: &Textures) {
|
||||||
|
|
|
@ -23,7 +23,7 @@ impl Level {
|
||||||
&self.description
|
&self.description
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_board(&self)->Option<String>{
|
pub fn init_board(&self) -> Option<String> {
|
||||||
self.init_board.clone()
|
self.init_board.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ impl Game {
|
||||||
while !rl.window_should_close() {
|
while !rl.window_should_close() {
|
||||||
let mut d = rl.begin_drawing(&thread);
|
let mut d = rl.begin_drawing(&thread);
|
||||||
if let Some(editor) = &mut self.open_editor {
|
if let Some(editor) = &mut self.open_editor {
|
||||||
editor.input(&d);
|
editor.update(&d);
|
||||||
editor.draw(&mut d, &self.textures);
|
editor.draw(&mut d, &self.textures);
|
||||||
match editor.get_exit_state() {
|
match editor.get_exit_state() {
|
||||||
ExitState::Dont => (),
|
ExitState::Dont => (),
|
||||||
|
@ -70,6 +70,7 @@ impl Game {
|
||||||
let solution = &mut self.solutions.get_mut(editor.level_id()).unwrap()
|
let solution = &mut self.solutions.get_mut(editor.level_id()).unwrap()
|
||||||
[self.selected_solution];
|
[self.selected_solution];
|
||||||
solution.board = editor.source_board().to_string();
|
solution.board = editor.source_board().to_string();
|
||||||
|
solution.score = editor.score();
|
||||||
solution.save();
|
solution.save();
|
||||||
self.open_editor = None;
|
self.open_editor = None;
|
||||||
}
|
}
|
||||||
|
@ -77,6 +78,7 @@ impl Game {
|
||||||
let solution = &mut self.solutions.get_mut(editor.level_id()).unwrap()
|
let solution = &mut self.solutions.get_mut(editor.level_id()).unwrap()
|
||||||
[self.selected_solution];
|
[self.selected_solution];
|
||||||
solution.board = editor.source_board().to_string();
|
solution.board = editor.source_board().to_string();
|
||||||
|
solution.score = editor.score();
|
||||||
solution.save();
|
solution.save();
|
||||||
}
|
}
|
||||||
ExitState::ExitNoSave => self.open_editor = None,
|
ExitState::ExitNoSave => self.open_editor = None,
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub struct Machine {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Machine {
|
impl Machine {
|
||||||
pub fn new_empty(input:Vec<u8>,width: usize) -> Self {
|
pub fn new_empty(input: Vec<u8>, width: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
board: Board::new_empty(width, width),
|
board: Board::new_empty(width, width),
|
||||||
marbles: Vec::new(),
|
marbles: Vec::new(),
|
||||||
|
@ -58,6 +58,10 @@ impl Machine {
|
||||||
&self.input
|
&self.input
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn step_count(&self) -> usize {
|
||||||
|
self.steps
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_input(&mut self, bytes: Vec<u8>) {
|
pub fn set_input(&mut self, bytes: Vec<u8>) {
|
||||||
self.input_index = self.input_index.min(bytes.len());
|
self.input_index = self.input_index.min(bytes.len());
|
||||||
self.input = bytes;
|
self.input = bytes;
|
||||||
|
|
|
@ -63,10 +63,10 @@ impl Board {
|
||||||
Board::new(rows)
|
Board::new(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_string(&self)->String{
|
pub fn to_string(&self) -> String {
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
for row in &self.rows{
|
for row in &self.rows {
|
||||||
for tile in row{
|
for tile in row {
|
||||||
out.push(tile.to_char())
|
out.push(tile.to_char())
|
||||||
}
|
}
|
||||||
out.push('\n');
|
out.push('\n');
|
||||||
|
@ -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 {
|
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
|
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)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Score {
|
pub struct Score {
|
||||||
pub cycles: u32,
|
pub cycles: usize,
|
||||||
pub tiles: u32,
|
pub tiles: usize,
|
||||||
pub area: u32,
|
pub area: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Solution {
|
impl Solution {
|
||||||
|
|
Loading…
Reference in a new issue