4 using FarseerPhysics.Dynamics;
5 using FarseerPhysics.Dynamics.Contacts;
6 using Microsoft.Xna.Framework;
8 using System.Collections.Generic;
9 using System.Collections.Immutable;
11 using System.Xml.Linq;
19 public readonly Vector2 AttachPos;
21 public EventData(Vector2 attachPos)
23 AttachPos = attachPos;
31 private readonly Vector2[] scaledHandlePos;
37 private float swingState;
43 private bool attachable, attached, attachedByDefault;
54 [
Serialize(
true,
IsPropertySaveable.Yes, description:
"Is the item currently able to push characters around? True by default. Only valid if blocksplayers is set to true.")]
66 [
Serialize(
false,
IsPropertySaveable.Yes, description:
"Is the item currently attached to a wall (only valid if Attachable is set to true).")]
78 [
Serialize(
true,
IsPropertySaveable.Yes, description:
"Can the item be pointed to a specific direction or do the characters always hold it in a static pose.")]
85 [
Serialize(
false,
IsPropertySaveable.No, description:
"Should the character adjust its pose when aiming with the item. Most noticeable underwater, where the character will rotate its entire body to face the direction the item is aimed at.")]
92 [
Serialize(
false,
IsPropertySaveable.No, description:
"Use the hand rotation instead of torso rotation for the item hold angle. Enable this if you want the item just to follow with the arm when not aiming instead of forcing the arm to a hold pose.")]
102 get {
return attachable; }
103 set { attachable = value; }
106 [
Serialize(
true,
IsPropertySaveable.No, description:
"Can the item be reattached to walls after it has been deattached (only valid if Attachable is set to true).")]
113 [
Serialize(
false,
IsPropertySaveable.No, description:
"Can the item only be attached in limited amount? Uses permanent stat values to check for legibility.")]
120 [
Serialize(
false,
IsPropertySaveable.No, description:
"Should the item be attached to a wall by default when it's placed in the submarine editor.")]
123 get {
return attachedByDefault; }
124 set { attachedByDefault = value; }
127 [
Serialize(
"0.0,0.0",
IsPropertySaveable.No, description:
"The position the character holds the item at (in pixels, as an offset from the character's shoulder)."+
128 " For example, a value of 10,-100 would make the character hold the item 100 pixels below the shoulder and 10 pixels forwards.")]
131 get {
return ConvertUnits.ToDisplayUnits(
holdPos); }
132 set {
holdPos = ConvertUnits.ToSimUnits(value); }
138 [
Serialize(
"0.0,0.0",
IsPropertySaveable.No, description:
"The position the character holds the item at when aiming (in pixels, as an offset from the character's shoulder)."+
139 " Works similarly as HoldPos, except that the position is rotated according to the direction the player is aiming at. For example, a value of 10,-100 would make the character hold the item 100 pixels below the shoulder and 10 pixels forwards when aiming directly to the right.")]
142 get {
return ConvertUnits.ToDisplayUnits(
aimPos); }
143 set {
aimPos = ConvertUnits.ToSimUnits(value); }
149 [
Editable,
Serialize(0.0f,
IsPropertySaveable.No, description:
"The rotation at which the character holds the item (in degrees, relative to the rotation of the character's hand).")]
155 get {
return MathHelper.ToDegrees(
holdAngle); }
156 set {
holdAngle = MathHelper.ToRadians(value); }
161 [
Editable,
Serialize(0.0f,
IsPropertySaveable.No, description:
"The rotation at which the character holds the item while aiming (in degrees, relative to the rotation of the character's hand).")]
167 get {
return MathHelper.ToDegrees(
aimAngle); }
168 set {
aimAngle = MathHelper.ToRadians(value); }
171 private Vector2 swingAmount;
173 [
Editable,
Serialize(
"0.0,0.0",
IsPropertySaveable.No, description:
"How much the item swings around when aiming/holding it (in pixels, as an offset from AimPos/HoldPos).")]
179 get {
return ConvertUnits.ToDisplayUnits(swingAmount); }
180 set { swingAmount = ConvertUnits.ToSimUnits(value); }
205 [
Editable,
Serialize(
false,
IsPropertySaveable.No, description:
"Should the item swing around when it's being used (for example, when firing a weapon or a welding tool).")]
218 [
Serialize(
false,
IsPropertySaveable.No, description:
"If true, this item can't be used if the character is also holding a ranged weapon.")]
229 : base(
item, element)
239 Physics.CollisionItemBlocking,
240 Physics.CollisionCharacter | Physics.CollisionProjectile)
251 scaledHandlePos =
new Vector2[2];
252 Vector2 previousValue = Vector2.Zero;
253 for (
int i = 1; i < 3; i++)
256 string attributeName =
"handle" + i;
259 var value = attribute !=
null ? ConvertUnits.ToSimUnits(XMLExtensions.ParseVector2(attribute.Value)) : previousValue;
261 previousValue = value;
296 Dictionary<StatTypes, float> statValues =
new Dictionary<StatTypes, float>();
297 foreach (var subElement
in element.GetChildElements(
"statvalue"))
300 float statValue = subElement.GetAttributeFloat(
"value", 0f);
301 if (statValues.ContainsKey(statType))
303 statValues[statType] += statValue;
307 statValues.TryAdd(statType, statValue);
313 private bool OnPusherCollision(Fixture sender, Fixture other, Contact contact)
315 if (other.Body.UserData is
Character character)
318 if (!
CanPush) {
return false; }
319 return character !=
picker;
327 private bool loadedFromInstance;
330 base.Load(componentElement, usePrefabValues, idRemap, isItemSwap);
332 loadedFromInstance =
true;
349 Drop(
true, dropper, setTransform);
352 private void Drop(
bool dropConnectedWires,
Character dropper,
bool setTransform =
true)
355 if (dropConnectedWires)
373 attachTargetCell =
null;
377 if (dropper ==
null || dropper.
Removed) {
return; }
384 if (
item.
body !=
null && setTransform)
388 DebugConsole.ThrowError(
389 "Failed to drop the Holdable component of the item \"" +
item.
Name +
"\" (body has been removed"
390 + (
item.
Removed ?
", item has been removed)" :
")"));
406 if (heldHand !=
null && !heldHand.Removed && arm !=
null && !arm.Removed)
409 Vector2 diff =
new Vector2(
410 (heldHand.SimPosition.X - arm.SimPosition.X) / 2f,
411 (heldHand.SimPosition.Y - arm.SimPosition.Y) / 2.5f);
430 bool inSuitableSlot =
false;
438 inSuitableSlot =
true;
443 if (!inSuitableSlot) {
return; }
450 DebugConsole.ThrowError($
"Attempted to equip a removed item ({item.Name})\n" + Environment.StackTrace.CleanupStackTrace());
457 if (wearable !=
null && !wearable.AllowedSlots.SequenceEqual(
allowedSlots))
502 if (prevEquipper !=
null)
508 if (
picker ==
null) {
return; }
521 Vector2 attachPos = user ==
null ?
item.
WorldPosition : GetAttachPosition(user, useWorldCoordinates:
true);
531 if (!attachable || !attached) {
return true; }
538 if (
item.GetComponent<Planter>() is { } planter && planter.GrowableSeeds.Any(seed => seed !=
null)) {
return false; }
542 if (connectionPanel !=
null && !connectionPanel.AlwaysAllowRewiring && (connectionPanel.Locked || !(
GameMain.
NetworkMember?.ServerSettings?.AllowRewiring ??
true)))
561 DebugConsole.ThrowError($
"Attempted to pick up a removed item ({item.Name})\n" + Environment.StackTrace.CleanupStackTrace());
597 if (base.OnPicked(
picker))
602 if (
GameMain.Server !=
null && attachable)
604 item.CreateServerEvent(
this);
605 if (
picker !=
null && wasAttached)
619 if (!attachable) {
return; }
623 throw new InvalidOperationException($
"Tried to attach an item with no physics body to a wall ({item.Prefab.Identifier}).");
634 if (attachTarget !=
null)
646 attachTargetCell = GetAttachTargetCell(150.0f);
647 if (attachTargetCell !=
null && attachTargetCell.IsDestructible)
649 attachTargetCell.OnDestroyed += () =>
651 if (attachTargetCell !=
null && attachTargetCell.CellType !=
Voronoi2.CellType.Solid)
653 Drop(dropConnectedWires:
true, dropper:
null);
661 if (containedItems !=
null)
663 foreach (
Item contained
in containedItems)
665 if (contained?.body ==
null) {
continue; }
682 if (!attachable) {
return; }
685 attachTargetCell =
null;
715 if (character !=
null)
718 if (!character.IsKeyDown(
InputType.Aim)) {
return false; }
723 if (character?.Info ==
null)
725 DebugConsole.AddWarning(
"Character without CharacterInfo attempting to attach a limited attachable item!");
728 Vector2 attachPos = GetAttachPosition(character, useWorldCoordinates:
true);
734 if (maxAttachableCount == 0)
739 GUI.AddMessage(TextManager.Get(
"itemmsgrequiretraining"), Color.Red);
744 else if (currentlyAttachedCount >= maxAttachableCount)
749 GUI.AddMessage($
"{TextManager.Get("itemmsgtotalnumberlimited
")} ({currentlyAttachedCount}/{maxAttachableCount})", Color.Red);
769 Vector2 attachPos = ConvertUnits.ToSimUnits(GetAttachPosition(character));
770 item.CreateClientEvent(
this,
new EventData(attachPos));
778 item.
SetTransform(ConvertUnits.ToSimUnits(GetAttachPosition(character)), 0.0f, findNewHull:
false);
781 RefreshLightSources(
item);
787 static void RefreshLightSources(
Item item)
792 light.SetLightSourceTransform();
797 RefreshLightSources(containedItem);
808 private Vector2 GetAttachPosition(
Character user,
bool useWorldCoordinates =
false)
813 mouseDiff = mouseDiff.ClampLength(MaxAttachDistance);
816 Vector2 attachPos = userPos + mouseDiff;
821 Vector2 offset =
new Vector2(
828 ConvertUnits.ToSimUnits(user.
Position),
829 ConvertUnits.ToSimUnits(user.
Position + mouseDiff), collisionCategory: Physics.CollisionWall) !=
null)
841 else if (Level.Loaded !=
null)
843 bool edgeFound =
false;
844 foreach (var cell
in Level.Loaded.GetCells(attachPos))
846 if (cell.CellType !=
Voronoi2.CellType.Solid) {
continue; }
847 foreach (var edge
in cell.Edges)
849 if (!edge.IsSolid) {
continue; }
850 if (MathUtils.GetLineSegmentIntersection(edge.Point1, edge.Point2, user.
WorldPosition, attachPos, out Vector2 intersection))
852 attachPos = intersection;
857 if (edgeFound) {
break; }
862 MathUtils.RoundTowardsClosest(attachPos.X + offset.X,
Submarine.GridSize.X),
863 MathUtils.RoundTowardsClosest(attachPos.Y + offset.Y,
Submarine.GridSize.Y)) - offset;
868 if (Level.Loaded ==
null) {
return null; }
871 if (cell.CellType !=
Voronoi2.CellType.Solid) {
continue; }
873 if (diff.LengthSquared() > 0.0001f) { diff = Vector2.Normalize(diff); }
890 if (rangedWeapon !=
null)
893 if (lastProjectile !=
null)
895 return lastProjectile.
Item.GetComponent<
Rope>();
915 if (attachTargetCell ==
null && owner ==
null) {
IsActive =
false; }
952 if (
GetRope() is { SnapWhenNotAimed:
true } rope)
954 if (rope.Item.ParentInventory ==
null)
964 Limb equipLimb =
null;
975 if (equipLimb !=
null && !equipLimb.
Removed)
979 Matrix itemTransfrom = Matrix.CreateRotationZ(equipLimb.
Rotation);
980 Vector2 transformedHandlePos = Vector2.Transform(
handlePos[0] *
item.
Scale, itemTransfrom);
990 swingPos = Vector2.Zero;
993 swingState += deltaTime;
999 swingPos = swingAmount *
new Vector2(
1001 PerlinNoise.GetPerlin(swingState *
SwingSpeed * 0.1f + 0.5f, swingState *
SwingSpeed * 0.1f + 0.5f) - 0.5f);
1020 public override void FlipX(
bool relativeToSub)
1039 if (!attachable) {
return; }
1042 if (!loadedFromInstance)
1044 if (attachedByDefault)
1069 base.RemoveComponentSpecific();
1070 attachTargetCell =
null;
1079 public override XElement
Save(XElement parentElement)
1083 return base.Save(parentElement);
1092 XElement saveElement = base.Save(parentElement);
static StatTypes ParseStatType(string statTypeString, string debugIdentifier)
void HoldItem(float deltaTime, Item item, Vector2[] handlePos, Vector2 itemPos, bool aim, float holdAngle, float itemAngleRelativeToHoldAngle=0.0f, bool aimMelee=false, Vector2? targetPos=null)
Vector2? CursorWorldPosition
bool HasEquippedItem(Item item, InvSlotType? slotType=null, Func< InvSlotType, bool > predicate=null)
override Vector2? SimPosition
CharacterInventory Inventory
static Character? Controlled
bool IsKeyDown(InputType inputType)
override Vector2 Position
readonly AnimController AnimController
IEnumerable< Item >?? HeldItems
Items the character has in their hand slots. Doesn't return nulls and only returns items held in both...
void FlashAllowedSlots(Item item, Color color)
Flash the slots the item is allowed to go in (not taking into account whether there's already somethi...
bool CanBeAutoMovedToCorrectSlots(Item item)
override void RemoveItem(Item item)
bool IsInLimbSlot(Item item, InvSlotType limbSlot)
bool GetAttributeBool(string key, bool def)
XAttribute? GetAttribute(string name)
virtual Vector2 WorldPosition
static SubEditorScreen SubEditorScreen
static NetworkMember NetworkMember
virtual IEnumerable< Item > AllItems
All items contained in the inventory. Stacked items are returned as individual instances....
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...
override Vector2? SimPosition
void Drop(Character dropper, bool createNetworkEvent=true, bool setTransform=true)
ItemInventory OwnInventory
Inventory ParentInventory
void SetTransform(Vector2 simPosition, float rotation, bool findNewHull=true, bool setPrevTransform=true)
override Vector2? Position
override void FlipX(bool relativeToSub)
Flip the entity horizontally
void CheckCleanable()
Recheck if the item needs to be included in the list of cleanable items
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 readonly List< Item > ItemList
Entity GetRootInventoryOwner()
const float DefaultInteractDistance
bool UsageDisabledByRangedWeapon(Character character)
override void FlipX(bool relativeToSub)
float SpriteDepthWhenDropped
bool CanBeAttached(Character user)
override bool SecondaryUse(float deltaTime, Character character=null)
override void Update(float deltaTime, Camera cam)
Holdable(Item item, ContentXElement element)
override void ReceiveSignal(Signal signal, Connection connection)
bool DisableWhenRangedWeaponEquipped
override void UpdateBroken(float deltaTime, Camera cam)
readonly ImmutableDictionary< StatTypes, float > HoldableStatValues
override void Equip(Character character)
override void OnMapLoaded()
Called when all items have been loaded. Use to initialize connections between items.
override bool Pick(Character picker)
a Character has picked the item
override bool OnPicked(Character picker)
override void Drop(Character dropper, bool setTransform=true)
a Character has dropped the item
override void Unequip(Character character)
override void Load(ContentXElement componentElement, bool usePrefabValues, IdRemap idRemap, bool isItemSwap)
override void OnItemLoaded()
Called when all the components of the item have been loaded. Use to initialize connections between co...
override void RemoveComponentSpecific()
override bool Use(float deltaTime, Character character=null)
void UpdateSwingPos(float deltaTime, out Vector2 swingPos)
override XElement Save(XElement parentElement)
bool UseHandRotationForHoldAngle
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)
LocalizedString DisplayMsg
Dictionary< RelatedItem.RelationType, List< RelatedItem > > RequiredItems
void CheckIfNeedsUpdate()
List< InvSlotType > allowedSlots
void DropConnectedWires(Character character)
Projectile LastProjectile
override void Unequip(Character character)
bool SetTransformIgnoreContacts(Vector2 simPosition, float rotation, bool setPrevTransform=true)
void UpdateDrawPosition(bool interpolate=true)
readonly Identifier Identifier
bool IsHoldingToRope
Is attached to something with a rope.
Limb GetLimb(LimbType limbType, bool excludeSevered=true)
Note that if there are multiple limbs of the same type, only the first (valid) limb is returned.
static Structure GetAttachTarget(Vector2 worldPosition)
Checks if there's a structure items can be attached to at the given position and returns it.
static Vector2 GetRelativeSimPosition(ISpatialEntity from, ISpatialEntity to, Vector2? targetWorldPos=null)
static bool RectContains(Rectangle rect, Vector2 pos, bool inclusive=false)
static readonly Vector2 GridSize
static float LastPickedFraction
override Vector2? Position
static Body PickBody(Vector2 rayStart, Vector2 rayEnd, IEnumerable< Body > ignoredBodies=null, Category? collisionCategory=null, bool ignoreSensors=true, Predicate< Fixture > customPredicate=null, bool allowInsideFixture=false)
Interface for entities that the clients can send events to the server
Interface for entities that the server can send events to the clients
ActionType
ActionTypes define when a StatusEffect is executed.
StatTypes
StatTypes are used to alter several traits of a character. They are mostly used by talents.