diff --git a/EDPlayerJournal/BGS/CombatZone.cs b/EDPlayerJournal/BGS/CombatZone.cs index 4f6d8ed..18bb10e 100644 --- a/EDPlayerJournal/BGS/CombatZone.cs +++ b/EDPlayerJournal/BGS/CombatZone.cs @@ -92,6 +92,13 @@ public class CombatZone : Transaction { get { return string.Compare(Type, CombatZones.AXCombatZone) == 0; } } + /// + /// Returns true if it is a power combat zone + /// + public bool IsPower { + get { return string.Compare(Type, CombatZones.PowerCombatZone) == 0; } + } + public override int CompareTo(Transaction? obj) { if (obj == null || obj.GetType() != typeof(CombatZone)) { return -1; diff --git a/EDPlayerJournal/BGS/MeritsGained.cs b/EDPlayerJournal/BGS/MeritsGained.cs new file mode 100644 index 0000000..872b018 --- /dev/null +++ b/EDPlayerJournal/BGS/MeritsGained.cs @@ -0,0 +1,25 @@ +using EDPlayerJournal.Entries; + +namespace EDPlayerJournal.BGS; + +public class MeritsGained : Transaction { + public MeritsGained() { } + + public MeritsGained(Entry entry) { + Entries.Add(entry); + } + + /// + /// Number of merits gained + /// + public long Merits { get; set; } = 0; + + /// + /// For what power those merits were gained + /// + public string Power { get; set; } = string.Empty; + + public override string ToString() { + return string.Format("{0} Merits gained for {1}", Merits, Power); + } +} diff --git a/EDPlayerJournal/BGS/Parsers/PowerplayParser.cs b/EDPlayerJournal/BGS/Parsers/PowerplayParser.cs new file mode 100644 index 0000000..a808f23 --- /dev/null +++ b/EDPlayerJournal/BGS/Parsers/PowerplayParser.cs @@ -0,0 +1,31 @@ +using EDPlayerJournal.Entries; + +namespace EDPlayerJournal.BGS.Parsers; + +internal class PowerplayParser : ITransactionParserPart { + public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { + PowerplayEntry? p = entry as PowerplayEntry; + if (p == null) { + return; + } + + if (context.LastMerits == null) { + context.LastMerits = p.Merits; + } + + context.CurrentMerits = p.Merits; + + if (context.LastMerits != context.CurrentMerits) { + if (!options.IgnorePowerplay) { + transactions.Add(new MeritsGained(entry) { + Merits = ((long)(context.CurrentMerits - context.LastMerits)), + Power = p.Power, + System = context.CurrentSystem, + Faction = p.Power, + }); + } + } + + context.LastMerits = context.CurrentMerits; + } +} diff --git a/EDPlayerJournal/BGS/TransactionParser.cs b/EDPlayerJournal/BGS/TransactionParser.cs index f8d0c1d..47f86d1 100644 --- a/EDPlayerJournal/BGS/TransactionParser.cs +++ b/EDPlayerJournal/BGS/TransactionParser.cs @@ -16,6 +16,12 @@ public class TransactionParserOptions { /// public bool IgnoreInfluenceSupport { get; set; } = false; + /// + /// Whether we ignore power play and merits gained for now. Support for this + /// is experimental at the moment, so that is why it ist `true`. + /// + public bool IgnorePowerplay { get; set; } = true; + /// /// Whether to ignore market buy. Buying from a market gives a small amount /// of INF if it is sold to a high demand market, but generally one buys from @@ -581,14 +587,25 @@ internal class ReceiveTextParser : ITransactionParserPart { } } +internal class SelfDestructParser : ITransactionParserPart { + public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { + context.SelfDestruct = true; + } +} + internal class DiedParser : ITransactionParserPart { public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { // Death only matters in ship. On foot you can just redeploy with the dropship. if (context.IsOnFoot) { return; } - // You can't complete a combat zone if you die in it. Others might keep it open - // for you, but still you will not have completed it unless you jump back in. + if (context.SelfDestruct != null && context.SelfDestruct == true) { + // Some people just suicide to fast track back to stations after a CZ, + // especially since combat bonds don't disappear on death. So count the CZ + // on self destruct + context.DiscernCombatZone(transactions, entry); + context.SelfDestruct = null; + } context.ResetCombatZone(); // Dying also moves you back to another instance context.LeftInstance(); @@ -627,9 +644,11 @@ public class TransactionParser { { Events.Missions, new MissionsParser() }, { Events.MultiSellExplorationData, new MultiSellExplorationDataParser() }, { Events.Music, new MusicParser() }, + { Events.Powerplay, new PowerplayParser() }, { Events.ReceiveText, new ReceiveTextParser() }, { Events.RedeemVoucher, new RedeemVoucherParser() }, { Events.SearchAndRescue, new SearchAndRescueParser() }, + { Events.SelfDestruct, new SelfDestructParser() }, { Events.SellExplorationData, new SellExplorationDataParser() }, { Events.SellMicroResources, new SellMicroResourcesParser() }, { Events.SellOrganicData, new SellOrganicDataParser() }, diff --git a/EDPlayerJournal/BGS/TransactionParserContext.cs b/EDPlayerJournal/BGS/TransactionParserContext.cs index 22b3e51..04a0f22 100644 --- a/EDPlayerJournal/BGS/TransactionParserContext.cs +++ b/EDPlayerJournal/BGS/TransactionParserContext.cs @@ -62,11 +62,23 @@ internal class TransactionParserContext { public bool HaveSeenAlliedCorrespondent { get; set; } = false; public bool HaveSeenEnemyCorrespondent { get; set; } = false; + public bool? SelfDestruct { get; set; } = null; + /// /// Current Odyssey settlement. /// public string? Settlement { get; set; } = null; + /// + /// Current Merits + /// + public long? CurrentMerits { get; set; } = null; + + /// + /// Merits from last login + /// + public long? LastMerits { get; set; } = null; + /// /// Returns true if the current session is legacy /// @@ -128,16 +140,29 @@ internal class TransactionParserContext { Settlement = null; } + private bool HadCombatZone() { + if (CurrentInstanceType != null && + Instances.IsInstance(CurrentInstanceType, Instances.PowerWarzoneMedium)) { + return true; + } + + if (HighestCombatBond == null && + LastRecordedAwardingFaction == null && + HaveSeenAXWarzoneNPC == false && + CurrentInstanceType == null) { + return false; + } + + return true; + } + public void DiscernCombatZone(TransactionList transactions, Entry e) { string? grade = CombatZones.DifficultyLow; string cztype; ulong highest = HighestCombatBond ?? 0; string? faction = LastRecordedAwardingFaction; - if (HighestCombatBond == null && - LastRecordedAwardingFaction == null && - HaveSeenAXWarzoneNPC == false && - CurrentInstanceType == null) { + if (!HadCombatZone()) { return; } @@ -157,7 +182,8 @@ internal class TransactionParserContext { return; } if (LastRecordedAwardingFaction == null && - Instances.IsHumanWarzone(CurrentInstanceType)) { + (Instances.IsHumanWarzone(CurrentInstanceType) || + Instances.IsPowerWarzone(CurrentInstanceType))) { transactions.AddIncomplete(new CombatZone(), "Could not discern for whom you fought for, " + "as it seems you haven't killed anyone in the ship combat zone.", @@ -187,6 +213,9 @@ internal class TransactionParserContext { } else if (Instances.IsInstance(CurrentInstanceType, Instances.WarzoneThargoidVeryHigh)) { cztype = CombatZones.AXCombatZone; grade = CombatZones.DifficultyVeryHigh; + } else if (Instances.IsInstance(CurrentInstanceType, Instances.PowerWarzoneMedium)) { + cztype = CombatZones.PowerCombatZone; + grade = CombatZones.DifficultyMedium; } else { transactions.AddIncomplete(new CombatZone(), "Unknown conflict zone difficulty", diff --git a/EDPlayerJournal/CombatZones.cs b/EDPlayerJournal/CombatZones.cs index 3c59477..4888f41 100644 --- a/EDPlayerJournal/CombatZones.cs +++ b/EDPlayerJournal/CombatZones.cs @@ -14,6 +14,11 @@ public class CombatZones { /// public static readonly string ShipCombatZone = "Ship"; + /// + /// Power combat zones, new in Ascendancy update. + /// + public static readonly string PowerCombatZone = "Power"; + /// /// AX combat zone /// diff --git a/EDPlayerJournal/Entries/Entry.cs b/EDPlayerJournal/Entries/Entry.cs index c4fa681..cd703a1 100644 --- a/EDPlayerJournal/Entries/Entry.cs +++ b/EDPlayerJournal/Entries/Entry.cs @@ -39,9 +39,11 @@ public class Entry { { Events.Missions, typeof(MissionsEntry) }, { Events.MultiSellExplorationData, typeof(MultiSellExplorationDataEntry) }, { Events.Music, typeof(MusicEntry) }, + { Events.Powerplay, typeof(PowerplayEntry) }, { Events.ReceiveText, typeof(ReceiveTextEntry) }, { Events.RedeemVoucher, typeof(RedeemVoucherEntry) }, { Events.SearchAndRescue, typeof(SearchAndRescueEntry) }, + { Events.SelfDestruct, typeof(SelfDestructEntry) }, { Events.SellExplorationData, typeof(SellExplorationDataEntry) }, { Events.SellMicroResources, typeof(SellMicroResourcesEntry) }, { Events.SellOrganicData, typeof(SellOrganicDataEntry) }, diff --git a/EDPlayerJournal/Entries/Events.cs b/EDPlayerJournal/Entries/Events.cs index d9b2fb6..649eff2 100644 --- a/EDPlayerJournal/Entries/Events.cs +++ b/EDPlayerJournal/Entries/Events.cs @@ -29,9 +29,11 @@ public class Events { public static readonly string Missions = "Missions"; public static readonly string MultiSellExplorationData = "MultiSellExplorationData"; public static readonly string Music = "Music"; + public static readonly string Powerplay = "Powerplay"; public static readonly string ReceiveText = "ReceiveText"; public static readonly string RedeemVoucher = "RedeemVoucher"; public static readonly string SearchAndRescue = "SearchAndRescue"; + public static readonly string SelfDestruct = "SelfDestruct"; public static readonly string SellExplorationData = "SellExplorationData"; public static readonly string SellMicroResources = "SellMicroResources"; public static readonly string SellOrganicData = "SellOrganicData"; diff --git a/EDPlayerJournal/Entries/PowerplayEntry.cs b/EDPlayerJournal/Entries/PowerplayEntry.cs new file mode 100644 index 0000000..22d9fc1 --- /dev/null +++ b/EDPlayerJournal/Entries/PowerplayEntry.cs @@ -0,0 +1,32 @@ +using System.Reflection; + +namespace EDPlayerJournal.Entries { + public class PowerplayEntry : Entry { + /// + /// Name of the power + /// + public string Power { get; set; } = string.Empty; + + /// + /// Player rank + /// + public int Rank { get; set; } = 0; + + /// + /// Current merits of the player + /// + public long Merits { get; set; } = 0; + + /// + /// Time pledged (in seconds?) + /// + public long TimePledged { get; set; } = 0; + + protected override void Initialise() { + Power = JSON.Value("Power") ?? string.Empty; + Rank = JSON.Value("Rank") ?? 0; + Merits = JSON.Value("Merits") ?? 0; + TimePledged = JSON.Value("TimePledged") ?? 0; + } + } +} diff --git a/EDPlayerJournal/Entries/SelfDestructEntry.cs b/EDPlayerJournal/Entries/SelfDestructEntry.cs new file mode 100644 index 0000000..79f723b --- /dev/null +++ b/EDPlayerJournal/Entries/SelfDestructEntry.cs @@ -0,0 +1,5 @@ +namespace EDPlayerJournal.Entries { + public class SelfDestructEntry : Entry { + // Has no data + } +} diff --git a/EDPlayerJournal/Instances.cs b/EDPlayerJournal/Instances.cs index c9068d0..1587425 100644 --- a/EDPlayerJournal/Instances.cs +++ b/EDPlayerJournal/Instances.cs @@ -18,6 +18,11 @@ public class Instances { /// public static readonly string WarzoneHigh = "$Warzone_PointRace_High"; + /// + /// Medium power play conflict zone, new in PP 2.0 Ascendancy update + /// + public static readonly string PowerWarzoneMedium = "$Warzone_Powerplay_Med"; + /// /// Low Thargoid combat zone /// @@ -52,8 +57,17 @@ public class Instances { ; } + public static bool IsPowerWarzone(string type) { + return + IsInstance(type, PowerWarzoneMedium) + ; + } + public static bool IsWarzone(string type) { - return IsHumanWarzone(type) || IsThargoidWarzone(type); + return IsHumanWarzone(type) || + IsThargoidWarzone(type) || + IsPowerWarzone(type) + ; } public static bool IsInstance(string type, string instance) { diff --git a/EliteBGS/CHANGELOG.md b/EliteBGS/CHANGELOG.md index 18ea6e8..681cbe6 100644 --- a/EliteBGS/CHANGELOG.md +++ b/EliteBGS/CHANGELOG.md @@ -1,5 +1,12 @@ # EliteBGS changelog +## 0.4.4 on ??.??.202? + +* Add support for Power Conflict Zones +* Show no inf as "Zero INF", so the Nova Navy bot can parse it properly +* Add support for MeritsGained, but disable it for now, as the player + journal is massively lacking in terms of Powerplay 2.0 support. + ## 0.4.3 on 18.09.2024 * Add possibility to post log reports to Discord webhooks. diff --git a/EliteBGS/DiscordLogGenerator.cs b/EliteBGS/DiscordLogGenerator.cs index d941366..22a23ed 100644 --- a/EliteBGS/DiscordLogGenerator.cs +++ b/EliteBGS/DiscordLogGenerator.cs @@ -21,6 +21,7 @@ public class DiscordLogGenerator { new CargoSoldFormatter(), new VistaGenomicsFormat(), new SearchAndRescueFormat(), + new MeritsGainedFormat(), }; protected virtual string GetToolVersion() { diff --git a/EliteBGS/EliteBGS - Backup.csproj b/EliteBGS/EliteBGS - Backup.csproj deleted file mode 100644 index fc32da3..0000000 --- a/EliteBGS/EliteBGS - Backup.csproj +++ /dev/null @@ -1,86 +0,0 @@ - - - net7.0-windows - WinExe - 0.2.6 - false - true - true - true - - - EliteBGSApplication - - - Salus.ico - BGS reporting and logging tool for Elite:Dangerous - nola - Copyright 2019 by Florian Stinglmayr - https://git.aror.org/florian/EDBGS - ED;Elite Dangerous;BGS - https://bgs.n0la.org - README.md - - - - PreserveNewest - - - PreserveNewest - True - \ - - - Always - - - - - PreserveNewest - - - - - PreserveNewest - - - - - - - - Always - - - - - - - - - - - - - - - - - - - - - - - True - True - Resources.resx - - - - - PublicResXFileCodeGenerator - Resources.Designer.cs - - - \ No newline at end of file diff --git a/EliteBGS/LogGenerator/MeritsGainedFormat.cs b/EliteBGS/LogGenerator/MeritsGainedFormat.cs new file mode 100644 index 0000000..564394a --- /dev/null +++ b/EliteBGS/LogGenerator/MeritsGainedFormat.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using EDPlayerJournal; +using EDPlayerJournal.BGS; + +namespace EliteBGS.LogGenerator; + +public class MeritsGainedFormat : LogFormatter { + public string GenerateLog(Objective objective) { + var builder = new StringBuilder(); + + var merits = objective + .EnabledOfType() + .GroupBy(x => x.Power) + .ToDictionary(x => x.Key, x => x.Sum(x => x.Merits)) + ; + + if (merits == null || merits.Count == 0) { + return ""; + } + + foreach (var merit in merits) { + builder.AppendFormat("{0} merits gained for {1}\n", merit.Value, merit.Key); + } + + return builder.ToString().Trim(); + } + + public string GenerateSummary(Objective objective) { + var builder = new StringBuilder(); + + var merits = objective + .EnabledOfType() + .GroupBy(x => x.Power) + .ToDictionary(x => x.Key, x => x.Sum(x => x.Merits)) + ; + + if (merits == null || merits.Count == 0) { + return ""; + } + + foreach (var merit in merits) { + builder.AppendFormat("MRT: {0}, {1}; ", merit.Key, merit.Value); + } + + return builder.ToString().Trim().TrimEnd(';'); + } +} diff --git a/EliteBGS/LogGenerator/MissionFormat.cs b/EliteBGS/LogGenerator/MissionFormat.cs index c92e539..ea70637 100644 --- a/EliteBGS/LogGenerator/MissionFormat.cs +++ b/EliteBGS/LogGenerator/MissionFormat.cs @@ -64,6 +64,7 @@ public class MissionFormat : LogFormatter { Dictionary passengers = new(); StringBuilder output = new StringBuilder(); long total_influence = 0; + bool hadmissions = false; var missions = objective.EnabledOfType(); var support = objective.EnabledOfType(); @@ -85,6 +86,7 @@ public class MissionFormat : LogFormatter { ++collated[m.MissionName][m.Influence]; + hadmissions = true; total_influence += m.Influence.Length; if (m.AcceptedEntry != null && @@ -121,19 +123,23 @@ public class MissionFormat : LogFormatter { output.Append(failedlog); output.Append("\n"); } + if (failed.Count > 0) { + hadmissions = true; + } total_influence += failed.Sum(x => x.InfluenceAmount); foreach (InfluenceSupport inf in support) { output.Append(inf.ToString()); output.Append("\n"); + hadmissions = true; total_influence += inf.Influence.InfluenceAmount; } - if (support.Count() > 0) { + if (support.Count > 0) { output.Append("\n"); } - if (total_influence != 0) { + if (hadmissions) { output.AppendFormat("Total Influence: {0}", total_influence); } @@ -141,6 +147,10 @@ public class MissionFormat : LogFormatter { } public string GenerateSummary(Objective objective) { + bool hadmissions = objective + .EnabledOfType() + .Count > 0 + ; long influence = objective .EnabledOfType() .Sum(x => x.Influence.Length) @@ -154,7 +164,7 @@ public class MissionFormat : LogFormatter { .Sum(x => x.InfluenceAmount) ; - if (influence == 0 && support == 0 && failed == 0) { + if (influence == 0 && support == 0 && failed == 0 && !hadmissions) { return ""; } diff --git a/EliteBGS/MainWindow.xaml b/EliteBGS/MainWindow.xaml index 4412c87..fff5493 100644 --- a/EliteBGS/MainWindow.xaml +++ b/EliteBGS/MainWindow.xaml @@ -121,6 +121,7 @@