Files
Dawn-Godot/cutscene/battle/BattleCutsceneAction.gd
T
2026-06-12 11:56:30 -05:00

117 lines
3.8 KiB
GDScript

class_name BattleCutsceneAction
const _NARRATION_BASE:String = "res://dialogue/battle/narration"
# State object passed as extra_game_states so {{variable}} tokens resolve in the dialogue file.
class BattleNarrationState:
var fighter_name:String
var user_name:String
var target_name:String
var move_name:String
var damage:int
func _init(params:Dictionary) -> void:
fighter_name = params.get('fighter_name', '')
user_name = params.get('user_name', '')
target_name = params.get('target_name', '')
move_name = params.get('move_name', '')
damage = params.get('damage', 0)
# Narrates and executes a single fighter's decision.
static func battleDecisionCallable(params:Dictionary) -> int:
var decision:BattleDecision = params['decision']
var state = BattleNarrationState.new({
'user_name': decision.user.name if decision.user is PartyMember else 'Enemy',
'move_name': decision.action.handle,
'target_name': decision.targets[0].name if decision.targets[0] is PartyMember else 'Enemy',
})
var cutscene:Cutscene = params['cutscene']
cutscene.addCallable(
DialogueAction.getDialogueCallable(_NARRATION_BASE, 'move_perform', [state], DialogueAction.DialogueMode.NARRATION).merged(
{'position': Cutscene.CUTSCENE_ADD_NEXT}, false
)
)
decision.action.perform({'user': decision.user, 'targets': decision.targets})
return Cutscene.CUTSCENE_CONTINUE
static func getBattleDecisionCallable(decision:BattleDecision) -> Dictionary:
return {
'function': battleDecisionCallable,
'decision': decision,
}
static func playerDecisionCallable(params:Dictionary) -> int:
# Check end conditions
var allPlayersDead:bool = true
var allEnemiesDead:bool = true
for fighter:BattleFighter in BATTLE.fighterMap.values():
if fighter == null:
continue
if fighter.status == BattleFighter.Status.DEAD:
continue
if fighter.isPlayerControlled():
allPlayersDead = false
else:
allEnemiesDead = false
if allPlayersDead:
params['cutscene'].addCallable(
DialogueAction.getDialogueCallable(_NARRATION_BASE, 'battle_defeat', [], DialogueAction.DialogueMode.NARRATION).merged(
{'position': Cutscene.CUTSCENE_ADD_NEXT}, false
)
)
return Cutscene.CUTSCENE_END
if allEnemiesDead:
params['cutscene'].addCallable(
DialogueAction.getDialogueCallable(_NARRATION_BASE, 'battle_victory', [], DialogueAction.DialogueMode.NARRATION).merged(
{'position': Cutscene.CUTSCENE_ADD_NEXT}, false
)
)
return Cutscene.CUTSCENE_END
# Collect fighters
var playerFighters:Array[BattleFighter] = []
var aiFighters:Array[BattleFighter] = []
for fighter:BattleFighter in BATTLE.fighterMap.values():
if !fighter || !fighter.canPlayerMakeDecision():
continue
playerFighters.append(fighter)
for fighter:BattleFighter in BATTLE.fighterMap.values():
if !fighter || !fighter.canMakeDecision():
continue
if fighter in playerFighters:
continue
aiFighters.append(fighter)
# Gather decisions
var decisions:Array[BattleDecision] = []
var playerDecisions = await BATTLE.battleScene.getBattleDecisions(playerFighters)
decisions.append_array(playerDecisions)
for fighter in aiFighters:
var decision = fighter.getAIDecision()
if decision != null:
decisions.append(decision)
# Sort by priority
decisions.shuffle()
decisions.sort_custom(func(a:BattleDecision, b:BattleDecision) -> int:
var pa = a.getPriority()
var pb = b.getPriority()
if pa > pb: return -1
elif pa < pb: return 1
else: return 0
)
for decision in decisions:
params['cutscene'].addCallable(getBattleDecisionCallable(decision))
params['cutscene'].addCallable(getPlayerDecisionCallable())
return Cutscene.CUTSCENE_CONTINUE
static func getPlayerDecisionCallable() -> Dictionary:
return {
'function': playerDecisionCallable,
}