384 lines
9.3 KiB
Go
384 lines
9.3 KiB
Go
package fortnite
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"slices"
|
|
"strings"
|
|
|
|
"github.com/ectrc/snow/aid"
|
|
"github.com/ectrc/snow/storage"
|
|
)
|
|
|
|
var (
|
|
DataClient *dataClient
|
|
)
|
|
|
|
type dataClient struct {
|
|
h *http.Client
|
|
FortniteSets map[string]*FortniteSet `json:"sets"`
|
|
FortniteItems map[string]*FortniteItem `json:"items"`
|
|
FortniteItemsWithDisplayAssets map[string]*FortniteItem `json:"-"`
|
|
FortniteItemsWithFeaturedImage []*FortniteItem `json:"-"`
|
|
TypedFortniteItems map[string][]*FortniteItem `json:"-"`
|
|
TypedFortniteItemsWithDisplayAssets map[string][]*FortniteItem `json:"-"`
|
|
SnowVariantTokens map[string]*FortniteVariantToken `json:"variants"`
|
|
StorefrontCosmeticOfferPriceLookup map[string]map[string]int `json:"-"`
|
|
StorefrontDailyItemCountLookup []struct{Season int;Items int} `json:"-"`
|
|
StorefrontWeeklySetCountLookup []struct{Season int;Sets int} `json:"-"`
|
|
StorefrontCurrencyOfferPriceLookup map[string]map[int]int `json:"-"`
|
|
StorefrontCurrencyMultiplier map[string]float64 `json:"-"`
|
|
}
|
|
|
|
func NewDataClient() *dataClient {
|
|
return &dataClient{
|
|
h: &http.Client{},
|
|
FortniteItems: make(map[string]*FortniteItem),
|
|
FortniteSets: make(map[string]*FortniteSet),
|
|
FortniteItemsWithDisplayAssets: make(map[string]*FortniteItem),
|
|
FortniteItemsWithFeaturedImage: []*FortniteItem{},
|
|
TypedFortniteItems: make(map[string][]*FortniteItem),
|
|
TypedFortniteItemsWithDisplayAssets: make(map[string][]*FortniteItem),
|
|
SnowVariantTokens: make(map[string]*FortniteVariantToken),
|
|
StorefrontDailyItemCountLookup: []struct{Season int;Items int}{
|
|
{2, 4},
|
|
{4, 6},
|
|
{13, 10},
|
|
},
|
|
StorefrontWeeklySetCountLookup: []struct{Season int;Sets int}{
|
|
{2, 2},
|
|
{4, 3},
|
|
{13, 5},
|
|
},
|
|
StorefrontCosmeticOfferPriceLookup: map[string]map[string]int{
|
|
"EFortRarity::Legendary": {
|
|
"AthenaCharacter": 2000,
|
|
"AthenaBackpack": 1500,
|
|
"AthenaPickaxe": 1500,
|
|
"AthenaGlider": 1800,
|
|
"AthenaDance": 500,
|
|
"AthenaItemWrap": 800,
|
|
},
|
|
"EFortRarity::Epic": {
|
|
"AthenaCharacter": 1500,
|
|
"AthenaBackpack": 1200,
|
|
"AthenaPickaxe": 1200,
|
|
"AthenaGlider": 1500,
|
|
"AthenaDance": 800,
|
|
"AthenaItemWrap": 800,
|
|
},
|
|
"EFortRarity::Rare": {
|
|
"AthenaCharacter": 1200,
|
|
"AthenaBackpack": 800,
|
|
"AthenaPickaxe": 800,
|
|
"AthenaGlider": 800,
|
|
"AthenaDance": 500,
|
|
"AthenaItemWrap": 600,
|
|
},
|
|
"EFortRarity::Uncommon": {
|
|
"AthenaCharacter": 800,
|
|
"AthenaBackpack": 200,
|
|
"AthenaPickaxe": 500,
|
|
"AthenaGlider": 500,
|
|
"AthenaDance": 200,
|
|
"AthenaItemWrap": 300,
|
|
},
|
|
"EFortRarity::Common": {
|
|
"AthenaCharacter": 500,
|
|
"AthenaBackpack": 200,
|
|
"AthenaPickaxe": 500,
|
|
"AthenaGlider": 500,
|
|
"AthenaDance": 200,
|
|
"AthenaItemWrap": 300,
|
|
},
|
|
},
|
|
StorefrontCurrencyOfferPriceLookup: map[string]map[int]int{
|
|
"USD": {
|
|
1000: 999,
|
|
2800: 2499,
|
|
5000: 3999,
|
|
7500: 5999,
|
|
13500: 9999,
|
|
},
|
|
"GBP": {
|
|
1000: 799,
|
|
2800: 1999,
|
|
5000: 3499,
|
|
7500: 4999,
|
|
13500: 7999,
|
|
},
|
|
},
|
|
StorefrontCurrencyMultiplier: map[string]float64{
|
|
"USD": 1.2503128911,
|
|
"GBP": 1.0,
|
|
},
|
|
}
|
|
}
|
|
|
|
func (c *dataClient) LoadExternalData() {
|
|
req, err := http.NewRequest("GET", "https://fortnite-api.com/v2/cosmetics/br", nil)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
resp, err := c.h.Do(req)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
bodyBytes, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
content := &FortniteCosmeticsResponse{}
|
|
err = json.Unmarshal(bodyBytes, content)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
for _, item := range content.Data {
|
|
c.LoadItem(&item)
|
|
}
|
|
|
|
for _, item := range c.TypedFortniteItems["AthenaBackpack"] {
|
|
c.AddBackpackToItem(item)
|
|
}
|
|
|
|
displayAssets := storage.HttpAsset[[]string]("assets.snow.json")
|
|
if displayAssets == nil {
|
|
return
|
|
}
|
|
|
|
for _, displayAsset := range *displayAssets {
|
|
c.AddDisplayAssetToItem(displayAsset)
|
|
}
|
|
|
|
variantTokens := storage.HttpAsset[map[string]SnowCosmeticVariantToken]("variants.snow.json")
|
|
if variantTokens == nil {
|
|
return
|
|
}
|
|
|
|
for k, v := range *variantTokens {
|
|
item := c.FortniteItems[v.Item]
|
|
if item == nil {
|
|
continue
|
|
}
|
|
|
|
c.SnowVariantTokens[k] = &FortniteVariantToken{
|
|
Grants: v.Grants,
|
|
Item: item,
|
|
Name: v.Name,
|
|
Gift: v.Gift,
|
|
Equip: v.Equip,
|
|
Unseen: v.Unseen,
|
|
}
|
|
}
|
|
|
|
addNumericStylesToSets := []string{"Soccer", "Football", "ScaryBall"}
|
|
for _, setValue := range addNumericStylesToSets {
|
|
set, found := c.FortniteSets[setValue]
|
|
if !found {
|
|
continue
|
|
}
|
|
|
|
for _, item := range set.Items {
|
|
c.AddNumericStylesToItem(item)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (c *dataClient) LoadItem(item *FortniteItem) {
|
|
if item.Introduction.BackendValue > aid.Config.Fortnite.Season || item.Introduction.BackendValue == 0 {
|
|
return
|
|
}
|
|
|
|
if c.FortniteSets[item.Set.BackendValue] == nil {
|
|
c.FortniteSets[item.Set.BackendValue] = &FortniteSet{
|
|
BackendName: item.Set.Value,
|
|
DisplayName: item.Set.Text,
|
|
Items: []*FortniteItem{},
|
|
}
|
|
}
|
|
|
|
if c.TypedFortniteItems[item.Type.BackendValue] == nil {
|
|
c.TypedFortniteItems[item.Type.BackendValue] = []*FortniteItem{}
|
|
}
|
|
|
|
c.FortniteItems[item.ID] = item
|
|
c.FortniteSets[item.Set.BackendValue].Items = append(c.FortniteSets[item.Set.BackendValue].Items, item)
|
|
c.TypedFortniteItems[item.Type.BackendValue] = append(c.TypedFortniteItems[item.Type.BackendValue], item)
|
|
|
|
if item.Type.BackendValue != "AthenaCharacter" || item.Images.Featured == "" || slices.Contains[[]string]([]string{
|
|
"Soccer",
|
|
"Football",
|
|
"Waypoint",
|
|
}, item.Set.BackendValue) {
|
|
return
|
|
}
|
|
|
|
for _, tag := range item.GameplayTags {
|
|
if strings.Contains(tag, "StarterPack") {
|
|
return
|
|
}
|
|
}
|
|
|
|
c.FortniteItemsWithFeaturedImage = append(c.FortniteItemsWithFeaturedImage, item)
|
|
}
|
|
|
|
func (c *dataClient) AddBackpackToItem(backpack *FortniteItem) {
|
|
if backpack.ItemPreviewHeroPath == "" {
|
|
return
|
|
}
|
|
|
|
splitter := strings.Split(backpack.ItemPreviewHeroPath, "/")
|
|
character, found := c.FortniteItems[splitter[len(splitter) - 1]]
|
|
if !found {
|
|
return
|
|
}
|
|
|
|
character.Backpack = backpack
|
|
}
|
|
|
|
func (c *dataClient) AddDisplayAssetToItem(displayAsset string) {
|
|
split := strings.Split(displayAsset, "_")[1:]
|
|
found := c.FortniteItems[strings.Join(split[:], "_")]
|
|
|
|
if found == nil && split[0] == "CID" {
|
|
r := aid.Regex(strings.Join(split[:], "_"), `(?:CID_)(\d+|A_\d+)(?:_.+)`)
|
|
if r != nil {
|
|
found = GetItemByShallowID(*r)
|
|
}
|
|
}
|
|
|
|
if found == nil {
|
|
return
|
|
}
|
|
|
|
found.DisplayAssetPath2 = displayAsset
|
|
c.FortniteItemsWithDisplayAssets[found.ID] = found
|
|
c.TypedFortniteItemsWithDisplayAssets[found.Type.BackendValue] = append(c.TypedFortniteItemsWithDisplayAssets[found.Type.BackendValue], found)
|
|
}
|
|
|
|
func (c *dataClient) AddNumericStylesToItem(item *FortniteItem) {
|
|
ownedStyles := []FortniteVariantChannel{}
|
|
for i := 0; i < 100; i++ {
|
|
ownedStyles = append(ownedStyles, FortniteVariantChannel{
|
|
Tag: fmt.Sprint(i),
|
|
})
|
|
}
|
|
|
|
item.Variants = append(item.Variants, FortniteVariant{
|
|
Channel: "Numeric",
|
|
Type: "int",
|
|
Options: ownedStyles,
|
|
})
|
|
}
|
|
|
|
func (c *dataClient) GetStorefrontDailyItemCount(season int) int {
|
|
currentValue := 4
|
|
for _, item := range c.StorefrontDailyItemCountLookup {
|
|
if item.Season > season {
|
|
continue
|
|
}
|
|
currentValue = item.Items
|
|
}
|
|
return currentValue
|
|
}
|
|
|
|
func (c *dataClient) GetStorefrontWeeklySetCount(season int) int {
|
|
currentValue := 2
|
|
for _, item := range c.StorefrontWeeklySetCountLookup {
|
|
if item.Season > season {
|
|
continue
|
|
}
|
|
currentValue = item.Sets
|
|
}
|
|
return currentValue
|
|
}
|
|
|
|
func (c *dataClient) GetStorefrontCosmeticOfferPrice(rarity string, type_ string) int {
|
|
return c.StorefrontCosmeticOfferPriceLookup[rarity][type_]
|
|
}
|
|
|
|
func (c *dataClient) GetStorefrontCurrencyOfferPrice(currency string, amount int) int {
|
|
return c.StorefrontCurrencyOfferPriceLookup[currency][amount]
|
|
}
|
|
|
|
func (c *dataClient) GetLocalizedPrice(currency string, amount int) int {
|
|
return int(float64(amount) * c.StorefrontCurrencyMultiplier[currency])
|
|
}
|
|
|
|
func PreloadCosmetics() error {
|
|
DataClient = NewDataClient()
|
|
DataClient.LoadExternalData()
|
|
|
|
aid.Print("(snow) " + fmt.Sprint(len(DataClient.FortniteItems)) + " cosmetics loaded from fortnite-api.com")
|
|
return nil
|
|
}
|
|
|
|
func GetItemByShallowID(shallowID string) *FortniteItem {
|
|
for _, item := range DataClient.TypedFortniteItems["AthenaCharacter"] {
|
|
if strings.Contains(item.ID, shallowID) {
|
|
return item
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func GetRandomItemWithDisplayAsset() *FortniteItem {
|
|
items := DataClient.FortniteItemsWithDisplayAssets
|
|
if len(items) == 0 {
|
|
return nil
|
|
}
|
|
|
|
flat := []FortniteItem{}
|
|
for _, item := range items {
|
|
flat = append(flat, *item)
|
|
}
|
|
|
|
slices.SortFunc[[]FortniteItem](flat, func(a, b FortniteItem) int {
|
|
return strings.Compare(a.ID, b.ID)
|
|
})
|
|
|
|
return &flat[aid.RandomInt(0, len(flat))]
|
|
}
|
|
|
|
func GetRandomItemWithDisplayAssetOfNotType(notType string) *FortniteItem {
|
|
flat := []FortniteItem{}
|
|
|
|
for t, items := range DataClient.TypedFortniteItemsWithDisplayAssets {
|
|
if t == notType {
|
|
continue
|
|
}
|
|
|
|
for _, item := range items {
|
|
flat = append(flat, *item)
|
|
}
|
|
}
|
|
|
|
slices.SortFunc[[]FortniteItem](flat, func(a, b FortniteItem) int {
|
|
return strings.Compare(a.ID, b.ID)
|
|
})
|
|
|
|
return &flat[aid.RandomInt(0, len(flat))]
|
|
}
|
|
|
|
func GetRandomSet() *FortniteSet {
|
|
sets := []FortniteSet{}
|
|
for _, set := range DataClient.FortniteSets {
|
|
if set.BackendName == "" {
|
|
continue
|
|
}
|
|
sets = append(sets, *set)
|
|
}
|
|
|
|
slices.SortFunc[[]FortniteSet](sets, func(a, b FortniteSet) int {
|
|
return strings.Compare(a.BackendName, b.BackendName)
|
|
})
|
|
|
|
return &sets[aid.RandomInt(0, len(sets))]
|
|
} |