diff --git a/fortnite/image.go b/fortnite/image.go index 570effc..2f378a1 100644 --- a/fortnite/image.go +++ b/fortnite/image.go @@ -6,7 +6,9 @@ import ( "image/draw" "image/png" "io" + "math" "net/http" + "strings" "github.com/ectrc/snow/aid" "github.com/ectrc/snow/storage" @@ -17,10 +19,118 @@ var ( PlaylistImages = map[string][]byte{ "Playlist_DefaultSolo": {}, "Playlist_DefaultDuo": {}, - // "Playlist_DefaultSquad": {}, + "Playlist_DefaultTrio": {}, + "Playlist_DefaultSquad": {}, } ) +type colours struct { + averageRed uint8 + averageGreen uint8 + averageBlue uint8 +} + +var SETS_NOT_ALLOWED = []string{ + "Soccer", + "Football", + "Waypoint", +} + +func getAverageColour(img image.Image) colours { + var red, green, blue uint64 + var count uint64 + + bounds := img.Bounds() + for x := bounds.Min.X; x < bounds.Max.X; x++ { + for y := bounds.Min.Y; y < bounds.Max.Y; y++ { + r, g, b, _ := img.At(x, y).RGBA() + red += uint64(r) + green += uint64(g) + blue += uint64(b) + count++ + } + } + + return colours{ + averageRed: uint8(red / count), + averageGreen: uint8(green / count), + averageBlue: uint8(blue / count), + } +} + +func colorDifference(c1, c2 colours) float64 { + diffRed := int(c1.averageRed) - int(c2.averageRed) + diffGreen := int(c1.averageGreen) - int(c2.averageGreen) + diffBlue := int(c1.averageBlue) - int(c2.averageBlue) + + // Using Euclidean distance to calculate color difference + return math.Sqrt(float64(diffRed*diffRed + diffGreen*diffGreen + diffBlue*diffBlue)) +} + +func getRandomCharacterImage() image.Image { + found := false + var character FAPI_Cosmetic + for !found { + character = Cosmetics.GetRandomItemByType("AthenaCharacter") + + if character.Images.Featured == "" { + continue + } + + continueLoop := false + for _, set := range SETS_NOT_ALLOWED { + if strings.Contains(character.Set.Value, set) { + continueLoop = true + break + } + } + + if continueLoop { + continue + } + + if character.Introduction.BackendValue < 2 { + continue + } + + // newDisplayAsset := "DAv2_" + strings.ReplaceAll(character.ID, "Athena_Commando_", "") + // if !KnownDisplayAssets[newDisplayAsset] { + // continue + // } + + found = true + } + + response, err := http.Get(character.Images.Featured) + if err != nil { + panic(err) + } + defer response.Body.Close() + + b, err := io.ReadAll(response.Body) + if err != nil { + panic(err) + } + + image, _, err := image.Decode(bytes.NewReader(b)) + if err != nil { + panic(err) + } + + return image +} + +func getRandomCharacterImageWithSimilarColour(colour colours) image.Image { + character := getRandomCharacterImage() + characterColour := getAverageColour(character) + + if colorDifference(characterColour, colour) <= 140 { + return character + } + + return getRandomCharacterImageWithSimilarColour(colour) +} + func GenerateSoloImage() { background := *storage.Asset("background.png") @@ -28,7 +138,6 @@ func GenerateSoloImage() { for itemFound.Images.Featured == "" { itemFound = Cosmetics.GetRandomItemByType("AthenaCharacter") } - aid.Print(itemFound.Images.Featured) res, err := http.Get(itemFound.Images.Featured) if err != nil { @@ -73,42 +182,31 @@ func GenerateSoloImage() { func GenerateDuoImage() { background := *storage.Asset("background.png") - itemFound := Cosmetics.GetRandomItemByType("AthenaCharacter") - for itemFound.Images.Featured == "" { - itemFound = Cosmetics.GetRandomItemByType("AthenaCharacter") - } - aid.Print(itemFound.Images.Featured) - - res, err := http.Get(itemFound.Images.Featured) - if err != nil { - panic(err) - } - defer res.Body.Close() - - data, err := io.ReadAll(res.Body) - if err != nil { - panic(err) - } - + player1 := getRandomCharacterImage() + player2 := getRandomCharacterImageWithSimilarColour(getAverageColour(player1)) + bg, _, err := image.Decode(bytes.NewReader(background)) if err != nil { panic(err) } - soloPlayer, _, err := image.Decode(bytes.NewReader(data)) - if err != nil { - panic(err) - } m := image.NewRGBA(bg.Bounds()) draw.Draw(m, m.Bounds(), bg, image.Point{0, 0}, draw.Src) - resized := resize.Resize(0, uint(float64(m.Bounds().Dy()) * 1.4), soloPlayer, resize.Lanczos3) - centre := image.Point{ - m.Bounds().Dx()/2 - resized.Bounds().Dx()/2, - (m.Bounds().Dy()/2 - resized.Bounds().Dy()/2) + 200, + resizedPlayerLeft := resize.Resize(0, uint(float64(m.Bounds().Dy()) * 1.25), player1, resize.Lanczos3) + leftPosition := image.Point{ + m.Bounds().Dx()/2 - resizedPlayerLeft.Bounds().Dx()/2 - 200, + (m.Bounds().Dy()/2 - resizedPlayerLeft.Bounds().Dy()/2) + 200, } - draw.Draw(m, resized.Bounds().Add(centre), resized, image.Point{0, 0}, draw.Over) + draw.Draw(m, resizedPlayerLeft.Bounds().Add(leftPosition), resizedPlayerLeft, image.Point{0, 0}, draw.Over) + + resizedPlayerRight := resize.Resize(0, uint(float64(m.Bounds().Dy()) * 1.4), player2, resize.Lanczos3) + rightPosition := image.Point{ + m.Bounds().Dx()/2 - resizedPlayerRight.Bounds().Dx()/2 + 240, + (m.Bounds().Dy()/2 - resizedPlayerRight.Bounds().Dy()/2) + 200, + } + draw.Draw(m, resizedPlayerRight.Bounds().Add(rightPosition), resizedPlayerRight, image.Point{0, 0}, draw.Over) var bytes bytes.Buffer err = png.Encode(&bytes, m) @@ -117,4 +215,135 @@ func GenerateDuoImage() { } PlaylistImages["Playlist_DefaultDuo"] = bytes.Bytes() +} + +func GenerateTrioImage() { + background := *storage.Asset("background.png") + glow := *storage.Asset("glow.png") + + player1 := getRandomCharacterImage() + player2 := getRandomCharacterImageWithSimilarColour(getAverageColour(player1)) + player3 := getRandomCharacterImageWithSimilarColour(getAverageColour(player1)) + + bg, _, err := image.Decode(bytes.NewReader(background)) + if err != nil { + panic(err) + } + + glowImage, _, err := image.Decode(bytes.NewReader(glow)) + if err != nil { + panic(err) + } + + m := image.NewRGBA(bg.Bounds()) + draw.Draw(m, m.Bounds(), bg, image.Point{0, 0}, draw.Src) + + resizedPlayer1 := resize.Resize(0, uint(float64(m.Bounds().Dy()) * 1), player1, resize.Lanczos3) + player1Position := image.Point{ + m.Bounds().Dx()/2 - resizedPlayer1.Bounds().Dx()/2 - 400, + (m.Bounds().Dy()/2 - resizedPlayer1.Bounds().Dy()/2) + 150, + } + draw.Draw(m, resizedPlayer1.Bounds().Add(player1Position), resizedPlayer1, image.Point{0, 0}, draw.Over) + + resizedPlayer2 := resize.Resize(0, uint(float64(m.Bounds().Dy()) * 1.1), player2, resize.Lanczos3) + player2Position := image.Point{ + m.Bounds().Dx()/2 - resizedPlayer2.Bounds().Dx()/2 + 350, + (m.Bounds().Dy()/2 - resizedPlayer2.Bounds().Dy()/2) + 100, + } + draw.Draw(m, resizedPlayer2.Bounds().Add(player2Position), resizedPlayer2, image.Point{0, 0}, draw.Over) + + centre := image.Point{ + m.Bounds().Dx()/2 - glowImage.Bounds().Dx()/2, + m.Bounds().Dy()/2 - glowImage.Bounds().Dy()/2, + } + draw.Draw(m, glowImage.Bounds().Add(centre), glowImage, image.Point{0, 0}, draw.Over) + + resizedPlayer3 := resize.Resize(0, uint(float64(m.Bounds().Dy()) * 1.4), player3, resize.Lanczos3) + player3Position := image.Point{ + m.Bounds().Dx()/2 - resizedPlayer3.Bounds().Dx()/2, + (m.Bounds().Dy()/2 - resizedPlayer3.Bounds().Dy()/2) + 200, + } + draw.Draw(m, resizedPlayer3.Bounds().Add(player3Position), resizedPlayer3, image.Point{0, 0}, draw.Over) + + var bytes bytes.Buffer + err = png.Encode(&bytes, m) + if err != nil { + panic(err) + } + + PlaylistImages["Playlist_DefaultTrio"] = bytes.Bytes() +} + +func GenerateSquadImage() { + background := *storage.Asset("background.png") + glow := *storage.Asset("glow.png") + + player1 := getRandomCharacterImage() + player2 := getRandomCharacterImageWithSimilarColour(getAverageColour(player1)) + player3 := getRandomCharacterImageWithSimilarColour(getAverageColour(player1)) + player4 := getRandomCharacterImageWithSimilarColour(getAverageColour(player1)) + + bg, _, err := image.Decode(bytes.NewReader(background)) + if err != nil { + panic(err) + } + + glowImage, _, err := image.Decode(bytes.NewReader(glow)) + if err != nil { + panic(err) + } + + m := image.NewRGBA(bg.Bounds()) + draw.Draw(m, m.Bounds(), bg, image.Point{0, 0}, draw.Src) + + resizedPlayer4 := resize.Resize(0, uint(float64(m.Bounds().Dy()) * 0.8), player4, resize.Lanczos3) + player4Position := image.Point{ + m.Bounds().Dx()/2 - resizedPlayer4.Bounds().Dx()/2 - 600, + (m.Bounds().Dy()/2 - resizedPlayer4.Bounds().Dy()/2) + 110, + } + draw.Draw(m, resizedPlayer4.Bounds().Add(player4Position), resizedPlayer4, image.Point{0, 0}, draw.Over) + + resizedPlayer1 := resize.Resize(0, uint(float64(m.Bounds().Dy()) * 1), player1, resize.Lanczos3) + player1Position := image.Point{ + m.Bounds().Dx()/2 - resizedPlayer1.Bounds().Dx()/2 - 350, + (m.Bounds().Dy()/2 - resizedPlayer1.Bounds().Dy()/2) + 100, + } + draw.Draw(m, resizedPlayer1.Bounds().Add(player1Position), resizedPlayer1, image.Point{0, 0}, draw.Over) + + resizedPlayer2 := resize.Resize(0, uint(float64(m.Bounds().Dy()) * 1.1), player2, resize.Lanczos3) + player2Position := image.Point{ + m.Bounds().Dx()/2 - resizedPlayer2.Bounds().Dx()/2 + 400, + (m.Bounds().Dy()/2 - resizedPlayer2.Bounds().Dy()/2) + 100, + } + draw.Draw(m, resizedPlayer2.Bounds().Add(player2Position), resizedPlayer2, image.Point{0, 0}, draw.Over) + + centre := image.Point{ + m.Bounds().Dx()/2 - glowImage.Bounds().Dx()/2, + m.Bounds().Dy()/2 - glowImage.Bounds().Dy()/2, + } + draw.Draw(m, glowImage.Bounds().Add(centre), glowImage, image.Point{0, 0}, draw.Over) + + resizedPlayer3 := resize.Resize(0, uint(float64(m.Bounds().Dy()) * 1.4), player3, resize.Lanczos3) + player3Position := image.Point{ + m.Bounds().Dx()/2 - resizedPlayer3.Bounds().Dx()/2 + 150, + (m.Bounds().Dy()/2 - resizedPlayer3.Bounds().Dy()/2) + 200, + } + draw.Draw(m, resizedPlayer3.Bounds().Add(player3Position), resizedPlayer3, image.Point{0, 0}, draw.Over) + + var bytes bytes.Buffer + err = png.Encode(&bytes, m) + if err != nil { + panic(err) + } + + PlaylistImages["Playlist_DefaultSquad"] = bytes.Bytes() +} + +func GeneratePlaylistImages() { + GenerateSoloImage() + GenerateDuoImage() + GenerateTrioImage() + GenerateSquadImage() + + aid.Print("Generated playlist images") } \ No newline at end of file diff --git a/fortnite/shop.go b/fortnite/shop.go index 622e487..7e4d1d7 100644 --- a/fortnite/shop.go +++ b/fortnite/shop.go @@ -372,13 +372,18 @@ func GenerateRandomStorefront() { daily.Add(*entry) } - minimum := 8 + minimumItems := 8 if aid.Config.Fortnite.Season < 14 { - minimum = 3 + minimumItems = 3 + } + + minimumSets := 4 + if aid.Config.Fortnite.Season < 11 { + minimumSets = 3 } setsAdded := 0 - for len(weekly.CatalogEntries) < minimum || setsAdded < 2 { + for len(weekly.CatalogEntries) < minimumItems || setsAdded < minimumSets { set := Cosmetics.GetRandomSet() itemsAdded := 0 diff --git a/storage/mem/glow.png b/storage/mem/glow.png new file mode 100644 index 0000000..21a82b2 Binary files /dev/null and b/storage/mem/glow.png differ