@@ -154,6 +154,7 @@ private async void TimerOnElapsed(object sender, ElapsedEventArgs elapsedEventAr
154154 RunDefenderAssignment ( ) ;
155155 await LaunchAllBattles ( ) ;
156156 RunGalaxyTick ( ) ;
157+ await ApplyTurnEndChargeBump ( ) ;
157158 AttackerSideCounter ++ ;
158159 ResetAttackOptions ( ) ;
159160 }
@@ -420,6 +421,8 @@ private double GetPlayerWhr(string name)
420421
421422 private async Task LaunchAllBattles ( )
422423 {
424+ var attackerNamesToChargeSpend = new List < string > ( ) ;
425+
423426 // merge squads on the same planet into one battle per planet
424427 foreach ( var planetId in FormedSquads . Select ( s => s . PlanetID ) . Distinct ( ) . ToList ( ) )
425428 {
@@ -434,6 +437,8 @@ private async Task LaunchAllBattles()
434437 merged . Defenders . AddRange ( squad . Defenders . Where ( x => server . ConnectedUsers . ContainsKey ( x ) ) ) ;
435438 }
436439
440+ if ( merged . Attackers . Count > 0 ) attackerNamesToChargeSpend . AddRange ( merged . Attackers ) ;
441+
437442 if ( merged . Defenders . Count > 0 && merged . Attackers . Count > 0 )
438443 {
439444 // battle (may be uneven)
@@ -473,6 +478,30 @@ private async Task LaunchAllBattles()
473478
474479 FormedSquads . Clear ( ) ;
475480 DefenderVotes . Clear ( ) ;
481+
482+ if ( attackerNamesToChargeSpend . Count > 0 ) await SpendAttackCharges ( attackerNamesToChargeSpend ) ;
483+ }
484+
485+ private async Task SpendAttackCharges ( List < string > playerNames )
486+ {
487+ var max = DynamicConfig . Instance . PwAttackChargesMax ;
488+ if ( max <= 0 ) return ;
489+ try
490+ {
491+ List < Account > accounts ;
492+ using ( var db = new ZkDataContext ( ) )
493+ {
494+ var turn = db . Galaxies . First ( g => g . IsDefault ) . Turn ;
495+ accounts = db . Accounts . Where ( a => playerNames . Contains ( a . Name ) ) . ToList ( ) ;
496+ foreach ( var acc in accounts ) acc . SpendPwAttackCharge ( turn ) ;
497+ db . SaveChanges ( ) ;
498+ }
499+ await Task . WhenAll ( accounts . Select ( acc => SendPwAttackCharges ( server , acc . Name , acc ) ) ) ;
500+ }
501+ catch ( Exception ex )
502+ {
503+ Trace . TraceError ( "PlanetWars SpendAttackCharges error: {0}" , ex ) ;
504+ }
476505 }
477506
478507
@@ -540,6 +569,13 @@ private async Task JoinPlanetAttack(int targetPlanetId, string userName)
540569 var account = db . Accounts . Find ( user . AccountID ) ;
541570 if ( account == null || account . FactionID != AttackingFaction . FactionID || ! account . CanPlayerPlanetWars ( ) ) return ;
542571
572+ var maxCharges = DynamicConfig . Instance . PwAttackChargesMax ;
573+ if ( maxCharges > 0 && account . PwAttackCharges <= 0 )
574+ {
575+ await server . GhostChanSay ( user . Faction , $ "{ userName } cannot attack: out of attack charges") ;
576+ return ;
577+ }
578+
543579 // remove from other options
544580 foreach ( var aop in AttackOptions . Where ( x => x . PlanetID != targetPlanetId ) )
545581 aop . Attackers . RemoveAll ( x => x == userName ) ;
@@ -604,7 +640,11 @@ public async Task OnLoginAccepted(ConnectedUser connectedUser)
604640 if ( MiscVar . PlanetWarsMode == PlanetWarsModes . Running )
605641 {
606642 var u = connectedUser . User ;
607- if ( u . CanUserPlanetWars ( ) ) await UpdateLobby ( u . Name ) ;
643+ if ( u . CanUserPlanetWars ( ) )
644+ {
645+ await UpdateLobby ( u . Name ) ;
646+ await SendPwAttackChargesForUser ( u . Name ) ;
647+ }
608648 }
609649 }
610650
@@ -939,6 +979,75 @@ public void RemoveFromRunningBattles(int battleID)
939979 RunningBattles . Remove ( battleID ) ;
940980 }
941981
982+
983+ // ===================== ATTACK CHARGES =====================
984+
985+ public static PwAttackCharges BuildPwAttackCharges ( Account account )
986+ {
987+ var max = DynamicConfig . Instance . PwAttackChargesMax ;
988+ int ? nextRechargeTurn = null ;
989+ if ( max > 0 && account . PwAttackCharges < max && account . PwLastChargeChangeTurn != null )
990+ nextRechargeTurn = account . PwLastChargeChangeTurn . Value + DynamicConfig . Instance . PwAttackChargesCooldownTurns ;
991+ return new PwAttackCharges
992+ {
993+ Current = account . PwAttackCharges ,
994+ NextRechargeTurn = nextRechargeTurn ,
995+ } ;
996+ }
997+
998+ public static async Task SendPwAttackCharges ( ZkLobbyServer . ZkLobbyServer server , string userName , Account account )
999+ {
1000+ var conus = server . ConnectedUsers . Get ( userName ) ;
1001+ if ( conus == null ) return ;
1002+ await conus . SendCommand ( BuildPwAttackCharges ( account ) ) ;
1003+ }
1004+
1005+ private async Task SendPwAttackChargesForUser ( string userName )
1006+ {
1007+ var conus = server . ConnectedUsers . Get ( userName ) ;
1008+ if ( conus ? . User == null ) return ;
1009+ using ( var db = new ZkDataContext ( ) )
1010+ {
1011+ var account = db . Accounts . Find ( conus . User . AccountID ) ;
1012+ if ( account == null ) return ;
1013+ await conus . SendCommand ( BuildPwAttackCharges ( account ) ) ;
1014+ }
1015+ }
1016+
1017+ private async Task ApplyTurnEndChargeBump ( )
1018+ {
1019+ try
1020+ {
1021+ var max = DynamicConfig . Instance . PwAttackChargesMax ;
1022+ if ( max <= 0 ) return ;
1023+ var cooldown = DynamicConfig . Instance . PwAttackChargesCooldownTurns ;
1024+
1025+ List < Account > bumped ;
1026+ using ( var db = new ZkDataContext ( ) )
1027+ {
1028+ var turn = db . Galaxies . First ( g => g . IsDefault ) . Turn ;
1029+ bumped = db . Accounts . Where ( a =>
1030+ a . FactionID != null &&
1031+ a . PwAttackCharges < max &&
1032+ ( a . PwLastChargeChangeTurn == null || turn - a . PwLastChargeChangeTurn >= cooldown ) ) . ToList ( ) ;
1033+
1034+ foreach ( var acc in bumped )
1035+ {
1036+ acc . PwAttackCharges ++ ;
1037+ acc . PwLastChargeChangeTurn = turn ;
1038+ }
1039+
1040+ if ( bumped . Count > 0 ) db . SaveChanges ( ) ;
1041+ }
1042+
1043+ await Task . WhenAll ( bumped . Select ( a => SendPwAttackCharges ( server , a . Name , a ) ) ) ;
1044+ }
1045+ catch ( Exception ex )
1046+ {
1047+ Trace . TraceError ( "PlanetWars turn-end charge bump error: {0}" , ex ) ;
1048+ }
1049+ }
1050+
9421051 private async Task UpdateLobby ( )
9431052 {
9441053 // per-player: flags (CanSelectForBattle / PlayerIsAttacker) depend on the viewer
0 commit comments