initial level selection gui
This commit is contained in:
parent
66c9b10264
commit
ed5084d0fd
11 changed files with 413 additions and 29 deletions
46
Cargo.lock
generated
46
Cargo.lock
generated
|
@ -192,6 +192,12 @@ dependencies = [
|
||||||
"either",
|
"either",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
@ -247,6 +253,8 @@ name = "marble2"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"raylib",
|
"raylib",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -433,12 +441,50 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "scopeguard"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.210"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.210"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.79",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.128"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "shlex"
|
name = "shlex"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
|
|
|
@ -5,3 +5,5 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
raylib = "5.0.2"
|
raylib = "5.0.2"
|
||||||
|
serde = { version = "1.0.210", features = ["derive"] }
|
||||||
|
serde_json = "1.0.128"
|
||||||
|
|
43
README.md
43
README.md
|
@ -3,7 +3,20 @@
|
||||||
|
|
||||||
logic mostly like https://git.crispypin.cc/CrispyPin/marble
|
logic mostly like https://git.crispypin.cc/CrispyPin/marble
|
||||||
|
|
||||||
file hierarchy
|
## todo
|
||||||
|
level selection
|
||||||
|
|
||||||
|
solution saving & loading
|
||||||
|
input/output display
|
||||||
|
grow grid automatically while editing
|
||||||
|
sim/speed control gui
|
||||||
|
make marble movement not order-dependent (`>ooo <` does not behave symmetrically)
|
||||||
|
blueprints
|
||||||
|
|
||||||
|
decide on marble data size (u32 or byte?)
|
||||||
|
blueprint rotation
|
||||||
|
|
||||||
|
## file hierarchy
|
||||||
```
|
```
|
||||||
- assets/
|
- assets/
|
||||||
- storage/
|
- storage/
|
||||||
|
@ -28,27 +41,33 @@ file hierarchy
|
||||||
`00_zeroes.json`
|
`00_zeroes.json`
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": "00_zeroes",
|
"id": "00_zeroes",
|
||||||
"name": "Zeroes",
|
"name": "Zeroes",
|
||||||
"description": "learn how to output data",
|
"description": "learn how to output data",
|
||||||
"init_board": null,
|
"init_board": null,
|
||||||
"inputs": [],
|
"inputs": [],
|
||||||
"outputs": [0, 0, 0, 0, 0, 0, 0, 0]
|
"outputs": [0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
`00_zeroes/solution_0.json`
|
`00_zeroes/solution_0.json`
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"level_id": "00_zeroes", //redundant, useful if sharing solution files?
|
"level_id": "00_zeroes", //redundant, useful if sharing solution files?
|
||||||
"name": "unnamed 1",
|
"solution_id": "solution_0",
|
||||||
"board": "oo\nP*\n|-"
|
"name": "unnamed 1",
|
||||||
|
"board": "oo\nP*\n|-",
|
||||||
|
"score": {
|
||||||
|
"cycles": 8,
|
||||||
|
"tiles": 6,
|
||||||
|
"area": 6,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`blueprints/blueprint_0.json`
|
`blueprints/blueprint_0.json`
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"name": "fast printer",
|
"name": "fast printer",
|
||||||
"board": "oo\nP*\n|-"
|
"board": "oo\nP*\n|-"
|
||||||
}
|
}
|
||||||
```
|
```
|
8
levels/00_zeroes.json
Normal file
8
levels/00_zeroes.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"id": "00_zeroes",
|
||||||
|
"name": "Zeroes",
|
||||||
|
"description": "learn how to output data",
|
||||||
|
"init_board": null,
|
||||||
|
"inputs": [],
|
||||||
|
"outputs": [0, 0, 0, 0, 0, 0, 0, 0]
|
||||||
|
}
|
8
levels/01_cat.json
Normal file
8
levels/01_cat.json
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"id": "01_cat",
|
||||||
|
"name": "Copy Cat",
|
||||||
|
"description": "learn how to meow",
|
||||||
|
"init_board": null,
|
||||||
|
"inputs": [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 103, 33],
|
||||||
|
"outputs": [72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 103, 33]
|
||||||
|
}
|
|
@ -3,17 +3,19 @@ use std::ops::Rem;
|
||||||
use raylib::prelude::*;
|
use raylib::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
level::Level,
|
||||||
marble_engine::{
|
marble_engine::{
|
||||||
board::{Board, Pos},
|
board::{Board, Pos},
|
||||||
tile::{Direction, GateType, MathOp, MirrorType, PTile, Tile, WireType},
|
tile::{Direction, GateType, MathOp, MirrorType, PTile, Tile, WireType},
|
||||||
Machine,
|
Machine,
|
||||||
},
|
},
|
||||||
text_input, texture_button, Textures,
|
text_input, texture_option_button, Textures,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Editor {
|
pub struct Editor {
|
||||||
source_board: Board,
|
source_board: Board,
|
||||||
|
level: Level,
|
||||||
machine: Machine,
|
machine: Machine,
|
||||||
sim_state: SimState,
|
sim_state: SimState,
|
||||||
view_offset: Vector2,
|
view_offset: Vector2,
|
||||||
|
@ -70,6 +72,7 @@ impl Editor {
|
||||||
tool_menu_arrow: Direction::Right,
|
tool_menu_arrow: Direction::Right,
|
||||||
tool_menu_mirror: MirrorType::Forward,
|
tool_menu_mirror: MirrorType::Forward,
|
||||||
tool_menu_wire: WireType::Vertical,
|
tool_menu_wire: WireType::Vertical,
|
||||||
|
level: Level::new_sandbox(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,6 +191,7 @@ impl Editor {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw(&mut self, d: &mut RaylibDrawHandle, textures: &Textures) {
|
pub fn draw(&mut self, d: &mut RaylibDrawHandle, textures: &Textures) {
|
||||||
|
d.clear_background(Color::new(64, 64, 64, 255));
|
||||||
self.draw_board(d, textures);
|
self.draw_board(d, textures);
|
||||||
|
|
||||||
let height = d.get_screen_height();
|
let height = d.get_screen_height();
|
||||||
|
@ -242,7 +246,7 @@ impl Editor {
|
||||||
let border = 4.;
|
let border = 4.;
|
||||||
let gap = 2.;
|
let gap = 2.;
|
||||||
let bound_offset = 32. + gap * 2. + border * 2.;
|
let bound_offset = 32. + gap * 2. + border * 2.;
|
||||||
texture_button(
|
texture_option_button(
|
||||||
d,
|
d,
|
||||||
Vector2 {
|
Vector2 {
|
||||||
x: 320. + col as f32 * bound_offset - if col < 0 { 15. } else { 0. },
|
x: 320. + col as f32 * bound_offset - if col < 0 { 15. } else { 0. },
|
||||||
|
|
50
src/level.rs
Normal file
50
src/level.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
|
use crate::marble_engine::board::Board;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
pub struct Level {
|
||||||
|
id: String,
|
||||||
|
name: String,
|
||||||
|
description: String,
|
||||||
|
init_board: Option<String>,
|
||||||
|
inputs: Vec<u8>,
|
||||||
|
outputs: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Level {
|
||||||
|
pub fn new_sandbox() -> Self {
|
||||||
|
Self {
|
||||||
|
id: "sandbox".into(),
|
||||||
|
name: "Sandbox".into(),
|
||||||
|
description: String::new(),
|
||||||
|
init_board: None,
|
||||||
|
inputs: Vec::new(),
|
||||||
|
outputs: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> &str {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn description(&self) -> &str {
|
||||||
|
&self.description
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init_board(&self) -> Option<Board> {
|
||||||
|
self.init_board.as_deref().map(Board::parse)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn inputs(&self) -> &[u8] {
|
||||||
|
&self.inputs
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn outputs(&self) -> &[u8] {
|
||||||
|
&self.outputs
|
||||||
|
}
|
||||||
|
}
|
181
src/main.rs
181
src/main.rs
|
@ -1,14 +1,31 @@
|
||||||
use std::fs::read_to_string;
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fs::{read_dir, read_to_string},
|
||||||
|
};
|
||||||
|
|
||||||
use editor::Editor;
|
|
||||||
use marble_engine::board::Board;
|
|
||||||
use raylib::prelude::*;
|
use raylib::prelude::*;
|
||||||
|
|
||||||
mod editor;
|
mod editor;
|
||||||
|
mod level;
|
||||||
mod marble_engine;
|
mod marble_engine;
|
||||||
|
mod solution;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
|
use editor::Editor;
|
||||||
|
use level::Level;
|
||||||
|
use marble_engine::board::Board;
|
||||||
|
use solution::Solution;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
|
struct Game {
|
||||||
|
levels: Vec<Level>,
|
||||||
|
solutions: HashMap<String, Vec<Solution>>,
|
||||||
|
open_editor: Option<Editor>,
|
||||||
|
textures: Textures,
|
||||||
|
selected_level: usize,
|
||||||
|
selected_solution: usize,
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let (mut rl, thread) = raylib::init()
|
let (mut rl, thread) = raylib::init()
|
||||||
.resizable()
|
.resizable()
|
||||||
|
@ -20,18 +37,156 @@ fn main() {
|
||||||
rl.set_exit_key(None);
|
rl.set_exit_key(None);
|
||||||
rl.set_trace_log(TraceLogLevel::LOG_WARNING);
|
rl.set_trace_log(TraceLogLevel::LOG_WARNING);
|
||||||
|
|
||||||
let mut textures = Textures::default();
|
let mut game = Game::new(&mut rl, &thread);
|
||||||
textures.load_dir("assets", &mut rl, &thread);
|
game.run(&mut rl, &thread);
|
||||||
textures.load_dir("assets/tiles", &mut rl, &thread);
|
// let board = Board::parse(&read_to_string("boards/adder.mbl").unwrap());
|
||||||
|
// game.load_board(board);
|
||||||
|
}
|
||||||
|
|
||||||
let mut game = Editor::new_sandbox();
|
impl Game {
|
||||||
let board = Board::parse(&read_to_string("boards/adder.mbl").unwrap());
|
fn new(rl: &mut RaylibHandle, thread: &RaylibThread) -> Self {
|
||||||
game.load_board(board);
|
let mut textures = Textures::default();
|
||||||
|
textures.load_dir("assets", rl, &thread);
|
||||||
|
textures.load_dir("assets/tiles", rl, &thread);
|
||||||
|
|
||||||
while !rl.window_should_close() {
|
Self {
|
||||||
game.input(&rl);
|
levels: get_levels(),
|
||||||
let mut d = rl.begin_drawing(&thread);
|
solutions: HashMap::new(),
|
||||||
|
open_editor: None,
|
||||||
|
textures,
|
||||||
|
selected_level: 0,
|
||||||
|
selected_solution: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self, rl: &mut RaylibHandle, thread: &RaylibThread) {
|
||||||
|
while !rl.window_should_close() {
|
||||||
|
let mut d = rl.begin_drawing(&thread);
|
||||||
|
if let Some(editor) = &mut self.open_editor {
|
||||||
|
editor.input(&d);
|
||||||
|
editor.draw(&mut d, &self.textures);
|
||||||
|
} else {
|
||||||
|
self.draw(&mut d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&mut self, d: &mut RaylibDrawHandle) {
|
||||||
d.clear_background(Color::new(64, 64, 64, 255));
|
d.clear_background(Color::new(64, 64, 64, 255));
|
||||||
game.draw(&mut d, &textures);
|
let level_list_width = 320;
|
||||||
|
let screen_height = d.get_screen_height();
|
||||||
|
d.draw_rectangle(0, 0, level_list_width, screen_height, Color::GRAY);
|
||||||
|
// let (a, b, c) = d.gui_scroll_panel(
|
||||||
|
// Rectangle {
|
||||||
|
// x: 10.,
|
||||||
|
// y: 10.,
|
||||||
|
// width: level_list_width as f32 - 20.,
|
||||||
|
// height: screen_height as f32 - 40.,
|
||||||
|
// },
|
||||||
|
// Some(rstr!("text")),
|
||||||
|
// Rectangle{},
|
||||||
|
// scroll,
|
||||||
|
// view,
|
||||||
|
// );
|
||||||
|
|
||||||
|
let clicked = d.is_mouse_button_pressed(MouseButton::MOUSE_BUTTON_LEFT);
|
||||||
|
let mouse_pos = d.get_mouse_position();
|
||||||
|
|
||||||
|
for (i, level) in self.levels.iter().enumerate() {
|
||||||
|
let level_entry_height = 48;
|
||||||
|
let y = 10 + i as i32 * level_entry_height;
|
||||||
|
let bounds = Rectangle {
|
||||||
|
x: 5.,
|
||||||
|
y: y as f32 - 5.,
|
||||||
|
width: level_list_width as f32 - 10.,
|
||||||
|
height: level_entry_height as f32 - 5.,
|
||||||
|
};
|
||||||
|
if clicked && bounds.check_collision_point_rec(mouse_pos) && self.selected_level != i {
|
||||||
|
self.selected_solution = 0;
|
||||||
|
self.selected_level = i;
|
||||||
|
}
|
||||||
|
if self.selected_level == i {
|
||||||
|
d.draw_rectangle_rec(bounds, Color::DARKCYAN);
|
||||||
|
}
|
||||||
|
d.draw_text(level.name(), 10, y, 20, Color::WHITE);
|
||||||
|
let solution_count = self
|
||||||
|
.solutions
|
||||||
|
.get(level.id())
|
||||||
|
.map(Vec::len)
|
||||||
|
.unwrap_or_default();
|
||||||
|
let subtext = format!("solutions: {solution_count}");
|
||||||
|
let subtext_color = if solution_count > 0 {
|
||||||
|
Color::GOLD
|
||||||
|
} else {
|
||||||
|
Color::LIGHTGRAY
|
||||||
|
};
|
||||||
|
d.draw_text(&subtext, 10, y + 20, 10, subtext_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(level) = self.levels.get(self.selected_level) {
|
||||||
|
d.draw_text(level.name(), level_list_width + 10, 10, 30, Color::CYAN);
|
||||||
|
d.draw_text(level.id(), level_list_width + 10, 40, 10, Color::GRAY);
|
||||||
|
|
||||||
|
let mut y = 60;
|
||||||
|
if let Some(solutions) = self.solutions.get_mut(level.id()) {
|
||||||
|
let solution_entry_height = 40;
|
||||||
|
for (solution_index, solution) in solutions.iter().enumerate() {
|
||||||
|
simple_option_button(
|
||||||
|
d,
|
||||||
|
level_list_width + 10,
|
||||||
|
y,
|
||||||
|
200,
|
||||||
|
solution_entry_height,
|
||||||
|
solution_index,
|
||||||
|
&mut self.selected_solution,
|
||||||
|
);
|
||||||
|
let name_color = if solution.score.is_some() {
|
||||||
|
Color::LIME
|
||||||
|
} else {
|
||||||
|
Color::ORANGE
|
||||||
|
};
|
||||||
|
d.draw_text(&solution.name, level_list_width + 15, y + 5, 20, name_color);
|
||||||
|
d.draw_text(
|
||||||
|
&solution.score_text(),
|
||||||
|
level_list_width + 15,
|
||||||
|
y + 25,
|
||||||
|
10,
|
||||||
|
Color::WHITE,
|
||||||
|
);
|
||||||
|
y += solution_entry_height + 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
// d.gui_button(bounds, text)
|
||||||
|
if simple_button(d, level_list_width + 10, y, 200, 30) {
|
||||||
|
let n = solutions.len();
|
||||||
|
solutions.push(Solution::new(level.id().to_owned(), n));
|
||||||
|
}
|
||||||
|
d.draw_text(
|
||||||
|
"new solution",
|
||||||
|
level_list_width + 15,
|
||||||
|
y + 5,
|
||||||
|
20,
|
||||||
|
Color::WHITE,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
self.solutions.insert(level.id().to_owned(), Vec::new());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_levels() -> Vec<Level> {
|
||||||
|
let mut levels = Vec::<Level>::new();
|
||||||
|
for d in read_dir("levels").unwrap().flatten() {
|
||||||
|
let l = read_to_string(d.path())
|
||||||
|
.ok()
|
||||||
|
.as_deref()
|
||||||
|
.map(|s| serde_json::from_str(s).ok())
|
||||||
|
.flatten();
|
||||||
|
if let Some(level) = l {
|
||||||
|
levels.push(level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
levels.sort_by(|a, b| a.id().cmp(b.id()));
|
||||||
|
levels
|
||||||
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub struct Pos {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pos {
|
impl Pos {
|
||||||
pub const fn to_vec(&self) -> Vector2 {
|
pub const fn to_vec(self) -> Vector2 {
|
||||||
Vector2 {
|
Vector2 {
|
||||||
x: self.x as f32,
|
x: self.x as f32,
|
||||||
y: self.y as f32,
|
y: self.y as f32,
|
||||||
|
|
40
src/solution.rs
Normal file
40
src/solution.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Solution {
|
||||||
|
solution_id: String,
|
||||||
|
level_id: String, // redundant?
|
||||||
|
pub name: String,
|
||||||
|
pub board: String,
|
||||||
|
pub score: Option<Score>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
pub struct Score {
|
||||||
|
pub cycles: u32,
|
||||||
|
pub tiles: u32,
|
||||||
|
pub area: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Solution {
|
||||||
|
pub fn new(level_id: String, number: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
solution_id: format!("solution_{number}"),
|
||||||
|
level_id,
|
||||||
|
name: format!("Unnamed {number}"),
|
||||||
|
board: " \n".repeat(20), // todo remove when auto resizing is implemented
|
||||||
|
// score: Some(Score { cycles: 5, tiles: 88, area: 987 }),
|
||||||
|
score: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn score_text(&self) -> String {
|
||||||
|
if let Some(score) = &self.score {
|
||||||
|
format!(
|
||||||
|
"C: {} T: {} A: {}",
|
||||||
|
score.cycles, score.tiles, score.area
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
"unsolved".into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
54
src/util.rs
54
src/util.rs
|
@ -28,6 +28,58 @@ impl Textures {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn simple_button(d: &mut RaylibDrawHandle, x: i32, y: i32, width: i32, height: i32) -> bool {
|
||||||
|
let mouse_pos = d.get_mouse_position();
|
||||||
|
let bounds = Rectangle {
|
||||||
|
x: x as f32,
|
||||||
|
y: y as f32,
|
||||||
|
width: width as f32,
|
||||||
|
height: height as f32,
|
||||||
|
};
|
||||||
|
let mut pressed = false;
|
||||||
|
let color = if bounds.check_collision_point_rec(mouse_pos) {
|
||||||
|
if d.is_mouse_button_pressed(MouseButton::MOUSE_BUTTON_LEFT) {
|
||||||
|
pressed = true;
|
||||||
|
}
|
||||||
|
Color::DARKCYAN
|
||||||
|
} else {
|
||||||
|
Color::GRAY
|
||||||
|
};
|
||||||
|
d.draw_rectangle(x, y, width, height, color);
|
||||||
|
pressed
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn simple_option_button<T>(
|
||||||
|
d: &mut RaylibDrawHandle,
|
||||||
|
x: i32,
|
||||||
|
y: i32,
|
||||||
|
width: i32,
|
||||||
|
height: i32,
|
||||||
|
option: T,
|
||||||
|
current: &mut T,
|
||||||
|
) where
|
||||||
|
T: PartialEq,
|
||||||
|
{
|
||||||
|
let color = if &option == current {
|
||||||
|
Color::DARKCYAN
|
||||||
|
} else {
|
||||||
|
Color::GRAY
|
||||||
|
};
|
||||||
|
let bounds = Rectangle {
|
||||||
|
x: x as f32,
|
||||||
|
y: y as f32,
|
||||||
|
width: width as f32,
|
||||||
|
height: height as f32,
|
||||||
|
};
|
||||||
|
let mouse_pos = d.get_mouse_position();
|
||||||
|
if d.is_mouse_button_pressed(MouseButton::MOUSE_BUTTON_LEFT)
|
||||||
|
&& bounds.check_collision_point_rec(mouse_pos)
|
||||||
|
{
|
||||||
|
*current = option;
|
||||||
|
}
|
||||||
|
d.draw_rectangle_rec(bounds, color);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn text_input(
|
pub fn text_input(
|
||||||
d: &mut RaylibDrawHandle,
|
d: &mut RaylibDrawHandle,
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
|
@ -78,7 +130,7 @@ pub fn text_input(
|
||||||
changed
|
changed
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn texture_button<T>(
|
pub fn texture_option_button<T>(
|
||||||
d: &mut RaylibDrawHandle,
|
d: &mut RaylibDrawHandle,
|
||||||
pos: Vector2,
|
pos: Vector2,
|
||||||
texture: &Texture2D,
|
texture: &Texture2D,
|
||||||
|
|
Loading…
Reference in a new issue