From 20b30df0f823aa92b4d048aa771192a1a62bf31d Mon Sep 17 00:00:00 2001 From: snipercup <50166150+snipercup@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:31:11 +0200 Subject: [PATCH 01/15] Editor remembers collapsed state --- Scenes/ContentManager/Scripts/content_list.gd | 51 +++++++++++++++++-- .../ContentManager/Scripts/contenteditor.gd | 5 +- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/Scenes/ContentManager/Scripts/content_list.gd b/Scenes/ContentManager/Scripts/content_list.gd index faed7555..84ab05db 100644 --- a/Scenes/ContentManager/Scripts/content_list.gd +++ b/Scenes/ContentManager/Scripts/content_list.gd @@ -23,7 +23,6 @@ var header: String = "Items": collapseButton.text = header - #This function adds items to the content list based on the provided path #If the path is a directory, it will list all the files in the directory #If the path is a json file, it will list all the items in the json file @@ -41,6 +40,8 @@ func load_data(): make_file_list() else: make_item_list() + load_collapse_state() + # Loops over all the items in contentData.data (which are dictionaries) # Creates a new item in the list with the id of the item as text @@ -95,17 +96,20 @@ func _on_content_items_item_activated(index: int): print_debug("Tried to signal that item with ID (" + str(index) + ") was activated,\ but the item has no metadata") + #This function will append an item to the game data func add_item_to_data(id: String): Gamedata.add_id_to_data(contentData, id) load_data() - + + #This function will show a pop-up asking the user to input an ID func _on_add_button_button_up(): popupAction = "Add" popup_textedit.text = "" pupup_ID.show() + #This function requires that an item from the list is selected #Once clicked, it will show pupup_ID to ask the user for a new ID #If the user enters an ID and presses OK, it will read the file from the source variable @@ -119,7 +123,7 @@ func _on_duplicate_button_button_up(): popupAction = "Duplicate" popup_textedit.text = selected_id pupup_ID.show() - + #Called after the user enters an ID into the popup textbox and presses OK func _on_ok_button_up(): @@ -139,11 +143,13 @@ func _on_ok_button_up(): popupAction = "" load_data() + #Called after the users presses cancel on the popup asking for an ID func _on_cancel_button_up(): pupup_ID.hide() popupAction = "" + #This function requires that an item from the list is selected #Once clicked, the selected item will be removed from contentItems #It will also remove the item from the json file specified by source @@ -155,13 +161,20 @@ func _on_delete_button_button_up(): Gamedata.remove_item_from_data(contentData, selected_id) load_data() + func get_selected_item_text() -> String: if !contentItems.is_anything_selected(): return "" return contentItems.get_item_text(contentItems.get_selected_items()[0]) + #This function will collapse and expand the $Content/ContentItems when the collapse button is pressed func _on_collapse_button_button_up(): + save_collapse_state() + set_collapsed() + + +func set_collapsed(): contentItems.visible = is_collapsed if is_collapsed: size_flags_vertical = Control.SIZE_EXPAND_FILL @@ -194,7 +207,7 @@ func _get_drag_data(_newpos): # This function should return true if the dragged data can be dropped here -func _can_drop_data(_newpos, data) -> bool: +func _can_drop_data(_newpos, _data) -> bool: return false @@ -244,3 +257,33 @@ func _on_content_items_gui_input(event): func _on_content_items_mouse_entered(): mouse_button_is_pressed = false + + +func save_collapse_state(): + var config = ConfigFile.new() + var path = "user://settings.cfg" + + # Ensure to load existing settings to not overwrite them + var err = config.load(path) + if err != OK and err != ERR_FILE_NOT_FOUND: + print("Failed to load settings:", err) + return + + config.set_value("contenteditor:contentlist:"+header, "is_collapsed", is_collapsed) + config.save(path) + + +func load_collapse_state(): + var config = ConfigFile.new() + var path = "user://settings.cfg" + + # Load the config file + var err = config.load(path) + if err == OK: + if config.has_section_key("contenteditor:contentlist:" + header, "is_collapsed"): + is_collapsed = config.get_value("contenteditor:contentlist:" + header, "is_collapsed") + set_collapsed() + else: + print("No saved state for:", header) + else: + print("Failed to load settings for:", header, "with error:", err) diff --git a/Scenes/ContentManager/Scripts/contenteditor.gd b/Scenes/ContentManager/Scripts/contenteditor.gd index ca99b093..2d1c4082 100644 --- a/Scenes/ContentManager/Scripts/contenteditor.gd +++ b/Scenes/ContentManager/Scripts/contenteditor.gd @@ -12,6 +12,7 @@ extends Control @export var tabContainer: TabContainer = null var selectedMod: String = "Core" + # Called when the node enters the scene tree for the first time. #This function will instatiate a tileScene, set the source property and add it as a child to the content VBoxContainer. The source property should be set to "./Mods/Core/Maps/" func _ready(): @@ -29,16 +30,18 @@ func load_content_list(data: Dictionary, strHeader: String): var contentListInstance: Control = contentList.instantiate() # Set the source property - contentListInstance.contentData = data contentListInstance.header = strHeader + contentListInstance.contentData = data contentListInstance.connect("item_activated", _on_content_item_activated) # Add it as a child to the content VBoxContainer content.add_child(contentListInstance) + func _on_back_button_button_up(): get_tree().change_scene_to_file("res://Scenes/ContentManager/contentmanager.tscn") + #The user has doubleclicked or pressed enter on one of the items in the content lists #Depending on wether the source is a JSON file, we are going to load the relevant content #If strSource is a json file, we load an item from this file with the ID of itemText From be551c44097d9df3906e7d1c25a1b707cb650091 Mon Sep 17 00:00:00 2001 From: snipercup <50166150+snipercup@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:31:41 +0200 Subject: [PATCH 02/15] Remove editor warnings --- .../Custom_Editors/Scripts/ItemgroupEditor.gd | 2 +- Scripts/FurniturePhysics.gd | 9 +++++---- Scripts/FurnitureStatic.gd | 2 -- Scripts/Helper/task_manager.gd | 4 ++-- Scripts/Mob.gd | 6 ++++-- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/Scenes/ContentManager/Custom_Editors/Scripts/ItemgroupEditor.gd b/Scenes/ContentManager/Custom_Editors/Scripts/ItemgroupEditor.gd index 943fe0e9..37ace329 100644 --- a/Scenes/ContentManager/Custom_Editors/Scripts/ItemgroupEditor.gd +++ b/Scenes/ContentManager/Custom_Editors/Scripts/ItemgroupEditor.gd @@ -192,7 +192,7 @@ func _handle_item_drop(dropped_data, _newpos) -> void: # The user clicked in the list, but not on any of the items # Deselect all items and reset the selectedItemNameDisplay label -func _on_item_list_empty_clicked(at_position, mouse_button_index): +func _on_item_list_empty_clicked(_at_position, _mouse_button_index): itemList.deselect_all() # Deselect any selected items selectedItemNameDisplay.text = "No item selected" # Reset the display label diff --git a/Scripts/FurniturePhysics.gd b/Scripts/FurniturePhysics.gd index 876dcc5f..eac5c481 100644 --- a/Scripts/FurniturePhysics.gd +++ b/Scripts/FurniturePhysics.gd @@ -27,9 +27,10 @@ func _ready(): func _physics_process(_delta): if global_transform.origin != furnitureposition and \ not global_transform.origin.x == 0 and not global_transform.origin.y == 0: - _moved(furnitureposition, global_transform.origin) - if rotation_degrees.y != last_rotation: - last_rotation = rotation_degrees.y + _moved(global_transform.origin) + var current_rotation = int(rotation_degrees.y) + if current_rotation != last_rotation: + last_rotation = current_rotation func get_hit(damage): @@ -125,7 +126,7 @@ func construct_self(furniturepos: Vector3, newFurnitureJSON: Dictionary): # Check if we crossed the chunk boundary and update our association with the chunks -func _moved(oldpos: Vector3, newpos:Vector3): +func _moved(newpos:Vector3): furnitureposition = newpos var new_chunk = Helper.map_manager.get_chunk_from_position(furnitureposition) if not current_chunk == new_chunk: diff --git a/Scripts/FurnitureStatic.gd b/Scripts/FurnitureStatic.gd index 409f3120..610c49a0 100644 --- a/Scripts/FurnitureStatic.gd +++ b/Scripts/FurnitureStatic.gd @@ -249,5 +249,3 @@ func add_container(pos: Vector3): newItem.construct_self(pos) # Add the new item with possibly set itemgroup as a child. add_child.call_deferred(newItem) - else: - print_debug("Function or container property not found in furniture JSON Data for ID: " + str(furnitureJSONData["id"])) diff --git a/Scripts/Helper/task_manager.gd b/Scripts/Helper/task_manager.gd index bd1cd7cd..2e28bfc0 100644 --- a/Scripts/Helper/task_manager.gd +++ b/Scripts/Helper/task_manager.gd @@ -13,8 +13,8 @@ class Task: signal completed - func _init(id: int): - self.id = id + func _init(ident: int): + self.id = ident func get_processed_element_count() -> int: return 1 if is_completed() else 0 diff --git a/Scripts/Mob.gd b/Scripts/Mob.gd index 4ae764c1..a3e1cc3c 100644 --- a/Scripts/Mob.gd +++ b/Scripts/Mob.gd @@ -48,8 +48,10 @@ func _physics_process(_delta): # We have crossed over to another chunk so we use that navigationmap now. update_navigation_agent_map(current_chunk) - if rotation_degrees.y != last_rotation: - last_rotation = rotation_degrees.y + var current_rotation = int(rotation_degrees.y) + if current_rotation != last_rotation: + last_rotation = current_rotation + func update_navigation_agent_map(chunk_position: Vector2): # Assume 'chunk_navigation_maps' is a global dictionary mapping chunk positions to navigation map IDs From b41c4f74cfe7baf23cedb2c152b05f372db60465 Mon Sep 17 00:00:00 2001 From: snipercup <50166150+snipercup@users.noreply.github.com> Date: Mon, 29 Apr 2024 14:31:50 +0200 Subject: [PATCH 03/15] Fix interaction bug --- Scripts/Helper.gd | 4 ++-- Scripts/player.gd | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Scripts/Helper.gd b/Scripts/Helper.gd index ebc71568..c1444e1d 100644 --- a/Scripts/Helper.gd +++ b/Scripts/Helper.gd @@ -127,14 +127,14 @@ func line(pos1: Vector3, pos2: Vector3, color = Color.WHITE_SMOKE) -> MeshInstan return mesh_instance -func raycast_from_mouse(m_pos, collision_mask): +func raycast_from_mouse(m_pos, collision_mask) -> Dictionary: var ray_start = get_tree().get_first_node_in_group("Camera").project_ray_origin(m_pos) var ray_end = ray_start + get_tree().get_first_node_in_group("Camera").project_ray_normal(m_pos) * 1000 var world3d : World3D = get_world_3d() var space_state = world3d.direct_space_state if space_state == null: - return + return {} var query = PhysicsRayQueryParameters3D.create(ray_start, ray_end, collision_mask) query.collide_with_areas = true diff --git a/Scripts/player.gd b/Scripts/player.gd index dfb1877f..92d9cf42 100644 --- a/Scripts/player.gd +++ b/Scripts/player.gd @@ -145,7 +145,10 @@ func _input(event): if event.is_action_pressed("interact"): var layer = pow(2, 1-1) + pow(2, 2-1) + pow(2, 3-1) var mouse_pos : Vector2 = get_viewport().get_mouse_position() - var world_mouse_position = Helper.raycast_from_mouse(mouse_pos, layer).position + var raycast: Dictionary = Helper.raycast_from_mouse(mouse_pos, layer) + if not raycast.has("position"): + return + var world_mouse_position = raycast.position var result = Helper.raycast(global_position, global_position + (Vector3(world_mouse_position.x - global_position.x, 0, world_mouse_position.z - global_position.z)).normalized() * interact_range, layer, [self]) print("Interact button pressed") From bb38be4527b8564ebeb437f334379d6814a2a828 Mon Sep 17 00:00:00 2001 From: snipercup <50166150+snipercup@users.noreply.github.com> Date: Mon, 29 Apr 2024 16:49:24 +0200 Subject: [PATCH 04/15] Save and load container contents --- Scripts/FurnitureStatic.gd | 78 ++++++++++++++++++++++++++++---------- Scripts/ItemDetector.gd | 6 ++- Scripts/container.gd | 12 +++++- 3 files changed, 73 insertions(+), 23 deletions(-) diff --git a/Scripts/FurnitureStatic.gd b/Scripts/FurnitureStatic.gd index 610c49a0..8a97fa5e 100644 --- a/Scripts/FurnitureStatic.gd +++ b/Scripts/FurnitureStatic.gd @@ -15,6 +15,7 @@ var sprite: Sprite3D = null var collider: CollisionShape3D = null var is_door: bool = false var door_state: String = "Closed" # Default state +var container: ContainerItem = null # Reference to the container, if this furniture acts as one var corpse_scene: PackedScene = preload("res://Defaults/Mobs/mob_corpse.tscn") var current_health: float = 100.0 @@ -63,8 +64,6 @@ func _die(): queue_free() - - # Will update the sprite of this furniture and set a collisionshape based on it's size func set_sprite(newSprite: Texture): if not sprite: @@ -131,8 +130,8 @@ func construct_self(furniturepos: Vector3, newFurnitureJSON: Dictionary): furnitureJSON = newFurnitureJSON # Position furniture at the center of the block by default furnitureposition = furniturepos - # Only previously saved furniture will have the global_position_x key. They do not need to be raised - if not newFurnitureJSON.has("global_position_x"): + # Peviously saved furniture do not need to be raised + if is_new_furniture(): furnitureposition.y += 0.5 # Move the furniture to slightly above the block add_to_group("furniture") @@ -215,6 +214,23 @@ func get_data() -> Dictionary: if "Function" in furnitureJSONData and "door" in furnitureJSONData.Function: newfurniturejson["Function"] = {"door": door_state} + + # Check for container functionality and save item list if applicable + if "Function" in furnitureJSONData and "container" in furnitureJSONData["Function"]: + # Initialize the 'Function' sub-dictionary if not already present + if "Function" not in newfurniturejson: + newfurniturejson["Function"] = {} + + # Check if this furniture has a container attached and if it has items + if container: + var item_ids = container.get_item_ids() + if item_ids.size() > 0: + var containerdata = container.get_inventory().serialize() + newfurniturejson["Function"]["container"] = {"items": containerdata} + else: + # No items in the container, store the container as empty + newfurniturejson["Function"]["container"] = {} + return newfurniturejson @@ -231,21 +247,43 @@ func add_corpse(pos: Vector3): get_tree().get_root().add_child.call_deferred(newItem) -# If this furniture is a container, it will add a container node to the furniture -# If there is an itemgroup assigned to the furniture, it will be added to the container -# Which will fill up the container with items from the itemgroup +# If this furniture is a container, it will add a container node to the furniture. func add_container(pos: Vector3): - # Check if the furnitureJSONData has 'Function' and if 'Function' has 'container' if "Function" in furnitureJSONData and "container" in furnitureJSONData["Function"]: - var newItem: ContainerItem = ContainerItem.new() - - # Check if the container has an 'itemgroup' - if "itemgroup" in furnitureJSONData["Function"]["container"]: - newItem.itemgroup = furnitureJSONData["Function"]["container"]["itemgroup"] - else: - # The furniture is a container, but no items are in it. We still create an empty container - print_debug("No itemgroup found for container in furniture ID: " + str(furnitureJSONData["id"])) - - newItem.construct_self(pos) - # Add the new item with possibly set itemgroup as a child. - add_child.call_deferred(newItem) + container = ContainerItem.new() + container.construct_self(pos) + handle_container_population() + add_child(container) + + +# Check if this is a new furniture or if it is one that was previously saved. +func handle_container_population(): + if is_new_furniture(): + populate_container_from_itemgroup() + else: + deserialize_container_data() + + +# If there is an itemgroup assigned to the furniture, it will be added to the container. +# Which will fill up the container with items from the itemgroup. +func populate_container_from_itemgroup(): + var itemgroup = furnitureJSONData["Function"]["container"].get("itemgroup", "") + if itemgroup: + container.itemgroup = itemgroup + else: + print_debug("No itemgroup found for container in furniture ID: " + str(furnitureJSON.id)) + + +# It will deserialize the container data if the furniture is not new. +func deserialize_container_data(): + if "items" in furnitureJSON["Function"]["container"]: + container.get_inventory().deserialize(furnitureJSON["Function"]["container"]["items"]) + else: + print_debug("No items to deserialize in container for furniture ID: " + str(furnitureJSON.id)) + + +# Only previously saved furniture will have the global_position_x key. +# Returns true if this is a new furniture +# Returns false if this is a previously saved furniture +func is_new_furniture() -> bool: + return not furnitureJSON.has("global_position_x") diff --git a/Scripts/ItemDetector.gd b/Scripts/ItemDetector.gd index 4a574971..3b6b7380 100644 --- a/Scripts/ItemDetector.gd +++ b/Scripts/ItemDetector.gd @@ -10,5 +10,7 @@ func _on_area_entered(area): func _on_area_exited(area): - if area.get_owner().is_in_group("Containers"): - remove_from_proximity_inventory.emit(area.get_owner()) + var areaowner = area.get_owner() + if areaowner: + if areaowner.is_in_group("Containers"): + remove_from_proximity_inventory.emit(area.get_owner()) diff --git a/Scripts/container.gd b/Scripts/container.gd index d03b1160..a03c59c6 100644 --- a/Scripts/container.gd +++ b/Scripts/container.gd @@ -18,6 +18,8 @@ func _ready(): create_loot() +# Will add item to the inventory based on the assigned itemgroup +# Only new furniture will have an itemgroup assigned, not previously saved furniture. func create_loot(): # Check if the inventory is not already populated if inventory.get_items() == []: @@ -104,9 +106,17 @@ func get_items(): return inventory.get_children() +func get_item_ids() -> Array[String]: + var returnarray: Array[String] = [] + for item: InventoryItem in inventory.get_items(): + var id = item.prototype_id + returnarray.append(id) + return returnarray + + func get_sprite(): return sprite_3d.texture -func get_inventory(): +func get_inventory() -> InventoryStacked: return inventory From c6a343f1a9ec3909c3ec9736e91e52bad1e72582 Mon Sep 17 00:00:00 2001 From: snipercup <50166150+snipercup@users.noreply.github.com> Date: Mon, 29 Apr 2024 21:30:28 +0200 Subject: [PATCH 05/15] Itemgroups and furniture reference eachother --- ItemProtosets.tres | 33 ++++++++-- Mods/Core/Furniture/Furniture.json | 5 ++ Mods/Core/Itemgroups/Itemgroups.json | 25 +++++++ Mods/Core/Items/Items.json | 33 ++++++++-- .../Custom_Editors/Scripts/FurnitureEditor.gd | 20 +++++- Scripts/gamedata.gd | 66 ++++++++++++++++++- 6 files changed, 163 insertions(+), 19 deletions(-) diff --git a/ItemProtosets.tres b/ItemProtosets.tres index 882b8136..efa76050 100644 --- a/ItemProtosets.tres +++ b/ItemProtosets.tres @@ -181,6 +181,9 @@ json_data = "[ \"height\": \"2\", \"id\": \"bottle_plastic_empty\", \"image\": \"./Mods/Core/Items/bottle_empty_32.png\", + \"itemgroups\": [ + \"cabinet_general\" + ], \"max_stack_size\": \"10\", \"name\": \"Empty plastic bottle\", \"sprite\": \"bottle_empty_32.png\", @@ -216,7 +219,8 @@ json_data = "[ \"id\": \"canned_food\", \"image\": \"./Mods/Core/Items/canned_food_32.png\", \"itemgroups\": [ - \"kitchen_cupboard\" + \"kitchen_cupboard\", + \"cabinet_general\" ], \"max_stack_size\": \"5\", \"name\": \"Canned food\\t\", @@ -262,7 +266,8 @@ json_data = "[ \"id\": \"hammer\", \"image\": \"./Mods/Core/Items/hammer_32.png\", \"itemgroups\": [ - \"kitchen_cupboard\" + \"kitchen_cupboard\", + \"cabinet_general\" ], \"max_stack_size\": \"1\", \"name\": \"Hammer\", @@ -280,7 +285,8 @@ json_data = "[ \"id\": \"screwdriver\", \"image\": \"./Mods/Core/Items/screwdriver_32.png\", \"itemgroups\": [ - \"kitchen_cupboard\" + \"kitchen_cupboard\", + \"cabinet_general\" ], \"max_stack_size\": \"1\", \"name\": \"Screwdriver\", @@ -298,7 +304,8 @@ json_data = "[ \"id\": \"bandage_basic\", \"image\": \"./Mods/Core/Items/bandage_32.png\", \"itemgroups\": [ - \"kitchen_cupboard\" + \"kitchen_cupboard\", + \"cabinet_general\" ], \"max_stack_size\": \"20\", \"name\": \"Bandage\", @@ -393,6 +400,9 @@ json_data = "[ \"iamge\": \"res://Textures/plank.png\", \"id\": \"radio_handheld\", \"image\": \"./Mods/Core/Items/battery_powered_radio_32.png\", + \"itemgroups\": [ + \"cabinet_general\" + ], \"max_stack_size\": \"1\", \"name\": \"Handheld radio\", \"sprite\": \"battery_powered_radio_32.png\", @@ -409,7 +419,8 @@ json_data = "[ \"id\": \"flashlight_basic\", \"image\": \"./Mods/Core/Items/flashlight_32.png\", \"itemgroups\": [ - \"kitchen_cupboard\" + \"kitchen_cupboard\", + \"cabinet_general\" ], \"max_stack_size\": \"1\", \"name\": \"Flashlight\", @@ -442,7 +453,8 @@ json_data = "[ \"id\": \"pack_sigarrettes\", \"image\": \"./Mods/Core/Items/sigarrette_pack_32.png\", \"itemgroups\": [ - \"kitchen_cupboard\" + \"kitchen_cupboard\", + \"cabinet_general\" ], \"max_stack_size\": \"5\", \"name\": \"Pack of sigarrettes\", @@ -505,7 +517,8 @@ json_data = "[ \"id\": \"battery_small\", \"image\": \"./Mods/Core/Items/battery_32.png\", \"itemgroups\": [ - \"kitchen_cupboard\" + \"kitchen_cupboard\", + \"cabinet_general\" ], \"max_stack_size\": \"20\", \"name\": \"Small battery\", @@ -630,6 +643,9 @@ json_data = "[ \"iamge\": \"res://Textures/plank.png\", \"id\": \"radio_two_way\", \"image\": \"./Mods/Core/Items/radio_two_way_32.png\", + \"itemgroups\": [ + \"cabinet_general\" + ], \"max_stack_size\": \"1\", \"name\": \"Two way radio\", \"sprite\": \"radio_two_way_32.png\", @@ -660,6 +676,9 @@ json_data = "[ \"iamge\": \"res://Textures/plank.png\", \"id\": \"kit_fishing_small\", \"image\": \"./Mods/Core/Items/kit_fishing_small_32.png\", + \"itemgroups\": [ + \"cabinet_general\" + ], \"max_stack_size\": \"1\", \"name\": \"Small fishing kit\", \"sprite\": \"kit_fishing_small_32.png\", diff --git a/Mods/Core/Furniture/Furniture.json b/Mods/Core/Furniture/Furniture.json index 21a40aa8..9e33849c 100644 --- a/Mods/Core/Furniture/Furniture.json +++ b/Mods/Core/Furniture/Furniture.json @@ -108,6 +108,11 @@ "sprite": "bed_wood_100_50.png" }, { + "Function": { + "container": { + "itemgroup": "cabinet_general" + } + }, "categories": [ "Urban", "Indoor" diff --git a/Mods/Core/Itemgroups/Itemgroups.json b/Mods/Core/Itemgroups/Itemgroups.json index 4cf29faa..f19e7b41 100644 --- a/Mods/Core/Itemgroups/Itemgroups.json +++ b/Mods/Core/Itemgroups/Itemgroups.json @@ -2,6 +2,9 @@ { "description": "Anything you can find in a kitchen cupboard", "id": "kitchen_cupboard", + "in_furniture": [ + "countertop_wood" + ], "items": [ "screwdriver", "hammer", @@ -28,5 +31,27 @@ ], "name": "Mob loot", "sprite": "machete_32.png" + }, + { + "description": "What you would find in a cabinet in a house, for example in the living room", + "id": "cabinet_general", + "in_furniture": [ + "cabinet_wood_00" + ], + "items": [ + "bottle_plastic_empty", + "canned_food", + "hammer", + "screwdriver", + "bandage_basic", + "flashlight_basic", + "radio_handheld", + "pack_sigarrettes", + "battery_small", + "radio_two_way", + "kit_fishing_small" + ], + "name": "General cabinet contents", + "sprite": "flashlight_32.png" } ] \ No newline at end of file diff --git a/Mods/Core/Items/Items.json b/Mods/Core/Items/Items.json index adfc0985..ce96addb 100644 --- a/Mods/Core/Items/Items.json +++ b/Mods/Core/Items/Items.json @@ -175,6 +175,9 @@ "height": "2", "id": "bottle_plastic_empty", "image": "./Mods/Core/Items/bottle_empty_32.png", + "itemgroups": [ + "cabinet_general" + ], "max_stack_size": "10", "name": "Empty plastic bottle", "sprite": "bottle_empty_32.png", @@ -210,7 +213,8 @@ "id": "canned_food", "image": "./Mods/Core/Items/canned_food_32.png", "itemgroups": [ - "kitchen_cupboard" + "kitchen_cupboard", + "cabinet_general" ], "max_stack_size": "5", "name": "Canned food\t", @@ -256,7 +260,8 @@ "id": "hammer", "image": "./Mods/Core/Items/hammer_32.png", "itemgroups": [ - "kitchen_cupboard" + "kitchen_cupboard", + "cabinet_general" ], "max_stack_size": "1", "name": "Hammer", @@ -274,7 +279,8 @@ "id": "screwdriver", "image": "./Mods/Core/Items/screwdriver_32.png", "itemgroups": [ - "kitchen_cupboard" + "kitchen_cupboard", + "cabinet_general" ], "max_stack_size": "1", "name": "Screwdriver", @@ -292,7 +298,8 @@ "id": "bandage_basic", "image": "./Mods/Core/Items/bandage_32.png", "itemgroups": [ - "kitchen_cupboard" + "kitchen_cupboard", + "cabinet_general" ], "max_stack_size": "20", "name": "Bandage", @@ -387,6 +394,9 @@ "iamge": "res://Textures/plank.png", "id": "radio_handheld", "image": "./Mods/Core/Items/battery_powered_radio_32.png", + "itemgroups": [ + "cabinet_general" + ], "max_stack_size": "1", "name": "Handheld radio", "sprite": "battery_powered_radio_32.png", @@ -403,7 +413,8 @@ "id": "flashlight_basic", "image": "./Mods/Core/Items/flashlight_32.png", "itemgroups": [ - "kitchen_cupboard" + "kitchen_cupboard", + "cabinet_general" ], "max_stack_size": "1", "name": "Flashlight", @@ -436,7 +447,8 @@ "id": "pack_sigarrettes", "image": "./Mods/Core/Items/sigarrette_pack_32.png", "itemgroups": [ - "kitchen_cupboard" + "kitchen_cupboard", + "cabinet_general" ], "max_stack_size": "5", "name": "Pack of sigarrettes", @@ -499,7 +511,8 @@ "id": "battery_small", "image": "./Mods/Core/Items/battery_32.png", "itemgroups": [ - "kitchen_cupboard" + "kitchen_cupboard", + "cabinet_general" ], "max_stack_size": "20", "name": "Small battery", @@ -624,6 +637,9 @@ "iamge": "res://Textures/plank.png", "id": "radio_two_way", "image": "./Mods/Core/Items/radio_two_way_32.png", + "itemgroups": [ + "cabinet_general" + ], "max_stack_size": "1", "name": "Two way radio", "sprite": "radio_two_way_32.png", @@ -654,6 +670,9 @@ "iamge": "res://Textures/plank.png", "id": "kit_fishing_small", "image": "./Mods/Core/Items/kit_fishing_small_32.png", + "itemgroups": [ + "cabinet_general" + ], "max_stack_size": "1", "name": "Small fishing kit", "sprite": "kit_fishing_small_32.png", diff --git a/Scenes/ContentManager/Custom_Editors/Scripts/FurnitureEditor.gd b/Scenes/ContentManager/Custom_Editors/Scripts/FurnitureEditor.gd index ae21811e..30f7c0c0 100644 --- a/Scenes/ContentManager/Custom_Editors/Scripts/FurnitureEditor.gd +++ b/Scenes/ContentManager/Custom_Editors/Scripts/FurnitureEditor.gd @@ -20,15 +20,23 @@ extends Control # For controlling the focus when the tab button is pressed var control_elements: Array = [] +# Original state of the container itemgroup for comparison when the itemgroup chanes +var original_itemgroup: String = "" # This signal will be emitted when the user presses the save button # This signal should alert Gamedata that the furniture data array should be saved to disk # The content editor has connected this signal to Gamedata already signal data_changed() +# Signal for container changes specifically +signal itemgroup_changed(old_group, new_group, furniture_id) + func _ready(): + # For properly using the tab key to switch elements control_elements = [furnitureImageDisplay,NameTextEdit,DescriptionTextEdit] - + itemgroup_changed.connect(Gamedata.on_furniture_itemgroup_changed) + + # The data that represents this furniture # The data is selected from the Gamedata.data.furniture.data array # based on the ID that the user has selected in the content editor @@ -59,14 +67,15 @@ func load_furniture_data(): select_option_by_string(edgeSnappingOptionButton, contentData["edgesnapping"]) if doorOptionButton: update_door_option(contentData.get("Function", {}).get("door", "None")) - + # Load container data if it exists within the 'Function' property var function_data = contentData.get("Function", {}) if "container" in function_data: containerCheckBox.button_pressed = true # Check the container checkbox var container_data = function_data["container"] if "itemgroup" in container_data: - containerTextEdit.text = container_data["itemgroup"] # Set the text edit with the itemgroup ID + containerTextEdit.text = container_data["itemgroup"] # Set text edit with the itemgroup ID + original_itemgroup = containerTextEdit.text # Initialize the original state else: containerTextEdit.clear() # Clear the text edit if no itemgroup is specified else: @@ -131,6 +140,11 @@ func _on_save_button_button_up(): # If the 'Function' dictionary becomes empty, remove it as well if contentData["Function"].is_empty(): contentData.erase("Function") + + var current_itemgroup = containerTextEdit.text + if original_itemgroup != current_itemgroup: + itemgroup_changed.emit(contentData["id"], original_itemgroup, current_itemgroup) + original_itemgroup = current_itemgroup # Update the original state data_changed.emit() diff --git a/Scripts/gamedata.gd b/Scripts/gamedata.gd index 5d837826..cbf1d04d 100644 --- a/Scripts/gamedata.gd +++ b/Scripts/gamedata.gd @@ -274,7 +274,6 @@ func get_items_by_type(item_type: String) -> Array: return filtered_items - # An itemgroup has been changed. Update items that were added or removed from the list # oldlist and newlist are arrays with item id strings in them func on_itemgroup_changed(itemgroup: String, oldlist: Array[String], newlist: Array[String]): @@ -335,6 +334,23 @@ func on_itemgroup_deleted(itemgroup_id: String): if itemgroup_data.is_empty(): print("Itemgroup with ID", itemgroup_id, "not found.") return + + # Handling the in_furniture attribute for furniture items + if "in_furniture" in itemgroup_data: + var furniture_ids = itemgroup_data["in_furniture"] + for furniture_id in furniture_ids: + var furniture_data = get_data_by_id(Gamedata.data.furniture, furniture_id) + if furniture_data.is_empty(): + print("Furniture with ID", furniture_id, "not found.") + continue + + # Navigate through the nested dictionary safely + if "Function" in furniture_data and "container" in furniture_data["Function"]: + if "itemgroup" in furniture_data["Function"]["container"]: + # Check if this itemgroup matches the one being deleted + if furniture_data["Function"]["container"]["itemgroup"] == itemgroup_id: + furniture_data["Function"]["container"].erase("itemgroup") + changes_made = true # The itemgroup data contains a list of item IDs in an 'items' attribute if "items" in itemgroup_data: @@ -353,10 +369,56 @@ func on_itemgroup_deleted(itemgroup_id: String): # If no more itemgroups are left in the item, consider removing the 'itemgroups' attribute if item_data["itemgroups"].size() == 0: item_data.erase("itemgroups") - + # Save changes to the data file if any changes were made if changes_made: save_data_to_file(Gamedata.data.items) + save_data_to_file(Gamedata.data.furniture) print("Itemgroup", itemgroup_id, "has been successfully deleted from all items.") else: print("No changes needed for itemgroup", itemgroup_id) + + +# Some furniture has had their itemgroup changed +# We need to update the relation between the furniture and the itemgroup +func on_furniture_itemgroup_changed(furniture_id: String, old_group: String, new_group: String): + # Exit if old_group and new_group are the same + if old_group == new_group: + print_debug("No change in itemgroup. Exiting function.") + return + var changes_made = false + + # Handle the old itemgroup + if old_group != "": + var old_itemgroup_data = get_data_by_id(Gamedata.data.itemgroups, old_group) + if old_itemgroup_data.has("in_furniture"): + # Check if the furniture_id is in the old itemgroup and remove it + if furniture_id in old_itemgroup_data["in_furniture"]: + old_itemgroup_data["in_furniture"].erase(furniture_id) + changes_made = true + # If in_furniture is now empty, remove the property + if old_itemgroup_data["in_furniture"].size() == 0: + old_itemgroup_data.erase("in_furniture") + + # Handle the new itemgroup + if new_group != "": + var new_itemgroup_data = get_data_by_id(Gamedata.data.itemgroups, new_group) + # Ensure the new group has the 'in_furniture' list initialized + if not new_itemgroup_data.has("in_furniture"): + new_itemgroup_data["in_furniture"] = [] + # Add the furniture_id to the new itemgroup if it's not already there + if furniture_id not in new_itemgroup_data["in_furniture"]: + new_itemgroup_data["in_furniture"].append(furniture_id) + changes_made = true + + # Save changes if any modifications were made + if changes_made: + if old_group != "": + save_data_to_file(Gamedata.data.itemgroups) + if new_group != "" and new_group != old_group: + save_data_to_file(Gamedata.data.itemgroups) + + if changes_made: + print_debug("Furniture itemgroup changes saved successfully.") + else: + print_debug("No changes were made to furniture itemgroups.") From dc8e444624bdeae26d5bdc68ec95d48cbe9be13b Mon Sep 17 00:00:00 2001 From: snipercup <50166150+snipercup@users.noreply.github.com> Date: Thu, 2 May 2024 22:16:32 +0200 Subject: [PATCH 06/15] Remove itemgroups property from items --- Mods/Core/Items/Items.json | 73 -------------------------------------- 1 file changed, 73 deletions(-) diff --git a/Mods/Core/Items/Items.json b/Mods/Core/Items/Items.json index ce96addb..b49d441f 100644 --- a/Mods/Core/Items/Items.json +++ b/Mods/Core/Items/Items.json @@ -2,12 +2,8 @@ { "description": "A wooden plank that could be used for all kinds of crafting and construction", "height": "2", - "iamge": "res://Textures/plank.png", "id": "plank_2x4", "image": "./Mods/Core/Items/plank.png", - "itemgroups": [ - "mob_loot" - ], "max_stack_size": "3", "name": "Plank 2x4", "sprite": "plank.png", @@ -22,9 +18,6 @@ "height": "2", "id": "steel_scrap", "image": "./Mods/Core/Items/steel_scrap.png", - "itemgroups": [ - "mob_loot" - ], "max_stack_size": "100", "name": "Steel scrap", "sprite": "steel_scrap.png", @@ -42,9 +35,6 @@ "height": "1", "id": "bullet_9mm", "image": "./Mods/Core/Items/9mm.png", - "itemgroups": [ - "mob_loot" - ], "max_stack_size": "100", "name": "bullet 9mm", "sprite": "9mm.png", @@ -64,9 +54,6 @@ "height": "1", "id": "pistol_magazine", "image": "./Mods/Core/Items/pistol_magazine.png", - "itemgroups": [ - "mob_loot" - ], "max_stack_size": "1", "name": "Pistol magazine", "sprite": "pistol_magazine.png", @@ -175,9 +162,6 @@ "height": "2", "id": "bottle_plastic_empty", "image": "./Mods/Core/Items/bottle_empty_32.png", - "itemgroups": [ - "cabinet_general" - ], "max_stack_size": "10", "name": "Empty plastic bottle", "sprite": "bottle_empty_32.png", @@ -195,9 +179,6 @@ "height": "2", "id": "bottle_plastic_water", "image": "./Mods/Core/Items/bottle_water_32.png", - "itemgroups": [ - "kitchen_cupboard" - ], "max_stack_size": "10", "name": "Plastic bottle with water", "sprite": "bottle_water_32.png", @@ -212,10 +193,6 @@ "height": "2", "id": "canned_food", "image": "./Mods/Core/Items/canned_food_32.png", - "itemgroups": [ - "kitchen_cupboard", - "cabinet_general" - ], "max_stack_size": "5", "name": "Canned food\t", "sprite": "canned_food_32.png", @@ -259,10 +236,6 @@ "iamge": "res://Textures/plank.png", "id": "hammer", "image": "./Mods/Core/Items/hammer_32.png", - "itemgroups": [ - "kitchen_cupboard", - "cabinet_general" - ], "max_stack_size": "1", "name": "Hammer", "sprite": "hammer_32.png", @@ -275,13 +248,8 @@ { "description": "A handle with a rod of steel in it. The tip can be flat or shaped like a star. Useful for construction projects", "height": "2", - "iamge": "res://Textures/plank.png", "id": "screwdriver", "image": "./Mods/Core/Items/screwdriver_32.png", - "itemgroups": [ - "kitchen_cupboard", - "cabinet_general" - ], "max_stack_size": "1", "name": "Screwdriver", "sprite": "screwdriver_32.png", @@ -294,13 +262,8 @@ { "description": "A basic bandage that will help to heal a wound", "height": "2", - "iamge": "res://Textures/plank.png", "id": "bandage_basic", "image": "./Mods/Core/Items/bandage_32.png", - "itemgroups": [ - "kitchen_cupboard", - "cabinet_general" - ], "max_stack_size": "20", "name": "Bandage", "sprite": "bandage_32.png", @@ -313,12 +276,8 @@ { "description": "A small bottle that contains antibiotics. It helps to recover from disease.", "height": "2", - "iamge": "res://Textures/plank.png", "id": "bottle_antibiotics", "image": "./Mods/Core/Items/antibiotics_32.png", - "itemgroups": [ - "kitchen_cupboard" - ], "max_stack_size": "5", "name": "Bottle of antibiotics", "sprite": "antibiotics_32.png", @@ -391,12 +350,8 @@ { "description": "A small radio that can be powered by batteries or an electical cord.", "height": "2", - "iamge": "res://Textures/plank.png", "id": "radio_handheld", "image": "./Mods/Core/Items/battery_powered_radio_32.png", - "itemgroups": [ - "cabinet_general" - ], "max_stack_size": "1", "name": "Handheld radio", "sprite": "battery_powered_radio_32.png", @@ -409,13 +364,8 @@ { "description": "A basic battery powered flashlight. It will shine a beam of light when it is turned on", "height": "2", - "iamge": "res://Textures/plank.png", "id": "flashlight_basic", "image": "./Mods/Core/Items/flashlight_32.png", - "itemgroups": [ - "kitchen_cupboard", - "cabinet_general" - ], "max_stack_size": "1", "name": "Flashlight", "sprite": "flashlight_32.png", @@ -443,13 +393,8 @@ { "description": "It's a carton pack that contains sigarrettes. You need a fire source to light them and start smoking", "height": "2", - "iamge": "res://Textures/plank.png", "id": "pack_sigarrettes", "image": "./Mods/Core/Items/sigarrette_pack_32.png", - "itemgroups": [ - "kitchen_cupboard", - "cabinet_general" - ], "max_stack_size": "5", "name": "Pack of sigarrettes", "sprite": "sigarrette_pack_32.png", @@ -462,7 +407,6 @@ { "description": "A piece of clothing that offers protection from environmental hazards", "height": "2", - "iamge": "res://Textures/plank.png", "id": "gas_mask", "image": "./Mods/Core/Items/gasmask_32.png", "max_stack_size": "1", @@ -507,13 +451,8 @@ { "description": "A very common type of battery that fits most battery-powered devices", "height": "2", - "iamge": "res://Textures/plank.png", "id": "battery_small", "image": "./Mods/Core/Items/battery_32.png", - "itemgroups": [ - "kitchen_cupboard", - "cabinet_general" - ], "max_stack_size": "20", "name": "Small battery", "sprite": "battery_32.png", @@ -616,12 +555,8 @@ { "description": "A small bottle that contains disinfectant. It helps to disinfect wounds", "height": "2", - "iamge": "res://Textures/plank.png", "id": "bottle_disinfectant", "image": "./Mods/Core/Items/bottle_disinfectant_32.png", - "itemgroups": [ - "kitchen_cupboard" - ], "max_stack_size": "5", "name": "Bottle of disinfectant", "sprite": "bottle_disinfectant_32.png", @@ -634,12 +569,8 @@ { "description": "A small radio that allows you to communicate with someone over a large distance", "height": "2", - "iamge": "res://Textures/plank.png", "id": "radio_two_way", "image": "./Mods/Core/Items/radio_two_way_32.png", - "itemgroups": [ - "cabinet_general" - ], "max_stack_size": "1", "name": "Two way radio", "sprite": "radio_two_way_32.png", @@ -667,12 +598,8 @@ { "description": "\n A true survival fishing kit in a very small, lightweight package!\n Contains 118 foot line and winder\n 2 lures, 2 hooks\n 2 swivels, 2 weghts\n Comes in a strong durable heavy duty resealable plastic bag", "height": "2", - "iamge": "res://Textures/plank.png", "id": "kit_fishing_small", "image": "./Mods/Core/Items/kit_fishing_small_32.png", - "itemgroups": [ - "cabinet_general" - ], "max_stack_size": "1", "name": "Small fishing kit", "sprite": "kit_fishing_small_32.png", From 67a363064751f89ef6d22482ff62656482bbf67d Mon Sep 17 00:00:00 2001 From: snipercup <50166150+snipercup@users.noreply.github.com> Date: Thu, 2 May 2024 23:18:28 +0200 Subject: [PATCH 07/15] Refactor references in content editor --- ItemProtosets.tres | 73 ------- Mods/Core/Furniture/Furniture.json | 4 +- Mods/Core/Itemgroups/Itemgroups.json | 37 ---- Scripts/ItemRangedEditor.gd | 1 + Scripts/gamedata.gd | 305 ++++++++++++++++++--------- 5 files changed, 213 insertions(+), 207 deletions(-) diff --git a/ItemProtosets.tres b/ItemProtosets.tres index efa76050..800c665d 100644 --- a/ItemProtosets.tres +++ b/ItemProtosets.tres @@ -8,12 +8,8 @@ json_data = "[ { \"description\": \"A wooden plank that could be used for all kinds of crafting and construction\", \"height\": \"2\", - \"iamge\": \"res://Textures/plank.png\", \"id\": \"plank_2x4\", \"image\": \"./Mods/Core/Items/plank.png\", - \"itemgroups\": [ - \"mob_loot\" - ], \"max_stack_size\": \"3\", \"name\": \"Plank 2x4\", \"sprite\": \"plank.png\", @@ -28,9 +24,6 @@ json_data = "[ \"height\": \"2\", \"id\": \"steel_scrap\", \"image\": \"./Mods/Core/Items/steel_scrap.png\", - \"itemgroups\": [ - \"mob_loot\" - ], \"max_stack_size\": \"100\", \"name\": \"Steel scrap\", \"sprite\": \"steel_scrap.png\", @@ -48,9 +41,6 @@ json_data = "[ \"height\": \"1\", \"id\": \"bullet_9mm\", \"image\": \"./Mods/Core/Items/9mm.png\", - \"itemgroups\": [ - \"mob_loot\" - ], \"max_stack_size\": \"100\", \"name\": \"bullet 9mm\", \"sprite\": \"9mm.png\", @@ -70,9 +60,6 @@ json_data = "[ \"height\": \"1\", \"id\": \"pistol_magazine\", \"image\": \"./Mods/Core/Items/pistol_magazine.png\", - \"itemgroups\": [ - \"mob_loot\" - ], \"max_stack_size\": \"1\", \"name\": \"Pistol magazine\", \"sprite\": \"pistol_magazine.png\", @@ -181,9 +168,6 @@ json_data = "[ \"height\": \"2\", \"id\": \"bottle_plastic_empty\", \"image\": \"./Mods/Core/Items/bottle_empty_32.png\", - \"itemgroups\": [ - \"cabinet_general\" - ], \"max_stack_size\": \"10\", \"name\": \"Empty plastic bottle\", \"sprite\": \"bottle_empty_32.png\", @@ -201,9 +185,6 @@ json_data = "[ \"height\": \"2\", \"id\": \"bottle_plastic_water\", \"image\": \"./Mods/Core/Items/bottle_water_32.png\", - \"itemgroups\": [ - \"kitchen_cupboard\" - ], \"max_stack_size\": \"10\", \"name\": \"Plastic bottle with water\", \"sprite\": \"bottle_water_32.png\", @@ -218,10 +199,6 @@ json_data = "[ \"height\": \"2\", \"id\": \"canned_food\", \"image\": \"./Mods/Core/Items/canned_food_32.png\", - \"itemgroups\": [ - \"kitchen_cupboard\", - \"cabinet_general\" - ], \"max_stack_size\": \"5\", \"name\": \"Canned food\\t\", \"sprite\": \"canned_food_32.png\", @@ -265,10 +242,6 @@ json_data = "[ \"iamge\": \"res://Textures/plank.png\", \"id\": \"hammer\", \"image\": \"./Mods/Core/Items/hammer_32.png\", - \"itemgroups\": [ - \"kitchen_cupboard\", - \"cabinet_general\" - ], \"max_stack_size\": \"1\", \"name\": \"Hammer\", \"sprite\": \"hammer_32.png\", @@ -281,13 +254,8 @@ json_data = "[ { \"description\": \"A handle with a rod of steel in it. The tip can be flat or shaped like a star. Useful for construction projects\", \"height\": \"2\", - \"iamge\": \"res://Textures/plank.png\", \"id\": \"screwdriver\", \"image\": \"./Mods/Core/Items/screwdriver_32.png\", - \"itemgroups\": [ - \"kitchen_cupboard\", - \"cabinet_general\" - ], \"max_stack_size\": \"1\", \"name\": \"Screwdriver\", \"sprite\": \"screwdriver_32.png\", @@ -300,13 +268,8 @@ json_data = "[ { \"description\": \"A basic bandage that will help to heal a wound\", \"height\": \"2\", - \"iamge\": \"res://Textures/plank.png\", \"id\": \"bandage_basic\", \"image\": \"./Mods/Core/Items/bandage_32.png\", - \"itemgroups\": [ - \"kitchen_cupboard\", - \"cabinet_general\" - ], \"max_stack_size\": \"20\", \"name\": \"Bandage\", \"sprite\": \"bandage_32.png\", @@ -319,12 +282,8 @@ json_data = "[ { \"description\": \"A small bottle that contains antibiotics. It helps to recover from disease.\", \"height\": \"2\", - \"iamge\": \"res://Textures/plank.png\", \"id\": \"bottle_antibiotics\", \"image\": \"./Mods/Core/Items/antibiotics_32.png\", - \"itemgroups\": [ - \"kitchen_cupboard\" - ], \"max_stack_size\": \"5\", \"name\": \"Bottle of antibiotics\", \"sprite\": \"antibiotics_32.png\", @@ -397,12 +356,8 @@ json_data = "[ { \"description\": \"A small radio that can be powered by batteries or an electical cord.\", \"height\": \"2\", - \"iamge\": \"res://Textures/plank.png\", \"id\": \"radio_handheld\", \"image\": \"./Mods/Core/Items/battery_powered_radio_32.png\", - \"itemgroups\": [ - \"cabinet_general\" - ], \"max_stack_size\": \"1\", \"name\": \"Handheld radio\", \"sprite\": \"battery_powered_radio_32.png\", @@ -415,13 +370,8 @@ json_data = "[ { \"description\": \"A basic battery powered flashlight. It will shine a beam of light when it is turned on\", \"height\": \"2\", - \"iamge\": \"res://Textures/plank.png\", \"id\": \"flashlight_basic\", \"image\": \"./Mods/Core/Items/flashlight_32.png\", - \"itemgroups\": [ - \"kitchen_cupboard\", - \"cabinet_general\" - ], \"max_stack_size\": \"1\", \"name\": \"Flashlight\", \"sprite\": \"flashlight_32.png\", @@ -449,13 +399,8 @@ json_data = "[ { \"description\": \"It's a carton pack that contains sigarrettes. You need a fire source to light them and start smoking\", \"height\": \"2\", - \"iamge\": \"res://Textures/plank.png\", \"id\": \"pack_sigarrettes\", \"image\": \"./Mods/Core/Items/sigarrette_pack_32.png\", - \"itemgroups\": [ - \"kitchen_cupboard\", - \"cabinet_general\" - ], \"max_stack_size\": \"5\", \"name\": \"Pack of sigarrettes\", \"sprite\": \"sigarrette_pack_32.png\", @@ -468,7 +413,6 @@ json_data = "[ { \"description\": \"A piece of clothing that offers protection from environmental hazards\", \"height\": \"2\", - \"iamge\": \"res://Textures/plank.png\", \"id\": \"gas_mask\", \"image\": \"./Mods/Core/Items/gasmask_32.png\", \"max_stack_size\": \"1\", @@ -513,13 +457,8 @@ json_data = "[ { \"description\": \"A very common type of battery that fits most battery-powered devices\", \"height\": \"2\", - \"iamge\": \"res://Textures/plank.png\", \"id\": \"battery_small\", \"image\": \"./Mods/Core/Items/battery_32.png\", - \"itemgroups\": [ - \"kitchen_cupboard\", - \"cabinet_general\" - ], \"max_stack_size\": \"20\", \"name\": \"Small battery\", \"sprite\": \"battery_32.png\", @@ -622,12 +561,8 @@ json_data = "[ { \"description\": \"A small bottle that contains disinfectant. It helps to disinfect wounds\", \"height\": \"2\", - \"iamge\": \"res://Textures/plank.png\", \"id\": \"bottle_disinfectant\", \"image\": \"./Mods/Core/Items/bottle_disinfectant_32.png\", - \"itemgroups\": [ - \"kitchen_cupboard\" - ], \"max_stack_size\": \"5\", \"name\": \"Bottle of disinfectant\", \"sprite\": \"bottle_disinfectant_32.png\", @@ -640,12 +575,8 @@ json_data = "[ { \"description\": \"A small radio that allows you to communicate with someone over a large distance\", \"height\": \"2\", - \"iamge\": \"res://Textures/plank.png\", \"id\": \"radio_two_way\", \"image\": \"./Mods/Core/Items/radio_two_way_32.png\", - \"itemgroups\": [ - \"cabinet_general\" - ], \"max_stack_size\": \"1\", \"name\": \"Two way radio\", \"sprite\": \"radio_two_way_32.png\", @@ -673,12 +604,8 @@ json_data = "[ { \"description\": \"\\n A true survival fishing kit in a very small, lightweight package!\\n Contains 118 foot line and winder\\n 2 lures, 2 hooks\\n 2 swivels, 2 weghts\\n Comes in a strong durable heavy duty resealable plastic bag\", \"height\": \"2\", - \"iamge\": \"res://Textures/plank.png\", \"id\": \"kit_fishing_small\", \"image\": \"./Mods/Core/Items/kit_fishing_small_32.png\", - \"itemgroups\": [ - \"cabinet_general\" - ], \"max_stack_size\": \"1\", \"name\": \"Small fishing kit\", \"sprite\": \"kit_fishing_small_32.png\", diff --git a/Mods/Core/Furniture/Furniture.json b/Mods/Core/Furniture/Furniture.json index 9e33849c..20053bf4 100644 --- a/Mods/Core/Furniture/Furniture.json +++ b/Mods/Core/Furniture/Furniture.json @@ -25,7 +25,7 @@ { "Function": { "container": { - "itemgroup": "kitchen_cupboard" + } }, "categories": [ @@ -110,7 +110,7 @@ { "Function": { "container": { - "itemgroup": "cabinet_general" + } }, "categories": [ diff --git a/Mods/Core/Itemgroups/Itemgroups.json b/Mods/Core/Itemgroups/Itemgroups.json index f19e7b41..aab044cc 100644 --- a/Mods/Core/Itemgroups/Itemgroups.json +++ b/Mods/Core/Itemgroups/Itemgroups.json @@ -2,55 +2,18 @@ { "description": "Anything you can find in a kitchen cupboard", "id": "kitchen_cupboard", - "in_furniture": [ - "countertop_wood" - ], - "items": [ - "screwdriver", - "hammer", - "bandage_basic", - "bottle_antibiotics", - "flashlight_basic", - "pack_sigarrettes", - "battery_small", - "bottle_disinfectant", - "canned_food", - "bottle_plastic_water" - ], "name": "Kitchen cupboard", "sprite": "canned_food_32.png" }, { "description": "Loot that's dropped by a mob", "id": "mob_loot", - "items": [ - "bullet_9mm", - "pistol_magazine", - "steel_scrap", - "plank_2x4" - ], "name": "Mob loot", "sprite": "machete_32.png" }, { "description": "What you would find in a cabinet in a house, for example in the living room", "id": "cabinet_general", - "in_furniture": [ - "cabinet_wood_00" - ], - "items": [ - "bottle_plastic_empty", - "canned_food", - "hammer", - "screwdriver", - "bandage_basic", - "flashlight_basic", - "radio_handheld", - "pack_sigarrettes", - "battery_small", - "radio_two_way", - "kit_fishing_small" - ], "name": "General cabinet contents", "sprite": "flashlight_32.png" } diff --git a/Scripts/ItemRangedEditor.gd b/Scripts/ItemRangedEditor.gd index e6547e21..6971de9b 100644 --- a/Scripts/ItemRangedEditor.gd +++ b/Scripts/ItemRangedEditor.gd @@ -20,6 +20,7 @@ func _ready(): var magazines = Gamedata.get_items_by_type("Magazine") initialize_magazine_selection(magazines) + func initialize_magazine_selection(magazines: Array): for magazine in magazines: var magazine_button = CheckBox.new() diff --git a/Scripts/gamedata.gd b/Scripts/gamedata.gd index cbf1d04d..eef24780 100644 --- a/Scripts/gamedata.gd +++ b/Scripts/gamedata.gd @@ -106,7 +106,7 @@ func duplicate_item_in_data(contentData: Dictionary, id: String, newID: String): item_to_duplicate["id"] = newID # Add the duplicated item to the JSON data. contentData.data.append(item_to_duplicate) - Helper.json_helper.write_json_file(contentData.dataPath,JSON.stringify(contentData.data)) + Helper.json_helper.write_json_file(contentData.dataPath,JSON.stringify(contentData.data,"\t")) else: print_debug("There should be code here for when a file in the gets duplicated") @@ -170,8 +170,7 @@ func remove_item_from_data(contentData: Dictionary, id: String): if contentData.data.is_empty(): return if contentData.data[0] is Dictionary: - if contentData == Gamedata.data.itemgroups: - on_itemgroup_deleted(id) + remove_relations_of_deleted_id(contentData, id) contentData.data.remove_at(get_array_index_by_id(contentData, id)) save_data_to_file(contentData) elif contentData.data[0] is String: @@ -244,7 +243,7 @@ func update_item_protoset_json_data(tres_path: String, new_json_data: String) -> # Load the ItemProtoset resource var item_protoset = load(tres_path) as ItemProtoset if not item_protoset: - print("Failed to load ItemProtoset resource from:", tres_path) + print_debug("Failed to load ItemProtoset resource from:", tres_path) return # Update the json_data property @@ -253,9 +252,9 @@ func update_item_protoset_json_data(tres_path: String, new_json_data: String) -> # Save the resource back to the .tres file var save_result = ResourceSaver.save(item_protoset, tres_path) if save_result != OK: - print("Failed to save updated ItemProtoset resource to:", tres_path) + print_debug("Failed to save updated ItemProtoset resource to:", tres_path) else: - print("ItemProtoset resource updated and saved successfully to:", tres_path) + print_debug("ItemProtoset resource updated and saved successfully to:", tres_path) # Function to filter items by type @@ -278,43 +277,19 @@ func get_items_by_type(item_type: String) -> Array: # oldlist and newlist are arrays with item id strings in them func on_itemgroup_changed(itemgroup: String, oldlist: Array[String], newlist: Array[String]): var changes_made = false - - # Dictionary to keep track of current memberships - var membership = {} + # Remove itemgroup from items in the old list that are not in the new list for item_id in oldlist: - membership[item_id] = false # Assume all old items are not in the new list initially + if item_id not in newlist: + # Call remove_reference to remove the itemgroup from this item + changes_made = remove_reference(Gamedata.data.items, "core", "itemgroups", \ + item_id, itemgroup) or changes_made - # Process each item in the new list + # Add itemgroup to items in the new list that were not in the old list for item_id in newlist: - var item_data = get_data_by_id(Gamedata.data.items, item_id) - if item_data.is_empty(): - continue # Skip if no data is found for this item - - # Ensure the itemgroups field exists and is a list - if "itemgroups" not in item_data: - item_data["itemgroups"] = [] - - # Add the current itemgroup if it's not already there - if itemgroup not in item_data["itemgroups"]: - item_data["itemgroups"].append(itemgroup) - changes_made = true - - # Mark this item as processed - membership[item_id] = true - - # Check old items to see if they should be removed from the itemgroup - for item_id in oldlist: - if not membership[item_id]: # This item was not in the new list - var item_data = get_data_by_id(Gamedata.data.items, item_id) - if item_data.is_empty() or "itemgroups" not in item_data: - continue # Skip if no data or no itemgroups property is found - - if itemgroup in item_data["itemgroups"]: - item_data["itemgroups"].erase(itemgroup) # Remove the itemgroup from the item - # Remove the itemgroups property if it's empty - if item_data["itemgroups"].size() == 0: - item_data.erase("itemgroups") - changes_made = true + if item_id not in oldlist: + # Call add_reference to add the itemgroup to this item + changes_made = add_reference(Gamedata.data.items, "core", "itemgroups", \ + item_id, itemgroup) or changes_made # Save changes if any items were updated if changes_made: @@ -326,57 +301,39 @@ func on_itemgroup_changed(itemgroup: String, oldlist: Array[String], newlist: Ar # We can get the items by calling get_data_by_id(contentData, id) # and getting the items property, which will return an array of item id's # For each item, we have to get the item's data, and delete the itemgroup from the item's itemgroups property if it is present -# Function to handle the deletion of an itemgroup. This will remove the itemgroup from all items that are part of it. func on_itemgroup_deleted(itemgroup_id: String): var changes_made = false var itemgroup_data = get_data_by_id(Gamedata.data.itemgroups, itemgroup_id) - + if itemgroup_data.is_empty(): - print("Itemgroup with ID", itemgroup_id, "not found.") + print_debug("Itemgroup with ID", itemgroup_id, "not found.") return - - # Handling the in_furniture attribute for furniture items - if "in_furniture" in itemgroup_data: - var furniture_ids = itemgroup_data["in_furniture"] - for furniture_id in furniture_ids: - var furniture_data = get_data_by_id(Gamedata.data.furniture, furniture_id) - if furniture_data.is_empty(): - print("Furniture with ID", furniture_id, "not found.") - continue - - # Navigate through the nested dictionary safely - if "Function" in furniture_data and "container" in furniture_data["Function"]: - if "itemgroup" in furniture_data["Function"]["container"]: - # Check if this itemgroup matches the one being deleted - if furniture_data["Function"]["container"]["itemgroup"] == itemgroup_id: - furniture_data["Function"]["container"].erase("itemgroup") - changes_made = true + + # This callable will remove this itemgroups from every furniture that reference this itemgroup. + var myfunc: Callable = func (furn_id): + if erase_property_by_path(Gamedata.data.furniture, furn_id, "Function.container.itemgroup"): + changes_made = true + # Pass the callable to every furniture in the itemgroup's references + # It will call myfunc on every furniture in itemgroup_data.references.core.furniture + execute_callable_on_references_of_type(itemgroup_data, "core", "furniture", myfunc) # The itemgroup data contains a list of item IDs in an 'items' attribute + # Loop over all the items in the list and remove the reference to this itemgroup if "items" in itemgroup_data: var item_ids = itemgroup_data["items"] for item_id in item_ids: - var item_data = get_data_by_id(Gamedata.data.items, item_id) - if item_data.is_empty(): - print("Item with ID", item_id, "not found.") - continue - - # Check if the 'itemgroups' attribute exists and contains the itemgroup to be deleted - if "itemgroups" in item_data and itemgroup_id in item_data["itemgroups"]: - item_data["itemgroups"].erase(itemgroup_id) # Remove the itemgroup from the list - changes_made = true + # Use remove_reference to handle deletion of itemgroup references + changes_made = remove_reference(Gamedata.data.items, "core", "itemgroups", \ + item_id, itemgroup_id) or changes_made - # If no more itemgroups are left in the item, consider removing the 'itemgroups' attribute - if item_data["itemgroups"].size() == 0: - item_data.erase("itemgroups") # Save changes to the data file if any changes were made if changes_made: save_data_to_file(Gamedata.data.items) save_data_to_file(Gamedata.data.furniture) - print("Itemgroup", itemgroup_id, "has been successfully deleted from all items.") + print_debug("Itemgroup", itemgroup_id, "has been successfully deleted from all items.") else: - print("No changes needed for itemgroup", itemgroup_id) + print_debug("No changes needed for itemgroup", itemgroup_id) # Some furniture has had their itemgroup changed @@ -389,27 +346,12 @@ func on_furniture_itemgroup_changed(furniture_id: String, old_group: String, new var changes_made = false # Handle the old itemgroup - if old_group != "": - var old_itemgroup_data = get_data_by_id(Gamedata.data.itemgroups, old_group) - if old_itemgroup_data.has("in_furniture"): - # Check if the furniture_id is in the old itemgroup and remove it - if furniture_id in old_itemgroup_data["in_furniture"]: - old_itemgroup_data["in_furniture"].erase(furniture_id) - changes_made = true - # If in_furniture is now empty, remove the property - if old_itemgroup_data["in_furniture"].size() == 0: - old_itemgroup_data.erase("in_furniture") - - # Handle the new itemgroup - if new_group != "": - var new_itemgroup_data = get_data_by_id(Gamedata.data.itemgroups, new_group) - # Ensure the new group has the 'in_furniture' list initialized - if not new_itemgroup_data.has("in_furniture"): - new_itemgroup_data["in_furniture"] = [] - # Add the furniture_id to the new itemgroup if it's not already there - if furniture_id not in new_itemgroup_data["in_furniture"]: - new_itemgroup_data["in_furniture"].append(furniture_id) - changes_made = true + changes_made = remove_reference(Gamedata.data.itemgroups, "core", "furniture", \ + old_group, furniture_id) or changes_made + + # Handle the new itemgroup the 'or' makes sure changes_made does not change back to false + changes_made = add_reference(Gamedata.data.itemgroups, "core", "furniture", \ + new_group, furniture_id) or changes_made # Save changes if any modifications were made if changes_made: @@ -422,3 +364,176 @@ func on_furniture_itemgroup_changed(furniture_id: String, old_group: String, new print_debug("Furniture itemgroup changes saved successfully.") else: print_debug("No changes were made to furniture itemgroups.") + + +# Adds a reference to an entity +# data = any data group, like Gamedata.data.itemgroups +# type = the type of reference, for example furniture +# onid = where to add the reference to +# refid = The reference to add on the fromid +# Example usage: var changes_made = add_reference(Gamedata.data.itemgroups, "core", +# "furniture", itemgroup_id, furniture_id) +# This example will add the specified furniture from the itemgroup's references +func add_reference(data: Dictionary, module: String, type: String, onid: String, refid: String) -> bool: + var changes_made: bool = false + if onid != "": + var entitydata = get_data_by_id(data, onid) + if not entitydata.has("references"): + entitydata["references"] = {} + if not entitydata["references"].has(module): + entitydata["references"][module] = {} + if not entitydata["references"][module].has(type): + entitydata["references"][module][type] = [] + + if refid not in entitydata["references"][module][type]: + entitydata["references"][module][type].append(refid) + changes_made = true + return changes_made + + +# Removes a reference from an entity. +# data = any data group, like Gamedata.data.itemgroups +# type = the type of reference, for example furniture +# fromid = where to remove the reference from +# refid = The reference to remove from the fromid +# Example usage: var changes_made = remove_reference(Gamedata.data.itemgroups, "core", +# "furniture", itemgroup_id, furniture_id) +# This example will remove the specified furniture from the itemgroup's references +func remove_reference(data: Dictionary, module: String, type: String, fromid: String, refid: String) -> bool: + var changes_made: bool = false + if fromid != "": + var entitydata = get_data_by_id(data, fromid) + if entitydata.has("references") and entitydata["references"].has(module) and entitydata["references"][module].has(type): + var refs = entitydata["references"][module][type] + if refid in refs: + refs.erase(refid) + changes_made = true + # Clean up if necessary + if refs.size() == 0: + entitydata["references"][module].erase(type) + if entitydata["references"][module].is_empty(): + entitydata["references"].erase(module) + if entitydata["references"].is_empty(): + entitydata.erase("references") + return changes_made + + +func remove_relations_of_deleted_id(contentData: Dictionary, id: String): + if contentData == Gamedata.data.itemgroups: + on_itemgroup_deleted(id) + if contentData == Gamedata.data.items: + on_item_deleted(id) + if contentData == Gamedata.data.furniture: + on_furniture_deleted(id) + + +# Erases a nested property from a given dictionary based on a dot-separated path +func erase_property_by_path(data: Dictionary, item_id: String, property_path: String): + var entity_data = get_data_by_id(data, item_id) + if entity_data.is_empty(): + print_debug("Entity with ID", item_id, "not found.") + return false + + # Split the path and process the nesting + var path_parts = property_path.split(".") + var current_dict = entity_data + for i in range(path_parts.size() - 1): # Navigate to the last dictionary + if path_parts[i] in current_dict: + current_dict = current_dict[path_parts[i]] + else: + print_debug("Path not found:", path_parts[i]) + return false + + # Last part of the path is the key to erase + var last_key = path_parts[-1] + if last_key in current_dict: + current_dict.erase(last_key) + print_debug("Property", last_key, "erased successfully.") + return true + else: + print_debug("Property", last_key, "not found.") + return false + + +# An item is being deleted from the data +# We have to remove it from everything that references it +func on_item_deleted(item_id: String): + var changes_made = false + var item_data = get_data_by_id(Gamedata.data.items, item_id) + + if item_data.is_empty(): + print_debug("Item with ID", item_data, "not found.") + return + + # This callable will remove this item from itemgroups that reference this item. + var myfunc: Callable = func (itemgroup_id): + var itemlist: Array = get_property_by_path(Gamedata.data.itemgroups, "items", itemgroup_id) + if item_id in itemlist: + itemlist.erase(item_id) + changes_made = true + # Pass the callable to every itemgroup in the item's references + # It will call myfunc on every itemgroup in item_data.references.core.itemgroups + execute_callable_on_references_of_type(item_data, "core", "itemgroups", myfunc) + + # Save changes to the data file if any changes were made + if changes_made: + save_data_to_file(Gamedata.data.itemgroups) + else: + print_debug("No changes needed for item", item_id) + + +# Some furniture is being deleted from the data +# We have to remove it from everything that references it +func on_furniture_deleted(furniture_id: String): + var changes_made = false + var furniture_data = get_data_by_id(Gamedata.data.furniture, furniture_id) + + if furniture_data.is_empty(): + print_debug("Item with ID", furniture_data, "not found.") + return + + var itemgroup: String = get_property_by_path(Gamedata.data.furniture, \ + "Function.container.itemgroup", furniture_id) + # Handle the old itemgroup + changes_made = remove_reference(Gamedata.data.itemgroups, "core", "furniture", \ + itemgroup, furniture_id) or changes_made + + # Save changes to the data file if any changes were made + if changes_made: + save_data_to_file(Gamedata.data.itemgroups) + else: + print_debug("No changes needed for item", furniture_id) + + +# Executes a callable function on each reference of the given type +# data = json data representing one entity +# module = name of the mod. for example "core" +# type = the type of reference we want to handle. For example "furniture" +# callable = a function to execute on each reference ID +# We will check if data has the ["references"] and [type] properties and execute the callable on each found ID +func execute_callable_on_references_of_type(data: Dictionary, module: String, type: String, callable: Callable): + # Check if 'data' contains a 'references' dictionary and if it contains the specified 'type' + if data.has("references") and data["references"].has(module) and data["references"][module].has(type): + # If the type exists, execute the callable on each ID found under this type + for ref_id in data["references"][module][type]: + callable.call(ref_id) + + +# Retrieves the value of a nested property from a given dictionary based on a dot-separated path +func get_property_by_path(data: Dictionary, property_path: String, entity_id: String) -> Variant: + var entity_data = get_data_by_id(data, entity_id) + if entity_data.is_empty(): + print_debug("Entity with ID", entity_id, "not found.") + return null + return get_nested_data(entity_data, property_path) + + +func get_nested_data(data: Dictionary, path: String) -> Variant: + var parts = path.split(".") + var current = data + for part in parts: + if current.has(part): + current = current[part] + else: + return null + return current From 4812f9fd3b573da764c4bb0b91f7145c392e6972 Mon Sep 17 00:00:00 2001 From: snipercup <50166150+snipercup@users.noreply.github.com> Date: Thu, 2 May 2024 23:24:13 +0200 Subject: [PATCH 08/15] Fill the itemgroups and furniture, remove editor warnings --- Mods/Core/Furniture/Furniture.json | 4 +- Mods/Core/Itemgroups/Itemgroups.json | 45 +++++++++ Mods/Core/Items/Items.json | 139 +++++++++++++++++++++++++++ Scripts/gamedata.gd | 22 ++--- 4 files changed, 197 insertions(+), 13 deletions(-) diff --git a/Mods/Core/Furniture/Furniture.json b/Mods/Core/Furniture/Furniture.json index 20053bf4..9e33849c 100644 --- a/Mods/Core/Furniture/Furniture.json +++ b/Mods/Core/Furniture/Furniture.json @@ -25,7 +25,7 @@ { "Function": { "container": { - + "itemgroup": "kitchen_cupboard" } }, "categories": [ @@ -110,7 +110,7 @@ { "Function": { "container": { - + "itemgroup": "cabinet_general" } }, "categories": [ diff --git a/Mods/Core/Itemgroups/Itemgroups.json b/Mods/Core/Itemgroups/Itemgroups.json index aab044cc..5698dd7a 100644 --- a/Mods/Core/Itemgroups/Itemgroups.json +++ b/Mods/Core/Itemgroups/Itemgroups.json @@ -2,19 +2,64 @@ { "description": "Anything you can find in a kitchen cupboard", "id": "kitchen_cupboard", + "items": [ + "canned_food", + "bottle_plastic_water", + "bottle_plastic_empty", + "screwdriver", + "bandage_basic", + "bottle_antibiotics", + "can_beer", + "bottle_disinfectant" + ], "name": "Kitchen cupboard", + "references": { + "core": { + "furniture": [ + "countertop_wood" + ] + } + }, "sprite": "canned_food_32.png" }, { "description": "Loot that's dropped by a mob", "id": "mob_loot", + "items": [ + "bullet_9mm", + "pistol_magazine", + "steel_scrap", + "plank_2x4" + ], "name": "Mob loot", "sprite": "machete_32.png" }, { "description": "What you would find in a cabinet in a house, for example in the living room", "id": "cabinet_general", + "items": [ + "steel_scrap", + "bullet_9mm", + "pistol_magazine", + "pistol_9mm", + "bottle_plastic_water", + "hammer", + "screwdriver", + "bandage_basic", + "boots", + "can_oil", + "flashlight_basic", + "battery_small", + "book_novel" + ], "name": "General cabinet contents", + "references": { + "core": { + "furniture": [ + "cabinet_wood_00" + ] + } + }, "sprite": "flashlight_32.png" } ] \ No newline at end of file diff --git a/Mods/Core/Items/Items.json b/Mods/Core/Items/Items.json index b49d441f..6c0fd92a 100644 --- a/Mods/Core/Items/Items.json +++ b/Mods/Core/Items/Items.json @@ -6,6 +6,13 @@ "image": "./Mods/Core/Items/plank.png", "max_stack_size": "3", "name": "Plank 2x4", + "references": { + "core": { + "itemgroups": [ + "mob_loot" + ] + } + }, "sprite": "plank.png", "stack_size": "1", "two_handed": false, @@ -20,6 +27,14 @@ "image": "./Mods/Core/Items/steel_scrap.png", "max_stack_size": "100", "name": "Steel scrap", + "references": { + "core": { + "itemgroups": [ + "cabinet_general", + "mob_loot" + ] + } + }, "sprite": "steel_scrap.png", "stack_size": "2", "two_handed": false, @@ -37,6 +52,14 @@ "image": "./Mods/Core/Items/9mm.png", "max_stack_size": "100", "name": "bullet 9mm", + "references": { + "core": { + "itemgroups": [ + "cabinet_general", + "mob_loot" + ] + } + }, "sprite": "9mm.png", "stack_size": "20", "two_handed": false, @@ -56,6 +79,14 @@ "image": "./Mods/Core/Items/pistol_magazine.png", "max_stack_size": "1", "name": "Pistol magazine", + "references": { + "core": { + "itemgroups": [ + "cabinet_general", + "mob_loot" + ] + } + }, "sprite": "pistol_magazine.png", "stack_size": "1", "two_handed": false, @@ -81,6 +112,13 @@ "image": "./Mods/Core/Items/pistol_32.png", "max_stack_size": "1", "name": "Pistol 9mm", + "references": { + "core": { + "itemgroups": [ + "cabinet_general" + ] + } + }, "sprite": "pistol_32.png", "stack_size": "1", "two_handed": false, @@ -164,6 +202,13 @@ "image": "./Mods/Core/Items/bottle_empty_32.png", "max_stack_size": "10", "name": "Empty plastic bottle", + "references": { + "core": { + "itemgroups": [ + "kitchen_cupboard" + ] + } + }, "sprite": "bottle_empty_32.png", "stack_size": "2", "two_handed": false, @@ -181,6 +226,14 @@ "image": "./Mods/Core/Items/bottle_water_32.png", "max_stack_size": "10", "name": "Plastic bottle with water", + "references": { + "core": { + "itemgroups": [ + "cabinet_general", + "kitchen_cupboard" + ] + } + }, "sprite": "bottle_water_32.png", "stack_size": "2", "two_handed": false, @@ -195,6 +248,13 @@ "image": "./Mods/Core/Items/canned_food_32.png", "max_stack_size": "5", "name": "Canned food\t", + "references": { + "core": { + "itemgroups": [ + "kitchen_cupboard" + ] + } + }, "sprite": "canned_food_32.png", "stack_size": "1", "two_handed": false, @@ -238,6 +298,13 @@ "image": "./Mods/Core/Items/hammer_32.png", "max_stack_size": "1", "name": "Hammer", + "references": { + "core": { + "itemgroups": [ + "cabinet_general" + ] + } + }, "sprite": "hammer_32.png", "stack_size": "1", "two_handed": false, @@ -252,6 +319,14 @@ "image": "./Mods/Core/Items/screwdriver_32.png", "max_stack_size": "1", "name": "Screwdriver", + "references": { + "core": { + "itemgroups": [ + "cabinet_general", + "kitchen_cupboard" + ] + } + }, "sprite": "screwdriver_32.png", "stack_size": "1", "two_handed": false, @@ -266,6 +341,14 @@ "image": "./Mods/Core/Items/bandage_32.png", "max_stack_size": "20", "name": "Bandage", + "references": { + "core": { + "itemgroups": [ + "cabinet_general", + "kitchen_cupboard" + ] + } + }, "sprite": "bandage_32.png", "stack_size": "3", "two_handed": false, @@ -280,6 +363,13 @@ "image": "./Mods/Core/Items/antibiotics_32.png", "max_stack_size": "5", "name": "Bottle of antibiotics", + "references": { + "core": { + "itemgroups": [ + "kitchen_cupboard" + ] + } + }, "sprite": "antibiotics_32.png", "stack_size": "1", "two_handed": false, @@ -310,6 +400,13 @@ "image": "./Mods/Core/Items/boots_32.png", "max_stack_size": "1", "name": "Boots", + "references": { + "core": { + "itemgroups": [ + "cabinet_general" + ] + } + }, "sprite": "boots_32.png", "stack_size": "1", "two_handed": false, @@ -340,6 +437,13 @@ "image": "./Mods/Core/Items/can_oil_32.png", "max_stack_size": "1", "name": "Can of oil", + "references": { + "core": { + "itemgroups": [ + "cabinet_general" + ] + } + }, "sprite": "can_oil_32.png", "stack_size": "1", "two_handed": false, @@ -368,6 +472,13 @@ "image": "./Mods/Core/Items/flashlight_32.png", "max_stack_size": "1", "name": "Flashlight", + "references": { + "core": { + "itemgroups": [ + "cabinet_general" + ] + } + }, "sprite": "flashlight_32.png", "stack_size": "1", "two_handed": false, @@ -383,6 +494,13 @@ "image": "./Mods/Core/Items/can_beer_32.png", "max_stack_size": "5", "name": "Can of beer", + "references": { + "core": { + "itemgroups": [ + "kitchen_cupboard" + ] + } + }, "sprite": "can_beer_32.png", "stack_size": "2", "two_handed": false, @@ -455,6 +573,13 @@ "image": "./Mods/Core/Items/battery_32.png", "max_stack_size": "20", "name": "Small battery", + "references": { + "core": { + "itemgroups": [ + "cabinet_general" + ] + } + }, "sprite": "battery_32.png", "stack_size": "2", "two_handed": false, @@ -545,6 +670,13 @@ "image": "./Mods/Core/Items/book_novel_32.png", "max_stack_size": "5", "name": "Novel", + "references": { + "core": { + "itemgroups": [ + "cabinet_general" + ] + } + }, "sprite": "book_novel_32.png", "stack_size": "1", "two_handed": false, @@ -559,6 +691,13 @@ "image": "./Mods/Core/Items/bottle_disinfectant_32.png", "max_stack_size": "5", "name": "Bottle of disinfectant", + "references": { + "core": { + "itemgroups": [ + "kitchen_cupboard" + ] + } + }, "sprite": "bottle_disinfectant_32.png", "stack_size": "1", "two_handed": false, diff --git a/Scripts/gamedata.gd b/Scripts/gamedata.gd index eef24780..6d77d565 100644 --- a/Scripts/gamedata.gd +++ b/Scripts/gamedata.gd @@ -399,10 +399,10 @@ func add_reference(data: Dictionary, module: String, type: String, onid: String, # Example usage: var changes_made = remove_reference(Gamedata.data.itemgroups, "core", # "furniture", itemgroup_id, furniture_id) # This example will remove the specified furniture from the itemgroup's references -func remove_reference(data: Dictionary, module: String, type: String, fromid: String, refid: String) -> bool: +func remove_reference(mydata: Dictionary, module: String, type: String, fromid: String, refid: String) -> bool: var changes_made: bool = false if fromid != "": - var entitydata = get_data_by_id(data, fromid) + var entitydata = get_data_by_id(mydata, fromid) if entitydata.has("references") and entitydata["references"].has(module) and entitydata["references"][module].has(type): var refs = entitydata["references"][module][type] if refid in refs: @@ -428,8 +428,8 @@ func remove_relations_of_deleted_id(contentData: Dictionary, id: String): # Erases a nested property from a given dictionary based on a dot-separated path -func erase_property_by_path(data: Dictionary, item_id: String, property_path: String): - var entity_data = get_data_by_id(data, item_id) +func erase_property_by_path(mydata: Dictionary, item_id: String, property_path: String): + var entity_data = get_data_by_id(mydata, item_id) if entity_data.is_empty(): print_debug("Entity with ID", item_id, "not found.") return false @@ -511,26 +511,26 @@ func on_furniture_deleted(furniture_id: String): # type = the type of reference we want to handle. For example "furniture" # callable = a function to execute on each reference ID # We will check if data has the ["references"] and [type] properties and execute the callable on each found ID -func execute_callable_on_references_of_type(data: Dictionary, module: String, type: String, callable: Callable): +func execute_callable_on_references_of_type(mydata: Dictionary, module: String, type: String, callable: Callable): # Check if 'data' contains a 'references' dictionary and if it contains the specified 'type' - if data.has("references") and data["references"].has(module) and data["references"][module].has(type): + if mydata.has("references") and mydata["references"].has(module) and mydata["references"][module].has(type): # If the type exists, execute the callable on each ID found under this type - for ref_id in data["references"][module][type]: + for ref_id in mydata["references"][module][type]: callable.call(ref_id) # Retrieves the value of a nested property from a given dictionary based on a dot-separated path -func get_property_by_path(data: Dictionary, property_path: String, entity_id: String) -> Variant: - var entity_data = get_data_by_id(data, entity_id) +func get_property_by_path(mydata: Dictionary, property_path: String, entity_id: String) -> Variant: + var entity_data = get_data_by_id(mydata, entity_id) if entity_data.is_empty(): print_debug("Entity with ID", entity_id, "not found.") return null return get_nested_data(entity_data, property_path) -func get_nested_data(data: Dictionary, path: String) -> Variant: +func get_nested_data(mydata: Dictionary, path: String) -> Variant: var parts = path.split(".") - var current = data + var current = mydata for part in parts: if current.has(part): current = current[part] From 878b150c64091e7290c8128d79bc65e617c3c4e1 Mon Sep 17 00:00:00 2001 From: snipercup <50166150+snipercup@users.noreply.github.com> Date: Fri, 3 May 2024 20:59:40 +0200 Subject: [PATCH 09/15] Add mob reference to itemgroup --- Mods/Core/Itemgroups/Itemgroups.json | 3 + Mods/Core/Tiles/Tiles.json | 3 +- .../Custom_Editors/Scripts/FurnitureEditor.gd | 33 ++---- .../Custom_Editors/Scripts/ItemgroupEditor.gd | 55 ++------- .../Custom_Editors/Scripts/MobEditor.gd | 14 ++- .../Scripts/TerrainTileEditor.gd | 12 +- .../ContentManager/Scripts/contenteditor.gd | 12 +- Scripts/ItemEditor.gd | 12 +- Scripts/gamedata.gd | 111 ++++++++++++------ 9 files changed, 135 insertions(+), 120 deletions(-) diff --git a/Mods/Core/Itemgroups/Itemgroups.json b/Mods/Core/Itemgroups/Itemgroups.json index 5698dd7a..ec8d7a2e 100644 --- a/Mods/Core/Itemgroups/Itemgroups.json +++ b/Mods/Core/Itemgroups/Itemgroups.json @@ -17,6 +17,9 @@ "core": { "furniture": [ "countertop_wood" + ], + "mobs": [ + "scrapwalker" ] } }, diff --git a/Mods/Core/Tiles/Tiles.json b/Mods/Core/Tiles/Tiles.json index fe9e8066..98e2a34c 100644 --- a/Mods/Core/Tiles/Tiles.json +++ b/Mods/Core/Tiles/Tiles.json @@ -7,7 +7,8 @@ "id": "grass_plain", "name": "Plain grass", "shape": "cube", - "sprite": "1.png" + "sprite": "1.png", + "transparent": false }, { "categories": [ diff --git a/Scenes/ContentManager/Custom_Editors/Scripts/FurnitureEditor.gd b/Scenes/ContentManager/Custom_Editors/Scripts/FurnitureEditor.gd index 30f7c0c0..540b0fd9 100644 --- a/Scenes/ContentManager/Custom_Editors/Scripts/FurnitureEditor.gd +++ b/Scenes/ContentManager/Custom_Editors/Scripts/FurnitureEditor.gd @@ -20,23 +20,14 @@ extends Control # For controlling the focus when the tab button is pressed var control_elements: Array = [] -# Original state of the container itemgroup for comparison when the itemgroup chanes -var original_itemgroup: String = "" - -# This signal will be emitted when the user presses the save button -# This signal should alert Gamedata that the furniture data array should be saved to disk -# The content editor has connected this signal to Gamedata already -signal data_changed() -# Signal for container changes specifically -signal itemgroup_changed(old_group, new_group, furniture_id) -func _ready(): - # For properly using the tab key to switch elements - control_elements = [furnitureImageDisplay,NameTextEdit,DescriptionTextEdit] - itemgroup_changed.connect(Gamedata.on_furniture_itemgroup_changed) +# This signal will be emitted when the user presses the save button +# This signal should alert Gamedata that the mob data array should be saved to disk +signal data_changed(game_data: Dictionary, new_data: Dictionary, old_data: Dictionary) +var olddata: Dictionary # Remember what the value of the data was before editing # The data that represents this furniture # The data is selected from the Gamedata.data.furniture.data array # based on the ID that the user has selected in the content editor @@ -45,6 +36,13 @@ var contentData: Dictionary = {}: contentData = value load_furniture_data() furnitureSelector.sprites_collection = Gamedata.data.furniture.sprites + olddata = contentData.duplicate(true) + + +func _ready(): + # For properly using the tab key to switch elements + control_elements = [furnitureImageDisplay,NameTextEdit,DescriptionTextEdit] + data_changed.connect(Gamedata.on_data_changed) func load_furniture_data(): @@ -75,7 +73,6 @@ func load_furniture_data(): var container_data = function_data["container"] if "itemgroup" in container_data: containerTextEdit.text = container_data["itemgroup"] # Set text edit with the itemgroup ID - original_itemgroup = containerTextEdit.text # Initialize the original state else: containerTextEdit.clear() # Clear the text edit if no itemgroup is specified else: @@ -140,13 +137,9 @@ func _on_save_button_button_up(): # If the 'Function' dictionary becomes empty, remove it as well if contentData["Function"].is_empty(): contentData.erase("Function") - - var current_itemgroup = containerTextEdit.text - if original_itemgroup != current_itemgroup: - itemgroup_changed.emit(contentData["id"], original_itemgroup, current_itemgroup) - original_itemgroup = current_itemgroup # Update the original state - data_changed.emit() + data_changed.emit(Gamedata.data.furniture, contentData, olddata) + olddata = contentData.duplicate(true) # If the door function is set, we save the value to contentData diff --git a/Scenes/ContentManager/Custom_Editors/Scripts/ItemgroupEditor.gd b/Scenes/ContentManager/Custom_Editors/Scripts/ItemgroupEditor.gd index 37ace329..f773c081 100644 --- a/Scenes/ContentManager/Custom_Editors/Scripts/ItemgroupEditor.gd +++ b/Scenes/ContentManager/Custom_Editors/Scripts/ItemgroupEditor.gd @@ -15,11 +15,13 @@ extends Control @export var itemList: ItemList = null # For controlling the focus when the tab button is pressed var control_elements: Array = [] -# Will keep track of the id's in the itemgroup list when it was loaded or saved -# This is used to compare the list when it is saved and see the difference -var old_list: Array[String] = [] +# This signal will be emitted when the user presses the save button +# This signal should alert Gamedata that the mob data array should be saved to disk +signal data_changed(game_data: Dictionary, new_data: Dictionary, old_data: Dictionary) + +var olddata: Dictionary # Remember what the value of the data was before editing # The data that represents this itemgroup # The data is selected from the Gamedata.data.itemgroup.data array # based on the ID that the user has selected in the content editor @@ -28,18 +30,12 @@ var contentData: Dictionary = {}: contentData = value load_itemgroup_data() itemgroupSelector.sprites_collection = Gamedata.data.itemgroups.sprites - - -# This signal will be emitted when the user presses the save button -# This signal should alert Gamedata that the itemgroup data array should be saved to disk -# The content editor has connected this signal to Gamedata already -signal data_changed() -signal itemlist_changed(itemgroup: String, oldlist: Array[String], newlist: Array[String]) + olddata = contentData.duplicate(true) func _ready(): control_elements = [itemgroupImageDisplay,NameTextEdit,DescriptionTextEdit] - itemlist_changed.connect(Gamedata.on_itemgroup_changed) + data_changed.connect(Gamedata.on_data_changed) func load_itemgroup_data(): @@ -62,8 +58,6 @@ func load_itemgroup_data(): var item_sprite = Gamedata.get_sprite_by_id(Gamedata.data.items, item_id) var item_index = itemList.add_item(item_data.get("name", "Unnamed Item"), item_sprite) itemList.set_item_metadata(item_index, item_id) - old_list.append(item_id) # Add to old list for comparison - _refresh_old_list() # This function will select the option in the option_button that matches the given string. @@ -102,13 +96,8 @@ func _on_save_button_button_up(): elif contentData.has("items"): contentData.erase("items") # Delete the "items" property if the list is empty - # Check if there are any changes between old and new item lists - if _is_itemList_changed(): - itemlist_changed.emit(contentData.id, old_list, newlist) - - # Update the old_list as the new baseline for future changes - old_list = newlist.duplicate() - data_changed.emit() + data_changed.emit(Gamedata.data.itemgroups, contentData, olddata) + olddata = contentData.duplicate(true) func _input(event): @@ -218,29 +207,3 @@ func _on_remove_item_button_button_up(): print_debug("Item removed at index: " + str(selected_index)) else: print_debug("No item selected to remove") - - -# Function to refresh old_list with the current item IDs in itemList -func _refresh_old_list(): - old_list.clear() # Clear the current contents of old_list - for i in range(itemList.get_item_count()): - var item_id = itemList.get_item_metadata(i) # Get the item ID stored in metadata - old_list.append(item_id) # Add the item ID to old_list - - -# Function to compare old_list with the current itemList IDs -func _is_itemList_changed() -> bool: - var current_list: Array = [] - for i in range(itemList.get_item_count()): - var item_id = itemList.get_item_metadata(i) # Assuming metadata holds the item ID - current_list.append(item_id) - - # Check if old_list and current_list have the same size and the same elements in the same order - if old_list.size() != current_list.size(): - return true # Sizes are different, lists are not the same - - for i in range(old_list.size()): - if old_list[i] != current_list[i]: - return true # Found a difference in one of the positions - - return false # No differences found diff --git a/Scenes/ContentManager/Custom_Editors/Scripts/MobEditor.gd b/Scenes/ContentManager/Custom_Editors/Scripts/MobEditor.gd index 9a0b4591..73b4abb7 100644 --- a/Scenes/ContentManager/Custom_Editors/Scripts/MobEditor.gd +++ b/Scenes/ContentManager/Custom_Editors/Scripts/MobEditor.gd @@ -22,9 +22,9 @@ extends Control @export var ItemGroupTextEdit: TextEdit = null # This signal will be emitted when the user presses the save button # This signal should alert Gamedata that the mob data array should be saved to disk -# The content editor has connected this signal to Gamedata already -signal data_changed() +signal data_changed(game_data: Dictionary, new_data: Dictionary, old_data: Dictionary) +var olddata: Dictionary # Remember what the value of the data was before editing # The data that represents this mob # The data is selected from the Gamedata.data.mobs.data array # based on the ID that the user has selected in the content editor @@ -33,6 +33,12 @@ var contentData: Dictionary = {}: contentData = value load_mob_data() mobSelector.sprites_collection = Gamedata.data.mobs.sprites + olddata = contentData.duplicate(true) + + +func _ready(): + data_changed.connect(Gamedata.on_data_changed) + #This function update the form based on the contentData that has been loaded func load_mob_data() -> void: @@ -90,7 +96,9 @@ func _on_save_button_button_up() -> void: contentData["loot_group"] = ItemGroupTextEdit.text else: contentData.erase("loot_group") - data_changed.emit() + data_changed.emit(Gamedata.data.mobs, contentData, olddata) + olddata = contentData.duplicate(true) + #When the mobImageDisplay is clicked, the user will be prompted to select an image from # "res://Mods/Core/mobs/". The texture of the mobImageDisplay will change to the selected image diff --git a/Scenes/ContentManager/Custom_Editors/Scripts/TerrainTileEditor.gd b/Scenes/ContentManager/Custom_Editors/Scripts/TerrainTileEditor.gd index 19e598bc..693f3f10 100644 --- a/Scenes/ContentManager/Custom_Editors/Scripts/TerrainTileEditor.gd +++ b/Scenes/ContentManager/Custom_Editors/Scripts/TerrainTileEditor.gd @@ -16,10 +16,10 @@ extends Control @export var slopeShapeCheckbox: Button = null @export var transparentCheckbox: Button = null # This signal will be emitted when the user presses the save button -# This signal should alert Gamedata that the tile data array should be saved to disk -# The content editor has connected this signal to Gamedata already -signal data_changed() +# This signal should alert Gamedata that the mob data array should be saved to disk +signal data_changed(game_data: Dictionary, new_data: Dictionary, old_data: Dictionary) +var olddata: Dictionary # Remember what the value of the data was before editing var control_elements: Array = [] # The data that represents this tile # The data is selected from the Gamedata.data.tiles.data array @@ -29,6 +29,7 @@ var contentData: Dictionary = {}: contentData = value load_tile_data() tileSelector.sprites_collection = Gamedata.data.tiles.sprites + olddata = contentData.duplicate(true) func _ready(): @@ -40,6 +41,8 @@ func _ready(): cubeShapeCheckbox, slopeShapeCheckbox ] + data_changed.connect(Gamedata.on_data_changed) + func _input(event): if event.is_action_pressed("ui_focus_next"): @@ -97,7 +100,8 @@ func _on_save_button_button_up(): if slopeShapeCheckbox.button_pressed: contentData["shape"] = "slope" contentData["transparent"] = transparentCheckbox.button_pressed - data_changed.emit() + data_changed.emit(Gamedata.data.tiles, contentData, olddata) + olddata = contentData.duplicate(true) #When the tileImageDisplay is clicked, the user will be prompted to select an image from # "res://Mods/Core/Tiles/". The texture of the tileImageDisplay will change to the selected image diff --git a/Scenes/ContentManager/Scripts/contenteditor.gd b/Scenes/ContentManager/Scripts/contenteditor.gd index 2d1c4082..59e4e501 100644 --- a/Scenes/ContentManager/Scripts/contenteditor.gd +++ b/Scenes/ContentManager/Scripts/contenteditor.gd @@ -77,20 +77,18 @@ func instantiate_editor(data: Dictionary, itemID: String, newEditor: PackedScene tabContainer.add_child(newContentEditor) tabContainer.current_tab = tabContainer.get_child_count()-1 if data.dataPath.ends_with(".json"): + var itemdata: Dictionary = data.data[Gamedata.get_array_index_by_id(data,itemID)] #We only pass the data for the specific id to the editor - newContentEditor.contentData = data.data[Gamedata.get_array_index_by_id(data,itemID)] - #Connect the data_changed signal to the Gamedata.on_data_changed function - #We pass trough the data collection that the changed data belongs to - newContentEditor.data_changed.connect(Gamedata.on_data_changed.bind(data)) - newContentEditor.data_changed.connect(_on_editor_data_changed.bind(data)) + newContentEditor.contentData = itemdata + newContentEditor.data_changed.connect(_on_editor_data_changed) else: #If the data source does not end with json, it's a directory #So now we pass in the file we want the editor to edit newContentEditor.contentSource = data.dataPath + itemID + ".json" -# function to handle data changes -func _on_editor_data_changed(data: Dictionary): +# The content_list that had it's data changed refreshes +func _on_editor_data_changed(data: Dictionary, _newdata: Dictionary, _olddata: Dictionary): for element in content.get_children(): if element.contentData == data: element.load_data() diff --git a/Scripts/ItemEditor.gd b/Scripts/ItemEditor.gd index d507c3fb..9d1d8d5c 100644 --- a/Scripts/ItemEditor.gd +++ b/Scripts/ItemEditor.gd @@ -32,12 +32,11 @@ extends Control @export var TwoHandedCheckBox: CheckBox = null - # This signal will be emitted when the user presses the save button -# This signal should alert Gamedata that the item data array should be saved to disk -# The content editor has connected this signal to Gamedata already -signal data_changed() +# This signal should alert Gamedata that the mob data array should be saved to disk +signal data_changed(game_data: Dictionary, new_data: Dictionary, old_data: Dictionary) +var olddata: Dictionary # Remember what the value of the data was before editing # The data that represents this item # The data is selected from the Gamedata.data.items.data array # based on the ID that the user has selected in the content editor @@ -46,9 +45,11 @@ var contentData: Dictionary = {}: contentData = value load_item_data() itemSelector.sprites_collection = Gamedata.data.items.sprites + olddata = contentData.duplicate(true) func _ready(): refresh_tab_visibility() + data_changed.connect(Gamedata.on_data_changed) #This function update the form based on the contentData that has been loaded func load_item_data() -> void: @@ -120,7 +121,8 @@ func _on_save_button_button_up() -> void: # Delete the property if checkbox is not checked and it exists in contentData if contentData.has(child.text): contentData.erase(child.text) - data_changed.emit() + data_changed.emit(Gamedata.data.items, contentData, olddata) + olddata = contentData.duplicate(true) #When the itemImageDisplay is clicked, the user will be prompted to select an image from diff --git a/Scripts/gamedata.gd b/Scripts/gamedata.gd index 6d77d565..d9b5c20c 100644 --- a/Scripts/gamedata.gd +++ b/Scripts/gamedata.gd @@ -232,7 +232,13 @@ func get_sprite_by_id(contentData: Dictionary, id: String) -> Resource: # The contenteditor (that initializes the individual editors) # connects the changed_data signal to this function # and binds the appropriate data array so it can be saved in this function -func on_data_changed(contentData: Dictionary): +func on_data_changed(contentData: Dictionary, newEntityData: Dictionary, oldEntityData: Dictionary): + if contentData == Gamedata.data.itemgroups: + on_itemgroup_changed(newEntityData, oldEntityData) + if contentData == Gamedata.data.mobs: + on_mob_changed(newEntityData, oldEntityData) + if contentData == Gamedata.data.furniture: + on_furniture_changed(newEntityData, oldEntityData) save_data_to_file(contentData) @@ -275,8 +281,11 @@ func get_items_by_type(item_type: String) -> Array: # An itemgroup has been changed. Update items that were added or removed from the list # oldlist and newlist are arrays with item id strings in them -func on_itemgroup_changed(itemgroup: String, oldlist: Array[String], newlist: Array[String]): +func on_itemgroup_changed(newdata: Dictionary, olddata: Dictionary): var changes_made = false + var oldlist: Array = get_nested_data(olddata, "items") + var newlist: Array = get_nested_data(newdata, "items") + var itemgroup: String = newdata.id # Remove itemgroup from items in the old list that are not in the new list for item_id in oldlist: if item_id not in newlist: @@ -336,36 +345,6 @@ func on_itemgroup_deleted(itemgroup_id: String): print_debug("No changes needed for itemgroup", itemgroup_id) -# Some furniture has had their itemgroup changed -# We need to update the relation between the furniture and the itemgroup -func on_furniture_itemgroup_changed(furniture_id: String, old_group: String, new_group: String): - # Exit if old_group and new_group are the same - if old_group == new_group: - print_debug("No change in itemgroup. Exiting function.") - return - var changes_made = false - - # Handle the old itemgroup - changes_made = remove_reference(Gamedata.data.itemgroups, "core", "furniture", \ - old_group, furniture_id) or changes_made - - # Handle the new itemgroup the 'or' makes sure changes_made does not change back to false - changes_made = add_reference(Gamedata.data.itemgroups, "core", "furniture", \ - new_group, furniture_id) or changes_made - - # Save changes if any modifications were made - if changes_made: - if old_group != "": - save_data_to_file(Gamedata.data.itemgroups) - if new_group != "" and new_group != old_group: - save_data_to_file(Gamedata.data.itemgroups) - - if changes_made: - print_debug("Furniture itemgroup changes saved successfully.") - else: - print_debug("No changes were made to furniture itemgroups.") - - # Adds a reference to an entity # data = any data group, like Gamedata.data.itemgroups # type = the type of reference, for example furniture @@ -374,10 +353,10 @@ func on_furniture_itemgroup_changed(furniture_id: String, old_group: String, new # Example usage: var changes_made = add_reference(Gamedata.data.itemgroups, "core", # "furniture", itemgroup_id, furniture_id) # This example will add the specified furniture from the itemgroup's references -func add_reference(data: Dictionary, module: String, type: String, onid: String, refid: String) -> bool: +func add_reference(mydata: Dictionary, module: String, type: String, onid: String, refid: String) -> bool: var changes_made: bool = false if onid != "": - var entitydata = get_data_by_id(data, onid) + var entitydata = get_data_by_id(mydata, onid) if not entitydata.has("references"): entitydata["references"] = {} if not entitydata["references"].has(module): @@ -537,3 +516,67 @@ func get_nested_data(mydata: Dictionary, path: String) -> Variant: else: return null return current + + +# A mob has been changed. +func on_mob_changed(newdata: Dictionary, olddata: Dictionary): + var old_loot_group: String = olddata.get("loot_group") + var new_loot_group: String = newdata.get("loot_group") + var mob_id: String = newdata.get("id") + # Exit if old_group and new_group are the same + if old_loot_group == new_loot_group: + print_debug("No change in itemgroup. Exiting function.") + return + var changes_made = false + + # This furniture will be removed from the old itemgroup's references + # The 'or' makes sure changes_made does not change back to false + changes_made = remove_reference(Gamedata.data.itemgroups, "core", "mobs", \ + old_loot_group, mob_id) or changes_made + + # This furniture will be added to the new itemgroup's references + # The 'or' makes sure changes_made does not change back to false + changes_made = add_reference(Gamedata.data.itemgroups, "core", "mobs", \ + new_loot_group, mob_id) or changes_made + + # Save changes if any modifications were made + if changes_made: + if old_loot_group != "": + save_data_to_file(Gamedata.data.itemgroups) + if new_loot_group != "" and new_loot_group != old_loot_group: + save_data_to_file(Gamedata.data.itemgroups) + + +# Some furniture has been changed +# We need to update the relation between the furniture and the itemgroup +func on_furniture_changed(newdata: Dictionary, olddata: Dictionary): + var old_group: String = get_nested_data(olddata, "Function.container.itemgroup") + var new_group: String = get_nested_data(newdata, "Function.container.itemgroup") + var furniture_id: String = newdata.id + # Exit if old_group and new_group are the same + if old_group == new_group: + print_debug("No change in itemgroup. Exiting function.") + return + var changes_made = false + + # This furniture will be removed from the old itemgroup's references + # The 'or' makes sure changes_made does not change back to false + changes_made = remove_reference(Gamedata.data.itemgroups, "core", "furniture", \ + old_group, furniture_id) or changes_made + + # This furniture will be added to the new itemgroup's references + # The 'or' makes sure changes_made does not change back to false + changes_made = add_reference(Gamedata.data.itemgroups, "core", "furniture", \ + new_group, furniture_id) or changes_made + + # Save changes if any modifications were made + if changes_made: + if old_group != "": + save_data_to_file(Gamedata.data.itemgroups) + if new_group != "" and new_group != old_group: + save_data_to_file(Gamedata.data.itemgroups) + + if changes_made: + print_debug("Furniture itemgroup changes saved successfully.") + else: + print_debug("No changes were made to furniture itemgroups.") From b66c4427dfe92e9e5d253264c6a57b7bb93cc328 Mon Sep 17 00:00:00 2001 From: snipercup <50166150+snipercup@users.noreply.github.com> Date: Fri, 3 May 2024 20:59:50 +0200 Subject: [PATCH 10/15] Update ItemProtosets.tres --- ItemProtosets.tres | 139 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/ItemProtosets.tres b/ItemProtosets.tres index 800c665d..8cf7295b 100644 --- a/ItemProtosets.tres +++ b/ItemProtosets.tres @@ -12,6 +12,13 @@ json_data = "[ \"image\": \"./Mods/Core/Items/plank.png\", \"max_stack_size\": \"3\", \"name\": \"Plank 2x4\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"mob_loot\" + ] + } + }, \"sprite\": \"plank.png\", \"stack_size\": \"1\", \"two_handed\": false, @@ -26,6 +33,14 @@ json_data = "[ \"image\": \"./Mods/Core/Items/steel_scrap.png\", \"max_stack_size\": \"100\", \"name\": \"Steel scrap\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"cabinet_general\", + \"mob_loot\" + ] + } + }, \"sprite\": \"steel_scrap.png\", \"stack_size\": \"2\", \"two_handed\": false, @@ -43,6 +58,14 @@ json_data = "[ \"image\": \"./Mods/Core/Items/9mm.png\", \"max_stack_size\": \"100\", \"name\": \"bullet 9mm\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"cabinet_general\", + \"mob_loot\" + ] + } + }, \"sprite\": \"9mm.png\", \"stack_size\": \"20\", \"two_handed\": false, @@ -62,6 +85,14 @@ json_data = "[ \"image\": \"./Mods/Core/Items/pistol_magazine.png\", \"max_stack_size\": \"1\", \"name\": \"Pistol magazine\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"cabinet_general\", + \"mob_loot\" + ] + } + }, \"sprite\": \"pistol_magazine.png\", \"stack_size\": \"1\", \"two_handed\": false, @@ -87,6 +118,13 @@ json_data = "[ \"image\": \"./Mods/Core/Items/pistol_32.png\", \"max_stack_size\": \"1\", \"name\": \"Pistol 9mm\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"cabinet_general\" + ] + } + }, \"sprite\": \"pistol_32.png\", \"stack_size\": \"1\", \"two_handed\": false, @@ -170,6 +208,13 @@ json_data = "[ \"image\": \"./Mods/Core/Items/bottle_empty_32.png\", \"max_stack_size\": \"10\", \"name\": \"Empty plastic bottle\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"kitchen_cupboard\" + ] + } + }, \"sprite\": \"bottle_empty_32.png\", \"stack_size\": \"2\", \"two_handed\": false, @@ -187,6 +232,14 @@ json_data = "[ \"image\": \"./Mods/Core/Items/bottle_water_32.png\", \"max_stack_size\": \"10\", \"name\": \"Plastic bottle with water\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"cabinet_general\", + \"kitchen_cupboard\" + ] + } + }, \"sprite\": \"bottle_water_32.png\", \"stack_size\": \"2\", \"two_handed\": false, @@ -201,6 +254,13 @@ json_data = "[ \"image\": \"./Mods/Core/Items/canned_food_32.png\", \"max_stack_size\": \"5\", \"name\": \"Canned food\\t\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"kitchen_cupboard\" + ] + } + }, \"sprite\": \"canned_food_32.png\", \"stack_size\": \"1\", \"two_handed\": false, @@ -244,6 +304,13 @@ json_data = "[ \"image\": \"./Mods/Core/Items/hammer_32.png\", \"max_stack_size\": \"1\", \"name\": \"Hammer\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"cabinet_general\" + ] + } + }, \"sprite\": \"hammer_32.png\", \"stack_size\": \"1\", \"two_handed\": false, @@ -258,6 +325,14 @@ json_data = "[ \"image\": \"./Mods/Core/Items/screwdriver_32.png\", \"max_stack_size\": \"1\", \"name\": \"Screwdriver\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"cabinet_general\", + \"kitchen_cupboard\" + ] + } + }, \"sprite\": \"screwdriver_32.png\", \"stack_size\": \"1\", \"two_handed\": false, @@ -272,6 +347,14 @@ json_data = "[ \"image\": \"./Mods/Core/Items/bandage_32.png\", \"max_stack_size\": \"20\", \"name\": \"Bandage\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"cabinet_general\", + \"kitchen_cupboard\" + ] + } + }, \"sprite\": \"bandage_32.png\", \"stack_size\": \"3\", \"two_handed\": false, @@ -286,6 +369,13 @@ json_data = "[ \"image\": \"./Mods/Core/Items/antibiotics_32.png\", \"max_stack_size\": \"5\", \"name\": \"Bottle of antibiotics\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"kitchen_cupboard\" + ] + } + }, \"sprite\": \"antibiotics_32.png\", \"stack_size\": \"1\", \"two_handed\": false, @@ -316,6 +406,13 @@ json_data = "[ \"image\": \"./Mods/Core/Items/boots_32.png\", \"max_stack_size\": \"1\", \"name\": \"Boots\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"cabinet_general\" + ] + } + }, \"sprite\": \"boots_32.png\", \"stack_size\": \"1\", \"two_handed\": false, @@ -346,6 +443,13 @@ json_data = "[ \"image\": \"./Mods/Core/Items/can_oil_32.png\", \"max_stack_size\": \"1\", \"name\": \"Can of oil\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"cabinet_general\" + ] + } + }, \"sprite\": \"can_oil_32.png\", \"stack_size\": \"1\", \"two_handed\": false, @@ -374,6 +478,13 @@ json_data = "[ \"image\": \"./Mods/Core/Items/flashlight_32.png\", \"max_stack_size\": \"1\", \"name\": \"Flashlight\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"cabinet_general\" + ] + } + }, \"sprite\": \"flashlight_32.png\", \"stack_size\": \"1\", \"two_handed\": false, @@ -389,6 +500,13 @@ json_data = "[ \"image\": \"./Mods/Core/Items/can_beer_32.png\", \"max_stack_size\": \"5\", \"name\": \"Can of beer\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"kitchen_cupboard\" + ] + } + }, \"sprite\": \"can_beer_32.png\", \"stack_size\": \"2\", \"two_handed\": false, @@ -461,6 +579,13 @@ json_data = "[ \"image\": \"./Mods/Core/Items/battery_32.png\", \"max_stack_size\": \"20\", \"name\": \"Small battery\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"cabinet_general\" + ] + } + }, \"sprite\": \"battery_32.png\", \"stack_size\": \"2\", \"two_handed\": false, @@ -551,6 +676,13 @@ json_data = "[ \"image\": \"./Mods/Core/Items/book_novel_32.png\", \"max_stack_size\": \"5\", \"name\": \"Novel\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"cabinet_general\" + ] + } + }, \"sprite\": \"book_novel_32.png\", \"stack_size\": \"1\", \"two_handed\": false, @@ -565,6 +697,13 @@ json_data = "[ \"image\": \"./Mods/Core/Items/bottle_disinfectant_32.png\", \"max_stack_size\": \"5\", \"name\": \"Bottle of disinfectant\", + \"references\": { + \"core\": { + \"itemgroups\": [ + \"kitchen_cupboard\" + ] + } + }, \"sprite\": \"bottle_disinfectant_32.png\", \"stack_size\": \"1\", \"two_handed\": false, From 2e675e88a09039868ef044a2541cdc9f8c9bd835 Mon Sep 17 00:00:00 2001 From: snipercup <50166150+snipercup@users.noreply.github.com> Date: Sat, 4 May 2024 16:37:14 +0200 Subject: [PATCH 11/15] Map references mobs, tiles and furniture --- .../Mapeditor/Scripts/GridContainer.gd | 10 +- Scripts/gamedata.gd | 343 +++++++++++++----- 2 files changed, 265 insertions(+), 88 deletions(-) diff --git a/Scenes/ContentManager/Mapeditor/Scripts/GridContainer.gd b/Scenes/ContentManager/Mapeditor/Scripts/GridContainer.gd index 3c535998..7d1c5829 100644 --- a/Scenes/ContentManager/Mapeditor/Scripts/GridContainer.gd +++ b/Scenes/ContentManager/Mapeditor/Scripts/GridContainer.gd @@ -36,10 +36,11 @@ var start_point = Vector2() var end_point = Vector2() var is_drawing = false var snapLevel: Vector2 = Vector2(snapAmount, snapAmount).round() -# Variable to hold copied tile data along with dimensions +# Variable to hold copied tile data along with dimensions. Used for copy-pasting. var copied_tiles_info: Dictionary = {"tiles_data": [], "all_levels_data": [], "width": 0, "height": 0} +var olddata: Dictionary #Used to remember the mapdata before it was changed #Contains map metadata like size as well as the data on all levels var mapData: Dictionary = defaultMapData.duplicate(): set(data): @@ -49,6 +50,7 @@ var mapData: Dictionary = defaultMapData.duplicate(): mapData = data.duplicate() loadLevelData(currentLevel) signal zoom_level_changed(zoom_level: int) +signal map_data_changed(map_path: String, new_data: Dictionary, old_data: Dictionary) # This function is called when the parent mapeditor node is ready @@ -61,6 +63,7 @@ func _on_mapeditor_ready() -> void: levelgrid_below.hide() levelgrid_above.hide() _on_zoom_level_changed(mapEditor.zoom_level) + map_data_changed.connect(Gamedata.on_mapdata_changed) # This function will fill fill this GridContainer with a grid of 32x32 instances of "res://Scenes/ContentManager/Mapeditor/mapeditortile.tscn" @@ -521,15 +524,16 @@ func _on_show_above_toggled(button_pressed): func save_map_json_file(): # Convert the TileGrid.mapData to a JSON string storeLevelData() + map_data_changed.emit(mapEditor.contentSource, mapData, olddata) var map_data_json = JSON.stringify(mapData.duplicate(), "\t") Helper.json_helper.write_json_file(mapEditor.contentSource, map_data_json) + olddata = mapData.duplicate(true) func load_map_json_file(): var fileToLoad: String = mapEditor.contentSource mapData = Helper.json_helper.load_json_dictionary_file(fileToLoad) - - + olddata = mapData.duplicate(true) # Function to create a 128x128 miniature map of the current level diff --git a/Scripts/gamedata.gd b/Scripts/gamedata.gd index d9b5c20c..ab59077e 100644 --- a/Scripts/gamedata.gd +++ b/Scripts/gamedata.gd @@ -305,44 +305,6 @@ func on_itemgroup_changed(newdata: Dictionary, olddata: Dictionary): save_data_to_file(Gamedata.data.items) -# An itemgroup is being deleted from the data -# We have to loop over all the items in the itemgroup -# We can get the items by calling get_data_by_id(contentData, id) -# and getting the items property, which will return an array of item id's -# For each item, we have to get the item's data, and delete the itemgroup from the item's itemgroups property if it is present -func on_itemgroup_deleted(itemgroup_id: String): - var changes_made = false - var itemgroup_data = get_data_by_id(Gamedata.data.itemgroups, itemgroup_id) - - if itemgroup_data.is_empty(): - print_debug("Itemgroup with ID", itemgroup_id, "not found.") - return - - # This callable will remove this itemgroups from every furniture that reference this itemgroup. - var myfunc: Callable = func (furn_id): - if erase_property_by_path(Gamedata.data.furniture, furn_id, "Function.container.itemgroup"): - changes_made = true - # Pass the callable to every furniture in the itemgroup's references - # It will call myfunc on every furniture in itemgroup_data.references.core.furniture - execute_callable_on_references_of_type(itemgroup_data, "core", "furniture", myfunc) - - # The itemgroup data contains a list of item IDs in an 'items' attribute - # Loop over all the items in the list and remove the reference to this itemgroup - if "items" in itemgroup_data: - var item_ids = itemgroup_data["items"] - for item_id in item_ids: - # Use remove_reference to handle deletion of itemgroup references - changes_made = remove_reference(Gamedata.data.items, "core", "itemgroups", \ - item_id, itemgroup_id) or changes_made - - - # Save changes to the data file if any changes were made - if changes_made: - save_data_to_file(Gamedata.data.items) - save_data_to_file(Gamedata.data.furniture) - print_debug("Itemgroup", itemgroup_id, "has been successfully deleted from all items.") - else: - print_debug("No changes needed for itemgroup", itemgroup_id) # Adds a reference to an entity @@ -434,54 +396,7 @@ func erase_property_by_path(mydata: Dictionary, item_id: String, property_path: return false -# An item is being deleted from the data -# We have to remove it from everything that references it -func on_item_deleted(item_id: String): - var changes_made = false - var item_data = get_data_by_id(Gamedata.data.items, item_id) - - if item_data.is_empty(): - print_debug("Item with ID", item_data, "not found.") - return - - # This callable will remove this item from itemgroups that reference this item. - var myfunc: Callable = func (itemgroup_id): - var itemlist: Array = get_property_by_path(Gamedata.data.itemgroups, "items", itemgroup_id) - if item_id in itemlist: - itemlist.erase(item_id) - changes_made = true - # Pass the callable to every itemgroup in the item's references - # It will call myfunc on every itemgroup in item_data.references.core.itemgroups - execute_callable_on_references_of_type(item_data, "core", "itemgroups", myfunc) - - # Save changes to the data file if any changes were made - if changes_made: - save_data_to_file(Gamedata.data.itemgroups) - else: - print_debug("No changes needed for item", item_id) - -# Some furniture is being deleted from the data -# We have to remove it from everything that references it -func on_furniture_deleted(furniture_id: String): - var changes_made = false - var furniture_data = get_data_by_id(Gamedata.data.furniture, furniture_id) - - if furniture_data.is_empty(): - print_debug("Item with ID", furniture_data, "not found.") - return - - var itemgroup: String = get_property_by_path(Gamedata.data.furniture, \ - "Function.container.itemgroup", furniture_id) - # Handle the old itemgroup - changes_made = remove_reference(Gamedata.data.itemgroups, "core", "furniture", \ - itemgroup, furniture_id) or changes_made - - # Save changes to the data file if any changes were made - if changes_made: - save_data_to_file(Gamedata.data.itemgroups) - else: - print_debug("No changes needed for item", furniture_id) # Executes a callable function on each reference of the given type @@ -580,3 +495,261 @@ func on_furniture_changed(newdata: Dictionary, olddata: Dictionary): print_debug("Furniture itemgroup changes saved successfully.") else: print_debug("No changes were made to furniture itemgroups.") + + +# Responds to a signal in GridContainer.gd. Is triggered when the user saves a map in the mapeditor +# Check that there is a difference between newdata["levels"] and olddata["levels"] +# It will update the references to this map +func on_mapdata_changed(map_path: String, newdata: Dictionary, olddata: Dictionary): + var max_levels = max(newdata["levels"].size(), olddata["levels"].size()) + var map_id = map_path.get_file().replace(".json", "") + var changes_made = false + + for level_index in range(max_levels): + var new_level = newdata["levels"][level_index] if newdata["levels"].size() > level_index else [] + var old_level = olddata["levels"][level_index] if olddata["levels"].size() > level_index else [] + + if new_level.size() > 0 and old_level.size() == 0: + # Entire level was added + for new_entity in new_level: + if new_entity.has("mob"): + changes_made = add_reference(Gamedata.data.mobs, "core", "maps", \ + map_id, new_entity["mob"]["id"]) or changes_made + if new_entity.has("furniture"): + changes_made = add_reference(Gamedata.data.furniture, "core", "maps", \ + map_id, new_entity["furniture"]["id"]) or changes_made + if new_entity.has("id"): + changes_made = add_reference(Gamedata.data.tiles, "core", "maps", \ + map_id, new_entity["id"]) or changes_made + elif new_level.size() == 0 and old_level.size() > 0: + # Entire level was removed + for old_entity in old_level: + if old_entity.has("mob"): + changes_made = remove_reference(Gamedata.data.mobs, "core", "maps", \ + map_id, old_entity["mob"]["id"]) or changes_made + if old_entity.has("furniture"): + changes_made = remove_reference(Gamedata.data.furniture, "core", "maps", \ + map_id, old_entity["furniture"]["id"]) or changes_made + if old_entity.has("id"): + changes_made = remove_reference(Gamedata.data.tiles, "core", "maps", \ + map_id, old_entity["id"]) or changes_made + else: + # Compare individual entities in levels + for entity_index in range(max(new_level.size(), old_level.size())): + var new_entity = new_level[entity_index] if new_level.size() > entity_index else {} + var old_entity = old_level[entity_index] if old_level.size() > entity_index else {} + + if new_entity.get("mob", {}) != old_entity.get("mob", {}): + if new_entity.has("mob"): + changes_made = add_reference(Gamedata.data.mobs, "core", "maps", \ + map_id, new_entity["mob"]["id"]) or changes_made + if old_entity.has("mob") and not new_entity.has("mob"): + changes_made = remove_reference(Gamedata.data.mobs, "core", "maps", \ + map_id, old_entity["mob"]["id"]) or changes_made + if new_entity.get("furniture", {}) != old_entity.get("furniture", {}): + if new_entity.has("furniture"): + changes_made = add_reference(Gamedata.data.furniture, "core", "maps", \ + map_id, new_entity["furniture"]["id"]) or changes_made + if old_entity.has("furniture") and not new_entity.has("furniture"): + changes_made = remove_reference(Gamedata.data.furniture, "core", "maps", \ + map_id, old_entity["furniture"]["id"]) or changes_made + + if new_entity.get("id", "") != old_entity.get("id", ""): + if new_entity.has("id"): + changes_made = add_reference(Gamedata.data.tiles, "core", "maps", \ + map_id, new_entity["id"]) or changes_made + if old_entity.has("id") and not new_entity.has("id"): + changes_made = remove_reference(Gamedata.data.tiles, "core", "maps", \ + map_id, old_entity["id"]) or changes_made + + if changes_made: + # References have been added to tiles, furniture and/or mobs + # We could track changes individually so we only save what has actually changed. + save_data_to_file(Gamedata.data.tiles) + save_data_to_file(Gamedata.data.furniture) + save_data_to_file(Gamedata.data.mobs) + + +# Removes all instances of the provided entity from the provided map +# map_id is the id of one of the maps. It will be loaded from json to manipulate it. +# entity_type can be "tile", "furniture" or "mob" +# entity_id is the id of the tile, furniture or mob +func remove_entity_from_map(map_id: String, entity_type: String, entity_id: String) -> void: + var fileToLoad = Gamedata.data.maps.dataPath + map_id + ".json" + var mapdata: Dictionary = Helper.json_helper.load_json_dictionary_file(fileToLoad) + if not mapdata.has("levels"): + print("Map data does not contain 'levels'.") + return + + var levels = mapdata["levels"] + # Translate the type to the actual key that we need + if entity_type == "tile": + entity_type = "id" + + # Iterate over each level in the map + for level_index in range(levels.size()): + var level = levels[level_index] + + # Iterate through each entity in the level + for entity_index in range(level.size()): + var entity = level[entity_index] + + match entity_type: + "id": + # Check if the entity's 'id' matches and replace the entire + # entity with an empty object + if entity.get("id", "") == entity_id: + level[entity_index] = {} # Replacing entity with an empty object + "furniture": + # Check if the entity has 'furniture' and the 'id' within it matches + if entity.has("furniture") and entity["furniture"].get("id", "") == entity_id: + entity.erase("furniture") # Removing the furniture object from the entity + "mob": + # Check if the entity has 'mob' and the 'id' within it matches + if entity.has("mob") and entity["mob"].get("id", "") == entity_id: + entity.erase("mob") # Removing the mob object from the entity + + # Update the level in the mapdata after modifications + levels[level_index] = level + + # Update the mapdata levels after processing all + mapdata["levels"] = levels + print_debug("Entity removal operations completed for all levels.") + var map_data_json = JSON.stringify(mapdata.duplicate(), "\t") + Helper.json_helper.write_json_file(fileToLoad, map_data_json) + + +# Some furniture is being deleted from the data +# We have to remove it from everything that references it +func on_furniture_deleted(furniture_id: String): + var changes_made = false + var furniture_data = get_data_by_id(Gamedata.data.furniture, furniture_id) + + if furniture_data.is_empty(): + print_debug("Item with ID", furniture_data, "not found.") + return + + var itemgroup: String = get_property_by_path(Gamedata.data.furniture, \ + "Function.container.itemgroup", furniture_id) + # Handle the old itemgroup + changes_made = remove_reference(Gamedata.data.itemgroups, "core", "furniture", \ + itemgroup, furniture_id) or changes_made + + # Check if the furniture has references to maps and remove it from those maps + var maps = get_nested_data(furniture_data,"references.core.maps") + for map_id in maps: + remove_entity_from_map(map_id, "furniture", furniture_id) + + # Save changes to the data file if any changes were made + if changes_made: + save_data_to_file(Gamedata.data.itemgroups) + else: + print_debug("No changes needed for item", furniture_id) + + +# Some mob is being deleted from the data +# We have to remove it from everything that references it +func on_mob_deleted(mob_id: String): + var changes_made = false + var mob_data = get_data_by_id(Gamedata.data.mobs, mob_id) + if mob_data.is_empty(): + print_debug("Item with ID", mob_data, "not found.") + return + + # Remove the reference to this mob from the loot_group + var loot_group: String = mob_data.get("loot_group") + changes_made = remove_reference(Gamedata.data.itemgroups, "core", "mobs", \ + loot_group, mob_id) or changes_made + + # Check if the mob has references to maps and remove it from those maps + var maps = get_nested_data(mob_data,"references.core.maps") + for map_id in maps: + remove_entity_from_map(map_id, "mob", mob_id) + + # Save changes to the data file if any changes were made + if changes_made: + save_data_to_file(Gamedata.data.itemgroups) + else: + print_debug("No changes needed for item", mob_id) + + +# Some mob is being deleted from the data +# We have to remove it from everything that references it +func on_tile_deleted(tile_id: String): + var tile_data = get_data_by_id(Gamedata.data.tiles, tile_id) + if tile_data.is_empty(): + print_debug("Item with ID", tile_data, "not found.") + return + + # Check if the tile has references to maps and remove it from those maps + var modules = get_nested_data(tile_data,"references") + for mod in modules: + var maps = get_nested_data(mod,"maps") + for map_id in maps: + remove_entity_from_map(map_id, "tile", tile_id) + + +# An item is being deleted from the data +# We have to remove it from everything that references it +func on_item_deleted(item_id: String): + var changes_made = false + var item_data = get_data_by_id(Gamedata.data.items, item_id) + + if item_data.is_empty(): + print_debug("Item with ID", item_data, "not found.") + return + + # This callable will remove this item from itemgroups that reference this item. + var myfunc: Callable = func (itemgroup_id): + var itemlist: Array = get_property_by_path(Gamedata.data.itemgroups, "items", itemgroup_id) + if item_id in itemlist: + itemlist.erase(item_id) + changes_made = true + # Pass the callable to every itemgroup in the item's references + # It will call myfunc on every itemgroup in item_data.references.core.itemgroups + execute_callable_on_references_of_type(item_data, "core", "itemgroups", myfunc) + + # Save changes to the data file if any changes were made + if changes_made: + save_data_to_file(Gamedata.data.itemgroups) + else: + print_debug("No changes needed for item", item_id) + + +# An itemgroup is being deleted from the data +# We have to loop over all the items in the itemgroup +# We can get the items by calling get_data_by_id(contentData, id) +# and getting the items property, which will return an array of item id's +# For each item, we have to get the item's data, and delete the itemgroup from the item's itemgroups property if it is present +func on_itemgroup_deleted(itemgroup_id: String): + var changes_made = false + var itemgroup_data = get_data_by_id(Gamedata.data.itemgroups, itemgroup_id) + + if itemgroup_data.is_empty(): + print_debug("Itemgroup with ID", itemgroup_id, "not found.") + return + + # This callable will remove this itemgroups from every furniture that reference this itemgroup. + var myfunc: Callable = func (furn_id): + if erase_property_by_path(Gamedata.data.furniture, furn_id, "Function.container.itemgroup"): + changes_made = true + # Pass the callable to every furniture in the itemgroup's references + # It will call myfunc on every furniture in itemgroup_data.references.core.furniture + execute_callable_on_references_of_type(itemgroup_data, "core", "furniture", myfunc) + + # The itemgroup data contains a list of item IDs in an 'items' attribute + # Loop over all the items in the list and remove the reference to this itemgroup + if "items" in itemgroup_data: + var item_ids = itemgroup_data["items"] + for item_id in item_ids: + # Use remove_reference to handle deletion of itemgroup references + changes_made = remove_reference(Gamedata.data.items, "core", "itemgroups", \ + item_id, itemgroup_id) or changes_made + + # Save changes to the data file if any changes were made + if changes_made: + save_data_to_file(Gamedata.data.items) + save_data_to_file(Gamedata.data.furniture) + print_debug("Itemgroup", itemgroup_id, "has been successfully deleted from all items.") + else: + print_debug("No changes needed for itemgroup", itemgroup_id) From 44e89e1c695db0b1595b0dbd49e8b445bbfbffbc Mon Sep 17 00:00:00 2001 From: snipercup <50166150+snipercup@users.noreply.github.com> Date: Sat, 4 May 2024 16:54:01 +0200 Subject: [PATCH 12/15] Function to handle map deletion --- Scripts/gamedata.gd | 47 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 41 insertions(+), 6 deletions(-) diff --git a/Scripts/gamedata.gd b/Scripts/gamedata.gd index ab59077e..e5f29603 100644 --- a/Scripts/gamedata.gd +++ b/Scripts/gamedata.gd @@ -169,16 +169,17 @@ func add_id_to_data(contentData: Dictionary, id: String): func remove_item_from_data(contentData: Dictionary, id: String): if contentData.data.is_empty(): return - if contentData.data[0] is Dictionary: - remove_relations_of_deleted_id(contentData, id) + if contentData.datapath.ends_with(".json"): # It's a json file + remove_references_of_deleted_id(contentData, id) contentData.data.remove_at(get_array_index_by_id(contentData, id)) save_data_to_file(contentData) - elif contentData.data[0] is String: + elif contentData.datapath.ends_with("/"): # It's a folder + remove_references_of_deleted_id(contentData, id) contentData.data.erase(id) Helper.json_helper.delete_json_file(contentData.dataPath, id) else: - print_debug("Tried to remove item from data, but the data contains \ - neither Dictionary nor String") + print_debug("Tried to remove item from data, but the data's datapath ends with \ + neither .json nor /") func get_array_index_by_id(contentData: Dictionary, id: String) -> int: @@ -359,13 +360,16 @@ func remove_reference(mydata: Dictionary, module: String, type: String, fromid: return changes_made -func remove_relations_of_deleted_id(contentData: Dictionary, id: String): +# Some kind of entity is deleted. We will remove all references to this entity +func remove_references_of_deleted_id(contentData: Dictionary, id: String): if contentData == Gamedata.data.itemgroups: on_itemgroup_deleted(id) if contentData == Gamedata.data.items: on_item_deleted(id) if contentData == Gamedata.data.furniture: on_furniture_deleted(id) + if contentData == Gamedata.data.maps: + on_map_deleted(id) # Erases a nested property from a given dictionary based on a dot-separated path @@ -753,3 +757,34 @@ func on_itemgroup_deleted(itemgroup_id: String): print_debug("Itemgroup", itemgroup_id, "has been successfully deleted from all items.") else: print_debug("No changes needed for itemgroup", itemgroup_id) + + +# A map is being deleted. Remove all references to this map +func on_map_deleted(map_id: String): + var changes_made = false + var fileToLoad = Gamedata.data.maps.dataPath + map_id + ".json" + var mapdata: Dictionary = Helper.json_helper.load_json_dictionary_file(fileToLoad) + if not mapdata.has("levels"): + print("Map data does not contain 'levels'.") + return + + for level_index in range(mapdata["levels"].size()): + var old_level = mapdata["levels"][level_index] if mapdata["levels"].size() > level_index else [] + # Entire level was removed + for old_entity in old_level: + if old_entity.has("mob"): + changes_made = remove_reference(Gamedata.data.mobs, "core", "maps", \ + map_id, old_entity["mob"]["id"]) or changes_made + if old_entity.has("furniture"): + changes_made = remove_reference(Gamedata.data.furniture, "core", "maps", \ + map_id, old_entity["furniture"]["id"]) or changes_made + if old_entity.has("id"): + changes_made = remove_reference(Gamedata.data.tiles, "core", "maps", \ + map_id, old_entity["id"]) or changes_made + + if changes_made: + # References have been added to tiles, furniture and/or mobs + # We could track changes individually so we only save what has actually changed. + save_data_to_file(Gamedata.data.tiles) + save_data_to_file(Gamedata.data.furniture) + save_data_to_file(Gamedata.data.mobs) From 181dcdc79e8a71cd1c5e97913938e1f9e9ae622f Mon Sep 17 00:00:00 2001 From: snipercup <50166150+snipercup@users.noreply.github.com> Date: Sat, 4 May 2024 17:18:48 +0200 Subject: [PATCH 13/15] Handle duplicated data in references --- Scenes/ContentManager/Scripts/content_list.gd | 8 +++----- Scripts/gamedata.gd | 10 ++++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Scenes/ContentManager/Scripts/content_list.gd b/Scenes/ContentManager/Scripts/content_list.gd index 84ab05db..a981cb07 100644 --- a/Scenes/ContentManager/Scripts/content_list.gd +++ b/Scenes/ContentManager/Scripts/content_list.gd @@ -134,12 +134,10 @@ func _on_ok_button_up(): if popupAction == "Add": Gamedata.add_id_to_data(contentData, myText) if popupAction == "Duplicate": - # This is true if contentData.data is an array of strings - # Else, it will be an array of dictionaries - if contentData.data[0] is String: - Gamedata.duplicate_file_in_data(contentData,get_selected_item_text(),myText) - else: + if contentData.data.datapath.ends_with(".json"): # It's a json file with items Gamedata.duplicate_item_in_data(contentData,get_selected_item_text(),myText) + else: #It's folder with json files + Gamedata.duplicate_file_in_data(contentData,get_selected_item_text(),myText) popupAction = "" load_data() diff --git a/Scripts/gamedata.gd b/Scripts/gamedata.gd index e5f29603..b36b6fe4 100644 --- a/Scripts/gamedata.gd +++ b/Scripts/gamedata.gd @@ -107,6 +107,7 @@ func duplicate_item_in_data(contentData: Dictionary, id: String, newID: String): # Add the duplicated item to the JSON data. contentData.data.append(item_to_duplicate) Helper.json_helper.write_json_file(contentData.dataPath,JSON.stringify(contentData.data,"\t")) + on_data_changed(contentData,item_to_duplicate,{}) else: print_debug("There should be code here for when a file in the gets duplicated") @@ -129,15 +130,16 @@ func duplicate_file_in_data(contentData: Dictionary, original_id: String, new_id var save_result = Helper.json_helper.write_json_file(new_file_path, JSON.stringify(original_content)) if save_result == OK: print_debug("File duplicated successfully: " + new_file_path) - # Add the new ID to the data array if it's managed as an array of IDs. - if contentData.data is Array and typeof(contentData.data[0]) == TYPE_STRING: + # Add the new ID to the data array if it's datapath references a folder. + var datapath: String = contentData.data.datapath + if contentData.data is Array and datapath.ends_with("/"): contentData.data.append(new_id) - save_data_to_file(contentData) # Save the updated data array to file. + if datapath.ends_with("/maps/"): # Update references to this duplicated map + on_mapdata_changed(datapath,original_content,{}) else: print_debug("Failed to duplicate file to: " + new_file_path) - # This function appends a new object to an existing array # Pass the contentData dictionary to this function and the value of the ID # If the data directory ends in .json, it will append an object From 56f208d4f3c943fe5c24bc851a2f346045da9939 Mon Sep 17 00:00:00 2001 From: snipercup <50166150+snipercup@users.noreply.github.com> Date: Sat, 4 May 2024 20:54:36 +0200 Subject: [PATCH 14/15] bug fixing references --- Scenes/ContentManager/Scripts/content_list.gd | 2 +- Scripts/Helper/json_helper.gd | 10 ++- Scripts/gamedata.gd | 70 +++++++++++-------- 3 files changed, 49 insertions(+), 33 deletions(-) diff --git a/Scenes/ContentManager/Scripts/content_list.gd b/Scenes/ContentManager/Scripts/content_list.gd index a981cb07..b1bc776c 100644 --- a/Scenes/ContentManager/Scripts/content_list.gd +++ b/Scenes/ContentManager/Scripts/content_list.gd @@ -134,7 +134,7 @@ func _on_ok_button_up(): if popupAction == "Add": Gamedata.add_id_to_data(contentData, myText) if popupAction == "Duplicate": - if contentData.data.datapath.ends_with(".json"): # It's a json file with items + if contentData.dataPath.ends_with(".json"): # It's a json file with items Gamedata.duplicate_item_in_data(contentData,get_selected_item_text(),myText) else: #It's folder with json files Gamedata.duplicate_file_in_data(contentData,get_selected_item_text(),myText) diff --git a/Scripts/Helper/json_helper.gd b/Scripts/Helper/json_helper.gd index fa2cadb3..f0628a8a 100644 --- a/Scripts/Helper/json_helper.gd +++ b/Scripts/Helper/json_helper.gd @@ -82,7 +82,7 @@ func folder_names_in_dir(path: String) -> Array: #This function takes a json string and saves it as a json file. -func write_json_file(path: String, json: String): +func write_json_file(path: String, json: String) -> Error: # If the file does not exists, we create a new one. if not FileAccess.file_exists(path): create_new_json_file(path) @@ -91,8 +91,10 @@ func write_json_file(path: String, json: String): if file: file.store_string(json) file.close() + return OK else: print_debug("Unable to write file " + path) + return FAILED # This function will take a path and create a new json file with just {} or [] as the contents. @@ -157,10 +159,12 @@ func add_id_to_json_file(source: String, id: String): #This function will take a path to a json file and delete it func delete_json_file(path: String): - var dir = DirAccess.open(path) + var filename: String = path.get_file() + var dirname: String = path.replace(filename,"") + var dir = DirAccess.open(dirname) if dir: # Delete the file - var err = dir.remove(path) + var err = dir.remove(filename) if err == OK: print_debug("File deleted successfully: " + path) else: diff --git a/Scripts/gamedata.gd b/Scripts/gamedata.gd index b36b6fe4..9d498a1f 100644 --- a/Scripts/gamedata.gd +++ b/Scripts/gamedata.gd @@ -124,18 +124,18 @@ func duplicate_file_in_data(contentData: Dictionary, original_id: String, new_id return # Load the original file content. - var original_content = Helper.json_helper.load_json_dictionary_file(original_file_path) + var orig_content = Helper.json_helper.load_json_dictionary_file(original_file_path) # Write the original content to a new file with the new ID. - var save_result = Helper.json_helper.write_json_file(new_file_path, JSON.stringify(original_content)) + var save_result = Helper.json_helper.write_json_file(new_file_path, JSON.stringify(orig_content,"\t")) if save_result == OK: print_debug("File duplicated successfully: " + new_file_path) # Add the new ID to the data array if it's datapath references a folder. - var datapath: String = contentData.data.datapath + var datapath: String = contentData.dataPath if contentData.data is Array and datapath.ends_with("/"): contentData.data.append(new_id) - if datapath.ends_with("/maps/"): # Update references to this duplicated map - on_mapdata_changed(datapath,original_content,{}) + if datapath.ends_with("/Maps/"): # Update references to this duplicated map + on_mapdata_changed(new_file_path,orig_content,{"levels":[]}) else: print_debug("Failed to duplicate file to: " + new_file_path) @@ -171,14 +171,21 @@ func add_id_to_data(contentData: Dictionary, id: String): func remove_item_from_data(contentData: Dictionary, id: String): if contentData.data.is_empty(): return - if contentData.datapath.ends_with(".json"): # It's a json file + if contentData.dataPath.ends_with(".json"): # It's a json file remove_references_of_deleted_id(contentData, id) contentData.data.remove_at(get_array_index_by_id(contentData, id)) save_data_to_file(contentData) - elif contentData.datapath.ends_with("/"): # It's a folder + elif contentData.dataPath.ends_with("/"): # It's a folder remove_references_of_deleted_id(contentData, id) contentData.data.erase(id) - Helper.json_helper.delete_json_file(contentData.dataPath, id) + var json_file_path = contentData.dataPath + id + ".json" + var png_file_path = id + ".png" + Helper.json_helper.delete_json_file(json_file_path) + # Use DirAccess to check and delete the PNG file (for maps) + var dir = DirAccess.open(contentData.dataPath) + if dir.file_exists(png_file_path): + dir.remove(id + ".png") + dir.remove(id + ".png.import") else: print_debug("Tried to remove item from data, but the data's datapath ends with \ neither .json nor /") @@ -372,6 +379,10 @@ func remove_references_of_deleted_id(contentData: Dictionary, id: String): on_furniture_deleted(id) if contentData == Gamedata.data.maps: on_map_deleted(id) + if contentData == Gamedata.data.mobs: + on_mob_deleted(id) + if contentData == Gamedata.data.tiles: + on_tile_deleted(id) # Erases a nested property from a given dictionary based on a dot-separated path @@ -471,8 +482,8 @@ func on_mob_changed(newdata: Dictionary, olddata: Dictionary): # Some furniture has been changed # We need to update the relation between the furniture and the itemgroup func on_furniture_changed(newdata: Dictionary, olddata: Dictionary): - var old_group: String = get_nested_data(olddata, "Function.container.itemgroup") - var new_group: String = get_nested_data(newdata, "Function.container.itemgroup") + var old_group = get_nested_data(olddata, "Function.container.itemgroup") + var new_group = get_nested_data(newdata, "Function.container.itemgroup") var furniture_id: String = newdata.id # Exit if old_group and new_group are the same if old_group == new_group: @@ -482,8 +493,9 @@ func on_furniture_changed(newdata: Dictionary, olddata: Dictionary): # This furniture will be removed from the old itemgroup's references # The 'or' makes sure changes_made does not change back to false - changes_made = remove_reference(Gamedata.data.itemgroups, "core", "furniture", \ - old_group, furniture_id) or changes_made + if old_group: + changes_made = remove_reference(Gamedata.data.itemgroups, "core", "furniture", \ + old_group, furniture_id) or changes_made # This furniture will be added to the new itemgroup's references # The 'or' makes sure changes_made does not change back to false @@ -520,25 +532,25 @@ func on_mapdata_changed(map_path: String, newdata: Dictionary, olddata: Dictiona for new_entity in new_level: if new_entity.has("mob"): changes_made = add_reference(Gamedata.data.mobs, "core", "maps", \ - map_id, new_entity["mob"]["id"]) or changes_made + new_entity["mob"]["id"], map_id) or changes_made if new_entity.has("furniture"): changes_made = add_reference(Gamedata.data.furniture, "core", "maps", \ - map_id, new_entity["furniture"]["id"]) or changes_made + new_entity["furniture"]["id"], map_id) or changes_made if new_entity.has("id"): changes_made = add_reference(Gamedata.data.tiles, "core", "maps", \ - map_id, new_entity["id"]) or changes_made + new_entity["id"], map_id) or changes_made elif new_level.size() == 0 and old_level.size() > 0: # Entire level was removed for old_entity in old_level: if old_entity.has("mob"): changes_made = remove_reference(Gamedata.data.mobs, "core", "maps", \ - map_id, old_entity["mob"]["id"]) or changes_made + old_entity["mob"]["id"], map_id) or changes_made if old_entity.has("furniture"): changes_made = remove_reference(Gamedata.data.furniture, "core", "maps", \ - map_id, old_entity["furniture"]["id"]) or changes_made + old_entity["furniture"]["id"], map_id) or changes_made if old_entity.has("id"): changes_made = remove_reference(Gamedata.data.tiles, "core", "maps", \ - map_id, old_entity["id"]) or changes_made + old_entity["id"], map_id) or changes_made else: # Compare individual entities in levels for entity_index in range(max(new_level.size(), old_level.size())): @@ -548,25 +560,25 @@ func on_mapdata_changed(map_path: String, newdata: Dictionary, olddata: Dictiona if new_entity.get("mob", {}) != old_entity.get("mob", {}): if new_entity.has("mob"): changes_made = add_reference(Gamedata.data.mobs, "core", "maps", \ - map_id, new_entity["mob"]["id"]) or changes_made + new_entity["mob"]["id"], map_id) or changes_made if old_entity.has("mob") and not new_entity.has("mob"): changes_made = remove_reference(Gamedata.data.mobs, "core", "maps", \ - map_id, old_entity["mob"]["id"]) or changes_made + old_entity["mob"]["id"], map_id) or changes_made if new_entity.get("furniture", {}) != old_entity.get("furniture", {}): if new_entity.has("furniture"): changes_made = add_reference(Gamedata.data.furniture, "core", "maps", \ - map_id, new_entity["furniture"]["id"]) or changes_made + new_entity["furniture"]["id"], map_id) or changes_made if old_entity.has("furniture") and not new_entity.has("furniture"): changes_made = remove_reference(Gamedata.data.furniture, "core", "maps", \ - map_id, old_entity["furniture"]["id"]) or changes_made + old_entity["furniture"]["id"], map_id) or changes_made if new_entity.get("id", "") != old_entity.get("id", ""): if new_entity.has("id"): changes_made = add_reference(Gamedata.data.tiles, "core", "maps", \ - map_id, new_entity["id"]) or changes_made + new_entity["id"], map_id) or changes_made if old_entity.has("id") and not new_entity.has("id"): changes_made = remove_reference(Gamedata.data.tiles, "core", "maps", \ - map_id, old_entity["id"]) or changes_made + old_entity["id"], map_id) or changes_made if changes_made: # References have been added to tiles, furniture and/or mobs @@ -688,9 +700,9 @@ func on_tile_deleted(tile_id: String): return # Check if the tile has references to maps and remove it from those maps - var modules = get_nested_data(tile_data,"references") + var modules = tile_data.get("references", []) for mod in modules: - var maps = get_nested_data(mod,"maps") + var maps = get_nested_data(tile_data,"references."+mod+".maps") for map_id in maps: remove_entity_from_map(map_id, "tile", tile_id) @@ -776,13 +788,13 @@ func on_map_deleted(map_id: String): for old_entity in old_level: if old_entity.has("mob"): changes_made = remove_reference(Gamedata.data.mobs, "core", "maps", \ - map_id, old_entity["mob"]["id"]) or changes_made + old_entity["mob"]["id"], map_id) or changes_made if old_entity.has("furniture"): changes_made = remove_reference(Gamedata.data.furniture, "core", "maps", \ - map_id, old_entity["furniture"]["id"]) or changes_made + old_entity["furniture"]["id"], map_id) or changes_made if old_entity.has("id"): changes_made = remove_reference(Gamedata.data.tiles, "core", "maps", \ - map_id, old_entity["id"]) or changes_made + old_entity["id"], map_id) or changes_made if changes_made: # References have been added to tiles, furniture and/or mobs From 5f9bbe338cd5f96cbcca5e40aa4bf19e0eac9bb6 Mon Sep 17 00:00:00 2001 From: snipercup <50166150+snipercup@users.noreply.github.com> Date: Sat, 4 May 2024 21:33:24 +0200 Subject: [PATCH 15/15] Re-think map changes regarding references --- Scripts/gamedata.gd | 136 ++++++++++++++++++++------------------------ 1 file changed, 63 insertions(+), 73 deletions(-) diff --git a/Scripts/gamedata.gd b/Scripts/gamedata.gd index 9d498a1f..3887c3bf 100644 --- a/Scripts/gamedata.gd +++ b/Scripts/gamedata.gd @@ -515,79 +515,6 @@ func on_furniture_changed(newdata: Dictionary, olddata: Dictionary): print_debug("No changes were made to furniture itemgroups.") -# Responds to a signal in GridContainer.gd. Is triggered when the user saves a map in the mapeditor -# Check that there is a difference between newdata["levels"] and olddata["levels"] -# It will update the references to this map -func on_mapdata_changed(map_path: String, newdata: Dictionary, olddata: Dictionary): - var max_levels = max(newdata["levels"].size(), olddata["levels"].size()) - var map_id = map_path.get_file().replace(".json", "") - var changes_made = false - - for level_index in range(max_levels): - var new_level = newdata["levels"][level_index] if newdata["levels"].size() > level_index else [] - var old_level = olddata["levels"][level_index] if olddata["levels"].size() > level_index else [] - - if new_level.size() > 0 and old_level.size() == 0: - # Entire level was added - for new_entity in new_level: - if new_entity.has("mob"): - changes_made = add_reference(Gamedata.data.mobs, "core", "maps", \ - new_entity["mob"]["id"], map_id) or changes_made - if new_entity.has("furniture"): - changes_made = add_reference(Gamedata.data.furniture, "core", "maps", \ - new_entity["furniture"]["id"], map_id) or changes_made - if new_entity.has("id"): - changes_made = add_reference(Gamedata.data.tiles, "core", "maps", \ - new_entity["id"], map_id) or changes_made - elif new_level.size() == 0 and old_level.size() > 0: - # Entire level was removed - for old_entity in old_level: - if old_entity.has("mob"): - changes_made = remove_reference(Gamedata.data.mobs, "core", "maps", \ - old_entity["mob"]["id"], map_id) or changes_made - if old_entity.has("furniture"): - changes_made = remove_reference(Gamedata.data.furniture, "core", "maps", \ - old_entity["furniture"]["id"], map_id) or changes_made - if old_entity.has("id"): - changes_made = remove_reference(Gamedata.data.tiles, "core", "maps", \ - old_entity["id"], map_id) or changes_made - else: - # Compare individual entities in levels - for entity_index in range(max(new_level.size(), old_level.size())): - var new_entity = new_level[entity_index] if new_level.size() > entity_index else {} - var old_entity = old_level[entity_index] if old_level.size() > entity_index else {} - - if new_entity.get("mob", {}) != old_entity.get("mob", {}): - if new_entity.has("mob"): - changes_made = add_reference(Gamedata.data.mobs, "core", "maps", \ - new_entity["mob"]["id"], map_id) or changes_made - if old_entity.has("mob") and not new_entity.has("mob"): - changes_made = remove_reference(Gamedata.data.mobs, "core", "maps", \ - old_entity["mob"]["id"], map_id) or changes_made - if new_entity.get("furniture", {}) != old_entity.get("furniture", {}): - if new_entity.has("furniture"): - changes_made = add_reference(Gamedata.data.furniture, "core", "maps", \ - new_entity["furniture"]["id"], map_id) or changes_made - if old_entity.has("furniture") and not new_entity.has("furniture"): - changes_made = remove_reference(Gamedata.data.furniture, "core", "maps", \ - old_entity["furniture"]["id"], map_id) or changes_made - - if new_entity.get("id", "") != old_entity.get("id", ""): - if new_entity.has("id"): - changes_made = add_reference(Gamedata.data.tiles, "core", "maps", \ - new_entity["id"], map_id) or changes_made - if old_entity.has("id") and not new_entity.has("id"): - changes_made = remove_reference(Gamedata.data.tiles, "core", "maps", \ - old_entity["id"], map_id) or changes_made - - if changes_made: - # References have been added to tiles, furniture and/or mobs - # We could track changes individually so we only save what has actually changed. - save_data_to_file(Gamedata.data.tiles) - save_data_to_file(Gamedata.data.furniture) - save_data_to_file(Gamedata.data.mobs) - - # Removes all instances of the provided entity from the provided map # map_id is the id of one of the maps. It will be loaded from json to manipulate it. # entity_type can be "tile", "furniture" or "mob" @@ -802,3 +729,66 @@ func on_map_deleted(map_id: String): save_data_to_file(Gamedata.data.tiles) save_data_to_file(Gamedata.data.furniture) save_data_to_file(Gamedata.data.mobs) + +# Function to collect unique entities from each level in newdata and olddata +func collect_unique_entities(newdata: Dictionary, olddata: Dictionary) -> Dictionary: + var new_entities = { + "mobs": [], + "furniture": [], + "tiles": [] + } + var old_entities = { + "mobs": [], + "furniture": [], + "tiles": [] + } + + # Collect entities from newdata + for level in newdata.get("levels", []): + add_entities_to_set(level, new_entities) + + # Collect entities from olddata + for level in olddata.get("levels", []): + add_entities_to_set(level, old_entities) + + return {"new_entities": new_entities, "old_entities": old_entities} + + +# Helper function to add entities to the respective sets +func add_entities_to_set(level: Array, entity_set: Dictionary): + for entity in level: + if entity.has("mob") and not entity_set["mobs"].has(entity["mob"]["id"]): + entity_set["mobs"].append(entity["mob"]["id"]) + if entity.has("furniture") and not entity_set["furniture"].has(entity["furniture"]["id"]): + entity_set["furniture"].append(entity["furniture"]["id"]) + if entity.has("id") and not entity_set["tiles"].has(entity["id"]): + entity_set["tiles"].append(entity["id"]) + + +# Function to update map entity references when a map's data changes +func on_mapdata_changed(map_id: String, newdata: Dictionary, olddata: Dictionary): + # Collect unique entities from both new and old data + var entities = collect_unique_entities(newdata, olddata) + var new_entities = entities["new_entities"] + var old_entities = entities["old_entities"] + map_id = map_id.get_file().replace(".json", "") + + # Add references for new entities + for entity_type in new_entities.keys(): + for entity_id in new_entities[entity_type]: + if not old_entities[entity_type].has(entity_id): + add_reference(Gamedata.data[entity_type], "core", "maps", entity_id, map_id) + + # Remove references for entities not present in new data + for entity_type in old_entities.keys(): + for entity_id in old_entities[entity_type]: + if not new_entities[entity_type].has(entity_id): + remove_reference(Gamedata.data[entity_type], "core", "maps", entity_id, map_id) + + # Save changes to the data files if there were any updates + if new_entities["mobs"].size() > 0 or old_entities["mobs"].size() > 0: + save_data_to_file(Gamedata.data.mobs) + if new_entities["furniture"].size() > 0 or old_entities["furniture"].size() > 0: + save_data_to_file(Gamedata.data.furniture) + if new_entities["tiles"].size() > 0 or old_entities["tiles"].size() > 0: + save_data_to_file(Gamedata.data.tiles)