Skip to main content

rustapi/undo/
project_atoms.rs

1use crate::prelude::*;
2use theframework::prelude::*;
3
4// #[allow(clippy::large_enum_variant)]
5#[derive(Clone, Debug)]
6pub enum ProjectUndoAtom {
7    MapEdit(ProjectContext, Box<Map>, Box<Map>),
8    AddRegion(Region),
9    RemoveRegion(usize, Region),
10    RenameRegion(Uuid, String, String),
11    AddRegionCharacterInstance(Uuid, Character),
12    RemoveRegionCharacterInstance(usize, Uuid, Character),
13    MoveRegionCharacterInstance(Uuid, Uuid, Vec3<f32>, Vec3<f32>), // region, instance, from, to
14    AddRegionItemInstance(Uuid, Item),
15    RemoveRegionItemInstance(usize, Uuid, Item),
16    MoveRegionItemInstance(Uuid, Uuid, Vec3<f32>, Vec3<f32>), // region, instance, from, to
17    AddCharacter(Character),
18    RemoveCharacter(usize, Character),
19    RenameCharacter(Uuid, String, String),
20    AddItem(Item),
21    RemoveItem(usize, Item),
22    RenameItem(Uuid, String, String),
23    AddTilemap(Tilemap),
24    RemoveTilemap(usize, Tilemap),
25    RenameTilemap(Uuid, String, String),
26    EditTilemapGridSize(Uuid, i32, i32),
27    AddScreen(Screen),
28    RemoveScreen(usize, Screen),
29    RenameScreen(Uuid, String, String),
30    AddAsset(Asset),
31    RemoveAsset(usize, Asset),
32    RenameAsset(Uuid, String, String),
33    AddAvatar(Avatar),
34    RemoveAvatar(usize, Avatar),
35    RenameAvatar(Uuid, String, String),
36    EditAvatarResolution(Uuid, u16, u16),
37    EditAvatarPerspectiveCount(Uuid, AvatarPerspectiveCount, AvatarPerspectiveCount),
38    AddAvatarAnimation(Uuid, AvatarAnimation),
39    RemoveAvatarAnimation(Uuid, usize, AvatarAnimation),
40    RenameAvatarAnimation(Uuid, Uuid, String, String),
41    EditAvatarAnimationFrameCount(Uuid, Uuid, usize, usize),
42    EditAvatarAnimationSpeed(Uuid, Uuid, f32, f32),
43    PaletteEdit(ThePalette, ThePalette),
44    TileEdit(rusterix::Tile, rusterix::Tile),
45}
46
47use ProjectUndoAtom::*;
48
49impl ProjectUndoAtom {
50    /// Returns the ProjectContext for the MapEdit
51    pub fn pc(&self) -> Option<ProjectContext> {
52        match self {
53            MapEdit(pc, _, _) => Some(*pc),
54            _ => None,
55        }
56    }
57
58    /// Descriptive text of the undo atom.
59    pub fn to_string(&self) -> String {
60        match self {
61            MapEdit(_, _, _) => "Map Edit".to_string(),
62            AddRegion(region) => format!("Add Region: {}", region.name),
63            RemoveRegion(_, region) => format!("Remove Region: {}", region.name),
64            RenameRegion(_, old, new) => format!("Rename Region: {} -> {}", old, new),
65            AddRegionCharacterInstance(_, character) => {
66                format!("Add Region Character Instance: {}", character.name)
67            }
68            RemoveRegionCharacterInstance(_, _, character) => {
69                format!("Remove Region Character Instance: {}", character.name)
70            }
71            MoveRegionCharacterInstance(_, _, _, _) => "Move Region Character Instance".into(),
72            AddRegionItemInstance(_, item) => {
73                format!("Add Region Item Instance: {}", item.name)
74            }
75            RemoveRegionItemInstance(_, _, item) => {
76                format!("Remove Region Item Instance: {}", item.name)
77            }
78            MoveRegionItemInstance(_, _, _, _) => "Move Region Item Instance".into(),
79            AddCharacter(character) => format!("Add Character: {}", character.name),
80            RemoveCharacter(_, character) => format!("Remove Character: {}", character.name),
81            RenameCharacter(_, old, new) => format!("Rename Character: {} -> {}", old, new),
82            AddItem(character) => format!("Add Item: {}", character.name),
83            RemoveItem(_, character) => format!("Remove Item: {}", character.name),
84            RenameItem(_, old, new) => format!("Rename Item: {} -> {}", old, new),
85            AddTilemap(tilemap) => format!("Add Tilemap: {}", tilemap.name),
86            RemoveTilemap(_, tilemap) => format!("Remove Tilemap: {}", tilemap.name),
87            RenameTilemap(_, old, new) => format!("Rename Tilemap: {} -> {}", old, new),
88            EditTilemapGridSize(_, old, new) => {
89                format!("Edit Tilemap Grid Size: {} -> {}", old, new)
90            }
91            AddScreen(screen) => format!("Add Screen: {}", screen.name),
92            RemoveScreen(_, screen) => format!("Remove Screen: {}", screen.name),
93            RenameScreen(_, old, new) => format!("Rename Screen: {} -> {}", old, new),
94            AddAsset(asset) => format!("Add Asset: {}", asset.name),
95            RemoveAsset(_, asset) => format!("Remove Asset: {}", asset.name),
96            RenameAsset(_, old, new) => format!("Rename Asset: {} -> {}", old, new),
97            AddAvatar(avatar) => format!("Add Avatar: {}", avatar.name),
98            RemoveAvatar(_, avatar) => format!("Remove Avatar: {}", avatar.name),
99            RenameAvatar(_, old, new) => format!("Rename Avatar: {} -> {}", old, new),
100            EditAvatarResolution(_, old, new) => {
101                format!("Edit Avatar Resolution: {} -> {}", old, new)
102            }
103            EditAvatarPerspectiveCount(_, old, new) => {
104                format!("Edit Avatar Perspectives: {:?} -> {:?}", old, new)
105            }
106            AddAvatarAnimation(_, anim) => format!("Add Animation: {}", anim.name),
107            RemoveAvatarAnimation(_, _, anim) => format!("Remove Animation: {}", anim.name),
108            RenameAvatarAnimation(_, _, old, new) => {
109                format!("Rename Animation: {} -> {}", old, new)
110            }
111            EditAvatarAnimationFrameCount(_, _, old, new) => {
112                format!("Edit Animation Frames: {} -> {}", old, new)
113            }
114            EditAvatarAnimationSpeed(_, _, old, new) => {
115                format!("Edit Animation Speed: {:.2} -> {:.2}", old, new)
116            }
117            PaletteEdit(_old, _new) => format!("Palette Changed"),
118            TileEdit(_old, _new) => format!("Tile Changed"),
119        }
120    }
121
122    pub fn undo(
123        &self,
124        project: &mut Project,
125        ui: &mut TheUI,
126        ctx: &mut TheContext,
127        server_ctx: &mut ServerContext,
128    ) {
129        match self {
130            MapEdit(pc, old, _new) => {
131                set_project_context(ctx, ui, project, server_ctx, *pc);
132                if let Some(map) = project.get_map_mut(server_ctx) {
133                    *map = *old.clone();
134                    map.clear_temp();
135                    if pc.is_region() {
136                        update_region(ctx);
137                        map.update_surfaces();
138                    }
139                }
140            }
141            AddRegion(region) => {
142                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
143                    if let Some(region_node) =
144                        tree_layout.get_node_by_id_mut(&server_ctx.tree_regions_id)
145                    {
146                        project.remove_region(&region.id);
147                        region_node.remove_child_by_uuid(&region.id);
148                    }
149                }
150            }
151            RemoveRegion(index, region) => {
152                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
153                    let mut region = region.clone();
154                    region.map.name = region.name.clone();
155
156                    let mut node = gen_region_tree_node(&region);
157                    node.set_open(true);
158                    if let Some(region_node) =
159                        tree_layout.get_node_by_id_mut(&server_ctx.tree_regions_id)
160                    {
161                        region_node.add_child_at(*index, node);
162                    }
163                    let region_id: Uuid = region.id;
164                    project.regions.insert(*index, region);
165
166                    server_ctx.curr_region = region_id;
167                    set_project_context(
168                        ctx,
169                        ui,
170                        project,
171                        server_ctx,
172                        ProjectContext::Region(region_id),
173                    );
174                    update_region(ctx);
175                }
176            }
177            RenameRegion(id, old, _new) => {
178                if let Some(region) = project.get_region_mut(id) {
179                    region.name = old.clone();
180                    region.map.name = old.clone();
181                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
182                        if let Some(region_node) = tree_layout.get_node_by_id_mut(&region.id) {
183                            region_node.widget.set_value(TheValue::Text(old.clone()));
184                            if let Some(widget) = region_node.widgets[0].as_tree_item() {
185                                if let Some(embedded) = widget.embedded_widget_mut() {
186                                    embedded.set_value(TheValue::Text(old.clone()));
187                                }
188                            }
189                        }
190                    }
191                }
192            }
193            AddRegionCharacterInstance(region_id, character) => {
194                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
195                    if let Some(region_node) =
196                        tree_layout.get_node_by_id_mut(&server_ctx.tree_regions_id)
197                    {
198                        region_node.remove_widget_by_uuid(&character.id);
199                    }
200
201                    if let Some(region) = project.get_region_mut(region_id) {
202                        region.characters.shift_remove(&character.id);
203                        region.map.entities.retain(|e| e.creator_id != character.id);
204                    }
205
206                    if let Some(region) = project.get_region(region_id) {
207                        if let Some(region_node) = tree_layout.get_node_by_id_mut(&region.id) {
208                            region_node.set_open(true);
209                        }
210                        server_ctx.curr_region = region.id;
211                        set_project_context(
212                            ctx,
213                            ui,
214                            project,
215                            server_ctx,
216                            ProjectContext::Region(region.id),
217                        );
218                        update_region(ctx);
219                    }
220                }
221            }
222            RemoveRegionCharacterInstance(index, region_id, character) => {
223                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
224                    let character = character.clone();
225
226                    if let Some(region) = project.get_region_mut(region_id) {
227                        region
228                            .characters
229                            .insert_before(*index, character.id, character);
230
231                        if let Some(region_node) = tree_layout.get_node_by_id_mut(&region_id) {
232                            gen_region_tree_items(region_node, region);
233                        }
234                    }
235                    shared::rusterix_utils::insert_content_into_maps(project);
236                }
237            }
238            MoveRegionCharacterInstance(region_id, instance_id, from, _to) => {
239                move_region_character_pos(
240                    project,
241                    ui,
242                    ctx,
243                    server_ctx,
244                    *region_id,
245                    *instance_id,
246                    *from,
247                );
248            }
249            AddRegionItemInstance(region_id, item) => {
250                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
251                    if let Some(region_node) =
252                        tree_layout.get_node_by_id_mut(&server_ctx.tree_regions_id)
253                    {
254                        region_node.remove_widget_by_uuid(&item.id);
255                    }
256
257                    if let Some(region) = project.get_region_mut(region_id) {
258                        region.items.shift_remove(&item.id);
259                        region.map.items.retain(|e| e.creator_id != item.id);
260                    }
261
262                    if let Some(region) = project.get_region(region_id) {
263                        if let Some(region_node) = tree_layout.get_node_by_id_mut(&region.id) {
264                            region_node.set_open(true);
265                        }
266                        server_ctx.curr_region = region.id;
267                        set_project_context(
268                            ctx,
269                            ui,
270                            project,
271                            server_ctx,
272                            ProjectContext::Region(region.id),
273                        );
274                        update_region(ctx);
275                    }
276                }
277            }
278            RemoveRegionItemInstance(index, region_id, item) => {
279                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
280                    let item = item.clone();
281
282                    if let Some(region) = project.get_region_mut(region_id) {
283                        region.items.insert_before(*index, item.id, item);
284
285                        if let Some(region_node) = tree_layout.get_node_by_id_mut(&region_id) {
286                            gen_region_tree_items(region_node, region);
287                        }
288                    }
289                    shared::rusterix_utils::insert_content_into_maps(project);
290                }
291            }
292            MoveRegionItemInstance(region_id, instance_id, from, _to) => {
293                move_region_item_pos(
294                    project,
295                    ui,
296                    ctx,
297                    server_ctx,
298                    *region_id,
299                    *instance_id,
300                    *from,
301                );
302            }
303            AddCharacter(character) => {
304                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
305                    if let Some(region_node) =
306                        tree_layout.get_node_by_id_mut(&server_ctx.tree_characters_id)
307                    {
308                        project.remove_character(&character.id);
309                        region_node.remove_child_by_uuid(&character.id);
310                    }
311                }
312            }
313            RemoveCharacter(index, character) => {
314                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
315                    let character = character.clone();
316
317                    let mut node = gen_character_tree_node(&character);
318                    node.set_open(true);
319                    if let Some(character_node) =
320                        tree_layout.get_node_by_id_mut(&server_ctx.tree_characters_id)
321                    {
322                        character_node.add_child_at(*index, node);
323                    }
324                    let character_id: Uuid = character.id;
325                    project
326                        .characters
327                        .insert_before(*index, character_id, character);
328
329                    set_project_context(
330                        ctx,
331                        ui,
332                        project,
333                        server_ctx,
334                        ProjectContext::Character(character_id),
335                    );
336                    update_region(ctx);
337                }
338            }
339            RenameCharacter(id, old, _new) => {
340                if let Some(character) = project.characters.get_mut(id) {
341                    character.name = old.clone();
342                    character.map.name = old.clone();
343                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
344                        if let Some(region_node) = tree_layout.get_node_by_id_mut(&character.id) {
345                            region_node.widget.set_value(TheValue::Text(old.clone()));
346                            if let Some(widget) = region_node.widgets[0].as_tree_item() {
347                                if let Some(embedded) = widget.embedded_widget_mut() {
348                                    embedded.set_value(TheValue::Text(old.clone()));
349                                }
350                            }
351                        }
352                    }
353                }
354            }
355            AddItem(item) => {
356                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
357                    if let Some(region_node) =
358                        tree_layout.get_node_by_id_mut(&server_ctx.tree_items_id)
359                    {
360                        project.remove_item(&item.id);
361                        region_node.remove_child_by_uuid(&item.id);
362                    }
363                }
364            }
365            RemoveItem(index, item) => {
366                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
367                    let item = item.clone();
368
369                    let mut node = gen_item_tree_node(&item);
370                    node.set_open(true);
371                    if let Some(item_node) =
372                        tree_layout.get_node_by_id_mut(&server_ctx.tree_items_id)
373                    {
374                        item_node.add_child_at(*index, node);
375                    }
376                    let item_id: Uuid = item.id;
377                    project.items.insert_before(*index, item_id, item);
378
379                    set_project_context(
380                        ctx,
381                        ui,
382                        project,
383                        server_ctx,
384                        ProjectContext::Item(item_id),
385                    );
386                    update_region(ctx);
387                }
388            }
389            RenameItem(id, old, _new) => {
390                if let Some(item) = project.items.get_mut(id) {
391                    item.name = old.clone();
392                    item.map.name = old.clone();
393                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
394                        if let Some(region_node) = tree_layout.get_node_by_id_mut(&item.id) {
395                            region_node.widget.set_value(TheValue::Text(old.clone()));
396                            if let Some(widget) = region_node.widgets[0].as_tree_item() {
397                                if let Some(embedded) = widget.embedded_widget_mut() {
398                                    embedded.set_value(TheValue::Text(old.clone()));
399                                }
400                            }
401                        }
402                    }
403                }
404            }
405            AddTilemap(tilemap) => {
406                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
407                    if let Some(tilemap_node) =
408                        tree_layout.get_node_by_id_mut(&server_ctx.tree_tilemaps_id)
409                    {
410                        // Find the index of the tilemap in the project
411                        if let Some(index) =
412                            project.tilemaps.iter().position(|t| t.id == tilemap.id)
413                        {
414                            project.tilemaps.remove(index);
415                        }
416                        tilemap_node.remove_child_by_uuid(&tilemap.id);
417                    }
418                }
419            }
420            RemoveTilemap(index, tilemap) => {
421                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
422                    let tilemap = tilemap.clone();
423
424                    let mut node = gen_tilemap_tree_node(&tilemap);
425                    node.set_open(true);
426                    if let Some(tilemap_node) =
427                        tree_layout.get_node_by_id_mut(&server_ctx.tree_tilemaps_id)
428                    {
429                        tilemap_node.add_child_at(*index, node);
430                    }
431                    project.tilemaps.insert(*index, tilemap.clone());
432
433                    set_project_context(
434                        ctx,
435                        ui,
436                        project,
437                        server_ctx,
438                        ProjectContext::Tilemap(tilemap.id),
439                    );
440                    update_region(ctx);
441                }
442            }
443            RenameTilemap(id, old, _new) => {
444                if let Some(tilemap) = project.get_tilemap_mut(*id) {
445                    tilemap.name = old.clone();
446                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
447                        if let Some(tilemap_node) = tree_layout.get_node_by_id_mut(id) {
448                            tilemap_node.widget.set_value(TheValue::Text(old.clone()));
449                            if let Some(widget) = tilemap_node.widgets[0].as_tree_item() {
450                                if let Some(embedded) = widget.embedded_widget_mut() {
451                                    embedded.set_value(TheValue::Text(old.clone()));
452                                }
453                            }
454                        }
455                    }
456                }
457            }
458            EditTilemapGridSize(id, old, _new) => {
459                if let Some(tilemap) = project.get_tilemap_mut(*id) {
460                    tilemap.grid_size = *old;
461                    // Update the tree node widget
462                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
463                        if let Some(node) = tree_layout.get_node_by_id_mut(id) {
464                            if let Some(widget) = node.widgets[1].as_tree_item() {
465                                if let Some(embedded) = widget.embedded_widget_mut() {
466                                    embedded.set_value(TheValue::Int(*old));
467
468                                    ctx.ui.send(TheEvent::Custom(
469                                        TheId::named("Tilemap Grid Size Changed"),
470                                        TheValue::Int(*old),
471                                    ));
472                                }
473                            }
474                        }
475                    }
476                }
477            }
478            AddScreen(screen) => {
479                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
480                    if let Some(screen_node) =
481                        tree_layout.get_node_by_id_mut(&server_ctx.tree_screens_id)
482                    {
483                        project.remove_screen(&screen.id);
484                        screen_node.remove_child_by_uuid(&screen.id);
485                    }
486                }
487            }
488            RemoveScreen(index, screen) => {
489                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
490                    let screen = screen.clone();
491
492                    let mut node = gen_screen_tree_node(&screen);
493                    node.set_open(true);
494                    if let Some(screen_node) =
495                        tree_layout.get_node_by_id_mut(&server_ctx.tree_screens_id)
496                    {
497                        screen_node.add_child_at(*index, node);
498                    }
499                    let screen_id: Uuid = screen.id;
500                    project.screens.insert_before(*index, screen_id, screen);
501
502                    set_project_context(
503                        ctx,
504                        ui,
505                        project,
506                        server_ctx,
507                        ProjectContext::Screen(screen_id),
508                    );
509                    update_region(ctx);
510                }
511            }
512            RenameScreen(id, old, _new) => {
513                if let Some(screen) = project.screens.get_mut(id) {
514                    screen.name = old.clone();
515                    screen.map.name = old.clone();
516                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
517                        if let Some(screen_node) = tree_layout.get_node_by_id_mut(&screen.id) {
518                            screen_node.widget.set_value(TheValue::Text(old.clone()));
519                            if let Some(widget) = screen_node.widgets[0].as_tree_item() {
520                                if let Some(embedded) = widget.embedded_widget_mut() {
521                                    embedded.set_value(TheValue::Text(old.clone()));
522                                }
523                            }
524                        }
525                    }
526                }
527            }
528            AddAsset(asset) => {
529                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
530                    let parent_id = match &asset.buffer {
531                        AssetBuffer::Audio(_) => server_ctx.tree_assets_audio_id,
532                        _ => server_ctx.tree_assets_fonts_id,
533                    };
534                    if let Some(asset_node) = tree_layout.get_node_by_id_mut(&parent_id) {
535                        project.remove_asset(&asset.id);
536                        asset_node.remove_child_by_uuid(&asset.id);
537                    }
538                }
539            }
540            RemoveAsset(index, asset) => {
541                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
542                    let asset = asset.clone();
543
544                    let mut node = gen_asset_tree_node(&asset);
545                    node.set_open(true);
546                    let parent_id = match &asset.buffer {
547                        AssetBuffer::Audio(_) => server_ctx.tree_assets_audio_id,
548                        _ => server_ctx.tree_assets_fonts_id,
549                    };
550                    if let Some(asset_node) = tree_layout.get_node_by_id_mut(&parent_id) {
551                        let insert_index = (*index).min(asset_node.childs.len());
552                        asset_node.add_child_at(insert_index, node);
553                    }
554                    let asset_id: Uuid = asset.id;
555                    project.assets.insert_before(*index, asset_id, asset);
556
557                    set_project_context(
558                        ctx,
559                        ui,
560                        project,
561                        server_ctx,
562                        ProjectContext::Asset(asset_id),
563                    );
564                    update_region(ctx);
565                }
566            }
567            RenameAsset(id, old, _new) => {
568                if let Some(asset) = project.assets.get_mut(id) {
569                    asset.name = old.clone();
570                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
571                        if let Some(asset_node) = tree_layout.get_node_by_id_mut(&asset.id) {
572                            asset_node.widget.set_value(TheValue::Text(old.clone()));
573                            if let Some(widget) = asset_node.widgets[0].as_tree_item() {
574                                if let Some(embedded) = widget.embedded_widget_mut() {
575                                    embedded.set_value(TheValue::Text(old.clone()));
576                                }
577                            }
578                        }
579                    }
580                }
581            }
582            AddAvatar(avatar) => {
583                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
584                    if let Some(avatar_node) =
585                        tree_layout.get_node_by_id_mut(&server_ctx.tree_avatars_id)
586                    {
587                        project.remove_avatar(&avatar.id);
588                        avatar_node.remove_child_by_uuid(&avatar.id);
589                    }
590                }
591            }
592            RemoveAvatar(index, avatar) => {
593                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
594                    let avatar = avatar.clone();
595
596                    let mut node = gen_avatar_tree_node(&avatar);
597                    node.set_open(true);
598                    if let Some(avatar_node) =
599                        tree_layout.get_node_by_id_mut(&server_ctx.tree_avatars_id)
600                    {
601                        avatar_node.add_child_at(*index, node);
602                    }
603                    let avatar_id: Uuid = avatar.id;
604                    project.avatars.insert_before(*index, avatar_id, avatar);
605
606                    set_project_context(
607                        ctx,
608                        ui,
609                        project,
610                        server_ctx,
611                        ProjectContext::Avatar(avatar_id),
612                    );
613                    update_region(ctx);
614                }
615            }
616            RenameAvatar(id, old, _new) => {
617                if let Some(avatar) = project.avatars.get_mut(id) {
618                    avatar.name = old.clone();
619                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
620                        if let Some(avatar_node) = tree_layout.get_node_by_id_mut(&avatar.id) {
621                            avatar_node.widget.set_value(TheValue::Text(old.clone()));
622                            if let Some(widget) = avatar_node.widgets[0].as_tree_item() {
623                                if let Some(embedded) = widget.embedded_widget_mut() {
624                                    embedded.set_value(TheValue::Text(old.clone()));
625                                }
626                            }
627                        }
628                    }
629                }
630            }
631            EditAvatarResolution(id, old, _new) => {
632                if let Some(avatar) = project.avatars.get_mut(id) {
633                    avatar.set_resolution(*old);
634                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
635                        if let Some(avatar_node) = tree_layout.get_node_by_id_mut(id) {
636                            if let Some(widget) = avatar_node.widgets[1].as_tree_item() {
637                                if let Some(embedded) = widget.embedded_widget_mut() {
638                                    embedded.set_value(TheValue::Int(*old as i32));
639                                }
640                            }
641                        }
642                    }
643                }
644            }
645            EditAvatarPerspectiveCount(id, old, _new) => {
646                if let Some(avatar) = project.avatars.get_mut(id) {
647                    avatar.set_perspective_count(*old);
648                }
649                rebuild_avatar_tree_node(id, project, ui, server_ctx);
650            }
651            AddAvatarAnimation(avatar_id, anim) => {
652                // Undo: remove the animation
653                if let Some(avatar) = project.avatars.get_mut(avatar_id) {
654                    avatar.animations.retain(|a| a.id != anim.id);
655                    rebuild_avatar_tree_node(avatar_id, project, ui, server_ctx);
656                }
657            }
658            RemoveAvatarAnimation(avatar_id, index, anim) => {
659                // Undo: re-insert the animation at its original index
660                if let Some(avatar) = project.avatars.get_mut(avatar_id) {
661                    let idx = (*index).min(avatar.animations.len());
662                    avatar.animations.insert(idx, anim.clone());
663                    rebuild_avatar_tree_node(avatar_id, project, ui, server_ctx);
664                }
665            }
666            RenameAvatarAnimation(_avatar_id, anim_id, old, _new) => {
667                if let Some(avatar) = project
668                    .avatars
669                    .values_mut()
670                    .find(|a| a.animations.iter().any(|anim| anim.id == *anim_id))
671                {
672                    if let Some(anim) = avatar.animations.iter_mut().find(|a| a.id == *anim_id) {
673                        anim.name = old.clone();
674                    }
675                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
676                        if let Some(anim_node) = tree_layout.get_node_by_id_mut(anim_id) {
677                            let label = format!("{} - Animation", old);
678                            anim_node.widget.set_value(TheValue::Text(label));
679                            if let Some(widget) = anim_node.widgets[0].as_tree_item() {
680                                if let Some(embedded) = widget.embedded_widget_mut() {
681                                    embedded.set_value(TheValue::Text(old.clone()));
682                                }
683                            }
684                        }
685                    }
686                }
687            }
688            EditAvatarAnimationFrameCount(avatar_id, anim_id, old, _new) => {
689                if let Some(avatar) = project.avatars.get_mut(avatar_id) {
690                    avatar.set_animation_frame_count(anim_id, *old);
691                }
692                rebuild_animation_tree_node(avatar_id, anim_id, project, ui, server_ctx);
693            }
694            EditAvatarAnimationSpeed(avatar_id, anim_id, old, _new) => {
695                if let Some(avatar) = project.avatars.get_mut(avatar_id) {
696                    if let Some(anim) = avatar.animations.iter_mut().find(|a| a.id == *anim_id) {
697                        anim.speed = *old;
698                    }
699                }
700                rebuild_animation_tree_node(avatar_id, anim_id, project, ui, server_ctx);
701            }
702            PaletteEdit(old, _new) => {
703                let sel = project.palette.current_index;
704                project.palette = old.clone();
705                project.palette.current_index = sel;
706                apply_palette(ui, ctx, server_ctx, project);
707            }
708            TileEdit(old, _new) => {
709                project.tiles.insert(old.id, old.clone());
710                ctx.ui.send(TheEvent::Custom(
711                    TheId::named("Update Tiles"),
712                    TheValue::Empty,
713                ));
714            }
715        }
716    }
717
718    pub fn redo(
719        &self,
720        project: &mut Project,
721        ui: &mut TheUI,
722        ctx: &mut TheContext,
723        server_ctx: &mut ServerContext,
724    ) {
725        match self {
726            MapEdit(pc, _old, new) => {
727                set_project_context(ctx, ui, project, server_ctx, *pc);
728                if let Some(map) = project.get_map_mut(server_ctx) {
729                    *map = *new.clone();
730                    map.clear_temp();
731                    if pc.is_region() {
732                        update_region(ctx);
733                        map.update_surfaces();
734                    }
735                }
736            }
737            AddRegion(region) => {
738                // Add Region
739                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
740                    let mut region = region.clone();
741                    region.map.name = region.name.clone();
742
743                    let mut node = gen_region_tree_node(&region);
744                    node.set_open(true);
745                    if let Some(region_node) =
746                        tree_layout.get_node_by_id_mut(&server_ctx.tree_regions_id)
747                    {
748                        region_node.add_child(node);
749                    }
750                    let region_id: Uuid = region.id;
751                    project.regions.push(region);
752
753                    server_ctx.curr_region = region_id;
754                    set_project_context(
755                        ctx,
756                        ui,
757                        project,
758                        server_ctx,
759                        ProjectContext::Region(region_id),
760                    );
761                    update_region(ctx);
762                }
763            }
764            RemoveRegion(_, region) => {
765                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
766                    if let Some(region_node) =
767                        tree_layout.get_node_by_id_mut(&server_ctx.tree_regions_id)
768                    {
769                        region_node.remove_child_by_uuid(&region.id);
770                    }
771                    project.remove_region(&region.id);
772
773                    if let Some(region) = project.regions.first() {
774                        if let Some(region_node) = tree_layout.get_node_by_id_mut(&region.id) {
775                            region_node.set_open(true);
776                        }
777                        server_ctx.curr_region = region.id;
778                        set_project_context(
779                            ctx,
780                            ui,
781                            project,
782                            server_ctx,
783                            ProjectContext::Region(region.id),
784                        );
785                        update_region(ctx);
786                    }
787                }
788            }
789            RenameRegion(id, _old, new) => {
790                if let Some(region) = project.get_region_mut(id) {
791                    region.name = new.clone();
792                    region.map.name = new.clone();
793                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
794                        if let Some(region_node) = tree_layout.get_node_by_id_mut(&region.id) {
795                            region_node.widget.set_value(TheValue::Text(new.clone()));
796                            if let Some(widget) = region_node.widgets[0].as_tree_item() {
797                                if let Some(embedded) = widget.embedded_widget_mut() {
798                                    embedded.set_value(TheValue::Text(new.clone()));
799                                }
800                            }
801                        }
802                    }
803                }
804            }
805            AddRegionCharacterInstance(region_id, character) => {
806                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
807                    let character = character.clone();
808
809                    if let Some(region) = project.get_region_mut(region_id) {
810                        region.characters.insert(character.id, character);
811
812                        if let Some(region_node) = tree_layout.get_node_by_id_mut(&region_id) {
813                            gen_region_tree_items(region_node, region);
814                        }
815                    }
816                    shared::rusterix_utils::insert_content_into_maps(project);
817                }
818            }
819            MoveRegionCharacterInstance(region_id, instance_id, _from, to) => {
820                move_region_character_pos(
821                    project,
822                    ui,
823                    ctx,
824                    server_ctx,
825                    *region_id,
826                    *instance_id,
827                    *to,
828                );
829            }
830            RemoveRegionCharacterInstance(_, region_id, character) => {
831                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
832                    if let Some(region_node) =
833                        tree_layout.get_node_by_id_mut(&server_ctx.tree_regions_id)
834                    {
835                        region_node.remove_widget_by_uuid(&character.id);
836                    }
837
838                    if let Some(region) = project.get_region_mut(region_id) {
839                        region.characters.shift_remove(&character.id);
840                        region.map.entities.retain(|e| e.creator_id != character.id);
841                    }
842
843                    if let Some(region) = project.get_region(region_id) {
844                        if let Some(region_node) = tree_layout.get_node_by_id_mut(&region.id) {
845                            region_node.set_open(true);
846                        }
847                        server_ctx.curr_region = region.id;
848                        set_project_context(
849                            ctx,
850                            ui,
851                            project,
852                            server_ctx,
853                            ProjectContext::Region(region.id),
854                        );
855                        update_region(ctx);
856                    }
857                }
858            }
859            AddRegionItemInstance(region_id, item) => {
860                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
861                    let item = item.clone();
862
863                    if let Some(region) = project.get_region_mut(region_id) {
864                        region.items.insert(item.id, item);
865
866                        if let Some(region_node) = tree_layout.get_node_by_id_mut(&region_id) {
867                            gen_region_tree_items(region_node, region);
868                        }
869                    }
870                    shared::rusterix_utils::insert_content_into_maps(project);
871                }
872            }
873            MoveRegionItemInstance(region_id, instance_id, _from, to) => {
874                move_region_item_pos(project, ui, ctx, server_ctx, *region_id, *instance_id, *to);
875            }
876            RemoveRegionItemInstance(_, region_id, item) => {
877                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
878                    if let Some(region_node) =
879                        tree_layout.get_node_by_id_mut(&server_ctx.tree_regions_id)
880                    {
881                        region_node.remove_widget_by_uuid(&item.id);
882                    }
883
884                    if let Some(region) = project.get_region_mut(region_id) {
885                        region.items.shift_remove(&item.id);
886                        region.map.items.retain(|e| e.creator_id != item.id);
887                    }
888
889                    if let Some(region) = project.get_region(region_id) {
890                        if let Some(region_node) = tree_layout.get_node_by_id_mut(&region.id) {
891                            region_node.set_open(true);
892                        }
893                        server_ctx.curr_region = region.id;
894                        set_project_context(
895                            ctx,
896                            ui,
897                            project,
898                            server_ctx,
899                            ProjectContext::Region(region.id),
900                        );
901                        update_region(ctx);
902                    }
903                }
904            }
905            AddCharacter(character) => {
906                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
907                    if let Some(node) =
908                        tree_layout.get_node_by_id_mut(&server_ctx.tree_characters_id)
909                    {
910                        let mut character = character.clone();
911
912                        if let Some(bytes) = crate::Embedded::get("eldrin/character.eldrin") {
913                            if let Ok(source) = std::str::from_utf8(bytes.data.as_ref()) {
914                                character.source = source.to_string();
915                            }
916                        }
917
918                        if let Some(bytes) = crate::Embedded::get("toml/character.toml") {
919                            if let Ok(source) = std::str::from_utf8(bytes.data.as_ref()) {
920                                character.data = source.to_string();
921                            }
922                        }
923
924                        character
925                            .module
926                            .set_module_type(codegridfx::ModuleType::CharacterTemplate);
927
928                        let mut character_node = gen_character_tree_node(&character);
929                        character_node.set_open(true);
930                        node.add_child(character_node);
931
932                        let character_id = character.id;
933                        project.add_character(character);
934
935                        set_project_context(
936                            ctx,
937                            ui,
938                            project,
939                            server_ctx,
940                            ProjectContext::Character(character_id),
941                        );
942                        update_region(ctx);
943                    }
944                }
945            }
946            RemoveCharacter(_, character) => {
947                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
948                    if let Some(character_node) =
949                        tree_layout.get_node_by_id_mut(&server_ctx.tree_characters_id)
950                    {
951                        character_node.remove_child_by_uuid(&character.id);
952                    }
953                    project.remove_character(&character.id);
954
955                    if let Some(character) = project.characters.first() {
956                        if let Some(character_node) = tree_layout.get_node_by_id_mut(character.0) {
957                            character_node.set_open(true);
958                        }
959                        set_project_context(
960                            ctx,
961                            ui,
962                            project,
963                            server_ctx,
964                            ProjectContext::Character(*character.0),
965                        );
966                    }
967                }
968            }
969            RenameCharacter(id, _old, new) => {
970                if let Some(character) = project.characters.get_mut(id) {
971                    character.name = new.clone();
972                    character.map.name = new.clone();
973                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
974                        if let Some(region_node) = tree_layout.get_node_by_id_mut(&character.id) {
975                            region_node.widget.set_value(TheValue::Text(new.clone()));
976                            if let Some(widget) = region_node.widgets[0].as_tree_item() {
977                                if let Some(embedded) = widget.embedded_widget_mut() {
978                                    embedded.set_value(TheValue::Text(new.clone()));
979                                }
980                            }
981                        }
982                    }
983                }
984            }
985            AddItem(item) => {
986                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
987                    if let Some(node) = tree_layout.get_node_by_id_mut(&server_ctx.tree_items_id) {
988                        let mut item = item.clone();
989
990                        if let Some(bytes) = crate::Embedded::get("eldrin/item.eldrin") {
991                            if let Ok(source) = std::str::from_utf8(bytes.data.as_ref()) {
992                                item.source = source.to_string();
993                            }
994                        }
995
996                        if let Some(bytes) = crate::Embedded::get("toml/item.toml") {
997                            if let Ok(source) = std::str::from_utf8(bytes.data.as_ref()) {
998                                item.data = source.to_string();
999                            }
1000                        }
1001
1002                        item.module
1003                            .set_module_type(codegridfx::ModuleType::ItemTemplate);
1004
1005                        let mut item_node = gen_item_tree_node(&item);
1006                        item_node.set_open(true);
1007                        node.add_child(item_node);
1008
1009                        let item_id = item.id;
1010                        project.add_item(item);
1011
1012                        set_project_context(
1013                            ctx,
1014                            ui,
1015                            project,
1016                            server_ctx,
1017                            ProjectContext::Character(item_id),
1018                        );
1019                        update_region(ctx);
1020                    }
1021                }
1022            }
1023            RemoveItem(_, item) => {
1024                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1025                    if let Some(character_node) =
1026                        tree_layout.get_node_by_id_mut(&server_ctx.tree_items_id)
1027                    {
1028                        character_node.remove_child_by_uuid(&item.id);
1029                    }
1030                    project.remove_character(&item.id);
1031
1032                    if let Some(item) = project.items.first() {
1033                        if let Some(item_node) = tree_layout.get_node_by_id_mut(item.0) {
1034                            item_node.set_open(true);
1035                        }
1036                        set_project_context(
1037                            ctx,
1038                            ui,
1039                            project,
1040                            server_ctx,
1041                            ProjectContext::Item(*item.0),
1042                        );
1043                    }
1044                }
1045            }
1046            RenameItem(id, _old, new) => {
1047                if let Some(item) = project.items.get_mut(id) {
1048                    item.name = new.clone();
1049                    item.map.name = new.clone();
1050                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1051                        if let Some(item_node) = tree_layout.get_node_by_id_mut(&item.id) {
1052                            item_node.widget.set_value(TheValue::Text(new.clone()));
1053                            if let Some(widget) = item_node.widgets[0].as_tree_item() {
1054                                if let Some(embedded) = widget.embedded_widget_mut() {
1055                                    embedded.set_value(TheValue::Text(new.clone()));
1056                                }
1057                            }
1058                        }
1059                    }
1060                }
1061            }
1062            AddTilemap(tilemap) => {
1063                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1064                    if let Some(node) = tree_layout.get_node_by_id_mut(&server_ctx.tree_tilemaps_id)
1065                    {
1066                        let tilemap = tilemap.clone();
1067
1068                        let mut tilemap_node = gen_tilemap_tree_node(&tilemap);
1069                        tilemap_node.set_open(true);
1070                        node.add_child(tilemap_node);
1071
1072                        let tilemap_id = tilemap.id;
1073                        project.add_tilemap(tilemap);
1074
1075                        set_project_context(
1076                            ctx,
1077                            ui,
1078                            project,
1079                            server_ctx,
1080                            ProjectContext::Tilemap(tilemap_id),
1081                        );
1082                        update_region(ctx);
1083                    }
1084                }
1085            }
1086            RemoveTilemap(_, tilemap) => {
1087                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1088                    if let Some(tilemap_node) =
1089                        tree_layout.get_node_by_id_mut(&server_ctx.tree_tilemaps_id)
1090                    {
1091                        tilemap_node.remove_child_by_uuid(&tilemap.id);
1092                    }
1093                    // Find the index of the tilemap in the project
1094                    if let Some(index) = project.tilemaps.iter().position(|t| t.id == tilemap.id) {
1095                        project.tilemaps.remove(index);
1096                    }
1097
1098                    if let Some(first_tilemap) = project.tilemaps.first() {
1099                        if let Some(tilemap_node) =
1100                            tree_layout.get_node_by_id_mut(&first_tilemap.id)
1101                        {
1102                            tilemap_node.set_open(true);
1103                        }
1104                        set_project_context(
1105                            ctx,
1106                            ui,
1107                            project,
1108                            server_ctx,
1109                            ProjectContext::Tilemap(first_tilemap.id),
1110                        );
1111                    }
1112                }
1113            }
1114            RenameTilemap(id, _old, new) => {
1115                if let Some(tilemap) = project.get_tilemap_mut(*id) {
1116                    tilemap.name = new.clone();
1117                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1118                        if let Some(tilemap_node) = tree_layout.get_node_by_id_mut(id) {
1119                            tilemap_node.widget.set_value(TheValue::Text(new.clone()));
1120                            if let Some(widget) = tilemap_node.widgets[0].as_tree_item() {
1121                                if let Some(embedded) = widget.embedded_widget_mut() {
1122                                    embedded.set_value(TheValue::Text(new.clone()));
1123                                }
1124                            }
1125                        }
1126                    }
1127                }
1128            }
1129            EditTilemapGridSize(id, _old, new) => {
1130                if let Some(tilemap) = project.get_tilemap_mut(*id) {
1131                    tilemap.grid_size = *new;
1132                    // Update the tree node widget
1133                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1134                        if let Some(node) = tree_layout.get_node_by_id_mut(id) {
1135                            if let Some(widget) = node.widgets[1].as_tree_item() {
1136                                if let Some(embedded) = widget.embedded_widget_mut() {
1137                                    embedded.set_value(TheValue::Int(*new));
1138
1139                                    ctx.ui.send(TheEvent::Custom(
1140                                        TheId::named("Tilemap Grid Size Changed"),
1141                                        TheValue::Int(*new),
1142                                    ));
1143                                }
1144                            }
1145                        }
1146                    }
1147                }
1148            }
1149            AddScreen(screen) => {
1150                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1151                    if let Some(node) = tree_layout.get_node_by_id_mut(&server_ctx.tree_screens_id)
1152                    {
1153                        let screen = screen.clone();
1154
1155                        let mut screen_node = gen_screen_tree_node(&screen);
1156                        screen_node.set_open(true);
1157                        node.add_child(screen_node);
1158
1159                        let screen_id = screen.id;
1160                        project.add_screen(screen);
1161
1162                        set_project_context(
1163                            ctx,
1164                            ui,
1165                            project,
1166                            server_ctx,
1167                            ProjectContext::Screen(screen_id),
1168                        );
1169                        update_region(ctx);
1170                    }
1171                }
1172            }
1173            RemoveScreen(_, screen) => {
1174                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1175                    if let Some(screen_node) =
1176                        tree_layout.get_node_by_id_mut(&server_ctx.tree_screens_id)
1177                    {
1178                        screen_node.remove_child_by_uuid(&screen.id);
1179                    }
1180                    project.remove_screen(&screen.id);
1181
1182                    if let Some(first_screen) = project.screens.first() {
1183                        if let Some(screen_node) = tree_layout.get_node_by_id_mut(first_screen.0) {
1184                            screen_node.set_open(true);
1185                        }
1186                        set_project_context(
1187                            ctx,
1188                            ui,
1189                            project,
1190                            server_ctx,
1191                            ProjectContext::Screen(*first_screen.0),
1192                        );
1193                    }
1194                }
1195            }
1196            RenameScreen(id, _old, new) => {
1197                if let Some(screen) = project.screens.get_mut(id) {
1198                    screen.name = new.clone();
1199                    screen.map.name = new.clone();
1200                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1201                        if let Some(screen_node) = tree_layout.get_node_by_id_mut(id) {
1202                            screen_node.widget.set_value(TheValue::Text(new.clone()));
1203                            if let Some(widget) = screen_node.widgets[0].as_tree_item() {
1204                                if let Some(embedded) = widget.embedded_widget_mut() {
1205                                    embedded.set_value(TheValue::Text(new.clone()));
1206                                }
1207                            }
1208                        }
1209                    }
1210                }
1211            }
1212            AddAsset(asset) => {
1213                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1214                    let parent_id = match &asset.buffer {
1215                        AssetBuffer::Audio(_) => server_ctx.tree_assets_audio_id,
1216                        _ => server_ctx.tree_assets_fonts_id,
1217                    };
1218                    if let Some(node) = tree_layout.get_node_by_id_mut(&parent_id) {
1219                        let asset = asset.clone();
1220
1221                        let mut asset_node = gen_asset_tree_node(&asset);
1222                        asset_node.set_open(true);
1223                        node.add_child(asset_node);
1224
1225                        let asset_id = asset.id;
1226                        project.add_asset(asset);
1227
1228                        set_project_context(
1229                            ctx,
1230                            ui,
1231                            project,
1232                            server_ctx,
1233                            ProjectContext::Asset(asset_id),
1234                        );
1235                        update_region(ctx);
1236                    }
1237                }
1238            }
1239            RemoveAsset(_, asset) => {
1240                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1241                    if let Some(asset_node) =
1242                        tree_layout.get_node_by_id_mut(&server_ctx.tree_assets_fonts_id)
1243                    {
1244                        asset_node.remove_child_by_uuid(&asset.id);
1245                    }
1246                    if let Some(asset_node) =
1247                        tree_layout.get_node_by_id_mut(&server_ctx.tree_assets_audio_id)
1248                    {
1249                        asset_node.remove_child_by_uuid(&asset.id);
1250                    }
1251                    project.remove_asset(&asset.id);
1252
1253                    if let Some(first_asset) = project.assets.first() {
1254                        if let Some(asset_node) = tree_layout.get_node_by_id_mut(first_asset.0) {
1255                            asset_node.set_open(true);
1256                        }
1257                        set_project_context(
1258                            ctx,
1259                            ui,
1260                            project,
1261                            server_ctx,
1262                            ProjectContext::Asset(*first_asset.0),
1263                        );
1264                    }
1265                }
1266            }
1267            RenameAsset(id, _old, new) => {
1268                if let Some(asset) = project.assets.get_mut(id) {
1269                    asset.name = new.clone();
1270                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1271                        if let Some(asset_node) = tree_layout.get_node_by_id_mut(id) {
1272                            asset_node.widget.set_value(TheValue::Text(new.clone()));
1273                            if let Some(widget) = asset_node.widgets[0].as_tree_item() {
1274                                if let Some(embedded) = widget.embedded_widget_mut() {
1275                                    embedded.set_value(TheValue::Text(new.clone()));
1276                                }
1277                            }
1278                        }
1279                    }
1280                }
1281            }
1282            AddAvatar(avatar) => {
1283                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1284                    if let Some(node) = tree_layout.get_node_by_id_mut(&server_ctx.tree_avatars_id)
1285                    {
1286                        let avatar = avatar.clone();
1287
1288                        let mut avatar_node = gen_avatar_tree_node(&avatar);
1289                        avatar_node.set_open(true);
1290                        node.add_child(avatar_node);
1291
1292                        let avatar_id = avatar.id;
1293                        project.add_avatar(avatar);
1294
1295                        set_project_context(
1296                            ctx,
1297                            ui,
1298                            project,
1299                            server_ctx,
1300                            ProjectContext::Avatar(avatar_id),
1301                        );
1302                        update_region(ctx);
1303                    }
1304                }
1305            }
1306            RemoveAvatar(_, avatar) => {
1307                if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1308                    if let Some(avatar_node) =
1309                        tree_layout.get_node_by_id_mut(&server_ctx.tree_avatars_id)
1310                    {
1311                        avatar_node.remove_child_by_uuid(&avatar.id);
1312                    }
1313                    project.remove_avatar(&avatar.id);
1314
1315                    if let Some(first_avatar) = project.avatars.first() {
1316                        if let Some(avatar_node) = tree_layout.get_node_by_id_mut(first_avatar.0) {
1317                            avatar_node.set_open(true);
1318                        }
1319                        set_project_context(
1320                            ctx,
1321                            ui,
1322                            project,
1323                            server_ctx,
1324                            ProjectContext::Avatar(*first_avatar.0),
1325                        );
1326                    }
1327                }
1328            }
1329            RenameAvatar(id, _old, new) => {
1330                if let Some(avatar) = project.avatars.get_mut(id) {
1331                    avatar.name = new.clone();
1332                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1333                        if let Some(avatar_node) = tree_layout.get_node_by_id_mut(id) {
1334                            avatar_node.widget.set_value(TheValue::Text(new.clone()));
1335                            if let Some(widget) = avatar_node.widgets[0].as_tree_item() {
1336                                if let Some(embedded) = widget.embedded_widget_mut() {
1337                                    embedded.set_value(TheValue::Text(new.clone()));
1338                                }
1339                            }
1340                        }
1341                    }
1342                }
1343            }
1344            EditAvatarResolution(id, _old, new) => {
1345                if let Some(avatar) = project.avatars.get_mut(id) {
1346                    avatar.set_resolution(*new);
1347                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1348                        if let Some(avatar_node) = tree_layout.get_node_by_id_mut(id) {
1349                            if let Some(widget) = avatar_node.widgets[1].as_tree_item() {
1350                                if let Some(embedded) = widget.embedded_widget_mut() {
1351                                    embedded.set_value(TheValue::Int(*new as i32));
1352                                }
1353                            }
1354                        }
1355                    }
1356                }
1357            }
1358            EditAvatarPerspectiveCount(id, _old, new) => {
1359                if let Some(avatar) = project.avatars.get_mut(id) {
1360                    avatar.set_perspective_count(*new);
1361                }
1362                rebuild_avatar_tree_node(id, project, ui, server_ctx);
1363            }
1364            AddAvatarAnimation(avatar_id, anim) => {
1365                // Redo: add the animation (already has correct perspectives/frames)
1366                if let Some(avatar) = project.avatars.get_mut(avatar_id) {
1367                    avatar.animations.push(anim.clone());
1368                    rebuild_avatar_tree_node(avatar_id, project, ui, server_ctx);
1369                }
1370            }
1371            RemoveAvatarAnimation(avatar_id, _, anim) => {
1372                // Redo: remove the animation
1373                if let Some(avatar) = project.avatars.get_mut(avatar_id) {
1374                    avatar.animations.retain(|a| a.id != anim.id);
1375                    rebuild_avatar_tree_node(avatar_id, project, ui, server_ctx);
1376                }
1377            }
1378            RenameAvatarAnimation(_avatar_id, anim_id, _old, new) => {
1379                if let Some(avatar) = project
1380                    .avatars
1381                    .values_mut()
1382                    .find(|a| a.animations.iter().any(|anim| anim.id == *anim_id))
1383                {
1384                    if let Some(anim) = avatar.animations.iter_mut().find(|a| a.id == *anim_id) {
1385                        anim.name = new.clone();
1386                    }
1387                    if let Some(tree_layout) = ui.get_tree_layout("Project Tree") {
1388                        if let Some(anim_node) = tree_layout.get_node_by_id_mut(anim_id) {
1389                            let label = format!("{} - Animation", new);
1390                            anim_node.widget.set_value(TheValue::Text(label));
1391                            if let Some(widget) = anim_node.widgets[0].as_tree_item() {
1392                                if let Some(embedded) = widget.embedded_widget_mut() {
1393                                    embedded.set_value(TheValue::Text(new.clone()));
1394                                }
1395                            }
1396                        }
1397                    }
1398                }
1399            }
1400            EditAvatarAnimationFrameCount(avatar_id, anim_id, _old, new) => {
1401                if let Some(avatar) = project.avatars.get_mut(avatar_id) {
1402                    avatar.set_animation_frame_count(anim_id, *new);
1403                }
1404                rebuild_animation_tree_node(avatar_id, anim_id, project, ui, server_ctx);
1405            }
1406            EditAvatarAnimationSpeed(avatar_id, anim_id, _old, new) => {
1407                if let Some(avatar) = project.avatars.get_mut(avatar_id) {
1408                    if let Some(anim) = avatar.animations.iter_mut().find(|a| a.id == *anim_id) {
1409                        anim.speed = *new;
1410                    }
1411                }
1412                rebuild_animation_tree_node(avatar_id, anim_id, project, ui, server_ctx);
1413            }
1414            PaletteEdit(_old, new) => {
1415                let sel = project.palette.current_index;
1416                project.palette = new.clone();
1417                project.palette.current_index = sel;
1418                apply_palette(ui, ctx, server_ctx, project);
1419            }
1420            TileEdit(_old, new) => {
1421                project.tiles.insert(new.id, new.clone());
1422                ctx.ui.send(TheEvent::Custom(
1423                    TheId::named("Update Tiles"),
1424                    TheValue::Empty,
1425                ));
1426            }
1427        }
1428    }
1429}
1430
1431fn move_region_character_pos(
1432    project: &mut Project,
1433    ui: &mut TheUI,
1434    ctx: &mut TheContext,
1435    server_ctx: &mut ServerContext,
1436    region_id: Uuid,
1437    instance_id: Uuid,
1438    pos: Vec3<f32>,
1439) {
1440    set_project_context(
1441        ctx,
1442        ui,
1443        project,
1444        server_ctx,
1445        ProjectContext::Region(region_id),
1446    );
1447
1448    if let Some(region) = project.get_region_mut(&region_id) {
1449        if let Some(instance) = region.characters.get_mut(&instance_id) {
1450            instance.position = pos;
1451        }
1452        for entity in region.map.entities.iter_mut() {
1453            if entity.creator_id == instance_id {
1454                entity.position = pos;
1455            }
1456        }
1457    }
1458
1459    if let Some(map) = project.get_map_mut(server_ctx) {
1460        for entity in map.entities.iter_mut() {
1461            if entity.creator_id == instance_id {
1462                entity.position = pos;
1463            }
1464        }
1465    }
1466}
1467
1468fn move_region_item_pos(
1469    project: &mut Project,
1470    ui: &mut TheUI,
1471    ctx: &mut TheContext,
1472    server_ctx: &mut ServerContext,
1473    region_id: Uuid,
1474    instance_id: Uuid,
1475    pos: Vec3<f32>,
1476) {
1477    set_project_context(
1478        ctx,
1479        ui,
1480        project,
1481        server_ctx,
1482        ProjectContext::Region(region_id),
1483    );
1484
1485    if let Some(region) = project.get_region_mut(&region_id) {
1486        if let Some(instance) = region.items.get_mut(&instance_id) {
1487            instance.position = pos;
1488        }
1489        for item in region.map.items.iter_mut() {
1490            if item.creator_id == instance_id {
1491                item.position = pos;
1492            }
1493        }
1494    }
1495
1496    if let Some(map) = project.get_map_mut(server_ctx) {
1497        for item in map.items.iter_mut() {
1498            if item.creator_id == instance_id {
1499                item.position = pos;
1500            }
1501        }
1502    }
1503}