Merge pull request 'domain_models' (#1) from domain_models into main
Reviewed-on: https://forge.darkwagonstudio.com/dark-wagon-studio/tcg/pulls/1
This commit is contained in:
commit
e3c17f7397
30 changed files with 665 additions and 0 deletions
11
data/cards/baraga.tres
Normal file
11
data/cards/baraga.tres
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[gd_resource type="Resource" script_class="MonsterCard" load_steps=2 format=3 uid="uid://cs7q8i7bvohmj"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://tcg/card/monster_card.gd" id="1_v65bb"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1_v65bb")
|
||||||
|
rock = 100
|
||||||
|
paper = 100
|
||||||
|
scissors = 100
|
||||||
|
energy_cost = 3
|
||||||
|
base_health = 170
|
||||||
16
data/cards/potion.tres
Normal file
16
data/cards/potion.tres
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
[gd_resource type="Resource" script_class="SupportCard" load_steps=5 format=3 uid="uid://4eod3m0vc5a8"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://tcg/card/support_card_effect_instance.gd" id="1_alamh"]
|
||||||
|
[ext_resource type="Resource" uid="uid://cvu0rtt5nggf" path="res://data/support_effects/heal.tres" id="2_3x5mu"]
|
||||||
|
[ext_resource type="Script" path="res://tcg/card/support_card.gd" id="2_72hmi"]
|
||||||
|
|
||||||
|
[sub_resource type="Resource" id="Resource_88lmk"]
|
||||||
|
script = ExtResource("1_alamh")
|
||||||
|
magnitude = 5
|
||||||
|
effect = ExtResource("2_3x5mu")
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("2_72hmi")
|
||||||
|
type = "green"
|
||||||
|
priority = 0
|
||||||
|
effects = Array[ExtResource("1_alamh")]([SubResource("Resource_88lmk")])
|
||||||
11
data/cards/taiman.tres
Normal file
11
data/cards/taiman.tres
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[gd_resource type="Resource" script_class="MonsterCard" load_steps=2 format=3 uid="uid://di76avwc0gn8e"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://tcg/card/monster_card.gd" id="1_j4601"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1_j4601")
|
||||||
|
rock = 50
|
||||||
|
paper = 50
|
||||||
|
scissors = 50
|
||||||
|
energy_cost = 1
|
||||||
|
base_health = 120
|
||||||
6
data/support_effects/heal.tres
Normal file
6
data/support_effects/heal.tres
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
[gd_resource type="Resource" script_class="SupportCardEffect" load_steps=2 format=3 uid="uid://cvu0rtt5nggf"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://tcg/card/support_card_effect.gd" id="1_exmke"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1_exmke")
|
||||||
62
demo_game.gd
Normal file
62
demo_game.gd
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
@export var player_1_deck: Array[Card]
|
||||||
|
@export var player_2_deck: Array[Card]
|
||||||
|
@onready var match_manager = $MatchManager
|
||||||
|
@onready var own_side = $Own
|
||||||
|
@onready var opponent_side = $Opponent
|
||||||
|
@onready var start_game_btn = $StartGameButton
|
||||||
|
|
||||||
|
var player_action_queue: Dictionary
|
||||||
|
var player_1_action: Action:
|
||||||
|
get:
|
||||||
|
return player_action_queue.get(MatchManager.PLAYER_1_ID)
|
||||||
|
set(value):
|
||||||
|
player_action_queue[MatchManager.PLAYER_1_ID] = value
|
||||||
|
var player_2_action: Action:
|
||||||
|
get:
|
||||||
|
return player_action_queue.get(MatchManager.PLAYER_2_ID)
|
||||||
|
set(value):
|
||||||
|
player_action_queue[MatchManager.PLAYER_2_ID] = value
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
match_manager.init({
|
||||||
|
MatchManager.PLAYER_1_ID: player_1_deck,
|
||||||
|
MatchManager.PLAYER_2_ID: player_2_deck
|
||||||
|
})
|
||||||
|
own_side.attach(match_manager)
|
||||||
|
opponent_side.attach(match_manager)
|
||||||
|
match_manager.state_transitioned.connect(_on_match_manager_state_transitioned)
|
||||||
|
|
||||||
|
var transition_history: Array[PhaseTransition] = []
|
||||||
|
func _on_match_manager_state_transitioned(transition: PhaseTransition):
|
||||||
|
start_game_btn.visible = match_manager.phase == Match.Phase.PREGAME
|
||||||
|
transition_history.append(transition)
|
||||||
|
print("Phase: ", Match.phase_to_str(transition.from), " -> ", Match.phase_to_str(transition.to))
|
||||||
|
|
||||||
|
func _on_start_game_button_button_up() -> void:
|
||||||
|
match_manager.resolve({})
|
||||||
|
|
||||||
|
func _on_own_play_card(card: Card) -> void:
|
||||||
|
player_1_action = ActionPlayCard.new(card)
|
||||||
|
if player_2_action:
|
||||||
|
match_manager.resolve(player_action_queue)
|
||||||
|
player_action_queue.clear()
|
||||||
|
|
||||||
|
func _on_opponent_play_card(card: Card) -> void:
|
||||||
|
player_2_action = ActionPlayCard.new(card)
|
||||||
|
if player_1_action:
|
||||||
|
match_manager.resolve(player_action_queue)
|
||||||
|
player_action_queue.clear()
|
||||||
|
|
||||||
|
func _on_own_rps_move(move: String) -> void:
|
||||||
|
player_1_action = ActionRPSMove.new(move)
|
||||||
|
if player_2_action:
|
||||||
|
match_manager.resolve(player_action_queue)
|
||||||
|
player_action_queue.clear()
|
||||||
|
|
||||||
|
func _on_opponent_rps_move(move: String) -> void:
|
||||||
|
player_2_action = ActionRPSMove.new(move)
|
||||||
|
if player_1_action:
|
||||||
|
match_manager.resolve(player_action_queue)
|
||||||
|
player_action_queue.clear()
|
||||||
169
demo_game.tscn
Normal file
169
demo_game.tscn
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
[gd_scene load_steps=8 format=3 uid="uid://bgc0u117jqyr1"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://demo_game.gd" id="1_jn16u"]
|
||||||
|
[ext_resource type="Script" path="res://player_side.gd" id="2_w4tnt"]
|
||||||
|
[ext_resource type="Script" path="res://tcg/card/card.gd" id="2_xuft0"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://cikstg43mudkn" path="res://tcg/match/match_manager.tscn" id="3_3yhrl"]
|
||||||
|
[ext_resource type="Resource" uid="uid://cs7q8i7bvohmj" path="res://data/cards/baraga.tres" id="3_we1tk"]
|
||||||
|
[ext_resource type="Resource" uid="uid://4eod3m0vc5a8" path="res://data/cards/potion.tres" id="4_kkhfk"]
|
||||||
|
[ext_resource type="Resource" uid="uid://di76avwc0gn8e" path="res://data/cards/taiman.tres" id="5_3cm5x"]
|
||||||
|
|
||||||
|
[node name="DemoGame" type="Control"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
script = ExtResource("1_jn16u")
|
||||||
|
player_1_deck = Array[ExtResource("2_xuft0")]([ExtResource("3_we1tk"), ExtResource("4_kkhfk"), ExtResource("4_kkhfk")])
|
||||||
|
player_2_deck = Array[ExtResource("2_xuft0")]([ExtResource("5_3cm5x"), ExtResource("4_kkhfk"), ExtResource("4_kkhfk")])
|
||||||
|
|
||||||
|
[node name="Own" type="VBoxContainer" parent="." node_paths=PackedStringArray("deck", "monster_name_label", "monster_health_label", "incoming_damage_label", "energy_label")]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 12
|
||||||
|
anchor_top = 1.0
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
offset_top = -131.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 0
|
||||||
|
alignment = 2
|
||||||
|
script = ExtResource("2_w4tnt")
|
||||||
|
deck = NodePath("Hand")
|
||||||
|
monster_name_label = NodePath("Monster/VBoxContainer/NameLabel")
|
||||||
|
monster_health_label = NodePath("Monster/VBoxContainer/HealthLabel")
|
||||||
|
incoming_damage_label = NodePath("Monster/VBoxContainer/IncomingDamageLabel")
|
||||||
|
energy_label = NodePath("HBoxContainer/EnergyLabel")
|
||||||
|
|
||||||
|
[node name="Monster" type="HBoxContainer" parent="Own"]
|
||||||
|
custom_minimum_size = Vector2(0, 128)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="Own/Monster"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="NameLabel" type="Label" parent="Own/Monster/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
text = "Name:"
|
||||||
|
|
||||||
|
[node name="HealthLabel" type="Label" parent="Own/Monster/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
text = "Health:"
|
||||||
|
|
||||||
|
[node name="IncomingDamageLabel" type="Label" parent="Own/Monster/VBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(128, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
text = "Incoming Damage:"
|
||||||
|
vertical_alignment = 2
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="Own"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="EnergyLabel" type="Label" parent="Own/HBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Energy:"
|
||||||
|
|
||||||
|
[node name="Hand" type="HBoxContainer" parent="Own"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="Button" type="Button" parent="Own/Hand"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "CARD_1"
|
||||||
|
|
||||||
|
[node name="Button2" type="Button" parent="Own/Hand"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "CARD_2"
|
||||||
|
|
||||||
|
[node name="Button3" type="Button" parent="Own/Hand"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "CARD_3"
|
||||||
|
|
||||||
|
[node name="Opponent" type="VBoxContainer" parent="." node_paths=PackedStringArray("deck", "monster_name_label", "monster_health_label", "incoming_damage_label", "energy_label")]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 10
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_bottom = 190.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
script = ExtResource("2_w4tnt")
|
||||||
|
player_id = 1
|
||||||
|
deck = NodePath("Hand")
|
||||||
|
monster_name_label = NodePath("Monster/VBoxContainer/NameLabel")
|
||||||
|
monster_health_label = NodePath("Monster/VBoxContainer/HealthLabel")
|
||||||
|
incoming_damage_label = NodePath("Monster/VBoxContainer/IncomingDamageLabel")
|
||||||
|
energy_label = NodePath("EnergyLabel")
|
||||||
|
|
||||||
|
[node name="Hand" type="HBoxContainer" parent="Opponent"]
|
||||||
|
layout_mode = 2
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="Button" type="Button" parent="Opponent/Hand"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "CARD_1"
|
||||||
|
|
||||||
|
[node name="Button2" type="Button" parent="Opponent/Hand"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "CARD_2"
|
||||||
|
|
||||||
|
[node name="Button3" type="Button" parent="Opponent/Hand"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "CARD_3"
|
||||||
|
|
||||||
|
[node name="EnergyLabel" type="Label" parent="Opponent"]
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Energy:"
|
||||||
|
|
||||||
|
[node name="Monster" type="HBoxContainer" parent="Opponent"]
|
||||||
|
custom_minimum_size = Vector2(0, 128)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
alignment = 1
|
||||||
|
|
||||||
|
[node name="VBoxContainer" type="VBoxContainer" parent="Opponent/Monster"]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="NameLabel" type="Label" parent="Opponent/Monster/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
text = "Name: "
|
||||||
|
|
||||||
|
[node name="HealthLabel" type="Label" parent="Opponent/Monster/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
text = "Health:"
|
||||||
|
|
||||||
|
[node name="IncomingDamageLabel" type="Label" parent="Opponent/Monster/VBoxContainer"]
|
||||||
|
custom_minimum_size = Vector2(128, 0)
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
text = "Incoming Damage:"
|
||||||
|
vertical_alignment = 2
|
||||||
|
|
||||||
|
[node name="StartGameButton" type="Button" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -132.0
|
||||||
|
offset_top = -59.5
|
||||||
|
offset_right = 132.0
|
||||||
|
offset_bottom = 59.5
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
text = "Start Game"
|
||||||
|
|
||||||
|
[node name="MatchManager" parent="." instance=ExtResource("3_3yhrl")]
|
||||||
|
|
||||||
|
[connection signal="play_card" from="Own" to="." method="_on_own_play_card"]
|
||||||
|
[connection signal="rps_move" from="Own" to="." method="_on_own_rps_move"]
|
||||||
|
[connection signal="play_card" from="Opponent" to="." method="_on_opponent_play_card"]
|
||||||
|
[connection signal="rps_move" from="Opponent" to="." method="_on_opponent_rps_move"]
|
||||||
|
[connection signal="button_up" from="StartGameButton" to="." method="_on_start_game_button_button_up"]
|
||||||
55
player_side.gd
Normal file
55
player_side.gd
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
signal play_card(card: Card)
|
||||||
|
signal rps_move(move: String)
|
||||||
|
@export var player_id: int
|
||||||
|
@export var deck: Container
|
||||||
|
@export var monster_name_label: Label
|
||||||
|
@export var monster_health_label: Label
|
||||||
|
@export var incoming_damage_label: Label
|
||||||
|
@export var energy_label: Label
|
||||||
|
|
||||||
|
var match_manager: MatchManager
|
||||||
|
|
||||||
|
func attach(match_manager: MatchManager):
|
||||||
|
self.match_manager = match_manager
|
||||||
|
match_manager.state_transitioned.connect(_on_update)
|
||||||
|
|
||||||
|
func _on_update(transition):
|
||||||
|
for child in deck.get_children():
|
||||||
|
child.queue_free()
|
||||||
|
var player: MatchPlayer = match_manager.players.get(player_id) as MatchPlayer
|
||||||
|
if not player:
|
||||||
|
return
|
||||||
|
energy_label.text = "Energy: " + str(player.energy)
|
||||||
|
var monster = player.monster
|
||||||
|
if monster:
|
||||||
|
monster_name_label.text = "Name: " + monster.card.id
|
||||||
|
monster_health_label.text = "Health: " + str(monster.health)
|
||||||
|
incoming_damage_label.text = "Incoming Damage: " + str(monster.health_delta)
|
||||||
|
if match_manager.phase in [Match.Phase.SUMMON, Match.Phase.SUPPORT_1, Match.Phase.SUPPORT_2]:
|
||||||
|
for card: Card in player.hand:
|
||||||
|
if match_manager.phase == Match.Phase.SUMMON and card is not MonsterCard:
|
||||||
|
continue
|
||||||
|
if (match_manager.phase == Match.Phase.SUPPORT_1 or match_manager.phase == Match.Phase.SUPPORT_2) and card is not SupportCard:
|
||||||
|
continue
|
||||||
|
if match_manager.phase == Match.Phase.SUPPORT_1 and card.type == "red":
|
||||||
|
continue
|
||||||
|
var btn = Button.new()
|
||||||
|
btn.text = card.id
|
||||||
|
btn.button_up.connect(func (): play_card.emit(card))
|
||||||
|
btn.disabled = (
|
||||||
|
card is MonsterCard and match_manager.phase != Match.Phase.SUMMON
|
||||||
|
) or (
|
||||||
|
card is SupportCard and match_manager.phase != Match.Phase.SUPPORT_1 and match_manager.phase != Match.Phase.SUPPORT_2
|
||||||
|
) or (
|
||||||
|
card is SupportCard and card.type == "red" and match_manager.phase != Match.Phase.SUPPORT_2
|
||||||
|
)
|
||||||
|
deck.add_child(btn)
|
||||||
|
if match_manager.phase == Match.Phase.RPS:
|
||||||
|
for move in ["rock", "paper", "scissors"]:
|
||||||
|
var btn = Button.new()
|
||||||
|
btn.text = move
|
||||||
|
btn.button_up.connect(func (): rps_move.emit(move))
|
||||||
|
deck.add_child(btn)
|
||||||
|
|
||||||
|
|
@ -15,6 +15,10 @@ run/main_scene="res://main.tscn"
|
||||||
config/features=PackedStringArray("4.3", "Mobile")
|
config/features=PackedStringArray("4.3", "Mobile")
|
||||||
config/icon="res://icon.svg"
|
config/icon="res://icon.svg"
|
||||||
|
|
||||||
|
[autoload]
|
||||||
|
|
||||||
|
Match="*res://tcg/match/match.gd"
|
||||||
|
|
||||||
[rendering]
|
[rendering]
|
||||||
|
|
||||||
renderer/rendering_method="mobile"
|
renderer/rendering_method="mobile"
|
||||||
|
|
|
||||||
6
tcg/card/card.gd
Normal file
6
tcg/card/card.gd
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
extends Resource
|
||||||
|
class_name Card
|
||||||
|
|
||||||
|
var id: String:
|
||||||
|
get:
|
||||||
|
return resource_path.rsplit(".", true, 1)[0].rsplit("/", true, 1)[1]
|
||||||
16
tcg/card/monster_card.gd
Normal file
16
tcg/card/monster_card.gd
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
extends Card
|
||||||
|
class_name MonsterCard
|
||||||
|
|
||||||
|
@export var rock: int = 0
|
||||||
|
@export var paper: int = 0
|
||||||
|
@export var scissors: int = 0
|
||||||
|
@export var energy_cost: int = 1
|
||||||
|
@export var base_health: int = 100
|
||||||
|
|
||||||
|
var damage: Dictionary:
|
||||||
|
get:
|
||||||
|
return {
|
||||||
|
"rock": rock,
|
||||||
|
"paper": paper,
|
||||||
|
"scissors": scissors
|
||||||
|
}
|
||||||
6
tcg/card/support_card.gd
Normal file
6
tcg/card/support_card.gd
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
extends Card
|
||||||
|
class_name SupportCard
|
||||||
|
|
||||||
|
@export_enum("red", "green") var type = "green"
|
||||||
|
@export var priority: int = 0
|
||||||
|
@export var effects: Array[SupportCardEffectInstance] = [null]
|
||||||
6
tcg/card/support_card_effect.gd
Normal file
6
tcg/card/support_card_effect.gd
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
extends Resource
|
||||||
|
class_name SupportCardEffect
|
||||||
|
|
||||||
|
var id: String:
|
||||||
|
get:
|
||||||
|
return resource_path.rsplit(".", true, 1)[0].rsplit("/", true, 1)[1]
|
||||||
5
tcg/card/support_card_effect_instance.gd
Normal file
5
tcg/card/support_card_effect_instance.gd
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
extends Resource
|
||||||
|
class_name SupportCardEffectInstance
|
||||||
|
|
||||||
|
@export var magnitude: int
|
||||||
|
@export var effect: SupportCardEffect
|
||||||
2
tcg/match/action/action.gd
Normal file
2
tcg/match/action/action.gd
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
extends Resource
|
||||||
|
class_name Action
|
||||||
7
tcg/match/action/action_play_card.gd
Normal file
7
tcg/match/action/action_play_card.gd
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
extends Action
|
||||||
|
class_name ActionPlayCard
|
||||||
|
|
||||||
|
@export var card: Card
|
||||||
|
|
||||||
|
func _init(card: Card) -> void:
|
||||||
|
self.card = card
|
||||||
7
tcg/match/action/action_rps_move.gd
Normal file
7
tcg/match/action/action_rps_move.gd
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
extends Action
|
||||||
|
class_name ActionRPSMove
|
||||||
|
|
||||||
|
var move: String # rock | paper | scissors
|
||||||
|
|
||||||
|
func _init(move: String) -> void:
|
||||||
|
self.move = move
|
||||||
2
tcg/match/event/event.gd
Normal file
2
tcg/match/event/event.gd
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
extends Resource
|
||||||
|
class_name Event
|
||||||
9
tcg/match/event/event_damage_dealt.gd
Normal file
9
tcg/match/event/event_damage_dealt.gd
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
extends Event
|
||||||
|
class_name EventDamageDealt
|
||||||
|
|
||||||
|
var target_player_id: int
|
||||||
|
var amount: int
|
||||||
|
|
||||||
|
func _init(target_player_id: int, amount: int) -> void:
|
||||||
|
self.target_player_id = target_player_id
|
||||||
|
self.amount = amount
|
||||||
9
tcg/match/event/event_draw_card.gd
Normal file
9
tcg/match/event/event_draw_card.gd
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
extends Event
|
||||||
|
class_name EventCardDrawn
|
||||||
|
|
||||||
|
var player_id: int
|
||||||
|
var card: Card
|
||||||
|
|
||||||
|
func _init(player_id: int, card: Card) -> void:
|
||||||
|
self.player_id = player_id
|
||||||
|
self.card = card
|
||||||
7
tcg/match/event/event_monster_died.gd
Normal file
7
tcg/match/event/event_monster_died.gd
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
extends Event
|
||||||
|
class_name EventMonsterDied
|
||||||
|
|
||||||
|
var player_id: int
|
||||||
|
|
||||||
|
func _init(player_id: int) -> void:
|
||||||
|
self.player_id = player_id
|
||||||
7
tcg/match/event/event_monster_summoned.gd
Normal file
7
tcg/match/event/event_monster_summoned.gd
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
extends Event
|
||||||
|
class_name EventMonsterSummoned
|
||||||
|
|
||||||
|
var monster: MonsterCard
|
||||||
|
|
||||||
|
func _init(monster: MonsterCard) -> void:
|
||||||
|
self.monster = monster
|
||||||
9
tcg/match/event/event_support_effect_applied.gd
Normal file
9
tcg/match/event/event_support_effect_applied.gd
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
extends Event
|
||||||
|
class_name EventSupportEffectApplied
|
||||||
|
|
||||||
|
var player_id: int
|
||||||
|
var effect_instance: SupportCardEffectInstance
|
||||||
|
|
||||||
|
func _init(player_id: int, effect_instance: SupportCardEffectInstance) -> void:
|
||||||
|
self.player_id = player_id
|
||||||
|
self.effect_instance = effect_instance
|
||||||
26
tcg/match/match.gd
Normal file
26
tcg/match/match.gd
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
enum Phase {
|
||||||
|
PREGAME = 0,
|
||||||
|
SUMMON = 1,
|
||||||
|
SUPPORT_1 = 2,
|
||||||
|
RPS = 3,
|
||||||
|
SUPPORT_2 = 4,
|
||||||
|
END
|
||||||
|
}
|
||||||
|
|
||||||
|
func phase_to_str(phase: Phase):
|
||||||
|
match phase:
|
||||||
|
Phase.PREGAME:
|
||||||
|
return "Pregame"
|
||||||
|
Phase.SUMMON:
|
||||||
|
return "Summon"
|
||||||
|
Phase.SUPPORT_1:
|
||||||
|
return "Support 1"
|
||||||
|
Phase.RPS:
|
||||||
|
return "RPS"
|
||||||
|
Phase.SUPPORT_2:
|
||||||
|
return "Support 2"
|
||||||
|
Phase.END:
|
||||||
|
return "End"
|
||||||
|
|
||||||
155
tcg/match/match_manager.gd
Normal file
155
tcg/match/match_manager.gd
Normal file
|
|
@ -0,0 +1,155 @@
|
||||||
|
extends Node
|
||||||
|
class_name MatchManager
|
||||||
|
|
||||||
|
signal match_begun
|
||||||
|
signal state_transitioned(transition: PhaseTransition)
|
||||||
|
|
||||||
|
const PLAYER_1_ID: int = 0
|
||||||
|
const PLAYER_2_ID: int = 1
|
||||||
|
|
||||||
|
var rps_wins = {
|
||||||
|
"rock": "scissors",
|
||||||
|
"scissors": "paper",
|
||||||
|
"paper": "rock"
|
||||||
|
}
|
||||||
|
|
||||||
|
var players: Dictionary
|
||||||
|
var player_1: MatchPlayer:
|
||||||
|
get:
|
||||||
|
return players[PLAYER_1_ID] as MatchPlayer
|
||||||
|
var player_2: MatchPlayer:
|
||||||
|
get:
|
||||||
|
return players[PLAYER_2_ID] as MatchPlayer
|
||||||
|
var phase: Match.Phase = Match.Phase.PREGAME
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
cleanup()
|
||||||
|
|
||||||
|
func init(decks: Dictionary):
|
||||||
|
cleanup()
|
||||||
|
for player_id in [PLAYER_1_ID, PLAYER_2_ID]:
|
||||||
|
var deck_shuffled = decks[player_id].duplicate() as Array[Card]
|
||||||
|
deck_shuffled.shuffle()
|
||||||
|
players[player_id] = MatchPlayer.new(player_id, deck_shuffled)
|
||||||
|
|
||||||
|
func cleanup():
|
||||||
|
for player_id in [PLAYER_1_ID, PLAYER_2_ID]:
|
||||||
|
var existing = players.get(player_id)
|
||||||
|
if existing:
|
||||||
|
existing.free()
|
||||||
|
phase = Match.Phase.PREGAME
|
||||||
|
|
||||||
|
func resolve(action_by_player_id: Dictionary) -> PhaseTransition:
|
||||||
|
var initial_phase = phase
|
||||||
|
var events = [] as Array[Event]
|
||||||
|
var player_1_action = action_by_player_id.get(PLAYER_1_ID) as Action
|
||||||
|
var player_2_action = action_by_player_id.get(PLAYER_2_ID) as Action
|
||||||
|
match phase:
|
||||||
|
Match.Phase.PREGAME:
|
||||||
|
var CARDS_TO_DRAW: int = 3
|
||||||
|
for player: MatchPlayer in [player_1, player_2]:
|
||||||
|
var n_cards_to_draw = min(player.deck.size(), CARDS_TO_DRAW)
|
||||||
|
var drawn_cards = range(n_cards_to_draw).map(func(_i): return player.deck.pop_back()) as Array[Card]
|
||||||
|
for card in drawn_cards:
|
||||||
|
player.hand.push_back(card)
|
||||||
|
var drawn_card_events = drawn_cards.map(func(card): return EventCardDrawn.new(player.id, card))
|
||||||
|
events.append_array(
|
||||||
|
drawn_card_events
|
||||||
|
)
|
||||||
|
phase = Match.Phase.SUMMON
|
||||||
|
Match.Phase.SUMMON:
|
||||||
|
for player_id in action_by_player_id.keys():
|
||||||
|
var action = action_by_player_id[player_id]
|
||||||
|
if action is ActionPlayCard:
|
||||||
|
var card = action.card
|
||||||
|
if card is MonsterCard:
|
||||||
|
players[player_id].monster = _monster_from_card(card)
|
||||||
|
players[player_id].hand = players[player_id].hand.filter(func(card_in_hand): return card.id != card_in_hand.id)
|
||||||
|
events.append(EventMonsterSummoned.new(card))
|
||||||
|
if players.values().all(func(player: MatchPlayer): return player.monster):
|
||||||
|
phase = Match.Phase.SUPPORT_1
|
||||||
|
Match.Phase.SUPPORT_1:
|
||||||
|
var support_cards = []
|
||||||
|
if player_1_action is ActionPlayCard:
|
||||||
|
support_cards.append({
|
||||||
|
"player": player_1,
|
||||||
|
"card": player_1_action.card
|
||||||
|
})
|
||||||
|
if player_2_action is ActionPlayCard:
|
||||||
|
support_cards.append({
|
||||||
|
"player": player_2,
|
||||||
|
"card": player_2_action.card
|
||||||
|
})
|
||||||
|
support_cards.sort_custom(func(a, b): return a.card.priority < b.card.priority)
|
||||||
|
for played_card in support_cards:
|
||||||
|
var player: MatchPlayer = played_card.player
|
||||||
|
var card: SupportCard = played_card.card
|
||||||
|
events.append_array(_resolve_support_card_effects(player, card))
|
||||||
|
phase = Match.Phase.RPS
|
||||||
|
Match.Phase.RPS:
|
||||||
|
var tie = player_1_action.move == player_2_action.move
|
||||||
|
var player_1_win = rps_wins[player_1_action.move] == player_2_action.move
|
||||||
|
var player_2_win = rps_wins[player_2_action.move] == player_1_action.move
|
||||||
|
if tie or player_1_win:
|
||||||
|
player_2.monster.health_delta -= player_1.monster.card.damage[player_1_action.move]
|
||||||
|
if tie or player_2_win:
|
||||||
|
player_1.monster.health_delta -= player_2.monster.card.damage[player_2_action.move]
|
||||||
|
phase = Match.Phase.SUPPORT_2
|
||||||
|
Match.Phase.SUPPORT_2:
|
||||||
|
var support_cards = []
|
||||||
|
if player_1_action is ActionPlayCard:
|
||||||
|
support_cards.append({
|
||||||
|
"player": player_1,
|
||||||
|
"card": player_1_action.card
|
||||||
|
})
|
||||||
|
if player_2_action is ActionPlayCard:
|
||||||
|
support_cards.append({
|
||||||
|
"player": player_2,
|
||||||
|
"card": player_2_action.card
|
||||||
|
})
|
||||||
|
support_cards.sort_custom(func(a, b): return a.card.priority < b.card.priority)
|
||||||
|
for played_card in support_cards:
|
||||||
|
var player: MatchPlayer = played_card.player
|
||||||
|
var card: SupportCard = played_card.card
|
||||||
|
events.append_array(_resolve_support_card_effects(player, card))
|
||||||
|
|
||||||
|
for player: MatchPlayer in players.values():
|
||||||
|
var monster_final_health = player.monster.health + player.monster.health_delta
|
||||||
|
if monster_final_health <= 0:
|
||||||
|
player.monster = null
|
||||||
|
events.append(EventMonsterDied.new(player.id))
|
||||||
|
else:
|
||||||
|
player.monster.health += player.monster.health_delta
|
||||||
|
player.monster.health_delta = 0
|
||||||
|
var drawn_card = player.deck.pop_back()
|
||||||
|
if drawn_card:
|
||||||
|
player.hand.append(drawn_card)
|
||||||
|
events.append(EventCardDrawn.new(player.id, drawn_card))
|
||||||
|
var players_without_monster = players.values().filter(func (player): return player.monster == null)
|
||||||
|
if players_without_monster.size() == 0:
|
||||||
|
phase = Match.Phase.SUPPORT_1
|
||||||
|
elif players_without_monster.all(
|
||||||
|
func (player): return player.hand.any(func (it): return it is MonsterCard and it.energy_cost <= player.energy)
|
||||||
|
):
|
||||||
|
phase = Match.Phase.SUMMON
|
||||||
|
else:
|
||||||
|
phase = Match.Phase.END
|
||||||
|
_:
|
||||||
|
pass
|
||||||
|
var transition = PhaseTransition.new(events, initial_phase, phase)
|
||||||
|
state_transitioned.emit(transition)
|
||||||
|
return transition
|
||||||
|
|
||||||
|
func _monster_from_card(card: MonsterCard) -> MatchMonster:
|
||||||
|
return MatchMonster.new(card, card.base_health, 0)
|
||||||
|
|
||||||
|
func _resolve_support_card_effects(player: MatchPlayer, card: SupportCard) -> Array[Event]:
|
||||||
|
var events: Array[Event]
|
||||||
|
for effect_instance in card.effects:
|
||||||
|
var effect = effect_instance.effect
|
||||||
|
var magnitude = effect_instance.magnitude
|
||||||
|
match effect.id:
|
||||||
|
"heal":
|
||||||
|
player.monster.health_delta += magnitude
|
||||||
|
events.append(EventSupportEffectApplied.new(player.id, effect_instance))
|
||||||
|
return events
|
||||||
6
tcg/match/match_manager.tscn
Normal file
6
tcg/match/match_manager.tscn
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
[gd_scene load_steps=2 format=3 uid="uid://cikstg43mudkn"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://tcg/match/match_manager.gd" id="1_tkhvw"]
|
||||||
|
|
||||||
|
[node name="MatchManager" type="Node"]
|
||||||
|
script = ExtResource("1_tkhvw")
|
||||||
11
tcg/match/match_monster.gd
Normal file
11
tcg/match/match_monster.gd
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
extends RefCounted
|
||||||
|
class_name MatchMonster
|
||||||
|
|
||||||
|
var card: MonsterCard
|
||||||
|
var health: int
|
||||||
|
var health_delta: int
|
||||||
|
|
||||||
|
func _init(card: MonsterCard, health: int, health_delta: int) -> void:
|
||||||
|
self.card = card
|
||||||
|
self.health = health
|
||||||
|
self.health_delta = health_delta
|
||||||
6
tcg/match/match_monster.tscn
Normal file
6
tcg/match/match_monster.tscn
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
[gd_scene load_steps=2 format=3 uid="uid://d3dm1bnooaxmu"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://tcg/match/match_monster.gd" id="1_o6dlj"]
|
||||||
|
|
||||||
|
[node name="Monster" type="Node"]
|
||||||
|
script = ExtResource("1_o6dlj")
|
||||||
12
tcg/match/match_player.gd
Normal file
12
tcg/match/match_player.gd
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
extends Object
|
||||||
|
class_name MatchPlayer
|
||||||
|
|
||||||
|
var id: int
|
||||||
|
var hand: Array[Card] = []
|
||||||
|
var deck: Array[Card]
|
||||||
|
var energy: int = 5
|
||||||
|
var monster: MatchMonster = null
|
||||||
|
|
||||||
|
func _init(id: int, deck: Array[Card]) -> void:
|
||||||
|
self.id = id
|
||||||
|
self.deck = deck
|
||||||
6
tcg/match/match_player.tscn
Normal file
6
tcg/match/match_player.tscn
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
[gd_scene load_steps=2 format=3 uid="uid://bwkfogonbhq6r"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://tcg/match/match_player.gd" id="1_pdj3n"]
|
||||||
|
|
||||||
|
[node name="MatchPlayer" type="Node"]
|
||||||
|
script = ExtResource("1_pdj3n")
|
||||||
11
tcg/match/phase_transition.gd
Normal file
11
tcg/match/phase_transition.gd
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
extends RefCounted
|
||||||
|
class_name PhaseTransition
|
||||||
|
|
||||||
|
var events: Array[Event]
|
||||||
|
var from: Match.Phase
|
||||||
|
var to: Match.Phase
|
||||||
|
|
||||||
|
func _init(events: Array[Event], from: Match.Phase, to: Match.Phase) -> void:
|
||||||
|
self.events = events
|
||||||
|
self.from = from
|
||||||
|
self.to = to
|
||||||
Loading…
Add table
Add a link
Reference in a new issue