pvp-game-dilapidation/src/engine.rs

196 lines
5.5 KiB
Rust

use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::time::Duration;
use sdl2::{
event::Event,
image::{self, LoadTexture, Sdl2ImageContext},
keyboard::Keycode,
pixels::Color,
rect::Rect,
render::{Canvas, Texture, TextureCreator},
video::{Window, WindowBuildError, WindowContext},
IntegerOrSdlError, Sdl, VideoSubsystem,
};
#[derive(Debug, Clone)]
pub struct Error(String);
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
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);
pub struct Entity(u64);
pub trait Component {
fn inner_type_id(&self) -> TypeId;
fn as_any(&mut self) -> &mut dyn Any;
}
pub struct Sprite {
path: std::path::PathBuf,
}
pub struct Context<'context, 'game>
where
'game: 'context,
{
id_counter: &'context mut u64,
canvas: &'context mut Canvas<Window>,
texture_creator: &'context TextureCreator<WindowContext>,
entities: &'context mut Vec<Entity>,
components: &'context mut Vec<(u64, Box<dyn Component>)>,
systems: &'context mut Vec<Box<dyn System>>,
textures: &'context mut HashMap<std::path::PathBuf, Texture<'game>>,
}
impl<'context, 'game> Context<'context, 'game> {
pub fn load_sprite<P>(&mut self, path: P) -> Result<Sprite, Error>
where
P: AsRef<std::path::Path>,
{
let path = path.as_ref().to_path_buf();
let texture: Texture<'game> = self.texture_creator.load_texture(&path)?;
self.textures.insert(path.clone(), texture);
Ok(Sprite { path })
}
pub fn draw_sprite(&mut self, sprite: &Sprite, x: i32, y: i32) -> Result<(), Error> {
let texture = &self.textures[&sprite.path];
self.canvas.copy(
texture,
None,
Rect::new(
x * 4,
y * 4,
texture.query().width * 4,
texture.query().height * 4,
),
)?;
Ok(())
}
pub fn spawn(&mut self, components: Vec<Box<dyn Component>>) {
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<dyn System>) {
system.on_add(self);
self.systems.push(system)
}
}
pub trait System {
fn on_add(&self, _ctx: &mut Context) {}
fn on_update(&self, _ctx: &mut Context) {}
}
pub struct Game<'a> {
id_counter: u64,
sdl_context: Sdl,
video_subsystem: VideoSubsystem,
image_context: Sdl2ImageContext,
canvas: Canvas<Window>,
texture_creator: TextureCreator<WindowContext>,
event_pump: sdl2::EventPump,
entities: Vec<Entity>,
components: Vec<(u64, Box<dyn Component>)>,
systems: Vec<Box<dyn System>>,
textures: HashMap<std::path::PathBuf, Texture<'a>>,
}
impl<'game> Game<'game> {
pub fn new() -> Result<Self, Error> {
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![],
textures: HashMap::new(),
})
}
pub fn run<F: Fn(&mut Context) -> ()>(&mut self, _f: F) {
// 'running: loop {
// for event in self.event_pump.poll_iter() {
// match event {
// Event::Quit { .. }
// | Event::KeyDown {
// keycode: Some(Keycode::Escape),
// ..
// } => break 'running,
// _ => {}
// }
// }
// self.canvas.set_draw_color(Color::RGB(60, 180, 180));
// self.canvas.clear();
// f(&mut self.context());
// self.canvas.present();
// }
// self.canvas.present();
// std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 60))
}
pub fn context<'context>(&'context mut self) -> Context<'context, 'game>
where
'game: 'context,
{
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,
textures: &mut self.textures,
}
}
}