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