public static void FillApplicableRatings(SpringBattle battle, SpringBattleContext result)
        {
            battle.ApplicableRatings = 0;
            if (battle.HasBots)
            {
                return;
            }
            if (battle.IsMission)
            {
                return;
            }
            if (battle.SpringBattlePlayers?.Where(x => !x.IsSpectator).Select(x => x.AllyNumber).Distinct().Count() < 2)
            {
                return;
            }
            if (battle.ResourceByMapResourceID?.MapIsSpecial == true)
            {
                return;
            }

            //only count balanced custom matches for elo
            if (battle.Mode == AutohostMode.None && battle.SpringBattlePlayers?.Where(x => !x.IsSpectator).GroupBy(x => x.AllyNumber).Select(x => x.Count()).Distinct().Count() > 1)
            {
                return;
            }
            if (battle.Duration < GlobalConst.MinDurationForElo)
            {
                return;
            }

            //don't mark battles for ratings if they can't be rated
            ICollection <int> winners = battle.SpringBattlePlayers.Where(p => p.IsInVictoryTeam && !p.IsSpectator).Select(p => (p.AccountID)).Distinct().ToList();
            ICollection <int> losers  = battle.SpringBattlePlayers.Where(p => !p.IsInVictoryTeam && !p.IsSpectator).Select(p => (p.AccountID)).Distinct().ToList();

            if (winners.Count == 0 || losers.Count == 0 || winners.Intersect(losers).Count() != 0)
            {
                return;
            }

            battle.ApplicableRatings |= (RatingCategoryFlags)result.LobbyStartContext.ApplicableRating;
            //Optionally add other flags here, like a casual or overall rating
        }
Example #2
0
        public void ResetAll()
        {
            playerOldRatings = new ConcurrentDictionary <int, PlayerRating>();
            playerRatings    = new ConcurrentDictionary <int, PlayerRating>();
            players          = new Dictionary <int, Player>();
            sortedPlayers    = new SortedDictionary <float, int>();
            topPlayers       = new List <int>();
            playerKeys       = new Dictionary <int, float>();
            activePlayers    = 0;
            lastBattleRanked = false;


            battlesRegistered = 0;
            firstBattle       = null;
            laddersCache      = new List <Account>();

            latestBattle     = null;
            lastUpdate       = null;
            ProcessedBattles = new HashSet <int>();
        }
        public BattleBalanceData(int battleID)
        {
            ZkDataContext db   = new ZkDataContext();
            SpringBattle  game = db.SpringBattles.FirstOrDefault(x => x.SpringBattleID == battleID);

            this.battleID = battleID;
            this.t1Elo    = new List <double>();
            this.t2Elo    = new List <double>();
            this.t1Names  = new List <string>();
            this.t2Names  = new List <string>();
            double t1Sum   = 0;
            double t2Sum   = 0;
            int    t1Count = 0;
            int    t2Count = 0;

            foreach (SpringBattlePlayer player in game.SpringBattlePlayers.Where(x => !x.IsSpectator))
            {
                if (player.IsInVictoryTeam)
                {
                    t1Sum += player.Account.EffectiveElo;
                    this.t1Elo.Add(Math.Floor(player.Account.EffectiveElo + 0.5));
                    this.t1Names.Add(player.Account.Name);
                    t1Count++;
                }
                else
                {
                    t2Sum += player.Account.EffectiveElo;
                    this.t2Elo.Add(Math.Floor(player.Account.EffectiveElo + 0.5));
                    this.t2Names.Add(player.Account.Name);
                    t2Count++;
                }
            }
            this.t1Avg      = Math.Floor(t1Sum / t1Count + 0.5);
            this.t2Avg      = Math.Floor(t2Sum / t2Count + 0.5);
            this.t1Variance = Math.Floor(Variance(this.t1Elo, this.t1Avg) + 0.5);
            this.t2Variance = Math.Floor(Variance(this.t2Elo, this.t2Avg) + 0.5);
        }
        private static SpringBattle SaveSpringBattle(SpringBattleContext result, ZkDataContext db)
        {
            var sb = new SpringBattle
            {
                HostAccountID     = Account.AccountByName(db, result.LobbyStartContext.FounderName)?.AccountID,
                Mode              = result.LobbyStartContext.Mode,
                Duration          = result.Duration,
                EngineGameID      = result.EngineBattleID,
                MapResourceID     = db.Resources.Single(x => x.InternalName == result.LobbyStartContext.Map).ResourceID,
                ModResourceID     = db.Resources.Single(x => x.InternalName == result.LobbyStartContext.Mod).ResourceID,
                HasBots           = result.LobbyStartContext.Bots.Any(),
                IsMission         = result.LobbyStartContext.IsMission,
                PlayerCount       = result.ActualPlayers.Count(x => !x.IsSpectator),
                StartTime         = result.StartTime,
                Title             = result.LobbyStartContext.Title,
                ReplayFileName    = Path.GetFileName(result.ReplayName),
                EngineVersion     = result.LobbyStartContext.EngineVersion,
                IsMatchMaker      = result.LobbyStartContext.IsMatchMakerGame,
                ApplicableRatings = 0,
            };

            db.SpringBattles.InsertOnSubmit(sb);

            // store players
            foreach (BattlePlayerResult p in result.ActualPlayers)
            {
                var account = Account.AccountByName(db, p.Name);
                if (account != null)
                {
                    sb.SpringBattlePlayers.Add(new SpringBattlePlayer
                    {
                        Account         = account,
                        AccountID       = account.AccountID,
                        AllyNumber      = p.AllyNumber,
                        IsInVictoryTeam = p.IsVictoryTeam,
                        IsSpectator     = p.IsSpectator,
                        LoseTime        = p.LoseTime
                    });
                }
            }

            // store bots
            var victoryAllyID = result.ActualPlayers.Where(x => x.IsVictoryTeam).Select(x => (int?)x.AllyNumber).FirstOrDefault() ?? -1;

            if (victoryAllyID == -1)
            {
                victoryAllyID = (result.ActualPlayers.Min(x => (int?)x.AllyNumber) ?? -1) + 1;                    // no player won, its likely to be next lowes team (stupid hack needed)
            }
            foreach (var bot in result.LobbyStartContext.Bots)
            {
                sb.SpringBattleBots.Add(new SpringBattleBot()
                {
                    AllyNumber      = bot.AllyID,
                    BotAI           = bot.BotAI,
                    BotName         = bot.BotName,
                    IsInVictoryTeam = bot.AllyID == victoryAllyID
                });
            }

            db.SaveChanges();

            return(db.SpringBattles.FirstOrDefault(x => x.SpringBattleID == sb.SpringBattleID)); // reselect from db to get proper lazy proxies
        }
        private static void ProcessPlanetWars(SpringBattleContext result, ZkLobbyServer.ZkLobbyServer server, SpringBattle sb, ZkDataContext db, StringBuilder text)
        {
            if (result.LobbyStartContext.Mode != AutohostMode.Planetwars || sb.PlayerCount < 2 || sb.Duration < GlobalConst.MinDurationForPlanetwars || sb.Duration > GlobalConst.MaxDurationForPlanetwars)
            {
                return;
            }

            List <int> winnerTeams = sb.SpringBattlePlayers.Where(x => x.IsInVictoryTeam && !x.IsSpectator).Select(x => x.AllyNumber).Distinct().ToList();
            int?       winNum      = null;

            if (winnerTeams.Count == 1)
            {
                winNum = winnerTeams[0];
                if (winNum > 1)
                {
                    winNum = null;
                }
            }

            /*PlanetWarsTurnHandler.EndTurn(result.LobbyStartContext.Map,
             *  result.OutputExtras,
             *  db,
             *  winNum,
             *  sb.SpringBattlePlayers.Where(x => !x.IsSpectator).Select(x => x.Account).ToList(),
             *  text,
             *  sb,
             *  sb.SpringBattlePlayers.Where(x => !x.IsSpectator && x.AllyNumber == 0).Select(x => x.Account).ToList(),
             *  server.PlanetWarsEventCreator, server);*/

            PlanetWarsTurnHandler.ResolveBattleOutcome(result.LobbyStartContext.Map,
                                                       result.OutputExtras,
                                                       db,
                                                       winNum,
                                                       sb.SpringBattlePlayers.Where(x => !x.IsSpectator).Select(x => x.Account).ToList(),
                                                       sb.SpringBattlePlayers.Where(x => !x.IsSpectator && x.AllyNumber == 0).Select(x => x.Account).ToList(),
                                                       text,
                                                       sb,
                                                       server.PlanetWarsEventCreator, server);

            server.PlanetWarsMatchMaker.RemoveFromRunningBattles(result.LobbyStartContext.BattleID);
        }
        private static void ProcessXP(SpringBattleContext result, ZkLobbyServer.ZkLobbyServer server, ZkDataContext db, SpringBattle sb)
        {
            sb.DispenseXP();

            foreach (var u in sb.SpringBattlePlayers.Where(x => !x.IsSpectator))
            {
                u.Account.CheckLevelUp();
            }


            db.SaveChanges();

            if (sb.ApplicableRatings == 0)
            {
                try
                {
                    foreach (Account a in sb.SpringBattlePlayers.Where(x => !x.IsSpectator).Select(x => x.Account))
                    {
                        server.PublishAccountUpdate(a);
                        server.PublishUserProfileUpdate(a);
                    }
                }
                catch (Exception ex)
                {
                    Trace.TraceError("error updating extension data: {0}", ex);
                }
            }
        }
    /// <summary>
    /// Updates shadow influence and new owners
    /// </summary>
    /// <param name="db"></param>
    /// <param name="sb">optional spring batle that caused this change (for event logging)</param>
    public static void SetPlanetOwners(IPlanetwarsEventCreator eventCreator, ZkDataContext db = null, SpringBattle sb = null)
    {
        if (db == null)
        {
            db = new ZkDataContext();
        }

        Galaxy gal = db.Galaxies.Single(x => x.IsDefault);

        foreach (Planet planet in gal.Planets)
        {
            if (planet.OwnerAccountID != null)
            {
                foreach (var ps in planet.PlanetStructures.Where(x => x.OwnerAccountID == null))
                {
                    ps.OwnerAccountID = planet.OwnerAccountID;
                    ps.ReactivateAfterBuild();
                }
            }


            PlanetFaction best       = planet.PlanetFactions.OrderByDescending(x => x.Influence).FirstOrDefault();
            Faction       newFaction = planet.Faction;
            Account       newAccount = planet.Account;

            if (best == null || best.Influence < GlobalConst.InfluenceToCapturePlanet)
            {
                // planet not capture

                if (planet.Faction != null)
                {
                    var curFacInfluence =
                        planet.PlanetFactions.Where(x => x.FactionID == planet.OwnerFactionID).Select(x => x.Influence).FirstOrDefault();

                    if (curFacInfluence <= GlobalConst.InfluenceToLosePlanet)
                    {
                        // owners have too small influence, planet belong to nobody
                        newFaction = null;
                        newAccount = null;
                    }
                }
            }
            else
            {
                if (best.Faction != planet.Faction)
                {
                    newFaction = best.Faction;

                    // best attacker without planets
                    Account candidate =
                        planet.AccountPlanets.Where(
                            x => x.Account.FactionID == newFaction.FactionID && x.AttackPoints > 0 && !x.Account.Planets.Any()).OrderByDescending(
                            x => x.AttackPoints).Select(x => x.Account).FirstOrDefault();

                    if (candidate == null)
                    {
                        // best attacker
                        candidate =
                            planet.AccountPlanets.Where(x => x.Account.FactionID == newFaction.FactionID && x.AttackPoints > 0).OrderByDescending(
                                x => x.AttackPoints).ThenBy(x => x.Account.Planets.Count()).Select(x => x.Account).FirstOrDefault();
                    }

                    // best player without planets
                    if (candidate == null)
                    {
                        candidate =
                            newFaction.Accounts.Where(x => !x.Planets.Any()).OrderByDescending(x => x.AccountPlanets.Sum(y => y.AttackPoints)).
                            FirstOrDefault();
                    }

                    // best with planets
                    if (candidate == null)
                    {
                        candidate =
                            newFaction.Accounts.OrderByDescending(x => x.AccountPlanets.Sum(y => y.AttackPoints)).
                            FirstOrDefault();
                    }

                    newAccount = candidate;
                }
            }

            // change has occured
            if (newFaction != planet.Faction)
            {
                // disable structures
                foreach (PlanetStructure structure in planet.PlanetStructures.Where(x => x.StructureType.OwnerChangeDisablesThis))
                {
                    structure.ReactivateAfterBuild();
                    structure.Account = newAccount;
                }

                // delete structures being lost on planet change
                foreach (PlanetStructure structure in
                         planet.PlanetStructures.Where(structure => structure.StructureType.OwnerChangeDeletesThis).ToList())
                {
                    db.PlanetStructures.DeleteOnSubmit(structure);
                }

                // reset attack points memory
                foreach (AccountPlanet acp in planet.AccountPlanets)
                {
                    acp.AttackPoints = 0;
                }

                if (newFaction == null)
                {
                    Account account = planet.Account;
                    Clan    clan    = null;
                    if (account != null)
                    {
                        clan = planet.Account != null ? planet.Account.Clan : null;
                    }

                    db.Events.InsertOnSubmit(eventCreator.CreateEvent("{0} planet {1} owned by {2} {3} was abandoned. {4}",
                                                                      planet.Faction,
                                                                      planet,
                                                                      account,
                                                                      clan,
                                                                      sb));
                    if (account != null)
                    {
                        eventCreator.GhostPm(planet.Account.Name, string.Format(
                                                 "Warning, you just lost planet {0}!! {2}/PlanetWars/Planet/{1}",
                                                 planet.Name,
                                                 planet.PlanetID,
                                                 GlobalConst.BaseSiteUrl));
                    }
                }
                else
                {
                    // new real owner

                    // log messages
                    if (planet.OwnerAccountID == null) // no previous owner
                    {
                        db.Events.InsertOnSubmit(eventCreator.CreateEvent("{0} has claimed planet {1} for {2} {3}. {4}",
                                                                          newAccount,
                                                                          planet,
                                                                          newFaction,
                                                                          newAccount.Clan,
                                                                          sb));
                        eventCreator.GhostPm(newAccount.Name, string.Format(
                                                 "Congratulations, you now own planet {0}!! {2}/PlanetWars/Planet/{1}",
                                                 planet.Name,
                                                 planet.PlanetID,
                                                 GlobalConst.BaseSiteUrl));
                    }
                    else
                    {
                        db.Events.InsertOnSubmit(eventCreator.CreateEvent("{0} of {1} {2} has captured planet {3} from {4} of {5} {6}. {7}",
                                                                          newAccount,
                                                                          newFaction,
                                                                          newAccount.Clan,
                                                                          planet,
                                                                          planet.Account,
                                                                          planet.Faction,
                                                                          planet.Account.Clan,
                                                                          sb));

                        eventCreator.GhostPm(newAccount.Name, string.Format(
                                                 "Congratulations, you now own planet {0}!! {2}/PlanetWars/Planet/{1}",
                                                 planet.Name,
                                                 planet.PlanetID,
                                                 GlobalConst.BaseSiteUrl));

                        eventCreator.GhostPm(planet.Account.Name, string.Format(
                                                 "Warning, you just lost planet {0}!! {2}/PlanetWars/Planet/{1}",
                                                 planet.Name,
                                                 planet.PlanetID,
                                                 GlobalConst.BaseSiteUrl));
                    }


                    if (planet.PlanetStructures.Any(x => x.StructureType.OwnerChangeWinsGame))
                    {
                        WinGame(db, gal, newFaction, eventCreator.CreateEvent("CONGRATULATIONS!! {0} has won the PlanetWars by capturing {1} planet {2}!", newFaction, planet.Faction, planet));
                    }
                }

                planet.Faction = newFaction;
                planet.Account = newAccount;
            }
            ReturnPeacefulDropshipsHome(db, planet);
        }
        db.SaveChanges();
    }
    private static double CalculateFactionMetalGain(Planet planet, StructureType hq, Faction forFaction, double baseMetal, IPlanetwarsEventCreator eventCreator, ZkDataContext db, StringBuilder texts, SpringBattle sb)
    {
        if (hq == null)
        {
            return(baseMetal);
        }
        Planet matchPlanet;

        var hqDistance = planet.GetLinkDistanceTo(p => p.PlanetStructures.Any(y => y.StructureTypeID == hq.StructureTypeID), forFaction, out matchPlanet);

        if (hqDistance == null)
        {
            if (hq.EffectDisconnectedMetalMalus != null)
            {
                baseMetal = baseMetal - hq.EffectDisconnectedMetalMalus.Value;

                var ev = eventCreator.CreateEvent("{0} metal gain reduced by {1} because it is disconnected from {2}. {3}",
                                                  forFaction,
                                                  hq.EffectDisconnectedMetalMalus,
                                                  hq.Name,
                                                  sb);
                db.Events.Add(ev);
                texts.AppendLine(ev.PlainText);
            }
        }
        else
        {
            if (hq.EffectDistanceMetalBonusMultiplier != null)
            {
                var bonus = Math.Max(hq.EffectDistanceMetalBonusMin ?? 0, (hq.EffectDistanceMetalBonusMax ?? 0) - hq.EffectDistanceMetalBonusMultiplier.Value * hqDistance.Value);

                if (bonus > 0)
                {
                    baseMetal = baseMetal + bonus;
                    var ev = eventCreator.CreateEvent("{0} metal gain improved by {1} because it is close to {2}. {3}",
                                                      forFaction,
                                                      bonus,
                                                      hq.Name,
                                                      sb);
                    db.Events.Add(ev);
                    texts.AppendLine(ev.PlainText);
                }
            }
        }
        return(baseMetal);
    }
    /// <summary>
    /// Process planet wars turn
    /// </summary>
    /// <param name="mapName"></param>
    /// <param name="extraData"></param>
    /// <param name="db"></param>
    /// <param name="winnerSide">0 = first team wins, 1 = second team wins</param>
    /// <param name="players"></param>
    /// <param name="text"></param>
    /// <param name="sb"></param>
    ///
    public static void ResolveBattleOutcome(string mapName, List <string> extraData, ZkDataContext db, int?winnerSide, List <Account> players, List <Account> firstSidePlayers, StringBuilder text, SpringBattle sb, IPlanetwarsEventCreator eventCreator, ZkLobbyServer.ZkLobbyServer server)
    {
        if (extraData == null)
        {
            extraData = new List <string>();
        }
        Galaxy gal         = db.Galaxies.Single(x => x.IsDefault);
        Planet planet      = gal.Planets.Single(x => x.Resource.InternalName == mapName);
        var    hqStructure = db.StructureTypes.FirstOrDefault(x => x.EffectDisconnectedMetalMalus != null || x.EffectDistanceMetalBonusMax != null);

        text.AppendFormat("Battle on {1}/PlanetWars/Planet/{0} has ended\n", planet.PlanetID, GlobalConst.BaseSiteUrl);

        bool firstFactionWon;
        bool wasFirstCcDestroyed  = false;
        bool wasSecondCcDestroyed = false;

        if (winnerSide != null)
        {
            if (winnerSide == 0)
            {
                firstFactionWon = true;
            }
            else
            {
                firstFactionWon = false;
            }

            wasFirstCcDestroyed  = extraData.Any(x => x.StartsWith("hqkilled,0"));
            wasSecondCcDestroyed = extraData.Any(x => x.StartsWith("hqkilled,1"));
        }
        else
        {
            text.AppendFormat("No winner on this battle!");
            Trace.TraceError("PW battle without a winner {0}", sb != null ? sb.SpringBattleID : (int?)null);
            return;
        }

        Faction firstFaction      = firstSidePlayers.Where(x => x.Faction != null).Select(x => x.Faction).First();
        var     secondSidePlayers = players.Where(x => x.FactionID != firstFaction.FactionID && x.FactionID != null).ToList();
        Faction secondFaction     = null;

        if (secondSidePlayers.Any())
        {
            secondFaction = secondSidePlayers.Where(x => x.Faction != null).Select(x => x.Faction).First();
        }

        Faction winningFaction;
        Faction losingFaction;
        bool    ccDestroyed = false;

        if (firstFactionWon)
        {
            winningFaction = firstFaction;
            losingFaction  = secondFaction;
            ccDestroyed    = wasFirstCcDestroyed;
        }
        else
        {
            winningFaction = secondFaction;
            losingFaction  = firstFaction;
            ccDestroyed    = wasSecondCcDestroyed;
        }

        if (winningFaction == null)
        {
            text.AppendFormat("Winning team had no players!");
            Trace.TraceError("PW battle where the winner had no players!", sb != null ? sb.SpringBattleID : (int?)null);
            return;
        }

        double baseInfluence   = GlobalConst.BaseInfluencePerBattle;
        double influenceChange = baseInfluence;
        double loserInfluence  = 0;
        double ipMultiplier    = 1;

        string influenceReport = "";
        string ipReason;

        bool reducedEnemyInfluence = false;
        bool flippedDominance      = false;
        bool planetConquered       = false;

        if (ccDestroyed)
        {
            ipMultiplier = GlobalConst.PlanetWarsLostCcMultiplier;
            ipReason     = "due to winning but losing Command Centre";
        }
        else
        {
            ipReason = "due to winning flawlessly";
        }


        influenceChange = (influenceChange) * ipMultiplier;
        if (influenceChange < 0)
        {
            influenceChange = 0;
        }
        influenceChange = Math.Floor(influenceChange * 100) / 100;

        PlanetFaction entry = planet.PlanetFactions.FirstOrDefault();

        if (entry == null)
        {
            entry = new PlanetFaction {
                Faction = winningFaction, Planet = planet,
            };
            planet.PlanetFactions.Add(entry);
        }
        // if non-winner currently dominates planet
        if (entry.Faction != winningFaction)
        {
            loserInfluence = entry.Influence;

            // if win is insufficient to change this
            if (loserInfluence >= influenceChange)
            {
                reducedEnemyInfluence = true;
                entry.Influence      -= influenceChange;
            }
            else // flip dominance
            {
                flippedDominance = true;
                planet.PlanetFactions.Remove(entry);
                entry = new PlanetFaction {
                    Faction = winningFaction, Planet = planet,
                };
                planet.PlanetFactions.Add(entry);
                entry.Influence += (influenceChange - loserInfluence);

                if (entry.Influence >= GlobalConst.PlanetWarsMaximumIP)
                {
                    entry.Influence = GlobalConst.PlanetWarsMaximumIP;
                    planetConquered = true;
                }
            }
        }
        else // increase winner's existing dominance
        {
            if (entry.Influence < GlobalConst.PlanetWarsMaximumIP)
            {
                entry.Influence += influenceChange;
                if (entry.Influence >= GlobalConst.PlanetWarsMaximumIP)
                {
                    entry.Influence = GlobalConst.PlanetWarsMaximumIP;
                    planetConquered = true;
                }
            }
        }

        string contestReport = "";
        int    newContested  = 0;

        if (planetConquered)
        {
            foreach (Link link in planet.LinksByPlanetID1.Union(planet.LinksByPlanetID2).ToList())
            {
                Planet        otherPlanet        = link.PlanetID1 == planet.PlanetID ? link.PlanetByPlanetID2 : link.PlanetByPlanetID1;
                PlanetFaction otherPlanetFaction = otherPlanet.PlanetFactions.FirstOrDefault();

                if (otherPlanetFaction.Faction != winningFaction && otherPlanetFaction.Influence > GlobalConst.BreakthroughInfluence)
                {
                    otherPlanetFaction.Influence = GlobalConst.BreakthroughInfluence;
                    if (newContested > 0)
                    {
                        contestReport += ", ";
                    }
                    contestReport += otherPlanet.Name;
                    newContested++;
                }
            }
        }
        if (newContested > 0)
        {
            contestReport = "Adjacent planets now contested: " + contestReport + ".";
        }

        // Check all planets to see if they are contested by a single faction
        string controlReport = "";
        int    newControlled = 0;

        foreach (Planet p1 in gal.Planets)
        {
            List <Faction> l = GetContestingFactions(p1);

            if (l.Count() == 1)
            {
                Faction       f      = l.FirstOrDefault(); // this faction should be made dominant if it is not already
                PlanetFaction cEntry = p1.PlanetFactions.FirstOrDefault();
                if (cEntry.Faction != f)
                {
                    p1.PlanetFactions.Remove(cEntry);
                    cEntry = new PlanetFaction {
                        Faction = f, Planet = p1, Influence = 0
                    };
                    p1.PlanetFactions.Add(cEntry);
                }
                if (cEntry.Influence != GlobalConst.PlanetWarsMaximumIP)
                {
                    cEntry.Influence = GlobalConst.PlanetWarsMaximumIP;
                    if (newControlled > 0)
                    {
                        controlReport += ", ";
                    }
                    controlReport += p1.Name;
                    newControlled++;
                }
            }
        }
        if (newControlled > 0)
        {
            controlReport = "Planets automatically controlled: " + controlReport + ".";
        }

        // Update actual *control* of all planets
        PlanetWarsTurnHandler.SetPlanetOwners(eventCreator, db, sb);

        try
        {
            if (planetConquered)
            {
                influenceReport = String.Format("{0} conquered the planet, gained {1} influence ({2}% {3} of {4})",
                                                winningFaction.Shortcut,
                                                influenceChange,
                                                (int)(ipMultiplier * 100.0),
                                                ipReason,
                                                baseInfluence + " base");
            }
            else
            {
                if (reducedEnemyInfluence)
                {
                    influenceReport = String.Format("{0} reduced influence of {1} by {2} ({3}% {4} of {5}); {6} has {7} influence remaining on {8}",
                                                    winningFaction.Shortcut,
                                                    (losingFaction == null) ? "opposing faction" : losingFaction.Shortcut,
                                                    influenceChange,
                                                    (int)(ipMultiplier * 100.0),
                                                    ipReason,
                                                    baseInfluence + " base",
                                                    entry.Faction,
                                                    entry.Influence,
                                                    entry.Planet);
                }
                else
                {
                    if (flippedDominance)
                    {
                        influenceReport = String.Format("{0} took dominance from {1} and gained {2} influence ({3}% {4} of {5}); {6} now has {7} influence on {8}",
                                                        winningFaction.Shortcut,
                                                        (losingFaction == null) ? "opposing faction" : losingFaction.Shortcut,
                                                        influenceChange,
                                                        (int)(ipMultiplier * 100.0),
                                                        ipReason,
                                                        baseInfluence + " base",
                                                        entry.Faction,
                                                        entry.Influence,
                                                        entry.Planet);
                    }
                    else
                    {
                        influenceReport = String.Format("{0} gained {1} influence ({2}% {3} of {4}); {5} now has {6} influence on {7} ",
                                                        winningFaction.Shortcut,
                                                        influenceChange,
                                                        (int)(ipMultiplier * 100.0),
                                                        ipReason,
                                                        baseInfluence + " base",
                                                        entry.Faction,
                                                        entry.Influence,
                                                        entry.Planet);
                    }
                }
            }
        }
        catch (Exception ex)
        {
            Trace.TraceError(ex.ToString());
        }

        // paranoia!
        try
        {
            var mainEvent = eventCreator.CreateEvent("{0} defeated {1} on {2} in {3}. {4}. {5} {6}",
                                                     winningFaction,
                                                     (losingFaction == null) ? "opposing faction" : losingFaction.Shortcut,
                                                     planet,
                                                     (sb == null) ? "no battle" : string.Format("B{0}", sb.SpringBattleID),
                                                     influenceReport,
                                                     contestReport,
                                                     controlReport
                                                     );
            db.Events.InsertOnSubmit(mainEvent);
            text.AppendLine(mainEvent.PlainText);
        }
        catch (Exception ex)
        {
            Trace.TraceError(ex.ToString());
        }

        db.SaveChanges();

        /*gal.Turn++;
         * db.SaveChanges();
         *
         * db = new ZkDataContext(); // is this needed - attempt to fix setplanetownersbeing buggy
         * SetPlanetOwners(eventCreator, db, sb != null ? db.SpringBattles.Find(sb.SpringBattleID) : null);
         * gal = db.Galaxies.Single(x => x.IsDefault);*/
    }
    public static void EndTurn(string mapName, List <string> extraData, ZkDataContext db, int?winNum, List <Account> players, StringBuilder text, SpringBattle sb, List <Account> attackers, IPlanetwarsEventCreator eventCreator, ZkLobbyServer.ZkLobbyServer server)
    {
        if (extraData == null)
        {
            extraData = new List <string>();
        }
        Galaxy gal         = db.Galaxies.Single(x => x.IsDefault);
        Planet planet      = gal.Planets.Single(x => x.Resource.InternalName == mapName);
        var    hqStructure = db.StructureTypes.FirstOrDefault(x => x.EffectDisconnectedMetalMalus != null || x.EffectDistanceMetalBonusMax != null);

        text.AppendFormat("Battle on {1}/PlanetWars/Planet/{0} has ended\n", planet.PlanetID, GlobalConst.BaseSiteUrl);


        Faction attacker  = attackers.Where(x => x.Faction != null).Select(x => x.Faction).First();
        var     defenders = players.Where(x => x.FactionID != attacker.FactionID && x.FactionID != null).ToList();
        bool    isAttackerWinner;
        bool    wasAttackerCcDestroyed = false;
        bool    wasDefenderCcDestroyed = false;

        if (winNum != null)
        {
            if (winNum == 0)
            {
                isAttackerWinner = true;
            }
            else
            {
                isAttackerWinner = false;
            }

            wasAttackerCcDestroyed = extraData.Any(x => x.StartsWith("hqkilled,0"));
            wasDefenderCcDestroyed = extraData.Any(x => x.StartsWith("hqkilled,1"));
        }
        else
        {
            text.AppendFormat("No winner on this battle!");
            Trace.TraceError("PW battle without a winner {0}", sb != null ? sb.SpringBattleID : (int?)null);
            return;
        }

        var evacuatedStructureTypeIDs = GetEvacuatedStructureTypes(extraData, db);

        string influenceReport = "";

        // distribute influence
        // save influence gains
        // give influence to main attackers
        double planetIpDefs = planet.GetEffectiveIpDefense();

        double  baseInfluence         = GlobalConst.BaseInfluencePerBattle;
        double  influenceChange       = baseInfluence;
        double  loserInfluence        = 0;
        Faction loserFaction          = attacker; // TODO make this less awful
        bool    reducedEnemyInfluence = false;
        bool    flippedDominance      = false;
        bool    planetConquered       = false;

        double shipBonus = planet.GetEffectiveShipIpBonus(attacker);

        double defenseBonus = -planetIpDefs;
        double techBonus    = attacker.GetFactionUnlocks().Count() * GlobalConst.InfluencePerTech;
        double ipMultiplier = 1;
        string ipReason;

        if (!isAttackerWinner)
        {
            if (wasDefenderCcDestroyed)
            {
                ipMultiplier = GlobalConst.PlanetWarsDefenderWinKillCcMultiplier;
                ipReason     = "from losing but killing defender's CC";
            }
            else
            {
                ipMultiplier = 0;
                ipReason     = "from losing horribly";
            }
        }
        else
        {
            if (wasAttackerCcDestroyed)
            {
                ipReason     = "from losing own CC";
                ipMultiplier = GlobalConst.PlanetWarsAttackerWinLoseCcMultiplier;
            }
            else
            {
                ipReason = "from winning flawlessly";
            }
        }

        influenceChange = (influenceChange + shipBonus + techBonus + defenseBonus) * ipMultiplier;
        if (influenceChange < 0)
        {
            influenceChange = 0;
        }
        influenceChange = Math.Floor(influenceChange * 100) / 100;

        // main winner influence
        // PlanetFaction entry = planet.PlanetFactions.FirstOrDefault(x => x.Faction == attacker);
        PlanetFaction entry = planet.PlanetFactions.FirstOrDefault();

        if (entry == null)
        {
            entry = new PlanetFaction {
                Faction = attacker, Planet = planet,
            };
            planet.PlanetFactions.Add(entry);
        }
        // if non-winner currently dominates planet
        if (entry.Faction != attacker)
        {
            loserFaction   = entry.Faction;
            loserInfluence = entry.Influence;

            // if win is insufficient to change this
            if (loserInfluence >= influenceChange)
            {
                reducedEnemyInfluence = true;
                entry.Influence      -= influenceChange;
            }
            else // flip dominance
            {
                flippedDominance = true;
                planet.PlanetFactions.Remove(entry);
                entry = new PlanetFaction {
                    Faction = attacker, Planet = planet,
                };
                planet.PlanetFactions.Add(entry);
                entry.Influence += (influenceChange - loserInfluence);

                if (entry.Influence >= GlobalConst.PlanetWarsMaximumIP)
                {
                    entry.Influence = GlobalConst.PlanetWarsMaximumIP;
                    planetConquered = true;
                }
            }
        }
        else // increase winner's existing dominance
        {
            if (entry.Influence < GlobalConst.PlanetWarsMaximumIP)
            {
                entry.Influence += influenceChange;
                if (entry.Influence >= GlobalConst.PlanetWarsMaximumIP)
                {
                    entry.Influence = GlobalConst.PlanetWarsMaximumIP;
                    planetConquered = true;
                }
            }
        }

        // TODO remove dependence on attacker

        if (planetConquered)
        {
            // Contest Adjacent Planets
            // GlobalConst.PlanetWarsBreakthroughIP
        }

        // Check all planets to see if they are contested by a single faction

        try
        {
            if (reducedEnemyInfluence)
            {
                influenceReport = String.Format("{0} reduced influence of {1} by {2} ({3}% {4} of {5}{6}{7}{8})",
                                                attacker.Shortcut,
                                                loserFaction.Shortcut,
                                                influenceChange,
                                                (int)(ipMultiplier * 100.0),
                                                ipReason,
                                                baseInfluence + " base",
                                                techBonus > 0 ? " +" + techBonus + " from techs" : "",
                                                shipBonus > 0 ? " +" + shipBonus + " from dropships" : "",
                                                defenseBonus != 0 ? " -" + -defenseBonus + " from defenses" : "");
            }
            else
            {
                if (flippedDominance)
                {
                    influenceReport = String.Format("{0} took dominance from {1} and gained {2} influence ({3}% {4} of {5}{6}{7}{8})",
                                                    attacker.Shortcut,
                                                    loserFaction.Shortcut,
                                                    influenceChange - loserInfluence,
                                                    (int)(ipMultiplier * 100.0),
                                                    ipReason,
                                                    baseInfluence + " base",
                                                    techBonus > 0 ? " +" + techBonus + " from techs" : "",
                                                    shipBonus > 0 ? " +" + shipBonus + " from dropships" : "",
                                                    defenseBonus != 0 ? " -" + -defenseBonus + " from defenses" : "");
                }
                else
                {
                    influenceReport = String.Format("{0} gained {1} influence ({2}% {3} of {4}{5}{6}{7})",
                                                    attacker.Shortcut,
                                                    influenceChange,
                                                    (int)(ipMultiplier * 100.0),
                                                    ipReason,
                                                    baseInfluence + " base",
                                                    techBonus > 0 ? " +" + techBonus + " from techs" : "",
                                                    shipBonus > 0 ? " +" + shipBonus + " from dropships" : "",
                                                    defenseBonus != 0 ? " -" + -defenseBonus + " from defenses" : "");
                }
            }
        }
        catch (Exception ex)
        {
            Trace.TraceError(ex.ToString());
        }

        // distribute metal
        var attackersTotalMetal = CalculateFactionMetalGain(planet, hqStructure, attacker, GlobalConst.PlanetWarsAttackerMetal, eventCreator, db, text, sb);
        var attackerMetal       = Math.Floor(attackersTotalMetal / attackers.Count);

        foreach (Account w in attackers)
        {
            w.ProduceMetal(attackerMetal);
            var ev = eventCreator.CreateEvent("{0} gained {1} metal from battle {2}",
                                              w,
                                              attackerMetal,
                                              sb);
            db.Events.InsertOnSubmit(ev);
            text.AppendLine(ev.PlainText);
        }


        var defendersTotalMetal = planet.OwnerFactionID == null?Math.Floor(GlobalConst.PlanetWarsDefenderMetal) : CalculateFactionMetalGain(planet, hqStructure, planet.Faction, GlobalConst.PlanetWarsDefenderMetal, eventCreator, db, text, sb);

        if (defenders.Count > 0)
        {
            var defenderMetal = Math.Floor(defendersTotalMetal / defenders.Count);
            foreach (Account w in defenders)
            {
                w.ProduceMetal(defenderMetal);
                var ev = eventCreator.CreateEvent("{0} gained {1} metal from battle {2}", w, defenderMetal, sb);
                db.Events.InsertOnSubmit(ev);
                text.AppendLine(ev.PlainText);
            }
        }
        else
        {
            // planet had no defenders, give metal to owner's faction
            if (planet.OwnerFactionID != null)
            {
                planet.Faction.ProduceMetal(defendersTotalMetal);
                var ev = eventCreator.CreateEvent("{0} gained {1} metal from battle {2}", planet.Faction, defendersTotalMetal, sb);
                db.Events.InsertOnSubmit(ev);
            }
        }


        // remove attacker's dropships
        foreach (var pf in planet.PlanetFactions.Where(x => x.Faction == attacker))
        {
            pf.Dropships = 0;
        }


        // remove dropships staying for too long (return to faction pool)
        foreach (var pf in planet.PlanetFactions.Where(x => x.Faction != attacker && x.Dropships > 0 && x.DropshipsLastAdded != null))
        {
            if (DateTime.UtcNow.Subtract(pf.DropshipsLastAdded.Value).TotalMinutes > GlobalConst.PlanetWarsDropshipsStayForMinutes)
            {
                pf.Faction.ProduceDropships(pf.Dropships);
                pf.Dropships = 0;
            }
        }


        // add attack points
        foreach (Account acc in players)
        {
            int ap = acc.Faction == attacker ? GlobalConst.AttackPointsForVictory : GlobalConst.AttackPointsForDefeat;
            if (acc.Faction != null)
            {
                AccountPlanet apentry = planet.AccountPlanets.SingleOrDefault(x => x.AccountID == acc.AccountID);
                if (apentry == null)
                {
                    apentry = new AccountPlanet {
                        AccountID = acc.AccountID, PlanetID = planet.PlanetID
                    };
                    db.AccountPlanets.InsertOnSubmit(apentry);
                }

                apentry.AttackPoints += ap;
            }
            acc.PwAttackPoints += ap;
        }

        // paranoia!
        try
        {
            var mainEvent = eventCreator.CreateEvent("{0} attacked {1} {2} in {3} and {4}. {5}",
                                                     attacker,
                                                     planet.Faction,
                                                     planet,
                                                     sb,
                                                     isAttackerWinner ? "won" : "lost",
                                                     influenceReport
                                                     );
            db.Events.InsertOnSubmit(mainEvent);
            text.AppendLine(mainEvent.PlainText);
        }
        catch (Exception ex)
        {
            Trace.TraceError(ex.ToString());
        }

        // destroy pw structures killed ingame
        if (!isAttackerWinner)
        {
            var handled = new List <string>();
            foreach (string line in extraData.Where(x => x.StartsWith("structurekilled", StringComparison.InvariantCulture)))
            {
                string[] data     = line.Substring(16).Split(',');
                string   unitName = data[0];
                if (handled.Contains(unitName))
                {
                    continue;
                }
                handled.Add(unitName);
                foreach (PlanetStructure s in planet.PlanetStructures.Where(x => x.StructureType.IngameUnitName == unitName))
                {
                    if (s.StructureType.IsIngameDestructible)
                    {
                        s.ReactivateAfterDestruction();

                        var ev = eventCreator.CreateEvent("{0} has been disabled on {1} planet {2}. {3}", s.StructureType.Name, planet.Faction, planet, sb);
                        db.Events.InsertOnSubmit(ev);
                        text.AppendLine(ev.PlainText);
                    }
                }
            }
        }
        else
        {
            // attacker won disable all but evacuated
            foreach (var s in planet.PlanetStructures.Where(x => x.StructureType.IsIngameDestructible))
            {
                if (evacuatedStructureTypeIDs.Contains(s.StructureTypeID))
                {
                    var evSaved = eventCreator.CreateEvent("{0} structure {1} on planet {2} has been saved by evacuation. {3}",
                                                           planet.Faction,
                                                           s.StructureType,
                                                           planet,
                                                           sb);
                    db.Events.InsertOnSubmit(evSaved);
                    text.AppendLine(evSaved.PlainText);
                }
                else
                {
                    s.ReactivateAfterDestruction();
                }
            }
            // destroy structures by battle (usually defenses)
            foreach (PlanetStructure s in planet.PlanetStructures.Where(x => x.StructureType.BattleDeletesThis).ToList())
            {
                if (evacuatedStructureTypeIDs.Contains(s.StructureTypeID))
                {
                    var evSaved = eventCreator.CreateEvent("{0} structure {1} on planet {2} has been saved by evacuation. {3}",
                                                           planet.Faction,
                                                           s.StructureType,
                                                           planet,
                                                           sb);
                    db.Events.InsertOnSubmit(evSaved);
                    text.AppendLine(evSaved.PlainText);
                }
                else
                {
                    planet.PlanetStructures.Remove(s);
                }
            }

            var ev = eventCreator.CreateEvent("All non-evacuated structures have been disabled on {0} planet {1}. {2}", planet.Faction, planet, sb);
            db.Events.InsertOnSubmit(ev);
            text.AppendLine(ev.PlainText);
        }

        db.SaveChanges();

        // no longer used?
        //gal.DecayInfluence();
        //gal.SpreadInfluence();

        // process faction energies
        foreach (var fac in db.Factions.Where(x => !x.IsDeleted))
        {
            fac.ProcessEnergy(gal.Turn);
        }

        // process production (incl. victory points)
        gal.ProcessProduction();
        db.SaveChanges();

        var vpFacs = db.Factions.Where(x => x.VictoryPoints > 0);

        if (vpFacs.Count() > 1)
        {
            foreach (var fac in vpFacs)
            {
                fac.VictoryPoints -= Math.Min(fac.VictoryPoints, GlobalConst.VictoryPointDecay);
            }
        }

        // delete one time activated structures
        gal.DeleteOneTimeActivated(eventCreator, db);
        db.SaveChanges();

        // process treaties
        foreach (var tr in db.FactionTreaties.Where(x => x.TreatyState == TreatyState.Accepted || x.TreatyState == TreatyState.Suspended))
        {
            var failedTradeFaction = tr.ProcessTrade(false);
            if (failedTradeFaction == null)
            {
                tr.TreatyState = TreatyState.Accepted;
                if (tr.TurnsTotal != null)
                {
                    tr.TurnsRemaining--;

                    if (tr.TurnsRemaining <= 0) // treaty expired
                    {
                        tr.TreatyState = TreatyState.Ended;
                        tr.FactionByAcceptingFactionID.ProduceMetal(tr.AcceptingFactionGuarantee ?? 0);
                        tr.FactionByProposingFactionID.ProduceMetal(tr.ProposingFactionGuarantee ?? 0);
                    }
                }
            }
            else
            {
                // failed to perform trade
                if (tr.TreatyUnableToTradeMode == TreatyUnableToTradeMode.Suspend)
                {
                    tr.TreatyState = TreatyState.Suspended;
                }
                else
                { // forced cancel
                    tr.CancelTreaty(failedTradeFaction);
                    db.Events.InsertOnSubmit(server.PlanetWarsEventCreator.CreateEvent("Treaty {0} between {1} and {2} cancelled by {3} because it failed to trade", tr, tr.FactionByProposingFactionID, tr.FactionByAcceptingFactionID, failedTradeFaction));
                }
            }
        }

        // burn extra energy
        foreach (var fac in db.Factions.Where(x => !x.IsDeleted))
        {
            fac.ConvertExcessEnergyToMetal();
        }


        int?oldOwner = planet.OwnerAccountID;

        gal.Turn++;
        db.SaveChanges();

        db = new ZkDataContext(); // is this needed - attempt to fix setplanetownersbeing buggy
        SetPlanetOwners(eventCreator, db, sb != null ? db.SpringBattles.Find(sb.SpringBattleID) : null);
        gal = db.Galaxies.Single(x => x.IsDefault);

        var winByVictoryPoints = db.Factions.FirstOrDefault(x => !x.IsDeleted && x.VictoryPoints >= GlobalConst.PlanetWarsVictoryPointsToWin);

        if (winByVictoryPoints != null)
        {
            WinGame(db,
                    gal,
                    winByVictoryPoints,
                    eventCreator.CreateEvent("CONGRATULATIONS!! {0} has won the PlanetWars by getting enough victory points!", winByVictoryPoints));
            db.SaveChanges();
        }


        planet = gal.Planets.Single(x => x.Resource.InternalName == mapName);
        if (planet.OwnerAccountID != oldOwner && planet.OwnerAccountID != null)
        {
            text.AppendFormat("Congratulations!! Planet {0} was conquered by {1} !!  {3}/PlanetWars/Planet/{2}\n",
                              planet.Name,
                              planet.Account.Name,
                              planet.PlanetID,
                              GlobalConst.BaseSiteUrl);
        }

        server.PublishUserProfilePlanetwarsPlayers();

        try
        {
            // store history
            foreach (Planet p in gal.Planets)
            {
                var hist = db.PlanetOwnerHistories.FirstOrDefault(x => x.PlanetID == p.PlanetID && x.Turn == gal.Turn);
                if (hist == null)
                {
                    hist = new PlanetOwnerHistory()
                    {
                        PlanetID = p.PlanetID, Turn = gal.Turn
                    };
                    db.PlanetOwnerHistories.Add(hist);
                }
                hist.OwnerAccountID = p.OwnerAccountID;
                hist.OwnerClanID    = p.OwnerAccountID != null ? p.Account.ClanID : null;
                hist.OwnerFactionID = p.OwnerFactionID;
            }

            db.SaveChanges();
        }
        catch (Exception ex)
        {
            Trace.TraceError(ex.ToString());
            text.AppendLine("error saving history: " + ex);
        }

        //rotate map
        if (GlobalConst.RotatePWMaps)
        {
            db     = new ZkDataContext();
            gal    = db.Galaxies.Single(x => x.IsDefault);
            planet = gal.Planets.Single(x => x.Resource.InternalName == mapName);
            var mapList = db.Resources.Where(x => x.MapPlanetWarsIcon != null && x.Planets.Where(p => p.GalaxyID == gal.GalaxyID).Count() == 0 && x.MapSupportLevel >= MapSupportLevel.Featured &&
                                             x.ResourceID != planet.MapResourceID && x.MapWaterLevel == planet.Resource.MapWaterLevel).ToList();
            if (mapList.Count > 0)
            {
                int      r          = new Random().Next(mapList.Count);
                int      resourceID = mapList[r].ResourceID;
                Resource newMap     = db.Resources.Single(x => x.ResourceID == resourceID);
                text.AppendLine(String.Format("Map cycler - {0} maps found, selected map {1} to replace map {2}", mapList.Count, newMap.InternalName, planet.Resource.InternalName));
                planet.Resource = newMap;
                gal.IsDirty     = true;
            }
            else
            {
                text.AppendLine("Map cycler - no maps found");
            }
            db.SaveChanges();
        }
    }
Example #11
0
        public static string SubmitSpringBattleResult(BattleContext context,
                                                      string password,
                                                      BattleResult result,
                                                      List <BattlePlayerResult> players,
                                                      List <string> extraData)
        {
            try
            {
                Account acc = AuthServiceClient.VerifyAccountPlain(context.AutohostName, password);
                if (acc == null)
                {
                    throw new Exception("Account name or password not valid");
                }
                AutohostMode mode = context.GetMode();
                var          db   = new ZkDataContext();

                if (extraData == null)
                {
                    extraData = new List <string>();
                }


                var sb = new SpringBattle
                {
                    HostAccountID  = acc.AccountID,
                    Duration       = result.Duration,
                    EngineGameID   = result.EngineBattleID,
                    MapResourceID  = db.Resources.Single(x => x.InternalName == result.Map).ResourceID,
                    ModResourceID  = db.Resources.Single(x => x.InternalName == result.Mod).ResourceID,
                    HasBots        = result.IsBots,
                    IsMission      = result.IsMission,
                    PlayerCount    = players.Count(x => !x.IsSpectator),
                    StartTime      = result.StartTime,
                    Title          = result.Title,
                    ReplayFileName = result.ReplayName,
                    EngineVersion  = result.EngineVersion,
                };
                db.SpringBattles.InsertOnSubmit(sb);

                foreach (BattlePlayerResult p in players)
                {
                    sb.SpringBattlePlayers.Add(new SpringBattlePlayer
                    {
                        AccountID       = db.Accounts.First(x => x.AccountID == p.LobbyID).AccountID,
                        AllyNumber      = p.AllyNumber,
                        CommanderType   = p.CommanderType,
                        IsInVictoryTeam = p.IsVictoryTeam,
                        IsSpectator     = p.IsSpectator,
                        LoseTime        = p.LoseTime
                    });
                }

                db.SubmitChanges();

                // awards
                foreach (string line in extraData.Where(x => x.StartsWith("award")))
                {
                    string[] partsSpace = line.Substring(6).Split(new[] { ' ' }, 3);
                    string   name       = partsSpace[0];
                    string   awardType  = partsSpace[1];
                    string   awardText  = partsSpace[2];

                    SpringBattlePlayer player = sb.SpringBattlePlayers.FirstOrDefault(x => x.Account.Name == name);
                    if (player != null)
                    {
                        db.AccountBattleAwards.InsertOnSubmit(new AccountBattleAward
                        {
                            AccountID        = player.AccountID,
                            SpringBattleID   = sb.SpringBattleID,
                            AwardKey         = awardType,
                            AwardDescription = awardText
                        });
                    }
                }

                var  text         = new StringBuilder();
                bool isPlanetwars = false;
                if (mode == AutohostMode.Planetwars && sb.SpringBattlePlayers.Count(x => !x.IsSpectator) >= 2 && sb.Duration >= GlobalConst.MinDurationForPlanetwars)
                {
                    // test that factions are not intermingled (each faction only has one ally number) - if they are it wasnt actually PW balanced
                    if (
                        sb.SpringBattlePlayers.Where(x => !x.IsSpectator && x.Account.Faction != null)
                        .GroupBy(x => x.Account.Faction)
                        .All(grp => grp.Select(x => x.AllyNumber).Distinct().Count() < 2))
                    {
                        isPlanetwars = true;


                        List <int> winnerTeams =
                            sb.SpringBattlePlayers.Where(x => x.IsInVictoryTeam && !x.IsSpectator).Select(x => x.AllyNumber).Distinct().ToList();
                        int?winNum = null;
                        if (winnerTeams.Count == 1)
                        {
                            winNum = winnerTeams[0];
                            if (winNum > 1)
                            {
                                winNum = null;
                                text.AppendLine("ERROR: Invalid winner");
                            }
                        }

                        PlanetWarsTurnHandler.EndTurn(result.Map, extraData, db, winNum, sb.SpringBattlePlayers.Where(x => !x.IsSpectator).Select(x => x.Account).ToList(), text, sb, sb.SpringBattlePlayers.Where(x => !x.IsSpectator && x.AllyNumber == 0).Select(x => x.Account).ToList());

                        Global.PlanetWarsMatchMaker.RemoveFromRunningBattles(context.AutohostName);
                    }
                    else
                    {
                        text.AppendLine("Battle wasn't PlanetWars balanced, it counts as a normal team game only");
                    }
                }

                bool noElo = (extraData.FirstOrDefault(x => x.StartsWith("noElo")) != null);
                try
                {
                    db.SubmitChanges();
                }
                catch (System.Data.Linq.DuplicateKeyException ex)
                {
                    Trace.TraceError(ex.ToString());
                }

                Dictionary <int, int> orgLevels = sb.SpringBattlePlayers.Select(x => x.Account).ToDictionary(x => x.AccountID, x => x.Level);

                sb.CalculateAllElo(noElo, isPlanetwars);
                foreach (var u in sb.SpringBattlePlayers.Where(x => !x.IsSpectator))
                {
                    u.Account.CheckLevelUp();
                }

                db.SubmitAndMergeChanges();

                try
                {
                    foreach (Account a in sb.SpringBattlePlayers.Where(x => !x.IsSpectator).Select(x => x.Account))
                    {
                        Global.Server.PublishAccountUpdate(a);
                    }
                }
                catch (Exception ex)
                {
                    Trace.TraceError("error updating extension data: {0}", ex);
                }

                foreach (Account account in sb.SpringBattlePlayers.Select(x => x.Account))
                {
                    if (account.Level > orgLevels[account.AccountID])
                    {
                        try
                        {
                            string message =
                                string.Format("Congratulations {0}! You just leveled up to level {1}. {3}/Users/Detail/{2}",
                                              account.Name,
                                              account.Level,
                                              account.AccountID,
                                              GlobalConst.BaseSiteUrl);
                            //text.AppendLine(message);
                            Global.Server.GhostPm(account.Name, message);
                        }
                        catch (Exception ex)
                        {
                            Trace.TraceError("Error sending level up lobby message: {0}", ex);
                        }
                    }
                }

                text.AppendLine(string.Format("BATTLE DETAILS AND REPLAY ----> {1}/Battles/Detail/{0} <-----", sb.SpringBattleID, GlobalConst.BaseSiteUrl));

                /*
                 * // create debriefing room, join players there and output message
                 * string channelName = "B" + sb.SpringBattleID;
                 * var joinplayers = new List<string>();
                 * joinplayers.AddRange(context.Players.Select(x => x.Name)); // add those who were there at start
                 * joinplayers.AddRange(sb.SpringBattlePlayers.Select(x => x.Account.Name)); // add those who played
                 * Battle bat = Global.Server.Battles.Values.FirstOrDefault(x => x.Founder.Name == context.AutohostName); // add those in lobby atm
                 *
                 *
                 * var conf = context.GetConfig();
                 * if (bat != null && (conf == null || conf.MinToJuggle == null)) // if not qm room do not join those who are in battle
                 * {
                 *  List<string> inbatPlayers = bat.Users.Keys.ToList();
                 *  joinplayers.RemoveAll(x => inbatPlayers.Contains(x));
                 * }
                 * foreach (string jp in joinplayers.Distinct().Where(x => x != context.AutohostName)) tas.ForceJoinChannel(jp, channelName);
                 * tas.JoinChannel(channelName); // join nightwatch and say it
                 * tas.Say(SayPlace.Channel, channelName, text.ToString(), true);
                 * tas.LeaveChannel(channelName);*/

                //text.Append(string.Format("Debriefing in #{0} - zk://chat/channel/{0}  ", channelName));
                return(text.ToString());
            }
            catch (Exception ex)
            {
                Trace.TraceError(ex.ToString());
                return(ex.ToString());
            }
        }
        private static void ProcessElos(SpringBattleContext result, ZkLobbyServer.ZkLobbyServer server, ZkDataContext db, SpringBattle sb)
        {
            bool noElo = result.OutputExtras.Any(x => x?.StartsWith("noElo", true, System.Globalization.CultureInfo.CurrentCulture) == true);

            sb.CalculateAllElo(noElo);

            if (!noElo)
            {
                RatingSystems.ProcessResult(sb);
            }

            foreach (var u in sb.SpringBattlePlayers.Where(x => !x.IsSpectator))
            {
                u.Account.CheckLevelUp();
            }

            db.SaveChanges();

            try
            {
                foreach (Account a in sb.SpringBattlePlayers.Where(x => !x.IsSpectator).Select(x => x.Account))
                {
                    server.PublishAccountUpdate(a);
                    server.PublishUserProfileUpdate(a);
                }
            }
            catch (Exception ex)
            {
                Trace.TraceError("error updating extension data: {0}", ex);
            }
        }
Example #13
0
        public void ProcessBattle(SpringBattle battle)
        {
            ICollection <int> winners = battle.SpringBattlePlayers.Where(p => p.IsInVictoryTeam && !p.IsSpectator).Select(p => RatingSystems.GetRatingId(p.AccountID)).Distinct().ToList();
            ICollection <int> losers  = battle.SpringBattlePlayers.Where(p => !p.IsInVictoryTeam && !p.IsSpectator).Select(p => RatingSystems.GetRatingId(p.AccountID)).Distinct().ToList();

            int date = RatingSystems.ConvertDateToDays(battle.StartTime);

            if (RatingSystems.Initialized)
            {
                if (winners.Intersect(losers).Any())
                {
                    Trace.TraceWarning("WHR B" + battle.SpringBattleID + " has winner loser intersection");
                }
                if (ProcessedBattles.Contains(battle.SpringBattleID))
                {
                    Trace.TraceWarning("WHR B" + battle.SpringBattleID + " has already been processed");
                }
                if (winners.Count == 0)
                {
                    Trace.TraceWarning("WHR B" + battle.SpringBattleID + " has no winner");
                }
                if (losers.Count == 0)
                {
                    Trace.TraceWarning("WHR B" + battle.SpringBattleID + " has no loser");
                }
            }

            if (!winners.Intersect(losers).Any() && !ProcessedBattles.Contains(battle.SpringBattleID) && winners.Count > 0 && losers.Count > 0)
            {
                battlesRegistered++;
                ProcessedBattles.Add(battle.SpringBattleID);

                if (date > RatingSystems.ConvertDateToDays(DateTime.UtcNow))
                {
                    Trace.TraceWarning("WHR " + category + ": Tried to register battle " + battle.SpringBattleID + " which is from the future " + (date) + " > " + RatingSystems.ConvertDateToDays(DateTime.UtcNow));
                }
                else
                {
                    CreateGame(losers, winners, false, date, battle.SpringBattleID);
                    futureDebriefings.ForEach(u => pendingDebriefings.TryAdd(u.Key, u.Value));
                    futureDebriefings.Clear();

                    if (RatingSystems.Initialized)
                    {
                        Trace.TraceInformation(battlesRegistered + " battles registered for WHR " + category + ", latest Battle: " + battle.SpringBattleID);
                        UpdateRatings();
                    }
                }
            }
            else
            {
                PendingDebriefing debriefing;
                futureDebriefings.TryGetValue(battle.SpringBattleID, out debriefing);
                if (debriefing == null)
                {
                    pendingDebriefings.TryGetValue(battle.SpringBattleID, out debriefing);
                }
                if (debriefing != null)
                {
                    Trace.TraceWarning("Battle " + battle.SpringBattleID + " was processed before attaching pending report");
                    debriefing.debriefingConsumer.Invoke(debriefing.partialDebriefing);
                }
            }
        }
    /// <summary>
    /// Process planet wars turn
    /// </summary>
    /// <param name="mapName"></param>
    /// <param name="extraData"></param>
    /// <param name="db"></param>
    /// <param name="winNum">0 = attacker wins, 1 = defender wins</param>
    /// <param name="players"></param>
    /// <param name="text"></param>
    /// <param name="sb"></param>
    public static void EndTurn(string mapName, List <string> extraData, ZkDataContext db, int?winNum, List <Account> players, StringBuilder text, SpringBattle sb, List <Account> attackers)
    {
        if (extraData == null)
        {
            extraData = new List <string>();
        }
        Galaxy gal    = db.Galaxies.Single(x => x.IsDefault);
        Planet planet = gal.Planets.Single(x => x.Resource.InternalName == mapName);

        text.AppendFormat("Battle on http://zero-k.info/PlanetWars/Planet/{0} has ended\n", planet.PlanetID);


        Faction attacker  = attackers.Where(x => x.Faction != null).Select(x => x.Faction).First();
        var     defenders = players.Where(x => x.FactionID != attacker.FactionID && x.FactionID != null).ToList();
        bool    isAttackerWinner;
        bool    wasAttackerCcDestroyed = false;
        bool    wasDefenderCcDestroyed = false;

        if (winNum != null)
        {
            if (winNum == 0)
            {
                isAttackerWinner = true;
            }
            else
            {
                isAttackerWinner = false;
            }

            wasAttackerCcDestroyed = extraData.Any(x => x.StartsWith("hqkilled,0"));
            wasDefenderCcDestroyed = extraData.Any(x => x.StartsWith("hqkilled,1"));
        }
        else
        {
            text.AppendFormat("No winner on this battle!");
            Trace.TraceError("PW battle without a winner {0}", sb != null ? sb.SpringBattleID : (int?)null);
            return;
        }

        int    dropshipsSent   = (planet.PlanetFactions.Where(x => x.Faction == attacker).Sum(x => (int?)x.Dropships) ?? 0);
        bool   isLinked        = planet.CanDropshipsAttack(attacker);
        string influenceReport = "";

        // distribute influence
        // save influence gains
        // give influence to main attackers
        double planetDropshipDefs = (planet.PlanetStructures.Where(x => x.IsActive).Sum(x => x.StructureType.EffectDropshipDefense) ?? 0);
        double planetIpDefs       = (planet.PlanetStructures.Where(x => x.IsActive).Sum(x => x.StructureType.EffectReduceBattleInfluenceGain) ?? 0);

        double baseInfluence = GlobalConst.BaseInfluencePerBattle;
        double influence     = baseInfluence;


        double effectiveShips = Math.Max(0, (dropshipsSent - planetDropshipDefs));
        double shipBonus      = effectiveShips * GlobalConst.InfluencePerShip;

        double defenseBonus = -planetIpDefs;
        double techBonus    = attacker.GetFactionUnlocks().Count() * GlobalConst.InfluencePerTech;
        double ipMultiplier = 1;
        string ipReason;

        if (!isAttackerWinner)
        {
            if (wasDefenderCcDestroyed)
            {
                ipMultiplier = 0.2;
                ipReason     = "from losing but killing defender's CC";
            }
            else
            {
                ipMultiplier = 0;
                ipReason     = "from losing horribly";
            }
        }
        else
        {
            if (wasAttackerCcDestroyed)
            {
                ipReason     = "from losing own CC";
                ipMultiplier = 0.5;
            }
            else
            {
                ipReason = "from winning flawlessly";
            }
        }
        if (!isLinked && effectiveShips < GlobalConst.DropshipsForFullWarpIPGain)
        {
            var newMult = effectiveShips / GlobalConst.DropshipsForFullWarpIPGain;
            ipMultiplier *= newMult;
            ipReason      = ipReason + string.Format(" and reduced to {0}% because only {1} of {2} ships needed for warp attack got past defenses", (int)(newMult * 100.0), (int)effectiveShips, GlobalConst.DropshipsForFullWarpIPGain);
        }


        influence = (influence + shipBonus + techBonus) * ipMultiplier + defenseBonus;
        if (influence < 0)
        {
            influence = 0;
        }
        influence = Math.Floor(influence * 100) / 100;

        // main winner influence
        PlanetFaction entry = planet.PlanetFactions.FirstOrDefault(x => x.Faction == attacker);

        if (entry == null)
        {
            entry = new PlanetFaction {
                Faction = attacker, Planet = planet,
            };
            planet.PlanetFactions.Add(entry);
        }

        entry.Influence += influence;


        // clamping of influence
        // gained over 100, sole owner
        if (entry.Influence >= 100)
        {
            entry.Influence = 100;
            foreach (var pf in planet.PlanetFactions.Where(x => x.Faction != attacker))
            {
                pf.Influence = 0;
            }
        }
        else
        {
            var sumOthers = planet.PlanetFactions.Where(x => x.Faction != attacker).Sum(x => (double?)x.Influence) ?? 0;
            if (sumOthers + entry.Influence > 100)
            {
                var excess = sumOthers + entry.Influence - 100;
                foreach (var pf in planet.PlanetFactions.Where(x => x.Faction != attacker))
                {
                    pf.Influence -= pf.Influence / sumOthers * excess;
                }
            }
        }
        try
        {
            influenceReport = String.Format("{0} gained {1} influence ({2}% {3} of {4}{5}{6}{7})",
                                            attacker.Shortcut,
                                            influence,
                                            (int)(ipMultiplier * 100.0),
                                            ipReason,
                                            baseInfluence + " base",
                                            techBonus > 0 ? " +" + techBonus + " from techs" : "",
                                            shipBonus > 0 ? " +" + shipBonus + " from dropships" : "",
                                            defenseBonus != 0 ? " -" + -defenseBonus + " from defenses" : "");
        }
        catch (Exception ex)
        {
            Trace.TraceError(ex.ToString());
        }


        // distribute metal
        var attackersTotalMetal = Math.Floor(GlobalConst.PlanetWarsAttackerMetal);
        var defendersTotalMetal = Math.Floor(GlobalConst.PlanetWarsDefenderMetal);
        var attackerMetal       = Math.Floor(attackersTotalMetal / attackers.Count);
        var defenderMetal       = Math.Floor(defendersTotalMetal / defenders.Count);

        foreach (Account w in attackers)
        {
            w.ProduceMetal(attackerMetal);
            var ev = Global.CreateEvent("{0} gained {1} metal from battle {2}",
                                        w,
                                        attackerMetal,
                                        sb);
            db.Events.InsertOnSubmit(ev);
            text.AppendLine(ev.PlainText);
        }

        foreach (Account w in defenders)
        {
            w.ProduceMetal(defenderMetal);
            var ev = Global.CreateEvent("{0} gained {1} metal from battle {2}",
                                        w,
                                        defenderMetal,
                                        sb);

            db.Events.InsertOnSubmit(ev);
            text.AppendLine(ev.PlainText);
        }


        // remove dropships
        foreach (var pf in planet.PlanetFactions.Where(x => x.Faction == attacker))
        {
            pf.Dropships = 0;
        }


        // add attack points
        foreach (Account acc in players)
        {
            int ap = acc.Faction == attacker ? GlobalConst.AttackPointsForVictory : GlobalConst.AttackPointsForDefeat;
            if (acc.Faction != null)
            {
                AccountPlanet apentry = planet.AccountPlanets.SingleOrDefault(x => x.AccountID == acc.AccountID);
                if (apentry == null)
                {
                    apentry = new AccountPlanet {
                        AccountID = acc.AccountID, PlanetID = planet.PlanetID
                    };
                    db.AccountPlanets.InsertOnSubmit(apentry);
                }

                apentry.AttackPoints += ap;
            }
            acc.PwAttackPoints += ap;
        }

        // paranoia!
        try
        {
            var mainEvent = Global.CreateEvent("{0} attacked {1} {2} in {3} and {4}. {5}",
                                               attacker,
                                               planet.Faction,
                                               planet,
                                               sb,
                                               isAttackerWinner ? "won. " : "lost. ",
                                               influenceReport
                                               );
            db.Events.InsertOnSubmit(mainEvent);
            text.AppendLine(mainEvent.PlainText);
        }
        catch (Exception ex)
        {
            Trace.TraceError(ex.ToString());
        }

        // destroy pw structures killed ingame
        if (!isAttackerWinner)
        {
            var handled = new List <string>();
            foreach (string line in extraData.Where(x => x.StartsWith("structurekilled")))
            {
                string[] data     = line.Substring(16).Split(',');
                string   unitName = data[0];
                if (handled.Contains(unitName))
                {
                    continue;
                }
                handled.Add(unitName);
                foreach (PlanetStructure s in planet.PlanetStructures.Where(x => x.StructureType.IngameUnitName == unitName))
                {
                    if (s.StructureType.IsIngameDestructible)
                    {
                        s.IsActive        = false;
                        s.ActivatedOnTurn = gal.Turn + (int)(s.StructureType.TurnsToActivate * (GlobalConst.StructureIngameDisableTimeMult - 1));

                        var ev = Global.CreateEvent("{0} has been disabled on {1} planet {2}. {3}", s.StructureType.Name, planet.Faction, planet, sb);
                        db.Events.InsertOnSubmit(ev);
                        text.AppendLine(ev.PlainText);
                    }
                }
            }
        }
        else
        {
            // attacker won disable all
            foreach (var s in planet.PlanetStructures.Where(x => x.StructureType.IsIngameDestructible))
            {
                s.IsActive        = false;
                s.ActivatedOnTurn = gal.Turn + (int)(s.StructureType.TurnsToActivate * (GlobalConst.StructureIngameDisableTimeMult - 1));
            }
            // destroy structures by battle (usually defenses)
            foreach (PlanetStructure s in planet.PlanetStructures.Where(x => x.StructureType.BattleDeletesThis).ToList())
            {
                planet.PlanetStructures.Remove(s);
            }

            var ev = Global.CreateEvent("All structures have been disabled on {0} planet {1}. {2}", planet.Faction, planet, sb);
            db.Events.InsertOnSubmit(ev);
            text.AppendLine(ev.PlainText);
        }

        db.SubmitAndMergeChanges();

        gal.DecayInfluence();
        gal.SpreadInfluence();

        // process faction energies
        foreach (var fac in db.Factions.Where(x => !x.IsDeleted))
        {
            fac.ProcessEnergy(gal.Turn);
        }

        // process production
        gal.ProcessProduction();


        // process treaties
        foreach (var tr in db.FactionTreaties.Where(x => x.TreatyState == TreatyState.Accepted || x.TreatyState == TreatyState.Suspended))
        {
            if (tr.ProcessTrade(false))
            {
                tr.TreatyState = TreatyState.Accepted;
                if (tr.TurnsTotal != null)
                {
                    tr.TurnsRemaining--;
                    if (tr.TurnsRemaining <= 0)
                    {
                        tr.TreatyState = TreatyState.Invalid;
                        db.FactionTreaties.DeleteOnSubmit(tr);
                    }
                }
            }
            else
            {
                tr.TreatyState = TreatyState.Suspended;
            }
        }

        // burn extra energy
        foreach (var fac in db.Factions.Where(x => !x.IsDeleted))
        {
            fac.ConvertExcessEnergyToMetal();
        }


        int?oldOwner = planet.OwnerAccountID;

        gal.Turn++;
        db.SubmitAndMergeChanges();

        db = new ZkDataContext(); // is this needed - attempt to fix setplanetownersbeing buggy
        PlanetwarsController.SetPlanetOwners(db, sb);
        gal = db.Galaxies.Single(x => x.IsDefault);

        planet = gal.Planets.Single(x => x.Resource.InternalName == mapName);
        if (planet.OwnerAccountID != oldOwner && planet.OwnerAccountID != null)
        {
            text.AppendFormat("Congratulations!! Planet {0} was conquered by {1} !!  http://zero-k.info/PlanetWars/Planet/{2}\n",
                              planet.Name,
                              planet.Account.Name,
                              planet.PlanetID);
        }

        try
        {
            // store history
            foreach (Planet p in gal.Planets)
            {
                db.PlanetOwnerHistories.InsertOnSubmit(new PlanetOwnerHistory
                {
                    PlanetID       = p.PlanetID,
                    OwnerAccountID = p.OwnerAccountID,
                    OwnerClanID    = p.OwnerAccountID != null ? p.Account.ClanID : null,
                    OwnerFactionID = p.OwnerFactionID,
                    Turn           = gal.Turn
                });
            }

            db.SubmitChanges();
        }
        catch (Exception ex)
        {
            Trace.TraceError(ex.ToString());
            text.AppendLine("error saving history: " + ex);
        }

        //rotate map
        if (GlobalConst.RotatePWMaps)
        {
            db     = new ZkDataContext();
            gal    = db.Galaxies.Single(x => x.IsDefault);
            planet = gal.Planets.Single(x => x.Resource.InternalName == mapName);
            var mapList = db.Resources.Where(x => x.MapPlanetWarsIcon != null && x.Planets.Where(p => p.GalaxyID == gal.GalaxyID).Count() == 0 && x.FeaturedOrder != null &&
                                             x.ResourceID != planet.MapResourceID && x.MapWaterLevel == planet.Resource.MapWaterLevel).ToList();
            if (mapList.Count > 0)
            {
                int      r          = new Random().Next(mapList.Count);
                int      resourceID = mapList[r].ResourceID;
                Resource newMap     = db.Resources.Single(x => x.ResourceID == resourceID);
                text.AppendLine(String.Format("Map cycler - {0} maps found, selected map {1} to replace map {2}", mapList.Count, newMap.InternalName, planet.Resource.InternalName));
                planet.Resource = newMap;
                gal.IsDirty     = true;
            }
            else
            {
                text.AppendLine("Map cycler - no maps found");
            }
            db.SubmitAndMergeChanges();
        }
    }
Example #15
0
        public override LinkedListNode <Tag> Translate(TranslateContext context, LinkedListNode <Tag> self)
        {
            if (self.Previous?.Value is LiteralTag) // previous is contiguous text
            {
                context.Append("@");
                return(self.Next);
            }
            var ender = self.Next.FirstNode(x => !(x.Value is LiteralTag || x.Value is UnderscoreTag));
            var text  = self.Next.GetOriginalContentUntilNode(ender); // get next string

            if (string.IsNullOrEmpty(text))
            {
                context.Append("@");
                return(self.Next);
            }
            int idx;

            for (idx = 0; idx < text.Length; idx++)
            {
                if (!Utils.ValidLobbyNameCharacter(text[idx]))
                {
                    break;
                }
            }
            string remainder = null;
            string val       = text;

            if (idx != 0 && idx < text.Length)
            {
                remainder = text.Substring(idx, text.Length - idx);
                val       = text.Substring(0, idx);
            }

            if (string.IsNullOrEmpty(val))
            {
                context.Append("@");
                return(self.Next);
            }
            var  db     = new ZkDataContext();
            char prefix = Char.ToLowerInvariant(val[0]);
            int  id     = 0;

            int.TryParse(val.Substring(1), out id);

            var fac = db.Factions.FirstOrDefault(x => x.Shortcut == val);

            if (fac == null && prefix == 'f')
            {
                fac = db.Factions.FirstOrDefault(x => x.FactionID == id);
            }
            if (fac != null)
            {
                context.Append(context.Html.PrintFaction(fac, false));
                context.Append(remainder);
                return(ender);
            }

            var acc = Account.AccountByName(db, val);

            if (acc == null && prefix == 'u')
            {
                acc = db.Accounts.FirstOrDefault(x => x.AccountID == id);
            }
            if (acc != null)
            {
                context.Append(context.Html.PrintAccount(acc));
                context.Append(remainder);
                return(ender);
            }

            var clan = db.Clans.FirstOrDefault(x => x.Shortcut == val);

            if (clan == null && prefix == 'c')
            {
                clan = db.Clans.FirstOrDefault(x => x.ClanID == id);
            }
            if (clan != null)
            {
                context.Append(context.Html.PrintClan(clan));
                context.Append(remainder);
                return(ender);
            }

            // can't tag a battle by its name
            SpringBattle bat = null;

            if (prefix == 'b')
            {
                bat = db.SpringBattles.FirstOrDefault(x => x.SpringBattleID == id);
            }
            if (bat != null)
            {
                context.Append(context.Html.PrintBattle(bat));
                context.Append(remainder);
                return(ender);
            }

            context.Append("@");
            return(self.Next);
        }