RapidGameFramework
Reusable Godot managers for data-driven small games
Loading...
Searching...
No Matches
spell_loadout_manager.gd
Go to the documentation of this file.
1extends RefCounted
2
3
6
7var unlocked := {}
8var equipped := []
9var max_slots := 3
10var min_slots := 1
11var fallback_ids := []
12
13
14
15func configure(unlocked_ids:Array, equipped_ids:Array = [], options:Dictionary = {}) -> void:
16 max_slots = max(1, int(options.get("max_slots", 3)))
17 min_slots = clamp(int(options.get("min_slots", 1)), 0, max_slots)
18 fallback_ids = _unique_ids(options.get("fallback_ids", []))
19 unlocked.clear()
20 for id in unlocked_ids:
21 var key = str(id)
22 if key != "":
23 unlocked[key] = true
24 for id in fallback_ids:
25 if id != "":
26 unlocked[id] = true
27 equipped = _unique_ids(equipped_ids)
28 _normalize()
29
30
31
32func add_unlocks(unlock_ids:Array, auto_equip := true) -> Array:
33 var newly_equipped := []
34 for id in unlock_ids:
35 var key = str(id)
36 if key == "":
37 continue
38 unlocked[key] = true
39 if auto_equip and equipped.size() < max_slots and not equipped.has(key):
40 equipped.append(key)
41 newly_equipped.append(key)
42 _normalize()
43 return newly_equipped
44
45
46func toggle(card_id:String) -> Dictionary:
47 if is_equipped(card_id):
48 return unequip(card_id)
49 return equip(card_id)
50
51
52func equip(card_id:String) -> Dictionary:
53 if not unlocked.has(card_id):
54 return {"ok": false, "reason": "locked", "message": "That spell is not unlocked yet."}
55 if equipped.has(card_id):
56 return {"ok": true, "action": "already_equipped", "equipped": get_equipped_ids()}
57 if equipped.size() >= max_slots:
58 return {"ok": false, "reason": "full", "message": "No open spell slots."}
59 equipped.append(card_id)
60 _normalize()
61 return {"ok": true, "action": "equipped", "equipped": get_equipped_ids()}
62
63
64func unequip(card_id:String) -> Dictionary:
65 if not equipped.has(card_id):
66 return {"ok": true, "action": "already_unequipped", "equipped": get_equipped_ids()}
67 if equipped.size() <= min_slots:
68 return {"ok": false, "reason": "minimum", "message": "Keep at least one spell equipped."}
69 equipped.erase(card_id)
70 _normalize()
71 return {"ok": true, "action": "unequipped", "equipped": get_equipped_ids()}
72
73
74func is_unlocked(card_id:String) -> bool:
75 return unlocked.has(card_id)
76
77
78func is_equipped(card_id:String) -> bool:
79 return equipped.has(card_id)
80
81
82func get_unlocked_ids() -> Array:
83 var ids = unlocked.keys()
84 ids.sort()
85 return ids
86
87
88func get_equipped_ids() -> Array:
89 return equipped.duplicate()
90
91
92func get_state() -> Dictionary:
93 return {
94 "unlocked": unlocked.duplicate(true),
95 "equipped": equipped.duplicate(),
96 "max_slots": max_slots,
97 "min_slots": min_slots,
98 "fallback_ids": fallback_ids.duplicate()
99 }
100
101
102func apply_state(state:Dictionary) -> void:
103 unlocked = state.get("unlocked", {}).duplicate(true)
104 equipped = state.get("equipped", []).duplicate()
105 max_slots = max(1, int(state.get("max_slots", 3)))
106 min_slots = clamp(int(state.get("min_slots", 1)), 0, max_slots)
107 fallback_ids = _unique_ids(state.get("fallback_ids", []))
108 _normalize()
109
110
111func _normalize() -> void:
112 var normalized := []
113 for id in equipped:
114 var key = str(id)
115 if unlocked.has(key) and not normalized.has(key):
116 normalized.append(key)
117 for id in fallback_ids:
118 if normalized.size() >= min_slots:
119 break
120 if unlocked.has(id) and not normalized.has(id):
121 normalized.append(id)
122 for id in get_unlocked_ids():
123 if normalized.size() >= min_slots:
124 break
125 if not normalized.has(id):
126 normalized.append(id)
127 equipped = normalized.slice(0, max_slots)
128
129
130func _unique_ids(ids:Array) -> Array:
131 var rows := []
132 for id in ids:
133 var key = str(id)
134 if key != "" and not rows.has(key):
135 rows.append(key)
136 return rows