diff --git a/battle/BattleScene.gd b/battle/BattleScene.gd index 96d759f..5347b35 100644 --- a/battle/BattleScene.gd +++ b/battle/BattleScene.gd @@ -1,8 +1,8 @@ class_name BattleScene extends Node3D @export var actionBox:ActionBox = null -@export var battleCursor:BattleCursor = null @export var battleFighterScenes:Node +@export var cursorScenes:Node signal neverEmitted @@ -47,16 +47,68 @@ func getBattleDecisions(fighters:Array[BattleFighter]) -> Array[BattleDecision]: return decisions func _getTargets(fighter:BattleFighter, move:BattleAction) -> Array[BattleFighter]: - print("Determining target") + # Determine possible targets and whether to blink or not + var possiblePositions:Array[BattleSingleton.BattlePosition] = [ + BattleSingleton.BattlePosition.RIGHT_TOP_FRONT, + BattleSingleton.BattlePosition.RIGHT_MIDDLE_FRONT, + BattleSingleton.BattlePosition.RIGHT_BOTTOM_FRONT + ] - battleCursor.visible = true - var positions:Array[BattleSingleton.BattlePosition] = [] - for pos in BATTLE.BattlePosition.values(): - positions.append(pos) - battleCursor.setCursors(self, positions) + # Should not be possible to have no possible targets here + assert(possiblePositions.size() > 0) - await neverEmitted - return [] + var activePositions:Array[BattleSingleton.BattlePosition] = [] + var selectedPositions:Array[BattleSingleton.BattlePosition] = [] + + # If there's only one possible target, auto-select it + if possiblePositions.size() == 1: + selectedPositions.append(possiblePositions[0]) + + # Here we wait for a selection to be confirmed. + while selectedPositions.size() == 0: + var blink = activePositions.size() > 1 + + activePositions.clear() + # activePositions.append(possiblePositions[0]) # For now, just single target + + # Sake of testing just showing all blinking + blink = true + for pos in possiblePositions: + activePositions.append(pos) + + # 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 + + # 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 getFighterSceneAtPosition(pos:BattleSingleton.BattlePosition) -> BattleFighterScene: for fighterScene in battleFighterScenes.get_children(): @@ -65,3 +117,11 @@ func getFighterSceneAtPosition(pos:BattleSingleton.BattlePosition) -> BattleFigh 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 diff --git a/battle/BattleScene.tscn b/battle/BattleScene.tscn index 7d17a5e..f90ce63 100644 --- a/battle/BattleScene.tscn +++ b/battle/BattleScene.tscn @@ -2,14 +2,14 @@ [ext_resource type="PackedScene" uid="uid://d1xyb0hdf1yeh" path="res://battle/fighter/BattleFighterScene.tscn" id="1_abr1f"] [ext_resource type="Script" uid="uid://dihfp05x6pktn" path="res://battle/BattleScene.gd" id="1_acaen"] -[ext_resource type="PackedScene" uid="uid://ktmvnapibv2q" path="res://battle/ui/ActionBox.tscn" id="2_c3ndu"] -[ext_resource type="PackedScene" uid="uid://c4knowtushjly" path="res://battle/ui/BattleCursor.tscn" id="3_7s6t6"] +[ext_resource type="PackedScene" uid="uid://ktmvnapibv2q" path="res://battle/ui/action/ActionBox.tscn" id="2_c3ndu"] +[ext_resource type="PackedScene" uid="uid://br2rs8skcn72l" path="res://battle/ui/action/BattleCursorIcon.tscn" id="3_7s6t6"] -[node name="BattleScene" type="Node3D" node_paths=PackedStringArray("actionBox", "battleCursor", "battleFighterScenes")] +[node name="BattleScene" type="Node3D" node_paths=PackedStringArray("actionBox", "battleFighterScenes", "cursorScenes")] script = ExtResource("1_acaen") actionBox = NodePath("UI/ActionBox") -battleCursor = NodePath("UI/BattleCursor") battleFighterScenes = NodePath("Fighters") +cursorScenes = NodePath("UI/Cursors") metadata/_custom_type_script = "uid://dihfp05x6pktn" [node name="UI" type="Control" parent="."] @@ -29,9 +29,189 @@ offset_right = 40.0 offset_bottom = 12.0 text = "Battle" -[node name="BattleCursor" parent="UI" instance=ExtResource("3_7s6t6")] +[node name="Cursors" type="Control" parent="UI"] +anchors_preset = 0 +offset_right = 40.0 +offset_bottom = 40.0 + +[node name="LeftTopBack" parent="UI/Cursors" instance=ExtResource("3_7s6t6")] layout_mode = 0 anchors_preset = 0 +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +offset_left = 0.0 +offset_top = 0.0 +offset_right = 8.0 +offset_bottom = 8.0 +grow_horizontal = 1 +grow_vertical = 1 + +[node name="LeftTopFront" parent="UI/Cursors" instance=ExtResource("3_7s6t6")] +layout_mode = 0 +anchors_preset = 0 +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +offset_left = 0.0 +offset_top = 0.0 +offset_right = 8.0 +offset_bottom = 8.0 +grow_horizontal = 1 +grow_vertical = 1 +battlePosition = 1 + +[node name="LeftMiddleBack" parent="UI/Cursors" instance=ExtResource("3_7s6t6")] +layout_mode = 0 +anchors_preset = 0 +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +offset_left = 0.0 +offset_top = 0.0 +offset_right = 8.0 +offset_bottom = 8.0 +grow_horizontal = 1 +grow_vertical = 1 +battlePosition = 2 + +[node name="LeftMiddleFront" parent="UI/Cursors" instance=ExtResource("3_7s6t6")] +layout_mode = 0 +anchors_preset = 0 +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +offset_left = 0.0 +offset_top = 0.0 +offset_right = 8.0 +offset_bottom = 8.0 +grow_horizontal = 1 +grow_vertical = 1 +battlePosition = 3 + +[node name="LeftBottomBack" parent="UI/Cursors" instance=ExtResource("3_7s6t6")] +layout_mode = 0 +anchors_preset = 0 +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +offset_left = 0.0 +offset_top = 0.0 +offset_right = 8.0 +offset_bottom = 8.0 +grow_horizontal = 1 +grow_vertical = 1 +battlePosition = 4 + +[node name="LeftBottomFront" parent="UI/Cursors" instance=ExtResource("3_7s6t6")] +layout_mode = 0 +anchors_preset = 0 +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +offset_left = 0.0 +offset_top = 0.0 +offset_right = 8.0 +offset_bottom = 8.0 +grow_horizontal = 1 +grow_vertical = 1 +battlePosition = 5 + +[node name="RightTopFront" parent="UI/Cursors" instance=ExtResource("3_7s6t6")] +layout_mode = 0 +anchors_preset = 0 +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +offset_left = 0.0 +offset_top = 0.0 +offset_right = 8.0 +offset_bottom = 8.0 +grow_horizontal = 1 +grow_vertical = 1 +battlePosition = 7 + +[node name="RightTopBack" parent="UI/Cursors" instance=ExtResource("3_7s6t6")] +layout_mode = 0 +anchors_preset = 0 +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +offset_left = 0.0 +offset_top = 0.0 +offset_right = 8.0 +offset_bottom = 8.0 +grow_horizontal = 1 +grow_vertical = 1 +battlePosition = 6 + +[node name="RightMiddleFront" parent="UI/Cursors" instance=ExtResource("3_7s6t6")] +layout_mode = 0 +anchors_preset = 0 +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +offset_left = 0.0 +offset_top = 0.0 +offset_right = 8.0 +offset_bottom = 8.0 +grow_horizontal = 1 +grow_vertical = 1 +battlePosition = 9 + +[node name="RightMiddleBack" parent="UI/Cursors" instance=ExtResource("3_7s6t6")] +layout_mode = 0 +anchors_preset = 0 +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +offset_left = 0.0 +offset_top = 0.0 +offset_right = 8.0 +offset_bottom = 8.0 +grow_horizontal = 1 +grow_vertical = 1 +battlePosition = 8 + +[node name="RightBottomFront" parent="UI/Cursors" instance=ExtResource("3_7s6t6")] +layout_mode = 0 +anchors_preset = 0 +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +offset_left = 0.0 +offset_top = 0.0 +offset_right = 8.0 +offset_bottom = 8.0 +grow_horizontal = 1 +grow_vertical = 1 +battlePosition = 11 + +[node name="RightBottomBack" parent="UI/Cursors" instance=ExtResource("3_7s6t6")] +layout_mode = 0 +anchors_preset = 0 +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +offset_left = 0.0 +offset_top = 0.0 +offset_right = 8.0 +offset_bottom = 8.0 +grow_horizontal = 1 +grow_vertical = 1 +battlePosition = 10 [node name="Fighters" type="Node" parent="."] diff --git a/battle/ui/BattleCursor.gd b/battle/ui/BattleCursor.gd deleted file mode 100644 index bed2bba..0000000 --- a/battle/ui/BattleCursor.gd +++ /dev/null @@ -1,23 +0,0 @@ -class_name BattleCursor extends Control - -@export var cursor:PackedScene - -func setCursors(battleScene:BattleScene, targets:Array[BattleSingleton.BattlePosition]) -> void: - clearCursors() - - for target in targets: - var fighterScene = battleScene.getFighterSceneAtPosition(target) - if fighterScene == null: - continue - - var cursorInstance = cursor.instantiate() - add_child(cursorInstance) - - # Convert world position to screen space - var screenPos = battleScene.get_viewport().get_camera_3d().unproject_position(fighterScene.global_transform.origin) - cursorInstance.position = screenPos - -func clearCursors() -> void: - # Clear all children (cursors). - for child in get_children(): - child.queue_free() \ No newline at end of file diff --git a/battle/ui/BattleCursor.gd.uid b/battle/ui/BattleCursor.gd.uid deleted file mode 100644 index e3ed227..0000000 --- a/battle/ui/BattleCursor.gd.uid +++ /dev/null @@ -1 +0,0 @@ -uid://cwnf5vqn3nb57 diff --git a/battle/ui/BattleCursor.tscn b/battle/ui/BattleCursor.tscn deleted file mode 100644 index 09ae5b0..0000000 --- a/battle/ui/BattleCursor.tscn +++ /dev/null @@ -1,11 +0,0 @@ -[gd_scene load_steps=3 format=3 uid="uid://c4knowtushjly"] - -[ext_resource type="Script" uid="uid://cwnf5vqn3nb57" path="res://battle/ui/BattleCursor.gd" id="1_wsiei"] -[ext_resource type="PackedScene" uid="uid://br2rs8skcn72l" path="res://battle/ui/BattleCursorCursor.tscn" id="2_6yv4v"] - -[node name="BattleCursor" type="Control"] -layout_mode = 3 -size_flags_horizontal = 0 -size_flags_vertical = 0 -script = ExtResource("1_wsiei") -cursor = ExtResource("2_6yv4v") diff --git a/battle/ui/ActionBox.gd b/battle/ui/action/ActionBox.gd similarity index 93% rename from battle/ui/ActionBox.gd rename to battle/ui/action/ActionBox.gd index 8d0de42..2447eb9 100644 --- a/battle/ui/ActionBox.gd +++ b/battle/ui/action/ActionBox.gd @@ -33,8 +33,6 @@ func getAction(fighter:BattleFighter) -> Action: btnAttack.disabled = fighter.movePrimary == null || !fighter.movePrimary.canFighterUse(fighter) btnMagic.disabled = fighter.movesMagical.size() == 0 btnItem.disabled = false # TODO: check if items available? - - # TODO: Update cursor position to first enabled button var act = await action return act diff --git a/battle/ui/ActionBox.gd.uid b/battle/ui/action/ActionBox.gd.uid similarity index 100% rename from battle/ui/ActionBox.gd.uid rename to battle/ui/action/ActionBox.gd.uid diff --git a/battle/ui/ActionBox.tscn b/battle/ui/action/ActionBox.tscn similarity index 95% rename from battle/ui/ActionBox.tscn rename to battle/ui/action/ActionBox.tscn index 472547f..d3104f4 100644 --- a/battle/ui/ActionBox.tscn +++ b/battle/ui/action/ActionBox.tscn @@ -1,6 +1,6 @@ [gd_scene load_steps=2 format=3 uid="uid://ktmvnapibv2q"] -[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/action/ActionBox.gd" id="1_w5s71"] [node name="ActionBox" type="GridContainer" node_paths=PackedStringArray("btnAttack", "btnMagic", "btnItem", "btnBack")] anchors_preset = 3 diff --git a/battle/ui/action/BattleCursorIcon.gd b/battle/ui/action/BattleCursorIcon.gd new file mode 100644 index 0000000..2f581bd --- /dev/null +++ b/battle/ui/action/BattleCursorIcon.gd @@ -0,0 +1,56 @@ +class_name BattleCursorIcon extends ColorRect + +# How often to blink in milliseconds. +const BLINK_RATE := 100 + +enum Mode { + INACTIVE, + ACTIVE, + BLINKING +} + +@export var battlePosition:BattleSingleton.BattlePosition = BattleSingleton.BattlePosition.LEFT_TOP_BACK: + get(): + return battlePosition + + set(value): + battlePosition = value + _updatePosition() + +@export var mode:Mode = Mode.INACTIVE + +func _ready() -> void: + _updatePosition() + +func _process(delta: float) -> void: + if mode == Mode.INACTIVE: + self.visible = false + return + elif mode == Mode.ACTIVE: + self.visible = true + elif mode == Mode.BLINKING: + self.visible = Time.get_ticks_msec() % (BLINK_RATE * 2) < BLINK_RATE + +func _updatePosition() -> void: + # Walk up the tree until BattleScene is found. + var battleScene:BattleScene = null + var currentNode:Node = self + while currentNode != null: + if currentNode is BattleScene: + battleScene = currentNode + break + currentNode = currentNode.get_parent() + + if battleScene == null: + push_error("BattleCursorIcon could not find BattleScene in parent nodes.") + return + + var targetFighter:BattleFighterScene = battleScene.getFighterSceneAtPosition(battlePosition) + if targetFighter == null: + push_error("No BattleFighter found at battlePosition %s" % str(battlePosition)) + return + + # Convert the target fighter's global position to screen space position. + var viewport:Viewport = get_viewport() + var screenPos:Vector2 = viewport.get_camera_3d().unproject_position(targetFighter.global_transform.origin) + self.position = screenPos diff --git a/battle/ui/action/BattleCursorIcon.gd.uid b/battle/ui/action/BattleCursorIcon.gd.uid new file mode 100644 index 0000000..c82ec07 --- /dev/null +++ b/battle/ui/action/BattleCursorIcon.gd.uid @@ -0,0 +1 @@ +uid://c2lye3hasxnkj diff --git a/battle/ui/BattleCursorCursor.tscn b/battle/ui/action/BattleCursorIcon.tscn similarity index 53% rename from battle/ui/BattleCursorCursor.tscn rename to battle/ui/action/BattleCursorIcon.tscn index 79e1b7d..36ea121 100644 --- a/battle/ui/BattleCursorCursor.tscn +++ b/battle/ui/action/BattleCursorIcon.tscn @@ -1,7 +1,9 @@ -[gd_scene format=3 uid="uid://br2rs8skcn72l"] +[gd_scene load_steps=2 format=3 uid="uid://br2rs8skcn72l"] + +[ext_resource type="Script" uid="uid://c2lye3hasxnkj" path="res://battle/ui/action/BattleCursorIcon.gd" id="1_cpfvx"] [node name="Control" type="ColorRect"] -anchors_preset = 8 +anchors_preset = -1 anchor_left = 0.5 anchor_top = 0.5 anchor_right = 0.5 @@ -13,3 +15,4 @@ offset_bottom = 4.0 grow_horizontal = 2 grow_vertical = 2 color = Color(1, 0.478431, 1, 1) +script = ExtResource("1_cpfvx")