aboutsummaryrefslogtreecommitdiff
path: root/light-client/src
diff options
context:
space:
mode:
Diffstat (limited to 'light-client/src')
-rw-r--r--light-client/src/game.rs155
-rw-r--r--light-client/src/main.rs18
-rw-r--r--light-client/src/network.rs50
-rw-r--r--light-client/src/sprite_renderer.rs48
-rw-r--r--light-client/src/tilemap.rs2
5 files changed, 220 insertions, 53 deletions
diff --git a/light-client/src/game.rs b/light-client/src/game.rs
index cf265344..b35a15d7 100644
--- a/light-client/src/game.rs
+++ b/light-client/src/game.rs
@@ -15,31 +15,47 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-use crate::{sprite_renderer::SpriteRenderer, tilemap::Tilemap};
+use crate::{
+ sprite_renderer::{SpriteRect, SpriteRenderer},
+ tilemap::Tilemap,
+};
use hurrycurry_protocol::{
glam::{IVec2, Vec2},
- PacketC, PlayerID, TileIndex,
+ movement::MovementBase,
+ ClientGamedata, ItemIndex, ItemLocation, PacketC, PacketS, PlayerID, TileIndex,
};
use log::{info, warn};
-use sdl2::rect::FRect;
-use std::collections::HashMap;
+use sdl2::{
+ keyboard::{KeyboardState, Scancode},
+ rect::{FRect, 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,
+
+ item_sprites: Vec<SpriteRect>,
+ movement_send_cooldown: 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,
- name: String,
- boosting: bool,
- rot: f32,
+ kind: ItemIndex,
}
impl Game {
@@ -49,6 +65,10 @@ impl Game {
players: HashMap::new(),
tilemap: Tilemap::default(),
my_id: PlayerID(0),
+ data: ClientGamedata::default(),
+ collision_map: HashSet::new(),
+ movement_send_cooldown: 0.,
+ item_sprites: Vec::new(),
}
}
@@ -57,6 +77,20 @@ impl Game {
PacketC::Init { id } => self.my_id = id,
PacketC::Data { data } => {
self.tilemap.init(&data.tile_names, renderer.metadata());
+ self.item_sprites = data
+ .item_names
+ .iter()
+ .map(|name| {
+ SpriteRect::new(
+ renderer
+ .metadata()
+ .get(&format!("{name}:a"))
+ .copied()
+ .unwrap_or(Rect::new(0, 0, 32, 24)),
+ )
+ })
+ .collect();
+ self.data = data;
}
PacketC::UpdateMap {
tile,
@@ -64,9 +98,15 @@ impl Game {
neighbors,
} => {
if let Some(kind) = kind {
- self.tiles.insert(tile, Tile { kind });
+ self.tiles.insert(tile, Tile { 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);
}
@@ -81,10 +121,16 @@ impl Game {
id,
Player {
character,
- position,
name,
- boosting: false,
- rot: 0.,
+ item: None,
+ movement: MovementBase {
+ position,
+ facing: Vec2::X,
+ rotation: 0.,
+ velocity: Vec2::ZERO,
+ boosting: false,
+ stamina: 0.,
+ },
},
);
}
@@ -98,14 +144,21 @@ impl Game {
rot,
boosting,
} => {
- if let Some(p) = self.players.get_mut(&player) {
- p.position = pos;
- p.rot = rot;
- p.boosting = 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 } => (),
- PacketC::SetItem { location, item } => (),
+ 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,
+ })
+ }
PacketC::SetProgress {
item,
progress,
@@ -132,18 +185,80 @@ impl Game {
}
}
+ 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 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);
+
+ self.movement_send_cooldown -= dt;
+ let send_movement = self.movement_send_cooldown < 0.;
+ if send_movement {
+ self.movement_send_cooldown += 0.04
+ }
+
+ for (pid, player) in &mut self.players {
+ if *pid == self.my_id {
+ let movement_packet =
+ player
+ .movement
+ .update(&self.collision_map, direction, boost, dt);
+
+ if send_movement {
+ packet_out.push_back(movement_packet);
+ }
+ }
+ 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) {
self.tilemap.draw(ctx);
for p in self.players.values() {
let src = ctx.misc_textures().player;
let dst = FRect::new(
- p.position.x - src.width() as f32 / 32. / 2.,
- p.position.y + 1. - src.height() as f32 / 24.,
+ p.movement.position.x - src.width() as f32 / 32. / 2.,
+ p.movement.position.y + 0.3 - src.height() as f32 / 24.,
src.width() as f32 / 32.,
src.height() as f32 / 24.,
);
- ctx.draw(((dst.y + dst.h + 1.) * 24.) as i32, src, dst);
+ ctx.draw(dst.y + dst.h + 1., src, dst);
+ if let Some(item) = &p.item {
+ item.draw(ctx, &self.item_sprites)
+ }
+ }
+ for tile in self.tiles.values() {
+ if let Some(item) = &tile.item {
+ item.draw(ctx, &self.item_sprites)
+ }
}
}
}
+
+impl Item {
+ pub fn draw(&self, ctx: &mut SpriteRenderer, item_sprites: &[SpriteRect]) {
+ eprintln!("item {} at {}", self.kind.0, self.position);
+ item_sprites[self.kind.0].draw_at(ctx, self.position)
+ }
+}
diff --git a/light-client/src/main.rs b/light-client/src/main.rs
index 8be38e4c..b00ba734 100644
--- a/light-client/src/main.rs
+++ b/light-client/src/main.rs
@@ -17,8 +17,13 @@
*/
use game::Game;
use network::Network;
-use sdl2::{event::Event, keyboard::Keycode, pixels::Color};
+use sdl2::{
+ event::Event,
+ keyboard::{KeyboardState, Keycode},
+ pixels::Color,
+};
use sprite_renderer::SpriteRenderer;
+use std::time::{Duration, Instant};
pub mod game;
pub mod network;
@@ -55,6 +60,10 @@ fn main() {
character: 0,
});
+ let mut events = sdl_context.event_pump().unwrap();
+
+ let mut last_tick = Instant::now();
+
'mainloop: loop {
net.poll();
@@ -62,6 +71,11 @@ fn main() {
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);
@@ -69,7 +83,7 @@ fn main() {
renderer.submit(&mut canvas);
canvas.present();
- for event in sdl_context.event_pump().unwrap().poll_iter() {
+ for event in events.poll_iter() {
match event {
Event::Quit { .. }
| Event::KeyDown {
diff --git a/light-client/src/network.rs b/light-client/src/network.rs
index 47eb66ab..ed160773 100644
--- a/light-client/src/network.rs
+++ b/light-client/src/network.rs
@@ -75,37 +75,41 @@ impl Network {
})
}
pub fn poll(&mut self) {
- 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, _)) => {
+ 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 bincode packet: {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:?}");
+ Ok(_) => None,
+ Err(e) => {
+ if let Some(e) = e.into_non_blocking() {
+ warn!("{e:?}");
+ None
+ } else {
+ break;
+ }
}
- None
- }
- });
+ });
+ }
for packet in self.queue_out.drain(..) {
debug!("-> {packet:?}");
diff --git a/light-client/src/sprite_renderer.rs b/light-client/src/sprite_renderer.rs
index 187ccc7b..aadbfbe3 100644
--- a/light-client/src/sprite_renderer.rs
+++ b/light-client/src/sprite_renderer.rs
@@ -46,6 +46,12 @@ pub struct SpriteDraw {
dst: FRect,
}
+pub struct SpriteRect {
+ z_offset: f32,
+ src: Rect,
+ relative_dst: FRect,
+}
+
impl<'a> SpriteRenderer<'a> {
pub fn init(texture_creator: &'a TextureCreator<WindowContext>) -> Self {
let palette = include_str!("../assets/palette.csv")
@@ -114,7 +120,7 @@ impl<'a> SpriteRenderer<'a> {
metadata,
sprites: vec![],
view_offset: Vec2::ZERO,
- view_scale: Vec2::new(32., 24.) * 3.,
+ view_scale: Vec2::splat(3.),
}
}
@@ -127,15 +133,15 @@ impl<'a> SpriteRenderer<'a> {
&self.misc_textures
}
- pub fn draw(&mut self, z_order: i32, src: Rect, dst: FRect) {
+ pub fn draw(&mut self, z_order: f32, src: Rect, dst: FRect) {
self.sprites.push(SpriteDraw {
- z_order,
+ z_order: (z_order * 24.) as i32,
src,
dst: FRect::new(
- (dst.x + self.view_offset.x) * self.view_scale.x,
- (dst.y + self.view_offset.y) * self.view_scale.y,
- dst.w * self.view_scale.x,
- dst.h * self.view_scale.y,
+ ((dst.x + self.view_offset.x) * 32.).round() * self.view_scale.x,
+ ((dst.y + self.view_offset.y) * 24.).round() * self.view_scale.y,
+ (dst.w * 32.).round() * self.view_scale.x,
+ (dst.h * 24.).round() * self.view_scale.y,
),
})
}
@@ -164,3 +170,31 @@ impl PartialEq for SpriteDraw {
self.z_order == other.z_order && self.src == other.src && self.dst == other.dst
}
}
+
+impl SpriteRect {
+ pub fn draw_at(&self, ctx: &mut SpriteRenderer, pos: Vec2) {
+ ctx.draw(
+ self.z_offset + pos.y + 5.,
+ self.src,
+ FRect::new(
+ self.relative_dst.x + pos.x,
+ self.relative_dst.y + pos.y,
+ self.relative_dst.w,
+ self.relative_dst.h,
+ ),
+ )
+ }
+ pub fn new(src: Rect) -> Self {
+ let relative_dst = FRect::new(
+ 0.0 - src.width() as f32 / 32. / 2.,
+ 0.3 - src.height() as f32 / 24.,
+ src.width() as f32 / 32.,
+ src.height() as f32 / 24.,
+ );
+ Self {
+ z_offset: -relative_dst.h,
+ relative_dst,
+ src,
+ }
+ }
+}
diff --git a/light-client/src/tilemap.rs b/light-client/src/tilemap.rs
index 3464248d..e84db8b1 100644
--- a/light-client/src/tilemap.rs
+++ b/light-client/src/tilemap.rs
@@ -88,7 +88,7 @@ impl Tilemap {
pub fn draw(&self, ctx: &mut SpriteRenderer) {
for &(src, dst) in self.tiles.values() {
- ctx.draw(((dst.y + dst.h) * 24.) as i32, src, dst);
+ ctx.draw(dst.y + dst.h, src, dst);
}
}
}