use crate::core::{
algebra::Vector2, color::Color, num_traits::Euclid, reflect::prelude::*,
type_traits::prelude::*, visitor::prelude::*, ImmutableString,
};
use std::fmt::{Debug, Display, Formatter};
use super::*;
use tileset::*;
pub trait TileSetPropertyId {
type Property: TryFrom<TileSetPropertyValue, Error = TilePropertyError> + Default;
fn property_uuid(&self) -> &Uuid;
fn get_from_tile_map(
&self,
tile_map: &TileMap,
position: Vector2<i32>,
) -> Result<Self::Property, TilePropertyError> {
tile_map.tile_property_value(position, *self.property_uuid())
}
fn get_from_tile_set(
&self,
tile_set: &TileSet,
handle: TileDefinitionHandle,
) -> Result<Self::Property, TilePropertyError> {
tile_set.tile_property_value(handle, *self.property_uuid())
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Visit, Reflect)]
pub struct TileSetPropertyI32(pub Uuid);
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Visit, Reflect)]
pub struct TileSetPropertyF32(pub Uuid);
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Visit, Reflect)]
pub struct TileSetPropertyString(pub Uuid);
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Visit, Reflect)]
pub struct TileSetPropertyNine(pub Uuid);
impl TileSetPropertyId for TileSetPropertyI32 {
type Property = i32;
fn property_uuid(&self) -> &Uuid {
&self.0
}
}
impl TileSetPropertyId for TileSetPropertyF32 {
type Property = f32;
fn property_uuid(&self) -> &Uuid {
&self.0
}
}
impl TileSetPropertyId for TileSetPropertyString {
type Property = ImmutableString;
fn property_uuid(&self) -> &Uuid {
&self.0
}
}
impl TileSetPropertyId for TileSetPropertyNine {
type Property = NineI8;
fn property_uuid(&self) -> &Uuid {
&self.0
}
}
#[derive(Clone, Default, Debug, Reflect, Visit)]
pub struct TileSetColliderLayer {
pub uuid: Uuid,
pub name: ImmutableString,
pub color: Color,
}
#[derive(Clone, Default, Debug, Reflect, Visit)]
pub struct TileSetPropertyLayer {
pub uuid: Uuid,
pub name: ImmutableString,
pub prop_type: TileSetPropertyType,
pub named_values: Vec<NamedValue>,
}
#[derive(Clone, Default, Debug, Reflect, Visit)]
pub struct NamedValue {
pub name: String,
pub value: NamableValue,
pub color: Color,
}
#[derive(Copy, Clone, Debug, Reflect, Visit, PartialEq)]
pub enum NamableValue {
I8(i8),
I32(i32),
F32(f32),
}
impl Display for NamableValue {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Self::I8(value) => write!(f, "{value}"),
Self::I32(value) => write!(f, "{value}"),
Self::F32(value) => write!(f, "{value}"),
}
}
}
impl Default for NamableValue {
fn default() -> Self {
Self::I32(0)
}
}
impl From<NamableValue> for TileSetPropertyValueElement {
fn from(value: NamableValue) -> Self {
match value {
NamableValue::I8(v) => Self::I8(v),
NamableValue::I32(v) => Self::I32(v),
NamableValue::F32(v) => Self::F32(v),
}
}
}
impl TryFrom<TileSetPropertyValueElement> for NamableValue {
type Error = ();
fn try_from(value: TileSetPropertyValueElement) -> Result<Self, ()> {
match value {
TileSetPropertyValueElement::I32(v) => Ok(Self::I32(v)),
TileSetPropertyValueElement::F32(v) => Ok(Self::F32(v)),
TileSetPropertyValueElement::I8(v) => Ok(Self::I8(v)),
TileSetPropertyValueElement::String(_) => Err(()),
}
}
}
impl NamableValue {
pub fn matches(&self, other: &TileSetPropertyOptionValue) -> bool {
match (self, other) {
(Self::I32(x), TileSetPropertyOptionValue::I32(Some(y))) => *x == *y,
(Self::F32(x), TileSetPropertyOptionValue::F32(Some(y))) => *x == *y,
_ => false,
}
}
}
impl TileSetPropertyLayer {
pub fn value_to_name(&self, value: NamableValue) -> String {
self.named_values
.iter()
.find(|v| v.value == value)
.map(|v| v.name.clone())
.unwrap_or_else(|| format!("{value}"))
}
pub fn value_to_color(&self, value: NamableValue) -> Option<Color> {
self.named_values
.iter()
.find(|v| v.value == value)
.map(|v| v.color)
}
pub fn find_value_index_from_property(
&self,
value: &TileSetPropertyOptionValue,
) -> Option<usize> {
self.named_values
.iter()
.position(|v| v.value.matches(value))
}
pub fn find_value_index(&self, value: NamableValue) -> Option<usize> {
self.named_values.iter().position(|v| v.value == value)
}
pub fn highlight_color(
&self,
position: Vector2<usize>,
value: &TileSetPropertyValue,
element_value: &TileSetPropertyValueElement,
) -> Option<Color> {
use TileSetPropertyValue as PropValue;
use TileSetPropertyValueElement as Element;
if position != Vector2::new(1, 1) && !matches!(value, PropValue::NineSlice(_)) {
return None;
}
match (value, element_value) {
(&PropValue::I32(v0), &Element::I32(v1)) => {
self.value_to_color(NamableValue::I32(v0)).or({
if v0 == v1 {
Some(ELEMENT_MATCH_HIGHLIGHT_COLOR)
} else {
None
}
})
}
(&PropValue::F32(v0), &Element::F32(v1)) => {
self.value_to_color(NamableValue::F32(v0)).or({
if v0 == v1 {
Some(ELEMENT_MATCH_HIGHLIGHT_COLOR)
} else {
None
}
})
}
(PropValue::String(v0), Element::String(v1)) => {
if v0 == v1 {
Some(ELEMENT_MATCH_HIGHLIGHT_COLOR)
} else {
None
}
}
(PropValue::NineSlice(v0), &Element::I8(v1)) => {
let v = v0.value_at(position);
self.value_to_color(NamableValue::I8(v)).or({
if v == v1 {
Some(ELEMENT_MATCH_HIGHLIGHT_COLOR)
} else {
None
}
})
}
_ => None,
}
}
}
#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Reflect, Visit)]
pub enum TileSetPropertyType {
#[default]
I32,
F32,
String,
NineSlice,
}
impl TileSetPropertyType {
pub fn default_value(&self) -> TileSetPropertyValue {
use TileSetPropertyType as PropType;
use TileSetPropertyValue as PropValue;
match self {
PropType::I32 => PropValue::I32(0),
PropType::F32 => PropValue::F32(0.0),
PropType::String => PropValue::String(ImmutableString::default()),
PropType::NineSlice => PropValue::NineSlice(NineI8::default()),
}
}
pub fn default_option_value(&self) -> TileSetPropertyOptionValue {
use TileSetPropertyOptionValue as PropValue;
use TileSetPropertyType as PropType;
match self {
PropType::I32 => PropValue::I32(None),
PropType::F32 => PropValue::F32(None),
PropType::String => PropValue::String(None),
PropType::NineSlice => PropValue::NineSlice([None; 9]),
}
}
}
#[derive(Clone, Debug, PartialEq, Reflect, Visit)]
pub enum TileSetPropertyValue {
I32(i32),
F32(f32),
String(ImmutableString),
NineSlice(NineI8),
}
#[derive(Default, Clone, Copy, PartialEq, Reflect)]
pub struct NineI8(pub [i8; 9]);
impl Visit for NineI8 {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
self.0.visit(name, visitor)
}
}
impl Debug for NineI8 {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let [v0, v1, v2, v3, v4, v5, v6, v7, v8] = self.0;
write!(f, "NineI8[{v0} {v1} {v2}/{v3} {v4} {v5}/{v6} {v7} {v8}]")
}
}
impl Display for NineI8 {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let [v0, v1, v2, v3, v4, v5, v6, v7, v8] = self.0;
write!(f, "[{v0} {v1} {v2}/{v3} {v4} {v5}/{v6} {v7} {v8}]")
}
}
impl From<[i8; 9]> for NineI8 {
fn from(value: [i8; 9]) -> Self {
NineI8(value)
}
}
impl From<NineI8> for [i8; 9] {
fn from(value: NineI8) -> Self {
value.0
}
}
impl NineI8 {
pub fn value_at(&self, position: Vector2<usize>) -> i8 {
let index = TileSetPropertyValue::nine_position_to_index(position);
self.0[index]
}
pub fn value_at_mut(&mut self, position: Vector2<usize>) -> &mut i8 {
let index = TileSetPropertyValue::nine_position_to_index(position);
&mut self.0[index]
}
pub fn swap(&mut self, a: Vector2<usize>, b: Vector2<usize>) {
let a_index = TileSetPropertyValue::nine_position_to_index(a);
let b_index = TileSetPropertyValue::nine_position_to_index(b);
self.0.swap(a_index, b_index);
}
}
#[derive(Clone, Debug, PartialEq, Visit, Reflect)]
pub enum TileSetPropertyValueElement {
I32(i32),
F32(f32),
String(ImmutableString),
I8(i8),
}
impl Default for TileSetPropertyValue {
fn default() -> Self {
Self::I32(0)
}
}
impl Default for TileSetPropertyValueElement {
fn default() -> Self {
Self::I32(0)
}
}
impl TileSetPropertyValueElement {
pub fn prop_type(&self) -> TileSetPropertyType {
match self {
TileSetPropertyValueElement::I32(_) => TileSetPropertyType::I32,
TileSetPropertyValueElement::F32(_) => TileSetPropertyType::F32,
TileSetPropertyValueElement::String(_) => TileSetPropertyType::String,
TileSetPropertyValueElement::I8(_) => TileSetPropertyType::NineSlice,
}
}
}
impl OrthoTransform for TileSetPropertyValue {
fn x_flipped(self) -> Self {
if let Self::NineSlice(mut v) = self {
fn pos(x: usize, y: usize) -> Vector2<usize> {
Vector2::new(x, y)
}
v.swap(pos(2, 0), pos(0, 0));
v.swap(pos(2, 1), pos(0, 1));
v.swap(pos(2, 2), pos(0, 2));
Self::NineSlice(v)
} else {
self
}
}
fn rotated(self, amount: i8) -> Self {
if let Self::NineSlice(mut v) = self {
let amount = amount.rem_euclid(4);
nine_rotate(&mut v, amount as usize * 2);
Self::NineSlice(v)
} else {
self
}
}
}
const fn nine_index(x: usize, y: usize) -> usize {
y * 3 + x
}
const NINE_ROTATE_LIST: [usize; 8] = [
nine_index(0, 0),
nine_index(1, 0),
nine_index(2, 0),
nine_index(2, 1),
nine_index(2, 2),
nine_index(1, 2),
nine_index(0, 2),
nine_index(0, 1),
];
fn nine_rotate(nine: &mut NineI8, amount: usize) {
let nine = &mut nine.0;
let copy = *nine;
for i in 0..(8 - amount) {
nine[NINE_ROTATE_LIST[i + amount]] = copy[NINE_ROTATE_LIST[i]];
}
for i in 0..amount {
nine[NINE_ROTATE_LIST[i]] = copy[NINE_ROTATE_LIST[8 - amount + i]];
}
}
impl TileSetPropertyValue {
pub fn make_default(&self) -> TileSetPropertyValue {
match self {
TileSetPropertyValue::I32(_) => TileSetPropertyValue::I32(0),
TileSetPropertyValue::F32(_) => TileSetPropertyValue::F32(0.0),
TileSetPropertyValue::String(_) => {
TileSetPropertyValue::String(ImmutableString::default())
}
TileSetPropertyValue::NineSlice(_) => {
TileSetPropertyValue::NineSlice(Default::default())
}
}
}
pub fn prop_type(&self) -> TileSetPropertyType {
match self {
TileSetPropertyValue::I32(_) => TileSetPropertyType::I32,
TileSetPropertyValue::F32(_) => TileSetPropertyType::F32,
TileSetPropertyValue::String(_) => TileSetPropertyType::String,
TileSetPropertyValue::NineSlice(_) => TileSetPropertyType::NineSlice,
}
}
#[inline]
pub fn nine_position_to_index(position: Vector2<usize>) -> usize {
if position.y > 2 || position.x > 2 {
panic!("Illegal nine slice position: {position:?}");
}
position.y * 3 + position.x
}
#[inline]
pub fn index_to_nine_position(index: usize) -> Vector2<usize> {
let (y, x) = index.div_rem_euclid(&3);
Vector2::new(x, y)
}
pub fn set_from(&mut self, value: &TileSetPropertyOptionValue) {
use TileSetPropertyOptionValue as OptValue;
use TileSetPropertyValue as PropValue;
match (self, value) {
(PropValue::I32(x0), OptValue::I32(Some(x1))) => *x0 = *x1,
(PropValue::F32(x0), OptValue::F32(Some(x1))) => *x0 = *x1,
(PropValue::String(x0), OptValue::String(Some(x1))) => *x0 = x1.clone(),
(PropValue::NineSlice(arr0), OptValue::NineSlice(arr1)) => {
for (x0, x1) in arr0.0.iter_mut().zip(arr1.iter()) {
if let Some(v) = x1 {
*x0 = *v;
}
}
}
_ => (),
}
}
}
#[derive(Clone, Debug, PartialEq, Reflect, Visit)]
pub enum TileSetPropertyOptionValue {
I32(Option<i32>),
F32(Option<f32>),
String(Option<ImmutableString>),
NineSlice([Option<i8>; 9]),
}
impl Default for TileSetPropertyOptionValue {
fn default() -> Self {
Self::I32(None)
}
}
impl TryFrom<TileSetPropertyValue> for i32 {
type Error = TilePropertyError;
fn try_from(value: TileSetPropertyValue) -> Result<Self, Self::Error> {
use TilePropertyError::*;
use TileSetPropertyValue::*;
match value {
I32(v) => Ok(v),
F32(_) => Err(WrongType("Expected: i32, Found: f32")),
String(_) => Err(WrongType("Expected: i32, Found: ImmutableString")),
NineSlice(_) => Err(WrongType("Expected: i32, Found: NineI8")),
}
}
}
impl TryFrom<TileSetPropertyValue> for f32 {
type Error = TilePropertyError;
fn try_from(value: TileSetPropertyValue) -> Result<Self, Self::Error> {
use TilePropertyError::*;
use TileSetPropertyValue::*;
match value {
I32(_) => Err(WrongType("Expected: f32, Found: i32")),
F32(v) => Ok(v),
String(_) => Err(WrongType("Expected: f32, Found: ImmutableString")),
NineSlice(_) => Err(WrongType("Expected: f32, Found: NineI8")),
}
}
}
impl TryFrom<TileSetPropertyValue> for ImmutableString {
type Error = TilePropertyError;
fn try_from(value: TileSetPropertyValue) -> Result<Self, Self::Error> {
use TilePropertyError::*;
use TileSetPropertyValue::*;
match value {
I32(_) => Err(WrongType("Expected: ImmutableString, Found: i32")),
F32(_) => Err(WrongType("Expected: ImmutableString, Found: f32")),
String(v) => Ok(v),
NineSlice(_) => Err(WrongType("Expected: ImmutableString, Found: NineI8")),
}
}
}
impl TryFrom<TileSetPropertyValue> for NineI8 {
type Error = TilePropertyError;
fn try_from(value: TileSetPropertyValue) -> Result<Self, Self::Error> {
use TilePropertyError::*;
use TileSetPropertyValue::*;
match value {
I32(_) => Err(WrongType("Expected: NineI8, Found: i32")),
F32(_) => Err(WrongType("Expected: NineI8, Found: f32")),
String(_) => Err(WrongType("Expected: NineI8, Found: ImmutableString")),
NineSlice(v) => Ok(v),
}
}
}
impl From<TileSetPropertyValue> for TileSetPropertyOptionValue {
fn from(value: TileSetPropertyValue) -> Self {
use TileSetPropertyOptionValue as OValue;
use TileSetPropertyValue as Value;
match value {
Value::I32(x) => OValue::I32(Some(x)),
Value::F32(x) => OValue::F32(Some(x)),
Value::String(x) => OValue::String(Some(x)),
Value::NineSlice(arr) => OValue::NineSlice(arr.0.map(Some)),
}
}
}
impl From<TileSetPropertyOptionValue> for TileSetPropertyValue {
fn from(value: TileSetPropertyOptionValue) -> Self {
use TileSetPropertyOptionValue as OValue;
use TileSetPropertyValue as Value;
match value {
OValue::I32(x) => Value::I32(x.unwrap_or_default()),
OValue::F32(x) => Value::F32(x.unwrap_or_default()),
OValue::String(x) => Value::String(x.unwrap_or_default()),
OValue::NineSlice(arr) => Value::NineSlice(NineI8(arr.map(Option::unwrap_or_default))),
}
}
}
impl TileSetPropertyOptionValue {
pub fn intersect(&mut self, value: &TileSetPropertyValue) {
use TileSetPropertyOptionValue as OptValue;
use TileSetPropertyValue as PropValue;
match self {
OptValue::I32(x0) => {
if let Some(x) = x0 {
if *value != PropValue::I32(*x) {
*x0 = None
}
}
}
OptValue::F32(x0) => {
if let Some(x) = x0 {
if *value != PropValue::F32(*x) {
*x0 = None
}
}
}
OptValue::String(x0) => {
if let Some(x) = x0 {
if let PropValue::String(x1) = value {
if *x != *x1 {
*x0 = None
}
} else {
*x0 = None
}
}
}
OptValue::NineSlice(arr0) => {
if let PropValue::NineSlice(arr1) = value {
for (x0, x1) in arr0.iter_mut().zip(arr1.0.iter()) {
if let Some(x) = x0 {
if *x != *x1 {
*x0 = None
}
}
}
} else {
*arr0 = [None; 9];
}
}
}
}
}