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); }
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); }
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 }); }
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; } } } }