Skip to content

Commit 37954cb

Browse files
committed
wiki skins
1 parent b8a7dad commit 37954cb

File tree

10 files changed

+1007
-252
lines changed

10 files changed

+1007
-252
lines changed

src/components/Profile/EquipmentPanel.tsx

Lines changed: 114 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useProfile } from '../../context/ProfileContext';
22
import { useComparison } from '../../context/ComparisonContext';
33
import { Card } from '../UI/Card';
44
import { Button } from '../UI/Button';
5-
import { X, Bookmark, GitCompare } from 'lucide-react';
5+
import { X, Bookmark, GitCompare, Shield, Zap } from 'lucide-react';
66
import { ItemSlot, MountSlot, UserProfile } from '../../types/Profile';
77
import { useState, useMemo } from 'react';
88
import { ItemSelectorModal } from './ItemSelectorModal';
@@ -13,6 +13,7 @@ import { getItemImage } from '../../utils/itemAssets';
1313
import { useGameData } from '../../hooks/useGameData';
1414
import { AGES } from '../../utils/constants';
1515
import { useTreeModifiers } from '../../hooks/useCalculatedStats';
16+
import { useSetBonuses } from '../../hooks/useSetBonuses';
1617
import { formatSecondaryStat } from '../../utils/statNames';
1718
import { SpriteSheetIcon } from '../UI/SpriteSheetIcon';
1819

@@ -83,6 +84,13 @@ const SLOT_TYPE_ID_MAP: Record<string, number> = {
8384
'Belt': 7
8485
};
8586

87+
// Map Set IDs to Icon filenames (same as Skins.tsx)
88+
const SET_ICONS: Record<string, string> = {
89+
'SantaSet': 'SteppingStoneCharIcon0.png',
90+
'SnowmanSet': 'SteppingStoneCharIcon1.png',
91+
'SkiSet': 'SteppingStoneCharIcon2.png'
92+
};
93+
8694
interface EquipmentPanelProps {
8795
variant?: 'default' | 'original' | 'test';
8896
title?: string;
@@ -153,6 +161,7 @@ export function EquipmentPanel({ variant = 'default', title, showCompareButton =
153161
const { data: itemBalancingConfig } = useGameData<any>('ItemBalancingConfig.json');
154162
const { data: weaponLibrary } = useGameData<any>('WeaponLibrary.json');
155163
const { data: secondaryStatLibrary } = useGameData<any>('SecondaryStatLibrary.json');
164+
const { data: skinsLibrary } = useGameData<any>('SkinsLibrary.json');
156165

157166
// Helper to calculate item perfection (avg of secondary stats vs max)
158167
const getPerfection = (item: ItemSlot): number | null => {
@@ -240,12 +249,26 @@ export function EquipmentPanel({ variant = 'default', title, showCompareButton =
240249
if (statType === 'Health') health += value;
241250
}
242251

243-
// For melee weapons: apply melee base multiplier (1.6x) to match in-game display
244252
if (slotKey === 'Weapon' && isMelee && damage > 0) {
245253
damage = damage * meleeBaseMulti;
246254
}
247255

248-
return { damage, health, bonus, isMelee };
256+
// Apply Skin Multipliers
257+
let skinBonuses = { damage: 0, health: 0 };
258+
if (item.skin && item.skin.stats) {
259+
const skinDamage = item.skin.stats['Damage'] || 0;
260+
const skinHealth = item.skin.stats['Health'] || 0;
261+
262+
if (skinDamage) {
263+
damage *= (1 + skinDamage);
264+
}
265+
if (skinHealth) {
266+
health *= (1 + skinHealth);
267+
}
268+
skinBonuses = { damage: skinDamage, health: skinHealth };
269+
}
270+
271+
return { damage, health, bonus, skinBonuses, isMelee };
249272
};
250273

251274
const getEquippedImage = (slotKey: string, item: ItemSlot | null): string | null => {
@@ -333,9 +356,14 @@ export function EquipmentPanel({ variant = 'default', title, showCompareButton =
333356
{panelTitle}
334357
</h2>
335358
{showCompareButton && !isComparing && variant === 'default' && (
336-
<Button variant="secondary" size="sm" onClick={enterCompareMode}>
359+
<Button
360+
variant="primary"
361+
size="sm"
362+
onClick={enterCompareMode}
363+
className="shadow-lg shadow-accent-primary/20 animate-pulse-subtle"
364+
>
337365
<GitCompare className="w-4 h-4 mr-2" />
338-
Compare
366+
Compare Build
339367
</Button>
340368
)}
341369
</div>
@@ -409,6 +437,79 @@ export function EquipmentPanel({ variant = 'default', title, showCompareButton =
409437
)
410438
)}
411439
</div>
440+
{equipped.skin ? (
441+
<div className="absolute -bottom-2 -right-2 z-20 w-8 h-8 rounded-md bg-bg-secondary border border-accent-primary shadow-sm overflow-hidden" title={`Skin ID: ${equipped.skin.idx}`}>
442+
{(() => {
443+
// Calculate sprite position for skin icon
444+
// This logic mimics ItemSelectorModal and Skins.tsx
445+
const skinId = equipped.skin.idx;
446+
// We need a helper or context to get global sorted index for precise sprite pos,
447+
// but for now, let's try to map it if possible or just use the generic icon.
448+
// Actually, without the full sorted list context, accurate sprite mapping is hard locally.
449+
// Let's use a simplified approach or generic skin icon if specific mapping is complex.
450+
// Ideally we should export getVisualOrder/sortedGlobalSkins logic.
451+
452+
// For now, let's display a styled indicator that looks better than "S"
453+
// Or better: Show the Skin Icon if we can calculate the background position.
454+
// Since we don't have the SkinsLibrary here easily without extra fetch,
455+
// let's stick to a robust visual indicator or fetch it.
456+
// Wait, we can use the same logic if we pass the mapping or use a helper.
457+
458+
return (
459+
<div className="w-full h-full flex items-center justify-center bg-accent-primary/20">
460+
<div
461+
className="w-full h-full opacity-80"
462+
style={{
463+
backgroundImage: 'url(./Texture2D/SkinsUiIcons.png)',
464+
backgroundSize: '400% 400%',
465+
backgroundPosition: (() => {
466+
if (!skinsLibrary) return 'center';
467+
468+
const allSkins = Object.values(skinsLibrary);
469+
// Sort using same getVisualOrder logic as Skins.tsx
470+
const getVisualOrder = (idx: number) => {
471+
if (idx === 0) return 0;
472+
if (idx === 2) return 1;
473+
if (idx === 1) return 2;
474+
return 10 + idx;
475+
};
476+
const sortedSkins = [...allSkins].sort((a: any, b: any) => {
477+
const orderA = getVisualOrder(a.SkinId.Idx);
478+
const orderB = getVisualOrder(b.SkinId.Idx);
479+
if (orderA !== orderB) return orderA - orderB;
480+
481+
const isHelmetA = a.SkinId.Type === 'Helmet';
482+
const isHelmetB = b.SkinId.Type === 'Helmet';
483+
if (isHelmetA && !isHelmetB) return -1;
484+
if (!isHelmetA && isHelmetB) return 1;
485+
return 0;
486+
});
487+
488+
const globalIndex = sortedSkins.findIndex((s: any) =>
489+
s.SkinId.Idx === equipped.skin!.idx &&
490+
s.SkinId.Type === (equipped.skin!.type || SLOT_TO_JSON_TYPE[slot.key] || slot.key)
491+
);
492+
493+
if (globalIndex === -1) return 'center';
494+
495+
const SPRITE_COLS = 4;
496+
const SPRITE_ROWS = 4;
497+
const col = globalIndex % SPRITE_COLS;
498+
const row = Math.floor(globalIndex / SPRITE_COLS);
499+
500+
const bgX = (col * 100) / (SPRITE_COLS - 1);
501+
const bgY = (row * 100) / (SPRITE_ROWS - 1);
502+
503+
return `${Number.isNaN(bgX) ? 0 : bgX}% ${Number.isNaN(bgY) ? 0 : bgY}%`;
504+
})(),
505+
imageRendering: 'pixelated'
506+
}}
507+
/>
508+
</div>
509+
);
510+
})()}
511+
</div>
512+
) : null}
412513
</div>
413514

414515
{/* Name - Below Icon */}
@@ -433,13 +534,19 @@ export function EquipmentPanel({ variant = 'default', title, showCompareButton =
433534
{stats.damage > 0 && (
434535
<div className="text-red-400 break-words flex flex-col items-center">
435536
<span>⚔️{Math.round(stats.damage).toLocaleString()}</span>
436-
{stats.bonus > 0 && <span className="text-green-400 text-[10px]">(+{Math.round(stats.bonus * 100)}%)</span>}
537+
<div className="flex gap-1 flex-wrap justify-center font-bold">
538+
{stats.bonus > 0 && <span className="text-green-400 text-[10px]">(+{Math.round(stats.bonus * 100)}%)</span>}
539+
{stats.skinBonuses?.damage > 0 && <span className="text-accent-primary text-[10px]">(+{Math.round(stats.skinBonuses.damage * 100)}% S)</span>}
540+
</div>
437541
</div>
438542
)}
439543
{stats.health > 0 && (
440544
<div className="text-green-400 break-words flex flex-col items-center mt-0.5">
441545
<span>{Math.round(stats.health).toLocaleString()}</span>
442-
{stats.bonus > 0 && <span className="text-green-400 text-[10px]">(+{Math.round(stats.bonus * 100)}%)</span>}
546+
<div className="flex gap-1 flex-wrap justify-center font-bold">
547+
{stats.bonus > 0 && <span className="text-green-400 text-[10px]">(+{Math.round(stats.bonus * 100)}%)</span>}
548+
{stats.skinBonuses?.health > 0 && <span className="text-accent-primary text-[10px]">(+{Math.round(stats.skinBonuses.health * 100)}% S)</span>}
549+
</div>
443550
</div>
444551
)}
445552
</div>

0 commit comments

Comments
 (0)