Added Furnance and smelting

This commit is contained in:
2026-08-11 17:12:34 +03:00
parent 2a30d4d8cf
commit 67e0bcfaaf
82 changed files with 25148 additions and 7186 deletions
@@ -0,0 +1,303 @@
using System.Collections.Generic;
using System.IO;
using Unity.Netcode;
using Unity.Netcode.Components;
using UnityEditor;
using UnityEngine;
public static class FurnacePipelinePrototypeSetup
{
private const string ItemDataFolder = "Assets/CONTENT/Game/Data/Items";
private const string RecipeDataFolder = "Assets/CONTENT/Game/Data/Recipes";
private const string ItemPrefabFolder = "Assets/CONTENT/Game/Prefabs/Items/FurnacePrototype";
private const string WorkstationPrefabFolder = "Assets/CONTENT/Game/Prefabs/Workstations";
private const string NetworkPrefabsPath = "Assets/DefaultNetworkPrefabs.asset";
[MenuItem("FORT-BO/Prototypes/Create Furnace Pipeline")]
public static void CreatePrototype()
{
EnsureFolder(ItemDataFolder);
EnsureFolder(RecipeDataFolder);
EnsureFolder(ItemPrefabFolder);
EnsureFolder(WorkstationPrefabFolder);
ItemDefinition scrapDefinition = GetOrCreateAsset<ItemDefinition>(
$"{ItemDataFolder}/Scrap.asset");
ItemDefinition plateDefinition = GetOrCreateAsset<ItemDefinition>(
$"{ItemDataFolder}/MetalPlate.asset");
ItemDefinition rodDefinition = GetOrCreateAsset<ItemDefinition>(
$"{ItemDataFolder}/MetalRod.asset");
ConfigureItemDefinition(scrapDefinition, "scrap", "Scrap", ItemCategory.Scrap);
ConfigureItemDefinition(plateDefinition, "metal_plate", "Metal Plate", ItemCategory.SmeltedMaterial);
ConfigureItemDefinition(rodDefinition, "metal_rod", "Metal Rod", ItemCategory.SmeltedMaterial);
GameObject scrapPrefab = CreateNetworkItemPrefab(
$"{ItemPrefabFolder}/Scrap.prefab",
"Scrap",
scrapDefinition,
new Vector3(0.55f, 0.4f, 0.5f),
4f,
25f);
GameObject platePrefab = CreateNetworkItemPrefab(
$"{ItemPrefabFolder}/MetalPlate.prefab",
"MetalPlate",
plateDefinition,
new Vector3(0.7f, 0.12f, 0.45f),
2f,
0f);
GameObject rodPrefab = CreateNetworkItemPrefab(
$"{ItemPrefabFolder}/MetalRod.prefab",
"MetalRod",
rodDefinition,
new Vector3(0.18f, 0.18f, 0.9f),
1.5f,
0f);
SetObjectReference(scrapDefinition, "worldPrefab", scrapPrefab.GetComponent<NetworkObject>());
SetObjectReference(plateDefinition, "worldPrefab", platePrefab.GetComponent<NetworkObject>());
SetObjectReference(rodDefinition, "worldPrefab", rodPrefab.GetComponent<NetworkObject>());
ItemCatalog catalog = GetOrCreateAsset<ItemCatalog>($"{ItemDataFolder}/ItemCatalog.asset");
SetObjectReferenceList(catalog, "items", new Object[]
{
scrapDefinition,
plateDefinition,
rodDefinition
});
SmeltingRecipe plateRecipe = GetOrCreateAsset<SmeltingRecipe>(
$"{RecipeDataFolder}/SmeltMetalPlate.asset");
ConfigureRecipe(plateRecipe, "metal_plate", "Metal Plate", plateDefinition, 25f, 3f);
SmeltingRecipe rodRecipe = GetOrCreateAsset<SmeltingRecipe>(
$"{RecipeDataFolder}/SmeltMetalRod.asset");
ConfigureRecipe(rodRecipe, "metal_rod", "Metal Rod", rodDefinition, 20f, 2.5f);
GameObject furnacePrefab = CreateFurnacePrefab(
$"{WorkstationPrefabFolder}/PrototypeFurnace.prefab",
plateRecipe,
rodRecipe);
RegisterNetworkPrefabs(scrapPrefab, platePrefab, rodPrefab, furnacePrefab);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Selection.activeObject = furnacePrefab;
Debug.Log("Furnace pipeline prototype created. Place PrototypeFurnace.prefab in Factory.");
}
private static GameObject CreateNetworkItemPrefab(
string path,
string objectName,
ItemDefinition definition,
Vector3 scale,
float mass,
float metalAmount)
{
GameObject root = GameObject.CreatePrimitive(PrimitiveType.Cube);
root.name = objectName;
root.transform.localScale = scale;
root.AddComponent<NetworkObject>();
NetworkTransform networkTransform = root.AddComponent<NetworkTransform>();
networkTransform.AuthorityMode = NetworkTransform.AuthorityModes.Server;
networkTransform.UseUnreliableDeltas = true;
Rigidbody body = root.AddComponent<Rigidbody>();
body.mass = mass;
body.interpolation = RigidbodyInterpolation.Interpolate;
body.collisionDetectionMode = CollisionDetectionMode.Continuous;
NetworkRigidbody networkRigidbody = root.AddComponent<NetworkRigidbody>();
networkRigidbody.UseRigidBodyForMotion = true;
NetworkCarryableInteractable carryable = root.AddComponent<NetworkCarryableInteractable>();
SetVector3(carryable, "carryOffset", new Vector3(0f, -0.3f, 1.25f));
NetworkItem item = root.AddComponent<NetworkItem>();
SetObjectReference(item, "definition", definition);
if (metalAmount > 0f)
{
FurnaceMetalSource source = root.AddComponent<FurnaceMetalSource>();
SetFloat(source, "metalAmount", metalAmount);
}
GameObject prefab = PrefabUtility.SaveAsPrefabAsset(root, path);
Object.DestroyImmediate(root);
return prefab;
}
private static GameObject CreateFurnacePrefab(
string path,
SmeltingRecipe plateRecipe,
SmeltingRecipe rodRecipe)
{
GameObject root = new("PrototypeFurnace");
root.AddComponent<NetworkObject>();
FurnaceController controller = root.AddComponent<FurnaceController>();
GameObject body = GameObject.CreatePrimitive(PrimitiveType.Cube);
body.name = "Body";
body.transform.SetParent(root.transform, false);
body.transform.localPosition = new Vector3(0f, 0.8f, 0f);
body.transform.localScale = new Vector3(2.2f, 1.6f, 2f);
GameObject inputTrigger = new("ScrapReceiver");
inputTrigger.transform.SetParent(root.transform, false);
inputTrigger.transform.localPosition = new Vector3(0f, 1.7f, 0f);
BoxCollider trigger = inputTrigger.AddComponent<BoxCollider>();
trigger.isTrigger = true;
trigger.size = new Vector3(1.4f, 0.8f, 1.4f);
FurnaceScrapReceiver receiver = inputTrigger.AddComponent<FurnaceScrapReceiver>();
SetObjectReference(receiver, "furnace", controller);
GameObject outputPoint = new("OutputPoint");
outputPoint.transform.SetParent(root.transform, false);
outputPoint.transform.localPosition = new Vector3(0f, 0.45f, 1.5f);
GameObject emptyPoint = new("MetalEmptyPoint");
emptyPoint.transform.SetParent(root.transform, false);
emptyPoint.transform.localPosition = new Vector3(0f, 0.25f, 0f);
GameObject fullPoint = new("MetalFullPoint");
fullPoint.transform.SetParent(root.transform, false);
fullPoint.transform.localPosition = new Vector3(0f, 1.25f, 0f);
GameObject metalVisual = GameObject.CreatePrimitive(PrimitiveType.Cube);
metalVisual.name = "MoltenMetalVisual";
metalVisual.transform.SetParent(root.transform, false);
metalVisual.transform.localPosition = emptyPoint.transform.localPosition;
metalVisual.transform.localScale = new Vector3(1.3f, 0.12f, 1.1f);
Object.DestroyImmediate(metalVisual.GetComponent<Collider>());
FurnaceMetalLevelView levelView = metalVisual.AddComponent<FurnaceMetalLevelView>();
SetObjectReference(levelView, "furnace", controller);
SetObjectReference(levelView, "metalVisual", metalVisual.transform);
SetObjectReference(levelView, "emptyLocalPosition", emptyPoint.transform);
SetObjectReference(levelView, "fullLocalPosition", fullPoint.transform);
GameObject panelAnchor = new("WorldPanelAnchor");
panelAnchor.transform.SetParent(root.transform, false);
panelAnchor.transform.localPosition = new Vector3(1.2f, 1.2f, 1.05f);
SetString(controller, "workstationName", "Prototype Furnace");
SetFloat(controller, "metalCapacity", 100f);
SetObjectReference(controller, "outputPoint", outputPoint.transform);
SetObjectReferenceList(controller, "recipes", new Object[] { plateRecipe, rodRecipe });
GameObject prefab = PrefabUtility.SaveAsPrefabAsset(root, path);
Object.DestroyImmediate(root);
return prefab;
}
private static void ConfigureItemDefinition(
ItemDefinition definition,
string itemId,
string displayName,
ItemCategory category)
{
SetString(definition, "itemId", itemId);
SetString(definition, "displayName", displayName);
SetInt(definition, "category", (int)category);
}
private static void ConfigureRecipe(
SmeltingRecipe recipe,
string recipeId,
string displayName,
ItemDefinition output,
float metalCost,
float duration)
{
SetString(recipe, "recipeId", recipeId);
SetString(recipe, "displayName", displayName);
SetObjectReference(recipe, "outputItem", output);
SetInt(recipe, "outputAmount", 1);
SetFloat(recipe, "metalCost", metalCost);
SetFloat(recipe, "processingDuration", duration);
}
private static void RegisterNetworkPrefabs(params GameObject[] prefabs)
{
NetworkPrefabsList list = AssetDatabase.LoadAssetAtPath<NetworkPrefabsList>(NetworkPrefabsPath);
if (list == null)
throw new FileNotFoundException("Default network prefab list was not found.", NetworkPrefabsPath);
foreach (GameObject prefab in prefabs)
{
if (prefab != null && !list.Contains(prefab))
list.Add(new NetworkPrefab { Prefab = prefab });
}
EditorUtility.SetDirty(list);
}
private static T GetOrCreateAsset<T>(string path) where T : ScriptableObject
{
T asset = AssetDatabase.LoadAssetAtPath<T>(path);
if (asset != null)
return asset;
asset = ScriptableObject.CreateInstance<T>();
AssetDatabase.CreateAsset(asset, path);
return asset;
}
private static void EnsureFolder(string folderPath)
{
string current = "Assets";
string[] segments = folderPath.Split('/');
for (int index = 1; index < segments.Length; index++)
{
string next = $"{current}/{segments[index]}";
if (!AssetDatabase.IsValidFolder(next))
AssetDatabase.CreateFolder(current, segments[index]);
current = next;
}
}
private static void SetString(Object target, string propertyName, string value) =>
Edit(target, propertyName, property => property.stringValue = value);
private static void SetFloat(Object target, string propertyName, float value) =>
Edit(target, propertyName, property => property.floatValue = value);
private static void SetInt(Object target, string propertyName, int value) =>
Edit(target, propertyName, property => property.intValue = value);
private static void SetVector3(Object target, string propertyName, Vector3 value) =>
Edit(target, propertyName, property => property.vector3Value = value);
private static void SetObjectReference(Object target, string propertyName, Object value) =>
Edit(target, propertyName, property => property.objectReferenceValue = value);
private static void SetObjectReferenceList(
Object target,
string propertyName,
IReadOnlyList<Object> values)
{
SerializedObject serializedObject = new(target);
SerializedProperty property = serializedObject.FindProperty(propertyName);
property.arraySize = values.Count;
for (int index = 0; index < values.Count; index++)
property.GetArrayElementAtIndex(index).objectReferenceValue = values[index];
serializedObject.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(target);
}
private static void Edit(
Object target,
string propertyName,
System.Action<SerializedProperty> edit)
{
SerializedObject serializedObject = new(target);
SerializedProperty property = serializedObject.FindProperty(propertyName);
if (property == null)
throw new System.MissingFieldException(target.GetType().Name, propertyName);
edit(property);
serializedObject.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(target);
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 29fefdfbc459d664e91002f5a2faa978