45 lines
1.7 KiB
GDScript
45 lines
1.7 KiB
GDScript
extends Object
|
|
|
|
## Pure resolver for digit hotbar → cast intent (NEO-31). [code]main.gd[/code] delegates here so
|
|
## GdUnit can cover unbound slots and [code]lockedTargetId[/code] sourcing without booting
|
|
## the scene.
|
|
## No [code]class_name[/code] — match [code]prototype_target_constants.gd[/code] preload pattern.
|
|
|
|
const KEY_OK := "ok"
|
|
const KEY_REASON := "reason"
|
|
const KEY_SLOT_INDEX := "slotIndex"
|
|
const KEY_ABILITY_ID := "abilityId"
|
|
const KEY_TARGET_ID := "targetId"
|
|
|
|
const REASON_INVALID_SLOT := "invalid_slot_index"
|
|
const REASON_EMPTY_SLOT := "empty_slot"
|
|
|
|
|
|
## [param slots] snapshot from [code]HotbarState.slots_snapshot()[/code] (fixed length, entries
|
|
## [code]null[/code] or [String] ability ids).
|
|
## [param target_cached_state] from [code]TargetSelectionClient.cached_state()[/code]
|
|
## (may be empty).
|
|
## Returns [code]{ "ok": true, "slotIndex", "abilityId", "targetId" }[/code] or
|
|
## [code]{ "ok": false, "reason" }[/code].
|
|
static func resolve(slot_index: int, slots: Array, target_cached_state: Dictionary) -> Dictionary:
|
|
if slot_index < 0 or slot_index >= slots.size():
|
|
return {KEY_OK: false, KEY_REASON: REASON_INVALID_SLOT}
|
|
var ability_variant: Variant = slots[slot_index]
|
|
if (
|
|
ability_variant == null
|
|
or not (ability_variant is String)
|
|
or (ability_variant as String).strip_edges().is_empty()
|
|
):
|
|
return {KEY_OK: false, KEY_REASON: REASON_EMPTY_SLOT}
|
|
var ability_id: String = (ability_variant as String).strip_edges()
|
|
var target_id: Variant = null
|
|
var locked: Variant = target_cached_state.get("lockedTargetId", null)
|
|
if locked is String and not (locked as String).is_empty():
|
|
target_id = locked as String
|
|
return {
|
|
KEY_OK: true,
|
|
KEY_SLOT_INDEX: slot_index,
|
|
KEY_ABILITY_ID: ability_id,
|
|
KEY_TARGET_ID: target_id,
|
|
}
|