Skip to main content

eldiron_shared/
project.rs

1use crate::prelude::*;
2use indexmap::IndexMap;
3pub use rusterix::map::*;
4use theframework::prelude::*;
5
6/// The default target fps for the game.
7fn default_target_fps() -> u32 {
8    30
9}
10
11/// The default ms per tick for the game.
12fn 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    /// Tiles in the project
35    #[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    /// Add Character
118    pub fn add_character(&mut self, character: Character) {
119        self.characters.insert(character.id, character);
120    }
121
122    /// Removes the given character from the project.
123    pub fn remove_character(&mut self, id: &Uuid) {
124        self.characters.shift_remove(id);
125    }
126
127    /// Returns a list of all characters sorted by name.
128    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    /// Returns a list of all items sorted by name.
140    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    /// Add Avatar
152    pub fn add_avatar(&mut self, avatar: Avatar) {
153        self.avatars.insert(avatar.id, avatar);
154    }
155
156    /// Removes the given avatar from the project.
157    pub fn remove_avatar(&mut self, id: &Uuid) {
158        self.avatars.shift_remove(id);
159    }
160
161    /// Finds the avatar that contains the given animation id.
162    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    /// Returns an immutable reference to the texture identified by the editing context.
169    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    /// Returns a mutable reference to the texture identified by the editing context.
194    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    /// Returns an immutable avatar frame for avatar frame editing contexts.
222    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    /// Returns a mutable avatar frame for avatar frame editing contexts.
243    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    /// Returns an immutable avatar perspective for avatar frame editing contexts.
264    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    /// Returns a mutable avatar perspective for avatar frame editing contexts.
279    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    /// Add Item
294    pub fn add_item(&mut self, item: Item) {
295        self.items.insert(item.id, item);
296    }
297
298    /// Removes the given item from the project.
299    pub fn remove_item(&mut self, id: &Uuid) {
300        self.items.shift_remove(id);
301    }
302
303    /// Add a tilemap
304    pub fn add_tilemap(&mut self, tilemap: Tilemap) {
305        self.tilemaps.push(tilemap)
306    }
307
308    /// Get the tilemap of the given uuid.
309    pub fn get_tilemap(&self, uuid: Uuid) -> Option<&Tilemap> {
310        self.tilemaps.iter().find(|t| t.id == uuid)
311    }
312
313    /// Get the tilemap of the given uuid.
314    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    /// Removes the given tilemap from the project.
319    pub fn remove_tilemap(&mut self, id: TheId) {
320        self.tilemaps.retain(|item| item.id != id.uuid);
321    }
322
323    /// Contains the region of the given uuid.
324    pub fn contains_region(&self, uuid: &Uuid) -> bool {
325        self.regions.iter().find(|t| t.id == *uuid).is_some()
326    }
327
328    /// Get the region of the given uuid.
329    pub fn get_region(&self, uuid: &Uuid) -> Option<&Region> {
330        self.regions.iter().find(|t| t.id == *uuid)
331    }
332
333    /// Get the region of the given uuid as mutable.
334    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    /// Get the region of the given uuid.
339    pub fn get_region_ctx(&self, ctx: &ServerContext) -> Option<&Region> {
340        self.regions.iter().find(|t| t.id == ctx.curr_region)
341    }
342
343    /// Get the region of the given uuid as mutable.
344    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    /// Get the screen of the given uuid.
349    pub fn get_screen_ctx(&self, ctx: &ServerContext) -> Option<&Screen> {
350        self.screens.get(&ctx.curr_screen)
351    }
352
353    /// Get the mut screen of the given uuid.
354    pub fn get_screen_ctx_mut(&mut self, ctx: &ServerContext) -> Option<&mut Screen> {
355        self.screens.get_mut(&ctx.curr_screen)
356    }
357
358    /// Remove a region
359    pub fn remove_region(&mut self, id: &Uuid) {
360        self.regions.retain(|item| item.id != *id);
361    }
362
363    /// Get the map of the current context.
364    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(&region.map);
368            }
369        } else if ctx.get_map_context() == MapContext::Region {
370            let id = ctx.curr_region;
371            // if let Some(id) = ctx.pc.id() {
372            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(&region.map);
383            }
384            // }
385        } 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    /// Get the mutable map of the current context.
408    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 let Some(id) = ctx.pc.id() {
412            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            // }
429        } 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    /// Add Screen
452    pub fn add_screen(&mut self, screen: Screen) {
453        self.screens.insert(screen.id, screen);
454    }
455
456    /// Removes the given code from the project.
457    pub fn remove_screen(&mut self, id: &Uuid) {
458        self.screens.shift_remove(id);
459    }
460
461    /// Returns a list of all screens sorted by name.
462    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    /// Add an asset
474    pub fn add_asset(&mut self, asset: Asset) {
475        self.assets.insert(asset.id, asset);
476    }
477
478    /// Removes the given code from the project.
479    pub fn remove_asset(&mut self, id: &Uuid) {
480        self.assets.shift_remove(id);
481    }
482
483    /// Returns a list of all assets sorted by name.
484    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    /// Removes the given tile from the project.
496    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    /// Gets the given tile from the project.
504    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    /// Gets the given mutable tile from the project.
516    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    /// Extract all tiles from all tilemaps and store them in a hash.
528    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    /// Extract all tiles from all tilemaps and store them in a vec.
547    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    /// Extract the given tile from the tilemaps.
563    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}