diff --git a/server/block/ancient_debris.go b/server/block/ancient_debris.go index 75117d8a8..7aa84743c 100644 --- a/server/block/ancient_debris.go +++ b/server/block/ancient_debris.go @@ -13,7 +13,7 @@ type AncientDebris struct { func (a AncientDebris) BreakInfo() BreakInfo { return newBreakInfo(30, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierDiamond.HarvestLevel - }, pickaxeEffective, oneOf(a)) + }, pickaxeEffective, oneOf(a)).withBlastResistance(3600) } // SmeltInfo ... diff --git a/server/block/anvil.go b/server/block/anvil.go index 550052df2..4454a4022 100644 --- a/server/block/anvil.go +++ b/server/block/anvil.go @@ -27,7 +27,7 @@ func (a Anvil) Model() world.BlockModel { // BreakInfo ... func (a Anvil) BreakInfo() BreakInfo { - return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(a)) + return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(a)).withBlastResistance(6000) } // Activate ... diff --git a/server/block/basalt.go b/server/block/basalt.go index fd37b0807..47e7db466 100644 --- a/server/block/basalt.go +++ b/server/block/basalt.go @@ -32,7 +32,7 @@ func (b Basalt) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world. // BreakInfo ... func (b Basalt) BreakInfo() BreakInfo { - return newBreakInfo(1.25, pickaxeHarvestable, pickaxeEffective, oneOf(b)) + return newBreakInfo(1.25, pickaxeHarvestable, pickaxeEffective, oneOf(b)).withBlastResistance(21) } // EncodeItem ... diff --git a/server/block/block.go b/server/block/block.go index 1625a12cf..3438f9f6f 100644 --- a/server/block/block.go +++ b/server/block/block.go @@ -3,10 +3,12 @@ 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/entity" + "github.com/df-mc/dragonfly/server/entity/damage" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" "github.com/df-mc/dragonfly/server/world/sound" + "github.com/go-gl/mathgl/mgl64" + "math/rand" "time" ) @@ -84,7 +86,7 @@ func calculateFace(user item.User, placePos cube.Pos) cube.Face { pos := cube.PosFromVec3(userPos) if abs(pos[0]-placePos[0]) < 2 && abs(pos[2]-placePos[2]) < 2 { y := userPos[1] - if eyed, ok := user.(entity.Eyed); ok { + if eyed, ok := user.(interface{ EyeHeight() float64 }); ok { y += eyed.EyeHeight() } @@ -203,7 +205,17 @@ func (g gravityAffected) fall(b world.Block, pos cube.Pos, w *world.World) { _, liquid := w.Liquid(pos.Side(cube.FaceDown)) if air || liquid { w.SetBlock(pos, nil, nil) - w.AddEntity(entity.NewFallingBlock(b, pos.Vec3Middle())) + + ent, ok := world.EntityByName("minecraft:falling_block") + if !ok { + return + } + + if p, ok := ent.(interface { + New(bl world.Block, pos mgl64.Vec3) world.Entity + }); ok { + w.AddEntity(p.New(b, pos.Vec3Centre())) + } } } @@ -232,6 +244,43 @@ func newFlammabilityInfo(encouragement, flammability int, lavaFlammable bool) Fl } } +// livingEntity ... +type livingEntity interface { + // AttackImmune checks if the entity is currently immune to entity attacks. Entities typically turn + // immune for half a second after being attacked. + AttackImmune() bool + // Hurt hurts the entity for a given amount of damage. The source passed represents the cause of the + // damage, for example damage.SourceEntityAttack if the entity is attacked by another entity. + // If the final damage exceeds the health that the entity currently has, the entity is killed. + // Hurt returns the final amount of damage dealt to the Living entity and returns whether the Living entity + // was vulnerable to the damage at all. + Hurt(damage float64, src damage.Source) (n float64, vulnerable bool) +} + +// flammableEntity ... +type flammableEntity interface { + // OnFireDuration returns duration of fire in ticks. + OnFireDuration() time.Duration + // SetOnFire sets the entity on fire for the specified duration. + SetOnFire(duration time.Duration) + // Extinguish extinguishes the entity. + Extinguish() +} + +// dropItem ... +func dropItem(w *world.World, it item.Stack, pos mgl64.Vec3) { + ent, ok := world.EntityByName("minecraft:item") + if !ok { + return + } + + if p, ok := ent.(interface { + New(it item.Stack, pos, vel mgl64.Vec3) world.Entity + }); ok { + w.AddEntity(p.New(it, pos, mgl64.Vec3{rand.Float64()*0.2 - 0.1, 0.2, rand.Float64()*0.2 - 0.1})) + } +} + // bass is a struct that may be embedded for blocks that create a bass sound. type bass struct{} diff --git a/server/block/break_info.go b/server/block/break_info.go index 189d815d9..211dfe741 100644 --- a/server/block/break_info.go +++ b/server/block/break_info.go @@ -85,15 +85,20 @@ type BreakInfo struct { Drops func(t item.Tool, enchantments []item.Enchantment) []item.Stack // XPDrops is the range of XP a block can drop when broken. XPDrops XPDropRange + // BlastResistance is the blast resistance of the block, which influences the block's ability to withstand an + // explosive blast. + BlastResistance float64 } -// newBreakInfo creates a BreakInfo struct with the properties passed. The XPDrops field is 0 by default. +// newBreakInfo creates a BreakInfo struct with the properties passed. The XPDrops field is 0 by default. The blast +// resistance is set to the block's hardness*5 by default. func newBreakInfo(hardness float64, harvestable func(item.Tool) bool, effective func(item.Tool) bool, drops func(item.Tool, []item.Enchantment) []item.Stack) BreakInfo { return BreakInfo{ - Hardness: hardness, - Harvestable: harvestable, - Effective: effective, - Drops: drops, + Hardness: hardness, + BlastResistance: hardness * 5, + Harvestable: harvestable, + Effective: effective, + Drops: drops, } } @@ -103,6 +108,12 @@ func (b BreakInfo) withXPDropRange(min, max int) BreakInfo { return b } +// withBlastResistance sets the BlastResistance field of the BreakInfo struct to the passed value. +func (b BreakInfo) withBlastResistance(res float64) BreakInfo { + b.BlastResistance = res + return b +} + // XPDropRange holds the min & max XP drop amounts of blocks. type XPDropRange [2]int diff --git a/server/block/bricks.go b/server/block/bricks.go index da87c0eda..3266513d7 100644 --- a/server/block/bricks.go +++ b/server/block/bricks.go @@ -8,7 +8,7 @@ type Bricks struct { // BreakInfo ... func (b Bricks) BreakInfo() BreakInfo { - return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(b)) + return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(b)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/cactus.go b/server/block/cactus.go index 1a5be8f15..295cab1dd 100644 --- a/server/block/cactus.go +++ b/server/block/cactus.go @@ -3,7 +3,6 @@ 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/entity" "github.com/df-mc/dragonfly/server/entity/damage" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" @@ -78,7 +77,7 @@ func (c Cactus) RandomTick(pos cube.Pos, w *world.World, r *rand.Rand) { // EntityInside ... func (c Cactus) EntityInside(_ cube.Pos, _ *world.World, e world.Entity) { - if l, ok := e.(entity.Living); ok && !l.AttackImmune() { + if l, ok := e.(livingEntity); ok && !l.AttackImmune() { l.Hurt(0.5, damage.SourceBlock{Block: c}) } } diff --git a/server/block/chain.go b/server/block/chain.go index 749786526..d196460ec 100644 --- a/server/block/chain.go +++ b/server/block/chain.go @@ -41,7 +41,7 @@ func (c Chain) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.W // BreakInfo ... func (c Chain) BreakInfo() BreakInfo { - return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(c)) + return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(c)).withBlastResistance(15) } // EncodeItem ... diff --git a/server/block/coal.go b/server/block/coal.go index fd2eb43bc..870e0a178 100644 --- a/server/block/coal.go +++ b/server/block/coal.go @@ -13,7 +13,7 @@ type Coal struct { // BreakInfo ... func (c Coal) BreakInfo() BreakInfo { - return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(c)) + return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(c)).withBlastResistance(30) } // FlammabilityInfo ... diff --git a/server/block/coal_ore.go b/server/block/coal_ore.go index 7c625cbc1..8ce825756 100644 --- a/server/block/coal_ore.go +++ b/server/block/coal_ore.go @@ -13,7 +13,11 @@ type CoalOre struct { // BreakInfo ... func (c CoalOre) BreakInfo() BreakInfo { - return newBreakInfo(c.Type.Hardness(), pickaxeHarvestable, pickaxeEffective, silkTouchOneOf(item.Coal{}, c)).withXPDropRange(0, 2) + i := newBreakInfo(c.Type.Hardness(), pickaxeHarvestable, pickaxeEffective, silkTouchOneOf(item.Coal{}, c)).withXPDropRange(0, 2) + if c.Type == DeepslateOre() { + i = i.withBlastResistance(9) + } + return i } // SmeltInfo ... diff --git a/server/block/cobblestone.go b/server/block/cobblestone.go index 78ebe1254..3abff59b5 100644 --- a/server/block/cobblestone.go +++ b/server/block/cobblestone.go @@ -14,7 +14,7 @@ type Cobblestone struct { // BreakInfo ... func (c Cobblestone) BreakInfo() BreakInfo { - return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(c)) + return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(c)).withBlastResistance(30) } // SmeltInfo ... diff --git a/server/block/cobblestone_stairs.go b/server/block/cobblestone_stairs.go index b49fb8a09..497655498 100644 --- a/server/block/cobblestone_stairs.go +++ b/server/block/cobblestone_stairs.go @@ -45,7 +45,7 @@ func (s CobblestoneStairs) Model() world.BlockModel { // BreakInfo ... func (s CobblestoneStairs) BreakInfo() BreakInfo { - return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(s)) + return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(s)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/cocoa_bean.go b/server/block/cocoa_bean.go index f56f312f4..8ca3d4296 100644 --- a/server/block/cocoa_bean.go +++ b/server/block/cocoa_bean.go @@ -92,7 +92,7 @@ func (c CocoaBean) BreakInfo() BreakInfo { return []item.Stack{item.NewStack(c, rand.Intn(2)+2)} } return []item.Stack{item.NewStack(c, 1)} - }) + }).withBlastResistance(15) } // EncodeItem ... diff --git a/server/block/copper_ore.go b/server/block/copper_ore.go index 231e30082..d68099b74 100644 --- a/server/block/copper_ore.go +++ b/server/block/copper_ore.go @@ -18,7 +18,7 @@ type CopperOre struct { func (c CopperOre) BreakInfo() BreakInfo { return newBreakInfo(c.Type.Hardness(), func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierStone.HarvestLevel - }, pickaxeEffective, silkTouchDrop(item.NewStack(item.RawCopper{}, rand.Intn(4)+2), item.NewStack(c, 1))) + }, pickaxeEffective, silkTouchDrop(item.NewStack(item.RawCopper{}, rand.Intn(4)+2), item.NewStack(c, 1))).withBlastResistance(9) } // SmeltInfo ... diff --git a/server/block/coral_block.go b/server/block/coral_block.go index c15f5fde4..f351b0c32 100644 --- a/server/block/coral_block.go +++ b/server/block/coral_block.go @@ -48,7 +48,7 @@ func (c CoralBlock) ScheduledTick(pos cube.Pos, w *world.World, _ *rand.Rand) { // BreakInfo ... func (c CoralBlock) BreakInfo() BreakInfo { - return newBreakInfo(7, pickaxeHarvestable, pickaxeEffective, silkTouchOneOf(CoralBlock{Type: c.Type, Dead: true}, c)) + return newBreakInfo(7, pickaxeHarvestable, pickaxeEffective, silkTouchOneOf(CoralBlock{Type: c.Type, Dead: true}, c)).withBlastResistance(4.5) } // EncodeBlock ... diff --git a/server/block/crop.go b/server/block/crop.go index 6f7407c27..26de4361f 100644 --- a/server/block/crop.go +++ b/server/block/crop.go @@ -2,11 +2,8 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" - "github.com/go-gl/mathgl/mgl64" - "math/rand" ) // Crop is an interface for all crops that are grown on farmland. A crop has a random chance to grow during random ticks. @@ -33,9 +30,7 @@ func (c crop) NeighbourUpdateTick(pos, _ cube.Pos, w *world.World) { w.SetBlock(pos, nil, nil) if breakable, ok := b.(Breakable); ok { for _, drop := range breakable.BreakInfo().Drops(item.ToolNone{}, []item.Enchantment{}) { - itemEntity := entity.NewItem(drop, pos.Vec3Centre()) - itemEntity.SetVelocity(mgl64.Vec3{rand.Float64()*0.2 - 0.1, 0.2, rand.Float64()*0.2 - 0.1}) - w.AddEntity(itemEntity) + dropItem(w, drop, pos.Vec3Centre()) } } } diff --git a/server/block/diamond.go b/server/block/diamond.go index b46d84e01..cd89c09f9 100644 --- a/server/block/diamond.go +++ b/server/block/diamond.go @@ -13,7 +13,7 @@ type Diamond struct { func (d Diamond) BreakInfo() BreakInfo { return newBreakInfo(5, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierIron.HarvestLevel - }, pickaxeEffective, oneOf(d)) + }, pickaxeEffective, oneOf(d)).withBlastResistance(30) } // PowersBeacon ... diff --git a/server/block/diamond_ore.go b/server/block/diamond_ore.go index d7fa7e49b..e4c2d1728 100644 --- a/server/block/diamond_ore.go +++ b/server/block/diamond_ore.go @@ -15,9 +15,13 @@ type DiamondOre struct { // BreakInfo ... func (d DiamondOre) BreakInfo() BreakInfo { - return newBreakInfo(d.Type.Hardness(), func(t item.Tool) bool { + i := newBreakInfo(d.Type.Hardness(), func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierIron.HarvestLevel }, pickaxeEffective, silkTouchOneOf(item.Diamond{}, d)).withXPDropRange(3, 7) + if d.Type == DeepslateOre() { + i = i.withBlastResistance(9) + } + return i } // SmeltInfo ... diff --git a/server/block/double_flower.go b/server/block/double_flower.go index e1a8e16e1..11416b058 100644 --- a/server/block/double_flower.go +++ b/server/block/double_flower.go @@ -2,12 +2,10 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" "github.com/df-mc/dragonfly/server/world/particle" "github.com/go-gl/mathgl/mgl64" - "math/rand" ) // DoubleFlower is a two block high flower consisting of an upper and lower part. @@ -28,9 +26,7 @@ func (d DoubleFlower) FlammabilityInfo() FlammabilityInfo { // BoneMeal ... func (d DoubleFlower) BoneMeal(pos cube.Pos, w *world.World) bool { - itemEntity := entity.NewItem(item.NewStack(d, 1), pos.Vec3Centre()) - itemEntity.SetVelocity(mgl64.Vec3{rand.Float64()*0.2 - 0.1, 0.2, rand.Float64()*0.2 - 0.1}) - w.AddEntity(itemEntity) + dropItem(w, item.NewStack(d, 1), pos.Vec3Centre()) return true } diff --git a/server/block/dripstone.go b/server/block/dripstone.go index 673721f97..b3bbcb2e7 100644 --- a/server/block/dripstone.go +++ b/server/block/dripstone.go @@ -8,7 +8,7 @@ type Dripstone struct { // BreakInfo ... func (d Dripstone) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(d)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(d)).withBlastResistance(5) } // EncodeItem ... diff --git a/server/block/emerald.go b/server/block/emerald.go index 0f39697e8..bebdcb9db 100644 --- a/server/block/emerald.go +++ b/server/block/emerald.go @@ -19,7 +19,7 @@ func (e Emerald) Instrument() sound.Instrument { func (e Emerald) BreakInfo() BreakInfo { return newBreakInfo(5, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierIron.HarvestLevel - }, pickaxeEffective, oneOf(e)) + }, pickaxeEffective, oneOf(e)).withBlastResistance(30) } // PowersBeacon ... diff --git a/server/block/emerald_ore.go b/server/block/emerald_ore.go index 487a48790..6600f3743 100644 --- a/server/block/emerald_ore.go +++ b/server/block/emerald_ore.go @@ -15,9 +15,13 @@ type EmeraldOre struct { // BreakInfo ... func (e EmeraldOre) BreakInfo() BreakInfo { - return newBreakInfo(e.Type.Hardness(), func(t item.Tool) bool { + i := newBreakInfo(e.Type.Hardness(), func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierIron.HarvestLevel }, pickaxeEffective, silkTouchOneOf(item.Emerald{}, e)).withXPDropRange(3, 7) + if e.Type == DeepslateOre() { + i = i.withBlastResistance(15) + } + return i } // SmeltInfo ... diff --git a/server/block/end_brick_stairs.go b/server/block/end_brick_stairs.go index a0aea5070..45478c35d 100644 --- a/server/block/end_brick_stairs.go +++ b/server/block/end_brick_stairs.go @@ -42,7 +42,7 @@ func (s EndBrickStairs) Model() world.BlockModel { // BreakInfo ... func (s EndBrickStairs) BreakInfo() BreakInfo { - return newBreakInfo(3, pickaxeHarvestable, pickaxeEffective, oneOf(s)) + return newBreakInfo(3, pickaxeHarvestable, pickaxeEffective, oneOf(s)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/end_stone.go b/server/block/end_stone.go index 254f0b454..98224ed1c 100644 --- a/server/block/end_stone.go +++ b/server/block/end_stone.go @@ -8,7 +8,7 @@ type EndStone struct { // BreakInfo ... func (e EndStone) BreakInfo() BreakInfo { - return newBreakInfo(3, pickaxeHarvestable, pickaxeEffective, oneOf(e)) + return newBreakInfo(3, pickaxeHarvestable, pickaxeEffective, oneOf(e)).withBlastResistance(45) } // EncodeItem ... diff --git a/server/block/ender_chest.go b/server/block/ender_chest.go index ad647bbae..37b2f012f 100644 --- a/server/block/ender_chest.go +++ b/server/block/ender_chest.go @@ -36,7 +36,7 @@ func NewEnderChest() EnderChest { // BreakInfo ... func (c EnderChest) BreakInfo() BreakInfo { - return newBreakInfo(22.5, pickaxeHarvestable, pickaxeEffective, silkTouchDrop(item.NewStack(Obsidian{}, 8), item.NewStack(NewEnderChest(), 1))) + return newBreakInfo(22.5, pickaxeHarvestable, pickaxeEffective, silkTouchDrop(item.NewStack(Obsidian{}, 8), item.NewStack(NewEnderChest(), 1))).withBlastResistance(3000) } // LightEmissionLevel ... diff --git a/server/block/explosion.go b/server/block/explosion.go new file mode 100644 index 000000000..f1d321c53 --- /dev/null +++ b/server/block/explosion.go @@ -0,0 +1,188 @@ +package block + +import ( + "github.com/df-mc/dragonfly/server/block/cube" + "github.com/df-mc/dragonfly/server/block/cube/trace" + "github.com/df-mc/dragonfly/server/item" + "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" + "math/rand" + "time" +) + +// ExplosionConfig is the configuration for an explosion. The world, position, size, sound, particle, and more can all +// be configured through this configuration. +type ExplosionConfig struct { + // Size is the size of the explosion, it is effectively the radius which entities/blocks will be affected within. + Size float64 + // Rand is the source to use for the explosion "randomness". + Rand rand.Source + // SpawnFire will cause the explosion to randomly start fires in 1/3 of all destroyed air blocks that are + // above opaque blocks. + SpawnFire bool + + // Sound is the sound to play when the explosion is created. If set to nil, this will default to the sound of a + // regular explosion. + Sound world.Sound + // Particle is the particle to spawn when the explosion is created. If set to nil, this will default to the particle + // of a regular huge explosion. + Particle world.Particle +} + +// ExplodableEntity represents an entity that can be exploded. +type ExplodableEntity interface { + // Explode is called when an explosion occurs. The entity can then react to the explosion using the configuration + // and impact provided. + Explode(explosionPos mgl64.Vec3, impact float64, c ExplosionConfig) +} + +// Explodable represents a block that can be exploded. +type Explodable interface { + // Explode is called when an explosion occurs. The block can react to the explosion using the configuration passed. + Explode(explosionPos mgl64.Vec3, pos cube.Pos, w *world.World, c ExplosionConfig) +} + +// rays ... +var rays = make([]mgl64.Vec3, 0, 1352) + +// init ... +func init() { + for x := 0.0; x < 16; x++ { + for y := 0.0; y < 16; y++ { + for z := 0.0; z < 16; z++ { + if x != 0 && x != 15 && y != 0 && y != 15 && z != 0 && z != 15 { + continue + } + rays = append(rays, mgl64.Vec3{x/15*2 - 1, y/15*2 - 1, z/15*2 - 1}.Normalize().Mul(0.3)) + } + } + } +} + +// Explode performs the explosion as specified by the configuration. +func (c ExplosionConfig) Explode(w *world.World, explosionPos mgl64.Vec3) { + if c.Sound == nil { + c.Sound = sound.Explosion{} + } + if c.Particle == nil { + c.Particle = particle.HugeExplosion{} + } + if c.Rand == nil { + c.Rand = rand.NewSource(time.Now().UnixNano()) + } + + r, d := rand.New(c.Rand), c.Size*2 + box := cube.Box( + math.Floor(explosionPos[0]-d-1), + math.Floor(explosionPos[1]-d-1), + math.Floor(explosionPos[2]-d-1), + math.Ceil(explosionPos[0]+d+1), + math.Ceil(explosionPos[1]+d+1), + math.Ceil(explosionPos[2]+d+1), + ) + + for _, e := range w.EntitiesWithin(box.Grow(2), nil) { + pos := e.Position() + if !e.BBox().Translate(pos).IntersectsWith(box) { + continue + } + dist := pos.Sub(pos).Len() + if dist >= d { + continue + } + if explodable, ok := e.(ExplodableEntity); ok { + impact := (1 - dist/d) * exposure(pos, e) + explodable.Explode(explosionPos, impact, c) + } + } + + affectedBlocks := make([]cube.Pos, 0, 32) + for _, ray := range rays { + pos := explosionPos + for blastForce := c.Size * (0.7 + r.Float64()*0.6); blastForce > 0.0; blastForce -= 0.225 { + current := cube.PosFromVec3(pos) + if r, ok := w.Block(current).(Breakable); ok { + if blastForce -= (r.BreakInfo().BlastResistance/5 + 0.3) * 0.3; blastForce > 0 { + affectedBlocks = append(affectedBlocks, current) + } + } + pos = pos.Add(ray) + } + } + for _, pos := range affectedBlocks { + bl := w.Block(pos) + if explodable, ok := bl.(Explodable); ok { + explodable.Explode(explosionPos, pos, w, c) + } else if breakable, ok := bl.(Breakable); ok { + w.SetBlock(pos, nil, nil) + if 1/c.Size > r.Float64() { + for _, drop := range breakable.BreakInfo().Drops(item.ToolNone{}, nil) { + dropItem(w, drop, pos.Vec3Centre()) + } + } + } + } + if c.SpawnFire { + for _, pos := range affectedBlocks { + if r.Intn(3) == 0 { + if _, ok := w.Block(pos).(Air); ok && w.Block(pos.Side(cube.FaceDown)).Model().FaceSolid(pos, cube.FaceUp, w) { + w.SetBlock(pos, Fire{}, nil) + } + } + } + } + + w.AddParticle(explosionPos, c.Particle) + w.PlaySound(explosionPos, c.Sound) +} + +// exposure returns the exposure of an explosion to an entity, used to calculate the impact of an explosion. +func exposure(origin mgl64.Vec3, e world.Entity) float64 { + w := e.World() + pos := e.Position() + box := e.BBox().Translate(pos) + + boxMin, boxMax := box.Min(), box.Max() + diff := boxMax.Sub(boxMin).Mul(2.0).Add(mgl64.Vec3{1, 1, 1}) + + step := mgl64.Vec3{1.0 / diff[0], 1.0 / diff[1], 1.0 / diff[2]} + if step[0] < 0.0 || step[1] < 0.0 || step[2] < 0.0 { + return 0.0 + } + + xOffset := (1.0 - math.Floor(diff[0])/diff[0]) / 2.0 + zOffset := (1.0 - math.Floor(diff[2])/diff[2]) / 2.0 + + var checks, misses int + for x := 0.0; x <= 1.0; x += step[0] { + for y := 0.0; y <= 1.0; y += step[1] { + for z := 0.0; z <= 1.0; z += step[2] { + point := mgl64.Vec3{ + lerp(x, boxMin[0], boxMax[0]) + xOffset, + lerp(y, boxMin[1], boxMax[1]), + lerp(z, boxMin[2], boxMax[2]) + zOffset, + } + + var collided bool + trace.TraverseBlocks(origin, point, func(pos cube.Pos) (con bool) { + _, air := w.Block(pos).(Air) + collided = !air + return air + }) + if !collided { + misses++ + } + checks++ + } + } + } + return float64(misses) / float64(checks) +} + +// lerp returns the linear interpolation between a and b at t. +func lerp(a, b, t float64) float64 { + return b + a*(t-b) +} diff --git a/server/block/farmland.go b/server/block/farmland.go index d500ea2f6..54c2027ce 100644 --- a/server/block/farmland.go +++ b/server/block/farmland.go @@ -2,7 +2,6 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/world" "math/rand" ) @@ -71,7 +70,7 @@ func (f Farmland) hydrated(pos cube.Pos, w *world.World) bool { // EntityLand ... func (f Farmland) EntityLand(pos cube.Pos, w *world.World, e world.Entity, distance *float64) { - if living, ok := e.(entity.Living); ok { + if living, ok := e.(livingEntity); ok { if fall, ok := living.(fallDistanceEntity); ok && rand.Float64() < fall.FallDistance()-0.5 { w.SetBlock(pos, Dirt{}, nil) } diff --git a/server/block/fire.go b/server/block/fire.go index ee1b1292c..acbbc9987 100644 --- a/server/block/fire.go +++ b/server/block/fire.go @@ -4,7 +4,6 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/entity/damage" "github.com/df-mc/dragonfly/server/event" "github.com/df-mc/dragonfly/server/world" @@ -195,8 +194,8 @@ func (f Fire) spread(from, to cube.Pos, w *world.World, r *rand.Rand) { // EntityInside ... func (f Fire) EntityInside(_ cube.Pos, _ *world.World, e world.Entity) { - if flammable, ok := e.(entity.Flammable); ok { - if l, ok := e.(entity.Living); ok && !l.AttackImmune() { + if flammable, ok := e.(flammableEntity); ok { + if l, ok := e.(livingEntity); ok && !l.AttackImmune() { l.Hurt(f.Type.Damage(), damage.SourceFire{}) } if flammable.OnFireDuration() < time.Second*8 { diff --git a/server/block/gilded_blackstone.go b/server/block/gilded_blackstone.go index 2611577e2..bcc5d4102 100644 --- a/server/block/gilded_blackstone.go +++ b/server/block/gilded_blackstone.go @@ -17,7 +17,7 @@ func (b GildedBlackstone) BreakInfo() BreakInfo { return []item.Stack{item.NewStack(item.GoldNugget{}, rand.Intn(4)+2)} } return []item.Stack{item.NewStack(b, 1)} - }) + }).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/gold.go b/server/block/gold.go index 85a676d7a..6e901e76a 100644 --- a/server/block/gold.go +++ b/server/block/gold.go @@ -19,7 +19,7 @@ func (g Gold) Instrument() sound.Instrument { func (g Gold) BreakInfo() BreakInfo { return newBreakInfo(5, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierIron.HarvestLevel - }, pickaxeEffective, oneOf(g)) + }, pickaxeEffective, oneOf(g)).withBlastResistance(30) } // PowersBeacon ... diff --git a/server/block/gold_ore.go b/server/block/gold_ore.go index 1f8370361..6d9871205 100644 --- a/server/block/gold_ore.go +++ b/server/block/gold_ore.go @@ -15,9 +15,13 @@ type GoldOre struct { // BreakInfo ... func (g GoldOre) BreakInfo() BreakInfo { - return newBreakInfo(g.Type.Hardness(), func(t item.Tool) bool { + i := newBreakInfo(g.Type.Hardness(), func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierIron.HarvestLevel }, pickaxeEffective, silkTouchOneOf(item.RawGold{}, g)) + if g.Type == DeepslateOre() { + i = i.withBlastResistance(9) + } + return i } // SmeltInfo ... diff --git a/server/block/iron.go b/server/block/iron.go index 79a401340..87903c995 100644 --- a/server/block/iron.go +++ b/server/block/iron.go @@ -19,7 +19,7 @@ func (i Iron) Instrument() sound.Instrument { func (i Iron) BreakInfo() BreakInfo { return newBreakInfo(5, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierStone.HarvestLevel - }, pickaxeEffective, oneOf(i)) + }, pickaxeEffective, oneOf(i)).withBlastResistance(30) } // PowersBeacon ... diff --git a/server/block/iron_bars.go b/server/block/iron_bars.go index 869544633..f12056c29 100644 --- a/server/block/iron_bars.go +++ b/server/block/iron_bars.go @@ -13,7 +13,7 @@ type IronBars struct { // BreakInfo ... func (i IronBars) BreakInfo() BreakInfo { - return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(i)) + return newBreakInfo(5, pickaxeHarvestable, pickaxeEffective, oneOf(i)).withBlastResistance(30) } // CanDisplace ... diff --git a/server/block/iron_ore.go b/server/block/iron_ore.go index dd6a1cbc3..25b37b0cc 100644 --- a/server/block/iron_ore.go +++ b/server/block/iron_ore.go @@ -15,9 +15,13 @@ type IronOre struct { // BreakInfo ... func (i IronOre) BreakInfo() BreakInfo { - return newBreakInfo(i.Type.Hardness(), func(t item.Tool) bool { + b := newBreakInfo(i.Type.Hardness(), func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierStone.HarvestLevel }, pickaxeEffective, silkTouchOneOf(item.RawIron{}, i)) + if i.Type == DeepslateOre() { + b = b.withBlastResistance(9) + } + return b } // SmeltInfo ... diff --git a/server/block/item_frame.go b/server/block/item_frame.go index c88b6f1dc..d7bd211ec 100644 --- a/server/block/item_frame.go +++ b/server/block/item_frame.go @@ -3,7 +3,6 @@ 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/entity" "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" @@ -60,9 +59,7 @@ func (i ItemFrame) Punch(pos cube.Pos, _ cube.Face, w *world.World, u item.User) GameMode() world.GameMode }); ok { if rand.Float64() <= i.DropChance && !g.GameMode().CreativeInventory() { - it := entity.NewItem(i.Item, pos.Vec3Centre()) - it.SetVelocity(mgl64.Vec3{rand.Float64()*0.2 - 0.1, 0.2, rand.Float64()*0.2 - 0.1}) - w.AddEntity(it) + dropItem(w, i.Item, pos.Vec3Centre()) } } i.Item, i.Rotations = item.Stack{}, 0 diff --git a/server/block/lapis_ore.go b/server/block/lapis_ore.go index b336beb6d..c8b427117 100644 --- a/server/block/lapis_ore.go +++ b/server/block/lapis_ore.go @@ -16,9 +16,13 @@ type LapisOre struct { // BreakInfo ... func (l LapisOre) BreakInfo() BreakInfo { - return newBreakInfo(l.Type.Hardness(), func(t item.Tool) bool { + i := newBreakInfo(l.Type.Hardness(), func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierStone.HarvestLevel }, pickaxeEffective, silkTouchDrop(item.NewStack(item.LapisLazuli{}, rand.Intn(5)+4), item.NewStack(l, 1))).withXPDropRange(2, 5) + if l.Type == DeepslateOre() { + i = i.withBlastResistance(9) + } + return i } // SmeltInfo ... diff --git a/server/block/lava.go b/server/block/lava.go index acf565cbb..f62c2154c 100644 --- a/server/block/lava.go +++ b/server/block/lava.go @@ -2,7 +2,6 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/entity/damage" "github.com/df-mc/dragonfly/server/event" "github.com/df-mc/dragonfly/server/world" @@ -42,8 +41,8 @@ func (l Lava) EntityInside(_ cube.Pos, _ *world.World, e world.Entity) { if fallEntity, ok := e.(fallDistanceEntity); ok { fallEntity.ResetFallDistance() } - if flammable, ok := e.(entity.Flammable); ok { - if l, ok := e.(entity.Living); ok && !l.AttackImmune() { + if flammable, ok := e.(flammableEntity); ok { + if l, ok := e.(livingEntity); ok && !l.AttackImmune() { l.Hurt(4, damage.SourceLava{}) } flammable.SetOnFire(15 * time.Second) diff --git a/server/block/liquid.go b/server/block/liquid.go index d6e41e743..48065049a 100644 --- a/server/block/liquid.go +++ b/server/block/liquid.go @@ -2,13 +2,10 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/event" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/world" - "github.com/go-gl/mathgl/mgl64" "math" - "math/rand" "sync" ) @@ -146,9 +143,7 @@ func flowInto(b world.Liquid, src, pos cube.Pos, w *world.World, falling bool) b if removable.HasLiquidDrops() { if b, ok := existing.(Breakable); ok { for _, d := range b.BreakInfo().Drops(item.ToolNone{}, nil) { - itemEntity := entity.NewItem(d, pos.Vec3Centre()) - itemEntity.SetVelocity(mgl64.Vec3{rand.Float64()*0.2 - 0.1, 0.2, rand.Float64()*0.2 - 0.1}) - w.AddEntity(itemEntity) + dropItem(w, d, pos.Vec3Centre()) } } else { panic("liquid drops should always implement breakable") diff --git a/server/block/nether_brick_fence.go b/server/block/nether_brick_fence.go index f02294afe..c0cc58db7 100644 --- a/server/block/nether_brick_fence.go +++ b/server/block/nether_brick_fence.go @@ -13,7 +13,7 @@ type NetherBrickFence struct { // BreakInfo ... func (n NetherBrickFence) BreakInfo() BreakInfo { - return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(n)) + return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(n)).withBlastResistance(30) } // CanDisplace ... diff --git a/server/block/nether_bricks.go b/server/block/nether_bricks.go index 520b792f1..3b12f6423 100644 --- a/server/block/nether_bricks.go +++ b/server/block/nether_bricks.go @@ -17,7 +17,7 @@ type NetherBricks struct { // BreakInfo ... func (n NetherBricks) BreakInfo() BreakInfo { - return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(n)) + return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(n)).withBlastResistance(30) } // SmeltInfo ... diff --git a/server/block/netherite.go b/server/block/netherite.go index 7eb9c2f46..3900ab172 100644 --- a/server/block/netherite.go +++ b/server/block/netherite.go @@ -14,7 +14,7 @@ type Netherite struct { func (n Netherite) BreakInfo() BreakInfo { return newBreakInfo(50, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierDiamond.HarvestLevel - }, pickaxeEffective, oneOf(n)) + }, pickaxeEffective, oneOf(n)).withBlastResistance(3600) } // PowersBeacon ... diff --git a/server/block/obsidian.go b/server/block/obsidian.go index 71f34b121..0a076116e 100644 --- a/server/block/obsidian.go +++ b/server/block/obsidian.go @@ -41,5 +41,5 @@ func (o Obsidian) EncodeBlock() (string, map[string]any) { func (o Obsidian) BreakInfo() BreakInfo { return newBreakInfo(50, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierDiamond.HarvestLevel - }, pickaxeEffective, oneOf(o)) + }, pickaxeEffective, oneOf(o)).withBlastResistance(6000) } diff --git a/server/block/ore_type.go b/server/block/ore_type.go index ba55b4973..cd9b20e41 100644 --- a/server/block/ore_type.go +++ b/server/block/ore_type.go @@ -66,7 +66,7 @@ func (o ore) Hardness() float64 { case 0: return 3 case 1: - return 4.5 + return 3.5 } panic("unknown ore type") } diff --git a/server/block/packed_mud.go b/server/block/packed_mud.go index 174ee21bf..e3f388763 100644 --- a/server/block/packed_mud.go +++ b/server/block/packed_mud.go @@ -7,7 +7,7 @@ type PackedMud struct { // BreakInfo ... func (p PackedMud) BreakInfo() BreakInfo { - return newBreakInfo(1, alwaysHarvestable, nothingEffective, oneOf(p)) + return newBreakInfo(1, alwaysHarvestable, nothingEffective, oneOf(p)).withBlastResistance(15) } // EncodeItem ... diff --git a/server/block/planks.go b/server/block/planks.go index f761382a4..8ee59b760 100644 --- a/server/block/planks.go +++ b/server/block/planks.go @@ -26,7 +26,7 @@ func (p Planks) FlammabilityInfo() FlammabilityInfo { // BreakInfo ... func (p Planks) BreakInfo() BreakInfo { - return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(p)) + return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(p)).withBlastResistance(15) } // RepairsWoodTools ... diff --git a/server/block/prismarine.go b/server/block/prismarine.go index e1083b1fe..63d2ad079 100644 --- a/server/block/prismarine.go +++ b/server/block/prismarine.go @@ -15,7 +15,7 @@ type Prismarine struct { // BreakInfo ... func (p Prismarine) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(p)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(p)).withBlastResistance(30) } // EncodeItem ... @@ -33,6 +33,5 @@ func allPrismarine() (c []world.Block) { for _, t := range PrismarineTypes() { c = append(c, Prismarine{Type: t}) } - return } diff --git a/server/block/purpur.go b/server/block/purpur.go index bf4d9a2c2..3f786dccb 100644 --- a/server/block/purpur.go +++ b/server/block/purpur.go @@ -25,7 +25,7 @@ type ( // BreakInfo ... func (p Purpur) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(p)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(p)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/raw_copper.go b/server/block/raw_copper.go index 3cf16748f..97fdb1c2d 100644 --- a/server/block/raw_copper.go +++ b/server/block/raw_copper.go @@ -14,7 +14,7 @@ type RawCopper struct { func (r RawCopper) BreakInfo() BreakInfo { return newBreakInfo(5, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierStone.HarvestLevel - }, pickaxeEffective, oneOf(r)) + }, pickaxeEffective, oneOf(r)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/raw_gold.go b/server/block/raw_gold.go index 37205a1b9..c47082bbd 100644 --- a/server/block/raw_gold.go +++ b/server/block/raw_gold.go @@ -14,7 +14,7 @@ type RawGold struct { func (g RawGold) BreakInfo() BreakInfo { return newBreakInfo(5, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierIron.HarvestLevel - }, pickaxeEffective, oneOf(g)) + }, pickaxeEffective, oneOf(g)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/raw_iron.go b/server/block/raw_iron.go index f420e9293..100919ee8 100644 --- a/server/block/raw_iron.go +++ b/server/block/raw_iron.go @@ -14,7 +14,7 @@ type RawIron struct { func (r RawIron) BreakInfo() BreakInfo { return newBreakInfo(5, func(t item.Tool) bool { return t.ToolType() == item.TypePickaxe && t.HarvestLevel() >= item.ToolTierStone.HarvestLevel - }, pickaxeEffective, oneOf(r)) + }, pickaxeEffective, oneOf(r)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/reinforced_deepslate.go b/server/block/reinforced_deepslate.go index 62e13035d..c3c9e0623 100644 --- a/server/block/reinforced_deepslate.go +++ b/server/block/reinforced_deepslate.go @@ -8,7 +8,7 @@ type ReinforcedDeepslate struct { // BreakInfo ... func (r ReinforcedDeepslate) BreakInfo() BreakInfo { - return newBreakInfo(55, alwaysHarvestable, nothingEffective, oneOf(r)) + return newBreakInfo(55, alwaysHarvestable, nothingEffective, oneOf(r)).withBlastResistance(3600) } // EncodeItem ... diff --git a/server/block/sandstone_stairs.go b/server/block/sandstone_stairs.go index ea39de403..9a1dbf2ed 100644 --- a/server/block/sandstone_stairs.go +++ b/server/block/sandstone_stairs.go @@ -49,7 +49,11 @@ func (s SandstoneStairs) Model() world.BlockModel { // BreakInfo ... func (s SandstoneStairs) BreakInfo() BreakInfo { - return newBreakInfo(s.Type.Hardness(), pickaxeHarvestable, pickaxeEffective, oneOf(s)) + i := newBreakInfo(s.Type.Hardness(), pickaxeHarvestable, pickaxeEffective, oneOf(s)) + if s.Type == SmoothSandstone() { + i = i.withBlastResistance(30) + } + return i } // EncodeItem ... diff --git a/server/block/stained_terracotta.go b/server/block/stained_terracotta.go index f85cde6e1..2715474c6 100644 --- a/server/block/stained_terracotta.go +++ b/server/block/stained_terracotta.go @@ -23,7 +23,7 @@ func (t StainedTerracotta) SoilFor(block world.Block) bool { // BreakInfo ... func (t StainedTerracotta) BreakInfo() BreakInfo { - return newBreakInfo(1.25, pickaxeHarvestable, pickaxeEffective, oneOf(t)) + return newBreakInfo(1.25, pickaxeHarvestable, pickaxeEffective, oneOf(t)).withBlastResistance(21) } // SmeltInfo ... diff --git a/server/block/stone.go b/server/block/stone.go index 5479ad525..b84d19d7b 100644 --- a/server/block/stone.go +++ b/server/block/stone.go @@ -32,24 +32,24 @@ type ( // BreakInfo ... func (s Stone) BreakInfo() BreakInfo { if s.Smooth { - return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(s)) + return newBreakInfo(2, pickaxeHarvestable, pickaxeEffective, oneOf(s)).withBlastResistance(30) } - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, silkTouchOneOf(Cobblestone{}, Stone{})) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, silkTouchOneOf(Cobblestone{}, Stone{})).withBlastResistance(30) } // BreakInfo ... func (g Granite) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(g)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(g)).withBlastResistance(30) } // BreakInfo ... func (d Diorite) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(d)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(d)).withBlastResistance(30) } // BreakInfo ... func (a Andesite) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(a)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(a)).withBlastResistance(30) } // SmeltInfo ... diff --git a/server/block/stone_brick_stairs.go b/server/block/stone_brick_stairs.go index 82e302ce9..5178c8d1f 100644 --- a/server/block/stone_brick_stairs.go +++ b/server/block/stone_brick_stairs.go @@ -44,7 +44,7 @@ func (s StoneBrickStairs) Model() world.BlockModel { // BreakInfo ... func (s StoneBrickStairs) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(s)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(s)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/stone_bricks.go b/server/block/stone_bricks.go index f84bbb281..65ff6d581 100644 --- a/server/block/stone_bricks.go +++ b/server/block/stone_bricks.go @@ -17,7 +17,7 @@ type StoneBricks struct { // BreakInfo ... func (s StoneBricks) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(s)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(s)).withBlastResistance(30) } // SmeltInfo ... diff --git a/server/block/terracotta.go b/server/block/terracotta.go index 2ba779a9b..0490068a9 100644 --- a/server/block/terracotta.go +++ b/server/block/terracotta.go @@ -17,7 +17,7 @@ func (Terracotta) SoilFor(block world.Block) bool { // BreakInfo ... func (t Terracotta) BreakInfo() BreakInfo { - return newBreakInfo(1.25, pickaxeHarvestable, pickaxeEffective, oneOf(t)) + return newBreakInfo(1.25, pickaxeHarvestable, pickaxeEffective, oneOf(t)).withBlastResistance(21) } // EncodeItem ... diff --git a/server/block/tuff.go b/server/block/tuff.go index 19d1c8c49..24457951a 100644 --- a/server/block/tuff.go +++ b/server/block/tuff.go @@ -8,7 +8,7 @@ type Tuff struct { // BreakInfo ... func (t Tuff) BreakInfo() BreakInfo { - return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(t)) + return newBreakInfo(1.5, pickaxeHarvestable, pickaxeEffective, oneOf(t)).withBlastResistance(30) } // EncodeItem ... diff --git a/server/block/water.go b/server/block/water.go index 316ec1f2f..caa2c3474 100644 --- a/server/block/water.go +++ b/server/block/water.go @@ -2,7 +2,6 @@ package block import ( "github.com/df-mc/dragonfly/server/block/cube" - "github.com/df-mc/dragonfly/server/entity" "github.com/df-mc/dragonfly/server/event" "github.com/df-mc/dragonfly/server/item" "github.com/df-mc/dragonfly/server/item/potion" @@ -32,7 +31,7 @@ func (w Water) EntityInside(_ cube.Pos, _ *world.World, e world.Entity) { if fallEntity, ok := e.(fallDistanceEntity); ok { fallEntity.ResetFallDistance() } - if flammable, ok := e.(entity.Flammable); ok { + if flammable, ok := e.(flammableEntity); ok { flammable.Extinguish() } } diff --git a/server/block/wood_fence.go b/server/block/wood_fence.go index d7b233af0..58bdc1c08 100644 --- a/server/block/wood_fence.go +++ b/server/block/wood_fence.go @@ -21,7 +21,7 @@ type WoodFence struct { // BreakInfo ... func (w WoodFence) BreakInfo() BreakInfo { - return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(w)) + return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(w)).withBlastResistance(15) } // CanDisplace ... diff --git a/server/block/wood_fence_gate.go b/server/block/wood_fence_gate.go index 1c4d675bf..9c0d65f04 100644 --- a/server/block/wood_fence_gate.go +++ b/server/block/wood_fence_gate.go @@ -28,7 +28,7 @@ type WoodFenceGate struct { // BreakInfo ... func (f WoodFenceGate) BreakInfo() BreakInfo { - return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(f)) + return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(f)).withBlastResistance(15) } // FlammabilityInfo ... diff --git a/server/block/wood_slab.go b/server/block/wood_slab.go index f26352d9c..811c049ae 100644 --- a/server/block/wood_slab.go +++ b/server/block/wood_slab.go @@ -81,7 +81,7 @@ func (s WoodSlab) BreakInfo() BreakInfo { return []item.Stack{item.NewStack(s, 2)} } return []item.Stack{item.NewStack(s, 1)} - }) + }).withBlastResistance(15) } // FuelInfo ... diff --git a/server/block/wood_stairs.go b/server/block/wood_stairs.go index c10eb3bf6..da4e12530 100644 --- a/server/block/wood_stairs.go +++ b/server/block/wood_stairs.go @@ -55,7 +55,7 @@ func (s WoodStairs) Model() world.BlockModel { // BreakInfo ... func (s WoodStairs) BreakInfo() BreakInfo { - return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(s)) + return newBreakInfo(2, alwaysHarvestable, axeEffective, oneOf(s)).withBlastResistance(15) } // FuelInfo ... diff --git a/server/entity/arrow.go b/server/entity/arrow.go index 98112cae1..ce5e84625 100644 --- a/server/entity/arrow.go +++ b/server/entity/arrow.go @@ -1,6 +1,7 @@ package entity import ( + "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/cube/trace" "github.com/df-mc/dragonfly/server/entity/damage" @@ -232,6 +233,13 @@ func (a *Arrow) New(pos, vel mgl64.Vec3, yaw, pitch float64, owner world.Entity, return arrow } +// Explode ... +func (a *Arrow) Explode(explosionPos mgl64.Vec3, impact float64, _ block.ExplosionConfig) { + a.mu.Lock() + a.vel = a.vel.Add(a.pos.Sub(explosionPos).Normalize().Mul(impact)) + a.mu.Unlock() +} + // Owner returns the world.Entity that fired the Arrow, or nil if it did not have any. func (a *Arrow) Owner() world.Entity { a.mu.Lock() diff --git a/server/entity/damage/source.go b/server/entity/damage/source.go index 9d121d9d5..79dc1ee52 100644 --- a/server/entity/damage/source.go +++ b/server/entity/damage/source.go @@ -81,6 +81,9 @@ type ( // Block is the block that caused the damage. Block world.Block } + + // SourceExplosion is used for damage caused by an explosion. + SourceExplosion struct{} ) func (SourceFall) ReducedByArmour() bool { return false } @@ -115,3 +118,5 @@ func (SourceThorns) ReducedByResistance() bool { return true } func (SourceThorns) ReducedByArmour() bool { return false } func (SourceBlock) ReducedByResistance() bool { return true } func (SourceBlock) ReducedByArmour() bool { return true } +func (SourceExplosion) ReducedByResistance() bool { return true } +func (SourceExplosion) ReducedByArmour() bool { return true } diff --git a/server/entity/ender_pearl.go b/server/entity/ender_pearl.go index 29fd2bb78..418cb2ad9 100644 --- a/server/entity/ender_pearl.go +++ b/server/entity/ender_pearl.go @@ -1,6 +1,7 @@ package entity import ( + "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/cube/trace" "github.com/df-mc/dragonfly/server/entity/damage" @@ -131,6 +132,13 @@ func (e *EnderPearl) New(pos, vel mgl64.Vec3, yaw, pitch float64) world.Entity { return pearl } +// Explode ... +func (e *EnderPearl) Explode(explosionPos mgl64.Vec3, impact float64, _ block.ExplosionConfig) { + e.mu.Lock() + e.vel = e.vel.Add(e.pos.Sub(explosionPos).Normalize().Mul(impact)) + e.mu.Unlock() +} + // Owner ... func (e *EnderPearl) Owner() world.Entity { e.mu.Lock() diff --git a/server/entity/experience_orb.go b/server/entity/experience_orb.go index 00634b713..2a39dfeb8 100644 --- a/server/entity/experience_orb.go +++ b/server/entity/experience_orb.go @@ -1,6 +1,7 @@ package entity import ( + "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/world" @@ -136,6 +137,11 @@ func (e *ExperienceOrb) Tick(w *world.World, current int64) { } } +// Explode ... +func (e *ExperienceOrb) Explode(mgl64.Vec3, float64, block.ExplosionConfig) { + _ = e.Close() +} + // DecodeNBT decodes the properties in a map to an Item and returns a new Item entity. func (e *ExperienceOrb) DecodeNBT(data map[string]any) any { o := NewExperienceOrb(nbtconv.MapVec3(data, "Pos"), int(nbtconv.Map[int32](data, "Value"))) diff --git a/server/entity/falling_block.go b/server/entity/falling_block.go index b491df6db..f19e2ec1f 100644 --- a/server/entity/falling_block.go +++ b/server/entity/falling_block.go @@ -3,6 +3,7 @@ package entity import ( "fmt" "github.com/df-mc/atomic" + "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/entity/damage" "github.com/df-mc/dragonfly/server/internal/nbtconv" @@ -129,6 +130,17 @@ func (f *FallingBlock) Tick(w *world.World, _ int64) { } } +// New creates and returns an FallingBlock with the world.Block and position provided. It doesn't spawn the FallingBlock +// by itself. +func (f *FallingBlock) New(bl world.Block, pos mgl64.Vec3) world.Entity { + return NewFallingBlock(bl, pos) +} + +// Explode ... +func (f *FallingBlock) Explode(mgl64.Vec3, float64, block.ExplosionConfig) { + _ = f.Close() +} + // DecodeNBT decodes the relevant data from the entity NBT passed and returns a new FallingBlock entity. func (f *FallingBlock) DecodeNBT(data map[string]any) any { b := nbtconv.MapBlock(data, "FallingBlock") diff --git a/server/entity/item.go b/server/entity/item.go index 33072912d..d184abfb3 100644 --- a/server/entity/item.go +++ b/server/entity/item.go @@ -2,6 +2,7 @@ package entity import ( "fmt" + "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/internal/nbtconv" "github.com/df-mc/dragonfly/server/item" @@ -166,6 +167,19 @@ func (it *Item) collect(w *world.World, collector Collector, pos mgl64.Vec3) { _ = it.Close() } +// New creates and returns an Item with the item.Stack, position, and velocity provided. It doesn't spawn the Item +// by itself. +func (it *Item) New(stack item.Stack, pos, vel mgl64.Vec3) world.Entity { + itemEntity := NewItem(stack, pos) + itemEntity.vel = vel + return itemEntity +} + +// Explode ... +func (it *Item) Explode(mgl64.Vec3, float64, block.ExplosionConfig) { + _ = it.Close() +} + // DecodeNBT decodes the properties in a map to an Item and returns a new Item entity. func (it *Item) DecodeNBT(data map[string]any) any { i := nbtconv.MapItem(data, "Item") diff --git a/server/entity/snowball.go b/server/entity/snowball.go index d2eb7aa84..d93e76bec 100644 --- a/server/entity/snowball.go +++ b/server/entity/snowball.go @@ -1,6 +1,7 @@ package entity import ( + "github.com/df-mc/dragonfly/server/block" "github.com/df-mc/dragonfly/server/block/cube" "github.com/df-mc/dragonfly/server/block/cube/trace" "github.com/df-mc/dragonfly/server/entity/damage" @@ -112,6 +113,13 @@ func (s *Snowball) New(pos, vel mgl64.Vec3, yaw, pitch float64) world.Entity { return snow } +// Explode ... +func (s *Snowball) Explode(explosionPos mgl64.Vec3, impact float64, _ block.ExplosionConfig) { + s.mu.Lock() + s.vel = s.vel.Add(s.pos.Sub(explosionPos).Normalize().Mul(impact)) + s.mu.Unlock() +} + // Owner ... func (s *Snowball) Owner() world.Entity { s.mu.Lock() diff --git a/server/entity/splash_potion.go b/server/entity/splash_potion.go index 20d46c29c..b1b61ddf2 100644 --- a/server/entity/splash_potion.go +++ b/server/entity/splash_potion.go @@ -1,6 +1,7 @@ package entity import ( + "github.com/df-mc/dragonfly/server/block" "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" @@ -176,6 +177,13 @@ func (s *SplashPotion) New(pos, vel mgl64.Vec3, yaw, pitch float64, t potion.Pot return splash } +// Explode ... +func (s *SplashPotion) Explode(explosionPos mgl64.Vec3, impact float64, _ block.ExplosionConfig) { + s.mu.Lock() + s.vel = s.vel.Add(s.pos.Sub(explosionPos).Normalize().Mul(impact)) + s.mu.Unlock() +} + // Owner ... func (s *SplashPotion) Owner() world.Entity { s.mu.Lock() diff --git a/server/player/player.go b/server/player/player.go index a61dc4fe3..37fbf8cd2 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -655,6 +655,13 @@ func (p *Player) FinalDamageFrom(dmg float64, src damage.Source) float64 { return math.Max(dmg, 0) } +// Explode ... +func (p *Player) Explode(explosionPos mgl64.Vec3, impact float64, c block.ExplosionConfig) { + diff := p.Position().Sub(explosionPos) + p.Hurt(math.Floor((impact*impact+impact)*3.5*c.Size+1), damage.SourceExplosion{}) + p.knockBack(explosionPos, impact, diff[1]/diff.Len()*impact) +} + // protectionEnchantment represents an enchantment that can protect the player from damage. type protectionEnchantment interface { Affects(damage.Source) bool @@ -708,13 +715,19 @@ func (p *Player) KnockBack(src mgl64.Vec3, force, height float64) { if p.Dead() || !p.GameMode().AllowsTakingDamage() { return } + p.knockBack(src, force, height) +} + +// knockBack is an unexported function that is used to knock the player back. This function does not check if the player +// can take damage or not. +func (p *Player) knockBack(src mgl64.Vec3, force, height float64) { velocity := p.Position().Sub(src) velocity[1] = 0 velocity = velocity.Normalize().Mul(force) velocity[1] = height - resistance := 0.0 + var resistance float64 for _, i := range p.armour.Items() { if a, ok := i.Item().(item.Armour); ok { resistance += a.KnockBackResistance()