112 lines
3.7 KiB
GDScript
112 lines
3.7 KiB
GDScript
class_name DefenseResolver
|
|
extends RefCounted
|
|
|
|
|
|
static func resolve_effective_defense(context: Dictionary, attack_tags: Array[StringName] = []) -> Dictionary:
|
|
var defense_state := StringName(str(context.get("defense_state", &"Vulnerable")))
|
|
# 背刺破格挡只作用于姿态本身的 Parrying,先降级再合并 effect 修饰符:
|
|
# 否则霸体(SuperArmor,优先级低于 Parrying)会先被格挡态吞掉、
|
|
# 再随背刺降级一起丢失,违反霸体「无击退、不打断」的承诺。
|
|
if defense_state == &"Parrying" and not bool(context.get("attack_from_front", true)):
|
|
defense_state = &"Vulnerable"
|
|
var modifiers := _defense_modifiers(context, attack_tags)
|
|
for modifier: Resource in modifiers:
|
|
var modified_state := StringName(str(modifier.get("defense_state")))
|
|
if not modified_state.is_empty():
|
|
defense_state = _stronger_defense_state(defense_state, modified_state)
|
|
var damage_mult := _damage_mult_for_state(defense_state, context)
|
|
var interrupts := defense_state != &"Invincible" and defense_state != &"SuperArmor" and defense_state != &"Parrying"
|
|
for modifier: Resource in modifiers:
|
|
damage_mult *= float(modifier.get("damage_mult"))
|
|
interrupts = interrupts and bool(modifier.get("interrupts"))
|
|
return {
|
|
"defense_state": defense_state,
|
|
"damage_mult": damage_mult,
|
|
"interrupts": interrupts,
|
|
}
|
|
|
|
|
|
static func _damage_mult_for_state(defense_state: StringName, context: Dictionary = {}) -> float:
|
|
match defense_state:
|
|
&"Invincible":
|
|
return 0.0
|
|
&"Parrying":
|
|
return _parry_damage_mult(context)
|
|
&"SuperArmor":
|
|
return 1.0
|
|
_:
|
|
return 1.0
|
|
|
|
|
|
static func _parry_damage_mult(context: Dictionary) -> float:
|
|
match StringName(str(context.get("block_judgement", &"perfect"))):
|
|
&"good":
|
|
return 0.2
|
|
&"bad":
|
|
return 0.4
|
|
&"miss":
|
|
return 1.0
|
|
return 0.0
|
|
|
|
|
|
static func _defense_modifiers(context: Dictionary, attack_tags: Array[StringName]) -> Array[Resource]:
|
|
var modifiers: Array[Resource] = []
|
|
if context.has("defense_modifiers"):
|
|
modifiers = _resource_array(context["defense_modifiers"])
|
|
else:
|
|
var container = context.get("effect_container", null)
|
|
if container != null and container.has_method("defense_modifiers"):
|
|
modifiers = _resource_array(container.call("defense_modifiers"))
|
|
var matched: Array[Resource] = []
|
|
for modifier: Resource in modifiers:
|
|
if _modifier_matches_attack_tags(modifier, attack_tags):
|
|
matched.append(modifier)
|
|
return matched
|
|
|
|
|
|
static func _resource_array(value: Variant) -> Array[Resource]:
|
|
var result: Array[Resource] = []
|
|
if value is Array:
|
|
for item: Variant in value:
|
|
if item is Resource:
|
|
result.append(item)
|
|
return result
|
|
|
|
|
|
static func _modifier_matches_attack_tags(modifier: Resource, attack_tags: Array[StringName]) -> bool:
|
|
var required_tags := _string_name_array(modifier.get("required_attack_tags"))
|
|
return required_tags.is_empty() or _has_any(attack_tags, required_tags)
|
|
|
|
|
|
static func _string_name_array(value: Variant) -> Array[StringName]:
|
|
var result: Array[StringName] = []
|
|
if value is Array:
|
|
for item: Variant in value:
|
|
result.append(StringName(str(item)))
|
|
return result
|
|
|
|
|
|
static func _has_any(values: Array[StringName], candidates: Array[StringName]) -> bool:
|
|
for value: StringName in values:
|
|
if candidates.has(value):
|
|
return true
|
|
return false
|
|
|
|
|
|
static func _stronger_defense_state(current_state: StringName, candidate_state: StringName) -> StringName:
|
|
return candidate_state if _defense_priority(candidate_state) > _defense_priority(current_state) else current_state
|
|
|
|
|
|
static func _defense_priority(defense_state: StringName) -> int:
|
|
match defense_state:
|
|
&"Invincible":
|
|
return 4
|
|
&"Parrying":
|
|
return 3
|
|
&"SuperArmor":
|
|
return 2
|
|
&"Vulnerable":
|
|
return 1
|
|
_:
|
|
return 0
|