trapbase.c 15 KB

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