confetty/simulation/simulation.go

102 lines
2.2 KiB
Go
Raw Normal View History

2021-08-09 01:08:28 +00:00
package simulation
2021-08-09 01:06:10 +00:00
import (
"fmt"
"strings"
2021-10-31 18:57:51 +00:00
"time"
2021-11-05 04:24:58 +00:00
"github.com/charmbracelet/harmonica"
2021-08-09 01:06:10 +00:00
)
type System struct {
Frame Frame
Particles []*Particle
2021-08-09 01:06:10 +00:00
}
type Particle struct {
2021-10-31 18:57:51 +00:00
Char string
TailChar string
2021-11-05 04:24:58 +00:00
Physics *harmonica.Projectile
2021-10-31 18:57:51 +00:00
Hidden bool
Shooting bool
ExplosionCall func(x, y float64, width, height int) []*Particle
2021-08-09 01:06:10 +00:00
}
type Frame struct {
Width int
Height int
}
2021-10-31 18:57:51 +00:00
func FPS(n int) float64 {
return (time.Second / time.Duration(n)).Seconds()
}
func RemoveParticleFromArray(s []*Particle, i int) []*Particle {
s[i] = nil
s[i] = s[len(s)-1]
return s[:len(s)-1]
}
2021-08-09 01:06:10 +00:00
func (s *System) Update() {
for i := len(s.Particles) - 1; i >= 0; i-- {
2021-10-31 18:57:51 +00:00
p := s.Particles[i]
2021-11-05 04:24:58 +00:00
pos := p.Physics.Position()
2021-10-31 18:57:51 +00:00
// if the shooting particle is slow enough then hide it and call the explosion function
2021-11-05 04:24:58 +00:00
if !p.Hidden && p.Shooting && p.Physics.Velocity().Y > -3 {
2021-10-31 18:57:51 +00:00
p.Hidden = true
if p.ExplosionCall != nil {
2021-11-05 04:24:58 +00:00
s.Particles = append(s.Particles, p.ExplosionCall(pos.X, pos.Y, s.Frame.Width, s.Frame.Height)...)
2021-10-31 18:57:51 +00:00
}
}
// remove particles that are hidden or out of the side/bottom of the frame
if p.Hidden || pos.X > float64(s.Frame.Width) || pos.X < 0 || pos.Y > float64(s.Frame.Height) {
s.Particles = RemoveParticleFromArray(s.Particles, i)
} else {
s.Particles[i].Physics.Update()
}
2021-08-09 01:06:10 +00:00
}
}
func (s *System) Visible(p *Particle) bool {
2021-11-05 04:24:58 +00:00
pos := p.Physics.Position()
x := int(pos.X)
y := int(pos.Y)
2021-10-31 18:57:51 +00:00
return !p.Hidden && y >= 0 && y < s.Frame.Height-1 && x >= 0 && x < s.Frame.Width-1
2021-08-09 01:06:10 +00:00
}
func (s *System) Render() string {
var out strings.Builder
plane := make([][]string, s.Frame.Height)
for i := range plane {
plane[i] = make([]string, s.Frame.Width)
}
for _, p := range s.Particles {
if s.Visible(p) {
2021-11-05 04:24:58 +00:00
pos := p.Physics.Position()
plane[int(pos.Y)][int(pos.X)] = p.Char
2021-10-31 18:57:51 +00:00
if p.Shooting {
2021-11-05 04:24:58 +00:00
l := -int(p.Physics.Velocity().Y)
2021-10-31 18:57:51 +00:00
for i := 1; i < l; i++ {
2021-11-05 04:24:58 +00:00
y := int(pos.Y) + i
2021-10-31 18:57:51 +00:00
if y > 0 && y < s.Frame.Height-1 {
2021-11-05 04:24:58 +00:00
plane[y][int(pos.X)] = p.TailChar
2021-10-31 18:57:51 +00:00
}
}
}
2021-08-09 01:06:10 +00:00
}
}
for i := range plane {
for _, col := range plane[i] {
if col == "" {
fmt.Fprint(&out, " ")
} else {
fmt.Fprint(&out, col)
}
}
fmt.Fprint(&out, "\n")
}
return out.String()
}