magazine.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. typedef Magazine Magazine_Base;
  2. enum CartridgeType
  3. {
  4. None = 0,
  5. Pistol = 1,
  6. Intermediate = 2,
  7. FullPower = 3,
  8. Shell = 4
  9. }
  10. enum ProjectileType
  11. {
  12. None = 0,
  13. Tracer = 1,
  14. AP = 2
  15. }
  16. class AmmoData
  17. {
  18. bool m_IsValid;
  19. CartridgeType m_CartridgeType;
  20. ProjectileType m_ProjectileType;
  21. void AmmoData( string init_type )
  22. {
  23. m_IsValid = GetGame().ConfigIsExisting( "CfgMagazines " + init_type );
  24. if ( m_IsValid )
  25. {
  26. m_CartridgeType = GetGame().ConfigGetInt( "CfgMagazines " + init_type + " iconCartridge" );
  27. m_ProjectileType = GetGame().ConfigGetInt( "CfgMagazines " + init_type + " iconType" );
  28. }
  29. }
  30. }
  31. class Magazine : InventoryItemSuper
  32. {
  33. protected static ref map<string, ref AmmoData> m_AmmoData;
  34. ref array<string> m_CompatiableAmmo;
  35. ref array<float> m_ChanceToJam;
  36. protected float m_ManipulationDamage;
  37. void Magazine ()
  38. {
  39. m_ChanceToJam = new array<float>;
  40. InitReliability(m_ChanceToJam);
  41. m_ManipulationDamage = ConfigGetFloat("manipulationDamage");
  42. m_CompatiableAmmo = new array<string>;
  43. ConfigGetTextArray("ammoItems", m_CompatiableAmmo);
  44. if ( !GetGame().IsDedicatedServer() )
  45. {
  46. if ( !m_AmmoData )
  47. m_AmmoData = new map<string, ref AmmoData>;
  48. string classname = ClassName();
  49. if ( !m_AmmoData.Contains(classname) )
  50. {
  51. ref AmmoData new_data = new AmmoData( classname );
  52. if ( new_data.m_IsValid )
  53. m_AmmoData.Insert( classname, new AmmoData( classname ) );
  54. }
  55. }
  56. }
  57. //! Gets magazine ammo count
  58. proto native int GetAmmoCount();
  59. //! Sets magazine ammo count
  60. proto native void ServerSetAmmoCount(int ammoCount);
  61. proto native void LocalSetAmmoCount(int ammoCount);
  62. /**@fn AcquireCartridge
  63. * @brief acquires cartridge(damage, type) to magazine
  64. * @param[out] ammoDamage \p damage of the ammo
  65. * @param[out] cartTypeName \p type name of the ejected ammo
  66. * @return true if acquired
  67. **/
  68. proto bool LocalAcquireCartridge(out float dmg, out string cartTypeName);
  69. proto bool ServerAcquireCartridge(out float dmg, out string cartTypeName);
  70. /**@fn StoreCartridge
  71. * @brief stores cartridge(damage, type) to magazine
  72. * @param[in] ammoDamage \p damage of the cartridge
  73. * @param[in] cartTypeName \p type name of the stored cartridge
  74. * @return true if stored
  75. **/
  76. proto native bool LocalStoreCartridge(float ammoDamage, string cartTypeName);
  77. proto native bool ServerStoreCartridge(float ammoDamage, string cartTypeName);
  78. /**@fn GetCartridgeAtIndex
  79. * @brief queries cartridge(damage, type) info at specified index
  80. * @param[in] cartIndex \p index of the cartridge.
  81. * @param[out] ammoDamage \p damage of the cartridge
  82. * @param[out] cartTypeName \p type name of the cartridge
  83. * @return true if index valid
  84. **/
  85. proto bool GetCartridgeAtIndex(int cartIndex, out float dmg, out string cartTypeName);
  86. /**@fn SetCartridgeAtIndex
  87. * @brief modifies cartridge(damage, type) info at specified index
  88. * @param[in] cartIndex \p index of the cartridge.
  89. * @param[in] ammoDamage \p damage of the cartridge
  90. * @param[in] cartTypeName \p type name of the cartridge
  91. * @return true if index valid
  92. **/
  93. proto bool SetCartridgeAtIndex(int cartIndex, out float dmg, out string cartTypeName);
  94. /**@fn SetCartridgeDamageAtIndex
  95. * @brief modifies cartridge damage info at specified index
  96. * @param[in] cartIndex \p index of the cartridge.
  97. * @param[in] ammoDamage \p damage of the cartridge
  98. * @return true if index valid
  99. **/
  100. proto bool SetCartridgeDamageAtIndex(int cartIndex, float dmg);
  101. static AmmoData GetAmmoData( string classname )
  102. {
  103. if ( !m_AmmoData )
  104. m_AmmoData = new map<string, ref AmmoData>;
  105. if ( !m_AmmoData.Contains(classname) )
  106. {
  107. ref AmmoData new_data = new AmmoData( classname );
  108. if ( new_data.m_IsValid )
  109. m_AmmoData.Insert( classname, new AmmoData( classname ) );
  110. return new_data;
  111. }
  112. else
  113. {
  114. return m_AmmoData.Get( classname );
  115. }
  116. }
  117. bool IsCompatiableAmmo( ItemBase ammo )
  118. {
  119. if ( m_CompatiableAmmo && ammo )
  120. return ( m_CompatiableAmmo.Find( ammo.GetType() ) > -1 );
  121. else
  122. return false;
  123. }
  124. bool CanAddCartridges(int count)
  125. {
  126. int spc_avail = GetAmmoMax() - GetAmmoCount();
  127. return count <= spc_avail;
  128. }
  129. //! Adds magazine ammo, MP safe
  130. void ServerAddAmmoCount(int ammoCount)
  131. {
  132. ServerSetAmmoCount(GetAmmoCount() + ammoCount);
  133. }
  134. void LocalAddAmmoCount(int ammoCount)
  135. {
  136. LocalSetAmmoCount(GetAmmoCount() + ammoCount);
  137. }
  138. //! returns max rounds for this mag (returns "count" config value)
  139. int GetAmmoMax()
  140. {
  141. return m_Count;
  142. }
  143. //! set max rounds for this mag
  144. void ServerSetAmmoMax()
  145. {
  146. ServerSetAmmoCount( GetAmmoMax() );
  147. }
  148. void LocalSetAmmoMax()
  149. {
  150. LocalSetAmmoCount( GetAmmoMax() );
  151. }
  152. //! Returns if this entity is Magazine
  153. override bool IsMagazine()
  154. {
  155. return true;
  156. }
  157. override bool CanBeSplit()
  158. {
  159. if ( m_CanThisBeSplit )
  160. return ( GetAmmoCount() > 1 );
  161. return false;
  162. }
  163. bool InitReliability(out array<float> reliability_array)
  164. {
  165. if (GetGame().ConfigIsExisting("cfgMagazines " + GetType() + " Reliability ChanceToJam"))
  166. {
  167. GetGame().ConfigGetFloatArray("cfgMagazines " + GetType() + " Reliability ChanceToJam",reliability_array);
  168. return true;
  169. }
  170. return false;
  171. }
  172. float GetChanceToJam()
  173. {
  174. int level = GetHealthLevel();
  175. if (level >= 0 && level < m_ChanceToJam.Count())
  176. return m_ChanceToJam[level];
  177. else
  178. return 0.0;
  179. }
  180. override void SplitItemToInventoryLocation( notnull InventoryLocation dst )
  181. {
  182. if ( !CanBeSplit() )
  183. return;
  184. Magazine new_pile = Magazine.Cast( GameInventory.LocationCreateEntity( dst, GetType(), ECE_IN_INVENTORY, RF_DEFAULT ) );
  185. if( new_pile )
  186. {
  187. MiscGameplayFunctions.TransferItemProperties(dst.GetItem(), new_pile);
  188. new_pile.ServerSetAmmoCount(0);
  189. int quantity = GetAmmoCount();
  190. for (int i = 0; i < Math.Floor( quantity * 0.5 ); ++i)
  191. {
  192. float damage;
  193. string cartrige_name;
  194. ServerAcquireCartridge(damage, cartrige_name);
  195. new_pile.ServerStoreCartridge(damage, cartrige_name);
  196. }
  197. new_pile.SetSynchDirty();
  198. SetSynchDirty();
  199. }
  200. }
  201. override void SplitItem(PlayerBase player)
  202. {
  203. if ( !CanBeSplit() )
  204. return;
  205. Magazine new_pile = Magazine.Cast( player.CreateCopyOfItemInInventoryOrGround( this ) );
  206. if( new_pile )
  207. {
  208. new_pile.ServerSetAmmoCount(0);
  209. int quantity = this.GetAmmoCount();
  210. for (int i = 0; i < Math.Floor( quantity / 2 ); i++)
  211. {
  212. float damage;
  213. string cartrige_name;
  214. ServerAcquireCartridge(damage, cartrige_name);
  215. new_pile.ServerStoreCartridge(damage, cartrige_name);
  216. }
  217. new_pile.SetSynchDirty();
  218. SetSynchDirty();
  219. }
  220. }
  221. void ApplyManipulationDamage()
  222. {
  223. AddHealth("","Health",-m_ManipulationDamage);
  224. }
  225. override bool IsFullQuantity()
  226. {
  227. if ( GetAmmoCount() == GetAmmoMax() )
  228. {
  229. return true;
  230. }
  231. else
  232. {
  233. return false;
  234. }
  235. }
  236. override protected float GetWeightSpecialized(bool forceRecalc = false)
  237. {
  238. #ifdef DEVELOPER
  239. if (WeightDebug.m_VerbosityFlags & WeightDebugType.RECALC_FORCED)
  240. {
  241. WeightDebugData data = WeightDebug.GetWeightDebug(this);
  242. data.SetCalcDetails("TMAG: ("+GetAmmoCount()+"(Ammo count) * " + ConfigGetFloat("weightPerQuantityUnit")+"(weightPerQuantityUnit)) + " + GetConfigWeightModifiedDebugText());
  243. }
  244. #endif
  245. return GetConfigWeightModified() + (GetAmmoCount() * ConfigGetFloat("weightPerQuantityUnit"));
  246. }
  247. override bool IsCombineAll( ItemBase other_item, bool use_stack_max = false)
  248. {
  249. Magazine other_magazine = Magazine.Cast(other_item);
  250. int free_space = GetAmmoMax() - GetAmmoCount();
  251. return free_space >= other_magazine.GetAmmoCount();
  252. }
  253. override void CombineItems( ItemBase other_item, bool use_stack_max = false )
  254. {
  255. if ( !CanBeCombined(other_item) )
  256. return;
  257. if ( other_item.GetType() != GetType() )
  258. return;
  259. Magazine other_magazine;
  260. if ( Class.CastTo(other_magazine, other_item) )
  261. {
  262. //int other_item_quantity = other_magazine.GetAmmoCount();
  263. int this_free_space = GetAmmoMax() - GetAmmoCount();
  264. int numberOfTransferredBullets = 0;
  265. int currentAmount = GetAmmoCount();
  266. for (int i = 0; i < this_free_space && other_magazine.GetAmmoCount() > 0 ; i++)
  267. {
  268. float damage;
  269. string cartrige_name;
  270. other_magazine.ServerAcquireCartridge(damage, cartrige_name);
  271. if (ServerStoreCartridge(damage, cartrige_name))
  272. ++numberOfTransferredBullets;
  273. }
  274. if (GetGame().IsServer())
  275. {
  276. float resultingHealth = (currentAmount * GetHealth() + numberOfTransferredBullets * other_magazine.GetHealth()) / GetAmmoCount();
  277. SetHealth("", "", resultingHealth);
  278. }
  279. OnCombine(other_item);
  280. other_magazine.SetSynchDirty();
  281. SetSynchDirty();
  282. }
  283. }
  284. override bool CanDetachAttachment(EntityAI parent)
  285. {
  286. PlayerBase player = PlayerBase.Cast(GetHierarchyRootPlayer());
  287. if (player)
  288. {
  289. Weapon_Base wpn = Weapon_Base.Cast(parent);
  290. if (wpn)
  291. {
  292. return player.GetWeaponManager().CanDetachMagazine(wpn,this);
  293. }
  294. }
  295. return super.CanDetachAttachment(parent);
  296. }
  297. override void OnInventoryEnter(Man player)
  298. {
  299. super.OnInventoryEnter(player);
  300. PlayerBase p = PlayerBase.Cast(player);
  301. p.GetWeaponManager().OnMagazineInventoryEnter(this);
  302. }
  303. override void OnInventoryExit(Man player)
  304. {
  305. super.OnInventoryExit(player);
  306. PlayerBase p = PlayerBase.Cast(player);
  307. p.GetWeaponManager().OnMagazineInventoryExit(this);
  308. }
  309. override void OnWasAttached( EntityAI parent, int slot_id )
  310. {
  311. super.OnWasAttached(parent, slot_id);
  312. PlayerBase player = PlayerBase.Cast(GetHierarchyRootPlayer());
  313. Weapon_Base wpn = Weapon_Base.Cast(parent);
  314. if (wpn && player)
  315. {
  316. player.GetWeaponManager().OnMagazineAttach(this);
  317. }
  318. }
  319. override void OnWasDetached( EntityAI parent, int slot_id )
  320. {
  321. super.OnWasDetached(parent, slot_id);
  322. PlayerBase player = PlayerBase.Cast(GetHierarchyRootPlayer());
  323. Weapon_Base wpn = Weapon_Base.Cast(parent);
  324. if (wpn && player)
  325. {
  326. player.GetWeaponManager().OnMagazineDetach(this);
  327. }
  328. }
  329. override void EEHealthLevelChanged( int oldLevel, int newLevel, string zone )
  330. {
  331. super.EEHealthLevelChanged(oldLevel, newLevel, zone);
  332. float damage = 1 - GetHealthLevelValue(newLevel) + 0.001;
  333. int cartridgeCount = GetAmmoCount();
  334. for (int i = 0; i < cartridgeCount; ++i)
  335. SetCartridgeDamageAtIndex(i, damage);
  336. }
  337. override void GetDebugActions(out TSelectableActionInfoArrayEx outputList)
  338. {
  339. super.GetDebugActions(outputList);
  340. if (GetAmmoCount() > 0)
  341. {
  342. outputList.Insert(new TSelectableActionInfoWithColor(SAT_DEBUG_ACTION, EActions.SEPARATOR, "", FadeColors.LIGHT_GREY));
  343. outputList.Insert(new TSelectableActionInfoWithColor(SAT_DEBUG_ACTION, EActions.PRINT_BULLETS, "Print Bullets", FadeColors.LIGHT_GREY));
  344. }
  345. }
  346. override bool OnAction(int action_id, Man player, ParamsReadContext ctx)
  347. {
  348. if (GetGame().IsServer())
  349. {
  350. if (action_id == EActions.PRINT_BULLETS)
  351. {
  352. Magazine magazine;
  353. Class.CastTo(magazine, this);
  354. for (int i = 0; i < magazine.GetAmmoCount(); i++)
  355. {
  356. float damage;
  357. string className;
  358. magazine.GetCartridgeAtIndex(i, damage, className);
  359. Debug.Log(string.Format("Bullet: %1, Damage %2", className, damage));
  360. }
  361. }
  362. }
  363. return super.OnAction(action_id, player, ctx);
  364. }
  365. override bool CanBeFSwaped()
  366. {
  367. Weapon_Base wpn = Weapon_Base.Cast(GetHierarchyParent());
  368. if (wpn)
  369. {
  370. return false;
  371. }
  372. return true;
  373. }
  374. }
  375. class MagazineStorage : Magazine
  376. {
  377. override void SetActions()
  378. {
  379. super.SetActions();
  380. AddAction(ActionLoadMagazine);
  381. AddAction(ActionEmptyMagazine);
  382. AddAction(ActionLoadMagazineQuick);
  383. }
  384. }