playerstomach.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. class StomachItem
  2. {
  3. ref NutritionalProfile m_Profile;
  4. float m_Amount;
  5. int m_FoodStage;
  6. //bool m_IsLiquid;
  7. string m_ClassName;
  8. int m_Agents;
  9. protected float m_Temperature;
  10. void StomachItem(string class_name, float amount, NutritionalProfile profile,int foodstage, int agents, float temperature)
  11. {
  12. m_Amount = amount;
  13. m_Profile = profile;
  14. //m_IsLiquid = is_liquid;
  15. m_FoodStage = foodstage;
  16. m_ClassName = class_name;
  17. m_Agents = agents;
  18. m_Temperature = temperature;
  19. }
  20. string GetClassName()
  21. {
  22. return m_ClassName;
  23. }
  24. /*
  25. void SetLiquid(bool is_liquid)
  26. {
  27. m_IsLiquid = is_liquid;
  28. }
  29. bool IsLiquid()
  30. {
  31. return m_IsLiquid;
  32. }*/
  33. int GetFoodStage()
  34. {
  35. return m_FoodStage;
  36. }
  37. void SetFoodStage(int food_stage)
  38. {
  39. m_FoodStage = food_stage;
  40. }
  41. // returns grams or ml's of this item/liquid
  42. float GetAmount()
  43. {
  44. return m_Amount;
  45. }
  46. // adds grams or ml's of this item/liquid
  47. void AddAmount(float amount)
  48. {
  49. m_Amount += amount;
  50. }
  51. void AddAgents(int agents)
  52. {
  53. m_Agents = m_Agents | agents;
  54. }
  55. float GetTemperature()
  56. {
  57. return m_Temperature;
  58. }
  59. // adjust temperature based on the fraction of the new amount compared to the original amount
  60. void AddTemperature(float temperature, float fraction)
  61. {
  62. float currentTempFraction = (1 - 1/fraction) * m_Temperature;
  63. float newTempFraction = (1/fraction) * temperature;
  64. m_Temperature = currentTempFraction + newTempFraction;
  65. }
  66. // "digests" given amount, "outs" nutritional components, and returns 'true' if leads to complete item digestion(no amount left)
  67. bool ProcessDigestion(float digestion_points, out float water, out float energy, out float toxicity, out float volume, out int agents, out float consumed_amount)
  68. {
  69. agents = m_Agents;
  70. consumed_amount = GetNutritions(digestion_points, m_Profile, water, energy, toxicity);
  71. m_Amount -= consumed_amount;
  72. volume = m_Profile.GetFullnessIndex() * m_Amount;
  73. return(m_Amount < 0.001);
  74. }
  75. float GetNutritions(float digestion_points, NutritionalProfile profile, out float water, out float energy, out float toxicity)
  76. {
  77. float energy_per_unit = profile.GetEnergy() / 100;
  78. float water_per_unit = profile.GetWaterContent() / 100;
  79. float toxicity_per_unit = profile.GetToxicity();
  80. float digestability = profile.GetDigestibility();
  81. if (digestability == 0)//if undefined
  82. {
  83. digestability = 1;
  84. }
  85. float consumed_quantity = digestion_points * digestability;
  86. if (m_Amount < consumed_quantity)
  87. {
  88. consumed_quantity = m_Amount;
  89. }
  90. if (consumed_quantity > 0)
  91. {
  92. water = consumed_quantity * water_per_unit;
  93. energy = consumed_quantity * energy_per_unit;
  94. toxicity = consumed_quantity * toxicity_per_unit;
  95. }
  96. return consumed_quantity;
  97. }
  98. }
  99. class PlayerStomach
  100. {
  101. const int DIGESTING_WATER = 1;
  102. const int DIGESTING_ENERGY = 2;
  103. const int quantity_bit_offset = 16;
  104. const int id_bit_offset = 4;//based on food stage count(+1 for safe measure)
  105. static int CHECKSUM;
  106. const float DIGESTION_POINTS = PlayerConstants.DIGESTION_SPEED;
  107. const int ACCEPTABLE_QUANTITY_MAX = 32768;
  108. const int ACCEPTABLE_FOODSTAGE_MAX = FoodStageType.COUNT - 1;
  109. static ref map<string, int> m_NamesToIDs = new map<string, int>;
  110. static ref map<int, string> m_IDsToNames = new map<int, string>;
  111. static const bool m_InitData = PlayerStomach.InitData();
  112. ref array<ref StomachItem> m_StomachContents = new array<ref StomachItem>;
  113. int m_AgentTransferFilter;//bit mask that prevents agents set in the mask from passing to the player
  114. bool m_Digesting;
  115. int m_DigestingType;
  116. PlayerBase m_Player;
  117. float m_StomachVolume;
  118. protected float m_StomachTemperature;
  119. const int STORAGE_VERSION = 106;
  120. void PlayerStomach(PlayerBase player)
  121. {
  122. m_Player = player;
  123. }
  124. float GetStomachVolume()
  125. {
  126. return m_StomachVolume;
  127. }
  128. float GetStomachTemperature()
  129. {
  130. return m_StomachTemperature;
  131. }
  132. void ClearContents()
  133. {
  134. m_StomachContents.Clear();
  135. m_StomachVolume = 0.0;
  136. m_StomachTemperature = 0;
  137. }
  138. // Reduce content of the stomach by provided percentage
  139. void ReduceContents(float percent)
  140. {
  141. percent = Math.Clamp(percent, 0, 100);
  142. float reduction = percent * 0.01;
  143. foreach (StomachItem item : m_StomachContents)
  144. {
  145. item.AddAmount( -(item.GetAmount() * reduction) );
  146. }
  147. m_StomachVolume -= m_StomachVolume * reduction;
  148. }
  149. void SetAgentTransferFilter(int filter_agents)
  150. {
  151. m_AgentTransferFilter;
  152. }
  153. int GetAgentTransferFilter()
  154. {
  155. return m_AgentTransferFilter;
  156. }
  157. static void RegisterItem(string classname, int id)
  158. {
  159. int hash = classname.Hash();
  160. CHECKSUM = CHECKSUM^hash; //xor hash vs current checksum
  161. m_NamesToIDs.Insert(classname, id);
  162. m_IDsToNames.Insert(id, classname);
  163. }
  164. static string GetClassnameFromID(int id)
  165. {
  166. return m_IDsToNames.Get(id);
  167. }
  168. static int GetIDFromClassname(string name)
  169. {
  170. if (!m_NamesToIDs.Contains(name))
  171. return -1;
  172. return m_NamesToIDs.Get(name);
  173. }
  174. static bool InitData()
  175. {
  176. TStringArray all_paths = new TStringArray;
  177. all_paths.Insert("CfgVehicles");
  178. all_paths.Insert("cfgLiquidDefinitions");
  179. string config_path;
  180. string child_name;
  181. int scope;
  182. string path;
  183. int consumable_count;
  184. for(int i = 0; i < all_paths.Count(); i++)
  185. {
  186. config_path = all_paths.Get(i);
  187. int children_count = GetGame().ConfigGetChildrenCount(config_path);
  188. for(int x = 0; x < children_count; x++)
  189. {
  190. GetGame().ConfigGetChildName(config_path, x, child_name);
  191. path = config_path + " " + child_name;
  192. scope = GetGame().ConfigGetInt(config_path + " " + child_name + " scope");
  193. bool should_check = 1;
  194. if (config_path == "CfgVehicles" && scope == 0)
  195. {
  196. should_check = 0;
  197. }
  198. if (should_check)
  199. {
  200. bool has_nutrition = GetGame().ConfigIsExisting(path + " Nutrition");
  201. bool has_stages = GetGame().ConfigIsExisting(path + " Food");
  202. if (has_nutrition || has_stages)
  203. {
  204. //Print("child name:" + child_name);
  205. RegisterItem(child_name, consumable_count);//consumable_count value serves as an unique ID for each item
  206. consumable_count++;
  207. }
  208. }
  209. }
  210. }
  211. //Print("consumable_count " + consumable_count);
  212. return true;
  213. }
  214. int GetStorageVersion()
  215. {
  216. return STORAGE_VERSION;
  217. }
  218. bool IsDigesting()
  219. {
  220. return (m_DigestingType != 0);
  221. }
  222. int GetDigestingType()
  223. {
  224. return m_DigestingType;
  225. }
  226. protected void UpdateStomachTemperature()
  227. {
  228. m_StomachTemperature = 0;
  229. int stomachItemsCount = m_StomachContents.Count();
  230. if (stomachItemsCount == 0)
  231. return;
  232. StomachItem item;
  233. for (int i = 0; i < stomachItemsCount; i++)
  234. {
  235. item = m_StomachContents[i];
  236. m_StomachTemperature += item.GetTemperature();
  237. }
  238. m_StomachTemperature = m_StomachTemperature / stomachItemsCount;
  239. }
  240. void Update(float delta_time)
  241. {
  242. ProcessNutrients(delta_time);
  243. }
  244. void ProcessNutrients(float delta_time)
  245. {
  246. m_DigestingType = 0;
  247. StomachItem item;
  248. int stomachItemsCount = m_StomachContents.Count();
  249. if (stomachItemsCount == 0)
  250. return;
  251. float digestionPointsPerItem = (DIGESTION_POINTS / stomachItemsCount) * delta_time;
  252. m_StomachVolume = 0;//reset, it's accumulated with each pass
  253. for (int i = stomachItemsCount - 1; i >= 0; i--)
  254. {
  255. item = m_StomachContents[i];
  256. float water, energy, toxicity, volume, consumedAmount;
  257. int agents;
  258. if (item.ProcessDigestion(digestionPointsPerItem, water, energy, toxicity, volume, agents, consumedAmount))
  259. {
  260. m_StomachContents.Remove(i);
  261. UpdateStomachTemperature();
  262. }
  263. m_StomachVolume += volume;
  264. m_Player.GetStatEnergy().Add(energy);
  265. m_Player.GetStatWater().Add(water);
  266. if (energy > 0)
  267. m_DigestingType |= DIGESTING_ENERGY;
  268. if (water > 0)
  269. m_DigestingType |= DIGESTING_WATER;
  270. //! try amount from nutritions/food stage first
  271. float amountOfAgents = item.m_Profile.m_AgentsPerDigest;
  272. if (amountOfAgents == 0)
  273. amountOfAgents = consumedAmount;
  274. //! food poisoning specific override by bloody hands
  275. if ((item.m_Agents & eAgents.FOOD_POISON) == eAgents.FOOD_POISON && m_Player.HasBloodyHands())
  276. amountOfAgents = Math.Max(amountOfAgents, PlayerConstants.BLOODY_HANDS_FOOD_POISON_AGENT_INCREMENT);
  277. DigestAgents(agents, amountOfAgents);
  278. }
  279. }
  280. void DigestAgents(int agents, float quantity)
  281. {
  282. if (!agents)
  283. return;
  284. agents = agents & (~m_AgentTransferFilter);//filter against the agent filter mask
  285. int highestBit = Math.Log2(agents) + 1;
  286. for (int i = 0; i < highestBit; ++i)
  287. {
  288. int agent = (1 << i)& agents;
  289. if (agent)
  290. {
  291. float rndPct = Math.RandomFloatInclusive(PlayerConstants.STOMACH_DIGEST_AGENT_RANDOM_MIN, PlayerConstants.STOMACH_DIGEST_AGENT_RANDOM_MAX);
  292. if (rndPct != 0)
  293. quantity += quantity * rndPct;
  294. m_Player.m_AgentPool.DigestAgent(agent, quantity);
  295. }
  296. }
  297. }
  298. float GetVolumeContainingAgent(eAgents agent)
  299. {
  300. float amountByAgent = 0.0;
  301. foreach (StomachItem item : m_StomachContents)
  302. {
  303. if ((item.m_Agents & agent) == agent)
  304. amountByAgent += item.m_Amount;
  305. }
  306. return amountByAgent;
  307. }
  308. float GetVolumeContainingAgent01(eAgents agent)
  309. {
  310. float amount = GetVolumeContainingAgent(agent);
  311. if (amount > 0.0)
  312. return Math.InverseLerp(0.0, GetStomachVolume(), amount);
  313. return 0.0;
  314. }
  315. void PrintUpdate()
  316. {
  317. Print("================================");
  318. for(int i = 0; i < m_StomachContents.Count(); i++)
  319. {
  320. string itemname = m_StomachContents.Get(i).m_ClassName;
  321. float amount = m_StomachContents.Get(i).GetAmount();
  322. int food_stage = m_StomachContents.Get(i).GetFoodStage();
  323. int agents = m_StomachContents.Get(i).m_Agents;
  324. Print(">");
  325. Print("itemname:" + itemname);
  326. Print("amount:" + amount);
  327. Print("food_stage:" + food_stage);
  328. Print("agents:" + agents);
  329. Print(">");
  330. }
  331. Print("m_StomachVolume:" + m_StomachVolume);
  332. Print("================================");
  333. }
  334. void AddToStomach(string class_name, float amount, int food_stage = 0, int agents = 0, float temperature = 0)
  335. {
  336. if (GetIDFromClassname(class_name) == -1)
  337. return;
  338. NutritionalProfile profile = Liquid.GetNutritionalProfileByName(class_name);
  339. if (!profile)
  340. profile = Edible_Base.GetNutritionalProfile(null, class_name, food_stage);
  341. if (profile)
  342. {
  343. // sanity checks start
  344. if (amount > ACCEPTABLE_QUANTITY_MAX || amount < 0)
  345. amount = 0;
  346. if (food_stage < 0 || food_stage > ACCEPTABLE_FOODSTAGE_MAX)
  347. food_stage = FoodStageType.RAW;
  348. // sanity checks end
  349. agents = agents | profile.GetAgents();
  350. bool found = false;
  351. int count = m_StomachContents.Count();
  352. for(int i = 0; i < count; i++)
  353. {
  354. StomachItem stomachItem = m_StomachContents.Get(i);
  355. if (stomachItem.GetClassName() == class_name && stomachItem.m_Agents == agents)
  356. {
  357. if (profile.IsLiquid() || stomachItem.GetFoodStage() == food_stage)
  358. {
  359. float fraction = 1;
  360. if (amount != 0)
  361. fraction = (stomachItem.m_Amount + amount) / amount;
  362. stomachItem.AddTemperature(temperature, fraction);
  363. stomachItem.AddAmount(amount);
  364. stomachItem.AddAgents(agents); //nutrition profile agents
  365. found = true;
  366. }
  367. }
  368. }
  369. if (!found)
  370. m_StomachContents.Insert(new StomachItem(class_name, amount, profile, food_stage, agents, temperature));
  371. UpdateStomachTemperature();
  372. }
  373. }
  374. void OnStoreSave(ParamsWriteContext ctx)
  375. {
  376. ctx.Write(PlayerStomach.CHECKSUM);
  377. ctx.Write(m_StomachContents.Count());
  378. StomachItem stomachItem;
  379. for(int i = 0; i < m_StomachContents.Count();i++)
  380. {
  381. stomachItem = m_StomachContents.Get(i);
  382. int id = PlayerStomach.GetIDFromClassname(stomachItem.m_ClassName);
  383. //Print("SAVE id:" + id);
  384. //Print("SAVE id_bit_offset:" + id_bit_offset);
  385. int writeResult = stomachItem.m_FoodStage | (id << id_bit_offset);
  386. writeResult = writeResult | ((int)stomachItem.m_Amount << quantity_bit_offset);
  387. ctx.Write(writeResult);
  388. ctx.Write(stomachItem.m_Agents);
  389. ctx.Write((int)stomachItem.GetTemperature());
  390. //Print("SAVE writeResult:" + writeResult);
  391. }
  392. //Print("SAVE CHECKSUM:" + PlayerStomach.CHECKSUM);
  393. }
  394. bool OnStoreLoad(ParamsReadContext ctx, int version)
  395. {
  396. int checksum, count;
  397. if (!ctx.Read(checksum))
  398. {
  399. return false;
  400. }
  401. if (!ctx.Read(count))
  402. {
  403. return false;
  404. }
  405. for (int i = 0; i < count; ++i)
  406. {
  407. int value, agents, temperature;
  408. if (!ctx.Read(value))
  409. return false;
  410. if (!ctx.Read(agents))
  411. return false;
  412. if (version >= 140 && !ctx.Read(temperature))
  413. return false;
  414. if (checksum == CHECKSUM)//if checksum matches, add to stomach, otherwise throw the data away but go through the de-serialization to keep the stream intact
  415. {
  416. int amount = value >> quantity_bit_offset;//isolate amount bits
  417. int id_mask = Math.Pow(2, quantity_bit_offset) - 1;
  418. int id = (id_mask & value) >> id_bit_offset;//isolate id bits
  419. int food_mask = Math.Pow(2, id_bit_offset) - 1;
  420. int food_stage = value & food_mask;
  421. //Print("LOAD value:" + value);
  422. //Print("LOAD id:" + id);
  423. //Print("LOAD id_bit_offset:" + id_bit_offset);
  424. //Print("LOAD food_stage:" + food_stage);
  425. string classname = GetClassnameFromID(id);
  426. AddToStomach(classname, amount, food_stage, agents, temperature);
  427. }
  428. }
  429. //Print("LOAD checksum:" + checksum);
  430. if (checksum != CHECKSUM)
  431. {
  432. Print("Stomach checksum fail");
  433. }
  434. return true;
  435. }
  436. int GetDebugObject(array<ref Param> object_out)
  437. {
  438. int count = m_StomachContents.Count();
  439. for(int i = 0; i < m_StomachContents.Count();i++)
  440. {
  441. int id = PlayerStomach.GetIDFromClassname(m_StomachContents.Get(i).m_ClassName);
  442. int food_stage = m_StomachContents.Get(i).m_FoodStage;
  443. int agents = m_StomachContents.Get(i).m_Agents;
  444. float amount = m_StomachContents.Get(i).m_Amount;
  445. float temperature = m_StomachContents.Get(i).GetTemperature();
  446. Param5<int,int,int,float,float> p5 = new Param5<int,int,int,float,float>(id, food_stage, agents, amount, temperature);
  447. object_out.Insert(p5);
  448. }
  449. Param1<float> p1 = new Param1<float>(m_StomachVolume);
  450. object_out.Insert(p1);
  451. Param1<float> paramTemp = new Param1<float>(m_StomachTemperature);
  452. object_out.Insert(paramTemp);
  453. return count;
  454. }
  455. }