247 lines
11 KiB
C#
247 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using EDJournal;
|
|
|
|
namespace EliteBGS.BGS {
|
|
public class Report {
|
|
private List<Objective> objectives = new List<Objective>();
|
|
|
|
public delegate void OnLogDelegate(string log);
|
|
|
|
public event OnLogDelegate OnLog;
|
|
|
|
public List<Objective> Objectives {
|
|
get { return objectives; }
|
|
set { objectives = value; }
|
|
}
|
|
|
|
public bool AddObjective(Objective objective) {
|
|
var found = objectives.Find(x => x.CompareTo(objective) == 0);
|
|
bool added = false;
|
|
|
|
if (found == null) {
|
|
objectives.Add(objective);
|
|
added = true;
|
|
}
|
|
|
|
return added;
|
|
}
|
|
|
|
private bool IsRelevant(Entry e) {
|
|
return e.Is(Events.MissionCompleted) ||
|
|
e.Is(Events.MissionFailed) ||
|
|
e.Is(Events.MissionAccepted) ||
|
|
e.Is(Events.Docked) ||
|
|
e.Is(Events.FSDJump) ||
|
|
e.Is(Events.Location) ||
|
|
e.Is(Events.MultiSellExplorationData) ||
|
|
e.Is(Events.SellMicroResources) ||
|
|
e.Is(Events.RedeemVoucher) ||
|
|
e.Is(Events.FactionKillBond) ||
|
|
e.Is(Events.MarketBuy) ||
|
|
e.Is(Events.MarketSell)
|
|
;
|
|
}
|
|
|
|
public void Scan(PlayerJournal journal, DateTime start, DateTime end) {
|
|
var entries = from file in journal.Files
|
|
where file.NormalisedTimestamp >= start && file.NormalisedTimestamp <= end
|
|
select file.Entries
|
|
;
|
|
var relevant = from log in entries.SelectMany(array => array)
|
|
where IsRelevant(log)
|
|
select log
|
|
;
|
|
|
|
Dictionary<int, MissionAcceptedEntry> acceptedMissions = new Dictionary<int, MissionAcceptedEntry>();
|
|
|
|
Dictionary<string, int> buyCost = new Dictionary<string, int>();
|
|
|
|
string current_system = null;
|
|
string current_station = null;
|
|
string controlling_faction = null;
|
|
|
|
objectives.ForEach(x => x.Clear());
|
|
|
|
foreach (var e in relevant) {
|
|
List<LogEntry> results = new List<LogEntry>();
|
|
bool collate = false;
|
|
|
|
if (e.Is(Events.Docked)) {
|
|
DockedEntry docked = e as DockedEntry;
|
|
/* gleem the current station from this message
|
|
*/
|
|
current_station = docked.StationName;
|
|
current_system = docked.StarSystem;
|
|
controlling_faction = docked.StationFaction;
|
|
} else if (e.Is(Events.FSDJump)) {
|
|
/* Gleem current system and controlling faction from this message.
|
|
*/
|
|
current_system = (e as FSDJumpEntry).StarSystem;
|
|
controlling_faction = (e as FSDJumpEntry).SystemFaction;
|
|
} else if (e.Is(Events.Location)) {
|
|
/* Get current system, faction name and station from Location message
|
|
*/
|
|
LocationEntry location = e as LocationEntry;
|
|
|
|
current_system = location.StarSystem;
|
|
if (!string.IsNullOrEmpty(location.SystemFaction)) {
|
|
controlling_faction = location.SystemFaction;
|
|
}
|
|
if (!string.IsNullOrEmpty(location.StationName)) {
|
|
current_station = location.StationName;
|
|
}
|
|
} else if (e.Is(Events.MissionCompleted)) {
|
|
var completed = e as MissionCompletedEntry;
|
|
results.Add(new MissionCompleted(completed) {
|
|
System = current_system,
|
|
Station = current_station
|
|
});
|
|
if (completed.HumanReadableNameWasGenerated) {
|
|
/* If the human readable name was generated, we send a log message. Because the
|
|
* generated names all sort of suck, we should have more human readable names in
|
|
* in the lookup dictionary.
|
|
*/
|
|
OnLog?.Invoke("Human readable name for mission \"" +
|
|
completed.Name +
|
|
"\" was generated, please report this.");
|
|
}
|
|
|
|
foreach (string other in completed.AffectedFactions.Where(x => !x.Equals(results[0].Faction))) {
|
|
string faction = other;
|
|
string influence = completed.GetInfluenceForFaction(faction);
|
|
|
|
results.Add(new InfluenceSupport() {
|
|
Faction = faction,
|
|
Influence = influence,
|
|
RelevantMission = results[0] as MissionCompleted
|
|
});
|
|
}
|
|
} else if (e.Is(Events.MissionAccepted)) {
|
|
MissionAcceptedEntry accepted = e as MissionAcceptedEntry;
|
|
acceptedMissions[accepted.MissionID] = accepted;
|
|
} else if (e.Is(Events.MissionFailed)) {
|
|
var failed = e as MissionFailedEntry;
|
|
MissionAcceptedEntry accepted = null;
|
|
if (!acceptedMissions.TryGetValue(failed.MissionID, out accepted)) {
|
|
OnLog?.Invoke("A mission failed which wasn't accepted in the given time frame. " +
|
|
"Please adjust start date to when the mission was accepted to include it in the list.");
|
|
continue;
|
|
}
|
|
results.Add(new MissionFailed(accepted) { Failed = failed, System = current_system });
|
|
if (failed.HumanReadableName == null) {
|
|
OnLog?.Invoke("Human readable name for mission \"" +
|
|
failed.Name +
|
|
"\" was not recognised");
|
|
}
|
|
|
|
/* Mission failed should be collated if they are in the same system/station
|
|
*/
|
|
collate = true;
|
|
} else if (e.Is(Events.MultiSellExplorationData)) {
|
|
/* For multi-sell-exploraton-data only the controlling faction of the station sold to matters.
|
|
*/
|
|
results.Add(new Cartographics(e as MultiSellExplorationDataEntry) {
|
|
System = current_system,
|
|
Station = current_station,
|
|
Faction = controlling_faction
|
|
});
|
|
} else if (e.Is(Events.RedeemVoucher)) {
|
|
/* Same for selling combat vouchers. Only the current controlling faction matters here.
|
|
*/
|
|
results.Add(new Vouchers(e as RedeemVoucherEntry) {
|
|
System = current_system,
|
|
Station = current_station,
|
|
Faction = (e as RedeemVoucherEntry).Factions.FirstOrDefault() ?? "",
|
|
ControllingFaction = controlling_faction,
|
|
});
|
|
|
|
collate = true;
|
|
} else if (e.Is(Events.SellMicroResources)) {
|
|
results.Add(new SellMicroResources(e as SellMicroResourcesEntry) {
|
|
Faction = controlling_faction,
|
|
Station = current_station,
|
|
System = current_system
|
|
});
|
|
} else if (e.Is(Events.MarketBuy)) {
|
|
MarketBuyEntry buy = e as MarketBuyEntry;
|
|
if (string.IsNullOrEmpty(buy.Type) || buy.BuyPrice == 0) {
|
|
continue;
|
|
}
|
|
buyCost[buy.Type] = buy.BuyPrice;
|
|
} else if (e.Is(Events.MarketSell)) {
|
|
MarketSellEntry sell = e as MarketSellEntry;
|
|
int profit = 0;
|
|
|
|
if (!buyCost.ContainsKey(sell.Type)) {
|
|
OnLog?.Invoke("Could not find buy order for the given commodity. Please adjust profit manually.");
|
|
} else {
|
|
int avg = buyCost[sell.Type];
|
|
profit = sell.TotalSale - (avg * sell.Count);
|
|
}
|
|
|
|
results.Add(new SellCargo(e as MarketSellEntry) {
|
|
Faction = controlling_faction,
|
|
Station = current_station,
|
|
System = current_system,
|
|
Profit = profit
|
|
});
|
|
}
|
|
|
|
if (results == null || results.Count <= 0) {
|
|
continue;
|
|
}
|
|
|
|
foreach (LogEntry entry in results) {
|
|
/* Find all objectives that generally match.
|
|
*/
|
|
var matches = objectives
|
|
.Where(x => x.Matches(entry) > 0)
|
|
.OrderBy(x => x.Matches(entry))
|
|
;
|
|
|
|
Objective objective = null;
|
|
if (matches != null && matches.Count() > 0) {
|
|
/* Then select the one that matches the most.
|
|
*/
|
|
objective = matches
|
|
.OrderBy(x => x.Matches(entry))
|
|
.Reverse()
|
|
.First()
|
|
;
|
|
} else {
|
|
/* create a new objective if we don't have one */
|
|
objective = new Objective() {
|
|
Station = entry.Station,
|
|
Faction = entry.Faction,
|
|
System = entry.System,
|
|
};
|
|
objectives.Add(objective);
|
|
}
|
|
|
|
LogEntry existing = null;
|
|
|
|
existing = objective.LogEntries.Find(x => {
|
|
try {
|
|
return x.CompareTo(entry) == 0;
|
|
} catch (NotImplementedException) {
|
|
return false;
|
|
}
|
|
});
|
|
|
|
if (collate && existing != null) {
|
|
existing.Entries.Add(e);
|
|
} else if (!collate || existing == null) {
|
|
objective.LogEntries.Add(entry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Scan(PlayerJournal journal) {
|
|
Scan(journal, DateTime.Now, DateTime.Now);
|
|
}
|
|
}
|
|
}
|