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::FileLoadError, 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::{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(FileLoadError),
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<FileLoadError> for TileSetResourceError {
94 fn from(e: FileLoadError) -> 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(ResourceKind::Embedded, Material::standard_tile()),
151 bounds: Default::default(),
152 }
153 }
154}
155
156impl OrthoTransform for TileMaterialBounds {
157 fn x_flipped(mut self) -> Self {
158 self.bounds = self.bounds.x_flipped();
159 self
160 }
161
162 fn rotated(mut self, amount: i8) -> Self {
163 self.bounds = self.bounds.rotated(amount);
164 self
165 }
166}
167
168#[derive(Clone, Default, PartialEq, Debug, Reflect, Visit)]
170#[visit(optional)]
171pub struct TileData {
172 pub colliders: FxHashMap<Uuid, TileCollider>,
174 pub color: Color,
176 pub properties: FxHashMap<Uuid, TileSetPropertyValue>,
180}
181
182impl OrthoTransform for TileData {
183 fn x_flipped(mut self) -> Self {
184 for (_, value) in self.properties.iter_mut() {
185 *value = value.clone().x_flipped();
186 }
187 for (_, value) in self.colliders.iter_mut() {
188 *value = value.clone().x_flipped();
189 }
190 self
191 }
192
193 fn rotated(mut self, amount: i8) -> Self {
194 for (_, value) in self.properties.iter_mut() {
195 *value = value.clone().rotated(amount);
196 }
197 for (_, value) in self.colliders.iter_mut() {
198 *value = value.clone().rotated(amount);
199 }
200 self
201 }
202}
203
204#[derive(Clone, Default, PartialEq, Debug, Reflect, Visit)]
209pub struct TileBounds {
210 pub left_top_corner: Vector2<u32>,
212 pub right_top_corner: Vector2<u32>,
214 pub right_bottom_corner: Vector2<u32>,
216 pub left_bottom_corner: Vector2<u32>,
218}
219
220fn pixel_coords_to_uv(position: Vector2<u32>, total_size: Vector2<u32>) -> Vector2<f32> {
221 Vector2::new(
222 position.x as f32 / total_size.x as f32,
223 position.y as f32 / total_size.y as f32,
224 )
225}
226
227impl TileBounds {
228 pub fn left_top_uv(&self, texture_size: Vector2<u32>) -> Vector2<f32> {
230 pixel_coords_to_uv(self.left_top_corner, texture_size)
231 }
232 pub fn right_top_uv(&self, texture_size: Vector2<u32>) -> Vector2<f32> {
234 pixel_coords_to_uv(self.right_top_corner, texture_size)
235 }
236 pub fn left_bottom_uv(&self, texture_size: Vector2<u32>) -> Vector2<f32> {
238 pixel_coords_to_uv(self.left_bottom_corner, texture_size)
239 }
240 pub fn right_bottom_uv(&self, texture_size: Vector2<u32>) -> Vector2<f32> {
242 pixel_coords_to_uv(self.right_bottom_corner, texture_size)
243 }
244 pub fn get(&self, index: usize) -> Vector2<u32> {
247 match index {
248 0 => self.left_bottom_corner,
249 1 => self.right_bottom_corner,
250 2 => self.right_top_corner,
251 3 => self.left_top_corner,
252 _ => panic!(),
253 }
254 }
255 pub fn get_mut(&mut self, index: usize) -> &mut Vector2<u32> {
258 match index {
259 0 => &mut self.left_bottom_corner,
260 1 => &mut self.right_bottom_corner,
261 2 => &mut self.right_top_corner,
262 3 => &mut self.left_top_corner,
263 _ => panic!(),
264 }
265 }
266}
267
268impl OrthoTransform for TileBounds {
269 fn x_flipped(mut self) -> Self {
270 std::mem::swap(&mut self.left_top_corner, &mut self.right_top_corner);
271 std::mem::swap(&mut self.left_bottom_corner, &mut self.right_bottom_corner);
272 self
273 }
274
275 fn rotated(mut self, amount: i8) -> Self {
276 let old = self.clone();
277 let amount = amount.rem_euclid(4) as usize;
278 for i in 0..4 {
279 *self.get_mut((i + amount).rem_euclid(4)) = old.get(i);
280 }
281 self
282 }
283}
284
285#[derive(Clone, Default, Debug, Visit, Reflect)]
288pub struct TileSetPage {
289 pub icon: TileDefinitionHandle,
291 pub source: TileSetPageSource,
293}
294
295impl TileSetPage {
296 pub fn is_material(&self) -> bool {
298 matches!(self.source, TileSetPageSource::Atlas(_))
299 }
300 pub fn is_freeform(&self) -> bool {
302 matches!(self.source, TileSetPageSource::Freeform(_))
303 }
304 pub fn is_transform_set(&self) -> bool {
306 matches!(self.source, TileSetPageSource::Transform(_))
307 }
308 pub fn is_animation(&self) -> bool {
310 matches!(self.source, TileSetPageSource::Animation(_))
311 }
312 pub fn page_type(&self) -> PageType {
314 self.source.page_type()
315 }
316 pub fn has_tile_at(&self, position: Vector2<i32>) -> bool {
318 match &self.source {
319 TileSetPageSource::Atlas(mat) => mat.tiles.contains_key(&position),
320 TileSetPageSource::Freeform(map) => map.contains_key(&position),
321 TileSetPageSource::Transform(tiles) => tiles.contains_key(&position),
322 TileSetPageSource::Animation(tiles) => tiles.contains_key(&position),
323 }
324 }
325 pub fn keys(&self) -> Vec<Vector2<i32>> {
327 match &self.source {
328 TileSetPageSource::Atlas(mat) => mat.tiles.keys().copied().collect(),
329 TileSetPageSource::Freeform(map) => map.keys().copied().collect(),
330 TileSetPageSource::Transform(tiles) => tiles.keys().copied().collect(),
331 TileSetPageSource::Animation(tiles) => tiles.keys().copied().collect(),
332 }
333 }
334 pub fn get_bounds(&self) -> OptionTileRect {
336 let mut result = OptionTileRect::default();
337 match &self.source {
338 TileSetPageSource::Atlas(mat) => {
339 for pos in mat.tiles.keys() {
340 result.push(*pos);
341 }
342 }
343 TileSetPageSource::Freeform(map) => {
344 for pos in map.keys() {
345 result.push(*pos);
346 }
347 }
348 TileSetPageSource::Transform(tiles) => {
349 for pos in tiles.keys() {
350 result.push(*pos);
351 }
352 }
353 TileSetPageSource::Animation(tiles) => {
354 for pos in tiles.keys() {
355 result.push(*pos);
356 }
357 }
358 }
359 result
360 }
361 pub fn swap_tile(&mut self, position: Vector2<i32>, update: &mut TileDataUpdate) {
364 match &mut self.source {
365 TileSetPageSource::Atlas(map0) => swap_material_tile(map0, position, update),
366 TileSetPageSource::Freeform(map0) => swap_freeform_tile(map0, position, update),
367 TileSetPageSource::Transform(map0) => swap_transform_tile(map0, position, update),
368 TileSetPageSource::Animation(map0) => swap_animation_tile(map0, position, update),
369 }
370 }
371
372 pub fn swap_all_values_for_property(
375 &mut self,
376 page: Vector2<i32>,
377 property_id: Uuid,
378 values: &mut FxHashMap<TileDefinitionHandle, TileSetPropertyValue>,
379 ) {
380 match &mut self.source {
381 TileSetPageSource::Atlas(map0) => {
382 let tiles = &mut map0.tiles;
383 for (tile, data) in tiles.iter_mut() {
384 let Some(handle) = TileDefinitionHandle::try_new(page, *tile) else {
385 continue;
386 };
387 swap_hash_map_entries(data.properties.entry(property_id), values.entry(handle));
388 }
389 }
390 TileSetPageSource::Freeform(tiles) => {
391 for (tile, tile_def) in tiles.iter_mut() {
392 let Some(handle) = TileDefinitionHandle::try_new(page, *tile) else {
393 continue;
394 };
395 swap_hash_map_entries(
396 tile_def.data.properties.entry(property_id),
397 values.entry(handle),
398 );
399 }
400 }
401 TileSetPageSource::Transform(_) => (),
402 TileSetPageSource::Animation(_) => (),
403 }
404 }
405 pub fn swap_all_values_for_collider(
408 &mut self,
409 page: Vector2<i32>,
410 collider_id: Uuid,
411 values: &mut FxHashMap<TileDefinitionHandle, TileCollider>,
412 ) {
413 match &mut self.source {
414 TileSetPageSource::Atlas(map0) => {
415 let tiles = &mut map0.tiles;
416 for (tile, data) in tiles.iter_mut() {
417 let Some(handle) = TileDefinitionHandle::try_new(page, *tile) else {
418 continue;
419 };
420 swap_hash_map_entries(data.colliders.entry(collider_id), values.entry(handle));
421 }
422 }
423 TileSetPageSource::Freeform(tiles) => {
424 for (tile, tile_def) in tiles.iter_mut() {
425 let Some(handle) = TileDefinitionHandle::try_new(page, *tile) else {
426 continue;
427 };
428 swap_hash_map_entries(
429 tile_def.data.colliders.entry(collider_id),
430 values.entry(handle),
431 );
432 }
433 }
434 _ => panic!(),
435 }
436 }
437}
438
439fn swap_material_tile(
440 map0: &mut TileMaterial,
441 position: Vector2<i32>,
442 update: &mut TileDataUpdate,
443) {
444 let e0 = map0.tiles.entry(position);
445 match (e0, update) {
446 (Entry::Occupied(d0), d1 @ TileDataUpdate::Erase) => {
447 *d1 = TileDataUpdate::MaterialTile(d0.remove())
448 }
449 (Entry::Vacant(d0), d1 @ TileDataUpdate::MaterialTile(_)) => {
450 d0.insert(d1.take_data());
451 }
452 (Entry::Vacant(_), TileDataUpdate::Erase) => (),
453 (Entry::Occupied(mut d0), d1) => d1.swap_with_data(d0.get_mut()),
454 (Entry::Vacant(d0), d1) => {
455 let mut data = TileData::default();
456 d1.swap_with_data(&mut data);
457 let _ = d0.insert(data);
458 *d1 = TileDataUpdate::Erase;
459 }
460 }
461}
462fn swap_freeform_tile(
463 map0: &mut TileGridMap<TileDefinition>,
464 position: Vector2<i32>,
465 update: &mut TileDataUpdate,
466) {
467 let e0 = map0.entry(position);
468 match (e0, update) {
469 (Entry::Occupied(mut d0), TileDataUpdate::Material(d1)) => {
470 std::mem::swap(&mut d0.get_mut().material_bounds, d1)
471 }
472 (Entry::Vacant(d0), d1 @ TileDataUpdate::Material(_)) => {
473 let TileDataUpdate::Material(material_bounds) = std::mem::take(d1) else {
474 unreachable!();
475 };
476 let def = TileDefinition::from_material_bounds(material_bounds);
477 let _ = d0.insert(def);
478 }
479 (Entry::Occupied(mut d0), TileDataUpdate::FreeformTile(d1)) => {
480 std::mem::swap(d0.get_mut(), d1);
481 }
482 (Entry::Occupied(d0), d1 @ TileDataUpdate::Erase) => {
483 *d1 = TileDataUpdate::FreeformTile(d0.remove())
484 }
485 (Entry::Vacant(d0), d1 @ TileDataUpdate::FreeformTile(_)) => {
486 d0.insert(d1.take_definition());
487 }
488 (Entry::Vacant(_), TileDataUpdate::Erase) => (),
489 (Entry::Occupied(mut d0), d1) => d1.swap_with_data(&mut d0.get_mut().data),
490 (Entry::Vacant(_), d1) => {
491 *d1 = TileDataUpdate::Erase;
493 }
494 }
495}
496
497fn swap_transform_tile(
498 map0: &mut TransformSetTiles,
499 position: Vector2<i32>,
500 update: &mut TileDataUpdate,
501) {
502 let e0 = map0.entry(position);
503 let TileDataUpdate::TransformSet(handle) = update else {
504 panic!()
505 };
506 swap_hash_map_entry(e0, handle);
507}
508
509fn swap_animation_tile(
510 map0: &mut AnimationTiles,
511 position: Vector2<i32>,
512 update: &mut TileDataUpdate,
513) {
514 let e0 = map0.entry(position);
515 let TileDataUpdate::TransformSet(handle) = update else {
516 panic!()
517 };
518 swap_hash_map_entry(e0, handle);
519}
520
521#[derive(Debug, Clone)]
524pub enum AbstractTile {
525 Atlas(TileData),
528 Freeform(TileDefinition),
532 Transform(TileDefinitionHandle),
537}
538
539#[derive(Clone, PartialEq, Debug, Visit, Reflect)]
541pub enum TileSetPageSource {
542 Atlas(TileMaterial),
547 Freeform(TileGridMap<TileDefinition>),
550 Transform(TransformSetTiles),
556 Animation(AnimationTiles),
562}
563
564impl Default for TileSetPageSource {
565 fn default() -> Self {
566 Self::Atlas(TileMaterial::default())
567 }
568}
569
570impl TileSetPageSource {
571 pub fn new_material() -> Self {
573 Self::Atlas(TileMaterial::default())
574 }
575 pub fn new_free() -> Self {
577 Self::Freeform(TileGridMap::default())
578 }
579 pub fn new_transform() -> Self {
581 Self::Transform(TransformSetTiles::default())
582 }
583 pub fn new_animation() -> Self {
585 Self::Animation(AnimationTiles::default())
586 }
587 pub fn page_type(&self) -> PageType {
589 match self {
590 TileSetPageSource::Atlas(_) => PageType::Atlas,
591 TileSetPageSource::Freeform(_) => PageType::Freeform,
592 TileSetPageSource::Transform(_) => PageType::Transform,
593 TileSetPageSource::Animation(_) => PageType::Animation,
594 }
595 }
596 pub fn contains_tile_at(&self, position: Vector2<i32>) -> bool {
598 match self {
599 TileSetPageSource::Atlas(map) => map.contains_key(&position),
600 TileSetPageSource::Freeform(map) => map.contains_key(&position),
601 TileSetPageSource::Transform(map) => map.contains_key(&position),
602 TileSetPageSource::Animation(map) => map.contains_key(&position),
603 }
604 }
605}
606
607#[derive(Default, Clone, PartialEq, Debug, Reflect, Visit)]
616pub struct TransformSetTiles(
617 #[reflect(hidden)]
618 #[visit(optional)]
619 pub Tiles,
620);
621
622impl Deref for TransformSetTiles {
623 type Target = Tiles;
624
625 fn deref(&self) -> &Self::Target {
626 &self.0
627 }
628}
629
630impl DerefMut for TransformSetTiles {
631 fn deref_mut(&mut self) -> &mut Self::Target {
632 &mut self.0
633 }
634}
635
636#[derive(Clone, PartialEq, Debug, Reflect, Visit)]
642pub struct AnimationTiles {
643 pub frame_rate: f32,
645 #[reflect(hidden)]
647 #[visit(optional)]
648 pub tiles: Tiles,
649}
650
651impl Default for AnimationTiles {
652 fn default() -> Self {
653 Self {
654 frame_rate: DEFAULT_ANIMATION_FRAME_RATE,
655 tiles: Default::default(),
656 }
657 }
658}
659
660impl Deref for AnimationTiles {
661 type Target = Tiles;
662
663 fn deref(&self) -> &Self::Target {
664 &self.tiles
665 }
666}
667
668impl DerefMut for AnimationTiles {
669 fn deref_mut(&mut self) -> &mut Self::Target {
670 &mut self.tiles
671 }
672}
673
674#[derive(Clone, PartialEq, Debug, Visit, Reflect)]
677pub struct TileMaterial {
678 pub material: MaterialResource,
680 pub tile_size: Vector2<u32>,
682 pub tiles: TileGridMap<TileData>,
684}
685
686impl Deref for TileMaterial {
687 type Target = TileGridMap<TileData>;
688
689 fn deref(&self) -> &Self::Target {
690 &self.tiles
691 }
692}
693
694impl DerefMut for TileMaterial {
695 fn deref_mut(&mut self) -> &mut Self::Target {
696 &mut self.tiles
697 }
698}
699
700impl Default for TileMaterial {
701 fn default() -> Self {
702 Self {
703 material: DEFAULT_TILE_MATERIAL.deep_copy_as_embedded(),
704 tile_size: DEFAULT_TILE_SIZE,
705 tiles: TileGridMap::default(),
706 }
707 }
708}
709
710impl TileMaterial {
711 fn get_tile_bounds(&self, position: Vector2<i32>) -> Option<TileMaterialBounds> {
712 let origin = Vector2::new(
713 u32::try_from(position.x).ok()? * self.tile_size.x,
714 u32::try_from(-1 - position.y).ok()? * self.tile_size.y,
715 );
716 Some(TileMaterialBounds {
717 material: self.material.clone(),
718 bounds: TileBounds {
719 left_top_corner: origin,
720 right_top_corner: origin + Vector2::new(self.tile_size.x, 0),
721 left_bottom_corner: origin + Vector2::new(0, self.tile_size.y),
722 right_bottom_corner: origin + self.tile_size,
723 },
724 })
725 }
726 fn get_abstract_tile(&self, position: Vector2<i32>) -> Option<AbstractTile> {
727 Some(AbstractTile::Atlas(self.tiles.get(&position)?.clone()))
728 }
729 fn set_abstract_tile(
730 &mut self,
731 position: Vector2<i32>,
732 tile: Option<AbstractTile>,
733 ) -> Option<AbstractTile> {
734 if let Some(tile) = tile {
735 let AbstractTile::Atlas(data) = tile else {
736 panic!();
737 };
738 self.tiles.insert(position, data).map(AbstractTile::Atlas)
739 } else {
740 self.tiles.remove(&position).map(AbstractTile::Atlas)
741 }
742 }
743 fn get_tile_data(&self, position: Vector2<i32>) -> Option<&TileData> {
744 self.tiles.get(&position)
745 }
746 fn get_tile_data_mut(&mut self, position: Vector2<i32>) -> Option<&mut TileData> {
747 self.tiles.get_mut(&position)
748 }
749}
750
751pub struct TileSetPaletteIterator<'a> {
753 keys: PaletteIterator<'a>,
754 page: Vector2<i32>,
755}
756
757enum PaletteIterator<'a> {
758 Empty,
759 Material(Keys<'a, Vector2<i32>, TileData>),
760 Freeform(Keys<'a, Vector2<i32>, TileDefinition>),
761 TransformSet(Keys<'a, Vector2<i32>, TileDefinitionHandle>),
762 Pages(Keys<'a, Vector2<i32>, TileSetPage>),
763}
764
765impl Iterator for TileSetPaletteIterator<'_> {
766 type Item = ResourceTilePosition;
767 fn next(&mut self) -> Option<Self::Item> {
768 match &mut self.keys {
769 PaletteIterator::Empty => None,
770 PaletteIterator::Material(iter) => iter
771 .next()
772 .map(|t| ResourceTilePosition::Tile(self.page, *t)),
773 PaletteIterator::Freeform(iter) => iter
774 .next()
775 .map(|t| ResourceTilePosition::Tile(self.page, *t)),
776 PaletteIterator::TransformSet(iter) => iter
777 .next()
778 .map(|t| ResourceTilePosition::Tile(self.page, *t)),
779 PaletteIterator::Pages(iter) => iter.next().copied().map(ResourceTilePosition::Page),
780 }
781 }
782}
783
784pub struct TileSetRef<'a>(ResourceDataRef<'a, TileSet>);
787pub struct OptionTileSet<'a>(Option<&'a mut TileSet>);
790
791impl<'a> From<ResourceDataRef<'a, TileSet>> for TileSetRef<'a> {
792 fn from(value: ResourceDataRef<'a, TileSet>) -> Self {
793 Self(value)
794 }
795}
796
797impl<'a> TileSetRef<'a> {
798 pub fn new(tile_set: &'a TileSetResource) -> Self {
801 tile_set.data_ref().into()
802 }
803 #[inline]
805 pub fn as_loaded(&mut self) -> OptionTileSet {
806 OptionTileSet(self.0.as_loaded_mut())
807 }
808}
809
810impl<'a> OptionTileSet<'a> {
811 pub fn as_ref(&'a self) -> Option<&'a TileSet> {
813 self.0.as_deref()
814 }
815 pub fn properties(&self) -> &[TileSetPropertyLayer] {
817 self.as_ref()
818 .map(|t| t.properties.deref())
819 .unwrap_or_default()
820 }
821 pub fn colliders(&self) -> &[TileSetColliderLayer] {
823 self.as_ref()
824 .map(|t| t.colliders.deref())
825 .unwrap_or_default()
826 }
827 pub fn collider_color(&self, uuid: Uuid) -> Option<Color> {
829 self.as_ref()
830 .map(|t| t.collider_color(uuid))
831 .unwrap_or_default()
832 }
833 pub fn tile_collider(&self, handle: TileDefinitionHandle, uuid: Uuid) -> &TileCollider {
835 self.as_ref()
836 .map(|t| t.tile_collider(handle, uuid))
837 .unwrap_or_default()
838 }
839 pub fn tile_color(&self, handle: TileDefinitionHandle) -> Option<Color> {
841 self.as_ref()
842 .map(|t| t.tile_color(handle))
843 .unwrap_or_default()
844 }
845 pub fn tile_data(&self, handle: TileDefinitionHandle) -> Option<&TileData> {
847 self.as_ref()
848 .map(|t| t.tile_data(handle))
849 .unwrap_or_default()
850 }
851 pub fn tile_bounds(&self, handle: TileDefinitionHandle) -> Option<&TileMaterialBounds> {
853 self.as_ref()
854 .map(|t| t.tile_bounds(handle))
855 .unwrap_or_default()
856 }
857 pub fn tile_redirect(&self, handle: TileDefinitionHandle) -> Option<TileDefinitionHandle> {
860 self.as_ref()
861 .map(|t| t.tile_redirect(handle))
862 .unwrap_or_default()
863 }
864 pub fn keys_on_page(&self, page: Vector2<i32>) -> Vec<Vector2<i32>> {
866 self.as_ref()
867 .map(|t| t.keys_on_page(page))
868 .unwrap_or_default()
869 }
870 pub fn page_keys(&self) -> Vec<Vector2<i32>> {
872 self.as_ref().map(|t| t.page_keys()).unwrap_or_default()
873 }
874 pub fn has_tile_at(&self, page: Vector2<i32>, tile: Vector2<i32>) -> bool {
876 self.as_ref()
877 .map(|t| t.has_tile_at(page, tile))
878 .unwrap_or_default()
879 }
880 pub fn page_icon(&self, page: Vector2<i32>) -> Option<TileDefinitionHandle> {
882 self.as_ref().map(|t| t.page_icon(page)).unwrap_or_default()
883 }
884 pub fn property_name_to_uuid(&self, name: &ImmutableString) -> Option<Uuid> {
886 self.as_ref()
887 .map(|t| t.property_name_to_uuid(name))
888 .unwrap_or_default()
889 }
890 pub fn collider_name_to_uuid(&self, name: &ImmutableString) -> Option<Uuid> {
892 self.as_ref()
893 .map(|t| t.collider_name_to_uuid(name))
894 .unwrap_or_default()
895 }
896 pub fn find_property(&self, uuid: Uuid) -> Option<&TileSetPropertyLayer> {
898 self.as_ref()
899 .map(|t| t.find_property(uuid))
900 .unwrap_or_default()
901 }
902 pub fn find_collider(&self, uuid: Uuid) -> Option<&TileSetColliderLayer> {
904 self.as_ref()
905 .map(|t| t.find_collider(uuid))
906 .unwrap_or_default()
907 }
908 pub fn rebuild_transform_sets(&mut self) {
913 if let Some(t) = &mut self.0 {
914 t.rebuild_transform_sets()
915 }
916 }
917 pub fn rebuild_animations(&mut self) {
921 if let Some(t) = &mut self.0 {
922 t.rebuild_animations()
923 }
924 }
925 pub fn preview_texture(&self) -> Option<TextureResource> {
927 self.as_ref()
928 .map(|t| t.preview_texture())
929 .unwrap_or_default()
930 }
931 pub fn get_page(&self, position: Vector2<i32>) -> Option<&TileSetPage> {
933 self.as_ref()
934 .map(|t| t.get_page(position))
935 .unwrap_or_default()
936 }
937 pub fn get_page_mut(&mut self, position: Vector2<i32>) -> Option<&mut TileSetPage> {
939 self.0
940 .as_mut()
941 .map(|t| t.get_page_mut(position))
942 .unwrap_or_default()
943 }
944 pub fn is_valid_tile(&self, handle: TileDefinitionHandle) -> bool {
946 self.as_ref()
947 .map(|t| t.is_valid_tile(handle))
948 .unwrap_or_default()
949 }
950 pub fn get_abstract_tile(
952 &self,
953 page: Vector2<i32>,
954 tile: Vector2<i32>,
955 ) -> Option<AbstractTile> {
956 self.as_ref()
957 .map(|t| t.get_abstract_tile(page, tile))
958 .unwrap_or_default()
959 }
960 pub fn get_transformed_render_data(
962 &self,
963 trans: OrthoTransformation,
964 handle: TileDefinitionHandle,
965 ) -> Option<TileRenderData> {
966 self.as_ref()
967 .map(|t| t.get_transformed_render_data(trans, handle))
968 .unwrap_or_else(|| Some(TileRenderData::missing_data()))
969 }
970 pub fn get_tile_render_data(&self, position: ResourceTilePosition) -> Option<TileRenderData> {
977 self.as_ref()
978 .map(|t| t.get_tile_render_data(position))
979 .unwrap_or_else(|| Some(TileRenderData::missing_data()))
980 }
981 pub fn get_tile_collider(
983 &self,
984 handle: TileDefinitionHandle,
985 uuid: Uuid,
986 ) -> Option<&TileCollider> {
987 self.as_ref()
988 .map(|t| t.get_tile_collider(handle, uuid))
989 .unwrap_or_default()
990 }
991 pub fn palette_render_loop<F>(&self, stage: TilePaletteStage, page: Vector2<i32>, func: F)
994 where
995 F: FnMut(Vector2<i32>, TileRenderData),
996 {
997 if let Some(tile_set) = &self.0 {
998 tile_set.palette_render_loop(stage, page, func);
999 }
1000 }
1001 pub fn tile_collider_loop<F>(&self, page: Vector2<i32>, func: F)
1004 where
1005 F: FnMut(Vector2<i32>, Uuid, Color, &TileCollider),
1006 {
1007 if let Some(tile_set) = &self.0 {
1008 tile_set.tile_collider_loop(page, func);
1009 }
1010 }
1011
1012 pub fn redirect_handle(&self, position: ResourceTilePosition) -> Option<TileDefinitionHandle> {
1019 self.as_ref()
1020 .map(|t| t.redirect_handle(position))
1021 .unwrap_or_default()
1022 }
1023 pub fn get_transform_tile_source(
1026 &self,
1027 handle: TileDefinitionHandle,
1028 ) -> Option<TileDefinitionHandle> {
1029 self.as_ref()
1030 .map(|t| t.get_transform_tile_source(handle))
1031 .unwrap_or_default()
1032 }
1033 pub fn get_definition(&self, handle: TileDefinitionHandle) -> Option<TileDefinition> {
1036 self.as_ref()
1037 .map(|t| t.get_definition(handle))
1038 .unwrap_or_default()
1039 }
1040 pub fn get_transformed_definition(
1042 &self,
1043 trans: OrthoTransformation,
1044 handle: TileDefinitionHandle,
1045 ) -> Option<TileDefinition> {
1046 self.as_ref()
1047 .map(|t| t.get_transformed_definition(trans, handle))
1048 .unwrap_or_default()
1049 }
1050 pub fn get_tile_bounds(&self, position: ResourceTilePosition) -> Option<TileMaterialBounds> {
1052 self.as_ref()
1053 .map(|t| t.get_tile_bounds(position))
1054 .unwrap_or_default()
1055 }
1056 pub fn property_value(
1058 &self,
1059 handle: TileDefinitionHandle,
1060 property_id: Uuid,
1061 ) -> Option<TileSetPropertyValue> {
1062 self.as_ref()
1063 .map(|t| t.property_value(handle, property_id))
1064 .unwrap_or_default()
1065 }
1066 pub fn get_tile_data(&self, position: ResourceTilePosition) -> Option<&TileData> {
1068 self.as_ref()
1069 .map(|t| t.get_tile_data(position))
1070 .unwrap_or_default()
1071 }
1072 pub fn get_tile_data_mut(&mut self, handle: TileDefinitionHandle) -> Option<&mut TileData> {
1074 self.0
1075 .as_mut()
1076 .map(|t| t.get_tile_data_mut(handle))
1077 .unwrap_or_default()
1078 }
1079
1080 pub fn get_transformed_version(
1086 &self,
1087 transform: OrthoTransformation,
1088 handle: TileDefinitionHandle,
1089 ) -> Option<TileDefinitionHandle> {
1090 self.as_ref()
1091 .map(|t| t.get_transformed_version(transform, handle))
1092 .unwrap_or_default()
1093 }
1094
1095 pub fn get_animated_version(
1098 &self,
1099 time: f32,
1100 handle: TileDefinitionHandle,
1101 ) -> Option<TileDefinitionHandle> {
1102 self.as_ref()
1103 .map(|t| t.get_animated_version(time, handle))
1104 .unwrap_or_default()
1105 }
1106
1107 pub fn get_tiles<I: Iterator<Item = Vector2<i32>>>(
1109 &self,
1110 stage: TilePaletteStage,
1111 page: Vector2<i32>,
1112 iter: I,
1113 tiles: &mut Tiles,
1114 ) {
1115 if let Some(tile_set) = &self.0 {
1116 tile_set.get_tiles(stage, page, iter, tiles);
1117 }
1118 }
1119 pub fn pages_bounds(&self) -> OptionTileRect {
1121 self.as_ref().map(|t| t.pages_bounds()).unwrap_or_default()
1122 }
1123 pub fn tiles_bounds(&self, stage: TilePaletteStage, page: Vector2<i32>) -> OptionTileRect {
1125 self.as_ref()
1126 .map(|t| t.tiles_bounds(stage, page))
1127 .unwrap_or_default()
1128 }
1129
1130 pub fn is_free_at(&self, position: ResourceTilePosition) -> bool {
1132 self.as_ref()
1133 .map(|t| t.is_free_at(position))
1134 .unwrap_or(true)
1135 }
1136}
1137
1138#[derive(Debug, Default, Clone)]
1141struct AnimationRef {
1142 index: usize,
1143 offset: i32,
1144}
1145
1146#[derive(Debug, Default, Clone)]
1148struct Animation {
1149 start: TileDefinitionHandle,
1150 length: i32,
1151}
1152
1153impl Animation {
1154 fn iter(&self) -> impl Iterator<Item = (TileDefinitionHandle, i32)> {
1155 let page = self.start.page();
1156 let tile = self.start.tile();
1157 (0..self.length).filter_map(move |i| {
1158 let x = tile.x.saturating_add(i);
1159 let handle = TileDefinitionHandle::try_new(page, Vector2::new(x, tile.y))?;
1160 Some((handle, i))
1161 })
1162 }
1163 fn page(&self) -> Vector2<i32> {
1164 self.start.page()
1165 }
1166 fn frame(&self, frame: i32) -> Vector2<i32> {
1167 let tile = self.start.tile();
1168 Vector2::new(tile.x + frame.rem_euclid(self.length), tile.y)
1169 }
1170}
1171
1172#[derive(Debug, Default, Clone)]
1174struct AnimationCache {
1175 handle_to_animation: FxHashMap<TileDefinitionHandle, AnimationRef>,
1176 animations: Vec<Animation>,
1177}
1178
1179impl AnimationCache {
1180 fn clear(&mut self) {
1181 self.handle_to_animation.clear();
1182 self.animations.clear();
1183 }
1184 fn add_animation(&mut self, start: TileDefinitionHandle, length: i32) {
1185 self.animations.push(Animation { start, length });
1186 }
1187 fn get_animation_and_offset(&self, handle: TileDefinitionHandle) -> Option<(&Animation, i32)> {
1188 let AnimationRef { index, offset } = self.handle_to_animation.get(&handle)?;
1189 let animation = self.animations.get(*index)?;
1190 Some((animation, *offset))
1191 }
1192}
1193
1194#[derive(Clone, Default, Debug, Visit, Reflect, TypeUuidProvider, ComponentProvider)]
1217#[type_uuid(id = "7b7e057b-a41e-4150-ab3b-0ae99f4024f0")]
1218pub struct TileSet {
1219 #[reflect(hidden)]
1221 #[visit(skip)]
1222 animation_map: AnimationCache,
1223 #[reflect(hidden)]
1225 #[visit(skip)]
1226 transform_map: FxHashMap<TileDefinitionHandle, TileDefinitionHandle>,
1227 pub pages: FxHashMap<Vector2<i32>, TileSetPage>,
1229 pub colliders: Vec<TileSetColliderLayer>,
1231 pub properties: Vec<TileSetPropertyLayer>,
1233 #[reflect(hidden)]
1237 #[visit(skip)]
1238 pub change_count: ChangeFlag,
1239}
1240
1241impl TileSet {
1242 pub fn collider_color(&self, uuid: Uuid) -> Option<Color> {
1244 self.find_collider(uuid).map(|layer| layer.color)
1245 }
1246 pub fn tile_collider(&self, handle: TileDefinitionHandle, uuid: Uuid) -> &TileCollider {
1248 let Some(data) = self.tile_data(handle) else {
1249 return &TileCollider::None;
1250 };
1251 data.colliders.get(&uuid).unwrap_or(&TileCollider::None)
1252 }
1253 pub fn tile_color(&self, handle: TileDefinitionHandle) -> Option<Color> {
1255 self.tile_data(handle).map(|d| d.color)
1256 }
1257 pub fn tile_data(&self, handle: TileDefinitionHandle) -> Option<&TileData> {
1259 let page_source = self.pages.get(&handle.page()).map(|p| &p.source)?;
1260 match page_source {
1261 TileSetPageSource::Atlas(m) => m.get(&handle.tile()),
1262 TileSetPageSource::Freeform(m) => m.get(&handle.tile()).map(|def| &def.data),
1263 TileSetPageSource::Transform(_) => None,
1264 TileSetPageSource::Animation(_) => None,
1265 }
1266 }
1267 pub fn tile_bounds(&self, handle: TileDefinitionHandle) -> Option<&TileMaterialBounds> {
1269 let page_source = self.pages.get(&handle.page()).map(|p| &p.source)?;
1270 if let TileSetPageSource::Freeform(m) = page_source {
1271 m.get(&handle.tile()).map(|t| &t.material_bounds)
1272 } else {
1273 None
1274 }
1275 }
1276 pub fn tile_redirect(&self, handle: TileDefinitionHandle) -> Option<TileDefinitionHandle> {
1279 let page_source = self.pages.get(&handle.page()).map(|p| &p.source)?;
1280 match page_source {
1281 TileSetPageSource::Transform(m) => m.get(&handle.tile()).copied(),
1282 TileSetPageSource::Animation(m) => m.get(&handle.tile()).copied(),
1283 _ => None,
1284 }
1285 }
1286 pub fn keys_on_page(&self, page: Vector2<i32>) -> Vec<Vector2<i32>> {
1288 self.pages.get(&page).map(|p| p.keys()).unwrap_or_default()
1289 }
1290 pub fn page_keys(&self) -> Vec<Vector2<i32>> {
1292 self.pages.keys().copied().collect()
1293 }
1294 pub fn has_tile_at(&self, page: Vector2<i32>, tile: Vector2<i32>) -> bool {
1296 let Some(page) = self.pages.get(&page).map(|p| &p.source) else {
1297 return false;
1298 };
1299 match page {
1300 TileSetPageSource::Atlas(m) => m.contains_key(&tile),
1301 TileSetPageSource::Freeform(m) => m.contains_key(&tile),
1302 TileSetPageSource::Transform(m) => m.contains_key(&tile),
1303 TileSetPageSource::Animation(m) => m.contains_key(&tile),
1304 }
1305 }
1306 pub fn page_icon(&self, page: Vector2<i32>) -> Option<TileDefinitionHandle> {
1308 self.pages.get(&page).map(|p| p.icon)
1309 }
1310 pub fn property_name_to_uuid(&self, name: &ImmutableString) -> Option<Uuid> {
1312 self.find_property_by_name(name).map(|p| p.uuid)
1313 }
1314 pub fn collider_name_to_uuid(&self, name: &ImmutableString) -> Option<Uuid> {
1316 self.colliders
1317 .iter()
1318 .find(|c| &c.name == name)
1319 .map(|c| c.uuid)
1320 }
1321 pub fn find_property_by_name(
1323 &self,
1324 property_name: &ImmutableString,
1325 ) -> Option<&TileSetPropertyLayer> {
1326 self.properties.iter().find(|p| &p.name == property_name)
1327 }
1328 pub fn find_property(&self, uuid: Uuid) -> Option<&TileSetPropertyLayer> {
1330 self.properties.iter().find(|p| p.uuid == uuid)
1331 }
1332 pub fn find_property_mut(&mut self, uuid: Uuid) -> Option<&mut TileSetPropertyLayer> {
1334 self.properties.iter_mut().find(|p| p.uuid == uuid)
1335 }
1336 pub fn find_collider(&self, uuid: Uuid) -> Option<&TileSetColliderLayer> {
1338 self.colliders.iter().find(|p| p.uuid == uuid)
1339 }
1340 pub fn find_collider_mut(&mut self, uuid: Uuid) -> Option<&mut TileSetColliderLayer> {
1342 self.colliders.iter_mut().find(|p| p.uuid == uuid)
1343 }
1344 pub fn rebuild_transform_sets(&mut self) {
1348 self.transform_map.clear();
1349 for (&position, page) in self.pages.iter_mut() {
1350 let TileSetPageSource::Transform(tiles) = &page.source else {
1351 continue;
1352 };
1353 self.transform_map.extend(
1354 tiles
1355 .iter()
1356 .filter_map(|(&k, &v)| Some((v, TileDefinitionHandle::try_new(position, k)?))),
1357 );
1358 }
1359 }
1360 pub fn rebuild_animations(&mut self) {
1370 self.animation_map.clear();
1371 for (&position, page) in self.pages.iter_mut() {
1372 let TileSetPageSource::Animation(tiles) = &page.source else {
1373 continue;
1374 };
1375 for &k in tiles.keys() {
1376 let left = Vector2::new(k.x - 1, k.y);
1377 if tiles.contains_key(&left) {
1378 continue;
1379 }
1380 let mut right = Vector2::new(k.x + 1, k.y);
1381 while tiles.contains_key(&right) {
1382 right.x += 1;
1383 }
1384 let Some(start) = TileDefinitionHandle::try_new(position, k) else {
1385 continue;
1386 };
1387 let length = right.x - k.x;
1388 self.animation_map.add_animation(start, length);
1389 }
1390 }
1391 for (index, animation) in self.animation_map.animations.iter().enumerate() {
1392 let page = self.pages.get(&animation.page()).unwrap();
1393 let TileSetPageSource::Animation(tiles) = &page.source else {
1394 unreachable!();
1395 };
1396 for (handle, offset) in animation.iter() {
1397 let handle = *tiles.get(&handle.tile()).unwrap();
1398 let anim_ref = AnimationRef { index, offset };
1399 match self.animation_map.handle_to_animation.entry(handle) {
1400 Entry::Occupied(mut entry) => {
1401 let prev = entry.get();
1402 if offset == 0 && prev.offset != 0 {
1403 entry.insert(anim_ref);
1404 } else if (offset == 0) == (prev.offset == 0) {
1405 let new_start = animation.start;
1406 let prev_start = self.animation_map.animations[prev.index].start;
1407 if new_start < prev_start {
1408 entry.insert(anim_ref);
1409 }
1410 }
1411 }
1412 Entry::Vacant(entry) => drop(entry.insert(anim_ref)),
1413 }
1414 }
1415 }
1416 }
1417 pub fn preview_texture(&self) -> Option<TextureResource> {
1419 self.pages
1420 .iter()
1421 .filter_map(|(&pos, p)| match &p.source {
1422 TileSetPageSource::Atlas(mat) => {
1423 Some((pos, mat.material.state().data()?.texture("diffuseTexture")?))
1424 }
1425 _ => None,
1426 })
1427 .min_by(|(a, _), (b, _)| a.y.cmp(&b.y).reverse().then(a.x.cmp(&b.x)))
1428 .map(|(_, texture)| texture)
1429 }
1430 pub fn swap(&mut self, update: &mut TileSetUpdate) {
1444 let mut transform_changes = false;
1445 let mut animation_changes: bool = false;
1446 for (handle, tile_update) in update.iter_mut() {
1447 let Some(page) = self.pages.get_mut(&handle.page()) else {
1448 Log::err("Tile set update page missing.");
1449 continue;
1450 };
1451 if page.is_transform_set() {
1452 transform_changes = true;
1453 }
1454 if page.is_animation() {
1455 animation_changes = true;
1456 }
1457 page.swap_tile(handle.tile(), tile_update);
1458 }
1459 if transform_changes {
1460 self.rebuild_transform_sets();
1461 }
1462 if animation_changes {
1463 self.rebuild_animations();
1464 }
1465 }
1466 pub fn get_page(&self, position: Vector2<i32>) -> Option<&TileSetPage> {
1468 self.pages.get(&position)
1469 }
1470 pub fn get_page_mut(&mut self, position: Vector2<i32>) -> Option<&mut TileSetPage> {
1472 self.pages.get_mut(&position)
1473 }
1474 pub fn insert_page(
1476 &mut self,
1477 position: Vector2<i32>,
1478 page: TileSetPage,
1479 ) -> Option<TileSetPage> {
1480 self.pages.insert(position, page)
1481 }
1482 pub fn remove_page(&mut self, position: Vector2<i32>) -> Option<TileSetPage> {
1484 self.pages.remove(&position)
1485 }
1486 pub fn is_valid_tile(&self, handle: TileDefinitionHandle) -> bool {
1488 let Some(source) = self.pages.get(&handle.page()).map(|p| &p.source) else {
1489 return false;
1490 };
1491 match source {
1492 TileSetPageSource::Atlas(mat) => mat.contains_key(&handle.tile()),
1493 TileSetPageSource::Freeform(map) => map.contains_key(&handle.tile()),
1494 TileSetPageSource::Transform(_) => false,
1495 TileSetPageSource::Animation(_) => false,
1496 }
1497 }
1498 pub fn get_abstract_tile(
1500 &self,
1501 page: Vector2<i32>,
1502 tile: Vector2<i32>,
1503 ) -> Option<AbstractTile> {
1504 let source = self.pages.get(&page).map(|p| &p.source)?;
1505 match source {
1506 TileSetPageSource::Atlas(tile_material) => tile_material.get_abstract_tile(tile),
1507 TileSetPageSource::Freeform(tile_grid_map) => {
1508 Some(AbstractTile::Freeform(tile_grid_map.get(&tile)?.clone()))
1509 }
1510 TileSetPageSource::Transform(tiles) => {
1511 Some(AbstractTile::Transform(tiles.get(&tile).copied()?))
1512 }
1513 TileSetPageSource::Animation(tiles) => {
1514 Some(AbstractTile::Transform(tiles.get(&tile).copied()?))
1515 }
1516 }
1517 }
1518 pub fn set_abstract_tile(
1520 &mut self,
1521 page: Vector2<i32>,
1522 tile: Vector2<i32>,
1523 value: Option<AbstractTile>,
1524 ) -> Option<AbstractTile> {
1525 let Some(source) = self.pages.get_mut(&page).map(|p| &mut p.source) else {
1526 panic!();
1527 };
1528 use AbstractTile as Tile;
1529 use TileSetPageSource as Source;
1530 match (source, value) {
1531 (Source::Atlas(d0), value) => d0.set_abstract_tile(tile, value),
1532 (Source::Freeform(d0), Some(Tile::Freeform(d1))) => {
1533 Some(Tile::Freeform(d0.insert(tile, d1)?))
1534 }
1535 (Source::Freeform(d0), None) => Some(Tile::Freeform(d0.remove(&tile)?)),
1536 (Source::Transform(d0), Some(Tile::Transform(d1))) => {
1537 d0.insert(tile, d1).map(Tile::Transform)
1538 }
1539 (Source::Transform(d0), None) => d0.remove(&tile).map(Tile::Transform),
1540 (Source::Animation(d0), Some(Tile::Transform(d1))) => {
1541 d0.insert(tile, d1).map(Tile::Transform)
1542 }
1543 (Source::Animation(d0), None) => d0.remove(&tile).map(Tile::Transform),
1544 _ => panic!(),
1545 }
1546 }
1547 pub fn get_transformed_render_data(
1549 &self,
1550 trans: OrthoTransformation,
1551 handle: TileDefinitionHandle,
1552 ) -> Option<TileRenderData> {
1553 if let Some(handle) = self.get_transformed_version(trans, handle) {
1554 self.get_tile_render_data(handle.into())
1555 } else {
1556 Some(self.get_tile_render_data(handle.into())?.transformed(trans))
1557 }
1558 }
1559 pub fn get_tile_render_data(&self, position: ResourceTilePosition) -> Option<TileRenderData> {
1566 if self.is_free_at(position) {
1567 return None;
1568 }
1569 self.inner_get_render_data(position)
1570 .or_else(|| Some(TileRenderData::missing_data()))
1571 }
1572 fn inner_get_render_data(&self, position: ResourceTilePosition) -> Option<TileRenderData> {
1573 if self.is_valid_tile(self.redirect_handle(position)?) {
1574 Some(TileRenderData {
1575 material_bounds: Some(self.get_tile_bounds(position)?),
1576 color: self.get_tile_data(position)?.color,
1577 })
1578 } else {
1579 Some(TileRenderData::missing_data())
1580 }
1581 }
1582 pub fn get_tile_collider(
1584 &self,
1585 handle: TileDefinitionHandle,
1586 uuid: Uuid,
1587 ) -> Option<&TileCollider> {
1588 if self.is_free_at(handle.into()) {
1589 return None;
1590 }
1591 let handle = self.redirect_handle(handle.into())?;
1592 let data = self.get_tile_data(handle.into())?;
1593 data.colliders.get(&uuid)
1594 }
1595 pub fn palette_iterator(
1597 &self,
1598 stage: TilePaletteStage,
1599 page: Vector2<i32>,
1600 ) -> TileSetPaletteIterator {
1601 TileSetPaletteIterator {
1602 page,
1603 keys: self.palette_keys(stage, page),
1604 }
1605 }
1606 fn palette_keys(&self, stage: TilePaletteStage, page: Vector2<i32>) -> PaletteIterator {
1607 match stage {
1608 TilePaletteStage::Pages => PaletteIterator::Pages(self.pages.keys()),
1609 TilePaletteStage::Tiles => {
1610 let Some(page) = self.pages.get(&page) else {
1611 return PaletteIterator::Empty;
1612 };
1613 match &page.source {
1614 TileSetPageSource::Atlas(mat) => PaletteIterator::Material(mat.tiles.keys()),
1615 TileSetPageSource::Freeform(map) => PaletteIterator::Freeform(map.keys()),
1616 TileSetPageSource::Transform(tiles) => {
1617 PaletteIterator::TransformSet(tiles.keys())
1618 }
1619 TileSetPageSource::Animation(tiles) => {
1620 PaletteIterator::TransformSet(tiles.keys())
1621 }
1622 }
1623 }
1624 }
1625 }
1626
1627 pub fn palette_render_loop<F>(&self, stage: TilePaletteStage, page: Vector2<i32>, mut func: F)
1630 where
1631 F: FnMut(Vector2<i32>, TileRenderData),
1632 {
1633 for position in self.palette_iterator(stage, page) {
1634 if let Some(data) = self.get_tile_render_data(position) {
1635 func(position.stage_position(), data);
1636 }
1637 }
1638 }
1639 pub fn tile_collider_loop<F>(&self, page: Vector2<i32>, mut func: F)
1642 where
1643 F: FnMut(Vector2<i32>, Uuid, Color, &TileCollider),
1644 {
1645 for layer in self.colliders.iter() {
1646 for position in self.palette_iterator(TilePaletteStage::Tiles, page) {
1647 if let Some(tile_collider) =
1648 self.get_tile_collider(position.handle().unwrap(), layer.uuid)
1649 {
1650 if !tile_collider.is_none() {
1651 func(
1652 position.stage_position(),
1653 layer.uuid,
1654 layer.color,
1655 tile_collider,
1656 );
1657 }
1658 }
1659 }
1660 }
1661 }
1662
1663 pub fn redirect_handle(&self, position: ResourceTilePosition) -> Option<TileDefinitionHandle> {
1670 match position.stage() {
1671 TilePaletteStage::Tiles => match &self.pages.get(&position.page())?.source {
1672 TileSetPageSource::Transform(tiles) => tiles.get_at(position.stage_position()),
1673 TileSetPageSource::Animation(tiles) => tiles.get_at(position.stage_position()),
1674 page => {
1675 if page.contains_tile_at(position.stage_position()) {
1676 position.handle()
1677 } else {
1678 None
1679 }
1680 }
1681 },
1682 TilePaletteStage::Pages => self
1683 .pages
1684 .get(&position.stage_position())
1685 .and_then(|p| self.redirect_handle(ResourceTilePosition::from(p.icon))),
1686 }
1687 }
1688 pub fn get_transform_tile_source(
1691 &self,
1692 handle: TileDefinitionHandle,
1693 ) -> Option<TileDefinitionHandle> {
1694 match &self.pages.get(&handle.page())?.source {
1695 TileSetPageSource::Transform(tiles) => tiles.get_at(handle.tile()),
1696 _ => None,
1697 }
1698 }
1699 pub fn get_definition(&self, handle: TileDefinitionHandle) -> Option<TileDefinition> {
1702 Some(TileDefinition {
1703 material_bounds: self.get_tile_bounds(handle.into())?,
1704 data: self.get_tile_data(handle.into()).cloned()?,
1705 })
1706 }
1707 pub fn get_transformed_definition(
1709 &self,
1710 trans: OrthoTransformation,
1711 handle: TileDefinitionHandle,
1712 ) -> Option<TileDefinition> {
1713 if let Some(handle) = self.get_transformed_version(trans, handle) {
1714 self.get_definition(handle)
1715 } else {
1716 Some(self.get_definition(handle)?.transformed(trans))
1717 }
1718 }
1719 pub fn get_tile_bounds(&self, position: ResourceTilePosition) -> Option<TileMaterialBounds> {
1721 let handle = self.redirect_handle(position)?;
1722 match &self.pages.get(&handle.page())?.source {
1723 TileSetPageSource::Atlas(mat) => mat.get_tile_bounds(handle.tile()),
1724 TileSetPageSource::Freeform(map) => {
1725 Some(map.get(&handle.tile())?.material_bounds.clone())
1726 }
1727 TileSetPageSource::Transform(_) => None,
1728 TileSetPageSource::Animation(_) => None,
1729 }
1730 }
1731 pub fn property_value(
1733 &self,
1734 handle: TileDefinitionHandle,
1735 property_id: Uuid,
1736 ) -> Option<TileSetPropertyValue> {
1737 self.get_tile_data(handle.into()).and_then(|d| {
1738 d.properties.get(&property_id).cloned().or_else(|| {
1739 self.find_property(property_id)
1740 .map(|p| p.prop_type.default_value())
1741 })
1742 })
1743 }
1744 pub fn get_tile_data(&self, position: ResourceTilePosition) -> Option<&TileData> {
1746 let handle = self.redirect_handle(position)?;
1747 match &self.pages.get(&handle.page())?.source {
1748 TileSetPageSource::Atlas(mat) => mat.get_tile_data(handle.tile()),
1749 TileSetPageSource::Freeform(map) => Some(&map.get(&handle.tile())?.data),
1750 TileSetPageSource::Transform(_) => None,
1751 TileSetPageSource::Animation(_) => None,
1752 }
1753 }
1754 pub fn get_tile_data_mut(&mut self, handle: TileDefinitionHandle) -> Option<&mut TileData> {
1756 match &mut self.pages.get_mut(&handle.page())?.source {
1757 TileSetPageSource::Atlas(mat) => mat.get_tile_data_mut(handle.tile()),
1758 TileSetPageSource::Freeform(map) => Some(&mut map.get_mut(&handle.tile())?.data),
1759 TileSetPageSource::Transform(_) => None,
1760 TileSetPageSource::Animation(_) => None,
1761 }
1762 }
1763
1764 pub fn get_animated_version(
1767 &self,
1768 time: f32,
1769 handle: TileDefinitionHandle,
1770 ) -> Option<TileDefinitionHandle> {
1771 let (animation, offset) = self.animation_map.get_animation_and_offset(handle)?;
1772 let page = self.get_page(animation.page())?;
1773 let TileSetPageSource::Animation(AnimationTiles { frame_rate, tiles }) = &page.source
1774 else {
1775 return None;
1776 };
1777 let frame_rate = *frame_rate;
1778 let length = animation.length;
1779 let frame_position = (time * frame_rate).rem_euclid(length as f32);
1780 let frame_index = (frame_position.floor() as i32).clamp(0, length - 1);
1781 let frame_index = (frame_index + offset).rem_euclid(length);
1782 let frame = animation.frame(frame_index);
1783 tiles.get(&frame).copied()
1784 }
1785
1786 pub fn get_transformed_version(
1792 &self,
1793 transform: OrthoTransformation,
1794 handle: TileDefinitionHandle,
1795 ) -> Option<TileDefinitionHandle> {
1796 if transform.is_identity() {
1797 return Some(handle);
1798 }
1799 let transform_tile = self.transform_map.get(&handle)?;
1800 let page = self.get_page(transform_tile.page())?;
1801 let tiles = match &page.source {
1802 TileSetPageSource::Transform(TransformSetTiles(tiles)) => Some(tiles),
1803 _ => None,
1804 }?;
1805 let cell = TransformSetCell::from_position(transform_tile.tile())
1806 .transformed(transform)
1807 .into_position();
1808 tiles.get(&cell).copied()
1809 }
1810
1811 pub fn get_tiles<I: Iterator<Item = Vector2<i32>>>(
1813 &self,
1814 stage: TilePaletteStage,
1815 page: Vector2<i32>,
1816 iter: I,
1817 tiles: &mut Tiles,
1818 ) {
1819 for pos in iter {
1820 if let Some(tile) = self.redirect_handle(ResourceTilePosition::new(stage, page, pos)) {
1821 tiles.insert(pos, tile);
1822 }
1823 }
1824 }
1825 pub fn pages_bounds(&self) -> OptionTileRect {
1827 let mut result = OptionTileRect::default();
1828 for pos in self.pages.keys() {
1829 result.push(*pos);
1830 }
1831 result
1832 }
1833 pub fn tiles_bounds(&self, stage: TilePaletteStage, page: Vector2<i32>) -> OptionTileRect {
1835 match stage {
1836 TilePaletteStage::Tiles => {
1837 let Some(page) = self.pages.get(&page) else {
1838 return OptionTileRect::default();
1839 };
1840 page.get_bounds()
1841 }
1842 TilePaletteStage::Pages => self.pages_bounds(),
1843 }
1844 }
1845
1846 pub async fn from_file(
1848 path: &Path,
1849 resource_manager: ResourceManager,
1850 io: &dyn ResourceIo,
1851 ) -> Result<Self, TileSetResourceError> {
1852 let bytes = io.load_file(path).await?;
1853 let mut visitor = Visitor::load_from_memory(&bytes)?;
1854 visitor.blackboard.register(Arc::new(resource_manager));
1855 let mut tile_set = TileSet::default();
1856 tile_set.visit("TileSet", &mut visitor)?;
1857 Ok(tile_set)
1858 }
1859
1860 pub fn is_free_at(&self, position: ResourceTilePosition) -> bool {
1862 match position.stage() {
1863 TilePaletteStage::Pages => self.get_page(position.stage_position()).is_none(),
1864 TilePaletteStage::Tiles => {
1865 self.get_page(position.page()).is_some()
1866 && !self.has_tile_at(position.page(), position.stage_position())
1867 }
1868 }
1869 }
1870
1871 pub fn find_free_location(
1874 &self,
1875 stage: TilePaletteStage,
1876 page: Vector2<i32>,
1877 position: Vector2<i32>,
1878 ) -> Vector2<i32> {
1879 let mut visited = FxHashSet::default();
1880 let mut stack = vec![position];
1881 while let Some(pos) = stack.pop() {
1882 if visited.contains(&pos) {
1883 continue;
1884 }
1885 visited.insert(pos);
1886 if self.is_free_at(ResourceTilePosition::new(stage, page, pos)) {
1887 return pos;
1888 } else {
1889 stack.extend_from_slice(&[
1890 Vector2::new(pos.x + 1, pos.y),
1891 Vector2::new(pos.x + 1, pos.y + 1),
1892 Vector2::new(pos.x, pos.y + 1),
1893 Vector2::new(pos.x - 1, pos.y + 1),
1894 Vector2::new(pos.x - 1, pos.y),
1895 Vector2::new(pos.x - 1, pos.y - 1),
1896 Vector2::new(pos.x, pos.y - 1),
1897 Vector2::new(pos.x + 1, pos.y - 1),
1898 ]);
1899 }
1900 }
1901 Default::default()
1902 }
1903 pub fn swap_all_values_for_property(
1906 &mut self,
1907 property_id: Uuid,
1908 values: &mut FxHashMap<TileDefinitionHandle, TileSetPropertyValue>,
1909 ) {
1910 for (page_pos, page) in self.pages.iter_mut() {
1911 page.swap_all_values_for_property(*page_pos, property_id, values);
1912 }
1913 }
1914 pub fn swap_all_values_for_collider(
1917 &mut self,
1918 collider_id: Uuid,
1919 values: &mut FxHashMap<TileDefinitionHandle, TileCollider>,
1920 ) {
1921 for (page_pos, page) in self.pages.iter_mut() {
1922 page.swap_all_values_for_collider(*page_pos, collider_id, values);
1923 }
1924 }
1925}
1926
1927impl ResourceData for TileSet {
1928 fn type_uuid(&self) -> Uuid {
1929 <Self as TypeUuidProvider>::type_uuid()
1930 }
1931
1932 fn save(&mut self, path: &Path) -> Result<(), Box<dyn Error>> {
1933 let mut visitor = Visitor::new();
1934 self.visit("TileSet", &mut visitor)?;
1935 visitor.save_binary(path)?;
1936 Ok(())
1937 }
1938
1939 fn can_be_saved(&self) -> bool {
1940 true
1941 }
1942}
1943
1944pub type TileSetResource = Resource<TileSet>;
1946
1947pub struct TileSetLoader {
1949 pub resource_manager: ResourceManager,
1951}
1952
1953impl ResourceLoader for TileSetLoader {
1954 fn extensions(&self) -> &[&str] {
1955 &["tileset"]
1956 }
1957
1958 fn data_type_uuid(&self) -> Uuid {
1959 <TileSet as TypeUuidProvider>::type_uuid()
1960 }
1961
1962 fn load(&self, path: PathBuf, io: Arc<dyn ResourceIo>) -> BoxedLoaderFuture {
1963 let resource_manager = self.resource_manager.clone();
1964 Box::pin(async move {
1965 let mut tile_set = TileSet::from_file(&path, resource_manager, io.as_ref())
1966 .await
1967 .map_err(LoadError::new)?;
1968 tile_set.rebuild_transform_sets();
1969 tile_set.rebuild_animations();
1970 Ok(LoaderPayload::new(tile_set))
1971 })
1972 }
1973}