playerstomach.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  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. StomachItem item;
  247. int stomach_items_count = m_StomachContents.Count();
  248. m_DigestingType = 0;
  249. if (stomach_items_count == 0)
  250. return;
  251. float digestion_points_per_item = (DIGESTION_POINTS / stomach_items_count) * delta_time;
  252. m_StomachVolume = 0;//reset, it's accumulated with each pass
  253. for (int i = stomach_items_count - 1; i >= 0; i--)
  254. {
  255. item = m_StomachContents[i];
  256. float water, energy, toxicity, volume, consumed_amount;
  257. int agents;
  258. if (item.ProcessDigestion(digestion_points_per_item, water, energy, toxicity, volume, agents, consumed_amount))
  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. {
  268. m_DigestingType = m_DigestingType | DIGESTING_ENERGY;
  269. }
  270. if (water > 0)
  271. {
  272. m_DigestingType = m_DigestingType | DIGESTING_WATER;
  273. }
  274. DigestAgents(agents, consumed_amount);
  275. }
  276. }
  277. void DigestAgents(int agents, float quantity)
  278. {
  279. if (!agents)
  280. return;
  281. agents = agents & (~m_AgentTransferFilter);//filter against the agent filter mask
  282. int highest_bit = Math.Log2(agents) + 1;
  283. for(int i = 0; i < highest_bit;i++)
  284. {
  285. int agent = (1 << i)& agents;
  286. if (agent)
  287. {
  288. m_Player.m_AgentPool.DigestAgent(agent, quantity);
  289. }
  290. }
  291. }
  292. float GetVolumeContainingAgent(eAgents agent)
  293. {
  294. float amountByAgent = 0.0;
  295. foreach (StomachItem item : m_StomachContents)
  296. {
  297. if ((item.m_Agents & agent) == agent)
  298. amountByAgent += item.m_Amount;
  299. }
  300. return amountByAgent;
  301. }
  302. float GetVolumeContainingAgent01(eAgents agent)
  303. {
  304. float amount = GetVolumeContainingAgent(agent);
  305. if (amount > 0.0)
  306. return Math.InverseLerp(0.0, GetStomachVolume(), amount);
  307. return 0.0;
  308. }
  309. void PrintUpdate()
  310. {
  311. Print("================================");
  312. for(int i = 0; i < m_StomachContents.Count(); i++)
  313. {
  314. string itemname = m_StomachContents.Get(i).m_ClassName;
  315. float amount = m_StomachContents.Get(i).GetAmount();
  316. int food_stage = m_StomachContents.Get(i).GetFoodStage();
  317. int agents = m_StomachContents.Get(i).m_Agents;
  318. Print(">");
  319. Print("itemname:" + itemname);
  320. Print("amount:" + amount);
  321. Print("food_stage:" + food_stage);
  322. Print("agents:" + agents);
  323. Print(">");
  324. }
  325. Print("m_StomachVolume:" + m_StomachVolume);
  326. Print("================================");
  327. }
  328. void AddToStomach(string class_name, float amount, int food_stage = 0, int agents = 0, float temperature = 0)
  329. {
  330. if (GetIDFromClassname(class_name) == -1)
  331. return;
  332. bool is_liquid;
  333. NutritionalProfile profile = Liquid.GetNutritionalProfileByName(class_name);
  334. if (profile)
  335. {
  336. is_liquid = true;
  337. }
  338. else
  339. {
  340. profile = Edible_Base.GetNutritionalProfile(null,class_name, food_stage);
  341. }
  342. if (profile)
  343. {
  344. // sanity checks start
  345. if (amount > ACCEPTABLE_QUANTITY_MAX || amount < 0)
  346. {
  347. amount = 0;
  348. }
  349. if (food_stage < 0 || food_stage > ACCEPTABLE_FOODSTAGE_MAX)
  350. {
  351. food_stage = FoodStageType.RAW;
  352. }
  353. // sanity checks end
  354. agents = agents | profile.GetAgents();
  355. bool found = false;
  356. int count = m_StomachContents.Count();
  357. float fraction;
  358. for(int i = 0; i < count; i++)
  359. {
  360. StomachItem stomach_item = m_StomachContents.Get(i);
  361. if (stomach_item.GetClassName() == class_name && stomach_item.m_Agents == agents)
  362. {
  363. if (is_liquid || stomach_item.GetFoodStage() == food_stage)
  364. {
  365. if (amount != 0)
  366. fraction = (stomach_item.m_Amount + amount) / amount;
  367. else
  368. fraction = 1;
  369. stomach_item.AddTemperature(temperature, fraction);
  370. stomach_item.AddAmount(amount);
  371. stomach_item.AddAgents(agents); //nutrition profile agents
  372. found = true;
  373. }
  374. }
  375. }
  376. if (!found)
  377. {
  378. m_StomachContents.Insert(new StomachItem(class_name, amount, profile, food_stage, agents, temperature));
  379. }
  380. UpdateStomachTemperature();
  381. }
  382. }
  383. void OnStoreSave(ParamsWriteContext ctx)
  384. {
  385. ctx.Write(PlayerStomach.CHECKSUM);
  386. ctx.Write(m_StomachContents.Count());
  387. StomachItem stomach_item;
  388. for(int i = 0; i < m_StomachContents.Count();i++)
  389. {
  390. stomach_item = m_StomachContents.Get(i);
  391. int id = PlayerStomach.GetIDFromClassname(stomach_item.m_ClassName);
  392. //Print("SAVE id:" + id);
  393. //Print("SAVE id_bit_offset:" + id_bit_offset);
  394. int write_result = stomach_item.m_FoodStage | (id << id_bit_offset);
  395. write_result = write_result | ((int)stomach_item.m_Amount << quantity_bit_offset);
  396. ctx.Write(write_result);
  397. ctx.Write(stomach_item.m_Agents);
  398. ctx.Write((int)stomach_item.GetTemperature());
  399. //Print("SAVE write_result:" + write_result);
  400. }
  401. //Print("SAVE CHECKSUM:" + PlayerStomach.CHECKSUM);
  402. }
  403. bool OnStoreLoad(ParamsReadContext ctx, int version)
  404. {
  405. int checksum, count;
  406. if (!ctx.Read(checksum))
  407. {
  408. return false;
  409. }
  410. if (!ctx.Read(count))
  411. {
  412. return false;
  413. }
  414. for(int i = 0; i < count; i++)
  415. {
  416. int value, agents, temperature;
  417. if (!ctx.Read(value))
  418. {
  419. return false;
  420. }
  421. if (!ctx.Read(agents))
  422. {
  423. return false;
  424. }
  425. if (!ctx.Read(temperature))
  426. {
  427. return false;
  428. }
  429. 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
  430. {
  431. int amount = value >> quantity_bit_offset;//isolate amount bits
  432. int id_mask = Math.Pow(2, quantity_bit_offset) - 1;
  433. int id = (id_mask & value) >> id_bit_offset;//isolate id bits
  434. int food_mask = Math.Pow(2, id_bit_offset) - 1;
  435. int food_stage = value & food_mask;
  436. //Print("LOAD value:" + value);
  437. //Print("LOAD id:" + id);
  438. //Print("LOAD id_bit_offset:" + id_bit_offset);
  439. //Print("LOAD food_stage:" + food_stage);
  440. string classname = GetClassnameFromID(id);
  441. AddToStomach(classname, amount, food_stage, agents, temperature);
  442. }
  443. }
  444. //Print("LOAD checksum:" + checksum);
  445. if (checksum != CHECKSUM)
  446. {
  447. Print("Stomach checksum fail");
  448. }
  449. return true;
  450. }
  451. int GetDebugObject(array<ref Param> object_out)
  452. {
  453. int count = m_StomachContents.Count();
  454. for(int i = 0; i < m_StomachContents.Count();i++)
  455. {
  456. int id = PlayerStomach.GetIDFromClassname(m_StomachContents.Get(i).m_ClassName);
  457. int food_stage = m_StomachContents.Get(i).m_FoodStage;
  458. int agents = m_StomachContents.Get(i).m_Agents;
  459. float amount = m_StomachContents.Get(i).m_Amount;
  460. float temperature = m_StomachContents.Get(i).GetTemperature();
  461. Param5<int,int,int,float,float> p5 = new Param5<int,int,int,float,float>(id, food_stage, agents, amount, temperature);
  462. object_out.Insert(p5);
  463. }
  464. Param1<float> p1 = new Param1<float>(m_StomachVolume);
  465. object_out.Insert(p1);
  466. Param1<float> paramTemp = new Param1<float>(m_StomachTemperature);
  467. object_out.Insert(paramTemp);
  468. return count;
  469. }
  470. }