Update crafting

This commit is contained in:
Bill 2022-09-21 00:23:13 -04:00
parent 5168eed641
commit 2fb75b3e17
5 changed files with 128 additions and 93 deletions

View File

@ -0,0 +1,6 @@
export interface ICraftingIngredient
{
name: string;
iconUrl: string;
count: number;
}

View File

@ -0,0 +1,6 @@
export interface ICraftingRecipe
{
name: string;
localizedName: string;
iconUrl: string;
}

View File

@ -11,6 +11,8 @@ export * from './DimmerFurnitureWidgetPresetItem';
export * from './DoChatsOverlap'; export * from './DoChatsOverlap';
export * from './FurnitureDimmerUtilities'; export * from './FurnitureDimmerUtilities';
export * from './IAvatarInfo'; export * from './IAvatarInfo';
export * from './ICraftingIngredient';
export * from './ICraftingRecipe';
export * from './IPhotoData'; export * from './IPhotoData';
export * from './MannequinUtilities'; export * from './MannequinUtilities';
export * from './PetSupplementEnum'; export * from './PetSupplementEnum';

View File

@ -1,21 +1,15 @@
import { CraftingRecipeIngredientParser, RoomObjectCategory } from '@nitrots/nitro-renderer'; import { RoomObjectCategory } from '@nitrots/nitro-renderer';
import { FC, useEffect, useMemo, useState } from 'react'; import { FC, ReactElement, useEffect, useMemo, useState } from 'react';
import { GetRoomEngine, IsOwnerOfFurniture, LocalizeText } from '../../../../api'; import { GetRoomEngine, IsOwnerOfFurniture, LocalizeText } from '../../../../api';
import { AutoGrid, Button, Column, Flex, LayoutGridItem, LayoutLoadingSpinnerView, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../common'; import { AutoGrid, Button, Column, Flex, LayoutGridItem, LayoutLoadingSpinnerView, NitroCardContentView, NitroCardHeaderView, NitroCardView } from '../../../../common';
import { useFurnitureCraftingWidget, useRoom } from '../../../../hooks'; import { useFurnitureCraftingWidget, useRoom } from '../../../../hooks';
export const FurnitureCraftingView: FC<{}> = props => export const FurnitureCraftingView: FC<{}> = props =>
{ {
const { objectId = -1, recipes = [], ingredients = [], selectedRecipe = null, selectedRecipeIngredients = null, isCrafting = false, craft = null, selectRecipe = null, onClose = null } = useFurnitureCraftingWidget(); const { objectId = -1, recipes = [], ingredients = [], selectedRecipe = null, requiredIngredients = null, isCrafting = false, craft = null, selectRecipe = null, onClose = null } = useFurnitureCraftingWidget();
const { roomSession = null } = useRoom(); const { roomSession = null } = useRoom();
const [ waitingToConfirm, setWaitingToConfirm ] = useState(false); const [ waitingToConfirm, setWaitingToConfirm ] = useState(false);
useEffect(() =>
{
setWaitingToConfirm(false);
}, [ selectedRecipe ]);
const isOwner = useMemo(() => const isOwner = useMemo(() =>
{ {
const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, objectId, RoomObjectCategory.FLOOR); const roomObject = GetRoomEngine().getRoomObject(roomSession.roomId, objectId, RoomObjectCategory.FLOOR);
@ -24,21 +18,24 @@ export const FurnitureCraftingView: FC<{}> = props =>
const canCraft = useMemo(() => const canCraft = useMemo(() =>
{ {
for (const ingredient of selectedRecipeIngredients) if(!requiredIngredients || !requiredIngredients.length) return false;
for (const ingredient of requiredIngredients)
{ {
const ingredientData = ingredients.find((i) => i.name === ingredient.itemName); const ingredientData = ingredients.find(data => (data.name === ingredient.itemName));
if (ingredientData.count < ingredient.count) return false; if (ingredientData.count < ingredient.count) return false;
} }
return true; return true;
}, [ ingredients, selectedRecipeIngredients ]); }, [ ingredients, requiredIngredients ]);
const tryCraft = () => const tryCraft = () =>
{ {
if (!waitingToConfirm) if (!waitingToConfirm)
{ {
setWaitingToConfirm(true); setWaitingToConfirm(true);
return; return;
} }
@ -46,19 +43,10 @@ export const FurnitureCraftingView: FC<{}> = props =>
setWaitingToConfirm(false); setWaitingToConfirm(false);
}; };
const renderSelectedRecipeIngredient = (ingredient: CraftingRecipeIngredientParser) => useEffect(() =>
{ {
const ingredientData = ingredients.find((i) => i.name === ingredient.itemName); setWaitingToConfirm(false);
}, [ selectedRecipe ]);
const elements = [];
for (let i = 0; i < ingredient.count; i++)
{
elements.push(<LayoutGridItem key={ i } itemImage={ ingredientData.iconUrl } className={ (ingredientData.count - (i) <= 0 ? 'opacity-0-5 ' : '') + 'cursor-default' } />);
}
return elements;
};
if(objectId === -1) return null; if(objectId === -1) return null;
@ -87,7 +75,19 @@ export const FurnitureCraftingView: FC<{}> = props =>
<Column overflow="hidden" fullHeight> <Column overflow="hidden" fullHeight>
<div className="bg-muted rounded py-1 text-center">{ LocalizeText('crafting.current_recipe') }</div> <div className="bg-muted rounded py-1 text-center">{ LocalizeText('crafting.current_recipe') }</div>
<AutoGrid columnCount={ 5 }> <AutoGrid columnCount={ 5 }>
{ (selectedRecipeIngredients.length > 0) && selectedRecipeIngredients.map((item) => renderSelectedRecipeIngredient(item)) } { !!requiredIngredients && (requiredIngredients.length > 0) && requiredIngredients.map(ingredient =>
{
const ingredientData = ingredients.find((i) => i.name === ingredient.itemName);
const elements: ReactElement[] = [];
for (let i = 0; i < ingredient.count; i++)
{
elements.push(<LayoutGridItem key={ i } itemImage={ ingredientData.iconUrl } className={ (ingredientData.count - (i) <= 0 ? 'opacity-0-5 ' : '') + 'cursor-default' } />);
}
return elements;
}) }
</AutoGrid> </AutoGrid>
</Column> </Column>
<Flex gap={ 2 } column fullHeight> <Flex gap={ 2 } column fullHeight>

View File

@ -1,30 +1,30 @@
import { CraftableProductsEvent, CraftComposer, CraftingRecipeEvent, CraftingRecipeIngredientParser, CraftingRecipesAvailableEvent, CraftingResultEvent, GetCraftableProductsComposer, GetCraftingRecipeComposer, RoomEngineTriggerWidgetEvent, RoomWidgetEnum } from '@nitrots/nitro-renderer'; import { CraftableProductsEvent, CraftComposer, CraftingRecipeEvent, CraftingRecipeIngredientParser, CraftingRecipesAvailableEvent, CraftingResultEvent, GetCraftableProductsComposer, GetCraftingRecipeComposer, RoomEngineTriggerWidgetEvent, RoomWidgetEnum } from '@nitrots/nitro-renderer';
import { useCallback, useState } from 'react'; import { useEffect, useState } from 'react';
import { GetRoomEngine, LocalizeText, SendMessageComposer } from '../../../../api'; import { GetRoomEngine, ICraftingIngredient, ICraftingRecipe, LocalizeText, SendMessageComposer } from '../../../../api';
import { useMessageEvent, useRoomEngineEvent } from '../../../events'; import { useMessageEvent, useRoomEngineEvent } from '../../../events';
import { useInventoryFurni } from '../../../inventory'; import { useInventoryFurni } from '../../../inventory';
import { useNotification } from './../../../notification/useNotification'; import { useNotification } from './../../../notification';
const useFurnitureCraftingWidgetState = () => const useFurnitureCraftingWidgetState = () =>
{ {
const { getItemsByType } = useInventoryFurni();
const { simpleAlert } = useNotification();
const [ objectId, setObjectId ] = useState(-1); const [ objectId, setObjectId ] = useState(-1);
const [ recipes, setRecipes ] = useState<{ name: string, localizedName: string, iconUrl: string }[]>([]); const [ recipes, setRecipes ] = useState<ICraftingRecipe[]>([]);
const [ ingredients, setIngredients ] = useState<{ name: string, iconUrl: string, count: number }[]>([]); const [ selectedRecipe, setSelectedRecipe ] = useState<ICraftingRecipe>(null);
const [ selectedRecipe, setSelectedRecipe ] = useState<{ name: string, localizedName: string, iconUrl: string }>(null); const [ ingredients, setIngredients ] = useState<ICraftingIngredient[]>([]);
const [ selectedRecipeIngredients, setSelectedRecipeIngredients ] = useState<CraftingRecipeIngredientParser[]>([]); const [ ingredientNames, setIngredientNames ] = useState<string[]>(null);
const [ cacheRecipeIngredients, setCacheRecipeIngredients ] = useState<Map<string, CraftingRecipeIngredientParser[]>>(new Map()); const [ cachedIngredients, setCachedIngredients ] = useState<Map<string, CraftingRecipeIngredientParser[]>>(new Map());
const [ isCrafting, setIsCrafting ] = useState(false); const [ isCrafting, setIsCrafting ] = useState(false);
const { groupItems = [], getItemsByType = null, activate = null, deactivate = null } = useInventoryFurni();
const { simpleAlert = null } = useNotification();
const requiredIngredients = ((selectedRecipe && cachedIngredients.get(selectedRecipe.name) || null));
const resetData = () => const resetData = () =>
{ {
setRecipes([]); setRecipes([]);
setIngredients([]);
setSelectedRecipe(null); setSelectedRecipe(null);
setSelectedRecipeIngredients([]); setIngredients([]);
setCacheRecipeIngredients(new Map()); setCachedIngredients(new Map());
}; };
const onClose = () => const onClose = () =>
@ -36,26 +36,23 @@ const useFurnitureCraftingWidgetState = () =>
const craft = () => const craft = () =>
{ {
setIsCrafting(true); setIsCrafting(true);
SendMessageComposer(new CraftComposer(objectId, selectedRecipe.name)); SendMessageComposer(new CraftComposer(objectId, selectedRecipe.name));
}; };
const selectRecipe = useCallback((recipe: { name: string, localizedName: string, iconUrl: string }) => const selectRecipe = (recipe: ICraftingRecipe) =>
{ {
setSelectedRecipeIngredients([]);
setSelectedRecipe(recipe); setSelectedRecipe(recipe);
const cache = cacheRecipeIngredients.get(recipe.name); const cache = cachedIngredients.get(recipe.name);
if (cache) if(!cache) SendMessageComposer(new GetCraftingRecipeComposer(recipe.name));
setSelectedRecipeIngredients(cache); }
else
SendMessageComposer(new GetCraftingRecipeComposer(recipe.name));
}, [ cacheRecipeIngredients ]);
useRoomEngineEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.OPEN_WIDGET, event => useRoomEngineEvent<RoomEngineTriggerWidgetEvent>(RoomEngineTriggerWidgetEvent.OPEN_WIDGET, event =>
{ {
if (event.widget !== RoomWidgetEnum.CRAFTING) return; if (event.widget !== RoomWidgetEnum.CRAFTING) return;
console.log(event);
setObjectId(event.objectId); setObjectId(event.objectId);
resetData(); resetData();
SendMessageComposer(new GetCraftableProductsComposer(event.objectId)); SendMessageComposer(new GetCraftableProductsComposer(event.objectId));
@ -68,78 +65,102 @@ const useFurnitureCraftingWidgetState = () =>
if (!parser.isActive()) if (!parser.isActive())
{ {
setObjectId(-1); setObjectId(-1);
return; return;
} }
const recipesToSet = []; setRecipes(prevValue =>
for(const recipe of parser.recipes)
{ {
//@ts-ignore const newValue: ICraftingRecipe[] = [];
const itemId = GetRoomEngine().roomContentLoader._activeObjectTypeIds.get(recipe.itemName);
const iconUrl = GetRoomEngine().getFurnitureFloorIconUrl(itemId);
recipesToSet.push({
name: recipe.itemName,
localizedName: LocalizeText('roomItem.name.' + itemId),
iconUrl
});
}
setRecipes(recipesToSet); for(const recipe of parser.recipes)
{
//@ts-ignore
const itemId = GetRoomEngine().roomContentLoader._activeObjectTypeIds.get(recipe.itemName);
const iconUrl = GetRoomEngine().getFurnitureFloorIconUrl(itemId);
const ingredientsToSet = []; newValue.push({
name: recipe.itemName,
localizedName: LocalizeText('roomItem.name.' + itemId),
iconUrl
});
}
for(const ingredient of parser.ingredients) return newValue;
{ });
//@ts-ignore
const itemId = GetRoomEngine().roomContentLoader._activeObjectTypeIds.get(ingredient);
const iconUrl = GetRoomEngine().getFurnitureFloorIconUrl(itemId);
const inventoryItems = getItemsByType(itemId); setIngredientNames(parser.ingredients);
let amountAvailable = 0;
for (const inventoryItem of inventoryItems) amountAvailable += inventoryItem.items.length;
ingredientsToSet.push({
name: ingredient,
iconUrl,
count: amountAvailable
});
}
setIngredients(ingredientsToSet);
}); });
useMessageEvent<CraftingRecipeEvent>(CraftingRecipeEvent, event => useMessageEvent<CraftingRecipeEvent>(CraftingRecipeEvent, event =>
{ {
const parser = event.getParser(); const parser = event.getParser();
setSelectedRecipeIngredients(parser.ingredients);
const newCache = new Map(cacheRecipeIngredients); setCachedIngredients(prevValue =>
newCache.set(selectedRecipe.name, parser.ingredients); {
setCacheRecipeIngredients(newCache); const newValue = new Map(prevValue);
newValue.set(selectedRecipe.name, parser.ingredients);
return newValue;
});
}); });
useMessageEvent<CraftingResultEvent>(CraftingResultEvent, event => useMessageEvent<CraftingResultEvent>(CraftingResultEvent, event =>
{ {
setSelectedRecipe(null); setSelectedRecipe(null);
setSelectedRecipeIngredients([]); setIsCrafting(false);
const parser = event.getParser(); const parser = event.getParser();
if (parser.result) if(parser.result) simpleAlert(LocalizeText('crafting.info.result.ok'));
{
simpleAlert(LocalizeText('crafting.info.result.ok'));
}
setIsCrafting(false);
}); });
useMessageEvent<CraftingRecipesAvailableEvent>(CraftingRecipesAvailableEvent, event => useMessageEvent<CraftingRecipesAvailableEvent>(CraftingRecipesAvailableEvent, event =>
{ {
}); });
return { objectId, recipes, ingredients, selectedRecipe, selectedRecipeIngredients, isCrafting, selectRecipe, craft, onClose }; useEffect(() =>
{
if(!groupItems || !groupItems.length || !ingredientNames || !ingredientNames.length) return;
setIngredients(prevValue =>
{
const newValue: ICraftingIngredient[] = [];
for(const name of ingredientNames)
{
//@ts-ignore
const itemId = GetRoomEngine().roomContentLoader._activeObjectTypeIds.get(name);
const iconUrl = GetRoomEngine().getFurnitureFloorIconUrl(itemId);
const inventoryItems = getItemsByType(itemId);
let amountAvailable = 0;
for (const inventoryItem of inventoryItems) amountAvailable += inventoryItem.items.length;
newValue.push({
name: name,
iconUrl,
count: amountAvailable
});
}
return newValue;
});
}, [ groupItems, ingredientNames, getItemsByType ]);
useEffect(() =>
{
if((objectId === -1)) return;
const id = activate();
return () => deactivate(id);
}, [ objectId, activate, deactivate ]);
return { objectId, recipes, ingredients, selectedRecipe, requiredIngredients, isCrafting, selectRecipe, craft, onClose };
} }
export const useFurnitureCraftingWidget = useFurnitureCraftingWidgetState; export const useFurnitureCraftingWidget = useFurnitureCraftingWidgetState;