(Feat) Add Craft Systeme
This commit is contained in:
@@ -0,0 +1,226 @@
|
||||
// Fill out your copyright notice in the Description page of Project Settings.
|
||||
|
||||
|
||||
#include "CraftingComponent.h"
|
||||
|
||||
#include "CraftingRecipeBook.h"
|
||||
#include "InventoryComponent.h"
|
||||
#include "ItemDataAsset.h"
|
||||
|
||||
UCraftingComponent::UCraftingComponent()
|
||||
{
|
||||
PrimaryComponentTick.bCanEverTick = false;
|
||||
|
||||
// Necessaire pour que InitializeComponent() soit appele.
|
||||
bWantsInitializeComponent = true;
|
||||
}
|
||||
|
||||
void UCraftingComponent::InitializeComponent()
|
||||
{
|
||||
Super::InitializeComponent();
|
||||
|
||||
// Les mains sont toujours disponibles. Les postes s'ajoutent par-dessus.
|
||||
AvailableStations.Add(ECraftingStation::Hands);
|
||||
|
||||
if (const AActor* Owner = GetOwner())
|
||||
{
|
||||
Inventory = Owner->FindComponentByClass<UInventoryComponent>();
|
||||
}
|
||||
|
||||
if (!Inventory)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("UCraftingComponent : aucun UInventoryComponent sur %s, aucune fabrication ne sera possible."),
|
||||
*GetNameSafe(GetOwner()));
|
||||
}
|
||||
|
||||
if (!RecipeBook)
|
||||
{
|
||||
UE_LOG(LogTemp, Warning, TEXT("UCraftingComponent : RecipeBook n'est pas assigne sur %s, la grille de craft restera vide."),
|
||||
*GetNameSafe(GetOwner()));
|
||||
}
|
||||
}
|
||||
|
||||
void UCraftingComponent::AddAvailableStation(ECraftingStation Station)
|
||||
{
|
||||
if (Station == ECraftingStation::Count)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool bAlreadyThere = false;
|
||||
AvailableStations.Add(Station, &bAlreadyThere);
|
||||
|
||||
// On ne diffuse que sur un vrai changement : deux volumes d'etabli qui se
|
||||
// chevauchent rafraichiraient l'interface deux fois pour rien.
|
||||
if (!bAlreadyThere)
|
||||
{
|
||||
OnAvailableStationsChanged.Broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
void UCraftingComponent::RemoveAvailableStation(ECraftingStation Station)
|
||||
{
|
||||
// Retirer les mains laisserait le joueur incapable de fabriquer quoi que ce
|
||||
// soit, sans qu'aucun message ne l'explique.
|
||||
if (Station == ECraftingStation::Hands)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (AvailableStations.Remove(Station) > 0)
|
||||
{
|
||||
OnAvailableStationsChanged.Broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
void UCraftingComponent::GatherRecipes(TArray<UCraftingRecipeDataAsset*>& OutRecipes, ECraftingStation StationFilter) const
|
||||
{
|
||||
OutRecipes.Reset();
|
||||
|
||||
if (!RecipeBook)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const TArray<TObjectPtr<UCraftingRecipeDataAsset>>& AllRecipes = RecipeBook->GetRecipes();
|
||||
OutRecipes.Reserve(AllRecipes.Num());
|
||||
|
||||
for (UCraftingRecipeDataAsset* Recipe : AllRecipes)
|
||||
{
|
||||
// Une entree vide dans le livre est signalee par sa validation d'asset.
|
||||
// Ici on se contente de la sauter.
|
||||
if (!Recipe)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!AvailableStations.Contains(Recipe->RequiredStation))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (StationFilter != ECraftingStation::Count && Recipe->RequiredStation != StationFilter)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
OutRecipes.Add(Recipe);
|
||||
}
|
||||
}
|
||||
|
||||
bool UCraftingComponent::CanCraft(const UCraftingRecipeDataAsset* Recipe, ECraftFailureReason& OutReason) const
|
||||
{
|
||||
OutReason = ECraftFailureReason::None;
|
||||
|
||||
if (!RecipeBook)
|
||||
{
|
||||
OutReason = ECraftFailureReason::NoRecipeBook;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Recipe || !Recipe->ResultItem)
|
||||
{
|
||||
OutReason = ECraftFailureReason::InvalidRecipe;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Inventory)
|
||||
{
|
||||
OutReason = ECraftFailureReason::InvalidRecipe;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AvailableStations.Contains(Recipe->RequiredStation))
|
||||
{
|
||||
OutReason = ECraftFailureReason::StationUnavailable;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const FCraftIngredient& Ingredient : Recipe->Ingredients)
|
||||
{
|
||||
// Un ingredient nul est refuse par la validation de l'asset. S'il passe
|
||||
// quand meme, on ne fabrique pas : mieux vaut un bouton grise qu'une
|
||||
// recette qui coute moins cher que ce qu'elle affiche.
|
||||
if (!Ingredient.Item)
|
||||
{
|
||||
OutReason = ECraftFailureReason::InvalidRecipe;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Inventory->GetItemCount(Ingredient.Item) < Ingredient.Quantity)
|
||||
{
|
||||
OutReason = ECraftFailureReason::MissingIngredients;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UCraftingComponent::GatherIngredientStatus(const UCraftingRecipeDataAsset* Recipe, TArray<FCraftIngredientStatus>& OutStatus) const
|
||||
{
|
||||
OutStatus.Reset();
|
||||
|
||||
if (!Recipe)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OutStatus.Reserve(Recipe->Ingredients.Num());
|
||||
|
||||
for (const FCraftIngredient& Ingredient : Recipe->Ingredients)
|
||||
{
|
||||
if (!Ingredient.Item)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
FCraftIngredientStatus& Status = OutStatus.AddDefaulted_GetRef();
|
||||
Status.Item = Ingredient.Item;
|
||||
Status.RequiredQuantity = Ingredient.Quantity;
|
||||
Status.OwnedQuantity = Inventory ? Inventory->GetItemCount(Ingredient.Item) : 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool UCraftingComponent::Craft(UCraftingRecipeDataAsset* Recipe)
|
||||
{
|
||||
ECraftFailureReason Reason = ECraftFailureReason::None;
|
||||
if (!CanCraft(Recipe, Reason))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// CanCraft a deja verifie que tout est present : aucun RemoveItem ne peut
|
||||
// echouer a partir d'ici, donc pas de consommation a moitie faite.
|
||||
//
|
||||
// Chaque retrait diffuse OnInventoryChanged, donc l'interface se rafraichit
|
||||
// une fois par ingredient. C'est du gaspillage assume : ca reste quelques
|
||||
// dizaines de SetSlot sur un clic, et grouper les diffusions demanderait
|
||||
// une API de lot dans l'inventaire pour un gain invisible.
|
||||
for (const FCraftIngredient& Ingredient : Recipe->Ingredients)
|
||||
{
|
||||
Inventory->RemoveItem(Ingredient.Item, Ingredient.Quantity);
|
||||
}
|
||||
|
||||
// -1 : l'objet sort neuf de la fabrication, avec toutes ses charges.
|
||||
const int32 Leftover = Inventory->AddItem(Recipe->ResultItem, Recipe->ResultQuantity, -1);
|
||||
|
||||
if (Leftover > 0)
|
||||
{
|
||||
if (OnCraftOverflow.IsBound())
|
||||
{
|
||||
OnCraftOverflow.Broadcast(Recipe->ResultItem, Leftover, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Personne pour poser l'objet au sol : il est perdu. C'est une erreur
|
||||
// de branchement, pas une situation de jeu -- d'ou le niveau Error.
|
||||
UE_LOG(LogTemp, Error,
|
||||
TEXT("UCraftingComponent : %d x %s n'ont pas pu entrer dans l'inventaire et personne n'ecoute OnCraftOverflow. Objets perdus."),
|
||||
Leftover, *GetNameSafe(Recipe->ResultItem));
|
||||
}
|
||||
}
|
||||
|
||||
OnCraftSucceeded.Broadcast(Recipe);
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user