ps99-api 2.4.0 โ 2.6.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/runConfigurations/test_changing.xml +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/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/rarity.d.ts +1 -0
- package/example-web/react/package-lock.json +1504 -1470
- package/example-web/react2/package-lock.json +3089 -2766
- 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 +15 -15
- package/example-web/react2/src/assets/guild_placeholder.png +0 -0
- package/example-web/react2/src/components/AchievementsComponent.tsx +74 -19
- package/example-web/react2/src/components/AutoSizer.tsx +49 -0
- package/example-web/react2/src/components/BoostsComponent.tsx +16 -5
- package/example-web/react2/src/components/BoothsComponent.tsx +22 -52
- package/example-web/react2/src/components/BoxesComponent.tsx +48 -16
- package/example-web/react2/src/components/BuffsComponent.tsx +82 -34
- package/example-web/react2/src/components/CharmsComponent.tsx +84 -24
- package/example-web/react2/src/components/CollectionConfigIndex.tsx +867 -33
- package/example-web/react2/src/components/CollectionsIndex.tsx +380 -27
- package/example-web/react2/src/components/CollectionsLayout.tsx +60 -0
- package/example-web/react2/src/components/CurrencyComponent.tsx +57 -39
- package/example-web/react2/src/components/DynamicCollectionConfigData.tsx +172 -15
- package/example-web/react2/src/components/EggsComponent.tsx +50 -12
- package/example-web/react2/src/components/EnchantsComponent.tsx +88 -42
- package/example-web/react2/src/components/FishingRodsComponent.tsx +36 -22
- package/example-web/react2/src/components/Footer.tsx +18 -8
- package/example-web/react2/src/components/FruitsComponent.tsx +40 -17
- package/example-web/react2/src/components/GenericFetchComponent.tsx +9 -1
- package/example-web/react2/src/components/GuildBattlesComponent.tsx +41 -34
- package/example-web/react2/src/components/Header.tsx +39 -52
- package/example-web/react2/src/components/HomePage.tsx +15 -17
- package/example-web/react2/src/components/HoverboardsComponent.tsx +23 -99
- 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 +22 -7
- package/example-web/react2/src/components/MasteryComponent.tsx +165 -30
- package/example-web/react2/src/components/MerchantsComponent.tsx +41 -16
- package/example-web/react2/src/components/MiscItemsComponent.tsx +26 -31
- package/example-web/react2/src/components/PetsComponent.tsx +100 -61
- package/example-web/react2/src/components/PotionsComponent.tsx +121 -27
- package/example-web/react2/src/components/RandomEventsComponent.tsx +32 -23
- package/example-web/react2/src/components/RanksComponent.tsx +187 -62
- package/example-web/react2/src/components/RarityComponent.tsx +123 -5
- package/example-web/react2/src/components/ReactWindowMock.tsx +73 -0
- package/example-web/react2/src/components/RebirthsComponent.tsx +36 -19
- package/example-web/react2/src/components/SecretRoomsComponent.tsx +5 -4
- package/example-web/react2/src/components/SeedsComponent.tsx +41 -21
- package/example-web/react2/src/components/ShovelsComponent.tsx +21 -9
- package/example-web/react2/src/components/Sidebar.tsx +105 -0
- package/example-web/react2/src/components/SprinklersComponent.tsx +25 -10
- package/example-web/react2/src/components/Tooltip.tsx +36 -0
- package/example-web/react2/src/components/UltimatesComponent.tsx +28 -16
- package/example-web/react2/src/components/UpgradesComponent.tsx +97 -47
- package/example-web/react2/src/components/WateringCansComponent.tsx +20 -14
- package/example-web/react2/src/components/WorldsComponent.tsx +21 -11
- package/example-web/react2/src/components/XPPotionsComponent.tsx +28 -11
- package/example-web/react2/src/components/ZoneFlagsComponent.tsx +25 -14
- package/example-web/react2/src/components/ZonesComponent.tsx +43 -60
- 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/context/ScrollContext.tsx +35 -0
- package/example-web/react2/src/hooks/useCollapsibleHeader.ts +69 -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/src/utils/gigantix.ts +40 -0
- 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/index.ts +1 -0
- package/src/responses/collection/rarity.ts +1 -0
- package/tsconfig.json +1 -1
- package/example-web/react2/public/service-worker.js +0 -63
|
@@ -1,67 +1,117 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { CollectionConfigData } from "ps99-api";
|
|
3
|
+
import ItemCard from "./ItemCard";
|
|
3
4
|
import ImageComponent from "./ImageComponent";
|
|
5
|
+
import { useExpandableList } from "../hooks/useExpandableList";
|
|
6
|
+
import { formatGigantix } from "../utils/gigantix";
|
|
4
7
|
|
|
5
8
|
const UpgradeComponent: React.FC<{
|
|
6
9
|
configData: CollectionConfigData<"Upgrades">;
|
|
7
10
|
}> = ({ configData }) => {
|
|
11
|
+
const { expandedIndices, toggle, expandAll, collapseAll, isExpanded } = useExpandableList(configData.TierCurrencies.length);
|
|
12
|
+
|
|
8
13
|
return (
|
|
9
|
-
<div>
|
|
10
|
-
<h2>Upgrade</h2>
|
|
14
|
+
<div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
|
|
11
15
|
{configData.Icon && (
|
|
12
|
-
<
|
|
16
|
+
<div style={{ maxWidth: '100px', margin: '0 auto 20px' }}>
|
|
17
|
+
<ImageComponent src={configData.Icon} alt="Upgrade Icon" />
|
|
18
|
+
</div>
|
|
13
19
|
)}
|
|
14
|
-
|
|
15
|
-
<
|
|
16
|
-
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
<h3>Tier Costs</h3>
|
|
23
|
-
<ul>
|
|
24
|
-
{configData.TierCosts.map((cost, index) => (
|
|
25
|
-
<li key={index}>
|
|
26
|
-
Tier {index + 1}: {cost}
|
|
27
|
-
</li>
|
|
28
|
-
))}
|
|
29
|
-
</ul>
|
|
30
|
-
<h3>Tier Currencies</h3>
|
|
31
|
-
{configData.TierCurrencies.map((currency, index) => (
|
|
32
|
-
<div key={index}>
|
|
33
|
-
<h4>{currency.DisplayName}</h4>
|
|
34
|
-
<p>Rarity Number: {currency.Rarity.RarityNumber}</p>
|
|
35
|
-
<p>Description: {currency.Desc}</p>
|
|
36
|
-
{currency.Tradable && <p>Tradable</p>}
|
|
37
|
-
<p>Max Amount: {currency.MaxAmount}</p>
|
|
38
|
-
<h5>Bag Tiers</h5>
|
|
39
|
-
<ul>
|
|
40
|
-
{currency.BagTiers.map((bagTier, bagIndex) => (
|
|
41
|
-
<li key={bagIndex}>
|
|
42
|
-
<p>Value: {bagTier.value}</p>
|
|
43
|
-
<ImageComponent
|
|
44
|
-
src={bagTier.image}
|
|
45
|
-
alt={`Bag Tier ${bagTier.value}`}
|
|
46
|
-
/>
|
|
20
|
+
|
|
21
|
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '20px', marginBottom: '30px' }}>
|
|
22
|
+
<div>
|
|
23
|
+
<h3 style={{ borderBottom: '1px solid #eee', paddingBottom: '5px' }}>Tier Powers</h3>
|
|
24
|
+
<ul style={{ listStyle: 'none', padding: 0, textAlign: 'left' }}>
|
|
25
|
+
{configData.TierPowers.map((power, index) => (
|
|
26
|
+
<li key={index} style={{ padding: '5px 0', borderBottom: '1px dashed #eee' }}>
|
|
27
|
+
<strong>Tier {index + 1}:</strong> {formatGigantix(power)}
|
|
47
28
|
</li>
|
|
48
29
|
))}
|
|
49
30
|
</ul>
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
<
|
|
57
|
-
{tier.isBottom && <p>Is Bottom</p>}
|
|
58
|
-
<ImageComponent src={tier.orbImage} alt="Orb" />
|
|
59
|
-
<ImageComponent src={tier.imageOutline} alt="Outline" />
|
|
31
|
+
</div>
|
|
32
|
+
<div>
|
|
33
|
+
<h3 style={{ borderBottom: '1px solid #eee', paddingBottom: '5px' }}>Tier Costs</h3>
|
|
34
|
+
<ul style={{ listStyle: 'none', padding: 0, textAlign: 'left' }}>
|
|
35
|
+
{configData.TierCosts.map((cost, index) => (
|
|
36
|
+
<li key={index} style={{ padding: '5px 0', borderBottom: '1px dashed #eee' }}>
|
|
37
|
+
<strong>Tier {index + 1}:</strong> {formatGigantix(cost)}
|
|
60
38
|
</li>
|
|
61
39
|
))}
|
|
62
40
|
</ul>
|
|
63
41
|
</div>
|
|
64
|
-
|
|
42
|
+
</div>
|
|
43
|
+
|
|
44
|
+
<div>
|
|
45
|
+
<h3 style={{ fontSize: '1.2em', marginBottom: '15px' }}>Tier Currencies</h3>
|
|
46
|
+
<div style={{ marginBottom: '20px', display: 'flex', gap: '10px', justifyContent: 'center' }}>
|
|
47
|
+
<button onClick={expandAll} style={{ padding: '8px 16px', borderRadius: '8px', border: '1px solid #ddd', background: '#fff', cursor: 'pointer' }}>Expand All</button>
|
|
48
|
+
<button onClick={collapseAll} style={{ padding: '8px 16px', borderRadius: '8px', border: '1px solid #ddd', background: '#fff', cursor: 'pointer' }}>Collapse All</button>
|
|
49
|
+
</div>
|
|
50
|
+
{configData.TierCurrencies.map((currency, index) => (
|
|
51
|
+
<div key={index} style={{ background: '#f9f9f9', padding: '15px', borderRadius: '12px', marginBottom: '20px' }}>
|
|
52
|
+
<div
|
|
53
|
+
onClick={() => toggle(index)}
|
|
54
|
+
style={{ cursor: 'pointer' }}
|
|
55
|
+
>
|
|
56
|
+
<h4 style={{ marginTop: 0, display: 'flex', alignItems: 'center', gap: '8px' }}>
|
|
57
|
+
{isExpanded(index) ? 'โผ' : 'โถ'} {currency.DisplayName}
|
|
58
|
+
</h4>
|
|
59
|
+
<div style={{ display: 'flex', gap: '10px', justifyContent: 'center', marginBottom: '10px' }}>
|
|
60
|
+
<span className="badge" style={{ borderColor: (currency.Rarity?.Color as string) || '#ccc' }}>{currency.Rarity?.DisplayName}</span>
|
|
61
|
+
{currency.Tradable && <span className="badge">Tradable</span>}
|
|
62
|
+
<span className="badge">Max: {formatGigantix(currency.MaxAmount)}</span>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
{isExpanded(index) && (
|
|
67
|
+
<>
|
|
68
|
+
<p style={{ fontStyle: 'italic', fontSize: '0.9em', color: '#666' }}>{currency.Desc}</p>
|
|
69
|
+
|
|
70
|
+
<h5 style={{ marginTop: '15px', borderBottom: '1px solid #e0e0e0' }}>Bag Tiers</h5>
|
|
71
|
+
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(150px, 1fr))", gap: "10px", marginTop: '10px' }}>
|
|
72
|
+
{currency.BagTiers.map((bagTier, bagIndex) => (
|
|
73
|
+
<div key={bagIndex}>
|
|
74
|
+
<ItemCard
|
|
75
|
+
id={`bag-tier-${bagIndex}`}
|
|
76
|
+
amount={bagTier.value}
|
|
77
|
+
label={`Bag Tier ${bagIndex + 1}`}
|
|
78
|
+
itemData={{
|
|
79
|
+
icon: bagTier.image,
|
|
80
|
+
rarity: currency.Rarity,
|
|
81
|
+
name: `Bag Tier ${bagIndex + 1}`
|
|
82
|
+
}}
|
|
83
|
+
rarityColor={(currency.Rarity?.Color as string) || null}
|
|
84
|
+
/>
|
|
85
|
+
</div>
|
|
86
|
+
))}
|
|
87
|
+
</div>
|
|
88
|
+
|
|
89
|
+
<h5 style={{ marginTop: '15px', borderBottom: '1px solid #e0e0e0' }}>Tiers</h5>
|
|
90
|
+
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(150px, 1fr))", gap: "10px", marginTop: '10px' }}>
|
|
91
|
+
{currency.Tiers.map((tier, tierIndex) => (
|
|
92
|
+
<div key={tierIndex}>
|
|
93
|
+
<ItemCard
|
|
94
|
+
id={tier.tierName}
|
|
95
|
+
amount={tier.value}
|
|
96
|
+
label={tier.tierName}
|
|
97
|
+
itemData={{
|
|
98
|
+
icon: tier.orbImage,
|
|
99
|
+
rarity: currency.Rarity,
|
|
100
|
+
name: tier.tierName
|
|
101
|
+
}}
|
|
102
|
+
rarityColor={(currency.Rarity?.Color as string) || null}
|
|
103
|
+
/>
|
|
104
|
+
<div style={{ marginTop: '5px', fontSize: '0.8em', color: '#666', textAlign: 'center' }}>
|
|
105
|
+
Order: {tier.Order}
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
))}
|
|
109
|
+
</div>
|
|
110
|
+
</>
|
|
111
|
+
)}
|
|
112
|
+
</div>
|
|
113
|
+
))}
|
|
114
|
+
</div>
|
|
65
115
|
</div>
|
|
66
116
|
);
|
|
67
117
|
};
|
|
@@ -1,25 +1,31 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { CollectionConfigData } from "ps99-api";
|
|
3
|
-
import
|
|
3
|
+
import ItemCard from "./ItemCard";
|
|
4
4
|
|
|
5
5
|
const WateringCanComponent: React.FC<{
|
|
6
6
|
configData: CollectionConfigData<"WateringCans">;
|
|
7
7
|
}> = ({ configData }) => {
|
|
8
8
|
return (
|
|
9
|
-
<div>
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
<div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
|
|
10
|
+
|
|
11
|
+
<div style={{ maxWidth: '300px', margin: '0 auto 15px auto' }}>
|
|
12
|
+
<ItemCard
|
|
13
|
+
id={configData.DisplayName}
|
|
14
|
+
amount={1}
|
|
15
|
+
label={configData.DisplayName}
|
|
16
|
+
itemData={{
|
|
17
|
+
icon: configData.Icon,
|
|
18
|
+
rarity: undefined,
|
|
19
|
+
name: configData.DisplayName
|
|
20
|
+
}}
|
|
21
|
+
rarityColor={null}
|
|
16
22
|
/>
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
</
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px', fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
|
|
26
|
+
<p><strong>Time Mult:</strong> {configData.PlantTimeMultiplier}x</p>
|
|
27
|
+
<p><strong>Duration:</strong> {configData.PlantTimeMultiplierDuration}</p>
|
|
28
|
+
</div>
|
|
23
29
|
</div>
|
|
24
30
|
);
|
|
25
31
|
};
|
|
@@ -5,21 +5,31 @@ const WorldComponent: React.FC<{
|
|
|
5
5
|
configData: CollectionConfigData<"Worlds">;
|
|
6
6
|
}> = ({ configData }) => {
|
|
7
7
|
return (
|
|
8
|
-
<div>
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
<div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
|
|
9
|
+
<div style={{ textAlign: 'left', width: '100%' }}>
|
|
10
|
+
<p><strong>World #:</strong> {configData.WorldNumber}</p>
|
|
11
|
+
<p><strong>Spawn ID:</strong> {configData.SpawnId}</p>
|
|
12
|
+
<p><strong>Currency:</strong> {configData.WorldCurrency}</p>
|
|
13
|
+
<p><strong>Place ID:</strong> {configData.PlaceId}</p>
|
|
14
|
+
</div>
|
|
15
|
+
|
|
15
16
|
{configData.AdditionalMusic && configData.AdditionalMusic.length > 0 && (
|
|
16
|
-
<div>
|
|
17
|
+
<div style={{ marginTop: '15px', width: '100%' }}>
|
|
17
18
|
<h4>Additional Music</h4>
|
|
18
|
-
<
|
|
19
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px', justifyContent: 'center' }}>
|
|
19
20
|
{configData.AdditionalMusic.map((music, index) => (
|
|
20
|
-
<
|
|
21
|
+
<span key={index} style={{
|
|
22
|
+
background: '#e0f7fa',
|
|
23
|
+
color: '#006064',
|
|
24
|
+
padding: '5px 10px',
|
|
25
|
+
borderRadius: '15px',
|
|
26
|
+
fontSize: '0.9em',
|
|
27
|
+
fontWeight: 'bold'
|
|
28
|
+
}}>
|
|
29
|
+
{music}
|
|
30
|
+
</span>
|
|
21
31
|
))}
|
|
22
|
-
</
|
|
32
|
+
</div>
|
|
23
33
|
</div>
|
|
24
34
|
)}
|
|
25
35
|
</div>
|
|
@@ -1,21 +1,38 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { CollectionConfigData } from "ps99-api";
|
|
3
|
-
import
|
|
3
|
+
import ItemCard from "./ItemCard";
|
|
4
|
+
import { useItemResolution } from "../hooks/useItemResolution";
|
|
4
5
|
|
|
5
6
|
const XPPotionsComponent: React.FC<{
|
|
6
7
|
configData: CollectionConfigData<"XPPotions">;
|
|
7
8
|
}> = ({ configData }) => {
|
|
9
|
+
const { getRarityColor } = useItemResolution();
|
|
10
|
+
const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
|
|
11
|
+
|
|
8
12
|
return (
|
|
9
|
-
<div>
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
<div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
|
|
14
|
+
|
|
15
|
+
<div style={{ maxWidth: '300px', margin: '0 auto 15px auto' }}>
|
|
16
|
+
<ItemCard
|
|
17
|
+
id={configData.DisplayName}
|
|
18
|
+
amount={1}
|
|
19
|
+
label={configData.DisplayName}
|
|
20
|
+
itemData={{
|
|
21
|
+
icon: configData.Icon,
|
|
22
|
+
rarity: configData.Rarity,
|
|
23
|
+
name: configData.DisplayName
|
|
24
|
+
}}
|
|
25
|
+
rarityColor={rarityColor}
|
|
26
|
+
/>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div style={{ fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
|
|
30
|
+
<p style={{ marginBottom: '10px' }}>{configData.Desc}</p>
|
|
31
|
+
<div style={{ display: 'flex', gap: '10px', justifyContent: 'center' }}>
|
|
32
|
+
<span className="badge">Amount: {configData.Amount}</span>
|
|
33
|
+
<span className="badge" style={{ background: '#eee', color: '#333' }}>ID: {configData.ItemId}</span>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
19
36
|
</div>
|
|
20
37
|
);
|
|
21
38
|
};
|
|
@@ -1,24 +1,35 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { CollectionConfigData } from "ps99-api";
|
|
3
|
-
import
|
|
3
|
+
import ItemCard from "./ItemCard";
|
|
4
|
+
import { useItemResolution } from "../hooks/useItemResolution";
|
|
4
5
|
|
|
5
6
|
const ZoneFlagComponent: React.FC<{
|
|
6
7
|
configData: CollectionConfigData<"ZoneFlags">;
|
|
7
8
|
}> = ({ configData }) => {
|
|
9
|
+
const { getRarityColor } = useItemResolution();
|
|
10
|
+
const rarityColor = configData.Rarity ? getRarityColor(configData.Rarity) : null;
|
|
11
|
+
|
|
8
12
|
return (
|
|
9
|
-
<div>
|
|
10
|
-
|
|
11
|
-
<
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
13
|
+
<div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
|
|
14
|
+
|
|
15
|
+
<div style={{ maxWidth: '300px', margin: '0 auto 15px auto' }}>
|
|
16
|
+
<ItemCard
|
|
17
|
+
id={configData.Name}
|
|
18
|
+
amount={1}
|
|
19
|
+
label={configData.Name}
|
|
20
|
+
itemData={{
|
|
21
|
+
icon: configData.Icon,
|
|
22
|
+
rarity: configData.Rarity,
|
|
23
|
+
name: configData.Name
|
|
24
|
+
}}
|
|
25
|
+
rarityColor={rarityColor}
|
|
26
|
+
/>
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<div style={{ fontSize: '0.9em', color: '#666', textAlign: 'center' }}>
|
|
30
|
+
<p>{configData.Desc}</p>
|
|
31
|
+
<p style={{ fontWeight: 'bold', marginTop: '5px' }}>Duration: {configData.Duration}s</p>
|
|
32
|
+
</div>
|
|
22
33
|
</div>
|
|
23
34
|
);
|
|
24
35
|
};
|
|
@@ -5,79 +5,62 @@ const ZoneComponent: React.FC<{
|
|
|
5
5
|
configData: CollectionConfigData<"Zones">;
|
|
6
6
|
}> = ({ configData }) => {
|
|
7
7
|
return (
|
|
8
|
-
<div>
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
)}
|
|
8
|
+
<div style={{ width: '100%', height: '100%', boxSizing: 'border-box' }}>
|
|
9
|
+
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px', width: '100%', textAlign: 'left' }}>
|
|
10
|
+
<p><strong>Zone #:</strong> {configData.ZoneNumber}</p>
|
|
11
|
+
<p><strong>World #:</strong> {configData.WorldNumber}</p>
|
|
12
|
+
<p><strong>Currency:</strong> {configData.Currency}</p>
|
|
13
|
+
<p><strong>Max Egg:</strong> {configData.MaximumAvailableEgg}</p>
|
|
14
|
+
{configData.Price && <p><strong>Price:</strong> {configData.Price}</p>}
|
|
15
|
+
{configData.GateHealth && <p><strong>Gate Health:</strong> {configData.GateHealth}</p>}
|
|
16
|
+
{configData.TeleportToZoneOnFall && (
|
|
17
|
+
<p><strong>Teleport on Fall:</strong> Yes</p>
|
|
18
|
+
)}
|
|
19
|
+
{configData.Ambience && (
|
|
20
|
+
<p><strong>Ambience ID:</strong> {configData.Ambience.SoundId}</p>
|
|
21
|
+
)}
|
|
22
|
+
</div>
|
|
23
|
+
|
|
25
24
|
{configData.QuestsRequired && configData.QuestsRequired.length > 0 && (
|
|
26
|
-
<div>
|
|
25
|
+
<div style={{ width: '100%', marginTop: '20px' }}>
|
|
27
26
|
<h4>Quests Required</h4>
|
|
28
|
-
<
|
|
27
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '10px', justifyContent: 'center' }}>
|
|
29
28
|
{configData.QuestsRequired.map((quest, index) => (
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
<div key={index} style={{
|
|
30
|
+
background: '#f0f0f0',
|
|
31
|
+
padding: '10px',
|
|
32
|
+
borderRadius: '8px',
|
|
33
|
+
border: '1px solid #ddd'
|
|
34
|
+
}}>
|
|
35
|
+
<strong>{quest.Type}</strong>: {quest.Amount}
|
|
36
|
+
</div>
|
|
33
37
|
))}
|
|
34
|
-
</
|
|
38
|
+
</div>
|
|
35
39
|
</div>
|
|
36
40
|
)}
|
|
41
|
+
|
|
37
42
|
{configData.Lighting && (
|
|
38
|
-
<div>
|
|
39
|
-
<h4>Lighting</h4>
|
|
43
|
+
<div style={{ width: '100%', marginTop: '20px', textAlign: 'left', background: '#f9f9f9', padding: '15px', borderRadius: '8px' }}>
|
|
44
|
+
<h4 style={{ textAlign: 'center' }}>Lighting Settings</h4>
|
|
40
45
|
<p>Brightness: {configData.Lighting.Brightness}</p>
|
|
41
46
|
<p>Clock Time: {configData.Lighting.ClockTime}</p>
|
|
47
|
+
|
|
42
48
|
{configData.Lighting.Bloom && (
|
|
43
|
-
<div>
|
|
44
|
-
<
|
|
45
|
-
<
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
</div>
|
|
50
|
-
)}
|
|
51
|
-
{configData.Lighting.ColorCorrection && (
|
|
52
|
-
<div>
|
|
53
|
-
<h5>Color Correction</h5>
|
|
54
|
-
<p>
|
|
55
|
-
Enabled:{" "}
|
|
56
|
-
{configData.Lighting.ColorCorrection.Enabled.toString()}
|
|
57
|
-
</p>
|
|
58
|
-
<p>
|
|
59
|
-
Saturation: {configData.Lighting.ColorCorrection.Saturation}
|
|
60
|
-
</p>
|
|
61
|
-
<p>Contrast: {configData.Lighting.ColorCorrection.Contrast}</p>
|
|
62
|
-
<p>
|
|
63
|
-
Brightness: {configData.Lighting.ColorCorrection.Brightness}
|
|
64
|
-
</p>
|
|
49
|
+
<div style={{ marginTop: '10px' }}>
|
|
50
|
+
<strong>Bloom:</strong>
|
|
51
|
+
<span style={{ fontSize: '0.9em', marginLeft: '10px' }}>
|
|
52
|
+
Enabled: {configData.Lighting.Bloom.Enabled.toString()},
|
|
53
|
+
Intensity: {configData.Lighting.Bloom.Intensity}
|
|
54
|
+
</span>
|
|
65
55
|
</div>
|
|
66
56
|
)}
|
|
57
|
+
|
|
67
58
|
{configData.Lighting.Sky && (
|
|
68
|
-
<div>
|
|
69
|
-
<
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
{configData.Lighting.Sky.CelestialBodiesShown.toString()}
|
|
74
|
-
</p>
|
|
75
|
-
<p>Skybox Up: {configData.Lighting.Sky.SkyboxUp}</p>
|
|
76
|
-
<p>Skybox Bk: {configData.Lighting.Sky.SkyboxBk}</p>
|
|
77
|
-
<p>Skybox Dn: {configData.Lighting.Sky.SkyboxDn}</p>
|
|
78
|
-
<p>Skybox Lf: {configData.Lighting.Sky.SkyboxLf}</p>
|
|
79
|
-
<p>Skybox Rt: {configData.Lighting.Sky.SkyboxRt}</p>
|
|
80
|
-
<p>Skybox Ft: {configData.Lighting.Sky.SkyboxFt}</p>
|
|
59
|
+
<div style={{ marginTop: '10px' }}>
|
|
60
|
+
<strong>Sky:</strong>
|
|
61
|
+
<span style={{ fontSize: '0.9em', marginLeft: '10px' }}>
|
|
62
|
+
Stars: {configData.Lighting.Sky.StarCount}
|
|
63
|
+
</span>
|
|
81
64
|
</div>
|
|
82
65
|
)}
|
|
83
66
|
</div>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const COLLECTION_ICONS: Record<string, string> = {
|
|
2
|
+
Pets: "๐ฑ",
|
|
3
|
+
Eggs: "๐ฅ",
|
|
4
|
+
Enchants: "๐",
|
|
5
|
+
Potions: "๐งช",
|
|
6
|
+
Items: "๐",
|
|
7
|
+
Hoverboards: "๐น",
|
|
8
|
+
Booths: "๐ช",
|
|
9
|
+
Ultimates: "๐",
|
|
10
|
+
MiscItems: "๐ฆ",
|
|
11
|
+
Keys: "๐๏ธ",
|
|
12
|
+
Tickets: "๐๏ธ",
|
|
13
|
+
Charms: "๐งฟ",
|
|
14
|
+
Fruits: "๐",
|
|
15
|
+
Seeds: "๐ฑ",
|
|
16
|
+
Flags: "๐ฉ",
|
|
17
|
+
Glitch: "๐พ",
|
|
18
|
+
Achievements: "๐",
|
|
19
|
+
Boosts: "๐",
|
|
20
|
+
Upgrades: "โฌ๏ธ",
|
|
21
|
+
Relics: "๐ฟ",
|
|
22
|
+
Ranks: "๐",
|
|
23
|
+
RandomEvents: "๐ฒ",
|
|
24
|
+
SecretRooms: "๐",
|
|
25
|
+
Zones: "uq",
|
|
26
|
+
World: "๐",
|
|
27
|
+
Clan: "๐ก๏ธ",
|
|
28
|
+
Mastery: "๐",
|
|
29
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React, { createContext, useContext, useState, useCallback, ReactNode } from 'react';
|
|
2
|
+
import { PetSimulator99API, Collections, CollectionName } from "ps99-api";
|
|
3
|
+
|
|
4
|
+
const api = new PetSimulator99API();
|
|
5
|
+
|
|
6
|
+
type CollectionDataCache = Partial<Record<string, Collections[]>>;
|
|
7
|
+
|
|
8
|
+
interface CollectionDataContextType {
|
|
9
|
+
data: CollectionDataCache;
|
|
10
|
+
fetchCollection: (collectionName: CollectionName) => Promise<void>;
|
|
11
|
+
isLoading: (collectionName: string) => boolean;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const CollectionDataContext = createContext<CollectionDataContextType | undefined>(undefined);
|
|
15
|
+
|
|
16
|
+
export const CollectionDataProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
|
|
17
|
+
const [data, setData] = useState<CollectionDataCache>({});
|
|
18
|
+
const [loadingStates, setLoadingStates] = useState<Record<string, boolean>>({});
|
|
19
|
+
|
|
20
|
+
const fetchCollection = useCallback(async (collectionName: CollectionName) => {
|
|
21
|
+
// If data already exists, don't fetch again unless force refresh is needed (not implemented yet)
|
|
22
|
+
if (data[collectionName]) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (loadingStates[collectionName]) return; // Already loading
|
|
27
|
+
|
|
28
|
+
setLoadingStates(prev => ({ ...prev, [collectionName]: true }));
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
console.log(`[CollectionDataContext] Fetching ${collectionName}...`);
|
|
32
|
+
const result = await api.getCollection(collectionName);
|
|
33
|
+
if (result.status === "ok") {
|
|
34
|
+
setData(prev => ({ ...prev, [collectionName]: result.data as Collections[] }));
|
|
35
|
+
} else {
|
|
36
|
+
console.error(`Status error fetching ${collectionName}:`, result);
|
|
37
|
+
}
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.error(`Failed to fetch collection ${collectionName}`, error);
|
|
40
|
+
} finally {
|
|
41
|
+
setLoadingStates(prev => ({ ...prev, [collectionName]: false }));
|
|
42
|
+
}
|
|
43
|
+
}, [data, loadingStates]);
|
|
44
|
+
|
|
45
|
+
const isLoading = useCallback((collectionName: string) => {
|
|
46
|
+
return !!loadingStates[collectionName];
|
|
47
|
+
}, [loadingStates]);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<CollectionDataContext.Provider value={{ data, fetchCollection, isLoading }}>
|
|
51
|
+
{children}
|
|
52
|
+
</CollectionDataContext.Provider>
|
|
53
|
+
);
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const useCollectionData = () => {
|
|
57
|
+
const context = useContext(CollectionDataContext);
|
|
58
|
+
if (!context) {
|
|
59
|
+
throw new Error('useCollectionData must be used within a CollectionDataProvider');
|
|
60
|
+
}
|
|
61
|
+
return context;
|
|
62
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React, { createContext, useContext, useRef, useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
interface ScrollContextType {
|
|
4
|
+
scrollPositions: React.MutableRefObject<Record<string, number>>;
|
|
5
|
+
saveScrollPosition: (key: string, offset: number) => void;
|
|
6
|
+
getScrollPosition: (key: string) => number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const ScrollContext = createContext<ScrollContextType | undefined>(undefined);
|
|
10
|
+
|
|
11
|
+
export const ScrollProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
|
12
|
+
const scrollPositions = useRef<Record<string, number>>({});
|
|
13
|
+
|
|
14
|
+
const saveScrollPosition = useCallback((key: string, offset: number) => {
|
|
15
|
+
scrollPositions.current[key] = offset;
|
|
16
|
+
}, []);
|
|
17
|
+
|
|
18
|
+
const getScrollPosition = useCallback((key: string) => {
|
|
19
|
+
return scrollPositions.current[key] || 0;
|
|
20
|
+
}, []);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<ScrollContext.Provider value={{ scrollPositions, saveScrollPosition, getScrollPosition }}>
|
|
24
|
+
{children}
|
|
25
|
+
</ScrollContext.Provider>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const useScrollPersistence = () => {
|
|
30
|
+
const context = useContext(ScrollContext);
|
|
31
|
+
if (!context) {
|
|
32
|
+
throw new Error("useScrollPersistence must be used within a ScrollProvider");
|
|
33
|
+
}
|
|
34
|
+
return context;
|
|
35
|
+
};
|