environment.c 59 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828
  1. enum EEnvironmentHeatcomfortBehaviorCategory
  2. {
  3. DEFAULT,
  4. CAR_ENGINE_ON
  5. }
  6. class EnvironmentSnapshotData
  7. {
  8. float m_ClothingHeatComfort;
  9. float m_TargetHeatComfort;
  10. }
  11. class Environment
  12. {
  13. const float RAIN_LIMIT_LOW = 0.05;
  14. protected const float SNOWFALL_LIMIT_LOW = 0.5;
  15. protected const float SNOWFALL_WIND_COMBINED_THRESHOLD = 1.3;
  16. const float WATER_LEVEL_HIGH = 1.5;
  17. const float WATER_LEVEL_MID = 1.2;
  18. const float WATER_LEVEL_LOW = 0.5;
  19. const float WATER_LEVEL_NONE = 0.15;
  20. protected float m_WetDryTick; //ticks passed since last clothing wetting or drying
  21. protected float m_ItemsWetnessMax; //! keeps wetness of most wet item in player's possesion
  22. protected float m_RoofCheckTimer; // keeps info about tick time
  23. //player
  24. protected PlayerBase m_Player;
  25. protected float m_PlayerHeightPos; // y position of player above water level (meters)
  26. protected float m_PlayerSpeed; // 1-3 speed of player movement
  27. protected float m_PlayerTemperature; //34-44
  28. protected float m_PlayerHeat; //3-9 heatcomfort generated by entites movement
  29. protected float m_HeatComfort; //! player's heatcomfort (buffered, stored in player stats)
  30. protected float m_TargetHeatComfort; //! target value of heatcomfort (non-buffered)
  31. //environment
  32. protected float m_Rain = 0; // 0-1 amount of rain
  33. protected float m_Snowfall = 0; // 0-1 amount of rain
  34. protected float m_Wind = 0; // strength of wind
  35. protected float m_Fog = 0; // 0-1 how foggy it is
  36. protected float m_DayOrNight = 0; // 0-1 day(0) or night(1)
  37. protected float m_Clouds = 0; // 0-1 how cloudy it is
  38. protected float m_EnvironmentTemperature; //temperature of environment player is in
  39. protected float m_Time = 0;
  40. protected string m_SurfaceType;
  41. protected int m_LiquidType;
  42. //item temperatures
  43. protected float m_ItemTemperatureCoef;
  44. //
  45. protected float m_WaterLevel;
  46. protected bool m_IsUnderRoof;
  47. private bool m_IsUnderRoofBuilding;
  48. protected bool m_IsInWater;
  49. protected bool m_IsTempSet;
  50. //
  51. protected float m_HeatBufferTimer; //! reused as state toggle
  52. protected float m_HeatBufferCapPrevious;
  53. protected ref array<int> m_SlotIdsComplete;
  54. protected ref array<int> m_SlotIdsUpper;
  55. protected ref array<int> m_SlotIdsBottom;
  56. protected ref array<int> m_SlotIdsLower;
  57. protected ref array<int> m_HeadParts;
  58. protected ref array<int> m_BodyParts;
  59. protected ref array<int> m_FeetParts;
  60. protected WorldData m_WorldData;
  61. protected bool m_HasTemperatureSources;
  62. protected float m_UTSAverageTemperature;
  63. protected ref array<UTemperatureSource> m_UTemperatureSources;
  64. protected ref SimpleMovingAverage<float> m_UTSAverageTemperatureBuffer;
  65. protected ref SimpleMovingAverage<float> m_AverageHeatComfortBuffer;
  66. protected int m_HeatComfortBehaviorCategory;
  67. private bool m_Initialized;
  68. #ifdef DIAG_DEVELOPER
  69. bool m_Debug = false;
  70. bool m_DebugLogDryWet = false;
  71. bool m_DebugLogItemHeat = false;
  72. #endif
  73. void Environment(PlayerBase pPlayer)
  74. {
  75. m_Player = pPlayer;
  76. }
  77. void Init()
  78. {
  79. m_PlayerSpeed = 0.0;
  80. m_WetDryTick = 0.0;
  81. m_RoofCheckTimer = 0.0;
  82. m_WaterLevel = 0.0;
  83. m_HeatComfort = m_Player.GetStatHeatComfort().Get();
  84. m_WorldData = g_Game.GetMission().GetWorldData();
  85. m_EnvironmentTemperature = m_WorldData.GetBaseEnvTemperatureAtObject(m_Player);
  86. m_IsUnderRoof = false;
  87. m_IsInWater = false;
  88. m_SurfaceType = "cp_dirt";
  89. m_HeatBufferTimer = 0.0;
  90. m_UTSAverageTemperature = 0.0;
  91. m_UTemperatureSources = new array<UTemperatureSource>();
  92. m_UTSAverageTemperatureBuffer = new SimpleMovingAverage<float>(10, 0.0);
  93. m_AverageHeatComfortBuffer = new SimpleMovingAverage<float>(20, m_HeatComfort);
  94. //! whole body slots
  95. m_SlotIdsComplete = new array<int>();
  96. m_SlotIdsComplete = {
  97. InventorySlots.HEADGEAR,
  98. InventorySlots.MASK,
  99. InventorySlots.EYEWEAR,
  100. InventorySlots.GLOVES,
  101. InventorySlots.ARMBAND,
  102. InventorySlots.BODY,
  103. InventorySlots.HIPS,
  104. InventorySlots.VEST,
  105. InventorySlots.BACK,
  106. InventorySlots.LEGS,
  107. InventorySlots.FEET
  108. };
  109. //! upper body part slots
  110. m_SlotIdsUpper = new array<int>();
  111. m_SlotIdsUpper = {
  112. InventorySlots.GLOVES,
  113. InventorySlots.ARMBAND,
  114. InventorySlots.BODY,
  115. InventorySlots.HIPS,
  116. InventorySlots.VEST,
  117. InventorySlots.BACK,
  118. InventorySlots.LEGS,
  119. InventorySlots.FEET
  120. };
  121. //! bottom body part slots
  122. m_SlotIdsBottom = new array<int>();
  123. m_SlotIdsBottom = {
  124. InventorySlots.HIPS,
  125. InventorySlots.LEGS,
  126. InventorySlots.FEET
  127. };
  128. //! lower body part slots
  129. m_SlotIdsLower = new array<int>();
  130. m_SlotIdsLower = {
  131. InventorySlots.FEET,
  132. };
  133. //! --------------------------
  134. //! heat comfort related slots
  135. m_HeadParts = new array<int>();
  136. m_HeadParts = {
  137. InventorySlots.HEADGEAR,
  138. InventorySlots.MASK,
  139. };
  140. m_BodyParts = new array<int>();
  141. m_BodyParts = {
  142. InventorySlots.GLOVES,
  143. InventorySlots.HIPS,
  144. InventorySlots.BODY,
  145. InventorySlots.BACK,
  146. InventorySlots.VEST,
  147. InventorySlots.MELEE,
  148. InventorySlots.SHOULDER
  149. };
  150. m_FeetParts = new array<int>();
  151. m_FeetParts = {
  152. InventorySlots.LEGS,
  153. InventorySlots.FEET,
  154. };
  155. m_HeatComfortBehaviorCategory = EEnvironmentHeatcomfortBehaviorCategory.DEFAULT;
  156. m_EnvironmentSnapshot = new EnvironmentSnapshotData();
  157. m_Initialized = true;
  158. }
  159. // Calculates heatisolation of clothing, process its wetness, collects heat from heated items and calculates player's heat comfort
  160. void Update(float pDelta)
  161. {
  162. if (m_Player && m_Initialized)
  163. {
  164. m_RoofCheckTimer += pDelta;
  165. //! check if player is under roof (only if the Building check is false)
  166. if ( m_RoofCheckTimer >= GameConstants.ENVIRO_TICK_ROOF_RC_CHECK )
  167. {
  168. if ( !IsInsideBuilding() )
  169. CheckUnderRoof();
  170. m_RoofCheckTimer = 0;
  171. }
  172. m_Time += pDelta;
  173. if (m_Time >= GameConstants.ENVIRO_TICK_RATE)
  174. {
  175. m_Time = 0;
  176. m_WetDryTick++; // Sets whether it is time to add wetness to items and clothing
  177. //! Updates data
  178. CheckWaterContact(m_WaterLevel);
  179. CollectAndSetPlayerData();
  180. CollectAndSetEnvironmentData();
  181. GatherTemperatureSources();
  182. ProcessTemperatureSources();
  183. //! Process temperatures
  184. ProcessItemsTemperature(m_HeadParts);
  185. ProcessItemsTemperature(m_BodyParts);
  186. ProcessItemsTemperature(m_FeetParts);
  187. ProcessItemsInHandsTemperature();
  188. //! heat comfort calculation
  189. if (DetermineHeatcomfortBehavior())
  190. SetHeatcomfortDirectly();
  191. else
  192. ProcessHeatComfort();
  193. //! Process item wetness/dryness
  194. if (m_WetDryTick >= GameConstants.ENVIRO_TICKS_TO_WETNESS_CALCULATION)
  195. {
  196. if (IsWaterContact())
  197. {
  198. ProcessWetnessByWaterLevel(m_WaterLevel);
  199. }
  200. else if ((IsRaining() || (IsSnowing() && MiscGameplayFunctions.GetCombinedSnowfallWindValue() > SNOWFALL_WIND_COMBINED_THRESHOLD)) && !IsInsideBuilding() && !IsUnderRoof() && !IsChildOfType({Car}))
  201. {
  202. ProcessItemsWetness(m_SlotIdsComplete);
  203. }
  204. else
  205. {
  206. ProcessItemsDryness();
  207. }
  208. //! setting of wetness/dryiness of player
  209. if ((m_ItemsWetnessMax < GameConstants.STATE_WET) && (m_Player.GetStatWet().Get() == 1))
  210. {
  211. m_Player.GetStatWet().Set(0);
  212. }
  213. else if ((m_ItemsWetnessMax >= GameConstants.STATE_WET) && (m_Player.GetStatWet().Get() == 0))
  214. {
  215. m_Player.GetStatWet().Set(1);
  216. }
  217. m_WetDryTick = 0;
  218. m_ItemsWetnessMax = 0; //! reset item wetness counter;
  219. }
  220. }
  221. }
  222. }
  223. bool IsTemperatureSet()
  224. {
  225. return m_IsTempSet;
  226. }
  227. //! Returns heat player generated based on player's movement speed (for now)
  228. protected float GetPlayerHeat()
  229. {
  230. float heat = m_PlayerSpeed * GameConstants.ENVIRO_DEFAULT_ENTITY_HEAT;
  231. return heat;
  232. }
  233. bool IsUnderRoof()
  234. {
  235. return m_IsUnderRoof;
  236. }
  237. protected bool IsWaterContact()
  238. {
  239. return m_IsInWater;
  240. }
  241. bool IsInsideBuilding()
  242. {
  243. return m_Player && m_Player.IsSoundInsideBuilding();
  244. }
  245. protected bool IsInsideVehicle()
  246. {
  247. return m_Player && m_Player.IsInVehicle();
  248. }
  249. private bool IsChildOfType(array<typename> typenames)
  250. {
  251. Object parent = Object.Cast(m_Player.GetParent());
  252. if (parent)
  253. return parent.IsAnyInherited(typenames);
  254. return false;
  255. }
  256. private bool IsUnderRoofBuilding()
  257. {
  258. return m_IsUnderRoofBuilding;
  259. }
  260. protected bool IsRaining()
  261. {
  262. return m_Rain > RAIN_LIMIT_LOW;
  263. }
  264. protected bool IsSnowing()
  265. {
  266. return m_Snowfall > SNOWFALL_LIMIT_LOW;
  267. }
  268. protected bool DetermineHeatcomfortBehavior()
  269. {
  270. if (IsChildOfType({Car}))
  271. {
  272. CarScript car = CarScript.Cast(m_Player.GetParent());
  273. if (car && car.EngineIsOn())
  274. {
  275. m_HeatComfortBehaviorCategory = EEnvironmentHeatcomfortBehaviorCategory.CAR_ENGINE_ON;
  276. return true;
  277. }
  278. }
  279. m_HeatComfortBehaviorCategory = EEnvironmentHeatcomfortBehaviorCategory.DEFAULT;
  280. return false;
  281. }
  282. //! Checks whether Player is sheltered
  283. protected void CheckUnderRoof()
  284. {
  285. // if inside vehicle return immediatelly
  286. if (IsChildOfType({Car}))
  287. {
  288. m_IsUnderRoof = false;
  289. m_IsUnderRoofBuilding = false;
  290. return;
  291. }
  292. float hitFraction;
  293. vector hitPosition, hitNormal;
  294. vector from = m_Player.GetPosition();
  295. vector to = from + "0 25 0";
  296. Object hitObject;
  297. PhxInteractionLayers collisionLayerMask = PhxInteractionLayers.ITEM_LARGE|PhxInteractionLayers.BUILDING|PhxInteractionLayers.VEHICLE;
  298. m_IsUnderRoof = DayZPhysics.RayCastBullet(from, to, collisionLayerMask, null, hitObject, hitPosition, hitNormal, hitFraction);
  299. m_IsUnderRoofBuilding = hitObject && hitObject.IsInherited(House);
  300. }
  301. protected void CheckWaterContact(out float pWaterLevel)
  302. {
  303. string surfType;
  304. int liquidType;
  305. m_IsInWater = false;
  306. if (m_Player.PhysicsGetLinkedEntity() || IsChildOfType({Transport}))
  307. return;
  308. if (m_Player.IsSwimming())
  309. {
  310. g_Game.SurfaceUnderObjectByBoneCorrectedLiquid(m_Player, SurfaceAnimationBone.RightFrontLimb, surfType, liquidType);
  311. m_SurfaceType = surfType;
  312. m_LiquidType = liquidType;
  313. m_IsInWater = true;
  314. m_Player.SetInWater(m_IsInWater);
  315. HumanMovementState hms = new HumanMovementState();
  316. m_Player.GetMovementState(hms);
  317. pWaterLevel = WATER_LEVEL_MID;
  318. if (hms.m_iMovement >= DayZPlayerConstants.MOVEMENTIDX_WALK)
  319. pWaterLevel = WATER_LEVEL_HIGH;
  320. return;
  321. }
  322. //! no valid surface under character
  323. if (IsUnderRoofBuilding())
  324. {
  325. m_IsInWater = false;
  326. return;
  327. }
  328. string impact;
  329. g_Game.SurfaceUnderObjectExCorrectedLiquid(m_Player, surfType, impact, liquidType);
  330. switch (liquidType)
  331. {
  332. case LIQUID_SALTWATER:
  333. case LIQUID_WATER:
  334. case LIQUID_RIVERWATER:
  335. case LIQUID_FRESHWATER:
  336. case LIQUID_STILLWATER:
  337. case LIQUID_HOTWATER:
  338. pWaterLevel = m_Player.GetCurrentWaterLevel();
  339. m_IsInWater = true;
  340. break;
  341. }
  342. //! sync info about water contact to player
  343. m_Player.SetInWater(m_IsInWater);
  344. //! update active surface
  345. m_SurfaceType = surfType;
  346. m_LiquidType = liquidType;
  347. }
  348. float GetWindModifierPerSurface()
  349. {
  350. if (IsUnderRoofBuilding())
  351. return 0.0;
  352. return g_Game.ConfigGetFloat("CfgSurfaces " + m_SurfaceType + " windModifier");
  353. }
  354. float GetTemperature()
  355. {
  356. return m_EnvironmentTemperature;
  357. }
  358. float GetTargetHeatComfort()
  359. {
  360. return m_TargetHeatComfort;
  361. }
  362. // Calculates and return temperature of environment
  363. protected float GetEnvironmentTemperature()
  364. {
  365. float temperature = m_WorldData.GetTemperature(m_Player, EEnvironmentTemperatureComponent.ALTITUDE | EEnvironmentTemperatureComponent.OVERCAST);
  366. if (IsWaterContact())
  367. {
  368. float waterBodyTemperature = m_WorldData.GetLiquidTypeEnviroTemperature(m_LiquidType);
  369. temperature = waterBodyTemperature - m_WorldData.m_WaterContactTemperatureModifier;
  370. return temperature;
  371. }
  372. if (IsInsideBuilding() || m_IsUnderRoofBuilding)
  373. {
  374. temperature += m_WorldData.m_TemperatureInsideBuildingsModifier;
  375. }
  376. else if (IsChildOfType({Car}))
  377. {
  378. temperature += Math.AbsFloat(temperature * GameConstants.ENVIRO_TEMPERATURE_INSIDE_VEHICLE_COEF);
  379. return temperature;
  380. }
  381. else if (IsUnderRoof() && !m_IsUnderRoofBuilding)
  382. {
  383. temperature = m_WorldData.GetTemperature(m_Player, EEnvironmentTemperatureComponent.ALTITUDE | EEnvironmentTemperatureComponent.OVERCAST|EEnvironmentTemperatureComponent.FOG);
  384. temperature += WindEffectTemperatureValue(temperature) * GetWindModifierPerSurface() * GameConstants.ENVIRO_TEMPERATURE_UNDERROOF_COEF;
  385. }
  386. else
  387. {
  388. temperature = m_WorldData.GetTemperature(m_Player, EEnvironmentTemperatureComponent.ALTITUDE | EEnvironmentTemperatureComponent.OVERCAST|EEnvironmentTemperatureComponent.FOG);
  389. temperature += m_WorldData.GetTemperatureComponentValue(temperature, EEnvironmentTemperatureComponent.WIND) * GetWindModifierPerSurface();
  390. }
  391. // incorporate temperature from temperature sources (buffer)
  392. if (Math.AbsFloat(m_UTSAverageTemperature) > 0.0 && m_UTSAverageTemperature > temperature)
  393. temperature = m_UTSAverageTemperature;
  394. return temperature;
  395. }
  396. // Calculates wet/drying delta based on player's location and weather
  397. float GetWetDelta()
  398. {
  399. float wetDelta = 0;
  400. if ( IsWaterContact() )
  401. {
  402. //! player is getting wet by movement/swimming in water (+differentiate wetDelta by water level)
  403. if (m_WaterLevel >= WATER_LEVEL_HIGH)
  404. {
  405. wetDelta = 1;
  406. }
  407. else if (m_WaterLevel >= WATER_LEVEL_MID && m_WaterLevel < WATER_LEVEL_HIGH)
  408. {
  409. wetDelta = 0.66;
  410. }
  411. else if (m_WaterLevel >= WATER_LEVEL_LOW && m_WaterLevel < WATER_LEVEL_MID)
  412. {
  413. wetDelta = 0.66;
  414. }
  415. else if (m_WaterLevel >= WATER_LEVEL_NONE && m_WaterLevel < WATER_LEVEL_LOW)
  416. {
  417. wetDelta = 0.33;
  418. }
  419. }
  420. else if (!IsInsideBuilding() && !IsUnderRoof() && !IsChildOfType({Car}))
  421. {
  422. if (IsRaining())
  423. wetDelta = GameConstants.ENVIRO_WET_INCREMENT * GameConstants.ENVIRO_TICKS_TO_WETNESS_CALCULATION * (m_Rain) * (1 + (GameConstants.ENVIRO_WIND_EFFECT * m_Wind));
  424. if (IsSnowing() && MiscGameplayFunctions.GetCombinedSnowfallWindValue() > SNOWFALL_WIND_COMBINED_THRESHOLD)
  425. wetDelta = GameConstants.ENVIRO_WET_INCREMENT * GameConstants.ENVIRO_TICKS_TO_WETNESS_CALCULATION * (m_Snowfall - SNOWFALL_LIMIT_LOW) * GameConstants.ENVIRO_SNOW_WET_COEF * (1 + (GameConstants.ENVIRO_WIND_EFFECT * m_Wind));
  426. }
  427. else
  428. {
  429. //! player is drying
  430. float tempEffect = Math.Max(m_PlayerHeat + GetEnvironmentTemperature(), 1.0);
  431. float weatherEffect = ((1 - (m_Fog * GameConstants.ENVIRO_FOG_DRY_EFFECT))) * (1 - (m_Clouds * GameConstants.ENVIRO_CLOUD_DRY_EFFECT));
  432. if (weatherEffect <= 0)
  433. {
  434. weatherEffect = 1.0;
  435. }
  436. wetDelta = -(GameConstants.ENVIRO_DRY_INCREMENT * weatherEffect * tempEffect);
  437. if (!IsInsideBuilding())
  438. {
  439. wetDelta *= 1 + (GameConstants.ENVIRO_WIND_EFFECT * m_Wind);
  440. }
  441. }
  442. return wetDelta;
  443. }
  444. // EXPOSURE
  445. // Each tick updates current entity member variables
  446. protected void CollectAndSetPlayerData()
  447. {
  448. vector playerPos = m_Player.GetPosition();
  449. m_PlayerHeightPos = playerPos[1];
  450. HumanCommandMove hcm = m_Player.GetCommand_Move();
  451. if (hcm)
  452. {
  453. m_PlayerSpeed = hcm.GetCurrentMovementSpeed();
  454. }
  455. m_PlayerHeat = GetPlayerHeat();
  456. }
  457. // Each tick updates current environment member variables
  458. protected void CollectAndSetEnvironmentData()
  459. {
  460. Weather weather = g_Game.GetWeather();
  461. m_Rain = weather.GetRain().GetActual();
  462. m_Snowfall = weather.GetSnowfall().GetActual();
  463. m_DayOrNight = g_Game.GetMission().GetWorldData().GetDaytime();
  464. m_Fog = weather.GetFog().GetActual();
  465. m_Clouds = weather.GetOvercast().GetActual();
  466. m_Wind = weather.GetWindMagnitude().GetActual();
  467. SetEnvironmentTemperature();
  468. SetAreaGenericColdness();
  469. }
  470. void SetEnvironmentTemperature()
  471. {
  472. m_IsTempSet = true;
  473. m_EnvironmentTemperature = GetEnvironmentTemperature();
  474. }
  475. //! Determines whether player is in cold area which restricts use of some actions (digging)
  476. void SetAreaGenericColdness()
  477. {
  478. float heigthCorrectedTemp = m_WorldData.GetBaseEnvTemperatureAtObject(m_Player);
  479. m_Player.SetInColdArea(heigthCorrectedTemp <= GameConstants.COLD_AREA_TEMPERATURE_THRESHOLD);
  480. }
  481. //! process attachments by water depth
  482. protected void ProcessWetnessByWaterLevel(float pWaterLevel)
  483. {
  484. if (pWaterLevel >= WATER_LEVEL_HIGH)
  485. ProcessItemsWetness(m_SlotIdsComplete);
  486. else if (pWaterLevel >= WATER_LEVEL_MID && pWaterLevel < WATER_LEVEL_HIGH)
  487. ProcessItemsWetness(m_SlotIdsUpper);
  488. else if (pWaterLevel >= WATER_LEVEL_LOW && pWaterLevel < WATER_LEVEL_MID)
  489. ProcessItemsWetness(m_SlotIdsBottom);
  490. else if (pWaterLevel >= WATER_LEVEL_NONE && pWaterLevel < WATER_LEVEL_LOW)
  491. ProcessItemsWetness(m_SlotIdsLower);
  492. }
  493. // Wets or dry items once in given time
  494. protected void ProcessItemsWetness(array<int> pSlotIds)
  495. {
  496. EntityAI attachment;
  497. int playerAttachmentCount = m_Player.GetInventory().AttachmentCount();
  498. LogDryWetProcess(string.Format("Environment :: ProcessItemsWetness (update interval=%1s)", GameConstants.ENVIRO_TICK_RATE));
  499. for (int attIdx = 0; attIdx < playerAttachmentCount; ++attIdx)
  500. {
  501. attachment = m_Player.GetInventory().GetAttachmentFromIndex(attIdx);
  502. if (attachment.IsItemBase())
  503. {
  504. int attachmentSlotsCount = attachment.GetInventory().GetSlotIdCount();
  505. for (int attachmentSlotId = 0; attachmentSlotId < attachmentSlotsCount; ++attachmentSlotId)
  506. {
  507. int attachmentSlot = attachment.GetInventory().GetSlotId(attachmentSlotId);
  508. for (int i = 0; i < pSlotIds.Count(); ++i)
  509. {
  510. if (attachmentSlot == pSlotIds.Get(i))
  511. {
  512. ApplyWetnessToItem(ItemBase.Cast(attachment));
  513. break;
  514. }
  515. }
  516. }
  517. }
  518. }
  519. if (m_Player.GetItemInHands())
  520. ApplyWetnessToItem(m_Player.GetItemInHands());
  521. LogDryWetProcess("==========");
  522. }
  523. protected void ProcessItemsDryness()
  524. {
  525. EntityAI attachment;
  526. ItemBase item;
  527. int attCount = m_Player.GetInventory().AttachmentCount();
  528. LogDryWetProcess(string.Format("Environment :: ProcessItemsDryness (update interval=%1s)", GameConstants.ENVIRO_TICK_RATE));
  529. EnvironmentDrynessData drynessData = new EnvironmentDrynessData();
  530. drynessData.m_UseTemperatureSources = m_HasTemperatureSources;
  531. if (m_HasTemperatureSources)
  532. {
  533. float distance = vector.Distance(m_UTemperatureSources[0].GetPosition(), m_Player.GetPosition());
  534. distance = Math.Max(distance, 0.1);
  535. drynessData.m_TemperatureSourceDistance = distance;
  536. LogDryWetProcess(string.Format("distance to heatsource: %1 m", distance));
  537. }
  538. for (int attIdx = 0; attIdx < attCount; ++attIdx)
  539. {
  540. attachment = m_Player.GetInventory().GetAttachmentFromIndex(attIdx);
  541. if (attachment && attachment.IsItemBase())
  542. {
  543. item = ItemBase.Cast(attachment);
  544. if (item)
  545. ApplyDrynessToItemEx(item, drynessData);
  546. }
  547. }
  548. if (m_Player.GetItemInHands())
  549. {
  550. ApplyDrynessToItemEx(m_Player.GetItemInHands(), drynessData);
  551. }
  552. LogDryWetProcess("==========");
  553. }
  554. protected void ApplyWetnessToItem(ItemBase pItem)
  555. {
  556. if (pItem)
  557. {
  558. ItemBase parentItem;
  559. bool isParentWet = false;
  560. bool parentContainsLiquid = false;
  561. InventoryLocation iLoc = new InventoryLocation();
  562. if (pItem.GetInventory().GetCurrentInventoryLocation(iLoc))
  563. {
  564. EntityAI parent = iLoc.GetParent();
  565. if (parent)
  566. {
  567. parentItem = ItemBase.Cast(parent);
  568. if (parentItem)
  569. {
  570. if (parentItem.GetWet() >= GameConstants.STATE_SOAKING_WET)
  571. isParentWet = true;
  572. if ((parentItem.GetLiquidType() != 0) && (parentItem.GetQuantity() > 0))
  573. parentContainsLiquid = true;
  574. }
  575. else
  576. isParentWet = true;
  577. if ((pItem.GetWet() > m_ItemsWetnessMax) && (parent == m_Player))
  578. m_ItemsWetnessMax = pItem.GetWet();
  579. }
  580. }
  581. if (isParentWet || parentContainsLiquid)
  582. {
  583. float soakingCoef = 0;
  584. if (parentContainsLiquid)
  585. {
  586. soakingCoef = pItem.GetSoakingIncrement("parentWithLiquid");
  587. LogDryWetProcess(string.Format("%1 (soak coef=%2/s, current wetness=%3) [parent contains liquid]", pItem.GetDisplayName(), soakingCoef / GameConstants.ENVIRO_TICK_RATE, pItem.GetWet()), parentItem != null);
  588. }
  589. else if (isParentWet && parentItem)
  590. {
  591. if (pItem.GetWet() < parentItem.GetWet())
  592. soakingCoef = GetWetDelta();
  593. LogDryWetProcess(string.Format("%1 (soak coef=%2/s, current wetness=%3) [parent wet]", pItem.GetDisplayName(), soakingCoef / GameConstants.ENVIRO_TICK_RATE, pItem.GetWet()), parentItem != null);
  594. }
  595. else
  596. {
  597. soakingCoef = GetWetDelta();
  598. LogDryWetProcess(string.Format("%1 (soak coef=%2/s, current wetness=%3) [normal]", pItem.GetDisplayName(), soakingCoef / GameConstants.ENVIRO_TICK_RATE, pItem.GetWet()), parentItem != null);
  599. }
  600. pItem.AddWet(soakingCoef);
  601. if (pItem.GetInventory().GetCargo())
  602. {
  603. int inItemCount = pItem.GetInventory().GetCargo().GetItemCount();
  604. for (int i = 0; i < inItemCount; ++i)
  605. {
  606. ItemBase inItem;
  607. if (Class.CastTo(inItem, pItem.GetInventory().GetCargo().GetItem(i)))
  608. ApplyWetnessToItem(inItem);
  609. }
  610. }
  611. int attCount = pItem.GetInventory().AttachmentCount();
  612. if (attCount > 0)
  613. {
  614. for (int attIdx = 0; attIdx < attCount; ++attIdx)
  615. {
  616. EntityAI attachment = pItem.GetInventory().GetAttachmentFromIndex(attIdx);
  617. ItemBase itemAtt = ItemBase.Cast(attachment);
  618. if (itemAtt)
  619. ApplyWetnessToItem(itemAtt);
  620. }
  621. }
  622. }
  623. }
  624. }
  625. protected void ApplyDrynessToItem(ItemBase pItem)
  626. {
  627. EnvironmentDrynessData drynessData = new EnvironmentDrynessData();
  628. ApplyDrynessToItemEx(pItem, drynessData);
  629. }
  630. protected void ApplyDrynessToItemEx(ItemBase pItem, EnvironmentDrynessData pDrynessData)
  631. {
  632. if (pItem)
  633. {
  634. float dryingIncrement = pItem.GetDryingIncrement("player");
  635. if (pDrynessData.m_UseTemperatureSources)
  636. dryingIncrement = pItem.GetDryingIncrement("playerHeatSource");
  637. ItemBase parentItem;
  638. bool isParentWet = false;
  639. bool parentContainsLiquid = false;
  640. InventoryLocation iLoc = new InventoryLocation();
  641. if (pItem.GetInventory().GetCurrentInventoryLocation(iLoc))
  642. {
  643. EntityAI parent = iLoc.GetParent();
  644. if (parent)
  645. {
  646. parentItem = ItemBase.Cast(parent);
  647. if (parentItem)
  648. {
  649. if (parentItem.GetWet() >= GameConstants.STATE_SOAKING_WET)
  650. isParentWet = true;
  651. if ((parentItem.GetLiquidType() != 0) && (parentItem.GetQuantity() > 0))
  652. parentContainsLiquid = true;
  653. }
  654. if ((pItem.GetWet() > m_ItemsWetnessMax) && (parent == m_Player))
  655. m_ItemsWetnessMax = pItem.GetWet();
  656. }
  657. }
  658. float dryingCoef = 0;
  659. if (!isParentWet && !parentContainsLiquid)
  660. {
  661. dryingCoef = (-1 * GameConstants.ENVIRO_TICK_RATE * dryingIncrement) / pDrynessData.m_TemperatureSourceDistance;
  662. if (pItem.GetWet() >= GameConstants.STATE_DAMP)
  663. {
  664. LogDryWetProcess(string.Format("%1 (dry coef=%2/s, current wetness=%3) [normal]", pItem.GetDisplayName(), dryingCoef / GameConstants.ENVIRO_TICK_RATE, pItem.GetWet()), parentItem != null);
  665. pItem.AddWet(dryingCoef);
  666. }
  667. if (pItem.GetInventory().GetCargo())
  668. {
  669. int inItemCount = pItem.GetInventory().GetCargo().GetItemCount();
  670. for (int i = 0; i < inItemCount; ++i)
  671. {
  672. ItemBase inItem;
  673. if (Class.CastTo(inItem, pItem.GetInventory().GetCargo().GetItem(i)))
  674. ApplyDrynessToItemEx(inItem, pDrynessData);
  675. }
  676. }
  677. int attCount = pItem.GetInventory().AttachmentCount();
  678. if (attCount > 0)
  679. {
  680. for (int attIdx = 0; attIdx < attCount; ++attIdx)
  681. {
  682. EntityAI attachment = pItem.GetInventory().GetAttachmentFromIndex(attIdx);
  683. ItemBase itemAtt;
  684. if (ItemBase.CastTo(itemAtt, attachment))
  685. ApplyDrynessToItemEx(itemAtt, pDrynessData);
  686. }
  687. }
  688. }
  689. if (parentContainsLiquid)
  690. {
  691. //! adds wetness to item inside parent item containing liquid
  692. dryingCoef = (GameConstants.ENVIRO_TICK_RATE * pItem.GetSoakingIncrement("parentWithLiquid")) / pDrynessData.m_TemperatureSourceDistance;
  693. LogDryWetProcess(string.Format("%1 (dry coef=%2/s, current wetness=%3) [parent contains liquid]", pItem.GetDisplayName(), dryingCoef / GameConstants.ENVIRO_TICK_RATE, pItem.GetWet()), parentItem != null);
  694. pItem.AddWet(dryingCoef);
  695. }
  696. if (isParentWet)
  697. {
  698. //! adds wetness to item inside wet parent item
  699. dryingCoef = (GameConstants.ENVIRO_TICK_RATE * pItem.GetSoakingIncrement("wetParent")) / pDrynessData.m_TemperatureSourceDistance;
  700. LogDryWetProcess(string.Format("%1 (dry coef=%2/s, current wetness=%3) [parent wet]", pItem.GetDisplayName(), dryingCoef / GameConstants.ENVIRO_TICK_RATE, pItem.GetWet()), parentItem != null);
  701. pItem.AddWet(dryingCoef);
  702. }
  703. }
  704. }
  705. // HEAT COMFORT
  706. //! Calculates and process player's heatcomfort related to body parts
  707. protected void ProcessHeatComfort()
  708. {
  709. float hcPenaltyTotal //! Heat Comfort Penalty
  710. // NEW body parts => splitted
  711. float hcBodyPartTotal, hcBodyPart;
  712. float hBodyPartTotal, hBodyPart;
  713. float heatComfortSum = 0.0;
  714. float heatItems = 0.0;
  715. LogItemHeat("====================");
  716. BodyPartHeatProperties(InventorySlots.HEADGEAR, GameConstants.ENVIRO_HEATCOMFORT_HEADGEAR_WEIGHT, hcBodyPart, hBodyPart);
  717. hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
  718. BodyPartHeatProperties(InventorySlots.MASK, GameConstants.ENVIRO_HEATCOMFORT_MASK_WEIGHT, hcBodyPart, hBodyPart);
  719. hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
  720. BodyPartHeatProperties(InventorySlots.VEST, GameConstants.ENVIRO_HEATCOMFORT_VEST_WEIGHT, hcBodyPart, hBodyPart);
  721. hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
  722. BodyPartHeatProperties(InventorySlots.BODY, GameConstants.ENVIRO_HEATCOMFORT_BODY_WEIGHT, hcBodyPart, hBodyPart);
  723. hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
  724. BodyPartHeatProperties(InventorySlots.BACK, GameConstants.ENVIRO_HEATCOMFORT_BACK_WEIGHT, hcBodyPart, hBodyPart);
  725. hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
  726. BodyPartHeatProperties(InventorySlots.GLOVES, GameConstants.ENVIRO_HEATCOMFORT_GLOVES_WEIGHT, hcBodyPart, hBodyPart);
  727. hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
  728. BodyPartHeatProperties(InventorySlots.LEGS, GameConstants.ENVIRO_HEATCOMFORT_LEGS_WEIGHT, hcBodyPart, hBodyPart);
  729. hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
  730. BodyPartHeatProperties(InventorySlots.FEET, GameConstants.ENVIRO_HEATCOMFORT_FEET_WEIGHT, hcBodyPart, hBodyPart);
  731. hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
  732. BodyPartHeatProperties(InventorySlots.HIPS, GameConstants.ENVIRO_HEATCOMFORT_HIPS_WEIGHT, hcBodyPart, hBodyPart);
  733. hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
  734. hcPenaltyTotal += NakedBodyPartHeatComfortPenalty(InventorySlots.HEADGEAR, GameConstants.ENVIRO_HEATCOMFORT_HEADGEAR_WEIGHT);
  735. hcPenaltyTotal += NakedBodyPartHeatComfortPenalty(InventorySlots.MASK, GameConstants.ENVIRO_HEATCOMFORT_MASK_WEIGHT);
  736. hcPenaltyTotal += NakedBodyPartHeatComfortPenalty(InventorySlots.BODY, GameConstants.ENVIRO_HEATCOMFORT_BODY_WEIGHT);
  737. hcPenaltyTotal += NakedBodyPartHeatComfortPenalty(InventorySlots.GLOVES, GameConstants.ENVIRO_HEATCOMFORT_GLOVES_WEIGHT);
  738. hcPenaltyTotal += NakedBodyPartHeatComfortPenalty(InventorySlots.LEGS, GameConstants.ENVIRO_HEATCOMFORT_LEGS_WEIGHT);
  739. hcPenaltyTotal += NakedBodyPartHeatComfortPenalty(InventorySlots.FEET, GameConstants.ENVIRO_HEATCOMFORT_FEET_WEIGHT);
  740. heatItems = hBodyPartTotal;
  741. heatComfortSum = hcBodyPartTotal;
  742. heatComfortSum += hcPenaltyTotal; //! heatcomfort body parts penalties
  743. //! Stomach temperature influence to heatcomfort
  744. {
  745. if (m_Player.GetStomach().GetStomachVolume() > 0.0)
  746. {
  747. float stomachContentTemperature = m_Player.GetStomach().GetStomachTemperature();
  748. if (stomachContentTemperature < GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT)
  749. {
  750. stomachContentTemperature = Math.Remap(
  751. -10.0,
  752. GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT,
  753. -GameConstants.ENVIRO_STOMACH_WEIGHT,
  754. 0.0,
  755. stomachContentTemperature,
  756. );
  757. }
  758. else if (stomachContentTemperature > GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
  759. {
  760. stomachContentTemperature = Math.Remap(
  761. GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT,
  762. 70.0,
  763. 0.0,
  764. GameConstants.ENVIRO_STOMACH_WEIGHT,
  765. stomachContentTemperature,
  766. );
  767. }
  768. else
  769. stomachContentTemperature = 0.0;
  770. heatComfortSum += stomachContentTemperature * GameConstants.ENVIRO_STOMACH_WEIGHT;
  771. }
  772. }
  773. float targetHeatComfort = (heatComfortSum + heatItems + (GetPlayerHeat() / 100)) + EnvTempToCoef(m_EnvironmentTemperature);
  774. //! uses the raw targetHeatComfort data
  775. m_EnvironmentSnapshot.m_ClothingHeatComfort = hcBodyPartTotal;
  776. m_EnvironmentSnapshot.m_TargetHeatComfort = targetHeatComfort;
  777. ProcessHeatBuffer(m_EnvironmentSnapshot);
  778. if (m_Player.GetModifiersManager().IsModifierActive(eModifiers.MDF_HEATBUFFER))
  779. targetHeatComfort = Math.Clamp(targetHeatComfort, 0.0, m_Player.GetStatHeatComfort().GetMax());
  780. else
  781. targetHeatComfort = Math.Clamp(targetHeatComfort, m_Player.GetStatHeatComfort().GetMin(), m_Player.GetStatHeatComfort().GetMax());
  782. targetHeatComfort = Math.Round(targetHeatComfort * 100) * 0.01;
  783. float dynamicHeatComfort;
  784. {
  785. float direction = 1.0;
  786. if (targetHeatComfort < 0.0)
  787. direction = -1.0;
  788. if (Math.AbsFloat(targetHeatComfort - m_HeatComfort) <= GameConstants.ENVIRO_HEATCOMFORT_MAX_STEP_SIZE)
  789. dynamicHeatComfort = m_AverageHeatComfortBuffer.Add(targetHeatComfort);
  790. else
  791. dynamicHeatComfort = m_AverageHeatComfortBuffer.Add((Math.AbsFloat(targetHeatComfort) - GameConstants.ENVIRO_HEATCOMFORT_MAX_STEP_SIZE) * direction);
  792. }
  793. dynamicHeatComfort = Math.Round(dynamicHeatComfort * 100) * 0.01;
  794. m_HeatComfort = dynamicHeatComfort;
  795. SetTargetHeatComfort(targetHeatComfort);
  796. m_Player.GetStatHeatComfort().Set(m_HeatComfort);
  797. }
  798. protected void SetTargetHeatComfort(float value)
  799. {
  800. m_TargetHeatComfort = value;
  801. }
  802. protected void SetHeatcomfortDirectly()
  803. {
  804. if (m_HeatComfortBehaviorCategory == EEnvironmentHeatcomfortBehaviorCategory.CAR_ENGINE_ON)
  805. {
  806. float targetHeatComfort = 0.0;
  807. float dynamicHeatComfort = m_AverageHeatComfortBuffer.Add(targetHeatComfort);
  808. m_HeatComfort = dynamicHeatComfort;
  809. SetTargetHeatComfort(0.0);
  810. m_Player.GetStatHeatComfort().Set(dynamicHeatComfort);
  811. }
  812. }
  813. protected void ProcessHeatBuffer(EnvironmentSnapshotData data)
  814. {
  815. if (m_HeatComfortBehaviorCategory == EEnvironmentHeatcomfortBehaviorCategory.DEFAULT)
  816. {
  817. float applicableHeatbuffer = GetApplicableHeatbuffer();
  818. //! dynamic HB cap based on actual heatcomfort (from cloths)
  819. float heatBufferCap = Math.InverseLerp(0.0, GameConstants.ENVIRO_HEATCOMFORT_WEIGHT_SUMMARY, data.m_ClothingHeatComfort);
  820. float heatBufferMax = GameConstants.ENVIRO_PLAYER_HEATBUFFER_CAPACITY_MIN + heatBufferCap * (1 - GameConstants.ENVIRO_PLAYER_HEATBUFFER_CAPACITY_MIN);
  821. m_Player.SetHeatBufferDynamicMax(heatBufferMax);
  822. //! deplete the heat buffer if there is difference in HB capacity (eg.: cloths were removed)
  823. if (heatBufferCap < m_HeatBufferCapPrevious)
  824. {
  825. float heatBufferValueCorrection = GameConstants.ENVIRO_PLAYER_HEATBUFFER_INCREASE / (heatBufferMax * ((-GameConstants.ENVIRO_PLAYER_HEATBUFFER_TEMP_AFFECT * data.m_TargetHeatComfort) + 1 ));
  826. m_Player.GetStatHeatBuffer().Add(-heatBufferValueCorrection);
  827. m_HeatBufferCapPrevious = heatBufferCap;
  828. }
  829. float increaseRate = 0.0;
  830. float decreaseRate = 0.0;
  831. {
  832. increaseRate = GameConstants.ENVIRO_PLAYER_HEATBUFFER_INCREASE / (heatBufferMax * (( -GameConstants.ENVIRO_PLAYER_HEATBUFFER_TEMP_AFFECT * data.m_TargetHeatComfort) + 1 ));
  833. decreaseRate = GameConstants.ENVIRO_PLAYER_HEATBUFFER_DECREASE / (heatBufferMax * (( GameConstants.ENVIRO_PLAYER_HEATBUFFER_TEMP_AFFECT * data.m_TargetHeatComfort) + 1 ));
  834. float decreaseRateByHeatBufferStageCoef = 1;
  835. if (heatBufferMax > HeatBufferMdfr.STAGE_THRESHOLDS[1])
  836. {
  837. float heatBufferMaxInversed = Math.InverseLerp(HeatBufferMdfr.STAGE_THRESHOLDS[1], 1.0, heatBufferMax);
  838. switch (m_Player.GetHeatBufferStage())
  839. {
  840. case 2:
  841. decreaseRateByHeatBufferStageCoef = Math.Lerp(
  842. GameConstants.ENVIRO_PLAYER_HEATBUFFER_STAGE_RATELIMIT[2][0],
  843. GameConstants.ENVIRO_PLAYER_HEATBUFFER_STAGE_RATELIMIT[2][1],
  844. heatBufferMaxInversed,
  845. );
  846. break;
  847. case 1:
  848. decreaseRateByHeatBufferStageCoef = Math.Lerp(
  849. GameConstants.ENVIRO_PLAYER_HEATBUFFER_STAGE_RATELIMIT[1][0],
  850. GameConstants.ENVIRO_PLAYER_HEATBUFFER_STAGE_RATELIMIT[1][1],
  851. heatBufferMaxInversed,
  852. );
  853. break;
  854. }
  855. }
  856. else
  857. {
  858. decreaseRateByHeatBufferStageCoef = GameConstants.ENVIRO_PLAYER_HEATBUFFER_STAGE_RATELIMIT[1][0];
  859. }
  860. decreaseRate *= decreaseRateByHeatBufferStageCoef;
  861. if (m_IsInWater)
  862. decreaseRate *= GameConstants.ENVIRO_PLAYER_HEATBUFFER_WATEREFFECT * m_WaterLevel;
  863. }
  864. if (!m_HasTemperatureSources)
  865. {
  866. if (m_Player.GetModifiersManager().IsModifierActive(eModifiers.MDF_HEATBUFFER))
  867. {
  868. if (m_HeatBufferTimer >= 1.0)
  869. m_Player.GetStatHeatBuffer().Add(-decreaseRate);
  870. else
  871. m_HeatBufferTimer = 1.0;
  872. }
  873. else
  874. {
  875. m_HeatBufferTimer = 0.0;
  876. if (applicableHeatbuffer > 0.0)
  877. m_Player.GetStatHeatBuffer().Add(-decreaseRate);
  878. else if (applicableHeatbuffer != 0.0 && !m_Player.GetModifiersManager().IsModifierActive(eModifiers.MDF_HEATBUFFER))
  879. m_Player.GetStatHeatBuffer().Set(0.0);
  880. }
  881. }
  882. else
  883. {
  884. if (m_HeatComfort > PlayerConstants.THRESHOLD_HEAT_COMFORT_MINUS_WARNING && m_UTSAverageTemperature > 0) // m_UTSAverageTemperature can be negative
  885. {
  886. if (applicableHeatbuffer < heatBufferMax)
  887. {
  888. m_Player.GetStatHeatBuffer().Add(increaseRate);
  889. m_HeatBufferCapPrevious = heatBufferCap;
  890. }
  891. }
  892. else if (applicableHeatbuffer > 0.0)
  893. m_Player.GetStatHeatBuffer().Add(-decreaseRate);
  894. else if (applicableHeatbuffer != 0.0 && !m_Player.GetModifiersManager().IsModifierActive(eModifiers.MDF_HEATBUFFER))
  895. m_Player.GetStatHeatBuffer().Set(0.0);
  896. m_HeatBufferTimer = 0.0;
  897. }
  898. }
  899. }
  900. protected float GetApplicableHeatbuffer()
  901. {
  902. float applicableHeatbuffer = Math.Round((m_Player.GetStatHeatBuffer().Get() / m_Player.GetStatHeatBuffer().GetMax()) * 1000) * 0.001;
  903. return applicableHeatbuffer;
  904. }
  905. //! go through all items in player's possession cool/warm them to neutral temperature
  906. protected void ProcessItemsTemperature(array<int> pBodyPartIds)
  907. {
  908. EntityAI attachment;
  909. ItemBase item;
  910. int attCount = m_Player.GetInventory().AttachmentCount();
  911. for (int attIdx = 0; attIdx < attCount; ++attIdx)
  912. {
  913. attachment = m_Player.GetInventory().GetAttachmentFromIndex(attIdx);
  914. item = ItemBase.Cast(attachment);
  915. int attachmentSlot = attachment.GetInventory().GetSlotId(0);
  916. //! go through all body parts we've defined for that zone (ex.: head, body, feet)
  917. for (int i = 0; i < pBodyPartIds.Count(); ++i)
  918. {
  919. if (attachmentSlot == pBodyPartIds[i])
  920. {
  921. float heatPermCoef = item.GetHeatPermeabilityCoef();
  922. //first handle the item itself, if necessary
  923. if (item.CanHaveTemperature() && !item.IsSelfAdjustingTemperature())
  924. SetProcessedItemTemperature(item,heatPermCoef);
  925. ProcessItemHierarchyRecursive(item,heatPermCoef);
  926. }
  927. }
  928. }
  929. }
  930. protected void ProcessItemsInHandsTemperature()
  931. {
  932. ItemBase item = m_Player.GetItemInHands();
  933. if (item)
  934. {
  935. float heatPermCoef = item.GetHeatPermeabilityCoef();
  936. //first handle the item itself, if necessary
  937. if (item.CanHaveTemperature() && !item.IsSelfAdjustingTemperature())
  938. SetProcessedItemTemperature(item,heatPermCoef);
  939. ProcessItemHierarchyRecursive(item,heatPermCoef);
  940. }
  941. }
  942. protected void ProcessItemHierarchyRecursive(ItemBase item, float heatPermeabilityCoef = 1.0)
  943. {
  944. float heatPermCoef = heatPermeabilityCoef;
  945. // go through any attachments and cargo, recursive
  946. int inventoryAttCount = item.GetInventory().AttachmentCount();
  947. if (inventoryAttCount > 0)
  948. {
  949. ItemBase attachmentItem;
  950. for (int inAttIdx = 0; inAttIdx < inventoryAttCount; ++inAttIdx)
  951. {
  952. if (Class.CastTo(attachmentItem,item.GetInventory().GetAttachmentFromIndex(inAttIdx)))
  953. {
  954. heatPermCoef = heatPermeabilityCoef;
  955. heatPermCoef *= attachmentItem.GetHeatPermeabilityCoef();
  956. if (attachmentItem.CanHaveTemperature() && !attachmentItem.IsSelfAdjustingTemperature())
  957. {
  958. SetProcessedItemTemperature(attachmentItem,heatPermCoef);
  959. }
  960. ProcessItemHierarchyRecursive(attachmentItem,heatPermCoef);
  961. }
  962. }
  963. }
  964. if (item.GetInventory().GetCargo())
  965. {
  966. int inventoryItemCount = item.GetInventory().GetCargo().GetItemCount();
  967. if (inventoryItemCount > 0)
  968. {
  969. ItemBase inventoryItem;
  970. for (int j = 0; j < inventoryItemCount; ++j)
  971. {
  972. if (Class.CastTo(inventoryItem,item.GetInventory().GetCargo().GetItem(j)))
  973. {
  974. heatPermCoef = heatPermeabilityCoef;
  975. heatPermCoef *= inventoryItem.GetHeatPermeabilityCoef();
  976. if (inventoryItem.CanHaveTemperature() && !inventoryItem.IsSelfAdjustingTemperature())
  977. {
  978. SetProcessedItemTemperature(inventoryItem,heatPermCoef);
  979. }
  980. ProcessItemHierarchyRecursive(inventoryItem,heatPermCoef);
  981. }
  982. }
  983. }
  984. }
  985. }
  986. protected void SetProcessedItemTemperature(ItemBase item, float heatPermeabilityCoef = 1.0)
  987. {
  988. float targetTemperature = GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_MIDDLE;
  989. bool globalCooling = true;
  990. if (m_Player.IsSwimming())
  991. {
  992. SetItemHeatingCoef(GameConstants.TEMP_COEF_SWIMMING);
  993. targetTemperature = m_WorldData.GetLiquidTypeEnviroTemperature(m_LiquidType);
  994. globalCooling = false;
  995. }
  996. if (item.GetTemperature() != targetTemperature || !item.IsFreezeThawProgressFinished())
  997. {
  998. TemperatureDataInterpolated temperatureData = new TemperatureDataInterpolated(
  999. targetTemperature,
  1000. ETemperatureAccessTypes.ACCESS_INVENTORY,
  1001. GameConstants.ENVIRO_TICK_RATE,
  1002. m_ItemTemperatureCoef,
  1003. heatPermeabilityCoef,
  1004. );
  1005. temperatureData.m_UseGlobalCooling = globalCooling;
  1006. item.SetTemperatureEx(temperatureData);
  1007. }
  1008. }
  1009. protected float EnvTempToCoef(float pTemp)
  1010. {
  1011. return (pTemp - GameConstants.ENVIRO_PLAYER_COMFORT_TEMP) / GameConstants.ENVIRO_TEMP_EFFECT_ON_PLAYER;
  1012. }
  1013. //! returns enhanced heat comfort for given body part
  1014. protected void BodyPartHeatProperties(int pBodyPartId, float pCoef, out float pHeatComfort, out float pHeat)
  1015. {
  1016. pHeatComfort = 0;
  1017. pHeat = 0;
  1018. int attCount = m_Player.GetInventory().AttachmentCount();
  1019. for (int attIdx = 0; attIdx < attCount; ++attIdx)
  1020. {
  1021. EntityAI attachment = m_Player.GetInventory().GetAttachmentFromIndex(attIdx);
  1022. if (attachment.IsClothing())
  1023. {
  1024. ItemBase item = ItemBase.Cast(attachment);
  1025. int attachmentSlot = attachment.GetInventory().GetSlotId(0);
  1026. if (attachmentSlot == pBodyPartId)
  1027. {
  1028. LogItemHeat(string.Format("BodyPartHeatProperties (%1)", EnumTools.EnumToString(InventorySlots, pBodyPartId)));
  1029. float itemHeatcomfort = 0;
  1030. float itemTemperature = 0;
  1031. // go through any attachments and cargo (only current level, ignore nested containers - they isolate)
  1032. int inventoryAttCount = item.GetInventory().AttachmentCount();
  1033. if (inventoryAttCount > 0)
  1034. {
  1035. LogItemHeat(string.Format("attachments:"), false);
  1036. for (int inAttIdx = 0; inAttIdx < inventoryAttCount; ++inAttIdx)
  1037. {
  1038. EntityAI inAttachment = item.GetInventory().GetAttachmentFromIndex(inAttIdx);
  1039. ItemBase attachmentItem = ItemBase.Cast(inAttachment);
  1040. if (attachmentItem && attachmentItem.CanHaveTemperature())
  1041. {
  1042. itemTemperature = attachmentItem.GetTemperature();
  1043. if (itemTemperature < GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT || itemTemperature > GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
  1044. {
  1045. itemHeatcomfort = NormalizedTemperature(itemTemperature) * attachmentItem.GetQuantityNormalizedScripted() * attachmentItem.GetTemperaturePerQuantityWeight();
  1046. LogItemHeat(string.Format("%1: temperature=%2 heat=%3", attachmentItem, itemTemperature, pHeat), true);
  1047. pHeat += itemHeatcomfort;
  1048. }
  1049. }
  1050. }
  1051. }
  1052. if (item.GetInventory().GetCargo())
  1053. {
  1054. int inventoryItemCount = item.GetInventory().GetCargo().GetItemCount();
  1055. if (inventoryItemCount > 0)
  1056. {
  1057. LogItemHeat(string.Format("cargo:"), false);
  1058. for (int j = 0; j < inventoryItemCount; ++j)
  1059. {
  1060. ItemBase inventoryItem = ItemBase.Cast(item.GetInventory().GetCargo().GetItem(j));
  1061. if (inventoryItem && inventoryItem.CanHaveTemperature())
  1062. {
  1063. itemTemperature = inventoryItem.GetTemperature();
  1064. if (itemTemperature < GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT || itemTemperature > GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
  1065. {
  1066. itemHeatcomfort = NormalizedTemperature(itemTemperature) * inventoryItem.GetQuantityNormalizedScripted() * inventoryItem.GetTemperaturePerQuantityWeight();
  1067. LogItemHeat(string.Format("%1: temperature=%2 heat=%3", inventoryItem, itemTemperature, itemHeatcomfort), true);
  1068. pHeat += itemHeatcomfort;
  1069. }
  1070. }
  1071. }
  1072. }
  1073. }
  1074. pHeatComfort = MiscGameplayFunctions.GetCurrentItemHeatIsolation(item) * pCoef;
  1075. LogItemHeat(string.Format("overall heat from items=%1 (coef applied)", pHeat));
  1076. LogItemHeat("");
  1077. break;
  1078. }
  1079. }
  1080. }
  1081. }
  1082. protected float NakedBodyPartHeatComfortPenalty(int pBodyPartSlotId, float pCoef)
  1083. {
  1084. float penalty = 0.0;
  1085. if (!IsInsideBuilding() && !IsUnderRoof() && !IsChildOfType({Car}) && !IsWaterContact())
  1086. {
  1087. if (m_Rain > GameConstants.ENVIRO_NAKED_BODY_PENALTY_RAIN_MIN_VALUE || m_Snowfall > GameConstants.ENVIRO_NAKED_BODY_PENALTY_SNOWFALL_MIN_VALUE)
  1088. {
  1089. penalty += GameConstants.ENVIRO_ISOLATION_WETFACTOR_DRENCHED;
  1090. penalty *= pCoef;
  1091. }
  1092. }
  1093. return penalty;
  1094. }
  1095. protected void GatherTemperatureSources()
  1096. {
  1097. m_UTemperatureSources.Clear();
  1098. array<Object> nearestObjects = new array<Object>();
  1099. GetGame().GetObjectsAtPosition(m_Player.GetPosition(), GameConstants.ENVIRO_TEMP_SOURCES_LOOKUP_RADIUS, nearestObjects, null);
  1100. foreach (Object nearestObject : nearestObjects)
  1101. {
  1102. EntityAI ent = EntityAI.Cast(nearestObject);
  1103. if (ent && ent.IsUniversalTemperatureSource() && ent != m_Player)
  1104. {
  1105. //! next temp source is too far
  1106. if (vector.DistanceSq(m_Player.GetPosition(), ent.GetPosition()) > Math.SqrFloat(ent.GetUniversalTemperatureSource().GetMaxRange()))
  1107. continue;
  1108. //! skip - this TS is not affecting player entities
  1109. if (ent.GetUniversalTemperatureSource().GetLambda().AffectsPlayer())
  1110. m_UTemperatureSources.Insert(ent.GetUniversalTemperatureSource());
  1111. }
  1112. }
  1113. if (m_Player.GetItemInHands() && m_Player.GetItemInHands().IsUniversalTemperatureSource())
  1114. m_UTemperatureSources.Insert(m_Player.GetItemInHands().GetUniversalTemperatureSource());
  1115. }
  1116. protected void SetItemHeatingCoef(float val)
  1117. {
  1118. m_ItemTemperatureCoef = val;
  1119. }
  1120. protected void ProcessTemperatureSources()
  1121. {
  1122. int UTScount = m_UTemperatureSources.Count();
  1123. if (UTScount == 0)
  1124. {
  1125. m_HasTemperatureSources = false;
  1126. OnTemperatureSourcesLeft();
  1127. m_UTSAverageTemperatureBuffer.Add(0.0);
  1128. m_UTSAverageTemperature = 0.0;
  1129. SetItemHeatingCoef(GameConstants.TEMP_COEF_INVENTORY);
  1130. return;
  1131. }
  1132. array<float> utsTemperatures = new array<float>();
  1133. // get temperature from the source (based on distance), save it for min/max filtering
  1134. float itemCoefAverage = 0.0;
  1135. foreach (UTemperatureSource tempSource : m_UTemperatureSources)
  1136. {
  1137. utsTemperatures.Insert(CalcTemperatureFromTemperatureSource(tempSource));
  1138. itemCoefAverage += tempSource.GetTemperatureItemCoef();
  1139. }
  1140. itemCoefAverage /= UTScount;
  1141. SetItemHeatingCoef(itemCoefAverage);
  1142. float min = MiscGameplayFunctions.GetMinValue(utsTemperatures);
  1143. float max = MiscGameplayFunctions.GetMaxValue(utsTemperatures);
  1144. if (max > 0 && min < 0)
  1145. {
  1146. //! adds average of 2 most significat sources to buffer
  1147. m_UTSAverageTemperature = m_UTSAverageTemperatureBuffer.Add((max + min) * 0.5);
  1148. }
  1149. else
  1150. {
  1151. m_UTSAverageTemperature = m_UTSAverageTemperatureBuffer.Add(max);
  1152. }
  1153. if (m_HasTemperatureSources == false)
  1154. OnTemperatureSourcesEnter();
  1155. m_HasTemperatureSources = true;
  1156. }
  1157. protected void OnTemperatureSourcesEnter();
  1158. protected void OnTemperatureSourcesLeft();
  1159. float GetUniversalSourcesTemperageAverage()
  1160. {
  1161. return m_UTSAverageTemperature;
  1162. }
  1163. float CalcTemperatureFromTemperatureSource(notnull UTemperatureSource uts)
  1164. {
  1165. float distance = vector.Distance(m_Player.GetPosition(), uts.GetPosition());
  1166. distance = Math.Max(distance, 0.1); //min distance cannot be 0 (division by zero)
  1167. float temperature = 0;
  1168. //! heat transfer through air to player (env temperature)
  1169. if (distance > uts.GetFullRange())
  1170. {
  1171. float distFactor = Math.InverseLerp(uts.GetMaxRange(), uts.GetFullRange(), distance);
  1172. temperature = uts.GetTemperatureCap() * distFactor;
  1173. }
  1174. else
  1175. {
  1176. temperature = uts.GetTemperatureCap();
  1177. }
  1178. return temperature;
  1179. }
  1180. //! debug
  1181. #ifdef DIAG_DEVELOPER
  1182. EnvDebugData GetEnvDebugData()
  1183. {
  1184. EnvDebugData data = new EnvDebugData();
  1185. data.Synch(this, m_Player);
  1186. return data;
  1187. }
  1188. void ShowEnvDebugPlayerInfo(bool enabled)
  1189. {
  1190. EnvDebugData data = GetEnvDebugData();
  1191. DisplayEnvDebugPlayerInfo(enabled, data);
  1192. }
  1193. static void DisplayEnvDebugPlayerInfo(bool enabled, EnvDebugData data)
  1194. {
  1195. int windowPosX = 10;
  1196. int windowPosY = 200;
  1197. Object obj;
  1198. DbgUI.Begin("Player stats", windowPosX, windowPosY);
  1199. if ( enabled )
  1200. {
  1201. DbgUI.Text(string.Format("Heat comfort(target): %1", data.m_PlayerData.m_HeatComfortTarget));
  1202. DbgUI.Text(string.Format("Heat comfort(dynamic): %1", data.m_PlayerData.m_HeatComfortDynamic));
  1203. DbgUI.Text(string.Format("Inside: %1 (%2)", data.m_PlayerData.m_Inside, data.m_PlayerData.m_Surface));
  1204. DbgUI.Text(string.Format("Under roof: %1 (%2)", data.m_PlayerData.m_UnderRoof, data.m_PlayerData.m_UnderRoofTimer));
  1205. if ( data.m_PlayerData.m_WaterLevel > 0 )
  1206. {
  1207. DbgUI.Text(string.Format("Water Level: %1", data.m_PlayerData.m_WaterLevel));
  1208. }
  1209. }
  1210. DbgUI.End();
  1211. DbgUI.Begin("Weather stats:", windowPosX, windowPosY + 200);
  1212. if ( enabled )
  1213. {
  1214. DbgUI.Text(string.Format("Env temperature (base): %1", data.m_MiscData.m_TemperatureBase));
  1215. DbgUI.Text(string.Format("Env temperature (height corrected): %1", data.m_MiscData.m_HeightCorrectedTemperature));
  1216. DbgUI.Text(string.Format("Env temperature (modfied): %1", data.m_MiscData.m_TemperatureModified));
  1217. DbgUI.Text(string.Format("Wind magnitude(surface mult): %1 (x%2)", data.m_WeatherData.m_Wind, data.m_WeatherData.m_WindModifier));
  1218. DbgUI.Text(string.Format("Rain: %1", data.m_WeatherData.m_Rain));
  1219. DbgUI.Text(string.Format("Snow: %1", data.m_WeatherData.m_Snowfall));
  1220. DbgUI.Text(string.Format("Datetime: %1", WorldDataDaytime.ToString(g_Game.GetMission().GetWorldData().GetDaytime())));
  1221. DbgUI.Text(string.Format("Fog: %1", data.m_WeatherData.m_Fog));
  1222. DbgUI.Text(string.Format("Clouds: %1", data.m_WeatherData.m_Clouds));
  1223. DbgUI.Text(string.Format("Wet delta: %1", data.m_MiscData.m_WetDelta));
  1224. }
  1225. DbgUI.End();
  1226. }
  1227. void FillDebugWeatherData(EnvDebugWeatherData data)
  1228. {
  1229. data.m_Wind = m_Wind;
  1230. data.m_WindModifier = GetWindModifierPerSurface();
  1231. data.m_Rain = m_Rain;
  1232. data.m_Snowfall = m_Snowfall;
  1233. data.m_Fog = m_Fog;
  1234. data.m_Clouds = m_Clouds;
  1235. }
  1236. #endif
  1237. string GetDebugMessage()
  1238. {
  1239. string message;
  1240. message += "Player stats";
  1241. message += "\nHeat comfort(target): " + GetTargetHeatComfort().ToString();
  1242. message += "\nHeat comfort(dynamic): " + m_HeatComfort.ToString();
  1243. int liquidType;
  1244. string impact, surfaceType;
  1245. g_Game.SurfaceUnderObjectExCorrectedLiquid(m_Player, surfaceType, impact, liquidType);
  1246. message += "\nInside: " + IsInsideBuilding().ToString();
  1247. message += "\nSurface: " + surfaceType;
  1248. message += "\nLiquid: " + liquidType;
  1249. message += "\nUnder roof: " + m_IsUnderRoof.ToString() + " (" + GetNextRoofCheck() + ")";
  1250. if (IsWaterContact() && m_WaterLevel > WATER_LEVEL_NONE)
  1251. {
  1252. message += "\nWater Level: " + m_WaterLevel;
  1253. }
  1254. message += "\n\nWeather stats";
  1255. message += "\nEnv temperature (base): " + m_WorldData.GetBaseEnvTemperature().ToString();
  1256. message += "\nEnv temperature (height corrected): " + m_WorldData.GetBaseEnvTemperatureAtObject(m_Player);
  1257. message += "\nEnv temperature (modified): " + m_EnvironmentTemperature.ToString();
  1258. message += "\nWind: " + m_Wind.ToString() + " (x" + GetWindModifierPerSurface() + ")";
  1259. message += "\nRain: " + m_Rain.ToString();
  1260. message += "\nSnow: " + m_Snowfall.ToString();
  1261. message += "\nDatetime: " + WorldDataDaytime.ToString(m_DayOrNight);
  1262. message += "\nFog: " + m_Fog.ToString();
  1263. message += "\nClouds: " + m_Clouds.ToString();
  1264. message += "\nWet delta: " + GetWetDelta().ToString();
  1265. return message;
  1266. }
  1267. int GetNextRoofCheck()
  1268. {
  1269. return (GameConstants.ENVIRO_TICK_ROOF_RC_CHECK - m_RoofCheckTimer) + 1;
  1270. }
  1271. float GetWaterLevel()
  1272. {
  1273. if (IsWaterContact() && m_WaterLevel > WATER_LEVEL_NONE)
  1274. {
  1275. return m_WaterLevel;
  1276. }
  1277. return 0.0;
  1278. }
  1279. private bool IsNeutralTemperature(float temperature, float lowerLimit = GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT, float upperLimit = GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
  1280. {
  1281. if (temperature >= lowerLimit && temperature <= upperLimit)
  1282. return true;
  1283. return false;
  1284. }
  1285. private float NormalizedTemperature(float temperature, float lowerLimit = GameConstants.ENVIRO_LOW_TEMP_LIMIT, float upperLimit = GameConstants.ENVIRO_HIGH_TEMP_LIMIT)
  1286. {
  1287. if (temperature >= GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT && temperature <= GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
  1288. return 0.0;
  1289. if (temperature < GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT)
  1290. return Math.Clamp(Math.InverseLerp(lowerLimit, GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT, temperature), -1.0, -0.1);
  1291. if (temperature > GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
  1292. return Math.Clamp(Math.InverseLerp(GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT, upperLimit, temperature), 0.1, 1.0);
  1293. // neutral zone
  1294. return 0.0;
  1295. }
  1296. private void LogDryWetProcess(string message, bool indented = false)
  1297. {
  1298. #ifdef DIAG_DEVELOPER
  1299. if (m_DebugLogDryWet)
  1300. {
  1301. string indentation = "";
  1302. if (indented)
  1303. indentation = "|--";
  1304. Debug.Log(string.Format("%1 %2", indentation, message));
  1305. }
  1306. #endif
  1307. }
  1308. private void LogItemHeat(string message, bool indented = false)
  1309. {
  1310. #ifdef DIAG_DEVELOPER
  1311. if (m_DebugLogItemHeat)
  1312. {
  1313. string indentation = "";
  1314. if (indented)
  1315. indentation = "|--";
  1316. Debug.Log(string.Format("%1 %2", indentation, message));
  1317. }
  1318. #endif
  1319. }
  1320. //! DEPRECATED
  1321. protected float m_HeatSourceTemp;
  1322. protected ref SimpleMovingAverage<float> m_WindAverageBuffer;
  1323. protected ref EnvironmentSnapshotData m_EnvironmentSnapshot; //! used for calculations before the data modification
  1324. void Init(PlayerBase pPlayer)
  1325. {
  1326. Init();
  1327. }
  1328. protected bool OverridenHeatComfort(out float value);
  1329. void AddToEnvironmentTemperature(float pTemperature);
  1330. protected void ProcessItemsHeat()
  1331. {
  1332. // for backward combatibility only
  1333. ProcessHeatComfort();
  1334. }
  1335. protected void ProcessWetnessByRain()
  1336. {
  1337. ProcessItemsWetness(m_SlotIdsComplete);
  1338. }
  1339. // Returns amount of deg C air temperature should be lowered by, based on player's height above water level
  1340. float GetTemperatureHeightCorrection()
  1341. {
  1342. float temperature_reduction = Math.Max(0, (m_PlayerHeightPos * m_WorldData.m_TemperaturePerHeightReductionModifier));
  1343. return temperature_reduction;
  1344. }
  1345. //! returns weighted avg heat comfort for bodypart
  1346. protected void BodyPartHeatProperties(array<int> pBodyPartIds, float pCoef, out float pHeatComfort, out float pHeat)
  1347. {
  1348. pHeatComfort = 0;
  1349. pHeat = 0;
  1350. if (pBodyPartIds.Count() > 0)
  1351. {
  1352. LogItemHeat(string.Format("BodyPartHeatProperties (%1)", EnumTools.EnumToString(InventorySlots, pBodyPartIds[0])));
  1353. int attCount = m_Player.GetInventory().AttachmentCount();
  1354. for (int attIdx = 0; attIdx < attCount; ++attIdx)
  1355. {
  1356. EntityAI attachment = m_Player.GetInventory().GetAttachmentFromIndex(attIdx);
  1357. if (attachment.IsClothing())
  1358. {
  1359. ItemBase item = ItemBase.Cast(attachment);
  1360. int attachmentSlot = attachment.GetInventory().GetSlotId(0);
  1361. //! go through all body parts we've defined for that zone (ex.: head, body, feet)
  1362. for (int i = 0; i < pBodyPartIds.Count(); ++i)
  1363. {
  1364. if (attachmentSlot == pBodyPartIds[i])
  1365. {
  1366. float heatIsoMult = 1.0;
  1367. if (attachmentSlot == InventorySlots.BACK)
  1368. heatIsoMult = GameConstants.ENVIRO_HEATISOLATION_BACK_WEIGHT;
  1369. else if (attachmentSlot == InventorySlots.VEST)
  1370. heatIsoMult = GameConstants.ENVIRO_HEATISOLATION_VEST_WEIGHT;
  1371. pHeatComfort += heatIsoMult * MiscGameplayFunctions.GetCurrentItemHeatIsolation(item);
  1372. float itemHeatcomfort = 0;
  1373. float itemTemperature = 0;
  1374. // go through any attachments and cargo (only current level, ignore nested containers - they isolate)
  1375. int inventoryAttCount = item.GetInventory().AttachmentCount();
  1376. if (inventoryAttCount > 0)
  1377. {
  1378. LogItemHeat(string.Format("attachments:"), false);
  1379. for (int inAttIdx = 0; inAttIdx < inventoryAttCount; ++inAttIdx)
  1380. {
  1381. EntityAI inAttachment = item.GetInventory().GetAttachmentFromIndex(inAttIdx);
  1382. ItemBase attachmentItem = ItemBase.Cast(inAttachment);
  1383. if (attachmentItem && attachmentItem.CanHaveTemperature())
  1384. {
  1385. itemTemperature = attachmentItem.GetTemperature();
  1386. if (itemTemperature < GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT || itemTemperature > GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
  1387. {
  1388. itemHeatcomfort = NormalizedTemperature(itemTemperature) * attachmentItem.GetQuantityNormalizedScripted() * attachmentItem.GetTemperaturePerQuantityWeight();
  1389. LogItemHeat(string.Format("%1: temperature=%2 heat=%3", attachmentItem, itemTemperature, pHeat), true);
  1390. pHeat += itemHeatcomfort;
  1391. }
  1392. }
  1393. }
  1394. }
  1395. if (item.GetInventory().GetCargo())
  1396. {
  1397. int inventoryItemCount = item.GetInventory().GetCargo().GetItemCount();
  1398. if (inventoryItemCount > 0)
  1399. {
  1400. LogItemHeat(string.Format("cargo:"), false);
  1401. for (int j = 0; j < inventoryItemCount; ++j)
  1402. {
  1403. ItemBase inventoryItem = ItemBase.Cast(item.GetInventory().GetCargo().GetItem(j));
  1404. if (inventoryItem && inventoryItem.CanHaveTemperature())
  1405. {
  1406. itemTemperature = inventoryItem.GetTemperature();
  1407. if (itemTemperature < GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT || itemTemperature > GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
  1408. {
  1409. itemHeatcomfort = NormalizedTemperature(itemTemperature) * inventoryItem.GetQuantityNormalizedScripted() * inventoryItem.GetTemperaturePerQuantityWeight();
  1410. LogItemHeat(string.Format("%1: temperature=%2 heat=%3", inventoryItem, itemTemperature, itemHeatcomfort), true);
  1411. pHeat += itemHeatcomfort;
  1412. }
  1413. }
  1414. }
  1415. }
  1416. }
  1417. }
  1418. }
  1419. }
  1420. }
  1421. pHeatComfort += (pHeatComfort / pBodyPartIds.Count()) * pCoef;
  1422. LogItemHeat(string.Format("overall heat from items=%1 (coef applied)", pHeat));
  1423. LogItemHeat("");
  1424. }
  1425. }
  1426. protected void SetEnvironmentSnapshotData()
  1427. {
  1428. EnvironmentSnapshotData data = new EnvironmentSnapshotData();
  1429. data.m_TargetHeatComfort = m_TargetHeatComfort;
  1430. m_EnvironmentSnapshot = data;
  1431. }
  1432. //! backward compatibility [<1.27]
  1433. protected void ProcessHeatBuffer(float heatComfortCloths)
  1434. {
  1435. m_EnvironmentSnapshot.m_ClothingHeatComfort = heatComfortCloths;
  1436. m_EnvironmentSnapshot.m_TargetHeatComfort = m_TargetHeatComfort;
  1437. ProcessHeatBuffer(m_EnvironmentSnapshot);
  1438. }
  1439. //! backward compatibility [<1.28]
  1440. protected float WindEffectTemperatureValue(float temperatureInput)
  1441. {
  1442. float output = 0.0;
  1443. output = (temperatureInput - GameConstants.ENVIRO_WIND_CHILL_LIMIT) / (GameConstants.ENVIRO_WIND_EFFECT_SLOPE - GameConstants.ENVIRO_WIND_CHILL_LIMIT);
  1444. output = output * m_Wind * m_WorldData.GetWindCoef();
  1445. return -output;
  1446. }
  1447. float GetDayOrNight()
  1448. {
  1449. return m_DayOrNight;
  1450. }
  1451. }
  1452. class EnvironmentDrynessData
  1453. {
  1454. bool m_UseTemperatureSources = false;
  1455. float m_TemperatureSourceDistance = 1.0;
  1456. }
  1457. #ifdef DIAG_DEVELOPER
  1458. class EnvDebugPlayerData : Param
  1459. {
  1460. float m_HeatComfortDynamic;
  1461. float m_HeatComfortTarget;
  1462. bool m_Inside;
  1463. string m_Surface;
  1464. bool m_UnderRoof;
  1465. int m_UnderRoofTimer;
  1466. float m_WaterLevel;
  1467. void Synch(Environment env, PlayerBase player)
  1468. {
  1469. m_HeatComfortTarget = env.GetTargetHeatComfort();
  1470. m_HeatComfortDynamic = player.GetStatHeatComfort().Get();
  1471. m_Inside = env.IsInsideBuilding();
  1472. m_Surface = player.GetSurfaceType();
  1473. m_UnderRoof = env.IsUnderRoof();
  1474. m_UnderRoofTimer = env.GetNextRoofCheck();
  1475. m_WaterLevel = env.GetWaterLevel();
  1476. }
  1477. override bool Serialize(Serializer ctx)
  1478. {
  1479. return (
  1480. ctx.Write(m_HeatComfortTarget) && ctx.Write(m_HeatComfortDynamic) && ctx.Write(m_Inside) && ctx.Write(m_Surface) && ctx.Write(m_UnderRoof) && ctx.Write(m_UnderRoofTimer) && ctx.Write(m_WaterLevel));
  1481. }
  1482. override bool Deserializer(Serializer ctx)
  1483. {
  1484. return ctx.Write(m_HeatComfortTarget) && ctx.Read(m_HeatComfortDynamic) && ctx.Read(m_Inside) && ctx.Read(m_Surface) && ctx.Read(m_UnderRoof) && ctx.Read(m_UnderRoofTimer) && ctx.Read(m_WaterLevel);
  1485. }
  1486. }
  1487. class EnvDebugMiscData : Param
  1488. {
  1489. float m_TemperatureBase;
  1490. float m_TemperatureModified;
  1491. float m_HeightCorrectedTemperature;
  1492. float m_WetDelta;
  1493. void Synch(Environment env)
  1494. {
  1495. m_TemperatureBase = g_Game.GetMission().GetWorldData().GetBaseEnvTemperature();
  1496. m_TemperatureModified = env.GetTemperature();
  1497. m_HeightCorrectedTemperature = m_TemperatureBase - env.GetTemperatureHeightCorrection();
  1498. m_WetDelta = env.GetWetDelta();
  1499. }
  1500. override bool Serialize(Serializer ctx)
  1501. {
  1502. return ctx.Write(m_TemperatureBase) && ctx.Write(m_TemperatureModified) && ctx.Write(m_HeightCorrectedTemperature) && ctx.Write(m_WetDelta);
  1503. }
  1504. override bool Deserializer(Serializer ctx)
  1505. {
  1506. return ctx.Read(m_TemperatureBase) && ctx.Read(m_TemperatureModified) && ctx.Read(m_HeightCorrectedTemperature) && ctx.Read(m_WetDelta);
  1507. }
  1508. }
  1509. class EnvDebugWeatherData : Param
  1510. {
  1511. float m_Wind;
  1512. float m_WindModifier;
  1513. float m_Rain;
  1514. float m_Snowfall;
  1515. float m_Fog;
  1516. float m_Clouds;
  1517. void Synch(Environment env)
  1518. {
  1519. env.FillDebugWeatherData(this);
  1520. }
  1521. override bool Serialize(Serializer ctx)
  1522. {
  1523. return ctx.Write(m_Wind) && ctx.Write(m_WindModifier) && ctx.Write(m_Rain) && ctx.Write(m_Snowfall) && ctx.Write(m_Fog) && ctx.Write(m_Clouds);
  1524. }
  1525. override bool Deserializer(Serializer ctx)
  1526. {
  1527. return ctx.Read(m_Wind) && ctx.Read(m_WindModifier) && ctx.Read(m_Rain) && ctx.Read(m_Snowfall) && ctx.Read(m_Fog) && ctx.Read(m_Clouds);
  1528. }
  1529. }
  1530. class EnvDebugData : Param
  1531. {
  1532. ref EnvDebugPlayerData m_PlayerData = new EnvDebugPlayerData();
  1533. ref EnvDebugMiscData m_MiscData = new EnvDebugMiscData();
  1534. ref EnvDebugWeatherData m_WeatherData = new EnvDebugWeatherData();
  1535. void Synch(Environment env, PlayerBase player)
  1536. {
  1537. m_PlayerData.Synch(env, player);
  1538. m_MiscData.Synch(env);
  1539. m_WeatherData.Synch(env);
  1540. }
  1541. override bool Serialize(Serializer ctx)
  1542. {
  1543. return m_PlayerData.Serialize(ctx) && m_MiscData.Serialize(ctx) && m_WeatherData.Serialize(ctx);
  1544. }
  1545. override bool Deserializer(Serializer ctx)
  1546. {
  1547. return m_PlayerData.Deserializer(ctx) && m_MiscData.Deserializer(ctx) && m_WeatherData.Deserializer(ctx);
  1548. }
  1549. }
  1550. #endif