2023-09-17 13:34:03 -04:00
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
_ "embed"
|
|
|
|
|
"fmt"
|
|
|
|
|
"image"
|
|
|
|
|
"image/color"
|
|
|
|
|
"math"
|
|
|
|
|
|
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
|
|
|
"github.com/hajimehoshi/ebiten/v2/text"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
ASB_HEIGHT = 50
|
|
|
|
|
ASB_BUFFER = 10
|
|
|
|
|
ASB_WIDTH = 1400
|
|
|
|
|
ASB_COLOR = 0x5d2f6a
|
|
|
|
|
ASB_MARGIN = 10
|
|
|
|
|
|
2023-10-02 14:38:49 -04:00
|
|
|
//scales and offsets
|
|
|
|
|
ASB_GRAD_TOP = 0x457cf1 //0xff887c //0x9933cc
|
|
|
|
|
ASB_GRAD_BOTTOM = 0x2051bf //0xb6325f //0x5d2f6a
|
|
|
|
|
ASB_FLAME_SCALE = 1.56
|
|
|
|
|
ASB_FLAME_OFFSET = 15
|
|
|
|
|
ASB_MASK_WIDTH = 10
|
|
|
|
|
ASB_MASK_HEIGHT = ASB_HEIGHT
|
2023-09-17 13:34:03 -04:00
|
|
|
ASB_STRFADE_XOFFSET = 80
|
|
|
|
|
ASB_STRFADE_YOFFSET = 50
|
|
|
|
|
ASB_STRFADE_VALUE = 0x50
|
2023-10-02 14:38:49 -04:00
|
|
|
|
|
|
|
|
//scoreboard ticker related
|
|
|
|
|
ASB_TICK_PIXEL_SCALE = 2 //how many pixels per cycle for ticker animation
|
|
|
|
|
ASB_TICK_MAX_ADJUST = 2 * ASB_HEIGHT
|
2023-09-17 13:34:03 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
//go:embed resources/images/barmask.png
|
|
|
|
|
barmaskAsset_png []byte
|
|
|
|
|
assetsBarmask *ebiten.Image
|
2023-10-02 14:38:49 -04:00
|
|
|
|
|
|
|
|
//go:embed resources/images/crown.png
|
|
|
|
|
crownAsset_png []byte
|
|
|
|
|
assetsCrown *ebiten.Image
|
2023-09-17 13:34:03 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type AnimatedScoreBar struct {
|
|
|
|
|
ScoreImage *ebiten.Image
|
|
|
|
|
name string
|
|
|
|
|
target float64
|
|
|
|
|
value float64
|
2023-10-02 14:38:49 -04:00
|
|
|
gradient *Gradient
|
|
|
|
|
barcolors []color.RGBA
|
2023-09-17 13:34:03 -04:00
|
|
|
character *Character
|
|
|
|
|
flames *Flame
|
|
|
|
|
barbuffer *ebiten.Image
|
|
|
|
|
order int //ranking
|
|
|
|
|
sf float64 //scaling factor
|
2023-10-02 14:38:49 -04:00
|
|
|
crown *ebiten.Image
|
|
|
|
|
leader bool
|
|
|
|
|
cycle int
|
2023-09-17 13:34:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewAnimatedScoreBar(t CharacterType, s string) *AnimatedScoreBar {
|
|
|
|
|
a := &AnimatedScoreBar{
|
|
|
|
|
name: s,
|
|
|
|
|
target: 0,
|
|
|
|
|
value: 0,
|
|
|
|
|
sf: 1,
|
|
|
|
|
ScoreImage: ebiten.NewImage(ASB_WIDTH, ASB_HEIGHT),
|
|
|
|
|
barbuffer: ebiten.NewImage(ASB_WIDTH, ASB_HEIGHT),
|
2023-10-02 14:38:49 -04:00
|
|
|
gradient: NewGradient(ASB_WIDTH, ASB_HEIGHT),
|
2023-09-17 13:34:03 -04:00
|
|
|
character: NewCharacter(t),
|
|
|
|
|
flames: NewFlame(),
|
2023-10-02 14:38:49 -04:00
|
|
|
crown: ebiten.NewImageFromImage(assetsCrown),
|
2023-09-17 13:34:03 -04:00
|
|
|
order: 0,
|
2023-10-02 14:38:49 -04:00
|
|
|
leader: false,
|
|
|
|
|
barcolors: []color.RGBA{HexToRGBA(ASB_GRAD_TOP), HexToRGBA(ASB_GRAD_BOTTOM)},
|
2023-09-17 13:34:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a.character.RandomizeCycleStart()
|
2023-10-02 14:38:49 -04:00
|
|
|
a.SetBarColors(a.barcolors[GRAD_IDX_TOP], a.barcolors[GRAD_IDX_BOTTOM])
|
|
|
|
|
return a
|
|
|
|
|
}
|
2023-09-17 13:34:03 -04:00
|
|
|
|
2023-10-02 14:38:49 -04:00
|
|
|
func (a *AnimatedScoreBar) RefreshBar() {
|
|
|
|
|
a.barbuffer.DrawImage(a.gradient.Scene, nil)
|
2023-09-17 13:34:03 -04:00
|
|
|
|
|
|
|
|
//mask start of the bar for smooth edges
|
|
|
|
|
op := &ebiten.DrawImageOptions{}
|
|
|
|
|
op.Blend = ebiten.BlendDestinationOut
|
|
|
|
|
a.barbuffer.DrawImage(assetsBarmask.SubImage(image.Rect(0, 0, ASB_MASK_WIDTH, ASB_MASK_HEIGHT)).(*ebiten.Image), op)
|
|
|
|
|
|
|
|
|
|
//add name to the fill bar, which will gradually display as the value increases
|
|
|
|
|
text.Draw(a.barbuffer, a.name, DashFont.TeamBarName, ASB_MARGIN, ASB_HEIGHT-ASB_MARGIN, color.White)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AnimatedScoreBar) GetCharacterType() CharacterType {
|
|
|
|
|
return a.character.GetType()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AnimatedScoreBar) Name() string {
|
|
|
|
|
return a.name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AnimatedScoreBar) SetTarget(t float64) {
|
|
|
|
|
a.target = t
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AnimatedScoreBar) GetTarget() float64 {
|
|
|
|
|
return a.target
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AnimatedScoreBar) GetValue() float64 {
|
|
|
|
|
return a.value
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-02 14:38:49 -04:00
|
|
|
func (a *AnimatedScoreBar) ResetLead() {
|
|
|
|
|
a.leader = false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AnimatedScoreBar) SetLead() {
|
|
|
|
|
a.leader = true
|
|
|
|
|
}
|
|
|
|
|
|
2023-09-17 13:34:03 -04:00
|
|
|
func (a *AnimatedScoreBar) Animate() {
|
|
|
|
|
|
|
|
|
|
a.ScoreImage.Clear()
|
|
|
|
|
|
2023-10-02 14:38:49 -04:00
|
|
|
//a.AddFadedTeamName() //peeps complained, so we cut this
|
2023-09-17 13:34:03 -04:00
|
|
|
a.AddScoreBar()
|
|
|
|
|
a.AddCharacter()
|
|
|
|
|
a.AddTextScore()
|
|
|
|
|
a.AdjustValue()
|
2023-10-02 14:38:49 -04:00
|
|
|
a.AddLeadIndicator()
|
|
|
|
|
|
|
|
|
|
a.CycleUpdate()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AnimatedScoreBar) CycleUpdate() {
|
|
|
|
|
a.cycle++
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AnimatedScoreBar) AddLeadIndicator() {
|
|
|
|
|
if a.leader {
|
|
|
|
|
a.ScoreImage.DrawImage(a.crown, nil)
|
|
|
|
|
}
|
2023-09-17 13:34:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AnimatedScoreBar) AddFadedTeamName() {
|
|
|
|
|
//alpha blended black for the team name
|
|
|
|
|
c := HexToRGBA(0x000000)
|
|
|
|
|
c.A = ASB_STRFADE_VALUE
|
2023-10-02 14:38:49 -04:00
|
|
|
text.Draw(a.ScoreImage, a.name, DashFont.TeamBackgroundName, 1000, ASB_STRFADE_YOFFSET, c)
|
2023-09-17 13:34:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AnimatedScoreBar) SetOrder(order int) {
|
|
|
|
|
a.order = order
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AnimatedScoreBar) GetOrder() int {
|
|
|
|
|
return a.order
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// draw our score bar by using subset of full bar
|
|
|
|
|
func (a *AnimatedScoreBar) AddScoreBar() {
|
|
|
|
|
//ss = subset start, se = subset end
|
|
|
|
|
ssx := 0
|
|
|
|
|
ssy := 0
|
|
|
|
|
sex := int(a.value * a.sf)
|
|
|
|
|
sey := ASB_HEIGHT
|
|
|
|
|
barSubImage := a.barbuffer.SubImage(image.Rect(ssx, ssy, sex, sey)).(*ebiten.Image)
|
|
|
|
|
a.ScoreImage.DrawImage(barSubImage, nil)
|
|
|
|
|
|
|
|
|
|
//add trailing image mask to round out the edges
|
|
|
|
|
op := &ebiten.DrawImageOptions{}
|
|
|
|
|
op.Blend = ebiten.BlendDestinationOut
|
|
|
|
|
op.GeoM.Translate(float64(sex)-ASB_MASK_WIDTH, float64(sey)-ASB_MASK_HEIGHT)
|
|
|
|
|
|
|
|
|
|
msx := ASB_MASK_WIDTH
|
|
|
|
|
msy := 0
|
|
|
|
|
mex := ASB_MASK_WIDTH + ASB_MASK_WIDTH
|
|
|
|
|
mey := ASB_MASK_HEIGHT
|
|
|
|
|
a.ScoreImage.DrawImage(assetsBarmask.SubImage(image.Rect(msx, msy, mex, mey)).(*ebiten.Image), op)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AnimatedScoreBar) AddCharacter() {
|
|
|
|
|
|
|
|
|
|
op := &ebiten.DrawImageOptions{}
|
|
|
|
|
|
|
|
|
|
//if we're on the move, let's add some flair to the character
|
|
|
|
|
if a.target != a.value {
|
|
|
|
|
op.GeoM.Scale(ASB_FLAME_SCALE, ASB_FLAME_SCALE)
|
|
|
|
|
op.GeoM.Rotate(-math.Pi / 4)
|
|
|
|
|
op.GeoM.Translate(a.value*a.sf+ASB_BUFFER-ASB_FLAME_OFFSET*5/2, ASB_FLAME_OFFSET*3/2)
|
|
|
|
|
a.ScoreImage.DrawImage(a.flames.Sprite, op)
|
|
|
|
|
a.flames.Animate()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
op = &ebiten.DrawImageOptions{}
|
|
|
|
|
op.GeoM.Translate(a.value*a.sf+ASB_BUFFER, 0)
|
|
|
|
|
a.ScoreImage.DrawImage(a.character.Sprite, op)
|
|
|
|
|
a.character.Animate()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AnimatedScoreBar) AddTextScore() {
|
2023-10-02 14:38:49 -04:00
|
|
|
var ypos int
|
|
|
|
|
M := ASB_HEIGHT
|
2023-09-17 13:34:03 -04:00
|
|
|
tx := int(a.value*a.sf) + ASB_BUFFER + a.character.GetWidth()
|
2023-10-02 14:38:49 -04:00
|
|
|
|
|
|
|
|
//animate ticker movement for the team name
|
|
|
|
|
adjust := ASB_TICK_MAX_ADJUST //max adjustment
|
|
|
|
|
if a.cycle%adjust < M/ASB_TICK_PIXEL_SCALE {
|
|
|
|
|
ypos = 2*M - ASB_TICK_PIXEL_SCALE*a.cycle%adjust
|
|
|
|
|
} else if a.cycle%adjust > 2*M/ASB_TICK_PIXEL_SCALE {
|
|
|
|
|
ypos = M - (ASB_TICK_PIXEL_SCALE*(a.cycle-2*M/ASB_TICK_PIXEL_SCALE))%adjust
|
|
|
|
|
} else {
|
|
|
|
|
ypos = M
|
|
|
|
|
}
|
|
|
|
|
text.Draw(a.ScoreImage, a.name, DashFont.Title, tx, ypos, color.White)
|
|
|
|
|
|
|
|
|
|
//now do the same for the points, offset by the height of the ticker
|
|
|
|
|
bcycle := a.cycle - M/ASB_TICK_PIXEL_SCALE
|
|
|
|
|
adjust = 2 * M
|
|
|
|
|
if bcycle%adjust < 2*M/ASB_TICK_PIXEL_SCALE {
|
|
|
|
|
ypos = 3*M - ASB_TICK_PIXEL_SCALE*bcycle%adjust
|
|
|
|
|
} else if bcycle%adjust > 3*M/ASB_TICK_PIXEL_SCALE {
|
|
|
|
|
ypos = M - (ASB_TICK_PIXEL_SCALE*(bcycle-3*M/ASB_TICK_PIXEL_SCALE))%adjust //M - (pixel_scale*(bcycle-2*M/pixel_scale))%ma
|
|
|
|
|
} else {
|
|
|
|
|
ypos = M
|
|
|
|
|
}
|
|
|
|
|
text.Draw(a.ScoreImage, fmt.Sprintf("%0.f", a.value), DashFont.Title, tx, ypos, color.White)
|
|
|
|
|
text.Draw(a.ScoreImage, "POINTS", DashFont.Title, tx+ASB_BUFFER*7, ypos, color.White)
|
|
|
|
|
|
2023-09-17 13:34:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AnimatedScoreBar) AdjustValue() {
|
|
|
|
|
if a.value < a.target {
|
|
|
|
|
a.value++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-02 14:38:49 -04:00
|
|
|
// set our point scale factor (scalefactor = pixels per score point)
|
2023-09-17 13:34:03 -04:00
|
|
|
func (a *AnimatedScoreBar) SetScaleFactor(sf float64) {
|
|
|
|
|
a.sf = sf
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AnimatedScoreBar) Reset() {
|
|
|
|
|
a.value = 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
|
img, _, err := image.Decode(bytes.NewReader(barmaskAsset_png))
|
|
|
|
|
if err != nil {
|
2023-10-02 14:38:49 -04:00
|
|
|
DashLogger.Fatal(err)
|
2023-09-17 13:34:03 -04:00
|
|
|
}
|
|
|
|
|
assetsBarmask = ebiten.NewImageFromImage(img)
|
2023-10-02 14:38:49 -04:00
|
|
|
|
|
|
|
|
img, _, err = image.Decode(bytes.NewReader(crownAsset_png))
|
|
|
|
|
if err != nil {
|
|
|
|
|
DashLogger.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
assetsCrown = ebiten.NewImageFromImage(img)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *AnimatedScoreBar) SetBarColors(top, bottom color.RGBA) {
|
|
|
|
|
//fill bar gradient
|
|
|
|
|
a.gradient.SetColors(top, bottom)
|
|
|
|
|
a.RefreshBar()
|
2023-09-17 13:34:03 -04:00
|
|
|
}
|