public static void Draw(SpriteBatch spriteBatch, Character character, Camera cam) { if (GUI.DisableHUD) { return; } character.CharacterHealth.Alignment = Alignment.Right; if (GameMain.GameSession?.CrewManager != null) { orderIndicatorCount.Clear(); foreach (Pair <Order, float> timedOrder in GameMain.GameSession.CrewManager.ActiveOrders) { DrawOrderIndicator(spriteBatch, cam, character, timedOrder.First, MathHelper.Clamp(timedOrder.Second / 10.0f, 0.2f, 1.0f)); } if (character.CurrentOrder != null) { DrawOrderIndicator(spriteBatch, cam, character, character.CurrentOrder, 1.0f); } } foreach (Character.ObjectiveEntity objectiveEntity in character.ActiveObjectiveEntities) { DrawObjectiveIndicator(spriteBatch, cam, character, objectiveEntity, 1.0f); } foreach (Item brokenItem in brokenItems) { if (brokenItem.NonInteractable) { continue; } float dist = Vector2.Distance(character.WorldPosition, brokenItem.WorldPosition); Vector2 drawPos = brokenItem.DrawPosition; float alpha = Math.Min((1000.0f - dist) / 1000.0f * 2.0f, 1.0f); if (alpha <= 0.0f) { continue; } GUI.DrawIndicator(spriteBatch, drawPos, cam, 100.0f, GUI.BrokenIcon, Color.Lerp(GUI.Style.Red, GUI.Style.Orange * 0.5f, brokenItem.Condition / brokenItem.MaxCondition) * alpha); } if (!character.IsIncapacitated && character.Stun <= 0.0f && !IsCampaignInterfaceOpen && (!character.IsKeyDown(InputType.Aim) || character.SelectedItems.Any(it => it?.GetComponent <Sprayer>() == null))) { if (character.FocusedCharacter != null && character.FocusedCharacter.CanBeSelected) { DrawCharacterHoverTexts(spriteBatch, cam, character); } if (character.FocusedItem != null) { if (focusedItem != character.FocusedItem) { focusedItemOverlayTimer = Math.Min(1.0f, focusedItemOverlayTimer); shouldRecreateHudTexts = true; } focusedItem = character.FocusedItem; } if (focusedItem != null && focusedItemOverlayTimer > ItemOverlayDelay) { Vector2 circlePos = cam.WorldToScreen(focusedItem.DrawPosition); float circleSize = Math.Max(focusedItem.Rect.Width, focusedItem.Rect.Height) * 1.5f; circleSize = MathHelper.Clamp(circleSize, 45.0f, 100.0f) * Math.Min((focusedItemOverlayTimer - 1.0f) * 5.0f, 1.0f); if (circleSize > 0.0f) { Vector2 scale = new Vector2(circleSize / GUI.Style.FocusIndicator.FrameSize.X); GUI.Style.FocusIndicator.Draw(spriteBatch, (int)((focusedItemOverlayTimer - 1.0f) * GUI.Style.FocusIndicator.FrameCount * 3.0f), circlePos, Color.LightBlue * 0.3f, origin: GUI.Style.FocusIndicator.FrameSize.ToVector2() / 2, rotate: (float)Timing.TotalTime, scale: scale); } if (!GUI.DisableItemHighlights && !Inventory.DraggingItemToWorld) { bool shiftDown = PlayerInput.KeyDown(Keys.LeftShift) || PlayerInput.KeyDown(Keys.RightShift); if (shouldRecreateHudTexts || heldDownShiftWhenGotHudTexts != shiftDown) { shouldRecreateHudTexts = true; heldDownShiftWhenGotHudTexts = shiftDown; } var hudTexts = focusedItem.GetHUDTexts(character, shouldRecreateHudTexts); shouldRecreateHudTexts = false; int dir = Math.Sign(focusedItem.WorldPosition.X - character.WorldPosition.X); Vector2 textSize = GUI.Font.MeasureString(focusedItem.Name); Vector2 largeTextSize = GUI.SubHeadingFont.MeasureString(focusedItem.Name); Vector2 startPos = cam.WorldToScreen(focusedItem.DrawPosition); startPos.Y -= (hudTexts.Count + 1) * textSize.Y; if (focusedItem.Sprite != null) { startPos.X += (int)(circleSize * 0.4f * dir); startPos.Y -= (int)(circleSize * 0.4f); } Vector2 textPos = startPos; if (dir == -1) { textPos.X -= largeTextSize.X; } float alpha = MathHelper.Clamp((focusedItemOverlayTimer - ItemOverlayDelay) * 2.0f, 0.0f, 1.0f); GUI.DrawString(spriteBatch, textPos, focusedItem.Name, GUI.Style.TextColor * alpha, Color.Black * alpha * 0.7f, 2, font: GUI.SubHeadingFont); startPos.X += dir * 10.0f * GUI.Scale; textPos.X += dir * 10.0f * GUI.Scale; textPos.Y += largeTextSize.Y; foreach (ColoredText coloredText in hudTexts) { if (dir == -1) { textPos.X = (int)(startPos.X - GUI.SmallFont.MeasureString(coloredText.Text).X); } GUI.DrawString(spriteBatch, textPos, coloredText.Text, coloredText.Color * alpha, Color.Black * alpha * 0.7f, 2, GUI.SmallFont); textPos.Y += textSize.Y; } } } foreach (HUDProgressBar progressBar in character.HUDProgressBars.Values) { progressBar.Draw(spriteBatch, cam); } foreach (Character npc in Character.CharacterList) { if (npc.CampaignInteractionType == CampaignMode.InteractionType.None || npc.Submarine != character.Submarine || npc.IsDead || npc.IsIncapacitated) { continue; } var iconStyle = GUI.Style.GetComponentStyle("CampaignInteractionIcon." + npc.CampaignInteractionType); GUI.DrawIndicator(spriteBatch, npc.WorldPosition, cam, 500.0f, iconStyle.GetDefaultSprite(), iconStyle.Color); } } if (character.SelectedConstruction != null && (character.CanInteractWith(Character.Controlled.SelectedConstruction) || Screen.Selected == GameMain.SubEditorScreen)) { character.SelectedConstruction.DrawHUD(spriteBatch, cam, Character.Controlled); } if (IsCampaignInterfaceOpen) { return; } if (character.Inventory != null) { for (int i = 0; i < character.Inventory.Items.Length - 1; i++) { var item = character.Inventory.Items[i]; if (item == null || character.Inventory.SlotTypes[i] == InvSlotType.Any) { continue; } foreach (ItemComponent ic in item.Components) { if (ic.DrawHudWhenEquipped) { ic.DrawHUD(spriteBatch, character); } } } } bool mouseOnPortrait = false; if (character.Stun <= 0.1f && !character.IsDead) { if (CharacterHealth.OpenHealthWindow == null && character.SelectedCharacter == null) { if (character.Info != null && !character.ShouldLockHud()) { character.Info.DrawBackground(spriteBatch); character.Info.DrawJobIcon(spriteBatch, new Rectangle( (int)(HUDLayoutSettings.BottomRightInfoArea.X + HUDLayoutSettings.BottomRightInfoArea.Width * 0.05f), (int)(HUDLayoutSettings.BottomRightInfoArea.Y + HUDLayoutSettings.BottomRightInfoArea.Height * 0.1f), (int)(HUDLayoutSettings.BottomRightInfoArea.Width / 2), (int)(HUDLayoutSettings.BottomRightInfoArea.Height * 0.7f))); character.Info.DrawPortrait(spriteBatch, HUDLayoutSettings.PortraitArea.Location.ToVector2(), new Vector2(-12 * GUI.Scale, 4 * GUI.Scale), targetWidth: HUDLayoutSettings.PortraitArea.Width, true); } mouseOnPortrait = HUDLayoutSettings.BottomRightInfoArea.Contains(PlayerInput.MousePosition) && !character.ShouldLockHud(); if (mouseOnPortrait) { GUI.UIGlow.Draw(spriteBatch, HUDLayoutSettings.BottomRightInfoArea, GUI.Style.Green * 0.5f); } } if (ShouldDrawInventory(character)) { character.Inventory.Locked = character == Character.Controlled && LockInventory(character); character.Inventory.DrawOwn(spriteBatch); character.Inventory.CurrentLayout = CharacterHealth.OpenHealthWindow == null && character.SelectedCharacter == null ? CharacterInventory.Layout.Default : CharacterInventory.Layout.Right; } } if (!character.IsIncapacitated && character.Stun <= 0.0f) { if (character.IsHumanoid && character.SelectedCharacter != null && character.SelectedCharacter.Inventory != null) { if (character.SelectedCharacter.CanInventoryBeAccessed) { character.SelectedCharacter.Inventory.Locked = false; character.SelectedCharacter.Inventory.CurrentLayout = CharacterInventory.Layout.Left; character.SelectedCharacter.Inventory.DrawOwn(spriteBatch); } if (CharacterHealth.OpenHealthWindow == character.SelectedCharacter.CharacterHealth) { character.SelectedCharacter.CharacterHealth.Alignment = Alignment.Left; character.SelectedCharacter.CharacterHealth.DrawStatusHUD(spriteBatch); } } else if (character.Inventory != null) { //character.Inventory.CurrentLayout = (CharacterHealth.OpenHealthWindow == null) ? Alignment.Center : Alignment.Left; } } if (mouseOnPortrait) { GUIComponent.DrawToolTip( spriteBatch, character.Info?.Job == null ? character.DisplayName : character.DisplayName + " (" + character.Info.Job.Name + ")", HUDLayoutSettings.PortraitArea); } }
private void UpdateAttack(float deltaTime) { if (selectedAiTarget == null || selectedAiTarget.Entity == null || selectedAiTarget.Entity.Removed) { state = AIState.None; return; } selectedTargetMemory.Priority -= deltaTime; Vector2 attackSimPosition = Character.Submarine == null?ConvertUnits.ToSimUnits(selectedAiTarget.WorldPosition) : selectedAiTarget.SimPosition; if (wallAttackPos != Vector2.Zero && targetEntity != null) { attackSimPosition = wallAttackPos; if (selectedAiTarget.Entity != null && Character.Submarine == null && selectedAiTarget.Entity.Submarine != null) { attackSimPosition += ConvertUnits.ToSimUnits(selectedAiTarget.Entity.Submarine.Position); } } else if (selectedAiTarget.Entity is Character) { //target the closest limb if the target is a character float closestDist = Vector2.DistanceSquared(selectedAiTarget.SimPosition, SimPosition); foreach (Limb limb in ((Character)selectedAiTarget.Entity).AnimController.Limbs) { if (limb == null) { continue; } float dist = Vector2.DistanceSquared(limb.SimPosition, SimPosition); if (dist < closestDist) { closestDist = dist; attackSimPosition = limb.SimPosition; } } } if (Math.Abs(Character.AnimController.movement.X) > 0.1f && !Character.AnimController.InWater) { Character.AnimController.TargetDir = Character.SimPosition.X < attackSimPosition.X ? Direction.Right : Direction.Left; } if (coolDownTimer > 0.0f) { UpdateCoolDown(attackSimPosition, deltaTime); return; } if (raycastTimer > 0.0) { raycastTimer -= deltaTime; } else { GetTargetEntity(); raycastTimer = RaycastInterval; } //steeringManager.SteeringAvoid(deltaTime, 1.0f); Limb attackLimb = attackingLimb; //check if any of the limbs is close enough to attack the target if (attackingLimb == null) { foreach (Limb limb in Character.AnimController.Limbs) { if (limb.attack == null) { continue; } attackLimb = limb; if (ConvertUnits.ToDisplayUnits(Vector2.Distance(limb.SimPosition, attackSimPosition)) > limb.attack.Range) { continue; } attackingLimb = limb; break; } if (Character.IsRemotePlayer) { if (!Character.IsKeyDown(InputType.Attack)) { return; } } } if (attackLimb != null) { steeringManager.SteeringSeek(attackSimPosition - (attackLimb.SimPosition - SimPosition), 3); if (steeringManager is IndoorsSteeringManager) { var indoorsSteering = (IndoorsSteeringManager)steeringManager; if (indoorsSteering.CurrentPath != null) { if (indoorsSteering.CurrentPath.Unreachable) { //wander around randomly and decrease the priority faster if no path is found if (selectedTargetMemory != null) { selectedTargetMemory.Priority -= deltaTime * 10.0f; } steeringManager.SteeringWander(); } else if (indoorsSteering.CurrentPath.Finished) { steeringManager.SteeringManual(deltaTime, attackSimPosition - attackLimb.SimPosition); } } } if (attackingLimb != null) { UpdateLimbAttack(deltaTime, attackingLimb, attackSimPosition); } } }
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 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 (!Character.LockHands) { DropUnnecessaryItems(); } 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 override void Update(float deltaTime) { if (DisableCrewAI || Character.IsUnconscious) { return; } Character.ClearInputs(); //steeringManager = Character.AnimController.CurrentHull == null ? outdoorsSteeringManager : indoorsSteeringManager; if (updateObjectiveTimer > 0.0f) { updateObjectiveTimer -= deltaTime; } else { objectiveManager.UpdateObjectives(); updateObjectiveTimer = UpdateObjectiveInterval; } objectiveManager.DoCurrentObjective(deltaTime); float currObjectivePriority = objectiveManager.GetCurrentPriority(Character); float moveSpeed = 1.0f; if (currObjectivePriority > 30.0f) { moveSpeed *= Character.AnimController.InWater ? Character.AnimController.SwimSpeedMultiplier : Character.AnimController.RunSpeedMultiplier; } steeringManager.Update(moveSpeed); bool ignorePlatforms = Character.AnimController.TargetMovement.Y < -0.5f && (-Character.AnimController.TargetMovement.Y > Math.Abs(Character.AnimController.TargetMovement.X)); var currPath = (steeringManager as IndoorsSteeringManager).CurrentPath; if (currPath != null && currPath.CurrentNode != null) { if (currPath.CurrentNode.SimPosition.Y < Character.AnimController.GetColliderBottom().Y) { ignorePlatforms = true; } } Character.AnimController.IgnorePlatforms = ignorePlatforms; (Character.AnimController as HumanoidAnimController).Crouching = false; if (!Character.AnimController.InWater) { Character.AnimController.TargetMovement = new Vector2( Character.AnimController.TargetMovement.X, MathHelper.Clamp(Character.AnimController.TargetMovement.Y, -1.0f, 1.0f)) * Character.SpeedMultiplier; Character.SpeedMultiplier = 1.0f; } if (Character.SelectedConstruction != null && Character.SelectedConstruction.GetComponent <Items.Components.Ladder>() != null) { if (currPath != null && currPath.CurrentNode != null && currPath.CurrentNode.Ladders != null) { Character.AnimController.TargetMovement = new Vector2(0.0f, Math.Sign(Character.AnimController.TargetMovement.Y)); } } //suit can be taken off if there character is inside a hull and there's air in the room bool canTakeOffSuit = Character.AnimController.CurrentHull != null && Character.AnimController.CurrentHull.OxygenPercentage > 30.0f && Character.AnimController.CurrentHull.Volume < Character.AnimController.CurrentHull.FullVolume * 0.3f; //the suit can be taken off and the character is running out of oxygen (couldn't find a tank for the suit?) or idling //-> take the suit off if (canTakeOffSuit && (Character.Oxygen < 50.0f || objectiveManager.CurrentObjective is AIObjectiveIdle)) { var divingSuit = Character.Inventory.FindItem("Diving Suit"); if (divingSuit != null) { divingSuit.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 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); } }