diff --git a/Assets/Materials/Build.meta b/Assets/Materials/Build.meta new file mode 100644 index 00000000..d0de8cad --- /dev/null +++ b/Assets/Materials/Build.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b8bc301eacaa25d4a9bbdf35f989ad2e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Materials/Build/WandAreaBrush.mat b/Assets/Materials/Build/WandAreaBrush.mat new file mode 100644 index 00000000..c7c6e3bb --- /dev/null +++ b/Assets/Materials/Build/WandAreaBrush.mat @@ -0,0 +1,79 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: WandAreaBrush + m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0} + m_ShaderKeywords: _ALPHAPREMULTIPLY_ON + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: 3000 + stringTagMap: + RenderType: Transparent + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + 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} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _DstBlend: 10 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 3 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 0 + m_Colors: + - _Color: {r: 0.031886935, g: 1, b: 0, a: 0.48235294} + - _EmissionColor: {r: 0.014443844, g: 0.014443844, b: 0.014443844, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Materials/Build/WandAreaBrush.mat.meta b/Assets/Materials/Build/WandAreaBrush.mat.meta new file mode 100644 index 00000000..fce4f1f5 --- /dev/null +++ b/Assets/Materials/Build/WandAreaBrush.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9e00eb9bc0cd7be478e657c251ce0680 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Materials/Build/WandTop.mat b/Assets/Materials/Build/WandTop.mat new file mode 100644 index 00000000..03962f30 --- /dev/null +++ b/Assets/Materials/Build/WandTop.mat @@ -0,0 +1,82 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!21 &2100000 +Material: + serializedVersion: 6 + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_Name: WandTop + m_Shader: {fileID: 4800000, guid: 9027c75630615624b8d7e4b02852aa4e, type: 3} + m_ShaderKeywords: + m_LightmapFlags: 4 + m_EnableInstancingVariants: 0 + m_DoubleSidedGI: 0 + m_CustomRenderQueue: -1 + stringTagMap: {} + disabledShaderPasses: [] + m_SavedProperties: + serializedVersion: 3 + m_TexEnvs: + - _BumpMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailAlbedoMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailMask: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _DetailNormalMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _EmissionMap: + 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} + - _MetallicGlossMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _OcclusionMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + - _ParallaxMap: + m_Texture: {fileID: 0} + m_Scale: {x: 1, y: 1} + m_Offset: {x: 0, y: 0} + m_Floats: + - _BumpScale: 1 + - _Cutoff: 0.5 + - _DetailNormalMapScale: 1 + - _Dst: 1 + - _DstBlend: 0 + - _GlossMapScale: 1 + - _Glossiness: 0.5 + - _GlossyReflections: 1 + - _Metallic: 0 + - _Mode: 0 + - _OcclusionStrength: 1 + - _Parallax: 0.02 + - _ScaleX: 1 + - _ScaleY: 1 + - _SmoothnessTextureChannel: 0 + - _SpecularHighlights: 1 + - _Src: 1 + - _SrcBlend: 1 + - _UVSec: 0 + - _ZWrite: 1 + m_Colors: + - _Color: {r: 1, g: 1, b: 1, a: 1} + - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} + m_BuildTextureStacks: [] diff --git a/Assets/Materials/Build/WandTop.mat.meta b/Assets/Materials/Build/WandTop.mat.meta new file mode 100644 index 00000000..05bed763 --- /dev/null +++ b/Assets/Materials/Build/WandTop.mat.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 5d46dd5277a083f49992dd646af64e34 +NativeFormatImporter: + externalObjects: {} + mainObjectFileID: 2100000 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scenes/Tests/BuildModeTest.unity b/Assets/Scenes/Tests/BuildModeTest.unity new file mode 100644 index 00000000..1af942d3 --- /dev/null +++ b/Assets/Scenes/Tests/BuildModeTest.unity @@ -0,0 +1,2026 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!29 &1 +OcclusionCullingSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_OcclusionBakeSettings: + smallestOccluder: 5 + smallestHole: 0.25 + backfaceThreshold: 100 + m_SceneGUID: 00000000000000000000000000000000 + m_OcclusionCullingData: {fileID: 0} +--- !u!104 &2 +RenderSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_Fog: 0 + m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1} + m_FogMode: 3 + m_FogDensity: 0.01 + m_LinearFogStart: 0 + m_LinearFogEnd: 300 + m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1} + m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1} + m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1} + m_AmbientIntensity: 1 + m_AmbientMode: 0 + m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1} + m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0} + m_HaloStrength: 0.5 + m_FlareStrength: 1 + m_FlareFadeSpeed: 3 + m_HaloTexture: {fileID: 0} + m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0} + m_DefaultReflectionMode: 0 + m_DefaultReflectionResolution: 128 + m_ReflectionBounces: 1 + m_ReflectionIntensity: 1 + m_CustomReflection: {fileID: 0} + m_Sun: {fileID: 0} + m_IndirectSpecularColor: {r: 0.44657874, g: 0.49641275, b: 0.5748172, a: 1} + m_UseRadianceAmbientProbe: 0 +--- !u!157 &3 +LightmapSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_GIWorkflowMode: 1 + m_GISettings: + serializedVersion: 2 + m_BounceScale: 1 + m_IndirectOutputScale: 1 + m_AlbedoBoost: 1 + m_EnvironmentLightingMode: 0 + m_EnableBakedLightmaps: 1 + m_EnableRealtimeLightmaps: 0 + m_LightmapEditorSettings: + serializedVersion: 12 + m_Resolution: 2 + m_BakeResolution: 40 + m_AtlasSize: 1024 + m_AO: 0 + m_AOMaxDistance: 1 + m_CompAOExponent: 1 + m_CompAOExponentDirect: 0 + m_ExtractAmbientOcclusion: 0 + m_Padding: 2 + m_LightmapParameters: {fileID: 0} + m_LightmapsBakeMode: 1 + m_TextureCompression: 1 + m_FinalGather: 0 + m_FinalGatherFiltering: 1 + m_FinalGatherRayCount: 256 + m_ReflectionCompression: 2 + m_MixedBakeMode: 2 + m_BakeBackend: 1 + m_PVRSampling: 1 + m_PVRDirectSampleCount: 32 + m_PVRSampleCount: 512 + m_PVRBounces: 2 + m_PVREnvironmentSampleCount: 256 + m_PVREnvironmentReferencePointCount: 2048 + m_PVRFilteringMode: 1 + m_PVRDenoiserTypeDirect: 1 + m_PVRDenoiserTypeIndirect: 1 + m_PVRDenoiserTypeAO: 1 + m_PVRFilterTypeDirect: 0 + m_PVRFilterTypeIndirect: 0 + m_PVRFilterTypeAO: 0 + m_PVREnvironmentMIS: 1 + m_PVRCulling: 1 + m_PVRFilteringGaussRadiusDirect: 1 + m_PVRFilteringGaussRadiusIndirect: 5 + m_PVRFilteringGaussRadiusAO: 2 + m_PVRFilteringAtrousPositionSigmaDirect: 0.5 + m_PVRFilteringAtrousPositionSigmaIndirect: 2 + m_PVRFilteringAtrousPositionSigmaAO: 1 + m_ExportTrainingData: 0 + m_TrainingDataDestination: TrainingData + m_LightProbeSampleCountMultiplier: 4 + m_LightingDataAsset: {fileID: 0} + m_LightingSettings: {fileID: 0} +--- !u!196 &4 +NavMeshSettings: + serializedVersion: 2 + m_ObjectHideFlags: 0 + m_BuildSettings: + serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.4 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + maxJobWorkers: 0 + preserveTilesOutsideBounds: 0 + debug: + m_Flags: 0 + m_NavMeshData: {fileID: 0} +--- !u!1 &89992084 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 89992085} + - component: {fileID: 89992086} + - component: {fileID: 89992087} + m_Layer: 5 + m_Name: Handle + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &89992085 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 89992084} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 420274207} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 20, y: 20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &89992086 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 89992084} + m_CullTransparentMesh: 1 +--- !u!114 &89992087 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 89992084} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!1 &420274206 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 420274207} + m_Layer: 5 + m_Name: Sliding Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &420274207 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 420274206} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 89992085} + m_Father: {fileID: 2062259699} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -20, y: -20} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &489915452 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 489915453} + - component: {fileID: 489915454} + m_Layer: 0 + m_Name: BuildModeTestController + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &489915453 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 489915452} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 35.671333, y: 1.8509755, z: 31.046001} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1538301813} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &489915454 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 489915452} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 94cb5833fa52cfc4392f5daf80bc17b3, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1 &548360276 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 548360277} + - component: {fileID: 548360279} + - component: {fileID: 548360278} + m_Layer: 0 + m_Name: curs + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &548360277 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 548360276} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 1.51, z: 0} + m_LocalScale: {x: 0.15, y: 1.5, z: 0.15} + m_Children: [] + m_Father: {fileID: 1458263242} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &548360278 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 548360276} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &548360279 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 548360276} + m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &648965860 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 648965865} + - component: {fileID: 648965864} + - component: {fileID: 648965863} + - component: {fileID: 648965862} + - component: {fileID: 648965861} + m_Layer: 5 + m_Name: Cheat Console Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &648965861 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 648965860} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: c7d73af69c9e8614080ca76faff3ae13, type: 3} + m_Name: + m_EditorClassIdentifier: + ConsoleParent: {fileID: 1951021496} + CheatInputField: {fileID: 2114196948} + ConsoleOutput: {fileID: 1078632927} + ConsoleScroll: {fileID: 1085664058} +--- !u!114 &648965862 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 648965860} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &648965863 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 648965860} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!223 &648965864 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 648965860} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 100 + m_TargetDisplay: 0 +--- !u!224 &648965865 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 648965860} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: + - {fileID: 1951021497} + m_Father: {fileID: 0} + m_RootOrder: 3 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!1 &783440545 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 783440546} + - component: {fileID: 783440547} + m_Layer: 5 + m_Name: Viewport + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &783440546 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 783440545} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1078632924} + m_Father: {fileID: 1085664059} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: -17, y: 0} + m_Pivot: {x: 0, y: 1} +--- !u!222 &783440547 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 783440545} + m_CullTransparentMesh: 1 +--- !u!1 &1078632923 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1078632924} + - component: {fileID: 1078632926} + - component: {fileID: 1078632927} + - component: {fileID: 1078632925} + m_Layer: 5 + m_Name: Console Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1078632924 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1078632923} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 783440546} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 1} +--- !u!114 &1078632925 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1078632923} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalFit: 0 + m_VerticalFit: 2 +--- !u!222 &1078632926 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1078632923} + m_CullTransparentMesh: 1 +--- !u!114 &1078632927 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1078632923} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 0 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: +--- !u!1 &1085664057 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1085664059} + - component: {fileID: 1085664060} + - component: {fileID: 1085664058} + m_Layer: 5 + m_Name: Scroll View + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1085664058 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1085664057} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 1aa08ab6e0800fa44ae55d278d1423e3, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Content: {fileID: 1078632924} + m_Horizontal: 0 + m_Vertical: 1 + m_MovementType: 2 + m_Elasticity: 0.1 + m_Inertia: 0 + m_DecelerationRate: 0.135 + m_ScrollSensitivity: 40 + m_Viewport: {fileID: 783440546} + m_HorizontalScrollbar: {fileID: 0} + m_VerticalScrollbar: {fileID: 2062259698} + m_HorizontalScrollbarVisibility: 2 + m_VerticalScrollbarVisibility: 1 + m_HorizontalScrollbarSpacing: -3 + m_VerticalScrollbarSpacing: -3 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!224 &1085664059 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1085664057} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 783440546} + m_Father: {fileID: 1326854590} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 1.6949463, y: -105.1} + m_SizeDelta: {x: -3.3900743, y: 200} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &1085664060 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1085664057} + m_CullTransparentMesh: 1 +--- !u!1 &1134778609 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1134778610} + - component: {fileID: 1134778611} + m_Layer: 0 + m_Name: LotViewUIController + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1134778610 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1134778609} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 1538301813} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &1134778611 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1134778609} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 9727801f799a10a41875182ace337639, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1 &1139364661 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1139364662} + - component: {fileID: 1139364665} + - component: {fileID: 1139364664} + m_Layer: 0 + m_Name: Plane + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1139364662 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1139364661} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 3.232, z: 0} + m_LocalScale: {x: 0.75, y: 0.75, z: 0} + m_Children: [] + m_Father: {fileID: 1458263242} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1139364664 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1139364661} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 5d46dd5277a083f49992dd646af64e34, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1139364665 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1139364661} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1157865906 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1157865908} + - component: {fileID: 1157865907} + m_Layer: 0 + m_Name: Directional Light + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!108 &1157865907 +Light: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1157865906} + m_Enabled: 1 + serializedVersion: 10 + m_Type: 1 + m_Shape: 0 + m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1} + m_Intensity: 1 + m_Range: 10 + m_SpotAngle: 30 + m_InnerSpotAngle: 21.80208 + m_CookieSize: 10 + m_Shadows: + m_Type: 2 + m_Resolution: -1 + m_CustomResolution: -1 + m_Strength: 1 + m_Bias: 0.05 + m_NormalBias: 0.4 + m_NearPlane: 0.2 + m_CullingMatrixOverride: + e00: 1 + e01: 0 + e02: 0 + e03: 0 + e10: 0 + e11: 1 + e12: 0 + e13: 0 + e20: 0 + e21: 0 + e22: 1 + e23: 0 + e30: 0 + e31: 0 + e32: 0 + e33: 1 + m_UseCullingMatrixOverride: 0 + m_Cookie: {fileID: 0} + m_DrawHalo: 0 + m_Flare: {fileID: 0} + m_RenderMode: 0 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingLayerMask: 1 + m_Lightmapping: 4 + m_LightShadowCasterMode: 0 + m_AreaSize: {x: 1, y: 1} + m_BounceIntensity: 1 + m_ColorTemperature: 6570 + m_UseColorTemperature: 0 + m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0} + m_UseBoundingSphereOverride: 0 + m_UseViewFrustumForShadowCasterCull: 1 + m_ShadowRadius: 0 + m_ShadowAngle: 0 +--- !u!4 &1157865908 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1157865906} + m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261} + m_LocalPosition: {x: 0, y: 3, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0} +--- !u!1 &1196709330 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1196709331} + - component: {fileID: 1196709333} + - component: {fileID: 1196709332} + m_Layer: 5 + m_Name: Placeholder + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1196709331 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1196709330} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 2114196945} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: -0.5} + m_SizeDelta: {x: -20, y: -13} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1196709332 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1196709330} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 0.5} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 2 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 1 + m_HorizontalOverflow: 0 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: +--- !u!222 &1196709333 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1196709330} + m_CullTransparentMesh: 1 +--- !u!1 &1267610420 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1267610421} + - component: {fileID: 1267610423} + - component: {fileID: 1267610422} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1267610421 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1267610420} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 2114196945} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: -0.5} + m_SizeDelta: {x: -20, y: -13} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1267610422 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1267610420} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 5f7201a12d95ffc409449d95f23cf332, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_FontData: + m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0} + m_FontSize: 14 + m_FontStyle: 0 + m_BestFit: 0 + m_MinSize: 10 + m_MaxSize: 40 + m_Alignment: 0 + m_AlignByGeometry: 0 + m_RichText: 0 + m_HorizontalOverflow: 1 + m_VerticalOverflow: 0 + m_LineSpacing: 1 + m_Text: +--- !u!222 &1267610423 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1267610420} + m_CullTransparentMesh: 1 +--- !u!1 &1326854589 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1326854590} + - component: {fileID: 1326854593} + - component: {fileID: 1326854592} + - component: {fileID: 1326854591} + m_Layer: 5 + m_Name: ConsolePanel + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1326854590 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1326854589} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1085664059} + - {fileID: 2062259699} + m_Father: {fileID: 1951021497} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: -99.20001} + m_SizeDelta: {x: 0, y: 198.4} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &1326854591 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1326854589} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 31a19414c41e5ae4aae2af33fee712f6, type: 3} + m_Name: + m_EditorClassIdentifier: + m_ShowMaskGraphic: 1 +--- !u!114 &1326854592 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1326854589} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &1326854593 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1326854589} + m_CullTransparentMesh: 1 +--- !u!1 &1428817267 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1428817268} + - component: {fileID: 1428817270} + - component: {fileID: 1428817269} + m_Layer: 0 + m_Name: curs + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1428817268 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1428817267} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 1.5, z: 0} + m_LocalScale: {x: 0.1, y: 3, z: 0.1} + m_Children: [] + m_Father: {fileID: 1637446162} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!23 &1428817269 +MeshRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1428817267} + m_Enabled: 1 + m_CastShadows: 1 + m_ReceiveShadows: 1 + m_DynamicOccludee: 1 + m_MotionVectors: 1 + m_LightProbeUsage: 1 + m_ReflectionProbeUsage: 1 + m_RayTracingMode: 2 + m_RayTraceProcedural: 0 + m_RenderingLayerMask: 1 + m_RendererPriority: 0 + m_Materials: + - {fileID: 2100000, guid: 9e00eb9bc0cd7be478e657c251ce0680, type: 2} + m_StaticBatchInfo: + firstSubMesh: 0 + subMeshCount: 0 + m_StaticBatchRoot: {fileID: 0} + m_ProbeAnchor: {fileID: 0} + m_LightProbeVolumeOverride: {fileID: 0} + m_ScaleInLightmap: 1 + m_ReceiveGI: 1 + m_PreserveUVs: 0 + m_IgnoreNormalsForChartDetection: 0 + m_ImportantGI: 0 + m_StitchLightmapSeams: 1 + m_SelectedEditorRenderState: 3 + m_MinimumChartSize: 4 + m_AutoUVMaxDistance: 0.5 + m_AutoUVMaxAngle: 89 + m_LightmapParameters: {fileID: 0} + m_SortingLayerID: 0 + m_SortingLayer: 0 + m_SortingOrder: 0 + m_AdditionalVertexStreams: {fileID: 0} +--- !u!33 &1428817270 +MeshFilter: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1428817267} + m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0} +--- !u!1 &1458263241 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1458263242} + m_Layer: 0 + m_Name: Wand + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1458263242 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1458263241} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 548360277} + - {fileID: 1139364662} + m_Father: {fileID: 0} + m_RootOrder: 6 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1538301812 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1538301813} + m_Layer: 0 + m_Name: Scene + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1538301813 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1538301812} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 2076958800} + - {fileID: 1134778610} + - {fileID: 489915453} + m_Father: {fileID: 0} + m_RootOrder: 5 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1592748653 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1592748656} + - component: {fileID: 1592748655} + - component: {fileID: 1592748654} + m_Layer: 0 + m_Name: EventSystem + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &1592748654 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1592748653} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4f231c4fb786f3946a6b90b886c48677, type: 3} + m_Name: + m_EditorClassIdentifier: + m_HorizontalAxis: Horizontal + m_VerticalAxis: Vertical + m_SubmitButton: Submit + m_CancelButton: Cancel + m_InputActionsPerSecond: 10 + m_RepeatDelay: 0.5 + m_ForceModuleActive: 0 +--- !u!114 &1592748655 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1592748653} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 76c392e42b5098c458856cdf6ecaaaa1, type: 3} + m_Name: + m_EditorClassIdentifier: + m_FirstSelected: {fileID: 0} + m_sendNavigationEvents: 1 + m_DragThreshold: 10 +--- !u!4 &1592748656 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1592748653} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 4 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1637446161 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1637446162} + m_Layer: 0 + m_Name: debugWandCompanion + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &1637446162 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1637446161} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1428817268} + m_Father: {fileID: 0} + m_RootOrder: 7 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!1 &1883555802 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1883555808} + - component: {fileID: 1883555804} + - component: {fileID: 1883555803} + - component: {fileID: 1883555806} + - component: {fileID: 1883555807} + - component: {fileID: 1883555805} + m_Layer: 0 + m_Name: Main Camera + m_TagString: MainCamera + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!81 &1883555803 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1883555802} + m_Enabled: 1 +--- !u!20 &1883555804 +Camera: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1883555802} + m_Enabled: 1 + serializedVersion: 2 + m_ClearFlags: 1 + m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0} + m_projectionMatrixMode: 1 + m_GateFitMode: 2 + m_FOVAxisMode: 0 + m_SensorSize: {x: 36, y: 24} + m_LensShift: {x: 0, y: 0} + m_FocalLength: 50 + m_NormalizedViewPortRect: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + near clip plane: 0.3 + far clip plane: 1000 + field of view: 60 + orthographic: 0 + orthographic size: 5 + m_Depth: -1 + m_CullingMask: + serializedVersion: 2 + m_Bits: 4294967295 + m_RenderingPath: -1 + m_TargetTexture: {fileID: 0} + m_TargetDisplay: 0 + m_TargetEye: 3 + m_HDR: 1 + m_AllowMSAA: 1 + m_AllowDynamicResolution: 0 + m_ForceIntoRT: 0 + m_OcclusionCulling: 1 + m_StereoConvergence: 10 + m_StereoSeparation: 0.022 +--- !u!114 &1883555805 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1883555802} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &1883555806 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1883555802} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 8021982501d05f645a42b19f4a41ab8a, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!223 &1883555807 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1883555802} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 2 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 0 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &1883555808 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1883555802} + m_LocalRotation: {x: 0.35355338, y: 0.35355338, z: -0.1464466, w: 0.8535535} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 45, y: 45, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 8} + m_SizeDelta: {x: 100, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &1951021496 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1951021497} + m_Layer: 5 + m_Name: Cheat Console + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &1951021497 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 1951021496} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 2114196945} + - {fileID: 1326854590} + m_Father: {fileID: 648965865} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!1 &2000707108 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2000707112} + - component: {fileID: 2000707111} + - component: {fileID: 2000707110} + - component: {fileID: 2000707109} + - component: {fileID: 2000707113} + m_Layer: 5 + m_Name: Canvas + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &2000707109 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2000707108} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: dc42784cf147c0c48a680349fa168899, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreReversedGraphics: 1 + m_BlockingObjects: 0 + m_BlockingMask: + serializedVersion: 2 + m_Bits: 4294967295 +--- !u!114 &2000707110 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2000707108} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 0cd44c1031e13a943bb63640046fad76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_UiScaleMode: 0 + m_ReferencePixelsPerUnit: 100 + m_ScaleFactor: 1 + m_ReferenceResolution: {x: 800, y: 600} + m_ScreenMatchMode: 0 + m_MatchWidthOrHeight: 0 + m_PhysicalUnit: 3 + m_FallbackScreenDPI: 96 + m_DefaultSpriteDPI: 96 + m_DynamicPixelsPerUnit: 1 + m_PresetInfoIsWorld: 0 +--- !u!223 &2000707111 +Canvas: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2000707108} + m_Enabled: 1 + serializedVersion: 3 + m_RenderMode: 0 + m_Camera: {fileID: 0} + m_PlaneDistance: 100 + m_PixelPerfect: 1 + m_ReceivesEvents: 1 + m_OverrideSorting: 0 + m_OverridePixelPerfect: 0 + m_SortingBucketNormalizedSize: 0 + m_AdditionalShaderChannelsFlag: 0 + m_SortingLayerID: 0 + m_SortingOrder: 0 + m_TargetDisplay: 0 +--- !u!224 &2000707112 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2000707108} + m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 0, y: 0, z: 0} + m_Children: [] + m_Father: {fileID: 0} + m_RootOrder: 2 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0} + m_AnchorMax: {x: 0, y: 0} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 0} + m_Pivot: {x: 0, y: 0} +--- !u!114 &2000707113 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2000707108} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: db3b6e6445c9df84bb2e95b9365382be, type: 3} + m_Name: + m_EditorClassIdentifier: +--- !u!1 &2062259697 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2062259699} + - component: {fileID: 2062259701} + - component: {fileID: 2062259700} + - component: {fileID: 2062259698} + m_Layer: 5 + m_Name: Scrollbar Vertical + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!114 &2062259698 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2062259697} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2a4db7a114972834c8e4117be1d82ba3, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 89992087} + m_HandleRect: {fileID: 89992085} + m_Direction: 2 + m_Value: 0 + m_Size: 1 + m_NumberOfSteps: 0 + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] +--- !u!224 &2062259699 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2062259697} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 420274207} + m_Father: {fileID: 1326854590} + m_RootOrder: 1 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 1, y: 0} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: 0.000015258789} + m_SizeDelta: {x: 20, y: 0} + m_Pivot: {x: 1, y: 1} +--- !u!114 &2062259700 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2062259697} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10907, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2062259701 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2062259697} + m_CullTransparentMesh: 1 +--- !u!1001 &2076958799 +PrefabInstance: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Modification: + m_TransformParent: {fileID: 1538301813} + m_Modifications: + - target: {fileID: 2467515431058217605, guid: 188047b711783b74aa73c17ff9d66147, type: 3} + propertyPath: m_Name + value: Scene Startup + objectReference: {fileID: 0} + - target: {fileID: 2467515431058217606, guid: 188047b711783b74aa73c17ff9d66147, type: 3} + propertyPath: m_RootOrder + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2467515431058217606, guid: 188047b711783b74aa73c17ff9d66147, type: 3} + propertyPath: m_LocalPosition.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2467515431058217606, guid: 188047b711783b74aa73c17ff9d66147, type: 3} + propertyPath: m_LocalPosition.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2467515431058217606, guid: 188047b711783b74aa73c17ff9d66147, type: 3} + propertyPath: m_LocalPosition.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2467515431058217606, guid: 188047b711783b74aa73c17ff9d66147, type: 3} + propertyPath: m_LocalRotation.w + value: 1 + objectReference: {fileID: 0} + - target: {fileID: 2467515431058217606, guid: 188047b711783b74aa73c17ff9d66147, type: 3} + propertyPath: m_LocalRotation.x + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2467515431058217606, guid: 188047b711783b74aa73c17ff9d66147, type: 3} + propertyPath: m_LocalRotation.y + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2467515431058217606, guid: 188047b711783b74aa73c17ff9d66147, type: 3} + propertyPath: m_LocalRotation.z + value: -0 + objectReference: {fileID: 0} + - target: {fileID: 2467515431058217606, guid: 188047b711783b74aa73c17ff9d66147, type: 3} + propertyPath: m_LocalEulerAnglesHint.x + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2467515431058217606, guid: 188047b711783b74aa73c17ff9d66147, type: 3} + propertyPath: m_LocalEulerAnglesHint.y + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 2467515431058217606, guid: 188047b711783b74aa73c17ff9d66147, type: 3} + propertyPath: m_LocalEulerAnglesHint.z + value: 0 + objectReference: {fileID: 0} + - target: {fileID: 7238001765745231388, guid: 188047b711783b74aa73c17ff9d66147, type: 3} + propertyPath: TargetScene + value: + objectReference: {fileID: 0} + m_RemovedComponents: [] + m_SourcePrefab: {fileID: 100100000, guid: 188047b711783b74aa73c17ff9d66147, type: 3} +--- !u!4 &2076958800 stripped +Transform: + m_CorrespondingSourceObject: {fileID: 2467515431058217606, guid: 188047b711783b74aa73c17ff9d66147, type: 3} + m_PrefabInstance: {fileID: 2076958799} + m_PrefabAsset: {fileID: 0} +--- !u!1 &2114196944 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2114196945} + - component: {fileID: 2114196947} + - component: {fileID: 2114196946} + - component: {fileID: 2114196948} + m_Layer: 5 + m_Name: InputField + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &2114196945 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2114196944} + m_LocalRotation: {x: -0, y: -0, z: -0, w: 1} + m_LocalPosition: {x: 0, y: 0, z: 0} + m_LocalScale: {x: 1, y: 1, z: 1} + m_Children: + - {fileID: 1196709331} + - {fileID: 1267610421} + m_Father: {fileID: 1951021497} + m_RootOrder: 0 + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: -213.4} + m_SizeDelta: {x: 0, y: 30} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2114196946 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2114196944} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Material: {fileID: 0} + m_Color: {r: 1, g: 1, b: 1, a: 1} + m_RaycastTarget: 1 + m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0} + m_Maskable: 1 + m_OnCullStateChanged: + m_PersistentCalls: + m_Calls: [] + m_Sprite: {fileID: 10911, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + m_PreserveAspect: 0 + m_FillCenter: 1 + m_FillMethod: 4 + m_FillAmount: 1 + m_FillClockwise: 1 + m_FillOrigin: 0 + m_UseSpriteMesh: 0 + m_PixelsPerUnitMultiplier: 1 +--- !u!222 &2114196947 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2114196944} + m_CullTransparentMesh: 1 +--- !u!114 &2114196948 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2114196944} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: d199490a83bb2b844b9695cbf13b01ef, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 1 + m_TargetGraphic: {fileID: 2114196946} + m_TextComponent: {fileID: 1267610422} + m_Placeholder: {fileID: 1196709332} + m_ContentType: 0 + m_InputType: 0 + m_AsteriskChar: 42 + m_KeyboardType: 0 + m_LineType: 0 + m_HideMobileInput: 0 + m_CharacterValidation: 0 + m_CharacterLimit: 0 + m_OnEndEdit: + m_PersistentCalls: + m_Calls: [] + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] + m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_CustomCaretColor: 0 + m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412} + m_Text: + m_CaretBlinkRate: 0.85 + m_CaretWidth: 1 + m_ReadOnly: 0 + m_ShouldActivateOnSelect: 1 diff --git a/Assets/Scenes/Tests/BuildModeTest.unity.meta b/Assets/Scenes/Tests/BuildModeTest.unity.meta new file mode 100644 index 00000000..83aed113 --- /dev/null +++ b/Assets/Scenes/Tests/BuildModeTest.unity.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 74352a833a6dc254ab203706be9743b9 +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Cameras.meta b/Assets/Scripts/OpenTS2/Cameras.meta new file mode 100644 index 00000000..c8ef2231 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Cameras.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 47b4f5547f913ba4882d83a6ffa154e4 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Cameras/TestLotViewCamera.cs b/Assets/Scripts/OpenTS2/Cameras/TestLotViewCamera.cs new file mode 100644 index 00000000..78f04bc5 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Cameras/TestLotViewCamera.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; + +public class TestLotViewCamera : MonoBehaviour +{ + private const int HitscanLength = 2000; + + private Camera _camera; + private Transform _camTransform; + private Vector3 _mouseHoldPosition; + private float _initialXCamRot; + + //level detection system + /// + /// A set of mesh colliders that the camera will shoot rays at to detect where the mouse cursor is in 3D world space + /// and also where the floor is to adjust its Y position to see the floor appropriately + /// See: + /// + Dictionary _floorElevationMeshes; + + // Start is called before the first frame update + private void Start() + { + _floorElevationMeshes = new Dictionary(); + _camTransform = GetComponentInParent(); + _camera = GetComponentInParent(); + } + + // Update is called once per frame + private void Update() + { + if (Input.GetMouseButtonDown(1) || Input.GetMouseButtonDown(2)) // right or middle click + { + _mouseHoldPosition = Input.mousePosition; + if (Input.GetMouseButtonDown(2)) + _initialXCamRot = _camTransform.rotation.eulerAngles.x; + } + + var mouseChange = _mouseHoldPosition - Input.mousePosition; + + if (Input.GetMouseButton(1)) // Right Click camera translation + { + var change = mouseChange / 10; + _camTransform.TransformDirection(change); + change *= Time.deltaTime; + + _camTransform.position = + new Vector3(_camTransform.position.x - change.x, + _camTransform.position.y, + _camTransform.position.z - change.y); + } + else if (Input.GetMouseButton(2)) // Middle Mouse Click Camera pitch + { + float pitchChange = (mouseChange / 1).y; + var transformedXRot = Math.Max(0, Math.Min(90, _initialXCamRot - pitchChange)); + var currentAngle = _camTransform.rotation.eulerAngles; + currentAngle = new Vector3(transformedXRot, currentAngle.y, currentAngle.z); + _camTransform.rotation = Quaternion.Euler(currentAngle); + } + } + + /// + /// Sets the mesh colliders used to detect the height of the floor the camera is looking at. + /// + /// + /// + public void SetCameraDetectionMeshes(int floor, IEnumerable colliders) + { + _floorElevationMeshes.Remove(floor); + _floorElevationMeshes.Add(floor, colliders.Where(x => x != default).ToArray()); + } + + public static Ray GetMouseCursor3DRay(Camera selectedCamera) + { + //transform scr pos to world + Vector3 mousePosition = Input.mousePosition; + mousePosition.z = 20; + mousePosition = Camera.main.ScreenToWorldPoint(mousePosition); + + //get directional vector + var direction = (mousePosition - selectedCamera.transform.position); direction.Normalize(); + + //build a ray from camera to terrain + return new Ray(selectedCamera.transform.position, direction); + } + + /// + /// Potentially expensive!! use with caution + /// + /// + /// + /// + public bool TranslateScreen2WorldPos(int maxFloor, out Vector3 worldPosition, out int floor) + { + worldPosition = new Vector3(); + floor = -1; + + if (!_floorElevationMeshes.Any()) + { + Debug.LogWarning("No terrain meshes added to the lot camera so hit detection is disabled!"); + return false; + } + + Ray camRay = GetMouseCursor3DRay(_camera); + + //Check each floor by casting the above ray at each collider. + //TODO: (NEEDS REFACTOR TO USE ELEVATION IN LOT DATA) + //also just in general this iteration is really not great should only be used on frames where theres actual movement + RaycastHit hitInfo = default; + bool hitComplete = false; + + //go top down searching for which floor we're looking at + for(int candidateFloor = maxFloor; candidateFloor >= _floorElevationMeshes.Keys.Min(); candidateFloor--) + { + if (!_floorElevationMeshes.ContainsKey(candidateFloor)) continue; + foreach(var collider in _floorElevationMeshes[candidateFloor]) + { + if (collider is MeshCollider mCollider) + { + var meshRef = collider.gameObject.GetComponent().sharedMesh; + mCollider.sharedMesh = meshRef; + } + if (collider.Raycast(camRay, out hitInfo, HitscanLength)) + { + hitComplete = true; + floor = candidateFloor; + break; + } + } + if (hitComplete) break; + } + if (!hitComplete) return false; + + worldPosition = hitInfo.point; + return true; + } +} diff --git a/Assets/Scripts/OpenTS2/Cameras/TestLotViewCamera.cs.meta b/Assets/Scripts/OpenTS2/Cameras/TestLotViewCamera.cs.meta new file mode 100644 index 00000000..2d9470ac --- /dev/null +++ b/Assets/Scripts/OpenTS2/Cameras/TestLotViewCamera.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8021982501d05f645a42b19f4a41ab8a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Components/AssetReferenceComponent.cs b/Assets/Scripts/OpenTS2/Components/AssetReferenceComponent.cs index 2427d0eb..acdfca05 100644 --- a/Assets/Scripts/OpenTS2/Components/AssetReferenceComponent.cs +++ b/Assets/Scripts/OpenTS2/Components/AssetReferenceComponent.cs @@ -16,5 +16,10 @@ public void AddReference(params AbstractAsset[] assets) { References.AddRange(assets); } + + /// + /// Indicates that this component should re-evaluate itself for changes made at runtime + /// + public virtual void Invalidate() { } } } diff --git a/Assets/Scripts/OpenTS2/Content/DBPF/WallGraphAsset.cs b/Assets/Scripts/OpenTS2/Content/DBPF/WallGraphAsset.cs index f3720d79..7372e10b 100644 --- a/Assets/Scripts/OpenTS2/Content/DBPF/WallGraphAsset.cs +++ b/Assets/Scripts/OpenTS2/Content/DBPF/WallGraphAsset.cs @@ -1,4 +1,7 @@ +using System; using System.Collections.Generic; +using System.Linq; +using UnityEngine; namespace OpenTS2.Content.DBPF { @@ -31,11 +34,12 @@ public class WallGraphAsset : AbstractAsset { public int Width { get; } public int Height { get; } - public int Floors { get; } + public int Floors { get; private set; } public int BaseFloor { get; } public Dictionary Positions { get; } public int[] Rooms { get; } - public WallGraphLineEntry[] Lines { get; } + public WallGraphLineEntry[] Lines { get => _lines; set => _lines = value; } + private WallGraphLineEntry[] _lines; public WallGraphAsset(int width, int height, int floors, int baseFloor, WallGraphPositionEntry[] pos, int[] rooms, WallGraphLineEntry[] lines) { @@ -59,6 +63,129 @@ private Dictionary ToPositionDictionary(WallGraphPo return result; } - } -} \ No newline at end of file + bool GetJuncIdByPosition(Vector2 position, int floor, out int juncId, bool createIfNull = true) + { + bool existed = false; + var junctions = Positions.Where(x => + x.Value.XPos == position.x && x.Value.YPos == position.y && x.Value.Level == floor); + juncId = -1; + if (junctions.Any()) + { + juncId = junctions.First().Key; // found a matching position + existed = true; + } + else if (createIfNull) + { + // make a new position + if (Positions.Any()) + juncId = Positions.Select(x => x.Key).Max() + 1; + else juncId = 0; + Positions.Add(juncId, new WallGraphPositionEntry() + { + Id = juncId, + Level = floor, + XPos = position.x, + YPos = position.y, + }); + } + + return existed; + } + + public bool PushWall(Vector2 pos1, Vector2 pos2, int floor, out int layerID) + { + layerID = -1; + + bool fromExistedAlready = GetJuncIdByPosition(pos1, floor, out int fromId); + if (fromId == -1) // unable to create !! + return false; + bool toExistedAlready = GetJuncIdByPosition(pos2, floor, out int toId); + if (toId == -1) // unable to create !! + return false; + + if (fromExistedAlready && toExistedAlready) + { // wall may already exist + bool exists = _lines.Any(x => x.ToId == toId && x.FromId == fromId); // check if this wall segment exists + if (exists) + return false; // this wall segment already exists. + exists = _lines.Any(x => x.ToId == fromId && x.FromId == toId); + if (exists) + return false; // this wall segment already exists. + } + + int layerId = 99; + if(_lines.Any()) layerId = _lines.Select(x => x.LayerId).Max() + 1; + + Array.Resize(ref _lines, Lines.Length + 1); + WallGraphLineEntry line = new WallGraphLineEntry() + { + FromId = fromId, + LayerId = layerId, + ToId = toId, + }; + _lines[_lines.Length - 1] = line; + layerID = layerId; + if (Floors <= floor) + Floors++; + return true; + } + + private void _delWall(int index) + { + int i = index; + //found the wall + if (i != _lines.Length - 1) // last wall + _lines[i] = _lines[_lines.Length - 1]; // move last line to this spot + Array.Resize(ref _lines, _lines.Length - 1); // shorten array by one + } + + internal bool RemoveWall(Vector2 from, Vector2 to, int floor, out int layerID) + { // should be refactored to batch delete a set of segments instead of one at a time due to iterations being potentially large + layerID = -1; + + GetJuncIdByPosition(from, floor, out int fromId, false); + if (fromId == -1) // wall index doesn't exist? + return false; + GetJuncIdByPosition(to, floor, out int toId, false); + if (toId == -1) // wall index doesn't exist? + return false; + + for (int i = _lines.Length - 1; i >= 0; i--) // reverse order -- since players may more often delete walls they just made + { + var line = _lines[i]; + + if (toId == fromId) throw new Exception("From and To IDs are the same!"); + if (line.ToId != toId && line.ToId != fromId) continue; + if (line.FromId != toId && line.FromId != fromId) continue; + + _delWall(i); + layerID = line.LayerId; + return true; + } + return false; // not found + } + /// + /// Deletes all the wall lines from the selected floor by their LayerID. + /// + /// + /// + internal int RemoveWalls(params int[] wallLayerIDs) + { + int index = -1; + int found = 0; + var deletionIndices = new HashSet(); + foreach(var line in _lines) + { + index++; + if (!wallLayerIDs.Contains(line.LayerId)) continue; + found++; + deletionIndices.Add(index); + } + int deleted = 0; + foreach (int i in deletionIndices) + _delWall(i - deleted++); + return deleted; + } + } +} diff --git a/Assets/Scripts/OpenTS2/Engine/Modes.meta b/Assets/Scripts/OpenTS2/Engine/Modes.meta new file mode 100644 index 00000000..819c8022 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1df3d8a68fd8f874eb366660526b7c53 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build.meta b/Assets/Scripts/OpenTS2/Engine/Modes/Build.meta new file mode 100644 index 00000000..378aafd1 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 28f9123fa5bd6d04ea188898ff9c5c80 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/BuildModeServer.cs b/Assets/Scripts/OpenTS2/Engine/Modes/Build/BuildModeServer.cs new file mode 100644 index 00000000..d5c582ad --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/BuildModeServer.cs @@ -0,0 +1,665 @@ +using OpenTS2.Content.DBPF; +using OpenTS2.Engine.Modes.Build.Tools; +using OpenTS2.Scenes.Lot; +using UnityEngine; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using OpenTS2.Scenes.Lot.Extensions; +using OpenTS2.Scenes.Lot.State; +using log4net.Core; +using UnityEngine.UIElements; +using OpenTS2.Files.Formats.DBPF; +using OpenTS2.Diagnostic; + +namespace OpenTS2.Engine.Modes.Build +{ + + /// + /// Controls the flow of changes to the lot architecture and serves the functionality for Build Mode + /// + internal class BuildModeServer : MonoBehaviour + { + /// + /// Modes that walls can be created + /// + public enum WallCreationModes + { + /// + /// A single normal wall + /// + Single, + /// + /// A square area of walls + /// + Room, + /// + /// A diagonally oriented square area of walls + /// + DiagnonalRoom + } + /// + /// Modes that describe the modification being made to the terrain + /// + public enum TerrainModificationModes + { + /// + /// No brush selected + /// + None, + /// + /// Raise the terrain brush + /// + Raise, + /// + /// Lower the terrain brush + /// + Lower, + /// + /// Smooth an area of terrain brush + /// + Smooth, + /// + /// Pond brush + /// + Water, + /// + /// Terrain paints brush + /// + Paint + } + + //STATIC + private static BuildModeServer Current { get; set; } + public static BuildModeServer Get() => Current; + + private static Dictionary _tools; + private ushort dryWallDesignPattern; + /// + /// The floor currently selected using the UI + /// This also is the highest level shown in the lot geometry + /// + public int CurrentFloor => loadedLot.Floor; + + //PRIVATE + private readonly StringBuilder lotHistory = new StringBuilder(); + + private LotLoad loadedLot; + private LotArchitecture architecture => loadedLot.Architecture; + [GameProperty("constrainfloorelevation", userProp:true)] + private static bool ConstrainedFloorElevation = false; + + //PUBLIC + /// + /// The current + /// See: + /// + public WorldState WorldState + { + get => loadedLot.WorldState; set => loadedLot.WorldState = value; + } + /// + /// After making changes to the , use this to apply the changes + /// + public void InvalidateLotState() => loadedLot.InvalidateState(); + /// + /// The tool that the user is currently using (or the one the user more recently selected) + /// + public BuildTools SelectedTool { get; private set;} = BuildTools.None; + /// + /// The handler for the tool the user is currently using + /// + public AbstractBuildTool CurrentTool { get; private set; } = null; + public int BaseFloor => loadedLot.BaseFloor; + public int MaxFloor => loadedLot.MaxFloor; + + private void LogHistory(string Message, string Caption = default, string Sender = default) + { + if (string.IsNullOrWhiteSpace(Sender)) Sender = typeof(BuildModeServer).Name; + var msgTxt = $"OpenTS2::{Sender} -> {(!string.IsNullOrWhiteSpace(Caption) ? $"({Caption})" : "")} {Message}"; + lotHistory.AppendLine(msgTxt); + Debug.Log(msgTxt); + } + + public void Initialize(LotLoad LoadedLot) + { + Current = this; + + loadedLot = LoadedLot; + + SelectedTool = BuildTools.None; + CurrentTool = null; + + MakeTools(); + Init(); + } + + void MakeTools() + { + _tools = new Dictionary + { // funfact: these are added in the order they were implemented .. incase you were wondering + { BuildTools.Wall, new WallTool(this) }, + { BuildTools.Hand, new HandTool(loadedLot, architecture) }, + { BuildTools.TerrainBrush, new TerrainBrushTool(this) }, + { BuildTools.Floor, new FloorTool(this) }, + { BuildTools.Foundation, new FoundationTool(this) } + }; + } + + void Init() + { + //set drywall design pattern (used so frequently we can save a little time frontloading it) + architecture.EnsurePatternReferenced("blank", LotArchitecture.ArchitectureGameObjectTypes.wall, out dryWallDesignPattern, out _); + } + /// + /// Changes the currently used tool by the User. + /// Passing will drop the current tool, if held. + /// + /// + public void ChangeTool(BuildTools Tool) + { + if (CurrentTool != null) + { // A tool is currently being used. + if (Tool == SelectedTool) + return; // the newly selected tool is the same as the one currently held, exit. + CurrentTool.OnToolCancel(Tool != BuildTools.None ? "The user selected a different tool." : "The current tool has been dropped."); + CurrentTool.SetActive(false); + } + switch(Tool) + { + default: + case BuildTools.None: + CurrentTool = null; break; + case BuildTools.Foundation: + case BuildTools.Hand: + case BuildTools.Wall: + case BuildTools.TerrainBrush: + case BuildTools.Floor: + CurrentTool = _tools[Tool]; + CurrentTool.SetActive(true); + LogHistory($"Selected tool: {Tool} (trans from: {SelectedTool})", "Change Tool"); + break; + } + SelectedTool = Tool; + } + + /// + /// Lists all the points for wall segments in the area of walls provided + /// + /// + /// + /// + /// + public static List<(Vector2Int A, Vector2Int B)> GetWallPoints(Vector2Int From, Vector2Int To, WallCreationModes CreationOption = WallCreationModes.Single) + { + List<(Vector2Int A, Vector2Int B)> walls = new List<(Vector2Int, Vector2Int)>(); + switch (CreationOption) + { + case WallCreationModes.Single: + walls.Add((From, To)); + break; + case WallCreationModes.Room: + walls.Add((From, new Vector2Int(To.x, From.y))); + walls.Add((new Vector2Int(To.x, From.y), To)); + walls.Add((To, new Vector2Int(From.x, To.y))); + walls.Add((new Vector2Int(From.x, To.y), From)); + break; + case WallCreationModes.DiagnonalRoom: break; + } + return walls; + } + + public float PollElevation(Vector2Int Position, int Floor) => architecture.PollElevation(Position, Floor); + + /// + /// Creates walls with the blank wallpaper set with optional creation options like Square area of walls or Single + /// + /// + /// + /// + /// + /// + public int[]? CreateWalls(Vector2Int From, Vector2Int To, int Floor, + WallType Type = WallType.Normal, + WallCreationModes CreationOption = WallCreationModes.Single, + string FrontWallpaperPattern = "blank", string BackWallpaperPattern = "blank") + { + if (From == To) return null; + if (CreationOption != WallCreationModes.Single && !RegionValid(From, To)) return null; + string argsStr = $"From: {From} To: {To} Floor: {Floor} Type: {Type} Mode: {CreationOption}" + + $"Front: {FrontWallpaperPattern} Back: {BackWallpaperPattern}"; + + //OrderPoints(ref From, ref To); + var walls = GetWallPoints(From, To, CreationOption); + + //WALLPAPERS + ushort front = dryWallDesignPattern; + ushort back = front; + if (FrontWallpaperPattern != "blank") + architecture.EnsurePatternReferenced(FrontWallpaperPattern, LotArchitecture.ArchitectureGameObjectTypes.wall, out front, out _); + if (BackWallpaperPattern != "blank") + architecture.EnsurePatternReferenced(BackWallpaperPattern, LotArchitecture.ArchitectureGameObjectTypes.wall, out back, out _); + + int[] createdWallIDs = null; + foreach (var wall in walls) + createdWallIDs = architecture.CreateWall(wall.A, wall.B, Floor, Type, front, back); + + if (createdWallIDs != null) // walls were placed here + { + loadedLot.InvalidateWalls(); + LogHistory($"Created wall(s). {argsStr}", "Create Walls"); + return createdWallIDs; + } + else LogHistory($"Could not create wall(s). {argsStr}", "Create Walls"); + return null; + } + /// + /// Causes the room detection system to re-evaluate the lot. + /// + public void SignalRoomsInvalidate() => loadedLot.EvaluateRoomMap(); + /// + /// Clears out all walls in the specified region with the given wall creation options + /// + /// + /// + /// + /// + /// + public void DeleteWalls(Vector2Int From, Vector2Int To, int Floor, WallCreationModes CreationOption = WallCreationModes.Single) + { + var walls = GetWallPoints(From, To, CreationOption); + string argsStr = $"From: {From} To: {To} Floor: {Floor} Mode: {CreationOption}"; + + bool? returnValue = true; + foreach (var wall in walls) + returnValue = architecture.DeleteWall(wall.A, wall.B, Floor); + + if (returnValue.HasValue) // walls were placed here + { + loadedLot.InvalidateWalls(); + LogHistory($"Deleted wall(s). {argsStr}", "Delete Walls"); + } + else LogHistory($"Could not delete wall(s). {argsStr}", "Delete Walls"); + } + public void DeleteAllWalls(params int[] WallLayerIDs) + { + if (WallLayerIDs.Length == 0) return; + architecture.DeleteAllWalls(WallLayerIDs); + loadedLot.InvalidateWalls(); + } + + /// + /// Puts the two points in ascending order + /// + void OrderPoints(ref Vector2Int A, ref Vector2Int B) + { + var a = new Vector2Int(Math.Min(A.x, B.x), Math.Min(A.y, B.y)); + var b = new Vector2Int(Math.Max(A.x, B.x), Math.Max(A.y, B.y)); + A = a; + B = b; + } + + /// + /// Checks if the area between two points is nonzero + /// + /// + /// + /// + bool RegionValid(Vector2Int From, Vector2Int To) => Math.Abs(From.x - To.x) * Math.Abs(From.y - To.y) != 0; + + /// + /// Checks to see if there is a floor at the given location. + /// If is provided as , it will check floors on ALL levels. + /// + /// + /// + /// + public bool IsFloorAt(Vector2Int Position, int? Level = default) + { + bool inquire(int level, int index) + { + var value = architecture.FloorPatterns.Data[level][index]; + return value.w != 0 || value.x != 0 || value.y != 0 || value.z != 0; + } + + int level = 0; + bool allLevels = Level == default; + if (!allLevels) + level = -architecture.BaseFloor + Level.Value; + + var mPos = Position; + + bool inquiryList(int level, int index) + { + if (inquire(level, index)) return true; + if (index == 0) return false; + if (inquire(level, index - 1)) return true; + index = ((mPos.x - 1) * architecture.FloorPatterns.Height) + mPos.y; + if (inquire(level, index)) return true; + return false; + } + + int index = (mPos.x * architecture.FloorPatterns.Height) + mPos.y; + + if (!allLevels) + return inquiryList(level, index); + + for (level = architecture.BaseFloor; level < architecture.FloorPatterns.Data.Length; level++) + if (inquiryList(level, index)) return true; + return false; + } + + /// + /// Ensures the provided has layers up to the provided floor level + /// + /// Function used to fill the array's new level(s) if applicable. + /// arg1 is the index in the array being filled + /// arg2 is the value at arg1 on the floor beneath this one. If this is the lowest floor, + /// it will reference itself. + void Ensure3DViewDepth2Floor(_3DArrayView ArrayView, int Floor, Func FillFunc) where T : unmanaged + { + //ensure a layer for elevation exists + if (ArrayView.Depth <= Floor) + { + int fromFloor = ArrayView.Depth; + int toFloor = Floor; + //make new level(s) + ArrayView.Resize(Floor + 1); + for(int f = fromFloor; f <= toFloor; f++) + { // iterate over new level(s) + int underFloor = f - 1; + if (underFloor < BaseFloor) underFloor = BaseFloor; + for (int i = 0; i < ArrayView.Width * ArrayView.Height; i++) + { + T underValue = ArrayView.Data[underFloor][i]; + ArrayView.Data[f][i] = FillFunc(i, underValue); // invoke fillfunc to fill the array + } + } + ArrayView.Commit(); + } + } + /// + /// Ensures the provided has layers up to the provided floor level + /// + /// Value used to fill the newly created array levels + void Ensure3DViewDepth2Floor(_3DArrayView ArrayView, int Floor, T DefaultFillVal) where T : unmanaged + { + //ensure a layer for elevation exists + if (ArrayView.Depth <= Floor) + { + int fromFloor = ArrayView.Depth; + int toFloor = Floor; + //make new level(s) + ArrayView.Resize(Floor + 1); + for (int f = fromFloor; f <= toFloor; f++) + { // iterate over new level(s) + for (int i = 0; i < ArrayView.Width * ArrayView.Height; i++) + ArrayView.Data[f][i] = DefaultFillVal; // invoke fillfunc to fill the array + } + ArrayView.Commit(); + } + } + + public void DeleteFloors(Vector2Int From, Vector2Int To, int Level = 0) => + CreateFloors(From, To, 0, Level, false); + /// + /// Creates an area of flooring with the specified . + /// This function handles adding the pattern reference to the + /// + /// + /// + /// + /// + /// + public void CreateFloors(Vector2Int From, Vector2Int To, string PatternName, int Level = 0, bool ModifyTerrain = true) + { + if (!architecture.EnsurePatternReferenced(PatternName, LotArchitecture.ArchitectureGameObjectTypes.floor, out ushort patternID, out _)) + { + LogHistory($"Could not create flooring due to the pattern: {PatternName} not existing or isn't valid."); + return; + } + CreateFloors(From, To, patternID, Level, ModifyTerrain); + } + public void CreateFloors(Vector2Int From, Vector2Int To, ushort PatternID, int Level = 0, bool ModifyTerrain = true) => + CreateFloors(From, To, new Files.Formats.DBPF.Vector4(PatternID, PatternID, PatternID, PatternID), Level, ModifyTerrain); + public void CreateFloors(Vector2Int From, Vector2Int To, + Vector4 QuarterTilePatternIDs, int Level = 0, bool ModifyTerrain = true) + { + if (!RegionValid(From, To)) return; // check area nonzero + + var from = From; var to = To; // order the points in order of closest to origin of the lot first + OrderPoints(ref from, ref to); + + if (ModifyTerrain) // flatten the area of terrain if applicable + { + Vector2Int pollPosition = From; // NE corner of the origin tile always + if(From.y > To.y) // selection is towards NORTH + pollPosition.y -= 1; + else pollPosition.y += 1; + if (From.x > To.x) // selection is towards WEST + ; + else pollPosition.x += 1; + + float elevation = architecture.PollElevation(pollPosition, Level); + LevelRegion(from, to, elevation, Level); // level terrain under floors + } + Level = -architecture.BaseFloor + Level; + _3DArrayView> floorPatterns = architecture.FloorPatterns; + + //ensure a layer for elevation exists + Ensure3DViewDepth2Floor(floorPatterns, Level, new Vector4()); // fill with empty + + ref Vector4[] baseFloorData = ref floorPatterns.Data[Level]; + + int changedFloors = 0; + for (int tx = from.x; tx < to.x; tx++) + { + for (int ty = from.y; ty < to.y; ty++) + { + var mPos = new Vector2Int(tx, ty); + int index = (mPos.x * floorPatterns.Height) + mPos.y; + + if (index > baseFloorData.Length - 1) + index = baseFloorData.Length - 1; + if (index < 0) + index = 0; + + baseFloorData[index] = QuarterTilePatternIDs; + changedFloors++; + } + } + + if (changedFloors <= 0) return; + + LogHistory($"Modified flooring. From: {From} To: {To} PatIDs: {QuarterTilePatternIDs}" + + $" Floor: {Level} TerrainMod?: {ModifyTerrain}", "Modify Flooring"); + loadedLot.InvalidateFloors(); // invalidating floors also may incur a terrain re-evaluation if necessary + } + + /// + /// Levels a region of terrain/elevation map. + /// + /// The point to start at + /// The point to go to. + /// What elevation value to level to. + /// Which level of the Elevation Map to perform the modification. + /// If true, will ensure each level beneath does not exceed this elevation. + /// Only greater elevations will be modified, lower ones will be ignored. + /// In the event the terrain on a lower floor exceeds the elevation here on this floor, by how much to + /// decrease to yield the elevation of the terrain on the lower floor? + public void LevelRegion(Vector2Int From, Vector2Int To, float Elevation = 0, int Floor = 0, bool LevelFloorsBeneathMe = true, float LevelBeneathMeDelta = -0f) + { + if (!RegionValid(From, To)) return; + Floor = -architecture.BaseFloor + Floor; + + var from = From; var to = To; + OrderPoints(ref from, ref to); + + //check the cheat system for constrainfloorelevation cheat + bool cfeProp = ConstrainedFloorElevation; + + _3DArrayView arr = architecture.Elevation; + + //ensure a layer for elevation exists + //fill function will set the entire level to appropriate wall height. + Ensure3DViewDepth2Floor(arr, Floor, (int i, float beneathElevation) => beneathElevation + LotWallComponent.WallHeight); + + ref float[] dataSet = ref arr.Data[Floor]; + bool levelModify = false; + for (int tx = from.x; tx <= to.x; tx++) + { + for(int ty = from.y; ty <= to.y; ty++) + { + var mPos = new Vector2Int(tx, ty); + int index = (mPos.x * arr.Height) + mPos.y; + + if (index > dataSet.Length - 1) + index = dataSet.Length - 1; + if (index < 0) + index = 0; + + if (cfeProp && IsFloorAt(mPos)) + continue; + + dataSet[index] = Elevation; + levelModify = true; + + //level beneath me + if (LevelFloorsBeneathMe) + { + try + { + for (int elevationLevel = Floor; elevationLevel > 0; elevationLevel--) + { + ref float[] meValDataSet = ref arr.Data[elevationLevel - 1]; + meValDataSet[index] = Math.Min(Elevation + LevelBeneathMeDelta, meValDataSet[index]); + } + } + catch (Exception e) + { + Debug.LogError($"LevelRegion() :: LevelBeneathMe error: {e.Message}"); + } + } + } + } + + if (!levelModify) return; + + LogHistory($"Leveled terrain. From: {From} To: {To} Elevation: {Elevation}" + + $" Floor: {Floor}", "Level Terrain"); + //all changes complete, invalidate mesh + loadedLot.InvalidateFloors(); + } + /// + /// Sets the elevation at the position in lot grid coordinates at the Floor provided by adding it to the elevation of the lower level. + /// + /// + /// + /// + public void SetElevationRelative(float RelativeElevation, int Floor, params Vector2Int[] Positions) + { + Floor = -architecture.BaseFloor + Floor; + + _3DArrayView arr = architecture.Elevation; + //ensure a layer for elevation exists + //fill function will set the entire level to appropriate wall height. + Ensure3DViewDepth2Floor(arr, Floor, (int i, float beneathElevation) => beneathElevation + RelativeElevation); + + ref float[] dataSet = ref arr.Data[Floor]; + foreach (var p in Positions) + { + var mPos = p; + int index = (mPos.x * arr.Height) + mPos.y; + + float lowerElevation = dataSet[index]; + if (Floor > 0) + lowerElevation = arr.Data[Floor - 1][index]; + + dataSet[index] = lowerElevation + RelativeElevation; + } + } + + /// + /// Submits a terrain modification with the supplied parameters + /// + /// The center point of the terrain modification, in lot grid coordinates + /// The radius of the terrain brush being used, in grid cells + /// By how much should the terrain be altered in this call + /// Which modification is requested + /// + public bool ModifyTerrain(Vector2Int Position, int Radius, float Strength, TerrainModificationModes Mode, int Floor = 0) + { + if (Mode == TerrainModificationModes.None) return false; + Floor = -architecture.BaseFloor + Floor; + ref float[] dataSet = ref architecture.Elevation.Data[Floor]; + + var mPos = Position; + int index = (mPos.x * architecture.Elevation.Height) + mPos.y; + + //The Sims 2 won't allow any terrain modifications if the cursor is directly over unexposed terrain + if (ConstrainedFloorElevation && IsFloorAt(mPos, Floor)) + return false; + + bool levelModify = false; + + for (int dx = -Radius; dx <= Radius; dx++) + { + for (int dy = -Radius; dy <= Radius; dy++) + { + //get position to edit + mPos = Position + new Vector2Int(dx, dy); + index = (mPos.x * architecture.Elevation.Height) + mPos.y; + + //measure length from center to this point + int length = (int)Math.Round(Vector2.Distance(mPos, Position),0); + if (length > Radius) continue; + + float _stren = Strength; + + //CFE cheat + if (ConstrainedFloorElevation && IsFloorAt(mPos)) + continue; + + //terrain modification + switch (Mode) + { + default: return false; // ?? + case TerrainModificationModes.Water: + case TerrainModificationModes.Lower: + _stren *= -1; + goto case TerrainModificationModes.Raise; + case TerrainModificationModes.Raise: + dataSet[index] += _stren; + levelModify = true; + break; + } + } + } + + if (!levelModify) return false; + + LogHistory($"{Mode}ed terrain. Center: {Position} Radius: {Radius} Strength: {Strength} Level: {Floor}", "Level Terrain"); + //all changes complete, invalidate mesh + loadedLot.InvalidateFloors(); + return true; + } + + /// + /// Resets the lot to having nothing on it and flattened terrain + /// Note: It is highly recommended to alert the user before performing an action like this. + /// + /// + public void FlattenLot(float Elevation = 0) + { + int Floor = 0; + Floor = -architecture.BaseFloor + Floor; + ref float[] dataSet = ref architecture.Elevation.Data[-architecture.BaseFloor]; + for(int i = 0; i < dataSet.Length; i++) + dataSet[i] = Elevation; + //all changes complete, invalidate mesh + loadedLot.InvalidateFloors(); + } + } +} diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/BuildModeServer.cs.meta b/Assets/Scripts/OpenTS2/Engine/Modes/Build/BuildModeServer.cs.meta new file mode 100644 index 00000000..e776a78d --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/BuildModeServer.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1e91cfd046356bd4496e1fe219b072b9 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/BuildTools.cs b/Assets/Scripts/OpenTS2/Engine/Modes/Build/BuildTools.cs new file mode 100644 index 00000000..626c10ae --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/BuildTools.cs @@ -0,0 +1,12 @@ +namespace OpenTS2.Engine.Modes.Build +{ + internal enum BuildTools + { + None, + Hand, + Wall, + TerrainBrush, + Foundation, + Floor, + } +} diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/BuildTools.cs.meta b/Assets/Scripts/OpenTS2/Engine/Modes/Build/BuildTools.cs.meta new file mode 100644 index 00000000..979e247e --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/BuildTools.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: be91059a50da924489cf226bdd5d6d45 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools.meta b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools.meta new file mode 100644 index 00000000..502e3bdb --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4b7ab705d82e6f8419b9d1db9e8ff271 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/AbstractBuildTool.cs b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/AbstractBuildTool.cs new file mode 100644 index 00000000..878da93a --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/AbstractBuildTool.cs @@ -0,0 +1,296 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace OpenTS2.Engine.Modes.Build.Tools +{ + /// + /// Context passed a parameter for functions on a instance + /// + internal class BuildToolContext + { + /// + /// The position of the 3D mouse cursor in world space + /// + public Vector3 Cursor3DWorldPosition { get; set; } + /// + /// The position of the wand snapped to grid coordinates + /// + public Vector3 WandPosition { get; set; } + /// + /// The position of the wand in the 2D lot grid + /// + public Vector2Int GridPosition { get; set; } + /// + /// The quadrant the 3D mouse cursor is in + /// Chart Quadrants: 1 = top left, 2 = top right, 3 = bottom right, 4 = bottom left + /// + public int GridTileCursorQuadrant { get; set; } + public int CursorFloor { get; internal set; } + } + + /// + /// A build tool with basic Pick-Up and Put-Down functionality + /// + internal abstract class AbstractBuildTool + { + public delegate void OnFinalizeToolHandler(AbstractBuildTool sender); + /// + /// Called when the user releases the tool and the changes will be submitted. + /// + public event OnFinalizeToolHandler OnFinalizeTool; + + /// + /// The name of this tool + /// + public abstract string ToolName { get; } + /// + /// The current type of tool this instance is acting as + /// + public abstract BuildTools ToolType { get; } + /// + /// True when the tool is actively being used by the user. (A wall is being created, for instance) + /// + public bool IsHolding { get; protected set; } + /// + /// True when the tool is not being actively used but is ready for user input + /// + public bool IsActive { get; private set; } = false; + /// + /// The position within the lot boundaries of the tool at the current time + /// + public Vector3 ToolLotPosition { get; protected set; } + /// + /// The position on the grid that the tool is currently nearest to + /// + public Vector2 ToolGridPosition { get; protected set; } + /// + /// Sets the property + /// + /// + public void SetActive(bool Activated) + { + IsActive = Activated; + OnActiveChanged(Activated); + } + protected abstract void OnActiveChanged(bool NewValue); + /// + /// Called once per frame for the tool to process changes between frames + /// + /// + public virtual void OnToolUpdate(BuildToolContext Context) + { + ToolLotPosition = Context.WandPosition; + ToolGridPosition = Context.GridPosition; + } + /// + /// Called when a tool begins to be used by the user + /// + /// + public abstract void OnToolStart(BuildToolContext Context); + /// + /// Called when a tool is cancelled. + /// Note: Calling this at any time will immediately cancel the current tool action, you can give a reason to add clarity + /// to why this was called. + /// + /// + public abstract void OnToolCancel(string Reason = null); + /// + /// Called when the user commits to an action. (Left click) + /// + /// + public virtual void OnToolFinalize(BuildToolContext Context) => OnFinalizeTool?.Invoke(this); + } + + /// + /// Represents shared behavior for tools in Build Mode where the user can click and drag an area to manipulate the lot + /// + internal abstract class AbstractRegionSelectBuildTool : AbstractBuildTool + { + //protected fields + /// + /// Defines behaviors for how the cursor should react to moving between levels + /// + protected enum MultilevelBehavior + { + /// + /// If the cursor leaves the level it started dragging, cancel the action immediately. + /// + CancelAction, + /// + /// If the cursor is not at the same level as it started when the action is finished, cancel the action. + /// Unlike , the action won't be cancelled immediately. + /// + Deny, + /// + /// Allows the cursor to freely move between levels and will not intervene. + /// + Allow, + /// + /// Keeps the level the same as when the tool started dragging so the level is retained even when the cursor moves between levels + /// + Constrain, + } + + protected Vector2Int toolDragStart; + protected Vector2Int toolDragEnd; + protected Vector2Int toolLastActionDragEnd; + protected Vector3 toolDragStartWorldPos; + protected int toolDragFloor; + + //public properties + /// + /// Where the user drag gesture initiated, in lot grid coordinates + /// + public Vector2Int ToolDragOrigin => toolDragStart; + /// + /// Where the user drag gesture ended, in lot grid coordinates + /// + public Vector2Int ToolDragDestination => toolDragEnd; + /// + /// Where the user drag gesture started in terms of which floor + /// + public int ToolDragFloor => toolDragFloor; + + //Protected + protected bool DeleteMode { get; set; } + protected static GameObject ToolCursorObject { get; set; } + protected static Transform ToolCursorObjectTransform => ToolCursorObject.transform; + protected static GameObject SelectionStartToolCursor { get; set; } + /// + /// See: + /// + protected virtual MultilevelBehavior MultiLevelBehavior { get; set; } = MultilevelBehavior.Deny; + + protected BuildModeServer BuildModeServer { get; } + + protected AbstractRegionSelectBuildTool(BuildModeServer Server) : base() + { + BuildModeServer = Server; + Init(); + } + + protected virtual void Init() + { + //get the default Wand + if (ToolCursorObject == null) + ToolCursorObject = GameObject.Find("Wand"); + if (SelectionStartToolCursor == null) + { + SelectionStartToolCursor = GameObject.Find("debugWandCompanion"); + SelectionStartToolCursor.SetActive(false); + } + } + + protected override void OnActiveChanged(bool NewValue) + { + ToolCursorObject.SetActive(NewValue); + if (!NewValue) SelectionStartToolCursor.SetActive(false); + } + + public override void OnToolStart(BuildToolContext Context) + { + if (IsHolding) return; // huh? weird edge case here + IsHolding = true; + toolDragStart = toolDragEnd = toolLastActionDragEnd = Context.GridPosition; + toolDragFloor = Context.CursorFloor; + + //set the selection origin cursor + toolDragStartWorldPos = Context.WandPosition; + SelectionStartToolCursor.SetActive(true); + SelectionStartToolCursor.transform.position = Context.WandPosition; + return; + } + + public override void OnToolUpdate(BuildToolContext Context) + { + base.OnToolUpdate(Context); + + var dirtyToolDragEnd = toolDragEnd; + + if (!IsActive) return; + ToolCursorObjectTransform.position = Context.WandPosition; + if (!IsHolding) return; + toolDragEnd = Context.GridPosition; + + if (MultiLevelBehavior == MultilevelBehavior.CancelAction && toolDragFloor != Context.CursorFloor) + {// drag between floors! + OnToolCancel("Start and end positions are not at the same level."); + return; + } + + //check if player change wall creation type + CheckDeleteMode(); + + if(dirtyToolDragEnd != toolDragEnd) + //clear any created wall facades + DoHoverAction(true); + toolLastActionDragEnd = toolDragEnd; + + //User let go of the Wand, signal finalize event + if (!Input.GetMouseButton(0)) + { // finalize + OnToolFinalize(Context); + return; + } + + if (dirtyToolDragEnd != toolDragEnd) + //Update facade + DoHoverAction(); + } + + public override void OnToolFinalize(BuildToolContext Context) + { + IsHolding = false; // finish drag gesture + if (toolDragStart == toolDragEnd) return; // misclick + if ((MultiLevelBehavior == MultilevelBehavior.CancelAction || MultiLevelBehavior == MultilevelBehavior.Deny) + && toolDragFloor != Context.CursorFloor) + {// drag between floors! + OnToolCancel("Start and end positions are not at the same level."); + return; + } + + //check if player change creation type + CheckDeleteMode(); + //Create / Delete the area + DoAction(); + + SelectionStartToolCursor.SetActive(false); // hide cursor + IsHolding = false; // drop tool + + base.OnToolFinalize(Context); + } + + public override void OnToolCancel(string Reason) + { + DoHoverAction(true); + + SelectionStartToolCursor.SetActive(false); // hide cursor + IsHolding = false; // drop tool + + Debug.Log($"{ToolName} cancelled. Reason: {Reason ?? "none given"}"); + } + + protected virtual void CheckDeleteMode() + { + //check if deleting objects + DeleteMode = false; + if (Input.GetKey(KeyCode.LeftControl)) DeleteMode = true; + } + /// + /// Performs the action that this tool advertises. + /// Note: This is not the same thing as as that is distinctly + /// when the user finishes the action. can be called many times depending on the implementation. + /// + /// + protected abstract void DoAction(bool Undo = false); + /// + /// Same as except with the intention of previewing the action before committing it + /// + /// + protected virtual void DoHoverAction(bool Undo = false) => DoAction(Undo); + } +} diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/AbstractBuildTool.cs.meta b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/AbstractBuildTool.cs.meta new file mode 100644 index 00000000..6d4038e1 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/AbstractBuildTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: be2df28f574842c48b230b4c3cf403dc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/FloorTool.cs b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/FloorTool.cs new file mode 100644 index 00000000..6a8a745a --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/FloorTool.cs @@ -0,0 +1,83 @@ +namespace OpenTS2.Engine.Modes.Build.Tools +{ + /// + /// Build tools that allow the user to make selections using the catalog in Build Mode + /// + internal interface ICatalogSelectableBuildTool + { + + } + /// + /// Build tool that takes patterns from Build Mode (walls and floors) + /// + internal interface IPatternSelectableBuildTool + { + /// + /// Dictates whether the tool should be using the or property + /// + bool IDPainting { get; } + + /// + /// The name of the pattern's material + /// + string PatternName { get; } + /// + /// The ID of the pattern in the target component map. + /// Walls and Floors (for example) both have individual WallMaps and FloorMaps + /// independent of one another and this mode should generally not be used in favor of . + /// + ushort PatternID { get; } + + /// + /// Sets the pattern the floor tool is currently painting + /// + void SetPaintPattern(string PatternName); + /// + /// Sets the pattern the floor tool is currently painting + /// + void SetPaintPatternID(ushort PatternID); + } + + /// + /// This Build Tool allows the User to drag out areas of flooring. + /// It invokes the for all lot modifications + /// + internal class FloorTool : AbstractRegionSelectBuildTool, IPatternSelectableBuildTool + { + public override string ToolName => "Floor Tool"; + public override BuildTools ToolType => BuildTools.Floor; + public bool IDPainting { get; private set; } + public string PatternName { get; private set; } + public ushort PatternID { get; private set; } + + public FloorTool(BuildModeServer Server) : base(Server) { } + + public void SetPaintPattern(string PatternName) + { + IDPainting = false; + this.PatternName = PatternName; + } + public void SetPaintPatternID(ushort PatternID) + { + IDPainting = true; + this.PatternID = PatternID; + } + + protected override void DoAction(bool Undo = false) + { + if (DeleteMode) + BuildModeServer.DeleteFloors(ToolDragOrigin, ToolDragDestination, ToolDragFloor); + else + { + if (IDPainting) + BuildModeServer.CreateFloors(ToolDragOrigin, ToolDragDestination, PatternID, ToolDragFloor); + else BuildModeServer.CreateFloors(ToolDragOrigin, ToolDragDestination, PatternName, ToolDragFloor); + } + } + + protected override void DoHoverAction(bool Undo = false) + { + ; + } + } +} diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/FloorTool.cs.meta b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/FloorTool.cs.meta new file mode 100644 index 00000000..50429916 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/FloorTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: af9a2f90db10abc4390cbda8afdecf9b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/FoundationTool.cs b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/FoundationTool.cs new file mode 100644 index 00000000..6f714be4 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/FoundationTool.cs @@ -0,0 +1,92 @@ +using OpenTS2.Content.DBPF; +using OpenTS2.Engine.Modes.Build.Tools; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace OpenTS2.Engine.Modes.Build.Tools +{ + /// + /// A build tool for creating foundations on the lot + /// This tool can be boiled down to the and put together + /// + internal class FoundationTool : AbstractRegionSelectBuildTool + { + const float FoundationHeight = 1; + const string FoundationWallpaper = "foundationbrick"; + private const string FoundationFlooring = "wood_wide_planks"; + + /// + /// Sets up a new Foundation Tool with the specified walls and floors + /// By default this is The Sims 2 default values Brick walls and Wood floors + /// + /// + /// + /// + public FoundationTool(BuildModeServer Server, + string Wallpaper = FoundationWallpaper, + string Flooring = FoundationFlooring) : base(Server) + { + WallpaperPattern = Wallpaper; + FlooringPattern = Flooring; + } + + public override string ToolName => "Foundation Tool"; + public override BuildTools ToolType => BuildTools.Foundation; + + //**PATTERN + /// + /// The pattern to paint the walls when creating a Foundation + /// + public string WallpaperPattern { get; set; } + /// + /// The pattern to paint the floors when creating a Foundation + /// + public string FlooringPattern { get; set; } + //*** + + protected override MultilevelBehavior MultiLevelBehavior { get; set; } = MultilevelBehavior.Constrain; + + + protected override void DoAction(bool Undo = false) + { + if (DeleteMode) Undo = !Undo; + if (ToolDragFloor > 1) + { + OnToolCancel("Foundation can only be placed on terrain."); + return; + } + + int currFloor = 0; + int nextFloor = currFloor + 1; + + float elevation = BuildModeServer.PollElevation(ToolDragOrigin, ToolDragFloor); + if (ToolDragFloor == 0) + elevation += FoundationHeight; + //This levels the Elevation map where the floor is on this foundation. + //This makes the walls stubby and the floor appear seamless. This also uses the LevelBeneathMe feature of the function. + //It will ensure all terrain beneath the foundation is less than the elevation -.1f. + BuildModeServer.LevelRegion(ToolDragOrigin, ToolDragDestination, elevation, nextFloor, true, -.1f); + if (!Undo) + { + BuildModeServer.CreateWalls(ToolDragOrigin, ToolDragDestination, currFloor, + Content.DBPF.WallType.Foundation, BuildModeServer.WallCreationModes.Room, + WallpaperPattern, WallpaperPattern); + BuildModeServer.CreateFloors(ToolDragOrigin, ToolDragDestination, FlooringPattern, nextFloor, false); + } + else + { + BuildModeServer.DeleteWalls(ToolDragOrigin, ToolDragDestination, currFloor, BuildModeServer.WallCreationModes.Room); + BuildModeServer.DeleteFloors(ToolDragOrigin, ToolDragDestination, nextFloor); + } + } + + protected override void DoHoverAction(bool Undo = false) + { + ; + } + } +} diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/FoundationTool.cs.meta b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/FoundationTool.cs.meta new file mode 100644 index 00000000..b73ebff2 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/FoundationTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 62528670419620547b98daca6a7118d5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/HandTool.cs b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/HandTool.cs new file mode 100644 index 00000000..f2123bd2 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/HandTool.cs @@ -0,0 +1,102 @@ +using OpenTS2.Components; +using OpenTS2.Scenes.Lot; +using System.Linq; +using UnityEngine; + +namespace OpenTS2.Engine.Modes.Build.Tools +{ + //*** NEEDS A REWORK ONCE SCENEGRAPH RENDERING IS REWORKED BECAUSE THIS IS NOT A GOOD SOLUTION + internal class HandTool : AbstractBuildTool + { + private LotLoad loadedLot; + private LotArchitecture architecture; + + private Transform holdingObjectRoot; + private GameObject holdingObjectReference; + private Vector3 originalPosition; + bool IsHoldingObject => holdingObjectReference != null; + + public override string ToolName => "Hand Tool"; + public override BuildTools ToolType => BuildTools.Hand; + + /// + /// Creates a new on the specified lot + /// + /// + /// + public HandTool(LotLoad loadedLot, LotArchitecture architecture) + { + this.loadedLot = loadedLot; + this.architecture = architecture; + + Init(); + } + + private void Init() + { + + } + + public override void OnToolCancel(string Reason) + { + if (!IsHoldingObject) return; + + holdingObjectRoot.position = originalPosition; + + holdingObjectReference = null; + holdingObjectRoot = null; + + Debug.Log($"{ToolName} cancelled. Reason: {Reason ?? "null"}"); + } + + public override void OnToolUpdate(BuildToolContext Context) + { + base.OnToolUpdate(Context); + + if (!IsHoldingObject) return; + holdingObjectRoot.position = Context.WandPosition; + + if (Input.GetMouseButtonDown(0)) + OnToolFinalize(Context); + } + + public override void OnToolFinalize(BuildToolContext Context) + { + if (!IsHoldingObject) return; + + holdingObjectRoot.position = Context.WandPosition; + + holdingObjectReference = null; + holdingObjectRoot = null; + + base.OnToolFinalize(Context); + } + + public override void OnToolStart(BuildToolContext Context) + { + // PERFORM OBJECT HITTEST (The colliders are added in ScenegraphComponent) + Ray camRay = TestLotViewCamera.GetMouseCursor3DRay(Camera.main); + var hits = Physics.RaycastAll(camRay); + + if (hits.Any()) + { // find any hits + foreach (RaycastHit hit in hits.OrderBy(x => x.distance)) + { + var parent = hit.collider.gameObject.transform.parent.gameObject; + if (parent.GetComponent() != null) + { + holdingObjectReference = parent; + holdingObjectRoot = holdingObjectReference.transform; + originalPosition = holdingObjectRoot.position; + break; + } + } + } + } + + protected override void OnActiveChanged(bool NewValue) + { + + } + } +} diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/HandTool.cs.meta b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/HandTool.cs.meta new file mode 100644 index 00000000..07a9c5d6 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/HandTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4bac8d546ec25bb4e94cf635b9d3d75f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/TerrainBrushTool.cs b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/TerrainBrushTool.cs new file mode 100644 index 00000000..78882de4 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/TerrainBrushTool.cs @@ -0,0 +1,109 @@ +using UnityEngine; +using static OpenTS2.Engine.Modes.Build.BuildModeServer; + +namespace OpenTS2.Engine.Modes.Build.Tools +{ + /// + /// Handles the functionality for the Wall Tool interacting with the lot + /// + internal class TerrainBrushTool : AbstractBuildTool + { + /// + /// Radii of terrain brush sizes + /// + public enum TerrainBrushSizes + { + Small = 0, // since this tool is only 1 point, it's not a tile wide + Medium = 2, + Large = 4 + } + + //private + BuildModeServer buildModeServer; + ushort dryWallDesignPattern; + private GameObject rootWallCursor; + private Transform _wallCursorTransform; + + /// + /// The current brush mode the tool is using + /// + public BuildModeServer.TerrainModificationModes CurrentBrushMode { get; set; } + /// + /// The current stroke thickness of the brush + /// + public int BrushSize { get; set; } = (int)TerrainBrushSizes.Large; + public float BrushStrength { get; set; } = 1f; + + /// + /// Creates a new on the specified lot + /// + /// + /// + public TerrainBrushTool(BuildModeServer Server) + { + buildModeServer = Server; + Init(); + } + + void Init() + { + //get the default Wand + rootWallCursor = GameObject.Find("Wand"); + _wallCursorTransform = rootWallCursor.transform; + } + + public override string ToolName => "Terrain Tool"; + public override BuildTools ToolType => BuildTools.TerrainBrush; + + protected override void OnActiveChanged(bool NewValue) + { + rootWallCursor.SetActive(NewValue); + CurrentBrushMode = TerrainModificationModes.Raise; + } + + public override void OnToolCancel(string Reason) + { + IsHolding = false; // drop tool + + Debug.Log($"{ToolName} cancelled. Reason: {Reason ?? "null"}"); + } + + public override void OnToolFinalize(BuildToolContext Context) + { + IsHolding = false; // finish brush stroke + + base.OnToolFinalize(Context); + } + + public override void OnToolStart(BuildToolContext Context) + { + if (IsHolding) return; // huh? weird edge case here + IsHolding = true; // brush stroke start + + CommitToolStroke(Context.GridPosition); // makes single clicks (only lasting a frame) possible + } + + public override void OnToolUpdate(BuildToolContext Context) + { + base.OnToolUpdate(Context); + + if (!IsActive) return; + _wallCursorTransform.position = Context.WandPosition; + if (!IsHolding) return; + + //User let go of the Wand, signal finalize event + if (!Input.GetMouseButton(0)) + { // finalize + OnToolFinalize(Context); + return; + } + //user is still painting + CommitToolStroke(Context.GridPosition); + } + + void CommitToolStroke(Vector2Int position) + { + buildModeServer.ModifyTerrain(position, BrushSize, BrushStrength * Time.deltaTime, CurrentBrushMode); + } + } +} diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/TerrainBrushTool.cs.meta b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/TerrainBrushTool.cs.meta new file mode 100644 index 00000000..de132f71 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/TerrainBrushTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 322a39bbf1f7a8446b4b6194c8ec1abf +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/WallTool.cs b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/WallTool.cs new file mode 100644 index 00000000..b2721ce3 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/WallTool.cs @@ -0,0 +1,124 @@ +using OpenTS2.Content.DBPF; +using OpenTS2.Engine.Modes.Build.Tools; +using OpenTS2.Scenes.Lot; +using OpenTS2.Scenes.Lot.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using static OpenTS2.Engine.Modes.Build.BuildModeServer; + +namespace OpenTS2.Engine.Modes.Build.Tools +{ + /// + /// Handles the functionality for the Wall Tool interacting with the lot + /// + internal class WallTool : AbstractRegionSelectBuildTool + { + // Wall cursor tool + WallCreationModes wallCreateMode = WallCreationModes.Single; + bool deletingWalls => DeleteMode; + + //private + /// + /// This is used when dragging an area of walls. + /// The walls created as a facade have their IDs stored here. + /// When the action is cancelled or the facade changes, these IDs + /// will be used to delete the unneeded walls. + /// + HashSet facadeWallIDs = new HashSet(); + BuildModeServer buildModeServer => BuildModeServer; + bool Hovering = false; + + /// + /// Creates a new on the specified lot + /// + /// + /// + public WallTool(BuildModeServer Server) : base(Server) { } + + public override string ToolName => "Wall Tool"; + public override BuildTools ToolType => BuildTools.Wall; + + /// + /// Updates depending on the player's current input + /// + protected override void CheckDeleteMode() + { + //creation mode + WallCreationModes mode = WallCreationModes.Single; + if (Input.GetKey(KeyCode.LeftShift)) mode = WallCreationModes.Room; + wallCreateMode = mode; + + //check delete mode + base.CheckDeleteMode(); + } + + /// + /// Creates/Deletes the walls in the selected space + /// + protected override void DoAction(bool Undoing = false) + { + bool actionSelected = !deletingWalls; + Vector2Int destination = toolDragEnd; + if (deletingWalls) destination = toolLastActionDragEnd; + if (Undoing) + { + if(destination != toolLastActionDragEnd) + destination = toolLastActionDragEnd; + else destination = toolDragEnd; + actionSelected = !actionSelected; + } + if (toolDragStart - destination == new Vector2Int(0, 0)) + { + if (Hovering && facadeWallIDs.Count > 0) + { + buildModeServer.DeleteAllWalls(facadeWallIDs.ToArray()); + facadeWallIDs.Clear(); + } + return; // zero area + } + if (actionSelected) + { + float startElevation = buildModeServer.PollElevation(toolDragStart, toolDragFloor); + //level upper elevation to ensure the wall is the correct size + foreach (var segment in GetWallPoints(toolDragStart, destination, wallCreateMode)) + { + //level terrain beneath the wall + //buildModeServer.SetElevationRelative(startElevation, toolDragFloor, segment.A, segment.B); + //level above the wall to ensure the height is accurate + buildModeServer.SetElevationRelative(LotWallComponent.WallHeight, toolDragFloor + 1, segment.A, segment.B); + } + var createdWallIDs = buildModeServer.CreateWalls(toolDragStart, destination, toolDragFloor, WallType.Normal, wallCreateMode); + if (Hovering) + { + if (createdWallIDs != null) + Array.ForEach(createdWallIDs, (int item) => facadeWallIDs.Add(item)); + } + else if (facadeWallIDs.Any()) + { + facadeWallIDs.Clear(); + buildModeServer.SignalRoomsInvalidate(); + } + } + else + { + if (Hovering && facadeWallIDs.Count > 0) + { + buildModeServer.DeleteAllWalls(facadeWallIDs.ToArray()); + facadeWallIDs.Clear(); + } + buildModeServer.DeleteWalls(toolDragStart, destination, ToolDragFloor, wallCreateMode); + } + } + + protected override void DoHoverAction(bool Undo = false) + { + Hovering = true; + DoAction(Undo); + Hovering = false; + } + } +} diff --git a/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/WallTool.cs.meta b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/WallTool.cs.meta new file mode 100644 index 00000000..e49f6257 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Modes/Build/Tools/WallTool.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 7ed8cbcbc0bb3d24f864d9ff7650d4e6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Engine/Tests/BuildModeTest.cs b/Assets/Scripts/OpenTS2/Engine/Tests/BuildModeTest.cs new file mode 100644 index 00000000..35dbe571 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Tests/BuildModeTest.cs @@ -0,0 +1,143 @@ +// JDrocks450 11-22-2023 on GitHub + +using OpenTS2.Common; +using OpenTS2.Content; +using OpenTS2.Content.DBPF.Scenegraph; +using OpenTS2.Content.DBPF; +using OpenTS2.Files.Formats.DBPF; +using OpenTS2.Files; +using OpenTS2.Scenes.Lot.State; +using OpenTS2.Scenes.Lot; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using UnityEngine; +using static OpenTS2.Content.DBPF.LotObjectAsset; +using OpenTS2.Scenes.Lot.Extensions; +using OpenTS2.Engine.Modes.Build; +using OpenTS2.UI.Layouts; +using System.Linq; +using OpenTS2.Engine; +using OpenTS2.UI.Layouts.Lot; +using UnityEditor.SearchService; +using OpenTS2.Engine.Modes.Build.Tools; + +/// +/// Serves the behavior for the Build Mode Test scene +/// +public class BuildModeTest : MonoBehaviour +{ + // Init + LotLoad _loadedLotProvider; + LotArchitecture _architectrue => _loadedLotProvider.Architecture; + TestLotViewCamera viewCamera; + BuildModeServer buildServer; + LotViewHUDController hudController; + + void ContentStartup(bool LoadGameContent = true) + { + EPManager.Instance.InstalledProducts = 0x3EFFF; + ContentLoading.LoadContentStartup(); + + GameGlobals.allowCustomContent = false; + + if (!LoadGameContent) return; + + //load the game content + ContentLoading.LoadGameContentSync(); + Core.OnFinishedLoading.Invoke(); + CatalogManager.Instance.Initialize(); + } + + void Awake() + { + //Load content + ContentStartup(true); + } + + void Start() + { + //load the default lot for now + _loadedLotProvider = gameObject.AddComponent(); + _loadedLotProvider.LoadLot("N001", 80); + + //create the build mode server + buildServer = gameObject.AddComponent(); + buildServer.Initialize(_loadedLotProvider); + buildServer.ChangeTool(BuildTools.None); + + //set the camera transform + viewCamera = GameObject.Find("Main Camera").GetComponent(); + + //connect to the HUD controller + hudController = GameObject.Find("Scene/LotViewUIController").GetComponent(); + hudController.Puck.SetMode(LotViewHUD.HUD_UILotModes.Build); // set puck display state to be Build mode + hudController.Puck.OnModeChangeRequested += HUD_GameModeRequest; + } + + private void HUD_GameModeRequest(object sender, LotViewHUD.ModeChangeEventArgs e) + { + e.Allow = false; // locked in Build Mode for the time being + } + + // Update is called once per frame + void Update() + { + HandleTool(); + } + + void HandleTool() + { + if (buildServer.CurrentTool == null) return; + + //set cursor position + ReevaluateTerrainMesh(); + bool successful = viewCamera.TranslateScreen2WorldPos(_loadedLotProvider.Floor, out var terrainPosition, out int currentCursorFloor); + if (!successful) return; // do not update the cursor when the mouse leaves the lot boundary. this prevents any events from firing with build tools + var buildCursorPos = new Vector3((float)Math.Round(terrainPosition.x, 0), terrainPosition.y, (float)Math.Round(terrainPosition.z, 0)); + Vector2Int wallcurPos = new Vector2Int((int)buildCursorPos.x, (int)buildCursorPos.z); // flat grid position for wall tool + + //context + BuildToolContext context = new BuildToolContext() + { + Cursor3DWorldPosition = terrainPosition, + WandPosition = buildCursorPos, + GridPosition = wallcurPos, + CursorFloor = currentCursorFloor + }; + + buildServer.CurrentTool.OnToolUpdate(context); // mouse moved + if (Input.GetMouseButtonDown(0)) // mouse left click start + { // start drag + buildServer.CurrentTool.OnToolStart(context); + return; + } + if (Input.GetKeyDown(KeyCode.Escape)) + { + buildServer.CurrentTool.OnToolCancel("User cancelled using the ESC key."); + return; + } + } + + /// + /// Resets the -- this may be necessary when the terrain mesh is updated, for example + /// + void ReevaluateTerrainMesh() + { + var floorComp = _architectrue.GetComponentByType(LotArchitecture.ArchitectureGameObjectTypes.floor); + for(int floor = 0; floor < _architectrue.BaseFloor + _architectrue.Elevation.Depth; floor++) + { + int actualFloor = _architectrue.BaseFloor + floor; + List collidersByFloor = new List(); + if (actualFloor == 0) + { + var terrainObject = GameObject.Find("terrain/Terrain"); + collidersByFloor.Add(terrainObject.GetComponent()); + } + if(floorComp.TryGetCollidersByFloor(floor, out var colliders)) + collidersByFloor.AddRange(colliders); + viewCamera.SetCameraDetectionMeshes(actualFloor, collidersByFloor); + } + } +} diff --git a/Assets/Scripts/OpenTS2/Engine/Tests/BuildModeTest.cs.meta b/Assets/Scripts/OpenTS2/Engine/Tests/BuildModeTest.cs.meta new file mode 100644 index 00000000..96a90ea0 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Engine/Tests/BuildModeTest.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 94cb5833fa52cfc4392f5daf80bc17b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Scenes/Lot/Extensions.meta b/Assets/Scripts/OpenTS2/Scenes/Lot/Extensions.meta new file mode 100644 index 00000000..faec23cf --- /dev/null +++ b/Assets/Scripts/OpenTS2/Scenes/Lot/Extensions.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a72c7287d7d00824197bc42606cacaca +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Scenes/Lot/Extensions/LotArchitectureBuildModeExtensions.cs b/Assets/Scripts/OpenTS2/Scenes/Lot/Extensions/LotArchitectureBuildModeExtensions.cs new file mode 100644 index 00000000..ad48ac49 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Scenes/Lot/Extensions/LotArchitectureBuildModeExtensions.cs @@ -0,0 +1,201 @@ +using Codice.CM.Triggers; +using Codice.Utils; +using log4net.Core; +using OpenTS2.Content.DBPF; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace OpenTS2.Scenes.Lot.Extensions +{ + //JDrocks450 'Bloaty' 12/9/23 -- Contains all build mode functions with respect to LotArchitecture instances in one unit + + /// + /// Extensions for manipulating instances for build mode operations + /// + internal static class LotArchitectureBuildModeExtensions + { + /// + /// Creates a wall. + /// This function will ensure the following: + /// The Wall is split into 1 unit long segments + /// The Wall has the wallpaper you provide added to each segment created. + /// The Wall is a straight line, diagonally, horizontally or vertically aligned + /// The Wall is of the type provided. + /// The Wall is added to the instance this is called on + /// + /// + /// + /// The point the wall originates + /// The point the wall is destined to + /// Which floor the wall is on + /// The type of wall you wish to create + /// The Pattern1 of the wall + /// The Pattern2 of the wall + /// upon total failure; no walls placed. + /// if some walls were placed. if all wall segments placed. + public static int[]? CreateWall(this LotArchitecture Architecture, Vector2 From, + Vector2 To, int Floor, WallType Type = WallType.Normal, ushort PatternFront = ushort.MaxValue, ushort PatternBack = ushort.MaxValue) + { + //get the wall's direction vector + var dirVector = To - From; + dirVector.Normalize(); + + bool diagonal = false; + + if (Math.Abs(dirVector.x) == Math.Abs(dirVector.y)) + { + dirVector = new Vector2(dirVector.x < 0 ? -1 : 1, dirVector.y < 0 ? -1 : 1); + diagonal = true; + } + if ((Math.Abs(dirVector.x) != 1 || dirVector.y != 0) && (Math.Abs(dirVector.y) != 1 || dirVector.x != 0) && + (Math.Abs(dirVector.x) != 1 || Math.Abs(dirVector.y) != 1)) // valid directions for a wall + return null; + + //subdivide the wall into 1 unit lengths (otherwise wall paints will appear stretchy) + var wallLength = (double)Math.Abs(Vector2.Distance(From, To)); + if (diagonal) wallLength *= 0.7; + + int successfulWallSegments = 0, totalSegments = (int)Math.Round(wallLength); + int[] createdWallIDs = new int[totalSegments]; + + for (int segment = 0; segment < (wallLength); segment++) + { + var fooFrom = From + (dirVector * segment); + var fooTo = From + (dirVector * (segment + 1)); + //Add wall to wallgraph + if (!Architecture.WallGraphAll.PushWall(fooFrom, fooTo, Floor, out int LayerID)) continue; + if (createdWallIDs.Length <= segment) + Array.Resize(ref createdWallIDs, segment + 1); + createdWallIDs[segment] = LayerID; + //Add wall layer data + Architecture.WallLayer.Walls.Add(LayerID, new WallLayerEntry() + { + Id = LayerID, + Pattern1 = PatternFront, + Pattern2 = PatternBack, + WallType = Type + }); + successfulWallSegments++; + } + if (successfulWallSegments <= 0) return null; // total failure + return createdWallIDs; // somewhat / complete success code + } + /// + /// Deletes wall segments between the two points provided on the given floor + /// + /// + /// + /// + /// + public static bool? DeleteWall(this LotArchitecture Architecture, Vector2 From, Vector2 To, int Floor) + { + //get the wall's direction vector + var dirVector = To - From; + dirVector.Normalize(); + + bool diagonal = false; + + if (Math.Abs(dirVector.x) == Math.Abs(dirVector.y)) + { + dirVector = new Vector2(dirVector.x < 0 ? -1 : 1, dirVector.y < 0 ? -1 : 1); + diagonal = true; + } + if ((Math.Abs(dirVector.x) != 1 || dirVector.y != 0) && (Math.Abs(dirVector.y) != 1 || dirVector.x != 0) && + (Math.Abs(dirVector.x) != 1 || Math.Abs(dirVector.y) != 1)) // valid directions for a wall + return false; + + //subdivide the wall into 1 unit lengths (otherwise wall paints will appear stretchy) + var wallLength = (double)Math.Abs(Vector2.Distance(From, To)); + if (diagonal) wallLength *= 0.7; + + int successfulWallSegments = 0, totalSegments = (int)wallLength; + for (int segment = 0; segment < (wallLength); segment++) + { + var fooFrom = From + (dirVector * segment); + var fooTo = From + (dirVector * (segment + 1)); + //Remove wall from wallgraph + if (!Architecture.WallGraphAll.RemoveWall(fooFrom, fooTo, Floor, out int LayerID)) continue; + //Remove wall layer data + Architecture.WallLayer.Walls.Remove(LayerID); + successfulWallSegments++; + } + if (successfulWallSegments <= 0) return null; // total failure + return successfulWallSegments < totalSegments ? false : true; // somewhat / complete success code + } + public static void DeleteAllWalls(this LotArchitecture Architecture, params int[] WallLayerIDs) + { + if (WallLayerIDs.Length <= 0) return; + foreach(var ID in WallLayerIDs) + { + //Remove wall from wallgraph + if (Architecture.WallGraphAll.RemoveWalls(WallLayerIDs) == 0) continue; + //Remove wall layer data + foreach(var id in WallLayerIDs) + Architecture.WallLayer.Walls.Remove(id); + } + } + /// + /// Checks to see if the given pattern name is referenced; if it isn't, will reference it in the + /// and returns the PatternID of the (potentially newly) referenced pattern + /// + /// + /// + /// + /// + /// true if the pattern was already referenced + /// + public static bool EnsurePatternReferenced(this LotArchitecture arch, string PatternName, + LotArchitecture.ArchitectureGameObjectTypes Scope, out ushort PatternID, out bool Existed) + { + Existed = false; + PatternID = 0; + + Dictionary Map = Scope switch + { + LotArchitecture.ArchitectureGameObjectTypes.wall => arch.WallMap.Map, + LotArchitecture.ArchitectureGameObjectTypes.floor => arch.FloorMap.Map, + _ => null + }; + + if (Map == null) return false; + if (Map.Count == 0) + goto create; + var hits = Map.Where(x => x.Value.Value == PatternName); + if (!hits.Any()) + goto create; + Existed = true; + PatternID = hits.FirstOrDefault().Key; + return true; + + //lord forgive me for using goto + create: + PatternID = (ushort)(Map.Count + 1); + if (Map.Any()) + PatternID = Math.Max(PatternID, (ushort)(Map.Keys.Max() + 1)); + Map.Add(PatternID, new StringMapEntry() + { + Id = PatternID, + Value = PatternName, + Unknown = 0 + }); + return true; + } + + public static float PollElevation(this LotArchitecture architecture, Vector2Int Position, int Floor = 0) + { + Floor = -architecture.BaseFloor + Floor; + + float[] dataSet = architecture.Elevation.Data[Floor]; + + var mPos = Position; + int index = (mPos.x * architecture.Elevation.Height) + mPos.y; + + return dataSet[index]; + } + } +} diff --git a/Assets/Scripts/OpenTS2/Scenes/Lot/Extensions/LotArchitectureBuildModeExtensions.cs.meta b/Assets/Scripts/OpenTS2/Scenes/Lot/Extensions/LotArchitectureBuildModeExtensions.cs.meta new file mode 100644 index 00000000..fb58a6c2 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Scenes/Lot/Extensions/LotArchitectureBuildModeExtensions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 118886c7f98c90e44b3d7c13cbab0440 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Scenes/Lot/LotArchitecture.cs b/Assets/Scripts/OpenTS2/Scenes/Lot/LotArchitecture.cs index 738abc99..f5ded5d4 100644 --- a/Assets/Scripts/OpenTS2/Scenes/Lot/LotArchitecture.cs +++ b/Assets/Scripts/OpenTS2/Scenes/Lot/LotArchitecture.cs @@ -1,4 +1,5 @@ using OpenTS2.Common; +using OpenTS2.Components; using OpenTS2.Content.DBPF; using OpenTS2.Files.Formats.DBPF; using OpenTS2.Scenes.Lot.Roof; @@ -154,27 +155,72 @@ private void Validate() } } + public T GetComponentByType(ArchitectureGameObjectTypes Type) where T : AssetReferenceComponent => Type switch + { + ArchitectureGameObjectTypes.roof => _roofComponent as T, + ArchitectureGameObjectTypes.wall => _wallComponent as T, + ArchitectureGameObjectTypes.floor => _floorComponent as T, + ArchitectureGameObjectTypes.terrain => _terrainComponent as T, + _ => default + }; + + public void InvalidateComponent(ArchitectureGameObjectTypes Type) => GetComponentByType(Type)?.Invalidate(); + /// /// Creates game objects for the lot architecture, and returns them in the provided list. /// /// List of created game objects public void CreateGameObjects(List componentList) { - var roofObj = new GameObject("roof", typeof(LotRoofComponent)); - _roofComponent = roofObj.GetComponent().CreateFromLotArchitecture(this); - componentList.Add(roofObj); - - var wall = new GameObject("wall", typeof(LotWallComponent)); - _wallComponent = wall.GetComponent().CreateFromLotArchitecture(this); - componentList.Add(wall); + // make gameobjects for each type of object on the lot + for(int type = 0; type < Enum.GetValues(typeof(ArchitectureGameObjectTypes)).Length; type++) + { + ArchitectureGameObjectTypes tType = (ArchitectureGameObjectTypes)type; + if (!CreateGameObjectsOfType(componentList, tType)) + ; // TODO: Handle if this fails + } + } - var floor = new GameObject("floor", typeof(LotFloorComponent)); - _floorComponent = floor.GetComponent().CreateFromLotArchitecture(this); - componentList.Add(floor); + public enum ArchitectureGameObjectTypes + { + roof, + wall, + floor, + terrain + } - var terrain = new GameObject("terrain", typeof(LotTerrainComponent)); - _terrainComponent = terrain.GetComponent().CreateFromLotArchitecture(this); - componentList.Add(terrain); + /// + /// Creates the correspondent GameObject for the type of geometry specified by + /// Adds it to the given component list automatically + /// + /// + /// + /// + public bool CreateGameObjectsOfType(List componentList, ArchitectureGameObjectTypes Type) + { + GameObject obj = default; + switch (Type) + { + case ArchitectureGameObjectTypes.roof: + var roofObj = obj = new GameObject("roof", typeof(LotRoofComponent)); + _roofComponent = roofObj.GetComponent().CreateFromLotArchitecture(this); + break; + case ArchitectureGameObjectTypes.wall: + var wall = obj = new GameObject("wall", typeof(LotWallComponent)); + _wallComponent = wall.GetComponent().CreateFromLotArchitecture(this); + break; + case ArchitectureGameObjectTypes.floor: + var floor = obj = new GameObject("floor", typeof(LotFloorComponent)); + _floorComponent = floor.GetComponent().CreateFromLotArchitecture(this); + break; + case ArchitectureGameObjectTypes.terrain: + var terrain = obj = new GameObject("terrain", typeof(LotTerrainComponent)); + _terrainComponent = terrain.GetComponent().CreateFromLotArchitecture(this); + break; + default: return false; + } + componentList.Add(obj); + return true; } private static int CalculateElevationIndex(int height, int x, int y) diff --git a/Assets/Scripts/OpenTS2/Scenes/Lot/LotFloorComponent.cs b/Assets/Scripts/OpenTS2/Scenes/Lot/LotFloorComponent.cs index 23b11477..70d0fe7d 100644 --- a/Assets/Scripts/OpenTS2/Scenes/Lot/LotFloorComponent.cs +++ b/Assets/Scripts/OpenTS2/Scenes/Lot/LotFloorComponent.cs @@ -24,10 +24,12 @@ public class LotFloorComponent : AssetReferenceComponent private LotArchitecture _architecture; private PatternMeshCollection _patterns; + private Dictionary> _colliders; public LotFloorComponent CreateFromLotArchitecture(LotArchitecture architecture) { _architecture = architecture; + _colliders = new Dictionary>(); LoadPatterns(); BuildFloorMeshes(); @@ -136,6 +138,7 @@ private void BuildFloorMeshes() int height = patternData.Height; int eHeight = height + 1; + _colliders.Clear(); _patterns.ClearAll(); Vector3[] tileVertices = new Vector3[5]; @@ -196,6 +199,8 @@ void AddThickness(int from, int to) PatternMesh thickness = floor.Get(0); thickComp = thickness.Component; } + if (!_colliders.ContainsKey(i)) + _colliders.Add(i, new HashSet()); // Pattern is present. float e0 = elevation[ei]; @@ -225,6 +230,8 @@ void AddThickness(int from, int to) comp.AddVertices(tileVertices, tileUVs); comp.AddTriangle(m1Base, 1, 0, 4); + + _colliders[i].Add(mesh1.Object.GetComponent()); } PatternMesh mesh2 = floor.Get(p.z + 1); @@ -245,6 +252,7 @@ void AddThickness(int from, int to) } comp.AddTriangle(m2Base, 2, 1, 4); + _colliders[i].Add(mesh2.Object.GetComponent()); } PatternMesh mesh3 = floor.Get(p.y + 1); @@ -269,6 +277,7 @@ void AddThickness(int from, int to) } comp.AddTriangle(m3Base, 3, 2, 4); + _colliders[i].Add(mesh3.Object.GetComponent()); } PatternMesh mesh4 = floor.Get(p.x + 1); @@ -296,7 +305,8 @@ void AddThickness(int from, int to) comp.AddVertices(tileVertices, tileUVs); } - comp.AddTriangle(m4Base, 0, 3, 4); + comp.AddTriangle(m4Base, 0, 3, 4); + _colliders[i].Add(mesh4.Object.GetComponent()); } if (i > -baseFloor) @@ -364,5 +374,12 @@ public void UpdateDisplay(WorldState state) { _patterns.UpdateDisplay(state, _architecture.BaseFloor); } + + internal bool TryGetCollidersByFloor(int floor, out IEnumerable Colliders) + { + var success = _colliders.TryGetValue(floor, out var value); + Colliders = value; // cringe out parameter doesn't do implicit casts ;_; + return success; + } } } \ No newline at end of file diff --git a/Assets/Scripts/OpenTS2/Scenes/Lot/LotLoad.cs b/Assets/Scripts/OpenTS2/Scenes/Lot/LotLoad.cs new file mode 100644 index 00000000..056ae6d7 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Scenes/Lot/LotLoad.cs @@ -0,0 +1,345 @@ +// JDrocks450 11-22-2023 on GitHub + +#define NEW_INVALIDATE +#undef NEW_INVALIDATE + +using OpenTS2.Common; +using OpenTS2.Content; +using OpenTS2.Content.DBPF; +using OpenTS2.Content.DBPF.Scenegraph; +using OpenTS2.Files; +using OpenTS2.Files.Formats.DBPF; +using OpenTS2.Scenes.Lot.State; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using UnityEngine; + +namespace OpenTS2.Scenes.Lot +{ + /// + /// Standard behavior for loading into the lot view. + /// Provides standard functionality for placing terrain, walls, floors, objects, etc. into the Scene + /// + public class LotLoad : MonoBehaviour + { + #region private + private WorldState _state = new WorldState(1, WallsMode.Roof); + + private string _nhood; + private int _lotId; + + private DBPFFile _lotPackage; + + private List _lotObject = new List(); + private List _testObjects = new List(); + + private RuntimeWallController _wallController = new RuntimeWallController(); + #endregion + + #region public + /// + /// Settings to dictate how the lot should be loaded + /// + public struct LotLoadSettings + { + [Flags] + public enum LotViewLayers : int + { + /// + /// Default - Do not display any architecture or objects + /// + None = 0, + /// + /// Flag - Loads terrain when set + /// + Terrain = 1, + /// + /// Flag - Loads walls when set + /// + Walls = 2, + /// + /// Flag - Loads floors when set + /// + Floors = 4, + /// + /// Flag - Loads objects when set + /// + Objects = 8 + } + /// + /// Generates the flags necessary to load all layers + /// + public const LotViewLayers AllLayers = + LotViewLayers.None | LotViewLayers.Terrain | LotViewLayers.Walls | LotViewLayers.Floors | LotViewLayers.Objects; + /// + /// Specifies which layers of the lot geometry should be loaded + /// + public LotViewLayers LoadLayers; + /// + /// Makes a new using the specified layers. + /// Default is value -- to load all layers on the lot + /// + /// + public LotLoadSettings(LotViewLayers loadLayers = AllLayers) + { + LoadLayers = loadLayers; + } + } + /// + /// (Debug) N001 + /// + public const string Default_NeighborhoodPrefix = "N001"; + /// + /// (Debug) 82 + /// + public const int Default_LotID = 82; + + /// + /// The current floor selected in the + /// + public int Floor => _state.Level; + /// + /// The current ViewMode in the + /// + /// + public WallsMode ViewMode = WallsMode.Roof; + + public int BaseFloor => Architecture?.BaseFloor ?? 0; + public int MaxFloor => Architecture?.FloorPatterns.Depth ?? 1; + /// + /// The current which indicates how the level should appear + /// Changing this directly will log in the console, but should be followed up with to ensure + /// changes can be perceived in the world. + /// + public WorldState WorldState + { + get => _state; + set + { + _state = value; + Debug.Log($"World state updated: {value}"); + } + } + + /// + /// The currently selected Neighborhood Prefix (N001 by default) + /// + public string NeighborhoodPrefix => _nhood; + /// + /// The currently selected Lot ID in the selected (82 by default) + /// + public int LotID => _lotId; + /// + /// The base that this instance used to load the scene + /// + public LotArchitecture Architecture { get; private set; } + #endregion + + /// + /// Loads the default lot + /// See: and + /// + public void Load() + { + Load(Default_NeighborhoodPrefix, Default_LotID); + } + + /// + /// Creates a new instance (that can be reused) with the specified Neighborhood and LotID to load + /// + /// + /// + public void Load(string Nhood, int LotID) + { + LoadLot(Nhood, LotID); + } + /// + /// Unloads the current lot and destroys each object instance in memory. + /// + public void UnloadLot() + { + foreach (var obj in _lotObject) + { + Destroy(obj); + } + + _lotObject.Clear(); + _testObjects.Clear(); + } + + /// + /// Updates and and loads the lot. + /// See: + /// + /// + /// + public void LoadLot(string neighborhoodPrefix, int id, LotLoadSettings Settings = default) + { + _nhood = neighborhoodPrefix; + _lotId = id; + + LoadLot(Settings); + } + /// + /// Unloads the current lot (if applicable) and loads the lot provided by + /// + /// The settings to use to load the lot + public void LoadLot(LotLoadSettings Settings = default) + { + //Unload previous session + UnloadLot(); + + var contentProvider = ContentManager.Instance; + //Find package file for the lot given + var lotsFolderPath = Path.Combine(Filesystem.UserDataDirectory, $"Neighborhoods/{NeighborhoodPrefix}/Lots"); + var lotFilename = $"{NeighborhoodPrefix}_Lot{LotID}.package"; + var lotFullPath = Path.Combine(lotsFolderPath, lotFilename); + + if (!File.Exists(lotFullPath)) + { + throw new FileNotFoundException($"LoadLot(): File not found. {lotFullPath}"); + } + + _lotPackage = contentProvider.AddPackage(lotFullPath); + + //add objects from scenegraph (primitive need to change to more robust solution later) + InvalidateObjects(); + + Architecture = new LotArchitecture(); + + Architecture.LoadFromPackage(_lotPackage); + Architecture.CreateGameObjects(_lotObject); + + ViewMode = WallsMode.Roof; // Default value is Roof + _state = new WorldState(Floor, ViewMode); + InvalidateState(); + } + /// + /// Flushes all currently loaded objects and reloads them from package + /// + public void InvalidateObjects() + { + //Clear any objects on the scene + UnloadLot(); + + var contentProvider = ContentManager.Instance; + // Go through each lot object. + foreach (var entry in _lotPackage.Entries) + { + if (entry.GlobalTGI.TypeID != TypeIDs.LOT_OBJECT) + { + continue; + } + + try + { + var lotObject = entry.GetAsset(); + var resource = contentProvider.GetAsset( + new ResourceKey(lotObject.Object.ResourceName + "_cres", GroupIDs.Scenegraph, + TypeIDs.SCENEGRAPH_CRES)); + if (resource == null) + { + Debug.Log($"Could not find lot object: {lotObject.Object.ResourceName}"); + continue; + } + + var model = resource.CreateRootGameObject(); + model.transform.GetChild(0).localPosition = lotObject.Object.Position; + model.transform.GetChild(0).localRotation = lotObject.Object.Rotation; + + _lotObject.Add(model); + _testObjects.Add(model); + } + catch (Exception e) + { + Debug.LogException(e); + } + } + } + /// + /// Invalidates the specified group supplied by + /// + /// + void BaseInvalidateGroup(LotArchitecture.ArchitectureGameObjectTypes group) + { +#if NEW_INVALIDATE + architecture.InvalidateComponent(group); + return; +#endif + if (group == LotArchitecture.ArchitectureGameObjectTypes.roof) + { + Architecture.InvalidateComponent(group); + return; + } + //old + string objectName = group.ToString(); + //destroy existing object + var groupParent = _lotObject.Where(x => x.name == objectName).FirstOrDefault(); + if (groupParent != null) + { // clean walls + Destroy(groupParent); + _lotObject.Remove(groupParent); + } + //rebuild new mesh from memory + Architecture.CreateGameObjectsOfType(_lotObject, group); + } + + public void InvalidateFloors() + { + BaseInvalidateGroup(LotArchitecture.ArchitectureGameObjectTypes.floor); + InvalidateTerrain(); + } + public void InvalidateWalls() => BaseInvalidateGroup(LotArchitecture.ArchitectureGameObjectTypes.wall); + public void InvalidateTerrain() => BaseInvalidateGroup(LotArchitecture.ArchitectureGameObjectTypes.terrain); + + /// + /// After changing the , use this method to update the visual + /// + public void InvalidateState() + { + Architecture.UpdateState(_state); + UpdateObjectVisibility(_state.Level); + } + /// + /// Sets the given object's visibility to be Visible/Invisible + /// + /// + /// + private void SetObjectVisiblity(GameObject obj, bool visible) + { + foreach (MeshRenderer renderer in obj.GetComponentsInChildren()) + { + renderer.shadowCastingMode = visible ? UnityEngine.Rendering.ShadowCastingMode.On : UnityEngine.Rendering.ShadowCastingMode.ShadowsOnly; + } + + foreach (SkinnedMeshRenderer renderer in obj.GetComponentsInChildren()) + { + renderer.shadowCastingMode = visible ? UnityEngine.Rendering.ShadowCastingMode.On : UnityEngine.Rendering.ShadowCastingMode.ShadowsOnly; + } + } + /// + /// Sets objects to be visible based on if they should be seen by the player or not. + /// This uses the to determine up to which floor an object should be viewable. + /// + /// + private void UpdateObjectVisibility(int viewLevel) + { + foreach (GameObject obj in _testObjects) + { + int level = Architecture.GetLevelAt(obj.transform.GetChild(0).localPosition); + + SetObjectVisiblity(obj, level < viewLevel); + } + } + + /// + /// Regenerates the RoomMap from scratch. [BETA] + /// + internal void EvaluateRoomMap() + { + _wallController.GenerateAll(Architecture); + } + } +} diff --git a/Assets/Scripts/OpenTS2/Scenes/Lot/LotLoad.cs.meta b/Assets/Scripts/OpenTS2/Scenes/Lot/LotLoad.cs.meta new file mode 100644 index 00000000..f1b1675a --- /dev/null +++ b/Assets/Scripts/OpenTS2/Scenes/Lot/LotLoad.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bfcccda4cb67ae846aeffc27284e6ded +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Scenes/Lot/LotTerrainComponent.cs b/Assets/Scripts/OpenTS2/Scenes/Lot/LotTerrainComponent.cs index ca6d0dd9..b5b0800d 100644 --- a/Assets/Scripts/OpenTS2/Scenes/Lot/LotTerrainComponent.cs +++ b/Assets/Scripts/OpenTS2/Scenes/Lot/LotTerrainComponent.cs @@ -47,9 +47,22 @@ public LotTerrainComponent CreateFromLotArchitecture(LotArchitecture architectur BindMaterialAndTextures(); + // Make Mesh Collider for terrain + MakeTerrainCollider(); + return this; } + /// + /// Adds a mesh collider matching the shape of the terrain to the current object + /// + /// + private void MakeTerrainCollider() + { + var comp = _terrain.Component.gameObject.AddComponent(); + comp.isTrigger = false; + } + private (ScenegraphTextureAsset color, ScenegraphTextureAsset bump) LoadTexture(ContentManager contentManager, string name) { var result = ( diff --git a/Assets/Scripts/OpenTS2/Scenes/Lot/LotWallComponent.cs b/Assets/Scripts/OpenTS2/Scenes/Lot/LotWallComponent.cs index 68689b62..8a23a26b 100644 --- a/Assets/Scripts/OpenTS2/Scenes/Lot/LotWallComponent.cs +++ b/Assets/Scripts/OpenTS2/Scenes/Lot/LotWallComponent.cs @@ -19,7 +19,7 @@ public class LotWallComponent : AssetReferenceComponent, IPatternMaterialConfigu private const string ThicknessTexture = "wall_top"; private const string FallbackMaterial = "wall_wallboard"; private const float HalfWallHeight = 1f; - private const float WallHeight = 3f; + public const float WallHeight = 3f; private const float WallsDownHeight = 0.2f; private const float YBias = 0.001f; private const float Thickness = 0.075f; @@ -65,6 +65,7 @@ public WallIntersection(WallGraphPositionEntry position) private LotArchitecture _architecture; private PatternMeshCollection _patterns; + private PatternDescriptor[] _loadedPatternDescriptions; private Dictionary _intersections; @@ -74,16 +75,21 @@ public LotWallComponent CreateFromLotArchitecture(LotArchitecture architecture) { _architecture = architecture; - LoadPatterns(); - BuildWallIntersections(); - BuildWallMeshes(); - AddFencePosts(); + Invalidate(); gameObject.transform.position = new Vector3(0, -YBias, 0); return this; } + public override void Invalidate() + { + LoadPatterns(); + BuildWallIntersections(); + BuildWallMeshes(); + AddFencePosts(); + } + private ScenegraphMaterialDefinitionAsset LoadMaterial(ContentManager contentManager, string name) { var material = contentManager.GetAsset(new ResourceKey($"{name}_txmt", GroupIDs.Scenegraph, TypeIDs.SCENEGRAPH_TXMT)); @@ -104,8 +110,22 @@ private void LoadPatterns() ushort highestId = patternMap.Count == 0 ? (ushort)0 : patternMap.Keys.Max(); PatternDescriptor[] patterns = new PatternDescriptor[highestId + 2]; + bool changesMade = false; + bool previousStateExisting = _loadedPatternDescriptions != null; + foreach (StringMapEntry entry in patternMap.Values) { + //Persistent data -- calls from Invalidate() will call this with data already loaded, in which case + //We can save time by not reloading the same loaded data + // TODO + if (previousStateExisting && _loadedPatternDescriptions.Length == patterns.Length && + _loadedPatternDescriptions[entry.Id + 1].Name != null) // loaded + { // PatternDescriptor is a struct -- check the name see if it's null, if not, the pattern is loaded already + patterns[entry.Id + 1] = _loadedPatternDescriptions[entry.Id + 1]; + continue; + } + changesMade = true; + string materialName; if (uint.TryParse(entry.Value, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint guid)) { @@ -142,11 +162,22 @@ private void LoadPatterns() } } + if (previousStateExisting) + { + if (!changesMade) return; // no changes to wall patterns since last load + + patterns[0] = _loadedPatternDescriptions[0]; + _loadedPatternDescriptions = patterns; + _patterns.UpdatePatterns(patterns); + return; + } + patterns[0] = new PatternDescriptor( ThicknessTexture, LoadMaterial(contentManager, ThicknessTexture).GetAsUnityMaterial() ); + _loadedPatternDescriptions = patterns; _patterns = new PatternMeshCollection(gameObject, patterns, Array.Empty(), this, _architecture.WallGraphAll.Floors); } diff --git a/Assets/Scripts/OpenTS2/Scenes/Lot/PatternMeshCollection.cs b/Assets/Scripts/OpenTS2/Scenes/Lot/PatternMeshCollection.cs index a02c2213..9e43e498 100644 --- a/Assets/Scripts/OpenTS2/Scenes/Lot/PatternMeshCollection.cs +++ b/Assets/Scripts/OpenTS2/Scenes/Lot/PatternMeshCollection.cs @@ -54,6 +54,7 @@ public PatternMeshFloor GetFloor(int floor) public void UpdatePatterns(PatternDescriptor[] patterns) { + _patterns = patterns; foreach (var floor in _floors) { floor?.UpdatePatterns(patterns); diff --git a/Assets/Scripts/OpenTS2/Scenes/Lot/PatternMeshFloor.cs b/Assets/Scripts/OpenTS2/Scenes/Lot/PatternMeshFloor.cs index a000d301..21d97757 100644 --- a/Assets/Scripts/OpenTS2/Scenes/Lot/PatternMeshFloor.cs +++ b/Assets/Scripts/OpenTS2/Scenes/Lot/PatternMeshFloor.cs @@ -95,6 +95,7 @@ public PatternMesh Get(int id, int variantId = 0) result = new PatternMesh(Object, descriptor.Name, descriptor.Material, _matConfig); } + result.Object.AddComponent(); // add collider to floors _patternMeshes[key] = result; } diff --git a/Assets/Scripts/OpenTS2/Scenes/Lot/Room.meta b/Assets/Scripts/OpenTS2/Scenes/Lot/Room.meta new file mode 100644 index 00000000..8c428f6d --- /dev/null +++ b/Assets/Scripts/OpenTS2/Scenes/Lot/Room.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ba84f982724998349b0988abe41c5515 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Scenes/Lot/Room/RuntimeWallController.cs b/Assets/Scripts/OpenTS2/Scenes/Lot/Room/RuntimeWallController.cs new file mode 100644 index 00000000..ffeaae71 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Scenes/Lot/Room/RuntimeWallController.cs @@ -0,0 +1,427 @@ +// JDrocks450 11-22-2023 on GitHub + +using OpenTS2.Content.DBPF; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Unity.Plastic.Newtonsoft.Json; +using UnityEngine; + +namespace OpenTS2.Scenes.Lot +{ + /// + /// BETA implementation. + /// Needs a lot of work to be shippable. + /// Will need to revisit this concept. + /// + internal class RuntimeWallController + { + [Serializable] + public struct GraphPlot + { + public GraphPlot(float x, float y) + { + X = x; + Y = y; + } + public float X { get; set; } + public float Y { get; set; } + } + [Serializable] + public struct GraphLine + { + public GraphLine(int fromID, int toID) + { + FromPlotID = fromID; + ToPlotID = toID; + } + + /// + /// The this line originates + /// + public int FromPlotID { get; set; } + /// + /// The this line ends at + /// + public int ToPlotID { get; set; } + + public override string ToString() => $"({FromPlotID} -> {ToPlotID})"; + } + + /// + /// A map of all positions in the WallGraph + /// For only points that are on line segments, see: + /// + public Dictionary Positions { get; private set; } = new Dictionary(); + /// + /// that are at the corners of two connected line segments + /// + public HashSet JointPositions { get; private set; } = new HashSet(); + /// + /// Lines between that form a straight wall of at least 1 unit length + /// + public Dictionary Lines { get; private set; } = new Dictionary(); + /// + /// Groups of that all form one enclosed shape (Room) on the lineplot + /// + public Dictionary> Rooms { get; private set; } = new Dictionary>(); + + /// + /// A serialized RoomState -- the current state of the walls on the lot + /// + [Serializable] + public class RoomPackage + { + public Dictionary Positions; + public HashSet JointPositions; + public Dictionary Lines; + public Dictionary> Rooms; + public int Width, Height; + } + /// + /// Simple distance formula, calculates the distance in units between two coordinates + /// + /// + /// + /// + double Distance(Vector2 p1, Vector2 p2) + { + return Math.Sqrt(Math.Pow(p1.x - p2.x, 2) + Math.Pow(p1.y - p2.y, 2)); + } + /// + /// Determines if three points create a triangle, can be used to detect straight walls versus joints + /// + /// + /// + /// + /// + bool IsTriangle(Vector2 p1, Vector2 p2, Vector2 p3) + { + // Calculate the lengths of the sides + double a = Distance(p1, p2); + double b = Distance(p2, p3); + double c = Distance(p3, p1); + + // Check if the triangle inequality theorem holds for all sides + return a + b > c && b + c > a && c + a > b; + } + /// + /// Regenerates the room map by discarding the previous data + /// + /// + public void GenerateAll(LotArchitecture architecture) + { + var graph = architecture.WallGraphAll; + Dictionary positionsData = graph.Positions; + WallGraphLineEntry[] wallData = graph.Lines; + + if (!wallData.Any()) return; + if (!positionsData.Any()) return; + + System.Diagnostics.Stopwatch debugTimer = new System.Diagnostics.Stopwatch(); + debugTimer.Start(); + + GenerateLineGraph(positionsData, wallData); + long genLineGraph = debugTimer.ElapsedMilliseconds; + debugTimer.Restart(); + + GenerateRoomMap(); + long genRoomMap = debugTimer.ElapsedMilliseconds; + debugTimer.Restart(); + + PackageRoomMap(graph.Width, graph.Height); + long genPkg = debugTimer.ElapsedMilliseconds; + debugTimer.Stop(); + + long tTime = genLineGraph + genRoomMap + genPkg; + + UnityEngine.Debug.Log($"RoomMap(): Lines took {genLineGraph}ms, Rooms took {genRoomMap}ms, Package took {genPkg}ms" + + $" and altogether took {tTime}ms. "); + //GenerateImage(positionsData, graph.Width, graph.Height); + } + + private void PackageRoomMap(int Width, int Height) + { + //Serialize this to file + using (StringWriter strJson = new StringWriter()) + { + JsonSerializer.CreateDefault().Serialize(strJson, new RoomPackage() + { + JointPositions = JointPositions, + Lines = Lines, + Positions = Positions, + Rooms = Rooms, + Width = Width, + Height = Height + }, typeof(RoomPackage)); + } + } + + /// + /// This function takes the wall graph from package and processes it into a lineplot + /// of only significant points -- as in points that form corners to build shapes. + /// This function will also create the network of lines between the points. + /// + /// + /// + private void GenerateLineGraph(Dictionary positionsData, WallGraphLineEntry[] wallData) + { + //build a map of points that are referenced by lines. + //Position ID to List of LineIDs in WallGraph + Dictionary> PositionIDReferences = new Dictionary>(); + + void AddPositionReference(int PositionID, int LayerID) + { + //Check if the point exists in our wall graph + if (!positionsData.ContainsKey(PositionID)) return; + //position existing + if (PositionIDReferences.TryGetValue(PositionID, out HashSet references)) + references.Add(LayerID); + else + { + PositionIDReferences.Add(PositionID, new HashSet() { LayerID }); + if (!Positions.ContainsKey(PositionID)) + Positions.Add(PositionID, new GraphPlot(positionsData[PositionID].XPos, + positionsData[PositionID].YPos)); + } + } + + //find all points that are referenced by wall lines + foreach (var wall in wallData) + { + AddPositionReference(wall.FromId, wall.LayerId); + AddPositionReference(wall.ToId, wall.LayerId); + } + + //map lines to their layerIds + Dictionary lineMap = new Dictionary(); + foreach (var line in wallData) + lineMap.Add(line.LayerId, line); + + List processed = new List(); + HashSet WallLayerIDsOnLine = new HashSet(); + Lines.Clear(); + + //filter out positions that form straight lines to get a + //lineplot of all significant points -- e.g. the ones that form + //corners + //-- + //This is done by taking two connected wall lines and seeing if all three points + //on these two lines form a triangle with any area -- if they do, it's a bend + foreach (var positionItem in PositionIDReferences) + { + if (positionItem.Value.Count <= 1) // 1 or less connections can never form a wall + continue; // this is due to them being open-ended + else if (positionItem.Value.Count == 2 && processed.Contains(positionItem.Key)) + continue; // only two connections means it's already been processed and cannot be anything more + + int lineFrom = -1, lineTo = -1; + + //tunnelling function -- watch out for stack overflow by using cautiously + //keep scope to only one level on the lot at a time, and this function tree will + //end as soon as a line segment is found + // -- todo: set limit on tunnelling level to be diagonal line from lot corner to corner + void Try(int meID, int tunneled) + { + if (JointPositions.Contains(meID)) + { // already a joint -- don't bother with the detection + lineTo = meID; + return; + } + var references = PositionIDReferences[meID]; // find all wall lines that contain this point + if (references.Count <= 1) goto exit; // none references!! + if (references.Count > 2) + { + JointPositions.Add(meID); + lineTo = meID; + if (tunneled == 0) + return; + } + //very bad -- change ASAP + WallGraphLineEntry line1 = lineMap[references.ElementAt(0)]; + WallGraphLineEntry line2 = lineMap[references.ElementAt(1)]; + int other1 = line1.ToId == meID ? line1.FromId : line1.ToId; // which end isn't the current point? + if (other1 == meID) + goto exit; // catastropic error -- both ends are the same and also this point... how would this happen? + int other2 = line2.ToId == meID ? line2.FromId : line2.ToId; // which end isn't the current point or other point? + if (other2 == meID) + goto exit; // catastropic error -- see above + if (other1 == other2) + goto exit; // catastropic error -- see above + + //form a triangle using all three points + Vector2 pos1 = new Vector2(positionsData[other1].XPos, positionsData[other1].YPos); + Vector2 pos2 = new Vector2(positionsData[other2].XPos, positionsData[other2].YPos); + Vector2 center = new Vector2(positionsData[meID].XPos, positionsData[meID].YPos); + + //check if this is a bend -- has an area + if (!IsTriangle(center, pos1, pos2)) + { + processed.Add(meID); // point is spent + int next = processed.Contains(other2) ? other1 : other2; // pick the point we haven't visited yet + if (lineFrom == -1) + lineFrom = next == other2 ? other1 : other2; // from point is the origin point + Try(next, tunneled + 1); // tunnel until bend is found to complete the segment + return; + } + //found a bend ... close and complete this tunnel + JointPositions.Add(meID); + lineTo = meID; + return; + exit:; + } + + lineFrom = lineTo = -1; + //begin -- walk the current line until it finds a bend + //adds what it finds to Lines if it is indeed a line + //also adds to Joints collection + Try(positionItem.Key, 0); + + if (lineFrom != -1 && lineTo != -1 && lineFrom != lineTo) + Lines.Add(Lines.Count + 1, new GraphLine(lineFrom, lineTo)); + } + } + + /// + /// This function will walk the lineplot created during + /// finding lines that connect to one another to form shapes making up enclosed spaces. + /// + private void GenerateRoomMap() + { + IDictionary> dirtyRooms = new Dictionary>(); + Stack currentlyVisitingRoomPositions = new Stack(); + + foreach (var line in Lines) + { + int findingPositionID = line.Value.FromPlotID; + bool FindRoom(int currentLineID, int fromPositionID) + { + currentlyVisitingRoomPositions.Push(currentLineID); + var lineValue = Lines[currentLineID]; + var nextPoint = lineValue.ToPlotID != fromPositionID ? lineValue.ToPlotID : lineValue.FromPlotID; + if (nextPoint == findingPositionID) + { + //found a room + int[] points = currentlyVisitingRoomPositions.ToArray(); + //ensure room is valid + //AssertRoomValid(points); + //does this room already exist -- also calls AssertRoomValid() + if (!RoomExisting(-1, dirtyRooms, points)) + dirtyRooms.Add(dirtyRooms.Count + 1, points); // doesn't, add it + return true; + } + var results = Lines.Where(x => x.Value.FromPlotID == nextPoint || x.Value.ToPlotID == nextPoint); + int nextLineID = -1; + foreach (var result in results) + { + if (result.Key == currentLineID) continue; + nextLineID = result.Key; + } + if (nextLineID == -1) return false; + return FindRoom(nextLineID, nextPoint); + } + currentlyVisitingRoomPositions.Clear(); + FindRoom(line.Key, findingPositionID); + } + + Rooms.Clear(); + foreach(var item in dirtyRooms) + Rooms.Add(item.Key, item.Value); + } + + /// + /// Assures the supplied set of points can make a valid room. + /// + /// + /// + /// + public void AssertRoomValid(params int[] RoomPoints) + { + if (RoomPoints == default) + throw new NullReferenceException("RoomPoints passed as null reference."); + if (RoomPoints.Length < 4) + throw new InvalidDataException($"Rooms must have at least four points. This one supplied has {RoomPoints.Length}"); + } + + /// + /// Tests to see if the given room exists in + /// Supplying the RoomID will ensure the current room isn't compared to itself in the map. + /// It is determined to be the same room if all the points supplied match all the points on a given + /// Room in the room map. + /// + /// + /// + /// + public bool RoomExisting(int RoomID, params int[] RoomPoints) => RoomExisting(RoomID, Rooms, RoomPoints); + + /// + /// Tests to see if the given room exists in + /// Supplying the RoomID will ensure the current room isn't compared to itself in the map. + /// It is determined to be the same room if all the points supplied match all the points on a given + /// Room in the room map. + /// + /// + /// + /// + public bool RoomExisting(int RoomID, IDictionary> RoomCollection, params int[] RoomPoints) + { + AssertRoomValid(RoomPoints); + bool duplicate = false; + foreach (var otherRoom in RoomCollection) + { + if (otherRoom.Key == RoomID) + continue; // same as the room I'm comparing + if (otherRoom.Value.Count() != RoomPoints.Length) + continue; // not the same number of points + foreach (var point in otherRoom.Value) + { + duplicate = RoomPoints.Contains(point); + if (!duplicate) break; + } + if (duplicate) break; + } + return duplicate; + } + + public void GenerateImage(string exportFileName, Dictionary positionsData, int Width, int Height) + { + Texture2D roomMap = new Texture2D(Width, Height); + + Color[] colors = new Color[] { + Color.green, + Color.cyan, + Color.magenta, + Color.grey, + Color.yellow, + Color.blue + }; + + foreach (var drawLine in Lines.Values) + { + var position = positionsData[drawLine.FromPlotID]; + roomMap.SetPixel((int)position.XPos, (int)position.YPos, Color.red); + position = positionsData[drawLine.ToPlotID]; + roomMap.SetPixel((int)position.XPos, (int)position.YPos, Color.red); + } + + int index = -1; + foreach (var room in Rooms) + { + index++; + + Color drawColor = colors[index]; + foreach (var lineID in room.Value) + { + var drawLine = Lines[lineID]; + var position = positionsData[drawLine.FromPlotID]; + roomMap.SetPixel((int)position.XPos, (int)position.YPos, drawColor); + position = positionsData[drawLine.ToPlotID]; + roomMap.SetPixel((int)position.XPos, (int)position.YPos, drawColor); + } + } + + File.WriteAllBytes(exportFileName, roomMap.EncodeToPNG()); + } + } +} diff --git a/Assets/Scripts/OpenTS2/Scenes/Lot/Room/RuntimeWallController.cs.meta b/Assets/Scripts/OpenTS2/Scenes/Lot/Room/RuntimeWallController.cs.meta new file mode 100644 index 00000000..eae74992 --- /dev/null +++ b/Assets/Scripts/OpenTS2/Scenes/Lot/Room/RuntimeWallController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 989c8164467e1524d9013a6579681d65 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/Scenes/Lot/State/WorldState.cs b/Assets/Scripts/OpenTS2/Scenes/Lot/State/WorldState.cs index 6ba48144..dc6770ea 100644 --- a/Assets/Scripts/OpenTS2/Scenes/Lot/State/WorldState.cs +++ b/Assets/Scripts/OpenTS2/Scenes/Lot/State/WorldState.cs @@ -18,5 +18,10 @@ public WorldState(int level, WallsMode walls) Level = level; Walls = walls; } + + public override string ToString() + { + return $"Walls: {Walls}, Level: {Level}"; + } } } \ No newline at end of file diff --git a/Assets/Scripts/OpenTS2/UI/Layouts/Lot.meta b/Assets/Scripts/OpenTS2/UI/Layouts/Lot.meta new file mode 100644 index 00000000..da027278 --- /dev/null +++ b/Assets/Scripts/OpenTS2/UI/Layouts/Lot.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 36c3b264b4416d742b336bab949d1995 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewBuildModeHUD.cs b/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewBuildModeHUD.cs new file mode 100644 index 00000000..7e2ed723 --- /dev/null +++ b/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewBuildModeHUD.cs @@ -0,0 +1,631 @@ +using OpenTS2.Common; +using OpenTS2.Content; +using OpenTS2.Content.DBPF; +using OpenTS2.Content.DBPF.Scenegraph; +using OpenTS2.Engine.Modes.Build; +using OpenTS2.Engine.Modes.Build.Tools; +using OpenTS2.Files.Formats.DBPF; +using OpenTS2.Files.Formats.XML; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using UnityEngine.UI; + +namespace OpenTS2.UI.Layouts.Lot +{ + /// + /// This is a sub-control of the with functionality serving the Build Mode menus in the Lot View UI puck + /// + public class LotViewBuildModeHUD : UILayoutInstance + { + class BuildHUD_CatalogItemComponent : UILayoutInstance + { + //PRIVATE + const uint BaseID = 0xDAFE6503; + const uint PreviewID = 0x00000001; + private CatalogObjectAsset item; + public CatalogObjectAsset SelectedItem => item; + + Material displayMaterial; + + //PROTECTED + protected override ResourceKey UILayoutResourceKey => new ResourceKey(BaseID, 0xA99D8A11, TypeIDs.UI); + + //PUBLIC + public event EventHandler Clicked; + + public BuildHUD_CatalogItemComponent(Transform parent) : base(parent) + { + Init(); + } + + void Init() + { + Components[0].GetChildByID(0x03).OnClick += delegate + { + Clicked?.Invoke(this, SelectedItem); + }; + } + + /// + /// Changes this component's displayed texture and attached catalog asset + /// + /// + public void Display(CatalogObjectAsset Asset) + { + if(displayMaterial != null) + { + UnityEngine.Object.Destroy(displayMaterial); + displayMaterial = null; + } + + item = Asset; + + var content = ContentManager.Instance; + var material = content.GetAsset( + new ResourceKey($"{Asset.Material}_txmt", GroupIDs.Scenegraph, TypeIDs.SCENEGRAPH_TXMT)); + + var preview = Components[0].GetChildByID(PreviewID); + preview.RectTransformComponent.localPosition = new Vector3(8, -8); + preview.RectTransformComponent.sizeDelta = new Vector2(34,34); + preview.gameObject.SetActive(true); + + //Set preview image + var bmp = preview.GetComponent(); + displayMaterial = material.GetAsUnityMaterial(); + bmp.texture = displayMaterial.mainTexture; + } + } + + /// + /// Contains the state of the catelog UI flyout + /// + class BuildHUD_CatalogComponent + { + const int XMARGIN = 10, YMARGIN = 5; // Margins between catalog items in the menu + internal const uint CatalogItemsExtender = 0xAD502BBF; + const uint CatalogItemsComponent = 0x00001006; + const uint PreviousButton = 0x00001004; + const uint NextButton = 0x00001005; + + UIComponent rootElement; + internal bool opened { get; private set; } + + private string subsort = "all"; + int itemsPerPage = 6, currentPage = 1; + + List items = new List(); + /// + /// The list of reusable UIComponent instances that will dynamically update their previews depending on which page we're on + /// + List controls = new List(); + + public BuildHUD_CatalogComponent(UIComponent CatalogComponent) + { + rootElement = CatalogComponent; + + Init(); + Close(); + } + + void Init() + { + var catalogItemsComponent = rootElement.GetChildByID(CatalogItemsComponent); + catalogItemsComponent.gameObject.SetActive(true); + catalogItemsComponent.GetComponent().enabled = false; // background is buggy turn it off + + for(int item = 0; item < itemsPerPage; item++) // create dynamic items + { + int x = item / 2; + int y = item - (x * 2); + + var control = new BuildHUD_CatalogItemComponent(catalogItemsComponent.transform); + var ctrlTransform = control.Components[0].RectTransformComponent; + ctrlTransform.localPosition = new Vector3( // handle padding here + (ctrlTransform.sizeDelta.x + XMARGIN) * x, -(ctrlTransform.sizeDelta.y + YMARGIN) * y); + controls.Add(control); + + //CLICK EVENT + control.Clicked += CatalogItemSelected; + } + + //Rig up next and prev buttons + var prevButton = rootElement.GetChildByID(PreviousButton); + prevButton.OnClick += Previous; + var nextButton = rootElement.GetChildByID(NextButton); + nextButton.OnClick += Advance; + } + + private void CatalogItemSelected(object sender, CatalogObjectAsset e) + { + //If the current build tool is compatible with patterns -- invoke it + (BuildModeServer.Get().CurrentTool as IPatternSelectableBuildTool)?.SetPaintPattern(e.Material); + } + + void Advance() => SwitchPage(currentPage + 1); + void Previous() => SwitchPage(currentPage - 1); + /// + /// Switches to the given page in the catalog by updating the dynamic controls to the new page's contents + /// + /// + void SwitchPage(int PageNumber) + { + void EnableButtons() + { + var button = rootElement.GetChildByID(PreviousButton); + button.gameObject.SetActive(true); + button = rootElement.GetChildByID(NextButton); + button.gameObject.SetActive(true); + } + void DisableForward() + { + var button = rootElement.GetChildByID(NextButton); + button.gameObject.SetActive(false); + } + void DisableBackward() + { + var button = rootElement.GetChildByID(PreviousButton); + button.gameObject.SetActive(false); + } + + EnableButtons(); // any buttons that are disabled should be re-enabled for safety. + + currentPage = PageNumber; + if (currentPage <= 0) + { + currentPage = 0; + DisableBackward(); // first page, disable back button + } + + int startIndex = currentPage * itemsPerPage; + + var itemsFilter = (IEnumerable)items; + if (subsort == "ots2_hiddentiles") + { + itemsFilter = items.Where(x => ((Uint32Prop)x.Properties.Properties["showincatalog"]).Value == 0); + } + else if (subsort != "all") // filter the itemssource if applicable (ignore this filter if "all" is selected) + itemsFilter = items.Where(x => ((StringProp)x.Properties.Properties["subsort"]).Value == subsort); + + for (int item = 0; item < itemsPerPage; item++) + { + //Set buttons hidden at first until some content to show has been found + var control = controls[item]; + control.Components[0].gameObject.SetActive(false); + + //attempt to load the content item + var assetRef = itemsFilter.ElementAtOrDefault(startIndex + item); + if (assetRef == null) continue; // no content here + //found some content, make the button visible and display a preview of the catalog item + control.Components[0].gameObject.SetActive(true); + control.Display(assetRef); + } + + if (startIndex + itemsPerPage >= itemsFilter.Count()) // end of last page + DisableForward(); // disable forward button + } + + public void Close() + { + rootElement.gameObject.SetActive(false); + opened = false; + } + public void Open(string CatelogItemType, string Subsort = "all") + { + if (string.IsNullOrWhiteSpace(Subsort)) + Subsort = "all"; + + rootElement.gameObject.SetActive(true); + opened = true; + + //Update the selection of items to match the definition + subsort = Subsort; + UpdateItemsSource(CatalogManager.Instance.Objects.Where(x => x.Type == CatelogItemType)); + } + public void SetLocalPosition(Vector3 Position) => + rootElement.RectTransformComponent.localPosition = Position; + + /// + /// Updates the catalog control's items source property. + /// Use this when changing the selection of items being shown in the catelog. + /// You do not need to account for multiple pages of items, that will be handled automatically by the control. + /// + void UpdateItemsSource(IEnumerable CatalogItems) + { + items.Clear(); + items.AddRange(CatalogItems); + + SwitchPage(0); // switch to page one of the new data + } + + /// + /// Sets a filter on the ItemsSource. + /// See: and + /// + /// + /// + internal void SetSubsort(string Subsort) + { + subsort = Subsort; + SwitchPage(0); + } + } + + #region TopLevelHUDElements + + /// + /// Sorts (pages) for different tools in the build mode hud + /// + enum BuildHUD_Sorts : uint + { + TopLevel = 0x0000A000, + Walls = 0x0000B100, + Foundations = 0x0000B200, + DoorsWindowsArches = 0x0000B300, + Floors = 0x0000B400, + WallPaints = 0x0000B500, + Stairs = 0x0000B600, + Terrain = 0x0000B700, + GardenCenter = 0x0000B800, + Roofs = 0x0000B900, + Garages = 0x0000BB00, + Architecture = 0x0000BC00, + Misc = 0x0000BA00 + } + enum BuildHUD_CollectionPages : uint + { + Normal = 0x000000A8 + } + enum BuildHUD_BasicToolButtons : uint + { + Hand = 0x000000A5, + EyedropperTool = 0x000000A4 + } + + #endregion + + #region Subsorts + + enum BuildHUD_TopLevelButtons : uint + { + TopLevelSortButton = 0x000000A6, + WallsSortButton = 0x0000A100, + FoundationsSortButton = 0x0000A200, + DoorsAndWindowsSortButton = 0x0000A300, + FloorsSortButton = 0x0000A400, + WallPaintsSortButton = 0x0000A500, + StairsSortButton = 0x0000A600, + TerrainCenterButton = 0x0000A700, + GardenCenterButton = 0x0000A800, + RoofsSortButton = 0x0000A900, + MiscButton = 0x0000AA00, + GarageButton = 0x0000AB00, + ArchitectureSortButton = 0x0000AC00, + } + enum BuildHUD_WallsSortButtons : uint + { + WallsNRooms = 0x0000B120, + HalfWalls = 0x0000B130 + } + enum BuildHUD_TerrainSortButtons : uint + { + ElevationTools = 0x0000B720, + TerrainPaints = 0x0000B730, // called 'grass' in UI, pretty cool :0 + Water = 0x0000B740, + FlattenLot = 0x000000AD, + } + enum BuildHUD_TerrainBrushSizeButtons : uint + { + SmallSizeButton = 0x0000B751, + MedSizeButton = 0x0000B752, + LgSizeButton = 0x0000B753, + } + enum BuildHUD_FloorsSubsortButtons : uint + { + StoneButton = 0x0000B420, + BrickButton = 0x0000B430, + WoodButton = 0x0000B440, + CarpetButton = 0x0000B450, + TileButton = 0x0000B460, + LinoleumButton = 0x0000B470, + PouredButton = 0x0000B480, + OtherButton = 0x0000B490, + AllButton = 0x0000B4A0, + } + + #endregion + + const uint BaseID = 0x49060003; + const uint TileBG = 0xB4; // UI Background Panel + const uint EndCap = 0x000000B5; // UI Curvy End Cap + const uint ProdCatelogFlyout = 0x49063004; + const uint AutoRoofOptionsExtender = 0x000000AC; + const uint TerrainPageSizesGroup = 0x0000B750; + + protected override ResourceKey UILayoutResourceKey => new ResourceKey(BaseID, 0xA99D8A11, TypeIDs.UI); + UIComponent rootElement => Components[0]; + + //Pages (Sorts) + + /// + /// Contains information for each sort page in build mode to display it correctly + /// + class BuildHUDSort + { + /// + /// The enum type that has the IDs for the buttons that are inside of this sort that should be Toggled/Untoggled + /// after the sort is opened and dismissed + /// + public Type ButtonIDsEnum { get; set; } = null; + /// + /// The UI ID for this sort + /// + public uint SortGroupID { get; set; } + + //CATALOG PROVIDER + public string CatalogItemType { get; set; } + public bool HasCatalogItems => CatalogItemType != null; + public string CatalogDefaultSubsort { get; set; } = "all"; + + public BuildHUDSort(Type buttonIDsEnum, uint sortGroupID) + { + ButtonIDsEnum = buttonIDsEnum; + SortGroupID = sortGroupID; + } + } + + BuildHUDSort CurrentSort; + /// + /// Links Sort buttons to their corresponding pages in the interface + /// Wall Button links to the Wall Subpage with Half Walls, Regular Walls, Diagonal Room, etc. + /// + Dictionary sortPageMap = new Dictionary() + { + { BuildHUD_Sorts.TopLevel, new BuildHUDSort(typeof(BuildHUD_TopLevelButtons), (uint)BuildHUD_Sorts.TopLevel) }, + { BuildHUD_Sorts.Walls, new BuildHUDSort(typeof(BuildHUD_WallsSortButtons), (uint)BuildHUD_Sorts.Walls) }, + { BuildHUD_Sorts.Terrain, new BuildHUDSort(typeof(BuildHUD_TerrainSortButtons), (uint)BuildHUD_Sorts.Terrain) }, + { BuildHUD_Sorts.Floors, new BuildHUDSort(typeof(BuildHUD_FloorsSubsortButtons), (uint)BuildHUD_Sorts.Floors) + { // activate the catelog page for this sort + CatalogItemType = CatalogObjectType.Floor, + } + }, + }; + + // UI buttons and event system + Dictionary buildHUDButtonCallbacks; // built at runtime + /// + /// All UI buttons added to the event system (See: ) will set this value to + /// themselves prior to the event being called (May change later) + /// + UIButtonComponent CurrentButtonEventContext; + BuildHUD_CatalogComponent Catalog; + + public bool CatalogOpen => Catalog.opened; + + public LotViewBuildModeHUD() : this(MainCanvas) + { + + } + public LotViewBuildModeHUD(Transform Canvas) : base(Canvas) + { + Init(); + BuildUIEventsMap(); + WireUpUIEvents(); + } + + void Init() + { + //Position in bot left + var root = Components[0]; + root.SetAnchor(UIComponent.AnchorType.BottomLeft); + root.transform.position = new Vector3(105, root.transform.position.y, 0); + + //fix sizing + var iconButton = root.GetChildByID((uint)BuildHUD_BasicToolButtons.Hand); + iconButton.RectTransformComponent.sizeDelta = new Vector2(30, 30); + iconButton = root.GetChildByID((uint)BuildHUD_BasicToolButtons.EyedropperTool); + iconButton.RectTransformComponent.sizeDelta = new Vector2(30, 30); + + //turn off all controls except necessary ones + var autoRoofOptions = root.GetChildByID(AutoRoofOptionsExtender); + autoRoofOptions.gameObject.SetActive(false); + Catalog = new BuildHUD_CatalogComponent(root.GetChildByID(BuildHUD_CatalogComponent.CatalogItemsExtender)); + } + + void BuildUIEventsMap() + { + buildHUDButtonCallbacks = new Dictionary() + { + // BASIC BUTTONS + { (uint)BuildHUD_BasicToolButtons.Hand, delegate { ActionChangeTool(BuildTools.Hand); } }, + + // TOP LEVEL SORTS BUTTONS + { (uint)BuildHUD_TopLevelButtons.TopLevelSortButton, delegate { ActionChangeSort(BuildHUD_Sorts.TopLevel); } }, + { (uint)BuildHUD_TopLevelButtons.WallsSortButton, delegate { ActionChangeSort(BuildHUD_Sorts.Walls); } }, + { (uint)BuildHUD_TopLevelButtons.DoorsAndWindowsSortButton, delegate { ActionChangeSort(BuildHUD_Sorts.DoorsWindowsArches); } }, + { (uint)BuildHUD_TopLevelButtons.FloorsSortButton, delegate + { + ActionChangeSort(BuildHUD_Sorts.Floors); + ActionChangeTool(BuildTools.Floor); + } + }, + { (uint)BuildHUD_TopLevelButtons.WallPaintsSortButton, delegate { ActionChangeSort(BuildHUD_Sorts.WallPaints); } }, + { (uint)BuildHUD_TopLevelButtons.ArchitectureSortButton, delegate { ActionChangeSort(BuildHUD_Sorts.Architecture); } }, + { (uint)BuildHUD_TopLevelButtons.GardenCenterButton, delegate { ActionChangeSort(BuildHUD_Sorts.GardenCenter); } }, + { (uint)BuildHUD_TopLevelButtons.TerrainCenterButton, delegate { ActionChangeSort(BuildHUD_Sorts.Terrain); } }, + { (uint)BuildHUD_TopLevelButtons.GarageButton, delegate { ActionChangeSort(BuildHUD_Sorts.Garages); } }, + { (uint)BuildHUD_TopLevelButtons.MiscButton, delegate { ActionChangeSort(BuildHUD_Sorts.Misc); } }, + { (uint)BuildHUD_TopLevelButtons.RoofsSortButton, delegate { ActionChangeSort(BuildHUD_Sorts.Roofs); } }, + { (uint)BuildHUD_TopLevelButtons.FoundationsSortButton, delegate { + ActionChangeSort(BuildHUD_Sorts.Foundations); + ActionChangeTool(BuildTools.Foundation); + } + }, + { (uint)BuildHUD_TopLevelButtons.StairsSortButton, delegate { ActionChangeSort(BuildHUD_Sorts.Stairs); } }, + + // Walls N' Rooms + { (uint)BuildHUD_WallsSortButtons.WallsNRooms, delegate { ActionChangeTool(BuildTools.Wall); } }, + + // Terrain + { (uint)BuildHUD_TerrainSortButtons.ElevationTools, delegate { ActionSetTerrainBrushTool(BuildModeServer.TerrainModificationModes.Raise); } }, + { (uint)BuildHUD_TerrainSortButtons.Water, delegate { ActionSetTerrainBrushTool(BuildModeServer.TerrainModificationModes.Water); } }, + { (uint)BuildHUD_TerrainSortButtons.FlattenLot, delegate { BuildModeServer.Get().FlattenLot(); } }, + { (uint)BuildHUD_TerrainBrushSizeButtons.SmallSizeButton, delegate { ActionModifyTerrainBrushSize(0); } }, + { (uint)BuildHUD_TerrainBrushSizeButtons.MedSizeButton, delegate { ActionModifyTerrainBrushSize(TerrainBrushTool.TerrainBrushSizes.Medium); } }, + { (uint)BuildHUD_TerrainBrushSizeButtons.LgSizeButton, delegate { ActionModifyTerrainBrushSize(TerrainBrushTool.TerrainBrushSizes.Large); } }, + + //Floors + { (uint)BuildHUD_FloorsSubsortButtons.StoneButton, delegate { ActionInvokeSubSort("stone"); } }, + { (uint)BuildHUD_FloorsSubsortButtons.BrickButton, delegate { ActionInvokeSubSort("brick"); } }, + { (uint)BuildHUD_FloorsSubsortButtons.WoodButton, delegate { ActionInvokeSubSort("wood"); } }, + { (uint)BuildHUD_FloorsSubsortButtons.PouredButton, delegate { ActionInvokeSubSort("poured"); } }, + { (uint)BuildHUD_FloorsSubsortButtons.CarpetButton, delegate { ActionInvokeSubSort("carpet"); } }, + { (uint)BuildHUD_FloorsSubsortButtons.OtherButton, delegate { ActionInvokeSubSort("other"); } }, + { (uint)BuildHUD_FloorsSubsortButtons.AllButton, delegate { ActionInvokeSubSort("all"); } }, + { (uint)BuildHUD_FloorsSubsortButtons.TileButton, delegate { ActionInvokeSubSort("tile"); } }, + { (uint)BuildHUD_FloorsSubsortButtons.LinoleumButton, delegate { ActionInvokeSubSort("lino"); } }, + }; + } + + void WireUpUIEvents() + { + foreach(var keyValuePair in buildHUDButtonCallbacks) + { + uint compID = keyValuePair.Key; + var button = rootElement.GetChildByID(compID); + if (!button) + { + Debug.Log($"No button for 0x{compID:X}"); + continue; + } + button.OnClick += delegate + { + //Set context + CurrentButtonEventContext = button; + //Invoke event + keyValuePair.Value(); + //Remove context + CurrentButtonEventContext = null; + }; + } + } + void EnsureGroupToggled(Type EnumTypeGroup, bool ToggleState = false) => EnsureGroup(EnumTypeGroup, (UIButtonComponent comp) => comp.SetToggled(ToggleState)); + void EnsureGroupHidden(Type EnumUIGroup) => EnsureGroup(EnumUIGroup, (UIComponent comp) => comp.gameObject.SetActive(false)); + void EnsureGroup(Type EnumUIGroup, Action Modification) => EnsureGroup(EnumUIGroup, Modification); + void EnsureGroup(Type EnumUIGroup, Action Modification) where T : UIComponent + { + if (EnumUIGroup == null) return; + foreach (uint groupID in Enum.GetValues(EnumUIGroup)) + Modification(rootElement.GetChildByID(groupID)); + } + void CloseCatalog() + { + Catalog.Close(); + //Set the endcap visible again + rootElement.GetChildByID(EndCap).gameObject.SetActive(true); + } + void OpenCatalog(string ObjectType, string Subsort = "all") + { + Catalog.Open(ObjectType, Subsort); + //Set the endcap invisible + rootElement.GetChildByID(EndCap).gameObject.SetActive(false); + } + + void ActionChangeTool(BuildTools Tool) + { + //Untoggle all basic buttons before transitioning + EnsureGroupToggled(typeof(BuildHUD_BasicToolButtons)); + if (CurrentSort != default) + EnsureGroupToggled(CurrentSort.ButtonIDsEnum); + //set tool button toggled + if (CurrentButtonEventContext != default) + CurrentButtonEventContext.SetToggled(true); + + BuildModeServer.Get().ChangeTool(Tool); + } + /// + /// Updates the User Interface to show the selected sort + /// See: to change the Subsort for this Sort + /// Note: This will cause whatever tool is currently being used to be cancelled. + /// + /// + void ActionChangeSort(BuildHUD_Sorts SortPage) + { + ActionChangeTool(BuildTools.None); // drop current tool + + //Hide all other pages before transitioning + EnsureGroupHidden(typeof(BuildHUD_Sorts)); + CloseCatalog(); + + var sortRoot = rootElement.GetChildByID((uint)SortPage); + sortRoot.gameObject.SetActive(true); + // stretch the ui panel to accomodate for the page we just opened up + float desiredWidth = sortRoot.RectTransformComponent.sizeDelta.x; + var TileBGComponent = rootElement.GetChildByID(TileBG).RectTransformComponent; // UI background panel + TileBGComponent.sizeDelta = new Vector2(desiredWidth, TileBGComponent.sizeDelta.y); + //move the endcap over so there's no gaps + var EndCapComp = rootElement.GetChildByID(EndCap).RectTransformComponent; // UI background panel + EndCapComp.localPosition = TileBGComponent.localPosition + new Vector3(desiredWidth, 0, 0); + //move the catelog as well -- whether it is active or not + Catalog.SetLocalPosition(new Vector3(TileBGComponent.localPosition.x + desiredWidth, 0, 0)); + //catelogComp.sizeDelta = new Vector2(catelogComp.position.x - (MainCanvas.transform as RectTransform).sizeDelta.x - 10, + // catelogComp.sizeDelta.y); + + if (sortPageMap.TryGetValue(SortPage, out var map)) + CurrentSort = map; + //Untoggle all buttons in the destination sort as well + EnsureGroupToggled(CurrentSort.ButtonIDsEnum); + + //open the catelog (if applicable) + if (CurrentSort.HasCatalogItems) + OpenCatalog(CurrentSort.CatalogItemType, CurrentSort.CatalogDefaultSubsort); + } + + /// + /// Sorts such as Flooring have subsorts (carpet, stone, etc.), this function will update the + /// subsort for the + /// + /// + void ActionInvokeSubSort(string Subsort) + { + EnsureGroupToggled(CurrentSort.ButtonIDsEnum); + if (Subsort == "all" && Input.GetKey(KeyCode.LeftShift)) // BuildDEBUG + Subsort = "ots2_hiddentiles"; + Catalog.SetSubsort(Subsort); + CurrentButtonEventContext.SetToggled(true); + } + /// + /// Invokes the Terrain brush tool and sets the size to Small by default + /// + /// + void ActionSetTerrainBrushTool(BuildModeServer.TerrainModificationModes BrushMode) + { + ActionChangeTool(BuildTools.TerrainBrush); + (BuildModeServer.Get().CurrentTool as TerrainBrushTool).CurrentBrushMode = BrushMode; + ActionModifyTerrainBrushSize(TerrainBrushTool.TerrainBrushSizes.Small); + } + /// + /// Updates the User Interface and the Terrain tool to match the selected size + /// + /// + void ActionModifyTerrainBrushSize(TerrainBrushTool.TerrainBrushSizes Size) + { + EnsureGroupToggled(typeof(BuildHUD_TerrainBrushSizeButtons)); + UIButtonComponent button = rootElement.GetChildByID((uint)BuildHUD_TerrainBrushSizeButtons.SmallSizeButton); + switch (Size) + { + case TerrainBrushTool.TerrainBrushSizes.Medium: // med + button = rootElement.GetChildByID((uint)BuildHUD_TerrainBrushSizeButtons.MedSizeButton); + break; + case TerrainBrushTool.TerrainBrushSizes.Large: // lg + button = rootElement.GetChildByID((uint)BuildHUD_TerrainBrushSizeButtons.LgSizeButton); + break; + default: + Size = TerrainBrushTool.TerrainBrushSizes.Small; break; + } + button.SetToggled(true); + (BuildModeServer.Get().CurrentTool as TerrainBrushTool).BrushSize = (int)Size; + } + } +} diff --git a/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewBuildModeHUD.cs.meta b/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewBuildModeHUD.cs.meta new file mode 100644 index 00000000..7615775c --- /dev/null +++ b/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewBuildModeHUD.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 81a357d2aeaa898479699bb2890f9123 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewHUD.cs b/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewHUD.cs new file mode 100644 index 00000000..29b0337a --- /dev/null +++ b/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewHUD.cs @@ -0,0 +1,182 @@ +using OpenTS2.Common; +using OpenTS2.Engine.Modes.Build; +using OpenTS2.Files.Formats.DBPF; +using OpenTS2.Scenes.Lot.State; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace OpenTS2.UI.Layouts +{ + + /// + /// The HUD found when the user is playing in a lot + /// + public class LotViewHUD : UILayoutInstance + { + //CONSTANTS + public enum HUD_UILotModes + { + None, + Live, + Buy, + Build, + Memories, + Settings + } + + const uint WallViewSelectorGadgetID = 0x00004000; + /// + /// probably not necessary since it's the only control in this UIData but im gonna roll w it + /// + const uint LotViewPuckID = 0xF15C6B85; + //Wall View button that invokes the Wall View Selector has 4 states for each wall view mode + const uint FoldButton_WallsDown = 0x00001100, FoldButton_WallsCutaway = 0x00001101, + FoldButton_WallsUp = 0x00001102, FoldButton_Roof = 0x00001103; + //Buttons inside the Wall View selector that change the Wall View Mode + const uint Button_WallsDown = 0x00000001, Button_WallsCutaway = 0x00000002, + Button_WallsUp = 0x00000003, Button_WallsRoof = 0x00000004; + const uint BuildButton = 0x77D97B28, BuyButton = 0x67D97B2A, LiveButton = 0x37D97B25, SettingsButton = 0x67D97B2F; + const uint FloorUpButton = 0x00002001, FloorDownButton = 0x00002000; + + //PRIVATE + UIComponent WallsUpDownSelector; + UIComponent rootElement => Components[0]; + + HUD_UILotModes CurrentMode = HUD_UILotModes.None; + + //EVENTS + public class ModeChangeEventArgs + { + public bool Allow = false; + HUD_UILotModes RequestedMode; + + public ModeChangeEventArgs(HUD_UILotModes requestedMode) + { + RequestedMode = requestedMode; + } + } + public event EventHandler OnModeChangeRequested; + public event EventHandler OnModeChanged; + + protected override ResourceKey UILayoutResourceKey => new ResourceKey(0x4905F85B, 0xA99D8A11, TypeIDs.UI); + public LotViewHUD() : this(MainCanvas) + { + + } + public LotViewHUD(Transform Canvas) : base(Canvas) + { + Init(); + WireUpUIEvents(); + } + + void Toggle(UIComponent comp) => comp.transform.gameObject.SetActive(!comp.transform.gameObject.activeSelf); + + /// + /// Sets the hud elements to default settings for first run + /// + void Init() + { + var root = Components[0]; + root.SetAnchor(UIComponent.AnchorType.Stretch); + var puck = root.GetChildByID(LotViewPuckID); + puck.SetAnchor(UIComponent.AnchorType.BottomLeft); // set gizmo to botleft + puck.transform.position = new Vector3(0, 200, 0); + + WallsUpDownSelector = root.GetChildByID(WallViewSelectorGadgetID, false); + Toggle(WallsUpDownSelector); + } + + /// + /// Sets up all button click event messages + /// + void WireUpUIEvents() + { + void RigUpSequential(uint Base, int count, Action Callback) + { + if (count < 0) return; // :| + if (Base + count > uint.MaxValue) throw new Exception($"UI Element ID overflow in WireUpUIEvents RigUpSeq method! {Base} + {count}"); + for (int i = 0; i < count; i++) + { + var button = rootElement.GetChildByID((uint)(Base + i)); // downcast doesn't matter per above + button.OnClick += Callback; + } + } + //Rig up Wall View Mode tray thing + RigUpSequential(FoldButton_WallsDown, 4, ShowHideWallViewOptions); + var modeButton = rootElement.GetChildByID(BuildButton); + modeButton.OnClick += delegate { SetMode(HUD_UILotModes.Build); }; // build mode button clicked + + //Wall view modes buttons + var wallButton = rootElement.GetChildByID(Button_WallsDown); + wallButton.OnClick += delegate { SetWallsCutawayMode(WallsMode.Down); }; + wallButton = rootElement.GetChildByID(Button_WallsCutaway); + wallButton.OnClick += delegate { SetWallsCutawayMode(WallsMode.Cutaway); }; + wallButton = rootElement.GetChildByID(Button_WallsUp); + wallButton.OnClick += delegate { SetWallsCutawayMode(WallsMode.Up); }; + wallButton = rootElement.GetChildByID(Button_WallsRoof); + wallButton.OnClick += delegate { SetWallsCutawayMode(WallsMode.Roof); }; + + //floor buttons + var floorButton = rootElement.GetChildByID(FloorUpButton); + floorButton.OnClick += delegate { SetFloorLevel(1); }; + floorButton = rootElement.GetChildByID(FloorDownButton); + floorButton.OnClick += delegate { SetFloorLevel(-1); }; + } + + void ShowHideWallViewOptions() + { + Toggle(WallsUpDownSelector); + } + + void SetWallsCutawayMode(WallsMode Mode) + { + var server = BuildModeServer.Get(); + var state = server.WorldState; + server.WorldState = new WorldState(state.Level, Mode); + server.InvalidateLotState(); + ShowHideWallViewOptions(); + } + + void SetFloorLevel(int Change) + { + var server = BuildModeServer.Get(); + var state = server.WorldState; + int level = Math.Max(0,state.Level + Change); // TODO: Make this base floor value... + server.WorldState = new WorldState(level, state.Walls); + server.InvalidateLotState(); + } + + /// + /// Sets the mode shown in the HUD + /// Note: If there are no subscribers to , the mode change will always be successful without verification. + /// + /// + public void SetMode(HUD_UILotModes Mode) + { + if (OnModeChangeRequested != null) + { + var args = new ModeChangeEventArgs(Mode); + OnModeChangeRequested(this, args); + if (!args.Allow) return; + } + //Change allowed + + CurrentMode = Mode; + var buildModeButton = rootElement.GetChildByID(BuildButton); + buildModeButton.SetToggled(false); + + switch(Mode) + { + case HUD_UILotModes.Build: + buildModeButton.SetToggled(true); + break; + } + + OnModeChanged?.Invoke(this, Mode); + } + } +} diff --git a/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewHUD.cs.meta b/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewHUD.cs.meta new file mode 100644 index 00000000..b9dc79d2 --- /dev/null +++ b/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewHUD.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5b5d074308e95ce42b13b0c46dd9eaed +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewHUDController.cs b/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewHUDController.cs new file mode 100644 index 00000000..278e11aa --- /dev/null +++ b/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewHUDController.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; + +namespace OpenTS2.UI.Layouts.Lot +{ + public class LotViewHUDController : MonoBehaviour + { + //UI Parts + public LotViewBuildModeHUD BuildModePanel { get; private set; } + public LotViewHUD Puck { get; private set; } + + void Start() + { + BuildModePanel = new LotViewBuildModeHUD(); + Puck = new LotViewHUD(); + Puck.OnModeChanged += GizmoModeChanged; + } + + private void GizmoModeChanged(object sender, LotViewHUD.HUD_UILotModes e) + { + //TODO: Make UI react to mode changes ... for now not really necessary + } + + void Update() + { + + } + } +} diff --git a/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewHUDController.cs.meta b/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewHUDController.cs.meta new file mode 100644 index 00000000..895daa9a --- /dev/null +++ b/Assets/Scripts/OpenTS2/UI/Layouts/Lot/LotViewHUDController.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9727801f799a10a41875182ace337639 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: -10 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/OpenTS2/UI/UIButtonComponent.cs b/Assets/Scripts/OpenTS2/UI/UIButtonComponent.cs index 4ade9a2b..714d4f8e 100644 --- a/Assets/Scripts/OpenTS2/UI/UIButtonComponent.cs +++ b/Assets/Scripts/OpenTS2/UI/UIButtonComponent.cs @@ -24,6 +24,7 @@ public class UIButtonComponent : UIComponent, IPointerEnterHandler, IPointerExit public ResourceKey ClickSound; private bool _hovering = false; private bool _held = false; + private bool _toggled = false; // some buttons are toggle buttons -- adding this here to account for this scenario private TextureAsset[] _textures; public RawImage RawImageComponent => GetComponent(); public void SetTexture(TextureAsset texture) @@ -52,6 +53,11 @@ void UpdateTexture() RawImageComponent.texture = _textures[0].Texture; else { + if (_toggled) // Toggle Button texture here + { + RawImageComponent.texture = _textures[2].Texture; + return; + } RawImageComponent.texture = _textures[1].Texture; if (_hovering && !UIManager.AnyMouseButtonHeld) RawImageComponent.texture = _textures[3].Texture; @@ -99,5 +105,16 @@ public void OnPointerUp(PointerEventData eventData) OnClick?.Invoke(); _held = false; } + + void Click() + { + OnClick?.Invoke(); + } + + public void SetToggled(bool Toggled) + { + _toggled = Toggled; + UpdateTexture(); + } } } diff --git a/Assets/Shaders/Lot/Billboard.shader b/Assets/Shaders/Lot/Billboard.shader new file mode 100644 index 00000000..a100701b --- /dev/null +++ b/Assets/Shaders/Lot/Billboard.shader @@ -0,0 +1,69 @@ +Shader "Unlit/Billboard" +{ + Properties + { + _MainTex ("Texture", 2D) = "white" {} + } + SubShader + { + Tags{ "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" "DisableBatching" = "True" } + + ZWrite Off + Blend SrcAlpha OneMinusSrcAlpha + + Pass + { + CGPROGRAM + #pragma vertex vert + #pragma fragment frag + // make fog work + #pragma multi_compile_fog + + #include "UnityCG.cginc" + + struct appdata + { + float4 vertex : POSITION; + float2 uv : TEXCOORD0; + }; + + struct v2f + { + float2 uv : TEXCOORD0; + UNITY_FOG_COORDS(1) + float4 pos : SV_POSITION; + }; + + sampler2D _MainTex; + float4 _MainTex_ST; + + v2f vert (appdata v) + { + v2f o; + o.pos = UnityObjectToClipPos(v.vertex); + o.uv = v.uv.xy; + + // billboard mesh towards camera + float3 vpos = mul((float3x3)unity_ObjectToWorld, v.vertex.xyz); + float4 worldCoord = float4(unity_ObjectToWorld._m03, unity_ObjectToWorld._m13, unity_ObjectToWorld._m23, 1); + float4 viewPos = mul(UNITY_MATRIX_V, worldCoord) + float4(vpos, 0); + float4 outPos = mul(UNITY_MATRIX_P, viewPos); + + o.pos = outPos; + + UNITY_TRANSFER_FOG(o,o.vertex); + return o; + } + + fixed4 frag (v2f i) : SV_Target + { + // sample the texture + fixed4 col = tex2D(_MainTex, i.uv); + // apply fog + UNITY_APPLY_FOG(i.fogCoord, col); + return col; + } + ENDCG + } + } +} diff --git a/Assets/Shaders/Lot/Billboard.shader.meta b/Assets/Shaders/Lot/Billboard.shader.meta new file mode 100644 index 00000000..775a27a2 --- /dev/null +++ b/Assets/Shaders/Lot/Billboard.shader.meta @@ -0,0 +1,10 @@ +fileFormatVersion: 2 +guid: 9027c75630615624b8d7e4b02852aa4e +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + preprocessorOverride: 0 + userData: + assetBundleName: + assetBundleVariant: