From 6b46d1c27413b7973b3000996ce76e8922d9da2a Mon Sep 17 00:00:00 2001 From: gamer147 Date: Thu, 9 Apr 2026 07:58:58 -0400 Subject: [PATCH] Appearance sets --- assets/sprites/CP002AB.BMP | Bin 0 -> 33078 bytes assets/sprites/CP002AB.BMP.import | 40 ++++ export_presets.cfg | 71 +++++++ prefabs/deployed_unit.tscn | 41 ---- .../appearance_sets/lily_child_deployed.tres | 178 ++++++++++++++++++ resources/units/lily_child.tres | 33 ++++ scenes/views/main_menu_view.tscn | 11 +- scripts/battle/combat_engine/combat_system.gd | 32 ++-- .../battle/combat_tactics/combat_tactic.gd | 6 +- .../ranges/any_combat_tactic_range.gd | 2 +- .../ranges/combat_tactic_range.gd | 2 +- .../ranges/fixed_combat_tactic_range.gd | 2 +- .../unit_matching_combat_tactic_range.gd | 4 +- .../tactics/attack_combat_tactic.gd | 8 +- .../tactics/defend_combat_tactic.gd | 6 +- scripts/battle/combat_ui.gd | 4 +- .../battle/deployed_units/deployed_unit.gd | 32 ++++ .../deployed_units/deployed_unit_stats.gd | 29 +++ scripts/units/unit.gd | 1 + scripts/units/unit_appearance.gd | 19 ++ scripts/units/unit_appearance.gd.uid | 1 + scripts/units/unit_appearance_set.gd | 3 + scripts/units/unit_appearance_set.gd.uid | 1 + 23 files changed, 445 insertions(+), 81 deletions(-) create mode 100644 assets/sprites/CP002AB.BMP create mode 100644 assets/sprites/CP002AB.BMP.import create mode 100644 export_presets.cfg create mode 100644 resources/units/appearance_sets/lily_child_deployed.tres create mode 100644 resources/units/lily_child.tres create mode 100644 scripts/units/unit_appearance.gd create mode 100644 scripts/units/unit_appearance.gd.uid create mode 100644 scripts/units/unit_appearance_set.gd create mode 100644 scripts/units/unit_appearance_set.gd.uid diff --git a/assets/sprites/CP002AB.BMP b/assets/sprites/CP002AB.BMP new file mode 100644 index 0000000000000000000000000000000000000000..bb812a43158a17c098cde05dcf3542bfc0930490 GIT binary patch literal 33078 zcmeI5Ux*~@S;jxX3oisOycL9q|3FOqLm>DMLd1BCi3ozo!6Ug4f~XwDn}7k0-h?0l zQ4|D415pWHcq4*AP{G;L(>v33|G__SwteuMo%=ra{)V3|e!j_l3y%%kvf=pc z;oAz_R=_Io?cerY?mNEqN8R_l?|tqEzxzkr2Y%rF?)$&<=iDpb`yuzyANqj%^&ft} z`?U}KtozhY{e=7Ihd<&z^V2`>UU}u??h~K*W%ubH`yKa@pZu)*y^p`@Ui+2T++V)> zG54#lebRm5)z{sheC!MEb3gO(lP_-~7CL{dfP+{nBs$qx-YZ{=WO< zAAiOD=V$uvH~#dV``qUb-M@b6Z{2_V!x!CO{Plfz@9)0i{_0C_xUGNwvistfBlk!D z_Kq9$?zqSIcHE!eJ9YnlH*x>9lem9+`rI8JU%2zL#0^ic+<%{6x$$!0t`-ZoT;5*% zH$BC-c*phs+L5y;y+5~YHyT%fwM%?SaaG!;+PtJVPrJR{-R^cX33Wr4o!jaXxxH=E zp*b!DZ#72vHCsx%eW$3v2ik55mJc?-?t)fJkk(>-lHRGuE2y!yEJgx5S_wmff7wYfO;66*?h z+B13g^^0JOtc$y8yv|+^%KJUrjxSW1+D&n6d%gP-*lUdo#8bk(&3K;J%G2vnm-cp7i?^W&^LAECz#qKZ>n@6ZG96 zo*TrIMIYXA95ZlZ(y!k}vms%Fcod&RgvD`G8y7E}duVv=dALA4Mpwk}jpAq*)~KG> zB@5|`hslswf~U{w0^`vlmcBthIvJA(aCKp%7KVqs5yZokH(~T@dEGFX#e@C+a7bu8 zO5)4Q7t)OS4i4%HI81E(2TT~$DX>0|P&{BnZ1RRGUp=piHknZ1 zw0tjKT)p$Q^i?h=&`e)E;1w--gyTsc#P$z;DQ|_ADtmn+22Hf*S66S|)%=TBBULg( zQ~8L?@CaPF+Jo^%1#vtY<-EgHIIk}maoHk|xRb4`yUP>GH(CuW^livPv>|w=PeUR+ zh&?YuQ{iQ<8=em%?(EJT%`skIE)4nz(>qW#k4ju99#|d_CevrUfUnR}C*qJl%Jpq+ zZAHkd>KkQnrZ3Gyffaqhc*Z-jN{lEtwDr7P8xmt_5O>1I7}bcr0j`ywq;Qc~vY~vZHrjp3K+u4a4#X#Y^K<7{1mrUJcC}FT=lFkO%q}iv@uuFPRUA ztM$uyscl;x!FVYz`W9ElD|t1%!^M0qeaUPzpDz~3TPCA|+Vjma7w`SRNLikH;$ zuEO!>X|kBlX3}>MQ(@GZLOz>S^f9LEc`TTc&H~F?jQYL0XYV z9u&2bhYDTIBOEW!qu_;hjaS68S)u}VXSR)!WL^_igGVCkk%4%1yt8#Q4X}o^(5LZT zRA!%Qxz5{^NFZJyuSSGxwtKMOO%fH5(XEkJp9gP~m3d$@Is`A27eKvT%)4F2mnp@2 zOJ&NZ3TpFMmluo|$_wCdIMcN|n=O{h8J}d< zdfK>!#yQtL=r*DhCvF-qlotiv5Pdg~7s^}IXv1D$9ve_@Pv2JHwgUeTD$wS% zOFJGyk2^ToJ4RkQo$p=;uR!wXIY_bN;mkYl77luGKHKvE_9*W;^@_8z#qVq3?L2yP zSaO|fM^1n{k5m!=sONQ?5nI*B`f+>xLh*JUXCvX!Yk2L%&SSafJNF(xZm)~mI+fLa zdOB{83&qx_$GKzF;blWB;wA$aD{mY3R6xWc3i>8)vj@QKNjue;S zJy_Q#Z@TZ?Q3t;^kJFPQJC4tsZ-jA7%69K-!{YCB{J}gX+lej_Sm2E@zOz&L?=ZZ6 z-@IgeQ>cv>Qa!;Sb(~B${N_B4lLT5+9#_gkuaG_#QKWd3rK&F&k3jzG_b;asmGVw6 zbx5Z&eG=ybv1E2}(9J1UlpQVN(ie;k&-4+(dW9S)-3i7U^!KUF`0}#EdkJ2j<7HZ$ zUkj8^V_qHK5kx+0zla-9@qr4jEzQF}(P6Di$hI&%y##r&8<%-WC!{t(FBI#2^XifhBvnArM&jlNIYP}Jx%zD?Qt~J zp^Pw9sXVlm^04QYM;a4|=loYP12gwmc&ST?cPPi6Z@w|NkeBdvBl>uh4@i}C&c12$ zaLR*Hm+@5ObZ8YFjMvJmx}?0b36?!o&g3PSG`8Wz$li)4(&uR{59E~c;8~i72ONgs z0SGU68+hGtA$^n&Ral5}5&a%dxlA}W(YNU-Tnk>7NEluyFDn@)EJ638uwYG}bV{TZ zkn_?f+SWX>M8fcl7hBMZ(ZVav3BFq%CmYyr2-F;8{eq^I)-kl^0ginS9foJTP;KHh zX5LCvxOB_(=}4}RG^l3_UY3Y5;Z==zINZuN&A2%(t&8GjhL_39>!$;pVSP#t*TOr@ z05{;pDV}(_IC9ov+*Wz7qpvLwoW(4W)_mWCK0h9kja`TTis6?HHdk;u9!j@OpOqsm zbRM^kmrn!D12gO!cv*>&%>&GCD>T}cxY$^~CQ~m&-$)lO8-M&-c=J3gE)Y-nkcgMY zl~vwR&dV&bID)S5e6Z8xJY%+-K8jlCYr)gH9-l`jUWUJk*Wwb5D(bLj@V8`2;_Ie! zHjp=6v|>}9e58h!J)%bz~GD(wv>C=cF=!tjFQFuxAEab7_4r)~bRqND6%h4O~t zg~uHn9CS-p2m`IFV}jqm;%5ftfeRXl7aq5N(B*2kSMSOA+aYKS8?2v3+Ru{C&U2-f-Gq3fDUP7SZMx8pY4F~zr_EM*1mXGdXvY(c{$ZZW8aJD`p?;3mZIsE0-P=2D zFqE%>*HOd$COi|TunXT{ZmsY2^&?8!tRb6U4V~5!c$0Wk<>DA$noS+ly(a&cICIZs zPHSt4yC$9uNHd;0HD1pu=d`YRI(9+(x9XR+p*O}|3(r;!$Qs|on>C^LT6j-;IJP~y zl*dtTw=vG=5r(IZ=dle(GcQf&;>B2owmFQI05qQOb!bu+W}`fJ9X!lVdaZE0Tkt$@ zT9#&7MwrI)&TcdXGir;oJi_oWPXR++8=mp9LP0&~O_R?brv*ICxa^m%y+(Ni;t7sn zgEuwl*1&WCUVLxUr*A_WLwE1fFn*4K-*mvb-=D;V zwZA5S7I4ytBMjoff?o5cO}r@{`qFREYk~vuV#USr7~VccT0{BN5k20GPH>52(GO>% z8b*J}^BUem=V6)paM@dNdb3n_38t=a9JmaFNup+Cr#^JjN%H7AQP) zF`a!ik)e3RP358+FU{WdXsm0Y`rRQAw z$iqr4zjQs&3?PR=Vc zE~l+ra2$8h$@1>i7H=;2X9}bheyx7%c{Jz?#bYvgUQf%6No~!Qd~p#qm+ss-+cNW< z0-8Qu1Le99SIq-JOQaeXgr~-G-pVpwQz1CxDG~EriKzR#qz{8rdP*D53Cr|U@e670Ub2|2 zJidIeDdj<(vplMCp?DyeZ@+<8YcA-FmiNXZuKCdc0=W~yIN zodxl-`io&l9Y0(jF>UDBZ1`-I55|1-VG&489xM>Zq}lI4Ja~26gN?jc$_yoeBVzFd zj~etT50h8TW43@lnk`UzZ15SRN{M z5MFqky0OhaXX1JPeC3kNpM>&;;swVk=VHtCtnCEmq1P2~wf{>nhYpSNFWB6Ke$#lN zylGsqth98Z?)2@k+X~!P;2Wa?nQK<(yp=2PQsN3yzgHGi z-hWAPRobQ+Dh}H@zejmn+$v@{$znf`!Del`n>o=I#z!W8`$vw{ZHkkXT$tF+aVgS9 zJTI|8t>LwhZLIOG{PCTRf602F9p8EMZq|9;8W)P^i&H{f=d}=va68!J8~Vy0(;4qU z?Xd>#T6jK5y5yVt*?nCC`_BXV#jk}I(9dr9hWCp;nti^Z|NI8=tpB{1@iz9Kr+wS_(|h}v=o|XaZxGM-pBwM}*1mG<7r)k@d50#N(dqYAet2n*9B|{~TVE*T9>{`TleK$$EWx+$KEi+_1j)4r$=; z>mMsPI*q((zxYGzKgaiO&^Haln_j+U{pTL9vo;=bQE4gWrt!W{J&O~sn!8IbtDZ*V zw`kkOc-B8#gCKo6v%1ydt%lgEqAB-`&+(9F<5tG=edUqDN^$gy%XKVFYBe$Q6Ft!l z8qoH2elYK?&suS%JS3wAZ&h82N8k1e9{N0QjwkmMAGZ>BIp&y7<){jxdD~VBusVW? zw~M?f4c>Mpk5e@p@K)-a;Rz$hvwSOfdH;DSuCf1|K5*93FbvwX=VH3jSNhLMgYIN4 z$tZZiQ-`ur%o8DKzrPWWa9)NrxyHLXXnYL>K9+9u3@7`AHg2Q-j2i@FpF)~{O=K!+ zd8ICw7m6p`MqbXPy!OoqT6Ftw3~KJ6t_SWbE^pFGVviQqnn#Klh?n-Ct8=@`o0)!| zH}3# zz-#t&nuzZ|--H*+%Qv?4pPRnuYhGhQe!-*K8> zjngZ8I+b@~`{>N|`TlcwN0TfM5-H`8B@&8vjkp;ev#iQud!)fs0=2CG|6%7(t(Ulg6vt^CS8s#%1Mx z@M#`azdSA!uQAS7s*E?onAFMx?_>L}Y3`%0ifXxn{T1d_H0fI&=#kB8cX==JD8+^1 z)y5_2doS}YhWs-h+na=mdTp^YsSl1n%OrArwRmfJgyIngp!(idc(K`5csbAg*UdjC zP!3_t9n!r$?UyU0`s#US)jUG+EDnJfwiRCF*)j>wOW!t$n0x6Hp;=x=Cbu==UNtNI z=NRal^9aTBaUfC(D`3vc3WI`|3pk;@d+Ci+1*(}J!A!|$ipLhr8;Tbirv(v(?~FL_pWFTL zi~7-TADBldURWIK4I2B;Ia3I void: await get_tree().create_timer(0.2).timeout var combat_instance := COMBAT_SCENE.instantiate() var combat_map: CombatMap = combat_instance.find_child(\"CombatMap\") - var player_unit := Unit.new() - player_unit.stats = UnitStats.new() - player_unit.info = UnitInfo.new() - player_unit.info.name = \"Putit\" + var player_unit: Unit = LILY_CHILD.duplicate(true) player_unit.allegiance = PLAYER_ALLEGIANCE combat_map.deploy_unit(player_unit, Vector2i(3, 3)) - var enemy_unit := Unit.new() - enemy_unit.stats = UnitStats.new() - enemy_unit.info = UnitInfo.new() - enemy_unit.info.name = \"Putit\" + var enemy_unit: Unit = LILY_CHILD.duplicate(true) enemy_unit.allegiance = ENEMY_ALLEGIANCE combat_map.deploy_unit(enemy_unit, Vector2i(6, 3)) diff --git a/scripts/battle/combat_engine/combat_system.gd b/scripts/battle/combat_engine/combat_system.gd index 379a02f..8c1b1de 100644 --- a/scripts/battle/combat_engine/combat_system.gd +++ b/scripts/battle/combat_engine/combat_system.gd @@ -22,7 +22,7 @@ func create_proposal(attacker: DeployedUnit, defender: DeployedUnit, distance: i func _filter_tactics(deployed: DeployedUnit, distance: int) -> Array[CombatTactic]: var valid: Array[CombatTactic] = [] for tactic in deployed.tactics: - if tactic.tactic_range and tactic.tactic_range.is_valid_range(distance, deployed.unit): + if tactic.tactic_range and tactic.tactic_range.is_valid_range(distance, deployed.current_stats): valid.append(tactic) return valid @@ -35,27 +35,29 @@ func _find_default_attack(tactics: Array[CombatTactic]) -> CombatTactic: func _snapshot(deployed: DeployedUnit, opponent: DeployedUnit, available: Array[CombatTactic], selected: CombatTactic, opponent_selected: CombatTactic) -> CombatProposal.CombatantStats: + var current := deployed.current_stats + var opp_current := opponent.current_stats var stats := CombatProposal.CombatantStats.new() stats.deployed = deployed - stats.max_hp = deployed.unit.stats.max_hp - stats.hp = deployed.current_stats.current_hp - stats.sp = deployed.current_stats.current_sp - stats.spd = deployed.unit.stats.spd + stats.max_hp = current.max_hp + stats.hp = current.current_hp + stats.sp = current.current_sp + stats.spd = current.spd stats.available_tactics = available stats.selected_tactic = selected if selected and selected.deals_damage(): - var offensive: Dictionary = selected.get_offensive_stats(deployed.unit) + var offensive: Dictionary = selected.get_offensive_stats(current) stats.atk = offensive["atk"] - stats.hit = offensive["hit"] - opponent.unit.stats.eva + stats.hit = offensive["hit"] - opp_current.eva else: stats.atk = 0 stats.hit = 0 if opponent_selected and opponent_selected.deals_damage(): - stats.def = opponent_selected.get_relevant_defense(deployed.unit) + stats.def = opponent_selected.get_relevant_defense(current) else: - stats.def = deployed.unit.stats.phys_def + stats.def = current.phys_def return stats @@ -74,18 +76,18 @@ func update_tactic(proposal: CombatProposal, is_attacker: bool, tactic: CombatTa # Recalculate this side's offensive stats if tactic and tactic.deals_damage(): - var offensive: Dictionary = tactic.get_offensive_stats(self_stats.deployed.unit) + var offensive: Dictionary = tactic.get_offensive_stats(self_stats.deployed.current_stats) self_stats.atk = offensive["atk"] - self_stats.hit = offensive["hit"] - opp_stats.deployed.unit.stats.eva + self_stats.hit = offensive["hit"] - opp_stats.deployed.current_stats.eva else: self_stats.atk = 0 self_stats.hit = 0 # Recalculate opponent's def based on this side's new tactic if tactic and tactic.deals_damage(): - opp_stats.def = tactic.get_relevant_defense(opp_stats.deployed.unit) + opp_stats.def = tactic.get_relevant_defense(opp_stats.deployed.current_stats) else: - opp_stats.def = opp_stats.deployed.unit.stats.phys_def + opp_stats.def = opp_stats.deployed.current_stats.phys_def func select_ai_tactic(deployed: DeployedUnit, opponent: DeployedUnit, available_tactics: Array[CombatTactic]) -> CombatTactic: @@ -95,8 +97,8 @@ func select_ai_tactic(deployed: DeployedUnit, opponent: DeployedUnit, available_ for tactic in available_tactics: if not tactic.deals_damage(): continue - var offensive: Dictionary = tactic.get_offensive_stats(deployed.unit) - var defense: int = tactic.get_relevant_defense(opponent.unit) + var offensive: Dictionary = tactic.get_offensive_stats(deployed.current_stats) + var defense: int = tactic.get_relevant_defense(opponent.current_stats) var damage := maxi(offensive["atk"] - defense, 0) if damage > best_damage: best_damage = damage diff --git a/scripts/battle/combat_tactics/combat_tactic.gd b/scripts/battle/combat_tactics/combat_tactic.gd index 0627085..cc8dca9 100644 --- a/scripts/battle/combat_tactics/combat_tactic.gd +++ b/scripts/battle/combat_tactics/combat_tactic.gd @@ -3,11 +3,11 @@ class_name CombatTactic extends Resource @export var tactic_name: String = "" @export var tactic_range: CombatTacticRange -func get_offensive_stats(_unit: Unit) -> Variant: +func get_offensive_stats(_stats: DeployedUnitStats) -> Variant: return null -func get_relevant_defense(unit: Unit) -> int: - return unit.stats.phys_def +func get_relevant_defense(stats: DeployedUnitStats) -> int: + return stats.phys_def func deals_damage() -> bool: return false diff --git a/scripts/battle/combat_tactics/ranges/any_combat_tactic_range.gd b/scripts/battle/combat_tactics/ranges/any_combat_tactic_range.gd index 9b2e81b..f2f86ad 100644 --- a/scripts/battle/combat_tactics/ranges/any_combat_tactic_range.gd +++ b/scripts/battle/combat_tactics/ranges/any_combat_tactic_range.gd @@ -1,5 +1,5 @@ # resources/resource_definitions/any_combat_tactic_range.gd class_name AnyCombatTacticRange extends CombatTacticRange -func is_valid_range(_distance: int, _unit: Unit) -> bool: +func is_valid_range(_distance: int, _stats: DeployedUnitStats) -> bool: return true diff --git a/scripts/battle/combat_tactics/ranges/combat_tactic_range.gd b/scripts/battle/combat_tactics/ranges/combat_tactic_range.gd index 7645ea5..23fa8e2 100644 --- a/scripts/battle/combat_tactics/ranges/combat_tactic_range.gd +++ b/scripts/battle/combat_tactics/ranges/combat_tactic_range.gd @@ -1,5 +1,5 @@ # resources/resource_definitions/combat_tactic_range.gd class_name CombatTacticRange extends Resource -func is_valid_range(_distance: int, _unit: Unit) -> bool: +func is_valid_range(_distance: int, _stats: DeployedUnitStats) -> bool: return false diff --git a/scripts/battle/combat_tactics/ranges/fixed_combat_tactic_range.gd b/scripts/battle/combat_tactics/ranges/fixed_combat_tactic_range.gd index 0901048..b97ccc8 100644 --- a/scripts/battle/combat_tactics/ranges/fixed_combat_tactic_range.gd +++ b/scripts/battle/combat_tactics/ranges/fixed_combat_tactic_range.gd @@ -3,5 +3,5 @@ class_name FixedCombatTacticRange extends CombatTacticRange @export var tactic_range: int = 1 -func is_valid_range(distance: int, unit: Unit) -> bool: +func is_valid_range(distance: int, _stats: DeployedUnitStats) -> bool: return distance <= tactic_range diff --git a/scripts/battle/combat_tactics/ranges/unit_matching_combat_tactic_range.gd b/scripts/battle/combat_tactics/ranges/unit_matching_combat_tactic_range.gd index b668e57..dbd7c09 100644 --- a/scripts/battle/combat_tactics/ranges/unit_matching_combat_tactic_range.gd +++ b/scripts/battle/combat_tactics/ranges/unit_matching_combat_tactic_range.gd @@ -1,5 +1,5 @@ # resources/resource_definitions/unit_matching_combat_tactic_range.gd class_name UnitMatchingCombatTacticRange extends CombatTacticRange -func is_valid_range(distance: int, unit: Unit) -> bool: - return distance <= unit.stats.atk_range +func is_valid_range(distance: int, stats: DeployedUnitStats) -> bool: + return distance <= stats.atk_range diff --git a/scripts/battle/combat_tactics/tactics/attack_combat_tactic.gd b/scripts/battle/combat_tactics/tactics/attack_combat_tactic.gd index d3758bc..161fb13 100644 --- a/scripts/battle/combat_tactics/tactics/attack_combat_tactic.gd +++ b/scripts/battle/combat_tactics/tactics/attack_combat_tactic.gd @@ -1,10 +1,10 @@ class_name AttackCombatTactic extends CombatTactic -func get_offensive_stats(unit: Unit) -> Variant: - return {"atk": unit.stats.phys_atk, "hit": unit.stats.hit} +func get_offensive_stats(stats: DeployedUnitStats) -> Variant: + return {"atk": stats.phys_atk, "hit": stats.hit} -func get_relevant_defense(unit: Unit) -> int: - return unit.stats.phys_def +func get_relevant_defense(stats: DeployedUnitStats) -> int: + return stats.phys_def func deals_damage() -> bool: return true diff --git a/scripts/battle/combat_tactics/tactics/defend_combat_tactic.gd b/scripts/battle/combat_tactics/tactics/defend_combat_tactic.gd index 9fc95cb..1f4c7c6 100644 --- a/scripts/battle/combat_tactics/tactics/defend_combat_tactic.gd +++ b/scripts/battle/combat_tactics/tactics/defend_combat_tactic.gd @@ -1,10 +1,10 @@ class_name DefendCombatTactic extends CombatTactic -func get_offensive_stats(_unit: Unit) -> Variant: +func get_offensive_stats(_stats: DeployedUnitStats) -> Variant: return null -func get_relevant_defense(unit: Unit) -> int: - return unit.stats.phys_def +func get_relevant_defense(stats: DeployedUnitStats) -> int: + return stats.phys_def func deals_damage() -> bool: return false diff --git a/scripts/battle/combat_ui.gd b/scripts/battle/combat_ui.gd index 78b211f..fb70e06 100644 --- a/scripts/battle/combat_ui.gd +++ b/scripts/battle/combat_ui.gd @@ -59,7 +59,7 @@ func _on_unit_died(deployed: DeployedUnit) -> void: func _process(_delta: float) -> void: if _selected_unit and is_instance_valid(_selected_unit): - hp_bar.max_value = _selected_unit.unit.stats.max_hp + hp_bar.max_value = _selected_unit.current_stats.max_hp hp_bar.value = _selected_unit.current_stats.current_hp func _unhandled_input(event: InputEvent) -> void: @@ -72,7 +72,7 @@ func _on_unit_selected_changed(deployed: DeployedUnit, selected: bool) -> void: if selected: _selected_unit = deployed name_label.text = deployed.unit.info.name - hp_bar.max_value = deployed.unit.stats.max_hp + hp_bar.max_value = deployed.current_stats.max_hp hp_bar.value = deployed.current_stats.current_hp unit_panel.visible = true else: diff --git a/scripts/battle/deployed_units/deployed_unit.gd b/scripts/battle/deployed_units/deployed_unit.gd index 36ab0f2..0e59100 100644 --- a/scripts/battle/deployed_units/deployed_unit.gd +++ b/scripts/battle/deployed_units/deployed_unit.gd @@ -8,6 +8,9 @@ var current_stats: DeployedUnitStats var tactics: Array[CombatTactic] = [] var state: UnitState = UnitState.ALIVE +var _sprite: AnimatedSprite2D +var _previous_position: Vector2 + signal unit_selected_changed(deployed: DeployedUnit, selected: bool) signal unit_allegiance_changed(deployed: DeployedUnit, allegiance: UnitAllegiance) signal unit_died(deployed: DeployedUnit) @@ -17,6 +20,35 @@ func _ready() -> void: tactics = unit.tactics.duplicate() _append_builtin_tactics() unit_allegiance_changed.emit(self, unit.allegiance) + _previous_position = position + _setup_appearance() + +func _setup_appearance() -> void: + _sprite = get_node_or_null("AnimatedSprite2D") as AnimatedSprite2D + if not _sprite: + return + var sprite_frames: SpriteFrames = unit.appearance.deployed_sprite_sheet if unit.appearance else null + if not sprite_frames: + return + _sprite.sprite_frames = sprite_frames + _sprite.play("idle") + +func _physics_process(_delta: float) -> void: + if not _sprite or not _sprite.sprite_frames: + return + var delta_pos := position - _previous_position + _previous_position = position + if delta_pos.length_squared() < 0.01: + if _sprite.animation != &"idle": + _sprite.play("idle") + return + var anim: StringName + if absf(delta_pos.x) >= absf(delta_pos.y): + anim = &"right" if delta_pos.x > 0 else &"left" + else: + anim = &"down" if delta_pos.y > 0 else &"up" + if _sprite.animation != anim: + _sprite.play(anim) func _append_builtin_tactics() -> void: var attack := AttackCombatTactic.new() diff --git a/scripts/battle/deployed_units/deployed_unit_stats.gd b/scripts/battle/deployed_units/deployed_unit_stats.gd index 40d44e5..5f55fb2 100644 --- a/scripts/battle/deployed_units/deployed_unit_stats.gd +++ b/scripts/battle/deployed_units/deployed_unit_stats.gd @@ -5,6 +5,35 @@ class_name DeployedUnitStats extends Resource @export var current_sp: int @export var current_fs: int +# Passthrough accessors. Future buff/debuff layers can override these +# without mutating the underlying UnitStats template. +var max_hp: int: + get: return unit_stats.max_hp +var max_sp: int: + get: return unit_stats.max_sp +var max_fs: int: + get: return unit_stats.max_fs +var phys_atk: int: + get: return unit_stats.phys_atk +var phys_def: int: + get: return unit_stats.phys_def +var magic_atk: int: + get: return unit_stats.magic_atk +var magic_def: int: + get: return unit_stats.magic_def +var hit: int: + get: return unit_stats.hit +var atk_range: int: + get: return unit_stats.atk_range +var spd: int: + get: return unit_stats.spd +var eva: int: + get: return unit_stats.eva +var lck: int: + get: return unit_stats.lck +var mov: int: + get: return unit_stats.mov + static func from_unit_stats(source: UnitStats) -> DeployedUnitStats: var stats := DeployedUnitStats.new() stats.unit_stats = source diff --git a/scripts/units/unit.gd b/scripts/units/unit.gd index 87f8e20..8a24114 100644 --- a/scripts/units/unit.gd +++ b/scripts/units/unit.gd @@ -4,3 +4,4 @@ class_name Unit extends Resource @export var info: UnitInfo @export var allegiance: UnitAllegiance @export var tactics: Array[CombatTactic] = [] +@export var appearance: UnitAppearance diff --git a/scripts/units/unit_appearance.gd b/scripts/units/unit_appearance.gd new file mode 100644 index 0000000..ed852e6 --- /dev/null +++ b/scripts/units/unit_appearance.gd @@ -0,0 +1,19 @@ +class_name UnitAppearance extends Resource + +@export var default_appearance_key: String = "default" +@export var current_appearance_key: String +@export var appearance_sets: Dictionary[String, UnitAppearanceSet] + +var deployed_sprite_sheet: SpriteFrames: + get: + var key: String = current_appearance_key + if(key == null or key == ""): + key = default_appearance_key + if(!appearance_sets.has(key)): + return null + var appearance_set: UnitAppearanceSet = appearance_sets[key] + var sprite_value = appearance_set.deployed_sprite_sheet + if(sprite_value == null): + sprite_value = appearance_sets[default_appearance_key].deployed_sprite_sheet + + return sprite_value diff --git a/scripts/units/unit_appearance.gd.uid b/scripts/units/unit_appearance.gd.uid new file mode 100644 index 0000000..750dab4 --- /dev/null +++ b/scripts/units/unit_appearance.gd.uid @@ -0,0 +1 @@ +uid://b402hsmbaj536 diff --git a/scripts/units/unit_appearance_set.gd b/scripts/units/unit_appearance_set.gd new file mode 100644 index 0000000..48bd70c --- /dev/null +++ b/scripts/units/unit_appearance_set.gd @@ -0,0 +1,3 @@ +class_name UnitAppearanceSet extends Resource + +@export var deployed_sprite_sheet: SpriteFrames diff --git a/scripts/units/unit_appearance_set.gd.uid b/scripts/units/unit_appearance_set.gd.uid new file mode 100644 index 0000000..1688835 --- /dev/null +++ b/scripts/units/unit_appearance_set.gd.uid @@ -0,0 +1 @@ +uid://divxkbo321ql