diff options
Diffstat (limited to 'client/src/world')
-rw-r--r-- | client/src/world/map.rs | 135 | ||||
-rw-r--r-- | client/src/world/mod.rs | 132 |
2 files changed, 267 insertions, 0 deletions
diff --git a/client/src/world/map.rs b/client/src/world/map.rs new file mode 100644 index 0000000..fb67918 --- /dev/null +++ b/client/src/world/map.rs @@ -0,0 +1,135 @@ +use ::map as mapfile; +use anyhow::Error; +use common::{num::Cast, vec}; +use log::{info, log}; +use ndarray::Array2; +use std::{ + collections::{hash_map, HashMap}, + fs::File, + mem, +}; + +pub use mapfile::format; +pub use mapfile::{ + format::Tile, + reader::{self, Color, LayerTilemapType}, +}; +pub const TILE_NUM: u32 = 16; + +pub struct Layer { + pub color: Color, + pub image: Option<usize>, + pub tiles: Array2<Tile>, + pub kind: LayerTilemapType, +} + +pub struct Map { + pub layers: Vec<Layer>, + pub tilesets: HashMap<Option<usize>, Array2<Color>>, +} + +impl Map { + pub fn empty() -> Self { + Self { + layers: Vec::new(), + tilesets: HashMap::new(), + } + } + + pub fn load(file: File) -> Result<Self, Error> { + info!("loading map"); + let datafile = datafile::Reader::new(file).unwrap(); + let mut map = mapfile::Reader::from_datafile(datafile); + + let mut layers = vec![]; + for group_idx in map.group_indices() { + let group = map.group(group_idx).unwrap(); + + if group.parallax_x != 100 + || group.parallax_y != 100 + || group.offset_x != 0 + || group.offset_y != 0 + || group.clipping.is_some() + { + continue; + } + + for layer_idx in group.layer_indices { + let layer = map.layer(layer_idx).unwrap(); + + let tilemap = if let reader::LayerType::Tilemap(t) = layer.t { + t + } else { + continue; + }; + let normal = if let Some(n) = tilemap.type_.to_normal() { + n + } else { + continue; + }; + let tiles = map.layer_tiles(tilemap.tiles(normal.data)).unwrap(); + layers.push(Layer { + color: normal.color, + image: normal.image, + kind: tilemap.type_, + tiles, + }); + } + } + + let mut tilesets = HashMap::new(); + for layer in &layers { + match tilesets.entry(layer.image) { + hash_map::Entry::Occupied(_) => {} + hash_map::Entry::Vacant(v) => { + let data = match layer.image { + None => Array2::from_elem( + (1, 1), + Color { + alpha: 255, + red: 255, + blue: 255, + green: 0, + }, + ), + Some(image_idx) => { + let image = map.image(image_idx).unwrap(); + let height = image.height.usize(); + let width = image.width.usize(); + match image.data { + Some(d) => { + let data = map.image_data(d).unwrap(); + if data.len() % mem::size_of::<Color>() != 0 { + panic!("image shape invalid"); + } + let data: Vec<Color> = unsafe { vec::transmute(data) }; + Array2::from_shape_vec((height, width), data).unwrap() + } + None => { + continue; + // let image_name = map.image_name(image.name)?; + // // WARN? Unknown external image + // // WARN! Wrong dimensions + // str::from_utf8(&image_name).ok() + // .and_then(sanitize) + // .map(&mut external_tileset_loader) + // .transpose()? + // .unwrap_or(None) + // .unwrap_or_else(|| Array2::from_elem((1, 1), Color::white())) + } + } + } + }; + v.insert(data); + } + } + } + + info!( + "{} layers + {} tilesets loaded", + layers.len(), + tilesets.len() + ); + Ok(Self { tilesets, layers }) + } +} diff --git a/client/src/world/mod.rs b/client/src/world/mod.rs new file mode 100644 index 0000000..ae985a6 --- /dev/null +++ b/client/src/world/mod.rs @@ -0,0 +1,132 @@ +use gamenet::{ + enums::{Emote, Team, Weapon}, + SnapObj, +}; + +use self::map::Map; +use crate::client::{helper::get_map_path, ClientMesgOut}; +use std::{collections::BTreeMap, fs::File}; + +pub mod map; + +pub use gamenet::enums; + +#[derive(Debug)] +pub struct Tee { + pub local: bool, + pub latency: i32, + pub score: i32, + + pub team: Team, + pub weapon: Weapon, + pub armor: i32, + pub ammo: i32, + pub emote: Emote, + pub attack_tick: i32, + + pub tick: i32, + pub angle: i32, + pub x: i32, + pub y: i32, + pub vel_x: i32, + pub vel_y: i32, + pub hook_x: i32, + pub hook_y: i32, + pub hook_dx: i32, + pub hook_dy: i32, + pub hook_player: i32, + pub hook_state: i32, +} + +impl Default for Tee { + fn default() -> Self { + Self { + x: Default::default(), + y: Default::default(), + local: false, + team: Team::Spectators, + latency: Default::default(), + score: Default::default(), + weapon: Weapon::Shotgun, + armor: Default::default(), + ammo: Default::default(), + attack_tick: Default::default(), + emote: Emote::Normal, + tick: Default::default(), + angle: Default::default(), + vel_x: Default::default(), + vel_y: Default::default(), + hook_x: Default::default(), + hook_y: Default::default(), + hook_dx: Default::default(), + hook_dy: Default::default(), + hook_player: Default::default(), + hook_state: Default::default(), + } + } +} + +pub struct World { + pub map: Map, + pub tees: BTreeMap<u16, Tee>, +} + +impl World { + pub fn new() -> Self { + Self { + map: Map::empty(), + tees: BTreeMap::new(), + } + } + + pub fn update(&mut self, m: &ClientMesgOut) { + match m { + ClientMesgOut::MapChange { name, crc } => { + let file = File::open(get_map_path(name.as_str(), *crc)).unwrap(); + self.map = Map::load(file).unwrap(); + } + ClientMesgOut::Snaps(s) => { + self.tees.clear(); + for (id, o) in s { + match o { + SnapObj::ClientInfo(_o) => { + // TODO + } + SnapObj::PlayerInfo(o) => { + let e = self.tees.entry(*id).or_default(); + e.local = o.local == 1; + e.team = o.team; + e.latency = o.latency; + e.score = o.score; + } + SnapObj::Character(c) => { + let e = self.tees.entry(*id).or_default(); + e.ammo = c.ammo_count; + e.weapon = c.weapon; + e.emote = c.emote; + e.attack_tick = c.attack_tick; + + e.x = c.character_core.x; + e.y = c.character_core.y; + e.vel_x = c.character_core.vel_x; + e.vel_y = c.character_core.vel_y; + + e.tick = c.character_core.tick; + e.hook_x = c.character_core.hook_x; + e.hook_y = c.character_core.hook_y; + e.hook_player = c.character_core.hooked_player; + e.hook_dx = c.character_core.hook_dx; + e.hook_dy = c.character_core.hook_dy; + e.hook_state = c.character_core.hook_state; + } + _ => (), + } + } + } + } + } + + pub fn local_tee(&self) -> Option<&Tee> { + self.tees.values().find(|e| e.local) + } +} |