Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6527f559e1 |
@@ -0,0 +1,4 @@
|
|||||||
|
# Memory Index
|
||||||
|
|
||||||
|
- [Ashwild Database tool](ashwild-database-tool.md) — Tools ▸ Ashwild ▸ Database: UI Toolkit window to manage items/recipes & generate prefabs.
|
||||||
|
- [Pickable vestigial NetworkObject](pickable-vestigial-networkobject.md) — old Pickable prefabs carry an unused FishNet NetworkObject; generated ones omit it.
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
name: ashwild-database-tool
|
||||||
|
description: Custom UI Toolkit editor window (Tools ▸ Ashwild ▸ Database) to browse/create ItemData & CraftingRecipe and generate prefabs.
|
||||||
|
metadata:
|
||||||
|
type: project
|
||||||
|
---
|
||||||
|
|
||||||
|
A custom editor window lives at `Assets/GAME/Script/Editor/Database/` (namespace
|
||||||
|
`Ashwild.EditorTools`), opened via **Tools ▸ Ashwild ▸ Database**. Built with UI Toolkit
|
||||||
|
(first UI Toolkit usage in the project) + a `.uss` stylesheet (`AshwildDatabase.uss`).
|
||||||
|
|
||||||
|
Pieces:
|
||||||
|
- `AshwildDatabaseWindow.cs` — shell: Items/Recipes tabs, search, list, action bar.
|
||||||
|
- `ItemEditorView.cs` — custom context-aware ItemData form (cards reveal by type).
|
||||||
|
- `RecipeEditorView.cs` — visual ingredient list builder ("A + B = Result" equation strip).
|
||||||
|
- `ItemAssetFactory.cs` / `RecipeAssetFactory.cs` — asset + prefab creation.
|
||||||
|
- `AshwildUI.cs` — shared styled builders (cards, type badges, type colours).
|
||||||
|
|
||||||
|
**Prefab generation** wraps a chosen source GameObject (model/prefab) as a child of a
|
||||||
|
generated root: world prefab = Pickable layer + auto-fit BoxCollider + `Pickable` (no
|
||||||
|
NetworkObject, see [[pickable-vestigial-networkobject]]); hand prefab = Tools layer +
|
||||||
|
`ToolBehaviour` (mask = Harvestable) + `HeldItemOffset`. Item creation rebuilds the
|
||||||
|
ItemDatabase automatically. Asset folders: items → `ScriptableObjects/Items/`, recipes →
|
||||||
|
`ScriptableObjects/Recipes/`, world prefabs → `Prefabs/Pickable/`, hand prefabs → `Prefabs/Tools/`.
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
name: pickable-vestigial-networkobject
|
||||||
|
description: Existing Pickable prefabs carry an unused FishNet NetworkObject; the canonical generated prefab omits it.
|
||||||
|
metadata:
|
||||||
|
type: project
|
||||||
|
---
|
||||||
|
|
||||||
|
The hand-authored Pickable prefabs (e.g. `Prefabs/Food/Pickable/Raw Steak.prefab`,
|
||||||
|
`Prefabs/Pickable/Tools/axe01.prefab`) still carry a FishNet `NetworkObject` component, left
|
||||||
|
over from a pre-refactor design.
|
||||||
|
|
||||||
|
**Why it's dead:** `Pickable` now derives from `WorldObject` (not `NetworkBehaviour`), and
|
||||||
|
`PickableRegistry.SpawnDropVisual` creates drops with a plain `Instantiate(item.WorldPrefab,…)`
|
||||||
|
— a local visual, never a network spawn. The registry syncs only ids/drop records. So the
|
||||||
|
`NetworkObject` on those prefabs does nothing. This matches CLAUDE.md §3 ("World objects
|
||||||
|
(pickables, harvestables) are not NetworkObjects").
|
||||||
|
|
||||||
|
**How to apply:** When generating or authoring world pickup prefabs, do NOT add a
|
||||||
|
`NetworkObject`. Required pieces are: the Pickable layer (11), a non-trigger collider on the
|
||||||
|
SAME GameObject as the `Pickable` (the interactor reads `IInteractable` via
|
||||||
|
`hit.collider.TryGetComponent`), the `Pickable` script wired to its `ItemData`, and id = -1
|
||||||
|
(scene instances get a baked id via Tools ▸ Ashwild ▸ Assign World Object IDs). The Ashwild
|
||||||
|
Database tool ([[ashwild-database-tool]]) generates them this clean way. The vestigial
|
||||||
|
NetworkObjects on old prefabs could be stripped in a cleanup pass.
|
||||||
@@ -14,20 +14,15 @@ MonoBehaviour:
|
|||||||
m_EditorClassIdentifier: FishNet.Runtime::FishNet.Managing.Object.DefaultPrefabObjects
|
m_EditorClassIdentifier: FishNet.Runtime::FishNet.Managing.Object.DefaultPrefabObjects
|
||||||
_prefabs:
|
_prefabs:
|
||||||
- {fileID: 4512293259955182956, guid: fe2b65b02f0484b41aa8cfa9fbbb0e1d, type: 3}
|
- {fileID: 4512293259955182956, guid: fe2b65b02f0484b41aa8cfa9fbbb0e1d, type: 3}
|
||||||
- {fileID: 3746800168945650153, guid: 8b1c5d7d20bb78c409bb820e38453998, type: 3}
|
|
||||||
- {fileID: 4512293259955182956, guid: 35639798ad77fc145871588b25d66259, type: 3}
|
- {fileID: 4512293259955182956, guid: 35639798ad77fc145871588b25d66259, type: 3}
|
||||||
- {fileID: 201277550, guid: 26a567abbe21227428f5c3d309d1d73c, type: 3}
|
- {fileID: 201277550, guid: 26a567abbe21227428f5c3d309d1d73c, type: 3}
|
||||||
- {fileID: 4512293259955182956, guid: 0d6d0f48b03b17f49a6340103cd9b9d0, type: 3}
|
- {fileID: 4512293259955182956, guid: 0d6d0f48b03b17f49a6340103cd9b9d0, type: 3}
|
||||||
- {fileID: 8475222101369129519, guid: 8cf33e8e99a9b0c4c8f29ff725650de6, type: 3}
|
- {fileID: 8475222101369129519, guid: 8cf33e8e99a9b0c4c8f29ff725650de6, type: 3}
|
||||||
- {fileID: 4512293259955182956, guid: dafef736ca1ae384e9a19eb672843563, type: 3}
|
- {fileID: 4512293259955182956, guid: dafef736ca1ae384e9a19eb672843563, type: 3}
|
||||||
- {fileID: 201277550, guid: 5b712878ecece354ba4ffb026c0a221c, type: 3}
|
- {fileID: 201277550, guid: 5b712878ecece354ba4ffb026c0a221c, type: 3}
|
||||||
- {fileID: 8518425841733827182, guid: 1e19fae830ce27a44b2068b7e4550efc, type: 3}
|
|
||||||
- {fileID: 1374869993561577375, guid: c2eaf86c6fcc88a4fb49a391b143c867, type: 3}
|
- {fileID: 1374869993561577375, guid: c2eaf86c6fcc88a4fb49a391b143c867, type: 3}
|
||||||
- {fileID: 1851170348908994853, guid: c607101b481f147429c13a876ca3d401, type: 3}
|
|
||||||
- {fileID: 4512293259955182956, guid: b8017cef39731ba439c70fecc09488e3, type: 3}
|
- {fileID: 4512293259955182956, guid: b8017cef39731ba439c70fecc09488e3, type: 3}
|
||||||
- {fileID: 7769887562568203203, guid: 7d11390e8f41d214396bc57f3c34e265, type: 3}
|
|
||||||
- {fileID: 4512293259955182956, guid: 44611030e61220d42ab7c37ba3c0ea92, type: 3}
|
- {fileID: 4512293259955182956, guid: 44611030e61220d42ab7c37ba3c0ea92, type: 3}
|
||||||
- {fileID: 2286933990687286586, guid: 292fc529ad2a9a54ea83dfc1beb0b683, type: 3}
|
|
||||||
- {fileID: -5889822588553870904, guid: e412c881030eedb408a7af5b1ecd5c2f, type: 3}
|
- {fileID: -5889822588553870904, guid: e412c881030eedb408a7af5b1ecd5c2f, type: 3}
|
||||||
- {fileID: 8192566354860284824, guid: 6331b3542e64a564c81bc39cedf70c8d, type: 3}
|
- {fileID: 8192566354860284824, guid: 6331b3542e64a564c81bc39cedf70c8d, type: 3}
|
||||||
- {fileID: 4512293259955182956, guid: f32d4c19de900e64cb73cedcb8ba6f70, type: 3}
|
- {fileID: 4512293259955182956, guid: f32d4c19de900e64cb73cedcb8ba6f70, type: 3}
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: de582e2355db187429b0896feceb04e9
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 0ea8988d57601254ca812967bcaf937f
|
||||||
|
TextureImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 13
|
||||||
|
mipmaps:
|
||||||
|
mipMapMode: 0
|
||||||
|
enableMipMap: 0
|
||||||
|
sRGBTexture: 1
|
||||||
|
linearTexture: 0
|
||||||
|
fadeOut: 0
|
||||||
|
borderMipMap: 0
|
||||||
|
mipMapsPreserveCoverage: 0
|
||||||
|
alphaTestReferenceValue: 0.5
|
||||||
|
mipMapFadeDistanceStart: 1
|
||||||
|
mipMapFadeDistanceEnd: 3
|
||||||
|
bumpmap:
|
||||||
|
convertToNormalMap: 0
|
||||||
|
externalNormalMap: 0
|
||||||
|
heightScale: 0.25
|
||||||
|
normalMapFilter: 0
|
||||||
|
flipGreenChannel: 0
|
||||||
|
isReadable: 0
|
||||||
|
streamingMipmaps: 0
|
||||||
|
streamingMipmapsPriority: 0
|
||||||
|
vTOnly: 0
|
||||||
|
ignoreMipmapLimit: 0
|
||||||
|
grayScaleToAlpha: 0
|
||||||
|
generateCubemap: 6
|
||||||
|
cubemapConvolution: 0
|
||||||
|
seamlessCubemap: 0
|
||||||
|
textureFormat: 1
|
||||||
|
maxTextureSize: 2048
|
||||||
|
textureSettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
filterMode: 1
|
||||||
|
aniso: 1
|
||||||
|
mipBias: 0
|
||||||
|
wrapU: 1
|
||||||
|
wrapV: 1
|
||||||
|
wrapW: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
spriteExtrude: 1
|
||||||
|
spriteMeshType: 1
|
||||||
|
alignment: 0
|
||||||
|
spritePivot: {x: 0.5, y: 0.5}
|
||||||
|
spritePixelsToUnits: 100
|
||||||
|
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
spriteGenerateFallbackPhysicsShape: 1
|
||||||
|
alphaUsage: 1
|
||||||
|
alphaIsTransparency: 1
|
||||||
|
spriteTessellationDetail: -1
|
||||||
|
textureType: 8
|
||||||
|
textureShape: 1
|
||||||
|
singleChannelComponent: 0
|
||||||
|
flipbookRows: 1
|
||||||
|
flipbookColumns: 1
|
||||||
|
maxTextureSizeSet: 0
|
||||||
|
compressionQualitySet: 0
|
||||||
|
textureFormatSet: 0
|
||||||
|
ignorePngGamma: 0
|
||||||
|
applyGammaDecoding: 0
|
||||||
|
swizzle: 50462976
|
||||||
|
cookieLightType: 0
|
||||||
|
platformSettings:
|
||||||
|
- serializedVersion: 4
|
||||||
|
buildTarget: DefaultTexturePlatform
|
||||||
|
maxTextureSize: 2048
|
||||||
|
resizeAlgorithm: 0
|
||||||
|
textureFormat: -1
|
||||||
|
textureCompression: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
ignorePlatformSupport: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
- serializedVersion: 4
|
||||||
|
buildTarget: Standalone
|
||||||
|
maxTextureSize: 2048
|
||||||
|
resizeAlgorithm: 0
|
||||||
|
textureFormat: -1
|
||||||
|
textureCompression: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
ignorePlatformSupport: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 573d176f3cdfdbb498814a58d66f6e49
|
||||||
|
TextureImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 13
|
||||||
|
mipmaps:
|
||||||
|
mipMapMode: 0
|
||||||
|
enableMipMap: 0
|
||||||
|
sRGBTexture: 1
|
||||||
|
linearTexture: 0
|
||||||
|
fadeOut: 0
|
||||||
|
borderMipMap: 0
|
||||||
|
mipMapsPreserveCoverage: 0
|
||||||
|
alphaTestReferenceValue: 0.5
|
||||||
|
mipMapFadeDistanceStart: 1
|
||||||
|
mipMapFadeDistanceEnd: 3
|
||||||
|
bumpmap:
|
||||||
|
convertToNormalMap: 0
|
||||||
|
externalNormalMap: 0
|
||||||
|
heightScale: 0.25
|
||||||
|
normalMapFilter: 0
|
||||||
|
flipGreenChannel: 0
|
||||||
|
isReadable: 0
|
||||||
|
streamingMipmaps: 0
|
||||||
|
streamingMipmapsPriority: 0
|
||||||
|
vTOnly: 0
|
||||||
|
ignoreMipmapLimit: 0
|
||||||
|
grayScaleToAlpha: 0
|
||||||
|
generateCubemap: 6
|
||||||
|
cubemapConvolution: 0
|
||||||
|
seamlessCubemap: 0
|
||||||
|
textureFormat: 1
|
||||||
|
maxTextureSize: 2048
|
||||||
|
textureSettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
filterMode: 1
|
||||||
|
aniso: 1
|
||||||
|
mipBias: 0
|
||||||
|
wrapU: 1
|
||||||
|
wrapV: 1
|
||||||
|
wrapW: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
spriteExtrude: 1
|
||||||
|
spriteMeshType: 1
|
||||||
|
alignment: 0
|
||||||
|
spritePivot: {x: 0.5, y: 0.5}
|
||||||
|
spritePixelsToUnits: 100
|
||||||
|
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
spriteGenerateFallbackPhysicsShape: 1
|
||||||
|
alphaUsage: 1
|
||||||
|
alphaIsTransparency: 1
|
||||||
|
spriteTessellationDetail: -1
|
||||||
|
textureType: 8
|
||||||
|
textureShape: 1
|
||||||
|
singleChannelComponent: 0
|
||||||
|
flipbookRows: 1
|
||||||
|
flipbookColumns: 1
|
||||||
|
maxTextureSizeSet: 0
|
||||||
|
compressionQualitySet: 0
|
||||||
|
textureFormatSet: 0
|
||||||
|
ignorePngGamma: 0
|
||||||
|
applyGammaDecoding: 0
|
||||||
|
swizzle: 50462976
|
||||||
|
cookieLightType: 0
|
||||||
|
platformSettings:
|
||||||
|
- serializedVersion: 4
|
||||||
|
buildTarget: DefaultTexturePlatform
|
||||||
|
maxTextureSize: 2048
|
||||||
|
resizeAlgorithm: 0
|
||||||
|
textureFormat: -1
|
||||||
|
textureCompression: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
ignorePlatformSupport: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
- serializedVersion: 4
|
||||||
|
buildTarget: Standalone
|
||||||
|
maxTextureSize: 2048
|
||||||
|
resizeAlgorithm: 0
|
||||||
|
textureFormat: -1
|
||||||
|
textureCompression: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
ignorePlatformSupport: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 43f5a0760e550c148ba53b7db9a7c24e
|
||||||
|
TextureImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 13
|
||||||
|
mipmaps:
|
||||||
|
mipMapMode: 0
|
||||||
|
enableMipMap: 0
|
||||||
|
sRGBTexture: 1
|
||||||
|
linearTexture: 0
|
||||||
|
fadeOut: 0
|
||||||
|
borderMipMap: 0
|
||||||
|
mipMapsPreserveCoverage: 0
|
||||||
|
alphaTestReferenceValue: 0.5
|
||||||
|
mipMapFadeDistanceStart: 1
|
||||||
|
mipMapFadeDistanceEnd: 3
|
||||||
|
bumpmap:
|
||||||
|
convertToNormalMap: 0
|
||||||
|
externalNormalMap: 0
|
||||||
|
heightScale: 0.25
|
||||||
|
normalMapFilter: 0
|
||||||
|
flipGreenChannel: 0
|
||||||
|
isReadable: 0
|
||||||
|
streamingMipmaps: 0
|
||||||
|
streamingMipmapsPriority: 0
|
||||||
|
vTOnly: 0
|
||||||
|
ignoreMipmapLimit: 0
|
||||||
|
grayScaleToAlpha: 0
|
||||||
|
generateCubemap: 6
|
||||||
|
cubemapConvolution: 0
|
||||||
|
seamlessCubemap: 0
|
||||||
|
textureFormat: 1
|
||||||
|
maxTextureSize: 2048
|
||||||
|
textureSettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
filterMode: 1
|
||||||
|
aniso: 1
|
||||||
|
mipBias: 0
|
||||||
|
wrapU: 1
|
||||||
|
wrapV: 1
|
||||||
|
wrapW: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
spriteExtrude: 1
|
||||||
|
spriteMeshType: 1
|
||||||
|
alignment: 0
|
||||||
|
spritePivot: {x: 0.5, y: 0.5}
|
||||||
|
spritePixelsToUnits: 100
|
||||||
|
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
spriteGenerateFallbackPhysicsShape: 1
|
||||||
|
alphaUsage: 1
|
||||||
|
alphaIsTransparency: 1
|
||||||
|
spriteTessellationDetail: -1
|
||||||
|
textureType: 8
|
||||||
|
textureShape: 1
|
||||||
|
singleChannelComponent: 0
|
||||||
|
flipbookRows: 1
|
||||||
|
flipbookColumns: 1
|
||||||
|
maxTextureSizeSet: 0
|
||||||
|
compressionQualitySet: 0
|
||||||
|
textureFormatSet: 0
|
||||||
|
ignorePngGamma: 0
|
||||||
|
applyGammaDecoding: 0
|
||||||
|
swizzle: 50462976
|
||||||
|
cookieLightType: 0
|
||||||
|
platformSettings:
|
||||||
|
- serializedVersion: 4
|
||||||
|
buildTarget: DefaultTexturePlatform
|
||||||
|
maxTextureSize: 2048
|
||||||
|
resizeAlgorithm: 0
|
||||||
|
textureFormat: -1
|
||||||
|
textureCompression: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
ignorePlatformSupport: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
- serializedVersion: 4
|
||||||
|
buildTarget: Standalone
|
||||||
|
maxTextureSize: 2048
|
||||||
|
resizeAlgorithm: 0
|
||||||
|
textureFormat: -1
|
||||||
|
textureCompression: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
ignorePlatformSupport: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
@@ -0,0 +1,117 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8a4bc5d9209e3344fbd9505fee280256
|
||||||
|
TextureImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 13
|
||||||
|
mipmaps:
|
||||||
|
mipMapMode: 0
|
||||||
|
enableMipMap: 0
|
||||||
|
sRGBTexture: 1
|
||||||
|
linearTexture: 0
|
||||||
|
fadeOut: 0
|
||||||
|
borderMipMap: 0
|
||||||
|
mipMapsPreserveCoverage: 0
|
||||||
|
alphaTestReferenceValue: 0.5
|
||||||
|
mipMapFadeDistanceStart: 1
|
||||||
|
mipMapFadeDistanceEnd: 3
|
||||||
|
bumpmap:
|
||||||
|
convertToNormalMap: 0
|
||||||
|
externalNormalMap: 0
|
||||||
|
heightScale: 0.25
|
||||||
|
normalMapFilter: 0
|
||||||
|
flipGreenChannel: 0
|
||||||
|
isReadable: 0
|
||||||
|
streamingMipmaps: 0
|
||||||
|
streamingMipmapsPriority: 0
|
||||||
|
vTOnly: 0
|
||||||
|
ignoreMipmapLimit: 0
|
||||||
|
grayScaleToAlpha: 0
|
||||||
|
generateCubemap: 6
|
||||||
|
cubemapConvolution: 0
|
||||||
|
seamlessCubemap: 0
|
||||||
|
textureFormat: 1
|
||||||
|
maxTextureSize: 2048
|
||||||
|
textureSettings:
|
||||||
|
serializedVersion: 2
|
||||||
|
filterMode: 1
|
||||||
|
aniso: 1
|
||||||
|
mipBias: 0
|
||||||
|
wrapU: 1
|
||||||
|
wrapV: 1
|
||||||
|
wrapW: 0
|
||||||
|
nPOTScale: 0
|
||||||
|
lightmap: 0
|
||||||
|
compressionQuality: 50
|
||||||
|
spriteMode: 1
|
||||||
|
spriteExtrude: 1
|
||||||
|
spriteMeshType: 1
|
||||||
|
alignment: 0
|
||||||
|
spritePivot: {x: 0.5, y: 0.5}
|
||||||
|
spritePixelsToUnits: 100
|
||||||
|
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||||
|
spriteGenerateFallbackPhysicsShape: 1
|
||||||
|
alphaUsage: 1
|
||||||
|
alphaIsTransparency: 1
|
||||||
|
spriteTessellationDetail: -1
|
||||||
|
textureType: 8
|
||||||
|
textureShape: 1
|
||||||
|
singleChannelComponent: 0
|
||||||
|
flipbookRows: 1
|
||||||
|
flipbookColumns: 1
|
||||||
|
maxTextureSizeSet: 0
|
||||||
|
compressionQualitySet: 0
|
||||||
|
textureFormatSet: 0
|
||||||
|
ignorePngGamma: 0
|
||||||
|
applyGammaDecoding: 0
|
||||||
|
swizzle: 50462976
|
||||||
|
cookieLightType: 0
|
||||||
|
platformSettings:
|
||||||
|
- serializedVersion: 4
|
||||||
|
buildTarget: DefaultTexturePlatform
|
||||||
|
maxTextureSize: 2048
|
||||||
|
resizeAlgorithm: 0
|
||||||
|
textureFormat: -1
|
||||||
|
textureCompression: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
ignorePlatformSupport: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
- serializedVersion: 4
|
||||||
|
buildTarget: Standalone
|
||||||
|
maxTextureSize: 2048
|
||||||
|
resizeAlgorithm: 0
|
||||||
|
textureFormat: -1
|
||||||
|
textureCompression: 1
|
||||||
|
compressionQuality: 50
|
||||||
|
crunchedCompression: 0
|
||||||
|
allowsAlphaSplitting: 0
|
||||||
|
overridden: 0
|
||||||
|
ignorePlatformSupport: 0
|
||||||
|
androidETC2FallbackOverride: 0
|
||||||
|
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||||
|
spriteSheet:
|
||||||
|
serializedVersion: 2
|
||||||
|
sprites: []
|
||||||
|
outline: []
|
||||||
|
customData:
|
||||||
|
physicsShape: []
|
||||||
|
bones: []
|
||||||
|
spriteID: 5e97eb03825dee720800000000000000
|
||||||
|
internalID: 0
|
||||||
|
vertices: []
|
||||||
|
indices:
|
||||||
|
edges: []
|
||||||
|
weights: []
|
||||||
|
secondaryTextures: []
|
||||||
|
spriteCustomMetadata:
|
||||||
|
entries: []
|
||||||
|
nameFileIdTable: {}
|
||||||
|
mipmapLimitGroupName:
|
||||||
|
pSDRemoveMatte: 0
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -9,11 +9,10 @@ GameObject:
|
|||||||
serializedVersion: 6
|
serializedVersion: 6
|
||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 6028821745233737597}
|
- component: {fileID: 6028821745233737597}
|
||||||
|
- component: {fileID: 5639895077949637244}
|
||||||
- component: {fileID: 3533309669773555587}
|
- component: {fileID: 3533309669773555587}
|
||||||
- component: {fileID: 2488581501338454467}
|
- component: {fileID: 2488581501338454467}
|
||||||
- component: {fileID: 5345512882623272780}
|
|
||||||
- component: {fileID: 8419294417422120418}
|
- component: {fileID: 8419294417422120418}
|
||||||
- component: {fileID: 3746800168945650153}
|
|
||||||
m_Layer: 11
|
m_Layer: 11
|
||||||
m_Name: CookedSteak
|
m_Name: CookedSteak
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -36,6 +35,22 @@ Transform:
|
|||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
|
--- !u!114 &5639895077949637244
|
||||||
|
MonoBehaviour:
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_GameObject: {fileID: 6507764929137009914}
|
||||||
|
m_Enabled: 1
|
||||||
|
m_EditorHideFlags: 0
|
||||||
|
m_Script: {fileID: 11500000, guid: eed740dea8aa59b4cb516f25ce139314, type: 3}
|
||||||
|
m_Name:
|
||||||
|
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Network.Pickable
|
||||||
|
id: -1
|
||||||
|
itemData: {fileID: 11400000, guid: 93acb9b63b2c4d03b8cbb021ebb7642d, type: 2}
|
||||||
|
quantity: 1
|
||||||
|
interactionVerb: Pick up
|
||||||
--- !u!33 &3533309669773555587
|
--- !u!33 &3533309669773555587
|
||||||
MeshFilter:
|
MeshFilter:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -93,24 +108,6 @@ MeshRenderer:
|
|||||||
m_SortingOrder: 0
|
m_SortingOrder: 0
|
||||||
m_MaskInteraction: 0
|
m_MaskInteraction: 0
|
||||||
m_AdditionalVertexStreams: {fileID: 0}
|
m_AdditionalVertexStreams: {fileID: 0}
|
||||||
--- !u!114 &5345512882623272780
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 6507764929137009914}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 7c57b699079965047b70cd1bad8f3554, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::Pickable
|
|
||||||
_componentIndexCache: 0
|
|
||||||
_addedNetworkObject: {fileID: 3746800168945650153}
|
|
||||||
_networkObjectCache: {fileID: 3746800168945650153}
|
|
||||||
itemData: {fileID: 11400000, guid: 93acb9b63b2c4d03b8cbb021ebb7642d, type: 2}
|
|
||||||
quantity: 1
|
|
||||||
interactionVerb: Pick up
|
|
||||||
--- !u!65 &8419294417422120418
|
--- !u!65 &8419294417422120418
|
||||||
BoxCollider:
|
BoxCollider:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -132,61 +129,3 @@ BoxCollider:
|
|||||||
serializedVersion: 3
|
serializedVersion: 3
|
||||||
m_Size: {x: 0.2084811, y: 0.04969957, z: 0.27743918}
|
m_Size: {x: 0.2084811, y: 0.04969957, z: 0.27743918}
|
||||||
m_Center: {x: -0.00043765083, y: 0.024869436, z: -0.0005873516}
|
m_Center: {x: -0.00043765083, y: 0.024869436, z: -0.0005873516}
|
||||||
--- !u!114 &3746800168945650153
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 6507764929137009914}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier: FishNet.Runtime::FishNet.Object.NetworkObject
|
|
||||||
<IsNested>k__BackingField: 0
|
|
||||||
WasActiveDuringEdit: 0
|
|
||||||
WasActiveDuringEdit_Set1: 1
|
|
||||||
<ComponentIndex>k__BackingField: 0
|
|
||||||
<PredictedSpawn>k__BackingField: {fileID: 0}
|
|
||||||
<PredictedOwner>k__BackingField: {fileID: 0}
|
|
||||||
NetworkBehaviours:
|
|
||||||
- {fileID: 5345512882623272780}
|
|
||||||
InitializedParentNetworkBehaviour: {fileID: 0}
|
|
||||||
InitializedNestedNetworkObjects: []
|
|
||||||
RuntimeParentNetworkBehaviour: {fileID: 0}
|
|
||||||
RuntimeChildNetworkBehaviours: []
|
|
||||||
_isNetworked: 1
|
|
||||||
_isSpawnable: 1
|
|
||||||
_isGlobal: 0
|
|
||||||
_initializeOrder: 0
|
|
||||||
_preventDespawnOnDisconnect: 0
|
|
||||||
_defaultDespawnType: 0
|
|
||||||
_initializedTimestamp: -8584197526105594093
|
|
||||||
_localLevelOfDetailCalculationType: 0
|
|
||||||
_useLevelOfDetail: 0
|
|
||||||
_useRootLevelOfDetail: 1
|
|
||||||
NetworkObserver: {fileID: 0}
|
|
||||||
_enablePrediction: 0
|
|
||||||
_predictionType: 0
|
|
||||||
_localReconcileCorrectionType: 2
|
|
||||||
_graphicalObject: {fileID: 0}
|
|
||||||
_detachGraphicalObject: 0
|
|
||||||
_enableStateForwarding: 1
|
|
||||||
_networkTransform: {fileID: 0}
|
|
||||||
_ownerInterpolation: 1
|
|
||||||
_ownerSmoothedProperties: 255
|
|
||||||
_adaptiveInterpolation: 3
|
|
||||||
_spectatorSmoothedProperties: 255
|
|
||||||
_spectatorInterpolation: 2
|
|
||||||
_enableTeleport: 0
|
|
||||||
_teleportThreshold: 1
|
|
||||||
<PrefabId>k__BackingField: 3
|
|
||||||
<SpawnableCollectionId>k__BackingField: 0
|
|
||||||
<AssetPathHash>k__BackingField: 4497985491359141594
|
|
||||||
SceneId: 0
|
|
||||||
SerializedTransformProperties:
|
|
||||||
Position: {x: 0, y: 0, z: 0}
|
|
||||||
Rotation: {x: 0.000000021855694, y: 0, z: -0, w: 1}
|
|
||||||
Scale: {x: 1, y: 1, z: 1}
|
|
||||||
IsValid: 1
|
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ GameObject:
|
|||||||
serializedVersion: 6
|
serializedVersion: 6
|
||||||
m_Component:
|
m_Component:
|
||||||
- component: {fileID: 2400332513845409276}
|
- component: {fileID: 2400332513845409276}
|
||||||
- component: {fileID: 4462286336473188449}
|
- component: {fileID: 5687242054726960884}
|
||||||
- component: {fileID: 4579747089151895854}
|
- component: {fileID: 4579747089151895854}
|
||||||
- component: {fileID: 6804968349030847047}
|
- component: {fileID: 6804968349030847047}
|
||||||
- component: {fileID: 8375007824052685006}
|
- component: {fileID: 8375007824052685006}
|
||||||
- component: {fileID: 7769887562568203203}
|
|
||||||
m_Layer: 11
|
m_Layer: 11
|
||||||
m_Name: Raw Steak
|
m_Name: Raw Steak
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -36,7 +35,7 @@ Transform:
|
|||||||
m_Children: []
|
m_Children: []
|
||||||
m_Father: {fileID: 0}
|
m_Father: {fileID: 0}
|
||||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||||
--- !u!114 &4462286336473188449
|
--- !u!114 &5687242054726960884
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
@@ -45,12 +44,10 @@ MonoBehaviour:
|
|||||||
m_GameObject: {fileID: 5263385035876967886}
|
m_GameObject: {fileID: 5263385035876967886}
|
||||||
m_Enabled: 1
|
m_Enabled: 1
|
||||||
m_EditorHideFlags: 0
|
m_EditorHideFlags: 0
|
||||||
m_Script: {fileID: 11500000, guid: 7c57b699079965047b70cd1bad8f3554, type: 3}
|
m_Script: {fileID: 11500000, guid: eed740dea8aa59b4cb516f25ce139314, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::Pickable
|
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Network.Pickable
|
||||||
_componentIndexCache: 0
|
id: -1
|
||||||
_addedNetworkObject: {fileID: 7769887562568203203}
|
|
||||||
_networkObjectCache: {fileID: 7769887562568203203}
|
|
||||||
itemData: {fileID: 11400000, guid: 6b543ed8734f4dcf984c1c43d63a9207, type: 2}
|
itemData: {fileID: 11400000, guid: 6b543ed8734f4dcf984c1c43d63a9207, type: 2}
|
||||||
quantity: 1
|
quantity: 1
|
||||||
interactionVerb: Pick up
|
interactionVerb: Pick up
|
||||||
@@ -132,61 +129,3 @@ BoxCollider:
|
|||||||
serializedVersion: 3
|
serializedVersion: 3
|
||||||
m_Size: {x: 0.20744237, y: 0.052912164, z: 0.2803244}
|
m_Size: {x: 0.20744237, y: 0.052912164, z: 0.2803244}
|
||||||
m_Center: {x: -0.000022616237, y: 0.026525198, z: -0.00026460737}
|
m_Center: {x: -0.000022616237, y: 0.026525198, z: -0.00026460737}
|
||||||
--- !u!114 &7769887562568203203
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 5263385035876967886}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier: FishNet.Runtime::FishNet.Object.NetworkObject
|
|
||||||
<IsNested>k__BackingField: 0
|
|
||||||
WasActiveDuringEdit: 0
|
|
||||||
WasActiveDuringEdit_Set1: 1
|
|
||||||
<ComponentIndex>k__BackingField: 0
|
|
||||||
<PredictedSpawn>k__BackingField: {fileID: 0}
|
|
||||||
<PredictedOwner>k__BackingField: {fileID: 0}
|
|
||||||
NetworkBehaviours:
|
|
||||||
- {fileID: 4462286336473188449}
|
|
||||||
InitializedParentNetworkBehaviour: {fileID: 0}
|
|
||||||
InitializedNestedNetworkObjects: []
|
|
||||||
RuntimeParentNetworkBehaviour: {fileID: 0}
|
|
||||||
RuntimeChildNetworkBehaviours: []
|
|
||||||
_isNetworked: 1
|
|
||||||
_isSpawnable: 1
|
|
||||||
_isGlobal: 0
|
|
||||||
_initializeOrder: 0
|
|
||||||
_preventDespawnOnDisconnect: 0
|
|
||||||
_defaultDespawnType: 0
|
|
||||||
_initializedTimestamp: -8584197526105594093
|
|
||||||
_localLevelOfDetailCalculationType: 0
|
|
||||||
_useLevelOfDetail: 0
|
|
||||||
_useRootLevelOfDetail: 1
|
|
||||||
NetworkObserver: {fileID: 0}
|
|
||||||
_enablePrediction: 0
|
|
||||||
_predictionType: 0
|
|
||||||
_localReconcileCorrectionType: 2
|
|
||||||
_graphicalObject: {fileID: 0}
|
|
||||||
_detachGraphicalObject: 0
|
|
||||||
_enableStateForwarding: 1
|
|
||||||
_networkTransform: {fileID: 0}
|
|
||||||
_ownerInterpolation: 1
|
|
||||||
_ownerSmoothedProperties: 255
|
|
||||||
_adaptiveInterpolation: 3
|
|
||||||
_spectatorSmoothedProperties: 255
|
|
||||||
_spectatorInterpolation: 2
|
|
||||||
_enableTeleport: 0
|
|
||||||
_teleportThreshold: 1
|
|
||||||
<PrefabId>k__BackingField: 21
|
|
||||||
<SpawnableCollectionId>k__BackingField: 0
|
|
||||||
<AssetPathHash>k__BackingField: 15131100777819643404
|
|
||||||
SceneId: 0
|
|
||||||
SerializedTransformProperties:
|
|
||||||
Position: {x: 0, y: 0, z: 0}
|
|
||||||
Rotation: {x: 0.000000021855694, y: 0, z: -0, w: 1}
|
|
||||||
Scale: {x: 1, y: 1, z: 1}
|
|
||||||
IsValid: 1
|
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ GameObject:
|
|||||||
- component: {fileID: 33113545875603948}
|
- component: {fileID: 33113545875603948}
|
||||||
- component: {fileID: 23481562414732822}
|
- component: {fileID: 23481562414732822}
|
||||||
- component: {fileID: -8701151183354302330}
|
- component: {fileID: -8701151183354302330}
|
||||||
- component: {fileID: -1685119718098522260}
|
|
||||||
- component: {fileID: 1433484159821976236}
|
- component: {fileID: 1433484159821976236}
|
||||||
- component: {fileID: 1851170348908994853}
|
- component: {fileID: 2073025095026659661}
|
||||||
m_Layer: 6
|
m_Layer: 6
|
||||||
m_Name: axe01
|
m_Name: axe01
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -115,24 +114,6 @@ BoxCollider:
|
|||||||
serializedVersion: 3
|
serializedVersion: 3
|
||||||
m_Size: {x: 0.5015001, y: 1.1154001, z: 0.16560005}
|
m_Size: {x: 0.5015001, y: 1.1154001, z: 0.16560005}
|
||||||
m_Center: {x: 0.15155002, y: 0.294, z: 0.0001999997}
|
m_Center: {x: 0.15155002, y: 0.294, z: 0.0001999997}
|
||||||
--- !u!114 &-1685119718098522260
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1878273237308662}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 7c57b699079965047b70cd1bad8f3554, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::ItemPickup
|
|
||||||
_componentIndexCache: 0
|
|
||||||
_addedNetworkObject: {fileID: 1851170348908994853}
|
|
||||||
_networkObjectCache: {fileID: 1851170348908994853}
|
|
||||||
itemData: {fileID: 11400000, guid: 66c8ce9f2516f22488502db44d9d49af, type: 2}
|
|
||||||
quantity: 1
|
|
||||||
interactionVerb: Pick up
|
|
||||||
--- !u!54 &1433484159821976236
|
--- !u!54 &1433484159821976236
|
||||||
Rigidbody:
|
Rigidbody:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -160,7 +141,7 @@ Rigidbody:
|
|||||||
m_Interpolate: 0
|
m_Interpolate: 0
|
||||||
m_Constraints: 0
|
m_Constraints: 0
|
||||||
m_CollisionDetection: 0
|
m_CollisionDetection: 0
|
||||||
--- !u!114 &1851170348908994853
|
--- !u!114 &2073025095026659661
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
@@ -169,52 +150,10 @@ MonoBehaviour:
|
|||||||
m_GameObject: {fileID: 1878273237308662}
|
m_GameObject: {fileID: 1878273237308662}
|
||||||
m_Enabled: 1
|
m_Enabled: 1
|
||||||
m_EditorHideFlags: 0
|
m_EditorHideFlags: 0
|
||||||
m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3}
|
m_Script: {fileID: 11500000, guid: eed740dea8aa59b4cb516f25ce139314, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: FishNet.Runtime::FishNet.Object.NetworkObject
|
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Network.Pickable
|
||||||
<IsNested>k__BackingField: 0
|
id: -1
|
||||||
WasActiveDuringEdit: 0
|
itemData: {fileID: 11400000, guid: 66c8ce9f2516f22488502db44d9d49af, type: 2}
|
||||||
WasActiveDuringEdit_Set1: 1
|
quantity: 1
|
||||||
<ComponentIndex>k__BackingField: 0
|
interactionVerb: Pick up
|
||||||
<PredictedSpawn>k__BackingField: {fileID: 0}
|
|
||||||
<PredictedOwner>k__BackingField: {fileID: 0}
|
|
||||||
NetworkBehaviours:
|
|
||||||
- {fileID: -1685119718098522260}
|
|
||||||
InitializedParentNetworkBehaviour: {fileID: 0}
|
|
||||||
InitializedNestedNetworkObjects: []
|
|
||||||
RuntimeParentNetworkBehaviour: {fileID: 0}
|
|
||||||
RuntimeChildNetworkBehaviours: []
|
|
||||||
_isNetworked: 1
|
|
||||||
_isSpawnable: 1
|
|
||||||
_isGlobal: 0
|
|
||||||
_initializeOrder: 0
|
|
||||||
_preventDespawnOnDisconnect: 0
|
|
||||||
_defaultDespawnType: 0
|
|
||||||
_initializedTimestamp: -8584197526105594093
|
|
||||||
_localLevelOfDetailCalculationType: 0
|
|
||||||
_useLevelOfDetail: 0
|
|
||||||
_useRootLevelOfDetail: 1
|
|
||||||
NetworkObserver: {fileID: 0}
|
|
||||||
_enablePrediction: 0
|
|
||||||
_predictionType: 0
|
|
||||||
_localReconcileCorrectionType: 2
|
|
||||||
_graphicalObject: {fileID: 0}
|
|
||||||
_detachGraphicalObject: 0
|
|
||||||
_enableStateForwarding: 1
|
|
||||||
_networkTransform: {fileID: 0}
|
|
||||||
_ownerInterpolation: 1
|
|
||||||
_ownerSmoothedProperties: 255
|
|
||||||
_adaptiveInterpolation: 3
|
|
||||||
_spectatorSmoothedProperties: 255
|
|
||||||
_spectatorInterpolation: 2
|
|
||||||
_enableTeleport: 0
|
|
||||||
_teleportThreshold: 1
|
|
||||||
<PrefabId>k__BackingField: 17
|
|
||||||
<SpawnableCollectionId>k__BackingField: 0
|
|
||||||
<AssetPathHash>k__BackingField: 10800420217546823107
|
|
||||||
SceneId: 0
|
|
||||||
SerializedTransformProperties:
|
|
||||||
Position: {x: 0, y: 0, z: 0}
|
|
||||||
Rotation: {x: 0, y: -0.7071068, z: 0, w: 0.7071068}
|
|
||||||
Scale: {x: 1, y: 1, z: 1}
|
|
||||||
IsValid: 1
|
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ GameObject:
|
|||||||
- component: {fileID: 33852252603615026}
|
- component: {fileID: 33852252603615026}
|
||||||
- component: {fileID: 23994028819166706}
|
- component: {fileID: 23994028819166706}
|
||||||
- component: {fileID: 7229770545233589333}
|
- component: {fileID: 7229770545233589333}
|
||||||
- component: {fileID: 3008732563107889085}
|
|
||||||
- component: {fileID: -4666900484668152208}
|
- component: {fileID: -4666900484668152208}
|
||||||
- component: {fileID: 8518425841733827182}
|
- component: {fileID: 2428768101541199601}
|
||||||
m_Layer: 6
|
m_Layer: 6
|
||||||
m_Name: hammer01
|
m_Name: hammer01
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -115,24 +114,6 @@ BoxCollider:
|
|||||||
serializedVersion: 3
|
serializedVersion: 3
|
||||||
m_Size: {x: 0.3812001, y: 0.7715, z: 0.20330004}
|
m_Size: {x: 0.3812001, y: 0.7715, z: 0.20330004}
|
||||||
m_Center: {x: -0.0043000067, y: 0.13665001, z: 0.0017500003}
|
m_Center: {x: -0.0043000067, y: 0.13665001, z: 0.0017500003}
|
||||||
--- !u!114 &3008732563107889085
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1085285520790580}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 7c57b699079965047b70cd1bad8f3554, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::ItemPickup
|
|
||||||
_componentIndexCache: 0
|
|
||||||
_addedNetworkObject: {fileID: 8518425841733827182}
|
|
||||||
_networkObjectCache: {fileID: 8518425841733827182}
|
|
||||||
itemData: {fileID: 11400000, guid: 4853e475b87b4e64aa4a568a2be2c7f0, type: 2}
|
|
||||||
quantity: 1
|
|
||||||
interactionVerb: Pick up
|
|
||||||
--- !u!54 &-4666900484668152208
|
--- !u!54 &-4666900484668152208
|
||||||
Rigidbody:
|
Rigidbody:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -160,7 +141,7 @@ Rigidbody:
|
|||||||
m_Interpolate: 0
|
m_Interpolate: 0
|
||||||
m_Constraints: 0
|
m_Constraints: 0
|
||||||
m_CollisionDetection: 0
|
m_CollisionDetection: 0
|
||||||
--- !u!114 &8518425841733827182
|
--- !u!114 &2428768101541199601
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
@@ -169,52 +150,10 @@ MonoBehaviour:
|
|||||||
m_GameObject: {fileID: 1085285520790580}
|
m_GameObject: {fileID: 1085285520790580}
|
||||||
m_Enabled: 1
|
m_Enabled: 1
|
||||||
m_EditorHideFlags: 0
|
m_EditorHideFlags: 0
|
||||||
m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3}
|
m_Script: {fileID: 11500000, guid: eed740dea8aa59b4cb516f25ce139314, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: FishNet.Runtime::FishNet.Object.NetworkObject
|
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Network.Pickable
|
||||||
<IsNested>k__BackingField: 0
|
id: -1
|
||||||
WasActiveDuringEdit: 0
|
itemData: {fileID: 11400000, guid: 4853e475b87b4e64aa4a568a2be2c7f0, type: 2}
|
||||||
WasActiveDuringEdit_Set1: 1
|
quantity: 1
|
||||||
<ComponentIndex>k__BackingField: 0
|
interactionVerb: Pick up
|
||||||
<PredictedSpawn>k__BackingField: {fileID: 0}
|
|
||||||
<PredictedOwner>k__BackingField: {fileID: 0}
|
|
||||||
NetworkBehaviours:
|
|
||||||
- {fileID: 3008732563107889085}
|
|
||||||
InitializedParentNetworkBehaviour: {fileID: 0}
|
|
||||||
InitializedNestedNetworkObjects: []
|
|
||||||
RuntimeParentNetworkBehaviour: {fileID: 0}
|
|
||||||
RuntimeChildNetworkBehaviours: []
|
|
||||||
_isNetworked: 1
|
|
||||||
_isSpawnable: 1
|
|
||||||
_isGlobal: 0
|
|
||||||
_initializeOrder: 0
|
|
||||||
_preventDespawnOnDisconnect: 0
|
|
||||||
_defaultDespawnType: 0
|
|
||||||
_initializedTimestamp: -8584197526105594093
|
|
||||||
_localLevelOfDetailCalculationType: 0
|
|
||||||
_useLevelOfDetail: 0
|
|
||||||
_useRootLevelOfDetail: 1
|
|
||||||
NetworkObserver: {fileID: 0}
|
|
||||||
_enablePrediction: 0
|
|
||||||
_predictionType: 0
|
|
||||||
_localReconcileCorrectionType: 2
|
|
||||||
_graphicalObject: {fileID: 0}
|
|
||||||
_detachGraphicalObject: 0
|
|
||||||
_enableStateForwarding: 1
|
|
||||||
_networkTransform: {fileID: 0}
|
|
||||||
_ownerInterpolation: 1
|
|
||||||
_ownerSmoothedProperties: 255
|
|
||||||
_adaptiveInterpolation: 3
|
|
||||||
_spectatorSmoothedProperties: 255
|
|
||||||
_spectatorInterpolation: 2
|
|
||||||
_enableTeleport: 0
|
|
||||||
_teleportThreshold: 1
|
|
||||||
<PrefabId>k__BackingField: 16
|
|
||||||
<SpawnableCollectionId>k__BackingField: 0
|
|
||||||
<AssetPathHash>k__BackingField: 10690802766664710013
|
|
||||||
SceneId: 0
|
|
||||||
SerializedTransformProperties:
|
|
||||||
Position: {x: 0, y: 0, z: 0}
|
|
||||||
Rotation: {x: 0, y: -0.7071068, z: 0, w: 0.7071068}
|
|
||||||
Scale: {x: 1, y: 1, z: 1}
|
|
||||||
IsValid: 1
|
|
||||||
|
|||||||
@@ -12,9 +12,8 @@ GameObject:
|
|||||||
- component: {fileID: 33121315882126232}
|
- component: {fileID: 33121315882126232}
|
||||||
- component: {fileID: 23142643217362146}
|
- component: {fileID: 23142643217362146}
|
||||||
- component: {fileID: 968333719620630415}
|
- component: {fileID: 968333719620630415}
|
||||||
- component: {fileID: 1867436537062894882}
|
|
||||||
- component: {fileID: 3862993572698624464}
|
- component: {fileID: 3862993572698624464}
|
||||||
- component: {fileID: 2286933990687286586}
|
- component: {fileID: 2898082274380822897}
|
||||||
m_Layer: 6
|
m_Layer: 6
|
||||||
m_Name: pickaxe01
|
m_Name: pickaxe01
|
||||||
m_TagString: Untagged
|
m_TagString: Untagged
|
||||||
@@ -115,24 +114,6 @@ BoxCollider:
|
|||||||
serializedVersion: 3
|
serializedVersion: 3
|
||||||
m_Size: {x: 0.92430025, y: 1.1134, z: 0.17970005}
|
m_Size: {x: 0.92430025, y: 1.1134, z: 0.17970005}
|
||||||
m_Center: {x: 0.004150004, y: 0.31980002, z: 0.0030500002}
|
m_Center: {x: 0.004150004, y: 0.31980002, z: 0.0030500002}
|
||||||
--- !u!114 &1867436537062894882
|
|
||||||
MonoBehaviour:
|
|
||||||
m_ObjectHideFlags: 0
|
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
|
||||||
m_PrefabInstance: {fileID: 0}
|
|
||||||
m_PrefabAsset: {fileID: 0}
|
|
||||||
m_GameObject: {fileID: 1392316285209212}
|
|
||||||
m_Enabled: 1
|
|
||||||
m_EditorHideFlags: 0
|
|
||||||
m_Script: {fileID: 11500000, guid: 7c57b699079965047b70cd1bad8f3554, type: 3}
|
|
||||||
m_Name:
|
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::ItemPickup
|
|
||||||
_componentIndexCache: 0
|
|
||||||
_addedNetworkObject: {fileID: 2286933990687286586}
|
|
||||||
_networkObjectCache: {fileID: 2286933990687286586}
|
|
||||||
itemData: {fileID: 11400000, guid: 60132cba2202496eb74f2afc1f89c81a, type: 2}
|
|
||||||
quantity: 1
|
|
||||||
interactionVerb: Pick up
|
|
||||||
--- !u!54 &3862993572698624464
|
--- !u!54 &3862993572698624464
|
||||||
Rigidbody:
|
Rigidbody:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
@@ -160,7 +141,7 @@ Rigidbody:
|
|||||||
m_Interpolate: 0
|
m_Interpolate: 0
|
||||||
m_Constraints: 0
|
m_Constraints: 0
|
||||||
m_CollisionDetection: 0
|
m_CollisionDetection: 0
|
||||||
--- !u!114 &2286933990687286586
|
--- !u!114 &2898082274380822897
|
||||||
MonoBehaviour:
|
MonoBehaviour:
|
||||||
m_ObjectHideFlags: 0
|
m_ObjectHideFlags: 0
|
||||||
m_CorrespondingSourceObject: {fileID: 0}
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
@@ -169,52 +150,10 @@ MonoBehaviour:
|
|||||||
m_GameObject: {fileID: 1392316285209212}
|
m_GameObject: {fileID: 1392316285209212}
|
||||||
m_Enabled: 1
|
m_Enabled: 1
|
||||||
m_EditorHideFlags: 0
|
m_EditorHideFlags: 0
|
||||||
m_Script: {fileID: 11500000, guid: 26b716c41e9b56b4baafaf13a523ba2e, type: 3}
|
m_Script: {fileID: 11500000, guid: eed740dea8aa59b4cb516f25ce139314, type: 3}
|
||||||
m_Name:
|
m_Name:
|
||||||
m_EditorClassIdentifier: FishNet.Runtime::FishNet.Object.NetworkObject
|
m_EditorClassIdentifier: Assembly-CSharp::Ashwild.Network.Pickable
|
||||||
<IsNested>k__BackingField: 0
|
id: -1
|
||||||
WasActiveDuringEdit: 0
|
itemData: {fileID: 11400000, guid: 60132cba2202496eb74f2afc1f89c81a, type: 2}
|
||||||
WasActiveDuringEdit_Set1: 1
|
quantity: 1
|
||||||
<ComponentIndex>k__BackingField: 0
|
interactionVerb: Pick up
|
||||||
<PredictedSpawn>k__BackingField: {fileID: 0}
|
|
||||||
<PredictedOwner>k__BackingField: {fileID: 0}
|
|
||||||
NetworkBehaviours:
|
|
||||||
- {fileID: 1867436537062894882}
|
|
||||||
InitializedParentNetworkBehaviour: {fileID: 0}
|
|
||||||
InitializedNestedNetworkObjects: []
|
|
||||||
RuntimeParentNetworkBehaviour: {fileID: 0}
|
|
||||||
RuntimeChildNetworkBehaviours: []
|
|
||||||
_isNetworked: 1
|
|
||||||
_isSpawnable: 1
|
|
||||||
_isGlobal: 0
|
|
||||||
_initializeOrder: 0
|
|
||||||
_preventDespawnOnDisconnect: 0
|
|
||||||
_defaultDespawnType: 0
|
|
||||||
_initializedTimestamp: -8584197526105594093
|
|
||||||
_localLevelOfDetailCalculationType: 0
|
|
||||||
_useLevelOfDetail: 0
|
|
||||||
_useRootLevelOfDetail: 1
|
|
||||||
NetworkObserver: {fileID: 0}
|
|
||||||
_enablePrediction: 0
|
|
||||||
_predictionType: 0
|
|
||||||
_localReconcileCorrectionType: 2
|
|
||||||
_graphicalObject: {fileID: 0}
|
|
||||||
_detachGraphicalObject: 0
|
|
||||||
_enableStateForwarding: 1
|
|
||||||
_networkTransform: {fileID: 0}
|
|
||||||
_ownerInterpolation: 1
|
|
||||||
_ownerSmoothedProperties: 255
|
|
||||||
_adaptiveInterpolation: 3
|
|
||||||
_spectatorSmoothedProperties: 255
|
|
||||||
_spectatorInterpolation: 2
|
|
||||||
_enableTeleport: 0
|
|
||||||
_teleportThreshold: 1
|
|
||||||
<PrefabId>k__BackingField: 23
|
|
||||||
<SpawnableCollectionId>k__BackingField: 0
|
|
||||||
<AssetPathHash>k__BackingField: 16031416610542904637
|
|
||||||
SceneId: 0
|
|
||||||
SerializedTransformProperties:
|
|
||||||
Position: {x: 0, y: 0, z: 0}
|
|
||||||
Rotation: {x: 0, y: -0.7071068, z: 0, w: 0.7071068}
|
|
||||||
Scale: {x: 1, y: 1, z: 1}
|
|
||||||
IsValid: 1
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5ca8a708a454f4e439dbff30ed6779fa
|
||||||
|
folderAsset: yes
|
||||||
|
DefaultImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,427 @@
|
|||||||
|
/* Ashwild Database window — dark, card-based styling. Loaded by AshwildDatabaseWindow. */
|
||||||
|
|
||||||
|
.ash-root {
|
||||||
|
flex-grow: 1;
|
||||||
|
background-color: rgb(36, 37, 41);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Toolbar ─────────────────────────────────────────────── */
|
||||||
|
.ash-toolbar {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
height: 38px;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
background-color: rgb(28, 29, 33);
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
border-bottom-color: rgb(18, 19, 22);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-tab {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 26px;
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 14px;
|
||||||
|
margin-right: 4px;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: rgba(0, 0, 0, 0);
|
||||||
|
border-width: 0;
|
||||||
|
color: rgb(150, 152, 158);
|
||||||
|
-unity-font-style: bold;
|
||||||
|
-unity-text-align: middle-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-tab__icon {
|
||||||
|
width: 15px;
|
||||||
|
height: 15px;
|
||||||
|
margin-right: 6px;
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-tab:hover {
|
||||||
|
background-color: rgb(46, 47, 52);
|
||||||
|
color: rgb(220, 221, 225);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-tab--active {
|
||||||
|
background-color: rgb(64, 110, 220);
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-tab--active:hover {
|
||||||
|
background-color: rgb(74, 120, 230);
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-toolbar-spacer {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-search {
|
||||||
|
width: 180px;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Body split ──────────────────────────────────────────── */
|
||||||
|
.ash-body {
|
||||||
|
flex-direction: row;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-left {
|
||||||
|
width: 250px;
|
||||||
|
background-color: rgb(31, 32, 36);
|
||||||
|
border-right-width: 1px;
|
||||||
|
border-right-color: rgb(18, 19, 22);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-list {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-right {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── List rows ───────────────────────────────────────────── */
|
||||||
|
.ash-row {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
height: 48px;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-row__icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
margin-right: 10px;
|
||||||
|
background-color: rgb(48, 49, 54);
|
||||||
|
border-radius: 6px;
|
||||||
|
-unity-background-scale-mode: scale-to-fit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-row__label {
|
||||||
|
flex-grow: 1;
|
||||||
|
color: rgb(210, 211, 215);
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-row__tag {
|
||||||
|
color: rgb(120, 122, 128);
|
||||||
|
font-size: 10px;
|
||||||
|
-unity-font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.unity-list-view__item:hover .ash-row__label {
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Footer (New button) ─────────────────────────────────── */
|
||||||
|
.ash-footer {
|
||||||
|
padding: 6px;
|
||||||
|
border-top-width: 1px;
|
||||||
|
border-top-color: rgb(18, 19, 22);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Buttons ─────────────────────────────────────────────── */
|
||||||
|
.ash-btn {
|
||||||
|
height: 26px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border-width: 0;
|
||||||
|
background-color: rgb(60, 61, 67);
|
||||||
|
color: rgb(220, 221, 225);
|
||||||
|
padding-left: 12px;
|
||||||
|
padding-right: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-btn:hover {
|
||||||
|
background-color: rgb(72, 73, 80);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-btn--primary {
|
||||||
|
background-color: rgb(64, 110, 220);
|
||||||
|
color: rgb(255, 255, 255);
|
||||||
|
-unity-font-style: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-btn--primary:hover {
|
||||||
|
background-color: rgb(74, 120, 230);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-empty {
|
||||||
|
flex-grow: 1;
|
||||||
|
-unity-text-align: middle-center;
|
||||||
|
color: rgb(110, 112, 118);
|
||||||
|
-unity-font-style: italic;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-row__tag {
|
||||||
|
color: rgb(120, 122, 128);
|
||||||
|
font-size: 10px;
|
||||||
|
-unity-font-style: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Action bar (Ping / Delete) ──────────────────────────── */
|
||||||
|
.ash-actionbar {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Hero header ─────────────────────────────────────────── */
|
||||||
|
.ash-hero {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
background-color: rgb(43, 44, 49);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-hero__icon {
|
||||||
|
width: 72px;
|
||||||
|
height: 72px;
|
||||||
|
margin-right: 14px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: rgb(30, 31, 35);
|
||||||
|
border-width: 1px;
|
||||||
|
border-color: rgb(56, 57, 63);
|
||||||
|
-unity-background-scale-mode: scale-to-fit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-hero__icon--clickable:hover {
|
||||||
|
border-color: rgb(64, 110, 220);
|
||||||
|
background-color: rgb(36, 38, 44);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-hero__info {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-hero__name {
|
||||||
|
margin-bottom: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-hero__name > .unity-text-field__input {
|
||||||
|
font-size: 18px;
|
||||||
|
-unity-font-style: bold;
|
||||||
|
background-color: rgba(0, 0, 0, 0);
|
||||||
|
border-width: 0;
|
||||||
|
border-bottom-width: 1px;
|
||||||
|
border-bottom-color: rgb(60, 61, 67);
|
||||||
|
padding-left: 0;
|
||||||
|
color: rgb(238, 239, 242);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-hero__typerow {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-hero__type {
|
||||||
|
width: 150px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-hero__badge {
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-badge {
|
||||||
|
-unity-font-style: bold;
|
||||||
|
font-size: 10px;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
padding-top: 3px;
|
||||||
|
padding-bottom: 3px;
|
||||||
|
border-radius: 10px;
|
||||||
|
-unity-text-align: middle-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Cards ───────────────────────────────────────────────── */
|
||||||
|
.ash-card {
|
||||||
|
background-color: rgb(43, 44, 49);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-card--accent {
|
||||||
|
border-left-width: 3px;
|
||||||
|
border-left-color: rgb(64, 110, 220);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-card__title {
|
||||||
|
-unity-font-style: bold;
|
||||||
|
font-size: 12px;
|
||||||
|
color: rgb(150, 175, 240);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-description .unity-text-field__input {
|
||||||
|
min-height: 48px;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tidy two-column label/field rows inside cards */
|
||||||
|
.ash-card .unity-base-field__label {
|
||||||
|
min-width: 130px;
|
||||||
|
color: rgb(170, 172, 178);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-buttons {
|
||||||
|
flex-direction: row;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-buttons .ash-btn {
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Recipe equation strip ───────────────────────────────── */
|
||||||
|
.ash-equation {
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
background-color: rgb(31, 32, 36);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-equation__op {
|
||||||
|
font-size: 18px;
|
||||||
|
-unity-font-style: bold;
|
||||||
|
color: rgb(120, 122, 130);
|
||||||
|
margin-left: 6px;
|
||||||
|
margin-right: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Recipe ingredient / result cards ────────────────────── */
|
||||||
|
.ash-rchip {
|
||||||
|
width: 116px;
|
||||||
|
min-height: 124px;
|
||||||
|
align-items: center;
|
||||||
|
background-color: rgb(46, 47, 53);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding-top: 12px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
border-width: 1px;
|
||||||
|
border-color: rgb(56, 57, 63);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-rchip--result {
|
||||||
|
background-color: rgb(40, 50, 44);
|
||||||
|
border-color: rgb(64, 110, 80);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-rchip__icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background-color: rgb(30, 31, 35);
|
||||||
|
border-width: 1px;
|
||||||
|
border-color: rgb(58, 59, 65);
|
||||||
|
-unity-background-scale-mode: scale-to-fit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-rchip__icon:hover {
|
||||||
|
border-color: rgb(64, 110, 220);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-rchip__name {
|
||||||
|
color: rgb(214, 215, 220);
|
||||||
|
font-size: 11px;
|
||||||
|
-unity-font-style: bold;
|
||||||
|
-unity-text-align: middle-center;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
max-width: 100px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-rchip__stepper {
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-rchip__step {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
border-radius: 5px;
|
||||||
|
border-width: 0;
|
||||||
|
background-color: rgb(60, 61, 67);
|
||||||
|
color: rgb(225, 226, 230);
|
||||||
|
font-size: 14px;
|
||||||
|
-unity-font-style: bold;
|
||||||
|
-unity-text-align: middle-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-rchip__step:hover {
|
||||||
|
background-color: rgb(72, 73, 80);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-rchip__qty {
|
||||||
|
width: 34px;
|
||||||
|
color: rgb(238, 239, 242);
|
||||||
|
-unity-font-style: bold;
|
||||||
|
-unity-text-align: middle-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-rchip__remove {
|
||||||
|
position: absolute;
|
||||||
|
top: 5px;
|
||||||
|
right: 5px;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-right: 0;
|
||||||
|
border-radius: 9px;
|
||||||
|
border-width: 0;
|
||||||
|
background-color: rgba(0, 0, 0, 0);
|
||||||
|
color: rgb(130, 110, 112);
|
||||||
|
font-size: 11px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-rchip__remove:hover {
|
||||||
|
background-color: rgb(110, 56, 58);
|
||||||
|
color: rgb(255, 210, 210);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dashed "add ingredient" tile */
|
||||||
|
.ash-rchip--add {
|
||||||
|
justify-content: center;
|
||||||
|
background-color: rgba(64, 110, 220, 0.07);
|
||||||
|
border-color: rgb(74, 90, 130);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-rchip--add:hover {
|
||||||
|
background-color: rgba(64, 110, 220, 0.16);
|
||||||
|
border-color: rgb(96, 130, 210);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ash-rchip__plus {
|
||||||
|
font-size: 30px;
|
||||||
|
-unity-font-style: bold;
|
||||||
|
color: rgb(120, 150, 220);
|
||||||
|
-unity-text-align: middle-center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Recipe name field (styled like an item hero name) ───── */
|
||||||
|
.ash-recipe-name {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: ae9520351e53151458d972c6a63ae1e6
|
||||||
|
ScriptedImporter:
|
||||||
|
internalIDToNameTable: []
|
||||||
|
externalObjects: {}
|
||||||
|
serializedVersion: 2
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
script: {fileID: 12385, guid: 0000000000000000e000000000000000, type: 0}
|
||||||
|
disableValidation: 0
|
||||||
|
unsupportedSelectorAction: 0
|
||||||
@@ -0,0 +1,463 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.UIElements;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
using Ashwild.Crafting;
|
||||||
|
using Ashwild.Inventory;
|
||||||
|
|
||||||
|
namespace Ashwild.EditorTools
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The Ashwild Database — a UI Toolkit window for browsing, creating and editing the project's
|
||||||
|
/// authored ScriptableObjects (ItemData and CraftingRecipe). The left pane lists every asset of
|
||||||
|
/// the active tab; the right pane edits the selection through a dedicated custom view
|
||||||
|
/// (ItemEditorView / RecipeEditorView), and for items offers one-click generation of the world
|
||||||
|
/// pickup and in-hand prefabs around a chosen source object (see ItemAssetFactory). Pure UI:
|
||||||
|
/// all asset/prefab work is delegated to the factories and views.
|
||||||
|
/// </summary>
|
||||||
|
public class AshwildDatabaseWindow : EditorWindow
|
||||||
|
{
|
||||||
|
#region Types
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Which asset family the left list currently shows.
|
||||||
|
/// </summary>
|
||||||
|
private enum Tab { Items, Recipes }
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Constants
|
||||||
|
|
||||||
|
private const string UssPath = "Assets/GAME/Script/Editor/Database/AshwildDatabase.uss";
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region State
|
||||||
|
|
||||||
|
private Tab activeTab = Tab.Items;
|
||||||
|
private string searchFilter = string.Empty;
|
||||||
|
|
||||||
|
private readonly List<Object> sourceItems = new List<Object>();
|
||||||
|
private Object selectedAsset;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region UI References
|
||||||
|
|
||||||
|
private ListView listView;
|
||||||
|
private VisualElement rightPane;
|
||||||
|
private Button itemsTab;
|
||||||
|
private Button recipesTab;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Menu
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens (or focuses) the database window.
|
||||||
|
/// </summary>
|
||||||
|
[MenuItem("Tools/Ashwild/Database")]
|
||||||
|
public static void Open()
|
||||||
|
{
|
||||||
|
AshwildDatabaseWindow window = GetWindow<AshwildDatabaseWindow>();
|
||||||
|
window.titleContent = new GUIContent("Ashwild Database");
|
||||||
|
window.minSize = new Vector2(720, 420);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Window Lifecycle
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the full window tree once when the window opens.
|
||||||
|
/// </summary>
|
||||||
|
private void CreateGUI()
|
||||||
|
{
|
||||||
|
VisualElement root = rootVisualElement;
|
||||||
|
root.AddToClassList("ash-root");
|
||||||
|
|
||||||
|
StyleSheet sheet = AssetDatabase.LoadAssetAtPath<StyleSheet>(UssPath);
|
||||||
|
if (sheet != null) root.styleSheets.Add(sheet);
|
||||||
|
|
||||||
|
root.Add(BuildToolbar());
|
||||||
|
|
||||||
|
VisualElement body = new VisualElement();
|
||||||
|
body.AddToClassList("ash-body");
|
||||||
|
body.Add(BuildLeftPane());
|
||||||
|
body.Add(BuildRightPane());
|
||||||
|
root.Add(body);
|
||||||
|
|
||||||
|
RefreshList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rebuilds the list when assets change outside the window (created/deleted/renamed elsewhere).
|
||||||
|
/// </summary>
|
||||||
|
private void OnProjectChange()
|
||||||
|
{
|
||||||
|
if (listView != null) RefreshList();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Toolbar
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the top toolbar: the two family tabs, a search field, and the database rebuild action.
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildToolbar()
|
||||||
|
{
|
||||||
|
VisualElement toolbar = new VisualElement();
|
||||||
|
toolbar.AddToClassList("ash-toolbar");
|
||||||
|
|
||||||
|
itemsTab = MakeTab("Items", Tab.Items, "d_Prefab Icon");
|
||||||
|
recipesTab = MakeTab("Recipes", Tab.Recipes, "d_ScriptableObject Icon");
|
||||||
|
toolbar.Add(itemsTab);
|
||||||
|
toolbar.Add(recipesTab);
|
||||||
|
|
||||||
|
VisualElement spacer = new VisualElement();
|
||||||
|
spacer.AddToClassList("ash-toolbar-spacer");
|
||||||
|
toolbar.Add(spacer);
|
||||||
|
|
||||||
|
ToolbarSearchField search = new ToolbarSearchField();
|
||||||
|
search.AddToClassList("ash-search");
|
||||||
|
search.RegisterValueChangedCallback(evt =>
|
||||||
|
{
|
||||||
|
searchFilter = evt.newValue ?? string.Empty;
|
||||||
|
RefreshList();
|
||||||
|
});
|
||||||
|
toolbar.Add(search);
|
||||||
|
|
||||||
|
Button rebuild = new Button(ItemDatabaseBuilder.Rebuild) { text = "Rebuild DB" };
|
||||||
|
rebuild.AddToClassList("ash-btn");
|
||||||
|
rebuild.tooltip = "Re-scan every ItemData and rewrite the network ItemDatabase.";
|
||||||
|
toolbar.Add(rebuild);
|
||||||
|
|
||||||
|
return toolbar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates one toolbar tab button (icon + label) that switches the active family when clicked.
|
||||||
|
/// The icon is a built-in editor icon resolved by name; it is simply skipped if not found.
|
||||||
|
/// </summary>
|
||||||
|
private Button MakeTab(string label, Tab tab, string iconName)
|
||||||
|
{
|
||||||
|
Button button = new Button(() => SetTab(tab));
|
||||||
|
button.AddToClassList("ash-tab");
|
||||||
|
if (tab == activeTab) button.AddToClassList("ash-tab--active");
|
||||||
|
|
||||||
|
Texture icon = EditorGUIUtility.IconContent(iconName).image;
|
||||||
|
if (icon != null)
|
||||||
|
{
|
||||||
|
Image image = new Image { image = icon, scaleMode = ScaleMode.ScaleToFit };
|
||||||
|
image.AddToClassList("ash-tab__icon");
|
||||||
|
button.Add(image);
|
||||||
|
}
|
||||||
|
|
||||||
|
button.Add(new Label(label));
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Switches the active family, repaints the tab highlight, clears the selection and reloads.
|
||||||
|
/// </summary>
|
||||||
|
private void SetTab(Tab tab)
|
||||||
|
{
|
||||||
|
if (tab == activeTab) return;
|
||||||
|
activeTab = tab;
|
||||||
|
|
||||||
|
itemsTab.EnableInClassList("ash-tab--active", tab == Tab.Items);
|
||||||
|
recipesTab.EnableInClassList("ash-tab--active", tab == Tab.Recipes);
|
||||||
|
|
||||||
|
selectedAsset = null;
|
||||||
|
RefreshList();
|
||||||
|
ShowSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Left Pane
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the left pane: a styled list of the active family plus a footer "New" button.
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildLeftPane()
|
||||||
|
{
|
||||||
|
VisualElement left = new VisualElement();
|
||||||
|
left.AddToClassList("ash-left");
|
||||||
|
|
||||||
|
listView = new ListView(sourceItems)
|
||||||
|
{
|
||||||
|
fixedItemHeight = 48,
|
||||||
|
selectionType = SelectionType.Single,
|
||||||
|
makeItem = MakeRow,
|
||||||
|
bindItem = BindRow
|
||||||
|
};
|
||||||
|
listView.AddToClassList("ash-list");
|
||||||
|
listView.selectionChanged += OnListSelectionChanged;
|
||||||
|
left.Add(listView);
|
||||||
|
|
||||||
|
VisualElement footer = new VisualElement();
|
||||||
|
footer.AddToClassList("ash-footer");
|
||||||
|
Button newButton = new Button(CreateNewAsset) { text = "+ New" };
|
||||||
|
newButton.AddToClassList("ash-btn");
|
||||||
|
newButton.AddToClassList("ash-btn--primary");
|
||||||
|
footer.Add(newButton);
|
||||||
|
left.Add(footer);
|
||||||
|
|
||||||
|
return left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the reusable visual for a single list row (icon, name, type tag).
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement MakeRow()
|
||||||
|
{
|
||||||
|
VisualElement row = new VisualElement();
|
||||||
|
row.AddToClassList("ash-row");
|
||||||
|
|
||||||
|
VisualElement icon = new VisualElement { name = "icon" };
|
||||||
|
icon.AddToClassList("ash-row__icon");
|
||||||
|
row.Add(icon);
|
||||||
|
|
||||||
|
Label label = new Label { name = "label" };
|
||||||
|
label.AddToClassList("ash-row__label");
|
||||||
|
row.Add(label);
|
||||||
|
|
||||||
|
Label tag = new Label { name = "tag" };
|
||||||
|
tag.AddToClassList("ash-row__tag");
|
||||||
|
row.Add(tag);
|
||||||
|
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fills a row visual from the asset at the given index (icon from the item/result sprite).
|
||||||
|
/// </summary>
|
||||||
|
private void BindRow(VisualElement element, int index)
|
||||||
|
{
|
||||||
|
Object asset = sourceItems[index];
|
||||||
|
element.Q<Label>("label").text = AssetDisplayName(asset);
|
||||||
|
|
||||||
|
Label tag = element.Q<Label>("tag");
|
||||||
|
tag.text = AssetTag(asset);
|
||||||
|
tag.style.color = asset is ItemData item
|
||||||
|
? AshwildUI.TypeColor(item.ItemType)
|
||||||
|
: new Color(0.5f, 0.52f, 0.58f);
|
||||||
|
|
||||||
|
VisualElement icon = element.Q<VisualElement>("icon");
|
||||||
|
Sprite sprite = AssetIcon(asset);
|
||||||
|
icon.style.backgroundImage = sprite != null ? new StyleBackground(sprite) : new StyleBackground();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stores the clicked asset and shows it in the right pane.
|
||||||
|
/// </summary>
|
||||||
|
private void OnListSelectionChanged(IEnumerable<object> selection)
|
||||||
|
{
|
||||||
|
selectedAsset = null;
|
||||||
|
foreach (object o in selection)
|
||||||
|
{
|
||||||
|
selectedAsset = o as Object;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ShowSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Right Pane
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the (initially empty) right pane container the selection editor is rendered into.
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildRightPane()
|
||||||
|
{
|
||||||
|
rightPane = new ScrollView();
|
||||||
|
rightPane.AddToClassList("ash-right");
|
||||||
|
return rightPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Renders the editor for the current selection: a slim action bar plus the custom item or
|
||||||
|
/// recipe view. Shows a placeholder when nothing is selected.
|
||||||
|
/// </summary>
|
||||||
|
private void ShowSelection()
|
||||||
|
{
|
||||||
|
if (rightPane == null) return;
|
||||||
|
rightPane.Clear();
|
||||||
|
|
||||||
|
if (selectedAsset == null)
|
||||||
|
{
|
||||||
|
Label empty = new Label("Select an asset on the left, or create a new one.");
|
||||||
|
empty.AddToClassList("ash-empty");
|
||||||
|
rightPane.Add(empty);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rightPane.Add(BuildActionBar());
|
||||||
|
|
||||||
|
if (selectedAsset is ItemData item)
|
||||||
|
rightPane.Add(new ItemEditorView(item, RefreshRow, ShowSelection).Root);
|
||||||
|
else if (selectedAsset is CraftingRecipe recipe)
|
||||||
|
rightPane.Add(new RecipeEditorView(recipe, RefreshRow).Root);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the slim action bar above the editor: a "Ping" locator and a delete action, aligned
|
||||||
|
/// to the right (the hero inside each view owns the title).
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildActionBar()
|
||||||
|
{
|
||||||
|
VisualElement bar = new VisualElement();
|
||||||
|
bar.AddToClassList("ash-actionbar");
|
||||||
|
|
||||||
|
VisualElement spacer = new VisualElement();
|
||||||
|
spacer.AddToClassList("ash-toolbar-spacer");
|
||||||
|
bar.Add(spacer);
|
||||||
|
|
||||||
|
Button ping = new Button(() => EditorGUIUtility.PingObject(selectedAsset)) { text = "Ping" };
|
||||||
|
ping.AddToClassList("ash-btn");
|
||||||
|
bar.Add(ping);
|
||||||
|
|
||||||
|
Button delete = new Button(DeleteSelected) { text = "Delete" };
|
||||||
|
delete.AddToClassList("ash-btn");
|
||||||
|
bar.Add(delete);
|
||||||
|
|
||||||
|
return bar;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Refreshes just the left-list rows so a renamed/retyped/re-iconed asset updates live without
|
||||||
|
/// rebuilding the whole window or stealing focus from the field being edited.
|
||||||
|
/// </summary>
|
||||||
|
private void RefreshRow() => listView.RefreshItems();
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Actions
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new asset of the active family, registers it, reloads the list, and selects it.
|
||||||
|
/// </summary>
|
||||||
|
private void CreateNewAsset()
|
||||||
|
{
|
||||||
|
Object created = activeTab == Tab.Items
|
||||||
|
? ItemAssetFactory.CreateItem("NewItem", ItemType.Material)
|
||||||
|
: (Object)RecipeAssetFactory.CreateRecipe("NewRecipe", null);
|
||||||
|
|
||||||
|
if (created == null) return;
|
||||||
|
|
||||||
|
RefreshList();
|
||||||
|
SelectAsset(created);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Deletes the selected asset after confirmation, then reloads (rebuilding the item database
|
||||||
|
/// when an item was removed so network ids stay correct).
|
||||||
|
/// </summary>
|
||||||
|
private void DeleteSelected()
|
||||||
|
{
|
||||||
|
if (selectedAsset == null) return;
|
||||||
|
|
||||||
|
string path = AssetDatabase.GetAssetPath(selectedAsset);
|
||||||
|
if (!EditorUtility.DisplayDialog("Delete asset", $"Delete '{selectedAsset.name}'?\n{path}", "Delete", "Cancel"))
|
||||||
|
return;
|
||||||
|
|
||||||
|
bool wasItem = selectedAsset is ItemData;
|
||||||
|
AssetDatabase.DeleteAsset(path);
|
||||||
|
selectedAsset = null;
|
||||||
|
|
||||||
|
if (wasItem) ItemDatabaseBuilder.Rebuild();
|
||||||
|
RefreshList();
|
||||||
|
ShowSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Data
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reloads the source list for the active family, applying the search filter and an alphabetical
|
||||||
|
/// sort, then refreshes the list view and restores the selection highlight if still present.
|
||||||
|
/// </summary>
|
||||||
|
private void RefreshList()
|
||||||
|
{
|
||||||
|
sourceItems.Clear();
|
||||||
|
|
||||||
|
string typeFilter = activeTab == Tab.Items ? "t:ItemData" : "t:CraftingRecipe";
|
||||||
|
string[] guids = AssetDatabase.FindAssets(typeFilter);
|
||||||
|
foreach (string guid in guids)
|
||||||
|
{
|
||||||
|
string path = AssetDatabase.GUIDToAssetPath(guid);
|
||||||
|
Object asset = AssetDatabase.LoadAssetAtPath<Object>(path);
|
||||||
|
if (asset == null) continue;
|
||||||
|
if (!string.IsNullOrEmpty(searchFilter)
|
||||||
|
&& AssetDisplayName(asset).IndexOf(searchFilter, System.StringComparison.OrdinalIgnoreCase) < 0)
|
||||||
|
continue;
|
||||||
|
sourceItems.Add(asset);
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceItems.Sort((a, b) => string.CompareOrdinal(AssetDisplayName(a), AssetDisplayName(b)));
|
||||||
|
|
||||||
|
listView.itemsSource = sourceItems;
|
||||||
|
listView.RefreshItems();
|
||||||
|
|
||||||
|
int index = selectedAsset != null ? sourceItems.IndexOf(selectedAsset) : -1;
|
||||||
|
if (index >= 0) listView.SetSelectionWithoutNotify(new[] { index });
|
||||||
|
else listView.ClearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reloads, selects an asset by reference and scrolls it into view.
|
||||||
|
/// </summary>
|
||||||
|
private void SelectAsset(Object asset)
|
||||||
|
{
|
||||||
|
selectedAsset = asset;
|
||||||
|
int index = sourceItems.IndexOf(asset);
|
||||||
|
if (index >= 0)
|
||||||
|
{
|
||||||
|
listView.SetSelection(index);
|
||||||
|
listView.ScrollToItem(index);
|
||||||
|
}
|
||||||
|
ShowSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Asset Display Helpers
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The human label for an asset: its authored name when set, otherwise the asset file name.
|
||||||
|
/// </summary>
|
||||||
|
private static string AssetDisplayName(Object asset)
|
||||||
|
{
|
||||||
|
if (asset is ItemData item && !string.IsNullOrWhiteSpace(item.ItemName)) return item.ItemName;
|
||||||
|
if (asset is CraftingRecipe recipe && !string.IsNullOrWhiteSpace(recipe.RecipeName)) return recipe.RecipeName;
|
||||||
|
return asset != null ? asset.name : string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The short type tag shown on the right of a list row (item type, or "Recipe").
|
||||||
|
/// </summary>
|
||||||
|
private static string AssetTag(Object asset)
|
||||||
|
{
|
||||||
|
if (asset is ItemData item) return item.ItemType.ToString();
|
||||||
|
if (asset is CraftingRecipe) return "Recipe";
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The preview sprite for a row: the item icon, or the recipe result's icon.
|
||||||
|
/// </summary>
|
||||||
|
private static Sprite AssetIcon(Object asset)
|
||||||
|
{
|
||||||
|
if (asset is ItemData item) return item.Icon;
|
||||||
|
if (asset is CraftingRecipe recipe) return recipe.Icon;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 5772e226e41ece54e952b4eb79fbd8c0
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
using Ashwild.Inventory;
|
||||||
|
|
||||||
|
namespace Ashwild.EditorTools
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Small library of styled VisualElement builders shared by the Ashwild Database editor views
|
||||||
|
/// (cards, type badges, accent colours). Centralised so the item and recipe editors stay visually
|
||||||
|
/// consistent and free of inline-style duplication.
|
||||||
|
/// </summary>
|
||||||
|
public static class AshwildUI
|
||||||
|
{
|
||||||
|
#region Type Colours
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The accent colour that identifies an item type across the list badges, hero and cards.
|
||||||
|
/// </summary>
|
||||||
|
public static Color TypeColor(ItemType type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case ItemType.Tool: return new Color(0.93f, 0.58f, 0.22f);
|
||||||
|
case ItemType.Weapon: return new Color(0.88f, 0.33f, 0.33f);
|
||||||
|
case ItemType.Consumable: return new Color(0.42f, 0.76f, 0.47f);
|
||||||
|
default: return new Color(0.47f, 0.57f, 0.69f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Builders
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds a titled card container; callers add their fields straight onto the returned element,
|
||||||
|
/// below its heading.
|
||||||
|
/// </summary>
|
||||||
|
public static VisualElement Card(string title)
|
||||||
|
{
|
||||||
|
VisualElement card = new VisualElement();
|
||||||
|
card.AddToClassList("ash-card");
|
||||||
|
|
||||||
|
Label heading = new Label(title);
|
||||||
|
heading.AddToClassList("ash-card__title");
|
||||||
|
card.Add(heading);
|
||||||
|
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds a small rounded badge tinted by a colour — used for item-type tags. The background is
|
||||||
|
/// a translucent wash of the colour with the text in the solid colour for legibility on dark.
|
||||||
|
/// </summary>
|
||||||
|
public static VisualElement Badge(string text, Color color)
|
||||||
|
{
|
||||||
|
Label badge = new Label(text);
|
||||||
|
badge.AddToClassList("ash-badge");
|
||||||
|
badge.style.color = color;
|
||||||
|
badge.style.backgroundColor = new Color(color.r, color.g, color.b, 0.16f);
|
||||||
|
return badge;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 25df569ab81300a4eb80ae674f0f18c2
|
||||||
@@ -0,0 +1,242 @@
|
|||||||
|
using System.IO;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using Ashwild.Inventory;
|
||||||
|
using Ashwild.Network;
|
||||||
|
using Ashwild.Player;
|
||||||
|
|
||||||
|
namespace Ashwild.EditorTools
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Editor-only factory that creates ItemData assets and, on demand, builds the world pickup
|
||||||
|
/// prefab (Pickable + collider on the Pickable layer) and the in-hand prefab (ToolBehaviour +
|
||||||
|
/// HeldItemOffset on the Tools layer) by wrapping a chosen source object — a model (FBX) or an
|
||||||
|
/// existing prefab. The source is nested as a child so the designer's authored model stays intact
|
||||||
|
/// and editable, while the generated root carries the gameplay components and a collider auto-fit
|
||||||
|
/// to the source's renderers. Every reference is wired back onto the ItemData. Follows §3 of the
|
||||||
|
/// project rules: world pickups are plain WorldObjects (registry-synced), never NetworkObjects.
|
||||||
|
/// </summary>
|
||||||
|
public static class ItemAssetFactory
|
||||||
|
{
|
||||||
|
#region Constants
|
||||||
|
|
||||||
|
private const string ItemsFolder = "Assets/GAME/ScriptableObjects/Items";
|
||||||
|
private const string WorldPrefabFolder = "Assets/GAME/Prefabs/Pickable";
|
||||||
|
private const string HandPrefabFolder = "Assets/GAME/Prefabs/Tools";
|
||||||
|
|
||||||
|
private const string PickableLayerName = "Pickable";
|
||||||
|
private const string ToolsLayerName = "Tools";
|
||||||
|
private const string HarvestableLayerName = "Harvestable";
|
||||||
|
|
||||||
|
private const float FallbackColliderSize = 0.3f;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Item Asset
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a fresh ItemData asset under the items folder with a unique name, seeds its
|
||||||
|
/// display name and type, registers it in the network ItemDatabase, and returns it so the
|
||||||
|
/// caller can select and edit it immediately. Returns null only if the folder cannot be made.
|
||||||
|
/// </summary>
|
||||||
|
public static ItemData CreateItem(string desiredName, ItemType itemType)
|
||||||
|
{
|
||||||
|
if (!EnsureFolder(ItemsFolder)) return null;
|
||||||
|
|
||||||
|
string safeName = string.IsNullOrWhiteSpace(desiredName) ? "NewItem" : desiredName.Trim();
|
||||||
|
string assetPath = AssetDatabase.GenerateUniqueAssetPath($"{ItemsFolder}/{safeName}.asset");
|
||||||
|
|
||||||
|
ItemData item = ScriptableObject.CreateInstance<ItemData>();
|
||||||
|
AssetDatabase.CreateAsset(item, assetPath);
|
||||||
|
|
||||||
|
SerializedObject so = new SerializedObject(item);
|
||||||
|
so.FindProperty("itemName").stringValue = safeName;
|
||||||
|
so.FindProperty("itemType").enumValueIndex = (int)itemType;
|
||||||
|
so.ApplyModifiedPropertiesWithoutUndo();
|
||||||
|
|
||||||
|
EditorUtility.SetDirty(item);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
ItemDatabaseBuilder.Rebuild();
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region World Prefab
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the world pickup prefab for an item by wrapping the source object: a root on the
|
||||||
|
/// Pickable layer carries the source as a child, an auto-fitted non-trigger BoxCollider (so the
|
||||||
|
/// interactor ray — which reads the Pickable off the hit collider's own GameObject — has a
|
||||||
|
/// target on the root), and the Pickable component wired to this item. The id stays -1; scene
|
||||||
|
/// instances get a baked id later via Tools ▸ Ashwild ▸ Assign World Object IDs. The finished
|
||||||
|
/// prefab is assigned back onto ItemData.worldPrefab. Returns the saved prefab, or null on error.
|
||||||
|
/// </summary>
|
||||||
|
public static GameObject GenerateWorldPrefab(ItemData item, GameObject source)
|
||||||
|
{
|
||||||
|
if (!ValidateInputs(item, source, "world")) return null;
|
||||||
|
if (!EnsureFolder(WorldPrefabFolder)) return null;
|
||||||
|
|
||||||
|
GameObject root = BuildRoot(item, source, ResolveLayer(PickableLayerName));
|
||||||
|
FitBoxCollider(root);
|
||||||
|
|
||||||
|
Pickable pickable = root.AddComponent<Pickable>();
|
||||||
|
SerializedObject so = new SerializedObject(pickable);
|
||||||
|
so.FindProperty("itemData").objectReferenceValue = item;
|
||||||
|
so.ApplyModifiedPropertiesWithoutUndo();
|
||||||
|
|
||||||
|
return SaveAndAssign(root, WorldPrefabFolder, item, "worldPrefab");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Hand Prefab
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the in-hand prefab for a tool/weapon by wrapping the source object: a root on the
|
||||||
|
/// Tools layer carries the source as a child, a ToolBehaviour (harvest ray masked to the
|
||||||
|
/// Harvestable layer) and a HeldItemOffset so it can be posed in the holder. No collider —
|
||||||
|
/// held items carry no physics. The prefab is assigned back onto ItemData.handPrefab.
|
||||||
|
/// Returns the prefab, or null on error.
|
||||||
|
/// </summary>
|
||||||
|
public static GameObject GenerateHandPrefab(ItemData item, GameObject source)
|
||||||
|
{
|
||||||
|
if (!ValidateInputs(item, source, "hand")) return null;
|
||||||
|
if (!EnsureFolder(HandPrefabFolder)) return null;
|
||||||
|
|
||||||
|
GameObject root = BuildRoot(item, source, ResolveLayer(ToolsLayerName));
|
||||||
|
|
||||||
|
ToolBehaviour tool = root.AddComponent<ToolBehaviour>();
|
||||||
|
SerializedObject so = new SerializedObject(tool);
|
||||||
|
so.FindProperty("harvestMask").intValue = 1 << ResolveLayer(HarvestableLayerName);
|
||||||
|
so.ApplyModifiedPropertiesWithoutUndo();
|
||||||
|
|
||||||
|
root.AddComponent<HeldItemOffset>();
|
||||||
|
|
||||||
|
return SaveAndAssign(root, HandPrefabFolder, item, "handPrefab");
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Internal Helpers
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Guards the shared preconditions for prefab generation, logging a clear reason on failure.
|
||||||
|
/// </summary>
|
||||||
|
private static bool ValidateInputs(ItemData item, GameObject source, string kind)
|
||||||
|
{
|
||||||
|
if (item == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[ItemAssetFactory] Cannot build {kind} prefab — no ItemData selected.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[ItemAssetFactory] Cannot build {kind} prefab for '{item.name}' — no source object chosen.", item);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the generated root named after the item, on the given layer, with the source object
|
||||||
|
/// (model or prefab) nested as a child at the origin so its authored transform is preserved as
|
||||||
|
/// a prefab link rather than flattened.
|
||||||
|
/// </summary>
|
||||||
|
private static GameObject BuildRoot(ItemData item, GameObject source, int layer)
|
||||||
|
{
|
||||||
|
GameObject root = new GameObject(item.name) { layer = layer };
|
||||||
|
|
||||||
|
GameObject visual = (GameObject)PrefabUtility.InstantiatePrefab(source);
|
||||||
|
if (visual == null) visual = Object.Instantiate(source);
|
||||||
|
visual.transform.SetParent(root.transform, false);
|
||||||
|
visual.transform.localPosition = Vector3.zero;
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a non-trigger BoxCollider to the root sized to the combined bounds of the source's
|
||||||
|
/// renderers (in root-local space), falling back to a small default cube when the source has
|
||||||
|
/// no renderers so the pickup is always interactable.
|
||||||
|
/// </summary>
|
||||||
|
private static void FitBoxCollider(GameObject root)
|
||||||
|
{
|
||||||
|
BoxCollider box = root.AddComponent<BoxCollider>();
|
||||||
|
|
||||||
|
Renderer[] renderers = root.GetComponentsInChildren<Renderer>();
|
||||||
|
if (renderers.Length == 0)
|
||||||
|
{
|
||||||
|
box.size = Vector3.one * FallbackColliderSize;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Bounds bounds = renderers[0].bounds;
|
||||||
|
for (int i = 1; i < renderers.Length; i++) bounds.Encapsulate(renderers[i].bounds);
|
||||||
|
|
||||||
|
box.center = root.transform.InverseTransformPoint(bounds.center);
|
||||||
|
box.size = bounds.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves a built GameObject as a prefab under a unique path, destroys the scene instance,
|
||||||
|
/// wires the saved prefab onto the given ItemData field, and refreshes the asset database.
|
||||||
|
/// </summary>
|
||||||
|
private static GameObject SaveAndAssign(GameObject root, string folder, ItemData item, string itemField)
|
||||||
|
{
|
||||||
|
string prefabPath = AssetDatabase.GenerateUniqueAssetPath($"{folder}/{item.name}.prefab");
|
||||||
|
GameObject prefab = PrefabUtility.SaveAsPrefabAsset(root, prefabPath);
|
||||||
|
Object.DestroyImmediate(root);
|
||||||
|
|
||||||
|
if (prefab == null)
|
||||||
|
{
|
||||||
|
Debug.LogError($"[ItemAssetFactory] Failed to save prefab at '{prefabPath}'.", item);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializedObject so = new SerializedObject(item);
|
||||||
|
so.FindProperty(itemField).objectReferenceValue = prefab;
|
||||||
|
so.ApplyModifiedPropertiesWithoutUndo();
|
||||||
|
EditorUtility.SetDirty(item);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
|
||||||
|
Debug.Log($"[ItemAssetFactory] Built '{prefabPath}' and assigned it to {item.name}.{itemField}.", prefab);
|
||||||
|
return prefab;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Resolves a layer index by name, falling back to the Default layer (0) with a warning when
|
||||||
|
/// the project is missing the expected layer so generation never silently lands on a wrong one.
|
||||||
|
/// </summary>
|
||||||
|
private static int ResolveLayer(string layerName)
|
||||||
|
{
|
||||||
|
int layer = LayerMask.NameToLayer(layerName);
|
||||||
|
if (layer >= 0) return layer;
|
||||||
|
|
||||||
|
Debug.LogWarning($"[ItemAssetFactory] Layer '{layerName}' not found — using Default. Add it in the Tags & Layers settings.");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures a project-relative asset folder exists, creating any missing segments. Returns
|
||||||
|
/// false (and logs) when the path cannot be created.
|
||||||
|
/// </summary>
|
||||||
|
private static bool EnsureFolder(string folder)
|
||||||
|
{
|
||||||
|
if (AssetDatabase.IsValidFolder(folder)) return true;
|
||||||
|
|
||||||
|
string parent = Path.GetDirectoryName(folder).Replace('\\', '/');
|
||||||
|
string leaf = Path.GetFileName(folder);
|
||||||
|
if (!EnsureFolder(parent))
|
||||||
|
{
|
||||||
|
Debug.LogError($"[ItemAssetFactory] Could not create folder '{folder}'.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetDatabase.CreateFolder(parent, leaf);
|
||||||
|
return AssetDatabase.IsValidFolder(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 054897b415a351841b60f8e1736267e0
|
||||||
@@ -0,0 +1,438 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.UIElements;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
using Ashwild.Inventory;
|
||||||
|
|
||||||
|
namespace Ashwild.EditorTools
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The custom, context-aware editor for an ItemData shown in the right pane of the Ashwild
|
||||||
|
/// Database. Replaces the default ScriptableObject inspector with a hero header (large icon, name,
|
||||||
|
/// type badge) and a set of cards that reveal themselves only when relevant — the Tool card for
|
||||||
|
/// tools/weapons, the Consumable card for consumables, fuel duration only when the item is fuel.
|
||||||
|
/// All fields bind live to the asset, and the bottom card generates the world/hand prefabs by
|
||||||
|
/// wrapping a chosen source object (see ItemAssetFactory).
|
||||||
|
/// </summary>
|
||||||
|
public class ItemEditorView
|
||||||
|
{
|
||||||
|
#region State
|
||||||
|
|
||||||
|
public VisualElement Root { get; }
|
||||||
|
|
||||||
|
private readonly ItemData item;
|
||||||
|
private readonly SerializedObject so;
|
||||||
|
private readonly Action onMetaChanged;
|
||||||
|
private readonly Action onPrefabGenerated;
|
||||||
|
|
||||||
|
private const int IconPickerControlId = 0x41534849;
|
||||||
|
|
||||||
|
private VisualElement iconPreview;
|
||||||
|
private VisualElement badgeHolder;
|
||||||
|
private VisualElement toolCard;
|
||||||
|
private VisualElement consumableCard;
|
||||||
|
private VisualElement cookingCard;
|
||||||
|
private VisualElement fuelCard;
|
||||||
|
private VisualElement maxStackRow;
|
||||||
|
private VisualElement fuelSecondsRow;
|
||||||
|
private VisualElement generationCard;
|
||||||
|
private Button worldGenButton;
|
||||||
|
private Button handGenButton;
|
||||||
|
private ObjectField worldField;
|
||||||
|
private ObjectField handField;
|
||||||
|
private IMGUIContainer iconPickerProxy;
|
||||||
|
private ItemType currentType;
|
||||||
|
private GameObject pendingSource;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Construction
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the full editor tree for an item. <paramref name="onMetaChanged"/> is raised when the
|
||||||
|
/// name, icon or type changes so the list can refresh that row; <paramref name="onPrefabGenerated"/>
|
||||||
|
/// after a prefab is built so the view re-renders with the new reference.
|
||||||
|
/// </summary>
|
||||||
|
public ItemEditorView(ItemData item, Action onMetaChanged, Action onPrefabGenerated)
|
||||||
|
{
|
||||||
|
this.item = item;
|
||||||
|
this.onMetaChanged = onMetaChanged;
|
||||||
|
this.onPrefabGenerated = onPrefabGenerated;
|
||||||
|
so = new SerializedObject(item);
|
||||||
|
|
||||||
|
Root = new VisualElement();
|
||||||
|
Root.Add(BuildHero());
|
||||||
|
Root.Add(BuildIdentityCard());
|
||||||
|
Root.Add(BuildStackingCard());
|
||||||
|
Root.Add(BuildToolCard());
|
||||||
|
Root.Add(BuildConsumableCard());
|
||||||
|
Root.Add(BuildCookingCard());
|
||||||
|
Root.Add(BuildFuelCard());
|
||||||
|
Root.Add(BuildPrefabsCard());
|
||||||
|
Root.Add(BuildGenerationCard());
|
||||||
|
|
||||||
|
currentType = item.ItemType;
|
||||||
|
ApplyTypeVisibility(currentType);
|
||||||
|
SetRowVisible(maxStackRow, item.IsStackable);
|
||||||
|
SetRowVisible(fuelSecondsRow, item.IsFuel);
|
||||||
|
UpdateGenerationVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Hero
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the hero header: a large icon preview, the editable item name, the type dropdown and
|
||||||
|
/// the coloured type badge. Name/type changes notify the list; the badge and conditional cards
|
||||||
|
/// react to the type immediately.
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildHero()
|
||||||
|
{
|
||||||
|
VisualElement hero = new VisualElement();
|
||||||
|
hero.AddToClassList("ash-hero");
|
||||||
|
|
||||||
|
iconPreview = new VisualElement();
|
||||||
|
iconPreview.AddToClassList("ash-hero__icon");
|
||||||
|
iconPreview.AddToClassList("ash-hero__icon--clickable");
|
||||||
|
iconPreview.tooltip = "Click to change the item icon";
|
||||||
|
iconPreview.RegisterCallback<ClickEvent>(_ => OpenIconPicker());
|
||||||
|
UpdateIconPreview(item.Icon);
|
||||||
|
hero.Add(iconPreview);
|
||||||
|
|
||||||
|
iconPickerProxy = new IMGUIContainer(HandleIconPickerCommands);
|
||||||
|
iconPickerProxy.style.position = Position.Absolute;
|
||||||
|
iconPickerProxy.style.width = 1;
|
||||||
|
iconPickerProxy.style.height = 1;
|
||||||
|
hero.Add(iconPickerProxy);
|
||||||
|
|
||||||
|
VisualElement info = new VisualElement();
|
||||||
|
info.AddToClassList("ash-hero__info");
|
||||||
|
|
||||||
|
TextField nameField = new TextField { value = item.ItemName };
|
||||||
|
nameField.AddToClassList("ash-hero__name");
|
||||||
|
nameField.BindProperty(so.FindProperty("itemName"));
|
||||||
|
nameField.RegisterValueChangedCallback(_ => onMetaChanged?.Invoke());
|
||||||
|
info.Add(nameField);
|
||||||
|
|
||||||
|
VisualElement typeRow = new VisualElement();
|
||||||
|
typeRow.AddToClassList("ash-hero__typerow");
|
||||||
|
|
||||||
|
EnumField typeField = new EnumField(item.ItemType);
|
||||||
|
typeField.AddToClassList("ash-hero__type");
|
||||||
|
typeField.BindProperty(so.FindProperty("itemType"));
|
||||||
|
typeField.RegisterValueChangedCallback(evt =>
|
||||||
|
{
|
||||||
|
currentType = (ItemType)evt.newValue;
|
||||||
|
ApplyTypeVisibility(currentType);
|
||||||
|
RebuildBadge(currentType);
|
||||||
|
UpdateGenerationVisibility();
|
||||||
|
onMetaChanged?.Invoke();
|
||||||
|
});
|
||||||
|
typeRow.Add(typeField);
|
||||||
|
|
||||||
|
badgeHolder = new VisualElement();
|
||||||
|
badgeHolder.AddToClassList("ash-hero__badge");
|
||||||
|
RebuildBadge(item.ItemType);
|
||||||
|
typeRow.Add(badgeHolder);
|
||||||
|
|
||||||
|
info.Add(typeRow);
|
||||||
|
hero.Add(info);
|
||||||
|
|
||||||
|
return hero;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Paints the type badge for the given type, replacing any previous one.
|
||||||
|
/// </summary>
|
||||||
|
private void RebuildBadge(ItemType type)
|
||||||
|
{
|
||||||
|
badgeHolder.Clear();
|
||||||
|
badgeHolder.Add(AshwildUI.Badge(type.ToString(), AshwildUI.TypeColor(type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows the item's icon in the hero preview, or a neutral placeholder when none is set.
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateIconPreview(Sprite sprite)
|
||||||
|
{
|
||||||
|
iconPreview.style.backgroundImage = sprite != null ? new StyleBackground(sprite) : new StyleBackground();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens Unity's sprite object picker, seeded with the current icon, so the designer can change
|
||||||
|
/// the item icon by clicking the hero preview directly.
|
||||||
|
/// </summary>
|
||||||
|
private void OpenIconPicker()
|
||||||
|
{
|
||||||
|
EditorGUIUtility.ShowObjectPicker<Sprite>(item.Icon, false, string.Empty, IconPickerControlId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Listens (through the hidden IMGUI proxy) for the picker selecting/closing on our control id
|
||||||
|
/// and applies the chosen sprite live. ObjectSelectorUpdated fires on each highlight, so the
|
||||||
|
/// hero, the bound Icon field and the list all preview the change immediately.
|
||||||
|
/// </summary>
|
||||||
|
private void HandleIconPickerCommands()
|
||||||
|
{
|
||||||
|
Event evt = Event.current;
|
||||||
|
if (evt == null || evt.type != EventType.ExecuteCommand) return;
|
||||||
|
if (EditorGUIUtility.GetObjectPickerControlID() != IconPickerControlId) return;
|
||||||
|
if (evt.commandName != "ObjectSelectorUpdated" && evt.commandName != "ObjectSelectorClosed") return;
|
||||||
|
|
||||||
|
AssignIcon(EditorGUIUtility.GetObjectPickerObject() as Sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Writes the picked icon onto the asset (no-op when unchanged) and refreshes the hero preview
|
||||||
|
/// and the list row.
|
||||||
|
/// </summary>
|
||||||
|
private void AssignIcon(Sprite sprite)
|
||||||
|
{
|
||||||
|
SerializedProperty iconProp = so.FindProperty("icon");
|
||||||
|
if (iconProp.objectReferenceValue == sprite) return;
|
||||||
|
|
||||||
|
iconProp.objectReferenceValue = sprite;
|
||||||
|
so.ApplyModifiedProperties();
|
||||||
|
UpdateIconPreview(sprite);
|
||||||
|
onMetaChanged?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Cards
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identity card: description and icon. Editing the icon updates the hero preview and the list.
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildIdentityCard()
|
||||||
|
{
|
||||||
|
VisualElement card = AshwildUI.Card("Identity");
|
||||||
|
|
||||||
|
TextField description = new TextField("Description") { multiline = true };
|
||||||
|
description.AddToClassList("ash-description");
|
||||||
|
description.BindProperty(so.FindProperty("description"));
|
||||||
|
card.Add(description);
|
||||||
|
|
||||||
|
ObjectField icon = new ObjectField("Icon") { objectType = typeof(Sprite), allowSceneObjects = false };
|
||||||
|
icon.BindProperty(so.FindProperty("icon"));
|
||||||
|
icon.RegisterValueChangedCallback(evt =>
|
||||||
|
{
|
||||||
|
UpdateIconPreview(evt.newValue as Sprite);
|
||||||
|
onMetaChanged?.Invoke();
|
||||||
|
});
|
||||||
|
card.Add(icon);
|
||||||
|
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stacking card: the stackable toggle and a max-stack field that only shows while stackable.
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildStackingCard()
|
||||||
|
{
|
||||||
|
VisualElement card = AshwildUI.Card("Stacking");
|
||||||
|
|
||||||
|
Toggle stackable = new Toggle("Is Stackable");
|
||||||
|
stackable.BindProperty(so.FindProperty("isStackable"));
|
||||||
|
stackable.RegisterValueChangedCallback(evt => SetRowVisible(maxStackRow, evt.newValue));
|
||||||
|
card.Add(stackable);
|
||||||
|
|
||||||
|
maxStackRow = new IntegerField("Max Stack Size");
|
||||||
|
((IntegerField)maxStackRow).BindProperty(so.FindProperty("maxStackSize"));
|
||||||
|
card.Add(maxStackRow);
|
||||||
|
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tool card: harvest type and tool power. Only shown for tools and weapons.
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildToolCard()
|
||||||
|
{
|
||||||
|
toolCard = AshwildUI.Card("Tool");
|
||||||
|
|
||||||
|
EnumField harvest = new EnumField("Harvest Type", item.HarvestType);
|
||||||
|
harvest.BindProperty(so.FindProperty("harvestType"));
|
||||||
|
toolCard.Add(harvest);
|
||||||
|
|
||||||
|
FloatField power = new FloatField("Tool Power");
|
||||||
|
power.BindProperty(so.FindProperty("toolPower"));
|
||||||
|
toolCard.Add(power);
|
||||||
|
|
||||||
|
return toolCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Consumable card: the restore values. Only shown for consumables.
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildConsumableCard()
|
||||||
|
{
|
||||||
|
consumableCard = AshwildUI.Card("Consumable");
|
||||||
|
consumableCard.Add(BoundFloat("Health Restore", "healthRestore"));
|
||||||
|
consumableCard.Add(BoundFloat("Hunger Restore", "hungerRestore"));
|
||||||
|
consumableCard.Add(BoundFloat("Thirst Restore", "thirstRestore"));
|
||||||
|
return consumableCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cooking card: the cooked result item and its cook time (applies to any item that can cook).
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildCookingCard()
|
||||||
|
{
|
||||||
|
cookingCard = AshwildUI.Card("Cooking");
|
||||||
|
|
||||||
|
ObjectField cooked = new ObjectField("Cooked Result") { objectType = typeof(ItemData), allowSceneObjects = false };
|
||||||
|
cooked.BindProperty(so.FindProperty("cookedResult"));
|
||||||
|
cookingCard.Add(cooked);
|
||||||
|
|
||||||
|
cookingCard.Add(BoundFloat("Cook Time", "cookTime"));
|
||||||
|
return cookingCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Fuel card: the fuel toggle and a burn-duration field revealed only while the item is fuel.
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildFuelCard()
|
||||||
|
{
|
||||||
|
fuelCard = AshwildUI.Card("Fuel");
|
||||||
|
|
||||||
|
Toggle isFuel = new Toggle("Is Fuel");
|
||||||
|
isFuel.BindProperty(so.FindProperty("isFuel"));
|
||||||
|
isFuel.RegisterValueChangedCallback(evt => SetRowVisible(fuelSecondsRow, evt.newValue));
|
||||||
|
fuelCard.Add(isFuel);
|
||||||
|
|
||||||
|
fuelSecondsRow = BoundFloat("Fuel Seconds", "fuelSeconds");
|
||||||
|
fuelCard.Add(fuelSecondsRow);
|
||||||
|
|
||||||
|
return fuelCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prefabs card: the world and hand prefab references (auto-filled by generation, editable here).
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildPrefabsCard()
|
||||||
|
{
|
||||||
|
VisualElement card = AshwildUI.Card("Prefabs");
|
||||||
|
|
||||||
|
worldField = new ObjectField("World Prefab") { objectType = typeof(GameObject), allowSceneObjects = false };
|
||||||
|
worldField.BindProperty(so.FindProperty("worldPrefab"));
|
||||||
|
worldField.RegisterValueChangedCallback(_ => UpdateGenerationVisibility());
|
||||||
|
card.Add(worldField);
|
||||||
|
|
||||||
|
handField = new ObjectField("Hand Prefab") { objectType = typeof(GameObject), allowSceneObjects = false };
|
||||||
|
handField.BindProperty(so.FindProperty("handPrefab"));
|
||||||
|
handField.RegisterValueChangedCallback(_ => UpdateGenerationVisibility());
|
||||||
|
card.Add(handField);
|
||||||
|
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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.
|
||||||
|
/// </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 };
|
||||||
|
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);
|
||||||
|
|
||||||
|
generationCard.Add(buttons);
|
||||||
|
return generationCard;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Actions
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the world pickup prefab around the chosen source and re-renders on success.
|
||||||
|
/// </summary>
|
||||||
|
private void GenerateWorld()
|
||||||
|
{
|
||||||
|
if (ItemAssetFactory.GenerateWorldPrefab(item, pendingSource) != null)
|
||||||
|
onPrefabGenerated?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Conditional Visibility
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reveals only the cards that make sense for the item type: Tool for tools/weapons; Consumable
|
||||||
|
/// for consumables; Cooking for consumables (food) and materials (smelting ore → ingot), but
|
||||||
|
/// never for a tool or weapon; and Fuel for materials only (a consumable is never burned).
|
||||||
|
/// </summary>
|
||||||
|
private void ApplyTypeVisibility(ItemType type)
|
||||||
|
{
|
||||||
|
SetRowVisible(toolCard, type == ItemType.Tool || type == ItemType.Weapon);
|
||||||
|
SetRowVisible(consumableCard, type == ItemType.Consumable);
|
||||||
|
SetRowVisible(cookingCard, type == ItemType.Consumable || type == ItemType.Material);
|
||||||
|
SetRowVisible(fuelCard, type == ItemType.Material);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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.
|
||||||
|
/// </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;
|
||||||
|
|
||||||
|
SetRowVisible(worldGenButton, showWorld);
|
||||||
|
SetRowVisible(handGenButton, showHand);
|
||||||
|
SetRowVisible(generationCard, showWorld || showHand);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Collapses or shows an element without leaving a gap when hidden.
|
||||||
|
/// </summary>
|
||||||
|
private static void SetRowVisible(VisualElement element, bool visible)
|
||||||
|
{
|
||||||
|
if (element != null) element.style.display = visible ? DisplayStyle.Flex : DisplayStyle.None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds a float field already bound to a serialized property.
|
||||||
|
/// </summary>
|
||||||
|
private FloatField BoundFloat(string label, string property)
|
||||||
|
{
|
||||||
|
FloatField field = new FloatField(label);
|
||||||
|
field.BindProperty(so.FindProperty(property));
|
||||||
|
return field;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 295e243d2ccb0934998aadc27ec073d6
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
using System.IO;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using Ashwild.Crafting;
|
||||||
|
using Ashwild.Inventory;
|
||||||
|
|
||||||
|
namespace Ashwild.EditorTools
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Editor-only factory that creates CraftingRecipe assets for the Ashwild Database window.
|
||||||
|
/// Kept separate from the window so recipe authoring stays a single-purpose, testable helper —
|
||||||
|
/// mirrors ItemAssetFactory for items.
|
||||||
|
/// </summary>
|
||||||
|
public static class RecipeAssetFactory
|
||||||
|
{
|
||||||
|
#region Constants
|
||||||
|
|
||||||
|
private const string RecipesFolder = "Assets/GAME/ScriptableObjects/Recipes";
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Recipe Asset
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a fresh CraftingRecipe asset under the recipes folder with a unique name, seeding
|
||||||
|
/// its recipe name and (optionally) the result item so the new asset is immediately coherent.
|
||||||
|
/// Returns the asset for selection, or null if the folder cannot be made.
|
||||||
|
/// </summary>
|
||||||
|
public static CraftingRecipe CreateRecipe(string desiredName, ItemData resultItem)
|
||||||
|
{
|
||||||
|
if (!EnsureFolder(RecipesFolder)) return null;
|
||||||
|
|
||||||
|
string safeName = string.IsNullOrWhiteSpace(desiredName) ? "NewRecipe" : desiredName.Trim();
|
||||||
|
string assetPath = AssetDatabase.GenerateUniqueAssetPath($"{RecipesFolder}/{safeName}.asset");
|
||||||
|
|
||||||
|
CraftingRecipe recipe = ScriptableObject.CreateInstance<CraftingRecipe>();
|
||||||
|
AssetDatabase.CreateAsset(recipe, assetPath);
|
||||||
|
|
||||||
|
SerializedObject so = new SerializedObject(recipe);
|
||||||
|
so.FindProperty("recipeName").stringValue = safeName;
|
||||||
|
so.FindProperty("resultQuantity").intValue = 1;
|
||||||
|
if (resultItem != null) so.FindProperty("resultItem").objectReferenceValue = resultItem;
|
||||||
|
so.ApplyModifiedPropertiesWithoutUndo();
|
||||||
|
|
||||||
|
EditorUtility.SetDirty(recipe);
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
return recipe;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Internal Helpers
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ensures a project-relative asset folder exists, creating any missing segments. Returns
|
||||||
|
/// false (and logs) when the path cannot be created.
|
||||||
|
/// </summary>
|
||||||
|
private static bool EnsureFolder(string folder)
|
||||||
|
{
|
||||||
|
if (AssetDatabase.IsValidFolder(folder)) return true;
|
||||||
|
|
||||||
|
string parent = Path.GetDirectoryName(folder).Replace('\\', '/');
|
||||||
|
string leaf = Path.GetFileName(folder);
|
||||||
|
if (!EnsureFolder(parent))
|
||||||
|
{
|
||||||
|
Debug.LogError($"[RecipeAssetFactory] Could not create folder '{folder}'.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetDatabase.CreateFolder(parent, leaf);
|
||||||
|
return AssetDatabase.IsValidFolder(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: f46334aa438fc6e4ab73fbf090b51e5e
|
||||||
@@ -0,0 +1,348 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.UIElements;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UIElements;
|
||||||
|
using Ashwild.Crafting;
|
||||||
|
using Ashwild.Inventory;
|
||||||
|
|
||||||
|
namespace Ashwild.EditorTools
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The custom editor for a CraftingRecipe shown in the right pane of the Ashwild Database. The
|
||||||
|
/// editor IS the recipe equation: a row of interactive ingredient cards ("+"-joined) leading to
|
||||||
|
/// "=" and the result card. Each card's icon opens an item picker to set/change the item, a
|
||||||
|
/// −/+ stepper adjusts its quantity, and ingredient cards carry a remove button; a dashed "+" tile
|
||||||
|
/// appends a new ingredient. A compact name field sits above it. All edits write straight to the
|
||||||
|
/// asset and repaint the strip.
|
||||||
|
/// </summary>
|
||||||
|
public class RecipeEditorView
|
||||||
|
{
|
||||||
|
#region State
|
||||||
|
|
||||||
|
public VisualElement Root { get; }
|
||||||
|
|
||||||
|
private const int RecipePickerControlId = 0x41534852;
|
||||||
|
|
||||||
|
private readonly CraftingRecipe recipe;
|
||||||
|
private readonly SerializedObject so;
|
||||||
|
private readonly SerializedProperty ingredientsProp;
|
||||||
|
private readonly Action onMetaChanged;
|
||||||
|
|
||||||
|
private VisualElement equation;
|
||||||
|
private Action<ItemData> pickHandler;
|
||||||
|
private bool pickCommitsOnCloseOnly;
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Construction
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the recipe editor tree. <paramref name="onMetaChanged"/> is raised when the recipe
|
||||||
|
/// name or result changes so the left list can refresh that row.
|
||||||
|
/// </summary>
|
||||||
|
public RecipeEditorView(CraftingRecipe recipe, Action onMetaChanged)
|
||||||
|
{
|
||||||
|
this.recipe = recipe;
|
||||||
|
this.onMetaChanged = onMetaChanged;
|
||||||
|
so = new SerializedObject(recipe);
|
||||||
|
ingredientsProp = so.FindProperty("ingredients");
|
||||||
|
|
||||||
|
Root = new VisualElement();
|
||||||
|
Root.Add(BuildNameField());
|
||||||
|
Root.Add(BuildEquation());
|
||||||
|
Root.Add(BuildPickerProxy());
|
||||||
|
|
||||||
|
RefreshEquation();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Header
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the recipe-name field shown above the equation, styled as the large editable title
|
||||||
|
/// (same look as an item's hero name field); renaming refreshes the list.
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildNameField()
|
||||||
|
{
|
||||||
|
TextField nameField = new TextField();
|
||||||
|
nameField.AddToClassList("ash-hero__name");
|
||||||
|
nameField.AddToClassList("ash-recipe-name");
|
||||||
|
nameField.BindProperty(so.FindProperty("recipeName"));
|
||||||
|
nameField.RegisterValueChangedCallback(_ => onMetaChanged?.Invoke());
|
||||||
|
return nameField;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Equation
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the container for the interactive equation; its cards are filled by RefreshEquation.
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildEquation()
|
||||||
|
{
|
||||||
|
equation = new VisualElement();
|
||||||
|
equation.AddToClassList("ash-equation");
|
||||||
|
return equation;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Repaints the whole equation: one interactive card per ingredient (joined by "+"), the dashed
|
||||||
|
/// add tile, then "=", then the result card.
|
||||||
|
/// </summary>
|
||||||
|
private void RefreshEquation()
|
||||||
|
{
|
||||||
|
equation.Clear();
|
||||||
|
|
||||||
|
int count = ingredientsProp.arraySize;
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (i > 0) equation.Add(Operator("+"));
|
||||||
|
equation.Add(BuildIngredientCard(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count > 0) equation.Add(Operator("+"));
|
||||||
|
equation.Add(BuildAddTile());
|
||||||
|
|
||||||
|
equation.Add(Operator("="));
|
||||||
|
equation.Add(BuildResultCard());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds a large "+"/"=" operator glyph between cards.
|
||||||
|
/// </summary>
|
||||||
|
private static Label Operator(string glyph)
|
||||||
|
{
|
||||||
|
Label op = new Label(glyph);
|
||||||
|
op.AddToClassList("ash-equation__op");
|
||||||
|
return op;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Cards
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the interactive card for the ingredient at <paramref name="index"/>: a clickable icon
|
||||||
|
/// (change item), the name, a −/+ quantity stepper, and a remove button.
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildIngredientCard(int index)
|
||||||
|
{
|
||||||
|
SerializedProperty element = ingredientsProp.GetArrayElementAtIndex(index);
|
||||||
|
ItemData item = element.FindPropertyRelative("item").objectReferenceValue as ItemData;
|
||||||
|
int quantity = element.FindPropertyRelative("quantity").intValue;
|
||||||
|
|
||||||
|
VisualElement card = MakeCard(item, () => OpenItemPicker(item, true, picked => SetIngredientItem(index, picked)));
|
||||||
|
card.Add(Stepper(quantity, delta => AdjustIngredientQuantity(index, delta)));
|
||||||
|
|
||||||
|
Button remove = new Button(() => RemoveIngredient(index)) { text = "✕" };
|
||||||
|
remove.AddToClassList("ash-rchip__remove");
|
||||||
|
card.Add(remove);
|
||||||
|
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the result card: a clickable icon (change result item) and a −/+ quantity stepper.
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildResultCard()
|
||||||
|
{
|
||||||
|
VisualElement card = MakeCard(recipe.ResultItem, () => OpenItemPicker(recipe.ResultItem, true, SetResultItem));
|
||||||
|
card.AddToClassList("ash-rchip--result");
|
||||||
|
card.Add(Stepper(recipe.ResultQuantity, AdjustResultQuantity));
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the dashed "+" tile that appends a new ingredient once an item is picked.
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildAddTile()
|
||||||
|
{
|
||||||
|
VisualElement tile = new VisualElement();
|
||||||
|
tile.AddToClassList("ash-rchip");
|
||||||
|
tile.AddToClassList("ash-rchip--add");
|
||||||
|
tile.tooltip = "Add an ingredient";
|
||||||
|
tile.RegisterCallback<ClickEvent>(_ => OpenItemPicker(null, false, AddIngredient));
|
||||||
|
|
||||||
|
Label plus = new Label("+");
|
||||||
|
plus.AddToClassList("ash-rchip__plus");
|
||||||
|
tile.Add(plus);
|
||||||
|
|
||||||
|
return tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the shared card body (icon + name) for an item, with the icon wired to open a picker.
|
||||||
|
/// The icon shows the item's sprite, or a neutral placeholder when unset.
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement MakeCard(ItemData item, Action onIconClicked)
|
||||||
|
{
|
||||||
|
VisualElement card = new VisualElement();
|
||||||
|
card.AddToClassList("ash-rchip");
|
||||||
|
|
||||||
|
VisualElement icon = new VisualElement();
|
||||||
|
icon.AddToClassList("ash-rchip__icon");
|
||||||
|
icon.tooltip = "Click to choose an item";
|
||||||
|
if (item != null && item.Icon != null) icon.style.backgroundImage = new StyleBackground(item.Icon);
|
||||||
|
icon.RegisterCallback<ClickEvent>(_ => onIconClicked());
|
||||||
|
card.Add(icon);
|
||||||
|
|
||||||
|
Label name = new Label(item != null ? item.ItemName : "Choose…");
|
||||||
|
name.AddToClassList("ash-rchip__name");
|
||||||
|
card.Add(name);
|
||||||
|
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds a −/+ quantity stepper showing the current value; <paramref name="onDelta"/> receives
|
||||||
|
/// -1 or +1.
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement Stepper(int quantity, Action<int> onDelta)
|
||||||
|
{
|
||||||
|
VisualElement stepper = new VisualElement();
|
||||||
|
stepper.AddToClassList("ash-rchip__stepper");
|
||||||
|
|
||||||
|
Button minus = new Button(() => onDelta(-1)) { text = "−" };
|
||||||
|
minus.AddToClassList("ash-rchip__step");
|
||||||
|
stepper.Add(minus);
|
||||||
|
|
||||||
|
Label value = new Label(quantity.ToString());
|
||||||
|
value.AddToClassList("ash-rchip__qty");
|
||||||
|
stepper.Add(value);
|
||||||
|
|
||||||
|
Button plus = new Button(() => onDelta(1)) { text = "+" };
|
||||||
|
plus.AddToClassList("ash-rchip__step");
|
||||||
|
stepper.Add(plus);
|
||||||
|
|
||||||
|
return stepper;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Mutations
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the item of an existing ingredient and repaints.
|
||||||
|
/// </summary>
|
||||||
|
private void SetIngredientItem(int index, ItemData item)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= ingredientsProp.arraySize) return;
|
||||||
|
ingredientsProp.GetArrayElementAtIndex(index).FindPropertyRelative("item").objectReferenceValue = item;
|
||||||
|
so.ApplyModifiedProperties();
|
||||||
|
RefreshEquation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes an ingredient's quantity by a delta, clamped to a minimum of one, and repaints.
|
||||||
|
/// </summary>
|
||||||
|
private void AdjustIngredientQuantity(int index, int delta)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= ingredientsProp.arraySize) return;
|
||||||
|
SerializedProperty quantity = ingredientsProp.GetArrayElementAtIndex(index).FindPropertyRelative("quantity");
|
||||||
|
quantity.intValue = Mathf.Max(1, quantity.intValue + delta);
|
||||||
|
so.ApplyModifiedProperties();
|
||||||
|
RefreshEquation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Appends a new ingredient (quantity 1) for the picked item and repaints; ignores a null pick
|
||||||
|
/// (e.g. the picker was cancelled).
|
||||||
|
/// </summary>
|
||||||
|
private void AddIngredient(ItemData item)
|
||||||
|
{
|
||||||
|
if (item == null) return;
|
||||||
|
|
||||||
|
int index = ingredientsProp.arraySize;
|
||||||
|
ingredientsProp.arraySize++;
|
||||||
|
SerializedProperty element = ingredientsProp.GetArrayElementAtIndex(index);
|
||||||
|
element.FindPropertyRelative("item").objectReferenceValue = item;
|
||||||
|
element.FindPropertyRelative("quantity").intValue = 1;
|
||||||
|
so.ApplyModifiedProperties();
|
||||||
|
RefreshEquation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes the ingredient at the given index and repaints.
|
||||||
|
/// </summary>
|
||||||
|
private void RemoveIngredient(int index)
|
||||||
|
{
|
||||||
|
if (index < 0 || index >= ingredientsProp.arraySize) return;
|
||||||
|
ingredientsProp.DeleteArrayElementAtIndex(index);
|
||||||
|
so.ApplyModifiedProperties();
|
||||||
|
RefreshEquation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the recipe's result item, repaints, and refreshes the list row (icon/name follow it).
|
||||||
|
/// </summary>
|
||||||
|
private void SetResultItem(ItemData item)
|
||||||
|
{
|
||||||
|
so.FindProperty("resultItem").objectReferenceValue = item;
|
||||||
|
so.ApplyModifiedProperties();
|
||||||
|
RefreshEquation();
|
||||||
|
onMetaChanged?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Changes the result quantity by a delta, clamped to a minimum of one, and repaints.
|
||||||
|
/// </summary>
|
||||||
|
private void AdjustResultQuantity(int delta)
|
||||||
|
{
|
||||||
|
SerializedProperty quantity = so.FindProperty("resultQuantity");
|
||||||
|
quantity.intValue = Mathf.Max(1, quantity.intValue + delta);
|
||||||
|
so.ApplyModifiedProperties();
|
||||||
|
RefreshEquation();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Item Picker
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Builds the hidden IMGUI proxy that relays Unity's object-picker commands to the active pick
|
||||||
|
/// handler (the editor window is UI Toolkit, which can't receive those commands directly).
|
||||||
|
/// </summary>
|
||||||
|
private VisualElement BuildPickerProxy()
|
||||||
|
{
|
||||||
|
IMGUIContainer proxy = new IMGUIContainer(HandlePickerCommands);
|
||||||
|
proxy.style.position = Position.Absolute;
|
||||||
|
proxy.style.width = 1;
|
||||||
|
proxy.style.height = 1;
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens Unity's item picker seeded with the current item. When <paramref name="commitLive"/>
|
||||||
|
/// is true the pick applies on every highlight (live preview, e.g. changing an existing item);
|
||||||
|
/// otherwise it applies only when the picker closes (e.g. adding a new ingredient — commit once).
|
||||||
|
/// </summary>
|
||||||
|
private void OpenItemPicker(ItemData seed, bool commitLive, Action<ItemData> onPicked)
|
||||||
|
{
|
||||||
|
pickHandler = onPicked;
|
||||||
|
pickCommitsOnCloseOnly = !commitLive;
|
||||||
|
EditorGUIUtility.ShowObjectPicker<ItemData>(seed, false, string.Empty, RecipePickerControlId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Forwards the picker's selection to the active handler, honouring the live-vs-on-close policy.
|
||||||
|
/// </summary>
|
||||||
|
private void HandlePickerCommands()
|
||||||
|
{
|
||||||
|
Event evt = Event.current;
|
||||||
|
if (evt == null || evt.type != EventType.ExecuteCommand) return;
|
||||||
|
if (EditorGUIUtility.GetObjectPickerControlID() != RecipePickerControlId) return;
|
||||||
|
|
||||||
|
bool updated = evt.commandName == "ObjectSelectorUpdated";
|
||||||
|
bool closed = evt.commandName == "ObjectSelectorClosed";
|
||||||
|
if (!updated && !closed) return;
|
||||||
|
if (updated && pickCommitsOnCloseOnly) return;
|
||||||
|
|
||||||
|
pickHandler?.Invoke(EditorGUIUtility.GetObjectPickerObject() as ItemData);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 81bef47c391c7eb4d83fbd5b4dfe3a5d
|
||||||
+8
-4
@@ -10,11 +10,11 @@ MonoBehaviour:
|
|||||||
m_Enabled: 1
|
m_Enabled: 1
|
||||||
m_EditorHideFlags: 0
|
m_EditorHideFlags: 0
|
||||||
m_Script: {fileID: 11500000, guid: f53cff53f28e82d42a1a8d25c52d87f4, type: 3}
|
m_Script: {fileID: 11500000, guid: f53cff53f28e82d42a1a8d25c52d87f4, type: 3}
|
||||||
m_Name: Axe
|
m_Name: WoodenAxe
|
||||||
m_EditorClassIdentifier: Assembly-CSharp::ItemData
|
m_EditorClassIdentifier: Assembly-CSharp::ItemData
|
||||||
itemName: Hache
|
itemName: Hache en bois
|
||||||
icon: {fileID: 21300000, guid: 98658eb15d5ed8c46a06a8b58eab3140, type: 3}
|
icon: {fileID: 21300000, guid: 573d176f3cdfdbb498814a58d66f6e49, type: 3}
|
||||||
description: Hache
|
description: Hache en bois
|
||||||
isStackable: 0
|
isStackable: 0
|
||||||
maxStackSize: 64
|
maxStackSize: 64
|
||||||
itemType: 1
|
itemType: 1
|
||||||
@@ -23,5 +23,9 @@ MonoBehaviour:
|
|||||||
healthRestore: 0
|
healthRestore: 0
|
||||||
hungerRestore: 0
|
hungerRestore: 0
|
||||||
thirstRestore: 0
|
thirstRestore: 0
|
||||||
|
cookedResult: {fileID: 0}
|
||||||
|
cookTime: 10
|
||||||
|
isFuel: 0
|
||||||
|
fuelSeconds: 30
|
||||||
worldPrefab: {fileID: 1878273237308662, guid: c607101b481f147429c13a876ca3d401, type: 3}
|
worldPrefab: {fileID: 1878273237308662, guid: c607101b481f147429c13a876ca3d401, type: 3}
|
||||||
handPrefab: {fileID: 1878273237308662, guid: 0031543705b8c024c916caa648809017, type: 3}
|
handPrefab: {fileID: 1878273237308662, guid: 0031543705b8c024c916caa648809017, type: 3}
|
||||||
+244
-9
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user