Merge branch 'master' into feature/avalonia

This commit is contained in:
Florian Stinglmayr 2024-11-18 09:43:07 +01:00
commit a95c882370
20 changed files with 277 additions and 98 deletions

View File

@ -92,6 +92,13 @@ public class CombatZone : Transaction {
get { return string.Compare(Type, CombatZones.AXCombatZone) == 0; }
}
/// <summary>
/// Returns true if it is a power combat zone
/// </summary>
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;

View File

@ -0,0 +1,25 @@
using EDPlayerJournal.Entries;
namespace EDPlayerJournal.BGS;
public class MeritsGained : Transaction {
public MeritsGained() { }
public MeritsGained(Entry entry) {
Entries.Add(entry);
}
/// <summary>
/// Number of merits gained
/// </summary>
public long Merits { get; set; } = 0;
/// <summary>
/// For what power those merits were gained
/// </summary>
public string Power { get; set; } = string.Empty;
public override string ToString() {
return string.Format("{0} Merits gained for {1}", Merits, Power);
}
}

View File

@ -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;
}
}

View File

@ -16,6 +16,12 @@ public class TransactionParserOptions {
/// </summary>
public bool IgnoreInfluenceSupport { get; set; } = false;
/// <summary>
/// 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`.
/// </summary>
public bool IgnorePowerplay { get; set; } = true;
/// <summary>
/// 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() },

View File

@ -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;
/// <summary>
/// Current Odyssey settlement.
/// </summary>
public string? Settlement { get; set; } = null;
/// <summary>
/// Current Merits
/// </summary>
public long? CurrentMerits { get; set; } = null;
/// <summary>
/// Merits from last login
/// </summary>
public long? LastMerits { get; set; } = null;
/// <summary>
/// Returns true if the current session is legacy
/// </summary>
@ -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",

View File

@ -14,6 +14,11 @@ public class CombatZones {
/// </summary>
public static readonly string ShipCombatZone = "Ship";
/// <summary>
/// Power combat zones, new in Ascendancy update.
/// </summary>
public static readonly string PowerCombatZone = "Power";
/// <summary>
/// AX combat zone
/// </summary>

View File

@ -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) },

View File

@ -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";

View File

@ -0,0 +1,32 @@
using System.Reflection;
namespace EDPlayerJournal.Entries {
public class PowerplayEntry : Entry {
/// <summary>
/// Name of the power
/// </summary>
public string Power { get; set; } = string.Empty;
/// <summary>
/// Player rank
/// </summary>
public int Rank { get; set; } = 0;
/// <summary>
/// Current merits of the player
/// </summary>
public long Merits { get; set; } = 0;
/// <summary>
/// Time pledged (in seconds?)
/// </summary>
public long TimePledged { get; set; } = 0;
protected override void Initialise() {
Power = JSON.Value<string>("Power") ?? string.Empty;
Rank = JSON.Value<int?>("Rank") ?? 0;
Merits = JSON.Value<long?>("Merits") ?? 0;
TimePledged = JSON.Value<long?>("TimePledged") ?? 0;
}
}
}

View File

@ -0,0 +1,5 @@
namespace EDPlayerJournal.Entries {
public class SelfDestructEntry : Entry {
// Has no data
}
}

View File

@ -18,6 +18,11 @@ public class Instances {
/// </summary>
public static readonly string WarzoneHigh = "$Warzone_PointRace_High";
/// <summary>
/// Medium power play conflict zone, new in PP 2.0 Ascendancy update
/// </summary>
public static readonly string PowerWarzoneMedium = "$Warzone_Powerplay_Med";
/// <summary>
/// Low Thargoid combat zone
/// </summary>
@ -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) {

View File

@ -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.

View File

@ -21,6 +21,7 @@ public class DiscordLogGenerator {
new CargoSoldFormatter(),
new VistaGenomicsFormat(),
new SearchAndRescueFormat(),
new MeritsGainedFormat(),
};
protected virtual string GetToolVersion() {

View File

@ -1,86 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<OutputType>WinExe</OutputType>
<Version>0.2.6</Version>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>
<ImportWindowsDesktopTargets>true</ImportWindowsDesktopTargets>
</PropertyGroup>
<PropertyGroup>
<StartupObject>EliteBGSApplication</StartupObject>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Salus.ico</ApplicationIcon>
<Title>BGS reporting and logging tool for Elite:Dangerous</Title>
<Authors>nola</Authors>
<Copyright>Copyright 2019 by Florian Stinglmayr</Copyright>
<RepositoryUrl>https://git.aror.org/florian/EDBGS</RepositoryUrl>
<PackageTags>ED;Elite Dangerous;BGS</PackageTags>
<PackageProjectUrl>https://bgs.n0la.org</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<Resource Include="main-page.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Resource>
<None Update="README.md">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Update="docs\CHANGELOG.md">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<Resource Include="docs\main-page.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Resource>
</ItemGroup>
<ItemGroup>
<Content Include="LICENCE.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Resource Include="Salus.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="logo_v4.png">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Resource>
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\EliteBGS.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="EliteBGS.ico" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Extended.Wpf.Toolkit" Version="4.5.0" />
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.Windows.Compatibility" Version="7.0.0" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\EDPlayerJournal\EDPlayerJournal.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

View File

@ -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<MeritsGained>()
.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<MeritsGained>()
.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(';');
}
}

View File

@ -64,6 +64,7 @@ public class MissionFormat : LogFormatter {
Dictionary<string, ulong> passengers = new();
StringBuilder output = new StringBuilder();
long total_influence = 0;
bool hadmissions = false;
var missions = objective.EnabledOfType<MissionCompleted>();
var support = objective.EnabledOfType<InfluenceSupport>();
@ -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<MissionCompleted>()
.Count > 0
;
long influence = objective
.EnabledOfType<MissionCompleted>()
.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 "";
}

View File

@ -121,6 +121,7 @@
<Button Content="Ground" x:Name="Ground" Click="Ground_Click"/>
<Button Content="Ship" x:Name="Ship" Click="Ship_Click"/>
<Button Content="AX" x:Name="Thargoid" Click="Thargoid_Click"/>
<Button Content="Power" x:Name="Power" Click="Power_Click"/>
</StackPanel>
</Expander>
</StackPanel>
@ -261,6 +262,7 @@
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
@ -269,6 +271,7 @@
<mah:ToggleSwitch x:Name="NoInfluenceSupport" Grid.Row="0" Grid.ColumnSpan="2" Content="Ignore secondary influence support given out by certain missions" Toggled="NoInfluenceSupport_Toggled"/>
<mah:ToggleSwitch x:Name="NoMarketBuy" Grid.Row="1" Grid.ColumnSpan="2" Content="Ignore commodities bought at stations" Toggled="NoMarketBuy_Toggled"/>
<mah:ToggleSwitch x:Name="NoFleetCarrier" Grid.Row="2" Grid.ColumnSpan="2" Content="Ignore transactions done on a Fleet Carrier" Toggled="NoFleetCarrier_Toggled" />
<mah:ToggleSwitch x:Name="NoPowerplay" Grid.Row="3" Grid.ColumnSpan="2" Content="Ignore Powerplay Merits" Toggled="NoPowerplay_Toggled" />
</Grid>
</GroupBox>
<GroupBox Header="Discord Webhooks" Grid.Row="3" VerticalAlignment="Top" Width="Auto" Grid.ColumnSpan="3" Margin="0,5,0,0">

View File

@ -64,6 +64,7 @@ public partial class MainWindow : MetroWindow {
this.NoInfluenceSupport.IsOn = Config.Global.IgnoreInfluenceSupport;
this.NoMarketBuy.IsOn = Config.Global.IgnoreMarketBuy;
this.NoFleetCarrier.IsOn = Config.Global.IgnoreFleetCarrier;
this.NoPowerplay.IsOn = Config.Global.IgnorePowerplay;
// Apply theme
try {
@ -182,6 +183,7 @@ public partial class MainWindow : MetroWindow {
options.IgnoreInfluenceSupport = Config.Global.IgnoreInfluenceSupport;
options.IgnoreMarketBuy = Config.Global.IgnoreMarketBuy;
options.IgnoreFleetCarrierFaction = Config.Global.IgnoreFleetCarrier;
options.IgnorePowerplay = Config.Global.IgnorePowerplay;
List<Transaction> transactions = parser.Parse(entries, options);
@ -476,6 +478,16 @@ public partial class MainWindow : MetroWindow {
RefreshView();
}
private void Power_Click(object sender, RoutedEventArgs e) {
CombatZone transaction = GetTransaction<CombatZone>(sender);
if (transaction == null) {
return;
}
transaction.Type = CombatZones.PowerCombatZone;
RefreshView();
}
private void Thargoid_Click(object sender, RoutedEventArgs e) {
CombatZone transaction = GetTransaction<CombatZone>(sender);
if (transaction == null) {
@ -573,6 +585,10 @@ public partial class MainWindow : MetroWindow {
Config.Global.IgnoreFleetCarrier = this.NoFleetCarrier.IsOn;
}
private void NoPowerplay_Toggled(object sender, RoutedEventArgs e) {
Config.Global.IgnorePowerplay = this.NoPowerplay.IsOn;
}
private void OpenInExplorer_Click(object sender, RoutedEventArgs e) {
try {
Process.Start(new ProcessStartInfo(Config.Global.JournalLocation) {

View File

@ -59,7 +59,9 @@ public class UITransaction : INotifyPropertyChanged {
return Visibility.Hidden;
}
if (string.Compare(combat.Type, CombatZones.ShipCombatZone) == 0) {
// Both normal and power combat zones have special objectives
if (string.Compare(combat.Type, CombatZones.ShipCombatZone) == 0 ||
string.Compare(combat.Type, CombatZones.PowerCombatZone) == 0) {
return Visibility.Visible;
}

View File

@ -67,6 +67,11 @@ public class AppConfig {
/// </summary>
public bool IgnoreFleetCarrier { get; set; } = true;
/// <summary>
/// Ignore power play?
/// </summary>
public bool IgnorePowerplay { get; set; } = true;
/// <summary>
/// List of Webhooks configured
/// </summary>