Client LuaCsForBarotrauma
Throwable.cs
2 using Microsoft.Xna.Framework;
3 using System.Linq;
4 
6 {
8  {
9  enum ThrowState
10  {
11  None,
12  Initiated,
13  Throwing
14  }
15 
16  private const float ThrowAngleStart = -MathHelper.PiOver2, ThrowAngleEnd = MathHelper.PiOver2;
17  private float throwAngle = ThrowAngleStart;
18 
19  private bool midAir;
20 
21  private ThrowState throwState;
22 
23 
24  //continuous collision detection is used while the item is moving faster than this
25  const float ContinuousCollisionThreshold = 5.0f;
26 
28  {
29  get;
30  private set;
31  }
32 
33  [Serialize(1.0f, IsPropertySaveable.No, description: "The impulse applied to the physics body of the item when thrown. Higher values make the item be thrown faster.")]
34  public float ThrowForce { get; set; }
35 
37  : base(item, element)
38  {
39  if (aimPos == Vector2.Zero)
40  {
41  aimPos = new Vector2(0.45f, 0.1f);
42  }
43  }
44 
45  public const float WaterDragCoefficient = 0.5f;
46 
47  public override bool Use(float deltaTime, Character character = null)
48  {
49  //actual throwing logic is handled in Update
50  return characterUsable || character == null;
51  }
52 
53  public override bool SecondaryUse(float deltaTime, Character character = null)
54  {
55  //actual throwing logic is handled in Update - SecondaryUse only triggers when the item is thrown
56  return false;
57  }
58 
59  public override void Drop(Character dropper, bool setTransform = true)
60  {
61  base.Drop(dropper, setTransform);
62  throwState = ThrowState.None;
63  throwAngle = ThrowAngleStart;
65  }
66 
67  public override void UpdateBroken(float deltaTime, Camera cam)
68  {
69  Update(deltaTime, cam);
70  }
71 
72  public override void Update(float deltaTime, Camera cam)
73  {
74  if (!item.body.Enabled) { return; }
75  if (midAir)
76  {
77  if (item.body.FarseerBody.IsBullet)
78  {
79  if (item.body.LinearVelocity.LengthSquared() < ContinuousCollisionThreshold * ContinuousCollisionThreshold)
80  {
81  item.body.FarseerBody.IsBullet = false;
82  }
83  }
84  if (item.body.LinearVelocity.LengthSquared() < 0.01f)
85  {
86  CurrentThrower = null;
87  if (statusEffectLists?.ContainsKey(ActionType.OnImpact) ?? false)
88  {
89  foreach (var statusEffect in statusEffectLists[ActionType.OnImpact])
90  {
91  statusEffect.SetUser(null);
92  }
93  }
94  if (statusEffectLists?.ContainsKey(ActionType.OnBroken) ?? false)
95  {
96  foreach (var statusEffect in statusEffectLists[ActionType.OnBroken])
97  {
98  statusEffect.SetUser(null);
99  }
100  }
101  item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionPlatform;
102  midAir = false;
104  }
105  return;
106  }
107 
108  if (picker == null || picker.Removed || !picker.HeldItems.Contains(item))
109  {
110  IsActive = false;
111  return;
112  }
113 
114  if (throwState != ThrowState.Throwing)
115  {
116  if (picker.IsKeyDown(InputType.Aim))
117  {
118  if (picker.IsKeyDown(InputType.Shoot)) { throwState = ThrowState.Initiated; }
119  }
120  else if (throwState != ThrowState.Initiated)
121  {
122  throwAngle = ThrowAngleStart;
123  }
124  }
125 
126  bool aim = picker.IsKeyDown(InputType.Aim) && picker.CanAim;
127  if (picker.IsDead || !picker.AllowInput)
128  {
129  throwState = ThrowState.None;
130  aim = false;
131  }
132 
133  ApplyStatusEffects(ActionType.OnActive, deltaTime, picker);
134  //return if the status effect got rid of the picker somehow
135  if (picker == null || picker.Removed || !picker.HeldItems.Contains(item))
136  {
137  IsActive = false;
138  return;
139  }
140 
141  if (item.body.Dir != picker.AnimController.Dir) { item.FlipX(relativeToSub: false); }
142 
144 
146 
147  if (throwState != ThrowState.Throwing)
148  {
149  if (aim || throwState == ThrowState.Initiated)
150  {
151  throwAngle = System.Math.Min(throwAngle + deltaTime * 8.0f, ThrowAngleEnd);
152  ac.HoldItem(deltaTime, item, handlePos, itemPos: aimPos, aim: false, throwAngle);
153  if (throwAngle >= ThrowAngleEnd && throwState == ThrowState.Initiated)
154  {
155  throwState = ThrowState.Throwing;
156  }
157  }
158  else
159  {
160  throwAngle = ThrowAngleStart;
161  ac.HoldItem(deltaTime, item, handlePos, itemPos: holdPos, aim: false, holdAngle);
162  }
163  }
164  else
165  {
166  throwAngle = MathUtils.WrapAnglePi(throwAngle - deltaTime * 15.0f);
167  ac.HoldItem(deltaTime, item, handlePos, itemPos: aimPos, aim: false, throwAngle);
168 
169  if (throwAngle < 0)
170  {
171  Vector2 throwVector = Vector2.Normalize(picker.CursorWorldPosition - picker.WorldPosition);
172  //throw upwards if cursor is at the position of the character
173  if (!MathUtils.IsValid(throwVector)) { throwVector = Vector2.UnitY; }
174 
175 #if SERVER
176  GameServer.Log(GameServer.CharacterLogName(picker) + " threw " + item.Name, ServerLog.MessageType.ItemInteraction);
177 #endif
179  if (statusEffectLists?.ContainsKey(ActionType.OnImpact) ?? false)
180  {
181  foreach (var statusEffect in statusEffectLists[ActionType.OnImpact])
182  {
183  statusEffect.SetUser(CurrentThrower);
184  }
185  }
186  if (statusEffectLists?.ContainsKey(ActionType.OnBroken) ?? false)
187  {
188  foreach (var statusEffect in statusEffectLists[ActionType.OnBroken])
189  {
190  statusEffect.SetUser(CurrentThrower);
191  }
192  }
193 
194  item.Drop(CurrentThrower, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer);
196  item.body.ApplyLinearImpulse(throwVector * ThrowForce * item.body.Mass * 3.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
197 
198  //disable platform collisions until the item comes back to rest again
199  item.body.CollidesWith = Physics.CollisionWall | Physics.CollisionLevel;
200  item.body.FarseerBody.IsBullet = true;
201  midAir = true;
202 
203  ac.GetLimb(LimbType.Head)?.body.ApplyLinearImpulse(throwVector * 10.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
204  ac.GetLimb(LimbType.Torso)?.body.ApplyLinearImpulse(throwVector * 10.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
205 
206  Limb rightHand = ac.GetLimb(LimbType.RightHand);
208  throwAngle = ThrowAngleStart;
209  IsActive = true;
210 
211  if (GameMain.NetworkMember is { IsServer: true })
212  {
213  GameMain.NetworkMember.CreateEntityEvent(item, new Item.ApplyStatusEffectEventData(ActionType.OnSecondaryUse, this, targetCharacter: CurrentThrower));
214  }
215  if (!(GameMain.NetworkMember is { IsClient: true }))
216  {
217  //Stun grenades, flares, etc. all have their throw-related things handled in "onSecondaryUse"
218  ApplyStatusEffects(ActionType.OnSecondaryUse, deltaTime, character: CurrentThrower, user: CurrentThrower);
219  }
220  throwState = ThrowState.None;
221  }
222  }
223  }
224  }
225 }
void HoldItem(float deltaTime, Item item, Vector2[] handlePos, Vector2 itemPos, bool aim, float holdAngle, float itemAngleRelativeToHoldAngle=0.0f, bool aimMelee=false, Vector2? targetPos=null)
IEnumerable< Item >?? HeldItems
Items the character has in their hand slots. Doesn't return nulls and only returns items held in both...
virtual Vector2 WorldPosition
Definition: Entity.cs:49
Submarine Submarine
Definition: Entity.cs:53
static NetworkMember NetworkMember
Definition: GameMain.cs:190
void Drop(Character dropper, bool createNetworkEvent=true, bool setTransform=true)
void ResetWaterDragCoefficient()
Removes the override value -> falls back to using the original value defined in the xml.
override void FlipX(bool relativeToSub)
Flip the entity horizontally
override string Name
Note that this is not a LocalizedString instance, just the current name of the item as a string....
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)
readonly Dictionary< ActionType, List< StatusEffect > > statusEffectLists
override void UpdateBroken(float deltaTime, Camera cam)
Definition: Throwable.cs:67
override void Update(float deltaTime, Camera cam)
Definition: Throwable.cs:72
override bool SecondaryUse(float deltaTime, Character character=null)
Definition: Throwable.cs:53
override void Drop(Character dropper, bool setTransform=true)
a Character has dropped the item
Definition: Throwable.cs:59
override bool Use(float deltaTime, Character character=null)
Definition: Throwable.cs:47
Throwable(Item item, ContentXElement element)
Definition: Throwable.cs:36
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.
ActionType
ActionTypes define when a StatusEffect is executed.
Definition: Enums.cs:19