103 lines
3.1 KiB
Go
103 lines
3.1 KiB
Go
|
|
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/entity/effect"
|
||
|
|
"github.com/df-mc/dragonfly/server/item/potion"
|
||
|
|
"github.com/df-mc/dragonfly/server/world"
|
||
|
|
"github.com/go-gl/mathgl/mgl64"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
// SplashableBlock is a block that can be splashed with a splash bottle.
|
||
|
|
type SplashableBlock interface {
|
||
|
|
world.Block
|
||
|
|
// Splash is called when a water bottle splashes onto a block.
|
||
|
|
Splash(tx *world.Tx, pos cube.Pos)
|
||
|
|
}
|
||
|
|
|
||
|
|
// SplashableEntity is an entity that can be splashed with a splash bottle.
|
||
|
|
type SplashableEntity interface {
|
||
|
|
world.Entity
|
||
|
|
// Splash is called when a water bottle splashes onto an entity.
|
||
|
|
Splash(tx *world.Tx, pos mgl64.Vec3)
|
||
|
|
}
|
||
|
|
|
||
|
|
// potionSplash returns a function that creates a potion splash with a specific
|
||
|
|
// duration multiplier and potion type.
|
||
|
|
func potionSplash(durMul float64, pot potion.Potion, linger bool) func(e *Ent, tx *world.Tx, res trace.Result) {
|
||
|
|
return func(e *Ent, tx *world.Tx, res trace.Result) {
|
||
|
|
pos := e.Position()
|
||
|
|
effects := pot.Effects()
|
||
|
|
box := e.H().Type().BBox(e).Translate(pos)
|
||
|
|
|
||
|
|
if len(effects) > 0 {
|
||
|
|
for otherE := range filterLiving(tx.EntitiesWithin(box.GrowVec3(mgl64.Vec3{8.25, 4.25, 8.25}))) {
|
||
|
|
otherPos := otherE.Position()
|
||
|
|
if !otherE.H().Type().BBox(otherE).Translate(otherPos).IntersectsWith(box.GrowVec3(mgl64.Vec3{4.125, 2.125, 4.125})) {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
dist := otherPos.Sub(pos).Len()
|
||
|
|
if dist > 4 {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
f := 1 - dist/4
|
||
|
|
if entityResult, ok := res.(trace.EntityResult); ok && entityResult.Entity().H() == otherE.H() {
|
||
|
|
f = 1
|
||
|
|
}
|
||
|
|
|
||
|
|
splashed := otherE.(Living)
|
||
|
|
for _, eff := range effects {
|
||
|
|
if _, ok := eff.Type().(effect.LastingType); !ok {
|
||
|
|
splashed.AddEffect(effect.NewInstantWithPotency(eff.Type(), eff.Level(), f))
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
|
||
|
|
dur := time.Duration(float64(eff.Duration()) * durMul * f)
|
||
|
|
if dur < time.Second {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
splashed.AddEffect(effect.New(eff.Type().(effect.LastingType), eff.Level(), dur))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else if pot == potion.Water() {
|
||
|
|
switch result := res.(type) {
|
||
|
|
case trace.BlockResult:
|
||
|
|
blockPos := result.BlockPosition().Side(result.Face())
|
||
|
|
if tx.Block(blockPos) == fire() {
|
||
|
|
tx.SetBlock(blockPos, nil, nil)
|
||
|
|
}
|
||
|
|
|
||
|
|
for _, f := range cube.HorizontalFaces() {
|
||
|
|
if h := blockPos.Side(f); tx.Block(h) == fire() {
|
||
|
|
tx.SetBlock(h, nil, nil)
|
||
|
|
}
|
||
|
|
|
||
|
|
if b, ok := tx.Block(blockPos.Side(f)).(SplashableBlock); ok {
|
||
|
|
b.Splash(tx, blockPos.Side(f))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
resultPos := result.BlockPosition()
|
||
|
|
if b, ok := tx.Block(resultPos).(SplashableBlock); ok {
|
||
|
|
b.Splash(tx, resultPos)
|
||
|
|
}
|
||
|
|
case trace.EntityResult:
|
||
|
|
// TODO: Damage endermen, blazes, striders and snow golems when implemented and rehydrate axolotls.
|
||
|
|
}
|
||
|
|
|
||
|
|
for otherE := range filterLiving(tx.EntitiesWithin(box.GrowVec3(mgl64.Vec3{8.25, 4.25, 8.25}))) {
|
||
|
|
if splashE, ok := otherE.(SplashableEntity); ok {
|
||
|
|
splashE.Splash(tx, otherE.Position())
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if linger {
|
||
|
|
tx.AddEntity(NewAreaEffectCloud(world.EntitySpawnOpts{Position: pos}, pot))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|