Unstable 0.1500.0.0
This commit is contained in:
@@ -204,8 +204,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
target.InitializeLinks();
|
||||
|
||||
if (!item.linkedTo.Contains(target.item)) item.linkedTo.Add(target.item);
|
||||
if (!target.item.linkedTo.Contains(item)) target.item.linkedTo.Add(item);
|
||||
if (!item.linkedTo.Contains(target.item)) { item.linkedTo.Add(target.item); }
|
||||
if (!target.item.linkedTo.Contains(item)) { target.item.linkedTo.Add(item); }
|
||||
|
||||
if (!target.item.Submarine.DockedTo.Contains(item.Submarine)) target.item.Submarine.ConnectedDockingPorts.Add(item.Submarine, target);
|
||||
if (!item.Submarine.DockedTo.Contains(target.item.Submarine)) item.Submarine.ConnectedDockingPorts.Add(target.item.Submarine, this);
|
||||
@@ -291,7 +291,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
|
||||
List<MapEntity> removedEntities = item.linkedTo.Where(e => e.Removed).ToList();
|
||||
foreach (MapEntity removed in removedEntities) item.linkedTo.Remove(removed);
|
||||
foreach (MapEntity removed in removedEntities) { item.linkedTo.Remove(removed); }
|
||||
|
||||
if (!item.linkedTo.Any(e => e is Hull) && !DockingTarget.item.linkedTo.Any(e => e is Hull))
|
||||
{
|
||||
@@ -306,9 +306,8 @@ namespace Barotrauma.Items.Components
|
||||
if (myWayPoint != null && targetWayPoint != null)
|
||||
{
|
||||
myWayPoint.FindHull();
|
||||
myWayPoint.linkedTo.Add(targetWayPoint);
|
||||
targetWayPoint.FindHull();
|
||||
targetWayPoint.linkedTo.Add(myWayPoint);
|
||||
myWayPoint.ConnectTo(targetWayPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -597,8 +596,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
hullRects[i].X -= expand;
|
||||
hullRects[i].Width += expand * 2;
|
||||
hullRects[i].Location -= MathUtils.ToPoint((subs[i].WorldPosition - subs[i].HiddenSubPosition));
|
||||
hullRects[i].Location -= MathUtils.ToPoint(subs[i].WorldPosition - subs[i].HiddenSubPosition);
|
||||
hulls[i] = new Hull(MapEntityPrefab.Find(null, "hull"), hullRects[i], subs[i]);
|
||||
hulls[i].RoomName = IsHorizontal ? "entityname.dockingport" : "entityname.dockinghatch";
|
||||
hulls[i].AddToGrid(subs[i]);
|
||||
hulls[i].FreeID();
|
||||
|
||||
@@ -716,8 +716,9 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
hullRects[i].Y += expand;
|
||||
hullRects[i].Height += expand * 2;
|
||||
hullRects[i].Location -= MathUtils.ToPoint((subs[i].WorldPosition - subs[i].HiddenSubPosition));
|
||||
hullRects[i].Location -= MathUtils.ToPoint(subs[i].WorldPosition - subs[i].HiddenSubPosition);
|
||||
hulls[i] = new Hull(MapEntityPrefab.Find(null, "hull"), hullRects[i], subs[i]);
|
||||
hulls[i].RoomName = IsHorizontal ? "entityname.dockingport" : "entityname.dockinghatch";
|
||||
hulls[i].AddToGrid(subs[i]);
|
||||
hulls[i].FreeID();
|
||||
|
||||
@@ -873,8 +874,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
myWayPoint.FindHull();
|
||||
myWayPoint.linkedTo.Remove(targetWayPoint);
|
||||
myWayPoint.OnLinksChanged?.Invoke(myWayPoint);
|
||||
targetWayPoint.FindHull();
|
||||
targetWayPoint.linkedTo.Remove(myWayPoint);
|
||||
targetWayPoint.OnLinksChanged?.Invoke(targetWayPoint);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1058,7 +1061,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (!item.linkedTo.Any()) return;
|
||||
if (!item.linkedTo.Any()) { return; }
|
||||
|
||||
List<MapEntity> linked = new List<MapEntity>(item.linkedTo);
|
||||
foreach (MapEntity entity in linked)
|
||||
|
||||
@@ -475,6 +475,21 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(Signal signal, Connection connection)
|
||||
{
|
||||
switch (connection.Name)
|
||||
{
|
||||
case "activate":
|
||||
case "use":
|
||||
case "trigger_in":
|
||||
if (signal.value != "0")
|
||||
{
|
||||
item.Use(1.0f, null);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private bool attachable, attached, attachedByDefault;
|
||||
private Voronoi2.VoronoiCell attachTargetCell;
|
||||
private readonly PhysicsBody body;
|
||||
private PhysicsBody body;
|
||||
public PhysicsBody Pusher
|
||||
{
|
||||
get;
|
||||
@@ -780,7 +780,19 @@ namespace Barotrauma.Items.Components
|
||||
DeattachFromWall();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
attachTargetCell = null;
|
||||
if (Pusher != null)
|
||||
{
|
||||
GameMain.World.Remove(Pusher.FarseerBody);
|
||||
Pusher = null;
|
||||
}
|
||||
body = null;
|
||||
}
|
||||
|
||||
public override XElement Save(XElement parentElement)
|
||||
{
|
||||
if (!attachable)
|
||||
|
||||
@@ -1,11 +1,29 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class IdCard : Pickable
|
||||
{
|
||||
[Serialize(CharacterTeamType.None, true, alwaysUseInstanceValues: true)]
|
||||
public CharacterTeamType TeamID
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0, true, alwaysUseInstanceValues: true)]
|
||||
public int SubmarineSpecificID
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private JobPrefab cachedJobPrefab;
|
||||
private string cachedName;
|
||||
|
||||
public IdCard(Item item, XElement element) : base(item, element)
|
||||
{
|
||||
|
||||
@@ -20,6 +38,8 @@ namespace Barotrauma.Items.Components
|
||||
item.AddTag("jobid:" + info.Job.Prefab.Identifier);
|
||||
}
|
||||
|
||||
TeamID = info.TeamID;
|
||||
|
||||
var head = info.Head;
|
||||
|
||||
if (info != null && head != null)
|
||||
@@ -50,5 +70,48 @@ namespace Barotrauma.Items.Components
|
||||
base.Unequip(character);
|
||||
character.Info?.CheckDisguiseStatus(true, this);
|
||||
}
|
||||
|
||||
public JobPrefab GetJob()
|
||||
{
|
||||
if (cachedJobPrefab != null)
|
||||
{
|
||||
return cachedJobPrefab;
|
||||
}
|
||||
|
||||
foreach (string tag in item.GetTags())
|
||||
{
|
||||
if (tag.StartsWith("jobid:"))
|
||||
{
|
||||
string jobIdentifier = tag.Split(':').Last();
|
||||
if (JobPrefab.Get(jobIdentifier) is { } jobPrefab)
|
||||
{
|
||||
cachedJobPrefab = jobPrefab;
|
||||
return jobPrefab;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public string GetName()
|
||||
{
|
||||
if (cachedName != null)
|
||||
{
|
||||
return cachedName;
|
||||
}
|
||||
|
||||
foreach (string tag in item.GetTags())
|
||||
{
|
||||
if (tag.StartsWith("name:"))
|
||||
{
|
||||
string ownerName = tag.Split(':').Last();
|
||||
cachedName = ownerName;
|
||||
return ownerName;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("attack", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
Attack = new Attack(subElement, item.Name + ", MeleeWeapon");
|
||||
Attack = new Attack(subElement, item.Name + ", MeleeWeapon", item);
|
||||
Attack.DamageRange = item.body == null ? 10.0f : ConvertUnits.ToDisplayUnits(item.body.GetMaxExtent());
|
||||
}
|
||||
item.IsShootable = true;
|
||||
@@ -95,7 +95,7 @@ namespace Barotrauma.Items.Components
|
||||
if (hitPos < MathHelper.PiOver4) { return false; }
|
||||
|
||||
ActivateNearbySleepingCharacters();
|
||||
reloadTimer = reload;
|
||||
reloadTimer = reload / (1 + character.GetStatValue(StatTypes.MeleeAttackSpeed));
|
||||
|
||||
item.body.FarseerBody.CollisionCategories = Physics.CollisionProjectile;
|
||||
item.body.FarseerBody.CollidesWith = Physics.CollisionCharacter | Physics.CollisionWall;
|
||||
|
||||
@@ -5,6 +5,7 @@ using FarseerPhysics.Dynamics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -52,6 +53,21 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0f, true, description: "The time required for a charge-type turret to charge up before able to fire.")]
|
||||
public float MaxChargeTime
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private enum ChargingState
|
||||
{
|
||||
Inactive,
|
||||
WindingUp,
|
||||
WindingDown,
|
||||
}
|
||||
private ChargingState currentChargingState;
|
||||
|
||||
public Vector2 TransformedBarrelPos
|
||||
{
|
||||
get
|
||||
@@ -62,7 +78,10 @@ namespace Barotrauma.Items.Components
|
||||
return Vector2.Transform(flippedPos, bodyTransform);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private float currentChargeTime;
|
||||
private bool tryingToCharge;
|
||||
|
||||
public RangedWeapon(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -88,10 +107,41 @@ namespace Barotrauma.Items.Components
|
||||
if (ReloadTimer < 0.0f)
|
||||
{
|
||||
ReloadTimer = 0.0f;
|
||||
IsActive = false;
|
||||
// was this an optimization or related to something else? currently disabled for charge-type weapons
|
||||
//IsActive = false;
|
||||
if (MaxChargeTime == 0.0f)
|
||||
{
|
||||
IsActive = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
float previousChargeTime = currentChargeTime;
|
||||
|
||||
float chargeDeltaTime = tryingToCharge ? deltaTime : -deltaTime;
|
||||
currentChargeTime = Math.Clamp(currentChargeTime + chargeDeltaTime, 0f, MaxChargeTime);
|
||||
|
||||
tryingToCharge = false;
|
||||
|
||||
if (currentChargeTime == 0f)
|
||||
{
|
||||
currentChargingState = ChargingState.Inactive;
|
||||
}
|
||||
else if (currentChargeTime < previousChargeTime)
|
||||
{
|
||||
currentChargingState = ChargingState.WindingDown;
|
||||
}
|
||||
else
|
||||
{
|
||||
// if we are charging up or at maxed charge, remain winding up
|
||||
currentChargingState = ChargingState.WindingUp;
|
||||
}
|
||||
|
||||
UpdateProjSpecific(deltaTime);
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
|
||||
private float GetSpread(Character user)
|
||||
{
|
||||
float degreeOfFailure = 1.0f - DegreeOfSuccess(user);
|
||||
@@ -102,11 +152,16 @@ namespace Barotrauma.Items.Components
|
||||
private readonly List<Body> limbBodies = new List<Body>();
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
tryingToCharge = true;
|
||||
if (character == null || character.Removed) { return false; }
|
||||
if ((item.RequireAimToUse && !character.IsKeyDown(InputType.Aim)) || ReloadTimer > 0.0f) { return false; }
|
||||
if (currentChargeTime < MaxChargeTime) { return false; }
|
||||
|
||||
IsActive = true;
|
||||
ReloadTimer = reload;
|
||||
ReloadTimer = reload / (1 + character.GetStatValue(StatTypes.RangedAttackSpeed));
|
||||
currentChargeTime = 0f;
|
||||
|
||||
character.CheckTalents(AbilityEffectType.OnUseRangedWeapon, item);
|
||||
|
||||
if (item.AiTarget != null)
|
||||
{
|
||||
|
||||
@@ -711,7 +711,27 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
bool CheckItems(RelatedItem relatedItem, IEnumerable<Item> itemList)
|
||||
{
|
||||
bool Predicate(Item it) => it != null && it.Condition > 0.0f && relatedItem.MatchesItem(it);
|
||||
bool Predicate(Item it)
|
||||
{
|
||||
if (it == null || it.Condition <= 0.0f || !relatedItem.MatchesItem(it)) { return false; }
|
||||
if (item.Submarine != null)
|
||||
{
|
||||
var idCard = it.GetComponent<IdCard>();
|
||||
if (idCard != null)
|
||||
{
|
||||
//id cards don't work in enemy subs (except on items that only require the default "idcard" tag)
|
||||
if (idCard.TeamID != CharacterTeamType.None && idCard.TeamID != item.Submarine.TeamID && relatedItem.Identifiers.Any(id => id != "idcard"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (idCard.SubmarineSpecificID != 0 && item.Submarine.SubmarineSpecificIDTag != idCard.SubmarineSpecificID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
bool shouldBreak = false;
|
||||
bool inEditor = false;
|
||||
#if CLIENT
|
||||
|
||||
@@ -140,6 +140,20 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false, description: "Should the items be injected into the user.")]
|
||||
public bool AutoInject
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(0.5f, false, description: "The rotation in which the contained sprites are drawn (in degrees).")]
|
||||
public float AutoInjectThreshold
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, false)]
|
||||
public bool RemoveContainedItemsOnDeconstruct { get; set; }
|
||||
|
||||
@@ -237,9 +251,23 @@ namespace Barotrauma.Items.Components
|
||||
SpawnAlwaysContainedItems();
|
||||
}
|
||||
|
||||
if (item.ParentInventory is CharacterInventory)
|
||||
if (item.ParentInventory is CharacterInventory ownerInventory)
|
||||
{
|
||||
item.SetContainedItemPositions();
|
||||
|
||||
if (AutoInject)
|
||||
{
|
||||
if (ownerInventory?.Owner is Character ownerCharacter &&
|
||||
ownerCharacter.HealthPercentage / 100f <= AutoInjectThreshold &&
|
||||
ownerCharacter.HasEquippedItem(item))
|
||||
{
|
||||
foreach (Item item in Inventory.AllItemsMod)
|
||||
{
|
||||
item.ApplyStatusEffects(ActionType.OnUse, 1.0f, ownerCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else if (item.body != null &&
|
||||
item.body.Enabled &&
|
||||
@@ -256,6 +284,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (var activeContainedItem in activeContainedItems)
|
||||
{
|
||||
Item contained = activeContainedItem.Item;
|
||||
|
||||
if (activeContainedItem.ExcludeBroken && contained.Condition <= 0.0f) { continue; }
|
||||
StatusEffect effect = activeContainedItem.StatusEffect;
|
||||
|
||||
@@ -299,6 +328,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
character.CheckTalents(AbilityEffectType.OnOpenItemContainer, item);
|
||||
|
||||
return base.Select(character);
|
||||
}
|
||||
|
||||
|
||||
@@ -122,6 +122,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
float voltageFactor = MinVoltage <= 0.0f ? 1.0f : Math.Min(Voltage, 1.0f);
|
||||
Vector2 currForce = new Vector2(force * maxForce * forceMultiplier * voltageFactor, 0.0f);
|
||||
|
||||
if (item.GetComponent<Repairable>()?.IsTinkering ?? false)
|
||||
{
|
||||
currForce *= 2.5f;
|
||||
}
|
||||
|
||||
//less effective when in a bad condition
|
||||
currForce *= MathHelper.Lerp(0.5f, 2.0f, item.Condition / item.MaxCondition);
|
||||
if (item.Submarine.FlippedX) { currForce *= -1; }
|
||||
|
||||
@@ -4,8 +4,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Abilities;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -24,6 +24,12 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private Character user;
|
||||
|
||||
public float FabricationSpeedMultiplier
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
private ItemContainer inputContainer, outputContainer;
|
||||
|
||||
[Serialize(1.0f, true)]
|
||||
@@ -240,8 +246,10 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (fabricatedItem == null || !CanBeFabricated(fabricatedItem))
|
||||
var availableIngredients = GetAvailableIngredients();
|
||||
if (fabricatedItem == null || !CanBeFabricated(fabricatedItem, availableIngredients, user))
|
||||
{
|
||||
FabricationSpeedMultiplier = 1f;
|
||||
CancelFabricating();
|
||||
return;
|
||||
}
|
||||
@@ -278,38 +286,56 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (powerConsumption <= 0) { Voltage = 1.0f; }
|
||||
|
||||
timeUntilReady -= deltaTime * Math.Min(Voltage, 1.0f);
|
||||
timeUntilReady -= deltaTime * Math.Min(Voltage, 1.0f) * FabricationSpeedMultiplier;
|
||||
FabricationSpeedMultiplier = 1f;
|
||||
|
||||
UpdateRequiredTimeProjSpecific();
|
||||
|
||||
if (timeUntilReady > 0.0f) { return; }
|
||||
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
var availableIngredients = GetAvailableIngredients();
|
||||
foreach (FabricationRecipe.RequiredItem ingredient in fabricatedItem.RequiredItems)
|
||||
{
|
||||
for (int i = 0; i < ingredient.Amount; i++)
|
||||
fabricatedItem.RequiredItems.ForEach(requiredItem => {
|
||||
for (int usedPrefabsAmount = 0; usedPrefabsAmount < requiredItem.Amount; usedPrefabsAmount++)
|
||||
{
|
||||
var availableItem = availableIngredients.FirstOrDefault(it =>
|
||||
it != null && ingredient.ItemPrefabs.Contains(it.Prefab) &&
|
||||
it.ConditionPercentage >= ingredient.MinCondition * 100.0f &&
|
||||
it.ConditionPercentage <= ingredient.MaxCondition * 100.0f);
|
||||
if (availableItem == null) { continue; }
|
||||
|
||||
if (ingredient.UseCondition && availableItem.ConditionPercentage - ingredient.MinCondition * 100 > 0.0f) //Leave it behind with reduced condition if it has enough to stay above 0
|
||||
foreach (ItemPrefab requiredPrefab in requiredItem.ItemPrefabs)
|
||||
{
|
||||
availableItem.Condition -= availableItem.Prefab.Health * ingredient.MinCondition;
|
||||
continue;
|
||||
if (!availableIngredients.ContainsKey(requiredPrefab.Identifier)) { continue; }
|
||||
|
||||
var availablePrefabs = availableIngredients[requiredPrefab.Identifier];
|
||||
var availablePrefab = availablePrefabs.FirstOrDefault(potentialPrefab =>
|
||||
{
|
||||
return potentialPrefab.ConditionPercentage >= requiredItem.MinCondition * 100.0f &&
|
||||
potentialPrefab.ConditionPercentage <= requiredItem.MaxCondition * 100.0f;
|
||||
});
|
||||
|
||||
if (availablePrefab == null) { continue; }
|
||||
|
||||
if (requiredItem.UseCondition && availablePrefab.ConditionPercentage - requiredItem.MinCondition * 100 > 0.0f) //Leave it behind with reduced condition if it has enough to stay above 0
|
||||
{
|
||||
availablePrefab.Condition -= availablePrefab.Prefab.Health * requiredItem.MinCondition;
|
||||
continue;
|
||||
}
|
||||
|
||||
availablePrefabs.Remove(availablePrefab);
|
||||
Entity.Spawner.AddToRemoveQueue(availablePrefab);
|
||||
inputContainer.Inventory.RemoveItem(availablePrefab);
|
||||
}
|
||||
availableIngredients.Remove(availableItem);
|
||||
Entity.Spawner.AddToRemoveQueue(availableItem);
|
||||
inputContainer.Inventory.RemoveItem(availableItem);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Character tempUser = user;
|
||||
|
||||
int amountFittingContainer = outputContainer.Inventory.HowManyCanBePut(fabricatedItem.TargetItem, fabricatedItem.OutCondition * fabricatedItem.TargetItem.Health);
|
||||
for (int i = 0; i < fabricatedItem.Amount; i++)
|
||||
var itemsCreated = new AbilityValue(fabricatedItem.Amount);
|
||||
foreach (Character character in Character.CharacterList.Where(c => c.TeamID == user.TeamID))
|
||||
{
|
||||
character.CheckTalents(AbilityEffectType.OnAllyItemFabricatedAmount, (fabricatedItem.TargetItem, itemsCreated));
|
||||
}
|
||||
|
||||
tempUser.CheckTalents(AbilityEffectType.OnItemFabricatedAmount, (fabricatedItem.TargetItem, itemsCreated));
|
||||
|
||||
for (int i = 0; i < (int)itemsCreated.Value; i++)
|
||||
{
|
||||
if (i < amountFittingContainer)
|
||||
{
|
||||
@@ -339,9 +365,13 @@ namespace Barotrauma.Items.Components
|
||||
foreach (Skill skill in fabricatedItem.RequiredSkills)
|
||||
{
|
||||
float userSkill = user.GetSkillLevel(skill.Identifier);
|
||||
float addedSkill = skill.Level * SkillSettings.Current.SkillIncreasePerFabricatorRequiredSkill / Math.Max(userSkill, 1.0f);
|
||||
var addedSkillValue = new AbilityValue(0f);
|
||||
user.CheckTalents(AbilityEffectType.OnItemFabricationSkillGain, addedSkillValue);
|
||||
|
||||
user.Info.IncreaseSkillLevel(
|
||||
skill.Identifier,
|
||||
skill.Level * SkillSettings.Current.SkillIncreasePerFabricatorRequiredSkill / Math.Max(userSkill, 1.0f),
|
||||
addedSkill + addedSkillValue.Value,
|
||||
user.Position + Vector2.UnitY * 150.0f);
|
||||
}
|
||||
}
|
||||
@@ -365,24 +395,36 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void UpdateRequiredTimeProjSpecific();
|
||||
|
||||
private bool CanBeFabricated(FabricationRecipe fabricableItem)
|
||||
private bool CanBeFabricated(FabricationRecipe fabricableItem, Dictionary<string, List<Item>> availableIngredients, Character character)
|
||||
{
|
||||
if (fabricableItem == null) { return false; }
|
||||
List<Item> availableIngredients = GetAvailableIngredients();
|
||||
return CanBeFabricated(fabricableItem, availableIngredients);
|
||||
}
|
||||
if (fabricableItem.RequiresRecipe && (character == null || !character.HasRecipeForItem(fabricableItem.TargetItem.Identifier))) { return false; }
|
||||
|
||||
private bool CanBeFabricated(FabricationRecipe fabricableItem, IEnumerable<Item> availableIngredients)
|
||||
{
|
||||
if (fabricableItem == null) { return false; }
|
||||
foreach (FabricationRecipe.RequiredItem requiredItem in fabricableItem.RequiredItems)
|
||||
return fabricableItem.RequiredItems.All(requiredItem =>
|
||||
{
|
||||
if (availableIngredients.Count(it => IsItemValidIngredient(it, requiredItem)) < requiredItem.Amount)
|
||||
int availablePrefabsAmount = 0;
|
||||
foreach (ItemPrefab requiredPrefab in requiredItem.ItemPrefabs)
|
||||
{
|
||||
return false;
|
||||
if (!availableIngredients.ContainsKey(requiredPrefab.Identifier)) { continue; }
|
||||
|
||||
var availablePrefabs = availableIngredients[requiredPrefab.Identifier];
|
||||
foreach (Item availablePrefab in availablePrefabs)
|
||||
{
|
||||
if (availablePrefab.Condition / availablePrefab.Prefab.Health >= requiredItem.MinCondition &&
|
||||
availablePrefab.Condition / availablePrefab.Prefab.Health <= requiredItem.MaxCondition)
|
||||
{
|
||||
availablePrefabsAmount++;
|
||||
}
|
||||
|
||||
if (availablePrefabsAmount >= requiredItem.Amount)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
private float GetRequiredTime(FabricationRecipe fabricableItem, Character user)
|
||||
@@ -416,7 +458,7 @@ namespace Barotrauma.Items.Components
|
||||
/// Get a list of all items available in the input container and linked containers
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private List<Item> GetAvailableIngredients()
|
||||
private Dictionary<string, List<Item>> GetAvailableIngredients()
|
||||
{
|
||||
List<Item> availableIngredients = new List<Item>();
|
||||
availableIngredients.AddRange(inputContainer.Inventory.AllItems);
|
||||
@@ -448,7 +490,19 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
#endif
|
||||
|
||||
return availableIngredients;
|
||||
Dictionary<string, List<Item>> ingredientsDictionary = new Dictionary<string, List<Item>>();
|
||||
for (int i = 0; i < availableIngredients.Count; i++)
|
||||
{
|
||||
var itemIdentifier = availableIngredients[i].prefab.Identifier;
|
||||
if (!ingredientsDictionary.ContainsKey(itemIdentifier))
|
||||
{
|
||||
ingredientsDictionary[itemIdentifier] = new List<Item>(availableIngredients.Count);
|
||||
}
|
||||
|
||||
ingredientsDictionary[itemIdentifier].Add(availableIngredients[i]);
|
||||
}
|
||||
|
||||
return ingredientsDictionary;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -463,40 +517,41 @@ namespace Barotrauma.Items.Components
|
||||
bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
|
||||
|
||||
var availableIngredients = GetAvailableIngredients();
|
||||
foreach (var requiredItem in targetItem.RequiredItems)
|
||||
{
|
||||
targetItem.RequiredItems.ForEach(requiredItem => {
|
||||
for (int i = 0; i < requiredItem.Amount; i++)
|
||||
{
|
||||
var matchingItem = availableIngredients.Find(it => !usedItems.Contains(it) && IsItemValidIngredient(it, requiredItem));
|
||||
if (matchingItem == null) { continue; }
|
||||
foreach (ItemPrefab requiredPrefab in requiredItem.ItemPrefabs)
|
||||
{
|
||||
if (!availableIngredients.ContainsKey(requiredPrefab.Identifier)) { continue; }
|
||||
|
||||
availableIngredients.Remove(matchingItem);
|
||||
|
||||
if (matchingItem.ParentInventory == inputContainer.Inventory)
|
||||
{
|
||||
//already in input container, all good
|
||||
usedItems.Add(matchingItem);
|
||||
}
|
||||
else //in another inventory, we need to move the item
|
||||
{
|
||||
if (!inputContainer.Inventory.CanBePut(matchingItem))
|
||||
var availablePrefabs = availableIngredients[requiredPrefab.Identifier];
|
||||
var availablePrefab = availablePrefabs.FirstOrDefault(potentialPrefab =>
|
||||
{
|
||||
var unneededItem = inputContainer.Inventory.AllItems.FirstOrDefault(it => !usedItems.Contains(it));
|
||||
unneededItem?.Drop(null, createNetworkEvent: !isClient);
|
||||
}
|
||||
inputContainer.Inventory.TryPutItem(matchingItem, user: null, createNetworkEvent: !isClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return !usedItems.Contains(potentialPrefab) &&
|
||||
potentialPrefab.ConditionPercentage >= requiredItem.MinCondition * 100.0f &&
|
||||
potentialPrefab.ConditionPercentage <= requiredItem.MaxCondition * 100.0f;
|
||||
});
|
||||
if (availablePrefab == null) { continue; }
|
||||
|
||||
private bool IsItemValidIngredient(Item item, FabricationRecipe.RequiredItem requiredItem)
|
||||
{
|
||||
return
|
||||
item != null &&
|
||||
requiredItem.ItemPrefabs.Contains(item.prefab) &&
|
||||
item.Condition / item.Prefab.Health >= requiredItem.MinCondition &&
|
||||
item.Condition / item.Prefab.Health <= requiredItem.MaxCondition;
|
||||
availablePrefabs.Remove(availablePrefab);
|
||||
|
||||
if (availablePrefab.ParentInventory == inputContainer.Inventory)
|
||||
{
|
||||
//already in input container, all good
|
||||
usedItems.Add(availablePrefab);
|
||||
}
|
||||
else //in another inventory, we need to move the item
|
||||
{
|
||||
if (!inputContainer.Inventory.CanBePut(availablePrefab))
|
||||
{
|
||||
var unneededItem = inputContainer.Inventory.AllItems.FirstOrDefault(it => !usedItems.Contains(it));
|
||||
unneededItem?.Drop(null, createNetworkEvent: !isClient);
|
||||
}
|
||||
inputContainer.Inventory.TryPutItem(availablePrefab, user: null, createNetworkEvent: !isClient);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public override XElement Save(XElement parentElement)
|
||||
|
||||
@@ -7,10 +7,15 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class MiniMap : Powered
|
||||
{
|
||||
class HullData
|
||||
internal class HullData
|
||||
{
|
||||
public float? Oxygen;
|
||||
public float? Water;
|
||||
public float? HullOxygenAmount,
|
||||
HullWaterAmount;
|
||||
|
||||
public float? ReceivedOxygenAmount,
|
||||
ReceivedWaterAmount;
|
||||
|
||||
public readonly HashSet<IdCard> Cards = new HashSet<IdCard>();
|
||||
|
||||
public bool Distort;
|
||||
public float DistortionTimer;
|
||||
@@ -45,17 +50,45 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Enable hull status mode.")]
|
||||
public bool EnableHullStatus
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Enable electrical view mode.")]
|
||||
public bool EnableElectricalView
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Enable hull condition mode.")]
|
||||
public bool EnableHullCondition
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
[Editable, Serialize(true, true, description: "Enable item finder mode.")]
|
||||
public bool EnableItemFinder
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public MiniMap(Item item, XElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
IsActive = true;
|
||||
hullDatas = new Dictionary<Hull, HullData>();
|
||||
InitProjSpecific(element);
|
||||
InitProjSpecific();
|
||||
}
|
||||
|
||||
partial void InitProjSpecific(XElement element);
|
||||
partial void InitProjSpecific();
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
//periodically reset all hull data
|
||||
//(so that outdated hull info won't be shown if detectors stop sending signals)
|
||||
@@ -65,13 +98,29 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!hullData.Distort)
|
||||
{
|
||||
hullData.Oxygen = null;
|
||||
hullData.Water = null;
|
||||
hullData.ReceivedOxygenAmount = null;
|
||||
hullData.ReceivedWaterAmount = null;
|
||||
}
|
||||
}
|
||||
resetDataTime = DateTime.Now + new TimeSpan(0, 0, 1);
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
if (cardRefreshTimer > cardRefreshDelay)
|
||||
{
|
||||
if (item.Submarine is { } sub)
|
||||
{
|
||||
UpdateIDCards(sub);
|
||||
}
|
||||
|
||||
cardRefreshTimer = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
cardRefreshTimer += deltaTime;
|
||||
}
|
||||
#endif
|
||||
|
||||
currPowerConsumption = powerConsumption;
|
||||
currPowerConsumption *= MathHelper.Lerp(1.5f, 1.0f, item.Condition / item.MaxCondition);
|
||||
|
||||
@@ -81,7 +130,7 @@ namespace Barotrauma.Items.Components
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
return picker != null;
|
||||
@@ -107,11 +156,11 @@ namespace Barotrauma.Items.Components
|
||||
//cheating a bit because water detectors don't actually send the water level
|
||||
if (source.GetComponent<WaterDetector>() == null)
|
||||
{
|
||||
hullData.Water = Rand.Range(0.0f, 1.0f);
|
||||
hullData.ReceivedWaterAmount = Rand.Range(0.0f, 1.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
hullData.Water = Math.Min(sourceHull.WaterVolume / sourceHull.Volume, 1.0f);
|
||||
hullData.ReceivedWaterAmount = Math.Min(sourceHull.WaterVolume / sourceHull.Volume, 1.0f);
|
||||
}
|
||||
break;
|
||||
case "oxygen_data_in":
|
||||
@@ -122,7 +171,7 @@ namespace Barotrauma.Items.Components
|
||||
oxy = Rand.Range(0.0f, 100.0f);
|
||||
}
|
||||
|
||||
hullData.Oxygen = oxy;
|
||||
hullData.ReceivedOxygenAmount = oxy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,6 +105,12 @@ namespace Barotrauma.Items.Components
|
||||
float powerFactor = Math.Min(currPowerConsumption <= 0.0f || MinVoltage <= 0.0f ? 1.0f : Voltage, 1.0f);
|
||||
|
||||
currFlow = flowPercentage / 100.0f * maxFlow * powerFactor;
|
||||
|
||||
if (item.GetComponent<Repairable>()?.IsTinkering ?? false)
|
||||
{
|
||||
currFlow *= 2.5f;
|
||||
}
|
||||
|
||||
//less effective when in a bad condition
|
||||
currFlow *= MathHelper.Lerp(0.5f, 1.0f, item.Condition / item.MaxCondition);
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
@@ -364,6 +364,19 @@ namespace Barotrauma.Items.Components
|
||||
float velY = MathHelper.Lerp((neutralBallastLevel * 100 - 50) * 2, -100 * Math.Sign(targetVelocity.Y), Math.Abs(targetVelocity.Y) / 100.0f);
|
||||
item.SendSignal(new Signal(velY.ToString(CultureInfo.InvariantCulture), sender: user), "velocity_y_out");
|
||||
|
||||
// converts the controlled sub's velocity to km/h and sends it.
|
||||
// TODO: add current_velocity_x and current_velocity_y pins on the navigation terminals and shuttle terminals
|
||||
// TODO: increase the size of the connection panels of both navigation terminals
|
||||
|
||||
if (controlledSub is { } sub)
|
||||
{
|
||||
item.SendSignal(new Signal((ConvertUnits.ToDisplayUnits(sub.Velocity.X * Physics.DisplayToRealWorldRatio) * 3.6f).ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_velocity_x");
|
||||
item.SendSignal(new Signal((ConvertUnits.ToDisplayUnits(sub.Velocity.Y * Physics.DisplayToRealWorldRatio) * -3.6f).ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_velocity_y");
|
||||
|
||||
item.SendSignal(new Signal(sub.WorldPosition.X.ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_position_x");
|
||||
item.SendSignal(new Signal(sub.RealWorldDepth.ToString("0.0000", CultureInfo.InvariantCulture), sender: user), "current_depth");
|
||||
}
|
||||
|
||||
// if our tactical AI pilot has left, revert back to maintaining position
|
||||
if (navigateTactically && (user == null || user.SelectedConstruction != item))
|
||||
{
|
||||
|
||||
@@ -370,5 +370,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void RemoveComponentSpecific()
|
||||
{
|
||||
base.RemoveComponentSpecific();
|
||||
connectedRecipients?.Clear();
|
||||
connectionDirty?.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace Barotrauma.Items.Components
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (!subElement.Name.ToString().Equals("attack", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
Attack = new Attack(subElement, item.Name + ", Projectile");
|
||||
Attack = new Attack(subElement, item.Name + ", Projectile", item);
|
||||
}
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
|
||||
@@ -103,18 +103,22 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsTinkering { get; private set; } = false;
|
||||
|
||||
public float RepairIconThreshold
|
||||
{
|
||||
get { return RepairThreshold / 2; }
|
||||
}
|
||||
|
||||
public Character CurrentFixer { get; private set; }
|
||||
private Item currentRepairItem;
|
||||
|
||||
public enum FixActions : int
|
||||
{
|
||||
None = 0,
|
||||
Repair = 1,
|
||||
Sabotage = 2
|
||||
Sabotage = 2,
|
||||
Tinker = 3,
|
||||
}
|
||||
|
||||
private FixActions currentFixerAction = FixActions.None;
|
||||
@@ -161,12 +165,14 @@ namespace Barotrauma.Items.Components
|
||||
/// <summary>
|
||||
/// Check if the character manages to succesfully repair the item
|
||||
/// </summary>
|
||||
public bool CheckCharacterSuccess(Character character)
|
||||
public bool CheckCharacterSuccess(Character character, Item bestRepairItem)
|
||||
{
|
||||
if (character == null) { return false; }
|
||||
|
||||
if (statusEffectLists == null || statusEffectLists.None(s => s.Key == ActionType.OnFailure)) { return true; }
|
||||
|
||||
if (bestRepairItem != null && bestRepairItem.Prefab.CannotRepairFail) { return true; }
|
||||
|
||||
// unpowered (electrical) items can be repaired without a risk of electrical shock
|
||||
if (requiredSkills.Any(s => s != null && s.Identifier.Equals("electrical", StringComparison.OrdinalIgnoreCase)) &&
|
||||
item.GetComponent<Powered>() is Powered powered && powered.Voltage < 0.1f) { return true; }
|
||||
@@ -201,10 +207,11 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
Item bestRepairItem = GetBestRepairItem(character);
|
||||
#if SERVER
|
||||
if (CurrentFixer != character || currentFixerAction != action)
|
||||
{
|
||||
if (!CheckCharacterSuccess(character))
|
||||
if (!CheckCharacterSuccess(character, bestRepairItem))
|
||||
{
|
||||
GameServer.Log($"{GameServer.CharacterLogName(character)} failed to {(action == FixActions.Sabotage ? "sabotage" : "repair")} {item.Name}", ServerLog.MessageType.ItemInteraction);
|
||||
GameMain.Server?.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFailure, this, character.ID });
|
||||
@@ -215,11 +222,18 @@ namespace Barotrauma.Items.Components
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
#else
|
||||
if (GameMain.Client == null && (CurrentFixer != character || currentFixerAction != action) && !CheckCharacterSuccess(character)) { return false; }
|
||||
if (GameMain.Client == null && (CurrentFixer != character || currentFixerAction != action) && !CheckCharacterSuccess(character, bestRepairItem)) { return false; }
|
||||
#endif
|
||||
CurrentFixer = character;
|
||||
currentRepairItem = bestRepairItem;
|
||||
CurrentFixerAction = action;
|
||||
return true;
|
||||
|
||||
Item GetBestRepairItem(Character character)
|
||||
{
|
||||
return character.HeldItems.OrderByDescending(i => i.Prefab.AddedRepairSpeedMultiplier).FirstOrDefault();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,8 +247,16 @@ namespace Barotrauma.Items.Components
|
||||
item.CreateServerEvent(this);
|
||||
}
|
||||
#endif
|
||||
if (currentRepairItem != null)
|
||||
{
|
||||
foreach (var ic in currentRepairItem.GetComponents<ItemComponent>())
|
||||
{
|
||||
ic.ApplyStatusEffects(ActionType.OnSuccess, 1.0f, character);
|
||||
}
|
||||
}
|
||||
CurrentFixer.AnimController.Anim = AnimController.Animation.None;
|
||||
CurrentFixer = null;
|
||||
currentRepairItem = null;
|
||||
currentFixerAction = FixActions.None;
|
||||
#if CLIENT
|
||||
repairSoundChannel?.FadeOutAndDispose();
|
||||
@@ -266,7 +288,8 @@ namespace Barotrauma.Items.Components
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
UpdateProjSpecific(deltaTime);
|
||||
|
||||
IsTinkering = false;
|
||||
|
||||
if (CurrentFixer == null)
|
||||
{
|
||||
if (deteriorateAlwaysResetTimer > 0.0f)
|
||||
@@ -314,6 +337,20 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentFixerAction == FixActions.Tinker)
|
||||
{
|
||||
// this is a bit code rotty to interject it here, should be less reliant on returning
|
||||
if (!CanTinker(CurrentFixer))
|
||||
{
|
||||
StopRepairing(CurrentFixer);
|
||||
}
|
||||
else
|
||||
{
|
||||
IsTinkering = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float successFactor = requiredSkills.Count == 0 ? 1.0f : RepairDegreeOfSuccess(CurrentFixer, requiredSkills);
|
||||
|
||||
//item must have been below the repair threshold for the player to get an achievement or XP for repairing it
|
||||
@@ -327,6 +364,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
float fixDuration = MathHelper.Lerp(FixDurationLowSkill, FixDurationHighSkill, successFactor);
|
||||
fixDuration /= 1 + CurrentFixer.GetStatValue(StatTypes.RepairSpeed) + currentRepairItem?.Prefab.AddedRepairSpeedMultiplier ?? 0f;
|
||||
|
||||
if (currentFixerAction == FixActions.Repair)
|
||||
{
|
||||
if (fixDuration <= 0.0f)
|
||||
@@ -354,6 +393,7 @@ namespace Barotrauma.Items.Components
|
||||
CurrentFixer.Position + Vector2.UnitY * 100.0f);
|
||||
}
|
||||
SteamAchievementManager.OnItemRepaired(item, CurrentFixer);
|
||||
CurrentFixer.CheckTalents(AbilityEffectType.OnRepairComplete);
|
||||
}
|
||||
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
|
||||
wasBroken = false;
|
||||
@@ -399,6 +439,12 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanTinker(Character character)
|
||||
{
|
||||
if (!character.HasAbilityFlag(AbilityFlags.CanTinker)) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
|
||||
public void AdjustPowerConsumption(ref float powerConsumption)
|
||||
|
||||
@@ -350,6 +350,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
Connections.Clear();
|
||||
|
||||
#if CLIENT
|
||||
rewireSoundChannel?.FadeOutAndDispose();
|
||||
|
||||
+15
-1
@@ -12,8 +12,10 @@ namespace Barotrauma.Items.Components
|
||||
public enum WaveType
|
||||
{
|
||||
Pulse,
|
||||
Sawtooth,
|
||||
Sine,
|
||||
Square,
|
||||
Triangle,
|
||||
}
|
||||
|
||||
private float frequency;
|
||||
@@ -22,8 +24,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
[InGameEditable, Serialize(WaveType.Pulse, true, description: "What kind of a signal the item outputs." +
|
||||
" Pulse: periodically sends out a signal of 1." +
|
||||
" Sawtooth: sends out a periodic wave that increases linearly from 0 to 1." +
|
||||
" Sine: sends out a sine wave oscillating between -1 and 1." +
|
||||
" Square: sends out a signal that alternates between 0 and 1.", alwaysUseInstanceValues: true)]
|
||||
" Square: sends out a signal that alternates between 0 and 1." +
|
||||
" Triangle: sends out a wave that alternates between increasing linearly from -1 to 1 and decreasing from 1 to -1.",
|
||||
alwaysUseInstanceValues: true)]
|
||||
public WaveType OutputType
|
||||
{
|
||||
get;
|
||||
@@ -63,6 +68,10 @@ namespace Barotrauma.Items.Components
|
||||
phase -= pulseInterval;
|
||||
}
|
||||
break;
|
||||
case WaveType.Sawtooth:
|
||||
phase = (phase + deltaTime * frequency) % 1.0f;
|
||||
item.SendSignal(phase.ToString(CultureInfo.InvariantCulture), "signal_out");
|
||||
break;
|
||||
case WaveType.Square:
|
||||
phase = (phase + deltaTime * frequency) % 1.0f;
|
||||
item.SendSignal(phase < 0.5f ? "0" : "1", "signal_out");
|
||||
@@ -71,6 +80,11 @@ namespace Barotrauma.Items.Components
|
||||
phase = (phase + deltaTime * frequency) % 1.0f;
|
||||
item.SendSignal(Math.Sin(phase * MathHelper.TwoPi).ToString(CultureInfo.InvariantCulture), "signal_out");
|
||||
break;
|
||||
case WaveType.Triangle:
|
||||
phase = (phase + deltaTime * frequency) % 1.0f;
|
||||
float output = 4.0f * MathF.Abs(MathUtils.PositiveModulo(phase - 0.25f, 1.0f) - 0.5f) - 1.0f;
|
||||
item.SendSignal(output.ToString(CultureInfo.InvariantCulture), "signal_out");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-3
@@ -85,14 +85,15 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(expression) || regex == null) return;
|
||||
if (string.IsNullOrWhiteSpace(expression) || regex == null) { return; }
|
||||
if (!ContinuousOutput && nonContinuousOutputSent) { return; }
|
||||
|
||||
if (receivedSignal != previousReceivedSignal && receivedSignal != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Match match = regex.Match(receivedSignal);
|
||||
previousResult = match.Success;
|
||||
previousResult = match.Success;
|
||||
previousGroups = UseCaptureGroup && previousResult ? match.Groups : null;
|
||||
previousReceivedSignal = receivedSignal;
|
||||
|
||||
@@ -133,7 +134,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (!string.IsNullOrEmpty(signalOut)) { item.SendSignal(signalOut, "signal_out"); }
|
||||
}
|
||||
else if (!nonContinuousOutputSent)
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(signalOut)) { item.SendSignal(signalOut, "signal_out"); }
|
||||
nonContinuousOutputSent = true;
|
||||
|
||||
@@ -45,6 +45,9 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize(false, true, description: "The terminal will use a monospace font if this box is ticked.", alwaysUseInstanceValues: true)]
|
||||
public bool UseMonospaceFont { get; set; }
|
||||
|
||||
private string OutputValue { get; set; }
|
||||
|
||||
public Terminal(Item item, XElement element)
|
||||
|
||||
@@ -100,9 +100,11 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (item.CurrentHull != null)
|
||||
{
|
||||
int waterPercentage = MathHelper.Clamp((int)Math.Round(item.CurrentHull.WaterPercentage), 0, 100);
|
||||
int waterPercentage = MathHelper.Clamp((int)Math.Ceiling(item.CurrentHull.WaterPercentage), 0, 100);
|
||||
item.SendSignal(waterPercentage.ToString(), "water_%");
|
||||
}
|
||||
string highPressureOut = (item.CurrentHull == null || item.CurrentHull.LethalPressure > 5.0f) ? "1" : "0";
|
||||
item.SendSignal(highPressureOut, "high_pressure");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,6 +544,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
Projectile launchedProjectile = null;
|
||||
bool loaderBroken = false;
|
||||
bool isTinkering = false;
|
||||
for (int i = 0; i < ProjectileCount; i++)
|
||||
{
|
||||
var projectiles = GetLoadedProjectiles();
|
||||
@@ -575,6 +576,7 @@ namespace Barotrauma.Items.Components
|
||||
projectiles = GetLoadedProjectiles();
|
||||
if (projectiles.Any()) { break; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if (projectiles.Count == 0 && !LaunchWithoutProjectile)
|
||||
@@ -601,10 +603,25 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
failedLaunchAttempts = 0;
|
||||
|
||||
foreach (MapEntity e in item.linkedTo)
|
||||
{
|
||||
if (!(e is Item linkedItem)) { continue; }
|
||||
if (!item.prefab.IsLinkAllowed(e.prefab)) { continue; }
|
||||
if (linkedItem.GetComponent<Repairable>() is Repairable repairable && linkedItem.HasTag("turretammosource"))
|
||||
{
|
||||
isTinkering = repairable.IsTinkering;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ignorePower)
|
||||
{
|
||||
var batteries = item.GetConnectedComponents<PowerContainer>();
|
||||
float neededPower = powerConsumption;
|
||||
if (isTinkering)
|
||||
{
|
||||
neededPower /= 1.25f;
|
||||
}
|
||||
while (neededPower > 0.0001f && batteries.Count > 0)
|
||||
{
|
||||
batteries.RemoveAll(b => b.Charge <= 0.0001f || b.MaxOutPut <= 0.0001f);
|
||||
@@ -622,7 +639,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
launchedProjectile = projectiles.FirstOrDefault();
|
||||
if (launchedProjectile?.Item.Container != null)
|
||||
Item container = launchedProjectile?.Item.Container;
|
||||
if (container != null)
|
||||
{
|
||||
var repairable = launchedProjectile?.Item.Container.GetComponent<Repairable>();
|
||||
if (repairable != null)
|
||||
@@ -637,18 +655,22 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
foreach (Projectile projectile in projectiles)
|
||||
{
|
||||
Launch(projectile.Item, character);
|
||||
Launch(projectile.Item, character, isTinkering: isTinkering);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Launch(null, character);
|
||||
Launch(null, character, isTinkering: isTinkering);
|
||||
}
|
||||
if (item.AiTarget != null)
|
||||
{
|
||||
item.AiTarget.SoundRange = item.AiTarget.MaxSoundRange;
|
||||
// Turrets also have a light component, which handles the sight range.
|
||||
}
|
||||
if (container != null)
|
||||
{
|
||||
ShiftItemsInProjectileContainer(container.GetComponent<ItemContainer>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -672,9 +694,18 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
private void Launch(Item projectile, Character user = null, float? launchRotation = null)
|
||||
private void Launch(Item projectile, Character user = null, float? launchRotation = null, bool isTinkering = false)
|
||||
{
|
||||
reload = reloadTime;
|
||||
if (isTinkering)
|
||||
{
|
||||
reload /= 1.25f;
|
||||
}
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
reload /= 1 + user.GetStatValue(StatTypes.TurretAttackSpeed);
|
||||
}
|
||||
|
||||
if (projectile != null)
|
||||
{
|
||||
@@ -698,6 +729,10 @@ namespace Barotrauma.Items.Components
|
||||
if (projectileComponent != null)
|
||||
{
|
||||
projectileComponent.Attacker = user;
|
||||
if (isTinkering)
|
||||
{
|
||||
projectileComponent.Attack.DamageMultiplier *= 1.25f;
|
||||
}
|
||||
projectileComponent.Use();
|
||||
projectile.GetComponent<Rope>()?.Attach(item, projectile);
|
||||
projectileComponent.User = user;
|
||||
@@ -725,6 +760,26 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void LaunchProjSpecific();
|
||||
|
||||
private void ShiftItemsInProjectileContainer(ItemContainer container)
|
||||
{
|
||||
if (container == null) { return; }
|
||||
bool moved;
|
||||
do
|
||||
{
|
||||
moved = false;
|
||||
for (int i = 1; i < container.Capacity; i++)
|
||||
{
|
||||
if (container.Inventory.GetItemAt(i) is Item item1 && container.Inventory.CanBePutInSlot(item1, i - 1))
|
||||
{
|
||||
if (container.Inventory.TryPutItem(item1, i - 1, allowSwapping: false, allowCombine: false, user: null, createNetworkEvent: true))
|
||||
{
|
||||
moved = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (moved);
|
||||
}
|
||||
|
||||
private float waitTimer;
|
||||
private float disorderTimer;
|
||||
|
||||
@@ -864,57 +919,26 @@ namespace Barotrauma.Items.Components
|
||||
float turretAngle = -rotation;
|
||||
if (Math.Abs(MathUtils.GetShortestAngle(enemyAngle, turretAngle)) > 0.15f) { return; }
|
||||
}
|
||||
|
||||
Vector2 start = ConvertUnits.ToSimUnits(item.WorldPosition);
|
||||
Vector2 end = ConvertUnits.ToSimUnits(target.WorldPosition);
|
||||
// Check that there's not other entities that shouldn't be targeted (like a friendly sub) between us and the target.
|
||||
Body worldTarget = CheckLineOfSight(start, end);
|
||||
bool shoot;
|
||||
if (target.Submarine != null)
|
||||
{
|
||||
start -= target.Submarine.SimPosition;
|
||||
end -= target.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) =>
|
||||
{
|
||||
if (f.UserData is Item i && i.GetComponent<Turret>() != null) { return false; }
|
||||
return !item.StaticFixtures.Contains(f);
|
||||
});
|
||||
if (pickedBody == null) { return; }
|
||||
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 (targetCharacter.Params.Group.Equals(ai.Config.Entity, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// Don't shoot friendly characters
|
||||
return;
|
||||
}
|
||||
Body transformedTarget = CheckLineOfSight(start, end);
|
||||
shoot = CanShoot(transformedTarget, user: null, ai, targetSubmarines) && (worldTarget == null || CanShoot(worldTarget, user: null, ai, targetSubmarines));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pickedBody.UserData is ISpatialEntity e)
|
||||
{
|
||||
Submarine sub = e.Submarine;
|
||||
if (sub == null) { return; }
|
||||
if (!targetSubmarines) { return; }
|
||||
if (sub == Item.Submarine) { return; }
|
||||
// Don't shoot non-player submarines, i.e. wrecks or outposts.
|
||||
if (!sub.Info.IsPlayer) { return; }
|
||||
}
|
||||
else
|
||||
{
|
||||
// Hit something else, probably a level wall
|
||||
return;
|
||||
}
|
||||
shoot = CanShoot(worldTarget, user: null, ai, targetSubmarines);
|
||||
}
|
||||
if (shoot)
|
||||
{
|
||||
TryLaunch(deltaTime, ignorePower: true);
|
||||
}
|
||||
TryLaunch(deltaTime, ignorePower: true);
|
||||
}
|
||||
|
||||
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
@@ -1067,7 +1091,8 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
// Ignore dead, friendly, and those that are inside the same sub
|
||||
if (enemy.IsDead || !enemy.Enabled || enemy.Submarine == character.Submarine) { continue; }
|
||||
// Don't aim monsters that are inside a submarine.
|
||||
if (enemy.Submarine != null && enemy.Submarine.TeamID == character.Submarine.TeamID) { continue; }
|
||||
// Don't aim monsters that are inside any submarine.
|
||||
if (!enemy.IsHuman && enemy.CurrentHull != null) { continue; }
|
||||
if (HumanAIController.IsFriendly(character, enemy)) { continue; }
|
||||
float dist = Vector2.DistanceSquared(enemy.WorldPosition, item.WorldPosition);
|
||||
@@ -1091,26 +1116,34 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (closestEnemy != null)
|
||||
{
|
||||
// Target the closest limb. Doesn't make much difference with smaller creatures, but enables the bots to shoot longer abyss creatures like the endworm. Otherwise they just target the main body = head.
|
||||
targetPos = closestEnemy.WorldPosition;
|
||||
float closestDist = closestDistance;
|
||||
foreach (Limb limb in closestEnemy.AnimController.Limbs)
|
||||
//if the enemy is inside another sub, aim at the room they're in to make it less obvious that the enemy "knows" exactly where the target is
|
||||
if (closestEnemy.Submarine != null && closestEnemy.CurrentHull != null && closestEnemy.Submarine != item.Submarine)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.Hidden) { continue; }
|
||||
if (!CheckTurretAngle(limb.WorldPosition)) { continue; }
|
||||
float dist = Vector2.DistanceSquared(limb.WorldPosition, item.WorldPosition);
|
||||
if (dist < closestDist)
|
||||
{
|
||||
closestDist = dist;
|
||||
targetPos = limb.WorldPosition;
|
||||
}
|
||||
targetPos = closestEnemy.CurrentHull.WorldPosition;
|
||||
}
|
||||
if (closestDist > shootDistance * shootDistance)
|
||||
else
|
||||
{
|
||||
// Not close enough to shoot
|
||||
closestEnemy = null;
|
||||
targetPos = null;
|
||||
// Target the closest limb. Doesn't make much difference with smaller creatures, but enables the bots to shoot longer abyss creatures like the endworm. Otherwise they just target the main body = head.
|
||||
float closestDist = closestDistance;
|
||||
foreach (Limb limb in closestEnemy.AnimController.Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.Hidden) { continue; }
|
||||
if (!CheckTurretAngle(limb.WorldPosition)) { continue; }
|
||||
float dist = Vector2.DistanceSquared(limb.WorldPosition, item.WorldPosition);
|
||||
if (dist < closestDist)
|
||||
{
|
||||
closestDist = dist;
|
||||
targetPos = limb.WorldPosition;
|
||||
}
|
||||
}
|
||||
if (closestDist > shootDistance * shootDistance)
|
||||
{
|
||||
// Not close enough to shoot
|
||||
closestEnemy = null;
|
||||
targetPos = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (item.Submarine != null && Level.Loaded != null)
|
||||
@@ -1158,7 +1191,7 @@ namespace Barotrauma.Items.Components
|
||||
continue;
|
||||
}
|
||||
// Allow targeting farther when heading towards the spire (up to 1000 px)
|
||||
dist -= MathHelper.Lerp(0, 1000, MathUtils.InverseLerp(minAngle, 1, dot)); ;
|
||||
dist -= MathHelper.Lerp(0, 1000, MathUtils.InverseLerp(minAngle, 1, dot));
|
||||
if (dist > closestDistance) { continue; }
|
||||
targetPos = closestPoint;
|
||||
closestDistance = dist;
|
||||
@@ -1222,58 +1255,25 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (Math.Abs(MathUtils.GetShortestAngle(enemyAngle, turretAngle)) > maxAngleError) { return false; }
|
||||
|
||||
Vector2 start = ConvertUnits.ToSimUnits(item.WorldPosition);
|
||||
Vector2 end = ConvertUnits.ToSimUnits(targetPos.Value);
|
||||
if (closestEnemy != null && 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) =>
|
||||
{
|
||||
if (f.UserData is Item i && i.GetComponent<Turret>() != null) { return false; }
|
||||
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 if (!(pickedBody.UserData is Voronoi2.VoronoiCell cell && cell.IsDestructible))
|
||||
{
|
||||
// Hit something else, probably a level wall
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (canShoot)
|
||||
{
|
||||
Vector2 start = ConvertUnits.ToSimUnits(item.WorldPosition);
|
||||
Vector2 end = ConvertUnits.ToSimUnits(targetPos.Value);
|
||||
// Check that there's not other entities that shouldn't be targeted (like a friendly sub) between us and the target.
|
||||
Body worldTarget = CheckLineOfSight(start, end);
|
||||
bool shoot;
|
||||
if (closestEnemy != null && closestEnemy.Submarine != null)
|
||||
{
|
||||
start -= closestEnemy.Submarine.SimPosition;
|
||||
end -= closestEnemy.Submarine.SimPosition;
|
||||
Body transformedTarget = CheckLineOfSight(start, end);
|
||||
shoot = CanShoot(transformedTarget, character) && (worldTarget == null || CanShoot(worldTarget, character));
|
||||
}
|
||||
else
|
||||
{
|
||||
shoot = CanShoot(worldTarget, character);
|
||||
}
|
||||
if (!shoot) { return false; }
|
||||
if (character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogFireTurret"), null, 0.0f, "fireturret", 10.0f);
|
||||
@@ -1284,6 +1284,67 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool CanShoot(Body targetBody, Character user = null, WreckAI ai = null, bool targetSubmarines = true)
|
||||
{
|
||||
if (targetBody == null) { return false; }
|
||||
Character targetCharacter = null;
|
||||
if (targetBody.UserData is Character c)
|
||||
{
|
||||
targetCharacter = c;
|
||||
}
|
||||
else if (targetBody.UserData is Limb limb)
|
||||
{
|
||||
targetCharacter = limb.character;
|
||||
}
|
||||
if (targetCharacter != null)
|
||||
{
|
||||
if (user != null)
|
||||
{
|
||||
if (HumanAIController.IsFriendly(user, targetCharacter))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (ai != null)
|
||||
{
|
||||
if (targetCharacter.Params.Group.Equals(ai.Config.Entity, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (targetBody.UserData is ISpatialEntity e)
|
||||
{
|
||||
Submarine sub = e.Submarine ?? e as Submarine;
|
||||
if (!targetSubmarines && e is Submarine) { return false; }
|
||||
if (sub == null) { return false; }
|
||||
if (sub == Item.Submarine) { return false; }
|
||||
if (sub.Info.IsOutpost || sub.Info.IsWreck || sub.Info.IsBeacon) { return false; }
|
||||
if (sub.TeamID == Item.Submarine.TeamID) { return false; }
|
||||
}
|
||||
else if (!(targetBody.UserData is Voronoi2.VoronoiCell cell && cell.IsDestructible))
|
||||
{
|
||||
// Hit something else, probably a level wall
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private Body CheckLineOfSight(Vector2 start, Vector2 end)
|
||||
{
|
||||
var collisionCategories = Physics.CollisionWall | Physics.CollisionCharacter | Physics.CollisionItem | Physics.CollisionLevel;
|
||||
Body pickedBody = Submarine.PickBody(start, end, null, collisionCategories, allowInsideFixture: true,
|
||||
customPredicate: (Fixture f) =>
|
||||
{
|
||||
if (f.UserData is Item i && i.GetComponent<Turret>() != null) { return false; }
|
||||
return !item.StaticFixtures.Contains(f);
|
||||
});
|
||||
return pickedBody;
|
||||
}
|
||||
|
||||
private Vector2 GetRelativeFiringPosition(bool useOffset = true)
|
||||
{
|
||||
Vector2 transformedFiringOffset = Vector2.Zero;
|
||||
|
||||
@@ -7,6 +7,7 @@ using System.Xml.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma.Abilities;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -210,7 +211,9 @@ namespace Barotrauma.Items.Components
|
||||
private readonly Limb[] limb;
|
||||
|
||||
private readonly List<DamageModifier> damageModifiers;
|
||||
public readonly Dictionary<string, float> SkillModifiers;
|
||||
public readonly Dictionary<string, float> SkillModifiers = new Dictionary<string, float>();
|
||||
|
||||
public readonly Dictionary<StatTypes, float> WearableStatValues = new Dictionary<StatTypes, float>();
|
||||
|
||||
public IEnumerable<DamageModifier> DamageModifiers
|
||||
{
|
||||
@@ -266,7 +269,6 @@ namespace Barotrauma.Items.Components
|
||||
this.item = item;
|
||||
|
||||
damageModifiers = new List<DamageModifier>();
|
||||
SkillModifiers = new Dictionary<string, float>();
|
||||
|
||||
int spriteCount = element.Elements().Count(x => x.Name.ToString() == "sprite");
|
||||
Variants = element.GetAttributeInt("variants", 0);
|
||||
@@ -322,6 +324,18 @@ namespace Barotrauma.Items.Components
|
||||
SkillModifiers.TryAdd(skillIdentifier, skillValue);
|
||||
}
|
||||
break;
|
||||
case "statvalue":
|
||||
StatTypes statType = CharacterAbilityGroup.ParseStatType(subElement.GetAttributeString("stattype", ""), Name);
|
||||
float statValue = subElement.GetAttributeFloat("value", 0f);
|
||||
if (WearableStatValues.ContainsKey(statType))
|
||||
{
|
||||
WearableStatValues[statType] += statValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
WearableStatValues.TryAdd(statType, statValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -334,6 +348,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
picker = character;
|
||||
|
||||
for (int i = 0; i < wearableSprites.Length; i++ )
|
||||
{
|
||||
var wearableSprite = wearableSprites[i];
|
||||
|
||||
Reference in New Issue
Block a user