diff --git a/EDPlayerJournal/BGS/TransactionParser.cs b/EDPlayerJournal/BGS/TransactionParser.cs index 91101d7..a5466d8 100644 --- a/EDPlayerJournal/BGS/TransactionParser.cs +++ b/EDPlayerJournal/BGS/TransactionParser.cs @@ -16,242 +16,12 @@ public class TransactionParserOptions { public bool IgnoreInfluenceSupport { get; set; } = false; } -internal class TransactionParserContext { - public string? CurrentSystem { get; set; } - public ulong? CurrentSystemAddress { get; set; } - 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; } - - public bool HaveSeenCapShip { get; set; } = false; - public bool HaveSeenCaptain { get; set; } = false; - public bool HaveSeenSpecOps { get; set; } = false; - public bool HaveSeenCorrespondent { get; set; } = false; - - /// - /// Returns true if the current session is legacy - /// - public bool IsLegacy { get; set; } = false; - - /// - /// How many on foot kills were done. - /// - public ulong OnFootKills { get; set; } = 0; - - /// - /// How many ship kills were done. - /// - public ulong ShipKills { get; set; } = 0; - - /// - /// Thargoid scouts killed - /// - public ulong ThargoidScoutKills { get; set; } = 0; - - /// - /// Thargoid interceptor kills - /// - public ulong ThargoidInterceptorKills { get; set; } = 0; - - /// - /// Whether we have seen an AX warzone NPC talk to us with ReceiveText - /// - public bool HaveSeenAXWarzoneNPC { get; set; } = false; - - /// - /// A list of accepted missions index by their mission ID - /// - public Dictionary AcceptedMissions { get; } = new(); - public Dictionary AcceptedMissionLocation { get; } = new(); - /// - /// A way to lookup a system by its system id - /// - public Dictionary SystemsByID { get; } = new(); - /// - /// A list of factions present in the given star system - /// - public Dictionary> SystemFactions { get; } = new(); - /// - /// To which faction a given named NPC belonged to. - /// - public Dictionary NPCFaction { get; } = new(); - /// - /// Buy costs for a given commodity - /// - public Dictionary BuyCost = new(); - - 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) { - return; - } - - if (OnFootKills > 0 || IsOnFoot == true) { - cztype = CombatZones.GroundCombatZone; - // High on foot combat zones have enforcers that bring 80k a pop - if (highest >= 60000) { - grade = CombatZones.DifficultyHigh; - } else if (highest >= 30000) { - // In medium conflict zones, the enforcers are worth 30k - grade = CombatZones.DifficultyMedium; - } else { - grade = CombatZones.DifficultyLow; - } - } else if (ShipKills > 0 && !IsOnFoot) { - // Ship combat zones can be identified by the amount of kills - if (ShipKills > 20) { - grade = CombatZones.DifficultyHigh; - } else if (ShipKills > 10) { - grade = CombatZones.DifficultyMedium; - } - - // Cap ship, means a high conflict zone - if (HaveSeenCapShip) { - grade = CombatZones.DifficultyHigh; - } else { - int warzoneNpcs = new List() { HaveSeenCaptain, HaveSeenCorrespondent, HaveSeenSpecOps } - .Where(x => x == true) - .Count() - ; - - if (warzoneNpcs >= 1 && grade == CombatZones.DifficultyLow) { - grade = CombatZones.DifficultyMedium; - } - } - cztype = CombatZones.ShipCombatZone; - } else if ((ThargoidScoutKills > 0 && ThargoidInterceptorKills > 0) || - HaveSeenAXWarzoneNPC == true) { - // Could be a thargoid combat zones if interceptors and scouts are there - cztype = CombatZones.AXCombatZone; - // Still unknown - grade = null; - } else { - transactions.AddIncomplete(new CombatZone(), "Failed to discern combat zone type", e); - return; - } - - CombatZone zone = new CombatZone() { - System = CurrentSystem, - Faction = faction, - IsLegacy = IsLegacy, - Grade = grade, - Type = cztype, - // Sad truth is, if HaveSeenXXX is false, we just don't know for certain - CapitalShip = HaveSeenCapShip ? true : null, - SpecOps = HaveSeenSpecOps ? true : null, - Correspondent = HaveSeenCorrespondent ? true : null, - Captain = HaveSeenCaptain ? true : null, - }; - zone.Entries.Add(e); - 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; - HaveSeenCapShip = false; - HaveSeenCaptain = false; - HaveSeenCorrespondent = false; - HaveSeenSpecOps = false; - LastRecordedAwardingFaction = null; - OnFootKills = 0; - ShipKills = 0; - ThargoidInterceptorKills = 0; - ThargoidScoutKills = 0; - HaveSeenAXWarzoneNPC = false; - } - - public void BoughtCargo(string? cargo, long? cost) { - if (cargo == null || cost == null) { - return; - } - - BuyCost[cargo] = cost.Value; - } - - public List? GetFactions(string? system) { - if (system == null || !SystemFactions.ContainsKey(system)) { - return null; - } - - return SystemFactions[system]; - } - - public void MissionAccepted(MissionAcceptedEntry? entry) { - if (entry == null) { - return; - } - - MissionAccepted(entry.Mission); - } - - public void MissionAccepted(Mission? mission) { - if (CurrentSystem == null || CurrentSystemAddress == null) { - throw new Exception("Mission accepted without knowing where."); - } - - if (mission == null) { - throw new Exception("Mission is null"); - } - - AcceptedMissions.TryAdd(mission.MissionID, mission); - - Location location = new() { - StarSystem = CurrentSystem, - SystemAddress = CurrentSystemAddress.Value, - Station = (CurrentStation ?? ""), - }; - - AcceptedMissionLocation.TryAdd(mission.MissionID, location); - } -} - public class TransactionList : List { public void AddIncomplete(Transaction underlying, string reason, Entry entry) { Add(new IncompleteTransaction(underlying, reason, entry)); } } -internal interface TransactionParserPart{ - /// - /// Parse a given entry of the entry type specified when declaring to implement this - /// interface. You must add your transaction to the passed list in case you did have - /// enough information to parse one or more. You may update the parser context - /// with new information in case the entry yielded new information. - /// Throw an exception if there was an error or a malformed entry. If you have an - /// incomplete entry, i.e. not enough information to complete one, add an - /// IncompleteTransaction to the list - /// - /// The entry to parse - /// Parsing context that may contain useful information - /// List of parsed transactions - public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions); -} - /// /// The location parser only updates the context with useful information, and does not /// by itself generate any transactions. Location is the best information gatherer here diff --git a/EDPlayerJournal/BGS/TransactionParserContext.cs b/EDPlayerJournal/BGS/TransactionParserContext.cs new file mode 100644 index 0000000..6c8c0f6 --- /dev/null +++ b/EDPlayerJournal/BGS/TransactionParserContext.cs @@ -0,0 +1,246 @@ +using EDPlayerJournal.Entries; + +namespace EDPlayerJournal.BGS; + +internal class TransactionParserContext { + /// + /// Name of the current system the player is in. + /// + public string? CurrentSystem { get; set; } + + /// + /// 64 bit address of the current system. + /// + public ulong? CurrentSystemAddress { get; set; } + + /// + /// Controlling faction of the current system. + /// + public string? ControllingFaction { get; set; } + + /// + /// Name of the current station the player is docked at. + /// + public string? CurrentStation { get; set; } + + /// + /// Faction that owns the current station. + /// + public string? StationOwner { get; set; } + + /// + /// Whether the player is currently on foot, or in an SRV/ship. + /// + public bool IsOnFoot { get; set; } = false; + + /// + /// Last faction that awarded the player with combat bonds. + /// + public string? LastRecordedAwardingFaction { get; set; } + + /// + /// Highest combat bond seen so far. + /// + public ulong? HighestCombatBond { get; set; } + + public bool HaveSeenCapShip { get; set; } = false; + public bool HaveSeenCaptain { get; set; } = false; + public bool HaveSeenSpecOps { get; set; } = false; + public bool HaveSeenCorrespondent { get; set; } = false; + + /// + /// Returns true if the current session is legacy + /// + public bool IsLegacy { get; set; } = false; + + /// + /// How many on foot kills were done. + /// + public ulong OnFootKills { get; set; } = 0; + + /// + /// How many ship kills were done. + /// + public ulong ShipKills { get; set; } = 0; + + /// + /// Thargoid scouts killed + /// + public ulong ThargoidScoutKills { get; set; } = 0; + + /// + /// Thargoid interceptor kills + /// + public ulong ThargoidInterceptorKills { get; set; } = 0; + + /// + /// Whether we have seen an AX warzone NPC talk to us with ReceiveText + /// + public bool HaveSeenAXWarzoneNPC { get; set; } = false; + + /// + /// A list of accepted missions index by their mission ID + /// + public Dictionary AcceptedMissions { get; } = new(); + public Dictionary AcceptedMissionLocation { get; } = new(); + /// + /// A way to lookup a system by its system id + /// + public Dictionary SystemsByID { get; } = new(); + /// + /// A list of factions present in the given star system + /// + public Dictionary> SystemFactions { get; } = new(); + /// + /// To which faction a given named NPC belonged to. + /// + public Dictionary NPCFaction { get; } = new(); + /// + /// Buy costs for a given commodity + /// + public Dictionary BuyCost = new(); + + 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) { + return; + } + + if (OnFootKills > 0 || IsOnFoot == true) { + cztype = CombatZones.GroundCombatZone; + // High on foot combat zones have enforcers that bring 80k a pop + if (highest >= 60000) { + grade = CombatZones.DifficultyHigh; + } else if (highest >= 30000) { + // In medium conflict zones, the enforcers are worth 30k + grade = CombatZones.DifficultyMedium; + } else { + grade = CombatZones.DifficultyLow; + } + } else if (ShipKills > 0 && !IsOnFoot) { + // Ship combat zones can be identified by the amount of kills + if (ShipKills > 20) { + grade = CombatZones.DifficultyHigh; + } else if (ShipKills > 10) { + grade = CombatZones.DifficultyMedium; + } + + // Cap ship, means a high conflict zone + if (HaveSeenCapShip) { + grade = CombatZones.DifficultyHigh; + } else { + int warzoneNpcs = new List() { HaveSeenCaptain, HaveSeenCorrespondent, HaveSeenSpecOps } + .Where(x => x == true) + .Count() + ; + + if (warzoneNpcs >= 1 && grade == CombatZones.DifficultyLow) { + grade = CombatZones.DifficultyMedium; + } + } + cztype = CombatZones.ShipCombatZone; + } else if ((ThargoidScoutKills > 0 && ThargoidInterceptorKills > 0) || + HaveSeenAXWarzoneNPC == true) { + // Could be a thargoid combat zones if interceptors and scouts are there + cztype = CombatZones.AXCombatZone; + // Still unknown + grade = null; + } else { + transactions.AddIncomplete(new CombatZone(), "Failed to discern combat zone type", e); + return; + } + + CombatZone zone = new CombatZone() { + System = CurrentSystem, + Faction = faction, + IsLegacy = IsLegacy, + Grade = grade, + Type = cztype, + // Sad truth is, if HaveSeenXXX is false, we just don't know for certain + CapitalShip = HaveSeenCapShip ? true : null, + SpecOps = HaveSeenSpecOps ? true : null, + Correspondent = HaveSeenCorrespondent ? true : null, + Captain = HaveSeenCaptain ? true : null, + }; + zone.Entries.Add(e); + 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; + HaveSeenCapShip = false; + HaveSeenCaptain = false; + HaveSeenCorrespondent = false; + HaveSeenSpecOps = false; + LastRecordedAwardingFaction = null; + OnFootKills = 0; + ShipKills = 0; + ThargoidInterceptorKills = 0; + ThargoidScoutKills = 0; + HaveSeenAXWarzoneNPC = false; + } + + public void BoughtCargo(string? cargo, long? cost) { + if (cargo == null || cost == null) { + return; + } + + BuyCost[cargo] = cost.Value; + } + + public List? GetFactions(string? system) { + if (system == null || !SystemFactions.ContainsKey(system)) { + return null; + } + + return SystemFactions[system]; + } + + public void MissionAccepted(MissionAcceptedEntry? entry) { + if (entry == null) { + return; + } + + MissionAccepted(entry.Mission); + } + + public void MissionAccepted(Mission? mission) { + if (CurrentSystem == null || CurrentSystemAddress == null) { + throw new Exception("Mission accepted without knowing where."); + } + + if (mission == null) { + throw new Exception("Mission is null"); + } + + AcceptedMissions.TryAdd(mission.MissionID, mission); + + Location location = new() { + StarSystem = CurrentSystem, + SystemAddress = CurrentSystemAddress.Value, + Station = (CurrentStation ?? ""), + }; + + AcceptedMissionLocation.TryAdd(mission.MissionID, location); + } +} diff --git a/EDPlayerJournal/BGS/TransactionParserPart.cs b/EDPlayerJournal/BGS/TransactionParserPart.cs new file mode 100644 index 0000000..00ebecf --- /dev/null +++ b/EDPlayerJournal/BGS/TransactionParserPart.cs @@ -0,0 +1,19 @@ +using EDPlayerJournal.Entries; + +namespace EDPlayerJournal.BGS; + +internal interface TransactionParserPart { + /// + /// Parse a given entry of the entry type specified when declaring to implement this + /// interface. You must add your transaction to the passed list in case you did have + /// enough information to parse one or more. You may update the parser context + /// with new information in case the entry yielded new information. + /// Throw an exception if there was an error or a malformed entry. If you have an + /// incomplete entry, i.e. not enough information to complete one, add an + /// IncompleteTransaction to the list + /// + /// The entry to parse + /// Parsing context that may contain useful information + /// List of parsed transactions + public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions); +}