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

This commit is contained in:
2026-07-09 08:33:57 +08:00
commit 26ed99fda6
845 changed files with 75419 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
package item
import "github.com/sandertv/gophertunnel/minecraft/text"
// AmethystShard is a crystalline mineral obtained from mining a fully grown amethyst cluster.
type AmethystShard struct{}
// EncodeItem ...
func (AmethystShard) EncodeItem() (name string, meta int16) {
return "minecraft:amethyst_shard", 0
}
// TrimMaterial ...
func (AmethystShard) TrimMaterial() string {
return "amethyst"
}
// MaterialColour ...
func (AmethystShard) MaterialColour() string {
return text.Amethyst
}
+26
View File
@@ -0,0 +1,26 @@
package item
import (
"github.com/df-mc/dragonfly/server/world"
)
// Apple is a food item that can be eaten by the player.
type Apple struct {
defaultFood
}
// Consume ...
func (a Apple) Consume(_ *world.Tx, c Consumer) Stack {
c.Saturate(4, 2.4)
return Stack{}
}
// CompostChance ...
func (Apple) CompostChance() float64 {
return 0.65
}
// EncodeItem ...
func (a Apple) EncodeItem() (name string, meta int16) {
return "minecraft:apple", 0
}
+149
View File
@@ -0,0 +1,149 @@
package item
import "image/color"
type (
// Armour represents an item that may be worn as armour. Generally, these items provide armour points, which
// reduce damage taken. Some pieces of armour also provide toughness, which negates damage proportional to
// the total damage dealt.
Armour interface {
// DefencePoints returns the defence points that the armour provides when worn.
DefencePoints() float64
// Toughness returns the toughness that the armour provides when worn. The toughness reduces defence reduction
// caused by increased damage.
Toughness() float64
// KnockBackResistance returns a number from 0-1 that decides the amount of knock back force that is
// resisted upon being attacked. 1 knock back resistance point client-side translates to 10% knock back
// reduction.
KnockBackResistance() float64
}
// ArmourTier represents the tier, or material, that a piece of armour is made of.
ArmourTier interface {
// BaseDurability is the base durability of armour with this tier. This is otherwise the durability of
// the helmet with this tier.
BaseDurability() float64
// Toughness reduces the defence reduction caused by damage increases.
Toughness() float64
// KnockBackResistance is a number from 0-1 that decides the amount of knock back force that is resisted
// upon being attacked. 1 knock back resistance point client-side translates to 10% knock back reduction.
KnockBackResistance() float64
// EnchantmentValue is the enchantment value of the armour used when selecting pseudo-random enchantments for
// enchanting tables. When this value is high, the enchantments that are selected are more likely to be good.
EnchantmentValue() int
// Name is the name of the tier.
Name() string
}
// HelmetType is an Armour item that can be worn in the helmet slot.
HelmetType interface {
Armour
Helmet() bool
}
// ChestplateType is an Armour item that can be worn in the chestplate slot.
ChestplateType interface {
Armour
Chestplate() bool
}
// LeggingsType are an Armour item that can be worn in the leggings slot.
LeggingsType interface {
Armour
Leggings() bool
}
// BootsType are an Armour item that can be worn in the boots slot.
BootsType interface {
Armour
Boots() bool
}
)
// ArmourTierLeather is the ArmourTier of leather armour
type ArmourTierLeather struct {
// Colour is the dyed colour of the armour.
Colour color.RGBA
}
func (ArmourTierLeather) BaseDurability() float64 { return 55 }
func (ArmourTierLeather) Toughness() float64 { return 0 }
func (ArmourTierLeather) KnockBackResistance() float64 { return 0 }
func (ArmourTierLeather) EnchantmentValue() int { return 15 }
func (ArmourTierLeather) Name() string { return "leather" }
// ArmourTierCopper is the ArmourTier of copper armour.
type ArmourTierCopper struct{}
func (ArmourTierCopper) BaseDurability() float64 { return 121 }
func (ArmourTierCopper) Toughness() float64 { return 0 }
func (ArmourTierCopper) KnockBackResistance() float64 { return 0 }
func (ArmourTierCopper) EnchantmentValue() int { return 8 }
func (ArmourTierCopper) Name() string { return "copper" }
// ArmourTierGold is the ArmourTier of gold armour.
type ArmourTierGold struct{}
func (ArmourTierGold) BaseDurability() float64 { return 77 }
func (ArmourTierGold) Toughness() float64 { return 0 }
func (ArmourTierGold) KnockBackResistance() float64 { return 0 }
func (ArmourTierGold) EnchantmentValue() int { return 25 }
func (ArmourTierGold) Name() string { return "golden" }
// ArmourTierChain is the ArmourTier of chain armour.
type ArmourTierChain struct{}
func (ArmourTierChain) BaseDurability() float64 { return 166 }
func (ArmourTierChain) Toughness() float64 { return 0 }
func (ArmourTierChain) KnockBackResistance() float64 { return 0 }
func (ArmourTierChain) EnchantmentValue() int { return 12 }
func (ArmourTierChain) Name() string { return "chainmail" }
// ArmourTierIron is the ArmourTier of iron armour.
type ArmourTierIron struct{}
func (ArmourTierIron) BaseDurability() float64 { return 165 }
func (ArmourTierIron) Toughness() float64 { return 0 }
func (ArmourTierIron) KnockBackResistance() float64 { return 0 }
func (ArmourTierIron) EnchantmentValue() int { return 9 }
func (ArmourTierIron) Name() string { return "iron" }
// ArmourTierDiamond is the ArmourTier of diamond armour.
type ArmourTierDiamond struct{}
func (ArmourTierDiamond) BaseDurability() float64 { return 363 }
func (ArmourTierDiamond) Toughness() float64 { return 2 }
func (ArmourTierDiamond) KnockBackResistance() float64 { return 0 }
func (ArmourTierDiamond) EnchantmentValue() int { return 10 }
func (ArmourTierDiamond) Name() string { return "diamond" }
// ArmourTierNetherite is the ArmourTier of netherite armour.
type ArmourTierNetherite struct{}
func (ArmourTierNetherite) BaseDurability() float64 { return 408 }
func (ArmourTierNetherite) Toughness() float64 { return 3 }
func (ArmourTierNetherite) KnockBackResistance() float64 { return 0.1 }
func (ArmourTierNetherite) EnchantmentValue() int { return 15 }
func (ArmourTierNetherite) Name() string { return "netherite" }
// ArmourTiers returns a list of all armour tiers.
func ArmourTiers() []ArmourTier {
return []ArmourTier{ArmourTierLeather{}, ArmourTierCopper{}, ArmourTierGold{}, ArmourTierChain{}, ArmourTierIron{}, ArmourTierDiamond{}, ArmourTierNetherite{}}
}
// armourTierRepairable returns true if the ArmourTier passed is repairable.
func armourTierRepairable(tier ArmourTier) func(Stack) bool {
return func(stack Stack) bool {
var ok bool
switch tier.(type) {
case ArmourTierLeather:
_, ok = stack.Item().(Leather)
case ArmourTierCopper:
_, ok = stack.Item().(CopperIngot)
case ArmourTierGold:
_, ok = stack.Item().(GoldIngot)
case ArmourTierChain, ArmourTierIron:
_, ok = stack.Item().(IronIngot)
case ArmourTierDiamond:
_, ok = stack.Item().(Diamond)
case ArmourTierNetherite:
_, ok = stack.Item().(NetheriteIngot)
}
return ok
}
}
+77
View File
@@ -0,0 +1,77 @@
package item
import "github.com/df-mc/dragonfly/server/world"
// ArmourTrim is a decorative addition to an armour piece. It consists of a
// template that specifies the pattern and a material that specifies the colour.
type ArmourTrim struct {
Template SmithingTemplateType
Material ArmourTrimMaterial
}
// Zero checks if an ArmourTrim is considered zero: Either its material is nil
// or its template TemplateNetheriteUpgrade.
func (trim ArmourTrim) Zero() bool {
return trim.Material == nil || trim.Template == TemplateNetheriteUpgrade()
}
// ArmourTrimMaterial is the material of an ArmourTrim, such as an IronIngot,
// that modifies the colour of an ArmourTrim.
type ArmourTrimMaterial interface {
// TrimMaterial returns the material name used for reading and writing trim data.
TrimMaterial() string
// MaterialColour returns the colour code used for internal text formatting.
MaterialColour() string
}
// trimMaterialFromString returns a TrimMaterial from a string.
func trimMaterialFromString(name string) (ArmourTrimMaterial, bool) {
switch name {
case "amethyst":
return AmethystShard{}, true
case "copper":
return CopperIngot{}, true
case "diamond":
return Diamond{}, true
case "emerald":
return Emerald{}, true
case "gold":
return GoldIngot{}, true
case "iron":
return IronIngot{}, true
case "lapis":
return LapisLazuli{}, true
case "netherite":
return NetheriteIngot{}, true
case "quartz":
return NetherQuartz{}, true
case "resin":
return ResinBrick{}, true
case "redstone":
return RedstoneWire{}, true
}
return nil, false
}
// ArmourTrimMaterials returns all the items that can be trim materials.
func ArmourTrimMaterials() []world.Item {
return []world.Item{
AmethystShard{},
CopperIngot{},
Diamond{},
Emerald{},
GoldIngot{},
IronIngot{},
LapisLazuli{},
NetheriteIngot{},
NetherQuartz{},
ResinBrick{},
RedstoneWire{},
}
}
// Trimmable represents an item, generally Armour, that can have an ArmourTrim
// applied to it in a smithing table.
type Trimmable interface {
WithTrim(trim ArmourTrim) world.Item
}
+23
View File
@@ -0,0 +1,23 @@
package item
import "github.com/df-mc/dragonfly/server/item/potion"
// Arrow is used as ammunition for bows, crossbows, and dispensers. Arrows can be modified to
// imbue status effects on players and mobs.
type Arrow struct {
// Tip is the potion effect that is tipped on the arrow.
Tip potion.Potion
}
// EncodeItem ...
func (a Arrow) EncodeItem() (name string, meta int16) {
if tip := a.Tip.Uint8(); tip > 4 {
return "minecraft:arrow", int16(tip + 1)
}
return "minecraft:arrow", 0
}
// OffHand ...
func (Arrow) OffHand() bool {
return true
}
+113
View File
@@ -0,0 +1,113 @@
package item
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/sound"
"github.com/go-gl/mathgl/mgl64"
"time"
)
// Axe is a tool generally used for mining wood-like blocks. It may also be used to break some plant-like
// blocks at a faster pace such as pumpkins.
type Axe struct {
// Tier is the tier of the axe.
Tier ToolTier
}
// UseOnBlock handles the stripping of logs when a player clicks a log with an axe.
func (a Axe) UseOnBlock(pos cube.Pos, _ cube.Face, _ mgl64.Vec3, tx *world.Tx, _ User, ctx *UseContext) bool {
if s, ok := tx.Block(pos).(Strippable); ok {
if res, so, ok := s.Strip(); ok {
tx.SetBlock(pos, res, nil)
tx.PlaySound(pos.Vec3(), sound.ItemUseOn{Block: res})
if so != nil {
tx.PlaySound(pos.Vec3(), so)
}
ctx.DamageItem(1)
return true
}
}
return false
}
// Strippable represents a block that can be stripped by right-clicking it with
// an axe.
type Strippable interface {
// Strip returns a block that is the result of stripping it. Alternatively,
// the bool returned may be false to indicate the block couldn't be
// stripped.
Strip() (world.Block, world.Sound, bool)
}
// MaxCount always returns 1.
func (a Axe) MaxCount() int {
return 1
}
// DurabilityInfo ...
func (a Axe) DurabilityInfo() DurabilityInfo {
return DurabilityInfo{
MaxDurability: a.Tier.Durability,
BrokenItem: simpleItem(Stack{}),
AttackDurability: 2,
BreakDurability: 1,
}
}
// SmeltInfo ...
func (a Axe) SmeltInfo() SmeltInfo {
switch a.Tier {
case ToolTierIron:
return newOreSmeltInfo(NewStack(IronNugget{}, 1), 0.1)
case ToolTierGold:
return newOreSmeltInfo(NewStack(GoldNugget{}, 1), 0.1)
case ToolTierCopper:
return newOreSmeltInfo(NewStack(CopperNugget{}, 1), 0.1)
}
return SmeltInfo{}
}
// FuelInfo ...
func (a Axe) FuelInfo() FuelInfo {
if a.Tier == ToolTierWood {
return newFuelInfo(time.Second * 10)
}
return FuelInfo{}
}
// AttackDamage ...
func (a Axe) AttackDamage() float64 {
return a.Tier.BaseAttackDamage + 2
}
// ToolType ...
func (a Axe) ToolType() ToolType {
return TypeAxe
}
// HarvestLevel ...
func (a Axe) HarvestLevel() int {
return a.Tier.HarvestLevel
}
// BaseMiningEfficiency ...
func (a Axe) BaseMiningEfficiency(world.Block) float64 {
return a.Tier.BaseMiningEfficiency
}
// RepairableBy ...
func (a Axe) RepairableBy(i Stack) bool {
return toolTierRepairable(a.Tier)(i)
}
// EnchantmentValue ...
func (a Axe) EnchantmentValue() int {
return a.Tier.EnchantmentValue
}
// EncodeItem ...
func (a Axe) EncodeItem() (name string, meta int16) {
return "minecraft:" + a.Tier.Name + "_axe", 0
}
+24
View File
@@ -0,0 +1,24 @@
package item
import "github.com/df-mc/dragonfly/server/world"
// BakedPotato is a food item that can be eaten by the player.
type BakedPotato struct {
defaultFood
}
// Consume ...
func (BakedPotato) Consume(_ *world.Tx, c Consumer) Stack {
c.Saturate(5, 6)
return Stack{}
}
// CompostChance ...
func (BakedPotato) CompostChance() float64 {
return 0.85
}
// EncodeItem ...
func (BakedPotato) EncodeItem() (name string, meta int16) {
return "minecraft:baked_potato", 0
}
+18
View File
@@ -0,0 +1,18 @@
package item
// BannerPattern is an item used to customise banners inside looms.
type BannerPattern struct {
// Type represents the type of banner pattern. These types do not include all patterns that can be applied to a
// banner.
Type BannerPatternType
}
// MaxCount always returns 1.
func (b BannerPattern) MaxCount() int {
return 1
}
// EncodeItem ...
func (b BannerPattern) EncodeItem() (name string, meta int16) {
return "minecraft:" + b.Type.String() + "_banner_pattern", 0
}
+106
View File
@@ -0,0 +1,106 @@
package item
// BannerPatternType represents a type of BannerPattern.
type BannerPatternType struct {
bannerPatternType
}
// CreeperBannerPattern represents the 'Creeper' banner pattern type.
func CreeperBannerPattern() BannerPatternType {
return BannerPatternType{0}
}
// SkullBannerPattern represents the 'Skull' banner pattern type.
func SkullBannerPattern() BannerPatternType {
return BannerPatternType{1}
}
// FlowerBannerPattern represents the 'Flower' banner pattern type.
func FlowerBannerPattern() BannerPatternType {
return BannerPatternType{2}
}
// MojangBannerPattern represents the 'Mojang' banner pattern type.
func MojangBannerPattern() BannerPatternType {
return BannerPatternType{3}
}
// FieldMasonedBannerPattern represents the 'Field Masoned' banner pattern type.
func FieldMasonedBannerPattern() BannerPatternType {
return BannerPatternType{4}
}
// BordureIndentedBannerPattern represents the 'Bordure Indented' banner pattern type.
func BordureIndentedBannerPattern() BannerPatternType {
return BannerPatternType{5}
}
// PiglinBannerPattern represents the 'Piglin' banner pattern type.
func PiglinBannerPattern() BannerPatternType {
return BannerPatternType{6}
}
// GlobeBannerPattern represents the 'Globe' banner pattern type.
func GlobeBannerPattern() BannerPatternType {
return BannerPatternType{7}
}
// FlowBannerPattern represents the 'Flow' banner pattern type.
func FlowBannerPattern() BannerPatternType {
return BannerPatternType{8}
}
// GusterBannerPattern represents the 'Guster' banner pattern type.
func GusterBannerPattern() BannerPatternType {
return BannerPatternType{9}
}
// BannerPatterns returns all possible banner patterns.
func BannerPatterns() []BannerPatternType {
return []BannerPatternType{
CreeperBannerPattern(),
SkullBannerPattern(),
FlowerBannerPattern(),
MojangBannerPattern(),
FieldMasonedBannerPattern(),
BordureIndentedBannerPattern(),
PiglinBannerPattern(),
GlobeBannerPattern(),
FlowBannerPattern(),
GusterBannerPattern(),
}
}
type bannerPatternType uint8
// Uint8 returns the uint8 value of the banner pattern type.
func (b bannerPatternType) Uint8() uint8 {
return uint8(b)
}
// String ...
func (b bannerPatternType) String() string {
switch b {
case 0:
return "creeper"
case 1:
return "skull"
case 2:
return "flower"
case 3:
return "mojang"
case 4:
return "field_masoned"
case 5:
return "bordure_indented"
case 6:
return "piglin"
case 7:
return "globe"
case 8:
return "flow"
case 9:
return "guster"
}
panic("should never happen")
}
+37
View File
@@ -0,0 +1,37 @@
package item
import "github.com/df-mc/dragonfly/server/world"
// Beef is a food item obtained from cows. It can be cooked in a furnace, smoker, or campfire.
type Beef struct {
defaultFood
// Cooked is whether the beef is cooked.
Cooked bool
}
// Consume ...
func (b Beef) Consume(_ *world.Tx, c Consumer) Stack {
if b.Cooked {
c.Saturate(8, 12.8)
} else {
c.Saturate(3, 1.8)
}
return Stack{}
}
// SmeltInfo ...
func (b Beef) SmeltInfo() SmeltInfo {
if b.Cooked {
return SmeltInfo{}
}
return newFoodSmeltInfo(NewStack(Beef{Cooked: true}, 1), 0.35)
}
// EncodeItem ...
func (b Beef) EncodeItem() (name string, meta int16) {
if b.Cooked {
return "minecraft:cooked_beef", 0
}
return "minecraft:beef", 0
}
+26
View File
@@ -0,0 +1,26 @@
package item
import (
"github.com/df-mc/dragonfly/server/world"
)
// Beetroot is a food and dye ingredient.
type Beetroot struct {
defaultFood
}
// Consume ...
func (b Beetroot) Consume(_ *world.Tx, c Consumer) Stack {
c.Saturate(1, 1.2)
return Stack{}
}
// CompostChance ...
func (Beetroot) CompostChance() float64 {
return 0.65
}
// EncodeItem ...
func (b Beetroot) EncodeItem() (name string, meta int16) {
return "minecraft:beetroot", 0
}
+26
View File
@@ -0,0 +1,26 @@
package item
import (
"github.com/df-mc/dragonfly/server/world"
)
// BeetrootSoup is an unstackable food item.
type BeetrootSoup struct {
defaultFood
}
// MaxCount ...
func (BeetrootSoup) MaxCount() int {
return 1
}
// Consume ...
func (BeetrootSoup) Consume(_ *world.Tx, c Consumer) Stack {
c.Saturate(6, 7.2)
return NewStack(Bowl{}, 1)
}
// EncodeItem ...
func (BeetrootSoup) EncodeItem() (name string, meta int16) {
return "minecraft:beetroot_soup", 0
}
+9
View File
@@ -0,0 +1,9 @@
package item
// BlazePowder is an item made from a blaze rod obtained from blazes.
type BlazePowder struct{}
// EncodeItem ...
func (BlazePowder) EncodeItem() (name string, meta int16) {
return "minecraft:blaze_powder", 0
}
+16
View File
@@ -0,0 +1,16 @@
package item
import "time"
// BlazeRod is an item exclusively obtained from blazes.
type BlazeRod struct{}
// FuelInfo ...
func (BlazeRod) FuelInfo() FuelInfo {
return newFuelInfo(time.Second * 120)
}
// EncodeItem ...
func (BlazeRod) EncodeItem() (name string, meta int16) {
return "minecraft:blaze_rod", 0
}
+9
View File
@@ -0,0 +1,9 @@
package item
// Bone is an item primarily obtained as a drop from skeletons and their variants.
type Bone struct{}
// EncodeItem ...
func (Bone) EncodeItem() (name string, meta int16) {
return "minecraft:bone", 0
}
+52
View File
@@ -0,0 +1,52 @@
package item
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/particle"
"github.com/go-gl/mathgl/mgl64"
)
// BoneMealResult represents the outcome of a bone meal interaction with a block,
// determining the intensity of the particle effect displayed.
type BoneMealResult int
const (
// BoneMealResultNone indicates that the bone meal had no effect on the block.
BoneMealResultNone BoneMealResult = iota
// BoneMealResultSmall indicates a minor growth effect, produces a small particle burst.
BoneMealResultSmall
// BoneMealResultArea indicates a significant growth effect over an area, produces a large particle burst.
BoneMealResultArea
)
// BoneMeal is an item used to force growth in plants & crops.
type BoneMeal struct{}
// BoneMealAffected represents a block that is affected when bone meal is used on it.
type BoneMealAffected interface {
// BoneMeal attempts to affect the block using a bone meal item.
BoneMeal(pos cube.Pos, tx *world.Tx) BoneMealResult
}
// UseOnBlock ...
func (b BoneMeal) UseOnBlock(pos cube.Pos, _ cube.Face, _ mgl64.Vec3, tx *world.Tx, _ User, ctx *UseContext) bool {
if bm, ok := tx.Block(pos).(BoneMealAffected); ok {
result := bm.BoneMeal(pos, tx)
if result == BoneMealResultNone {
return false
}
ctx.SubtractFromCount(1)
tx.AddParticle(pos.Vec3(), particle.BoneMeal{
Area: result == BoneMealResultArea,
})
return true
}
return false
}
// EncodeItem ...
func (b BoneMeal) EncodeItem() (name string, meta int16) {
return "minecraft:bone_meal", 0
}
+14
View File
@@ -0,0 +1,14 @@
package item
// Book is an item used in enchanting and crafting.
type Book struct{}
// EnchantmentValue ...
func (b Book) EnchantmentValue() int {
return 1
}
// EncodeItem ...
func (Book) EncodeItem() (name string, meta int16) {
return "minecraft:book", 0
}
+116
View File
@@ -0,0 +1,116 @@
package item
import "slices"
// BookAndQuill is an item used to write WrittenBook(s).
type BookAndQuill struct {
// Pages represents the pages within the book.
Pages []string
}
// MaxCount always returns 1.
func (BookAndQuill) MaxCount() int {
return 1
}
// TotalPages returns the total number of pages in the book.
func (b BookAndQuill) TotalPages() int {
return len(b.Pages)
}
// Page returns a specific page from the book and true when the page exists. It will otherwise return an empty string
// and false.
func (b BookAndQuill) Page(page int) (string, bool) {
if page < 0 || len(b.Pages) <= page {
return "", false
}
return b.Pages[page], true
}
// DeletePage attempts to delete a page from the book.
func (b BookAndQuill) DeletePage(page int) BookAndQuill {
if page < 0 || page >= 50 {
panic("invalid page number")
}
if _, ok := b.Page(page); !ok {
panic("cannot delete nonexistent page")
}
b.Pages = slices.Delete(b.Pages, page, page+1)
return b
}
// InsertPage attempts to insert a page within the book
func (b BookAndQuill) InsertPage(page int, text string) BookAndQuill {
if page < 0 || page >= 50 {
panic("invalid page number")
}
if len(text) > 256 {
panic("text longer then 256 bytes")
}
if page > len(b.Pages) {
panic("unable to insert page at invalid position")
}
b.Pages = slices.Insert(b.Pages, page, text)
return b
}
// SetPage writes a page to the book, if the page doesn't exist it will be created. It will panic if the
// text is longer then 256 characters. It will return a new book representing this data.
func (b BookAndQuill) SetPage(page int, text string) BookAndQuill {
if page < 0 || page >= 50 {
panic("invalid page number")
}
if len(text) > 256 {
panic("text longer then 256 bytes")
}
if _, ok := b.Page(page); !ok {
pages := make([]string, page+1)
copy(pages, b.Pages)
b.Pages = pages
}
b.Pages[page] = text
return b
}
// SwapPages swaps two different pages, it will panic if the largest of the two numbers doesn't exist. It will
// return the newly updated pages.
func (b BookAndQuill) SwapPages(pageOne, pageTwo int) BookAndQuill {
if pageOne < 0 || pageTwo < 0 {
panic("negative page number")
}
if _, ok := b.Page(max(pageOne, pageTwo)); !ok {
panic("invalid page number")
}
b.Pages[pageOne], b.Pages[pageTwo] = b.Pages[pageTwo], b.Pages[pageOne]
return b
}
// DecodeNBT ...
func (b BookAndQuill) DecodeNBT(data map[string]any) any {
pages, _ := data["pages"].([]any)
for _, page := range pages {
if pageData, ok := page.(map[string]any); ok {
if text, ok := pageData["text"].(string); ok {
b.Pages = append(b.Pages, text)
}
}
}
return b
}
// EncodeNBT ...
func (b BookAndQuill) EncodeNBT() map[string]any {
if len(b.Pages) == 0 {
return nil
}
pages := make([]any, 0, len(b.Pages))
for _, page := range b.Pages {
pages = append(pages, map[string]any{"text": page})
}
return map[string]any{"pages": pages}
}
// EncodeItem ...
func (BookAndQuill) EncodeItem() (name string, meta int16) {
return "minecraft:writable_book", 0
}
+118
View File
@@ -0,0 +1,118 @@
package item
import (
"github.com/df-mc/dragonfly/server/world"
"image/color"
)
// Boots are a defensive item that may be equipped in the boots armour slot. They come in several tiers, like
// leather, gold, chain, iron and diamond.
type Boots struct {
// Tier is the tier of the boots.
Tier ArmourTier
// Trim specifies the trim of the armour.
Trim ArmourTrim
}
// Use handles the auto-equipping of boots in the armour slot when using it.
func (b Boots) Use(_ *world.Tx, _ User, ctx *UseContext) bool {
ctx.SwapHeldWithArmour(3)
return false
}
// MaxCount always returns 1.
func (b Boots) MaxCount() int {
return 1
}
// DurabilityInfo ...
func (b Boots) DurabilityInfo() DurabilityInfo {
return DurabilityInfo{
MaxDurability: int(b.Tier.BaseDurability() + b.Tier.BaseDurability()/5.5),
BrokenItem: simpleItem(Stack{}),
}
}
// SmeltInfo ...
func (b Boots) SmeltInfo() SmeltInfo {
switch b.Tier.(type) {
case ArmourTierIron, ArmourTierChain:
return newOreSmeltInfo(NewStack(IronNugget{}, 1), 0.1)
case ArmourTierGold:
return newOreSmeltInfo(NewStack(GoldNugget{}, 1), 0.1)
case ArmourTierCopper:
return newOreSmeltInfo(NewStack(CopperNugget{}, 1), 0.1)
}
return SmeltInfo{}
}
// RepairableBy ...
func (b Boots) RepairableBy(i Stack) bool {
return armourTierRepairable(b.Tier)(i)
}
// DefencePoints ...
func (b Boots) DefencePoints() float64 {
switch b.Tier.Name() {
case "leather", "copper", "golden", "chainmail":
return 1
case "iron":
return 2
case "diamond", "netherite":
return 3
}
panic("invalid boots tier")
}
// Toughness ...
func (b Boots) Toughness() float64 {
return b.Tier.Toughness()
}
// KnockBackResistance ...
func (b Boots) KnockBackResistance() float64 {
return b.Tier.KnockBackResistance()
}
// EnchantmentValue ...
func (b Boots) EnchantmentValue() int {
return b.Tier.EnchantmentValue()
}
// Boots ...
func (b Boots) Boots() bool {
return true
}
// WithTrim ...
func (b Boots) WithTrim(trim ArmourTrim) world.Item {
b.Trim = trim
return b
}
// EncodeItem ...
func (b Boots) EncodeItem() (name string, meta int16) {
return "minecraft:" + b.Tier.Name() + "_boots", 0
}
// DecodeNBT ...
func (b Boots) DecodeNBT(data map[string]any) any {
if t, ok := b.Tier.(ArmourTierLeather); ok {
if v, ok := data["customColor"].(int32); ok {
t.Colour = rgbaFromInt32(v)
b.Tier = t
}
}
b.Trim = readTrim(data)
return b
}
// EncodeNBT ...
func (b Boots) EncodeNBT() map[string]any {
m := map[string]any{}
if t, ok := b.Tier.(ArmourTierLeather); ok && t.Colour != (color.RGBA{}) {
m["customColor"] = int32FromRGBA(t.Colour)
}
writeTrim(m, b.Trim)
return m
}
+25
View File
@@ -0,0 +1,25 @@
package item
import (
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/sound"
)
// BottleOfEnchanting is a bottle that releases experience orbs when thrown.
type BottleOfEnchanting struct{}
// Use ...
func (b BottleOfEnchanting) Use(tx *world.Tx, user User, ctx *UseContext) bool {
create := tx.World().EntityRegistry().Config().BottleOfEnchanting
opts := world.EntitySpawnOpts{Position: eyePosition(user), Velocity: throwableOffset(user.Rotation()).Vec3().Mul(0.6)}
tx.AddEntity(create(opts, user))
tx.PlaySound(user.Position(), sound.ItemThrow{})
ctx.SubtractFromCount(1)
return true
}
// EncodeItem ...
func (b BottleOfEnchanting) EncodeItem() (name string, meta int16) {
return "minecraft:experience_bottle", 0
}
+119
View File
@@ -0,0 +1,119 @@
package item
import (
"github.com/df-mc/dragonfly/server/item/potion"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/sound"
"math"
"time"
)
// Bow is a ranged weapon that fires arrows.
type Bow struct{}
// MaxCount always returns 1.
func (Bow) MaxCount() int {
return 1
}
// DurabilityInfo ...
func (Bow) DurabilityInfo() DurabilityInfo {
return DurabilityInfo{
MaxDurability: 385,
BrokenItem: simpleItem(Stack{}),
}
}
// FuelInfo ...
func (Bow) FuelInfo() FuelInfo {
return newFuelInfo(time.Second * 10)
}
// Release ...
func (Bow) Release(releaser Releaser, tx *world.Tx, ctx *UseContext, duration time.Duration) {
creative := releaser.GameMode().CreativeInventory()
ticks := duration.Milliseconds() / 50
if ticks < 3 {
// The player must hold the bow for at least three ticks.
return
}
d := float64(ticks) / 20
force := math.Min((d*d+d*2)/3, 1)
if force < 0.1 {
// The force must be at least 0.1.
return
}
arrow, ok := ctx.FirstFunc(func(stack Stack) bool {
_, ok := stack.Item().(Arrow)
return ok
})
if !ok && !creative {
// No arrows in inventory and not in creative mode.
return
}
var tip potion.Potion
if !arrow.Empty() {
// Arrow is empty if not found in the creative inventory.
tip = arrow.Item().(Arrow).Tip
}
held, _ := releaser.HeldItems()
damage, punchLevel, burnDuration, consume := 2.0, 0, time.Duration(0), !creative
for _, enchant := range held.Enchantments() {
if f, ok := enchant.Type().(interface{ BurnDuration() time.Duration }); ok {
burnDuration = f.BurnDuration()
}
if _, ok := enchant.Type().(interface{ KnockBackMultiplier() float64 }); ok {
punchLevel = enchant.Level()
}
if p, ok := enchant.Type().(interface{ PowerDamage(int) float64 }); ok {
damage += p.PowerDamage(enchant.Level())
}
if i, ok := enchant.Type().(interface{ ConsumesArrows() bool }); ok && !i.ConsumesArrows() {
consume = false
}
}
create := tx.World().EntityRegistry().Config().Arrow
opts := world.EntitySpawnOpts{
Position: eyePosition(releaser),
Velocity: releaser.Rotation().Vec3().Mul(force * 5),
Rotation: releaser.Rotation().Neg(),
}
projectile := tx.AddEntity(create(opts, world.ArrowSpawnConfig{
Damage: damage,
Owner: releaser,
Critical: force >= 1,
ObtainArrowOnPickup: !creative && consume,
PunchLevel: punchLevel,
Tip: tip,
}))
if f, ok := projectile.(interface{ SetOnFire(duration time.Duration) }); ok {
f.SetOnFire(burnDuration)
}
ctx.DamageItem(1)
if consume {
ctx.Consume(arrow.Grow(-arrow.Count() + 1))
}
tx.PlaySound(releaser.Position(), sound.BowShoot{})
}
// EnchantmentValue ...
func (Bow) EnchantmentValue() int {
return 1
}
// Requirements returns the required items to release this item.
func (Bow) Requirements() []Stack {
return []Stack{NewStack(Arrow{}, 1)}
}
// EncodeItem ...
func (Bow) EncodeItem() (name string, meta int16) {
return "minecraft:bow", 0
}
+16
View File
@@ -0,0 +1,16 @@
package item
import "time"
// Bowl is a container that can hold certain foods.
type Bowl struct{}
// FuelInfo ...
func (Bowl) FuelInfo() FuelInfo {
return newFuelInfo(time.Second * 10)
}
// EncodeItem ...
func (Bowl) EncodeItem() (name string, meta int16) {
return "minecraft:bowl", 0
}
+24
View File
@@ -0,0 +1,24 @@
package item
import "github.com/df-mc/dragonfly/server/world"
// Bread is a food item that can be eaten by the player.
type Bread struct {
defaultFood
}
// Consume ...
func (Bread) Consume(_ *world.Tx, c Consumer) Stack {
c.Saturate(5, 6)
return Stack{}
}
// CompostChance ...
func (Bread) CompostChance() float64 {
return 0.85
}
// EncodeItem ...
func (Bread) EncodeItem() (name string, meta int16) {
return "minecraft:bread", 0
}
+14
View File
@@ -0,0 +1,14 @@
package item
// Brick is an item made from clay, and is used for making bricks and flower pots.
type Brick struct{}
// EncodeItem ...
func (b Brick) EncodeItem() (name string, meta int16) {
return "minecraft:brick", 0
}
// PotDecoration ...
func (Brick) PotDecoration() bool {
return true
}
+169
View File
@@ -0,0 +1,169 @@
package item
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/sound"
"github.com/go-gl/mathgl/mgl64"
"time"
)
// BucketContent is the content of a bucket.
type BucketContent struct {
liquid world.Liquid
milk bool
}
// LiquidBucketContent returns a new BucketContent with the liquid passed in.
func LiquidBucketContent(l world.Liquid) BucketContent {
return BucketContent{liquid: l}
}
// MilkBucketContent returns a new BucketContent with the milk flag set.
func MilkBucketContent() BucketContent {
return BucketContent{milk: true}
}
// Liquid returns the world.Liquid that a Bucket with this BucketContent places.
// If this BucketContent does not place a liquid block, false is returned.
func (b BucketContent) Liquid() (world.Liquid, bool) {
return b.liquid, b.liquid != nil
}
// String converts the BucketContent to a string.
func (b BucketContent) String() string {
if b.milk {
return "milk"
} else if b.liquid != nil {
return b.liquid.LiquidType()
}
return ""
}
// LiquidType returns the type of liquid the bucket contains.
func (b BucketContent) LiquidType() string {
if b.liquid != nil {
return b.liquid.LiquidType()
}
return "milk"
}
// Bucket is a tool used to carry water, lava and fish.
type Bucket struct {
// Content is the content that the bucket has. By default, this value resolves to an empty bucket.
Content BucketContent
}
// MaxCount returns 16.
func (b Bucket) MaxCount() int {
if b.Empty() {
return 16
}
return 1
}
// AlwaysConsumable ...
func (b Bucket) AlwaysConsumable() bool {
return b.Content.milk
}
// CanConsume ...
func (b Bucket) CanConsume() bool {
return b.Content.milk
}
// ConsumeDuration ...
func (b Bucket) ConsumeDuration() time.Duration {
return DefaultConsumeDuration
}
// Consume ...
func (b Bucket) Consume(_ *world.Tx, c Consumer) Stack {
for _, effect := range c.Effects() {
c.RemoveEffect(effect.Type())
}
return NewStack(Bucket{}, 1)
}
// Empty returns true if the bucket is empty.
func (b Bucket) Empty() bool {
return b.Content.liquid == nil && !b.Content.milk
}
// FuelInfo ...
func (b Bucket) FuelInfo() FuelInfo {
if liq := b.Content.liquid; liq != nil && liq.LiquidType() == "lava" {
return newFuelInfo(time.Second * 1000).WithResidue(NewStack(Bucket{}, 1))
}
return FuelInfo{}
}
// UseOnBlock handles the bucket filling and emptying logic.
func (b Bucket) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, tx *world.Tx, _ User, ctx *UseContext) bool {
if b.Content.milk {
return false
}
if b.Empty() {
return b.fillFrom(pos, tx, ctx)
}
liq := b.Content.liquid.WithDepth(8, false)
if bl := tx.Block(pos); canDisplace(bl, liq) || replaceableWith(bl, liq) {
tx.SetLiquid(pos, liq)
} else if bl := tx.Block(pos.Side(face)); canDisplace(bl, liq) || replaceableWith(bl, liq) {
tx.SetLiquid(pos.Side(face), liq)
} else {
return false
}
tx.PlaySound(pos.Vec3Centre(), sound.BucketEmpty{Liquid: b.Content.liquid})
ctx.NewItem = NewStack(Bucket{}, 1)
ctx.NewItemSurvivalOnly = true
ctx.SubtractFromCount(1)
return true
}
// fillFrom fills a bucket from the liquid at the position passed in the world. If there is no liquid or if
// the liquid is no source, fillFrom returns false.
func (b Bucket) fillFrom(pos cube.Pos, tx *world.Tx, ctx *UseContext) bool {
liquid, ok := tx.Liquid(pos)
if !ok {
return false
}
if liquid.LiquidDepth() != 8 || liquid.LiquidFalling() {
// Only allow picking up liquid source blocks.
return false
}
tx.SetLiquid(pos, nil)
tx.PlaySound(pos.Vec3Centre(), sound.BucketFill{Liquid: liquid})
ctx.NewItem = NewStack(Bucket{Content: LiquidBucketContent(liquid)}, 1)
ctx.NewItemSurvivalOnly = true
ctx.SubtractFromCount(1)
return true
}
// EncodeItem ...
func (b Bucket) EncodeItem() (name string, meta int16) {
if !b.Empty() {
return "minecraft:" + b.Content.String() + "_bucket", 0
}
return "minecraft:bucket", 0
}
type replaceable interface {
ReplaceableBy(b world.Block) bool
}
func replaceableWith(b world.Block, with world.Block) bool {
if r, ok := b.(replaceable); ok {
return r.ReplaceableBy(with)
}
return false
}
func canDisplace(b world.Block, liq world.Liquid) bool {
if d, ok := b.(world.LiquidDisplacer); ok {
return d.CanDisplace(liq)
}
return false
}
+14
View File
@@ -0,0 +1,14 @@
package item
// CarrotOnAStick is an item that can be used to control saddled pigs.
type CarrotOnAStick struct{}
// MaxCount ...
func (CarrotOnAStick) MaxCount() int {
return 1
}
// EncodeItem ...
func (CarrotOnAStick) EncodeItem() (name string, meta int16) {
return "minecraft:carrot_on_a_stick", 0
}
+65
View File
@@ -0,0 +1,65 @@
package category
// Category represents the category a custom item will be displayed in, in the creative inventory.
type Category struct {
group string
category uint8
}
// Construction is the first tab in the creative inventory and usually contains blocks that are more for decoration and
// building than actual functionality.
func Construction() Category {
return Category{category: 1}
}
// Nature is the fourth tab in the creative inventory and usually contains blocks and items that can be found naturally
// in vanilla-generated world.
func Nature() Category {
return Category{category: 2}
}
// Equipment is the second tab in the creative inventory and usually contains armour, weapons and tools.
func Equipment() Category {
return Category{category: 3}
}
// Items is the third tab in the creative inventory and usually contains blocks and items that do not come under any
// other category, such as minerals, mob drops, containers and redstone etc.
func Items() Category {
return Category{category: 4}
}
// Uint8 ...
func (c Category) Uint8() uint8 {
return c.category
}
// WithGroup returns the category with the provided subgroup. This can be used to put an item inside a group such as
// swords, food or different types of blocks.
func (c Category) WithGroup(group string) Category {
c.group = group
return c
}
// String ...
func (c Category) String() string {
switch c.category {
case 1:
return "construction"
case 2:
return "nature"
case 3:
return "equipment"
case 4:
return "items"
}
panic("should never happen")
}
// Group ...
func (c Category) Group() string {
if len(c.group) > 0 {
return "itemGroup.name." + c.group
}
return ""
}
+16
View File
@@ -0,0 +1,16 @@
package item
import "time"
// Charcoal is an item obtained by smelting logs or wood.
type Charcoal struct{}
// FuelInfo ...
func (Charcoal) FuelInfo() FuelInfo {
return newFuelInfo(time.Second * 80)
}
// EncodeItem ...
func (Charcoal) EncodeItem() (name string, meta int16) {
return "minecraft:charcoal", 0
}
+122
View File
@@ -0,0 +1,122 @@
package item
import (
"github.com/df-mc/dragonfly/server/world"
"image/color"
)
// Chestplate is a defensive item that may be equipped in the chestplate slot. Generally, chestplates provide
// the most defence of all armour items.
type Chestplate struct {
// Tier is the tier of the chestplate.
Tier ArmourTier
// Trim specifies the trim of the armour.
Trim ArmourTrim
}
// Use handles the using of a chestplate to auto-equip it in the designated armour slot.
func (c Chestplate) Use(_ *world.Tx, _ User, ctx *UseContext) bool {
ctx.SwapHeldWithArmour(1)
return false
}
// MaxCount always returns 1.
func (c Chestplate) MaxCount() int {
return 1
}
// DefencePoints ...
func (c Chestplate) DefencePoints() float64 {
switch c.Tier.Name() {
case "leather":
return 3
case "copper":
return 4
case "golden", "chainmail":
return 5
case "iron":
return 6
case "diamond", "netherite":
return 8
}
panic("invalid chestplate tier")
}
// Toughness ...
func (c Chestplate) Toughness() float64 {
return c.Tier.Toughness()
}
// KnockBackResistance ...
func (c Chestplate) KnockBackResistance() float64 {
return c.Tier.KnockBackResistance()
}
// EnchantmentValue ...
func (c Chestplate) EnchantmentValue() int {
return c.Tier.EnchantmentValue()
}
// DurabilityInfo ...
func (c Chestplate) DurabilityInfo() DurabilityInfo {
return DurabilityInfo{
MaxDurability: int(c.Tier.BaseDurability() + c.Tier.BaseDurability()/2.2),
BrokenItem: simpleItem(Stack{}),
}
}
// SmeltInfo ...
func (c Chestplate) SmeltInfo() SmeltInfo {
switch c.Tier.(type) {
case ArmourTierIron, ArmourTierChain:
return newOreSmeltInfo(NewStack(IronNugget{}, 1), 0.1)
case ArmourTierGold:
return newOreSmeltInfo(NewStack(GoldNugget{}, 1), 0.1)
case ArmourTierCopper:
return newOreSmeltInfo(NewStack(CopperNugget{}, 1), 0.1)
}
return SmeltInfo{}
}
// RepairableBy ...
func (c Chestplate) RepairableBy(i Stack) bool {
return armourTierRepairable(c.Tier)(i)
}
// Chestplate ...
func (c Chestplate) Chestplate() bool {
return true
}
// WithTrim ...
func (c Chestplate) WithTrim(trim ArmourTrim) world.Item {
c.Trim = trim
return c
}
// EncodeItem ...
func (c Chestplate) EncodeItem() (name string, meta int16) {
return "minecraft:" + c.Tier.Name() + "_chestplate", 0
}
// DecodeNBT ...
func (c Chestplate) DecodeNBT(data map[string]any) any {
if t, ok := c.Tier.(ArmourTierLeather); ok {
if v, ok := data["customColor"].(int32); ok {
t.Colour = rgbaFromInt32(v)
c.Tier = t
}
}
c.Trim = readTrim(data)
return c
}
// EncodeNBT ...
func (c Chestplate) EncodeNBT() map[string]any {
m := map[string]any{}
if t, ok := c.Tier.(ArmourTierLeather); ok && t.Colour != (color.RGBA{}) {
m["customColor"] = int32FromRGBA(t.Colour)
}
writeTrim(m, c.Trim)
return m
}
+45
View File
@@ -0,0 +1,45 @@
package item
import (
"github.com/df-mc/dragonfly/server/entity/effect"
"github.com/df-mc/dragonfly/server/world"
"math/rand/v2"
"time"
)
// Chicken is a food item obtained from chickens. It can be cooked in a furnace, smoker, or campfire.
type Chicken struct {
defaultFood
// Cooked is whether the chicken is cooked.
Cooked bool
}
// Consume ...
func (c Chicken) Consume(_ *world.Tx, co Consumer) Stack {
if c.Cooked {
co.Saturate(6, 7.2)
} else {
co.Saturate(2, 1.2)
if rand.Float64() < 0.3 {
co.AddEffect(effect.New(effect.Hunger, 1, 30*time.Second))
}
}
return Stack{}
}
// SmeltInfo ...
func (c Chicken) SmeltInfo() SmeltInfo {
if c.Cooked {
return SmeltInfo{}
}
return newFoodSmeltInfo(NewStack(Chicken{Cooked: true}, 1), 0.35)
}
// EncodeItem ...
func (c Chicken) EncodeItem() (name string, meta int16) {
if c.Cooked {
return "minecraft:cooked_chicken", 0
}
return "minecraft:chicken", 0
}
+14
View File
@@ -0,0 +1,14 @@
package item
// ClayBall is obtained from mining clay blocks
type ClayBall struct{}
// SmeltInfo ...
func (ClayBall) SmeltInfo() SmeltInfo {
return newSmeltInfo(NewStack(Brick{}, 1), 0.3)
}
// EncodeItem ...
func (ClayBall) EncodeItem() (name string, meta int16) {
return "minecraft:clay_ball", 0
}
+9
View File
@@ -0,0 +1,9 @@
package item
// Clock is used to measure and display in-game time.
type Clock struct{}
// EncodeItem ...
func (w Clock) EncodeItem() (name string, meta int16) {
return "minecraft:clock", 0
}
+16
View File
@@ -0,0 +1,16 @@
package item
import "time"
// Coal is an item used as fuel & crafting torches.
type Coal struct{}
// FuelInfo ...
func (Coal) FuelInfo() FuelInfo {
return newFuelInfo(time.Second * 80)
}
// EncodeItem ...
func (Coal) EncodeItem() (name string, meta int16) {
return "minecraft:coal", 0
}
+37
View File
@@ -0,0 +1,37 @@
package item
import "github.com/df-mc/dragonfly/server/world"
// Cod is a food item obtained from cod. It can be cooked in a furnace, smoker, or campfire.
type Cod struct {
defaultFood
// Cooked is whether the cod is cooked.
Cooked bool
}
// Consume ...
func (c Cod) Consume(_ *world.Tx, co Consumer) Stack {
if c.Cooked {
co.Saturate(5, 6)
} else {
co.Saturate(2, 0.4)
}
return Stack{}
}
// SmeltInfo ...
func (c Cod) SmeltInfo() SmeltInfo {
if c.Cooked {
return SmeltInfo{}
}
return newFoodSmeltInfo(NewStack(Cod{Cooked: true}, 1), 0.35)
}
// EncodeItem ...
func (c Cod) EncodeItem() (name string, meta int16) {
if c.Cooked {
return "minecraft:cooked_cod", 0
}
return "minecraft:cod", 0
}
+212
View File
@@ -0,0 +1,212 @@
package item
import (
"image/color"
)
// Colour represents the colour of a block. Typically, Minecraft blocks have a total of 16 different colours.
type Colour struct {
colour
}
// ColourWhite returns the white colour.
func ColourWhite() Colour {
return Colour{0}
}
// ColourOrange returns the orange colour.
func ColourOrange() Colour {
return Colour{1}
}
// ColourMagenta returns the magenta colour.
func ColourMagenta() Colour {
return Colour{2}
}
// ColourLightBlue returns the light blue colour.
func ColourLightBlue() Colour {
return Colour{3}
}
// ColourYellow returns the yellow colour.
func ColourYellow() Colour {
return Colour{4}
}
// ColourLime returns the lime colour.
func ColourLime() Colour {
return Colour{5}
}
// ColourPink returns the pink colour.
func ColourPink() Colour {
return Colour{6}
}
// ColourGrey returns the grey colour.
func ColourGrey() Colour {
return Colour{7}
}
// ColourLightGrey returns the light grey colour.
func ColourLightGrey() Colour {
return Colour{8}
}
// ColourCyan returns the cyan colour.
func ColourCyan() Colour {
return Colour{9}
}
// ColourPurple returns the purple colour.
func ColourPurple() Colour {
return Colour{10}
}
// ColourBlue returns the blue colour.
func ColourBlue() Colour {
return Colour{11}
}
// ColourBrown returns the brown colour.
func ColourBrown() Colour {
return Colour{12}
}
// ColourGreen returns the green colour.
func ColourGreen() Colour {
return Colour{13}
}
// ColourRed returns the red colour.
func ColourRed() Colour {
return Colour{14}
}
// ColourBlack returns the black colour.
func ColourBlack() Colour {
return Colour{15}
}
// Colours returns a list of all existing colours.
func Colours() []Colour {
return []Colour{
ColourWhite(), ColourOrange(), ColourMagenta(), ColourLightBlue(), ColourYellow(), ColourLime(), ColourPink(), ColourGrey(),
ColourLightGrey(), ColourCyan(), ColourPurple(), ColourBlue(), ColourBrown(), ColourGreen(), ColourRed(), ColourBlack(),
}
}
// colour is the underlying value of a Colour struct.
type colour uint8
// RGBA returns the colour as RGBA. The alpha channel is always set to the maximum value. Colour values as returned here
// were obtained by placing signs in a world with all possible dyes used on them. The world was then loaded in Dragonfly
// to read their respective colours.
func (c colour) RGBA() color.RGBA {
switch c {
case 0:
return color.RGBA{R: 0xf0, G: 0xf0, B: 0xf0, A: 0xff}
case 1:
return color.RGBA{R: 0xf9, G: 0x80, B: 0x1d, A: 0xff}
case 2:
return color.RGBA{R: 0xc7, G: 0x4e, B: 0xbd, A: 0xff}
case 3:
return color.RGBA{R: 0x3a, G: 0xb3, B: 0xda, A: 0xff}
case 4:
return color.RGBA{R: 0xfe, G: 0xd8, B: 0x3d, A: 0xff}
case 5:
return color.RGBA{R: 0x80, G: 0xc7, B: 0x1f, A: 0xff}
case 6:
return color.RGBA{R: 0xf3, G: 0x8b, B: 0xaa, A: 0xff}
case 7:
return color.RGBA{R: 0x47, G: 0x4f, B: 0x52, A: 0xff}
case 8:
return color.RGBA{R: 0x9d, G: 0x9d, B: 0x97, A: 0xff}
case 9:
return color.RGBA{R: 0x16, G: 0x9c, B: 0x9c, A: 0xff}
case 10:
return color.RGBA{R: 0x89, G: 0x32, B: 0xb8, A: 0xff}
case 11:
return color.RGBA{R: 0x3c, G: 0x44, B: 0xaa, A: 0xff}
case 12:
return color.RGBA{R: 0x83, G: 0x54, B: 0x32, A: 0xff}
case 13:
return color.RGBA{R: 0x5e, G: 0x7c, B: 0x16, A: 0xff}
case 14:
return color.RGBA{R: 0xb0, G: 0x2e, B: 0x26, A: 0xff}
default:
return color.RGBA{R: 0x1d, G: 0x1d, B: 0x21, A: 0xff}
}
}
// SignRGBA returns the colour as RGBA. It is identical to the RGBA method, except for the colour black. For the colour
// black, a value of rgba(0, 0, 0, 255) is returned rather than rgba(29, 29, 33, 255), in order to match the black sign
// text colour found in vanilla Minecraft.
func (c colour) SignRGBA() color.RGBA {
if c == 15 {
return color.RGBA{R: 0x00, G: 0x00, B: 0x00, A: 0xff}
}
return c.RGBA()
}
// String ...
func (c colour) String() string {
switch c {
default:
return "white"
case 1:
return "orange"
case 2:
return "magenta"
case 3:
return "light_blue"
case 4:
return "yellow"
case 5:
return "lime"
case 6:
return "pink"
case 7:
return "gray"
case 8:
return "light_gray"
case 9:
return "cyan"
case 10:
return "purple"
case 11:
return "blue"
case 12:
return "brown"
case 13:
return "green"
case 14:
return "red"
case 15:
return "black"
}
}
// SilverString returns the name of the colour, with light_gray being replaced by silver.
func (c colour) SilverString() string {
if c == 8 {
return "silver"
}
return c.String()
}
// Uint8 ...
func (c colour) Uint8() uint8 {
return uint8(c)
}
// invertColour converts the item.Colour passed and returns the colour ID inverted.
func invertColour(c Colour) int16 {
return ^int16(c.Uint8()) & 0xf
}
// invertColourID converts the int16 passed the returns the item.Colour inverted.
func invertColourID(id int16) Colour {
return Colours()[uint8(^id&0xf)]
}
+9
View File
@@ -0,0 +1,9 @@
package item
// Compass is an item used to find the spawn position of a world.
type Compass struct{}
// EncodeItem ...
func (Compass) EncodeItem() (name string, meta int16) {
return "minecraft:compass", 0
}
+24
View File
@@ -0,0 +1,24 @@
package item
import "github.com/df-mc/dragonfly/server/world"
// Cookie is a food item that can be obtained in large quantities, but do not restore hunger or saturation significantly.
type Cookie struct {
defaultFood
}
// Consume ...
func (Cookie) Consume(_ *world.Tx, c Consumer) Stack {
c.Saturate(2, 0.4)
return Stack{}
}
// CompostChance ...
func (Cookie) CompostChance() float64 {
return 0.85
}
// EncodeItem ...
func (Cookie) EncodeItem() (name string, meta int16) {
return "minecraft:cookie", 0
}
+21
View File
@@ -0,0 +1,21 @@
package item
import "github.com/sandertv/gophertunnel/minecraft/text"
// CopperIngot is a metal ingot melted from copper ore.
type CopperIngot struct{}
// EncodeItem ...
func (c CopperIngot) EncodeItem() (name string, meta int16) {
return "minecraft:copper_ingot", 0
}
// TrimMaterial ...
func (CopperIngot) TrimMaterial() string {
return "copper"
}
// MaterialColour ...
func (CopperIngot) MaterialColour() string {
return text.Copper
}
+9
View File
@@ -0,0 +1,9 @@
package item
// CopperNugget is a piece of copper that can be obtained by smelting copper tools/weapons or armour.
type CopperNugget struct{}
// EncodeItem ...
func (CopperNugget) EncodeItem() (name string, meta int16) {
return "minecraft:copper_nugget", 0
}
+37
View File
@@ -0,0 +1,37 @@
package creative
// Category represents a category of items in the creative inventory which are shown as different tabs.
type Category struct {
category
}
type category uint8
// ConstructionCategory is the construction category which contains only blocks that do not fall under
// any other category.
func ConstructionCategory() Category {
return Category{1}
}
// NatureCategory is the nature category which contains blocks and items that can be naturally found in the
// world.
func NatureCategory() Category {
return Category{2}
}
// EquipmentCategory is the equipment category which contains tools, armour, food and any other form of
// equipment.
func EquipmentCategory() Category {
return Category{3}
}
// ItemsCategory is the items category for all the miscellaneous items that do not fall under any other
// category.
func ItemsCategory() Category {
return Category{4}
}
// Uint8 returns the category type as a uint8.
func (s category) Uint8() uint8 {
return uint8(s)
}
+176
View File
@@ -0,0 +1,176 @@
package creative
import (
_ "embed"
"fmt"
"github.com/df-mc/dragonfly/server/internal/nbtconv"
// The following four imports are essential for this package: They make sure this package is loaded after
// all these imports. This ensures that all blocks and items are registered before the creative items are
// registered in the init function in this package.
_ "github.com/df-mc/dragonfly/server/block"
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
"github.com/sandertv/gophertunnel/minecraft/nbt"
)
// Item represents a registered item in the creative inventory. It holds a stack of the item and a group that
// the item is part of.
type Item struct {
// Stack is the stack of the item that is registered in the creative inventory.
Stack item.Stack
// Group is the name of the group that the item is part of. If two groups are registered with the same
// name, the item will always reside in the first group that was registered.
Group string
}
// Group represents a group of items in the creative inventory. Each group has a category, a name and an icon.
// If either the name or icon is empty, the group is considered an 'anonymous group' and will not group its
// contents together in the creative inventory.
type Group struct {
// Category is the category of the group. It determines the tab in which the group will be displayed in the
// creative inventory.
Category Category
// Name is the localised name of the group, i.e. "itemGroup.name.planks".
Name string
// Icon is the item that will be displayed as the icon of the group in the creative inventory.
Icon item.Stack
}
// Groups returns a list with all groups that have been registered as a creative group. These groups will be
// accessible by players in-game who have creative mode enabled.
func Groups() []Group {
return creativeGroups
}
// RegisterGroup registers a group as a creative group, exposing it in the creative inventory. It can then
// be referenced using its name when calling RegisterItem.
func RegisterGroup(group Group) {
creativeGroups = append(creativeGroups, group)
}
// Items returns a list with all items that have been registered as a creative item. These items will
// be accessible by players in-game who have creative mode enabled.
func Items() []Item {
return creativeItemStacks
}
// RegisterItem registers an item as a creative item, exposing it in the creative inventory.
func RegisterItem(item Item) {
creativeItemStacks = append(creativeItemStacks, item)
}
var (
//go:embed creative_items.nbt
creativeItemData []byte
// creativeGroups holds a list of all groups that were registered to the creative inventory using
// RegisterGroup.
creativeGroups []Group
// creativeItemStacks holds a list of all item stacks that were registered to the creative inventory using
// RegisterItem.
creativeItemStacks []Item
)
// creativeGroupEntry holds data of a creative group as present in the creative inventory.
type creativeGroupEntry struct {
Category int32 `nbt:"category"`
Name string `nbt:"name"`
Icon creativeItemEntry `nbt:"icon"`
}
// creativeItemEntry holds data of a creative item as present in the creative inventory.
type creativeItemEntry struct {
Name string `nbt:"name"`
Meta int16 `nbt:"meta"`
NBT map[string]any `nbt:"nbt,omitempty"`
BlockProperties map[string]any `nbt:"block_properties,omitempty"`
GroupIndex int32 `nbt:"group_index,omitempty"`
}
// registerCreativeItems initialises the creative items, registering all creative items that have also been registered as
// normal items and are present in vanilla.
// noinspection GoUnusedFunction
//
//lint:ignore U1000 Function is used through compiler directives.
func registerCreativeItems() {
var m struct {
Groups []creativeGroupEntry `nbt:"groups"`
Items []creativeItemEntry `nbt:"items"`
}
if err := nbt.Unmarshal(creativeItemData, &m); err != nil {
panic(err)
}
for i, group := range m.Groups {
name := group.Name
if name == "" {
name = fmt.Sprint("anon", i)
}
st, _ := itemStackFromEntry(group.Icon)
c := Category{category(group.Category)}
RegisterGroup(Group{Category: c, Name: name, Icon: st})
}
for _, data := range m.Items {
if data.GroupIndex >= int32(len(creativeGroups)) {
panic(fmt.Errorf("invalid group index %v for item %v", data.GroupIndex, data.Name))
}
st, ok := itemStackFromEntry(data)
if !ok {
continue
}
RegisterItem(Item{st, creativeGroups[data.GroupIndex].Name})
}
}
func itemStackFromEntry(data creativeItemEntry) (item.Stack, bool) {
var (
it world.Item
ok bool
)
if len(data.BlockProperties) > 0 {
// Item with a block, try parsing the block, then try asserting that to an item. Blocks no longer
// have their metadata sent, but we still need to get that metadata in order to be able to register
// different block states as different items.
if b, ok := world.BlockByName(data.Name, data.BlockProperties); ok {
if it, ok = b.(world.Item); !ok {
return item.Stack{}, false
}
}
} else {
if it, ok = world.ItemByName(data.Name, data.Meta); !ok {
// The item wasn't registered, so don't register it as a creative item.
return item.Stack{}, false
}
if _, resultingMeta := it.EncodeItem(); resultingMeta != data.Meta {
// We found an item registered with that ID and a meta of 0, but we only need items with strictly
// the same meta here.
return item.Stack{}, false
}
}
if n, ok := it.(world.NBTer); ok {
if len(data.NBT) > 0 {
it = n.DecodeNBT(data.NBT).(world.Item)
}
}
st := item.NewStack(it, 1)
if len(data.NBT) > 0 {
var invalid bool
for _, e := range nbtconv.Slice(data.NBT, "ench") {
if v, ok := e.(map[string]any); ok {
t, ok := item.EnchantmentByID(int(nbtconv.Int16(v, "id")))
if !ok {
invalid = true
break
}
st = st.WithEnchantments(item.NewEnchantment(t, int(nbtconv.Int16(v, "lvl"))))
}
}
if invalid {
// Invalid enchantment, skip this item.
return item.Stack{}, false
}
}
return st, true
}
Binary file not shown.
+249
View File
@@ -0,0 +1,249 @@
package item
import (
"time"
_ "unsafe"
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/sound"
)
// Crossbow is a ranged weapon similar to a bow that uses arrows or fireworks
// as ammunition.
type Crossbow struct {
// Item is the item the crossbow is charged with.
Item Stack
}
// Charge starts the charging process and checks if the charge duration meets
// the required duration.
func (c Crossbow) Charge(releaser Releaser, _ *world.Tx, ctx *UseContext, duration time.Duration) bool {
if !c.Item.Empty() {
return false
}
creative := releaser.GameMode().CreativeInventory()
held, left := releaser.HeldItems()
if chargeDuration, _ := c.chargeDuration(held); duration < chargeDuration {
return false
}
projectileItem, ok := c.findProjectile(releaser, ctx)
if !ok {
return false
}
c.Item = projectileItem.Grow(-projectileItem.Count() + 1)
if !creative {
ctx.Consume(c.Item)
}
releaser.SetHeldItems(held.WithItem(c), left)
return true
}
// ContinueCharge ...
func (c Crossbow) ContinueCharge(releaser Releaser, tx *world.Tx, ctx *UseContext, duration time.Duration) {
if !c.Item.Empty() {
return
}
held, _ := releaser.HeldItems()
if _, ok := c.findProjectile(releaser, ctx); !ok {
return
}
chargeDuration, qcLevel := c.chargeDuration(held)
if duration.Seconds() <= 0.1 {
tx.PlaySound(releaser.Position(), sound.CrossbowLoad{Stage: sound.CrossbowLoadingStart, QuickCharge: qcLevel > 0})
}
// Base reload time is 25 ticks; each Quick Charge level reduces by 5 ticks
multiplier := 25.0 / float64(25-(5*qcLevel))
// Adjust ticks based on the multiplier
adjustedTicks := int(float64(duration.Milliseconds()) / (50 / multiplier))
// Play sound after every 16 ticks (adjusted by Quick Charge)
if adjustedTicks%16 == 0 {
tx.PlaySound(releaser.Position(), sound.CrossbowLoad{Stage: sound.CrossbowLoadingMiddle, QuickCharge: qcLevel > 0})
}
if progress := float64(duration) / float64(chargeDuration); progress >= 1 {
tx.PlaySound(releaser.Position(), sound.CrossbowLoad{Stage: sound.CrossbowLoadingEnd, QuickCharge: qcLevel > 0})
}
}
// chargeDuration calculates the duration required to charge the crossbow and
// the quick charge enchantment level, if any.
func (c Crossbow) chargeDuration(s Stack) (dur time.Duration, quickChargeLvl int) {
dur, lvl := time.Duration(1.25*float64(time.Second)), 0
for _, enchant := range s.Enchantments() {
if q, ok := enchant.Type().(interface{ ChargeDuration(int) time.Duration }); ok {
dur = min(dur, q.ChargeDuration(enchant.Level()))
lvl = enchant.Level()
}
}
return dur, lvl
}
// findProjectile looks through the inventory of a Releaser to find a projectile
// to insert into the crossbow. It first checks the left hand for fireworks or
// arrows, and searches the rest of the inventory for arrows if no valid
// projectile was in the left hand. False is returned if no valid projectile was
// anywhere in the inventory.
func (c Crossbow) findProjectile(r Releaser, ctx *UseContext) (Stack, bool) {
_, left := r.HeldItems()
_, isFirework := left.Item().(Firework)
_, isArrow := left.Item().(Arrow)
if isFirework || isArrow {
return left, true
}
if res, ok := ctx.FirstFunc(func(stack Stack) bool {
_, ok := stack.Item().(Arrow)
return ok
}); ok {
return res, true
}
if r.GameMode().CreativeInventory() {
// No projectiles in inventory but the player is in creative mode, so
// return an arrow anyway.
return NewStack(Arrow{}, 1), true
}
return Stack{}, false
}
// ReleaseCharge checks if the item is fully charged and, if so, releases it.
func (c Crossbow) ReleaseCharge(releaser Releaser, tx *world.Tx, ctx *UseContext) bool {
if c.Item.Empty() {
return false
}
held, _ := releaser.HeldItems()
creative := releaser.GameMode().CreativeInventory()
pierceLevel, multishot := 0, false
for _, enchant := range held.Enchantments() {
if _, ok := enchant.Type().(interface{ MultipleProjectiles() bool }); ok {
multishot = true
}
if _, ok := enchant.Type().(interface{ Pierces() bool }); ok {
pierceLevel = enchant.Level()
}
}
arrowConf := world.ArrowSpawnConfig{
Damage: 9,
Owner: releaser,
Critical: true,
ObtainArrowOnPickup: !creative,
PiercingLevel: pierceLevel,
}
c.shoot(releaser, tx, 0, arrowConf)
if multishot {
arrowConf.ObtainArrowOnPickup = false
c.shoot(releaser, tx, -10, arrowConf)
c.shoot(releaser, tx, 10, arrowConf)
}
c.applyDamage(ctx)
c.Item = Stack{}
held, left := releaser.HeldItems()
crossbow := held.WithItem(c)
releaser.SetHeldItems(crossbow, left)
tx.PlaySound(releaser.Position(), sound.CrossbowShoot{})
return true
}
// CanCharge ...
func (c Crossbow) CanCharge(releaser Releaser, _ *world.Tx, ctx *UseContext) bool {
_, found := c.findProjectile(releaser, ctx)
return found && !c.Item.Empty()
}
// shoot fires the crossbow's loaded projectiles.
func (c Crossbow) shoot(releaser Releaser, tx *world.Tx, offsetAngle float64, arrowConf world.ArrowSpawnConfig) {
rot := releaser.Rotation()
dirVec := cube.Rotation{rot[0] + offsetAngle, rot[1]}.Vec3()
if firework, ok := c.Item.Item().(Firework); ok {
createFirework := tx.World().EntityRegistry().Config().Firework
projectile := createFirework(world.EntitySpawnOpts{
Position: torsoPosition(releaser),
Velocity: dirVec.Mul(0.8),
Rotation: rot.Neg(),
}, firework, releaser, 1.0, 0, false)
tx.AddEntity(projectile)
} else {
createArrow := tx.World().EntityRegistry().Config().Arrow
arrowConf.Tip = c.Item.Item().(Arrow).Tip
arrow := createArrow(world.EntitySpawnOpts{
Position: torsoPosition(releaser),
Velocity: dirVec.Mul(5.15),
Rotation: rot.Neg(),
}, arrowConf)
tx.AddEntity(arrow)
}
}
// applyDamage applies damage on a UseContext based on the projectile loaded
// in the crossboww.
func (c Crossbow) applyDamage(ctx *UseContext) {
if _, ok := c.Item.Item().(Firework); ok {
ctx.DamageItem(3)
} else {
ctx.DamageItem(1)
}
}
// MaxCount always returns 1.
func (Crossbow) MaxCount() int {
return 1
}
// DurabilityInfo ...
func (Crossbow) DurabilityInfo() DurabilityInfo {
return DurabilityInfo{
MaxDurability: 464,
BrokenItem: simpleItem(Stack{}),
}
}
// FuelInfo ...
func (Crossbow) FuelInfo() FuelInfo {
return newFuelInfo(time.Second * 15)
}
// EnchantmentValue ...
func (Crossbow) EnchantmentValue() int {
return 1
}
// EncodeItem ...
func (Crossbow) EncodeItem() (name string, meta int16) {
return "minecraft:crossbow", 0
}
// DecodeNBT ...
func (c Crossbow) DecodeNBT(data map[string]any) any {
c.Item = mapItem(data, "chargedItem")
return c
}
// EncodeNBT ...
func (c Crossbow) EncodeNBT() map[string]any {
if !c.Item.Empty() {
return map[string]any{"chargedItem": writeItem(c.Item, true)}
}
return nil
}
// noinspection ALL
//
//go:linkname writeItem github.com/df-mc/dragonfly/server/internal/nbtconv.WriteItem
func writeItem(s Stack, disk bool) map[string]any
// noinspection ALL
//
//go:linkname mapItem github.com/df-mc/dragonfly/server/internal/nbtconv.MapItem
func mapItem(x map[string]any, k string) Stack
+26
View File
@@ -0,0 +1,26 @@
package item
import "github.com/sandertv/gophertunnel/minecraft/text"
// Diamond is a rare mineral obtained from diamond ore or loot chests.
type Diamond struct{}
// EncodeItem ...
func (Diamond) EncodeItem() (name string, meta int16) {
return "minecraft:diamond", 0
}
// TrimMaterial ...
func (Diamond) TrimMaterial() string {
return "diamond"
}
// MaterialColour ...
func (Diamond) MaterialColour() string {
return text.Diamond
}
// PayableForBeacon ...
func (Diamond) PayableForBeacon() bool {
return true
}
+10
View File
@@ -0,0 +1,10 @@
package item
// DiscFragment is a music disc fragment obtained from ancient city loot chests. They are extremely rare to find and
// nine of them in a crafting table makes a music disc named, "5".
type DiscFragment struct{}
// EncodeItem ...
func (DiscFragment) EncodeItem() (name string, meta int16) {
return "minecraft:disc_fragment_5", 0
}
+9
View File
@@ -0,0 +1,9 @@
package item
// DragonBreath is a brewing item that is used solely to make lingering potions.
type DragonBreath struct{}
// EncodeItem ...
func (DragonBreath) EncodeItem() (name string, meta int16) {
return "minecraft:dragon_breath", 0
}
+35
View File
@@ -0,0 +1,35 @@
package item
import (
"github.com/df-mc/dragonfly/server/world"
"time"
)
// DriedKelp is a food item that can be quickly eaten by the player.
type DriedKelp struct{}
// AlwaysConsumable ...
func (DriedKelp) AlwaysConsumable() bool {
return false
}
// ConsumeDuration ...
func (DriedKelp) ConsumeDuration() time.Duration {
return DefaultConsumeDuration / 2
}
// Consume ...
func (DriedKelp) Consume(_ *world.Tx, c Consumer) Stack {
c.Saturate(1, 0.2)
return Stack{}
}
// CompostChance ...
func (DriedKelp) CompostChance() float64 {
return 0.3
}
// EncodeItem ...
func (DriedKelp) EncodeItem() (name string, meta int16) {
return "minecraft:dried_kelp", 0
}
+37
View File
@@ -0,0 +1,37 @@
package item
// Durable represents an item that has durability, and may therefore be broken. Some durable items, when
// broken, create a new item, such as an elytra.
type Durable interface {
// DurabilityInfo returns info related to the durability of an item.
DurabilityInfo() DurabilityInfo
}
// DurabilityInfo is the info of a durable item. It includes fields that must be set in order to define
// durability related behaviour.
type DurabilityInfo struct {
// MaxDurability is the maximum durability that this item may have. This field must be positive for the
// durability to function properly.
MaxDurability int
// BrokenItem is the item created when the item is broken. For most durable items, this is simply an
// air item.
BrokenItem func() Stack
// AttackDurability and BreakDurability are the losses in durability that the item sustains when they are
// used to do the respective actions.
AttackDurability, BreakDurability int
// Persistent is true if the item is persistent, i.e. it will not be destroyed when at its last durability stage.
Persistent bool
}
// Repairable represents a durable item that can be repaired by other items.
type Repairable interface {
Durable
RepairableBy(i Stack) bool
}
// simpleItem is a convenience function to return an item stack as BrokenItem.
func simpleItem(i Stack) func() Stack {
return func() Stack {
return i
}
}
+37
View File
@@ -0,0 +1,37 @@
package item
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/go-gl/mathgl/mgl64"
)
// Dye is an item that comes in 16 colours which allows you to colour blocks like concrete and sheep.
type Dye struct {
// Colour is the colour of the dye.
Colour Colour
}
// UseOnBlock implements the colouring behaviour of signs.
func (d Dye) UseOnBlock(pos cube.Pos, _ cube.Face, _ mgl64.Vec3, tx *world.Tx, user User, ctx *UseContext) bool {
if dy, ok := tx.Block(pos).(dyeable); ok {
if res, ok := dy.Dye(pos, user.Position(), d.Colour); ok {
tx.SetBlock(pos, res, nil)
ctx.SubtractFromCount(1)
return true
}
}
return false
}
// dyeable represents a block that may be dyed by clicking it with a dye item.
type dyeable interface {
// Dye uses a dye with the Colour passed on the block. The resulting block is returned. A bool is returned to
// indicate if dyeing the block was successful.
Dye(pos cube.Pos, userPos mgl64.Vec3, c Colour) (world.Block, bool)
}
// EncodeItem ...
func (d Dye) EncodeItem() (name string, meta int16) {
return "minecraft:" + d.Colour.String() + "_dye", 0
}
+9
View File
@@ -0,0 +1,9 @@
package item
// EchoShard is an item found in ancient cities which can be used to craft recovery compasses.
type EchoShard struct{}
// EncodeItem ...
func (EchoShard) EncodeItem() (name string, meta int16) {
return "minecraft:echo_shard", 0
}
+30
View File
@@ -0,0 +1,30 @@
package item
import (
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/sound"
)
// Egg is an item that can be used to craft food items, or as a throwable entity to spawn chicks.
type Egg struct{}
// MaxCount ...
func (e Egg) MaxCount() int {
return 16
}
// Use ...
func (e Egg) Use(tx *world.Tx, user User, ctx *UseContext) bool {
create := tx.World().EntityRegistry().Config().Egg
opts := world.EntitySpawnOpts{Position: eyePosition(user), Velocity: user.Rotation().Vec3().Mul(1.5)}
tx.AddEntity(create(opts, user))
tx.PlaySound(user.Position(), sound.ItemThrow{})
ctx.SubtractFromCount(1)
return true
}
// EncodeItem ...
func (e Egg) EncodeItem() (name string, meta int16) {
return "minecraft:egg", 0
}
+57
View File
@@ -0,0 +1,57 @@
package item
import "github.com/df-mc/dragonfly/server/world"
// Elytra is a pair of rare wings found in end ships that are the only single-item source of flight in Survival mode.
type Elytra struct{}
// Use handles the using of an elytra to auto-equip it in an armour slot.
func (Elytra) Use(_ *world.Tx, _ User, ctx *UseContext) bool {
ctx.SwapHeldWithArmour(1)
return false
}
// DurabilityInfo ...
func (Elytra) DurabilityInfo() DurabilityInfo {
return DurabilityInfo{
MaxDurability: 433,
Persistent: true,
BrokenItem: simpleItem(Stack{}),
}
}
// RepairableBy ...
func (Elytra) RepairableBy(i Stack) bool {
_, ok := i.Item().(PhantomMembrane)
return ok
}
// MaxCount always returns 1.
func (Elytra) MaxCount() int {
return 1
}
// Chestplate ...
func (Elytra) Chestplate() bool {
return true
}
// DefencePoints ...
func (Elytra) DefencePoints() float64 {
return 0
}
// Toughness ...
func (e Elytra) Toughness() float64 {
return 0
}
// KnockBackResistance ...
func (e Elytra) KnockBackResistance() float64 {
return 0
}
// EncodeItem ...
func (Elytra) EncodeItem() (name string, meta int16) {
return "minecraft:elytra", 0
}
+26
View File
@@ -0,0 +1,26 @@
package item
import "github.com/sandertv/gophertunnel/minecraft/text"
// Emerald is a rare mineral obtained from emerald ore or from villagers.
type Emerald struct{}
// EncodeItem ...
func (Emerald) EncodeItem() (name string, meta int16) {
return "minecraft:emerald", 0
}
// TrimMaterial ...
func (Emerald) TrimMaterial() string {
return "emerald"
}
// MaterialColour ...
func (Emerald) MaterialColour() string {
return text.Emerald
}
// PayableForBeacon ...
func (Emerald) PayableForBeacon() bool {
return true
}
+35
View File
@@ -0,0 +1,35 @@
package item
import (
"github.com/df-mc/dragonfly/server/entity/effect"
"github.com/df-mc/dragonfly/server/world"
"time"
)
// EnchantedApple is a rare variant of the golden apple that has stronger effects.
type EnchantedApple struct{}
// AlwaysConsumable ...
func (EnchantedApple) AlwaysConsumable() bool {
return true
}
// ConsumeDuration ...
func (EnchantedApple) ConsumeDuration() time.Duration {
return DefaultConsumeDuration
}
// Consume ...
func (EnchantedApple) Consume(_ *world.Tx, c Consumer) Stack {
c.Saturate(4, 9.6)
c.AddEffect(effect.New(effect.Absorption, 4, 2*time.Minute))
c.AddEffect(effect.New(effect.Regeneration, 2, 30*time.Second))
c.AddEffect(effect.New(effect.FireResistance, 1, 5*time.Minute))
c.AddEffect(effect.New(effect.Resistance, 1, 5*time.Minute))
return Stack{}
}
// EncodeItem ...
func (EnchantedApple) EncodeItem() (name string, meta int16) {
return "minecraft:enchanted_golden_apple", 0
}
+14
View File
@@ -0,0 +1,14 @@
package item
// EnchantedBook is an item that lets players add enchantments to certain items using an anvil.
type EnchantedBook struct{}
// MaxCount ...
func (b EnchantedBook) MaxCount() int {
return 1
}
// EncodeItem ...
func (EnchantedBook) EncodeItem() (name string, meta int16) {
return "minecraft:enchanted_book", 0
}
+98
View File
@@ -0,0 +1,98 @@
package item
import (
"github.com/df-mc/dragonfly/server/world"
"maps"
"slices"
"sort"
)
// Enchantment is an enchantment that can be applied to a Stack. It holds an EnchantmentType and level that influences
// the power of the enchantment.
type Enchantment struct {
t EnchantmentType
lvl int
}
// NewEnchantment creates and returns an Enchantment with a specific EnchantmentType and level. If the level passed
// exceeds EnchantmentType.MaxLevel, NewEnchantment panics.
func NewEnchantment(t EnchantmentType, lvl int) Enchantment {
if lvl < 1 {
panic("enchantment level must never be below 1")
}
return Enchantment{t: t, lvl: lvl}
}
// Level returns the current level of the Enchantment as passed to NewEnchantment upon construction.
func (e Enchantment) Level() int {
return e.lvl
}
// Type returns the EnchantmentType of the Enchantment as passed to NewEnchantment upon construction.
func (e Enchantment) Type() EnchantmentType {
return e.t
}
// EnchantmentType represents an enchantment type that can be applied to a Stack, with specific behaviour that modifies
// the Stack's behaviour.
// An instance of an EnchantmentType may be created using NewEnchantment.
type EnchantmentType interface {
// Name returns the name of the enchantment.
Name() string
// MaxLevel returns the maximum level the enchantment should be able to have.
MaxLevel() int
// Cost returns the minimum and maximum cost the enchantment may inhibit. The higher this range is, the more likely
// better enchantments are to be selected.
Cost(level int) (int, int)
// Rarity returns the enchantment's rarity.
Rarity() EnchantmentRarity
// CompatibleWithEnchantment is called when an enchantment is added to an item. It can be used to check if
// the enchantment is compatible with other enchantments.
CompatibleWithEnchantment(t EnchantmentType) bool
// CompatibleWithItem is also called when an enchantment is added to an item. It can be used to check if
// the enchantment is compatible with the item type.
CompatibleWithItem(i world.Item) bool
}
// Enchantable is an interface that can be implemented by items that can be enchanted through an enchanting table.
type Enchantable interface {
// EnchantmentValue returns the value the item may inhibit on possible enchantments.
EnchantmentValue() int
}
// RegisterEnchantment registers an enchantment with the ID passed. Once registered, enchantments may be received
// by instantiating an EnchantmentType struct (e.g. enchantment.Protection{})
func RegisterEnchantment(id int, enchantment EnchantmentType) {
enchantmentsMap[id] = enchantment
enchantmentIDs[enchantment] = id
}
var (
enchantmentsMap = map[int]EnchantmentType{}
enchantmentIDs = map[EnchantmentType]int{}
)
// EnchantmentByID attempts to return an enchantment by the ID it was registered with. If found, the enchantment found
// is returned and the bool true.
func EnchantmentByID(id int) (EnchantmentType, bool) {
e, ok := enchantmentsMap[id]
return e, ok
}
// EnchantmentID attempts to return the ID the enchantment was registered with. If found, the id is returned and
// the bool true.
func EnchantmentID(e EnchantmentType) (int, bool) {
id, ok := enchantmentIDs[e]
return id, ok
}
// Enchantments returns a slice of all registered enchantments.
func Enchantments() []EnchantmentType {
e := slices.Collect(maps.Values(enchantmentsMap))
sort.Slice(e, func(i, j int) bool {
id1, _ := EnchantmentID(e[i])
id2, _ := EnchantmentID(e[j])
return id1 < id2
})
return e
}
+42
View File
@@ -0,0 +1,42 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// AquaAffinity is a helmet enchantment that increases underwater mining speed.
var AquaAffinity aquaAffinity
type aquaAffinity struct{}
// Name ...
func (aquaAffinity) Name() string {
return "Aqua Affinity"
}
// MaxLevel ...
func (aquaAffinity) MaxLevel() int {
return 1
}
// Cost ...
func (aquaAffinity) Cost(int) (int, int) {
return 1, 41
}
// Rarity ...
func (aquaAffinity) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityRare
}
// CompatibleWithEnchantment ...
func (aquaAffinity) CompatibleWithEnchantment(item.EnchantmentType) bool {
return true
}
// CompatibleWithItem ...
func (aquaAffinity) CompatibleWithItem(i world.Item) bool {
h, ok := i.(item.HelmetType)
return ok && h.Helmet()
}
@@ -0,0 +1,48 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// BlastProtection is an armour enchantment that reduces damage from explosions.
var BlastProtection blastProtection
type blastProtection struct{}
// Name ...
func (blastProtection) Name() string {
return "Blast Protection"
}
// MaxLevel ...
func (blastProtection) MaxLevel() int {
return 4
}
// Cost ...
func (blastProtection) Cost(level int) (int, int) {
minCost := 5 + (level-1)*8
return minCost, minCost + 8
}
// Rarity ...
func (blastProtection) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityRare
}
// Modifier returns the base protection modifier for the enchantment.
func (blastProtection) Modifier() float64 {
return 0.08
}
// CompatibleWithEnchantment ...
func (blastProtection) CompatibleWithEnchantment(t item.EnchantmentType) bool {
return t != FireProtection && t != ProjectileProtection && t != Protection
}
// CompatibleWithItem ...
func (blastProtection) CompatibleWithItem(i world.Item) bool {
_, ok := i.(item.Armour)
return ok
}
+52
View File
@@ -0,0 +1,52 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
"math"
)
// AffectedDamageSource represents a world.DamageSource whose damage may be
// affected by an enchantment. A world.DamageSource does not need to implement
// AffectedDamageSource to let protection affect the damage. This happens
// depending on the (world.DamageSource).ReducedByResistance() method.
type AffectedDamageSource interface {
world.DamageSource
// AffectedByEnchantment specifies if a world.DamageSource is affected by
// the item.EnchantmentType passed.
AffectedByEnchantment(e item.EnchantmentType) bool
}
// DamageModifier is an item.EnchantmentType that can reduce damage through a
// modifier if an AffectedDamageSource returns true for it.
type DamageModifier interface {
Modifier() float64
}
// ProtectionFactor calculates the combined protection factor for a slice of
// item.Enchantment. The factor depends on the world.DamageSource passed and is
// in a range of [0, 0.8], where 0.8 means incoming damage would be reduced by
// 80%.
func ProtectionFactor(src world.DamageSource, enchantments []item.Enchantment) float64 {
f := 0.0
for _, e := range enchantments {
t := e.Type()
modifier, ok := t.(DamageModifier)
if !ok {
continue
}
reduced := false
if _, ok := t.(protection); ok && src.ReducedByResistance() {
// Special case for protection, because it applies to all damage
// sources by default, except those not reduced by resistance.
reduced = true
} else if asrc, ok := src.(AffectedDamageSource); ok && asrc.AffectedByEnchantment(t) {
reduced = true
}
if reduced {
f += float64(e.Level()) * modifier.Modifier()
}
}
return math.Min(f, 0.8)
}
+44
View File
@@ -0,0 +1,44 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// DepthStrider is a boot enchantment that increases underwater movement speed.
var DepthStrider depthStrider
type depthStrider struct{}
// Name ...
func (depthStrider) Name() string {
return "Depth Strider"
}
// MaxLevel ...
func (depthStrider) MaxLevel() int {
return 3
}
// Cost ...
func (depthStrider) Cost(level int) (int, int) {
minCost := level * 10
return minCost, minCost + 15
}
// Rarity ...
func (depthStrider) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityRare
}
// CompatibleWithEnchantment ...
func (depthStrider) CompatibleWithEnchantment(item.EnchantmentType) bool {
// TODO: Frost Walker
return true
}
// CompatibleWithItem ...
func (depthStrider) CompatibleWithItem(i world.Item) bool {
b, ok := i.(item.BootsType)
return ok && b.Boots()
}
+48
View File
@@ -0,0 +1,48 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// Efficiency is an enchantment that increases mining speed.
var Efficiency efficiency
type efficiency struct{}
// Name ...
func (efficiency) Name() string {
return "Efficiency"
}
// MaxLevel ...
func (efficiency) MaxLevel() int {
return 5
}
// Cost ...
func (efficiency) Cost(level int) (int, int) {
minCost := 1 + 10*(level-1)
return minCost, minCost + 50
}
// Rarity ...
func (efficiency) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityCommon
}
// Addend returns the mining speed addend from efficiency.
func (efficiency) Addend(level int) float64 {
return float64(level*level + 1)
}
// CompatibleWithEnchantment ...
func (efficiency) CompatibleWithEnchantment(item.EnchantmentType) bool {
return true
}
// CompatibleWithItem ...
func (efficiency) CompatibleWithItem(i world.Item) bool {
t, ok := i.(item.Tool)
return ok && (t.ToolType() != item.TypeSword && t.ToolType() != item.TypeNone)
}
@@ -0,0 +1,49 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// FeatherFalling is an enchantment to boots that reduces fall damage. It does
// not affect falling speed.
var FeatherFalling featherFalling
type featherFalling struct{}
// Name ...
func (featherFalling) Name() string {
return "Feather Falling"
}
// MaxLevel ...
func (featherFalling) MaxLevel() int {
return 4
}
// Cost ...
func (featherFalling) Cost(level int) (int, int) {
minCost := 5 + (level-1)*6
return minCost, minCost + 6
}
// Rarity ...
func (featherFalling) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityUncommon
}
// Modifier returns the base protection modifier for the enchantment.
func (featherFalling) Modifier() float64 {
return 0.12
}
// CompatibleWithEnchantment ...
func (featherFalling) CompatibleWithEnchantment(item.EnchantmentType) bool {
return true
}
// CompatibleWithItem ...
func (featherFalling) CompatibleWithItem(i world.Item) bool {
b, ok := i.(item.BootsType)
return ok && b.Boots()
}
+49
View File
@@ -0,0 +1,49 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
"time"
)
// FireAspect is a sword enchantment that sets the target on fire.
var FireAspect fireAspect
type fireAspect struct{}
// Name ...
func (fireAspect) Name() string {
return "Fire Aspect"
}
// MaxLevel ...
func (fireAspect) MaxLevel() int {
return 2
}
// Cost ...
func (fireAspect) Cost(level int) (int, int) {
minCost := 10 + (level-1)*20
return minCost, minCost + 50
}
// Rarity ...
func (fireAspect) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityRare
}
// Duration returns how long the fire from fire aspect will last.
func (fireAspect) Duration(level int) time.Duration {
return time.Second * 4 * time.Duration(level)
}
// CompatibleWithEnchantment ...
func (fireAspect) CompatibleWithEnchantment(item.EnchantmentType) bool {
return true
}
// CompatibleWithItem ...
func (fireAspect) CompatibleWithItem(i world.Item) bool {
t, ok := i.(item.Tool)
return ok && t.ToolType() == item.TypeSword
}
@@ -0,0 +1,48 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// FireProtection is an armour enchantment that decreases fire damage.
var FireProtection fireProtection
type fireProtection struct{}
// Name ...
func (fireProtection) Name() string {
return "Fire Protection"
}
// MaxLevel ...
func (fireProtection) MaxLevel() int {
return 4
}
// Cost ...
func (fireProtection) Cost(level int) (int, int) {
minCost := 10 + (level-1)*8
return minCost, minCost + 8
}
// Rarity ...
func (fireProtection) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityUncommon
}
// Modifier returns the base protection modifier for the enchantment.
func (fireProtection) Modifier() float64 {
return 0.08
}
// CompatibleWithEnchantment ...
func (fireProtection) CompatibleWithEnchantment(t item.EnchantmentType) bool {
return t != BlastProtection && t != ProjectileProtection && t != Protection
}
// CompatibleWithItem ...
func (fireProtection) CompatibleWithItem(i world.Item) bool {
_, ok := i.(item.Armour)
return ok
}
+49
View File
@@ -0,0 +1,49 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
"time"
)
// Flame turns your arrows into flaming arrows allowing you to set your targets
// on fire.
var Flame flame
type flame struct{}
// Name ...
func (flame) Name() string {
return "Flame"
}
// MaxLevel ...
func (flame) MaxLevel() int {
return 1
}
// Cost ...
func (flame) Cost(int) (int, int) {
return 20, 50
}
// Rarity ...
func (flame) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityRare
}
// BurnDuration always returns five seconds, no matter the level.
func (flame) BurnDuration() time.Duration {
return time.Second * 5
}
// CompatibleWithEnchantment ...
func (flame) CompatibleWithEnchantment(item.EnchantmentType) bool {
return true
}
// CompatibleWithItem ...
func (flame) CompatibleWithItem(i world.Item) bool {
_, ok := i.(item.Bow)
return ok
}
+43
View File
@@ -0,0 +1,43 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// Fortune is an enchantment that gives a chance to receive more item drops from certain blocks.
var Fortune fortune
type fortune struct{}
// Name ...
func (fortune) Name() string {
return "Fortune"
}
// MaxLevel ...
func (fortune) MaxLevel() int {
return 3
}
// Cost ...
func (fortune) Cost(level int) (int, int) {
minCost := 15 + (level-1)*9
return minCost, minCost + 50 + level
}
// Rarity ...
func (fortune) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityRare
}
// CompatibleWithEnchantment ...
func (fortune) CompatibleWithEnchantment(t item.EnchantmentType) bool {
return t != SilkTouch
}
// CompatibleWithItem ...
func (fortune) CompatibleWithItem(i world.Item) bool {
t, ok := i.(item.Tool)
return ok && (t.ToolType() == item.TypePickaxe || t.ToolType() == item.TypeShovel || t.ToolType() == item.TypeAxe || t.ToolType() == item.TypeHoe)
}
+48
View File
@@ -0,0 +1,48 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// Infinity is an enchantment to bows that prevents regular arrows from being
// consumed when shot.
var Infinity infinity
type infinity struct{}
// Name ...
func (infinity) Name() string {
return "Infinity"
}
// MaxLevel ...
func (infinity) MaxLevel() int {
return 1
}
// Cost ...
func (infinity) Cost(int) (int, int) {
return 20, 50
}
// Rarity ...
func (infinity) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityVeryRare
}
// ConsumesArrows always returns false.
func (infinity) ConsumesArrows() bool {
return false
}
// CompatibleWithEnchantment ...
func (infinity) CompatibleWithEnchantment(t item.EnchantmentType) bool {
return t != Mending
}
// CompatibleWithItem ...
func (infinity) CompatibleWithItem(i world.Item) bool {
_, ok := i.(item.Bow)
return ok
}
+48
View File
@@ -0,0 +1,48 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// Knockback is an enchantment to a sword that increases the sword's knock-back.
var Knockback knockback
type knockback struct{}
// Name ...
func (knockback) Name() string {
return "Knockback"
}
// MaxLevel ...
func (knockback) MaxLevel() int {
return 2
}
// Cost ...
func (knockback) Cost(level int) (int, int) {
minCost := 5 + (level-1)*20
return minCost, minCost + 50
}
// Rarity ...
func (knockback) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityUncommon
}
// Force returns the increase in knock-back force from the enchantment.
func (knockback) Force(level int) float64 {
return float64(level) / 2
}
// CompatibleWithEnchantment ...
func (knockback) CompatibleWithEnchantment(item.EnchantmentType) bool {
return true
}
// CompatibleWithItem ...
func (knockback) CompatibleWithItem(i world.Item) bool {
t, ok := i.(item.Tool)
return ok && t.ToolType() == item.TypeSword
}
+49
View File
@@ -0,0 +1,49 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// Mending is an enchantment that repairs the item when experience orbs are
// collected.
var Mending mending
type mending struct{}
// Name ...
func (mending) Name() string {
return "Mending"
}
// MaxLevel ...
func (mending) MaxLevel() int {
return 1
}
// Cost ...
func (mending) Cost(level int) (int, int) {
minCost := level * 25
return minCost, minCost + 50
}
// Rarity ...
func (mending) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityRare
}
// Treasure ...
func (mending) Treasure() bool {
return true
}
// CompatibleWithEnchantment ...
func (mending) CompatibleWithEnchantment(t item.EnchantmentType) bool {
return t != Infinity
}
// CompatibleWithItem ...
func (mending) CompatibleWithItem(i world.Item) bool {
_, ok := i.(item.Durable)
return ok
}
+47
View File
@@ -0,0 +1,47 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// Multishot is an enchantment for crossbows that allow them to shoot three arrows or firework rockets at the cost of one.
var Multishot multishot
type multishot struct{}
// Name ...
func (multishot) Name() string {
return "Multishot"
}
// MaxLevel ...
func (multishot) MaxLevel() int {
return 1
}
// Cost ...
func (m multishot) Cost(level int) (int, int) {
return 20, 50
}
// Rarity ...
func (multishot) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityRare
}
// CompatibleWithEnchantment ...
func (multishot) CompatibleWithEnchantment(t item.EnchantmentType) bool {
return t != Piercing
}
// CompatibleWithItem ...
func (multishot) CompatibleWithItem(i world.Item) bool {
_, ok := i.(item.Crossbow)
return ok
}
// MultipleProjectiles ...
func (multishot) MultipleProjectiles() bool {
return true
}
+40
View File
@@ -0,0 +1,40 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// Piercing is an enchantment that allows arrows to damage and pierce through multiple entities, including shields.
var Piercing piercing
type piercing struct{}
func (p piercing) Name() string {
return "Piercing"
}
func (p piercing) MaxLevel() int {
return 4
}
func (p piercing) Cost(level int) (int, int) {
return 1 + (level-1)*10, 50
}
func (p piercing) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityCommon
}
func (p piercing) CompatibleWithEnchantment(t item.EnchantmentType) bool {
return t != Multishot
}
func (p piercing) CompatibleWithItem(i world.Item) bool {
_, ok := i.(item.Crossbow)
return ok
}
func (p piercing) Pierces() bool {
return true
}
+48
View File
@@ -0,0 +1,48 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// Power is a bow enchantment which increases arrow damage.
var Power power
type power struct{}
// Name ...
func (power) Name() string {
return "Power"
}
// MaxLevel ...
func (power) MaxLevel() int {
return 5
}
// Cost ...
func (power) Cost(level int) (int, int) {
minCost := 1 + (level-1)*10
return minCost, minCost + 15
}
// Rarity ...
func (power) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityCommon
}
// PowerDamage returns the extra base damage dealt by the enchantment and level.
func (power) PowerDamage(level int) float64 {
return float64(level+1) * 0.5
}
// CompatibleWithEnchantment ...
func (power) CompatibleWithEnchantment(item.EnchantmentType) bool {
return true
}
// CompatibleWithItem ...
func (power) CompatibleWithItem(i world.Item) bool {
_, ok := i.(item.Bow)
return ok
}
@@ -0,0 +1,49 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// ProjectileProtection is an armour enchantment that reduces damage from
// projectiles.
var ProjectileProtection projectileProtection
type projectileProtection struct{}
// Name ...
func (projectileProtection) Name() string {
return "Projectile Protection"
}
// MaxLevel ...
func (projectileProtection) MaxLevel() int {
return 4
}
// Cost ...
func (projectileProtection) Cost(level int) (int, int) {
minCost := 3 + (level-1)*6
return minCost, minCost + 6
}
// Rarity ...
func (projectileProtection) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityUncommon
}
// Modifier returns the base protection modifier for the enchantment.
func (projectileProtection) Modifier() float64 {
return 0.08
}
// CompatibleWithEnchantment ...
func (projectileProtection) CompatibleWithEnchantment(t item.EnchantmentType) bool {
return t != BlastProtection && t != FireProtection && t != Protection
}
// CompatibleWithItem ...
func (projectileProtection) CompatibleWithItem(i world.Item) bool {
_, ok := i.(item.Armour)
return ok
}
+48
View File
@@ -0,0 +1,48 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// Protection is an armour enchantment which increases the damage reduction.
var Protection protection
type protection struct{}
// Name ...
func (protection) Name() string {
return "Protection"
}
// MaxLevel ...
func (protection) MaxLevel() int {
return 4
}
// Cost ...
func (protection) Cost(level int) (int, int) {
minCost := 1 + (level-1)*11
return minCost, minCost + 11
}
// Rarity ...
func (protection) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityCommon
}
// Modifier returns the base protection modifier for the enchantment.
func (protection) Modifier() float64 {
return 0.04
}
// CompatibleWithEnchantment ...
func (protection) CompatibleWithEnchantment(t item.EnchantmentType) bool {
return t != BlastProtection && t != FireProtection && t != ProjectileProtection
}
// CompatibleWithItem ...
func (protection) CompatibleWithItem(i world.Item) bool {
_, ok := i.(item.Armour)
return ok
}
+48
View File
@@ -0,0 +1,48 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// Punch increases the knock-back dealt when hitting a player or mob with a bow.
var Punch punch
type punch struct{}
// Name ...
func (punch) Name() string {
return "Punch"
}
// MaxLevel ...
func (punch) MaxLevel() int {
return 2
}
// Cost ...
func (punch) Cost(level int) (int, int) {
minCost := 12 + (level-1)*20
return minCost, minCost + 25
}
// Rarity ...
func (punch) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityRare
}
// KnockBackMultiplier returns the punch multiplier for the level and horizontal speed.
func (punch) KnockBackMultiplier() float64 {
return 0.25
}
// CompatibleWithEnchantment ...
func (punch) CompatibleWithEnchantment(item.EnchantmentType) bool {
return true
}
// CompatibleWithItem ...
func (punch) CompatibleWithItem(i world.Item) bool {
_, ok := i.(item.Bow)
return ok
}
+49
View File
@@ -0,0 +1,49 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
"time"
)
// QuickCharge is an enchantment for quickly reloading a crossbow.
var QuickCharge quickCharge
type quickCharge struct{}
// Name ...
func (quickCharge) Name() string {
return "Quick Charge"
}
// MaxLevel ...
func (quickCharge) MaxLevel() int {
return 3
}
// Cost ...
func (quickCharge) Cost(level int) (int, int) {
minCost := 12 + (level-1)*20
return minCost, 50
}
// Rarity ...
func (quickCharge) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityUncommon
}
// ChargeDuration returns the charge duration.
func (quickCharge) ChargeDuration(level int) time.Duration {
return time.Duration((1.25 - 0.25*float64(level)) * float64(time.Second))
}
// CompatibleWithEnchantment ...
func (quickCharge) CompatibleWithEnchantment(item.EnchantmentType) bool {
return true
}
// CompatibleWithItem ...
func (quickCharge) CompatibleWithItem(i world.Item) bool {
_, ok := i.(item.Crossbow)
return ok
}
+44
View File
@@ -0,0 +1,44 @@
package enchantment
import "github.com/df-mc/dragonfly/server/item"
func init() {
item.RegisterEnchantment(0, Protection)
item.RegisterEnchantment(1, FireProtection)
item.RegisterEnchantment(2, FeatherFalling)
item.RegisterEnchantment(3, BlastProtection)
item.RegisterEnchantment(4, ProjectileProtection)
item.RegisterEnchantment(5, Thorns)
item.RegisterEnchantment(6, Respiration)
item.RegisterEnchantment(7, DepthStrider)
item.RegisterEnchantment(8, AquaAffinity)
item.RegisterEnchantment(9, Sharpness)
// TODO: (10) Smite. (Requires undead mobs)
// TODO: (11) Bane of Arthropods. (Requires arthropod mobs)
item.RegisterEnchantment(12, Knockback)
item.RegisterEnchantment(13, FireAspect)
// TODO: (14) Looting.
item.RegisterEnchantment(15, Efficiency)
item.RegisterEnchantment(16, SilkTouch)
item.RegisterEnchantment(17, Unbreaking)
item.RegisterEnchantment(18, Fortune)
item.RegisterEnchantment(19, Power)
item.RegisterEnchantment(20, Punch)
item.RegisterEnchantment(21, Flame)
item.RegisterEnchantment(22, Infinity)
// TODO: (23) Luck of the Sea.
// TODO: (24) Lure.
// TODO: (25) Frost Walker.
item.RegisterEnchantment(26, Mending)
// TODO: (27) Curse of Binding.
item.RegisterEnchantment(28, CurseOfVanishing)
// TODO: (29) Impaling.
// TODO: (30) Riptide.
// TODO: (31) Loyalty.
// TODO: (32) Channeling.
item.RegisterEnchantment(33, Multishot)
item.RegisterEnchantment(34, Piercing)
item.RegisterEnchantment(35, QuickCharge)
item.RegisterEnchantment(36, SoulSpeed)
item.RegisterEnchantment(37, SwiftSneak)
}
+49
View File
@@ -0,0 +1,49 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// Respiration extends underwater breathing time by +15 seconds per enchantment
// level in addition to the default time of 15 seconds.
var Respiration respiration
type respiration struct{}
// Name ...
func (respiration) Name() string {
return "Respiration"
}
// MaxLevel ...
func (respiration) MaxLevel() int {
return 3
}
// Cost ...
func (respiration) Cost(level int) (int, int) {
minCost := 10 * level
return minCost, minCost + 30
}
// Rarity ...
func (respiration) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityRare
}
// Chance returns the chance of the enchantment blocking the air supply from ticking.
func (respiration) Chance(level int) float64 {
return 1.0 / float64(level+1)
}
// CompatibleWithEnchantment ...
func (respiration) CompatibleWithEnchantment(item.EnchantmentType) bool {
return true
}
// CompatibleWithItem ...
func (respiration) CompatibleWithItem(i world.Item) bool {
h, ok := i.(item.HelmetType)
return ok && h.Helmet()
}
+49
View File
@@ -0,0 +1,49 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// Sharpness is an enchantment applied to a sword or axe that increases melee
// damage.
var Sharpness sharpness
type sharpness struct{}
// Name ...
func (sharpness) Name() string {
return "Sharpness"
}
// MaxLevel ...
func (sharpness) MaxLevel() int {
return 5
}
// Cost ...
func (sharpness) Cost(level int) (int, int) {
minCost := 1 + (level-1)*11
return minCost, minCost + 20
}
// Rarity ...
func (sharpness) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityCommon
}
// Addend returns the additional damage when attacking with sharpness.
func (sharpness) Addend(level int) float64 {
return float64(level) * 1.25
}
// CompatibleWithEnchantment ...
func (sharpness) CompatibleWithEnchantment(item.EnchantmentType) bool {
return true
}
// CompatibleWithItem ...
func (sharpness) CompatibleWithItem(i world.Item) bool {
t, ok := i.(item.Tool)
return ok && (t.ToolType() == item.TypeSword || t.ToolType() == item.TypeAxe)
}
+43
View File
@@ -0,0 +1,43 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// SilkTouch is an enchantment that allows many blocks to drop themselves
// instead of their usual items when mined.
var SilkTouch silkTouch
type silkTouch struct{}
// Name ...
func (silkTouch) Name() string {
return "Silk Touch"
}
// MaxLevel ...
func (silkTouch) MaxLevel() int {
return 1
}
// Cost ...
func (silkTouch) Cost(int) (int, int) {
return 15, 65
}
// Rarity ...
func (silkTouch) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityVeryRare
}
// CompatibleWithEnchantment ...
func (silkTouch) CompatibleWithEnchantment(t item.EnchantmentType) bool {
return t != Fortune
}
// CompatibleWithItem ...
func (silkTouch) CompatibleWithItem(i world.Item) bool {
t, ok := i.(item.Tool)
return ok && (t.ToolType() != item.TypeSword && t.ToolType() != item.TypeNone)
}
+49
View File
@@ -0,0 +1,49 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// SoulSpeed is an enchantment that can be applied on boots and allows the
// player to walk more quickly on soul sand or soul soil.
var SoulSpeed soulSpeed
type soulSpeed struct{}
// Name ...
func (soulSpeed) Name() string {
return "Soul Speed"
}
// MaxLevel ...
func (soulSpeed) MaxLevel() int {
return 3
}
// Cost ...
func (soulSpeed) Cost(level int) (int, int) {
minCost := level * 10
return minCost, minCost + 15
}
// Rarity ...
func (soulSpeed) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityVeryRare
}
// Treasure ...
func (soulSpeed) Treasure() bool {
return true
}
// CompatibleWithEnchantment ...
func (soulSpeed) CompatibleWithEnchantment(item.EnchantmentType) bool {
return true
}
// CompatibleWithItem ...
func (soulSpeed) CompatibleWithItem(i world.Item) bool {
b, ok := i.(item.BootsType)
return ok && b.Boots()
}
+49
View File
@@ -0,0 +1,49 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// SwiftSneak is a non-renewable enchantment that can be applied to leggings
// and allows the player to walk more quickly while sneaking.
var SwiftSneak swiftSneak
type swiftSneak struct{}
// Name ...
func (swiftSneak) Name() string {
return "Swift Sneak"
}
// MaxLevel ...
func (swiftSneak) MaxLevel() int {
return 3
}
// Cost ...
func (swiftSneak) Cost(level int) (int, int) {
minCost := level * 25
return minCost, minCost + 50
}
// Rarity ...
func (swiftSneak) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityVeryRare
}
// CompatibleWithEnchantment ...
func (swiftSneak) CompatibleWithEnchantment(item.EnchantmentType) bool {
return true
}
// Treasure ...
func (swiftSneak) Treasure() bool {
return true
}
// CompatibleWithItem ...
func (swiftSneak) CompatibleWithItem(i world.Item) bool {
b, ok := i.(item.LeggingsType)
return ok && b.Leggings()
}
+54
View File
@@ -0,0 +1,54 @@
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.
var Thorns thorns
type thorns struct{}
// Name ...
func (thorns) Name() string {
return "Thorns"
}
// MaxLevel ...
func (thorns) MaxLevel() int {
return 3
}
// Cost ...
func (thorns) Cost(level int) (int, int) {
minCost := 10 + 20*(level-1)
return minCost, minCost + 50
}
// 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
}
// ThornsDamageSource is used for damage caused by thorns.
type ThornsDamageSource struct {
// Owner is the owner of the armour with the thorns enchantment.
Owner world.Entity
}
func (ThornsDamageSource) ReducedByResistance() bool { return true }
func (ThornsDamageSource) ReducedByArmour() bool { return false }
func (ThornsDamageSource) Fire() bool { return false }
func (ThornsDamageSource) IgnoreTotem() bool { return false }
+58
View File
@@ -0,0 +1,58 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
"math/rand/v2"
)
// Unbreaking is an enchantment that gives a chance for an item to avoid
// durability reduction when it is used, effectively increasing the item's
// durability.
var Unbreaking unbreaking
type unbreaking struct{}
// Name ...
func (unbreaking) Name() string {
return "Unbreaking"
}
// MaxLevel ...
func (unbreaking) MaxLevel() int {
return 3
}
// Cost ...
func (unbreaking) Cost(level int) (int, int) {
minCost := 5 + 8*(level-1)
return minCost, minCost + 50
}
// Rarity ...
func (unbreaking) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityUncommon
}
// CompatibleWithEnchantment ...
func (unbreaking) CompatibleWithEnchantment(item.EnchantmentType) bool {
return true
}
// CompatibleWithItem ...
func (unbreaking) CompatibleWithItem(i world.Item) bool {
_, ok := i.(item.Durable)
return ok
}
// Reduce returns the amount of damage that should be reduced with unbreaking.
func (unbreaking) Reduce(it world.Item, level, amount int) int {
after := amount
_, ok := it.(item.Armour)
for i := 0; i < amount; i++ {
if (!ok || rand.Float64() >= 0.6) && rand.IntN(level+1) > 0 {
after--
}
}
return after
}
+58
View File
@@ -0,0 +1,58 @@
package enchantment
import (
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
)
// CurseOfVanishing is an enchantment that causes the item to disappear on
// death.
var CurseOfVanishing curseOfVanishing
type curseOfVanishing struct{}
// Name ...
func (curseOfVanishing) Name() string {
return "Curse of Vanishing"
}
// MaxLevel ...
func (curseOfVanishing) MaxLevel() int {
return 1
}
// Cost ...
func (curseOfVanishing) Cost(int) (int, int) {
return 25, 50
}
// Rarity ...
func (curseOfVanishing) Rarity() item.EnchantmentRarity {
return item.EnchantmentRarityVeryRare
}
// CompatibleWithEnchantment ...
func (curseOfVanishing) CompatibleWithEnchantment(_ item.EnchantmentType) bool {
return true
}
// CompatibleWithItem ...
func (curseOfVanishing) CompatibleWithItem(i world.Item) bool {
_, arm := i.(item.Armour)
_, com := i.(item.Compass)
_, dur := i.(item.Durable)
_, rec := i.(item.RecoveryCompass)
// TODO: Carrot on a Stick
// TODO: Warped Fungus on a Stick
return arm || com || dur || rec
}
// Treasure ...
func (curseOfVanishing) Treasure() bool {
return true
}
// Curse ...
func (curseOfVanishing) Curse() bool {
return true
}
+51
View File
@@ -0,0 +1,51 @@
package item
// EnchantmentRarity represents an enchantment rarity for enchantments. These rarities may inhibit certain properties,
// such as anvil costs or enchanting table weights.
type EnchantmentRarity interface {
// Name returns the name of the enchantment rarity.
Name() string
// Cost returns the cost of the enchantment rarity.
Cost() int
// Weight returns the weight of the enchantment rarity.
Weight() int
}
var (
// EnchantmentRarityCommon represents the common enchantment rarity.
EnchantmentRarityCommon enchantmentRarityCommon
// EnchantmentRarityUncommon represents the uncommon enchantment rarity.
EnchantmentRarityUncommon enchantmentRarityUncommon
// EnchantmentRarityRare represents the rare enchantment rarity.
EnchantmentRarityRare enchantmentRarityRare
// EnchantmentRarityVeryRare represents the very rare enchantment rarity.
EnchantmentRarityVeryRare enchantmentRarityVeryRare
)
// enchantmentRarityCommon represents the common enchantment rarity.
type enchantmentRarityCommon struct{}
func (enchantmentRarityCommon) Name() string { return "Common" }
func (enchantmentRarityCommon) Cost() int { return 1 }
func (enchantmentRarityCommon) Weight() int { return 10 }
// enchantmentRarityUncommon represents the uncommon enchantment rarity.
type enchantmentRarityUncommon struct{}
func (enchantmentRarityUncommon) Name() string { return "Uncommon" }
func (enchantmentRarityUncommon) Cost() int { return 2 }
func (enchantmentRarityUncommon) Weight() int { return 5 }
// enchantmentRarityRare represents the rare enchantment rarity.
type enchantmentRarityRare struct{}
func (enchantmentRarityRare) Name() string { return "Rare" }
func (enchantmentRarityRare) Cost() int { return 4 }
func (enchantmentRarityRare) Weight() int { return 2 }
// enchantmentRarityVeryRare represents the very rare enchantment rarity.
type enchantmentRarityVeryRare struct{}
func (enchantmentRarityVeryRare) Name() string { return "Very Rare" }
func (enchantmentRarityVeryRare) Cost() int { return 8 }
func (enchantmentRarityVeryRare) Weight() int { return 1 }
+36
View File
@@ -0,0 +1,36 @@
package item
import (
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/sound"
"time"
)
// EnderPearl is a smooth, greenish-blue item used to teleport and to make an eye of ender.
type EnderPearl struct{}
// Use ...
func (e EnderPearl) Use(tx *world.Tx, user User, ctx *UseContext) bool {
create := tx.World().EntityRegistry().Config().EnderPearl
opts := world.EntitySpawnOpts{Position: eyePosition(user), Velocity: user.Rotation().Vec3().Mul(1.5)}
tx.AddEntity(create(opts, user))
tx.PlaySound(user.Position(), sound.ItemThrow{})
ctx.SubtractFromCount(1)
return true
}
// Cooldown ...
func (EnderPearl) Cooldown() time.Duration {
return time.Second
}
// MaxCount ...
func (EnderPearl) MaxCount() int {
return 16
}
// EncodeItem ...
func (EnderPearl) EncodeItem() (name string, meta int16) {
return "minecraft:ender_pearl", 0
}
+9
View File
@@ -0,0 +1,9 @@
package item
// Feather are items dropped by chickens and parrots, as well as tamed cats as morning gifts.
type Feather struct{}
// EncodeItem ...
func (Feather) EncodeItem() (name string, meta int16) {
return "minecraft:feather", 0
}
+9
View File
@@ -0,0 +1,9 @@
package item
// FermentedSpiderEye is a brewing ingredient.
type FermentedSpiderEye struct{}
// EncodeItem ...
func (FermentedSpiderEye) EncodeItem() (name string, meta int16) {
return "minecraft:fermented_spider_eye", 0
}
+37
View File
@@ -0,0 +1,37 @@
package item
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/sound"
"github.com/go-gl/mathgl/mgl64"
"math/rand/v2"
"time"
)
// FireCharge is an item that can be used to place fire when used on a block, or shot from a dispenser to create a small
// fireball.
type FireCharge struct{}
// EncodeItem ...
func (f FireCharge) EncodeItem() (name string, meta int16) {
return "minecraft:fire_charge", 0
}
// UseOnBlock ...
func (f FireCharge) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, tx *world.Tx, user User, ctx *UseContext) bool {
if l, ok := tx.Block(pos).(ignitable); ok && l.Ignite(pos, tx, user) {
ctx.SubtractFromCount(1)
tx.PlaySound(pos.Vec3Centre(), sound.FireCharge{})
return true
} else if s := pos.Side(face); tx.Block(s) == air() {
ctx.SubtractFromCount(1)
tx.PlaySound(s.Vec3Centre(), sound.FireCharge{})
flame := fire()
tx.SetBlock(s, flame, nil)
tx.ScheduleBlockUpdate(s, flame, time.Duration(30+rand.IntN(10))*time.Second/20)
return true
}
return false
}
+93
View File
@@ -0,0 +1,93 @@
package item
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/sound"
"github.com/go-gl/mathgl/mgl64"
"math/rand/v2"
"time"
)
// Firework is an item (and entity) used for creating decorative explosions, boosting when flying with elytra, and
// loading into a crossbow as ammunition.
type Firework struct {
// Duration is the flight duration of the firework.
Duration time.Duration
// Explosions is the list of explosions the firework should create when launched.
Explosions []FireworkExplosion
}
// Use ...
func (f Firework) Use(tx *world.Tx, user User, ctx *UseContext) bool {
if g, ok := user.(interface {
Gliding() bool
}); !ok || !g.Gliding() {
return false
}
pos := user.Position()
tx.PlaySound(pos, sound.FireworkLaunch{})
create := tx.World().EntityRegistry().Config().Firework
opts := world.EntitySpawnOpts{Position: pos, Rotation: user.Rotation()}
tx.AddEntity(create(opts, f, user, 1.15, 0.04, true))
ctx.SubtractFromCount(1)
return true
}
// UseOnBlock ...
func (f Firework) UseOnBlock(pos cube.Pos, _ cube.Face, clickPos mgl64.Vec3, tx *world.Tx, user User, ctx *UseContext) bool {
fpos := pos.Vec3().Add(clickPos)
create := tx.World().EntityRegistry().Config().Firework
opts := world.EntitySpawnOpts{Position: fpos, Rotation: cube.Rotation{rand.Float64() * 360, 90}}
tx.AddEntity(create(opts, f, user, 1.15, 0.04, false))
tx.PlaySound(fpos, sound.FireworkLaunch{})
ctx.SubtractFromCount(1)
return true
}
// EncodeNBT ...
func (f Firework) EncodeNBT() map[string]any {
explosions := make([]any, 0, len(f.Explosions))
for _, explosion := range f.Explosions {
explosions = append(explosions, explosion.EncodeNBT())
}
return map[string]any{"Fireworks": map[string]any{
"Explosions": explosions,
"Flight": uint8((f.Duration/10 - time.Millisecond*50).Milliseconds() / 50),
}}
}
// DecodeNBT ...
func (f Firework) DecodeNBT(data map[string]any) any {
if fireworks, ok := data["Fireworks"].(map[string]any); ok {
if explosions, ok := fireworks["Explosions"].([]any); ok {
f.Explosions = make([]FireworkExplosion, len(explosions))
for i, explosion := range f.Explosions {
f.Explosions[i] = explosion.DecodeNBT(explosions[i].(map[string]any)).(FireworkExplosion)
}
}
if durationTicks, ok := fireworks["Flight"].(uint8); ok {
f.Duration = (time.Duration(durationTicks)*time.Millisecond*50 + time.Millisecond*50) * 10
}
}
return f
}
// RandomisedDuration returns the randomised flight duration of the firework.
func (f Firework) RandomisedDuration() time.Duration {
return f.Duration + time.Duration(rand.IntN(int(time.Millisecond*600)))
}
// OffHand ...
func (Firework) OffHand() bool {
return true
}
// EncodeItem ...
func (Firework) EncodeItem() (name string, meta int16) {
return "minecraft:firework_rocket", 0
}
+51
View File
@@ -0,0 +1,51 @@
package item
// FireworkExplosion represents an explosion of a firework.
type FireworkExplosion struct {
// Shape represents the shape of the explosion.
Shape FireworkShape
// Colour is the colour of the explosion.
Colour Colour
// Fade is the colour the explosion should fade into. Fades must be set to true in order for this to function.
Fade Colour
// Fades is true if the explosion should fade into the fade colour.
Fades bool
// Twinkle is true if the explosion should twinkle on explode.
Twinkle bool
// Trail is true if the explosion should have a trail.
Trail bool
}
// EncodeNBT ...
func (f FireworkExplosion) EncodeNBT() map[string]any {
data := map[string]any{
"FireworkType": f.Shape.Uint8(),
"FireworkColor": [1]uint8{uint8(invertColour(f.Colour))},
"FireworkFade": [0]uint8{},
"FireworkFlicker": boolByte(f.Twinkle),
"FireworkTrail": boolByte(f.Trail),
}
if f.Fades {
data["FireworkFade"] = [1]uint8{uint8(invertColour(f.Fade))}
}
return data
}
// DecodeNBT ...
func (f FireworkExplosion) DecodeNBT(data map[string]any) any {
f.Shape = FireworkShapes()[data["FireworkType"].(uint8)]
f.Twinkle = data["FireworkFlicker"].(uint8) == 1
f.Trail = data["FireworkTrail"].(uint8) == 1
colours := data["FireworkColor"]
if diskColour, ok := colours.([1]uint8); ok {
f.Colour = invertColourID(int16(diskColour[0]))
} else if networkColours, ok := colours.([]any); ok {
f.Colour = invertColourID(int16(networkColours[0].(uint8)))
}
if fades, ok := data["FireworkFade"].([1]uint8); ok {
f.Fade, f.Fades = invertColourID(int16(fades[0])), true
}
return f
}
+77
View File
@@ -0,0 +1,77 @@
package item
// FireworkShape represents a shape of a firework.
type FireworkShape struct {
fireworkShape
}
// FireworkShapeSmallSphere is a small sphere firework.
func FireworkShapeSmallSphere() FireworkShape {
return FireworkShape{0}
}
// FireworkShapeHugeSphere is a huge sphere firework.
func FireworkShapeHugeSphere() FireworkShape {
return FireworkShape{1}
}
// FireworkShapeStar is a star firework.
func FireworkShapeStar() FireworkShape {
return FireworkShape{2}
}
// FireworkShapeCreeperHead is a creeper head firework.
func FireworkShapeCreeperHead() FireworkShape {
return FireworkShape{3}
}
// FireworkShapeBurst is a burst firework.
func FireworkShapeBurst() FireworkShape {
return FireworkShape{4}
}
type fireworkShape uint8
// Uint8 returns the firework as a uint8.
func (f fireworkShape) Uint8() uint8 {
return uint8(f)
}
// Name ...
func (f fireworkShape) Name() string {
switch f {
case 0:
return "Small Sphere"
case 1:
return "Huge Sphere"
case 2:
return "Star"
case 3:
return "Creeper Head"
case 4:
return "Burst"
}
panic("unknown firework type")
}
// String ...
func (f fireworkShape) String() string {
switch f {
case 0:
return "small_sphere"
case 1:
return "huge_sphere"
case 2:
return "star"
case 3:
return "creeper_head"
case 4:
return "burst"
}
panic("unknown firework type")
}
// FireworkShapes ...
func FireworkShapes() []FireworkShape {
return []FireworkShape{FireworkShapeSmallSphere(), FireworkShapeHugeSphere(), FireworkShapeStar(), FireworkShapeCreeperHead(), FireworkShapeBurst()}
}
+27
View File
@@ -0,0 +1,27 @@
package item
// FireworkStar is an item used to determine the color, effect, and shape of firework rockets.
type FireworkStar struct {
FireworkExplosion
}
// EncodeNBT ...
func (f FireworkStar) EncodeNBT() map[string]any {
return map[string]any{
"FireworksItem": f.FireworkExplosion.EncodeNBT(),
"customColor": int32FromRGBA(f.Colour.RGBA()),
}
}
// DecodeNBT ...
func (f FireworkStar) DecodeNBT(data map[string]any) any {
if i, ok := data["FireworksItem"].(map[string]any); ok {
f.FireworkExplosion = f.FireworkExplosion.DecodeNBT(i).(FireworkExplosion)
}
return f
}
// EncodeItem ...
func (f FireworkStar) EncodeItem() (name string, meta int16) {
return "minecraft:firework_star", invertColour(f.Colour)
}
+9
View File
@@ -0,0 +1,9 @@
package item
// Flint is an item dropped rarely by gravel.
type Flint struct{}
// EncodeItem ...
func (Flint) EncodeItem() (name string, meta int16) {
return "minecraft:flint", 0
}
+71
View File
@@ -0,0 +1,71 @@
package item
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/df-mc/dragonfly/server/world/sound"
"github.com/go-gl/mathgl/mgl64"
"math/rand/v2"
"time"
)
// FlintAndSteel is an item used to light blocks on fire.
type FlintAndSteel struct{}
// MaxCount ...
func (f FlintAndSteel) MaxCount() int {
return 1
}
// DurabilityInfo ...
func (f FlintAndSteel) DurabilityInfo() DurabilityInfo {
return DurabilityInfo{
MaxDurability: 65,
BrokenItem: simpleItem(Stack{}),
}
}
// ignitable represents a block that can be lit by a fire emitter, such as flint and steel.
type ignitable interface {
// Ignite is called when the block is lit by flint and steel.
Ignite(pos cube.Pos, tx *world.Tx, igniter world.Entity) bool
}
// UseOnBlock ...
func (f FlintAndSteel) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, tx *world.Tx, user User, ctx *UseContext) bool {
ctx.DamageItem(1)
if l, ok := tx.Block(pos).(ignitable); ok && l.Ignite(pos, tx, user) {
return true
} else if s := pos.Side(face); tx.Block(s) == air() {
tx.PlaySound(s.Vec3Centre(), sound.Ignite{})
flame := fire()
tx.SetBlock(s, flame, nil)
tx.ScheduleBlockUpdate(s, flame, time.Duration(30+rand.IntN(10))*time.Second/20)
return true
}
return false
}
// EncodeItem ...
func (f FlintAndSteel) EncodeItem() (name string, meta int16) {
return "minecraft:flint_and_steel", 0
}
// air returns an air block.
func air() world.Block {
a, ok := world.BlockByName("minecraft:air", nil)
if !ok {
panic("could not find air block")
}
return a
}
// fire returns a fire block.
func fire() world.Block {
f, ok := world.BlockByName("minecraft:fire", map[string]any{"age": int32(0)})
if !ok {
panic("could not find fire block")
}
return f
}
+9
View File
@@ -0,0 +1,9 @@
package item
// GhastTear is a brewing item dropped by ghasts.
type GhastTear struct{}
// EncodeItem ...
func (GhastTear) EncodeItem() (name string, meta int16) {
return "minecraft:ghast_tear", 0
}
+41
View File
@@ -0,0 +1,41 @@
package item
import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
"github.com/go-gl/mathgl/mgl64"
)
// GlassBottle is an item that can hold various liquids.
type GlassBottle struct{}
// bottleFiller is implemented by blocks that can fill bottles by clicking on them.
type bottleFiller interface {
// FillBottle fills a GlassBottle by interacting with a block. Blocks that implement this interface return both the
// block that should be placed in the world after filling the bottle, and the item that was produced as a result of
// the filling.
// If the bool returned is false, nothing will happen when using a GlassBottle on the block.
FillBottle() (world.Block, Stack, bool)
}
// UseOnBlock ...
func (g GlassBottle) UseOnBlock(pos cube.Pos, _ cube.Face, _ mgl64.Vec3, tx *world.Tx, _ User, ctx *UseContext) bool {
bl := tx.Block(pos)
if b, ok := bl.(bottleFiller); ok {
var res world.Block
if res, ctx.NewItem, ok = b.FillBottle(); ok {
ctx.SubtractFromCount(1)
if res != bl {
// Some blocks (think a cauldron) change when using a bottle on it.
tx.SetBlock(pos, res, nil)
}
return true
}
}
return false
}
// EncodeItem ...
func (g GlassBottle) EncodeItem() (name string, meta int16) {
return "minecraft:glass_bottle", 0
}
+10
View File
@@ -0,0 +1,10 @@
package item
// GlisteringMelonSlice is an inedible item used for brewing potions of healing. It is also one of the many potion
// ingredients that can be used to make mundane potions.
type GlisteringMelonSlice struct{}
// EncodeItem ...
func (GlisteringMelonSlice) EncodeItem() (name string, meta int16) {
return "minecraft:glistering_melon_slice", 0
}
+9
View File
@@ -0,0 +1,9 @@
package item
// GlowstoneDust is dropped when breaking the glowstone block.
type GlowstoneDust struct{}
// EncodeItem ...
func (g GlowstoneDust) EncodeItem() (name string, meta int16) {
return "minecraft:glowstone_dust", 0
}

Some files were not shown because too many files have changed in this diff Show More