diff options
author | metamuffin <metamuffin@disroot.org> | 2024-07-16 23:38:46 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2024-07-16 23:38:46 +0200 |
commit | 775b0148cec4329a6abb19d03220dc1d8a8b68c3 (patch) | |
tree | 9e715df1db4f23a9c3f1e9c07cf7e93e376b512f /light-client/src | |
parent | 3a358c6dd39aa78319549658adf1028cea61f643 (diff) | |
download | hurrycurry-775b0148cec4329a6abb19d03220dc1d8a8b68c3.tar hurrycurry-775b0148cec4329a6abb19d03220dc1d8a8b68c3.tar.bz2 hurrycurry-775b0148cec4329a6abb19d03220dc1d8a8b68c3.tar.zst |
rename pixel client
Diffstat (limited to 'light-client/src')
-rw-r--r-- | light-client/src/game.rs | 344 | ||||
-rw-r--r-- | light-client/src/helper.rs | 11 | ||||
-rw-r--r-- | light-client/src/main.rs | 105 | ||||
-rw-r--r-- | light-client/src/network.rs | 123 | ||||
-rw-r--r-- | light-client/src/render/misc.rs | 17 | ||||
-rw-r--r-- | light-client/src/render/mod.rs | 158 | ||||
-rw-r--r-- | light-client/src/render/sprite.rs | 76 | ||||
-rw-r--r-- | light-client/src/tilemap.rs | 117 |
8 files changed, 0 insertions, 951 deletions
diff --git a/light-client/src/game.rs b/light-client/src/game.rs deleted file mode 100644 index 7d8e466a..00000000 --- a/light-client/src/game.rs +++ /dev/null @@ -1,344 +0,0 @@ -/* - Hurry Curry! - a game about cooking - Copyright 2024 metamuffin - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, version 3 of the License only. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. - -*/ -use crate::{ - helper::Vec2InterpolateExt, - render::{ - misc::MiscTextures, - sprite::{Sprite, SpriteDraw}, - AtlasLayout, SpriteRenderer, - }, - tilemap::Tilemap, -}; -use hurrycurry_protocol::{ - glam::{IVec2, Vec2}, - movement::MovementBase, - ClientGamedata, ItemIndex, ItemLocation, PacketC, PacketS, PlayerID, TileIndex, -}; -use log::{info, warn}; -use sdl2::{ - keyboard::{KeyboardState, Scancode}, - rect::Rect, -}; -use std::collections::{HashMap, HashSet, VecDeque}; - -pub struct Game { - data: ClientGamedata, - tiles: HashMap<IVec2, Tile>, - tilemap: Tilemap, - collision_map: HashSet<IVec2>, - players: HashMap<PlayerID, Player>, - my_id: PlayerID, - - camera_center: Vec2, - misc_textures: MiscTextures, - item_sprites: Vec<Sprite>, - movement_send_cooldown: f32, - interacting: bool, - score: Score, -} - -#[derive(Debug, Default)] -pub struct Score { - points: i64, - demands_failed: usize, - demands_completed: usize, - time_remaining: f32, -} - -pub struct Tile { - _kind: TileIndex, - item: Option<Item>, -} - -pub struct Player { - movement: MovementBase, - item: Option<Item>, - _name: String, - _character: i32, -} - -pub struct Item { - position: Vec2, - kind: ItemIndex, - progress: Option<(f32, bool)>, -} - -impl Game { - pub fn new(layout: &AtlasLayout) -> Self { - Self { - tiles: HashMap::new(), - players: HashMap::new(), - tilemap: Tilemap::default(), - my_id: PlayerID(0), - data: ClientGamedata::default(), - collision_map: HashSet::new(), - movement_send_cooldown: 0., - misc_textures: MiscTextures::init(layout), - item_sprites: Vec::new(), - interacting: false, - score: Score::default(), - camera_center: Vec2::ZERO, - } - } - - pub fn packet_in(&mut self, packet: PacketC, renderer: &mut SpriteRenderer) { - match packet { - PacketC::Init { id } => self.my_id = id, - PacketC::Data { data } => { - self.tilemap.init(&data.tile_names, renderer.atlas_layout()); - self.item_sprites = data - .item_names - .iter() - .map(|name| { - Sprite::new( - renderer - .atlas_layout() - .get(&format!("{name}+a")) - .copied() - .unwrap_or_else(|| { - warn!("no sprite for item {name:?}"); - Rect::new(0, 0, 32, 24) - }), - Vec2::new(0., 0.0), - 0.1, - ) - }) - .collect(); - self.data = data; - } - PacketC::UpdateMap { - tile, - kind, - neighbors, - } => { - if let Some(kind) = kind { - self.tiles.insert( - tile, - Tile { - _kind: kind, - item: None, - }, - ); - if self.data.tile_collide[kind.0] { - self.collision_map.remove(&tile); - } else { - self.collision_map.insert(tile); - } - } else { - self.tiles.remove(&tile); - self.collision_map.remove(&tile); - } - self.tilemap.set(tile, kind, neighbors); - } - PacketC::AddPlayer { - id, - position, - character, - name, - } => { - info!("add player {} {name:?}", id.0); - self.players.insert( - id, - Player { - _character: character, - _name: name, - item: None, - movement: MovementBase { - position, - facing: Vec2::X, - rotation: 0., - velocity: Vec2::ZERO, - boosting: false, - stamina: 0., - }, - }, - ); - } - PacketC::RemovePlayer { id } => { - info!("remove player {}", id.0); - self.players.remove(&id); - } - PacketC::Position { - player, - pos, - rot, - boosting, - } => { - if player != self.my_id { - if let Some(p) = self.players.get_mut(&player) { - p.movement.position = pos; - p.movement.rotation = rot; - p.movement.boosting = boosting; - } - } - } - PacketC::MoveItem { from, to } => *self.get_item(to) = self.get_item(from).take(), - PacketC::SetItem { location, item } => { - *self.get_item(location) = item.map(|kind| Item { - kind, - position: Vec2::ZERO, - progress: None, - }) - } - PacketC::SetProgress { - item, - progress, - warn, - } => { - self.get_item(item).as_mut().unwrap().progress = progress.map(|s| (s, warn)); - } - PacketC::Collide { - player: _, - force: _, - } => (), - PacketC::Communicate { .. } => { - // TODO - } - PacketC::ServerMessage { text: _ } => { - // TODO - } - PacketC::Score { - points, - demands_failed, - demands_completed, - time_remaining, - } => { - self.score.points = points; - self.score.demands_completed = demands_completed; - self.score.demands_failed = demands_failed; - self.score.time_remaining = time_remaining.unwrap_or(-1.); - } - PacketC::SetIngame { state: _, lobby: _ } => { - // TODO - } - PacketC::Error { message } => { - warn!("server error: {message:?}") - } - _ => (), - } - } - - pub fn get_item(&mut self, location: ItemLocation) -> &mut Option<Item> { - match location { - ItemLocation::Tile(pos) => &mut self.tiles.get_mut(&pos).unwrap().item, - ItemLocation::Player(pid) => &mut self.players.get_mut(&pid).unwrap().item, - } - } - - pub fn tick(&mut self, dt: f32, keyboard: &KeyboardState, packet_out: &mut VecDeque<PacketS>) { - let mut direction = IVec2::new( - keyboard.is_scancode_pressed(Scancode::D) as i32 - - keyboard.is_scancode_pressed(Scancode::A) as i32, - keyboard.is_scancode_pressed(Scancode::S) as i32 - - keyboard.is_scancode_pressed(Scancode::W) as i32, - ) - .as_vec2(); - let boost = keyboard.is_scancode_pressed(Scancode::K); - let interact = keyboard.is_scancode_pressed(Scancode::Space) - | keyboard.is_scancode_pressed(Scancode::J); - - if interact { - direction *= 0.; - } - - self.movement_send_cooldown -= dt; - let send_movement = self.movement_send_cooldown < 0.; - if send_movement { - self.movement_send_cooldown += 0.04 - } - - self.score.time_remaining -= dt; - self.score.time_remaining -= self.score.time_remaining.max(0.); - - if interact != self.interacting { - if interact { - packet_out.push_back(PacketS::Interact { - pos: Some(self.players[&self.my_id].movement.get_interact_target()), - }); - } else { - packet_out.push_back(PacketS::Interact { pos: None }); - } - self.interacting = interact; - } - - if let Some(player) = self.players.get_mut(&self.my_id) { - let movement_packet = player - .movement - .update(&self.collision_map, direction, boost, dt); - if send_movement { - packet_out.push_back(movement_packet); - } - - self.camera_center.exp_to(player.movement.position, dt * 5.); - } - - for (_pid, player) in &mut self.players { - if let Some(item) = &mut player.item { - item.position = player.movement.position - } - } - for (pos, tile) in &mut self.tiles { - if let Some(item) = &mut tile.item { - item.position = pos.as_vec2() + 0.5 - } - } - } - - pub fn draw(&self, ctx: &mut SpriteRenderer) { - ctx.set_view(-self.camera_center + (ctx.size / ctx.get_scale() / 2.), 1.); - - self.tilemap.draw(ctx); - - for p in self.players.values() { - ctx.draw_world(self.misc_textures.player.at(p.movement.position)); - if let Some(item) = &p.item { - item.draw(ctx, &self.item_sprites, &self.misc_textures) - } - } - for tile in self.tiles.values() { - if let Some(item) = &tile.item { - item.draw(ctx, &self.item_sprites, &self.misc_textures) - } - } - } -} - -impl Item { - pub fn draw(&self, ctx: &mut SpriteRenderer, item_sprites: &[Sprite], misc: &MiscTextures) { - ctx.draw_world(item_sprites[self.kind.0].at(self.position)); - if let Some((progress, warn)) = self.progress { - let (bg, fg) = if warn { - ([100, 0, 0, 200], [255, 0, 0, 200]) - } else { - ([0, 100, 0, 200], [0, 255, 0, 200]) - }; - ctx.draw_world(SpriteDraw::overlay( - misc.solid, - self.position + Vec2::new(-0.5, -1.3), - Vec2::new(1., 0.2), - Some(bg), - )); - ctx.draw_world(SpriteDraw::overlay( - misc.solid, - self.position + Vec2::new(-0.5, -1.3), - Vec2::new(progress, 0.2), - Some(fg), - )) - } - } -} diff --git a/light-client/src/helper.rs b/light-client/src/helper.rs deleted file mode 100644 index 9654f519..00000000 --- a/light-client/src/helper.rs +++ /dev/null @@ -1,11 +0,0 @@ -use hurrycurry_protocol::glam::Vec2; - -pub trait Vec2InterpolateExt { - fn exp_to(&mut self, target: Vec2, dt: f32); -} -impl Vec2InterpolateExt for Vec2 { - fn exp_to(&mut self, target: Vec2, dt: f32) { - self.x = target.x + (self.x - target.x) * (-dt).exp(); - self.y = target.y + (self.y - target.y) * (-dt).exp(); - } -} diff --git a/light-client/src/main.rs b/light-client/src/main.rs deleted file mode 100644 index e3aaa5cc..00000000 --- a/light-client/src/main.rs +++ /dev/null @@ -1,105 +0,0 @@ -/* - Hurry Curry! - a game about cooking - Copyright 2024 metamuffin - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, version 3 of the License only. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. - -*/ -use game::Game; -use hurrycurry_protocol::glam::Vec2; -use network::Network; -use render::SpriteRenderer; -use sdl2::{ - event::Event, - keyboard::{KeyboardState, Keycode}, - pixels::Color, -}; -use std::time::{Duration, Instant}; - -pub mod game; -pub mod helper; -pub mod network; -pub mod render; -pub mod tilemap; - -fn main() { - env_logger::init_from_env("LOG"); - - let sdl_context = sdl2::init().unwrap(); - - let video_subsystem = sdl_context.video().unwrap(); - let window = video_subsystem - .window("Hurry Curry! Light Client", 1280, 720) - .position_centered() - .resizable() - .build() - .map_err(|e| e.to_string()) - .unwrap(); - - let mut canvas = window - .into_canvas() - .accelerated() - .build() - .map_err(|e| e.to_string()) - .unwrap(); - let texture_creator = canvas.texture_creator(); - - let mut net = Network::connect("ws://127.0.0.1/").unwrap(); - let mut renderer = SpriteRenderer::init(&texture_creator); - let mut game = Game::new(&renderer.atlas_layout()); - - net.queue_out.push_back(hurrycurry_protocol::PacketS::Join { - name: "light".to_string(), - character: 0, - }); - - let mut events = sdl_context.event_pump().unwrap(); - - let mut last_tick = Instant::now(); - - canvas.set_logical_size(320, 240).unwrap(); - - 'mainloop: loop { - net.poll(); - - let (width, height) = canvas.logical_size(); - renderer.size = Vec2::new(width as f32, height as f32); - - for packet in net.queue_in.drain(..) { - game.packet_in(packet, &mut renderer); - } - - let keyboard = KeyboardState::new(&events); - let dt = last_tick.elapsed().min(Duration::from_secs_f32(1. / 30.)); - game.tick(dt.as_secs_f32(), &keyboard, &mut net.queue_out); - last_tick += dt; - - game.draw(&mut renderer); - - canvas.set_draw_color(Color::BLACK); - canvas.clear(); - renderer.submit(&mut canvas); - canvas.present(); - - for event in events.poll_iter() { - match event { - Event::Quit { .. } - | Event::KeyDown { - keycode: Option::Some(Keycode::Escape), - .. - } => break 'mainloop, - _ => {} - } - } - } -} diff --git a/light-client/src/network.rs b/light-client/src/network.rs deleted file mode 100644 index ed160773..00000000 --- a/light-client/src/network.rs +++ /dev/null @@ -1,123 +0,0 @@ -/* - Hurry Curry! - a game about cooking - Copyright 2024 metamuffin - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, version 3 of the License only. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. - -*/ -use anyhow::Result; -use hurrycurry_protocol::{PacketC, PacketS, BINCODE_CONFIG}; -use log::{debug, warn}; -use std::{collections::VecDeque, net::TcpStream}; -use tungstenite::{ - client::{uri_mode, IntoClientRequest}, - client_tls_with_config, - handshake::client::Request, - stream::{MaybeTlsStream, Mode}, - util::NonBlockingError, - Message, WebSocket, -}; - -pub struct Network { - sock: WebSocket<MaybeTlsStream<TcpStream>>, - pub queue_in: VecDeque<PacketC>, - pub queue_out: VecDeque<PacketS>, -} - -impl Network { - pub fn connect(addr: &str) -> Result<Self> { - let (parts, _) = addr.into_client_request().unwrap().into_parts(); - let mut builder = Request::builder() - .uri(parts.uri.clone().clone()) - .method(parts.method.clone()) - .version(parts.version); - *builder.headers_mut().unwrap() = parts.headers.clone(); - let request = builder.body(()).unwrap(); - - let host = request.uri().host().unwrap(); - let host = if host.starts_with('[') { - &host[1..host.len() - 1] - } else { - host - }; - let port = request - .uri() - .port_u16() - .unwrap_or(match uri_mode(request.uri())? { - Mode::Plain => 27032, - Mode::Tls => 443, - }); - let stream = TcpStream::connect((host, port))?; - stream.set_nodelay(true).unwrap(); - - let (mut sock, _) = client_tls_with_config(request, stream, None, None).unwrap(); - - match sock.get_mut() { - MaybeTlsStream::Plain(s) => s.set_nonblocking(true).unwrap(), - MaybeTlsStream::Rustls(s) => s.sock.set_nonblocking(true).unwrap(), - _ => todo!(), - }; - - Ok(Self { - sock, - queue_in: VecDeque::new(), - queue_out: VecDeque::new(), - }) - } - pub fn poll(&mut self) { - loop { - self.queue_in.extend(match self.sock.read() { - Ok(Message::Text(packet)) => match serde_json::from_str(&packet) { - Ok(packet) => { - debug!("<- {packet:?}"); - Some(packet) - } - Err(e) => { - warn!("invalid json packet: {e:?}"); - None - } - }, - Ok(Message::Binary(packet)) => { - match bincode::decode_from_slice(&packet, BINCODE_CONFIG) { - Ok((packet, _)) => { - debug!("<- {packet:?}"); - Some(packet) - } - Err(e) => { - warn!("invalid bincode packet: {e:?}"); - None - } - } - } - Ok(_) => None, - Err(e) => { - if let Some(e) = e.into_non_blocking() { - warn!("{e:?}"); - None - } else { - break; - } - } - }); - } - - for packet in self.queue_out.drain(..) { - debug!("-> {packet:?}"); - self.sock - .write(Message::Text(serde_json::to_string(&packet).unwrap())) - .unwrap(); - } - - self.sock.flush().unwrap(); - } -} diff --git a/light-client/src/render/misc.rs b/light-client/src/render/misc.rs deleted file mode 100644 index 9f866568..00000000 --- a/light-client/src/render/misc.rs +++ /dev/null @@ -1,17 +0,0 @@ -use super::{sprite::Sprite, AtlasLayout}; -use hurrycurry_protocol::glam::Vec2; -use sdl2::rect::Rect; - -pub struct MiscTextures { - pub player: Sprite, - pub solid: Rect, -} - -impl MiscTextures { - pub fn init(layout: &AtlasLayout) -> Self { - MiscTextures { - player: Sprite::new(*layout.get("player+a").unwrap(), Vec2::Y * 0.3, 0.5 + 0.3), - solid: *layout.get("solid+a").unwrap(), - } - } -} diff --git a/light-client/src/render/mod.rs b/light-client/src/render/mod.rs deleted file mode 100644 index a2aea365..00000000 --- a/light-client/src/render/mod.rs +++ /dev/null @@ -1,158 +0,0 @@ -/* - Hurry Curry! - a game about cooking - Copyright 2024 metamuffin - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, version 3 of the License only. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. - -*/ -pub mod misc; -pub mod sprite; - -use hurrycurry_protocol::glam::Vec2; -use sdl2::{ - pixels::PixelFormatEnum, - rect::{FRect, Rect}, - render::{BlendMode, Canvas, Texture, TextureAccess, TextureCreator}, - video::{Window, WindowContext}, -}; -use sprite::SpriteDraw; -use std::collections::HashMap; - -pub struct SpriteRenderer<'a> { - metadata: AtlasLayout, - - pub size: Vec2, - texture: Texture<'a>, - - view_scale: Vec2, - view_offset: Vec2, - - sprites: Vec<SpriteDraw>, -} - -pub type AtlasLayout = HashMap<String, Rect>; - -impl<'a> SpriteRenderer<'a> { - pub fn init(texture_creator: &'a TextureCreator<WindowContext>) -> Self { - let palette = include_str!("../../assets/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::<u8>().unwrap(), - toks.next().unwrap().parse::<u8>().unwrap(), - toks.next().unwrap().parse::<u8>().unwrap(), - toks.next().unwrap().parse::<u8>().unwrap(), - ], - ) - }) - .collect::<HashMap<_, _>>(); - - let mut texels = vec![255; 1024 * 1024 * 4]; - - for (y, line) in include_str!("../../assets/atlas.ta").lines().enumerate() { - if line.is_empty() { - continue; - } - for (x, char) in line.chars().enumerate() { - let color = palette.get(&char).unwrap(); - texels[(y * 1024 + x) * 4 + 0] = color[3]; - texels[(y * 1024 + x) * 4 + 1] = color[2]; - texels[(y * 1024 + x) * 4 + 2] = color[1]; - texels[(y * 1024 + x) * 4 + 3] = color[0]; - } - } - - let mut texture = texture_creator - .create_texture( - Some(PixelFormatEnum::RGBA8888), - TextureAccess::Streaming, - 1024, - 1024, - ) - .unwrap(); - - texture.update(None, &texels, 1024 * 4).unwrap(); - texture.set_blend_mode(BlendMode::Blend); - - let metadata = include_str!("../../assets/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::<HashMap<_, _>>(); - - Self { - texture, - size: Vec2::ONE, - metadata, - sprites: vec![], - view_offset: Vec2::ZERO, - view_scale: Vec2::ZERO, - } - } - - pub fn set_view(&mut self, offset: Vec2, scale: f32) { - self.view_offset = offset; - self.view_scale = Vec2::new(32., 24.) * scale; - } - pub fn get_scale(&self) -> Vec2 { - self.view_scale - } - - #[inline] - pub fn atlas_layout(&self) -> &HashMap<String, Rect> { - &self.metadata - } - - pub fn set_modulation(&mut self, r: u8, g: u8, b: u8, a: u8) { - self.texture.set_alpha_mod(a); - self.texture.set_color_mod(r, g, b); - } - pub fn reset_modulation(&mut self) { - self.set_modulation(255, 255, 255, 255) - } - - pub fn draw_world(&mut self, sprite: SpriteDraw) { - self.sprites.push(SpriteDraw { - tint: sprite.tint, - z_order: sprite.z_order, - src: sprite.src, - dst: FRect::new( - ((sprite.dst.x + self.view_offset.x) * self.view_scale.x).round(), - ((sprite.dst.y + self.view_offset.y) * self.view_scale.y).round(), - (sprite.dst.w * self.view_scale.x).round(), - (sprite.dst.h * self.view_scale.y).round(), - ), - }) - } - - pub fn submit(&mut self, canvas: &mut Canvas<Window>) { - self.sprites.sort(); - for SpriteDraw { src, dst, tint, .. } in self.sprites.drain(..) { - self.texture.set_color_mod(tint[0], tint[1], tint[2]); - self.texture.set_alpha_mod(tint[3]); - canvas.copy_f(&self.texture, src, dst).unwrap(); - } - } -} diff --git a/light-client/src/render/sprite.rs b/light-client/src/render/sprite.rs deleted file mode 100644 index 711f45bf..00000000 --- a/light-client/src/render/sprite.rs +++ /dev/null @@ -1,76 +0,0 @@ -use hurrycurry_protocol::glam::Vec2; -use sdl2::rect::{FRect, Rect}; - -pub struct Sprite { - z_offset: f32, - src: Rect, - relative_dst: FRect, -} - -impl Sprite { - pub fn new(src: Rect, anchor: Vec2, elevation: f32) -> Self { - let relative_dst = FRect::new( - anchor.x - (src.w as f32) / 32. / 2., - anchor.y - (src.h as f32) / 24., - (src.w as f32) / 32., - (src.h as f32) / 24., - ); - Self { - z_offset: elevation, - src, - relative_dst, - } - } - pub fn new_tile(src: Rect) -> Self { - Self::new(src, Vec2::new(0.5, 1.0), 0.5) - } - pub fn at(&self, pos: Vec2) -> SpriteDraw { - SpriteDraw { - z_order: ((self.z_offset + pos.y) * 24.) as i32, - src: self.src, - dst: FRect::new( - self.relative_dst.x + pos.x, - self.relative_dst.y + pos.y, - self.relative_dst.w, - self.relative_dst.h, - ), - tint: [0xff; 4], - } - } -} - -#[derive(Debug, Clone, Copy)] -pub struct SpriteDraw { - pub tint: [u8; 4], - pub z_order: i32, - pub src: Rect, - pub dst: FRect, -} - -impl SpriteDraw { - pub fn overlay(src: Rect, pos: Vec2, size: Vec2, tint: Option<[u8; 4]>) -> Self { - Self { - dst: FRect::new(pos.x, pos.y, size.x, size.y), - src, - tint: tint.unwrap_or([0xff; 4]), - z_order: i32::MAX, - } - } -} - -impl Ord for SpriteDraw { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.z_order.cmp(&other.z_order) - } -} -impl PartialOrd for SpriteDraw { - fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { - Some(self.cmp(&other)) - } -} -impl Eq for SpriteDraw {} -impl PartialEq for SpriteDraw { - fn eq(&self, other: &Self) -> bool { - self.z_order == other.z_order && self.src == other.src && self.dst == other.dst - } -} diff --git a/light-client/src/tilemap.rs b/light-client/src/tilemap.rs deleted file mode 100644 index 768f79ba..00000000 --- a/light-client/src/tilemap.rs +++ /dev/null @@ -1,117 +0,0 @@ -/* - Hurry Curry! - a game about cooking - Copyright 2024 metamuffin - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, version 3 of the License only. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. - -*/ -use hurrycurry_protocol::{glam::IVec2, TileIndex}; -use log::warn; -use sdl2::rect::Rect; -use std::collections::{HashMap, HashSet}; - -use crate::render::{ - sprite::{Sprite, SpriteDraw}, - SpriteRenderer, -}; - -#[derive(Default)] -pub struct Tilemap { - connect_group_by_tile: Vec<Option<usize>>, - connect_members_by_group: Vec<HashSet<Option<TileIndex>>>, - tile_srcs: Vec<[Rect; 16]>, - tiles: HashMap<IVec2, SpriteDraw>, -} - -impl Tilemap { - pub fn init(&mut self, tile_names: &[String], sprite_rects: &HashMap<String, Rect>) { - let tile_index = tile_names - .iter() - .enumerate() - .map(|(t, i)| (i.to_string(), t)) - .collect::<HashMap<_, _>>(); - self.connect_group_by_tile = vec![None; tile_names.len()]; - self.connect_members_by_group = include_str!("../assets/connect.csv") - .lines() - .enumerate() - .map(|(gid, line)| { - line.split(",") - .flat_map(|tile| tile_index.get(tile).copied()) - .map(|ti| { - self.connect_group_by_tile[ti] = Some(gid); - Some(TileIndex(ti)) - }) - .collect::<HashSet<_>>() - }) - .collect::<Vec<_>>(); - - self.tile_srcs = tile_names - .iter() - .map(|name| { - let fallback = sprite_rects - .get(&format!("{name}+a")) - .copied() - .unwrap_or_else(|| { - warn!("no sprite for tile {name:?}"); - Rect::new(0, 0, 0, 0) - }); - - [ - sprite_rects.get(&format!("{name}+")), - sprite_rects.get(&format!("{name}+w")), - sprite_rects.get(&format!("{name}+e")), - sprite_rects.get(&format!("{name}+we")), - sprite_rects.get(&format!("{name}+n")), - sprite_rects.get(&format!("{name}+wn")), - sprite_rects.get(&format!("{name}+en")), - sprite_rects.get(&format!("{name}+wen")), - sprite_rects.get(&format!("{name}+s")), - sprite_rects.get(&format!("{name}+ws")), - sprite_rects.get(&format!("{name}+es")), - sprite_rects.get(&format!("{name}+wes")), - sprite_rects.get(&format!("{name}+ns")), - sprite_rects.get(&format!("{name}+wns")), - sprite_rects.get(&format!("{name}+ens")), - sprite_rects.get(&format!("{name}+wens")), - ] - .map(|e| e.copied().unwrap_or(fallback)) - }) - .collect(); - } - - pub fn set(&mut self, pos: IVec2, tile: Option<TileIndex>, neighbors: [Option<TileIndex>; 4]) { - let Some(tile) = tile else { - self.tiles.remove(&pos); - return; - }; - - let mut idx = 0; - if let Some(gid) = self.connect_group_by_tile[tile.0] { - let cgroup = &self.connect_members_by_group[gid]; - idx |= 0b0100 * (cgroup.contains(&neighbors[0])) as usize; - idx |= 0b0001 * (cgroup.contains(&neighbors[1])) as usize; - idx |= 0b1000 * (cgroup.contains(&neighbors[2])) as usize; - idx |= 0b0010 * (cgroup.contains(&neighbors[3])) as usize; - } - - let src = self.tile_srcs[tile.0][idx]; - self.tiles - .insert(pos, Sprite::new_tile(src).at(pos.as_vec2())); - } - - pub fn draw(&self, ctx: &mut SpriteRenderer) { - for &sprite in self.tiles.values() { - ctx.draw_world(sprite); - } - } -} |