Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented skull block #396

Merged
merged 8 commits into from
Jul 17, 2022
5 changes: 4 additions & 1 deletion server/block/attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ func (a Attachment) Uint8() uint8 {

// FaceUint8 returns the facing of the Attachment as a uint8.
func (a Attachment) FaceUint8() uint8 {
return uint8(a.facing)
if !a.hanging {
return 1
}
return uint8(a.facing) << 1
}

// RotateLeft rotates the Attachment the left way around by 90 degrees.
Expand Down
19 changes: 19 additions & 0 deletions server/block/cube/bbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,25 @@ func (box BBox) Translate(vec mgl64.Vec3) BBox {
return BBox{min: box.min.Add(vec), max: box.max.Add(vec)}
}

// TranslateTowards moves the entire AABB by x in the direction of a Face passed.
func (box BBox) TranslateTowards(f Face, x float64) BBox {
switch f {
case FaceDown:
return box.Translate(mgl64.Vec3{0, -x, 0})
case FaceUp:
return box.Translate(mgl64.Vec3{0, x, 0})
case FaceNorth:
return box.Translate(mgl64.Vec3{0, 0, -x})
case FaceSouth:
return box.Translate(mgl64.Vec3{0, 0, x})
case FaceWest:
return box.Translate(mgl64.Vec3{-x, 0, 0})
case FaceEast:
return box.Translate(mgl64.Vec3{x, 0, 0})
}
return box
}

// IntersectsWith checks if the BBox intersects with another BBox, returning true if this is the case.
func (box BBox) IntersectsWith(other BBox) bool {
return box.intersectsWith(other, 1e-5)
Expand Down
5 changes: 5 additions & 0 deletions server/block/hash.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions server/block/model/skull.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package model

import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/world"
)

// Skull is a model used by skull blocks.
type Skull struct {
// Direction is the direction the skull is facing.
Direction cube.Face
// Hanging specifies if the Skull is hanging on a wall.
Hanging bool
}

// BBox ...
func (s Skull) BBox(cube.Pos, *world.World) []cube.BBox {
box := cube.Box(0.25, 0, 0.25, 0.75, 0.5, 0.75)
if !s.Hanging {
return []cube.BBox{box}
}
return []cube.BBox{box.TranslateTowards(s.Direction.Opposite(), 0.25).TranslateTowards(cube.FaceUp, 0.25)}
}

// FaceSolid ...
func (Skull) FaceSolid(cube.Pos, cube.Face, *world.World) bool {
return false
}
4 changes: 4 additions & 0 deletions server/block/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ func init() {
registerAll(allSandstones())
registerAll(allSeaPickles())
registerAll(allSigns())
registerAll(allSkulls())
registerAll(allSmokers())
registerAll(allStainedGlass())
registerAll(allStainedGlassPane())
Expand Down Expand Up @@ -397,6 +398,9 @@ func init() {
for _, t := range FroglightTypes() {
world.RegisterItem(Froglight{Type: t})
}
for _, s := range SkullTypes() {
world.RegisterItem(Skull{Type: s})
}
}

func registerAll(blocks []world.Block) {
Expand Down
126 changes: 126 additions & 0 deletions server/block/skull.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package block

import (
"github.com/df-mc/dragonfly/server/block/cube"
"github.com/df-mc/dragonfly/server/block/model"
"github.com/df-mc/dragonfly/server/internal/nbtconv"
"github.com/df-mc/dragonfly/server/item"
"github.com/df-mc/dragonfly/server/world"
"github.com/go-gl/mathgl/mgl64"
)

// TODO: Dragon Heads can be powered by redstone

// Skull is a decorative block. There are six types of skulls: player, zombie, skeleton, wither skeleton, creeper,
// and dragon.
type Skull struct {
transparent

// Type is the type of the skull.
Type SkullType

// Attach is the attachment of the Skull. It is either of the type WallAttachment or StandingAttachment.
//blockhash:facing_only
Attach Attachment
}

// Helmet ...
func (Skull) Helmet() bool {
return true
}

// DefencePoints ...
func (Skull) DefencePoints() float64 {
return 0
}

// Toughness ...
func (Skull) Toughness() float64 {
return 0
}

// KnockBackResistance ...
func (Skull) KnockBackResistance() float64 {
return 0
}

// Model ...
func (s Skull) Model() world.BlockModel {
return model.Skull{Direction: s.Attach.facing.Face(), Hanging: s.Attach.hanging}
}

// UseOnBlock ...
func (s Skull) UseOnBlock(pos cube.Pos, face cube.Face, _ mgl64.Vec3, w *world.World, user item.User, ctx *item.UseContext) (used bool) {
pos, face, used = firstReplaceable(w, pos, face, s)
if !used || face == cube.FaceDown {
return false
}

if face == cube.FaceUp {
yaw, _ := user.Rotation()
s.Attach = StandingAttachment(cube.OrientationFromYaw(yaw))
} else {
s.Attach = WallAttachment(face.Direction())
}
place(w, pos, s, user, ctx)
return placed(ctx)
}

// CanDisplace ...
func (Skull) CanDisplace(b world.Liquid) bool {
_, water := b.(Water)
return water
}

// SideClosed ...
func (Skull) SideClosed(cube.Pos, cube.Pos, *world.World) bool {
return false
}

// HasLiquidDrops ...
func (Skull) HasLiquidDrops() bool {
return true
}

// BreakInfo ...
func (s Skull) BreakInfo() BreakInfo {
return newBreakInfo(1, alwaysHarvestable, nothingEffective, oneOf(Skull{Type: s.Type}))
}

// EncodeItem ...
func (s Skull) EncodeItem() (name string, meta int16) {
return "minecraft:skull", int16(s.Type.Uint8())
}

// DecodeNBT ...
func (s Skull) DecodeNBT(data map[string]interface{}) interface{} {
s.Type = SkullType{skull(nbtconv.Map[byte](data, "SkullType"))}
s.Attach.o = cube.Orientation(nbtconv.Map[byte](data, "Rot"))
if s.Attach.facing >= 0 {
s.Attach.hanging = true
}
return s
}

// EncodeNBT ...
func (s Skull) EncodeNBT() map[string]interface{} {
return map[string]interface{}{"id": "Skull", "SkullType": s.Type.Uint8(), "Rot": byte(s.Attach.o)}
}

// EncodeBlock ...
func (s Skull) EncodeBlock() (string, map[string]interface{}) {
if s.Attach.hanging {
return "minecraft:skull", map[string]interface{}{"facing_direction": int32(s.Attach.facing) + 2}
}
return "minecraft:skull", map[string]interface{}{"facing_direction": int32(1)}
}

// allSkulls ...
func allSkulls() (skulls []world.Block) {
for _, f := range cube.HorizontalFaces() {
// A direction of -2 and -1 isn't actually valid, but when encoding the block these are encoded as 0 and 1. We
// can't otherwise represent this properly in an Attachment type.
skulls = append(skulls, Skull{Attach: WallAttachment(f.Direction())})
}
return append(skulls, Skull{Attach: StandingAttachment(0)})
}
86 changes: 86 additions & 0 deletions server/block/skull_type.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package block

// SkullType represents a mob variant of a skull.
type SkullType struct {
skull
}

// SkeletonSkull returns the skull variant for skeletons.
func SkeletonSkull() SkullType {
return SkullType{0}
}

// WitherSkeletonSkull returns the skull variant for wither skeletons.
func WitherSkeletonSkull() SkullType {
return SkullType{1}
}

// ZombieHead returns the skull variant for zombies.
func ZombieHead() SkullType {
return SkullType{2}
}

// PlayerHead returns the skull variant for players.
func PlayerHead() SkullType {
return SkullType{3}
}

// CreeperHead returns the skull variant for creepers.
func CreeperHead() SkullType {
return SkullType{4}
}

// DragonHead returns the skull variant for ender dragons.
func DragonHead() SkullType {
return SkullType{5}
}

// SkullTypes returns all variants of skulls.
func SkullTypes() []SkullType {
return []SkullType{SkeletonSkull(), WitherSkeletonSkull(), ZombieHead(), PlayerHead(), CreeperHead(), DragonHead()}
}

type skull uint8

// Uint8 ...
func (s skull) Uint8() uint8 {
return uint8(s)
}

// Name ...
func (s skull) Name() string {
switch s {
case 0:
return "Skeleton Skull"
case 1:
return "Wither Skeleton Skull"
case 2:
return "Zombie Head"
case 3:
return "Player Head"
case 4:
return "Creeper Head"
case 5:
return "Dragon Head"
}
panic("unknown skull type")
}

// String ...
func (s skull) String() string {
switch s {
case 0:
return "skeleton"
case 1:
return "wither_skeleton"
case 2:
return "zombie"
case 3:
return "player"
case 4:
return "creeper"
case 5:
return "dragon"
}
panic("unknown skull type")
}