up3
Build and deploy / Build (push) Has been cancelled
Build and deploy / Update Contributors (push) Has been cancelled
Build and deploy / Deploy (push) Has been cancelled
Build and deploy / Build (push) Has been cancelled
Build and deploy / Update Contributors (push) Has been cancelled
Build and deploy / Deploy (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,273 @@
|
||||
package block
|
||||
|
||||
import (
|
||||
"github.com/df-mc/dragonfly/server/block/cube"
|
||||
"github.com/df-mc/dragonfly/server/block/model"
|
||||
"github.com/df-mc/dragonfly/server/item"
|
||||
"github.com/df-mc/dragonfly/server/world"
|
||||
"github.com/go-gl/mathgl/mgl64"
|
||||
)
|
||||
|
||||
// RedstoneWire is a block that is used to transfer a charge between objects. Charged objects can be used to open doors
|
||||
// or activate certain items. This block is the placed form of redstone which can be found by mining redstone ore with
|
||||
// an iron pickaxe or better. Deactivated redstone wire will appear dark red, but activated redstone wire will appear
|
||||
// bright red with a sparkling particle effect.
|
||||
type RedstoneWire struct {
|
||||
empty
|
||||
transparent
|
||||
|
||||
// Power is the current power level of the redstone wire. It ranges from 0 to 15.
|
||||
Power int
|
||||
}
|
||||
|
||||
// HasLiquidDrops ...
|
||||
func (RedstoneWire) HasLiquidDrops() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// BreakInfo ...
|
||||
func (r RedstoneWire) BreakInfo() BreakInfo {
|
||||
return newBreakInfo(0, alwaysHarvestable, nothingEffective, oneOf(RedstoneWire{})).withBreakHandler(func(pos cube.Pos, tx *world.Tx, _ item.User) {
|
||||
updateStrongRedstone(pos, tx)
|
||||
})
|
||||
}
|
||||
|
||||
// EncodeBlock ...
|
||||
func (r RedstoneWire) EncodeBlock() (string, map[string]any) {
|
||||
return "minecraft:redstone_wire", map[string]any{
|
||||
"redstone_signal": int32(r.Power),
|
||||
}
|
||||
}
|
||||
|
||||
// EncodeItem ...
|
||||
func (RedstoneWire) EncodeItem() (name string, meta int16) {
|
||||
return "minecraft:redstone", 0
|
||||
}
|
||||
|
||||
// UseOnBlock ...
|
||||
func (r RedstoneWire) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, tx *world.Tx, user item.User, ctx *item.UseContext) bool {
|
||||
pos, _, used := firstReplaceable(tx, pos, face, r)
|
||||
if !used {
|
||||
return false
|
||||
}
|
||||
belowPos := pos.Side(cube.FaceDown)
|
||||
if !tx.Block(belowPos).Model().FaceSolid(belowPos, cube.FaceUp, tx) {
|
||||
return false
|
||||
}
|
||||
r.Power = r.calculatePower(pos, tx)
|
||||
place(tx, pos, r, user, ctx)
|
||||
if placed(ctx) {
|
||||
updateStrongRedstone(pos, tx)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NeighbourUpdateTick ...
|
||||
func (r RedstoneWire) NeighbourUpdateTick(pos, neighbour cube.Pos, tx *world.Tx) {
|
||||
if pos == neighbour {
|
||||
// Ignore the self-update sent after this wire's block state changes.
|
||||
return
|
||||
}
|
||||
below := pos.Side(cube.FaceDown)
|
||||
if !tx.Block(below).Model().FaceSolid(below, cube.FaceUp, tx) {
|
||||
breakBlock(r, pos, tx)
|
||||
return
|
||||
}
|
||||
if changed, ok := r.updateFromNeighbour(pos, tx); ok && !changed {
|
||||
updateStrongRedstone(pos, tx)
|
||||
}
|
||||
}
|
||||
|
||||
// RedstoneUpdate ...
|
||||
func (r RedstoneWire) RedstoneUpdate(pos cube.Pos, tx *world.Tx) {
|
||||
r.updatePower(pos, tx)
|
||||
}
|
||||
|
||||
// updateFromNeighbour updates the wire after a neighbour change. changed reports whether the wire's power changed,
|
||||
// and ok reports whether the update was allowed by the redstone update handler.
|
||||
func (r RedstoneWire) updateFromNeighbour(pos cube.Pos, tx *world.Tx) (changed bool, ok bool) {
|
||||
if redstoneUpdateCancelled(pos, tx) {
|
||||
return false, false
|
||||
}
|
||||
return r.updatePower(pos, tx), true
|
||||
}
|
||||
|
||||
// updatePower recalculates the wire's power and propagates the network when the power changes.
|
||||
func (r RedstoneWire) updatePower(pos cube.Pos, tx *world.Tx) bool {
|
||||
if power := r.calculatePower(pos, tx); r.Power != power {
|
||||
r.Power = power
|
||||
tx.SetBlock(pos, r, &world.SetOpts{DisableBlockUpdates: true})
|
||||
updateStrongRedstone(pos, tx)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RedstoneSource ...
|
||||
func (RedstoneWire) RedstoneSource() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// WeaklyPowersBlocks returns true because powered redstone dust weakly powers conductive blocks it points into or rests on top of.
|
||||
func (RedstoneWire) WeaklyPowersBlocks() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// WeakPower returns the power emitted by the wire toward a neighbouring receiver. Dust powers upward, never powers
|
||||
// downward, and only powers horizontal receivers in connected directions. A powered wire with no horizontal
|
||||
// connections behaves as an unconnected cross and powers every horizontal side.
|
||||
func (r RedstoneWire) WeakPower(pos cube.Pos, face cube.Face, tx *world.Tx, accountForDust bool) int {
|
||||
if !accountForDust {
|
||||
return 0
|
||||
}
|
||||
if face == cube.FaceUp {
|
||||
return r.Power
|
||||
}
|
||||
if face == cube.FaceDown {
|
||||
return 0
|
||||
}
|
||||
if !r.hasHorizontalRedstoneConnection(pos, tx) {
|
||||
return r.Power
|
||||
}
|
||||
if r.connection(pos, face.Opposite(), tx) {
|
||||
return r.Power
|
||||
}
|
||||
if r.connection(pos, face, tx) && !r.connection(pos, face.RotateLeft(), tx) && !r.connection(pos, face.RotateRight(), tx) {
|
||||
return r.Power
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// StrongPower returns 0 because redstone dust weakly powers conductive blocks rather than strongly powering them.
|
||||
func (RedstoneWire) StrongPower(cube.Pos, cube.Face, *world.Tx, bool) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// calculatePower returns the highest level of received redstone power at the provided position.
|
||||
func (r RedstoneWire) calculatePower(pos cube.Pos, tx *world.Tx) int {
|
||||
return calculateRedstoneWirePower(pos, tx, tx.Block)
|
||||
}
|
||||
|
||||
// calculateRedstoneWirePower returns the highest level of received redstone power at the provided position. blockAt is
|
||||
// injected so direct updates and the BFS wire network share the same rules while the BFS path may still read cached
|
||||
// node state.
|
||||
func calculateRedstoneWirePower(pos cube.Pos, tx *world.Tx, blockAt func(cube.Pos) world.Block) int {
|
||||
aboveBlocksVerticalTravel := blocksRedstoneWireVerticalTravel(blockAt(pos.Side(cube.FaceUp)))
|
||||
var blockPower, wirePower int
|
||||
for _, side := range cube.Faces() {
|
||||
neighbourPos := pos.Side(side)
|
||||
neighbour := blockAt(neighbourPos)
|
||||
|
||||
wirePower = maxRedstoneWirePower(neighbour, wirePower)
|
||||
blockPower = max(blockPower, tx.RedstonePower(neighbourPos, side, false))
|
||||
|
||||
if side.Axis() == cube.Y {
|
||||
// Only check horizontal neighbours from here on.
|
||||
continue
|
||||
}
|
||||
|
||||
if canRedstoneWireStepDown(pos, neighbourPos, neighbour, tx) && !aboveBlocksVerticalTravel {
|
||||
wirePower = maxRedstoneWirePower(blockAt(neighbourPos.Side(cube.FaceUp)), wirePower)
|
||||
}
|
||||
if canRedstoneWireStepDown(neighbourPos.Side(cube.FaceDown), neighbourPos, neighbour, tx) && !blocksRedstoneWireVerticalTravel(neighbour) {
|
||||
wirePower = maxRedstoneWirePower(blockAt(neighbourPos.Side(cube.FaceDown)), wirePower)
|
||||
}
|
||||
|
||||
if _, neighbourSolid := neighbour.Model().(model.Solid); !neighbourSolid {
|
||||
wirePower = maxRedstoneWirePower(blockAt(neighbourPos.Side(cube.FaceDown)), wirePower)
|
||||
}
|
||||
}
|
||||
return max(blockPower, wirePower-1)
|
||||
}
|
||||
|
||||
// hasHorizontalRedstoneConnection checks if the dust connects horizontally to redstone wire or a redstone source. It
|
||||
// does not include passive receivers such as doors, trapdoors, or note blocks.
|
||||
func (r RedstoneWire) hasHorizontalRedstoneConnection(pos cube.Pos, tx *world.Tx) bool {
|
||||
for _, face := range cube.HorizontalFaces() {
|
||||
if r.connection(pos, face, tx) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// connection returns true if the dust shape connects through the given face to another wire or a redstone source. It
|
||||
// also accounts for valid one-block vertical wire connections.
|
||||
func (r RedstoneWire) connection(pos cube.Pos, face cube.Face, tx *world.Tx) bool {
|
||||
sidePos := pos.Side(face)
|
||||
sideBlock := tx.Block(sidePos)
|
||||
if r.connectsAbove(pos, sidePos, sideBlock, tx) || r.connectsTo(sideBlock, true) {
|
||||
return true
|
||||
}
|
||||
return r.connectsBelow(sidePos, sideBlock, tx)
|
||||
}
|
||||
|
||||
// connectsAbove checks if the redstone wire can connect to the block above it.
|
||||
func (r RedstoneWire) connectsAbove(pos, sidePos cube.Pos, sideBlock world.Block, tx *world.Tx) bool {
|
||||
if blocksRedstoneWireVerticalTravel(tx.Block(pos.Side(cube.FaceUp))) || !r.canRunOnTop(tx, sidePos, sideBlock) {
|
||||
return false
|
||||
}
|
||||
return r.connectsTo(tx.Block(sidePos.Side(cube.FaceUp)), false)
|
||||
}
|
||||
|
||||
// connectsBelow checks if the redstone wire can connect to the block below it.
|
||||
func (r RedstoneWire) connectsBelow(sidePos cube.Pos, sideBlock world.Block, tx *world.Tx) bool {
|
||||
_, sideSolid := sideBlock.Model().(model.Solid)
|
||||
return !sideSolid && r.connectsTo(tx.Block(sidePos.Side(cube.FaceDown)), false)
|
||||
}
|
||||
|
||||
// connectsTo reports whether a block is part of the redstone wire connection graph. Passive redstone receivers are not
|
||||
// connections; direct source conductors count only when allowDirectSources is true.
|
||||
func (RedstoneWire) connectsTo(block world.Block, allowDirectSources bool) bool {
|
||||
if _, ok := block.(RedstoneWire); ok {
|
||||
return true
|
||||
}
|
||||
c, ok := block.(world.Conductor)
|
||||
return ok && allowDirectSources && c.RedstoneSource()
|
||||
}
|
||||
|
||||
// canRunOnTop checks whether redstone dust can be placed on top of the block.
|
||||
func (RedstoneWire) canRunOnTop(tx *world.Tx, pos cube.Pos, block world.Block) bool {
|
||||
return block.Model().FaceSolid(pos, cube.FaceUp, tx)
|
||||
}
|
||||
|
||||
// blocksRedstoneWireVerticalTravel checks if the block above redstone wire blocks vertical wire travel.
|
||||
func blocksRedstoneWireVerticalTravel(block world.Block) bool {
|
||||
if _, ok := block.Model().(model.Solid); !ok {
|
||||
return false
|
||||
}
|
||||
diffuser, ok := block.(LightDiffuser)
|
||||
return !ok || diffuser.LightDiffusionLevel() != 0
|
||||
}
|
||||
|
||||
// canRedstoneWireStepDown checks if redstone dust can provide power while travelling down around the side block.
|
||||
func canRedstoneWireStepDown(from, side cube.Pos, block world.Block, tx *world.Tx) bool {
|
||||
if stepDowner, ok := block.(RedstoneWireStepDowner); ok {
|
||||
return stepDowner.CanRedstoneWireStepDown(side, from, tx)
|
||||
}
|
||||
for _, face := range cube.Faces() {
|
||||
if !block.Model().FaceSolid(side, face, tx) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// TrimMaterial delegates to item.RedstoneWire so the block form stays valid for smithing trim decoding too.
|
||||
func (RedstoneWire) TrimMaterial() string {
|
||||
return item.RedstoneWire{}.TrimMaterial()
|
||||
}
|
||||
|
||||
// MaterialColour delegates to item.RedstoneWire to keep trim metadata defined in one place.
|
||||
func (RedstoneWire) MaterialColour() string {
|
||||
return item.RedstoneWire{}.MaterialColour()
|
||||
}
|
||||
|
||||
// allRedstoneWires returns a list of all redstone dust states.
|
||||
func allRedstoneWires() (all []world.Block) {
|
||||
for i := range 16 {
|
||||
all = append(all, RedstoneWire{Power: i})
|
||||
}
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user