use hurrycurry_protocol::{ glam::{IVec2, Vec2}, ClientGamedata, ItemIndex, TileIndex, }; use sdl2::{ pixels::PixelFormatEnum, rect::Rect, render::{Canvas, Texture, TextureAccess, TextureCreator}, video::{Window, WindowContext}, }; use std::collections::HashMap; pub struct SpriteRenderer<'a> { texture: Texture<'a>, tiles: Vec, items: Vec, view_scale: u32, view_offset: Vec2, sprites: Vec, } pub struct DrawItem { z_order: i32, src: Rect, dst: Rect, } impl<'a> SpriteRenderer<'a> { pub fn init(texture_creator: &'a TextureCreator) -> Self { let palette = include_str!("../textures/palette.csv") .split('\n') .filter(|l| !l.is_empty()) .map(|s| { let mut toks = s.split(","); ( toks.next().unwrap().chars().next().unwrap(), [ toks.next().unwrap().parse::().unwrap(), toks.next().unwrap().parse::().unwrap(), toks.next().unwrap().parse::().unwrap(), toks.next().unwrap().parse::().unwrap(), ], ) }) .collect::>(); let mut texels = vec![255; 1024 * 1024 * 4]; for (y, line) in include_str!("../textures/atlas.ta").lines().enumerate() { if line.is_empty() { continue; } for (x, char) in line.chars().enumerate() { let color = palette.get(&char).unwrap(); let base = (y * 1024 + x) * 4; texels[base..base + 4].copy_from_slice(color); } } let mut texture = texture_creator .create_texture( Some(PixelFormatEnum::RGBA8888), TextureAccess::Streaming, 1024, 1024, ) .unwrap(); texture.update(None, &texels, 1024 * 4).unwrap(); Self { texture, items: vec![], tiles: vec![], sprites: vec![], view_offset: Vec2::ZERO, view_scale: 32, } } pub fn set_sprite_map(&mut self, data: ClientGamedata) { let meta = include_str!("../textures/atlas.meta.csv") .lines() .filter(|l| !l.is_empty()) .map(|l| { let mut toks = l.split(","); let x: i32 = toks.next().unwrap().parse().unwrap(); let y: i32 = toks.next().unwrap().parse().unwrap(); let w: u32 = toks.next().unwrap().parse().unwrap(); let h: u32 = toks.next().unwrap().parse().unwrap(); let name = toks.next().unwrap().to_string(); (name, Rect::new(x, y, w, h)) }) .collect::>(); self.items = data .item_names .iter() .map(|i| meta.get(i).copied().unwrap_or(Rect::new(0, 0, 100, 100))) .collect(); self.tiles = data .tile_names .iter() .map(|i| meta.get(i).copied().unwrap_or(Rect::new(0, 0, 100, 100))) .collect(); } pub fn draw_tile(&mut self, TileIndex(i): TileIndex, position: IVec2) { let p = (self.view_offset.as_ivec2() + position) * self.view_scale as i32; self.sprites.push(DrawItem { z_order: position.y, src: self.tiles[i], dst: Rect::from_center((p.x as i32, p.y as i32), self.view_scale, self.view_scale), }); } pub fn draw_item(&mut self, ItemIndex(i): ItemIndex, position: Vec2) { self.sprites.push(DrawItem { z_order: position.y as i32, src: self.tiles[i], dst: Rect::from_center( (position.x as i32, position.y as i32), self.view_scale, self.view_scale, ), }) } pub fn submit(&mut self, canvas: &mut Canvas) { self.sprites.sort(); for DrawItem { src, dst, .. } in self.sprites.drain(..) { canvas.copy(&self.texture, src, dst).unwrap(); } } } impl Ord for DrawItem { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.z_order.cmp(&other.z_order) } } impl PartialOrd for DrawItem { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(&other)) } } impl Eq for DrawItem {} impl PartialEq for DrawItem { fn eq(&self, other: &Self) -> bool { self.z_order == other.z_order && self.src == other.src && self.dst == other.dst } }