Compare commits

..

No commits in common. "d7dc9bd904105e244b4162185b38b2d569013a0e" and "29dc6e9cba311b984aceec3b76994771cf8d4e64" have entirely different histories.

64 changed files with 526 additions and 1711 deletions

View File

@ -1,11 +1,10 @@
using System.Text; using System.Linq;
using System.Text;
using EDPlayerJournal.Entries; using EDPlayerJournal.Entries;
namespace EDPlayerJournal.BGS; namespace EDPlayerJournal.BGS;
public class Cartographics : Transaction { public class Cartographics : Transaction {
public Cartographics() { }
public Cartographics(MultiSellExplorationDataEntry e) { public Cartographics(MultiSellExplorationDataEntry e) {
Entries.Add(e); Entries.Add(e);
} }

View File

@ -4,8 +4,6 @@ using EDPlayerJournal.Entries;
namespace EDPlayerJournal.BGS; namespace EDPlayerJournal.BGS;
public class OrganicData : Transaction { public class OrganicData : Transaction {
public OrganicData() { }
public OrganicData(SellOrganicDataEntry e) { public OrganicData(SellOrganicDataEntry e) {
Entries.Add(e); Entries.Add(e);
} }
@ -38,8 +36,6 @@ public class OrganicData : Transaction {
/* Selling organic data only helps the controlling faction, just like /* Selling organic data only helps the controlling faction, just like
* selling cartographic data. * selling cartographic data.
*
* Right now: Organic data helps no one.
*/ */
public override bool OnlyControllingFaction => true; public override bool OnlyControllingFaction => true;
} }

View File

@ -1,44 +0,0 @@
using EDPlayerJournal.Entries;
namespace EDPlayerJournal.BGS;
internal class MarketBuyParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
MarketBuyEntry? entry = e as MarketBuyEntry;
if (entry == null) {
throw new NotImplementedException();
}
context.BoughtCargo(entry.Type, entry.BuyPrice);
// We still want the information on buy price for profit,
// but if the option is on, we don't care for parsing it
// further.
// TODO: might be wise to split this parser into two; one for
// determining profit, the other for the BGS information
if (options.IgnoreMarketBuy) {
return;
}
if (context.StationOwner == null) {
transactions.AddIncomplete(
new BuyCargo(),
"Could not discern the station owner for market buy.",
e);
return;
}
// Ignore if its a fleet carrier faction.
if (string.Compare(context.StationOwner, Factions.FleetCarrier, StringComparison.OrdinalIgnoreCase) == 0 &&
options.IgnoreFleetCarrierFaction) {
return;
}
transactions.Add(new BuyCargo(entry) {
System = context.CurrentSystem,
Station = context.CurrentStation,
Faction = context.StationOwner,
IsLegacy = context.IsLegacy,
});
}
}

View File

@ -1,45 +0,0 @@
using EDPlayerJournal.Entries;
namespace EDPlayerJournal.BGS;
internal class MarketSellParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
long profit = 0;
if (context.StationOwner == null) {
transactions.AddIncomplete(
new SellCargo(),
"Could not discern the station owner market sell.",
e);
return;
}
// Ignore if its a fleet carrier faction.
if (string.Compare(context.StationOwner, Factions.FleetCarrier, StringComparison.OrdinalIgnoreCase) == 0 &&
options.IgnoreFleetCarrierFaction) {
return;
}
MarketSellEntry? entry = e as MarketSellEntry;
if (entry == null) {
throw new NotImplementedException();
}
if (entry.Type == null) {
throw new InvalidJournalEntryException("market sell contains no cargo type");
}
if (context.BuyCost.ContainsKey(entry.Type)) {
long avg = context.BuyCost[entry.Type];
profit = (long)entry.TotalSale - (avg * entry.Count);
}
transactions.Add(new SellCargo(entry) {
System = context.CurrentSystem,
Station = context.CurrentStation,
Faction = context.StationOwner,
Profit = profit,
IsLegacy = context.IsLegacy,
});
}
}

View File

@ -1,33 +0,0 @@
using EDPlayerJournal.Entries;
namespace EDPlayerJournal.BGS;
internal class MultiSellExplorationDataParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
MultiSellExplorationDataEntry? entry = e as MultiSellExplorationDataEntry;
if (entry == null) {
throw new NotImplementedException();
}
if (context.StationOwner == null) {
transactions.AddIncomplete(
new Cartographics(),
"Could not discern the station owner for cartographics sell.",
e);
return;
}
// Ignore if its a fleet carrier faction.
if (string.Compare(context.StationOwner, Factions.FleetCarrier, StringComparison.OrdinalIgnoreCase) == 0 &&
options.IgnoreFleetCarrierFaction) {
return;
}
transactions.Add(new Cartographics(entry) {
System = context.CurrentSystem,
Station = context.CurrentStation,
Faction = context.StationOwner,
IsLegacy = context.IsLegacy,
});
}
}

View File

@ -1,33 +0,0 @@
using EDPlayerJournal.Entries;
namespace EDPlayerJournal.BGS;
internal class SellExplorationDataParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
SellExplorationDataEntry? entry = e as SellExplorationDataEntry;
if (entry == null) {
throw new NotImplementedException();
}
if (context.StationOwner == null) {
transactions.AddIncomplete(
new Cartographics(),
"Could not discern the station owner for cartographics sell.",
e);
return;
}
// Ignore if its a fleet carrier faction.
if (string.Compare(context.StationOwner, Factions.FleetCarrier, StringComparison.OrdinalIgnoreCase) == 0 &&
options.IgnoreFleetCarrierFaction) {
return;
}
transactions.Add(new Cartographics(entry) {
System = context.CurrentSystem,
Station = context.CurrentStation,
Faction = context.StationOwner,
IsLegacy = context.IsLegacy,
});
}
}

View File

@ -1,38 +0,0 @@
using EDPlayerJournal.Entries;
namespace EDPlayerJournal.BGS;
internal class SellOrganicDataParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
// If exo biology is ignored, simply do nothing
if (options.IgnoreExoBiology) {
return;
}
SellOrganicDataEntry? entry = e as SellOrganicDataEntry;
if (entry == null) {
throw new NotImplementedException();
}
if (context.StationOwner == null) {
transactions.AddIncomplete(
new OrganicData(),
"Could not discern the station owner for organic data sell.",
e);
return;
}
// Ignore if its a fleet carrier faction.
if (string.Compare(context.StationOwner, Factions.FleetCarrier, StringComparison.OrdinalIgnoreCase) == 0 &&
options.IgnoreFleetCarrierFaction) {
return;
}
transactions.Add(new OrganicData(entry) {
System = context.CurrentSystem,
Station = context.CurrentStation,
Faction = context.StationOwner,
IsLegacy = context.IsLegacy,
});
}
}

View File

@ -1,14 +0,0 @@
using EDPlayerJournal.Entries;
namespace EDPlayerJournal.BGS.Parsers;
internal class SupercruiseDestinationDropParser : ITransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
SupercruiseDestinationDropEntry? drop = entry as SupercruiseDestinationDropEntry;
if (drop == null || drop.Type == null) {
return;
}
context.CurrentInstanceType = drop.Type;
}
}

View File

@ -1,33 +1,220 @@
using EDPlayerJournal.BGS.Parsers; using EDPlayerJournal;
using EDPlayerJournal.Entries; using EDPlayerJournal.Entries;
namespace EDPlayerJournal.BGS; namespace EDPlayerJournal.BGS;
public class TransactionParserOptions { internal class TransactionParserContext {
/// <summary> public string? CurrentSystem { get; set; }
/// Whether to ignore exo biology. It does not contribute to BGS, so this public ulong? CurrentSystemAddress { get; set; }
/// is true per default. public string? CurrentStation { get; set; }
/// </summary> public string? ControllingFaction { get; set; }
public bool IgnoreExoBiology { get; set; } = true;
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;
/// <summary> /// <summary>
/// Whether to ignore influence support. Usually one only cares about the /// Returns true if the current session is legacy
/// primary faction for the influence.
/// </summary> /// </summary>
public bool IgnoreInfluenceSupport { get; set; } = false; public bool IsLegacy { get; set; } = false;
/// <summary> /// <summary>
/// Whether to ignore market buy. Buying from a market gives a small amount /// How many on foot kills were done.
/// of INF if it is sold to a high demand market, but generally one buys from
/// a market to aid the faction the stuff is being sold to. So allow it to be
/// disabled.
/// </summary> /// </summary>
public bool IgnoreMarketBuy { get; set; } = false; public ulong OnFootKills { get; set; } = 0;
/// <summary> /// <summary>
/// Whether we should ignore things done for the fleet carrier faction. /// How many ship kills were done.
/// </summary> /// </summary>
public bool IgnoreFleetCarrierFaction { get; set; } = true; public ulong ShipKills { get; set; } = 0;
/// <summary>
/// Thargoid scouts killed
/// </summary>
public ulong ThargoidScoutKills { get; set; } = 0;
/// <summary>
/// Thargoid interceptor kills
/// </summary>
public ulong ThargoidInterceptorKills { get; set; } = 0;
/// <summary>
/// Whether we have seen an AX warzone NPC talk to us with ReceiveText
/// </summary>
public bool HaveSeenAXWarzoneNPC { get; set; } = false;
/// <summary>
/// A list of accepted missions index by their mission ID
/// </summary>
public Dictionary<ulong, Mission> AcceptedMissions { get; } = new();
public Dictionary<ulong, Location> AcceptedMissionLocation { get; } = new();
/// <summary>
/// A way to lookup a system by its system id
/// </summary>
public Dictionary<ulong, string> SystemsByID { get; } = new();
/// <summary>
/// A list of factions present in the given star system
/// </summary>
public Dictionary<string, List<Faction>> SystemFactions { get; } = new();
/// <summary>
/// To which faction a given named NPC belonged to.
/// </summary>
public Dictionary<string, string> NPCFaction { get; } = new();
/// <summary>
/// Buy costs for a given commodity
/// </summary>
public Dictionary<string, long> 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<bool>() { 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<Faction>? 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<Transaction> { public class TransactionList : List<Transaction> {
@ -36,13 +223,29 @@ public class TransactionList : List<Transaction> {
} }
} }
internal interface TransactionParserPart{
/// <summary>
/// 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
/// </summary>
/// <param name="entry">The entry to parse</param>
/// <param name="context">Parsing context that may contain useful information</param>
/// <param name="transactions">List of parsed transactions</param>
public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions);
}
/// <summary> /// <summary>
/// The location parser only updates the context with useful information, and does not /// 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 /// by itself generate any transactions. Location is the best information gatherer here
/// as we are getting controlling faction, system factions, address and station name. /// as we are getting controlling faction, system factions, address and station name.
/// </summary> /// </summary>
internal class LocationParser : ITransactionParserPart { internal class LocationParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
LocationEntry? entry = e as LocationEntry; LocationEntry? entry = e as LocationEntry;
if (entry == null) { if (entry == null) {
throw new NotImplementedException(); throw new NotImplementedException();
@ -65,20 +268,15 @@ internal class LocationParser : ITransactionParserPart {
context.CurrentStation = entry.StationName; context.CurrentStation = entry.StationName;
} }
if (!string.IsNullOrEmpty(entry.StationFaction)) { if (!context.SystemFactions.ContainsKey(entry.StarSystem) &&
context.StationOwner = entry.StationFaction; entry.SystemFactions != null && entry.SystemFactions.Count > 0) {
} else {
context.StationOwner = null;
}
if (entry.SystemFactions != null && entry.SystemFactions.Count > 0) {
context.SystemFactions[entry.StarSystem] = entry.SystemFactions; context.SystemFactions[entry.StarSystem] = entry.SystemFactions;
} }
} }
} }
internal class FSDJumpParser : ITransactionParserPart { internal class FSDJumpParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
FSDJumpEntry? entry = e as FSDJumpEntry; FSDJumpEntry? entry = e as FSDJumpEntry;
if (entry == null) { if (entry == null) {
throw new NotImplementedException(); throw new NotImplementedException();
@ -94,8 +292,6 @@ internal class FSDJumpParser : ITransactionParserPart {
context.DiscernCombatZone(transactions, e); context.DiscernCombatZone(transactions, e);
context.ResetCombatZone(); context.ResetCombatZone();
context.LeftInstance();
context.CurrentSystem = entry.StarSystem; context.CurrentSystem = entry.StarSystem;
context.CurrentSystemAddress = entry.SystemAddress; context.CurrentSystemAddress = entry.SystemAddress;
@ -105,14 +301,15 @@ internal class FSDJumpParser : ITransactionParserPart {
context.ControllingFaction = entry.SystemFaction; context.ControllingFaction = entry.SystemFaction;
} }
if (entry.SystemFactions != null && entry.SystemFactions.Count > 0) { if (!context.SystemFactions.ContainsKey(entry.StarSystem) &&
entry.SystemFactions != null && entry.SystemFactions.Count > 0) {
context.SystemFactions[entry.StarSystem] = entry.SystemFactions; context.SystemFactions[entry.StarSystem] = entry.SystemFactions;
} }
} }
} }
internal class DockedParser : ITransactionParserPart { internal class DockedParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
DockedEntry? entry = e as DockedEntry; DockedEntry? entry = e as DockedEntry;
if (entry == null) { if (entry == null) {
throw new NotImplementedException(); throw new NotImplementedException();
@ -128,7 +325,7 @@ internal class DockedParser : ITransactionParserPart {
context.SystemsByID.TryAdd(entry.SystemAddress.Value, entry.StarSystem); context.SystemsByID.TryAdd(entry.SystemAddress.Value, entry.StarSystem);
if (!string.IsNullOrEmpty(entry.StationFaction)) { if (!string.IsNullOrEmpty(entry.StationFaction)) {
context.StationOwner = entry.StationFaction; context.ControllingFaction = entry.StationFaction;
} }
if (!string.IsNullOrEmpty(entry.StationName)) { if (!string.IsNullOrEmpty(entry.StationName)) {
@ -141,8 +338,8 @@ internal class DockedParser : ITransactionParserPart {
/// With ship targeted we might find out to which faction a given NPC belonged. This is /// With ship targeted we might find out to which faction a given NPC belonged. This is
/// useful later when said NPC gets killed or murdered. /// useful later when said NPC gets killed or murdered.
/// </summary> /// </summary>
internal class ShipTargetedParser : ITransactionParserPart { internal class ShipTargetedParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
ShipTargetedEntry? entry = e as ShipTargetedEntry; ShipTargetedEntry? entry = e as ShipTargetedEntry;
if (entry == null) { if (entry == null) {
throw new NotImplementedException(); throw new NotImplementedException();
@ -176,8 +373,8 @@ internal class ShipTargetedParser : ITransactionParserPart {
/// Commit crime can result in a transaction, especially if the crime committed is /// Commit crime can result in a transaction, especially if the crime committed is
/// murder. /// murder.
/// </summary> /// </summary>
internal class CommitCrimeParser : ITransactionParserPart { internal class CommitCrimeParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
CommitCrimeEntry? entry = e as CommitCrimeEntry; CommitCrimeEntry? entry = e as CommitCrimeEntry;
if (entry == null) { if (entry == null) {
throw new NotImplementedException(); throw new NotImplementedException();
@ -232,8 +429,8 @@ internal class CommitCrimeParser : ITransactionParserPart {
} }
} }
internal class MissionsParser : ITransactionParserPart { internal class MissionsParser : TransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
MissionsEntry? missions = entry as MissionsEntry; MissionsEntry? missions = entry as MissionsEntry;
if (missions == null) { if (missions == null) {
@ -261,8 +458,8 @@ internal class MissionsParser : ITransactionParserPart {
} }
} }
internal class MissionAcceptedParser : ITransactionParserPart { internal class MissionAcceptedParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
MissionAcceptedEntry? entry = e as MissionAcceptedEntry; MissionAcceptedEntry? entry = e as MissionAcceptedEntry;
if (entry == null) { if (entry == null) {
throw new NotImplementedException(); throw new NotImplementedException();
@ -287,15 +484,15 @@ internal class MissionAcceptedParser : ITransactionParserPart {
} }
} }
internal class MissionCompletedParser : ITransactionParserPart { internal class MissionCompletedParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
MissionCompletedEntry? entry = e as MissionCompletedEntry; MissionCompletedEntry? entry = e as MissionCompletedEntry;
if (entry == null || entry.Mission == null) { if (entry == null || entry.Mission == null) {
throw new NotImplementedException(); throw new NotImplementedException();
} }
Mission? mission; Mission? mission = null;
Location? accepted_location; Location? accepted_location = null;
string? target_faction_name = entry.Mission.TargetFaction; string? target_faction_name = entry.Mission.TargetFaction;
string? source_faction_name = entry.Mission.Faction; string? source_faction_name = entry.Mission.Faction;
@ -380,10 +577,6 @@ internal class MissionCompletedParser : ITransactionParserPart {
} else if (string.Compare(faction, source_faction_name, true) != 0 || } else if (string.Compare(faction, source_faction_name, true) != 0 ||
(string.Compare(faction, source_faction_name) == 0 && (string.Compare(faction, source_faction_name) == 0 &&
system_address != accepted_location.SystemAddress)) { system_address != accepted_location.SystemAddress)) {
// Whether we ignore influence support
if (options.IgnoreInfluenceSupport) {
continue;
}
// Source or target faction are different, and/or the system // Source or target faction are different, and/or the system
// differs. Sometimes missions go to different systems but to // differs. Sometimes missions go to different systems but to
// the same faction. // the same faction.
@ -402,11 +595,11 @@ internal class MissionCompletedParser : ITransactionParserPart {
} }
} }
internal class MissionFailedParser : ITransactionParserPart { internal class MissionFailedParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
Mission? mission; Mission? mission = null;
Location? accepted_location; Location? accepted_location = null;
string? accepted_system; string? accepted_system = null;
MissionFailedEntry? entry = e as MissionFailedEntry; MissionFailedEntry? entry = e as MissionFailedEntry;
if (entry == null) { if (entry == null) {
@ -457,8 +650,56 @@ internal class MissionFailedParser : ITransactionParserPart {
} }
} }
internal class RedeemVoucherParser : ITransactionParserPart { internal class SellExplorationDataParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
SellExplorationDataEntry? entry = e as SellExplorationDataEntry;
if (entry == null) {
throw new NotImplementedException();
}
transactions.Add(new Cartographics(entry) {
System = context.CurrentSystem,
Station = context.CurrentStation,
Faction = context.ControllingFaction,
IsLegacy = context.IsLegacy,
});
}
}
internal class SellOrganicDataParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
SellOrganicDataEntry? entry = e as SellOrganicDataEntry;
if (entry == null) {
throw new NotImplementedException();
}
transactions.Add(new OrganicData(entry) {
System = context.CurrentSystem,
Station = context.CurrentStation,
Faction = context.ControllingFaction,
IsLegacy = context.IsLegacy,
});
}
}
internal class MultiSellExplorationDataParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
MultiSellExplorationDataEntry? entry = e as MultiSellExplorationDataEntry;
if (entry == null) {
throw new NotImplementedException();
}
transactions.Add(new Cartographics(entry) {
System = context.CurrentSystem,
Station = context.CurrentStation,
Faction = context.ControllingFaction,
IsLegacy = context.IsLegacy,
});
}
}
internal class RedeemVoucherParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
RedeemVoucherEntry? entry = e as RedeemVoucherEntry; RedeemVoucherEntry? entry = e as RedeemVoucherEntry;
if (entry == null) { if (entry == null) {
throw new NotImplementedException(); throw new NotImplementedException();
@ -514,56 +755,86 @@ internal class RedeemVoucherParser : ITransactionParserPart {
} }
} }
internal class SellMicroResourcesParser : ITransactionParserPart { internal class SellMicroResourcesParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
SellMicroResourcesEntry? entry = e as SellMicroResourcesEntry; SellMicroResourcesEntry? entry = e as SellMicroResourcesEntry;
if (entry == null) { if (entry == null) {
throw new NotImplementedException(); throw new NotImplementedException();
} }
if (context.StationOwner == null) {
transactions.AddIncomplete(
new SellMicroResources(),
"Could not discern the station owner for micro resources sell.",
e);
return;
}
transactions.Add(new SellMicroResources(entry) { transactions.Add(new SellMicroResources(entry) {
System = context.CurrentSystem, System = context.CurrentSystem,
Station = context.CurrentStation, Station = context.CurrentStation,
Faction = context.StationOwner, Faction = context.ControllingFaction,
IsLegacy = context.IsLegacy, IsLegacy = context.IsLegacy,
}); });
} }
} }
internal class SearchAndRescueParser : ITransactionParserPart { internal class SearchAndRescueParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
SearchAndRescueEntry? entry = e as SearchAndRescueEntry; SearchAndRescueEntry? entry = e as SearchAndRescueEntry;
if (entry == null) { if (entry == null) {
throw new NotImplementedException(); throw new NotImplementedException();
} }
if (context.StationOwner == null) {
transactions.AddIncomplete(
new OrganicData(),
"Could not discern the station owner for S&R operations.",
e);
return;
}
transactions.Add(new SearchAndRescue(entry) { transactions.Add(new SearchAndRescue(entry) {
System = context.CurrentSystem, Faction = context.ControllingFaction,
Station = context.CurrentStation, Station = context.CurrentStation,
Faction = context.StationOwner, System = context.CurrentSystem,
IsLegacy = context.IsLegacy, IsLegacy = context.IsLegacy,
}); });
} }
} }
internal class FactionKillBondParser : ITransactionParserPart { internal class MarketBuyParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
MarketBuyEntry? entry = e as MarketBuyEntry;
if (entry == null) {
throw new NotImplementedException();
}
context.BoughtCargo(entry.Type, entry.BuyPrice);
transactions.Add(new BuyCargo(entry) {
Faction = context.ControllingFaction,
Station = context.CurrentStation,
System = context.CurrentSystem,
IsLegacy = context.IsLegacy,
});
}
}
internal class MarketSellParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
long profit = 0;
MarketSellEntry? entry = e as MarketSellEntry;
if (entry == null) {
throw new NotImplementedException();
}
if (entry.Type == null) {
throw new InvalidJournalEntryException("market sell contains no cargo type");
}
if (context.BuyCost.ContainsKey(entry.Type)) {
long avg = context.BuyCost[entry.Type];
profit = (long)entry.TotalSale - (avg * entry.Count);
}
transactions.Add(new SellCargo(entry) {
Faction = context.ControllingFaction,
Station = context.CurrentStation,
System = context.CurrentSystem,
Profit = profit,
IsLegacy = context.IsLegacy,
});
}
}
internal class FactionKillBondParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
FactionKillBondEntry? entry = e as FactionKillBondEntry; FactionKillBondEntry? entry = e as FactionKillBondEntry;
if (entry == null) { if (entry == null) {
throw new NotImplementedException(); throw new NotImplementedException();
@ -595,8 +866,8 @@ internal class FactionKillBondParser : ITransactionParserPart {
} }
} }
internal class EmbarkDisembarkParser : ITransactionParserPart { internal class EmbarkDisembarkParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
if (e.Is(Events.Embark)) { if (e.Is(Events.Embark)) {
context.IsOnFoot = false; context.IsOnFoot = false;
} else if (e.Is(Events.Disembark)) { } else if (e.Is(Events.Disembark)) {
@ -605,28 +876,24 @@ internal class EmbarkDisembarkParser : ITransactionParserPart {
} }
} }
internal class SupercruiseEntryParser : ITransactionParserPart { internal class SupercruiseEntryParser : TransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
// After a super cruise entry we are no longer on foot. // After a super cruise entry we are no longer on foot.
context.IsOnFoot = false; context.IsOnFoot = false;
context.DiscernCombatZone(transactions, entry); context.DiscernCombatZone(transactions, entry);
context.ResetCombatZone(); context.ResetCombatZone();
// Supercruise entry means you left the current local instance
context.LeftInstance();
} }
} }
internal class ShutdownParser : ITransactionParserPart { internal class ShutdownParser : TransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
context.DiscernCombatZone(transactions, entry); context.DiscernCombatZone(transactions, entry);
context.ResetCombatZone(); context.ResetCombatZone();
// Shutdown (logout) means you left the instance
context.LeftInstance();
} }
} }
internal class CapShipBondParser : ITransactionParserPart { internal class CapShipBondParser : TransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
if (entry.GetType() != typeof(CapShipBondEntry)) { if (entry.GetType() != typeof(CapShipBondEntry)) {
return; return;
} }
@ -635,8 +902,8 @@ internal class CapShipBondParser : ITransactionParserPart {
} }
} }
internal class FileHeaderParser : ITransactionParserPart { internal class FileHeaderParser : TransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
FileHeaderEntry? fileheader = entry as FileHeaderEntry; FileHeaderEntry? fileheader = entry as FileHeaderEntry;
if (fileheader == null) { if (fileheader == null) {
@ -647,8 +914,8 @@ internal class FileHeaderParser : ITransactionParserPart {
} }
} }
internal class ReceiveTextParser : ITransactionParserPart { internal class ReceiveTextParser : TransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
ReceiveTextEntry? receivetext = entry as ReceiveTextEntry; ReceiveTextEntry? receivetext = entry as ReceiveTextEntry;
if (receivetext == null) { if (receivetext == null) {
@ -665,25 +932,23 @@ internal class ReceiveTextParser : ITransactionParserPart {
} }
} }
internal class DiedParser : ITransactionParserPart { internal class DiedParser : TransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
// You can't complete a combat zone if you die in it. Others might keep it open // 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. // for you, but still you will not have completed it unless you jump back in.
context.ResetCombatZone(); context.ResetCombatZone();
// Dying also moves you back to another instance
context.LeftInstance();
} }
} }
internal class DropshipDeployParser : ITransactionParserPart { internal class DropshipDeployParser : TransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
// On drop ship deploy we are now on foot // On drop ship deploy we are now on foot
context.IsOnFoot = true; context.IsOnFoot = true;
} }
} }
internal class CommanderParser : ITransactionParserPart { internal class CommanderParser : TransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) { public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
// A commander entry happens when you log out, and log back in again // A commander entry happens when you log out, and log back in again
// for example when switching from Open, to Solo or PG. // for example when switching from Open, to Solo or PG.
context.DiscernCombatZone(transactions, entry); context.DiscernCombatZone(transactions, entry);
@ -692,7 +957,7 @@ internal class CommanderParser : ITransactionParserPart {
} }
public class TransactionParser { public class TransactionParser {
private static Dictionary<string, ITransactionParserPart> ParserParts { get; } = new() private static Dictionary<string, TransactionParserPart> ParserParts { get; } = new()
{ {
{ Events.CapShipBond, new CapShipBondParser() }, { Events.CapShipBond, new CapShipBondParser() },
{ Events.Commander, new CommanderParser() }, { Events.Commander, new CommanderParser() },
@ -721,7 +986,6 @@ public class TransactionParser {
{ Events.SellOrganicData, new SellOrganicDataParser() }, { Events.SellOrganicData, new SellOrganicDataParser() },
{ Events.ShipTargeted, new ShipTargetedParser() }, { Events.ShipTargeted, new ShipTargetedParser() },
{ Events.Shutdown, new ShutdownParser() }, { Events.Shutdown, new ShutdownParser() },
{ Events.SupercruiseDestinationDrop, new SupercruiseDestinationDropParser() },
{ Events.SupercruiseEntry, new SupercruiseEntryParser() }, { Events.SupercruiseEntry, new SupercruiseEntryParser() },
}; };
@ -736,17 +1000,7 @@ public class TransactionParser {
return IsRelevant(e.Event); return IsRelevant(e.Event);
} }
/// <summary>
/// Parses a list of entries with default options.
/// </summary>
/// <param name="entries"></param>
/// <returns></returns>
public List<Transaction>? Parse(IEnumerable<Entry> entries) { public List<Transaction>? Parse(IEnumerable<Entry> entries) {
TransactionParserOptions defaultOptions = new();
return Parse(entries, defaultOptions);
}
public List<Transaction>? Parse(IEnumerable<Entry> entries, TransactionParserOptions options) {
TransactionList transactions = new(); TransactionList transactions = new();
TransactionParserContext context = new(); TransactionParserContext context = new();
@ -759,8 +1013,8 @@ public class TransactionParser {
continue; continue;
} }
ITransactionParserPart transactionParserPart = ParserParts[entry.Event]; TransactionParserPart transactionParserPart = ParserParts[entry.Event];
transactionParserPart.Parse(entry, context, options, transactions); transactionParserPart.Parse(entry, context, transactions);
} }
return transactions.ToList(); return transactions.ToList();

View File

@ -1,303 +0,0 @@
using EDPlayerJournal.Entries;
namespace EDPlayerJournal.BGS;
internal class TransactionParserContext {
/// <summary>
/// Name of the current system the player is in.
/// </summary>
public string? CurrentSystem { get; set; }
/// <summary>
/// 64 bit address of the current system.
/// </summary>
public ulong? CurrentSystemAddress { get; set; }
/// <summary>
/// Controlling faction of the current system.
/// </summary>
public string? ControllingFaction { get; set; }
/// <summary>
/// Name of the current station the player is docked at.
/// </summary>
public string? CurrentStation { get; set; }
/// <summary>
/// Faction that owns the current station.
/// </summary>
public string? StationOwner { get; set; }
/// <summary>
/// Whether the player is currently on foot, or in an SRV/ship.
/// </summary>
public bool IsOnFoot { get; set; } = false;
/// <summary>
/// Type of the current instance after a SupercruiseDestinationDropEntry.
/// This is null if there is no current instance, or the current instance
/// is not known.
/// </summary>
public string? CurrentInstanceType { get; set; } = null;
/// <summary>
/// Last faction that awarded the player with combat bonds.
/// </summary>
public string? LastRecordedAwardingFaction { get; set; }
/// <summary>
/// Highest combat bond seen so far.
/// </summary>
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;
/// <summary>
/// Returns true if the current session is legacy
/// </summary>
public bool IsLegacy { get; set; } = false;
/// <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>
/// Thargoid scouts killed
/// </summary>
public ulong ThargoidScoutKills { get; set; } = 0;
/// <summary>
/// Thargoid interceptor kills
/// </summary>
public ulong ThargoidInterceptorKills { get; set; } = 0;
/// <summary>
/// Whether we have seen an AX warzone NPC talk to us with ReceiveText
/// </summary>
public bool HaveSeenAXWarzoneNPC { get; set; } = false;
/// <summary>
/// A list of accepted missions index by their mission ID
/// </summary>
public Dictionary<ulong, Mission> AcceptedMissions { get; } = new();
public Dictionary<ulong, Location> AcceptedMissionLocation { get; } = new();
/// <summary>
/// A way to lookup a system by its system id
/// </summary>
public Dictionary<ulong, string> SystemsByID { get; } = new();
/// <summary>
/// A list of factions present in the given star system
/// </summary>
public Dictionary<string, List<Faction>> SystemFactions { get; } = new();
/// <summary>
/// To which faction a given named NPC belonged to.
/// </summary>
public Dictionary<string, string> NPCFaction { get; } = new();
/// <summary>
/// Buy costs for a given commodity
/// </summary>
public Dictionary<string, long> BuyCost = new();
/// <summary>
/// Called when the player leaves an instance, either through logout, FSD jump or
/// supercruise instance.
/// </summary>
public void LeftInstance() {
CurrentInstanceType = null;
}
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) {
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 (CurrentInstanceType != null) {
if (!Instances.IsWarzone(CurrentInstanceType)) {
return;
}
if (LastRecordedAwardingFaction == null &&
Instances.IsHumanWarzone(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.",
e);
return;
}
// If we have information about the current instance being a warship use that
// information to determine the warzone.
if (Instances.IsInstance(CurrentInstanceType, Instances.WarzoneLow)) {
cztype = CombatZones.ShipCombatZone;
grade = CombatZones.DifficultyLow;
} else if (Instances.IsInstance(CurrentInstanceType, Instances.WarzoneMedium)) {
cztype = CombatZones.ShipCombatZone;
grade = CombatZones.DifficultyMedium;
} else if (Instances.IsInstance(CurrentInstanceType, Instances.WarzoneHigh)) {
cztype = CombatZones.ShipCombatZone;
grade = CombatZones.DifficultyHigh;
} else if (Instances.IsInstance(CurrentInstanceType, Instances.WarzoneThargoidLow)) {
cztype = CombatZones.AXCombatZone;
grade = CombatZones.DifficultyLow;
} else if (Instances.IsInstance(CurrentInstanceType, Instances.WarzoneThargoidMedium)) {
cztype = CombatZones.AXCombatZone;
grade = CombatZones.DifficultyMedium;
} else if (Instances.IsInstance(CurrentInstanceType, Instances.WarzoneThargoidHigh)) {
cztype = CombatZones.AXCombatZone;
grade = CombatZones.DifficultyHigh;
} else if (Instances.IsInstance(CurrentInstanceType, Instances.WarzoneThargoidVeryHigh)) {
cztype = CombatZones.AXCombatZone;
grade = CombatZones.DifficultyVeryHigh;
} else {
transactions.AddIncomplete(new CombatZone(),
"Unknown conflict zone difficulty",
e);
return;
}
} 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<bool>() { 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<Faction>? 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);
}
}

View File

@ -1,19 +0,0 @@
using EDPlayerJournal.Entries;
namespace EDPlayerJournal.BGS;
internal interface ITransactionParserPart {
/// <summary>
/// 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
/// </summary>
/// <param name="entry">The entry to parse</param>
/// <param name="context">Parsing context that may contain useful information</param>
/// <param name="transactions">List of parsed transactions</param>
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions);
}

View File

@ -38,27 +38,4 @@ public class CombatZones {
/// Very high difficulty, so far AX combat zone only /// Very high difficulty, so far AX combat zone only
/// </summary> /// </summary>
public static readonly string DifficultyVeryHigh = "Very High"; public static readonly string DifficultyVeryHigh = "Very High";
/// <summary>
/// Returns the given combat zone difficulty as an integer, so it can be sorted.
/// 0 = lowest difficulty, 1 = medium and so forth.
/// </summary>
public static int? DifficultyRank(string? difficulty) {
Dictionary<string, int> ranks = new() {
{ DifficultyLow, 0 },
{ DifficultyMedium, 1 },
{ DifficultyHigh, 2 },
{ DifficultyVeryHigh, 3 }
};
if (difficulty == null ) {
return null;
}
if (ranks.TryGetValue(difficulty, out int rank)) {
return rank;
}
return null;
}
} }

View File

@ -34,16 +34,4 @@ public class Credits {
return string.Format("{0} CR", amount.ToString("N", format)); return string.Format("{0} CR", amount.ToString("N", format));
} }
public static string FormatMillions(long amount) {
double millions = (amount / 1000000.0);
if (amount >= 100000) {
return string.Format("{0:0.0}M", millions);
} else if (amount >= 10000) {
return string.Format("{0:0.00}M", millions);
}
return "";
}
} }

View File

@ -15,7 +15,6 @@ public class EnglishMissionNames {
{"Mission_AltruismCredits_name", "Donate Credits"}, {"Mission_AltruismCredits_name", "Donate Credits"},
{"Mission_AltruismCredits_Outbreak_name", "Donate Credits (Outbreak)" }, {"Mission_AltruismCredits_Outbreak_name", "Donate Credits (Outbreak)" },
{"Mission_Assassinate_Illegal_BLOPS_name", "Assassination (Illegal)"}, {"Mission_Assassinate_Illegal_BLOPS_name", "Assassination (Illegal)"},
{"MISSION_assassinate_Illegal_Planetary_name", "Assassination (Illegal, Planetary)" },
{"Mission_Assassinate_Legal_Bust_name", "Assassination (Bust, Legal)" }, {"Mission_Assassinate_Legal_Bust_name", "Assassination (Bust, Legal)" },
{"MISSION_assassinate_legal_CivilUnrest_name", "Assassination (Civil Unrest, Legal)" }, {"MISSION_assassinate_legal_CivilUnrest_name", "Assassination (Civil Unrest, Legal)" },
{"Mission_Assassinate_Legal_Communism_name", "Assassination (Communism, Legal)" }, {"Mission_Assassinate_Legal_Communism_name", "Assassination (Communism, Legal)" },
@ -52,10 +51,6 @@ public class EnglishMissionNames {
{"Mission_Delivery_Retreat_name", "Delivery (Retreat)"}, {"Mission_Delivery_Retreat_name", "Delivery (Retreat)"},
{"Mission_DeliveryWing_name", "Delivery (Wing)"}, {"Mission_DeliveryWing_name", "Delivery (Wing)"},
{"Mission_DeliveryWing_War_name", "Delivery (Wing) (War)"}, {"Mission_DeliveryWing_War_name", "Delivery (Wing) (War)"},
{"Mission_Disable_BLOPS_Expansion_name", "Disable Surface Installation (Expansion) (Black Ops)" },
{"MISSION_Disable_BLOPS_name", "Disable Surface Installation (Black Ops)" },
{"MISSION_Disable_name", "Disable Surface Installation" },
{"MISSION_Hack_name", "Hack Surface Installation" },
{"Mission_Hack_BLOPS_Boom_name", "Hack Surface Installation (Boom)"}, {"Mission_Hack_BLOPS_Boom_name", "Hack Surface Installation (Boom)"},
{"Mission_Hack_BLOPS_Elections_name", "Hack Surface Installation (Elections)"}, {"Mission_Hack_BLOPS_Elections_name", "Hack Surface Installation (Elections)"},
{"Mission_Hack_BLOPS_Expansion_name", "Hack Surface Installation (Expansion)"}, {"Mission_Hack_BLOPS_Expansion_name", "Hack Surface Installation (Expansion)"},
@ -70,30 +65,24 @@ public class EnglishMissionNames {
{"Mission_MassacreWing_name", "Massacre (Wing)"}, {"Mission_MassacreWing_name", "Massacre (Wing)"},
{"Mission_Mining_name", "Mining" }, {"Mission_Mining_name", "Mining" },
{"Mission_OnFoot_Assassination_MB_name", "On Foot Assassination"}, {"Mission_OnFoot_Assassination_MB_name", "On Foot Assassination"},
{"Mission_OnFoot_Assassination_Hard_MB_name", "On Foot Assassination (Flight-Risk)" },
{"Mission_OnFoot_Assassination_Covert_MB_name", "On Foot Assassination (Covert)" },
{"Mission_OnFoot_AssassinationIllegal_MB_name", "On Foot Assassination (Illegal)"}, {"Mission_OnFoot_AssassinationIllegal_MB_name", "On Foot Assassination (Illegal)"},
{"Mission_OnFoot_AssassinationIllegal_NCD_MB_name", "On Foot Assassination (Illegal)" }, {"Mission_OnFoot_AssassinationIllegal_NCD_MB_name", "On Foot Assassination (Illegal)" },
{"Mission_OnFoot_Collect_Contact_MB_name", "On Foot Collect"}, {"Mission_OnFoot_Collect_Contact_MB_name", "On Foot Collect"},
{"Mission_OnFoot_Collect_MB_name", "On Foot Collection"}, {"Mission_OnFoot_Collect_MB_name", "On Foot Collection"},
{"Mission_OnFoot_Defence_MacGuffin_MB_StandardCanister_name", "On Foot Cargo Defence" },
{"Mission_OnFoot_Delivery_Contact_MB_name", "On Foot Delivery (Contact)"}, {"Mission_OnFoot_Delivery_Contact_MB_name", "On Foot Delivery (Contact)"},
{"Mission_OnFoot_Hack_Upload_Covert_MB_name", "On Foot Hack (Covert Upload)"}, {"Mission_OnFoot_Hack_Upload_Covert_MB_name", "On Foot Hack (Covert Upload)"},
{"Mission_OnFoot_Hack_Upload_MB_name", "On Foot Hack (Upload)"}, {"Mission_OnFoot_Hack_Upload_MB_name", "On Foot Hack (Upload)"},
{"Mission_OnFoot_Heist_MB_name", "On Foot Heist" }, {"Mission_OnFoot_Heist_MB_name", "On Foot Heist" },
{"Mission_OnFoot_Heist_POI_MB_name", "On Foot Heist (POI)"}, {"Mission_OnFoot_Heist_POI_MB_name", "On Foot Heist (POI)"},
{"Mission_OnFoot_Massacre_MB_name", "On Foot Massacre" }, {"Mission_OnFoot_Massacre_MB_name", "On Foot Massacre" },
{"Mission_OnFoot_MassacreIllegal_MB_name", "On Foot Massacre (Illegal)"}, {"Mission_OnFoot_MassacreIllegal_MB_name", "On Foot Massacre (Illegal)" },
{"Mission_OnFoot_Onslaught_MB_name", "On Foot Onslaught"}, {"Mission_OnFoot_Onslaught_MB_name", "On Foot Onslaught"},
{"Mission_OnFoot_Onslaught_Offline_MB_name", "On Foot Onslaught (Offline)"}, {"Mission_OnFoot_Onslaught_Offline_MB_name", "On Foot Onslaught (Offline)"},
{"Mission_OnFoot_ProductionHeist_Covert_MB_name", "On Foot Production Heist (Covert)"}, {"Mission_OnFoot_ProductionHeist_Covert_MB_name", "On Foot Production Heist (Covert)"},
{"Mission_OnFoot_ProductionHeist_MB_name", "On Foot Production Heist"}, {"Mission_OnFoot_ProductionHeist_MB_name", "On Foot Production Heist"},
{"Mission_OnFoot_Reboot_MB_name", "On Foot Reboot"}, {"Mission_OnFoot_Reboot_MB_name", "On Foot Reboot"},
{"Mission_OnFoot_RebootRestore_MB_name", "On Foot Reboot/Restore"}, {"Mission_OnFoot_RebootRestore_MB_name", "On Foot Reboot/Restore"},
{"Mission_OnFoot_Sabotage_Power_MB_name", "On Foot Power Sabotage"}, {"Mission_OnFoot_Sabotage_Production_Covert_MB_name", "On Foot Sabotage Production (Covert)"},
{"Mission_OnFoot_Sabotage_Power_Covert_MB_name", "On Foot Power Sabotage (Covert)"},
{"Mission_OnFoot_Sabotage_Production_MB_name", "On Foot Production Sabotage"},
{"Mission_OnFoot_Sabotage_Production_Covert_MB_name", "On Foot Production Sabotage (Covert)"},
{"Mission_OnFoot_Salvage_MB_name", "On Foot Salvage"}, {"Mission_OnFoot_Salvage_MB_name", "On Foot Salvage"},
{"Mission_OnFoot_SalvageIllegal_MB_name", "On Foot Salvage (Illegal)"}, {"Mission_OnFoot_SalvageIllegal_MB_name", "On Foot Salvage (Illegal)"},
{"Mission_OnFoot_Smuggle_Contact_MB_name", "On Foot Smuggling" }, {"Mission_OnFoot_Smuggle_Contact_MB_name", "On Foot Smuggling" },
@ -104,10 +93,8 @@ public class EnglishMissionNames {
{"Mission_PassengerVIP_name", "Passenger (VIP)"}, {"Mission_PassengerVIP_name", "Passenger (VIP)"},
{"Mission_PassengerVIP_Scientist_FAMINE_name", "Passenger Scientist (VIP) (Famine)"}, {"Mission_PassengerVIP_Scientist_FAMINE_name", "Passenger Scientist (VIP) (Famine)"},
{"Mission_PassengerVIP_Tourist_BOOM_name", "Passenger Tourist (VIP) (Boom)"}, {"Mission_PassengerVIP_Tourist_BOOM_name", "Passenger Tourist (VIP) (Boom)"},
{"Mission_Rescue_Elections_name", "Liberate Hostages (Election)" },
{"Mission_Rescue_name", "Liberate Hostages" },
{"Mission_Rescue_Planet_Expansion_name", "Planet Rescue (Expansion)" },
{"Mission_Rescue_Planet_name", "Planet Rescue"}, {"Mission_Rescue_Planet_name", "Planet Rescue"},
{"Mission_Rescue_Planet_Expansion_name", "Planet Rescue (Expansion)" },
{"MISSION_Salvage_CivilUnrest_name", "Salvage (Civil Unrest)"}, {"MISSION_Salvage_CivilUnrest_name", "Salvage (Civil Unrest)"},
{"MISSION_Salvage_Expansion_name", "Salvage (Expansion)"}, {"MISSION_Salvage_Expansion_name", "Salvage (Expansion)"},
{"MISSION_Salvage_Illegal_name", "Salvage (Illegal)"}, {"MISSION_Salvage_Illegal_name", "Salvage (Illegal)"},
@ -119,16 +106,11 @@ public class EnglishMissionNames {
{"Mission_Sightseeing_Criminal_FAMINE_name", "Sightseeing (Criminal) (Famine)"}, {"Mission_Sightseeing_Criminal_FAMINE_name", "Sightseeing (Criminal) (Famine)"},
{"Mission_Sightseeing_name", "Sightseeing"}, {"Mission_Sightseeing_name", "Sightseeing"},
{"Mission_Smuggle_Anarchy_name", "Smuggling (Anarchy)" }, {"Mission_Smuggle_Anarchy_name", "Smuggling (Anarchy)" },
{"Mission_TW_Massacre_Basilisk_Plural_name", "Kill Basilisks" }, {"Mission_TW_Massacre_Basilisk_Plural_name", "Kill Basilisk" },
{"Mission_TW_Massacre_Basilisk_Singular_name", "Kill Basilisk" }, {"Mission_TW_Massacre_Basilisk_Singular_name", "Kill Basilisk" },
{"Mission_TW_Massacre_Cyclops_Plural_name", "Kill Cyclops" }, {"Mission_TW_Massacre_Cyclops_Plural_name", "Kill Cyclops" },
{"Mission_TW_Massacre_Cyclops_Singular_name", "Kill Cyclop" }, {"Mission_TW_Massacre_Cyclops_Singular_name", "Kill Cyclops" },
{"Mission_TW_Massacre_Hydra_Plural_name", "Kill Hydras" }, {"Mission_TW_Massacre_Scout_Plural_name", "Kill Scouts (Wing)" },
{"Mission_TW_Massacre_Hydra_Singular_name", "Kill Hydra" },
{"Mission_TW_Massacre_Medusa_Plural_name", "Kill Medusas" },
{"Mission_TW_Massacre_Medusa_Singular_name", "Kill Medusa" },
{"Mission_TW_Massacre_Scout_Plural_name", "Kill Scouts" },
{"Mission_TW_OnFoot_Reboot_Occupied_MB_name", "Reboot Settlement (Thargoid)" },
{"Mission_TW_PassengerEvacuation_Burning_name", "Passenger Evacuation (Significant Damage)" }, {"Mission_TW_PassengerEvacuation_Burning_name", "Passenger Evacuation (Significant Damage)" },
{"Mission_TW_PassengerEvacuation_UnderAttack_name", "Passenger Evacuation (Thargoid Invasion)" }, {"Mission_TW_PassengerEvacuation_UnderAttack_name", "Passenger Evacuation (Thargoid Invasion)" },
{"Mission_TW_Rescue_UnderAttack_name", "Rescue (Thargoid Attack)" }, {"Mission_TW_Rescue_UnderAttack_name", "Rescue (Thargoid Attack)" },

View File

@ -44,7 +44,6 @@ public class Entry {
{ Events.SellOrganicData, typeof(SellOrganicDataEntry) }, { Events.SellOrganicData, typeof(SellOrganicDataEntry) },
{ Events.ShieldState, typeof(ShieldStateEntry) }, { Events.ShieldState, typeof(ShieldStateEntry) },
{ Events.ShipTargeted, typeof(ShipTargetedEntry) }, { Events.ShipTargeted, typeof(ShipTargetedEntry) },
{ Events.SupercruiseDestinationDrop, typeof(SupercruiseDestinationDropEntry) },
{ Events.SupercruiseEntry, typeof(SupercruiseEntryEntry) }, { Events.SupercruiseEntry, typeof(SupercruiseEntryEntry) },
{ Events.SupercruiseExit, typeof(SupercruiseExitEntry) }, { Events.SupercruiseExit, typeof(SupercruiseExitEntry) },
{ Events.UnderAttack, typeof(UnderAttackEntry) }, { Events.UnderAttack, typeof(UnderAttackEntry) },

View File

@ -35,7 +35,6 @@ public class Events {
public static readonly string ShieldState = "ShieldState"; public static readonly string ShieldState = "ShieldState";
public static readonly string ShipTargeted = "ShipTargeted"; public static readonly string ShipTargeted = "ShipTargeted";
public static readonly string Shutdown = "Shutdown"; public static readonly string Shutdown = "Shutdown";
public static readonly string SupercruiseDestinationDrop = "SupercruiseDestinationDrop";
public static readonly string SupercruiseEntry = "SupercruiseEntry"; public static readonly string SupercruiseEntry = "SupercruiseEntry";
public static readonly string SupercruiseExit = "SupercruiseExit"; public static readonly string SupercruiseExit = "SupercruiseExit";
public static readonly string UnderAttack = "UnderAttack"; public static readonly string UnderAttack = "UnderAttack";

View File

@ -1,47 +1,18 @@
using Newtonsoft.Json.Linq; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
namespace EDPlayerJournal.Entries; namespace EDPlayerJournal.Entries;
public class LocationEntry : Entry { public class LocationEntry : Entry {
/// <summary>
/// Current star system.
/// </summary>
public string? StarSystem { get; set; } public string? StarSystem { get; set; }
/// <summary>
/// Faction in control of the current star system. Empty for unpopulated
/// systems.
/// </summary>
public string? SystemFaction { get; set; } public string? SystemFaction { get; set; }
/// <summary>
/// 64bit system address.
/// </summary>
public ulong SystemAddress { get; set; }
/// <summary>
/// Station name if docked at a station.
/// </summary>
public string? StationName { get; set; } public string? StationName { get; set; }
public ulong SystemAddress { get; set; }
/// <summary>
/// Faction in control of the current station, if docked.
/// </summary>
public string? StationFaction { get; set; }
/// <summary>
/// Body within the system, might be null.
/// </summary>
public string? Body { get; set; } public string? Body { get; set; }
/// <summary>
/// Returns true if the player is docked somewhere.
/// </summary>
public bool Docked { get; set; } public bool Docked { get; set; }
/// <summary>
/// Position of the star system.
/// </summary>
public long[]? StarPos { get; set; } public long[]? StarPos { get; set; }
public List<Faction> SystemFactions { get; set; } = new List<Faction>(); public List<Faction> SystemFactions { get; set; } = new List<Faction>();
@ -59,12 +30,7 @@ public class LocationEntry : Entry {
JObject? systemfaction = JSON.Value<JObject>("SystemFaction"); JObject? systemfaction = JSON.Value<JObject>("SystemFaction");
if (systemfaction != null) { if (systemfaction != null) {
SystemFaction = systemfaction.Value<string>("Name"); SystemFaction = systemfaction.Value<string>("Name") ?? "";
}
JObject? stationfaction = JSON.Value<JObject>("StationFaction");
if (stationfaction != null) {
StationFaction = stationfaction.Value<string>("Name");
} }
JArray? factions = JSON.Value<JArray>("Factions"); JArray? factions = JSON.Value<JArray>("Factions");

View File

@ -1,30 +0,0 @@
namespace EDPlayerJournal.Entries;
public class SupercruiseDestinationDropEntry : Entry {
/// <summary>
/// Destination type, internal string.
/// </summary>
public string? Type { get; set; } = null;
/// <summary>
/// Destination type, localised string.
/// </summary>
public string? TypeLocalised { get; set; } = null;
/// <summary>
/// Threat of the destination, if known.
/// </summary>
public long? Threat { get; set; } = null;
/// <summary>
/// Market ID if it has one.
/// </summary>
public ulong? MarketID { get; set; } = null;
protected override void Initialise() {
Type = JSON.Value<string>("Type");
TypeLocalised = JSON.Value<string>("Type_Localised");
Threat = JSON.Value<long?>("Threat");
MarketID = JSON.Value<ulong?>("MarketID");
}
}

View File

@ -55,11 +55,6 @@ public class Factions {
/// </summary> /// </summary>
public static string Thargoid = "Thargoids"; public static string Thargoid = "Thargoids";
/// <summary>
/// The faction the fleet carriers use.
/// </summary>
public static string FleetCarrier = "FleetCarrier";
public static bool IsPilotsFederation(string? name) { public static bool IsPilotsFederation(string? name) {
if (name == null) { if (name == null) {
return false; return false;

View File

@ -1,73 +0,0 @@
namespace EDPlayerJournal;
/// <summary>
/// Contains information regarding instances you can supercruise into,
/// such as combat zones, installations and megaship scenarios.
/// </summary>
public class Instances {
/// <summary>
/// Low ship combat zone
/// </summary>
public static readonly string WarzoneLow = "$Warzone_PointRace_Low";
/// <summary>
/// Medium ship combat zone
/// </summary>
public static readonly string WarzoneMedium = "$Warzone_PointRace_Med";
/// <summary>
/// High ship combat zone.
/// </summary>
public static readonly string WarzoneHigh = "$Warzone_PointRace_High";
/// <summary>
/// Low Thargoid combat zone
/// </summary>
public static readonly string WarzoneThargoidLow = "$Warzone_TG_Low";
/// <summary>
/// Medium Thargoid combat zone
/// </summary>
public static readonly string WarzoneThargoidMedium = "$Warzone_TG_Med";
/// <summary>
/// High Thargoid combat zone
/// </summary>
public static readonly string WarzoneThargoidHigh = "$Warzone_TG_High";
/// <summary>
/// Very High Thargoid combat zone
/// </summary>
public static readonly string WarzoneThargoidVeryHigh = "$Warzone_TG_VeryHigh";
public static bool IsThargoidWarzone(string type) {
return
IsInstance(type, WarzoneThargoidLow) ||
IsInstance(type, WarzoneThargoidMedium) ||
IsInstance(type, WarzoneThargoidHigh) ||
IsInstance(type, WarzoneThargoidVeryHigh)
;
}
public static bool IsHumanWarzone(string type) {
return
IsInstance(type, WarzoneLow) ||
IsInstance(type, WarzoneMedium) ||
IsInstance(type, WarzoneHigh)
;
}
public static bool IsWarzone(string type) {
return IsHumanWarzone(type) || IsThargoidWarzone(type);
}
public static bool IsInstance(string type, string instance) {
if (string.IsNullOrEmpty(type) || string.IsNullOrEmpty(instance)) {
return false;
}
// Instance names are split by a semi colon, with the remainder being
// additional info to such as index.
string[] parts = type.Split(":");
if (!parts[0].StartsWith("$")) {
return false;
}
return string.Compare(parts[0], instance, true) == 0;
}
}

View File

@ -8,11 +8,6 @@ public enum ThargoidVessel {
Basilisk = 4, Basilisk = 4,
Medusa = 5, Medusa = 5,
Hydra = 6, Hydra = 6,
Glaive = 7,
/// <summary>
/// Thargoid drone
/// </summary>
Revenant = 8,
} }
public class Thargoid { public class Thargoid {
@ -20,41 +15,34 @@ public class Thargoid {
public static Dictionary<ulong, ThargoidVessel> VesselPayout { get; } = new() { public static Dictionary<ulong, ThargoidVessel> VesselPayout { get; } = new() {
// Up to date values ever since 14.02 // Up to date values ever since 14.02
{ 25000, ThargoidVessel.Revenant },
{ 65000, ThargoidVessel.Scout }, { 65000, ThargoidVessel.Scout },
{ 75000, ThargoidVessel.Scout }, { 75000, ThargoidVessel.Scout },
// New in Update 15
{ 4500000, ThargoidVessel.Glaive },
{ 6500000, ThargoidVessel.Cyclops }, { 6500000, ThargoidVessel.Cyclops },
{ 20000000, ThargoidVessel.Basilisk }, { 20000000, ThargoidVessel.Basilisk },
//{ 25000000, ThargoidVessel.Orthrus }, { 25000000, ThargoidVessel.Orthrus },
{ 34000000, ThargoidVessel.Medusa }, { 34000000, ThargoidVessel.Medusa },
// March, 16th 2023 the Orthrus payout was buffed again.
{ 40000000, ThargoidVessel.Orthrus },
{ 50000000, ThargoidVessel.Hydra }, { 50000000, ThargoidVessel.Hydra },
// These are the old values pre Update 14.02 // These are the old values pre Update 14.02
//{ 80000, ThargoidVessel.Scout }, { 80000, ThargoidVessel.Scout },
//{ 8000000, ThargoidVessel.Cyclops }, { 8000000, ThargoidVessel.Cyclops },
//{ 24000000, ThargoidVessel.Basilisk }, { 24000000, ThargoidVessel.Basilisk },
// In Update 14.1 the payout for Orthrus has been rebalanced. // In Update 14.1 the payout for Orthrus has been rebalanced.
//{ 30000000, ThargoidVessel.Orthrus }, { 30000000, ThargoidVessel.Orthrus },
//{ 40000000, ThargoidVessel.Medusa }, { 40000000, ThargoidVessel.Medusa },
// This used to be the old payout value for Orthrus, it now conflicts // This used to be the old payout value for Orthrus, it now conflicts
// with Post Update 14.02 Hydra values // with Post Update 14.02 Hydra values
//{ 50000000, ThargoidVessel.Orthrus }, //{ 50000000, ThargoidVessel.Orthrus },
//{ 60000000, ThargoidVessel.Hydra }, { 60000000, ThargoidVessel.Hydra },
}; };
public static Dictionary<ThargoidVessel, string?> VesselNames { get; } = new() { public static Dictionary<ThargoidVessel, string?> VesselNames { get; } = new() {
{ ThargoidVessel.Unknown, null }, { ThargoidVessel.Unknown, null },
{ ThargoidVessel.Revenant, "Revenant" }, { ThargoidVessel.Scout, "Thargoid Scout" },
{ ThargoidVessel.Scout, "Scout" },
{ ThargoidVessel.Orthrus, "Orthrus" }, { ThargoidVessel.Orthrus, "Orthrus" },
{ ThargoidVessel.Cyclops, "Cyclops" }, { ThargoidVessel.Cyclops, "Cyclops" },
{ ThargoidVessel.Basilisk, "Basilisk" }, { ThargoidVessel.Basilisk, "Basilisk" },
{ ThargoidVessel.Medusa, "Medusa" }, { ThargoidVessel.Medusa, "Medusa" },
{ ThargoidVessel.Hydra, "Hydra" }, { ThargoidVessel.Hydra, "Hydra" },
{ ThargoidVessel.Glaive, "Glaive" },
}; };
public static ThargoidVessel GetVesselByPayout(ulong payout) { public static ThargoidVessel GetVesselByPayout(ulong payout) {

View File

@ -4,14 +4,6 @@
xmlns:local="clr-namespace:EliteBGS" xmlns:local="clr-namespace:EliteBGS"
StartupUri="MainWindow.xaml"> StartupUri="MainWindow.xaml">
<Application.Resources> <Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<!-- Theme setting -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources> </Application.Resources>
</Application> </Application>

View File

@ -1,5 +1,4 @@
using ControlzEx.Theming; using System.Windows;
using System.Windows;
namespace EliteBGS { namespace EliteBGS {
/// <summary> /// <summary>

View File

@ -23,31 +23,6 @@ public class DiscordLogGenerator {
new SearchAndRescueFormat(), new SearchAndRescueFormat(),
}; };
protected virtual string GenerateSummary(Objective objective) {
StringBuilder sb = new StringBuilder();
foreach (var formatter in formatters) {
string summary = "";
try {
summary = formatter.GenerateSummary(objective);
} catch (NotImplementedException) {
}
if (string.IsNullOrEmpty(summary)) {
continue;
}
if (sb.Length > 0) {
sb.Append("; ");
}
sb.Append(summary);
}
return sb.ToString();
}
protected virtual string GenerateHeader() { protected virtual string GenerateHeader() {
return ""; return "";
} }
@ -74,13 +49,8 @@ public class DiscordLogGenerator {
.Count() .Count()
; ;
string summary = GenerateSummary(objective);
log.AppendFormat("**Date:** {0}\n", DateTime.Now.ToString("dd/MM/yyyy")); log.AppendFormat("**Date:** {0}\n", DateTime.Now.ToString("dd/MM/yyyy"));
log.AppendFormat("**Target:** {0}\n", location); log.AppendFormat("**Target:** {0}\n", location);
if (!string.IsNullOrEmpty(summary)) {
log.AppendFormat("**Summary**: {0}\n", summary);
}
if (legacycount > 0) { if (legacycount > 0) {
log.AppendFormat("**Warning:** Some actions were performed on ED Legacy\n"); log.AppendFormat("**Warning:** Some actions were performed on ED Legacy\n");
} }

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

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework> <TargetFramework>net7.0-windows</TargetFramework>
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>
<Version>0.3.3</Version> <Version>0.2.6</Version>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<UseWindowsForms>true</UseWindowsForms> <UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF> <UseWPF>true</UseWPF>
@ -16,22 +16,15 @@
<Title>BGS reporting and logging tool for Elite:Dangerous</Title> <Title>BGS reporting and logging tool for Elite:Dangerous</Title>
<Authors>nola</Authors> <Authors>nola</Authors>
<Copyright>Copyright 2019 by Florian Stinglmayr</Copyright> <Copyright>Copyright 2019 by Florian Stinglmayr</Copyright>
<RepositoryUrl>https://codeberg.org/nola/EDBGS</RepositoryUrl> <RepositoryUrl>https://git.aror.org/florian/EDBGS</RepositoryUrl>
<PackageTags>ED;Elite Dangerous;BGS</PackageTags> <PackageTags>ED;Elite Dangerous;BGS</PackageTags>
<PackageProjectUrl>https://bgs.n0la.org</PackageProjectUrl> <PackageProjectUrl>https://bgs.n0la.org</PackageProjectUrl>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>
<Description>Elite: Dangerous BGS logging and reporting tool
</Description>
<PackageIcon>logo_v5.png</PackageIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Resource Include="main-page.png"> <Resource Include="main-page.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Resource> </Resource>
<None Update="logo_v5.png">
<Pack>True</Pack>
<PackagePath>\</PackagePath>
</None>
<None Update="README.md"> <None Update="README.md">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Pack>True</Pack> <Pack>True</Pack>
@ -55,7 +48,18 @@
<Resource Include="Salus.ico" /> <Resource Include="Salus.ico" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="MahApps.Metro" Version="2.4.9" /> <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.CSharp" Version="4.7.0" />
<PackageReference Include="Microsoft.Windows.Compatibility" Version="7.0.0" /> <PackageReference Include="Microsoft.Windows.Compatibility" Version="7.0.0" />
</ItemGroup> </ItemGroup>
@ -79,7 +83,4 @@
<LastGenOutput>Resources.Designer.cs</LastGenOutput> <LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Resources\" />
</ItemGroup>
</Project> </Project>

BIN
EliteBGS/EliteBGS.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

View File

@ -1,6 +1,5 @@
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Windows.Documents;
using EDPlayerJournal; using EDPlayerJournal;
using EDPlayerJournal.BGS; using EDPlayerJournal.BGS;
@ -54,24 +53,4 @@ public class CargoSoldFormatter : LogFormatter {
return builder.ToString(); return builder.ToString();
} }
public string GenerateSummary(Objective objective) {
SellCargo[] sold = objective.EnabledOfType<SellCargo>().ToArray();
long totalProfit = sold.Sum(x => x.Profit);
long tons = sold.Sum(x => x.Amount);
if (tons <= 0) {
return "";
}
StringBuilder builder = new();
builder.Append("Sold: ");
builder.AppendFormat("{0}t", tons);
if (totalProfit >= 100000) {
builder.AppendFormat(", {0} profit", Credits.FormatMillions(totalProfit));
}
return builder.ToString();
}
} }

View File

@ -19,15 +19,4 @@ public class CartographicsFormat : LogFormatter {
pages, Credits.FormatCredits(sum) pages, Credits.FormatCredits(sum)
); );
} }
public string GenerateSummary(Objective objective) {
Cartographics[] sold = objective.EnabledOfType<Cartographics>().ToArray();
long totalProfit = sold.Sum(x => x.TotalSum);
if (totalProfit >= 100000) {
return string.Format("Explo: {0}", Credits.FormatMillions(totalProfit));
}
return "";
}
} }

View File

@ -9,7 +9,6 @@ class CombatZoneFormat : LogFormatter {
public string GenerateLog(Objective objective) { public string GenerateLog(Objective objective) {
var logs = objective var logs = objective
.EnabledOfType<CombatZone>() .EnabledOfType<CombatZone>()
.OrderBy(x => (CombatZones.DifficultyRank(x.Grade) ?? 0))
.GroupBy(x => new { x.Type, x.Grade }) .GroupBy(x => new { x.Type, x.Grade })
.ToDictionary(x => x.Key, x => x.ToList()) .ToDictionary(x => x.Key, x => x.ToList())
; ;
@ -44,49 +43,4 @@ class CombatZoneFormat : LogFormatter {
return builder.ToString().Trim(); return builder.ToString().Trim();
} }
public string GenerateSummary(Objective objective) {
var logs = objective
.EnabledOfType<CombatZone>()
.OrderBy(x => (CombatZones.DifficultyRank(x.Grade) ?? 0))
.GroupBy(x => new { x.Type, x.Grade })
.ToDictionary(x => x.Key, x => x.ToList())
;
StringBuilder builder = new StringBuilder();
if (logs == null || logs.Count() <= 0) {
return "";
}
foreach (var log in logs) {
int optionals = log.Value
.Sum(x => x.OptionalObjectivesCompleted)
;
if (builder.Length > 0) {
builder.Append(", ");
}
if (!string.IsNullOrEmpty(log.Key.Grade)) {
string grade = log.Key.Grade.Substring(0, 1);
if (log.Key.Grade == CombatZones.DifficultyVeryHigh) {
grade = "VH";
}
builder.AppendFormat("CZ: {0}x{1}{2}",
log.Value.Count,
grade,
log.Key.Type.Substring(0, 1)
);
} else {
builder.AppendFormat("CZ: {0}x?{1}",
log.Value.Count,
log.Key.Type.Substring(0, 1)
);
}
if (optionals > 0) {
builder.AppendFormat("+ {0} OPTS", optionals);
}
}
return builder.ToString().Trim();
}
} }

View File

@ -1,5 +1,6 @@
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using EDPlayerJournal.Entries;
using EDPlayerJournal.BGS; using EDPlayerJournal.BGS;
namespace EliteBGS.LogGenerator; namespace EliteBGS.LogGenerator;
@ -30,31 +31,4 @@ public class FailedMissionFormat : LogFormatter {
return builder.ToString().Trim(); return builder.ToString().Trim();
} }
public string GenerateSummary(Objective objective) {
var missions = objective.EnabledOfType<MissionFailed>();
if (missions.Count <= 0) {
return "";
}
StringBuilder sb = new();
int onFootFails = missions.Where(x => x.Mission.IsOnFoot).Count();
int shipFails = missions.Where(x => !x.Mission.IsOnFoot).Count();
sb.Append("Fails: ");
if (onFootFails > 0) {
sb.AppendFormat("{0} Ground", onFootFails);
}
if (shipFails > 0) {
if (onFootFails > 0) {
sb.Append(", ");
}
sb.AppendFormat("{0} Ship", shipFails);
}
return sb.ToString();
}
} }

View File

@ -25,8 +25,4 @@ public class GenericFormat<Type> : LogFormatter where Type : Transaction {
return builder.ToString(); return builder.ToString();
} }
public virtual string GenerateSummary(Objective objective) {
throw new System.NotImplementedException();
}
} }

View File

@ -1,38 +1,6 @@
using EDPlayerJournal; using EDPlayerJournal.BGS;
using EDPlayerJournal.BGS;
using System.Linq;
using System.Text;
namespace EliteBGS.LogGenerator; namespace EliteBGS.LogGenerator;
public class KillBondsFormat : GenericFormat<FactionKillBonds> { public class KillBondsFormat : GenericFormat<FactionKillBonds> {
public override string GenerateSummary(Objective objective) {
StringBuilder builder = new StringBuilder();
var bonds = objective
.EnabledOfType<FactionKillBonds>()
.GroupBy(x => x.VictimFaction)
.ToDictionary(x => x.Key, x => x.ToList())
;
if (bonds.Count <= 0) {
return "";
}
builder.Append("Killbonds: ");
foreach (var entry in bonds) {
long sum = (long)entry.Value.Sum(x => (decimal)x.TotalSum);
builder.AppendFormat("{0} against {1}, ",
Credits.FormatMillions(sum),
entry.Key
);
}
if (builder.Length > 2) {
// Remove trailing comma
builder.Remove(builder.Length - 2, 2);
}
return builder.ToString();
}
} }

View File

@ -2,5 +2,4 @@
public interface LogFormatter { public interface LogFormatter {
string GenerateLog(Objective objective); string GenerateLog(Objective objective);
string GenerateSummary(Objective objective);
} }

View File

@ -1,17 +1,6 @@
using EDPlayerJournal.BGS; using EDPlayerJournal.BGS;
using System.Linq;
namespace EliteBGS.LogGenerator; namespace EliteBGS.LogGenerator;
public class MarketBuyFormat : GenericFormat<BuyCargo> { public class MarketBuyFormat : GenericFormat<BuyCargo> {
public override string GenerateSummary(Objective objective) {
long tons = objective
.EnabledOfType<BuyCargo>()
.Sum(x => x.Amount)
;
if (tons <= 0) {
return "";
}
return string.Format("Bought: {0}t", tons);
}
} }

View File

@ -16,8 +16,4 @@ public class MicroResourcesFormat : LogFormatter {
return string.Format("Sold {0} worth of Micro Resources\n", return string.Format("Sold {0} worth of Micro Resources\n",
Credits.FormatCredits(sum)); Credits.FormatCredits(sum));
} }
public string GenerateSummary(Objective objective) {
return "";
}
} }

View File

@ -76,21 +76,4 @@ public class MissionFormat : LogFormatter {
return output.ToString().Trim(); return output.ToString().Trim();
} }
public string GenerateSummary(Objective objective) {
long influence = objective
.EnabledOfType<MissionCompleted>()
.Sum(x => x.Influence.Length)
;
long support = objective
.EnabledOfType<InfluenceSupport>()
.Sum(x => x.Influence.Length)
;
if (influence + support <= 0) {
return "";
}
return string.Format("INF: {0}", influence + support);
}
} }

View File

@ -1,7 +1,9 @@
using EDPlayerJournal; using EDPlayerJournal.BGS;
using EDPlayerJournal.BGS; using System.Collections.Generic;
using System.Text; using System.Text;
using System;
using System.Linq; using System.Linq;
using EDPlayerJournal;
namespace EliteBGS.LogGenerator; namespace EliteBGS.LogGenerator;
@ -34,27 +36,13 @@ public class MurderFormat : LogFormatter {
type = "person"; type = "person";
} }
} }
long bounties = log.Value.Sum(x => x.Bounties); builder.AppendFormat("Murdered {0} {1} (Bounties: {2}, Fines: {3})",
builder.AppendFormat("Murdered {0} {1} (Bounties: {2})\n",
log.Value.Count, type, log.Value.Count, type,
Credits.FormatMillions(bounties) log.Value.Sum(x => x.Bounties),
log.Value.Sum(x => x.Fines)
); );
} }
return builder.ToString().Trim(); return builder.ToString();
}
public string GenerateSummary(Objective objective) {
long murders = objective
.EnabledOfType<FoulMurder>()
.Where(x => x.CrimeType == CrimeTypes.Murder || x.CrimeType == CrimeTypes.OnFootMurder)
.Count()
;
if (murders <= 0) {
return "";
}
return string.Format("Kills: {0}", murders);
} }
} }

View File

@ -1,22 +1,6 @@
using EDPlayerJournal; using EDPlayerJournal.BGS;
using EDPlayerJournal.BGS;
using System.Linq;
namespace EliteBGS.LogGenerator; namespace EliteBGS.LogGenerator;
public class SearchAndRescueFormat : GenericFormat<SearchAndRescue> { public class SearchAndRescueFormat : GenericFormat<SearchAndRescue> {
public override string GenerateSummary(Objective objective) {
long tons = objective
.EnabledOfType<SearchAndRescue>()
.Sum(x => x.Count)
;
long profit = objective
.EnabledOfType<SearchAndRescue>()
.Sum(x => x.Reward)
;
if (tons <= 0) {
return "";
}
return string.Format("S&R: {0}t, {1} profit", tons, Credits.FormatMillions(profit));
}
} }

View File

@ -29,37 +29,4 @@ public class ThargoidFormatter : LogFormatter {
return builder.ToString(); return builder.ToString();
} }
public string GenerateSummary(Objective objective) {
List<ThargoidKill> kills = objective.EnabledOfType<ThargoidKill>().ToList();
if (kills.Count == 0 ) {
return "";
}
int drones = kills.Where(x => x.ThargoidType == ThargoidVessel.Revenant).Count();
int scouts = kills.Where(x => x.ThargoidType == ThargoidVessel.Scout).Count();
int interceptors = kills.Count - scouts - drones;
StringBuilder builder = new StringBuilder();
builder.Append("AX: ");
if (interceptors > 0) {
builder.AppendFormat("{0} INT", interceptors);
}
if (scouts > 0) {
if (interceptors > 0) {
builder.Append(", ");
}
builder.AppendFormat("{0} SCT", scouts);
}
if (drones > 0) {
if (interceptors > 0 || scouts > 0) {
builder.Append(", ");
}
builder.AppendFormat("{0} DRN", drones);
}
return builder.ToString();
}
} }

View File

@ -1,18 +1,6 @@
using EDPlayerJournal; using EDPlayerJournal.BGS;
using EDPlayerJournal.BGS;
using System.Linq;
namespace EliteBGS.LogGenerator; namespace EliteBGS.LogGenerator;
class VistaGenomicsFormat : GenericFormat<OrganicData> { class VistaGenomicsFormat : GenericFormat<OrganicData> {
public override string GenerateSummary(Objective objective) {
long profit = objective
.EnabledOfType<OrganicData>()
.Sum(x => x.TotalValue)
;
if (profit <= 0) {
return "";
}
return string.Format("Organic: {0} Profit", Credits.FormatMillions(profit));
}
} }

View File

@ -19,38 +19,10 @@ public class VoucherFormat : LogFormatter {
} }
foreach (var m in missions) { foreach (var m in missions) {
long total = (long)m.Value.Sum(x => (decimal)x.TotalSum); ulong total = (ulong)m.Value.Sum(x => (decimal)x.TotalSum);
builder.AppendFormat("Handed in {0} vouchers: {1}\n", m.Key, Credits.FormatMillions(total)); builder.AppendFormat("Handed in {0} vouchers: {1}\n", m.Key, Credits.FormatCredits(total));
} }
return builder.ToString().Trim(); return builder.ToString().Trim();
} }
public string GenerateSummary(Objective objective) {
long bounties = objective
.EnabledOfType<Vouchers>()
.Where(x => x.Type == "Bounty")
.Sum(x => x.TotalSum)
;
long bonds = objective
.EnabledOfType<Vouchers>()
.Where(x => x.Type == "Combat Bond")
.Sum(x => x.TotalSum)
;
StringBuilder sb = new();
if (bounties > 0) {
sb.AppendFormat("Bounties: {0}", Credits.FormatMillions(bounties));
}
if (bonds > 0) {
if (sb.Length > 0) {
sb.Append(", ");
}
sb.AppendFormat("Bonds: {0}", Credits.FormatMillions(bonds));
}
return sb.ToString();
}
} }

View File

@ -1,9 +1,10 @@
<mah:MetroWindow <Window
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:abc="http://wpfcontrols.com/"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
xmlns:local="clr-namespace:EliteBGS" xmlns:local="clr-namespace:EliteBGS"
xmlns:Util="clr-namespace:EliteBGS.Util" d:DataContext="{d:DesignInstance Type=Util:AppConfig}" x:Name="window" x:Class="EliteBGS.MainWindow" xmlns:Util="clr-namespace:EliteBGS.Util" d:DataContext="{d:DesignInstance Type=Util:AppConfig}" x:Name="window" x:Class="EliteBGS.MainWindow"
mc:Ignorable="d" mc:Ignorable="d"
@ -14,17 +15,6 @@
</Style> </Style>
<local:MinusFortyFiveConverter x:Key="MinusFortyFiveConverter" /> <local:MinusFortyFiveConverter x:Key="MinusFortyFiveConverter" />
</Window.Resources> </Window.Resources>
<mah:MetroWindow.RightWindowCommands>
<mah:WindowCommands ShowSeparators="False">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,5,0">
<Hyperlink x:Name="URL" NavigateUri="https://bgs.n0la.org/" RequestNavigate="URL_RequestNavigate">Homepage</Hyperlink>
</TextBlock>
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5,0,15,0">
<Hyperlink x:Name="SRC" NavigateUri="https://codeberg.org/nola/EDBGS" RequestNavigate="URL_RequestNavigate">Source</Hyperlink>
</TextBlock>
<mah:ToggleSwitch Content="Dark Theme" x:Name="SwitchTheme" IsOn="True" Toggled="SwitchTheme_Toggled"/>
</mah:WindowCommands>
</mah:MetroWindow.RightWindowCommands>
<Grid> <Grid>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
@ -32,9 +22,9 @@
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TabControl Style="{DynamicResource MahApps.Styles.TabControl.Animated}"> <TabControl>
<TabItem Header="Current Objectives"> <TabItem Header="Current Objectives">
<Grid> <Grid Background="#FFE5E5E5">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
@ -51,16 +41,16 @@
<Button x:Name="ParseJournal" Content="Parse Journal" VerticalAlignment="Center" Click="ParseJournal_Click" HorizontalAlignment="Center"/> <Button x:Name="ParseJournal" Content="Parse Journal" VerticalAlignment="Center" Click="ParseJournal_Click" HorizontalAlignment="Center"/>
<Separator Margin="1" VerticalAlignment="Center" MinWidth="1" HorizontalAlignment="Center" MinHeight="22"/> <Separator Margin="1" VerticalAlignment="Center" MinWidth="1" HorizontalAlignment="Center" MinHeight="22"/>
<Label Content="From (UTC):" VerticalAlignment="Center" VerticalContentAlignment="Center" HorizontalAlignment="Center"/> <Label Content="From (UTC):" VerticalAlignment="Center" VerticalContentAlignment="Center" HorizontalAlignment="Center"/>
<mah:DateTimePicker x:Name="startdate" Height="26.2857142857143" VerticalAlignment="Center" HorizontalAlignment="Center"/> <xctk:DateTimePicker x:Name="startdate" Height="26.2857142857143" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<Label Content="To (UTC):" Height="26.2857142857143" VerticalAlignment="Center" HorizontalAlignment="Center"/> <Label Content="To (UTC):" Height="26.2857142857143" VerticalAlignment="Top"/>
<mah:DateTimePicker x:Name="enddate" Height="26.2857142857143" VerticalAlignment="Center" HorizontalAlignment="Center"/> <xctk:DateTimePicker x:Name="enddate" Height="26.2857142857143" VerticalAlignment="Center" HorizontalAlignment="Center"/>
<Separator Margin="1" VerticalAlignment="Center" MinWidth="1" HorizontalAlignment="Center" MinHeight="22"/> <Separator Margin="1" VerticalAlignment="Center" MinWidth="1" HorizontalAlignment="Center" MinHeight="22"/>
<Button x:Name="ResetTime" Content="Reset Time" Click="ResetTime_Click" /> <Button x:Name="ResetTime" Content="Reset Time" Click="ResetTime_Click" />
<Separator Margin="1" VerticalAlignment="Center" MinWidth="1" HorizontalAlignment="Center" MinHeight="22"/> <Separator Margin="1" VerticalAlignment="Center" MinWidth="1" HorizontalAlignment="Center" MinHeight="22"/>
<Button x:Name="ManuallyParse" Content="Manually Parse" Click="ManuallyParse_Click" /> <Button x:Name="ManuallyParse" Content="Manually Parse" Click="ManuallyParse_Click" />
</ToolBar> </ToolBar>
<ToolBar Grid.Row="1" HorizontalAlignment="Left" Height="Auto" VerticalAlignment="Top" Width="Auto" Grid.ColumnSpan="3"> <ToolBar Grid.Row="1" HorizontalAlignment="Left" Height="36" VerticalAlignment="Top" Width="Auto" Grid.ColumnSpan="3">
<Button x:Name="GenerateDiscord" Content="Generate Discord Report" VerticalAlignment="Stretch" Margin="0,0,0,0" VerticalContentAlignment="Center" Click="GenerateDiscord_Click"/> <Button x:Name="GenerateDiscord" Content="Generate Discord Report" VerticalAlignment="Stretch" Margin="0,0,0,0" VerticalContentAlignment="Center" Click="GenerateDiscord_Click" Height="26"/>
<Separator /> <Separator />
<ComboBox x:Name="LogType" VerticalAlignment="Stretch" Margin="0,3,0,3" Width="140" SelectionChanged="LogType_SelectionChanged" /> <ComboBox x:Name="LogType" VerticalAlignment="Stretch" Margin="0,3,0,3" Width="140" SelectionChanged="LogType_SelectionChanged" />
</ToolBar> </ToolBar>
@ -101,7 +91,7 @@
<HierarchicalDataTemplate> <HierarchicalDataTemplate>
<!-- This will stretch out the width of the item--> <!-- This will stretch out the width of the item-->
<Grid Initialized="Transaction_Initialized" <Grid Initialized="Transaction_Initialized"
HorizontalAlignment="Left" HorizontalAlignment="Stretch"
Width="{Binding ActualWidth, ElementName=entries, Converter={StaticResource MinusFortyFiveConverter}}" Width="{Binding ActualWidth, ElementName=entries, Converter={StaticResource MinusFortyFiveConverter}}"
Margin="0,2,0,2" Margin="0,2,0,2"
> >
@ -160,10 +150,9 @@
<TextBox x:Name="DiscordLog" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="4" Height="Auto" TextWrapping="Wrap" FontFamily="Consolas" FontSize="14" Grid.ColumnSpan="3" AcceptsReturn="True" AcceptsTab="True"/> <TextBox x:Name="DiscordLog" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Grid.Row="4" Height="Auto" TextWrapping="Wrap" FontFamily="Consolas" FontSize="14" Grid.ColumnSpan="3" AcceptsReturn="True" AcceptsTab="True"/>
</Grid> </Grid>
</TabItem> </TabItem>
<TabItem Header="Settings"> <TabItem Header="Settings" HorizontalAlignment="Left" Height="20" VerticalAlignment="Top" Width="53.7142857142857">
<Grid> <Grid Background="#FFE5E5E5">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
@ -182,49 +171,16 @@
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/> <ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<Label Content="Location on disk for the player journal. There is usually no need to change this setting." Grid.Row="0" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.08,0.496"/> <Label Content="Location on disk for the player journal. There is usually no need to change this setting." Grid.Row="0" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top" RenderTransformOrigin="-0.08,0.496"/>
<TextBox x:Name="journallocation" IsReadOnly="true" Text="" Grid.Row="1" Grid.Column="0" Margin="5,0,5,10" TextWrapping="Wrap" /> <TextBox x:Name="journallocation" IsReadOnly="true" Text="" Grid.Row="1" Grid.Column="0" Margin="5,0,5,10" TextWrapping="Wrap" />
<Button x:Name="browsejournallocation" Content="Browse" Grid.Row="1" Grid.Column="1" Margin="0,0,0,0" Width="Auto" VerticalAlignment="Top" HorizontalAlignment="Left" Click="browsejournallocation_Click"/> <Button x:Name="browsejournallocation" Content="Browse" Grid.Row="1" Grid.Column="1" Margin="0,0,0,0" Width="Auto" VerticalAlignment="Top" HorizontalAlignment="Left" Click="browsejournallocation_Click"/>
<Button x:Name="OpenInExplorer" Content="Open Folder" Grid.Row="1" Grid.Column="2" Margin="5,0,0,0" Width="Auto" VerticalAlignment="Top" HorizontalAlignment="Left" Click="OpenInExplorer_Click" />
</Grid>
</GroupBox>
<GroupBox Header="Theme Colour" Height="Auto" Grid.Row="1" VerticalAlignment="Top" Width="Auto" Grid.ColumnSpan="3" Margin="0,5,0,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Content="Colour of the current theme." Grid.Row="0" Grid.ColumnSpan="2" HorizontalAlignment="Left" Margin="0,0,0,0" VerticalAlignment="Top"/>
<ComboBox x:Name="Colour" IsEditable="False" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" Width="Auto" SelectionChanged="Colour_SelectionChanged"/>
</Grid>
</GroupBox>
<GroupBox Header="Advanced Parsing Options" Grid.Row="2" VerticalAlignment="Top" Width="Auto" Grid.ColumnSpan="3" Margin="0,5,0,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<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" />
</Grid> </Grid>
</GroupBox> </GroupBox>
</Grid> </Grid>
</TabItem> </TabItem>
<TabItem Header="Event Log"> <TabItem Header="Event Log" HorizontalAlignment="Left" Height="20" VerticalAlignment="Top">
<Grid> <Grid Background="#FFE5E5E5">
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
@ -232,8 +188,8 @@
<RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
</Grid.RowDefinitions> </Grid.RowDefinitions>
<TextBox IsReadOnly="True" Grid.Row="1" x:Name="log" Height="Auto" Margin="5" TextWrapping="Wrap" FontFamily="Courier New" /> <TextBox IsReadOnly="True" Grid.Row="1" x:Name="log" Height="Auto" Margin="5" TextWrapping="Wrap" FontFamily="Courier New" Background="{x:Null}"/>
<RichTextBox IsReadOnly="True" HorizontalAlignment="Left" Height="Auto" Margin="5" Width="Auto" VerticalContentAlignment="Stretch" VerticalAlignment="Top" BorderBrush="{x:Null}" SelectionBrush="{x:Null}"> <RichTextBox IsReadOnly="True" HorizontalAlignment="Left" Height="Auto" Margin="5" Width="Auto" VerticalContentAlignment="Stretch" VerticalAlignment="Top" Background="{x:Null}" BorderBrush="{x:Null}" SelectionBrush="{x:Null}">
<FlowDocument> <FlowDocument>
<Paragraph> <Paragraph>
<Run Text="This tool does not recognise every option and/or configuration that E:D outputs through its JSON files."/> <Run Text="This tool does not recognise every option and/or configuration that E:D outputs through its JSON files."/>
@ -247,4 +203,4 @@
</TabItem> </TabItem>
</TabControl> </TabControl>
</Grid> </Grid>
</mah:MetroWindow> </Window>

View File

@ -12,18 +12,15 @@ using EDPlayerJournal.Entries;
using EliteBGS.BGS; using EliteBGS.BGS;
using EliteBGS.Util; using EliteBGS.Util;
using System.Globalization; using System.Globalization;
using System.Windows.Forms;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
using MahApps.Metro.Controls;
using ControlzEx.Theming;
using System.Windows.Media;
using System.Diagnostics;
namespace EliteBGS; namespace EliteBGS;
/// <summary> /// <summary>
/// Interaction logic for MainWindow.xaml /// Interaction logic for MainWindow.xaml
/// </summary> /// </summary>
public partial class MainWindow : MetroWindow { public partial class MainWindow : Window {
private PlayerJournal journal; private PlayerJournal journal;
private Report report; private Report report;
@ -58,42 +55,6 @@ public partial class MainWindow : MetroWindow {
LogType.SelectedIndex = 0; LogType.SelectedIndex = 0;
} }
this.NoInfluenceSupport.IsOn = Config.Global.IgnoreInfluenceSupport;
this.NoMarketBuy.IsOn = Config.Global.IgnoreMarketBuy;
this.NoFleetCarrier.IsOn = Config.Global.IgnoreFleetCarrier;
// Apply theme
try {
AddCustomThemes();
string[] colours = ThemeManager.Current.Themes
.Select(x => x.ColorScheme)
.DistinctBy(x => x)
.OrderBy(x => x)
.ToArray()
;
foreach (var colour in colours) {
Colour.Items.Add(colour);
if (!string.IsNullOrEmpty(Config.Global.Colour) &&
string.Compare(colour, Config.Global.Colour, true) == 0) {
Colour.SelectedIndex = Colour.Items.Count - 1;
}
}
if (Colour.SelectedIndex < 0) {
Colour.SelectedIndex = 0;
}
SwitchTheme.IsOn = (string.Compare(Config.Global.Theme, "dark", true) == 0);
ThemeManager.Current.ChangeTheme(this, Config.Global.FullTheme);
} catch (Exception) {
// Theme is invalid, revert back to our standard dark theme
Config.Global.Colour = "Amber";
Config.Global.Theme = "Dark";
}
journal = new PlayerJournal(Config.Global.JournalLocation); journal = new PlayerJournal(Config.Global.JournalLocation);
// Set both to now // Set both to now
@ -102,50 +63,14 @@ public partial class MainWindow : MetroWindow {
journallocation.Text = Config.Global.JournalLocation; journallocation.Text = Config.Global.JournalLocation;
} }
private void AddCustomThemes() {
Dictionary<string, Color> colorThemes = new() {
//{ "HouseSalus", Color.FromRgb(0xBC, 0x94, 0x39) },
{ "HouseSalus", Color.FromRgb(0xED, 0xDA, 0x70) },
{ "NovaNavy", Color.FromRgb(0xA1, 0xA4, 0xDB) },
};
foreach (var colourtheme in colorThemes) {
var brush = new SolidColorBrush(colourtheme.Value);
// Add light theme
ThemeManager.Current.AddTheme(new Theme(
"Light." + colourtheme.Key,
"Light." + colourtheme.Key,
"Light",
colourtheme.Key,
colourtheme.Value,
brush,
true,
false)
);
// Add dark theme
ThemeManager.Current.AddTheme(new Theme(
"Dark." + colourtheme.Key,
"Dark." + colourtheme.Key,
"Dark",
colourtheme.Key,
colourtheme.Value,
brush,
true,
false)
);
}
}
private void InitialiseTime() { private void InitialiseTime() {
DateTime today = DateTime.Today; DateTime today = DateTime.Today;
DateTime tomorrow = today.AddDays(1); DateTime tomorrow = today.AddDays(1);
// HOCHKULTUR startdate.CultureInfo = enddate.CultureInfo = CultureInfo.InvariantCulture;
startdate.Culture = enddate.Culture = CultureInfo.GetCultureInfo("de-AT");
startdate.SelectedDateTime = today; startdate.Value = today;
enddate.SelectedDateTime = tomorrow; enddate.Value = tomorrow;
} }
private void TreeView_CheckBox_Updated(object sender, RoutedEventArgs args) { private void TreeView_CheckBox_Updated(object sender, RoutedEventArgs args) {
@ -169,14 +94,8 @@ public partial class MainWindow : MetroWindow {
private void HandleEntries(List<Entry> entries, DateTime start, DateTime end) { private void HandleEntries(List<Entry> entries, DateTime start, DateTime end) {
try { try {
TransactionParser parser = new(); TransactionParser parser = new TransactionParser();
TransactionParserOptions options = new(); List<Transaction> transactions = parser.Parse(entries);
options.IgnoreInfluenceSupport = Config.Global.IgnoreInfluenceSupport;
options.IgnoreMarketBuy = Config.Global.IgnoreMarketBuy;
options.IgnoreFleetCarrierFaction = Config.Global.IgnoreFleetCarrier;
List<Transaction> transactions = parser.Parse(entries, options);
// Filter the transactions down to the given time frame // Filter the transactions down to the given time frame
transactions = transactions transactions = transactions
@ -201,7 +120,7 @@ public partial class MainWindow : MetroWindow {
} }
private void HandleEntries(List<Entry> entries) { private void HandleEntries(List<Entry> entries) {
HandleEntries(entries, startdate.SelectedDateTime ?? DateTime.Now, enddate.SelectedDateTime ?? DateTime.Now); HandleEntries(entries, startdate.Value ?? DateTime.Now, enddate.Value ?? DateTime.Now);
} }
private void Loadentries_EntriesLoaded(List<Entry> lines) { private void Loadentries_EntriesLoaded(List<Entry> lines) {
@ -212,8 +131,8 @@ public partial class MainWindow : MetroWindow {
try { try {
TransactionParser parser = new TransactionParser(); TransactionParser parser = new TransactionParser();
DateTime start = startdate.SelectedDateTime ?? DateTime.Now; DateTime start = startdate.Value ?? DateTime.Now;
DateTime end = enddate.SelectedDateTime ?? DateTime.Now; DateTime end = enddate.Value ?? DateTime.Now;
journal.Open(); // Load all files journal.Open(); // Load all files
// Log files only get rotated if you restart the game client. This means that there might // Log files only get rotated if you restart the game client. This means that there might
@ -281,7 +200,7 @@ public partial class MainWindow : MetroWindow {
} }
} }
private void entries_KeyUp(object sender, KeyEventArgs e) { private void entries_KeyUp(object sender, System.Windows.Input.KeyEventArgs e) {
if (e.Key == Key.Delete) { if (e.Key == Key.Delete) {
RemoveCurrentObjective(); RemoveCurrentObjective();
} }
@ -300,7 +219,7 @@ public partial class MainWindow : MetroWindow {
} }
private Objective GetObjectiveFromControl(object sender) { private Objective GetObjectiveFromControl(object sender) {
Control control = sender as Control; System.Windows.Controls.Control control = sender as System.Windows.Controls.Control;
if (control == null || control.DataContext == null) { if (control == null || control.DataContext == null) {
return null; return null;
} }
@ -361,19 +280,13 @@ public partial class MainWindow : MetroWindow {
private void window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { private void window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
loadentries?.Close(); loadentries?.Close();
loadentries = null; loadentries = null;
try {
Config.SaveGlobal();
} catch (Exception error) {
MessageBox.Show("There was an error saving your settings: " + error.Message);
}
} }
private void Transaction_Initialized(object sender, EventArgs e) { private void Transaction_Initialized(object sender, EventArgs e) {
} }
private TransactionType GetTransaction<TransactionType>(object sender) where TransactionType : Transaction { private TransactionType GetTransaction<TransactionType>(object sender) where TransactionType : Transaction {
Control button = sender as Control; System.Windows.Controls.Control button = sender as System.Windows.Controls.Control;
if (button == null || button.DataContext == null) { if (button == null || button.DataContext == null) {
return null; return null;
} }
@ -477,14 +390,14 @@ public partial class MainWindow : MetroWindow {
} }
private void ResetTime_Click(object sender, RoutedEventArgs e) { private void ResetTime_Click(object sender, RoutedEventArgs e) {
DateTime? d = startdate.SelectedDateTime; DateTime? d = startdate.Value;
if (d != null) { if (d != null) {
startdate.SelectedDateTime = ResetTimeToZero(d.Value); startdate.Value = ResetTimeToZero(d.Value);
} }
d = enddate.SelectedDateTime; d = enddate.Value;
if (d != null) { if (d != null) {
enddate.SelectedDateTime = ResetTimeToZero(d.Value); enddate.Value = ResetTimeToZero(d.Value);
} }
} }
@ -499,56 +412,4 @@ public partial class MainWindow : MetroWindow {
.ForEach(x => x.IsEnabled = (button.IsChecked ?? true)) .ForEach(x => x.IsEnabled = (button.IsChecked ?? true))
; ;
} }
private void UpdateTheme() {
ThemeManager.Current.ChangeTheme(this, Config.Global.FullTheme);
}
private void SwitchTheme_Toggled(object sender, RoutedEventArgs e) {
ToggleSwitch toggle = sender as ToggleSwitch;
if (toggle.IsOn) {
Config.Global.Theme = "Dark";
} else {
Config.Global.Theme = "Light";
}
UpdateTheme();
}
private void Colour_SelectionChanged(object sender, SelectionChangedEventArgs e) {
if (Colour.SelectedItem == null) {
return;
}
Config.Global.Colour = Colour.SelectedItem.ToString();
UpdateTheme();
}
private void URL_RequestNavigate(object sender, System.Windows.Navigation.RequestNavigateEventArgs e) {
ProcessStartInfo info = new ProcessStartInfo();
info.UseShellExecute = true;
info.FileName = e.Uri.AbsoluteUri;
Process.Start(info);
e.Handled = true;
}
private void NoInfluenceSupport_Toggled(object sender, RoutedEventArgs e) {
Config.Global.IgnoreInfluenceSupport = this.NoInfluenceSupport.IsOn;
}
private void NoMarketBuy_Toggled(object sender, RoutedEventArgs e) {
Config.Global.IgnoreMarketBuy = this.NoMarketBuy.IsOn;
}
private void NoFleetCarrier_Toggled(object sender, RoutedEventArgs e) {
Config.Global.IgnoreFleetCarrier = this.NoFleetCarrier.IsOn;
}
private void OpenInExplorer_Click(object sender, RoutedEventArgs e) {
try {
Process.Start(new ProcessStartInfo(Config.Global.JournalLocation) {
UseShellExecute = true,
});
} catch (Exception) {
}
}
} }

View File

@ -8,7 +8,7 @@ public class MinusFortyFiveConverter : IValueConverter {
/// <inheritdoc/> /// <inheritdoc/>
public object Convert( public object Convert(
object value, Type targetType, object parameter, CultureInfo culture) { object value, Type targetType, object parameter, CultureInfo culture) {
return (double)value - 80; return (double)value - 65;
} }
/// <inheritdoc/> /// <inheritdoc/>

View File

@ -51,12 +51,7 @@ public class NonaDiscordLog : DiscordLogGenerator {
.Count() .Count()
; ;
string summary = GenerateSummary(objective);
log.AppendFormat(":globe_with_meridians: `Target:` {0}\n", location); log.AppendFormat(":globe_with_meridians: `Target:` {0}\n", location);
if (!string.IsNullOrEmpty(summary)) {
log.AppendFormat(":scroll: `Summary:` {0}\n", summary);
}
if (legacycount > 0) { if (legacycount > 0) {
log.Append(":rotating_light: `Warning`: Some actions were done in E:D Legacy\n"); log.Append(":rotating_light: `Warning`: Some actions were done in E:D Legacy\n");
} }

View File

@ -1,8 +1,4 @@
using EliteBGS.LogGenerator; namespace EliteBGS;
using System.Linq;
using System.Text;
namespace EliteBGS;
public class OneLineDiscordLog : DiscordLogGenerator { public class OneLineDiscordLog : DiscordLogGenerator {
protected override string GenerateObjectiveHeader(Objective objective) { protected override string GenerateObjectiveHeader(Objective objective) {
@ -14,7 +10,7 @@ public class OneLineDiscordLog : DiscordLogGenerator {
} }
protected override string GenerateObjectiveFooter(Objective objective) { protected override string GenerateObjectiveFooter(Objective objective) {
return "\n"; return "";
} }
protected override string GenerateFooter() { protected override string GenerateFooter() {
@ -25,38 +21,9 @@ public class OneLineDiscordLog : DiscordLogGenerator {
return ""; return "";
} }
public override string GenerateDiscordLog(Report report) { protected override string TransformFinalLogForObjective(Objective objective, string log) {
StringBuilder log = new StringBuilder(); string[] lines = log.Split("\n", System.StringSplitOptions.RemoveEmptyEntries);
return string.Join(", ", lines);
if (report == null) {
return "";
}
var objectives = report.Objectives
.Where(x => x.IsEnabled && x.Transactions.Count() > 0)
;
if (objectives == null || objectives.Count() <= 0) {
return "";
}
foreach (Objective objective in objectives) {
log.AppendFormat("{0}", GenerateObjectiveHeader(objective));
foreach (LogFormatter formatter in formatters) {
string text = formatter.GenerateSummary(objective);
text = text.Trim();
if (!string.IsNullOrEmpty(text)) {
log.AppendFormat("{0}; ", text);
}
}
log.AppendFormat("{0}", GenerateObjectiveFooter(objective));
}
log.AppendFormat("{0}", GenerateFooter());
return log.ToString().Trim();
} }
public override string ToString() { public override string ToString() {

View File

@ -51,5 +51,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.3.3.0")] [assembly: AssemblyVersion("0.1.1.0")]
[assembly: AssemblyFileVersion("0.3.3.0")] [assembly: AssemblyFileVersion("0.1.1.0")]

View File

@ -117,4 +117,8 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="EliteBGS" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\EliteBGS.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root> </root>

View File

@ -22,14 +22,11 @@ transactions. Currently the tool recognises the following transactions:
* Selling cartography data * Selling cartography data
* Selling of cargo to stations * Selling of cargo to stations
* Selling of micro resources (Odyssey only) * Selling of micro resources (Odyssey only)
* Selling of organic data (Odyssey only)
* Vouchers, including bounty vouchers, combat bonds, and settlement vouchers (aka intel packages) * Vouchers, including bounty vouchers, combat bonds, and settlement vouchers (aka intel packages)
* Thargoid kills * Thargoid kills
* Contributions to Thargoid war effort * Contributions to Thargoid war effort
The following transactions are recognised but not listed, because they do not affect BGS:
* Selling of organic data (Odyssey only)
Vouchers help the faction that is listed for them. If said faction is not present in the 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 current system, then there is no BGS impact. So the tool looks for all system factions, and
makes sure that your vouchers actually have a BGS impact, otherwise it won't list them. makes sure that your vouchers actually have a BGS impact, otherwise it won't list them.
@ -230,7 +227,7 @@ The project also requires:
* `Ookii.Dialogs.WPF` * `Ookii.Dialogs.WPF`
* `Newtonsoft.Json` * `Newtonsoft.Json`
* `MahApps` * `Extended.Wpf.Toolkit`
## About ## About

View File

@ -60,6 +60,16 @@ namespace EliteBGS {
} }
} }
/// <summary>
/// Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
/// </summary>
public static System.Drawing.Icon EliteBGS {
get {
object obj = ResourceManager.GetObject("EliteBGS", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary> /// <summary>
/// Looks up a localized resource of type System.Drawing.Bitmap. /// Looks up a localized resource of type System.Drawing.Bitmap.
/// </summary> /// </summary>

View File

@ -118,6 +118,9 @@
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="EliteBGS" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>EliteBGS.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="logo_v5" type="System.Resources.ResXFileRef, System.Windows.Forms"> <data name="logo_v5" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>logo_v5.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value> <value>logo_v5.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data> </data>

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@ -1,14 +1,20 @@
using Newtonsoft.Json; using System.ComponentModel;
namespace EliteBGS.Util { namespace EliteBGS.Util {
public class AppConfig { public class AppConfig : INotifyPropertyChanged {
private static readonly string default_journal_location = "%UserProfile%\\Saved Games\\Frontier Developments\\Elite Dangerous"; private static readonly string default_journal_location = "%UserProfile%\\Saved Games\\Frontier Developments\\Elite Dangerous";
private string journal_location = default_journal_location; private string journal_location = default_journal_location;
public string DefaultJournalLocation => default_journal_location; private string lastdiscordlog;
private string colour = "Amber";
private string theme = "Dark";
public string LastUsedDiscordTemplate { get; set; } public string DefaultJournalLocation => default_journal_location;
public string LastUsedDiscordTemplate {
get => lastdiscordlog;
set {
lastdiscordlog = value;
FirePropertyChanged("LastUsedDiscordTemplate");
}
}
public string JournalLocation { public string JournalLocation {
get { get {
@ -19,53 +25,14 @@ namespace EliteBGS.Util {
} }
set { set {
journal_location = value; journal_location = value;
FirePropertyChanged("JournalLocation");
} }
} }
public string Theme { private void FirePropertyChanged(string property) {
get { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
return theme;
}
set {
if (string.IsNullOrEmpty(value)) {
theme = "Dark";
} else {
theme = value;
}
}
} }
public string Colour { public event PropertyChangedEventHandler PropertyChanged;
get {
return colour;
}
set {
if (string.IsNullOrEmpty(value)) {
colour = "Blue";
} else {
colour = value;
}
}
}
/// <summary>
/// Whether we ignore influence support scenarios form parsing.
/// </summary>
public bool IgnoreInfluenceSupport { get; set; } = false;
/// <summary>
/// Whether we ignore market buy entries during parsing.
/// </summary>
public bool IgnoreMarketBuy { get; set; } = false;
/// <summary>
/// Whether to ignore fleet carrier stuff when parsing.
/// </summary>
public bool IgnoreFleetCarrier { get; set; } = true;
[JsonIgnore]
public string FullTheme {
get { return Theme + "." + Colour; }
}
} }
} }

View File

@ -12,6 +12,15 @@ namespace EliteBGS.Util {
public Config() { public Config() {
DetermineConfigFolder(); DetermineConfigFolder();
global_config.PropertyChanged += Global_config_PropertyChanged;
}
private void Global_config_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) {
try {
SaveGlobal();
} catch (Exception) {
/* ignored */
}
} }
public string ConfigPath => config_folder; public string ConfigPath => config_folder;
@ -36,7 +45,6 @@ namespace EliteBGS.Util {
filestream.Flush(); filestream.Flush();
using (StreamWriter file = new StreamWriter(filestream, Encoding.UTF8)) { using (StreamWriter file = new StreamWriter(filestream, Encoding.UTF8)) {
var stream = new JsonTextWriter(file); var stream = new JsonTextWriter(file);
stream.Formatting = Formatting.Indented;
serializer.Serialize(stream, global_config); serializer.Serialize(stream, global_config);
} }
} }
@ -50,6 +58,7 @@ namespace EliteBGS.Util {
if (app != null) { if (app != null) {
this.global_config = app; this.global_config = app;
global_config.PropertyChanged += Global_config_PropertyChanged;
} }
} }
} }

View File

@ -1,50 +1,5 @@
# EliteBGS changelog # EliteBGS changelog
## 0.3.3 on 15.05.2023
* Added payout for the Thargoid Glaive.
* Added payout and support for the Thargoid Revenant.
* Added more English mission names.
* Ship and AX combat zones can now be detected through a new event, which
allows the tool to properly determine the difficulty. Combat zone outcome,
additional objectives, or the name of the factions fighting are still guess
work however.
* Fixed a bug where newly added factions to a system weren't properly
recognised. Thanks to Shakaka.
* Added option to ignore FleetCarrier actions.
* Added an "Open in Explorer" button to journal locations.
## 0.3.2 on 19.04.2023
* Orthrus payout has been adapted to 14.02 values.
* Old Thargoid payout values have been removed, so very old logs now no
longer parse correctly. The old value clashed with new ones, making
detection of interceptor kills difficult.
* Fixed a problem regarding the proper detection of station owner, and
thus for whom the tool attributed market buy and market sell.
* Added GUI options to ignore some less important BGS entries.
* One line report has changed to show summaries
* Summaries have been streamlined:
* semi-colons now separate entries
* missing BGS actions (like S&R) have received a summary
* Trade has been renamed to "Sold" and "Bought"
* Sold now shows tons of cargo sold, plus profit if available
* Bought shows tons of cargo bought
## 0.3.1 on 26.02.2023
* Fixed: AX combat zones caused crashes with the summary.
* Fixed: Summaries for thargoid kills didn't render probably.
## 0.3.0 on 26.02.2023
* Move to MahApps toolkit, which replaces extended toolkit.
* Introduce themes (by MahApps) including dark mode.
* Add summary to the two bigger BGS log types.
* Remove organic data from BGS tool, as it has been confirmed that
it does not contribute to the BGS.
* Add a few more English mission names.
## 0.2.6 on 04.02.2023 ## 0.2.6 on 04.02.2023
* Update Post 14.02 Thargoid bounties * Update Post 14.02 Thargoid bounties

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View File

@ -5,7 +5,7 @@ The tool allows you to configure BGS objectives, and will then parse your player
for tasks you completed relating to that BGS objective. Once the JSON player journal has for tasks you completed relating to that BGS objective. Once the JSON player journal has
been parsed, you may then generate a BGS report you can copy/paste into Discord. been parsed, you may then generate a BGS report you can copy/paste into Discord.
Source code is available [here](https://codeberg.org/nola/EDBGS). Source code is available [here](https://git.aror.org/florian/elitebgs).
Binary downloads can be found here: [https://bgs.n0la.org/](https://bgs.n0la.org/). Binary downloads can be found here: [https://bgs.n0la.org/](https://bgs.n0la.org/).
@ -22,14 +22,11 @@ missions. Currently the tool recognises the following completed tasks:
* Selling cartography data * Selling cartography data
* Selling of cargo to stations * Selling of cargo to stations
* Selling of micro resources (Odyssey only) * Selling of micro resources (Odyssey only)
* Selling of organic data (Odyssey only)
* Vouchers, including bounty vouchers, combat bonds, and settlement vouchers (aka intel packages) * Vouchers, including bounty vouchers, combat bonds, and settlement vouchers (aka intel packages)
* Thargoid kills * Thargoid kills
* Combat zones (experimental) * Combat zones (experimental)
The following transactions are recognised but not listed, because they do not affect BGS:
* Selling of organic data (Odyssey only)
Combat zone detection is highly wonky at this time. There is no direct event for detecting 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 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 it will assume on foot combat zones, and if you don't, it will assume a ship CZ. This
@ -236,5 +233,3 @@ It would be helpful if you included the JSON player journal. This player journal
The project also requires `Ookii.Dialogs.WPF` controls, which contains the auto complete text box. The project also requires `Ookii.Dialogs.WPF` controls, which contains the auto complete text box.
And of course, `Newtonsoft.Json` as the JSON parser. And of course, `Newtonsoft.Json` as the JSON parser.
`MahApps` is used to generate a prettier look, and for its dark themes.

View File

@ -68,9 +68,6 @@ The time span you specify must include the day where you accepted the mission,
as well as the day where you failed the mission. Otherwise the tool cannot handle as well as the day where you failed the mission. Otherwise the tool cannot handle
that failed mission. that failed mission.
Prior to update 15 missions only failed once you dismissed them from your transaction
tab. With update 15, this behaviour should be fixed.
### The tool complains about missing factions for an NPC I murdered. ### The tool complains about missing factions for an NPC I murdered.
The player journal only tells the faction that issued the bounty upon murder, and The player journal only tells the faction that issued the bounty upon murder, and

View File

@ -20,13 +20,9 @@ command line:
winget install Microsoft.DotNet.DesktopRuntime.7 winget install Microsoft.DotNet.DesktopRuntime.7
``` ```
You can download the **latest** version **0.3.3** at CodeBerg: You can download the **latest** version **0.2.5** here:
* [https://codeberg.org/nola/EDBGS/releases](https://codeberg.org/nola/EDBGS/releases) * [https://bgs.n0la.org/elitebgs-0.2.5.zip](https://bgs.n0la.org/elitebgs-0.2.5.zip)
Or alternatively from my server:
* [https://bgs.n0la.org/elitebgs-0.3.3.zip](https://bgs.n0la.org/elitebgs-0.3.3.zip)
## Old Versions ## Old Versions
@ -58,14 +54,11 @@ You can then select which of the two actions goes into the final log.
* Selling cartography data * Selling cartography data
* Selling of cargo to stations * Selling of cargo to stations
* Selling of micro resources (Odyssey only) * Selling of micro resources (Odyssey only)
* Selling of organic data (Odyssey only)
* Vouchers, including bounty vouchers, combat bonds, and settlement vouchers (aka intel packages) * Vouchers, including bounty vouchers, combat bonds, and settlement vouchers (aka intel packages)
* Thargoid kills * Thargoid kills
* Combat zones (experimental feature) * Combat zones (experimental feature)
The following transactions are recognised but not listed, because they do not affect BGS:
* Selling of organic data (Odyssey only)
### What it does not detect: ### What it does not detect:
* Combat zone objectives * Combat zone objectives

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 55 KiB

BIN
EliteBGS/logo_v4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 55 KiB

View File

@ -5,14 +5,7 @@ class library EDPlayerJournal, which reads and parses Elite Dangerous player jou
See [https://bgs.n0la.org/](https://bgs.n0la.org) for further details. See [https://bgs.n0la.org/](https://bgs.n0la.org) for further details.
The tool helps with creating BGS reports for squadrons that support factions in
Elite: Dangerous that can be copy and pasted into a Discord server. It finds all BGS
relevant actions in the player journals, and constructs a human readable BGS report
from them:
![MainPage](EliteBGS/main-page.png)
## Requirements ## Requirements
This repository depends on dotnet desktop SDK 7, as well as `Newtonsoft.JSON`, MahApps, This repository depends on dotnet desktop SDK 7, as well as Newtonsoft.JSON, Extended
and Ookii Dialogs. WPF toolkit, and Ookii Dialogs.