1 using Microsoft.Xna.Framework;
3 using System.Collections.Generic;
5 using System.Reflection;
9 using MoonSharp.Interpreter;
13 using Microsoft.Xna.Framework.Graphics;
27 void Draw(SpriteBatch spriteBatch,
bool editing,
float itemDepth = -1, Color? overrideColor =
null);
40 private bool isActive;
61 get {
return parent; }
64 if (parent == value) {
return; }
68 if (value !=
null) { value.OnActiveStateChanged += SetActiveState; }
75 [
Serialize(
true,
IsPropertySaveable.No, description:
"If this is a child component of another component, should this component inherit the IsActive state of the parent?")]
115 get {
return isActive; }
133 private bool drawable =
true;
146 get {
return drawable; }
149 if (value == drawable) {
return; }
152 DebugConsole.ThrowError(
"Couldn't make \"" +
this +
"\" drawable (the component doesn't implement the IDrawableComponent interface)");
168 [
Editable,
Serialize(
false,
IsPropertySaveable.No, description:
"Can the item be picked up (or interacted with, if the pick action does something else than picking up the item).")]
175 [
Serialize(
false,
IsPropertySaveable.No, description:
"Should the interface of the item (if it has one) be drawn when the item is equipped.")]
202 [
Serialize(
false,
IsPropertySaveable.No, description:
"Should the item be removed if combining it with an other item causes the condition of this item to drop to 0.")]
209 [
Serialize(
false,
IsPropertySaveable.No, description:
"Can the \"Use\" action of the item be triggered by characters or just other items/StatusEffects.")]
217 [
Serialize(
true,
IsPropertySaveable.No, description:
"Can the properties of the component be edited in-game (only applicable if the component has in-game editable properties)."),
Editable()]
253 [
Editable,
Serialize(
"",
IsPropertySaveable.Yes, translationTextTag:
"ItemMsg", description:
"A text displayed next to the item when it's highlighted (generally instructs how to interact with the item, e.g. \"[Mouse1] Pick up\").")]
266 [
Serialize(0f,
IsPropertySaveable.No, description:
"How useful the item is in combat? Used by AI to decide which item it should use as a weapon. For the sake of clarity, use a value between 0 and 100 (not forced). Note that there's also a generic BotPriority for all item prefabs.")]
281 public readonly NamedEvent<ItemUseInfo>
OnUsed =
new();
293 hasSoundsOfType =
new bool[Enum.GetValues(typeof(
ActionType)).Length];
294 sounds =
new Dictionary<ActionType, List<ItemSound>>();
302 selectKeyStr = ToolBox.ConvertInputType(selectKeyStr);
307 DebugConsole.ThrowError(
"Invalid select key in " + element +
"!", e,
316 pickKeyStr = ToolBox.ConvertInputType(pickKeyStr);
321 DebugConsole.ThrowError(
"Invalid pick key in " + element +
"!", e,
328 string inheritRequiredSkillsFrom = element.
GetAttributeString(
"inheritrequiredskillsfrom",
"");
329 if (!
string.IsNullOrEmpty(inheritRequiredSkillsFrom))
331 var component =
item.
Components.Find(ic => ic.Name.Equals(inheritRequiredSkillsFrom, StringComparison.OrdinalIgnoreCase));
332 if (component ==
null)
334 DebugConsole.ThrowError($
"Error in item \"{item.Name}\" - component \"{name}\" is set to inherit its required skills from \"{inheritRequiredSkillsFrom}\", but a component of that type couldn't be found.",
343 string inheritStatusEffectsFrom = element.
GetAttributeString(
"inheritstatuseffectsfrom",
"");
344 if (!
string.IsNullOrEmpty(inheritStatusEffectsFrom))
347 var component =
item.
Components.Find(ic => ic.Name.Equals(inheritStatusEffectsFrom, StringComparison.OrdinalIgnoreCase));
348 if (component ==
null)
350 DebugConsole.ThrowError($
"Error in item \"{item.Name}\" - component \"{name}\" is set to inherit its StatusEffects from \"{inheritStatusEffectsFrom}\", but a component of that type couldn't be found.",
353 else if (component.statusEffectLists !=
null)
356 foreach (KeyValuePair<
ActionType, List<StatusEffect>> kvp
in component.statusEffectLists)
360 effectList =
new List<StatusEffect>();
363 effectList.AddRange(kvp.Value);
368 foreach (var subElement
in element.Elements())
370 switch (subElement.Name.ToString().ToLowerInvariant())
372 case "activeconditional":
373 case "isactiveconditional":
379 case "requireditems":
382 case "requiredskill":
383 case "requiredskills":
384 if (subElement.GetAttribute(
"name") !=
null)
386 DebugConsole.ThrowError(
"Error in item config \"" +
item.
ConfigFilePath +
"\" - skill requirement in component " + GetType().ToString() +
" should use a skill identifier instead of the name of the skill.",
391 Identifier skillIdentifier = subElement.GetAttributeIdentifier(
"identifier",
"");
396 LoadStatusEffect(subElement);
399 if (LoadElemProjSpecific(subElement)) {
break; }
401 if (ic ==
null) {
break; }
418 if (!
statusEffectLists.TryGetValue(statusEffect.type, out List<StatusEffect> effectList))
420 effectList =
new List<StatusEffect>();
423 effectList.Add(statusEffect);
427 private void SetActiveState(
bool isActive)
434 bool returnEmpty =
false;
454 else if (!allowEmpty)
456 DebugConsole.ThrowError(
"Error in item config \"" +
item.
ConfigFilePath +
"\" - component " + GetType().ToString() +
" requires an item with no identifiers.",
461 public virtual void Move(Vector2 amount,
bool ignoreContacts =
false) { }
520 switch (connection.
Name)
525 if (signal.
value !=
"0")
531 if (signal.
value !=
"0")
549 float transferAmount = Math.Min(
item.
Condition,
this.item.MaxCondition -
this.item.Condition);
551 if (MathUtils.NearlyEqual(transferAmount, 0.0f)) {
return false; }
580 RemoveItem(this.
Item);
611 if (loopingSoundChannel !=
null)
614 loopingSoundChannel =
null;
626 GUI.RemoveFromUpdateList(
GuiFrame,
true);
648 if (loopingSoundChannel !=
null)
651 loopingSoundChannel =
null;
669 => subElement.DoesAttributeReferenceFileNameAlone(
"texture") ? Path.GetDirectoryName(
item.
Prefab.
FilePath) :
string.Empty;
680 float characterLevel = character.GetSkillLevel(skill.
Identifier);
683 insufficientSkill = skill;
687 insufficientSkill =
null;
708 if (requiredSkills.Count == 0)
return 1.0f;
710 if (character ==
null)
712 string errorMsg =
"ItemComponent.DegreeOfSuccess failed (character was null).\n" + Environment.StackTrace.CleanupStackTrace();
713 DebugConsole.ThrowError(errorMsg);
714 GameAnalyticsManager.AddErrorEventOnce(
"ItemComponent.DegreeOfSuccess:CharacterNull", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
718 float skillSuccessSum = 0.0f;
719 for (
int i = 0; i < requiredSkills.Count; i++)
721 float characterLevel = character.
GetSkillLevel(requiredSkills[i].Identifier);
722 skillSuccessSum += (characterLevel - requiredSkills[i].Level);
724 float average = skillSuccessSum / requiredSkills.Count;
726 return ((average + 100.0f) / 2.0f) / 100.0f;
729 public virtual void FlipX(
bool relativeToSub) { }
731 public virtual void FlipY(
bool relativeToSub) { }
751 if (addMessage && !msg.IsNullOrEmpty())
753 GUI.AddMessage(msg, Color.Red);
781 if (!CheckIdCardAccess(relatedItem, idCard))
817 if (character.
Inventory ==
null) {
return false; }
818 bool hasRequiredItems =
false;
819 bool canContinue =
true;
824 canContinue = CheckItems(ri, character.
HeldItems);
825 if (!canContinue) {
break; }
840 if (!hasRequiredItems && addMessage && !msg.IsNullOrEmpty())
842 GUI.AddMessage(msg, Color.Red);
845 return hasRequiredItems;
847 bool CheckItems(
RelatedItem relatedItem, IEnumerable<Item> itemList)
849 bool Predicate(
Item it)
854 if (!CheckIdCardAccess(relatedItem, idCard))
861 bool shouldBreak =
false;
862 bool inEditor =
false;
868 hasRequiredItems =
true;
872 if (!hasRequiredItems)
874 hasRequiredItems = itemList.Any(Predicate);
879 if (itemList.Any(Predicate))
887 if (!hasRequiredItems)
892 if (!hasRequiredItems)
894 if (msg ==
null && !relatedItem.
Msg.IsNullOrEmpty())
896 msg = relatedItem.
Msg;
907 if (!
statusEffectLists.TryGetValue(type, out List<StatusEffect> statusEffects)) {
return; }
910 bool reducesCondition =
false;
914 if (user !=
null) { effect.
SetUser(user); }
922 item.
ApplyStatusEffect(effect, type, deltaTime, c, targetLimb, useTarget, isNetworkEvent:
false, checkCondition:
false, worldPosition);
927 if (reducesCondition && user !=
null && type !=
ActionType.OnBroken)
932 foreach (var brokenEffect
in brokenEffects)
934 brokenEffect.SetUser(user);
940 HintManager.OnStatusEffectApplied(
this, type, character);
946 if (componentElement !=
null)
948 foreach (XAttribute attribute
in componentElement.
Attributes())
951 if (property.OverridePrefabValues ||
953 (isItemSwap && property.GetAttribute<
Editable>() is { TransferToSwappedItem: true }))
955 property.TrySetValue(
this, attribute.Value);
959 OverrideRequiredItems(componentElement);
965 if (guiFrameDragHandle !=
null)
998 type = ReflectionUtils.GetDerivedNonAbstract<
ItemComponent>().Append(typeof(
ItemComponent)).FirstOrDefault(t => t.Name == typeName);
1003 DebugConsole.ThrowError($
"Could not find the component \"{typeName}\" ({item.Prefab.ContentFile.Path})",
1013 DebugConsole.ThrowError($
"Could not find the component \"{typeName}\" ({item.Prefab.ContentFile.Path})", e,
1019 ConstructorInfo constructor;
1024 if (constructor ==
null)
1026 DebugConsole.ThrowError(
1027 $
"Could not find the constructor of the component \"{typeName}\" ({item.Prefab.ContentFile.Path})",
1034 DebugConsole.ThrowError(
1035 $
"Could not find the constructor of the component \"{typeName}\" ({item.Prefab.ContentFile.Path})", e,
1042 object[] lobject =
new object[] {
item, element };
1043 object component = constructor.Invoke(lobject);
1047 catch (TargetInvocationException e)
1049 DebugConsole.ThrowError($
"Error while loading component of the type {type}.", e.InnerException, contentPackage: element.
ContentPackage);
1050 GameAnalyticsManager.AddErrorEventOnce(
1051 $
"ItemComponent.Load:TargetInvocationException{item.Name}{element.Name}",
1052 GameAnalyticsManager.ErrorSeverity.Error,
1053 $
"Error while loading entity of the type {type} ({e.InnerException})\n{Environment.StackTrace.CleanupStackTrace()}");
1059 public virtual XElement
Save(XElement parentElement)
1061 XElement componentElement =
new XElement(
name);
1067 XElement newElement =
new XElement(
"requireditem");
1068 ri.
Save(newElement);
1069 componentElement.Add(newElement);
1074 XElement newElement =
new XElement(
"requireditem");
1075 ri.
Save(newElement);
1076 componentElement.Add(newElement);
1082 parentElement.Add(componentElement);
1083 return componentElement;
1099 bool returnEmptyRequirements =
false;
1103 foreach (var subElement
in element.
Elements())
1105 switch (subElement.Name.ToString().ToLowerInvariant())
1107 case "requireditem":
1108 case "requireditems":
1110 if (newRequiredItem ==
null)
continue;
1112 var prevRequiredItem = prevRequiredItems.ContainsKey(newRequiredItem.
Type) ?
1114 if (prevRequiredItem !=
null)
1116 newRequiredItem.
StatusEffects = prevRequiredItem.StatusEffects;
1117 newRequiredItem.
Msg = prevRequiredItem.Msg;
1118 newRequiredItem.
IsOptional = prevRequiredItem.IsOptional;
1119 newRequiredItem.
IgnoreInEditor = prevRequiredItem.IgnoreInEditor;
1137 msg = TextManager.ParseInputTypes(msg);
1152 => TryExtractEventData(data, out T componentData)
1154 :
throw new Exception($
"Malformed item component state event for {item.Name} " +
1155 $
"(item ID {item.ID}, component type {GetType().Name}): " +
1156 $
"could not extract ComponentData of type {typeof(T).Name}");
1160 componentData =
default;
1163 componentData = nestedData;
1181 ItemCount = itemCount,
1183 RemoveEmpty = removeEmpty,
1184 GetItemPriority = i =>
1186 if (i.ParentInventory?.Owner is
Item)
1189 if (((
Item)i.ParentInventory.Owner).GetComponent<T>() !=
null)
1198 containObjective.
Abandoned += () => aiController.IgnoredItems.Add(container.
Item);
1199 if (dropItemOnDeselected)
1203 if (containObjective ==
null) {
return; }
1214 return containObjective;
void AddSubObjective(AIObjective objective, bool addFirst=false)
Action Deselected
A single shot event. Automatically cleared after launching. Use OnDeselected method for implementing ...
Action Abandoned
A single shot event. Automatically cleared after launching. Use OnAbandoned method for implementing (...
readonly AIObjectiveManager objectiveManager
float GetSkillLevel(string skillIdentifier)
bool CanInteractWith(Character c, float maxDist=200.0f, bool checkVisibility=true, bool skipDistanceCheck=false)
virtual AIController AIController
CharacterInventory Inventory
IEnumerable< Item >?? HeldItems
Items the character has in their hand slots. Doesn't return nulls and only returns items held in both...
readonly? ContentPackage ContentPackage
string? GetAttributeString(string key, string? def)
IEnumerable< XAttribute > Attributes()
ContentPackage? ContentPackage
Identifier NameAsIdentifier()
IEnumerable< ContentXElement > Elements()
static EntitySpawner Spawner
void AddItemToRemoveQueue(Item item)
RectTransform RectTransform
static SubEditorScreen SubEditorScreen
static NetworkMember NetworkMember
virtual IEnumerable< Item > AllItems
All items contained in the inventory. Stacked items are returned as individual instances....
virtual void RemoveItem(Item item)
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 DisableDrawableComponent(IDrawableComponent drawable)
void EnableDrawableComponent(IDrawableComponent drawable)
Inventory ParentInventory
void Use(float deltaTime, Character user=null, Limb targetLimb=null, Entity useTarget=null, Character userForOnUsedEvent=null)
bool IsInteractable(Character character)
Returns interactibility based on whether the character is on a player team
bool IgnoreByAI(Character character)
void AddComponent(ItemComponent component)
ContentPath ConfigFilePath
override string Name
Note that this is not a LocalizedString instance, just the current name of the item as a string....
List< ItemComponent > Components
float? Speed
Can be used by status effects or conditionals to the speed of the item
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
bool TryExtractEventData< T >(NetEntityEvent.IData data, out T componentData)
PropertyConditional.LogicalOperatorType IsActiveConditionalComparison
virtual bool ValidateEventData(NetEntityEvent.IData data)
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 void ShallowRemoveComponentSpecific()
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...
const float CorrectionDelay
bool HasRequiredSkills(Character character, out Skill insufficientSkill)
int ManuallySelectedSound
Which sound should be played when manual sound selection type is selected? Not [Editable] because we ...
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
bool IsEmpty(Character user)
Returns true if the item is lacking required contained items, or if there's nothing with a non-zero c...
CoroutineHandle delayedCorrectionCoroutine
virtual void RemoveComponentSpecific()
virtual bool HasAccess(Character character)
Only checks if any of the Picked requirements are matched (used for checking id card(s))....
ItemComponent(Item item, ContentXElement element)
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)
string GetTextureDirectory(ContentXElement subElement)
const float AIUpdateInterval
readonly bool InheritStatusEffects
bool LockGuiFramePosition
virtual bool DontTransferInventoryBetweenSubs
If enabled, the contents of the item are not transferred when the player transfers items between subs...
readonly List< RelatedItem > DisabledRequiredItems
bool InheritParentIsActive
void SetRequiredItems(ContentXElement element, bool allowEmpty=false)
virtual void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap, bool isItemSwap)
T ExtractEventData< T >(NetEntityEvent.IData data)
float Speed
Can be used by status effects or conditionals to the speed of the item
AIObjectiveContainItem AIContainItems< T >(ItemContainer container, Character character, AIObjective currentObjective, int itemCount, bool equip, bool removeEmpty, bool spawnItemIfNotFound=false, bool dropItemOnDeselected=false)
List< PropertyConditional > IsActiveConditionals
float DegreeOfSuccess(Character character)
Returns 0.0f-1.0f based on how well the Character can use the itemcomponent
virtual bool CrewAIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
true if the operation was completed
float DegreeOfSuccess(Character character, List< Skill > requiredSkills)
Returns 0.0f-1.0f based on how well the Character can use the itemcomponent
virtual float GetSkillMultiplier()
LocalizedString DisplayMsg
virtual bool DisallowSellingItemsFromContainer
If enabled, the items inside any of the item containers on this item cannot be sold at an outpost....
virtual void Move(Vector2 amount, bool ignoreContacts=false)
virtual bool SecondaryUse(float deltaTime, Character character=null)
Dictionary< RelatedItem.RelationType, List< RelatedItem > > RequiredItems
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)
virtual void OnInventoryChanged()
Called when the item has an ItemContainer and the contents inside of it changed.
static ItemComponent Load(ContentXElement element, Item item, bool errorMessages=true)
void StopSounds(ActionType type)
readonly ContentXElement originalElement
virtual void ReceiveSignal(Signal signal, Connection connection)
virtual bool UpdateWhenInactive
virtual void OnScaleChanged()
readonly NamedEvent< ItemUseInfo > OnUsed
readonly List< Skill > RequiredSkills
virtual void Equip(Character character)
virtual XElement Save(XElement parentElement)
bool HasRequiredSkills(Character character)
ImmutableHashSet< Identifier > ContainableItemIdentifiers
bool ContainsItemsWithSameIdentifier(Item item)
Conditionals are used by some in-game mechanics to require one or more conditions to be met for those...
static IEnumerable< PropertyConditional > FromXElement(ContentXElement element, Predicate< XAttribute >? predicate=null)
static Dictionary< Identifier, SerializableProperty > DeserializeProperties(object obj, XElement element=null)
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)
readonly Identifier Identifier
StatusEffects can be used to execute various kinds of effects: modifying the state of some entity in ...
bool ReducesItemCondition()
bool HasTargetType(TargetType targetType)
void SetUser(Character user)
readonly bool AllowWhenBroken
Can the StatusEffect be applied when the item applying it is broken?
float AfflictionMultiplier
static StatusEffect Load(ContentXElement element, string parentDebugName)
int??? SubmarineSpecificIDTag
Vector2 DrawSize
The extents of the sprites or other graphics this component needs to draw. Used to determine which it...
void Draw(SpriteBatch spriteBatch, bool editing, float itemDepth=-1, Color? overrideColor=null)
ActionType
ActionTypes define when a StatusEffect is executed.