mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-20 07:26:19 +00:00
Add NFT avatar tab and wired extras UI
This commit is contained in:
@@ -15,6 +15,7 @@ const useAvatarEditorState = () =>
|
||||
const [ maxPaletteCount, setMaxPaletteCount ] = useState<number>(1);
|
||||
const [ figureSetIds, setFigureSetIds ] = useState<number[]>([]);
|
||||
const [ boundFurnitureNames, setBoundFurnitureNames ] = useState<string[]>([]);
|
||||
const [ figureSetNames, setFigureSetNames ] = useState<Record<number, string>>({});
|
||||
const [ savedFigures, setSavedFigures ] = useState<[ IAvatarFigureContainer, string ][]>(null);
|
||||
const { selectedColors, gender, setGender, loadAvatarData, selectPart, selectColor, getFigureString, getFigureStringWithFace, selectedParts } = useFigureData();
|
||||
|
||||
@@ -65,6 +66,8 @@ const useAvatarEditorState = () =>
|
||||
|
||||
if(GetClubMemberLevel() < partItem.partSet.clubLevel) return;
|
||||
|
||||
if(partItem.isSellableNotOwned) return;
|
||||
|
||||
setMaxPaletteCount(partItem.maxPaletteCount || 1);
|
||||
|
||||
selectPart(setType, partId);
|
||||
@@ -194,12 +197,27 @@ const useAvatarEditorState = () =>
|
||||
loadAvatarData(figureContainer.getFigureString(), gender);
|
||||
}, [ figureSetIds, gender, loadAvatarData, selectedColors, selectedParts ]);
|
||||
|
||||
const nftFigureSetIds = useMemo(() =>
|
||||
{
|
||||
const nftSetIds = new Set<number>();
|
||||
|
||||
for(const [ setId, furnitureName ] of Object.entries(figureSetNames))
|
||||
{
|
||||
if(!furnitureName?.toLowerCase().includes('nft')) continue;
|
||||
|
||||
nftSetIds.add(Number(setId));
|
||||
}
|
||||
|
||||
return nftSetIds;
|
||||
}, [ figureSetNames ]);
|
||||
|
||||
useMessageEvent<FigureSetIdsMessageEvent>(FigureSetIdsMessageEvent, event =>
|
||||
{
|
||||
const parser = event.getParser();
|
||||
|
||||
setFigureSetIds(parser.figureSetIds);
|
||||
setBoundFurnitureNames(parser.boundsFurnitureNames);
|
||||
setFigureSetNames(parser.figureSetNameMap);
|
||||
});
|
||||
|
||||
useMessageEvent<UserWardrobePageEvent>(UserWardrobePageEvent, event =>
|
||||
@@ -236,8 +254,10 @@ const useAvatarEditorState = () =>
|
||||
if(!isVisible) return;
|
||||
|
||||
const newAvatarModels: { [index: string]: IAvatarEditorCategory[] } = {};
|
||||
const buildModeDefault = 'default';
|
||||
const buildModeNft = 'nft';
|
||||
|
||||
const buildCategory = (setType: string) =>
|
||||
const buildCategory = (setType: string, buildMode: string = buildModeDefault) =>
|
||||
{
|
||||
const partItems: IAvatarEditorCategoryPartItem[] = [];
|
||||
const colorItems: IPartColor[][] = [];
|
||||
@@ -271,13 +291,20 @@ const useAvatarEditorState = () =>
|
||||
|
||||
if(!partSet || !partSet.isSelectable || ((partSet.gender !== gender) && (partSet.gender !== AvatarFigurePartType.UNISEX))) continue;
|
||||
|
||||
if(partSet.isSellable && figureSetIds.indexOf(partSet.id) === -1) continue;
|
||||
const isNftPartSet = nftFigureSetIds.has(partSet.id);
|
||||
|
||||
if((buildMode === buildModeDefault) && isNftPartSet) continue;
|
||||
if((buildMode === buildModeNft) && !isNftPartSet) continue;
|
||||
|
||||
const isSellableNotOwned = partSet.isSellable && figureSetIds.indexOf(partSet.id) === -1;
|
||||
|
||||
if(isSellableNotOwned && (buildMode !== buildModeNft) && setType !== AvatarFigurePartType.PET) continue;
|
||||
|
||||
let maxPaletteCount = 0;
|
||||
|
||||
for(const part of partSet.parts) maxPaletteCount = Math.max(maxPaletteCount, part.colorLayerIndex);
|
||||
|
||||
partItems.push({ id: partSet.id, partSet, usesColor, maxPaletteCount });
|
||||
partItems.push({ id: partSet.id, partSet, usesColor, maxPaletteCount, isSellableNotOwned });
|
||||
}
|
||||
|
||||
partItems.sort(AvatarEditorPartSorter(false));
|
||||
@@ -287,16 +314,31 @@ const useAvatarEditorState = () =>
|
||||
return { setType, partItems, colorItems };
|
||||
};
|
||||
|
||||
newAvatarModels[AvatarEditorFigureCategory.GENERIC] = [ AvatarFigurePartType.HEAD ].map(setType => buildCategory(setType));
|
||||
newAvatarModels[AvatarEditorFigureCategory.HEAD] = [ AvatarFigurePartType.HAIR, AvatarFigurePartType.HEAD_ACCESSORY, AvatarFigurePartType.HEAD_ACCESSORY_EXTRA, AvatarFigurePartType.EYE_ACCESSORY, AvatarFigurePartType.FACE_ACCESSORY ].map(setType => buildCategory(setType));
|
||||
newAvatarModels[AvatarEditorFigureCategory.TORSO] = [ AvatarFigurePartType.CHEST, AvatarFigurePartType.CHEST_PRINT, AvatarFigurePartType.COAT_CHEST, AvatarFigurePartType.CHEST_ACCESSORY ].map(setType => buildCategory(setType));
|
||||
newAvatarModels[AvatarEditorFigureCategory.LEGS] = [ AvatarFigurePartType.LEGS, AvatarFigurePartType.SHOES, AvatarFigurePartType.WAIST_ACCESSORY ].map(setType => buildCategory(setType));
|
||||
newAvatarModels[AvatarEditorFigureCategory.GENERIC] = [ AvatarFigurePartType.HEAD ].map(setType => buildCategory(setType, buildModeDefault));
|
||||
newAvatarModels[AvatarEditorFigureCategory.HEAD] = [ AvatarFigurePartType.HAIR, AvatarFigurePartType.HEAD_ACCESSORY, AvatarFigurePartType.HEAD_ACCESSORY_EXTRA, AvatarFigurePartType.EYE_ACCESSORY, AvatarFigurePartType.FACE_ACCESSORY ].map(setType => buildCategory(setType, buildModeDefault));
|
||||
newAvatarModels[AvatarEditorFigureCategory.TORSO] = [ AvatarFigurePartType.CHEST, AvatarFigurePartType.CHEST_PRINT, AvatarFigurePartType.COAT_CHEST, AvatarFigurePartType.CHEST_ACCESSORY ].map(setType => buildCategory(setType, buildModeDefault));
|
||||
newAvatarModels[AvatarEditorFigureCategory.LEGS] = [ AvatarFigurePartType.LEGS, AvatarFigurePartType.SHOES, AvatarFigurePartType.WAIST_ACCESSORY ].map(setType => buildCategory(setType, buildModeDefault));
|
||||
newAvatarModels[AvatarEditorFigureCategory.PETS] = [ AvatarFigurePartType.PET ].map(setType => buildCategory(setType)).filter(Boolean);
|
||||
newAvatarModels[AvatarEditorFigureCategory.NFT] = [
|
||||
AvatarFigurePartType.HEAD,
|
||||
AvatarFigurePartType.HAIR,
|
||||
AvatarFigurePartType.HEAD_ACCESSORY,
|
||||
AvatarFigurePartType.HEAD_ACCESSORY_EXTRA,
|
||||
AvatarFigurePartType.EYE_ACCESSORY,
|
||||
AvatarFigurePartType.FACE_ACCESSORY,
|
||||
AvatarFigurePartType.CHEST,
|
||||
AvatarFigurePartType.CHEST_PRINT,
|
||||
AvatarFigurePartType.COAT_CHEST,
|
||||
AvatarFigurePartType.CHEST_ACCESSORY,
|
||||
AvatarFigurePartType.LEGS,
|
||||
AvatarFigurePartType.SHOES,
|
||||
AvatarFigurePartType.WAIST_ACCESSORY
|
||||
].map(setType => buildCategory(setType, buildModeNft)).filter(Boolean);
|
||||
newAvatarModels[AvatarEditorFigureCategory.WARDROBE] = [];
|
||||
|
||||
setAvatarModels(newAvatarModels);
|
||||
setActiveModelKey(AvatarEditorFigureCategory.GENERIC);
|
||||
}, [ isVisible, gender, figureSetIds ]);
|
||||
}, [ isVisible, gender, figureSetIds, nftFigureSetIds ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
|
||||
@@ -75,14 +75,37 @@ const useWiredState = () =>
|
||||
return rawValue.toLowerCase();
|
||||
};
|
||||
|
||||
const getComparableInteractionNames = (furniData: any): string[] =>
|
||||
{
|
||||
if(!furniData) return [];
|
||||
|
||||
const values = [
|
||||
getInteractionTypeName(furniData),
|
||||
(typeof (furniData as any).className === 'string') ? (furniData as any).className.toLowerCase() : null,
|
||||
(typeof (furniData as any).fullName === 'string') ? (furniData as any).fullName.toLowerCase() : null,
|
||||
(typeof (furniData as any).name === 'string') ? (furniData as any).name.toLowerCase() : null
|
||||
];
|
||||
|
||||
return values.filter((value, index, array): value is string => !!value && (array.indexOf(value) === index));
|
||||
};
|
||||
|
||||
const matchesAllowedPattern = (value: string, pattern: string) =>
|
||||
{
|
||||
const normalizedPattern = pattern.toLowerCase();
|
||||
|
||||
if(normalizedPattern.endsWith('*')) return value.startsWith(normalizedPattern.slice(0, -1));
|
||||
|
||||
return (normalizedPattern === value);
|
||||
};
|
||||
|
||||
const isAllowedInteraction = (furniData: any): boolean =>
|
||||
{
|
||||
if(!allowedInteractionTypes || !allowedInteractionTypes.length) return true;
|
||||
|
||||
const interactionType = getInteractionTypeName(furniData);
|
||||
if(!interactionType) return true;
|
||||
const comparableNames = getComparableInteractionNames(furniData);
|
||||
if(!comparableNames.length) return true;
|
||||
|
||||
return allowedInteractionTypes.some(type => (type && type.toLowerCase() === interactionType));
|
||||
return comparableNames.some(value => allowedInteractionTypes.some(type => !!type && matchesAllowedPattern(value, type)));
|
||||
};
|
||||
|
||||
const handleDisallowedInteraction = () =>
|
||||
|
||||
Reference in New Issue
Block a user