3 using Microsoft.Xna.Framework;
5 using System.Collections.Generic;
6 using System.Collections.Immutable;
7 using System.Globalization;
15 class MissionRequirement
23 MissionIdentifier = element.GetAttributeIdentifier(nameof(MissionIdentifier),
Identifier.Empty);
24 MissionTag = element.GetAttributeIdentifier(nameof(MissionTag),
Identifier.Empty);
28 DebugConsole.ThrowError($
"Error in traitor event \"{prefab.Identifier}\". Mission requirement with no {nameof(MissionIdentifier)}, {nameof(MissionTag)} or {nameof(MissionType)}.",
33 public bool Match(
Mission mission)
35 if (mission ==
null) {
return MissionIdentifier.IsEmpty && MissionTag.IsEmpty &&
MissionType ==
MissionType.None; }
36 if (!MissionIdentifier.IsEmpty)
40 else if (!MissionTag.IsEmpty)
42 return mission.
Prefab.
Tags.Contains(MissionTag);
52 class LevelRequirement
54 private enum LevelType
61 private readonly LevelType levelType;
62 public ImmutableArray<Identifier> LocationTypes {
get; }
67 private readonly
float minDifficulty;
71 private readonly
float minDifficultyInCampaign;
74 public ImmutableArray<PropertyConditional> RequiredItemConditionals;
78 levelType = element.GetAttributeEnum(nameof(LevelType), LevelType.Any);
81 minDifficultyInCampaign = element.
GetAttributeFloat(nameof(minDifficultyInCampaign), Math.Max(minDifficulty, 5.0f));
82 List<PropertyConditional> requiredItemConditionals =
new List<PropertyConditional>();
83 foreach (var subElement
in element.
Elements())
85 if (subElement.NameAsIdentifier() ==
"itemconditional")
90 RequiredItemConditionals = requiredItemConditionals.ToImmutableArray();
93 public bool Match(
Level level)
95 if (level?.
LevelData ==
null) {
return false; }
98 case LevelType.LocationConnection:
101 case LevelType.Outpost:
107 if (level.
Difficulty < minDifficultyInCampaign) {
return false; }
111 if (level.
Difficulty < minDifficulty) {
return false; }
115 if (LocationTypes.Any()) {
return false; }
121 if (RequiredItemConditionals.Any())
123 bool matchFound =
false;
126 if (RequiredItemConditionals.All(c => item.ConditionalMatches(c)))
132 if (!matchFound) {
return false; }
138 class ReputationRequirement
142 public float CompareToValue;
150 string conditionStr = element.GetAttributeString(
"reputation",
string.Empty);
152 string[] splitString = conditionStr.Split(
' ');
154 if (splitString.Length > 0)
157 value =
string.Join(
" ", splitString.Skip(1));
161 DebugConsole.ThrowError(
162 $
"{conditionStr} in {prefab.Identifier} is too short."+
163 "It should start with an operator followed by a faction identifier or a floating point value.");
168 if (
float.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var floatVal))
170 CompareToValue = floatVal;
174 CompareToFaction = value.ToIdentifier();
181 if (faction1 ==
null)
183 DebugConsole.ThrowError($
"Could not find the faction {Faction}.");
186 if (!CompareToFaction.IsEmpty)
189 if (faction2 ==
null)
191 DebugConsole.ThrowError($
"Could not find the faction {CompareToFaction}.");
206 public ImmutableHashSet<Identifier>
Tags;
208 private readonly ImmutableArray<ReputationRequirement> reputationRequirements;
209 private readonly ImmutableArray<MissionRequirement> missionRequirements;
210 private readonly ImmutableArray<LevelRequirement> levelRequirements;
268 : base(element, file, fallbackIdentifier)
291 List<ReputationRequirement> reputationRequirements =
new List<ReputationRequirement>();
292 List<LevelRequirement> levelRequirements =
new List<LevelRequirement>();
293 List<MissionRequirement> missionRequirements =
new List<MissionRequirement>();
294 foreach (var subElement
in element.Elements())
296 switch (subElement.Name.ToString().ToLowerInvariant())
298 case "reputationrequirement":
299 reputationRequirements.Add(
new ReputationRequirement(subElement!,
this));
301 case "missionrequirement":
302 missionRequirements.Add(
new MissionRequirement(subElement!,
this));
304 case "levelrequirement":
305 levelRequirements.Add(
new LevelRequirement(subElement!,
this));
309 IconColor = subElement.GetAttributeColor(
"color", Color.White);
313 this.reputationRequirements = reputationRequirements.ToImmutableArray();
314 this.levelRequirements = levelRequirements.ToImmutableArray();
315 this.missionRequirements = missionRequirements.ToImmutableArray();
320 if (campaign ==
null)
325 foreach (ReputationRequirement requirement
in reputationRequirements)
327 if (!requirement.Match(campaign)) {
return false; }
333 if (gameSession ==
null) {
return false; }
334 foreach (MissionRequirement requirement
in missionRequirements)
336 if (gameSession.Missions.None(m => requirement.Match(m))) {
return false; }
342 if (level ==
null) {
return false; }
348 foreach (LevelRequirement requirement
in levelRequirements)
350 if (!requirement.Match(level)) {
return false; }
362 return $
"{nameof(TraitorEventPrefab)} ({Identifier})";
Faction GetFaction(Identifier identifier)
Identifier[] GetAttributeIdentifierArray(Identifier[] def, params string[] keys)
float GetAttributeFloat(string key, float def)
IEnumerable< ContentXElement > Elements()
bool GetAttributeBool(string key, bool def)
int GetAttributeInt(string key, int def)
ImmutableHashSet< Identifier > GetAttributeIdentifierImmutableHashSet(string key, ImmutableHashSet< Identifier >? def, bool trim=true)
readonly Identifier Faction
If set, the event set can only be chosen in locations that belong to this faction.
static GameSession?? GameSession
static readonly List< Item > ItemList
readonly LevelData LevelData
readonly MissionPrefab Prefab
readonly MissionType Type
readonly ImmutableHashSet< Identifier > Tags
ContentPackage? ContentPackage
readonly Identifier Identifier
Conditionals are used by some in-game mechanics to require one or more conditions to be met for those...
static ComparisonOperatorType GetComparisonOperatorType(string op)
ComparisonOperatorType
There are several ways to compare properties to values. The comparison operator to use can be specifi...
static bool CompareFloat(float val1, float val2, ComparisonOperatorType op)
static IEnumerable< PropertyConditional > FromXElement(ContentXElement element, Predicate< XAttribute >? predicate=null)
readonly int RequiredPreviousDangerLevel
An event of this danger level (or higher) must have been selected previously for this event to trigge...
bool ReputationRequirementsMet(CampaignMode? campaign)
readonly int MoneyPenaltyForUnfoundedTraitorAccusation
Money penalty if the crew votes a wrong player as the traitor
readonly float StealPercentageOfExperience
bool HasLevelRequirements
bool MissionRequirementsMet(GameSession? gameSession)
bool LevelRequirementsMet(Level? level)
readonly bool RequirePreviousDangerLevelCompleted
An event of a lower danger level must have been completed on the previous round for this event to tri...
readonly int MinPlayerCount
Minimum number of non-spectating human players on the server for the event to get selected.
bool HasReputationRequirements
readonly bool AllowAccusingSecondaryTraitor
Does accusing a secondary traitor count as correctly identifying the traitor?
TraitorEventPrefab(ContentXElement element, RandomEventsFile file, Identifier fallbackIdentifier=default)
bool HasMissionRequirements
override string ToString()
readonly int SecondaryTraitorAmount
Number of players to assign as a "secondary traitor". If both this and SecondaryTraitorPercentage are...
readonly bool IsChainable
Is this event chainable, i.e. does the same traitor get another, higher-lvl one if they complete this...
ImmutableHashSet< Identifier > Tags
readonly float SecondaryTraitorPercentage
Percentage of players to assign as a "secondary traitor".
ImmutableHashSet< Identifier > RequiredCompletedTags
An event with one of these tags must've been completed previously for this event to trigger.