public override void OnItemLoaded() { base.OnItemLoaded(); var containers = item.GetComponents <ItemContainer>().ToList(); if (containers.Count < 2) { DebugConsole.ThrowError("Error in item \"" + item.Name + "\": Fabricators must have two ItemContainer components!"); return; } inputContainer = containers[0]; outputContainer = containers[1]; foreach (var recipe in fabricationRecipes) { if (recipe.RequiredItems.Count > inputContainer.Capacity) { DebugConsole.ThrowError("Error in item \"" + item.Name + "\": There's not enough room in the input inventory for the ingredients of \"" + recipe.TargetItem.Name + "\"!"); } } OnItemLoadedProjSpecific(); }
private bool CanBeFabricated(FabricableItem fabricableItem, Character user) { if (fabricableItem == null) { return(false); } if (user != null && fabricableItem.RequiredSkills.Any(skill => user.GetSkillLevel(skill.Name) < skill.Level)) { return(false); } ItemContainer container = item.GetComponent <ItemContainer>(); foreach (Tuple <ItemPrefab, int, float, bool> ip in fabricableItem.RequiredItems) { if (Array.FindAll(container.Inventory.Items, it => it != null && it.Prefab == ip.Item1 && it.Condition >= ip.Item1.Health * ip.Item3).Length < ip.Item2) { return(false); } } return(true); }
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); }
private bool TryLaunch(float deltaTime, Character character = null, bool ignorePower = false) { if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return(false); } if (reload > 0.0f) { return(false); } if (MaxActiveProjectiles >= 0) { activeProjectiles.RemoveAll(it => it.Removed); if (activeProjectiles.Count >= MaxActiveProjectiles) { return(false); } } if (!ignorePower) { if (GetAvailableBatteryPower() < powerConsumption) { #if CLIENT if (!flashLowPower && character != null && character == Character.Controlled) { flashLowPower = true; GUI.PlayUISound(GUISoundType.PickItemFail); } #endif return(false); } } Projectile launchedProjectile = null; for (int i = 0; i < ProjectileCount; i++) { foreach (MapEntity e in item.linkedTo) { //use linked projectile containers in case they have to react to the turret being launched somehow //(play a sound, spawn more projectiles) if (!(e is Item linkedItem)) { continue; } ItemContainer projectileContainer = linkedItem.GetComponent <ItemContainer>(); if (projectileContainer != null) { linkedItem.Use(deltaTime, null); var repairable = linkedItem.GetComponent <Repairable>(); if (repairable != null && failedLaunchAttempts < 2) { repairable.LastActiveTime = (float)Timing.TotalTime + 1.0f; } } } var projectiles = GetLoadedProjectiles(true); if (projectiles.Count == 0 && !LaunchWithoutProjectile) { //coilguns spawns ammo in the ammo boxes with the OnUse statuseffect when the turret is launched, //causing a one frame delay before the gun can be launched (or more in multiplayer where there may be a longer delay) // -> attempt to launch the gun multiple times before showing the "no ammo" flash failedLaunchAttempts++; #if CLIENT if (!flashNoAmmo && character != null && character == Character.Controlled && failedLaunchAttempts > 20) { flashNoAmmo = true; failedLaunchAttempts = 0; GUI.PlayUISound(GUISoundType.PickItemFail); } #endif return(false); } failedLaunchAttempts = 0; launchedProjectile = projectiles.FirstOrDefault(); if (!ignorePower) { var batteries = item.GetConnectedComponents <PowerContainer>(); float neededPower = powerConsumption; while (neededPower > 0.0001f && batteries.Count > 0) { batteries.RemoveAll(b => b.Charge <= 0.0001f || b.MaxOutPut <= 0.0001f); float takePower = neededPower / batteries.Count; takePower = Math.Min(takePower, batteries.Min(b => Math.Min(b.Charge * 3600.0f, b.MaxOutPut))); foreach (PowerContainer battery in batteries) { neededPower -= takePower; battery.Charge -= takePower / 3600.0f; #if SERVER battery.Item.CreateServerEvent(battery); #endif } } } if (launchedProjectile != null || LaunchWithoutProjectile) { Launch(launchedProjectile?.Item, character); } } #if SERVER if (character != null && launchedProjectile != null) { string msg = GameServer.CharacterLogName(character) + " launched " + item.Name + " (projectile: " + launchedProjectile.Item.Name; var containedItems = launchedProjectile.Item.ContainedItems; if (containedItems == null || !containedItems.Any()) { msg += ")"; } else { msg += ", contained items: " + string.Join(", ", containedItems.Select(i => i.Name)) + ")"; } GameServer.Log(msg, ServerLog.MessageType.ItemInteraction); } #endif return(true); }
/// <summary> /// Returns true when done seeking the suitable container. /// </summary> protected bool AIDecontainEmptyItems(Character character, AIObjective objective, bool equip, ItemContainer sourceContainer = null) { if (character.AIController is HumanAIController aiController) { ItemContainer sourceC = sourceContainer ?? (item.OwnInventory?.Owner is Item it ? it.GetComponent <ItemContainer>() : null); var containedItems = sourceContainer != null ? sourceContainer.Inventory.Items : item.OwnInventory.Items; foreach (Item containedItem in containedItems) { if (containedItem != null && containedItem.Condition <= 0.0f) { if (FindSuitableContainer(character, i => { var container = i.GetComponent <ItemContainer>(); if (container == null) { return(0); } if (container.Inventory.IsFull()) { return(0); } // Ignore containers that are identical to the source container if (sourceC != null && container.Item.Prefab == sourceC.Item.Prefab) { return(0); } if (container.ShouldBeContained(containedItem, out bool isRestrictionsDefined)) { if (isRestrictionsDefined) { return(4); } else { if (containedItem.Prefab.IsContainerPreferred(container, out bool isPreferencesDefined, out bool isSecondary)) { return(isPreferencesDefined ? isSecondary ? 2 : 3 : 1); } else { return(isPreferencesDefined ? 0 : 1); } } } else { return(0); } }, out Item targetContainer))
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective) { var projectiles = GetLoadedProjectiles(); if (projectiles.Count == 0 || (projectiles.Count == 1 && objective.Option.ToLowerInvariant() != "fire at will")) { ItemContainer container = null; foreach (MapEntity e in item.linkedTo) { var 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); } var containShellObjective = new AIObjectiveContainItem(character, container.ContainableItems[0].Names[0], container); containShellObjective.IgnoreAlreadyContainedItems = true; objective.AddSubObjective(containShellObjective); return(false); } else 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, "", false)); return(false); } } //enough shells and power Character closestEnemy = null; float closestDist = 3000.0f; foreach (Character enemy in Character.CharacterList) { //ignore humans and characters that are inside the sub if (enemy.IsDead || enemy.SpeciesName == "human" || enemy.AnimController.CurrentHull != null) { continue; } float dist = Vector2.Distance(enemy.WorldPosition, item.WorldPosition); if (dist < closestDist) { closestEnemy = enemy; closestDist = dist; } } if (closestEnemy == null) { return(false); } character.CursorPosition = closestEnemy.WorldPosition; if (item.Submarine != null) { character.CursorPosition -= item.Submarine.Position; } character.SetInput(InputType.Aim, false, true); float enemyAngle = MathUtils.VectorToAngle(closestEnemy.WorldPosition - item.WorldPosition); float turretAngle = -rotation; if (Math.Abs(MathUtils.GetShortestAngle(enemyAngle, turretAngle)) > 0.01f) { return(false); } var pickedBody = Submarine.PickBody(ConvertUnits.ToSimUnits(item.WorldPosition), closestEnemy.SimPosition, null); if (pickedBody != null && !(pickedBody.UserData is Limb)) { return(false); } if (objective.Option.ToLowerInvariant() == "fire at will") { Use(deltaTime, character); } return(false); }
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 bool TryLaunch(float deltaTime, Character character = null) { #if CLIENT if (GameMain.Client != null) { return(false); } #endif if (reload > 0.0f) { return(false); } if (GetAvailablePower() < powerConsumption) { #if CLIENT if (!flashLowPower && character != null && character == Character.Controlled) { flashLowPower = true; GUI.PlayUISound(GUISoundType.PickItemFail); } #endif return(false); } foreach (MapEntity e in item.linkedTo) { //use linked projectile containers in case they have to react to the turret being launched somehow //(play a sound, spawn more projectiles) if (!(e is Item linkedItem)) { continue; } ItemContainer projectileContainer = linkedItem.GetComponent <ItemContainer>(); if (projectileContainer != null) { linkedItem.Use(deltaTime, null); var repairable = linkedItem.GetComponent <Repairable>(); if (repairable != null) { repairable.LastActiveTime = (float)Timing.TotalTime + 1.0f; } } } var projectiles = GetLoadedProjectiles(true); if (projectiles.Count == 0) { //coilguns spawns ammo in the ammo boxes with the OnUse statuseffect when the turret is launched, //causing a one frame delay before the gun can be launched (or more in multiplayer where there may be a longer delay) // -> attempt to launch the gun multiple times before showing the "no ammo" flash failedLaunchAttempts++; #if CLIENT if (!flashNoAmmo && character != null && character == Character.Controlled && failedLaunchAttempts > 20) { flashNoAmmo = true; failedLaunchAttempts = 0; GUI.PlayUISound(GUISoundType.PickItemFail); } #endif return(false); } failedLaunchAttempts = 0; var batteries = item.GetConnectedComponents <PowerContainer>(); float availablePower = 0.0f; foreach (PowerContainer battery in batteries) { float batteryPower = Math.Min(battery.Charge * 3600.0f, battery.MaxOutPut); float takePower = Math.Min(powerConsumption - availablePower, batteryPower); battery.Charge -= takePower / 3600.0f; #if SERVER if (GameMain.Server != null) { battery.Item.CreateServerEvent(battery); } #endif } Launch(projectiles[0].Item, character); #if SERVER if (character != null) { string msg = character.LogName + " launched " + item.Name + " (projectile: " + projectiles[0].Item.Name; var containedItems = projectiles[0].Item.ContainedItems; if (containedItems == null || !containedItems.Any()) { msg += ")"; } else { msg += ", contained items: " + string.Join(", ", containedItems.Select(i => i.Name)) + ")"; } GameServer.Log(msg, ServerLog.MessageType.ItemInteraction); } #endif return(true); }
public override void OnItemLoaded() { liquidContainer = item.GetComponent <ItemContainer>(); }