46 Commits
0.3.2 ... 0.3.5

Author SHA1 Message Date
acb60e0a08 bump version 2023-09-11 10:51:17 +02:00
dac9b7b8c7 bump assembly version 2023-09-08 11:51:51 +02:00
3338f573c8 update changelog 2023-09-08 11:50:00 +02:00
204d6b8914 rename glaive to hunter
Glaive and Scythe can no longer be distinguished from the bounty claims you get.
2023-09-08 11:49:55 +02:00
4c75515a70 include failed missions in mission format
Since we now have to calculate negative INF with the normal INF (in case both happens), mission formatter now also handles failed missions.
2023-09-08 11:43:05 +02:00
c43c6f742a introduce negative influence
Sometimes missions actually tell us how much negative influence a faction got through secondary influences. We now count this, and present is as negative influence via minuses. Summaries have been updated to reflect this change.
2023-09-08 11:20:49 +02:00
c7a70598c4 don't lose small credit amounts in formatting 2023-09-08 10:21:10 +02:00
cdd7eb33de add settlements for ground CZs to the combat log 2023-08-26 10:53:25 +02:00
dab39b9a4e fix note in FAQ 2023-06-25 19:27:44 +02:00
20c44d41ca fix latest/earliest entry functions 2023-06-18 15:33:07 +02:00
4f339944c6 bump version 2023-06-18 15:25:09 +02:00
ae4e181e7f update changelog 2023-06-18 15:23:38 +02:00
be59588351 improve standard format by adding date range and tool version 2023-06-18 15:22:32 +02:00
1b2a162ac5 add allied to the combat zone object 2023-06-18 15:09:40 +02:00
f490d4ba4d add code to detect which captain/correspondent it was 2023-06-18 15:07:29 +02:00
6b09ec4db3 split captain and correspondent to enemy/allied 2023-06-18 14:55:22 +02:00
d7a1ac5ef2 add another English name 2023-06-13 18:50:58 +02:00
3643dda39e update English mission names 2023-06-01 09:04:28 +02:00
c7be0ef3f5 thargoid bonds are not a system contribution 2023-05-25 18:09:50 +02:00
6a36458026 death only matters on foot 2023-05-25 17:33:59 +02:00
d7dc9bd904 change site to new version number 2023-05-15 19:00:08 +02:00
af66983497 bump version number 2023-05-15 18:59:06 +02:00
3f82e335aa add one more English mission name 2023-05-15 18:56:46 +02:00
77fcbfbba7 update changelog 2023-05-15 18:54:32 +02:00
a45ca9f5bc add option to browse journal location 2023-05-15 18:44:11 +02:00
c9e87958ae implement ignore fleet carrier faction into UI 2023-05-15 18:38:03 +02:00
12b15bb910 fix wrong incomplete type in MarketBuyParser 2023-05-15 18:29:29 +02:00
bec73931b9 add an option to ignore Fleet Carrier faction 2023-05-15 18:15:38 +02:00
b3fca4a63e add faction name for fleet carriers 2023-05-15 17:58:19 +02:00
6a61aa4946 update changelog 2023-05-14 20:22:21 +02:00
b005edc27f don't ignore new system factions
If a factions expands or retreats into a system we see, we won't pick it up if we remain with old data.
2023-05-14 20:20:43 +02:00
8f9f4f3e35 add one more English mission name 2023-05-13 20:19:09 +02:00
7b4176fce5 add drones to the thargoid log 2023-05-13 17:50:26 +02:00
434756695a add information regarding Revenant 2023-05-13 17:47:21 +02:00
a50f0c2313 update description 2023-05-11 20:49:44 +02:00
6dce0116ec update changelog 2023-05-11 20:45:19 +02:00
79914919e5 add combat zone detection through SupercruiseDestinationDrop 2023-05-11 20:43:48 +02:00
c23e8627f6 update instance code 2023-05-11 20:43:30 +02:00
1e36eb3419 fix naming of the event 2023-05-11 20:33:17 +02:00
d49612e7e5 rename class properly 2023-05-11 20:02:53 +02:00
b19a576515 add internal names for combat zones 2023-05-10 09:36:02 +02:00
9ffca16a78 add the new supercruise destination drop event 2023-05-10 09:22:01 +02:00
73a1975964 add payout value for glaive 2023-05-09 21:43:00 +02:00
f9f1842cb7 remove extra bought from log 2023-04-27 16:24:48 +02:00
5487cb3d37 fix log format for murders 2023-04-27 16:24:10 +02:00
e11572a565 update assembly version 2023-04-19 15:52:20 +02:00
45 changed files with 997 additions and 369 deletions

View File

@@ -19,20 +19,36 @@ public class CombatZone : Transaction {
public bool? SpecOps { get; set; }
/// <summary>
/// Whether captain was won
/// Whether allied captain objective was won
/// </summary>
public bool? Captain { get; set; }
public bool? AlliedCaptain { get; set; }
/// <summary>
/// Whether correspondent objective was won
/// Whether enemy captain objective was won
/// </summary>
public bool? Correspondent { get; set; }
public bool? EnemyCaptain { get; set; }
/// <summary>
/// Whether the allied correspondent objective was won
/// </summary>
public bool? AlliedCorrespondent { get; set; }
/// <summary>
/// Whether the enemy correspondent objective was won
/// </summary>
public bool? EnemyCorrespondent { get; set; }
/// <summary>
/// Whether cap ship objective was won
/// </summary>
public bool? CapitalShip { get; set; }
/// <summary>
/// If we have a combat zone, this might point to the settlement
/// in question.
/// </summary>
public string? Settlement { get; set; }
/// <summary>
/// How many optional objectives were completed?
/// </summary>
@@ -41,7 +57,14 @@ public class CombatZone : Transaction {
if (IsGround) {
return 0;
}
return new List<bool?>() { SpecOps, Captain, Correspondent, CapitalShip }
return new List<bool?>() {
SpecOps,
AlliedCaptain,
EnemyCaptain,
AlliedCorrespondent,
EnemyCorrespondent,
CapitalShip
}
.Where(x => x != null && x == true)
.Count()
;

View File

@@ -9,7 +9,7 @@ namespace EDPlayerJournal.BGS;
/// faction to another. Both sometimes gain influence.
/// </summary>
public class InfluenceSupport : Transaction {
public string Influence { get; set; } = "";
public MissionInfluence? Influence { get; set; } = null;
/// <summary>
/// Relevant mission completed entry
@@ -46,7 +46,7 @@ public class InfluenceSupport : Transaction {
builder.AppendFormat("Influence gained from \"{0}\": \"{1}\"",
missionname,
string.IsNullOrEmpty(Influence) ? "NONE" : Influence
Influence == null ? "NONE" : Influence.TrendAdjustedInfluence
);
return builder.ToString();

View File

@@ -38,7 +38,14 @@ public class MissionCompleted : Transaction {
return "";
}
return (CompletedEntry.Mission.GetInfluenceForFaction(Faction, SystemAddress) ?? "");
return string.Join("",
CompletedEntry
.Mission
.GetInfluenceForFaction(Faction, SystemAddress)
.Select(x => x.Influence)
.ToArray()
)
;
}
}
@@ -70,8 +77,10 @@ public class MissionCompleted : Transaction {
var influence = CompletedEntry.Mission.GetInfluenceForFaction(Faction, SystemAddress);
builder.AppendFormat("{0}", MissionName);
if (influence != "") {
builder.AppendFormat(", Influence: {0}", influence);
if (influence != null && influence.Length > 0) {
builder.AppendFormat(", Influence: {0}",
influence.Select(x => x.InfluenceAmount).Sum()
);
}
return builder.ToString();

View File

@@ -13,6 +13,21 @@ public class MissionFailed : Transaction {
Failed = failed;
}
/// <summary>
/// Returns the amount of influence generated by failing this mission. The
/// system returns no influence for one INF missions, but sadly also for
/// when the influence had no affect at all (i.e. during a war).
/// </summary>
public long InfluenceAmount {
get {
if (Mission == null || string.IsNullOrEmpty(Mission.Influence)) {
return -1;
} else {
return Mission.Influence.Length * -1;
}
}
}
public override int CompareTo(Transaction? other) {
if (other == null || other.GetType() != typeof(MissionFailed)) {
return -1;

View File

@@ -0,0 +1,15 @@
using EDPlayerJournal.Entries;
namespace EDPlayerJournal.BGS.Parsers;
internal class ApproachSettlementParser : ITransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
ApproachSettlementEntry? approach = entry as ApproachSettlementEntry;
if (approach == null || string.IsNullOrEmpty(approach.Name)) {
return;
}
context.Settlement = approach.Name;
}
}

View File

@@ -0,0 +1,44 @@
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

@@ -0,0 +1,45 @@
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

@@ -0,0 +1,33 @@
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

@@ -0,0 +1,33 @@
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

@@ -0,0 +1,38 @@
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

@@ -0,0 +1,65 @@
using EDPlayerJournal.Entries;
namespace EDPlayerJournal.BGS;
/// <summary>
/// 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.
/// </summary>
internal class ShipTargetedParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
ShipTargetedEntry? entry = e as ShipTargetedEntry;
if (entry == null) {
throw new NotImplementedException();
}
// Scan happens in stages, and sometimes this information is not known
// yet. Do now throw an error, this is expected behaviour.
if (!string.IsNullOrEmpty(entry.PilotNameLocalised) &&
!string.IsNullOrEmpty(entry.Faction)) {
context.NPCFaction.TryAdd(entry.PilotNameLocalised, entry.Faction);
}
string? faction = context.LastRecordedAwardingFaction;
// We have seen a captain?
if (NPCs.IsWarzoneCaptain(entry.PilotName)) {
// if we have faction information, we can compare it to figure out
// whether it is the enemy or allied faction. but this is not always
// possible. In such a case we assume we have seen the enemy captain.
if (!string.IsNullOrEmpty(entry.Faction) &&
!string.IsNullOrEmpty(faction)) {
if (string.Compare(faction, entry.Faction) != 0) {
context.HaveSeenEnemyCaptain = true;
} else {
context.HaveSeenAlliedCaptain = true;
}
} else {
context.HaveSeenEnemyCaptain = true;
}
}
// Spec ops?
if (NPCs.IsSpecOps(entry.PilotName)) {
context.HaveSeenSpecOps = true;
}
// Correspondent?
if (NPCs.IsWarzoneCorrespondent(entry.PilotName)) {
// if we have faction information, we can compare it to figure out
// whether it is the enemy or allied faction. but this is not always
// possible. In such a case we assume we have seen the enemy
// correspondent.
if (!string.IsNullOrEmpty(entry.Faction) &&
!string.IsNullOrEmpty(faction)) {
if (string.Compare(faction, entry.Faction) != 0) {
context.HaveSeenEnemyCorrespondent = true;
} else {
context.HaveSeenAlliedCorrespondent = true;
}
} else {
context.HaveSeenEnemyCorrespondent = true;
}
}
}
}

View File

@@ -0,0 +1,14 @@
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,4 +1,5 @@
using EDPlayerJournal.Entries;
using EDPlayerJournal.BGS.Parsers;
using EDPlayerJournal.Entries;
namespace EDPlayerJournal.BGS;
@@ -22,6 +23,11 @@ public class TransactionParserOptions {
/// disabled.
/// </summary>
public bool IgnoreMarketBuy { get; set; } = false;
/// <summary>
/// Whether we should ignore things done for the fleet carrier faction.
/// </summary>
public bool IgnoreFleetCarrierFaction { get; set; } = true;
}
public class TransactionList : List<Transaction> {
@@ -35,7 +41,7 @@ public class TransactionList : List<Transaction> {
/// by itself generate any transactions. Location is the best information gatherer here
/// as we are getting controlling faction, system factions, address and station name.
/// </summary>
internal class LocationParser : TransactionParserPart {
internal class LocationParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
LocationEntry? entry = e as LocationEntry;
if (entry == null) {
@@ -65,14 +71,13 @@ internal class LocationParser : TransactionParserPart {
context.StationOwner = null;
}
if (!context.SystemFactions.ContainsKey(entry.StarSystem) &&
entry.SystemFactions != null && entry.SystemFactions.Count > 0) {
if (entry.SystemFactions != null && entry.SystemFactions.Count > 0) {
context.SystemFactions[entry.StarSystem] = entry.SystemFactions;
}
}
}
internal class FSDJumpParser : TransactionParserPart {
internal class FSDJumpParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
FSDJumpEntry? entry = e as FSDJumpEntry;
if (entry == null) {
@@ -89,6 +94,8 @@ internal class FSDJumpParser : TransactionParserPart {
context.DiscernCombatZone(transactions, e);
context.ResetCombatZone();
context.LeftInstance();
context.CurrentSystem = entry.StarSystem;
context.CurrentSystemAddress = entry.SystemAddress;
@@ -98,14 +105,13 @@ internal class FSDJumpParser : TransactionParserPart {
context.ControllingFaction = entry.SystemFaction;
}
if (!context.SystemFactions.ContainsKey(entry.StarSystem) &&
entry.SystemFactions != null && entry.SystemFactions.Count > 0) {
if (entry.SystemFactions != null && entry.SystemFactions.Count > 0) {
context.SystemFactions[entry.StarSystem] = entry.SystemFactions;
}
}
}
internal class DockedParser : TransactionParserPart {
internal class DockedParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
DockedEntry? entry = e as DockedEntry;
if (entry == null) {
@@ -131,46 +137,11 @@ internal class DockedParser : TransactionParserPart {
}
}
/// <summary>
/// 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.
/// </summary>
internal class ShipTargetedParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
ShipTargetedEntry? entry = e as ShipTargetedEntry;
if (entry == null) {
throw new NotImplementedException();
}
// Scan happens in stages, and sometimes this information is not known
// yet. Do now throw an error, this is expected behaviour.
if (!string.IsNullOrEmpty(entry.PilotNameLocalised) &&
!string.IsNullOrEmpty(entry.Faction)) {
context.NPCFaction.TryAdd(entry.PilotNameLocalised, entry.Faction);
}
// We have seen a captain?
if (NPCs.IsWarzoneCaptain(entry.PilotName)) {
context.HaveSeenCaptain = true;
}
// Spec ops?
if (NPCs.IsSpecOps(entry.PilotName)) {
context.HaveSeenSpecOps = true;
}
// Correspondent?
if (NPCs.IsWarzoneCorrespondent(entry.PilotName)) {
context.HaveSeenCorrespondent = true;
}
}
}
/// <summary>
/// Commit crime can result in a transaction, especially if the crime committed is
/// murder.
/// </summary>
internal class CommitCrimeParser : TransactionParserPart {
internal class CommitCrimeParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
CommitCrimeEntry? entry = e as CommitCrimeEntry;
if (entry == null) {
@@ -226,7 +197,7 @@ internal class CommitCrimeParser : TransactionParserPart {
}
}
internal class MissionsParser : TransactionParserPart {
internal class MissionsParser : ITransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
MissionsEntry? missions = entry as MissionsEntry;
@@ -255,7 +226,7 @@ internal class MissionsParser : TransactionParserPart {
}
}
internal class MissionAcceptedParser : TransactionParserPart {
internal class MissionAcceptedParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
MissionAcceptedEntry? entry = e as MissionAcceptedEntry;
if (entry == null) {
@@ -281,7 +252,7 @@ internal class MissionAcceptedParser : TransactionParserPart {
}
}
internal class MissionCompletedParser : TransactionParserPart {
internal class MissionCompletedParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
MissionCompletedEntry? entry = e as MissionCompletedEntry;
if (entry == null || entry.Mission == null) {
@@ -327,20 +298,20 @@ internal class MissionCompletedParser : TransactionParserPart {
if (context.CurrentSystemAddress == null) {
continue;
}
other.Value.Add(context.CurrentSystemAddress.Value, "");
other.Value.Add(context.CurrentSystemAddress.Value, new MissionInfluence());
// Mission gave no influence to the target faction, so we assume
// the target faction was in the same system.
} else if (string.Compare(source_faction_name, faction, true) == 0) {
// This happens if the source faction is not getting any influence
// This could be if the source faction is in a conflict, and thus does
// not gain any influence at all.
other.Value.Add(accepted_location.SystemAddress, "");
other.Value.Add(accepted_location.SystemAddress, new MissionInfluence());
// Just check if the target/source faction are the same, in which case
// we also have to make an additional entry
if (string.Compare(source_faction_name, target_faction_name, true) == 0 &&
context.CurrentSystemAddress != null) {
other.Value.Add(context.CurrentSystemAddress.Value, "");
other.Value.Add(context.CurrentSystemAddress.Value, new MissionInfluence());
}
}
}
@@ -396,7 +367,7 @@ internal class MissionCompletedParser : TransactionParserPart {
}
}
internal class MissionFailedParser : TransactionParserPart {
internal class MissionFailedParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
Mission? mission;
Location? accepted_location;
@@ -451,84 +422,7 @@ internal class MissionFailedParser : TransactionParserPart {
}
}
internal class SellExplorationDataParser : TransactionParserPart {
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;
}
transactions.Add(new Cartographics(entry) {
System = context.CurrentSystem,
Station = context.CurrentStation,
Faction = context.StationOwner,
IsLegacy = context.IsLegacy,
});
}
}
internal class SellOrganicDataParser : TransactionParserPart {
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;
}
transactions.Add(new OrganicData(entry) {
System = context.CurrentSystem,
Station = context.CurrentStation,
Faction = context.StationOwner,
IsLegacy = context.IsLegacy,
});
}
}
internal class MultiSellExplorationDataParser : TransactionParserPart {
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;
}
transactions.Add(new Cartographics(entry) {
System = context.CurrentSystem,
Station = context.CurrentStation,
Faction = context.StationOwner,
IsLegacy = context.IsLegacy,
});
}
}
internal class RedeemVoucherParser : TransactionParserPart {
internal class RedeemVoucherParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
RedeemVoucherEntry? entry = e as RedeemVoucherEntry;
if (entry == null) {
@@ -585,7 +479,7 @@ internal class RedeemVoucherParser : TransactionParserPart {
}
}
internal class SellMicroResourcesParser : TransactionParserPart {
internal class SellMicroResourcesParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
SellMicroResourcesEntry? entry = e as SellMicroResourcesEntry;
if (entry == null) {
@@ -609,7 +503,7 @@ internal class SellMicroResourcesParser : TransactionParserPart {
}
}
internal class SearchAndRescueParser : TransactionParserPart {
internal class SearchAndRescueParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
SearchAndRescueEntry? entry = e as SearchAndRescueEntry;
if (entry == null) {
@@ -633,78 +527,7 @@ internal class SearchAndRescueParser : TransactionParserPart {
}
}
internal class MarketBuyParser : TransactionParserPart {
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 OrganicData(),
"Could not discern the station owner for market buy.",
e);
return;
}
transactions.Add(new BuyCargo(entry) {
System = context.CurrentSystem,
Station = context.CurrentStation,
Faction = context.StationOwner,
IsLegacy = context.IsLegacy,
});
}
}
internal class MarketSellParser : TransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
long profit = 0;
if (context.StationOwner == null) {
transactions.AddIncomplete(
new OrganicData(),
"Could not discern the station owner market sell.",
e);
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,
});
}
}
internal class FactionKillBondParser : TransactionParserPart {
internal class FactionKillBondParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
FactionKillBondEntry? entry = e as FactionKillBondEntry;
if (entry == null) {
@@ -737,7 +560,7 @@ internal class FactionKillBondParser : TransactionParserPart {
}
}
internal class EmbarkDisembarkParser : TransactionParserPart {
internal class EmbarkDisembarkParser : ITransactionParserPart {
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
if (e.Is(Events.Embark)) {
context.IsOnFoot = false;
@@ -747,23 +570,27 @@ internal class EmbarkDisembarkParser : TransactionParserPart {
}
}
internal class SupercruiseEntryParser : TransactionParserPart {
internal class SupercruiseEntryParser : ITransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
// After a super cruise entry we are no longer on foot.
context.IsOnFoot = false;
context.DiscernCombatZone(transactions, entry);
context.ResetCombatZone();
// Supercruise entry means you left the current local instance
context.LeftInstance();
}
}
internal class ShutdownParser : TransactionParserPart {
internal class ShutdownParser : ITransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
context.DiscernCombatZone(transactions, entry);
context.ResetCombatZone();
// Shutdown (logout) means you left the instance
context.LeftInstance();
}
}
internal class CapShipBondParser : TransactionParserPart {
internal class CapShipBondParser : ITransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
if (entry.GetType() != typeof(CapShipBondEntry)) {
return;
@@ -773,7 +600,7 @@ internal class CapShipBondParser : TransactionParserPart {
}
}
internal class FileHeaderParser : TransactionParserPart {
internal class FileHeaderParser : ITransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
FileHeaderEntry? fileheader = entry as FileHeaderEntry;
@@ -785,7 +612,7 @@ internal class FileHeaderParser : TransactionParserPart {
}
}
internal class ReceiveTextParser : TransactionParserPart {
internal class ReceiveTextParser : ITransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
ReceiveTextEntry? receivetext = entry as ReceiveTextEntry;
@@ -803,22 +630,28 @@ internal class ReceiveTextParser : TransactionParserPart {
}
}
internal class DiedParser : TransactionParserPart {
internal class DiedParser : ITransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
// Death only matters in ship. On foot you can just redeploy with the dropship.
if (context.IsOnFoot) {
return;
}
// You can't complete a combat zone if you die in it. Others might keep it open
// for you, but still you will not have completed it unless you jump back in.
context.ResetCombatZone();
// Dying also moves you back to another instance
context.LeftInstance();
}
}
internal class DropshipDeployParser : TransactionParserPart {
internal class DropshipDeployParser : ITransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
// On drop ship deploy we are now on foot
context.IsOnFoot = true;
}
}
internal class CommanderParser : TransactionParserPart {
internal class CommanderParser : ITransactionParserPart {
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
// A commander entry happens when you log out, and log back in again
// for example when switching from Open, to Solo or PG.
@@ -828,8 +661,9 @@ internal class CommanderParser : TransactionParserPart {
}
public class TransactionParser {
private static Dictionary<string, TransactionParserPart> ParserParts { get; } = new()
private static Dictionary<string, ITransactionParserPart> ParserParts { get; } = new()
{
{ Events.ApproachSettlement, new ApproachSettlementParser() },
{ Events.CapShipBond, new CapShipBondParser() },
{ Events.Commander, new CommanderParser() },
{ Events.CommitCrime, new CommitCrimeParser() },
@@ -857,6 +691,7 @@ public class TransactionParser {
{ Events.SellOrganicData, new SellOrganicDataParser() },
{ Events.ShipTargeted, new ShipTargetedParser() },
{ Events.Shutdown, new ShutdownParser() },
{ Events.SupercruiseDestinationDrop, new SupercruiseDestinationDropParser() },
{ Events.SupercruiseEntry, new SupercruiseEntryParser() },
};
@@ -894,7 +729,7 @@ public class TransactionParser {
continue;
}
TransactionParserPart transactionParserPart = ParserParts[entry.Event];
ITransactionParserPart transactionParserPart = ParserParts[entry.Event];
transactionParserPart.Parse(entry, context, options, transactions);
}

View File

@@ -33,6 +33,13 @@ internal class TransactionParserContext {
/// </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>
@@ -44,9 +51,16 @@ internal class TransactionParserContext {
public ulong? HighestCombatBond { get; set; }
public bool HaveSeenCapShip { get; set; } = false;
public bool HaveSeenCaptain { get; set; } = false;
public bool HaveSeenAlliedCaptain { get; set; } = false;
public bool HaveSeenEnemyCaptain { get; set; } = false;
public bool HaveSeenSpecOps { get; set; } = false;
public bool HaveSeenCorrespondent { get; set; } = false;
public bool HaveSeenAlliedCorrespondent { get; set; } = false;
public bool HaveSeenEnemyCorrespondent { get; set; } = false;
/// <summary>
/// Current Odyssey settlement.
/// </summary>
public string? Settlement { get; set; } = null;
/// <summary>
/// Returns true if the current session is legacy
@@ -100,6 +114,15 @@ internal class TransactionParserContext {
/// </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;
Settlement = null;
}
public void DiscernCombatZone(TransactionList transactions, Entry e) {
string? grade = CombatZones.DifficultyLow;
string cztype;
@@ -108,7 +131,8 @@ internal class TransactionParserContext {
if (HighestCombatBond == null &&
LastRecordedAwardingFaction == null &&
HaveSeenAXWarzoneNPC == false) {
HaveSeenAXWarzoneNPC == false &&
CurrentInstanceType == null) {
return;
}
@@ -123,6 +147,47 @@ internal class TransactionParserContext {
} 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) {
@@ -135,7 +200,11 @@ internal class TransactionParserContext {
if (HaveSeenCapShip) {
grade = CombatZones.DifficultyHigh;
} else {
int warzoneNpcs = new List<bool>() { HaveSeenCaptain, HaveSeenCorrespondent, HaveSeenSpecOps }
int warzoneNpcs = new List<bool>() {
HaveSeenEnemyCaptain,
HaveSeenEnemyCorrespondent,
HaveSeenSpecOps
}
.Where(x => x == true)
.Count()
;
@@ -160,13 +229,16 @@ internal class TransactionParserContext {
System = CurrentSystem,
Faction = faction,
IsLegacy = IsLegacy,
Settlement = Settlement,
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,
EnemyCorrespondent = HaveSeenEnemyCorrespondent ? true : null,
AlliedCorrespondent = HaveSeenAlliedCorrespondent ? true : null,
EnemyCaptain = HaveSeenEnemyCaptain ? true : null,
AlliedCaptain = HaveSeenAlliedCaptain ? true : null,
};
zone.Entries.Add(e);
transactions.Add(zone);
@@ -189,8 +261,10 @@ internal class TransactionParserContext {
public void ResetCombatZone() {
HighestCombatBond = null;
HaveSeenCapShip = false;
HaveSeenCaptain = false;
HaveSeenCorrespondent = false;
HaveSeenAlliedCaptain = false;
HaveSeenEnemyCaptain = false;
HaveSeenAlliedCorrespondent = false;
HaveSeenEnemyCorrespondent = false;
HaveSeenSpecOps = false;
LastRecordedAwardingFaction = null;
OnFootKills = 0;

View File

@@ -2,7 +2,7 @@
namespace EDPlayerJournal.BGS;
internal interface TransactionParserPart {
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

View File

@@ -13,16 +13,6 @@ public class Vouchers : Transaction {
Entries.Add(e);
}
public override bool SystemContribution {
get {
if (Faction == Factions.PilotsFederation && Type == "Combat Bond") {
return true;
}
return false;
}
}
public long TotalSum {
get {
if (Faction == null) {

View File

@@ -44,6 +44,6 @@ public class Credits {
return string.Format("{0:0.00}M", millions);
}
return "";
return string.Format("{0}", amount);
}
}

View File

@@ -52,8 +52,9 @@ public class EnglishMissionNames {
{"Mission_Delivery_Retreat_name", "Delivery (Retreat)"},
{"Mission_DeliveryWing_name", "Delivery (Wing)"},
{"Mission_DeliveryWing_War_name", "Delivery (Wing) (War)"},
{"MISSION_Disable_name", "Disable Surface Installation" },
{"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_Elections_name", "Hack Surface Installation (Elections)"},
@@ -106,6 +107,7 @@ public class EnglishMissionNames {
{"Mission_Rescue_Elections_name", "Liberate Hostages (Election)" },
{"Mission_Rescue_name", "Liberate Hostages" },
{"Mission_Rescue_Planet_Expansion_name", "Planet Rescue (Expansion)" },
{"Mission_Rescue_Planet_Retreat_name", "Planet Rescue (Retreat)" },
{"Mission_Rescue_Planet_name", "Planet Rescue"},
{"MISSION_Salvage_CivilUnrest_name", "Salvage (Civil Unrest)"},
{"MISSION_Salvage_Expansion_name", "Salvage (Expansion)"},
@@ -127,6 +129,9 @@ public class EnglishMissionNames {
{"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_OnFoot_Reboot_NR_name", "Reboot Settlement (Thargoid)" },
{"Mission_TW_OnFoot_Reboot_MB_name", "Reboot Settlement (Thargoid)" },
{"Mission_TW_PassengerEvacuation_Burning_name", "Passenger Evacuation (Significant Damage)" },
{"Mission_TW_PassengerEvacuation_UnderAttack_name", "Passenger Evacuation (Thargoid Invasion)" },
{"Mission_TW_Rescue_UnderAttack_name", "Rescue (Thargoid Attack)" },

View File

@@ -0,0 +1,48 @@
namespace EDPlayerJournal.Entries;
public class ApproachSettlementEntry : Entry {
/// <summary>
/// Settlement name
/// </summary>
public string? Name { get; set; } = null;
/// <summary>
/// Market ID of the settlement
/// </summary>
public long? MarketID { get; set; } = null;
/// <summary>
/// System ID
/// </summary>
public long? SystemAddress { get; set; } = null;
/// <summary>
/// Body ID
/// </summary>
public long? BodyID { get; set; } = null;
/// <summary>
/// Name of the planet
/// </summary>
public string? BodyName { get; set; } = null;
/// <summary>
/// Planet latitude
/// </summary>
public double Latitude { get; set; } = 0.0;
/// <summary>
/// Planet longitude
/// </summary>
public double Longitude { get; set; } = 0.0;
protected override void Initialise() {
Name = JSON.Value<string?>("Name");
MarketID = JSON.Value<long?>("MarketID");
SystemAddress = JSON.Value<long?>("SystemID");
BodyID = JSON.Value<long?>("BodyID");
BodyName = JSON.Value<string?>("BodyName");
Longitude = JSON.Value<double?>("Longitude") ?? 0.0;
Latitude = JSON.Value<double?>("Latitude") ?? 0.0;
}
}

View File

@@ -12,6 +12,7 @@ namespace EDPlayerJournal.Entries;
/// </summary>
public class Entry {
private static readonly Dictionary<string, Type> classes = new Dictionary<string, Type> {
{ Events.ApproachSettlement, typeof(ApproachSettlementEntry) },
{ Events.Bounty, typeof(BountyEntry) },
{ Events.CapShipBond, typeof(CapShipBondEntry) },
{ Events.Commander, typeof(CommanderEntry) },
@@ -44,6 +45,7 @@ public class Entry {
{ Events.SellOrganicData, typeof(SellOrganicDataEntry) },
{ Events.ShieldState, typeof(ShieldStateEntry) },
{ Events.ShipTargeted, typeof(ShipTargetedEntry) },
{ Events.SupercruiseDestinationDrop, typeof(SupercruiseDestinationDropEntry) },
{ Events.SupercruiseEntry, typeof(SupercruiseEntryEntry) },
{ Events.SupercruiseExit, typeof(SupercruiseExitEntry) },
{ Events.UnderAttack, typeof(UnderAttackEntry) },

View File

@@ -1,6 +1,7 @@
namespace EDPlayerJournal.Entries;
public class Events {
public static readonly string ApproachSettlement = "ApproachSettlement";
public static readonly string Bounty = "Bounty";
public static readonly string CapShipBond = "CapShipBond";
public static readonly string Commander = "Commander";
@@ -35,6 +36,7 @@ public class Events {
public static readonly string ShieldState = "ShieldState";
public static readonly string ShipTargeted = "ShipTargeted";
public static readonly string Shutdown = "Shutdown";
public static readonly string SupercruiseDestinationDrop = "SupercruiseDestinationDrop";
public static readonly string SupercruiseEntry = "SupercruiseEntry";
public static readonly string SupercruiseExit = "SupercruiseExit";
public static readonly string UnderAttack = "UnderAttack";

View File

@@ -0,0 +1,30 @@
namespace EDPlayerJournal.Entries;
public class 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,6 +55,11 @@ public class Factions {
/// </summary>
public static string Thargoid = "Thargoids";
/// <summary>
/// The faction the fleet carriers use.
/// </summary>
public static string FleetCarrier = "FleetCarrier";
public static bool IsPilotsFederation(string? name) {
if (name == null) {
return false;

View File

@@ -0,0 +1,73 @@
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

@@ -36,6 +36,31 @@ public class MissionInfluence {
/// </summary>
public string Influence { get; set; } = string.Empty;
public long InfluenceAmount {
get {
string trend = TrendAdjustedInfluence;
return (long)
(trend.Count(x => x == '-') * -1) +
trend.Count(x => x == '+')
;
}
}
/// <summary>
/// Returns how much influence was made, represented in pluses for positive influence,
/// and minuses with negative influence. This takes Trend (up, bad etc.) into account.
/// </summary>
public string TrendAdjustedInfluence {
get {
if (!string.IsNullOrEmpty(Trend) &&
Trend.Contains("bad", StringComparison.OrdinalIgnoreCase)) {
return new string('-', Influence.Length);
} else {
return new string('+', Influence.Length);
}
}
}
public static MissionInfluence FromJSON(JObject obj) {
MissionInfluence missionInfluence = new MissionInfluence();
@@ -394,27 +419,29 @@ public class Mission : IComparable<Mission> {
/// <param name="faction">Faction name in question.</param>
/// <param name="systemaddr">Star System address</param>
/// <returns>null if no entry was found, or a string denoting pluses for the amount influence gained.</returns>
public string? GetInfluenceForFaction(string faction, ulong systemaddr) {
public MissionInfluence[]? GetInfluenceForFaction(string faction, ulong systemaddr) {
var results = FactionEffects
.Where(x => string.Compare(x.Faction, faction) == 0)
.SelectMany(x => x.Influences)
.Where(x => (x.SystemAddress != null && x.SystemAddress == systemaddr))
.Select(x => x.Influence)
.Select(x => x)
.ToArray()
;
if (results == null || results.Length == 0) {
return null;
return new MissionInfluence[0];
}
return string.Join("", results);
return results;
}
/// <summary>
/// A convenient Dictionary containing all influences given out by faction,
/// then by system address and then by influence handed out.
/// then by system address and then by influence handed out. Influence can
/// be either a series of "+" for positive influence, or "-" for negative
/// influence.
/// </summary>
public Dictionary<string, Dictionary<ulong, string>> Influences {
public Dictionary<string, Dictionary<ulong, MissionInfluence>> Influences {
get {
return FactionEffects
.Where(x => x.Faction != null)
@@ -422,7 +449,10 @@ public class Mission : IComparable<Mission> {
x => (x.Faction ?? string.Empty),
x => x.Influences
.Where(x => x.SystemAddress != null)
.ToDictionary(x => (x.SystemAddress ?? 0), x => x.Influence)
.ToDictionary(
x => (x.SystemAddress ?? 0),
x => x
)
);
}
}

View File

@@ -8,6 +8,12 @@ public enum ThargoidVessel {
Basilisk = 4,
Medusa = 5,
Hydra = 6,
// Includes Glaive and Scythe
Hunter = 7,
/// <summary>
/// Thargoid drone
/// </summary>
Revenant = 8,
}
public class Thargoid {
@@ -15,8 +21,11 @@ public class Thargoid {
public static Dictionary<ulong, ThargoidVessel> VesselPayout { get; } = new() {
// Up to date values ever since 14.02
{ 25000, ThargoidVessel.Revenant },
{ 65000, ThargoidVessel.Scout },
{ 75000, ThargoidVessel.Scout },
// New in Update 15
{ 4500000, ThargoidVessel.Hunter },
{ 6500000, ThargoidVessel.Cyclops },
{ 20000000, ThargoidVessel.Basilisk },
//{ 25000000, ThargoidVessel.Orthrus },
@@ -39,12 +48,14 @@ public class Thargoid {
public static Dictionary<ThargoidVessel, string?> VesselNames { get; } = new() {
{ ThargoidVessel.Unknown, null },
{ ThargoidVessel.Scout, "Thargoid Scout" },
{ ThargoidVessel.Revenant, "Revenant" },
{ ThargoidVessel.Scout, "Scout" },
{ ThargoidVessel.Orthrus, "Orthrus" },
{ ThargoidVessel.Cyclops, "Cyclops" },
{ ThargoidVessel.Basilisk, "Basilisk" },
{ ThargoidVessel.Medusa, "Medusa" },
{ ThargoidVessel.Hydra, "Hydra" },
{ ThargoidVessel.Hunter, "Hunter" },
};
public static ThargoidVessel GetVesselByPayout(ulong payout) {

View File

@@ -59,9 +59,9 @@ public class MissionTest {
Assert.IsTrue(e.IsEmptyFaction);
Assert.AreEqual(e.Faction, string.Empty);
string? influence = m.GetInfluenceForFaction("", 251012319587UL);
var influence = m.GetInfluenceForFaction("", 251012319587UL);
Assert.IsNotNull(influence);
Assert.AreEqual(influence, "+");
Assert.AreEqual(influence[0].Influence, "+");
e = m.FactionEffects[1];
Assert.AreEqual(e.Faction, "Social LHS 6103 Confederation");
@@ -101,22 +101,25 @@ public class MissionTest {
Assert.AreEqual(effect.Reputation, "++");
string? influence;
influence = m.GetInfluenceForFaction("Salus Imperial Society", 1865919973739UL);
Assert.AreEqual(influence, "++");
var influence = m.GetInfluenceForFaction("Salus Imperial Society", 1865919973739UL);
Assert.IsNotNull(influence);
Assert.IsTrue(influence.Length > 0);
Assert.AreEqual(influence[0].Influence, "++");
influence = m.GetInfluenceForFaction("Salus Imperial Society", 1733186884306UL);
Assert.AreEqual(influence, "++");
Assert.IsNotNull(influence);
Assert.IsTrue(influence.Length > 0);
Assert.AreEqual(influence[0].Influence, "++");
influence = m.GetInfluenceForFaction("Saelishi Saxons", 1733186884306UL);
Assert.IsNull(influence);
Assert.IsNotNull(influence);
Assert.AreEqual(influence.Length, 0);
// Only one entry are we only have Salus
Assert.AreEqual(m.Influences.Count, 1);
Assert.AreEqual(m.Influences["Salus Imperial Society"].Count, 2);
Assert.AreEqual(m.Influences["Salus Imperial Society"][1865919973739UL], "++");
Assert.AreEqual(m.Influences["Salus Imperial Society"][1733186884306UL], "++");
Assert.AreEqual(m.Influences["Salus Imperial Society"][1865919973739UL].Influence, "++");
Assert.AreEqual(m.Influences["Salus Imperial Society"][1733186884306UL].Influence, "++");
}
[TestMethod]

View File

@@ -19,7 +19,13 @@ public class TestTransactionParser {
return;
}
List<Transaction>? transactions = parser.Parse(entries);
var options = new TransactionParserOptions() {
IgnoreInfluenceSupport = false,
IgnoreExoBiology = false,
IgnoreFleetCarrierFaction = false,
IgnoreMarketBuy = false,
};
List<Transaction>? transactions = parser.Parse(entries, options);
Assert.IsNotNull(transactions, "could not parse entries");
Assert.AreEqual(transactions.Count, 3);
@@ -144,7 +150,14 @@ public class TestTransactionParser {
return;
}
List<Transaction>? transactions = parser.Parse(entries);
var options = new TransactionParserOptions() {
IgnoreInfluenceSupport = false,
IgnoreExoBiology = false,
IgnoreFleetCarrierFaction = false,
IgnoreMarketBuy = false,
};
List<Transaction>? transactions = parser.Parse(entries, options);
Assert.IsNotNull(transactions, "could not parse entries");
Assert.AreEqual(transactions.Count, 1);
Assert.IsInstanceOfType(transactions[0], typeof(OrganicData), "result is not of type Organic Data");

View File

@@ -22,14 +22,17 @@ public class ThargoidKills {
Assert.IsNotNull(transactions, "could not parse entries");
Assert.AreEqual(transactions.Count, 3);
// In recent updates the payout was changed, that's why this test reports unknown thargoid vessels
// This test makes sure the new parser does not conflict with legacy values
//
Assert.IsInstanceOfType(transactions[0], typeof(ThargoidKill), "result is not of type ThargoidKill");
Assert.AreEqual(transactions[0].ThargoidType, EDPlayerJournal.ThargoidVessel.Scout);
Assert.AreEqual(transactions[0].ThargoidType, EDPlayerJournal.ThargoidVessel.Unknown);
Assert.IsInstanceOfType(transactions[1], typeof(ThargoidKill), "result is not of type ThargoidKill");
Assert.AreEqual(transactions[1].ThargoidType, EDPlayerJournal.ThargoidVessel.Basilisk);
Assert.AreEqual(transactions[1].ThargoidType, EDPlayerJournal.ThargoidVessel.Unknown);
Assert.IsInstanceOfType(transactions[2], typeof(ThargoidKill), "result is not of type ThargoidKill");
Assert.AreEqual(transactions[2].ThargoidType, EDPlayerJournal.ThargoidVessel.Scout);
Assert.AreEqual(transactions[2].ThargoidType, EDPlayerJournal.ThargoidVessel.Unknown);
}
[TestMethod]

View File

@@ -3,13 +3,13 @@ using System.Linq;
using System.Collections.Generic;
using System.Text;
using EliteBGS.LogGenerator;
using System.Reflection;
namespace EliteBGS;
public class DiscordLogGenerator {
protected List<LogFormatter> formatters = new List<LogFormatter>() {
new MissionFormat(),
new FailedMissionFormat(),
new MurderFormat(),
new VoucherFormat(),
new ThargoidFormatter(),
@@ -23,6 +23,41 @@ public class DiscordLogGenerator {
new SearchAndRescueFormat(),
};
protected virtual string GetToolVersion() {
Version v = Assembly.GetCallingAssembly().GetName().Version;
string ver;
if (v == null) {
ver = "v?.?.?";
} else {
ver = "v" + v.ToString(3);
}
return string.Format("EliteBGS {0}", ver);
}
protected virtual DateTime? GetDateOfEarliestEntry(Objective objective) {
var it = objective
.Transactions
.OrderBy(x => x.CompletedAtDateTime)
.FirstOrDefault()
;
if (it != null) {
return it.CompletedAtDateTime;
}
return null;
}
protected virtual DateTime? GetDateOfLatestEntry(Objective objective) {
var it = objective
.Transactions
.OrderByDescending(x => x.CompletedAtDateTime)
.FirstOrDefault()
;
if (it != null) {
return it.CompletedAtDateTime;
}
return null;
}
protected virtual string GenerateSummary(Objective objective) {
StringBuilder sb = new StringBuilder();
@@ -76,7 +111,18 @@ public class DiscordLogGenerator {
string summary = GenerateSummary(objective);
log.AppendFormat("**Date:** {0}\n", DateTime.Now.ToString("dd/MM/yyyy"));
log.AppendFormat("**Log Generated:** {0} by {1}\n",
DateTime.Now.ToString("dd/MM/yyyy"),
GetToolVersion()
);
var earliest = GetDateOfEarliestEntry(objective);
var latest = GetDateOfLatestEntry(objective);
if (earliest != null && latest != null) {
log.AppendFormat("**Date:** {0} - {1}\n",
GetDateOfEarliestEntry(objective),
GetDateOfLatestEntry(objective)
);
}
log.AppendFormat("**Target:** {0}\n", location);
if (!string.IsNullOrEmpty(summary)) {
log.AppendFormat("**Summary**: {0}\n", summary);

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>net7.0-windows</TargetFramework>
<OutputType>WinExe</OutputType>
<Version>0.3.2</Version>
<Version>0.3.5</Version>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<UseWindowsForms>true</UseWindowsForms>
<UseWPF>true</UseWPF>

View File

@@ -10,7 +10,7 @@ class CombatZoneFormat : LogFormatter {
var logs = objective
.EnabledOfType<CombatZone>()
.OrderBy(x => (CombatZones.DifficultyRank(x.Grade) ?? 0))
.GroupBy(x => new { x.Type, x.Grade })
.GroupBy(x => new { x.Type, x.Grade, x.Settlement })
.ToDictionary(x => x.Key, x => x.ToList())
;
StringBuilder builder = new StringBuilder();
@@ -23,6 +23,11 @@ class CombatZoneFormat : LogFormatter {
int optionals = log.Value
.Sum(x => x.OptionalObjectivesCompleted)
;
var settlements = log.Value
.Select(x => x.Settlement)
.Distinct()
;
string settl = string.Join(", ", settlements);
if (!string.IsNullOrEmpty(log.Key.Grade)) {
builder.AppendFormat("Won {0}x {1} {2} Combat Zone(s)",
log.Value.Count,
@@ -39,6 +44,9 @@ class CombatZoneFormat : LogFormatter {
if (optionals > 0) {
builder.AppendFormat(" (with {0} optional objectives)", optionals);
}
if (!string.IsNullOrEmpty(settl)) {
builder.AppendFormat(" (at {0})", settl);
}
builder.Append("\n");
}

View File

@@ -1,60 +0,0 @@
using System.Linq;
using System.Text;
using EDPlayerJournal.BGS;
namespace EliteBGS.LogGenerator;
public class FailedMissionFormat : LogFormatter {
public string GenerateLog(Objective objective) {
var missions = objective.EnabledOfType<MissionFailed>();
if (missions.Count <= 0) {
return "";
}
StringBuilder builder = new StringBuilder();
var grouping = missions
.GroupBy(x => x.Mission.IsOnFoot)
;
foreach (var group in grouping) {
int amount = group.Count();
if (group.Key) {
builder.AppendFormat("Failed {0} On Foot Mission(s)\n", amount);
} else {
builder.AppendFormat("Failed {0} Ship Mission(s)\n", amount);
}
}
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

@@ -12,6 +12,6 @@ public class MarketBuyFormat : GenericFormat<BuyCargo> {
if (tons <= 0) {
return "";
}
return string.Format("Bought: {0}t bought", tons);
return string.Format("Bought: {0}t", tons);
}
}

View File

@@ -6,17 +6,72 @@ using EDPlayerJournal.BGS;
namespace EliteBGS.LogGenerator;
public class MissionFormat : LogFormatter {
private string GenerateFailedLog(Objective objective) {
var missions = objective.EnabledOfType<MissionFailed>();
if (missions.Count <= 0) {
return "";
}
StringBuilder builder = new StringBuilder();
var grouping = missions
.GroupBy(x => x.Mission.IsOnFoot)
;
foreach (var group in grouping) {
int amount = group.Count();
if (group.Key) {
builder.AppendFormat("Failed {0} On Foot Mission(s)\n", amount);
} else {
builder.AppendFormat("Failed {0} Ship Mission(s)\n", amount);
}
}
return builder.ToString().Trim();
}
private string GenerateFailedSummary(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();
}
public string GenerateLog(Objective objective) {
Dictionary<string, Dictionary<string, int>> collated = new();
Dictionary<string, ulong> passengers = new();
StringBuilder output = new StringBuilder();
int total_influence = 0;
long total_influence = 0;
var missions = objective.EnabledOfType<MissionCompleted>();
var support = objective.EnabledOfType<InfluenceSupport>();
var failed = objective.EnabledOfType<MissionFailed>();
if ((missions == null || missions.Count == 0) &&
(support == null || support.Count == 0)) {
(support == null || support.Count == 0) &&
(failed == null || failed.Count == 0)) {
return "";
}
@@ -60,17 +115,25 @@ public class MissionFormat : LogFormatter {
output.Append("\n");
// Handle failed missions, and add them to the log and influence tally
string failedlog = GenerateFailedLog(objective);
if (!string.IsNullOrEmpty(failedlog)) {
output.Append(failedlog);
output.Append("\n");
}
total_influence += failed.Sum(x => x.InfluenceAmount);
foreach (InfluenceSupport inf in support) {
output.Append(inf.ToString());
output.Append("\n");
total_influence += inf.Influence.Length;
total_influence += inf.Influence.InfluenceAmount;
}
if (support.Count() > 0) {
output.Append("\n");
}
if (total_influence > 0) {
if (total_influence != 0) {
output.AppendFormat("Total Influence: {0}", total_influence);
}
@@ -84,13 +147,24 @@ public class MissionFormat : LogFormatter {
;
long support = objective
.EnabledOfType<InfluenceSupport>()
.Sum(x => x.Influence.Length)
.Sum(x => x.Influence.InfluenceAmount)
;
long failed = objective
.EnabledOfType<MissionFailed>()
.Sum(x => x.InfluenceAmount)
;
if (influence + support <= 0) {
if (influence == 0 && support == 0 && failed == 0) {
return "";
}
return string.Format("INF: {0}", influence + support);
string failedsummary = GenerateFailedSummary(objective);
string summary = string.Format("INF: {0}", influence + support + failed);
if (!string.IsNullOrEmpty(failedsummary)) {
string.Join("; ", summary, failedsummary);
}
return summary;
}
}

View File

@@ -1,9 +1,7 @@
using EDPlayerJournal.BGS;
using System.Collections.Generic;
using EDPlayerJournal;
using EDPlayerJournal.BGS;
using System.Text;
using System;
using System.Linq;
using EDPlayerJournal;
namespace EliteBGS.LogGenerator;
@@ -36,14 +34,14 @@ public class MurderFormat : LogFormatter {
type = "person";
}
}
builder.AppendFormat("Murdered {0} {1} (Bounties: {2}, Fines: {3})",
long bounties = log.Value.Sum(x => x.Bounties);
builder.AppendFormat("Murdered {0} {1} (Bounties: {2})\n",
log.Value.Count, type,
log.Value.Sum(x => x.Bounties),
log.Value.Sum(x => x.Fines)
Credits.FormatMillions(bounties)
);
}
return builder.ToString();
return builder.ToString().Trim();
}
public string GenerateSummary(Objective objective) {

View File

@@ -37,8 +37,9 @@ public class ThargoidFormatter : LogFormatter {
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;
int interceptors = kills.Count - scouts - drones;
StringBuilder builder = new StringBuilder();
@@ -48,10 +49,16 @@ public class ThargoidFormatter : LogFormatter {
}
if (scouts > 0) {
if (interceptors > 0) {
builder.Append(" + ");
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

@@ -120,8 +120,10 @@
<Expander Header="Optional Objectives" Visibility="{Binding IsShipCombatZone}">
<StackPanel Orientation="Vertical">
<ToggleButton x:Name="CapitalShip" Margin="2,0,2,0" Content="Capital Ship" IsChecked="{Binding HasCapitalShip, Mode=TwoWay}" IsThreeState="False"/>
<ToggleButton x:Name="Captain" Margin="2,0,2,0" Content="Captain" IsChecked="{Binding HasCaptain, Mode=TwoWay}" IsThreeState="False"/>
<ToggleButton x:Name="Correspondent" Margin="2,0,2,0" Content="Correspondent" IsChecked="{Binding HasCorrespondent, Mode=TwoWay}" IsThreeState="False"/>
<ToggleButton x:Name="AlliedCaptain" Margin="2,0,2,0" Content="Allied Captain" IsChecked="{Binding HasAlliedCaptain, Mode=TwoWay}" IsThreeState="False"/>
<ToggleButton x:Name="EnemyCaptain" Margin="2,0,2,0" Content="Enemy Captain" IsChecked="{Binding HasEnemyCaptain, Mode=TwoWay}" IsThreeState="False"/>
<ToggleButton x:Name="AlliedCorrespondent" Margin="2,0,2,0" Content="Allied Correspondent" IsChecked="{Binding HasAlliedCorrespondent, Mode=TwoWay}" IsThreeState="False"/>
<ToggleButton x:Name="EnemyCorrespondent" Margin="2,0,2,0" Content="Enemy Correspondent" IsChecked="{Binding HasEnemyCorrespondent, Mode=TwoWay}" IsThreeState="False"/>
<ToggleButton x:Name="SpecOps" Margin="2,0,2,0" Content="Spec Ops" IsChecked="{Binding HasSpecOps, Mode=TwoWay}" IsThreeState="False"/>
</StackPanel>
</Expander>
@@ -182,10 +184,13 @@
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</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"/>
<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="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">
@@ -213,9 +218,9 @@
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<mah:ToggleSwitch x:Name="NoInfluenceSupport" Grid.Row="1" Grid.ColumnSpan="2" Content="Ignore secondary influence support given out by certain missions" Toggled="NoInfluenceSupport_Toggled"/>
<mah:ToggleSwitch x:Name="NoMarketBuy" Grid.Row="2" Grid.ColumnSpan="2" Content="Ignore commodities bought at stations" Toggled="NoMarketBuy_Toggled"/>
<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>
</GroupBox>
</Grid>

View File

@@ -60,6 +60,7 @@ public partial class MainWindow : MetroWindow {
this.NoInfluenceSupport.IsOn = Config.Global.IgnoreInfluenceSupport;
this.NoMarketBuy.IsOn = Config.Global.IgnoreMarketBuy;
this.NoFleetCarrier.IsOn = Config.Global.IgnoreFleetCarrier;
// Apply theme
try {
@@ -173,6 +174,7 @@ public partial class MainWindow : MetroWindow {
options.IgnoreInfluenceSupport = Config.Global.IgnoreInfluenceSupport;
options.IgnoreMarketBuy = Config.Global.IgnoreMarketBuy;
options.IgnoreFleetCarrierFaction = Config.Global.IgnoreFleetCarrier;
List<Transaction> transactions = parser.Parse(entries, options);
@@ -536,4 +538,17 @@ public partial class MainWindow : MetroWindow {
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

@@ -120,14 +120,14 @@ public class UITransaction : INotifyPropertyChanged {
}
}
public bool HasCaptain {
public bool HasEnemyCaptain {
get {
CombatZone combat = Transaction as CombatZone;
if (combat == null) {
return false;
}
return combat.Captain ?? false;
return combat.EnemyCaptain ?? false;
}
set {
CombatZone combat = Transaction as CombatZone;
@@ -135,18 +135,18 @@ public class UITransaction : INotifyPropertyChanged {
return;
}
combat.Captain = value;
combat.EnemyCaptain = value;
}
}
public bool HasCorrespondent {
public bool HasAlliedCaptain {
get {
CombatZone combat = Transaction as CombatZone;
if (combat == null) {
return false;
}
return combat.Correspondent ?? false;
return combat.AlliedCaptain ?? false;
}
set {
CombatZone combat = Transaction as CombatZone;
@@ -154,7 +154,45 @@ public class UITransaction : INotifyPropertyChanged {
return;
}
combat.Correspondent = value;
combat.AlliedCaptain = value;
}
}
public bool HasEnemyCorrespondent {
get {
CombatZone combat = Transaction as CombatZone;
if (combat == null) {
return false;
}
return combat.EnemyCorrespondent ?? false;
}
set {
CombatZone combat = Transaction as CombatZone;
if (combat == null) {
return;
}
combat.EnemyCorrespondent = value;
}
}
public bool HasAlliedCorrespondent {
get {
CombatZone combat = Transaction as CombatZone;
if (combat == null) {
return false;
}
return combat.AlliedCorrespondent ?? false;
}
set {
CombatZone combat = Transaction as CombatZone;
if (combat == null) {
return;
}
combat.AlliedCorrespondent = value;
}
}

View File

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

View File

@@ -58,6 +58,11 @@ namespace EliteBGS.Util {
/// </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

@@ -1,5 +1,36 @@
# EliteBGS changelog
## 0.3.5 on 11.09.2023
* Small bounty voucher formats are no longer suppressed.
* Glaive has been renamed to "Hunter" since Scythe has the same bounty
and they cannot be distinguished.
* Mission influence can now also be negative. Failed missions now properly
report the amount of negative influence given to a faction.
* Mission secondary influences now also support negative influences. This
for example happens if you take a mission to murder another faction's
civlians, which causes negative INF.
## 0.3.4 on 18.06.2023
* Added possibility to specify allied, as well as enemy captain and correspondent.
* Added date range, and tool version to standard log format.
* Added new English mission names.
## 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.

View File

@@ -68,11 +68,14 @@ 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
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 player journal only tells the faction that issued the bounty upon murder, and
not the faction of the NPC killed. The tool has to fetch that from you scanning the
hip. If you didn't fully scan the ship before murdering it, the tool won't know
ship. If you didn't fully scan the ship before murdering it, the tool won't know
the faction of the NPC.
### Why does cartography data, and sold cargo show up for the wrong faction, but for the right station/system?

View File

@@ -20,13 +20,13 @@ command line:
winget install Microsoft.DotNet.DesktopRuntime.7
```
You can download the **latest** version **0.3.2** at CodeBerg:
You can download the **latest** version **0.3.5** at CodeBerg:
* [https://codeberg.org/nola/EDBGS/releases](https://codeberg.org/nola/EDBGS/releases)
Or alternatively from my server:
* [https://bgs.n0la.org/elitebgs-0.3.2.zip](https://bgs.n0la.org/elitebgs-0.3.2.zip)
* [https://bgs.n0la.org/elitebgs-0.3.5.zip](https://bgs.n0la.org/elitebgs-0.3.5.zip)
## Old Versions