diff options
-rw-r--r-- | light-client/assets/misc.ini | 1 | ||||
-rw-r--r-- | light-client/assets/textures/solid.ta | 1 | ||||
-rw-r--r-- | light-client/src/game.rs | 103 | ||||
-rw-r--r-- | light-client/src/main.rs | 2 | ||||
-rw-r--r-- | light-client/src/render/misc.rs | 17 | ||||
-rw-r--r-- | light-client/src/render/mod.rs | 30 | ||||
-rw-r--r-- | light-client/src/render/sprite.rs | 15 | ||||
-rw-r--r-- | server/protocol/src/movement.rs | 4 |
8 files changed, 127 insertions, 46 deletions
diff --git a/light-client/assets/misc.ini b/light-client/assets/misc.ini index 639f8c30..667517db 100644 --- a/light-client/assets/misc.ini +++ b/light-client/assets/misc.ini @@ -1,5 +1,6 @@ player=player +solid=solid costumer=costumer interact-target-thick=interact_target_thick interact-target-thin=interact_target_thin diff --git a/light-client/assets/textures/solid.ta b/light-client/assets/textures/solid.ta new file mode 100644 index 00000000..78981922 --- /dev/null +++ b/light-client/assets/textures/solid.ta @@ -0,0 +1 @@ +a diff --git a/light-client/src/game.rs b/light-client/src/game.rs index ac5c3571..1425a3f8 100644 --- a/light-client/src/game.rs +++ b/light-client/src/game.rs @@ -16,7 +16,7 @@ */ use crate::{ - render::{sprite::Sprite, MiscTextures, SpriteRenderer}, + render::{misc::MiscTextures, sprite::Sprite, AtlasLayout, SpriteRenderer}, tilemap::Tilemap, }; use hurrycurry_protocol::{ @@ -42,25 +42,38 @@ pub struct Game { 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, + _kind: TileIndex, item: Option<Item>, } + pub struct Player { movement: MovementBase, item: Option<Item>, - name: String, - character: i32, + _name: String, + _character: i32, } + pub struct Item { position: Vec2, kind: ItemIndex, + progress: Option<(f32, bool)>, } impl Game { - pub fn new(renderer: &SpriteRenderer) -> Self { + pub fn new(layout: &AtlasLayout) -> Self { Self { tiles: HashMap::new(), players: HashMap::new(), @@ -69,8 +82,10 @@ impl Game { data: ClientGamedata::default(), collision_map: HashSet::new(), movement_send_cooldown: 0., - misc_textures: MiscTextures::init(renderer), + misc_textures: MiscTextures::init(layout), item_sprites: Vec::new(), + interacting: false, + score: Score::default(), } } @@ -78,18 +93,21 @@ impl Game { match packet { PacketC::Init { id } => self.my_id = id, PacketC::Data { data } => { - self.tilemap.init(&data.tile_names, renderer.metadata()); + self.tilemap.init(&data.tile_names, renderer.atlas_layout()); self.item_sprites = data .item_names .iter() .map(|name| { Sprite::new( renderer - .metadata() - .get(&format!("{name}:a")) + .atlas_layout() + .get(&format!("{name}+a")) .copied() - .unwrap_or(Rect::new(0, 0, 32, 24)), - Vec2::ZERO, + .unwrap_or_else(|| { + warn!("no sprite for item {name:?}"); + Rect::new(0, 0, 32, 24) + }), + Vec2::new(0., 0.0), 0.1, ) }) @@ -102,7 +120,13 @@ impl Game { neighbors, } => { if let Some(kind) = kind { - self.tiles.insert(tile, Tile { kind, item: None }); + self.tiles.insert( + tile, + Tile { + _kind: kind, + item: None, + }, + ); if self.data.tile_collide[kind.0] { self.collision_map.remove(&tile); } else { @@ -124,8 +148,8 @@ impl Game { self.players.insert( id, Player { - character, - name, + _character: character, + _name: name, item: None, movement: MovementBase { position, @@ -161,27 +185,40 @@ impl Game { *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::Collide { player, force } => (), - PacketC::Communicate { - player, - message, - persist, - } => (), - PacketC::ServerMessage { text } => (), + PacketC::Communicate { .. } => { + // TODO + } + PacketC::ServerMessage { text: _ } => { + // TODO + } PacketC::Score { points, demands_failed, demands_completed, time_remaining, - } => (), - PacketC::SetIngame { state, lobby } => (), + } => { + 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:?}") } @@ -197,7 +234,7 @@ impl Game { } pub fn tick(&mut self, dt: f32, keyboard: &KeyboardState, packet_out: &mut VecDeque<PacketS>) { - let direction = IVec2::new( + 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 @@ -208,12 +245,30 @@ impl Game { 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; + } + for (pid, player) in &mut self.players { if *pid == self.my_id { let movement_packet = diff --git a/light-client/src/main.rs b/light-client/src/main.rs index 278229d2..7526b394 100644 --- a/light-client/src/main.rs +++ b/light-client/src/main.rs @@ -53,7 +53,7 @@ fn main() { let mut net = Network::connect("ws://127.0.0.1/").unwrap(); let mut renderer = SpriteRenderer::init(&texture_creator); - let mut game = Game::new(&renderer); + let mut game = Game::new(&renderer.atlas_layout()); net.queue_out.push_back(hurrycurry_protocol::PacketS::Join { name: "light".to_string(), diff --git a/light-client/src/render/misc.rs b/light-client/src/render/misc.rs new file mode 100644 index 00000000..9f866568 --- /dev/null +++ b/light-client/src/render/misc.rs @@ -0,0 +1,17 @@ +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 index fbf3d7a5..a410da6f 100644 --- a/light-client/src/render/mod.rs +++ b/light-client/src/render/mod.rs @@ -15,6 +15,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ +pub mod misc; pub mod sprite; use hurrycurry_protocol::glam::Vec2; @@ -24,11 +25,11 @@ use sdl2::{ render::{BlendMode, Canvas, Texture, TextureAccess, TextureCreator}, video::{Window, WindowContext}, }; -use sprite::{Sprite, SpriteDraw}; +use sprite::SpriteDraw; use std::collections::HashMap; pub struct SpriteRenderer<'a> { - metadata: HashMap<String, Rect>, + metadata: AtlasLayout, texture: Texture<'a>, @@ -38,6 +39,8 @@ pub struct SpriteRenderer<'a> { 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") @@ -108,7 +111,7 @@ impl<'a> SpriteRenderer<'a> { } #[inline] - pub fn metadata(&self) -> &HashMap<String, Rect> { + pub fn atlas_layout(&self) -> &HashMap<String, Rect> { &self.metadata } @@ -122,6 +125,7 @@ impl<'a> SpriteRenderer<'a> { 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( @@ -135,24 +139,10 @@ impl<'a> SpriteRenderer<'a> { pub fn submit(&mut self, canvas: &mut Canvas<Window>) { self.sprites.sort(); - for SpriteDraw { src, dst, .. } in self.sprites.drain(..) { + 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(); } } } - -pub struct MiscTextures { - pub player: Sprite, -} - -impl MiscTextures { - pub fn init(renderer: &SpriteRenderer) -> Self { - MiscTextures { - player: Sprite::new( - *renderer.metadata().get("player+a").unwrap(), - Vec2::Y * 0.3, - 0., - ), - } - } -} diff --git a/light-client/src/render/sprite.rs b/light-client/src/render/sprite.rs index c19f8392..711f45bf 100644 --- a/light-client/src/render/sprite.rs +++ b/light-client/src/render/sprite.rs @@ -22,7 +22,7 @@ impl Sprite { } } pub fn new_tile(src: Rect) -> Self { - Self::new(src, Vec2::new(0.5, 1.0), 0.) + Self::new(src, Vec2::new(0.5, 1.0), 0.5) } pub fn at(&self, pos: Vec2) -> SpriteDraw { SpriteDraw { @@ -34,17 +34,30 @@ impl Sprite { 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) diff --git a/server/protocol/src/movement.rs b/server/protocol/src/movement.rs index 486da816..5a96155f 100644 --- a/server/protocol/src/movement.rs +++ b/server/protocol/src/movement.rs @@ -81,6 +81,10 @@ impl MovementBase { rot: self.rotation, } } + + pub fn get_interact_target(&self) -> IVec2 { + (self.position + Vec2::new(self.rotation.sin(), self.rotation.cos())).as_ivec2() + } } pub fn collide_player(p: &mut MovementBase, map: &HashSet<IVec2>) { |