Compare commits
174 Commits
Author | SHA1 | Date | |
---|---|---|---|
4cefc393b5 | |||
693d49be14 | |||
32dbde2c82 | |||
894918b5ba | |||
41fca4fa8c | |||
2acb63025c | |||
eaa4bda098 | |||
9d8f0c1b9a | |||
88b770e5ec | |||
d9dd2cc524 | |||
2905696641 | |||
aeaa6b5220 | |||
cdbca10f2d | |||
1da6f41ec8 | |||
2c6eb9190a | |||
8a92cac02a | |||
4fe77e6946 | |||
912e8b602f | |||
be3bceb880 | |||
6cbe54ff73 | |||
6fd5bbc582 | |||
262182cfaf | |||
007b391dc2 | |||
9918c7d559 | |||
2bf8d9018d | |||
8eaf94f634 | |||
20adf93d39 | |||
fd3e5f61cb | |||
e617c3852b | |||
9f013bed38 | |||
916afb2348 | |||
6eb892151c | |||
6a9e4978aa | |||
0203008202 | |||
03621721b8 | |||
ccba55ac35 | |||
0708880284 | |||
18c3073635 | |||
c43d2ff1d3 | |||
a3b7623557 | |||
1c2fc1e2e6 | |||
53da6b4bc2 | |||
bc44ceb205 | |||
4d3048a37d | |||
463598c779 | |||
bf56f3a2d5 | |||
450824733d | |||
110f909c6f | |||
a1099628a0 | |||
7e85159fd5 | |||
2e8daca61c | |||
05b714a607 | |||
3b0492c70f | |||
c20280eb13 | |||
dd863c326e | |||
2012aab7b3 | |||
abb7954614 | |||
0f44a6a9a7 | |||
34e0a0c8ba | |||
b0b82811cc | |||
4855d78823 | |||
5ea288ee86 | |||
f3fc99a3f3 | |||
2bee03dbc2 | |||
16b579688d | |||
9994a45d06 | |||
d6e2280a00 | |||
160f4f8370 | |||
4400418d30 | |||
43037b0a5b | |||
afc831cf31 | |||
4ab54ee576 | |||
d6842115c5 | |||
acb60e0a08 | |||
dac9b7b8c7 | |||
3338f573c8 | |||
204d6b8914 | |||
4c75515a70 | |||
c43c6f742a | |||
c7a70598c4 | |||
cdd7eb33de | |||
dab39b9a4e | |||
20c44d41ca | |||
4f339944c6 | |||
ae4e181e7f | |||
be59588351 | |||
1b2a162ac5 | |||
f490d4ba4d | |||
6b09ec4db3 | |||
d7a1ac5ef2 | |||
3643dda39e | |||
c7be0ef3f5 | |||
6a36458026 | |||
d7dc9bd904 | |||
af66983497 | |||
3f82e335aa | |||
77fcbfbba7 | |||
a45ca9f5bc | |||
c9e87958ae | |||
12b15bb910 | |||
bec73931b9 | |||
b3fca4a63e | |||
6a61aa4946 | |||
b005edc27f | |||
8f9f4f3e35 | |||
7b4176fce5 | |||
434756695a | |||
a50f0c2313 | |||
6dce0116ec | |||
79914919e5 | |||
c23e8627f6 | |||
1e36eb3419 | |||
d49612e7e5 | |||
b19a576515 | |||
9ffca16a78 | |||
73a1975964 | |||
f9f1842cb7 | |||
5487cb3d37 | |||
e11572a565 | |||
16ee7047cd | |||
cedc576b47 | |||
58b8ed07c4 | |||
ef85f51734 | |||
00deaf20fc | |||
5fe652f98c | |||
8cf8d7f529 | |||
fce534c88f | |||
25338c8754 | |||
139cbd05f8 | |||
b0302572f8 | |||
87314d0258 | |||
f4bbd3df2b | |||
d6acbda55c | |||
5c9d9c9153 | |||
da3a355695 | |||
5224dd4555 | |||
15fda7692e | |||
4cd0c02c67 | |||
e39dd6ffda | |||
f666a2c7c5 | |||
8574b74494 | |||
1255bf9fa5 | |||
781bb7a209 | |||
24817a02a9 | |||
d00c8d5c06 | |||
b588050fb4 | |||
f9eb0d34f9 | |||
2a393809fc | |||
7ee734bc33 | |||
5799b3ed16 | |||
a13e8446d2 | |||
0665e64459 | |||
d8ac2a7ee7 | |||
1a8c163fc4 | |||
7b7fdf0ceb | |||
bf86ce3fe8 | |||
bbf3d6c6a4 | |||
d1c232df9d | |||
82281ecf1b | |||
7bf2b028c3 | |||
d2300fa088 | |||
32d7bccadc | |||
831bf38e78 | |||
e864db8488 | |||
0250878ee7 | |||
5120c7991f | |||
3c1abe5e8c | |||
637b4f85d0 | |||
53bf0d22b8 | |||
1c0c864e62 | |||
52aa2706c0 | |||
fd10b86c79 | |||
2f8d32f57a | |||
926150cd18 |
@ -1,10 +1,11 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text;
|
||||
using EDPlayerJournal.Entries;
|
||||
|
||||
namespace EDPlayerJournal.BGS;
|
||||
|
||||
public class Cartographics : Transaction {
|
||||
public Cartographics() { }
|
||||
|
||||
public Cartographics(MultiSellExplorationDataEntry e) {
|
||||
Entries.Add(e);
|
||||
}
|
||||
@ -34,10 +35,10 @@ public class Cartographics : Transaction {
|
||||
/* add multi sell and normal ones together */
|
||||
long total =
|
||||
Entries.OfType<MultiSellExplorationDataEntry>()
|
||||
.Sum(x => x.TotalEarnings)
|
||||
.Sum(x => x.Total)
|
||||
+
|
||||
Entries.OfType<SellExplorationDataEntry>()
|
||||
.Sum(x => x.TotalEarnings)
|
||||
.Sum(x => x.Total)
|
||||
;
|
||||
return total;
|
||||
}
|
||||
|
@ -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()
|
||||
;
|
||||
@ -69,6 +92,13 @@ public class CombatZone : Transaction {
|
||||
get { return string.Compare(Type, CombatZones.AXCombatZone) == 0; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if it is a power combat zone
|
||||
/// </summary>
|
||||
public bool IsPower {
|
||||
get { return string.Compare(Type, CombatZones.PowerCombatZone) == 0; }
|
||||
}
|
||||
|
||||
public override int CompareTo(Transaction? obj) {
|
||||
if (obj == null || obj.GetType() != typeof(CombatZone)) {
|
||||
return -1;
|
||||
|
@ -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();
|
||||
|
32
EDPlayerJournal/BGS/MeritsGained.cs
Normal file
32
EDPlayerJournal/BGS/MeritsGained.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using EDPlayerJournal.Entries;
|
||||
|
||||
namespace EDPlayerJournal.BGS;
|
||||
|
||||
public class MeritsGained : Transaction {
|
||||
public MeritsGained() { }
|
||||
|
||||
public MeritsGained(Entry entry) {
|
||||
Entries.Add(entry);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of merits gained
|
||||
/// </summary>
|
||||
public long Merits {
|
||||
get {
|
||||
return Entries
|
||||
.OfType<PowerplayMeritsEntry>()
|
||||
.Sum(x => x.MeritsGained)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For what power those merits were gained
|
||||
/// </summary>
|
||||
public string Power { get; set; } = string.Empty;
|
||||
|
||||
public override string ToString() {
|
||||
return string.Format("{0} Merits gained for {1}", Merits, Power);
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -4,6 +4,8 @@ using EDPlayerJournal.Entries;
|
||||
namespace EDPlayerJournal.BGS;
|
||||
|
||||
public class OrganicData : Transaction {
|
||||
public OrganicData() { }
|
||||
|
||||
public OrganicData(SellOrganicDataEntry e) {
|
||||
Entries.Add(e);
|
||||
}
|
||||
@ -36,6 +38,8 @@ public class OrganicData : Transaction {
|
||||
|
||||
/* Selling organic data only helps the controlling faction, just like
|
||||
* selling cartographic data.
|
||||
*
|
||||
* Right now: Organic data helps no one.
|
||||
*/
|
||||
public override bool OnlyControllingFaction => true;
|
||||
}
|
||||
|
15
EDPlayerJournal/BGS/Parsers/ApproachSettlementParser.cs
Normal file
15
EDPlayerJournal/BGS/Parsers/ApproachSettlementParser.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
|
30
EDPlayerJournal/BGS/Parsers/CarrierJumpParser.cs
Normal file
30
EDPlayerJournal/BGS/Parsers/CarrierJumpParser.cs
Normal file
@ -0,0 +1,30 @@
|
||||
using EDPlayerJournal.Entries;
|
||||
|
||||
namespace EDPlayerJournal.BGS.Parsers;
|
||||
|
||||
internal class CarrierJumpParser : ITransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
CarrierJump? jump = entry as CarrierJump;
|
||||
|
||||
if (jump == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!jump.Docked) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.CurrentSystem = jump.StarSystem;
|
||||
context.CurrentSystemAddress = jump.SystemAddress;
|
||||
|
||||
context.SystemsByID.TryAdd(jump.SystemAddress, jump.StarSystem);
|
||||
|
||||
if (!string.IsNullOrEmpty(jump.SystemFaction)) {
|
||||
context.ControllingFaction = jump.SystemFaction;
|
||||
}
|
||||
|
||||
if (jump.SystemFactions != null && jump.SystemFactions.Count > 0) {
|
||||
context.SystemFactions[jump.StarSystem] = jump.SystemFactions;
|
||||
}
|
||||
}
|
||||
}
|
17
EDPlayerJournal/BGS/Parsers/CommanderParser.cs
Normal file
17
EDPlayerJournal/BGS/Parsers/CommanderParser.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using EDPlayerJournal.BGS;
|
||||
using EDPlayerJournal.Entries;
|
||||
|
||||
internal class CommanderParser : ITransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
CommanderEntry commanderEntry = (CommanderEntry)entry;
|
||||
if (commanderEntry != null && !string.IsNullOrEmpty(commanderEntry.FullName)) {
|
||||
if (!context.Commanders.Contains(commanderEntry.FullName)) {
|
||||
context.Commanders.Add(commanderEntry.FullName);
|
||||
}
|
||||
}
|
||||
// A commander entry happens when you log out, and log back in again
|
||||
// for example when switching from Open, to Solo or PG.
|
||||
context.DiscernCombatZone(transactions, entry);
|
||||
context.ResetCombatZone();
|
||||
}
|
||||
}
|
44
EDPlayerJournal/BGS/Parsers/MarketBuyParser.cs
Normal file
44
EDPlayerJournal/BGS/Parsers/MarketBuyParser.cs
Normal 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,
|
||||
});
|
||||
}
|
||||
}
|
45
EDPlayerJournal/BGS/Parsers/MarketSellParser.cs
Normal file
45
EDPlayerJournal/BGS/Parsers/MarketSellParser.cs
Normal 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,
|
||||
});
|
||||
}
|
||||
}
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
17
EDPlayerJournal/BGS/Parsers/MusicParser.cs
Normal file
17
EDPlayerJournal/BGS/Parsers/MusicParser.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using EDPlayerJournal.Entries;
|
||||
|
||||
namespace EDPlayerJournal.BGS.Parsers;
|
||||
|
||||
internal class MusicParser : ITransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
MusicEntry? entryMusic = (MusicEntry)entry;
|
||||
|
||||
if (entryMusic == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.Compare(entryMusic.MusicTrack, "Combat_CapitalShip") == 0) {
|
||||
context.HaveSeenCapShip = true;
|
||||
}
|
||||
}
|
||||
}
|
31
EDPlayerJournal/BGS/Parsers/PowerplayMeritsParser.cs
Normal file
31
EDPlayerJournal/BGS/Parsers/PowerplayMeritsParser.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using EDPlayerJournal.Entries;
|
||||
|
||||
namespace EDPlayerJournal.BGS.Parsers;
|
||||
|
||||
internal class PowerplayMeritsParser : ITransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
PowerplayMeritsEntry? e = entry as PowerplayMeritsEntry;
|
||||
if (e == null) {
|
||||
throw new ApplicationException("not a valid PowerplayMerits entry");
|
||||
}
|
||||
|
||||
MeritsGained? transaction = null;
|
||||
|
||||
transaction = transactions
|
||||
.OfType<MeritsGained>()
|
||||
.Where(x => x.System == context.CurrentSystem &&
|
||||
x.Power == e.Power)
|
||||
.FirstOrDefault()
|
||||
;
|
||||
if (transaction == null) {
|
||||
transaction = new MeritsGained(e) {
|
||||
System = context.CurrentSystem,
|
||||
Power = e.Power,
|
||||
Faction = e.Power,
|
||||
};
|
||||
transactions.Add(transaction);
|
||||
} else {
|
||||
transaction.Entries.Add(e);
|
||||
}
|
||||
}
|
||||
}
|
84
EDPlayerJournal/BGS/Parsers/RedeemVoucherParser.cs
Normal file
84
EDPlayerJournal/BGS/Parsers/RedeemVoucherParser.cs
Normal file
@ -0,0 +1,84 @@
|
||||
using EDPlayerJournal.Entries;
|
||||
|
||||
namespace EDPlayerJournal.BGS.Parsers;
|
||||
|
||||
internal class RedeemVoucherParser : ITransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
RedeemVoucherEntry? entry = e as RedeemVoucherEntry;
|
||||
if (entry == null) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
if (context.CurrentSystem == null) {
|
||||
transactions.AddIncomplete(new Vouchers(),
|
||||
"Could not find out where the vouchers were redeemed", e
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
List<Faction>? current_factions = context.GetFactions(context.CurrentSystem);
|
||||
if (current_factions == null) {
|
||||
transactions.AddIncomplete(new Vouchers(),
|
||||
"Current system factions are unknown, so vouchers were ineffective", e);
|
||||
}
|
||||
|
||||
foreach (string faction in entry.Factions) {
|
||||
bool relevantBond = false;
|
||||
string relevantFaction = faction;
|
||||
|
||||
if (string.Compare(faction, Factions.PilotsFederationVouchers) == 0) {
|
||||
// Target faction is pilots' federation, so we assume thargoid bonks
|
||||
// Also assign this combat bond to the Pilots Federation
|
||||
relevantFaction = Factions.PilotsFederation;
|
||||
relevantBond = true;
|
||||
}
|
||||
|
||||
if (current_factions != null && !relevantBond) {
|
||||
// If we have local factions, and it ain't thargoid bonds see if the bonds were
|
||||
// useful in the current system
|
||||
if (current_factions.Find(x => string.Compare(x.Name, faction, true) == 0) != null) {
|
||||
relevantBond = true;
|
||||
} else {
|
||||
transactions.AddIncomplete(new Vouchers(),
|
||||
string.Format("Vouchers for \"{0}\" had no effect in \"{1}\" since said " +
|
||||
"faction is not present there", faction, context.CurrentSystem), e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!relevantBond) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var voucher = new Vouchers(entry) {
|
||||
System = context.CurrentSystem,
|
||||
Station = context.CurrentStation,
|
||||
Faction = relevantFaction,
|
||||
ControllingFaction = context.ControllingFaction,
|
||||
IsLegacy = context.IsLegacy,
|
||||
};
|
||||
|
||||
if (options.FilterDoubleRedeemVouchers) {
|
||||
// To filter out doubly redeemed vouchers, find another redeem voucher
|
||||
// event that happened in the same system, same total sum, and also the
|
||||
// same faction. If there is one, filter this one out.
|
||||
var doubledEntry = transactions
|
||||
.OfType<Vouchers>()
|
||||
.Where(x => x.TotalSum == voucher.TotalSum &&
|
||||
x.System == voucher.System &&
|
||||
x.Faction == voucher.Faction)
|
||||
.ToList()
|
||||
;
|
||||
if (doubledEntry.Count > 0) {
|
||||
transactions.AddIncomplete(
|
||||
voucher,
|
||||
string.Format("A doubled redeem voucher for {0} valued {1} was detected",
|
||||
voucher.Faction, Credits.FormatMillions(voucher.TotalSum)),
|
||||
e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
transactions.Add(voucher);
|
||||
}
|
||||
}
|
||||
}
|
33
EDPlayerJournal/BGS/Parsers/SellExplorationDataParser.cs
Normal file
33
EDPlayerJournal/BGS/Parsers/SellExplorationDataParser.cs
Normal 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,
|
||||
});
|
||||
}
|
||||
}
|
38
EDPlayerJournal/BGS/Parsers/SellOrganicDataParser.cs
Normal file
38
EDPlayerJournal/BGS/Parsers/SellOrganicDataParser.cs
Normal 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,
|
||||
});
|
||||
}
|
||||
}
|
65
EDPlayerJournal/BGS/Parsers/ShipTargetedParser.cs
Normal file
65
EDPlayerJournal/BGS/Parsers/ShipTargetedParser.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
@ -49,10 +49,11 @@ public class SellCargo : Transaction {
|
||||
}
|
||||
|
||||
foreach (MarketSellEntry sell in sold) {
|
||||
builder.AppendFormat("Sold {0} {1} to the {2}",
|
||||
builder.AppendFormat("Sold {0} {1} to the {2} of {3}",
|
||||
sell.Count,
|
||||
Cargo,
|
||||
Market
|
||||
Market,
|
||||
Station
|
||||
);
|
||||
|
||||
if (Profit != 0) {
|
||||
|
@ -1,220 +1,47 @@
|
||||
using EDPlayerJournal;
|
||||
using EDPlayerJournal.BGS.Parsers;
|
||||
using EDPlayerJournal.Entries;
|
||||
|
||||
namespace EDPlayerJournal.BGS;
|
||||
|
||||
internal class TransactionParserContext {
|
||||
public string? CurrentSystem { get; set; }
|
||||
public ulong? CurrentSystemAddress { get; set; }
|
||||
public string? CurrentStation { get; set; }
|
||||
public string? ControllingFaction { get; set; }
|
||||
|
||||
public bool IsOnFoot { get; set; } = false;
|
||||
|
||||
public string? LastRecordedAwardingFaction { get; set; }
|
||||
|
||||
public ulong? HighestCombatBond { get; set; }
|
||||
|
||||
public bool HaveSeenCapShip { get; set; } = false;
|
||||
public bool HaveSeenCaptain { get; set; } = false;
|
||||
public bool HaveSeenSpecOps { get; set; } = false;
|
||||
public bool HaveSeenCorrespondent { get; set; } = false;
|
||||
public class TransactionParserOptions {
|
||||
/// <summary>
|
||||
/// Whether to ignore exo biology. It does not contribute to BGS, so this
|
||||
/// is true per default.
|
||||
/// </summary>
|
||||
public bool IgnoreExoBiology { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the current session is legacy
|
||||
/// Whether to ignore influence support. Usually one only cares about the
|
||||
/// primary faction for the influence.
|
||||
/// </summary>
|
||||
public bool IsLegacy { get; set; } = false;
|
||||
public bool IgnoreInfluenceSupport { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// How many on foot kills were done.
|
||||
/// Whether we ignore power play and merits gained for now. Support for this
|
||||
/// is experimental at the moment, so that is why it ist `true`.
|
||||
/// </summary>
|
||||
public ulong OnFootKills { get; set; } = 0;
|
||||
public bool IgnorePowerplay { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// How many ship kills were done.
|
||||
/// Whether to ignore market buy. Buying from a market gives a small amount
|
||||
/// of INF if it is sold to a high demand market, but generally one buys from
|
||||
/// a market to aid the faction the stuff is being sold to. So allow it to be
|
||||
/// disabled.
|
||||
/// </summary>
|
||||
public ulong ShipKills { get; set; } = 0;
|
||||
public bool IgnoreMarketBuy { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Thargoid scouts killed
|
||||
/// Whether we should ignore things done for the fleet carrier faction.
|
||||
/// </summary>
|
||||
public ulong ThargoidScoutKills { get; set; } = 0;
|
||||
public bool IgnoreFleetCarrierFaction { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Thargoid interceptor kills
|
||||
/// Filter out double redeem vouchers that happen when you redeem a specific
|
||||
/// voucher, and then redeem the rest of your vouchers (say from a KWS) in
|
||||
/// bulk. The bulk redeem will also list the first voucher redeem again in
|
||||
/// its bulk list.
|
||||
/// </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 bool FilterDoubleRedeemVouchers { get; set; } = true;
|
||||
}
|
||||
|
||||
public class TransactionList : List<Transaction> {
|
||||
@ -223,29 +50,13 @@ 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>
|
||||
/// 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
|
||||
/// as we are getting controlling faction, system factions, address and station name.
|
||||
/// </summary>
|
||||
internal class LocationParser : TransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
|
||||
internal class LocationParser : ITransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
LocationEntry? entry = e as LocationEntry;
|
||||
if (entry == null) {
|
||||
throw new NotImplementedException();
|
||||
@ -268,15 +79,20 @@ internal class LocationParser : TransactionParserPart {
|
||||
context.CurrentStation = entry.StationName;
|
||||
}
|
||||
|
||||
if (!context.SystemFactions.ContainsKey(entry.StarSystem) &&
|
||||
entry.SystemFactions != null && entry.SystemFactions.Count > 0) {
|
||||
if (!string.IsNullOrEmpty(entry.StationFaction)) {
|
||||
context.StationOwner = entry.StationFaction;
|
||||
} else {
|
||||
context.StationOwner = null;
|
||||
}
|
||||
|
||||
if (entry.SystemFactions != null && entry.SystemFactions.Count > 0) {
|
||||
context.SystemFactions[entry.StarSystem] = entry.SystemFactions;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class FSDJumpParser : TransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
|
||||
internal class FSDJumpParser : ITransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
FSDJumpEntry? entry = e as FSDJumpEntry;
|
||||
if (entry == null) {
|
||||
throw new NotImplementedException();
|
||||
@ -292,6 +108,8 @@ internal class FSDJumpParser : TransactionParserPart {
|
||||
context.DiscernCombatZone(transactions, e);
|
||||
context.ResetCombatZone();
|
||||
|
||||
context.LeftInstance();
|
||||
|
||||
context.CurrentSystem = entry.StarSystem;
|
||||
context.CurrentSystemAddress = entry.SystemAddress;
|
||||
|
||||
@ -301,15 +119,14 @@ 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 {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
|
||||
internal class DockedParser : ITransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
DockedEntry? entry = e as DockedEntry;
|
||||
if (entry == null) {
|
||||
throw new NotImplementedException();
|
||||
@ -325,7 +142,7 @@ internal class DockedParser : TransactionParserPart {
|
||||
context.SystemsByID.TryAdd(entry.SystemAddress.Value, entry.StarSystem);
|
||||
|
||||
if (!string.IsNullOrEmpty(entry.StationFaction)) {
|
||||
context.ControllingFaction = entry.StationFaction;
|
||||
context.StationOwner = entry.StationFaction;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(entry.StationName)) {
|
||||
@ -334,47 +151,12 @@ 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, 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 {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
|
||||
internal class CommitCrimeParser : ITransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
CommitCrimeEntry? entry = e as CommitCrimeEntry;
|
||||
if (entry == null) {
|
||||
throw new NotImplementedException();
|
||||
@ -429,8 +211,8 @@ internal class CommitCrimeParser : TransactionParserPart {
|
||||
}
|
||||
}
|
||||
|
||||
internal class MissionsParser : TransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
|
||||
internal class MissionsParser : ITransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
MissionsEntry? missions = entry as MissionsEntry;
|
||||
|
||||
if (missions == null) {
|
||||
@ -458,8 +240,8 @@ internal class MissionsParser : TransactionParserPart {
|
||||
}
|
||||
}
|
||||
|
||||
internal class MissionAcceptedParser : TransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
|
||||
internal class MissionAcceptedParser : ITransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
MissionAcceptedEntry? entry = e as MissionAcceptedEntry;
|
||||
if (entry == null) {
|
||||
throw new NotImplementedException();
|
||||
@ -484,15 +266,15 @@ internal class MissionAcceptedParser : TransactionParserPart {
|
||||
}
|
||||
}
|
||||
|
||||
internal class MissionCompletedParser : TransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
|
||||
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) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Mission? mission = null;
|
||||
Location? accepted_location = null;
|
||||
Mission? mission;
|
||||
Location? accepted_location;
|
||||
string? target_faction_name = entry.Mission.TargetFaction;
|
||||
string? source_faction_name = entry.Mission.Faction;
|
||||
|
||||
@ -530,20 +312,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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -577,6 +359,10 @@ internal class MissionCompletedParser : TransactionParserPart {
|
||||
} else if (string.Compare(faction, source_faction_name, true) != 0 ||
|
||||
(string.Compare(faction, source_faction_name) == 0 &&
|
||||
system_address != accepted_location.SystemAddress)) {
|
||||
// Whether we ignore influence support
|
||||
if (options.IgnoreInfluenceSupport) {
|
||||
continue;
|
||||
}
|
||||
// Source or target faction are different, and/or the system
|
||||
// differs. Sometimes missions go to different systems but to
|
||||
// the same faction.
|
||||
@ -595,11 +381,11 @@ internal class MissionCompletedParser : TransactionParserPart {
|
||||
}
|
||||
}
|
||||
|
||||
internal class MissionFailedParser : TransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
|
||||
Mission? mission = null;
|
||||
Location? accepted_location = null;
|
||||
string? accepted_system = null;
|
||||
internal class MissionFailedParser : ITransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
Mission? mission;
|
||||
Location? accepted_location;
|
||||
string? accepted_system;
|
||||
|
||||
MissionFailedEntry? entry = e as MissionFailedEntry;
|
||||
if (entry == null) {
|
||||
@ -650,191 +436,56 @@ internal class MissionFailedParser : TransactionParserPart {
|
||||
}
|
||||
}
|
||||
|
||||
internal class SellExplorationDataParser : TransactionParserPart {
|
||||
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;
|
||||
if (entry == null) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
if (context.CurrentSystem == null) {
|
||||
transactions.AddIncomplete(new Vouchers(),
|
||||
"Could not find out where the vouchers were redeemed", e
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
List<Faction>? current_factions = context.GetFactions(context.CurrentSystem);
|
||||
if (current_factions == null) {
|
||||
transactions.AddIncomplete(new Vouchers(),
|
||||
"Current system factions are unknown, so vouchers were ineffective", e);
|
||||
}
|
||||
|
||||
foreach (string faction in entry.Factions) {
|
||||
bool relevantBond = false;
|
||||
string relevantFaction = faction;
|
||||
|
||||
if (string.Compare(faction, Factions.PilotsFederationVouchers) == 0) {
|
||||
// Target faction is pilots' federation, so we assume thargoid bonks
|
||||
// Also assign this combat bond to the Pilots Federation
|
||||
relevantFaction = Factions.PilotsFederation;
|
||||
relevantBond = true;
|
||||
}
|
||||
|
||||
if (current_factions != null && !relevantBond) {
|
||||
// If we have local factions, and it ain't thargoid bonds see if the bonds were
|
||||
// useful in the current system
|
||||
if (current_factions.Find(x => string.Compare(x.Name, faction, true) == 0) != null) {
|
||||
relevantBond = true;
|
||||
} else {
|
||||
transactions.AddIncomplete(new Vouchers(),
|
||||
string.Format("Vouchers for \"{0}\" had no effect in \"{1}\" since said " +
|
||||
"faction is not present there", faction, context.CurrentSystem), e
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (relevantBond) {
|
||||
transactions.Add(new Vouchers(entry) {
|
||||
System = context.CurrentSystem,
|
||||
Station = context.CurrentStation,
|
||||
Faction = relevantFaction,
|
||||
ControllingFaction = context.ControllingFaction,
|
||||
IsLegacy = context.IsLegacy,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class SellMicroResourcesParser : TransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
|
||||
internal class SellMicroResourcesParser : ITransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
SellMicroResourcesEntry? entry = e as SellMicroResourcesEntry;
|
||||
if (entry == null) {
|
||||
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) {
|
||||
System = context.CurrentSystem,
|
||||
Station = context.CurrentStation,
|
||||
Faction = context.ControllingFaction,
|
||||
Faction = context.StationOwner,
|
||||
IsLegacy = context.IsLegacy,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal class SearchAndRescueParser : TransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
|
||||
internal class SearchAndRescueParser : ITransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
SearchAndRescueEntry? entry = e as SearchAndRescueEntry;
|
||||
if (entry == null) {
|
||||
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) {
|
||||
Faction = context.ControllingFaction,
|
||||
Station = context.CurrentStation,
|
||||
System = context.CurrentSystem,
|
||||
Station = context.CurrentStation,
|
||||
Faction = context.StationOwner,
|
||||
IsLegacy = context.IsLegacy,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal class MarketBuyParser : TransactionParserPart {
|
||||
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) {
|
||||
internal class FactionKillBondParser : ITransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
FactionKillBondEntry? entry = e as FactionKillBondEntry;
|
||||
if (entry == null) {
|
||||
throw new NotImplementedException();
|
||||
@ -866,8 +517,8 @@ internal class FactionKillBondParser : TransactionParserPart {
|
||||
}
|
||||
}
|
||||
|
||||
internal class EmbarkDisembarkParser : TransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionList transactions) {
|
||||
internal class EmbarkDisembarkParser : ITransactionParserPart {
|
||||
public void Parse(Entry e, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
if (e.Is(Events.Embark)) {
|
||||
context.IsOnFoot = false;
|
||||
} else if (e.Is(Events.Disembark)) {
|
||||
@ -876,24 +527,28 @@ internal class EmbarkDisembarkParser : TransactionParserPart {
|
||||
}
|
||||
}
|
||||
|
||||
internal class SupercruiseEntryParser : TransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
|
||||
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 {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
|
||||
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 {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
|
||||
internal class CapShipBondParser : ITransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
if (entry.GetType() != typeof(CapShipBondEntry)) {
|
||||
return;
|
||||
}
|
||||
@ -902,8 +557,8 @@ internal class CapShipBondParser : TransactionParserPart {
|
||||
}
|
||||
}
|
||||
|
||||
internal class FileHeaderParser : TransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
|
||||
internal class FileHeaderParser : ITransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
FileHeaderEntry? fileheader = entry as FileHeaderEntry;
|
||||
|
||||
if (fileheader == null) {
|
||||
@ -914,8 +569,8 @@ internal class FileHeaderParser : TransactionParserPart {
|
||||
}
|
||||
}
|
||||
|
||||
internal class ReceiveTextParser : TransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
|
||||
internal class ReceiveTextParser : ITransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
ReceiveTextEntry? receivetext = entry as ReceiveTextEntry;
|
||||
|
||||
if (receivetext == null) {
|
||||
@ -932,34 +587,44 @@ internal class ReceiveTextParser : TransactionParserPart {
|
||||
}
|
||||
}
|
||||
|
||||
internal class DiedParser : TransactionParserPart {
|
||||
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
|
||||
// for you, but still you will not have completed it unless you jump back in.
|
||||
context.ResetCombatZone();
|
||||
internal class SelfDestructParser : ITransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionParserOptions options, TransactionList transactions) {
|
||||
context.SelfDestruct = true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class DropshipDeployParser : TransactionParserPart {
|
||||
public void Parse(Entry entry, TransactionParserContext context, TransactionList transactions) {
|
||||
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;
|
||||
}
|
||||
if (context.SelfDestruct != null && context.SelfDestruct == true) {
|
||||
// Some people just suicide to fast track back to stations after a CZ,
|
||||
// especially since combat bonds don't disappear on death. So count the CZ
|
||||
// on self destruct
|
||||
context.DiscernCombatZone(transactions, entry);
|
||||
context.SelfDestruct = null;
|
||||
}
|
||||
context.ResetCombatZone();
|
||||
// Dying also moves you back to another instance
|
||||
context.LeftInstance();
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
public void Parse(Entry entry, TransactionParserContext context, 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.
|
||||
context.DiscernCombatZone(transactions, entry);
|
||||
context.ResetCombatZone();
|
||||
}
|
||||
}
|
||||
|
||||
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.CarrierJump, new CarrierJumpParser() },
|
||||
{ Events.Commander, new CommanderParser() },
|
||||
{ Events.CommitCrime, new CommitCrimeParser() },
|
||||
{ Events.Died, new DiedParser() },
|
||||
@ -978,14 +643,18 @@ public class TransactionParser {
|
||||
{ Events.MissionFailed, new MissionFailedParser() },
|
||||
{ Events.Missions, new MissionsParser() },
|
||||
{ Events.MultiSellExplorationData, new MultiSellExplorationDataParser() },
|
||||
{ Events.Music, new MusicParser() },
|
||||
{ Events.PowerplayMerits, new PowerplayMeritsParser() },
|
||||
{ Events.ReceiveText, new ReceiveTextParser() },
|
||||
{ Events.RedeemVoucher, new RedeemVoucherParser() },
|
||||
{ Events.SearchAndRescue, new SearchAndRescueParser() },
|
||||
{ Events.SelfDestruct, new SelfDestructParser() },
|
||||
{ Events.SellExplorationData, new SellExplorationDataParser() },
|
||||
{ Events.SellMicroResources, new SellMicroResourcesParser() },
|
||||
{ Events.SellOrganicData, new SellOrganicDataParser() },
|
||||
{ Events.ShipTargeted, new ShipTargetedParser() },
|
||||
{ Events.Shutdown, new ShutdownParser() },
|
||||
{ Events.SupercruiseDestinationDrop, new SupercruiseDestinationDropParser() },
|
||||
{ Events.SupercruiseEntry, new SupercruiseEntryParser() },
|
||||
};
|
||||
|
||||
@ -1000,7 +669,22 @@ public class TransactionParser {
|
||||
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) {
|
||||
TransactionParserOptions defaultOptions = new();
|
||||
return Parse(entries, defaultOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// List of commanders seen during parsing.
|
||||
/// </summary>
|
||||
public List<string> Commanders { get; set; } = new();
|
||||
|
||||
public List<Transaction>? Parse(IEnumerable<Entry> entries, TransactionParserOptions options) {
|
||||
TransactionList transactions = new();
|
||||
TransactionParserContext context = new();
|
||||
|
||||
@ -1013,10 +697,13 @@ public class TransactionParser {
|
||||
continue;
|
||||
}
|
||||
|
||||
TransactionParserPart transactionParserPart = ParserParts[entry.Event];
|
||||
transactionParserPart.Parse(entry, context, transactions);
|
||||
ITransactionParserPart transactionParserPart = ParserParts[entry.Event];
|
||||
transactionParserPart.Parse(entry, context, options, transactions);
|
||||
}
|
||||
|
||||
// Copy out list of commanders seen
|
||||
Commanders = context.Commanders;
|
||||
|
||||
return transactions.ToList();
|
||||
}
|
||||
}
|
||||
|
354
EDPlayerJournal/BGS/TransactionParserContext.cs
Normal file
354
EDPlayerJournal/BGS/TransactionParserContext.cs
Normal file
@ -0,0 +1,354 @@
|
||||
using EDPlayerJournal.Entries;
|
||||
|
||||
namespace EDPlayerJournal.BGS;
|
||||
|
||||
internal class TransactionParserContext {
|
||||
/// <summary>
|
||||
/// List of commander names seen in the logs. May be empty.
|
||||
/// </summary>
|
||||
public List<string> Commanders { get; } = new();
|
||||
|
||||
/// <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 HaveSeenAlliedCaptain { get; set; } = false;
|
||||
public bool HaveSeenEnemyCaptain { get; set; } = false;
|
||||
public bool HaveSeenSpecOps { get; set; } = false;
|
||||
public bool HaveSeenAlliedCorrespondent { get; set; } = false;
|
||||
public bool HaveSeenEnemyCorrespondent { get; set; } = false;
|
||||
|
||||
public bool? SelfDestruct { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Current Odyssey settlement.
|
||||
/// </summary>
|
||||
public string? Settlement { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Current Merits
|
||||
/// </summary>
|
||||
public long? CurrentMerits { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Merits from last login
|
||||
/// </summary>
|
||||
public long? LastMerits { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the current session is legacy
|
||||
/// </summary>
|
||||
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;
|
||||
Settlement = null;
|
||||
}
|
||||
|
||||
private bool HadCombatZone() {
|
||||
if (CurrentInstanceType != null &&
|
||||
Instances.IsInstance(CurrentInstanceType, Instances.PowerWarzoneMedium)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (HighestCombatBond == null &&
|
||||
LastRecordedAwardingFaction == null &&
|
||||
HaveSeenAXWarzoneNPC == false &&
|
||||
CurrentInstanceType == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void DiscernCombatZone(TransactionList transactions, Entry e) {
|
||||
string? grade = CombatZones.DifficultyLow;
|
||||
string cztype;
|
||||
ulong highest = HighestCombatBond ?? 0;
|
||||
string? faction = LastRecordedAwardingFaction;
|
||||
|
||||
if (!HadCombatZone()) {
|
||||
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) ||
|
||||
Instances.IsPowerWarzone(CurrentInstanceType))) {
|
||||
transactions.AddIncomplete(new CombatZone(),
|
||||
"Could not discern for whom you fought for, " +
|
||||
"as it seems you haven't killed anyone in the ship combat zone.",
|
||||
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 if (Instances.IsInstance(CurrentInstanceType, Instances.PowerWarzoneMedium)) {
|
||||
cztype = CombatZones.PowerCombatZone;
|
||||
grade = CombatZones.DifficultyMedium;
|
||||
} 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>() {
|
||||
HaveSeenEnemyCaptain,
|
||||
HaveSeenEnemyCorrespondent,
|
||||
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,
|
||||
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,
|
||||
EnemyCorrespondent = HaveSeenEnemyCorrespondent ? true : null,
|
||||
AlliedCorrespondent = HaveSeenAlliedCorrespondent ? true : null,
|
||||
EnemyCaptain = HaveSeenEnemyCaptain ? true : null,
|
||||
AlliedCaptain = HaveSeenAlliedCaptain ? 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;
|
||||
HaveSeenAlliedCaptain = false;
|
||||
HaveSeenEnemyCaptain = false;
|
||||
HaveSeenAlliedCorrespondent = false;
|
||||
HaveSeenEnemyCorrespondent = 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);
|
||||
}
|
||||
}
|
19
EDPlayerJournal/BGS/TransactionParserPart.cs
Normal file
19
EDPlayerJournal/BGS/TransactionParserPart.cs
Normal file
@ -0,0 +1,19 @@
|
||||
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);
|
||||
}
|
@ -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) {
|
||||
|
@ -14,6 +14,11 @@ public class CombatZones {
|
||||
/// </summary>
|
||||
public static readonly string ShipCombatZone = "Ship";
|
||||
|
||||
/// <summary>
|
||||
/// Power combat zones, new in Ascendancy update.
|
||||
/// </summary>
|
||||
public static readonly string PowerCombatZone = "Power";
|
||||
|
||||
/// <summary>
|
||||
/// AX combat zone
|
||||
/// </summary>
|
||||
@ -38,4 +43,27 @@ public class CombatZones {
|
||||
/// Very high difficulty, so far AX combat zone only
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -34,4 +34,16 @@ public class Credits {
|
||||
|
||||
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 string.Format("{0}", amount);
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ public class EnglishMissionNames {
|
||||
{"Mission_AltruismCredits_name", "Donate Credits"},
|
||||
{"Mission_AltruismCredits_Outbreak_name", "Donate Credits (Outbreak)" },
|
||||
{"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_CivilUnrest_name", "Assassination (Civil Unrest, Legal)" },
|
||||
{"Mission_Assassinate_Legal_Communism_name", "Assassination (Communism, Legal)" },
|
||||
@ -51,6 +52,10 @@ public class EnglishMissionNames {
|
||||
{"Mission_Delivery_Retreat_name", "Delivery (Retreat)"},
|
||||
{"Mission_DeliveryWing_name", "Delivery (Wing)"},
|
||||
{"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_Elections_name", "Hack Surface Installation (Elections)"},
|
||||
{"Mission_Hack_BLOPS_Expansion_name", "Hack Surface Installation (Expansion)"},
|
||||
@ -65,24 +70,30 @@ public class EnglishMissionNames {
|
||||
{"Mission_MassacreWing_name", "Massacre (Wing)"},
|
||||
{"Mission_Mining_name", "Mining" },
|
||||
{"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_NCD_MB_name", "On Foot Assassination (Illegal)" },
|
||||
{"Mission_OnFoot_Collect_Contact_MB_name", "On Foot Collect"},
|
||||
{"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_Hack_Upload_Covert_MB_name", "On Foot Hack (Covert Upload)"},
|
||||
{"Mission_OnFoot_Hack_Upload_MB_name", "On Foot Hack (Upload)"},
|
||||
{"Mission_OnFoot_Heist_MB_name", "On Foot Heist" },
|
||||
{"Mission_OnFoot_Heist_POI_MB_name", "On Foot Heist (POI)"},
|
||||
{"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_Offline_MB_name", "On Foot Onslaught (Offline)"},
|
||||
{"Mission_OnFoot_ProductionHeist_Covert_MB_name", "On Foot Production Heist (Covert)"},
|
||||
{"Mission_OnFoot_ProductionHeist_MB_name", "On Foot Production Heist"},
|
||||
{"Mission_OnFoot_Reboot_MB_name", "On Foot Reboot"},
|
||||
{"Mission_OnFoot_RebootRestore_MB_name", "On Foot Reboot/Restore"},
|
||||
{"Mission_OnFoot_Sabotage_Production_Covert_MB_name", "On Foot Sabotage Production (Covert)"},
|
||||
{"Mission_OnFoot_Sabotage_Power_MB_name", "On Foot Power Sabotage"},
|
||||
{"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_SalvageIllegal_MB_name", "On Foot Salvage (Illegal)"},
|
||||
{"Mission_OnFoot_Smuggle_Contact_MB_name", "On Foot Smuggling" },
|
||||
@ -93,8 +104,11 @@ public class EnglishMissionNames {
|
||||
{"Mission_PassengerVIP_name", "Passenger (VIP)"},
|
||||
{"Mission_PassengerVIP_Scientist_FAMINE_name", "Passenger Scientist (VIP) (Famine)"},
|
||||
{"Mission_PassengerVIP_Tourist_BOOM_name", "Passenger Tourist (VIP) (Boom)"},
|
||||
{"Mission_Rescue_Planet_name", "Planet Rescue"},
|
||||
{"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)"},
|
||||
{"MISSION_Salvage_Illegal_name", "Salvage (Illegal)"},
|
||||
@ -106,11 +120,18 @@ public class EnglishMissionNames {
|
||||
{"Mission_Sightseeing_Criminal_FAMINE_name", "Sightseeing (Criminal) (Famine)"},
|
||||
{"Mission_Sightseeing_name", "Sightseeing"},
|
||||
{"Mission_Smuggle_Anarchy_name", "Smuggling (Anarchy)" },
|
||||
{"Mission_TW_Massacre_Basilisk_Plural_name", "Kill Basilisk" },
|
||||
{"Mission_TW_Massacre_Basilisk_Plural_name", "Kill Basilisks" },
|
||||
{"Mission_TW_Massacre_Basilisk_Singular_name", "Kill Basilisk" },
|
||||
{"Mission_TW_Massacre_Cyclops_Plural_name", "Kill Cyclops" },
|
||||
{"Mission_TW_Massacre_Cyclops_Singular_name", "Kill Cyclops" },
|
||||
{"Mission_TW_Massacre_Scout_Plural_name", "Kill Scouts (Wing)" },
|
||||
{"Mission_TW_Massacre_Cyclops_Singular_name", "Kill Cyclop" },
|
||||
{"Mission_TW_Massacre_Hydra_Plural_name", "Kill Hydras" },
|
||||
{"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_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)" },
|
||||
|
48
EDPlayerJournal/Entries/ApproachSettlementEntry.cs
Normal file
48
EDPlayerJournal/Entries/ApproachSettlementEntry.cs
Normal 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;
|
||||
}
|
||||
}
|
44
EDPlayerJournal/Entries/CarrierJump.cs
Normal file
44
EDPlayerJournal/Entries/CarrierJump.cs
Normal file
@ -0,0 +1,44 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace EDPlayerJournal.Entries;
|
||||
|
||||
public class CarrierJump : Entry {
|
||||
public bool Docked { get; set; } = false;
|
||||
|
||||
public string? StationName { get; set; } = null;
|
||||
|
||||
public string? StationType { get; set; } = null;
|
||||
|
||||
public string? StarSystem { get; set; } = null;
|
||||
|
||||
public ulong SystemAddress { get; set; } = 0;
|
||||
|
||||
public string? SystemFaction { get; set; } = null;
|
||||
|
||||
public List<Faction> SystemFactions { get; set; } = new List<Faction>();
|
||||
|
||||
protected override void Initialise() {
|
||||
Docked = JSON.Value<bool?>("Docked") ?? false;
|
||||
|
||||
StarSystem = JSON.Value<string>("StarSystem");
|
||||
SystemAddress = JSON.Value<ulong?>("SystemAddress") ?? 0;
|
||||
|
||||
StationName = JSON.Value<string?>("StationName");
|
||||
StationType = JSON.Value<string?>("StationType");
|
||||
|
||||
var faction = JSON.Value<JObject>("SystemFaction");
|
||||
if (faction != null) {
|
||||
SystemFaction = faction.Value<string>("Name");
|
||||
}
|
||||
|
||||
var factions = JSON.Value<JArray>("Factions");
|
||||
if (factions != null) {
|
||||
foreach (JObject system_faction in factions) {
|
||||
Faction? f = Faction.FromJSON(system_faction);
|
||||
if (f != null) {
|
||||
SystemFactions.Add(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -8,4 +8,13 @@ public class CommanderEntry : Entry {
|
||||
Name = JSON.Value<string>("Name") ?? "";
|
||||
FID = JSON.Value<string>("FID") ?? "";
|
||||
}
|
||||
|
||||
public string FullName {
|
||||
get {
|
||||
if (string.IsNullOrEmpty(Name)) {
|
||||
return string.Empty;
|
||||
}
|
||||
return "CMDR " + Name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,10 @@ 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.CarrierJump, typeof(CarrierJump) },
|
||||
{ Events.Commander, typeof(CommanderEntry) },
|
||||
{ Events.CommitCrime, typeof(CommitCrimeEntry) },
|
||||
{ Events.Died, typeof(DiedEntry) },
|
||||
@ -36,14 +38,22 @@ public class Entry {
|
||||
{ Events.MissionRedirected, typeof(MissionRedirectedEntry) },
|
||||
{ Events.Missions, typeof(MissionsEntry) },
|
||||
{ Events.MultiSellExplorationData, typeof(MultiSellExplorationDataEntry) },
|
||||
{ Events.Music, typeof(MusicEntry) },
|
||||
{ Events.Powerplay, typeof(PowerplayEntry) },
|
||||
{ Events.PowerplayCollect, typeof(PowerplayCollectEntry) },
|
||||
{ Events.PowerplayDeliver, typeof(PowerplayDeliverEntry) },
|
||||
{ Events.PowerplayMerits, typeof(PowerplayMeritsEntry) },
|
||||
{ Events.PowerplayRank, typeof(PowerplayRankEntry) },
|
||||
{ Events.ReceiveText, typeof(ReceiveTextEntry) },
|
||||
{ Events.RedeemVoucher, typeof(RedeemVoucherEntry) },
|
||||
{ Events.SearchAndRescue, typeof(SearchAndRescueEntry) },
|
||||
{ Events.SelfDestruct, typeof(SelfDestructEntry) },
|
||||
{ Events.SellExplorationData, typeof(SellExplorationDataEntry) },
|
||||
{ Events.SellMicroResources, typeof(SellMicroResourcesEntry) },
|
||||
{ Events.SellOrganicData, typeof(SellOrganicDataEntry) },
|
||||
{ Events.ShieldState, typeof(ShieldStateEntry) },
|
||||
{ Events.ShipTargeted, typeof(ShipTargetedEntry) },
|
||||
{ Events.SupercruiseDestinationDrop, typeof(SupercruiseDestinationDropEntry) },
|
||||
{ Events.SupercruiseEntry, typeof(SupercruiseEntryEntry) },
|
||||
{ Events.SupercruiseExit, typeof(SupercruiseExitEntry) },
|
||||
{ Events.UnderAttack, typeof(UnderAttackEntry) },
|
||||
@ -62,7 +72,15 @@ public class Entry {
|
||||
public static Entry? Parse(string journalline) {
|
||||
using (JsonReader reader = new JsonTextReader(new StringReader(journalline))) {
|
||||
reader.DateParseHandling = DateParseHandling.None;
|
||||
var json = JObject.Load(reader);
|
||||
JObject? json = null;
|
||||
try {
|
||||
json = JObject.Load(reader);
|
||||
} catch (Exception e) {
|
||||
throw new InvalidJournalEntryException(
|
||||
"invalid JSON journal entry: " + journalline,
|
||||
e
|
||||
);
|
||||
}
|
||||
if (json == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
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 CarrierJump = "CarrierJump";
|
||||
public static readonly string Commander = "Commander";
|
||||
public static readonly string CommitCrime = "CommitCrime";
|
||||
public static readonly string Died = "Died";
|
||||
@ -26,15 +28,23 @@ public class Events {
|
||||
public static readonly string MissionRedirected = "MissionRedirected";
|
||||
public static readonly string Missions = "Missions";
|
||||
public static readonly string MultiSellExplorationData = "MultiSellExplorationData";
|
||||
public static readonly string Music = "Music";
|
||||
public static readonly string Powerplay = "Powerplay";
|
||||
public static readonly string PowerplayCollect = "PowerplayCollect";
|
||||
public static readonly string PowerplayDeliver = "PowerplayDeliver";
|
||||
public static readonly string PowerplayMerits = "PowerplayMerits";
|
||||
public static readonly string PowerplayRank = "PowerplayRank";
|
||||
public static readonly string ReceiveText = "ReceiveText";
|
||||
public static readonly string RedeemVoucher = "RedeemVoucher";
|
||||
public static readonly string SearchAndRescue = "SearchAndRescue";
|
||||
public static readonly string SelfDestruct = "SelfDestruct";
|
||||
public static readonly string SellExplorationData = "SellExplorationData";
|
||||
public static readonly string SellMicroResources = "SellMicroResources";
|
||||
public static readonly string SellOrganicData = "SellOrganicData";
|
||||
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";
|
||||
|
@ -1,18 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace EDPlayerJournal.Entries;
|
||||
|
||||
public class LocationEntry : Entry {
|
||||
/// <summary>
|
||||
/// Current star system.
|
||||
/// </summary>
|
||||
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? StationName { 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; }
|
||||
|
||||
/// <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; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the player is docked somewhere.
|
||||
/// </summary>
|
||||
public bool Docked { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Position of the star system.
|
||||
/// </summary>
|
||||
public long[]? StarPos { get; set; }
|
||||
|
||||
public List<Faction> SystemFactions { get; set; } = new List<Faction>();
|
||||
@ -30,7 +59,12 @@ public class LocationEntry : Entry {
|
||||
|
||||
JObject? systemfaction = JSON.Value<JObject>("SystemFaction");
|
||||
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");
|
||||
|
@ -1,8 +1,23 @@
|
||||
namespace EDPlayerJournal.Entries;
|
||||
|
||||
public class MultiSellExplorationDataEntry : Entry {
|
||||
protected override void Initialise() {
|
||||
TotalEarnings = (JSON.Value<int?>("TotalEarnings") ?? 0);
|
||||
TotalEarnings = JSON.Value<long?>("TotalEarnings") ?? 0;
|
||||
BaseValue = JSON.Value<long?>("BaseValue") ?? 0;
|
||||
Bonus = JSON.Value<long?>("Bonus") ?? 0;
|
||||
}
|
||||
|
||||
public int TotalEarnings { get; set; } = 0;
|
||||
public long Total {
|
||||
get { return BaseValue + Bonus; }
|
||||
}
|
||||
|
||||
public long BaseValue { get; set; } = 0;
|
||||
|
||||
public long Bonus { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Total Earnings are the actual earnings without bonus. So this
|
||||
/// value is BaseValue minus any percent a crew pilot takes.
|
||||
/// </summary>
|
||||
public long TotalEarnings { get; set; } = 0;
|
||||
}
|
||||
|
9
EDPlayerJournal/Entries/MusicEntry.cs
Normal file
9
EDPlayerJournal/Entries/MusicEntry.cs
Normal file
@ -0,0 +1,9 @@
|
||||
namespace EDPlayerJournal.Entries;
|
||||
|
||||
public class MusicEntry : Entry {
|
||||
public string? MusicTrack { get; set; } = null;
|
||||
|
||||
protected override void Initialise() {
|
||||
MusicTrack = JSON.Value<string?>("MusicTrack");
|
||||
}
|
||||
}
|
7
EDPlayerJournal/Entries/PowerplayCollectEntry.cs
Normal file
7
EDPlayerJournal/Entries/PowerplayCollectEntry.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace EDPlayerJournal.Entries;
|
||||
|
||||
public class PowerplayCollectEntry : Entry {
|
||||
protected override void Initialise() {
|
||||
// TODO
|
||||
}
|
||||
}
|
7
EDPlayerJournal/Entries/PowerplayDeliverEntry.cs
Normal file
7
EDPlayerJournal/Entries/PowerplayDeliverEntry.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace EDPlayerJournal.Entries;
|
||||
|
||||
public class PowerplayDeliverEntry : Entry {
|
||||
protected override void Initialise() {
|
||||
// TODO
|
||||
}
|
||||
}
|
32
EDPlayerJournal/Entries/PowerplayEntry.cs
Normal file
32
EDPlayerJournal/Entries/PowerplayEntry.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using System.Reflection;
|
||||
|
||||
namespace EDPlayerJournal.Entries {
|
||||
public class PowerplayEntry : Entry {
|
||||
/// <summary>
|
||||
/// Name of the power
|
||||
/// </summary>
|
||||
public string Power { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Player rank
|
||||
/// </summary>
|
||||
public int Rank { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Current merits of the player
|
||||
/// </summary>
|
||||
public long Merits { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Time pledged (in seconds?)
|
||||
/// </summary>
|
||||
public long TimePledged { get; set; } = 0;
|
||||
|
||||
protected override void Initialise() {
|
||||
Power = JSON.Value<string>("Power") ?? string.Empty;
|
||||
Rank = JSON.Value<int?>("Rank") ?? 0;
|
||||
Merits = JSON.Value<long?>("Merits") ?? 0;
|
||||
TimePledged = JSON.Value<long?>("TimePledged") ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
15
EDPlayerJournal/Entries/PowerplayMeritsEntry.cs
Normal file
15
EDPlayerJournal/Entries/PowerplayMeritsEntry.cs
Normal file
@ -0,0 +1,15 @@
|
||||
namespace EDPlayerJournal.Entries;
|
||||
|
||||
public class PowerplayMeritsEntry : Entry {
|
||||
protected override void Initialise() {
|
||||
Power = JSON.Value<string?>("Power") ?? string.Empty;
|
||||
MeritsGained = JSON.Value<long?>("MeritsGained") ?? 0;
|
||||
TotalMerits = JSON.Value<long?>("TotalMerits") ?? 0;
|
||||
}
|
||||
|
||||
public string Power { get; set; } = string.Empty;
|
||||
|
||||
public long MeritsGained { get; set; } = 0;
|
||||
|
||||
public long TotalMerits { get; set; } = 0;
|
||||
}
|
7
EDPlayerJournal/Entries/PowerplayRankEntry.cs
Normal file
7
EDPlayerJournal/Entries/PowerplayRankEntry.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace EDPlayerJournal.Entries;
|
||||
|
||||
public class PowerplayRankEntry : Entry {
|
||||
protected override void Initialise() {
|
||||
// TODO
|
||||
}
|
||||
}
|
5
EDPlayerJournal/Entries/SelfDestructEntry.cs
Normal file
5
EDPlayerJournal/Entries/SelfDestructEntry.cs
Normal file
@ -0,0 +1,5 @@
|
||||
namespace EDPlayerJournal.Entries {
|
||||
public class SelfDestructEntry : Entry {
|
||||
// Has no data
|
||||
}
|
||||
}
|
@ -6,8 +6,17 @@ namespace EDPlayerJournal.Entries;
|
||||
public class SellExplorationDataEntry : Entry {
|
||||
public long BaseValue { get; set; }
|
||||
public long Bonus { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Total Earnings are the actual earnings without bonus. So this
|
||||
/// value is BaseValue minus any percent a crew pilot takes.
|
||||
/// </summary>
|
||||
public long TotalEarnings { get; set; }
|
||||
|
||||
public long Total {
|
||||
get { return BaseValue + Bonus; }
|
||||
}
|
||||
|
||||
public List<string> Systems { get; set; } = new List<string>();
|
||||
public List<string> Discovered { get; set; } = new List<string>();
|
||||
|
||||
|
30
EDPlayerJournal/Entries/SupercruiseDestinationDropEntry.cs
Normal file
30
EDPlayerJournal/Entries/SupercruiseDestinationDropEntry.cs
Normal 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");
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
87
EDPlayerJournal/Instances.cs
Normal file
87
EDPlayerJournal/Instances.cs
Normal file
@ -0,0 +1,87 @@
|
||||
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>
|
||||
/// Medium power play conflict zone, new in PP 2.0 Ascendancy update
|
||||
/// </summary>
|
||||
public static readonly string PowerWarzoneMedium = "$Warzone_Powerplay_Med";
|
||||
|
||||
/// <summary>
|
||||
/// Low Thargoid combat zone
|
||||
/// </summary>
|
||||
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 IsPowerWarzone(string type) {
|
||||
return
|
||||
IsInstance(type, PowerWarzoneMedium)
|
||||
;
|
||||
}
|
||||
|
||||
public static bool IsWarzone(string type) {
|
||||
return IsHumanWarzone(type) ||
|
||||
IsThargoidWarzone(type) ||
|
||||
IsPowerWarzone(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;
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
public class InvalidJournalEntryException : Exception {
|
||||
public InvalidJournalEntryException() { }
|
||||
public InvalidJournalEntryException(string message) : base(message) { }
|
||||
public InvalidJournalEntryException(string message, Exception inner) : base(message, inner) { }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -17,6 +17,11 @@ public class JournalFile : IComparable<JournalFile>
|
||||
private static Regex update11regex = new Regex("Journal\\.([^\\.]+)\\.(\\d+).log");
|
||||
private static string iso8601 = "yyyyMMddTHHmmss";
|
||||
|
||||
/// <summary>
|
||||
/// A public list of errors encountered while parsing the journal files
|
||||
/// </summary>
|
||||
public List<Exception> Errors { get; private set; } = new List<Exception>();
|
||||
|
||||
public static bool VerifyFile(string path) {
|
||||
string filename = Path.GetFileName(path);
|
||||
|
||||
@ -125,14 +130,19 @@ public class JournalFile : IComparable<JournalFile>
|
||||
}
|
||||
|
||||
entries.Clear();
|
||||
foreach(var line in lines) {
|
||||
Errors.Clear();
|
||||
foreach (var line in lines) {
|
||||
// Skip empty lines
|
||||
if (line.Trim().Length == 0) {
|
||||
continue;
|
||||
}
|
||||
Entry? entry = Entry.Parse(line);
|
||||
if (entry != null) {
|
||||
entries.Add(entry);
|
||||
try {
|
||||
Entry? entry = Entry.Parse(line);
|
||||
if (entry != null) {
|
||||
entries.Add(entry);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
Errors.Add(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,12 @@ public class PlayerJournal {
|
||||
ScanFiles();
|
||||
}
|
||||
|
||||
public List<Exception> AllErrors {
|
||||
get {
|
||||
return Files.SelectMany(x => x.Errors).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
public List<JournalFile> Files {
|
||||
get { return journalfiles; }
|
||||
}
|
||||
|
101
EDPlayerJournal/PowerPlay/Powers.cs
Normal file
101
EDPlayerJournal/PowerPlay/Powers.cs
Normal file
@ -0,0 +1,101 @@
|
||||
namespace EDPlayerJournal.PowerPlay;
|
||||
|
||||
/// <summary>
|
||||
/// Holds a list of all available powers in E:D, and helps
|
||||
/// to translate commonly used short-hands and aliases to
|
||||
/// journal approved power names.
|
||||
/// </summary>
|
||||
public class Powers {
|
||||
public static readonly string
|
||||
// Basking
|
||||
ALavignyDuval = "A. Lavigny-Duval",
|
||||
AislingDuval = "Aisling Duval",
|
||||
DentonPatreus = "Denton Patreus",
|
||||
ZeminaTorval = "Zemina Torval",
|
||||
// Feds
|
||||
JeromeArcher = "Jerome Archer",
|
||||
FeliciaWinters = "Felicia Winters",
|
||||
// Alliance
|
||||
EdmunMahon = "Edmund Mahon",
|
||||
NakatoKaine = "Nakato Kaine",
|
||||
// Independents
|
||||
YuriGrom = "Yuri Grom",
|
||||
ArchonDelaine = "Archon Delaine",
|
||||
LiYongRui = "Li Yong-Rui",
|
||||
PranavAntal = "Pranav Antal"
|
||||
;
|
||||
|
||||
public static readonly Dictionary<string, string> aliases = new() {
|
||||
// ALD
|
||||
{ Powers.ALavignyDuval, Powers.ALavignyDuval },
|
||||
{ "ALD", Powers.ALavignyDuval },
|
||||
{ "Arissa", Powers.ALavignyDuval },
|
||||
{ "Emperor", Powers.ALavignyDuval },
|
||||
{ "Kaiser", Powers.ALavignyDuval },
|
||||
// AD
|
||||
{ Powers.AislingDuval, Powers.AislingDuval },
|
||||
{ "AD", Powers.AislingDuval },
|
||||
{ "Aisling", Powers.AislingDuval },
|
||||
// DP
|
||||
{ Powers.DentonPatreus, Powers.DentonPatreus },
|
||||
{ "DP", Powers.DentonPatreus },
|
||||
{ "Denton", Powers.DentonPatreus },
|
||||
// ZT
|
||||
{ Powers.ZeminaTorval, Powers.ZeminaTorval },
|
||||
{ "ZT", Powers.ZeminaTorval },
|
||||
{ "Torval", Powers.ZeminaTorval },
|
||||
// Archer
|
||||
{ Powers.JeromeArcher, Powers.JeromeArcher },
|
||||
{ "JA", Powers.JeromeArcher },
|
||||
{ "Archer", Powers.JeromeArcher },
|
||||
// Winters
|
||||
{ Powers.FeliciaWinters, Powers.FeliciaWinters },
|
||||
{ "FW", Powers.FeliciaWinters },
|
||||
{ "Winters", Powers.FeliciaWinters },
|
||||
// Mahon
|
||||
{ Powers.EdmunMahon, Powers.EdmunMahon },
|
||||
{ "EM", Powers.EdmunMahon },
|
||||
{ "Mahon", Powers.EdmunMahon },
|
||||
// Kaine
|
||||
{ Powers.NakatoKaine, Powers.NakatoKaine },
|
||||
{ "NK", Powers.NakatoKaine },
|
||||
{ "Kaine", Powers.NakatoKaine },
|
||||
// Grom
|
||||
{ Powers.YuriGrom, Powers.YuriGrom },
|
||||
{ "YG", Powers.YuriGrom },
|
||||
{ "Grom", Powers.YuriGrom },
|
||||
// Archon
|
||||
{ Powers.ArchonDelaine, Powers.ArchonDelaine },
|
||||
{ "Archon", Powers.ArchonDelaine },
|
||||
{ "Kumo", Powers.ArchonDelaine },
|
||||
{ "KumoBurger", Powers.ArchonDelaine },
|
||||
// LYR
|
||||
{ Powers.LiYongRui, Powers.LiYongRui },
|
||||
{ "LYR", Powers.LiYongRui },
|
||||
// Pranav
|
||||
{ Powers.PranavAntal, Powers.PranavAntal },
|
||||
{ "PA", Powers.PranavAntal },
|
||||
{ "Pranav", Powers.PranavAntal },
|
||||
};
|
||||
|
||||
public static bool IsValidPower(string power) {
|
||||
try {
|
||||
string p = GetPower(power);
|
||||
return true;
|
||||
} catch (Exception) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetPower(string nameOrAlias) {
|
||||
string? val = aliases
|
||||
.Where(x => string.Compare(x.Key, nameOrAlias, StringComparison.InvariantCultureIgnoreCase) == 0)
|
||||
.Select(x => x.Value)
|
||||
.FirstOrDefault()
|
||||
;
|
||||
if (string.IsNullOrEmpty(val)) {
|
||||
throw new ApplicationException($"not a valid power: {nameOrAlias}");
|
||||
}
|
||||
return val;
|
||||
}
|
||||
}
|
@ -8,6 +8,16 @@ public enum ThargoidVessel {
|
||||
Basilisk = 4,
|
||||
Medusa = 5,
|
||||
Hydra = 6,
|
||||
// Includes Glaive and Scythe
|
||||
Hunter = 7,
|
||||
/// <summary>
|
||||
/// Thargoid drone
|
||||
/// </summary>
|
||||
Revenant = 8,
|
||||
/// <summary>
|
||||
/// New thargoid drone in U17
|
||||
/// </summary>
|
||||
Banshee = 9,
|
||||
}
|
||||
|
||||
public class Thargoid {
|
||||
@ -15,34 +25,44 @@ 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 17
|
||||
{ 100000, ThargoidVessel.Banshee },
|
||||
// New in Update 15
|
||||
{ 4500000, ThargoidVessel.Hunter },
|
||||
{ 6500000, ThargoidVessel.Cyclops },
|
||||
{ 20000000, ThargoidVessel.Basilisk },
|
||||
{ 25000000, ThargoidVessel.Orthrus },
|
||||
//{ 25000000, ThargoidVessel.Orthrus },
|
||||
{ 34000000, ThargoidVessel.Medusa },
|
||||
// March, 16th 2023 the Orthrus payout was buffed again.
|
||||
{ 40000000, ThargoidVessel.Orthrus },
|
||||
{ 50000000, ThargoidVessel.Hydra },
|
||||
// These are the old values pre Update 14.02
|
||||
{ 80000, ThargoidVessel.Scout },
|
||||
{ 8000000, ThargoidVessel.Cyclops },
|
||||
{ 24000000, ThargoidVessel.Basilisk },
|
||||
//{ 80000, ThargoidVessel.Scout },
|
||||
//{ 8000000, ThargoidVessel.Cyclops },
|
||||
//{ 24000000, ThargoidVessel.Basilisk },
|
||||
// In Update 14.1 the payout for Orthrus has been rebalanced.
|
||||
{ 30000000, ThargoidVessel.Orthrus },
|
||||
{ 40000000, ThargoidVessel.Medusa },
|
||||
//{ 30000000, ThargoidVessel.Orthrus },
|
||||
//{ 40000000, ThargoidVessel.Medusa },
|
||||
// This used to be the old payout value for Orthrus, it now conflicts
|
||||
// with Post Update 14.02 Hydra values
|
||||
//{ 50000000, ThargoidVessel.Orthrus },
|
||||
{ 60000000, ThargoidVessel.Hydra },
|
||||
//{ 60000000, ThargoidVessel.Hydra },
|
||||
};
|
||||
|
||||
public static Dictionary<ThargoidVessel, string?> VesselNames { get; } = new() {
|
||||
{ ThargoidVessel.Unknown, null },
|
||||
{ ThargoidVessel.Scout, "Thargoid Scout" },
|
||||
{ ThargoidVessel.Unknown, "(Unknown)" },
|
||||
{ ThargoidVessel.Revenant, "Revenant" },
|
||||
{ ThargoidVessel.Scout, "Scout" },
|
||||
{ ThargoidVessel.Orthrus, "Orthrus" },
|
||||
{ ThargoidVessel.Cyclops, "Cyclops" },
|
||||
{ ThargoidVessel.Basilisk, "Basilisk" },
|
||||
{ ThargoidVessel.Medusa, "Medusa" },
|
||||
{ ThargoidVessel.Hydra, "Hydra" },
|
||||
{ ThargoidVessel.Hunter, "Hunter" },
|
||||
{ ThargoidVessel.Banshee, "Banshee" },
|
||||
};
|
||||
|
||||
public static ThargoidVessel GetVesselByPayout(ulong payout) {
|
||||
|
@ -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]
|
||||
|
@ -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");
|
||||
|
@ -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]
|
||||
|
7
EDPlayerJournalTests/double-vouchers-2.txt
Normal file
7
EDPlayerJournalTests/double-vouchers-2.txt
Normal file
@ -0,0 +1,7 @@
|
||||
{"timestamp":"2024-04-27T13:27:08Z","event":"Fileheader","part":1,"language":"English/UK","Odyssey":true,"gameversion":"4.0.0.1803","build":"r301470/r0 "}
|
||||
{"timestamp":"2024-04-27T13:27:34Z","event":"Commander","FID":"F9183790","Name":"Jeremaya"}
|
||||
{"timestamp":"2024-04-27T15:06:31Z","event":"FSDJump","Taxi":false,"Multicrew":false,"StarSystem":"HIP 3318","SystemAddress":525890177387,"StarPos":[50.21875,-190.6875,37.5],"SystemAllegiance":"Empire","SystemEconomy":"$economy_Refinery;","SystemEconomy_Localised":"Refinery","SystemSecondEconomy":"$economy_Extraction;","SystemSecondEconomy_Localised":"Extraction","SystemGovernment":"$government_Patronage;","SystemGovernment_Localised":"Patronage","SystemSecurity":"$SYSTEM_SECURITY_high;","SystemSecurity_Localised":"High Security","Population":239484,"Body":"HIP 3318 A","BodyID":2,"BodyType":"Star","JumpDist":27.495,"FuelUsed":3.115176,"FuelLevel":13.228082,"Factions":[{"Name":"HIP 3318 Autocracy","FactionState":"None","Government":"Dictatorship","Influence":0.119192,"Allegiance":"Empire","Happiness":"$Faction_HappinessBand2;","Happiness_Localised":"Happy","MyReputation":0.0},{"Name":"Chakho Gold Galactic Limited","FactionState":"None","Government":"Corporate","Influence":0.09798,"Allegiance":"Empire","Happiness":"$Faction_HappinessBand2;","Happiness_Localised":"Happy","MyReputation":0.0},{"Name":"HIP 3318 Interstellar","FactionState":"None","Government":"Corporate","Influence":0.075758,"Allegiance":"Empire","Happiness":"$Faction_HappinessBand2;","Happiness_Localised":"Happy","MyReputation":0.0},{"Name":"HIP 3318 Values Party","FactionState":"None","Government":"Democracy","Influence":0.032323,"Allegiance":"Independent","Happiness":"$Faction_HappinessBand2;","Happiness_Localised":"Happy","MyReputation":0.0},{"Name":"Nationalists of HIP 3318","FactionState":"None","Government":"Dictatorship","Influence":0.035354,"Allegiance":"Empire","Happiness":"$Faction_HappinessBand2;","Happiness_Localised":"Happy","MyReputation":0.0},{"Name":"Nova Paresa","FactionState":"Boom","Government":"Patronage","Influence":0.434343,"Allegiance":"Empire","Happiness":"$Faction_HappinessBand2;","Happiness_Localised":"Happy","SquadronFaction":true,"MyReputation":100.0,"PendingStates":[{"State":"Expansion","Trend":0}]},{"Name":"Empire Consulate Ltd","FactionState":"None","Government":"Patronage","Influence":0.20505,"Allegiance":"Empire","Happiness":"$Faction_HappinessBand2;","Happiness_Localised":"Happy","MyReputation":94.474998}],"SystemFaction":{"Name":"Nova Paresa","FactionState":"Boom"}}
|
||||
{"timestamp":"2024-04-27T15:12:23Z","event":"ApproachSettlement","Name":"Koh Biological Installation","MarketID":3803792128,"StationFaction":{"Name":"Nova Paresa","FactionState":"Boom"},"StationGovernment":"$government_Patronage;","StationGovernment_Localised":"Patronage","StationAllegiance":"Empire","StationServices":["dock","autodock","commodities","contacts","exploration","missions","refuel","repair","engineer","missionsgenerated","flightcontroller","stationoperations","searchrescue","stationMenu"],"StationEconomy":"$economy_HighTech;","StationEconomy_Localised":"High Tech","StationEconomies":[{"Name":"$economy_HighTech;","Name_Localised":"High Tech","Proportion":1.0}],"SystemAddress":525890177387,"BodyID":26,"BodyName":"HIP 3318 D 3","Latitude":-0.893061,"Longitude":-62.928345}
|
||||
{"timestamp":"2024-04-27T15:14:08Z","event":"Docked","StationName":"Koh Biological Installation","StationType":"OnFootSettlement","Taxi":false,"Multicrew":false,"StarSystem":"HIP 3318","SystemAddress":525890177387,"MarketID":3803792128,"StationFaction":{"Name":"Nova Paresa","FactionState":"Boom"},"StationGovernment":"$government_Patronage;","StationGovernment_Localised":"Patronage","StationAllegiance":"Empire","StationServices":["dock","autodock","commodities","contacts","exploration","missions","refuel","repair","engineer","missionsgenerated","flightcontroller","stationoperations","searchrescue","stationMenu"],"StationEconomy":"$economy_HighTech;","StationEconomy_Localised":"High Tech","StationEconomies":[{"Name":"$economy_HighTech;","Name_Localised":"High Tech","Proportion":1.0}],"DistFromStarLS":11972.63409,"LandingPads":{"Small":1,"Medium":0,"Large":1}}
|
||||
{"timestamp":"2024-04-27T15:15:10Z","event":"RedeemVoucher","Type":"bounty","Amount":9449329,"Factions":[{"Faction":"Nova Paresa","Amount":9449329}]}
|
||||
{"timestamp":"2024-04-27T15:15:51Z","event":"RedeemVoucher","Type":"bounty","Amount":18780130,"Factions":[{"Faction":"","Amount":224449},{"Faction":"","Amount":730880},{"Faction":"","Amount":1272764},{"Faction":"","Amount":580384},{"Faction":"","Amount":10180261},{"Faction":"","Amount":1413153},{"Faction":"","Amount":1365934},{"Faction":"","Amount":202193},{"Faction":"Nova Paresa","Amount":9449329},{"Faction":"","Amount":104412},{"Faction":"","Amount":2453443},{"Faction":"","Amount":252257}]}
|
@ -4,6 +4,14 @@
|
||||
xmlns:local="clr-namespace:EliteBGS"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<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>
|
||||
|
@ -1,4 +1,5 @@
|
||||
using System.Windows;
|
||||
using ControlzEx.Theming;
|
||||
using System.Windows;
|
||||
|
||||
namespace EliteBGS {
|
||||
/// <summary>
|
||||
|
@ -1,5 +1,122 @@
|
||||
# EliteBGS changelog
|
||||
|
||||
## 0.4.4 on ??.??.202?
|
||||
|
||||
* Add support for Power Conflict Zones
|
||||
* Show no inf as "Zero INF", so the Nova Navy bot can parse it properly
|
||||
* Add support for MeritsGained, but disable it for now, as the player
|
||||
journal is massively lacking in terms of Powerplay 2.0 support.
|
||||
|
||||
## 0.4.3 on 18.09.2024
|
||||
|
||||
* Add possibility to post log reports to Discord webhooks.
|
||||
Logs are automatically split to fit discord length, and you can choose the
|
||||
name of the Commander to post it as. There are some restrictions, so for
|
||||
ultra long logs the one line log format might be necessary.
|
||||
* Fix cartographics data value by igonring `TotalEarnings`. Total earnings
|
||||
is the same as `BaseValue`, except any percentages taken by crew members
|
||||
is deducted. Actual total value is `BaseValue` plus `Bonus`.
|
||||
|
||||
## 0.4.2 on 02.05.2024
|
||||
|
||||
* Add a bot header for all generated logs that shows the tool version, as
|
||||
well as the name of of the log format used. This makes it easier for bots
|
||||
to parse these logs. Since the different formats have become popular, its
|
||||
always good to make it easier for bots to parse the logs.
|
||||
|
||||
## 0.4.1 on 28.04.2024
|
||||
|
||||
* Filter out vouchers that are redeemed twice, due to bulk turn-in. If you
|
||||
redeem a singular voucher for value X, and then redeem the rest of your
|
||||
vouchers - say KWS vouchers - in bulk, the first voucher of value X will
|
||||
appear again in the logs. It appears twice in the logs, but only counts
|
||||
once.
|
||||
* Add the market name to the trading log entries.
|
||||
|
||||
## 0.4.0 on 13.04.2024
|
||||
|
||||
* Change layout of the results into System -> Faction -> Transaction.
|
||||
Many people who contribute *a lot* of things to the BGS have preferred such
|
||||
a layout to find the right things to post to various discord guilds/threads.
|
||||
* Sort all systems in the new overview by name. It just makes them easier to
|
||||
find when there are a lot of entries.
|
||||
* Add a button to deselect/select all buttons.
|
||||
|
||||
## 0.3.7 on 29.01.2024
|
||||
|
||||
* Fix wrong locations of BGS action if you remain on your carrier while its
|
||||
jumping to a new system.
|
||||
* Identify a capital ship in a high CZ by its music.
|
||||
|
||||
## 0.3.6 on 25.10.2023
|
||||
|
||||
* U17 introduced invalid JSON into the player journal. EliteBGS can now skip over
|
||||
those and keep processing. This way players won't have to delete lines in their
|
||||
journals anymore to keep using the tool.
|
||||
* Banshee has been added.
|
||||
|
||||
## 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.
|
||||
* 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
|
||||
|
||||
* Update Post 14.02 Thargoid bounties
|
@ -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(),
|
||||
@ -21,8 +21,69 @@ public class DiscordLogGenerator {
|
||||
new CargoSoldFormatter(),
|
||||
new VistaGenomicsFormat(),
|
||||
new SearchAndRescueFormat(),
|
||||
new MeritsGainedFormat(),
|
||||
};
|
||||
|
||||
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();
|
||||
|
||||
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() {
|
||||
return "";
|
||||
}
|
||||
@ -49,8 +110,20 @@ public class DiscordLogGenerator {
|
||||
.Count()
|
||||
;
|
||||
|
||||
log.AppendFormat("**Date:** {0}\n", DateTime.Now.ToString("dd/MM/yyyy"));
|
||||
string summary = GenerateSummary(objective);
|
||||
|
||||
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);
|
||||
}
|
||||
if (legacycount > 0) {
|
||||
log.AppendFormat("**Warning:** Some actions were performed on ED Legacy\n");
|
||||
}
|
||||
@ -73,6 +146,16 @@ public class DiscordLogGenerator {
|
||||
return log;
|
||||
}
|
||||
|
||||
public virtual string Name {
|
||||
get { return "GenericLog"; }
|
||||
}
|
||||
|
||||
protected virtual string BotHeader() {
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat("**Bot-Header:** {0}; {1}\n", GetToolVersion(), this.Name);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public virtual string GenerateDiscordLog(Report report) {
|
||||
StringBuilder log = new StringBuilder();
|
||||
|
||||
@ -88,6 +171,7 @@ public class DiscordLogGenerator {
|
||||
return "";
|
||||
}
|
||||
|
||||
log.AppendFormat("{0}", BotHeader());
|
||||
log.AppendFormat("{0}", GenerateHeader());
|
||||
|
||||
foreach (Objective objective in objectives) {
|
||||
@ -115,4 +199,65 @@ public class DiscordLogGenerator {
|
||||
|
||||
return log.ToString().Trim();
|
||||
}
|
||||
|
||||
public virtual string[] SplitLog(string log, int maxcount = 2000) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
protected string[] SplitLogWithHeader(string log, string header, int maxcount = 2000) {
|
||||
string[] lines = log.Split("\n");
|
||||
List<string> chunks = new();
|
||||
string chunk = string.Empty;
|
||||
|
||||
// Optimisation
|
||||
if (log.Length <= maxcount) {
|
||||
return new string[] { log };
|
||||
}
|
||||
|
||||
// First split the log into its headers
|
||||
// skip first bot header line
|
||||
for (int i = 1; i < lines.Length; i++) {
|
||||
string line = lines[i];
|
||||
|
||||
if (line.StartsWith(header) && !string.IsNullOrEmpty(chunk)) {
|
||||
chunks.Add(chunk.Trim());
|
||||
chunk = string.Empty;
|
||||
}
|
||||
|
||||
chunk = chunk + "\n" + line;
|
||||
}
|
||||
|
||||
int curchunk = 0;
|
||||
string botheader = BotHeader().Trim() + "\n";
|
||||
// Leave room for botheader and some leeway
|
||||
int maxlength = (2000 - botheader.Length - 10);
|
||||
|
||||
// Then try to collate chunks
|
||||
for (curchunk = 0; curchunk < chunks.Count; ++curchunk) {
|
||||
int count = chunks[curchunk].Length;
|
||||
while (count < maxlength && (curchunk+1) < chunks.Count) {
|
||||
count += chunks[curchunk + 1].Length + 2;
|
||||
if (count < maxlength) {
|
||||
chunks[curchunk] += "\n";
|
||||
chunks[curchunk] += chunks[curchunk + 1];
|
||||
chunks.RemoveAt(curchunk + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Readd bott headers
|
||||
for (int i = 0; i < chunks.Count; i++) {
|
||||
chunks[i] = chunks[i].Insert(0, botheader);
|
||||
}
|
||||
|
||||
// Now finally check if any one chunk is bigger than max length
|
||||
for (int i = 0; i < chunks.Count; i++) {
|
||||
if (chunks[i].Length > maxcount) {
|
||||
throw new ApplicationException(
|
||||
string.Format("a chunk turned out bigger than {0}", maxcount));
|
||||
}
|
||||
}
|
||||
|
||||
return chunks.ToArray();
|
||||
}
|
||||
}
|
||||
|
32
EliteBGS/DiscordPoster.cs
Normal file
32
EliteBGS/DiscordPoster.cs
Normal file
@ -0,0 +1,32 @@
|
||||
using EliteBGS.Util;
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace EliteBGS;
|
||||
|
||||
public class DiscordPoster {
|
||||
public static readonly int DiscordLimit = 2000;
|
||||
|
||||
public static void PostToDiscord(DiscordWebhook webhook, string log, string username = "EDBGS") {
|
||||
JsonObject obj = new();
|
||||
obj.Add("content", log);
|
||||
obj.Add("username", username);
|
||||
|
||||
using (var client = new HttpClient()) {
|
||||
var content = new StringContent(obj.ToString(), Encoding.UTF8, "application/json");
|
||||
var response = client.PostAsync(webhook.Webhook, content);
|
||||
if (response == null) {
|
||||
throw new Exception("failed to post content to Discord webhook");
|
||||
}
|
||||
response.Wait();
|
||||
var resp = response.Result;
|
||||
if (!resp.IsSuccessStatusCode) {
|
||||
throw new Exception(string.Format(
|
||||
"failed to post content to webhook {0}: {1} / {2}",
|
||||
webhook.Name, resp.StatusCode, resp.Content.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net7.0-windows</TargetFramework>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<Version>0.2.6</Version>
|
||||
<Version>0.4.3</Version>
|
||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||
<UseWindowsForms>true</UseWindowsForms>
|
||||
<UseWPF>true</UseWPF>
|
||||
@ -16,29 +16,34 @@
|
||||
<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>
|
||||
<RepositoryUrl>https://codeberg.org/nola/EDBGS</RepositoryUrl>
|
||||
<PackageTags>ED;Elite Dangerous;BGS</PackageTags>
|
||||
<PackageProjectUrl>https://bgs.n0la.org</PackageProjectUrl>
|
||||
<PackageProjectUrl>https://salusinvicta.org/bgstool/</PackageProjectUrl>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<Description>Elite: Dangerous BGS logging and reporting tool
|
||||
</Description>
|
||||
<PackageIcon>logo_v5.png</PackageIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<None Remove="main-page.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="main-page.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</Resource>
|
||||
<None Update="logo_v5.png">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath>\</PackagePath>
|
||||
</None>
|
||||
<None Update="README.md">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Pack>True</Pack>
|
||||
<PackagePath>\</PackagePath>
|
||||
</None>
|
||||
<None Update="docs\CHANGELOG.md">
|
||||
<None Update="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>
|
||||
@ -48,23 +53,12 @@
|
||||
<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="MahApps.Metro" Version="2.4.10" />
|
||||
<PackageReference Include="Microsoft.CSharp" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.Windows.Compatibility" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.Windows.Compatibility" Version="8.0.4" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Ookii.Dialogs.Wpf" Version="5.0.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@ -83,4 +77,7 @@
|
||||
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Resources\" />
|
||||
</ItemGroup>
|
||||
</Project>
|
Binary file not shown.
Before Width: | Height: | Size: 200 KiB |
@ -1,7 +1,18 @@
|
||||
namespace EliteBGS;
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Documents;
|
||||
|
||||
namespace EliteBGS;
|
||||
|
||||
public class GenericDiscordLog : DiscordLogGenerator {
|
||||
public override string ToString() {
|
||||
return "Generic Log";
|
||||
return "Generic";
|
||||
}
|
||||
|
||||
public override string Name {
|
||||
get { return "Generic"; }
|
||||
}
|
||||
|
||||
public override string[] SplitLog(string log, int maxcount = 2000) {
|
||||
return SplitLogWithHeader(log, "**Date:**", maxcount);
|
||||
}
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public partial class LoadEntriesWindow : Window {
|
||||
dialog.DefaultExt = ".log";
|
||||
dialog.Filter = "Log files (*.log)|*.log|All files (*.*)|*";
|
||||
|
||||
var location = config.Global.DefaultJournalLocation;
|
||||
var location = AppConfig.DefaultJournalLocation;
|
||||
if (Directory.Exists(location)) {
|
||||
dialog.InitialDirectory = location;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Documents;
|
||||
using EDPlayerJournal;
|
||||
using EDPlayerJournal.BGS;
|
||||
|
||||
@ -53,4 +54,24 @@ public class CargoSoldFormatter : LogFormatter {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -19,4 +19,15 @@ public class CartographicsFormat : LogFormatter {
|
||||
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 "";
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,8 @@ class CombatZoneFormat : LogFormatter {
|
||||
public string GenerateLog(Objective objective) {
|
||||
var logs = objective
|
||||
.EnabledOfType<CombatZone>()
|
||||
.GroupBy(x => new { x.Type, x.Grade })
|
||||
.OrderBy(x => (CombatZones.DifficultyRank(x.Grade) ?? 0))
|
||||
.GroupBy(x => new { x.Type, x.Grade, x.Settlement })
|
||||
.ToDictionary(x => x.Key, x => x.ToList())
|
||||
;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@ -22,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,
|
||||
@ -38,9 +44,57 @@ 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");
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,34 +0,0 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using EDPlayerJournal.Entries;
|
||||
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();
|
||||
}
|
||||
}
|
@ -25,4 +25,8 @@ public class GenericFormat<Type> : LogFormatter where Type : Transaction {
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
public virtual string GenerateSummary(Objective objective) {
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,38 @@
|
||||
using EDPlayerJournal.BGS;
|
||||
using EDPlayerJournal;
|
||||
using EDPlayerJournal.BGS;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace EliteBGS.LogGenerator;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -2,4 +2,5 @@
|
||||
|
||||
public interface LogFormatter {
|
||||
string GenerateLog(Objective objective);
|
||||
string GenerateSummary(Objective objective);
|
||||
}
|
||||
|
@ -1,6 +1,17 @@
|
||||
using EDPlayerJournal.BGS;
|
||||
using System.Linq;
|
||||
|
||||
namespace EliteBGS.LogGenerator;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
47
EliteBGS/LogGenerator/MeritsGainedFormat.cs
Normal file
47
EliteBGS/LogGenerator/MeritsGainedFormat.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using EDPlayerJournal.BGS;
|
||||
|
||||
namespace EliteBGS.LogGenerator;
|
||||
|
||||
public class MeritsGainedFormat : LogFormatter {
|
||||
public string GenerateLog(Objective objective) {
|
||||
var builder = new StringBuilder();
|
||||
|
||||
var merits = objective
|
||||
.EnabledOfType<MeritsGained>()
|
||||
.GroupBy(x => x.Power)
|
||||
.ToDictionary(x => x.Key, x => x.Sum(x => x.Merits))
|
||||
;
|
||||
|
||||
if (merits == null || merits.Count == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
foreach (var merit in merits) {
|
||||
builder.AppendFormat("{0} merits gained for {1}\n", merit.Value, merit.Key);
|
||||
}
|
||||
|
||||
return builder.ToString().Trim();
|
||||
}
|
||||
|
||||
public string GenerateSummary(Objective objective) {
|
||||
var builder = new StringBuilder();
|
||||
|
||||
var merits = objective
|
||||
.EnabledOfType<MeritsGained>()
|
||||
.GroupBy(x => x.Power)
|
||||
.ToDictionary(x => x.Key, x => x.Sum(x => x.Merits))
|
||||
;
|
||||
|
||||
if (merits == null || merits.Count == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
foreach (var merit in merits) {
|
||||
builder.AppendFormat("MRT: {0}, {1}; ", merit.Key, merit.Value);
|
||||
}
|
||||
|
||||
return builder.ToString().Trim().TrimEnd(';');
|
||||
}
|
||||
}
|
@ -16,4 +16,8 @@ public class MicroResourcesFormat : LogFormatter {
|
||||
return string.Format("Sold {0} worth of Micro Resources\n",
|
||||
Credits.FormatCredits(sum));
|
||||
}
|
||||
|
||||
public string GenerateSummary(Objective objective) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
@ -6,17 +6,73 @@ 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;
|
||||
bool hadmissions = false;
|
||||
|
||||
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 "";
|
||||
}
|
||||
|
||||
@ -30,6 +86,7 @@ public class MissionFormat : LogFormatter {
|
||||
|
||||
++collated[m.MissionName][m.Influence];
|
||||
|
||||
hadmissions = true;
|
||||
total_influence += m.Influence.Length;
|
||||
|
||||
if (m.AcceptedEntry != null &&
|
||||
@ -60,20 +117,64 @@ 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");
|
||||
}
|
||||
if (failed.Count > 0) {
|
||||
hadmissions = true;
|
||||
}
|
||||
total_influence += failed.Sum(x => x.InfluenceAmount);
|
||||
|
||||
foreach (InfluenceSupport inf in support) {
|
||||
output.Append(inf.ToString());
|
||||
output.Append("\n");
|
||||
total_influence += inf.Influence.Length;
|
||||
hadmissions = true;
|
||||
total_influence += inf.Influence.InfluenceAmount;
|
||||
}
|
||||
|
||||
if (support.Count() > 0) {
|
||||
if (support.Count > 0) {
|
||||
output.Append("\n");
|
||||
}
|
||||
|
||||
if (total_influence > 0) {
|
||||
if (hadmissions) {
|
||||
output.AppendFormat("Total Influence: {0}", total_influence);
|
||||
}
|
||||
|
||||
return output.ToString().Trim();
|
||||
}
|
||||
|
||||
public string GenerateSummary(Objective objective) {
|
||||
bool hadmissions = objective
|
||||
.EnabledOfType<MissionCompleted>()
|
||||
.Count > 0
|
||||
;
|
||||
long influence = objective
|
||||
.EnabledOfType<MissionCompleted>()
|
||||
.Sum(x => x.Influence.Length)
|
||||
;
|
||||
long support = objective
|
||||
.EnabledOfType<InfluenceSupport>()
|
||||
.Sum(x => x.Influence.InfluenceAmount)
|
||||
;
|
||||
long failed = objective
|
||||
.EnabledOfType<MissionFailed>()
|
||||
.Sum(x => x.InfluenceAmount)
|
||||
;
|
||||
|
||||
if (influence == 0 && support == 0 && failed == 0 && !hadmissions) {
|
||||
return "";
|
||||
}
|
||||
|
||||
string failedsummary = GenerateFailedSummary(objective);
|
||||
string summary = string.Format("INF: {0}", influence + support + failed);
|
||||
|
||||
if (!string.IsNullOrEmpty(failedsummary)) {
|
||||
string.Join("; ", summary, failedsummary);
|
||||
}
|
||||
|
||||
return summary;
|
||||
}
|
||||
}
|
||||
|
@ -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,13 +34,27 @@ 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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,22 @@
|
||||
using EDPlayerJournal.BGS;
|
||||
using EDPlayerJournal;
|
||||
using EDPlayerJournal.BGS;
|
||||
using System.Linq;
|
||||
|
||||
namespace EliteBGS.LogGenerator;
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -29,4 +29,37 @@ public class ThargoidFormatter : LogFormatter {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,18 @@
|
||||
using EDPlayerJournal.BGS;
|
||||
using EDPlayerJournal;
|
||||
using EDPlayerJournal.BGS;
|
||||
using System.Linq;
|
||||
|
||||
namespace EliteBGS.LogGenerator;
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -19,10 +19,38 @@ public class VoucherFormat : LogFormatter {
|
||||
}
|
||||
|
||||
foreach (var m in missions) {
|
||||
ulong total = (ulong)m.Value.Sum(x => (decimal)x.TotalSum);
|
||||
builder.AppendFormat("Handed in {0} vouchers: {1}\n", m.Key, Credits.FormatCredits(total));
|
||||
long total = (long)m.Value.Sum(x => (decimal)x.TotalSum);
|
||||
builder.AppendFormat("Handed in {0} vouchers: {1}\n", m.Key, Credits.FormatMillions(total));
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,20 +1,149 @@
|
||||
<Window
|
||||
<mah:MetroWindow
|
||||
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
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: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"
|
||||
Title="Elite: Dangerous BGS Helper" Height="520" Width="950" Icon="Salus.ico" Closing="window_Closing">
|
||||
Title="Elite: Dangerous BGS Helper" Height="620" Width="950" Icon="Salus.ico" Closing="window_Closing"
|
||||
>
|
||||
<Window.Resources>
|
||||
<local:MinusFortyFiveConverter x:Key="MinusFortyFiveConverter" />
|
||||
<Style x:Key="StretchingTreeViewStyle" TargetType="TreeViewItem" BasedOn="{StaticResource {x:Type TreeViewItem}}">
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
|
||||
</Style>
|
||||
<local:MinusFortyFiveConverter x:Key="MinusFortyFiveConverter" />
|
||||
|
||||
<local:Report x:Key="ObjectivesBasedOnSystem" />
|
||||
<Util:Config x:Key="Config" />
|
||||
|
||||
<DataTemplate DataType="{x:Type Util:DiscordWebhook}">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="Name: " Grid.Column="0" Margin="2,0,0,0"/>
|
||||
<TextBox Text="{Binding Name}" Grid.Column="1"/>
|
||||
|
||||
<TextBlock Text="Webhook URL: " Grid.Column="2" Margin="2,0,0,0"/>
|
||||
<TextBox Text="{Binding Webhook}" Grid.Column="3"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
<HierarchicalDataTemplate DataType="{x:Type local:SystemObjectives}" ItemsSource="{Binding Path=Objectives}">
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
Width="{Binding ActualWidth, ElementName=entries, Converter={StaticResource MinusFortyFiveConverter}}"
|
||||
>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,2,0,2">
|
||||
<CheckBox Focusable="False" IsChecked="{Binding IsEnabled}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="System: " Margin="2,0,0,0"/>
|
||||
<TextBlock Text="{Binding SystemName}" FontWeight="DemiBold"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</HierarchicalDataTemplate>
|
||||
|
||||
<HierarchicalDataTemplate DataType="{x:Type local:Objective}"
|
||||
ItemsSource="{Binding Path=UITransactions}"
|
||||
ItemContainerStyle="{StaticResource StretchingTreeViewStyle}">
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
Width="{Binding ActualWidth, ElementName=entries, Converter={StaticResource MinusFortyFiveConverter}}"
|
||||
>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,2,0,2">
|
||||
<CheckBox Focusable="False" IsChecked="{Binding IsEnabled}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="Faction: " Margin="2,0,0,0"/>
|
||||
<TextBlock Text="{Binding Faction}" FontWeight="DemiBold"/>
|
||||
</StackPanel>
|
||||
<Separator Visibility="Hidden" Grid.Column="1" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" />
|
||||
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Orientation="Horizontal">
|
||||
<ToggleButton x:Name="ToggleAll" Content="Toggle All" Click="ToggleAll_Click" IsChecked="True" IsThreeState="False"/>
|
||||
<Separator Margin="2,0,2,0" />
|
||||
<Button x:Name="AddCombatZone" Content="Add Combat Zone" Click="AddCombatZone_Click" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</HierarchicalDataTemplate>
|
||||
|
||||
<DataTemplate DataType="{x:Type local:UITransaction}">
|
||||
<!-- This will stretch out the width of the item-->
|
||||
<Grid Initialized="Transaction_Initialized"
|
||||
HorizontalAlignment="Left"
|
||||
Width="{Binding ActualWidth, ElementName=entries, Converter={StaticResource MinusFortyFiveConverter}}"
|
||||
Margin="0,2,0,2"
|
||||
>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Stretch" VerticalAlignment="Center">
|
||||
<CheckBox Focusable="False" IsChecked="{Binding IsEnabled}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding CompletedAt}" Margin="2,0,2,0" HorizontalAlignment="Right" TextAlignment="Center"/>
|
||||
<TextBlock Text="{Binding Name}" FontWeight="DemiBold" TextAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right" x:Name="CombatZone" Visibility="{Binding IsCombatZone}">
|
||||
<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="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>
|
||||
<Expander Header="Difficulty">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Button x:Name="Low" Content="Low" Click="Low_Click"/>
|
||||
<Button x:Name="Med" Content="Med" Click="Med_Click"/>
|
||||
<Button x:Name="High" Content="High" Click="High_Click"/>
|
||||
<Button x:Name="VeryHigh" Content="Very High" Click="VeryHigh_Click" />
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
<Expander Header="Type">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Button Content="Ground" x:Name="Ground" Click="Ground_Click"/>
|
||||
<Button Content="Ship" x:Name="Ship" Click="Ship_Click"/>
|
||||
<Button Content="AX" x:Name="Thargoid" Click="Thargoid_Click"/>
|
||||
<Button Content="Power" x:Name="Power" Click="Power_Click"/>
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right" x:Name="SellCargo" Visibility="{Binding IsSellCargo}">
|
||||
<TextBlock Text="Adjust Profit: " TextAlignment="Center" />
|
||||
<TextBox x:Name="Profit" MinWidth="80" HorizontalContentAlignment="Right" Text="{Binding Profit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" LostFocus="Profit_LostFocus" KeyUp="Profit_KeyUp"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
|
||||
</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://salusinvicta.org/bgstool/" 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.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
@ -22,9 +151,9 @@
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<TabControl>
|
||||
<TabControl Style="{DynamicResource MahApps.Styles.TabControl.Animated}">
|
||||
<TabItem Header="Current Objectives">
|
||||
<Grid Background="#FFE5E5E5">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
@ -41,105 +170,36 @@
|
||||
<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"/>
|
||||
<Label Content="From (UTC):" VerticalAlignment="Center" VerticalContentAlignment="Center" HorizontalAlignment="Center"/>
|
||||
<xctk:DateTimePicker x:Name="startdate" Height="26.2857142857143" VerticalAlignment="Center" HorizontalAlignment="Center"/>
|
||||
<Label Content="To (UTC):" Height="26.2857142857143" VerticalAlignment="Top"/>
|
||||
<xctk:DateTimePicker x:Name="enddate" Height="26.2857142857143" VerticalAlignment="Center" HorizontalAlignment="Center"/>
|
||||
<mah:DateTimePicker x:Name="startdate" Height="26.2857142857143" VerticalAlignment="Center" HorizontalAlignment="Center"/>
|
||||
<Label Content="To (UTC):" Height="26.2857142857143" VerticalAlignment="Center" HorizontalAlignment="Center"/>
|
||||
<mah:DateTimePicker x:Name="enddate" Height="26.2857142857143" VerticalAlignment="Center" HorizontalAlignment="Center"/>
|
||||
<Separator Margin="1" VerticalAlignment="Center" MinWidth="1" HorizontalAlignment="Center" MinHeight="22"/>
|
||||
<Button x:Name="ResetTime" Content="Reset Time" Click="ResetTime_Click" />
|
||||
<Separator Margin="1" VerticalAlignment="Center" MinWidth="1" HorizontalAlignment="Center" MinHeight="22"/>
|
||||
<Button x:Name="ManuallyParse" Content="Manually Parse" Click="ManuallyParse_Click" />
|
||||
</ToolBar>
|
||||
<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" Height="26"/>
|
||||
<ToolBar Grid.Row="1" HorizontalAlignment="Left" Height="Auto" 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"/>
|
||||
<Separator />
|
||||
<ComboBox x:Name="LogType" VerticalAlignment="Stretch" Margin="0,3,0,3" Width="140" SelectionChanged="LogType_SelectionChanged" />
|
||||
<Separator />
|
||||
<CheckBox x:Name="SelectAll" Content="Select All" IsChecked="True" Click="SelectAll_Click"/>
|
||||
<Separator />
|
||||
<Label Content="Post log as" VerticalAlignment="Center" Margin="0,3,0,3" />
|
||||
<ComboBox x:Name="Commanders" VerticalAlignment="Stretch" Margin="0,3,0,3" Width="180" />
|
||||
<Label Content="to" VerticalAlignment="Center" Margin="0,3,0,3" />
|
||||
<mah:DropDownButton x:Name="PostToDiscord" Content="Discord" />
|
||||
</ToolBar>
|
||||
<TreeView CheckBox.Checked="TreeView_CheckBox_Updated"
|
||||
CheckBox.Unchecked="TreeView_CheckBox_Updated"
|
||||
x:Name="entries" Margin="0,0,0,0"
|
||||
Grid.ColumnSpan="3" Grid.Row="2"
|
||||
Grid.ColumnSpan="3"
|
||||
Grid.Row="2"
|
||||
KeyUp="entries_KeyUp"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
ItemsSource="{Binding Source={StaticResource ObjectivesBasedOnSystem}}"
|
||||
>
|
||||
<TreeView.ItemTemplate>
|
||||
<HierarchicalDataTemplate DataType="{x:Type local:Objective}" ItemsSource="{Binding UITransactions}" ItemContainerStyle="{StaticResource StretchingTreeViewStyle}">
|
||||
<Grid
|
||||
HorizontalAlignment="Stretch"
|
||||
Width="{Binding ActualWidth, ElementName=entries, Converter={StaticResource MinusFortyFiveConverter}}"
|
||||
>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,2,0,2">
|
||||
<CheckBox Focusable="False" IsChecked="{Binding IsEnabled}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="System: " Visibility="{Binding HasSystem}" Margin="2,0,0,0"/>
|
||||
<TextBlock Text="{Binding System}" FontWeight="DemiBold" Visibility="{Binding HasSystem}"/>
|
||||
<TextBlock Text="Faction: " Visibility="{Binding HasFaction}" Margin="2,0,0,0"/>
|
||||
<TextBlock Text="{Binding Faction}" FontWeight="DemiBold" Visibility="{Binding HasFaction}"/>
|
||||
</StackPanel>
|
||||
<Separator Visibility="Hidden" Grid.Column="1" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" />
|
||||
<StackPanel Grid.Column="2" HorizontalAlignment="Right" VerticalAlignment="Center" Orientation="Horizontal">
|
||||
<ToggleButton x:Name="ToggleAll" Content="Toggle All" Click="ToggleAll_Click" IsChecked="True" IsThreeState="False"/>
|
||||
<Separator Margin="2,0,2,0" />
|
||||
<Button x:Name="AddCombatZone" Content="Add Combat Zone" Click="AddCombatZone_Click" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<HierarchicalDataTemplate.ItemTemplate>
|
||||
<HierarchicalDataTemplate>
|
||||
<!-- This will stretch out the width of the item-->
|
||||
<Grid Initialized="Transaction_Initialized"
|
||||
HorizontalAlignment="Stretch"
|
||||
Width="{Binding ActualWidth, ElementName=entries, Converter={StaticResource MinusFortyFiveConverter}}"
|
||||
Margin="0,2,0,2"
|
||||
>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<StackPanel Grid.Column="0" Orientation="Horizontal" HorizontalAlignment="Stretch" VerticalAlignment="Center">
|
||||
<CheckBox Focusable="False" IsChecked="{Binding IsEnabled}" VerticalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding CompletedAt}" Margin="2,0,2,0" HorizontalAlignment="Right" TextAlignment="Center"/>
|
||||
<TextBlock Text="{Binding Name}" FontWeight="DemiBold" TextAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right" x:Name="CombatZone" Visibility="{Binding IsCombatZone}">
|
||||
<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="SpecOps" Margin="2,0,2,0" Content="Spec Ops" IsChecked="{Binding HasSpecOps, Mode=TwoWay}" IsThreeState="False"/>
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
<Expander Header="Difficulty">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Button x:Name="Low" Content="Low" Click="Low_Click"/>
|
||||
<Button x:Name="Med" Content="Med" Click="Med_Click"/>
|
||||
<Button x:Name="High" Content="High" Click="High_Click"/>
|
||||
<Button x:Name="VeryHigh" Content="Very High" Click="VeryHigh_Click" />
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
<Expander Header="Type">
|
||||
<StackPanel Orientation="Vertical">
|
||||
<Button Content="Ground" x:Name="Ground" Click="Ground_Click"/>
|
||||
<Button Content="Ship" x:Name="Ship" Click="Ship_Click"/>
|
||||
<Button Content="AX" x:Name="Thargoid" Click="Thargoid_Click"/>
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" HorizontalAlignment="Right" x:Name="SellCargo" Visibility="{Binding IsSellCargo}">
|
||||
<TextBlock Text="Adjust Profit: " TextAlignment="Center" />
|
||||
<TextBox x:Name="Profit" MinWidth="80" HorizontalContentAlignment="Right" Text="{Binding Profit, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" LostFocus="Profit_LostFocus" KeyUp="Profit_KeyUp"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</HierarchicalDataTemplate>
|
||||
</HierarchicalDataTemplate.ItemTemplate>
|
||||
</HierarchicalDataTemplate>
|
||||
</TreeView.ItemTemplate>
|
||||
<TreeView.ItemContainerStyle>
|
||||
<Style TargetType="TreeViewItem">
|
||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
|
||||
@ -150,9 +210,11 @@
|
||||
<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>
|
||||
</TabItem>
|
||||
<TabItem Header="Settings" HorizontalAlignment="Left" Height="20" VerticalAlignment="Top" Width="53.7142857142857">
|
||||
<Grid Background="#FFE5E5E5">
|
||||
<TabItem Header="Settings">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
@ -171,16 +233,80 @@
|
||||
<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">
|
||||
<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"/>
|
||||
<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" />
|
||||
<mah:ToggleSwitch x:Name="NoPowerplay" Grid.Row="3" Grid.ColumnSpan="2" Content="Ignore Powerplay Merits" Toggled="NoPowerplay_Toggled" />
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
<GroupBox Header="Discord Webhooks" Grid.Row="3" VerticalAlignment="Top" Width="Auto" Grid.ColumnSpan="3" Margin="0,5,0,0">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<DataGrid x:Name="Webhooks"
|
||||
Grid.Row="0"
|
||||
ItemsSource="{Binding Webhooks}"
|
||||
AutoGenerateColumns="False"
|
||||
ScrollViewer.CanContentScroll="True"
|
||||
ScrollViewer.VerticalScrollBarVisibility="Auto"
|
||||
ScrollViewer.HorizontalScrollBarVisibility="Auto"
|
||||
MaxHeight="100"
|
||||
MinHeight="100"
|
||||
KeyUp="Webhooks_KeyUp"
|
||||
CellEditEnding="Webhooks_CellEditEnding"
|
||||
>
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
|
||||
<DataGridTextColumn Header="Webhook URL" Binding="{Binding Webhook}" Width="*"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Left" Margin="5,10,5,0">
|
||||
<Button x:Name="AddWebHook" Content="Add" Click="AddWebHook_Click"/>
|
||||
<Button x:Name="RemoveWebHook" Content="Remove" Click="RemoveWebHook_Click" />
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
<TabItem Header="Event Log" HorizontalAlignment="Left" Height="20" VerticalAlignment="Top">
|
||||
<Grid Background="#FFE5E5E5">
|
||||
<TabItem Header="Event Log">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
@ -188,8 +314,8 @@
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<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" Background="{x:Null}" BorderBrush="{x:Null}" SelectionBrush="{x:Null}">
|
||||
<TextBox IsReadOnly="True" Grid.Row="1" x:Name="log" Height="Auto" Margin="5" TextWrapping="Wrap" FontFamily="Courier New" />
|
||||
<RichTextBox IsReadOnly="True" HorizontalAlignment="Left" Height="Auto" Margin="5" Width="Auto" VerticalContentAlignment="Stretch" VerticalAlignment="Top" BorderBrush="{x:Null}" SelectionBrush="{x:Null}">
|
||||
<FlowDocument>
|
||||
<Paragraph>
|
||||
<Run Text="This tool does not recognise every option and/or configuration that E:D outputs through its JSON files."/>
|
||||
@ -203,4 +329,4 @@
|
||||
</TabItem>
|
||||
</TabControl>
|
||||
</Grid>
|
||||
</Window>
|
||||
</mah:MetroWindow>
|
||||
|
@ -12,15 +12,18 @@ using EDPlayerJournal.Entries;
|
||||
using EliteBGS.BGS;
|
||||
using EliteBGS.Util;
|
||||
using System.Globalization;
|
||||
using System.Windows.Forms;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using MahApps.Metro.Controls;
|
||||
using ControlzEx.Theming;
|
||||
using System.Windows.Media;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace EliteBGS;
|
||||
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window {
|
||||
public partial class MainWindow : MetroWindow {
|
||||
private PlayerJournal journal;
|
||||
private Report report;
|
||||
|
||||
@ -29,8 +32,8 @@ public partial class MainWindow : Window {
|
||||
private LoadEntriesWindow loadentries = null;
|
||||
|
||||
private static readonly List<DiscordLogGenerator> logtypes = new List<DiscordLogGenerator>() {
|
||||
new NonaDiscordLog(),
|
||||
new GenericDiscordLog(),
|
||||
new NonaDiscordLog(),
|
||||
new OneLineDiscordLog(),
|
||||
};
|
||||
|
||||
@ -43,6 +46,9 @@ public partial class MainWindow : Window {
|
||||
/* ignored */
|
||||
}
|
||||
|
||||
Webhooks.ItemsSource = Config.Global.Webhooks;
|
||||
RefreshPostMenu();
|
||||
|
||||
foreach (DiscordLogGenerator type in logtypes) {
|
||||
LogType.Items.Add(type);
|
||||
}
|
||||
@ -55,6 +61,43 @@ public partial class MainWindow : Window {
|
||||
LogType.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
this.NoInfluenceSupport.IsOn = Config.Global.IgnoreInfluenceSupport;
|
||||
this.NoMarketBuy.IsOn = Config.Global.IgnoreMarketBuy;
|
||||
this.NoFleetCarrier.IsOn = Config.Global.IgnoreFleetCarrier;
|
||||
this.NoPowerplay.IsOn = Config.Global.IgnorePowerplay;
|
||||
|
||||
// Apply theme
|
||||
try {
|
||||
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);
|
||||
|
||||
// Set both to now
|
||||
@ -63,14 +106,54 @@ public partial class MainWindow : Window {
|
||||
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) },
|
||||
// Official Red of the Polish Flag
|
||||
{ "PolskaGurom", Color.FromRgb(0xD4, 0x21, 0x3D) },
|
||||
// Official Blue in the Armenian Flag
|
||||
{ "ArmeniaBlue", Color.FromRgb(0x00, 0x33, 0xA0) },
|
||||
};
|
||||
|
||||
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() {
|
||||
DateTime today = DateTime.Today;
|
||||
DateTime tomorrow = today.AddDays(1);
|
||||
|
||||
startdate.CultureInfo = enddate.CultureInfo = CultureInfo.InvariantCulture;
|
||||
// HOCHKULTUR
|
||||
startdate.Culture = enddate.Culture = CultureInfo.GetCultureInfo("de-AT");
|
||||
|
||||
startdate.Value = today;
|
||||
enddate.Value = tomorrow;
|
||||
startdate.SelectedDateTime = today;
|
||||
enddate.SelectedDateTime = tomorrow;
|
||||
}
|
||||
|
||||
private void TreeView_CheckBox_Updated(object sender, RoutedEventArgs args) {
|
||||
@ -94,8 +177,20 @@ public partial class MainWindow : Window {
|
||||
|
||||
private void HandleEntries(List<Entry> entries, DateTime start, DateTime end) {
|
||||
try {
|
||||
TransactionParser parser = new TransactionParser();
|
||||
List<Transaction> transactions = parser.Parse(entries);
|
||||
TransactionParser parser = new();
|
||||
TransactionParserOptions options = new();
|
||||
|
||||
options.IgnoreInfluenceSupport = Config.Global.IgnoreInfluenceSupport;
|
||||
options.IgnoreMarketBuy = Config.Global.IgnoreMarketBuy;
|
||||
options.IgnoreFleetCarrierFaction = Config.Global.IgnoreFleetCarrier;
|
||||
options.IgnorePowerplay = Config.Global.IgnorePowerplay;
|
||||
|
||||
List<Transaction> transactions = parser.Parse(entries, options);
|
||||
|
||||
Commanders.ItemsSource = parser.Commanders;
|
||||
if (Commanders.Items.Count > 0) {
|
||||
Commanders.SelectedIndex = 0;
|
||||
}
|
||||
|
||||
// Filter the transactions down to the given time frame
|
||||
transactions = transactions
|
||||
@ -111,7 +206,7 @@ public partial class MainWindow : Window {
|
||||
transactions.RemoveAll(x => incompletes.Contains(x));
|
||||
|
||||
report = new Report(transactions);
|
||||
this.entries.ItemsSource = report.Objectives;
|
||||
this.entries.ItemsSource = report.SystemObjectives;
|
||||
} catch (Exception exception) {
|
||||
Log("Something went terribly wrong while parsing the E:D player journal.");
|
||||
Log("Please send this to CMDR Hekateh:");
|
||||
@ -120,7 +215,7 @@ public partial class MainWindow : Window {
|
||||
}
|
||||
|
||||
private void HandleEntries(List<Entry> entries) {
|
||||
HandleEntries(entries, startdate.Value ?? DateTime.Now, enddate.Value ?? DateTime.Now);
|
||||
HandleEntries(entries, startdate.SelectedDateTime ?? DateTime.Now, enddate.SelectedDateTime ?? DateTime.Now);
|
||||
}
|
||||
|
||||
private void Loadentries_EntriesLoaded(List<Entry> lines) {
|
||||
@ -131,8 +226,8 @@ public partial class MainWindow : Window {
|
||||
try {
|
||||
TransactionParser parser = new TransactionParser();
|
||||
|
||||
DateTime start = startdate.Value ?? DateTime.Now;
|
||||
DateTime end = enddate.Value ?? DateTime.Now;
|
||||
DateTime start = startdate.SelectedDateTime ?? DateTime.Now;
|
||||
DateTime end = enddate.SelectedDateTime ?? DateTime.Now;
|
||||
|
||||
journal.Open(); // Load all files
|
||||
// Log files only get rotated if you restart the game client. This means that there might
|
||||
@ -152,6 +247,12 @@ public partial class MainWindow : Window {
|
||||
|
||||
HandleEntries(entries, start, end);
|
||||
GenerateLog();
|
||||
|
||||
var errors = journal.AllErrors;
|
||||
foreach (var error in errors) {
|
||||
Log("An error has occured in the Journal file, please send this to CMDR Hekateh:");
|
||||
Log(error.ToString());
|
||||
}
|
||||
} catch (Exception exception) {
|
||||
Log("Something went terribly wrong while parsing the E:D player journal.");
|
||||
Log("Please send this to CMDR Hekateh:");
|
||||
@ -184,15 +285,27 @@ public partial class MainWindow : Window {
|
||||
object obj = entries.SelectedItem;
|
||||
bool removed = false;
|
||||
|
||||
if (obj.GetType() == typeof(Objective)) {
|
||||
removed = report.Objectives.Remove(obj as Objective);
|
||||
if (obj.GetType() == typeof(SystemObjectives)) {
|
||||
removed = report.SystemObjectives.Remove(obj as SystemObjectives);
|
||||
} else if (obj.GetType() == typeof(Objective)) {
|
||||
report
|
||||
.SystemObjectives
|
||||
.ForEach(x => {
|
||||
if (x.Objectives.Remove(obj as Objective)) {
|
||||
removed = true;
|
||||
}
|
||||
});
|
||||
} else if (obj.GetType() == typeof(UITransaction) ||
|
||||
obj.GetType().IsSubclassOf(typeof(UITransaction))) {
|
||||
foreach (Objective parent in report.Objectives) {
|
||||
if (parent.UITransactions.Remove(obj as UITransaction)) {
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
obj.GetType().IsSubclassOf(typeof(UITransaction))) {
|
||||
report
|
||||
.SystemObjectives
|
||||
.SelectMany(x =>
|
||||
x.Objectives
|
||||
.Where(x => x.UITransactions.Contains(obj as UITransaction))
|
||||
)
|
||||
.ToList()
|
||||
.ForEach(x => removed = x.UITransactions.Remove(obj as UITransaction))
|
||||
;
|
||||
}
|
||||
|
||||
if (removed) {
|
||||
@ -200,7 +313,7 @@ public partial class MainWindow : Window {
|
||||
}
|
||||
}
|
||||
|
||||
private void entries_KeyUp(object sender, System.Windows.Input.KeyEventArgs e) {
|
||||
private void entries_KeyUp(object sender, KeyEventArgs e) {
|
||||
if (e.Key == Key.Delete) {
|
||||
RemoveCurrentObjective();
|
||||
}
|
||||
@ -219,7 +332,7 @@ public partial class MainWindow : Window {
|
||||
}
|
||||
|
||||
private Objective GetObjectiveFromControl(object sender) {
|
||||
System.Windows.Controls.Control control = sender as System.Windows.Controls.Control;
|
||||
Control control = sender as Control;
|
||||
if (control == null || control.DataContext == null) {
|
||||
return null;
|
||||
}
|
||||
@ -280,13 +393,19 @@ public partial class MainWindow : Window {
|
||||
private void window_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
|
||||
loadentries?.Close();
|
||||
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 TransactionType GetTransaction<TransactionType>(object sender) where TransactionType : Transaction {
|
||||
System.Windows.Controls.Control button = sender as System.Windows.Controls.Control;
|
||||
Control button = sender as Control;
|
||||
if (button == null || button.DataContext == null) {
|
||||
return null;
|
||||
}
|
||||
@ -359,6 +478,16 @@ public partial class MainWindow : Window {
|
||||
RefreshView();
|
||||
}
|
||||
|
||||
private void Power_Click(object sender, RoutedEventArgs e) {
|
||||
CombatZone transaction = GetTransaction<CombatZone>(sender);
|
||||
if (transaction == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
transaction.Type = CombatZones.PowerCombatZone;
|
||||
RefreshView();
|
||||
}
|
||||
|
||||
private void Thargoid_Click(object sender, RoutedEventArgs e) {
|
||||
CombatZone transaction = GetTransaction<CombatZone>(sender);
|
||||
if (transaction == null) {
|
||||
@ -390,14 +519,14 @@ public partial class MainWindow : Window {
|
||||
}
|
||||
|
||||
private void ResetTime_Click(object sender, RoutedEventArgs e) {
|
||||
DateTime? d = startdate.Value;
|
||||
DateTime? d = startdate.SelectedDateTime;
|
||||
if (d != null) {
|
||||
startdate.Value = ResetTimeToZero(d.Value);
|
||||
startdate.SelectedDateTime = ResetTimeToZero(d.Value);
|
||||
}
|
||||
|
||||
d = enddate.Value;
|
||||
d = enddate.SelectedDateTime;
|
||||
if (d != null) {
|
||||
enddate.Value = ResetTimeToZero(d.Value);
|
||||
enddate.SelectedDateTime = ResetTimeToZero(d.Value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -412,4 +541,192 @@ public partial class MainWindow : Window {
|
||||
.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 NoPowerplay_Toggled(object sender, RoutedEventArgs e) {
|
||||
Config.Global.IgnorePowerplay = this.NoPowerplay.IsOn;
|
||||
}
|
||||
|
||||
private void OpenInExplorer_Click(object sender, RoutedEventArgs e) {
|
||||
try {
|
||||
Process.Start(new ProcessStartInfo(Config.Global.JournalLocation) {
|
||||
UseShellExecute = true,
|
||||
});
|
||||
} catch (Exception) {
|
||||
}
|
||||
}
|
||||
|
||||
private void SelectAll_Click(object sender, RoutedEventArgs e) {
|
||||
if (report == null) {
|
||||
return;
|
||||
}
|
||||
report.SystemObjectives.ForEach(t => { t.IsEnabled = (bool)SelectAll.IsChecked; });
|
||||
}
|
||||
|
||||
private void AddWebHook_Click(object sender, RoutedEventArgs e) {
|
||||
Config.Global.Webhooks.Add(new DiscordWebhook {
|
||||
Name = "Discord Server Name",
|
||||
Webhook = "..."
|
||||
});
|
||||
Webhooks.Items.Refresh();
|
||||
RefreshPostMenu();
|
||||
}
|
||||
|
||||
private void RemoveWebHook_Click(object sender, RoutedEventArgs e) {
|
||||
if (Webhooks.SelectedItems.Count <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var selection = Webhooks.SelectedItems
|
||||
.OfType<DiscordWebhook>()
|
||||
.ToList()
|
||||
;
|
||||
foreach (var item in selection) {
|
||||
Config.Global.Webhooks.Remove(item);
|
||||
}
|
||||
Webhooks.Items.Refresh();
|
||||
RefreshPostMenu();
|
||||
}
|
||||
|
||||
private void Webhooks_KeyUp(object sender, KeyEventArgs e) {
|
||||
DataGridCell cell = e.OriginalSource as DataGridCell;
|
||||
/* We also get keypresses from DataGridCells that are currently
|
||||
* editing their content. Filter those out. We don't want to delete
|
||||
* the row when the user presses DEL while editing the cells content
|
||||
*/
|
||||
if (cell == null || cell.IsEditing) {
|
||||
return;
|
||||
}
|
||||
if (e.Key == Key.Delete) {
|
||||
RemoveWebHook_Click(this, new RoutedEventArgs());
|
||||
}
|
||||
}
|
||||
|
||||
private void Webhooks_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e) {
|
||||
try {
|
||||
Config.SaveGlobal();
|
||||
} catch (Exception) { }
|
||||
e.Cancel = false;
|
||||
RefreshPostMenu();
|
||||
}
|
||||
|
||||
private void RefreshPostMenu() {
|
||||
MenuItem menu;
|
||||
PostToDiscord.Items.Clear();
|
||||
if (Config.Global.Webhooks.Count <= 0) {
|
||||
PostToDiscord.IsEnabled = false;
|
||||
} else {
|
||||
PostToDiscord.IsEnabled = true;
|
||||
foreach (var item in Config.Global.Webhooks) {
|
||||
menu = new MenuItem();
|
||||
menu.Header = item.Name;
|
||||
menu.Click += DiscordWebhook_Click;
|
||||
PostToDiscord.Items.Add(menu);
|
||||
}
|
||||
PostToDiscord.Items.Add(new Separator());
|
||||
|
||||
menu = new MenuItem();
|
||||
menu.Header = "All";
|
||||
menu.Click += PostToAll_Click;
|
||||
PostToDiscord.Items.Add(menu);
|
||||
}
|
||||
}
|
||||
|
||||
private void PostToDiscordWebhook(IEnumerable<DiscordWebhook> hooks) {
|
||||
if (string.IsNullOrEmpty(DiscordLog.Text)) {
|
||||
return;
|
||||
}
|
||||
|
||||
DiscordLogGenerator discord = LogType.SelectedItem as DiscordLogGenerator;
|
||||
string[] chunks;
|
||||
|
||||
try {
|
||||
chunks = discord.SplitLog(DiscordLog.Text);
|
||||
} catch (Exception) {
|
||||
MessageBox.Show(
|
||||
"The log could not be split into discord appropriate length.\n" +
|
||||
"This happens with the bigger logs formats if you do lots of things in one system.\n" +
|
||||
"Try posting the log in the OneLine format.",
|
||||
"Sorry!", MessageBoxButton.OK, MessageBoxImage.Error
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
string commander = "EDBGS";
|
||||
if (Commanders.SelectedIndex >= 0) {
|
||||
commander = Commanders.SelectedItem.ToString();
|
||||
}
|
||||
|
||||
foreach (var hook in hooks) {
|
||||
try {
|
||||
foreach (var chunk in chunks) {
|
||||
DiscordPoster.PostToDiscord(hook, chunk, commander);
|
||||
}
|
||||
Log(string.Format("successfully posted to discord webhook `{0}`", hook.Name));
|
||||
} catch (Exception ex) {
|
||||
Log(string.Format("failed to post to discord webhook `{0}`: {1}",
|
||||
hook.Name, ex.Message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DiscordWebhook_Click(object sender, RoutedEventArgs e) {
|
||||
MenuItem item = sender as MenuItem;
|
||||
if (item == null) {
|
||||
return;
|
||||
}
|
||||
DiscordWebhook hook = Config.Global.Webhooks
|
||||
.Find(x => string.Compare(x.Name, item.Header.ToString()) == 0)
|
||||
;
|
||||
if (hook == null) {
|
||||
return;
|
||||
}
|
||||
PostToDiscordWebhook(new DiscordWebhook[] { hook });
|
||||
}
|
||||
|
||||
private void PostToAll_Click(object sender, RoutedEventArgs e) {
|
||||
PostToDiscordWebhook(Config.Global.Webhooks);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ public class MinusFortyFiveConverter : IValueConverter {
|
||||
/// <inheritdoc/>
|
||||
public object Convert(
|
||||
object value, Type targetType, object parameter, CultureInfo culture) {
|
||||
return (double)value - 65;
|
||||
return (double)value - 110;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@ -7,6 +7,12 @@ using System.Linq;
|
||||
namespace EliteBGS.BGS;
|
||||
|
||||
public class NonaDiscordLog : DiscordLogGenerator {
|
||||
protected override string BotHeader() {
|
||||
var sb = new StringBuilder();
|
||||
sb.AppendFormat(":robot: `Bot-Header:` {0}; {1}\n", GetToolVersion(), this.Name);
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private string FormatDate() {
|
||||
CultureInfo cultureInfo = CultureInfo.InvariantCulture;
|
||||
StringBuilder date = new StringBuilder();
|
||||
@ -51,7 +57,12 @@ public class NonaDiscordLog : DiscordLogGenerator {
|
||||
.Count()
|
||||
;
|
||||
|
||||
string summary = GenerateSummary(objective);
|
||||
|
||||
log.AppendFormat(":globe_with_meridians: `Target:` {0}\n", location);
|
||||
if (!string.IsNullOrEmpty(summary)) {
|
||||
log.AppendFormat(":scroll: `Summary:` {0}\n", summary);
|
||||
}
|
||||
if (legacycount > 0) {
|
||||
log.Append(":rotating_light: `Warning`: Some actions were done in E:D Legacy\n");
|
||||
}
|
||||
@ -74,6 +85,14 @@ public class NonaDiscordLog : DiscordLogGenerator {
|
||||
}
|
||||
|
||||
public override string ToString() {
|
||||
return "Nova Navy Log";
|
||||
return "Nova Navy";
|
||||
}
|
||||
|
||||
public override string Name {
|
||||
get { return "NovaNavy"; }
|
||||
}
|
||||
|
||||
public override string[] SplitLog(string log, int maxcount = 2000) {
|
||||
return SplitLogWithHeader(log, ":clock2: `Date:`", maxcount);
|
||||
}
|
||||
}
|
||||
|
@ -59,7 +59,9 @@ public class UITransaction : INotifyPropertyChanged {
|
||||
return Visibility.Hidden;
|
||||
}
|
||||
|
||||
if (string.Compare(combat.Type, CombatZones.ShipCombatZone) == 0) {
|
||||
// Both normal and power combat zones have special objectives
|
||||
if (string.Compare(combat.Type, CombatZones.ShipCombatZone) == 0 ||
|
||||
string.Compare(combat.Type, CombatZones.PowerCombatZone) == 0) {
|
||||
return Visibility.Visible;
|
||||
}
|
||||
|
||||
@ -120,14 +122,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 +137,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 +156,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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,7 +244,7 @@ public class UITransaction : INotifyPropertyChanged {
|
||||
}
|
||||
|
||||
public class Objective : IComparable<Objective> {
|
||||
public bool IsEnabled { get; set; }
|
||||
public bool IsEnabled { get; set; } = true;
|
||||
|
||||
public List<UITransaction> UITransactions { get; } = new List<UITransaction>();
|
||||
|
||||
|
@ -1,4 +1,11 @@
|
||||
namespace EliteBGS;
|
||||
using EliteBGS.LogGenerator;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Windows.Documents;
|
||||
|
||||
namespace EliteBGS;
|
||||
|
||||
public class OneLineDiscordLog : DiscordLogGenerator {
|
||||
protected override string GenerateObjectiveHeader(Objective objective) {
|
||||
@ -10,7 +17,7 @@ public class OneLineDiscordLog : DiscordLogGenerator {
|
||||
}
|
||||
|
||||
protected override string GenerateObjectiveFooter(Objective objective) {
|
||||
return "";
|
||||
return "\n";
|
||||
}
|
||||
|
||||
protected override string GenerateFooter() {
|
||||
@ -21,12 +28,71 @@ public class OneLineDiscordLog : DiscordLogGenerator {
|
||||
return "";
|
||||
}
|
||||
|
||||
protected override string TransformFinalLogForObjective(Objective objective, string log) {
|
||||
string[] lines = log.Split("\n", System.StringSplitOptions.RemoveEmptyEntries);
|
||||
return string.Join(", ", lines);
|
||||
public override string[] SplitLog(string log, int maxcount = 2000) {
|
||||
string[] lines = log.Split('\n');
|
||||
List<string> chunks = new();
|
||||
string chunk = string.Empty;
|
||||
|
||||
// Optimisation
|
||||
if (log.Length <= maxcount) {
|
||||
return new string[] { log };
|
||||
}
|
||||
|
||||
for (int i = 0; i < lines.Length; i++) {
|
||||
string line = lines[i];
|
||||
if ((chunk.Length + line.Length) > maxcount || i == lines.Length - 1) {
|
||||
chunks.Add(chunk.Trim());
|
||||
chunk = string.Empty;
|
||||
chunk = chunk.Insert(0, BotHeader()).Trim();
|
||||
} else {
|
||||
chunk = chunk + "\n" + line;
|
||||
}
|
||||
}
|
||||
|
||||
return chunks.ToArray();
|
||||
}
|
||||
|
||||
public override string GenerateDiscordLog(Report report) {
|
||||
StringBuilder log = new StringBuilder();
|
||||
|
||||
if (report == null) {
|
||||
return "";
|
||||
}
|
||||
|
||||
var objectives = report.Objectives
|
||||
.Where(x => x.IsEnabled && x.Transactions.Count() > 0)
|
||||
;
|
||||
|
||||
if (objectives == null || objectives.Count() <= 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
log.AppendFormat("{0}", BotHeader());
|
||||
|
||||
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() {
|
||||
return "One Line Report";
|
||||
return "One Line";
|
||||
}
|
||||
|
||||
public override string Name {
|
||||
get { return "OneLine"; }
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
using System.Reflection;
|
||||
using System.Resources;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Windows;
|
||||
|
||||
@ -51,5 +49,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.4.3.0")]
|
||||
[assembly: AssemblyFileVersion("0.4.3.0")]
|
||||
|
@ -117,8 +117,4 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</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>
|
@ -7,7 +7,7 @@ been parsed, you may then generate a BGS report you can copy/paste into Discord.
|
||||
|
||||
Source code is available at [https://codeberg.org/nola/edbgs](https://codeberg.org/nola/edbgs).
|
||||
|
||||
Binary downloads can be found here: [https://bgs.n0la.org/](https://bgs.n0la.org/).
|
||||
Binary downloads can be found here: [https://salusinvicta.org/bgstool/](https://salusinvicta.org/bgstool/).
|
||||
|
||||
## How To
|
||||
|
||||
@ -22,11 +22,14 @@ transactions. Currently the tool recognises the following transactions:
|
||||
* Selling cartography data
|
||||
* Selling of cargo to stations
|
||||
* Selling of micro resources (Odyssey only)
|
||||
* Selling of organic data (Odyssey only)
|
||||
* Vouchers, including bounty vouchers, combat bonds, and settlement vouchers (aka intel packages)
|
||||
* Thargoid kills
|
||||
* 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
|
||||
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.
|
||||
@ -227,7 +230,7 @@ The project also requires:
|
||||
|
||||
* `Ookii.Dialogs.WPF`
|
||||
* `Newtonsoft.Json`
|
||||
* `Extended.Wpf.Toolkit`
|
||||
* `MahApps`
|
||||
|
||||
## About
|
||||
|
||||
|
@ -1,10 +1,38 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using EDPlayerJournal.BGS;
|
||||
|
||||
namespace EliteBGS;
|
||||
|
||||
public class SystemObjectives : INotifyPropertyChanged, IComparable<SystemObjectives> {
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
private bool isenabled = true;
|
||||
|
||||
public bool IsEnabled {
|
||||
get { return isenabled; }
|
||||
set {
|
||||
isenabled = value;
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsEnabled"));
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsExpanded { get; set; } = false;
|
||||
|
||||
public string SystemName { get; set; } = string.Empty;
|
||||
|
||||
public List<Objective> Objectives { get; set; } = new();
|
||||
|
||||
public int CompareTo(SystemObjectives other) {
|
||||
if (other == null) return 1;
|
||||
return string.Compare(SystemName, other.SystemName, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
public class Report {
|
||||
public List<Objective> Objectives { get; set; } = new List<Objective>();
|
||||
public List<SystemObjectives> SystemObjectives { get; set; } = new();
|
||||
|
||||
public Report() { }
|
||||
|
||||
@ -12,29 +40,35 @@ public class Report {
|
||||
Populate(transactions);
|
||||
}
|
||||
|
||||
public List<Objective> Objectives {
|
||||
get {
|
||||
return SystemObjectives
|
||||
.Where(t => t.IsEnabled)
|
||||
.SelectMany(x => x.Objectives)
|
||||
.ToList()
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
private void Populate(List<Transaction> transactions) {
|
||||
if (transactions == null || transactions.Count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (Transaction t in transactions) {
|
||||
Objective o;
|
||||
if (t.SystemContribution) {
|
||||
o = Objectives.Find(x => x.Matches(t.System));
|
||||
} else {
|
||||
o = Objectives.Find(x => x.Matches(t.System, t.Faction));
|
||||
}
|
||||
|
||||
var o = SystemObjectives.Find(x => string.Compare(x.SystemName, t.System) == 0);
|
||||
if (o == null) {
|
||||
if (t.SystemContribution) {
|
||||
o = new Objective() { System = t.System };
|
||||
} else {
|
||||
o = new Objective() { Faction = t.Faction, System = t.System };
|
||||
}
|
||||
Objectives.Add(o);
|
||||
o = new SystemObjectives() { SystemName = t.System };
|
||||
SystemObjectives.Add(o);
|
||||
}
|
||||
|
||||
o.UITransactions.Add(new UITransaction(t));
|
||||
var objective = o.Objectives.Find(x => x.Matches(t.System, t.Faction));
|
||||
if (objective == null) {
|
||||
objective = new Objective() { Faction = t.Faction, System = t.System };
|
||||
o.Objectives.Add(objective);
|
||||
}
|
||||
objective.UITransactions.Add(new UITransaction(t));
|
||||
}
|
||||
|
||||
SystemObjectives.Sort();
|
||||
}
|
||||
}
|
||||
|
10
EliteBGS/Resources.Designer.cs
generated
10
EliteBGS/Resources.Designer.cs
generated
@ -60,16 +60,6 @@ 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>
|
||||
/// Looks up a localized resource of type System.Drawing.Bitmap.
|
||||
/// </summary>
|
||||
|
@ -118,9 +118,6 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</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>
|
||||
<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>
|
||||
</data>
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 44 KiB |
@ -1,38 +1,84 @@
|
||||
using System.ComponentModel;
|
||||
using Newtonsoft.Json;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace EliteBGS.Util {
|
||||
public class AppConfig : INotifyPropertyChanged {
|
||||
private static readonly string default_journal_location = "%UserProfile%\\Saved Games\\Frontier Developments\\Elite Dangerous";
|
||||
private string journal_location = default_journal_location;
|
||||
private string lastdiscordlog;
|
||||
namespace EliteBGS.Util;
|
||||
|
||||
public string DefaultJournalLocation => default_journal_location;
|
||||
public class AppConfig {
|
||||
private static readonly string default_journal_location = "%UserProfile%\\Saved Games\\Frontier Developments\\Elite Dangerous";
|
||||
private string journal_location = default_journal_location;
|
||||
private string colour = "Amber";
|
||||
private string theme = "Dark";
|
||||
|
||||
public string LastUsedDiscordTemplate {
|
||||
get => lastdiscordlog;
|
||||
set {
|
||||
lastdiscordlog = value;
|
||||
FirePropertyChanged("LastUsedDiscordTemplate");
|
||||
public static string DefaultJournalLocation => default_journal_location;
|
||||
|
||||
public string LastUsedDiscordTemplate { get; set; }
|
||||
|
||||
public string JournalLocation {
|
||||
get {
|
||||
if (journal_location == null) {
|
||||
return DefaultJournalLocation;
|
||||
}
|
||||
return journal_location;
|
||||
}
|
||||
set {
|
||||
journal_location = value;
|
||||
}
|
||||
}
|
||||
|
||||
public string Theme {
|
||||
get {
|
||||
return theme;
|
||||
}
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
theme = "Dark";
|
||||
} else {
|
||||
theme = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string JournalLocation {
|
||||
get {
|
||||
if (journal_location == null) {
|
||||
return DefaultJournalLocation;
|
||||
}
|
||||
return journal_location;
|
||||
}
|
||||
set {
|
||||
journal_location = value;
|
||||
FirePropertyChanged("JournalLocation");
|
||||
public string Colour {
|
||||
get {
|
||||
return colour;
|
||||
}
|
||||
set {
|
||||
if (string.IsNullOrEmpty(value)) {
|
||||
colour = "Blue";
|
||||
} else {
|
||||
colour = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FirePropertyChanged(string property) {
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
|
||||
}
|
||||
/// <summary>
|
||||
/// Whether we ignore influence support scenarios form parsing.
|
||||
/// </summary>
|
||||
public bool IgnoreInfluenceSupport { get; set; } = false;
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
/// <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;
|
||||
|
||||
/// <summary>
|
||||
/// Ignore power play?
|
||||
/// </summary>
|
||||
public bool IgnorePowerplay { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// List of Webhooks configured
|
||||
/// </summary>
|
||||
public List<DiscordWebhook> Webhooks { get; set; } = new List<DiscordWebhook>();
|
||||
|
||||
[JsonIgnore]
|
||||
public string FullTheme {
|
||||
get { return Theme + "." + Colour; }
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
using System.Text;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace EliteBGS.Util {
|
||||
public class Config {
|
||||
@ -12,15 +13,6 @@ namespace EliteBGS.Util {
|
||||
|
||||
public Config() {
|
||||
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;
|
||||
@ -45,6 +37,7 @@ namespace EliteBGS.Util {
|
||||
filestream.Flush();
|
||||
using (StreamWriter file = new StreamWriter(filestream, Encoding.UTF8)) {
|
||||
var stream = new JsonTextWriter(file);
|
||||
stream.Formatting = Formatting.Indented;
|
||||
serializer.Serialize(stream, global_config);
|
||||
}
|
||||
}
|
||||
@ -58,7 +51,6 @@ namespace EliteBGS.Util {
|
||||
|
||||
if (app != null) {
|
||||
this.global_config = app;
|
||||
global_config.PropertyChanged += Global_config_PropertyChanged;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
13
EliteBGS/Util/DiscordWebhook.cs
Normal file
13
EliteBGS/Util/DiscordWebhook.cs
Normal file
@ -0,0 +1,13 @@
|
||||
namespace EliteBGS.Util;
|
||||
|
||||
public class DiscordWebhook {
|
||||
/// <summary>
|
||||
/// Webhook URL
|
||||
/// </summary>
|
||||
public string Webhook { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Human readable name for easier identification
|
||||
/// </summary>
|
||||
public string Name { get; set; } = string.Empty;
|
||||
}
|
BIN
EliteBGS/combatzone.png
Normal file
BIN
EliteBGS/combatzone.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 75 KiB |
Binary file not shown.
Before Width: | Height: | Size: 51 KiB |
@ -1,21 +0,0 @@
|
||||
# Combat Zones
|
||||
|
||||
Starting with version 0.2.0, the EliteBGS tool will attempt to figure out when you
|
||||
have participated in a combat zone.
|
||||
|
||||
Since there is no official journal entry for combat zones as of yet (Update 13), the
|
||||
tool has to make an educated guess on what sort of combat zone it is.
|
||||
|
||||
For Odyssey on foot combat zones the tool can attempt to determine the difficulty
|
||||
from the from the highest combat bond you have been awarded. Enforcers in high on foot
|
||||
combat zones nets you roughly 88k credits, and the payout reduces from there.
|
||||
|
||||
Ship combat zones are more difficult. If you ship scan one of the warzone NPCs (either
|
||||
a captain, spec ops, or a correspondent), the tool can assume you are either in a medium
|
||||
or a high combat zone. If you get more than 10 kills, you are also either in a medium or
|
||||
high combat zone (a low combat zone is complete with 8 kills).
|
||||
|
||||
None of this perfect however, and the tool *will* get it wrong. For your convenience there
|
||||
are several small buttons next to the combat zone entry, where you can fix the result:
|
||||
|
||||

|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user