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
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:
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 ""
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)]
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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.
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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 }
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 }
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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()}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user