private void SetContent(Block block, uint data, Liquid liquid, LiquidLevel level, bool isStatic, Vector3i position, bool tickLiquid) { Chunk? chunk = GetChunkWithPosition(position); if (chunk == null) return; uint val = Section.Encode(block, data, liquid, level, isStatic); chunk.GetSection(position.Y >> Section.SectionSizeExp).SetContent(position, val); if (tickLiquid) liquid.TickNow(this, position, level, isStatic); // Block updates - Side is passed out of the perspective of the block receiving the block update. foreach (BlockSide side in BlockSide.All.Sides()) { Vector3i neighborPosition = side.Offset(position); (BlockInstance? blockNeighbor, LiquidInstance? liquidNeighbor) = GetContent(neighborPosition); blockNeighbor?.Block.BlockUpdate(this, neighborPosition, blockNeighbor.Data, side.Opposite()); liquidNeighbor?.Liquid.TickSoon(this, neighborPosition, liquidNeighbor.IsStatic); } ProcessChangedSection(chunk, position); }
/// <inheritdoc /> public void LiquidChange(World world, Vector3i position, Liquid liquid, LiquidLevel level) { if (liquid.IsLiquid && level > LiquidLevel.Three) { ScheduleDestroy(world, position); } }
private void InvalidLocationFlow(World world, Vector3i position, LiquidLevel level) { if (FlowVertical( world, position, currentFillable: null, level, Direction, handleContact: false, out int remaining) && remaining == -1 || FlowVertical( world, position, currentFillable: null, (LiquidLevel)remaining, Direction.Opposite(), handleContact: false, out remaining) && remaining == -1) { return; } SpreadOrDestroyLiquid(world, position, (LiquidLevel)remaining); }
/// <summary> /// Try to place a concrete block at the given position. /// The block will only be actually placed if the placement conditions are met, e.g. the position is replaceable. /// </summary> /// <param name="world">The world in which the block will be placed.</param> /// <param name="level">The height of the block, given in liquid levels.</param> /// <param name="position">The position where the block will be placed.</param> public void Place(World world, LiquidLevel level, Vector3i position) { if (base.Place(world, position)) { world.SetBlock(this.AsInstance(Encode(BlockColor.Default, level.GetBlockHeight())), position); } }
/// <inheritdoc /> public void LiquidChange(World world, Vector3i position, Liquid liquid, LiquidLevel level) { if (liquid.IsLiquid) { world.SetBlock(Dirt.AsInstance(), position); } }
/// <summary> /// Will schedule a tick for a liquid in the next possible update. /// </summary> internal void TickNow(World world, Vector3i position, LiquidLevel level, bool isStatic) { if (this == None) { return; } ScheduledUpdate(world, position, level, isStatic); }
/// <inheritdoc /> internal override void RandomUpdate(World world, Vector3i position, LiquidLevel level, bool isStatic) { if (!isStatic) { return; } world.SetDefaultLiquid(position); Block.Specials.Concrete.Place(world, level, position); }
/// <inheritdoc /> protected override void ScheduledUpdate(World world, Vector3i position, LiquidLevel level, bool isStatic) { if (world.GetBlock(position)?.Block is IFlammable block) { block.Burn(world, position, Block.Fire); } BurnAround(world, position); base.ScheduledUpdate(world, position, level, isStatic); }
/// <inheritdoc /> protected override void ScheduledUpdate(World world, Vector3i position, LiquidLevel level, bool isStatic) { if (CheckVerticalWorldBounds(world, position)) { return; } Block block = world.GetBlock(position)?.Block ?? Block.Air; if (block is IFillable fillable) { ValidLocationFlow(world, position, level, fillable); } else { InvalidLocationFlow(world, position, level); } }
private void RetrieveContent(Vector3i position, out Block? block, out uint data, out Liquid? liquid, out LiquidLevel level, out bool isStatic) { Chunk? chunk = GetChunkWithPosition(position); if (chunk != null) { uint val = chunk.GetSection(position.Y >> Section.SectionSizeExp).GetContent(position); Section.Decode(val, out block, out data, out liquid, out level, out isStatic); return; } block = null; data = 0; liquid = null; level = 0; isStatic = false; }
private void ValidLocationFlow(World world, Vector3i position, LiquidLevel level, IFillable current) { if (FlowVertical( world, position, current, level, Direction, handleContact: true, out _)) { return; } if (level != LiquidLevel.One ? FlowHorizontal(world, position, level, current) || FarFlowHorizontal(world, position, level, current) : TryPuddleFlow(world, position, current)) { return; } world.ModifyLiquid(isStatic: true, position); }
private bool FlowHorizontal(World world, Vector3i position, LiquidLevel level, IFillable currentFillable) { Vector3i horizontalPosition = position; var isHorStatic = false; var levelHorizontal = LiquidLevel.Eight; IFillable?horizontalFillable = null; foreach (Orientation orientation in Orientations.ShuffledStart(position)) { if (CheckNeighbor( currentFillable.AllowOutflow(world, position, orientation.ToBlockSide()), orientation.Offset(position), orientation.Opposite().ToBlockSide())) { return(true); } } if (horizontalPosition == position) { return(false); } SetLiquid(world, this, levelHorizontal + 1, isStatic: false, horizontalFillable, horizontalPosition); if (isHorStatic) { ScheduleTick(world, horizontalPosition); } bool hasRemaining = level != LiquidLevel.One; SetLiquid( world, hasRemaining ? this : None, hasRemaining ? level - 1 : LiquidLevel.Eight, !hasRemaining, currentFillable, position); if (hasRemaining) { ScheduleTick(world, position); } return(true); bool CheckNeighbor(bool outflowAllowed, Vector3i neighborPosition, BlockSide side) { (BlockInstance? blockNeighbor, LiquidInstance? liquidNeighbor) = world.GetContent(neighborPosition); if (!outflowAllowed || blockNeighbor?.Block is not IFillable neighborFillable || !neighborFillable.AllowInflow(world, neighborPosition, side, this)) { return(false); } bool isStatic = liquidNeighbor?.IsStatic ?? false; if (liquidNeighbor?.Liquid == None) { isStatic = true; Vector3i belowNeighborPosition = neighborPosition + FlowDirection; (BlockInstance? belowNeighborBlock, LiquidInstance? belowNeighborLiquid) = world.GetContent( belowNeighborPosition); if (belowNeighborLiquid?.Liquid == None && belowNeighborBlock?.Block is IFillable belowFillable && belowFillable.AllowInflow( world, belowNeighborPosition, Direction.EntrySide(), this) && neighborFillable.AllowOutflow( world, neighborPosition, Direction.ExitSide())) { SetLiquid(world, this, level, isStatic: false, belowFillable, belowNeighborPosition); ScheduleTick(world, belowNeighborPosition); SetLiquid(world, None, LiquidLevel.Eight, isStatic: true, currentFillable, position); } else { SetLiquid(world, this, LiquidLevel.One, isStatic: false, neighborFillable, neighborPosition); if (isStatic) { ScheduleTick(world, neighborPosition); } bool remaining = level != LiquidLevel.One; SetLiquid( world, remaining ? this : None, remaining ? level - 1 : LiquidLevel.Eight, !remaining, currentFillable, position); if (remaining) { ScheduleTick(world, position); } } return(true); } if (liquidNeighbor != null && liquidNeighbor.Liquid != this) { if (ContactManager.HandleContact( world, this.AsInstance(level), position, liquidNeighbor, neighborPosition)) { return(true); } } else if (liquidNeighbor?.Liquid == this && level > liquidNeighbor.Level && liquidNeighbor.Level < levelHorizontal) { bool allowsFlow = liquidNeighbor.Level != level - 1 || level == LiquidLevel.Eight && !IsAtSurface(world, position) && IsAtSurface(world, neighborPosition) || HasNeighborWithLevel(world, level - 2, neighborPosition) || HasNeighborWithEmpty(world, neighborPosition); if (!allowsFlow) { return(false); } levelHorizontal = liquidNeighbor.Level; horizontalPosition = neighborPosition; isHorStatic = isStatic; horizontalFillable = neighborFillable; } return(false); }
/// <summary> /// Get a liquid as instance. /// </summary> public static LiquidInstance AsInstance(this Liquid?liquid, LiquidLevel level = LiquidLevel.Eight, bool isStatic = true) { return(liquid is null ? LiquidInstance.Default : new LiquidInstance(liquid, level, isStatic)); }
private LiquidMeshInfo(LiquidLevel level, BlockSide side, bool isStatic) { Level = level; Side = side; IsStatic = isStatic; }
/// <summary> /// Create liquid meshing information. /// </summary> public static LiquidMeshInfo Liquid(LiquidLevel level, BlockSide side, bool isStatic) { return(new(level, side, isStatic)); }
/// <summary> /// Get the liquid level as block height. /// </summary> public static int GetBlockHeight(this LiquidLevel level) { return(((int)level * 2) + 1); }
/// <inheritdoc /> protected override void ScheduledUpdate(World world, Vector3i position, LiquidLevel level, bool isStatic) { }
public void SetPosition(Block block, uint data, Liquid liquid, LiquidLevel level, bool isStatic, Vector3i position) { SetContent(block, data, liquid, level, isStatic, position, tickLiquid: true); }
/// <inheritdoc /> internal override void RandomUpdate(World world, Vector3i position, LiquidLevel level, bool isStatic) { BurnAround(world, position); }
private bool FlowVertical(World world, Vector3i position, IFillable?currentFillable, LiquidLevel level, VerticalFlow flow, bool handleContact, out int remaining) { (BlockInstance? blockVertical, LiquidInstance? liquidVertical) = world.GetContent( position + flow.Direction()); if (blockVertical?.Block is IFillable verticalFillable && verticalFillable.AllowInflow( world, position + flow.Direction(), flow.EntrySide(), this) && (currentFillable?.AllowOutflow(world, position, flow.ExitSide()) ?? true)) { if (liquidVertical?.Liquid == None) { SetLiquid(world, this, level, isStatic: false, verticalFillable, position + flow.Direction()); SetLiquid(world, None, LiquidLevel.Eight, isStatic: true, currentFillable, position); ScheduleTick(world, position + flow.Direction()); remaining = -1; return(true); } if (liquidVertical?.Liquid == this) { if (liquidVertical.Level == LiquidLevel.Eight) { remaining = (int)level; return(false); } int volume = LiquidLevel.Eight - liquidVertical.Level - 1; if (volume >= (int)level) { SetLiquid( world, this, liquidVertical.Level + (int)level + 1, isStatic: false, verticalFillable, position + flow.Direction()); SetLiquid(world, None, LiquidLevel.Eight, isStatic: true, currentFillable, position); remaining = -1; } else { SetLiquid( world, this, LiquidLevel.Eight, isStatic: false, verticalFillable, position + flow.Direction()); SetLiquid(world, this, level - volume - 1, isStatic: false, currentFillable, position); remaining = (int)(level - volume - 1); ScheduleTick(world, position); } if (liquidVertical.IsStatic) { ScheduleTick(world, position + flow.Direction()); } return(true); } if (handleContact && liquidVertical != null) { remaining = (int)level; return(ContactManager.HandleContact( world, this.AsInstance(level), position, liquidVertical, position + flow.Direction())); } } remaining = (int)level; return(false); }