protected bool HasOrder(Character character, string aiTag, string option = null) { if (character.CurrentOrder?.AITag == aiTag) { if (option == null) { return(true); } else { HumanAIController humanAI = character.AIController as HumanAIController; return(humanAI.CurrentOrderOption == option); } } return(false); }
private bool HasOrder(string aiTag, string option = null) { for (int i = 0; i < crew.Count; i++) { if (crew[i].CurrentOrder?.AITag == aiTag) { if (option == null) { return(true); } else { HumanAIController humanAI = crew[i].AIController as HumanAIController; return(humanAI.CurrentOrderOption == option); } } } return(false); }
protected void ApplyEffectSpecific(Character targetCharacter) { foreach (var statusEffect in statusEffects) { if (statusEffect.HasTargetType(StatusEffect.TargetType.UseTarget)) { // currently used to spawn items on the targeted character statusEffect.SetUser(targetCharacter); statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, targetCharacter, targetCharacter); } else if (statusEffect.HasTargetType(StatusEffect.TargetType.NearbyCharacters)) { targets.Clear(); targets.AddRange(statusEffect.GetNearbyTargets(targetCharacter.WorldPosition, targets)); if (!nearbyCharactersAppliesToSelf) { targets.RemoveAll(c => c == Character); } if (!nearbyCharactersAppliesToAllies) { targets.RemoveAll(c => c is Character otherCharacter && HumanAIController.IsFriendly(otherCharacter, Character)); } statusEffect.SetUser(Character); statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, targetCharacter, targets); } else if (statusEffect.HasTargetType(StatusEffect.TargetType.Character)) { statusEffect.SetUser(Character); statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, Character, targetCharacter); } else { statusEffect.SetUser(Character); statusEffect.Apply(ActionType.OnAbility, EffectDeltaTime, Character, Character); } } }
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective) { if (character.AIController.SelectedAiTarget?.Entity is Character previousTarget && previousTarget.IsDead) { character?.Speak(TextManager.Get("DialogTurretTargetDead"), null, 0.0f, "killedtarget" + previousTarget.ID, 30.0f); character.AIController.SelectTarget(null); } if (GetAvailableBatteryPower() < powerConsumption) { var batteries = item.GetConnectedComponents <PowerContainer>(); float lowestCharge = 0.0f; PowerContainer batteryToLoad = null; foreach (PowerContainer battery in batteries) { if (batteryToLoad == null || battery.Charge < lowestCharge) { batteryToLoad = battery; lowestCharge = battery.Charge; } } if (batteryToLoad == null) { return(true); } if (batteryToLoad.RechargeSpeed < batteryToLoad.MaxRechargeSpeed * 0.4f) { objective.AddSubObjective(new AIObjectiveOperateItem(batteryToLoad, character, objective.objectiveManager, option: "", requireEquip: false)); return(false); } } int usableProjectileCount = 0; int maxProjectileCount = 0; foreach (MapEntity e in item.linkedTo) { if (!(e is Item projectileContainer)) { continue; } var containedItems = projectileContainer.ContainedItems; if (containedItems != null) { var container = projectileContainer.GetComponent <ItemContainer>(); maxProjectileCount += container.Capacity; int projectiles = containedItems.Count(it => it.Condition > 0.0f); usableProjectileCount += projectiles; } } if (usableProjectileCount == 0 || (usableProjectileCount < maxProjectileCount && objective.Option.Equals("fireatwill", StringComparison.OrdinalIgnoreCase))) { ItemContainer container = null; Item containerItem = null; foreach (MapEntity e in item.linkedTo) { containerItem = e as Item; if (containerItem == null) { continue; } container = containerItem.GetComponent <ItemContainer>(); if (container != null) { break; } } if (container == null || container.ContainableItems.Count == 0) { return(true); } if (objective.SubObjectives.None()) { if (!AIDecontainEmptyItems(character, objective, equip: true, sourceContainer: container)) { return(false); } } if (objective.SubObjectives.None()) { var loadItemsObjective = AIContainItems <Turret>(container, character, objective, usableProjectileCount + 1, equip: true, removeEmpty: true); loadItemsObjective.ignoredContainerIdentifiers = new string[] { containerItem.prefab.Identifier }; character.Speak(TextManager.GetWithVariable("DialogLoadTurret", "[itemname]", item.Name, true), null, 0.0f, "loadturret", 30.0f); } return(false); } //enough shells and power Character closestEnemy = null; float closestDist = AIRange * AIRange; foreach (Character enemy in Character.CharacterList) { // Ignore dead, friendly, and those that are inside the same sub if (enemy.IsDead || !enemy.Enabled || enemy.Submarine == character.Submarine) { continue; } if (HumanAIController.IsFriendly(character, enemy)) { continue; } float dist = Vector2.DistanceSquared(enemy.WorldPosition, item.WorldPosition); if (dist > closestDist) { continue; } float angle = -MathUtils.VectorToAngle(enemy.WorldPosition - item.WorldPosition); float midRotation = (minRotation + maxRotation) / 2.0f; while (midRotation - angle < -MathHelper.Pi) { angle -= MathHelper.TwoPi; } while (midRotation - angle > MathHelper.Pi) { angle += MathHelper.TwoPi; } if (angle < minRotation || angle > maxRotation) { continue; } closestEnemy = enemy; closestDist = dist; } if (closestEnemy == null) { return(false); } character.AIController.SelectTarget(closestEnemy.AiTarget); character.CursorPosition = closestEnemy.WorldPosition; if (item.Submarine != null) { character.CursorPosition -= item.Submarine.Position; } float enemyAngle = MathUtils.VectorToAngle(closestEnemy.WorldPosition - item.WorldPosition); float turretAngle = -rotation; if (Math.Abs(MathUtils.GetShortestAngle(enemyAngle, turretAngle)) > 0.15f) { return(false); } Vector2 start = ConvertUnits.ToSimUnits(item.WorldPosition); Vector2 end = ConvertUnits.ToSimUnits(closestEnemy.WorldPosition); if (closestEnemy.Submarine != null) { start -= closestEnemy.Submarine.SimPosition; end -= closestEnemy.Submarine.SimPosition; } var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel; var pickedBody = Submarine.PickBody(start, end, null, collisionCategories, allowInsideFixture: true, customPredicate: (Fixture f) => { return(!item.StaticFixtures.Contains(f)); }); if (pickedBody == null) { return(false); } Character targetCharacter = null; if (pickedBody.UserData is Character c) { targetCharacter = c; } else if (pickedBody.UserData is Limb limb) { targetCharacter = limb.character; } if (targetCharacter != null) { if (HumanAIController.IsFriendly(character, targetCharacter)) { // Don't shoot friendly characters return(false); } } else { if (pickedBody.UserData is ISpatialEntity e) { Submarine sub = e.Submarine; if (sub == null) { return(false); } if (sub == Item.Submarine) { return(false); } // Don't shoot non-player submarines, i.e. wrecks or outposts. if (!sub.Info.IsPlayer) { return(false); } // Don't shoot friendly submarines. if (sub.TeamID == Item.Submarine.TeamID) { return(false); } } else { // Hit something else, probably a level wall return(false); } } if (objective.Option.Equals("fireatwill", StringComparison.OrdinalIgnoreCase)) { character?.Speak(TextManager.GetWithVariable("DialogFireTurret", "[itemname]", item.Name, true), null, 0.0f, "fireturret", 5.0f); character.SetInput(InputType.Shoot, true, true); } return(false); }
public static void ServerRead(IReadMessage msg, Client c) { c.KickAFKTimer = 0.0f; UInt16 ID = msg.ReadUInt16(); ChatMessageType type = (ChatMessageType)msg.ReadByte(); string txt; Character orderTargetCharacter = null; Entity orderTargetEntity = null; OrderChatMessage orderMsg = null; OrderTarget orderTargetPosition = null; Order.OrderTargetType orderTargetType = Order.OrderTargetType.Entity; int?wallSectionIndex = null; if (type == ChatMessageType.Order) { var orderMessageInfo = OrderChatMessage.ReadOrder(msg); if (orderMessageInfo.OrderIndex < 0 || orderMessageInfo.OrderIndex >= Order.PrefabList.Count) { DebugConsole.ThrowError($"Invalid order message from client \"{c.Name}\" - order index out of bounds ({orderMessageInfo.OrderIndex})."); if (NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID)) { c.LastSentChatMsgID = ID; } return; } orderTargetCharacter = orderMessageInfo.TargetCharacter; orderTargetEntity = orderMessageInfo.TargetEntity; orderTargetPosition = orderMessageInfo.TargetPosition; orderTargetType = orderMessageInfo.TargetType; wallSectionIndex = orderMessageInfo.WallSectionIndex; var orderPrefab = orderMessageInfo.OrderPrefab ?? Order.PrefabList[orderMessageInfo.OrderIndex]; string orderOption = orderMessageInfo.OrderOption ?? (orderMessageInfo.OrderOptionIndex == null || orderMessageInfo.OrderOptionIndex < 0 || orderMessageInfo.OrderOptionIndex >= orderPrefab.Options.Length ? "" : orderPrefab.Options[orderMessageInfo.OrderOptionIndex.Value]); orderMsg = new OrderChatMessage(orderPrefab, orderOption, orderMessageInfo.Priority, orderTargetPosition ?? orderTargetEntity as ISpatialEntity, orderTargetCharacter, c.Character) { WallSectionIndex = wallSectionIndex }; txt = orderMsg.Text; } else { txt = msg.ReadString() ?? ""; } if (!NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID)) { return; } c.LastSentChatMsgID = ID; if (txt.Length > MaxLength) { txt = txt.Substring(0, MaxLength); } c.LastSentChatMessages.Add(txt); if (c.LastSentChatMessages.Count > 10) { c.LastSentChatMessages.RemoveRange(0, c.LastSentChatMessages.Count - 10); } float similarity = 0.0f; for (int i = 0; i < c.LastSentChatMessages.Count; i++) { float closeFactor = 1.0f / (c.LastSentChatMessages.Count - i); if (string.IsNullOrEmpty(txt)) { similarity += closeFactor; } else { int levenshteinDist = ToolBox.LevenshteinDistance(txt, c.LastSentChatMessages[i]); similarity += Math.Max((txt.Length - levenshteinDist) / (float)txt.Length * closeFactor, 0.0f); } } //order/report messages can be sent a little faster than normal messages without triggering the spam filter if (orderMsg != null) { similarity *= 0.25f; } bool isOwner = GameMain.Server.OwnerConnection != null && c.Connection == GameMain.Server.OwnerConnection; if (similarity + c.ChatSpamSpeed > 5.0f && !isOwner) { GameMain.Server.KarmaManager.OnSpamFilterTriggered(c); c.ChatSpamCount++; if (c.ChatSpamCount > 3) { //kick for spamming too much GameMain.Server.KickClient(c, TextManager.Get("SpamFilterKicked")); } else { ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null); c.ChatSpamTimer = 10.0f; GameMain.Server.SendDirectChatMessage(denyMsg, c); } return; } c.ChatSpamSpeed += similarity + 0.5f; if (c.ChatSpamTimer > 0.0f && !isOwner) { ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null); c.ChatSpamTimer = 10.0f; GameMain.Server.SendDirectChatMessage(denyMsg, c); return; } var should = GameMain.Lua.hook.Call("chatMessage", new DynValue[] { DynValue.NewString(txt), UserData.Create(c) }); if (should != null) { if (should.CastToBool()) { return; } else { } } if (type == ChatMessageType.Order) { if (c.Character == null || c.Character.SpeechImpediment >= 100.0f || c.Character.IsDead) { return; } if (orderMsg.Order.IsReport) { HumanAIController.ReportProblem(orderMsg.Sender, orderMsg.Order); } Order order = orderTargetType switch { Order.OrderTargetType.Entity => new Order(orderMsg.Order, orderTargetEntity, orderMsg.Order?.GetTargetItemComponent(orderTargetEntity as Item), orderGiver: orderMsg.Sender), Order.OrderTargetType.Position => new Order(orderMsg.Order, orderTargetPosition, orderGiver: orderMsg.Sender), Order.OrderTargetType.WallSection when orderTargetEntity is Structure s && wallSectionIndex.HasValue => new Order(orderMsg.Order, s, wallSectionIndex, orderGiver: orderMsg.Sender), _ => throw new NotImplementedException() }; if (order != null) { if (order.TargetAllCharacters) { if (order.IsIgnoreOrder) { switch (orderTargetType) { case Order.OrderTargetType.Entity: if (!(orderTargetEntity is IIgnorable ignorableEntity)) { break; } ignorableEntity.OrderedToBeIgnored = order.Identifier == "ignorethis"; break; case Order.OrderTargetType.Position: throw new NotImplementedException(); case Order.OrderTargetType.WallSection: if (!wallSectionIndex.HasValue) { break; } if (!(orderTargetEntity is Structure s)) { break; } if (!(s.GetSection(wallSectionIndex.Value) is IIgnorable ignorableWall)) { break; } ignorableWall.OrderedToBeIgnored = order.Identifier == "ignorethis"; break; } } GameMain.GameSession?.CrewManager?.AddOrder(order, order.IsIgnoreOrder ? (float?)null : order.FadeOutTime); } else if (orderTargetCharacter != null) { orderTargetCharacter.SetOrder(order, orderMsg.OrderOption, orderMsg.OrderPriority, orderMsg.Sender); } } GameMain.Server.SendOrderChatMessage(orderMsg); } else { GameMain.Server.SendChatMessage(txt, null, c); } }
public static void ServerRead(IReadMessage msg, Client c) { c.KickAFKTimer = 0.0f; UInt16 ID = msg.ReadUInt16(); ChatMessageType type = (ChatMessageType)msg.ReadByte(); string txt; Character orderTargetCharacter = null; Entity orderTargetEntity = null; OrderChatMessage orderMsg = null; OrderTarget orderTargetPosition = null; Order.OrderTargetType orderTargetType = Order.OrderTargetType.Entity; int?wallSectionIndex = null; if (type == ChatMessageType.Order) { int orderIndex = msg.ReadByte(); orderTargetCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character; orderTargetEntity = Entity.FindEntityByID(msg.ReadUInt16()) as Entity; Order orderPrefab = null; int? orderOptionIndex = null; string orderOption = null; // The option of a Dismiss order is written differently so we know what order we target // now that the game supports multiple current orders simultaneously if (orderIndex >= 0 && orderIndex < Order.PrefabList.Count) { orderPrefab = Order.PrefabList[orderIndex]; if (orderPrefab.Identifier != "dismissed") { orderOptionIndex = msg.ReadByte(); } // Does the dismiss order have a specified target? else if (msg.ReadBoolean()) { int identifierCount = msg.ReadByte(); if (identifierCount > 0) { int dismissedOrderIndex = msg.ReadByte(); Order dismissedOrderPrefab = null; if (dismissedOrderIndex >= 0 && dismissedOrderIndex < Order.PrefabList.Count) { dismissedOrderPrefab = Order.PrefabList[dismissedOrderIndex]; orderOption = dismissedOrderPrefab.Identifier; } if (identifierCount > 1) { int dismissedOrderOptionIndex = msg.ReadByte(); if (dismissedOrderPrefab != null) { var options = dismissedOrderPrefab.Options; if (options != null && dismissedOrderOptionIndex >= 0 && dismissedOrderOptionIndex < options.Length) { orderOption += $".{options[dismissedOrderOptionIndex]}"; } } } } } } else { orderOptionIndex = msg.ReadByte(); } int orderPriority = msg.ReadByte(); orderTargetType = (Order.OrderTargetType)msg.ReadByte(); if (msg.ReadBoolean()) { var x = msg.ReadSingle(); var y = msg.ReadSingle(); var hull = Entity.FindEntityByID(msg.ReadUInt16()) as Hull; orderTargetPosition = new OrderTarget(new Vector2(x, y), hull, true); } else if (orderTargetType == Order.OrderTargetType.WallSection) { wallSectionIndex = msg.ReadByte(); } if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count) { DebugConsole.ThrowError($"Invalid order message from client \"{c.Name}\" - order index out of bounds ({orderIndex})."); if (NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID)) { c.LastSentChatMsgID = ID; } return; } orderPrefab ??= Order.PrefabList[orderIndex]; orderOption ??= orderOptionIndex == null || orderOptionIndex < 0 || orderOptionIndex >= orderPrefab.Options.Length ? "" : orderPrefab.Options[orderOptionIndex.Value]; orderMsg = new OrderChatMessage(orderPrefab, orderOption, orderPriority, orderTargetPosition ?? orderTargetEntity as ISpatialEntity, orderTargetCharacter, c.Character) { WallSectionIndex = wallSectionIndex }; txt = orderMsg.Text; } else { txt = msg.ReadString() ?? ""; } if (!NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID)) { return; } c.LastSentChatMsgID = ID; if (txt.Length > MaxLength) { txt = txt.Substring(0, MaxLength); } c.LastSentChatMessages.Add(txt); if (c.LastSentChatMessages.Count > 10) { c.LastSentChatMessages.RemoveRange(0, c.LastSentChatMessages.Count - 10); } float similarity = 0.0f; for (int i = 0; i < c.LastSentChatMessages.Count; i++) { float closeFactor = 1.0f / (c.LastSentChatMessages.Count - i); if (string.IsNullOrEmpty(txt)) { similarity += closeFactor; } else { int levenshteinDist = ToolBox.LevenshteinDistance(txt, c.LastSentChatMessages[i]); similarity += Math.Max((txt.Length - levenshteinDist) / (float)txt.Length * closeFactor, 0.0f); } } //order/report messages can be sent a little faster than normal messages without triggering the spam filter if (orderMsg != null) { similarity *= 0.25f; } bool isOwner = GameMain.Server.OwnerConnection != null && c.Connection == GameMain.Server.OwnerConnection; if (similarity + c.ChatSpamSpeed > 5.0f && !isOwner) { GameMain.Server.KarmaManager.OnSpamFilterTriggered(c); c.ChatSpamCount++; if (c.ChatSpamCount > 3) { //kick for spamming too much GameMain.Server.KickClient(c, TextManager.Get("SpamFilterKicked")); } else { ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null); c.ChatSpamTimer = 10.0f; GameMain.Server.SendDirectChatMessage(denyMsg, c); } return; } c.ChatSpamSpeed += similarity + 0.5f; if (c.ChatSpamTimer > 0.0f && !isOwner) { ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null); c.ChatSpamTimer = 10.0f; GameMain.Server.SendDirectChatMessage(denyMsg, c); return; } if (type == ChatMessageType.Order) { if (c.Character == null || c.Character.SpeechImpediment >= 100.0f || c.Character.IsDead) { return; } Order order = null; if (orderMsg.Order.IsReport) { HumanAIController.ReportProblem(orderMsg.Sender, orderMsg.Order); } else if (orderTargetCharacter != null && !orderMsg.Order.TargetAllCharacters) { switch (orderTargetType) { case Order.OrderTargetType.Entity: order = new Order(orderMsg.Order.Prefab, orderTargetEntity, orderMsg.Order.Prefab?.GetTargetItemComponent(orderTargetEntity as Item), orderGiver: orderMsg.Sender); break; case Order.OrderTargetType.Position: order = new Order(orderMsg.Order.Prefab, orderTargetPosition, orderGiver: orderMsg.Sender); break; } if (order != null) { orderTargetCharacter.SetOrder(order, orderMsg.OrderOption, orderMsg.OrderPriority, orderMsg.Sender); } } else if (orderMsg.Order.IsIgnoreOrder) { switch (orderTargetType) { case Order.OrderTargetType.Entity: if (orderTargetEntity is IIgnorable ignorableEntity) { ignorableEntity.OrderedToBeIgnored = orderMsg.Order.Identifier == "ignorethis"; } break; case Order.OrderTargetType.WallSection: if (!wallSectionIndex.HasValue) { break; } if (orderTargetEntity is Structure s && s.GetSection(wallSectionIndex.Value) is IIgnorable ignorableWall) { ignorableWall.OrderedToBeIgnored = orderMsg.Order.Identifier == "ignorethis"; } break; } } GameMain.Server.SendOrderChatMessage(orderMsg); } else { GameMain.Server.SendChatMessage(txt, null, c); } }
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective) { if (!(objective.OperateTarget is Gap leak)) { return(true); } if (leak.Submarine == null) { return(true); } Vector2 fromCharacterToLeak = leak.WorldPosition - character.WorldPosition; float dist = fromCharacterToLeak.Length(); float reach = Range + ConvertUnits.ToDisplayUnits(((HumanoidAnimController)character.AnimController).ArmLength); //too far away -> consider this done and hope the AI is smart enough to move closer if (dist > reach * 2) { return(true); } character.AIController.SteeringManager.Reset(); //steer closer if almost in range if (dist > reach) { if (character.AnimController.InWater) { if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering) { // Swimming inside the sub if (indoorSteering.CurrentPath != null && !indoorSteering.IsPathDirty && indoorSteering.CurrentPath.Unreachable) { Vector2 dir = Vector2.Normalize(fromCharacterToLeak); character.AIController.SteeringManager.SteeringManual(deltaTime, dir); } else { character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak)); } } else { // Swimming outside the sub character.AIController.SteeringManager.SteeringSeek(character.GetRelativeSimPosition(leak)); } } else { // TODO: use the collider size? if (!character.AnimController.InWater && character.AnimController is HumanoidAnimController && Math.Abs(fromCharacterToLeak.X) < 100.0f && fromCharacterToLeak.Y < 0.0f && fromCharacterToLeak.Y > -150.0f) { ((HumanoidAnimController)character.AnimController).Crouching = true; } Vector2 standPos = new Vector2(Math.Sign(-fromCharacterToLeak.X), Math.Sign(-fromCharacterToLeak.Y)) / 2; if (leak.IsHorizontal) { standPos.X *= 2; standPos.Y = 0; } else { standPos.X = 0; } character.AIController.SteeringManager.SteeringSeek(standPos); } } else { if (dist < reach / 2) { // Too close -> steer away character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition)); } else if (dist <= reach) { // In range character.CursorPosition = leak.Position; character.CursorPosition += VectorExtensions.Forward(Item.body.TransformedRotation + (float)Math.Sin(sinTime) / 2, dist / 2); if (character.AnimController.InWater) { var torso = character.AnimController.GetLimb(LimbType.Torso); // Turn facing the target when not moving (handled in the animcontroller if not moving) Vector2 mousePos = ConvertUnits.ToSimUnits(character.CursorPosition); Vector2 diff = (mousePos - torso.SimPosition) * character.AnimController.Dir; float newRotation = MathUtils.VectorToAngle(diff); character.AnimController.Collider.SmoothRotate(newRotation, 5.0f); if (VectorExtensions.Angle(VectorExtensions.Forward(torso.body.TransformedRotation), fromCharacterToLeak) < MathHelper.PiOver4) { // Swim past Vector2 moveDir = leak.IsHorizontal ? Vector2.UnitY : Vector2.UnitX; moveDir *= character.AnimController.Dir; character.AIController.SteeringManager.SteeringManual(deltaTime, moveDir); } } } } if (item.RequireAimToUse) { character.SetInput(InputType.Aim, false, true); sinTime += deltaTime * 5; } // Press the trigger only when the tool is approximately facing the target. Vector2 fromItemToLeak = leak.WorldPosition - item.WorldPosition; var angle = VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak); if (angle < MathHelper.PiOver4) { // Check that we don't hit any friendlies if (Submarine.PickBodies(item.SimPosition, leak.SimPosition, collisionCategory: Physics.CollisionCharacter).None(hit => { if (hit.UserData is Character c) { if (c == character) { return(false); } return(HumanAIController.IsFriendly(character, c)); } return(false); })) { character.SetInput(InputType.Shoot, false, true); Use(deltaTime, character); } } bool leakFixed = (leak.Open <= 0.0f || leak.Removed) && (leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1); if (leakFixed && leak.FlowTargetHull != null) { if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f)) { character.Speak(TextManager.GetWithVariable("DialogLeaksFixed", "[roomname]", leak.FlowTargetHull.DisplayName, true), null, 0.0f, "leaksfixed", 10.0f); } else { character.Speak(TextManager.GetWithVariable("DialogLeakFixed", "[roomname]", leak.FlowTargetHull.DisplayName, true), null, 0.0f, "leakfixed", 10.0f); } } return(leakFixed); }
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective) { if (character.AIController.SelectedAiTarget?.Entity is Character previousTarget && previousTarget.IsDead) { character?.Speak(TextManager.Get("DialogTurretTargetDead"), null, 0.0f, "killedtarget" + previousTarget.ID, 30.0f); character.AIController.SelectTarget(null); } if (GetAvailablePower() < powerConsumption) { var batteries = item.GetConnectedComponents <PowerContainer>(); float lowestCharge = 0.0f; PowerContainer batteryToLoad = null; foreach (PowerContainer battery in batteries) { if (batteryToLoad == null || battery.Charge < lowestCharge) { batteryToLoad = battery; lowestCharge = battery.Charge; } } if (batteryToLoad == null) { return(true); } if (batteryToLoad.RechargeSpeed < batteryToLoad.MaxRechargeSpeed * 0.4f) { objective.AddSubObjective(new AIObjectiveOperateItem(batteryToLoad, character, objective.objectiveManager, option: "", requireEquip: false)); return(false); } } int usableProjectileCount = 0; int maxProjectileCount = 0; foreach (MapEntity e in item.linkedTo) { if (!(e is Item projectileContainer)) { continue; } var containedItems = projectileContainer.ContainedItems; if (containedItems != null) { var container = projectileContainer.GetComponent <ItemContainer>(); maxProjectileCount += container.Capacity; int projectiles = containedItems.Count(it => it.Condition > 0.0f); usableProjectileCount += projectiles; } } if (usableProjectileCount == 0 || (usableProjectileCount < maxProjectileCount && objective.Option.ToLowerInvariant() != "fireatwill")) { ItemContainer container = null; Item containerItem = null; foreach (MapEntity e in item.linkedTo) { containerItem = e as Item; if (containerItem == null) { continue; } container = containerItem.GetComponent <ItemContainer>(); if (container != null) { break; } } if (container == null || container.ContainableItems.Count == 0) { return(true); } if (container.Inventory.Items[0] != null && container.Inventory.Items[0].Condition <= 0.0f) { var removeShellObjective = new AIObjectiveDecontainItem(character, container.Inventory.Items[0], container, objective.objectiveManager); objective.AddSubObjective(removeShellObjective); } var containShellObjective = new AIObjectiveContainItem(character, container.ContainableItems[0].Identifiers[0], container, objective.objectiveManager); character?.Speak(TextManager.GetWithVariable("DialogLoadTurret", "[itemname]", item.Name, true), null, 0.0f, "loadturret", 30.0f); containShellObjective.targetItemCount = usableProjectileCount + 1; containShellObjective.ignoredContainerIdentifiers = new string[] { containerItem.prefab.Identifier }; objective.AddSubObjective(containShellObjective); return(false); } //enough shells and power Character closestEnemy = null; float closestDist = 3000 * 3000; foreach (Character enemy in Character.CharacterList) { // Ignore friendly and those that are inside the sub if (enemy.IsDead || enemy.AnimController.CurrentHull != null || !enemy.Enabled) { continue; } if (HumanAIController.IsFriendly(character, enemy)) { continue; } float dist = Vector2.DistanceSquared(enemy.WorldPosition, item.WorldPosition); if (dist > closestDist) { continue; } float angle = -MathUtils.VectorToAngle(enemy.WorldPosition - item.WorldPosition); float midRotation = (minRotation + maxRotation) / 2.0f; while (midRotation - angle < -MathHelper.Pi) { angle -= MathHelper.TwoPi; } while (midRotation - angle > MathHelper.Pi) { angle += MathHelper.TwoPi; } if (angle < minRotation || angle > maxRotation) { continue; } closestEnemy = enemy; closestDist = dist; } if (closestEnemy == null) { return(false); } character.AIController.SelectTarget(closestEnemy.AiTarget); character.CursorPosition = closestEnemy.WorldPosition; if (item.Submarine != null) { character.CursorPosition -= item.Submarine.Position; } float enemyAngle = MathUtils.VectorToAngle(closestEnemy.WorldPosition - item.WorldPosition); float turretAngle = -rotation; if (Math.Abs(MathUtils.GetShortestAngle(enemyAngle, turretAngle)) > 0.15f) { return(false); } var pickedBody = Submarine.PickBody(ConvertUnits.ToSimUnits(item.WorldPosition), closestEnemy.SimPosition); if (pickedBody == null) { return(false); } Character target = null; if (pickedBody.UserData is Character c) { target = c; } else if (pickedBody.UserData is Limb limb) { target = limb.character; } if (target == null || HumanAIController.IsFriendly(character, target)) { return(false); } if (objective.Option.ToLowerInvariant() == "fireatwill") { character?.Speak(TextManager.GetWithVariable("DialogFireTurret", "[itemname]", item.Name, true), null, 0.0f, "fireturret", 5.0f); character.SetInput(InputType.Shoot, true, true); } return(false); }
private void CheckActiveObjectives(TutorialSegment objective, float deltaTime) { switch (objective.Id) { case "ReactorCommand": // Reactor commanded if (!IsReactorPoweredUp()) { if (!HasOrder("operatereactor")) { return; } } break; case "NavConsole": // traveled 50 meters if (Vector2.Distance(subStartingPosition, Submarine.MainSub.WorldPosition) < 4000f) { return; } break; case "Flood": // Hull breaches repaired if (IsFlooding()) { return; } break; case "Medical": if (injuredMember != null && !injuredMember.IsDead) { if (injuredMember.CharacterHealth.DroppedItem == null) { return; } } break; case "EnemyOnSonar": // Enemy dispatched if (HasEnemyOnSonarForDuration(deltaTime)) { return; } break; case "Degrading": // Fixed if (mechanic != null && !mechanic.IsDead) { HumanAIController humanAI = mechanic.AIController as HumanAIController; if (mechanic.CurrentOrder?.AITag != "repairsystems" || humanAI.CurrentOrderOption != "jobspecific") { return; } } if (engineer != null && !engineer.IsDead) { HumanAIController humanAI = engineer.AIController as HumanAIController; if (engineer.CurrentOrder?.AITag != "repairsystems" || humanAI.CurrentOrderOption != "jobspecific") { return; } } break; case "Approach1": // Wait until docked if (!Submarine.MainSub.AtEndPosition || Submarine.MainSub.DockedTo.Count == 0) { return; } break; } RemoveCompletedObjective(objective); }
public static void ServerRead(IReadMessage msg, Client c) { c.KickAFKTimer = 0.0f; UInt16 ID = msg.ReadUInt16(); ChatMessageType type = (ChatMessageType)msg.ReadByte(); string txt; Character orderTargetCharacter = null; Entity orderTargetEntity = null; OrderChatMessage orderMsg = null; OrderTarget orderTargetPosition = null; if (type == ChatMessageType.Order) { int orderIndex = msg.ReadByte(); orderTargetCharacter = Entity.FindEntityByID(msg.ReadUInt16()) as Character; orderTargetEntity = Entity.FindEntityByID(msg.ReadUInt16()) as Entity; int orderOptionIndex = msg.ReadByte(); if (msg.ReadBoolean()) { var x = msg.ReadSingle(); var y = msg.ReadSingle(); var hull = Entity.FindEntityByID(msg.ReadUInt16()) as Hull; orderTargetPosition = new OrderTarget(new Vector2(x, y), hull, true); } if (orderIndex < 0 || orderIndex >= Order.PrefabList.Count) { DebugConsole.ThrowError($"Invalid order message from client \"{c.Name}\" - order index out of bounds ({orderIndex}, {orderOptionIndex})."); if (NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID)) { c.LastSentChatMsgID = ID; } return; } Order order = Order.PrefabList[orderIndex]; string orderOption = orderOptionIndex < 0 || orderOptionIndex >= order.Options.Length ? "" : order.Options[orderOptionIndex]; orderMsg = new OrderChatMessage(order, orderOption, orderTargetPosition ?? orderTargetEntity as ISpatialEntity, orderTargetCharacter, c.Character); txt = orderMsg.Text; } else { txt = msg.ReadString() ?? ""; } if (!NetIdUtils.IdMoreRecent(ID, c.LastSentChatMsgID)) { return; } c.LastSentChatMsgID = ID; if (txt.Length > MaxLength) { txt = txt.Substring(0, MaxLength); } c.LastSentChatMessages.Add(txt); if (c.LastSentChatMessages.Count > 10) { c.LastSentChatMessages.RemoveRange(0, c.LastSentChatMessages.Count - 10); } float similarity = 0.0f; for (int i = 0; i < c.LastSentChatMessages.Count; i++) { float closeFactor = 1.0f / (c.LastSentChatMessages.Count - i); if (string.IsNullOrEmpty(txt)) { similarity += closeFactor; } else { int levenshteinDist = ToolBox.LevenshteinDistance(txt, c.LastSentChatMessages[i]); similarity += Math.Max((txt.Length - levenshteinDist) / (float)txt.Length * closeFactor, 0.0f); } } //order/report messages can be sent a little faster than normal messages without triggering the spam filter if (orderMsg != null) { similarity *= 0.25f; } bool isOwner = GameMain.Server.OwnerConnection != null && c.Connection == GameMain.Server.OwnerConnection; if (similarity + c.ChatSpamSpeed > 5.0f && !isOwner) { GameMain.Server.KarmaManager.OnSpamFilterTriggered(c); c.ChatSpamCount++; if (c.ChatSpamCount > 3) { //kick for spamming too much GameMain.Server.KickClient(c, TextManager.Get("SpamFilterKicked")); } else { ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null); c.ChatSpamTimer = 10.0f; GameMain.Server.SendDirectChatMessage(denyMsg, c); } return; } c.ChatSpamSpeed += similarity + 0.5f; if (c.ChatSpamTimer > 0.0f && !isOwner) { ChatMessage denyMsg = Create("", TextManager.Get("SpamFilterBlocked"), ChatMessageType.Server, null); c.ChatSpamTimer = 10.0f; GameMain.Server.SendDirectChatMessage(denyMsg, c); return; } if (type == ChatMessageType.Order) { if (c.Character == null || c.Character.SpeechImpediment >= 100.0f || c.Character.IsDead) { return; } if (orderMsg.Order.TargetAllCharacters) { HumanAIController.ReportProblem(orderMsg.Sender, orderMsg.Order); } else if (orderTargetCharacter != null) { var order = orderTargetPosition == null ? new Order(orderMsg.Order.Prefab, orderTargetEntity, orderMsg.Order.Prefab?.GetTargetItemComponent(orderTargetEntity as Item), orderMsg.Sender) : new Order(orderMsg.Order.Prefab, orderTargetPosition, orderMsg.Sender); orderTargetCharacter.SetOrder(order, orderMsg.OrderOption, orderMsg.Sender); } GameMain.Server.SendOrderChatMessage(orderMsg); } else { GameMain.Server.SendChatMessage(txt, null, c); } }