2023-11-21 23:42:39 +00:00
|
|
|
package fortnite
|
|
|
|
|
|
|
|
import (
|
2024-02-18 00:09:02 +00:00
|
|
|
"math/rand"
|
|
|
|
"regexp"
|
2023-11-26 22:39:05 +00:00
|
|
|
|
2023-11-21 23:42:39 +00:00
|
|
|
"github.com/ectrc/snow/aid"
|
2023-11-26 22:39:05 +00:00
|
|
|
"github.com/google/uuid"
|
2023-11-21 23:42:39 +00:00
|
|
|
)
|
|
|
|
|
2023-11-26 22:39:05 +00:00
|
|
|
var (
|
2024-02-18 00:09:02 +00:00
|
|
|
priceLookup = map[string]map[string]int{
|
2024-01-31 00:16:57 +00:00
|
|
|
"EFortRarity::Legendary": {
|
2024-01-21 00:46:06 +00:00
|
|
|
"AthenaCharacter": 2000,
|
2024-02-18 00:09:02 +00:00
|
|
|
"AthenaBackpack": 1500,
|
|
|
|
"AthenaPickaxe": 1500,
|
|
|
|
"AthenaGlider": 1800,
|
|
|
|
"AthenaDance": 500,
|
|
|
|
"AthenaItemWrap": 800,
|
2024-01-21 00:46:06 +00:00
|
|
|
},
|
2024-01-31 00:16:57 +00:00
|
|
|
"EFortRarity::Epic": {
|
2024-01-21 00:46:06 +00:00
|
|
|
"AthenaCharacter": 1500,
|
2024-02-18 00:09:02 +00:00
|
|
|
"AthenaBackpack": 1200,
|
|
|
|
"AthenaPickaxe": 1200,
|
|
|
|
"AthenaGlider": 1500,
|
|
|
|
"AthenaDance": 800,
|
|
|
|
"AthenaItemWrap": 800,
|
2024-01-21 00:46:06 +00:00
|
|
|
},
|
2024-01-31 00:16:57 +00:00
|
|
|
"EFortRarity::Rare": {
|
2024-01-21 00:46:06 +00:00
|
|
|
"AthenaCharacter": 1200,
|
2024-02-18 00:09:02 +00:00
|
|
|
"AthenaBackpack": 800,
|
|
|
|
"AthenaPickaxe": 800,
|
|
|
|
"AthenaGlider": 800,
|
|
|
|
"AthenaDance": 500,
|
|
|
|
"AthenaItemWrap": 600,
|
2024-01-21 00:46:06 +00:00
|
|
|
},
|
2024-01-31 00:16:57 +00:00
|
|
|
"EFortRarity::Uncommon": {
|
2024-01-21 00:46:06 +00:00
|
|
|
"AthenaCharacter": 800,
|
2024-02-18 00:09:02 +00:00
|
|
|
"AthenaBackpack": 200,
|
|
|
|
"AthenaPickaxe": 500,
|
|
|
|
"AthenaGlider": 500,
|
|
|
|
"AthenaDance": 200,
|
|
|
|
"AthenaItemWrap": 300,
|
2024-01-21 00:46:06 +00:00
|
|
|
},
|
2024-01-31 00:16:57 +00:00
|
|
|
"EFortRarity::Common": {
|
2024-01-21 00:46:06 +00:00
|
|
|
"AthenaCharacter": 500,
|
2024-02-18 00:09:02 +00:00
|
|
|
"AthenaBackpack": 200,
|
|
|
|
"AthenaPickaxe": 500,
|
|
|
|
"AthenaGlider": 500,
|
|
|
|
"AthenaDance": 200,
|
|
|
|
"AthenaItemWrap": 300,
|
2024-01-21 00:46:06 +00:00
|
|
|
},
|
2023-11-26 22:39:05 +00:00
|
|
|
}
|
2023-11-21 23:42:39 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
dailyItemLookup = map[int]int{
|
|
|
|
2: 4,
|
|
|
|
4: 6,
|
|
|
|
13: 10,
|
2023-11-21 23:42:39 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
weeklySetLookup = map[int]int{
|
|
|
|
2: 2,
|
|
|
|
4: 3,
|
|
|
|
11: 4,
|
|
|
|
13: 3,
|
2023-11-21 23:42:39 +00:00
|
|
|
}
|
2024-02-18 00:09:02 +00:00
|
|
|
)
|
2023-11-21 23:42:39 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
func price(rarity, type_ string) int {
|
|
|
|
return priceLookup[rarity][type_]
|
2023-12-03 15:10:37 +00:00
|
|
|
}
|
2023-11-21 23:42:39 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
func dailyItems(season int) int {
|
|
|
|
var items int
|
2023-11-21 23:42:39 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
for s, i := range dailyItemLookup {
|
|
|
|
if season >= s {
|
|
|
|
items = i
|
2023-12-03 20:16:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
return items
|
2023-12-03 20:16:40 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
func weeklySets(season int) int {
|
|
|
|
var sets int
|
2023-11-21 23:42:39 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
for s, i := range weeklySetLookup {
|
|
|
|
if season >= s {
|
|
|
|
sets = i
|
|
|
|
}
|
2023-11-21 23:42:39 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
return sets
|
2023-11-21 23:42:39 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
type FortniteCatalogSectionOffer struct {
|
|
|
|
ID string
|
|
|
|
Grants []*FortniteItem
|
|
|
|
TotalPrice int
|
|
|
|
Meta struct {
|
|
|
|
DisplayAssetPath string
|
|
|
|
NewDisplayAssetPath string
|
|
|
|
SectionId string
|
|
|
|
TileSize string
|
|
|
|
Category string
|
|
|
|
ProfileId string
|
2023-11-21 23:42:39 +00:00
|
|
|
}
|
2024-02-18 00:09:02 +00:00
|
|
|
Frontend struct {
|
|
|
|
Title string
|
|
|
|
Description string
|
|
|
|
ShortDescription string
|
2023-11-21 23:42:39 +00:00
|
|
|
}
|
2024-02-18 00:09:02 +00:00
|
|
|
Giftable bool
|
|
|
|
BundleInfo struct {
|
|
|
|
IsBundle bool
|
|
|
|
PricePercent float32
|
2023-11-21 23:42:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
func NewFortniteCatalogSectionOffer() *FortniteCatalogSectionOffer {
|
|
|
|
return &FortniteCatalogSectionOffer{}
|
2023-11-21 23:42:39 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
func (f *FortniteCatalogSectionOffer) GenerateID() {
|
|
|
|
for _, item := range f.Grants {
|
|
|
|
f.ID += item.Type.BackendValue + ":" + item.ID + ","
|
2023-12-03 02:02:19 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
f.ID = "v2:/" + aid.Hash([]byte(f.ID))
|
2023-12-03 02:02:19 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
func (f *FortniteCatalogSectionOffer) GenerateTotalPrice() {
|
|
|
|
if !f.BundleInfo.IsBundle {
|
|
|
|
f.TotalPrice = price(f.Grants[0].Rarity.BackendValue, f.Grants[0].Type.BackendValue)
|
|
|
|
return
|
|
|
|
}
|
2023-11-21 23:42:39 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
for _, item := range f.Grants {
|
|
|
|
f.TotalPrice += price(item.Rarity.BackendValue, item.Rarity.BackendValue)
|
|
|
|
}
|
2023-11-26 22:39:05 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
func (f *FortniteCatalogSectionOffer) GenerateFortniteCatalogSectionOffer() aid.JSON {
|
|
|
|
f.GenerateTotalPrice()
|
2023-11-21 23:42:39 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
itemGrantResponse := []aid.JSON{}
|
|
|
|
purchaseRequirementsResponse := []aid.JSON{}
|
2023-11-26 22:39:05 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
for _, item := range f.Grants {
|
|
|
|
itemGrantResponse = append(itemGrantResponse, aid.JSON{
|
|
|
|
"templateId": item.Type.BackendValue + ":" + item.ID,
|
|
|
|
"quantity": 1,
|
|
|
|
})
|
2023-12-03 02:02:19 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
purchaseRequirementsResponse = append(purchaseRequirementsResponse, aid.JSON{
|
|
|
|
"requirementType": "DenyOnItemOwnership",
|
|
|
|
"requiredId": item.Type.BackendValue + ":" + item.ID,
|
|
|
|
"minQuantity": 1,
|
|
|
|
})
|
2023-12-03 02:02:19 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
return aid.JSON{
|
|
|
|
"devName": uuid.New().String(),
|
|
|
|
"offerId": f.ID,
|
2023-11-26 22:39:05 +00:00
|
|
|
"offerType": "StaticPrice",
|
2024-02-18 00:09:02 +00:00
|
|
|
"prices": []aid.JSON{{
|
|
|
|
"currencyType": "MtxCurrency",
|
|
|
|
"currencySubType": "",
|
|
|
|
"regularPrice": f.TotalPrice,
|
|
|
|
"dynamicRegularPrice": f.TotalPrice,
|
|
|
|
"finalPrice": f.TotalPrice,
|
|
|
|
"basePrice": f.TotalPrice,
|
|
|
|
"saleExpiration": "9999-12-31T23:59:59.999Z",
|
|
|
|
}},
|
|
|
|
"itemGrants": itemGrantResponse,
|
|
|
|
"meta": aid.JSON{
|
|
|
|
"TileSize": f.Meta.TileSize,
|
|
|
|
"SectionId": f.Meta.SectionId,
|
|
|
|
"NewDisplayAssetPath": f.Meta.NewDisplayAssetPath,
|
|
|
|
"DisplayAssetPath": f.Meta.DisplayAssetPath,
|
|
|
|
},
|
|
|
|
"metaInfo": []aid.JSON{
|
|
|
|
{
|
|
|
|
"Key": "TileSize",
|
|
|
|
"Value": f.Meta.TileSize,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Key": "SectionId",
|
|
|
|
"Value": f.Meta.SectionId,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"Key": "NewDisplayAssetPath",
|
|
|
|
"Value": f.Meta.NewDisplayAssetPath,
|
|
|
|
},
|
2023-11-21 23:42:39 +00:00
|
|
|
{
|
2024-02-18 00:09:02 +00:00
|
|
|
"Key": "DisplayAssetPath",
|
|
|
|
"Value": f.Meta.DisplayAssetPath,
|
2023-11-21 23:42:39 +00:00
|
|
|
},
|
|
|
|
},
|
2024-02-18 00:09:02 +00:00
|
|
|
"giftInfo": aid.JSON{
|
|
|
|
"bIsEnabled": f.Giftable,
|
|
|
|
"forcedGiftBoxTemplateId": "",
|
|
|
|
"purchaseRequirements": purchaseRequirementsResponse,
|
|
|
|
"giftRecordIds": []string{},
|
|
|
|
},
|
|
|
|
"purchaseRequirements": purchaseRequirementsResponse,
|
|
|
|
"categories": []string{f.Meta.Category},
|
|
|
|
"title": f.Frontend.Title,
|
|
|
|
"description": f.Frontend.Description,
|
|
|
|
"shortDescription": f.Frontend.ShortDescription,
|
|
|
|
"displayAssetPath": f.Meta.DisplayAssetPath,
|
|
|
|
"appStoreId": []string{},
|
|
|
|
"fufillmentIds": []string{},
|
2023-11-21 23:42:39 +00:00
|
|
|
"dailyLimit": -1,
|
|
|
|
"weeklyLimit": -1,
|
|
|
|
"monthlyLimit": -1,
|
2024-02-18 00:09:02 +00:00
|
|
|
"sortPriority": 0,
|
|
|
|
"catalogGroupPriority": 0,
|
|
|
|
"filterWeight": 0,
|
|
|
|
"refundable": true,
|
2023-11-26 22:39:05 +00:00
|
|
|
}
|
2024-02-18 00:09:02 +00:00
|
|
|
}
|
2023-11-21 23:42:39 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
type FortniteCatalogSection struct {
|
|
|
|
Name string
|
|
|
|
Offers []*FortniteCatalogSectionOffer
|
|
|
|
}
|
2023-11-21 23:42:39 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
func NewFortniteCatalogSection(name string) *FortniteCatalogSection {
|
|
|
|
return &FortniteCatalogSection{
|
|
|
|
Name: name,
|
2023-11-21 23:42:39 +00:00
|
|
|
}
|
2024-02-18 00:09:02 +00:00
|
|
|
}
|
2023-11-21 23:42:39 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
func (f *FortniteCatalogSection) GenerateFortniteCatalogSection() aid.JSON {
|
|
|
|
catalogEntiresResponse := []aid.JSON{}
|
|
|
|
for _, offer := range f.Offers {
|
|
|
|
catalogEntiresResponse = append(catalogEntiresResponse, offer.GenerateFortniteCatalogSectionOffer())
|
2023-11-21 23:42:39 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
return aid.JSON{
|
|
|
|
"name": f.Name,
|
|
|
|
"catalogEntries": catalogEntiresResponse,
|
2024-01-31 21:40:47 +00:00
|
|
|
}
|
2023-12-03 02:02:19 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
func (f *FortniteCatalogSection) GetGroupedOffers() map[string][]*FortniteCatalogSectionOffer {
|
|
|
|
groupedOffers := map[string][]*FortniteCatalogSectionOffer{}
|
2023-12-03 02:02:19 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
for _, offer := range f.Offers {
|
|
|
|
if groupedOffers[offer.Meta.Category] == nil {
|
|
|
|
groupedOffers[offer.Meta.Category] = []*FortniteCatalogSectionOffer{}
|
2023-12-03 02:02:19 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
groupedOffers[offer.Meta.Category] = append(groupedOffers[offer.Meta.Category], offer)
|
|
|
|
}
|
2023-12-03 02:02:19 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
return groupedOffers
|
|
|
|
}
|
2023-12-03 02:02:19 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
type FortniteCatalog struct {
|
|
|
|
Sections []*FortniteCatalogSection
|
|
|
|
}
|
2023-12-03 20:16:40 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
func NewFortniteCatalog() *FortniteCatalog {
|
|
|
|
return &FortniteCatalog{}
|
|
|
|
}
|
2023-12-03 15:10:37 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
func (f *FortniteCatalog) GenerateFortniteCatalog() aid.JSON {
|
|
|
|
catalogSectionsResponse := []aid.JSON{}
|
|
|
|
for _, section := range f.Sections {
|
|
|
|
catalogSectionsResponse = append(catalogSectionsResponse, section.GenerateFortniteCatalogSection())
|
2023-12-03 02:02:19 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
return aid.JSON{
|
|
|
|
"storefronts": catalogSectionsResponse,
|
|
|
|
"refreshIntervalHrs": 24,
|
|
|
|
"dailyPurchaseHrs": 24,
|
|
|
|
"expiration": "9999-12-31T23:59:59.999Z",
|
2023-12-03 02:02:19 +00:00
|
|
|
}
|
2024-02-18 00:09:02 +00:00
|
|
|
}
|
2023-12-03 02:02:19 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
func NewRandomFortniteCatalog() *FortniteCatalog {
|
|
|
|
aid.SetRandom(rand.New(rand.NewSource(int64(aid.Config.Fortnite.ShopSeed) + aid.CurrentDayUnix())))
|
|
|
|
catalog := NewFortniteCatalog()
|
2023-12-09 00:21:08 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
daily := NewFortniteCatalogSection("BRDailyStorefront")
|
|
|
|
for len(daily.Offers) < dailyItems(aid.Config.Fortnite.Season) {
|
|
|
|
entry := newEntryFromFortniteItem(GetRandomItemWithDisplayAssetOfNotType("AthenaCharacter"), false)
|
|
|
|
entry.Meta.SectionId = "Daily"
|
|
|
|
daily.Offers = append(daily.Offers, entry)
|
2023-12-03 15:10:37 +00:00
|
|
|
}
|
2024-02-18 00:09:02 +00:00
|
|
|
catalog.Sections = append(catalog.Sections, daily)
|
2023-12-03 15:10:37 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
weekly := NewFortniteCatalogSection("BRWeeklyStorefront")
|
|
|
|
for len(weekly.GetGroupedOffers()) < weeklySets(aid.Config.Fortnite.Season) {
|
|
|
|
set := GetRandomSet()
|
2023-12-03 02:02:19 +00:00
|
|
|
for _, item := range set.Items {
|
2024-02-18 00:09:02 +00:00
|
|
|
if item.DisplayAssetPath == "" || item.DisplayAssetPath2 == "" {
|
|
|
|
continue
|
2023-12-03 15:10:37 +00:00
|
|
|
}
|
2023-12-03 02:02:19 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
entry := newEntryFromFortniteItem(item, true)
|
|
|
|
entry.Meta.Category = set.BackendName
|
|
|
|
entry.Meta.SectionId = "Featured"
|
|
|
|
weekly.Offers = append(weekly.Offers, entry)
|
2023-12-03 02:02:19 +00:00
|
|
|
}
|
2024-02-18 00:09:02 +00:00
|
|
|
}
|
|
|
|
catalog.Sections = append(catalog.Sections, weekly)
|
2023-12-03 02:02:19 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
return catalog
|
|
|
|
}
|
2023-12-03 02:02:19 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
func newEntryFromFortniteItem(fortniteItem *FortniteItem, addAssets bool) *FortniteCatalogSectionOffer {
|
|
|
|
displayAsset := regexp.MustCompile(`[^/]+$`).FindString(fortniteItem.DisplayAssetPath)
|
2023-12-03 15:10:37 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
entry := NewFortniteCatalogSectionOffer()
|
|
|
|
entry.Meta.TileSize = "Small"
|
|
|
|
if fortniteItem.Type.BackendValue == "AthenaCharacter" {
|
|
|
|
entry.Meta.TileSize = "Normal"
|
|
|
|
}
|
|
|
|
if addAssets {
|
|
|
|
entry.Meta.NewDisplayAssetPath = "/Game/Catalog/NewDisplayAssets/" + fortniteItem.DisplayAssetPath2 + "." + fortniteItem.DisplayAssetPath2
|
|
|
|
if displayAsset != "" {
|
|
|
|
entry.Meta.DisplayAssetPath = "/Game/Catalog/DisplayAssets/" + displayAsset + "." + displayAsset
|
2023-12-03 02:02:19 +00:00
|
|
|
}
|
2024-02-18 00:09:02 +00:00
|
|
|
}
|
|
|
|
entry.Meta.ProfileId = "athena"
|
|
|
|
entry.Giftable = true
|
|
|
|
entry.Grants = append(entry.Grants, fortniteItem)
|
|
|
|
entry.GenerateTotalPrice()
|
|
|
|
entry.GenerateID()
|
2023-12-03 02:02:19 +00:00
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
return entry
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetOfferByOfferId(id string) *FortniteCatalogSectionOffer {
|
|
|
|
catalog := NewRandomFortniteCatalog()
|
|
|
|
|
|
|
|
for _, section := range catalog.Sections {
|
|
|
|
for _, offer := range section.Offers {
|
|
|
|
if offer.ID == id {
|
|
|
|
return offer
|
|
|
|
}
|
|
|
|
}
|
2023-12-03 02:02:19 +00:00
|
|
|
}
|
|
|
|
|
2024-02-18 00:09:02 +00:00
|
|
|
return nil
|
2023-11-21 23:42:39 +00:00
|
|
|
}
|