using Newtonsoft.Json.Linq; using EDPlayerJournal.Entries; using System.Runtime.CompilerServices; namespace EDPlayerJournal; public class FactionEffect { public string? Effect { get; set; } public string? EffectLocalised { get; set; } public string? Trend { get; set; } public static FactionEffect FromJSON(JObject obj) { FactionEffect effect = new FactionEffect(); effect.Effect = obj.Value<string?>("Effect"); effect.EffectLocalised = obj.Value<string?>("Effect_Localised"); effect.Trend = obj.Value<string?>("Trend"); return effect; } } public class MissionInfluence { /// <summary> /// System Address this influence happened in. /// </summary> public ulong? SystemAddress { get; set; } /// <summary> /// Generic description of the trend (up or down) /// </summary> public string? Trend { get; set; } /// <summary> /// Influence gained in plusses /// </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(); missionInfluence.SystemAddress = obj.Value<ulong?>("SystemAddress"); missionInfluence.Trend = obj.Value<string?>("Trend"); missionInfluence.Influence = obj.Value<string?>("Influence") ?? ""; return missionInfluence; } } public class MissionFactionEffects { /// <summary> /// Faction in question /// </summary> public string? Faction { get; set; } /// <summary> /// Reputation trend /// </summary> public string? ReputationTrend { get; set; } /// <summary> /// Reputation in pluses. /// </summary> public string Reputation { get; set; } = string.Empty; /// <summary> /// List of economic and security affects. /// </summary> public List<FactionEffect> Effects { get; set; } = new List<FactionEffect>(); /// <summary> /// Influence gained. /// </summary> public List<MissionInfluence> Influences { get; set; } = new List<MissionInfluence>(); /// <summary> /// Whether this affect is for an empty, or non-existant faction. /// </summary> public bool IsEmptyFaction { get { return string.IsNullOrEmpty(Faction); } } public static MissionFactionEffects FromJSON(JObject j) { MissionFactionEffects o = new MissionFactionEffects(); o.Faction = j.Value<string>("Faction"); o.ReputationTrend = j.Value<string?>("ReputationTrend"); o.Reputation = j.Value<string?>("Reputation") ?? string.Empty; JArray? effects = j.Value<JArray?>("Effects"); if (effects != null) { foreach (JObject effect in effects.Children().OfType<JObject>()) { FactionEffect e = FactionEffect.FromJSON(effect); o.Effects.Add(e); } } JArray? influences = j.Value<JArray?>("Influence"); if (influences != null) { foreach (JObject influence in influences.Children().OfType<JObject>()) { MissionInfluence missionInfluence = MissionInfluence.FromJSON(influence); o.Influences.Add(missionInfluence); } } return o; } } public class Mission : IComparable<Mission> { /// <summary> /// Passenger type for refugees /// </summary> public static readonly string PassengerTypeRefugee = "Refugee"; public ulong MissionID { get; set; } = 0; /// <summary> /// Name of the mission, in machine readable format. /// </summary> public string? Name { get; set; } /// <summary> /// Localised, human readable mission name. /// </summary> public string? LocalisedName { get; set; } /// <summary> /// Target of the mission. Optional. /// </summary> public string? Target { get; set; } /// <summary> /// Target of the mission as a localised human readable string. /// </summary> public string? TargetLocalised { get; set; } /// <summary> /// Target system of the mission /// </summary> public string? DestinationSystem { get; set; } /// <summary> /// Destination station. /// </summary> public string? DestinationStation { get; set; } /// <summary> /// Destination settlement /// </summary> public string? DestinationSettlement { get; set; } /// <summary> /// In case of redirection, these values denote the new system. /// </summary> public string? NewDestinationSystem { get; set; } /// <summary> /// In case of redirection, these values denote the new station. /// </summary> public string? NewDestinationStation { get; set; } /// <summary> /// Faction offering the mission. /// </summary> public string? Faction { get; set; } /// <summary> /// Target faction (for example for courier missions). /// </summary> public string? TargetFaction { get; set; } /// <summary> /// Whether the mission is a wing message. /// </summary> public bool Wing { get; set; } = false; /// <summary> /// Expiry date for the mission. /// </summary> public string? Expiry { get; set; } /// <summary> /// Certain missions have an expires number. No one knows what that is. /// </summary> public ulong? Expires { get; set; } /// <summary> /// Influence reward offered. This is for accepting missions only, see the /// mission effects for actual effects once the mission is complete. /// </summary> public string? Influence { get; set; } /// <summary> /// Reputation reward offered. /// </summary> public string? Reputation { get; set; } /// <summary> /// Number of kills required for massacre missions. /// </summary> public ulong? KillCount { get; set; } /// <summary> /// Monatery reward offered for the mission. Optional, as donate missions don't give /// a monatery reward. /// </summary> public ulong? Reward { get; set; } /// <summary> /// Amount donated for donation missions, optional. If this is null, then /// the mission was not a donation mission. /// </summary> public ulong? Donation { get; set; } /// <summary> /// Actual amount donated. /// </summary> public ulong? Donated { get; set; } /// <summary> /// Commodity delivered, or donated. Optional, if this is null, then no /// commodity was donated or delivered. /// </summary> public string? Commodity { get; set; } /// <summary> /// Amount of the commodity donated or delivered. Optional. /// </summary> public ulong? Count { get; set; } /// <summary> /// How many passengers are being transported. /// </summary> public ulong? PassengerCount { get; set; } /// <summary> /// If the passengers are VIPs. /// </summary> public bool? PassengerVIPs { get; set; } /// <summary> /// If the passengers are wanted. /// </summary> public bool? PassengerWanted { get; set; } /// <summary> /// What sort of passengers are being transported. /// </summary> public string? PassengerType { get; set; } /// <summary> /// Affect this mission had on factions. /// </summary> public List<MissionFactionEffects> FactionEffects { get; set; } = new List<MissionFactionEffects>(); /// <summary> /// Returns true if the name is an on foot mission. /// </summary> public bool IsOnFoot { get { if (string.IsNullOrEmpty(Name)) { return false; } return Name.Contains("OnFoot"); } } public bool IsPassengerMission { get { if (PassengerCount == null || PassengerCount == 0) { return false; } return true; } } public bool IsRescueMission { get { if (!IsPassengerMission) { return false; } if (string.Compare(PassengerType, PassengerTypeRefugee) == 0) { return true; } return false; } } /// <summary> /// Returns a friendly human-readable name for the mission. If a localised name is available /// it will use that, baring that it will check EnglishMissionNames for a translation, and /// if that ain't available either, it will just use the internal Name. /// </summary> public string FriendlyName { get { if (!string.IsNullOrEmpty(LocalisedName)) { return LocalisedName; } if (string.IsNullOrEmpty(Name)) { return "Unknown Mission"; } string? translate = EnglishMissionNames.Translate(Name); if (!string.IsNullOrEmpty(translate)) { return translate; } return Name; } } public int CompareTo(Mission? other) { if (other == null) { return 1; } return MissionID.CompareTo(other.MissionID); } private static Mission FromJSON(JObject o) { Mission mission = new Mission(); mission.MissionID = o.Value<ulong?>("MissionID") ?? 0; mission.Reputation = o.Value<string>("Reputation"); mission.Influence = o.Value<string>("Influence"); mission.DestinationSystem = o.Value<string>("DestinationSystem"); mission.DestinationSettlement = o.Value<string>("DestinationSettlement"); mission.DestinationStation = o.Value<string>("DestinationStation"); mission.NewDestinationSystem = o.Value<string>("NewDestinationSystem"); mission.NewDestinationStation = o.Value<string>("NewDestinationSystem"); mission.Reward = o.Value<ulong?>("Reward"); mission.Target = o.Value<string>("Target"); mission.TargetLocalised = o.Value<string>("Target_Localised"); mission.Expiry = o.Value<string?>("Expiry"); mission.Expires = o.Value<ulong?>("Expires"); mission.Wing = o.Value<bool?>("Wing") ?? false; mission.Name = o.Value<string>("Name"); mission.LocalisedName = o.Value<string>("LocalisedName"); mission.Faction = o.Value<string?>("Faction"); mission.TargetFaction = o.Value<string?>("TargetFaction"); mission.Donation = o.Value<ulong?>("Donation"); mission.Donated = o.Value<ulong?>("Donated"); mission.PassengerCount = o.Value<ulong?>("PassengerCount"); mission.PassengerVIPs = o.Value<bool?>("PassengerVIPs"); mission.PassengerWanted = o.Value<bool?>("PassengerWanted"); mission.PassengerType = o.Value<string>("PassengerType"); mission.KillCount = o.Value<ulong?>("KillCount"); JArray? factionEffects = o.Value<JArray?>("FactionEffects"); if (factionEffects != null) { foreach (JObject effect in factionEffects.Children().OfType<JObject>()) { MissionFactionEffects factionEffect = MissionFactionEffects.FromJSON(effect); mission.FactionEffects.Add(factionEffect); } } return mission; } /// <summary> /// Returns a list of all affected factions. /// </summary> public string[] AffectedFactions { get { return FactionEffects .Where(x => !string.IsNullOrEmpty(x.Faction)) .Select(x => (x.Faction ?? string.Empty)) .ToArray() ; } } /// <summary> /// Returns the influence for a given faction in a given star system. /// </summary> /// <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 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) .ToArray() ; if (results == null || results.Length == 0) { return new MissionInfluence[0]; } return results; } /// <summary> /// A convenient Dictionary containing all influences given out by faction, /// 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, MissionInfluence>> Influences { get { return FactionEffects .Where(x => x.Faction != null) .ToDictionary( x => (x.Faction ?? string.Empty), x => x.Influences .Where(x => x.SystemAddress != null) .ToDictionary( x => (x.SystemAddress ?? 0), x => x ) ); } } public static Mission FromMissionAccepted(JObject o) { return FromJSON(o); } public static Mission FromMissionAbandoned(JObject o) { return FromJSON(o); } public static Mission FromMissionFailed(JObject o) { return FromJSON(o); } public static Mission FromMissionCompleted(JObject o) { return FromJSON(o); } }