1use crate::core::{
38 algebra::Vector2, color::Color, num_traits::Euclid, reflect::prelude::*,
39 type_traits::prelude::*, visitor::prelude::*, ImmutableString,
40};
41use std::fmt::{Debug, Display, Formatter};
42
43use super::*;
44use tileset::*;
45
46pub trait TileSetPropertyId {
48 type Property: TryFrom<TileSetPropertyValue, Error = TilePropertyError> + Default;
50 fn property_uuid(&self) -> &Uuid;
52 fn get_from_tile_map(
54 &self,
55 tile_map: &TileMap,
56 position: Vector2<i32>,
57 ) -> Result<Self::Property, TilePropertyError> {
58 tile_map.tile_property_value(position, *self.property_uuid())
59 }
60 fn get_from_tile_set(
62 &self,
63 tile_set: &TileSet,
64 handle: TileDefinitionHandle,
65 ) -> Result<Self::Property, TilePropertyError> {
66 tile_set.tile_property_value(handle, *self.property_uuid())
67 }
68}
69
70#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Visit, Reflect)]
72pub struct TileSetPropertyI32(pub Uuid);
73#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Visit, Reflect)]
75pub struct TileSetPropertyF32(pub Uuid);
76#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Visit, Reflect)]
78pub struct TileSetPropertyString(pub Uuid);
79#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Visit, Reflect)]
81pub struct TileSetPropertyNine(pub Uuid);
82
83impl TileSetPropertyId for TileSetPropertyI32 {
84 type Property = i32;
85 fn property_uuid(&self) -> &Uuid {
86 &self.0
87 }
88}
89
90impl TileSetPropertyId for TileSetPropertyF32 {
91 type Property = f32;
92 fn property_uuid(&self) -> &Uuid {
93 &self.0
94 }
95}
96
97impl TileSetPropertyId for TileSetPropertyString {
98 type Property = ImmutableString;
99 fn property_uuid(&self) -> &Uuid {
100 &self.0
101 }
102}
103
104impl TileSetPropertyId for TileSetPropertyNine {
105 type Property = NineI8;
106 fn property_uuid(&self) -> &Uuid {
107 &self.0
108 }
109}
110
111#[derive(Clone, Default, Debug, Reflect, Visit)]
115pub struct TileSetColliderLayer {
116 pub uuid: Uuid,
118 pub name: ImmutableString,
120 pub color: Color,
122}
123
124#[derive(Clone, Default, Debug, Reflect, Visit)]
128pub struct TileSetPropertyLayer {
129 pub uuid: Uuid,
131 pub name: ImmutableString,
133 pub prop_type: TileSetPropertyType,
135 pub named_values: Vec<NamedValue>,
137}
138
139#[derive(Clone, Default, Debug, Reflect, Visit)]
143pub struct NamedValue {
144 pub name: String,
146 pub value: NamableValue,
148 pub color: Color,
150}
151
152#[derive(Copy, Clone, Debug, Reflect, Visit, PartialEq)]
155pub enum NamableValue {
156 I8(i8),
158 I32(i32),
160 F32(f32),
162}
163
164impl Display for NamableValue {
165 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
166 match self {
167 Self::I8(value) => write!(f, "{value}"),
168 Self::I32(value) => write!(f, "{value}"),
169 Self::F32(value) => write!(f, "{value}"),
170 }
171 }
172}
173
174impl Default for NamableValue {
175 fn default() -> Self {
176 Self::I32(0)
177 }
178}
179
180impl From<NamableValue> for TileSetPropertyValueElement {
181 fn from(value: NamableValue) -> Self {
182 match value {
183 NamableValue::I8(v) => Self::I8(v),
184 NamableValue::I32(v) => Self::I32(v),
185 NamableValue::F32(v) => Self::F32(v),
186 }
187 }
188}
189
190impl TryFrom<TileSetPropertyValueElement> for NamableValue {
191 type Error = ();
192
193 fn try_from(value: TileSetPropertyValueElement) -> Result<Self, ()> {
194 match value {
195 TileSetPropertyValueElement::I32(v) => Ok(Self::I32(v)),
196 TileSetPropertyValueElement::F32(v) => Ok(Self::F32(v)),
197 TileSetPropertyValueElement::I8(v) => Ok(Self::I8(v)),
198 TileSetPropertyValueElement::String(_) => Err(()),
199 }
200 }
201}
202
203impl NamableValue {
204 pub fn matches(&self, other: &TileSetPropertyOptionValue) -> bool {
206 match (self, other) {
207 (Self::I32(x), TileSetPropertyOptionValue::I32(Some(y))) => *x == *y,
208 (Self::F32(x), TileSetPropertyOptionValue::F32(Some(y))) => *x == *y,
209 _ => false,
210 }
211 }
212}
213
214impl TileSetPropertyLayer {
215 pub fn value_to_name(&self, value: NamableValue) -> String {
217 self.named_values
218 .iter()
219 .find(|v| v.value == value)
220 .map(|v| v.name.clone())
221 .unwrap_or_else(|| format!("{value}"))
222 }
223 pub fn value_to_color(&self, value: NamableValue) -> Option<Color> {
225 self.named_values
226 .iter()
227 .find(|v| v.value == value)
228 .map(|v| v.color)
229 }
230 pub fn find_value_index_from_property(
232 &self,
233 value: &TileSetPropertyOptionValue,
234 ) -> Option<usize> {
235 self.named_values
236 .iter()
237 .position(|v| v.value.matches(value))
238 }
239 pub fn find_value_index(&self, value: NamableValue) -> Option<usize> {
241 self.named_values.iter().position(|v| v.value == value)
242 }
243 pub fn highlight_color(
249 &self,
250 position: Vector2<usize>,
251 value: &TileSetPropertyValue,
252 element_value: &TileSetPropertyValueElement,
253 ) -> Option<Color> {
254 use TileSetPropertyValue as PropValue;
255 use TileSetPropertyValueElement as Element;
256 if position != Vector2::new(1, 1) && !matches!(value, PropValue::NineSlice(_)) {
257 return None;
258 }
259 match (value, element_value) {
260 (&PropValue::I32(v0), &Element::I32(v1)) => {
261 self.value_to_color(NamableValue::I32(v0)).or({
262 if v0 == v1 {
263 Some(ELEMENT_MATCH_HIGHLIGHT_COLOR)
264 } else {
265 None
266 }
267 })
268 }
269 (&PropValue::F32(v0), &Element::F32(v1)) => {
270 self.value_to_color(NamableValue::F32(v0)).or({
271 if v0 == v1 {
272 Some(ELEMENT_MATCH_HIGHLIGHT_COLOR)
273 } else {
274 None
275 }
276 })
277 }
278 (PropValue::String(v0), Element::String(v1)) => {
279 if v0 == v1 {
280 Some(ELEMENT_MATCH_HIGHLIGHT_COLOR)
281 } else {
282 None
283 }
284 }
285 (PropValue::NineSlice(v0), &Element::I8(v1)) => {
286 let v = v0.value_at(position);
287 self.value_to_color(NamableValue::I8(v)).or({
288 if v == v1 {
289 Some(ELEMENT_MATCH_HIGHLIGHT_COLOR)
290 } else {
291 None
292 }
293 })
294 }
295 _ => None,
296 }
297 }
298}
299
300#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Reflect, Visit)]
304pub enum TileSetPropertyType {
305 #[default]
307 I32,
308 F32,
310 String,
312 NineSlice,
315}
316
317impl TileSetPropertyType {
318 pub fn default_value(&self) -> TileSetPropertyValue {
320 use TileSetPropertyType as PropType;
321 use TileSetPropertyValue as PropValue;
322 match self {
323 PropType::I32 => PropValue::I32(0),
324 PropType::F32 => PropValue::F32(0.0),
325 PropType::String => PropValue::String(ImmutableString::default()),
326 PropType::NineSlice => PropValue::NineSlice(NineI8::default()),
327 }
328 }
329 pub fn default_option_value(&self) -> TileSetPropertyOptionValue {
331 use TileSetPropertyOptionValue as PropValue;
332 use TileSetPropertyType as PropType;
333 match self {
334 PropType::I32 => PropValue::I32(None),
335 PropType::F32 => PropValue::F32(None),
336 PropType::String => PropValue::String(None),
337 PropType::NineSlice => PropValue::NineSlice([None; 9]),
338 }
339 }
340}
341
342#[derive(Clone, Debug, PartialEq, Reflect, Visit)]
344pub enum TileSetPropertyValue {
345 I32(i32),
347 F32(f32),
349 String(ImmutableString),
351 NineSlice(NineI8),
354}
355
356#[derive(Default, Clone, Copy, PartialEq, Reflect)]
362pub struct NineI8(pub [i8; 9]);
363
364impl Visit for NineI8 {
365 fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
366 self.0.visit(name, visitor)
367 }
368}
369
370impl Debug for NineI8 {
371 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
372 let [v0, v1, v2, v3, v4, v5, v6, v7, v8] = self.0;
373 write!(f, "NineI8[{v0} {v1} {v2}/{v3} {v4} {v5}/{v6} {v7} {v8}]")
374 }
375}
376
377impl Display for NineI8 {
378 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
379 let [v0, v1, v2, v3, v4, v5, v6, v7, v8] = self.0;
380 write!(f, "[{v0} {v1} {v2}/{v3} {v4} {v5}/{v6} {v7} {v8}]")
381 }
382}
383
384impl From<[i8; 9]> for NineI8 {
385 fn from(value: [i8; 9]) -> Self {
386 NineI8(value)
387 }
388}
389
390impl From<NineI8> for [i8; 9] {
391 fn from(value: NineI8) -> Self {
392 value.0
393 }
394}
395
396impl NineI8 {
397 pub fn value_at(&self, position: Vector2<usize>) -> i8 {
404 let index = TileSetPropertyValue::nine_position_to_index(position);
405 self.0[index]
406 }
407 pub fn value_at_mut(&mut self, position: Vector2<usize>) -> &mut i8 {
414 let index = TileSetPropertyValue::nine_position_to_index(position);
415 &mut self.0[index]
416 }
417 pub fn swap(&mut self, a: Vector2<usize>, b: Vector2<usize>) {
419 let a_index = TileSetPropertyValue::nine_position_to_index(a);
420 let b_index = TileSetPropertyValue::nine_position_to_index(b);
421 self.0.swap(a_index, b_index);
422 }
423}
424
425#[derive(Clone, Debug, PartialEq, Visit, Reflect)]
429pub enum TileSetPropertyValueElement {
430 I32(i32),
432 F32(f32),
434 String(ImmutableString),
436 I8(i8),
438}
439
440impl Default for TileSetPropertyValue {
441 fn default() -> Self {
442 Self::I32(0)
443 }
444}
445
446impl Default for TileSetPropertyValueElement {
447 fn default() -> Self {
448 Self::I32(0)
449 }
450}
451
452impl TileSetPropertyValueElement {
453 pub fn prop_type(&self) -> TileSetPropertyType {
455 match self {
456 TileSetPropertyValueElement::I32(_) => TileSetPropertyType::I32,
457 TileSetPropertyValueElement::F32(_) => TileSetPropertyType::F32,
458 TileSetPropertyValueElement::String(_) => TileSetPropertyType::String,
459 TileSetPropertyValueElement::I8(_) => TileSetPropertyType::NineSlice,
460 }
461 }
462}
463
464impl OrthoTransform for TileSetPropertyValue {
465 fn x_flipped(self) -> Self {
466 if let Self::NineSlice(mut v) = self {
467 fn pos(x: usize, y: usize) -> Vector2<usize> {
468 Vector2::new(x, y)
469 }
470 v.swap(pos(2, 0), pos(0, 0));
471 v.swap(pos(2, 1), pos(0, 1));
472 v.swap(pos(2, 2), pos(0, 2));
473 Self::NineSlice(v)
474 } else {
475 self
476 }
477 }
478
479 fn rotated(self, amount: i8) -> Self {
480 if let Self::NineSlice(mut v) = self {
481 let amount = amount.rem_euclid(4);
482 nine_rotate(&mut v, amount as usize * 2);
483 Self::NineSlice(v)
484 } else {
485 self
486 }
487 }
488}
489
490const fn nine_index(x: usize, y: usize) -> usize {
491 y * 3 + x
492}
493
494const NINE_ROTATE_LIST: [usize; 8] = [
495 nine_index(0, 0),
496 nine_index(1, 0),
497 nine_index(2, 0),
498 nine_index(2, 1),
499 nine_index(2, 2),
500 nine_index(1, 2),
501 nine_index(0, 2),
502 nine_index(0, 1),
503];
504
505fn nine_rotate(nine: &mut NineI8, amount: usize) {
506 let nine = &mut nine.0;
507 let copy = *nine;
508 for i in 0..(8 - amount) {
509 nine[NINE_ROTATE_LIST[i + amount]] = copy[NINE_ROTATE_LIST[i]];
510 }
511 for i in 0..amount {
512 nine[NINE_ROTATE_LIST[i]] = copy[NINE_ROTATE_LIST[8 - amount + i]];
513 }
514}
515
516impl TileSetPropertyValue {
517 pub fn make_default(&self) -> TileSetPropertyValue {
519 match self {
520 TileSetPropertyValue::I32(_) => TileSetPropertyValue::I32(0),
521 TileSetPropertyValue::F32(_) => TileSetPropertyValue::F32(0.0),
522 TileSetPropertyValue::String(_) => {
523 TileSetPropertyValue::String(ImmutableString::default())
524 }
525 TileSetPropertyValue::NineSlice(_) => {
526 TileSetPropertyValue::NineSlice(Default::default())
527 }
528 }
529 }
530 pub fn prop_type(&self) -> TileSetPropertyType {
532 match self {
533 TileSetPropertyValue::I32(_) => TileSetPropertyType::I32,
534 TileSetPropertyValue::F32(_) => TileSetPropertyType::F32,
535 TileSetPropertyValue::String(_) => TileSetPropertyType::String,
536 TileSetPropertyValue::NineSlice(_) => TileSetPropertyType::NineSlice,
537 }
538 }
539 #[inline]
541 pub fn nine_position_to_index(position: Vector2<usize>) -> usize {
542 if position.y > 2 || position.x > 2 {
543 panic!("Illegal nine slice position: {position:?}");
544 }
545 position.y * 3 + position.x
546 }
547 #[inline]
549 pub fn index_to_nine_position(index: usize) -> Vector2<usize> {
550 let (y, x) = index.div_rem_euclid(&3);
551 Vector2::new(x, y)
552 }
553 pub fn set_from(&mut self, value: &TileSetPropertyOptionValue) {
556 use TileSetPropertyOptionValue as OptValue;
557 use TileSetPropertyValue as PropValue;
558 match (self, value) {
559 (PropValue::I32(x0), OptValue::I32(Some(x1))) => *x0 = *x1,
560 (PropValue::F32(x0), OptValue::F32(Some(x1))) => *x0 = *x1,
561 (PropValue::String(x0), OptValue::String(Some(x1))) => *x0 = x1.clone(),
562 (PropValue::NineSlice(arr0), OptValue::NineSlice(arr1)) => {
563 for (x0, x1) in arr0.0.iter_mut().zip(arr1.iter()) {
564 if let Some(v) = x1 {
565 *x0 = *v;
566 }
567 }
568 }
569 _ => (),
570 }
571 }
572}
573
574#[derive(Clone, Debug, PartialEq, Reflect, Visit)]
577pub enum TileSetPropertyOptionValue {
578 I32(Option<i32>),
580 F32(Option<f32>),
582 String(Option<ImmutableString>),
584 NineSlice([Option<i8>; 9]),
587}
588
589impl Default for TileSetPropertyOptionValue {
590 fn default() -> Self {
591 Self::I32(None)
592 }
593}
594
595impl TryFrom<TileSetPropertyValue> for i32 {
596 type Error = TilePropertyError;
597
598 fn try_from(value: TileSetPropertyValue) -> Result<Self, Self::Error> {
599 use TilePropertyError::*;
600 use TileSetPropertyValue::*;
601 match value {
602 I32(v) => Ok(v),
603 F32(_) => Err(WrongType("Expected: i32, Found: f32")),
604 String(_) => Err(WrongType("Expected: i32, Found: ImmutableString")),
605 NineSlice(_) => Err(WrongType("Expected: i32, Found: NineI8")),
606 }
607 }
608}
609
610impl TryFrom<TileSetPropertyValue> for f32 {
611 type Error = TilePropertyError;
612
613 fn try_from(value: TileSetPropertyValue) -> Result<Self, Self::Error> {
614 use TilePropertyError::*;
615 use TileSetPropertyValue::*;
616 match value {
617 I32(_) => Err(WrongType("Expected: f32, Found: i32")),
618 F32(v) => Ok(v),
619 String(_) => Err(WrongType("Expected: f32, Found: ImmutableString")),
620 NineSlice(_) => Err(WrongType("Expected: f32, Found: NineI8")),
621 }
622 }
623}
624
625impl TryFrom<TileSetPropertyValue> for ImmutableString {
626 type Error = TilePropertyError;
627
628 fn try_from(value: TileSetPropertyValue) -> Result<Self, Self::Error> {
629 use TilePropertyError::*;
630 use TileSetPropertyValue::*;
631 match value {
632 I32(_) => Err(WrongType("Expected: ImmutableString, Found: i32")),
633 F32(_) => Err(WrongType("Expected: ImmutableString, Found: f32")),
634 String(v) => Ok(v),
635 NineSlice(_) => Err(WrongType("Expected: ImmutableString, Found: NineI8")),
636 }
637 }
638}
639
640impl TryFrom<TileSetPropertyValue> for NineI8 {
641 type Error = TilePropertyError;
642
643 fn try_from(value: TileSetPropertyValue) -> Result<Self, Self::Error> {
644 use TilePropertyError::*;
645 use TileSetPropertyValue::*;
646 match value {
647 I32(_) => Err(WrongType("Expected: NineI8, Found: i32")),
648 F32(_) => Err(WrongType("Expected: NineI8, Found: f32")),
649 String(_) => Err(WrongType("Expected: NineI8, Found: ImmutableString")),
650 NineSlice(v) => Ok(v),
651 }
652 }
653}
654
655impl From<TileSetPropertyValue> for TileSetPropertyOptionValue {
656 fn from(value: TileSetPropertyValue) -> Self {
657 use TileSetPropertyOptionValue as OValue;
658 use TileSetPropertyValue as Value;
659 match value {
660 Value::I32(x) => OValue::I32(Some(x)),
661 Value::F32(x) => OValue::F32(Some(x)),
662 Value::String(x) => OValue::String(Some(x)),
663 Value::NineSlice(arr) => OValue::NineSlice(arr.0.map(Some)),
664 }
665 }
666}
667
668impl From<TileSetPropertyOptionValue> for TileSetPropertyValue {
669 fn from(value: TileSetPropertyOptionValue) -> Self {
670 use TileSetPropertyOptionValue as OValue;
671 use TileSetPropertyValue as Value;
672 match value {
673 OValue::I32(x) => Value::I32(x.unwrap_or_default()),
674 OValue::F32(x) => Value::F32(x.unwrap_or_default()),
675 OValue::String(x) => Value::String(x.unwrap_or_default()),
676 OValue::NineSlice(arr) => Value::NineSlice(NineI8(arr.map(Option::unwrap_or_default))),
677 }
678 }
679}
680
681impl TileSetPropertyOptionValue {
682 pub fn intersect(&mut self, value: &TileSetPropertyValue) {
685 use TileSetPropertyOptionValue as OptValue;
686 use TileSetPropertyValue as PropValue;
687 match self {
688 OptValue::I32(x0) => {
689 if let Some(x) = x0 {
690 if *value != PropValue::I32(*x) {
691 *x0 = None
692 }
693 }
694 }
695 OptValue::F32(x0) => {
696 if let Some(x) = x0 {
697 if *value != PropValue::F32(*x) {
698 *x0 = None
699 }
700 }
701 }
702 OptValue::String(x0) => {
703 if let Some(x) = x0 {
704 if let PropValue::String(x1) = value {
705 if *x != *x1 {
706 *x0 = None
707 }
708 } else {
709 *x0 = None
710 }
711 }
712 }
713 OptValue::NineSlice(arr0) => {
714 if let PropValue::NineSlice(arr1) = value {
715 for (x0, x1) in arr0.iter_mut().zip(arr1.0.iter()) {
716 if let Some(x) = x0 {
717 if *x != *x1 {
718 *x0 = None
719 }
720 }
721 }
722 } else {
723 *arr0 = [None; 9];
724 }
725 }
726 }
727 }
728}