4 using FarseerPhysics.Dynamics;
5 using FarseerPhysics.Dynamics.Contacts;
6 using Microsoft.Xna.Framework;
8 using System.Collections.Concurrent;
9 using System.Collections.Generic;
10 using System.Globalization;
12 using System.Xml.Linq;
15 using MoonSharp.Interpreter;
16 using System.Collections.Immutable;
20 using Microsoft.Xna.Framework.Graphics;
27 public static readonly List<Item>
ItemList =
new List<Item>();
29 private static readonly HashSet<Item> dangerousItems =
new HashSet<Item>();
31 public static IReadOnlyCollection<Item>
DangerousItems {
get {
return dangerousItems; } }
33 private static readonly List<Item> repairableItems =
new List<Item>();
40 private static readonly List<Item> cleanableItems =
new List<Item>();
47 private static readonly HashSet<Item> deconstructItems =
new HashSet<Item>();
54 private static readonly List<Item> sonarVisibleItems =
new List<Item>();
65 private readonly HashSet<Identifier> tags;
67 private readonly
bool isWire, isLogic;
69 private Hull currentHull;
72 get {
return currentHull; }
87 get {
return campaignInteractionType; }
92 if (campaignInteractionType == interactionType) {
return; }
93 campaignInteractionType = interactionType;
94 AssignCampaignInteractionTypeProjSpecific(campaignInteractionType, targetClients);
98 partial
void AssignCampaignInteractionTypeProjSpecific(
CampaignMode.
InteractionType interactionType, IEnumerable<Client> targetClients);
107 private readonly Dictionary<Type, ItemComponent> componentsByType =
new Dictionary<Type, ItemComponent>();
108 private readonly List<ItemComponent> components;
112 private readonly List<ItemComponent> updateableComponents =
new List<ItemComponent>();
113 private readonly List<IDrawableComponent> drawableComponents;
114 private bool hasComponentsToDraw;
123 private readonly
float originalWaterDragCoefficient;
124 private float? overrideWaterDragCoefficient;
127 get => overrideWaterDragCoefficient ?? originalWaterDragCoefficient;
128 set => overrideWaterDragCoefficient = value;
155 private bool transformDirty =
true;
157 private static readonly List<Item> itemsWithPendingConditionUpdates =
new List<Item>();
159 private float lastSentCondition;
160 private float sendConditionUpdateTimer;
162 private float prevCondition;
163 private float condition;
165 private bool inWater;
166 private readonly
bool hasInWaterStatusEffects;
167 private readonly
bool hasNotInWaterStatusEffects;
178 get {
return defaultRect; }
179 set { defaultRect = value; }
182 private readonly Dictionary<string, Connection> connections;
184 private readonly List<Repairable> repairables;
186 private readonly
Quality qualityComponent;
188 private ConcurrentQueue<float> impactQueue;
191 private readonly
bool[] hasStatusEffectsOfType =
new bool[Enum.GetValues(typeof(
ActionType)).Length];
192 private readonly Dictionary<ActionType, List<StatusEffect>> statusEffectLists;
197 private readonly
float conditionMultiplierCampaign = 1.0f;
203 private bool? hasInGameEditableProperties;
204 bool HasInGameEditableProperties
208 if (hasInGameEditableProperties ==
null)
210 hasInGameEditableProperties =
false;
213 hasInGameEditableProperties =
true;
223 hasInGameEditableProperties =
true;
229 return (
bool)hasInGameEditableProperties;
246 return parentInventory;
250 parentInventory = value;
251 if (parentInventory !=
null)
254 RemoveFromDroppedStack(allowClientExecute:
false);
257 PreviousParentInventory = value;
262 private Item rootContainer;
265 get {
return rootContainer; }
270 DebugConsole.ThrowError($
"Attempted to set the item \"{Prefab.Identifier}\" as it's own root container!\n{Environment.StackTrace.CleanupStackTrace()}");
271 rootContainer =
null;
274 rootContainer = value;
278 private bool inWaterProofContainer;
280 private Item container;
283 get {
return container; }
286 if (value != container)
291 RefreshRootContainer();
303 get {
return base.Prefab.Name.Value; }
306 private string description;
309 get {
return description ?? base.Prefab.Description.Value; }
310 set { description = value; }
313 private string descriptionTag;
321 get {
return descriptionTag; }
324 if (value == descriptionTag) {
return; }
325 if (value.IsNullOrEmpty())
327 descriptionTag =
null;
332 description = TextManager.Get(value).Value;
333 descriptionTag = value;
354 [
Editable,
Serialize(
false,
IsPropertySaveable.Yes, description:
"When enabled, item is interactable only for characters on non-player teams.", alwaysUseInstanceValues:
true)]
419 if (!
Prefab.AllowRotatingInEditor) {
return; }
420 RotationRad = MathUtils.WrapAnglePi(MathHelper.ToRadians(value));
425 foreach (var light
in GetComponents<LightComponent>())
427 light.SetLightSourceTransform();
429 foreach (var turret
in GetComponents<Turret>())
431 turret.UpdateLightComponents();
440 get {
return Prefab.ImpactTolerance; }
445 get {
return Prefab.InteractDistance; }
450 get {
return Prefab.InteractPriority; }
477 private float scale = 1.0f;
480 get {
return scale; }
483 if (scale == value) {
return; }
484 scale = MathHelper.Clamp(value,
Prefab.MinScale,
Prefab.MaxScale);
486 float relativeScale = scale / base.Prefab.Scale;
491 int newHeight =
ResizeVertical ?
rect.Height : (int)(defaultRect.Height * relativeScale);
495 if (components !=
null)
509 } =
float.PositiveInfinity;
526 [
Serialize(
"1.0,1.0,1.0,1.0",
IsPropertySaveable.Yes, description:
"Changes the color of the item this item is contained inside. Only has an effect if either of the UseContainedSpriteColor or UseContainedInventoryIconColor property of the container is set to true."),
555 return parentInventory !=
null;
572 return character.AnimController.MainLimb.LinearVelocity.Length();
574 else if (container !=
null)
576 return container.
Speed;
595 string trimmedStr = !
string.IsNullOrEmpty(value) && value.Length > 250 ? value.Substring(250) : value;
681 return MathUtils.Percentage(
Condition, defaultMaxCondition);
685 private float offsetOnSelectedMultiplier = 1.0f;
690 get => offsetOnSelectedMultiplier;
691 set => offsetOnSelectedMultiplier = value;
694 private float healthMultiplier = 1.0f;
699 get => healthMultiplier;
703 healthMultiplier = MathHelper.Clamp(value, 0.0f,
float.PositiveInfinity);
705 condition =
MaxCondition * prevConditionPercentage / 100.0f;
710 private float maxRepairConditionMultiplier = 1.0f;
715 get => maxRepairConditionMultiplier;
718 maxRepairConditionMultiplier = MathHelper.Clamp(value, 0.0f,
float.PositiveInfinity);
724 private bool HasBeenInstantiatedOnce {
get;
set; }
731 get {
return condition; }
734 SetCondition(value, isNetworkEvent:
false);
738 private double ConditionLastUpdated {
get;
set; }
739 private float LastConditionChange {
get;
set; }
747 get {
return condition; }
750 private bool? indestructible;
756 get => indestructible ??
Prefab.Indestructible;
757 set => indestructible = value;
780 private bool spawnedInCurrentOutpost;
783 get {
return spawnedInCurrentOutpost; }
786 if (!spawnedInCurrentOutpost && value)
790 spawnedInCurrentOutpost = value;
794 private bool allowStealing;
796 description: $
"Determined by where/how the item originally spawned. If ItemPrefab.AllowStealing is true, stealing the item is always allowed.")]
799 get {
return allowStealing ||
Prefab.AllowStealingAlways; }
800 set { allowStealing = value; }
805 private string originalOutpost;
809 get {
return originalOutpost; }
812 originalOutpost = value;
813 if (!
string.IsNullOrEmpty(value) &&
817 spawnedInCurrentOutpost =
true;
825 get {
return string.Join(
",", tags); }
830 base.Prefab.Tags.ForEach(t => tags.Add(t));
831 if (!
string.IsNullOrWhiteSpace(value))
833 string[] splitTags = value.Split(
',');
834 foreach (
string tag
in splitTags)
836 string[] splitTag = tag.Trim().Split(
':');
837 splitTag[0] = splitTag[0].ToLowerInvariant();
838 tags.Add(
string.Join(
":", splitTag).ToIdentifier());
850 private bool waterProof;
854 get {
return waterProof; }
857 if (waterProof == value) {
return; }
861 containedItem.RefreshInWaterProofContainer();
868 get {
return Prefab.UseInHealthInterface; }
879 if (qualityComponent !=
null)
881 qualityComponent.QualityLevel = value;
892 if (hasInWaterStatusEffects) {
return inWater; }
906 } =
new List<Connection>(20);
911 private readonly HashSet<InvSlotType> allowedSlots =
new HashSet<InvSlotType>();
925 if (panel ==
null)
return null;
947 foreach (var item
in inventory.AllItems)
958 get {
return ownInventory; }
961 public readonly ImmutableArray<ItemInventory>
OwnInventories = ImmutableArray<ItemInventory>.Empty;
964 "Enable if you want to display the item HUD side by side with another item's HUD, when linked together. " +
965 "Disclaimer: It's possible or even likely that the views block each other, if they were not designed to be viewed together!")]
970 get {
return repairables; }
975 get {
return components; }
980 get {
return Prefab.Linkable; }
1017 return Name +
" (ID: " +
ID +
")";
1020 private readonly List<ISerializableEntity> allPropertyObjects =
new List<ISerializableEntity>();
1023 get {
return allPropertyObjects; }
1041 if (
CurrentHull?.BallastFlora ==
null) {
return false; }
1053 private ItemStatManager statManager;
1058 statManager ??=
new ItemStatManager(
this);
1072 (int)(position.X - itemPrefab.
Sprite.size.X / 2 * itemPrefab.
Scale),
1073 (int)(position.Y + itemPrefab.
Sprite.size.Y / 2 * itemPrefab.
Scale),
1074 (int)(itemPrefab.
Sprite.size.X * itemPrefab.
Scale),
1075 (int)(itemPrefab.
Sprite.size.Y * itemPrefab.
Scale)),
1076 itemPrefab, submarine, callOnItemLoaded, id: id)
1086 : base(itemPrefab, submarine, id)
1090 components =
new List<ItemComponent>();
1091 drawableComponents =
new List<IDrawableComponent>(); hasComponentsToDraw =
false;
1092 tags =
new HashSet<Identifier>();
1093 repairables =
new List<Repairable>();
1095 defaultRect = newRect;
1101 lastSentCondition = condition;
1105 allPropertyObjects.Add(
this);
1108 if (element ==
null)
return;
1117 foreach (var subElement
in element.Elements())
1119 switch (subElement.Name.ToString().ToLowerInvariant())
1122 bodyElement = subElement;
1123 float density = subElement.
GetAttributeFloat(
"density", Physics.NeutralDensity);
1124 float minDensity = subElement.GetAttributeFloat(
"mindensity", density);
1125 float maxDensity = subElement.GetAttributeFloat(
"maxdensity", density);
1126 if (minDensity < maxDensity)
1129 density = MathHelper.Lerp(minDensity, maxDensity, (
float)rand.NextDouble());
1132 string collisionCategoryStr = subElement.GetAttributeString(
"collisioncategory",
null);
1134 Category collisionCategory = Physics.CollisionItem;
1135 Category collidesWith = Physics.DefaultItemCollidesWith;
1142 collisionCategory = Physics.CollisionCharacter;
1143 collidesWith |= Physics.CollisionProjectile;
1145 if (collisionCategoryStr !=
null)
1147 if (!Physics.TryParseCollisionCategory(collisionCategoryStr, out Category cat))
1149 DebugConsole.ThrowError(
"Invalid collision category in item \"" +
Name +
"\" (" + collisionCategoryStr +
")",
1150 contentPackage: element.ContentPackage);
1154 collisionCategory = cat;
1155 if (cat.HasFlag(Physics.CollisionCharacter))
1157 collisionCategory |= Physics.CollisionProjectile;
1161 body =
new PhysicsBody(subElement, ConvertUnits.ToSimUnits(
Position),
Scale, density, collisionCategory, collidesWith, findNewContacts:
false);
1162 body.
FarseerBody.AngularDamping = subElement.GetAttributeFloat(
"angulardamping", 0.2f);
1163 body.
FarseerBody.LinearDamping = subElement.GetAttributeFloat(
"lineardamping", 0.1f);
1164 body.
FarseerBody.LinearDamping = subElement.GetAttributeFloat(
"lineardamping", 0.1f);
1168 case "inventoryicon":
1171 case "brokensprite":
1172 case "decorativesprite":
1173 case "upgradepreviewsprite":
1175 case "levelcommonness":
1176 case "suitabletreatment":
1177 case "containedsprite":
1180 case "fabricableitem":
1182 case "preferredcontainer":
1183 case "upgrademodule":
1184 case "upgradeoverride":
1186 case "infectedsprite":
1187 case "damagedinfectedsprite":
1188 case "swappableitem":
1189 case "skillrequirementhint":
1199 if (ic ==
null)
break;
1210 foreach (var allowedSlot
in pickable.AllowedSlots)
1212 allowedSlots.Add(allowedSlot);
1216 if (ic is
Repairable repairable) { repairables.Add(repairable); }
1221 hasComponentsToDraw =
true;
1232 statusEffectLists ??=
new Dictionary<ActionType, List<StatusEffect>>();
1238 ActionType actionType = componentEffectList.First().type;
1239 if (!statusEffectLists.TryGetValue(actionType, out List<StatusEffect> statusEffectList))
1241 statusEffectList =
new List<StatusEffect>();
1242 statusEffectLists.Add(actionType, statusEffectList);
1243 hasStatusEffectsOfType[(int)actionType] =
true;
1248 statusEffectList.Add(effect);
1253 hasInWaterStatusEffects = hasStatusEffectsOfType[(int)
ActionType.InWater];
1254 hasNotInWaterStatusEffects = hasStatusEffectsOfType[(
int)
ActionType.NotInWater];
1259 originalWaterDragCoefficient = bodyElement.GetAttributeFloat(
"waterdragcoefficient", 5.0f);
1263 var connectionPanel = GetComponent<ConnectionPanel>();
1264 if (connectionPanel !=
null)
1266 connections =
new Dictionary<string, Connection>();
1267 foreach (
Connection c
in connectionPanel.Connections)
1269 if (!connections.ContainsKey(c.
Name))
1270 connections.Add(c.
Name, c);
1279 var itemContainer = GetComponent<ItemContainer>();
1280 if (itemContainer !=
null)
1282 ownInventory = itemContainer.Inventory;
1285 OwnInventories = GetComponents<ItemContainer>().Select(ic => ic.Inventory).ToImmutableArray();
1287 qualityComponent = GetComponent<Quality>();
1289 IsLadder = GetComponent<Ladder>() !=
null;
1294 if (callOnItemLoaded)
1302 var holdables = components.Where(c => c is
Holdable);
1303 if (holdables.Count() > 1)
1305 DebugConsole.AddWarning($
"Item {Prefab.Identifier} has multiple {nameof(Holdable)} components ({string.Join(",
", holdables.Select(h => h.GetType().Name))}).",
1311 if (
Prefab.IsDangerous) { dangerousItems.Add(
this); }
1312 if (
Repairables.Any()) { repairableItems.Add(
this); }
1313 if (
Prefab.SonarSize > 0.0f) { sonarVisibleItems.Add(
this); }
1316 DebugConsole.Log(
"Created " +
Name +
" (" +
ID +
")");
1330 conditionMultiplierCampaign *= campaign.Settings.OxygenMultiplier;
1334 conditionMultiplierCampaign *= campaign.Settings.FuelMultiplier;
1337 if (!HasBeenInstantiatedOnce)
1342 condition *= conditionMultiplierCampaign;
1347 if (callOnItemLoaded)
1354 HasBeenInstantiatedOnce =
true;
1357 partial
void InitProjSpecific();
1360 =>
Prefab.IsContainerPreferred(
this, container, out isPreferencesDefined, out isSecondary, requireConditionRestriction);
1366 defaultRect = defaultRect
1370 if (property.Value.Attributes.OfType<
Serialize>().None()) {
continue; }
1371 clone.SerializableProperties[
property.Key].TrySetValue(clone, property.Value.GetValue(
this));
1374 if (components.Count != clone.components.Count)
1376 string errorMsg =
"Error while cloning item \"" +
Name +
"\" - clone does not have the same number of components. ";
1377 errorMsg +=
"Original components: " +
string.Join(
", ", components.Select(c => c.GetType().ToString()));
1378 errorMsg +=
", cloned components: " +
string.Join(
", ", clone.components.Select(c => c.GetType().ToString()));
1379 DebugConsole.ThrowError(errorMsg);
1380 GameAnalyticsManager.AddErrorEventOnce(
"Item.Clone:" +
Name, GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
1383 for (
int i = 0; i < components.Count && i < clone.components.Count; i++)
1386 foreach (KeyValuePair<Identifier, SerializableProperty> property
in components[i].
SerializableProperties.OrderBy(s => s.Key))
1388 if (property.Value.Attributes.OfType<
Serialize>().None()) {
continue; }
1389 clone.components[i].SerializableProperties[
property.Key].TrySetValue(clone.components[i], property.Value.GetValue(components[i]));
1393 foreach (var kvp
in components[i].RequiredItems)
1395 for (
int j = 0; j < kvp.Value.Count; j++)
1397 if (!clone.components[i].RequiredItems.ContainsKey(kvp.Key) ||
1398 clone.components[i].RequiredItems[kvp.Key].Count <= j)
1403 clone.components[i].RequiredItems[kvp.Key][j].JoinedIdentifiers =
1404 kvp.Value[j].JoinedIdentifiers;
1409 if (
FlippedX) { clone.FlipX(
false); }
1410 if (
FlippedY) { clone.FlipY(
false); }
1420 Dictionary<ushort, Item> clonedContainedItems =
new();
1422 for (
int i = 0; i < components.Count && i < clone.components.Count; i++)
1425 cloneComp = clone.components[i];
1433 foreach (var containedItem
in origInv.Inventory.AllItems)
1435 var containedClone = (
Item)containedItem.Clone();
1437 cloneInv.Inventory.TryPutItem(containedClone,
null);
1438 clonedContainedItems.Add(containedItem.ID, containedClone);
1442 for (
int i = 0; i < components.Count && i < clone.components.Count; i++)
1445 cloneComp = clone.components[i];
1447 if (component is not CircuitBox origBox || cloneComp is not CircuitBox cloneBox)
1452 cloneBox.CloneFrom(origBox, clonedContainedItems);
1455 clone.FullyInitialized =
true;
1461 allPropertyObjects.Add(component);
1462 components.Add(component);
1466 updateableComponents.Add(component);
1471 bool needsSoundUpdate =
false;
1478 !needsSoundUpdate &&
1479 component.
Parent ==
null &&
1482 if (updateableComponents.Contains(component)) { updateableComponents.Remove(component); }
1486 if (!updateableComponents.Contains(component))
1488 updateableComponents.Add(component);
1489 this.isActive =
true;
1494 Type type = component.GetType();
1495 if (!componentsByType.ContainsKey(type))
1497 componentsByType.Add(type, component);
1498 Type baseType = type.BaseType;
1499 while (baseType !=
null && baseType != typeof(
ItemComponent))
1501 if (!componentsByType.ContainsKey(baseType))
1503 componentsByType.Add(baseType, component);
1505 baseType = baseType.BaseType;
1512 if (!drawableComponents.Contains(drawable))
1514 drawableComponents.Add(drawable);
1515 hasComponentsToDraw =
true;
1518 cachedVisibleExtents =
null;
1525 if (drawableComponents.Contains(drawable))
1527 drawableComponents.Remove(drawable);
1528 hasComponentsToDraw = drawableComponents.Count > 0;
1530 cachedVisibleExtents =
null;
1537 return components.IndexOf(component);
1542 if (componentsByType.TryGetValue(typeof(T), out
ItemComponent component))
1544 return (T)component;
1548 return (T)components.FirstOrDefault();
1557 return components.Cast<T>();
1559 if (!componentsByType.ContainsKey(typeof(T))) {
return Enumerable.Empty<T>(); }
1560 return components.Where(c => c is T).Cast<T>();
1565 return GetComponent<Quality>()?.GetValue(statType) ?? 0.0f;
1570 ownInventory?.RemoveItem(contained);
1574 public void SetTransform(Vector2 simPosition,
float rotation,
bool findNewHull =
true,
bool setPrevTransform =
true)
1576 if (!MathUtils.IsValid(simPosition))
1579 "Attempted to move the item " +
Name +
1580 " to an invalid position (" + simPosition +
")\n" + Environment.StackTrace.CleanupStackTrace();
1582 DebugConsole.ThrowError(errorMsg);
1583 GameAnalyticsManager.AddErrorEventOnce(
1584 "Item.SetPosition:InvalidPosition" +
ID,
1585 GameAnalyticsManager.ErrorSeverity.Error,
1608 DebugConsole.ThrowError(
"Failed to set item transform", e);
1613 Vector2 displayPos = ConvertUnits.ToDisplayUnits(simPosition);
1615 rect.X = (int)MathF.Round(displayPos.X -
rect.Width / 2.0f);
1616 rect.Y = (int)MathF.Round(displayPos.Y +
rect.Height / 2.0f);
1626 if (!
Prefab.AllowDroppingOnSwap || otherItem ==
null) {
return false; }
1627 if (
Prefab.AllowDroppingOnSwapWith.Any())
1629 foreach (Identifier tagOrIdentifier
in Prefab.AllowDroppingOnSwapWith)
1632 if (otherItem.
HasTag(tagOrIdentifier)) {
return true; }
1644 SetActiveSpriteProjSpecific();
1647 partial
void SetActiveSpriteProjSpecific();
1654 var pickable = GetComponent<Pickable>();
1655 if (pickable !=
null && !pickable.IsAttached &&
1656 Prefab.PreferredContainers.Any() &&
1659 if (!cleanableItems.Contains(
this))
1661 cleanableItems.Add(
this);
1666 cleanableItems.Remove(
this);
1670 public override void Move(Vector2 amount,
bool ignoreContacts =
true)
1672 if (!MathUtils.IsValid(amount))
1674 DebugConsole.ThrowError($
"Attempted to move an item by an invalid amount ({amount})\n{Environment.StackTrace.CleanupStackTrace()}");
1678 base.Move(amount, ignoreContacts);
1693 ic.
Move(amount, ignoreContacts);
1705 (
int)(baseRect.X + trigger.X *
Scale),
1706 (
int)(baseRect.Y + trigger.Y *
Scale),
1707 (trigger.Width == 0) ?
Rect.Width : (
int)(trigger.Width *
Scale),
1708 (trigger.Height == 0) ?
Rect.Height : (
int)(trigger.Height *
Scale));
1712 transformedRect.X = baseRect.X + (baseRect.Right - transformedRect.Right);
1716 transformedRect.Y = baseRect.Y + ((baseRect.Y - baseRect.Height) - (transformedRect.Y - transformedRect.Height));
1719 return transformedRect;
1738 if (parentInventory !=
null && parentInventory.
Owner !=
null)
1742 CurrentHull = character.AnimController.CurrentHull;
1744 else if (parentInventory.
Owner is
Item item)
1765 private void RefreshRootContainer()
1767 Item newRootContainer =
null;
1768 inWaterProofContainer =
false;
1776 rootContainer = rootContainer.
Container;
1777 if (rootContainer ==
this)
1779 DebugConsole.ThrowError($
"Invalid container hierarchy: \"{Prefab.Identifier}\" was contained inside itself!\n{Environment.StackTrace.CleanupStackTrace()}");
1780 rootContainer =
null;
1783 inWaterProofContainer |= rootContainer.
WaterProof;
1785 newRootContainer = rootContainer;
1793 containedItem.RefreshRootContainer();
1798 private void RefreshInWaterProofContainer()
1800 inWaterProofContainer =
false;
1801 if (container ==
null) {
return; }
1802 if (container.
WaterProof || container.inWaterProofContainer)
1804 inWaterProofContainer =
true;
1808 containedItem.RefreshInWaterProofContainer();
1820 var itemContainer = GetComponent<ItemContainer>();
1821 if (itemContainer !=
null && !itemContainer.HasAccess(character)) {
return false; }
1823 if (GetComponent<Pickable>() is { CanBePicked:
false }) {
return false; }
1839 if (parentInventory !=
null)
1841 if (predicate(parentInventory))
1843 return parentInventory;
1845 if (parentInventory.
Owner is
Item owner)
1847 return owner.FindParentInventory(predicate);
1863 AddTag(tag.ToIdentifier());
1868 if (tags.Contains(tag)) {
return; }
1874 if (!tags.Contains(tag)) {
return; }
1880 if (tag ==
null) {
return true; }
1881 return tags.Contains(tag) || base.Prefab.Tags.Contains(tag);
1886 if (identifiersOrTags ==
null) {
return false; }
1888 foreach (Identifier tag
in identifiersOrTags)
1890 if (
HasTag(tag)) {
return true; }
1897 ReplaceTag(tag.ToIdentifier(), newTag.ToIdentifier());
1902 if (!tags.Contains(tag)) {
return; }
1912 public bool HasTag(IEnumerable<Identifier> allowedTags)
1914 if (allowedTags ==
null)
return true;
1915 foreach (Identifier tag
in allowedTags)
1917 if (tags.Contains(tag))
return true;
1935 return container?.container !=
null && container.container.
ConditionalMatches(conditional, checkContainer:
false);
1937 return container !=
null && container.
ConditionalMatches(conditional, checkContainer:
false);
1942 if (!conditional.
Matches(
this)) {
return false; }
1949 if (!conditional.
Matches(component)) {
return false; }
1960 if (!hasStatusEffectsOfType[(
int)type]) {
return; }
1962 foreach (
StatusEffect effect
in statusEffectLists[type])
1964 ApplyStatusEffect(effect, type, deltaTime, character, limb, useTarget, isNetworkEvent, checkCondition:
false, worldPosition);
1968 readonly List<ISerializableEntity> targets =
new List<ISerializableEntity>();
1973 if (!isNetworkEvent && checkCondition)
1977 if (effect.
type != type) {
return; }
2000 targets.Add(containedItem);
2007 if (targets.Count > 0)
2016 targets.Add(serializableTarget);
2019 if (!hasTargets) {
return; }
2030 targets.Add(pobject);
2034 if (character !=
null)
2044 targets.Add(character);
2049 targets.AddRange(character.AnimController.Limbs);
2053 foreach (var characterLimb
in character.AnimController.Limbs)
2055 if (effect.
targetLimbs.Contains(characterLimb.type)) { targets.Add(characterLimb); }
2066 effect.
Apply(type, deltaTime,
this, targets, worldPosition);
2077 if (damageAmount >=
Prefab.OnDamagedThreshold)
2085 private void SetCondition(
float value,
bool isNetworkEvent,
bool executeEffects =
true)
2087 if (!isNetworkEvent)
2091 if (!MathUtils.IsValid(value)) {
return; }
2097 float diff = value - condition;
2098 if (GetComponent<Door>() is
Door door && door.
IsStuck && diff < 0)
2102 float prevStuck = door.Stuck;
2104 if (door.IsStuck) {
return; }
2106 float damageReduction = dmg - prevStuck;
2107 if (damageReduction < 0) {
return; }
2108 value -= damageReduction;
2111 condition = MathHelper.Clamp(value, 0.0f,
MaxCondition);
2113 if (MathUtils.NearlyEqual(prevCondition, value, epsilon: 0.000001f)) {
return; }
2117 bool wasPreviousConditionChanged =
false;
2118 if (condition == 0.0f && prevCondition > 0.0f)
2121 flagChangedConnections(connections);
2131 if (Screen.Selected == GameMain.SubEditorScreen) {
return; }
2134 SetPreviousCondition();
2140 else if (condition > 0.0f && prevCondition <= 0.0f)
2143 flagChangedConnections(connections);
2148 if (GameMain.NetworkMember !=
null && GameMain.NetworkMember.IsServer)
2150 bool needsConditionUpdate =
false;
2151 if (!MathUtils.NearlyEqual(lastSentCondition, condition) && (condition <= 0.0f || condition >=
MaxCondition))
2154 sendConditionUpdateTimer = 0.0f;
2155 needsConditionUpdate =
true;
2157 else if (Math.Abs(lastSentCondition - condition) > 1.0f || wasInFullCondition !=
IsFullCondition)
2159 needsConditionUpdate =
true;
2161 if (needsConditionUpdate && !itemsWithPendingConditionUpdates.Contains(
this))
2163 itemsWithPendingConditionUpdates.Add(
this);
2167 if (!wasPreviousConditionChanged)
2169 SetPreviousCondition();
2172 void SetPreviousCondition()
2174 LastConditionChange = condition - prevCondition;
2175 ConditionLastUpdated = Timing.TotalTime;
2176 prevCondition = condition;
2177 wasPreviousConditionChanged =
true;
2180 static void flagChangedConnections(Dictionary<string, Connection> connections)
2182 if (connections ==
null) {
return; }
2203 MaxCondition =
Prefab.Health * healthMultiplier * conditionMultiplierCampaign * maxRepairConditionMultiplier * (1.0f +
GetQualityModifier(Items.Components.Quality.StatType.Condition));
2208 private bool IsInWater()
2219 if (!itemsWithPendingConditionUpdates.Contains(
this)) {
return; }
2220 SendPendingNetworkUpdatesInternal();
2221 itemsWithPendingConditionUpdates.Remove(
this);
2224 private void SendPendingNetworkUpdatesInternal()
2227 lastSentCondition = condition;
2228 sendConditionUpdateTimer = NetConfig.ItemConditionUpdateInterval;
2239 for (
int i = 0; i < itemsWithPendingConditionUpdates.Count; i++)
2241 var item = itemsWithPendingConditionUpdates[i];
2242 if (item ==
null || item.Removed)
2244 itemsWithPendingConditionUpdates.RemoveAt(i--);
2247 if (item.Submarine is { Loading: true }) {
continue; }
2249 item.sendConditionUpdateTimer -= deltaTime;
2250 if (item.sendConditionUpdateTimer <= 0.0f)
2252 item.SendPendingNetworkUpdatesInternal();
2253 itemsWithPendingConditionUpdates.RemoveAt(i--);
2258 private bool isActive =
true;
2264 if (impactQueue !=
null)
2266 while (impactQueue.TryDequeue(out
float impact))
2268 HandleCollision(impact);
2271 if (isDroppedStackOwner &&
body !=
null)
2273 foreach (var item
in droppedStack)
2277 item.body.Enabled =
false;
2288 var containedEffectType = parentInventory ==
null ?
ActionType.OnNotContained :
ActionType.OnContained;
2293 for (
int i = 0; i < updateableComponents.Count; i++)
2303 bool shouldBeActive =
true;
2308 shouldBeActive =
false;
2316 bool shouldBeActive =
false;
2321 shouldBeActive =
true;
2338 ic.WasSecondaryUsed =
false;
2342 if (condition <= 0.0f)
2348 ic.
Update(deltaTime, cam);
2365 bool needsWaterCheck = hasInWaterStatusEffects || hasNotInWaterStatusEffects;
2368 System.Diagnostics.Debug.Assert(
body.
FarseerBody.FixtureList !=
null);
2382 needsWaterCheck =
true;
2383 UpdateNetPosition(deltaTime);
2391 if (needsWaterCheck)
2393 bool wasInWater = inWater;
2394 inWater = !inWaterProofContainer && IsInWater();
2401 if (GetComponent<Projectile>() is not { IsActive:
true })
2408 if ((hasInWaterStatusEffects || hasNotInWaterStatusEffects) && condition > 0.0f)
2412 if (inWaterProofContainer && !hasNotInWaterStatusEffects)
2414 needsWaterCheck =
false;
2418 if (!needsWaterCheck &&
2419 updateableComponents.Count == 0 &&
2421 !hasStatusEffectsOfType[(
int)
ActionType.Always] &&
2422 !hasStatusEffectsOfType[(
int)containedEffectType] &&
2426 positionBuffer.Clear();
2433 partial
void Splash();
2437 if (
body ==
null) {
return; }
2440 var projectile = GetComponent<Projectile>();
2441 if (projectile?.StickTarget !=
null)
2443 if (projectile?.StickTarget.UserData is
Limb limb && limb.
character !=
null)
2446 currentHull = limb.character.CurrentHull;
2448 else if (projectile.StickTarget.UserData is
Structure structure)
2453 else if (projectile.StickTarget.UserData is
Item targetItem)
2456 currentHull = targetItem.CurrentHull;
2458 else if (projectile.StickTarget.UserData is
Submarine)
2470 if (
Submarine ==
null && prevSub !=
null)
2474 else if (
Submarine !=
null && prevSub ==
null)
2487 if (containedItem ==
null) {
continue; }
2493 rect.X = (int)(displayPos.X -
rect.Width / 2.0f);
2494 rect.Y = (int)(displayPos.Y +
rect.Height / 2.0f);
2500 MathHelper.Clamp(
body.
LinearVelocity.X, -NetConfig.MaxPhysicsBodyVelocity, NetConfig.MaxPhysicsBodyVelocity),
2501 MathHelper.Clamp(
body.
LinearVelocity.Y, -NetConfig.MaxPhysicsBodyVelocity, NetConfig.MaxPhysicsBodyVelocity));
2504 transformDirty =
false;
2510 private void ApplyWaterForces()
2517 float forceFactor = 1.0f;
2524 forceFactor = Math.Min((waterLevel -
Position.Y) /
rect.Height, 1.0f);
2525 if (forceFactor <= 0.0f) {
return; }
2538 Vector2 frontVel =
body.
FarseerBody.GetLinearVelocityFromLocalPoint(localFront);
2540 float speed = frontVel.Length();
2544 Vector2 dragVec = -frontVel / speed * drag;
2554 Vector2 buoyancy = -GameMain.World.Gravity * forceFactor * volume * Physics.NeutralDensity;
2566 private bool OnCollision(Fixture f1, Fixture f2, Contact contact)
2568 if (transformDirty) {
return false; }
2570 var projectile = GetComponent<Projectile>();
2571 if (projectile !=
null)
2575 if (f2.CollisionCategories == Physics.CollisionCharacter) {
return false; }
2576 if (projectile.IgnoredBodies !=
null && projectile.IgnoredBodies.Contains(f2.Body)) {
return false; }
2577 if (projectile.ShouldIgnoreSubmarineCollision(f2, contact)) {
return false; }
2580 if (GameMain.GameSession ==
null || GameMain.GameSession.RoundDuration > 1.0f)
2582 contact.GetWorldManifold(out Vector2 normal, out _);
2583 if (contact.FixtureA.Body == f1.Body) { normal = -normal; }
2584 float impact = Vector2.Dot(f1.Body.LinearVelocity, -normal);
2585 impactQueue ??=
new ConcurrentQueue<float>();
2586 impactQueue.Enqueue(impact);
2594 private void HandleCollision(
float impact)
2596 OnCollisionProjSpecific(impact);
2597 if (GameMain.NetworkMember is { IsClient: true }) {
return; }
2601 foreach (StatusEffect effect
in statusEffectLists[
ActionType.OnImpact])
2606 GameMain.Server?.CreateEntityEvent(
this,
new ApplyStatusEffectEventData(
ActionType.OnImpact));
2612 if (contained.body !=
null) { contained.HandleCollision(impact); }
2616 partial
void OnCollisionProjSpecific(
float impact);
2618 public override void FlipX(
bool relativeToSub)
2621 base.FlipX(relativeToSub);
2629 if (
Prefab.AllowRotatingInEditor)
2634 if (
Prefab.CanSpriteFlipX)
2642 component.
FlipX(relativeToSub);
2647 public override void FlipY(
bool relativeToSub)
2650 base.FlipY(relativeToSub);
2658 if (
Prefab.AllowRotatingInEditor)
2663 if (
Prefab.CanSpriteFlipY)
2671 component.
FlipY(relativeToSub);
2681 List<T> connectedComponents =
new List<T>();
2685 HashSet<Connection> alreadySearched =
new HashSet<Connection>();
2686 GetConnectedComponentsRecursive(alreadySearched, connectedComponents, allowTraversingBackwards: allowTraversingBackwards);
2687 return connectedComponents;
2691 if (connectionPanel ==
null) {
return connectedComponents; }
2693 foreach (
Connection c
in connectionPanel.Connections)
2695 if (connectionFilter !=
null && !connectionFilter.Invoke(c)) {
continue; }
2698 var component = recipient.
Item.GetComponent<T>();
2699 if (component !=
null && !connectedComponents.Contains(component))
2701 connectedComponents.Add(component);
2706 return connectedComponents;
2709 private void GetConnectedComponentsRecursive<T>(HashSet<Connection> alreadySearched, List<T> connectedComponents,
bool ignoreInactiveRelays =
false,
bool allowTraversingBackwards =
true) where T :
ItemComponent
2712 if (connectionPanel ==
null) {
return; }
2716 if (alreadySearched.Contains(c)) {
continue; }
2717 alreadySearched.Add(c);
2718 GetConnectedComponentsRecursive(c, alreadySearched, connectedComponents, ignoreInactiveRelays, allowTraversingBackwards);
2725 public List<T> GetConnectedComponentsRecursive<T>(
Connection c,
bool ignoreInactiveRelays =
false,
bool allowTraversingBackwards =
true) where T :
ItemComponent
2727 List<T> connectedComponents =
new List<T>();
2728 HashSet<Connection> alreadySearched =
new HashSet<Connection>();
2729 GetConnectedComponentsRecursive(c, alreadySearched, connectedComponents, ignoreInactiveRelays, allowTraversingBackwards);
2731 return connectedComponents;
2734 public static readonly ImmutableArray<(Identifier Input, Identifier Output)>
connectionPairs =
new (Identifier, Identifier)[]
2736 (
"power_in".ToIdentifier(),
"power_out".ToIdentifier()),
2737 (
"signal_in1".ToIdentifier(),
"signal_out1".ToIdentifier()),
2738 (
"signal_in2".ToIdentifier(),
"signal_out2".ToIdentifier()),
2739 (
"signal_in3".ToIdentifier(),
"signal_out3".ToIdentifier()),
2740 (
"signal_in4".ToIdentifier(),
"signal_out4".ToIdentifier()),
2741 (
"signal_in".ToIdentifier(),
"signal_out".ToIdentifier()),
2742 (
"signal_in1".ToIdentifier(),
"signal_out".ToIdentifier()),
2743 (
"signal_in2".ToIdentifier(),
"signal_out".ToIdentifier())
2744 }.ToImmutableArray();
2746 private void GetConnectedComponentsRecursive<T>(
Connection c, HashSet<Connection> alreadySearched, List<T> connectedComponents,
bool ignoreInactiveRelays,
bool allowTraversingBackwards =
true) where T :
ItemComponent
2748 alreadySearched.Add(c);
2749 static IEnumerable<Connection> GetRecipients(
Connection c)
2753 yield
return recipient;
2758 yield
return circuitBoxConnection.
Connection;
2762 foreach (
Connection recipient
in GetRecipients(c))
2764 if (alreadySearched.Contains(recipient)) {
continue; }
2765 var component = recipient.
Item.GetComponent<T>();
2766 if (component !=
null && !connectedComponents.Contains(component))
2768 connectedComponents.Add(component);
2771 var circuitBox = recipient.
Item.GetComponent<CircuitBox>();
2772 if (circuitBox !=
null)
2775 var potentialCbConnection = circuitBox.FindInputOutputConnection(recipient);
2776 if (potentialCbConnection.TryUnwrap(out var cbConnection))
2778 if (cbConnection is CircuitBoxInputConnection inputConnection)
2780 foreach (var connectedTo
in inputConnection.ExternallyConnectedTo)
2782 if (alreadySearched.Contains(connectedTo.Connection)) {
continue; }
2783 CheckRecipient(connectedTo.Connection);
2788 foreach (var connectedFrom
in cbConnection.ExternallyConnectedFrom)
2790 if (alreadySearched.Contains(connectedFrom.Connection) || !allowTraversingBackwards) {
continue; }
2791 CheckRecipient(connectedFrom.Connection);
2796 CheckRecipient(recipient);
2802 if (wifiComponent !=
null && wifiComponent.CanTransmit())
2804 foreach (var wifiReceiver
in wifiComponent.GetTransmittersInRange())
2807 if (receiverConnections ==
null) {
continue; }
2808 foreach (
Connection wifiOutput
in receiverConnections)
2810 if ((wifiOutput.
IsOutput == recipient.
IsOutput) || alreadySearched.Contains(wifiOutput)) {
continue; }
2811 GetConnectedComponentsRecursive(wifiOutput, alreadySearched, connectedComponents, ignoreInactiveRelays, allowTraversingBackwards);
2816 recipient.
Item.GetConnectedComponentsRecursive(recipient, alreadySearched, connectedComponents, ignoreInactiveRelays, allowTraversingBackwards);
2820 if (ignoreInactiveRelays)
2822 var relay = GetComponent<RelayComponent>();
2823 if (relay !=
null && !relay.IsOn) {
return; }
2828 void searchFromAToB(Identifier connectionEndA, Identifier connectionEndB)
2830 if (connectionEndA == c.
Name)
2832 var pairedConnection = c.
Item.
Connections.FirstOrDefault(c2 => c2.Name == connectionEndB);
2833 if (pairedConnection !=
null)
2835 if (alreadySearched.Contains(pairedConnection)) {
return; }
2836 GetConnectedComponentsRecursive(pairedConnection, alreadySearched, connectedComponents, ignoreInactiveRelays, allowTraversingBackwards);
2840 searchFromAToB(input, output);
2841 if (allowTraversingBackwards) { searchFromAToB(output, input); }
2848 var controllers = GetConnectedComponents<Controller>();
2849 bool needsTag = tags !=
null && tags.Value.Length > 0;
2850 if (controllers.None() || (needsTag && controllers.None(c => c.
Item.
HasTag(tags))))
2852 controllers = GetConnectedComponents<Controller>(recursive:
true);
2856 controllers.RemoveAll(c => !c.
Item.
HasTag(tags));
2858 return controllers.Count < 2 ?
2859 controllers.FirstOrDefault() :
2860 controllers.FirstOrDefault(c => c.GetFocusTarget() ==
this) ?? controllers.FirstOrDefault();
2866 return controller !=
null;
2876 if (connections ==
null) {
return; }
2877 if (!connections.TryGetValue(connectionName, out
Connection connection)) {
return; }
2888 if (connections ==
null || connection ==
null) {
return; }
2895 int duplicateRecipients = 0;
2898 if (recipient == connection)
2900 duplicateRecipients++;
2901 if (duplicateRecipients > 2) {
return; }
2913 bool duplicateFound =
false;
2914 foreach (var s
in delayedSignals)
2916 if (s.Connection == connection && s.Signal.source == signal.
source && s.Signal.value == signal.
value && s.Signal.sender == signal.
sender)
2918 duplicateFound =
true;
2922 if (!duplicateFound)
2924 delayedSignals.Add((signal, connection));
2925 CoroutineManager.StartCoroutine(DelaySignal(signal, connection));
2930 if (connection.
Effects !=
null && signal.
value !=
"0" && !
string.IsNullOrEmpty(signal.
value))
2934 if (condition <= 0.0f && effect.
type !=
ActionType.OnBroken) {
continue; }
2943 private IEnumerable<CoroutineStatus> DelaySignal(
Signal signal,
Connection connection)
2949 }
while (CoroutineManager.DeltaTime <= 0.0f);
2951 delayedSignals.Remove((signal, connection));
2954 yield
return CoroutineStatus.Success;
2979 public bool TryInteract(
Character user,
bool ignoreRequiredItems =
false,
bool forceSelectKey =
false,
bool forceUseKey =
false)
2983 var ownerClient =
GameMain.Server.ConnectedClients.Find(c => c.Character == user);
2984 if (ownerClient !=
null)
2986 if (!campaignInteractionTypePerClient.TryGetValue(ownerClient, out campaignInteractionType))
2997 bool picked =
false, selected =
false;
2999 bool hasRequiredSkills =
true;
3000 Skill requiredSkill =
null;
3001 float skillMultiplier = 1;
3006 bool pickHit =
false, selectHit =
false;
3025 else if (forceUseKey)
3038 pickHit = user.IsKeyHit(ic.
PickKey);
3039 selectHit = user.IsKeyHit(ic.
SelectKey);
3046 if (GameSettings.CurrentConfig.KeyMap.Bindings[ic.
PickKey].MouseButton == 0)
3051 if (GameSettings.CurrentConfig.KeyMap.Bindings[ic.
SelectKey].MouseButton == 0)
3064 pickHit = selectHit = GameSettings.CurrentConfig.KeyMap.Bindings[
InputType.Use].MouseButton ==
MouseButton.None ?
3069 if (!pickHit && !selectHit) {
continue; }
3071 bool showUiMsg =
false;
3076 if (!ignoreRequiredItems && !ic.
HasRequiredItems(user, showUiMsg)) {
continue; }
3084 if (tempRequiredSkill !=
null) { requiredSkill = tempRequiredSkill; }
3090 GetComponent<ItemContainer>() !=
null)
3097 if (!picked) {
return false; }
3103 if (user.SelectedItem ==
this)
3105 if (user.IsKeyHit(
InputType.Select) || forceSelectKey)
3107 user.SelectedItem =
null;
3110 else if (user.SelectedSecondaryItem ==
this)
3112 if (user.IsKeyHit(
InputType.Select) || forceSelectKey)
3114 user.SelectedSecondaryItem =
null;
3121 user.SelectedSecondaryItem =
this;
3125 user.SelectedItem =
this;
3133 if (requiredSkill !=
null)
3135 GUI.AddMessage(TextManager.GetWithVariables(
"InsufficientSkills",
3136 (
"[requiredskill]", TextManager.Get(
"SkillName." + requiredSkill.Identifier),
FormatCapitals.Yes),
3137 (
"[requiredlevel]", ((
int)(requiredSkill.Level * skillMultiplier)).ToString(),
FormatCapitals.No)), GUIStyle.Red);
3152 if (ownInventory ==
null) {
return -1; }
3154 float condition = 0f;
3155 float maxCondition = 0f;
3158 condition += item.condition;
3161 if (maxCondition > 0.0f)
3163 return condition / maxCondition;
3179 if (condition <= 0.0f) {
return; }
3181 var should =
GameMain.
LuaCs.
Hook.
Call<
bool?>(
"item.use",
new object[] {
this, user, targetLimb, useTarget });
3183 if (should !=
null && should.Value) {
return; }
3185 bool remove =
false;
3189 bool isControlled =
false;
3194 if (ic.
Use(deltaTime, user))
3214 if (condition <= 0.0f) {
return; }
3218 if (should !=
null && should.Value)
3221 bool remove =
false;
3225 bool isControlled =
false;
3232 ic.WasSecondaryUsed =
true;
3251 if (character.
IsDead) {
return; }
3256 Rand.Range(0.0f, 1.0f) < 0.05f)
3258 GameAnalyticsManager.AddDesignEvent(
"ApplyTreatment:" +
Prefab.
Identifier);
3263 if (HealingCooldown.IsOnCooldown) {
return; }
3265 HealingCooldown.PutOnCooldown();
3274 bool remove =
false;
3283 ic.
PlaySound(conditionalActionType, user);
3288 ic.
ApplyStatusEffects(conditionalActionType, 1.0f, character, targetLimb, useTarget: character, user: user);
3311 if (item ==
this) {
return false; }
3312 bool isCombined =
false;
3315 if (ic.
Combine(item, user)) { isCombined =
true; }
3329 public void Drop(
Character dropper,
bool createNetworkEvent =
true,
bool setTransform =
true)
3331 if (createNetworkEvent)
3348 if (dropper !=
null)
3352 DebugConsole.ThrowError(
3353 "Failed to drop the item \"" +
Name +
"\" (body has been removed"
3354 + (
Removed ?
", item has been removed)" :
")"));
3356 else if (setTransform)
3388 private List<Item> droppedStack;
3391 private bool isDroppedStackOwner;
3401 int itemCount = items.Count();
3403 if (itemCount == 1) {
return; }
3405 if (items.DistinctBy(it => it.Prefab).Count() > 1)
3407 DebugConsole.ThrowError($
"Attempted to create a dropped stack of multiple different items ({string.Join(",
", items.DistinctBy(it => it.Prefab))})\n{Environment.StackTrace}");
3410 if (items.Any(it => it.body ==
null))
3412 DebugConsole.ThrowError($
"Attempted to create a dropped stack for an item with no body ({items.First().Prefab.Identifier})\n{Environment.StackTrace}");
3417 DebugConsole.ThrowError($
"Attempted to create a dropped stack of an empty list of items.\n{Environment.StackTrace}");
3421 int maxStackSize = items.First().Prefab.MaxStackSize;
3422 if (itemCount > maxStackSize)
3424 for (
int i = 0; i < MathF.Ceiling(itemCount / maxStackSize); i++)
3426 int startIndex = i * maxStackSize;
3427 items.ElementAt(startIndex).CreateDroppedStack(items.Skip(startIndex).Take(maxStackSize), allowClientExecute);
3432 droppedStack ??=
new List<Item>();
3433 foreach (
Item item
in items)
3435 if (!droppedStack.Contains(item))
3437 droppedStack.Add(item);
3440 SetDroppedStackItemStates();
3444 server.CreateEntityEvent(
this,
new DroppedStackEventData(droppedStack));
3450 private void RemoveFromDroppedStack(
bool allowClientExecute)
3453 if (droppedStack ==
null) {
return; }
3456 isDroppedStackOwner =
false;
3457 droppedStack.Remove(
this);
3458 SetDroppedStackItemStates();
3459 droppedStack =
null;
3461 if (GameMain.NetworkMember is { IsServer: true } server && !
Removed)
3463 server.CreateEntityEvent(
this,
new DroppedStackEventData(Enumerable.Empty<
Item>()));
3468 private void SetDroppedStackItemStates()
3470 if (droppedStack ==
null) {
return; }
3471 bool isFirst =
true;
3472 foreach (
Item item
in droppedStack)
3474 item.droppedStack = droppedStack;
3475 item.isDroppedStackOwner = isFirst;
3476 if (item.body !=
null)
3478 item.body.Enabled = item.body.PhysEnabled = isFirst;
3481 item.isActive =
true;
3482 item.body.ResetDynamics();
3499 if (stackedItem ==
this) {
continue; }
3500 yield
return stackedItem;
3507 if (stackedItem ==
this) {
continue; }
3508 yield
return stackedItem;
3517 DebugConsole.ThrowError($
"Tried to equip a removed item ({Name}).\n{Environment.StackTrace.CleanupStackTrace()}");
3537 foreach (var itemProperty
in itemProperties)
3539 allProperties.Add((
this, itemProperty));
3544 foreach (var componentProperty
in componentProperties)
3546 allProperties.Add((ic, componentProperty));
3549 return allProperties;
3552 private void WritePropertyChange(
IWriteMessage msg, ChangePropertyEventData extraData,
bool inGameEditableOnly)
3556 var allProperties = inGameEditableOnly ? GetInGameEditableProperties(ignoreConditions:
true) : GetProperties<
Editable>();
3559 if (property !=
null)
3561 if (allProperties.Count > 1)
3563 int propertyIndex = allProperties.FindIndex(p => p.property == property && p.obj == entity);
3564 if (propertyIndex < 0)
3566 throw new Exception($
"Could not find the property \"{property.Name}\" in \"{entity.Name ?? "null"}\"");
3571 object value =
property.GetValue(entity);
3572 if (value is
string stringVal)
3576 else if (value is Identifier idValue)
3580 else if (value is
float floatVal)
3584 else if (value is
int intVal)
3588 else if (value is
bool boolVal)
3592 else if (value is Color color)
3599 else if (value is Vector2 vector2)
3604 else if (value is Vector3 vector3)
3610 else if (value is Vector4 vector4)
3617 else if (value is Point point)
3622 else if (value is Rectangle
rect)
3629 else if (value is Enum)
3633 else if (value is
string[] a)
3636 for (
int i = 0; i < a.Length; i++)
3643 throw new NotImplementedException(
"Serializing item properties of the type \"" + value.GetType() +
"\" not supported");
3648 throw new ArgumentException(
"Failed to write propery value - property \"" + (property ==
null ?
"null" : property.Name) +
"\" is not serializable.");
3652 private List<(
object obj, SerializableProperty property)> GetInGameEditableProperties(
bool ignoreConditions =
false)
3654 if (ignoreConditions)
3656 return GetProperties<ConditionallyEditable>().Union(GetProperties<InGameEditable>()).ToList();
3660 return GetProperties<ConditionallyEditable>()
3662 .Union(GetProperties<InGameEditable>()).ToList();
3666 private void ReadPropertyChange(
IReadMessage msg,
bool inGameEditableOnly,
Client sender =
null)
3670 var allProperties = inGameEditableOnly ? GetInGameEditableProperties(ignoreConditions:
true) : GetProperties<
Editable>();
3671 if (allProperties.Count == 0) {
return; }
3673 int propertyIndex = 0;
3674 if (allProperties.Count > 1)
3679 if (propertyIndex >= allProperties.Count || propertyIndex < 0)
3681 throw new Exception($
"Error in ReadPropertyChange. Property index out of bounds (index: {propertyIndex}, property count: {allProperties.Count}, in-game editable only: {inGameEditableOnly})");
3684 bool allowEditing =
true;
3685 object parentObject = allProperties[propertyIndex].obj;
3686 SerializableProperty
property = allProperties[propertyIndex].property;
3689 if (!ic.AllowInGameEditing) { allowEditing =
false; }
3692 if (GameMain.NetworkMember !=
null && GameMain.NetworkMember.IsServer)
3694 bool conditionAllowsEditing =
true;
3697 conditionAllowsEditing = condition.IsEditable(
this);
3700 bool canAccess =
false;
3701 if (
Container?.GetComponent<CircuitBox>() is { } cb &&
3706 canAccess = !cb.Locked;
3713 if (!canAccess || !conditionAllowsEditing)
3715 allowEditing =
false;
3719 var result = GameMain.LuaCs.Hook.Call<
bool?>(
"item.readPropertyChange",
this, property, parentObject, allowEditing, sender);
3720 if (result !=
null && result.Value)
3723 Type type =
property.PropertyType;
3724 string logValue =
"";
3725 if (type == typeof(
string))
3731 property.TrySetValue(parentObject, val);
3734 else if (type == typeof(Identifier))
3737 logValue = val.Value;
3738 if (allowEditing) {
property.TrySetValue(parentObject, val); }
3740 else if (type == typeof(
float))
3743 logValue = val.ToString(
"G", CultureInfo.InvariantCulture);
3744 if (allowEditing) {
property.TrySetValue(parentObject, val); }
3746 else if (type == typeof(
int))
3749 logValue = val.ToString();
3750 if (allowEditing) {
property.TrySetValue(parentObject, val); }
3752 else if (type == typeof(
bool))
3755 logValue = val.ToString();
3756 if (allowEditing) {
property.TrySetValue(parentObject, val); }
3758 else if (type == typeof(Color))
3761 logValue = XMLExtensions.ColorToString(val);
3762 if (allowEditing) {
property.TrySetValue(parentObject, val); }
3764 else if (type == typeof(Vector2))
3767 logValue = XMLExtensions.Vector2ToString(val);
3768 if (allowEditing) {
property.TrySetValue(parentObject, val); }
3770 else if (type == typeof(Vector3))
3773 logValue = XMLExtensions.Vector3ToString(val);
3774 if (allowEditing) {
property.TrySetValue(parentObject, val); }
3776 else if (type == typeof(Vector4))
3779 logValue = XMLExtensions.Vector4ToString(val);
3780 if (allowEditing) {
property.TrySetValue(parentObject, val); }
3782 else if (type == typeof(Point))
3785 logValue = XMLExtensions.PointToString(val);
3786 if (allowEditing) {
property.TrySetValue(parentObject, val); }
3788 else if (type == typeof(Rectangle))
3791 logValue = XMLExtensions.RectToString(val);
3792 if (allowEditing) {
property.TrySetValue(parentObject, val); }
3794 else if (type == typeof(
string[]))
3797 string[] val =
new string[arrayLength];
3798 for (
int i = 0; i < arrayLength; i++)
3804 property.TrySetValue(parentObject, val);
3807 else if (typeof(Enum).IsAssignableFrom(type))
3814 property.TrySetValue(parentObject, Enum.ToObject(type, intVal));
3815 logValue =
property.GetValue(parentObject).ToString();
3821 DebugConsole.ThrowError(
"Failed to convert the int value \"" + intVal +
"\" to " + type, e);
3823 GameAnalyticsManager.AddErrorEventOnce(
3824 "Item.ReadPropertyChange:" +
Name +
":" + type,
3825 GameAnalyticsManager.ErrorSeverity.Warning,
3826 "Failed to convert the int value \"" + intVal +
"\" to " + type +
" (item " +
Name +
")");
3838 if (logPropertyChangeCoroutine !=
null)
3840 CoroutineManager.StopCoroutines(logPropertyChangeCoroutine);
3842 logPropertyChangeCoroutine = CoroutineManager.Invoke(() =>
3844 GameServer.Log($
"{sender.Character?.Name ?? sender.Name} set the value \"{property.Name}\" of the item \"{Name}\" to \"{logValue}\".",
ServerLog.
MessageType.ItemInteraction);
3849 if (GameMain.NetworkMember is { IsServer: true } && parentObject is ISerializableEntity entity)
3851 GameMain.NetworkMember.CreateEntityEvent(
this,
new ChangePropertyEventData(property, entity));
3855 partial
void UpdateNetPosition(
float deltaTime);
3859 return Load(element, submarine, createNetworkEvent:
false, idRemap: idRemap);
3874 if (
string.IsNullOrWhiteSpace(name) && identifier.IsEmpty)
3876 string errorMessage =
"Failed to load an item (both name and identifier were null):\n"+element.ToString();
3877 DebugConsole.ThrowError(errorMessage);
3878 GameAnalyticsManager.AddErrorEventOnce(
"Item.Load:NameAndIdentifierNull", GameAnalyticsManager.ErrorSeverity.Error, errorMessage);
3889 identifier = pendingSwap;
3890 pendingSwap = Identifier.Empty;
3894 if (prefab ==
null) {
return null; }
3897 Vector2 centerPos =
new Vector2(
rect.X +
rect.Width / 2,
rect.Y -
rect.Height / 2);
3898 if (appliedSwap !=
null)
3903 else if (
rect.Width == 0 &&
rect.Height == 0)
3917 if (createNetworkEvent)
3923 foreach (XAttribute attribute
in (appliedSwap?.ConfigElement ?? element).Attributes())
3925 if (!item.SerializableProperties.TryGetValue(attribute.NameAsIdentifier(), out
SerializableProperty property)) {
continue; }
3926 bool shouldBeLoaded =
false;
3927 foreach (var propertyAttribute
in property.Attributes.OfType<
Serialize>())
3931 shouldBeLoaded =
true;
3938 object prevValue =
property.GetValue(item);
3939 property.TrySetValue(item, attribute.Value);
3943 (submarine ==
null || !submarine.Loading))
3945 if (property.Name ==
"Tags" ||
3946 property.Name ==
"Condition" ||
3947 property.Name ==
"Description")
3953 if (!(property.GetValue(item)?.Equals(prevValue) ??
true))
3962 item.ParseLinks(element, idRemap);
3964 bool thisIsOverride = element.GetAttributeBool(
"isoverride",
false);
3968 bool isItemSwap = appliedSwap !=
null;
3969 bool usePrefabValues = thisIsOverride !=
ItemPrefab.
Prefabs.IsOverride(prefab) || isItemSwap;
3970 List<ItemComponent> unloadedComponents =
new List<ItemComponent>(item.components);
3971 foreach (var subElement
in element.Elements())
3973 switch (subElement.Name.ToString().ToLowerInvariant())
3977 var upgradeIdentifier = subElement.GetAttributeIdentifier(
"identifier", Identifier.Empty);
3978 UpgradePrefab upgradePrefab = UpgradePrefab.Find(upgradeIdentifier);
3979 int level = subElement.GetAttributeInt(
"level", 1);
3980 if (upgradePrefab !=
null)
3982 item.AddUpgrade(
new Upgrade(item, upgradePrefab, level, isItemSwap ?
null : subElement));
3986 DebugConsole.AddWarning($
"An upgrade with identifier \"{upgradeIdentifier}\" on {item.Name} was not found. " +
3987 "It's effect will not be applied and won't be saved after the round ends.");
3993 item.StatManager.Load(subElement);
3998 ItemComponent component = unloadedComponents.Find(x => x.Name == subElement.Name.ToString());
3999 if (component ==
null) {
continue; }
4000 component.
Load(subElement, usePrefabValues, idRemap, isItemSwap);
4001 unloadedComponents.Remove(component);
4006 if (usePrefabValues && !isItemSwap)
4009 item.Scale = prefab.ConfigElement.GetAttributeFloat(item.scale,
"scale",
"Scale");
4012 item.Upgrades.ForEach(upgrade => upgrade.ApplyUpgrade());
4014 var availableSwapIds = element.GetAttributeIdentifierArray(
"availableswaps", Array.Empty<Identifier>());
4015 foreach (Identifier swapId
in availableSwapIds)
4018 if (swapPrefab !=
null)
4020 item.AvailableSwaps.Add(swapPrefab);
4024 if (element.GetAttributeBool(
"markedfordeconstruction",
false)) { deconstructItems.Add(item); }
4026 float prevRotation = item.Rotation;
4027 if (element.GetAttributeBool(
"flippedx",
false)) { item.FlipX(
false); }
4028 if (element.GetAttributeBool(
"flippedy",
false)) { item.FlipY(
false); }
4029 item.Rotation = prevRotation;
4031 if (appliedSwap !=
null)
4033 item.SpriteDepth = element.GetAttributeFloat(
"spritedepth", item.SpriteDepth);
4034 item.
SpriteColor = element.GetAttributeColor(
"spritecolor", item.SpriteColor);
4035 item.Rotation = element.GetAttributeFloat(
"rotation", item.Rotation);
4036 item.PurchasedNewSwap = element.GetAttributeBool(
"purchasednewswap",
false);
4038 float scaleRelativeToPrefab = element.GetAttributeFloat(item.scale,
"scale",
"Scale") / oldPrefab.Scale;
4039 item.Scale *= scaleRelativeToPrefab;
4041 if (oldPrefab.SwappableItem !=
null && prefab.SwappableItem !=
null)
4043 Vector2 oldRelativeOrigin = (oldPrefab.SwappableItem.SwapOrigin - oldPrefab.Size / 2) * element.GetAttributeFloat(item.scale,
"scale",
"Scale");
4044 oldRelativeOrigin.Y = -oldRelativeOrigin.Y;
4045 oldRelativeOrigin = MathUtils.RotatePoint(oldRelativeOrigin, -item.RotationRad);
4046 Vector2 oldOrigin = centerPos + oldRelativeOrigin;
4048 Vector2 relativeOrigin = (prefab.SwappableItem.SwapOrigin - prefab.Size / 2) * item.Scale;
4049 relativeOrigin.Y = -relativeOrigin.Y;
4050 relativeOrigin = MathUtils.RotatePoint(relativeOrigin, -item.RotationRad);
4051 Vector2 origin =
new Vector2(
rect.X +
rect.Width / 2,
rect.Y -
rect.Height / 2) + relativeOrigin;
4053 item.rect.Location -= (origin - oldOrigin).ToPoint();
4056 if (item.PurchasedNewSwap && !
string.IsNullOrEmpty(appliedSwap.SwappableItem?.SpawnWithId))
4059 if (container !=
null)
4061 container.
SpawnWithId = appliedSwap.SwappableItem.SpawnWithId;
4079 item.PurchasedNewSwap =
false;
4082 Version savedVersion = submarine?.Info.GameVersion;
4083 if (element.Document?.Root !=
null && element.Document.Root.Name.ToString().Equals(
"gamesession", StringComparison.OrdinalIgnoreCase))
4087 savedVersion =
new Version(element.Document.Root.GetAttributeString(
"version",
"0.0.0.0"));
4090 float prevCondition = item.condition;
4091 if (savedVersion !=
null)
4096 if (element.GetAttribute(
"conditionpercentage") !=
null)
4098 item.condition = element.GetAttributeFloat(
"conditionpercentage", 100.0f) / 100.0f * item.MaxCondition;
4103 item.condition = element.GetAttributeFloat(
"condition", item.condition);
4107 if (item.condition > 0)
4109 bool wasFullCondition = prevCondition >= item.Prefab.Health;
4110 if (wasFullCondition)
4112 item.condition = item.MaxCondition;
4114 item.condition = MathHelper.Clamp(item.condition, 0, item.MaxCondition);
4117 item.lastSentCondition = item.prevCondition = item.condition;
4118 item.RecalculateConditionValues();
4119 item.SetActiveSprite();
4127 item.FullyInitialized =
true;
4132 public override XElement
Save(XElement parentElement)
4134 XElement element =
new XElement(
"Item");
4137 new XAttribute(
"name",
Prefab.OriginalName),
4139 new XAttribute(
"ID",
ID),
4140 new XAttribute(
"markedfordeconstruction", deconstructItems.Contains(
this)));
4150 if (
FlippedX) { element.Add(
new XAttribute(
"flippedx",
true)); }
4151 if (
FlippedY) { element.Add(
new XAttribute(
"flippedy",
true)); }
4155 element.Add(
new XAttribute(
"availableswaps",
string.Join(
',',
AvailableSwaps.Select(s => s.Identifier))));
4158 if (!MathUtils.NearlyEqual(healthMultiplier, 1.0f))
4160 element.Add(
new XAttribute(
"healthmultiplier",
HealthMultiplier.ToString(
"G", CultureInfo.InvariantCulture)));
4170 element.Add(
new XAttribute(
"rect",
4171 (
int)(
rect.X - subPosition.X) +
"," +
4172 (
int)(
rect.Y - subPosition.Y) +
"," +
4173 width +
"," + height));
4178 var saveableLinked =
linkedTo.Where(l => l.ShouldBeSaved && (l.Removed ==
Removed) && (l.Submarine ==
null || l.Submarine.Info.IsOutpost == isOutpost));
4179 element.Add(
new XAttribute(
"linked",
string.Join(
",", saveableLinked.Select(l => l.ID.ToString()))));
4191 upgrade.Save(element);
4194 statManager?.Save(element);
4196 element.Add(
new XAttribute(
"conditionpercentage",
ConditionPercentage.ToString(
"G", CultureInfo.InvariantCulture)));
4198 var conditionAttribute = element.GetAttribute(
"condition");
4199 if (conditionAttribute !=
null) { conditionAttribute.Remove(); }
4201 parentElement.Add(element);
4208 var holdable = GetComponent<Holdable>();
4209 bool wasAttached = holdable?.Attached ??
false;
4215 components.ForEach(c => c.Reset());
4218 holdable.AttachToWall();
4238 base.ShallowRemove();
4259 DebugConsole.ThrowError(
"Attempting to remove an already removed item (" +
Name +
")\n" + Environment.StackTrace.CleanupStackTrace());
4262 DebugConsole.Log(
"Removing item " +
Name +
" (ID: " +
ID +
")");
4272 Door door = GetComponent<Door>();
4273 Ladder ladder = GetComponent<Ladder>();
4274 if (door !=
null || ladder !=
null)
4279 if (ladder !=
null && wp.
Ladders == ladder) { wp.
Ladders =
null; }
4283 connections?.Clear();
4285 if (parentInventory !=
null)
4289 characterInventory.RemoveItem(
this, tryEquipFromSameStack:
true);
4295 parentInventory =
null;
4322 if (fixture.Body?.World ==
null) {
continue; }
4323 fixture.Body.
Remove(fixture);
4336 RemoveProjSpecific();
4341 private void RemoveFromLists()
4344 dangerousItems.Remove(
this);
4345 repairableItems.Remove(
this);
4346 sonarVisibleItems.Remove(
this);
4347 cleanableItems.Remove(
this);
4348 deconstructItems.Remove(
this);
4349 RemoveFromDroppedStack(allowClientExecute:
true);
4352 partial
void RemoveProjSpecific();
4357 List<Item> list =
new List<Item>(
ItemList);
4358 foreach (
Item item
in list)
bool NeedsUpdate
Does the AI target do something that requires Update() to be called (e.g. static targets don't need t...
LocalizedString SonarLabel
void Update(float deltaTime)
AbilityApplyTreatment(Character user, Character target, Item item, Limb limb)
Attacks are used to deal damage to characters, structures and items. They can be defined in the weapo...
float GetItemDamage(float deltaTime, float multiplier=1)
static bool BlocksInteraction(InteractionType interactionType)
bool CanInteractWith(Character c, float maxDist=200.0f, bool checkVisibility=true, bool skipDistanceCheck=false)
override Vector2? SimPosition
Item????????? SelectedItem
The primary selected item. It can be any device that character interacts with. This excludes items li...
static readonly List< Character > CharacterList
static Character? Controlled
Item SelectedSecondaryItem
The secondary selected item. It's an item other than a device (see SelectedItem), e....
readonly ContentPath Path
float GetAttributeFloat(string key, float def)
Rectangle GetAttributeRect(string key, in Rectangle def)
XAttribute? GetAttribute(string name)
Identifier GetAttributeIdentifier(string key, string def)
static CoroutineStatus Running
static EntitySpawner Spawner
const ushort NullEntityID
readonly ushort ID
Unique, but non-persistent identifier. Stays the same if the entities are created in the exactly same...
void AddItemToRemoveQueue(Item item)
static GameSession?? GameSession
static SubEditorScreen SubEditorScreen
static NetworkMember NetworkMember
BallastFloraBehavior BallastFlora
static Hull FindHull(Vector2 position, Hull guess=null, bool useWorldCoordinates=true, bool inclusive=true)
Returns the hull which contains the point (or null if it isn't inside any)
void ApplyFlowForces(float deltaTime, Item item)
ushort GetOffsetId(XElement element)
void CreateNetworkEvent()
virtual IEnumerable< Item > AllItems
All items contained in the inventory. Stacked items are returned as individual instances....
virtual void RemoveItem(Item item)
int FindIndex(Item item)
Find the index of the first slot the item is contained in.
IEnumerable< Item > GetItemsAt(int index)
Get all the item stored in the specified inventory slot. Can return more than one item if the slot co...
readonly ImmutableArray< ItemInventory > OwnInventories
Identifier??????? ContainerIdentifier
Can be used by status effects or conditionals to check what item this item is contained inside
override Vector2? SimPosition
bool IsPlayerTeamInteractable
Checks both NonInteractable and NonPlayerTeamInteractable
IReadOnlyCollection< Identifier > GetTags()
void Drop(Character dropper, bool createNetworkEvent=true, bool setTransform=true)
void Unequip(Character character)
ItemInventory OwnInventory
void ApplyStatusEffect(StatusEffect effect, ActionType type, float deltaTime, Character character=null, Limb limb=null, Entity useTarget=null, bool isNetworkEvent=false, bool checkCondition=true, Vector2? worldPosition=null)
void RecalculateConditionValues()
Recalculates the item's maximum condition, condition percentage and whether it's in full condition....
bool NonPlayerTeamInteractable
Use IsPlayerInteractable to also check NonInteractable
static IReadOnlyCollection< Item > CleanableItems
Items that may potentially need to be cleaned up (pickable, not attached to a wall,...
void ApplyTreatment(Character user, Character character, Limb targetLimb)
bool IsShootable
Should the item's Use method be called with the "Use" or with the "Shoot" key?
float MaxRepairConditionMultiplier
static void RemoveByPrefab(ItemPrefab prefab)
List< Connection > Connections
override Quad2D GetTransformedQuad()
void DisableDrawableComponent(IDrawableComponent drawable)
readonly HashSet< ItemPrefab > AvailableSwaps
void RemoveTag(Identifier tag)
bool InvulnerableToDamage
BallastFloraBranch Infector
void SendPendingNetworkUpdates()
float GetContainedItemConditionPercentage()
bool IsContained
Can be used by status effects or conditionals to check whether the item is contained inside something
void ReplaceTag(string tag, string newTag)
List<(object obj, SerializableProperty property)> GetProperties< T >()
bool?? Indestructible
Per-instance value - if not set, the value of the prefab is used.
bool ConditionIncreasedRecently
Return true if the condition of this item increased within the last second.
float PositionY
Can be used to move the item from XML (e.g. to correct the positions of items whose sprite origin has...
bool RequireAimToSecondaryUse
If true, the user has to hold the "aim" key before secondary use is registered. True by default.
string? DescriptionTag
Can be used to set a localized description via StatusEffects
void EnableDrawableComponent(IDrawableComponent drawable)
void ApplyStatusEffects(ActionType type, float deltaTime, Character character=null, Limb limb=null, Entity useTarget=null, bool isNetworkEvent=false, Vector2? worldPosition=null)
Executes all StatusEffects of the specified type. Note that condition checks are ignored here: that s...
bool TryFindController(out Controller controller, ImmutableArray< Identifier >? tags=null)
new float? SightRange
Can be used by status effects or conditionals to modify the sight range
Inventory ParentInventory
bool ConditionalMatches(PropertyConditional conditional)
IEnumerable< Item > GetStackedItems()
Returns this item and all the other items in the stack (either in the same inventory slot,...
IEnumerable< InvSlotType > AllowedSlots
void ReplaceTag(Identifier tag, Identifier newTag)
bool UseInHealthInterface
ItemStatManager StatManager
bool? HasBallastFloraInHull
void ResetWaterDragCoefficient()
Removes the override value -> falls back to using the original value defined in the xml.
override void Move(Vector2 amount, bool ignoreContacts=true)
List< Repairable > Repairables
float OffsetOnSelectedMultiplier
IReadOnlyList< ISerializableEntity > AllPropertyObjects
void SendSignal(Signal signal, Connection connection)
void SetTransform(Vector2 simPosition, float rotation, bool findNewHull=true, bool setPrevTransform=true)
override void OnMapLoaded()
void Use(float deltaTime, Character user=null, Limb targetLimb=null, Entity useTarget=null, Character userForOnUsedEvent=null)
float??? HullOxygenPercentage
static void UpdateHulls()
goes through every item and re-checks which hull they are in
override Vector2? Position
override void FlipX(bool relativeToSub)
Flip the entity horizontally
float?? WaterDragCoefficient
override void Update(float deltaTime, Camera cam)
void SetContainedItemPositions()
Rectangle TransformTrigger(Rectangle trigger, bool world=false)
override void FlipY(bool relativeToSub)
Flip the entity vertically
IEnumerable< T > GetComponents< T >()
readonly XElement StaticBodyConfig
void AssignCampaignInteractionType(CampaignMode.InteractionType interactionType, IEnumerable< Client > targetClients=null)
bool IsInteractable(Character character)
Returns interactibility based on whether the character is on a player team
bool IgnoreByAI(Character character)
bool IsOwnedBy(Entity entity)
Item(Rectangle newRect, ItemPrefab itemPrefab, Submarine submarine, bool callOnItemLoaded=true, ushort id=Entity.NullEntityID)
Creates a new item
Rectangle InteractionRect
bool TryInteract(Character user, bool ignoreRequiredItems=false, bool forceSelectKey=false, bool forceUseKey=false)
bool Illegitimate
Item shouldn't be in the player's inventory. If the guards find it, they will consider it as a theft.
void SecondaryUse(float deltaTime, Character character=null)
static Item Load(ContentXElement element, Submarine submarine, IdRemap idRemap)
bool CanClientAccess(Client c)
AttackResult AddDamage(Character attacker, Vector2 worldPosition, Attack attack, Vector2 impulseDirection, float deltaTime, bool playSound=true)
bool?? SpawnedInCurrentOutpost
void AddComponent(ItemComponent component)
float ConditionPercentageRelativeToDefaultMaxCondition
Condition percentage disregarding MaxRepairConditionMultiplier (i.e. this can go above 100% if the it...
IEnumerable< Item > DroppedStack
static readonly ImmutableArray<(Identifier Input, Identifier Output)> connectionPairs
ItemPrefab PendingItemSwap
bool HasIdentifierOrTags(IEnumerable< Identifier > identifiersOrTags)
bool HasTag(IEnumerable< Identifier > allowedTags)
List< T > GetConnectedComponents< T >(bool recursive=false, bool allowTraversingBackwards=true, Func< Connection, bool > connectionFilter=null)
Note: This function generates garbage and might be a bit too heavy to be used once per frame.
new float? SoundRange
Can be used by status effects or conditionals to modify the sound range
override XElement Save(XElement parentElement)
Character Equipper
Which character equipped this item? May not be the same character as the one who it's equipped on (yo...
bool EditableWhenEquipped
Action< Character > OnDeselect
bool IsInsideTrigger(Vector2 worldPosition)
void CheckCleanable()
Recheck if the item needs to be included in the list of cleanable items
float LastEatenTime
Timing.TotalTimeUnpaused when some character was last eating the item
bool IsInsideTrigger(Vector2 worldPosition, out Rectangle transformedTrigger)
static void UpdatePendingConditionUpdates(float deltaTime)
static IReadOnlyCollection< Item > RepairableItems
Items that have one more more Repairable component
bool? IsClaimedByBallastFlora
bool ConditionalMatches(PropertyConditional conditional, bool checkContainer)
void Equip(Character character)
override MapEntity Clone()
bool FullyInitialized
Has everything in the item been loaded/instantiated/initialized (basically, can be used to check if t...
void RemoveContained(Item contained)
bool HasTag(Identifier tag)
static Item Load(ContentXElement element, Submarine submarine, bool createNetworkEvent, IdRemap idRemap)
Instantiate a new item and load its data from the XML element.
bool RequireAimToUse
If true, the user has to hold the "aim" key before use is registered. False by default.
bool IsContainerPreferred(ItemContainer container, out bool isPreferencesDefined, out bool isSecondary, bool requireConditionRestriction=false)
ContentPath ConfigFilePath
override string Name
Note that this is not a LocalizedString instance, just the current name of the item as a string....
IEnumerable< Item > ContainedItems
static IReadOnlyCollection< Item > DangerousItems
void CreateDroppedStack(IEnumerable< Item > items, bool allowClientExecute)
"Merges" the set of items so they behave as one physical object and can be picked up by clicking once...
static readonly List< Item > ItemList
float PositionUpdateInterval
bool IsSalvageMissionItem
float ConditionPercentage
bool PhysicsBodyActive
Can be used by status effects or conditionals to check if the physics body of the item is active
bool DontCleanUp
Can be set by status effects to prevent bots from cleaning up the item
Inventory FindParentInventory(Func< Inventory, bool > predicate)
float PositionX
Can be used to move the item from XML (e.g. to correct the positions of items whose sprite origin has...
Dictionary< Identifier, SerializableProperty > SerializableProperties
override string ToString()
void SendSignal(Signal signal, string connectionName)
Rectangle DefaultRect
Unscaled rect
List< ItemComponent > Components
Item(ItemPrefab itemPrefab, Vector2 position, Submarine submarine, ushort id=Entity.NullEntityID, bool callOnItemLoaded=true)
List< Connection > LastSentSignalRecipients
A list of connections the last signal sent by this item went through
bool AllowDroppingOnSwapWith(Item otherItem)
Is dropping the item allowed when trying to swap it with the other item
bool StolenDuringRound
Was the item stolen during the current round. Note that it's possible for the items to be found in th...
void SendSignal(string signal, string connectionName)
SpriteEffects SpriteEffects
override void ShallowRemove()
Remove the item so that it doesn't appear to exist in the game world (stop sounds,...
static HashSet< Item > DeconstructItems
Items that have been marked for deconstruction
float? Speed
Can be used by status effects or conditionals to the speed of the item
void AddTag(Identifier tag)
static IReadOnlyCollection< Item > SonarVisibleItems
Items whose ItemPrefab.SonarSize is larger than 0
float GetQualityModifier(Quality.StatType statType)
bool Combine(Item item, Character user)
BodyType??? BodyType
Can be used by StatusEffects to set the type of the body (if the item has one)
CampaignMode.InteractionType CampaignInteractionType
int GetComponentIndex(ItemComponent component)
string????? SonarLabel
Can be used to modify the AITarget's label using status effects
void CreateStatusEvent(bool loadingRound)
List< Fixture > StaticFixtures
Entity GetRootInventoryOwner()
bool HasAccess(Character character)
Used by the AI to check whether they can (in principle) and are allowed (in practice) to interact wit...
string???? OriginalOutpost
bool DisplaySideBySideWhenLinked
Controller FindController(ImmutableArray< Identifier >? tags=null)
static readonly PrefabCollection< ItemPrefab > Prefabs
static ItemPrefab Find(string name, Identifier identifier)
ContentXElement ConfigElement
readonly List< StatusEffect > Effects
void SendSignal(Signal signal)
List< CircuitBoxConnection > CircuitBoxConnections
Circuit box input and output connections that are linked to this connection.
List< Connection > Recipients
Connection(ContentXElement element, ConnectionPanel connectionPanel, IdRemap idRemap)
readonly List< Connection > Connections
The base class for components holding the different functionalities of the item
bool HasRequiredContainedItems(Character user, bool addMessage, LocalizedString msg=null)
Dictionary< Identifier, SerializableProperty > SerializableProperties
PropertyConditional.LogicalOperatorType IsActiveConditionalComparison
virtual bool Use(float deltaTime, Character character=null)
void ApplyStatusEffects(ActionType type, float deltaTime, Character character=null, Limb targetLimb=null, Entity useTarget=null, Character user=null, Vector2? worldPosition=null, float afflictionMultiplier=1.0f)
virtual bool HasRequiredItems(Character character, bool addMessage, LocalizedString msg=null)
virtual bool Select(Character character)
virtual void FlipX(bool relativeToSub)
readonly record struct ItemUseInfo(Item Item, Character User)
virtual void OnItemLoaded()
Called when all the components of the item have been loaded. Use to initialize connections between co...
virtual void Unequip(Character character)
virtual void FlipY(bool relativeToSub)
void ShallowRemove()
Remove the component so that it doesn't appear to exist in the game world (stop sounds,...
Action< bool > OnActiveStateChanged
void PlaySound(ActionType type, Character user=null)
virtual void Drop(Character dropper, bool setTransform=true)
a Character has dropped the item
virtual void OnMapLoaded()
Called when all items have been loaded. Use to initialize connections between items.
readonly Dictionary< ActionType, List< StatusEffect > > statusEffectLists
virtual void UpdateBroken(float deltaTime, Camera cam)
readonly bool InheritStatusEffects
bool InheritParentIsActive
virtual void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap, bool isItemSwap)
List< PropertyConditional > IsActiveConditionals
float DegreeOfSuccess(Character character)
Returns 0.0f-1.0f based on how well the Character can use the itemcomponent
virtual float GetSkillMultiplier()
virtual void Move(Vector2 amount, bool ignoreContacts=false)
virtual bool SecondaryUse(float deltaTime, Character character=null)
virtual bool Combine(Item item, Character user)
virtual bool Pick(Character picker)
a Character has picked the item
virtual void Update(float deltaTime, Camera cam)
void StopSounds(ActionType type)
virtual bool UpdateWhenInactive
virtual void OnScaleChanged()
readonly NamedEvent< ItemUseInfo > OnUsed
virtual void Equip(Character character)
virtual XElement Save(XElement parentElement)
bool HasRequiredSkills(Character character)
static readonly HashSet< Connection > ChangedConnections
readonly Character character
LocalizedString Fallback(LocalizedString fallback, bool useDefaultLanguageIfFound=true)
Use this text instead if the original text cannot be found.
object Call(string name, params object[] args)
bool IsHidden
Is the entity hidden due to HiddenInGame being enabled or the layer the entity is in being hidden?
List< ushort > linkedToID
bool IsLayerHidden
Is the layer this entity is in currently hidden? If it is, the entity is not updated and should do no...
readonly List< MapEntity > linkedTo
readonly List< Upgrade > Upgrades
List of upgrades this item has
static MapEntityPrefab Find(string name, string identifier=null, bool showErrorMessages=true)
Find a matching map entity prefab
override void CreateEntityEvent(INetSerializable entity, NetEntityEvent.IData extraData=null)
void ApplyTorque(float torque)
void ApplyForce(Vector2 force, float maxVelocity=NetConfig.MaxPhysicsBodyVelocity)
bool SetTransform(Vector2 simPosition, float rotation, bool setPrevTransform=true)
Vector2 GetLocalFront(float? spritesheetRotation=null)
Returns the farthest point towards the forward of the body. For capsules and circles,...
Category CollisionCategories
bool SetTransformIgnoreContacts(Vector2 simPosition, float rotation, bool setPrevTransform=true)
readonly ContentFile ContentFile
ContentPackage? ContentPackage
readonly Identifier Identifier
Conditionals are used by some in-game mechanics to require one or more conditions to be met for those...
bool Matches(ISerializableEntity? target)
readonly bool TargetContainer
If set to true, the conditionals defined by this element check against the entity containing the targ...
readonly bool TargetGrandParent
If this and TargetContainer are set to true, the conditionals defined by this element check against t...
readonly string TargetItemComponent
If set to the name of one of the target's ItemComponents, the conditionals defined by this element ch...
static Dictionary< Identifier, SerializableProperty > DeserializeProperties(object obj, XElement element=null)
SerializableProperty(PropertyDescriptor property)
static void UpgradeGameVersion(ISerializableEntity entity, ContentXElement configElement, Version savedVersion)
Upgrade the properties of an entity saved with an older version of the game. Properties that should b...
static Dictionary< Identifier, SerializableProperty > GetProperties(object obj)
static void SerializeProperties(ISerializableEntity obj, XElement element, bool saveIfDefault=false, bool ignoreEditable=false)
void ReloadXML()
Works only if there is a name attribute defined for the sprite. For items and structures,...
StatusEffects can be used to execute various kinds of effects: modifying the state of some entity in ...
bool HasTargetType(TargetType targetType)
bool ShouldWaitForInterval(Entity entity, float deltaTime)
readonly bool AllowWhenBroken
Can the StatusEffect be applied when the item applying it is broken?
void AddNearbyTargets(Vector2 worldPosition, List< ISerializableEntity > targets)
int TargetSlot
Index of the slot the target must be in. Only valid when targeting a Contained item.
readonly LimbType[] targetLimbs
Which types of limbs this effect can target? Only valid when targeting characters or limbs.
readonly ImmutableHashSet< Identifier > TargetIdentifiers
Identifier(s), tag(s) or species name(s) of the entity the effect can target. Null if there's no iden...
virtual void Apply(ActionType type, float deltaTime, Entity entity, ISerializableEntity target, Vector2? worldPosition=null)
Submarine(SubmarineInfo info, bool showErrorMessages=true, Func< Submarine, List< MapEntity >> loadEntities=null, IdRemap linkedRemap=null)
static void ForceVisibilityRecheck()
static bool RectContains(Rectangle rect, Vector2 pos, bool inclusive=false)
Vector2 HiddenSubPosition
override Vector2 SimPosition
static List< WayPoint > WayPointList
bool IsEditable(ISerializableEntity entity)
Interface for entities that the clients can send events to the server
UInt32 ReadVariableUInt32()
Identifier ReadIdentifier()
Interface for entities that handle ServerNetObject.ENTITY_POSITION
void WriteString(string val)
void WriteBoolean(bool val)
void WriteIdentifier(Identifier val)
void WriteVariableUInt32(UInt32 val)
void WriteInt32(Int32 val)
void WriteSingle(Single val)
ActionType
ActionTypes define when a StatusEffect is executed.