This commit is contained in:
2026-01-17 13:59:18 -06:00
parent c46afdac76
commit fbc4e070da
10 changed files with 145 additions and 52 deletions

View File

@@ -2,9 +2,48 @@ class_name BattleScene extends Node3D
@export var actionBox:ActionBox = null @export var actionBox:ActionBox = null
signal neverEmitted
func _init() ->void: func _init() ->void:
BATTLE.battleScene = self BATTLE.battleScene = self
BATTLE.battleStarted.connect(onBattleStarted) BATTLE.battleStarted.connect(onBattleStarted)
func onBattleStarted() -> void: func onBattleStarted() -> void:
pass 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
break
elif action == ActionBox.Action.ATTACK:
var targets = await _getTargets(fighter, fighter.movePrimary)
actionBox.visible = false
if decision != null:
decisions.append(decision)
# Return the made decision
return decisions
func _getTargets(fighter:BattleFighter, move:BattleAction) -> Array[BattleFighter]:
print("Determining target")
await neverEmitted
return []

View File

@@ -1,11 +1,22 @@
class_name BattleAction class_name BattleAction
var speedModifier:float var speedModifier:float
var canTargetMultiple:bool
var canTargetEnemy:bool
var canTargetAlly:bool
var preferAlly:bool
func _init(params:Dictionary) -> void: func _init(params:Dictionary) -> void:
self.speedModifier = params.get("speedModifier", 1.0) self.speedModifier = params.get("speedModifier", 1.0)
self.canTargetMultiple = params.get("canTargetMultiple", false)
self.canTargetEnemy = params.get("canTargetEnemy", true)
self.canTargetAlly = params.get("canTargetAlly", true)
self.preferAlly = params.get("preferAlly", false)
func perform(params:Dictionary) -> void: func perform(params:Dictionary) -> void:
assert(params.has("user")) assert(params.has("user"))
assert(params.has("target")) assert(params.has("targets"))
pass pass
func canFighterUse(fighter:BattleFighter) -> bool:
return true

View File

@@ -2,12 +2,15 @@ class_name BattleDecision
var action:BattleAction var action:BattleAction
var user:BattleFighter var user:BattleFighter
var target:BattleFighter var targets:Array[BattleFighter]
func _init(_action:BattleAction, _user:BattleFighter, _target:BattleFighter) -> void: func _init(_action:BattleAction, _user:BattleFighter, _targets:Array[BattleFighter]) -> void:
action = _action action = _action
user = _user user = _user
target = _target targets = _targets
func getPriority() -> float: func getPriority() -> float:
return 1.0 return 1.0
func execute(cutscene:Cutscene) -> void:
pass

View File

@@ -11,4 +11,4 @@ func perform(params:Dictionary) -> void:
super.perform(params) super.perform(params)
var user:BattleFighter = params.get("user") var user:BattleFighter = params.get("user")
var target:BattleFighter = params.get("target") var targets:Array[BattleFighter] = params.get("targets")

View File

@@ -32,12 +32,13 @@ func perform(params:Dictionary):
super.perform(params) super.perform(params)
var user:BattleFighter = params.get("user") var user:BattleFighter = params.get("user")
var target:BattleFighter = params.get("target") var targets:Array[BattleFighter] = params.get("targets")
# What to do if target is dead? # What to do if target is dead?
if target.status == BattleFighter.Status.DEAD: for target in targets:
print("Target is already dead. Move has no effect.") if target.status == BattleFighter.Status.DEAD:
assert(false) print("Target is already dead. Move has no effect.")
assert(false)
# TODO: Determine damage # TODO: Determine damage
var damage:int = 0 var damage:int = 0
@@ -51,9 +52,19 @@ func perform(params:Dictionary):
if isCrit: if isCrit:
print("CRITICAL HIT!") print("CRITICAL HIT!")
target.damage(damage, isCrit) for target in targets:
target.damage(damage, isCrit)
print("DAMAGE DONE") print("DAMAGE DONE")
func canFighterUse(fighter:BattleFighter) -> bool:
if !super.canFighterUse(fighter):
return false
if fighter.mp < self.mpCost:
return false
return true
# Moves # Moves
static var MOVE_PUNCH = BattleMove.new({ static var MOVE_PUNCH = BattleMove.new({
"name": "Punch", "name": "Punch",
@@ -67,7 +78,8 @@ static var MOVE_FIRE1 = BattleMove.new({
"power": 25, "power": 25,
"mpCost": 5, "mpCost": 5,
"accuracy": 0.9, "accuracy": 0.9,
"moveType": MoveType.MAGICAL "moveType": MoveType.MAGICAL,
"canTargetMultiple": true
}) })
static var MOVE_HEAL1 = BattleMove.new({ static var MOVE_HEAL1 = BattleMove.new({
@@ -75,5 +87,7 @@ static var MOVE_HEAL1 = BattleMove.new({
"power": -20, "power": -20,
"mpCost": 8, "mpCost": 8,
"accuracy": 1.0, "accuracy": 1.0,
"moveType": MoveType.ABILITY "moveType": MoveType.ABILITY,
"canTargetMultiple": true,
"preferAlly": true
}) })

View File

@@ -6,7 +6,7 @@ enum Status {
} }
enum FighterTeam { enum FighterTeam {
PLAYER, ALLY,
ENEMY ENEMY
} }
@@ -106,4 +106,12 @@ func isPlayerControlled() -> bool:
return controller == FighterController.PLAYER return controller == FighterController.PLAYER
func getAIDecision() -> BattleDecision: func getAIDecision() -> BattleDecision:
if !canMakeDecision():
return null
return null return null
func canMakeDecision() -> bool:
return status != Status.DEAD
func canPlayerMakeDecision() -> bool:
return isPlayerControlled() && canMakeDecision()

View File

@@ -1,25 +1,40 @@
class_name ActionBox extends GridContainer class_name ActionBox extends GridContainer
enum Action {
ATTACK,
MAGIC,
ITEM,
BACK
}
@export var btnAttack:Button @export var btnAttack:Button
@export var btnMagic:Button @export var btnMagic:Button
@export var btnItem:Button @export var btnItem:Button
@export var btnBack:Button
signal decisionMade(move:BattleDecision) signal action(action:Action)
func _ready() -> void: func _ready() -> void:
btnAttack.pressed.connect(onAttackPressed) btnAttack.pressed.connect(func():
btnMagic.pressed.connect(onMagicPressed) action.emit(Action.ATTACK)
btnItem.pressed.connect(onItemPressed) )
btnMagic.pressed.connect(func():
action.emit(Action.MAGIC)
)
btnItem.pressed.connect(func():
action.emit(Action.ITEM)
)
btnBack.pressed.connect(func():
action.emit(Action.BACK)
)
self.visible = false self.visible = false
func onAttackPressed() -> void: func getAction(fighter:BattleFighter) -> Action:
print("Attack button pressed") btnAttack.disabled = fighter.movePrimary == null || !fighter.movePrimary.canFighterUse(fighter)
decisionMade.emit(BattleDecision.new(BattleMove.MOVE_PUNCH, null, null)) btnMagic.disabled = fighter.movesMagical.size() == 0
btnItem.disabled = false # TODO: check if items available?
func onMagicPressed() -> void: # TODO: Update cursor position to first enabled button
print("Magic button pressed")
decisionMade.emit(BattleDecision.new(BattleMove.MOVE_FIRE1, null, null))
func onItemPressed() -> void: var act = await action
print("Item button pressed") return act
decisionMade.emit(null)

View File

@@ -2,7 +2,7 @@
[ext_resource type="Script" uid="uid://27274005hbgh" path="res://battle/ui/ActionBox.gd" id="1_w5s71"] [ext_resource type="Script" uid="uid://27274005hbgh" path="res://battle/ui/ActionBox.gd" id="1_w5s71"]
[node name="ActionBox" type="GridContainer" node_paths=PackedStringArray("btnAttack", "btnMagic", "btnItem")] [node name="ActionBox" type="GridContainer" node_paths=PackedStringArray("btnAttack", "btnMagic", "btnItem", "btnBack")]
anchors_preset = 3 anchors_preset = 3
anchor_left = 1.0 anchor_left = 1.0
anchor_top = 1.0 anchor_top = 1.0
@@ -17,6 +17,7 @@ script = ExtResource("1_w5s71")
btnAttack = NodePath("Attack") btnAttack = NodePath("Attack")
btnMagic = NodePath("Magic") btnMagic = NodePath("Magic")
btnItem = NodePath("Items") btnItem = NodePath("Items")
btnBack = NodePath("Back")
metadata/_custom_type_script = "uid://27274005hbgh" metadata/_custom_type_script = "uid://27274005hbgh"
[node name="Attack" type="Button" parent="."] [node name="Attack" type="Button" parent="."]
@@ -31,6 +32,6 @@ text = "Magic"
layout_mode = 2 layout_mode = 2
text = "Items" text = "Items"
[node name="idk" type="Button" parent="."] [node name="Back" type="Button" parent="."]
layout_mode = 2 layout_mode = 2
text = "idk" text = "Back"

View File

@@ -37,29 +37,31 @@ static func playerDecisionCallable(_params:Dictionary) -> int:
print("All enemies defeated! Victory!") print("All enemies defeated! Victory!")
assert(false) assert(false)
# OK we are actually fighting. # Determine fighters and whether they're AI or player controlled
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)
# Get decisions
var decisions:Array[BattleDecision] = [] var decisions:Array[BattleDecision] = []
var playerDecisions = await BATTLE.battleScene.getBattleDecisions(playerFighters)
decisions.append_array(playerDecisions)
for fighter:BattleFighter in BATTLE.fighterMap.values(): for fighter in aiFighters:
if !fighter || !fighter.isPlayerControlled():
continue
# Handle player decision
BATTLE.battleScene.actionBox.visible = true
var decision = await BATTLE.battleScene.actionBox.decisionMade
BATTLE.battleScene.actionBox.visible = false
if decision == null:
continue
decisions.append(decision)
# AI decisions
for fighter:BattleFighter in BATTLE.fighterMap.values():
if !fighter || fighter.isPlayerControlled():
continue
var decision = fighter.getAIDecision() var decision = fighter.getAIDecision()
if decision == null: if decision != null:
continue decisions.append(decision)
decisions.append(decision)
# Here I could do something like cutscene specific logic? # Here I could do something like cutscene specific logic?

View File

@@ -2,7 +2,7 @@ class_name PartySingleton extends Node
static var PARTY_JOHN = PartyMember.new({ static var PARTY_JOHN = PartyMember.new({
'name': "John", 'name': "John",
'team': BattleFighter.FighterTeam.PLAYER, 'team': BattleFighter.FighterTeam.ALLY,
"maxHealth": 9999999999, "maxHealth": 9999999999,
"movePrimary": BattleMove.MOVE_PUNCH, "movePrimary": BattleMove.MOVE_PUNCH,
"movesMagical": [ BattleMove.MOVE_FIRE1 ], "movesMagical": [ BattleMove.MOVE_FIRE1 ],