Skip to content

Commit

Permalink
Mostly finished un-breaking entities.
Browse files Browse the repository at this point in the history
  • Loading branch information
Sandertv committed Oct 29, 2024
1 parent 6e323bf commit 786d930
Show file tree
Hide file tree
Showing 44 changed files with 366 additions and 493 deletions.
6 changes: 4 additions & 2 deletions server/block/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,8 @@ func (g gravityAffected) fall(b world.Block, pos cube.Pos, tx *world.Tx) {
_, liquid := tx.Liquid(pos.Side(cube.FaceDown))
if air || liquid {
tx.SetBlock(pos, nil, nil)
tx.AddEntity(tx.World().EntityRegistry().Config().FallingBlock(b, pos.Vec3Centre()))
opts := world.EntitySpawnOpts{Position: pos.Vec3Centre()}
tx.AddEntity(tx.World().EntityRegistry().Config().FallingBlock(opts, b))
}
}

Expand Down Expand Up @@ -271,7 +272,8 @@ type flammableEntity interface {
// dropItem ...
func dropItem(tx *world.Tx, it item.Stack, pos mgl64.Vec3) {
create := tx.World().EntityRegistry().Config().Item
tx.AddEntity(create(it, pos, mgl64.Vec3{rand.Float64()*0.2 - 0.1, 0.2, rand.Float64()*0.2 - 0.1}))
opts := world.EntitySpawnOpts{Position: pos, Velocity: mgl64.Vec3{rand.Float64()*0.2 - 0.1, 0.2, rand.Float64()*0.2 - 0.1}}
tx.AddEntity(create(opts, it))
}

// bass is a struct that may be embedded for blocks that create a bass sound.
Expand Down
2 changes: 1 addition & 1 deletion server/block/fire.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func (f Fire) burn(from, to cube.Pos, tx *world.Tx, r *rand.Rand, chanceBound in
return
}
if t, ok := flammable.(TNT); ok {
t.Ignite(to, tx, nil)
t.Ignite(to, tx)
return
}
tx.SetBlock(to, nil, nil)
Expand Down
20 changes: 7 additions & 13 deletions server/block/tnt.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,29 +21,22 @@ type TNT struct {
func (t TNT) Activate(pos cube.Pos, clickedFace cube.Face, tx *world.Tx, u item.User, ctx *item.UseContext) bool {
held, _ := u.HeldItems()
if _, ok := held.Enchantment(enchantment.FireAspect{}); ok {
t.Ignite(pos, tx, u)
t.Ignite(pos, tx)
ctx.DamageItem(1)
return true
}
return false
}

// Ignite ...
func (t TNT) Ignite(pos cube.Pos, tx *world.Tx, igniter world.Entity) bool {
t.igniter = igniter
spawnTnt(pos, tx, time.Second*4, t.igniter)
func (t TNT) Ignite(pos cube.Pos, tx *world.Tx) bool {
spawnTnt(pos, tx, time.Second*4)
return true
}

// Igniter returns the entity that ignited the TNT.
// It is nil if ignited by a world source like fire.
func (t TNT) Igniter() world.Entity {
return t.igniter
}

// Explode ...
func (t TNT) Explode(explosionPos mgl64.Vec3, pos cube.Pos, tx *world.Tx, c ExplosionConfig) {
spawnTnt(pos, tx, time.Second/2+time.Duration(rand.Intn(int(time.Second+time.Second/2))), t.igniter)
spawnTnt(pos, tx, time.Second/2+time.Duration(rand.Intn(int(time.Second+time.Second/2))))
}

// BreakInfo ...
Expand All @@ -67,8 +60,9 @@ func (t TNT) EncodeBlock() (name string, properties map[string]interface{}) {
}

// spawnTnt creates a new TNT entity at the given position with the given fuse duration.
func spawnTnt(pos cube.Pos, tx *world.Tx, fuse time.Duration, igniter world.Entity) {
func spawnTnt(pos cube.Pos, tx *world.Tx, fuse time.Duration) {
tx.PlaySound(pos.Vec3Centre(), sound.TNT{})
tx.SetBlock(pos, nil, nil)
tx.AddEntity(tx.World().EntityRegistry().Config().TNT(pos.Vec3Centre(), fuse, igniter))
opts := world.EntitySpawnOpts{Position: pos.Vec3Centre()}
tx.AddEntity(tx.World().EntityRegistry().Config().TNT(opts, fuse))
}
44 changes: 23 additions & 21 deletions server/entity/area_effect_cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,20 @@ import (
"github.com/df-mc/dragonfly/server/internal/nbtconv"
"github.com/df-mc/dragonfly/server/item/potion"
"github.com/df-mc/dragonfly/server/world"
"github.com/go-gl/mathgl/mgl64"
"time"
)

// NewAreaEffectCloud creates a new area effect cloud entity and returns it.
func NewAreaEffectCloud(pos mgl64.Vec3, p potion.Potion) *Ent {
func NewAreaEffectCloud(opts world.EntitySpawnOpts, p potion.Potion) *world.EntityHandle {
config := areaEffectCloudConf
config.Potion = p
for _, e := range p.Effects() {
if _, ok := e.Type().(effect.LastingType); !ok {
config.ReapplicationDelay = 0
break
}
}
return Config{Behaviour: config.New(p)}.New(AreaEffectCloudType{}, pos)
return opts.New(AreaEffectCloudType{}, config)
}

var areaEffectCloudConf = AreaEffectCloudBehaviourConfig{
Expand All @@ -29,51 +29,53 @@ var areaEffectCloudConf = AreaEffectCloudBehaviourConfig{
}

// NewAreaEffectCloudWith ...
func NewAreaEffectCloudWith(pos mgl64.Vec3, t potion.Potion, duration, reapplicationDelay, durationOnUse time.Duration, radius, radiusOnUse, radiusGrowth float64) *Ent {
func NewAreaEffectCloudWith(opts world.EntitySpawnOpts, t potion.Potion, duration, reapplicationDelay, durationOnUse time.Duration, radius, radiusOnUse, radiusGrowth float64) *world.EntityHandle {
config := AreaEffectCloudBehaviourConfig{
Potion: t,
Radius: radius,
RadiusUseGrowth: radiusOnUse,
RadiusTickGrowth: radiusGrowth,
Duration: duration,
DurationUseGrowth: durationOnUse,
ReapplicationDelay: reapplicationDelay,
}
return Config{Behaviour: config.New(t)}.New(AreaEffectCloudType{}, pos)
return opts.New(AreaEffectCloudType{}, config)
}

// AreaEffectCloudType is a world.EntityType implementation for AreaEffectCloud.
type AreaEffectCloudType struct{}

func (t AreaEffectCloudType) Open(tx *world.Tx, handle *world.EntityHandle, data *world.EntityData) world.Entity {
return &Ent{tx: tx, handle: handle, data: data}
}

func (AreaEffectCloudType) EncodeEntity() string { return "minecraft:area_effect_cloud" }
func (AreaEffectCloudType) BBox(e world.Entity) cube.BBox {
r := e.(*Ent).Behaviour().(*AreaEffectCloudBehaviour).Radius()
return cube.Box(-r, 0, -r, r, 0.5, r)
}

func (AreaEffectCloudType) DecodeNBT(m map[string]any) world.Entity {
return NewAreaEffectCloudWith(
nbtconv.Vec3(m, "Pos"),
potion.From(nbtconv.Int32(m, "PotionId")),
nbtconv.TickDuration[int32](m, "Duration"),
nbtconv.TickDuration[int32](m, "ReapplicationDelay"),
nbtconv.TickDuration[int32](m, "DurationOnUse"),
float64(nbtconv.Float32(m, "Radius")),
float64(nbtconv.Float32(m, "RadiusOnUse")),
float64(nbtconv.Float32(m, "RadiusPerTick")),
)
func (AreaEffectCloudType) DecodeNBT(m map[string]any, data *world.EntityData) {
data.Data = AreaEffectCloudBehaviourConfig{
Potion: potion.From(nbtconv.Int32(m, "PotionId")),
Radius: float64(nbtconv.Float32(m, "Radius")),
RadiusUseGrowth: float64(nbtconv.Float32(m, "RadiusOnUse")),
RadiusTickGrowth: float64(nbtconv.Float32(m, "RadiusPerTick")),
Duration: nbtconv.TickDuration[int32](m, "Duration"),
DurationUseGrowth: nbtconv.TickDuration[int32](m, "ReapplicationDelay"),
ReapplicationDelay: nbtconv.TickDuration[int32](m, "DurationOnUse"),
}.New()
}

func (AreaEffectCloudType) EncodeNBT(e world.Entity) map[string]any {
ent := e.(*Ent)
a := ent.Behaviour().(*AreaEffectCloudBehaviour)
func (AreaEffectCloudType) EncodeNBT(data *world.EntityData) map[string]any {
a := data.Data.(*AreaEffectCloudBehaviour)
return map[string]any{
"Pos": nbtconv.Vec3ToFloat32Slice(ent.Position()),
"PotionId": int32(a.conf.Potion.Uint8()),
"ReapplicationDelay": int32(a.conf.ReapplicationDelay),
"RadiusPerTick": float32(a.conf.RadiusTickGrowth),
"RadiusOnUse": float32(a.conf.RadiusUseGrowth),
"DurationOnUse": int32(a.conf.DurationUseGrowth),
"Radius": float32(a.radius),
"Duration": int32(a.duration),
"PotionId": int32(a.t.Uint8()),
}
}
33 changes: 14 additions & 19 deletions server/entity/area_effect_cloud_behaviour.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
// AreaEffectCloudBehaviourConfig contains optional parameters for an area
// effect cloud entity.
type AreaEffectCloudBehaviourConfig struct {
Potion potion.Potion
// Radius specifies the initial radius of the cloud. Defaults to 3.0.
Radius float64
// RadiusUseGrowth is the value that is added to the radius every time the
Expand All @@ -29,24 +30,25 @@ type AreaEffectCloudBehaviourConfig struct {
ReapplicationDelay time.Duration
}

func (conf AreaEffectCloudBehaviourConfig) Apply(data *world.EntityData) {
data.Data = conf.New()
}

// New creates an AreaEffectCloudBehaviour using the parameter in conf and t.
func (conf AreaEffectCloudBehaviourConfig) New(t potion.Potion) *AreaEffectCloudBehaviour {
func (conf AreaEffectCloudBehaviourConfig) New() *AreaEffectCloudBehaviour {
if conf.Radius == 0 {
conf.Radius = 3.0
}
if conf.Duration == 0 {
conf.Duration = time.Second * 30
}
stationary := StationaryBehaviourConfig{
ExistenceDuration: conf.Duration,
}
stationary := StationaryBehaviourConfig{ExistenceDuration: conf.Duration}
return &AreaEffectCloudBehaviour{
conf: conf,
stationary: stationary.New(),
duration: conf.Duration,
radius: conf.Radius,
targets: make(map[world.Entity]time.Duration),
t: t,
}
}

Expand All @@ -55,7 +57,6 @@ func (conf AreaEffectCloudBehaviourConfig) New(t potion.Potion) *AreaEffectCloud
// hit the ground.
type AreaEffectCloudBehaviour struct {
conf AreaEffectCloudBehaviourConfig
t potion.Potion

stationary *StationaryBehaviour

Expand All @@ -71,13 +72,13 @@ func (a *AreaEffectCloudBehaviour) Radius() float64 {

// Effects returns the effects the area effect cloud provides.
func (a *AreaEffectCloudBehaviour) Effects() []effect.Effect {
return a.t.Effects()
return a.conf.Potion.Effects()
}

// Tick ...
func (a *AreaEffectCloudBehaviour) Tick(e *Ent, tx *world.Tx) *Movement {
a.stationary.Tick(e, tx)
if a.stationary.close || a.stationary.age < 10 {
if a.stationary.close || e.Age() < time.Second/2 {
// The cloud lives for at least half a second before it may begin
// spreading effects and growing/shrinking.
return nil
Expand All @@ -90,13 +91,13 @@ func (a *AreaEffectCloudBehaviour) Tick(e *Ent, tx *world.Tx) *Movement {
}
}

if a.stationary.age%10 != 0 {
if int16(e.Age()/(time.Second*20))%10 != 0 {
// Area effect clouds only trigger updates every ten ticks.
return nil
}

for target, expiration := range a.targets {
if a.stationary.age >= expiration {
if e.Age() >= expiration {
delete(a.targets, target)
}
}
Expand All @@ -117,25 +118,22 @@ func (a *AreaEffectCloudBehaviour) Tick(e *Ent, tx *world.Tx) *Movement {
// applyEffects applies the effects of an area effect cloud at pos to all
// entities passed if they were within the radius and don't have an active
// cooldown period.
func (a *AreaEffectCloudBehaviour) applyEffects(pos mgl64.Vec3, e *Ent, entities []world.Entity) bool {
e.mu.Lock()
defer e.mu.Unlock()

func (a *AreaEffectCloudBehaviour) applyEffects(pos mgl64.Vec3, ent *Ent, entities []world.Entity) bool {
var update bool
for _, e := range entities {
delta := e.Position().Sub(pos)
delta[1] = 0
if delta.Len() <= a.radius {
l := e.(Living)
for _, eff := range a.t.Effects() {
for _, eff := range a.Effects() {
if lasting, ok := eff.Type().(effect.LastingType); ok {
l.AddEffect(effect.New(lasting, eff.Level(), eff.Duration()/4))
continue
}
l.AddEffect(eff)
}

a.targets[e] = a.stationary.age + a.conf.ReapplicationDelay
a.targets[e] = ent.Age() + a.conf.ReapplicationDelay
a.subtractUseDuration()
a.subtractUseRadius()

Expand All @@ -148,9 +146,6 @@ func (a *AreaEffectCloudBehaviour) applyEffects(pos mgl64.Vec3, e *Ent, entities
// subtractTickRadius grows the cloud's radius by the radiusTickGrowth value. If the
// radius goes under 1/2, it will close the entity.
func (a *AreaEffectCloudBehaviour) subtractTickRadius(e *Ent) bool {
e.mu.Lock()
defer e.mu.Unlock()

a.radius += a.conf.RadiusTickGrowth
if a.radius < 0.5 {
a.stationary.close = true
Expand Down
27 changes: 12 additions & 15 deletions server/entity/bottle_of_enchanting.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ package entity
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/block/cube/trace"
"github.com/df-mc/dragonfly/server/internal/nbtconv"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/particle"
"github.com/df-mc/dragonfly/server/world/sound"
"github.com/go-gl/mathgl/mgl64"
"math/rand"
)

// NewBottleOfEnchanting ...
func NewBottleOfEnchanting(pos mgl64.Vec3, owner world.Entity) *Ent {
return Config{Behaviour: bottleOfEnchantingConf.New()}.New(BottleOfEnchantingType{}, pos)
func NewBottleOfEnchanting(opts world.EntitySpawnOpts, owner world.Entity) *world.EntityHandle {
conf := bottleOfEnchantingConf
conf.Owner = owner
return opts.New(BottleOfEnchantingType{}, conf)
}

var bottleOfEnchantingConf = ProjectileBehaviourConfig{
Expand All @@ -29,14 +29,17 @@ var bottleOfEnchantingConf = ProjectileBehaviourConfig{
// a trace.Result.
func spawnExperience(e *Ent, tx *world.Tx, target trace.Result) {
for _, orb := range NewExperienceOrbs(target.Position(), rand.Intn(9)+3) {
orb.SetVelocity(mgl64.Vec3{(rand.Float64()*0.2 - 0.1) * 2, rand.Float64() * 0.4, (rand.Float64()*0.2 - 0.1) * 2})
tx.AddEntity(orb)
}
}

// BottleOfEnchantingType is a world.EntityType for BottleOfEnchanting.
type BottleOfEnchantingType struct{}

func (t BottleOfEnchantingType) Open(tx *world.Tx, handle *world.EntityHandle, data *world.EntityData) world.Entity {
return &Ent{tx: tx, handle: handle, data: data}
}

// Glint returns true if the bottle should render with glint. It always returns
// true for bottles of enchanting.
func (BottleOfEnchantingType) Glint() bool {
Expand All @@ -49,16 +52,10 @@ func (BottleOfEnchantingType) BBox(world.Entity) cube.BBox {
return cube.Box(-0.125, 0, -0.125, 0.125, 0.25, 0.125)
}

func (BottleOfEnchantingType) DecodeNBT(m map[string]any) world.Entity {
b := NewBottleOfEnchanting(nbtconv.Vec3(m, "Pos"), nil)
b.vel = nbtconv.Vec3(m, "Motion")
return b
func (BottleOfEnchantingType) DecodeNBT(_ map[string]any, data *world.EntityData) {
data.Data = bottleOfEnchantingConf.New()
}

func (BottleOfEnchantingType) EncodeNBT(e world.Entity) map[string]any {
b := e.(*Ent)
return map[string]any{
"Pos": nbtconv.Vec3ToFloat32Slice(b.Position()),
"Motion": nbtconv.Vec3ToFloat32Slice(b.Velocity()),
}
func (BottleOfEnchantingType) EncodeNBT(data *world.EntityData) map[string]any {
return nil
}
Loading

0 comments on commit 786d930

Please sign in to comment.