From c31a4740ae230a148b6953087ebc4180ce0b3d84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Tue, 18 Feb 2020 23:24:00 +0100 Subject: [PATCH] Added combat simulation display --- ai/dumb.gd | 2 + arenas/anchor.gd | 10 ++--- cards/base_card.gd | 23 +++++----- helpers/battle.gd | 37 ++++++++++++---- helpers/drag.gd | 2 +- screens/battle/battle.gd | 55 +++++++++++++++++------ screens/battle/battle.tscn | 25 ++++++++++- screens/battle/hand.gd | 15 +++---- units/base_unit.gd | 89 +++++++++++++++++++------------------- units/points.gd | 19 +++++++- 10 files changed, 181 insertions(+), 96 deletions(-) create mode 100644 ai/dumb.gd diff --git a/ai/dumb.gd b/ai/dumb.gd new file mode 100644 index 0000000..7cf80f3 --- /dev/null +++ b/ai/dumb.gd @@ -0,0 +1,2 @@ +static func play(arena, hero, hand): + hand.emit_turn_end() diff --git a/arenas/anchor.gd b/arenas/anchor.gd index 3e71c82..8370d9c 100644 --- a/arenas/anchor.gd +++ b/arenas/anchor.gd @@ -14,16 +14,16 @@ func _process(delta): modulate = Color(1, 0.8, 0.2) else: modulate = Color(1, 1, 1) - + func _ready(): add_to_group("anchors") - + func is_connected_to(other: Anchor) -> bool: for route in get_tree().get_nodes_in_group("routes"): if route is Route and route.is_connecting(self, other): return true return false - + func set_content(val): if val is Node: var old_parent = val.get_parent() @@ -35,12 +35,12 @@ func set_content(val): content = val.get_path() else: content = null - + func get_content(): if content: return get_node_or_null(content) else: return null - + func is_empty() -> bool: return not get_content() diff --git a/cards/base_card.gd b/cards/base_card.gd index 3652a37..504b0a5 100644 --- a/cards/base_card.gd +++ b/cards/base_card.gd @@ -1,7 +1,7 @@ tool extends Node2D -signal played(card, anchor) +signal played(card, anchor, unit) const AnimHelper = preload("res://helpers/anims.gd") const BattleHelper = preload("res://helpers/battle.gd") @@ -20,7 +20,7 @@ func _ready(): set_title(title) set_spawned_unit(spawned_unit) base_position = position - + func set_title(val): title = val if has_node("title"): @@ -43,10 +43,10 @@ func set_spawned_unit(val): $points.visible = true else: $points.visible = false - + func set_hero(val: Node): hero = val - + func can_be_used_on_anchor(anchor): if anchor is Anchor: if spawned_unit: @@ -55,22 +55,25 @@ func can_be_used_on_anchor(anchor): return true else: return false - + func set_hand_location(loc: Vector2): position = loc base_position = loc - + func update_anchors(): var position = global_position if dragged else null selected_anchor = BattleHelper.update_anchors(get_tree(), position, funcref(self, "can_be_used_on_anchor")) func play(anchor): + var unit + if spawned_unit: - var unit = spawned_unit.instance() + unit = spawned_unit.instance() unit.attacker = hero.attacker if hero else false anchor.set_content(unit) - emit_signal("played", self, anchor) - + + emit_signal("played", self, anchor, unit) + func return_to_base(): AnimHelper.linear_goto(self, base_position, 0.3) @@ -87,5 +90,5 @@ func on_dragged(active, relative, absolute): if relative: set_position(position + relative) - + update_anchors() diff --git a/helpers/battle.gd b/helpers/battle.gd index 21d6e32..e4649dd 100644 --- a/helpers/battle.gd +++ b/helpers/battle.gd @@ -13,12 +13,12 @@ static func update_anchors(tree: SceneTree, position, acceptability) -> Anchor: if distance < 150 and distance < selected_distance: selected_anchor = anchor selected_distance = distance - + for anchor in anchors: anchor.selected = (anchor == selected_anchor) - + return selected_anchor - + static func spawn_unit(name: String, anchor: Anchor, attacker: bool) -> Node: """ Spawn a unit on an empty anchor """ @@ -27,7 +27,7 @@ static func spawn_unit(name: String, anchor: Anchor, attacker: bool) -> Node: anchor.set_content(node) node.attacker = attacker return node - + static func list_units(tree: SceneTree): """ List all units in battle """ @@ -37,21 +37,40 @@ static func list_units(tree: SceneTree): var content = anchor.get_content() if content: units.append(content) - + return units - + +static func apply_combat(attacker: UnitPoints, defender: UnitPoints): + """ Apply a unit combat to points + """ + while attacker.damage: + attacker.damage -= 1 + + if defender.shield: + defender.shield -= 1 + elif defender.hull: + defender.hull -= 1 + + while defender.damage: + defender.damage -= 1 + + if attacker.shield: + attacker.shield -= 1 + elif attacker.hull: + attacker.hull -= 1 + static func get_state(tree: SceneTree): """ Build the simple state associated with current battle scene """ var nodes = [] var routes = [] - + for anchor in tree.get_nodes_in_group("anchors"): pass - + for route in tree.get_nodes_in_group("routes"): pass - + return { "nodes": nodes, "routes": routes diff --git a/helpers/drag.gd b/helpers/drag.gd index d8983e3..bdb61ed 100644 --- a/helpers/drag.gd +++ b/helpers/drag.gd @@ -5,7 +5,7 @@ signal dragged(active, relative, absolute) func _gui_input(event): if event is InputEventMouseButton and event.button_index == 1: emit_signal("dragged", event.pressed, Vector2(), event.global_position) - + if event is InputEventMouseMotion: if event.button_mask == BUTTON_LEFT: emit_signal("dragged", true, event.relative, event.global_position) diff --git a/screens/battle/battle.gd b/screens/battle/battle.gd index 6d80522..cdb3f96 100644 --- a/screens/battle/battle.gd +++ b/screens/battle/battle.gd @@ -4,6 +4,7 @@ const BattleHelper = preload("res://helpers/battle.gd") export(PackedScene) var deck_attack export(PackedScene) var deck_defend +export(Script) var defend_ai onready var deck_attack_cards = deck_attack.instance() onready var deck_defend_cards = deck_defend.instance() @@ -14,30 +15,32 @@ var turn_attack = true func _ready(): hero_attack = create_hero_unit("tomahawk", "attack_start", true) hero_defend = create_hero_unit("rhino", "defend_start", false) - - fill_hand($hand_attack, deck_attack_cards, hero_attack) - fill_hand($hand_defend, deck_defend_cards, hero_defend) - + $hand_attack.connect("turn_end", self, "end_turn") $hand_defend.connect("turn_end", self, "end_turn") - + + $combat_info.visible = false + + fill_hands() adjust_playable() - + #print(BattleHelper.get_state(get_tree())) - + func find_free_anchor(anchor_type: String): for anchor in get_tree().get_nodes_in_group("anchors"): if anchor.anchor_type == anchor_type and anchor.is_empty(): return anchor return null - + func create_hero_unit(name: String, anchor_type: String, attacker: bool): var anchor = find_free_anchor(anchor_type) if anchor: - return BattleHelper.spawn_unit("heroes/" + name, anchor, attacker) + var unit = BattleHelper.spawn_unit("heroes/" + name, anchor, attacker) + unit.connect("combat_simulation", self, "_on_combat_simulation") + return unit else: return null - + func fill_hand(hand: Node, deck: Node, hero: Node, limit=4): while hand.get_card_count() < 4: var count = deck.get_child_count() @@ -47,13 +50,37 @@ func fill_hand(hand: Node, deck: Node, hero: Node, limit=4): hand.add_card(card) hand.rearrange() - + +func fill_hands(): + fill_hand($hand_attack, deck_attack_cards, hero_attack) + fill_hand($hand_defend, deck_defend_cards, hero_defend) + func end_turn(): turn_attack = not turn_attack - adjust_playable() - + fill_hands() + adjust_playable() + for unit in BattleHelper.list_units(get_tree()): + unit.turn_ended() + +func _process(delta): + if not turn_attack and defend_ai: + defend_ai.play($arena, hero_defend, $hand_defend) + func adjust_playable(): $hand_attack.visible = turn_attack - for unit in BattleHelper.list_units(get_tree()): unit.playable = unit.attacker == turn_attack + +func _on_unit_created(unit): + unit.connect("combat_simulation", self, "_on_combat_simulation") + +func _on_combat_simulation(attacker, defender): + if attacker and defender: + $combat_info/attacker_points.set_from(attacker.get_points()) + $combat_info/defender_points.set_from(defender.get_points()) + BattleHelper.apply_combat($combat_info/attacker_points, $combat_info/defender_points) + $combat_info.visible = true + $hand_attack.visible = false + else: + $combat_info.visible = false + $hand_attack.visible = turn_attack diff --git a/screens/battle/battle.tscn b/screens/battle/battle.tscn index dd87865..d7205e6 100644 --- a/screens/battle/battle.tscn +++ b/screens/battle/battle.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=8 format=2] +[gd_scene load_steps=10 format=2] [ext_resource path="res://screens/battle/mechanolith.ogg" type="AudioStream" id=1] [ext_resource path="res://theme/ui.tres" type="Theme" id=2] @@ -6,6 +6,8 @@ [ext_resource path="res://screens/battle/background.jpg" type="Texture" id=4] [ext_resource path="res://screens/battle/battle.gd" type="Script" id=5] [ext_resource path="res://screens/battle/hand.gd" type="Script" id=6] +[ext_resource path="res://units/points.tscn" type="PackedScene" id=7] +[ext_resource path="res://ai/dumb.gd" type="Script" id=8] [ext_resource path="res://decks/all.tscn" type="PackedScene" id=9] [node name="view" type="Panel"] @@ -14,10 +16,12 @@ anchor_bottom = 1.0 theme = ExtResource( 2 ) script = ExtResource( 5 ) __meta__ = { +"_edit_lock_": true, "_edit_use_anchors_": false } deck_attack = ExtResource( 9 ) deck_defend = ExtResource( 9 ) +defend_ai = ExtResource( 8 ) [node name="background" type="Sprite" parent="."] position = Vector2( 540, 970 ) @@ -27,7 +31,7 @@ __meta__ = { "_edit_lock_": true } -[node name="arena_normal1" parent="." instance=ExtResource( 3 )] +[node name="arena" parent="." instance=ExtResource( 3 )] [node name="bgm" type="AudioStreamPlayer" parent="."] stream = ExtResource( 1 ) @@ -69,3 +73,20 @@ script = ExtResource( 6 ) __meta__ = { "_edit_use_anchors_": false } + +[node name="combat_info" type="Control" parent="."] +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +__meta__ = { +"_edit_use_anchors_": false +} + +[node name="attacker_points" parent="combat_info" instance=ExtResource( 7 )] +position = Vector2( 260, -167.5 ) + +[node name="defender_points" parent="combat_info" instance=ExtResource( 7 )] +position = Vector2( 840, -167.5 ) +[connection signal="unit_created" from="hand_attack" to="." method="_on_unit_created"] +[connection signal="pressed" from="hand_attack/skip" to="hand_attack" method="emit_turn_end"] +[connection signal="unit_created" from="hand_defend" to="." method="_on_unit_created"] diff --git a/screens/battle/hand.gd b/screens/battle/hand.gd index cba9bc1..9e7b65f 100644 --- a/screens/battle/hand.gd +++ b/screens/battle/hand.gd @@ -2,22 +2,19 @@ extends Control const NodesHelpers = preload("res://helpers/nodes.gd") +signal unit_created(unit) signal turn_end export var attack = false var cards = [] -func _ready(): - if has_node("skip"): - $skip.connect("pressed", self, "emit_turn_end") - func add_card(card): """ Add a card to the hand """ cards.append(card) NodesHelpers.set_parent(card, self) card.connect("played", self, "on_card_played") - + func get_card_count(): """ Get the number of cards in hand """ @@ -30,14 +27,16 @@ func rearrange(): for card in cards: card.set_hand_location(Vector2(i * 210, 0)) i += 1 - -func on_card_played(card, anchor): + +func on_card_played(card, anchor, unit): """ Called when a card is played """ card.queue_free() cards.erase(card) rearrange() + if unit: + emit_signal("unit_created", unit) emit_turn_end() - + func emit_turn_end(): emit_signal("turn_end") diff --git a/units/base_unit.gd b/units/base_unit.gd index 81eead3..c7df612 100644 --- a/units/base_unit.gd +++ b/units/base_unit.gd @@ -3,6 +3,8 @@ extends Node2D class_name BaseUnit +signal combat_simulation(attacker, defender) + const AnimHelper = preload("res://helpers/anims.gd") const BattleHelper = preload("res://helpers/battle.gd") @@ -19,29 +21,26 @@ var dragged func _ready(): $move_hint.visible = false - + set_sprite(sprite) - + set_base_damage_points(base_damage_points) set_base_move_points(base_move_points) set_base_hull_points(base_hull_points) set_base_shield_points(base_shield_points) set_attacker(attacker) set_playable(playable) - + func destroy(): if get_parent() is Anchor: (get_parent() as Anchor).set_content(null) - + queue_free() - + func set_attacker(val): attacker = val if has_node("points"): - if attacker: - $points.modulate = Color(1.0, 1.0, 1.0) - else: - $points.modulate = Color(1.0, 0.3, 0.3) + $points.enemy = not attacker func set_playable(val): playable = val @@ -49,7 +48,7 @@ func set_playable(val): modulate = Color(1.0, 1.0, 1.0) else: modulate = Color(0.5, 0.5, 0.5) - + func set_sprite(val): sprite = val if has_node("sprite"): @@ -80,23 +79,34 @@ func can_target(other): return false else: return other.attacker != attacker - + func can_be_used_on_anchor(anchor): return playable and anchor is Anchor and anchor.is_connected_to(get_parent()) and (anchor.is_empty() or can_target(anchor.get_content())) - + func update_anchors(): selected_anchor = BattleHelper.update_anchors(get_tree(), dragged, funcref(self, "can_be_used_on_anchor")) func play(anchor): - if playable: + if playable and anchor: if anchor.is_empty(): move_to(anchor) else: var other = anchor.get_content() if other.has_method("get_points"): attack(other) - return_to_base() - + + reset_state() + +func simulate(anchor): + if playable and anchor and not anchor.is_empty(): + var other = anchor.get_content() + if other.has_method("get_points"): + emit_signal("combat_simulation", self, other) + else: + emit_signal("combat_simulation", null, null) + else: + emit_signal("combat_simulation", null, null) + func move_to(anchor): if $points.move: $points.move -= 1 @@ -105,54 +115,43 @@ func move_to(anchor): set_position(old_position - anchor.position) $sprite.rotation = atan2(-position.y, -position.x) AnimHelper.linear_goto(self, Vector2()) - + func get_points() -> UnitPoints: if $points is UnitPoints: return $points as UnitPoints else: return null - -func attack(other) -> bool: + +func attack(other): var selfpoints = get_points() var otherpoints = other.get_points() - - while selfpoints.damage: - selfpoints.damage -= 1 - - if otherpoints.shield: - otherpoints.shield -= 1 - elif otherpoints.hull: - otherpoints.hull -= 1 - while otherpoints.damage: - otherpoints.damage -= 1 - - if selfpoints.shield: - selfpoints.shield -= 1 - elif selfpoints.hull: - selfpoints.hull -= 1 - + BattleHelper.apply_combat(selfpoints, otherpoints) + if selfpoints.hull <= 0: destroy() if otherpoints.hull <= 0: other.destroy() - - return selfpoints.hull > 0 && otherpoints.hull == 0 - -func return_to_base(): + +func reset_state(): dragged = null $move_hint.visible = false $move_hint.set_point_position(1, Vector2(0, 0)) +func turn_ended(): + $points.move = base_move_points + $points.damage = base_damage_points + $points.shield = base_shield_points + func on_dragged(active, relative, absolute): if not playable: return - if dragged and not active: - if selected_anchor: - play(selected_anchor) - else: - return_to_base() + if active: + simulate(selected_anchor) + elif dragged: + simulate(null) + play(selected_anchor) $move_hint.visible = active @@ -160,8 +159,8 @@ func on_dragged(active, relative, absolute): dragged = absolute else: dragged = null - + update_anchors() - + if dragged: $move_hint.set_point_position(1, (selected_anchor.global_position if selected_anchor else dragged) - global_position) diff --git a/units/points.gd b/units/points.gd index 7540fe0..55ff0df 100644 --- a/units/points.gd +++ b/units/points.gd @@ -7,12 +7,14 @@ export var move = 0 setget set_move export var hull = 0 setget set_hull export var shield = 0 setget set_shield export var damage = 0 setget set_damage +export var enemy = false setget set_enemy func _ready(): set_move(move) set_hull(hull) set_shield(shield) set_damage(damage) + set_enemy(enemy) func set_move(val): move = val @@ -23,14 +25,27 @@ func set_hull(val): hull = val if has_node("hud/hull"): $hud/hull.text = String(val) - + func set_shield(val): shield = val if has_node("hud/shield"): $hud/shield.text = String(val) - + func set_damage(val): damage = val if has_node("hud/damage"): $hud/damage.text = String(val) +func set_enemy(val): + enemy = val + if enemy: + modulate = Color(1.0, 0.3, 0.3) + else: + modulate = Color(1.0, 1.0, 1.0) + +func set_from(other): + set_move(other.move) + set_hull(other.hull) + set_shield(other.shield) + set_damage(other.damage) + set_enemy(other.enemy)