1use crate::prelude::*;
2use buildergraph::BuilderGraph;
3use indexmap::IndexMap;
4pub use rusterix::map::*;
5use theframework::prelude::*;
6use tilegraph::TileGraphPaletteSource;
7
8fn default_target_fps() -> u32 {
10 30
11}
12
13fn default_tick_ms() -> u32 {
15 250
16}
17
18fn default_rules() -> String {
19 String::new()
20}
21
22fn default_locales() -> String {
23 String::new()
24}
25
26fn default_audio_fx() -> String {
27 String::new()
28}
29
30fn default_authoring() -> String {
31 String::new()
32}
33
34fn default_tile_board_cols() -> i32 {
35 13
36}
37
38fn default_tile_board_rows() -> i32 {
39 9
40}
41
42fn default_tile_collection_name() -> String {
43 "New Collection".to_string()
44}
45
46fn default_tile_collection_version() -> String {
47 "0.1".to_string()
48}
49
50fn default_palette_material_slots() -> Vec<PaletteMaterial> {
51 vec![PaletteMaterial::default(); 256]
52}
53
54#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
55pub struct PaletteMaterial {
56 #[serde(default = "default_palette_roughness")]
57 pub roughness: f32,
58 #[serde(default = "default_palette_metallic")]
59 pub metallic: f32,
60 #[serde(default = "default_palette_opacity")]
61 pub opacity: f32,
62 #[serde(default = "default_palette_emissive")]
63 pub emissive: f32,
64}
65
66fn default_palette_roughness() -> f32 {
67 0.5
68}
69
70fn default_palette_metallic() -> f32 {
71 0.0
72}
73
74fn default_palette_opacity() -> f32 {
75 1.0
76}
77
78fn default_palette_emissive() -> f32 {
79 0.0
80}
81
82impl Default for PaletteMaterial {
83 fn default() -> Self {
84 Self {
85 roughness: default_palette_roughness(),
86 metallic: default_palette_metallic(),
87 opacity: default_palette_opacity(),
88 emissive: default_palette_emissive(),
89 }
90 }
91}
92
93#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
94pub struct BuilderGraphAsset {
95 pub id: Uuid,
96 pub graph_id: Uuid,
97 #[serde(default)]
98 pub graph_name: String,
99 #[serde(default)]
100 pub graph_data: String,
101}
102
103impl BuilderGraphAsset {
104 pub fn new_table(name: String) -> Self {
105 let graph_data = BuilderGraph::preset_table_script_named(name.clone());
106 let graph_name = if let Ok(document) = buildergraph::BuilderDocument::from_text(&graph_data)
107 {
108 document.name().to_string()
109 } else if name.is_empty() {
110 "Table".to_string()
111 } else {
112 name.clone()
113 };
114 Self {
115 id: Uuid::new_v4(),
116 graph_id: Uuid::new_v4(),
117 graph_name,
118 graph_data,
119 }
120 }
121
122 pub fn new_empty(name: String) -> Self {
123 let graph_data = BuilderGraph::empty_script_named(name.clone());
124 let graph_name = if let Ok(document) = buildergraph::BuilderDocument::from_text(&graph_data)
125 {
126 document.name().to_string()
127 } else if name.is_empty() {
128 "Empty".to_string()
129 } else {
130 name.clone()
131 };
132 Self {
133 id: Uuid::new_v4(),
134 graph_id: Uuid::new_v4(),
135 graph_name,
136 graph_data,
137 }
138 }
139
140 pub fn new_wall_torch(name: String) -> Self {
141 let graph_data = BuilderGraph::preset_wall_torch_script_named(name.clone());
142 let graph_name = if let Ok(document) = buildergraph::BuilderDocument::from_text(&graph_data)
143 {
144 document.name().to_string()
145 } else if name.is_empty() {
146 "Wall Torch".to_string()
147 } else {
148 name.clone()
149 };
150 Self {
151 id: Uuid::new_v4(),
152 graph_id: Uuid::new_v4(),
153 graph_name,
154 graph_data,
155 }
156 }
157
158 pub fn new_wall_lantern(name: String) -> Self {
159 let graph_data = BuilderGraph::preset_wall_lantern_script_named(name.clone());
160 let graph_name = if let Ok(document) = buildergraph::BuilderDocument::from_text(&graph_data)
161 {
162 document.name().to_string()
163 } else if name.is_empty() {
164 "Wall Lantern".to_string()
165 } else {
166 name.clone()
167 };
168 Self {
169 id: Uuid::new_v4(),
170 graph_id: Uuid::new_v4(),
171 graph_name,
172 graph_data,
173 }
174 }
175
176 pub fn new_campfire(name: String) -> Self {
177 let graph_data = BuilderGraph::preset_campfire_script_named(name.clone());
178 let graph_name = if let Ok(document) = buildergraph::BuilderDocument::from_text(&graph_data)
179 {
180 document.name().to_string()
181 } else if name.is_empty() {
182 "Campfire".to_string()
183 } else {
184 name.clone()
185 };
186 Self {
187 id: Uuid::new_v4(),
188 graph_id: Uuid::new_v4(),
189 graph_name,
190 graph_data,
191 }
192 }
193}
194
195#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
196pub struct NodeGroupAsset {
197 pub group_id: Uuid,
198 pub graph_id: Uuid,
199 #[serde(default)]
200 pub graph_name: String,
201 pub output_grid_width: u16,
202 pub output_grid_height: u16,
203 pub tile_pixel_width: u16,
204 pub tile_pixel_height: u16,
205 #[serde(default)]
206 pub palette_source: TileGraphPaletteSource,
207 #[serde(default)]
208 pub palette_colors: Vec<TheColor>,
209 #[serde(default)]
210 pub graph_data: String,
211}
212
213impl NodeGroupAsset {
214 pub fn new(
215 group_id: Uuid,
216 output_grid_width: u16,
217 output_grid_height: u16,
218 palette_colors: Vec<TheColor>,
219 ) -> Self {
220 Self {
221 group_id,
222 graph_id: Uuid::new_v4(),
223 graph_name: "New Node Graph".to_string(),
224 output_grid_width,
225 output_grid_height,
226 tile_pixel_width: 32,
227 tile_pixel_height: 32,
228 palette_source: TileGraphPaletteSource::Local,
229 palette_colors,
230 graph_data: String::new(),
231 }
232 }
233}
234
235#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
236pub enum TileCollectionEntry {
237 SingleTile(Uuid),
238 TileGroup(Uuid),
239}
240
241impl TileCollectionEntry {
242 pub fn matches_source(&self, source: rusterix::TileSource) -> bool {
243 match (self, source) {
244 (Self::SingleTile(a), rusterix::TileSource::SingleTile(b)) => *a == b,
245 (Self::TileGroup(a), rusterix::TileSource::TileGroup(b)) => *a == b,
246 _ => false,
247 }
248 }
249}
250
251#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
252pub struct TileCollectionAsset {
253 pub id: Uuid,
254 #[serde(default = "default_tile_collection_name")]
255 pub name: String,
256 #[serde(default)]
257 pub author: String,
258 #[serde(default = "default_tile_collection_version")]
259 pub version: String,
260 #[serde(default)]
261 pub description: String,
262 #[serde(default)]
263 pub entries: Vec<TileCollectionEntry>,
264 #[serde(default)]
265 pub tile_board_tiles: IndexMap<Uuid, Vec2<i32>>,
266 #[serde(default)]
267 pub tile_board_groups: IndexMap<Uuid, Vec2<i32>>,
268 #[serde(default)]
269 pub tile_board_empty_slots: Vec<Vec2<i32>>,
270}
271
272impl TileCollectionAsset {
273 pub fn new(name: String) -> Self {
274 Self {
275 id: Uuid::new_v4(),
276 name,
277 author: String::new(),
278 version: default_tile_collection_version(),
279 description: String::new(),
280 entries: Vec::new(),
281 tile_board_tiles: IndexMap::default(),
282 tile_board_groups: IndexMap::default(),
283 tile_board_empty_slots: Vec::new(),
284 }
285 }
286}
287
288#[derive(Serialize, Deserialize, Clone, Debug)]
289pub struct Project {
290 pub name: String,
291 pub regions: Vec<Region>,
292 pub tilemaps: Vec<Tilemap>,
293
294 #[serde(default)]
296 pub tiles: IndexMap<Uuid, rusterix::Tile>,
297
298 #[serde(default)]
300 pub tile_groups: IndexMap<Uuid, rusterix::TileGroup>,
301
302 #[serde(default)]
304 pub tile_node_groups: IndexMap<Uuid, NodeGroupAsset>,
305
306 #[serde(default)]
308 pub builder_graphs: IndexMap<Uuid, BuilderGraphAsset>,
309
310 #[serde(default)]
312 pub tile_collections: IndexMap<Uuid, TileCollectionAsset>,
313
314 #[serde(default)]
316 pub tile_board_tiles: IndexMap<Uuid, Vec2<i32>>,
317
318 #[serde(default)]
320 pub tile_board_groups: IndexMap<Uuid, Vec2<i32>>,
321
322 #[serde(default)]
324 pub tile_board_empty_slots: Vec<Vec2<i32>>,
325
326 #[serde(default = "default_tile_board_cols")]
328 pub tile_board_cols: i32,
329
330 #[serde(default = "default_tile_board_rows")]
332 pub tile_board_rows: i32,
333
334 #[serde(default)]
335 pub time: TheTime,
336
337 #[serde(default)]
338 pub characters: IndexMap<Uuid, Character>,
339 #[serde(default)]
340 pub items: IndexMap<Uuid, Item>,
341
342 #[serde(default)]
343 pub screens: IndexMap<Uuid, Screen>,
344
345 #[serde(default)]
346 pub assets: IndexMap<Uuid, Asset>,
347
348 #[serde(default)]
349 pub palette: ThePalette,
350
351 #[serde(default = "default_target_fps")]
352 pub target_fps: u32,
353
354 #[serde(default = "default_tick_ms")]
355 pub tick_ms: u32,
356
357 #[serde(default)]
358 pub config: String,
359
360 #[serde(default = "default_rules")]
361 pub rules: String,
362
363 #[serde(default = "default_locales")]
364 pub locales: String,
365
366 #[serde(default = "default_audio_fx")]
367 pub audio_fx: String,
368
369 #[serde(default = "default_authoring")]
370 pub authoring: String,
371
372 #[serde(default)]
373 pub avatars: IndexMap<Uuid, Avatar>,
374
375 #[serde(default = "default_palette_material_slots")]
376 pub palette_materials: Vec<PaletteMaterial>,
377}
378
379impl Default for Project {
380 fn default() -> Self {
381 Self::new()
382 }
383}
384
385impl Project {
386 pub fn new() -> Self {
387 let region = Region::default();
388
389 Self {
390 name: String::new(),
391
392 regions: vec![region],
393 tilemaps: vec![],
394
395 tiles: IndexMap::default(),
396 tile_groups: IndexMap::default(),
397 tile_node_groups: IndexMap::default(),
398 builder_graphs: IndexMap::default(),
399 tile_collections: IndexMap::default(),
400 tile_board_tiles: IndexMap::default(),
401 tile_board_groups: IndexMap::default(),
402 tile_board_empty_slots: Vec::new(),
403 tile_board_cols: default_tile_board_cols(),
404 tile_board_rows: default_tile_board_rows(),
405
406 time: TheTime::default(),
407
408 characters: IndexMap::default(),
409 items: IndexMap::default(),
410
411 screens: IndexMap::default(),
412 assets: IndexMap::default(),
413
414 palette: ThePalette::default(),
415
416 target_fps: default_target_fps(),
417 tick_ms: default_tick_ms(),
418
419 avatars: IndexMap::default(),
420 palette_materials: default_palette_material_slots(),
421
422 config: String::new(),
423 rules: default_rules(),
424 locales: default_locales(),
425 audio_fx: default_audio_fx(),
426 authoring: default_authoring(),
427 }
428 }
429
430 pub fn add_character(&mut self, character: Character) {
432 self.characters.insert(character.id, character);
433 }
434
435 pub fn ensure_palette_materials_len(&mut self) {
436 if self.palette_materials.len() < self.palette.colors.len() {
437 self.palette_materials
438 .resize(self.palette.colors.len(), PaletteMaterial::default());
439 } else if self.palette_materials.len() > self.palette.colors.len() {
440 self.palette_materials.truncate(self.palette.colors.len());
441 }
442 }
443
444 pub fn reset_palette_material(&mut self, index: usize) {
445 self.ensure_palette_materials_len();
446 if let Some(material) = self.palette_materials.get_mut(index) {
447 *material = PaletteMaterial::default();
448 }
449 }
450
451 pub fn reset_all_palette_materials(&mut self) {
452 self.palette_materials = default_palette_material_slots();
453 self.ensure_palette_materials_len();
454 }
455
456 pub fn remove_character(&mut self, id: &Uuid) {
458 self.characters.shift_remove(id);
459 }
460
461 pub fn sorted_character_list(&self) -> Vec<(Uuid, String)> {
463 let mut entries: Vec<(Uuid, String)> = self
464 .characters
465 .iter()
466 .map(|(uuid, data)| (*uuid, data.name.clone()))
467 .collect();
468
469 entries.sort_by(|a, b| a.1.cmp(&b.1));
470 entries
471 }
472
473 pub fn sorted_item_list(&self) -> Vec<(Uuid, String)> {
475 let mut entries: Vec<(Uuid, String)> = self
476 .items
477 .iter()
478 .map(|(uuid, data)| (*uuid, data.name.clone()))
479 .collect();
480
481 entries.sort_by(|a, b| a.1.cmp(&b.1));
482 entries
483 }
484
485 pub fn add_avatar(&mut self, avatar: Avatar) {
487 self.avatars.insert(avatar.id, avatar);
488 }
489
490 pub fn add_tile_group(&mut self, tile_group: rusterix::TileGroup) {
491 self.tile_groups.insert(tile_group.id, tile_group);
492 }
493
494 pub fn add_tile_node_group(&mut self, node_group: NodeGroupAsset) {
495 self.tile_node_groups
496 .insert(node_group.group_id, node_group);
497 }
498
499 pub fn add_builder_graph(&mut self, builder_graph: BuilderGraphAsset) {
500 self.builder_graphs.insert(builder_graph.id, builder_graph);
501 }
502
503 pub fn add_tile_collection(&mut self, collection: TileCollectionAsset) {
504 self.tile_collections.insert(collection.id, collection);
505 }
506
507 pub fn is_tile_node_group(&self, id: &Uuid) -> bool {
508 self.tile_node_groups.contains_key(id)
509 }
510
511 pub fn collection_contains_source(
512 &self,
513 collection_id: &Uuid,
514 source: rusterix::TileSource,
515 ) -> bool {
516 self.tile_collections
517 .get(collection_id)
518 .map(|collection| {
519 collection
520 .entries
521 .iter()
522 .any(|entry| entry.matches_source(source))
523 })
524 .unwrap_or(false)
525 }
526
527 pub fn add_source_to_collection(&mut self, collection_id: &Uuid, source: rusterix::TileSource) {
528 let Some(collection) = self.tile_collections.get_mut(collection_id) else {
529 return;
530 };
531 let entry = match source {
532 rusterix::TileSource::SingleTile(id) => TileCollectionEntry::SingleTile(id),
533 rusterix::TileSource::TileGroup(id) => TileCollectionEntry::TileGroup(id),
534 _ => return,
535 };
536 if !collection.entries.contains(&entry) {
537 collection.entries.push(entry);
538 }
539 }
540
541 pub fn remove_source_from_collections(&mut self, source: rusterix::TileSource) {
542 for collection in self.tile_collections.values_mut() {
543 collection
544 .entries
545 .retain(|entry| !entry.matches_source(source));
546 match source {
547 rusterix::TileSource::SingleTile(id) => {
548 collection.tile_board_tiles.shift_remove(&id);
549 }
550 rusterix::TileSource::TileGroup(id) => {
551 collection.tile_board_groups.shift_remove(&id);
552 }
553 _ => {}
554 }
555 }
556 }
557
558 pub fn remove_tile_group(&mut self, id: &Uuid) {
559 self.tile_groups.shift_remove(id);
560 self.tile_node_groups.shift_remove(id);
561 self.tile_board_groups.shift_remove(id);
562 self.remove_source_from_collections(rusterix::TileSource::TileGroup(*id));
563 }
564
565 pub fn tile_board_position(&self, source: rusterix::TileSource) -> Option<Vec2<i32>> {
566 match source {
567 rusterix::TileSource::SingleTile(id) => self.tile_board_tiles.get(&id).copied(),
568 rusterix::TileSource::TileGroup(id) => self.tile_board_groups.get(&id).copied(),
569 _ => None,
570 }
571 }
572
573 pub fn collection_tile_board_position(
574 &self,
575 collection_id: &Uuid,
576 source: rusterix::TileSource,
577 ) -> Option<Vec2<i32>> {
578 let collection = self.tile_collections.get(collection_id)?;
579 match source {
580 rusterix::TileSource::SingleTile(id) => collection.tile_board_tiles.get(&id).copied(),
581 rusterix::TileSource::TileGroup(id) => collection.tile_board_groups.get(&id).copied(),
582 _ => None,
583 }
584 }
585
586 pub fn tile_board_empty_slots(&self) -> &[Vec2<i32>] {
587 &self.tile_board_empty_slots
588 }
589
590 pub fn collection_tile_board_empty_slots(&self, collection_id: &Uuid) -> Option<&[Vec2<i32>]> {
591 self.tile_collections
592 .get(collection_id)
593 .map(|collection| collection.tile_board_empty_slots.as_slice())
594 }
595
596 pub fn set_tile_board_position(&mut self, source: rusterix::TileSource, pos: Vec2<i32>) {
597 self.clear_tile_board_empty_slot(pos);
598 match source {
599 rusterix::TileSource::SingleTile(id) => {
600 self.tile_board_tiles.insert(id, pos);
601 }
602 rusterix::TileSource::TileGroup(id) => {
603 self.tile_board_groups.insert(id, pos);
604 }
605 _ => {}
606 }
607 }
608
609 pub fn set_collection_tile_board_position(
610 &mut self,
611 collection_id: &Uuid,
612 source: rusterix::TileSource,
613 pos: Vec2<i32>,
614 ) {
615 let Some(collection) = self.tile_collections.get_mut(collection_id) else {
616 return;
617 };
618 if let Some(index) = collection
619 .tile_board_empty_slots
620 .iter()
621 .position(|p| *p == pos)
622 {
623 collection.tile_board_empty_slots.swap_remove(index);
624 }
625 match source {
626 rusterix::TileSource::SingleTile(id) => {
627 collection.tile_board_tiles.insert(id, pos);
628 }
629 rusterix::TileSource::TileGroup(id) => {
630 collection.tile_board_groups.insert(id, pos);
631 }
632 _ => {}
633 }
634 }
635
636 pub fn reserve_tile_board_empty_slot(&mut self, pos: Vec2<i32>) {
637 if !self.tile_board_empty_slots.contains(&pos) {
638 self.tile_board_empty_slots.push(pos);
639 }
640 }
641
642 pub fn reserve_collection_tile_board_empty_slot(
643 &mut self,
644 collection_id: &Uuid,
645 pos: Vec2<i32>,
646 ) {
647 let Some(collection) = self.tile_collections.get_mut(collection_id) else {
648 return;
649 };
650 if !collection.tile_board_empty_slots.contains(&pos) {
651 collection.tile_board_empty_slots.push(pos);
652 }
653 }
654
655 pub fn clear_tile_board_empty_slot(&mut self, pos: Vec2<i32>) {
656 if let Some(index) = self.tile_board_empty_slots.iter().position(|p| *p == pos) {
657 self.tile_board_empty_slots.swap_remove(index);
658 }
659 }
660
661 pub fn ensure_tile_board_space(&mut self, pos: Vec2<i32>) {
662 if pos.x >= self.tile_board_cols - 1 {
663 self.tile_board_cols = pos.x + 2;
664 }
665 if pos.y >= self.tile_board_rows - 1 {
666 self.tile_board_rows = pos.y + 2;
667 }
668 }
669
670 pub fn remove_avatar(&mut self, id: &Uuid) {
672 self.avatars.shift_remove(id);
673 }
674
675 pub fn find_avatar_for_animation(&self, animation_id: &Uuid) -> Option<&Avatar> {
677 self.avatars
678 .values()
679 .find(|a| a.animations.iter().any(|anim| anim.id == *animation_id))
680 }
681
682 pub fn get_editing_texture(
684 &self,
685 editing_ctx: &PixelEditingContext,
686 ) -> Option<&rusterix::Texture> {
687 match editing_ctx {
688 PixelEditingContext::None => None,
689 PixelEditingContext::Tile(tile_id, frame_index) => {
690 let tile = self.tiles.get(tile_id)?;
691 tile.textures.get(*frame_index)
692 }
693 PixelEditingContext::AvatarFrame(
694 avatar_id,
695 anim_id,
696 perspective_index,
697 frame_index,
698 ) => {
699 let avatar = self.avatars.get(avatar_id)?;
700 let anim = avatar.animations.iter().find(|a| a.id == *anim_id)?;
701 let perspective = anim.perspectives.get(*perspective_index)?;
702 perspective.frames.get(*frame_index).map(|f| &f.texture)
703 }
704 }
705 }
706
707 pub fn get_editing_texture_mut(
709 &mut self,
710 editing_ctx: &PixelEditingContext,
711 ) -> Option<&mut rusterix::Texture> {
712 match editing_ctx {
713 PixelEditingContext::None => None,
714 PixelEditingContext::Tile(tile_id, frame_index) => {
715 let tile = self.tiles.get_mut(tile_id)?;
716 tile.textures.get_mut(*frame_index)
717 }
718 PixelEditingContext::AvatarFrame(
719 avatar_id,
720 anim_id,
721 perspective_index,
722 frame_index,
723 ) => {
724 let avatar = self.avatars.get_mut(avatar_id)?;
725 let anim = avatar.animations.iter_mut().find(|a| a.id == *anim_id)?;
726 let perspective = anim.perspectives.get_mut(*perspective_index)?;
727 perspective
728 .frames
729 .get_mut(*frame_index)
730 .map(|f| &mut f.texture)
731 }
732 }
733 }
734
735 pub fn get_editing_avatar_frame(
737 &self,
738 editing_ctx: &PixelEditingContext,
739 ) -> Option<&rusterix::AvatarAnimationFrame> {
740 match editing_ctx {
741 PixelEditingContext::AvatarFrame(
742 avatar_id,
743 anim_id,
744 perspective_index,
745 frame_index,
746 ) => {
747 let avatar = self.avatars.get(avatar_id)?;
748 let anim = avatar.animations.iter().find(|a| a.id == *anim_id)?;
749 let perspective = anim.perspectives.get(*perspective_index)?;
750 perspective.frames.get(*frame_index)
751 }
752 _ => None,
753 }
754 }
755
756 pub fn get_editing_avatar_frame_mut(
758 &mut self,
759 editing_ctx: &PixelEditingContext,
760 ) -> Option<&mut rusterix::AvatarAnimationFrame> {
761 match editing_ctx {
762 PixelEditingContext::AvatarFrame(
763 avatar_id,
764 anim_id,
765 perspective_index,
766 frame_index,
767 ) => {
768 let avatar = self.avatars.get_mut(avatar_id)?;
769 let anim = avatar.animations.iter_mut().find(|a| a.id == *anim_id)?;
770 let perspective = anim.perspectives.get_mut(*perspective_index)?;
771 perspective.frames.get_mut(*frame_index)
772 }
773 _ => None,
774 }
775 }
776
777 pub fn get_editing_avatar_perspective(
779 &self,
780 editing_ctx: &PixelEditingContext,
781 ) -> Option<&rusterix::AvatarPerspective> {
782 match editing_ctx {
783 PixelEditingContext::AvatarFrame(avatar_id, anim_id, perspective_index, _) => {
784 let avatar = self.avatars.get(avatar_id)?;
785 let anim = avatar.animations.iter().find(|a| a.id == *anim_id)?;
786 anim.perspectives.get(*perspective_index)
787 }
788 _ => None,
789 }
790 }
791
792 pub fn get_editing_avatar_perspective_mut(
794 &mut self,
795 editing_ctx: &PixelEditingContext,
796 ) -> Option<&mut rusterix::AvatarPerspective> {
797 match editing_ctx {
798 PixelEditingContext::AvatarFrame(avatar_id, anim_id, perspective_index, _) => {
799 let avatar = self.avatars.get_mut(avatar_id)?;
800 let anim = avatar.animations.iter_mut().find(|a| a.id == *anim_id)?;
801 anim.perspectives.get_mut(*perspective_index)
802 }
803 _ => None,
804 }
805 }
806
807 pub fn add_item(&mut self, item: Item) {
809 self.items.insert(item.id, item);
810 }
811
812 pub fn remove_item(&mut self, id: &Uuid) {
814 self.items.shift_remove(id);
815 }
816
817 pub fn add_tilemap(&mut self, tilemap: Tilemap) {
819 self.tilemaps.push(tilemap)
820 }
821
822 pub fn get_tilemap(&self, uuid: Uuid) -> Option<&Tilemap> {
824 self.tilemaps.iter().find(|t| t.id == uuid)
825 }
826
827 pub fn get_tilemap_mut(&mut self, uuid: Uuid) -> Option<&mut Tilemap> {
829 self.tilemaps.iter_mut().find(|t| t.id == uuid)
830 }
831
832 pub fn remove_tilemap(&mut self, id: TheId) {
834 self.tilemaps.retain(|item| item.id != id.uuid);
835 }
836
837 pub fn contains_region(&self, uuid: &Uuid) -> bool {
839 self.regions.iter().find(|t| t.id == *uuid).is_some()
840 }
841
842 pub fn get_region(&self, uuid: &Uuid) -> Option<&Region> {
844 self.regions.iter().find(|t| t.id == *uuid)
845 }
846
847 pub fn get_region_mut(&mut self, uuid: &Uuid) -> Option<&mut Region> {
849 self.regions.iter_mut().find(|t| t.id == *uuid)
850 }
851
852 pub fn get_region_ctx(&self, ctx: &ServerContext) -> Option<&Region> {
854 self.regions.iter().find(|t| t.id == ctx.curr_region)
855 }
856
857 pub fn get_region_ctx_mut(&mut self, ctx: &ServerContext) -> Option<&mut Region> {
859 self.regions.iter_mut().find(|t| t.id == ctx.curr_region)
860 }
861
862 pub fn get_screen_ctx(&self, ctx: &ServerContext) -> Option<&Screen> {
864 self.screens.get(&ctx.curr_screen)
865 }
866
867 pub fn get_screen_ctx_mut(&mut self, ctx: &ServerContext) -> Option<&mut Screen> {
869 self.screens.get_mut(&ctx.curr_screen)
870 }
871
872 pub fn remove_region(&mut self, id: &Uuid) {
874 self.regions.retain(|item| item.id != *id);
875 }
876
877 pub fn get_map(&self, ctx: &ServerContext) -> Option<&Map> {
879 if ctx.editor_view_mode != EditorViewMode::D2 {
880 if let Some(region) = self.get_region(&ctx.curr_region) {
881 if ctx.geometry_edit_mode == GeometryEditMode::Detail {
882 if let Some(surface) = ctx.active_detail_surface.as_ref() {
883 if let Some(surface) = region.map.surfaces.get(&surface.id) {
884 if let Some(profile_id) = surface.profile {
885 return region.map.profiles.get(&profile_id);
886 }
887 }
888 }
889 }
890 return Some(®ion.map);
891 }
892 } else if ctx.get_map_context() == MapContext::Region {
893 let id = ctx.curr_region;
894 if let Some(surface) = &ctx.editing_surface {
896 if let Some(region) = self.regions.iter().find(|t| t.id == id) {
897 if let Some(surface) = region.map.surfaces.get(&surface.id) {
898 if let Some(profile_id) = surface.profile {
899 return region.map.profiles.get(&profile_id);
900 }
901 }
902 }
903 return None;
904 } else if let Some(region) = self.regions.iter().find(|t| t.id == id) {
905 return Some(®ion.map);
906 }
907 } else if ctx.get_map_context() == MapContext::Screen {
909 if let Some(id) = ctx.pc.id() {
910 if let Some(screen) = self.screens.get(&id) {
911 return Some(&screen.map);
912 }
913 }
914 } else if ctx.get_map_context() == MapContext::Character {
915 if let ContentContext::CharacterTemplate(id) = ctx.curr_character {
916 if let Some(character) = self.characters.get(&id) {
917 return Some(&character.map);
918 }
919 }
920 } else if ctx.get_map_context() == MapContext::Item {
921 if let ContentContext::ItemTemplate(id) = ctx.curr_item {
922 if let Some(item) = self.items.get(&id) {
923 return Some(&item.map);
924 }
925 }
926 }
927 None
928 }
929
930 pub fn get_map_mut(&mut self, ctx: &ServerContext) -> Option<&mut Map> {
932 if ctx.get_map_context() == MapContext::Region {
933 let id = ctx.curr_region;
934 if ctx.editor_view_mode != EditorViewMode::D2 {
936 if let Some(region) = self.get_region_mut(&ctx.curr_region) {
937 if ctx.geometry_edit_mode == GeometryEditMode::Detail {
938 if let Some(surface) = ctx.active_detail_surface.as_ref() {
939 if let Some(surface) = region.map.surfaces.get_mut(&surface.id) {
940 if let Some(profile_id) = surface.profile {
941 return region.map.profiles.get_mut(&profile_id);
942 }
943 }
944 }
945 }
946 return Some(&mut region.map);
947 }
948 } else if let Some(surface) = &ctx.editing_surface {
949 if let Some(region) = self.regions.iter_mut().find(|t| t.id == id) {
950 if let Some(surface) = region.map.surfaces.get_mut(&surface.id) {
951 if let Some(profile_id) = surface.profile {
952 return region.map.profiles.get_mut(&profile_id);
953 }
954 }
955 }
956 return None;
957 } else if let Some(region) = self.regions.iter_mut().find(|t| t.id == id) {
958 return Some(&mut region.map);
959 }
960 } else if ctx.get_map_context() == MapContext::Screen {
962 if let Some(id) = ctx.pc.id() {
963 if let Some(screen) = self.screens.get_mut(&id) {
964 return Some(&mut screen.map);
965 }
966 }
967 } else if ctx.get_map_context() == MapContext::Character {
968 if let ContentContext::CharacterTemplate(id) = ctx.curr_character {
969 if let Some(character) = self.characters.get_mut(&id) {
970 return Some(&mut character.map);
971 }
972 }
973 } else if ctx.get_map_context() == MapContext::Item {
974 if let ContentContext::ItemTemplate(id) = ctx.curr_item {
975 if let Some(item) = self.items.get_mut(&id) {
976 return Some(&mut item.map);
977 }
978 }
979 }
980 None
981 }
982
983 pub fn add_screen(&mut self, screen: Screen) {
985 self.screens.insert(screen.id, screen);
986 }
987
988 pub fn remove_screen(&mut self, id: &Uuid) {
990 self.screens.shift_remove(id);
991 }
992
993 pub fn sorted_screens_list(&self) -> Vec<(Uuid, String)> {
995 let mut entries: Vec<(Uuid, String)> = self
996 .screens
997 .iter()
998 .map(|(uuid, data)| (*uuid, data.name.clone()))
999 .collect();
1000
1001 entries.sort_by(|a, b| a.1.cmp(&b.1));
1002 entries
1003 }
1004
1005 pub fn add_asset(&mut self, asset: Asset) {
1007 self.assets.insert(asset.id, asset);
1008 }
1009
1010 pub fn remove_asset(&mut self, id: &Uuid) {
1012 self.assets.shift_remove(id);
1013 }
1014
1015 pub fn sorted_assets_list(&self) -> Vec<(Uuid, String)> {
1017 let mut entries: Vec<(Uuid, String)> = self
1018 .assets
1019 .iter()
1020 .map(|(uuid, data)| (*uuid, data.name.clone()))
1021 .collect();
1022
1023 entries.sort_by(|a, b| a.1.cmp(&b.1));
1024 entries
1025 }
1026
1027 pub fn remove_tile(&mut self, id: &Uuid) {
1029 for tilemap in &mut self.tilemaps {
1030 tilemap.tiles.retain(|t| t.id != *id);
1031 }
1032 self.tiles.shift_remove(id);
1033 }
1034
1035 pub fn get_tile(&self, id: &Uuid) -> Option<&Tile> {
1037 for tilemap in &self.tilemaps {
1038 for tile in &tilemap.tiles {
1039 if tile.id == *id {
1040 return Some(tile);
1041 }
1042 }
1043 }
1044 None
1045 }
1046
1047 pub fn get_tile_mut(&mut self, id: &Uuid) -> Option<&mut Tile> {
1049 for tilemap in &mut self.tilemaps {
1050 for tile in &mut tilemap.tiles {
1051 if tile.id == *id {
1052 return Some(tile);
1053 }
1054 }
1055 }
1056 None
1057 }
1058
1059 pub fn extract_tiles(&self) -> IndexMap<Uuid, TheRGBATile> {
1061 let mut tiles = IndexMap::default();
1062 for tilemap in &self.tilemaps {
1063 for tile in &tilemap.tiles {
1064 let mut rgba_tile = TheRGBATile::new();
1065 rgba_tile.id = tile.id;
1066 rgba_tile.name.clone_from(&tile.name);
1067 rgba_tile.buffer = tilemap.buffer.extract_sequence(&tile.sequence);
1068 rgba_tile.role = tile.role as u8;
1069 rgba_tile.scale = tile.scale;
1070 rgba_tile.render_mode = tile.render_mode;
1071 rgba_tile.blocking = tile.blocking;
1072 tiles.insert(tile.id, rgba_tile);
1073 }
1074 }
1075 tiles
1076 }
1077
1078 pub fn extract_tiles_vec(&self) -> Vec<TheRGBATile> {
1080 let mut tiles = vec![];
1081 for tilemap in &self.tilemaps {
1082 for tile in &tilemap.tiles {
1083 let mut rgba_tile = TheRGBATile::new();
1084 rgba_tile.id = tile.id;
1085 rgba_tile.name.clone_from(&tile.name);
1086 rgba_tile.buffer = tilemap.buffer.extract_sequence(&tile.sequence);
1087 rgba_tile.role = tile.role as u8;
1088 tiles.push(rgba_tile);
1089 }
1090 }
1091 tiles
1092 }
1093
1094 pub fn extract_tile(&self, id: &Uuid) -> Option<TheRGBATile> {
1096 for tilemap in &self.tilemaps {
1097 for tile in &tilemap.tiles {
1098 if tile.id == *id {
1099 let mut rgba_tile = TheRGBATile::new();
1100 rgba_tile.id = tile.id;
1101 rgba_tile.name.clone_from(&tile.name);
1102 rgba_tile.buffer = tilemap.buffer.extract_sequence(&tile.sequence);
1103 rgba_tile.role = tile.role as u8;
1104 return Some(rgba_tile);
1105 }
1106 }
1107 }
1108 None
1109 }
1110}