use std::{ collections::HashMap, fs::{read_dir, read_to_string}, ops::Rem, }; use marble_engine::{board::Board, parse, tile::Tile, Machine}; use raylib::prelude::*; mod marble_engine; mod util; use util::*; #[derive(Debug)] struct Game { source_board: Board, machine: Machine, sim_state: SimState, view_offset: Vector2, zoom: i32, output_as_text: bool, input_as_text: bool, active_tool: Tool, input_text_selected: bool, sim_speed: f32, time_since_step: f32, } #[derive(Debug)] enum Tool { None, SetTile(Tile), // Erase, // Select, } #[derive(Debug, Clone, PartialEq)] enum SimState { Editing, Running, Stepping, } fn main() { let (mut rl, thread) = raylib::init() .resizable() .title("good window title") .build(); rl.set_target_fps(60); rl.set_exit_key(None); let mut textures: HashMap = HashMap::new(); for d in read_dir("assets/tiles").unwrap().flatten() { let name = d.file_name(); if d.path().is_file() { let name = name.to_string_lossy(); let texture = rl .load_texture(&thread, &format!("assets/tiles/{name}")) .unwrap(); textures.insert(name.to_string(), texture); } } let mut game = Game::new_sandbox(); let board = parse(&read_to_string("boards/adder.mbl").unwrap()); game.load_board(board); while !rl.window_should_close() { game.input(&rl); let mut d = rl.begin_drawing(&thread); d.clear_background(Color::new(64, 64, 64, 255)); game.gui(&mut d, &textures); d.draw_fps(2, 2); } } impl Game { fn new_sandbox() -> Self { Self { source_board: Board::new_empty(1, 1), machine: Machine::new_empty(1), sim_state: SimState::Editing, view_offset: Vector2::zero(), zoom: 1, active_tool: Tool::None, output_as_text: false, input_as_text: false, input_text_selected: false, sim_speed: 8., time_since_step: 0., } } fn load_board(&mut self, board: Board) { self.source_board = board; } fn start_sim(&mut self) { self.machine.reset(); self.machine.set_board(self.source_board.clone()); } fn input(&mut self, rl: &RaylibHandle) { 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(); } } if rl.is_key_pressed(KeyboardKey::KEY_SPACE) { match self.sim_state { SimState::Editing => { self.start_sim(); self.machine.step(); } SimState::Running => (), SimState::Stepping => self.machine.step(), } self.sim_state = SimState::Stepping; } if rl.is_key_pressed(KeyboardKey::KEY_ESCAPE) { self.sim_state = SimState::Editing; } if rl.is_key_pressed(KeyboardKey::KEY_ENTER) { match self.sim_state { SimState::Editing => { self.start_sim(); self.sim_state = SimState::Running; } SimState::Running => self.sim_state = SimState::Editing, SimState::Stepping => self.sim_state = SimState::Running, } } if rl.get_mouse_wheel_move() > 0. && self.zoom < 3 { self.zoom += 1; } if rl.get_mouse_wheel_move() < 0. && self.zoom > 0 { self.zoom -= 1; } if rl.is_mouse_button_down(MouseButton::MOUSE_BUTTON_MIDDLE) { self.view_offset += rl.get_mouse_delta() } if rl.is_mouse_button_pressed(MouseButton::MOUSE_BUTTON_RIGHT) { self.view_offset = Vector2::zero(); } } fn draw_board(&self, d: &mut RaylibDrawHandle, textures: &HashMap) { if self.sim_state == SimState::Editing { self.source_board .draw(d, textures, self.view_offset, self.zoom); } else { self.machine .board() .draw(d, textures, self.view_offset, self.zoom); self.machine .draw_marble_values(d, self.view_offset, self.zoom); } } fn gui(&mut self, d: &mut RaylibDrawHandle, textures: &HashMap) { self.draw_board(d, textures); let height = d.get_screen_height(); let footer_height = 100; let footer_top = (height - footer_height) as f32; d.draw_rectangle( 0, height - footer_height, d.get_screen_width(), footer_height, Color::new(32, 32, 32, 255), ); let tile_size = (16 << self.zoom) as f32; let grid_spill_x = (self.view_offset.x).rem(tile_size) - tile_size; let grid_spill_y = (self.view_offset.y).rem(tile_size) - tile_size; d.gui_grid( Rectangle::new( grid_spill_x, grid_spill_y, d.get_screen_width() as f32 * 2., height as f32 - grid_spill_y - footer_height as f32, ), None, tile_size, 1, ); d.gui_check_box( Rectangle::new(5., footer_top + 5., 25., 25.), Some(rstr!("output as text")), &mut self.output_as_text, ); let out_text = if self.output_as_text { String::from_utf8_lossy(self.machine.output()).to_string() } else { format!("{:?}", self.machine.output()) }; d.draw_text(&out_text, 5, footer_top as i32 + 35, 20, Color::WHITE); let mut input_text = String::from_utf8_lossy(self.machine.input()).to_string(); if text_input( d, Rectangle::new(350., footer_top + 60., 200., 25.), &mut input_text, &mut self.input_text_selected, ) { self.machine.set_input(input_text.into_bytes()); } } }