This skill should be used when the user asks to "implement progression", "build inventory", "code save system", "create UI", "implement quests", or mentions RPG systems, game systems, or feature implementation.
Guide the programming implementation of core game systems for a 2D action-RPG.
For a solo dev, implement in this order:
var save_data = {
# Player state
"player_position": player.global_position,
"player_health": player.health,
"player_level": player.level,
"player_xp": player.experience,
"player_stats": player.stats.duplicate(),
# Inventory
"inventory": inventory.serialize(),
"equipment": equipment.serialize(),
"currency": player.currency,
# World state
"current_zone": current_zone_id,
"unlocked_zones": unlocked_zones.duplicate(),
"opened_chests": opened_chests.duplicate(),
"defeated_bosses": defeated_bosses.duplicate(),
"npc_states": npc_manager.serialize(),
# Quests
"active_quests": quest_manager.serialize_active(),
"completed_quests": quest_manager.completed.duplicate(),
# Meta
"play_time": play_time,
"save_timestamp": Time.get_unix_time_from_system()
}
const SAVE_PATH = "user://save.dat"
func save_game():
var save_data = collect_save_data()
var file = FileAccess.open(SAVE_PATH, FileAccess.WRITE)
file.store_var(save_data)
file.close()
func load_game():
if not FileAccess.file_exists(SAVE_PATH):
return false
var file = FileAccess.open(SAVE_PATH, FileAccess.READ)
var save_data = file.get_var()
file.close()
apply_save_data(save_data)
return true
func apply_save_data(data):
# Restore player
player.global_position = data.player_position
player.health = data.player_health
# ... restore all data
Save Points (Recommended for Action-RPG):
Auto-Save:
class_name Inventory
var slots: Array[InventorySlot] = []
var max_slots: int = 20
class InventorySlot:
var item_id: String
var quantity: int
var metadata: Dictionary # durability, enchants, etc.
func add_item(item_id: String, quantity: int = 1) -> bool:
# Check for existing stack
for slot in slots:
if slot.item_id == item_id and can_stack(item_id):
slot.quantity += quantity
return true
# Find empty slot
if slots.size() < max_slots:
slots.append(InventorySlot.new(item_id, quantity))
return true
return false # Inventory full
func remove_item(item_id: String, quantity: int = 1) -> bool:
for slot in slots:
if slot.item_id == item_id:
slot.quantity -= quantity
if slot.quantity <= 0:
slots.erase(slot)
return true
return false
func has_item(item_id: String, quantity: int = 1) -> bool:
for slot in slots:
if slot.item_id == item_id and slot.quantity >= quantity:
return true
return false
# items.json or resource files
{
"health_potion": {
"name": "Health Potion",
"type": "consumable",
"icon": "res://items/health_potion.png",
"effect": "heal",
"value": 50,
"price": 25,
"stackable": true,
"max_stack": 99
},
"iron_sword": {
"name": "Iron Sword",
"type": "weapon",
"icon": "res://items/iron_sword.png",
"damage": 15,
"price": 100,
"stackable": false
}
}
var level: int = 1
var experience: int = 0
func xp_for_level(lvl: int) -> int:
# Exponential curve
return int(100 * pow(1.5, lvl - 1))
func total_xp_for_level(lvl: int) -> int:
var total = 0
for i in range(1, lvl):
total += xp_for_level(i)
return total
func add_experience(amount: int):
experience += amount
while experience >= xp_for_level(level):
experience -= xp_for_level(level)
level_up()
func level_up():
level += 1
# Apply stat increases
max_health += health_per_level
attack += attack_per_level
defense += defense_per_level
# Heal on level up (optional)
health = max_health
emit_signal("leveled_up", level)
class_name Stats
var base_stats = {
"max_health": 100,
"attack": 10,
"defense": 5,
"speed": 100
}
var stat_modifiers = {
"max_health": [],
"attack": [],
"defense": [],
"speed": []
}
func get_stat(stat_name: String) -> int:
var base = base_stats[stat_name]
var flat_bonus = 0
var multiplier = 1.0
for mod in stat_modifiers[stat_name]:
if mod.type == "flat":
flat_bonus += mod.value
elif mod.type == "percent":
multiplier += mod.value
return int((base + flat_bonus) * multiplier)
func add_modifier(stat_name: String, mod_type: String, value: float, source: String):
stat_modifiers[stat_name].append({
"type": mod_type,
"value": value,
"source": source
})
func remove_modifiers_from_source(source: String):
for stat in stat_modifiers:
stat_modifiers[stat] = stat_modifiers[stat].filter(
func(m): return m.source != source
)
class_name Equipment
enum Slot { WEAPON, ARMOR, ACCESSORY_1, ACCESSORY_2 }
var equipped: Dictionary = {
Slot.WEAPON: null,
Slot.ARMOR: null,
Slot.ACCESSORY_1: null,
Slot.ACCESSORY_2: null
}
func equip(item_id: String, slot: Slot):
var item_data = ItemDatabase.get_item(item_id)
# Unequip current
if equipped[slot] != null:
unequip(slot)
# Equip new
equipped[slot] = item_id
# Apply stat modifiers
for stat in item_data.stats:
player.stats.add_modifier(stat, "flat", item_data.stats[stat], item_id)
emit_signal("equipment_changed", slot)
func unequip(slot: Slot):
var item_id = equipped[slot]
if item_id == null:
return
# Remove stat modifiers
player.stats.remove_modifiers_from_source(item_id)
# Return to inventory
player.inventory.add_item(item_id)
equipped[slot] = null
emit_signal("equipment_changed", slot)
{
"npc_merchant": {
"greeting": {
"text": "Welcome, traveler. See anything you like?",
"options": [
{"text": "Show me your wares.", "action": "open_shop"},
{"text": "What can you tell me about this place?", "next": "lore_info"},
{"text": "Goodbye.", "action": "close"}
]
},
"lore_info": {
"text": "This village has seen better days. The creatures from the forest grow bolder...",
"next": "greeting"
}
}
}
class_name DialogueManager
signal dialogue_started(npc_id)
signal dialogue_line(text, options)
signal dialogue_ended
var current_npc: String
var current_node: String
func start_dialogue(npc_id: String):
current_npc = npc_id
current_node = "greeting"
dialogue_started.emit(npc_id)
show_current_node()
func show_current_node():
var dialogue = DialogueDatabase.get_dialogue(current_npc, current_node)
dialogue_line.emit(dialogue.text, dialogue.get("options", []))
func select_option(option_index: int):
var dialogue = DialogueDatabase.get_dialogue(current_npc, current_node)
var option = dialogue.options[option_index]
if option.has("action"):
execute_action(option.action)
if option.has("next"):
current_node = option.next
show_current_node()
else:
end_dialogue()
func end_dialogue():
current_npc = ""
current_node = ""
dialogue_ended.emit()
class_name Quest
var id: String
var title: String
var description: String
var objectives: Array[QuestObjective]
var rewards: Dictionary
var is_main_quest: bool
class QuestObjective:
var type: String # "kill", "collect", "talk", "reach"
var target: String # enemy_id, item_id, npc_id, zone_id
var required: int
var current: int = 0
func is_complete() -> bool:
return current >= required
func is_complete() -> bool:
return objectives.all(func(o): return o.is_complete())
class_name QuestManager
var active_quests: Array[Quest] = []
var completed_quests: Array[String] = []
signal quest_accepted(quest)
signal quest_updated(quest, objective)
signal quest_completed(quest)
func accept_quest(quest_id: String):
var quest = QuestDatabase.create_quest(quest_id)
active_quests.append(quest)
quest_accepted.emit(quest)
func update_objective(type: String, target: String, amount: int = 1):
for quest in active_quests:
for objective in quest.objectives:
if objective.type == type and objective.target == target:
objective.current = min(objective.current + amount, objective.required)
quest_updated.emit(quest, objective)
if quest.is_complete():
complete_quest(quest)
func complete_quest(quest: Quest):
active_quests.erase(quest)
completed_quests.append(quest.id)
grant_rewards(quest.rewards)
quest_completed.emit(quest)
CanvasLayer (UI)
├── HUD
│ ├── HealthBar
│ ├── Currency
│ └── Minimap
├── Menus
│ ├── PauseMenu
│ ├── InventoryScreen
│ ├── EquipmentScreen
│ └── MapScreen
└── Overlays
├── DialogueBox
├── DamageNumbers
└── Notifications
extends ProgressBar
func _ready():
player.health_changed.connect(_on_health_changed)
max_value = player.max_health
value = player.health
func _on_health_changed(new_health, max_health):
max_value = max_health
# Animate health change
var tween = create_tween()
tween.tween_property(self, "value", new_health, 0.2)
references/data-persistence.md - Save system patternsreferences/ui-patterns.md - Common UI implementationsexamples/inventory-system.gd - Complete inventory implementationexamples/quest-manager.gd - Quest system template