Skip to content

Commit

Permalink
Merge pull request #527 from snipercup/rskills
Browse files Browse the repository at this point in the history
Enable modding of skills
  • Loading branch information
snipercup authored Dec 5, 2024
2 parents a1cdf0b + f4d05ce commit 191ff9a
Show file tree
Hide file tree
Showing 19 changed files with 194 additions and 116 deletions.
54 changes: 0 additions & 54 deletions Mods/Core/Skills/Skills.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,6 @@
"description": "Skill in crafting objects from raw materials, including tools, weapons, and other gear, often essential for survival.",
"id": "fabrication",
"name": "Fabrication",
"references": {
"core": {
"items": [
"pistol_magazine",
"cutting_board",
"rolling_pin",
"stone_spear"
]
}
},
"sprite": "crafting_32.png"
},
{
Expand All @@ -79,13 +69,6 @@
"description": "Expertise in handling and repairing electronic devices and components, from simple circuits to complex systems.",
"id": "electronics",
"name": "Electronics",
"references": {
"core": {
"items": [
"transmitter"
]
}
},
"sprite": "electronics_32.png"
},
{
Expand All @@ -110,26 +93,12 @@
"description": "Proficiency in handling and maintaining pistols, enhancing accuracy and reload speed, especially in close combat.",
"id": "handguns",
"name": "Handguns",
"references": {
"core": {
"items": [
"pistol_9mm"
]
}
},
"sprite": "handgun_32.png"
},
{
"description": "Expertise in using rifles, improving long-range accuracy and maintenance of such firearms.",
"id": "rifle",
"name": "Rifle",
"references": {
"core": {
"items": [
"rifle_m4a1"
]
}
},
"sprite": "rifle_32.png"
},
{
Expand All @@ -142,13 +111,6 @@
"description": "Proficiency with submachine guns, focusing on rapid fire and handling, ideal for close to mid-range combat.",
"id": "smg",
"name": "Smg",
"references": {
"core": {
"items": [
"chugunov_mpap"
]
}
},
"sprite": "smg_32.png"
},
{
Expand All @@ -167,28 +129,12 @@
"description": "Skill in using blunt weapons to inflict damage, increasing force and effectiveness in close combat.",
"id": "bashing",
"name": "Bashing",
"references": {
"core": {
"items": [
"stick",
"long_stick"
]
}
},
"sprite": "bashing_32.png"
},
{
"description": "Skill in using sharp weapons like knives and swords, enhancing precision and damage during melee combat.",
"id": "cutting",
"name": "Cutting",
"references": {
"core": {
"items": [
"machete",
"stone_spear"
]
}
},
"sprite": "stabbing_32.png"
},
{
Expand Down
10 changes: 5 additions & 5 deletions Scenes/ContentManager/Custom_Editors/Scripts/SkillsEditor.gd
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ signal data_changed()
var olddata: DSkill # Remember what the value of the data was before editing

# The data that represents this skill
# The data is selected from the Gamedata.skills
# The data is selected from the dskill.parent
# based on the ID that the user has selected in the content editor
var dskill: DSkill = null:
set(value):
dskill = value
load_skill_data()
skillSelector.sprites_collection = Gamedata.skills.sprites
olddata = DSkill.new(dskill.get_data().duplicate(true))
skillSelector.sprites_collection = dskill.parent.sprites
olddata = DSkill.new(dskill.get_data().duplicate(true), null)

# This function updates the form based on the DSkill that has been loaded
func load_skill_data() -> void:
Expand All @@ -46,7 +46,7 @@ func _on_close_button_button_up() -> void:
queue_free()

# This function takes all data from the form elements and stores them in the DSkill instance
# Since dskill is a reference to an item in Gamedata.skills
# Since dskill is a reference to an item in dskill.parent
# the central array for skill data is updated with the changes as well
# The function will signal to Gamedata that the data has changed and needs to be saved
func _on_save_button_button_up() -> void:
Expand All @@ -56,7 +56,7 @@ func _on_save_button_button_up() -> void:
dskill.sprite = skillImageDisplay.texture
dskill.save_to_disk()
data_changed.emit()
olddata = DSkill.new(dskill.get_data().duplicate(true))
olddata = DSkill.new(dskill.get_data().duplicate(true), null)

# When the skillImageDisplay is clicked, the user will be prompted to select an image from
# "res://Mods/Core/Skills/". The texture of the skillImageDisplay will change to the selected image
Expand Down
2 changes: 1 addition & 1 deletion Scenes/ContentManager/Scripts/content_list.gd
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ var mod_id: String = "Core"
var contentType: DMod.ContentType:
set(newData):
contentType = newData
if newData == DMod.ContentType.STATS or newData == DMod.ContentType.TILES or newData == DMod.ContentType.TACTICALMAPS or newData == DMod.ContentType.MAPS:
if newData == DMod.ContentType.STATS or newData == DMod.ContentType.SKILLS or newData == DMod.ContentType.TILES or newData == DMod.ContentType.TACTICALMAPS or newData == DMod.ContentType.MAPS:
# Use mod-specific data for these content types
datainstance = Gamedata.mods.by_id(mod_id).get_data_of_type(contentType)
else:
Expand Down
2 changes: 1 addition & 1 deletion Scenes/ContentManager/Scripts/contenteditor.gd
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ func instantiate_editor(type: DMod.ContentType, itemID: String, newEditor: Packe
newContentEditor.data_changed.connect(list.load_data)

DMod.ContentType.SKILLS:
newContentEditor.dskill = Gamedata.skills.by_id(itemID)
newContentEditor.dskill = currentmod.skills.by_id(itemID)
newContentEditor.data_changed.connect(list.load_data)

DMod.ContentType.QUESTS:
Expand Down
2 changes: 1 addition & 1 deletion Scenes/ContentManager/contenteditor.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
[ext_resource type="PackedScene" uid="uid://b07i30w3ey3aa" path="res://Scenes/ContentManager/Custom_Editors/PlayerAttributeEditor.tscn" id="14_uu117"]
[ext_resource type="PackedScene" uid="uid://b3ggaal1e2obk" path="res://Scenes/ContentManager/Custom_Editors/OvermapAreaEditor.tscn" id="15_qbq5b"]
[ext_resource type="PackedScene" uid="uid://bw14nfwdnu41d" path="res://Scenes/ContentManager/Custom_Editors/MobgroupsEditor.tscn" id="16_lrbdj"]
[ext_resource type="PackedScene" uid="uid://c6qjrrt1xgro2" path="res://Scenes/ContentManager/Custom_Editors/MobfactionsEditor.tscn" id="17_mcu7c"]
[ext_resource type="PackedScene" path="res://Scenes/ContentManager/Custom_Editors/MobfactionsEditor.tscn" id="17_mcu7c"]

[node name="contenteditor" type="Control" node_paths=PackedStringArray("select_mods", "content", "tabContainer", "type_selector_menu_button")]
layout_mode = 3
Expand Down
10 changes: 5 additions & 5 deletions Scripts/CharacterWindow.gd
Original file line number Diff line number Diff line change
Expand Up @@ -38,25 +38,25 @@ func _on_player_skill_changed(player_node: CharacterBody3D):
return
clear_container(skillsContainer) # Clear existing content
for skill_id in player_node.skills:
var skill_data: DSkill = Gamedata.skills.by_id(skill_id)
var skill_data: RSkill = Runtimedata.skills.by_id(skill_id)
if skill_data:
var skill_value = player_node.skills[skill_id]
var skill_entry = create_skill_entry(skill_data, skill_value)
skillsContainer.add_child(skill_entry)


# Utility function to create an HBoxContainer for a stat or skill entry
func create_skill_entry(dskill: DSkill, value: Variant) -> HBoxContainer:
func create_skill_entry(rskill: RSkill, value: Variant) -> HBoxContainer:
var hbox = HBoxContainer.new()
var icon = TextureRect.new()
icon.texture = dskill.sprite
icon.texture = rskill.sprite
hbox.add_child(icon)

var label = Label.new()
# For skills, display level and XP with a maximum of 2 decimal places
var xp_value = str(round(value["xp"] * 100) / 100.0) # Round XP to 2 decimal places
label.text = dskill.name + ": Level " + str(value["level"]) + ", XP: " + xp_value
label.tooltip_text = dskill.description
label.text = rskill.name + ": Level " + str(value["level"]) + ", XP: " + xp_value
label.tooltip_text = rskill.description
hbox.add_child(label)

return hbox
Expand Down
6 changes: 3 additions & 3 deletions Scripts/Gamedata/DItem.gd
Original file line number Diff line number Diff line change
Expand Up @@ -619,11 +619,11 @@ func update_item_skill_references(olddata: DItem):
# Remove old skill references that are not in the new list
for old_skill_id in old_skill_ids:
if not new_skill_ids.has(old_skill_id):
Gamedata.skills.remove_reference(old_skill_id, "core", "items", id)
Gamedata.mods.remove_reference(DMod.ContentType.SKILLS, old_skill_id, DMod.ContentType.ITEMS, id)

# Add new skill references
for new_skill_id in new_skill_ids:
Gamedata.skills.add_reference(new_skill_id, "core", "items", id)
Gamedata.mods.add_reference(DMod.ContentType.SKILLS, new_skill_id, DMod.ContentType.ITEMS, id)


# Collects all attributes defined in an item and updates the references to that attribute
Expand Down Expand Up @@ -717,7 +717,7 @@ func delete():

# Remove the reference of this item from each skill
for skill_id in skill_ids.keys():
Gamedata.skills.remove_reference(skill_id, "core", "items", id)
Gamedata.mods.remove_reference(DMod.ContentType.SKILLS, skill_id, DMod.ContentType.ITEMS, id)

# Save changes to the data file if any changes were made
if changes_made["value"]:
Expand Down
2 changes: 1 addition & 1 deletion Scripts/Gamedata/DMod.gd
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ func _init(modinfo: Dictionary, myparent: DMods):
itemgroups = DItemgroups.new()
playerattributes = DPlayerAttributes.new()
wearableslots = DWearableSlots.new()
skills = DSkills.new()
skills = DSkills.new(id)
stats = DStats.new(id) # Pass the mod_id for stats initialization
quests = DQuests.new()
overmapareas = DOvermapareas.new(id)
Expand Down
39 changes: 16 additions & 23 deletions Scripts/Gamedata/DSkill.gd
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@ var name: String
var description: String
var spriteid: String
var sprite: Texture
var references: Dictionary = {}
var parent: DSkills

# Constructor to initialize skill properties from a dictionary
func _init(data: Dictionary):
# myparent: The list containing all skills for this mod
func _init(data: Dictionary, myparent: DSkills):
parent = myparent
id = data.get("id", "")
name = data.get("name", "")
description = data.get("description", "")
spriteid = data.get("sprite", "")
references = data.get("references", {})


# Get data function to return a dictionary with all properties
Expand All @@ -43,30 +44,16 @@ func get_data() -> Dictionary:
"description": description,
"sprite": spriteid
}
if not references.is_empty():
data["references"] = references
return data

# Method to save any changes to the skill back to disk
func save_to_disk():
Gamedata.skills.save_skills_to_disk()

# Removes the provided reference from references
func remove_reference(module: String, type: String, refid: String):
var changes_made = Gamedata.dremove_reference(references, module, type, refid)
if changes_made:
Gamedata.skills.save_skills_to_disk()

# Adds a reference to the references list
func add_reference(module: String, type: String, refid: String):
var changes_made = Gamedata.dadd_reference(references, module, type, refid)
if changes_made:
Gamedata.skills.save_skills_to_disk()
parent.save_skills_to_disk()

# Some skill has been changed
# INFO if the skill references other entities, update them here
func changed(_olddata: DSkill):
Gamedata.skills.save_skills_to_disk()
parent.save_skills_to_disk()

# A skill is being deleted from the data
# We have to remove it from everything that references it
Expand All @@ -81,7 +68,7 @@ func delete():

# Pass the callable to every item in the skill's references
# It will call myfunc on every item in skill_data.references.core.items
execute_callable_on_references_of_type("core", "items", myfunc)
execute_callable_on_references_of_type(DMod.ContentType.ITEMS, myfunc)

# Save changes to the data file if any changes were made
if changes.made:
Expand All @@ -91,9 +78,15 @@ func delete():


# Executes a callable function on each reference of the given type
func execute_callable_on_references_of_type(module: String, type: String, callable: Callable):
# type: The type of entity that you want to execute the callable for
# callable: The function that will be executed for every entity of this type
func execute_callable_on_references_of_type(type: DMod.ContentType, callable: Callable):
# myreferences will ba dictionary that contains entity types that have references to this skill's id
# See DMod.add_reference for an example structure of references
var myreferences: Dictionary = parent.references.get(id, {})
var type_string: String = DMod.get_content_type_string(type)
# Check if it contains the specified 'module' and 'type'
if references.has(module) and references[module].has(type):
if myreferences.has(type_string):
# If the type exists, execute the callable on each ID found under this type
for ref_id in references[module][type]:
for ref_id in myreferences[type_string]:
callable.call(ref_id)
39 changes: 30 additions & 9 deletions Scripts/Gamedata/DSkills.gd
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,48 @@ extends RefCounted

# There's a D in front of the class name to indicate this class only handles skills data, nothing more
# This script is intended to be used inside the GameData autoload singleton
# This script handles the list of skills. You can access it through Gamedata.skills
# This script handles the list of skills. You can access it through Gamedata.mods.by_id("Core").skills

# Paths for skills data and sprites
var dataPath: String = "./Mods/Core/Skills/Skills.json"
var dataPath: String = "./Mods/Core/Skills/"
var filePath: String = "./Mods/Core/Skills/Skills.json"
var spritePath: String = "./Mods/Core/Skills/"
var skilldict: Dictionary = {}
var sprites: Dictionary = {}
var references: Dictionary = {}

# Constructor
func _init():

# Add a mod_id parameter to dynamically initialize paths
func _init(mod_id: String) -> void:
# Update dataPath and spritePath using the provided mod_id
dataPath = "./Mods/" + mod_id + "/Skills/"
filePath = "./Mods/" + mod_id + "/Skills/Skills.json"
spritePath = "./Mods/" + mod_id + "/Skills/"

# Load stats and sprites
load_sprites()
load_skills_from_disk()
load_references()


# Load all skills data from disk into memory
func load_skills_from_disk() -> void:
var skillslist: Array = Helper.json_helper.load_json_array_file(dataPath)
var skillslist: Array = Helper.json_helper.load_json_array_file(filePath)
for myskill in skillslist:
var skill: DSkill = DSkill.new(myskill)
var skill: DSkill = DSkill.new(myskill, self)
skill.sprite = sprites[skill.spriteid]
skilldict[skill.id] = skill


# Load references from references.json
func load_references() -> void:
var path = dataPath + "references.json"
if FileAccess.file_exists(path):
references = Helper.json_helper.load_json_dictionary_file(path)
else:
references = {} # Initialize an empty references dictionary if the file doesn't exist


# Loads sprites and assigns them to the proper dictionary
func load_sprites() -> void:
var png_files: Array = Helper.json_helper.file_names_in_dir(spritePath, ["png"])
Expand All @@ -42,7 +63,7 @@ func save_skills_to_disk() -> void:
var save_data: Array = []
for skill in skilldict.values():
save_data.append(skill.get_data())
Helper.json_helper.write_json_file(dataPath, JSON.stringify(save_data, "\t"))
Helper.json_helper.write_json_file(filePath, JSON.stringify(save_data, "\t"))

# Returns the dictionary containing all skills
func get_all() -> Dictionary:
Expand All @@ -55,13 +76,13 @@ func duplicate_to_disk(skillid: String, newskillid: String) -> void:
# So we delete the references from the duplicated data if it is present
skilldata.erase("references")
skilldata["id"] = newskillid
var newskill: DSkill = DSkill.new(skilldata)
var newskill: DSkill = DSkill.new(skilldata, self)
skilldict[newskillid] = newskill
save_skills_to_disk()

# Adds a new skill with a given ID
func add_new(newid: String) -> void:
var newskill: DSkill = DSkill.new({"id": newid})
var newskill: DSkill = DSkill.new({"id": newid}, self)
skilldict[newskill.id] = newskill
save_skills_to_disk()

Expand Down
1 change: 0 additions & 1 deletion Scripts/Gamedata/DStats.gd
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ var spritePath: String = "./Mods/Core/Stats/"
var statdict: Dictionary = {}
var sprites: Dictionary = {}

# Constructor
# Add a mod_id parameter to dynamically initialize paths
func _init(mod_id: String) -> void:
# Update dataPath and spritePath using the provided mod_id
Expand Down
Loading

0 comments on commit 191ff9a

Please sign in to comment.