diff --git a/src/engine.rs b/src/engine.rs index a37da1b..62dffe6 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,17 +1,16 @@ -use std::time::Duration; +use std::{path::Component, time::Duration}; use sdl2::{ - event::Event, keyboard::Keycode, pixels::Color, render::Canvas, video::Window, Sdl, - VideoSubsystem, + event::Event, + image::{self, LoadTexture, Sdl2ImageContext}, + keyboard::Keycode, + pixels::Color, + rect::Rect, + render::{Canvas, Texture, TextureCreator}, + video::{Window, WindowBuildError, WindowContext}, + IntegerOrSdlError, Sdl, VideoSubsystem, }; -pub struct Game { - sdl_context: Sdl, - video_subsystem: VideoSubsystem, - canvas: Canvas, - event_pump: sdl2::EventPump, -} - #[derive(Debug, Clone)] pub struct Error(String); @@ -21,29 +20,143 @@ impl std::fmt::Display for Error { } } -impl From for Error { - fn from(value: T) -> Self { - Self(value.to_string()) +macro_rules! impl_from_T_for_Error { + ($t:ty) => { + impl From<$t> for Error { + fn from(value: $t) -> Self { + Self(value.to_string()) + } + } + }; + ($t:ty, $($ts:ty),+) => { + impl From<$t> for Error { + fn from(value: $t) -> Self { + Self(value.to_string()) + } + } + impl_from_T_for_Error!($($ts),+); + }; +} + +impl_from_T_for_Error!(String, WindowBuildError, IntegerOrSdlError); + +static RANDOM_U64_COUNTER: u64 = 0; + +pub struct Entity(u64); + +pub trait Component {} + +pub struct Sprite<'a> { + texture: Texture<'a>, +} + +pub struct Context<'a> { + id_counter: &'a mut u64, + canvas: &'a mut Canvas, + texture_creator: &'a TextureCreator, + entities: &'a mut Vec, + components: &'a mut Vec<(u64, Box)>, + systems: &'a mut Vec>, +} + +impl<'a> Context<'a> { + pub fn load_sprite<'b, P>(&self, path: P) -> Result, Error> + where + 'a: 'b, + P: AsRef, + { + let texture = self.texture_creator.load_texture(path)?; + Ok(Sprite { texture }) } + + pub fn draw_sprite<'b>(&mut self, sprite: &Sprite<'b>, x: i32, y: i32) -> Result<(), Error> { + self.canvas.copy( + &sprite.texture, + None, + Rect::new( + x * 4, + y * 4, + sprite.texture.query().width * 4, + sprite.texture.query().height * 4, + ), + )?; + Ok(()) + } + + pub fn spawn(&mut self, components: Vec>) { + let id = *self.id_counter; + *self.id_counter += 1; + self.entities.push(Entity(id)); + self.components + .extend(components.into_iter().map(|component| (id, component))); + } + + pub fn add_system(&mut self, system: Box) { + system.on_add(self); + self.systems.push(system) + } + + pub fn query bool>(&mut self, predicate: F) -> Vec { + let mut entities = Vec::::new(); + for entity in &mut *self.entities { + if predicate(entity.0) { + entities.push(entity.0) + } + } + entities + } + + pub fn has_component(&mut self, entity_id: u64) -> bool { + for (id, component) in self.components {} + } +} + +pub trait System { + fn on_add(&self, _ctx: &mut Context) {} + fn on_update(&self, _ctx: &mut Context) {} +} + +pub struct Game { + id_counter: u64, + sdl_context: Sdl, + video_subsystem: VideoSubsystem, + image_context: Sdl2ImageContext, + canvas: Canvas, + texture_creator: TextureCreator, + event_pump: sdl2::EventPump, + entities: Vec, + components: Vec<(u64, Box)>, + systems: Vec>, } impl Game { pub fn new() -> Result { let sdl_context = sdl2::init()?; let video_subsystem = sdl_context.video()?; + let image_context = image::init(image::InitFlag::PNG)?; let window = video_subsystem .window("pvp-game-dilapidation", 1280, 720) .position_centered() .build()?; + let mut canvas = window.into_canvas().build()?; + let texture_creator = canvas.texture_creator(); + canvas.set_draw_color(Color::RGB(60, 180, 180)); canvas.clear(); + canvas.present(); let event_pump = sdl_context.event_pump()?; Ok(Self { + id_counter: 0, sdl_context, video_subsystem, + image_context, canvas, + texture_creator, event_pump, + entities: vec![], + components: vec![], + systems: vec![], }) } @@ -64,4 +177,15 @@ impl Game { self.canvas.present(); std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60)) } + + pub fn context<'a>(&'a mut self) -> Context<'a> { + Context { + id_counter: &mut self.id_counter, + canvas: &mut self.canvas, + texture_creator: &self.texture_creator, + entities: &mut self.entities, + components: &mut self.components, + systems: &mut self.systems, + } + } } diff --git a/src/main.rs b/src/main.rs index 66ace41..fc70348 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,35 @@ -#[allow(dead_code)] +#![allow(dead_code)] + +use engine::{Component, Sprite, System}; mod engine; -fn main() { - let game = engine::Game::new().unwrap(); - game.run(); - println!("Hello, world!"); +struct Player<'a> { + sprite: Option>, +} + +impl<'a> Component for Player<'a> {} + +struct PlayerRenderer; + +impl System for PlayerRenderer { + fn on_add(&self, ctx: &mut engine::Context) { + for entity in ctx.query(|id| ) {} + } + + fn on_update(&self, _ctx: &mut engine::Context) {} +} + +fn main() { + let mut game = engine::Game::new().unwrap(); + + { + let mut context = game.context(); + context.add_system(Box::new(PlayerRenderer)); + context.spawn(vec![Box::new(Player { sprite: None })]); + + // let sprite = context.load_sprite("textures/player.png").unwrap(); + // context.draw_sprite(&sprite, 16, 16).unwrap(); + } + + game.run(); } diff --git a/textures/dirt.png b/textures/dirt.png new file mode 100644 index 0000000..2255474 Binary files /dev/null and b/textures/dirt.png differ diff --git a/textures/grass.png b/textures/grass.png new file mode 100644 index 0000000..e773698 Binary files /dev/null and b/textures/grass.png differ diff --git a/textures/player.png b/textures/player.png new file mode 100644 index 0000000..881d686 Binary files /dev/null and b/textures/player.png differ