Compare commits

..

3 Commits

9 changed files with 341 additions and 2 deletions

View File

@ -1,6 +1,7 @@
using EDPlayerJournal.Entries;
using System.Collections.Generic;
using System.Reflection.Metadata.Ecma335;
using System.Transactions;
namespace EDPlayerJournal.BGS;
@ -10,6 +11,22 @@ internal class TransactionParserContext {
public string? CurrentStation { get; set; }
public string? ControllingFaction { get; set; }
public bool IsOnFoot { get; set; } = false;
public string? LastRecordedAwardingFaction { get; set; }
public ulong? HighestCombatBond { get; set; }
/// <summary>
/// How many on foot kills were done.
/// </summary>
public ulong OnFootKills { get; set; } = 0;
/// <summary>
/// How many ship kills were done.
/// </summary>
public ulong ShipKills { get; set; } = 0;
/// <summary>
/// A list of accepted missions index by their mission ID
/// </summary>
@ -32,6 +49,67 @@ internal class TransactionParserContext {
/// </summary>
public Dictionary<string, long> BuyCost = new();
public void DiscernCombatZone(TransactionList transactions) {
string grade = "Low";
string cztype;
ulong? highest = HighestCombatBond;
if (highest == null || LastRecordedAwardingFaction == null) {
return;
}
if (OnFootKills > 0) {
cztype = "OnFoot";
// High on foot combat zones have enforcers that bring 80k a pop
if (highest >= 80000) {
grade = "High";
} else if (highest >= 4000) {
grade = "Medium";
}
} else if (ShipKills > 0) {
// Ship combat zones can be identified by the amount of kills
if (ShipKills > 20) {
grade = "High";
} else if (ShipKills > 10) {
grade = "Medium";
}
cztype = "Ship";
} else {
transactions.AddIncomplete(new CombatZone(), "Failed to discern combat zone type");
return;
}
CombatZone zone = new CombatZone() {
System = CurrentSystem,
Faction = LastRecordedAwardingFaction,
Grade = grade,
Type = cztype,
Amount = 1,
};
transactions.Add(zone);
}
public void RecordCombatBond(FactionKillBondEntry e) {
if (HighestCombatBond == null || e.Reward > HighestCombatBond) {
HighestCombatBond = e.Reward;
}
LastRecordedAwardingFaction = e.AwardingFaction;
if (IsOnFoot) {
++OnFootKills;
} else {
++ShipKills;
}
}
public void ResetCombatZone() {
HighestCombatBond = null;
LastRecordedAwardingFaction = null;
OnFootKills = 0;
ShipKills = 0;
}
public void BoughtCargo(string? cargo, long? cost) {
if (cargo == null || cost == null) {
return;
@ -610,7 +688,36 @@ internal class FactionKillBondParser : TransactionParserPart {
Faction = Factions.PilotsFederation,
Station = context.CurrentStation,
});
// We are done
return;
}
context.RecordCombatBond(entry);
}
}
internal class EmbarkDisembarkParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
if (e.Is(Events.Embark)) {
context.IsOnFoot = false;
} else if (e.Is(Events.Disembark)) {
context.IsOnFoot = true;
}
}
}
internal class SupercruiseEntryParser : TransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
context.DiscernCombatZone(transactions);
context.ResetCombatZone();
}
}
internal class ShutdownParser : TransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
context.DiscernCombatZone(transactions);
context.ResetCombatZone();
}
}
@ -618,7 +725,9 @@ public class TransactionParser {
private static Dictionary<string, TransactionParserPart> ParserParts { get; } = new()
{
{ Events.CommitCrime, new CommitCrimeParser() },
{ Events.Disembark, new EmbarkDisembarkParser() },
{ Events.Docked, new DockedParser() },
{ Events.Embark, new EmbarkDisembarkParser() },
{ Events.FactionKillBond, new FactionKillBondParser() },
{ Events.FSDJump, new FSDJumpParser() },
{ Events.Location, new LocationParser() },
@ -634,6 +743,8 @@ public class TransactionParser {
{ Events.SellMicroResources, new SellMicroResourcesParser() },
{ Events.SellOrganicData, new SellOrganicDataParser() },
{ Events.ShipTargeted, new ShipTargetedParser() },
{ Events.Shutdown, new ShutdownParser() },
{ Events.SupercruiseEntry, new SupercruiseEntryParser() },
};
public List<Transaction>? Parse(IEnumerable<Entry> entries) {

View File

@ -0,0 +1,66 @@
namespace EDPlayerJournal.Entries;
public class DisembarkEntry : Entry {
/// <summary>
/// Disembarked into an SRV?
/// </summary>
public bool SRV { get; set; } = false;
/// <summary>
/// Taxi?
/// </summary>
public bool Taxi { get; set; } = false;
/// <summary>
/// Multicrew or not.
/// </summary>
public bool Multicrew { get; set; } = false;
/// <summary>
/// Player's ship ID
/// </summary>
public ulong? ID { get; set; }
/// <summary>
/// Star system
/// </summary>
public string? StarSystem { get; set; }
/// <summary>
/// System address
/// </summary>
public ulong? SystemAddress { get; set; }
/// <summary>
/// Body, name e.g. HIP 6182 B 2 a
/// </summary>
public string? Body { get; set; }
/// <summary>
/// Body ID
/// </summary>
public ulong? BodyID { get; set; }
/// <summary>
/// Disembarked on a station?
/// </summary>
public bool OnStation { get; set; } = false;
/// <summary>
/// Disembarked on a planet?
/// </summary>
public bool OnPlanet { get; set; } = false;
protected override void Initialise() {
SRV = JSON.Value<bool?>("SRV") ?? false;
Taxi = JSON.Value<bool?>("Taxi") ?? false;
Multicrew = JSON.Value<bool?>("Multicrew") ?? false;
ID = JSON.Value<ulong?>("ID");
StarSystem = JSON.Value<string?>("StarSystem");
SystemAddress = JSON.Value<ulong?>("SystemAddress");
Body = JSON.Value<string?>("Body");
BodyID = JSON.Value<ulong?>("BodyID");
OnStation = JSON.Value<bool?>("OnStation") ?? false;
OnPlanet = JSON.Value<bool?>("OnPlanet") ?? false;
}
}

View File

@ -0,0 +1,66 @@
namespace EDPlayerJournal.Entries;
public class EmbarkEntry : Entry {
/// <summary>
/// Disembarked into an SRV?
/// </summary>
public bool SRV { get; set; } = false;
/// <summary>
/// Taxi?
/// </summary>
public bool Taxi { get; set; } = false;
/// <summary>
/// Multicrew or not.
/// </summary>
public bool Multicrew { get; set; } = false;
/// <summary>
/// Player's ship ID
/// </summary>
public ulong? ID { get; set; }
/// <summary>
/// Star system
/// </summary>
public string? StarSystem { get; set; }
/// <summary>
/// System address
/// </summary>
public ulong? SystemAddress { get; set; }
/// <summary>
/// Body, name e.g. HIP 6182 B 2 a
/// </summary>
public string? Body { get; set; }
/// <summary>
/// Body ID
/// </summary>
public ulong? BodyID { get; set; }
/// <summary>
/// Disembarked on a station?
/// </summary>
public bool OnStation { get; set; } = false;
/// <summary>
/// Disembarked on a planet?
/// </summary>
public bool OnPlanet { get; set; } = false;
protected override void Initialise() {
SRV = JSON.Value<bool?>("SRV") ?? false;
Taxi = JSON.Value<bool?>("Taxi") ?? false;
Multicrew = JSON.Value<bool?>("Multicrew") ?? false;
ID = JSON.Value<ulong?>("ID");
StarSystem = JSON.Value<string?>("StarSystem");
SystemAddress = JSON.Value<ulong?>("SystemAddress");
Body = JSON.Value<string?>("Body");
BodyID = JSON.Value<ulong?>("BodyID");
OnStation = JSON.Value<bool?>("OnStation") ?? false;
OnPlanet = JSON.Value<bool?>("OnPlanet") ?? false;
}
}

View File

@ -16,9 +16,11 @@ public class Entry {
{ Events.Commander, typeof(CommanderEntry) },
{ Events.CommitCrime, typeof(CommitCrimeEntry) },
{ Events.Died, typeof(DiedEntry) },
{ Events.Disembark, typeof(DisembarkEntry) },
{ Events.Docked, typeof(DockedEntry) },
{ Events.FileHeader, typeof(FileHeaderEntry) },
{ Events.Embark, typeof(EmbarkEntry) },
{ Events.FactionKillBond, typeof(FactionKillBondEntry) },
{ Events.FileHeader, typeof(FileHeaderEntry) },
{ Events.FSDJump, typeof(FSDJumpEntry) },
{ Events.HullDamage, typeof(HullDamageEntry) },
{ Events.LoadGame, typeof(LoadGameEntry) },
@ -39,6 +41,8 @@ public class Entry {
{ Events.SellOrganicData, typeof(SellOrganicDataEntry) },
{ Events.ShieldState, typeof(ShieldStateEntry) },
{ Events.ShipTargeted, typeof(ShipTargetedEntry) },
{ Events.SupercruiseEntry, typeof(SupercruiseEntryEntry) },
{ Events.SupercruiseExit, typeof(SupercruiseExitEntry) },
{ Events.UnderAttack, typeof(UnderAttackEntry) },
};

View File

@ -5,7 +5,9 @@ public class Events {
public static readonly string Commander = "Commander";
public static readonly string CommitCrime = "CommitCrime";
public static readonly string Died = "Died";
public static readonly string Disembark = "Disembark";
public static readonly string Docked = "Docked";
public static readonly string Embark = "Embark";
public static readonly string FactionKillBond = "FactionKillBond";
public static readonly string FighterDestroyed = "FighterDestroyed";
public static readonly string FileHeader = "Fileheader";
@ -29,5 +31,8 @@ public class Events {
public static readonly string SellOrganicData = "SellOrganicData";
public static readonly string ShieldState = "ShieldState";
public static readonly string ShipTargeted = "ShipTargeted";
public static readonly string Shutdown = "Shutdown";
public static readonly string SupercruiseEntry = "SupercruiseEntry";
public static readonly string SupercruiseExit = "SupercruiseExit";
public static readonly string UnderAttack = "UnderAttack";
}

View File

@ -0,0 +1,30 @@
namespace EDPlayerJournal.Entries;
public class SupercruiseEntryEntry : Entry {
/// <summary>
/// Taxi?
/// </summary>
public bool Taxi { get; set; } = false;
/// <summary>
/// Multicrew or not.
/// </summary>
public bool Multicrew { get; set; } = false;
/// <summary>
/// Star system
/// </summary>
public string? StarSystem { get; set; }
/// <summary>
/// System address
/// </summary>
public ulong? SystemAddress { get; set; }
protected override void Initialise() {
Taxi = JSON.Value<bool?>("Taxi") ?? false;
Multicrew = JSON.Value<bool?>("Multicrew") ?? false;
StarSystem = JSON.Value<string?>("StarSystem");
SystemAddress = JSON.Value<ulong?>("SystemAddress");
}
}

View File

@ -0,0 +1,48 @@
namespace EDPlayerJournal.Entries;
public class SupercruiseExitEntry : Entry {
/// <summary>
/// Taxi?
/// </summary>
public bool Taxi { get; set; } = false;
/// <summary>
/// Multicrew or not.
/// </summary>
public bool Multicrew { get; set; } = false;
/// <summary>
/// Star system
/// </summary>
public string? StarSystem { get; set; }
/// <summary>
/// System address
/// </summary>
public ulong? SystemAddress { get; set; }
/// <summary>
/// Body, name e.g. HIP 6182 B 2 a
/// </summary>
public string? Body { get; set; }
/// <summary>
/// Body ID
/// </summary>
public ulong? BodyID { get; set; }
/// <summary>
/// Body type (star, planet etc.)
/// </summary>
public string? BodyType { get; set; }
protected override void Initialise() {
Taxi = JSON.Value<bool?>("Taxi") ?? false;
Multicrew = JSON.Value<bool?>("Multicrew") ?? false;
StarSystem = JSON.Value<string?>("StarSystem");
SystemAddress = JSON.Value<ulong?>("SystemAddress");
Body = JSON.Value<string?>("Body");
BodyType = JSON.Value<string?>("BodyType");
BodyID = JSON.Value<ulong?>("BodyID");
}
}

View File

@ -24,6 +24,13 @@ missions. Currently the tool recognises the following completed tasks:
* Selling of micro resources (Odyssey only)
* Selling of organic data (Odyssey only)
* Vouchers, including bounty vouchers, combat bonds, and settlement vouchers (aka intel packages)
* Thargoid kills
* Combat zones (experimental)
Combat zone detection is highly wonky at this time. There is no direct event for detecting
combat zones, and so the tool makes a few assumptions and goes from there. If you disembark
it will assume on foot combat zones, and if you don't, it will assume a ship CZ. This
detection can, and will be wrong, so caution is advised.
Vouchers help the faction that is listed for them. If said faction is not present in the
current system, then there is no BGS impact. So the tool looks for all system factions, and

View File

@ -31,10 +31,12 @@ You can then select which of the two actions goes into the final log.
* Selling of micro resources (Odyssey only)
* Selling of organic data (Odyssey only)
* Vouchers, including bounty vouchers, combat bonds, and settlement vouchers (aka intel packages)
* Thargoid kills
* Combat zones (experimental feature)
### What it does not detect:
* Combat zone wins, and its objectives
* Combat zone objectives
* Megaship scenarios
* On foot missions accepted by NPCs in stations (pre Update 13)
* Murders of NPCs you haven't fully scanned