diff --git a/server/entity/damage/source.go b/server/entity/damage/source.go index 765b353df..9d121d9d5 100644 --- a/server/entity/damage/source.go +++ b/server/entity/damage/source.go @@ -1,6 +1,8 @@ package damage -import "github.com/df-mc/dragonfly/server/world" +import ( + "github.com/df-mc/dragonfly/server/world" +) type ( // Source represents the source of the damage dealt to an entity. This source may be passed to the Hurt() @@ -68,6 +70,12 @@ type ( Projectile, Owner world.Entity } + // SourceThorns is used for damage caused by thorns. + SourceThorns struct { + // Owner holds the entity wearing the thorns armour. + Owner world.Entity + } + // SourceBlock is used for damage caused by a block, such as an anvil. SourceBlock struct { // Block is the block that caused the damage. @@ -103,5 +111,7 @@ func (SourceLava) ReducedByResistance() bool { return true } func (SourceLava) ReducedByArmour() bool { return true } func (SourceProjectile) ReducedByResistance() bool { return true } func (SourceProjectile) ReducedByArmour() bool { return true } +func (SourceThorns) ReducedByResistance() bool { return true } +func (SourceThorns) ReducedByArmour() bool { return false } func (SourceBlock) ReducedByResistance() bool { return true } func (SourceBlock) ReducedByArmour() bool { return true } diff --git a/server/item/enchantment/register.go b/server/item/enchantment/register.go index b47fd0371..9600c5b6f 100644 --- a/server/item/enchantment/register.go +++ b/server/item/enchantment/register.go @@ -8,7 +8,7 @@ func init() { item.RegisterEnchantment(2, FeatherFalling{}) // TODO: (3) Blast Protection. item.RegisterEnchantment(4, ProjectileProtection{}) - // TODO: (5) Thorns. + item.RegisterEnchantment(5, Thorns{}) item.RegisterEnchantment(6, Respiration{}) item.RegisterEnchantment(7, DepthStrider{}) item.RegisterEnchantment(8, AquaAffinity{}) diff --git a/server/item/enchantment/thorns.go b/server/item/enchantment/thorns.go new file mode 100644 index 000000000..ed3c7bd7b --- /dev/null +++ b/server/item/enchantment/thorns.go @@ -0,0 +1,35 @@ +package enchantment + +import ( + "github.com/df-mc/dragonfly/server/item" + "github.com/df-mc/dragonfly/server/world" +) + +// Thorns is an enchantment that inflicts damage on attackers. +type Thorns struct{} + +// Name ... +func (Thorns) Name() string { + return "Thorns" +} + +// MaxLevel ... +func (Thorns) MaxLevel() int { + return 3 +} + +// Rarity ... +func (Thorns) Rarity() item.EnchantmentRarity { + return item.EnchantmentRarityVeryRare +} + +// CompatibleWithEnchantment ... +func (Thorns) CompatibleWithEnchantment(item.EnchantmentType) bool { + return true +} + +// CompatibleWithItem ... +func (Thorns) CompatibleWithItem(i world.Item) bool { + _, ok := i.(item.Armour) + return ok +} diff --git a/server/player/player.go b/server/player/player.go index 1d79447f1..8033fe448 100644 --- a/server/player/player.go +++ b/server/player/player.go @@ -28,6 +28,7 @@ import ( "github.com/df-mc/dragonfly/server/world/sound" "github.com/go-gl/mathgl/mgl64" "github.com/google/uuid" + "golang.org/x/exp/maps" "golang.org/x/text/language" "math" "math/rand" @@ -557,11 +558,40 @@ func (p *Player) Hurt(dmg float64, source damage.Source) (float64, bool) { p.Exhaust(0.1) damageToArmour := int(math.Max(math.Floor(dmg/4), 1)) + var damageToAttacker int + thornsArmour := map[int]item.Stack{} for slot, it := range p.armour.Slots() { if _, ok := it.Item().(item.Durable); ok { + if e, ok := it.Enchantment(enchantment.Thorns{}); ok && rand.Float64() < float64(e.Level())*0.15 { + damageToArmour++ + thornsArmour[slot] = it + if e.Level() > 10 { + damageToAttacker += e.Level() - 10 + } else { + damageToAttacker += 1 + rand.Intn(4) + } + } _ = p.armour.Inventory().SetItem(slot, p.damageItem(it, damageToArmour)) } } + + if length := len(thornsArmour); length > 0 { + slot := maps.Keys(thornsArmour)[rand.Intn(length)] + item := thornsArmour[slot] + + _ = p.armour.Inventory().SetItem(slot, p.damageItem(item, 2)) + if damageToAttacker > 0 { + var attacker world.Entity + if s, ok := source.(damage.SourceEntityAttack); ok { + attacker = s.Attacker + } else if s, ok := source.(damage.SourceProjectile); ok { + attacker = s.Owner + } + if l, ok := attacker.(entity.Living); ok { + l.Hurt(float64(damageToAttacker), damage.SourceThorns{Owner: attacker}) + } + } + } } w, pos := p.World(), p.Position()