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,219 @@
|
||||
package block
|
||||
|
||||
import (
|
||||
"github.com/df-mc/dragonfly/server/block/cube"
|
||||
"github.com/df-mc/dragonfly/server/entity/effect"
|
||||
"github.com/df-mc/dragonfly/server/internal/nbtconv"
|
||||
"github.com/df-mc/dragonfly/server/item"
|
||||
"github.com/df-mc/dragonfly/server/world"
|
||||
"math"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Beacon is a block that projects a light beam skyward, and can provide status effects such as speed, Jump
|
||||
// Boost, haste, regeneration, resistance, or strength to nearby players.
|
||||
type Beacon struct {
|
||||
solid
|
||||
transparent
|
||||
clicksAndSticks
|
||||
sourceWaterDisplacer
|
||||
|
||||
// Primary and Secondary are the primary and secondary effects broadcast to nearby entities by the
|
||||
// beacon.
|
||||
Primary, Secondary effect.LastingType
|
||||
// level is the amount of the pyramid's levels, it is defined by the mineral blocks which build up the
|
||||
// pyramid, and can be 0-4.
|
||||
level int
|
||||
}
|
||||
|
||||
// BeaconSource represents a block which is capable of contributing to powering a beacon pyramid.
|
||||
type BeaconSource interface {
|
||||
// PowersBeacon returns a bool which indicates whether this block can contribute to powering up a
|
||||
// beacon pyramid.
|
||||
PowersBeacon() bool
|
||||
}
|
||||
|
||||
// BreakInfo ...
|
||||
func (b Beacon) BreakInfo() BreakInfo {
|
||||
return newBreakInfo(3, alwaysHarvestable, nothingEffective, oneOf(Beacon{}))
|
||||
}
|
||||
|
||||
// Activate manages the opening of a beacon by activating it.
|
||||
func (b Beacon) Activate(pos cube.Pos, _ cube.Face, tx *world.Tx, u item.User, _ *item.UseContext) bool {
|
||||
if opener, ok := u.(ContainerOpener); ok {
|
||||
opener.OpenBlockContainer(pos, tx)
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// DecodeNBT ...
|
||||
func (b Beacon) DecodeNBT(data map[string]any) any {
|
||||
b.level = int(nbtconv.Int32(data, "Levels"))
|
||||
if primary, ok := effect.ByID(int(nbtconv.Int32(data, "Primary"))); ok {
|
||||
b.Primary = primary.(effect.LastingType)
|
||||
}
|
||||
if secondary, ok := effect.ByID(int(nbtconv.Int32(data, "Secondary"))); ok {
|
||||
b.Secondary = secondary.(effect.LastingType)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// EncodeNBT ...
|
||||
func (b Beacon) EncodeNBT() map[string]any {
|
||||
m := map[string]any{
|
||||
"id": "Beacon",
|
||||
"Levels": int32(b.level),
|
||||
}
|
||||
if primary, ok := effect.ID(b.Primary); ok {
|
||||
m["Primary"] = int32(primary)
|
||||
}
|
||||
if secondary, ok := effect.ID(b.Secondary); ok {
|
||||
m["Secondary"] = int32(secondary)
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// SideClosed ...
|
||||
func (b Beacon) SideClosed(cube.Pos, cube.Pos, *world.Tx) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// LightEmissionLevel ...
|
||||
func (Beacon) LightEmissionLevel() uint8 {
|
||||
return 15
|
||||
}
|
||||
|
||||
// Level returns an integer 0-4 which defines the current pyramid level of the beacon.
|
||||
func (b Beacon) Level() int {
|
||||
return b.level
|
||||
}
|
||||
|
||||
// Tick recalculates level, recalculates the active state of the beacon, and powers players,
|
||||
// once every 80 ticks (4 seconds).
|
||||
func (b Beacon) Tick(currentTick int64, pos cube.Pos, tx *world.Tx) {
|
||||
if currentTick%80 == 0 {
|
||||
before := b.level
|
||||
// Recalculating pyramid level and powering up players in range once every 4 seconds.
|
||||
b.level = b.recalculateLevel(pos, tx)
|
||||
if before != b.level {
|
||||
tx.SetBlock(pos, b, nil)
|
||||
}
|
||||
if b.level == 0 {
|
||||
return
|
||||
}
|
||||
if !b.obstructed(pos, tx) {
|
||||
b.broadcastBeaconEffects(pos, tx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// recalculateLevel recalculates the level of the beacon's pyramid and returns it. The level can be 0-4.
|
||||
func (b Beacon) recalculateLevel(pos cube.Pos, tx *world.Tx) int {
|
||||
var lvl int
|
||||
iter := 1
|
||||
// This loop goes over all 4 possible pyramid levels.
|
||||
for y := pos.Y() - 1; y >= pos.Y()-4; y-- {
|
||||
for x := pos.X() - iter; x <= pos.X()+iter; x++ {
|
||||
for z := pos.Z() - iter; z <= pos.Z()+iter; z++ {
|
||||
if s, ok := tx.Block(cube.Pos{x, y, z}).(BeaconSource); !ok || !s.PowersBeacon() {
|
||||
return lvl
|
||||
}
|
||||
}
|
||||
}
|
||||
iter++
|
||||
lvl++
|
||||
}
|
||||
return lvl
|
||||
}
|
||||
|
||||
// obstructed determines whether the beacon is currently obstructed.
|
||||
func (b Beacon) obstructed(pos cube.Pos, tx *world.Tx) bool {
|
||||
// Fast obstructed light calculation.
|
||||
if tx.SkyLight(pos.Side(cube.FaceUp)) == 15 {
|
||||
return false
|
||||
}
|
||||
// Slow obstructed light calculation, if the fast way out didn't suffice.
|
||||
return tx.HighestLightBlocker(pos.X(), pos.Z()) > pos[1]
|
||||
}
|
||||
|
||||
// broadcastBeaconEffects determines the entities in range which could receive the beacon's powers, and
|
||||
// determines the powers (effects) that these entities could get. Afterwards, the entities in range that are
|
||||
// beaconAffected get their according effect(s).
|
||||
func (b Beacon) broadcastBeaconEffects(pos cube.Pos, tx *world.Tx) {
|
||||
seconds := 9 + b.level*2
|
||||
if b.level == 4 {
|
||||
seconds--
|
||||
}
|
||||
dur := time.Duration(seconds) * time.Second
|
||||
|
||||
// Establishing what effects are active with the current amount of beacon levels.
|
||||
primary, secondary := b.Primary, effect.LastingType(nil)
|
||||
switch b.level {
|
||||
case 0:
|
||||
primary = nil
|
||||
case 1:
|
||||
switch primary {
|
||||
case effect.Resistance, effect.JumpBoost, effect.Strength:
|
||||
primary = nil
|
||||
}
|
||||
case 2:
|
||||
if primary == effect.Strength {
|
||||
primary = nil
|
||||
}
|
||||
case 3:
|
||||
// Accept all effects for primary, but leave secondary as nil.
|
||||
default:
|
||||
secondary = b.Secondary
|
||||
}
|
||||
var primaryEff, secondaryEff effect.Effect
|
||||
// Determining whether the primary power is set.
|
||||
if primary != nil {
|
||||
primaryEff = effect.NewAmbient(primary, 1, dur)
|
||||
// Secondary power can only be set if the primary power is set.
|
||||
if secondary != nil {
|
||||
// It is possible to select 2 primary powers if the beacon's level is 4. This then means that the effect
|
||||
// should get a level of 2.
|
||||
if primary == secondary {
|
||||
primaryEff = effect.NewAmbient(primary, 2, dur)
|
||||
} else {
|
||||
secondaryEff = effect.NewAmbient(secondary, 1, dur)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finding entities in range.
|
||||
r := 10 + (b.level * 10)
|
||||
entitiesInRange := tx.EntitiesWithin(cube.Box(
|
||||
float64(pos.X()-r), -math.MaxFloat64, float64(pos.Z()-r),
|
||||
float64(pos.X()+r), math.MaxFloat64, float64(pos.Z()+r),
|
||||
))
|
||||
for e := range entitiesInRange {
|
||||
if p, ok := e.(beaconAffected); ok {
|
||||
if primaryEff.Type() != nil {
|
||||
p.AddEffect(primaryEff)
|
||||
}
|
||||
if secondaryEff.Type() != nil {
|
||||
p.AddEffect(secondaryEff)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// beaconAffected represents an entity that can be powered by a beacon. Only players will implement this.
|
||||
type beaconAffected interface {
|
||||
// AddEffect adds a specific effect to the entity that implements this interface.
|
||||
AddEffect(e effect.Effect)
|
||||
// BeaconAffected returns whether this entity can be powered by a beacon.
|
||||
BeaconAffected() bool
|
||||
}
|
||||
|
||||
// EncodeItem ...
|
||||
func (Beacon) EncodeItem() (name string, meta int16) {
|
||||
return "minecraft:beacon", 0
|
||||
}
|
||||
|
||||
// EncodeBlock ...
|
||||
func (Beacon) EncodeBlock() (string, map[string]any) {
|
||||
return "minecraft:beacon", nil
|
||||
}
|
||||
Reference in New Issue
Block a user