first commit
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using OTGIntegrated.Core;
|
||||
using OTGIntegrated.Player;
|
||||
using OTGIntegrated.UI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OTGIntegrated.Crafting
|
||||
{
|
||||
public sealed class CraftingSystem : MonoBehaviour, IInteractable
|
||||
{
|
||||
public List<RecipeData> availableRecipes = new List<RecipeData>();
|
||||
[TextArea(1, 3)] public string lastMessage;
|
||||
|
||||
private OTGIntegrated.Inventory.Inventory playerInventory;
|
||||
private UIManager uiManager;
|
||||
|
||||
public event Action OnCraftingChanged;
|
||||
public Transform InteractTransform => transform;
|
||||
|
||||
public void Initialize(OTGIntegrated.Inventory.Inventory inventory, UIManager manager)
|
||||
{
|
||||
playerInventory = inventory;
|
||||
uiManager = manager;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
Collider collider = GetComponent<Collider>();
|
||||
if (collider != null)
|
||||
{
|
||||
collider.isTrigger = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanCraft(RecipeData recipe)
|
||||
{
|
||||
if (recipe == null || playerInventory == null || recipe.resultItem == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (recipe.ingredients == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < recipe.ingredients.Length; i++)
|
||||
{
|
||||
Ingredient ingredient = recipe.ingredients[i];
|
||||
if (ingredient == null || ingredient.item == null || ingredient.amount <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!playerInventory.HasItem(ingredient.item, ingredient.amount))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return playerInventory.CanAddItem(recipe.resultItem, recipe.resultAmount);
|
||||
}
|
||||
|
||||
public bool Craft(RecipeData recipe)
|
||||
{
|
||||
if (recipe == null)
|
||||
{
|
||||
lastMessage = "Рецепт не выбран.";
|
||||
Refresh();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (playerInventory == null)
|
||||
{
|
||||
lastMessage = "Инвентарь не подключен.";
|
||||
Refresh();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!HasIngredients(recipe))
|
||||
{
|
||||
lastMessage = "Не хватает ресурсов: " + GetMissingIngredientsText(recipe);
|
||||
GameEvents.RaiseNotification(lastMessage);
|
||||
Refresh();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (recipe.resultItem == null || !playerInventory.CanAddItem(recipe.resultItem, recipe.resultAmount))
|
||||
{
|
||||
lastMessage = "Недостаточно места для результата.";
|
||||
GameEvents.RaiseNotification(lastMessage);
|
||||
Refresh();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < recipe.ingredients.Length; i++)
|
||||
{
|
||||
Ingredient ingredient = recipe.ingredients[i];
|
||||
playerInventory.RemoveItem(ingredient.item, ingredient.amount);
|
||||
}
|
||||
|
||||
playerInventory.AddItem(recipe.resultItem, recipe.resultAmount);
|
||||
lastMessage = $"Создано: {recipe.resultItem.displayName} x{recipe.resultAmount}";
|
||||
GameEvents.RaiseNotification(lastMessage);
|
||||
Refresh();
|
||||
return true;
|
||||
}
|
||||
|
||||
public string GetMissingIngredientsText(RecipeData recipe)
|
||||
{
|
||||
if (recipe == null || recipe.ingredients == null || playerInventory == null)
|
||||
{
|
||||
return "нет данных";
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < recipe.ingredients.Length; i++)
|
||||
{
|
||||
Ingredient ingredient = recipe.ingredients[i];
|
||||
if (ingredient == null || ingredient.item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int current = playerInventory.GetAmount(ingredient.item);
|
||||
if (current >= ingredient.amount)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (builder.Length > 0)
|
||||
{
|
||||
builder.Append(", ");
|
||||
}
|
||||
|
||||
builder.Append(ingredient.item.displayName);
|
||||
builder.Append(" ");
|
||||
builder.Append(current);
|
||||
builder.Append("/");
|
||||
builder.Append(ingredient.amount);
|
||||
}
|
||||
|
||||
return builder.Length == 0 ? "место в инвентаре" : builder.ToString();
|
||||
}
|
||||
|
||||
public string GetPrompt()
|
||||
{
|
||||
return "E — открыть верстак";
|
||||
}
|
||||
|
||||
public bool CanInteract(GameObject interactor)
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void Interact(GameObject interactor)
|
||||
{
|
||||
if (uiManager != null)
|
||||
{
|
||||
uiManager.ToggleCrafting();
|
||||
}
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
OnCraftingChanged?.Invoke();
|
||||
}
|
||||
|
||||
private bool HasIngredients(RecipeData recipe)
|
||||
{
|
||||
if (recipe == null || recipe.ingredients == null || playerInventory == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < recipe.ingredients.Length; i++)
|
||||
{
|
||||
Ingredient ingredient = recipe.ingredients[i];
|
||||
if (ingredient == null || ingredient.item == null || ingredient.amount <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!playerInventory.HasItem(ingredient.item, ingredient.amount))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 79a99d064135b6bf6809c0ac449bb5b4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,228 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace OTGIntegrated.Crafting
|
||||
{
|
||||
public sealed class CraftingUI : MonoBehaviour
|
||||
{
|
||||
public GameObject panelRoot;
|
||||
public Transform recipeListRoot;
|
||||
public Text ingredientsText;
|
||||
public Text resultText;
|
||||
public Text descriptionText;
|
||||
public Text messageText;
|
||||
public Button craftButton;
|
||||
public Button closeButton;
|
||||
public Font uiFont;
|
||||
|
||||
private CraftingSystem craftingSystem;
|
||||
private OTGIntegrated.Inventory.Inventory inventory;
|
||||
private RecipeData selectedRecipe;
|
||||
private readonly List<GameObject> spawnedButtons = new List<GameObject>();
|
||||
|
||||
public void Initialize(CraftingSystem system, OTGIntegrated.Inventory.Inventory playerInventory)
|
||||
{
|
||||
craftingSystem = system;
|
||||
inventory = playerInventory;
|
||||
|
||||
if (craftingSystem != null)
|
||||
{
|
||||
craftingSystem.OnCraftingChanged -= Refresh;
|
||||
craftingSystem.OnCraftingChanged += Refresh;
|
||||
}
|
||||
|
||||
if (inventory != null)
|
||||
{
|
||||
inventory.OnInventoryChanged -= Refresh;
|
||||
inventory.OnInventoryChanged += Refresh;
|
||||
}
|
||||
|
||||
if (craftButton != null)
|
||||
{
|
||||
craftButton.onClick.RemoveAllListeners();
|
||||
craftButton.onClick.AddListener(() =>
|
||||
{
|
||||
if (craftingSystem != null && selectedRecipe != null)
|
||||
{
|
||||
craftingSystem.Craft(selectedRecipe);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (closeButton != null)
|
||||
{
|
||||
closeButton.onClick.RemoveAllListeners();
|
||||
closeButton.onClick.AddListener(() => FindObjectOfType<OTGIntegrated.UI.UIManager>()?.CloseCurrentWindow());
|
||||
}
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
Refresh();
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
if (craftingSystem != null)
|
||||
{
|
||||
craftingSystem.OnCraftingChanged -= Refresh;
|
||||
}
|
||||
|
||||
if (inventory != null)
|
||||
{
|
||||
inventory.OnInventoryChanged -= Refresh;
|
||||
}
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
{
|
||||
if (recipeListRoot == null || craftingSystem == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ClearButtons();
|
||||
|
||||
if (selectedRecipe == null && craftingSystem.availableRecipes.Count > 0)
|
||||
{
|
||||
selectedRecipe = craftingSystem.availableRecipes[0];
|
||||
}
|
||||
|
||||
for (int i = 0; i < craftingSystem.availableRecipes.Count; i++)
|
||||
{
|
||||
RecipeData recipe = craftingSystem.availableRecipes[i];
|
||||
CreateRecipeButton(recipe);
|
||||
}
|
||||
|
||||
UpdateDetails();
|
||||
}
|
||||
|
||||
private void CreateRecipeButton(RecipeData recipe)
|
||||
{
|
||||
GameObject root = new GameObject(recipe != null ? recipe.displayName : "Recipe", typeof(RectTransform), typeof(Image), typeof(Button));
|
||||
root.transform.SetParent(recipeListRoot, false);
|
||||
spawnedButtons.Add(root);
|
||||
|
||||
RectTransform rect = root.GetComponent<RectTransform>();
|
||||
rect.sizeDelta = new Vector2(300f, 46f);
|
||||
|
||||
bool canCraft = craftingSystem != null && craftingSystem.CanCraft(recipe);
|
||||
Image image = root.GetComponent<Image>();
|
||||
image.color = recipe == selectedRecipe
|
||||
? new Color(0.82f, 0.58f, 0.18f, 0.95f)
|
||||
: canCraft ? new Color(0.14f, 0.28f, 0.22f, 0.92f) : new Color(0.12f, 0.13f, 0.16f, 0.88f);
|
||||
|
||||
Text label = CreateText(root.transform, "Text", recipe != null ? recipe.displayName : "Recipe", 17, TextAnchor.MiddleLeft);
|
||||
label.rectTransform.anchorMin = new Vector2(0f, 0f);
|
||||
label.rectTransform.anchorMax = new Vector2(1f, 1f);
|
||||
label.rectTransform.offsetMin = new Vector2(14f, 0f);
|
||||
label.rectTransform.offsetMax = new Vector2(-8f, 0f);
|
||||
|
||||
Button button = root.GetComponent<Button>();
|
||||
button.onClick.AddListener(() =>
|
||||
{
|
||||
selectedRecipe = recipe;
|
||||
Refresh();
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateDetails()
|
||||
{
|
||||
if (selectedRecipe == null)
|
||||
{
|
||||
if (ingredientsText != null) ingredientsText.text = "Рецепт не выбран.";
|
||||
if (resultText != null) resultText.text = string.Empty;
|
||||
if (descriptionText != null) descriptionText.text = string.Empty;
|
||||
if (craftButton != null) craftButton.interactable = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ingredientsText != null)
|
||||
{
|
||||
ingredientsText.text = BuildIngredientStatus(selectedRecipe);
|
||||
}
|
||||
|
||||
if (resultText != null)
|
||||
{
|
||||
string resultName = selectedRecipe.resultItem != null ? selectedRecipe.resultItem.displayName : "Нет результата";
|
||||
resultText.text = $"Результат\n{resultName} x{selectedRecipe.resultAmount}";
|
||||
}
|
||||
|
||||
if (descriptionText != null)
|
||||
{
|
||||
descriptionText.text = selectedRecipe.description;
|
||||
}
|
||||
|
||||
if (messageText != null)
|
||||
{
|
||||
messageText.text = craftingSystem != null ? craftingSystem.lastMessage : string.Empty;
|
||||
}
|
||||
|
||||
if (craftButton != null)
|
||||
{
|
||||
craftButton.interactable = craftingSystem != null && craftingSystem.CanCraft(selectedRecipe);
|
||||
}
|
||||
}
|
||||
|
||||
private string BuildIngredientStatus(RecipeData recipe)
|
||||
{
|
||||
if (recipe == null || recipe.ingredients == null)
|
||||
{
|
||||
return "Ингредиенты отсутствуют.";
|
||||
}
|
||||
|
||||
System.Text.StringBuilder builder = new System.Text.StringBuilder();
|
||||
builder.AppendLine("Ингредиенты");
|
||||
for (int i = 0; i < recipe.ingredients.Length; i++)
|
||||
{
|
||||
Ingredient ingredient = recipe.ingredients[i];
|
||||
if (ingredient == null || ingredient.item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int owned = inventory != null ? inventory.GetAmount(ingredient.item) : 0;
|
||||
bool enough = owned >= ingredient.amount;
|
||||
builder.Append(enough ? "<color=#88E08A>" : "<color=#FF8888>");
|
||||
builder.Append(ingredient.item.displayName);
|
||||
builder.Append(": ");
|
||||
builder.Append(owned);
|
||||
builder.Append(" / ");
|
||||
builder.Append(ingredient.amount);
|
||||
builder.AppendLine("</color>");
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private Text CreateText(Transform parent, string name, string value, int fontSize, TextAnchor anchor)
|
||||
{
|
||||
GameObject textObject = new GameObject(name, typeof(RectTransform), typeof(Text));
|
||||
textObject.transform.SetParent(parent, false);
|
||||
Text text = textObject.GetComponent<Text>();
|
||||
text.text = value;
|
||||
text.font = uiFont != null ? uiFont : Resources.GetBuiltinResource<Font>("Arial.ttf");
|
||||
text.fontSize = fontSize;
|
||||
text.alignment = anchor;
|
||||
text.supportRichText = true;
|
||||
text.color = new Color(0.94f, 0.93f, 0.88f, 1f);
|
||||
return text;
|
||||
}
|
||||
|
||||
private void ClearButtons()
|
||||
{
|
||||
for (int i = 0; i < spawnedButtons.Count; i++)
|
||||
{
|
||||
if (spawnedButtons[i] != null)
|
||||
{
|
||||
Destroy(spawnedButtons[i]);
|
||||
}
|
||||
}
|
||||
|
||||
spawnedButtons.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a9f9af7e28388a641b3ae88edb6b98e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using OTGIntegrated.Items;
|
||||
using UnityEngine;
|
||||
|
||||
namespace OTGIntegrated.Crafting
|
||||
{
|
||||
[Serializable]
|
||||
public sealed class Ingredient
|
||||
{
|
||||
public ItemData item;
|
||||
[Min(1)] public int amount = 1;
|
||||
}
|
||||
|
||||
[CreateAssetMenu(fileName = "RecipeData", menuName = "OTG Integrated/Recipe Data")]
|
||||
public sealed class RecipeData : ScriptableObject
|
||||
{
|
||||
public string recipeId;
|
||||
public string displayName;
|
||||
[TextArea(2, 5)] public string description;
|
||||
public Ingredient[] ingredients;
|
||||
public ItemData resultItem;
|
||||
[Min(1)] public int resultAmount = 1;
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(recipeId))
|
||||
{
|
||||
recipeId = name;
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(displayName))
|
||||
{
|
||||
displayName = recipeId;
|
||||
}
|
||||
|
||||
resultAmount = Mathf.Max(1, resultAmount);
|
||||
}
|
||||
|
||||
public string GetIngredientsText()
|
||||
{
|
||||
if (ingredients == null || ingredients.Length == 0)
|
||||
{
|
||||
return "Без ингредиентов";
|
||||
}
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
for (int i = 0; i < ingredients.Length; i++)
|
||||
{
|
||||
Ingredient ingredient = ingredients[i];
|
||||
if (ingredient == null || ingredient.item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (builder.Length > 0)
|
||||
{
|
||||
builder.Append(", ");
|
||||
}
|
||||
|
||||
builder.Append(ingredient.item.displayName);
|
||||
builder.Append(" x");
|
||||
builder.Append(ingredient.amount);
|
||||
}
|
||||
|
||||
return builder.Length == 0 ? "Без ингредиентов" : builder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9b0fcbd4f1eda0f44a96202d7011d01e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user