Skip to content

Commit 79fcb41

Browse files
committed
feat: add the most scuffed players stats implementation, cba to do it any better
1 parent b7dc721 commit 79fcb41

8 files changed

Lines changed: 276 additions & 12 deletions

File tree

src/constants/player_stats.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package constants
2+
3+
import "skycrypt/src/models"
4+
5+
var PLAYER_STATS = map[string]models.StatsInfo{
6+
"health": {"base": 100},
7+
"defense": {"base": 0},
8+
"strength": {"base": 0},
9+
"speed": {"base": 100},
10+
"critical_chance": {"base": 30},
11+
"critical_damage": {"base": 50},
12+
"intelligence": {"base": 0},
13+
"bonus_attack_speed": {"base": 0},
14+
"sea_creature_chance": {"base": 20},
15+
"magic_find": {"base": 0},
16+
"pet_luck": {"base": 0},
17+
"true_defense": {"base": 0},
18+
"ferocity": {"base": 0},
19+
"ability_damage": {"base": 0},
20+
"mining_speed": {"base": 0},
21+
"mining_fortune": {"base": 0},
22+
"farming_fortune": {"base": 0},
23+
"foraging_fortune": {"base": 0},
24+
"pristine": {"base": 0},
25+
"fishing_speed": {"base": 0},
26+
"health_regen": {"base": 100},
27+
"vitality": {"base": 100},
28+
"mending": {"base": 100},
29+
"combat_wisdom": {"base": 0},
30+
"mining_wisdom": {"base": 0},
31+
"farming_wisdom": {"base": 0},
32+
"foraging_wisdom": {"base": 0},
33+
"fishing_wisdom": {"base": 0},
34+
"enchanting_wisdom": {"base": 0},
35+
"alchemy_wisdom": {"base": 0},
36+
"carpentry_wisdom": {"base": 0},
37+
"runecrafting_wisdom": {"base": 0},
38+
"social_wisdom": {"base": 0},
39+
"mining_spread": {"base": 0},
40+
"gemstone_spread": {"base": 0},
41+
"ore_fortune": {"base": 0},
42+
"block_fortune": {"base": 0},
43+
"dwarven_metal_fortune": {"base": 0},
44+
"gemstone_fortune": {"base": 0},
45+
"wheat_fortune": {"base": 0},
46+
"carrot_fortune": {"base": 0},
47+
"potato_fortune": {"base": 0},
48+
"pumpkin_fortune": {"base": 0},
49+
"melon_fortune": {"base": 0},
50+
"mushroom_fortune": {"base": 0},
51+
"cactus_fortune": {"base": 0},
52+
"sugar_cane_fortune": {"base": 0},
53+
"nether_wart_fortune": {"base": 0},
54+
"cocoa_beans_fortune": {"base": 0},
55+
"double_hook_chance": {"base": 0},
56+
"trophy_fish_chance": {"base": 0},
57+
"heat_resistance": {"base": 0},
58+
"fear": {"base": 0},
59+
}

src/models/player_stats.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package models
2+
3+
type StatsInfo map[string]int

src/routes.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,8 @@ func SetupRoutes(app *fiber.App) {
102102
api.Get("/stats/:uuid/:profileId", routes.StatsHandler)
103103
api.Get("/stats/:uuid", routes.StatsHandler)
104104

105+
api.Get("/playerStats/:uuid/:profileId", routes.PlayerStatsHandler)
106+
105107
api.Get("/networth/:uuid/:profileId", routes.NetworthHandler)
106108

107109
api.Get("/gear/:uuid/:profileId", routes.GearHandler)

src/routes/pets.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,9 @@ func PetsHandler(c *fiber.Ctx) error {
2525
userProfileValue := profile.Members[uuid]
2626
userProfile := &userProfileValue
2727

28-
pets, err := stats.GetPets(userProfile, profile)
29-
if err != nil {
30-
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
31-
"error": fmt.Sprintf("Failed to get pets: %v", err),
32-
})
33-
}
34-
3528
fmt.Printf("Returning /api/pets/%s in %s\n", profileId, time.Since(timeNow))
3629

3730
return c.JSON(fiber.Map{
38-
"pets": pets,
31+
"pets": stats.GetPets(userProfile, profile),
3932
})
4033
}

src/routes/player_stats.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package routes
2+
3+
import (
4+
"fmt"
5+
"skycrypt/src/api"
6+
"skycrypt/src/stats"
7+
"time"
8+
9+
"github.com/gofiber/fiber/v2"
10+
)
11+
12+
func PlayerStatsHandler(c *fiber.Ctx) error {
13+
timeNow := time.Now()
14+
15+
uuid := c.Params("uuid")
16+
profileId := c.Params("profileId")
17+
18+
profile, err := api.GetProfile(uuid, profileId)
19+
if err != nil {
20+
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
21+
"error": fmt.Sprintf("Failed to get profile: %v", err),
22+
})
23+
}
24+
25+
userProfileValue := profile.Members[uuid]
26+
userProfile := &userProfileValue
27+
28+
fmt.Printf("Returning /api/playerStats/%s in %s\n", profileId, time.Since(timeNow))
29+
30+
return c.JSON(fiber.Map{
31+
"stats": stats.GetPlayerStats(userProfile, profile, profileId),
32+
})
33+
}

src/stats/pets.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -427,8 +427,7 @@ func GetPetScore(pets []models.ProcessedPet) models.PetScore {
427427
return output
428428
}
429429

430-
func GetPets(userProfile *skycrypttypes.Member, profile *skycrypttypes.Profile) (models.OutputPets, error) {
431-
430+
func GetPets(userProfile *skycrypttypes.Member, profile *skycrypttypes.Profile) models.OutputPets {
432431
allPets := []skycrypttypes.Pet{}
433432
allPets = append(allPets, userProfile.Pets.Pets...)
434433
if userProfile.Rift.DeadCats.Montezuma.Rarity != "" {
@@ -463,5 +462,5 @@ func GetPets(userProfile *skycrypttypes.Member, profile *skycrypttypes.Profile)
463462

464463
output.MissingPets = stats.StripPets(getMissingPets(userProfile, pets, profile.GameMode))
465464

466-
return output, nil
465+
return output
467466
}

src/stats/player_stats.go

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
package stats
2+
3+
import (
4+
"fmt"
5+
"maps"
6+
"skycrypt/src/constants"
7+
redis "skycrypt/src/db"
8+
"skycrypt/src/models"
9+
statsItems "skycrypt/src/stats/items"
10+
statsLeveling "skycrypt/src/stats/leveling"
11+
12+
skycrypttypes "github.com/DuckySoLucky/SkyCrypt-Types"
13+
jsoniter "github.com/json-iterator/go"
14+
)
15+
16+
func GetPlayerStats(userProfile *skycrypttypes.Member, profile *skycrypttypes.Profile, profileId string) map[string]models.StatsInfo {
17+
stats := map[string]models.StatsInfo{}
18+
for statName, statInfo := range constants.PLAYER_STATS {
19+
stats[statName] = models.StatsInfo{}
20+
maps.Copy(stats[statName], statInfo)
21+
}
22+
23+
items := getItems(userProfile, profileId)
24+
processedItems := processItems(items)
25+
26+
accessoriesStats := GetAccessories(userProfile, items)
27+
for statName, statValue := range accessoriesStats.Stats {
28+
if _, exists := stats[statName]; !exists {
29+
continue
30+
}
31+
32+
stats[statName]["accessories"] += int(statValue)
33+
}
34+
35+
armorStats := statsItems.GetStatsFromItems(processedItems["armor"])
36+
for statName, statValue := range armorStats {
37+
if _, exists := stats[statName]; !exists {
38+
continue
39+
}
40+
41+
stats[statName]["armor"] += int(statValue)
42+
}
43+
44+
equipmentStats := statsItems.GetStatsFromItems(processedItems["equipment"])
45+
for statName, statValue := range equipmentStats {
46+
if _, exists := stats[statName]; !exists {
47+
continue
48+
}
49+
50+
stats[statName]["equipment"] += int(statValue)
51+
}
52+
53+
skyblockLevel := GetSkyBlockLevel(userProfile)
54+
if skyblockLevel.Level > 0 {
55+
stats["health"]["skyblock_level"] = int(skyblockLevel.Level * 5)
56+
stats["strength"]["skyblock_level"] = int(skyblockLevel.Level / 5)
57+
}
58+
59+
slayerStats := GetSlayers(userProfile).Stats
60+
for statName, statValue := range slayerStats {
61+
if _, exists := stats[statName]; !exists {
62+
continue
63+
}
64+
65+
stats[statName]["slayers"] += int(statValue)
66+
}
67+
68+
pets := GetPets(userProfile, &skycrypttypes.Profile{})
69+
activePet := pets.Pets[0]
70+
if !activePet.Active {
71+
for i := range pets.Pets {
72+
if !pets.Pets[i].Active {
73+
continue
74+
}
75+
76+
activePet = pets.Pets[i]
77+
break
78+
}
79+
}
80+
81+
for statName, statValue := range activePet.Stats {
82+
if _, exists := stats[statName]; !exists {
83+
continue
84+
}
85+
86+
stats[statName]["active_pet"] += int(statValue)
87+
}
88+
89+
for statName, statValue := range pets.PetScore.Stats {
90+
if _, exists := stats[statName]; !exists {
91+
continue
92+
}
93+
94+
stats[statName]["pet_score"] += int(statValue)
95+
}
96+
97+
skills := GetSkills(userProfile, &skycrypttypes.Profile{}, &skycrypttypes.Player{})
98+
for skillId, skillData := range skills.Skills {
99+
statsBonus := constants.STATS_BONUS[fmt.Sprintf("skill_%s", skillId)]
100+
if statsBonus == nil {
101+
continue
102+
}
103+
104+
skillStats := constants.GetBonusStat(skillData.Level, fmt.Sprintf("skill_%s", skillId), skillData.MaxLevel)
105+
for statName, value := range skillStats {
106+
if _, exists := stats[statName]; !exists {
107+
continue
108+
}
109+
110+
stats[statName]["skills"] += int(value)
111+
}
112+
}
113+
114+
catacombs := userProfile.Dungeons.DungeonTypes["catacombs"]
115+
if catacombs.Experience > 0 {
116+
dungeoneeringLevel := statsLeveling.GetLevelByXp(int(catacombs.Experience), &statsLeveling.ExtraSkillData{Type: "dungeoneering"})
117+
skillStats := constants.GetBonusStat(dungeoneeringLevel.Level, "skill_dungeoneering", 50)
118+
for statName, value := range skillStats {
119+
if _, exists := stats[statName]; !exists {
120+
continue
121+
}
122+
123+
stats[statName]["dungeons"] += int(value)
124+
}
125+
}
126+
127+
bestiaryData := GetBestiary(userProfile)
128+
if bestiaryData.Level > 0 {
129+
stats["health"]["bestiary"] = int(bestiaryData.Level)
130+
}
131+
132+
for statName, statInfo := range stats {
133+
total := 0
134+
for _, value := range statInfo {
135+
total += value
136+
}
137+
stats[statName]["total"] = total
138+
}
139+
140+
return stats
141+
}
142+
143+
func getItems(userProfile *skycrypttypes.Member, profileId string) map[string][]skycrypttypes.Item {
144+
var items map[string][]skycrypttypes.Item
145+
cache, err := redis.Get(fmt.Sprintf("items:%s", profileId))
146+
if err == nil && cache != "" {
147+
var json = jsoniter.ConfigCompatibleWithStandardLibrary
148+
err = json.Unmarshal([]byte(cache), &items)
149+
if err != nil {
150+
return map[string][]skycrypttypes.Item{}
151+
}
152+
} else {
153+
items, err = GetItems(userProfile, profileId)
154+
if err != nil {
155+
return map[string][]skycrypttypes.Item{}
156+
}
157+
}
158+
159+
return items
160+
}
161+
162+
func processItems(rawItems map[string][]skycrypttypes.Item) map[string][]models.ProcessedItem {
163+
var processedItems = make(map[string][]models.ProcessedItem)
164+
inventoryKeys := []string{"armor", "equipment", "wardrobe", "inventory", "enderchest", "backpack"}
165+
for _, inventoryId := range inventoryKeys {
166+
inventoryData := rawItems[inventoryId]
167+
if len(inventoryData) == 0 {
168+
continue
169+
}
170+
171+
processedItems[inventoryId] = statsItems.ProcessItems(&inventoryData, inventoryId)
172+
}
173+
174+
return processedItems
175+
}

0 commit comments

Comments
 (0)