cfgplayerspawnhandler.c 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. class PlayerSpawnHandler
  2. {
  3. private static bool m_Initialized;
  4. static ref PlayerSpawnJsonData m_Data = new PlayerSpawnJsonData();
  5. static bool LoadData()
  6. {
  7. array<string> spawnGearPresetFiles = CfgGameplayHandler.GetPlayerSpawnGearPresetFiles();
  8. if (!spawnGearPresetFiles || (spawnGearPresetFiles && spawnGearPresetFiles.Count() == 0))
  9. return false;
  10. m_Data.presets = {};
  11. foreach (string spawnPresetFile : spawnGearPresetFiles)
  12. {
  13. PlayerSpawnPreset preset;
  14. string path = "$mission:" + spawnPresetFile;
  15. string errorMessage;
  16. if (!JsonFileLoader<PlayerSpawnPreset>.LoadFile(path, preset, errorMessage))
  17. {
  18. ErrorEx(errorMessage);
  19. return false;
  20. }
  21. if (preset != null)
  22. m_Data.presets.Insert(preset);
  23. }
  24. m_Initialized = m_Data.presets.Count() > 0;
  25. return true;
  26. }
  27. static bool IsInitialized()
  28. {
  29. return m_Initialized;
  30. }
  31. static PlayerSpawnPreset GetRandomCharacterPreset()
  32. {
  33. array<int> weightedPresetIndexes = new array<int>();
  34. int count = m_Data.presets.Count();
  35. PlayerSpawnPreset p;
  36. for (int i = 0; i < count; i++)
  37. {
  38. p = m_Data.presets[i];
  39. if (p.IsValid())
  40. {
  41. for (int j = 0; j < p.spawnWeight; j++)
  42. {
  43. weightedPresetIndexes.Insert(i);
  44. }
  45. }
  46. }
  47. return m_Data.presets.Get(weightedPresetIndexes.GetRandomElement());
  48. }
  49. //! equips character with the chosen preset
  50. static bool ProcessEquipmentData(PlayerBase player, PlayerSpawnPreset data)
  51. {
  52. if (data.IsValid())
  53. {
  54. ProcessSlotsEquipment(player, data);
  55. ProcessCargoEquipment(player, data);
  56. }
  57. return true;
  58. }
  59. //! iterates over each object and spawns alternatives
  60. private static void ProcessSlotsEquipment(PlayerBase player, PlayerSpawnPreset data)
  61. {
  62. if (!data.HasAttachmentSlotSetsDefined())
  63. {
  64. Debug.Log("No non-empty 'attachmentSlotItemSets' array found. Skipping slot spawns","n/a","n/a","ProcessSlotsEquipment");
  65. return;
  66. }
  67. foreach (PlayerSpawnPresetSlotData slotData : data.attachmentSlotItemSets)
  68. {
  69. SelectAndSpawnSlotEquipment(player,slotData);
  70. }
  71. }
  72. //! selects weighted slot equipment variant
  73. private static bool SelectAndSpawnSlotEquipment(PlayerBase player, PlayerSpawnPresetSlotData slotData)
  74. {
  75. int slotID;
  76. if (!slotData.TranslateAndValidateSlot(player,slotID))
  77. return false;
  78. if (!slotData.IsValid())
  79. return false;
  80. array<int> weightedDiscreteSetIndexes = new array<int>();
  81. int count = slotData.discreteItemSets.Count();
  82. PlayerSpawnPresetDiscreteItemSetSlotData dis;
  83. for (int i = 0; i < count; i++)
  84. {
  85. dis = slotData.discreteItemSets[i];
  86. if (dis.IsValid()) //only when the type exists and spawnWeight is set
  87. {
  88. for (int j = 0; j < dis.spawnWeight; j++)
  89. {
  90. weightedDiscreteSetIndexes.Insert(i);
  91. }
  92. }
  93. }
  94. dis = null;
  95. if (weightedDiscreteSetIndexes.Count() > 0)
  96. dis = slotData.discreteItemSets.Get(weightedDiscreteSetIndexes.GetRandomElement());
  97. return SpawnDiscreteSlotItemSet(player,dis,slotID);
  98. }
  99. //! chooses one object from the array
  100. private static void ProcessCargoEquipment(PlayerBase player, PlayerSpawnPreset data)
  101. {
  102. if (!data.HasDiscreteUnsortedItemSetsDefined())
  103. {
  104. Debug.Log("No non-empty 'discreteUnsortedItemSets' array found. Skipping cargo spawns","n/a","n/a","ProcessCargoEquipment");
  105. return;
  106. }
  107. SelectAndSpawnCargoSet(player,data);
  108. }
  109. private static bool SelectAndSpawnCargoSet(PlayerBase player, PlayerSpawnPreset data)
  110. {
  111. array<int> weightedDiscreteSetIndexes = new array<int>();
  112. int count = data.discreteUnsortedItemSets.Count();
  113. PlayerSpawnPresetDiscreteCargoSetData csd;
  114. for (int i = 0; i < count; i++)
  115. {
  116. csd = data.discreteUnsortedItemSets[i];
  117. if (csd.IsValid()) //only when the spawnWeight is set
  118. {
  119. for (int j = 0; j < csd.spawnWeight; j++)
  120. {
  121. weightedDiscreteSetIndexes.Insert(i);
  122. }
  123. }
  124. }
  125. csd = null;
  126. if (weightedDiscreteSetIndexes.Count() > 0)
  127. csd = data.discreteUnsortedItemSets.Get(weightedDiscreteSetIndexes.GetRandomElement());
  128. return SpawnDiscreteCargoItemSet(player,csd);
  129. }
  130. private static bool SpawnDiscreteCargoItemSet(PlayerBase player, PlayerSpawnPresetDiscreteCargoSetData csd)
  131. {
  132. SpawnComplexChildrenItems(player,csd);
  133. SpawnSimpleChildrenItems(player,csd);
  134. return true;
  135. }
  136. private static bool SpawnDiscreteSlotItemSet(PlayerBase player, PlayerSpawnPresetDiscreteItemSetSlotData dis, int slotID)
  137. {
  138. if (!dis)
  139. {
  140. Debug.Log("No PlayerSpawnPresetDiscreteItemSet found. Skipping spawn for slot: " + InventorySlots.GetSlotName(slotID),"n/a","n/a","SpawnDiscreteSlotItemSet");
  141. return false;
  142. }
  143. ItemBase item;
  144. if (slotID == InventorySlots.HANDS) //hands exception
  145. item = ItemBase.Cast(player.GetHumanInventory().CreateInHands(dis.itemType));
  146. else
  147. item = ItemBase.Cast(player.GetInventory().CreateAttachmentEx(dis.itemType,slotID));
  148. if (item)
  149. {
  150. HandleNewItem(item,dis);
  151. }
  152. else if (dis.itemType != string.Empty)
  153. {
  154. Debug.Log("FAILED spawning item type: " + dis.itemType + " into slot: " + InventorySlots.GetSlotName(slotID) + " of parent: " + player,"n/a","n/a","SpawnDiscreteSlotItemSet");
  155. return false;
  156. }
  157. return item != null;
  158. }
  159. //! could spawn other items recursively. Parent item is guaranteed here.
  160. private static bool SpawnComplexChildrenItems(EntityAI parent, notnull PlayerSpawnPresetItemSetBase data)
  161. {
  162. if (!data.complexChildrenTypes || data.complexChildrenTypes.Count() < 1) //no children defined, still valid!
  163. {
  164. return false;
  165. }
  166. foreach (PlayerSpawnPresetComplexChildrenType cct : data.complexChildrenTypes)
  167. {
  168. if (cct.itemType == string.Empty)
  169. {
  170. Debug.Log("Empty item type found in 'complexChildrenTypes' of parent : " + parent,"n/a","n/a","SpawnSimpleChildrenItems");
  171. continue;
  172. }
  173. ItemBase item;
  174. Class.CastTo(item,CreateChildItem(parent,cct.itemType));
  175. if (item)
  176. {
  177. HandleNewItem(item,cct);
  178. }
  179. else
  180. {
  181. Weapon_Base wep;
  182. if (!Class.CastTo(wep,parent) || !IsWeaponAndMagazineType(parent,cct.itemType) || !wep.HasInternalMagazine(-1))
  183. Debug.Log("FAILED spawning item: " + cct.itemType + " of parent: " + parent,"n/a","n/a","SpawnComplexChildrenItems");
  184. }
  185. }
  186. return true;
  187. }
  188. private static bool SpawnSimpleChildrenItems(EntityAI parent, PlayerSpawnPresetItemSetBase data)
  189. {
  190. if (!data || !data.simpleChildrenTypes || data.simpleChildrenTypes.Count() < 1) //no children defined, still valid!
  191. {
  192. return false;
  193. }
  194. int count = data.simpleChildrenTypes.Count();
  195. string itemType;
  196. for (int i = 0; i < count; i++)
  197. {
  198. itemType = data.simpleChildrenTypes[i];
  199. if (itemType == string.Empty)
  200. {
  201. Debug.Log("Empty item type found at idx: " + i.ToString() + " of 'simpleChildrenTypes' array. Skipping","n/a","n/a","SpawnSimpleChildrenItems");
  202. continue;
  203. }
  204. ItemBase item;
  205. Class.CastTo(item,CreateChildItem(parent,itemType));
  206. if (item)
  207. {
  208. if (!data.simpleChildrenUseDefaultAttributes)
  209. ApplyAttributes(item,data.attributes);
  210. }
  211. else
  212. {
  213. Weapon_Base wep;
  214. if (!Class.CastTo(wep,parent) || !IsWeaponAndMagazineType(parent,itemType) || !wep.HasInternalMagazine(-1))
  215. Debug.Log("FAILED spawning item type: " + itemType + " to parent: " + parent,"n/a","n/a","SpawnSimpleChildrenItems");
  216. }
  217. }
  218. return true;
  219. }
  220. private static void HandleNewItem(notnull ItemBase item, PlayerSpawnPresetItemSetBase data)
  221. {
  222. ApplyAttributes(item,data.attributes);
  223. PlayerBase player;
  224. if (Class.CastTo(player,item.GetHierarchyRootPlayer()) && data.GetQuickbarIdx() > -1)
  225. player.SetQuickBarEntityShortcut(item,data.GetQuickbarIdx());
  226. SpawnComplexChildrenItems(item,data);
  227. SpawnSimpleChildrenItems(item,data);
  228. }
  229. private static EntityAI CreateChildItem(EntityAI parent, string type)
  230. {
  231. PlayerBase player;
  232. ItemBase newItem;
  233. if (Class.CastTo(player,parent)) //special behavior
  234. {
  235. if (Class.CastTo(newItem,player.GetInventory().CreateInInventory(type)))
  236. return newItem;
  237. Debug.Log("FAILED spawning item: " + type + ", it fits in no cargo or attachment on any worn item","n/a","n/a","CreateChildItem");
  238. return null;
  239. }
  240. //weapon magazine exception
  241. if (GetGame().ConfigIsExisting(CFG_MAGAZINESPATH + " " + type) && parent.IsWeapon())
  242. {
  243. Weapon_Base wep
  244. if (Class.CastTo(wep,parent) && wep.SpawnAmmo(type) && !wep.HasInternalMagazine(-1)) //assuming weps with internal magazine don't attach external magazines
  245. {
  246. Magazine mag;
  247. int muzzleCount = wep.GetMuzzleCount();
  248. for (int i = 0; i < muzzleCount; i++)
  249. {
  250. if (Class.CastTo(mag,wep.GetMagazine(i)) && mag.GetType() == type)
  251. return mag;
  252. }
  253. }
  254. return null;
  255. }
  256. return parent.GetInventory().CreateInInventory(type);
  257. }
  258. private static void ApplyAttributes(ItemBase item, PlayerSpawnAttributesData attributes)
  259. {
  260. if (!attributes)
  261. return;
  262. float health01 = Math.RandomFloatInclusive(attributes.healthMin,attributes.healthMax);
  263. item.SetHealth01("","Health",health01);
  264. float quantity01 = Math.RandomFloatInclusive(attributes.quantityMin,attributes.quantityMax);
  265. if (item.IsMagazine())
  266. {
  267. Magazine mag = Magazine.Cast(item);
  268. /*if (attributes.magazineAmmoOrdered && attributes.magazineAmmoOrdered.Count() > 0)
  269. {
  270. mag.ServerSetAmmoCount(0);
  271. foreach (string bulletType : attributes.magazineAmmoOrdered)
  272. {
  273. mag.ServerStoreCartridge(health01,bulletType);
  274. }
  275. mag.SetSynchDirty();
  276. }
  277. else*/
  278. {
  279. int ammoQuantity = (int)Math.Lerp(0,mag.GetAmmoMax(),quantity01);
  280. mag.ServerSetAmmoCount(ammoQuantity);
  281. }
  282. }
  283. else //'varQuantityDestroyOnMin' quantity safeguard
  284. {
  285. float quantityAbsolute = Math.Lerp(item.GetQuantityMin(),item.GetQuantityMax(),quantity01);
  286. quantityAbsolute = Math.Round(quantityAbsolute); //avoids weird floats
  287. if (quantityAbsolute <= item.GetQuantityMin() && item.ConfigGetBool("varQuantityDestroyOnMin"))
  288. quantityAbsolute++;
  289. item.SetQuantity(quantityAbsolute);
  290. }
  291. }
  292. //! Used for exceptions in the system
  293. private static bool IsWeaponAndMagazineType(EntityAI parent, string type)
  294. {
  295. return (GetGame().ConfigIsExisting(CFG_MAGAZINESPATH + " " + type) && parent.IsWeapon());
  296. }
  297. }