environment.c 66 KB

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