void TraverseRayAndMarkVisible(Vec3i fromPos, Vec3i toPosRel, double yoffset = 0.5) { ray.origin.Set(fromPos.X + 0.5, fromPos.Y + yoffset, fromPos.Z + 0.5); ray.dir.Set(toPosRel); toPos.Set(fromPos.X + toPosRel.X, fromPos.Y + toPosRel.Y, fromPos.Z + toPosRel.Z); curpos.Set(fromPos); BlockFacing fromFace = null, toFace; int manhattenLength = fromPos.ManhattenDistanceTo(toPos); int curMhDist; while ((curMhDist = curpos.ManhattenDistanceTo(fromPos)) <= manhattenLength + 2) { // Since chunks are arranged in a uniform grid, all we have to do is to find out on // what facing (N/E/S/W/U/D) the ray leaves the chunk and move into that direction. // This may seem inaccurate, but works surpisingly well toFace = GetExitingFace(curpos); if (toFace == null) { return; } long index3d = ((long)curpos.Y * game.WorldMap.chunkMapSizeZFast + curpos.Z) * game.WorldMap.chunkMapSizeXFast + curpos.X; ClientChunk chunk = null; game.WorldMap.chunks.TryGetValue(index3d, out chunk); if (chunk != null) { chunk.SetVisible(true); if (curMhDist > 1 && !chunk.IsTraversable(fromFace, toFace)) { break; } } curpos.Offset(toFace); fromFace = toFace.GetOpposite(); if (!game.WorldMap.IsValidChunkPosFast(curpos.X, curpos.Y, curpos.Z) && (!isAboveHeightLimit || curpos.Y <= 0)) { break; } } }
public override bool OnHeldInteractStep(float secondsUsed, ItemSlot slot, EntityAgent byEntity, BlockSelection blockSel, EntitySelection entitySel) { if (blockSel == null) { return(false); } if (slot.Itemstack.TempAttributes.GetInt("refilled") > 0) { return(false); } float prevsecondsused = slot.Itemstack.TempAttributes.GetFloat("secondsUsed"); slot.Itemstack.TempAttributes.SetFloat("secondsUsed", secondsUsed); float remainingwater = GetRemainingWateringSeconds(slot.Itemstack); SetRemainingWateringSeconds(slot.Itemstack, remainingwater -= secondsUsed - prevsecondsused); if (remainingwater <= 0) { return(false); } IWorldAccessor world = byEntity.World; BlockPos targetPos = blockSel.Position; if (api.World.Side == EnumAppSide.Server) { var beburningBh = world.BlockAccessor.GetBlockEntity(blockSel.Position.AddCopy(blockSel.Face))?.GetBehavior <BEBehaviorBurning>(); if (beburningBh != null) { beburningBh.KillFire(false); } beburningBh = world.BlockAccessor.GetBlockEntity(blockSel.Position)?.GetBehavior <BEBehaviorBurning>(); if (beburningBh != null) { beburningBh.KillFire(false); } Vec3i voxelPos = new Vec3i(); for (int dx = -2; dx < 2; dx++) { for (int dy = -2; dy < 2; dy++) { for (int dz = -2; dz < 2; dz++) { int x = (int)(blockSel.HitPosition.X * 16); int y = (int)(blockSel.HitPosition.Y * 16); int z = (int)(blockSel.HitPosition.Z * 16); if (x + dx < 0 || x + dx > 15 || y + dy < 0 || y + dy > 15 || z + dz < 0 || z + dz > 15) { continue; } voxelPos.Set(x + dx, y + dy, z + dz); int faceAndSubPosition = CollectibleBehaviorArtPigment.BlockSelectionToSubPosition(blockSel.Face, voxelPos); Block decorblock = world.BlockAccessor.GetDecor(blockSel.Position, faceAndSubPosition); if (decorblock?.FirstCodePart() == "caveart") { world.BlockAccessor.BreakDecor(blockSel.Position, blockSel.Face, faceAndSubPosition); } } } } } Block block = world.BlockAccessor.GetBlock(blockSel.Position); bool notOnSolidblock = false; if ((block.CollisionBoxes == null || block.CollisionBoxes.Length == 0) && !block.IsLiquid()) { notOnSolidblock = true; targetPos = targetPos.DownCopy(); } BlockEntityFarmland be = world.BlockAccessor.GetBlockEntity(targetPos) as BlockEntityFarmland; if (be != null) { be.WaterFarmland(secondsUsed - prevsecondsused); } float speed = 3f; if (world.Side == EnumAppSide.Client) { ModelTransform tf = new ModelTransform(); tf.EnsureDefaultValues(); tf.Origin.Set(0.5f, 0.2f, 0.5f); tf.Translation.Set(-Math.Min(0.25f, speed * secondsUsed / 2), 0, 0); tf.Rotation.Z = GameMath.Min(60, secondsUsed * 90 * speed, 120 - remainingwater * 4); byEntity.Controls.UsingHeldItemTransformBefore = tf; } IPlayer byPlayer = null; if (byEntity is EntityPlayer) { byPlayer = byEntity.World.PlayerByUid(((EntityPlayer)byEntity).PlayerUID); } if (secondsUsed > 1 / speed) { Vec3d pos = blockSel.Position.ToVec3d().Add(blockSel.HitPosition); if (notOnSolidblock) { pos.Y = (int)pos.Y + 0.05; } WaterParticles.MinPos = pos.Add(-0.125 / 2, 1 / 16f, -0.125 / 2); byEntity.World.SpawnParticles(WaterParticles, byPlayer); } return(true); }
public void CullInvisibleChunks() { if (!ClientSettings.Occlusionculling || game.WorldMap.chunks.Count < 100) { return; } Vec3d camPos = game.player.Entity.CameraPos; centerpos.Set((int)(camPos.X / chunksize), (int)(camPos.Y / chunksize), (int)(camPos.Z / chunksize)); isAboveHeightLimit = centerpos.Y >= game.WorldMap.ChunkMapSizeY; playerViewVec = EntityPos.GetViewVector(game.mousePitch, game.mouseYaw).Normalize(); lock (game.WorldMap.chunksLock) { foreach (var val in game.WorldMap.chunks) { val.Value.SetVisible(false); } // We sometimes have issues with chunks adjacent to the player getting culled, so lets make these always visible for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { for (int dz = -1; dz <= 1; dz++) { long index3d = game.WorldMap.ChunkIndex3D(dx + centerpos.X, dy + centerpos.Y, dz + centerpos.Z); ClientChunk chunk = null; if (game.WorldMap.chunks.TryGetValue(index3d, out chunk)) { chunk.SetVisible(true); } } } } } // Add some 15 extra degrees to the field of view angle for safety float fov = GameMath.Cos(game.MainCamera.Fov + 15 * GameMath.DEG2RAD); for (int i = 0; i < cubicShellPositions.Length; i++) { Vec3i vec = cubicShellPositions[i]; float dotProd = playerViewVec.Dot(cubicShellPositionsNormalized[i]); if (dotProd <= fov / 2) { // Outside view frustum continue; } // It seems that one trace can cause issues where chunks are culled when they shouldn't // 2 traces with a y-offset seems to mitigate most of that issue TraverseRayAndMarkVisible(centerpos, vec, 0.25); TraverseRayAndMarkVisible(centerpos, vec, 0.75); } }