-
Notifications
You must be signed in to change notification settings - Fork 144
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
server/item: Implemented fireworks and firework stars (#576)
- Loading branch information
1 parent
b281564
commit 5d6a225
Showing
18 changed files
with
576 additions
and
126 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,181 @@ | ||
package entity | ||
|
||
import ( | ||
"github.com/df-mc/dragonfly/server/block/cube" | ||
"github.com/df-mc/dragonfly/server/block/cube/trace" | ||
"github.com/df-mc/dragonfly/server/entity/damage" | ||
"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" | ||
"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 | ||
|
||
owner world.Entity | ||
|
||
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.RandomisedDuration().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.BBox{} | ||
} | ||
|
||
// 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 { | ||
return | ||
} | ||
|
||
explosions := f.Firework().Explosions | ||
for _, v := range w.Viewers(m.pos) { | ||
v.ViewEntityAction(f, FireworkExplosionAction{}) | ||
} | ||
for _, explosion := range 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{}) | ||
} | ||
} | ||
|
||
if len(explosions) > 0 { | ||
force := float64(len(explosions)*2) + 5.0 | ||
for _, e := range w.EntitiesWithin(f.BBox().Translate(m.pos).Grow(5.25), func(e world.Entity) bool { | ||
l, living := e.(Living) | ||
return !living || l.AttackImmune() | ||
}) { | ||
pos := e.Position() | ||
dist := m.pos.Sub(pos).Len() | ||
if dist > 5.0 { | ||
// The maximum distance allowed is 5.0 blocks. | ||
continue | ||
} | ||
if _, ok := trace.Perform(m.pos, pos, w, e.BBox().Grow(0.3), func(world.Entity) bool { | ||
return true | ||
}); ok { | ||
dmg := force * math.Sqrt((5.0-dist)/5.0) | ||
e.(Living).Hurt(dmg, damage.SourceProjectile{Owner: f.Owner(), Projectile: f}) | ||
} | ||
} | ||
} | ||
|
||
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) | ||
} | ||
|
||
// Owner ... | ||
func (f *Firework) Owner() world.Entity { | ||
f.mu.Lock() | ||
defer f.mu.Unlock() | ||
return f.owner | ||
} | ||
|
||
// Own ... | ||
func (f *Firework) Own(owner world.Entity) { | ||
f.mu.Lock() | ||
defer f.mu.Unlock() | ||
f.owner = owner | ||
} | ||
|
||
// 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, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
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 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) | ||
entity := p.New(pos, rand.Float64()*360, 90, f) | ||
if o, ok := entity.(owned); ok { | ||
o.Own(user) | ||
} | ||
|
||
ctx.SubtractFromCount(1) | ||
|
||
w.PlaySound(pos, sound.FireworkLaunch{}) | ||
w.AddEntity(entity) | ||
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 | ||
} | ||
|
||
// RandomisedDuration returns the randomised flight duration of the firework. | ||
func (f Firework) RandomisedDuration() time.Duration { | ||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. 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 = 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 = 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 | ||
} |
Oops, something went wrong.