trapbase.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. enum SoundTypeTrap
  2. {
  3. ACTIVATING = 5,
  4. }
  5. class TrapBase extends ItemBase
  6. {
  7. #ifdef SERVER
  8. protected const int SPAWN_FLAGS = ECE_CREATEPHYSICS;
  9. #else
  10. protected const int SPAWN_FLAGS = ECE_LOCAL;
  11. #endif
  12. protected const int DAMAGE_TRIGGER_MINE = 75;
  13. protected const float UPDATE_TIMER_INTERVAL = 0.05;
  14. float m_InitWaitTime; //After this time after deployment, the trap is activated
  15. bool m_NeedActivation; //If activation of trap is needed
  16. float m_DefectRate; //Added damage after trap activation
  17. float m_DamagePlayers; //How much damage player gets when caught
  18. float m_DamageOthers; //How much damage player gets when caught
  19. bool m_AddActivationDefect; // Damage trap after activation
  20. bool m_AddDeactivationDefect; // Damage trap after deactivation
  21. protected bool m_IsActive; // True means that the trap is ready to detonate
  22. protected bool m_IsInProgress;
  23. protected bool m_Disarmed = false; //! DEPRECATED Used for explosive traps to prevent detonation after destroying through disarm action
  24. bool m_WasActivatedOrDeactivated;
  25. string m_AnimationPhaseGrounded;
  26. string m_AnimationPhaseSet;
  27. string m_AnimationPhaseTriggered;
  28. string m_InfoSetup;
  29. string m_InfoDeactivated;
  30. string m_InfoDamageManipulation;
  31. string m_InfoDamage;
  32. string m_InfoActivationTime;
  33. protected ref Timer m_Timer;
  34. protected ref Timer m_UpdateTimer;
  35. protected TrapTrigger m_TrapTrigger;
  36. protected ref array<int> m_ClothingDmg;
  37. void TrapBase()
  38. {
  39. m_IsInProgress = false;
  40. m_NeedActivation = true;
  41. m_InitWaitTime = 5; //After this time after deployment, the trap is activated
  42. m_DefectRate = 15; //Added damage after trap activation
  43. m_DamagePlayers = 25; //How much damage player gets when caught
  44. m_DamageOthers = 100; //How much damage others gets when caught
  45. m_AddActivationDefect = false;
  46. m_AddDeactivationDefect = false;
  47. m_WasActivatedOrDeactivated = false;
  48. m_AnimationPhaseGrounded = "";
  49. m_AnimationPhaseSet = "";
  50. m_AnimationPhaseTriggered = "";
  51. m_InfoSetup = "#STR_TrapBase0";
  52. m_InfoDeactivated = "#STR_TrapBase1";
  53. m_InfoDamageManipulation = "#STR_TrapBase2";
  54. m_InfoDamage = "#STR_TrapBase3";
  55. m_InfoActivationTime = "#STR_TrapBase4" + m_InitWaitTime.ToString() + "#STR_TrapBase5";
  56. m_UpdateTimer = new ref Timer(); //! timer calling OnUpdate in configured interval
  57. RegisterNetSyncVariableBool("m_IsActive");
  58. RegisterNetSyncVariableBool("m_IsInProgress");
  59. RegisterNetSyncVariableBool("m_IsSoundSynchRemote");
  60. RegisterNetSyncVariableBool("m_IsDeploySound");
  61. }
  62. void OnUpdate(EntityAI victim);
  63. TrapTrigger GetTrapTrigger()
  64. {
  65. return m_TrapTrigger;
  66. }
  67. //! this event is called all variables are synchronized on client
  68. override void OnVariablesSynchronized()
  69. {
  70. super.OnVariablesSynchronized();
  71. if (IsDeploySound())
  72. PlayDeploySound();
  73. if (GetGame().IsMultiplayer())
  74. {
  75. if (m_IsActive && !m_IsInProgress)
  76. SetActive();
  77. if (m_IsInProgress && !m_IsActive)
  78. StartActivate(null);
  79. }
  80. }
  81. override void EEDelete(EntityAI parent)
  82. {
  83. super.EEDelete(parent);
  84. if (GetGame() && m_TrapTrigger)
  85. {
  86. GetGame().ObjectDelete(m_TrapTrigger);
  87. m_TrapTrigger = null;
  88. }
  89. }
  90. override void OnStoreSave(ParamsWriteContext ctx)
  91. {
  92. super.OnStoreSave(ctx);
  93. ctx.Write(m_IsActive);
  94. ctx.Write(m_IsInProgress);
  95. }
  96. //----------------------------------------------------------------
  97. override bool OnStoreLoad(ParamsReadContext ctx, int version)
  98. {
  99. if ( !super.OnStoreLoad(ctx, version) )
  100. return false;
  101. bool b_is_active = false;
  102. if ( !ctx.Read( b_is_active ) )
  103. b_is_active = false;
  104. bool b_is_in_progress = false;
  105. if ( !ctx.Read( b_is_in_progress ) )
  106. b_is_in_progress = false;
  107. if ( b_is_active )
  108. {
  109. SetActive();
  110. }
  111. if (b_is_in_progress && !b_is_active)
  112. {
  113. StartActivate(null);
  114. }
  115. return true;
  116. }
  117. bool IsActive()
  118. {
  119. return m_IsActive && m_IsInProgress == false && GetHierarchyRootPlayer() == null;
  120. }
  121. bool IsInactive()
  122. {
  123. return !IsActive() && m_IsInProgress == false && GetHierarchyRootPlayer() == null;
  124. }
  125. // trap cannot be taken when is activated
  126. override bool IsTakeable()
  127. {
  128. if ( m_IsInProgress == false && !IsActive() )
  129. {
  130. return true;
  131. }
  132. return false;
  133. }
  134. bool IsActivable()
  135. {
  136. return !IsActive() && GetHierarchyRootPlayer() == null && GetHierarchyParent() == null && m_IsInProgress == false && !IsRuined() && m_NeedActivation;
  137. }
  138. bool IsPlaceable()
  139. {
  140. if ( GetHierarchyRootPlayer() != null && GetHierarchyRootPlayer().GetHumanInventory().GetEntityInHands() == this )
  141. {
  142. PlayerBase player = PlayerBase.Cast( GetHierarchyRootPlayer() );
  143. vector player_pos = player.GetPosition();
  144. vector aim_pos = player.GetAimPosition();
  145. if ( vector.DistanceSq( player_pos, aim_pos ) <= ( Math.SqrFloat( 1.5 ) ) )
  146. {
  147. return IsPlaceableAtPosition( aim_pos );
  148. }
  149. }
  150. return false;
  151. }
  152. bool IsPlaceableAtPosition( vector position )
  153. {
  154. if ( position[1] < g_Game.SurfaceGetSeaLevelMax() + 0.03 )
  155. {
  156. return false;
  157. }
  158. else if ( GetGame().SurfaceIsPond( position[0], position[2] ) )
  159. {
  160. return false;
  161. }
  162. return true;
  163. }
  164. void Disarm()
  165. {
  166. SetInactive(false);
  167. RefreshState();
  168. GetGame().RPCSingleParam(this, ERPCs.RPC_TRAP_DISARM, null, true);
  169. OnDisarm();
  170. }
  171. //! also called from RPC on client
  172. void OnDisarm();
  173. void SnapOnObject(EntityAI victim)
  174. {
  175. if ( GetGame().IsServer() )
  176. {
  177. if ( m_Timer )
  178. {
  179. m_Timer.Stop();
  180. }
  181. RefreshState();
  182. if (m_DamagePlayers > 0)
  183. {
  184. if (victim)
  185. {
  186. if ( victim.IsInherited(SurvivorBase))
  187. {
  188. victim.DecreaseHealth("", "", m_DamagePlayers);
  189. }
  190. else if (victim.IsInherited(DayZCreatureAI))
  191. {
  192. victim.DecreaseHealth("", "", m_DamageOthers);
  193. }
  194. else if (victim.IsInherited(ItemBase))
  195. {
  196. ItemBase victim_item = ItemBase.Cast(victim);
  197. float damage_coef = 1;
  198. if (victim_item.HasQuantity() && victim_item.GetQuantityMax() != 0 && victim_item.GetQuantity() > 0)
  199. {
  200. damage_coef = victim_item.GetQuantityMax() / victim_item.GetQuantity(); // Lower quantity increases damage exposure
  201. }
  202. if (damage_coef > 0)
  203. {
  204. int item_size_x = 1;
  205. int item_size_y = 1;
  206. GetGame().GetInventoryItemSize(victim_item, item_size_x, item_size_y);
  207. float add_damage = 300 * damage_coef / Math.Clamp(item_size_x * item_size_y, 1, int.MAX);
  208. victim_item.DecreaseHealth("", "", add_damage);
  209. }
  210. }
  211. }
  212. }
  213. Synch(victim);
  214. }
  215. OnSteppedOn(victim);
  216. }
  217. void RemoveFromObject(EntityAI victim)
  218. {
  219. OnSteppedOut(victim);
  220. }
  221. void OnSteppedOn(EntityAI victim)
  222. {
  223. SetInactive(false);
  224. }
  225. void OnSteppedOut(EntityAI victim); //! keeping "step" here for consistency only
  226. // Synchronizes states
  227. protected void Synch(EntityAI victim)
  228. {
  229. if (GetGame().IsServer())
  230. {
  231. if (victim && !victim.GetAllowDamage())
  232. return;
  233. Param1<EntityAI> p = new Param1<EntityAI>(victim);
  234. GetGame().RPCSingleParam(this, ERPCs.RPC_TRAP_VICTIM, p, true);
  235. }
  236. }
  237. // On server -> client synchronization
  238. override void OnRPC(PlayerIdentity sender, int rpc_type, ParamsReadContext ctx)
  239. {
  240. super.OnRPC(sender, rpc_type, ctx);
  241. if ( !GetGame().IsDedicatedServer() )
  242. {
  243. switch (rpc_type)
  244. {
  245. case ERPCs.RPC_TRAP_VICTIM:
  246. Param1<EntityAI> victim = new Param1<EntityAI>(null);
  247. if (ctx.Read(victim))
  248. {
  249. if (victim.param1)
  250. SnapOnObject(victim.param1);
  251. }
  252. break;
  253. case ERPCs.RPC_TRAP_DISARM:
  254. OnDisarm();
  255. break;
  256. case SoundTypeTrap.ACTIVATING:
  257. Param1<bool> p = new Param1<bool>(false);
  258. bool isActivating = false;
  259. if (ctx.Read(p))
  260. isActivating = p.param1;
  261. break;
  262. }
  263. }
  264. }
  265. void RefreshState()
  266. {
  267. if ( !m_WasActivatedOrDeactivated )
  268. {
  269. return;
  270. }
  271. if ( GetGame().IsServer() )
  272. {
  273. // item is owned by player
  274. if ( GetHierarchyRootPlayer() != NULL && m_AnimationPhaseGrounded != "" )
  275. {
  276. SetAnimationPhase( m_AnimationPhaseSet, 1 );
  277. if ( m_AnimationPhaseTriggered != m_AnimationPhaseGrounded )
  278. {
  279. SetAnimationPhase( m_AnimationPhaseTriggered, 1 );
  280. }
  281. SetAnimationPhase( m_AnimationPhaseGrounded, 0 );
  282. }
  283. // item is set active
  284. else if ( IsActive() )
  285. {
  286. if ( m_AnimationPhaseGrounded != "" )
  287. {
  288. SetAnimationPhase( m_AnimationPhaseGrounded, 1 );
  289. }
  290. if ( m_AnimationPhaseSet != "" && m_AnimationPhaseTriggered != "" )
  291. {
  292. SetAnimationPhase( m_AnimationPhaseTriggered, 1 );
  293. SetAnimationPhase( m_AnimationPhaseSet, 0 );
  294. }
  295. }
  296. // item is inactive and not owned by player (on the ground)
  297. else if ( IsInactive() )
  298. {
  299. if ( m_AnimationPhaseGrounded != "" && m_AnimationPhaseTriggered != m_AnimationPhaseGrounded )
  300. {
  301. SetAnimationPhase( m_AnimationPhaseGrounded, 1 );
  302. }
  303. if ( m_AnimationPhaseSet != "" && m_AnimationPhaseTriggered != "" )
  304. {
  305. SetAnimationPhase( m_AnimationPhaseSet, 1 );
  306. SetAnimationPhase( m_AnimationPhaseTriggered, 0 );
  307. }
  308. }
  309. }
  310. }
  311. void SetupTrap()
  312. {
  313. if (GetGame().IsServer())
  314. {
  315. if (GetHierarchyRootPlayer() && GetHierarchyRootPlayer().CanDropEntity(this))
  316. SetupTrapPlayer(PlayerBase.Cast(GetHierarchyRootPlayer()));
  317. }
  318. }
  319. void SetupTrapPlayer( PlayerBase player, bool set_position = true )
  320. {
  321. if (GetGame().IsServer())
  322. {
  323. if (set_position)
  324. {
  325. player.LocalDropEntity(this);
  326. vector trapPos = player.GetDirection() * 1.5;
  327. trapPos[1] = 0;
  328. SetPosition(player.GetPosition() + trapPos);
  329. }
  330. if (m_NeedActivation == false)
  331. SetActive();
  332. }
  333. }
  334. void AddDefect()
  335. {
  336. if ( GetGame().IsServer() )
  337. {
  338. DecreaseHealth( "", "", m_DefectRate );
  339. }
  340. }
  341. void SetActive()
  342. {
  343. m_WasActivatedOrDeactivated = true;
  344. m_IsInProgress = false;
  345. m_IsActive = true;
  346. if (m_AddActivationDefect)
  347. {
  348. AddDefect();
  349. }
  350. if (GetGame().IsServer())
  351. {
  352. CreateTrigger();
  353. RefreshState();
  354. SetSynchDirty();
  355. }
  356. OnActivate();
  357. }
  358. void OnActivate();
  359. void StartActivate(PlayerBase player)
  360. {
  361. if (GetGame().IsServer())
  362. {
  363. m_Timer = new Timer(CALL_CATEGORY_SYSTEM);
  364. HideSelection("safety_pin");
  365. if (m_InitWaitTime > 0)
  366. {
  367. m_IsInProgress = true;
  368. m_Timer.Run(m_InitWaitTime, this, "SetActive");
  369. SetSynchDirty();
  370. }
  371. else
  372. SetActive();
  373. }
  374. }
  375. void StartDeactivate(PlayerBase player);
  376. void SetInactive(bool stop_timer = true)
  377. {
  378. m_WasActivatedOrDeactivated = true;
  379. m_IsActive = false;
  380. if (m_Timer && stop_timer)
  381. m_Timer.Stop();
  382. if (m_AddDeactivationDefect)
  383. AddDefect();
  384. SetSynchDirty();
  385. DeleteTrigger();
  386. RefreshState();
  387. }
  388. void CreateTrigger()
  389. {
  390. if (Class.CastTo(m_TrapTrigger, GetGame().CreateObjectEx("TrapTrigger", GetPosition(), SPAWN_FLAGS)))
  391. {
  392. vector mins = "-0.01 -0.05 -0.01";
  393. vector maxs = "0.01 0.5 0.01";
  394. m_TrapTrigger.SetOrientation(GetOrientation());
  395. m_TrapTrigger.SetExtents(mins, maxs);
  396. m_TrapTrigger.SetParentObject(this);
  397. GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).Call(DeferredEnableTrigger);
  398. }
  399. }
  400. void DeleteTrigger()
  401. {
  402. if (m_TrapTrigger)
  403. {
  404. m_TrapTrigger.SetParentObject(null);
  405. m_TrapTrigger.DeleteSafe();
  406. }
  407. }
  408. void DeferredEnableTrigger()
  409. {
  410. if (m_TrapTrigger)
  411. m_TrapTrigger.SetEnabled();
  412. }
  413. override void OnItemLocationChanged(EntityAI old_owner, EntityAI new_owner)
  414. {
  415. super.OnItemLocationChanged(old_owner, new_owner);
  416. if (GetGame().IsServer())
  417. {
  418. RefreshState();
  419. // TAKE ACTIVE TRAP FROM VICINITY
  420. if (old_owner == NULL && new_owner != NULL && IsActive())
  421. {
  422. // TAKE INTO HANDS
  423. if ( new_owner.IsPlayer() )
  424. {
  425. SnapOnObject(new_owner);
  426. }
  427. else if (new_owner.GetHierarchyRootPlayer())
  428. {
  429. SnapOnObject(new_owner.GetHierarchyRootPlayer());
  430. }
  431. }
  432. }
  433. }
  434. override void EEItemAttached(EntityAI item, string slot_name)
  435. {
  436. super.EEItemAttached(item, slot_name);
  437. if (GetGame().IsServer())
  438. RefreshState();
  439. }
  440. override void EEItemDetached(EntityAI item, string slot_name)
  441. {
  442. super.EEItemDetached(item, slot_name);
  443. if (GetGame().IsServer())
  444. RefreshState();
  445. }
  446. override void OnPlacementComplete(Man player, vector position = "0 0 0", vector orientation = "0 0 0")
  447. {
  448. super.OnPlacementComplete(player, position, orientation);
  449. if (GetGame().IsServer())
  450. {
  451. SetOrientation(orientation);
  452. SetPosition(position);
  453. PlaceOnSurface();
  454. SetSynchDirty();
  455. }
  456. }
  457. override bool CanPutInCargo( EntityAI parent )
  458. {
  459. if ( !super.CanPutInCargo(parent) )
  460. {
  461. return false;
  462. }
  463. return IsTakeable();
  464. }
  465. override bool CanPutIntoHands( EntityAI parent )
  466. {
  467. if ( !super.CanPutIntoHands( parent ) )
  468. {
  469. return false;
  470. }
  471. return IsTakeable();
  472. }
  473. override bool CanRemoveFromHands( EntityAI parent )
  474. {
  475. return IsTakeable();
  476. }
  477. override bool CanBePlaced( Man player, vector position )
  478. {
  479. return IsPlaceableAtPosition( position );
  480. }
  481. //! DEPRECATED Set if trap can be disarmed using ActionClapBearTrapWithThisItem
  482. bool CanBeClapped()
  483. {
  484. return true;
  485. }
  486. //Set if trap can be disarmed using trap-specific action
  487. bool CanBeDisarmed()
  488. {
  489. return false;
  490. }
  491. //! DEPRECATED
  492. void SetDisarmed( bool disarmed )
  493. {
  494. m_Disarmed = disarmed;
  495. }
  496. //! DEPRECATED
  497. bool GetDisarmed()
  498. {
  499. return m_Disarmed;
  500. }
  501. //================================================================
  502. // ADVANCED PLACEMENT
  503. //================================================================
  504. override void SetActions()
  505. {
  506. super.SetActions();
  507. AddAction(ActionActivateTrap);
  508. }
  509. // HELPERS
  510. protected EntityAI GetClosestCarWheel(EntityAI victim)
  511. {
  512. //! carscript specific handling (not all traps are uses this)
  513. vector trapPosXZ = GetPosition();
  514. trapPosXZ[1] = 0;
  515. GameInventory inv = victim.GetInventory();
  516. for (int i = 0; i < inv.AttachmentCount(); i++)
  517. {
  518. //! ruined wheel, bail out
  519. EntityAI wheelEntity = inv.GetAttachmentFromIndex(i);
  520. if (wheelEntity && wheelEntity.Type() == CarWheel_Ruined)
  521. {
  522. continue;
  523. }
  524. //! ignore all spare wheel
  525. int slotId;
  526. string slotName;
  527. wheelEntity.GetInventory().GetCurrentAttachmentSlotInfo(slotId, slotName);
  528. slotName.ToLower();
  529. if (slotName.Contains("spare"))
  530. {
  531. continue
  532. }
  533. //! actual, healthy wheel, let it pass
  534. if (wheelEntity && wheelEntity.IsInherited(CarWheel))
  535. {
  536. vector entPosXZ = wheelEntity.GetPosition();
  537. entPosXZ[1] = 0;
  538. if (vector.Distance(trapPosXZ, entPosXZ) < 1)
  539. {
  540. return wheelEntity;
  541. }
  542. }
  543. }
  544. return null;
  545. }
  546. protected void DamageClothing(PlayerBase player)
  547. {
  548. //Array used to find all relevant information about currently equipped clothes
  549. array<ClothingBase> equippedClothes = new array<ClothingBase>;
  550. equippedClothes.Insert(ClothingBase.Cast(player.GetItemOnSlot("LEGS")));
  551. equippedClothes.Insert(ClothingBase.Cast(player.GetItemOnSlot("BACK")));
  552. equippedClothes.Insert(ClothingBase.Cast(player.GetItemOnSlot("VEST")));
  553. equippedClothes.Insert(ClothingBase.Cast(player.GetItemOnSlot("HeadGear")));
  554. equippedClothes.Insert(ClothingBase.Cast(player.GetItemOnSlot("Mask")));
  555. equippedClothes.Insert(ClothingBase.Cast(player.GetItemOnSlot("BODY")));
  556. equippedClothes.Insert(ClothingBase.Cast(player.GetItemOnSlot("FEET")));
  557. equippedClothes.Insert(ClothingBase.Cast(player.GetItemOnSlot("GLOVES")));
  558. //Damage all currently equipped clothes
  559. for (int i = 0; i < equippedClothes.Count(); i++)
  560. {
  561. //If no item is equipped on slot, slot is ignored
  562. if (equippedClothes[i] == null)
  563. {
  564. continue;
  565. }
  566. equippedClothes[i].DecreaseHealth(m_ClothingDmg[i], false);
  567. }
  568. }
  569. //!DEPRECATED
  570. protected ref EffectSound m_DeployLoopSound; //DEPRECATED in favor of m_DeployLoopSoundEx
  571. void PlayDeployLoopSound(); //!DEPRECATED
  572. void StopDeployLoopSound(); //!DEPRECATED
  573. }