Files
Dawn-Godot/battle/BattleScene.gd
2026-01-18 11:44:34 -06:00

181 lines
5.6 KiB
GDScript

class_name BattleScene extends Node3D
@export var actionBox:ActionBox = null
@export var battleFighterScenes:Node
@export var cursorScenes:Node
signal neverEmitted
func _init() ->void:
BATTLE.battleScene = self
BATTLE.battleStarted.connect(onBattleStarted)
func onBattleStarted() -> void:
pass
func getBattleDecisions(fighters:Array[BattleFighter]) -> Array[BattleDecision]:
var decisions:Array[BattleDecision] = []
while fighters.size() > decisions.size():
var fighter = fighters[decisions.size()]
var decision:BattleDecision = null
# Simulate walking forward animation like in Final Fantasy
await get_tree().create_timer(0.5).timeout
# Until decision made...
while decision == null:
# Get the selected action
actionBox.visible = true
var action = await self.actionBox.getAction(fighter)
# Go back to previous fighter
if action == ActionBox.Action.BACK:
decisions.pop_back()
actionBox.visible = false
# Get target for attack
elif action == ActionBox.Action.ATTACK:
var targets = await _getTargets(fighter, fighter.movePrimary)
decision = BattleDecision.new(
fighter.movePrimary,
fighter,
targets
)
# Get Magic move and targets
elif action == ActionBox.Action.MAGIC:
var magicMove = await _getMagicMove(fighter)
if magicMove != null:
var targets = await _getTargets(fighter, magicMove)
decision = BattleDecision.new(
magicMove,
fighter,
targets
)
# Get Item use and targets
elif action == ActionBox.Action.ITEM:
var itemMove = await _getItemUse(fighter)
if itemMove != null:
var targets = await _getTargets(fighter, itemMove)
decision = BattleDecision.new(
itemMove,
fighter,
targets
)
if decision != null:
decisions.append(decision)
# Return the made decision
return decisions
func _getInitialTargetPosition(
fighter:BattleFighter,
move:BattleAction,
possiblePositions:Array[BattleSingleton.BattlePosition],
) -> BattleSingleton.BattlePosition:
# Find ally in possible positions
if move.preferAlly:
for pos in possiblePositions:
var targetFighter = BATTLE.getFighterAtPosition(pos)
if targetFighter != null and targetFighter.team == fighter.team:
return pos
# Otherwise, prefer to target enemy
for pos in possiblePositions:
var targetFighter = BATTLE.getFighterAtPosition(pos)
if targetFighter != null and targetFighter.team != fighter.team:
return pos
# If still noting found, just return first possible position
if possiblePositions.size() > 0:
return possiblePositions[0]
# Fallback (should not happen)
assert(false)
return BATTLE.BattlePosition.LEFT_TOP_BACK
func _getTargets(fighter:BattleFighter, move:BattleAction) -> Array[BattleFighter]:
assert(fighter != null)
assert(move != null)
# Determine possible targets and whether to blink or not
var possiblePositions:Array[BattleSingleton.BattlePosition] = []
if move.canTargetEnemy:
possiblePositions.append_array(BATTLE.getEnemyPositions())
if move.canTargetAlly:
possiblePositions.append_array(BATTLE.getAllyPositions())
# Should not be possible to have no possible targets here
assert(possiblePositions.size() > 0)
var activePositions:Array[BattleSingleton.BattlePosition] = [
_getInitialTargetPosition(fighter, move, possiblePositions)
]
# This is where output will be stored.
var selectedPositions:Array[BattleSingleton.BattlePosition] = []
# Here we wait for a selection to be confirmed.
while selectedPositions.size() == 0:
var blink = activePositions.size() > 1
# Show cursors
for cursor in cursorScenes.get_children():
if !(cursor is BattleCursorIcon):
continue
if cursor.battlePosition in activePositions:
if blink:
cursor.mode = BattleCursorIcon.Mode.BLINKING
else:
cursor.mode = BattleCursorIcon.Mode.ACTIVE
else:
cursor.mode = BattleCursorIcon.Mode.INACTIVE
# Handle movement
# TODO: Allow movement here, wait for selection, etc.
# Wait 1 second for now and select all possible targets
await get_tree().create_timer(1.0).timeout
# selectedPositions = possiblePositions
# By this point selection has been made, hide all cursors
for cursor in cursorScenes.get_children():
if !(cursor is BattleCursorIcon):
continue
cursor.mode = BattleCursorIcon.Mode.INACTIVE
# Convert selected positions to fighters
var targets:Array[BattleFighter] = []
for pos in selectedPositions:
var targetFighter = BATTLE.getFighterAtPosition(pos)
if targetFighter != null:
targets.append(targetFighter)
# Should not be possible to have no targets here
assert(targets.size() > 0)
return targets
func _getMagicMove(fighter:BattleFighter) -> BattleAction:
return null
func _getItemUse(fighter:BattleFighter) -> BattleAction:
return null
func getFighterSceneAtPosition(pos:BattleSingleton.BattlePosition) -> BattleFighterScene:
for fighterScene in battleFighterScenes.get_children():
if !(fighterScene is BattleFighterScene):
continue
if fighterScene.battlePosition == pos:
return fighterScene
return null
func getBattleCursorAtPosition(pos:BattleSingleton.BattlePosition) -> BattleCursorIcon:
for cursorScene in cursorScenes.get_children():
if !(cursorScene is BattleCursorIcon):
continue
if cursorScene.battlePosition == pos:
return cursorScene
return null