Merge remote-tracking branch 'origin/feat/inport-props' into feat/craft-discovery
# Conflicts: # Assets/External/Animated PBR Chest Demo/Materials/WoodChest.mat # Packages/com.distantlands.cozy.core/Content/Integration/Import for BiRP.unitypackage.meta # Packages/com.distantlands.cozy.core/Content/Integration/Import for HDRP.unitypackage.meta # Packages/com.distantlands.cozy.core/Content/Integration/Import for URP.unitypackage.meta
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using UnityEditor;
|
||||
using UnityEditor.UIElements;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UIElements;
|
||||
using Ashwild.Inventory;
|
||||
using Ashwild.Player;
|
||||
|
||||
namespace Ashwild.EditorTools
|
||||
{
|
||||
@@ -37,8 +40,6 @@ namespace Ashwild.EditorTools
|
||||
private VisualElement maxStackRow;
|
||||
private VisualElement fuelSecondsRow;
|
||||
private VisualElement generationCard;
|
||||
private Button worldGenButton;
|
||||
private Button handGenButton;
|
||||
private ObjectField worldField;
|
||||
private ObjectField handField;
|
||||
private IMGUIContainer iconPickerProxy;
|
||||
@@ -70,6 +71,7 @@ namespace Ashwild.EditorTools
|
||||
Root.Add(BuildCookingCard());
|
||||
Root.Add(BuildFuelCard());
|
||||
Root.Add(BuildPrefabsCard());
|
||||
Root.Add(BuildAnimationCard());
|
||||
Root.Add(BuildGenerationCard());
|
||||
|
||||
currentType = item.ItemType;
|
||||
@@ -114,6 +116,7 @@ namespace Ashwild.EditorTools
|
||||
nameField.AddToClassList("ash-hero__name");
|
||||
nameField.BindProperty(so.FindProperty("itemName"));
|
||||
nameField.RegisterValueChangedCallback(_ => onMetaChanged?.Invoke());
|
||||
nameField.RegisterCallback<FocusOutEvent>(_ => RenameAssetToItemName());
|
||||
info.Add(AshwildUI.EditableNameRow(nameField));
|
||||
|
||||
VisualElement typeRow = new VisualElement();
|
||||
@@ -201,6 +204,50 @@ namespace Ashwild.EditorTools
|
||||
|
||||
#endregion
|
||||
|
||||
#region Asset Naming
|
||||
|
||||
/// <summary>
|
||||
/// Renames the underlying asset file to match the authored item name (spaces → underscores) so
|
||||
/// the ScriptableObject on disk reads like the item it holds instead of the generic "NewItem".
|
||||
/// Runs on focus-out, not per keystroke, so the file is renamed once the name is committed; a
|
||||
/// blank name, an unchanged name, or a rename collision is skipped (the latter logged).
|
||||
/// </summary>
|
||||
private void RenameAssetToItemName()
|
||||
{
|
||||
string path = AssetDatabase.GetAssetPath(item);
|
||||
if (string.IsNullOrEmpty(path)) return;
|
||||
|
||||
string desired = ToAssetFileName(item.ItemName);
|
||||
if (string.IsNullOrEmpty(desired)) return;
|
||||
|
||||
if (string.Equals(Path.GetFileNameWithoutExtension(path), desired, StringComparison.Ordinal)) return;
|
||||
|
||||
string error = AssetDatabase.RenameAsset(path, desired);
|
||||
if (!string.IsNullOrEmpty(error))
|
||||
{
|
||||
Debug.LogError($"[ItemEditorView] Could not rename asset to '{desired}': {error}", item);
|
||||
return;
|
||||
}
|
||||
|
||||
onMetaChanged?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Turns an authored item name into a valid asset file name: trims, collapses every whitespace
|
||||
/// run to a single underscore, and drops characters the filesystem forbids in a file name.
|
||||
/// </summary>
|
||||
private static string ToAssetFileName(string itemName)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(itemName)) return string.Empty;
|
||||
|
||||
string underscored = Regex.Replace(itemName.Trim(), @"\s+", "_");
|
||||
foreach (char invalid in Path.GetInvalidFileNameChars())
|
||||
underscored = underscored.Replace(invalid.ToString(), string.Empty);
|
||||
return underscored;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Cards
|
||||
|
||||
/// <summary>
|
||||
@@ -330,29 +377,47 @@ namespace Ashwild.EditorTools
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generation card: pick a source model/prefab, then build the world pickup (always) and the
|
||||
/// in-hand prefab (tools/weapons only) wrapped around it.
|
||||
/// Animation card: the clip sets this item imposes on each rig while it is held. Both are
|
||||
/// optional — an item that leaves them empty simply keeps the bare-hand animations, which is the
|
||||
/// right answer for every material and most consumables.
|
||||
/// </summary>
|
||||
private VisualElement BuildAnimationCard()
|
||||
{
|
||||
VisualElement card = AshwildUI.Card("Animation");
|
||||
|
||||
ObjectField armsField = new ObjectField("Arms Set") { objectType = typeof(PlayerAnimationSet), allowSceneObjects = false };
|
||||
armsField.BindProperty(so.FindProperty("armsAnimationSet"));
|
||||
card.Add(armsField);
|
||||
|
||||
ObjectField bodyField = new ObjectField("Body Set") { objectType = typeof(PlayerAnimationSet), allowSceneObjects = false };
|
||||
bodyField.BindProperty(so.FindProperty("bodyAnimationSet"));
|
||||
card.Add(bodyField);
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generation card: pick a source prefab, then build both the world pickup (always) and the
|
||||
/// in-hand prefab (tools/weapons only) from it with one button, creating only the ones still
|
||||
/// missing.
|
||||
/// </summary>
|
||||
private VisualElement BuildGenerationCard()
|
||||
{
|
||||
generationCard = AshwildUI.Card("Prefab Generation");
|
||||
generationCard.AddToClassList("ash-card--accent");
|
||||
|
||||
ObjectField source = new ObjectField("Source (Prefab / Model)") { objectType = typeof(GameObject), allowSceneObjects = false };
|
||||
ObjectField source = new ObjectField("Source Prefab") { objectType = typeof(GameObject), allowSceneObjects = false };
|
||||
source.tooltip = "The set-up prefab (meshes + materials). Generation saves a Prefab Variant of it with the gameplay components added.";
|
||||
source.RegisterValueChangedCallback(evt => pendingSource = evt.newValue as GameObject);
|
||||
generationCard.Add(source);
|
||||
|
||||
VisualElement buttons = new VisualElement();
|
||||
buttons.AddToClassList("ash-buttons");
|
||||
|
||||
worldGenButton = new Button(GenerateWorld) { text = "Generate World Prefab" };
|
||||
worldGenButton.AddToClassList("ash-btn");
|
||||
worldGenButton.AddToClassList("ash-btn--primary");
|
||||
buttons.Add(worldGenButton);
|
||||
|
||||
handGenButton = new Button(GenerateHand) { text = "Generate Hand Prefab" };
|
||||
handGenButton.AddToClassList("ash-btn");
|
||||
buttons.Add(handGenButton);
|
||||
Button generate = new Button(GeneratePrefabs) { text = "Generate Prefab" };
|
||||
generate.AddToClassList("ash-btn");
|
||||
generate.AddToClassList("ash-btn--primary");
|
||||
buttons.Add(generate);
|
||||
|
||||
generationCard.Add(buttons);
|
||||
return generationCard;
|
||||
@@ -363,21 +428,23 @@ namespace Ashwild.EditorTools
|
||||
#region Actions
|
||||
|
||||
/// <summary>
|
||||
/// Builds the world pickup prefab around the chosen source and re-renders on success.
|
||||
/// Builds every prefab still missing for the item from the chosen source: the world pickup when
|
||||
/// none is set, and the in-hand prefab when the item is a tool/weapon and none is set. Re-renders
|
||||
/// once if anything was generated so the view reflects the new references and hides the card when
|
||||
/// nothing is left to build.
|
||||
/// </summary>
|
||||
private void GenerateWorld()
|
||||
private void GeneratePrefabs()
|
||||
{
|
||||
if (ItemAssetFactory.GenerateWorldPrefab(item, pendingSource) != null)
|
||||
onPrefabGenerated?.Invoke();
|
||||
}
|
||||
bool generated = false;
|
||||
|
||||
/// <summary>
|
||||
/// Builds the in-hand prefab around the chosen source and re-renders on success.
|
||||
/// </summary>
|
||||
private void GenerateHand()
|
||||
{
|
||||
if (ItemAssetFactory.GenerateHandPrefab(item, pendingSource) != null)
|
||||
onPrefabGenerated?.Invoke();
|
||||
if (worldField.value == null)
|
||||
generated |= ItemAssetFactory.GenerateWorldPrefab(item, pendingSource) != null;
|
||||
|
||||
bool isToolLike = currentType == ItemType.Tool || currentType == ItemType.Weapon;
|
||||
if (isToolLike && handField.value == null)
|
||||
generated |= ItemAssetFactory.GenerateHandPrefab(item, pendingSource) != null;
|
||||
|
||||
if (generated) onPrefabGenerated?.Invoke();
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -398,21 +465,19 @@ namespace Ashwild.EditorTools
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides a generation button once its prefab already exists (and the hand button entirely for
|
||||
/// non-tools), and collapses the whole generation card when nothing is left to generate — so a
|
||||
/// fully wired item shows no redundant tooling. Re-evaluated when the type or a prefab field changes.
|
||||
/// Collapses the whole generation card once every prefab the item needs already exists — the
|
||||
/// world pickup for any item, plus the hand prefab for tools/weapons — so a fully wired item
|
||||
/// shows no redundant tooling. Re-evaluated when the type or a prefab field changes.
|
||||
/// </summary>
|
||||
private void UpdateGenerationVisibility()
|
||||
{
|
||||
if (worldField == null || handField == null || generationCard == null) return;
|
||||
|
||||
bool isToolLike = currentType == ItemType.Tool || currentType == ItemType.Weapon;
|
||||
bool showWorld = worldField.value == null;
|
||||
bool showHand = isToolLike && handField.value == null;
|
||||
bool needWorld = worldField.value == null;
|
||||
bool needHand = isToolLike && handField.value == null;
|
||||
|
||||
SetRowVisible(worldGenButton, showWorld);
|
||||
SetRowVisible(handGenButton, showHand);
|
||||
SetRowVisible(generationCard, showWorld || showHand);
|
||||
SetRowVisible(generationCard, needWorld || needHand);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user