package main import ( "bytes" _ "embed" "fmt" "image" "image/color" "log" "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 ASB_GRAD_TOP = 0x9933cc ASB_GRAD_BOTTOM = 0x5d2f6a ASB_FLAME_SCALE = 1.56 ASB_FLAME_OFFSET = 15 ASB_MASK_WIDTH = 10 ASB_MASK_HEIGHT = ASB_HEIGHT ASB_STRFADE_XOFFSET = 80 ASB_STRFADE_YOFFSET = 50 ASB_STRFADE_VALUE = 0x50 ) var ( //go:embed resources/images/barmask.png barmaskAsset_png []byte assetsBarmask *ebiten.Image ) type AnimatedScoreBar struct { ScoreImage *ebiten.Image name string target float64 value float64 gradient *image.RGBA character *Character flames *Flame barbuffer *ebiten.Image order int //ranking sf float64 //scaling factor } 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), character: NewCharacter(t), flames: NewFlame(), order: 0, } a.character.RandomizeCycleStart() //fill bar gradient a.gradient = image.NewRGBA(image.Rect(0, 0, ASB_WIDTH, ASB_HEIGHT)) FillGradient(a.gradient, a.barbuffer.Bounds().Dx(), a.barbuffer.Bounds().Dy(), HexToRGBA(ASB_GRAD_TOP), HexToRGBA(ASB_GRAD_BOTTOM)) a.barbuffer.WritePixels(a.gradient.Pix) //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) return a } 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 } func (a *AnimatedScoreBar) Animate() { a.ScoreImage.Clear() a.AddFadedTeamName() a.AddScoreBar() a.AddCharacter() a.AddTextScore() a.AdjustValue() } func (a *AnimatedScoreBar) AddFadedTeamName() { //alpha blended black for the team name c := HexToRGBA(0x000000) c.A = ASB_STRFADE_VALUE text.Draw(a.ScoreImage, a.name, DashFont.TeamBackgroundName, ASB_STRFADE_XOFFSET, ASB_STRFADE_YOFFSET, c) } 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() { tx := int(a.value*a.sf) + ASB_BUFFER + a.character.GetWidth() ty := ASB_HEIGHT text.Draw(a.ScoreImage, fmt.Sprintf("%0.f", a.value), DashFont.Title, tx, ty, color.White) text.Draw(a.ScoreImage, "POINTS", DashFont.Title, tx+ASB_BUFFER*7, ty, color.White) } func (a *AnimatedScoreBar) AdjustValue() { if a.value < a.target { a.value++ } } 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 { log.Fatal(err) } assetsBarmask = ebiten.NewImageFromImage(img) }