ps99-api 2.3.3 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release-on-main.yml +1 -2
- package/.idea/node-ps99-api.iml +1 -0
- package/.idea/runConfigurations/test_changing.xml +1 -1
- package/README.md +1 -1
- package/debug_currency.json +57 -0
- package/debug_goals.json +271 -0
- package/dist/ps99-api.d.ts +2 -0
- package/dist/ps99-api.js +4 -1
- package/dist/ps99-api.js.map +1 -1
- package/dist/request-client/axios.js +6 -1
- package/dist/request-client/axios.js.map +1 -1
- package/dist/responses/collection/achievement.d.ts +2 -0
- package/dist/responses/collection/guild-battle.d.ts +2 -4
- package/dist/responses/collection/index.d.ts +1 -0
- package/dist/responses/collection/index.js +15 -0
- package/dist/responses/collection/index.js.map +1 -1
- package/dist/responses/collection/rank.d.ts +1 -0
- package/dist/responses/collection/rarity.d.ts +1 -0
- package/dist/responses/collection/seed.d.ts +1 -0
- package/dist/responses/exists.d.ts +2 -0
- package/example-web/react/package-lock.json +1504 -1470
- package/example-web/react2/package-lock.json +3082 -2759
- package/example-web/react2/package.json +6 -1
- package/example-web/react2/public/assets/gold_variant_icon.png +0 -0
- package/example-web/react2/public/assets/hot_cocoa_egg.png +0 -0
- package/example-web/react2/public/index.html +34 -31
- package/example-web/react2/src/App.tsx +6 -9
- package/example-web/react2/src/assets/guild_placeholder.png +0 -0
- package/example-web/react2/src/components/AchievementsComponent.tsx +78 -30
- package/example-web/react2/src/components/BoostsComponent.tsx +18 -14
- package/example-web/react2/src/components/BoothsComponent.tsx +24 -22
- package/example-web/react2/src/components/BoxesComponent.tsx +46 -21
- package/example-web/react2/src/components/BuffsComponent.tsx +83 -13
- package/example-web/react2/src/components/CharmsComponent.tsx +47 -29
- package/example-web/react2/src/components/CollectionConfigIndex.tsx +398 -35
- package/example-web/react2/src/components/CollectionsIndex.tsx +132 -23
- package/example-web/react2/src/components/CollectionsLayout.tsx +50 -0
- package/example-web/react2/src/components/CurrencyComponent.tsx +59 -50
- package/example-web/react2/src/components/DynamicCollectionConfigData.tsx +178 -11
- package/example-web/react2/src/components/EggsComponent.tsx +77 -44
- package/example-web/react2/src/components/EnchantsComponent.tsx +84 -34
- package/example-web/react2/src/components/FishingRodsComponent.tsx +38 -31
- package/example-web/react2/src/components/Footer.tsx +75 -18
- package/example-web/react2/src/components/FruitsComponent.tsx +41 -25
- package/example-web/react2/src/components/GenericFetchComponent.tsx +40 -22
- package/example-web/react2/src/components/GuildBattlesComponent.tsx +93 -65
- package/example-web/react2/src/components/Header.tsx +5 -37
- package/example-web/react2/src/components/HomePage.tsx +16 -16
- package/example-web/react2/src/components/HoverboardsComponent.tsx +25 -37
- package/example-web/react2/src/components/ImageComponent.tsx +255 -45
- package/example-web/react2/src/components/ItemCard.tsx +240 -0
- package/example-web/react2/src/components/LootboxesComponent.tsx +24 -16
- package/example-web/react2/src/components/MasteryComponent.tsx +93 -37
- package/example-web/react2/src/components/MerchantsComponent.tsx +46 -28
- package/example-web/react2/src/components/MiscItemsComponent.tsx +28 -26
- package/example-web/react2/src/components/PetsComponent.tsx +115 -46
- package/example-web/react2/src/components/PotionsComponent.tsx +53 -36
- package/example-web/react2/src/components/RandomEventsComponent.tsx +39 -35
- package/example-web/react2/src/components/RanksComponent.tsx +187 -71
- package/example-web/react2/src/components/RarityComponent.tsx +124 -13
- package/example-web/react2/src/components/RebirthsComponent.tsx +37 -26
- package/example-web/react2/src/components/SecretRoomsComponent.tsx +7 -13
- package/example-web/react2/src/components/SeedsComponent.tsx +45 -32
- package/example-web/react2/src/components/ShovelsComponent.tsx +23 -18
- package/example-web/react2/src/components/Sidebar.tsx +105 -0
- package/example-web/react2/src/components/SprinklersComponent.tsx +27 -19
- package/example-web/react2/src/components/Tooltip.tsx +36 -0
- package/example-web/react2/src/components/UltimatesComponent.tsx +29 -24
- package/example-web/react2/src/components/UpgradesComponent.tsx +99 -55
- package/example-web/react2/src/components/WateringCansComponent.tsx +23 -21
- package/example-web/react2/src/components/WorldsComponent.tsx +27 -24
- package/example-web/react2/src/components/XPPotionsComponent.tsx +29 -19
- package/example-web/react2/src/components/ZoneFlagsComponent.tsx +27 -23
- package/example-web/react2/src/components/ZonesComponent.tsx +56 -73
- package/example-web/react2/src/constants/collectionIcons.ts +29 -0
- package/example-web/react2/src/context/CollectionDataContext.tsx +62 -0
- package/example-web/react2/src/hooks/useExpandableList.ts +38 -0
- package/example-web/react2/src/hooks/useItemResolution.ts +351 -0
- package/example-web/react2/src/index.css +257 -0
- package/example-web/react2/src/index.tsx +2 -1
- package/example-web/react2/temp_model.rbxm +0 -0
- package/example-web/react2/webpack.config.js +103 -47
- package/package.json +11 -11
- package/ranks.json +1 -0
- package/repro_collection_fetch.ts +33 -0
- package/repro_image_fetch.ts +50 -0
- package/src/__tests__/__snapshots__/ps99-api-changes.ts.snap +34841 -10439
- package/src/__tests__/__snapshots__/ps99-api-live.ts.snap +160667 -67217
- package/src/ps99-api.ts +9 -5
- package/src/request-client/axios.ts +6 -2
- package/src/responses/collection/achievement.ts +2 -0
- package/src/responses/collection/guild-battle.ts +2 -4
- package/src/responses/collection/index.ts +1 -0
- package/src/responses/collection/rank.ts +1 -0
- package/src/responses/collection/rarity.ts +1 -0
- package/src/responses/collection/seed.ts +1 -0
- package/src/responses/exists.ts +2 -0
- package/tsconfig.json +1 -1
- package/example-web/react2/public/service-worker.js +0 -63
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
import {
|
|
3
|
+
PetSimulator99API,
|
|
4
|
+
EnchantmentData,
|
|
5
|
+
PotionData,
|
|
6
|
+
MiscItemData,
|
|
7
|
+
CurrencyData,
|
|
8
|
+
FruitData,
|
|
9
|
+
HoverboardData,
|
|
10
|
+
BoothData,
|
|
11
|
+
ZoneFlagData,
|
|
12
|
+
SeedData,
|
|
13
|
+
RandomEventData,
|
|
14
|
+
LootboxData,
|
|
15
|
+
UltimateData,
|
|
16
|
+
RarityData,
|
|
17
|
+
} from "ps99-api";
|
|
18
|
+
|
|
19
|
+
export interface ItemData {
|
|
20
|
+
icon: string | null;
|
|
21
|
+
rarity: any | null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const useItemResolution = () => {
|
|
25
|
+
const [collections, setCollections] = useState({
|
|
26
|
+
enchants: [] as EnchantmentData[],
|
|
27
|
+
potions: [] as PotionData[],
|
|
28
|
+
miscItems: [] as MiscItemData[],
|
|
29
|
+
currencies: [] as CurrencyData[],
|
|
30
|
+
fruits: [] as FruitData[],
|
|
31
|
+
hoverboards: [] as HoverboardData[],
|
|
32
|
+
booths: [] as BoothData[],
|
|
33
|
+
zoneFlags: [] as ZoneFlagData[],
|
|
34
|
+
seeds: [] as SeedData[],
|
|
35
|
+
randomEvents: [] as RandomEventData[],
|
|
36
|
+
lootboxes: [] as LootboxData[],
|
|
37
|
+
ultimates: [] as UltimateData[],
|
|
38
|
+
rarities: [] as RarityData[],
|
|
39
|
+
pets: [] as any[], // PetData
|
|
40
|
+
eggs: [] as any[], // EggData
|
|
41
|
+
});
|
|
42
|
+
const [loading, setLoading] = useState(true);
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
const api = new PetSimulator99API();
|
|
46
|
+
const fetchData = async () => {
|
|
47
|
+
try {
|
|
48
|
+
const results = await Promise.allSettled([
|
|
49
|
+
api.getCollection("Enchants"),
|
|
50
|
+
api.getCollection("Potions"),
|
|
51
|
+
api.getCollection("MiscItems"),
|
|
52
|
+
api.getCollection("Currency"),
|
|
53
|
+
api.getCollection("Fruits"),
|
|
54
|
+
api.getCollection("Hoverboards"),
|
|
55
|
+
api.getCollection("Booths"),
|
|
56
|
+
api.getCollection("ZoneFlags"),
|
|
57
|
+
api.getCollection("Seeds"),
|
|
58
|
+
api.getCollection("RandomEvents"),
|
|
59
|
+
api.getCollection("Lootboxes"),
|
|
60
|
+
api.getCollection("Ultimates"),
|
|
61
|
+
api.getCollection("Rarity"),
|
|
62
|
+
api.getCollection("Pets"),
|
|
63
|
+
api.getCollection("Eggs"),
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
const getData = (result: PromiseSettledResult<any>) =>
|
|
67
|
+
result.status === 'fulfilled' && result.value.status === 'ok' ? result.value.data : [];
|
|
68
|
+
|
|
69
|
+
const [
|
|
70
|
+
enchants, potions, miscItems, currencies, fruits, hoverboards,
|
|
71
|
+
booths, zoneFlags, seeds, randomEvents, lootboxes, ultimates, rarities,
|
|
72
|
+
pets, eggs
|
|
73
|
+
] = results.map(getData);
|
|
74
|
+
|
|
75
|
+
// Manual Injection for Missing Items
|
|
76
|
+
miscItems.push({
|
|
77
|
+
category: "MiscItems",
|
|
78
|
+
configName: "Superior Mini Chest",
|
|
79
|
+
configData: {
|
|
80
|
+
id: "76",
|
|
81
|
+
DisplayName: "Superior Mini Chest",
|
|
82
|
+
Icon: "17602729261", // rbxassetid://17602729261
|
|
83
|
+
Rarity: { DisplayName: "Exclusive", RarityNumber: 5 }
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Force rebuild check
|
|
88
|
+
console.log("Seeds (v2) loaded:", seeds.length);
|
|
89
|
+
seeds.forEach((s: any) => {
|
|
90
|
+
if (s.configName.includes("Insta") || s.configData.DisplayName.includes("Insta")) {
|
|
91
|
+
console.log("Found Insta Seed:", s.configName, s.configData.DisplayName);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
setCollections({
|
|
96
|
+
enchants, potions, miscItems, currencies, fruits, hoverboards,
|
|
97
|
+
booths, zoneFlags, seeds, randomEvents, lootboxes, ultimates, rarities,
|
|
98
|
+
pets, eggs
|
|
99
|
+
});
|
|
100
|
+
} catch (e) {
|
|
101
|
+
console.error("Failed to load item collections", e);
|
|
102
|
+
} finally {
|
|
103
|
+
setLoading(false);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
fetchData();
|
|
107
|
+
}, []);
|
|
108
|
+
|
|
109
|
+
const resolveItem = (id: string, tn?: number): ItemData & { name?: string } | null => {
|
|
110
|
+
try {
|
|
111
|
+
const {
|
|
112
|
+
enchants, potions, miscItems, currencies, fruits, hoverboards,
|
|
113
|
+
booths, zoneFlags, seeds, randomEvents, lootboxes, ultimates,
|
|
114
|
+
pets, eggs
|
|
115
|
+
} = collections;
|
|
116
|
+
|
|
117
|
+
const numericId = parseInt(id);
|
|
118
|
+
const isNumeric = !isNaN(numericId);
|
|
119
|
+
|
|
120
|
+
// Manual Mappings for Breakables/Events
|
|
121
|
+
if (id === "76") { // Superior Mini Chest
|
|
122
|
+
// Icon found via investigation: rbxassetid://17602729261
|
|
123
|
+
return { icon: "rbxassetid://17602729261", rarity: null, name: "Superior Mini Chest" };
|
|
124
|
+
}
|
|
125
|
+
if (id === "78" || id === "Hot Cocoa Egg") { // Hot Cocoa Egg
|
|
126
|
+
// Local Asset
|
|
127
|
+
return { icon: "/node-ps99-api/assets/hot_cocoa_egg.png", rarity: null, name: "Hot Cocoa Egg" };
|
|
128
|
+
}
|
|
129
|
+
if (id === "40") { // Lucky Block
|
|
130
|
+
const block = miscItems.find(i => i.configName.includes("Lucky Block")) || randomEvents.find(i => i.configName.includes("Lucky Block"));
|
|
131
|
+
const icon = (block?.configData as any).icon || block?.configData.Icon;
|
|
132
|
+
if (icon) return { icon: icon, rarity: (block?.configData as any).rarity || (block?.configData as any).Rarity, name: "Lucky Block" };
|
|
133
|
+
}
|
|
134
|
+
if (id === "41") { // Piñata
|
|
135
|
+
const pinata = miscItems.find(i => i.configName.includes("Pinata")) || randomEvents.find(i => i.configName.includes("Pinata"));
|
|
136
|
+
const icon = (pinata?.configData as any).icon || pinata?.configData.Icon;
|
|
137
|
+
if (icon) return { icon: icon, rarity: (pinata?.configData as any).rarity || (pinata?.configData as any).Rarity, name: "Piñata" };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (id === "Clan Gift") {
|
|
141
|
+
const box = lootboxes.find(i => i.configName === "Clan Gift");
|
|
142
|
+
if (box?.configData.Icon) return { icon: box.configData.Icon, rarity: (box.configData as any).Rarity, name: "Clan Gift" };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Pets
|
|
146
|
+
// Check exact match, or "Huge [name]", or numeric ID
|
|
147
|
+
const pet = pets.find(i => i.configName === id || i.configData.name === id || (isNumeric && (i.configData as any)._index === numericId));
|
|
148
|
+
if (pet) {
|
|
149
|
+
const data = pet.configData as any;
|
|
150
|
+
let icon = data.thumbnail;
|
|
151
|
+
if (tn === 1 && data.goldenThumbnail) icon = data.goldenThumbnail;
|
|
152
|
+
// if (tn === 2) ... Rainbow usually just uses regular or golden with shader, but for icon usage we might just use thumbnail
|
|
153
|
+
|
|
154
|
+
// If we have specific golden/rainbow thumbnails in data, use them.
|
|
155
|
+
// The PetsComponent used data.goldenThumbnail for pt=1.
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
icon: icon.startsWith("rbxassetid://") ? icon : `rbxassetid://${icon}`,
|
|
159
|
+
rarity: data.rarity || { DisplayName: "Basic", RarityNumber: 1 }, // Default if missing
|
|
160
|
+
name: data.name
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Eggs
|
|
165
|
+
const egg = eggs.find(i => i.configName === id || (i.configData as any).DisplayName === id || (isNumeric && (i.configData as any)._index === numericId));
|
|
166
|
+
if (egg) {
|
|
167
|
+
const data = egg.configData as any;
|
|
168
|
+
const icon = data.icon || data.Icon;
|
|
169
|
+
return {
|
|
170
|
+
icon: icon?.startsWith("rbxassetid://") ? icon : `rbxassetid://${icon}`,
|
|
171
|
+
rarity: data.rarity || data.Rarity,
|
|
172
|
+
name: data.DisplayName || data.name || egg.configName
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
if (id === "21" || id === "34") {
|
|
178
|
+
console.log(`Resolving special ID: ${id}, numeric: ${numericId}, tn: ${tn}`);
|
|
179
|
+
const sampleCurrency = currencies[0] as any;
|
|
180
|
+
console.log("Sample Currency _index:", sampleCurrency?._index, "Name:", sampleCurrency?.configName);
|
|
181
|
+
const match = currencies.find(i => (i.configData as any)._index === numericId);
|
|
182
|
+
console.log("Found Currency Match:", match?.configName);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Currency
|
|
186
|
+
const currency = currencies.find(i => i.configName === id || i.configName === `Currency | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
|
|
187
|
+
if (currency?.configData.Tiers) {
|
|
188
|
+
const tierIndex = tn ? tn - 1 : 0;
|
|
189
|
+
const tier = currency.configData.Tiers[tierIndex] || currency.configData.Tiers[0];
|
|
190
|
+
if (tier) {
|
|
191
|
+
const icon = tier.tinyImage || tier.orbImage;
|
|
192
|
+
if (icon) return { icon, rarity: null, name: (currency.configData as any).DisplayName || id };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// MiscItems
|
|
197
|
+
const misc = miscItems.find(i => i.configName === id || i.configName === `MiscItem | ${id}` || (i.configData as any).DisplayName === id || (isNumeric && (i.configData as any)._index === numericId));
|
|
198
|
+
if (misc) {
|
|
199
|
+
const data = misc.configData as any;
|
|
200
|
+
const icon = data.icon || data.Icon;
|
|
201
|
+
if (icon) return { icon, rarity: data.rarity || data.Rarity, name: data.DisplayName || data.name || misc.configName };
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Enchants
|
|
205
|
+
const enchant = enchants.find(i => i.configName === id || i.configName === `Enchant | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
|
|
206
|
+
if (enchant) {
|
|
207
|
+
const data = enchant.configData as any;
|
|
208
|
+
const tier = data.Tiers && data.Tiers[tn ? tn - 1 : 0] ? data.Tiers[tn ? tn - 1 : 0] : data;
|
|
209
|
+
const icon = tier.icon || tier.Icon || data.icon || data.Icon;
|
|
210
|
+
return {
|
|
211
|
+
icon: icon?.startsWith("rbxassetid://") ? icon : `rbxassetid://${icon}`,
|
|
212
|
+
rarity: tier.rarity || tier.Rarity || data.rarity || data.Rarity,
|
|
213
|
+
name: tier.DisplayName || data.DisplayName || data.Name || enchant.configName
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Potions
|
|
218
|
+
const potion = potions.find(i => i.configName === id || i.configName === `Potion | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
|
|
219
|
+
if (potion) {
|
|
220
|
+
const data = potion.configData as any;
|
|
221
|
+
const tier = data.Tiers && data.Tiers[tn ? tn - 1 : 0] ? data.Tiers[tn ? tn - 1 : 0] : data;
|
|
222
|
+
const icon = tier.icon || tier.Icon || data.icon || data.Icon;
|
|
223
|
+
return {
|
|
224
|
+
icon: icon?.startsWith("rbxassetid://") ? icon : `rbxassetid://${icon}`,
|
|
225
|
+
rarity: tier.rarity || tier.Rarity || data.rarity || data.Rarity,
|
|
226
|
+
name: tier.DisplayName || data.DisplayName || data.Name || potion.configName
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Fruits
|
|
231
|
+
const fruit = fruits.find(i => i.configName === id || i.configName === `Fruit | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
|
|
232
|
+
if (fruit?.configData.Icon) return { icon: fruit.configData.Icon, rarity: (fruit.configData as any).Rarity, name: (fruit.configData as any).DisplayName };
|
|
233
|
+
|
|
234
|
+
// Hoverboards
|
|
235
|
+
const hoverboard = hoverboards.find(i => i.configName === id || i.configName === `Hoverboard | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
|
|
236
|
+
if (hoverboard?.configData.Icon) return { icon: hoverboard.configData.Icon, rarity: (hoverboard.configData as any).Rarity, name: (hoverboard.configData as any).DisplayName };
|
|
237
|
+
|
|
238
|
+
// Booths
|
|
239
|
+
const booth = booths.find(i => i.configName === id || i.configName === `Booth | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
|
|
240
|
+
if (booth?.configData.Icon) return { icon: booth.configData.Icon, rarity: (booth.configData as any).Rarity, name: (booth.configData as any).DisplayName };
|
|
241
|
+
|
|
242
|
+
// ZoneFlags
|
|
243
|
+
const flag = zoneFlags.find(i => i.configName === id || i.configName === `ZoneFlag | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
|
|
244
|
+
if (flag?.configData.Icon) return { icon: flag.configData.Icon, rarity: (flag.configData as any).Rarity, name: (flag.configData as any).DisplayName };
|
|
245
|
+
|
|
246
|
+
// Seeds
|
|
247
|
+
const seed = seeds.find(i => i.configName === id || i.configName === `Seed | ${id}` || (i.configData as any).DisplayName === id || (isNumeric && (i.configData as any)._index === numericId));
|
|
248
|
+
if (seed?.configData.Icon) return { icon: seed.configData.Icon, rarity: (seed.configData as any).Rarity, name: (seed.configData as any).DisplayName };
|
|
249
|
+
|
|
250
|
+
// RandomEvents
|
|
251
|
+
const event = randomEvents.find(i => i.configName === id || i.configName === `RandomEvent | ${id}` || i.configData.Name === id || (isNumeric && (i.configData as any)._index === numericId));
|
|
252
|
+
if (event?.configData.Icon) return { icon: event.configData.Icon, rarity: null, name: event.configData.Name };
|
|
253
|
+
|
|
254
|
+
// Lootboxes
|
|
255
|
+
const box = lootboxes.find(i => i.configName === id || i.configName === `Lootbox | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
|
|
256
|
+
if (box) {
|
|
257
|
+
const data = box.configData as any;
|
|
258
|
+
const icon = data.icon || data.Icon;
|
|
259
|
+
if (icon) return { icon, rarity: data.rarity || data.Rarity, name: data.DisplayName || data.name };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Ultimates
|
|
263
|
+
const ult = ultimates.find(i => i.configName === id || i.configName === `Ultimate | ${id}` || (isNumeric && (i.configData as any)._index === numericId));
|
|
264
|
+
if (ult?.configData.Icon) return { icon: ult.configData.Icon, rarity: (ult.configData as any).Rarity, name: (ult.configData as any).DisplayName };
|
|
265
|
+
|
|
266
|
+
return null;
|
|
267
|
+
} catch (e) {
|
|
268
|
+
console.error("resolveItem crashed:", e);
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const getRarityColor = (rarityData: any) => {
|
|
274
|
+
if (!rarityData) return null;
|
|
275
|
+
if (rarityData.Color) return rarityData.Color;
|
|
276
|
+
if (rarityData._id) {
|
|
277
|
+
const r = collections.rarities.find(r => r.configData._id === rarityData._id || r.configData.DisplayName === rarityData._id);
|
|
278
|
+
if (r?.configData.Color) return r.configData.Color;
|
|
279
|
+
}
|
|
280
|
+
return null;
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
const resolveIcon = (itemData: any): string | null => {
|
|
284
|
+
try {
|
|
285
|
+
if (!itemData) return null;
|
|
286
|
+
|
|
287
|
+
if (itemData.name === "Hot Cocoa Egg" || itemData.DisplayName === "Hot Cocoa Egg") {
|
|
288
|
+
return "/node-ps99-api/assets/hot_cocoa_egg.png";
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Check for nested Tiers/Icons common in Enchants, Potions, Currency, Boxes
|
|
292
|
+
if (itemData.Tiers && Array.isArray(itemData.Tiers) && itemData.Tiers.length > 0) {
|
|
293
|
+
const firstTier = itemData.Tiers[0];
|
|
294
|
+
if (firstTier.Icon) return firstTier.Icon;
|
|
295
|
+
if (firstTier.icon) return firstTier.icon;
|
|
296
|
+
if (firstTier.orbImage) return firstTier.orbImage;
|
|
297
|
+
if (firstTier.tinyImage) return firstTier.tinyImage;
|
|
298
|
+
if (firstTier.imageOutline) return firstTier.imageOutline;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (itemData.Icons && Array.isArray(itemData.Icons) && itemData.Icons.length > 0) {
|
|
302
|
+
const firstIcon = itemData.Icons[0];
|
|
303
|
+
if (firstIcon.Icon) return firstIcon.Icon;
|
|
304
|
+
if (firstIcon.icon) return firstIcon.icon;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Handle Buffs (AssociatedItemID)
|
|
308
|
+
if (itemData.AssociatedItemID) {
|
|
309
|
+
const associatedId = itemData.AssociatedItemID;
|
|
310
|
+
// Check MiscItems first (most common for buffs like Toy Ball, Squeaky Toy)
|
|
311
|
+
const misc = collections.miscItems.find(i => i.configName === associatedId);
|
|
312
|
+
if (misc) {
|
|
313
|
+
const miscIcon = resolveIcon(misc.configData); // Recursive resolve
|
|
314
|
+
if (miscIcon) return miscIcon;
|
|
315
|
+
}
|
|
316
|
+
// Could check other collections if needed
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return (
|
|
320
|
+
itemData.icon ||
|
|
321
|
+
itemData.Icon ||
|
|
322
|
+
itemData.thumbnail ||
|
|
323
|
+
itemData.image ||
|
|
324
|
+
itemData.texture ||
|
|
325
|
+
itemData.orbImage ||
|
|
326
|
+
itemData.titanicIcon ||
|
|
327
|
+
itemData.petIcon ||
|
|
328
|
+
itemData.eggIcon ||
|
|
329
|
+
itemData.enchantIcon ||
|
|
330
|
+
itemData.potionIcon ||
|
|
331
|
+
itemData.fruitIcon ||
|
|
332
|
+
itemData.toyIcon ||
|
|
333
|
+
itemData.charmIcon ||
|
|
334
|
+
itemData.boothIcon ||
|
|
335
|
+
itemData.flagIcon ||
|
|
336
|
+
itemData.keyIcon ||
|
|
337
|
+
itemData.seedIcon ||
|
|
338
|
+
itemData.bookIcon ||
|
|
339
|
+
itemData.giftIcon ||
|
|
340
|
+
itemData.currencyIcon ||
|
|
341
|
+
itemData.miscIcon ||
|
|
342
|
+
null
|
|
343
|
+
);
|
|
344
|
+
} catch (e) {
|
|
345
|
+
console.error("resolveIcon crashed:", e);
|
|
346
|
+
return null;
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
return { loading, resolveItem, getRarityColor, resolveIcon };
|
|
351
|
+
};
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/* Import a cute, rounded font */
|
|
2
|
+
@import url('https://fonts.googleapis.com/css2?family=Fredoka:wght@300;400;500;600;700&display=swap');
|
|
3
|
+
|
|
4
|
+
:root {
|
|
5
|
+
--bg-color: #e0f7fa;
|
|
6
|
+
--primary-color: #4fc3f7;
|
|
7
|
+
--secondary-color: #ff8a65;
|
|
8
|
+
--accent-color: #ffd54f;
|
|
9
|
+
--text-color: #37474f;
|
|
10
|
+
--card-bg: #ffffff;
|
|
11
|
+
--border-radius: 16px;
|
|
12
|
+
--shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
13
|
+
--border-width: 3px;
|
|
14
|
+
--border-color: #263238;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
html,
|
|
18
|
+
body,
|
|
19
|
+
#root {
|
|
20
|
+
height: 100%;
|
|
21
|
+
margin: 0;
|
|
22
|
+
overflow: hidden;
|
|
23
|
+
/* Prevent body scroll, let apps handle it */
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#root {
|
|
27
|
+
display: flex;
|
|
28
|
+
flex-direction: column;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
body {
|
|
32
|
+
margin: 0;
|
|
33
|
+
font-family: 'Fredoka', sans-serif;
|
|
34
|
+
background-color: var(--bg-color);
|
|
35
|
+
color: var(--text-color);
|
|
36
|
+
-webkit-font-smoothing: antialiased;
|
|
37
|
+
-moz-osx-font-smoothing: grayscale;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
h1,
|
|
41
|
+
h2,
|
|
42
|
+
h3,
|
|
43
|
+
h4,
|
|
44
|
+
h5,
|
|
45
|
+
h6 {
|
|
46
|
+
color: var(--text-color);
|
|
47
|
+
text-transform: uppercase;
|
|
48
|
+
letter-spacing: 1px;
|
|
49
|
+
text-shadow: 2px 2px 0px white;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
a {
|
|
53
|
+
color: var(--primary-color);
|
|
54
|
+
text-decoration: none;
|
|
55
|
+
font-weight: bold;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
a:hover {
|
|
59
|
+
text-decoration: underline;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* Button Styles */
|
|
63
|
+
.game-button {
|
|
64
|
+
display: inline-block;
|
|
65
|
+
padding: 12px 24px;
|
|
66
|
+
background-color: var(--primary-color);
|
|
67
|
+
color: white;
|
|
68
|
+
font-size: 1.2rem;
|
|
69
|
+
font-weight: bold;
|
|
70
|
+
border: var(--border-width) solid var(--border-color);
|
|
71
|
+
border-radius: var(--border-radius);
|
|
72
|
+
box-shadow: 4px 4px 0px var(--border-color);
|
|
73
|
+
transition: all 0.1s ease;
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
text-transform: uppercase;
|
|
76
|
+
text-decoration: none !important;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
.game-button:hover {
|
|
80
|
+
transform: translate(-2px, -2px);
|
|
81
|
+
box-shadow: 6px 6px 0px var(--border-color);
|
|
82
|
+
background-color: #29b6f6;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.game-button:active {
|
|
86
|
+
transform: translate(2px, 2px);
|
|
87
|
+
box-shadow: 0px 0px 0px var(--border-color);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.game-button.secondary {
|
|
91
|
+
background-color: var(--secondary-color);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
.game-button.accent {
|
|
95
|
+
background-color: var(--accent-color);
|
|
96
|
+
color: var(--text-color);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/* Grids */
|
|
100
|
+
.item-grid {
|
|
101
|
+
display: grid;
|
|
102
|
+
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
|
103
|
+
gap: 16px;
|
|
104
|
+
padding: 20px;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.collection-grid {
|
|
108
|
+
display: grid;
|
|
109
|
+
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
110
|
+
gap: 20px;
|
|
111
|
+
padding: 20px;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/* Cards */
|
|
115
|
+
.game-card {
|
|
116
|
+
background: var(--card-bg);
|
|
117
|
+
border: var(--border-width) solid var(--border-color);
|
|
118
|
+
border-radius: var(--border-radius);
|
|
119
|
+
padding: 16px;
|
|
120
|
+
text-align: center;
|
|
121
|
+
box-shadow: 4px 4px 0px rgba(0, 0, 0, 0.1);
|
|
122
|
+
transition: transform 0.2s;
|
|
123
|
+
display: flex;
|
|
124
|
+
flex-direction: column;
|
|
125
|
+
align-items: center;
|
|
126
|
+
justify-content: center;
|
|
127
|
+
position: relative;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.game-card:hover {
|
|
131
|
+
transform: translateY(-5px);
|
|
132
|
+
box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.15);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.game-card img {
|
|
136
|
+
max-width: 100%;
|
|
137
|
+
height: auto;
|
|
138
|
+
border-radius: 8px;
|
|
139
|
+
margin-bottom: 8px;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/* Tooltips */
|
|
143
|
+
.tooltip-container {
|
|
144
|
+
position: relative;
|
|
145
|
+
display: inline-block;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.tooltip-popover {
|
|
149
|
+
position: absolute;
|
|
150
|
+
bottom: 120%;
|
|
151
|
+
left: 50%;
|
|
152
|
+
transform: translateX(-50%);
|
|
153
|
+
background-color: rgba(255, 255, 255, 0.95);
|
|
154
|
+
border: 2px solid var(--border-color);
|
|
155
|
+
border-radius: 8px;
|
|
156
|
+
padding: 10px;
|
|
157
|
+
min-width: 200px;
|
|
158
|
+
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
|
|
159
|
+
z-index: 1000;
|
|
160
|
+
pointer-events: none;
|
|
161
|
+
/* Prevent tooltip from blocking interaction */
|
|
162
|
+
font-size: 0.9rem;
|
|
163
|
+
line-height: 1.4;
|
|
164
|
+
text-align: left;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.tooltip-popover h4 {
|
|
168
|
+
margin: 0 0 5px 0;
|
|
169
|
+
font-size: 1rem;
|
|
170
|
+
color: var(--secondary-color);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
.badge {
|
|
175
|
+
background: #e0f2f1;
|
|
176
|
+
color: #00695c;
|
|
177
|
+
padding: 4px 8px;
|
|
178
|
+
border-radius: 12px;
|
|
179
|
+
font-size: 0.8em;
|
|
180
|
+
font-weight: bold;
|
|
181
|
+
border: 1px solid #b2dfdb;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* Animations */
|
|
185
|
+
@keyframes sheen {
|
|
186
|
+
0% {
|
|
187
|
+
transform: translateX(-100%) rotate(45deg);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
20%,
|
|
191
|
+
100% {
|
|
192
|
+
transform: translateX(100%) rotate(45deg);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.sheen-effect {
|
|
197
|
+
position: relative;
|
|
198
|
+
overflow: hidden;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.sheen-effect::after {
|
|
202
|
+
content: '';
|
|
203
|
+
position: absolute;
|
|
204
|
+
top: 0;
|
|
205
|
+
left: 0;
|
|
206
|
+
width: 100%;
|
|
207
|
+
height: 100%;
|
|
208
|
+
background: linear-gradient(120deg, transparent, rgba(255, 255, 255, 0.6), transparent);
|
|
209
|
+
transform: translateX(-100%);
|
|
210
|
+
animation: sheen 3s infinite;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
@keyframes shine {
|
|
214
|
+
0% {
|
|
215
|
+
left: -100%;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
100% {
|
|
219
|
+
left: 100%;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/* Header & Footer */
|
|
224
|
+
.game-header {
|
|
225
|
+
padding: 1rem;
|
|
226
|
+
background-color: var(--primary-color);
|
|
227
|
+
border-bottom: var(--border-width) solid var(--border-color);
|
|
228
|
+
display: flex;
|
|
229
|
+
justify-content: space-between;
|
|
230
|
+
align-items: center;
|
|
231
|
+
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.game-header a {
|
|
235
|
+
color: white;
|
|
236
|
+
text-decoration: none;
|
|
237
|
+
text-shadow: 2px 2px 0px var(--border-color);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.game-header ol {
|
|
241
|
+
display: flex;
|
|
242
|
+
list-style: none;
|
|
243
|
+
padding: 0;
|
|
244
|
+
margin: 0;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
.game-header ol li {
|
|
248
|
+
margin: 0 0.5em;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.game-footer {
|
|
252
|
+
text-align: center;
|
|
253
|
+
padding: 1rem;
|
|
254
|
+
margin-top: 2rem;
|
|
255
|
+
background-color: var(--card-bg);
|
|
256
|
+
border-top: var(--border-width) solid var(--border-color);
|
|
257
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import "./index.css";
|
|
2
3
|
import { createRoot } from "react-dom/client";
|
|
3
4
|
import App from "./App";
|
|
4
5
|
import * as serviceWorkerRegistration from "./serviceWorkerRegistration";
|
|
@@ -8,4 +9,4 @@ const root = createRoot(container!);
|
|
|
8
9
|
root.render(<App />);
|
|
9
10
|
|
|
10
11
|
// Register the service worker
|
|
11
|
-
serviceWorkerRegistration.
|
|
12
|
+
serviceWorkerRegistration.unregister();
|
|
Binary file
|