Skip to content
Snippets Groups Projects
Verified Commit dfb52c4f authored by Louis's avatar Louis :fire:
Browse files

Add new types for map data

parent c9df9af3
No related branches found
No related tags found
No related merge requests found
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::ops::{Deref, DerefMut};
use std::path::Path;
use bevy::math::{IVec2, UVec2};
use bevy::utils::HashMap;
use ldtk_rust::{LayerInstance, Level, TileInstance};
use bevy::math::{IVec2, Rect, UVec2, Vec2};
use ldtk_rust::{EntityInstance, FieldInstance, LayerInstance, Level, TileInstance};
use num_traits::AsPrimitive;
use quadtree_rs::area::{Area, AreaBuilder};
use quadtree_rs::point::Point;
use quadtree_rs::Quadtree;
use serde_json::Value;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Number, Value};
use crate::utils::{Indexer, SerdeClone};
use crate::{get_ldtk_tile_scale, px_to_grid, MapQuery};
......@@ -21,7 +23,7 @@ pub struct TileRef<'a> {
}
#[repr(C)]
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Default, Debug)]
#[derive(Serialize, Deserialize, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Default, Debug)]
pub enum TileFlip {
#[default]
None = 0,
......@@ -74,7 +76,7 @@ impl<'a> Debug for TileRef<'a> {
}
}
#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Copy, Hash, Debug)]
#[derive(Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq, Clone, Copy, Hash, Debug)]
pub struct SpatialIndex(i64, i64);
impl<A, B> From<(A, B)> for SpatialIndex
where
......@@ -316,3 +318,404 @@ impl AsRef<LayerInstance> for LdtkLayer {
&self.layer
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct WorldTile {
gid: usize,
rotation: TileFlip,
}
impl From<&TileInstance> for WorldTile {
fn from(value: &TileInstance) -> Self {
Self {
gid: value.t.max(0) as usize,
rotation: match value.f {
1 => TileFlip::Horizontal,
2 => TileFlip::Vertical,
3 => TileFlip::Both,
_ => TileFlip::None,
},
}
}
}
#[derive(Serialize, Deserialize, Clone, Copy, Debug)]
pub enum EntityPosition {
Point { position: IVec2 },
Zone { position: IVec2, size: IVec2 },
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct WorldEntity {
entity_type: String,
position: Rect,
properties: Properties,
tags: Vec<String>,
colour: String,
}
impl From<&EntityInstance> for WorldEntity {
fn from(value: &EntityInstance) -> Self {
let x = value.px[0];
let y = value.px[1];
Self {
entity_type: value.identifier.clone(),
position: Rect::from_corners(
Vec2::new(x as f32, y as f32),
Vec2::new((x + value.width) as f32, (y + value.height) as f32),
),
properties: Properties::from(&value.field_instances),
tags: value.tags.clone(),
colour: value.smart_color.clone(),
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct Properties(HashMap<String, Value>);
impl Deref for Properties {
type Target = HashMap<String, Value>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Properties {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl From<HashMap<String, Value>> for Properties {
fn from(value: HashMap<String, Value>) -> Self {
Self(value)
}
}
impl From<Vec<FieldInstance>> for Properties {
fn from(value: Vec<FieldInstance>) -> Self {
(&value).into()
}
}
impl From<&Vec<FieldInstance>> for Properties {
fn from(value: &Vec<FieldInstance>) -> Self {
value
.iter()
.map(|field| {
(
field.identifier.clone(),
field.value.clone().unwrap_or(Value::Null),
)
})
.collect::<HashMap<String, Value>>()
.into()
}
}
fn convert_map_types(map: &Map<String, Value>) -> HashMap<String, Value> {
map.iter()
.map(|(name, value)| (name.clone(), value.clone()))
.collect()
}
impl Properties {
pub fn as_string(&self, name: impl ToString) -> Option<String> {
self.get(&name.to_string()).and_then(|value| match value {
Value::String(value) => Some(format!("{}", value)),
Value::Bool(value) => Some(format!("{}", value)),
Value::Number(value) => Some(format!("{}", value)),
_ => None,
})
}
pub fn as_number_cast<T>(&self, name: impl ToString) -> Option<T>
where
T: 'static + Copy,
f64: AsPrimitive<T>,
{
self.get(&name.to_string()).and_then(|value| match value {
Value::Number(number) => {
let num = number.as_f64();
num.map(|val| val.as_())
}
_ => None,
})
}
pub fn as_bool(&self, name: impl ToString) -> Option<bool> {
self.get(&name.to_string()).and_then(|value| match value {
Value::Bool(value) => Some(*value),
_ => None,
})
}
pub fn as_vec(&self, name: impl ToString) -> Option<&Vec<Value>> {
self.get(&name.to_string()).and_then(|value| match value {
Value::Array(value) => Some(value),
_ => None,
})
}
pub fn as_vec_owned(&self, name: impl ToString) -> Option<Vec<Value>> {
self.get(&name.to_string()).and_then(|value| match value {
Value::Array(value) => Some(value.clone()),
_ => None,
})
}
pub fn as_map(&self, name: impl ToString) -> Option<&Map<String, Value>> {
self.get(&name.to_string()).and_then(|value| match value {
Value::Object(value) => Some(value),
_ => None,
})
}
pub fn as_map_owned(&self, name: impl ToString) -> Option<Map<String, Value>> {
self.get(&name.to_string()).and_then(|value| match value {
Value::Object(value) => Some(value.clone()),
_ => None,
})
}
pub fn matches(&self, name: impl ToString, value: Value) -> bool {
match (self.0.get(&name.to_string()), value) {
(Some(Value::String(a)), Value::String(ref b)) => a == b,
(Some(Value::Number(a)), Value::Number(ref b)) => a == b,
(Some(Value::Bool(a)), Value::Bool(ref b)) => a == b,
(Some(Value::Array(a)), Value::Array(ref b)) => a == b,
(Some(Value::Object(a)), Value::Object(ref b)) => a == b,
(Some(Value::Null), Value::Null) => true,
_ => false,
}
}
pub fn is_null(&self, name: impl ToString) -> bool {
match self.0.get(&name.to_string()) {
Some(Value::Null) => true,
_ => false,
}
}
pub fn is_null_or_undefined(&self, name: impl ToString) -> bool {
match self.0.get(&name.to_string()) {
None | Some(Value::Null) => true,
_ => false,
}
}
pub fn is_null_or_falsy(&self, name: impl ToString) -> bool {
match self.0.get(&name.to_string()) {
None | Some(Value::Null) => true,
Some(Value::Bool(value)) => !*value,
Some(Value::String(value)) => value.is_empty(),
Some(Value::Number(num)) => num == &Number::from(0),
Some(Value::Array(value)) => value.is_empty(),
Some(Value::Object(value)) => value.is_empty(),
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub enum LayerType {
Entities(Vec<WorldEntity>),
GidTiles(HashMap<SpatialIndex, WorldTile>),
IntTiles(Vec<usize>),
}
fn infer_tileset_name(layer: &LayerInstance) -> Option<String> {
layer.tileset_rel_path.as_ref().and_then(|path| {
Path::new(path)
.file_stem()
.and_then(|stem| stem.to_str())
.map(String::from)
})
}
impl From<&LayerInstance> for LayerType {
fn from(value: &LayerInstance) -> Self {
match value.layer_instance_type.as_str() {
"IntGrid" => LayerType::IntTiles(
value
.int_grid_csv
.iter()
.map(|num| (*num).max(0) as usize)
.collect(),
),
"Entities" => LayerType::Entities(
value
.entity_instances
.iter()
.map(|entity| entity.into())
.collect(),
),
"Tiles" => LayerType::GidTiles(
value
.grid_tiles
.iter()
.map(|tile| (SpatialIndex(tile.px[0], tile.px[1]), tile.into()))
.collect(),
),
"AutoLayer" => LayerType::GidTiles(
value
.auto_layer_tiles
.iter()
.map(|tile| (SpatialIndex(tile.px[0], tile.px[1]), tile.into()))
.collect(),
),
_ => {
panic!("Invalid layer type {}", value.layer_instance_type.as_str());
}
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct WorldLayer {
id: String,
depth: usize,
world_width: i64,
world_height: i64,
tile_width: i64,
tile_height: i64,
layer_data: LayerType,
tileset_name: Option<String>,
}
impl WorldLayer {
pub fn from_layer(depth: usize, layer: &LayerInstance) -> Self {
let tile_size = get_ldtk_tile_scale() as i64;
Self {
depth,
id: layer.identifier.clone(),
tileset_name: infer_tileset_name(layer),
world_width: layer.c_wid * tile_size,
world_height: layer.c_hei * tile_size,
tile_width: layer.c_wid,
tile_height: layer.c_hei,
layer_data: layer.into(),
}
}
pub fn has_tiles(&self) -> bool {
match &self.layer_data {
LayerType::GidTiles(map) => !map.is_empty(),
_ => false,
}
}
pub fn for_each_tile(&self, mut cb: impl FnMut(i64, i64, &WorldTile)) {
match &self.layer_data {
LayerType::GidTiles(map) => {
map.iter().for_each(|(pos, tile)| {
cb(pos.0, pos.1, tile);
});
}
_ => {}
}
}
pub fn for_each_tile_mut(&mut self, mut cb: impl FnMut(i64, i64, &mut WorldTile)) {
match &mut self.layer_data {
LayerType::GidTiles(map) => {
map.iter_mut().for_each(|(pos, tile)| {
cb(pos.0, pos.1, tile);
});
}
_ => {}
}
}
pub fn get_z_delta(&self) -> f32 {
(self.depth as f32) / 100.0
}
pub fn get_tile(
&self,
x: impl AsPrimitive<i32>,
y: impl AsPrimitive<i32>,
) -> Option<&WorldTile> {
match &self.layer_data {
LayerType::GidTiles(map) => map.get(&IVec2::from((x.as_(), y.as_())).into()),
_ => None,
}
}
pub fn get_tile_mut(
&mut self,
x: impl AsPrimitive<i32>,
y: impl AsPrimitive<i32>,
) -> Option<&mut WorldTile> {
match &mut self.layer_data {
LayerType::GidTiles(map) => map.get_mut(&IVec2::from((x.as_(), y.as_())).into()),
_ => None,
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
pub struct WorldData {
layers: Vec<WorldLayer>,
properties: Properties,
meta: Properties,
}
impl From<&Level> for WorldData {
fn from(value: &Level) -> Self {
let mut properties: Properties = (&value.field_instances).into();
let meta: Properties = properties
.as_map("meta")
.map(convert_map_types)
.unwrap_or_default()
.into();
properties.remove("meta");
Self {
properties,
meta,
layers: value
.layer_instances
.as_ref()
.map(|insts| {
insts
.iter()
.enumerate()
.map(|(idx, layer)| WorldLayer::from_layer(idx, layer))
.collect()
})
.unwrap_or_default(),
}
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct LayerSet(pub Vec<WorldLayer>);
trait AsWorldLayer {
fn as_world_layer(&self, depth: usize) -> WorldLayer;
}
// impl<T, V> From<V> for LayerSet
// where
// T: AsWorldLayer,
// V: Iterator<Item = T>,
// {
// fn from(value: V) -> Self {
// Self(value.enumerate().map(T::as_world_layer).collect())
// }
// }
// impl<T> From<T> for WorldData
// where
// T: Into<LayerSet>,
// {
// fn from(value: T) -> Self {
// let layers = value.into();
// WorldData {
// layers: layers.0,
// ..Default::default()
// }
// }
// }
......@@ -36,7 +36,7 @@ pub fn entity_centre(level_height: i64, entity: &EntityInstance) -> (f32, f32) {
#[derive(Component)]
pub struct WorldLinked;
#[derive(Default, Resource, Clone)]
#[derive(Default, Resource, Clone, Debug)]
pub struct ActiveLevel {
pub map: String,
pub dirty: bool,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment