1
0
Fork 0

Added combat simulation display

This commit is contained in:
Michaël Lemaire 2020-02-18 23:24:00 +01:00
parent db3ec0aa67
commit c31a4740ae
10 changed files with 181 additions and 96 deletions

2
ai/dumb.gd Normal file
View file

@ -0,0 +1,2 @@
static func play(arena, hero, hand):
hand.emit_turn_end()

View file

@ -14,16 +14,16 @@ func _process(delta):
modulate = Color(1, 0.8, 0.2) modulate = Color(1, 0.8, 0.2)
else: else:
modulate = Color(1, 1, 1) modulate = Color(1, 1, 1)
func _ready(): func _ready():
add_to_group("anchors") add_to_group("anchors")
func is_connected_to(other: Anchor) -> bool: func is_connected_to(other: Anchor) -> bool:
for route in get_tree().get_nodes_in_group("routes"): for route in get_tree().get_nodes_in_group("routes"):
if route is Route and route.is_connecting(self, other): if route is Route and route.is_connecting(self, other):
return true return true
return false return false
func set_content(val): func set_content(val):
if val is Node: if val is Node:
var old_parent = val.get_parent() var old_parent = val.get_parent()
@ -35,12 +35,12 @@ func set_content(val):
content = val.get_path() content = val.get_path()
else: else:
content = null content = null
func get_content(): func get_content():
if content: if content:
return get_node_or_null(content) return get_node_or_null(content)
else: else:
return null return null
func is_empty() -> bool: func is_empty() -> bool:
return not get_content() return not get_content()

View file

@ -1,7 +1,7 @@
tool tool
extends Node2D extends Node2D
signal played(card, anchor) signal played(card, anchor, unit)
const AnimHelper = preload("res://helpers/anims.gd") const AnimHelper = preload("res://helpers/anims.gd")
const BattleHelper = preload("res://helpers/battle.gd") const BattleHelper = preload("res://helpers/battle.gd")
@ -20,7 +20,7 @@ func _ready():
set_title(title) set_title(title)
set_spawned_unit(spawned_unit) set_spawned_unit(spawned_unit)
base_position = position base_position = position
func set_title(val): func set_title(val):
title = val title = val
if has_node("title"): if has_node("title"):
@ -43,10 +43,10 @@ func set_spawned_unit(val):
$points.visible = true $points.visible = true
else: else:
$points.visible = false $points.visible = false
func set_hero(val: Node): func set_hero(val: Node):
hero = val hero = val
func can_be_used_on_anchor(anchor): func can_be_used_on_anchor(anchor):
if anchor is Anchor: if anchor is Anchor:
if spawned_unit: if spawned_unit:
@ -55,22 +55,25 @@ func can_be_used_on_anchor(anchor):
return true return true
else: else:
return false return false
func set_hand_location(loc: Vector2): func set_hand_location(loc: Vector2):
position = loc position = loc
base_position = loc base_position = loc
func update_anchors(): func update_anchors():
var position = global_position if dragged else null var position = global_position if dragged else null
selected_anchor = BattleHelper.update_anchors(get_tree(), position, funcref(self, "can_be_used_on_anchor")) selected_anchor = BattleHelper.update_anchors(get_tree(), position, funcref(self, "can_be_used_on_anchor"))
func play(anchor): func play(anchor):
var unit
if spawned_unit: if spawned_unit:
var unit = spawned_unit.instance() unit = spawned_unit.instance()
unit.attacker = hero.attacker if hero else false unit.attacker = hero.attacker if hero else false
anchor.set_content(unit) anchor.set_content(unit)
emit_signal("played", self, anchor)
emit_signal("played", self, anchor, unit)
func return_to_base(): func return_to_base():
AnimHelper.linear_goto(self, base_position, 0.3) AnimHelper.linear_goto(self, base_position, 0.3)
@ -87,5 +90,5 @@ func on_dragged(active, relative, absolute):
if relative: if relative:
set_position(position + relative) set_position(position + relative)
update_anchors() update_anchors()

View file

@ -13,12 +13,12 @@ static func update_anchors(tree: SceneTree, position, acceptability) -> Anchor:
if distance < 150 and distance < selected_distance: if distance < 150 and distance < selected_distance:
selected_anchor = anchor selected_anchor = anchor
selected_distance = distance selected_distance = distance
for anchor in anchors: for anchor in anchors:
anchor.selected = (anchor == selected_anchor) anchor.selected = (anchor == selected_anchor)
return selected_anchor return selected_anchor
static func spawn_unit(name: String, anchor: Anchor, attacker: bool) -> Node: static func spawn_unit(name: String, anchor: Anchor, attacker: bool) -> Node:
""" Spawn a unit on an empty anchor """ 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) anchor.set_content(node)
node.attacker = attacker node.attacker = attacker
return node return node
static func list_units(tree: SceneTree): static func list_units(tree: SceneTree):
""" List all units in battle """ List all units in battle
""" """
@ -37,21 +37,40 @@ static func list_units(tree: SceneTree):
var content = anchor.get_content() var content = anchor.get_content()
if content: if content:
units.append(content) units.append(content)
return units 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): static func get_state(tree: SceneTree):
""" Build the simple state associated with current battle scene """ Build the simple state associated with current battle scene
""" """
var nodes = [] var nodes = []
var routes = [] var routes = []
for anchor in tree.get_nodes_in_group("anchors"): for anchor in tree.get_nodes_in_group("anchors"):
pass pass
for route in tree.get_nodes_in_group("routes"): for route in tree.get_nodes_in_group("routes"):
pass pass
return { return {
"nodes": nodes, "nodes": nodes,
"routes": routes "routes": routes

View file

@ -5,7 +5,7 @@ signal dragged(active, relative, absolute)
func _gui_input(event): func _gui_input(event):
if event is InputEventMouseButton and event.button_index == 1: if event is InputEventMouseButton and event.button_index == 1:
emit_signal("dragged", event.pressed, Vector2(), event.global_position) emit_signal("dragged", event.pressed, Vector2(), event.global_position)
if event is InputEventMouseMotion: if event is InputEventMouseMotion:
if event.button_mask == BUTTON_LEFT: if event.button_mask == BUTTON_LEFT:
emit_signal("dragged", true, event.relative, event.global_position) emit_signal("dragged", true, event.relative, event.global_position)

View file

@ -4,6 +4,7 @@ const BattleHelper = preload("res://helpers/battle.gd")
export(PackedScene) var deck_attack export(PackedScene) var deck_attack
export(PackedScene) var deck_defend export(PackedScene) var deck_defend
export(Script) var defend_ai
onready var deck_attack_cards = deck_attack.instance() onready var deck_attack_cards = deck_attack.instance()
onready var deck_defend_cards = deck_defend.instance() onready var deck_defend_cards = deck_defend.instance()
@ -14,30 +15,32 @@ var turn_attack = true
func _ready(): func _ready():
hero_attack = create_hero_unit("tomahawk", "attack_start", true) hero_attack = create_hero_unit("tomahawk", "attack_start", true)
hero_defend = create_hero_unit("rhino", "defend_start", false) 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_attack.connect("turn_end", self, "end_turn")
$hand_defend.connect("turn_end", self, "end_turn") $hand_defend.connect("turn_end", self, "end_turn")
$combat_info.visible = false
fill_hands()
adjust_playable() adjust_playable()
#print(BattleHelper.get_state(get_tree())) #print(BattleHelper.get_state(get_tree()))
func find_free_anchor(anchor_type: String): func find_free_anchor(anchor_type: String):
for anchor in get_tree().get_nodes_in_group("anchors"): for anchor in get_tree().get_nodes_in_group("anchors"):
if anchor.anchor_type == anchor_type and anchor.is_empty(): if anchor.anchor_type == anchor_type and anchor.is_empty():
return anchor return anchor
return null return null
func create_hero_unit(name: String, anchor_type: String, attacker: bool): func create_hero_unit(name: String, anchor_type: String, attacker: bool):
var anchor = find_free_anchor(anchor_type) var anchor = find_free_anchor(anchor_type)
if anchor: 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: else:
return null return null
func fill_hand(hand: Node, deck: Node, hero: Node, limit=4): func fill_hand(hand: Node, deck: Node, hero: Node, limit=4):
while hand.get_card_count() < 4: while hand.get_card_count() < 4:
var count = deck.get_child_count() 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.add_card(card)
hand.rearrange() 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(): func end_turn():
turn_attack = not turn_attack 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(): func adjust_playable():
$hand_attack.visible = turn_attack $hand_attack.visible = turn_attack
for unit in BattleHelper.list_units(get_tree()): for unit in BattleHelper.list_units(get_tree()):
unit.playable = unit.attacker == turn_attack 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

View file

@ -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://screens/battle/mechanolith.ogg" type="AudioStream" id=1]
[ext_resource path="res://theme/ui.tres" type="Theme" id=2] [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/background.jpg" type="Texture" id=4]
[ext_resource path="res://screens/battle/battle.gd" type="Script" id=5] [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://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] [ext_resource path="res://decks/all.tscn" type="PackedScene" id=9]
[node name="view" type="Panel"] [node name="view" type="Panel"]
@ -14,10 +16,12 @@ anchor_bottom = 1.0
theme = ExtResource( 2 ) theme = ExtResource( 2 )
script = ExtResource( 5 ) script = ExtResource( 5 )
__meta__ = { __meta__ = {
"_edit_lock_": true,
"_edit_use_anchors_": false "_edit_use_anchors_": false
} }
deck_attack = ExtResource( 9 ) deck_attack = ExtResource( 9 )
deck_defend = ExtResource( 9 ) deck_defend = ExtResource( 9 )
defend_ai = ExtResource( 8 )
[node name="background" type="Sprite" parent="."] [node name="background" type="Sprite" parent="."]
position = Vector2( 540, 970 ) position = Vector2( 540, 970 )
@ -27,7 +31,7 @@ __meta__ = {
"_edit_lock_": true "_edit_lock_": true
} }
[node name="arena_normal1" parent="." instance=ExtResource( 3 )] [node name="arena" parent="." instance=ExtResource( 3 )]
[node name="bgm" type="AudioStreamPlayer" parent="."] [node name="bgm" type="AudioStreamPlayer" parent="."]
stream = ExtResource( 1 ) stream = ExtResource( 1 )
@ -69,3 +73,20 @@ script = ExtResource( 6 )
__meta__ = { __meta__ = {
"_edit_use_anchors_": false "_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"]

View file

@ -2,22 +2,19 @@ extends Control
const NodesHelpers = preload("res://helpers/nodes.gd") const NodesHelpers = preload("res://helpers/nodes.gd")
signal unit_created(unit)
signal turn_end signal turn_end
export var attack = false export var attack = false
var cards = [] var cards = []
func _ready():
if has_node("skip"):
$skip.connect("pressed", self, "emit_turn_end")
func add_card(card): func add_card(card):
""" Add a card to the hand """ Add a card to the hand
""" """
cards.append(card) cards.append(card)
NodesHelpers.set_parent(card, self) NodesHelpers.set_parent(card, self)
card.connect("played", self, "on_card_played") card.connect("played", self, "on_card_played")
func get_card_count(): func get_card_count():
""" Get the number of cards in hand """ Get the number of cards in hand
""" """
@ -30,14 +27,16 @@ func rearrange():
for card in cards: for card in cards:
card.set_hand_location(Vector2(i * 210, 0)) card.set_hand_location(Vector2(i * 210, 0))
i += 1 i += 1
func on_card_played(card, anchor): func on_card_played(card, anchor, unit):
""" Called when a card is played """ Called when a card is played
""" """
card.queue_free() card.queue_free()
cards.erase(card) cards.erase(card)
rearrange() rearrange()
if unit:
emit_signal("unit_created", unit)
emit_turn_end() emit_turn_end()
func emit_turn_end(): func emit_turn_end():
emit_signal("turn_end") emit_signal("turn_end")

View file

@ -3,6 +3,8 @@ extends Node2D
class_name BaseUnit class_name BaseUnit
signal combat_simulation(attacker, defender)
const AnimHelper = preload("res://helpers/anims.gd") const AnimHelper = preload("res://helpers/anims.gd")
const BattleHelper = preload("res://helpers/battle.gd") const BattleHelper = preload("res://helpers/battle.gd")
@ -19,29 +21,26 @@ var dragged
func _ready(): func _ready():
$move_hint.visible = false $move_hint.visible = false
set_sprite(sprite) set_sprite(sprite)
set_base_damage_points(base_damage_points) set_base_damage_points(base_damage_points)
set_base_move_points(base_move_points) set_base_move_points(base_move_points)
set_base_hull_points(base_hull_points) set_base_hull_points(base_hull_points)
set_base_shield_points(base_shield_points) set_base_shield_points(base_shield_points)
set_attacker(attacker) set_attacker(attacker)
set_playable(playable) set_playable(playable)
func destroy(): func destroy():
if get_parent() is Anchor: if get_parent() is Anchor:
(get_parent() as Anchor).set_content(null) (get_parent() as Anchor).set_content(null)
queue_free() queue_free()
func set_attacker(val): func set_attacker(val):
attacker = val attacker = val
if has_node("points"): if has_node("points"):
if attacker: $points.enemy = not attacker
$points.modulate = Color(1.0, 1.0, 1.0)
else:
$points.modulate = Color(1.0, 0.3, 0.3)
func set_playable(val): func set_playable(val):
playable = val playable = val
@ -49,7 +48,7 @@ func set_playable(val):
modulate = Color(1.0, 1.0, 1.0) modulate = Color(1.0, 1.0, 1.0)
else: else:
modulate = Color(0.5, 0.5, 0.5) modulate = Color(0.5, 0.5, 0.5)
func set_sprite(val): func set_sprite(val):
sprite = val sprite = val
if has_node("sprite"): if has_node("sprite"):
@ -80,23 +79,34 @@ func can_target(other):
return false return false
else: else:
return other.attacker != attacker return other.attacker != attacker
func can_be_used_on_anchor(anchor): 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())) 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(): func update_anchors():
selected_anchor = BattleHelper.update_anchors(get_tree(), dragged, funcref(self, "can_be_used_on_anchor")) selected_anchor = BattleHelper.update_anchors(get_tree(), dragged, funcref(self, "can_be_used_on_anchor"))
func play(anchor): func play(anchor):
if playable: if playable and anchor:
if anchor.is_empty(): if anchor.is_empty():
move_to(anchor) move_to(anchor)
else: else:
var other = anchor.get_content() var other = anchor.get_content()
if other.has_method("get_points"): if other.has_method("get_points"):
attack(other) 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): func move_to(anchor):
if $points.move: if $points.move:
$points.move -= 1 $points.move -= 1
@ -105,54 +115,43 @@ func move_to(anchor):
set_position(old_position - anchor.position) set_position(old_position - anchor.position)
$sprite.rotation = atan2(-position.y, -position.x) $sprite.rotation = atan2(-position.y, -position.x)
AnimHelper.linear_goto(self, Vector2()) AnimHelper.linear_goto(self, Vector2())
func get_points() -> UnitPoints: func get_points() -> UnitPoints:
if $points is UnitPoints: if $points is UnitPoints:
return $points as UnitPoints return $points as UnitPoints
else: else:
return null return null
func attack(other) -> bool: func attack(other):
var selfpoints = get_points() var selfpoints = get_points()
var otherpoints = other.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: BattleHelper.apply_combat(selfpoints, otherpoints)
otherpoints.damage -= 1
if selfpoints.shield:
selfpoints.shield -= 1
elif selfpoints.hull:
selfpoints.hull -= 1
if selfpoints.hull <= 0: if selfpoints.hull <= 0:
destroy() destroy()
if otherpoints.hull <= 0: if otherpoints.hull <= 0:
other.destroy() other.destroy()
return selfpoints.hull > 0 && otherpoints.hull == 0 func reset_state():
func return_to_base():
dragged = null dragged = null
$move_hint.visible = false $move_hint.visible = false
$move_hint.set_point_position(1, Vector2(0, 0)) $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): func on_dragged(active, relative, absolute):
if not playable: if not playable:
return return
if dragged and not active: if active:
if selected_anchor: simulate(selected_anchor)
play(selected_anchor) elif dragged:
else: simulate(null)
return_to_base() play(selected_anchor)
$move_hint.visible = active $move_hint.visible = active
@ -160,8 +159,8 @@ func on_dragged(active, relative, absolute):
dragged = absolute dragged = absolute
else: else:
dragged = null dragged = null
update_anchors() update_anchors()
if dragged: if dragged:
$move_hint.set_point_position(1, (selected_anchor.global_position if selected_anchor else dragged) - global_position) $move_hint.set_point_position(1, (selected_anchor.global_position if selected_anchor else dragged) - global_position)

View file

@ -7,12 +7,14 @@ export var move = 0 setget set_move
export var hull = 0 setget set_hull export var hull = 0 setget set_hull
export var shield = 0 setget set_shield export var shield = 0 setget set_shield
export var damage = 0 setget set_damage export var damage = 0 setget set_damage
export var enemy = false setget set_enemy
func _ready(): func _ready():
set_move(move) set_move(move)
set_hull(hull) set_hull(hull)
set_shield(shield) set_shield(shield)
set_damage(damage) set_damage(damage)
set_enemy(enemy)
func set_move(val): func set_move(val):
move = val move = val
@ -23,14 +25,27 @@ func set_hull(val):
hull = val hull = val
if has_node("hud/hull"): if has_node("hud/hull"):
$hud/hull.text = String(val) $hud/hull.text = String(val)
func set_shield(val): func set_shield(val):
shield = val shield = val
if has_node("hud/shield"): if has_node("hud/shield"):
$hud/shield.text = String(val) $hud/shield.text = String(val)
func set_damage(val): func set_damage(val):
damage = val damage = val
if has_node("hud/damage"): if has_node("hud/damage"):
$hud/damage.text = String(val) $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)