1use crate::prelude::*;
2use indexmap::IndexMap;
3pub use rusterix::map::*;
4use theframework::prelude::*;
5
6fn default_target_fps() -> u32 {
8 30
9}
10
11fn default_tick_ms() -> u32 {
13 250
14}
15
16fn default_rules() -> String {
17 String::new()
18}
19
20fn default_locales() -> String {
21 String::new()
22}
23
24fn default_audio_fx() -> String {
25 String::new()
26}
27
28#[derive(Serialize, Deserialize, Clone, Debug)]
29pub struct Project {
30 pub name: String,
31 pub regions: Vec<Region>,
32 pub tilemaps: Vec<Tilemap>,
33
34 #[serde(default)]
36 pub tiles: IndexMap<Uuid, rusterix::Tile>,
37
38 #[serde(default)]
39 pub time: TheTime,
40
41 #[serde(default)]
42 pub characters: IndexMap<Uuid, Character>,
43 #[serde(default)]
44 pub items: IndexMap<Uuid, Item>,
45
46 #[serde(default)]
47 pub screens: IndexMap<Uuid, Screen>,
48
49 #[serde(default)]
50 pub assets: IndexMap<Uuid, Asset>,
51
52 #[serde(default)]
53 pub palette: ThePalette,
54
55 #[serde(default = "default_target_fps")]
56 pub target_fps: u32,
57
58 #[serde(default = "default_tick_ms")]
59 pub tick_ms: u32,
60
61 #[serde(default)]
62 pub config: String,
63
64 #[serde(default = "default_rules")]
65 pub rules: String,
66
67 #[serde(default = "default_locales")]
68 pub locales: String,
69
70 #[serde(default = "default_audio_fx")]
71 pub audio_fx: String,
72
73 #[serde(default)]
74 pub avatars: IndexMap<Uuid, Avatar>,
75}
76
77impl Default for Project {
78 fn default() -> Self {
79 Self::new()
80 }
81}
82
83impl Project {
84 pub fn new() -> Self {
85 let region = Region::default();
86
87 Self {
88 name: String::new(),
89
90 regions: vec![region],
91 tilemaps: vec![],
92
93 tiles: IndexMap::default(),
94
95 time: TheTime::default(),
96
97 characters: IndexMap::default(),
98 items: IndexMap::default(),
99
100 screens: IndexMap::default(),
101 assets: IndexMap::default(),
102
103 palette: ThePalette::default(),
104
105 target_fps: default_target_fps(),
106 tick_ms: default_tick_ms(),
107
108 avatars: IndexMap::default(),
109
110 config: String::new(),
111 rules: default_rules(),
112 locales: default_locales(),
113 audio_fx: default_audio_fx(),
114 }
115 }
116
117 pub fn add_character(&mut self, character: Character) {
119 self.characters.insert(character.id, character);
120 }
121
122 pub fn remove_character(&mut self, id: &Uuid) {
124 self.characters.shift_remove(id);
125 }
126
127 pub fn sorted_character_list(&self) -> Vec<(Uuid, String)> {
129 let mut entries: Vec<(Uuid, String)> = self
130 .characters
131 .iter()
132 .map(|(uuid, data)| (*uuid, data.name.clone()))
133 .collect();
134
135 entries.sort_by(|a, b| a.1.cmp(&b.1));
136 entries
137 }
138
139 pub fn sorted_item_list(&self) -> Vec<(Uuid, String)> {
141 let mut entries: Vec<(Uuid, String)> = self
142 .items
143 .iter()
144 .map(|(uuid, data)| (*uuid, data.name.clone()))
145 .collect();
146
147 entries.sort_by(|a, b| a.1.cmp(&b.1));
148 entries
149 }
150
151 pub fn add_avatar(&mut self, avatar: Avatar) {
153 self.avatars.insert(avatar.id, avatar);
154 }
155
156 pub fn remove_avatar(&mut self, id: &Uuid) {
158 self.avatars.shift_remove(id);
159 }
160
161 pub fn find_avatar_for_animation(&self, animation_id: &Uuid) -> Option<&Avatar> {
163 self.avatars
164 .values()
165 .find(|a| a.animations.iter().any(|anim| anim.id == *animation_id))
166 }
167
168 pub fn get_editing_texture(
170 &self,
171 editing_ctx: &PixelEditingContext,
172 ) -> Option<&rusterix::Texture> {
173 match editing_ctx {
174 PixelEditingContext::None => None,
175 PixelEditingContext::Tile(tile_id, frame_index) => {
176 let tile = self.tiles.get(tile_id)?;
177 tile.textures.get(*frame_index)
178 }
179 PixelEditingContext::AvatarFrame(
180 avatar_id,
181 anim_id,
182 perspective_index,
183 frame_index,
184 ) => {
185 let avatar = self.avatars.get(avatar_id)?;
186 let anim = avatar.animations.iter().find(|a| a.id == *anim_id)?;
187 let perspective = anim.perspectives.get(*perspective_index)?;
188 perspective.frames.get(*frame_index).map(|f| &f.texture)
189 }
190 }
191 }
192
193 pub fn get_editing_texture_mut(
195 &mut self,
196 editing_ctx: &PixelEditingContext,
197 ) -> Option<&mut rusterix::Texture> {
198 match editing_ctx {
199 PixelEditingContext::None => None,
200 PixelEditingContext::Tile(tile_id, frame_index) => {
201 let tile = self.tiles.get_mut(tile_id)?;
202 tile.textures.get_mut(*frame_index)
203 }
204 PixelEditingContext::AvatarFrame(
205 avatar_id,
206 anim_id,
207 perspective_index,
208 frame_index,
209 ) => {
210 let avatar = self.avatars.get_mut(avatar_id)?;
211 let anim = avatar.animations.iter_mut().find(|a| a.id == *anim_id)?;
212 let perspective = anim.perspectives.get_mut(*perspective_index)?;
213 perspective
214 .frames
215 .get_mut(*frame_index)
216 .map(|f| &mut f.texture)
217 }
218 }
219 }
220
221 pub fn get_editing_avatar_frame(
223 &self,
224 editing_ctx: &PixelEditingContext,
225 ) -> Option<&rusterix::AvatarAnimationFrame> {
226 match editing_ctx {
227 PixelEditingContext::AvatarFrame(
228 avatar_id,
229 anim_id,
230 perspective_index,
231 frame_index,
232 ) => {
233 let avatar = self.avatars.get(avatar_id)?;
234 let anim = avatar.animations.iter().find(|a| a.id == *anim_id)?;
235 let perspective = anim.perspectives.get(*perspective_index)?;
236 perspective.frames.get(*frame_index)
237 }
238 _ => None,
239 }
240 }
241
242 pub fn get_editing_avatar_frame_mut(
244 &mut self,
245 editing_ctx: &PixelEditingContext,
246 ) -> Option<&mut rusterix::AvatarAnimationFrame> {
247 match editing_ctx {
248 PixelEditingContext::AvatarFrame(
249 avatar_id,
250 anim_id,
251 perspective_index,
252 frame_index,
253 ) => {
254 let avatar = self.avatars.get_mut(avatar_id)?;
255 let anim = avatar.animations.iter_mut().find(|a| a.id == *anim_id)?;
256 let perspective = anim.perspectives.get_mut(*perspective_index)?;
257 perspective.frames.get_mut(*frame_index)
258 }
259 _ => None,
260 }
261 }
262
263 pub fn get_editing_avatar_perspective(
265 &self,
266 editing_ctx: &PixelEditingContext,
267 ) -> Option<&rusterix::AvatarPerspective> {
268 match editing_ctx {
269 PixelEditingContext::AvatarFrame(avatar_id, anim_id, perspective_index, _) => {
270 let avatar = self.avatars.get(avatar_id)?;
271 let anim = avatar.animations.iter().find(|a| a.id == *anim_id)?;
272 anim.perspectives.get(*perspective_index)
273 }
274 _ => None,
275 }
276 }
277
278 pub fn get_editing_avatar_perspective_mut(
280 &mut self,
281 editing_ctx: &PixelEditingContext,
282 ) -> Option<&mut rusterix::AvatarPerspective> {
283 match editing_ctx {
284 PixelEditingContext::AvatarFrame(avatar_id, anim_id, perspective_index, _) => {
285 let avatar = self.avatars.get_mut(avatar_id)?;
286 let anim = avatar.animations.iter_mut().find(|a| a.id == *anim_id)?;
287 anim.perspectives.get_mut(*perspective_index)
288 }
289 _ => None,
290 }
291 }
292
293 pub fn add_item(&mut self, item: Item) {
295 self.items.insert(item.id, item);
296 }
297
298 pub fn remove_item(&mut self, id: &Uuid) {
300 self.items.shift_remove(id);
301 }
302
303 pub fn add_tilemap(&mut self, tilemap: Tilemap) {
305 self.tilemaps.push(tilemap)
306 }
307
308 pub fn get_tilemap(&self, uuid: Uuid) -> Option<&Tilemap> {
310 self.tilemaps.iter().find(|t| t.id == uuid)
311 }
312
313 pub fn get_tilemap_mut(&mut self, uuid: Uuid) -> Option<&mut Tilemap> {
315 self.tilemaps.iter_mut().find(|t| t.id == uuid)
316 }
317
318 pub fn remove_tilemap(&mut self, id: TheId) {
320 self.tilemaps.retain(|item| item.id != id.uuid);
321 }
322
323 pub fn contains_region(&self, uuid: &Uuid) -> bool {
325 self.regions.iter().find(|t| t.id == *uuid).is_some()
326 }
327
328 pub fn get_region(&self, uuid: &Uuid) -> Option<&Region> {
330 self.regions.iter().find(|t| t.id == *uuid)
331 }
332
333 pub fn get_region_mut(&mut self, uuid: &Uuid) -> Option<&mut Region> {
335 self.regions.iter_mut().find(|t| t.id == *uuid)
336 }
337
338 pub fn get_region_ctx(&self, ctx: &ServerContext) -> Option<&Region> {
340 self.regions.iter().find(|t| t.id == ctx.curr_region)
341 }
342
343 pub fn get_region_ctx_mut(&mut self, ctx: &ServerContext) -> Option<&mut Region> {
345 self.regions.iter_mut().find(|t| t.id == ctx.curr_region)
346 }
347
348 pub fn get_screen_ctx(&self, ctx: &ServerContext) -> Option<&Screen> {
350 self.screens.get(&ctx.curr_screen)
351 }
352
353 pub fn get_screen_ctx_mut(&mut self, ctx: &ServerContext) -> Option<&mut Screen> {
355 self.screens.get_mut(&ctx.curr_screen)
356 }
357
358 pub fn remove_region(&mut self, id: &Uuid) {
360 self.regions.retain(|item| item.id != *id);
361 }
362
363 pub fn get_map(&self, ctx: &ServerContext) -> Option<&Map> {
365 if ctx.editor_view_mode != EditorViewMode::D2 {
366 if let Some(region) = self.get_region(&ctx.curr_region) {
367 return Some(®ion.map);
368 }
369 } else if ctx.get_map_context() == MapContext::Region {
370 let id = ctx.curr_region;
371 if let Some(surface) = &ctx.editing_surface {
373 if let Some(region) = self.regions.iter().find(|t| t.id == id) {
374 if let Some(surface) = region.map.surfaces.get(&surface.id) {
375 if let Some(profile_id) = surface.profile {
376 return region.map.profiles.get(&profile_id);
377 }
378 }
379 }
380 return None;
381 } else if let Some(region) = self.regions.iter().find(|t| t.id == id) {
382 return Some(®ion.map);
383 }
384 } else if ctx.get_map_context() == MapContext::Screen {
386 if let Some(id) = ctx.pc.id() {
387 if let Some(screen) = self.screens.get(&id) {
388 return Some(&screen.map);
389 }
390 }
391 } else if ctx.get_map_context() == MapContext::Character {
392 if let ContentContext::CharacterTemplate(id) = ctx.curr_character {
393 if let Some(character) = self.characters.get(&id) {
394 return Some(&character.map);
395 }
396 }
397 } else if ctx.get_map_context() == MapContext::Item {
398 if let ContentContext::ItemTemplate(id) = ctx.curr_item {
399 if let Some(item) = self.items.get(&id) {
400 return Some(&item.map);
401 }
402 }
403 }
404 None
405 }
406
407 pub fn get_map_mut(&mut self, ctx: &ServerContext) -> Option<&mut Map> {
409 if ctx.get_map_context() == MapContext::Region {
410 let id = ctx.curr_region;
411 if ctx.editor_view_mode != EditorViewMode::D2 {
413 if let Some(region) = self.get_region_mut(&ctx.curr_region) {
414 return Some(&mut region.map);
415 }
416 } else if let Some(surface) = &ctx.editing_surface {
417 if let Some(region) = self.regions.iter_mut().find(|t| t.id == id) {
418 if let Some(surface) = region.map.surfaces.get_mut(&surface.id) {
419 if let Some(profile_id) = surface.profile {
420 return region.map.profiles.get_mut(&profile_id);
421 }
422 }
423 }
424 return None;
425 } else if let Some(region) = self.regions.iter_mut().find(|t| t.id == id) {
426 return Some(&mut region.map);
427 }
428 } else if ctx.get_map_context() == MapContext::Screen {
430 if let Some(id) = ctx.pc.id() {
431 if let Some(screen) = self.screens.get_mut(&id) {
432 return Some(&mut screen.map);
433 }
434 }
435 } else if ctx.get_map_context() == MapContext::Character {
436 if let ContentContext::CharacterTemplate(id) = ctx.curr_character {
437 if let Some(character) = self.characters.get_mut(&id) {
438 return Some(&mut character.map);
439 }
440 }
441 } else if ctx.get_map_context() == MapContext::Item {
442 if let ContentContext::ItemTemplate(id) = ctx.curr_item {
443 if let Some(item) = self.items.get_mut(&id) {
444 return Some(&mut item.map);
445 }
446 }
447 }
448 None
449 }
450
451 pub fn add_screen(&mut self, screen: Screen) {
453 self.screens.insert(screen.id, screen);
454 }
455
456 pub fn remove_screen(&mut self, id: &Uuid) {
458 self.screens.shift_remove(id);
459 }
460
461 pub fn sorted_screens_list(&self) -> Vec<(Uuid, String)> {
463 let mut entries: Vec<(Uuid, String)> = self
464 .screens
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 add_asset(&mut self, asset: Asset) {
475 self.assets.insert(asset.id, asset);
476 }
477
478 pub fn remove_asset(&mut self, id: &Uuid) {
480 self.assets.shift_remove(id);
481 }
482
483 pub fn sorted_assets_list(&self) -> Vec<(Uuid, String)> {
485 let mut entries: Vec<(Uuid, String)> = self
486 .assets
487 .iter()
488 .map(|(uuid, data)| (*uuid, data.name.clone()))
489 .collect();
490
491 entries.sort_by(|a, b| a.1.cmp(&b.1));
492 entries
493 }
494
495 pub fn remove_tile(&mut self, id: &Uuid) {
497 for tilemap in &mut self.tilemaps {
498 tilemap.tiles.retain(|t| t.id != *id);
499 }
500 self.tiles.shift_remove(id);
501 }
502
503 pub fn get_tile(&self, id: &Uuid) -> Option<&Tile> {
505 for tilemap in &self.tilemaps {
506 for tile in &tilemap.tiles {
507 if tile.id == *id {
508 return Some(tile);
509 }
510 }
511 }
512 None
513 }
514
515 pub fn get_tile_mut(&mut self, id: &Uuid) -> Option<&mut Tile> {
517 for tilemap in &mut self.tilemaps {
518 for tile in &mut tilemap.tiles {
519 if tile.id == *id {
520 return Some(tile);
521 }
522 }
523 }
524 None
525 }
526
527 pub fn extract_tiles(&self) -> IndexMap<Uuid, TheRGBATile> {
529 let mut tiles = IndexMap::default();
530 for tilemap in &self.tilemaps {
531 for tile in &tilemap.tiles {
532 let mut rgba_tile = TheRGBATile::new();
533 rgba_tile.id = tile.id;
534 rgba_tile.name.clone_from(&tile.name);
535 rgba_tile.buffer = tilemap.buffer.extract_sequence(&tile.sequence);
536 rgba_tile.role = tile.role as u8;
537 rgba_tile.scale = tile.scale;
538 rgba_tile.render_mode = tile.render_mode;
539 rgba_tile.blocking = tile.blocking;
540 tiles.insert(tile.id, rgba_tile);
541 }
542 }
543 tiles
544 }
545
546 pub fn extract_tiles_vec(&self) -> Vec<TheRGBATile> {
548 let mut tiles = vec![];
549 for tilemap in &self.tilemaps {
550 for tile in &tilemap.tiles {
551 let mut rgba_tile = TheRGBATile::new();
552 rgba_tile.id = tile.id;
553 rgba_tile.name.clone_from(&tile.name);
554 rgba_tile.buffer = tilemap.buffer.extract_sequence(&tile.sequence);
555 rgba_tile.role = tile.role as u8;
556 tiles.push(rgba_tile);
557 }
558 }
559 tiles
560 }
561
562 pub fn extract_tile(&self, id: &Uuid) -> Option<TheRGBATile> {
564 for tilemap in &self.tilemaps {
565 for tile in &tilemap.tiles {
566 if tile.id == *id {
567 let mut rgba_tile = TheRGBATile::new();
568 rgba_tile.id = tile.id;
569 rgba_tile.name.clone_from(&tile.name);
570 rgba_tile.buffer = tilemap.buffer.extract_sequence(&tile.sequence);
571 rgba_tile.role = tile.role as u8;
572 return Some(rgba_tile);
573 }
574 }
575 }
576 None
577 }
578}