5180 fichiers 38.5 Mo 0 téléchargements 236 vues mis à jour il y a 5 heures ui
useNftWardrobe.ts 6 Ko Télécharger le fichier
import {
    AvatarEditorFigureCategory,
    GetSelectedNftWardrobeOutfitComposer,
    GetUserNftWardrobeComposer,
    SaveUserNftWardrobeComposer,
    SelectedNftWardrobeOutfitEvent,
    UserNftWardrobeEvent
} from '@octane/renderer';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { AVATAR_EDITOR_EFFECTS_TAB, SendMessageComposer } from '../../api';
import { useMessageEvent } from '../events';
import {
    findNftOutfitByToken,
    NftOutfit,
    NftWornOutfit,
    normalizeNftGender,
    resolveNftTabSwitch,
    toNftOutfits
} from './nftWardrobe.helpers';
import { useAvatarEditor } from './useAvatarEditor';

interface LookSnapshot {
    figure: string;
    gender: string;
}

/**
 * AIR avatar collectibles in the avatar editor (avatar/nft/NftAvatarsModel and the NFT parts of
 * HabboAvatarEditor). While `enabled` (editor open, `collectibles.enabled`, no clothing booth) it asks the
 * user's collectible looks (3206 -> 2236) and the worn one (3810 -> 350). Picking a look previews it; saving
 * with one picked wears it (133, then 3810 again) instead of saving a figure. While one is worn, the NFT tab
 * shows it and the clothing tabs the look it replaced.
 */
export const useNftWardrobe = (enabled: boolean, activeModelKey: string) => {
    const { loadOutfitFigure = null, getFigureString = '', gender = 'M' } = useAvatarEditor();
    const [outfits, setOutfits] = useState<NftOutfit[]>([]);
    const [worn, setWorn] = useState<NftWornOutfit>(null);
    const [pendingOutfit, setPendingOutfit] = useState<NftOutfit>(null);
    const previousLookRef = useRef<LookSnapshot>(null);
    const activeModelKeyRef = useRef(activeModelKey);
    const enabledRef = useRef(enabled);

    useEffect(() => {
        activeModelKeyRef.current = activeModelKey;
        enabledRef.current = enabled;
    }, [activeModelKey, enabled]);

    useMessageEvent<UserNftWardrobeEvent>(UserNftWardrobeEvent, (event) => setOutfits(toNftOutfits(event.getParser().nftAvatars)));

    useMessageEvent<SelectedNftWardrobeOutfitEvent>(SelectedNftWardrobeOutfitEvent, (event) => {
        const parser = event.getParser();
        const next: NftWornOutfit = {
            tokenId: parser.tokenId,
            fallbackFigure: parser.fallbackFigure,
            fallbackGender: normalizeNftGender(parser.fallbackGender)
        };

        setWorn(next);

        // AIR onUserNftWardrobeMessage: away from the NFT tab the editor edits the look from before the collectible.
        if (!enabledRef.current || activeModelKeyRef.current === AvatarEditorFigureCategory.NFT || !next.fallbackFigure) return;

        loadOutfitFigure?.(next.fallbackFigure, next.fallbackGender);
    });

    useEffect(() => {
        setPendingOutfit(null);
        previousLookRef.current = null;

        if (!enabled) return;

        setWorn(null);
        SendMessageComposer(new GetSelectedNftWardrobeOutfitComposer());
        SendMessageComposer(new GetUserNftWardrobeComposer());
    }, [enabled]);

    const wornOutfit = useMemo(() => findNftOutfitByToken(outfits, worn?.tokenId), [outfits, worn]);

    const selectOutfit = useCallback(
        (outfit: NftOutfit) => {
            if (!enabled || !outfit?.figure) return;

            if (!pendingOutfit) previousLookRef.current = { figure: getFigureString, gender };

            setPendingOutfit(outfit);
            loadOutfitFigure?.(outfit.figure, outfit.gender);
        },
        [enabled, gender, getFigureString, loadOutfitFigure, pendingOutfit]
    );

    /** AIR AvatarEditorView.windowEventProc: call before switching the main tab. */
    const onTabChange = useCallback(
        (nextModelKey: string) => {
            if (!enabled || nextModelKey === activeModelKey) return;

            const action = resolveNftTabSwitch({
                toNft: nextModelKey === AvatarEditorFigureCategory.NFT,
                toEffects: nextModelKey === AVATAR_EDITOR_EFFECTS_TAB,
                toHotLooks: nextModelKey === AvatarEditorFigureCategory.HOTLOOKS,
                hasWorn: !!worn,
                hasPending: !!pendingOutfit
            });

            switch (action) {
                case 'loadWorn': {
                    const outfit = pendingOutfit ?? wornOutfit;

                    if (outfit) loadOutfitFigure?.(outfit.figure, outfit.gender);
                    return;
                }
                case 'loadFallback':
                    setPendingOutfit(null);
                    previousLookRef.current = null;

                    if (worn?.fallbackFigure) loadOutfitFigure?.(worn.fallbackFigure, worn.fallbackGender);
                    return;
                case 'restorePrevious': {
                    const previous = previousLookRef.current;

                    setPendingOutfit(null);
                    previousLookRef.current = null;

                    if (previous) loadOutfitFigure?.(previous.figure, previous.gender);
                    return;
                }
            }
        },
        [activeModelKey, enabled, loadOutfitFigure, pendingOutfit, worn, wornOutfit]
    );

    /**
     * AIR HabboAvatarEditor.saveCurrentSelection: with a collectible picked, wears it and asks the worn one again;
     * true when it did, and the caller must not save the figure. A normal save takes the collectible off.
     */
    const save = useCallback((): boolean => {
        if (!enabled) return false;

        if (pendingOutfit) {
            SendMessageComposer(new SaveUserNftWardrobeComposer(pendingOutfit.id));
            SendMessageComposer(new GetSelectedNftWardrobeOutfitComposer());
            setPendingOutfit(null);
            previousLookRef.current = null;

            return true;
        }

        setWorn(null);

        return false;
    }, [enabled, pendingOutfit]);

    // The NftWardrobeParamView label: the picked look, else the worn one while the NFT tab is open.
    const selectedOutfit = pendingOutfit ?? (activeModelKey === AvatarEditorFigureCategory.NFT ? wornOutfit : null);

    return { enabled, outfits, wornOutfit, pendingOutfit, selectedOutfit, selectOutfit, onTabChange, save };
};
15Sujets
1Dépôts
20Membres
2En ligne
Profile
Inscrit
Dernière visite
Messages
0
Votes positifs
0
Votes négatifs
0

Nous utilisons des cookies pour assurer le bon fonctionnement du site et améliorer votre expérience. en savoir plus

Autoriser