2 using Microsoft.Xna.Framework;
4 using System.Collections.Generic;
5 using System.Collections.Immutable;
18 [
Serialize(
"",
IsPropertySaveable.Yes, description:
"Tag of the mission to unlock. If there are multiple missions with the tag, one is chosen randomly.")]
21 [
Serialize(
"",
IsPropertySaveable.Yes, description:
"The mission can only be unlocked in a location that's occupied by this faction.")]
26 [
Serialize(0,
IsPropertySaveable.Yes, description:
"Minimum distance to the location the mission is unlocked in (1 = one path between locations).")]
29 [
Serialize(
true,
IsPropertySaveable.Yes, description:
"If true, the mission has to be unlocked in a location further on the campaign map.")]
35 private bool isFinished;
37 private readonly
Random random;
43 DebugConsole.ThrowError($
"Error in event \"{parentEvent.Prefab.Identifier}\": neither MissionIdentifier or MissionTag has been configured.",
48 DebugConsole.ThrowError($
"Error in event \"{parentEvent.Prefab.Identifier}\": both MissionIdentifier or MissionTag have been configured. The tag will be ignored.",
71 public override void Update(
float deltaTime)
73 if (isFinished) {
return; }
84 DebugConsole.NewMessage($
"Failed to find a suitable location to unlock the mission \"{missionDebugId}\" further on the map. Attempting to find a location earlier on the map...");
90 DebugConsole.NewMessage($
"Failed to find a suitable location to unlock the mission \"{missionDebugId}\". Attempting to change the type of an empty location to create a suitable location...");
92 var emptyLocation = FindUnlockLocation(Math.Max(
MinLocationDistance, 3), unlockFurtherOnMap:
true,
"none".ToIdentifier().ToEnumerable(),
93 mustAllowLocationTypeChanges:
true,
94 requireCorrectFaction:
false);
95 if (emptyLocation ==
null)
97 DebugConsole.NewMessage($
"Failed to find a suitable empty location further on the map. Attempting to find a location earlier on the map...");
98 emptyLocation = FindUnlockLocation(Math.Max(
MinLocationDistance, 3), unlockFurtherOnMap:
false,
"none".ToIdentifier().ToEnumerable(),
99 mustAllowLocationTypeChanges:
true,
100 requireCorrectFaction:
false);
102 if (emptyLocation !=
null)
104 System.Diagnostics.Debug.Assert(!emptyLocation.LocationTypeChangesBlocked);
106 unlockLocation = emptyLocation;
109 emptyLocation.Faction = campaign.Factions.Find(f => f.Prefab.Identifier ==
RequiredFaction);
114 if (unlockLocation !=
null)
123 unlockedMission = unlockLocation.UnlockMissionByTag(
MissionTag, random,
130 if (unlockedMission !=
null)
133 campaign.Map.Discover(unlockLocation, checkTalents:
false);
136 DebugConsole.NewMessage($
"Unlocked mission \"{unlockedMission.Name}\" in the location \"{unlockLocation.DisplayName}\".");
140 DebugConsole.NewMessage($
"Unlocked mission \"{unlockedMission.Name}\" in the connection from \"{unlockedMission.Locations[0].DisplayName}\" to \"{unlockedMission.Locations[1].DisplayName}\".");
143 new GUIMessageBox(
string.Empty, TextManager.GetWithVariable(
"missionunlocked",
"[missionname]", unlockedMission.
Name),
149 missionsUnlockedThisRound.Add(unlockedMission);
150 NotifyMissionUnlock(unlockedMission);
156 DebugConsole.AddWarning($
"Failed to find a suitable location to unlock the mission \"{missionDebugId}\" (LocationType: {string.Join(",
", LocationTypes)}, MinLocationDistance: {MinLocationDistance}, UnlockFurtherOnMap: {UnlockFurtherOnMap})",
163 private Location FindUnlockLocation(
int minDistance,
bool unlockFurtherOnMap, IEnumerable<Identifier> locationTypes,
bool mustAllowLocationTypeChanges,
bool requireCorrectFaction =
true)
171 var currentLocation = campaign.Map.CurrentLocation;
173 HashSet<Location> checkedLocations =
new HashSet<Location>();
174 HashSet<Location> pendingLocations =
new HashSet<Location>() { currentLocation };
177 List<Location> currentLocations = pendingLocations.ToList();
178 pendingLocations.Clear();
179 foreach (var location
in currentLocations)
181 checkedLocations.Add(location);
182 if (IsLocationValid(currentLocation, location, unlockFurtherOnMap, distance, minDistance, locationTypes, mustAllowLocationTypeChanges, requireCorrectFaction))
188 foreach (LocationConnection connection
in location.Connections)
190 var otherLocation = connection.OtherLocation(location);
191 if (checkedLocations.Contains(otherLocation)) {
continue; }
192 pendingLocations.Add(otherLocation);
197 }
while (pendingLocations.Any());
202 private bool IsLocationValid(Location currLocation, Location location,
bool unlockFurtherOnMap,
int distance,
int minDistance, IEnumerable<Identifier> locationTypes,
bool mustAllowLocationTypeChanges,
bool requireCorrectFaction)
204 if (mustAllowLocationTypeChanges && location.LocationTypeChangesBlocked)
216 if (!locationTypes.Contains(location.Type.Identifier) && !(location.HasOutpost() && locationTypes.Contains(Tags.AnyOutpost)))
220 if (distance < minDistance)
224 if (unlockFurtherOnMap && location.MapPosition.X < currLocation.MapPosition.X)
233 return $
"{ToolBox.GetDebugSymbol(isFinished)} {nameof(MissionAction)} -> ({(MissionIdentifier.IsEmpty ? MissionTag : MissionIdentifier)})";
Identifier[] GetAttributeIdentifierArray(Identifier[] def, params string[] keys)
ContentPackage? ContentPackage
readonly ScriptedEvent ParentEvent
static GameSession?? GameSession
static readonly PrefabCollection< LocationType > Prefabs
Mersenne Twister based random
Unlocks a mission in a nearby level or location.
Identifier RequiredFaction
override bool IsFinished(ref string goTo)
Has the action finished.
override string ToDebugString()
Rich test to display in debugdraw
override void Update(float deltaTime)
ImmutableArray< Identifier > LocationTypes
bool CreateLocationIfNotFound
Identifier MissionIdentifier
MissionAction(ScriptedEvent parentEvent, ContentXElement element)
readonly MissionPrefab Prefab
virtual LocalizedString Name
Location OriginLocation
Where was this mission received from? Affects which faction we give reputation for if the mission is ...
readonly Location[] Locations
ContentPackage? ContentPackage
readonly Identifier Identifier
List< EventAction > Actions