From 27f38c6c1fa6d09ff1de33d4222899a63e77867e Mon Sep 17 00:00:00 2001 From: iisaev Date: Thu, 10 Sep 2026 10:58:00 +0300 Subject: [PATCH] init --- .gitignore | 37 ++ locale/ui_en.properties | 4 + locale/ui_ru.properties | 4 + manifest.json | 11 + mod.png | Bin 0 -> 19278 bytes scenes/action_card_node.tscn | 46 +++ scenes/battle_card_panel.tscn | 14 + scenes/character_card_node.tscn | 135 +++++++ scenes/end_turn_button.tscn | 16 + sounds/battle/card_defence_empty.ogg | Bin 0 -> 9648 bytes sounds/battle/card_drop_1.ogg | Bin 0 -> 5268 bytes sounds/battle/card_drop_2.ogg | Bin 0 -> 4913 bytes sounds/battle/card_drop_3.ogg | Bin 0 -> 5198 bytes sounds/battle/card_drop_4.ogg | Bin 0 -> 4661 bytes sounds/battle/card_empty.ogg | Bin 0 -> 7538 bytes sounds/battle/card_physics_damage.ogg | Bin 0 -> 12282 bytes sounds/battle/card_physics_defence.ogg | Bin 0 -> 13546 bytes sounds/battle/card_pick_1.ogg | Bin 0 -> 5355 bytes sounds/battle/card_pick_2.ogg | Bin 0 -> 5914 bytes sounds/battle/card_pick_3.ogg | Bin 0 -> 4939 bytes sounds/battle/card_pick_4.ogg | Bin 0 -> 5578 bytes sounds/battle/card_show_actions.ogg | Bin 0 -> 11320 bytes sounds/battle/card_stun_electro.ogg | Bin 0 -> 21558 bytes src/Actions/AbstractBattleAction.cs | 87 ++++ src/Actions/ActionPhase.cs | 26 ++ src/Actions/CardBattleActionProvider.cs | 44 ++ src/Actions/PhysicsAttackAction.cs | 357 +++++++++++++++++ src/Actions/PhysicsDamageActionCard.cs | 55 +++ src/Actions/PhysicsDefenceAction.cs | 215 ++++++++++ src/Actions/PhysicsDefenceActionCard.cs | 39 ++ src/Actions/PhysicsStunActionCard.cs | 39 ++ src/CardBattleCore.cs | 390 ++++++++++++++++++ src/ICard.cs | 24 ++ src/Player/AttackToEnemyStrategy.cs | 57 +++ src/Player/CardActionStrategyFactory.cs | 52 +++ src/Player/ICardActionStrategy.cs | 32 ++ src/Player/SelfStrategy.cs | 56 +++ src/Player/StunToEnemyStrategy.cs | 49 +++ src/Player/SupportStrategy.cs | 56 +++ src/StatusEffect/IStatusActions.cs | 21 + src/StatusEffect/IStatusEffect.cs | 26 ++ src/StatusEffect/StatusEffectItem.cs | 27 ++ src/StatusEffect/StatusEffectQueue.cs | 103 +++++ src/StatusEffect/StunStatusEffect.cs | 52 +++ src/UI/ActionCardNode.cs | 137 +++++++ src/UI/BattleCardPanel.cs | 263 ++++++++++++ src/UI/CardController.cs | 510 ++++++++++++++++++++++++ src/UI/CardDeathAnimation.cs | 101 +++++ src/UI/CharacterCardNode.cs | 277 +++++++++++++ src/UI/EndTurnButton.cs | 41 ++ 50 files changed, 3403 insertions(+) create mode 100644 .gitignore create mode 100644 locale/ui_en.properties create mode 100644 locale/ui_ru.properties create mode 100644 manifest.json create mode 100644 mod.png create mode 100644 scenes/action_card_node.tscn create mode 100644 scenes/battle_card_panel.tscn create mode 100644 scenes/character_card_node.tscn create mode 100644 scenes/end_turn_button.tscn create mode 100644 sounds/battle/card_defence_empty.ogg create mode 100644 sounds/battle/card_drop_1.ogg create mode 100644 sounds/battle/card_drop_2.ogg create mode 100644 sounds/battle/card_drop_3.ogg create mode 100644 sounds/battle/card_drop_4.ogg create mode 100644 sounds/battle/card_empty.ogg create mode 100644 sounds/battle/card_physics_damage.ogg create mode 100644 sounds/battle/card_physics_defence.ogg create mode 100644 sounds/battle/card_pick_1.ogg create mode 100644 sounds/battle/card_pick_2.ogg create mode 100644 sounds/battle/card_pick_3.ogg create mode 100644 sounds/battle/card_pick_4.ogg create mode 100644 sounds/battle/card_show_actions.ogg create mode 100644 sounds/battle/card_stun_electro.ogg create mode 100644 src/Actions/AbstractBattleAction.cs create mode 100644 src/Actions/ActionPhase.cs create mode 100644 src/Actions/CardBattleActionProvider.cs create mode 100644 src/Actions/PhysicsAttackAction.cs create mode 100644 src/Actions/PhysicsDamageActionCard.cs create mode 100644 src/Actions/PhysicsDefenceAction.cs create mode 100644 src/Actions/PhysicsDefenceActionCard.cs create mode 100644 src/Actions/PhysicsStunActionCard.cs create mode 100644 src/CardBattleCore.cs create mode 100644 src/ICard.cs create mode 100644 src/Player/AttackToEnemyStrategy.cs create mode 100644 src/Player/CardActionStrategyFactory.cs create mode 100644 src/Player/ICardActionStrategy.cs create mode 100644 src/Player/SelfStrategy.cs create mode 100644 src/Player/StunToEnemyStrategy.cs create mode 100644 src/Player/SupportStrategy.cs create mode 100644 src/StatusEffect/IStatusActions.cs create mode 100644 src/StatusEffect/IStatusEffect.cs create mode 100644 src/StatusEffect/StatusEffectItem.cs create mode 100644 src/StatusEffect/StatusEffectQueue.cs create mode 100644 src/StatusEffect/StunStatusEffect.cs create mode 100644 src/UI/ActionCardNode.cs create mode 100644 src/UI/BattleCardPanel.cs create mode 100644 src/UI/CardController.cs create mode 100644 src/UI/CardDeathAnimation.cs create mode 100644 src/UI/CharacterCardNode.cs create mode 100644 src/UI/EndTurnButton.cs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c301ca8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,37 @@ +# Godot 4.x +.godot/ +.idea/ +sdk/ +bin/ +obj/ + +#ai +.omo/ +.opencode/ + +gen/ +obj/ +bin/ + +*.tres~ +*.tscn~ + +# GDScript LSP +.godot/ + +# Export +*.exe +*.dmg +*.apk +*.aab +*.pck +*.zip +*.log + +# OS +.DS_Store +Thumbs.db + +# Editor +*.import +*.godot.uid diff --git a/locale/ui_en.properties b/locale/ui_en.properties new file mode 100644 index 0000000..e2a74ae --- /dev/null +++ b/locale/ui_en.properties @@ -0,0 +1,4 @@ +mod_card_battle_name = CardBattle +mod_card_battle_desc = Card battle mechanics + +battle/end_turn = End turn diff --git a/locale/ui_ru.properties b/locale/ui_ru.properties new file mode 100644 index 0000000..be650d0 --- /dev/null +++ b/locale/ui_ru.properties @@ -0,0 +1,4 @@ +mod_card_battle_name=Карточная битва +mod_card_battle_desc=Механики карточных сражений + +battle/end_turn=Конец хода \ No newline at end of file diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..c8f829d --- /dev/null +++ b/manifest.json @@ -0,0 +1,11 @@ +{ + "id": "card_battle", + "name": "CardBattle", + "version": "1.0.0", + "author": "ct", + "description": "Card battle mechanics", + "sources": [ + "src/**/*.cs" + ], + "depends": [ "core>=1", "interface>=1" ] +} diff --git a/mod.png b/mod.png new file mode 100644 index 0000000000000000000000000000000000000000..2edfb90878318aff3e62f6b9d6cf3a89691d86ae GIT binary patch literal 19278 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 IM6N<$f@B`BsQ>@~ literal 0 HcmV?d00001 diff --git a/scenes/action_card_node.tscn b/scenes/action_card_node.tscn new file mode 100644 index 0000000..83011dd --- /dev/null +++ b/scenes/action_card_node.tscn @@ -0,0 +1,46 @@ +[gd_scene format=3] + +[ext_resource type="Script" path="src/UI/ActionCardNode.cs" id="ActionCardNodeScript"] + +[node name="ActionCardNode" type="Control" script=ExtResource("ActionCardNodeScript")] +custom_minimum_size = Vector2(200, 280) +layout_mode = 3 + +[node name="Image" type="TextureRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +offset_left = 15.0 +offset_top = 15.0 +offset_right = -15.0 +offset_bottom = -40.0 +mouse_filter = 2 +expand_mode = 1 +stretch_mode = 4 + +[node name="Frame" type="TextureRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +expand_mode = 1 +stretch_mode = 4 + +[node name="NameLabel" type="Label" parent="."] +layout_mode = 1 +anchors_preset = 12 +anchor_left = 0.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +offset_top = -40.0 +offset_bottom = 0.0 +theme_override_font_sizes/font_size = 14 +horizontal_alignment = 1 diff --git a/scenes/battle_card_panel.tscn b/scenes/battle_card_panel.tscn new file mode 100644 index 0000000..9b50421 --- /dev/null +++ b/scenes/battle_card_panel.tscn @@ -0,0 +1,14 @@ +[gd_scene format=3] + +[ext_resource type="Script" path="src/UI/BattleCardPanel.cs" id="1_script"] + +[node name="BattleCardPanel" type="Control" script=ExtResource("1_script")] +layout_mode = 3 + +[node name="Content" type="Control" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 diff --git a/scenes/character_card_node.tscn b/scenes/character_card_node.tscn new file mode 100644 index 0000000..48f0f7f --- /dev/null +++ b/scenes/character_card_node.tscn @@ -0,0 +1,135 @@ +[gd_scene format=3] + +[ext_resource type="Script" path="src/UI/CharacterCardNode.cs" id="1_script"] + +[node name="CharacterCardNode" type="Control" script=ExtResource("1_script")] +custom_minimum_size = Vector2(230, 420) +layout_mode = 3 + +[node name="TeamOutline" type="ColorRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +offset_left = -4.0 +offset_top = -4.0 +offset_right = 4.0 +offset_bottom = 4.0 +mouse_filter = 2 + +[node name="Image" type="TextureRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +offset_left = 20.0 +offset_top = 20.0 +offset_right = -20.0 +offset_bottom = -60.0 +mouse_filter = 2 +expand_mode = 1 +stretch_mode = 4 + +[node name="Frame" type="TextureRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +expand_mode = 1 +stretch_mode = 4 + +[node name="Selection" type="TextureRect" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 + +[node name="InfoPanel" type="Control" parent="."] +layout_mode = 1 +anchors_preset = 12 +anchor_left = 0.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +offset_top = -60.0 +offset_bottom = 0.0 + +[node name="NameLabel" type="Label" parent="InfoPanel"] +layout_mode = 1 +anchors_preset = 10 +anchor_right = 1.0 +grow_horizontal = 2 +offset_bottom = 18.0 +horizontal_alignment = 1 +theme_override_font_sizes/font_size = 16 + +[node name="HealthBar" type="Control" parent="InfoPanel"] +layout_mode = 1 +anchors_preset = 12 +anchor_left = 0.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +offset_top = -18.0 +offset_bottom = 0.0 + +[node name="HealthBg" type="ColorRect" parent="InfoPanel/HealthBar"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0.2, 0.2, 0.2, 1) + +[node name="HealthRect" type="ColorRect" parent="InfoPanel/HealthBar"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0.8, 0, 1) + +[node name="Labels" type="HBoxContainer" parent="InfoPanel"] +layout_mode = 1 +anchors_preset = 13 +anchor_left = 0.0 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +grow_horizontal = 2 +alignment = 1 + +[node name="LabelS" type="Label" parent="InfoPanel/Labels"] +text = "S" +horizontal_alignment = 1 + +[node name="DefenceLabel" type="Label" parent="InfoPanel/Labels"] +text = "0" + +[node name="LabelA" type="Label" parent="InfoPanel/Labels"] +text = "A" +horizontal_alignment = 1 + +[node name="AttackLabel" type="Label" parent="InfoPanel/Labels"] +text = "0" + +[node name="LabelP" type="Label" parent="InfoPanel/Labels"] +text = "P" +horizontal_alignment = 1 + +[node name="PointsLabel" type="Label" parent="InfoPanel/Labels"] +text = "0" diff --git a/scenes/end_turn_button.tscn b/scenes/end_turn_button.tscn new file mode 100644 index 0000000..27d3858 --- /dev/null +++ b/scenes/end_turn_button.tscn @@ -0,0 +1,16 @@ +[gd_scene format=3] + +[ext_resource type="Script" path="src/UI/EndTurnButton.cs" id="1_script"] + +[node name="EndTurnButton" type="Control" script=ExtResource("1_script")] +custom_minimum_size = Vector2(160, 50) +layout_mode = 3 + +[node name="BtnEndTurn" type="Button" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_font_sizes/font_size = 16 diff --git a/sounds/battle/card_defence_empty.ogg b/sounds/battle/card_defence_empty.ogg new file mode 100644 index 0000000000000000000000000000000000000000..97c2baf9d1d94c08d505303af8957831b8d3b34b GIT binary patch literal 9648 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 literal 0 HcmV?d00001 diff --git a/sounds/battle/card_drop_1.ogg b/sounds/battle/card_drop_1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..7fa771de7227d05c962dcea2adc11c7da78d0b8c GIT binary patch literal 5268 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 literal 0 HcmV?d00001 diff --git a/sounds/battle/card_drop_2.ogg b/sounds/battle/card_drop_2.ogg new file mode 100644 index 0000000000000000000000000000000000000000..af7482a5f446d7ede54f939da775ea27658f62ac GIT binary patch literal 4913 zcmb7He>{}g_ka8dBcw>!VMl2*GHFCLHYGp8pv*9SHpz^MFtwSPBunxuEb?PajA1Ec zEJfRB*(H^q#bnIRjGxI0m1@%uYTM2CKBL`N`~Bnl`F!u|KKD7#IrpCPoO93nKIb_Q z7Um5pLZ8~$S9$wjGR)-LHwwlICyz%{jxu0|f>RxQgP@lO6y*1x6b`^E|7q|_SW+AQ zcy^Wj(&sHtX<3gZ5PO82JhI(t3l{XPSa|-I;G!L)9uK8O+W{dJ2(5O)LR8dI%JMpe z70dUp92|WWATt(~Zdw|D4S%Uh-wz3UU?FvT%=S&L59G=?E z8mnmrtbjml?A1(CbcwDesecdXsv=4zy1IyR7kwGY=)-oSlC^9fR~C3VAkdXwcyoRw zn~29q0~s$`RmT~>?|p|#(RKhz{vPChpuoETW~iJAYhKp3>=p`21aX1f@^tStBKt+i z{(0p6)f)+GHj1n;Vxo=rHan7!v&4twM~$=hy?1pN?7FXGb6;%Tuj5Iu_7wjY-COspJYRq# zCZ$I>5s7Nsazo@{Nd#*3E7Tk{mMwt^=@pMq+yUn z;%0eKLW3zDLx{dx+*w{cz-0*CG`LmERP34()q4IQ;2F&Fw7LvJz`Ub_Tvh(CKUf!cBq|86zc;%?L7}-iPvD^dn#%u={C1$kWFj z>|4l?ephJ{Dn3VrqH9uZQeezHuT@Zv?Lk)>XXot0^OL=Z2AiswNpjCo5R{_*c@~?b z=~L28F7u+*&1)+2GtKXs{n%%9&jKFm?g(w?8t!O2YGAJq;w@|>zSL~NOc~<}&NZ@1 zAPwAH1ri)xwTvjRsi@H0y($KBe54gc15}hJseit4FHP^SHWGc-@6RJaGB4+|Jef&; zC+PtfSfe*m$wRCOS^R`7J9u3hEr9c{zU(^i1cKotha;Pkh3)1g?{`2fZ}2DNxSY`K z&O`R*nYv#y^&GV5e`S5(wY5mc8KjH%TC!*@ORPup-cA{=L|60-FIo#- z#iI&Ms@aUYxM^PUpc|jZ7{sCTi11^y(CrD&M~Fk0rhw0xA`)B{DF721^&|6YJ83rSX$YG8xwF8?X>RDl43pvL@eoh?{_eEHNv7Vu2M_%p$*Y zmc5N9apI{0TJXEn3I2z!`K;R7x!ULIH_h+eZdm#A>#;UVHf!`9eLQTe?fm@IpIv#8 zqqnhhO^mU&D;76jZ%d&0pRPVST0Qqi%afKi%efZpSld-yl0-%d+MXsUOr6q8m>PeFPL;`;vL_ef??%n-`|{iGU-h+QKmX7b{lRuNCP(;E0|62W&W)`V z8oGwp)mx6ck1qljN;mr6z6&l^yhjRm}Y#*C}6&{#QJ$yzzP=)4q0#%q!e@8bAhfTqt zJBKKJ&7GVe)e1o_J`22VP!{fnG(xkg!++U$mS902Hm>h(ZUa`(Z5Bo-bvOYhGNN%t zk9uD)EsYemUY169HcpRF`s`-(L;IUmS2+L&^&HXICTRp6JA?dW94!T^FwzkqkQkN6yEACq8`9Vk<>oBPEKTh^CCAW7eHNOC-@*0d8q){BIK}5G3(S% zQ8DS_6%3#{iWo%mE?#mjzO#s-jlauF_QKPBC1~Pw9 zK8FS1;3;OpoY94Nu{BA?qJeWhgeB)l=~RE1Gu%Wc^c@uAI9qx+?Y z64b7Pgc8-G{Ea3n!z~)*Z>0i3AsP-UCaD?@pekfLxS}^g(BXCk2-Tiq zM0Es4)`O2Fg%1WmrQzj||JNY@s{X%+wa{a~N$BCy;X6;&HaSzzrl!>?L$^V3O$9k> z`^2>J*R9j~DMX@MRpCDDEQ*~^#MhIAZmv~T(!lQ;qxXYd5acmc$NCkq9j`u3{t^pCuPQ=N=cfW|bnAZ^5UVM9LCccsFkDoq{T$rEUxlabXP=oEIL5Sx}>pck}qtrDnawc*l9@vNfR z)?5WhRSiLe>_g60IHss+Uutjv>8I=9Z^t1t&DF_5B?BR`Ct!^ri!ECpDcPrW?HlP_ z!8CZ7wB?1Zwu5$(cE@j!x@-F_#ExC_X`99#Dwkd)s>*-Vz=636g1S|-SBsD?=ekv0 z(|gq1+&~2d%6X_=6>1MrbaJxp7ER78tXXg6KwKji!Gp5$a@7z294afz|Mrw6z;n34 ze|s?hLxx^~CdPb=IcOI5uD4t*=e^x$%w9+!Pt=}meqsngqL|vN(cWUNI^qWBa0uoO$Nwl^QBr?fw}5ai2G%?aC#|MMBKxAW{H1_|cyW zgFn5{P#9aWhgC!2Wd2DqK!_m2;$WjSzSp0=J0gB+w4hC(gFA02IF+1syz!yAht+^R

5~*$JNjq!f{W1&5Lm# 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 literal 0 HcmV?d00001 diff --git a/sounds/battle/card_drop_4.ogg b/sounds/battle/card_drop_4.ogg new file mode 100644 index 0000000000000000000000000000000000000000..65e8f3cb68cd84f1733f5f87a96aa072bb067d8b GIT binary patch literal 4661 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 literal 0 HcmV?d00001 diff --git a/sounds/battle/card_physics_damage.ogg b/sounds/battle/card_physics_damage.ogg new file mode 100644 index 0000000000000000000000000000000000000000..7337e84fe4af3661a37b6df9bd3777442f01e82d GIT binary patch literal 12282 zcmeHtcTm&8w(uq-AygBZp_+suRR{(|L`@(_R|o_FMX5@QA|Q&0h89C8(nUn1cN7q? zLy#^gT|g|T2sTu}Uhw+`y?5@t^Jd zK|O3e!}l5LuOmTGkHjCc2KxCP@IL6{8?qT{^oAPsjrfiDAb+nv<6y`?Uky_;XBa>O zz#`h>I#`3L3;-|y6x3|Uj_kSh<#A+mg@sfkho5_1V=;J)a}CP9yHze1*$35y;>kYJ z03ZNM1DzW(ZTkAw7bV$Qtb?3pH$NAjpqD8dzd7rH+Z#g0L(|ur=akT5u1JV@KtU7Y zr0AA}_CcVQ5pnDqIPl@Kq(Ghf+$d$nGDX!teP2ZO{)F6$v$-!T^MzNs02tJTAEu9T=Z>cA zk4RrhbIbj6%l@a4;@6`IC=f5Z4=MbK79lEvD;(|tkW49bmF^I2i%@cRsEI`wtvk%N zH`A%NX}8EfgaC;h3KU6gb4Qcz43hy#gMkt9T}e9!vfar4AUaA#ujmUz2;uy`>;M*e zeX8dQTUZcSTp`*_^baPe1E+8XpF5blba$h8?u&v-sdFQ32~OwMUaQPG7kY2wTwc&U z+2?to_cZRD?JPJum>YV}N;J3hPcA~@oM3I`FjOZymnYsO>k0MqAI=r5oOFT4x(?>f zR$P;UatWE|);3*p`U7tTXz0DlJ$d)qHa>+$Bs@45cumM1%6;?6dv{leUkBm+n+UC_ z4+WB&=LD|nd<;G+1?440C?jMjg`F19C?NG{lqe;qY_qP6wWrIjEe(rVp&SoD9OhrO zn83z)aR}vkA;Ov!CFxwvJL;GF_4~B>Nlp7b<2QBf7&B2)6Jw4&Wqh{D)dLd ze=W!2kW_b?LT{RiO@oTvu=d?W@{T3)kfb@JE{@XfL(+#wWPBYB-A4?3M;N|i;l7j2 zzPqkOxi&{V`SLGu{P4e84rDgOjVaG9|J`y@o*AVmTcxYGrmL-3PUYGBSCvKoVL1hU z-0~c5IfpyPNl{P8_D{&Ix>gi)=i-~||84xID6MG&I@C0zd-^ZoxY{-5Rm zq;@cI=qF=ZNYH|_j9}oD3N;;XHArUpxDJy2|)jb=^X;RKh%(mazI$}8_KBvupwyFpsi#P+9A##ZZZ<5ZYt%O`(HDb1po^s z2v|V!4&|S7Dsqtlz$o3@74k2P(tTlo;z%>TUhKM=xL>HwUA5C$vs z!Bf0lDe2lG9>L@4tml5&a|uQ2xn)&Vnh>Ial`V?FaQDmoTYY_<$-v*< z?yK|XB8t?xWtCMIMI5BeA;$+=z{imgP^hZv^>qrr4ytlr&G`fJ;v4wiPFe0O|Ej7g zXup9wK^?{q`^pyZhg{U^1LKF?$FqE0QPWWCod#`45Ih*b^U%h<6%A;4yQ?37lw1Q5 za3Ebs^0X$Eih4_!3Kx3XnpzTk=yXkuQejg~)hs&Ivb2P*Z>>|BO-B;4(yc4;C3Lz5 z60j7Fpq~pmo?U|gi;%bH&EK@=R7|Z&vw5Ksgi;*j*;Gn-^L?DmbQ0v~Y`1=u1J$i9 z_34M|GU =f_H;Gz{tj`PQZ{6~Z9@<9w*NDGX5laGM*nT~ESkh27GCH_aDU{#tIC zzg2o!ME+btSye!K$x2OS)j#>kZ&=||i2Gk|s`eH6@!4A?!gng27~}DL2e+!~&FyAB zTgMN3PC-nBxWt242C=Y*zkO?q0I(w%0L?nj!*Ll!^*m^!gY);xI1*X|^0|8OB^Xmn zjri1mSAQ7G397&Z)6X&f!Z&-I7RZ;}9jK z*kYaR{E7> z{=)}`idH&JnRN9(4Vmd~5SSHEPy!*MTmNmKTiZbJ_&8QtS5l|HMUB!?{;)r-Zh;Vk z>6(A~HIBLkLg4@O^RI;vS!-nd0S_&^(E@i!ullO&FvMM;}d1J$v%x{p)Y{ zr(ccVe_?2#t-Ic6!;3q;qXTU>dT-T~r4S&vZ&h|2Hox7HVKOEyv?(POQS?3|;sSO) zu*<3Qr`YMQUuox%`ujc(e6^!=>9RKcGiSq$UR}1>D^gX3i$>!BD;iG4WpJdF;m%C~ z0^Im`oR?d!ICGO|@sOY2l;X2TH0-ZkxAxxHv1aMSqxHj^Ceg$%YfA7E2^}0PjY#t9 z;LwOzn0Pw|JmBOEygoVhfdgfpVr6#2c4AaDB+`g*)(+{r*MI-swA&7G^g{o)mOUMo zDk+>oS7X!Z)>W25NjSoXawg&`3qJULY3}4$d8g<$G)8e~t5*jac2AIZ5_f=i((L2~ zRPGM}APT$SF$S!$B~yl89B-x_x#b6)$%O zLqdQQd^dAu|Cdd%(wMVT8^4^ViG7iME43Y&BKL0nghrn5@z-J|YuW*OY@ZvGZB>CG zZYf`l)wXF9iHpI*kC7A+m=uXm;bc+frjO?^PSO_|3~BZXnv8#B&bzhOL34hfonnL| z+R%t8XsIr8)Nf9ZUJv$E^K7VZ_Qiv;-A_i2gJaGLWZBtIH={H6S42+k{&bHUtJZ!q ze?#h`=waP+)sHths^8&QI|T%24nL5}=oY!jQ*LvPMHf{8K-`8!R1=*s3M8hpfr56* zv+0+ZCL$?D&nd}LMbykZiHap4`lWxJyL0~Ast8WnOqA(`{&RI+`a^cSkHTs#OV^9n z!^&D~PX{Qz^DB)!Ilp{ezDI3b_T148VX12*y-Airg>v33KR|S_xO<+*XV{kb&6krBLpRovmS4OJ67;)2cw^P$Y=s#C zy>`>`_G_L@8}UV0j%VS>(}#8MB2W2E7aOBqAQi|M4FE($0s|e#h9$5gbzSSiv7K(C zRSbqmePcbT_5sRPR79@>IBM67(GgM z)swotTff%SNv#Wg#rD~bW2xvJPumw{nn>!Vy!+hAXn0oqYHa#c^PK1fqe_#9uU`-9 zJ)$6XToQBW-@Gs^15)iU9cZ8DAm2|k4cQ}Jl!W(QD`(~dYlu}>?wR<}rQG4Hnd)KB zh*N>C3uT(MM;nvlo1){TcZJutbEDl{>M`|jcB#jNgg|E%!K*3@N5xRZZSi_78%@rb z>1%1OUOn#k@Ji&TdX4j{k!w-j=Uc~JZ8L0f>}UnY2{pz(<=L;wvp<@iWV(4wa)s+k z-i}G^9Scaq1?p6lF7H2P)I*aA65DTDX45&Qhr4@YR8H2L?OnzF&HP>^77`QJW-~YL zQXg?|=Hs0=b$Q)qgVGx3e>-|zT%S_Et^bkkb>c>*w}75VORm&5q!kIy+hr$Q98-dW zP0#O|UO0SIQKF)NDqsrThDvI&bwmQ2$U34U+igS~Ytb&SUD<3;Z%>AHt&X;|b_Z|} zvG3=3$oGbQj+e;KJg_zDTki5m@U1(SXp;TH*q#`BBD-b~t3a$nn+-+U8##!Qk2_sl z68W;BTxNIP&HV8Fn{G^Eww7M5Si2d{A|MoG@5}N~=58`PgXB=E`PMq;Es>wJQ2ipr zil}>gesN(-G3bB`XLi>uz)wTQMu}M*Eubm^mM4cGb0gY6Cjeg{W9Zdc#i-5>H}B-e zGe#aXBLUlgY&ak(wZcpgTZ69)9hh%@S@QVN@dqvancMG^j(PRpNN{*mbUIq??Grt} z>&xH1^xl`G<_44$#xBGcmkcMA&)Us2ZQeb1VmwsevDH3hS~&aReDW2)jQf52Y!BYJ z^s@%{n}`y9@nrJHyUV4OqBf*s)?N9J&_=48Xh>a+jgsqcEOxv0W)X0$&oGNQuWrg* zZV}pY$LK<81OR&?nd<}eM-dXH+pBLi%NocAz=a6q?(2C$LIlDGv_pcX!)gH7RmG1Q zaRJzBxJwn&AxK+q^!Bi+mxPe_Hn>Hain+8yg*`$%9+NU5K7%P?^}JjcT2;*Ma#Us? z$=-J3?Xosy!F~*Bv05i1>T@5*K2@gGM#4__6Z4y@27B^*c$YtZSH^`_mTKVp`033S z=-${wn;Z5A+Q_y)vhP!~f;*@kna4s8y^7)PV;Lhh9T>i4bF25w5ikuyuuwTgQ8$cE zF9g264wFWJkzsL1pp=-L=toc-q>86>c9}P(ioe7Q?#U9C>1y~qfK8RR5dFC!pHa}a53Hpdj9!)>*(_-?K^n)aRuZ_Rr6Dg07@ z&xx`fNac`N6mrwl&!z9&>I$waiJ8a*!#oZzA zgZ-|r?R#IZdcJJ4*2i_87~AqaV+PTOa{BAOK3|CO&~F)XF%EQt9Y>#RtQ-Hfr+mlf zr_SqjjwZkhH}-t;rJe|MKMd?io!QC$F!%|%Px0t*Y9mn{6ZtRMvk3XqYfoGFDJt!V ztsr&r7e4CrB%9h^6VR>@nE4TScW#NV^7}McfSTI%eyN#MEP#{W=dbTr25XGLk?qZ zZtLU{ZKlwqmy&w8Nm|FdY0|*<1JImPmx2NT#sSfVA{5Y5&g&F)vGQmMQjg1 zwNKeBHeZ=pec}wlFiz73s2UJi~OF>cy*F`i6+HwT+)XVG?XGbXLgwAQ5}TF#Rq zMWJ9AAA)9nq-+jWl_#W1S)jg!yh0%dtC=Z?z6nF*_gz!T%u(_1dDyOvCpFL-zVL)!H@ zIlL>|%Jdhylwj)lg^u9WzHxo*-ubo8qyBcY$^Ks_7tlR4xQGoZ6}HDXIYB1JyImSD zsD8eSCemO3R5(>&w?xkQK>{IlP^b!36ZcFLu*XxRe;hNrjP=@iikzk8frlpW@Ho&*S*uESkiu}|)dQxaM)XFV>P0_)V(i73>}jYF z{8B$JA9W=5WJy(1_?rPFi7 z!F9GQ1cM>8Hx_{KL;+_i!oCfGba+d|d#JM+B-?uCDTcY2eEYClC)d9d(G=imAc3-! zNV6}zU7%BTis)zL-rH)J)+pqB+tYlY-_m$rWS+*v@h7iVQ#yy`1gQ3_W}{+3PXMS7 zm=e|m>P6}WEG`pEU49*U>^(Q_lD_H%llUf`ZX@neW_HMU%Y0~fm+iWUYA2G|?!^An=g1Q*eEy?RYng*n3> zBW;Fls%zF8B{SuTbxCk37;L9P3*)$PHSL!os^4~JYsKk{v=j2@drJx5vBJK|!uWul zQj~9Qt*`F}P8Ph2uV>h7<&^4(P_Yywpdv8x4ls|o?`y}&UKqm$)w7&{%zWLjgLZF% zF3)V(Feh=&=z^O$L+4Dt+r1b1SE4T)UwCNNPu4CqJmtMRHX}Iv z)oZ7@dd(%@ufrL+Lb|^qv`l`*)i)(-5{ts?6H6DlcUtgI&rt{iZMu8 z(OTs4v!xNsMP+a0VZ+9O!kRBNqbJ#z@cEUg@1KIX=dcA!y6dJHLp`KlNqzJlYH z6CiD45aiv7*;J!5nhl*5%Qzg4zj90w7f&oQy4iGDL(wccnS%9{U9i(POA6ex{2u3f zyKeatN(Tm}h(%SLB=4rl6g6I#QwQr&tO}XtDW=L?>XW6rVL% z_b4~@aaT)Fqu7;8b8or!;P^pFHC9K-wQTKEC&b%Sbyde|SChWQ$7*i*1Ldn zOZp=_3Jm2Radjn2yEY$erQA#M$>$ohZbkA!L}mL>a_Y_eQ-6Up6FT0rb=sk;Gytlb zMPYiwO6$IF-gMLW2;p%seyz+;8EwMWt~~`<7Q6Bjap15*Bj&BY&{_ME#a(Vf$6ID# zqfSqq!5Oum)5nC^LZG=G0HysZNi`+=J#>Sqd1-nL>G{#Oc*q}+XETNTgzyogSg2M7 zoT`SdAS$R3Sb6#1J_hI1rJ@2Sj zyU~r9n*l$u0w&y%118G97WEFcOg8Uf%BAEYJVoPCh%Ss?tr(zCqAd`dZOZ(c3jpG= z0svn(Us1>&eL*0+ezE=BddXjHInf8ImMlLkbRnn3QeYMhhb5}a+=4sLwVS7S88*lV zxTZ|b<}ht`AC+uugZ}&qr``U3IW)dyTJ<4 zCt=*Pfwj)80J`ez?}nTk8qKb~tN?I|(NzFmr5P~ow?UeLtdQ%gZ&<9q3V z8nUYz>8g-oXy{lWyVrcj)2sInxvcs6k{vi-JX<_JUCatzK=vaM1X_$Gf~AW9dmus; z&dukd;lp`X`+H=K45tjRh?0#9Eel84C;|HZ#sUMJ6C(x&Fcj#|J9{ac!0Ix8v7(qJ zx2E`bbs0mpi=5X~w%db-#(J|_w_OaaGPyEKa;lI$x)2SEfB1NRdTJA`D6tH+Mtq>d z*D8LoBx}9`fEYct9+(jWAFx5tWjX+^EUEfr=&g!+l6u3|!#5UA$n6ioz3zMX%R64? zY#d7K!DN4oP};?kay5Amq=|tv`tq(Fp3cwrd4{(}&YB2N#3y!q`MG{FX7aA##v&Sc z?Aw?mKU3`)mfXKl`L(`Vm)PQ%-t}#>dD!{L>B}zyx-W9@*$8PH{xZ|Py}n4MsH%)1 z?I;EjS6EiX&2g^ntn%zMZ0?}fHR_3J*R=^}6SHIFyt>{K4$Kb>%%12xTQJAeA`0M@ zuk9Xoe`~Ra0L)EA1V+2&Qu5mY6(@}YdSEMHLbfu|901wj2XNq^=nvP3Ll5sx#$ie#r-NtFjjN2y3+|*$9-v3oF^Vl0G#6|umBil zmpFdk>u#HR%!iYt`YLmI=W59kC0XKoJW-2gr^)qe^3s8BUzo8XDr>f_a$_)J;~*Q9 z(c-qrRd>m~@z^k*;mPf2xmiT{tLB~2A9v`c!LaRJxVg;3NwLq9%Ao4#X+PQ1Gcp0`w5b zlxB70*KyH9&rYTrAIk6Tn->rJo^2-UQb##ACK|_n=$LiI^ZH(;h{4mH1OzB6$&vDxlQxqk*bbrYv}EC4HM5ksMuJS~YT zuVGcZjT`rrOe)R1neIp4hKJe8cE9z~8ac1=JjXrA_j;(kia+{V?4!2EO3x4Cm!lpW z8Y~uq3u0R}5hnnd0GWJND?idR< z4Hte=8urHiX?mE5f{NIMqAyCGk|RflKWH3#k2NGEuNf8Gu33r+9If?}MYq_i7Rlo2N3ToN^jrhXR@zP(9;tY*vdEw2aj89md&1|Lz!BaoixKo01dHxwUcjF^!ii<& z6i$ou$KS6Wj7vdJTfmI7eWpIH84=iM@j8wdq21g3YHOKbNXn>C^wK$eqOm#EVc$j4 z1bo2!3{LMG(>g{oo`i?~uyLy8NO!ntaiv8`wu%1TuU~Tf1!!YKnwr3+-mBBJwxYB& zUM!m{n-#+vOu-dTCje&RxdrW;c3j# zU&NO7TVC9H-&<>|y7Ol1 zevZwW_GRJS*sH~(t=1lNke_XPIr7{>^nCtufw?JO$bZk0h=j9J+rkmr7>{U>#+QjDhnd`} z65;J$bCL0)wuqM+Vc{}neEwh+lCxSl=}6YPVKV4XwcYZx|7}|;;WDVTMM?JcuVpnm zm?wH7l=kH*MkU$^M)}g`(VktCLszbb4enGMO9}Qg)Zd^aw0ma zz=}~%aTb(rZzHmdAJ`cHz;Hm)9DtTHvSz|1hg;eN`Mqt8%>kWsDO(Z8QGHL$w$yeG z)#t|=YEtvZpd=NM!wo0=M|Lpej&B4hJO>(hwqML%iOe{ zV~K$+*?HriN#2vBfK?Y49bvXD^)ok{Ps{eDI79|#T@dzIoLA)Tk9GHg?R{c^A+C~4 z@}YntXuY&m&`}cxIlx1XD3g8P3v!V)_i_1`jU9{}u;H z32Znk_sFqVovO2MH|c&%XHz@$VBjVi(kW2Hmkb~=AzdW&z(`XF0PUb% z=FrQ_Nof+F2Cpd?Xo2p<9yHiH02Uq04jj95SAfz&{+bIbaR=F2Lf+Cs@Ss!8ifk0@ zqVByZzyNoALoZ&ty+{7=>Z5un7$lE}XZm*N_S1g;^0iO&O9<7zrhDeXo}7fQKl%z} zh9vg4E?D9VUpz;~?Kba`#6Z_M(EAFOUjZeX9WUlQ%AJ6`x5jo_jT$}s`SgnwS08{{q0J&Yx%km z`miea&;w?Uh&CiBgKJ0uiirT)iSm^3&{0?z351)j4YZsG;#Nj*uk5h5lvM)DSr6Zp zzZ0`R{JddJXF4IWHGS&W=q*^)f!&{rUG#n4w=AIj>*gP9?1wX;#}v?tlFKHt;n3Pu z<;8Sqs`lrU5(Cr?13ZOuAdSi{i!aRfGS_KSZzN``ngS&U@U-z6DP0m=N)I`=)=U&9 z+K~!e0nHT7gHt-hZJ-&mZkj?VFpq5q7?bJQHLH4pc2>H6JJan|QU7k+z%edEb^Oq- z1aEA=@q@CEOBovbPE#f329B|H(eKcAnujVr$+aKlQDIxMe;(f6dR-#y!9nxz)ZmcQ zswdBnT)K!j|9MxMqDtPwscoWS&2eEWG=mC=44}@6*0B+(j-!rJF&!M?cq(QDB_NG7 zGbf43#!1ms1j3j-8SOUoOHq}NuJ$ku!D5uu8XeKgT4mx-i*0CPbUflR8Sof#Ql7b-*h4k~mJ8762Fd z9OiHYW-swTzYVH^d?^a@DTij(P1IjLF#yPt69RiEp^O|*^cs1-5uTxl| zrO!>AN=?x|8-vPNtUg<`h7}7b`_IVjdw=8l*I|6;>SvyWe*fO>5gx6+Ng4eV`Osr$ zL^^(`fC~Lx?Q=&$2^2vwZKj_m&#|mb|8S@9&h)V}bDD8-;Ld z)0bgKp@%(P5X#Cpxt~2pbzcoHsn4ez?Byu!T3egb*Xwt$<=b9&VW2MOfdHvFTAFph z2eWkPrmJ3es??~S;GXK%6RhbZQ`Y^dt#qk^t`ZZuT$t$*R=bGLN1J0NoA=m_wB)}W p2>F?`sm(ja2uRrTXES%Ug$#Shb6LEw|NAf^7&-v{YY-O~|33%{p3DFM literal 0 HcmV?d00001 diff --git a/sounds/battle/card_physics_defence.ogg b/sounds/battle/card_physics_defence.ogg new file mode 100644 index 0000000000000000000000000000000000000000..e30b214603177a47990477d101268c531cda6aa2 GIT binary patch literal 13546 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 btkgjo|WW=uRPa5(APNl5T1tNo_XAe00 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 bk>fUR%;1}3E-o%FrOlm`kj>ft7zq3y331&X literal 0 HcmV?d00001 diff --git a/sounds/battle/card_pick_1.ogg b/sounds/battle/card_pick_1.ogg new file mode 100644 index 0000000000000000000000000000000000000000..49d8a86be1937b4f74d952526c74f291874d443f GIT binary patch literal 5355 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 iaVF2#4r<--Al7KK>q{p6C9KP literal 0 HcmV?d00001 diff --git a/sounds/battle/card_pick_2.ogg b/sounds/battle/card_pick_2.ogg new file mode 100644 index 0000000000000000000000000000000000000000..f27a4cdd9bfaac29cb75b82e5b475870a07f60dd GIT binary patch literal 5914 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 literal 0 HcmV?d00001 diff --git a/sounds/battle/card_pick_3.ogg b/sounds/battle/card_pick_3.ogg new file mode 100644 index 0000000000000000000000000000000000000000..6084701a3f158de47a233b2aa90532b7121e7936 GIT binary patch literal 4939 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 ZA`e#>CMa*)4%UBl+_>S7al-`le*;EJS0?}f literal 0 HcmV?d00001 diff --git a/sounds/battle/card_pick_4.ogg b/sounds/battle/card_pick_4.ogg new file mode 100644 index 0000000000000000000000000000000000000000..809152aae4b0d9d83c65b135e586ed4191a015c1 GIT binary patch literal 5578 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 yZR literal 0 HcmV?d00001 diff --git a/sounds/battle/card_show_actions.ogg b/sounds/battle/card_show_actions.ogg new file mode 100644 index 0000000000000000000000000000000000000000..dba95d496bb45237529c2caf2eba678e9a403ff0 GIT binary patch literal 11320 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 D|9%VC literal 0 HcmV?d00001 diff --git a/src/Actions/AbstractBattleAction.cs b/src/Actions/AbstractBattleAction.cs new file mode 100644 index 0000000..826ed9a --- /dev/null +++ b/src/Actions/AbstractBattleAction.cs @@ -0,0 +1,87 @@ +using Cthangover.CardBattle.UI; +using Cthangover.Core.Battle.Actions; +using Cthangover.Core.Characters; +using Godot; + +namespace Cthangover.CardBattle.Actions +{ + ///

+ /// Base class for animated battle actions that move, rotate, and scale card controls + /// through a sequence of visual phases. Subclasses implement DoInternalAction to advance + /// a frame-by-frame animation state machine driven by 's + /// per-frame polling loop (while (!DoAction()) await frame;). + /// Captures source/target positions and rotations in so that + /// can restore them regardless of what the animation did. + /// + public abstract class AbstractBattleAction : IBattleAction + { + protected double Timestamp { get; set; } = -1; + protected float Speed { get; set; } + protected ActionCharacter Action { get; set; } + protected CharacterCardNode Source { get; set; } + protected CharacterCardNode Target { get; set; } + protected Control SourceControl { get; set; } + protected Vector2 SourcePos { get; set; } + protected float SourceRot { get; set; } + protected Control TargetControl { get; set; } + protected Vector2 TargetPos { get; set; } + protected float TargetRot { get; set; } + + /// + /// Advances the animation by one frame. Called in a loop by + /// with await root.ToSignal(frame) between calls. Returns true when the animation + /// has completed all phases and the action result has been applied. + /// Returns true early if any required node became null (card was destroyed mid-animation). + /// + /// true when the animation is finished or cannot proceed. + public bool DoAction() + { + if (Source == null || Target == null || Action == null || SourceControl == null || TargetControl == null) + return true; + return DoInternalAction(); + } + + protected abstract bool DoInternalAction(); + protected abstract void DoInternalStart(); + protected abstract void DoInternalEnd(); + + protected AbstractBattleAction(CharacterCardNode source, CharacterCardNode target, ActionCharacter action, float speed = 1) + { + Source = source; + Target = target; + Action = action; + Speed = speed; + } + + /// + /// Captures the current positions, rotations, and references of both source + /// and target cards before the animation begins, then delegates to the subclass + /// for any additional setup (e.g. resetting timestamps). + /// + public void DoStart() + { + if (Source == null || Target == null || Action == null) + return; + + SourceControl = Source.GetControlNode(); + SourcePos = SourceControl.GlobalPosition; + SourceRot = SourceControl.Rotation; + TargetControl = Target.GetControlNode(); + TargetPos = TargetControl.GlobalPosition; + TargetRot = TargetControl.Rotation; + + DoInternalStart(); + } + + /// + /// Restores source and target card controls to their original positions, rotations, and visual state + /// captured during . Delegates specific cleanup to . + /// + public void DoEnd() + { + if (Source == null || Target == null || Action == null) + return; + DoInternalEnd(); + } + } +} diff --git a/src/Actions/ActionPhase.cs b/src/Actions/ActionPhase.cs new file mode 100644 index 0000000..afb378a --- /dev/null +++ b/src/Actions/ActionPhase.cs @@ -0,0 +1,26 @@ +namespace Cthangover.CardBattle.Actions +{ + /// + /// Phases of the animated battle action state machine used by + /// and . Each tick, the action evaluates the current phase, + /// interpolates card positions/rotations/scales, and transitions to the next phase when its + /// duration elapses. The Recover phase signals completion by returning true from DoInternalAction. + /// + public enum ActionPhase + { + /// Initial delay before any movement. + Wait, + /// Source card rotates toward target and begins slight movement. + Prepare, + /// Source card travels from its position to the target along a movement pattern. + MoveForAttack, + /// Damage/effect is applied to the target card at the point of impact. + AttackImpact, + /// Target card shows knockback, squashing, and shaking reaction. + TargetReaction, + /// Source card returns toward its original position. + MoveForBack, + /// All cards smoothly return to their original transforms. + Recover + } +} diff --git a/src/Actions/CardBattleActionProvider.cs b/src/Actions/CardBattleActionProvider.cs new file mode 100644 index 0000000..819f850 --- /dev/null +++ b/src/Actions/CardBattleActionProvider.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using Cthangover.Core.Battle.Actions; +using Cthangover.Core.Utils; + +namespace Cthangover.CardBattle.Actions +{ + /// + /// Registry that maps action type IDs ("physics/attack", "physics/defence", "physics/stun") + /// to their respective implementations. Created by + /// and registered with at battle startup so that actions resolved + /// during drag-and-drop or enemy AI turns go through the correct damage/defence/stun logic. + /// + public class CardBattleActionProvider : IActionExecutorProvider + { + private readonly Dictionary _executors; + + /// + /// Initializes the provider with the three card-battle action executors: + /// for attacks, for defence buffs, + /// and for stun effects. + /// + public CardBattleActionProvider() + { + _executors = new Dictionary + { + ["physics/attack"] = new PhysicsDamageActionCard(), + ["physics/defence"] = new PhysicsDefenceActionCard(), + ["physics/stun"] = new PhysicsStunActionCard(), + }; + } + + /// + /// Looks up the for the given . + /// Logs a warning and returns null if no executor is registered, allowing the caller + /// to gracefully skip unrecognized actions. + /// + public IActionExecutor GetExecutor(string actionId) + { + if (!_executors.TryGetValue(actionId, out var executor)) + GameLogger.Log("CARD_BATTLE", $"No executor registered for action '{actionId}'", LogLevel.Warning); + return executor; + } + } +} diff --git a/src/Actions/PhysicsAttackAction.cs b/src/Actions/PhysicsAttackAction.cs new file mode 100644 index 0000000..1c4149b --- /dev/null +++ b/src/Actions/PhysicsAttackAction.cs @@ -0,0 +1,357 @@ +using Cthangover.Core.Battle; +using Cthangover.Core.Battle.Actions; +using Cthangover.Core.Utils; +using Cthangover.Core.Characters; +using Cthangover.CardBattle.UI; +using Godot; + +namespace Cthangover.CardBattle.Actions +{ + /// + /// Animated battle action that moves the source card toward an enemy target, applies damage/defence + /// via , and animates the target's knockback/squash reaction. + /// The source card follows one of five randomly-selected trajectories + /// (direct, arc, zigzag, spiral, dash). This is the visual counterpart of + /// — it handles the presentation while the executor handles the stat changes. + /// Created by for enemy turns. + /// + public class PhysicsAttackAction : AbstractBattleAction + { + /// + /// Pre-defined movement trajectory patterns for the source card's approach to the target. + /// Selected randomly at construction to add visual variety to enemy attack animations. + /// + public enum MovementPattern + { + Direct, + Arc, + Zigzag, + Spiral, + Dash + } + + private ActionPhase phase = ActionPhase.Wait; + private float attackRotation; + private bool damageApplied; + private bool stunEffectSpawned; + + private Vector2 targetOriginalPos; + private Vector2 targetOriginalScale; + private Vector2 knockbackDirection; + private float targetReactionIntensity = 0.3f; + + private Vector2 knockbackPos; + private float shake; + private Vector2 squashScale; + + private float waitDuration = 0.2f; + private float prepareDuration = 0.3f; + private float moveForAttackDuration = 0.4f; + private float attackImpactDuration = 0.3f; + private float targetReactionDuration = 0.4f; + private float moveForBackDuration = 0.5f; + private float recoverDuration = 0.3f; + + private MovementPattern movementPattern = MovementPattern.Direct; + private Vector2 movementControlPoint; + + /// + /// Creates an attack animation from to + /// using the stats defined in . The is + /// randomly selected on construction to make enemy attacks visually distinct. + /// acts as a multiplier on all phase durations. + /// + public PhysicsAttackAction(CharacterCardNode source, CharacterCardNode target, ActionCharacter action, float speed = 1) + : base(source, target, action, speed) + { + movementPattern = (MovementPattern)GD.RandRange(0, 5); + } + + protected override void DoInternalStart() + { + Timestamp = Time.GetTicksUsec() / 1_000_000.0; + damageApplied = false; + stunEffectSpawned = false; + phase = ActionPhase.Wait; + targetOriginalScale = TargetControl.Scale; + } + + private float GetPhaseProgress() + { + float elapsed = (float)(Time.GetTicksUsec() / 1_000_000.0 - Timestamp) * Speed * (float)Engine.TimeScale; + float duration = GetCurrentPhaseDuration(); + return Mathf.Clamp(elapsed / duration, 0f, 1f); + } + + private float GetCurrentPhaseDuration() + { + switch (phase) + { + case ActionPhase.Wait: return waitDuration; + case ActionPhase.Prepare: return prepareDuration; + case ActionPhase.MoveForAttack: return moveForAttackDuration; + case ActionPhase.AttackImpact: return attackImpactDuration; + case ActionPhase.TargetReaction: return targetReactionDuration; + case ActionPhase.MoveForBack: return moveForBackDuration; + case ActionPhase.Recover: return recoverDuration; + default: return 1f; + } + } + + private void ResetTimestamp() + { + Timestamp = Time.GetTicksUsec() / 1_000_000.0; + } + + protected override bool DoInternalAction() + { + float progress = GetPhaseProgress(); + + switch (phase) + { + case ActionPhase.Wait: + if (progress >= 1f) + { + phase = ActionPhase.Prepare; + ResetTimestamp(); + targetOriginalPos = TargetControl.GlobalPosition; + targetOriginalScale = TargetControl.Scale; + knockbackDirection = (TargetControl.GlobalPosition - SourceControl.GlobalPosition).Normalized(); + InitializeMovementPattern(); + } + break; + + case ActionPhase.Prepare: + { + float prepareProgress = EaseOutQuad(Mathf.Min(progress, 1f)); + Vector2 preparePosition = GetMovementPosition(prepareProgress * 0.1f); + SourceControl.GlobalPosition = SourcePos.Lerp(preparePosition, prepareProgress); + SourceControl.Rotation = Mathf.Lerp(SourceRot, TargetRot, prepareProgress * 0.3f); + + if (progress >= 1f) + { + phase = ActionPhase.MoveForAttack; + ResetTimestamp(); + } + break; + } + + case ActionPhase.MoveForAttack: + { + float attackProgress = EaseInOutQuad(Mathf.Min(progress, 1f)); + Vector2 targetPosition = GetMovementPosition(attackProgress); + SourceControl.GlobalPosition = targetPosition; + SourceControl.Rotation = Mathf.Lerp(SourceRot, TargetRot, attackProgress); + + if (progress >= 1f) + { + phase = ActionPhase.AttackImpact; + ResetTimestamp(); + attackRotation = (float)GD.RandRange(-35f, 35f) * Mathf.Pi / 180f; + } + break; + } + + case ActionPhase.AttackImpact: + { + float impactProgress = EaseInOutQuad(Mathf.Min(progress * 3f, 1f)); + SourceControl.Rotation = Mathf.Lerp(TargetRot, attackRotation, impactProgress); + + if (!damageApplied && progress >= 0.3f) + { + damageApplied = true; + var result = ActionExecutorHub.Instance.Execute(Action, Source.Card, Target.Card); + Source.UpdateInfo(); + Target.UpdateInfo(); + + if (result.Result) + { + if (result.Target.Damage > 0) + ShowDamageBehaviour.SpawnDamage(result.Target.Damage, TargetControl, TargetControl.GlobalPosition); + + var defenceLost = -result.Target.Defence; + if (defenceLost > 0) + ShowDamageBehaviour.SpawnDefence(defenceLost, TargetControl, TargetControl.GlobalPosition); + } + else + { + GameLogger.Log("CARD_ACT", + $"PhysicsAttackAction: failed to execute action {Action.Name} (source={Source.Card.Name} target={Target.Card.Name})", + LogLevel.Error); + } + } + + if (progress >= 1f) + { + phase = ActionPhase.TargetReaction; + ResetTimestamp(); + } + break; + } + + case ActionPhase.TargetReaction: + { + float reactionProgress = EaseInOutQuad(Mathf.Min(progress, 1f)); + + bool isStun = Action != null && Action.GetInt("Turn", 0) > 1; + float currentKnockback = targetReactionIntensity; + if (isStun) + currentKnockback *= 2.5f; + + knockbackPos = targetOriginalPos + knockbackDirection * currentKnockback; + TargetControl.GlobalPosition = targetOriginalPos.Lerp(knockbackPos, reactionProgress); + + squashScale = targetOriginalScale; + float currentSquash = isStun ? 0.35f : 0.2f; + squashScale.X *= 1f - reactionProgress * currentSquash; + squashScale.Y *= 1f + reactionProgress * (currentSquash * 0.5f); + TargetControl.Scale = squashScale; + + float currentShakeMultiplier = isStun ? 1.8f : 1f; + shake = Mathf.Sin(reactionProgress * Mathf.Pi * (isStun ? 6f : 4f)) * (1f - reactionProgress) * 0.05f * currentShakeMultiplier; + TargetControl.Rotation = shake * 30f * Mathf.Pi / 180f; + + if (progress >= 1f) + { + phase = ActionPhase.MoveForBack; + ResetTimestamp(); + } + break; + } + + case ActionPhase.MoveForBack: + { + float backProgress = EaseOutQuad(Mathf.Min(progress, 1f)); + Vector2 backPosition = GetMovementPosition(1f - progress); + SourceControl.GlobalPosition = backPosition.Lerp(SourcePos, backProgress); + SourceControl.Rotation = Mathf.Lerp(attackRotation, SourceRot, backProgress); + + TargetControl.GlobalPosition = knockbackPos.Lerp(targetOriginalPos, backProgress); + TargetControl.Scale = squashScale.Lerp(targetOriginalScale, backProgress); + float currentShake = Mathf.Sin(backProgress * Mathf.Pi) * shake * (1f - backProgress); + TargetControl.Rotation = Mathf.Lerp(currentShake * 30f * Mathf.Pi / 180f, TargetRot, backProgress); + + if (progress >= 1f) + { + phase = ActionPhase.Recover; + ResetTimestamp(); + } + break; + } + + case ActionPhase.Recover: + { + float recoverProgress = EaseOutQuad(Mathf.Min(progress, 1f)); + + SourceControl.GlobalPosition = SourceControl.GlobalPosition.Lerp(SourcePos, recoverProgress); + SourceControl.Rotation = Mathf.Lerp(SourceControl.Rotation, SourceRot, recoverProgress); + TargetControl.GlobalPosition = TargetControl.GlobalPosition.Lerp(TargetPos, recoverProgress); + TargetControl.Rotation = Mathf.Lerp(TargetControl.Rotation, TargetRot, recoverProgress); + TargetControl.Scale = TargetControl.Scale.Lerp(targetOriginalScale, recoverProgress); + + if (progress >= 1f) + return true; + break; + } + + default: + return true; + } + + return false; + } + + protected override void DoInternalEnd() + { + if (SourceControl != null) + { + SourceControl.GlobalPosition = SourcePos; + SourceControl.Rotation = SourceRot; + } + if (TargetControl != null) + { + TargetControl.GlobalPosition = TargetPos; + TargetControl.Rotation = TargetRot; + TargetControl.Scale = targetOriginalScale; + } + } + + private void InitializeMovementPattern() + { + Vector2 direction = (TargetPos - SourcePos).Normalized(); + Vector2 perpendicular = new Vector2(-direction.Y, direction.X); + + switch (movementPattern) + { + case MovementPattern.Arc: + movementControlPoint = SourcePos + direction * 0.5f + perpendicular * 2f; + break; + + case MovementPattern.Zigzag: + movementControlPoint = SourcePos + direction * 0.5f + perpendicular * 1.5f; + break; + + case MovementPattern.Spiral: + movementControlPoint = TargetPos + perpendicular * 1.5f; + break; + + case MovementPattern.Dash: + movementControlPoint = SourcePos + direction * 0.3f; + break; + + default: + movementControlPoint = TargetPos; + break; + } + } + + private Vector2 GetMovementPosition(float progress) + { + switch (movementPattern) + { + case MovementPattern.Direct: + return SourcePos.Lerp(TargetPos, progress); + + case MovementPattern.Arc: + return SourcePos.Lerp(movementControlPoint, progress) + .Lerp(movementControlPoint.Lerp(TargetPos, progress), progress); + + case MovementPattern.Zigzag: + if (progress < 0.5f) + return SourcePos.Lerp(movementControlPoint, progress * 2f); + else + return movementControlPoint.Lerp(TargetPos, (progress - 0.5f) * 2f); + + case MovementPattern.Spiral: + { + Vector2 toTarget = TargetPos - SourcePos; + float angle = progress * Mathf.Pi * 2f; + float radius = (1f - progress) * 1.5f; + Vector2 spiralOffset = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * radius; + return SourcePos.Lerp(TargetPos, progress) + spiralOffset; + } + + case MovementPattern.Dash: + if (progress < 0.3f) + return SourcePos.Lerp(movementControlPoint, progress / 0.3f); + else if (progress < 0.7f) + return movementControlPoint; + else + return movementControlPoint.Lerp(TargetPos, (progress - 0.7f) / 0.3f); + + default: + return SourcePos.Lerp(TargetPos, progress); + } + } + + private float EaseOutQuad(float t) + { + return 1f - (1f - t) * (1f - t); + } + + private float EaseInOutQuad(float t) + { + return t < 0.5f ? 2f * t * t : 1f - Mathf.Pow(-2f * t + 2f, 2f) / 2f; + } + } +} diff --git a/src/Actions/PhysicsDamageActionCard.cs b/src/Actions/PhysicsDamageActionCard.cs new file mode 100644 index 0000000..cdafbf4 --- /dev/null +++ b/src/Actions/PhysicsDamageActionCard.cs @@ -0,0 +1,55 @@ +using Cthangover.Core.Characters; +using Godot; + +namespace Cthangover.CardBattle.Actions +{ + + /// + /// Executor that applies physical damage from an attack card to a target. + /// Calculates damage as card_attack_multiplier × user_attack_stat, reduces it by + /// the target's current defence value (which is consumed in the process), and applies the + /// remainder to the target's health. Invokes OnTakeDamage/OnDealDamage hooks + /// on both characters' so that damage-modifying status effects + /// can intercept and adjust the values before they are applied. + /// Registered in under "physics/attack". + /// + public class PhysicsDamageActionCard : ActionBase + { + + /// + /// Unique identifier registered in as "physics/attack". + /// + public override string ID => "PhysicsDamageActionCard"; + + /// + /// Resolves the attack: deducts action points from via CheckRequiredAndUsePoint, + /// computes raw damage from the card's attack multiplier and the user's attack stat, + /// applies status-effect damage hooks, consumes 's defence shield first, + /// then subtracts remaining damage from health. Returns a with + /// the actual damage and defence-loss deltas for the floating text display. + /// + public override ChangedAttributes Execute(ActionCharacter actionCard, Character user, Character target) + { + if (target == null || user == null || !CheckRequiredAndUsePoint(actionCard, user)) + return new ChangedAttributes { Result = false }; + + var damageDelta = Mathf.RoundToInt(actionCard.GetFloat("Attack", 1f) * user.Attributes.Attack.Value); + target.StatusEffectQueue.OnTakeDamage(user, ref damageDelta); + user.StatusEffectQueue.OnDealDamage(target, ref damageDelta); + + var defenceDelta = 0; + var ignoreArmor = user.HasPassiveEffect("IgnoreArmor"); + if (!ignoreArmor && target.Attributes.Defence.Value > 0) + { + defenceDelta = target.Attributes.Defence.Value >= damageDelta ? damageDelta : target.Attributes.Defence.Value; + damageDelta -= defenceDelta; + } + target.Attributes.Defence.Value -= defenceDelta; + target.Attributes.Health.Value -= damageDelta; + + return new ChangedAttributes { Result = true, Target = { Damage = damageDelta, Defence = -defenceDelta}}; + } + + } + +} diff --git a/src/Actions/PhysicsDefenceAction.cs b/src/Actions/PhysicsDefenceAction.cs new file mode 100644 index 0000000..f5fe6a6 --- /dev/null +++ b/src/Actions/PhysicsDefenceAction.cs @@ -0,0 +1,215 @@ +using Cthangover.Core.Battle; +using Cthangover.Core.Battle.Actions; +using Cthangover.Core.Characters; +using Cthangover.CardBattle.UI; +using Godot; + +namespace Cthangover.CardBattle.Actions +{ + /// + /// Animated battle action that plays a self-buff or ally-support animation: the source card + /// briefly rotates toward the target, applies a defence boost via , + /// and shows a subtle block/stagger reaction on the target. Used for both ToSelf and + /// ToAlias action types. This is the visual counterpart of . + /// Created by for enemy self-buff/support turns. + /// + public class PhysicsDefenceAction : AbstractBattleAction + { + private ActionPhase phase = ActionPhase.Prepare; + private float defendRotation; + private bool effectApplied; + + private Vector2 targetOriginalScale; + private Color targetOriginalColor; + + private Vector2 blockScale; + private Vector2 blockKnockback; + private Color blockColor; + + private float prepareDuration = 0.3f; + private float attackImpactDuration = 0.4f; + private float targetReactionDuration = 0.3f; + private float moveForBackDuration = 0.4f; + private float recoverDuration = 0.3f; + + /// + /// Creates a defence/support animation. Both and + /// can be the same card (for self-buffs) or different (for ally support). + /// multiplies all phase durations. + /// + public PhysicsDefenceAction(CharacterCardNode source, CharacterCardNode target, ActionCharacter action, float speed = 1) + : base(source, target, action, speed) { } + + protected override void DoInternalStart() + { + Timestamp = Time.GetTicksUsec() / 1_000_000.0; + effectApplied = false; + phase = ActionPhase.Prepare; + targetOriginalScale = TargetControl.Scale; + targetOriginalColor = TargetControl.Modulate; + } + + private float GetPhaseProgress() + { + float elapsed = (float)(Time.GetTicksUsec() / 1_000_000.0 - Timestamp) * Speed * (float)Engine.TimeScale; + float duration = GetCurrentPhaseDuration(); + return Mathf.Clamp(elapsed / duration, 0f, 1f); + } + + private float GetCurrentPhaseDuration() + { + switch (phase) + { + case ActionPhase.Prepare: return prepareDuration; + case ActionPhase.AttackImpact: return attackImpactDuration; + case ActionPhase.TargetReaction: return targetReactionDuration; + case ActionPhase.MoveForBack: return moveForBackDuration; + case ActionPhase.Recover: return recoverDuration; + default: return 1f; + } + } + + private void ResetTimestamp() + { + Timestamp = Time.GetTicksUsec() / 1_000_000.0; + } + + protected override bool DoInternalAction() + { + float progress = GetPhaseProgress(); + + switch (phase) + { + case ActionPhase.Prepare: + { + float prepareProgress = EaseOutQuad(Mathf.Min(progress, 1f)); + SourceControl.Rotation = Mathf.Lerp(SourceRot, TargetRot, prepareProgress); + + if (progress >= 1f) + { + phase = ActionPhase.AttackImpact; + ResetTimestamp(); + defendRotation = (float)GD.RandRange(-15f, 15f) * Mathf.Pi / 180f; + + targetOriginalScale = TargetControl.Scale; + targetOriginalColor = TargetControl.Modulate; + } + break; + } + + case ActionPhase.AttackImpact: + { + float impactProgress = EaseInOutQuad(Mathf.Min(progress * 2f, 1f)); + SourceControl.Rotation = Mathf.Lerp(TargetRot, defendRotation, impactProgress); + + blockScale = targetOriginalScale * (1f + impactProgress * 0.1f); + TargetControl.Scale = blockScale; + + blockColor = targetOriginalColor; + blockColor.G = Mathf.Min(1f, blockColor.G + impactProgress * 0.3f); + blockColor.B = Mathf.Min(1f, blockColor.B + impactProgress * 0.2f); + TargetControl.Modulate = blockColor; + + if (!effectApplied && progress >= 0.4f) + { + effectApplied = true; + var result = ActionExecutorHub.Instance.Execute(Action, Source.Card, Target.Card); + Source.UpdateInfo(); + Target.UpdateInfo(); + + if (result.Result && result.Target.Defence > 0) + ShowDamageBehaviour.SpawnDefence(result.Target.Defence, TargetControl, TargetControl.GlobalPosition); + } + + if (progress >= 1f) + { + phase = ActionPhase.TargetReaction; + ResetTimestamp(); + } + break; + } + + case ActionPhase.TargetReaction: + { + float reactionProgress = EaseInOutQuad(Mathf.Min(progress * 1.5f, 1f)); + + blockKnockback = TargetPos + (TargetPos - SourcePos).Normalized() * 0.1f; + TargetControl.GlobalPosition = TargetPos.Lerp(blockKnockback, reactionProgress * 0.5f); + + TargetControl.Scale = blockScale.Lerp(targetOriginalScale, reactionProgress); + + if (progress >= 1f) + { + phase = ActionPhase.MoveForBack; + ResetTimestamp(); + } + break; + } + + case ActionPhase.MoveForBack: + { + float backProgress = EaseOutQuad(Mathf.Min(progress, 1f)); + SourceControl.GlobalPosition = TargetPos.Lerp(SourcePos, backProgress); + SourceControl.Rotation = Mathf.Lerp(defendRotation, SourceRot, backProgress); + + TargetControl.GlobalPosition = TargetControl.GlobalPosition.Lerp(TargetPos, backProgress); + TargetControl.Scale = TargetControl.Scale.Lerp(targetOriginalScale, backProgress); + + Color currentColor = TargetControl.Modulate; + TargetControl.Modulate = currentColor.Lerp(targetOriginalColor, backProgress); + + if (progress >= 1f) + { + phase = ActionPhase.Recover; + ResetTimestamp(); + } + break; + } + + case ActionPhase.Recover: + { + float recoverProgress = EaseOutQuad(Mathf.Min(progress, 1f)); + + SourceControl.GlobalPosition = SourceControl.GlobalPosition.Lerp(SourcePos, recoverProgress); + SourceControl.Rotation = Mathf.Lerp(SourceControl.Rotation, SourceRot, recoverProgress); + TargetControl.Rotation = Mathf.Lerp(TargetControl.Rotation, TargetRot, recoverProgress); + + if (progress >= 1f) + return true; + break; + } + + default: + return true; + } + + return false; + } + + protected override void DoInternalEnd() + { + if (SourceControl != null) + { + SourceControl.GlobalPosition = SourcePos; + SourceControl.Rotation = SourceRot; + } + if (TargetControl != null) + { + TargetControl.GlobalPosition = TargetPos; + TargetControl.Rotation = TargetRot; + TargetControl.Scale = targetOriginalScale; + TargetControl.Modulate = targetOriginalColor; + } + } + + private float EaseOutQuad(float t) + { + return 1f - (1f - t) * (1f - t); + } + + private float EaseInOutQuad(float t) + { + return t < 0.5f ? 2f * t * t : 1f - Mathf.Pow(-2f * t + 2f, 2f) / 2f; + } + } +} diff --git a/src/Actions/PhysicsDefenceActionCard.cs b/src/Actions/PhysicsDefenceActionCard.cs new file mode 100644 index 0000000..da51abc --- /dev/null +++ b/src/Actions/PhysicsDefenceActionCard.cs @@ -0,0 +1,39 @@ +using Cthangover.Core.Characters; + +namespace Cthangover.CardBattle.Actions +{ + + /// + /// Executor that adds a fixed amount of defence to the target character. + /// Defence acts as a damage-absorbing shield that is consumed before health when the + /// character takes damage (see ). The defence delta + /// comes from the "Defence" property on the action card. + /// Registered in under "physics/defence". + /// + public class PhysicsDefenceActionCard : ActionBase + { + + /// + /// Unique identifier registered in as "physics/defence". + /// + public override string ID => "PhysicsDefenceActionCard"; + + /// + /// Adds a flat defence value to after deducting action points + /// from . The returned carries the + /// defence delta for floating text display. + /// + public override ChangedAttributes Execute(ActionCharacter actionCard, Character user, Character target) + { + if (target == null || user == null || !CheckRequiredAndUsePoint(actionCard, user)) + return new ChangedAttributes { Result = false }; + + var defenceDelta = actionCard.GetInt("Defence"); + target.Attributes.Defence.Value += defenceDelta; + + return new ChangedAttributes { Result = true, Target = { Defence = defenceDelta}}; + } + + } + +} diff --git a/src/Actions/PhysicsStunActionCard.cs b/src/Actions/PhysicsStunActionCard.cs new file mode 100644 index 0000000..e42c67e --- /dev/null +++ b/src/Actions/PhysicsStunActionCard.cs @@ -0,0 +1,39 @@ +using Cthangover.Core.Characters; + +namespace Cthangover.CardBattle.Actions +{ + + /// + /// Executor that applies a stun status effect to the target character. + /// The stun duration (number of turns) is read from the "Turn" property on the action card. + /// During a stunned character's turn, returns true, + /// causing to skip that character's actions entirely. + /// Registered in under "physics/stun". + /// + public class PhysicsStunActionCard : ActionBase + { + + /// + /// Unique identifier registered in as "physics/stun". + /// + public override string ID => "PhysicsStunActionCard"; + + /// + /// Adds a with the configured turn count to 's + /// after deducting action points from . + /// + public override ChangedAttributes Execute(ActionCharacter actionCard, Character user, Character target) + { + if (target == null || user == null || !CheckRequiredAndUsePoint(actionCard, user)) + return new ChangedAttributes { Result = false }; + + if (target.HasPassiveEffect("StunImmune")) + return new ChangedAttributes { Result = true }; + + target.StatusEffectQueue.Add("effect/physics/stun", actionCard.GetInt("Turn", 3)); + + return new ChangedAttributes { Result = true }; + } + } + +} diff --git a/src/CardBattleCore.cs b/src/CardBattleCore.cs new file mode 100644 index 0000000..483a91c --- /dev/null +++ b/src/CardBattleCore.cs @@ -0,0 +1,390 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Cthangover.Core.Battle; +using Cthangover.Core.Battle.Actions; +using Cthangover.Core.Scenes; +using Cthangover.Core.Utils; +using Cthangover.Core.Characters; +using Cthangover.CardBattle.Player; +using Cthangover.CardBattle.UI; +using Godot; + +namespace Cthangover.CardBattle +{ + /// + /// Core orchestrator for the card-based turn battle system. + /// Manages the full battle lifecycle: initializing UI panels ( and ), + /// cycling between player and enemy turns, spawning animated actions via , + /// applying status effects each turn, and detecting win/loss conditions when all cards on one side are dead. + /// Implements so it can be registered as a battle type provider in the core battle engine. + /// + public class CardBattleCore : IBattleCore + { + /// + /// Unique identifier for this battle core, used by the battle engine to select the card battle mod. + /// + public string Id => "card_battle"; + + /// + /// Provides instances for the three card action types + /// (physics/attack, physics/defence, physics/stun) used during action resolution. + /// + public IActionExecutorProvider ActionProvider { get; } = new Actions.CardBattleActionProvider(); + + private Character[] _playerChars; + private Character[] _enemyChars; + private IBattleContext _ctx; + + private BattleCardPanel _playerPanel; + private BattleCardPanel _enemyPanel; + private CardController _cardController; + private Control _actionPanel; + private Control _toolPanel; + private EndTurnButton _endTurnButton; + + private BattleSide _currentOrder; + + private const float CELL_OFFSET = 10f; + private const int PLAYER_CARD_LIMIT = 4; + private const float BASE_CARD_WIDTH = 230f; + private const float BASE_CARD_HEIGHT = 420f; + + /// + /// Initializes the card battle by constructing the player and enemy instances, + /// the for drag-and-drop input, and the . + /// Enemy characters are deep-copied so defeats are tracked independently per battle instance. + /// The is registered with . + /// + public void Init(Character[] playerChars, Character[] enemyChars, IBattleContext ctx) + { + _playerChars = playerChars; + _ctx = ctx; + + _enemyChars = new Character[enemyChars.Length]; + for (int i = 0; i < enemyChars.Length; i++) + _enemyChars[i] = enemyChars[i]?.Copy() ?? enemyChars[i]; + + ActionExecutorHub.Instance.SetActiveProvider(ActionProvider); + + var root = ctx.RootNode as Node; + + var panel = root.GetNodeOrNull("Panel") ?? root; + + _playerPanel = (BattleCardPanel)TscnScenes.LoadAndBuild("scenes/battle_card_panel.tscn"); + _playerPanel.Name = "PlayerPanel"; + _playerPanel.EnsureConstructed(); + panel.AddChild(_playerPanel); + + _enemyPanel = (BattleCardPanel)TscnScenes.LoadAndBuild("scenes/battle_card_panel.tscn"); + _enemyPanel.Name = "EnemyPanel"; + _enemyPanel.AlignType = "Right"; + _enemyPanel.EnsureConstructed(); + _enemyPanel.MouseFilter = Control.MouseFilterEnum.Ignore; + panel.AddChild(_enemyPanel); + + _actionPanel = new Control { Name = "ActionPanel" }; + panel.AddChild(_actionPanel); + + _toolPanel = root.GetNodeOrNull("ToolPanel"); + + _endTurnButton = (EndTurnButton)TscnScenes.LoadAndBuild("scenes/end_turn_button.tscn"); + _endTurnButton.Name = "EndTurnButton"; + _endTurnButton.EnsureConstructed(); + _endTurnButton.SetVisible(false); + _endTurnButton.OnPressed += OnEndTurnPressed; + _toolPanel?.AddChild(_endTurnButton); + + _cardController = new CardController { Name = "CardController" }; + panel.AddChild(_cardController); + _cardController.FindPanels(); + } + + /// + /// Positions the UI panels relative to the viewport, calculates a unified cardScale that + /// fits both player and enemy cards on screen, initializes all character cards, and starts the first player turn. + /// Subscribes to OnCardDead to record defeated enemies and to OnActionExecuted to detect + /// automatic turn completion when all player cards exhaust their action points. + /// + public void Start() + { + var root = _ctx.RootNode as Node; + + var panelSize = root.GetViewport().GetVisibleRect().Size; + var enemyCount = _enemyChars?.Length ?? 1; + + _enemyPanel.UseRows = enemyCount > 6; + + _playerPanel.Position = new Vector2(0, panelSize.Y * 0.55f); + _playerPanel.Size = new Vector2(panelSize.X * 0.55f, panelSize.Y * 0.35f); + + _enemyPanel.Position = new Vector2(panelSize.X * 0.02f, panelSize.Y * 0.022f); + _enemyPanel.Size = new Vector2(panelSize.X * 0.96f, panelSize.Y * 0.446f); + + _actionPanel.Position = new Vector2(panelSize.X * 0.53f, panelSize.Y * 0.55f); + _actionPanel.Size = new Vector2(panelSize.X * 0.46f, panelSize.Y * 0.35f); + + if (_endTurnButton != null) + _endTurnButton.Position = new Vector2(panelSize.X - 180f, (panelSize.Y * 0.1f - 50f) / 2); + + var cardScale = CalculateCardScale(enemyCount); + _cardController.CardScale = cardScale; + + _playerPanel.Init(_playerChars, true, cardScale); + _enemyPanel.Init(_enemyChars, false, cardScale); + + _enemyPanel.OnCardDead += OnEnemyCardDead; + _cardController.OnActionExecuted += CheckPlayerTurnEnd; + + BattleSceneContext.Instance.OnBattleCleared += OnBattleCleared; + + _currentOrder = BattleSide.Player; + StartPlayerTurn(); + } + + private float CalculateCardScale(int enemyCount) + { + var viewport = ((Node)_ctx.RootNode).GetViewport().GetVisibleRect().Size; + float enemyPanelWidth = viewport.X * 0.96f; + float playerPanelWidth = viewport.X * 0.55f; + float enemyPanelHeight = viewport.Y * 0.446f; + + float maxCardScale = 1f; + + if (_enemyPanel.UseRows) + { + int maxPerRow = _enemyPanel.GetMaxCardsPerRow(); + int rows = _enemyPanel.GetCurrentRowCount(enemyCount); + + float widthScale = (enemyPanelWidth - (maxPerRow - 1) * CELL_OFFSET) / (maxPerRow * BASE_CARD_WIDTH); + float heightScale = (enemyPanelHeight - (rows - 1) * CELL_OFFSET) / (rows * BASE_CARD_HEIGHT); + maxCardScale = Mathf.Max(0.1f, Mathf.Min(widthScale, heightScale)); + } + else + { + int effectiveCount = Mathf.Max(enemyCount, 1); + maxCardScale = Mathf.Max(0.1f, (enemyPanelWidth - (effectiveCount - 1) * CELL_OFFSET) / (effectiveCount * BASE_CARD_WIDTH)); + } + + float playerFitScale = Mathf.Max(0.1f, (playerPanelWidth - (PLAYER_CARD_LIMIT - 1) * CELL_OFFSET) / (PLAYER_CARD_LIMIT * BASE_CARD_WIDTH)); + + float cardScale = Mathf.Min(maxCardScale, playerFitScale); + return Mathf.Min(cardScale, 1f); + } + + private void OnEnemyCardDead(CharacterCardNode card) + { + if (card?.Card == null) + return; + + var ctx = BattleSceneContext.Instance; + if (ctx != null) + { + ctx.RecordEnemyDefeated(card.Card); + ctx.NotifyCharacterDied(card.Card); + } + } + + private void StartPlayerTurn() + { + GameLogger.Log("CARD_BATTLE", "Player turn started", LogLevel.Debug); + + _currentOrder = BattleSide.Player; + BattleSceneContext.Instance.IsWait = false; + + _cardController.ClearSelections(); + _cardController.ClearActions(); + + _endTurnButton?.SetVisible(true); + + foreach (var card in _playerPanel.CardList) + { + card.Card.StatusEffectQueue.OnTurnStart(); + card.Card.Attributes.Point.Value = card.Card.Attributes.Point.BaseValue; + card.UpdateInfo(); + } + } + + private void OnEndTurnPressed() + { + GameLogger.Log("CARD_BATTLE", "End turn button pressed", LogLevel.Debug); + OnPlayerTurnEnd(); + } + + private void CheckPlayerTurnEnd() + { + if (_currentOrder != BattleSide.Player) + return; + + if (!HasAliveCards(_enemyPanel)) + { + _ctx.EndBattle(BattleSide.Player); + return; + } + + bool allOutOfPoints = true; + foreach (var card in _playerPanel.CardList) + { + if (!card.IsDead && card.Card?.Attributes?.Point?.Value > 0) + { + allOutOfPoints = false; + break; + } + } + + if (allOutOfPoints) + OnPlayerTurnEnd(); + } + + /// + /// Ends the player's turn by hiding the end-turn button, setting the battle to IsWait, + /// and yielding two frames for UI to settle before running the enemy AI turn via RunEnemyTurn. + /// If all enemies are already dead, the battle ends immediately with the player as winner. + /// This method is async void so it can await frames without blocking the game loop. + /// + public async void OnPlayerTurnEnd() + { + GameLogger.Log("CARD_BATTLE", "Player turn ended", LogLevel.Debug); + + _endTurnButton?.SetVisible(false); + BattleSceneContext.Instance.IsWait = true; + + if (!HasAliveCards(_enemyPanel)) + { + _ctx.EndBattle(BattleSide.Player); + return; + } + + var root = (Node)_ctx.RootNode; + await root.ToSignal(root.GetTree(), "process_frame"); + await root.ToSignal(root.GetTree().CreateTimer(0.5f), "timeout"); + + await RunEnemyTurn(); + } + + private async Task RunEnemyTurn() + { + GameLogger.Log("CARD_BATTLE", "Enemy turn started", LogLevel.Debug); + + _currentOrder = BattleSide.Enemy; + _endTurnButton?.SetVisible(false); + + var root = (Node)_ctx.RootNode; + + foreach (var card in _enemyPanel.CardList.ToList()) + { + if (card.IsDead || card.Card?.Actions == null || card.Card.Actions.Count == 0) + continue; + + card.Card.StatusEffectQueue.OnTurnStart(); + + if (card.Card.StatusEffectQueue.HasStun()) + { + GameLogger.Log("CARD_BATTLE", $"{card.Card.Name} is stunned, skipping", LogLevel.Debug); + continue; + } + + card.Card.Attributes.Point.Value = card.Card.Attributes.Point.BaseValue; + + var action = PickEnemyAction(card); + if (action == null) + continue; + + var target = PickEnemyTarget(card, action); + if (target == null) + continue; + + GameLogger.Log("CARD_BATTLE", + $"Enemy action: {card.Card.Name} -> {action.Name} -> {target.Card.Name}", + LogLevel.Message); + + var animatedAction = CreateAnimatedAction(card, target, action); + if (animatedAction != null) + { + animatedAction.DoStart(); + while (!animatedAction.DoAction()) + await root.ToSignal(root.GetTree(), "process_frame"); + animatedAction.DoEnd(); + } + + await root.ToSignal(root.GetTree().CreateTimer(0.2f), "timeout"); + + if (!HasAliveCards(_playerPanel)) + { + GameLogger.Log("CARD_BATTLE", "All players dead — enemy wins", LogLevel.Message); + _ctx.EndBattle(BattleSide.Enemy); + return; + } + } + + if (!HasAliveCards(_playerPanel)) + { + _ctx.EndBattle(BattleSide.Enemy); + return; + } + + StartPlayerTurn(); + } + + private ActionCharacter PickEnemyAction(CharacterCardNode card) + { + var actions = card.Card.Actions; + if (actions == null || actions.Count == 0) + return null; + + var random = new Random(); + return actions[random.Next(actions.Count)]; + } + + private CharacterCardNode PickEnemyTarget(CharacterCardNode source, ActionCharacter action) + { + if (action.Type == ActionCharacterType.ToEnemy) + { + var alivePlayers = _playerPanel.CardList + .Where(c => !c.IsDead && c.Card?.Attributes?.Health?.Value > 0) + .ToList(); + + if (alivePlayers.Count == 0) + return null; + return alivePlayers[new Random().Next(alivePlayers.Count)]; + } + + if (action.Type == ActionCharacterType.ToAlias || action.Type == ActionCharacterType.ToSelf) + return source; + + GameLogger.Log("CARD_BATTLE", $"PickEnemyTarget: unsupported action type {action.Type}", LogLevel.Warning); + return null; + } + + private Actions.AbstractBattleAction CreateAnimatedAction(CharacterCardNode source, CharacterCardNode target, ActionCharacter action) + { + if (action.Type == ActionCharacterType.ToEnemy) + return new Actions.PhysicsAttackAction(source, target, action); + + if (action.Type == ActionCharacterType.ToAlias || action.Type == ActionCharacterType.ToSelf) + return new Actions.PhysicsDefenceAction(source, target, action); + + GameLogger.Log("CARD_BATTLE", $"CreateAnimatedAction: unsupported action type {action.Type}", LogLevel.Warning); + return null; + } + + private bool HasAliveCards(BattleCardPanel panel) + { + foreach (var card in panel.CardList) + { + if (!card.IsDead && card.Card?.Attributes?.Health?.Value > 0) + return true; + } + return false; + } + + private void OnBattleCleared() + { + _playerPanel?.ClearAll(); + _enemyPanel?.ClearAll(); + _cardController?.ClearActions(); + _endTurnButton?.SetVisible(false); + } + } +} diff --git a/src/ICard.cs b/src/ICard.cs new file mode 100644 index 0000000..173ce8b --- /dev/null +++ b/src/ICard.cs @@ -0,0 +1,24 @@ +using Godot; + +namespace Cthangover.CardBattle +{ + + /// + /// Common interface for all card UI nodes in the card battle system. + /// Exposes the Frame and Image controls so that + /// can perform hit-testing during drag-and-drop targeting + /// without knowing the concrete card type ( or ). + /// + public interface ICard + { + /// + /// The frame used for hit-testing whether a pointer is over this card. + /// + TextureRect Frame { get; } + /// + /// The portrait/image of the card, used for dissolve shader effects on death. + /// + TextureRect Image { get; } + } + +} \ No newline at end of file diff --git a/src/Player/AttackToEnemyStrategy.cs b/src/Player/AttackToEnemyStrategy.cs new file mode 100644 index 0000000..89ae99f --- /dev/null +++ b/src/Player/AttackToEnemyStrategy.cs @@ -0,0 +1,57 @@ +using Cthangover.CardBattle.UI; +using Cthangover.Core.Battle; +using Cthangover.Core.Battle.Actions; +using Cthangover.Core.Characters; + +namespace Cthangover.CardBattle.Player +{ + /// + /// Strategy for ToEnemy attack actions. Validates that the target is an enemy + /// (non-player card), highlights both cards with a red attack outline, and executes + /// damage via with floating damage/defence numbers. + /// This is the standard strategy for all offensive cards in the player's deck. + /// + public class AttackToEnemyStrategy : ICardActionStrategy + { + /// + /// Returns true if the action type is ToEnemy and the target is an enemy card. + /// + public bool Check(ActionCharacter action, CharacterCardNode source, CharacterCardNode target) + { + return action.Type == ActionCharacterType.ToEnemy && target != null && !target.IsPlayer; + } + + /// + /// Shows a red attack overlay on both the dragged action card and the target enemy card. + /// + public void HighlightTarget(ActionCardNode actionCard, CharacterCardNode target) + { + actionCard.Attack(); + target.Attack(); + } + + /// + /// Resolves the attack via and spawns floating damage + /// and defence-loss numbers above the card. + /// + public void Execute(CharacterCardNode source, CharacterCardNode target, ActionCharacter action) + { + if (source?.Card == null || target?.Card == null || action == null) + return; + + var result = ActionExecutorHub.Instance.Execute(action, source.Card, target.Card); + source.UpdateInfo(); + target.UpdateInfo(); + + if (result.Result) + { + if (result.Target.Damage > 0) + ShowDamageBehaviour.SpawnDamage(result.Target.Damage, target, target.GlobalPosition); + + var defenceLost = -result.Target.Defence; + if (defenceLost > 0) + ShowDamageBehaviour.SpawnDefence(defenceLost, target, target.GlobalPosition); + } + } + } +} diff --git a/src/Player/CardActionStrategyFactory.cs b/src/Player/CardActionStrategyFactory.cs new file mode 100644 index 0000000..1224ca6 --- /dev/null +++ b/src/Player/CardActionStrategyFactory.cs @@ -0,0 +1,52 @@ +using Cthangover.CardBattle.UI; +using Cthangover.Core.Characters; + +namespace Cthangover.CardBattle.Player +{ + /// + /// Factory that returns the appropriate implementation + /// for a given . Dispatches based on : + /// ToEnemy, + /// ToAlias, + /// ToSelf. + /// Used by during drag-and-drop to determine valid targeting behavior + /// and by for pre-validation. + /// + public static class CardActionStrategyFactory + { + /// + /// Returns the matching strategy for the action's type, or null if any argument is null + /// or the type is unrecognized. + /// + public static ICardActionStrategy Get(ActionCharacter action, CharacterCardNode source, CharacterCardNode target) + { + if (action == null || source == null || target == null) + return null; + + switch (action.Type) + { + case ActionCharacterType.ToEnemy: + return new AttackToEnemyStrategy(); + case ActionCharacterType.ToAlias: + return new SupportStrategy(); + case ActionCharacterType.ToSelf: + return new SelfStrategy(); + default: + return null; + } + } + + /// + /// Convenience method that gets the strategy via and immediately calls + /// on it. Returns false if no strategy matches + /// or any argument is null. + /// + public static bool CanTarget(ActionCharacter action, CharacterCardNode source, CharacterCardNode target) + { + if (action == null || source == null || target == null) + return false; + + return Get(action, source, target)?.Check(action, source, target) ?? false; + } + } +} diff --git a/src/Player/ICardActionStrategy.cs b/src/Player/ICardActionStrategy.cs new file mode 100644 index 0000000..0662d81 --- /dev/null +++ b/src/Player/ICardActionStrategy.cs @@ -0,0 +1,32 @@ +using Cthangover.CardBattle.UI; +using Cthangover.Core.Characters; + +namespace Cthangover.CardBattle.Player +{ + /// + /// Strategy interface that defines validation, visual highlighting, and execution for + /// a specific type of card action during the player's drag-and-drop targeting flow. + /// The dispatches to the correct strategy based on + /// the action's . calls + /// during drag to validate targets, to show + /// valid/invalid visual feedback, and when the card is dropped on a valid target. + /// + public interface ICardActionStrategy + { + /// + /// Validates whether can be performed by + /// on . Called each frame during drag to determine visual feedback. + /// + bool Check(ActionCharacter action, CharacterCardNode source, CharacterCardNode target); + /// + /// Applies visual highlighting to both the being dragged + /// and the character card (e.g. red outline for attack, green for support). + /// + void HighlightTarget(ActionCardNode actionCard, CharacterCardNode target); + /// + /// Executes the action via and spawns floating damage/defence text. + /// Called on successful card drop by . + /// + void Execute(CharacterCardNode source, CharacterCardNode target, ActionCharacter action); + } +} diff --git a/src/Player/SelfStrategy.cs b/src/Player/SelfStrategy.cs new file mode 100644 index 0000000..1c9bcd7 --- /dev/null +++ b/src/Player/SelfStrategy.cs @@ -0,0 +1,56 @@ +using Cthangover.CardBattle.UI; +using Cthangover.Core.Battle; +using Cthangover.Core.Battle.Actions; +using Cthangover.Core.Characters; + +namespace Cthangover.CardBattle.Player +{ + /// + /// Strategy for self-targeting actions (ToSelf). Validates that source and target + /// are the same card, highlights with a green selection outline, and applies the action + /// to the source card itself via . Used for self-buffs like + /// defence boosts. Damage and defence floating text spawn above the source card. + /// + public class SelfStrategy : ICardActionStrategy + { + /// + /// Returns true only when and are the same card. + /// + public bool Check(ActionCharacter action, CharacterCardNode source, CharacterCardNode target) + { + return source != null && source == target; + } + + /// + /// Shows a green selection overlay on both the dragged action card and the source card. + /// + public void HighlightTarget(ActionCardNode actionCard, CharacterCardNode target) + { + actionCard.Select(); + target.Select(); + } + + /// + /// Executes the self-targeted action and spawns floating defence/damage numbers + /// above the card. Damage numbers are marked with the + /// isHeal flag for green coloring via . + /// + public void Execute(CharacterCardNode source, CharacterCardNode target, ActionCharacter action) + { + if (source?.Card == null || action == null) + return; + + var result = ActionExecutorHub.Instance.Execute(action, source.Card, source.Card); + source.UpdateInfo(); + + if (result.Result) + { + if (result.Target.Defence > 0) + ShowDamageBehaviour.SpawnDefence(result.Target.Defence, source, source.GlobalPosition); + + if (result.Target.Damage > 0) + ShowDamageBehaviour.SpawnDamage(result.Target.Damage, source, source.GlobalPosition, true); + } + } + } +} diff --git a/src/Player/StunToEnemyStrategy.cs b/src/Player/StunToEnemyStrategy.cs new file mode 100644 index 0000000..60ed090 --- /dev/null +++ b/src/Player/StunToEnemyStrategy.cs @@ -0,0 +1,49 @@ +using Cthangover.CardBattle.UI; +using Cthangover.Core.Battle.Actions; +using Cthangover.Core.Characters; + +namespace Cthangover.CardBattle.Player +{ + /// + /// Strategy for stun actions targeting enemies. Extends the standard ToEnemy check + /// by also verifying that the action has at least 2 turns of stun duration + /// ("Turn" > 1). Highlights with red attack overlays and applies + /// the stun effect via without spawning damage text + /// (stun has no numerical effect to display). + /// Not currently returned by — available for + /// future use when stun cards need distinct drag-and-drop behavior from attack cards. + /// + public class StunToEnemyStrategy : ICardActionStrategy + { + /// + /// Returns true when the action targets an enemy and has a turn count greater than 1. + /// + public bool Check(ActionCharacter action, CharacterCardNode source, CharacterCardNode target) + { + return action.Type == ActionCharacterType.ToEnemy && target != null && !target.IsPlayer + && action.GetInt("Turn", 0) > 1; + } + + /// + /// Shows a red attack overlay on both cards, same as . + /// + public void HighlightTarget(ActionCardNode actionCard, CharacterCardNode target) + { + actionCard.Attack(); + target.Attack(); + } + + /// + /// Applies the stun via . Does not spawn damage/defence text. + /// + public void Execute(CharacterCardNode source, CharacterCardNode target, ActionCharacter action) + { + if (source?.Card == null || target?.Card == null || action == null) + return; + + ActionExecutorHub.Instance.Execute(action, source.Card, target.Card); + source.UpdateInfo(); + target.UpdateInfo(); + } + } +} diff --git a/src/Player/SupportStrategy.cs b/src/Player/SupportStrategy.cs new file mode 100644 index 0000000..af9195e --- /dev/null +++ b/src/Player/SupportStrategy.cs @@ -0,0 +1,56 @@ +using Cthangover.CardBattle.UI; +using Cthangover.Core.Battle; +using Cthangover.Core.Battle.Actions; +using Cthangover.Core.Characters; + +namespace Cthangover.CardBattle.Player +{ + /// + /// Strategy for ally-support actions (ToAlias). Validates that the target is a friendly + /// player card, highlights with green selection overlays, and applies defence/healing effects + /// via . Allows one player card to buff another during the player's turn. + /// + public class SupportStrategy : ICardActionStrategy + { + /// + /// Returns true when the action type is ToAlias and the target is a player card. + /// + public bool Check(ActionCharacter action, CharacterCardNode source, CharacterCardNode target) + { + return action.Type == ActionCharacterType.ToAlias && target != null && target.IsPlayer; + } + + /// + /// Shows a green selection overlay on both the dragged action card and the target ally card. + /// + public void HighlightTarget(ActionCardNode actionCard, CharacterCardNode target) + { + actionCard.Select(); + target.Select(); + } + + /// + /// Resolves the support action and spawns floating defence and heal text above + /// the ally. Damage numbers are shown with the isHeal + /// flag for green coloring. + /// + public void Execute(CharacterCardNode source, CharacterCardNode target, ActionCharacter action) + { + if (source?.Card == null || target?.Card == null || action == null) + return; + + var result = ActionExecutorHub.Instance.Execute(action, source.Card, target.Card); + source.UpdateInfo(); + target.UpdateInfo(); + + if (result.Result) + { + if (result.Target.Defence > 0) + ShowDamageBehaviour.SpawnDefence(result.Target.Defence, target, target.GlobalPosition); + + if (result.Target.Damage > 0) + ShowDamageBehaviour.SpawnDamage(result.Target.Damage, target, target.GlobalPosition, true); + } + } + } +} diff --git a/src/StatusEffect/IStatusActions.cs b/src/StatusEffect/IStatusActions.cs new file mode 100644 index 0000000..c9586f5 --- /dev/null +++ b/src/StatusEffect/IStatusActions.cs @@ -0,0 +1,21 @@ +namespace Cthangover.CardBattle.StatusEffect +{ + /// + /// Interface exposed by to instances + /// so that status effects can communicate back to the queue during turn lifecycle callbacks. + /// SkipTurn allows an effect (e.g. stun) to mark the character's entire turn as skipped. + /// RemoveStatus allows an effect to self-remove from the queue. + /// + public interface IStatusActions + { + /// + /// Marks the character's current turn as skipped. Called by . + /// + void SkipTurn(); + /// + /// Removes the specified status effect from the queue. Called by effects that want to + /// self-terminate before their turn count expires. + /// + void RemoveStatus(IStatusEffect status); + } +} diff --git a/src/StatusEffect/IStatusEffect.cs b/src/StatusEffect/IStatusEffect.cs new file mode 100644 index 0000000..d011660 --- /dev/null +++ b/src/StatusEffect/IStatusEffect.cs @@ -0,0 +1,26 @@ +using Cthangover.Core.Characters; + +namespace Cthangover.CardBattle.StatusEffect +{ + /// + /// Defines a status effect that is applied to a during battle + /// and processed each turn by . Effects receive callbacks + /// at turn start and turn end, and self-report expiration via + /// when their counter reaches zero. The + /// callback parameter allows effects to influence the character's turn (e.g. skip it). + /// + public interface IStatusEffect + { + /// Integer identifier for the effect type (1 = stun). Used for serialization. + int Type { get; } + /// Remaining turn count before expiry. Decremented by . + int Turns { get; set; } + /// Returns true when ≤ 0, signalling removal from the queue. + bool IsExpired { get; } + + /// Called by before the turn counter is decremented. + void OnTurnStart(Character character, IStatusActions actions); + /// Called by after the character's actions have resolved. + void OnTurnEnd(Character character, IStatusActions actions); + } +} diff --git a/src/StatusEffect/StatusEffectItem.cs b/src/StatusEffect/StatusEffectItem.cs new file mode 100644 index 0000000..b55a3fe --- /dev/null +++ b/src/StatusEffect/StatusEffectItem.cs @@ -0,0 +1,27 @@ +namespace Cthangover.CardBattle.StatusEffect +{ + /// + /// Data-only container for a status effect's serializable fields ( and ). + /// Used in to deep-clone status effect state when a character + /// is copied for battle. Cast to when added back to a queue. + /// + public class StatusEffectItem + { + /// Effect type identifier (matches ). + public int Type { get; set; } + /// Remaining turns before expiry. + public int Turns { get; set; } + + /// + /// Creates a field-by-field copy for deep-cloning during character duplication. + /// + public StatusEffectItem Copy() + { + return new StatusEffectItem + { + Type = Type, + Turns = Turns + }; + } + } +} diff --git a/src/StatusEffect/StatusEffectQueue.cs b/src/StatusEffect/StatusEffectQueue.cs new file mode 100644 index 0000000..be6e6aa --- /dev/null +++ b/src/StatusEffect/StatusEffectQueue.cs @@ -0,0 +1,103 @@ +using System.Collections.Generic; +using Cthangover.Core.Characters; + +namespace Cthangover.CardBattle.StatusEffect +{ + /// + /// Manages the collection of instances attached to a single . + /// Implements so that individual effects can call back into the queue + /// (e.g. to skip the turn). During , each effect's callback is invoked and + /// its turn counter is decremented; expired effects are removed. is checked by + /// to skip stunned enemies. + /// Supports deep copying for character duplication via . + /// + public class StatusEffectQueue : IStatusActions + { + private readonly Character _character; + private readonly List _effects = new(); + + /// + /// Set to true when an effect calls during . + /// Reset to false at the start of each turn before effects are processed. + /// + public bool SkipTurn { get; private set; } + + /// + /// Creates a new queue bound to the given . + /// + public StatusEffectQueue(Character character) + { + _character = character; + } + + /// + /// Creates a deep copy of this queue bound to . + /// effects are field-copied; other effect types are shared by reference. + /// + public StatusEffectQueue Copy(Character newCharacter) + { + var clone = new StatusEffectQueue(newCharacter); + foreach (var effect in _effects) + { + if (effect is StatusEffectItem item) + clone._effects.Add(item.Copy() as IStatusEffect); + else + clone._effects.Add(effect); + } + return clone; + } + + /// + /// Adds a new status effect to the end of the processing list. Effects are processed + /// in reverse order during turn callbacks (last-added first). + /// + public void AddEffect(IStatusEffect effect) + { + _effects.Add(effect); + } + + /// + /// Processes all effects at the start of the character's turn: resets , + /// calls for each effect in reverse order, + /// decrements the turn counter, and removes expired effects. + /// Called by and . + /// + public void OnTurnStart() + { + SkipTurn = false; + for (int i = _effects.Count - 1; i >= 0; i--) + { + var effect = _effects[i]; + effect.OnTurnStart(_character, this); + effect.Turns--; + if (effect.IsExpired) + _effects.RemoveAt(i); + } + } + + /// + /// Processes all effects at the end of the character's turn: calls + /// in reverse order and removes expired effects. + /// + public void OnTurnEnd() + { + for (int i = _effects.Count - 1; i >= 0; i--) + { + var effect = _effects[i]; + effect.OnTurnEnd(_character, this); + if (effect.IsExpired) + _effects.RemoveAt(i); + } + } + + void IStatusActions.SkipTurn() + { + SkipTurn = true; + } + + void IStatusActions.RemoveStatus(IStatusEffect status) + { + _effects.Remove(status); + } + } +} diff --git a/src/StatusEffect/StunStatusEffect.cs b/src/StatusEffect/StunStatusEffect.cs new file mode 100644 index 0000000..d77fcbc --- /dev/null +++ b/src/StatusEffect/StunStatusEffect.cs @@ -0,0 +1,52 @@ +using Cthangover.Core.Characters; + +namespace Cthangover.CardBattle.StatusEffect +{ + /// + /// Status effect that causes a character to skip their entire turn. + /// On , calls which sets + /// to true. + /// checks this flag and skips the character's action selection and resolution entirely. + /// Created by when a stun card is played, + /// and added to the target's via Add("effect/physics/stun", turns). + /// + public class StunStatusEffect : IStatusEffect + { + /// Type identifier for stun (1). + public int Type => 1; + /// Number of turns this stun remains active. + public int Turns { get; set; } + /// Expired when the turn counter reaches zero or below. + public bool IsExpired => Turns <= 0; + + /// + /// Creates a stun effect lasting for turns. + /// + public StunStatusEffect(int turns) + { + Turns = turns; + } + + /// + /// Parameterless constructor for deserialization. Sets to 0 (expired immediately). + /// + public StunStatusEffect() + { + } + + /// + /// Calls to mark the character's turn as skipped. + /// + public void OnTurnStart(Character character, IStatusActions actions) + { + actions.SkipTurn(); + } + + /// + /// Stun has no end-of-turn effect. + /// + public void OnTurnEnd(Character character, IStatusActions actions) + { + } + } +} diff --git a/src/UI/ActionCardNode.cs b/src/UI/ActionCardNode.cs new file mode 100644 index 0000000..75c283a --- /dev/null +++ b/src/UI/ActionCardNode.cs @@ -0,0 +1,137 @@ +using Cthangover.Core.Cards; +using Cthangover.Core.Characters; +using Cthangover.Core.UI; +using Godot; + +namespace Cthangover.CardBattle.UI +{ + /// + /// UI node representing a single action card that the player can drag onto a target. + /// Implements so can hit-test drag targets. + /// Displayed in the 's action panel below the character cards. + /// Supports hover scale-up animation, selection highlighting (, , ), + /// and info updates from the underlying data model. + /// + public partial class ActionCardNode : ModWidget, ICard + { + private TextureRect frame; + private TextureRect image; + private Label nameLabel; + + /// + public TextureRect Frame => frame; + /// + public TextureRect Image => image; + + /// + /// The data model backing this card. Contains the action's + /// name, image, type, and stat modifiers used during execution. + /// + public ActionCharacter Card { get; set; } + + /// + /// Returns this — action cards are nodes themselves, + /// unlike which wraps a separate control. + /// + public Control GetControlNode() => this; + + private Tween hoverTween; + private float baseY; + private bool isHovered; + private Vector2 _baseScale; + + protected override void Construct() + { + image = GetNode("Image"); + frame = GetNode("Frame"); + nameLabel = GetNode