Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Shared/LobbyClient/Protocol/Messages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -749,6 +749,13 @@ public class PwStatus
public int MinLevel { get; set; }
}

[Message(Origin.Server)]
public class PwAttackCharges
{
public int Current { get; set; }
public int? NextRechargeTurn { get; set; }
}




Expand Down
7 changes: 7 additions & 0 deletions Zero-K.info/Controllers/PlanetwarsAdminController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,13 @@ public ActionResult StartGalaxy(int galaxyID)
gal.Ended = null;
gal.EndMessage = null;
db.SaveChanges();

var maxCharges = DynamicConfig.Instance.PwAttackChargesMax;
db.Accounts.Where(x => x.FactionID != null).Update(x => new Account()
{
PwAttackCharges = maxCharges,
PwLastChargeChangeTurn = null,
});
}

return RedirectToAction("Index");
Expand Down
17 changes: 17 additions & 0 deletions ZkData/Ef/Account.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ public static double AdjustEloWeight(double currentWeight, double sumWeight, int
public double PwWarpProduced { get; set; }
public double PwWarpUsed { get; set; }
public double PwAttackPoints { get; set; }
public int PwAttackCharges { get; set; }
public int? PwLastChargeChangeTurn { get; set; }
public bool HasVpnException { get; set; }
public bool HasKudos { get; set; }
public int ForumTotalUpvotes { get; set; }
Expand Down Expand Up @@ -530,6 +532,21 @@ public void ResetQuotas()



public void SpendPwAttackCharge(int currentTurn)
{
if (PwAttackCharges > 0) PwAttackCharges--;
PwLastChargeChangeTurn = currentTurn;
}

public void GrantPwAttackCharge(int maxCharges, int currentTurn)
{
if (maxCharges <= 0) return;
if (PwAttackCharges >= maxCharges) return;
PwAttackCharges++;
PwLastChargeChangeTurn = currentTurn;
}


public void SpendBombers(double count)
{
PwBombersUsed += count;
Expand Down
4 changes: 2 additions & 2 deletions ZkData/Ef/DynamicConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ public class DynamicConfig
[Description("PlanetWars: maximum attack charges a player can hold. 0 disables the charge system.")]
public int PwAttackChargesMax { get; set; } = 2;

[Description("PlanetWars: minutes a player must sit below max charges before gaining one passively.")]
public int PwAttackChargesRechargeMinutes { get; set; } = 60;
[Description("PlanetWars: turns a player must be idle (no spend/gain) before their next +1 passive charge, up to max. Every charge gain or loss resets this clock, so the average regen rate is at most 1 charge per this many turns.")]
public int PwAttackChargesCooldownTurns { get; set; } = 4;

[Description("PlanetWars: fraction of enemy bomber IP damage also applied to the bomber's own faction. 0 disables self-damage.")]
public double PwBomberSelfIpRate { get; set; } = 0.5;
Expand Down
29 changes: 29 additions & 0 deletions ZkData/Migrations/202604241242120_AddPwAttackCharges.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 24 additions & 0 deletions ZkData/Migrations/202604241242120_AddPwAttackCharges.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
namespace ZkData.Migrations
{
using System;
using System.Data.Entity.Migrations;

public partial class AddPwAttackCharges : DbMigration
{
public override void Up()
{
AddColumn("dbo.Accounts", "PwAttackCharges", c => c.Int(nullable: false, defaultValue: 2));
AddColumn("dbo.Accounts", "PwLastChargeChangeTurn", c => c.Int());
AddColumn("dbo.DynamicConfigs", "PwAttackChargesCooldownTurns", c => c.Int(nullable: false, defaultValue: 4));
DropColumn("dbo.DynamicConfigs", "PwAttackChargesRechargeMinutes");
}

public override void Down()
{
AddColumn("dbo.DynamicConfigs", "PwAttackChargesRechargeMinutes", c => c.Int(nullable: false, defaultValue: 60));
DropColumn("dbo.DynamicConfigs", "PwAttackChargesCooldownTurns");
DropColumn("dbo.Accounts", "PwLastChargeChangeTurn");
DropColumn("dbo.Accounts", "PwAttackCharges");
}
}
}
126 changes: 126 additions & 0 deletions ZkData/Migrations/202604241242120_AddPwAttackCharges.resx

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions ZkData/ZkData.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,10 @@
<Compile Include="Migrations\202604231826040_AddPwBomberNerfConfigs.Designer.cs">
<DependentUpon>202604231826040_AddPwBomberNerfConfigs.cs</DependentUpon>
</Compile>
<Compile Include="Migrations\202604241242120_AddPwAttackCharges.cs" />
<Compile Include="Migrations\202604241242120_AddPwAttackCharges.Designer.cs">
<DependentUpon>202604241242120_AddPwAttackCharges.cs</DependentUpon>
</Compile>
<Compile Include="SpringFilesUnitsyncAttempt.cs" />
<Compile Include="SteamWebApi.cs" />
<Compile Include="UserLanguageNoteAttribute.cs" />
Expand Down Expand Up @@ -967,6 +971,9 @@
<EmbeddedResource Include="Migrations\202604231826040_AddPwBomberNerfConfigs.resx">
<DependentUpon>202604231826040_AddPwBomberNerfConfigs.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Migrations\202604241242120_AddPwAttackCharges.resx">
<DependentUpon>202604241242120_AddPwAttackCharges.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="ZkDataResources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>ZkDataResources.Designer.cs</LastGenOutput>
Expand Down
111 changes: 110 additions & 1 deletion ZkLobbyServer/SpringieInterface/PlanetWarsMatchMaker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ private async void TimerOnElapsed(object sender, ElapsedEventArgs elapsedEventAr
RunDefenderAssignment();
await LaunchAllBattles();
RunGalaxyTick();
await ApplyTurnEndChargeBump();
AttackerSideCounter++;
ResetAttackOptions();
}
Expand Down Expand Up @@ -420,6 +421,8 @@ private double GetPlayerWhr(string name)

private async Task LaunchAllBattles()
{
var attackerNamesToChargeSpend = new List<string>();

// merge squads on the same planet into one battle per planet
foreach (var planetId in FormedSquads.Select(s => s.PlanetID).Distinct().ToList())
{
Expand All @@ -434,6 +437,8 @@ private async Task LaunchAllBattles()
merged.Defenders.AddRange(squad.Defenders.Where(x => server.ConnectedUsers.ContainsKey(x)));
}

if (merged.Attackers.Count > 0) attackerNamesToChargeSpend.AddRange(merged.Attackers);

if (merged.Defenders.Count > 0 && merged.Attackers.Count > 0)
{
// battle (may be uneven)
Expand Down Expand Up @@ -473,6 +478,30 @@ private async Task LaunchAllBattles()

FormedSquads.Clear();
DefenderVotes.Clear();

if (attackerNamesToChargeSpend.Count > 0) await SpendAttackCharges(attackerNamesToChargeSpend);
}

private async Task SpendAttackCharges(List<string> playerNames)
{
var max = DynamicConfig.Instance.PwAttackChargesMax;
if (max <= 0) return;
try
{
List<Account> accounts;
using (var db = new ZkDataContext())
{
var turn = db.Galaxies.First(g => g.IsDefault).Turn;
accounts = db.Accounts.Where(a => playerNames.Contains(a.Name)).ToList();
foreach (var acc in accounts) acc.SpendPwAttackCharge(turn);
db.SaveChanges();
}
await Task.WhenAll(accounts.Select(acc => SendPwAttackCharges(server, acc.Name, acc)));
}
catch (Exception ex)
{
Trace.TraceError("PlanetWars SpendAttackCharges error: {0}", ex);
}
}


Expand Down Expand Up @@ -540,6 +569,13 @@ private async Task JoinPlanetAttack(int targetPlanetId, string userName)
var account = db.Accounts.Find(user.AccountID);
if (account == null || account.FactionID != AttackingFaction.FactionID || !account.CanPlayerPlanetWars()) return;

var maxCharges = DynamicConfig.Instance.PwAttackChargesMax;
if (maxCharges > 0 && account.PwAttackCharges <= 0)
{
await server.GhostChanSay(user.Faction, $"{userName} cannot attack: out of attack charges");
return;
}

// remove from other options
foreach (var aop in AttackOptions.Where(x => x.PlanetID != targetPlanetId))
aop.Attackers.RemoveAll(x => x == userName);
Expand Down Expand Up @@ -604,7 +640,11 @@ public async Task OnLoginAccepted(ConnectedUser connectedUser)
if (MiscVar.PlanetWarsMode == PlanetWarsModes.Running)
{
var u = connectedUser.User;
if (u.CanUserPlanetWars()) await UpdateLobby(u.Name);
if (u.CanUserPlanetWars())
{
await UpdateLobby(u.Name);
await SendPwAttackChargesForUser(u.Name);
}
}
}

Expand Down Expand Up @@ -939,6 +979,75 @@ public void RemoveFromRunningBattles(int battleID)
RunningBattles.Remove(battleID);
}


// ===================== ATTACK CHARGES =====================

public static PwAttackCharges BuildPwAttackCharges(Account account)
{
var max = DynamicConfig.Instance.PwAttackChargesMax;
int? nextRechargeTurn = null;
if (max > 0 && account.PwAttackCharges < max && account.PwLastChargeChangeTurn != null)
nextRechargeTurn = account.PwLastChargeChangeTurn.Value + DynamicConfig.Instance.PwAttackChargesCooldownTurns;
return new PwAttackCharges
{
Current = account.PwAttackCharges,
NextRechargeTurn = nextRechargeTurn,
};
}

public static async Task SendPwAttackCharges(ZkLobbyServer.ZkLobbyServer server, string userName, Account account)
{
var conus = server.ConnectedUsers.Get(userName);
if (conus == null) return;
await conus.SendCommand(BuildPwAttackCharges(account));
}

private async Task SendPwAttackChargesForUser(string userName)
{
var conus = server.ConnectedUsers.Get(userName);
if (conus?.User == null) return;
using (var db = new ZkDataContext())
{
var account = db.Accounts.Find(conus.User.AccountID);
if (account == null) return;
await conus.SendCommand(BuildPwAttackCharges(account));
}
}

private async Task ApplyTurnEndChargeBump()
{
try
{
var max = DynamicConfig.Instance.PwAttackChargesMax;
if (max <= 0) return;
var cooldown = DynamicConfig.Instance.PwAttackChargesCooldownTurns;

List<Account> bumped;
using (var db = new ZkDataContext())
{
var turn = db.Galaxies.First(g => g.IsDefault).Turn;
bumped = db.Accounts.Where(a =>
a.FactionID != null &&
a.PwAttackCharges < max &&
(a.PwLastChargeChangeTurn == null || turn - a.PwLastChargeChangeTurn >= cooldown)).ToList();

foreach (var acc in bumped)
{
acc.PwAttackCharges++;
acc.PwLastChargeChangeTurn = turn;
}

if (bumped.Count > 0) db.SaveChanges();
}

await Task.WhenAll(bumped.Select(a => SendPwAttackCharges(server, a.Name, a)));
}
catch (Exception ex)
{
Trace.TraceError("PlanetWars turn-end charge bump error: {0}", ex);
}
}

private async Task UpdateLobby()
{
// per-player: flags (CanSelectForBattle / PlayerIsAttacker) depend on the viewer
Expand Down
10 changes: 10 additions & 0 deletions ZkLobbyServer/SpringieInterface/PlanetWarsTurnHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,16 @@ public static void ProcessBattleResult(string mapName, List<string> extraData, Z
db.Events.InsertOnSubmit(ev);
text.AppendLine(ev.PlainText);
}

var maxCharges = DynamicConfig.Instance.PwAttackChargesMax;
if (maxCharges > 0)
{
foreach (Account w in defenders)
{
w.GrantPwAttackCharge(maxCharges, gal.Turn);
_ = ZeroKWeb.PlanetWarsMatchMaker.SendPwAttackCharges(server, w.Name, w);
}
}
}
else
{
Expand Down
Loading