From 34ce39930cb3d85199656dcebbcfbb01d362ce17 Mon Sep 17 00:00:00 2001 From: hefanyang Date: Sun, 24 May 2026 12:34:49 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=B8=E6=88=8F=E5=8F=AF=E4=BB=A5=E8=BF=90?= =?UTF-8?q?=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- card_game/__pycache__/ai.cpython-312.pyc | Bin 10862 -> 10981 bytes .../__pycache__/battlefield.cpython-312.pyc | Bin 14903 -> 15103 bytes card_game/__pycache__/config.cpython-312.pyc | Bin 23169 -> 23193 bytes card_game/__pycache__/main.cpython-312.pyc | Bin 32061 -> 33998 bytes card_game/__pycache__/player.cpython-312.pyc | Bin 9811 -> 10337 bytes card_game/__pycache__/ui.cpython-312.pyc | Bin 52882 -> 54751 bytes card_game/ai.py | 4 +- card_game/battlefield.py | 7 ++- card_game/config.py | 5 +- card_game/main.py | 42 +++++++++++++--- card_game/player.py | 20 ++++++-- card_game/ui.py | 47 ++++++++++++------ 12 files changed, 94 insertions(+), 31 deletions(-) diff --git a/card_game/__pycache__/ai.cpython-312.pyc b/card_game/__pycache__/ai.cpython-312.pyc index 3a0805a9245551e0452018394f61468d8c3043de..c12a5201af4c55aa7a7dfdab664119839410fa4a 100644 GIT binary patch delta 701 zcmaDC@-&q1G%qg~0}!}p2xZAhZ{&L^$h41j@&i8k$r3_ljQ=MGib%`K*05x8fE1yE zV!;~5EbhsS;vxd886i@|j6J$F%vr3H9TlWmS%EBG5Nq-gAr(eGkiy9uc|<1nt8q-W z78O_IFA;_rSi_mZ*ut=eX*Cl>q?Rj>uS9fmps*pMI8dK5qvYh}!m5lHCqHBqXOsq; z^i!A@D9SFP#5IAjhmnN==oa3|h9dHe{iM5D57W)8Y(P)TPBs*hVk+7^`MHQPh6f~N zk)6+m?EF0565+{(qNt94h|TdzK*tN#uog1`Y0(;17lwY0WTte6WTsdp28LRW8jdXV za6`401H;<$V#wC2umBC7wAokuA~PfZWO->D;ah^qiFxsfX=(XIDe;LVC5g$|lcS}N zGA@{`C}YpJqv*PQ@J0LJ3n39#>?0>9$n0jkHd#hi8&g7Ia=Pqlpro+eEagLtO5Or& zhg4XA?}a?q==HY6x&I27H_G|7vxSbF=lO^smRL8Rt}8iqKe6LB_$_+ zP|;WbbF?LMW)skTBp6sTj0RRll Bs;~e6 delta 554 zcmaDF`Ywd;G%qg~0}!OA3T54r+Q|1(kg1Gy@&i8k$r3_ljBh9V3rREbOr9tr#!|%C zBQyD|h%zJhR^WW~yPCT*xCbSwMkfvbC_JydcOh5UAlyVQgVo!?c!!QBB&t%vFW!M$R=@d zRGV_e)fpQ$PZht&%*Z!6UD{^yHtC~`Et6|x>?iM&RhaxzW;Wy1$*r>5K;CH}t4nT{ z@<9V7FCDf+!Yn}Y7^9MxA=@z(79e>7C~n4f5-4uA*KS8wnYQ-2`l7)92+U zWE};y2c(cR%^Y&5D$z>KrH6oAsXat$rK;+ohl)fM&h~^f zzQfUADAX*oTv9tUb4^ykZVI%T2zm7XlJnU%B)64Px+U8IEW*LktTbv)y4CJ$$Q@?N z`S(=otqrZJ31z{9O3O#a#Hk{k8BR{)B zaKV3+>{f33J9y6^>yW<9AZ9VUrRU+hz!14n5e~fPBdG!E+dY z2Eoi6LyO%IM~A`L8416}Ory~hD4#b)E74hWk0K1gNN1Nbk1>|KTA*`qt@9Kq!NblG zG70vscybDTBy9*xh4dvuV1luaMOmP92pKLRj3bl~G{|&?$P!%dx=7Y4Pr7~;;yFAo zBFrPqFnDV?M(8p#>NDO%&n@`v=t5XaK)S^6YW6% zRfKDpYEzdrcC++4o|v=7(Rv@@187LL9x`jL0NeGvM`85BD9%(=^*U(Mo4dkKR!ap7`)NfN(L+WJ|7{c zVYR=548!OBZR8aE(Et7U2~3z|Uq40~dvE r!S`$mmIqdXU!&&$0#5gr`089yS;(ue(}&=CB_xdrB=(F$G=u&HU3+x) delta 1387 zcmZ{kO>7%g5Xay1dj0Kqv)yWff6AFlil#hrytL!GS<2cRp zI%&-s65x=MphOH3swyE8aX}TImH_p{g$oFwiUZUFiB=$SL4sb8dVx1~Qm6#)@YB5a z=FR+PcC>#LJ~isT@_Jo_FY~mV**x%3U8k?E&q~OfWR{c!lV&Jv_l0dGt42+39#o)C z@;R-VP?QNNS>f?e5MK0oM3LQRim>QXU{G?yxa@~>l0RtGY(+2n8E(vh%d{9CSd-e| zR+HajidSjvxMYK5I3!t%K|-c2DM#&;z*ab@Xm-=4SxoCyt_v3>CV}l%1y*E7J0+UK zq^Y~{H7DPBYK}acFixPSIRMur7i@+bVXIz*GocpQyf>aD(}Hzc^M_0U{%BWVvfJh0 zc|;c7Evs2}3gQI{JuM0h$WE`cmqK&b8e?0cY=fKo+EAF6Rs-6YWLh%&F}9MUnx|wp z?V4xqI3a9;H@68^+x+lZs1Du;`**b|yq`|lq^A8}_0m0k^}G6B@VQ{cZ*Tg)x=AL^ zleWQ5s~_70Klmr80#i!6xlmoX%iXdEM%G)669FSok1TVtLNAp z495oPBD@kCqm%GWtdpLDJF##>5}l_IXAp}B28npHv%t-Khy$L82SAGt(3i{G@t?)c zH2TaVa)=p@Cr>6QTjIuE)tB&Q9X{wfOr7OlyEZJ(y~R%|E0Ncnz_^QLz|C#crsK_bSh} zAkcG)`pT)Eh=rQ4(i`w^ai_e%5s5@t0G}O&cY7nWtNdl}7>(dJDTanr!^rA0X+5iT zr>3*Ink$@7X}YTQm-`MXln%jYe+NAd^ZoniL$KNZMRpt`O>$JkB{f^nE25E}*IB~C z>hT6^NQku|sS3X9&)(x#d69z)+LMGcQViasLe;dCJ~N|F8^iGVp_}x5*f~5!PlIse z!)_cw_7vhW;tJvM$(6IL6&kKyK#XT^l5+&&9~4;D{+f;Wu_?8Y6(!2E(%$m++~ jHNM5aS3b<~yFe+uK}LQd9Y2!3KW(ETP248P>f65oiS$nc diff --git a/card_game/__pycache__/config.cpython-312.pyc b/card_game/__pycache__/config.cpython-312.pyc index 7067ebfc921a86e567c247279398785eb4f47aa2..b5b6e3e3000eacc8e212e54a3d2e09892f506900 100644 GIT binary patch delta 1524 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 delta 5155 zcmbVQ3viUx75?wP??)bFH=75^l8}&O6JC*L5Ktk20gVYTU`1xL?7vAi*-g^Be}KeY z1A>M`DXGT}Dr&K|RjVD)wc~?_;JekTs8##-F+N5eA7e3$b$pFI=l;7{pzU;~Kf{-M z&%O8DbIv{YoOAc(5&76REc?Z*EW3nXODM0ay7i&#U&-t~ctpP6IWXB*q8V2OqM;yz z6BTaREW?eCsc?eXlMfljm@zJO)xpWu66iJMOfkeIzlZqHr%o3cmrRere)C+m99}YSU`L?Bav=GfB_S`Z#&JFuL1XQjQ3vh(~BnFP!#V z;3kDN1Mlw{vwJbTmV6MUGAa(t*P$U#op}%-)C7FeL5qt(A9B&hA+rvuCfGu5; zBgL~>6h14y$XT_gaqsHWHp!X8Ym?8H-eZ8{C8f|`zS4OEtuuCq24ehrc)r{_cL)hS zbA~CW8Jx7#%)M%72+g+xJ25i?Zi64{Tr_nl+m^g?>T!#C7t;B)P*PjL>fz$r{DNjA z_+0!(qTfsa1h+t>wy=1Fn)?Zw2+9a9Bv=D?)soSU)>ah@bF~rmtmV|*58u@;WHm6a zZZX@NAt4+Q?Bgt0n#$i-~48%(aZYQAKG&2uHd$)%8o!B|sc>FFTWQ<4i1Ms(5 zrR)%VJL{LM77orX$|*yC-Y=;PN9lv|nFX-n5)XK*t#~>71~@g_Yv6n0aQ93Pln%O~ z?=lzE8QpN~nleL&l%iL;AbzpERCVcG@b<6=QY;&#%~g_=U>ObzE^*NXG~HBVOs(Ii zQ)IOWu8CCT;=WmFMtDI>;O>MAzOE6g8WkCbbJN3kFAdEf(*@HkeUZRGxTnK6iCeL+ zo4%Y{QwYfUH8af#akyz-mBSuW16=iY;6;Sb=S^Gall4o%{4u)Y>FfFvY!Bw@*EO@^ z38coJLD(U|*7;s|b1(-y3rb=60*|2{u7u4CrcJUs-?A2-vKGE&tvF?^c*8mko?TF3 z&G+sx?zQt;@LyDv^e${NVyNm|T;e{2%(QI3zC|GHSNnMn>|Oju&V;jQ@|JdfAM`D$ zKo7ZP$$a)g^2H^<&_nze9g=IFMFu=cTui-O332`~wtZQ;_V>}C@S8(0f0@_(IQ1Wc ztCnpj75qsRPLs~{mlN>EWs~K9Nbt$B@6lmUWcey1)VE9qZ{s`60zWhs6ds7Pu8H$N z4yi8&Eh~zd4Z2s97l`0Ybaf)2#*JM?@WrZXc=!T0Gs59AH~NGXj;*j7nG|QxccCB{ zxhrmhopao9;YuGIonwRXg+(xStsBPH+RVH$E_YQ!OZ60k+*JcFuAGA#{;;x~*`a(@ z9lMR>E4t~w$TtM7D~kSK0nd?X#Q^39`sXt@YnE7lUtcd*`R`C)%@Pdsh1EcWKSDiQ zJMZmLBjKKqFF$n}NKTy)ym#p`CqGJr#|WBWdDBJaJE-{*>}gt%D-KGWvN+A>;FYH7 z<{BD*3fSsmGa62cJqG2gx52(Om%vA>Z^yrFj+%x&L#GW=c2&yf3NCZYts-`PKx;oF z5H}>_Bjg>Lt0ScPQ_A>bkzUmwP0i%E^a)&a)zIW(UFi8F-<& zf^CP-nhSNokAVFOFPjFwD?URv_OzUohh8Th4&6G!OEa<&dw!DOC4yH7UPaK{Bx1(;(&xC ze+SdX8E|}FK}}9D5(r2AnbSQx+lZxN$Iqc{V@X8~?Q;xaut1M0oq--D67o|F_Xi{4 zV0SD0eB=A6-S4<^M6UY+XQN%k{mty3G0YiaDQt2QS~k_xk7HlU4ybAXb8N~YXw!*E zq8s38S|y}=7w+G5seU5I;j2wvb33_`19q-+!>p^n#iOa)yjNzI!{h!nL!6d9Nbp;N z>j{J?Z(>W!qN4~zBL4oKuo~lgiRMtTOz|s$XrLp+-$6>+M+kzB<%h43`mzycn!zB-n6M?R>g2TKaal1xKq&}6?v$mtB`&A4so-Q!I5M0P_Vai- zMlfY!Cbt9|EY`R!ZjU=)Rj|o{e-+Chngyfl!5Tf>J{sJ?K7@tJRQ3UEQf9D5tO?vX zF5LLcA~f62?E9sW94K$i$9V6;yqp#T(>Y5Ge69SNWy8Uc2iiiWN!A|Yh;<}OC}oTr zN3x(Vz2UpVhuyEc=fB#}^g`>YhUQc5<}vo?ni;oEKD_2c<*B(V$INfLi*H?ju=Tbd zopM)=vA11rodYM&<3fcEq=H*>1_N9Xb;Zp7u#l4riABsMeWvj|(m+IBp=3rs@>rfv zqaL`Yqj^YF>-lP*!$v`qN)8-{E*Va0L_v0Gv#YdJ33Vjd-Tjq3bUBV|HmddfR4Vg(iOpIBd|5eCI#9&o-_roODHn;zE;iT2 z5M}&KvK0BR<_Ptm`g)-zUR8WHjfo1Ktdx>iGvm80j~;H*$U1ajXjt^gGd;EJ`J}h^ zgnWUJrVfXFHhq02wBt>d_>;7sy^|!zaLyHe5^{OPs!Ti=QCvh*k!b#7Y6~Kh!Mt-) zj^Owa4Dy>X+hoO{em-e12`BoDXVoV{B_UE34#jYelzPi{z^PcF{3U}=V;5qUs8(-a z6;v>1w76fds>N$=qOOMD_b+mMPv+Q-ld*(unLZ1aY+YX>qIfFw;fNl=$z*4m8J3;J z;BBxZU*0-{4T)k)h{~_UaZZf%Q>9Z#zm#?ob&TK_1kV#J#c{rbHm8l!WxgNV=UGSO zVlz&P(KBm@^tK~s-=dL4w|ZW4Oo8XNd*Gkj+tF^D2foS@XJ^s1o(q9N@9c8iKr{FE z;ST^Ie!kf{X)~vO6#A4rI5fBxnSE+0k~<9(sxG~zf*H3;{BAhd=YiSy{3@#Vzz&}k zZd+RjuWYmAMijGRiCCky1j7U+Gkm}m!nQ@O*AKe1YrtrWve>Bw7ujPv7SwA80 zGMoXo3{}>MC`+30MmhpZJ67EOsKQkuSqMA?{qW9^uR+|P%w(e)+OD4Wb4?g$58Cy! zqFsx~A(~*>4qtNLj!I@Bmu>cy!V^2E4f%+31;HAEIKfVWj0*;+E3Sxe#uL5-r`NP(Hw#X0#Opup8Q5=-qpaSz@A+zt`fg3 z%|ohY!Jn*pFceeO7wZeCoto9}Zx3S)?AL$D5`ter$7iIFts8PJX)ls3Dff+aakN+I gkA#-;%}~92YtBY1Yi3_a;?HR{7arX0HPY;V0iu}#^#A|> diff --git a/card_game/__pycache__/player.cpython-312.pyc b/card_game/__pycache__/player.cpython-312.pyc index 2a4d497589f22be77131d66753af519ae5895247..0e209a4d40c43274a7e7f87e1017de43d75b917f 100644 GIT binary patch delta 2637 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 G6a6>7b(~oM delta 5392 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 enemy support > capital for unit in ai.get_frontline_units(): - if not unit.can_attack or unit.has_attacked: + if not unit.can_attack or unit.has_attacked or not ai.can_afford_attack(unit): continue enemy_front = player.get_frontline_units() enemy_support = player.get_support_units() @@ -104,7 +104,7 @@ class AIPlayer: # Support units: attack enemy frontline > enemy support > capital (ranged only) for unit in ai.get_support_units(): - if not unit.can_attack or unit.has_attacked: + if not unit.can_attack or unit.has_attacked or not ai.can_afford_attack(unit): continue enemy_front = player.get_frontline_units() enemy_support = player.get_support_units() diff --git a/card_game/battlefield.py b/card_game/battlefield.py index 18ebd8f..1823de7 100644 --- a/card_game/battlefield.py +++ b/card_game/battlefield.py @@ -79,12 +79,17 @@ class Battlefield: def resolve_attack(self, attacker, defender): dead = [] + owner = self._get_unit_owner(attacker) + if owner: + attack_cost = owner.get_attack_cost(attacker) + if owner.provisions < attack_cost: + return dead + owner.provisions -= attack_cost atk = attacker.get_effective_attack() if "siege" in attacker.abilities and defender == "capital": atk *= 2 - owner = self._get_unit_owner(attacker) if (owner and owner.faction_id == "han" and attacker.unit_type == "archer" and defender == "capital"): atk += 1 diff --git a/card_game/config.py b/card_game/config.py index 7d3e3ba..ea6e19d 100644 --- a/card_game/config.py +++ b/card_game/config.py @@ -95,9 +95,10 @@ MAX_HAND_SIZE = 8 # --- Game Rules --- STARTING_CAPITAL_HP = 20 -MAX_PROVISIONS = 10 +MAX_PROVISIONS = 12 DECK_SIZE = 30 -STARTING_HAND_SIZE = 4 +FIRST_HAND_SIZE = 4 +SECOND_HAND_SIZE = 5 FATIGUE_START_DAMAGE = 1 # --- Rarity --- diff --git a/card_game/main.py b/card_game/main.py index 95a125f..63a4700 100644 --- a/card_game/main.py +++ b/card_game/main.py @@ -33,6 +33,7 @@ class Game: self.player_faction = None self.ai_faction = None self.custom_deck = None + self.player_goes_first = True self._load_custom_deck() self.custom_deck = None @@ -112,6 +113,16 @@ class Game: self.player_faction = fid self.custom_deck = None self._load_custom_deck() + self.state = "turn_order" + + # --- State: Turn Order --- + + def _handle_click_turn_order(self, pos): + if "first" in self.ui.menu_buttons and self.ui.menu_buttons["first"].collidepoint(pos): + self.player_goes_first = True + self.state = "deck_select" + elif "second" in self.ui.menu_buttons and self.ui.menu_buttons["second"].collidepoint(pos): + self.player_goes_first = False self.state = "deck_select" # --- State: Deck Select --- @@ -184,16 +195,30 @@ class Game: self.ui.deck_builder_cards.remove(cid) def _start_game(self): + from card_game.config import FIRST_HAND_SIZE, SECOND_HAND_SIZE self.battlefield = Battlefield(self.player_faction, self.ai_faction) + if self.player_goes_first: + self.battlefield.player.start_game(FIRST_HAND_SIZE) + self.battlefield.ai.start_game(SECOND_HAND_SIZE) + else: + self.battlefield.player.start_game(SECOND_HAND_SIZE) + self.battlefield.ai.start_game(FIRST_HAND_SIZE) if self.custom_deck: self.battlefield.player.deck.build(self.custom_deck) + hand_size = FIRST_HAND_SIZE if self.player_goes_first else SECOND_HAND_SIZE self.battlefield.player.hand = [] - for _ in range(4): + for _ in range(hand_size): self.battlefield.player.draw_card() - self.battlefield.start_game() + self.battlefield.turn_number = 1 + self.battlefield.frontline_controller = None + self.battlefield.current_turn = "player" + self.battlefield.player.start_turn(1) self.ai_player = AIPlayer(self.battlefield) self.ui.clear_selection() - self.state = "playing" + if self.player_goes_first: + self.state = "playing" + else: + self._start_ai_turn() # --- State: Playing --- @@ -267,7 +292,7 @@ class Game: opponent = self.battlefield.get_opponent(player) if unit.zone == "support": - if unit.can_attack and not unit.has_attacked: + if unit.can_attack and not unit.has_attacked and player.can_afford_attack(unit): self.ui.selected_unit = unit self.ui.target_mode = "attack" targets = list(opponent.get_frontline_units()) @@ -282,7 +307,7 @@ class Game: self.ui.selected_unit = unit self.ui.target_mode = "move" elif unit.zone == "frontline": - if unit.can_attack and not unit.has_attacked: + if unit.can_attack and not unit.has_attacked and player.can_afford_attack(unit): self.ui.selected_unit = unit self.ui.target_mode = "attack" targets = list(opponent.get_frontline_units()) @@ -417,8 +442,7 @@ class Game: y = zone_y + ZONE_HEIGHT // 2 else: x = _frontline_slot_x(unit.slot, n_fl) + FIELD_CARD_WIDTH // 2 - zone_y = FRONTLINE_Y if is_ai else (FRONTLINE_Y + half) - y = zone_y + half // 2 + y = FRONTLINE_Y + ZONE_HEIGHT // 2 return x, y try: @@ -518,6 +542,8 @@ class Game: def _handle_click(self, pos): if self.state == "menu": self._handle_click_menu(pos) + elif self.state == "turn_order": + self._handle_click_turn_order(pos) elif self.state == "deck_select": self._handle_click_deck_select(pos) elif self.state == "deck_build": @@ -537,6 +563,8 @@ class Game: def _draw(self): if self.state == "menu": self.ui.draw_menu() + elif self.state == "turn_order": + self.ui.draw_turn_order(self.player_faction) elif self.state == "deck_select": self.ui.draw_deck_select(self.player_faction) elif self.state == "deck_build": diff --git a/card_game/player.py b/card_game/player.py index 4547a59..894cf86 100644 --- a/card_game/player.py +++ b/card_game/player.py @@ -2,8 +2,9 @@ from card_game.config import ( STARTING_CAPITAL_HP, MAX_PROVISIONS, MAX_HAND_SIZE, - MAX_SUPPORT_SLOTS, MAX_FRONTLINE_SLOTS, DECK_SIZE, STARTING_HAND_SIZE, + MAX_SUPPORT_SLOTS, MAX_FRONTLINE_SLOTS, DECK_SIZE, FATIGUE_START_DAMAGE, FACTIONS, DECK_PRESETS, + FIRST_HAND_SIZE, SECOND_HAND_SIZE, ) from card_game.deck import Deck from card_game.card import Card @@ -33,14 +34,16 @@ class Player: preset = DECK_PRESETS[self.faction_id] self.deck.build(preset["cards"]) - def start_game(self): + def start_game(self, hand_size=None): self.build_deck() - for _ in range(STARTING_HAND_SIZE): + if hand_size is None: + hand_size = FIRST_HAND_SIZE + for _ in range(hand_size): self.draw_card() def start_turn(self, turn_number): self.turn_number = turn_number - gained = min(turn_number + 1, MAX_PROVISIONS) + gained = min(turn_number, MAX_PROVISIONS) self.provisions = gained self.max_provisions = gained @@ -176,6 +179,15 @@ class Player: if unit.current_hp < unit.max_hp: unit.current_hp = min(unit.max_hp, unit.current_hp + 1) + def get_attack_cost(self, unit): + cost = unit.op_cost + if self.faction_id == "yan" and unit.unit_type == "cavalry": + cost = max(0, cost - 1) + return cost + + def can_afford_attack(self, unit): + return self.provisions >= self.get_attack_cost(unit) + def cleanup_dead(self): for i in range(len(self.support_line)): u = self.support_line[i] diff --git a/card_game/ui.py b/card_game/ui.py index 410a0d7..31fd6fb 100644 --- a/card_game/ui.py +++ b/card_game/ui.py @@ -131,6 +131,30 @@ class UI: inst = self.font_sm.render("选择你的国家开始游戏", True, INK_WASH_3) self.screen.blit(inst, (WINDOW_WIDTH // 2 - inst.get_width() // 2, WINDOW_HEIGHT - 60)) + def draw_turn_order(self, player_faction): + ink_style.blit_paper_background(self.screen) + ink_style.blit_mountains(self.screen) + faction = FACTIONS[player_faction] + title = self.font_xl.render("选择先后手", True, FACTION_COLORS[player_faction]) + self.screen.blit(title, (WINDOW_WIDTH // 2 - title.get_width() // 2, 100)) + faction_text = self.font_lg.render(f"你的国家:{faction['name']}", True, PAPER_WHITE) + self.screen.blit(faction_text, (WINDOW_WIDTH // 2 - faction_text.get_width() // 2, 180)) + + self.menu_buttons = {} + for key, lbl, desc, y in [ + ("first", "先手", "先行动,起始4张手牌", 280), + ("second", "后手", "后行动,起始5张手牌", 370), + ]: + btn_w, btn_h = 300, 70 + rect = pygame.Rect(WINDOW_WIDTH // 2 - btn_w // 2, y, btn_w, btn_h) + ink_style.draw_ink_rect(self.screen, rect, FACTION_COLORS[player_faction], alpha=200) + pygame.draw.rect(self.screen, PAPER_WHITE, rect, 2, border_radius=5) + t = self.font_lg.render(lbl, True, PAPER_WHITE) + self.screen.blit(t, (rect.centerx - t.get_width() // 2, y + 10)) + d = self.font_sm.render(desc, True, LIGHT_GRAY) + self.screen.blit(d, (rect.centerx - d.get_width() // 2, y + 45)) + self.menu_buttons[key] = rect + def draw_deck_select(self, player_faction): ink_style.blit_paper_background(self.screen) ink_style.blit_mountains(self.screen) @@ -423,27 +447,23 @@ class UI: def _draw_frontline(self, battlefield): y = FRONTLINE_Y - half = ZONE_HEIGHT // 2 - - # Center divider line - pygame.draw.line(self.screen, INK_WASH_3, (0, y + half), (WINDOW_WIDTH, y + half), 1) lbl = self.font_sm.render("前 线", True, (ZHU_HONG[0], ZHU_HONG[1], ZHU_HONG[2])) self.screen.blit(lbl, (WINDOW_WIDTH // 2 - lbl.get_width() // 2, y + 3)) n_fl = MAX_FRONTLINE_SLOTS + uy = y + (ZONE_HEIGHT - FIELD_CARD_HEIGHT) // 2 + for i, unit in enumerate(battlefield.ai.frontline): if unit is None: continue ux = _frontline_slot_x(i, n_fl) - uy = y + (half - FIELD_CARD_HEIGHT) // 2 self._draw_field_unit(unit, ux, uy, False, battlefield.ai) for i, unit in enumerate(battlefield.player.frontline): if unit is None: continue ux = _frontline_slot_x(i, n_fl) - uy = y + half + (half - FIELD_CARD_HEIGHT) // 2 self._draw_field_unit(unit, ux, uy, True, battlefield.player) def _draw_capital(self, player, zone_y): @@ -659,10 +679,9 @@ class UI: ux = _support_slot_x(unit.slot) uy = zone_y + (zone_h - FIELD_CARD_HEIGHT) // 2 else: - half = ZONE_HEIGHT // 2 n_fl = MAX_FRONTLINE_SLOTS - zone_y = FRONTLINE_Y if is_ai else (FRONTLINE_Y + half) - zone_h = half + zone_y = FRONTLINE_Y + zone_h = ZONE_HEIGHT ux = _frontline_slot_x(unit.slot, n_fl) uy = zone_y + (zone_h - FIELD_CARD_HEIGHT) // 2 s = pygame.Surface((FIELD_CARD_WIDTH, FIELD_CARD_HEIGHT), pygame.SRCALPHA) @@ -681,12 +700,11 @@ class UI: # Move highlights if self.target_mode == "move": - half = ZONE_HEIGHT // 2 n_fl = MAX_FRONTLINE_SLOTS for i, slot in enumerate(battlefield.player.frontline): if slot is None: sx = _frontline_slot_x(i, n_fl) - sy = FRONTLINE_Y + half + (half - FIELD_CARD_HEIGHT) // 2 + sy = FRONTLINE_Y + (ZONE_HEIGHT - FIELD_CARD_HEIGHT) // 2 s = pygame.Surface((FIELD_CARD_WIDTH, FIELD_CARD_HEIGHT), pygame.SRCALPHA) s.fill((100, 180, 220, 50)) self.screen.blit(s, (sx, sy)) @@ -717,7 +735,7 @@ class UI: n_fl = MAX_FRONTLINE_SLOTS zones = [ (battlefield.player.support_line, PLAYER_SUPPORT_Y, ZONE_HEIGHT, battlefield.player, "support"), - (battlefield.player.frontline, FRONTLINE_Y + half, half, battlefield.player, "frontline"), + (battlefield.player.frontline, FRONTLINE_Y, ZONE_HEIGHT, battlefield.player, "frontline"), (battlefield.ai.support_line, ENEMY_SUPPORT_Y, ZONE_HEIGHT, battlefield.ai, "support"), (battlefield.ai.frontline, FRONTLINE_Y, half, battlefield.ai, "frontline"), ] @@ -750,12 +768,11 @@ class UI: def get_frontline_slot_at(self, pos, player): mx, my = pos - half = ZONE_HEIGHT // 2 n_fl = MAX_FRONTLINE_SLOTS - zone_y = FRONTLINE_Y + half + zone_y = FRONTLINE_Y for i, slot in enumerate(player.frontline): sx = _frontline_slot_x(i, n_fl) - sy = zone_y + (half - FIELD_CARD_HEIGHT) // 2 + sy = zone_y + (ZONE_HEIGHT - FIELD_CARD_HEIGHT) // 2 rect = pygame.Rect(sx, sy, FIELD_CARD_WIDTH, FIELD_CARD_HEIGHT) if rect.collidepoint(mx, my): return i