227 lines
5.7 KiB
C++
227 lines
5.7 KiB
C++
// 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;
|
|
}
|