4 using Microsoft.Xna.Framework;
6 using System.Collections.Generic;
12 static class HintManager
14 private const string HintManagerFile =
"hintmanager.xml";
16 public static bool Enabled => !GameSettings.CurrentConfig.DisableInGameHints;
17 private static HashSet<Identifier> HintIdentifiers {
get;
set; }
18 private static Dictionary<Identifier, HashSet<Identifier>> HintTags {
get; } =
new Dictionary<Identifier, HashSet<Identifier>>();
19 private static Dictionary<Identifier, (Identifier identifier, Identifier option)> HintOrders {
get; } =
new Dictionary<Identifier, (Identifier orderIdentifier, Identifier orderOption)>();
23 private static HashSet<Identifier> HintsIgnoredThisRound {
get; } =
new HashSet<Identifier>();
24 private static GUIMessageBox ActiveHintMessageBox {
get;
set; }
25 private static Action OnUpdate {
get;
set; }
26 private static double TimeStoppedInteracting {
get;
set; }
27 private static double TimeRoundStarted {
get;
set; }
31 private static int TimeBeforeReminders {
get;
set; }
35 private static int ReminderCooldown {
get;
set; }
36 private static double TimeReminderLastDisplayed {
get;
set; }
37 private static HashSet<Hull> BallastHulls {
get; } =
new HashSet<Hull>();
39 public static void Init()
41 if (File.Exists(HintManagerFile))
43 var doc = XMLExtensions.TryLoadXml(HintManagerFile);
44 if (doc?.Root !=
null)
46 HintIdentifiers =
new HashSet<Identifier>();
47 foreach (var element
in doc.Root.Elements())
49 GetHintsRecursive(element, element.NameAsIdentifier());
54 DebugConsole.ThrowError($
"File \"{HintManagerFile}\" is empty - cannot initialize the HintManager!");
59 DebugConsole.ThrowError($
"File \"{HintManagerFile}\" is missing - cannot initialize the HintManager!");
62 static void GetHintsRecursive(XElement element, Identifier identifier)
64 if (!element.HasElements)
66 HintIdentifiers.Add(identifier);
67 if (element.GetAttributeIdentifierArray(
"tags",
null) is Identifier[] tags)
69 HintTags.TryAdd(identifier, tags.ToHashSet());
71 if (element.GetAttributeIdentifier(
"order", Identifier.Empty) is Identifier orderIdentifier && orderIdentifier != Identifier.Empty)
73 Identifier orderOption = element.GetAttributeIdentifier(
"orderoption", Identifier.Empty);
74 HintOrders.Add(identifier, (orderIdentifier, orderOption));
78 else if (element.Name.ToString().Equals(
"reminder"))
80 TimeBeforeReminders = element.GetAttributeInt(
"timebeforereminders", TimeBeforeReminders);
81 ReminderCooldown = element.GetAttributeInt(
"remindercooldown", ReminderCooldown);
83 foreach (var childElement
in element.Elements())
85 GetHintsRecursive(childElement, $
"{identifier}.{childElement.Name}".ToIdentifier());
90 public static void Update()
92 if (HintIdentifiers ==
null || GameSettings.CurrentConfig.DisableInGameHints) {
return; }
93 if (GameMain.GameSession ==
null || !GameMain.GameSession.IsRunning) {
return; }
95 if (ActiveHintMessageBox !=
null)
97 if (ActiveHintMessageBox.Closed)
99 ActiveHintMessageBox =
null;
109 CheckIsInteracting();
110 CheckIfDivingGearOutOfOxygen();
115 public static void OnSetSelectedItem(Character character, Item oldItem, Item newItem)
117 if (oldItem == newItem) {
return; }
119 if (
Character.Controlled !=
null &&
Character.Controlled == character && oldItem !=
null && !oldItem.IsLadder)
121 TimeStoppedInteracting = Timing.TotalTime;
124 if (newItem ==
null) {
return; }
125 if (newItem.IsLadder) {
return; }
127 OnStartedInteracting(character, newItem);
130 private static void OnStartedInteracting(Character character, Item item)
132 if (!CanDisplayHints()) {
return; }
133 if (character !=
Character.Controlled || item ==
null) {
return; }
135 string hintIdentifierBase =
"onstartedinteracting";
138 if (item.Repairables.Any(r => r.IsBelowRepairThreshold))
140 if (DisplayHint($
"{hintIdentifierBase}.brokenitem".ToIdentifier())) {
return; }
144 if (item.Repairables.Any(r => r.ShouldDrawHUD(character))) {
return; }
148 item.ContainedItems.Any(i => !i.AllowStealing))
150 if (DisplayHint($
"{hintIdentifierBase}.lootingisstealing".ToIdentifier())) {
return; }
154 if (item.HasTag(Tags.Periscope) &&
155 item.GetConnectedComponents<
Turret>().FirstOrDefault(t => t.Item.HasTag(Tags.Turret)) is
Turret)
157 if (DisplayHint($
"{hintIdentifierBase}.turretperiscope".ToIdentifier(),
160 (
"[shootkey]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(
InputType.Shoot)),
161 (
"[deselectkey]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(
InputType.Deselect))
167 hintIdentifierBase +=
".item";
168 foreach (Identifier hintIdentifier
in HintIdentifiers)
170 if (!hintIdentifier.StartsWith(hintIdentifierBase)) {
continue; }
171 if (!HintTags.TryGetValue(hintIdentifier, out var hintTags)) {
continue; }
172 if (!item.HasTag(hintTags)) {
continue; }
173 if (DisplayHint(hintIdentifier)) {
return; }
177 public static void OnStartRepairing(Character character,
Repairable repairable)
181 CoroutineManager.Invoke(() =>
183 DisplayHint($
"repairingsabotageditem".ToIdentifier());
188 public static void OnItemMarkedForRelocation()
190 DisplayHint($
"onitemmarkedforrelocation".ToIdentifier());
193 public static void OnItemMarkedForDeconstruction(Character character)
197 DisplayHint($
"onitemmarkedfordeconstruction".ToIdentifier());
201 private static void CheckIsInteracting()
203 if (!CanDisplayHints()) {
return; }
204 if (
Character.Controlled?.SelectedItem ==
null) {
return; }
207 Character.Controlled.SelectedItem.OwnInventory?.AllItems is IEnumerable<Item> containedItems &&
208 containedItems.Count(i => i.HasTag(Tags.ReactorFuel)) > 1)
210 if (DisplayHint(
"onisinteracting.reactorwithextrarods".ToIdentifier())) {
return; }
214 public static void OnRoundStarted()
218 TimeRoundStarted = GameMain.GameScreen.GameTime;
220 var initRoundHandle = CoroutineManager.StartCoroutine(InitRound(),
"HintManager.InitRound");
221 if (!CanDisplayHints(requireGameScreen:
false, requireControllingCharacter:
false)) {
return; }
222 CoroutineManager.StartCoroutine(DisplayRoundStartedHints(initRoundHandle),
"HintManager.DisplayRoundStartedHints");
224 static IEnumerable<CoroutineStatus> InitRound()
226 while (
Character.Controlled ==
null) { yield
return CoroutineStatus.Running; }
228 BallastHulls.Clear();
229 var sub =
Submarine.MainSubs.FirstOrDefault(s => s !=
null && s.TeamID ==
Character.Controlled.TeamID);
232 foreach (var item
in sub.GetItems(
true))
234 if (item.CurrentHull ==
null) {
continue; }
235 if (item.GetComponent<
Pump>() ==
null) {
continue; }
236 if (!item.HasTag(Tags.Ballast) && !item.CurrentHull.RoomName.Contains(
"ballast", StringComparison.OrdinalIgnoreCase)) {
continue; }
237 BallastHulls.Add(item.CurrentHull);
240 yield
return CoroutineStatus.Success;
243 static IEnumerable<CoroutineStatus> DisplayRoundStartedHints(CoroutineHandle initRoundHandle)
245 while (GameMain.Instance.LoadingScreenOpen || Screen.Selected != GameMain.GameScreen ||
246 CoroutineManager.IsCoroutineRunning(initRoundHandle) ||
247 CoroutineManager.IsCoroutineRunning(
"LevelTransition") ||
248 CoroutineManager.IsCoroutineRunning(
"SinglePlayerCampaign.DoInitialCameraTransition") ||
249 CoroutineManager.IsCoroutineRunning(
"MultiPlayerCampaign.DoInitialCameraTransition") ||
250 GUIMessageBox.VisibleBox !=
null ||
Character.Controlled ==
null)
252 yield
return CoroutineStatus.Running;
255 OnStartedControlling();
257 while (ActiveHintMessageBox !=
null)
259 yield
return CoroutineStatus.Running;
262 if (!GameMain.GameSession.GameMode.IsSinglePlayer &&
263 GameSettings.CurrentConfig.Audio.VoiceSetting ==
VoiceMode.Disabled)
265 DisplayHint(
"onroundstarted.voipdisabled".ToIdentifier(), onUpdate: () =>
267 if (GameSettings.CurrentConfig.Audio.VoiceSetting ==
VoiceMode.Disabled) { return; }
268 ActiveHintMessageBox.Close();
272 if (GameMain.GameSession is { TraitorsEnabled: true })
274 DisplayHint(
"traitorsonboard".ToIdentifier());
275 DisplayHint(
"traitorsonboard2".ToIdentifier());
277 yield
return CoroutineStatus.Success;
282 public static void OnRoundEnded()
287 private static void Reset()
289 CoroutineManager.StopCoroutines(
"HintManager.InitRound");
290 CoroutineManager.StopCoroutines(
"HintManager.DisplayRoundStartedHints");
291 if (ActiveHintMessageBox !=
null)
293 GUIMessageBox.MessageBoxes.Remove(ActiveHintMessageBox);
294 ActiveHintMessageBox =
null;
297 HintsIgnoredThisRound.Clear();
300 public static void OnSonarSpottedCharacter(Item sonar, Character spottedCharacter)
302 if (!CanDisplayHints()) {
return; }
303 if (sonar ==
null || sonar.Removed) {
return; }
304 if (spottedCharacter ==
null || spottedCharacter.Removed || spottedCharacter.IsDead) {
return; }
305 if (
Character.Controlled.SelectedItem != sonar) {
return; }
306 if (HumanAIController.IsFriendly(
Character.Controlled, spottedCharacter)) {
return; }
307 DisplayHint(
"onsonarspottedenemy".ToIdentifier());
310 public static void OnAfflictionDisplayed(Character character, List<Affliction> displayedAfflictions)
312 if (!CanDisplayHints()) {
return; }
313 if (character !=
Character.Controlled || displayedAfflictions ==
null) {
return; }
314 foreach (var affliction
in displayedAfflictions)
316 if (affliction?.Prefab ==
null) {
continue; }
317 if (affliction.Prefab.IsBuff) {
continue; }
318 if (affliction.Prefab == AfflictionPrefab.OxygenLow) {
continue; }
319 if (affliction.Prefab == AfflictionPrefab.RadiationSickness && (GameMain.GameSession.Map?.Radiation?.IsEntityRadiated(character) ??
false)) {
continue; }
320 if (affliction.Strength < affliction.Prefab.ShowIconThreshold) {
continue; }
321 DisplayHint(
"onafflictiondisplayed".ToIdentifier(),
322 variables:
new[] { (
"[key]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(
InputType.Health)) },
323 icon: affliction.Prefab.Icon,
324 iconColor: CharacterHealth.GetAfflictionIconColor(affliction),
327 if (CharacterHealth.OpenHealthWindow ==
null) {
return; }
328 ActiveHintMessageBox.Close();
334 public static void OnShootWithoutAiming(Character character, Item item)
336 if (!CanDisplayHints()) {
return; }
337 if (character !=
Character.Controlled) {
return; }
338 if (character.HasSelectedAnyItem || character.FocusedItem !=
null) {
return; }
339 if (item ==
null || !item.IsShootable || !item.RequireAimToUse) {
return; }
340 if (TimeStoppedInteracting + 1 > Timing.TotalTime) {
return; }
341 if (GUI.MouseOn !=
null) {
return; }
342 if (
Character.Controlled.Inventory?.visualSlots !=
null &&
Character.Controlled.Inventory.visualSlots.Any(s => s.InteractRect.Contains(PlayerInput.MousePosition))) {
return; }
343 Identifier hintIdentifier =
"onshootwithoutaiming".ToIdentifier();
344 if (!HintTags.TryGetValue(hintIdentifier, out var tags)) {
return; }
345 if (!item.HasTag(tags)) {
return; }
346 DisplayHint(hintIdentifier,
347 variables:
new[] { (
"[key]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(
InputType.Aim)) },
350 if (character.SelectedItem ==
null && GUI.MouseOn ==
null && PlayerInput.KeyDown(
InputType.Aim))
352 ActiveHintMessageBox.Close();
357 public static void OnWeldingDoor(Character character,
Door door)
359 if (!CanDisplayHints()) {
return; }
360 if (character !=
Character.Controlled) {
return; }
361 if (door ==
null || door.
Stuck < 20.0f) {
return; }
362 DisplayHint(
"onweldingdoor".ToIdentifier());
365 public static void OnTryOpenStuckDoor(Character character)
367 if (!CanDisplayHints()) {
return; }
368 if (character !=
Character.Controlled) {
return; }
369 DisplayHint(
"ontryopenstuckdoor".ToIdentifier());
372 public static void OnShowCampaignInterface(CampaignMode.InteractionType interactionType)
374 if (!CanDisplayHints()) {
return; }
375 if (interactionType == CampaignMode.InteractionType.None) {
return; }
376 Identifier hintIdentifier = $
"onshowcampaigninterface.{interactionType}".ToIdentifier();
377 DisplayHint(hintIdentifier, onUpdate: () =>
380 if (!(GameMain.GameSession?.Campaign is CampaignMode campaign) ||
381 (!campaign.ShowCampaignUI && !campaign.ForceMapUI) ||
382 campaign.CampaignUI?.SelectedTab != CampaignMode.InteractionType.Map)
384 ActiveHintMessageBox.Close();
389 public static void OnShowCommandInterface()
391 IgnoreReminder(
"commandinterface");
392 if (!CanDisplayHints()) {
return; }
393 DisplayHint(
"onshowcommandinterface".ToIdentifier(), onUpdate: () =>
395 if (CrewManager.IsCommandInterfaceOpen) { return; }
396 ActiveHintMessageBox.Close();
400 public static void OnShowHealthInterface()
402 if (!CanDisplayHints()) {
return; }
403 if (CharacterHealth.OpenHealthWindow ==
null) {
return; }
404 DisplayHint(
"onshowhealthinterface".ToIdentifier(), onUpdate: () =>
406 if (CharacterHealth.OpenHealthWindow !=
null) { return; }
407 ActiveHintMessageBox.Close();
411 public static void OnShowTabMenu()
413 IgnoreReminder(
"tabmenu");
416 public static void OnObtainedItem(Character character, Item item)
418 if (!CanDisplayHints()) {
return; }
419 if (character !=
Character.Controlled || item ==
null) {
return; }
421 if (DisplayHint($
"onobtaineditem.{item.Prefab.Identifier}".ToIdentifier())) {
return; }
422 foreach (Identifier tag
in item.GetTags())
424 if (DisplayHint($
"onobtaineditem.{tag}".ToIdentifier())) {
return; }
427 if ((item.HasTag(Tags.GeneticMaterial) && character.Inventory.FindItemByTag(Tags.GeneticMaterial, recursive:
true) !=
null) ||
428 (item.HasTag(Tags.GeneticDevice) && character.Inventory.FindItemByTag(Tags.GeneticDevice, recursive:
true) !=
null))
430 if (DisplayHint($
"geneticmaterial.useinstructions".ToIdentifier())) {
return; }
434 public static void OnStartDeconstructing(Character character,
Deconstructor deconstructor)
436 if (!CanDisplayHints()) {
return; }
437 if (character !=
Character.Controlled || deconstructor ==
null) {
return; }
440 DisplayHint($
"geneticmaterial.onrefiningorcombining".ToIdentifier());
444 public static void OnStoleItem(Character character, Item item)
446 if (!CanDisplayHints()) {
return; }
447 if (character !=
Character.Controlled) {
return; }
448 if (item ==
null || item.AllowStealing || !item.StolenDuringRound) {
return; }
449 DisplayHint(
"onstoleitem".ToIdentifier(), onUpdate: () =>
451 if (item ==
null || item.Removed || item.GetRootInventoryOwner() != character)
453 ActiveHintMessageBox.Close();
458 public static void OnHandcuffed(Character character)
460 if (!CanDisplayHints()) {
return; }
461 if (character !=
Character.Controlled || !character.LockHands) {
return; }
462 DisplayHint(
"onhandcuffed".ToIdentifier(), onUpdate: () =>
464 if (character !=
null && !character.Removed && character.LockHands) { return; }
465 ActiveHintMessageBox.Close();
469 public static void OnRadioJammed(Item radioItem)
471 if (!CanDisplayHints()) {
return; }
472 if (radioItem?.ParentInventory is not CharacterInventory characterInventory) {
return; }
473 if (characterInventory.Owner !=
Character.Controlled) {
return; }
474 DisplayHint(
"radiojammed".ToIdentifier());
477 public static void OnReactorOutOfFuel(
Reactor reactor)
479 if (!CanDisplayHints()) {
return; }
480 if (reactor ==
null) {
return; }
482 if (!HasValidJob(
"engineer")) {
return; }
483 DisplayHint(
"onreactoroutoffuel".ToIdentifier(), onUpdate: () =>
486 ActiveHintMessageBox.Close();
490 public static void OnAssignedAsTraitor()
492 if (!CanDisplayHints()) {
return; }
493 DisplayHint(
"assignedastraitor".ToIdentifier());
494 DisplayHint(
"assignedastraitor2".ToIdentifier());
497 public static void OnAvailableTransition(CampaignMode.TransitionType transitionType)
499 if (!CanDisplayHints()) {
return; }
500 if (transitionType == CampaignMode.TransitionType.None) {
return; }
501 DisplayHint($
"onavailabletransition.{transitionType}".ToIdentifier());
504 public static void OnShowSubInventory(Item item)
506 if (item?.Prefab ==
null) {
return; }
507 if (item.Prefab.Identifier ==
"toolbelt")
509 IgnoreReminder(
"toolbelt");
513 public static void OnChangeCharacter()
515 IgnoreReminder(
"characterchange");
518 public static void OnCharacterUnconscious(Character character)
520 if (!CanDisplayHints()) {
return; }
521 if (character !=
Character.Controlled) {
return; }
522 if (character.IsDead) {
return; }
523 if (character.CharacterHealth !=
null && character.Vitality < character.CharacterHealth.MinVitality) {
return; }
524 DisplayHint(
"oncharacterunconscious".ToIdentifier());
527 public static void OnCharacterKilled(Character character)
529 if (!CanDisplayHints()) {
return; }
530 if (character !=
Character.Controlled) {
return; }
531 if (GameMain.IsMultiplayer) {
return; }
532 if (GameMain.GameSession?.CrewManager ==
null) {
return; }
533 if (GameMain.GameSession.CrewManager.GetCharacters().None(c => !c.IsDead)) {
return; }
534 DisplayHint(
"oncharacterkilled".ToIdentifier());
537 private static void OnStartedControlling()
539 if (Level.IsLoadedOutpost) {
return; }
540 if (
Character.Controlled?.Info?.Job?.Prefab ==
null) {
return; }
541 Identifier hintIdentifier = $
"onstartedcontrolling.job.{Character.Controlled.Info.Job.Prefab.Identifier}".ToIdentifier();
542 DisplayHint(hintIdentifier,
543 icon:
Character.Controlled.Info.Job.Prefab.Icon,
544 iconColor:
Character.Controlled.Info.Job.Prefab.UIColor,
547 if (!HintOrders.TryGetValue(hintIdentifier, out var orderInfo)) { return; }
548 var orderPrefab = OrderPrefab.Prefabs[orderInfo.identifier];
549 if (orderPrefab ==
null) {
return; }
550 Item targetEntity =
null;
552 if (orderPrefab.MustSetTarget)
554 targetEntity = orderPrefab.GetMatchingItems(true, interactableFor: Character.Controlled, orderOption: orderInfo.option).FirstOrDefault();
555 if (targetEntity == null) { return; }
556 targetItem = orderPrefab.GetTargetItemComponent(targetEntity);
558 var order =
new Order(orderPrefab, orderInfo.option, targetEntity, targetItem, orderGiver:
Character.Controlled).WithManualPriority(CharacterInfo.HighestManualOrderPriority);
559 GameMain.GameSession.CrewManager.SetCharacterOrder(
Character.Controlled, order);
563 public static void OnAutoPilotPathUpdated(
Steering steering)
565 if (!CanDisplayHints()) {
return; }
566 if (!HasValidJob(
"captain")) {
return; }
567 if (steering?.Item?.Submarine?.Info ==
null) {
return; }
575 DisplayHint(
"onautopilotreachedoutpost".ToIdentifier());
578 public static void OnStatusEffectApplied(
ItemComponent component, ActionType actionType, Character character)
580 if (!CanDisplayHints()) {
return; }
581 if (character !=
Character.Controlled) {
return; }
584 DisplayHint(
"onrepairfailed".ToIdentifier());
587 public static void OnActiveOrderAdded(Order order)
589 if (!CanDisplayHints()) {
return; }
590 if (order ==
null) {
return; }
592 if (order.Identifier ==
"reportballastflora" &&
593 order.TargetEntity is Hull h &&
594 h.Submarine?.TeamID ==
Character.Controlled.TeamID)
596 DisplayHint(
"onballastflorainfected".ToIdentifier());
598 if (order.Identifier ==
"deconstructitems" &&
599 Item.DeconstructItems.None())
601 DisplayHint(
"ondeconstructorder".ToIdentifier());
605 public static void OnSetOrder(Character character, Order order)
607 if (!CanDisplayHints()) {
return; }
608 if (character ==
null || order ==
null) {
return; }
610 if (order.OrderGiver ==
Character.Controlled &&
611 order.Identifier ==
"deconstructitems" &&
612 Item.DeconstructItems.None())
614 DisplayHint(
"ondeconstructorder".ToIdentifier());
618 private static void CheckIfDivingGearOutOfOxygen()
620 if (!CanDisplayHints()) {
return; }
621 var divingGear =
Character.Controlled.GetEquippedItem(Tags.DivingGear,
InvSlotType.OuterClothes);
622 if (divingGear?.OwnInventory ==
null) {
return; }
623 if (divingGear.GetContainedItemConditionPercentage() > 0.0f) {
return; }
624 DisplayHint(
"ondivinggearoutofoxygen".ToIdentifier(), onUpdate: () =>
626 if (divingGear ==
null || divingGear.Removed ||
628 divingGear.GetContainedItemConditionPercentage() > 0.0f)
630 ActiveHintMessageBox.Close();
635 private static void CheckHulls()
637 if (!CanDisplayHints()) {
return; }
638 if (
Character.Controlled.CurrentHull ==
null) {
return; }
639 if (HumanAIController.IsBallastFloraNoticeable(
Character.Controlled,
Character.Controlled.CurrentHull))
641 if (IsOnFriendlySub() && DisplayHint(
"onballastflorainfected".ToIdentifier())) {
return; }
643 foreach (var gap
in Character.Controlled.CurrentHull.ConnectedGaps)
645 if (gap.ConnectedDoor ==
null || gap.ConnectedDoor.Impassable) {
continue; }
646 if (Vector2.DistanceSquared(
Character.Controlled.WorldPosition, gap.ConnectedDoor.Item.WorldPosition) > 400 * 400) {
continue; }
647 if (!gap.IsRoomToRoom)
649 if (!IsWearingDivingSuit()) {
continue; }
650 if (
Character.Controlled.IsProtectedFromPressure) {
continue; }
651 if (DisplayHint(
"divingsuitwarning".ToIdentifier(), extendTextTag:
false)) {
return; }
654 foreach (var me
in gap.linkedTo)
656 if (me ==
Character.Controlled.CurrentHull) {
continue; }
657 if (me is not Hull adjacentHull) {
continue; }
658 if (!IsOnFriendlySub()) {
continue; }
659 if (IsWearingDivingSuit()) {
continue; }
660 if (adjacentHull.LethalPressure > 5.0f && DisplayHint(
"onadjacenthull.highpressure".ToIdentifier())) {
return; }
661 if (adjacentHull.WaterPercentage > 75 && !BallastHulls.Contains(adjacentHull) && DisplayHint(
"onadjacenthull.highwaterpercentage".ToIdentifier())) {
return; }
664 static bool IsWearingDivingSuit() =>
Character.Controlled.GetEquippedItem(Tags.HeavyDivingGear,
InvSlotType.OuterClothes) is
Item;
670 private static void CheckReminders()
672 if (!CanDisplayHints()) {
return; }
673 if (Level.Loaded ==
null) {
return; }
674 if (GameMain.GameScreen.GameTime < TimeRoundStarted + TimeBeforeReminders) {
return; }
675 if (GameMain.GameScreen.GameTime < TimeReminderLastDisplayed + ReminderCooldown) {
return; }
677 string hintIdentifierBase =
"reminder";
679 if (GameMain.GameSession.GameMode.IsSinglePlayer)
681 if (DisplayHint($
"{hintIdentifierBase}.characterchange".ToIdentifier()))
683 TimeReminderLastDisplayed = GameMain.GameScreen.GameTime;
688 if (Level.Loaded.Type != LevelData.LevelType.Outpost)
690 if (DisplayHint($
"{hintIdentifierBase}.commandinterface".ToIdentifier(),
691 variables:
new[] { (
"[commandkey]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(
InputType.Command)) },
694 if (!CrewManager.IsCommandInterfaceOpen) {
return; }
695 ActiveHintMessageBox.Close();
698 TimeReminderLastDisplayed = GameMain.GameScreen.GameTime;
703 if (DisplayHint($
"{hintIdentifierBase}.tabmenu".ToIdentifier(),
704 variables:
new[] { (
"[infotabkey]".ToIdentifier(), GameSettings.CurrentConfig.KeyMap.KeyBindText(
InputType.InfoTab)) },
707 if (!GameSession.IsTabMenuOpen) {
return; }
708 ActiveHintMessageBox.Close();
711 TimeReminderLastDisplayed = GameMain.GameScreen.GameTime;
715 if (
Character.Controlled.Inventory?.GetItemInLimbSlot(
InvSlotType.Bag)?.Prefab?.Identifier ==
"toolbelt")
717 if (DisplayHint($
"{hintIdentifierBase}.toolbelt".ToIdentifier()))
719 TimeReminderLastDisplayed = GameMain.GameScreen.GameTime;
725 private static bool DisplayHint(Identifier hintIdentifier,
bool extendTextTag =
true, (Identifier Tag, LocalizedString Value)[] variables =
null, Sprite icon =
null, Color? iconColor =
null, Action onDisplay =
null, Action onUpdate =
null)
727 if (hintIdentifier == Identifier.Empty) {
return false; }
728 if (!HintIdentifiers.Contains(hintIdentifier)) {
return false; }
729 if (IgnoredHints.Instance.Contains(hintIdentifier)) {
return false; }
730 if (HintsIgnoredThisRound.Contains(hintIdentifier)) {
return false; }
732 LocalizedString text;
733 Identifier textTag = extendTextTag ? $
"hint.{hintIdentifier}".ToIdentifier() : hintIdentifier;
734 if (variables !=
null && variables.Length > 0)
736 text = TextManager.GetWithVariables(textTag, variables);
740 text = TextManager.Get(textTag);
743 if (text.IsNullOrEmpty())
746 DebugConsole.ThrowError($
"No hint text found for text tag \"{textTag}\"");
751 HintsIgnoredThisRound.Add(hintIdentifier);
753 ActiveHintMessageBox =
new GUIMessageBox(hintIdentifier, TextManager.ParseInputTypes(text), icon);
754 if (iconColor.HasValue) { ActiveHintMessageBox.IconColor = iconColor.Value; }
758 ActiveHintMessageBox.InnerFrame.Flash(color: iconColor ?? Color.Orange, flashDuration: 0.75f);
761 GameAnalyticsManager.AddDesignEvent($
"HintManager:{GameMain.GameSession?.GameMode?.Preset?.Identifier ?? "none
".ToIdentifier()}:HintDisplayed:{hintIdentifier}");
766 public static bool OnDontShowAgain(GUITickBox tickBox)
768 IgnoreHint((Identifier)tickBox.UserData, ignore: tickBox.Selected);
772 private static void IgnoreHint(Identifier hintIdentifier,
bool ignore =
true)
774 if (hintIdentifier.IsEmpty) {
return; }
775 if (!HintIdentifiers.Contains(hintIdentifier))
778 DebugConsole.ThrowError($
"Tried to ignore a hint not defined in {HintManagerFile}: {hintIdentifier}");
784 IgnoredHints.Instance.Add(hintIdentifier);
788 IgnoredHints.Instance.Remove(hintIdentifier);
792 private static void IgnoreReminder(
string reminderIdentifier)
794 HintsIgnoredThisRound.Add($
"reminder.{reminderIdentifier}".ToIdentifier());
797 public static bool OnDisableHints(GUITickBox tickBox)
799 var config = GameSettings.CurrentConfig;
800 config.DisableInGameHints = tickBox.Selected;
801 GameSettings.SetCurrentConfig(config);
802 GameSettings.SaveCurrentConfig();
806 private static bool CanDisplayHints(
bool requireGameScreen =
true,
bool requireControllingCharacter =
true)
808 if (HintIdentifiers ==
null) {
return false; }
809 if (GameSettings.CurrentConfig.DisableInGameHints) {
return false; }
810 if (ActiveHintMessageBox !=
null) {
return false; }
811 if (requireControllingCharacter &&
Character.Controlled ==
null) {
return false; }
812 var gameMode = GameMain.GameSession?.GameMode;
813 if (!(gameMode is CampaignMode || gameMode is MissionMode)) {
return false; }
814 if (ObjectiveManager.AnyObjectives) {
return false; }
815 if (requireGameScreen && Screen.Selected != GameMain.GameScreen) {
return false; }
819 private static bool HasValidJob(
string jobIdentifier)
822 if (GameMain.GameSession.GameMode.IsSinglePlayer) {
return true; }
823 if (
Character.Controlled.HasJob(jobIdentifier)) {
return true; }
825 foreach (var c
in GameMain.GameSession.CrewManager.GetCharacters())
827 if (c ==
null || !c.IsRemotePlayer) {
continue; }
828 if (c.IsUnconscious || c.IsDead || c.Removed) {
continue; }
829 if (!c.HasJob(jobIdentifier)) {
continue; }
virtual IEnumerable< Item > AllItems
All items contained in the inventory. Stacked items are returned as individual instances....
ItemContainer InputContainer
The base class for components holding the different functionalities of the item
readonly ItemInventory Inventory
float ForceDeteriorationTimer
SteeringPath SteeringPath
ActionType
ActionTypes define when a StatusEffect is executed.
@ Character
Characters only