private void AdjustKarma(Character target, float amount, string debugKarmaChangeReason = "") { if (target == null) { return; } Client client = GameMain.Server.ConnectedClients.Find(c => c.Character == target); if (client == null) { return; } //all penalties/rewards are halved when wearing a clown costume if (target.HasEquippedItem("clownmask") && target.HasEquippedItem("clowncostume")) { amount *= 0.5f; } client.Karma += amount; if (TestMode) { SendKarmaNotifications(client, debugKarmaChangeReason); } }
public void RemoveItem(Item item, bool tryEquipFromSameStack) { if (!Contains(item)) { return; } bool wasEquipped = character.HasEquippedItem(item); var indices = FindIndices(item); base.RemoveItem(item); #if CLIENT CreateSlots(); #endif //if the item was equipped and there are more items in the same stack, equip one of those items if (tryEquipFromSameStack && wasEquipped) { int limbSlot = indices.Find(j => SlotTypes[j] != InvSlotType.Any); foreach (int i in indices) { var itemInSameSlot = GetItemAt(i); if (itemInSameSlot != null) { if (TryPutItem(itemInSameSlot, limbSlot, allowSwapping: false, allowCombine: false, character)) { #if CLIENT visualSlots[i].ShowBorderHighlight(GUI.Style.Green, 0.1f, 0.412f); #endif } break; } } } }
private static void CheckMidRoundAchievements(Character c) { if (c == null || c.Removed) { return; } if (c.HasEquippedItem("clownmask") && c.HasEquippedItem("clowncostume")) { UnlockAchievement(c, "clowncostume"); } if (Submarine.MainSub != null && c.Submarine == null && c.SpeciesName.Equals(CharacterPrefab.HumanSpeciesName, StringComparison.OrdinalIgnoreCase)) { float requiredDist = 500 / Physics.DisplayToRealWorldRatio; float distSquared = Vector2.DistanceSquared(c.WorldPosition, Submarine.MainSub.WorldPosition); if (cachedDistances.TryGetValue(c, out var cachedDistance)) { if (cachedDistance.ShouldUpdateDistance(c.WorldPosition, Submarine.MainSub.WorldPosition)) { cachedDistances.Remove(c); cachedDistance = CalculateNewCachedDistance(c); if (cachedDistance != null) { cachedDistances.Add(c, cachedDistance); } } } else { cachedDistance = CalculateNewCachedDistance(c); if (cachedDistance != null) { cachedDistances.Add(c, cachedDistance); } } if (cachedDistance != null) { distSquared = Math.Max(distSquared, cachedDistance.Distance * cachedDistance.Distance); } if (distSquared > requiredDist * requiredDist) { UnlockAchievement(c, "crewaway"); }
private void AdjustKarma(Character target, float amount, string debugKarmaChangeReason = "") { if (target == null) { return; } Client client = GameMain.Server.ConnectedClients.Find(c => c.Character == target); if (client == null) { return; } //all penalties/rewards are halved when wearing a clown costume if (target.HasEquippedItem("clownmask") && target.HasEquippedItem("clowncostume")) { amount *= 0.5f; } client.Karma += amount; if (amount < 0.0f) { float?herpesStrength = client.Character?.CharacterHealth.GetAfflictionStrength("spaceherpes"); var clientMemory = GetClientMemory(client); clientMemory.KarmaDecreasesInPastMinute.RemoveAll(ta => ta.Time + 60.0f < Timing.TotalTime); float aggregate = clientMemory.KarmaDecreasesInPastMinute.Select(ta => ta.Amount).DefaultIfEmpty().Aggregate((a, b) => a + b); clientMemory.KarmaDecreasesInPastMinute.Add(new ClientMemory.TimeAmount() { Time = Timing.TotalTime, Amount = -amount }); if (herpesStrength.HasValue && herpesStrength <= 0.0f && aggregate - amount > 25.0f && aggregate <= 25.0f) { GameServer.Log($"{GameServer.ClientLogName(client)} has lost more than 25 karma in the past minute.", ServerLog.MessageType.Karma); } } if (TestMode) { SendKarmaNotifications(client, debugKarmaChangeReason); } }
public static bool HasItem(Character character, string tag, string containedTag, float conditionPercentage = 0) { var item = character.Inventory.FindItemByTag(tag); return(item != null && item.ConditionPercentage > conditionPercentage && character.HasEquippedItem(item) && (containedTag == null || (item.ContainedItems != null && item.ContainedItems.Any(i => i.HasTag(containedTag) && i.ConditionPercentage > conditionPercentage)))); }
private static void CheckMidRoundAchievements(Character c) { if (c == null || c.Removed) { return; } if (c.HasEquippedItem("clownmask") && c.HasEquippedItem("clowncostume")) { UnlockAchievement(c, "clowncostume"); } if (Submarine.MainSub != null && c.Submarine == null) { float dist = 500 / Physics.DisplayToRealWorldRatio; if (Vector2.DistanceSquared(c.WorldPosition, Submarine.MainSub.WorldPosition) > dist * dist) { UnlockAchievement(c, "crewaway"); } } }
public static void OnCharacterKilled(Character character, CauseOfDeath causeOfDeath) { #if CLIENT if (GameMain.Client != null || GameMain.GameSession == null) { return; } #endif if (character != Character.Controlled && causeOfDeath.Killer != null && causeOfDeath.Killer == Character.Controlled) { SteamManager.IncrementStat( character.SpeciesName.ToLowerInvariant() == "human" ? "humanskilled" : "monsterskilled", 1); } roundData.Casualties.Add(character); UnlockAchievement(causeOfDeath.Killer, "kill" + character.SpeciesName); if (character.CurrentHull != null) { UnlockAchievement(causeOfDeath.Killer, "kill" + character.SpeciesName + "indoors"); } if (character.HasEquippedItem("clownmask") && character.HasEquippedItem("clowncostume") && causeOfDeath.Killer != character) { UnlockAchievement(causeOfDeath.Killer, "killclown"); } if (causeOfDeath.DamageSource is Item item) { switch (item.Prefab.Identifier) { case "weldingtool": case "plasmacutter": case "wrench": UnlockAchievement(causeOfDeath.Killer, "killtool"); break; case "morbusine": UnlockAchievement(causeOfDeath.Killer, "killpoison"); break; case "nuclearshell": case "nucleardepthcharge": UnlockAchievement(causeOfDeath.Killer, "killnuke"); break; } } #if SERVER if (GameMain.Server?.TraitorManager != null) { foreach (Traitor traitor in GameMain.Server.TraitorManager.TraitorList) { if (traitor.TargetCharacter == character) { //killed the target as a traitor UnlockAchievement(traitor.Character, "traitorwin"); } else if (traitor.Character == character) { //someone killed a traitor UnlockAchievement(causeOfDeath.Killer, "killtraitor"); } } } #endif }
public void OnCharacterHealthChanged(Character target, Character attacker, float damage, IEnumerable <Affliction> appliedAfflictions = null) { if (target == null || attacker == null) { return; } if (target == attacker) { return; } //damaging dead characters doesn't affect karma if (target.IsDead || target.Removed) { return; } bool isEnemy = target.AIController is EnemyAIController || target.TeamID != attacker.TeamID; if (GameMain.Server.TraitorManager?.Traitors != null) { if (GameMain.Server.TraitorManager.Traitors.Any(t => t.Character == target)) { //traitors always count as enemies isEnemy = true; } if (GameMain.Server.TraitorManager.Traitors.Any(t => t.Character == attacker && t.CurrentObjective != null && t.CurrentObjective.IsEnemy(target))) { //target counts as an enemy to the traitor isEnemy = true; } } bool targetIsHusk = target.CharacterHealth?.GetAffliction <AfflictionHusk>("huskinfection")?.State == AfflictionHusk.InfectionState.Active; bool attackerIsHusk = attacker.CharacterHealth?.GetAffliction <AfflictionHusk>("huskinfection")?.State == AfflictionHusk.InfectionState.Active; //huskified characters count as enemies to healthy characters and vice versa if (targetIsHusk != attackerIsHusk) { isEnemy = true; } if (appliedAfflictions != null) { foreach (Affliction affliction in appliedAfflictions) { if (MathUtils.NearlyEqual(affliction.Prefab.KarmaChangeOnApplied, 0.0f)) { continue; } damage -= affliction.Prefab.KarmaChangeOnApplied * affliction.Strength; } } Client targetClient = GameMain.Server.ConnectedClients.Find(c => c.Character == target); if (damage > 0 && targetClient != null) { var targetMemory = GetClientMemory(targetClient); targetMemory.LastAttackTime[attacker] = Timing.TotalTime; } Client attackerClient = GameMain.Server.ConnectedClients.Find(c => c.Character == attacker); if (attackerClient != null) { //if the attacker has been attacked by the target within the last x seconds, ignore the damage //(= no karma penalty from retaliating against someone who attacked you) var attackerMemory = GetClientMemory(attackerClient); if (attackerMemory.LastAttackTime.ContainsKey(target) && attackerMemory.LastAttackTime[target] > Timing.TotalTime - AllowedRetaliationTime) { damage = Math.Min(damage, 0); } } //attacking/healing clowns has a smaller effect on karma if (target.HasEquippedItem("clownmask") && target.HasEquippedItem("clowncostume")) { damage *= 0.5f; } //smaller karma penalty for attacking someone who's aiming with a weapon if (damage > 0.0f && target.IsKeyDown(InputType.Aim) && target.SelectedItems.Any(it => it != null && (it.GetComponent <MeleeWeapon>() != null || it.GetComponent <RangedWeapon>() != null))) { damage *= 0.5f; } //damage scales according to the karma of the target //(= smaller karma penalty from attacking someone who has a low karma) if (damage > 0 && targetClient != null) { damage *= MathUtils.InverseLerp(0.0f, 50.0f, targetClient.Karma); } if (isEnemy) { if (damage > 0) { float karmaIncrease = damage * DamageEnemyKarmaIncrease; if (attacker?.Info?.Job.Prefab.Identifier == "securityofficer") { karmaIncrease *= 2.0f; } AdjustKarma(attacker, karmaIncrease, "Damaged enemy"); } } else { if (damage > 0) { AdjustKarma(attacker, -damage * DamageFriendlyKarmaDecrease, "Damaged friendly"); } else { float karmaIncrease = -damage * HealFriendlyKarmaIncrease; if (attacker?.Info?.Job.Prefab.Identifier == "medicaldoctor") { karmaIncrease *= 2.0f; } AdjustKarma(attacker, karmaIncrease, "Healed friendly"); } } }
public override void Update(float deltaTime) { if (DisableCrewAI || Character.IsUnconscious) { return; } float maxDistanceToSub = 3000; if (Character.Submarine != null || SelectedAiTarget?.Entity?.Submarine != null && Vector2.DistanceSquared(Character.WorldPosition, SelectedAiTarget.Entity.Submarine.WorldPosition) < maxDistanceToSub * maxDistanceToSub) { if (steeringManager != insideSteering) { insideSteering.Reset(); } steeringManager = insideSteering; } else { if (steeringManager != outsideSteering) { outsideSteering.Reset(); } steeringManager = outsideSteering; } AnimController.Crouching = shouldCrouch; CheckCrouching(deltaTime); Character.ClearInputs(); if (hullVisibilityTimer > 0) { hullVisibilityTimer--; } else { hullVisibilityTimer = hullVisibilityInterval; VisibleHulls = Character.GetVisibleHulls(); } objectiveManager.UpdateObjectives(deltaTime); if (sortTimer > 0.0f) { sortTimer -= deltaTime; } else { objectiveManager.SortObjectives(); sortTimer = sortObjectiveInterval; } if (reactTimer > 0.0f) { reactTimer -= deltaTime; } else { if (Character.CurrentHull != null) { VisibleHulls.ForEach(h => PropagateHullSafety(Character, h)); } if (Character.SpeechImpediment < 100.0f) { ReportProblems(); UpdateSpeaking(); } reactTimer = reactionTime * Rand.Range(0.75f, 1.25f); } if (objectiveManager.CurrentObjective == null) { return; } objectiveManager.DoCurrentObjective(deltaTime); bool run = objectiveManager.CurrentObjective.ForceRun || objectiveManager.GetCurrentPriority() > AIObjectiveManager.RunPriority; if (ObjectiveManager.CurrentObjective is AIObjectiveGoTo goTo && goTo.Target != null) { if (Character.CurrentHull == null) { run = Vector2.DistanceSquared(Character.WorldPosition, goTo.Target.WorldPosition) > 300 * 300; } else { float yDiff = goTo.Target.WorldPosition.Y - Character.WorldPosition.Y; if (Math.Abs(yDiff) > 100) { run = true; } else { float xDiff = goTo.Target.WorldPosition.X - Character.WorldPosition.X; run = Math.Abs(xDiff) > 300; } } } if (run) { run = !AnimController.Crouching && !AnimController.IsMovingBackwards; } float currentSpeed = Character.AnimController.GetCurrentSpeed(run); steeringManager.Update(currentSpeed); bool ignorePlatforms = Character.AnimController.TargetMovement.Y < -0.5f && (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X)); if (steeringManager == insideSteering) { var currPath = PathSteering.CurrentPath; if (currPath != null && currPath.CurrentNode != null) { if (currPath.CurrentNode.SimPosition.Y < Character.AnimController.GetColliderBottom().Y) { // Don't allow to jump from too high. The formula might require tweaking. float allowedJumpHeight = Character.AnimController.ImpactTolerance / 2; float height = Math.Abs(currPath.CurrentNode.SimPosition.Y - Character.SimPosition.Y); ignorePlatforms = height < allowedJumpHeight; } } if (Character.IsClimbing && PathSteering.IsNextLadderSameAsCurrent) { Character.AnimController.TargetMovement = new Vector2(0.0f, Math.Sign(Character.AnimController.TargetMovement.Y)); } } Character.AnimController.IgnorePlatforms = ignorePlatforms; Vector2 targetMovement = AnimController.TargetMovement; if (!Character.AnimController.InWater) { targetMovement = new Vector2(Character.AnimController.TargetMovement.X, MathHelper.Clamp(Character.AnimController.TargetMovement.Y, -1.0f, 1.0f)); } float maxSpeed = Character.ApplyTemporarySpeedLimits(currentSpeed); targetMovement.X = MathHelper.Clamp(targetMovement.X, -maxSpeed, maxSpeed); targetMovement.Y = MathHelper.Clamp(targetMovement.Y, -maxSpeed, maxSpeed); //apply speed multiplier if // a. it's boosting the movement speed and the character is trying to move fast (= running) // b. it's a debuff that decreases movement speed float speedMultiplier = Character.SpeedMultiplier; if (run || speedMultiplier <= 0.0f) { targetMovement *= speedMultiplier; } Character.ResetSpeedMultiplier(); // Reset, items will set the value before the next update Character.AnimController.TargetMovement = targetMovement; if (!NeedsDivingGear(Character.CurrentHull)) { bool oxygenLow = Character.OxygenAvailable < CharacterHealth.LowOxygenThreshold; bool highPressure = Character.CurrentHull == null || Character.CurrentHull.LethalPressure > 0 && Character.PressureProtection <= 0; bool shouldKeepTheGearOn = !ObjectiveManager.IsCurrentObjective <AIObjectiveIdle>(); bool removeDivingSuit = oxygenLow && !highPressure; if (!removeDivingSuit) { bool targetHasNoSuit = objectiveManager.CurrentOrder is AIObjectiveGoTo gtObj && gtObj.mimic && !HasDivingSuit(gtObj.Target as Character); bool canDropTheSuit = Character.CurrentHull.WaterPercentage < 1 && !Character.IsClimbing && steeringManager == insideSteering && !PathSteering.InStairs; removeDivingSuit = (!shouldKeepTheGearOn || targetHasNoSuit) && canDropTheSuit; } if (removeDivingSuit) { var divingSuit = Character.Inventory.FindItemByIdentifier("divingsuit") ?? Character.Inventory.FindItemByTag("divingsuit"); if (divingSuit != null) { // TODO: take the item where it was taken from? divingSuit.Drop(Character); } } bool targetHasNoMask = objectiveManager.CurrentOrder is AIObjectiveGoTo gotoObjective && gotoObjective.mimic && !HasDivingMask(gotoObjective.Target as Character); bool takeMaskOff = oxygenLow || (!shouldKeepTheGearOn && Character.CurrentHull.WaterPercentage < 20) || targetHasNoMask; if (takeMaskOff) { var mask = Character.Inventory.FindItemByIdentifier("divingmask"); if (mask != null && Character.Inventory.IsInLimbSlot(mask, InvSlotType.Head)) { // Try to put the mask in an Any slot, and drop it if that fails if (!mask.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(mask, Character, new List <InvSlotType>() { InvSlotType.Any })) { mask.Drop(Character); } } } } if (!ObjectiveManager.IsCurrentObjective <AIObjectiveExtinguishFires>() && !ObjectiveManager.IsCurrentObjective <AIObjectiveExtinguishFire>()) { var extinguisherItem = Character.Inventory.FindItemByIdentifier("extinguisher") ?? Character.Inventory.FindItemByTag("extinguisher"); if (extinguisherItem != null && Character.HasEquippedItem(extinguisherItem)) { // TODO: take the item where it was taken from? extinguisherItem.Drop(Character); } } foreach (var item in Character.Inventory.Items) { if (item == null) { continue; } if (ObjectiveManager.CurrentObjective is AIObjectiveIdle) { if (item.AllowedSlots.Contains(InvSlotType.RightHand | InvSlotType.LeftHand) && Character.HasEquippedItem(item)) { // Try to put the weapon in an Any slot, and drop it if that fails if (!item.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(item, Character, new List <InvSlotType>() { InvSlotType.Any })) { item.Drop(Character); } } } } if (Character.IsKeyDown(InputType.Aim)) { var cursorDiffX = Character.CursorPosition.X - Character.Position.X; if (cursorDiffX > 10.0f) { Character.AnimController.TargetDir = Direction.Right; } else if (cursorDiffX < -10.0f) { Character.AnimController.TargetDir = Direction.Left; } if (Character.SelectedConstruction != null) { Character.SelectedConstruction.SecondaryUse(deltaTime, Character); } } else if (Math.Abs(Character.AnimController.TargetMovement.X) > 0.1f && !Character.AnimController.InWater) { Character.AnimController.TargetDir = Character.AnimController.TargetMovement.X > 0.0f ? Direction.Right : Direction.Left; } }
public static void OnCharacterKilled(Character character, CauseOfDeath causeOfDeath) { #if CLIENT if (GameMain.Client != null || GameMain.GameSession == null) { return; } #endif if (character != Character.Controlled && causeOfDeath.Killer != null && causeOfDeath.Killer == Character.Controlled) { SteamManager.IncrementStat( character.IsHuman ? "humanskilled" : "monsterskilled", 1); } roundData?.Casualties.Add(character); UnlockAchievement(causeOfDeath.Killer, "kill" + character.SpeciesName); if (character.CurrentHull != null) { UnlockAchievement(causeOfDeath.Killer, "kill" + character.SpeciesName + "indoors"); } if (character.SpeciesName.EndsWith("boss")) { UnlockAchievement(causeOfDeath.Killer, "kill" + character.SpeciesName.Replace("boss", "")); if (character.CurrentHull != null) { UnlockAchievement(causeOfDeath.Killer, "kill" + character.SpeciesName.Replace("boss", "") + "indoors"); } } if (character.SpeciesName.EndsWith("_m")) { UnlockAchievement(causeOfDeath.Killer, "kill" + character.SpeciesName.Replace("_m", "")); if (character.CurrentHull != null) { UnlockAchievement(causeOfDeath.Killer, "kill" + character.SpeciesName.Replace("_m", "") + "indoors"); } } if (character.HasEquippedItem("clownmask") && character.HasEquippedItem("clowncostume") && causeOfDeath.Killer != character) { UnlockAchievement(causeOfDeath.Killer, "killclown"); } if (character.CharacterHealth?.GetAffliction("morbusinepoisoning") != null) { UnlockAchievement(causeOfDeath.Killer, "killpoison"); } if (causeOfDeath.DamageSource is Item item) { if (item.HasTag("tool")) { UnlockAchievement(causeOfDeath.Killer, "killtool"); } else { switch (item.Prefab.Identifier) { case "morbusine": UnlockAchievement(causeOfDeath.Killer, "killpoison"); break; case "nuclearshell": case "nucleardepthcharge": UnlockAchievement(causeOfDeath.Killer, "killnuke"); break; } } } #if SERVER if (GameMain.Server?.TraitorManager != null) { if (GameMain.Server.TraitorManager.IsTraitor(character)) { UnlockAchievement(causeOfDeath.Killer, "killtraitor"); } } #endif }
private void DropUnnecessaryItems() { if (!NeedsDivingGear(Character.CurrentHull)) { bool oxygenLow = Character.OxygenAvailable < CharacterHealth.LowOxygenThreshold; bool highPressure = Character.CurrentHull == null || Character.CurrentHull.LethalPressure > 0 && Character.PressureProtection <= 0; bool shouldKeepTheGearOn = !ObjectiveManager.IsCurrentObjective <AIObjectiveIdle>(); bool removeDivingSuit = oxygenLow && !highPressure; if (!removeDivingSuit) { bool targetHasNoSuit = objectiveManager.CurrentOrder is AIObjectiveGoTo gtObj && gtObj.mimic && !HasDivingSuit(gtObj.Target as Character); bool canDropTheSuit = Character.CurrentHull.WaterPercentage < 1 && !Character.IsClimbing && steeringManager == insideSteering && !PathSteering.InStairs; removeDivingSuit = (!shouldKeepTheGearOn || targetHasNoSuit) && canDropTheSuit; } if (removeDivingSuit) { var divingSuit = Character.Inventory.FindItemByIdentifier("divingsuit") ?? Character.Inventory.FindItemByTag("divingsuit"); if (divingSuit != null) { // TODO: take the item where it was taken from? divingSuit.Drop(Character); } } bool targetHasNoMask = objectiveManager.CurrentOrder is AIObjectiveGoTo gotoObjective && gotoObjective.mimic && !HasDivingMask(gotoObjective.Target as Character); bool takeMaskOff = oxygenLow || (!shouldKeepTheGearOn && Character.CurrentHull.WaterPercentage < 20) || targetHasNoMask; if (takeMaskOff) { var mask = Character.Inventory.FindItemByIdentifier("divingmask"); if (mask != null && Character.Inventory.IsInLimbSlot(mask, InvSlotType.Head)) { // Try to put the mask in an Any slot, and drop it if that fails if (!mask.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(mask, Character, new List <InvSlotType>() { InvSlotType.Any })) { mask.Drop(Character); } } } } if (!ObjectiveManager.IsCurrentObjective <AIObjectiveExtinguishFires>() && !ObjectiveManager.IsCurrentObjective <AIObjectiveExtinguishFire>()) { var extinguisherItem = Character.Inventory.FindItemByIdentifier("extinguisher") ?? Character.Inventory.FindItemByTag("extinguisher"); if (extinguisherItem != null && Character.HasEquippedItem(extinguisherItem)) { // TODO: take the item where it was taken from? extinguisherItem.Drop(Character); } } foreach (var item in Character.Inventory.Items) { if (item == null) { continue; } if (ObjectiveManager.CurrentObjective is AIObjectiveIdle) { if (item.AllowedSlots.Contains(InvSlotType.RightHand | InvSlotType.LeftHand) && Character.HasEquippedItem(item)) { // Try to put the weapon in an Any slot, and drop it if that fails if (!item.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(item, Character, new List <InvSlotType>() { InvSlotType.Any })) { item.Drop(Character); } } } } }
private void UnequipUnnecessaryItems() { if (ObjectiveManager.HasActiveObjective <AIObjectiveDecontainItem>()) { return; } if (findItemState == FindItemState.None || findItemState == FindItemState.Extinguisher) { if (!ObjectiveManager.IsCurrentObjective <AIObjectiveExtinguishFires>() && !objectiveManager.HasActiveObjective <AIObjectiveExtinguishFire>()) { var extinguisher = Character.Inventory.FindItemByTag("extinguisher"); if (extinguisher != null && Character.HasEquippedItem(extinguisher)) { if (ObjectiveManager.GetCurrentPriority() >= AIObjectiveManager.RunPriority) { extinguisher.Drop(Character); } else { findItemState = FindItemState.Extinguisher; if (FindSuitableContainer(Character, extinguisher, out Item targetContainer)) { findItemState = FindItemState.None; itemIndex = 0; if (targetContainer != null) { var decontainObjective = new AIObjectiveDecontainItem(Character, extinguisher, targetContainer.GetComponent <ItemContainer>(), ObjectiveManager, targetContainer.GetComponent <ItemContainer>()); decontainObjective.Abandoned += () => ignoredContainers.Add(targetContainer); ObjectiveManager.CurrentObjective.AddSubObjective(decontainObjective, addFirst: true); return; } else { extinguisher.Drop(Character); } } } } } } if (findItemState == FindItemState.None || findItemState == FindItemState.DivingSuit || findItemState == FindItemState.DivingMask) { if (!NeedsDivingGear(Character, Character.CurrentHull, out _)) { bool oxygenLow = Character.OxygenAvailable < CharacterHealth.LowOxygenThreshold; bool shouldKeepTheGearOn = Character.AnimController.HeadInWater || Character.CurrentHull.WaterPercentage > 50 || ObjectiveManager.IsCurrentObjective <AIObjectiveFindSafety>() || ObjectiveManager.CurrentObjective.GetSubObjectivesRecursive(true).Any(o => o.KeepDivingGearOn); bool removeDivingSuit = !Character.AnimController.HeadInWater && oxygenLow; AIObjectiveGoTo gotoObjective = ObjectiveManager.CurrentOrder as AIObjectiveGoTo; if (!removeDivingSuit) { bool targetHasNoSuit = gotoObjective != null && gotoObjective.mimic && !HasDivingSuit(gotoObjective.Target as Character); removeDivingSuit = !shouldKeepTheGearOn && (gotoObjective == null || targetHasNoSuit); } bool takeMaskOff = !Character.AnimController.HeadInWater && oxygenLow; if (!takeMaskOff && Character.CurrentHull.WaterPercentage < 40) { bool targetHasNoMask = gotoObjective != null && gotoObjective.mimic && !HasDivingMask(gotoObjective.Target as Character); takeMaskOff = !shouldKeepTheGearOn && (gotoObjective == null || targetHasNoMask); } if (gotoObjective != null) { if (gotoObjective.Target is Hull h) { if (NeedsDivingGear(Character, h, out _)) { removeDivingSuit = false; takeMaskOff = false; } } else if (gotoObjective.Target is Character c) { if (NeedsDivingGear(Character, c.CurrentHull, out _)) { removeDivingSuit = false; takeMaskOff = false; } } else if (gotoObjective.Target is Item i) { if (NeedsDivingGear(Character, i.CurrentHull, out _)) { removeDivingSuit = false; takeMaskOff = false; } } } if (findItemState == FindItemState.None || findItemState == FindItemState.DivingSuit) { if (removeDivingSuit) { var divingSuit = Character.Inventory.FindItemByTag("divingsuit"); if (divingSuit != null) { if (oxygenLow || ObjectiveManager.GetCurrentPriority() >= AIObjectiveManager.RunPriority) { divingSuit.Drop(Character); } else { findItemState = FindItemState.DivingSuit; if (FindSuitableContainer(Character, divingSuit, out Item targetContainer)) { findItemState = FindItemState.None; itemIndex = 0; if (targetContainer != null) { var decontainObjective = new AIObjectiveDecontainItem(Character, divingSuit, targetContainer.GetComponent <ItemContainer>(), ObjectiveManager, targetContainer.GetComponent <ItemContainer>()); decontainObjective.Abandoned += () => ignoredContainers.Add(targetContainer); ObjectiveManager.CurrentObjective.AddSubObjective(decontainObjective, addFirst: true); return; } else { divingSuit.Drop(Character); } } } } } } if (findItemState == FindItemState.None || findItemState == FindItemState.DivingMask) { if (takeMaskOff) { var mask = Character.Inventory.FindItemByTag("divingmask"); if (mask != null && Character.Inventory.IsInLimbSlot(mask, InvSlotType.Head)) { if (!mask.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(mask, Character, new List <InvSlotType>() { InvSlotType.Any })) { if (oxygenLow || ObjectiveManager.GetCurrentPriority() >= AIObjectiveManager.RunPriority) { mask.Drop(Character); } else { findItemState = FindItemState.DivingMask; if (FindSuitableContainer(Character, mask, out Item targetContainer)) { findItemState = FindItemState.None; itemIndex = 0; if (targetContainer != null) { var decontainObjective = new AIObjectiveDecontainItem(Character, mask, targetContainer.GetComponent <ItemContainer>(), ObjectiveManager, targetContainer.GetComponent <ItemContainer>()); decontainObjective.Abandoned += () => ignoredContainers.Add(targetContainer); ObjectiveManager.CurrentObjective.AddSubObjective(decontainObjective, addFirst: true); return; } else { mask.Drop(Character); } } } } } } } } } if (findItemState == FindItemState.None || findItemState == FindItemState.OtherItem) { if (ObjectiveManager.IsCurrentObjective <AIObjectiveIdle>() || ObjectiveManager.IsCurrentObjective <AIObjectiveOperateItem>() || ObjectiveManager.IsCurrentObjective <AIObjectivePumpWater>() || ObjectiveManager.IsCurrentObjective <AIObjectiveChargeBatteries>()) { foreach (var item in Character.Inventory.Items) { if (item == null) { continue; } if (Character.HasEquippedItem(item) && (Character.Inventory.IsInLimbSlot(item, InvSlotType.RightHand) || Character.Inventory.IsInLimbSlot(item, InvSlotType.LeftHand) || Character.Inventory.IsInLimbSlot(item, InvSlotType.RightHand | InvSlotType.LeftHand))) { if (!item.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(item, Character, new List <InvSlotType>() { InvSlotType.Any })) { if (FindSuitableContainer(Character, item, out Item targetContainer)) { findItemState = FindItemState.None; itemIndex = 0; if (targetContainer != null) { var decontainObjective = new AIObjectiveDecontainItem(Character, item, targetContainer.GetComponent <ItemContainer>(), ObjectiveManager, targetContainer.GetComponent <ItemContainer>()); decontainObjective.Abandoned += () => ignoredContainers.Add(targetContainer); ObjectiveManager.CurrentObjective.AddSubObjective(decontainObjective, addFirst: true); return; } else { item.Drop(Character); } } else { findItemState = FindItemState.OtherItem; } } } } } } }
public override void Update(float deltaTime) { if (DisableCrewAI || Character.IsUnconscious) { return; } if (Character.Submarine != null || SelectedAiTarget?.Entity?.Submarine != null) { if (steeringManager != insideSteering) { insideSteering.Reset(); } steeringManager = insideSteering; } else { if (steeringManager != outsideSteering) { outsideSteering.Reset(); } steeringManager = outsideSteering; } AnimController.Crouching = shouldCrouch; CheckCrouching(deltaTime); Character.ClearInputs(); objectiveManager.UpdateObjectives(deltaTime); if (updateObjectiveTimer > 0.0f) { updateObjectiveTimer -= deltaTime; } else { objectiveManager.SortObjectives(); updateObjectiveTimer = UpdateObjectiveInterval; } if (Character.SpeechImpediment < 100.0f) { ReportProblems(); UpdateSpeaking(); } objectiveManager.DoCurrentObjective(deltaTime); bool run = objectiveManager.GetCurrentPriority() > AIObjectiveManager.OrderPriority; if (ObjectiveManager.CurrentObjective is AIObjectiveGoTo goTo && goTo.Target != null) { if (Vector2.DistanceSquared(Character.SimPosition, goTo.Target.SimPosition) > 3 * 3) { run = true; } } if (!run) { run = objectiveManager.CurrentObjective.ForceRun; } if (run) { run = !AnimController.Crouching && !AnimController.IsMovingBackwards; } float currentSpeed = Character.AnimController.GetCurrentSpeed(run); steeringManager.Update(currentSpeed); bool ignorePlatforms = Character.AnimController.TargetMovement.Y < -0.5f && (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X)); if (steeringManager == insideSteering) { var currPath = PathSteering.CurrentPath; if (currPath != null && currPath.CurrentNode != null) { if (currPath.CurrentNode.SimPosition.Y < Character.AnimController.GetColliderBottom().Y) { ignorePlatforms = true; } } if (Character.IsClimbing && PathSteering.InLadders && PathSteering.IsNextLadderSameAsCurrent) { Character.AnimController.TargetMovement = new Vector2(0.0f, Math.Sign(Character.AnimController.TargetMovement.Y)); } } Character.AnimController.IgnorePlatforms = ignorePlatforms; Vector2 targetMovement = AnimController.TargetMovement; if (!Character.AnimController.InWater) { targetMovement = new Vector2(Character.AnimController.TargetMovement.X, MathHelper.Clamp(Character.AnimController.TargetMovement.Y, -1.0f, 1.0f)); } float maxSpeed = Character.ApplyTemporarySpeedLimits(currentSpeed); targetMovement.X = MathHelper.Clamp(targetMovement.X, -maxSpeed, maxSpeed); targetMovement.Y = MathHelper.Clamp(targetMovement.Y, -maxSpeed, maxSpeed); //apply speed multiplier if // a. it's boosting the movement speed and the character is trying to move fast (= running) // b. it's a debuff that decreases movement speed float speedMultiplier = Character.SpeedMultiplier; if (run || speedMultiplier <= 0.0f) { targetMovement *= speedMultiplier; } Character.ResetSpeedMultiplier(); // Reset, items will set the value before the next update Character.AnimController.TargetMovement = targetMovement; if (!NeedsDivingGear(Character.CurrentHull)) { bool oxygenLow = Character.OxygenAvailable < CharacterHealth.LowOxygenThreshold; bool highPressure = Character.CurrentHull == null || Character.CurrentHull.LethalPressure > 0 && Character.PressureProtection <= 0; bool shouldKeepTheGearOn = objectiveManager.CurrentObjective.KeepDivingGearOn; bool removeDivingSuit = (oxygenLow && !highPressure) || (!shouldKeepTheGearOn && Character.CurrentHull.WaterPercentage < 1 && !Character.IsClimbing && steeringManager == insideSteering && !PathSteering.InStairs); if (removeDivingSuit) { var divingSuit = Character.Inventory.FindItemByIdentifier("divingsuit") ?? Character.Inventory.FindItemByTag("divingsuit"); if (divingSuit != null) { // TODO: take the item where it was taken from? divingSuit.Drop(Character); } } bool takeMaskOff = oxygenLow || (!shouldKeepTheGearOn && Character.CurrentHull.WaterPercentage < 20); if (takeMaskOff) { var mask = Character.Inventory.FindItemByIdentifier("divingmask"); if (mask != null) { // Try to put the mask in an Any slot, and drop it if that fails if (!mask.AllowedSlots.Contains(InvSlotType.Any) || !Character.Inventory.TryPutItem(mask, Character, new List <InvSlotType>() { InvSlotType.Any })) { mask.Drop(Character); } } } } if (!(ObjectiveManager.CurrentOrder is AIObjectiveExtinguishFires) && !(ObjectiveManager.CurrentObjective is AIObjectiveExtinguishFire)) { var extinguisherItem = Character.Inventory.FindItemByIdentifier("extinguisher") ?? Character.Inventory.FindItemByTag("extinguisher"); if (extinguisherItem != null && Character.HasEquippedItem(extinguisherItem)) { // TODO: take the item where it was taken from? extinguisherItem.Drop(Character); } } if (Character.IsKeyDown(InputType.Aim)) { var cursorDiffX = Character.CursorPosition.X - Character.Position.X; if (cursorDiffX > 10.0f) { Character.AnimController.TargetDir = Direction.Right; } else if (cursorDiffX < -10.0f) { Character.AnimController.TargetDir = Direction.Left; } if (Character.SelectedConstruction != null) { Character.SelectedConstruction.SecondaryUse(deltaTime, Character); } } else if (Math.Abs(Character.AnimController.TargetMovement.X) > 0.1f && !Character.AnimController.InWater) { Character.AnimController.TargetDir = Character.AnimController.TargetMovement.X > 0.0f ? Direction.Right : Direction.Left; } if (Character.CurrentHull != null) { PropagateHullSafety(Character, Character.CurrentHull); } }