Change battle actions to use battle decisions instead.

This commit is contained in:
2026-01-15 19:09:46 -06:00
parent 3b7de160dc
commit 274cbef780
15 changed files with 173 additions and 41 deletions

View File

@@ -46,8 +46,7 @@ func startBattle(params) -> void:
# Initial cutscene elements. In future I may need to make this editable # Initial cutscene elements. In future I may need to make this editable
# somehow? # somehow?
for fighter:BattleFighter in params['fighters'].values(): battleCutscene.addCallable(BattleCutsceneAction.getPlayerDecisionCallable())
battleCutscene.addCallable(BattleCutsceneAction.getPlayerDecisionCallable(fighter))
# Emit signals # Emit signals
active = true active = true
@@ -56,7 +55,15 @@ func startBattle(params) -> void:
# Start running the battle cutscene. # Start running the battle cutscene.
if !battleCutscene.running: if !battleCutscene.running:
battleCutscene.start() battleCutscene.start()# Should this await?
func getFighterAtPosition(battlePos:BattlePosition) -> BattleFighter: func getFighterAtPosition(battlePos:BattlePosition) -> BattleFighter:
return fighterMap.get(battlePos, null) return fighterMap.get(battlePos, null)
func getPositionOfFighter(fighter:BattleFighter) -> BattlePosition:
for pos in fighterMap.keys():
if fighterMap[pos] == fighter:
return pos
assert(false)
return BattlePosition.LEFT_TOP_BACK

View File

@@ -4,7 +4,6 @@ class_name BattleScene extends Node3D
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:

View File

@@ -0,0 +1,11 @@
class_name BattleAction
var speedModifier:float
func _init(params:Dictionary) -> void:
self.speedModifier = params.get("speedModifier", 1.0)
func perform(params:Dictionary) -> void:
assert(params.has("user"))
assert(params.has("target"))
pass

View File

@@ -0,0 +1 @@
uid://ref2clyh0nc2

View File

@@ -0,0 +1,13 @@
class_name BattleDecision
var action:BattleAction
var user:BattleFighter
var target:BattleFighter
func _init(_action:BattleAction, _user:BattleFighter, _target:BattleFighter) -> void:
action = _action
user = _user
target = _target
func getPriority() -> float:
return 1.0

View File

@@ -0,0 +1 @@
uid://bhdv13g6t1h01

View File

@@ -0,0 +1,50 @@
class_name BattleMove extends BattleAction
enum MoveType {
PHYSICAL,
ABILITY,
MAGICAL,
}
var name:String
var power:int
var mpCost:int
var accuracy:float
var moveType:MoveType
var fieldUse:bool
func _init(params:Dictionary) -> void:
self.name = params.get("name", "Unknown Move")
self.power = params.get("power", 0)
self.mpCost = params.get("mpCost", 0)
self.speedModifier = params.get("speedModifier", 1.0)
self.accuracy = params.get("accuracy", 1.0)
self.moveType = params.get("moveType", MoveType.PHYSICAL)
self.fieldUse = params.get("fieldUse", false)
func getPriority(_fighter:BattleFighter) -> float:
return 1.0
# Moves
static var MOVE_PUNCH = BattleMove.new({
"name": "Punch",
"power": 15,
"accuracy": 0.95,
"moveType": MoveType.PHYSICAL
})
static var MOVE_FIRE1 = BattleMove.new({
"name": "Fire",
"power": 25,
"mpCost": 5,
"accuracy": 0.9,
"moveType": MoveType.MAGICAL
})
static var MOVE_HEAL1 = BattleMove.new({
"name": "Heal",
"power": -20,
"mpCost": 8,
"accuracy": 1.0,
"moveType": MoveType.ABILITY
})

View File

@@ -0,0 +1 @@
uid://gb08hhwk0paw

View File

@@ -102,8 +102,8 @@ func mpRestore(amount:int) -> void:
mp = min(mp + amount, maxMp) mp = min(mp + amount, maxMp)
mpChanged.emit(amount) mpChanged.emit(amount)
func isCrit() -> bool: func isPlayerControlled() -> bool:
# 10% chance of a crit return controller == FighterController.PLAYER
var chance = 10 + min(luck * 5, 60)
var roll = randi() % 100 func getAIDecision() -> BattleDecision:
return roll < chance return null

View File

@@ -1,4 +1,4 @@
class_name BattleMove class_name BattleMove extends BattleAction
enum MoveType { enum MoveType {
PHYSICAL, PHYSICAL,
@@ -9,24 +9,19 @@ enum MoveType {
var name:String var name:String
var power:int var power:int
var mpCost:int var mpCost:int
var speedModifier:float
var accuracy:float var accuracy:float
var moveType:MoveType var moveType:MoveType
var fieldUse:bool var fieldUse:bool
func _init(params:Dictionary) -> void: func _init(params:Dictionary) -> void:
super(params)
self.name = params.get("name", "Unknown Move") self.name = params.get("name", "Unknown Move")
self.power = params.get("power", 0) self.power = params.get("power", 0)
self.mpCost = params.get("mpCost", 0) self.mpCost = params.get("mpCost", 0)
self.speedModifier = params.get("speedModifier", 1.0)
self.accuracy = params.get("accuracy", 1.0) self.accuracy = params.get("accuracy", 1.0)
self.moveType = params.get("moveType", MoveType.PHYSICAL) self.moveType = params.get("moveType", MoveType.PHYSICAL)
self.fieldUse = params.get("fieldUse", false) self.fieldUse = params.get("fieldUse", false)
func start(_params:Dictionary) -> void:
# Implement move execution logic here
await UI.TEXTBOX.setTextAndWait("Action")
# Moves # Moves
static var MOVE_PUNCH = BattleMove.new({ static var MOVE_PUNCH = BattleMove.new({
"name": "Punch", "name": "Punch",

View File

@@ -1 +1 @@
uid://gb08hhwk0paw uid://bgq0u537pt5lm

View File

@@ -1,11 +1,10 @@
class_name ActionBox extends GridContainer class_name ActionBox extends GridContainer
const BattleMove = preload("res://battle/fighter/BattleMove.gd")
@export var btnAttack:Button @export var btnAttack:Button
@export var btnMagic:Button @export var btnMagic:Button
@export var btnItem:Button @export var btnItem:Button
signal decisionMade(move:BattleMove) signal decisionMade(move:BattleDecision)
func _ready() -> void: func _ready() -> void:
btnAttack.pressed.connect(onAttackPressed) btnAttack.pressed.connect(onAttackPressed)
@@ -15,11 +14,11 @@ func _ready() -> void:
func onAttackPressed() -> void: func onAttackPressed() -> void:
print("Attack button pressed") print("Attack button pressed")
decisionMade.emit(BattleMove.MOVE_PUNCH) decisionMade.emit(BattleDecision.new(BattleMove.MOVE_PUNCH, null, null))
func onMagicPressed() -> void: func onMagicPressed() -> void:
print("Magic button pressed") print("Magic button pressed")
decisionMade.emit(BattleMove.MOVE_FIRE1) decisionMade.emit(BattleDecision.new(BattleMove.MOVE_FIRE1, null, null))
func onItemPressed() -> void: func onItemPressed() -> void:
print("Item button pressed") print("Item button pressed")

View File

@@ -1,14 +1,62 @@
class_name BattleAction class_name BattleCutsceneAction
const BattleFighter = preload("res://battle/fighter/BattleFighter.gd")
static func playerDecisionCallable(_params:Dictionary) -> int: static func battleDecisionCallable(_params:Dictionary) -> int:
BATTLE.battleScene.actionBox.visible = true print("Battle action executed.")
var move = await BATTLE.battleScene.actionBox.decisionMade
BATTLE.battleScene.actionBox.visible = false
return Cutscene.CUTSCENE_CONTINUE return Cutscene.CUTSCENE_CONTINUE
static func getPlayerDecisionCallable(fighter:BattleFighter) -> Dictionary:
static func getBattleDecisionCallable(decision:BattleDecision) -> Dictionary:
return { return {
"function": playerDecisionCallable, "function": battleDecisionCallable,
"fighter": fighter "decision": decision
}
static func playerDecisionCallable(_params:Dictionary) -> int:
var decisions:Array[BattleDecision] = []
for fighter:BattleFighter in BATTLE.fighterMap.values():
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()
if decision == null:
continue
decisions.append(decision)
# Here I could do something like cutscene specific logic?
# Prioritize moves
decisions.shuffle()# Randomizes for moves that have equal chance of happening
decisions.sort_custom(func(a:BattleDecision, b:BattleDecision) -> int:
var priorityA = a.getPriority()
var priorityB = b.getPriority()
if priorityA > priorityB:
return -1
elif priorityA < priorityB:
return 1
else:
return 0
)
# Turn into cutscene actions.
for decision in decisions:
_params['cutscene'].addCallable(BattleCutsceneAction.getBattleDecisionCallable(decision))
return Cutscene.CUTSCENE_CONTINUE
static func getPlayerDecisionCallable() -> Dictionary:
return {
"function": playerDecisionCallable
} }

View File

@@ -7,17 +7,23 @@ class_name RootScene extends Node3D
@export var currentScene:Node = null @export var currentScene:Node = null
func _enter_tree() -> void: func _enter_tree() -> void:
SCENE.sceneChanged.connect(onSceneChange) # SCENE.sceneChanged.connect(onSceneChange)
SCENE.setScene(SceneSingleton.SceneType.INITIAL) # SCENE.setScene(SceneSingleton.SceneType.INITIAL)
# SCENE.setScene(SceneSingleton.SceneType.BATTLE) SCENE.setScene(SceneSingleton.SceneType.BATTLE)
# # Wait a frame # Wait a frame
# await get_tree().process_frame await get_tree().process_frame
# BATTLE.startBattle({
# 'fighters': { var testEnemy = BattleFighter.new({
# BATTLE.BattlePosition.RIGHT_TOP_FRONT: PartySingleton.PARTY_JOHN, 'controller': BattleFighter.FighterController.AI
# } })
# }) BATTLE.startBattle({
'fighters': {
# Test fighters
BATTLE.BattlePosition.RIGHT_MIDDLE_FRONT: PartySingleton.PARTY_JOHN,
BATTLE.BattlePosition.LEFT_MIDDLE_FRONT: testEnemy
}
})
func _exit_tree() -> void: func _exit_tree() -> void:
push_error("RootScene should not be removed from the scene tree. This is a bug.") push_error("RootScene should not be removed from the scene tree. This is a bug.")

View File

@@ -31,3 +31,4 @@ visible = false
[node name="CurrentScene" type="Node" parent="."] [node name="CurrentScene" type="Node" parent="."]
[node name="RootUI" parent="." instance=ExtResource("6_ajii0")] [node name="RootUI" parent="." instance=ExtResource("6_ajii0")]
visible = false