Dayz Explorer  1.24.157551 (v105080)
Dayz Code Explorer by Zeroy
weapon_base.c
Go to the documentation of this file.
1 
5 {
6  int m_action;
7  int m_actionType;
8  void AbilityRecord (int a, int at) { m_action = a; m_actionType = at; }
9 };
10 
11 enum WeaponWithAmmoFlags
12 {
14  NONE = 0,
16  CHAMBER = 1,
29 }
30 
32 
38 class Weapon_Base extends Weapon
39 {
41  const int SAMF_DEFAULT = WeaponWithAmmoFlags.CHAMBER | WeaponWithAmmoFlags.MAX_CAPACITY_MAG;
43  const int SAMF_RNG = WeaponWithAmmoFlags.CHAMBER_RNG | WeaponWithAmmoFlags.QUANTITY_RNG;
44 
45  protected const float DEFAULT_DAMAGE_ON_SHOT = 0.05;
46  protected ref array<ref AbilityRecord> m_abilities = new array<ref AbilityRecord>;
47  protected ref WeaponFSM m_fsm;
48  protected bool m_isJammed = false;
49  protected bool m_LiftWeapon = false;
50  protected bool m_BayonetAttached;
51  protected bool m_ButtstockAttached;
52  protected bool m_Charged = false;
53  protected bool m_WeaponOpen = false;
54  protected int m_BurstCount;
55  protected int m_BayonetAttachmentIdx;
56  protected int m_ButtstockAttachmentIdx;
57  protected int m_weaponAnimState = -1;
58  protected int m_magazineSimpleSelectionIndex = -1;
59  protected int m_weaponHideBarrelIdx = -1; //index in simpleHiddenSelections cfg array
60  protected float m_DmgPerShot = 0; //default is set to zero, since C++ solution has been implemented. See 'damageBarrel' and 'barrelArmor' in configs.
61  protected float m_WeaponLength;
62  protected float m_WeaponLiftCheckVerticalOffset;
63  protected float m_ShoulderDistance;
64  protected vector m_LastLiftPosition;
65  ref array<int> m_bulletSelectionIndex = new array<int>;
66  ref array<float> m_DOFProperties;
67  ref array<float> m_ChanceToJam = new array<float>;
68  protected float m_ChanceToJamSync = 0;
69  protected ref PropertyModifiers m_PropertyModifierObject;
71 
72  void Weapon_Base()
73  {
74  //m_DmgPerShot = ConfigGetFloat("damagePerShot");
75  m_BayonetAttached = false;
76  m_ButtstockAttached = false;
77  m_BayonetAttachmentIdx = -1;
78  m_ButtstockAttachmentIdx = -1;
79  m_BurstCount = 0;
80  m_DOFProperties = new array<float>;
81 
82  if ( ConfigIsExisting("simpleHiddenSelections") )
83  {
84  TStringArray selectionNames = new TStringArray;
85  ConfigGetTextArray("simpleHiddenSelections",selectionNames);
86  m_weaponHideBarrelIdx = selectionNames.Find("hide_barrel");
87  m_magazineSimpleSelectionIndex = selectionNames.Find("magazine");
88 
89  int bulletIndex = selectionNames.Find("bullet");
90  if ( bulletIndex != -1 )
91  {
92  m_bulletSelectionIndex.Insert(bulletIndex);
93 
94  for (int i = 2; i < 100; i++)
95  {
96  bulletIndex = selectionNames.Find(string.Format("bullet%1",i));
97  if (bulletIndex != -1)
98  {
99  m_bulletSelectionIndex.Insert(bulletIndex);
100  }
101  else
102  {
103  break;
104  }
105  }
106  }
107  }
108 
109  InitWeaponLength();
110  InitWeaponLiftCheckVerticalOffset();
111  InitShoulderDistance();
112  InitDOFProperties(m_DOFProperties);
113  if (GetGame().IsServer())
114  {
115  InitReliability(m_ChanceToJam);
116  }
117  InitStateMachine();
118  }
119 
120  void InitStateMachine() { }
121 
122  override void EEInit()
123  {
124  super.EEInit();
125 
126  if (GetGame().IsServer())
127  {
128  GetGame().GetCallQueue( CALL_CATEGORY_GAMEPLAY ).Call( AssembleGun );
129  }
130  }
131 
132  void SetInitialState(WeaponStableState initState)
133  {
134  m_fsm.SetInitialState(initState);
135  SetCharged(!initState.IsDischarged());
136  SetWeaponOpen(!initState.IsWeaponOpen());
137  SetGroundAnimFrameIndex(initState.m_animState);
138  }
139 
140  bool IsCharged()
141  {
142  return m_Charged;
143  }
144 
145  void SetCharged(bool value)
146  {
147  m_Charged = value;
148  }
149 
150  bool IsWeaponOpen()
151  {
152  return m_WeaponOpen;
153  }
154 
155  void SetWeaponOpen(bool value)
156  {
157  m_WeaponOpen = value;
158  }
159 
160  override protected float GetWeightSpecialized(bool forceRecalc = false)
161  {
162  float baseWeight = GetInventoryAndCargoWeight(forceRecalc);
163  float ammoWeight;
164  float ammoDamage;
165  string bulletTypeName, ammoTypeName;
166 
167  int muzzleCount = GetMuzzleCount();
168  #ifdef DEVELOPER
169  if (WeightDebug.m_VerbosityFlags & WeightDebugType.RECALC_FORCED)
170  {
171  WeightDebugData data1 = WeightDebug.GetWeightDebug(this);
172  data1.SetCalcDetails("TWPN: " + m_ConfigWeight+"(item weight) + " + baseWeight +"(contents weight)" );
173  }
174  #endif
175  for (int muzzleIndex = 0; muzzleIndex < muzzleCount; muzzleIndex++)
176  {
177  //chamber weight
178  if (!IsChamberEmpty(muzzleIndex))
179  {
180  ammoTypeName = GetChamberAmmoTypeName(muzzleIndex);
181  ammoWeight += g_Game.ConfigGetFloat(string.Format("CfgMagazines %1 weight", ammoTypeName));
182 
183  #ifdef DEVELOPER
184  if (WeightDebug.m_VerbosityFlags & WeightDebugType.RECALC_FORCED)
185  {
186  WeightDebugData data2 = WeightDebug.GetWeightDebug(this);
187  data2.AddCalcDetails( g_Game.ConfigGetFloat("CfgMagazines " + ammoTypeName + " weight").ToString() +"(chamber weight)");
188  }
189  #endif
190  }
191 
192  //correctly calculates internal magazine weight based on the ammo type of each bullet
193  if (HasInternalMagazine(muzzleIndex))
194  {
195  #ifdef DEVELOPER
196  float debugInternalMagWeight;
197  #endif
198  int cartridgeCount = GetInternalMagazineCartridgeCount(muzzleIndex);
199  for (int cartridgeIndex = 0; cartridgeIndex < cartridgeCount; cartridgeIndex++)
200  {
201  GetInternalMagazineCartridgeInfo(muzzleIndex, cartridgeIndex, ammoDamage, bulletTypeName);
202  ammoWeight += Ammunition_Base.GetAmmoWeightByBulletType(bulletTypeName);
203  #ifdef DEVELOPER
204  debugInternalMagWeight += g_Game.ConfigGetFloat("CfgMagazines " + ammoTypeName + " weight");
205  #endif
206  }
207  #ifdef DEVELOPER
208 
209  if (WeightDebug.m_VerbosityFlags & WeightDebugType.RECALC_FORCED)
210  {
211  WeightDebugData data3 = WeightDebug.GetWeightDebug(this);
212  data3.AddCalcDetails(debugInternalMagWeight.ToString()+ "(internal mag weight)");
213  }
214  #endif
215  }
216 
217  }
218  return ammoWeight + baseWeight + GetConfigWeightModified();
219  }
221  void AssembleGun();
222 
223  bool CanProcessAction(int action, int actionType)
224  {
225  return false; // @TODO
226  }
233  bool HasActionAbility(int action, int actionType)
234  {
235  int count = GetAbilityCount();
236  for (int i = 0; i < count; ++i)
237  {
238  AbilityRecord rec = GetAbility(i);
239  if (rec.m_action == action && rec.m_actionType == actionType)
240  return true;
241  }
242  return false;
243  }
247  int GetAbilityCount() { return m_abilities.Count(); }
252  AbilityRecord GetAbility(int index) { return m_abilities.Get(index); }
253 
257  bool CanProcessWeaponEvents() { return m_fsm && m_fsm.IsRunning(); }
258 
263  WeaponStateBase GetCurrentState() { return m_fsm.GetCurrentState(); }
264 
269  {
270  return CanProcessWeaponEvents() && GetCurrentState().IsWaitingForActionFinish();
271  }
272 
273  bool IsIdle()
274  {
275  return CanProcessWeaponEvents() && GetCurrentState().IsIdle();
276  }
277 
282  bool ProcessWeaponEvent(WeaponEventBase e)
283  {
284  SyncEventToRemote(e);
285 
286  // @NOTE: synchronous events not handled by fsm
287  if (e.GetEventID() == WeaponEventID.SET_NEXT_MUZZLE_MODE)
288  {
289  SetNextMuzzleMode(GetCurrentMuzzle());
290  return true;
291  }
292 
293  if (m_fsm.ProcessEvent(e) == ProcessEventResult.FSM_OK)
294  return true;
295 
296  //if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("FSM refused to process event (no transition): src=" + GetCurrentState().ToString() + " event=" + e.ToString()); }
297  return false;
298  }
302  bool ProcessWeaponAbortEvent(WeaponEventBase e)
303  {
304  SyncEventToRemote(e);
305 
307  m_fsm.ProcessAbortEvent(e, aa);
308  return aa == ProcessEventResult.FSM_OK;
309  }
310 
311  bool CanChamberBullet(int muzzleIndex, Magazine mag)
312  {
313  return CanChamberFromMag(muzzleIndex, mag) && (!IsChamberFull(muzzleIndex) || IsChamberFiredOut(muzzleIndex) || !IsInternalMagazineFull(muzzleIndex));
314  }
315 
316  void SetWeaponAnimState(int state)
317  {
318  m_weaponAnimState = state;
319  SetGroundAnimFrameIndex(state);
320  }
321  void ResetWeaponAnimState()
322  {
323  if (LogManager.IsWeaponLogEnable()) fsmDebugSpam("[wpnfsm] " + Object.GetDebugName(this) + " resetting anim state: " + typename.EnumToString(PistolAnimState, m_weaponAnimState) + " --> " + typename.EnumToString(PistolAnimState, -1));
324  m_weaponAnimState = -1;
325  }
326  int GetWeaponAnimState() { return m_weaponAnimState; }
327 
328  void EEFired(int muzzleType, int mode, string ammoType)
329  {
330  if ( !GetGame().IsDedicatedServer() )
331  {
332  ItemBase suppressor = GetAttachedSuppressor();
333 
334  // Muzzle flash & overheating effects
335  ItemBase.PlayFireParticles(this, muzzleType, ammoType, this, suppressor, "CfgWeapons" );
336  IncreaseOverheating(this, ammoType, this, suppressor, "CfgWeapons");
337 
338  if (suppressor)
339  {
340  ItemBase.PlayFireParticles(this, muzzleType, ammoType, suppressor, NULL, "CfgVehicles" );
341  suppressor.IncreaseOverheating(this, ammoType, this, suppressor, "CfgVehicles");
342  }
343  }
344 
345  //obsolete, replaced by C++ solution!
346 /*
347  if (GetGame().IsServer())
348  {
349  AddHealth("","Health",-m_DmgPerShot); //damages weapon
350  if (suppressor)
351  suppressor.AddHealth("","Health",-m_DmgPerShot); //damages suppressor; TODO add suppressor damage coeficient/parameter (?) to suppressors/weapons (?)
352  }
353 */
354  //JamCheck(muzzleType);
355 
356  #ifdef DIAG_DEVELOPER
357  MiscGameplayFunctions.UnlimitedAmmoDebugCheck(this);
358  #endif
359  }
360 
361  bool JamCheck(int muzzleIndex )
362  {
363  PlayerBase player = PlayerBase.Cast(GetHierarchyRootPlayer());
364  if ( player )
365  {
366  float rnd = player.GetRandomGeneratorSyncManager().GetRandom01(RandomGeneratorSyncUsage.RGSJam);
367  //Print("Random Jam - " + rnd);
368  if (rnd < GetSyncChanceToJam())
369  return true;
370  }
371  return false;
372  }
373 
374  void ShowBullet(int muzzleIndex)
375  {
376  if ( m_bulletSelectionIndex.Count() > muzzleIndex )
377  {
378  SetSimpleHiddenSelectionState(m_bulletSelectionIndex[muzzleIndex],1);
379  }
380  else
381  SelectionBulletShow();
382  }
383 
384  void HideBullet(int muzzleIndex)
385  {
386  if ( m_bulletSelectionIndex.Count() > muzzleIndex )
387  {
388  SetSimpleHiddenSelectionState(m_bulletSelectionIndex[muzzleIndex],0);
389  }
390  else
391  SelectionBulletHide();
392  }
393 
394  bool IsJammed() { return m_isJammed; }
395  bool CanEjectBullet() {return true;}
396  void SetJammed(bool value) { m_isJammed = value; }
397  float GetSyncChanceToJam() { return m_ChanceToJamSync; }
398  float GetChanceToJam()
399  {
400  int level = GetHealthLevel();
401 
402  if (level >= 0 && level < m_ChanceToJam.Count())
403  return m_ChanceToJam[level];
404  else
405  return 0.0;
406  }
407 
408  void SyncSelectionState(bool has_bullet, bool has_mag)
409  {
410  if (has_bullet)
411  {
412  string chamberedAmmoTypeName;
413  float chamberedAmmoDmg;
414 
415  if ( GetCartridgeInfo(0, chamberedAmmoDmg, chamberedAmmoTypeName) )
416  {
417  EffectBulletShow(0, chamberedAmmoDmg, chamberedAmmoTypeName);
418  }
419  //ShowBullet(0);
420  SelectionBulletShow();
421  }
422  else
423  {
424  //HideBullet(0);
425  SelectionBulletHide();
426  EffectBulletHide(0);
427  }
428 
429  if (has_mag)
430  ShowMagazine();
431  else
432  HideMagazine();
433  }
434 
435  /*override void EEOnAfterLoad()
436  {
437  super.EEOnAfterLoad();
438 
439  string chamberedAmmoTypeName;
440  float chamberedAmmoDmg;
441 
442  if ( GetCartridgeInfo(0, chamberedAmmoDmg, chamberedAmmoTypeName) )
443  {
444  EffectBulletShow(0, chamberedAmmoDmg, chamberedAmmoTypeName);
445  }
446 
447  }*/
448 
449  void ForceSyncSelectionState()
450  {
451  int nMuzzles = GetMuzzleCount();
452  for (int i = 0; i < nMuzzles; ++i)
453  {
454  if (IsChamberFull(i))
455  {
456  ShowBullet(i);
457  float damage;
458  string ammoTypeName;
459  GetCartridgeInfo(i, damage, ammoTypeName);
460  EffectBulletShow(i, damage, ammoTypeName);
461  }
462  else
463  {
464  HideBullet(i);
465  EffectBulletHide(i);
466  }
467 
468  Magazine mag = GetMagazine(i);
469  if (mag)
470  ShowMagazine();
471  else
472  HideMagazine();
473  }
474  }
475 
476  override bool OnStoreLoad(ParamsReadContext ctx, int version)
477  {
478  if ( !super.OnStoreLoad(ctx, version) )
479  return false;
480 
481 
482  if (version >= 113)
483  {
484  int current_muzzle = 0;
485  if (!ctx.Read(current_muzzle))
486  {
487  Error("Weapon.OnStoreLoad " + this + " cannot read current muzzle!");
488  return false;
489  }
490 
491  if (current_muzzle >= GetMuzzleCount() || current_muzzle < 0)
492  Error("Weapon.OnStoreLoad " + this + " trying to set muzzle index " + current_muzzle + " while it only has " + GetMuzzleCount() + " muzzles!");
493  else
494  SetCurrentMuzzle(current_muzzle);
495  }
496 
497  if (version >= 105)
498  {
499  int mode_count = 0;
500  if (!ctx.Read(mode_count))
501  {
502  Error("Weapon.OnStoreLoad " + this + " cannot read mode count!");
503  return false;
504  }
505 
506  for (int m = 0; m < mode_count; ++m)
507  {
508  int mode = 0;
509  if (!ctx.Read(mode))
510  {
511  Error("Weapon.OnStoreLoad " + this + " cannot read mode[" + m + "]");
512  return false;
513  }
514 
515  if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("[wpnfsm] " + Object.GetDebugName(this) + " OnStoreLoad - loaded muzzle[" + m + "].mode = " + mode); }
516  SetCurrentMode(m, mode);
517  }
518  }
519 
520  if ( version >= 106 )
521  {
522  if ( !ctx.Read(m_isJammed) )
523  {
524  Error("Weapon.OnStoreLoad cannot load jamming state");
525  return false;
526  }
527  }
528 
529  if (m_fsm)
530  {
531  if (!m_fsm.OnStoreLoad(ctx, version))
532  return false;
533 
534  WeaponStableState wss = WeaponStableState.Cast(m_fsm.GetCurrentState());
535  if (wss)
536  {
537  SetGroundAnimFrameIndex(wss.m_animState);
538  }
539 
540  }
541  else
542  {
543  int dummy = 0;
544  if (!ctx.Read(dummy))
545  return false;
546  }
547 
548  return true;
549  }
550 
552  {
553  if (m_fsm && m_fsm.IsRunning())
554  {
555  if (m_fsm.SaveCurrentFSMState(ctx))
556  {
557  if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("[wpnfsm] " + Object.GetDebugName(this) + " Weapon=" + this + " state saved."); }
558  }
559  else
560  Error("[wpnfsm] " + Object.GetDebugName(this) + " Weapon=" + this + " state NOT saved.");
561  }
562  else
563  Error("[wpnfsm] " + Object.GetDebugName(this) + " Weapon.SaveCurrentFSMState: trying to save weapon without FSM (or uninitialized weapon) this=" + this + " type=" + GetType());
564  }
565 
566  bool LoadCurrentFSMState(ParamsReadContext ctx, int version)
567  {
568  if (m_fsm)
569  {
570  if (m_fsm.LoadCurrentFSMState(ctx, version))
571  {
572  WeaponStableState state = WeaponStableState.Cast(GetCurrentState());
573  if (state)
574  {
575  SyncSelectionState(state.HasBullet(), state.HasMagazine());
576  state.SyncAnimState();
577  if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("[wpnfsm] " + Object.GetDebugName(this) + " Weapon=" + this + " stable state loaded and synced."); }
578  return true;
579  }
580  else
581  {
582  if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("[wpnfsm] " + Object.GetDebugName(this) + " Weapon=" + this + " unstable/error state loaded."); }
583  return false;
584  }
585  }
586  else
587  {
588  Error("[wpnfsm] " + Object.GetDebugName(this) + " Weapon=" + this + " did not load.");
589  return false;
590  }
591  }
592  else
593  {
594  Error("[wpnfsm] " + Object.GetDebugName(this) + " Weapon.LoadCurrentFSMState: trying to load weapon without FSM (or uninitialized weapon) this=" + this + " type=" + GetType());
595  return false;
596  }
597  }
598 
599  override void AfterStoreLoad()
600  {
601  if (m_fsm)
602  {
603  int mi = GetCurrentMuzzle();
604  Magazine mag = GetMagazine(mi);
605  bool has_mag = mag != null;
606  bool has_bullet = !IsChamberEmpty(mi);
607  SyncSelectionState(has_bullet, has_mag);
608  }
609  }
610 
611  override void OnStoreSave(ParamsWriteContext ctx)
612  {
613  super.OnStoreSave(ctx);
614 
615  // current muzzle added in version 113
616  int current_muzzle = GetCurrentMuzzle();
617  ctx.Write(current_muzzle);
618 
619  // fire mode added in version 105
620  int mode_count = GetMuzzleCount();
621  ctx.Write(mode_count);
622  for (int m = 0; m < mode_count; ++m)
623  ctx.Write(GetCurrentMode(m));
624 
625  ctx.Write(m_isJammed);
626 
627  if (m_fsm)
628  m_fsm.OnStoreSave(ctx);
629  else
630  {
631  int dummy = 0;
632  ctx.Write(dummy);
633  }
634  }
635 
639  int GetInternalStateID()
640  {
641  if (m_fsm)
642  return m_fsm.GetInternalStateID();
643  return 0;
644  }
645 
649  int GetCurrentStableStateID()
650  {
651  if (m_fsm)
652  {
653  return m_fsm.GetCurrentStableStateID();
654  }
655  return 0;
656  }
657 
662  void RandomizeFSMState()
663  {
664  if (m_fsm)
665  {
666  int mi = GetCurrentMuzzle();
667  Magazine mag = GetMagazine(mi);
668  bool has_mag = mag != null;
669  bool has_bullet = !IsChamberEmpty(mi);
670  bool has_jam = IsJammed();
671  array<MuzzleState> muzzleStates = GetMuzzleStates();
672  m_fsm.RandomizeFSMStateEx(muzzleStates, has_mag, has_jam);
673  ForceSyncSelectionState();
674  }
675  }
676 
678  protected array<MuzzleState> GetMuzzleStates()
679  {
680  array<MuzzleState> muzzleStates = new array<MuzzleState>;
681 
682  int nMuzzles = GetMuzzleCount();
683  for (int i = 0; i < nMuzzles; ++i)
684  {
685  MuzzleState state = MuzzleState.U;
686  if (IsChamberFiredOut(i))
687  state = MuzzleState.F;
688  else if (IsChamberFull(i))
689  state = MuzzleState.L;
690  else if (IsChamberEmpty(i))
691  state = MuzzleState.E;
692  else
693  ErrorEx(string.Format("Unable to identify chamber state of muzzle %1", i));
694 
695  muzzleStates.Insert(state);
696  }
697 
698  return muzzleStates;
699  }
700 
707 
715  static Weapon_Base CreateWeaponWithAmmo( string weaponType, string magazineType = "", int flags = WeaponWithAmmoFlags.CHAMBER )
716  {
717  Weapon_Base wpn = Weapon_Base.Cast(GetGame().CreateObjectEx( weaponType, vector.Zero, ECE_PLACE_ON_SURFACE ));
718 
719  if ( !wpn )
720  {
721  ErrorEx(string.Format("%1 does not exist or is not a weapon.", weaponType));
722  return null;
723  }
724 
725  wpn.SpawnAmmo(magazineType, flags);
726  return wpn;
727  }
728 
735  bool SpawnAmmo( string magazineType = "", int flags = WeaponWithAmmoFlags.CHAMBER )
736  {
737  // Attempt internal mag
738  if ( HasInternalMagazine(-1) && FillInnerMagazine(magazineType, flags) )
739  return true;
740 
741  // Attempt mag attachment
742  if ( GetMagazineTypeCount(0) > 0 && SpawnAttachedMagazine(magazineType, flags) )
743  return true;
744 
745  // Attempt chamber
746  if ( FillChamber(magazineType, flags) )
747  return true;
748 
749  return false;
750  }
751 
758  Magazine SpawnAttachedMagazine( string magazineType = "", int flags = WeaponWithAmmoFlags.CHAMBER )
759  {
760  // Check if the gun has any magazines registered in config
761  if ( GetMagazineTypeCount(0) == 0 )
762  {
763  ErrorEx(string.Format("No 'magazines' config entry for %1.", this));
764  return null;
765  }
766 
767  // Randomize when no specific one is given
768  if ( magazineType == "" )
769  {
770  if ( flags & WeaponWithAmmoFlags.MAX_CAPACITY_MAG)
771  magazineType = GetMaxMagazineTypeName(0);
772  else
773  magazineType = GetRandomMagazineTypeName(0);
774  }
775 
776  EntityAI magAI = GetInventory().CreateAttachment(magazineType);
777  if (!magAI)
778  {
779  ErrorEx(string.Format("Failed to create and attach %1 to %2", GetDebugName(magAI), this));
780  return null;
781  }
782 
783  Magazine mag;
784  if (!CastTo(mag, magAI))
785  {
786  ErrorEx(string.Format("Expected magazine, created: %1", GetDebugName(magAI)));
787  return null;
788  }
789 
790  // Decide random quantity when enabled
791  if (flags & WeaponWithAmmoFlags.QUANTITY_RNG)
792  mag.ServerSetAmmoCount(Math.RandomIntInclusive(0, mag.GetAmmoMax()));
793 
794  // Fill chamber when flagged
795  bool chamberRng = (flags & WeaponWithAmmoFlags.CHAMBER_RNG);
796  bool chamber = (flags & WeaponWithAmmoFlags.CHAMBER) || chamberRng;
797  if (chamber || chamberRng)
798  {
799  FillChamber(magazineType, flags);
800  }
801 
802  // FSM cares about magazine state
803  RandomizeFSMState();
804  Synchronize();
805 
806  return mag;
807  }
808 
816  bool FillInnerMagazine( string ammoType = "", int flags = WeaponWithAmmoFlags.CHAMBER )
817  {
818  // Don't try to fill it when there are none
819  if (!HasInternalMagazine(-1))
820  return false;
821 
822  // Make sure the given ammoType is actually useable
823  if (ammoType != "")
824  {
825  if (!AmmoTypesAPI.MagazineTypeToAmmoType(ammoType, ammoType))
826  return false;
827  }
828 
829 
830  bool didSomething = false;
831  int muzzCount = GetMuzzleCount();
832 
833  bool ammoRng = ammoType == "";
834  bool ammoFullRng = ammoRng && (flags & WeaponWithAmmoFlags.AMMO_MAG_RNG);
835 
836  // No full RNG flag, so pick one random and use only this one
837  if (ammoRng && !ammoFullRng)
838  ammoType = GetRandomChamberableAmmoTypeName(0);
839 
840  // Fill the internal magazine
841  for (int i = 0; i < muzzCount; ++i)
842  {
843  int ammoCount = GetInternalMagazineMaxCartridgeCount(i);
844 
845  // Decide random quantity when enabled
846  if ( flags & WeaponWithAmmoFlags.QUANTITY_RNG )
847  ammoCount = Math.RandomIntInclusive(0, ammoCount);
848 
849  // Only do the things when there is actually ammo to fill
850  if (ammoCount > 0)
851  {
852  // Push in the cartridges
853  for (int j = 0; j < ammoCount; ++j)
854  {
855  // Full random, decide a new one for every cartridge
856  if ( ammoFullRng )
857  ammoType = GetRandomChamberableAmmoTypeName(i);
858 
859  PushCartridgeToInternalMagazine(i, 0, ammoType);
860  didSomething = true;
861  }
862  }
863  }
864 
865  // Call the chamber method if asked for
866  bool chamber = (flags & WeaponWithAmmoFlags.CHAMBER) || (flags & WeaponWithAmmoFlags.CHAMBER_RNG);
867  if (chamber && FillChamber(ammoType, flags))
868  {
869  didSomething = true;
870  }
871 
872  // Does not need any FSM fixing, FSM does not care about inner magazines
873 
874  return didSomething;
875  }
876 
884  bool FillChamber( string ammoType = "", int flags = WeaponWithAmmoFlags.CHAMBER )
885  {
886  // Quickly check if there are any chambers we can fill
887  int muzzCount = GetMuzzleCount();
888  bool anyEmpty = false;
889 
890  for (int m = 0; m < muzzCount; ++m)
891  {
892  if (IsChamberEmpty(m))
893  {
894  anyEmpty = true;
895  break;
896  }
897  }
898 
899  if (!anyEmpty)
900  return false;
901 
902  // Make sure the given ammoType is actually useable
903  if (ammoType != "")
904  if (!AmmoTypesAPI.MagazineTypeToAmmoType(ammoType, ammoType))
905  return false;
906 
907  // Just so we don't '&' wastefully in a loop
908  bool didSomething = false;
909  bool chamberFullRng = (flags & WeaponWithAmmoFlags.CHAMBER_RNG_SPORADIC);
910  bool chamberRng = (flags & WeaponWithAmmoFlags.CHAMBER_RNG);
911  bool chamber = (flags & WeaponWithAmmoFlags.CHAMBER);
912 
913  if (chamber || chamberRng || chamberFullRng)
914  {
915  int amountToChamber = muzzCount;
916 
917  // No need to do this for full rng, as that will roll for every muzzle
918  if (chamberRng)
919  amountToChamber = Math.RandomIntInclusive(0, muzzCount);
920 
921  bool chamberAmmoRng = (ammoType == "");
922  bool chamberAmmoFullRng = chamberAmmoRng && (flags & WeaponWithAmmoFlags.AMMO_CHAMBER_RNG);
923 
924  // No full RNG flag, so pick one random and use only this one
925  if (chamberAmmoRng && !chamberAmmoFullRng)
926  ammoType = GetRandomChamberableAmmoTypeName(0);
927 
928  for (int i = 0; i < muzzCount; ++i)
929  {
930  // Skip when there's already something in the chamber
931  if (!IsChamberEmpty(i))
932  continue;
933 
934  // Roll the rng when enabled
935  if (chamberFullRng)
936  chamber = Math.RandomIntInclusive(0, 1);
937 
938  // We chambering
939  if (chamber)
940  {
941  // Full random, decide a new one for every muzzle
942  if ( chamberAmmoFullRng )
943  ammoType = GetRandomChamberableAmmoTypeName(i);
944 
945  // Push it
946  PushCartridgeToChamber(i, 0, ammoType);
947  didSomething = true;
948 
949  // Stop chambering when we hit the desired amount
950  --amountToChamber;
951  if (amountToChamber <= 0)
952  break;
953  }
954  }
955  }
956 
957  // Only fix the FSM and Synchronize when absolutely needed
958  if (!didSomething)
959  return false;
960 
961  // FSM cares about chamber state
962  RandomizeFSMState();
963  Synchronize();
964 
965  return true;
966  }
967 
969 
970 
971 
975  override int GetSlotsCountCorrect()
976  {
977  int ac = GetInventory().AttachmentCount();
978  int sc = GetInventory().GetAttachmentSlotsCount() + GetMuzzleCount();
979  if (ac > sc) sc = ac; // fix of some weapons which has 1 attachments but 0 slots...
980  return sc;
981  };
982 
983  PropertyModifiers GetPropertyModifierObject()
984  {
985  if (!m_PropertyModifierObject)
986  {
987  m_PropertyModifierObject = new PropertyModifiers(this);
988  }
989  return m_PropertyModifierObject;
990  }
991 
992  void OnFire(int muzzle_index)
993  {
994 /*
995  array<Man> players();
996  GetGame().GetPlayers(players);
997 
998  Man root = GetHierarchyRootPlayer();
999 
1000  if (!root)
1001  {
1002  return;
1003  }
1004 
1005  vector safePosition = root.GetPosition() + (root.GetDirection() * "0 1 0" * 3.0);
1006 
1007  Man other = null;
1008  foreach (auto player : players)
1009  {
1010  if (player != GetHierarchyRootPlayer())
1011  {
1012  player.SetPosition(safePosition);
1013  }
1014  }
1015 */
1016 
1017  m_BurstCount++;
1018  }
1019 
1020  void OnFireModeChange(int fireMode)
1021  {
1022  if ( !GetGame().IsDedicatedServer() )
1023  {
1024  EffectSound eff;
1025 
1026  if ( fireMode == 0 )
1027  eff = SEffectManager.PlaySound("Fire_Mode_Switch_Marked_Click_SoundSet", GetPosition());
1028  else
1029  eff = SEffectManager.PlaySound("Fire_Mode_Switch_Simple_Click_SoundSet", GetPosition());
1030 
1031  eff.SetAutodestroy(true);
1032  }
1033 
1034  ResetBurstCount();
1035  }
1036 
1037  void ValidateAndRepair()
1038  {
1039  if ( m_fsm )
1040  m_fsm.ValidateAndRepair();
1041  }
1042 
1043  override void OnInventoryEnter(Man player)
1044  {
1045  m_PropertyModifierObject = null;
1046 
1047  ValidateAndRepair();
1048 
1049  super.OnInventoryEnter(player);
1050  }
1051 
1052  override void OnInventoryExit(Man player)
1053  {
1054  m_PropertyModifierObject = null;
1055  super.OnInventoryExit(player);
1056  }
1057 
1058  override void EEItemAttached(EntityAI item, string slot_name)
1059  {
1060  super.EEItemAttached(item, slot_name);
1061 
1062  GetPropertyModifierObject().UpdateModifiers();
1063  }
1064 
1065  override void EEItemDetached(EntityAI item, string slot_name)
1066  {
1067  super.EEItemDetached(item, slot_name);
1068 
1069  GetPropertyModifierObject().UpdateModifiers();
1070  }
1071 
1072  override void EEItemLocationChanged(notnull InventoryLocation oldLoc, notnull InventoryLocation newLoc)
1073  {
1074  super.EEItemLocationChanged(oldLoc, newLoc);
1075 
1076  if (newLoc.GetType() == InventoryLocationType.HANDS)
1077  {
1078  PlayerBase player;
1079  if (newLoc.GetParent() && PlayerBase.CastTo(player, newLoc.GetParent()))
1080  {
1081  HumanCommandMove cm = player.GetCommand_Move();
1082  if (cm)
1083  {
1084  cm.SetMeleeBlock(false);
1085  }
1086  }
1087  }
1088  }
1089 
1090  override void OnItemLocationChanged(EntityAI old_owner, EntityAI new_owner)
1091  {
1092  super.OnItemLocationChanged(old_owner,new_owner);
1093 
1094  // "resets" optics memory on optics
1095  PlayerBase player;
1096  if (PlayerBase.CastTo(player,old_owner))
1097  {
1098  player.SetReturnToOptics(false);
1099 
1100  //optics item state reset
1101  ItemOptics optics;
1102  if (Class.CastTo(optics,GetAttachedOptics()))
1103  {
1104  player.SwitchOptics(optics,false);
1105  }
1106  }
1107 
1108  HideWeaponBarrel(false);
1109  }
1110 
1111  override bool CanReleaseAttachment(EntityAI attachment)
1112  {
1113  if ( !super.CanReleaseAttachment( attachment ) )
1114  return false;
1115  Magazine mag = Magazine.Cast(attachment);
1116  if (mag)
1117  {
1118  PlayerBase player = PlayerBase.Cast( GetHierarchyRootPlayer() );
1119  if ( player )
1120  {
1121  if ( player.GetItemInHands() == this )
1122  return true;
1123  }
1124  return false;
1125  }
1126 
1127  return true;
1128  }
1129 
1130  override bool CanRemoveFromHands(EntityAI parent)
1131  {
1132  if (IsIdle())
1133  {
1134  return true;
1135  }
1136  if (LogManager.IsWeaponLogEnable()) { wpnDebugPrint("[wpnfsm] " + Object.GetDebugName(this) + " Weapon=" + this + " not in stable state=" + GetCurrentState().Type()); }
1137  return false; // do not allow removal of weapon while weapon is busy
1138  }
1139 
1140  bool IsRemoteWeapon()
1141  {
1143  if (GetInventory().GetCurrentInventoryLocation(il))
1144  {
1145  EntityAI parent = il.GetParent();
1146  DayZPlayer dayzp = DayZPlayer.Cast(parent);
1147  if (il.GetType() == InventoryLocationType.HANDS && dayzp)
1148  {
1149  bool remote = dayzp.GetInstanceType() == DayZPlayerInstanceType.INSTANCETYPE_REMOTE;
1150  return remote;
1151  }
1152  }
1153  return true;
1154  }
1155 
1156  void SyncEventToRemote(WeaponEventBase e)
1157  {
1158  DayZPlayer p = DayZPlayer.Cast(GetHierarchyParent());
1159  if (p && p.GetInstanceType() == DayZPlayerInstanceType.INSTANCETYPE_SERVER)
1160  {
1162 
1163  ctx.Write(INPUT_UDT_WEAPON_REMOTE_EVENT);
1164  e.WriteToContext(ctx);
1165 
1166  if (LogManager.IsWeaponLogEnable())
1167  wpnDebugPrint("[wpnfsm] " + Object.GetDebugName(this) + " send 2 remote: sending e=" + e + " id=" + e.GetEventID() + " p=" + e.m_player + " m=" + e.m_magazine);
1168 
1169  p.StoreInputForRemotes(ctx);
1170  }
1171  }
1172 
1173 
1174  RecoilBase SpawnRecoilObject()
1175  {
1176  return new DefaultRecoil(this);
1177  }
1178 
1179  int GetWeaponSpecificCommand(int weaponAction, int subCommand)
1180  {
1181  return subCommand;
1182  }
1183 
1184  bool CanFire()
1185  {
1186  if (!IsChamberEmpty(GetCurrentMuzzle()) && !IsChamberFiredOut(GetCurrentMuzzle()) && !IsJammed() && !m_LiftWeapon && !IsDamageDestroyed())
1187  return true;
1188  return false;
1189  }
1190 
1191  bool CanEnterIronsights()
1192  {
1193  ItemOptics optic = GetAttachedOptics();
1194  if (!optic)
1195  return true;
1196 
1197  return optic.HasWeaponIronsightsOverride();
1198  }
1199 
1201  bool InitDOFProperties(out array<float> temp_array)
1202  {
1203  if (GetGame().ConfigIsExisting("cfgWeapons " + GetType() + " PPDOFProperties"))
1204  {
1205  GetGame().ConfigGetFloatArray("cfgWeapons " + GetType() + " PPDOFProperties", temp_array);
1206  return true;
1207  }
1208  return false;
1209  }
1210 
1211  bool InitReliability(out array<float> reliability_array)
1212  {
1213  if (GetGame().ConfigIsExisting("cfgWeapons " + GetType() + " Reliability ChanceToJam"))
1214  {
1215  GetGame().ConfigGetFloatArray("cfgWeapons " + GetType() + " Reliability ChanceToJam", reliability_array);
1216  return true;
1217  }
1218  return false;
1219  }
1220 
1222  bool InitWeaponLength()
1223  {
1224  if (ConfigIsExisting("WeaponLength"))
1225  {
1226  m_WeaponLength = ConfigGetFloat("WeaponLength");
1227  return true;
1228  }
1229  m_WeaponLength = 0.8; //default value if not set in config; should not be zero
1230  return false;
1231  }
1232 
1234  bool InitWeaponLiftCheckVerticalOffset()
1235  {
1236  if (ConfigIsExisting("WeaponLiftCheckVerticalOffset"))
1237  {
1238  m_WeaponLiftCheckVerticalOffset = ConfigGetFloat("WeaponLiftCheckVerticalOffset");
1239  return true;
1240  }
1241  m_WeaponLiftCheckVerticalOffset = 0.0; //no offset by default
1242  return false;
1243  }
1244 
1246  protected bool InitShoulderDistance()
1247  {
1248  if (ConfigIsExisting("ShoulderDistance"))
1249  {
1250  m_ShoulderDistance = ConfigGetFloat("ShoulderDistance");
1251  return true;
1252  }
1253 
1254  m_ShoulderDistance = 0;
1255  return false;
1256  }
1257 
1258  ref array<float> GetWeaponDOF()
1259  {
1260  return m_DOFProperties;
1261  }
1262 
1263  // lifting weapon on obstcles
1264  bool LiftWeaponCheck(PlayerBase player)
1265  {
1266  int idx;
1267  float distance;
1268  float hit_fraction; //junk
1269  vector start, end;
1270  vector direction;
1271  vector usti_hlavne_position;
1272  vector trigger_axis_position;
1273  vector hit_pos, hit_normal; //junk
1274  Object obj;
1275  ItemBase attachment;
1276  HumanMovementState movementState = new HumanMovementState();
1277 
1278  bool wasLift = m_LiftWeapon;
1279  vector lastLiftPosition = m_LastLiftPosition;
1280 
1281  m_LiftWeapon = false;
1282  // not a gun, no weap.raise for now
1283  if ( HasSelection("Usti hlavne") )
1284  return false;
1285 
1286  if (!player)
1287  {
1288  Print("Error: No weapon owner, returning");
1289  return false;
1290  }
1291 
1292  // weapon not raised
1293  player.GetMovementState(movementState);
1294  if (!movementState.IsRaised())
1295  return false;
1296 
1297  // suppress raising of weapon during melee attack preventing state inconsistency
1298  if (movementState.m_CommandTypeId == DayZPlayerConstants.COMMANDID_MELEE || movementState.m_CommandTypeId == DayZPlayerConstants.COMMANDID_MELEE2)
1299  {
1300  return false;
1301  }
1302 
1303  usti_hlavne_position = GetSelectionPositionLS( "Usti hlavne" ); // Usti hlavne
1304  trigger_axis_position = GetSelectionPositionLS("trigger_axis");
1305 
1306  // If possible use aiming angles instead as these will work consistently
1307  // and independently of any cameras, etc.
1308  HumanCommandWeapons hcw = player.GetCommandModifier_Weapons();
1309  if (hcw)
1310  {
1311  vector yawPitchRoll = Vector(
1312  hcw.GetBaseAimingAngleLR() + player.GetOrientation()[0],
1313  hcw.GetBaseAimingAngleUD(),
1314  0.0);
1315  direction = yawPitchRoll.AnglesToVector();
1316  }
1317  else // Fallback to previously implemented logic
1318  {
1319  // freelook raycast
1320  if (player.GetInputController().CameraIsFreeLook())
1321  {
1322  if (player.m_DirectionToCursor != vector.Zero)
1323  {
1324  direction = player.m_DirectionToCursor;
1325  }
1326  // if player raises weapon in freelook
1327  else
1328  {
1329  direction = MiscGameplayFunctions.GetHeadingVector(player);
1330  }
1331  }
1332  else
1333  {
1334  direction = GetGame().GetCurrentCameraDirection(); // exception for freelook. Much better this way!
1335  }
1336  }
1337 
1338  idx = player.GetBoneIndexByName("Neck"); //RightHandIndex1
1339  if ( idx == -1 )
1340  { start = player.GetPosition()[1] + 1.5; }
1341  else
1342  { start = player.GetBonePositionWS(idx); }
1343 
1344 
1345  // Updated weapon lift detection code prototype
1346  {
1347  // 0: construct stable trasformation matrix that
1348  // approximately aligns with the weapon transform
1349  // without actually using the weapon as reference
1350  // (as the weapon can be moved unpredictably by anims)
1351  vector resTM[4];
1352  resTM[0] = Vector(direction[0], 0, direction[2]).Normalized();
1353  resTM[0] = vector.RotateAroundZeroDeg(resTM[0], vector.Up, 90);
1354  resTM[2] = direction;
1355  resTM[1] = resTM[2] * resTM[0];
1356  resTM[3] = start;
1357 
1358  // Approximate the roll of leaning
1360  player.GetMovementState(hms);
1361  float leanAngle = hms.m_fLeaning * 35;
1362  vector rotTM[3];
1363  Math3D.YawPitchRollMatrix( Vector(0, 0, leanAngle), rotTM );
1364  Math3D.MatrixMultiply3(resTM, rotTM, resTM);
1365 
1366  // Draw relative TM diagnostic
1367  #ifdef DIAG_DEVELOPER
1368  if (DiagMenu.GetValue(DiagMenuIDs.WEAPON_LIFT_DEBUG))
1369  {
1370  Shape.CreateArrow(resTM[3], resTM[3] + resTM[0], 0.05, COLOR_RED, ShapeFlags.ONCE);
1371  Shape.CreateArrow(resTM[3], resTM[3] + resTM[1], 0.05, COLOR_GREEN, ShapeFlags.ONCE);
1372  Shape.CreateArrow(resTM[3], resTM[3] + resTM[2], 0.05, COLOR_BLUE, ShapeFlags.ONCE);
1373  }
1374  #endif
1375 
1376  // 1: pick from predefined offset relative to
1377  // the previously constructed transform
1378  float udAngle = Math.Asin(direction[1]) * Math.RAD2DEG;
1379 
1380  // offsets are [right, up, forward]
1381  // values are based on what felt right after iterating
1382  vector offsets[] =
1383  {
1384  "0.11 0.17 0.0", // offset while aiming down
1385  "0.12 0.05 0.0", // offset while aiming forward
1386  "0.112 0.03 0.0" // offset while aiming up
1387  };
1388  const int lastIndex = 2; // length of offsets - 1
1389 
1390  // <0,1> range of aiming
1391  float a = Math.Clamp(Math.InverseLerp(DayZPlayerCamera1stPerson.CONST_UD_MIN, DayZPlayerCamera1stPerson.CONST_UD_MAX, udAngle), 0, 0.9999);
1392  int lo = a * lastIndex;
1393  int hi = Math.Clamp(lo+1, 0, lastIndex);
1394 
1395  // remap to current lo-hi range
1396  float t = Math.Clamp(a * lastIndex - lo, 0, 1);
1397  vector offset = vector.Lerp(offsets[lo], offsets[hi], t);
1398 
1399  // offsets are [right, up forward]
1400  // additional offsets added to previous offsets per stance
1401  vector stanceOffsets[] =
1402  {
1403  "0 -0.015 0", // erect
1404  "0 0.03 0", // crouch
1405  "0 -0.04 0",// prone
1406  };
1407 
1408  // 2. pick from predefined offsets based on stance,
1409  // allows to even further approximate the alignment
1410  int stanceOffsetIndex = hms.m_iStanceIdx;
1411  if (stanceOffsetIndex >= DayZPlayerConstants.STANCEIDX_PRONE)
1412  stanceOffsetIndex -= DayZPlayerConstants.STANCEIDX_RAISED;
1413 
1414  stanceOffsetIndex -= DayZPlayerConstants.STANCEIDX_ERECT;
1415  offset += stanceOffsets[stanceOffsetIndex];
1416 
1417  // if any additional height offset is defined, apply it
1418  if (m_WeaponLiftCheckVerticalOffset != 0)
1419  {
1420  offset[1] = offset[1] + m_WeaponLiftCheckVerticalOffset;
1421  }
1422 
1423  // 3. use the offset as the start position.
1424  // it will not be perfect, but it should reflect
1425  // the actual weapon transform more accurately
1426  start = offset.Multiply4(resTM);
1427  }
1428 
1430  /*usti_hlavne_position = ModelToWorld(usti_hlavne_position);
1431  distance = vector.Distance(start,usti_hlavne_position);*/
1432  distance = m_WeaponLength + GetEffectiveAttachmentLength();
1433 
1434  vector weaponStart = start + (m_ShoulderDistance * direction);
1435  vector weaponEnd = weaponStart + (distance * direction);
1436 
1437  // Draw diagnostics: Script -> Weapon -> Weapon Lift
1438  #ifdef DIAG_DEVELOPER
1439  if (DiagMenu.GetValue(DiagMenuIDs.WEAPON_LIFT_DEBUG))
1440  {
1441  vector diagNoAttachEnd = weaponStart + (m_WeaponLength * direction);
1442  int diagPtsShpFlgs = ShapeFlags.ONCE | ShapeFlags.NOOUTLINE;
1443  float diagPtsRadius = 0.025;
1444  Shape.CreateSphere(COLOR_GREEN, diagPtsShpFlgs, weaponStart, diagPtsRadius);
1445  Shape.CreateSphere(COLOR_YELLOW, diagPtsShpFlgs, diagNoAttachEnd, diagPtsRadius);
1446  Shape.CreateSphere(COLOR_BLUE, diagPtsShpFlgs, weaponEnd, diagPtsRadius);
1447  }
1448  #endif
1449 
1450  // For the physical cast, extend the distance by x%
1451  // to allow for smoother transition handling in some cases
1452  end = weaponEnd + ((0.1 * distance) * direction);
1453 
1454  // Prepare raycast params and perform the cast in fire geo
1455  RaycastRVParams rayParm = new RaycastRVParams(start, end, player, 0.04);
1456  rayParm.flags = CollisionFlags.ALLOBJECTS;
1457  rayParm.type = ObjIntersect.Fire;
1458 
1459  DbgUI.BeginCleanupScope();
1460  array<ref RaycastRVResult> results = {};
1461  if (!DayZPhysics.RaycastRVProxy(rayParm, results) || results.Count() == 0)
1462  {
1463  hit_pos = vector.Zero;
1464  hit_fraction = 0;
1465  }
1466  else
1467  {
1468  RaycastRVResult res = results[0];
1469 
1470  #ifdef DIAG_DEVELOPER // isect diag
1471  if (DiagMenu.GetValue(DiagMenuIDs.WEAPON_LIFT_DEBUG) == 2)
1472  {
1473  DbgUI.Begin("Weapon Lift Diag");
1474  {
1475  if (res.surface)
1476  {
1477  DbgUI.Text("Intersection data:");
1478  DbgUI.Text(" Name: " + res.surface.GetName());
1479  DbgUI.Text(" EntryName: " + res.surface.GetEntryName());
1480  DbgUI.Text(" SurfaceType: " + res.surface.GetSurfaceType());
1481 
1482  DbgUI.Text(" IsPassThrough: " + res.surface.IsPassthrough());
1483  DbgUI.Text(" IsSolid: " + res.surface.IsSolid());
1484  }
1485  else
1486  {
1487  DbgUI.Text("Intersection with no surface");
1488  }
1489  }
1490  DbgUI.End();
1491  }
1492  #endif // !isect diag
1493 
1494  if (LiftWeaponRaycastResultCheck(res))
1495  {
1496  hit_pos = res.pos;
1497  float len0 = (hit_pos - start).Length();
1498  float len1 = (end - start).Length();
1499  if (len0 <= 0 || len1 <= 0)
1500  {
1501  hit_fraction = 1;
1502  }
1503  else
1504  {
1505  hit_fraction = len0 / len1;
1506  }
1507  }
1508  else
1509  {
1510  hit_pos = vector.Zero;
1511  hit_fraction = 0;
1512  }
1513  }
1514 
1515  DbgUI.EndCleanupScope();
1516 
1517  // Draw diagnostics: Script -> Weapon -> Weapon Lift
1518  #ifdef DIAG_DEVELOPER
1519  if (DiagMenu.GetValue(DiagMenuIDs.WEAPON_LIFT_DEBUG))
1520  {
1521  const vector epsilon = "0 0.0002 0"; // to overcome excessive z-fighting for diag
1522  if (lastLiftPosition!=vector.Zero)
1523  {
1524  Shape.CreateArrow(start-epsilon, lastLiftPosition-epsilon, 0.05, COLOR_YELLOW, ShapeFlags.ONCE);
1525  }
1526 
1527  Shape.CreateArrow(start, weaponEnd, 0.05, COLOR_WHITE, ShapeFlags.ONCE | ShapeFlags.NOZBUFFER );
1528 
1529  if (hit_fraction != 0)
1530  {
1531  Shape.CreateArrow(start+epsilon, hit_pos+epsilon, 0.05, COLOR_RED, ShapeFlags.ONCE);
1532  }
1533  }
1534  #endif
1535 
1536  // Start by assuming that we want to retain state
1537  bool wantsLift = wasLift;
1538 
1539  // Sq. distance of weapon movement required to trigger lift (in)
1540  const float inThreshold = 0.002;
1541  // Sq. distance of weapon movement required to trigger lift (out)
1542  const float outThreshold = 0.003;
1543  const float noIsctOutThreshold = 0.01;
1544 
1545  // Min angle in degrees change from last lift to stop lifting
1546  // Base threshold of 0.75 degrees + 0.6 degrees per meter of weapon length
1547  float angleThreshold = 0.75 + Math.Clamp( m_WeaponLength * 0.6, 0, 1.5 );
1548 
1549  // Update state when a hit is registered
1550  if (hit_fraction != 0)
1551  {
1552  vector v1 = hit_pos - weaponEnd;
1553  vector v2 = hit_pos - end;
1554  float d = vector.Dot(v1, v2);
1555  // But leave some threshold where previous state is kept
1556  // to prevent excessive switches from occuring
1557  if (!wasLift && d > inThreshold)
1558  {
1559  wantsLift = true;
1560  }
1561  else if (wasLift && d < -outThreshold)
1562  {
1563  wantsLift = false;
1564  }
1565 
1566  m_LastLiftPosition = hit_pos;
1567  }
1568  else
1569  {
1570  // With no hit and no previous lift
1571  if (lastLiftPosition == vector.Zero)
1572  {
1573  wantsLift = false;
1574  m_LastLiftPosition = vector.Zero;
1575  }
1576  // See if previous hit wasn't very close to our current position,
1577  // in which case simply don't lift the weapon
1578  else
1579  {
1580  vector v3 = (lastLiftPosition - start).Normalized();
1581  vector v4 = (end-start).Normalized();
1582  float d2 = vector.Dot(v3, v4);
1583  // no isect, angle delta check
1584  if (Math.Acos(d2) > (angleThreshold * Math.DEG2RAD)) // if relative angle is > x degree, stop lifting
1585  {
1586  wantsLift = false;
1587  m_LastLiftPosition = vector.Zero;
1588  }
1589  // no isect, distance check
1590  else
1591  {
1592  float d3 = vector.Dot( lastLiftPosition - weaponEnd, (start-end).Normalized() );
1593  if (d3 < -noIsctOutThreshold)
1594  {
1595  wantsLift = false;
1596  m_LastLiftPosition = vector.Zero;
1597  }
1598  }
1599  }
1600  }
1601 
1602  // lift is desired
1603  if (wantsLift)
1604  {
1605  //Print(distance);
1606  m_LiftWeapon = true;
1607  return true;
1608  }
1609  return false;
1610  }
1611 
1613  bool LiftWeaponRaycastResultCheck(notnull RaycastRVResult res)
1614  {
1615  return res.surface.IsSolid();
1616  }
1617 
1619  float GetEffectiveAttachmentLength()
1620  {
1621  ItemBase attachment;
1622  if (HasBayonetAttached())
1623  {
1624  int bayonetIndex = GetBayonetAttachmentIdx();
1625  attachment = ItemBase.Cast(GetInventory().FindAttachment(bayonetIndex));
1626  }
1627  else
1628  {
1629  attachment = GetAttachedSuppressor();
1630  }
1631 
1632  if (attachment)
1633  {
1634  return Math.Max(attachment.m_ItemModelLength + attachment.m_ItemAttachOffset, 0);
1635  }
1636  else
1637  {
1638  return 0;
1639  }
1640  }
1641 
1642  void SetSyncJammingChance( float jamming_chance )
1643  {
1644  m_ChanceToJamSync = jamming_chance;
1645  }
1646 
1657  bool EjectCartridge(int muzzleIndex, out float ammoDamage, out string ammoTypeName)
1658  {
1659  if (IsChamberEjectable(muzzleIndex))
1660  {
1661  if (PopCartridgeFromChamber(muzzleIndex, ammoDamage, ammoTypeName))
1662  return true;
1663  }
1664  else if (GetInternalMagazineCartridgeCount(muzzleIndex) > 0)
1665  {
1666  if (PopCartridgeFromInternalMagazine(muzzleIndex, ammoDamage, ammoTypeName))
1667  return true;
1668  }
1669  return false;
1670  }
1671 
1672  bool CopyWeaponStateFrom(notnull Weapon_Base src)
1673  {
1674  float damage = 0.0;
1675  string type;
1676 
1677  for (int mi = 0; mi < src.GetMuzzleCount(); ++mi)
1678  {
1679  if (!src.IsChamberEmpty(mi))
1680  {
1681  if (src.GetCartridgeInfo(mi, damage, type))
1682  {
1683  PushCartridgeToChamber(mi, damage, type);
1684  }
1685  }
1686 
1687  for (int ci = 0; ci < src.GetInternalMagazineCartridgeCount(mi); ++ci)
1688  {
1689  if (src.GetInternalMagazineCartridgeInfo(mi, ci, damage, type))
1690  {
1691  PushCartridgeToInternalMagazine(mi, damage, type);
1692  }
1693  }
1694  }
1695 
1696  int dummy_version = int.MAX;
1697  PlayerBase parentPlayer = PlayerBase.Cast(src.GetHierarchyRootPlayer());
1698  if (!parentPlayer)
1699  dummy_version -= 1;
1701  src.OnStoreSave(ctx.GetWriteContext());
1702  OnStoreLoad(ctx.GetReadContext(), dummy_version);
1703  return true;
1704  }
1705 
1707  override void SetBayonetAttached(bool pState, int slot_idx = -1)
1708  {
1709  m_BayonetAttached = pState;
1710  m_BayonetAttachmentIdx = slot_idx;
1711  }
1712 
1713  override bool HasBayonetAttached()
1714  {
1715  return m_BayonetAttached;
1716  }
1717 
1718  override int GetBayonetAttachmentIdx()
1719  {
1720  return m_BayonetAttachmentIdx;
1721  }
1722 
1723  override void SetButtstockAttached(bool pState, int slot_idx = -1)
1724  {
1725  m_ButtstockAttached = pState;
1726  m_ButtstockAttachmentIdx = slot_idx;
1727  }
1728 
1729  override bool HasButtstockAttached()
1730  {
1731  return m_ButtstockAttached;
1732  }
1733 
1734  override int GetButtstockAttachmentIdx()
1735  {
1736  return m_ButtstockAttachmentIdx;
1737  }
1738 
1739  void HideWeaponBarrel(bool state)
1740  {
1741  if ( !GetGame().IsDedicatedServer() )//hidden for client only
1742  {
1743  ItemOptics optics = GetAttachedOptics();
1744  if ( optics && !optics.AllowsDOF() && m_weaponHideBarrelIdx != -1 )
1745  {
1746  SetSimpleHiddenSelectionState(m_weaponHideBarrelIdx,!state);
1747  }
1748  }
1749  }
1750 
1751  void ShowMagazine()
1752  {
1753  if (m_magazineSimpleSelectionIndex > -1)
1754  SetSimpleHiddenSelectionState(m_magazineSimpleSelectionIndex,1);
1755  else
1756  SelectionMagazineShow();
1757  }
1758 
1759  void HideMagazine()
1760  {
1761  if (m_magazineSimpleSelectionIndex > -1)
1762  SetSimpleHiddenSelectionState(m_magazineSimpleSelectionIndex,0);
1763  else
1764  SelectionMagazineHide();
1765  }
1766 
1767  override EntityAI ProcessMeleeItemDamage(int mode = 0)
1768  {
1769  EntityAI attachment;
1770 
1771  switch (mode)
1772  {
1773  case 0:
1774  super.ProcessMeleeItemDamage();
1775  break;
1776 
1777  case 1:
1778  attachment = GetInventory().FindAttachment(m_ButtstockAttachmentIdx);
1779  break;
1780 
1781  case 2:
1782  attachment = GetInventory().FindAttachment(m_BayonetAttachmentIdx);
1783  break;
1784 
1785  default:
1786  super.ProcessMeleeItemDamage();
1787  break;
1788  }
1789 
1790  if (attachment)
1791  {
1792  attachment.ProcessMeleeItemDamage();
1793  return attachment;
1794  }
1795 
1796  return this;
1797  }
1798 
1799  bool IsShowingChamberedBullet()
1800  {
1801  return true;
1802  }
1803 
1804  int GetBurstCount()
1805  {
1806  return m_BurstCount;
1807  }
1808 
1809  void ResetBurstCount()
1810  {
1811  m_BurstCount = 0;
1812  }
1813 
1814  override void SetActions()
1815  {
1816  super.SetActions();
1823 
1825  AddAction(FirearmActionLoadBulletQuick); // Easy reload
1826  }
1827 
1828  override bool CanBeUsedForSuicide()
1829  {
1830  if (!ConfigGetBool("isSuicideWeapon"))
1831  return false;
1832 
1833  return super.CanBeUsedForSuicide();
1834  }
1835 
1836  //Debug menu Spawn Ground Special
1837  override void OnDebugSpawn()
1838  {
1839  SpawnAmmo("", SAMF_DEFAULT);
1840  }
1841 };
1842 
ItemBase
Definition: inventoryitem.c:730
LoadCurrentFSMState
override bool LoadCurrentFSMState(ParamsReadContext ctx, int version)
Definition: weaponchambering.c:867
ActionTurnOffWeaponFlashlight
Definition: actionturnoffweaponflashlight.c:1
GetGame
proto native CGame GetGame()
PistolAnimState
PistolAnimState
Definition: pistol_base.c:2
MAX_CAPACITY_MAG
@ MAX_CAPACITY_MAG
Instead of randomizing when type is empty, it looks for the one which has the highest capacity.
Definition: weapon_base.c:28
IsIdle
override bool IsIdle()
Definition: weaponstablestate.c:91
CanEnterIronsights
AugOptic CanEnterIronsights
Definition: aug.c:63
GetWeightSpecialized
override protected float GetWeightSpecialized(bool forceRecalc=false)
Definition: itembase.c:3341
Error
void Error(string err)
Messagebox with error message.
Definition: endebug.c:90
AmmoTypesAPI
Definition: ammotypes.c:1
CALL_CATEGORY_GAMEPLAY
const int CALL_CATEGORY_GAMEPLAY
Definition: tools.c:10
LogManager
Definition: debug.c:734
DbgUI
Definition: dbgui.c:59
EEItemLocationChanged
override void EEItemLocationChanged(notnull InventoryLocation oldLoc, notnull InventoryLocation newLoc)
Definition: remotedetonator.c:72
DiagMenu
Definition: endebug.c:232
TStringArray
array< string > TStringArray
Definition: enscript.c:685
Synchronize
void Synchronize(eInjuryHandlerLevels level)
Definition: injuryhandler.c:173
FirearmActionMechanicManipulate
Definition: firearmactionmechanicmanipulate.c:1
IsDamageDestroyed
protected bool IsDamageDestroyed(ActionTarget target)
Definition: actionbase.c:913
WeaponEventID
WeaponEventID
identifier for events. mainly for rpc purposes
Definition: events.c:5
WeightDebugData
Definition: debug.c:939
ScriptRemoteInputUserData
Definition: gameplay.c:142
wpnDebugPrint
void wpnDebugPrint(string s)
Definition: debug.c:9
CanReleaseAttachment
override bool CanReleaseAttachment(EntityAI attachment)
Definition: fireplacebase.c:2654
IsChamberFull
bool IsChamberFull(int idx)
Definition: weaponstablestate.c:154
Weapon
script counterpart to engine's class Weapon
Definition: inventoryitem.c:48
FirearmActionLoadBulletQuick
FirearmActionLoadMultiBullet FirearmActionLoadBulletQuick
Print
proto void Print(void var)
Prints content of variable to console/log.
InventoryLocation
InventoryLocation.
Definition: inventorylocation.c:27
IsChamberFiredOut
bool IsChamberFiredOut(int idx)
Definition: weaponstablestate.c:153
ECE_PLACE_ON_SURFACE
const int ECE_PLACE_ON_SURFACE
Definition: centraleconomy.c:37
Ammunition_Base
ammo pile base
Definition: ammunitionpiles.c:2
COLOR_YELLOW
const int COLOR_YELLOW
Definition: constants.c:67
m_action
class WeaponEndAction extends WeaponStartAction m_action
RaycastRVResult
Definition: dayzphysics.c:98
OnInventoryEnter
override protected void OnInventoryEnter(Man player)
Definition: fireworksbase.c:71
NONE
@ NONE
Attached magazine will be full and no round will be chambered.
Definition: weapon_base.c:14
OnInventoryExit
override protected void OnInventoryExit(Man player)
Definition: fireworksbase.c:79
DayZPlayer
Definition: dayzplayerimplement.c:72
QUANTITY_RNG
@ QUANTITY_RNG
Randomizes the quantity of the bullets in the spawned magazine.
Definition: weapon_base.c:22
ErrorEx
enum ShapeType ErrorEx
HumanMovementState
Definition: human.c:1125
IsJammed
override bool IsJammed()
Definition: crossbow.c:22
Serializer
Serialization general interface. Serializer API works with:
Definition: serializer.c:55
CollisionFlags
CollisionFlags
Definition: endebug.c:140
FirearmActionUnjam
Definition: firearmactionunjam.c:1
CHAMBER_RNG
@ CHAMBER_RNG
Maybe chambers bullets (sequential rng) example: 1 1 1 0 0 0.
Definition: weapon_base.c:18
DiagMenuIDs
DiagMenuIDs
Definition: ediagmenuids.c:1
GetPosition
class JsonUndergroundAreaTriggerData GetPosition
Definition: undergroundarealoader.c:9
RandomGeneratorSyncUsage
RandomGeneratorSyncUsage
Definition: randomgeneratorsyncmanager.c:1
EffectSound
Wrapper class for managing sound through SEffectManager.
Definition: effectsound.c:4
IsWaitingForActionFinish
class WeaponChambering_Cartridge_InnerMag extends WeaponChambering_Base IsWaitingForActionFinish
Definition: weaponchambering.c:190
IncreaseOverheating
void IncreaseOverheating(ItemBase weapon, string ammoType, ItemBase muzzle_owner, ItemBase suppressor, string config_to_search)
Definition: itembase.c:497
PlayerBase
Definition: playerbaseclient.c:1
RaycastRVParams
Definition: dayzphysics.c:49
vector
Definition: enconvert.c:105
AfterStoreLoad
void AfterStoreLoad()
Definition: emotemanager.c:577
RecoilBase
Definition: recoilbase.c:1
DayZPlayerCamera1stPerson
Definition: dayzplayercameravehicles.c:4
ActionTurnOnWeaponFlashlight
Definition: actionturnonweaponflashlight.c:1
fsmDebugSpam
void fsmDebugSpam(string s)
Definition: hfsmbase.c:9
DayZPlayerConstants
DayZPlayerConstants
defined in C++
Definition: dayzplayer.c:601
SaveCurrentFSMState
override bool SaveCurrentFSMState(ParamsWriteContext ctx)
Definition: weaponchambering.c:848
InventoryLocationType
InventoryLocationType
types of Inventory Location
Definition: inventorylocation.c:3
FirearmActionLoadBullet
Definition: firearmactionloadbullet.c:1
ShapeFlags
ShapeFlags
Definition: endebug.c:125
HumanCommandMove
Definition: human.c:433
g_Game
DayZGame g_Game
Definition: dayzgame.c:3727
CanRemoveFromHands
override bool CanRemoveFromHands(EntityAI parent)
Definition: explosivesbase.c:267
AddAction
void AddAction(typename actionName)
Definition: advancedcommunication.c:86
Object
Definition: objecttyped.c:1
FSMTransition
represents transition src -— event[guard]/action -—|> dst
CHAMBER_RNG_SPORADIC
@ CHAMBER_RNG_SPORADIC
Maybe chambers bullets (full random) example: 0 1 0 0 1 1.
Definition: weapon_base.c:20
COLOR_GREEN
const int COLOR_GREEN
Definition: constants.c:65
AMMO_CHAMBER_RNG
@ AMMO_CHAMBER_RNG
Fully randomizes the ammo type instead of picking one random for all chambers (needs to have type as ...
Definition: weapon_base.c:26
WeaponTransition
enum FSMTransition WeaponTransition
COLOR_RED
const int COLOR_RED
Definition: constants.c:64
DefaultRecoil
Definition: defaultrecoil.c:1
CHAMBER
@ CHAMBER
Chambers bullets.
Definition: weapon_base.c:16
FirearmActionAttachMagazineQuick
Definition: firearmactionattachmagazine.c:225
array< ref AbilityRecord >
AMMO_MAG_RNG
@ AMMO_MAG_RNG
Fully randomizes the ammo type instead of picking one random for the entire mag (needs to have type a...
Definition: weapon_base.c:24
AbilityRecord
pair ( action, actionType )
Definition: weapon_base.c:4
PropertyModifiers
Definition: propertymodifiers.c:1
GetDebugName
override string GetDebugName()
Definition: dayzplayer.c:1126
OnStoreSave
void OnStoreSave(ParamsWriteContext ctx)
Definition: modifierbase.c:229
HumanCommandWeapons
Definition: human.c:986
WeaponEventBase
signalize mechanism manipulation
Definition: events.c:34
WeaponStateBase
represent weapon state base
Definition: bullethide.c:1
Weapon_Base
shorthand
Definition: boltactionrifle_base.c:5
DayZPhysics
Definition: dayzphysics.c:123
EEInit
override void EEInit()
Definition: contaminatedarea.c:27
CanBeUsedForSuicide
bool CanBeUsedForSuicide()
Definition: itembase.c:4661
Type
string Type
Definition: jsondatacontaminatedarea.c:11
ProcessEventResult
ProcessEventResult
Definition: fsmbase.c:40
ItemOptics
Definition: itemoptics.c:1
WeaponFSM
weapon finite state machine
Math
Definition: enmath.c:6
INPUT_UDT_WEAPON_REMOTE_EVENT
const int INPUT_UDT_WEAPON_REMOTE_EVENT
Definition: _constants.c:12
Class
Super root of all classes in Enforce script.
Definition: enscript.c:10
EEItemAttached
override void EEItemAttached(EntityAI item, string slot_name)
Definition: basebuildingbase.c:520
IsWeaponOpen
bool IsWeaponOpen()
Whether the gun is open.
Definition: weaponstablestate.c:134
OnItemLocationChanged
override void OnItemLocationChanged(EntityAI old_owner, EntityAI new_owner)
Definition: combinationlock.c:75
SEffectManager
Manager class for managing Effect (EffectParticle, EffectSound)
Definition: effectmanager.c:5
FirearmActionAttachMagazine
Definition: firearmactionattachmagazine.c:11
COLOR_BLUE
const int COLOR_BLUE
Definition: constants.c:66
Vector
proto native vector Vector(float x, float y, float z)
Vector constructor from components.
EEItemDetached
override void EEItemDetached(EntityAI item, string slot_name)
Definition: basebuildingbase.c:529
OnStoreLoad
bool OnStoreLoad(ParamsReadContext ctx, int version)
Definition: modifiersmanager.c:270
EntityAI
Definition: building.c:5
COLOR_WHITE
const int COLOR_WHITE
Definition: constants.c:63
GetType
override int GetType()
Definition: huddebugwincharagents.c:49
PhxInteractionLayers
PhxInteractionLayers
Definition: dayzphysics.c:1
Math3D
Definition: enmath3d.c:27
Shape
class DiagMenu Shape
don't call destructor directly. Use Destroy() instead
WeaponStableState
represents weapon's stable state (i.e. the basic states that the weapon will spend the most time in)
Definition: crossbow.c:26
ScriptReadWriteContext
Definition: gameplay.c:133
MuzzleState
MuzzleState
Definition: weaponstablestate.c:14
DayZPlayerInstanceType
DayZPlayerInstanceType
defined in C++
Definition: dayzplayer.c:1058