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 fireworks and firework stars #576

Merged
merged 15 commits into from
Jul 29, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions server/entity/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ type PickedUpAction struct {
action
}

// FireworkParticleAction is a world.EntityAction that makes a Firework rocket display an explosion particle.
type FireworkParticleAction struct{ action }
JustTalDevelops marked this conversation as resolved.
Show resolved Hide resolved

// action implements the Action interface. Structures in this package may embed it to gets its functionality
// out of the box.
type action struct{}
Expand Down
137 changes: 137 additions & 0 deletions server/entity/firework.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package entity

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

// 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 {
transform

yaw, pitch float64
firework item.Firework

c *MovementComputer

ticks int
close bool
}

// NewFirework ...
func NewFirework(pos mgl64.Vec3, yaw, pitch float64, firework item.Firework) *Firework {
f := &Firework{
yaw: yaw,
pitch: pitch,
firework: firework,
c: &MovementComputer{},
ticks: int(firework.RandomizedDuration().Milliseconds() / 50),
}
f.transform = newTransform(f, pos)
f.vel = mgl64.Vec3{rand.Float64() * 0.001, 0.05, rand.Float64() * 0.001}
return f
}

// Name ...
func (f *Firework) Name() string {
return "Firework Rocket"
}

// EncodeEntity ...
func (f *Firework) EncodeEntity() string {
return "minecraft:fireworks_rocket"
}

// BBox ...
func (f *Firework) BBox() cube.BBox {
return cube.Box(-0.125, 0, -0.125, 0.125, 0.25, 0.125)
}

// Firework returns the underlying item.Firework of the Firework.
func (f *Firework) Firework() item.Firework {
return f.firework
}

// Rotation ...
func (f *Firework) Rotation() (float64, float64) {
f.mu.Lock()
defer f.mu.Unlock()
return f.yaw, f.pitch
}

// Tick ...
func (f *Firework) Tick(w *world.World, current int64) {
if f.close {
_ = f.Close()
return
}

f.mu.Lock()
f.vel[0] *= 1.15
f.vel[1] += 0.04
f.vel[2] *= 1.15
m := f.c.TickMovement(f, f.pos, f.vel, f.yaw, f.pitch)
f.pos, f.vel, f.yaw, f.pitch = m.pos, m.vel, m.yaw, m.pitch
f.mu.Unlock()

m.Send()

if m.pos[1] < float64(w.Range()[0]) && current%10 == 0 {
f.close = true
return
}

f.ticks--
if f.ticks < 0 {
JustTalDevelops marked this conversation as resolved.
Show resolved Hide resolved
for _, v := range w.Viewers(m.pos) {
v.ViewEntityAction(f, FireworkParticleAction{})
}
for _, explosion := range f.Firework().Explosions {
if explosion.Shape == item.FireworkShapeHugeSphere() {
w.PlaySound(m.pos, sound.FireworkHugeBlast{})
} else {
w.PlaySound(m.pos, sound.FireworkBlast{})
}
if explosion.Twinkle {
w.PlaySound(m.pos, sound.FireworkTwinkle{})
}
}
f.close = true
}
}

// New creates an firework with the position, velocity, yaw, and pitch provided. It doesn't spawn the firework,
// only returns it.
func (f *Firework) New(pos mgl64.Vec3, yaw, pitch float64, firework item.Firework) world.Entity {
return NewFirework(pos, yaw, pitch, firework)
}

// DecodeNBT decodes the properties in a map to a Firework and returns a new Firework entity.
func (f *Firework) DecodeNBT(data map[string]any) any {
firework := NewFirework(
nbtconv.MapVec3(data, "Pos"),
float64(nbtconv.Map[float32](data, "Pitch")),
float64(nbtconv.Map[float32](data, "Yaw")),
nbtconv.MapItem(data, "Item").Item().(item.Firework),
)
firework.vel = nbtconv.MapVec3(data, "Motion")
return firework
}

// EncodeNBT encodes the Firework entity's properties as a map and returns it.
func (f *Firework) EncodeNBT() map[string]any {
yaw, pitch := f.Rotation()
return map[string]any{
"Item": nbtconv.WriteItem(item.NewStack(f.Firework(), 1), true),
"Pos": nbtconv.Vec3ToFloat32Slice(f.Position()),
"Motion": nbtconv.Vec3ToFloat32Slice(f.Velocity()),
"Yaw": yaw,
"Pitch": pitch,
}
}
1 change: 1 addition & 0 deletions server/entity/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ func init() {
world.RegisterEntity(&Lightning{})
world.RegisterEntity(&Arrow{})
world.RegisterEntity(&ExperienceOrb{})
world.RegisterEntity(&Firework{})
}
80 changes: 80 additions & 0 deletions server/item/firework.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
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"
"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
}

// UseOnBlock ...
func (f Firework) UseOnBlock(blockPos cube.Pos, _ cube.Face, clickPos mgl64.Vec3, w *world.World, _ User, ctx *UseContext) bool {
firework, ok := world.EntityByName("minecraft:fireworks_rocket")
if !ok {
return false
}

p, ok := firework.(interface {
New(pos mgl64.Vec3, yaw, pitch float64, firework Firework) world.Entity
})
if !ok {
return false
}

pos := blockPos.Vec3().Add(clickPos)
w.AddEntity(p.New(pos, rand.Float64()*360, 90, f))
w.PlaySound(pos, 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.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) * 50 * time.Millisecond
}
}
return f
}

// RandomizedDuration returns the randomized flight duration of the firework.
func (f Firework) RandomizedDuration() time.Duration {
JustTalDevelops marked this conversation as resolved.
Show resolved Hide resolved
definite := f.Duration + time.Millisecond*50
randomness := time.Duration(rand.Intn(int(time.Millisecond * 600)))
return definite*10 + randomness
}

// EncodeItem ...
func (Firework) EncodeItem() (name string, meta int16) {
return "minecraft:firework_rocket", 0
}
51 changes: 51 additions & 0 deletions server/item/firework_explosion.go
Original file line number Diff line number Diff line change
@@ -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.
JustTalDevelops marked this conversation as resolved.
Show resolved Hide resolved
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{f.Colour.Uint8()},
"FireworkFade": [0]uint8{},
"FireworkFlicker": boolByte(f.Twinkle),
"FireworkTrail": boolByte(f.Trail),
}
if f.Fades {
data["FireworkFade"] = [1]uint8{f.Fade.Uint8()}
}
return data
}

// DecodeNBT ...
func (f FireworkExplosion) DecodeNBT(data map[string]any) any {
f.Shape = FireworkTypes()[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 = Colours()[diskColour[0]]
} else if networkColours, ok := colours.([]any); ok {
f.Colour = Colours()[networkColours[0].(uint8)]
}

if fades, ok := data["FireworkFade"].([1]uint8); ok {
f.Fade, f.Fades = Colours()[fades[0]], true
}
return f
}
77 changes: 77 additions & 0 deletions server/item/firework_type.go
Original file line number Diff line number Diff line change
@@ -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")
}

// FireworkTypes ...
func FireworkTypes() []FireworkShape {
return []FireworkShape{FireworkShapeSmallSphere(), FireworkShapeHugeSphere(), FireworkShapeStar(), FireworkShapeCreeperHead(), FireworkShapeBurst()}
}
8 changes: 8 additions & 0 deletions server/item/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,3 +250,11 @@ func rgbaFromInt32(x int32) color.RGBA {

return color.RGBA{A: b[0], R: b[1], G: b[2], B: b[3]}
}

// boolByte returns 1 if the bool passed is true, or 0 if it is false.
func boolByte(b bool) uint8 {
if b {
return 1
}
return 0
}
3 changes: 3 additions & 0 deletions server/item/recipe/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ func (d outputItems) Stacks() ([]item.Stack, bool) {
return nil, false
}
}
if n, ok := it.(world.NBTer); ok {
it = n.DecodeNBT(o.NBTData).(world.Item)
}
s = append(s, item.NewStack(it, int(o.Count)))
}
return s, true
Expand Down
1 change: 1 addition & 0 deletions server/item/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ func init() {
world.RegisterItem(DriedKelp{})
world.RegisterItem(Feather{})
world.RegisterItem(FermentedSpiderEye{})
world.RegisterItem(Firework{})
world.RegisterItem(GhastTear{})
world.RegisterItem(Gunpowder{})
world.RegisterItem(HeartOfTheSea{})
Expand Down
Loading