Esempio n. 1
0
        public List <PathNode> FindPath(BlockPos start, BlockPos end, int maxFallHeight, float stepHeight, Cuboidf entityCollBox, int searchDepth = 9999, bool allowReachAlmost = false)
        {
            blockAccess.Begin();

            centerOffsetX = 0.35 + api.World.Rand.NextDouble() * 0.35;
            centerOffsetZ = 0.35 + api.World.Rand.NextDouble() * 0.35;

            NodesChecked = 0;

            PathNode startNode  = new PathNode(start);
            PathNode targetNode = new PathNode(end);

            openSet.Clear();
            closedSet.Clear();

            openSet.Add(startNode);

            while (openSet.Count > 0)
            {
                if (NodesChecked++ > searchDepth)
                {
                    return(null);
                }

                PathNode nearestNode = openSet.First();
                foreach (var node in openSet)
                {
                    if (node.fCost <= nearestNode.fCost && node.hCost < nearestNode.hCost)
                    {
                        nearestNode = node;
                    }
                }

                openSet.Remove(nearestNode);
                closedSet.Add(nearestNode);


                if (nearestNode == targetNode || (allowReachAlmost && Math.Abs(nearestNode.X - targetNode.X) <= 1 && Math.Abs(nearestNode.Z - targetNode.Z) <= 1 && nearestNode.Y == targetNode.Y))
                {
                    return(retracePath(startNode, nearestNode));
                }

                for (int i = 0; i < Cardinal.ALL.Length; i++)
                {
                    Cardinal card = Cardinal.ALL[i];

                    PathNode neighbourNode = new PathNode(nearestNode, card);

                    float extraCost = 0;
                    if (traversable(neighbourNode, stepHeight, maxFallHeight, entityCollBox, card.IsDiagnoal, ref extraCost) && !closedSet.Contains(neighbourNode))
                    {
                        addIfNearer(nearestNode, neighbourNode, targetNode, openSet, extraCost);
                    }
                }
            }

            return(null);
        }
Esempio n. 2
0
        public List <PathNode> FindPath(BlockPos start, BlockPos end, int maxFallHeight, float stepHeight, Cuboidf entityCollBox, int searchDepth = 9999, int mhdistanceTolerance = 0)
        {
            blockAccess.Begin();

            centerOffsetX = 0.3 + api.World.Rand.NextDouble() * 0.4;
            centerOffsetZ = 0.3 + api.World.Rand.NextDouble() * 0.4;

            NodesChecked = 0;

            PathNode startNode  = new PathNode(start);
            PathNode targetNode = new PathNode(end);

            openSet.Clear();
            closedSet.Clear();

            openSet.Add(startNode);

            while (openSet.Count > 0)
            {
                if (NodesChecked++ > searchDepth)
                {
                    return(null);
                }

                PathNode nearestNode = openSet.RemoveNearest();
                closedSet.Add(nearestNode);

                if (nearestNode == targetNode || (mhdistanceTolerance > 0 && Math.Abs(nearestNode.X - targetNode.X) <= mhdistanceTolerance && Math.Abs(nearestNode.Z - targetNode.Z) <= mhdistanceTolerance && (Math.Abs(nearestNode.Y - targetNode.Y) <= mhdistanceTolerance || (targetNode.Y > nearestNode.Y && targetNode.Y - nearestNode.Y < 4 + mhdistanceTolerance))))
                {
                    return(retracePath(startNode, nearestNode));
                }

                for (int i = 0; i < Cardinal.ALL.Length; i++)
                {
                    Cardinal card = Cardinal.ALL[i];

                    PathNode neighbourNode = new PathNode(nearestNode, card);

                    float    extraCost             = 0;
                    PathNode existingNeighbourNode = openSet.TryFindValue(neighbourNode);
                    if (!(existingNeighbourNode is null))   // we have to do a null check using "is null" due to foibles in PathNode.Equals()
                    {
                        // if it is already in openSet, update the gCost and parent if this nearestNode gives a shorter route to it
                        float baseCostToNeighbour = nearestNode.gCost + nearestNode.distanceTo(neighbourNode);
                        if (existingNeighbourNode.gCost > baseCostToNeighbour + 0.0001f)
                        {
                            if (traversable(neighbourNode, stepHeight, maxFallHeight, entityCollBox, card, ref extraCost) && existingNeighbourNode.gCost > baseCostToNeighbour + extraCost + 0.0001f)
                            {
                                UpdateNode(nearestNode, existingNeighbourNode, extraCost);
                            }
                        }
                    }
        public bool CheckChest()
        {
            if (workChest == null)
            {
                return(false);
            }

            blockCheck.Begin();
            Block check = blockCheck.GetBlock(workChest);

            if (!blockCheck.LastChunkLoaded)
            {
                return(false);
            }
            if (entity.World.BlockAccessor.GetBlockEntity(workChest) is BlockEntityGenericTypedContainer)
            {
                return(true);
            }

            RemoveChest();
            return(false);
        }
Esempio n. 4
0
        private Room FindRoomForPosition(BlockPos pos, ChunkRooms otherRooms)
        {
            QueueOfInt bfsQueue = new QueueOfInt();

            int halfSize = (ARRAYSIZE - 1) / 2;
            int maxSize  = halfSize + halfSize;

            bfsQueue.Enqueue(halfSize << 10 | halfSize << 5 | halfSize);

            int visitedIndex = (halfSize * ARRAYSIZE + halfSize) * ARRAYSIZE + halfSize; // Center node
            int iteration    = ++this.iteration;

            currentVisited[visitedIndex] = iteration;

            int coolingWallCount    = 0;
            int nonCoolingWallCount = 0;

            int skyLightCount    = 0;
            int nonSkyLightCount = 0;
            int exitCount        = 0;

            blockAccess.Begin();

            bool allChunksLoaded = true;

            int      minx = halfSize, miny = halfSize, minz = halfSize, maxx = halfSize, maxy = halfSize, maxz = halfSize;
            int      posX = pos.X - halfSize;
            int      posY = pos.Y - halfSize;
            int      posZ = pos.Z - halfSize;
            BlockPos npos = new BlockPos();
            BlockPos bpos = new BlockPos();
            int      dx, dy, dz;

            while (bfsQueue.Count > 0)
            {
                int compressedPos = bfsQueue.Dequeue();
                dx = compressedPos >> 10;
                dy = (compressedPos >> 5) & 0x1f;
                dz = compressedPos & 0x1f;
                npos.Set(posX + dx, posY + dy, posZ + dz);
                bpos.Set(npos);

                if (dx < minx)
                {
                    minx = dx;
                }
                else if (dx > maxx)
                {
                    maxx = dx;
                }

                if (dy < miny)
                {
                    miny = dy;
                }
                else if (dy > maxy)
                {
                    maxy = dy;
                }

                if (dz < minz)
                {
                    minz = dz;
                }
                else if (dz > maxz)
                {
                    maxz = dz;
                }

                Block bBlock = blockAccess.GetBlock(bpos);

                foreach (BlockFacing facing in BlockFacing.ALLFACES)
                {
                    facing.IterateThruFacingOffsets(npos);  // This must be the first command in the loop, to ensure all facings will be properly looped through regardless of any 'continue;' statements

                    // We cannot exit current block, if the facing is heat retaining (e.g. chiselled block with solid side)
                    if (bBlock.Id != 0 && bBlock.GetHeatRetention(bpos, facing) != 0)
                    {
                        continue;
                    }

                    if (!blockAccess.IsValidPos(npos))
                    {
                        nonCoolingWallCount++;
                        continue;
                    }

                    Block nBlock = blockAccess.GetBlock(npos);
                    allChunksLoaded &= blockAccess.LastChunkLoaded;
                    int heatRetention = nBlock.GetHeatRetention(npos, facing.Opposite);

                    // We hit a wall, no need to scan further
                    if (heatRetention != 0)
                    {
                        if (heatRetention < 0)
                        {
                            coolingWallCount -= heatRetention;
                        }
                        else
                        {
                            nonCoolingWallCount += heatRetention;
                        }

                        continue;
                    }

                    // Compute the new dx, dy, dz offsets for npos
                    dx = npos.X - posX;
                    dy = npos.Y - posY;
                    dz = npos.Z - posZ;

                    // Only traverse within maxSize range, and overall room size must not exceed MAXROOMSIZE
                    //   If outside that, count as an exit and don't continue searching in this direction
                    //   Note: for performance, this switch statement ensures only one conditional check in each case on the dimension which has actually changed, instead of 6 conditionals or more
                    bool outsideCube = false;
                    switch (facing.Index)
                    {
                    case 0:     // North
                        if (dz < minz)
                        {
                            outsideCube = dz < 0 || maxz - minz >= MAXROOMSIZE;
                        }
                        break;

                    case 1:     // East
                        if (dx > maxx)
                        {
                            outsideCube = dx > maxSize || maxx - minx >= MAXROOMSIZE;
                        }
                        break;

                    case 2:     // South
                        if (dz > maxz)
                        {
                            outsideCube = dz > maxSize || maxz - minz >= MAXROOMSIZE;
                        }
                        break;

                    case 3:     // West
                        if (dx < minx)
                        {
                            outsideCube = dx < 0 || maxx - minx >= MAXROOMSIZE;
                        }
                        break;

                    case 4:     // Up
                        if (dy > maxy)
                        {
                            outsideCube = dy > maxSize || maxy - miny >= MAXROOMSIZE;
                        }
                        break;

                    case 5:     // Down
                        if (dy < miny)
                        {
                            outsideCube = dy < 0 || maxy - miny >= MAXROOMSIZE;
                        }
                        break;
                    }
                    if (outsideCube)
                    {
                        exitCount++;
                        continue;
                    }


                    visitedIndex = (dx * ARRAYSIZE + dy) * ARRAYSIZE + dz;
                    if (currentVisited[visitedIndex] == iteration)
                    {
                        continue;                                              // continue if block position was already visited
                    }
                    currentVisited[visitedIndex] = iteration;

                    // We only need to check the skylight if it's a block position not already visited ...
                    int skyLightIndex = dx * ARRAYSIZE + dz;
                    if (skyLightXZChecked[skyLightIndex] < iteration)
                    {
                        skyLightXZChecked[skyLightIndex] = iteration;
                        int light = blockAccess.GetLightLevel(npos, EnumLightLevelType.OnlySunLight);

                        if (light >= api.World.SunBrightness - 1)
                        {
                            skyLightCount++;
                        }
                        else
                        {
                            nonSkyLightCount++;
                        }
                    }

                    bfsQueue.Enqueue(dx << 10 | dy << 5 | dz);
                }
            }



            int sizex = maxx - minx + 1;
            int sizey = maxy - miny + 1;
            int sizez = maxz - minz + 1;

            byte[] posInRoom = new byte[(sizex * sizey * sizez + 7) / 8];

            int volumeCount = 0;

            for (dx = 0; dx < sizex; dx++)
            {
                for (dy = 0; dy < sizey; dy++)
                {
                    visitedIndex = ((dx + minx) * ARRAYSIZE + (dy + miny)) * ARRAYSIZE + minz;
                    for (dz = 0; dz < sizez; dz++)
                    {
                        if (currentVisited[visitedIndex + dz] == iteration)
                        {
                            int index = (dy * sizez + dz) * sizex + dx;

                            posInRoom[index / 8] = (byte)(posInRoom[index / 8] | (1 << (index % 8)));
                            volumeCount++;
                        }
                    }
                }
            }

            bool isCellar = sizex <= MAXCELLARSIZE && sizey <= MAXCELLARSIZE && sizez <= MAXCELLARSIZE;

            if (!isCellar && volumeCount <= ALTMAXCELLARVOLUME)
            {
                isCellar = sizex <= ALTMAXCELLARSIZE && sizey <= MAXCELLARSIZE && sizez <= MAXCELLARSIZE ||
                           sizex <= MAXCELLARSIZE && sizey <= ALTMAXCELLARSIZE && sizez <= MAXCELLARSIZE ||
                           sizex <= MAXCELLARSIZE && sizey <= MAXCELLARSIZE && sizez <= ALTMAXCELLARSIZE;
            }


            return(new Room()
            {
                CoolingWallCount = coolingWallCount,
                NonCoolingWallCount = nonCoolingWallCount,
                SkylightCount = skyLightCount,
                NonSkylightCount = nonSkyLightCount,
                ExitCount = exitCount,
                AnyChunkUnloaded = allChunksLoaded ? 0 : 1,
                Location = new Cuboidi(posX + minx, posY + miny, posZ + minz, posX + maxx, posY + maxy, posZ + maxz),
                PosInRoom = posInRoom,
                IsSmallRoom = isCellar && exitCount == 0
            });
        }
Esempio n. 5
0
        private Room FindRoomForPosition(BlockPos pos, ChunkRooms otherRooms, HashSet <BlockPos> visitedPositions)
        {
            Queue <BlockPos> bfsQueue = new Queue <BlockPos>();

            bfsQueue.Enqueue(pos);
            visitedPositions.Add(pos);

            int maxHalfSize = 7;

            int coolingWallCount    = 0;
            int nonCoolingWallCount = 0;

            int skyLightCount    = 0;
            int nonSkyLightCount = 0;
            int exitCount        = 0;

            blockAccess.Begin();

            HashSet <Vec2i> skyLightXZChecked = new HashSet <Vec2i>();

            bool allChunksLoaded = true;

            int minx = pos.X, miny = pos.Y, minz = pos.Z, maxx = pos.X + 1, maxy = pos.Y + 1, maxz = pos.Z + 1;

            while (bfsQueue.Count > 0)
            {
                BlockPos bpos = bfsQueue.Dequeue();

                foreach (BlockFacing facing in BlockFacing.ALLFACES)
                {
                    BlockPos npos = bpos.AddCopy(facing);

                    if (!api.World.BlockAccessor.IsValidPos(npos))
                    {
                        nonCoolingWallCount++;
                        continue;
                    }

                    Block nBlock = blockAccess.GetBlock(npos);
                    allChunksLoaded &= blockAccess.LastChunkLoaded;
                    int heatRetention = nBlock.GetHeatRetention(npos, facing);

                    // We hit a wall, no need to scan further
                    if (heatRetention != 0)
                    {
                        if (heatRetention < 0)
                        {
                            coolingWallCount -= heatRetention;
                        }
                        else
                        {
                            nonCoolingWallCount += heatRetention;
                        }

                        continue;
                    }

                    // Only traverse within maxHalfSize range
                    bool inCube = Math.Abs(npos.X - pos.X) <= maxHalfSize && Math.Abs(npos.Y - pos.Y) <= maxHalfSize && Math.Abs(npos.Z - pos.Z) <= maxHalfSize;

                    // Outside maxHalfSize range. Count as exit and don't continue searching in this direction
                    if (!inCube)
                    {
                        exitCount++;
                        continue;
                    }

                    minx = Math.Min(minx, bpos.X);
                    miny = Math.Min(miny, bpos.Y);
                    minz = Math.Min(minz, bpos.Z);

                    maxx = Math.Max(maxx, bpos.X);
                    maxy = Math.Max(maxy, bpos.Y);
                    maxz = Math.Max(maxz, bpos.Z);

                    Vec2i vec = new Vec2i(npos.X, npos.Z);
                    if (!skyLightXZChecked.Contains(vec))
                    {
                        skyLightXZChecked.Add(vec);

                        int light = api.World.BlockAccessor.GetLightLevel(npos, EnumLightLevelType.OnlySunLight);

                        if (light >= api.World.SunBrightness - 1)
                        {
                            skyLightCount++;
                        }
                        else
                        {
                            nonSkyLightCount++;
                        }
                    }


                    if (!visitedPositions.Contains(npos))
                    {
                        bfsQueue.Enqueue(npos);
                        visitedPositions.Add(npos);
                    }
                }
            }

            // Find all rooms inside this boundary
            // Lame but what can we do salasinji

            /*BlockPos cpos = new BlockPos();
             * for (int x = minx; x < maxx; x++)
             * {
             *  for (int y = miny; y < maxy; y++)
             *  {
             *      for (int z = minz; z < maxz; z++)
             *      {
             *          cpos.Set(x, y, z);
             *          if (visitedPositions.Contains(cpos)) continue;
             *
             *          Block cBlock = api.World.BlockAccessor.GetBlock(cpos);
             *          if (cBlock.Replaceable > 6000)
             *          {
             *              bool contains = false;
             *              for (int i = 0; !contains && i < otherRooms.Rooms.Count; i++)
             *              {
             *                  Room exroom = otherRooms.Rooms[i];
             *                  contains = exroom.Location.Contains(pos);
             *              }
             *
             *              if (!contains)
             *              {
             *                  Room room = FindRoomForPosition(cpos, otherRooms, visitedPositions);
             *                  otherRooms.AddRoom(room);
             *              }
             *          }
             *      }
             *  }
             * }*/

            return(new Room()
            {
                CoolingWallCount = coolingWallCount,
                NonCoolingWallCount = nonCoolingWallCount,
                SkylightCount = skyLightCount,
                NonSkylightCount = nonSkyLightCount,
                ExitCount = exitCount,
                AnyChunkUnloaded = !allChunksLoaded,
                Location = new Cuboidi(minx, miny, minz, maxx, maxy, maxz)
            });
        }
        public override void OnGameTick(float deltaTime)
        {
            accum         += deltaTime;
            slowaccum     += deltaTime;
            veryslowaccum += deltaTime;

            plrpos.Set((int)entity.Pos.X, (int)entity.Pos.Y, (int)entity.Pos.Z);

            if (veryslowaccum > 10 && damagingFreezeHours > 3)
            {
                if (api.World.Config.GetString("harshWinters").ToBool(true))
                {
                    entity.ReceiveDamage(new DamageSource()
                    {
                        DamageTier = 0, Source = EnumDamageSource.Weather, Type = EnumDamageType.Frost
                    }, 0.2f);
                }

                veryslowaccum = 0;

                if (eagent.Controls.Sprint)
                {
                    sprinterCounter = GameMath.Clamp(sprinterCounter + 1, 0, 10);
                }
                else
                {
                    sprinterCounter = GameMath.Clamp(sprinterCounter - 1, 0, 10);
                }
            }

            if (slowaccum > 3)
            {
                // No need to call this on the client, because we sync nearHeatSourceStrength
                if (api.World.Side == EnumAppSide.Server)
                {
                    Room room = api.ModLoader.GetModSystem <RoomRegistry>().GetRoomForPosition(plrpos);
                    // Check whether it is a proper room, or something like a room i.e. with a roof, for exaample a natural cave
                    inEnclosedRoom         = room.ExitCount == 0 || room.SkylightCount < room.NonSkylightCount;
                    nearHeatSourceStrength = 0;

                    double px = entity.Pos.X;
                    double py = entity.Pos.Y + 0.9;
                    double pz = entity.Pos.Z;

                    // Fire heat proximity effect (measured by straight-line shortest distance to fire, i.e. what the player sees visually)
                    // within 1 block from the edge of the fire block: full heat
                    // within 2 blocks from the edge of the fire block: ~66% heat
                    // within 3 blocks from the edge of the fire block: ~33% heat
                    // max range (3 blocks diagonally in both X and Z directions): ~12% heat

                    // similar consequences in a room, but slower falloff, still 33% heat at 6 blocks range

                    // http://fooplot.com/#W3sidHlwZSI6MCwiZXEiOiJtaW4oMSw5Lyg4K3heMi41KSkiLCJjb2xvciI6IiMwMDAwMDAifSx7InR5cGUiOjAsImVxIjoibWluKDEsOS8oOCt4XjEuNzUpKSIsImNvbG9yIjoiIzAwMDAwMCJ9LHsidHlwZSI6MTAwMCwid2luZG93IjpbIjAiLCI5IiwiMCIsIjEuMiJdfV0-

                    double   proximityPower = inEnclosedRoom ? 0.875 : 1.25;
                    BlockPos min, max;
                    if (inEnclosedRoom && room.Location.SizeX >= 1 && room.Location.SizeY >= 1 && room.Location.SizeZ >= 1)
                    {
                        min = new BlockPos(room.Location.MinX, room.Location.MinY, room.Location.MinZ);
                        max = new BlockPos(room.Location.MaxX, room.Location.MaxY, room.Location.MaxZ);
                    }
                    else
                    {
                        min = plrpos.AddCopy(-3, -3, -3);
                        max = plrpos.AddCopy(3, 3, 3);
                    }

                    blockAccess.Begin();
                    blockAccess.WalkBlocks(min, max, (block, pos) =>
                    {
                        BlockBehavior src;
                        if ((src = block.GetBehavior(typeof(IHeatSource), true)) != null)
                        {
                            float factor            = Math.Min(1f, 9 / (8 + (float)Math.Pow(pos.DistanceSqToNearerEdge(px, py, pz), proximityPower)));
                            nearHeatSourceStrength += (src as IHeatSource).GetHeatStrength(api.World, pos, plrpos) * factor;
                        }
                    });
                }

                updateWearableConditions();
                entity.WatchedAttributes.MarkPathDirty("bodyTemp");
                slowaccum = 0;
            }

            if (accum > 1)
            {
                IPlayer plr = (entity as EntityPlayer)?.Player;

                if (entity.World.Side == EnumAppSide.Server && (plr as IServerPlayer)?.ConnectionState != EnumClientState.Playing)
                {
                    return;
                }

                if ((plr?.WorldData.CurrentGameMode == EnumGameMode.Creative || plr?.WorldData.CurrentGameMode == EnumGameMode.Spectator))
                {
                    CurBodyTemperature = NormalBodyTemperature;
                    entity.WatchedAttributes.SetFloat("freezingEffectStrength", 0);
                    return;
                }

                if (plr.Entity.Controls.TriesToMove || plr.Entity.Controls.Jump || plr.Entity.Controls.LeftMouseDown || plr.Entity.Controls.RightMouseDown)
                {
                    lastMoveMs = entity.World.ElapsedMilliseconds;
                }

                ClimateCondition conds = api.World.BlockAccessor.GetClimateAt(plrpos, EnumGetClimateMode.NowValues);
                if (conds == null)
                {
                    return;
                }
                Vec3d windspeed = api.World.BlockAccessor.GetWindSpeedAt(plrpos);

                bool rainExposed = api.World.BlockAccessor.GetRainMapHeightAt(plrpos) <= plrpos.Y;

                Wetness = GameMath.Clamp(
                    Wetness
                    + conds.Rainfall * (rainExposed ? 0.06f : 0) * (conds.Temperature < -1 ? 0.2f : 1) /* Get wet 5 times slower with snow */
                    + (entity.Swimming ? 1 : 0)
                    - (float)Math.Max(0, (api.World.Calendar.TotalHours - LastWetnessUpdateTotalHours) * GameMath.Clamp(nearHeatSourceStrength, 1, 2))
                    , 0, 1);

                LastWetnessUpdateTotalHours = api.World.Calendar.TotalHours;
                accum = 0;

                float sprintBonus   = sprinterCounter / 2f;
                float wetnessDebuff = (float)Math.Max(0, Wetness - 0.1) * 10f;

                // Can bear anything above 10 degrees without clothing, while standing still
                float hereTemperature = conds.Temperature + clothingBonus + sprintBonus - wetnessDebuff;

                float tempDiff = hereTemperature - GameMath.Clamp(hereTemperature, bodyTemperatureResistance, 50);
                // Above 10 degrees, slowly warms up
                if (tempDiff == 0)
                {
                    tempDiff = Math.Max((hereTemperature - bodyTemperatureResistance), 0);
                }

                float ambientTempChange = GameMath.Clamp(tempDiff / 6f, -6, 6);

                tempChange = nearHeatSourceStrength + (inEnclosedRoom ? 1 : -(float)Math.Max((windspeed.Length() - 0.15) * 2, 0) + ambientTempChange);

                bool sleeping = entity.GetBehavior <EntityBehaviorTiredness>()?.IsSleeping == true;
                if (sleeping)
                {
                    if (inEnclosedRoom)
                    {
                        tempChange = GameMath.Clamp(NormalBodyTemperature - CurBodyTemperature, -0.15f, 0.15f);
                    }
                    else if (!rainExposed)
                    {
                        tempChange += GameMath.Clamp(NormalBodyTemperature - CurBodyTemperature, 1f, 1f);
                    }
                }

                if (entity.IsOnFire)
                {
                    tempChange = Math.Max(25, tempChange);
                }


                float tempUpdateHoursPassed = (float)(api.World.Calendar.TotalHours - BodyTempUpdateTotalHours);
                if (tempUpdateHoursPassed > 0.01)
                {
                    if (tempChange < -0.5 || tempChange > 0)
                    {
                        if (tempChange > 0.5)
                        {
                            tempChange *= 2;                   // Warming up with a firepit is twice as fast, because nobody wants to wait forever
                        }
                        CurBodyTemperature = GameMath.Clamp(CurBodyTemperature + tempChange * tempUpdateHoursPassed, 31, 45);
                    }

                    BodyTempUpdateTotalHours = api.World.Calendar.TotalHours;

                    entity.WatchedAttributes.SetFloat("freezingEffectStrength", GameMath.Clamp((NormalBodyTemperature - CurBodyTemperature) / 4f - 0.5f, 0, 1));

                    if (NormalBodyTemperature - CurBodyTemperature > 4)
                    {
                        damagingFreezeHours += tempUpdateHoursPassed;
                    }
                    else
                    {
                        damagingFreezeHours = 0;
                    }
                }
            }
        }