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:
|
||||
Mauer. Werte vorl\xE4ufig."
|
||||
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
previewSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
baseSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
basePrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||
gameplayPrefab: {fileID: 0}
|
||||
gameplayPrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||
previewLayers:
|
||||
- sprite: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.36
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 0
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 1
|
||||
targets: 1
|
||||
rotation: 0
|
||||
|
||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
||||
description: "Minigun: Schaden 5 \xB7 8 Sch\xFCsse/s. Munition: Kugeln. Baufl\xE4che:
|
||||
Mauer. Werte vorl\xE4ufig."
|
||||
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
previewSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
baseSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
basePrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||
gameplayPrefab: {fileID: 0}
|
||||
gameplayPrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||
previewLayers:
|
||||
- sprite: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.36
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 0
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 1
|
||||
targets: 1
|
||||
rotation: 0
|
||||
|
||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
||||
description: "Sniper: Schaden 60 \xB7 0,35 Sch\xFCsse/s. Munition: Kugeln. Baufl\xE4che:
|
||||
Mauer. Werte vorl\xE4ufig."
|
||||
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
previewSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
baseSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
basePrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||
gameplayPrefab: {fileID: 0}
|
||||
gameplayPrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||
previewLayers:
|
||||
- sprite: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.36
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 0
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 1
|
||||
targets: 3
|
||||
rotation: 2
|
||||
|
||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
||||
description: "Granatenwerfer: Schaden 40 \xB7 0,7 Sch\xFCsse/s. Munition: Sprengstoff.
|
||||
Baufl\xE4che: Mauer. Werte vorl\xE4ufig."
|
||||
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
previewSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
baseSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
basePrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||
gameplayPrefab: {fileID: 0}
|
||||
gameplayPrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||
previewLayers:
|
||||
- sprite: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.36
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 0
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 2
|
||||
targets: 2
|
||||
rotation: 0
|
||||
|
||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
||||
description: "Howitzer: Schaden 40 \xB7 0,25 Sch\xFCsse/s. Munition: Sprengstoff
|
||||
+ \xD6l. Baufl\xE4che: Mauer. Werte vorl\xE4ufig."
|
||||
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
previewSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
baseSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
basePrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||
gameplayPrefab: {fileID: 0}
|
||||
gameplayPrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||
previewLayers:
|
||||
- sprite: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.36
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 0
|
||||
minimumRange: 1.5
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 3
|
||||
targets: 4
|
||||
rotation: 2
|
||||
@@ -118,4 +130,5 @@ MonoBehaviour:
|
||||
Alle physischen Waffen teilen Physical, Energiewaffen EnergyCells. supply ist
|
||||
die Quelle f\xFCr Magazinkosten; combat.ammoPerShot/ammoPerSecond/energyPerSecond
|
||||
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
|
||||
unendlicher Feuerrate. Werte vorl\xE4ufig."
|
||||
icon: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
previewSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
baseSprite: {fileID: -543501159, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
basePrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||
gameplayPrefab: {fileID: 0}
|
||||
gameplayPrefab: {fileID: 6005720031823437216, guid: cb948f7d9fa2f3749b14ffacdbdcf00d, type: 3}
|
||||
previewLayers:
|
||||
- sprite: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.36
|
||||
baseSize: {x: 0.5, y: 0.5}
|
||||
placementRadius: 0.6
|
||||
allowPlayerRotation: 0
|
||||
allowPlayerRotation: 1
|
||||
blocksMovement: 0
|
||||
requirePathValidation: 0
|
||||
countsTowardWallLimit: 1
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 2
|
||||
continuousAttack: 1
|
||||
unlimitedRange: 0
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 2
|
||||
targets: 2
|
||||
rotation: 1
|
||||
|
||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
||||
description: "Laser Gewehr: Schaden 40 \xB7 2 Sch\xFCsse/s. Baufl\xE4che: Mauer.
|
||||
Werte vorl\xE4ufig."
|
||||
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
previewSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
baseSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
basePrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||
gameplayPrefab: {fileID: 0}
|
||||
gameplayPrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||
previewLayers:
|
||||
- sprite: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.36
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 0
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 1
|
||||
targets: 1
|
||||
rotation: 0
|
||||
|
||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
||||
description: "Bouncer: Schaden 8 \xB7 2 Sch\xFCsse/s. Baufl\xE4che: Mauer. Werte
|
||||
vorl\xE4ufig."
|
||||
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
previewSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
baseSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
basePrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||
gameplayPrefab: {fileID: 0}
|
||||
gameplayPrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||
previewLayers:
|
||||
- sprite: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.36
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 0
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 1
|
||||
targets: 1
|
||||
rotation: 0
|
||||
|
||||
@@ -23,16 +23,25 @@ MonoBehaviour:
|
||||
description: "Pusher: Schaden 8 \xB7 4 Sch\xFCsse/s. Baufl\xE4che: Mauer. Werte
|
||||
vorl\xE4ufig."
|
||||
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
previewSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
baseSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
basePrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||
gameplayPrefab: {fileID: 0}
|
||||
gameplayPrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||
previewLayers:
|
||||
- sprite: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.36
|
||||
baseSize: {x: 0.5, y: 0.5}
|
||||
placementRadius: 0.6
|
||||
allowPlayerRotation: 0
|
||||
allowPlayerRotation: 1
|
||||
blocksMovement: 0
|
||||
requirePathValidation: 0
|
||||
countsTowardWallLimit: 1
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 0
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 2
|
||||
targets: 2
|
||||
rotation: 1
|
||||
|
||||
@@ -23,16 +23,25 @@ MonoBehaviour:
|
||||
description: "Knocker: Schaden 8 \xB7 0,7 Sch\xFCsse/s. Baufl\xE4che: Mauer. Werte
|
||||
vorl\xE4ufig."
|
||||
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
previewSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
baseSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
basePrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||
gameplayPrefab: {fileID: 0}
|
||||
gameplayPrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||
previewLayers:
|
||||
- sprite: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.36
|
||||
baseSize: {x: 0.5, y: 0.5}
|
||||
placementRadius: 0.6
|
||||
allowPlayerRotation: 0
|
||||
allowPlayerRotation: 1
|
||||
blocksMovement: 0
|
||||
requirePathValidation: 0
|
||||
countsTowardWallLimit: 1
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 0
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 2
|
||||
targets: 2
|
||||
rotation: 1
|
||||
|
||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
||||
description: "Laser-Beam: Schaden 40/s \xB7 Kontinuierlich. Dauerverbrauch statt
|
||||
unendlicher Feuerrate. Werte vorl\xE4ufig."
|
||||
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
previewSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
baseSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
basePrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||
gameplayPrefab: {fileID: 0}
|
||||
gameplayPrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||
previewLayers:
|
||||
- sprite: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.36
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 1
|
||||
unlimitedRange: 1
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 1
|
||||
targets: 1
|
||||
rotation: 0
|
||||
|
||||
@@ -23,16 +23,25 @@ MonoBehaviour:
|
||||
description: "Laser Shotgun: Schaden 20 \xB7 0,7 Sch\xFCsse/s. Baufl\xE4che: Mauer.
|
||||
Werte vorl\xE4ufig."
|
||||
icon: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
previewSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
baseSprite: {fileID: -16298893, guid: e5b69ac5c61ad9b45bf96d69b7ab3b7a, type: 3}
|
||||
basePrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||
gameplayPrefab: {fileID: 0}
|
||||
gameplayPrefab: {fileID: 2743134360449186016, guid: a2ef118a6f124d4429c56b3f72e9310d, type: 3}
|
||||
previewLayers:
|
||||
- sprite: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.36
|
||||
baseSize: {x: 0.5, y: 0.5}
|
||||
placementRadius: 0.6
|
||||
allowPlayerRotation: 0
|
||||
allowPlayerRotation: 1
|
||||
blocksMovement: 0
|
||||
requirePathValidation: 0
|
||||
countsTowardWallLimit: 1
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 0
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 2
|
||||
targets: 2
|
||||
rotation: 1
|
||||
|
||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
||||
description: "Caltrops: Bodenfalle \xB7 5 Kontakte. Baufl\xE4che: Innenfeld. Werte
|
||||
vorl\xE4ufig."
|
||||
icon: {fileID: 0}
|
||||
previewSprite: {fileID: 0}
|
||||
baseSprite: {fileID: 0}
|
||||
basePrefab: {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
|
||||
baseShape: 1
|
||||
baseRadius: 0.71
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 0
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 4
|
||||
targets: 2
|
||||
rotation: 3
|
||||
|
||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
||||
description: "Generator: Erzeugt Energie \xB7 Kapazit\xE4t +100. Baufl\xE4che:
|
||||
Mauer. Werte vorl\xE4ufig."
|
||||
icon: {fileID: 0}
|
||||
previewSprite: {fileID: 0}
|
||||
baseSprite: {fileID: 0}
|
||||
basePrefab: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.36
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 0
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 0
|
||||
targets: 0
|
||||
rotation: 3
|
||||
|
||||
@@ -22,10 +22,19 @@ MonoBehaviour:
|
||||
summary: Sammelt Leichen
|
||||
description: "Sauger: Sammelt Leichen. Baufl\xE4che: Mauer. Werte vorl\xE4ufig."
|
||||
icon: {fileID: 0}
|
||||
previewSprite: {fileID: 0}
|
||||
baseSprite: {fileID: 0}
|
||||
basePrefab: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.36
|
||||
@@ -50,6 +59,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 0
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 3
|
||||
targets: 2
|
||||
rotation: 3
|
||||
|
||||
@@ -22,10 +22,19 @@ MonoBehaviour:
|
||||
summary: Reflektiert Strahlen
|
||||
description: "Spiegel: Reflektiert Strahlen. Baufl\xE4che: Innenfeld. Werte vorl\xE4ufig."
|
||||
icon: {fileID: 0}
|
||||
previewSprite: {fileID: 0}
|
||||
baseSprite: {fileID: 0}
|
||||
basePrefab: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.25
|
||||
@@ -50,6 +59,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 0
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 0
|
||||
targets: 0
|
||||
rotation: 2
|
||||
|
||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
||||
description: "Reparaturtrupp: Repariert Schleusen. Baufl\xE4che: Schleuse. Werte
|
||||
vorl\xE4ufig."
|
||||
icon: {fileID: 0}
|
||||
previewSprite: {fileID: 0}
|
||||
baseSprite: {fileID: 0}
|
||||
basePrefab: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.36
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 0
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 0
|
||||
targets: 5
|
||||
rotation: 3
|
||||
|
||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
||||
description: "Bombardement: Fl\xE4chenangriff \xB7 120 s Cooldown. Baufl\xE4che:
|
||||
Innenfeld. Werte vorl\xE4ufig."
|
||||
icon: {fileID: 0}
|
||||
previewSprite: {fileID: 0}
|
||||
baseSprite: {fileID: 0}
|
||||
basePrefab: {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
|
||||
baseShape: 0
|
||||
baseRadius: 0.36
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 1
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 3
|
||||
targets: 4
|
||||
rotation: 3
|
||||
|
||||
@@ -23,10 +23,19 @@ MonoBehaviour:
|
||||
description: "Barrikade: Blockiert Wege \xB7 30 s. Baufl\xE4che: Innenfeld. Werte
|
||||
vorl\xE4ufig."
|
||||
icon: {fileID: 0}
|
||||
previewSprite: {fileID: 0}
|
||||
baseSprite: {fileID: 0}
|
||||
basePrefab: {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
|
||||
baseShape: 1
|
||||
baseRadius: 1.59
|
||||
@@ -51,6 +60,9 @@ MonoBehaviour:
|
||||
ammoPerSecond: 0
|
||||
continuousAttack: 0
|
||||
unlimitedRange: 0
|
||||
minimumRange: 0
|
||||
coverage: 0
|
||||
forwardAngleOffset: 90
|
||||
attackShape: 4
|
||||
targets: 0
|
||||
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 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");
|
||||
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");
|
||||
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");}
|
||||
|
||||
@@ -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>();
|
||||
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;
|
||||
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)));
|
||||
@@ -73,17 +73,19 @@ public static class SupplyBasementValidation
|
||||
catch(Exception e){EditorApplication.update-=run;Debug.LogException(e);EditorApplication.Exit(1);}
|
||||
};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 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;
|
||||
canvas.renderMode=RenderMode.ScreenSpaceCamera;canvas.worldCamera=camera;canvas.planeDistance=1;canvas.sortingOrder=200;
|
||||
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 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());
|
||||
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.")]
|
||||
[Min(0)] public float continuousDamagePerSecond, ammoPerSecond;
|
||||
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 TargetMode targets;
|
||||
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")]
|
||||
[Tooltip("Bild im Bau-, Index- und Upgrademenü.")]
|
||||
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;
|
||||
[InspectorName("Book Icon")] public Sprite icon;
|
||||
[Tooltip("Vollständiges Objekt für die spätere Gameplay-Anbindung.")]
|
||||
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")]
|
||||
public BuildRegion region;
|
||||
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;
|
||||
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 source = renderer && renderer.sprite ? renderer.sprite.bounds.size : Vector2.one;
|
||||
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;
|
||||
[Header("Authored scene references")]
|
||||
public GameObject basementView;
|
||||
public BasementRoomView roomView;
|
||||
public GameObject handbookRoot;
|
||||
public CanvasGroup handbookVisibility;
|
||||
public HandbookController handbook;
|
||||
@@ -61,7 +62,7 @@ namespace DepthsOfGreed.HandbookUI
|
||||
{
|
||||
signScale = liftSign.localScale;
|
||||
truckScale = truck.localScale;
|
||||
basementView.SetActive(false);
|
||||
if(roomView)roomView.SetVisible(false);else basementView.SetActive(false);
|
||||
doorGroup.gameObject.SetActive(false);
|
||||
liftButton.onClick.AddListener(UseLift);
|
||||
for (int i = 0; i < connectionButtons.Length; i++)
|
||||
@@ -105,7 +106,7 @@ namespace DepthsOfGreed.HandbookUI
|
||||
yield return Doors(true);
|
||||
if (handbook) handbook.Close();
|
||||
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.
|
||||
handbookVisibility.alpha = IsInBasement ? 0f : 1f;
|
||||
handbookVisibility.interactable = !IsInBasement;
|
||||
@@ -181,22 +182,21 @@ namespace DepthsOfGreed.HandbookUI
|
||||
}
|
||||
private void Update()
|
||||
{
|
||||
if (!IsInBasement) return;
|
||||
if (Time.unscaledTime > nextRefresh) { nextRefresh = Time.unscaledTime + 0.25f; RefreshConnections(); }
|
||||
if (HasSupplyBackend) { RenderBackendTruck(); return; }
|
||||
if (!animateTruckPreview) { truckStatus.text = "LKW bereit"; return; }
|
||||
if (Time.unscaledTime < truckPauseUntil) return;
|
||||
if (Time.time < truckPauseUntil) return;
|
||||
float destination = unloading ? unloadingPosition : loadingPositions[previewKind];
|
||||
var p = truck.anchoredPosition;
|
||||
float direction = destination - p.x;
|
||||
if (Mathf.Abs(direction) > 0.1f)
|
||||
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]);
|
||||
if (Mathf.Abs(p.x - destination) < 0.1f)
|
||||
{
|
||||
truckStatus.text = (unloading ? "Abladen" : "Beladen: " + kinds[previewKind]);
|
||||
truckPauseUntil = Time.unscaledTime + loadingPause;
|
||||
truckPauseUntil = Time.time + loadingPause;
|
||||
if (unloading) previewKind = Priority >= 0 ? Priority : (previewKind + 1) % 3;
|
||||
unloading = !unloading;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,9 @@ namespace DepthsOfGreed.HandbookUI
|
||||
private CanvasGroup bookGroup;
|
||||
private GameObject prefab, preview, controls;
|
||||
private Transform container, area;
|
||||
[Header("Mauer-Baubereich")]
|
||||
[Tooltip("Baugrenzen und Mittelpunkt. Konfiguration auf PlacementTest/BuildArea.")]
|
||||
[SerializeField] private WallBuildArea buildArea;
|
||||
private SpriteRenderer ghost, footprint;
|
||||
private Sprite ghostPlaceholder;
|
||||
private TMP_Text controlsText, feedback, resources, buildHint;
|
||||
@@ -46,7 +49,11 @@ namespace DepthsOfGreed.HandbookUI
|
||||
private LineRenderer previewOutline, innerOutline, outerOutline;
|
||||
private Material lineMaterial;
|
||||
private float placementAngle;
|
||||
private float manualAimOffset;
|
||||
public bool alignPreviewToCenter = true;
|
||||
[SerializeField] private LayeredTowerPreview layeredPreview;
|
||||
private GeneralFeedbackController general;
|
||||
[SerializeField] private AttackRangePreview attackRangePreview;
|
||||
|
||||
public void Initialize(HandbookController owner, BuildMenuController menu, RectTransform openBook, GameObject testPrefab, MonoBehaviour gameplayBackend = null)
|
||||
{
|
||||
@@ -62,7 +69,8 @@ namespace DepthsOfGreed.HandbookUI
|
||||
enabled = false;
|
||||
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");
|
||||
preview = test.transform.Find("PlacementPreview").gameObject;
|
||||
ghost = preview.transform.Find("GhostVisual").GetComponent<SpriteRenderer>();
|
||||
@@ -87,8 +95,14 @@ namespace DepthsOfGreed.HandbookUI
|
||||
}
|
||||
resources = menu.transform.Find("RightPage/ResourcesText").GetComponent<TMP_Text>();
|
||||
buildHint = menu.transform.Find("BuildHintText").GetComponent<TMP_Text>();
|
||||
innerRadius = RadiusOf("BackgroundTower", 3f);
|
||||
outerRadius = RadiusOf("BackgroundOuterwall", 4f);
|
||||
if (!buildArea)
|
||||
{
|
||||
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;
|
||||
else
|
||||
{
|
||||
@@ -101,6 +115,7 @@ namespace DepthsOfGreed.HandbookUI
|
||||
previewOutline = MakeCircle("PlacementOutline", preview.transform, 11);
|
||||
innerOutline = MakeCircle("BuildArea_Inner", area, 8);
|
||||
outerOutline = MakeCircle("BuildArea_Outer", area, 8);
|
||||
innerOutline.useWorldSpace = outerOutline.useWorldSpace = true;
|
||||
DrawCircle(innerOutline, innerRadius);
|
||||
DrawCircle(outerOutline, outerRadius);
|
||||
ShowGuides(false);
|
||||
@@ -109,28 +124,21 @@ namespace DepthsOfGreed.HandbookUI
|
||||
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)
|
||||
{
|
||||
if (!enabled || IsDragging || IsWaitingForReturn || entry == null) return false;
|
||||
var availability = Backend.CheckAvailability(entry);
|
||||
if (!availability.Allowed) { failedTypeId = entry.id; Message(availability.message); return false; }
|
||||
draggingEntry = entry; placementAngle = 0; LastFailure = null;
|
||||
ghost.sprite = entry.previewSprite ? entry.previewSprite : ghostPlaceholder;
|
||||
Vector2 visualSize = entry.baseShape == BaseShape.Rectangle ? entry.baseSize : Vector2.one * entry.baseRadius * 1.4142136f;
|
||||
ghost.transform.localScale = new Vector3(visualSize.x / ghost.sprite.bounds.size.x, visualSize.y / ghost.sprite.bounds.size.y, 1);
|
||||
draggingEntry = entry; placementAngle = 0; manualAimOffset = 0; LastFailure = null;
|
||||
if(attackRangePreview)attackRangePreview.Show(entry);
|
||||
if(layeredPreview)layeredPreview.Show(entry,ghostPlaceholder);
|
||||
ghost.gameObject.SetActive(!layeredPreview);
|
||||
IsDragging = true;
|
||||
preview.SetActive(true);
|
||||
footprint.transform.localScale = new Vector3(entry.placementRadius * 2f, entry.placementRadius * 2f, 1f);
|
||||
DrawCircle(previewOutline, entry.placementRadius);
|
||||
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;
|
||||
ShowGuides(true);
|
||||
foreach (var record in Backend.BuiltObjects)
|
||||
@@ -153,12 +161,18 @@ namespace DepthsOfGreed.HandbookUI
|
||||
if (!plane.Raycast(ray, out float distance)) return;
|
||||
target = ray.GetPoint(distance);
|
||||
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);
|
||||
string reason = Check(draggingEntry, target);
|
||||
bool outside = Backend.CheckBuild(new BuildRequest(draggingEntry, target, placementAngle)).failure == BuildFailure.OutsideRegion;
|
||||
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);
|
||||
ghost.color = new Color(1f, 1f, 1f, outside ? 0.3f : 0.65f);
|
||||
if(layeredPreview)layeredPreview.SetOpacity(outside?.3f:.8f);
|
||||
Tint(previewOutline, color);
|
||||
DrawCircle(previewOutline, draggingEntry.placementRadius, outside);
|
||||
controlsText.text = "Loslassen: Platzieren · Rechtsklick / Esc: Abbrechen";
|
||||
@@ -222,7 +236,7 @@ namespace DepthsOfGreed.HandbookUI
|
||||
{
|
||||
if (Mouse.current.rightButton.wasPressedThisFrame ||
|
||||
(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;
|
||||
if (!lowered) bookGroup.blocksRaycasts = true;
|
||||
@@ -293,7 +307,8 @@ namespace DepthsOfGreed.HandbookUI
|
||||
for (int i = 0; i < segments; i++)
|
||||
{
|
||||
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.
|
||||
if (dashed)
|
||||
@@ -307,3 +322,5 @@ namespace DepthsOfGreed.HandbookUI
|
||||
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
|
||||
Reference in new issue
Block a user