Add goals
This commit is contained in:
211
generator-unlock-goals/generator_unlock_system.gd
Normal file
211
generator-unlock-goals/generator_unlock_system.gd
Normal file
@@ -0,0 +1,211 @@
|
||||
extends Node
|
||||
|
||||
class UnlockGoalRequirement extends RefCounted:
|
||||
var currency: GameState.CurrencyType
|
||||
var amount: BigNumber
|
||||
|
||||
func _init(req_currency: GameState.CurrencyType, req_amount: BigNumber) -> void:
|
||||
currency = req_currency
|
||||
amount = req_amount
|
||||
|
||||
class UnlockGoal extends RefCounted:
|
||||
var id: StringName
|
||||
var target_generator_id: StringName
|
||||
var requirements: Array[UnlockGoalRequirement]
|
||||
|
||||
func _init(goal_id: StringName, target_id: StringName, goal_requirements: Array[UnlockGoalRequirement]) -> void:
|
||||
id = goal_id
|
||||
target_generator_id = target_id
|
||||
requirements = goal_requirements
|
||||
|
||||
const GOALS_FILE_PATH: String = "res://generator-unlock-goals/generator_unlock_goals.json"
|
||||
const SCHEMA_VERSION: int = 1
|
||||
|
||||
var _goals: Array[UnlockGoal] = []
|
||||
var _known_generator_ids: Dictionary = {}
|
||||
var _warned_unknown_target_ids: Dictionary = {}
|
||||
|
||||
func _ready() -> void:
|
||||
_collect_known_generator_ids()
|
||||
_load_goals()
|
||||
GameState.currency_changed.connect(_on_currency_changed)
|
||||
_evaluate_all_goals()
|
||||
|
||||
func _on_currency_changed(_changed_currency: GameState.CurrencyType, _new_amount: BigNumber) -> void:
|
||||
_evaluate_all_goals()
|
||||
|
||||
func _evaluate_all_goals() -> void:
|
||||
if _goals.is_empty():
|
||||
return
|
||||
|
||||
for goal in _goals:
|
||||
_evaluate_goal(goal)
|
||||
|
||||
func _evaluate_goal(goal: UnlockGoal) -> bool:
|
||||
if _is_goal_completed(goal):
|
||||
return false
|
||||
if not _can_resolve_target(goal.target_generator_id):
|
||||
return false
|
||||
|
||||
for requirement in goal.requirements:
|
||||
var total_amount: BigNumber = _get_total_currency_amount(requirement.currency)
|
||||
if total_amount.is_less_than(requirement.amount):
|
||||
return false
|
||||
|
||||
GameState.set_generator_unlocked(goal.target_generator_id, true)
|
||||
GameState.set_generator_available(goal.target_generator_id, true)
|
||||
print("[UnlockSystem] Goal completed: %s -> %s" % [String(goal.id), String(goal.target_generator_id)])
|
||||
return true
|
||||
|
||||
func _is_goal_completed(goal: UnlockGoal) -> bool:
|
||||
return (
|
||||
GameState.is_generator_unlocked(goal.target_generator_id)
|
||||
and GameState.is_generator_available(goal.target_generator_id)
|
||||
)
|
||||
|
||||
func _can_resolve_target(target_generator_id: StringName) -> bool:
|
||||
var target_key: String = String(target_generator_id)
|
||||
if target_key.is_empty():
|
||||
return false
|
||||
|
||||
if _known_generator_ids.has(target_key) or GameState.generator_states.has(target_key):
|
||||
return true
|
||||
|
||||
if not _warned_unknown_target_ids.has(target_key):
|
||||
_warned_unknown_target_ids[target_key] = true
|
||||
push_warning("Unlock goal target generator not found in scene/state: '%s'" % target_key)
|
||||
return false
|
||||
|
||||
func _load_goals() -> void:
|
||||
_goals.clear()
|
||||
|
||||
if not FileAccess.file_exists(GOALS_FILE_PATH):
|
||||
push_warning("Unlock goals file not found: %s" % GOALS_FILE_PATH)
|
||||
return
|
||||
|
||||
var file: FileAccess = FileAccess.open(GOALS_FILE_PATH, FileAccess.READ)
|
||||
if file == null:
|
||||
push_warning("Unable to open unlock goals file: %s" % GOALS_FILE_PATH)
|
||||
return
|
||||
|
||||
var parsed: Variant = JSON.parse_string(file.get_as_text())
|
||||
if not (parsed is Dictionary):
|
||||
push_warning("Unlock goals root must be a Dictionary: %s" % GOALS_FILE_PATH)
|
||||
return
|
||||
|
||||
var root: Dictionary = parsed
|
||||
var version: int = int(root.get("version", SCHEMA_VERSION))
|
||||
if version != SCHEMA_VERSION:
|
||||
push_warning("Unlock goals schema version %d is different from expected %d" % [version, SCHEMA_VERSION])
|
||||
|
||||
var raw_goals: Variant = root.get("goals", [])
|
||||
if not (raw_goals is Array):
|
||||
push_warning("Unlock goals file has invalid 'goals' field (expected Array)")
|
||||
return
|
||||
|
||||
var seen_goal_ids: Dictionary = {}
|
||||
for raw_goal in raw_goals:
|
||||
var goal: UnlockGoal = _parse_goal(raw_goal)
|
||||
if goal == null:
|
||||
continue
|
||||
|
||||
var goal_key: String = String(goal.id)
|
||||
if seen_goal_ids.has(goal_key):
|
||||
push_warning("Duplicate unlock goal id skipped: '%s'" % goal_key)
|
||||
continue
|
||||
|
||||
seen_goal_ids[goal_key] = true
|
||||
_goals.append(goal)
|
||||
|
||||
if _goals.is_empty():
|
||||
push_warning("Unlock goals loaded, but no valid entries were found.")
|
||||
|
||||
func _parse_goal(raw_goal: Variant) -> UnlockGoal:
|
||||
if not (raw_goal is Dictionary):
|
||||
push_warning("Skipping unlock goal: entry is not a Dictionary")
|
||||
return null
|
||||
|
||||
var goal_dict: Dictionary = raw_goal
|
||||
var goal_id: String = String(goal_dict.get("id", "")).strip_edges()
|
||||
var target_id: String = String(goal_dict.get("target_generator_id", "")).strip_edges()
|
||||
|
||||
if goal_id.is_empty():
|
||||
push_warning("Skipping unlock goal: missing 'id'")
|
||||
return null
|
||||
if target_id.is_empty():
|
||||
push_warning("Skipping unlock goal '%s': missing 'target_generator_id'" % goal_id)
|
||||
return null
|
||||
|
||||
var raw_requirements: Variant = goal_dict.get("requirements", [])
|
||||
if not (raw_requirements is Array):
|
||||
push_warning("Skipping unlock goal '%s': 'requirements' must be an Array" % goal_id)
|
||||
return null
|
||||
|
||||
var parsed_requirements: Array[UnlockGoalRequirement] = []
|
||||
for raw_requirement in raw_requirements:
|
||||
var requirement: UnlockGoalRequirement = _parse_requirement(raw_requirement, goal_id)
|
||||
if requirement != null:
|
||||
parsed_requirements.append(requirement)
|
||||
|
||||
if parsed_requirements.is_empty():
|
||||
push_warning("Skipping unlock goal '%s': no valid requirements" % goal_id)
|
||||
return null
|
||||
|
||||
return UnlockGoal.new(StringName(goal_id), StringName(target_id), parsed_requirements)
|
||||
|
||||
func _parse_requirement(raw_requirement: Variant, goal_id: String) -> UnlockGoalRequirement:
|
||||
if not (raw_requirement is Dictionary):
|
||||
push_warning("Skipping requirement in goal '%s': entry is not a Dictionary" % goal_id)
|
||||
return null
|
||||
|
||||
var requirement_dict: Dictionary = raw_requirement
|
||||
var currency_value: int = _parse_currency(requirement_dict.get("currency", ""))
|
||||
if currency_value < 0:
|
||||
push_warning("Skipping requirement in goal '%s': unknown currency '%s'" % [goal_id, String(requirement_dict.get("currency", ""))])
|
||||
return null
|
||||
|
||||
var raw_amount: Variant = requirement_dict.get("amount", {})
|
||||
if not (raw_amount is Dictionary):
|
||||
push_warning("Skipping requirement in goal '%s': 'amount' must be a Dictionary" % goal_id)
|
||||
return null
|
||||
|
||||
var amount: BigNumber = BigNumber.deserialize(raw_amount)
|
||||
if amount.mantissa < 0.0:
|
||||
push_warning("Skipping requirement in goal '%s': amount must be non-negative" % goal_id)
|
||||
return null
|
||||
|
||||
return UnlockGoalRequirement.new(currency_value, amount)
|
||||
|
||||
func _parse_currency(raw_currency: Variant) -> int:
|
||||
if raw_currency is int:
|
||||
var currency_int: int = raw_currency
|
||||
if currency_int == GameState.CurrencyType.gold or currency_int == GameState.CurrencyType.gems:
|
||||
return currency_int
|
||||
return -1
|
||||
|
||||
var currency_text: String = String(raw_currency).to_lower().strip_edges()
|
||||
match currency_text:
|
||||
"gold":
|
||||
return GameState.CurrencyType.gold
|
||||
"gems", "gem":
|
||||
return GameState.CurrencyType.gems
|
||||
_:
|
||||
return -1
|
||||
|
||||
func _get_total_currency_amount(currency: GameState.CurrencyType) -> BigNumber:
|
||||
return GameState.get_total_currency_acquired(currency)
|
||||
|
||||
func _collect_known_generator_ids() -> void:
|
||||
_known_generator_ids.clear()
|
||||
|
||||
var generator_nodes: Array[Node] = find_children("*", "CurrencyGenerator", true, false)
|
||||
for generator_node in generator_nodes:
|
||||
var generator: CurrencyGenerator = generator_node as CurrencyGenerator
|
||||
if generator == null:
|
||||
continue
|
||||
|
||||
var generator_key: String = String(generator.get_generator_id())
|
||||
if generator_key.is_empty():
|
||||
continue
|
||||
|
||||
_known_generator_ids[generator_key] = true
|
||||
Reference in New Issue
Block a user