Client LuaCsForBarotrauma
Pickable.cs
3 using Microsoft.Xna.Framework;
4 using System;
5 using System.Collections.Generic;
6 using System.Linq;
7 
9 {
11  {
12  protected Character picker;
13 
14  protected List<InvSlotType> allowedSlots;
15 
16  private float pickTimer;
17 
18  private Character activePicker;
19 
20  private CoroutineHandle pickingCoroutine;
21 
22  public virtual bool IsAttached => false;
23 
24  public List<InvSlotType> AllowedSlots
25  {
26  get { return allowedSlots; }
27  }
28 
29  public bool PickingDone => pickTimer >= PickingTime;
30 
32  {
33  get
34  {
35  if (picker != null && picker.Removed)
36  {
37  picker = null;
38  }
39  return picker;
40  }
41  }
42 
43  public Pickable(Item item, ContentXElement element)
44  : base(item, element)
45  {
46  allowedSlots = new List<InvSlotType>();
47 
48  string slotString = element.GetAttributeString("slots", "Any");
49  string[] slotCombinations = slotString.Split(',');
50  foreach (string slotCombination in slotCombinations)
51  {
52  string[] slots = slotCombination.Split('+');
53  InvSlotType allowedSlot = InvSlotType.None;
54  foreach (string slot in slots)
55  {
56  switch (slot.ToLowerInvariant())
57  {
58  case "bothhands":
59  allowedSlot = InvSlotType.LeftHand | InvSlotType.RightHand;
60  break;
61  default:
62  allowedSlot = allowedSlot | (InvSlotType)Enum.Parse(typeof(InvSlotType), slot.Trim());
63  break;
64  }
65  }
66  allowedSlots.Add(allowedSlot);
67  }
68 
69  canBePicked = true;
70  }
71 
72  public override bool Pick(Character picker)
73  {
74  //return if someone is already trying to pick the item
75  if (pickTimer > 0.0f) { return false; }
76  if (PickingTime >= float.MaxValue) { return false; }
77  if (picker == null || picker.Inventory == null) { return false; }
79 
80  if (PickingTime > 0.0f)
81  {
82  var abilityPickingTime = new AbilityItemPickingTime(PickingTime, item.Prefab);
83  picker.CheckTalents(AbilityEffectType.OnItemPicked, abilityPickingTime);
84 
85  if (RequiredItems.ContainsKey(RelatedItem.RelationType.Equipped))
86  {
87  foreach (RelatedItem ri in RequiredItems[RelatedItem.RelationType.Equipped])
88  {
89  foreach (var heldItem in picker.HeldItems)
90  {
91  if (ri.MatchesItem(heldItem))
92  {
93  abilityPickingTime.Value /= 1 + heldItem.Prefab.AddedPickingSpeedMultiplier;
94  }
95  }
96  }
97  }
98 
99  if ((picker.PickingItem == null || picker.PickingItem == item) && PickingTime <= float.MaxValue)
100  {
101 #if SERVER
102  // Set active picker before creating the server event to make sure it's set correctly
103  activePicker = picker;
104  item.CreateServerEvent(this);
105 #endif
106  pickingCoroutine = CoroutineManager.StartCoroutine(WaitForPick(picker, abilityPickingTime.Value));
107  }
108  return false;
109  }
110  else
111  {
112  return OnPicked(picker);
113  }
114  }
115 
116 
117  public virtual bool OnPicked(Character picker)
118  {
119  return OnPicked(picker, pickDroppedStack: true);
120  }
121 
122  public virtual bool OnPicked(Character picker, bool pickDroppedStack)
123  {
124  //if the item has multiple Pickable components (e.g. Holdable and Wearable, check that we don't equip it in hands when the item is worn or vice versa)
125  if (item.GetComponents<Pickable>().Any())
126  {
127  bool alreadyEquipped = false;
128  for (int i = 0; i < picker.Inventory.Capacity; i++)
129  {
130  if (picker.Inventory.GetItemsAt(i).Contains(item))
131  {
132  if (picker.Inventory.SlotTypes[i] != InvSlotType.Any &&
133  !allowedSlots.Any(a => a.HasFlag(picker.Inventory.SlotTypes[i])))
134  {
135  alreadyEquipped = true;
136  break;
137  }
138  }
139  }
140  if (alreadyEquipped) { return false; }
141  }
142  var droppedStack = pickDroppedStack ? item.DroppedStack.ToList() : null;
144  {
145  if (!picker.HeldItems.Contains(item) && item.body != null) { item.body.Enabled = false; }
146  this.picker = picker;
147 
148 
149  for (int i = item.linkedTo.Count - 1; i >= 0; i--)
150  {
151  item.linkedTo[i].RemoveLinked(item);
152  }
153  item.linkedTo.Clear();
154 
156 
157  ApplyStatusEffects(ActionType.OnPicked, 1.0f, picker);
158 #if CLIENT
159  if (!GameMain.Instance.LoadingScreenOpen && picker == Character.Controlled) { SoundPlayer.PlayUISound(GUISoundType.PickItem); }
160  PlaySound(ActionType.OnPicked, picker);
161 #endif
162  if (pickDroppedStack)
163  {
164  foreach (var droppedItem in droppedStack)
165  {
166  if (droppedItem == item) { continue; }
167  droppedItem.GetComponent<Pickable>().OnPicked(picker, pickDroppedStack: false);
168  }
169  }
170  return true;
171  }
172 
173 #if CLIENT
174  if (!GameMain.Instance.LoadingScreenOpen && picker == Character.Controlled) { SoundPlayer.PlayUISound(GUISoundType.PickItemFail); }
175 #endif
176 
177  return false;
178  }
179 
180  private IEnumerable<CoroutineStatus> WaitForPick(Character picker, float requiredTime)
181  {
182  activePicker = picker;
184  pickTimer = 0.0f;
185  while (pickTimer < requiredTime && Screen.Selected != GameMain.SubEditorScreen)
186  {
187  if (!CoroutineManager.Paused)
188  {
189  //cancel if the item is currently selected
190  //attempting to pick does not select the item, so if it is selected at this point, another ItemComponent
191  //must have been selected and we should not keep deattaching (happens when for example interacting with
192  //an electrical component while holding both a screwdriver and a wrench).
194  picker.IsKeyDown(InputType.Aim) ||
196  item.Removed || item.ParentInventory != null)
197  {
199  yield return CoroutineStatus.Success;
200  }
201 
202 #if CLIENT
203  if (requiredTime < float.MaxValue && picker == Character.Controlled)
204  {
206  this,
208  pickTimer / requiredTime,
209  GUIStyle.Red, GUIStyle.Green,
210  !string.IsNullOrWhiteSpace(PickingMsg) ? PickingMsg : this is Door ? "progressbar.opening" : "progressbar.deattaching");
211  }
212 #endif
213  picker.AnimController.UpdateUseItem(!picker.IsClimbing, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((pickTimer / 10.0f) % 0.1f));
214  pickTimer += CoroutineManager.DeltaTime;
215  }
216  yield return CoroutineStatus.Running;
217  }
218 
220  if (!item.Removed)
221  {
222  bool isNotRemote = true;
223 #if CLIENT
224  isNotRemote = !picker.IsRemotePlayer;
225 #endif
226  if (isNotRemote) { OnPicked(picker); }
227  }
228 
229  yield return CoroutineStatus.Success;
230  }
231 
232  protected void StopPicking(Character picker)
233  {
234  if (picker != null)
235  {
237  picker.PickingItem = null;
238  }
239  if (pickingCoroutine != null)
240  {
241  CoroutineManager.StopCoroutines(pickingCoroutine);
242  pickingCoroutine = null;
243  }
244  activePicker = null;
245  pickTimer = 0.0f;
246  }
247 
248  protected void DropConnectedWires(Character character)
249  {
250  Vector2 pos = character == null ? item.SimPosition : character.SimPosition;
251 
252  foreach (ConnectionPanel connectionPanel in item.GetComponents<ConnectionPanel>())
253  {
254  connectionPanel.DisconnectedWires.Clear();
255  foreach (Connection c in connectionPanel.Connections)
256  {
257  foreach (Wire w in c.Wires.ToArray())
258  {
259  if (w == null) continue;
260  w.Item.Drop(character);
261  w.Item.SetTransform(pos, 0.0f);
262  }
263  }
264  }
265  }
266 
267  public override void Drop(Character dropper, bool setTransform = true)
268  {
269  picker ??= dropper;
270 
271  Vector2 bodyDropPos = Vector2.Zero;
272 
273  if (picker == null || picker.Inventory == null)
274  {
276  {
277  bodyDropPos = item.ParentInventory.Owner.SimPosition;
278  item.body?.ResetDynamics();
279  }
280  }
281  else if (!picker.Removed)
282  {
284 
286  bodyDropPos = picker.SimPosition;
287 
289  picker = null;
290  }
291 
292  if (item.body != null && !item.body.Enabled && setTransform)
293  {
294  if (item.body.Removed)
295  {
296  DebugConsole.ThrowError(
297  "Failed to drop the Pickable component of the item \"" + item.Name + "\" (body has been removed"
298  + (item.Removed ? ", item has been removed)" : ")"));
299  }
300  else
301  {
303  item.SetTransform(bodyDropPos, 0.0f);
304  item.body.Enabled = true;
305  }
306  }
307  }
308 
309  public virtual void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData = null)
310  {
311  msg.WriteUInt16(activePicker?.ID ?? (ushort)0);
312  }
313 
314  public virtual void ClientEventRead(IReadMessage msg, float sendingTime)
315  {
316  ushort pickerID = msg.ReadUInt16();
317  if (pickerID == 0)
318  {
319  StopPicking(activePicker);
320  }
321  else
322  {
323  Pick(Entity.FindEntityByID(pickerID) as Character);
324  }
325  }
326  }
327 
329  {
330  public AbilityItemPickingTime(float pickingTime, ItemPrefab itemPrefab)
331  {
332  Value = pickingTime;
333  ItemPrefab = itemPrefab;
334  }
335  public float Value { get; set; }
336  public ItemPrefab ItemPrefab { get; set; }
337  }
338 }
void UpdateUseItem(bool allowMovement, Vector2 handWorldPos)
void CheckTalents(AbilityEffectType abilityEffectType, AbilityObject abilityObject)
bool CanInteractWith(Character c, float maxDist=200.0f, bool checkVisibility=true, bool skipDistanceCheck=false)
bool IsAnySelectedItem(Item item)
Is the item either the primary or the secondary selected item?
HUDProgressBar UpdateHUDProgressBar(object linkedObject, Vector2 worldPosition, float progress, Color emptyColor, Color fullColor, string textTag="")
Creates a progress bar that's "linked" to the specified object (or updates an existing one if there's...
IEnumerable< Item >?? HeldItems
Items the character has in their hand slots. Doesn't return nulls and only returns items held in both...
bool IsRemotePlayer
Is the character controlled by another human player (should always be false in single player)
bool AccessibleWhenAlive
Can the inventory be accessed when the character is still alive
InvSlotType[] SlotTypes
Slot type for each inventory slot. Vanilla package has one type for each slot, although it is technic...
bool AccessibleByOwner
Can the inventory be accessed by the character itself when the character is still alive (only has an ...
bool TryPutItemWithAutoEquipCheck(Item item, Character user, IEnumerable< InvSlotType > allowedSlots=null, bool createNetworkEvent=true)
If there is no room in the generic inventory (InvSlotType.Any), check if the item can be auto-equippe...
string? GetAttributeString(string key, string? def)
static CoroutineStatus Success
Submarine Submarine
Definition: Entity.cs:53
virtual Vector2 SimPosition
Definition: Entity.cs:45
static Entity FindEntityByID(ushort ID)
Find an entity based on the ID
Definition: Entity.cs:204
static SubEditorScreen SubEditorScreen
Definition: GameMain.cs:68
static GameMain Instance
Definition: GameMain.cs:144
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...
void Drop(Character dropper, bool createNetworkEvent=true, bool setTransform=true)
void SetTransform(Vector2 simPosition, float rotation, bool findNewHull=true, bool setPrevTransform=true)
override string Name
Note that this is not a LocalizedString instance, just the current name of the item as a string....
AbilityItemPickingTime(float pickingTime, ItemPrefab itemPrefab)
Definition: Pickable.cs:330
readonly HashSet< Wire > DisconnectedWires
Wires that have been disconnected from the panel, but not removed completely (visible at the bottom o...
The base class for components holding the different functionalities of the item
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)
Dictionary< RelatedItem.RelationType, List< RelatedItem > > RequiredItems
virtual void ServerEventWrite(IWriteMessage msg, Client c, NetEntityEvent.IData extraData=null)
Definition: Pickable.cs:309
virtual void ClientEventRead(IReadMessage msg, float sendingTime)
Definition: Pickable.cs:314
override void Drop(Character dropper, bool setTransform=true)
a Character has dropped the item
Definition: Pickable.cs:267
Pickable(Item item, ContentXElement element)
Definition: Pickable.cs:43
List< InvSlotType > AllowedSlots
Definition: Pickable.cs:25
List< InvSlotType > allowedSlots
Definition: Pickable.cs:14
virtual bool OnPicked(Character picker, bool pickDroppedStack)
Definition: Pickable.cs:122
override bool Pick(Character picker)
a Character has picked the item
Definition: Pickable.cs:72
void StopPicking(Character picker)
Definition: Pickable.cs:232
virtual bool OnPicked(Character picker)
Definition: Pickable.cs:117
void DropConnectedWires(Character character)
Definition: Pickable.cs:248
Interface for entities that the server can send events to the clients
GUISoundType
Definition: GUI.cs:21
ActionType
ActionTypes define when a StatusEffect is executed.
Definition: Enums.cs:26
AbilityEffectType
Definition: Enums.cs:140