1use crate::{
32 asset::{
33 io::ResourceIo,
34 loader::{BoxedLoaderFuture, LoaderPayload, ResourceLoader},
35 manager::ResourceManager,
36 state::LoadError,
37 Resource, ResourceData, ResourceDataRef,
38 },
39 core::{
40 algebra::Vector2, color::Color, io::FileError, log::Log, reflect::prelude::*,
41 type_traits::prelude::*, visitor::prelude::*, ImmutableString,
42 },
43 fxhash::{FxHashMap, FxHashSet},
44 material::{MaterialResource, MaterialResourceExtension},
45 resource::texture::TextureResource,
46};
47use std::{
48 collections::hash_map::{self, Entry, Keys},
49 error::Error,
50 fmt::{Display, Formatter},
51 ops::{Deref, DerefMut},
52 path::{Path, PathBuf},
53 sync::Arc,
54};
55
56use super::*;
57use fyrox_core::{swap_hash_map_entries, swap_hash_map_entry};
58pub use property::*;
59
60const DEFAULT_TILE_SIZE: Vector2<u32> = Vector2::new(16, 16);
61const DEFAULT_ANIMATION_FRAME_RATE: f32 = 12.0;
62
63pub const ELEMENT_MATCH_HIGHLIGHT_COLOR: Color = Color::from_rgba(255, 255, 0, 200);
66
67#[derive(Debug)]
69pub enum TileSetResourceError {
70 Io(FileError),
72
73 Visit(VisitError),
75}
76
77impl Display for TileSetResourceError {
78 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
79 match self {
80 Self::Io(v) => {
81 write!(f, "A file load error has occurred {v:?}")
82 }
83 Self::Visit(v) => {
84 write!(
85 f,
86 "An error that may occur due to version incompatibilities. {v:?}"
87 )
88 }
89 }
90 }
91}
92
93impl From<FileError> for TileSetResourceError {
94 fn from(e: FileError) -> Self {
95 Self::Io(e)
96 }
97}
98
99impl From<VisitError> for TileSetResourceError {
100 fn from(e: VisitError) -> Self {
101 Self::Visit(e)
102 }
103}
104
105#[derive(Clone, Default, PartialEq, Debug, Reflect, Visit)]
107#[visit(optional)]
108pub struct TileDefinition {
109 pub material_bounds: TileMaterialBounds,
111 pub data: TileData,
113}
114
115impl TileDefinition {
116 fn from_material_bounds(material_bounds: TileMaterialBounds) -> Self {
117 Self {
118 material_bounds,
119 data: TileData::default(),
120 }
121 }
122}
123
124impl OrthoTransform for TileDefinition {
125 fn x_flipped(mut self) -> Self {
126 self.material_bounds = self.material_bounds.x_flipped();
127 self.data = self.data.x_flipped();
128 self
129 }
130
131 fn rotated(mut self, amount: i8) -> Self {
132 self.material_bounds = self.material_bounds.rotated(amount);
133 self.data = self.data.rotated(amount);
134 self
135 }
136}
137
138#[derive(Clone, PartialEq, Debug, Reflect, Visit)]
140pub struct TileMaterialBounds {
141 pub material: MaterialResource,
143 pub bounds: TileBounds,
145}
146
147impl Default for TileMaterialBounds {
148 fn default() -> Self {
149 Self {
150 material: Resource::new_ok(
151 Uuid::new_v4(),
152 ResourceKind::Embedded,
153 Material::standard_tile(),
154 ),
155 bounds: Default::default(),
156 }
157 }
158}
159
160impl OrthoTransform for TileMaterialBounds {
161 fn x_flipped(mut self) -> Self {
162 self.bounds = self.bounds.x_flipped();
163 self
164 }
165
166 fn rotated(mut self, amount: i8) -> Self {
167 self.bounds = self.bounds.rotated(amount);
168 self
169 }
170}
171
172#[derive(Clone, Default, PartialEq, Debug, Reflect, Visit)]
174#[visit(optional)]
175pub struct TileData {
176 pub colliders: FxHashMap<Uuid, TileCollider>,
178 pub color: Color,
180 pub properties: FxHashMap<Uuid, TileSetPropertyValue>,
184}
185
186impl OrthoTransform for TileData {
187 fn x_flipped(mut self) -> Self {
188 for (_, value) in self.properties.iter_mut() {
189 *value = value.clone().x_flipped();
190 }
191 for (_, value) in self.colliders.iter_mut() {
192 *value = value.clone().x_flipped();
193 }
194 self
195 }
196
197 fn rotated(mut self, amount: i8) -> Self {
198 for (_, value) in self.properties.iter_mut() {
199 *value = value.clone().rotated(amount);
200 }
201 for (_, value) in self.colliders.iter_mut() {
202 *value = value.clone().rotated(amount);
203 }
204 self
205 }
206}
207
208#[derive(Clone, Default, PartialEq, Debug, Reflect, Visit)]
213pub struct TileBounds {
214 pub left_top_corner: Vector2<u32>,
216 pub right_top_corner: Vector2<u32>,
218 pub right_bottom_corner: Vector2<u32>,
220 pub left_bottom_corner: Vector2<u32>,
222}
223
224fn pixel_coords_to_uv(position: Vector2<u32>, total_size: Vector2<u32>) -> Vector2<f32> {
225 Vector2::new(
226 position.x as f32 / total_size.x as f32,
227 position.y as f32 / total_size.y as f32,
228 )
229}
230
231impl TileBounds {
232 pub fn left_top_uv(&self, texture_size: Vector2<u32>) -> Vector2<f32> {
234 pixel_coords_to_uv(self.left_top_corner, texture_size)
235 }
236 pub fn right_top_uv(&self, texture_size: Vector2<u32>) -> Vector2<f32> {
238 pixel_coords_to_uv(self.right_top_corner, texture_size)
239 }
240 pub fn left_bottom_uv(&self, texture_size: Vector2<u32>) -> Vector2<f32> {
242 pixel_coords_to_uv(self.left_bottom_corner, texture_size)
243 }
244 pub fn right_bottom_uv(&self, texture_size: Vector2<u32>) -> Vector2<f32> {
246 pixel_coords_to_uv(self.right_bottom_corner, texture_size)
247 }
248 pub fn get(&self, index: usize) -> Vector2<u32> {
251 match index {
252 0 => self.left_bottom_corner,
253 1 => self.right_bottom_corner,
254 2 => self.right_top_corner,
255 3 => self.left_top_corner,
256 _ => panic!(),
257 }
258 }
259 pub fn get_mut(&mut self, index: usize) -> &mut Vector2<u32> {
262 match index {
263 0 => &mut self.left_bottom_corner,
264 1 => &mut self.right_bottom_corner,
265 2 => &mut self.right_top_corner,
266 3 => &mut self.left_top_corner,
267 _ => panic!(),
268 }
269 }
270}
271
272impl OrthoTransform for TileBounds {
273 fn x_flipped(mut self) -> Self {
274 std::mem::swap(&mut self.left_top_corner, &mut self.right_top_corner);
275 std::mem::swap(&mut self.left_bottom_corner, &mut self.right_bottom_corner);
276 self
277 }
278
279 fn rotated(mut self, amount: i8) -> Self {
280 let old = self.clone();
281 let amount = amount.rem_euclid(4) as usize;
282 for i in 0..4 {
283 *self.get_mut((i + amount).rem_euclid(4)) = old.get(i);
284 }
285 self
286 }
287}
288
289#[derive(Clone, Default, Debug, Visit, Reflect)]
292pub struct TileSetPage {
293 pub icon: TileDefinitionHandle,
295 pub source: TileSetPageSource,
297}
298
299impl TileSetPage {
300 pub fn is_material(&self) -> bool {
302 matches!(self.source, TileSetPageSource::Atlas(_))
303 }
304 pub fn is_freeform(&self) -> bool {
306 matches!(self.source, TileSetPageSource::Freeform(_))
307 }
308 pub fn is_transform_set(&self) -> bool {
310 matches!(self.source, TileSetPageSource::Transform(_))
311 }
312 pub fn is_animation(&self) -> bool {
314 matches!(self.source, TileSetPageSource::Animation(_))
315 }
316 pub fn page_type(&self) -> PageType {
318 self.source.page_type()
319 }
320 pub fn has_tile_at(&self, position: Vector2<i32>) -> bool {
322 match &self.source {
323 TileSetPageSource::Atlas(mat) => mat.tiles.contains_key(&position),
324 TileSetPageSource::Freeform(map) => map.contains_key(&position),
325 TileSetPageSource::Transform(tiles) => tiles.contains_key(&position),
326 TileSetPageSource::Animation(tiles) => tiles.contains_key(&position),
327 }
328 }
329 pub fn keys(&self) -> Vec<Vector2<i32>> {
331 match &self.source {
332 TileSetPageSource::Atlas(mat) => mat.tiles.keys().copied().collect(),
333 TileSetPageSource::Freeform(map) => map.keys().copied().collect(),
334 TileSetPageSource::Transform(tiles) => tiles.keys().copied().collect(),
335 TileSetPageSource::Animation(tiles) => tiles.keys().copied().collect(),
336 }
337 }
338 pub fn get_bounds(&self) -> OptionTileRect {
340 let mut result = OptionTileRect::default();
341 match &self.source {
342 TileSetPageSource::Atlas(mat) => {
343 for pos in mat.tiles.keys() {
344 result.push(*pos);
345 }
346 }
347 TileSetPageSource::Freeform(map) => {
348 for pos in map.keys() {
349 result.push(*pos);
350 }
351 }
352 TileSetPageSource::Transform(tiles) => {
353 for pos in tiles.keys() {
354 result.push(*pos);
355 }
356 }
357 TileSetPageSource::Animation(tiles) => {
358 for pos in tiles.keys() {
359 result.push(*pos);
360 }
361 }
362 }
363 result
364 }
365 pub fn swap_tile(&mut self, position: Vector2<i32>, update: &mut TileDataUpdate) {
368 match &mut self.source {
369 TileSetPageSource::Atlas(map0) => swap_material_tile(map0, position, update),
370 TileSetPageSource::Freeform(map0) => swap_freeform_tile(map0, position, update),
371 TileSetPageSource::Transform(map0) => swap_transform_tile(map0, position, update),
372 TileSetPageSource::Animation(map0) => swap_animation_tile(map0, position, update),
373 }
374 }
375
376 pub fn swap_all_values_for_property(
379 &mut self,
380 page: Vector2<i32>,
381 property_id: Uuid,
382 values: &mut FxHashMap<TileDefinitionHandle, TileSetPropertyValue>,
383 ) {
384 match &mut self.source {
385 TileSetPageSource::Atlas(map0) => {
386 let tiles = &mut map0.tiles;
387 for (tile, data) in tiles.iter_mut() {
388 let Some(handle) = TileDefinitionHandle::try_new(page, *tile) else {
389 continue;
390 };
391 swap_hash_map_entries(data.properties.entry(property_id), values.entry(handle));
392 }
393 }
394 TileSetPageSource::Freeform(tiles) => {
395 for (tile, tile_def) in tiles.iter_mut() {
396 let Some(handle) = TileDefinitionHandle::try_new(page, *tile) else {
397 continue;
398 };
399 swap_hash_map_entries(
400 tile_def.data.properties.entry(property_id),
401 values.entry(handle),
402 );
403 }
404 }
405 TileSetPageSource::Transform(_) => (),
406 TileSetPageSource::Animation(_) => (),
407 }
408 }
409 pub fn swap_all_values_for_collider(
412 &mut self,
413 page: Vector2<i32>,
414 collider_id: Uuid,
415 values: &mut FxHashMap<TileDefinitionHandle, TileCollider>,
416 ) {
417 match &mut self.source {
418 TileSetPageSource::Atlas(map0) => {
419 let tiles = &mut map0.tiles;
420 for (tile, data) in tiles.iter_mut() {
421 let Some(handle) = TileDefinitionHandle::try_new(page, *tile) else {
422 continue;
423 };
424 swap_hash_map_entries(data.colliders.entry(collider_id), values.entry(handle));
425 }
426 }
427 TileSetPageSource::Freeform(tiles) => {
428 for (tile, tile_def) in tiles.iter_mut() {
429 let Some(handle) = TileDefinitionHandle::try_new(page, *tile) else {
430 continue;
431 };
432 swap_hash_map_entries(
433 tile_def.data.colliders.entry(collider_id),
434 values.entry(handle),
435 );
436 }
437 }
438 _ => panic!(),
439 }
440 }
441}
442
443fn swap_material_tile(
444 map0: &mut TileMaterial,
445 position: Vector2<i32>,
446 update: &mut TileDataUpdate,
447) {
448 let e0 = map0.tiles.entry(position);
449 match (e0, update) {
450 (Entry::Occupied(d0), d1 @ TileDataUpdate::Erase) => {
451 *d1 = TileDataUpdate::MaterialTile(d0.remove())
452 }
453 (Entry::Vacant(d0), d1 @ TileDataUpdate::MaterialTile(_)) => {
454 d0.insert(d1.take_data());
455 }
456 (Entry::Vacant(_), TileDataUpdate::Erase) => (),
457 (Entry::Occupied(mut d0), d1) => d1.swap_with_data(d0.get_mut()),
458 (Entry::Vacant(d0), d1) => {
459 let mut data = TileData::default();
460 d1.swap_with_data(&mut data);
461 let _ = d0.insert(data);
462 *d1 = TileDataUpdate::Erase;
463 }
464 }
465}
466fn swap_freeform_tile(
467 map0: &mut TileGridMap<TileDefinition>,
468 position: Vector2<i32>,
469 update: &mut TileDataUpdate,
470) {
471 let e0 = map0.entry(position);
472 match (e0, update) {
473 (Entry::Occupied(mut d0), TileDataUpdate::Material(d1)) => {
474 std::mem::swap(&mut d0.get_mut().material_bounds, d1)
475 }
476 (Entry::Vacant(d0), d1 @ TileDataUpdate::Material(_)) => {
477 let TileDataUpdate::Material(material_bounds) = std::mem::take(d1) else {
478 unreachable!();
479 };
480 let def = TileDefinition::from_material_bounds(material_bounds);
481 let _ = d0.insert(def);
482 }
483 (Entry::Occupied(mut d0), TileDataUpdate::FreeformTile(d1)) => {
484 std::mem::swap(d0.get_mut(), d1);
485 }
486 (Entry::Occupied(d0), d1 @ TileDataUpdate::Erase) => {
487 *d1 = TileDataUpdate::FreeformTile(d0.remove())
488 }
489 (Entry::Vacant(d0), d1 @ TileDataUpdate::FreeformTile(_)) => {
490 d0.insert(d1.take_definition());
491 }
492 (Entry::Vacant(_), TileDataUpdate::Erase) => (),
493 (Entry::Occupied(mut d0), d1) => d1.swap_with_data(&mut d0.get_mut().data),
494 (Entry::Vacant(_), d1) => {
495 *d1 = TileDataUpdate::Erase;
497 }
498 }
499}
500
501fn swap_transform_tile(
502 map0: &mut TransformSetTiles,
503 position: Vector2<i32>,
504 update: &mut TileDataUpdate,
505) {
506 let e0 = map0.entry(position);
507 let TileDataUpdate::TransformSet(handle) = update else {
508 panic!()
509 };
510 swap_hash_map_entry(e0, handle);
511}
512
513fn swap_animation_tile(
514 map0: &mut AnimationTiles,
515 position: Vector2<i32>,
516 update: &mut TileDataUpdate,
517) {
518 let e0 = map0.entry(position);
519 let TileDataUpdate::TransformSet(handle) = update else {
520 panic!()
521 };
522 swap_hash_map_entry(e0, handle);
523}
524
525#[derive(Debug, Clone)]
528pub enum AbstractTile {
529 Atlas(TileData),
532 Freeform(TileDefinition),
536 Transform(TileDefinitionHandle),
541}
542
543#[derive(Clone, PartialEq, Debug, Visit, Reflect)]
545pub enum TileSetPageSource {
546 Atlas(TileMaterial),
551 Freeform(TileGridMap<TileDefinition>),
554 Transform(TransformSetTiles),
560 Animation(AnimationTiles),
566}
567
568impl Default for TileSetPageSource {
569 fn default() -> Self {
570 Self::Atlas(TileMaterial::default())
571 }
572}
573
574impl TileSetPageSource {
575 pub fn new_material() -> Self {
577 Self::Atlas(TileMaterial::default())
578 }
579 pub fn new_free() -> Self {
581 Self::Freeform(TileGridMap::default())
582 }
583 pub fn new_transform() -> Self {
585 Self::Transform(TransformSetTiles::default())
586 }
587 pub fn new_animation() -> Self {
589 Self::Animation(AnimationTiles::default())
590 }
591 pub fn page_type(&self) -> PageType {
593 match self {
594 TileSetPageSource::Atlas(_) => PageType::Atlas,
595 TileSetPageSource::Freeform(_) => PageType::Freeform,
596 TileSetPageSource::Transform(_) => PageType::Transform,
597 TileSetPageSource::Animation(_) => PageType::Animation,
598 }
599 }
600 pub fn contains_tile_at(&self, position: Vector2<i32>) -> bool {
602 match self {
603 TileSetPageSource::Atlas(map) => map.contains_key(&position),
604 TileSetPageSource::Freeform(map) => map.contains_key(&position),
605 TileSetPageSource::Transform(map) => map.contains_key(&position),
606 TileSetPageSource::Animation(map) => map.contains_key(&position),
607 }
608 }
609}
610
611#[derive(Default, Clone, PartialEq, Debug, Reflect, Visit)]
620pub struct TransformSetTiles(
621 #[reflect(hidden)]
622 #[visit(optional)]
623 pub Tiles,
624);
625
626impl Deref for TransformSetTiles {
627 type Target = Tiles;
628
629 fn deref(&self) -> &Self::Target {
630 &self.0
631 }
632}
633
634impl DerefMut for TransformSetTiles {
635 fn deref_mut(&mut self) -> &mut Self::Target {
636 &mut self.0
637 }
638}
639
640#[derive(Clone, PartialEq, Debug, Reflect, Visit)]
646pub struct AnimationTiles {
647 pub frame_rate: f32,
649 #[reflect(hidden)]
651 #[visit(optional)]
652 pub tiles: Tiles,
653}
654
655impl Default for AnimationTiles {
656 fn default() -> Self {
657 Self {
658 frame_rate: DEFAULT_ANIMATION_FRAME_RATE,
659 tiles: Default::default(),
660 }
661 }
662}
663
664impl Deref for AnimationTiles {
665 type Target = Tiles;
666
667 fn deref(&self) -> &Self::Target {
668 &self.tiles
669 }
670}
671
672impl DerefMut for AnimationTiles {
673 fn deref_mut(&mut self) -> &mut Self::Target {
674 &mut self.tiles
675 }
676}
677
678#[derive(Clone, PartialEq, Debug, Visit, Reflect)]
681pub struct TileMaterial {
682 pub material: MaterialResource,
684 pub tile_size: Vector2<u32>,
686 pub tiles: TileGridMap<TileData>,
688}
689
690impl Deref for TileMaterial {
691 type Target = TileGridMap<TileData>;
692
693 fn deref(&self) -> &Self::Target {
694 &self.tiles
695 }
696}
697
698impl DerefMut for TileMaterial {
699 fn deref_mut(&mut self) -> &mut Self::Target {
700 &mut self.tiles
701 }
702}
703
704impl Default for TileMaterial {
705 fn default() -> Self {
706 Self {
707 material: DEFAULT_TILE_MATERIAL.deep_copy_as_embedded(),
708 tile_size: DEFAULT_TILE_SIZE,
709 tiles: TileGridMap::default(),
710 }
711 }
712}
713
714impl TileMaterial {
715 fn get_tile_bounds(&self, position: Vector2<i32>) -> Option<TileMaterialBounds> {
716 let origin = Vector2::new(
717 u32::try_from(position.x).ok()? * self.tile_size.x,
718 u32::try_from(-1 - position.y).ok()? * self.tile_size.y,
719 );
720 Some(TileMaterialBounds {
721 material: self.material.clone(),
722 bounds: TileBounds {
723 left_top_corner: origin,
724 right_top_corner: origin + Vector2::new(self.tile_size.x, 0),
725 left_bottom_corner: origin + Vector2::new(0, self.tile_size.y),
726 right_bottom_corner: origin + self.tile_size,
727 },
728 })
729 }
730 fn get_abstract_tile(&self, position: Vector2<i32>) -> Option<AbstractTile> {
731 Some(AbstractTile::Atlas(self.tiles.get(&position)?.clone()))
732 }
733 fn set_abstract_tile(
734 &mut self,
735 position: Vector2<i32>,
736 tile: Option<AbstractTile>,
737 ) -> Option<AbstractTile> {
738 if let Some(tile) = tile {
739 let AbstractTile::Atlas(data) = tile else {
740 panic!();
741 };
742 self.tiles.insert(position, data).map(AbstractTile::Atlas)
743 } else {
744 self.tiles.remove(&position).map(AbstractTile::Atlas)
745 }
746 }
747 fn get_tile_data(&self, position: Vector2<i32>) -> Option<&TileData> {
748 self.tiles.get(&position)
749 }
750 fn get_tile_data_mut(&mut self, position: Vector2<i32>) -> Option<&mut TileData> {
751 self.tiles.get_mut(&position)
752 }
753}
754
755#[derive(Default)]
757pub struct TileSetHandleIterator<'a> {
758 pages_iter: Option<hash_map::Iter<'a, Vector2<i32>, TileSetPage>>,
759 page_position: Vector2<i32>,
760 handles_iter: TileSetPageHandleIterator<'a>,
761}
762
763#[derive(Default)]
764enum TileSetPageHandleIterator<'a> {
765 #[default]
766 Empty,
767 Atlas(Keys<'a, Vector2<i32>, TileData>),
768 Freeform(Keys<'a, Vector2<i32>, TileDefinition>),
769}
770
771impl<'a> TileSetHandleIterator<'a> {
772 fn new(pages_iter: Option<hash_map::Iter<'a, Vector2<i32>, TileSetPage>>) -> Self {
773 Self {
774 pages_iter,
775 page_position: Vector2::default(),
776 handles_iter: TileSetPageHandleIterator::Empty,
777 }
778 }
779}
780
781impl Iterator for TileSetPageHandleIterator<'_> {
782 type Item = Vector2<i32>;
783
784 fn next(&mut self) -> Option<Self::Item> {
785 match self {
786 TileSetPageHandleIterator::Empty => None,
787 TileSetPageHandleIterator::Atlas(keys) => keys.next().cloned(),
788 TileSetPageHandleIterator::Freeform(keys) => keys.next().cloned(),
789 }
790 }
791}
792
793impl Iterator for TileSetHandleIterator<'_> {
794 type Item = TileDefinitionHandle;
795
796 fn next(&mut self) -> Option<Self::Item> {
797 loop {
798 if let Some(tile) = self.handles_iter.next() {
799 return TileDefinitionHandle::try_new(self.page_position, tile);
800 }
801 let (&page_position, page) = self.pages_iter.as_mut()?.next()?;
802 self.page_position = page_position;
803 self.handles_iter = match &page.source {
804 TileSetPageSource::Atlas(map) => TileSetPageHandleIterator::Atlas(map.keys()),
805 TileSetPageSource::Freeform(map) => TileSetPageHandleIterator::Freeform(map.keys()),
806 _ => TileSetPageHandleIterator::Empty,
807 };
808 }
809 }
810}
811
812pub struct TileSetPaletteIterator<'a> {
814 keys: PaletteIterator<'a>,
815 page: Vector2<i32>,
816}
817
818enum PaletteIterator<'a> {
819 Empty,
820 Material(Keys<'a, Vector2<i32>, TileData>),
821 Freeform(Keys<'a, Vector2<i32>, TileDefinition>),
822 TransformSet(Keys<'a, Vector2<i32>, TileDefinitionHandle>),
823 Pages(Keys<'a, Vector2<i32>, TileSetPage>),
824}
825
826impl Iterator for TileSetPaletteIterator<'_> {
827 type Item = ResourceTilePosition;
828 fn next(&mut self) -> Option<Self::Item> {
829 match &mut self.keys {
830 PaletteIterator::Empty => None,
831 PaletteIterator::Material(iter) => iter
832 .next()
833 .map(|t| ResourceTilePosition::Tile(self.page, *t)),
834 PaletteIterator::Freeform(iter) => iter
835 .next()
836 .map(|t| ResourceTilePosition::Tile(self.page, *t)),
837 PaletteIterator::TransformSet(iter) => iter
838 .next()
839 .map(|t| ResourceTilePosition::Tile(self.page, *t)),
840 PaletteIterator::Pages(iter) => iter.next().copied().map(ResourceTilePosition::Page),
841 }
842 }
843}
844
845pub struct TileSetRef<'a>(ResourceDataRef<'a, TileSet>);
848pub struct OptionTileSet<'a>(pub Option<&'a mut TileSet>);
851
852impl<'a> From<ResourceDataRef<'a, TileSet>> for TileSetRef<'a> {
853 fn from(value: ResourceDataRef<'a, TileSet>) -> Self {
854 Self(value)
855 }
856}
857
858impl<'a> TileSetRef<'a> {
859 pub fn new(tile_set: &'a TileSetResource) -> Self {
862 tile_set.data_ref().into()
863 }
864 #[inline]
866 pub fn as_loaded(&mut self) -> OptionTileSet {
867 OptionTileSet(self.0.as_loaded_mut())
868 }
869}
870
871impl<'a> Deref for OptionTileSet<'a> {
872 type Target = Option<&'a mut TileSet>;
873
874 fn deref(&self) -> &Self::Target {
875 &self.0
876 }
877}
878
879impl<'a> OptionTileSet<'a> {
880 pub fn as_ref(&'a self) -> Option<&'a TileSet> {
882 self.0.as_deref()
883 }
884 pub fn all_tiles(&self) -> TileSetHandleIterator {
886 self.as_ref().map(|t| t.all_tiles()).unwrap_or_default()
887 }
888 pub fn tile_property_value<T>(
892 &self,
893 handle: TileDefinitionHandle,
894 property_id: Uuid,
895 ) -> Result<T, TilePropertyError>
896 where
897 T: TryFrom<TileSetPropertyValue, Error = TilePropertyError> + Default,
898 {
899 self.as_ref()
900 .map(|t| t.tile_property_value(handle, property_id))
901 .unwrap_or(Err(TilePropertyError::MissingTileSet))
902 }
903 pub fn tile_property_value_by_name(
906 &self,
907 handle: TileDefinitionHandle,
908 property_name: &ImmutableString,
909 ) -> Result<TileSetPropertyValue, TilePropertyError> {
910 self.as_ref()
911 .map(|t| t.tile_property_value_by_name(handle, property_name))
912 .unwrap_or(Err(TilePropertyError::MissingTileSet))
913 }
914 pub fn tile_property_value_by_uuid_untyped(
917 &self,
918 handle: TileDefinitionHandle,
919 property_id: Uuid,
920 ) -> Result<TileSetPropertyValue, TilePropertyError> {
921 self.as_ref()
922 .map(|t| t.tile_property_value_by_uuid_untyped(handle, property_id))
923 .unwrap_or(Err(TilePropertyError::MissingTileSet))
924 }
925 pub fn properties(&self) -> &[TileSetPropertyLayer] {
927 self.as_ref()
928 .map(|t| t.properties.deref())
929 .unwrap_or_default()
930 }
931 pub fn colliders(&self) -> &[TileSetColliderLayer] {
933 self.as_ref()
934 .map(|t| t.colliders.deref())
935 .unwrap_or_default()
936 }
937 pub fn collider_color(&self, uuid: Uuid) -> Option<Color> {
939 self.as_ref()
940 .map(|t| t.collider_color(uuid))
941 .unwrap_or_default()
942 }
943 pub fn tile_collider(&self, handle: TileDefinitionHandle, uuid: Uuid) -> &TileCollider {
945 self.as_ref()
946 .map(|t| t.tile_collider(handle, uuid))
947 .unwrap_or_default()
948 }
949 pub fn tile_color(&self, handle: TileDefinitionHandle) -> Option<Color> {
951 self.as_ref()
952 .map(|t| t.tile_color(handle))
953 .unwrap_or_default()
954 }
955 pub fn tile_data(&self, handle: TileDefinitionHandle) -> Option<&TileData> {
957 self.as_ref()
958 .map(|t| t.tile_data(handle))
959 .unwrap_or_default()
960 }
961 pub fn tile_bounds(&self, handle: TileDefinitionHandle) -> Option<&TileMaterialBounds> {
963 self.as_ref()
964 .map(|t| t.tile_bounds(handle))
965 .unwrap_or_default()
966 }
967 pub fn tile_redirect(&self, handle: TileDefinitionHandle) -> Option<TileDefinitionHandle> {
970 self.as_ref()
971 .map(|t| t.tile_redirect(handle))
972 .unwrap_or_default()
973 }
974 pub fn keys_on_page(&self, page: Vector2<i32>) -> Vec<Vector2<i32>> {
976 self.as_ref()
977 .map(|t| t.keys_on_page(page))
978 .unwrap_or_default()
979 }
980 pub fn page_keys(&self) -> Vec<Vector2<i32>> {
982 self.as_ref().map(|t| t.page_keys()).unwrap_or_default()
983 }
984 pub fn has_tile_at(&self, page: Vector2<i32>, tile: Vector2<i32>) -> bool {
986 self.as_ref()
987 .map(|t| t.has_tile_at(page, tile))
988 .unwrap_or_default()
989 }
990 pub fn page_icon(&self, page: Vector2<i32>) -> Option<TileDefinitionHandle> {
992 self.as_ref().map(|t| t.page_icon(page)).unwrap_or_default()
993 }
994 pub fn property_name_to_uuid(&self, name: &ImmutableString) -> Option<Uuid> {
996 self.as_ref()
997 .map(|t| t.property_name_to_uuid(name))
998 .unwrap_or_default()
999 }
1000 pub fn collider_name_to_uuid(&self, name: &ImmutableString) -> Option<Uuid> {
1002 self.as_ref()
1003 .map(|t| t.collider_name_to_uuid(name))
1004 .unwrap_or_default()
1005 }
1006 pub fn find_property(&self, uuid: Uuid) -> Option<&TileSetPropertyLayer> {
1008 self.as_ref()
1009 .map(|t| t.find_property(uuid))
1010 .unwrap_or_default()
1011 }
1012 pub fn find_collider(&self, uuid: Uuid) -> Option<&TileSetColliderLayer> {
1014 self.as_ref()
1015 .map(|t| t.find_collider(uuid))
1016 .unwrap_or_default()
1017 }
1018 pub fn rebuild_transform_sets(&mut self) {
1023 if let Some(t) = &mut self.0 {
1024 t.rebuild_transform_sets()
1025 }
1026 }
1027 pub fn rebuild_animations(&mut self) {
1031 if let Some(t) = &mut self.0 {
1032 t.rebuild_animations()
1033 }
1034 }
1035 pub fn preview_texture(&self) -> Option<TextureResource> {
1037 self.as_ref()
1038 .map(|t| t.preview_texture())
1039 .unwrap_or_default()
1040 }
1041 pub fn get_page(&self, position: Vector2<i32>) -> Option<&TileSetPage> {
1043 self.as_ref()
1044 .map(|t| t.get_page(position))
1045 .unwrap_or_default()
1046 }
1047 pub fn get_page_mut(&mut self, position: Vector2<i32>) -> Option<&mut TileSetPage> {
1049 self.0
1050 .as_mut()
1051 .map(|t| t.get_page_mut(position))
1052 .unwrap_or_default()
1053 }
1054 pub fn is_valid_tile(&self, handle: TileDefinitionHandle) -> bool {
1056 self.as_ref()
1057 .map(|t| t.is_valid_tile(handle))
1058 .unwrap_or_default()
1059 }
1060 pub fn get_abstract_tile(
1062 &self,
1063 page: Vector2<i32>,
1064 tile: Vector2<i32>,
1065 ) -> Option<AbstractTile> {
1066 self.as_ref()
1067 .map(|t| t.get_abstract_tile(page, tile))
1068 .unwrap_or_default()
1069 }
1070 pub fn get_transformed_render_data(
1072 &self,
1073 trans: OrthoTransformation,
1074 handle: TileDefinitionHandle,
1075 ) -> Option<TileRenderData> {
1076 self.as_ref()
1077 .map(|t| t.get_transformed_render_data(trans, handle))
1078 .unwrap_or_else(|| Some(TileRenderData::missing_data()))
1079 }
1080 pub fn get_tile_render_data(&self, position: ResourceTilePosition) -> Option<TileRenderData> {
1087 self.as_ref()
1088 .map(|t| t.get_tile_render_data(position))
1089 .unwrap_or_else(|| Some(TileRenderData::missing_data()))
1090 }
1091 pub fn get_tile_collider(
1093 &self,
1094 handle: TileDefinitionHandle,
1095 uuid: Uuid,
1096 ) -> Option<&TileCollider> {
1097 self.as_ref()
1098 .map(|t| t.get_tile_collider(handle, uuid))
1099 .unwrap_or_default()
1100 }
1101 pub fn palette_render_loop<F>(&self, stage: TilePaletteStage, page: Vector2<i32>, func: F)
1104 where
1105 F: FnMut(Vector2<i32>, TileRenderData),
1106 {
1107 if let Some(tile_set) = &self.0 {
1108 tile_set.palette_render_loop(stage, page, func);
1109 }
1110 }
1111 pub fn tile_collider_loop<F>(&self, page: Vector2<i32>, func: F)
1114 where
1115 F: FnMut(Vector2<i32>, Uuid, Color, &TileCollider),
1116 {
1117 if let Some(tile_set) = &self.0 {
1118 tile_set.tile_collider_loop(page, func);
1119 }
1120 }
1121
1122 pub fn redirect_handle(&self, position: ResourceTilePosition) -> Option<TileDefinitionHandle> {
1129 self.as_ref()
1130 .map(|t| t.redirect_handle(position))
1131 .unwrap_or_default()
1132 }
1133 pub fn get_transform_tile_source(
1136 &self,
1137 handle: TileDefinitionHandle,
1138 ) -> Option<TileDefinitionHandle> {
1139 self.as_ref()
1140 .map(|t| t.get_transform_tile_source(handle))
1141 .unwrap_or_default()
1142 }
1143 pub fn get_definition(&self, handle: TileDefinitionHandle) -> Option<TileDefinition> {
1146 self.as_ref()
1147 .map(|t| t.get_definition(handle))
1148 .unwrap_or_default()
1149 }
1150 pub fn get_transformed_definition(
1152 &self,
1153 trans: OrthoTransformation,
1154 handle: TileDefinitionHandle,
1155 ) -> Option<TileDefinition> {
1156 self.as_ref()
1157 .map(|t| t.get_transformed_definition(trans, handle))
1158 .unwrap_or_default()
1159 }
1160 pub fn get_tile_bounds(&self, position: ResourceTilePosition) -> Option<TileMaterialBounds> {
1162 self.as_ref()
1163 .map(|t| t.get_tile_bounds(position))
1164 .unwrap_or_default()
1165 }
1166 pub fn property_value(
1168 &self,
1169 handle: TileDefinitionHandle,
1170 property_id: Uuid,
1171 ) -> Option<TileSetPropertyValue> {
1172 self.as_ref()
1173 .map(|t| t.property_value(handle, property_id))
1174 .unwrap_or_default()
1175 }
1176 pub fn get_tile_data(&self, position: ResourceTilePosition) -> Option<&TileData> {
1178 self.as_ref()
1179 .map(|t| t.get_tile_data(position))
1180 .unwrap_or_default()
1181 }
1182 pub fn get_tile_data_mut(&mut self, handle: TileDefinitionHandle) -> Option<&mut TileData> {
1184 self.0
1185 .as_mut()
1186 .map(|t| t.get_tile_data_mut(handle))
1187 .unwrap_or_default()
1188 }
1189
1190 pub fn get_transformed_version(
1196 &self,
1197 transform: OrthoTransformation,
1198 handle: TileDefinitionHandle,
1199 ) -> Option<TileDefinitionHandle> {
1200 self.as_ref()
1201 .map(|t| t.get_transformed_version(transform, handle))
1202 .unwrap_or_default()
1203 }
1204
1205 pub fn get_animated_version(
1208 &self,
1209 time: f32,
1210 handle: TileDefinitionHandle,
1211 ) -> Option<TileDefinitionHandle> {
1212 self.as_ref()
1213 .map(|t| t.get_animated_version(time, handle))
1214 .unwrap_or_default()
1215 }
1216
1217 pub fn get_tiles<I: Iterator<Item = Vector2<i32>>>(
1219 &self,
1220 stage: TilePaletteStage,
1221 page: Vector2<i32>,
1222 iter: I,
1223 tiles: &mut Tiles,
1224 ) {
1225 if let Some(tile_set) = &self.0 {
1226 tile_set.get_tiles(stage, page, iter, tiles);
1227 }
1228 }
1229 pub fn pages_bounds(&self) -> OptionTileRect {
1231 self.as_ref().map(|t| t.pages_bounds()).unwrap_or_default()
1232 }
1233 pub fn tiles_bounds(&self, stage: TilePaletteStage, page: Vector2<i32>) -> OptionTileRect {
1235 self.as_ref()
1236 .map(|t| t.tiles_bounds(stage, page))
1237 .unwrap_or_default()
1238 }
1239
1240 pub fn is_free_at(&self, position: ResourceTilePosition) -> bool {
1242 self.as_ref()
1243 .map(|t| t.is_free_at(position))
1244 .unwrap_or(true)
1245 }
1246}
1247
1248#[derive(Debug, Default, Clone)]
1251struct AnimationRef {
1252 index: usize,
1253 offset: i32,
1254}
1255
1256#[derive(Debug, Default, Clone)]
1258struct Animation {
1259 start: TileDefinitionHandle,
1260 length: i32,
1261}
1262
1263impl Animation {
1264 fn iter(&self) -> impl Iterator<Item = (TileDefinitionHandle, i32)> {
1265 let page = self.start.page();
1266 let tile = self.start.tile();
1267 (0..self.length).filter_map(move |i| {
1268 let x = tile.x.saturating_add(i);
1269 let handle = TileDefinitionHandle::try_new(page, Vector2::new(x, tile.y))?;
1270 Some((handle, i))
1271 })
1272 }
1273 fn page(&self) -> Vector2<i32> {
1274 self.start.page()
1275 }
1276 fn frame(&self, frame: i32) -> Vector2<i32> {
1277 let tile = self.start.tile();
1278 Vector2::new(tile.x + frame.rem_euclid(self.length), tile.y)
1279 }
1280}
1281
1282#[derive(Debug, Default, Clone)]
1284struct AnimationCache {
1285 handle_to_animation: FxHashMap<TileDefinitionHandle, AnimationRef>,
1286 animations: Vec<Animation>,
1287}
1288
1289impl AnimationCache {
1290 fn clear(&mut self) {
1291 self.handle_to_animation.clear();
1292 self.animations.clear();
1293 }
1294 fn add_animation(&mut self, start: TileDefinitionHandle, length: i32) {
1295 self.animations.push(Animation { start, length });
1296 }
1297 fn get_animation_and_offset(&self, handle: TileDefinitionHandle) -> Option<(&Animation, i32)> {
1298 let AnimationRef { index, offset } = self.handle_to_animation.get(&handle)?;
1299 let animation = self.animations.get(*index)?;
1300 Some((animation, *offset))
1301 }
1302}
1303
1304#[derive(Clone, Default, Debug, Visit, Reflect, TypeUuidProvider, ComponentProvider)]
1327#[type_uuid(id = "7b7e057b-a41e-4150-ab3b-0ae99f4024f0")]
1328pub struct TileSet {
1329 #[reflect(hidden)]
1331 #[visit(skip)]
1332 animation_map: AnimationCache,
1333 #[reflect(hidden)]
1335 #[visit(skip)]
1336 transform_map: FxHashMap<TileDefinitionHandle, TileDefinitionHandle>,
1337 pub pages: FxHashMap<Vector2<i32>, TileSetPage>,
1339 pub colliders: Vec<TileSetColliderLayer>,
1341 pub properties: Vec<TileSetPropertyLayer>,
1343 #[reflect(hidden)]
1347 #[visit(skip)]
1348 pub change_flag: ChangeFlag,
1349}
1350
1351impl TileSet {
1352 pub fn stamp_element(&self, position: ResourceTilePosition) -> Option<StampElement> {
1357 self.redirect_handle(position).map(|handle| StampElement {
1358 handle,
1359 source: Some(position),
1360 })
1361 }
1362
1363 pub fn all_tiles(&self) -> TileSetHandleIterator {
1365 TileSetHandleIterator::new(Some(self.pages.iter()))
1366 }
1367 pub fn tile_property_value<T>(
1371 &self,
1372 handle: TileDefinitionHandle,
1373 property_id: Uuid,
1374 ) -> Result<T, TilePropertyError>
1375 where
1376 T: TryFrom<TileSetPropertyValue, Error = TilePropertyError> + Default,
1377 {
1378 self.property_value(handle, property_id)
1379 .map(T::try_from)
1380 .unwrap_or_else(|| Ok(T::default()))
1381 }
1382 pub fn tile_property_value_by_name(
1385 &self,
1386 handle: TileDefinitionHandle,
1387 property_name: &ImmutableString,
1388 ) -> Result<TileSetPropertyValue, TilePropertyError> {
1389 let property = self
1390 .find_property_by_name(property_name)
1391 .ok_or_else(|| TilePropertyError::UnrecognizedName(property_name.clone()))?;
1392 Ok(self
1393 .property_value(handle, property.uuid)
1394 .unwrap_or_else(|| property.prop_type.default_value()))
1395 }
1396 pub fn tile_property_value_by_uuid_untyped(
1399 &self,
1400 handle: TileDefinitionHandle,
1401 property_id: Uuid,
1402 ) -> Result<TileSetPropertyValue, TilePropertyError> {
1403 if let Some(value) = self.property_value(handle, property_id) {
1404 Ok(value)
1405 } else {
1406 let property = self
1407 .find_property(property_id)
1408 .ok_or(TilePropertyError::UnrecognizedUuid(property_id))?;
1409 Ok(property.prop_type.default_value())
1410 }
1411 }
1412 pub fn collider_color(&self, uuid: Uuid) -> Option<Color> {
1414 self.find_collider(uuid).map(|layer| layer.color)
1415 }
1416 pub fn tile_collider(&self, handle: TileDefinitionHandle, uuid: Uuid) -> &TileCollider {
1418 let Some(data) = self.tile_data(handle) else {
1419 return &TileCollider::None;
1420 };
1421 data.colliders.get(&uuid).unwrap_or(&TileCollider::None)
1422 }
1423 pub fn tile_color(&self, handle: TileDefinitionHandle) -> Option<Color> {
1425 self.tile_data(handle).map(|d| d.color)
1426 }
1427 pub fn tile_data(&self, handle: TileDefinitionHandle) -> Option<&TileData> {
1429 let page_source = self.pages.get(&handle.page()).map(|p| &p.source)?;
1430 match page_source {
1431 TileSetPageSource::Atlas(m) => m.get(&handle.tile()),
1432 TileSetPageSource::Freeform(m) => m.get(&handle.tile()).map(|def| &def.data),
1433 TileSetPageSource::Transform(_) => None,
1434 TileSetPageSource::Animation(_) => None,
1435 }
1436 }
1437 pub fn tile_bounds(&self, handle: TileDefinitionHandle) -> Option<&TileMaterialBounds> {
1439 let page_source = self.pages.get(&handle.page()).map(|p| &p.source)?;
1440 if let TileSetPageSource::Freeform(m) = page_source {
1441 m.get(&handle.tile()).map(|t| &t.material_bounds)
1442 } else {
1443 None
1444 }
1445 }
1446 pub fn tile_redirect(&self, handle: TileDefinitionHandle) -> Option<TileDefinitionHandle> {
1449 let page_source = self.pages.get(&handle.page()).map(|p| &p.source)?;
1450 match page_source {
1451 TileSetPageSource::Transform(m) => m.get(&handle.tile()).copied(),
1452 TileSetPageSource::Animation(m) => m.get(&handle.tile()).copied(),
1453 _ => None,
1454 }
1455 }
1456 pub fn keys_on_page(&self, page: Vector2<i32>) -> Vec<Vector2<i32>> {
1458 self.pages.get(&page).map(|p| p.keys()).unwrap_or_default()
1459 }
1460 pub fn page_keys(&self) -> Vec<Vector2<i32>> {
1462 self.pages.keys().copied().collect()
1463 }
1464 pub fn has_tile_at(&self, page: Vector2<i32>, tile: Vector2<i32>) -> bool {
1466 let Some(page) = self.pages.get(&page).map(|p| &p.source) else {
1467 return false;
1468 };
1469 match page {
1470 TileSetPageSource::Atlas(m) => m.contains_key(&tile),
1471 TileSetPageSource::Freeform(m) => m.contains_key(&tile),
1472 TileSetPageSource::Transform(m) => m.contains_key(&tile),
1473 TileSetPageSource::Animation(m) => m.contains_key(&tile),
1474 }
1475 }
1476 pub fn page_icon(&self, page: Vector2<i32>) -> Option<TileDefinitionHandle> {
1478 self.pages.get(&page).map(|p| p.icon)
1479 }
1480 pub fn property_name_to_uuid(&self, name: &ImmutableString) -> Option<Uuid> {
1482 self.find_property_by_name(name).map(|p| p.uuid)
1483 }
1484 pub fn collider_name_to_uuid(&self, name: &ImmutableString) -> Option<Uuid> {
1486 self.colliders
1487 .iter()
1488 .find(|c| &c.name == name)
1489 .map(|c| c.uuid)
1490 }
1491 pub fn find_property_by_name(
1493 &self,
1494 property_name: &ImmutableString,
1495 ) -> Option<&TileSetPropertyLayer> {
1496 self.properties.iter().find(|p| &p.name == property_name)
1497 }
1498 pub fn find_property(&self, uuid: Uuid) -> Option<&TileSetPropertyLayer> {
1500 self.properties.iter().find(|p| p.uuid == uuid)
1501 }
1502 pub fn find_property_mut(&mut self, uuid: Uuid) -> Option<&mut TileSetPropertyLayer> {
1504 self.properties.iter_mut().find(|p| p.uuid == uuid)
1505 }
1506 pub fn find_collider(&self, uuid: Uuid) -> Option<&TileSetColliderLayer> {
1508 self.colliders.iter().find(|p| p.uuid == uuid)
1509 }
1510 pub fn find_collider_mut(&mut self, uuid: Uuid) -> Option<&mut TileSetColliderLayer> {
1512 self.colliders.iter_mut().find(|p| p.uuid == uuid)
1513 }
1514 pub fn rebuild_transform_sets(&mut self) {
1518 self.transform_map.clear();
1519 for (&position, page) in self.pages.iter_mut() {
1520 let TileSetPageSource::Transform(tiles) = &page.source else {
1521 continue;
1522 };
1523 self.transform_map.extend(
1524 tiles
1525 .iter()
1526 .filter_map(|(&k, &v)| Some((v, TileDefinitionHandle::try_new(position, k)?))),
1527 );
1528 }
1529 }
1530 pub fn rebuild_animations(&mut self) {
1540 self.animation_map.clear();
1541 for (&position, page) in self.pages.iter_mut() {
1542 let TileSetPageSource::Animation(tiles) = &page.source else {
1543 continue;
1544 };
1545 for &k in tiles.keys() {
1546 let left = Vector2::new(k.x - 1, k.y);
1547 if tiles.contains_key(&left) {
1548 continue;
1549 }
1550 let mut right = Vector2::new(k.x + 1, k.y);
1551 while tiles.contains_key(&right) {
1552 right.x += 1;
1553 }
1554 let Some(start) = TileDefinitionHandle::try_new(position, k) else {
1555 continue;
1556 };
1557 let length = right.x - k.x;
1558 self.animation_map.add_animation(start, length);
1559 }
1560 }
1561 for (index, animation) in self.animation_map.animations.iter().enumerate() {
1562 let page = self.pages.get(&animation.page()).unwrap();
1563 let TileSetPageSource::Animation(tiles) = &page.source else {
1564 unreachable!();
1565 };
1566 for (handle, offset) in animation.iter() {
1567 let handle = *tiles.get(&handle.tile()).unwrap();
1568 let anim_ref = AnimationRef { index, offset };
1569 match self.animation_map.handle_to_animation.entry(handle) {
1570 Entry::Occupied(mut entry) => {
1571 let prev = entry.get();
1572 if offset == 0 && prev.offset != 0 {
1573 entry.insert(anim_ref);
1574 } else if (offset == 0) == (prev.offset == 0) {
1575 let new_start = animation.start;
1576 let prev_start = self.animation_map.animations[prev.index].start;
1577 if new_start < prev_start {
1578 entry.insert(anim_ref);
1579 }
1580 }
1581 }
1582 Entry::Vacant(entry) => drop(entry.insert(anim_ref)),
1583 }
1584 }
1585 }
1586 }
1587 pub fn preview_texture(&self) -> Option<TextureResource> {
1589 self.pages
1590 .iter()
1591 .filter_map(|(&pos, p)| match &p.source {
1592 TileSetPageSource::Atlas(mat) => {
1593 Some((pos, mat.material.state().data()?.texture("diffuseTexture")?))
1594 }
1595 _ => None,
1596 })
1597 .min_by(|(a, _), (b, _)| a.y.cmp(&b.y).reverse().then(a.x.cmp(&b.x)))
1598 .map(|(_, texture)| texture)
1599 }
1600 pub fn swap(&mut self, update: &mut TileSetUpdate) {
1614 let mut transform_changes = false;
1615 let mut animation_changes: bool = false;
1616 for (handle, tile_update) in update.iter_mut() {
1617 let Some(page) = self.pages.get_mut(&handle.page()) else {
1618 Log::err("Tile set update page missing.");
1619 continue;
1620 };
1621 if page.is_transform_set() {
1622 transform_changes = true;
1623 }
1624 if page.is_animation() {
1625 animation_changes = true;
1626 }
1627 page.swap_tile(handle.tile(), tile_update);
1628 }
1629 if transform_changes {
1630 self.rebuild_transform_sets();
1631 }
1632 if animation_changes {
1633 self.rebuild_animations();
1634 }
1635 }
1636 pub fn get_page(&self, position: Vector2<i32>) -> Option<&TileSetPage> {
1638 self.pages.get(&position)
1639 }
1640 pub fn get_page_mut(&mut self, position: Vector2<i32>) -> Option<&mut TileSetPage> {
1642 self.pages.get_mut(&position)
1643 }
1644 pub fn insert_page(
1646 &mut self,
1647 position: Vector2<i32>,
1648 page: TileSetPage,
1649 ) -> Option<TileSetPage> {
1650 self.pages.insert(position, page)
1651 }
1652 pub fn remove_page(&mut self, position: Vector2<i32>) -> Option<TileSetPage> {
1654 self.pages.remove(&position)
1655 }
1656 pub fn is_valid_tile(&self, handle: TileDefinitionHandle) -> bool {
1658 let Some(source) = self.pages.get(&handle.page()).map(|p| &p.source) else {
1659 return false;
1660 };
1661 match source {
1662 TileSetPageSource::Atlas(mat) => mat.contains_key(&handle.tile()),
1663 TileSetPageSource::Freeform(map) => map.contains_key(&handle.tile()),
1664 TileSetPageSource::Transform(_) => false,
1665 TileSetPageSource::Animation(_) => false,
1666 }
1667 }
1668 pub fn get_abstract_tile(
1670 &self,
1671 page: Vector2<i32>,
1672 tile: Vector2<i32>,
1673 ) -> Option<AbstractTile> {
1674 let source = self.pages.get(&page).map(|p| &p.source)?;
1675 match source {
1676 TileSetPageSource::Atlas(tile_material) => tile_material.get_abstract_tile(tile),
1677 TileSetPageSource::Freeform(tile_grid_map) => {
1678 Some(AbstractTile::Freeform(tile_grid_map.get(&tile)?.clone()))
1679 }
1680 TileSetPageSource::Transform(tiles) => {
1681 Some(AbstractTile::Transform(tiles.get(&tile).copied()?))
1682 }
1683 TileSetPageSource::Animation(tiles) => {
1684 Some(AbstractTile::Transform(tiles.get(&tile).copied()?))
1685 }
1686 }
1687 }
1688 pub fn set_abstract_tile(
1690 &mut self,
1691 page: Vector2<i32>,
1692 tile: Vector2<i32>,
1693 value: Option<AbstractTile>,
1694 ) -> Option<AbstractTile> {
1695 let Some(source) = self.pages.get_mut(&page).map(|p| &mut p.source) else {
1696 panic!();
1697 };
1698 use AbstractTile as Tile;
1699 use TileSetPageSource as Source;
1700 match (source, value) {
1701 (Source::Atlas(d0), value) => d0.set_abstract_tile(tile, value),
1702 (Source::Freeform(d0), Some(Tile::Freeform(d1))) => {
1703 Some(Tile::Freeform(d0.insert(tile, d1)?))
1704 }
1705 (Source::Freeform(d0), None) => Some(Tile::Freeform(d0.remove(&tile)?)),
1706 (Source::Transform(d0), Some(Tile::Transform(d1))) => {
1707 d0.insert(tile, d1).map(Tile::Transform)
1708 }
1709 (Source::Transform(d0), None) => d0.remove(&tile).map(Tile::Transform),
1710 (Source::Animation(d0), Some(Tile::Transform(d1))) => {
1711 d0.insert(tile, d1).map(Tile::Transform)
1712 }
1713 (Source::Animation(d0), None) => d0.remove(&tile).map(Tile::Transform),
1714 _ => panic!(),
1715 }
1716 }
1717 pub fn get_transformed_render_data(
1719 &self,
1720 trans: OrthoTransformation,
1721 handle: TileDefinitionHandle,
1722 ) -> Option<TileRenderData> {
1723 if handle.is_empty() {
1724 Some(TileRenderData::empty())
1725 } else if let Some(handle) = self.get_transformed_version(trans, handle) {
1726 self.get_tile_render_data(handle.into())
1727 } else {
1728 Some(self.get_tile_render_data(handle.into())?.transformed(trans))
1729 }
1730 }
1731 pub fn get_tile_render_data(&self, position: ResourceTilePosition) -> Option<TileRenderData> {
1738 if self.is_free_at(position) {
1739 return None;
1740 }
1741 self.inner_get_render_data(position)
1742 .or_else(|| Some(TileRenderData::missing_data()))
1743 }
1744 fn inner_get_render_data(&self, position: ResourceTilePosition) -> Option<TileRenderData> {
1745 let handle = self.redirect_handle(position)?;
1746 if handle.is_empty() {
1747 Some(TileRenderData::empty())
1748 } else if self.is_valid_tile(handle) {
1749 Some(TileRenderData::new(
1750 Some(self.get_tile_bounds(position)?),
1751 self.get_tile_data(position)?.color,
1752 ))
1753 } else {
1754 Some(TileRenderData::missing_data())
1755 }
1756 }
1757 pub fn get_tile_collider(
1759 &self,
1760 handle: TileDefinitionHandle,
1761 uuid: Uuid,
1762 ) -> Option<&TileCollider> {
1763 if self.is_free_at(handle.into()) {
1764 return None;
1765 }
1766 let handle = self.redirect_handle(handle.into())?;
1767 let data = self.get_tile_data(handle.into())?;
1768 data.colliders.get(&uuid)
1769 }
1770 pub fn palette_iterator(
1772 &self,
1773 stage: TilePaletteStage,
1774 page: Vector2<i32>,
1775 ) -> TileSetPaletteIterator {
1776 TileSetPaletteIterator {
1777 page,
1778 keys: self.palette_keys(stage, page),
1779 }
1780 }
1781 fn palette_keys(&self, stage: TilePaletteStage, page: Vector2<i32>) -> PaletteIterator {
1782 match stage {
1783 TilePaletteStage::Pages => PaletteIterator::Pages(self.pages.keys()),
1784 TilePaletteStage::Tiles => {
1785 let Some(page) = self.pages.get(&page) else {
1786 return PaletteIterator::Empty;
1787 };
1788 match &page.source {
1789 TileSetPageSource::Atlas(mat) => PaletteIterator::Material(mat.tiles.keys()),
1790 TileSetPageSource::Freeform(map) => PaletteIterator::Freeform(map.keys()),
1791 TileSetPageSource::Transform(tiles) => {
1792 PaletteIterator::TransformSet(tiles.keys())
1793 }
1794 TileSetPageSource::Animation(tiles) => {
1795 PaletteIterator::TransformSet(tiles.keys())
1796 }
1797 }
1798 }
1799 }
1800 }
1801
1802 pub fn palette_render_loop<F>(&self, stage: TilePaletteStage, page: Vector2<i32>, mut func: F)
1805 where
1806 F: FnMut(Vector2<i32>, TileRenderData),
1807 {
1808 for position in self.palette_iterator(stage, page) {
1809 if let Some(data) = self.get_tile_render_data(position) {
1810 func(position.stage_position(), data);
1811 }
1812 }
1813 }
1814 pub fn tile_collider_loop<F>(&self, page: Vector2<i32>, mut func: F)
1817 where
1818 F: FnMut(Vector2<i32>, Uuid, Color, &TileCollider),
1819 {
1820 for layer in self.colliders.iter() {
1821 for position in self.palette_iterator(TilePaletteStage::Tiles, page) {
1822 if let Some(tile_collider) =
1823 self.get_tile_collider(position.handle().unwrap(), layer.uuid)
1824 {
1825 if !tile_collider.is_none() {
1826 func(
1827 position.stage_position(),
1828 layer.uuid,
1829 layer.color,
1830 tile_collider,
1831 );
1832 }
1833 }
1834 }
1835 }
1836 }
1837
1838 pub fn redirect_handle(&self, position: ResourceTilePosition) -> Option<TileDefinitionHandle> {
1845 match position.stage() {
1846 TilePaletteStage::Tiles => match &self.pages.get(&position.page())?.source {
1847 TileSetPageSource::Transform(tiles) => {
1848 tiles.get(&position.stage_position()).copied()
1849 }
1850 TileSetPageSource::Animation(tiles) => {
1851 tiles.get(&position.stage_position()).copied()
1852 }
1853 page => {
1854 if page.contains_tile_at(position.stage_position()) {
1855 position.handle()
1856 } else {
1857 None
1858 }
1859 }
1860 },
1861 TilePaletteStage::Pages => self
1862 .pages
1863 .get(&position.stage_position())
1864 .and_then(|p| self.redirect_handle(ResourceTilePosition::from(p.icon))),
1865 }
1866 }
1867 pub fn get_transform_tile_source(
1870 &self,
1871 handle: TileDefinitionHandle,
1872 ) -> Option<TileDefinitionHandle> {
1873 match &self.pages.get(&handle.page())?.source {
1874 TileSetPageSource::Transform(tiles) => tiles.get(&handle.tile()).copied(),
1875 _ => None,
1876 }
1877 }
1878 pub fn get_definition(&self, handle: TileDefinitionHandle) -> Option<TileDefinition> {
1881 Some(TileDefinition {
1882 material_bounds: self.get_tile_bounds(handle.into())?,
1883 data: self.get_tile_data(handle.into()).cloned()?,
1884 })
1885 }
1886 pub fn get_transformed_definition(
1888 &self,
1889 trans: OrthoTransformation,
1890 handle: TileDefinitionHandle,
1891 ) -> Option<TileDefinition> {
1892 if let Some(handle) = self.get_transformed_version(trans, handle) {
1893 self.get_definition(handle)
1894 } else {
1895 Some(self.get_definition(handle)?.transformed(trans))
1896 }
1897 }
1898 pub fn get_tile_bounds(&self, position: ResourceTilePosition) -> Option<TileMaterialBounds> {
1900 let handle = self.redirect_handle(position)?;
1901 match &self.pages.get(&handle.page())?.source {
1902 TileSetPageSource::Atlas(mat) => mat.get_tile_bounds(handle.tile()),
1903 TileSetPageSource::Freeform(map) => {
1904 Some(map.get(&handle.tile())?.material_bounds.clone())
1905 }
1906 TileSetPageSource::Transform(_) => None,
1907 TileSetPageSource::Animation(_) => None,
1908 }
1909 }
1910 pub fn property_value(
1912 &self,
1913 handle: TileDefinitionHandle,
1914 property_id: Uuid,
1915 ) -> Option<TileSetPropertyValue> {
1916 self.get_tile_data(handle.into()).and_then(|d| {
1917 d.properties.get(&property_id).cloned().or_else(|| {
1918 self.find_property(property_id)
1919 .map(|p| p.prop_type.default_value())
1920 })
1921 })
1922 }
1923 pub fn get_tile_data(&self, position: ResourceTilePosition) -> Option<&TileData> {
1925 let handle = self.redirect_handle(position)?;
1926 match &self.pages.get(&handle.page())?.source {
1927 TileSetPageSource::Atlas(mat) => mat.get_tile_data(handle.tile()),
1928 TileSetPageSource::Freeform(map) => Some(&map.get(&handle.tile())?.data),
1929 TileSetPageSource::Transform(_) => None,
1930 TileSetPageSource::Animation(_) => None,
1931 }
1932 }
1933 pub fn get_tile_data_mut(&mut self, handle: TileDefinitionHandle) -> Option<&mut TileData> {
1935 match &mut self.pages.get_mut(&handle.page())?.source {
1936 TileSetPageSource::Atlas(mat) => mat.get_tile_data_mut(handle.tile()),
1937 TileSetPageSource::Freeform(map) => Some(&mut map.get_mut(&handle.tile())?.data),
1938 TileSetPageSource::Transform(_) => None,
1939 TileSetPageSource::Animation(_) => None,
1940 }
1941 }
1942
1943 pub fn get_animated_version(
1946 &self,
1947 time: f32,
1948 handle: TileDefinitionHandle,
1949 ) -> Option<TileDefinitionHandle> {
1950 let (animation, offset) = self.animation_map.get_animation_and_offset(handle)?;
1951 let page = self.get_page(animation.page())?;
1952 let TileSetPageSource::Animation(AnimationTiles { frame_rate, tiles }) = &page.source
1953 else {
1954 return None;
1955 };
1956 let frame_rate = *frame_rate;
1957 let length = animation.length;
1958 let frame_position = (time * frame_rate).rem_euclid(length as f32);
1959 let frame_index = (frame_position.floor() as i32).clamp(0, length - 1);
1960 let frame_index = (frame_index + offset).rem_euclid(length);
1961 let frame = animation.frame(frame_index);
1962 tiles.get(&frame).copied()
1963 }
1964
1965 pub fn get_transformed_version(
1971 &self,
1972 transform: OrthoTransformation,
1973 handle: TileDefinitionHandle,
1974 ) -> Option<TileDefinitionHandle> {
1975 if transform.is_identity() {
1976 return Some(handle);
1977 }
1978 let transform_tile = self.transform_map.get(&handle)?;
1979 let page = self.get_page(transform_tile.page())?;
1980 let tiles = match &page.source {
1981 TileSetPageSource::Transform(TransformSetTiles(tiles)) => Some(tiles),
1982 _ => None,
1983 }?;
1984 let cell = TransformSetCell::from_position(transform_tile.tile())
1985 .transformed(transform)
1986 .into_position();
1987 tiles.get(&cell).copied()
1988 }
1989
1990 pub fn get_tiles<I: Iterator<Item = Vector2<i32>>>(
1992 &self,
1993 stage: TilePaletteStage,
1994 page: Vector2<i32>,
1995 iter: I,
1996 tiles: &mut Tiles,
1997 ) {
1998 for pos in iter {
1999 if let Some(tile) = self.redirect_handle(ResourceTilePosition::new(stage, page, pos)) {
2000 tiles.insert(pos, tile);
2001 }
2002 }
2003 }
2004 pub fn pages_bounds(&self) -> OptionTileRect {
2006 let mut result = OptionTileRect::default();
2007 for pos in self.pages.keys() {
2008 result.push(*pos);
2009 }
2010 result
2011 }
2012 pub fn tiles_bounds(&self, stage: TilePaletteStage, page: Vector2<i32>) -> OptionTileRect {
2014 match stage {
2015 TilePaletteStage::Tiles => {
2016 let Some(page) = self.pages.get(&page) else {
2017 return OptionTileRect::default();
2018 };
2019 page.get_bounds()
2020 }
2021 TilePaletteStage::Pages => self.pages_bounds(),
2022 }
2023 }
2024
2025 pub async fn from_file(
2027 path: &Path,
2028 resource_manager: ResourceManager,
2029 io: &dyn ResourceIo,
2030 ) -> Result<Self, TileSetResourceError> {
2031 let bytes = io.load_file(path).await?;
2032 let mut visitor = Visitor::load_from_memory(&bytes)?;
2033 visitor.blackboard.register(Arc::new(resource_manager));
2034 let mut tile_set = TileSet::default();
2035 tile_set.visit("TileSet", &mut visitor)?;
2036 Ok(tile_set)
2037 }
2038
2039 pub fn is_free_at(&self, position: ResourceTilePosition) -> bool {
2041 match position.stage() {
2042 TilePaletteStage::Pages => self.get_page(position.stage_position()).is_none(),
2043 TilePaletteStage::Tiles => {
2044 self.get_page(position.page()).is_some()
2045 && !self.has_tile_at(position.page(), position.stage_position())
2046 }
2047 }
2048 }
2049
2050 pub fn find_free_location(
2053 &self,
2054 stage: TilePaletteStage,
2055 page: Vector2<i32>,
2056 position: Vector2<i32>,
2057 ) -> Vector2<i32> {
2058 let mut visited = FxHashSet::default();
2059 let mut stack = vec![position];
2060 while let Some(pos) = stack.pop() {
2061 if visited.contains(&pos) {
2062 continue;
2063 }
2064 visited.insert(pos);
2065 if self.is_free_at(ResourceTilePosition::new(stage, page, pos)) {
2066 return pos;
2067 } else {
2068 stack.extend_from_slice(&[
2069 Vector2::new(pos.x + 1, pos.y),
2070 Vector2::new(pos.x + 1, pos.y + 1),
2071 Vector2::new(pos.x, pos.y + 1),
2072 Vector2::new(pos.x - 1, pos.y + 1),
2073 Vector2::new(pos.x - 1, pos.y),
2074 Vector2::new(pos.x - 1, pos.y - 1),
2075 Vector2::new(pos.x, pos.y - 1),
2076 Vector2::new(pos.x + 1, pos.y - 1),
2077 ]);
2078 }
2079 }
2080 Default::default()
2081 }
2082 pub fn swap_all_values_for_property(
2085 &mut self,
2086 property_id: Uuid,
2087 values: &mut FxHashMap<TileDefinitionHandle, TileSetPropertyValue>,
2088 ) {
2089 for (page_pos, page) in self.pages.iter_mut() {
2090 page.swap_all_values_for_property(*page_pos, property_id, values);
2091 }
2092 }
2093 pub fn swap_all_values_for_collider(
2096 &mut self,
2097 collider_id: Uuid,
2098 values: &mut FxHashMap<TileDefinitionHandle, TileCollider>,
2099 ) {
2100 for (page_pos, page) in self.pages.iter_mut() {
2101 page.swap_all_values_for_collider(*page_pos, collider_id, values);
2102 }
2103 }
2104}
2105
2106impl ResourceData for TileSet {
2107 fn type_uuid(&self) -> Uuid {
2108 <Self as TypeUuidProvider>::type_uuid()
2109 }
2110
2111 fn save(&mut self, path: &Path) -> Result<(), Box<dyn Error>> {
2112 let mut visitor = Visitor::new();
2113 self.visit("TileSet", &mut visitor)?;
2114 visitor.save_ascii_to_file(path)?;
2115 Ok(())
2116 }
2117
2118 fn can_be_saved(&self) -> bool {
2119 true
2120 }
2121
2122 fn try_clone_box(&self) -> Option<Box<dyn ResourceData>> {
2123 Some(Box::new(self.clone()))
2124 }
2125}
2126
2127pub type TileSetResource = Resource<TileSet>;
2129
2130pub struct TileSetLoader {
2132 pub resource_manager: ResourceManager,
2134}
2135
2136impl ResourceLoader for TileSetLoader {
2137 fn extensions(&self) -> &[&str] {
2138 &["tileset"]
2139 }
2140
2141 fn is_native_extension(&self, ext: &str) -> bool {
2142 fyrox_core::cmp_strings_case_insensitive(ext, "tileset")
2143 }
2144
2145 fn data_type_uuid(&self) -> Uuid {
2146 <TileSet as TypeUuidProvider>::type_uuid()
2147 }
2148
2149 fn load(&self, path: PathBuf, io: Arc<dyn ResourceIo>) -> BoxedLoaderFuture {
2150 let resource_manager = self.resource_manager.clone();
2151 Box::pin(async move {
2152 let mut tile_set = TileSet::from_file(&path, resource_manager, io.as_ref())
2153 .await
2154 .map_err(LoadError::new)?;
2155 tile_set.rebuild_transform_sets();
2156 tile_set.rebuild_animations();
2157 Ok(LoaderPayload::new(tile_set))
2158 })
2159 }
2160}