confetty/fireworks/fireworks.go

125 lines
3.0 KiB
Go
Raw Normal View History

2021-08-07 21:45:59 +00:00
package fireworks
import (
"math"
"math/rand"
"time"
"github.com/maaslalani/confetty/array"
2021-08-09 01:08:28 +00:00
"github.com/maaslalani/confetty/simulation"
2021-08-07 21:45:59 +00:00
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
const (
2021-11-05 04:08:48 +00:00
framesPerSecond = 30.0
2021-08-08 00:09:20 +00:00
numParticles = 50
2021-08-07 21:45:59 +00:00
)
var (
colors = []string{"#a864fd", "#29cdff", "#78ff44", "#ff718d", "#fdff6a"}
2021-08-08 00:09:20 +00:00
characters = []string{"+", "*", "•"}
2021-10-31 18:57:51 +00:00
head_char = "▄"
tail_char = "│"
2021-08-07 21:45:59 +00:00
)
type frameMsg time.Time
func animate() tea.Cmd {
return tea.Tick(time.Second/framesPerSecond, func(t time.Time) tea.Msg {
return frameMsg(t)
})
}
type model struct {
2021-08-09 01:08:28 +00:00
system *simulation.System
2021-08-07 21:45:59 +00:00
}
func SpawnShoot(width, height int) *simulation.Particle {
2021-10-31 18:57:51 +00:00
color := lipgloss.Color(array.Sample(colors))
v := float64(rand.Intn(15) + 15.0)
x := rand.Float64() * float64(width)
p := simulation.Particle{
Physics: simulation.NewProjectile(
simulation.FPS(framesPerSecond),
simulation.Point{X: x, Y: float64(height)},
simulation.Vector{X: 0, Y: -v},
simulation.Vector(simulation.TerminalGravity),
),
Char: lipgloss.NewStyle().Foreground(color).Render(head_char),
TailChar: lipgloss.NewStyle().Foreground(color).Render(tail_char),
Shooting: true,
ExplosionCall: SpawnExplosion,
}
return &p
2021-10-31 18:57:51 +00:00
}
func SpawnExplosion(x, y float64, width, height int) []*simulation.Particle {
2021-08-07 21:45:59 +00:00
color := lipgloss.Color(array.Sample(colors))
v := float64(rand.Intn(10) + 20.0)
particles := []*simulation.Particle{}
2021-08-07 21:45:59 +00:00
for i := 0; i < numParticles; i++ {
2021-08-09 01:08:28 +00:00
p := simulation.Particle{
2021-10-31 18:57:51 +00:00
Physics: simulation.NewProjectile(
simulation.FPS(framesPerSecond),
simulation.Point{X: x, Y: y},
simulation.Vector{X: math.Cos(float64(i)) * v, Y: math.Sin(float64(i)) * v / 2},
simulation.Vector(simulation.TerminalGravity),
2021-08-07 21:45:59 +00:00
),
2021-10-31 18:57:51 +00:00
Char: lipgloss.NewStyle().Foreground(color).Render(array.Sample(characters)),
Shooting: false,
2021-08-07 21:45:59 +00:00
}
particles = append(particles, &p)
2021-08-07 21:45:59 +00:00
}
return particles
}
func InitialModelWithSize(width, height int) model {
2021-08-09 01:08:28 +00:00
return model{system: &simulation.System{
Particles: []*simulation.Particle{SpawnShoot(width, height)},
2021-08-09 01:08:28 +00:00
Frame: simulation.Frame{
2021-08-09 01:06:10 +00:00
Width: width,
Height: height,
},
}}
2021-08-07 21:45:59 +00:00
}
// Init initializes the confetti after a small delay
func (m model) Init() tea.Cmd {
2021-08-09 01:06:10 +00:00
return animate()
2021-08-07 21:45:59 +00:00
}
// Update updates the model every frame, it handles the animation loop and
// updates the particle physics every frame
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
2021-08-09 02:51:40 +00:00
switch msg.String() {
case "ctrl+c", "q":
return m, tea.Quit
}
2021-10-31 18:57:51 +00:00
m.system.Particles = append(m.system.Particles, SpawnShoot(m.system.Frame.Width, m.system.Frame.Height))
2021-08-09 02:51:40 +00:00
return m, nil
2021-08-07 21:45:59 +00:00
case frameMsg:
2021-08-09 01:06:10 +00:00
m.system.Update()
2021-08-07 21:45:59 +00:00
return m, animate()
case tea.WindowSizeMsg:
2021-08-09 01:06:10 +00:00
m.system.Frame.Width = msg.Width
m.system.Frame.Height = msg.Height
2021-08-07 21:45:59 +00:00
return m, nil
default:
return m, nil
}
}
// View displays all the particles on the screen
func (m model) View() string {
2021-08-09 01:06:10 +00:00
return m.system.Render()
2021-08-07 21:45:59 +00:00
}