Angepasste Garage, verbessertes Icon Asset system, range der türme vorschau beim ziehen
This commit is contained in:
1 parent
5486444dde
commit
5af8655c97
59 files changed
+12079
-457
No files matched your search
@@ -0,0 +1,13 @@
|
|||||||
|
Shader "Handbook/ArenaRange" {
|
||||||
|
Properties { _Color("Color",Color)=(1,1,1,1) }
|
||||||
|
SubShader { Tags {"Queue"="Transparent" "RenderType"="Transparent"} Cull Off ZWrite Off Blend SrcAlpha OneMinusSrcAlpha
|
||||||
|
Pass { CGPROGRAM
|
||||||
|
#pragma vertex vert
|
||||||
|
#pragma fragment frag
|
||||||
|
#include "UnityCG.cginc"
|
||||||
|
struct app {float4 vertex:POSITION;float4 color:COLOR;float2 uv:TEXCOORD0;};
|
||||||
|
struct v2f {float4 position:SV_POSITION;float2 world:TEXCOORD0;float4 color:COLOR;float2 uv:TEXCOORD1;};
|
||||||
|
float4 _Color,_ArenaCenter,_Tower,_Forward;float _ArenaRadius,_Fill,_Minimum,_Maximum,_CosHalf,_Dashed;
|
||||||
|
v2f vert(app v){v2f o;o.position=UnityObjectToClipPos(v.vertex);o.world=mul(unity_ObjectToWorld,v.vertex).xy;o.color=v.color*_Color;o.uv=v.uv;return o;}
|
||||||
|
fixed4 frag(v2f i):SV_Target {clip(_ArenaRadius-distance(i.world,_ArenaCenter.xy));if(_Fill>.5){float2 d=i.world-_Tower.xy;float r=length(d);clip(r-_Minimum);clip(_Maximum-r);clip(dot(d/max(r,.00001),_Forward.xy)-_CosHalf);return fixed4(.25,.65,.9,.07);}if(_Dashed>.5)clip(frac(i.uv.x*48)-.4);return i.color;}
|
||||||
|
ENDCG } } }
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: c1bcc86ecd69ead439ec8d5583707c9e
|
||||||
|
ShaderImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
defaultTextures: []
|
||||||
|
nonModifiableTextures: []
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
%YAML 1.1
|
||||||
|
%TAG !u! tag:unity3d.com,2011:
|
||||||
|
--- !u!21 &2100000
|
||||||
|
Material:
|
||||||
|
serializedVersion: 8
|
||||||
|
m_ObjectHideFlags: 0
|
||||||
|
m_CorrespondingSourceObject: {fileID: 0}
|
||||||
|
m_PrefabInstance: {fileID: 0}
|
||||||
|
m_PrefabAsset: {fileID: 0}
|
||||||
|
m_Name: RangePreview
|
||||||
|
m_Shader: {fileID: 4800000, guid: c1bcc86ecd69ead439ec8d5583707c9e, type: 3}
|
||||||
|
m_Parent: {fileID: 0}
|
||||||
|
m_ModifiedSerializedProperties: 0
|
||||||
|
m_ValidKeywords: []
|
||||||
|
m_InvalidKeywords: []
|
||||||
|
m_LightmapFlags: 4
|
||||||
|
m_EnableInstancingVariants: 0
|
||||||
|
m_DoubleSidedGI: 0
|
||||||
|
m_CustomRenderQueue: -1
|
||||||
|
stringTagMap: {}
|
||||||
|
disabledShaderPasses: []
|
||||||
|
m_LockedProperties:
|
||||||
|
m_SavedProperties:
|
||||||
|
serializedVersion: 3
|
||||||
|
m_TexEnvs:
|
||||||
|
- _AlphaTex:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
- _MainTex:
|
||||||
|
m_Texture: {fileID: 0}
|
||||||
|
m_Scale: {x: 1, y: 1}
|
||||||
|
m_Offset: {x: 0, y: 0}
|
||||||
|
m_Ints: []
|
||||||
|
m_Floats:
|
||||||
|
- PixelSnap: 0
|
||||||
|
- _EnableExternalAlpha: 0
|
||||||
|
m_Colors:
|
||||||
|
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
- _Flip: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
- _RendererColor: {r: 1, g: 1, b: 1, a: 1}
|
||||||
|
m_BuildTextureStacks: []
|
||||||
|
m_AllowLocking: 1
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 676b379c4ce94b547a115a3d96030e45
|
||||||
|
NativeFormatImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
mainObjectFileID: 2100000
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
|||||||
description: "Gewehr: Schaden 20 \xB7 2 Sch\xFCsse/s. Munition: Kugeln. Baufl\xE4che:
|
description: "Gewehr: Schaden 20 \xB7 2 Sch\xFCsse/s. Munition: Kugeln. Baufl\xE4che:
|
||||||
Mauer. Werte vorl\xE4ufig."
|
Mauer. Werte vorl\xE4ufig."
|
||||||
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
previewSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
gameplayPrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||||
baseSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
previewLayers:
|
||||||
basePrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
- sprite: {fileID: 0}
|
||||||
gameplayPrefab: {fileID: 0}
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 0
|
region: 0
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.36
|
baseRadius: 0.36
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 1
|
attackShape: 1
|
||||||
targets: 1
|
targets: 1
|
||||||
rotation: 0
|
rotation: 0
|
||||||
|
|||||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
|||||||
description: "Minigun: Schaden 5 \xB7 8 Sch\xFCsse/s. Munition: Kugeln. Baufl\xE4che:
|
description: "Minigun: Schaden 5 \xB7 8 Sch\xFCsse/s. Munition: Kugeln. Baufl\xE4che:
|
||||||
Mauer. Werte vorl\xE4ufig."
|
Mauer. Werte vorl\xE4ufig."
|
||||||
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
previewSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
gameplayPrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||||
baseSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
previewLayers:
|
||||||
basePrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
- sprite: {fileID: 0}
|
||||||
gameplayPrefab: {fileID: 0}
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 0
|
region: 0
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.36
|
baseRadius: 0.36
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 1
|
attackShape: 1
|
||||||
targets: 1
|
targets: 1
|
||||||
rotation: 0
|
rotation: 0
|
||||||
|
|||||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
|||||||
description: "Sniper: Schaden 60 \xB7 0,35 Sch\xFCsse/s. Munition: Kugeln. Baufl\xE4che:
|
description: "Sniper: Schaden 60 \xB7 0,35 Sch\xFCsse/s. Munition: Kugeln. Baufl\xE4che:
|
||||||
Mauer. Werte vorl\xE4ufig."
|
Mauer. Werte vorl\xE4ufig."
|
||||||
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
previewSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
gameplayPrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||||
baseSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
previewLayers:
|
||||||
basePrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
- sprite: {fileID: 0}
|
||||||
gameplayPrefab: {fileID: 0}
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 0
|
region: 0
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.36
|
baseRadius: 0.36
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 1
|
attackShape: 1
|
||||||
targets: 3
|
targets: 3
|
||||||
rotation: 2
|
rotation: 2
|
||||||
|
|||||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
|||||||
description: "Granatenwerfer: Schaden 40 \xB7 0,7 Sch\xFCsse/s. Munition: Sprengstoff.
|
description: "Granatenwerfer: Schaden 40 \xB7 0,7 Sch\xFCsse/s. Munition: Sprengstoff.
|
||||||
Baufl\xE4che: Mauer. Werte vorl\xE4ufig."
|
Baufl\xE4che: Mauer. Werte vorl\xE4ufig."
|
||||||
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
previewSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
gameplayPrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||||
baseSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
previewLayers:
|
||||||
basePrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
- sprite: {fileID: 0}
|
||||||
gameplayPrefab: {fileID: 0}
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 0
|
region: 0
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.36
|
baseRadius: 0.36
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 2
|
attackShape: 2
|
||||||
targets: 2
|
targets: 2
|
||||||
rotation: 0
|
rotation: 0
|
||||||
|
|||||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
|||||||
description: "Howitzer: Schaden 40 \xB7 0,25 Sch\xFCsse/s. Munition: Sprengstoff
|
description: "Howitzer: Schaden 40 \xB7 0,25 Sch\xFCsse/s. Munition: Sprengstoff
|
||||||
+ \xD6l. Baufl\xE4che: Mauer. Werte vorl\xE4ufig."
|
+ \xD6l. Baufl\xE4che: Mauer. Werte vorl\xE4ufig."
|
||||||
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
previewSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
gameplayPrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||||
baseSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
previewLayers:
|
||||||
basePrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
- sprite: {fileID: 0}
|
||||||
gameplayPrefab: {fileID: 0}
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 0
|
region: 0
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.36
|
baseRadius: 0.36
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 1.5
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 3
|
attackShape: 3
|
||||||
targets: 4
|
targets: 4
|
||||||
rotation: 2
|
rotation: 2
|
||||||
@@ -118,4 +130,5 @@ MonoBehaviour:
|
|||||||
Alle physischen Waffen teilen Physical, Energiewaffen EnergyCells. supply ist
|
Alle physischen Waffen teilen Physical, Energiewaffen EnergyCells. supply ist
|
||||||
die Quelle f\xFCr Magazinkosten; combat.ammoPerShot/ammoPerSecond/energyPerSecond
|
die Quelle f\xFCr Magazinkosten; combat.ammoPerShot/ammoPerSecond/energyPerSecond
|
||||||
sind Altwerte und d\xFCrfen nicht zus\xE4tzlich aus demselben Magazin abgezogen
|
sind Altwerte und d\xFCrfen nicht zus\xE4tzlich aus demselben Magazin abgezogen
|
||||||
werden."
|
werden.\nVorl\xE4ufige Howitzer-Mindestreichweite: 1,5 Welteinheiten. Gameplay
|
||||||
|
muss minimumRange bei der Zielpr\xFCfung verwenden."
|
||||||
@@ -23,16 +23,25 @@ MonoBehaviour:
|
|||||||
description: "Flammenwerfer: Schaden 8/s \xB7 Kontinuierlich. Dauerverbrauch statt
|
description: "Flammenwerfer: Schaden 8/s \xB7 Kontinuierlich. Dauerverbrauch statt
|
||||||
unendlicher Feuerrate. Werte vorl\xE4ufig."
|
unendlicher Feuerrate. Werte vorl\xE4ufig."
|
||||||
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
previewSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
gameplayPrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||||
baseSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
previewLayers:
|
||||||
basePrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
- sprite: {fileID: 0}
|
||||||
gameplayPrefab: {fileID: 0}
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 0
|
region: 0
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.36
|
baseRadius: 0.36
|
||||||
baseSize: {x: 0.5, y: 0.5}
|
baseSize: {x: 0.5, y: 0.5}
|
||||||
placementRadius: 0.6
|
placementRadius: 0.6
|
||||||
allowPlayerRotation: 0
|
allowPlayerRotation: 1
|
||||||
blocksMovement: 0
|
blocksMovement: 0
|
||||||
requirePathValidation: 0
|
requirePathValidation: 0
|
||||||
countsTowardWallLimit: 1
|
countsTowardWallLimit: 1
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 2
|
ammoPerSecond: 2
|
||||||
continuousAttack: 1
|
continuousAttack: 1
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 2
|
attackShape: 2
|
||||||
targets: 2
|
targets: 2
|
||||||
rotation: 1
|
rotation: 1
|
||||||
|
|||||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
|||||||
description: "Laser Gewehr: Schaden 40 \xB7 2 Sch\xFCsse/s. Baufl\xE4che: Mauer.
|
description: "Laser Gewehr: Schaden 40 \xB7 2 Sch\xFCsse/s. Baufl\xE4che: Mauer.
|
||||||
Werte vorl\xE4ufig."
|
Werte vorl\xE4ufig."
|
||||||
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
previewSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
gameplayPrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||||
baseSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
previewLayers:
|
||||||
basePrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
- sprite: {fileID: 0}
|
||||||
gameplayPrefab: {fileID: 0}
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 0
|
region: 0
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.36
|
baseRadius: 0.36
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 1
|
attackShape: 1
|
||||||
targets: 1
|
targets: 1
|
||||||
rotation: 0
|
rotation: 0
|
||||||
|
|||||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
|||||||
description: "Bouncer: Schaden 8 \xB7 2 Sch\xFCsse/s. Baufl\xE4che: Mauer. Werte
|
description: "Bouncer: Schaden 8 \xB7 2 Sch\xFCsse/s. Baufl\xE4che: Mauer. Werte
|
||||||
vorl\xE4ufig."
|
vorl\xE4ufig."
|
||||||
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
previewSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
gameplayPrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||||
baseSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
previewLayers:
|
||||||
basePrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
- sprite: {fileID: 0}
|
||||||
gameplayPrefab: {fileID: 0}
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 0
|
region: 0
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.36
|
baseRadius: 0.36
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 1
|
attackShape: 1
|
||||||
targets: 1
|
targets: 1
|
||||||
rotation: 0
|
rotation: 0
|
||||||
|
|||||||
@@ -23,16 +23,25 @@ MonoBehaviour:
|
|||||||
description: "Pusher: Schaden 8 \xB7 4 Sch\xFCsse/s. Baufl\xE4che: Mauer. Werte
|
description: "Pusher: Schaden 8 \xB7 4 Sch\xFCsse/s. Baufl\xE4che: Mauer. Werte
|
||||||
vorl\xE4ufig."
|
vorl\xE4ufig."
|
||||||
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
previewSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
gameplayPrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||||
baseSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
previewLayers:
|
||||||
basePrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
- sprite: {fileID: 0}
|
||||||
gameplayPrefab: {fileID: 0}
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 0
|
region: 0
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.36
|
baseRadius: 0.36
|
||||||
baseSize: {x: 0.5, y: 0.5}
|
baseSize: {x: 0.5, y: 0.5}
|
||||||
placementRadius: 0.6
|
placementRadius: 0.6
|
||||||
allowPlayerRotation: 0
|
allowPlayerRotation: 1
|
||||||
blocksMovement: 0
|
blocksMovement: 0
|
||||||
requirePathValidation: 0
|
requirePathValidation: 0
|
||||||
countsTowardWallLimit: 1
|
countsTowardWallLimit: 1
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 2
|
attackShape: 2
|
||||||
targets: 2
|
targets: 2
|
||||||
rotation: 1
|
rotation: 1
|
||||||
|
|||||||
@@ -23,16 +23,25 @@ MonoBehaviour:
|
|||||||
description: "Knocker: Schaden 8 \xB7 0,7 Sch\xFCsse/s. Baufl\xE4che: Mauer. Werte
|
description: "Knocker: Schaden 8 \xB7 0,7 Sch\xFCsse/s. Baufl\xE4che: Mauer. Werte
|
||||||
vorl\xE4ufig."
|
vorl\xE4ufig."
|
||||||
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
previewSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
gameplayPrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||||
baseSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
previewLayers:
|
||||||
basePrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
- sprite: {fileID: 0}
|
||||||
gameplayPrefab: {fileID: 0}
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 0
|
region: 0
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.36
|
baseRadius: 0.36
|
||||||
baseSize: {x: 0.5, y: 0.5}
|
baseSize: {x: 0.5, y: 0.5}
|
||||||
placementRadius: 0.6
|
placementRadius: 0.6
|
||||||
allowPlayerRotation: 0
|
allowPlayerRotation: 1
|
||||||
blocksMovement: 0
|
blocksMovement: 0
|
||||||
requirePathValidation: 0
|
requirePathValidation: 0
|
||||||
countsTowardWallLimit: 1
|
countsTowardWallLimit: 1
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 2
|
attackShape: 2
|
||||||
targets: 2
|
targets: 2
|
||||||
rotation: 1
|
rotation: 1
|
||||||
|
|||||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
|||||||
description: "Laser-Beam: Schaden 40/s \xB7 Kontinuierlich. Dauerverbrauch statt
|
description: "Laser-Beam: Schaden 40/s \xB7 Kontinuierlich. Dauerverbrauch statt
|
||||||
unendlicher Feuerrate. Werte vorl\xE4ufig."
|
unendlicher Feuerrate. Werte vorl\xE4ufig."
|
||||||
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
previewSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
gameplayPrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||||
baseSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
previewLayers:
|
||||||
basePrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
- sprite: {fileID: 0}
|
||||||
gameplayPrefab: {fileID: 0}
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 0
|
region: 0
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.36
|
baseRadius: 0.36
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 1
|
continuousAttack: 1
|
||||||
unlimitedRange: 1
|
unlimitedRange: 1
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 1
|
attackShape: 1
|
||||||
targets: 1
|
targets: 1
|
||||||
rotation: 0
|
rotation: 0
|
||||||
|
|||||||
@@ -23,16 +23,25 @@ MonoBehaviour:
|
|||||||
description: "Laser Shotgun: Schaden 20 \xB7 0,7 Sch\xFCsse/s. Baufl\xE4che: Mauer.
|
description: "Laser Shotgun: Schaden 20 \xB7 0,7 Sch\xFCsse/s. Baufl\xE4che: Mauer.
|
||||||
Werte vorl\xE4ufig."
|
Werte vorl\xE4ufig."
|
||||||
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
previewSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
gameplayPrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||||
baseSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
previewLayers:
|
||||||
basePrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
- sprite: {fileID: 0}
|
||||||
gameplayPrefab: {fileID: 0}
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 0
|
region: 0
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.36
|
baseRadius: 0.36
|
||||||
baseSize: {x: 0.5, y: 0.5}
|
baseSize: {x: 0.5, y: 0.5}
|
||||||
placementRadius: 0.6
|
placementRadius: 0.6
|
||||||
allowPlayerRotation: 0
|
allowPlayerRotation: 1
|
||||||
blocksMovement: 0
|
blocksMovement: 0
|
||||||
requirePathValidation: 0
|
requirePathValidation: 0
|
||||||
countsTowardWallLimit: 1
|
countsTowardWallLimit: 1
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 2
|
attackShape: 2
|
||||||
targets: 2
|
targets: 2
|
||||||
rotation: 1
|
rotation: 1
|
||||||
|
|||||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
|||||||
description: "Caltrops: Bodenfalle \xB7 5 Kontakte. Baufl\xE4che: Innenfeld. Werte
|
description: "Caltrops: Bodenfalle \xB7 5 Kontakte. Baufl\xE4che: Innenfeld. Werte
|
||||||
vorl\xE4ufig."
|
vorl\xE4ufig."
|
||||||
icon: {fileID: 0}
|
icon: {fileID: 0}
|
||||||
previewSprite: {fileID: 0}
|
|
||||||
baseSprite: {fileID: 0}
|
|
||||||
basePrefab: {fileID: 0}
|
|
||||||
gameplayPrefab: {fileID: 0}
|
gameplayPrefab: {fileID: 0}
|
||||||
|
previewLayers:
|
||||||
|
- sprite: {fileID: 0}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: 0}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 1
|
region: 1
|
||||||
baseShape: 1
|
baseShape: 1
|
||||||
baseRadius: 0.71
|
baseRadius: 0.71
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 4
|
attackShape: 4
|
||||||
targets: 2
|
targets: 2
|
||||||
rotation: 3
|
rotation: 3
|
||||||
|
|||||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
|||||||
description: "Generator: Erzeugt Energie \xB7 Kapazit\xE4t +100. Baufl\xE4che:
|
description: "Generator: Erzeugt Energie \xB7 Kapazit\xE4t +100. Baufl\xE4che:
|
||||||
Mauer. Werte vorl\xE4ufig."
|
Mauer. Werte vorl\xE4ufig."
|
||||||
icon: {fileID: 0}
|
icon: {fileID: 0}
|
||||||
previewSprite: {fileID: 0}
|
|
||||||
baseSprite: {fileID: 0}
|
|
||||||
basePrefab: {fileID: 0}
|
|
||||||
gameplayPrefab: {fileID: 0}
|
gameplayPrefab: {fileID: 0}
|
||||||
|
previewLayers:
|
||||||
|
- sprite: {fileID: 0}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: 0}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 0
|
region: 0
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.36
|
baseRadius: 0.36
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 0
|
attackShape: 0
|
||||||
targets: 0
|
targets: 0
|
||||||
rotation: 3
|
rotation: 3
|
||||||
|
|||||||
@@ -22,10 +22,19 @@ MonoBehaviour:
|
|||||||
summary: Sammelt Leichen
|
summary: Sammelt Leichen
|
||||||
description: "Sauger: Sammelt Leichen. Baufl\xE4che: Mauer. Werte vorl\xE4ufig."
|
description: "Sauger: Sammelt Leichen. Baufl\xE4che: Mauer. Werte vorl\xE4ufig."
|
||||||
icon: {fileID: 0}
|
icon: {fileID: 0}
|
||||||
previewSprite: {fileID: 0}
|
|
||||||
baseSprite: {fileID: 0}
|
|
||||||
basePrefab: {fileID: 0}
|
|
||||||
gameplayPrefab: {fileID: 0}
|
gameplayPrefab: {fileID: 0}
|
||||||
|
previewLayers:
|
||||||
|
- sprite: {fileID: 0}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: 0}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 0
|
region: 0
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.36
|
baseRadius: 0.36
|
||||||
@@ -50,6 +59,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 3
|
attackShape: 3
|
||||||
targets: 2
|
targets: 2
|
||||||
rotation: 3
|
rotation: 3
|
||||||
|
|||||||
@@ -22,10 +22,19 @@ MonoBehaviour:
|
|||||||
summary: Reflektiert Strahlen
|
summary: Reflektiert Strahlen
|
||||||
description: "Spiegel: Reflektiert Strahlen. Baufl\xE4che: Innenfeld. Werte vorl\xE4ufig."
|
description: "Spiegel: Reflektiert Strahlen. Baufl\xE4che: Innenfeld. Werte vorl\xE4ufig."
|
||||||
icon: {fileID: 0}
|
icon: {fileID: 0}
|
||||||
previewSprite: {fileID: 0}
|
|
||||||
baseSprite: {fileID: 0}
|
|
||||||
basePrefab: {fileID: 0}
|
|
||||||
gameplayPrefab: {fileID: 0}
|
gameplayPrefab: {fileID: 0}
|
||||||
|
previewLayers:
|
||||||
|
- sprite: {fileID: 0}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: 0}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 1
|
region: 1
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.25
|
baseRadius: 0.25
|
||||||
@@ -50,6 +59,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 0
|
attackShape: 0
|
||||||
targets: 0
|
targets: 0
|
||||||
rotation: 2
|
rotation: 2
|
||||||
|
|||||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
|||||||
description: "Reparaturtrupp: Repariert Schleusen. Baufl\xE4che: Schleuse. Werte
|
description: "Reparaturtrupp: Repariert Schleusen. Baufl\xE4che: Schleuse. Werte
|
||||||
vorl\xE4ufig."
|
vorl\xE4ufig."
|
||||||
icon: {fileID: 0}
|
icon: {fileID: 0}
|
||||||
previewSprite: {fileID: 0}
|
|
||||||
baseSprite: {fileID: 0}
|
|
||||||
basePrefab: {fileID: 0}
|
|
||||||
gameplayPrefab: {fileID: 0}
|
gameplayPrefab: {fileID: 0}
|
||||||
|
previewLayers:
|
||||||
|
- sprite: {fileID: 0}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: 0}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 2
|
region: 2
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.36
|
baseRadius: 0.36
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 0
|
attackShape: 0
|
||||||
targets: 5
|
targets: 5
|
||||||
rotation: 3
|
rotation: 3
|
||||||
|
|||||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
|||||||
description: "Bombardement: Fl\xE4chenangriff \xB7 120 s Cooldown. Baufl\xE4che:
|
description: "Bombardement: Fl\xE4chenangriff \xB7 120 s Cooldown. Baufl\xE4che:
|
||||||
Innenfeld. Werte vorl\xE4ufig."
|
Innenfeld. Werte vorl\xE4ufig."
|
||||||
icon: {fileID: 0}
|
icon: {fileID: 0}
|
||||||
previewSprite: {fileID: 0}
|
|
||||||
baseSprite: {fileID: 0}
|
|
||||||
basePrefab: {fileID: 0}
|
|
||||||
gameplayPrefab: {fileID: 0}
|
gameplayPrefab: {fileID: 0}
|
||||||
|
previewLayers:
|
||||||
|
- sprite: {fileID: 0}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: 0}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 1
|
region: 1
|
||||||
baseShape: 0
|
baseShape: 0
|
||||||
baseRadius: 0.36
|
baseRadius: 0.36
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 1
|
unlimitedRange: 1
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 3
|
attackShape: 3
|
||||||
targets: 4
|
targets: 4
|
||||||
rotation: 3
|
rotation: 3
|
||||||
|
|||||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
|||||||
description: "Barrikade: Blockiert Wege \xB7 30 s. Baufl\xE4che: Innenfeld. Werte
|
description: "Barrikade: Blockiert Wege \xB7 30 s. Baufl\xE4che: Innenfeld. Werte
|
||||||
vorl\xE4ufig."
|
vorl\xE4ufig."
|
||||||
icon: {fileID: 0}
|
icon: {fileID: 0}
|
||||||
previewSprite: {fileID: 0}
|
|
||||||
baseSprite: {fileID: 0}
|
|
||||||
basePrefab: {fileID: 0}
|
|
||||||
gameplayPrefab: {fileID: 0}
|
gameplayPrefab: {fileID: 0}
|
||||||
|
previewLayers:
|
||||||
|
- sprite: {fileID: 0}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 1
|
||||||
|
- sprite: {fileID: 0}
|
||||||
|
offset: {x: 0, y: 0}
|
||||||
|
scale: {x: 1, y: 1}
|
||||||
|
rotation: 0
|
||||||
|
rotateWithAim: 0
|
||||||
|
previewScale: 0
|
||||||
region: 1
|
region: 1
|
||||||
baseShape: 1
|
baseShape: 1
|
||||||
baseRadius: 1.59
|
baseRadius: 1.59
|
||||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
|||||||
ammoPerSecond: 0
|
ammoPerSecond: 0
|
||||||
continuousAttack: 0
|
continuousAttack: 0
|
||||||
unlimitedRange: 0
|
unlimitedRange: 0
|
||||||
|
minimumRange: 0
|
||||||
|
coverage: 0
|
||||||
|
forwardAngleOffset: 90
|
||||||
attackShape: 4
|
attackShape: 4
|
||||||
targets: 0
|
targets: 0
|
||||||
rotation: 2
|
rotation: 2
|
||||||
|
|||||||
+10985
-342
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,60 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace DepthsOfGreed.HandbookUI
|
||||||
|
{
|
||||||
|
public enum AttackCoverage { Automatic, Circle, Sector }
|
||||||
|
|
||||||
|
public static class AttackRangeGeometry
|
||||||
|
{
|
||||||
|
public static float CoverageAngle(CombatDefinition c)
|
||||||
|
{
|
||||||
|
if(c.coverage==AttackCoverage.Circle)return 360;
|
||||||
|
if(c.coverage==AttackCoverage.Sector || (c.rotation==RotationMode.Fixed && c.attackShape==AttackShape.Cone))
|
||||||
|
return Mathf.Clamp(c.coneAngle,1,360);
|
||||||
|
return 360;
|
||||||
|
}
|
||||||
|
// Shared optional targeting helper. No shots or damage are performed here.
|
||||||
|
public static bool CanReach(CombatDefinition c,Vector2 origin,Vector2 target,float baseAngleDegrees)
|
||||||
|
{
|
||||||
|
if(c==null)return false;
|
||||||
|
Vector2 delta=target-origin;float distance=delta.magnitude;
|
||||||
|
if(distance<Mathf.Max(0,c.minimumRange) || (!c.unlimitedRange && distance>Mathf.Max(0,c.range)))return false;
|
||||||
|
float coverage=CoverageAngle(c);
|
||||||
|
return coverage>=360 || distance<.0001f || Mathf.Abs(Mathf.DeltaAngle(baseAngleDegrees+c.forwardAngleOffset,Mathf.Atan2(delta.y,delta.x)*Mathf.Rad2Deg))<=coverage*.5f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class AttackRangePreview : MonoBehaviour
|
||||||
|
{
|
||||||
|
public LineRenderer outerEdge,minimumEdge,sectorLeft,sectorRight;
|
||||||
|
public bool showRange=true;
|
||||||
|
[Min(1)] public float unlimitedDirectionLength=10;
|
||||||
|
public Color rangeColor=new Color(.25f,.75f,1f,.35f);
|
||||||
|
public void Show(HandbookTowerEntry entry)
|
||||||
|
{
|
||||||
|
GetComponent<RangeArenaClip>()?.Configure(entry);
|
||||||
|
var c=entry?entry.combat:null;
|
||||||
|
bool ability=entry && entry.use==BuildUse.Ability;
|
||||||
|
bool visible=showRange && c!=null && (ability?c.areaRadius>0:c.range>0 || c.unlimitedRange);
|
||||||
|
gameObject.SetActive(visible);if(!visible)return;
|
||||||
|
float max=ability?c.areaRadius:c.unlimitedRange?unlimitedDirectionLength:c.range;
|
||||||
|
float min=ability?0:Mathf.Clamp(c.minimumRange,0,max);
|
||||||
|
float angle=ability?360:AttackRangeGeometry.CoverageAngle(c);
|
||||||
|
float offset=ability?0:c.forwardAngleOffset;
|
||||||
|
bool unlimited=!ability && c.unlimitedRange;
|
||||||
|
outerEdge.gameObject.SetActive(!unlimited);minimumEdge.gameObject.SetActive(min>0);sectorLeft.gameObject.SetActive(angle<360);sectorRight.gameObject.SetActive(angle<360);
|
||||||
|
Arc(outerEdge,max,angle,offset);Arc(minimumEdge,min,angle,offset);
|
||||||
|
sectorLeft.loop=sectorRight.loop=false;sectorLeft.positionCount=sectorRight.positionCount=2;
|
||||||
|
sectorLeft.SetPosition(0,Point(min,offset-angle*.5f));sectorLeft.SetPosition(1,Point(max,offset-angle*.5f));
|
||||||
|
sectorRight.SetPosition(0,Point(min,offset+angle*.5f));sectorRight.SetPosition(1,Point(max,offset+angle*.5f));
|
||||||
|
foreach(var line in new[]{outerEdge,minimumEdge,sectorLeft,sectorRight})line.startColor=line.endColor=rangeColor;
|
||||||
|
}
|
||||||
|
static Vector3 Point(float radius,float angle)=>new Vector3(Mathf.Cos(angle*Mathf.Deg2Rad)*radius,Mathf.Sin(angle*Mathf.Deg2Rad)*radius,0);
|
||||||
|
static void Arc(LineRenderer line,float radius,float angle,float offset)
|
||||||
|
{
|
||||||
|
int count=97;line.loop=angle>=360;line.positionCount=angle>=360?count-1:count;
|
||||||
|
for(int i=0;i<line.positionCount;i++)line.SetPosition(i,Point(radius,offset-angle*.5f+angle*i/(count-1)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: e2424492d0690534fb0a6e3fda8eea2a
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace DepthsOfGreed.HandbookUI
|
||||||
|
{
|
||||||
|
public sealed class BasementBattleOverview : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Tooltip("Optionale Komponente mit ICombatOverview. Keine eigene Kampf- oder Schleusenlogik.")]
|
||||||
|
[SerializeField] private MonoBehaviour combatBackend;
|
||||||
|
public TMP_Text floorText;
|
||||||
|
public Image[] gateIcons;
|
||||||
|
public TMP_Text[] gateLabels;
|
||||||
|
[Range(0,1)]public float criticalFraction=.25f;
|
||||||
|
private readonly long[] lastHits=new long[4];
|
||||||
|
private readonly float[] flashUntil=new float[4];
|
||||||
|
private readonly bool[] initialized=new bool[4];
|
||||||
|
private float nextRefresh;
|
||||||
|
private CombatOverviewState state;
|
||||||
|
private string lastFloor;
|
||||||
|
public void BindCombatBackend(MonoBehaviour backend)
|
||||||
|
{if(backend && !(backend is ICombatOverview)){Debug.LogError("Schleusenübersicht benötigt ICombatOverview.",backend);return;}combatBackend=backend;System.Array.Clear(initialized,0,4);nextRefresh=0;}
|
||||||
|
void Update()
|
||||||
|
{
|
||||||
|
if(Time.unscaledTime>=nextRefresh){nextRefresh=Time.unscaledTime+.15f;state=combatBackend && combatBackend is ICombatOverview source?source.ReadCombatOverview():null;}
|
||||||
|
if(lastFloor!=state?.floorName){System.Array.Clear(initialized,0,4);lastFloor=state?.floorName;}
|
||||||
|
floorText.text=string.IsNullOrEmpty(state?.floorName)?"Ebene —":state.floorName;
|
||||||
|
string[] directions={"N","O","S","W"};
|
||||||
|
for(int i=0;i<4;i++)
|
||||||
|
{
|
||||||
|
GateOverviewState gate=null;
|
||||||
|
if(state?.gates!=null)foreach(var item in state.gates)if(item!=null && (int)item.direction==i){gate=item;break;}
|
||||||
|
if(gate==null){gateLabels[i].text=directions[i]+" —";gateIcons[i].color=new Color(.5f,.5f,.5f);initialized[i]=false;continue;}
|
||||||
|
if(initialized[i] && gate.hitSequence>lastHits[i])flashUntil[i]=Time.unscaledTime+.65f;
|
||||||
|
initialized[i]=true;lastHits[i]=gate.hitSequence;
|
||||||
|
bool known=gate.maxHealth>0;float ratio=known?Mathf.Clamp01(gate.health/gate.maxHealth):1;
|
||||||
|
bool critical=known && ratio<=criticalFraction;
|
||||||
|
bool alarm=critical || gate.underAttack || Time.unscaledTime<flashUntil[i];
|
||||||
|
gateIcons[i].color=alarm?Color.Lerp(new Color(.5f,.12f,.1f),new Color(1,.4f,.2f),.5f+.5f*Mathf.Sin(Time.unscaledTime*9)):gate.isOpen?new Color(.8f,.6f,.2f):new Color(.3f,.3f,.3f);
|
||||||
|
gateLabels[i].text=directions[i]+" "+(known?Mathf.Max(0,gate.health).ToString("0"):"—")+(known && gate.health<=0?" ×":(gate.isOpen?" offen":"")+(gate.underAttack?" !":""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4419706a197fada4593f62a6ccb8a37a
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace DepthsOfGreed.HandbookUI
|
||||||
|
{
|
||||||
|
// Only rendering and input change; all GameObjects and gameplay keep running.
|
||||||
|
public sealed class BasementRoomView : MonoBehaviour
|
||||||
|
{
|
||||||
|
public Canvas roomCanvas;
|
||||||
|
public GraphicRaycaster roomRaycaster;
|
||||||
|
public Camera roomCamera;
|
||||||
|
public RectTransform[] horizontalSurfaces;
|
||||||
|
public RectTransform leftBoundary,rightBoundary;
|
||||||
|
public Vector2 referenceSize=new Vector2(1920,1080);
|
||||||
|
public bool IsVisible {get;private set;}
|
||||||
|
public void SetVisible(bool visible)
|
||||||
|
{
|
||||||
|
IsVisible=visible;
|
||||||
|
roomCanvas.enabled=visible;roomRaycaster.enabled=visible;roomCamera.enabled=visible;
|
||||||
|
Fit();
|
||||||
|
}
|
||||||
|
void Awake()=>SetVisible(false);
|
||||||
|
void LateUpdate(){if(IsVisible)Fit();}
|
||||||
|
void Fit()
|
||||||
|
{
|
||||||
|
float scale=Mathf.Abs(roomCanvas.transform.lossyScale.y);
|
||||||
|
roomCamera.orthographicSize=Mathf.Max(referenceSize.y*.5f,referenceSize.x*.5f/Mathf.Max(.1f,roomCamera.aspect))*scale;
|
||||||
|
float width=Mathf.Max(referenceSize.x,roomCamera.orthographicSize*2*roomCamera.aspect/Mathf.Max(.00001f,scale));
|
||||||
|
((RectTransform)roomCanvas.transform).sizeDelta=new Vector2(width,referenceSize.y);
|
||||||
|
if(horizontalSurfaces!=null)foreach(var surface in horizontalSurfaces)if(surface)surface.sizeDelta=new Vector2(width,surface.sizeDelta.y);
|
||||||
|
if(leftBoundary)leftBoundary.anchoredPosition=new Vector2(-width*.5f+10,leftBoundary.anchoredPosition.y);
|
||||||
|
if(rightBoundary)rightBoundary.anchoredPosition=new Vector2(width*.5f-10,rightBoundary.anchoredPosition.y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: a21ab60c5c4c98647bd5c8a6374c550d
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
namespace DepthsOfGreed.HandbookUI
|
||||||
|
{
|
||||||
|
public sealed class BasementSupplyMonitor:MonoBehaviour
|
||||||
|
{
|
||||||
|
public SupplyBasementController source;public TMP_Text text;float next;
|
||||||
|
void Update(){if(Time.unscaledTime<next)return;next=Time.unscaledTime+.3f;var state=source.SupplyBackend?.ReadState();var result="VERSORGUNG\n";string[] names={"Physisch","Zellen","Sonder"};for(int i=0;i<3;i++){SupplyChannelState c=null;if(state?.channels!=null)foreach(var x in state.channels)if((int)x.kind==i)c=x;result+="\n"+names[i]+"\n"+(c==null?"—":c.buffer.ToString("0")+" / "+c.bufferCapacity.ToString("0"))+" · "+HandbookStatisticsController.FormatRate(c?.consumedPerSecond,true)+"\n"+HandbookStatisticsController.Estimate(c==null?0:c.buffer,c?.consumedPerSecond);}text.text=result;}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: fbfb71b7cc3d28d4ba8986b421e0ce57
|
||||||
@@ -38,7 +38,7 @@ public static class HandbookInspectorValidation
|
|||||||
var menu=UnityEngine.Object.FindFirstObjectByType<BuildMenuController>();int[] expected={6,6,3,4};for(int i=0;i<4;i++){menu.SelectCategory(i);if(menu.GetComponentsInChildren<TowerCardView>().Length!=expected[i])throw new Exception("Category count incorrect: "+i);}
|
var menu=UnityEngine.Object.FindFirstObjectByType<BuildMenuController>();int[] expected={6,6,3,4};for(int i=0;i<4;i++){menu.SelectCategory(i);if(menu.GetComponentsInChildren<TowerCardView>().Length!=expected[i])throw new Exception("Category count incorrect: "+i);}
|
||||||
var p=root.GetComponent<TestPlacementController>();var catalog=AssetDatabase.LoadAssetAtPath<HandbookTowerCatalog>("Assets/HandbookData/TestTurmkatalog.asset");var gun=catalog.towers.First(t=>t.id=="ammo_0");var energy=catalog.towers.First(t=>t.id=="energy_0");
|
var p=root.GetComponent<TestPlacementController>();var catalog=AssetDatabase.LoadAssetAtPath<HandbookTowerCatalog>("Assets/HandbookData/TestTurmkatalog.asset");var gun=catalog.towers.First(t=>t.id=="ammo_0");var energy=catalog.towers.First(t=>t.id=="energy_0");
|
||||||
if(!p.Backend.ExecuteBuild(new BuildRequest(gun,new Vector3(3.5f,0,0))).Allowed||!p.Backend.ExecuteBuild(new BuildRequest(energy,new Vector3(0,3.5f,0))).Allowed)throw new Exception("Base placement failed");
|
if(!p.Backend.ExecuteBuild(new BuildRequest(gun,new Vector3(3.5f,0,0))).Allowed||!p.Backend.ExecuteBuild(new BuildRequest(energy,new Vector3(0,3.5f,0))).Allowed)throw new Exception("Base placement failed");
|
||||||
var obj=p.Backend.BuiltObjects[0].instance;var renderer=obj.GetComponent<SpriteRenderer>();if(!renderer || !renderer.sprite || (gun.baseSprite && renderer.sprite!=gun.baseSprite) || renderer.bounds.size.x>0.8f)throw new Exception("Base sprite or placeholder scaling incorrect");
|
var obj=p.Backend.BuiltObjects[0].instance;var renderer=obj.GetComponent<SpriteRenderer>();if(!renderer || !renderer.sprite || renderer.bounds.size.x>0.8f)throw new Exception("Base sprite or placeholder scaling incorrect");
|
||||||
int before=p.Backend.Energy;if(!p.Backend.ExecuteUpgrade(p.Backend.BuiltObjects[0].instanceId).Allowed||p.Backend.Energy>=before)throw new Exception("Upgrade failed");
|
int before=p.Backend.Energy;if(!p.Backend.ExecuteUpgrade(p.Backend.BuiltObjects[0].instanceId).Allowed||p.Backend.Energy>=before)throw new Exception("Upgrade failed");
|
||||||
c.Open(2);var upgrades=UnityEngine.Object.FindFirstObjectByType<HandbookUpgradesController>();if(upgrades.VisibleUpgradeCount!=2)throw new Exception("Upgrade listing failed");
|
c.Open(2);var upgrades=UnityEngine.Object.FindFirstObjectByType<HandbookUpgradesController>();if(upgrades.VisibleUpgradeCount!=2)throw new Exception("Upgrade listing failed");
|
||||||
c.Open(3);var index=UnityEngine.Object.FindFirstObjectByType<HandbookIndexController>();if(index.VisibleEntryCount!=24)throw new Exception("All entries not displayed");int[] counts={24,12,3,4,5};for(int i=0;i<5;i++){index.SelectFilter(i);if(index.VisibleEntryCount!=counts[i])throw new Exception("Index filter count incorrect");}
|
c.Open(3);var index=UnityEngine.Object.FindFirstObjectByType<HandbookIndexController>();if(index.VisibleEntryCount!=24)throw new Exception("All entries not displayed");int[] counts={24,12,3,4,5};for(int i=0;i<5;i++){index.SelectFilter(i);if(index.VisibleEntryCount!=counts[i])throw new Exception("Index filter count incorrect");}
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.SceneManagement;
|
||||||
|
using DepthsOfGreed.HandbookUI;
|
||||||
|
public static class PreviewLayersValidation
|
||||||
|
{
|
||||||
|
static void Check(bool ok,string message){if(!ok)throw new Exception(message);}
|
||||||
|
public static void Validate(){GameObject root=null,source=null;Texture2D texture=null;Sprite a=null,b=null;HandbookTowerEntry t=null;try{
|
||||||
|
EditorSceneManager.OpenScene("Assets/Scenes/Tim_Test_UI.unity");
|
||||||
|
root=new GameObject("TEMP_LayersTest");var preview=root.AddComponent<LayeredTowerPreview>();texture=new Texture2D(32,32);a=Sprite.Create(texture,new Rect(0,0,32,32),new Vector2(.25f,.75f),32);b=Sprite.Create(texture,new Rect(0,0,32,32),new Vector2(.8f,.1f),32);
|
||||||
|
t=ScriptableObject.CreateInstance<HandbookTowerEntry>();t.id="layer-test";t.previewLayers=new[]{new PreviewSpriteLayer{sprite=a},new PreviewSpriteLayer{sprite=b,rotateWithAim=false}};preview.Show(t,a);
|
||||||
|
var layers=root.GetComponentsInChildren<SpriteRenderer>();Check(layers.Length==2 && layers[0].sortingOrder>layers[1].sortingOrder,"Front-to-back order");Check(layers[0].transform.position==layers[1].transform.position,"Different sprite pivots share mouse anchor");Check(layers[0].transform.localScale==layers[1].transform.localScale,"Shared scale preserves proportions");
|
||||||
|
t.baseShape=BaseShape.Rectangle;t.baseSize=new Vector2(3,1);t.previewLayers=new PreviewSpriteLayer[2];preview.Show(t,a);Check(Mathf.Abs(layers[0].transform.localScale.x/layers[0].transform.localScale.y-3)<.01f,"Rectangular fallback retains base proportions");t.baseShape=BaseShape.Circle;
|
||||||
|
var gameplay=root.AddComponent<PrototypeHandbookGameplay>();gameplay.Initialize(root.transform,Vector3.zero,3,4,null);source=new GameObject("ActualGameplayPrefab",typeof(SpriteRenderer));source.transform.localScale=Vector3.one*1.2f;t.gameplayPrefab=source;t.baseRadius=.2f;
|
||||||
|
Check(gameplay.ExecuteBuild(new BuildRequest(t,new Vector3(3.5f,0,0),30)).Allowed,"Prefab placement");Check(gameplay.BuiltObjects[0].instance.transform.localScale==source.transform.localScale,"Assigned prefab instantiated without rescaling");
|
||||||
|
Check(Shader.Find("Handbook/ArenaRange"),"Arena clipping shader available");
|
||||||
|
UnityEngine.Object.DestroyImmediate(root);UnityEngine.Object.DestroyImmediate(source);UnityEngine.Object.DestroyImmediate(t);UnityEngine.Object.DestroyImmediate(a);UnityEngine.Object.DestroyImmediate(b);UnityEngine.Object.DestroyImmediate(texture);Debug.Log("PREVIEW_LAYERS_VALIDATION_OK");EditorApplication.Exit(0);
|
||||||
|
}catch(Exception e){Debug.LogException(e);EditorApplication.Exit(1);}}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1efe7a0c358869143800296d28875a94
|
||||||
@@ -31,7 +31,7 @@ public static class SupplyBasementValidation
|
|||||||
{
|
{
|
||||||
c=UnityEngine.Object.FindFirstObjectByType<SupplyBasementController>();
|
c=UnityEngine.Object.FindFirstObjectByType<SupplyBasementController>();
|
||||||
Assert(c && c.connectionButtons.Length==8 && c.priorityButtons.Length==3,"Authored references");
|
Assert(c && c.connectionButtons.Length==8 && c.priorityButtons.Length==3,"Authored references");
|
||||||
Assert(!c.basementView.activeSelf && c.handbookRoot.activeSelf,"Starts on tower floor");
|
Assert((c.roomView ? !c.roomView.IsVisible && c.basementView.activeSelf : !c.basementView.activeSelf) && c.handbookRoot.activeSelf,"Starts on tower floor; room remains active");
|
||||||
count=Count(c);bookScale=c.handbook.transform.Find("OpenBook").localScale;
|
count=Count(c);bookScale=c.handbook.transform.Find("OpenBook").localScale;
|
||||||
var catalog=AssetDatabase.LoadAssetAtPath<HandbookTowerCatalog>("Assets/HandbookData/TestTurmkatalog.asset");
|
var catalog=AssetDatabase.LoadAssetAtPath<HandbookTowerCatalog>("Assets/HandbookData/TestTurmkatalog.asset");
|
||||||
var result=c.placement.Backend.ExecuteBuild(new BuildRequest(catalog.towers.First(t=>t.id=="ammo_0"),new Vector3(3.5f,0,0)));
|
var result=c.placement.Backend.ExecuteBuild(new BuildRequest(catalog.towers.First(t=>t.id=="ammo_0"),new Vector3(3.5f,0,0)));
|
||||||
@@ -73,17 +73,19 @@ public static class SupplyBasementValidation
|
|||||||
catch(Exception e){EditorApplication.update-=run;Debug.LogException(e);EditorApplication.Exit(1);}
|
catch(Exception e){EditorApplication.update-=run;Debug.LogException(e);EditorApplication.Exit(1);}
|
||||||
};EditorApplication.update+=run;
|
};EditorApplication.update+=run;
|
||||||
}
|
}
|
||||||
static int Count(SupplyBasementController c)=>c.GetComponentInParent<Canvas>().GetComponentsInChildren<RectTransform>(true).Count(t=>!t.name.StartsWith("TMP"));
|
static int Count(SupplyBasementController c)=>c.GetComponentInParent<Canvas>().GetComponentsInChildren<RectTransform>(true).Concat(c.basementView.GetComponentsInChildren<RectTransform>(true)).Distinct().Count(t=>!t.name.StartsWith("TMP"));
|
||||||
static void Assert(bool condition,string message){if(!condition)throw new Exception(message);}
|
static void Assert(bool condition,string message){if(!condition)throw new Exception(message);}
|
||||||
static void Capture(SupplyBasementController c)
|
static void Capture(SupplyBasementController c)
|
||||||
{
|
{
|
||||||
var camera=Camera.main;var canvas=c.GetComponentInParent<Canvas>();
|
var camera=c.roomView?c.roomView.roomCamera:Camera.main;var canvas=c.GetComponentInParent<Canvas>();
|
||||||
var rt=new RenderTexture(1920,1080,24);rt.Create();camera.targetTexture=rt;camera.aspect=1920f/1080;
|
var rt=new RenderTexture(1920,1080,24);rt.Create();camera.targetTexture=rt;camera.aspect=1920f/1080;
|
||||||
canvas.renderMode=RenderMode.ScreenSpaceCamera;canvas.worldCamera=camera;canvas.planeDistance=1;canvas.sortingOrder=200;
|
canvas.renderMode=RenderMode.ScreenSpaceCamera;canvas.worldCamera=camera;canvas.planeDistance=1;canvas.sortingOrder=200;
|
||||||
Canvas.ForceUpdateCanvases();RenderPipeline.SubmitRenderRequest(camera,new UniversalRenderPipeline.SingleCameraRequest{destination=rt});
|
Canvas.ForceUpdateCanvases();RenderPipeline.SubmitRenderRequest(camera,new UniversalRenderPipeline.SingleCameraRequest{destination=rt});
|
||||||
var previous=RenderTexture.active;RenderTexture.active=rt;var tex=new Texture2D(1920,1080,TextureFormat.RGB24,false);tex.ReadPixels(new Rect(0,0,1920,1080),0,0);tex.Apply();RenderTexture.active=previous;
|
var previous=RenderTexture.active;RenderTexture.active=rt;var tex=new Texture2D(1920,1080,TextureFormat.RGB24,false);tex.ReadPixels(new Rect(0,0,1920,1080),0,0);tex.Apply();RenderTexture.active=previous;
|
||||||
var folder=Path.Combine(Path.GetDirectoryName(Application.dataPath),"Temp","HandbookValidation");
|
var folder=Path.Combine(Path.GetDirectoryName(Application.dataPath),"Logs","HandbookValidation");
|
||||||
Directory.CreateDirectory(folder);File.WriteAllBytes(Path.Combine(folder,"basement-preview.png"),tex.EncodeToPNG());
|
Directory.CreateDirectory(folder);File.WriteAllBytes(Path.Combine(folder,"basement-preview.png"),tex.EncodeToPNG());
|
||||||
UnityEngine.Object.DestroyImmediate(tex);camera.targetTexture=null;rt.Release();UnityEngine.Object.DestroyImmediate(rt);canvas.renderMode=RenderMode.ScreenSpaceOverlay;
|
UnityEngine.Object.DestroyImmediate(tex);camera.targetTexture=null;rt.Release();UnityEngine.Object.DestroyImmediate(rt);canvas.renderMode=RenderMode.ScreenSpaceOverlay;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.SceneManagement;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Rendering;
|
||||||
|
using UnityEngine.Rendering.Universal;
|
||||||
|
using UnityEngine.EventSystems;
|
||||||
|
using DepthsOfGreed.HandbookUI;
|
||||||
|
|
||||||
|
[InitializeOnLoad]
|
||||||
|
public static class UINotesValidation
|
||||||
|
{
|
||||||
|
static UINotesValidation(){if(SessionState.GetBool("NotesValidation",false))EditorApplication.playModeStateChanged+=Ready;}
|
||||||
|
public static void Validate(){EditorSceneManager.OpenScene("Assets/Scenes/Tim_Test_UI.unity");SessionState.SetBool("NotesValidation",true);EditorApplication.playModeStateChanged+=Ready;EditorApplication.EnterPlaymode();}
|
||||||
|
static void Check(bool condition,string message){if(!condition)throw new Exception(message);}
|
||||||
|
static void Ready(PlayModeStateChange state)
|
||||||
|
{
|
||||||
|
if(state!=PlayModeStateChange.EnteredPlayMode)return;SessionState.SetBool("NotesValidation",false);EditorApplication.playModeStateChanged-=Ready;
|
||||||
|
int stage=0;double due=EditorApplication.timeSinceStartup+1;SupplyBasementController c=null;HandbookStatisticsController stats=null;SupplyContractTestBackend supply=null;UIOverviewTestBackend battle=null;BasementBattleOverview monitor=null;HandbookTowerCatalog catalog=null;float truckX=0;Camera main=null;
|
||||||
|
EditorApplication.CallbackFunction run=null;run=()=>
|
||||||
|
{
|
||||||
|
if(EditorApplication.timeSinceStartup<due)return;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if(stage==0)
|
||||||
|
{
|
||||||
|
c=UnityEngine.Object.FindFirstObjectByType<SupplyBasementController>();main=Camera.main;catalog=AssetDatabase.LoadAssetAtPath<HandbookTowerCatalog>("Assets/HandbookData/TestTurmkatalog.asset");
|
||||||
|
Check(c.roomView && c.roomView.roomCanvas.renderMode==RenderMode.WorldSpace && c.roomView.roomCanvas.transform.position.x>30,"Room exists outside game area");
|
||||||
|
Check(c.basementView.activeInHierarchy && !c.roomView.IsVisible && main.enabled,"Room active while hidden, main camera active");
|
||||||
|
var combat=new CombatDefinition{range=5,minimumRange=1.5f,rotation=RotationMode.Free,attackShape=AttackShape.Cone,coneAngle=60,forwardAngleOffset=90};
|
||||||
|
Check(AttackRangeGeometry.CoverageAngle(combat)==360,"Rotating cone covers full circle");
|
||||||
|
Check(!AttackRangeGeometry.CanReach(combat,Vector2.zero,Vector2.up,0) && AttackRangeGeometry.CanReach(combat,Vector2.zero,Vector2.up*3,0),"Minimum range respected");
|
||||||
|
combat.coverage=AttackCoverage.Sector;Check(AttackRangeGeometry.CanReach(combat,Vector2.zero,Vector2.up*3,0) && !AttackRangeGeometry.CanReach(combat,Vector2.zero,Vector2.right*3,0),"Cone angle and orientation");
|
||||||
|
combat.coverage=AttackCoverage.Circle;combat.unlimitedRange=true;Check(AttackRangeGeometry.CanReach(combat,Vector2.zero,Vector2.right*1000,0),"Unlimited range has no false maximum");
|
||||||
|
truckX=c.truck.anchoredPosition.x;
|
||||||
|
typeof(SupplyBasementController).GetField("truckPauseUntil",System.Reflection.BindingFlags.Instance|System.Reflection.BindingFlags.NonPublic).SetValue(c,0f);
|
||||||
|
stage=1;due=EditorApplication.timeSinceStartup+.8;
|
||||||
|
}
|
||||||
|
else if(stage==1)
|
||||||
|
{
|
||||||
|
Check(Mathf.Abs(c.truck.anchoredPosition.x-truckX)>.1f,"Truck preview continues outside basement");
|
||||||
|
var howitzer=catalog.towers.First(t=>t.id=="ammo_4");Check(howitzer.combat.minimumRange>0,"Howitzer minimum seeded");
|
||||||
|
var range=UnityEngine.Object.FindFirstObjectByType<AttackRangePreview>(FindObjectsInactive.Include);
|
||||||
|
c.handbook.Open(0);Check(c.placement.Begin(howitzer,main.WorldToScreenPoint(new Vector3(3.5f,0,0))),"Begin range preview");
|
||||||
|
Check(range.gameObject.activeInHierarchy && range.minimumEdge.gameObject.activeSelf && range.outerEdge.loop,"Annular placement preview");
|
||||||
|
stage=2;due=EditorApplication.timeSinceStartup+.7;
|
||||||
|
}
|
||||||
|
else if(stage==2)
|
||||||
|
{
|
||||||
|
c.placement.Move(main.WorldToScreenPoint(new Vector3(3.5f,0,0)));
|
||||||
|
Capture(main,c.handbook.GetComponentInParent<Canvas>(),c.GetComponentInParent<Canvas>(),"range-preview.png");c.placement.Cancel();
|
||||||
|
supply=new GameObject("TEST_ONLY_Supply").AddComponent<SupplyContractTestBackend>();
|
||||||
|
supply.state.channels=new[]{new SupplyChannelState{kind=SupplyKind.Physical,storage=180,storageCapacity=300,buffer=60,bufferCapacity=100,consumedPerSecond=2}};
|
||||||
|
supply.state.towers=new[]{new TowerSupplyState{type=catalog.towers.First(t=>t.id=="ammo_0"),instanceId=811,magazine=12,magazineCapacity=30,consumedPerSecond=2}};
|
||||||
|
c.BindSupplyBackend(supply);c.handbook.Open(1);stats=UnityEngine.Object.FindFirstObjectByType<HandbookStatisticsController>();stats.Refresh();
|
||||||
|
Check(stats.channelRates[0].text.Contains("120 / min") && stats.channelTimes[0].text.Contains("00:30"),"Measured rates and reserve estimate");
|
||||||
|
Check(stats.towerNames[0].text.Contains("811") && stats.towerMagazines[0].text=="12 / 30","Per-instance magazine and rate");
|
||||||
|
stats.SetUnit(false);stats.Refresh();Check(stats.channelRates[0].text.Contains("2 / s"),"Rate unit conversion");
|
||||||
|
Check(HandbookStatisticsController.FormatRate(null,true)=="—" && HandbookStatisticsController.Estimate(60,0)=="kein Verbrauch","Unknown differs from zero");
|
||||||
|
stats.SetUnit(true);stats.Refresh();stage=3;due=EditorApplication.timeSinceStartup+.4;
|
||||||
|
}
|
||||||
|
else if(stage==3)
|
||||||
|
{
|
||||||
|
Capture(main,c.handbook.GetComponentInParent<Canvas>(),c.GetComponentInParent<Canvas>(),"statistics-preview.png");
|
||||||
|
battle=new GameObject("TEST_ONLY_CombatOverview").AddComponent<UIOverviewTestBackend>();
|
||||||
|
battle.state.floorName="Ebene 2";battle.state.gates=new[]{new GateOverviewState{direction=GateDirection.North,health=100,maxHealth=100},new GateOverviewState{direction=GateDirection.East,health=80,maxHealth=100,isOpen=true},new GateOverviewState{direction=GateDirection.South,health=100,maxHealth=100},new GateOverviewState{direction=GateDirection.West,health=100,maxHealth=100}};
|
||||||
|
monitor=UnityEngine.Object.FindFirstObjectByType<BasementBattleOverview>();monitor.BindCombatBackend(battle);monitor.SendMessage("Update");
|
||||||
|
Check(monitor.gateLabels[0].text.Contains("100") && monitor.gateLabels[1].text.Contains("offen"),"Gate state and open gate separate");
|
||||||
|
battle.state.gates[0].health=20;battle.state.gates[0].underAttack=true;battle.state.gates[0].hitSequence++;
|
||||||
|
c.UseLift();stage=4;due=EditorApplication.timeSinceStartup+1.7;
|
||||||
|
}
|
||||||
|
else if(stage==4)
|
||||||
|
{
|
||||||
|
Check(c.IsInBasement && c.roomView.IsVisible && c.basementView.activeInHierarchy && c.roomView.roomCamera.enabled && main.enabled,"Only view changes, both rooms stay active");
|
||||||
|
Check(monitor.gateLabels[0].text.Contains("20") && monitor.gateLabels[0].text.Contains("!"),"Gate damage appears in basement");
|
||||||
|
Check(monitor.gateIcons[0].color.r>monitor.gateIcons[0].color.g,"Critical gate warning color");
|
||||||
|
var pointer=new PointerEventData(EventSystem.current){position=RectTransformUtility.WorldToScreenPoint(c.roomView.roomCamera,c.connectionButtons[0].transform.position)};
|
||||||
|
var hits=new List<RaycastResult>();EventSystem.current.RaycastAll(pointer,hits);
|
||||||
|
Check(hits.Any(hit=>hit.gameObject==c.connectionButtons[0].gameObject),"World-space room buttons receive UI pointer hits");
|
||||||
|
c.roomView.roomCamera.aspect=2.5f;c.roomView.SendMessage("LateUpdate");
|
||||||
|
Check(((RectTransform)c.roomView.roomCanvas.transform).rect.width>=2699,"Room expands to wide screen");
|
||||||
|
Capture(c.roomView.roomCamera,null,c.GetComponentInParent<Canvas>(),"basement-monitor-preview.png");
|
||||||
|
c.UseLift();stage=5;due=EditorApplication.timeSinceStartup+1.7;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Check(!c.IsInBasement && !c.roomView.roomCamera.enabled && c.basementView.activeInHierarchy && main.enabled,"Return keeps room and main simulation active");
|
||||||
|
Debug.Log("UI_NOTES_VALIDATION_OK: separate active room, background preview, attack/minimum/sector ranges, statistics, unit conversion and gate alerts.");EditorApplication.update-=run;EditorApplication.Exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(Exception e){EditorApplication.update-=run;Debug.LogException(e);EditorApplication.Exit(1);}
|
||||||
|
};EditorApplication.update+=run;
|
||||||
|
}
|
||||||
|
static void Capture(Camera camera,Canvas book,Canvas lift,string filename)
|
||||||
|
{
|
||||||
|
if(SystemInfo.graphicsDeviceType==GraphicsDeviceType.Null)return;
|
||||||
|
var target=new RenderTexture(1920,1080,24);target.Create();camera.targetTexture=target;camera.aspect=1920f/1080;
|
||||||
|
var room=camera.GetComponentInParent<BasementRoomView>();if(room)room.SendMessage("LateUpdate");
|
||||||
|
int oldMask=camera.cullingMask;camera.cullingMask|=1;
|
||||||
|
if(book){book.renderMode=RenderMode.ScreenSpaceCamera;book.worldCamera=camera;book.planeDistance=1;book.sortingOrder=100;}
|
||||||
|
lift.renderMode=RenderMode.ScreenSpaceCamera;lift.worldCamera=camera;lift.planeDistance=.5f;lift.sortingOrder=200;
|
||||||
|
Canvas.ForceUpdateCanvases();RenderPipeline.SubmitRenderRequest(camera,new UniversalRenderPipeline.SingleCameraRequest{destination=target});
|
||||||
|
var previous=RenderTexture.active;RenderTexture.active=target;var texture=new Texture2D(1920,1080,TextureFormat.RGB24,false);texture.ReadPixels(new Rect(0,0,1920,1080),0,0);texture.Apply();RenderTexture.active=previous;
|
||||||
|
var folder=Path.Combine(Path.GetDirectoryName(Application.dataPath),"Logs","HandbookValidation");Directory.CreateDirectory(folder);File.WriteAllBytes(Path.Combine(folder,filename),texture.EncodeToPNG());
|
||||||
|
UnityEngine.Object.DestroyImmediate(texture);camera.targetTexture=null;camera.cullingMask=oldMask;target.Release();UnityEngine.Object.DestroyImmediate(target);if(book)book.renderMode=RenderMode.ScreenSpaceOverlay;lift.renderMode=RenderMode.ScreenSpaceOverlay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1f370da3130a64348937147e95cf6d8f
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEditor.SceneManagement;
|
||||||
|
using UnityEngine;
|
||||||
|
using DepthsOfGreed.HandbookUI;
|
||||||
|
|
||||||
|
public static class WallBuildAreaValidation
|
||||||
|
{
|
||||||
|
static void Check(bool condition,string message){if(!condition)throw new Exception(message);}
|
||||||
|
public static void Validate()
|
||||||
|
{
|
||||||
|
GameObject test=null;HandbookTowerEntry tower=null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EditorSceneManager.OpenScene("Assets/Scenes/Tim_Test_UI.unity");
|
||||||
|
var area=UnityEngine.Object.FindFirstObjectByType<WallBuildArea>();
|
||||||
|
Check(area && area.TryGetRadii(out _,out _,out _),"Scene has configured build boundaries");
|
||||||
|
Check(new SerializedObject(UnityEngine.Object.FindFirstObjectByType<TestPlacementController>()).FindProperty("buildArea").objectReferenceValue==area,"Inspector reference wired");
|
||||||
|
test=new GameObject("TEMP_WallAreaValidation");var settings=test.AddComponent<WallBuildArea>();
|
||||||
|
settings.innerBoundarySprite=area.innerBoundarySprite;settings.outerBoundarySprite=area.outerBoundarySprite;settings.boundarySource=WallBoundarySource.SpriteRenderers;
|
||||||
|
string oldInner=settings.innerBoundarySprite.name,oldOuter=settings.outerBoundarySprite.name;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
settings.innerBoundarySprite.name="ArbitraryInnerObject";settings.outerBoundarySprite.name="ArbitraryOuterObject";
|
||||||
|
Check(settings.TryGetRadii(out float a,out float b,out _) && b>a,"Sprite assignment works after renaming");
|
||||||
|
}
|
||||||
|
finally{settings.innerBoundarySprite.name=oldInner;settings.outerBoundarySprite.name=oldOuter;}
|
||||||
|
settings.boundarySource=WallBoundarySource.CustomRadii;settings.innerBoundarySprite=null;settings.outerBoundarySprite=null;settings.innerRadius=1;settings.outerRadius=2;
|
||||||
|
settings.transform.localScale=Vector3.one*5;
|
||||||
|
Check(settings.TryGetRadii(out float inner,out float outer,out _) && inner==1 && outer==2,"Custom world radii ignore missing sprites and transform scale");
|
||||||
|
var prototype=test.AddComponent<PrototypeHandbookGameplay>();prototype.Initialize(test.transform,Vector3.zero,inner,outer,null);
|
||||||
|
var catalog=AssetDatabase.LoadAssetAtPath<HandbookTowerCatalog>("Assets/HandbookData/TestTurmkatalog.asset");tower=UnityEngine.Object.Instantiate(catalog.towers.First(t=>t.id=="ammo_0"));tower.baseShape=BaseShape.Circle;tower.baseRadius=.2f;
|
||||||
|
Check(prototype.CheckBuild(new BuildRequest(tower,new Vector3(1.5f,0,0))).Allowed,"Custom ring accepts fitting base");
|
||||||
|
Check(!prototype.CheckBuild(new BuildRequest(tower,new Vector3(1.1f,0,0))).Allowed,"Base must clear inner edge");
|
||||||
|
Check(!prototype.CheckBuild(new BuildRequest(tower,new Vector3(1.9f,0,0))).Allowed,"Base must clear outer edge");
|
||||||
|
settings.innerRadius=2;Check(!settings.TryGetRadii(out _,out _,out _),"Invalid custom ring rejected");
|
||||||
|
settings.boundarySource=WallBoundarySource.SpriteRenderers;Check(!settings.TryGetRadii(out _,out _,out _),"Missing sprite references rejected");
|
||||||
|
UnityEngine.Object.DestroyImmediate(tower);UnityEngine.Object.DestroyImmediate(test);
|
||||||
|
Debug.Log("WALL_AREA_VALIDATION_OK: inspector binding, renamed sprites, custom radii, invalid values, placement and edge clearance.");EditorApplication.Exit(0);
|
||||||
|
}
|
||||||
|
catch(Exception e){if(tower)UnityEngine.Object.DestroyImmediate(tower);if(test)UnityEngine.Object.DestroyImmediate(test);Debug.LogException(e);EditorApplication.Exit(1);}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8f68c3932a0690f4ca3434bf439c096d
|
||||||
@@ -17,6 +17,12 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
[Tooltip("Used for continuous weapons instead of damagePerHit / ammoPerShot.")]
|
[Tooltip("Used for continuous weapons instead of damagePerHit / ammoPerShot.")]
|
||||||
[Min(0)] public float continuousDamagePerSecond, ammoPerSecond;
|
[Min(0)] public float continuousDamagePerSecond, ammoPerSecond;
|
||||||
public bool continuousAttack, unlimitedRange;
|
public bool continuousAttack, unlimitedRange;
|
||||||
|
[Header("Angriffsbereich / Bauvorschau")]
|
||||||
|
[Min(0)] public float minimumRange;
|
||||||
|
[Tooltip("Automatic: fest gerichtete Kegel als Sektor, frei drehbare Waffen rundum.")]
|
||||||
|
public AttackCoverage coverage = AttackCoverage.Automatic;
|
||||||
|
[Tooltip("Vorwärtsrichtung relativ zur Turmrotation, Grad in der XY-Ebene (90 = oben).")]
|
||||||
|
public float forwardAngleOffset = 90;
|
||||||
public AttackShape attackShape;
|
public AttackShape attackShape;
|
||||||
public TargetMode targets;
|
public TargetMode targets;
|
||||||
public RotationMode rotation;
|
public RotationMode rotation;
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using TMPro;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace DepthsOfGreed.HandbookUI
|
||||||
|
{
|
||||||
|
public sealed class HandbookStatisticsController : MonoBehaviour
|
||||||
|
{
|
||||||
|
public SupplyBasementController supplyView;
|
||||||
|
public TestPlacementController placement;
|
||||||
|
public TMP_Text resourcesText,emptyText;
|
||||||
|
public TMP_Text[] channelTitles,channelStocks,channelRates,channelTimes;
|
||||||
|
public TMP_Text[] towerNames,towerMagazines,towerRates;
|
||||||
|
public GameObject[] towerRows;
|
||||||
|
public Button perSecondButton,perMinuteButton;
|
||||||
|
public TMP_Text rateHeading;
|
||||||
|
private bool perMinute=true;
|
||||||
|
private float nextRefresh;
|
||||||
|
private readonly List<TowerSupplyState> towers=new List<TowerSupplyState>();
|
||||||
|
private readonly string[] names={"Physisch","Energiezellen","Sondermunition"};
|
||||||
|
void Awake(){perSecondButton.onClick.AddListener(()=>SetUnit(false));perMinuteButton.onClick.AddListener(()=>SetUnit(true));}
|
||||||
|
void OnEnable(){nextRefresh=0;}
|
||||||
|
public void SetUnit(bool minute){perMinute=minute;nextRefresh=0;}
|
||||||
|
public static string FormatRate(float? rate,bool minute)
|
||||||
|
{
|
||||||
|
if(!rate.HasValue || float.IsNaN(rate.Value) || float.IsInfinity(rate.Value) || rate.Value<0)return "—";
|
||||||
|
return (rate.Value*(minute?60:1)).ToString("0.#")+(minute?" / min":" / s");
|
||||||
|
}
|
||||||
|
public static string Estimate(float buffer,float? rate)
|
||||||
|
{
|
||||||
|
if(!rate.HasValue || float.IsNaN(rate.Value) || float.IsInfinity(rate.Value) || rate.Value<0)return "—";
|
||||||
|
if(rate.Value==0)return "kein Verbrauch";
|
||||||
|
float seconds=Mathf.Max(0,buffer)/rate.Value;
|
||||||
|
if(seconds>5999)return "> 99 min";
|
||||||
|
int rounded=Mathf.CeilToInt(seconds);return "ca. "+(rounded/60).ToString("00")+":"+(rounded%60).ToString("00");
|
||||||
|
}
|
||||||
|
void Update(){if(Time.unscaledTime<nextRefresh)return;nextRefresh=Time.unscaledTime+.25f;Refresh();}
|
||||||
|
public void Refresh()
|
||||||
|
{
|
||||||
|
SupplyState state=supplyView && supplyView.HasSupplyBackend?supplyView.SupplyBackend.ReadState():null;
|
||||||
|
resourcesText.text=placement && placement.Backend!=null?"Mauerplätze "+placement.Backend.WallCount+" / "+placement.Backend.WallLimit+"\nEnergie "+placement.Backend.Energy+" · Gedärme "+placement.Backend.Entrails:"";
|
||||||
|
perSecondButton.GetComponent<Image>().color=perMinute?new Color(.82f,.82f,.82f):new Color(.55f,.55f,.55f);
|
||||||
|
perMinuteButton.GetComponent<Image>().color=perMinute?new Color(.55f,.55f,.55f):new Color(.82f,.82f,.82f);
|
||||||
|
rateHeading.text=perMinute?"Verbrauch / min":"Verbrauch / s";
|
||||||
|
for(int i=0;i<3;i++)
|
||||||
|
{
|
||||||
|
SupplyChannelState channel=null;
|
||||||
|
if(state?.channels!=null)foreach(var c in state.channels)if(c!=null && (int)c.kind==i){channel=c;break;}
|
||||||
|
channelTitles[i].text=names[i];
|
||||||
|
channelStocks[i].text=channel==null?"Lager — · Verteiler —":"Lager "+channel.storage.ToString("0.#")+" / "+channel.storageCapacity.ToString("0.#")+"\nVerteiler "+channel.buffer.ToString("0.#")+" / "+channel.bufferCapacity.ToString("0.#");
|
||||||
|
channelRates[i].text="Verbrauch: "+FormatRate(channel?.consumedPerSecond,perMinute);
|
||||||
|
channelTimes[i].text="Verteiler ohne Nachschub: "+Estimate(channel==null?0:channel.buffer,channel?.consumedPerSecond);
|
||||||
|
}
|
||||||
|
towers.Clear();
|
||||||
|
if(state?.towers!=null){foreach(var t in state.towers)if(t!=null && t.type)towers.Add(t);}
|
||||||
|
else if(placement && placement.Backend!=null && !(supplyView && supplyView.HasSupplyBackend))
|
||||||
|
foreach(var t in placement.Backend.BuiltObjects)if(t.type && t.type.supply.kind!=SupplyKind.None)
|
||||||
|
towers.Add(new TowerSupplyState{instanceId=t.instanceId,type=t.type,magazine=-1,magazineCapacity=-1});
|
||||||
|
towers.Sort((a,b)=>{int type=string.CompareOrdinal(a.type.id,b.type.id);return type!=0?type:a.instanceId.CompareTo(b.instanceId);});
|
||||||
|
emptyText.gameObject.SetActive(towers.Count==0);
|
||||||
|
for(int i=0;i<towerRows.Length;i++)
|
||||||
|
{
|
||||||
|
towerRows[i].SetActive(i<towers.Count);if(i>=towers.Count)continue;var t=towers[i];
|
||||||
|
towerNames[i].text=t.type.displayName+" #"+t.instanceId;
|
||||||
|
towerMagazines[i].text=t.magazine<0?"—":t.magazine.ToString("0.#")+" / "+t.magazineCapacity.ToString("0.#");
|
||||||
|
towerRates[i].text=FormatRate(t.consumedPerSecond,perMinute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3a70fe0fa232119479715883bcc9816b
|
||||||
@@ -28,16 +28,14 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
|
|
||||||
[Header("Grafiken und Prefabs")]
|
[Header("Grafiken und Prefabs")]
|
||||||
[Tooltip("Bild im Bau-, Index- und Upgrademenü.")]
|
[Tooltip("Bild im Bau-, Index- und Upgrademenü.")]
|
||||||
public Sprite icon;
|
[InspectorName("Book Icon")] public Sprite icon;
|
||||||
[Tooltip("Grafik, die während der Platzierung dem Mauszeiger folgt.")]
|
|
||||||
public Sprite previewSprite;
|
|
||||||
[Tooltip("Grafik des unbeweglichen Unterteils. Wird vom Platzierungsprototyp genutzt.")]
|
|
||||||
public Sprite baseSprite;
|
|
||||||
[Tooltip("Vorbereitetes Unterteil als GameObject. Noch nicht vom Platzierungsprototyp verwendet.")]
|
|
||||||
public GameObject basePrefab;
|
|
||||||
[Tooltip("Vollständiges Objekt für die spätere Gameplay-Anbindung.")]
|
[Tooltip("Vollständiges Objekt für die spätere Gameplay-Anbindung.")]
|
||||||
public GameObject gameplayPrefab;
|
public GameObject gameplayPrefab;
|
||||||
|
|
||||||
|
[Header("Ziehvorschau: vorne nach hinten")]
|
||||||
|
public PreviewSpriteLayer[] previewLayers = { new PreviewSpriteLayer(), new PreviewSpriteLayer { rotateWithAim = false } };
|
||||||
|
[Tooltip("Eine gemeinsame Skalierung für alle Ebenen. 0 = aus unterster belegter Ebene und Basisgröße ableiten.")]
|
||||||
|
[Min(0)] public float previewScale;
|
||||||
[Header("Platzierung und Platzbedarf")]
|
[Header("Platzierung und Platzbedarf")]
|
||||||
public BuildRegion region;
|
public BuildRegion region;
|
||||||
public BaseShape baseShape;
|
public BaseShape baseShape;
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace DepthsOfGreed.HandbookUI
|
||||||
|
{
|
||||||
|
public enum GateDirection { North, East, South, West }
|
||||||
|
public sealed class GateOverviewState
|
||||||
|
{
|
||||||
|
public GateDirection direction;
|
||||||
|
public float health,maxHealth;
|
||||||
|
public bool isOpen,underAttack;
|
||||||
|
// Increase on every hit. It stays unchanged between snapshots.
|
||||||
|
public long hitSequence;
|
||||||
|
}
|
||||||
|
public sealed class CombatOverviewState
|
||||||
|
{
|
||||||
|
public string floorName;
|
||||||
|
public IReadOnlyList<GateOverviewState> gates=Array.Empty<GateOverviewState>();
|
||||||
|
}
|
||||||
|
public interface ICombatOverview
|
||||||
|
{
|
||||||
|
CombatOverviewState ReadCombatOverview();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 8a61348f97e25cc44bb55e1c05c75b28
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
namespace DepthsOfGreed.HandbookUI
|
||||||
|
{
|
||||||
|
[Serializable] public sealed class PreviewSpriteLayer
|
||||||
|
{
|
||||||
|
public Sprite sprite;
|
||||||
|
public Vector2 offset;
|
||||||
|
public Vector2 scale=Vector2.one;
|
||||||
|
public float rotation;
|
||||||
|
public bool rotateWithAim=true;
|
||||||
|
}
|
||||||
|
public sealed class LayeredTowerPreview:MonoBehaviour
|
||||||
|
{
|
||||||
|
readonly List<SpriteRenderer> renderers=new List<SpriteRenderer>();
|
||||||
|
PreviewSpriteLayer[] layers;
|
||||||
|
public void Show(HandbookTowerEntry entry,Sprite fallback)
|
||||||
|
{
|
||||||
|
layers=entry.previewLayers??Array.Empty<PreviewSpriteLayer>();
|
||||||
|
Sprite reference=null;for(int i=layers.Length-1;i>=0;i--)if(layers[i]?.sprite){reference=layers[i].sprite;break;}
|
||||||
|
bool empty=!reference;if(empty)reference=fallback;
|
||||||
|
float size=entry.baseShape==BaseShape.Rectangle?entry.baseSize.x:entry.baseRadius*1.4142136f;
|
||||||
|
float common=entry.previewScale>0?entry.previewScale:size/Mathf.Max(.001f,reference.bounds.size.x);
|
||||||
|
int count=empty?1:layers.Length;
|
||||||
|
while(renderers.Count<count){var go=new GameObject("PreviewLayer_"+renderers.Count);go.transform.SetParent(transform,false);renderers.Add(go.AddComponent<SpriteRenderer>());}
|
||||||
|
for(int i=0;i<renderers.Count;i++)
|
||||||
|
{
|
||||||
|
var r=renderers[i];bool visible=i<count && (empty || layers[i]?.sprite);r.gameObject.SetActive(visible);if(!visible)continue;
|
||||||
|
var layer=empty?new PreviewSpriteLayer{sprite=fallback}:layers[i];r.sprite=layer.sprite;r.sortingOrder=20+count-i;
|
||||||
|
r.transform.localPosition=(Vector3)(layer.offset*common);r.transform.localScale=new Vector3(common*layer.scale.x,common*layer.scale.y,1);
|
||||||
|
if(empty && entry.baseShape==BaseShape.Rectangle && entry.previewScale<=0)r.transform.localScale=new Vector3(entry.baseSize.x/reference.bounds.size.x,entry.baseSize.y/reference.bounds.size.y,1);
|
||||||
|
}
|
||||||
|
LateUpdate();
|
||||||
|
}
|
||||||
|
public void SetOpacity(float alpha){foreach(var r in renderers)r.color=new Color(1,1,1,alpha);}
|
||||||
|
void LateUpdate()
|
||||||
|
{for(int i=0;i<renderers.Count;i++)if(renderers[i].gameObject.activeSelf){var l=layers!=null && i<layers.Length?layers[i]:null;renderers[i].transform.localRotation=Quaternion.Euler(0,0,(l?.rotation??0)-(l!=null && !l.rotateWithAim?transform.eulerAngles.z:0));}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 4669741d7ab05c24db6d0ecfe58e2a68
|
||||||
@@ -87,7 +87,7 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
obj.name = t.displayName + " #" + nextId;
|
obj.name = t.displayName + " #" + nextId;
|
||||||
if (!t.gameplayPrefab)
|
if (!t.gameplayPrefab)
|
||||||
{
|
{
|
||||||
var renderer = obj.GetComponent<SpriteRenderer>(); if (t.baseSprite && renderer) { renderer.sprite = t.baseSprite; renderer.color = Color.white; }
|
var renderer = obj.GetComponent<SpriteRenderer>();
|
||||||
Vector2 size = t.baseShape == BaseShape.Rectangle ? t.baseSize : Vector2.one * t.baseRadius * 1.4142136f;
|
Vector2 size = t.baseShape == BaseShape.Rectangle ? t.baseSize : Vector2.one * t.baseRadius * 1.4142136f;
|
||||||
Vector2 source = renderer && renderer.sprite ? renderer.sprite.bounds.size : Vector2.one;
|
Vector2 source = renderer && renderer.sprite ? renderer.sprite.bounds.size : Vector2.one;
|
||||||
obj.transform.localScale = new Vector3(size.x / source.x, size.y / source.y, 1);
|
obj.transform.localScale = new Vector3(size.x / source.x, size.y / source.y, 1);
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
namespace DepthsOfGreed.HandbookUI
|
||||||
|
{
|
||||||
|
public sealed class RangeArenaClip:MonoBehaviour
|
||||||
|
{
|
||||||
|
public WallBuildArea arena;
|
||||||
|
public Renderer[] outlines;
|
||||||
|
public Renderer fill;
|
||||||
|
public TestPlacementController placement;
|
||||||
|
CombatDefinition combat;bool ability;
|
||||||
|
MaterialPropertyBlock block;
|
||||||
|
public void Configure(HandbookTowerEntry entry){combat=entry.combat;ability=entry.use==BuildUse.Ability;}
|
||||||
|
void LateUpdate()
|
||||||
|
{
|
||||||
|
if(!arena || combat==null || !arena.TryGetRadii(out float radius,out _,out _))return;
|
||||||
|
float angle=transform.eulerAngles.z+combat.forwardAngleOffset;
|
||||||
|
if(block==null)block=new MaterialPropertyBlock();
|
||||||
|
block.Clear();block.SetVector("_ArenaCenter",arena.Center);block.SetFloat("_ArenaRadius",radius);
|
||||||
|
block.SetVector("_Tower",transform.position);block.SetFloat("_Minimum",ability?0:combat.minimumRange);block.SetFloat("_Maximum",ability?combat.areaRadius:combat.unlimitedRange?100000:combat.range);
|
||||||
|
block.SetVector("_Forward",new Vector4(Mathf.Cos(angle*Mathf.Deg2Rad),Mathf.Sin(angle*Mathf.Deg2Rad),0,0));block.SetFloat("_CosHalf",Mathf.Cos((ability?360:AttackRangeGeometry.CoverageAngle(combat))*.5f*Mathf.Deg2Rad));
|
||||||
|
block.SetFloat("_Fill",0);
|
||||||
|
for(int i=0;i<outlines.Length;i++){block.SetFloat("_Dashed",i==1?1:0);outlines[i].SetPropertyBlock(block);}
|
||||||
|
if(fill){fill.transform.position=arena.Center;fill.transform.rotation=Quaternion.identity;fill.transform.localScale=Vector3.one*(radius*2);block.SetFloat("_Fill",1);block.SetFloat("_Dashed",0);fill.SetPropertyBlock(block);}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 648bfe9143bff7b47b435fc829f73753
|
||||||
@@ -17,6 +17,7 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
public event System.Action<int> PriorityChanged;
|
public event System.Action<int> PriorityChanged;
|
||||||
[Header("Authored scene references")]
|
[Header("Authored scene references")]
|
||||||
public GameObject basementView;
|
public GameObject basementView;
|
||||||
|
public BasementRoomView roomView;
|
||||||
public GameObject handbookRoot;
|
public GameObject handbookRoot;
|
||||||
public CanvasGroup handbookVisibility;
|
public CanvasGroup handbookVisibility;
|
||||||
public HandbookController handbook;
|
public HandbookController handbook;
|
||||||
@@ -61,7 +62,7 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
{
|
{
|
||||||
signScale = liftSign.localScale;
|
signScale = liftSign.localScale;
|
||||||
truckScale = truck.localScale;
|
truckScale = truck.localScale;
|
||||||
basementView.SetActive(false);
|
if(roomView)roomView.SetVisible(false);else basementView.SetActive(false);
|
||||||
doorGroup.gameObject.SetActive(false);
|
doorGroup.gameObject.SetActive(false);
|
||||||
liftButton.onClick.AddListener(UseLift);
|
liftButton.onClick.AddListener(UseLift);
|
||||||
for (int i = 0; i < connectionButtons.Length; i++)
|
for (int i = 0; i < connectionButtons.Length; i++)
|
||||||
@@ -105,7 +106,7 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
yield return Doors(true);
|
yield return Doors(true);
|
||||||
if (handbook) handbook.Close();
|
if (handbook) handbook.Close();
|
||||||
IsInBasement = !IsInBasement;
|
IsInBasement = !IsInBasement;
|
||||||
basementView.SetActive(IsInBasement);
|
if(roomView)roomView.SetVisible(IsInBasement);else basementView.SetActive(IsInBasement);
|
||||||
// Hide only the presentation. The backend on HandbookRoot must keep updating.
|
// Hide only the presentation. The backend on HandbookRoot must keep updating.
|
||||||
handbookVisibility.alpha = IsInBasement ? 0f : 1f;
|
handbookVisibility.alpha = IsInBasement ? 0f : 1f;
|
||||||
handbookVisibility.interactable = !IsInBasement;
|
handbookVisibility.interactable = !IsInBasement;
|
||||||
@@ -181,22 +182,21 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
}
|
}
|
||||||
private void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
if (!IsInBasement) return;
|
|
||||||
if (Time.unscaledTime > nextRefresh) { nextRefresh = Time.unscaledTime + 0.25f; RefreshConnections(); }
|
if (Time.unscaledTime > nextRefresh) { nextRefresh = Time.unscaledTime + 0.25f; RefreshConnections(); }
|
||||||
if (HasSupplyBackend) { RenderBackendTruck(); return; }
|
if (HasSupplyBackend) { RenderBackendTruck(); return; }
|
||||||
if (!animateTruckPreview) { truckStatus.text = "LKW bereit"; return; }
|
if (!animateTruckPreview) { truckStatus.text = "LKW bereit"; return; }
|
||||||
if (Time.unscaledTime < truckPauseUntil) return;
|
if (Time.time < truckPauseUntil) return;
|
||||||
float destination = unloading ? unloadingPosition : loadingPositions[previewKind];
|
float destination = unloading ? unloadingPosition : loadingPositions[previewKind];
|
||||||
var p = truck.anchoredPosition;
|
var p = truck.anchoredPosition;
|
||||||
float direction = destination - p.x;
|
float direction = destination - p.x;
|
||||||
if (Mathf.Abs(direction) > 0.1f)
|
if (Mathf.Abs(direction) > 0.1f)
|
||||||
truck.localScale = new Vector3(Mathf.Abs(truckScale.x) * Mathf.Sign(direction), truckScale.y, truckScale.z);
|
truck.localScale = new Vector3(Mathf.Abs(truckScale.x) * Mathf.Sign(direction), truckScale.y, truckScale.z);
|
||||||
p.x = Mathf.MoveTowards(p.x, destination, Mathf.Max(0, truckSpeed) * Time.unscaledDeltaTime); truck.anchoredPosition = p;
|
p.x = Mathf.MoveTowards(p.x, destination, Mathf.Max(0, truckSpeed) * Time.deltaTime); truck.anchoredPosition = p;
|
||||||
truckStatus.text = (unloading ? "Zur Verteilerstation" : "Zum Behälter: " + kinds[previewKind]);
|
truckStatus.text = (unloading ? "Zur Verteilerstation" : "Zum Behälter: " + kinds[previewKind]);
|
||||||
if (Mathf.Abs(p.x - destination) < 0.1f)
|
if (Mathf.Abs(p.x - destination) < 0.1f)
|
||||||
{
|
{
|
||||||
truckStatus.text = (unloading ? "Abladen" : "Beladen: " + kinds[previewKind]);
|
truckStatus.text = (unloading ? "Abladen" : "Beladen: " + kinds[previewKind]);
|
||||||
truckPauseUntil = Time.unscaledTime + loadingPause;
|
truckPauseUntil = Time.time + loadingPause;
|
||||||
if (unloading) previewKind = Priority >= 0 ? Priority : (previewKind + 1) % 3;
|
if (unloading) previewKind = Priority >= 0 ? Priority : (previewKind + 1) % 3;
|
||||||
unloading = !unloading;
|
unloading = !unloading;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
private CanvasGroup bookGroup;
|
private CanvasGroup bookGroup;
|
||||||
private GameObject prefab, preview, controls;
|
private GameObject prefab, preview, controls;
|
||||||
private Transform container, area;
|
private Transform container, area;
|
||||||
|
[Header("Mauer-Baubereich")]
|
||||||
|
[Tooltip("Baugrenzen und Mittelpunkt. Konfiguration auf PlacementTest/BuildArea.")]
|
||||||
|
[SerializeField] private WallBuildArea buildArea;
|
||||||
private SpriteRenderer ghost, footprint;
|
private SpriteRenderer ghost, footprint;
|
||||||
private Sprite ghostPlaceholder;
|
private Sprite ghostPlaceholder;
|
||||||
private TMP_Text controlsText, feedback, resources, buildHint;
|
private TMP_Text controlsText, feedback, resources, buildHint;
|
||||||
@@ -46,7 +49,11 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
private LineRenderer previewOutline, innerOutline, outerOutline;
|
private LineRenderer previewOutline, innerOutline, outerOutline;
|
||||||
private Material lineMaterial;
|
private Material lineMaterial;
|
||||||
private float placementAngle;
|
private float placementAngle;
|
||||||
|
private float manualAimOffset;
|
||||||
|
public bool alignPreviewToCenter = true;
|
||||||
|
[SerializeField] private LayeredTowerPreview layeredPreview;
|
||||||
private GeneralFeedbackController general;
|
private GeneralFeedbackController general;
|
||||||
|
[SerializeField] private AttackRangePreview attackRangePreview;
|
||||||
|
|
||||||
public void Initialize(HandbookController owner, BuildMenuController menu, RectTransform openBook, GameObject testPrefab, MonoBehaviour gameplayBackend = null)
|
public void Initialize(HandbookController owner, BuildMenuController menu, RectTransform openBook, GameObject testPrefab, MonoBehaviour gameplayBackend = null)
|
||||||
{
|
{
|
||||||
@@ -62,7 +69,8 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
enabled = false;
|
enabled = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
area = test.transform.Find("BuildArea");
|
area = buildArea ? buildArea.transform : test.transform.Find("BuildArea");
|
||||||
|
if (!buildArea && area) buildArea = area.GetComponent<WallBuildArea>();
|
||||||
container = test.transform.Find("PlacedTowers") ?? test.transform.Find("PlaceTowers");
|
container = test.transform.Find("PlacedTowers") ?? test.transform.Find("PlaceTowers");
|
||||||
preview = test.transform.Find("PlacementPreview").gameObject;
|
preview = test.transform.Find("PlacementPreview").gameObject;
|
||||||
ghost = preview.transform.Find("GhostVisual").GetComponent<SpriteRenderer>();
|
ghost = preview.transform.Find("GhostVisual").GetComponent<SpriteRenderer>();
|
||||||
@@ -87,8 +95,14 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
}
|
}
|
||||||
resources = menu.transform.Find("RightPage/ResourcesText").GetComponent<TMP_Text>();
|
resources = menu.transform.Find("RightPage/ResourcesText").GetComponent<TMP_Text>();
|
||||||
buildHint = menu.transform.Find("BuildHintText").GetComponent<TMP_Text>();
|
buildHint = menu.transform.Find("BuildHintText").GetComponent<TMP_Text>();
|
||||||
innerRadius = RadiusOf("BackgroundTower", 3f);
|
if (!buildArea)
|
||||||
outerRadius = RadiusOf("BackgroundOuterwall", 4f);
|
{
|
||||||
|
Debug.LogError("Testplatzierung: WallBuildArea zuweisen.", this); enabled = false; return;
|
||||||
|
}
|
||||||
|
if (!buildArea.TryGetRadii(out innerRadius, out outerRadius, out string boundaryError))
|
||||||
|
{
|
||||||
|
Debug.LogError("Testplatzierung: " + boundaryError, buildArea); enabled = false; return;
|
||||||
|
}
|
||||||
if (gameplayBackend && gameplayBackend is IHandbookGameplay real) Backend = real;
|
if (gameplayBackend && gameplayBackend is IHandbookGameplay real) Backend = real;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -101,6 +115,7 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
previewOutline = MakeCircle("PlacementOutline", preview.transform, 11);
|
previewOutline = MakeCircle("PlacementOutline", preview.transform, 11);
|
||||||
innerOutline = MakeCircle("BuildArea_Inner", area, 8);
|
innerOutline = MakeCircle("BuildArea_Inner", area, 8);
|
||||||
outerOutline = MakeCircle("BuildArea_Outer", area, 8);
|
outerOutline = MakeCircle("BuildArea_Outer", area, 8);
|
||||||
|
innerOutline.useWorldSpace = outerOutline.useWorldSpace = true;
|
||||||
DrawCircle(innerOutline, innerRadius);
|
DrawCircle(innerOutline, innerRadius);
|
||||||
DrawCircle(outerOutline, outerRadius);
|
DrawCircle(outerOutline, outerRadius);
|
||||||
ShowGuides(false);
|
ShowGuides(false);
|
||||||
@@ -109,28 +124,21 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
RefreshResources();
|
RefreshResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float RadiusOf(string name, float fallback)
|
|
||||||
{
|
|
||||||
var obj = GameObject.Find(name);
|
|
||||||
var renderer = obj ? obj.GetComponent<SpriteRenderer>() : null;
|
|
||||||
return renderer ? Mathf.Min(renderer.bounds.extents.x, renderer.bounds.extents.y) : fallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Begin(HandbookTowerEntry entry, Vector2 screenPosition)
|
public bool Begin(HandbookTowerEntry entry, Vector2 screenPosition)
|
||||||
{
|
{
|
||||||
if (!enabled || IsDragging || IsWaitingForReturn || entry == null) return false;
|
if (!enabled || IsDragging || IsWaitingForReturn || entry == null) return false;
|
||||||
var availability = Backend.CheckAvailability(entry);
|
var availability = Backend.CheckAvailability(entry);
|
||||||
if (!availability.Allowed) { failedTypeId = entry.id; Message(availability.message); return false; }
|
if (!availability.Allowed) { failedTypeId = entry.id; Message(availability.message); return false; }
|
||||||
draggingEntry = entry; placementAngle = 0; LastFailure = null;
|
draggingEntry = entry; placementAngle = 0; manualAimOffset = 0; LastFailure = null;
|
||||||
ghost.sprite = entry.previewSprite ? entry.previewSprite : ghostPlaceholder;
|
if(attackRangePreview)attackRangePreview.Show(entry);
|
||||||
Vector2 visualSize = entry.baseShape == BaseShape.Rectangle ? entry.baseSize : Vector2.one * entry.baseRadius * 1.4142136f;
|
if(layeredPreview)layeredPreview.Show(entry,ghostPlaceholder);
|
||||||
ghost.transform.localScale = new Vector3(visualSize.x / ghost.sprite.bounds.size.x, visualSize.y / ghost.sprite.bounds.size.y, 1);
|
ghost.gameObject.SetActive(!layeredPreview);
|
||||||
IsDragging = true;
|
IsDragging = true;
|
||||||
preview.SetActive(true);
|
preview.SetActive(true);
|
||||||
footprint.transform.localScale = new Vector3(entry.placementRadius * 2f, entry.placementRadius * 2f, 1f);
|
footprint.transform.localScale = new Vector3(entry.placementRadius * 2f, entry.placementRadius * 2f, 1f);
|
||||||
DrawCircle(previewOutline, entry.placementRadius);
|
DrawCircle(previewOutline, entry.placementRadius);
|
||||||
controls.SetActive(false);
|
controls.SetActive(false);
|
||||||
general.ShowControls("Loslassen: Bauen\nRechtsklick / Esc: Abbrechen" + (entry.allowPlayerRotation ? "\nR: Drehen" : ""));
|
general.ShowControls("Loslassen: Bauen\nRechtsklick / Esc: Abbrechen" + (entry.allowPlayerRotation ? "\nR: Drehen" : "") + (entry.combat != null && entry.combat.unlimitedRange ? "\nReichweite: unbegrenzt" : ""));
|
||||||
bookGroup.blocksRaycasts = false;
|
bookGroup.blocksRaycasts = false;
|
||||||
ShowGuides(true);
|
ShowGuides(true);
|
||||||
foreach (var record in Backend.BuiltObjects)
|
foreach (var record in Backend.BuiltObjects)
|
||||||
@@ -153,12 +161,18 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
if (!plane.Raycast(ray, out float distance)) return;
|
if (!plane.Raycast(ray, out float distance)) return;
|
||||||
target = ray.GetPoint(distance);
|
target = ray.GetPoint(distance);
|
||||||
target.z = 0;
|
target.z = 0;
|
||||||
|
if(alignPreviewToCenter)
|
||||||
|
{
|
||||||
|
Vector2 inward = area.position-target;
|
||||||
|
if(inward.sqrMagnitude>.0001f)placementAngle=Mathf.LerpAngle(placementAngle,Mathf.Atan2(inward.y,inward.x)*Mathf.Rad2Deg-(draggingEntry.combat?.forwardAngleOffset??90)+manualAimOffset,1-Mathf.Exp(-16*Time.unscaledDeltaTime));
|
||||||
|
}
|
||||||
preview.transform.position = target; preview.transform.rotation = Quaternion.Euler(0, 0, placementAngle);
|
preview.transform.position = target; preview.transform.rotation = Quaternion.Euler(0, 0, placementAngle);
|
||||||
string reason = Check(draggingEntry, target);
|
string reason = Check(draggingEntry, target);
|
||||||
bool outside = Backend.CheckBuild(new BuildRequest(draggingEntry, target, placementAngle)).failure == BuildFailure.OutsideRegion;
|
bool outside = Backend.CheckBuild(new BuildRequest(draggingEntry, target, placementAngle)).failure == BuildFailure.OutsideRegion;
|
||||||
Color color = outside ? Color.gray : reason == null ? Color.green : Color.red;
|
Color color = outside ? Color.gray : reason == null ? Color.green : Color.red;
|
||||||
footprint.color = new Color(color.r, color.g, color.b, outside ? 0.08f : 0.18f);
|
footprint.color = new Color(color.r, color.g, color.b, outside ? 0.08f : 0.18f);
|
||||||
ghost.color = new Color(1f, 1f, 1f, outside ? 0.3f : 0.65f);
|
ghost.color = new Color(1f, 1f, 1f, outside ? 0.3f : 0.65f);
|
||||||
|
if(layeredPreview)layeredPreview.SetOpacity(outside?.3f:.8f);
|
||||||
Tint(previewOutline, color);
|
Tint(previewOutline, color);
|
||||||
DrawCircle(previewOutline, draggingEntry.placementRadius, outside);
|
DrawCircle(previewOutline, draggingEntry.placementRadius, outside);
|
||||||
controlsText.text = "Loslassen: Platzieren · Rechtsklick / Esc: Abbrechen";
|
controlsText.text = "Loslassen: Platzieren · Rechtsklick / Esc: Abbrechen";
|
||||||
@@ -222,7 +236,7 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
{
|
{
|
||||||
if (Mouse.current.rightButton.wasPressedThisFrame ||
|
if (Mouse.current.rightButton.wasPressedThisFrame ||
|
||||||
(Keyboard.current != null && Keyboard.current.escapeKey.wasPressedThisFrame)) Cancel();
|
(Keyboard.current != null && Keyboard.current.escapeKey.wasPressedThisFrame)) Cancel();
|
||||||
else { if (draggingEntry.allowPlayerRotation && Keyboard.current != null && Keyboard.current.rKey.wasPressedThisFrame) placementAngle += 90; Move(Mouse.current.position.ReadValue()); }
|
else { if (draggingEntry.allowPlayerRotation && Keyboard.current != null && Keyboard.current.rKey.wasPressedThisFrame) { if(alignPreviewToCenter)manualAimOffset+=90;else placementAngle+=90; } Move(Mouse.current.position.ReadValue()); }
|
||||||
}
|
}
|
||||||
bool lowered = IsDragging || IsWaitingForReturn;
|
bool lowered = IsDragging || IsWaitingForReturn;
|
||||||
if (!lowered) bookGroup.blocksRaycasts = true;
|
if (!lowered) bookGroup.blocksRaycasts = true;
|
||||||
@@ -293,7 +307,8 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
for (int i = 0; i < segments; i++)
|
for (int i = 0; i < segments; i++)
|
||||||
{
|
{
|
||||||
float angle = i * Mathf.PI * 2f / segments;
|
float angle = i * Mathf.PI * 2f / segments;
|
||||||
line.SetPosition(i, new Vector3(Mathf.Cos(angle) * radius, Mathf.Sin(angle) * radius, 0));
|
var point = new Vector3(Mathf.Cos(angle) * radius, Mathf.Sin(angle) * radius, 0);
|
||||||
|
line.SetPosition(i, line.useWorldSpace ? line.transform.position + point : point);
|
||||||
}
|
}
|
||||||
// Alternating width provides a procedural broken outline without an extra sprite.
|
// Alternating width provides a procedural broken outline without an extra sprite.
|
||||||
if (dashed)
|
if (dashed)
|
||||||
@@ -307,3 +322,5 @@ namespace DepthsOfGreed.HandbookUI
|
|||||||
private void OnDestroy() { if (Backend != null) Backend.StateChanged -= RefreshResources; if (lineMaterial) Destroy(lineMaterial); }
|
private void OnDestroy() { if (Backend != null) Backend.StateChanged -= RefreshResources; if (lineMaterial) Destroy(lineMaterial); }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
#if UNITY_EDITOR
|
||||||
|
using DepthsOfGreed.HandbookUI;
|
||||||
|
using UnityEngine;
|
||||||
|
// Isolated editor fixture, never saved in a scene or included in builds.
|
||||||
|
public sealed class UIOverviewTestBackend : MonoBehaviour,ICombatOverview
|
||||||
|
{
|
||||||
|
public CombatOverviewState state=new CombatOverviewState();
|
||||||
|
public CombatOverviewState ReadCombatOverview()=>state;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: b1f8e752479376549a225c2261492fee
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace DepthsOfGreed.HandbookUI
|
||||||
|
{
|
||||||
|
public enum WallBoundarySource { SpriteRenderers, CustomRadii }
|
||||||
|
|
||||||
|
[DisallowMultipleComponent]
|
||||||
|
public sealed class WallBuildArea : MonoBehaviour
|
||||||
|
{
|
||||||
|
[Header("Mauer-Baugrenzen")]
|
||||||
|
public WallBoundarySource boundarySource = WallBoundarySource.CustomRadii;
|
||||||
|
[Tooltip("Äußere Kante dieses Sprites ergibt die innere Baugrenze. Kreisförmiges Sprite, zentriert auf BuildArea verwenden.")]
|
||||||
|
public SpriteRenderer innerBoundarySprite;
|
||||||
|
[Tooltip("Äußere Kante dieses Sprites ergibt die äußere Baugrenze. Kreisförmiges Sprite, zentriert auf BuildArea verwenden.")]
|
||||||
|
public SpriteRenderer outerBoundarySprite;
|
||||||
|
[Tooltip("Eigener Innenradius in Welt-Einheiten. Wird nur bei Custom Radii verwendet; unabhängig von der Transform-Skalierung.")]
|
||||||
|
[Min(0)] public float innerRadius = 3f;
|
||||||
|
[Tooltip("Eigener Außenradius in Welt-Einheiten. Muss größer als der Innenradius sein.")]
|
||||||
|
[Min(0.01f)] public float outerRadius = 4f;
|
||||||
|
public bool showSelectedGizmos = true;
|
||||||
|
public Vector3 Center => transform.position;
|
||||||
|
|
||||||
|
public bool TryGetRadii(out float inner, out float outer, out string error)
|
||||||
|
{
|
||||||
|
inner = innerRadius; outer = outerRadius; error = null;
|
||||||
|
if (boundarySource == WallBoundarySource.SpriteRenderers)
|
||||||
|
{
|
||||||
|
if (!innerBoundarySprite || !outerBoundarySprite || !innerBoundarySprite.sprite || !outerBoundarySprite.sprite)
|
||||||
|
{ error = "Beide Baugrenzen benötigen einen zugewiesenen SpriteRenderer mit Sprite."; return false; }
|
||||||
|
inner = Mathf.Min(innerBoundarySprite.bounds.extents.x, innerBoundarySprite.bounds.extents.y);
|
||||||
|
outer = Mathf.Min(outerBoundarySprite.bounds.extents.x, outerBoundarySprite.bounds.extents.y);
|
||||||
|
}
|
||||||
|
if (float.IsNaN(inner) || float.IsInfinity(inner) || float.IsNaN(outer) || float.IsInfinity(outer) || inner < 0 || outer <= inner)
|
||||||
|
{ error = "Ungültige Mauer-Baugrenzen: 0 <= Innenradius < Außenradius erforderlich."; return false; }
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDrawGizmosSelected()
|
||||||
|
{
|
||||||
|
if (!showSelectedGizmos || !TryGetRadii(out float inner, out float outer, out _)) return;
|
||||||
|
var old = Gizmos.color;
|
||||||
|
Gizmos.color = new Color(1f,.65f,.15f); Circle(inner);
|
||||||
|
Gizmos.color = new Color(.2f,.8f,1f); Circle(outer);
|
||||||
|
Gizmos.color = old;
|
||||||
|
}
|
||||||
|
private void Circle(float radius)
|
||||||
|
{
|
||||||
|
var previous = Center + Vector3.right * radius;
|
||||||
|
for(int i=1;i<=128;i++)
|
||||||
|
{
|
||||||
|
float angle=i*Mathf.PI*2f/128;
|
||||||
|
var next=Center+new Vector3(Mathf.Cos(angle),Mathf.Sin(angle),0)*radius;
|
||||||
|
Gizmos.DrawLine(previous,next);previous=next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 6fa413815d8f1294b8cec6b33713cc5b
|
||||||
+65
-3
@@ -1,6 +1,6 @@
|
|||||||
# Handbuch / Gameplay – Übergabe
|
# Handbuch / Gameplay – Übergabe
|
||||||
|
|
||||||
Stand: 15. Oktober 2026. Daten sind einsatzfähige Unity-Assets, aber vorläufig balanciert. Grafik und echtes Kampfverhalten werden nicht durch das Handbuch implementiert.
|
Stand: 2. November 2026. Daten sind einsatzfähige Unity-Assets, aber vorläufig balanciert. Grafik und echtes Kampfverhalten werden nicht durch das Handbuch implementiert.
|
||||||
|
|
||||||
## Wo die Daten liegen
|
## Wo die Daten liegen
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ Grafiken: `Assets/Art/Handbook`. Original-PNGs sind unverändert übernommen; ac
|
|||||||
|
|
||||||
`category` ist Waffentechnik/Basisfarbe (physisch, Energie, Spezial), `menuGroup` die Buchkategorie. Spezialgebäude gehören vorläufig zu Anlagen, Fallen/Fähigkeiten/Reparaturtrupp zu Einsätzen. Die Gruppe bedeutet nicht automatisch eine Baufläche.
|
`category` ist Waffentechnik/Basisfarbe (physisch, Energie, Spezial), `menuGroup` die Buchkategorie. Spezialgebäude gehören vorläufig zu Anlagen, Fallen/Fähigkeiten/Reparaturtrupp zu Einsätzen. Die Gruppe bedeutet nicht automatisch eine Baufläche.
|
||||||
|
|
||||||
`baseSprite`/`basePrefab` sind die gezeichnete Basis, `previewSprite` vorerst ebenfalls diese Basis. `gameplayPrefab` bleibt frei für den echten vollständigen Turm. Zum Aufbau zuerst eine Basis instanziieren, darüber ein separat drehbares Geschütz; keine gezeichnete Basis als fertigen Turm behandeln.
|
Aktuelle Grafikfelder: Book Icon, Preview Layers und Gameplay Prefab. Die Basis-/Geschütz-Hierarchie liegt vollständig im Gameplay-Prefab. Die alten separaten Basisfelder wurden am 03.11.2026 nach Migration entfernt.
|
||||||
|
|
||||||
Die einmaligen Aufbau- und Umbauwerkzeuge wurden nach Fertigstellung entfernt. Szene, Prefabs und Assets normal im Inspector bearbeiten; kein Generierungs- oder Einrichtungsschritt ist nötig.
|
Die einmaligen Aufbau- und Umbauwerkzeuge wurden nach Fertigstellung entfernt. Szene, Prefabs und Assets normal im Inspector bearbeiten; kein Generierungs- oder Einrichtungsschritt ist nötig.
|
||||||
|
|
||||||
@@ -96,4 +96,66 @@ Die verbliebenen Editor-Prüfungen werden per Unity-Batchmode ausgeführt. Sie s
|
|||||||
- `SupplyBasementValidation.Validate`: Keller ohne Backend, Anschlüsse, Priorität, Aufzug und LKW-Ausrichtung.
|
- `SupplyBasementValidation.Validate`: Keller ohne Backend, Anschlüsse, Priorität, Aufzug und LKW-Ausrichtung.
|
||||||
- `SupplyBindingValidation.Validate`: Versorgungsschnittstelle mit isolierter Testkomponente.
|
- `SupplyBindingValidation.Validate`: Versorgungsschnittstelle mit isolierter Testkomponente.
|
||||||
|
|
||||||
Beispiel: Unity mit `-batchmode -nographics -projectPath "<Projektpfad>" -executeMethod SupplyBindingValidation.Validate -logFile "<Logpfad>"` starten. Optional rendert `SupplyBasementValidation.Validate` mit Grafikunterstützung (ohne `-nographics`) und dem zusätzlichen Argument `-captureSupplyPreview` ein Bild nach `Temp/HandbookValidation/basement-preview.png`. Der Temp-Ordner wird nicht versioniert.
|
Beispiel: Unity mit `-batchmode -nographics -projectPath "<Projektpfad>" -executeMethod SupplyBindingValidation.Validate -logFile "<Logpfad>"` starten. Optional rendert `SupplyBasementValidation.Validate` mit Grafikunterstützung (ohne `-nographics`) und dem zusätzlichen Argument `-captureSupplyPreview` ein Bild nach `Logs/HandbookValidation/basement-preview.png`. Der Logs-Ordner wird nicht versioniert.
|
||||||
|
## Mauer-Baubereich im Inspector (02.11.2026)
|
||||||
|
|
||||||
|
`PlacementTest > BuildArea` besitzt `WallBuildArea`; `HandbookRoot > TestPlacementController > Build Area` verweist darauf. Namenssuchen nach BackgroundTower/BackgroundOuterwall sind für die Grenzen entfernt.
|
||||||
|
|
||||||
|
- `Boundary Source = Sprite Renderers`: `Inner Boundary Sprite` und `Outer Boundary Sprite` zuweisen. Es wird der kleinere halbe Welt-Bounds-Durchmesser der zugewiesenen SpriteRenderer verwendet, wie bisher. Kreisförmige Sprites mit derselben Mitte wie BuildArea verwenden.
|
||||||
|
- `Boundary Source = Custom Radii`: `Inner Radius` und `Outer Radius` in Welt-Einheiten einstellen, unabhängig von Sprite- und BuildArea-Skalierung. 0 <= innen < außen. Die Mitte ist BuildArea.transform.position.
|
||||||
|
- BuildArea auswählen und Gizmos in der Scene-Ansicht aktivieren: orange = innen, cyan = außen. `Show Selected Gizmos` schaltet diese Hilfslinien.
|
||||||
|
- Die Testplatzierung liest Werte bei Spielstart. Einstellungen außerhalb von Play ändern und Play neu starten. Bestehende Szene: bisherige Sprites zugewiesen, Innenradius 3 / Außenradius 4 erhalten.
|
||||||
|
- Ein echtes IHandbookGameplay-Backend muss dieselbe WallBuildArea-Konfiguration für seine eigene Bauprüfung verwenden. UI und Prototyp übernehmen sie bereits. Der gesamte Turmfuß muss weiterhin in den Ring passen; Angriffsreichweite und Abstandskreis sind davon unabhängig.
|
||||||
|
|
||||||
|
`WallBuildAreaValidation.Validate` prüft die Zuweisung unabhängig vom Objektnamen, eigene Radien, ungültige Konfigurationen und das vollständige Einpassen der Basis. Das einmalige Einrichtungsscript wurde nach der Umstellung entfernt.
|
||||||
|
|
||||||
|
## Ergänzungen vom 02.11.2026
|
||||||
|
|
||||||
|
### Keller außerhalb des Spielfelds
|
||||||
|
|
||||||
|
Die bestehende Raumoberfläche liegt jetzt unter `SupplyBasementRoom / RoomCanvas / SupplyBasement`, außerhalb der Turmebene bei X=50. Das ist ein World-Space-Canvas mit austauschbaren Grafiken und unveränderter Raum-Anordnung. `BasementCamera` zeigt diesen Raum beim Aufzugwechsel. Die normale Kamera und alle Spielobjekte bleiben aktiv. Nur Canvas-Darstellung, UI-Raycaster und Kellerkamera werden umgeschaltet. `Canvas_SupplyBasement` enthält weiterhin Aufzugschild und Übergangstüren als Bildschirm-UI.
|
||||||
|
|
||||||
|
Die bisherige LKW-Fahrvorschau läuft auch bei geschlossenem Keller weiter und verwendet Spielzeit. Ein echtes ISupplyGameplay-Backend bleibt unverändert für die eigentliche Versorgung zuständig. Es muss dauerhaft aktiv sein und darf seine Berechnungen nicht von IsInBasement abhängig machen.
|
||||||
|
|
||||||
|
### Angriffsvorschau und Zielgeometrie
|
||||||
|
|
||||||
|
Buildables besitzen unter combat zusätzlich minimumRange, coverage (Automatic / Circle / Sector) und forwardAngleOffset. range, unlimitedRange und coneAngle werden wiederverwendet. Automatic zeigt bei fest gerichteten Kegelwaffen einen Sektor, bei frei drehbaren Waffen einen Kreis. Ein kegelförmiger Angriff beschränkt bei frei drehbaren Geschützen nicht automatisch den erreichbaren Rundumbereich.
|
||||||
|
|
||||||
|
`AttackRangeGeometry.CanReach(combat, origin, target, baseAngleDegrees)` ist ein gemeinsamer optionaler Helfer für die Zielprüfung. Ein echtes Schießsystem muss diese Grenzen beachten; die Vorschau allein verhindert noch keine Schüsse. Winkel in XY: 0=rechts, 90=oben; forwardAngleOffset standardmäßig 90 relativ zur Platzierungsrotation.
|
||||||
|
|
||||||
|
`PlacementTest / PlacementPreview / AttackRangePreview` enthält die vier dauerhaft angelegten Linien. Dort sind Farbe und Sichtbarkeit einstellbar. Blau = Angriff, der bisherige grün/rot/graue Kreis = Platzierungsabstand. Der ausgesparte Innenbereich wird über den inneren blauen Rand markiert. Unbegrenzte Reichweite bekommt keinen irreführenden endlichen Außenkreis; der General nennt sie beim Platzieren. Bei flächengezielten Einmalaktionen zeigt der Kreis stattdessen die Wirkungsfläche.
|
||||||
|
|
||||||
|
Howitzer: vorläufig minimumRange=1.5. Flammenwerfer, Pusher, Knocker und Laser Shotgun dürfen beim Bauen mit R gedreht werden. Bestehende Schadens-, Kosten- und Versorgungswerte bleiben gleich. Die Mindestreichweite bleibt bei Reichweiten-Upgrades zunächst unverändert.
|
||||||
|
|
||||||
|
### Statistikseite
|
||||||
|
|
||||||
|
`OpenBook / PageArea / Page_Statistics / StatisticsContent` ist dauerhaft angelegt, ohne Scrollen oder Seitenwechsel. Links stehen Bauplätze/Kaufressourcen und die drei Versorgungspools mit Lager, Verteiler, gemessenem Verbrauch und geschätzter Verteiler-Restzeit ohne weitere Lieferungen. Rechts stehen bis zu acht versorgte Türme, nach Typ und Instanz sortiert, mit Magazin und gemessenem Verbrauch. Die Einheit lässt sich zwischen pro Sekunde und pro Minute umstellen.
|
||||||
|
|
||||||
|
Die Statistik liest vorhandene ISupplyGameplay-Snapshots. `consumedPerSecond` muss tatsächliche Entnahmen über ein Messfenster (beispielsweise die letzten zehn Sekunden) beschreiben, keine theoretische Feuerrate. null = unbekannt (Strich), 0 = kein Verbrauch. Die Schätzung buffer / consumedPerSecond nimmt ausdrücklich keinen weiteren Nachschub an. Sie ist keine Garantie für den wirklichen Leerzeitpunkt. Ohne Versorgungssystem zeigt die Seite vorhandene Turmtypen und Bauressourcen, aber keine erfundenen Verbrauchs- oder Magazinwerte.
|
||||||
|
|
||||||
|
### Schleusenübersicht im Keller
|
||||||
|
|
||||||
|
Die kleine Ebenenkarte oben am Raum zeigt vier Richtungen, HP, offene Schleusen, Angriffe und kritische Zustände. Datenquelle: optionale MonoBehaviour-Komponente mit ICombatOverview, zugewiesen an `BattleOverview > Basement Battle Overview > Combat Backend`. Geschlossen/offen und angegriffen sind getrennte Zustände. hitSequence erhöht sich bei Treffern und löst kurzes Blinken aus; bei kritischen HP bleibt die Warnung bestehen. Ohne Daten stehen Striche. Die UI implementiert keine Schleusen- oder Schadenslogik.
|
||||||
|
|
||||||
|
### Noch tatsächlich offen
|
||||||
|
|
||||||
|
Echte Waffen/Verbrauch, LKW-Versorgung und Schleusenzustände müssen vom Gameplay-System geliefert werden. Die dafür erforderlichen Schnittstellen und Anzeigen sind vorbereitet. Endgültige Grafik, Sound, Balancing und spätere Statistik-Erweiterungen (z.B. Abschüsse/Schaden) bleiben Teamaufgaben.
|
||||||
|
|
||||||
|
### Zusätzliche Prüfung
|
||||||
|
|
||||||
|
`UINotesValidation.Validate` prüft den aktiven Raum, Hintergrund-Vorschau, Kreise/Sektoren/Mindestreichweite, Statistik und Einheiten sowie Schleusenwarnungen. Mit Grafikunterstützung schreibt sie Ansichten nach Logs/HandbookValidation. Die einmalige Szenenumstellung wurde nach Verwendung entfernt.
|
||||||
|
|
||||||
|
|
||||||
|
## Grafik- und Vorschau-Struktur (03.11.2026)
|
||||||
|
|
||||||
|
Die bisherigen Felder baseSprite, basePrefab und previewSprite sind nach Migration entfernt. Im Inspector: Book Icon (intern weiterhin icon), Preview Layers und Gameplay Prefab. Die Ebenenliste hat standardmäßig zwei Plätze, oben = vorne, unten = hinten. Alle Sprite-Pivots liegen auf demselben Anker; offset, scale und rotation sind optionale Korrekturen. Gleiche Pixels-per-Unit verwenden, wenn die Zeichnungen denselben Pixelmaßstab haben. previewScale ist eine gemeinsame Skalierung für alle Ebenen; 0 passt sie anhand der untersten belegten Ebene an die Basisgröße an. rotateWithAim lässt sich pro Ebene abschalten, etwa für die Basis. Leere Ebenen werden ausgelassen. Änderungen beim nächsten Ziehen prüfen.
|
||||||
|
|
||||||
|
Vorhandene Vorschau-/Basisgrafiken wurden in die unterste Ebene übertragen. Wo Gameplay Prefab zuvor leer war, wurden vorhandene Basis-Prefabs als vorläufiges Gameplay-Prefab übernommen; echte Geschützmodelle und Spiellogik folgen durch den Gameplay-Programmierer. Bereits zugewiesene Gameplay-Prefabs bleiben erhalten. Der Prototyp instanziiert das zugewiesene Prefab ohne nachträgliche Größenänderung; bei weiterhin leerem Feld bleibt der bisherige Test-Platzhalter. Eigene IHandbookGameplay-Backends müssen ebenfalls gameplayPrefab instanziieren und die Instanz-ID melden.
|
||||||
|
|
||||||
|
Die Ziehvorschau richtet sich während des gesamten Ziehens weich zur Arenamitte aus. alignPreviewToCenter am TestPlacementController kann dies abschalten. R versetzt bei erlaubter Rotation die Blickrichtung zusätzlich. Der Bauauftrag verwendet die tatsächlich sichtbare Rotation. Die fertige Geschütz-/Basis-Hierarchie muss das echte Gameplay-Prefab selbst behandeln.
|
||||||
|
|
||||||
|
Angriffsvorschau: dünne blaue Grenzen und sehr schwache blaue Fläche; Mindestreichweite ausgespart/gestrichelt. RangeArenaClip begrenzt die Darstellung auf den Innenradius von WallBuildArea, da Gegner nur dort existieren. Bei abweichender tatsächlicher Arenageometrie die Quelle entsprechend anpassen. Die wirklichen Kampfwerte bleiben unverändert. Der grün/rot/graue Abstandskreis ist davon unabhängig und unverändert.
|
||||||
|
|
||||||
|
Der Keller besitzt zusätzlich einen Hintergrundaufzug als anklickbaren Rückweg, einen Versorgungsmonitor sowie Wandnischen, Schattenstreifen und einen Vordergrundabschluss. Ein großer Hintergrund deckt auch freie Bildschirmbereiche ab; Bedienelemente bleiben im sicheren Referenzrahmen. Alle Teile sind austauschbare, graue Platzhalter. Die separate Ebenen-/Schleusenanzeige bleibt außerhalb des Monitors. Aktuelle Werte stammen weiterhin ausschließlich aus den Gameplay-Schnittstellen.
|
||||||
|
|
||||||
|
Decke, Boden, Wandhintergrund und äußere Seitenbegrenzungen passen ihre Breite an die Kellerkamera an. Die Bedienelemente bleiben im sicheren Referenzbereich; auch auf breiten Bildschirmen entstehen keine freien Seitenflächen außerhalb des Raums.
|
||||||
+11
-2
@@ -66,8 +66,17 @@ Vorräte / Grenzen, tatsächliche Entnahme beim Schießen, Nachfüllen, Transpor
|
|||||||
|
|
||||||
## Keller im Inspector bearbeiten
|
## Keller im Inspector bearbeiten
|
||||||
|
|
||||||
`Canvas_SupplyBasement / SupplyBasement` ist im gespeicherten Zustand deaktiviert. Außerhalb von Play zum Bearbeiten einschalten, danach wieder ausschalten. Darunter liegen die Deckenanschlüsse (`TowerConnections`), der Wandverteiler (`Distributor`), der LKW (`TruckLane/Truck`), die drei Bodenbehälter (`StorageTank_0` bis `_2`) und der Raumquerschnitt (`RoomArchitecture`). Alle Grafiken sind austauschbare Platzhalter.
|
`SupplyBasementRoom / RoomCanvas / SupplyBasement` ist dauerhaft aktiv und als World-Space-Canvas außerhalb der Turmebene angelegt. Zum Bearbeiten in der Scene-Ansicht auswählen und mit F fokussieren. Darunter liegen die Deckenanschlüsse (`TowerConnections`), der Wandverteiler (`Distributor`), der LKW (`TruckLane/Truck`), die drei Bodenbehälter (`StorageTank_0` bis `_2`) und der Raumquerschnitt (`RoomArchitecture`). Alle Grafiken sind austauschbare Platzhalter.
|
||||||
|
|
||||||
`LiftCallButton` enthält das klappende Schild und den Controller mit allen zugewiesenen Referenzen; `ElevatorDoors` die Übergangstüren. Die Ansicht schaltet innerhalb derselben Szene um. Das Buch wird nur ausgeblendet; das Gameplay läuft weiter. Aufzug-Sound fehlt noch.
|
`LiftCallButton` enthält das klappende Schild und den Controller mit allen zugewiesenen Referenzen; `ElevatorDoors` die Übergangstüren. Die Ansicht schaltet innerhalb derselben Szene auf BasementCamera um; die normale Spielkamera bleibt aktiv. Das Buch wird nur ausgeblendet; das Gameplay läuft weiter. LiftCallButton und ElevatorDoors bleiben unter Canvas_SupplyBasement. Aufzug-Sound fehlt noch.
|
||||||
|
|
||||||
Ohne Backend zeigen die Mengen einen Strich. Anschluss- und Prioritätsklicks verändern dann nur die Bedienvorschau; der LKW fährt eine Vorschau. Ein zugewiesenes Backend ersetzt diese Vorschau vollständig durch seine Zustände. Abstecken muss dort das Nachfüllen stoppen, nicht vorhandene Restmunition löschen.
|
Ohne Backend zeigen die Mengen einen Strich. Anschluss- und Prioritätsklicks verändern dann nur die Bedienvorschau; der LKW fährt eine Vorschau. Ein zugewiesenes Backend ersetzt diese Vorschau vollständig durch seine Zustände. Abstecken muss dort das Nachfüllen stoppen, nicht vorhandene Restmunition löschen.
|
||||||
|
|
||||||
|
|
||||||
|
## Schleusen- und Statistikdaten
|
||||||
|
|
||||||
|
Optional implementiert derselbe oder ein anderer Gameplay-Controller zusätzlich `ICombatOverview.ReadCombatOverview()`. Im Keller unter `SupplyBasementRoom/RoomCanvas/SupplyBasement/BattleOverview` dessen Komponente dem Feld Combat Backend zuweisen. Das Ergebnis enthält floorName und bis zu vier GateOverviewState mit direction, health, maxHealth, isOpen, underAttack, hitSequence. maxHealth <= 0 bedeutet unbekannte HP. hitSequence bei jedem tatsächlichen Treffer erhöhen, nicht bei jeder UI-Abfrage. Die UI liest Zustände ungefähr alle 0,15 Sekunden; der Controller muss unabhängig davon arbeiten.
|
||||||
|
|
||||||
|
Die Statistikseite verwendet bereits die optionalen consumedPerSecond-Felder der SupplyState-Snapshots. Gemessene Einheiten pro Sekunde liefern, unabhängig von der vom Spieler gewählten Anzeigeeinheit. Ohne Messung null zurückgeben, nicht 0 erfinden. Die Seite zeigt daraus pro Sekunde/Minute und die geschätzte Verteilerreserve ohne weitere Lieferung. Aktuelle Turm-Magazine weiterhin pro instanceId führen.
|
||||||
|
|
||||||
|
Spielereingaben auf die Turmebene während des Kellerbesuchs sperren (IsInBasement bzw. EventSystem-UI-Prüfung), ohne Gegner-, Schuss- und Versorgungssimulation zu pausieren. Die normale Spielkamera bleibt aktiv; die zusätzliche Kellerkamera ist nicht als MainCamera getaggt.
|
||||||
Reference in new issue
Block a user