Environment.c
class EnvironmentSnapshotDataclass EnvironmentDrynessDataclass Environmentclass EnvDebugPlayerDataclass EnvDebugMiscDataclass EnvDebugWeatherDataclass EnvDebugDataenum EEnvironmentHeatcomfortBehaviorCategory
/**
* \brief Categories that are changing behavior of Heat comfort processing
*/
enum EEnvironmentHeatcomfortBehaviorCategory
{
DEFAULT,
CAR_ENGINE_ON
}
class EnvironmentSnapshotData
{
float m_ClothingHeatComfort;
float m_TargetHeatComfort;
}
class EnvironmentDrynessData
{
bool m_UseTemperatureSources = false;
float m_TemperatureSourceDistance = 1.0;
}
/**
* \brief Simulates influence of environment to character
* Takes input data from WorldData, Weather system and entities simulating temperature and wetness.
*/
class Environment
{
const float RAIN_LIMIT_LOW = 0.05;
protected const float SNOWFALL_LIMIT_LOW = 0.5;
protected const float SNOWFALL_WIND_COMBINED_THRESHOLD = 1.3;
const float WATER_LEVEL_HIGH = 1.5;
const float WATER_LEVEL_MID = 1.2;
const float WATER_LEVEL_LOW = 0.5;
const float WATER_LEVEL_NONE = 0.15;
protected float m_WetDryTick; //ticks passed since last clothing wetting or drying
protected float m_ItemsWetnessMax; //! keeps wetness of most wet item in player's possesion
protected float m_RoofCheckTimer; // keeps info about tick time
//player
protected PlayerBase m_Player;
protected float m_PlayerHeightPos; // y position of player above water level (meters)
protected float m_PlayerSpeed; // 1-3 speed of player movement
protected float m_PlayerTemperature; //34-44
protected float m_PlayerHeat; //3-9 heatcomfort generated by entites movement
protected float m_HeatComfort; //! player's heatcomfort (buffered, stored in player stats)
protected float m_TargetHeatComfort; //! target value of heatcomfort (non-buffered)
//environment
protected float m_Rain = 0; // 0-1 amount of rain
protected float m_Snowfall = 0; // 0-1 amount of rain
protected float m_Wind = 0; // strength of wind
protected float m_Fog = 0; // 0-1 how foggy it is
protected float m_DayOrNight = 0; // 0-1 day(0) or night(1)
protected float m_Clouds = 0; // 0-1 how cloudy it is
protected float m_EnvironmentTemperature; //temperature of environment player is in
protected float m_Time = 0;
protected string m_SurfaceType;
protected int m_LiquidType;
//item temperatures
protected float m_ItemTemperatureCoef;
//
protected float m_WaterLevel;
protected bool m_IsUnderRoof;
private bool m_IsUnderRoofBuilding;
protected bool m_IsInWater;
protected bool m_IsTempSet;
//
protected float m_HeatBufferTimer; //! reused as state toggle
protected float m_HeatBufferCapPrevious;
protected ref array<int> m_SlotIdsComplete;
protected ref array<int> m_SlotIdsUpper;
protected ref array<int> m_SlotIdsBottom;
protected ref array<int> m_SlotIdsLower;
protected ref array<int> m_HeadParts;
protected ref array<int> m_BodyParts;
protected ref array<int> m_FeetParts;
protected WorldData m_WorldData;
protected bool m_HasTemperatureSources;
protected float m_UTSAverageTemperature;
protected ref array<UTemperatureSource> m_UTemperatureSources;
protected ref SimpleMovingAverage<float> m_UTSAverageTemperatureBuffer;
protected ref SimpleMovingAverage<float> m_AverageHeatComfortBuffer;
protected int m_HeatComfortBehaviorCategory;
private bool m_Initialized;
#ifdef DIAG_DEVELOPER
bool m_Debug = false;
#endif
#ifdef ENABLE_LOGGING
bool m_DebugLogDryWet = false;
bool m_DebugLogItemHeat = false;
#endif
void Environment(PlayerBase pPlayer)
{
m_Player = pPlayer;
}
void Init()
{
m_PlayerSpeed = 0.0;
m_WetDryTick = 0.0;
m_RoofCheckTimer = 0.0;
m_WaterLevel = 0.0;
m_HeatComfort = m_Player.GetStatHeatComfort().Get();
m_WorldData = g_Game.GetMission().GetWorldData();
m_EnvironmentTemperature = m_WorldData.GetBaseEnvTemperatureAtObject(m_Player);
m_IsUnderRoof = false;
m_IsInWater = false;
m_SurfaceType = "cp_dirt";
m_HeatBufferTimer = 0.0;
m_UTSAverageTemperature = 0.0;
m_UTemperatureSources = new array<UTemperatureSource>();
m_UTSAverageTemperatureBuffer = new SimpleMovingAverage<float>(10, 0.0);
m_AverageHeatComfortBuffer = new SimpleMovingAverage<float>(20, m_HeatComfort);
//! whole body slots
m_SlotIdsComplete = new array<int>();
m_SlotIdsComplete = {
InventorySlots.HEADGEAR,
InventorySlots.MASK,
InventorySlots.EYEWEAR,
InventorySlots.GLOVES,
InventorySlots.ARMBAND,
InventorySlots.BODY,
InventorySlots.HIPS,
InventorySlots.VEST,
InventorySlots.BACK,
InventorySlots.LEGS,
InventorySlots.FEET
};
//! upper body part slots
m_SlotIdsUpper = new array<int>();
m_SlotIdsUpper = {
InventorySlots.GLOVES,
InventorySlots.ARMBAND,
InventorySlots.BODY,
InventorySlots.HIPS,
InventorySlots.VEST,
InventorySlots.BACK,
InventorySlots.LEGS,
InventorySlots.FEET
};
//! bottom body part slots
m_SlotIdsBottom = new array<int>();
m_SlotIdsBottom = {
InventorySlots.HIPS,
InventorySlots.LEGS,
InventorySlots.FEET
};
//! lower body part slots
m_SlotIdsLower = new array<int>();
m_SlotIdsLower = {
InventorySlots.FEET,
};
//! --------------------------
//! heat comfort related slots
m_HeadParts = new array<int>();
m_HeadParts = {
InventorySlots.HEADGEAR,
InventorySlots.MASK,
};
m_BodyParts = new array<int>();
m_BodyParts = {
InventorySlots.GLOVES,
InventorySlots.HIPS,
InventorySlots.BODY,
InventorySlots.BACK,
InventorySlots.VEST,
InventorySlots.MELEE,
InventorySlots.SHOULDER
};
m_FeetParts = new array<int>();
m_FeetParts = {
InventorySlots.LEGS,
InventorySlots.FEET,
};
m_HeatComfortBehaviorCategory = EEnvironmentHeatcomfortBehaviorCategory.DEFAULT;
m_EnvironmentSnapshot = new EnvironmentSnapshotData();
m_Initialized = true;
}
// --------------------------------------------------------------------------------
/**
* \brief Main loop that runs calculations for various Environment parts (wetness, heatcomfort, watercontact, etc.)
*/
void Update(float pDelta)
{
if (m_Player && m_Initialized)
{
m_RoofCheckTimer += pDelta;
//! check if player is under roof (only if the Building check is false)
if ( m_RoofCheckTimer >= GameConstants.ENVIRO_TICK_ROOF_RC_CHECK )
{
if ( !IsInsideBuilding() )
CheckUnderRoof();
m_RoofCheckTimer = 0;
}
m_Time += pDelta;
if (m_Time >= GameConstants.ENVIRO_TICK_RATE)
{
m_Time = 0;
m_WetDryTick++; // Sets whether it is time to add wetness to items and clothing
//! Updates data
CheckWaterContact(m_WaterLevel);
CollectAndSetPlayerData();
CollectAndSetEnvironmentData();
GatherTemperatureSources();
ProcessTemperatureSources();
//! Process temperatures
ProcessItemsTemperature(m_HeadParts);
ProcessItemsTemperature(m_BodyParts);
ProcessItemsTemperature(m_FeetParts);
ProcessItemsInHandsTemperature();
//! heat comfort calculation
if (DetermineHeatcomfortBehavior())
SetHeatcomfortDirectly();
else
ProcessHeatComfort();
//! Process item wetness/dryness
if (m_WetDryTick >= GameConstants.ENVIRO_TICKS_TO_WETNESS_CALCULATION)
{
if (IsWaterContact())
{
ProcessWetnessByWaterLevel(m_WaterLevel);
}
else if ((IsRaining() || (IsSnowing() && MiscGameplayFunctions.GetCombinedSnowfallWindValue() > SNOWFALL_WIND_COMBINED_THRESHOLD)) && !IsInsideBuilding() && !IsUnderRoof() && !IsChildOfType({Car}))
{
ProcessItemsWetness(m_SlotIdsComplete);
}
else
{
ProcessItemsDryness();
}
//! setting of wetness/dryiness of player
if ((m_ItemsWetnessMax < GameConstants.STATE_WET) && (m_Player.GetStatWet().Get() == 1))
{
m_Player.GetStatWet().Set(0);
}
else if ((m_ItemsWetnessMax >= GameConstants.STATE_WET) && (m_Player.GetStatWet().Get() == 0))
{
m_Player.GetStatWet().Set(1);
}
m_WetDryTick = 0;
m_ItemsWetnessMax = 0; //! reset item wetness counter;
}
}
}
}
// --------------------------------------------------------------------------------
/**
* \brief For safe-guards, waiting for the proper temperature to be set
*/
bool IsTemperatureSet()
{
return m_IsTempSet;
}
/**
* \brief Character's heat (calculated from movement speed multiplied by constant)
* \return generated heat value
*/
protected float GetPlayerHeat()
{
float heat = m_PlayerSpeed * GameConstants.ENVIRO_DEFAULT_ENTITY_HEAT;
return heat;
}
/**
* \brief Is character under roof (periodically checked - GameConstants.ENVIRO_TICK_ROOF_RC_CHECK).
* Runs when player is not inside of building
*/
bool IsUnderRoof()
{
return m_IsUnderRoof;
}
/**
* \brief Is character inside building? (periodically checked - GameConstants.ENVIRO_TICK_ROOF_RC_CHECK).
*/
bool IsInsideBuilding()
{
return m_Player.IsSoundInsideBuilding();
}
/**
* \brief Is character in contact with water body? (periodically checked - GameConstants.ENVIRO_TICK_RATE).
*/
protected bool IsWaterContact()
{
return m_IsInWater;
}
/**
* \brief Returns true if character is child of given parent type(s)
* @param typenames list of types to check against
*/
private bool IsChildOfType(array<typename> typenames)
{
Object parent = Object.Cast(m_Player.GetParent());
if (parent)
return parent.IsAnyInherited(typenames);
return false;
}
/**
* \brief Is character under building's roof (periodically checked - GameConstants.ENVIRO_TICK_ROOF_RC_CHECK).
* There might be situations where the surface under is external, but character is inside building
*/
private bool IsUnderRoofBuilding()
{
return m_IsUnderRoofBuilding;
}
/**
* \brief Rain phenomenon actual value > RAIN_LIMIT_LOW
*/
protected bool IsRaining()
{
return m_Rain > RAIN_LIMIT_LOW;
}
/**
* \brief Snowfall phenomenon actual value > SNOWFALL_LIMIT_LOW
*/
protected bool IsSnowing()
{
return m_Snowfall > SNOWFALL_LIMIT_LOW;
}
/**
* \brief Changes Heat Comfort curve behavior based on where the character is
*
* \return true if the behavior is altered otherwise false
*/
protected bool DetermineHeatcomfortBehavior()
{
if (IsChildOfType({Car}))
{
CarScript car = CarScript.Cast(m_Player.GetParent());
if (car && car.EngineIsOn())
{
m_HeatComfortBehaviorCategory = EEnvironmentHeatcomfortBehaviorCategory.CAR_ENGINE_ON;
return true;
}
}
m_HeatComfortBehaviorCategory = EEnvironmentHeatcomfortBehaviorCategory.DEFAULT;
return false;
}
// --------------------------------------------------------------------------------
/**
* \brief Checks whether character is sheltered and sets the information
*/
protected void CheckUnderRoof()
{
//! if inside vehicle return immediatelly
if (IsChildOfType({Car}))
{
m_IsUnderRoof = false;
m_IsUnderRoofBuilding = false;
return;
}
float hitFraction;
vector hitPosition, hitNormal;
vector from = m_Player.GetPosition();
vector to = from + "0 25 0";
Object hitObject;
PhxInteractionLayers collisionLayerMask = PhxInteractionLayers.ITEM_LARGE|PhxInteractionLayers.BUILDING|PhxInteractionLayers.VEHICLE;
m_IsUnderRoof = DayZPhysics.RayCastBullet(from, to, collisionLayerMask, null, hitObject, hitPosition, hitNormal, hitFraction);
m_IsUnderRoofBuilding = hitObject && hitObject.IsInherited(House);
}
// --------------------------------------------------------------------------------
/**
* \brief Checks player's contanct with water
* @param[out] pWaterLevel water level height
*
* \return Nothing
*/
protected void CheckWaterContact(out float pWaterLevel)
{
string surfType;
int liquidType;
m_IsInWater = false;
if (m_Player.PhysicsGetLinkedEntity() || IsChildOfType({Transport}))
{
m_Player.SetInWater(m_IsInWater);
return;
}
if (m_Player.IsSwimming())
{
g_Game.SurfaceUnderObjectByBoneCorrectedLiquid(m_Player, SurfaceAnimationBone.RightFrontLimb, surfType, liquidType);
m_SurfaceType = surfType;
m_LiquidType = liquidType;
m_IsInWater = true;
m_Player.SetInWater(m_IsInWater);
HumanMovementState hms = new HumanMovementState();
m_Player.GetMovementState(hms);
pWaterLevel = WATER_LEVEL_MID;
if (hms.m_iMovement >= DayZPlayerConstants.MOVEMENTIDX_WALK)
pWaterLevel = WATER_LEVEL_HIGH;
return;
}
if (m_Player.IsClimbingLadder())
{
pWaterLevel = 0.0;
m_IsInWater = false;
return;
}
//! no valid surface under character
if (IsUnderRoofBuilding())
{
m_IsInWater = false;
return;
}
string impact;
g_Game.SurfaceUnderObjectExCorrectedLiquid(m_Player, surfType, impact, liquidType);
switch (liquidType)
{
case LIQUID_SALTWATER:
case LIQUID_WATER:
case LIQUID_RIVERWATER:
case LIQUID_FRESHWATER:
case LIQUID_STILLWATER:
case LIQUID_HOTWATER:
pWaterLevel = Math.Max(m_Player.GetCurrentWaterLevel(), 0.0);
m_IsInWater = true;
break;
}
//! sync info about water contact to player
m_Player.SetInWater(m_IsInWater);
//! update active surface
m_SurfaceType = surfType;
m_LiquidType = liquidType;
}
// --------------------------------------------------------------------------------
/**
* \brief Wind intensity (influence) modifier of temperature value
*
* \return float modifier used in temperature calculations
*/
float GetWindModifierPerSurface()
{
if (IsUnderRoofBuilding())
return 0.0;
return g_Game.ConfigGetFloat("CfgSurfaces " + m_SurfaceType + " windModifier");
}
float GetTemperature()
{
return m_EnvironmentTemperature;
}
float GetTargetHeatComfort()
{
return m_TargetHeatComfort;
}
// --------------------------------------------------------------------------------
/**
* \brief Calculations of temperarute for different situations
* - in water
* - inside building / inside vehicle / under roof
* - influence of UniversalTemperatureSource
*
* \return Resulting temperature of the environment
*/
protected float GetEnvironmentTemperature()
{
float temperature = m_WorldData.GetTemperature(m_Player, EEnvironmentTemperatureComponent.ALTITUDE | EEnvironmentTemperatureComponent.OVERCAST);
if (IsWaterContact())
{
float waterBodyTemperature = m_WorldData.GetLiquidTypeEnviroTemperature(m_LiquidType);
temperature = waterBodyTemperature - m_WorldData.m_WaterContactTemperatureModifier;
return temperature;
}
if (IsInsideBuilding() || m_IsUnderRoofBuilding)
{
temperature += m_WorldData.m_TemperatureInsideBuildingsModifier;
}
else if (IsChildOfType({Car}))
{
temperature += Math.AbsFloat(temperature * GameConstants.ENVIRO_TEMPERATURE_INSIDE_VEHICLE_COEF);
return temperature;
}
else if (IsUnderRoof() && !m_IsUnderRoofBuilding)
{
temperature = m_WorldData.GetTemperature(m_Player, EEnvironmentTemperatureComponent.ALTITUDE | EEnvironmentTemperatureComponent.OVERCAST|EEnvironmentTemperatureComponent.FOG);
temperature += WindEffectTemperatureValue(temperature) * GetWindModifierPerSurface() * GameConstants.ENVIRO_TEMPERATURE_UNDERROOF_COEF;
}
else
{
temperature = m_WorldData.GetTemperature(m_Player, EEnvironmentTemperatureComponent.ALTITUDE | EEnvironmentTemperatureComponent.OVERCAST|EEnvironmentTemperatureComponent.FOG);
temperature += m_WorldData.GetTemperatureComponentValue(temperature, EEnvironmentTemperatureComponent.WIND) * GetWindModifierPerSurface();
}
float temperaturesDiff = temperature - m_UTSAverageTemperature;
if (temperaturesDiff * (temperaturesDiff - temperature) > 0.0)
temperature = m_UTSAverageTemperature;
return temperature;
}
// --------------------------------------------------------------------------------
/**
* \brief Calculates soaking/drying delta based on character's location and weather
*
* \return Resulting wet delta modifier
*/
float GetWetDelta()
{
float wetDelta = 0;
if ( IsWaterContact() )
{
//! player is getting wet by movement/swimming in water (+differentiate wetDelta by water level)
if (m_WaterLevel >= WATER_LEVEL_HIGH)
{
wetDelta = 1;
}
else if (m_WaterLevel >= WATER_LEVEL_MID && m_WaterLevel < WATER_LEVEL_HIGH)
{
wetDelta = 0.66;
}
else if (m_WaterLevel >= WATER_LEVEL_LOW && m_WaterLevel < WATER_LEVEL_MID)
{
wetDelta = 0.66;
}
else if (m_WaterLevel >= WATER_LEVEL_NONE && m_WaterLevel < WATER_LEVEL_LOW)
{
wetDelta = 0.33;
}
}
else if (!IsInsideBuilding() && !IsUnderRoof() && !IsChildOfType({Car}))
{
if (IsRaining())
wetDelta = GameConstants.ENVIRO_WET_INCREMENT * GameConstants.ENVIRO_TICKS_TO_WETNESS_CALCULATION * (m_Rain) * (1 + (GameConstants.ENVIRO_WIND_EFFECT * m_Wind));
if (IsSnowing() && MiscGameplayFunctions.GetCombinedSnowfallWindValue() > SNOWFALL_WIND_COMBINED_THRESHOLD)
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));
}
else
{
//! player is drying
float tempEffect = Math.Max(m_PlayerHeat + GetEnvironmentTemperature(), 1.0);
float weatherEffect = ((1 - (m_Fog * GameConstants.ENVIRO_FOG_DRY_EFFECT))) * (1 - (m_Clouds * GameConstants.ENVIRO_CLOUD_DRY_EFFECT));
if (weatherEffect <= 0)
{
weatherEffect = 1.0;
}
wetDelta = -(GameConstants.ENVIRO_DRY_INCREMENT * weatherEffect * tempEffect);
if (!IsInsideBuilding())
{
wetDelta *= 1 + (GameConstants.ENVIRO_WIND_EFFECT * m_Wind);
}
}
return wetDelta;
}
// --------------------------------------------------------------------------------
/**
* \brief Sets character related value for furher use
* - position
* - movement speed
* - player heat (actual speed * heat constant)
*/
protected void CollectAndSetPlayerData()
{
vector playerPos = m_Player.GetPosition();
m_PlayerHeightPos = playerPos[1];
m_PlayerSpeed = 0.0;
HumanCommandMove hcm = m_Player.GetCommand_Move();
if (hcm)
m_PlayerSpeed = hcm.GetCurrentMovementSpeed();
m_PlayerHeat = GetPlayerHeat();
}
// Each tick updates current environment member variables
/**
* \brief Sets actual weather related values for further use (rain, snow, wind, etc.)
*/
protected void CollectAndSetEnvironmentData()
{
Weather weather = g_Game.GetWeather();
m_Rain = weather.GetRain().GetActual();
m_Snowfall = weather.GetSnowfall().GetActual();
m_DayOrNight = g_Game.GetMission().GetWorldData().GetDaytime();
m_Fog = weather.GetFog().GetActual();
m_Clouds = weather.GetOvercast().GetActual();
m_Wind = weather.GetWindMagnitude().GetActual();
SetEnvironmentTemperature();
SetAreaGenericColdness();
}
// --------------------------------------------------------------------------------
void SetEnvironmentTemperature()
{
m_IsTempSet = true;
m_EnvironmentTemperature = GetEnvironmentTemperature();
}
/**
* \brief Determines whether player is in cold area which restricts use of some actions (digging)
*/
void SetAreaGenericColdness()
{
float heigthCorrectedTemp = m_WorldData.GetBaseEnvTemperatureAtObject(m_Player);
m_Player.SetInColdArea(heigthCorrectedTemp <= GameConstants.COLD_AREA_TEMPERATURE_THRESHOLD);
}
/**
* \brief Processes items wetness in player possession based on the current water level (the character is in)
* @param pWaterLevel Water level height
*/
protected void ProcessWetnessByWaterLevel(float pWaterLevel)
{
if (pWaterLevel >= WATER_LEVEL_HIGH)
ProcessItemsWetness(m_SlotIdsComplete);
else if (pWaterLevel >= WATER_LEVEL_MID && pWaterLevel < WATER_LEVEL_HIGH)
ProcessItemsWetness(m_SlotIdsUpper);
else if (pWaterLevel >= WATER_LEVEL_LOW && pWaterLevel < WATER_LEVEL_MID)
ProcessItemsWetness(m_SlotIdsBottom);
else if (pWaterLevel >= WATER_LEVEL_NONE && pWaterLevel < WATER_LEVEL_LOW)
ProcessItemsWetness(m_SlotIdsLower);
}
// --------------------------------------------------------------------------------
/**
* \brief Soak items at specific Slot ID(s)
* @param pSlotIds Inventory Slot IDs to process
*/
protected void ProcessItemsWetness(array<int> pSlotIds)
{
EntityAI attachment;
int playerAttachmentCount = m_Player.GetInventory().AttachmentCount();
#ifdef ENABLE_LOGGING
LogDryWetProcess(string.Format("Environment :: ProcessItemsWetness (update interval=%1s)", GameConstants.ENVIRO_TICK_RATE));
#endif
for (int attIdx = 0; attIdx < playerAttachmentCount; ++attIdx)
{
attachment = m_Player.GetInventory().GetAttachmentFromIndex(attIdx);
if (attachment.IsItemBase())
{
int attachmentSlotsCount = attachment.GetInventory().GetSlotIdCount();
for (int attachmentSlotId = 0; attachmentSlotId < attachmentSlotsCount; ++attachmentSlotId)
{
int attachmentSlot = attachment.GetInventory().GetSlotId(attachmentSlotId);
for (int i = 0; i < pSlotIds.Count(); ++i)
{
if (attachmentSlot == pSlotIds.Get(i))
{
ApplyWetnessToItem(ItemBase.Cast(attachment));
break;
}
}
}
}
}
if (m_Player.GetItemInHands())
ApplyWetnessToItem(m_Player.GetItemInHands());
#ifdef ENABLE_LOGGING
LogDryWetProcess("==========");
#endif
}
protected void ApplyWetnessToItem(ItemBase pItem)
{
if (pItem)
{
ItemBase parentItem;
bool isParentWet = false;
bool parentContainsLiquid = false;
InventoryLocation iLoc = new InventoryLocation();
if (pItem.GetInventory().GetCurrentInventoryLocation(iLoc))
{
EntityAI parent = iLoc.GetParent();
if (parent)
{
parentItem = ItemBase.Cast(parent);
if (parentItem)
{
if (parentItem.GetWet() >= GameConstants.STATE_SOAKING_WET)
isParentWet = true;
if ((parentItem.GetLiquidType() != 0) && (parentItem.GetQuantity() > 0))
parentContainsLiquid = true;
}
else
isParentWet = true;
if ((pItem.GetWet() > m_ItemsWetnessMax) && (parent == m_Player))
m_ItemsWetnessMax = pItem.GetWet();
}
}
if (isParentWet || parentContainsLiquid)
{
float soakingCoef = 0;
if (parentContainsLiquid)
{
soakingCoef = pItem.GetSoakingIncrement("parentWithLiquid");
#ifdef ENABLE_LOGGING
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);
#endif
}
else if (isParentWet && parentItem)
{
if (pItem.GetWet() < parentItem.GetWet())
soakingCoef = GetWetDelta();
#ifdef ENABLE_LOGGING
LogDryWetProcess(string.Format("%1 (soak coef=%2/s, current wetness=%3) [parent wet]", pItem.GetDisplayName(), soakingCoef / GameConstants.ENVIRO_TICK_RATE, pItem.GetWet()), parentItem != null);
#endif
}
else
{
soakingCoef = GetWetDelta();
#ifdef ENABLE_LOGGING
LogDryWetProcess(string.Format("%1 (soak coef=%2/s, current wetness=%3) [normal]", pItem.GetDisplayName(), soakingCoef / GameConstants.ENVIRO_TICK_RATE, pItem.GetWet()), parentItem != null);
#endif
}
pItem.AddWet(soakingCoef);
if (pItem.GetInventory().GetCargo())
{
int inItemCount = pItem.GetInventory().GetCargo().GetItemCount();
for (int i = 0; i < inItemCount; ++i)
{
ItemBase inItem;
if (Class.CastTo(inItem, pItem.GetInventory().GetCargo().GetItem(i)))
ApplyWetnessToItem(inItem);
}
}
int attCount = pItem.GetInventory().AttachmentCount();
if (attCount > 0)
{
for (int attIdx = 0; attIdx < attCount; ++attIdx)
{
EntityAI attachment = pItem.GetInventory().GetAttachmentFromIndex(attIdx);
ItemBase itemAtt = ItemBase.Cast(attachment);
if (itemAtt)
ApplyWetnessToItem(itemAtt);
}
}
}
}
}
// --------------------------------------------------------------------------------
/**
* \brief Dry items in player possession
*/
protected void ProcessItemsDryness()
{
EntityAI attachment;
ItemBase item;
int attCount = m_Player.GetInventory().AttachmentCount();
#ifdef ENABLE_LOGGING
LogDryWetProcess(string.Format("Environment :: ProcessItemsDryness (update interval=%1s)", GameConstants.ENVIRO_TICK_RATE));
#endif
EnvironmentDrynessData drynessData = new EnvironmentDrynessData();
drynessData.m_UseTemperatureSources = m_HasTemperatureSources;
if (m_HasTemperatureSources)
{
float distance = vector.Distance(m_UTemperatureSources[0].GetPosition(), m_Player.GetPosition());
distance = Math.Max(distance, 0.1);
drynessData.m_TemperatureSourceDistance = distance;
#ifdef ENABLE_LOGGING
LogDryWetProcess(string.Format("distance to heatsource: %1 m", distance));
#endif
}
for (int attIdx = 0; attIdx < attCount; ++attIdx)
{
attachment = m_Player.GetInventory().GetAttachmentFromIndex(attIdx);
if (attachment && attachment.IsItemBase())
{
item = ItemBase.Cast(attachment);
if (item)
ApplyDrynessToItemEx(item, drynessData);
}
}
if (m_Player.GetItemInHands())
{
ApplyDrynessToItemEx(m_Player.GetItemInHands(), drynessData);
}
#ifdef ENABLE_LOGGING
LogDryWetProcess("==========");
#endif
}
protected void ApplyDrynessToItem(ItemBase pItem)
{
EnvironmentDrynessData drynessData = new EnvironmentDrynessData();
ApplyDrynessToItemEx(pItem, drynessData);
}
protected void ApplyDrynessToItemEx(ItemBase pItem, EnvironmentDrynessData pDrynessData)
{
if (pItem)
{
float dryingIncrement = pItem.GetDryingIncrement("player");
if (pDrynessData.m_UseTemperatureSources)
dryingIncrement = pItem.GetDryingIncrement("playerHeatSource");
ItemBase parentItem;
bool isParentWet = false;
bool parentContainsLiquid = false;
InventoryLocation iLoc = new InventoryLocation();
if (pItem.GetInventory().GetCurrentInventoryLocation(iLoc))
{
EntityAI parent = iLoc.GetParent();
if (parent)
{
parentItem = ItemBase.Cast(parent);
if (parentItem)
{
if (parentItem.GetWet() >= GameConstants.STATE_SOAKING_WET)
isParentWet = true;
if ((parentItem.GetLiquidType() != 0) && (parentItem.GetQuantity() > 0))
parentContainsLiquid = true;
}
if ((pItem.GetWet() > m_ItemsWetnessMax) && (parent == m_Player))
m_ItemsWetnessMax = pItem.GetWet();
}
}
float dryingCoef = 0;
if (!isParentWet && !parentContainsLiquid)
{
dryingCoef = (-1 * GameConstants.ENVIRO_TICK_RATE * dryingIncrement) / pDrynessData.m_TemperatureSourceDistance;
if (pItem.GetWet() >= GameConstants.STATE_DAMP)
{
#ifdef ENABLE_LOGGING
LogDryWetProcess(string.Format("%1 (dry coef=%2/s, current wetness=%3) [normal]", pItem.GetDisplayName(), dryingCoef / GameConstants.ENVIRO_TICK_RATE, pItem.GetWet()), parentItem != null);
#endif
pItem.AddWet(dryingCoef);
}
if (pItem.GetInventory().GetCargo())
{
int inItemCount = pItem.GetInventory().GetCargo().GetItemCount();
for (int i = 0; i < inItemCount; ++i)
{
ItemBase inItem;
if (Class.CastTo(inItem, pItem.GetInventory().GetCargo().GetItem(i)))
ApplyDrynessToItemEx(inItem, pDrynessData);
}
}
int attCount = pItem.GetInventory().AttachmentCount();
if (attCount > 0)
{
for (int attIdx = 0; attIdx < attCount; ++attIdx)
{
EntityAI attachment = pItem.GetInventory().GetAttachmentFromIndex(attIdx);
ItemBase itemAtt;
if (ItemBase.CastTo(itemAtt, attachment))
ApplyDrynessToItemEx(itemAtt, pDrynessData);
}
}
}
if (parentContainsLiquid)
{
//! adds wetness to item inside parent item containing liquid
dryingCoef = (GameConstants.ENVIRO_TICK_RATE * pItem.GetSoakingIncrement("parentWithLiquid")) / pDrynessData.m_TemperatureSourceDistance;
#ifdef ENABLE_LOGGING
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);
#endif
pItem.AddWet(dryingCoef);
}
if (isParentWet)
{
//! adds wetness to item inside wet parent item
dryingCoef = (GameConstants.ENVIRO_TICK_RATE * pItem.GetSoakingIncrement("wetParent")) / pDrynessData.m_TemperatureSourceDistance;
#ifdef ENABLE_LOGGING
LogDryWetProcess(string.Format("%1 (dry coef=%2/s, current wetness=%3) [parent wet]", pItem.GetDisplayName(), dryingCoef / GameConstants.ENVIRO_TICK_RATE, pItem.GetWet()), parentItem != null);
#endif
pItem.AddWet(dryingCoef);
}
}
}
// --------------------------------------------------------------------------------
/**
* \brief Calculates and process player's heatcomfort related to defined body parts
*/
protected void ProcessHeatComfort()
{
float hcPenaltyTotal //! Heat Comfort Penalty
// NEW body parts => splitted
float hcBodyPartTotal, hcBodyPart;
float hBodyPartTotal, hBodyPart;
float heatComfortSum = 0.0;
float heatItems = 0.0;
#ifdef ENABLE_LOGGING
LogItemHeat("====================");
#endif
BodyPartHeatProperties(InventorySlots.HEADGEAR, GameConstants.ENVIRO_HEATCOMFORT_HEADGEAR_WEIGHT, hcBodyPart, hBodyPart);
hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
BodyPartHeatProperties(InventorySlots.MASK, GameConstants.ENVIRO_HEATCOMFORT_MASK_WEIGHT, hcBodyPart, hBodyPart);
hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
BodyPartHeatProperties(InventorySlots.VEST, GameConstants.ENVIRO_HEATCOMFORT_VEST_WEIGHT, hcBodyPart, hBodyPart);
hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
BodyPartHeatProperties(InventorySlots.BODY, GameConstants.ENVIRO_HEATCOMFORT_BODY_WEIGHT, hcBodyPart, hBodyPart);
hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
BodyPartHeatProperties(InventorySlots.BACK, GameConstants.ENVIRO_HEATCOMFORT_BACK_WEIGHT, hcBodyPart, hBodyPart);
hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
BodyPartHeatProperties(InventorySlots.GLOVES, GameConstants.ENVIRO_HEATCOMFORT_GLOVES_WEIGHT, hcBodyPart, hBodyPart);
hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
BodyPartHeatProperties(InventorySlots.LEGS, GameConstants.ENVIRO_HEATCOMFORT_LEGS_WEIGHT, hcBodyPart, hBodyPart);
hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
BodyPartHeatProperties(InventorySlots.FEET, GameConstants.ENVIRO_HEATCOMFORT_FEET_WEIGHT, hcBodyPart, hBodyPart);
hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
BodyPartHeatProperties(InventorySlots.HIPS, GameConstants.ENVIRO_HEATCOMFORT_HIPS_WEIGHT, hcBodyPart, hBodyPart);
hcBodyPartTotal += hcBodyPart; hBodyPartTotal += hBodyPart;
hcPenaltyTotal += NakedBodyPartHeatComfortPenalty(InventorySlots.HEADGEAR, GameConstants.ENVIRO_HEATCOMFORT_HEADGEAR_WEIGHT);
hcPenaltyTotal += NakedBodyPartHeatComfortPenalty(InventorySlots.MASK, GameConstants.ENVIRO_HEATCOMFORT_MASK_WEIGHT);
hcPenaltyTotal += NakedBodyPartHeatComfortPenalty(InventorySlots.BODY, GameConstants.ENVIRO_HEATCOMFORT_BODY_WEIGHT);
hcPenaltyTotal += NakedBodyPartHeatComfortPenalty(InventorySlots.GLOVES, GameConstants.ENVIRO_HEATCOMFORT_GLOVES_WEIGHT);
hcPenaltyTotal += NakedBodyPartHeatComfortPenalty(InventorySlots.LEGS, GameConstants.ENVIRO_HEATCOMFORT_LEGS_WEIGHT);
hcPenaltyTotal += NakedBodyPartHeatComfortPenalty(InventorySlots.FEET, GameConstants.ENVIRO_HEATCOMFORT_FEET_WEIGHT);
heatItems = hBodyPartTotal;
heatComfortSum = hcBodyPartTotal;
heatComfortSum += hcPenaltyTotal; //! heatcomfort body parts penalties
//! Stomach temperature influence to heatcomfort
{
if (m_Player.GetStomach().GetStomachVolume() > 0.0)
{
float stomachContentTemperature = m_Player.GetStomach().GetStomachTemperature();
if (stomachContentTemperature < GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT)
{
stomachContentTemperature = Math.Remap(
-10.0,
GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT,
-GameConstants.ENVIRO_STOMACH_WEIGHT,
0.0,
stomachContentTemperature,
);
}
else if (stomachContentTemperature > GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
{
stomachContentTemperature = Math.Remap(
GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT,
70.0,
0.0,
GameConstants.ENVIRO_STOMACH_WEIGHT,
stomachContentTemperature,
);
}
else
stomachContentTemperature = 0.0;
heatComfortSum += stomachContentTemperature * GameConstants.ENVIRO_STOMACH_WEIGHT;
}
}
float targetHeatComfort = (heatComfortSum + heatItems + (m_PlayerHeat / 100)) + EnvTempToCoef(m_EnvironmentTemperature);
//! uses the raw targetHeatComfort data
m_EnvironmentSnapshot.m_ClothingHeatComfort = hcBodyPartTotal;
m_EnvironmentSnapshot.m_TargetHeatComfort = targetHeatComfort;
ProcessHeatBuffer(m_EnvironmentSnapshot);
if (m_Player.GetModifiersManager().IsModifierActive(eModifiers.MDF_HEATBUFFER))
targetHeatComfort = Math.Clamp(targetHeatComfort, 0.0, m_Player.GetStatHeatComfort().GetMax());
else
targetHeatComfort = Math.Clamp(targetHeatComfort, m_Player.GetStatHeatComfort().GetMin(), m_Player.GetStatHeatComfort().GetMax());
targetHeatComfort = Math.Round(targetHeatComfort * 100) * 0.01;
float dynamicHeatComfort;
{
float direction = 1.0;
if (targetHeatComfort < 0.0)
direction = -1.0;
if (Math.AbsFloat(targetHeatComfort - m_HeatComfort) <= GameConstants.ENVIRO_HEATCOMFORT_MAX_STEP_SIZE)
dynamicHeatComfort = m_AverageHeatComfortBuffer.Add(targetHeatComfort);
else
dynamicHeatComfort = m_AverageHeatComfortBuffer.Add((Math.AbsFloat(targetHeatComfort) - GameConstants.ENVIRO_HEATCOMFORT_MAX_STEP_SIZE) * direction);
}
dynamicHeatComfort = Math.Round(dynamicHeatComfort * 100) * 0.01;
m_HeatComfort = dynamicHeatComfort;
SetTargetHeatComfort(targetHeatComfort);
m_Player.GetStatHeatComfort().Set(m_HeatComfort);
}
protected void SetTargetHeatComfort(float value)
{
m_TargetHeatComfort = value;
}
protected void SetHeatcomfortDirectly()
{
if (m_HeatComfortBehaviorCategory == EEnvironmentHeatcomfortBehaviorCategory.CAR_ENGINE_ON)
{
float targetHeatComfort = 0.0;
float dynamicHeatComfort = m_AverageHeatComfortBuffer.Add(targetHeatComfort);
m_HeatComfort = dynamicHeatComfort;
SetTargetHeatComfort(0.0);
m_Player.GetStatHeatComfort().Set(dynamicHeatComfort);
}
}
// --------------------------------------------------------------------------------
protected void ProcessHeatBuffer(EnvironmentSnapshotData data)
{
if (m_HeatComfortBehaviorCategory == EEnvironmentHeatcomfortBehaviorCategory.DEFAULT)
{
float applicableHeatbuffer = GetApplicableHeatbuffer();
//! dynamic HB cap based on actual heatcomfort (from cloths)
float heatBufferCap = Math.InverseLerp(0.0, GameConstants.ENVIRO_HEATCOMFORT_WEIGHT_SUMMARY, data.m_ClothingHeatComfort);
float heatBufferMax = GameConstants.ENVIRO_PLAYER_HEATBUFFER_CAPACITY_MIN + heatBufferCap * (1 - GameConstants.ENVIRO_PLAYER_HEATBUFFER_CAPACITY_MIN);
m_Player.SetHeatBufferDynamicMax(heatBufferMax);
PlayerStat<float> heatBuffer = m_Player.GetStatHeatBuffer();
//! deplete the heat buffer if there is difference in HB capacity (eg.: cloths were removed)
if (heatBufferCap < m_HeatBufferCapPrevious)
{
float heatBufferValueCorrection = GameConstants.ENVIRO_PLAYER_HEATBUFFER_INCREASE / (heatBufferMax * ((-GameConstants.ENVIRO_PLAYER_HEATBUFFER_TEMP_AFFECT * data.m_TargetHeatComfort) + 1 ));
heatBuffer.Add(-heatBufferValueCorrection);
m_HeatBufferCapPrevious = heatBufferCap;
}
float increaseRate = 0.0;
float decreaseRate = 0.0;
{
increaseRate = GameConstants.ENVIRO_PLAYER_HEATBUFFER_INCREASE / (heatBufferMax * (( -GameConstants.ENVIRO_PLAYER_HEATBUFFER_TEMP_AFFECT * data.m_TargetHeatComfort) + 1 ));
decreaseRate = GameConstants.ENVIRO_PLAYER_HEATBUFFER_DECREASE / (heatBufferMax * (( GameConstants.ENVIRO_PLAYER_HEATBUFFER_TEMP_AFFECT * data.m_TargetHeatComfort) + 1 ));
float decreaseRateByHeatBufferStageCoef = 1;
if (heatBufferMax > HeatBufferMdfr.STAGE_THRESHOLDS[1])
{
float heatBufferMaxInversed = Math.InverseLerp(HeatBufferMdfr.STAGE_THRESHOLDS[1], 1.0, heatBufferMax);
switch (m_Player.GetHeatBufferStage())
{
case 2:
decreaseRateByHeatBufferStageCoef = Math.Lerp(
GameConstants.ENVIRO_PLAYER_HEATBUFFER_STAGE_RATELIMIT[2][0],
GameConstants.ENVIRO_PLAYER_HEATBUFFER_STAGE_RATELIMIT[2][1],
heatBufferMaxInversed,
);
break;
case 1:
decreaseRateByHeatBufferStageCoef = Math.Lerp(
GameConstants.ENVIRO_PLAYER_HEATBUFFER_STAGE_RATELIMIT[1][0],
GameConstants.ENVIRO_PLAYER_HEATBUFFER_STAGE_RATELIMIT[1][1],
heatBufferMaxInversed,
);
break;
}
}
else
{
decreaseRateByHeatBufferStageCoef = GameConstants.ENVIRO_PLAYER_HEATBUFFER_STAGE_RATELIMIT[1][0];
}
decreaseRate *= decreaseRateByHeatBufferStageCoef;
if (m_IsInWater)
decreaseRate *= GameConstants.ENVIRO_PLAYER_HEATBUFFER_WATEREFFECT * m_WaterLevel;
}
if (!m_HasTemperatureSources)
{
if (m_Player.GetModifiersManager().IsModifierActive(eModifiers.MDF_HEATBUFFER))
{
if (m_HeatBufferTimer >= 1.0)
heatBuffer.Add(-decreaseRate);
else
m_HeatBufferTimer = 1.0;
}
else
{
m_HeatBufferTimer = 0.0;
if (applicableHeatbuffer > 0.0)
heatBuffer.Add(-decreaseRate);
else if (applicableHeatbuffer != 0.0 && !m_Player.GetModifiersManager().IsModifierActive(eModifiers.MDF_HEATBUFFER))
heatBuffer.Set(0.0);
}
}
else
{
if (m_HeatComfort > PlayerConstants.THRESHOLD_HEAT_COMFORT_MINUS_WARNING && m_UTSAverageTemperature > 0) // m_UTSAverageTemperature can be negative
{
if (applicableHeatbuffer < heatBufferMax)
{
heatBuffer.Add(increaseRate);
m_HeatBufferCapPrevious = heatBufferCap;
}
}
else if (applicableHeatbuffer > 0.0)
heatBuffer.Add(-decreaseRate);
else if (applicableHeatbuffer != 0.0 && !m_Player.GetModifiersManager().IsModifierActive(eModifiers.MDF_HEATBUFFER))
heatBuffer.Set(0.0);
m_HeatBufferTimer = 0.0;
}
}
}
protected float GetApplicableHeatbuffer()
{
PlayerStat<float> heatBuffer = m_Player.GetStatHeatBuffer();
float applicableHeatbuffer = Math.Round((heatBuffer.Get() / heatBuffer.GetMax()) * 1000) * 0.001;
return applicableHeatbuffer;
}
// --------------------------------------------------------------------------------
/**
* \brief Iterate through items in player posession (by given body parts) and cool/warm them to neutral temparature
* @param pBodyPartIds List of body parts to iterate through (see InventorySlots)
*/
protected void ProcessItemsTemperature(array<int> pBodyPartIds)
{
EntityAI attachment;
ItemBase item;
int attCount = m_Player.GetInventory().AttachmentCount();
for (int attIdx = 0; attIdx < attCount; ++attIdx)
{
attachment = m_Player.GetInventory().GetAttachmentFromIndex(attIdx);
item = ItemBase.Cast(attachment);
int attachmentSlot = attachment.GetInventory().GetSlotId(0);
//! go through all body parts we've defined for that zone (ex.: head, body, feet)
for (int i = 0; i < pBodyPartIds.Count(); ++i)
{
if (attachmentSlot == pBodyPartIds[i])
{
float heatPermCoef = item.GetHeatPermeabilityCoef();
//first handle the item itself, if necessary
if (item.CanHaveTemperature() && !item.IsSelfAdjustingTemperature())
SetProcessedItemTemperature(item, heatPermCoef);
ProcessItemHierarchyRecursive(item, heatPermCoef);
}
}
}
}
/**
* \brief Process temperature of item in character hands and cool/warm it to neutral temparature
*/
protected void ProcessItemsInHandsTemperature()
{
ItemBase item = m_Player.GetItemInHands();
if (item)
{
float heatPermCoef = item.GetHeatPermeabilityCoef();
//first handle the item itself, if necessary
if (item.CanHaveTemperature() && !item.IsSelfAdjustingTemperature())
SetProcessedItemTemperature(item,heatPermCoef);
ProcessItemHierarchyRecursive(item,heatPermCoef);
}
}
protected void ProcessItemHierarchyRecursive(ItemBase item, float heatPermeabilityCoef = 1.0)
{
float heatPermCoef = heatPermeabilityCoef;
// go through any attachments and cargo, recursive
int inventoryAttCount = item.GetInventory().AttachmentCount();
if (inventoryAttCount > 0)
{
ItemBase attachmentItem;
for (int inAttIdx = 0; inAttIdx < inventoryAttCount; ++inAttIdx)
{
if (Class.CastTo(attachmentItem,item.GetInventory().GetAttachmentFromIndex(inAttIdx)))
{
heatPermCoef = heatPermeabilityCoef;
heatPermCoef *= attachmentItem.GetHeatPermeabilityCoef();
if (attachmentItem.CanHaveTemperature() && !attachmentItem.IsSelfAdjustingTemperature())
{
SetProcessedItemTemperature(attachmentItem,heatPermCoef);
}
ProcessItemHierarchyRecursive(attachmentItem,heatPermCoef);
}
}
}
if (item.GetInventory().GetCargo())
{
int inventoryItemCount = item.GetInventory().GetCargo().GetItemCount();
if (inventoryItemCount > 0)
{
ItemBase inventoryItem;
for (int j = 0; j < inventoryItemCount; ++j)
{
if (Class.CastTo(inventoryItem,item.GetInventory().GetCargo().GetItem(j)))
{
heatPermCoef = heatPermeabilityCoef;
heatPermCoef *= inventoryItem.GetHeatPermeabilityCoef();
if (inventoryItem.CanHaveTemperature() && !inventoryItem.IsSelfAdjustingTemperature())
{
SetProcessedItemTemperature(inventoryItem,heatPermCoef);
}
ProcessItemHierarchyRecursive(inventoryItem,heatPermCoef);
}
}
}
}
}
protected void SetProcessedItemTemperature(ItemBase item, float heatPermeabilityCoef = 1.0)
{
float targetTemperature = GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_MIDDLE;
bool globalCooling = true;
//! swimming special behavior
if (m_Player.IsSwimming())
{
SetItemHeatingCoef(GameConstants.TEMP_COEF_SWIMMING);
targetTemperature = m_WorldData.GetLiquidTypeEnviroTemperature(m_LiquidType);
globalCooling = false;
}
if (item.GetTemperature() != targetTemperature || !item.IsFreezeThawProgressFinished())
{
TemperatureDataInterpolated temperatureData = new TemperatureDataInterpolated(
targetTemperature,
ETemperatureAccessTypes.ACCESS_INVENTORY,
GameConstants.ENVIRO_TICK_RATE,
m_ItemTemperatureCoef,
heatPermeabilityCoef,
);
temperatureData.m_UseGlobalCooling = globalCooling;
item.SetTemperatureEx(temperatureData);
}
}
// --------------------------------------------------------------------------------
protected float EnvTempToCoef(float pTemp)
{
return (pTemp - GameConstants.ENVIRO_PLAYER_COMFORT_TEMP) / GameConstants.ENVIRO_TEMP_EFFECT_ON_PLAYER;
}
/**
* \brief Iterate through given body part and calculates heatcofort and item heat value
* @param pBodyPartIds List of body parts to iterate through (see InventorySlots)
* @param pCoef Multiplier used for heatcomfort enhancing
* @param[out] pHeatComfort overall heatcomfort from items of given body part
* @param[out] pHeat overall heat from items for given body part
*/
protected void BodyPartHeatProperties(int pBodyPartId, float pCoef, out float pHeatComfort, out float pHeat)
{
pHeatComfort = 0;
pHeat = 0;
GameInventory playerInventory = m_Player.GetInventory();
int attCount = playerInventory.AttachmentCount();
for (int attIdx = 0; attIdx < attCount; ++attIdx)
{
EntityAI attachment = playerInventory.GetAttachmentFromIndex(attIdx);
if (attachment.IsClothing())
{
ItemBase item = ItemBase.Cast(attachment);
int attachmentSlot = attachment.GetInventory().GetSlotId(0);
if (attachmentSlot == pBodyPartId)
{
#ifdef ENABLE_LOGGING
LogItemHeat(string.Format("BodyPartHeatProperties (%1)", EnumTools.EnumToString(InventorySlots, pBodyPartId)));
#endif
float itemHeatcomfort = 0;
float itemTemperature = 0;
// go through any attachments and cargo (only current level, ignore nested containers - they isolate)
GameInventory itemInventory = item.GetInventory();
int inventoryAttCount = itemInventory.AttachmentCount();
if (inventoryAttCount > 0)
{
#ifdef ENABLE_LOGGING
LogItemHeat(string.Format("attachments:"), false);
#endif
for (int inAttIdx = 0; inAttIdx < inventoryAttCount; ++inAttIdx)
{
EntityAI inAttachment = itemInventory.GetAttachmentFromIndex(inAttIdx);
ItemBase attachmentItem = ItemBase.Cast(inAttachment);
if (attachmentItem && attachmentItem.CanHaveTemperature())
{
itemTemperature = attachmentItem.GetTemperature();
if (itemTemperature < GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT || itemTemperature > GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
{
itemHeatcomfort = NormalizedTemperature(itemTemperature) * attachmentItem.GetQuantityNormalizedScripted() * attachmentItem.GetTemperaturePerQuantityWeight();
#ifdef ENABLE_LOGGING
LogItemHeat(string.Format("%1: temperature=%2 heat=%3", attachmentItem, itemTemperature, pHeat), true);
#endif
pHeat += itemHeatcomfort;
}
}
}
}
CargoBase inventoryItemCargo = itemInventory.GetCargo();
if (inventoryItemCargo)
{
int inventoryItemCount = inventoryItemCargo.GetItemCount();
if (inventoryItemCount > 0)
{
#ifdef ENABLE_LOGGING
LogItemHeat(string.Format("cargo:"), false);
#endif
for (int j = 0; j < inventoryItemCount; ++j)
{
ItemBase inventoryItem = ItemBase.Cast(inventoryItemCargo.GetItem(j));
if (inventoryItem && inventoryItem.CanHaveTemperature())
{
itemTemperature = inventoryItem.GetTemperature();
if (itemTemperature < GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT || itemTemperature > GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
{
itemHeatcomfort = NormalizedTemperature(itemTemperature) * inventoryItem.GetQuantityNormalizedScripted() * inventoryItem.GetTemperaturePerQuantityWeight();
#ifdef ENABLE_LOGGING
LogItemHeat(string.Format("%1: temperature=%2 heat=%3", inventoryItem, itemTemperature, itemHeatcomfort), true);
#endif
pHeat += itemHeatcomfort;
}
}
}
}
}
pHeatComfort = MiscGameplayFunctions.GetCurrentItemHeatIsolation(item) * pCoef;
#ifdef ENABLE_LOGGING
LogItemHeat(string.Format("overall heat from items=%1 (coef applied)", pHeat));
LogItemHeat("");
#endif
break;
}
}
}
}
/**
* \brief Calculates penalty value for heatcomfort - this simulates uncovered body part reaction
* @param pBodyPartSlotId InventorySlot ID to check
* @param pCoef external coefficient used for that slot
* \return Value of penalty for given slot ID
*/
protected float NakedBodyPartHeatComfortPenalty(int pBodyPartSlotId, float pCoef)
{
float penalty = 0.0;
if (!IsInsideBuilding() && !IsUnderRoof() && !IsChildOfType({Car}) && !IsWaterContact())
{
if (m_Rain > GameConstants.ENVIRO_NAKED_BODY_PENALTY_RAIN_MIN_VALUE || m_Snowfall > GameConstants.ENVIRO_NAKED_BODY_PENALTY_SNOWFALL_MIN_VALUE)
{
penalty += GameConstants.ENVIRO_ISOLATION_WETFACTOR_DRENCHED;
penalty *= pCoef;
}
}
return penalty;
}
// --------------------------------------------------------------------------------
/**
* \brief Checks characters proximity for usable Universal Temperature Sources and register them
*/
protected void GatherTemperatureSources()
{
m_UTemperatureSources.Clear();
// Calculate min and max positions of the box
vector pos = m_Player.GetPosition();
vector minPos = pos - Vector(GameConstants.ENVIRO_TEMP_SOURCES_LOOKUP_RADIUS, GameConstants.ENVIRO_TEMP_SOURCES_LOOKUP_RADIUS / 2, GameConstants.ENVIRO_TEMP_SOURCES_LOOKUP_RADIUS);
vector maxPos = pos + Vector(GameConstants.ENVIRO_TEMP_SOURCES_LOOKUP_RADIUS, GameConstants.ENVIRO_TEMP_SOURCES_LOOKUP_RADIUS / 2, GameConstants.ENVIRO_TEMP_SOURCES_LOOKUP_RADIUS);
array<EntityAI> nearestObjects = {};
DayZPlayerUtils.SceneGetEntitiesInBox(minPos, maxPos, nearestObjects, QueryFlags.STATIC|QueryFlags.DYNAMIC); //STATIC catches area effects and other static (or 'static') sources
foreach (EntityAI nearestEntity : nearestObjects)
{
if (nearestEntity.IsUniversalTemperatureSource() && nearestEntity != m_Player)
{
//! skip - Temperature Source is not affecting player entities
if (!nearestEntity.GetUniversalTemperatureSource().GetLambda().AffectsPlayer())
continue;
//! skip - Temperature Source is too far
if (vector.DistanceSq(pos, nearestEntity.GetPosition()) > Math.SqrFloat(nearestEntity.GetUniversalTemperatureSource().GetMaxRange()))
continue;
m_UTemperatureSources.Insert(nearestEntity.GetUniversalTemperatureSource());
}
}
ItemBase inHandsItem = m_Player.GetItemInHands();
if (inHandsItem && inHandsItem.IsUniversalTemperatureSource())
m_UTemperatureSources.Insert(inHandsItem.GetUniversalTemperatureSource());
}
/**
* \brief Processes registered UTSources and calculates resulting m_UTSAverageTemperature
*/
protected void ProcessTemperatureSources()
{
int UTScount = m_UTemperatureSources.Count();
if (UTScount == 0)
{
m_HasTemperatureSources = false;
OnTemperatureSourcesLeft();
m_UTSAverageTemperatureBuffer.Add(0.0);
m_UTSAverageTemperature = 0.0;
SetItemHeatingCoef(GameConstants.TEMP_COEF_INVENTORY);
return;
}
TFloatArray utsTemperatures = new TFloatArray();
// get temperature from the source (based on distance), save it for min/max filtering
float itemCoefAverage = 0.0;
foreach (UTemperatureSource tempSource : m_UTemperatureSources)
{
utsTemperatures.Insert(CalcTemperatureFromTemperatureSource(tempSource));
itemCoefAverage += tempSource.GetTemperatureItemCoef();
}
itemCoefAverage /= UTScount;
SetItemHeatingCoef(itemCoefAverage);
float min = MiscGameplayFunctions.GetMinValue(utsTemperatures);
float max = MiscGameplayFunctions.GetMaxValue(utsTemperatures);
if (max > 0 && min < 0)
{
//! adds average of 2 most significat sources to buffer
m_UTSAverageTemperature = m_UTSAverageTemperatureBuffer.Add((max + min) * 0.5);
}
else
{
m_UTSAverageTemperature = m_UTSAverageTemperatureBuffer.Add(max);
}
if (m_HasTemperatureSources == false)
OnTemperatureSourcesEnter();
m_HasTemperatureSources = true;
}
protected void SetItemHeatingCoef(float val)
{
m_ItemTemperatureCoef = val;
}
/**
* \brief Event fired when characters enters into UTSource proximity
*/
protected void OnTemperatureSourcesEnter();
/**
* \brief Event fired when characters leave the UTSource proximity
*/
protected void OnTemperatureSourcesLeft();
float GetUniversalSourcesTemperageAverage()
{
return m_UTSAverageTemperature;
}
float CalcTemperatureFromTemperatureSource(notnull UTemperatureSource uts)
{
float distance = vector.Distance(m_Player.GetPosition(), uts.GetPosition());
distance = Math.Max(distance, 0.1); //min distance cannot be 0 (division by zero)
float temperature = 0;
//! heat transfer through air to player (env temperature)
if (distance > uts.GetFullRange())
{
float distFactor = Math.InverseLerp(uts.GetMaxRange(), uts.GetFullRange(), distance);
temperature = uts.GetTemperatureCap() * distFactor;
}
else
{
temperature = uts.GetTemperatureCap();
}
return temperature;
}
// --------------------------------------------------------------------------------
//! debug
#ifdef DIAG_DEVELOPER
EnvDebugData GetEnvDebugData()
{
EnvDebugData data = new EnvDebugData();
data.Synch(this, m_Player);
return data;
}
void ShowEnvDebugPlayerInfo(bool enabled)
{
EnvDebugData data = GetEnvDebugData();
DisplayEnvDebugPlayerInfo(enabled, data);
}
static void DisplayEnvDebugPlayerInfo(bool enabled, EnvDebugData data)
{
int windowPosX = 10;
int windowPosY = 200;
Object obj;
DbgUI.Begin("Player stats", windowPosX, windowPosY);
if ( enabled )
{
DbgUI.Text(string.Format("Heat comfort(target): %1", data.m_PlayerData.m_HeatComfortTarget));
DbgUI.Text(string.Format("Heat comfort(dynamic): %1", data.m_PlayerData.m_HeatComfortDynamic));
DbgUI.Text(string.Format("Inside: %1 (%2)", data.m_PlayerData.m_Inside, data.m_PlayerData.m_Surface));
DbgUI.Text(string.Format("Under roof: %1 (%2)", data.m_PlayerData.m_UnderRoof, data.m_PlayerData.m_UnderRoofTimer));
if ( data.m_PlayerData.m_WaterLevel > 0 )
{
DbgUI.Text(string.Format("Water Level: %1", data.m_PlayerData.m_WaterLevel));
}
}
DbgUI.End();
DbgUI.Begin("Weather stats:", windowPosX, windowPosY + 200);
if ( enabled )
{
DbgUI.Text(string.Format("Env temperature (base): %1", data.m_MiscData.m_TemperatureBase));
DbgUI.Text(string.Format("Env temperature (height corrected): %1", data.m_MiscData.m_HeightCorrectedTemperature));
DbgUI.Text(string.Format("Env temperature (modfied): %1", data.m_MiscData.m_TemperatureModified));
DbgUI.Text(string.Format("Wind magnitude(surface mult): %1 (x%2)", data.m_WeatherData.m_Wind, data.m_WeatherData.m_WindModifier));
DbgUI.Text(string.Format("Rain: %1", data.m_WeatherData.m_Rain));
DbgUI.Text(string.Format("Snow: %1", data.m_WeatherData.m_Snowfall));
DbgUI.Text(string.Format("Datetime: %1", WorldDataDaytime.ToString(g_Game.GetMission().GetWorldData().GetDaytime())));
DbgUI.Text(string.Format("Fog: %1", data.m_WeatherData.m_Fog));
DbgUI.Text(string.Format("Clouds: %1", data.m_WeatherData.m_Clouds));
DbgUI.Text(string.Format("Wet delta: %1", data.m_MiscData.m_WetDelta));
}
DbgUI.End();
}
void FillDebugWeatherData(EnvDebugWeatherData data)
{
data.m_Wind = m_Wind;
data.m_WindModifier = GetWindModifierPerSurface();
data.m_Rain = m_Rain;
data.m_Snowfall = m_Snowfall;
data.m_Fog = m_Fog;
data.m_Clouds = m_Clouds;
}
#endif
string GetDebugMessage()
{
string message;
message += "Player stats";
message += "\nHeat comfort(target): " + GetTargetHeatComfort().ToString();
message += "\nHeat comfort(dynamic): " + m_HeatComfort.ToString();
int liquidType;
string impact, surfaceType;
g_Game.SurfaceUnderObjectExCorrectedLiquid(m_Player, surfaceType, impact, liquidType);
message += "\nInside: " + IsInsideBuilding().ToString();
message += "\nSurface: " + surfaceType;
message += "\nLiquid: " + liquidType;
message += "\nUnder roof: " + m_IsUnderRoof.ToString() + " (" + GetNextRoofCheck() + ")";
if (IsWaterContact() && m_WaterLevel > WATER_LEVEL_NONE)
{
message += "\nWater Level: " + m_WaterLevel;
}
message += "\n\nWeather stats";
message += "\nEnv temperature (base): " + m_WorldData.GetBaseEnvTemperature().ToString();
message += "\nEnv temperature (height corrected): " + m_WorldData.GetBaseEnvTemperatureAtObject(m_Player);
message += "\nEnv temperature (modified): " + m_EnvironmentTemperature.ToString();
message += "\nWind: " + m_Wind.ToString() + " (x" + GetWindModifierPerSurface() + ")";
message += "\nRain: " + m_Rain.ToString();
message += "\nSnow: " + m_Snowfall.ToString();
message += "\nDatetime: " + WorldDataDaytime.ToString(m_DayOrNight);
message += "\nFog: " + m_Fog.ToString();
message += "\nClouds: " + m_Clouds.ToString();
message += "\nWet delta: " + GetWetDelta().ToString();
return message;
}
int GetNextRoofCheck()
{
return (GameConstants.ENVIRO_TICK_ROOF_RC_CHECK - m_RoofCheckTimer) + 1;
}
float GetWaterLevel()
{
if (IsWaterContact() && m_WaterLevel > WATER_LEVEL_NONE)
{
return m_WaterLevel;
}
return 0.0;
}
private bool IsNeutralTemperature(float temperature, float lowerLimit = GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT, float upperLimit = GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
{
if (temperature >= lowerLimit && temperature <= upperLimit)
return true;
return false;
}
private float NormalizedTemperature(float temperature, float lowerLimit = GameConstants.ENVIRO_LOW_TEMP_LIMIT, float upperLimit = GameConstants.ENVIRO_HIGH_TEMP_LIMIT)
{
if (temperature >= GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT && temperature <= GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
return 0.0;
if (temperature < GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT)
return Math.Clamp(Math.InverseLerp(lowerLimit, GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT, temperature), -1.0, -0.1);
if (temperature > GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
return Math.Clamp(Math.InverseLerp(GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT, upperLimit, temperature), 0.1, 1.0);
// neutral zone
return 0.0;
}
#ifdef ENABLE_LOGGING
private void LogDryWetProcess(string message, bool indented = false)
{
if (m_DebugLogDryWet)
{
string indentation = "";
if (indented)
indentation = "|--";
Debug.Log(string.Format("%1 %2", indentation, message));
}
}
#endif
#ifdef ENABLE_LOGGING
private void LogItemHeat(string message, bool indented = false)
{
if (m_DebugLogItemHeat)
{
string indentation = "";
if (indented)
indentation = "|--";
Debug.Log(string.Format("%1 %2", indentation, message));
}
}
#endif
// --------------------------------------------------------------------------------
//! DEPRECATED
protected float m_HeatSourceTemp;
protected ref SimpleMovingAverage<float> m_WindAverageBuffer;
protected ref EnvironmentSnapshotData m_EnvironmentSnapshot; //! used for calculations before the data modification
void Init(PlayerBase pPlayer)
{
Init();
}
protected bool OverridenHeatComfort(out float value);
void AddToEnvironmentTemperature(float pTemperature);
protected void ProcessItemsHeat()
{
// for backward combatibility only
ProcessHeatComfort();
}
protected void ProcessWetnessByRain()
{
ProcessItemsWetness(m_SlotIdsComplete);
}
// Returns amount of deg C air temperature should be lowered by, based on player's height above water level
float GetTemperatureHeightCorrection()
{
float temperature_reduction = Math.Max(0, (m_PlayerHeightPos * m_WorldData.m_TemperaturePerHeightReductionModifier));
return temperature_reduction;
}
//! returns weighted avg heat comfort for bodypart
protected void BodyPartHeatProperties(array<int> pBodyPartIds, float pCoef, out float pHeatComfort, out float pHeat)
{
pHeatComfort = 0;
pHeat = 0;
if (pBodyPartIds.Count() > 0)
{
#ifdef ENABLE_LOGGING
LogItemHeat(string.Format("BodyPartHeatProperties (%1)", EnumTools.EnumToString(InventorySlots, pBodyPartIds[0])));
#endif
int attCount = m_Player.GetInventory().AttachmentCount();
for (int attIdx = 0; attIdx < attCount; ++attIdx)
{
EntityAI attachment = m_Player.GetInventory().GetAttachmentFromIndex(attIdx);
if (attachment.IsClothing())
{
ItemBase item = ItemBase.Cast(attachment);
int attachmentSlot = attachment.GetInventory().GetSlotId(0);
//! go through all body parts we've defined for that zone (ex.: head, body, feet)
for (int i = 0; i < pBodyPartIds.Count(); ++i)
{
if (attachmentSlot == pBodyPartIds[i])
{
float heatIsoMult = 1.0;
if (attachmentSlot == InventorySlots.BACK)
heatIsoMult = GameConstants.ENVIRO_HEATISOLATION_BACK_WEIGHT;
else if (attachmentSlot == InventorySlots.VEST)
heatIsoMult = GameConstants.ENVIRO_HEATISOLATION_VEST_WEIGHT;
pHeatComfort += heatIsoMult * MiscGameplayFunctions.GetCurrentItemHeatIsolation(item);
float itemHeatcomfort = 0;
float itemTemperature = 0;
// go through any attachments and cargo (only current level, ignore nested containers - they isolate)
int inventoryAttCount = item.GetInventory().AttachmentCount();
if (inventoryAttCount > 0)
{
#ifdef ENABLE_LOGGING
LogItemHeat(string.Format("attachments:"), false);
#endif
for (int inAttIdx = 0; inAttIdx < inventoryAttCount; ++inAttIdx)
{
EntityAI inAttachment = item.GetInventory().GetAttachmentFromIndex(inAttIdx);
ItemBase attachmentItem = ItemBase.Cast(inAttachment);
if (attachmentItem && attachmentItem.CanHaveTemperature())
{
itemTemperature = attachmentItem.GetTemperature();
if (itemTemperature < GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT || itemTemperature > GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
{
itemHeatcomfort = NormalizedTemperature(itemTemperature) * attachmentItem.GetQuantityNormalizedScripted() * attachmentItem.GetTemperaturePerQuantityWeight();
#ifdef ENABLE_LOGGING
LogItemHeat(string.Format("%1: temperature=%2 heat=%3", attachmentItem, itemTemperature, pHeat), true);
pHeat += itemHeatcomfort;
#endif
}
}
}
}
if (item.GetInventory().GetCargo())
{
int inventoryItemCount = item.GetInventory().GetCargo().GetItemCount();
if (inventoryItemCount > 0)
{
#ifdef ENABLE_LOGGING
LogItemHeat(string.Format("cargo:"), false);
#endif
for (int j = 0; j < inventoryItemCount; ++j)
{
ItemBase inventoryItem = ItemBase.Cast(item.GetInventory().GetCargo().GetItem(j));
if (inventoryItem && inventoryItem.CanHaveTemperature())
{
itemTemperature = inventoryItem.GetTemperature();
if (itemTemperature < GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_LOWER_LIMIT || itemTemperature > GameConstants.ITEM_TEMPERATURE_NEUTRAL_ZONE_UPPER_LIMIT)
{
itemHeatcomfort = NormalizedTemperature(itemTemperature) * inventoryItem.GetQuantityNormalizedScripted() * inventoryItem.GetTemperaturePerQuantityWeight();
#ifdef ENABLE_LOGGING
LogItemHeat(string.Format("%1: temperature=%2 heat=%3", inventoryItem, itemTemperature, itemHeatcomfort), true);
#endif
pHeat += itemHeatcomfort;
}
}
}
}
}
}
}
}
}
pHeatComfort += (pHeatComfort / pBodyPartIds.Count()) * pCoef;
#ifdef ENABLE_LOGGING
LogItemHeat(string.Format("overall heat from items=%1 (coef applied)", pHeat));
LogItemHeat("");
#endif
}
}
protected void SetEnvironmentSnapshotData()
{
EnvironmentSnapshotData data = new EnvironmentSnapshotData();
data.m_TargetHeatComfort = m_TargetHeatComfort;
m_EnvironmentSnapshot = data;
}
//! backward compatibility [<1.27]
protected void ProcessHeatBuffer(float heatComfortCloths)
{
m_EnvironmentSnapshot.m_ClothingHeatComfort = heatComfortCloths;
m_EnvironmentSnapshot.m_TargetHeatComfort = m_TargetHeatComfort;
ProcessHeatBuffer(m_EnvironmentSnapshot);
}
//! backward compatibility [<1.28]
protected float WindEffectTemperatureValue(float temperatureInput)
{
float output = 0.0;
output = (temperatureInput - GameConstants.ENVIRO_WIND_CHILL_LIMIT) / (GameConstants.ENVIRO_WIND_EFFECT_SLOPE - GameConstants.ENVIRO_WIND_CHILL_LIMIT);
output = output * m_Wind * m_WorldData.GetWindCoef();
return -output;
}
float GetDayOrNight()
{
return m_DayOrNight;
}
protected bool IsInsideVehicle()
{
return IsChildOfType({Car});
}
}
#ifdef DIAG_DEVELOPER
class EnvDebugPlayerData : Param
{
float m_HeatComfortDynamic;
float m_HeatComfortTarget;
bool m_Inside;
string m_Surface;
bool m_UnderRoof;
int m_UnderRoofTimer;
float m_WaterLevel;
void Synch(Environment env, PlayerBase player)
{
m_HeatComfortTarget = env.GetTargetHeatComfort();
m_HeatComfortDynamic = player.GetStatHeatComfort().Get();
m_Inside = env.IsInsideBuilding();
m_Surface = player.GetSurfaceType();
m_UnderRoof = env.IsUnderRoof();
m_UnderRoofTimer = env.GetNextRoofCheck();
m_WaterLevel = env.GetWaterLevel();
}
override bool Serialize(Serializer ctx)
{
return (
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));
}
override bool Deserializer(Serializer ctx)
{
return ctx.Read(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);
}
}
class EnvDebugMiscData : Param
{
float m_TemperatureBase;
float m_TemperatureModified;
float m_HeightCorrectedTemperature;
float m_WetDelta;
void Synch(Environment env)
{
m_TemperatureBase = g_Game.GetMission().GetWorldData().GetBaseEnvTemperature();
m_TemperatureModified = env.GetTemperature();
m_HeightCorrectedTemperature = m_TemperatureBase - env.GetTemperatureHeightCorrection();
m_WetDelta = env.GetWetDelta();
}
override bool Serialize(Serializer ctx)
{
return ctx.Write(m_TemperatureBase) && ctx.Write(m_TemperatureModified) && ctx.Write(m_HeightCorrectedTemperature) && ctx.Write(m_WetDelta);
}
override bool Deserializer(Serializer ctx)
{
return ctx.Read(m_TemperatureBase) && ctx.Read(m_TemperatureModified) && ctx.Read(m_HeightCorrectedTemperature) && ctx.Read(m_WetDelta);
}
}
class EnvDebugWeatherData : Param
{
float m_Wind;
float m_WindModifier;
float m_Rain;
float m_Snowfall;
float m_Fog;
float m_Clouds;
void Synch(Environment env)
{
env.FillDebugWeatherData(this);
}
override bool Serialize(Serializer ctx)
{
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);
}
override bool Deserializer(Serializer ctx)
{
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);
}
}
class EnvDebugData : Param
{
ref EnvDebugPlayerData m_PlayerData = new EnvDebugPlayerData();
ref EnvDebugMiscData m_MiscData = new EnvDebugMiscData();
ref EnvDebugWeatherData m_WeatherData = new EnvDebugWeatherData();
void Synch(Environment env, PlayerBase player)
{
m_PlayerData.Synch(env, player);
m_MiscData.Synch(env);
m_WeatherData.Synch(env);
}
override bool Serialize(Serializer ctx)
{
return m_PlayerData.Serialize(ctx) && m_MiscData.Serialize(ctx) && m_WeatherData.Serialize(ctx);
}
override bool Deserializer(Serializer ctx)
{
return m_PlayerData.Deserializer(ctx) && m_MiscData.Deserializer(ctx) && m_WeatherData.Deserializer(ctx);
}
}
#endif