From 2f1020187d4174fa7ce3a7addec75e0beed9e985 Mon Sep 17 00:00:00 2001 From: chrisbell Date: Wed, 22 Oct 2025 16:48:24 -0500 Subject: [PATCH] datapacks refresh, again Reverting datapacks stuff again, I am feeling like my original plan for a small dsl-like format was the correct way to go, but combining that with the datapack archive structure for embedded images and other resources. --- .gitignore | 2 +- sessionzero-client/datapack_test.tres | 10 +- .../scenes/desktop/main_ui.tscn | 15 +- .../scripts/datapacks/datapack_manager.gd | 126 ------ .../scripts/datapacks/datapack_manager.gd.uid | 2 +- .../scripts/datapacks/datapack_tests.gd | 374 ------------------ .../scripts/datapacks/datapack_tests.gd.uid | 1 - .../scripts/datapacks/datapacks.md | 234 +++++++++++ .../datapacks/models/character_template.gd | 13 - .../models/character_template.gd.uid | 1 - .../datapacks/models/datapack_dependency.gd | 6 - .../models/datapack_dependency.gd.uid | 1 - .../datapacks/models/datapack_model.gd | 35 -- .../datapacks/models/datapack_model.gd.uid | 1 - .../scripts/datapacks/models/dataset_model.gd | 5 - .../datapacks/models/dataset_model.gd.uid | 1 - .../datapacks/models/session_template.gd | 13 - .../datapacks/models/session_template.gd.uid | 1 - .../scripts/datapacks/models/szobject.gd | 10 - .../scripts/datapacks/models/szobject.gd.uid | 1 - .../datapacks/models/template_model.gd | 7 - .../datapacks/models/template_model.gd.uid | 1 - .../datapacks/other/data_field_value.gd | 133 ------- .../datapacks/other/data_field_value.gd.uid | 1 - .../scripts/datapacks/other/dataset_entry.gd | 9 - .../datapacks/other/dataset_entry.gd.uid | 1 - .../scripts/datapacks/other/dataset_group.gd | 6 - .../datapacks/other/dataset_group.gd.uid | 1 - .../datapacks/other/dependency_source.gd | 20 - .../datapacks/other/dependency_source.gd.uid | 1 - .../datapacks/other/formula_resolver.gd | 52 --- .../datapacks/other/formula_resolver.gd.uid | 1 - .../scripts/datapacks/other/formula_value.gd | 10 - .../datapacks/other/formula_value.gd.uid | 1 - .../scripts/datapacks/other/list_value.gd | 5 - .../scripts/datapacks/other/list_value.gd.uid | 1 - .../scripts/datapacks/other/template_field.gd | 22 -- .../datapacks/other/template_field.gd.uid | 1 - .../scripts/datapacks/other/template_group.gd | 8 - .../datapacks/other/template_group.gd.uid | 1 - .../scripts/helpers/datapack_creator.gd | 70 ---- .../scripts/helpers/datapack_creator.gd.uid | 1 - .../scripts/helpers/datapack_loader.gd | 46 --- .../scripts/helpers/datapack_loader.gd.uid | 1 - .../scripts/helpers/datetime.gd | 42 -- .../scripts/helpers/datetime.gd.uid | 1 - sessionzero-client/scripts/helpers/guid.gd | 49 --- .../scripts/helpers/guid.gd.uid | 1 - 48 files changed, 255 insertions(+), 1090 deletions(-) delete mode 100644 sessionzero-client/scripts/datapacks/datapack_tests.gd delete mode 100644 sessionzero-client/scripts/datapacks/datapack_tests.gd.uid create mode 100644 sessionzero-client/scripts/datapacks/datapacks.md delete mode 100644 sessionzero-client/scripts/datapacks/models/character_template.gd delete mode 100644 sessionzero-client/scripts/datapacks/models/character_template.gd.uid delete mode 100644 sessionzero-client/scripts/datapacks/models/datapack_dependency.gd delete mode 100644 sessionzero-client/scripts/datapacks/models/datapack_dependency.gd.uid delete mode 100644 sessionzero-client/scripts/datapacks/models/datapack_model.gd delete mode 100644 sessionzero-client/scripts/datapacks/models/datapack_model.gd.uid delete mode 100644 sessionzero-client/scripts/datapacks/models/dataset_model.gd delete mode 100644 sessionzero-client/scripts/datapacks/models/dataset_model.gd.uid delete mode 100644 sessionzero-client/scripts/datapacks/models/session_template.gd delete mode 100644 sessionzero-client/scripts/datapacks/models/session_template.gd.uid delete mode 100644 sessionzero-client/scripts/datapacks/models/szobject.gd delete mode 100644 sessionzero-client/scripts/datapacks/models/szobject.gd.uid delete mode 100644 sessionzero-client/scripts/datapacks/models/template_model.gd delete mode 100644 sessionzero-client/scripts/datapacks/models/template_model.gd.uid delete mode 100644 sessionzero-client/scripts/datapacks/other/data_field_value.gd delete mode 100644 sessionzero-client/scripts/datapacks/other/data_field_value.gd.uid delete mode 100644 sessionzero-client/scripts/datapacks/other/dataset_entry.gd delete mode 100644 sessionzero-client/scripts/datapacks/other/dataset_entry.gd.uid delete mode 100644 sessionzero-client/scripts/datapacks/other/dataset_group.gd delete mode 100644 sessionzero-client/scripts/datapacks/other/dataset_group.gd.uid delete mode 100644 sessionzero-client/scripts/datapacks/other/dependency_source.gd delete mode 100644 sessionzero-client/scripts/datapacks/other/dependency_source.gd.uid delete mode 100644 sessionzero-client/scripts/datapacks/other/formula_resolver.gd delete mode 100644 sessionzero-client/scripts/datapacks/other/formula_resolver.gd.uid delete mode 100644 sessionzero-client/scripts/datapacks/other/formula_value.gd delete mode 100644 sessionzero-client/scripts/datapacks/other/formula_value.gd.uid delete mode 100644 sessionzero-client/scripts/datapacks/other/list_value.gd delete mode 100644 sessionzero-client/scripts/datapacks/other/list_value.gd.uid delete mode 100644 sessionzero-client/scripts/datapacks/other/template_field.gd delete mode 100644 sessionzero-client/scripts/datapacks/other/template_field.gd.uid delete mode 100644 sessionzero-client/scripts/datapacks/other/template_group.gd delete mode 100644 sessionzero-client/scripts/datapacks/other/template_group.gd.uid delete mode 100644 sessionzero-client/scripts/helpers/datapack_creator.gd delete mode 100644 sessionzero-client/scripts/helpers/datapack_creator.gd.uid delete mode 100644 sessionzero-client/scripts/helpers/datapack_loader.gd delete mode 100644 sessionzero-client/scripts/helpers/datapack_loader.gd.uid delete mode 100644 sessionzero-client/scripts/helpers/datetime.gd delete mode 100644 sessionzero-client/scripts/helpers/datetime.gd.uid delete mode 100644 sessionzero-client/scripts/helpers/guid.gd delete mode 100644 sessionzero-client/scripts/helpers/guid.gd.uid diff --git a/.gitignore b/.gitignore index 8cb5096..0090703 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ .hidden/ -.idea/ \ No newline at end of file +.idea/ diff --git a/sessionzero-client/datapack_test.tres b/sessionzero-client/datapack_test.tres index 780d4fb..1283d31 100644 --- a/sessionzero-client/datapack_test.tres +++ b/sessionzero-client/datapack_test.tres @@ -1,10 +1,10 @@ [gd_resource type="Resource" script_class="DatapackModel" load_steps=8 format=3 uid="uid://cuyhpk5xnm735"] -[ext_resource type="Script" uid="uid://1eht88nmv63j" path="res://scripts/datapacks/models/datapack_dependency.gd" id="1_0qrwo"] -[ext_resource type="Script" uid="uid://b2k68if5pcfyn" path="res://scripts/helpers/datetime.gd" id="1_jdefw"] -[ext_resource type="Script" uid="uid://dgj5rubcp6v00" path="res://scripts/datapacks/models/datapack_model.gd" id="2_3lhj8"] -[ext_resource type="Script" uid="uid://xiy5j06o8254" path="res://scripts/helpers/guid.gd" id="3_8vm13"] -[ext_resource type="Script" uid="uid://c8co7n1xvfmou" path="res://scripts/datapacks/models/szobject.gd" id="3_jdefw"] +[ext_resource type="Script" uid="uid://1eht88nmv63j" path="res://scripts/datapacks/old/datapack_dependency.gd" id="1_0qrwo"] +[ext_resource type="Script" uid="uid://b2k68if5pcfyn" path="res://scripts/datapacks/old/helpers/datetime.gd" id="1_jdefw"] +[ext_resource type="Script" uid="uid://dgj5rubcp6v00" path="res://scripts/datapacks/old/datapack_model.gd" id="2_3lhj8"] +[ext_resource type="Script" uid="uid://xiy5j06o8254" path="res://scripts/datapacks/old/helpers/guid.gd" id="3_8vm13"] +[ext_resource type="Script" uid="uid://c8co7n1xvfmou" path="res://scripts/datapacks/old/szobject.gd" id="3_jdefw"] [sub_resource type="Resource" id="Resource_4o47k"] script = ExtResource("1_jdefw") diff --git a/sessionzero-client/scenes/desktop/main_ui.tscn b/sessionzero-client/scenes/desktop/main_ui.tscn index 546fb48..b956d84 100644 --- a/sessionzero-client/scenes/desktop/main_ui.tscn +++ b/sessionzero-client/scenes/desktop/main_ui.tscn @@ -1,4 +1,7 @@ -[gd_scene format=3 uid="uid://fy5iji5t58jk"] +[gd_scene load_steps=3 format=3 uid="uid://fy5iji5t58jk"] + +[ext_resource type="Script" path="res://scripts/datapacks/old/test.gd" id="1_ffwby"] +[ext_resource type="Script" path="res://scripts/datapacks/old/ui_test.gd" id="2_tlkf1"] [node name="MainUI" type="CanvasLayer"] @@ -9,3 +12,13 @@ anchor_bottom = 1.0 grow_horizontal = 2 grow_vertical = 2 theme_type_variation = &"Background_Panel" +script = ExtResource("1_ffwby") + +[node name="Control" type="Control" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("2_tlkf1") diff --git a/sessionzero-client/scripts/datapacks/datapack_manager.gd b/sessionzero-client/scripts/datapacks/datapack_manager.gd index 5d3f842..61510e1 100644 --- a/sessionzero-client/scripts/datapacks/datapack_manager.gd +++ b/sessionzero-client/scripts/datapacks/datapack_manager.gd @@ -1,127 +1 @@ -# datapack_manager.gd extends Node - -# --- CONFIGURATION --- -const DATAPACKS_DIR = "user://datapacks/" - -# --- STATE --- -var loaded_packs: Dictionary[String, DatapackModel] = {} - - -func _ready(): - var dir = DirAccess.open("user://") - if dir: - if not dir.dir_exists("datapacks"): - dir.make_dir("datapacks") - - load_all_local_packs() - - -## Scans the local DATAPACKS_DIR, loads all valid datapack subdirectories, -## and registers them with the manager. This serves as the initial load and the refresh option. -func load_all_local_packs(): - print("DatapackManager: Starting scan for local datapacks in '%s'" % DATAPACKS_DIR) - - var loader = DatapackLoader.new() - var dir = DirAccess.open(DATAPACKS_DIR) - - if dir == null: - push_error("DatapackManager: Failed to open datapacks directory: %s" % DATAPACKS_DIR) - return - - dir.list_dir_begin() - var dir_name = dir.get_next() - - while dir_name != "": - if dir_name == "." or dir_name == "..": - dir_name = dir.get_next() - continue - - if dir.current_is_dir(): - print("DatapackManager: Found pack directory: %s" % dir_name) - - var datapack: DatapackModel = loader.load_datapack(dir_name) - - if datapack: - register_pack(datapack) - else: - push_warning("DatapackManager: Failed to load pack in directory: %s. Skipping." % dir_name) - - dir_name = dir.get_next() - - dir.list_dir_end() - print("DatapackManager: Finished scanning local datapacks.") - - -func clear_loaded_packs(): - loaded_packs.clear() - - -func register_pack(datapack: DatapackModel): - if datapack and datapack.guid: - var guid_str = datapack.guid.to_string() - loaded_packs[guid_str] = datapack - print("DatapackManager: Registered pack '%s' (%s)" % [datapack.name, guid_str]) - else: - push_error("DatapackManager: Attempted to register invalid or un-GUIDed datapack.") - - -func unregister_pack(pack_guid: String): - if loaded_packs.has(pack_guid): - loaded_packs.erase(pack_guid) - print("DatapackManager: Unregistered pack with GUID %s." % pack_guid) - -# ---------------------------------------------------------------------- -# --- DEPENDENCY RESOLUTION --- -# ---------------------------------------------------------------------- - -## Resolves a single SzObject ID across all loaded packs. -## Used by SPECIFIC_DATASET mode when the pack GUID is unknown (or referencing local content). -func resolve_object_dependency(object_id: String) -> SzObject: - for pack_guid in loaded_packs: - var pack = loaded_packs[pack_guid] - var obj = pack.get_object_by_id(object_id) - - if obj: - return obj - - push_error("Dependency Resolution Failed: Could not find object with ID '%s' in any loaded datapack." % object_id) - return null - - -## Finds all SzObjects (Datasets or Templates) across all loaded packs -## that match a given sz_type string. -## Used by DATASET_TYPE_WILDCARD and CHARACTER_TEMPLATE_REF modes. -func resolve_type_wildcard(sz_type: String) -> Array[SzObject]: - var matching_objects: Array[SzObject] = [] - for pack_guid in loaded_packs: - var pack = loaded_packs[pack_guid] - - for obj in pack.sz_objects: - if obj.sz_type == sz_type: - matching_objects.append(obj) - - if matching_objects.is_empty(): - push_warning("Wildcard Resolution Failed: Could not find any object with sz_type '%s' in loaded datapacks." % sz_type) - - return matching_objects - -## Main entry point for resolving a DependencySource defined in a Template. -## Returns an array of SzObjects (can be Datasets or Templates) -func resolve_dependency_source(source: DependencySource) -> Array: - match source.mode: - DependencySource.DependencyMode.SPECIFIC_DATASET: - if source.target_datapack_guid.is_empty(): - var target_obj = resolve_object_dependency(source.target_sz_object_id) - return [target_obj] if target_obj else [] - - var target_pack = loaded_packs.get(source.target_datapack_guid) - if target_pack: - var target_obj = target_pack.get_object_by_id(source.target_sz_object_id) - return [target_obj] if target_obj else [] - return [] - - DependencySource.DependencyMode.DATASET_TYPE_WILDCARD: - return resolve_type_wildcard(source.target_type_string) - - return [] diff --git a/sessionzero-client/scripts/datapacks/datapack_manager.gd.uid b/sessionzero-client/scripts/datapacks/datapack_manager.gd.uid index b3e1b79..d25678d 100644 --- a/sessionzero-client/scripts/datapacks/datapack_manager.gd.uid +++ b/sessionzero-client/scripts/datapacks/datapack_manager.gd.uid @@ -1 +1 @@ -uid://chlpgt0jnkd52 +uid://dsndkirkgjl5q diff --git a/sessionzero-client/scripts/datapacks/datapack_tests.gd b/sessionzero-client/scripts/datapacks/datapack_tests.gd deleted file mode 100644 index ec585c1..0000000 --- a/sessionzero-client/scripts/datapacks/datapack_tests.gd +++ /dev/null @@ -1,374 +0,0 @@ - - - -# ------------------------------- WARNING -------------------------------- -# These tests were auto generated by an LLM according to a design document -# to simply test the workflow. These tests should not be completely relied -# on for tesing full functionality of the datapack system. -# ------------------------------------------------------------------------ - - -class_name DatapackTestNode -extends Node - -# --- PRIMARY PACK CONSTANTS --- -const PACK_NAME = "core_data_test_pack" -const ENTRY_ID = "test_item_sword" -const WEAPONS_DATASET_ID = "weapons_dataset" -const SKILLS_DATASET_ID = "skills_dataset" -const CHAR_TEMPLATE_ID = "base_char_template" -const SESSION_TEMPLATE_ID = "basic_session_template" - -# --- NEW CROSS-PACK CONSTANTS --- -const DEPENDENCY_PACK_NAME = "stat_core_pack" -const DEPENDENCY_DATASET_ID = "core_stats_dataset" -const DEPENDENCY_ENTRY_ID = "base_strength" -const DEPENDER_PACK_NAME = "class_pack" -const DEPENDER_DATASET_ID = "warrior_class" - - -func _ready() -> void: - - print("--- Starting Datapack Test ---") - - # --- Primary Test (Pack 1) --- - var creation_success = _create_and_save_datapack() - if not creation_success: - push_error("TEST FAILED: Datapack creation failed.") - return - - _load_and_verify_datapack() - _test_formula_resolution() - - # --- New Cross-Pack Dependency Test (Pack 2 & 3) --- - _test_cross_datapack_dependency() - - print("--- Datapack Test Complete ---") - -#---------------------------------------------------------------------- -## --- STEP 2: CREATION AND SAVING --- -#---------------------------------------------------------------------- -func _create_and_save_datapack() -> bool: - print("\n--- 2. Creating and Saving Datapack ---") - - # --- 0. Setup Core Data --- - var stats_group := DatasetGroup.new() - stats_group.id = "base_stats" - stats_group.name = "Base Stats" - - # Simple Numeric Field - var damage_field := DataFieldValue.new() - damage_field.field_type = DataFieldValue.DataFieldType.NUMBER - damage_field.value = 12 - stats_group.fields["damage"] = damage_field - - # --- FORMULA Field --- - var formula_res := FormulaValue.new() - formula_res.expression = "Base_Damage * Critical_Multiplier" - - var crit_damage_field := DataFieldValue.new() - crit_damage_field.field_type = DataFieldValue.DataFieldType.FORMULA - crit_damage_field.value = formula_res - stats_group.fields["critical_damage"] = crit_damage_field - - # --- LIST Field --- - var list_res := ListValue.new() - - # Add nested DataFieldValue items to the ListValue resource - var list_item_1 := DataFieldValue.new() - list_item_1.field_type = DataFieldValue.DataFieldType.NUMBER - list_item_1.value = 5 - - var list_item_2 := DataFieldValue.new() - list_item_2.field_type = DataFieldValue.DataFieldType.TEXT - list_item_2.value = "Stun" - - list_res.items.append(list_item_1) - list_res.items.append(list_item_2) - - var effect_list_field := DataFieldValue.new() - effect_list_field.field_type = DataFieldValue.DataFieldType.LIST - effect_list_field.value = list_res - stats_group.fields["on_hit_effects"] = effect_list_field - - # Create Dataset Entry - var sword_entry := DatasetEntry.new() - sword_entry.id = ENTRY_ID - sword_entry.name = "Longsword of Testing" - sword_entry.description = "A sword for unit testing." - sword_entry.groups.append(stats_group) - - # Create Dataset Model (Weapons) - var weapons_dataset := DatasetModel.new() - weapons_dataset.id = WEAPONS_DATASET_ID - weapons_dataset.name = "Weapons" - weapons_dataset.entries[sword_entry.id] = sword_entry - weapons_dataset.sz_type = "Weapon" - - # --- Dependency Setup: Skills Dataset --- - var skills_dataset := DatasetModel.new() - skills_dataset.id = SKILLS_DATASET_ID - skills_dataset.name = "Skills" - skills_dataset.sz_type = "Skill" - - # --- Dependency Setup: CharacterTemplate --- - var char_template := CharacterTemplate.new() - char_template.id = CHAR_TEMPLATE_ID - char_template.name = "Standard Character" - - var skills_dependency := DependencySource.new() - skills_dependency.mode = DependencySource.DependencyMode.SPECIFIC_DATASET - skills_dependency.target_sz_object_id = SKILLS_DATASET_ID - - char_template.data_dependencies["Skills_Source"] = skills_dependency - - # --- Dependency Setup: SessionTemplate --- - var session_template := SessionTemplate.new() - session_template.id = SESSION_TEMPLATE_ID - session_template.name = "Basic Campaign Session" - - var char_dep_source := DependencySource.new() - char_dep_source.mode = DependencySource.DependencyMode.SPECIFIC_DATASET - char_dep_source.target_sz_object_id = CHAR_TEMPLATE_ID - session_template.character_template_ref = char_dep_source - - # --- Create and Save Datapack Model --- - var datapack := DatapackModel.new() - datapack.name = "Test Pack" - datapack.version = "1.0.0" - - datapack.sz_objects.append(weapons_dataset) - datapack.sz_objects.append(skills_dataset) - datapack.sz_objects.append(char_template) - datapack.sz_objects.append(session_template) - - var creator = DatapackCreator.new() - var success = creator.save_datapack(datapack, PACK_NAME) - - return success - -#---------------------------------------------------------------------- -## --- STEP 3: LOADING AND VERIFICATION (Omitted for brevity, assumed stable) --- -#---------------------------------------------------------------------- -func _load_and_verify_datapack() -> void: - print("\n--- 3. Loading and Verification ---") - var loader = DatapackLoader.new() - - var loaded_pack: DatapackModel = loader.load_datapack(PACK_NAME) - - if not loaded_pack: - push_error("TEST FAILED: DatapackLoader returned null.") - return - - DatapackManager.register_pack(loaded_pack) - print("Verification: Datapack registered with DatapackManager.") - - var loaded_weapons_dataset: DatasetModel = loaded_pack.get_object_by_id(WEAPONS_DATASET_ID) - var loaded_entry: DatasetEntry = loaded_weapons_dataset.entries.get(ENTRY_ID) - var loaded_group: DatasetGroup = loaded_entry.groups.filter(func(g): return g.id == "base_stats").front() - var loaded_char_template: CharacterTemplate = loaded_pack.get_object_by_id(CHAR_TEMPLATE_ID) - var loaded_session_template: SessionTemplate = loaded_pack.get_object_by_id(SESSION_TEMPLATE_ID) - - # 1. SessionTemplate Character Reference - var char_ref_dep_source: DependencySource = loaded_session_template.character_template_ref - var resolved_char_template_array = DatapackManager.resolve_dependency_source(char_ref_dep_source) - if resolved_char_template_array.size() == 1 and resolved_char_template_array.front() == loaded_char_template: - print("✅ SUCCESS: SessionTemplate dependency link resolved.") - else: - push_error("TEST FAILED: SessionTemplate character_template_ref resolution failed.") - - # 2. CharacterTemplate Skills Dependency - var skills_dep_source: DependencySource = loaded_char_template.data_dependencies.get("Skills_Source") - var resolved_skills_array = DatapackManager.resolve_dependency_source(skills_dep_source) - var loaded_skills_dataset = loaded_pack.get_object_by_id(SKILLS_DATASET_ID) - if resolved_skills_array.size() == 1 and resolved_skills_array.front() == loaded_skills_dataset: - print("✅ SUCCESS: CharacterTemplate data dependency link resolved.") - else: - push_error("TEST FAILED: CharacterTemplate data dependency resolution failed.") - - # 3. Verify FORMULA field - var loaded_crit_field: DataFieldValue = loaded_group.fields.get("critical_damage") - if loaded_crit_field and loaded_crit_field.field_type == DataFieldValue.DataFieldType.FORMULA: - var formula: FormulaValue = loaded_crit_field.value - if formula is FormulaValue and formula.expression == "Base_Damage * Critical_Multiplier": - print("✅ SUCCESS: Found and verified FormulaValue resource.") - else: - push_error("TEST FAILED: FormulaValue verification failed (wrong type or expression).") - else: - push_error("TEST FAILED: Critical damage field not found or wrong type.") - - # 4. Verify LIST field - var loaded_list_field: DataFieldValue = loaded_group.fields.get("on_hit_effects") - if loaded_list_field and loaded_list_field.field_type == DataFieldValue.DataFieldType.LIST: - var list_val: ListValue = loaded_list_field.value - if list_val is ListValue and list_val.items.size() == 2: - if list_val.items[1] is DataFieldValue and list_val.items[1].value == "Stun": - print("✅ SUCCESS: Found and verified ListValue and its nested contents.") - else: - push_error("TEST FAILED: ListValue contents verification failed.") - else: - push_error("TEST FAILED: ListValue resource failed to load correctly.") - else: - push_error("TEST FAILED: On hit effects field not found or wrong type.") - - print("\nVerification: All checks completed.") - -#---------------------------------------------------------------------- -## --- STEP 4: FORMULA RESOLUTION TEST (Omitted for brevity, assumed stable) --- -#---------------------------------------------------------------------- -func _test_formula_resolution(): - print("\n--- 4. Testing Formula Resolver with Variable Substitution ---") - - var resolver := FormulaResolver.new() - var raw_expression := "Base_Damage * Critical_Multiplier + floor(Skill_Level / 2)" - - var context: Dictionary = { - "Base_Damage": 15, - "Critical_Multiplier": 2.5, - "Skill_Level": 7 - } - - var expected_result = 40.5 - var result = resolver.resolve_formula(raw_expression, context) - - if abs(result - expected_result) < 0.001: - print("✅ SUCCESS: FormulaResolver calculated the correct result: %f" % result) - else: - push_error("TEST FAILED: FormulaResolver result incorrect. Expected %f, Got %f" % [expected_result, result]) - - -#---------------------------------------------------------------------- -## --- STEP 5: CROSS-PACK DEPENDENCY RESOLUTION --- -#---------------------------------------------------------------------- -func _test_cross_datapack_dependency(): - print("\n--- 5. Testing Cross-Pack Dependency Resolution ---") - - DatapackManager.clear_loaded_packs() - print("DatapackManager cleared for cross-pack test.") - - # --- 5a. Create and Save DEPENDENCY PACK (Pack A: Stat Core) --- - # FIX: Capture the DatapackModel to get its GUID - var pack_a_model: DatapackModel = _create_dependency_pack_a() - if not pack_a_model: - push_error("TEST FAILED: Failed to create Dependency Pack A.") - return - - var dependency_pack_guid: Guid = pack_a_model.guid # <--- This is the Guid object needed - print("Pack A GUID captured: %s" % dependency_pack_guid.to_string()) - - # --- 5b. Create and Save DEPENDER PACK (Pack B: Class Pack) --- - # FIX: Pass the GUID object to the creator function - var success_b = _create_depender_pack_b(dependency_pack_guid) - if not success_b: - push_error("TEST FAILED: Failed to create Depender Pack B.") - return - - # --- 5c. Load and Register Both Packs --- - var loader = DatapackLoader.new() - var pack_a: DatapackModel = loader.load_datapack(DEPENDENCY_PACK_NAME) - var pack_b: DatapackModel = loader.load_datapack(DEPENDER_PACK_NAME) - - if not pack_a or not pack_b: - push_error("TEST FAILED: Failed to load one or both dependency test packs.") - return - - DatapackManager.register_pack(pack_a) - DatapackManager.register_pack(pack_b) - print("Registered both Dependency (A) and Depender (B) packs.") - - # --- 5d. Resolve Dependency in Pack B --- - var depender_dataset: DatasetModel = pack_b.get_object_by_id(DEPENDER_DATASET_ID) - if not depender_dataset: - push_error("TEST FAILED: Could not find Depender Dataset in Pack B.") - return - - var dependency_source: DependencySource = depender_dataset.get_meta("core_stats_source") - if not dependency_source: - push_error("TEST FAILED: Could not find the DependencySource object in Pack B.") - return - - var resolved_objects: Array = DatapackManager.resolve_dependency_source(dependency_source) - - # --- 5e. Verification --- - if resolved_objects.size() != 1: - push_error("TEST FAILED: Dependency resolution returned incorrect count: %d" % resolved_objects.size()) - return - - var resolved_dataset: DatasetModel = resolved_objects.front() - var expected_dataset: DatasetModel = pack_a.get_object_by_id(DEPENDENCY_DATASET_ID) - - if resolved_dataset == expected_dataset: - print("✅ SUCCESS: Cross-pack dependency successfully resolved.") - if resolved_dataset.entries.has(DEPENDENCY_ENTRY_ID): - print("✅ SUCCESS: Resolved dataset contains the expected entry '%s'." % DEPENDENCY_ENTRY_ID) - else: - push_error("TEST FAILED: Dependency resolved to the wrong object.") - - -# --- Helper Functions for Cross-Pack Test --- - -# UPDATED: Returns the DatapackModel for GUID retrieval -func _create_dependency_pack_a() -> DatapackModel: - # Create entry - var str_field := DataFieldValue.new() - str_field.field_type = DataFieldValue.DataFieldType.NUMBER - str_field.value = 10 - - var strength_entry := DatasetEntry.new() - strength_entry.id = DEPENDENCY_ENTRY_ID - strength_entry.name = "Strength" - strength_entry.top_level_fields["value"] = str_field - - # Create dataset - var core_stats_dataset := DatasetModel.new() - core_stats_dataset.id = DEPENDENCY_DATASET_ID - core_stats_dataset.name = "Core Stats" - core_stats_dataset.sz_type = "StatDefinition" - core_stats_dataset.entries[strength_entry.id] = strength_entry - - # Create datapack - var datapack := DatapackModel.new() - datapack.name = "Stat Core Pack" - datapack.version = "1.0.0" - datapack.sz_objects.append(core_stats_dataset) - - var creator = DatapackCreator.new() - var success = creator.save_datapack(datapack, DEPENDENCY_PACK_NAME) - - if success: - return datapack - else: - return null # Return null on failure - -# UPDATED: Accepts the GUID to correctly create the DatapackDependency -func _create_depender_pack_b(dependency_pack_guid: Guid) -> bool: - # Create DependencySource (internal reference by string ID) - var dependency_source := DependencySource.new() - dependency_source.mode = DependencySource.DependencyMode.SPECIFIC_DATASET - dependency_source.target_sz_object_id = DEPENDENCY_DATASET_ID - - # Create Depender Dataset - var warrior_class_dataset := DatasetModel.new() - warrior_class_dataset.id = DEPENDER_DATASET_ID - warrior_class_dataset.name = "Warrior Class" - warrior_class_dataset.sz_type = "ClassDefinition" - - warrior_class_dataset.set_meta("core_stats_source", dependency_source) - - # Create Datapack - var datapack := DatapackModel.new() - datapack.name = "Warrior Class Pack" - datapack.version = "1.0.0" - datapack.sz_objects.append(warrior_class_dataset) - - # Add the explicit pack dependency - var pack_dep := DatapackDependency.new() - # FIX: Assign the Guid object to the 'id' property - pack_dep.id = dependency_pack_guid - pack_dep.name = "Stat Core Pack" - pack_dep.version = "1.0.0" - datapack.dependencies.append(pack_dep) - - var creator = DatapackCreator.new() - var success = creator.save_datapack(datapack, DEPENDER_PACK_NAME) - return success diff --git a/sessionzero-client/scripts/datapacks/datapack_tests.gd.uid b/sessionzero-client/scripts/datapacks/datapack_tests.gd.uid deleted file mode 100644 index b233f78..0000000 --- a/sessionzero-client/scripts/datapacks/datapack_tests.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b6xurr0segcug diff --git a/sessionzero-client/scripts/datapacks/datapacks.md b/sessionzero-client/scripts/datapacks/datapacks.md new file mode 100644 index 0000000..cdc54e6 --- /dev/null +++ b/sessionzero-client/scripts/datapacks/datapacks.md @@ -0,0 +1,234 @@ +# Proposed Datapack system + +A datapack is a collection of files that are user-generated and are used for loading data into SesssionZero. They store two types of data: Asset files like images, and custom data types called `datasets` and `templates`. There are two main template types: `character_template` and `session_template`. + +## Psuedocode notes +- Not the final syntax or file type, rather a rough outline of the data structure +- All paths are relative to the datapack root +- Icon paths are relative to `datapack_root/assets/images` +- All GUIDs are in the format `00000000-0000-0000-0000-000000000000` to comply with SQL GUID standards +- Quoted strings inside of brackets are assumed to be the `id` of the object (eg. `[entry "longsword"]`, longsword is the id of the entry) +- Ids must be unique within their scope + - All objects under the datapack must have unique ids, entries must have unique ids within their dataset, groups must have unique ids within their entry, and fields must have unique ids within their group, etc + +### Dependencies +- A datapack can have dependencies on other datapacks. These dependencies are defined in the datapack metadata file with an id to access the dependency. +- SessionTemplates can also list datapack dependencies in the same manor as the root datapack for the sake of adding additional content outside of the core datapack that the session_template is loacated. This means there will be cases where a datapack and a session template have the same accessor name for the dependency, meaning the DependenyResolver will most likely fall back to the main datapack for the dependency. + +### Special Types +- The `list` type can be used to reference a list of values from dataset(s) either by referencing dataset(s) by id or by providing a list of allowed dataset types (eg. "item"). +- The `formula` type can be used to calculate a value based on other fields and/or values from datasets or otherwise. (eg. `damage = 10 + (level * 2) + current_weapon.stats.base_damage`) +- The `reference` type can be used to reference another dataset entry by id, and holds the full path to the referenced dataset entry. (The guid of the datapack that the dataset is in, the dataset id, and the entry id). This is used for formualas, etc. + +## Datapack definition +A datapack is defined in a metadata file in the pack's root. It also can hold dependencies to other datapacks. This file contains the following fields in psuedocode: + ```ini +[datapack] +guid = "00000000-0000-0000-0000-000000000000" +name = "My datapack" +description = "My datapack description" +version = "1.0.0" +author = "My name" +creation_date = "2021-01-01" +icon = "icons/my_icon.png" + +# example of a dependency to another datapack, the id given here (in the brackets) is the accessor name for the datapack but does not have to be the same as the actual datapack id (for the case where there are multiple datapacks with the same name) +[datapack_dependency "other_datapack"] +guid = "00000000-0000-0000-0000-000000000000" +version = "1.0.0" +``` + +## Dataset definition +A dataset is a collection of user-defined data (like items, weapons, npcs, etc). Each entry in a dataset has fields that can be optionally grouped into categories. (ex. a `damage` field under the `stats` group on an entry for a `longsword`). Dataset fields have a type, which can be a `text`, `textbox`, `number`, `boolean`, `list`, or `formula` type. The structure in psuedocode looks like this: + +```ini +# dataset definition + +[dataset "core_items"] +description = "Core items dataset" +icon = "icons/core_items.png" +version = "1.0.0" + +# the type of the dataset (can be any lowercase string, this type is arbitrary) +dataset_type = "item" + +[entry "longsword"] +description = "A longsword" +icon = "icons/items/longsword.png" + +[group "stats"] +icon = "icons/stats.png" +description = "Stats group" + +[field "damage"] +type = "number" +description = "Damage number" +value = 10 + +# ... other field types to be defined later ... +``` + +## Templates +Templates are used to define the structure and provide fill-in-the-blank functionality for various types of data that will be instanced. Currently, there are two types of templates: `character_template` and `session_template`. Templates define the structure of the data, and provide a way to fill in the data. All templates will follow a similar structure of `sections` with `fields` and nested `groups` with `fields` inside of them. Fields have no stored value (unlike a dataset), but a default value can be provided. Examples of templates are shown below. + +### Character template definition +A character template is a collection of user-defined fields that are used to create a character (like a character sheet for Dungeons and Dragons). Psuedocode: + +```ini +[character_template "core_character"] +description = "Core character template" +icon = "icons/core_character_template.png" +version = "1.0.0" + +# example of a dataset dependency, the id given here (in the brackets) is the accessor name for the dataset but does not have to be the same as the actual dataset id. +[dataset_dependency "core_items"] +datapack_guid = "00000000-0000-0000-0000-000000000000" +dataset_id = "core_items" +version = "1.0.0" + +[section "character_info"] +description = "Character info section" +icon = "icons/character_info.png" +[field "name"] +type = "text" +description = "Character name" +value = "My character" +[field "description"] +type = "textbox" +description = "Character description" +value = "My character description" + +# --- Only one of the following attributes can be present in a list field --- +# Preset list of values +allowed_values = ["fighter", "rogue", "wizard"] + +# List of datasets that can be used to fill in the list (ids must be listed in the dataset dependency section) +allowed_datasets = ["core_classes", "other_classes"] + +# List of allowed dataset types (will pull entries from all datasets that have the given type from any source of dependency (datapack level, dataset level, session level)) +allowed_dataset_types = ["classes"] +# --- End of list field attributes --- + +[group "attributes"] +description = "Attributes group" +icon = "icons/attributes.png" +[field "level"] +type = "number" +description = "Character level" +value = 1 +[field "class"] +type = "list" +description = "Character class" +value = "fighter" +[field "damage"] +type = "formula" +description = "Damage" +# this formula assumes that the `class` field has been filled with an entry from a dataset that has a `stats` group with a `base_damage` field, and the `current_weapon` field has been filled with an entry from a dataset that has a `stats` group with a `base_damage` field. +# the formula is evaluated using the `strength`, `level`, `current_weapon.stats.base_damage`, and `class.stats.base_damage` fields as variables +# if the formula is invalid the the field will default to 0 OR the default value if provided +formula = "strength + (level * 2) + class.stats.damage + current_weapon.stats.base_damage" + +# optional default value for the field +default_value = 10 +[field "armor_class"] +type = "number" +default_value = 10 +description = "Armor class" +[field "strength"] +type = "number" +default_value = 10 +description = "Strength" + +[section "inventory] +description = "Inventory section" +icon = "icons/inventory.png" +[field "current_weapon"] +type = "list" +description = "Equipped weapon" +allowed_dataset_types = ["weapons"] + +``` + +### Session template definition +A session template is a collection of user-defined fields that are used to create a session (the actual game state). It can hold any data that is needed for the session (such as extra datapack dependencies), and can be used to create a session from a template. A session template also holds a direct reference to a character_template to use for character creation within a session, meaning it will need to be listed as a dependency. A session_template can also hold a list of external datapack dependendencies that will get injected at runtime to allow for extra content. Psuedocode: + +```ini +[session_template "core_session"] +description = "Core session template" +icon = "icons/core_session_template.png" +version = "1.0.0" + +[character_template_dependency "core_character"] +datapack_guid = "00000000-0000-0000-0000-000000000000" +template_id = "core_character" + +[datapack_dependency "other_datapack"] +datapack_guid = "00000000-0000-0000-0000-000000000000" +version = "1.0.0" +``` + +## Possible directory structure +Filenames and types are not final, just an example of the possible directory structure. +```text +- my_datapack + - assets + - images + - icons + - my_icon.png + - ... + - ... + - datasets + - ... + - character_templates + - ... + - session_templates + - ... + - my_datapack.szpack +``` + +## Possible implementations + +### Godot 4.5 Resources +Using Godot 4.5's resource system to store all data, along with the assets, packed into an archive. + +Pros: +- Theoretically Easy to implement in Godot +- Can be made into a binary format (.res) for faster load times and storage size +- Godot resources are deeply integrated with the engine, so it understands them well with built-in loading and saving + +Cons: +- Not a standard format +- Not as easily human-readable +- Not portable to other development environments outside Godot +- Not as extensible for an open-source project like SessionZero + +### JSON format +Using JSON to store all data, along with the assets, packed into an archive. + +Pros: +- Standard format +- Human-readable +- Understood by most systems/languages +- Portable to other development environments + +Cons: +- Not as easy to work with in Godot +- Not as fast to load +- Not as compact +- Could get complex to implement the custom features like dependencies and formulas + +### Custom text format / DSL +Using a custom text format or DSL to store all data, along with the assets, packed into an archive. + +Pros: +- Extremely Human-readable +- Portable to other development environments +- Can be as specific as needed for the complexity of the data +- Can create data just by writing text instead of relying on the SesssionZero godot client application +- Very valuable for an open-source project like SessionZero as it can be well documented and understood by anyone +- Can more easily create datapack generators in other languages/frameworks as its a simple text format/dsl + +Cons: +- More complex to implement (All features need to be implemented like parsing, etc) +- Not a standard format +- Will have to document everything as it is very specific to SessionZero diff --git a/sessionzero-client/scripts/datapacks/models/character_template.gd b/sessionzero-client/scripts/datapacks/models/character_template.gd deleted file mode 100644 index c8e2f2c..0000000 --- a/sessionzero-client/scripts/datapacks/models/character_template.gd +++ /dev/null @@ -1,13 +0,0 @@ -# character_template.gd -class_name CharacterTemplate -extends SzObject -# sz_type will be set to "CharacterTemplate" - -# Core definitions for character sheet fields -@export var top_level_field_definitions: Dictionary = {} # TemplateFieldDefinition -@export var group_definitions: Array[TemplateGroup] = [] - -# --- SPECIALIZED DEPENDENCIES --- -# The Character Template needs to reference *Datasets* (like Weapons, Skills, etc.) -# Map of logical_name (e.g., "Weapon_Source") -> DependencySource (the new class below) -@export var data_dependencies: Dictionary[String, DependencySource] = {} diff --git a/sessionzero-client/scripts/datapacks/models/character_template.gd.uid b/sessionzero-client/scripts/datapacks/models/character_template.gd.uid deleted file mode 100644 index b5c2c20..0000000 --- a/sessionzero-client/scripts/datapacks/models/character_template.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://5k8j3av0kqg4 diff --git a/sessionzero-client/scripts/datapacks/models/datapack_dependency.gd b/sessionzero-client/scripts/datapacks/models/datapack_dependency.gd deleted file mode 100644 index de79c36..0000000 --- a/sessionzero-client/scripts/datapacks/models/datapack_dependency.gd +++ /dev/null @@ -1,6 +0,0 @@ -class_name DatapackDependency -extends Resource - -@export var id: Guid -@export var name: String -@export var version: String diff --git a/sessionzero-client/scripts/datapacks/models/datapack_dependency.gd.uid b/sessionzero-client/scripts/datapacks/models/datapack_dependency.gd.uid deleted file mode 100644 index 446a9f1..0000000 --- a/sessionzero-client/scripts/datapacks/models/datapack_dependency.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://1eht88nmv63j diff --git a/sessionzero-client/scripts/datapacks/models/datapack_model.gd b/sessionzero-client/scripts/datapacks/models/datapack_model.gd deleted file mode 100644 index 677a406..0000000 --- a/sessionzero-client/scripts/datapacks/models/datapack_model.gd +++ /dev/null @@ -1,35 +0,0 @@ -class_name DatapackModel -extends Resource - -@export var guid: Guid -@export var name: String -@export var version: String -@export var author: String -@export var license: String -@export var description: String -@export var icon: String -@export var created_at: DateTime -@export var session_zero_version: String -@export var dependencies: Array[DatapackDependency] = [] -@export var sz_objects: Array[SzObject] = [] - -var content_map: Dictionary = {} - - -func _init() -> void: - if guid == null: - guid = Guid.new_guid() - if created_at == null: - created_at = DateTime.now() - - -func _build_content_map(): - content_map.clear() - for obj in sz_objects: - content_map[obj.id] = obj - - -func get_object_by_id(obj_id: String) -> SzObject: - if content_map.is_empty() and !sz_objects.is_empty(): - _build_content_map() - return content_map.get(obj_id) diff --git a/sessionzero-client/scripts/datapacks/models/datapack_model.gd.uid b/sessionzero-client/scripts/datapacks/models/datapack_model.gd.uid deleted file mode 100644 index 5d60c1b..0000000 --- a/sessionzero-client/scripts/datapacks/models/datapack_model.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dgj5rubcp6v00 diff --git a/sessionzero-client/scripts/datapacks/models/dataset_model.gd b/sessionzero-client/scripts/datapacks/models/dataset_model.gd deleted file mode 100644 index 5b58544..0000000 --- a/sessionzero-client/scripts/datapacks/models/dataset_model.gd +++ /dev/null @@ -1,5 +0,0 @@ -class_name DatasetModel -extends SzObject - -@export var dataset_type: String -@export var entries: Dictionary[String, DatasetEntry] = {} diff --git a/sessionzero-client/scripts/datapacks/models/dataset_model.gd.uid b/sessionzero-client/scripts/datapacks/models/dataset_model.gd.uid deleted file mode 100644 index a0c0b03..0000000 --- a/sessionzero-client/scripts/datapacks/models/dataset_model.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c8nua2p5okuvx diff --git a/sessionzero-client/scripts/datapacks/models/session_template.gd b/sessionzero-client/scripts/datapacks/models/session_template.gd deleted file mode 100644 index 88022e5..0000000 --- a/sessionzero-client/scripts/datapacks/models/session_template.gd +++ /dev/null @@ -1,13 +0,0 @@ -# session_template.gd -class_name SessionTemplate -extends SzObject - -# Core definitions for session fields (e.g., initial currency, setting notes) -@export var top_level_field_definitions: Dictionary = {} -@export var group_definitions: Array[TemplateGroup] = [] - -@export var character_template_ref: DependencySource -@export var data_dependencies: Dictionary[String, DependencySource] = {} - -func _init() -> void: - sz_type = "session_template" diff --git a/sessionzero-client/scripts/datapacks/models/session_template.gd.uid b/sessionzero-client/scripts/datapacks/models/session_template.gd.uid deleted file mode 100644 index 3a221fb..0000000 --- a/sessionzero-client/scripts/datapacks/models/session_template.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://dnjuh1ypyoie8 diff --git a/sessionzero-client/scripts/datapacks/models/szobject.gd b/sessionzero-client/scripts/datapacks/models/szobject.gd deleted file mode 100644 index 735f2c3..0000000 --- a/sessionzero-client/scripts/datapacks/models/szobject.gd +++ /dev/null @@ -1,10 +0,0 @@ -class_name SzObject -extends Resource - -@export var id: String -@export var name: String -@export var sz_type: String -@export var description: String -@export var icon: String -@export var version: String -@export var schema_version: String diff --git a/sessionzero-client/scripts/datapacks/models/szobject.gd.uid b/sessionzero-client/scripts/datapacks/models/szobject.gd.uid deleted file mode 100644 index 8bdd77f..0000000 --- a/sessionzero-client/scripts/datapacks/models/szobject.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c8co7n1xvfmou diff --git a/sessionzero-client/scripts/datapacks/models/template_model.gd b/sessionzero-client/scripts/datapacks/models/template_model.gd deleted file mode 100644 index 7bcff2f..0000000 --- a/sessionzero-client/scripts/datapacks/models/template_model.gd +++ /dev/null @@ -1,7 +0,0 @@ -# template_model.gd -class_name TemplateModel -extends SzObject - -@export var top_level_field_definitions: Dictionary = {} -@export var group_definitions: Array[TemplateGroup] = [] -@export var data_dependencies: Dictionary[String, String] = {} diff --git a/sessionzero-client/scripts/datapacks/models/template_model.gd.uid b/sessionzero-client/scripts/datapacks/models/template_model.gd.uid deleted file mode 100644 index 3387a9c..0000000 --- a/sessionzero-client/scripts/datapacks/models/template_model.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bd60mhmrr4olw diff --git a/sessionzero-client/scripts/datapacks/other/data_field_value.gd b/sessionzero-client/scripts/datapacks/other/data_field_value.gd deleted file mode 100644 index c9edcda..0000000 --- a/sessionzero-client/scripts/datapacks/other/data_field_value.gd +++ /dev/null @@ -1,133 +0,0 @@ -class_name DataFieldValue -extends Resource - -enum DataFieldType { - TEXT, - MULTILINE_TEXT, - NUMBER, - BOOL, - FORMULA, - LIST, - REFERENCE_SINGLE, - REFERENCE_LIST -} - -@export var field_type: DataFieldType -@export var value: Variant - -# --- TYPE STRING MAPPING --- - -static func _type_to_string(t: DataFieldType) -> String: - match t: - DataFieldType.TEXT: return "Text" - DataFieldType.MULTILINE_TEXT: return "MultiText" - DataFieldType.NUMBER: return "Number" - DataFieldType.BOOL: return "Boolean" - DataFieldType.FORMULA: return "Formula" - DataFieldType.LIST: return "List" - DataFieldType.REFERENCE_SINGLE: return "RefSingle" - DataFieldType.REFERENCE_LIST: return "RefList" - _: return str(int(t)) - -static func _string_to_type(s: String) -> DataFieldType: - match s: - "Text": return DataFieldType.TEXT - "MultiText": return DataFieldType.MULTILINE_TEXT - "Number": return DataFieldType.NUMBER - "Boolean": return DataFieldType.BOOL - "Formula": return DataFieldType.FORMULA - "List": return DataFieldType.LIST - "RefSingle": return DataFieldType.REFERENCE_SINGLE - "RefList": return DataFieldType.REFERENCE_LIST - _: return DataFieldType.TEXT - -# --- COMPLEX VALUE ACCESS --- - -func get_concrete_value() -> Variant: - match field_type: - DataFieldType.FORMULA: - return value as FormulaValue - DataFieldType.LIST, DataFieldType.REFERENCE_LIST: - return value as ListValue - _: - return value - -# --- SERIALIZATION HELPERS --- - -static func _envelope_variant(v: Variant) -> Dictionary: - return { - "_kind": "gd-variant-b64", - "data": Marshalls.raw_to_base64(var_to_bytes(v)) - } - -static func _deenvelope_variant(d: Dictionary) -> Variant: - var raw: PackedByteArray = Marshalls.base64_to_raw(d.get("data", "")) - return bytes_to_var(raw) - -static func _serialize_list_value(v: Variant) -> Array: - if v is Array: - var out: Array = [] - for item in v: - if item is Dictionary or item is Array or item is String or item is int or item is float or item is bool or item == null: - out.append(item) - else: - out.append(_envelope_variant(item)) - return out - return [] - -static func _deserialize_list_value(a: Array) -> Array: - var out: Array = [] - for item in a: - if item is Dictionary and item.get("_kind") == "gd-variant-b64": - out.append(_deenvelope_variant(item)) - else: - out.append(item) - return out - -# --- SERIALIZE/DESERIALIZE VALUE --- - -static func serialize_value(p_field_type: DataFieldType, v: Variant) -> Variant: - match p_field_type: - DataFieldType.TEXT, DataFieldType.MULTILINE_TEXT: - return str(v) - DataFieldType.NUMBER: - if v is int or v is float: - return v - var s := str(v) - if s.find(".") >= 0: - return float(s) - else: - return int(s) - DataFieldType.BOOL: - return bool(v) - _: - if v is Dictionary or v is Array or v is String or v is int or v is float or v is bool or v == null: - return v - return _envelope_variant(v) - -static func deserialize_value(p_field_type: DataFieldType, raw: Variant) -> Variant: - match p_field_type: - DataFieldType.TEXT, DataFieldType.MULTILINE_TEXT: - return str(raw) - DataFieldType.NUMBER: - return raw if (raw is float or raw is int) else float(str(raw)) - DataFieldType.BOOL: - return bool(raw) - _: - if raw is Dictionary and raw.get("_kind") == "gd-variant-b64": - return _deenvelope_variant(raw) - return raw - -# --- DICT CONVERSION --- - -func to_dict() -> Dictionary: - return { - "field_type": _type_to_string(field_type), - "value": serialize_value(field_type, value) - } - -static func from_dict(d: Dictionary) -> DataFieldValue: - var inst := DataFieldValue.new() - inst.field_type = _string_to_type(d.get("field_type", "Text")) - inst.value = deserialize_value(inst.field_type, d.get("value")) - return inst diff --git a/sessionzero-client/scripts/datapacks/other/data_field_value.gd.uid b/sessionzero-client/scripts/datapacks/other/data_field_value.gd.uid deleted file mode 100644 index 387941e..0000000 --- a/sessionzero-client/scripts/datapacks/other/data_field_value.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cfl5yreaugqde diff --git a/sessionzero-client/scripts/datapacks/other/dataset_entry.gd b/sessionzero-client/scripts/datapacks/other/dataset_entry.gd deleted file mode 100644 index 2887a61..0000000 --- a/sessionzero-client/scripts/datapacks/other/dataset_entry.gd +++ /dev/null @@ -1,9 +0,0 @@ -class_name DatasetEntry -extends Resource - -@export var id: String -@export var name: String -@export var description: String -@export var icon: String -@export var top_level_fields: Dictionary[String, DataFieldValue] -@export var groups: Array[DatasetGroup] diff --git a/sessionzero-client/scripts/datapacks/other/dataset_entry.gd.uid b/sessionzero-client/scripts/datapacks/other/dataset_entry.gd.uid deleted file mode 100644 index 37ef8a3..0000000 --- a/sessionzero-client/scripts/datapacks/other/dataset_entry.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://c266t0ugcrkr diff --git a/sessionzero-client/scripts/datapacks/other/dataset_group.gd b/sessionzero-client/scripts/datapacks/other/dataset_group.gd deleted file mode 100644 index 6a00706..0000000 --- a/sessionzero-client/scripts/datapacks/other/dataset_group.gd +++ /dev/null @@ -1,6 +0,0 @@ -class_name DatasetGroup -extends Resource - -@export var id: String -@export var name: String -@export var fields: Dictionary[String, DataFieldValue] diff --git a/sessionzero-client/scripts/datapacks/other/dataset_group.gd.uid b/sessionzero-client/scripts/datapacks/other/dataset_group.gd.uid deleted file mode 100644 index e6d9280..0000000 --- a/sessionzero-client/scripts/datapacks/other/dataset_group.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cyr6ocjkgd6vm diff --git a/sessionzero-client/scripts/datapacks/other/dependency_source.gd b/sessionzero-client/scripts/datapacks/other/dependency_source.gd deleted file mode 100644 index d3694d4..0000000 --- a/sessionzero-client/scripts/datapacks/other/dependency_source.gd +++ /dev/null @@ -1,20 +0,0 @@ -# dependency_source.gd -class_name DependencySource -extends Resource - -enum DependencyMode { - SPECIFIC_DATASET, # Reference a known Dataset/Template by its ID and GUID - DATASET_TYPE_WILDCARD, # Reference ANY Dataset/Template that matches a specific type string -} - -@export var mode: DependencyMode = DependencyMode.SPECIFIC_DATASET -@export var display_name: String = "" - -# --- Used for SPECIFIC_DATASET mode --- -# The GUID of the Datapack the dependency lives in (optional, can be empty for local pack) -@export var target_datapack_guid: String = "" -@export var target_sz_object_id: String = "" - -# --- Used for DATASET_TYPE_WILDCARD mode --- -# The type string to match (e.g., "items"). -@export var target_type_string: String = "" diff --git a/sessionzero-client/scripts/datapacks/other/dependency_source.gd.uid b/sessionzero-client/scripts/datapacks/other/dependency_source.gd.uid deleted file mode 100644 index e2bef7c..0000000 --- a/sessionzero-client/scripts/datapacks/other/dependency_source.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cly4hb2wrx8fn diff --git a/sessionzero-client/scripts/datapacks/other/formula_resolver.gd b/sessionzero-client/scripts/datapacks/other/formula_resolver.gd deleted file mode 100644 index 001b890..0000000 --- a/sessionzero-client/scripts/datapacks/other/formula_resolver.gd +++ /dev/null @@ -1,52 +0,0 @@ -# formula_resolver.gd -class_name FormulaResolver -extends RefCounted - -var _expression: Expression = Expression.new() - -## Internal helper to replace named variables with their numerical values. -## NOTE: This simple approach sorts keys by length descending (Max_HP before HP) -## to prevent incorrect substitution of substrings. -func _substitute_variables(raw_expression: String, context: Dictionary) -> String: - var result = raw_expression - - # Sort keys by length descending to prevent shorter names (like 'HP') from matching - # before longer names that contain them (like 'Max_HP'). - var keys = context.keys() - keys.sort_custom(func(a, b): return len(a) > len(b)) - - for key in keys: - if raw_expression.find(key) != -1: - var value = str(context[key]) - # Replace the variable name with its numerical value string - result = result.replace(key, value) - - return result - -## Resolves the final value of the formula given a set of variable values. -## Handles variable substitution, parsing, and execution in one step. -func resolve_formula(raw_expression: String, context: Dictionary) -> float: - # 1. Substitute variables with their numerical values - var numerical_expression = _substitute_variables(raw_expression, context) - - # 2. Parse the new numerical expression - # The expression must be re-parsed every time, as its text content changes. - var error: Error = _expression.parse(numerical_expression, PackedStringArray([])) - - if error != OK: - push_error("FormulaResolver: Substitution/Parse failed. Expression: '%s'. Numerical: '%s'. Error: %s" % [raw_expression, numerical_expression, _expression.get_error_text()]) - return 0.0 - - # 3. Execute the expression (inputs: empty array, base_instance: null, show_error: true) - var result = _expression.execute([], null, true) - - if _expression.has_execute_failed(): - push_error("FormulaResolver: Execution failed for numerical expression: %s" % numerical_expression) - return 0.0 - - # 4. Check type and return - if typeof(result) != TYPE_FLOAT and typeof(result) != TYPE_INT: - push_error("FormulaResolver: Expression result was not a number. Got type: %s" % typeof(result)) - return 0.0 - - return float(result) diff --git a/sessionzero-client/scripts/datapacks/other/formula_resolver.gd.uid b/sessionzero-client/scripts/datapacks/other/formula_resolver.gd.uid deleted file mode 100644 index 32b0007..0000000 --- a/sessionzero-client/scripts/datapacks/other/formula_resolver.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://ils0nw5fyh7j diff --git a/sessionzero-client/scripts/datapacks/other/formula_value.gd b/sessionzero-client/scripts/datapacks/other/formula_value.gd deleted file mode 100644 index 0aad5f3..0000000 --- a/sessionzero-client/scripts/datapacks/other/formula_value.gd +++ /dev/null @@ -1,10 +0,0 @@ -# formula_value.gd -class_name FormulaValue -extends Resource - -# The raw expression string (e.g., "Attack_Modifier + Weapon_Damage_Dice") -@export var expression: String = "" -@export var variables: Dictionary = {} - -# need a separate system (a FormulaEvaluator) later to parse and execute this. -# For now, this resource simply stores the definition. diff --git a/sessionzero-client/scripts/datapacks/other/formula_value.gd.uid b/sessionzero-client/scripts/datapacks/other/formula_value.gd.uid deleted file mode 100644 index b7d0c73..0000000 --- a/sessionzero-client/scripts/datapacks/other/formula_value.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cyij11ojm1ecy diff --git a/sessionzero-client/scripts/datapacks/other/list_value.gd b/sessionzero-client/scripts/datapacks/other/list_value.gd deleted file mode 100644 index 60210bd..0000000 --- a/sessionzero-client/scripts/datapacks/other/list_value.gd +++ /dev/null @@ -1,5 +0,0 @@ -# list_value.gd -class_name ListValue -extends Resource - -@export var items: Array[DataFieldValue] = [] diff --git a/sessionzero-client/scripts/datapacks/other/list_value.gd.uid b/sessionzero-client/scripts/datapacks/other/list_value.gd.uid deleted file mode 100644 index 1cdd1bf..0000000 --- a/sessionzero-client/scripts/datapacks/other/list_value.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cpdx3w11rbt5j diff --git a/sessionzero-client/scripts/datapacks/other/template_field.gd b/sessionzero-client/scripts/datapacks/other/template_field.gd deleted file mode 100644 index efc1d6d..0000000 --- a/sessionzero-client/scripts/datapacks/other/template_field.gd +++ /dev/null @@ -1,22 +0,0 @@ -# template_field_definition.gd -class_name TemplateField -extends Resource - -enum DataFieldType { - TEXT, - MULTILINE_TEXT, - NUMBER, - BOOL, - FORMULA, - LIST, - REFERENCE_SINGLE, - REFERENCE_LIST -} - -@export var field_id: String -@export var display_name: String -@export var field_type: DataFieldType = DataFieldType.TEXT -@export var default_value: Variant - -@export var dependency_source_name: String = "" -@export var dependency_source_key: String = "" diff --git a/sessionzero-client/scripts/datapacks/other/template_field.gd.uid b/sessionzero-client/scripts/datapacks/other/template_field.gd.uid deleted file mode 100644 index 07b0579..0000000 --- a/sessionzero-client/scripts/datapacks/other/template_field.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b4d6mtffupbk3 diff --git a/sessionzero-client/scripts/datapacks/other/template_group.gd b/sessionzero-client/scripts/datapacks/other/template_group.gd deleted file mode 100644 index 99b0f3c..0000000 --- a/sessionzero-client/scripts/datapacks/other/template_group.gd +++ /dev/null @@ -1,8 +0,0 @@ -# template_group.gd -class_name TemplateGroup -extends Resource - -@export var name: String -@export var group_id: String -@export var field_definitions: Dictionary = {} -@export var is_list: bool = false diff --git a/sessionzero-client/scripts/datapacks/other/template_group.gd.uid b/sessionzero-client/scripts/datapacks/other/template_group.gd.uid deleted file mode 100644 index dbbe638..0000000 --- a/sessionzero-client/scripts/datapacks/other/template_group.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://h3ptq7tj3tad diff --git a/sessionzero-client/scripts/helpers/datapack_creator.gd b/sessionzero-client/scripts/helpers/datapack_creator.gd deleted file mode 100644 index f9b40cd..0000000 --- a/sessionzero-client/scripts/helpers/datapack_creator.gd +++ /dev/null @@ -1,70 +0,0 @@ -# datapack_creator.gd -class_name DatapackCreator -extends RefCounted - -const BASE_DATAPACKS_PATH = "user://datapacks/" -const METADATA_FILENAME = "config.szpack" -const DATAPACK_RESOURCE_FILENAME = "datapack.res" -const ASSETS_SUBDIR = "assets" - -const SINGLE_FILE_SAVE_FLAGS = ResourceSaver.FLAG_COMPRESS | ResourceSaver.FLAG_CHANGE_PATH - -var _pack_base_path: String = "" - - -func save_datapack(datapack: DatapackModel, pack_name: String) -> bool: - var full_pack_name = pack_name.validate_node_name() - if full_pack_name.is_empty(): - push_error("DatapackCreator: Invalid pack_name provided.") - return false - - _pack_base_path = BASE_DATAPACKS_PATH + full_pack_name + "/" - - var assets_path = _pack_base_path + ASSETS_SUBDIR - - var success_dirs = true - success_dirs = success_dirs and _make_dir_recursive_safe(_pack_base_path) - success_dirs = success_dirs and _make_dir_recursive_safe(assets_path) - - if not success_dirs: - push_error("DatapackCreator: Failed to create necessary directories.") - return false - - var main_resource_path = _pack_base_path + DATAPACK_RESOURCE_FILENAME - - var error = ResourceSaver.save(datapack, main_resource_path, SINGLE_FILE_SAVE_FLAGS) - - if error != OK: - push_error("DatapackCreator: Failed to save datapack resource at path '%s'. Error: %s" % [main_resource_path, error]) - return false - - if not _write_metadata_file(datapack, _pack_base_path + METADATA_FILENAME): - return false - - print("Datapack saved successfully to: %s" % _pack_base_path) - return true - - -func _make_dir_recursive_safe(path: String) -> bool: - var error = DirAccess.make_dir_recursive_absolute(path) - if error != OK and error != ERR_ALREADY_EXISTS: - push_error("DirAccess: Failed to create directory: %s (Error: %s)" % [path, error]) - return false - return true - - -func _write_metadata_file(datapack: DatapackModel, path: String) -> bool: - var config = ConfigFile.new() - var section = "Datapack" - - config.set_value(section, "guid", datapack.guid.to_string()) - config.set_value(section, "name", datapack.name) - config.set_value(section, "version", datapack.version) - config.set_value(section, "entry_point", DATAPACK_RESOURCE_FILENAME) - - var error = config.save(path) - if error != OK: - push_error("DatapackCreator: Failed to save metadata file at path '%s'. Error: %s" % [path, error]) - return false - - return true diff --git a/sessionzero-client/scripts/helpers/datapack_creator.gd.uid b/sessionzero-client/scripts/helpers/datapack_creator.gd.uid deleted file mode 100644 index 8f0cf69..0000000 --- a/sessionzero-client/scripts/helpers/datapack_creator.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cu8n3sgejs6it diff --git a/sessionzero-client/scripts/helpers/datapack_loader.gd b/sessionzero-client/scripts/helpers/datapack_loader.gd deleted file mode 100644 index fcf5397..0000000 --- a/sessionzero-client/scripts/helpers/datapack_loader.gd +++ /dev/null @@ -1,46 +0,0 @@ -# datapack_loader.gd -class_name DatapackLoader -extends RefCounted - -const BASE_DATAPACKS_PATH = "user://datapacks/" -const METADATA_FILENAME = "config.szpack" -# CHANGE HERE -const DATAPACK_RESOURCE_FILENAME = "datapack.res" - -## Loads a DatapackModel resource and all its referenced content from the user://datapacks/ folder. -## Returns the loaded DatapackModel object or null on failure. -func load_datapack(pack_name: String) -> DatapackModel: - var full_pack_name = pack_name.validate_node_name() - if full_pack_name.is_empty(): - push_error("DatapackLoader: Invalid pack_name provided.") - return null - - var pack_dir = BASE_DATAPACKS_PATH + full_pack_name + "/" - var metadata_path = pack_dir + METADATA_FILENAME - - var config = ConfigFile.new() - var error = config.load(metadata_path) - - if error != OK: - push_error("DatapackLoader: Could not load external manifest at: %s (Error: %s)" % [metadata_path, error]) - return null - - var entry_point = config.get_value("Datapack", "entry_point", DATAPACK_RESOURCE_FILENAME) - - var resource_path = pack_dir + entry_point - - if not ResourceLoader.exists(resource_path): - push_error("DatapackLoader: Main Resource file not found at path: %s" % resource_path) - return null - - # Godot automatically detects the binary format and loads the resource. - var datapack: DatapackModel = ResourceLoader.load(resource_path) - - if not datapack: - push_error("DatapackLoader: Failed to load resource object from path: %s" % resource_path) - return null - - datapack._build_content_map() - - print("Successfully loaded Datapack: %s (GUID: %s)" % [datapack.name, datapack.guid.to_string()]) - return datapack diff --git a/sessionzero-client/scripts/helpers/datapack_loader.gd.uid b/sessionzero-client/scripts/helpers/datapack_loader.gd.uid deleted file mode 100644 index b01a09a..0000000 --- a/sessionzero-client/scripts/helpers/datapack_loader.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://bwuvsyn1y3dge diff --git a/sessionzero-client/scripts/helpers/datetime.gd b/sessionzero-client/scripts/helpers/datetime.gd deleted file mode 100644 index e03dd23..0000000 --- a/sessionzero-client/scripts/helpers/datetime.gd +++ /dev/null @@ -1,42 +0,0 @@ -# datetime.gd -class_name DateTime -extends Resource - -@export var _unix_time: float = 0.0 - - -static func now() -> DateTime: - var dt = DateTime.new() - dt._unix_time = Time.get_unix_time_from_system() - return dt - - -static func from_unix(unix_seconds: float) -> DateTime: - var dt = DateTime.new() - dt._unix_time = unix_seconds - return dt - - -static func from_string(input: String) -> DateTime: - var s := input.strip_edges() - if s.ends_with("Z"): - s = s.substr(0, s.length() - 1) - var unix := Time.get_unix_time_from_datetime_string(s) - if s.find("-") == -1 or s.find(":") == -1: - push_error("Invalid datetime string format: %s" % input) - return null - var dt = DateTime.new() - dt._unix_time = unix - return dt - - -func to_unix() -> float: - return _unix_time - - -func _to_string() -> String: - var dict := Time.get_datetime_dict_from_unix_time(_unix_time) - return "%04d-%02d-%02dT%02d:%02d:%02dZ" % [ - dict.year, dict.month, dict.day, - dict.hour, dict.minute, dict.second - ] diff --git a/sessionzero-client/scripts/helpers/datetime.gd.uid b/sessionzero-client/scripts/helpers/datetime.gd.uid deleted file mode 100644 index fec6961..0000000 --- a/sessionzero-client/scripts/helpers/datetime.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://b2k68if5pcfyn diff --git a/sessionzero-client/scripts/helpers/guid.gd b/sessionzero-client/scripts/helpers/guid.gd deleted file mode 100644 index fb022a8..0000000 --- a/sessionzero-client/scripts/helpers/guid.gd +++ /dev/null @@ -1,49 +0,0 @@ -class_name Guid -extends Resource - -@export var bytes: PackedByteArray - - -static func new_guid() -> Guid: - var guid = Guid.new() - guid.bytes = _generate_random_bytes(16) - # Set UUID version (4) and variant bits - guid.bytes[6] = (guid.bytes[6] & 0x0F) | 0x40 - guid.bytes[8] = (guid.bytes[8] & 0x3F) | 0x80 - return guid - - -static func from_string(input: String) -> Guid: - var clean = input.replace("-", "") - if clean.length() != 32: - push_error("Invalid GUID string format") - return null - var guid = Guid.new() - guid.bytes = PackedByteArray() - for i in range(0, 32, 2): - guid.bytes.append(clean.substr(i, 2).hex_to_int()) - return guid - - -func _to_string() -> String: - var hex = "" - for b in bytes: - hex += "%02x" % b - return ( - hex.substr(0,8) + "-" + - hex.substr(8,4) + "-" + - hex.substr(12,4) + "-" + - hex.substr(16,4) + "-" + - hex.substr(20,12) - ) - - -func to_base64() -> String: - return Marshalls.raw_to_base64(bytes) - - -static func _generate_random_bytes(length: int) -> PackedByteArray: - var arr = PackedByteArray() - for i in range(length): - arr.append(randi() & 0xFF) - return arr diff --git a/sessionzero-client/scripts/helpers/guid.gd.uid b/sessionzero-client/scripts/helpers/guid.gd.uid deleted file mode 100644 index b5059ff..0000000 --- a/sessionzero-client/scripts/helpers/guid.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://xiy5j06o8254