diff --git a/.gitignore b/.gitignore index bbb5c564b..9950f251b 100644 --- a/.gitignore +++ b/.gitignore @@ -367,7 +367,7 @@ MigrationBackup/ # Vi swap files *.swp -**/Plugins/** +/VisualPinball.Unity/Plugins/** obj.meta VisualPinball.Unity/VisualPinball.Unity.Test/TestProject~/*.vpx @@ -386,4 +386,4 @@ VisualPinball.Unity/VisualPinball.Unity.Test/TestProject~/VisualPinball.Unity.cs VisualPinball.Unity/VisualPinball.Unity.Test/TestProject~/editmode-results.xml .DS_Store - + diff --git a/VisualPinball.Engine.Test/VisualPinball.Engine.Test.csproj b/VisualPinball.Engine.Test/VisualPinball.Engine.Test.csproj index 5af7c1c15..11d16c5f7 100644 --- a/VisualPinball.Engine.Test/VisualPinball.Engine.Test.csproj +++ b/VisualPinball.Engine.Test/VisualPinball.Engine.Test.csproj @@ -24,14 +24,14 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + @@ -40,7 +40,7 @@ - $(NuGetPackageRoot)\fluentassertions\6.3.0\lib\netstandard2.1\FluentAssertions.dll + $(NuGetPackageRoot)\fluentassertions\6.5.1\lib\netstandard2.1\FluentAssertions.dll diff --git a/VisualPinball.Engine/Common/Constants.cs b/VisualPinball.Engine/Common/Constants.cs index 8078644dd..25f37dce7 100644 --- a/VisualPinball.Engine/Common/Constants.cs +++ b/VisualPinball.Engine/Common/Constants.cs @@ -148,6 +148,10 @@ public static class InputConstants public const string ActionCoinDoorDown = "Coin Door Down (WPC)"; public const string ActionCoinDoorUp = "Coin Door Up (WPC)"; public const string ActionCoinDoorEnter = "Coin Door Enter (WPC)"; + public const string ActionCoinDoorBack = "Coin Door Back (SAM)"; + public const string ActionCoinDoorMinus = "Coin Door Minus (-) (SAM)"; + public const string ActionCoinDoorPlus = "Coin Door Plus (+) (SAM)"; + public const string ActionCoinDoorSelect = "Coin Door Select (SAM)"; public const string ActionCoinDoorAdvance = "Coin Door Advance"; public const string ActionCoinDoorUpDown = "Coin Door Up/Down"; public const string ActionSlamTilt = "Slam Tilt"; diff --git a/VisualPinball.Engine/Game/Engines/GamelogicEngineLamp.cs b/VisualPinball.Engine/Game/Engines/GamelogicEngineLamp.cs index 36e71afd0..e46c3f748 100644 --- a/VisualPinball.Engine/Game/Engines/GamelogicEngineLamp.cs +++ b/VisualPinball.Engine/Game/Engines/GamelogicEngineLamp.cs @@ -107,4 +107,92 @@ public enum LampType RgbMulti = 2, Rgb = 3, } + + public enum LampStatus + { + Off = 0, + On = 1, + Blinking = 2, + } + + public class LampState + { + public LampStatus Status { + get => _status; + set { + if (value != LampStatus.Off) { + _lastOnStatus = value; + } + _status = value; + } + } + + public bool IsOn { + get => _status is LampStatus.On or LampStatus.Blinking; + set => _status = value ? _lastOnStatus : LampStatus.Off; + } + + public float Intensity { + get => Color.A; + set => Color = Color.WithAlpha(value); + } + + public Color Color; + + private LampStatus _status; + private LampStatus _lastOnStatus = LampStatus.On; + + public LampState(LampStatus status, Color color) + { + Status = status; + Color = color; + } + + public LampState(float intensity) + { + if (intensity == 0f) { + _status = LampStatus.Off; + Color = new Color(255, 255, 255, 255); + + } else { + _status = LampStatus.On; + Color = new Color(255, 255, 255, (int)(intensity * 255)); + } + } + + public LampState(Color color) + { + _status = color.A > 0 ? LampStatus.On : LampStatus.Off; + Color = color; + } + + public void SetChannel(ColorChannel channel, float value) + { + switch (channel) { + + case ColorChannel.Red: + Color.R = value; + break; + case ColorChannel.Green: + Color.G = value; + break; + case ColorChannel.Blue: + Color.B = value; + break; + case ColorChannel.Alpha: + Color.A = value; + break; + + default: + throw new ArgumentOutOfRangeException(nameof(channel), channel, null); + } + } + + public override string ToString() + { + return $"[{_status}] {Color}"; + } + + public static LampState Default => new LampState(LampStatus.Off, Colors.White); + } } diff --git a/VisualPinball.Engine/Math/Color.cs b/VisualPinball.Engine/Math/Color.cs index c8f122e5e..8031600d5 100644 --- a/VisualPinball.Engine/Math/Color.cs +++ b/VisualPinball.Engine/Math/Color.cs @@ -14,10 +14,13 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +// ReSharper disable InconsistentNaming + using System; namespace VisualPinball.Engine.Math { + // todo make struct [Serializable] public class Color { @@ -26,10 +29,10 @@ public class Color public int Blue; public int Alpha = 0xff; - public float R => Red / 255f; - public float G => Green / 255f; - public float B => Blue / 255f; - public float A => Alpha / 255f; + public float R { get => Red / 255f; set => Red = (int)(value * 255f); } + public float G { get => Green / 255f; set => Green = (int)(value * 255f); } + public float B { get => Blue / 255f; set => Blue = (int)(value * 255f); } + public float A { get => Alpha / 255f; set => Alpha = (int)(value * 255f); } public uint Bgr => (uint)Alpha * 16777216 + (uint)Blue * 65536 + (uint)Green * 256 + (uint)Red; @@ -69,11 +72,23 @@ public Color WithAlpha(int alpha) return new Color(Red, Green, Blue, alpha); } + public Color WithAlpha(float alpha) + { + return new Color(Red, Green, Blue, (int)(alpha * 255f)); + } + public bool IsGray() { return Red == Green && Green == Blue; } + public void SetColorWithoutAlpha(Color color) + { + Red = color.Red; + Green = color.Green; + Blue = color.Blue; + } + public override string ToString() { return $"rgba({System.Math.Round(R, 3)}, {System.Math.Round(G, 3)}, {System.Math.Round(B, 3)}, {System.Math.Round(A, 3)})"; diff --git a/VisualPinball.Engine/VisualPinball.Engine.csproj b/VisualPinball.Engine/VisualPinball.Engine.csproj index 1d39651d7..ecb680643 100644 --- a/VisualPinball.Engine/VisualPinball.Engine.csproj +++ b/VisualPinball.Engine/VisualPinball.Engine.csproj @@ -28,12 +28,12 @@ - + - + @@ -60,7 +60,7 @@ - + diff --git a/VisualPinball.Unity/Assets/Art/Meshes/Bumper/Bumper (Ring).fbx b/VisualPinball.Unity/Assets/Art/Meshes/Bumper/Bumper (Ring).fbx new file mode 100644 index 000000000..74436ee20 Binary files /dev/null and b/VisualPinball.Unity/Assets/Art/Meshes/Bumper/Bumper (Ring).fbx differ diff --git a/VisualPinball.Unity/Assets/Art/Meshes/Bumper/Bumper (Ring).fbx.meta b/VisualPinball.Unity/Assets/Art/Meshes/Bumper/Bumper (Ring).fbx.meta new file mode 100644 index 000000000..029c90b6e --- /dev/null +++ b/VisualPinball.Unity/Assets/Art/Meshes/Bumper/Bumper (Ring).fbx.meta @@ -0,0 +1,105 @@ +fileFormatVersion: 2 +guid: e0beb34b8c47fb2488076fdc547bd1e0 +ModelImporter: + serializedVersion: 21202 + internalIDToNameTable: [] + externalObjects: {} + materials: + materialImportMode: 2 + materialName: 0 + materialSearch: 1 + materialLocation: 1 + animations: + legacyGenerateAnimations: 4 + bakeSimulation: 0 + resampleCurves: 1 + optimizeGameObjects: 0 + removeConstantScaleCurves: 1 + motionNodeName: + rigImportErrors: + rigImportWarnings: + animationImportErrors: + animationImportWarnings: + animationRetargetingWarnings: + animationDoRetargetingWarnings: 0 + importAnimatedCustomProperties: 0 + importConstraints: 0 + animationCompression: 1 + animationRotationError: 0.5 + animationPositionError: 0.5 + animationScaleError: 0.5 + animationWrapMode: 0 + extraExposedTransformPaths: [] + extraUserProperties: [] + clipAnimations: [] + isReadable: 0 + meshes: + lODScreenPercentages: [] + globalScale: 1 + meshCompression: 0 + addColliders: 0 + useSRGBMaterialColor: 1 + sortHierarchyByName: 1 + importVisibility: 1 + importBlendShapes: 1 + importCameras: 1 + importLights: 1 + nodeNameCollisionStrategy: 1 + fileIdsGeneration: 2 + swapUVChannels: 0 + generateSecondaryUV: 0 + useFileUnits: 1 + keepQuads: 0 + weldVertices: 1 + bakeAxisConversion: 0 + preserveHierarchy: 0 + skinWeightsMode: 0 + maxBonesPerVertex: 4 + minBoneWeight: 0.001 + optimizeBones: 0 + meshOptimizationFlags: -1 + indexFormat: 0 + secondaryUVAngleDistortion: 8 + secondaryUVAreaDistortion: 15.000001 + secondaryUVHardAngle: 88 + secondaryUVMarginMethod: 1 + secondaryUVMinLightmapResolution: 40 + secondaryUVMinObjectScale: 1 + secondaryUVPackMargin: 4 + useFileScale: 1 + tangentSpace: + normalSmoothAngle: 60 + normalImportMode: 0 + tangentImportMode: 3 + normalCalculationMode: 4 + legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0 + blendShapeNormalImportMode: 1 + normalSmoothingSource: 0 + referencedClips: [] + importAnimation: 1 + humanDescription: + serializedVersion: 3 + human: [] + skeleton: [] + armTwist: 0.5 + foreArmTwist: 0.5 + upperLegTwist: 0.5 + legTwist: 0.5 + armStretch: 0.05 + legStretch: 0.05 + feetSpacing: 0 + globalScale: 1 + rootMotionBoneName: + hasTranslationDoF: 0 + hasExtraRoot: 0 + skeletonHasParents: 1 + lastHumanDescriptionAvatarSource: {instanceID: 0} + autoGenerateAvatarMappingIfUnspecified: 1 + animationType: 2 + humanoidOversampling: 1 + avatarSetup: 0 + addHumanoidExtraRootOnlyWhenUsingAvatar: 1 + additionalBone: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_blue/score_reel.png b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/score_reel.png new file mode 100644 index 000000000..b5d008988 Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/score_reel.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_blue/score_reel.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/score_reel.png.meta new file mode 100644 index 000000000..d88407cc7 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/score_reel.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: d2a8899033566c24a8c8925f3b7ba845 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_blue/score_reel_single.png b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/score_reel_single.png new file mode 100644 index 000000000..e77a49d24 Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/score_reel_single.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_blue/score_reel_single.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/score_reel_single.png.meta new file mode 100644 index 000000000..0063b5ccb --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_blue/score_reel_single.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 970ca6b4e71c19747b08421a7448998e +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_colored/player_variable.png b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/player_variable.png new file mode 100644 index 000000000..8c6f71f3e Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/player_variable.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_colored/player_variable.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/player_variable.png.meta new file mode 100644 index 000000000..ff84e8324 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/player_variable.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: e189194ac0af3314aa4d20cad503f401 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_colored/player_variable_event.png b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/player_variable_event.png new file mode 100644 index 000000000..6352c8e71 Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/player_variable_event.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_colored/player_variable_event.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/player_variable_event.png.meta new file mode 100644 index 000000000..1edca2e17 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/player_variable_event.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 5f265e75b5991b04889720960f242523 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_colored/table_variable.png b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/table_variable.png new file mode 100644 index 000000000..8d1d15e16 Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/table_variable.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_colored/table_variable.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/table_variable.png.meta new file mode 100644 index 000000000..cadf0c9ba --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/table_variable.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: addc65d91ed637c40b0ccd28af890610 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_colored/table_variable_event.png b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/table_variable_event.png new file mode 100644 index 000000000..d6b03fe74 Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/table_variable_event.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_colored/table_variable_event.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/table_variable_event.png.meta new file mode 100644 index 000000000..9140993aa --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/table_variable_event.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 7ac405a4d480cb4458658018cdc6ff2b +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_colored/update_display.png b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/update_display.png new file mode 100644 index 000000000..b5fa0ff74 Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/update_display.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_colored/update_display.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/update_display.png.meta new file mode 100644 index 000000000..44ecd2ca8 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_colored/update_display.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: cfaddf46d73ddc549a121291647fc598 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_gray/score_reel.png b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/score_reel.png new file mode 100644 index 000000000..5030fea2b Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/score_reel.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_gray/score_reel.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/score_reel.png.meta new file mode 100644 index 000000000..5d2f5a384 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/score_reel.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 79b32ddb8ab2a5c4fbdd1247d5529a38 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_gray/score_reel_single.png b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/score_reel_single.png new file mode 100644 index 000000000..0ea395d1e Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/score_reel_single.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_gray/score_reel_single.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/score_reel_single.png.meta new file mode 100644 index 000000000..6cc4cd25e --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_gray/score_reel_single.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: a35d16bb185b3f548825b998f1366562 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_green/score_reel.png b/VisualPinball.Unity/Assets/Editor/Icons/large_green/score_reel.png new file mode 100644 index 000000000..f0511deb4 Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_green/score_reel.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_green/score_reel.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_green/score_reel.png.meta new file mode 100644 index 000000000..fb06894f0 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_green/score_reel.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: e2c916ea4b9f63046ac488819223b148 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_green/score_reel_single.png b/VisualPinball.Unity/Assets/Editor/Icons/large_green/score_reel_single.png new file mode 100644 index 000000000..6419b23cf Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_green/score_reel_single.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_green/score_reel_single.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_green/score_reel_single.png.meta new file mode 100644 index 000000000..ad656394a --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_green/score_reel_single.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 9cd73adaeeba2844bacf8ee50856099d +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_orange/score_reel.png b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/score_reel.png new file mode 100644 index 000000000..43d35c46a Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/score_reel.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_orange/score_reel.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/score_reel.png.meta new file mode 100644 index 000000000..491073ffc --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/score_reel.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 1a51154c68177294399fa22dba980ab2 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_orange/score_reel_single.png b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/score_reel_single.png new file mode 100644 index 000000000..dd07e92a5 Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/score_reel_single.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/large_orange/score_reel_single.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/score_reel_single.png.meta new file mode 100644 index 000000000..6097ae371 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/large_orange/score_reel_single.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: bd5054a39b3f95443bf686c4f48f0986 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 1 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 1 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 0 + wrapV: 0 + wrapW: 0 + nPOTScale: 1 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 0 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 512 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_blue/score_reel.png b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/score_reel.png new file mode 100644 index 000000000..c26c3b09f Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/score_reel.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_blue/score_reel.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/score_reel.png.meta new file mode 100644 index 000000000..c5066008d --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/score_reel.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: bbfcc2b1e6c9e174cb7fe20f7fb98904 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 64 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_blue/score_reel_single.png b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/score_reel_single.png new file mode 100644 index 000000000..11f51a857 Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/score_reel_single.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_blue/score_reel_single.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/score_reel_single.png.meta new file mode 100644 index 000000000..4e9d9e000 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_blue/score_reel_single.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 1ec8bb775aa3c8b4d90b951f3795c03c +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 64 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_gray/score_reel.png b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/score_reel.png new file mode 100644 index 000000000..77135e46b Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/score_reel.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_gray/score_reel.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/score_reel.png.meta new file mode 100644 index 000000000..0d4279cab --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/score_reel.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: db674a184a1c6374cbb5cf2f49bdff33 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 64 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_gray/score_reel_single.png b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/score_reel_single.png new file mode 100644 index 000000000..53a72b215 Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/score_reel_single.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_gray/score_reel_single.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/score_reel_single.png.meta new file mode 100644 index 000000000..9f945e2d0 --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_gray/score_reel_single.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 5b9e6c47478666746abd5a1924ca5bbe +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 64 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_green/score_reel.png b/VisualPinball.Unity/Assets/Editor/Icons/small_green/score_reel.png new file mode 100644 index 000000000..b3af6164a Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_green/score_reel.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_green/score_reel.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_green/score_reel.png.meta new file mode 100644 index 000000000..9bd4022ee --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_green/score_reel.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: c88b5537ec1114a4487a16e259225f42 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 64 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_green/score_reel_single.png b/VisualPinball.Unity/Assets/Editor/Icons/small_green/score_reel_single.png new file mode 100644 index 000000000..8ff8e9caf Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_green/score_reel_single.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_green/score_reel_single.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_green/score_reel_single.png.meta new file mode 100644 index 000000000..a6be878bd --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_green/score_reel_single.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 31da5f3c4e46d3c4e9eb6066b228f2a0 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 64 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_orange/score_reel.png b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/score_reel.png new file mode 100644 index 000000000..ef278c91b Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/score_reel.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_orange/score_reel.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/score_reel.png.meta new file mode 100644 index 000000000..def76346d --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/score_reel.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 792a87c8fea13a442a9485185dcc60d1 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 64 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_orange/score_reel_single.png b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/score_reel_single.png new file mode 100644 index 000000000..89f57de45 Binary files /dev/null and b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/score_reel_single.png differ diff --git a/VisualPinball.Unity/Assets/Editor/Icons/small_orange/score_reel_single.png.meta b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/score_reel_single.png.meta new file mode 100644 index 000000000..45a72710f --- /dev/null +++ b/VisualPinball.Unity/Assets/Editor/Icons/small_orange/score_reel_single.png.meta @@ -0,0 +1,122 @@ +fileFormatVersion: 2 +guid: 690cb6a39af01c243a459d20d333cb33 +TextureImporter: + internalIDToNameTable: [] + externalObjects: {} + serializedVersion: 11 + mipmaps: + mipMapMode: 0 + enableMipMap: 0 + sRGBTexture: 1 + linearTexture: 0 + fadeOut: 0 + borderMipMap: 0 + mipMapsPreserveCoverage: 0 + alphaTestReferenceValue: 0.5 + mipMapFadeDistanceStart: 1 + mipMapFadeDistanceEnd: 3 + bumpmap: + convertToNormalMap: 0 + externalNormalMap: 0 + heightScale: 0.25 + normalMapFilter: 0 + isReadable: 0 + streamingMipmaps: 0 + streamingMipmapsPriority: 0 + vTOnly: 0 + ignoreMasterTextureLimit: 0 + grayScaleToAlpha: 0 + generateCubemap: 6 + cubemapConvolution: 0 + seamlessCubemap: 0 + textureFormat: 1 + maxTextureSize: 2048 + textureSettings: + serializedVersion: 2 + filterMode: 2 + aniso: 1 + mipBias: 0 + wrapU: 1 + wrapV: 1 + wrapW: 0 + nPOTScale: 0 + lightmap: 0 + compressionQuality: 50 + spriteMode: 0 + spriteExtrude: 1 + spriteMeshType: 1 + alignment: 0 + spritePivot: {x: 0.5, y: 0.5} + spritePixelsToUnits: 100 + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spriteGenerateFallbackPhysicsShape: 1 + alphaUsage: 1 + alphaIsTransparency: 1 + spriteTessellationDetail: -1 + textureType: 2 + textureShape: 1 + singleChannelComponent: 0 + flipbookRows: 1 + flipbookColumns: 1 + maxTextureSizeSet: 0 + compressionQualitySet: 0 + textureFormatSet: 0 + ignorePngGamma: 0 + applyGammaDecoding: 0 + platformSettings: + - serializedVersion: 3 + buildTarget: DefaultTexturePlatform + maxTextureSize: 64 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 0 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Standalone + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + - serializedVersion: 3 + buildTarget: Server + maxTextureSize: 2048 + resizeAlgorithm: 0 + textureFormat: -1 + textureCompression: 1 + compressionQuality: 50 + crunchedCompression: 0 + allowsAlphaSplitting: 0 + overridden: 0 + androidETC2FallbackOverride: 0 + forceMaximumCompressionQuality_BC6H_BC7: 0 + spriteSheet: + serializedVersion: 2 + sprites: [] + outline: [] + physicsShape: [] + bones: [] + spriteID: + internalID: 0 + vertices: [] + indices: + edges: [] + weights: [] + secondaryTextures: [] + nameFileIdTable: {} + spritePackingTag: + pSDRemoveMatte: 0 + pSDShowRemoveMatteOption: 0 + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/Documentation~/creators-guide/introduction/overview.md b/VisualPinball.Unity/Documentation~/creators-guide/introduction/overview.md index f8c249312..358160409 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/introduction/overview.md +++ b/VisualPinball.Unity/Documentation~/creators-guide/introduction/overview.md @@ -14,6 +14,10 @@ VPE uses [Unity](https://unity.com/) for its underlying game architecture. Unity > [!Video https://www.youtube.com/embed/JxjdZ6mohfA] *An imported .vpx file in VPE, using Unitys' High Definition Render Pipeline.* +> [!Video https://www.youtube.com/embed/wHcKd_FExsE] +Gottlieb's *Volley*, remodeled and retextured in Unity + + > [!NOTE] > Technically, VPE is what we call a "library". A library is not executable per se, because it needs a host application. > @@ -21,6 +25,4 @@ VPE uses [Unity](https://unity.com/) for its underlying game architecture. Unity ## Audience -This documentation is mainly aimed at table creators ("authors"). Since it covers table scripting as well, it also contains code-related documentation. - -VPE is currently not in a state where it is usable by the general public ("players"). Documentation about how to setup VPE to play will follow at a later stage. +This documentation is mainly aimed at table creators ("authors"). VPE is currently not in a state where it is usable by the general public ("players"). Documentation about how to setup VPE to play will follow at a later stage. diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/display-add-component.png b/VisualPinball.Unity/Documentation~/creators-guide/manual/display-add-component.png index 6be8bc428..d89aace55 100644 Binary files a/VisualPinball.Unity/Documentation~/creators-guide/manual/display-add-component.png and b/VisualPinball.Unity/Documentation~/creators-guide/manual/display-add-component.png differ diff --git a/VisualPinball.Unity/Documentation~/creators-guide/manual/displays.md b/VisualPinball.Unity/Documentation~/creators-guide/manual/displays.md index 8f603e3ce..4edde08d8 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/manual/displays.md +++ b/VisualPinball.Unity/Documentation~/creators-guide/manual/displays.md @@ -9,16 +9,16 @@ Every pinball machine has one or more displays where the score and other importa ![DMD](dmd-game_over.jpg) *A dot matrix display used in the late 90s - Photo © 2009 by [ElHeineken](https://commons.wikimedia.org/wiki/File:Pinball_Dot_Matrix_Display_-_Demolition_Man.JPG)* -VPE supports both segment displays and dot matrix displays (usually referred to as DMDs). During game play displays are driven by the [Gamelogic Engine](xref:gamelogic_engine). VPE supports multiple displays per game. +VPE supports score reels used in the earliest electro-mechanical machines, as well as segment displays and dot matrix displays (usually referred to as DMDs). During game play displays are driven by the [Gamelogic Engine](xref:gamelogic_engine). VPE supports multiple displays per game. > [!note] -> While the earliest electro-mechanical pinball machines used motorized score reels, and today's are using high resolution LCDs neither are yet supported in VPE. +> Many of today's machines are using high resolution LCDs, which are not yet supported in VPE. ## Setup -Displays are lazily bound, meaning that when the game starts the gamelogic engine announces its displays and VPE connects them to objects in your scene that actually render them. Matching is done with an ID and depends on how the gamelogic engine being used manages displays. +Displays are lazily bound, meaning that when the game starts, the gamelogic engine announces its displays and VPE connects them to objects in your scene that actually render them. Matching is done with an ID and depends on how the gamelogic engine being used manages displays. -For example, in [MPF](xref:mpf_index) you name your displays yourself in the machine configuration, while PinMAME uses IDs like `dmd0` and `display0` to identify its DMDs and segment displays. +For example, in [MPF](xref:mpf_index) you name your displays yourself in the machine configuration, while [PinMAME](xref:pinmame_index) uses IDs like `dmd0` and `display0` to identify its DMDs and segment displays. With [Visual scripting](xref:uvs_index), you define your own display IDs that must match with the displays on the playfield. ### Editor @@ -28,6 +28,8 @@ VPE provides two display components, one for segment displays and one for DMDs. You can also create the game object with a component already assigned by right-clicking in the hierarchy and choosing *Visual Pinball -> Dot Matrix Display*. This will place the display into your scene right behind your playfield. +Since score reels come with additional geometry and textures, VPE provides them as prefabs through the asset library. + DMD Inspector Selecting the game object will let you customize it in the inspector, and assign the ID that links it to the gamelogic engine. diff --git a/VisualPinball.Unity/Documentation~/creators-guide/setup/installing-vpe.md b/VisualPinball.Unity/Documentation~/creators-guide/setup/installing-vpe.md index 21d01d667..427227398 100644 --- a/VisualPinball.Unity/Documentation~/creators-guide/setup/installing-vpe.md +++ b/VisualPinball.Unity/Documentation~/creators-guide/setup/installing-vpe.md @@ -15,6 +15,9 @@ Unity uses an application called *Unity Hub* to update itself, create new projec When installing Unity, use the latest **2021.2** version. You can leave all the other options unchecked. +> [!NOTE] +> Since 2021.2 is not an LTS version, Unity will not show other options than 2020.3 if you don't have anything installed yet. If that's the case, click on *Skip Installation* in the first dialog, which will take you to a screen with more options. + Once Unity is downloaded and installed, you're ready to create a new VPE project. Click on *New Project*, be sure to have selected the 2021.2 version at the top, and you'll see the following choices: ![New Unity Project](unity-create-new-project.png) @@ -23,7 +26,7 @@ The relevant options for VPE are: - **3D** - Unity's original built-in renderer. - **3D (URP)** - Unity's [Universal Render Pipeline](https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@8.2/manual/index.html) is aimed at mobile and low-end platforms. -- **3D (HDRP)** - Unity's [High Definition Render Pipeline](https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@0.0.0/manual/index.html) used for high-end platforms. +- **3D (HDRP)** - Unity's [High Definition Render Pipeline](https://docs.unity3d.com/Packages/com.unity.render-pipelines.high-definition@12.0/manual/index.html) used for high-end platforms. We recommend using HDRP. It's what we're using when developing and should be the most stable pipeline. Alternatively if you're on a laptop don't have a beefy GPU, use the URP. The built-in renderer is legacy not recommended. diff --git a/VisualPinball.Unity/Documentation~/docfx.json b/VisualPinball.Unity/Documentation~/docfx.json index 1ea0fa126..2cb2fe9de 100644 --- a/VisualPinball.Unity/Documentation~/docfx.json +++ b/VisualPinball.Unity/Documentation~/docfx.json @@ -83,7 +83,7 @@ "postProcessors": [ "ExtractSearchIndex" ], "globalMetadata": { "_appTitle": "VPE Documentation", - "_appFooter": "Copyright © 2021 VPE Team", + "_appFooter": "Copyright © 2022 VPE Team", "_gitContribute": { "branch": "master" } diff --git a/VisualPinball.Unity/Documentation~/plugins/index.md b/VisualPinball.Unity/Documentation~/plugins/index.md index 049360355..996f2ce04 100644 --- a/VisualPinball.Unity/Documentation~/plugins/index.md +++ b/VisualPinball.Unity/Documentation~/plugins/index.md @@ -12,6 +12,9 @@ VPE has a plug-in system that allows other software to integrate with it. Plugin [PinMAME](https://github.com/vpinball/pinmame) is an emulator written in C which supports most of the hardware found in pinball machines the mid 1970's until 2014. +## [Visual Scripting](xref:uvs_index) + +For original games or EM machines, we recommend using our visual scripting package which extends [Unity's visual scripting](https://unity.com/products/unity-visual-scripting). ## [Mission Pinball Framework](xref:mpf_index) diff --git a/VisualPinball.Unity/Documentation~/plugins/toc.yml b/VisualPinball.Unity/Documentation~/plugins/toc.yml index 7e25dcfb2..61a6064ec 100644 --- a/VisualPinball.Unity/Documentation~/plugins/toc.yml +++ b/VisualPinball.Unity/Documentation~/plugins/toc.yml @@ -7,6 +7,19 @@ - name: Mechs href: pinmame/mechs.md +- name: Visual Scripting + items: + - name: Overview + href: visual-scripting/index.md + - name: Setup + href: visual-scripting/setup.md + - name: Variables + href: visual-scripting/variables.md + - name: Complemetary Usage + href: visual-scripting/complementary-usage.md + - name: Node Reference + href: visual-scripting/node-reference.md + - name: Mission Pinball Framework items: - name: Overview diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/banner.jpg b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/banner.jpg new file mode 100644 index 000000000..1697224fa Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/banner.jpg differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/bridge-component.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/bridge-component.png new file mode 100644 index 000000000..6026dde61 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/bridge-component.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/change-player-state-example.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/change-player-state-example.png new file mode 100644 index 000000000..d672f4ba9 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/change-player-state-example.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/coils.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/coils.png new file mode 100644 index 000000000..1b31f62c6 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/coils.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/complementary-usage.md b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/complementary-usage.md new file mode 100644 index 000000000..f500e8065 --- /dev/null +++ b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/complementary-usage.md @@ -0,0 +1,30 @@ +--- +uid: uvs_complementary_u_sage +title: Visual Scripting - Complementary Usage +description: How we use visual scripting along with other gamelogic engines +--- + +# Complementary Usage + +So far, we have talked about visual scripting driving the logic of a pinball game. But what if we need to add logic to an *existing* gamelogic engine, without replacing it entirely? + +For example, [*Rock (Premier 1978)*](https://www.ipdb.org/machine.cgi?id=1978) isn't entirely emulated in PinMAME; the start-up light sequence is handled by an auxiliary board, and we're only getting control signals, but not signals for every individual light. + +In this case, we'd like to keep PinMAME as the driving GLE, but add a visual script that handles the light sequence properly. + +## Setup + +Visual Scripting Bridge + +In order to give VPE's visual scripting nodes access to your game logic engine, we provide what we call a *Visual Scripting Bridge*. It's a component that you'll need to add to your table's game object, along with your original gamelogic engine. You can do that by selecting the game object, clicking *Add Component* in the inspector, and choosing *Visual Pinball -> Gamelogic Engine -> Visual Scripting Game Logic*. + +This will give you access to most of the nodes. For example, the [On Lamp Changed](xref:uvs_node_reference#on-lamp-changed) event will now be triggered by whatever other gamelogic engine you're using. + +## Nodes + +There are a few nodes you cannot rely on, because they need the visual scripting gamelogic engine and won't work with the bridge. These nodes currently are: + +- The [display node](xref:uvs_node_reference#displays) +- The [variable nodes](xref:uvs_node_reference#variables) + +All other nodes ([coils](xref:uvs_node_reference#coils), [switches](xref:uvs_node_reference#switches), [lamps](xref:uvs_node_reference#lamps) and [events](xref:uvs_node_reference#events)) are available for use. \ No newline at end of file diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/create-player-state-example.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/create-player-state-example.png new file mode 100644 index 000000000..f8eebeeb7 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/create-player-state-example.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/displays.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/displays.png new file mode 100644 index 000000000..4ee4dc425 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/displays.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/event-setup.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/event-setup.png new file mode 100644 index 000000000..c48ddf7a4 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/event-setup.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/get-lamp-example.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/get-lamp-example.png new file mode 100644 index 000000000..5fedb0053 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/get-lamp-example.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/get-switch-example.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/get-switch-example.png new file mode 100644 index 000000000..1228eb1b6 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/get-switch-example.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/get-variable-example.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/get-variable-example.png new file mode 100644 index 000000000..a50443635 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/get-variable-example.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/increase-variable-example.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/increase-variable-example.png new file mode 100644 index 000000000..37aefe432 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/increase-variable-example.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/index.md b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/index.md new file mode 100644 index 000000000..4f0e58760 --- /dev/null +++ b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/index.md @@ -0,0 +1,61 @@ +--- +uid: uvs_index +title: Visual Scripting +description: Unity Visual Scripting and VPE +--- + +![Cover](banner.jpg) + +# Visual Scripting + +Unity has a powerful [visual scripting](https://unity.com/products/unity-visual-scripting) feature that VPE leverages for creating game logic without having to write code. It uses a node system which VPE extends to significantly simplify common tasks in a pinball game. + +> [!note] +> Visual scripting was previously named *Bolt*, and was a commercial product in Unity's asset store. In 2020, [Unity acquired Bolt](https://ludiq.io/blog/unity-acquires-bolt) and made it freely available. + + +## Why? + +VPE provides many tools that help you create pinball games. Our goal is to make it as easy as possible, so also non-technical people can pour their creativity into making original games. + +Looking at Visual Pinball, which uses VBScript for not only the game logic but also all the physical logic, people often ask which scripting language VPE will use. We don't have an answer to that yet. While Unity uses C#, and you can code whatever you want with it, there are a few obstacles that we need to overcome before recommending C#, namely: + +- **APIs** - The VPE code base is quite large, and we haven't put any effort into declaring which of the interfaces are internal and which are public. This is important, because internals can be changed and refactored in future updates, while public interfaces must stay the same in order not to break backwards compatibility. Thus, these APIs need to be well defined and documented. +- **Compilation** - Unlike VBS, C# is technically not a [scripting language](https://en.wikipedia.org/wiki/Scripting_language) but needs to be compiled. With VPE being cross-platform, this is problematic when shipping in a modding-friendly format. Additionally, Unity's [AssetBundles](https://docs.unity3d.com/Manual/AssetBundlesIntro.html) don't allow assemblies to be included, so we'll need a way of working around that as well. + +Visual scripting doesn't have the above problems. It doesn't need to be compiled, and the VPE APIs are the nodes that we provide. + +> [!note] +> Personal note: As software developers, we're obviously sceptical about anything that is supposed to replace code. So we started implementing an EM game in visual scripting. After putting some effort into creating the right kind of nodes to bring down the graph size significantly, we're happy and we think it's an awesome way of creating game logic. + +## How? + +If you're coming from Visual Pinball, you're probably used to doing everything in VBScript, including physics hacks and transforming objects on the playfield. VPE is different in that it separates the physical aspect of the table from the logical part (the "game logic"). We use visual scripting exclusively as a [Gamelogic Engine](xref:gamelogic_engine). + +This approach already reduces a lot what visual scripting must be able to do. It basically boils down to triggering coils and lamps based on switch events. Of course there are a few more things like updating the display and for more complicated games, triggering entire light shows and sounds, but in a nutshell it's very simple. + +For more details about visual scripting in general, check out [Unity's documentation](https://docs.unity3d.com/Packages/com.unity.visualscripting@1.8/manual/index.html). You will learn about [graphs](https://docs.unity3d.com/Packages/com.unity.visualscripting@1.8/manual/vs-graph-types.html), and about [script and state machines](https://docs.unity3d.com/Packages/com.unity.visualscripting@1.8/manual/vs-graph-machine-types.html). + +## Installation + +Visual scripting comes as an UPM package. In Unity, add it by choosing *Window -> Package Manager -> Add package from git URL*: + +

Package Manager

+ +Then, input `org.visualpinball.engine.unity.visualscripting` and click *Add* or press `Enter`. This will download and add visual scripting to the project. + +> [!NOTE] +> You will need to have our scoped registry added in order for Unity to find the visual scripting package. How to do this is documented in the [general setup section](/creators-guide/setup/installing-vpe.html#vpe-package). + +Once the visual scripting package is install, you can [set up](xref:uvs_setup) the gamelogic engine for it. + +## Disclaimer + +We've successfully used this package in a single player EM game. Its logic was relatively simple, and there are two important distinctions between an EM game and a modern era game: + +1. Modern games have light shows that need to be programmed +2. Modern games have a display like a DMD or even a high resolution LCD that needs content in form of pixel data. + +Visual scripting does neither of that. As mentioned above, it's good at reading switch changes and triggering coils and single lamps. Driving a segment display or score reel is very well feasible too, but don't expect to recreate JJP's Guns N' Roses just yet. + +For this you'll need a proper light sequencer and a video engine. Both are tools that will eventually be created, but today that's not yet the case. \ No newline at end of file diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/lamp-sequence-example.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/lamp-sequence-example.png new file mode 100644 index 000000000..0e38c593a Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/lamp-sequence-example.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/lamps.md b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/lamps.md new file mode 100644 index 000000000..694ea57c6 --- /dev/null +++ b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/lamps.md @@ -0,0 +1,26 @@ +--- +uid: uvs_lamps +title: Lamps +description: How lamps are driven. +--- + +# Lamps + +Lamps a bit more complex than coils and switches, because besides of simply being turned on or off, they have an intensity and a color. Additionally, they have a *blinking* state. This means that all our lamp nodes include a dropdown indicating how it should be driven, with the input (or output) types changing accordingly: + +- **Status** corresponds to a `enum`, one of *On*, *Off* and *Blinking*. +- **On/Off** is a `boolean`, where `true` corresponds to the *On* status, and `false` to the *Off* status. +- **Intensity** corresponds to a `float`, and is the brightness of the lamp. +- **Color** has its own `Color` type. + +These four modes allow you to completely control a lamp (with *On/Off* being sugar for setting the status using a `boolean`). However, there is a second factor that defines how the lamp will actually react, and that is its [mapping type](xref:lamp_manager#type) in the Lamp Manager. + +See, VPE supports a wide range of gamelogic engines, and they often don't have an internal API as rich as our visual scripting package. For example, when PinMAME sets a light to the value of 255, it doesn't know whether it just "turned it on" from 0 or whether it was "faded in" from a previous non-0 value. That's information we have to manually set in the Lamp Manager (in this example, the mapping type would be *Single On|Off* or *Single Fading* respectively). + +That said, the only mode that might leads to confusion is *Intensity*, mainly because it's the only value that PinMAME emits. So if you choose *Intensity*, here is how the value is treated depending each mapping type: + +- *Single On|Off* sets the **status** of the lamp to *On* if the value is greater than 0, and to *Off* otherwise. +- *Single Fading* sets the **intensity** to the value *divided by [maximal intensity](xref:lamp_manager#max-intensity)*. We recommend setting the maximal intensity to 100 in the Lamp Manager and use values from 0 to 100 in the visual scripting nodes. +- *RGB* sets the **intensity**, where the value is between 0 and 1. +- *RGB Multi* you probably won't use. It sets the [channel](xref:lamp_manager#channel) defined in the mapping to the value divided by 255 (yes, it's very PinMAME specific). + diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/lamps.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/lamps.png new file mode 100644 index 000000000..30573f75c Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/lamps.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/node-reference.md b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/node-reference.md new file mode 100644 index 000000000..516d192d8 --- /dev/null +++ b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/node-reference.md @@ -0,0 +1,278 @@ +--- +uid: uvs_node_reference +title: Visual Scripting Node Reference +description: Reference documentation for all VPE-specific nodes. +--- + +# Node Reference + +This page details all the VPE-specific nodes that we have created for visual scripting. + +You can recognize VPE nodes easily by their color: they are orange. When creating new nodes, VPE event nodes can be found under *Events/Visual Pinball*, and other nodes simply under the root's *Visual Pinball*. + +Besides the simple read/write/event nodes, there are a bunch of nodes that solve common patterns in pinball games. While you could implement the same logic using Unity's standard nodes, we recommend using these custom nodes, because they save you space and thus increase the readability of your graphs. + +However, it's hard to use them without knowing about them, so we recommend reading through this page in order to familiarize with them. + +## Coils + +### On Coil Enabled + +This event is triggered when the status of any coil in a list of coils is *enabled*. + +### On Coil Changed + +This coil event triggers when the status of a coil in a list of coils changes. + +![On Coil Changed](on-coil-changed.png) + +### On All Coils Enabled + +This coil event triggers when all coils in a list of coils are enabled. + +### Set Coil + +This node assigns a given value to one or multiple coils and keeps that value. This is useful when both the *enabled* and *disabled* status are important. Otherwise, use the [*Pulse Coil*](#pulse-coil) node, which enables a coil, and automatically disables it after a short delay. + +A typical use case for this node is linking the flipper coil to a switch event. Here an example of a game that has an upper flipper and a lower flipper, both linked to the same *left flipper* switch. + +![Set Coil](set-coil-example.png) + +As seen in the screenshot, you can set the number of affected coils in the header of the node. Increasing the number will add additional ports below. + +### Pulse Coil + +This node enables one or multiple coils and disables them after a given delay. This is useful when you only care about the "enabled" event, which often the case. Here is an example of the eject coil of the trough being pulsed when the *running* state is entered. + +![Pulse Coil](pulse-coil-example.png) + +### Get Coil Value + +This node returns the current coil status of a given coil. While usually you should rely on player and table variables for saving and retrieving status, it still has its usage. For example, you might need to see if a coil is still *enabled* after a period of time. + +## Switches + +### On Switch Enabled + +This is probably the most common switch event you will use. It triggers when any switch in a list of switches is *enabled*. + +Here is an example of the drain switch increasing the *current ball* variable. + +![On Switch Enabled](on-switch-enabled.png) + +### On Switch Changed + +The other switch event triggers in both cases, when the switch is enabled, and when it gets disabled. The classic example already mentioned above is the flipper buttons. + +### On All Switches Enabled + +This switch event triggers when all switches in a list of switches are enabled. + +### Set Switch + +This node enables or disables one or multiple switches. + +### Pulse Switch + +This node enables one or multiple switches and disables them after a given delay. + +### Get Switch Value + +This node returns the current switch value of a given switch. While usually you should rely on player and table variables for saving and retrieving status, it still has its usage. For example, you might want to not add the state of a kicker to the variables and rely on the kicker switch directly instead. + +![Get Switch](get-switch-example.png) + +You can also add multiple switches, in which case the output is only true if *all switches* are enabled. + +## Lamps + +Lamps are a bit more complex than coils and switches, because besides simply being *on* or *off*, they have an *intensity* and a *color*. Additionally, they can be set to a *blinking* state. This means that all our lamp nodes include a dropdown indicating how it should be driven, with the port types changing accordingly: + +- **Status** corresponds to an `enum`, one of *On*, *Off* and *Blinking*. +- **On/Off** is a `Boolean`, where `true` corresponds to the *On* status, and `false` to the *Off* status. +- **Intensity** corresponds to a `float` and is explained in more detail below. +- **Color** has its own `Color` type. + +These four modes allow you to completely control a lamp (with *On/Off* setting the status using a `Boolean`). However, there is a second factor that defines how the lamp will actually react, and that is its [mapping type](xref:lamp_manager#type) in the lamp manager. + +See, VPE supports a wide range of gamelogic engines, and they often don't have an internal API as rich as our visual scripting package. For example, when PinMAME sets a light to the value of 255, it doesn't know whether it just "turned it on" from 0 or whether it was "faded in" from a previous non 0 value. That's information we have to manually set in the lamp manager (in this example, the mapping type would be *Single On|Off* and *Single Fading* respectively). + +That said, the only mode that might lead to confusion is *Intensity*, mainly because it's the only value that PinMAME emits. So, if you choose *Intensity*, here is how the value is treated depending on each mapping type: + +- *Single On|Off* sets the **status** of the lamp to *On* if the value is greater than 0, and to *Off* otherwise. +- *Single Fading* sets the **intensity** to the value *divided by [maximal intensity](xref:lamp_manager#max-intensity)*. We recommend setting the maximum intensity to 100 in the lamp manager and use values from 0 to 100 in the visual scripting nodes. +- *RGB* sets the **intensity**, where the value is between 0 and 1. +- *RGB Multi* you probably will not use. It sets the [channel](xref:lamp_manager#channel) defined in the mapping to the value divided by 255 (yes, it's very PinMAME specific). + + +> [!NOTE] +> When creating your proper game logic, you should rely on [variables](#variables) instead of lamp status in your logic. However, since you can also use visual scripting along with different gamelogic engine such as [PinMAME](xref:pinmame_index), where you can't access the internal state, we also provide nodes for lamp events and retrieving their value. + +Let's jump to the nodes. + +### Set Lamp + +This node assigns a given value to a lamp defined by its mapped ID. This also triggers the lamp changed event. + +In the example, we have defined a player variable of type `Boolean` called *Yellow Bank Lit*. We synchronize the lamp status with the variable by setting the lamp with the ID `l_yellow_bank` to the value of the variable when it changes. + +![Set Lamp](set-lamp-example.png) + +### Get Lamp Value + +This node retrieves the current value of a lamp by its mapped ID. As described before, the type of the output port depends on which mode has been selected. + +A typical usage is to check whether a lamp has previously been set, where no variable is available. That's the case if you're expanding PinMAME with additional logic. + +![Get Lamp](get-lamp-example.png) + +This is an example from the table *Rock (Premier 1985)*, where PinMAME doesn't emulate the starting light sequence. The graph above checks if lamp 01 is lit and turns off a bunch of lamps if that's not the case (otherwise, it's more complicated). It does this when lamp 01, 12 or 13 changes, and at the beginning of the game. + +### On Lamp Changed + +This node triggers when any of the defined lamps changes state. Its application is quite similar to the previous *[get lamp value](#get-lamp-value)* node, where the example covers this node as well. + +### Lamp Sequence + +This node takes in a list of lamps and loops through each lamp with a given step size, while applying a given value to the active lamp(s) and another value to the inactive lamps. The counter is internal to the node and increases each time the node is processed. + +The inputs are the following: + +- *Lamps* - The list of lamps to loop through. Note that light groups are flattened, i.e., split into single lamps. +- *Step* - By how much the internal counter increases when the node is processed. It's also how many lamps are active. +- *Value* - The value that is applied to the *active* lamps +- *Non Step Value* - The value that is applied to *inactive* lamps. + +As an example, here is a loop that gets triggered when lamp 13 gets lit and repeats until lamp 13 is unlit. On each iteration, two lamps get turned on, while the others are turned off. It starts with the first two lamps, then the second two lamps, then lamp 5 and 6, and so on. All these lamps are part of the `l_auxiliary` light group, which contains 20 lamps. After lamp 19 and 20 were turned on, it goes back to lamp 1 and 2. After each iteration, the loop pauses for 60ms before continuing. + +Finally, when lamp 13 is turned off, the loop exits, and the entire light group is turned off as well. + +![Lamp Sequence](lamp-sequence-example.png) + +### Switch Lamp + +This node takes in a list of lamps, matches them based on an input, and updates them based on the match. + +In the following example we enable the lamp on an EM backglass which indicates the current ball in play. As an input we get an integer representing the ball number, and we toggle the respective lamp based on that value. + +![Switch Lamp](switch-lamp-example.png) + +You don't need to populate all possible input values with your lamps. Here is an example that matches only if the current ball number is 0 (no game running), in which case the GAME OVER lamp is set to *blinking*. + +![Switch Lamp](switch-lamp-example2.png) + + +## Variables + +See [Variables](xref:uvs_variables) for an overview on how variables work. We will be using examples for player variables, but apart from [creation](#create-player-state) and [changing](#change-player-state), they work the same way as table variables. + +### Create Player State + +This node adds variables for a new player. If the game starts and you want to use player variables, you need to create a player state, even if your game has only one player. + +You would typically do it when the game starts. Multiplayer games would execute this node when *start* is pressed during the first ball. + +This node has the following options: + +- **Auto-Increment** automatically sets the player ID. It does that by increasing the largest existing player ID by one. +- **Player ID** is only visible if auto-increment is not set, and lets you specify the player ID. +- **Set as Active** will make the newly created player state the current state. This makes sense when a new game is started, but not when new players are added. +- **Destroy Previous** deletes all player states before creating the new one. This is useful when starting a new game. + +Here an example of the player state being created right after the state machine enters the game state, i.e., when the game starts. + +![Create Player State](create-player-state-example.png) + +> [!note] +> This is one of two nodes that doesn't exist for table variables. + +### Change Player State + +This node swaps out the current player variables with the ones from another player. You do this when a player has finished playing and it's the next player's turn. + +The following options are available: + +- **Next Player** automatically choses the next player. If the current player is the last player, the next player is the first player. You typically enable this option when using *auto-increment* during player state creation. It means that VPE handles the player IDs. +- **Player ID** lets you explicitly set the ID of the player you want to change to (only visible of *Next Player* is disabled). + +The following is an example of a multiplayer game with infinite balls (i.e., remaining balls are not checked). The flow starts with the drain switch, which then checks whether the current player has any extra balls left. If that's not the case, then the player state is changed to the next player, otherwise the number of extra balls is decreased instead, and finally the eject coil of the trough is pulsed. + +![Change Player State](change-player-state-example.png) + +> [!note] +> This the second node that doesn't exist for table variables. + +### Get Player ID + +This node gives you access to the player ID. There are three different modes: + +- *Current* returns the ID of the current player +- *First* return the smallest player ID +- *Last* returns the largest player ID + +A typical example is shown in the [next section](#get-variable). + +### Get Variable + +This node returns the value of a given variable. To build on the previous example, let's do a check whether we should end the game if a ball was drained. + +To do that, we retrieve the player variable *Current Ball Number* and check if it's the same as the global variable *Balls per Game*. If that's the case, we assume that it's the last ball. Then we compare the current player ID to the last player ID. The final *And* node checks if both conditions are true, and what comes out is whether we should end the game or not. + +![Get Variable](get-variable-example.png) + +### Set Variable + +This node applies a given value to a variable. It's very straightforward. Here an example of a trigger enabling the lit status of a bumper. + +![Set Variable](set-variable-example.png) + +### Increase Variable + +More often than not, you want to increase a variable by a given value rather than setting an absolute value. This node does exactly that, for integer and float typed variables. For string types, it concatenates the value to the current one. For Boolean types, it inverts the current value, *if* the input value is `true`. + +A typical example for this node is scoring. This example adds 1000 points to the score when the bumper switch is enabled. + +![Increase Variable](increase-variable-example.png) + +### On Variable Changed + +One of the main advantages of using VPE's variable system is that you get events when they change. That makes it easy to separate how the variable is updated from what effect updating it causes. That's great, because you shouldn't care *why* a variable was updated, only *when* and *to which value* (see also [Synchronizing State](xref:uvs_variables#synchronizing-state)). + +In this example, we listen to the score variable and fetch it into our *Update Display* node, which sends the data to our score reel component, which then rotates the reels accordingly. Note that you'll also get the previous value of the variable before it changed. + +![On Variable Changed](on-variable-changed-example.png) + + +## Events + +### Trigger Pinball Event + +This node triggers an event that was previously defined in the inspector of the visual scripting gamelogic engine. It can be fed with an any number of arguments. + +In this example we don't set the score directly but emit an event so we can have centralized logic dealing with scores (it's for an EM, and while the reel motor is on, no scoring is skipped): + +![Trigger Event](trigger-event-example.png) + + +### On Pinball Event + +On the receiving end, this is the event node that is triggered when a pinball event node with the same event is executed. To continue the previous example, here the graph was triggered by a pinball event, which updates the score if the score reel motor is not running. + +![On Pinball Event](pinball-event-example.png) + + +## Displays + +Creating original *graphical* content for displays is not yet supported by VPE. We're looking into leveraging Unity's 2D engine to create content and send the result to the different output devices VPE supports. This will most likely be a system independent from visual scripting. + +Even if we have nothing that creates the graphical content, we've defined the APIs used to pass the data around. This allows us to already have a working system that supports number and text data. + +### Update Display + +This node takes in some data and sends it to one of the [displays defined in the GLE](xref:uvs_setup#displays). VPE supports segment displays and score reels, so the data can be numeric (score reels and segment displays) or alphanumeric (segment displays). + +This example shows how the display is updated for a simple one player EM machine. + +![Update Display](update-display-example.png) + +The score reel animation is handled by the component driving the reel. It's also on component level where you can define the speed and delays of the score reel animation. diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/on-coil-changed.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/on-coil-changed.png new file mode 100644 index 000000000..98a13aa17 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/on-coil-changed.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/on-switch-enabled.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/on-switch-enabled.png new file mode 100644 index 000000000..3e714547b Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/on-switch-enabled.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/on-variable-changed-example.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/on-variable-changed-example.png new file mode 100644 index 000000000..383d284a6 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/on-variable-changed-example.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/pinball-event-example.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/pinball-event-example.png new file mode 100644 index 000000000..d647da200 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/pinball-event-example.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/pulse-coil-example.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/pulse-coil-example.png new file mode 100644 index 000000000..f20299f0d Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/pulse-coil-example.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/set-coil-example.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/set-coil-example.png new file mode 100644 index 000000000..0ade61627 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/set-coil-example.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/set-lamp-example.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/set-lamp-example.png new file mode 100644 index 000000000..04d1d4684 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/set-lamp-example.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/set-variable-example.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/set-variable-example.png new file mode 100644 index 000000000..4bd653358 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/set-variable-example.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/setup.md b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/setup.md new file mode 100644 index 000000000..e666dead9 --- /dev/null +++ b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/setup.md @@ -0,0 +1,62 @@ +--- +uid: uvs_setup +title: Visual Scripting Setup +description: How set up the visual scripting GLE +--- + +# Setup + +In order to use visual scripting as your gamelogic engine, you need to add it as a component to your table. In order to do that, navigate in your hierarchy to the root node of your table, where you most likely still have the *Default Gamelogic Engine* added. If that's the case, remove it by clicking on the three dots and choose *Remove Component*. + +Then, add the visual scripting GLE component by (still in the inspector) clicking on *Add Component -> Visual Pinball -> Gamelogic Engine -> Visual Scripting Game Logic*. + +## Configuration + +Remember that a [gamelogic engine](xref:gamelogic_engine) doesn't know about your playfield. It communicates with the game through IDs that you assign in editor. We acknowledge that visual scripting is tied somewhat more closely to the playfield than, let's say, PinMAME, because it's equally created in the editor. However, there are multiple reasons to use the same pattern as in other GLEs, which is what visual scripting does. + +So instead of interacting with playfield items directly, you need to define the displays, switches, coils and lamps in the visual scripting component. Once added, you can link them with the respective editors to the playfield. + +### Displays + +Display Configuration + +The first configuration you'll see in the inspector is which type of displays your game expects to find on the playfield. You can have multiple displays. For each display, you define the size and which type of formats it must able to handle. These are the possible types: + +- *DMD2, DMD4, DMD8 and DMD24* is pixel data, where the number is the number of bits: + - 2, 4 and 8 bits equal 4, 16 and 256 respectively gray tones + - 24 means RGB24, standing for 16mio colors. +- *Segment* are segment displays, which take in an array of binary data, where each two bytes represent the segments of one digit. +- *Alphanumeric* are displays that can render text on one line. +- *Numeric* displays only render digits. + +The display components that VPE provides understand multiple formats, e.g. our segment display component takes in segment data, but also text and numeric data. In the future, we'll also add a simple numeric and text renderer for DMDs. + +### Switches + +Switch Definitions + +In this section you define the switches that appear in the [Switch Manager](xref:switch_manager) when you auto-populate them. It's also the source for all the [switch-related nodes](xref:uvs_node_reference#switches) that VPE provides within your visual scripting graphs. + +### Coils + +Coil Definitions + +Here you define your coils. Same concept as for switches. These are the coils you'll see in the [Coil Manager](xref:coil_manager), and they are the source when looking up IDs in visual scripting's [coil nodes](xref:uvs_node_reference#coils). + +### Lamps + +Lamp Definitions + +Same goes for the lamps. They appear in the [Lamp Manager](xref:lamp_manager) and are used in the [lamp nodes](xref:uvs_node_reference#lamps) that VPE provides. + +It's worth noting that VPE currently doesn't provide any tools for creating light shows. Once we do, we'll provide additional lamp nodes to trigger and blend them together. + +### Events + +Event Definitions + +Unity provides [Custom Events](https://docs.unity3d.com/Packages/com.unity.visualscripting@1.7/manual/vs-events-reference.html#custom-events) which allow you to globally pass data across graphs. While this is a valid approach, VPE provides its own event nodes called *Pinball Events*, which allow you to declare your events. This has the benefit of being able to simply pick an event from a drop-down when configuring the nodes, as opposed to having to remember or separately document existing event names. + +### Variables + +It's worth describing variables more in detail, which we do in the [next section](xref:uvs_variables). \ No newline at end of file diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/switch-lamp-example.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/switch-lamp-example.png new file mode 100644 index 000000000..e27504e44 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/switch-lamp-example.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/switch-lamp-example2.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/switch-lamp-example2.png new file mode 100644 index 000000000..9cf393c04 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/switch-lamp-example2.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/switches.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/switches.png new file mode 100644 index 000000000..b5e16e935 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/switches.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/trigger-event-example.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/trigger-event-example.png new file mode 100644 index 000000000..e1ad93879 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/trigger-event-example.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/update-display-example.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/update-display-example.png new file mode 100644 index 000000000..d74567e0b Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/update-display-example.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/variables-gle-inspector.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/variables-gle-inspector.png new file mode 100644 index 000000000..161c7ea73 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/variables-gle-inspector.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/variables-light-events.png b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/variables-light-events.png new file mode 100644 index 000000000..a7448ff58 Binary files /dev/null and b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/variables-light-events.png differ diff --git a/VisualPinball.Unity/Documentation~/plugins/visual-scripting/variables.md b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/variables.md new file mode 100644 index 000000000..9a53fac4e --- /dev/null +++ b/VisualPinball.Unity/Documentation~/plugins/visual-scripting/variables.md @@ -0,0 +1,94 @@ +--- +uid: uvs_variables +title: Visual Scripting Variables +description: How to deal with runtime variables +--- + +# Variables + +As you create your game logic, you'll want to read and write data such as the player's score, or the number of balls per game. There are two ways of doing that: + +1. Using VPE's game- and player variables. +2. Using Unity's [visual scripting variables](https://docs.unity3d.com/Packages/com.unity.visualscripting@1.8/manual/vs-variables.html). + +## VPE Variables + +A common pattern in pinball games is the notion of multiple players: Player one starts the game, scores some points, drains the ball, then player two starts with zero points, scores some as well, drains the ball, and when it's back to player one, player one's score should be displayed again. + +This means that we need something that tracks the score (and many other things) *per player*. We should then be able to easily switch from one player to another, and we want the playfield and the display to be automatically updated when that happens. + +VPE provides its own type of variables for that, and they come in two flavors: + +- **Player Variables** are as described above, scoped *per player*. Here, you would be storing data such as the *current score*. +- **Table Variables** are *global* to the table. You would use them for things like *balls per game*. + +In the visual scripting GLE, you can define the structure of both types of variables. Then, in the graph, you'll get events for every defined variable that you can use to trigger the desired action. + +If you're testing gameplay in the editor, the current values of both player and table variables are shown in the inspector and get updated in real time. + +> [!NOTE] +> In the future, table variables could also marked as *editable*, so the player app could allow the user to change them. + + +### Setup + +Variable Definitions + +In the hierarchy, select the GameObject where you added the visual scripting GLE (usually the root node of the table). In there, you'll find two sections, *Player Variables* and *Table Variables*. + +You can define as many as you want for each. You'll need a name and a variable type, for which [`string`](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/reference-types#the-string-type), [`int`](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/integral-numeric-types), [`float`](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types) and [`bool`](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/bool) are supported. + +
 

 

+ +> [!NOTE] +> You can use any human-readable name (with spaces and so on), since variables are referenced by an internal ID. You can also rename them later, but changing the *type* later might result in issues. + +### Accessing Variables + +Once you have declared your variables, you can use them in your graphs. In the graph editor, those nodes can be found under *Visual Pinball/Variables* and *Events/Visual Pinball* for the event node. + +There are two sets of nodes, one for player variables and one for table variables. They are identical apart from two additional player variable nodes described in the *Player State* section below. + +- **Get Variable** contains an output with the current value of your variable. +- **Set Variable** contains an input with the new value of your variable. When executed, it applies that value to the variable. +- **Increase Variable** adds a number to a variable of type float or integer. +- **On Variable Changed** is an event node that triggers when a given variable changes. + +#### Player State + +We call *player state* the set of player variables assigned to a player. There are as many player states as there are players in a running game, but only one active state. You can create new states whenever you want (typically when the start button is pressed during the first ball, and there are enough credits). + +When creating a new state, you can either explicitly define a player ID (it's an integer), or automatically create one. You can then switch states by either indicating the player ID, or just "the next" player (which will cycle to the first player if the last player is active). + +You currently cannot explicitly destroy player states, but you can tell the player state creation node to destroy all previous states prior to creation. This is because typically, your player state persists even after the game and gets reset only when a new game begins. + +#### Synchronizing State + +In most of your graphs, you should be reading and writing variables only. For example, if you increase the player's score, you shouldn't have to worry about updating the display where the score is shown. You just update the *score* player variable, that's it. + +The idea here is to separate the pure game logic from what's going on on the playfield and the display. This is where events come in. As described earlier, there is an *On Variable Changed* event that you can put in any graph. This event triggers every time you change the value of a given player- or table variable, no matter from which subgraph. + +So in this example, you would have one subgraph that would handle display updates. This graph would contain event nodes for all relevant variables that have any effect on the display, and update the display accordingly. + +> [!NOTE] +> In computer science, this is called *SoC*, or [Separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns). It means that each section (in our case, a subgraph) should only care about one specific thing, and not about any of the side effects. + +Besides not having to think about all the consequences every time you change a variable, this patterns also gives you a free perk: When you switch players, VPE will automatically trigger events for all player variables that changed, which will result in the display showing the correct score, without the need of any further logic from your side. + +Here is an example of the graph that synchronizes the light variables of Gottlieb's Volley with the lamps on the playfield: + +![Variable to Lamp mapping](variables-light-events.png) + + +#### Persisting Variables + +You might want to persist state across launches, i.e. have them written to disk. Examples are high scores or game settings. This is still work in progress. + + +## Unity Visual Scripting Variables + +For everything game related, we recommend using VPE variables as decribed above. However, there is a use case where using Unity's built-in variable system might be necessary. + +Imagine you have created somewhat complex in a graph and you want to re-use that logic in different ways, by passing different arguments to that graph. Like a function that takes in parameters. + +In this case you should rely on the graph's [data ports](https://docs.unity3d.com/Packages/com.unity.visualscripting@1.7/manual/vs-add-triggers-data-graph.html) that allow passing data to graphs, which you then can access through the *Input* node. \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Display.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/Display.meta new file mode 100644 index 000000000..47926b8d3 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Display.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d85b88f5d19744b8828696ce183bcfeb +timeCreated: 1644670283 \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DisplayInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Display/DisplayInspector.cs similarity index 100% rename from VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DisplayInspector.cs rename to VisualPinball.Unity/VisualPinball.Unity.Editor/Display/DisplayInspector.cs diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Display/DisplayInspector.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/Display/DisplayInspector.cs.meta new file mode 100644 index 000000000..ef4dfb124 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Display/DisplayInspector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1770a0597953d3a41a1968b698112849 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DotMatrixDisplayInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Display/DotMatrixDisplayInspector.cs similarity index 100% rename from VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DotMatrixDisplayInspector.cs rename to VisualPinball.Unity/VisualPinball.Unity.Editor/Display/DotMatrixDisplayInspector.cs diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DotMatrixDisplayInspector.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/Display/DotMatrixDisplayInspector.cs.meta similarity index 83% rename from VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DotMatrixDisplayInspector.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity.Editor/Display/DotMatrixDisplayInspector.cs.meta index 573fa08e2..f276466d6 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DotMatrixDisplayInspector.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Display/DotMatrixDisplayInspector.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d7cdfa169d7568f4081e03ef1f47fefb +guid: 5242763464961c74583db37e58321516 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Display/ScoreReelDisplayInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Display/ScoreReelDisplayInspector.cs new file mode 100644 index 000000000..df354d35d --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Display/ScoreReelDisplayInspector.cs @@ -0,0 +1,46 @@ +// Visual Pinball Engine +// Copyright (C) 2022 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +using UnityEditor; +using UnityEngine; + +namespace VisualPinball.Unity.Editor +{ + [CustomEditor(typeof(ScoreReelDisplayComponent)), CanEditMultipleObjects] + public class ScoreReelDisplayInspector : UnityEditor.Editor + { + private SerializedProperty _idProperty; + private SerializedProperty _speedProperty; + private SerializedProperty _waitProperty; + private SerializedProperty _reelObjectsProperty; + + private void OnEnable() + { + _idProperty = serializedObject.FindProperty(nameof(ScoreReelDisplayComponent._id)); + _speedProperty = serializedObject.FindProperty(nameof(ScoreReelDisplayComponent.Speed)); + _waitProperty = serializedObject.FindProperty(nameof(ScoreReelDisplayComponent.Wait)); + _reelObjectsProperty = serializedObject.FindProperty(nameof(ScoreReelDisplayComponent.ReelObjects)); + } + + public override void OnInspectorGUI() + { + EditorGUILayout.PropertyField(_idProperty, new GUIContent("ID")); + EditorGUILayout.PropertyField(_speedProperty); + EditorGUILayout.PropertyField(_waitProperty); + EditorGUILayout.PropertyField(_reelObjectsProperty); + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Display/ScoreReelDisplayInspector.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/Display/ScoreReelDisplayInspector.cs.meta new file mode 100644 index 000000000..399dba8d9 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Display/ScoreReelDisplayInspector.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: cb6285219077b6b4c9993a5c5ac827a2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/SegmentDisplayInspector.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Display/SegmentDisplayInspector.cs similarity index 100% rename from VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/SegmentDisplayInspector.cs rename to VisualPinball.Unity/VisualPinball.Unity.Editor/Display/SegmentDisplayInspector.cs diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/SegmentDisplayInspector.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/Display/SegmentDisplayInspector.cs.meta similarity index 83% rename from VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/SegmentDisplayInspector.cs.meta rename to VisualPinball.Unity/VisualPinball.Unity.Editor/Display/SegmentDisplayInspector.cs.meta index 5da9ced79..ccc729116 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/SegmentDisplayInspector.cs.meta +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Display/SegmentDisplayInspector.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: d701503b2a5484949b1bfc97900900d4 +guid: 26ce9ff820eabf546a028521390554dc MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DisplayInspector.cs.meta b/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DisplayInspector.cs.meta deleted file mode 100644 index a9618dcd6..000000000 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Inspectors/DisplayInspector.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 59a47ed5de914df180f1a52ce0dc43f7 -timeCreated: 1617047811 \ No newline at end of file diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Coil/CoilManager.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Coil/CoilManager.cs index 912bc69f0..0104eff58 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Coil/CoilManager.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Coil/CoilManager.cs @@ -182,7 +182,7 @@ private void RefreshCoilIds() private GamelogicEngineCoil[] GetAvailableEngineCoils() { var gle = TableComponent.gameObject.GetComponent(); - return gle == null ? Array.Empty() : gle.AvailableCoils; + return gle == null ? Array.Empty() : gle.RequestedCoils; } #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampListViewItemRenderer.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampListViewItemRenderer.cs index e1dbeb577..b6dfc3757 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampListViewItemRenderer.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampListViewItemRenderer.cs @@ -25,11 +25,11 @@ namespace VisualPinball.Unity.Editor { - public class LampListViewItemRenderer : ListViewItemRenderer + public class LampListViewItemRenderer : ListViewItemRenderer { protected override List GleItems => _gleLamps; protected override GamelogicEngineLamp InstantiateGleItem(string id) => new GamelogicEngineLamp(id); - protected override Texture2D StatusIcon(float status) => Icons.Light(IconSize.Small, status > 0 ? IconColor.Orange : IconColor.Gray); + protected override Texture2D StatusIcon(LampState status) => Icons.Light(IconSize.Small, status.IsOn ? IconColor.Orange : IconColor.Gray); private enum LampListColumn { @@ -101,12 +101,11 @@ protected override void OnIconClick(LampListData data, bool pressedDown) var lamp = player.Lamp(data.Device); lamp?.OnChange(pressedDown); if (player.LampStatuses.ContainsKey(data.Id)) { - - player.LampStatuses[data.Id] = pressedDown ? 1 : 0; + player.LampStatuses[data.Id].IsOn = pressedDown; } } - private void RenderCoilId(Dictionary lampStatuses, LampListData lampListData, Rect cellRect) + private void RenderCoilId(Dictionary lampStatuses, LampListData lampListData, Rect cellRect) { // add some padding cellRect.x = cellRect.width - 45; @@ -125,7 +124,7 @@ private void RenderCoilId(Dictionary lampStatuses, LampListData l } var statusAvail = Application.isPlaying && lampStatuses != null && lampStatuses.ContainsKey(lampListData.Id); - var icon = Icons.Coil(IconSize.Small, statusAvail && lampStatuses[lampListData.Id] > 0 ? IconColor.Orange : IconColor.Gray); + var icon = Icons.Coil(IconSize.Small, statusAvail && lampStatuses[lampListData.Id].IsOn ? IconColor.Orange : IconColor.Gray); if (icon != null) { var guiColor = GUI.color; GUI.color = Color.clear; diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampManager.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampManager.cs index f663f82db..6ad8656aa 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampManager.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Lamp/LampManager.cs @@ -228,7 +228,7 @@ private void RefreshLampIds() private GamelogicEngineLamp[] GetAvailableEngineLamps() { var gle = TableComponent.gameObject.GetComponent(); - return gle == null ? Array.Empty() : gle.AvailableLamps; + return gle == null ? Array.Empty() : gle.RequestedLamps; } #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Switch/SwitchManager.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Switch/SwitchManager.cs index 838e9ffd4..2ec84ca68 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Switch/SwitchManager.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Managers/Switch/SwitchManager.cs @@ -185,7 +185,7 @@ private void RefreshSwitchIds() private GamelogicEngineSwitch[] GetAvailableEngineSwitches() { var gle = TableComponent.gameObject.GetComponent(); - return gle == null ? Array.Empty() : gle.AvailableSwitches; + return gle == null ? Array.Empty() : gle.RequestedSwitches; } #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs index 42c7b6aa8..c470ef0af 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Editor/Utils/Icons.cs @@ -85,6 +85,8 @@ public IconVariant(string name, IconSize size, IconColor color) private const string RampName = "ramp"; private const string RotatorName = "rotator"; private const string RubberName = "rubber"; + private const string ScoreReelName = "score_reel"; + private const string ScoreReelSimpleName = "score_reel_simple"; private const string SlingshotName = "slingshot"; private const string SpinnerName = "spinner"; private const string SurfaceName = "surface"; @@ -101,13 +103,19 @@ public IconVariant(string name, IconSize size, IconColor color) private const string SwitchEventName = "switch_event"; private const string LampEventName = "lamp_event"; private const string LampSeqName = "lamp_seq"; + private const string PlayerVariableName = "player_variable"; + private const string PlayerVariableEventName = "player_variable_event"; + private const string TableVariableName = "table_variable"; + private const string TableVariableEventName = "table_variable_event"; + private const string UpdateDisplayName = "update_display"; private static readonly string[] Names = { BallRollerName, BoltName, BumperName, CannonName, CoilName, DropTargetBankName, DropTargetName, FlasherName, FlipperName, GateName, HitTargetName, KeyName, KickerName, LightGroupName, LightName, MechName, MechPinMameName, PlayfieldName, PlugName, PlungerName, - PrimitiveName, RampName, RotatorName, RubberName, SlingshotName, SpinnerName, SurfaceName, SwitchNcName, SwitchNoName, TableName, - TeleporterName, TriggerName, TroughName, - CoilEventName, SwitchEventName, LampEventName, LampSeqName, MetalWireGuideName + PrimitiveName, RampName, RotatorName, RubberName, ScoreReelName, ScoreReelSimpleName, SlingshotName, SpinnerName, SurfaceName, + SwitchNcName, SwitchNoName, TableName, TeleporterName, TriggerName, TroughName, + CoilEventName, SwitchEventName, LampEventName, LampSeqName, MetalWireGuideName, + PlayerVariableName, PlayerVariableEventName, TableVariableName, TableVariableEventName, UpdateDisplayName }; private readonly Dictionary _icons = new Dictionary(); @@ -175,6 +183,8 @@ private static IIconLookup[] GetLookups() { public static Texture2D Ramp(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(RampName, size, color); public static Texture2D Rotator(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(RotatorName, size, color); public static Texture2D Rubber(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(RubberName, size, color); + public static Texture2D ScoreReel(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(ScoreReelName, size, color); + public static Texture2D ScoreReelSimple(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(ScoreReelSimpleName, size, color); public static Texture2D Slingshot(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(SlingshotName, size, color); public static Texture2D Spinner(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(SpinnerName, size, color); public static Texture2D Surface(IconSize size = IconSize.Large, IconColor color = IconColor.Gray) => Instance.GetItem(SurfaceName, size, color); @@ -188,6 +198,11 @@ private static IIconLookup[] GetLookups() { public static Texture2D SwitchEvent => Instance.GetItem(SwitchEventName, IconSize.Large, IconColor.Colored); public static Texture2D LampEvent => Instance.GetItem(LampEventName, IconSize.Large, IconColor.Colored); public static Texture2D LampSequence => Instance.GetItem(LampSeqName, IconSize.Large, IconColor.Colored); + public static Texture2D PlayerVariable => Instance.GetItem(PlayerVariableName, IconSize.Large, IconColor.Colored); + public static Texture2D PlayerVariableEvent => Instance.GetItem(PlayerVariableEventName, IconSize.Large, IconColor.Colored); + public static Texture2D TableVariable => Instance.GetItem(TableVariableName, IconSize.Large, IconColor.Colored); + public static Texture2D TableVariableEvent => Instance.GetItem(TableVariableEventName, IconSize.Large, IconColor.Colored); + public static Texture2D UpdateDisplay => Instance.GetItem(UpdateDisplayName, IconSize.Large, IconColor.Colored); public static Texture2D ByComponent(T mb, IconSize size = IconSize.Large, IconColor color = IconColor.Gray) @@ -268,6 +283,8 @@ public Texture2D Lookup(T mb, IconSize size = IconSize.Large, IconColor color case RampComponent _: return Icons.Ramp(size, color); case RotatorComponent _: return Icons.Rotator(size, color); case RubberComponent _: return Icons.Rubber(size, color); + case ScoreReelDisplayComponent _: return Icons.ScoreReel(size, color); + case ScoreReelComponent _: return Icons.ScoreReelSimple(size, color); case SpinnerComponent _: return Icons.Spinner(size, color); case SlingshotComponent _: return Icons.Slingshot(size, color); case SurfaceComponent _: return Icons.Surface(size, color); @@ -330,6 +347,8 @@ public void DisableGizmoIcons() Icons.DisableGizmo(); Icons.DisableGizmo(); Icons.DisableGizmo(); + Icons.DisableGizmo(); + Icons.DisableGizmo(); Icons.DisableGizmo(); Icons.DisableGizmo(); Icons.DisableGizmo(); diff --git a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Rock.cs b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Rock.cs index fb33130d9..1aa1bee06 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Rock.cs +++ b/VisualPinball.Unity/VisualPinball.Unity.Patcher/Patcher/Tables/Rock.cs @@ -95,9 +95,6 @@ private static void SetupLights(GameObject tableGo, GameObject playfieldGo) AddLightGroup(tableGo, CreateEmptyGameObject(lightGroups, "Lower"), "gi6", "gi10"); - AddLightGroup(tableGo, CreateEmptyGameObject(lightGroups, "AllGI"), - "UpperLeft1", "UpperLeft2", "UpperRight", "Lower"); - AddLightGroup(tableGo, CreateEmptyGameObject(lightGroups, "Auxiliary"), "AL1a", "AL1b", "AL2a", "AL2b", "AL3a", "AL3b", "AL4a", "AL4b", "AL5a", "AL5b", "AL6a", "AL6b", "AL7a", "AL7b", "AL8a", "AL8b", diff --git a/VisualPinball.Unity/VisualPinball.Unity.Test/VisualPinball.Unity.Test.csproj b/VisualPinball.Unity/VisualPinball.Unity.Test/VisualPinball.Unity.Test.csproj index 3c5bb5d53..5193b96e5 100644 --- a/VisualPinball.Unity/VisualPinball.Unity.Test/VisualPinball.Unity.Test.csproj +++ b/VisualPinball.Unity/VisualPinball.Unity.Test/VisualPinball.Unity.Test.csproj @@ -24,9 +24,9 @@ - - - + + + @@ -36,7 +36,7 @@ - $(NuGetPackageRoot)\fluentassertions\6.3.0\lib\netstandard2.1\FluentAssertions.dll + $(NuGetPackageRoot)\fluentassertions\6.5.1\lib\netstandard2.1\FluentAssertions.dll ..\Plugins\.unity\UnityEngine.CoreModule.dll diff --git a/VisualPinball.Unity/VisualPinball.Unity/Common/TableSelector.cs b/VisualPinball.Unity/VisualPinball.Unity/Common/TableSelector.cs index 8cf584e8f..77503e989 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Common/TableSelector.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Common/TableSelector.cs @@ -15,6 +15,7 @@ // along with this program. If not, see . using System; +using Object = UnityEngine.Object; namespace VisualPinball.Unity { @@ -28,6 +29,20 @@ public TableComponent SelectedTable { set => SetSelectedTable(value); } + public TableComponent SelectedOrFirstTable + { + get { + if (HasSelectedTable) { + return _selectedTable; + } + var selectedTable = Object.FindObjectOfType(); + if (selectedTable) { + SetSelectedTable(selectedTable); + } + return selectedTable; + } + } + public void TableUpdated() { OnTableSelected?.Invoke(this, EventArgs.Empty); @@ -48,7 +63,7 @@ private TableSelector() { } - public static TableSelector Instance => _instance ?? (_instance = new TableSelector()); + public static TableSelector Instance => _instance ??= new TableSelector(); private void SetSelectedTable(TableComponent ta) { diff --git a/VisualPinball.Unity/VisualPinball.Unity/Display/DisplayComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Display/DisplayComponent.cs index 7d304122a..6d5591c02 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Display/DisplayComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Display/DisplayComponent.cs @@ -60,7 +60,6 @@ public void RegenerateMesh(bool flipX = false) mr.sharedMaterial.mainTexture = _texture; mr.sharedMaterial.SetTexture(DataProp, _texture); - var mf = gameObject.GetComponent(); if (mf == null) { mf = gameObject.AddComponent(); diff --git a/VisualPinball.Unity/VisualPinball.Unity/Display/ScoreReelComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Display/ScoreReelComponent.cs new file mode 100644 index 000000000..d4ec7352a --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Display/ScoreReelComponent.cs @@ -0,0 +1,75 @@ +// Visual Pinball Engine +// Copyright (C) 2022 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// ReSharper disable InconsistentNaming + +using System.Collections; +using UnityEngine; + +namespace VisualPinball.Unity +{ + public class ScoreReelComponent : MonoBehaviour + { + [HideInInspector] + public float Speed = 1; + + [HideInInspector] + public float Wait; + + private bool _running; + private int _nextPosition; + private int _remainingPositions; + + private float _currentRotation; + + public void AnimateTo(int position) + { + var numPositions = (position - _nextPosition + 10) % 10; + _remainingPositions += numPositions; + _nextPosition = position; + if (!_running) { + _running = true; + StartCoroutine(nameof(Rotate)); + } + } + + private IEnumerator Rotate() + { + while (_remainingPositions > 0) { + var lastPosition = (int)(_currentRotation / 36f); + _currentRotation += Time.deltaTime * Speed * 36f; + var currentPosition = (int)(_currentRotation / 36f); + _currentRotation %= 360f; + + if (currentPosition != lastPosition) { + + // stop on correct position + _currentRotation -= _currentRotation % 36f; + transform.localRotation = Quaternion.Euler(0, 0, _currentRotation); + + _remainingPositions--; + + yield return new WaitForSeconds(Wait / 1000f); + + } else { + transform.localRotation = Quaternion.Euler(0, 0, _currentRotation); + yield return null; + } + } + _running = false; + } + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/Display/ScoreReelComponent.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Display/ScoreReelComponent.cs.meta new file mode 100644 index 000000000..e15e14582 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Display/ScoreReelComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c8580245ee3efd04aac822f08fbac385 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 690cb6a39af01c243a459d20d333cb33, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/Display/ScoreReelDisplayComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Display/ScoreReelDisplayComponent.cs new file mode 100644 index 000000000..12105bba5 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Display/ScoreReelDisplayComponent.cs @@ -0,0 +1,126 @@ +// Visual Pinball Engine +// Copyright (C) 2022 freezy and VPE Team +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +// ReSharper disable InconsistentNaming + +using System; +using UnityEngine; + +namespace VisualPinball.Unity +{ + [AddComponentMenu("Visual Pinball/Display/Score Reel")] + public class ScoreReelDisplayComponent : DisplayComponent + { + public override string Id { get => _id; set => _id = value; } + + [Unit("positions/s")] + [Tooltip("Positions per second")] + public float Speed = 15; + + [Unit("ms")] + [Tooltip("Wait between positions in milliseconds")] + public float Wait = 30; + + [Tooltip("The reel components, from left to right.")] + public ScoreReelComponent[] ReelObjects; + + [SerializeField] public string _id = "display0"; + + private void Start() + { + foreach (var reelObject in ReelObjects) { + reelObject.Speed = Speed; + reelObject.Wait = Wait; + } + } + + public override void UpdateFrame(DisplayFrameFormat format, byte[] data) + { + var score = (int)BitConverter.ToSingle(data); + var digits = DigitArr(score); + var j = digits.Length - 1; + for (var i = ReelObjects.Length - 1; i >= 0; i--) { + if (j < 0) { + SetReel(ReelObjects[i], 0); + j--; + continue; + } + SetReel(ReelObjects[i], digits[j]); + j--; + } + } + + public override void Clear() + { + foreach (var reelObject in ReelObjects) { + reelObject.AnimateTo(0); + } + } + + private static void SetReel(ScoreReelComponent sr, int num) + { + sr.AnimateTo(num); + } + + private static int NumDigits(int n) { + if (n < 0) { + n = n == int.MinValue ? int.MaxValue : -n; + } + return n switch { + < 10 => 1, + < 100 => 2, + < 1000 => 3, + < 10000 => 4, + < 100000 => 5, + < 1000000 => 6, + < 10000000 => 7, + < 100000000 => 8, + < 1000000000 => 9, + _ => 10 + }; + } + + private static int[] DigitArr(int n) + { + var result = new int[NumDigits(n)]; + for (var i = result.Length - 1; i >= 0; i--) { + result[i] = n % 10; + n /= 10; + } + return result; + } + + #region Unused + + protected override Material CreateMaterial() + { + throw new NotImplementedException(); + } + public override void UpdateDimensions(int width, int height, bool flipX = false) + { + Debug.Log($"Reel of {width} requested."); + } + + public override Color LitColor { get; set; } + public override Color UnlitColor { get; set; } + protected override float MeshWidth { get; } + public override float MeshHeight { get; } + protected override float MeshDepth { get; } + public override float AspectRatio { get; set; } + + #endregion + } +} diff --git a/VisualPinball.Unity/VisualPinball.Unity/Display/ScoreReelDisplayComponent.cs.meta b/VisualPinball.Unity/VisualPinball.Unity/Display/ScoreReelDisplayComponent.cs.meta new file mode 100644 index 000000000..a607198f1 --- /dev/null +++ b/VisualPinball.Unity/VisualPinball.Unity/Display/ScoreReelDisplayComponent.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 99fe3667b915b4047915635f923aa6b3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {fileID: 2800000, guid: 792a87c8fea13a442a9485185dcc60d1, type: 3} + userData: + assetBundleName: + assetBundleVariant: diff --git a/VisualPinball.Unity/VisualPinball.Unity/Display/SegmentDisplayComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/Display/SegmentDisplayComponent.cs index 4ff6fa675..4f182f4e8 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Display/SegmentDisplayComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Display/SegmentDisplayComponent.cs @@ -19,7 +19,10 @@ using System; using System.Collections.Generic; +using System.Globalization; +using System.Text; using NLog; +using NLog.LayoutRenderers.Wrappers; using Unity.Mathematics; using UnityEngine; using Logger = NLog.Logger; @@ -239,8 +242,38 @@ protected override Material CreateMaterial() public override void UpdateFrame(DisplayFrameFormat format, byte[] source) { - var target = new ushort[source.Length / sizeof(short)]; - Buffer.BlockCopy(source, 0, target, 0, source.Length); + Debug.Log($"Getting segment data!"); + ushort[] target; + switch (format) { + case DisplayFrameFormat.Dmd2: + case DisplayFrameFormat.Dmd4: + case DisplayFrameFormat.Dmd8: + case DisplayFrameFormat.Dmd24: + Logger.Error("Segment displays cannot not render pixel data. Use a DMD display!"); + return; + + case DisplayFrameFormat.Segment: { + target = new ushort[source.Length / sizeof(short)]; + Buffer.BlockCopy(source, 0, target, 0, source.Length); + break; + } + + case DisplayFrameFormat.AlphaNumeric: { + var text = FixedLeft(Encoding.UTF8.GetString(source)); + target = FromAlphaNumeric(text); + break; + } + + case DisplayFrameFormat.Numeric: { + var num = BitConverter.ToSingle(source); + var text = FixedRight(num.ToString(CultureInfo.InvariantCulture)); + target = FromAlphaNumeric(text); + break; + } + default: + throw new ArgumentOutOfRangeException(nameof(format), format, null); + } + UpdateFrame(target); } @@ -258,7 +291,7 @@ public override void Clear() public void SetText(string text) { - var source = GenerateAlphaNumeric(text); + var source = FromAlphaNumeric(text); const int size = sizeof(short); var target = new byte[_numChars * size]; Buffer.BlockCopy(source, 0, target, 0, math.min(source.Length, _numChars) * size); @@ -292,7 +325,23 @@ private void UpdateFrame(IReadOnlyList data) _texture.Apply(); } - private static ushort[] GenerateAlphaNumeric(string text) + private string FixedRight(string str) + { + if (str.Length > _numChars) { + return str[^_numChars..]; + } + return str.Length < _numChars ? str.PadLeft(_numChars) : str; + } + + private string FixedLeft(string str) + { + if (str.Length > _numChars) { + return str[.._numChars]; + } + return str.Length < _numChars ? str.PadRight(_numChars) : str; + } + + private static ushort[] FromAlphaNumeric(string text) { var data = new ushort[text.Length]; for (var i = 0; i < text.Length; i++) { @@ -305,6 +354,19 @@ private static ushort[] GenerateAlphaNumeric(string text) return data; } + private static readonly Dictionary Numeric14Map = new Dictionary { + { '0', 0x443f }, + { '1', 0x406 }, + { '2', 0x85b }, + { '3', 0x80f }, + { '4', 0x866 }, + { '5', 0x1069 }, + { '6', 0x87d }, + { '7', 0x7 }, + { '8', 0x87f }, + { '9', 0x86f }, + }; + private static readonly Dictionary AlphaNumericMap = new Dictionary { { '0', 0x443f }, { '1', 0x406 }, diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/CoilPlayer.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/CoilPlayer.cs index 727153737..2d2dd47fa 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/CoilPlayer.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/CoilPlayer.cs @@ -14,16 +14,15 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#nullable enable + using System; using System.Collections.Generic; using System.Linq; -using UnityEngine; using NLog; -using Logger = NLog.Logger; - -#if UNITY_EDITOR using UnityEditor; -#endif +using UnityEngine; +using Logger = NLog.Logger; namespace VisualPinball.Unity { @@ -32,26 +31,25 @@ public class CoilPlayer /// /// Maps the coil component to the API class. /// - private readonly Dictionary _coilDevices = new Dictionary(); + private readonly Dictionary _coilDevices = new(); /// /// Maps the coil configuration ID to a destination. /// - private readonly Dictionary> _coilAssignments = new Dictionary>(); + private readonly Dictionary> _coilAssignments = new(); - private Player _player; - private TableComponent _tableComponent; - private IGamelogicEngine _gamelogicEngine; - private LampPlayer _lampPlayer; - private WirePlayer _wirePlayer; + private Player? _player; + private TableComponent? _tableComponent; + private IGamelogicEngine? _gamelogicEngine; + private LampPlayer? _lampPlayer; + private WirePlayer? _wirePlayer; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - internal Dictionary CoilStatuses { get; } = new Dictionary(); + internal Dictionary CoilStatuses { get; } = new(); internal void RegisterCoilDevice(ICoilDeviceComponent component, IApiCoilDevice coilDeviceApi) => _coilDevices[component] = coilDeviceApi; - internal IApiCoil Coil(ICoilDeviceComponent component, string coilItem) - => _coilDevices.ContainsKey(component) ? _coilDevices[component].Coil(coilItem) : null; + internal IApiCoil? Coil(ICoilDeviceComponent component, string coilItem) => _coilDevices.ContainsKey(component) ? _coilDevices[component].Coil(coilItem) : null; public void Awake(Player player, TableComponent tableComponent, IGamelogicEngine gamelogicEngine, LampPlayer lampPlayer, WirePlayer wirePlayer) { @@ -64,7 +62,7 @@ public void Awake(Player player, TableComponent tableComponent, IGamelogicEngine public void OnStart() { - if (_gamelogicEngine != null) { + if (_gamelogicEngine != null && _tableComponent != null) { var config = _tableComponent.MappingConfig; _coilAssignments.Clear(); foreach (var coilMapping in config.Coils) { @@ -97,6 +95,9 @@ public void OnStart() AssignCoilMapping(coilMapping, true); break; } + + default: + throw new ArgumentOutOfRangeException(); } } @@ -111,7 +112,7 @@ private void AssignCoilMapping(CoilMapping coilMapping, bool isLampCoil) if (!_coilAssignments.ContainsKey(coilMapping.Id)) { _coilAssignments[coilMapping.Id] = new List(); } - var hasDynamicWire = _tableComponent.MappingConfig.Wires.FirstOrDefault(w => + var hasDynamicWire = _tableComponent!.MappingConfig.Wires.FirstOrDefault(w => w.DestinationDevice == coilMapping.Device && w.DestinationDeviceItem == coilMapping.DeviceItem) != null; @@ -121,19 +122,21 @@ private void AssignCoilMapping(CoilMapping coilMapping, bool isLampCoil) private void HandleCoilEvent(object sender, CoilEventArgs coilEvent) { - if (_coilAssignments.ContainsKey(coilEvent.Id)) { - CoilStatuses[coilEvent.Id] = coilEvent.IsEnabled; + // always save state + CoilStatuses[coilEvent.Id] = coilEvent.IsEnabled; + // trigger coil if mapped + if (_coilAssignments.ContainsKey(coilEvent.Id)) { foreach (var destConfig in _coilAssignments[coilEvent.Id]) { if (destConfig.HasDynamicWire) { // goes back through the wire mapping, which will decide whether it has already sent the event or not - _wirePlayer.HandleCoilEvent(coilEvent.Id, coilEvent.IsEnabled); + _wirePlayer!.HandleCoilEvent(coilEvent.Id, coilEvent.IsEnabled); continue; } if (destConfig.IsLampCoil) { - _lampPlayer.HandleLampEvent(new LampEventArgs(coilEvent.Id, coilEvent.IsEnabled ? 1 : 0, true)); + _lampPlayer!.HandleCoilEvent(coilEvent.Id, coilEvent.IsEnabled); continue; } @@ -179,7 +182,7 @@ public void OnDestroy() #if UNITY_EDITOR private void RefreshUI() { - if (!_player.UpdateDuringGamplay) { + if (!_player!.UpdateDuringGamplay) { return; } @@ -192,8 +195,8 @@ private void RefreshUI() internal class CoilDestConfig { - public readonly ICoilDeviceComponent Device; - public readonly string DeviceItem; + public readonly ICoilDeviceComponent? Device; + public readonly string? DeviceItem; public readonly bool IsLampCoil; public readonly bool HasDynamicWire; diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/DisplayPlayer.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/DisplayPlayer.cs index ffd9e0abd..4d3677250 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/DisplayPlayer.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/DisplayPlayer.cs @@ -31,7 +31,7 @@ public class DisplayPlayer public void Awake(IGamelogicEngine gamelogicEngine) { _gamelogicEngine = gamelogicEngine; - _gamelogicEngine.OnDisplaysAvailable += HandleDisplayAvailable; + _gamelogicEngine.OnDisplaysRequested += HandleDisplayRequested; _gamelogicEngine.OnDisplayFrame += HandleFrameEvent; var dmds = Object.FindObjectsOfType(); @@ -41,9 +41,9 @@ public void Awake(IGamelogicEngine gamelogicEngine) } } - private void HandleDisplayAvailable(object sender, AvailableDisplays availableDisplays) + private void HandleDisplayRequested(object sender, RequestedDisplays requestedDisplays) { - foreach (var display in availableDisplays.Displays) { + foreach (var display in requestedDisplays.Displays) { if (_displayGameObjects.ContainsKey(display.Id)) { Logger.Info($"Updating display \"{display.Id}\" to {display.Width}x{display.Height}"); _displayGameObjects[display.Id].UpdateDimensions(display.Width, display.Height, display.FlipX); @@ -64,7 +64,7 @@ private void HandleFrameEvent(object sender, DisplayFrameData e) public void OnDestroy() { - _gamelogicEngine.OnDisplaysAvailable -= HandleDisplayAvailable; + _gamelogicEngine.OnDisplaysRequested -= HandleDisplayRequested; _gamelogicEngine.OnDisplayFrame -= HandleFrameEvent; } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Engine/DefaultGamelogicEngine.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Engine/DefaultGamelogicEngine.cs index ffff504e3..b3e65dd8a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Engine/DefaultGamelogicEngine.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Engine/DefaultGamelogicEngine.cs @@ -48,8 +48,7 @@ public class DefaultGamelogicEngine : MonoBehaviour, IGamelogicEngine public event EventHandler OnLampChanged; public event EventHandler OnLampsChanged; public event EventHandler OnSwitchChanged; - public event EventHandler OnLampColorChanged; - public event EventHandler OnDisplaysAvailable; + public event EventHandler OnDisplaysRequested; public event EventHandler OnDisplayFrame; public event EventHandler OnStarted; @@ -73,7 +72,7 @@ public class DefaultGamelogicEngine : MonoBehaviour, IGamelogicEngine private const string SwMotorStart = "s_motor_start"; private const string SwMotorEnd = "s_motor_end"; - public GamelogicEngineSwitch[] AvailableSwitches => _availableSwitches.ToArray(); + public GamelogicEngineSwitch[] RequestedSwitches => _availableSwitches.ToArray(); private readonly List _availableSwitches = new List { new GamelogicEngineSwitch(SwLeftFlipper) { Description = "Left Flipper (Button)", InputActionHint = InputConstants.ActionLeftFlipper }, new GamelogicEngineSwitch(SwRightFlipper) { Description = "Right Flipper (Button)", InputActionHint = InputConstants.ActionRightFlipper }, @@ -99,7 +98,9 @@ public class DefaultGamelogicEngine : MonoBehaviour, IGamelogicEngine private const string CoilTroughEject = "c_trough_eject"; private const string CoilMotorStart = "c_motor_start"; - public GamelogicEngineCoil[] AvailableCoils => _availableCoils.ToArray(); + public DisplayConfig[] RequiredDisplays => new[] { new DisplayConfig(DisplayDmd, DmdWidth, DmdHeight) }; + + public GamelogicEngineCoil[] RequestedCoils => _availableCoils.ToArray(); private readonly List _availableCoils = new List { new GamelogicEngineCoil(CoilLeftFlipperMain) { Description = "Left Flipper (Main)", DeviceHint = "^(LeftFlipper|LFlipper|FlipperLeft|FlipperL)$", DeviceItemHint = FlipperComponent.MainCoilItem }, new GamelogicEngineCoil(CoilLeftFlipperHold) { Description = "Left Flipper (Hold)", DeviceHint = "^(LeftFlipper|LFlipper|FlipperLeft|FlipperL)$", DeviceItemHint = FlipperComponent.HoldCoilItem }, @@ -134,7 +135,7 @@ public class DefaultGamelogicEngine : MonoBehaviour, IGamelogicEngine private const string LampRedBumper = "l_bumper"; - public GamelogicEngineLamp[] AvailableLamps { get; } = { + public GamelogicEngineLamp[] RequestedLamps { get; } = { new GamelogicEngineLamp(GiSlingshotRightLower) { Description = "Right Slingshot (lower)", DeviceHint = "gi1$" }, new GamelogicEngineLamp(GiSlingshotRightUpper) { Description = "Right Slingshot (upper)", DeviceHint = "gi2$" }, new GamelogicEngineLamp(GiSlingshotLeftLower) { Description = "Left Slingshot (lower)", DeviceHint = "gi3$" }, @@ -189,7 +190,7 @@ public void OnInit(Player player, TableApi tableApi, BallManager ballManager) _player = player; _ballManager = ballManager; - OnDisplaysAvailable?.Invoke(this, new AvailableDisplays(new DisplayConfig(DisplayDmd, DmdWidth, DmdHeight))); + OnDisplaysRequested?.Invoke(this, new RequestedDisplays(new DisplayConfig(DisplayDmd, DmdWidth, DmdHeight))); // debug print stuff OnCoilChanged += DebugPrintCoil; @@ -302,19 +303,14 @@ public void SetLamp(string id, float value, bool isCoil = false, LampSource sour OnLampChanged?.Invoke(this, new LampEventArgs(id, value, isCoil, source)); } - public void SetLamp(string id, Color color) - { - OnLampColorChanged?.Invoke(this, new LampColorEventArgs(id, color)); - } - public void SetLamps(LampEventArgs[] values) { OnLampsChanged?.Invoke(this, new LampsEventArgs(values)); } - public float GetLamp(string id) + public LampState GetLamp(string id) { - return _player.LampStatuses.ContainsKey(id) ? _player.LampStatuses[id] : 0; + return _player.LampStatuses.ContainsKey(id) ? _player.LampStatuses[id] : LampState.Default; } public bool GetSwitch(string id) diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Engine/IGamelogicEngine.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Engine/IGamelogicEngine.cs index 8a769e166..c2fdad61e 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Engine/IGamelogicEngine.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Engine/IGamelogicEngine.cs @@ -33,7 +33,15 @@ public interface IGamelogicEngine : IGamelogicBridge #region Displays - event EventHandler OnDisplaysAvailable; + /// + /// Emitted during gameplay when a new display is requested. + /// + /// + /// + /// The reason we don't have a `AvailableDisplays` at design time is because + /// most GLEs only know about their displays when they start the game. + /// + event EventHandler OnDisplaysRequested; event EventHandler OnDisplayFrame; #endregion @@ -43,12 +51,12 @@ public interface IGamelogicEngine : IGamelogicBridge /// /// A list of available switches supported by the game logic engine. /// - GamelogicEngineSwitch[] AvailableSwitches { get; } + GamelogicEngineSwitch[] RequestedSwitches { get; } /// /// Enables or disables a switch. /// - /// Name of the switch, as defined by . + /// Name of the switch, as defined by . /// True for normally closed (NC) i.e. contact, a.k.a. "enabled". False for normally open (NO), i.e. no contact, a.k.a "off". void Switch(string id, bool isClosed); @@ -59,7 +67,7 @@ public interface IGamelogicEngine : IGamelogicBridge /// /// A list of available lamps. /// - GamelogicEngineLamp[] AvailableLamps { get; } + GamelogicEngineLamp[] RequestedLamps { get; } /// /// Triggered when a lamp is turned on or off. @@ -76,11 +84,6 @@ public interface IGamelogicEngine : IGamelogicBridge /// event EventHandler OnLampsChanged; - /// - /// Triggered when the an RGB lamp changes color. - /// - event EventHandler OnLampColorChanged; - #endregion #region Coils @@ -88,7 +91,7 @@ public interface IGamelogicEngine : IGamelogicBridge /// /// A list of available coils. /// - GamelogicEngineCoil[] AvailableCoils { get; } + GamelogicEngineCoil[] RequestedCoils { get; } /// /// Triggered when a coil is enabled or disabled. @@ -106,6 +109,9 @@ public interface IGamelogicEngine : IGamelogicBridge #endregion + /// + /// This event is triggered when the gamelogic has booted up and is ready to handle events. + /// event EventHandler OnStarted; } @@ -116,33 +122,34 @@ public interface IGamelogicEngine : IGamelogicBridge public interface IGamelogicBridge { void SetCoil(string id, bool isEnabled); + void SetLamp(string id, float value, bool isCoil = false, LampSource source = LampSource.Lamp); - void SetLamp(string id, Color color); bool GetSwitch(string id); bool GetCoil(string id); - float GetLamp(string id); + LampState GetLamp(string id); public event EventHandler OnSwitchChanged; // todo displays } - public class AvailableDisplays + public class RequestedDisplays { public readonly DisplayConfig[] Displays; - public AvailableDisplays(DisplayConfig[] availableDisplays) + public RequestedDisplays(DisplayConfig[] availableDisplays) { Displays = availableDisplays; } - public AvailableDisplays(DisplayConfig config) + public RequestedDisplays(DisplayConfig config) { Displays = new [] { config }; } } + [Serializable] public class DisplayConfig { public readonly string Id; @@ -180,6 +187,8 @@ public enum DisplayFrameFormat Dmd8, // 8-bit (0-255) Dmd24, // rgb (3x 0-255) Segment, + AlphaNumeric, // gets a byte-array converted string + Numeric // gets a byte-array converted float } public class DisplayFrameData @@ -200,7 +209,7 @@ public DisplayFrameData(string id, DisplayFrameFormat format, byte[] data) public readonly struct CoilEventArgs { /// - /// Id of the coil, as defined by . + /// Id of the coil, as defined by . /// public readonly string Id; @@ -219,13 +228,14 @@ public CoilEventArgs(string id, bool isEnabled) public readonly struct LampEventArgs { /// - /// Id of the lamp, as defined by . + /// Id of the lamp, as defined by . /// public readonly string Id; /// - /// Value of the lamp, between 0.0 and 1.0. - /// a fading light. + /// The intensity of the light. The range is dependent on the GLE, + /// i.e. PinMAME sends 0-255 or sometimes 0-8 for GI. MPF sends 0-1. + /// This value get normalized *after* the mapping. /// public readonly float Value; @@ -239,7 +249,7 @@ public readonly struct LampEventArgs /// public readonly bool IsCoil; - public LampEventArgs(string id, int value, LampSource source = LampSource.Lamp) + public LampEventArgs(string id, float value, LampSource source = LampSource.Lamp) { Id = id; Value = value; @@ -256,25 +266,6 @@ public LampEventArgs(string id, float value, bool isCoil, LampSource source = La } } - public readonly struct LampColorEventArgs - { - /// - /// Id of the lamp, as defined by . - /// - public readonly string Id; - - /// - /// New color - /// - public readonly Color Color; - - public LampColorEventArgs(string id, Color color) - { - Id = id; - Color = color; - } - } - public readonly struct LampsEventArgs { public readonly LampEventArgs[] LampsChanged; diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/LampPlayer.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/LampPlayer.cs index ff00248f8..d774dcb53 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/LampPlayer.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/LampPlayer.cs @@ -14,19 +14,16 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +#nullable enable + using System; using System.Collections.Generic; +using NLog; using UnityEngine; using VisualPinball.Engine.Game.Engines; -using VisualPinball.Engine.Math; -using Color = UnityEngine.Color; -using NLog; +using Color = VisualPinball.Engine.Math.Color; using Logger = NLog.Logger; -#if UNITY_EDITOR -using UnityEditor; -#endif - namespace VisualPinball.Unity { public class LampPlayer @@ -34,28 +31,27 @@ public class LampPlayer /// /// List of all registered lamp APIs. /// - private readonly Dictionary _lamps = new Dictionary(); + private readonly Dictionary _lamps = new(); /// /// Links the GLE's IDs to the lamps. /// - private readonly Dictionary> _lampAssignments = new Dictionary>(); + private readonly Dictionary> _lampAssignments = new(); /// /// Links the GLE's IDs to the mappings. /// - private readonly Dictionary> _lampMappings = new Dictionary>(); + private readonly Dictionary> _lampMappings = new(); - private Player _player; - private TableComponent _tableComponent; - private IGamelogicEngine _gamelogicEngine; + private Player? _player; + private TableComponent? _tableComponent; + private IGamelogicEngine? _gamelogicEngine; private static readonly Logger Logger = LogManager.GetCurrentClassLogger(); - internal IApiLamp Lamp(ILampDeviceComponent component) - => _lamps.ContainsKey(component) ? _lamps[component] : null; + internal IApiLamp? Lamp(ILampDeviceComponent component) => _lamps.ContainsKey(component) ? _lamps[component] : null; - internal Dictionary LampStatuses { get; } = new Dictionary(); + internal Dictionary LampStates { get; } = new(); internal void RegisterLamp(ILampDeviceComponent component, IApiLamp lampApi) => _lamps[component] = lampApi; public void Awake(Player player, TableComponent tableComponent, IGamelogicEngine gamelogicEngine) @@ -68,7 +64,7 @@ public void Awake(Player player, TableComponent tableComponent, IGamelogicEngine public void OnStart() { if (_gamelogicEngine != null) { - var config = _tableComponent.MappingConfig; + var config = _tableComponent!.MappingConfig; _lampAssignments.Clear(); _lampMappings.Clear(); foreach (var lampMapping in config.Lamps) { @@ -81,74 +77,137 @@ public void OnStart() AssignLampMapping(lampMapping); // turn it off - if (_lamps.ContainsKey(lampMapping.Device)) { - _lamps[lampMapping.Device].OnLamp(0f, ColorChannel.Alpha); + HandleLampEvent(lampMapping.Id, LampStatus.Off); } } if (_lampAssignments.Count > 0) { _gamelogicEngine.OnLampChanged += HandleLampEvent; _gamelogicEngine.OnLampsChanged += HandleLampsEvent; - _gamelogicEngine.OnLampColorChanged += HandleLampColorEvent; } } } - public void HandleLampEvent(LampEventArgs lampEvent) + private void HandleLampsEvent(object sender, LampsEventArgs lampsEvent) + { + foreach (var lampEvent in lampsEvent.LampsChanged) { + Apply(lampEvent.Id, lampEvent.Source, lampEvent.IsCoil, (state, lamp, mapping) => ApplyValue(lampEvent.Id, lampEvent.Value, state, lamp, mapping)); + } + } + + private void HandleLampEvent(object sender, LampEventArgs lampEvent) + { + Apply(lampEvent.Id, lampEvent.Source, lampEvent.IsCoil, (state, lamp, mapping) => ApplyValue(lampEvent.Id, lampEvent.Value, state, lamp, mapping)); + } + + public void HandleLampEvent(string id, float value) { - if (_lampAssignments.ContainsKey(lampEvent.Id)) { - foreach (var component in _lampAssignments[lampEvent.Id]) { - var mapping = _lampMappings[lampEvent.Id][component]; - if (mapping.Source != lampEvent.Source || mapping.IsCoil != lampEvent.IsCoil) { + Apply(id, LampSource.Lamp, false, (state, lamp, mapping) => ApplyValue(id, value, state, lamp, mapping)); + } + + public void HandleLampEvent(string id, LampStatus status) + { + Apply(id, LampSource.Lamp, false, (state, lamp, _) => ApplyStatus(id, status, state, lamp)); + } + + public void HandleLampEvent(string id, Color color) + { + Apply(id, LampSource.Lamp, false, (state, lamp, _) => ApplyColor(id, color, state, lamp)); + } + + public void HandleCoilEvent(string id, bool isEnabled) + { + Apply(id, LampSource.Lamp, true, (state, lamp, _) => ApplyStatus(id, isEnabled ? LampStatus.On : LampStatus.Off, state, lamp)); + } + + private void Apply(string id, LampSource lampSource, bool isCoil, Action action) + { + if (_lampAssignments.ContainsKey(id)) { + foreach (var component in _lampAssignments[id]) { + var mapping = _lampMappings[id][component]; + if (mapping.Source != lampSource || mapping.IsCoil != isCoil) { // so, if we have a coil here that happens to have the same name as a lamp, // or a GI light with the same name as an other lamp, skip. continue; } if (_lamps.ContainsKey(component)) { var lamp = _lamps[component]; - var value = 0f; - var channel = ColorChannel.Alpha; - switch (mapping.Type) { - case LampType.SingleOnOff: - value = lampEvent.Value / 255f; - break; - - case LampType.Rgb: - value = lampEvent.Value / 255f; // todo test - - break; - case LampType.RgbMulti: - value = lampEvent.Value / 255f; // todo test - channel = mapping.Channel; - break; - - case LampType.SingleFading: - value = lampEvent.Value / mapping.FadingSteps; - break; - - default: - Logger.Error($"Unknown mapping type \"{mapping.Type}\" of lamp ID {lampEvent.Id} for light {component}."); - break; - } - LampStatuses[lampEvent.Id] = value; - lamp.OnLamp(value, channel); + var state = LampStates[id]; + action(state, lamp, mapping); } } + + #if UNITY_EDITOR + RefreshUI(); + #endif + } else { + if (!LampStates.ContainsKey(id)) { + LampStates[id] = LampState.Default; + } + action(LampStates[id], null, null); } - else { - LampStatuses[lampEvent.Id] = lampEvent.Value; + } + + private void ApplyStatus(string id, LampStatus status, LampState state, IApiLamp? lamp) + { + state.Status = status; + LampStates[id] = state; + lamp?.OnLamp(status); + } + + private void ApplyColor(string id, Color color, LampState state, IApiLamp? lamp) + { + state.Color.SetColorWithoutAlpha(color); + LampStates[id] = state; + lamp?.OnLamp(state.Color.ToUnityColor()); + } + + private void ApplyValue(string id, float value, LampState state, IApiLamp? lamp, LampMapping? mapping) + { + if (mapping == null) { + // if not mapped, there is no lamp, so just save the state. + // we do that by setting both status and intensity + state.IsOn = value > 0; + state.Color.Alpha = (int)value; + LampStates[id] = state; + return; } -#if UNITY_EDITOR - RefreshUI(); -#endif + switch (mapping.Type) { + case LampType.SingleOnOff: + state.IsOn = value > 0; + LampStates[id] = state; + lamp?.OnLamp(state.Status); + break; + + case LampType.Rgb: + state.Color.Alpha = (int)value; + LampStates[id] = state; + lamp?.OnLamp(state.Intensity); + break; + + case LampType.RgbMulti: + state.SetChannel(mapping.Channel, value / 255f); // todo test + LampStates[id] = state; + lamp?.OnLamp(state.Color.ToUnityColor()); + break; + + case LampType.SingleFading: + state.Intensity = value / mapping.FadingSteps; + LampStates[id] = state; + lamp?.OnLamp(state.Intensity); + break; + + default: + Logger.Error($"Unknown mapping type \"{mapping.Type}\" of lamp ID {id} for light {lamp}."); + break; + } } public void OnDestroy() { if (_lampAssignments.Count > 0 && _gamelogicEngine != null) { - _gamelogicEngine.OnLampColorChanged -= HandleLampColorEvent; _gamelogicEngine.OnLampChanged -= HandleLampEvent; _gamelogicEngine.OnLampsChanged -= HandleLampsEvent; } @@ -165,63 +224,17 @@ private void AssignLampMapping(LampMapping lampMapping) } _lampAssignments[id].Add(lampMapping.Device); _lampMappings[id][lampMapping.Device] = lampMapping; - LampStatuses[id] = 0f; - } - - private void HandleLampColorEvent(object sender, LampColorEventArgs lampEvent) - { - if (_lampAssignments.ContainsKey(lampEvent.Id)) { - foreach (var component in _lampAssignments[lampEvent.Id]) { - var mapping = _lampMappings[lampEvent.Id][component]; - if (_lamps.ContainsKey(component)) { - var lamp = _lamps[component]; - switch (mapping.Type) { - case LampType.Rgb: - lamp.OnLampColor(lampEvent.Color); - break; - - default: - Logger.Error($"Received an RGB event for lamp {component} but lamp mapping type is {mapping.Type} ({mapping.Id})"); - break; - } - - } else { - Logger.Error($"Cannot trigger unknown lamp {component}."); - } - } - - } else { - Logger.Error($"Should update unassigned lamp {lampEvent.Id}."); - } - } - - private void HandleLampsEvent(object sender, LampsEventArgs lampsEvent) - { - var colors = new Dictionary(); - var lamps = new Dictionary(); - foreach (var lampEvent in lampsEvent.LampsChanged) { - HandleLampEvent(lampEvent); - } - - foreach (var mappingId in colors.Keys) { - lamps[mappingId].Color = colors[mappingId]; - LampStatuses[mappingId] = colors[mappingId].grayscale; - } - } - - private void HandleLampEvent(object sender, LampEventArgs lampEvent) - { - HandleLampEvent(lampEvent); + LampStates[id] = new LampState(lampMapping.Device.LampStatus, lampMapping.Device.LampColor.ToEngineColor()); } #if UNITY_EDITOR private void RefreshUI() { - if (!_player.UpdateDuringGamplay) { + if (!_player!.UpdateDuringGamplay) { return; } - foreach (var manager in (EditorWindow[])Resources.FindObjectsOfTypeAll(Type.GetType("VisualPinball.Unity.Editor.LampManager, VisualPinball.Unity.Editor"))) { + foreach (var manager in (UnityEditor.EditorWindow[])Resources.FindObjectsOfTypeAll(Type.GetType("VisualPinball.Unity.Editor.LampManager, VisualPinball.Unity.Editor"))) { manager.Repaint(); } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs index c39cf8195..89c88f3a8 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Game/Player.cs @@ -24,6 +24,7 @@ using UnityEngine.InputSystem; using VisualPinball.Engine.Common; using VisualPinball.Engine.Game; +using VisualPinball.Engine.Game.Engines; using VisualPinball.Engine.VPT.Trigger; using Logger = NLog.Logger; @@ -47,6 +48,7 @@ public class Player : MonoBehaviour public List SwitchMapping => _tableComponent.MappingConfig.Switches; public List CoilMapping => _tableComponent.MappingConfig.Coils; + public List LampMapping => _tableComponent.MappingConfig.Lamps; public event EventHandler OnUpdate; @@ -64,6 +66,7 @@ public class Player : MonoBehaviour private readonly Dictionary _collidables = new Dictionary(); private readonly Dictionary _spinnables = new Dictionary(); private readonly Dictionary _slingshots = new Dictionary(); + private readonly Dictionary _droppables = new Dictionary(); internal readonly Dictionary FlipperTransforms = new Dictionary(); internal readonly Dictionary BumperSkirtTransforms = new Dictionary(); @@ -109,10 +112,14 @@ public class Player : MonoBehaviour public Dictionary SwitchStatuses => _switchPlayer.SwitchStatuses; public Dictionary CoilStatuses => _coilPlayer.CoilStatuses; - public Dictionary LampStatuses => _lampPlayer.LampStatuses; + public Dictionary LampStatuses => _lampPlayer.LampStates; public Dictionary WireStatuses => _wirePlayer.WireStatuses; public float3 Gravity => _playfieldComponent.Gravity; + public void SetLamp(string lampId, float value) => _lampPlayer.HandleLampEvent(lampId, value); + public void SetLamp(string lampId, LampStatus status) => _lampPlayer.HandleLampEvent(lampId, status); + public void SetLamp(string lampId, VisualPinball.Engine.Math.Color color) => _lampPlayer.HandleLampEvent(lampId, color); + #endregion #region Lifecycle @@ -199,7 +206,7 @@ private void OnDestroy() #endregion -#region Registrations + #region Registrations public void RegisterBumper(BumperComponent component, Entity entity) { @@ -354,6 +361,9 @@ private void Register(TApi api, MonoBehaviour component, Entity entity = d if (api is IApiSlingshot slingshot) { _slingshots[entity] = slingshot; } + if (api is IApiDroppable droppable) { + _droppables[entity] = droppable; + } if (api is IApiSpinnable spinnable) { _spinnables[entity] = spinnable; } @@ -415,9 +425,9 @@ private void RegisterCollider(Entity entity, IApiColliderGenerator apiColl) } } -#endregion + #endregion -#region Events + #region Events public void Queue(Action action) => _simulationSystemGroup.QueueBeforeBallCreation(action); public void ScheduleAction(int timeMs, Action action) => _simulationSystemGroup.ScheduleAction(timeMs, action); @@ -457,14 +467,22 @@ public void OnEvent(in EventData eventData) _slingshots[eventData.ItemEntity].OnSlingshot(eventData.BallEntity); break; + case EventId.TargetEventsDropped: + _droppables[eventData.ItemEntity].OnDropStatusChanged(true, eventData.BallEntity); + break; + + case EventId.TargetEventsRaised: + _droppables[eventData.ItemEntity].OnDropStatusChanged(false, eventData.BallEntity); + break; + default: throw new InvalidOperationException($"Unknown event {eventData.eventId} for entity {eventData.ItemEntity}"); } } -#endregion + #endregion -#region API + #region API public void AddHardwareRule(string switchId, string coilId) { @@ -516,7 +534,7 @@ public void RemoveHardwareRule(string switchId, string coilId) _tableComponent.MappingConfig.RemoveWire(wire); } -#endregion + #endregion private static void HandleInput(object obj, InputActionChange change) { diff --git a/VisualPinball.Unity/VisualPinball.Unity/Input/InputManager.cs b/VisualPinball.Unity/VisualPinball.Unity/Input/InputManager.cs index fc60cb326..811e0d61a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Input/InputManager.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Input/InputManager.cs @@ -161,6 +161,10 @@ public static InputActionAsset GetDefaultInputActionAsset() map.AddAction(InputConstants.ActionCoinDoorEnter, InputActionType.Button, "/0"); map.AddAction(InputConstants.ActionCoinDoorAdvance, InputActionType.Button, "/8"); map.AddAction(InputConstants.ActionCoinDoorUpDown, InputActionType.Button, "/7"); + map.AddAction(InputConstants.ActionCoinDoorBack, InputActionType.Button, "/7"); + map.AddAction(InputConstants.ActionCoinDoorMinus, InputActionType.Button, "/8"); + map.AddAction(InputConstants.ActionCoinDoorPlus, InputActionType.Button, "/9"); + map.AddAction(InputConstants.ActionCoinDoorSelect, InputActionType.Button, "/0"); map.AddAction(InputConstants.ActionSlamTilt, InputActionType.Button, "/home"); map.AddAction(InputConstants.ActionSelfTest, InputActionType.Button, "/8"); map.AddAction(InputConstants.ActionLeftAdvance, InputActionType.Button, "/a"); diff --git a/VisualPinball.Unity/VisualPinball.Unity/Mappings/MappingConfig.cs b/VisualPinball.Unity/VisualPinball.Unity/Mappings/MappingConfig.cs index 486af84d6..00b946fe9 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/Mappings/MappingConfig.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/Mappings/MappingConfig.cs @@ -474,20 +474,18 @@ public void PopulateLamps(GamelogicEngineLamp[] engineLamps, TableComponent tabl { var lamps = tableComponent.GetComponentsInChildren().OrderBy(LampTypePriority).ToArray(); foreach (var engineLamp in GetLamps(engineLamps)) { - var lampMapping = Lamps.FirstOrDefault(mappingsLampData => mappingsLampData.Id == engineLamp.Id && !mappingsLampData.IsCoil); + var lampMapping = Lamps.FirstOrDefault(mappingsLampData => mappingsLampData.Id == engineLamp.Id && mappingsLampData.Channel == engineLamp.Channel && !mappingsLampData.IsCoil); if (lampMapping != null) { continue; } var description = string.IsNullOrEmpty(engineLamp.Description) ? string.Empty : engineLamp.Description; - var deviceAdded = false; foreach (var device in GuessLampDevices(lamps, engineLamp)) { var deviceItem = GuessLampDeviceItem(engineLamp, device); - AddLamp(new LampMapping - { + AddLamp(new LampMapping { Id = engineLamp.Id, InternalId = engineLamp.InternalId, Channel = engineLamp.Channel, @@ -503,8 +501,7 @@ public void PopulateLamps(GamelogicEngineLamp[] engineLamps, TableComponent tabl } if (!deviceAdded) { - AddLamp(new LampMapping - { + AddLamp(new LampMapping { Id = engineLamp.Id, InternalId = engineLamp.InternalId, Channel = engineLamp.Channel, diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetAnimationSystem.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetAnimationSystem.cs index 549e3a298..4ec3b5a94 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetAnimationSystem.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetAnimationSystem.cs @@ -17,6 +17,7 @@ using Unity.Collections; using Unity.Entities; using Unity.Profiling; +using UnityEngine; using VisualPinball.Engine.Game; namespace VisualPinball.Unity @@ -24,6 +25,7 @@ namespace VisualPinball.Unity [UpdateInGroup(typeof(UpdateAnimationsSystemGroup))] internal class DropTargetAnimationSystem : SystemBase { + private Player _player; private VisualPinballSimulationSystemGroup _visualPinballSimulationSystemGroup; private NativeQueue _eventQueue; @@ -31,6 +33,7 @@ internal class DropTargetAnimationSystem : SystemBase protected override void OnCreate() { + _player = Object.FindObjectOfType(); _visualPinballSimulationSystemGroup = World.GetOrCreateSystem(); _eventQueue = new NativeQueue(Allocator.Persistent); } @@ -103,6 +106,11 @@ protected override void OnUpdate() marker.End(); }).Run(); + + // dequeue events + while (_eventQueue.TryDequeue(out var eventData)) { + _player.OnEvent(in eventData); + } } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetApi.cs index 3e5ec5314..6f5a85b0a 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/HitTarget/DropTargetApi.cs @@ -23,7 +23,7 @@ namespace VisualPinball.Unity { public class DropTargetApi : CollidableApi, - IApi, IApiHittable, IApiSwitch, IApiSwitchDevice + IApi, IApiHittable, IApiSwitch, IApiSwitchDevice, IApiDroppable { /// /// Event emitted when the table is started. @@ -60,6 +60,12 @@ internal DropTargetApi(GameObject go, Entity entity, Player player) { } + public void OnDropStatusChanged(bool isDropped, Entity ballEntity) + { + OnSwitch(isDropped); + Switch?.Invoke(this, new SwitchEventArgs(isDropped, ballEntity)); + } + /// /// /// @@ -72,17 +78,10 @@ private void SetIsDropped(bool isDropped) data.MoveAnimation = true; if (isDropped) { data.MoveDown = true; - - OnSwitch(true); - Switch?.Invoke(this, new SwitchEventArgs(true, Entity.Null)); - } else { data.MoveDown = false; data.TimeStamp = World.DefaultGameObjectInjectionWorld.GetOrCreateSystem().TimeMsec; - - OnSwitch(false); - Switch?.Invoke(this, new SwitchEventArgs(false, Entity.Null)); } } else { data.IsDropped = isDropped; @@ -130,9 +129,6 @@ void IApi.OnDestroy() void IApiHittable.OnHit(Entity ballEntity, bool _) { Hit?.Invoke(this, new HitEventArgs(ballEntity)); - - OnSwitch(true); - Switch?.Invoke(this, new SwitchEventArgs(true, ballEntity)); } #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/IApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/IApi.cs index 3fc8bd665..003855d4f 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/IApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/IApi.cs @@ -17,8 +17,8 @@ using System; using System.Collections.Generic; using Unity.Entities; -using VisualPinball.Engine.Math; -using Color = UnityEngine.Color; +using UnityEngine; +using VisualPinball.Engine.Game.Engines; namespace VisualPinball.Unity { @@ -123,6 +123,14 @@ internal interface IApiSlingshot void OnSlingshot(Entity ballEntity); } + /// + /// Internal interface for droppables. + /// + internal interface IApiDroppable + { + void OnDropStatusChanged(bool isDropped, Entity ballEntity); + } + /// /// This interface makes the implementation act as a switch that can emit switch events to /// one or many destinations. @@ -243,23 +251,9 @@ internal interface IApiCoilDevice /// public interface IApiLamp : IApiWireDest, IApi { - /// - /// Sets the color of the light. - /// - Color Color { get; set; } - - /// - /// Sets the light intensity to a given value between 0 and 1. - /// - /// 0.0 = off, 1.0 = full intensity - /// If channel is , change intensity, otherwise update color. - void OnLamp(float value, ColorChannel channel); - - /// - /// Sets the light color of the lamp. - /// - /// New color to set - void OnLampColor(Color color); + void OnLamp(LampStatus newStatus); + void OnLamp(float intensity); + void OnLamp(Color color); } /// diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/ILampDeviceComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/ILampDeviceComponent.cs index 5cb3cc02e..e91bef726 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/ILampDeviceComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/ILampDeviceComponent.cs @@ -30,5 +30,8 @@ public interface ILampDeviceComponent : IWireableComponent, IDeviceComponent LightSources { get; } + + Color LampColor { get; } + LampStatus LampStatus { get; } } } diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Light/LightApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Light/LightApi.cs index 766bbc185..8e9376af0 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Light/LightApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Light/LightApi.cs @@ -16,106 +16,85 @@ using System; using UnityEngine; -using VisualPinball.Engine.Math; -using VisualPinball.Engine.VPT; +using VisualPinball.Engine.Game.Engines; using VisualPinball.Engine.VPT.Light; -using Color = UnityEngine.Color; namespace VisualPinball.Unity { - public class LightApi : ItemApi, - IApi, IApiLamp, IApiWireDeviceDest + public class LightApi : ItemApi, IApiLamp, IApiWireDeviceDest { /// /// Event emitted when the table is started. /// public event EventHandler Init; - public int State { - get => _state; - set => Set(value, value == LightStatus.LightStateOn ? 1.0f : 0f); - } - - private int _state; - private readonly LightComponent _lightComponent; private bool _initialized; + private LampStatus _status; + private float _intensity = 1f; - void IApiWireDest.OnChange(bool enabled) => Set( - enabled ? LightStatus.LightStateOn : LightStatus.LightStateOff, - enabled ? 1.0f : 0f); - - public Color Color { get => _lightComponent.Color; set => _lightComponent.Color = value; } + private readonly LightComponent _lightComponent; - IApiWireDest IApiWireDeviceDest.Wire(string deviceItem) => this; + void IApiWireDest.OnChange(bool enabled) + { + OnLamp(enabled ? LampStatus.On : LampStatus.Off); + } - void IApiLamp.OnLamp(float value, ColorChannel channel) + public void OnLamp(LampStatus status) { if (!_initialized) { // might have disabled some lights in the editor.. return; } - switch (channel) { - case ColorChannel.Alpha: { - Set(value == 0.0f ? LightStatus.LightStateOff : LightStatus.LightStateOn, value); - break; - } - case ColorChannel.Red: { - var color = _lightComponent.Color; - color.r = value; - _lightComponent.Color = color; - break; - } - case ColorChannel.Green: { - var color = _lightComponent.Color; - color.g = value; - _lightComponent.Color = color; - break; - } - case ColorChannel.Blue: { - var color = _lightComponent.Color; - color.b = value; - _lightComponent.Color = color; - break; - } - default: - throw new ArgumentOutOfRangeException(nameof(channel), channel, null); - } + _status = status; + Update(); } - void IApiLamp.OnLampColor(Color color) + public void OnLamp(float intensity) { - _lightComponent.Color = color; - } + if (!_initialized) { + // might have disabled some lights in the editor.. + return; + } - internal LightApi(GameObject go, Player player) : base(go, player) - { - _lightComponent = go.GetComponentInChildren(); - _state = _lightComponent.State; + _intensity = intensity; + Update(); } - private void Set(int lightStatus, float value) + private void Update() { - switch (lightStatus) { - case LightStatus.LightStateOff: { + switch (_status) { + case LampStatus.Off: { _lightComponent.FadeTo(0); break; } - - case LightStatus.LightStateOn: { - _lightComponent.FadeTo(value); - + case LampStatus.On: { + _lightComponent.FadeTo(_intensity); break; } - - case LightStatus.LightStateBlinking: { - _lightComponent.StartBlinking(); + case LampStatus.Blinking: { + _lightComponent.StartBlinking(_intensity); break; } - default: throw new ArgumentOutOfRangeException(); } - _state = lightStatus; + } + + public void OnLamp(Color color) + { + if (!_initialized) { + // might have disabled some lights in the editor.. + return; + } + _lightComponent.Color = new Color(color.r, color.g, color.b, _lightComponent.Color.a); + } + + IApiWireDest IApiWireDeviceDest.Wire(string deviceItem) => this; + + internal LightApi(GameObject go, Player player) : base(go, player) + { + _lightComponent = go.GetComponentInChildren(); + _status = _lightComponent.State; } #region Events diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Light/LightComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Light/LightComponent.cs index 1f0f529eb..31d115b90 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Light/LightComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Light/LightComponent.cs @@ -52,7 +52,7 @@ public class LightComponent : MainRenderableComponent, ILampDeviceCom [Tooltip("The radius of the bulb mesh")] public float BulbSize = 20f; - public int State; + public LampStatus State; public string BlinkPattern; public int BlinkInterval; @@ -93,6 +93,15 @@ public class LightComponent : MainRenderableComponent, ILampDeviceCom public IApiLamp GetApi(Player player) => _api ??= new LightApi(gameObject, player); public IEnumerable LightSources => GetComponentsInChildren(); + public Color LampColor { + get { + var src = GetComponentInChildren(); + return Color.magenta; //src == null ? Color.white : src.color; + } + } + + public LampStatus LampStatus => State; + [NonSerialized] private LightApi _api; @@ -170,6 +179,8 @@ public Color Color { set { foreach (var unityLight in _unityLights) { unityLight.color = value; + + // todo handle insert material color } } } @@ -235,26 +246,28 @@ public void FadeTo(float value) } } - public void StartBlinking() + public void StartBlinking(float blinkIntensity) { if (!_hasLights) { return; } StopAllCoroutines(); - StartCoroutine(nameof(Blink)); + StartCoroutine(nameof(Blink), blinkIntensity); } - private IEnumerator Blink() + private IEnumerator Blink(float blinkIntensity) { // parse blink sequence - var sequence = BlinkPattern.ToCharArray().Select(c => c == '1').ToArray(); + var blinkInterval = BlinkInterval == 0 ? 1000 : BlinkInterval; + var blinkPattern = BlinkPattern.Trim().Length < 2 ? "10" : BlinkPattern.Trim(); + var sequence = blinkPattern.ToCharArray().Select(c => c == '1').ToArray(); // step time is stored in ms but we need seconds - var stepTime = BlinkInterval / 1000f; + var stepTime = blinkInterval / 1000f; while (true) { foreach (var on in sequence) { - yield return Fade(on ? 1 : 0); + yield return Fade(on ? 1 * blinkIntensity : 0); var timeFading = on ? FadeSpeedUp : FadeSpeedDown; if (timeFading < stepTime) { yield return new WaitForSeconds(stepTime - timeFading); @@ -332,7 +345,7 @@ public override IEnumerable SetData(LightData data) BulbSize = data.MeshRadius; // logical params - State = data.State; + State = (LampStatus)data.State; BlinkPattern = data.BlinkPattern; BlinkInterval = data.BlinkInterval; FadeSpeedUp = data.FadeSpeedUp; @@ -397,7 +410,7 @@ public override LightData CopyDataTo(LightData data, string[] materialNames, str data.MeshRadius = BulbSize; // logical params - data.State = State; + data.State = (int)State; data.BlinkPattern = BlinkPattern; data.BlinkInterval = BlinkInterval; data.FadeSpeedUp = FadeSpeedUp; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Light/LightGroupApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Light/LightGroupApi.cs index 69236ad90..8db7c512e 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Light/LightGroupApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Light/LightGroupApi.cs @@ -15,7 +15,7 @@ // along with this program. If not, see . using System; -using VisualPinball.Engine.Math; +using VisualPinball.Engine.Game.Engines; using Color = UnityEngine.Color; namespace VisualPinball.Unity @@ -49,22 +49,12 @@ void IApi.OnDestroy() #region IApiLamp - public Color Color - { - get => _color; - set { - _color = value; - Do(l => l.Color = value); - } - } - private Color _color; void IApiWireDest.OnChange(bool enabled) => Do(l => l.OnChange(enabled)); - - void IApiLamp.OnLamp(float value, ColorChannel channel) => Do(l => l.OnLamp(value, channel)); - - void IApiLamp.OnLampColor(Color color) => Do(l => l.OnLampColor(color)); + void IApiLamp.OnLamp(LampStatus newState) => Do(l => l.OnLamp(newState)); + void IApiLamp.OnLamp(float newState) => Do(l => l.OnLamp(newState)); + void IApiLamp.OnLamp(Color newState) => Do(l => l.OnLamp(newState)); #endregion diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Light/LightGroupComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Light/LightGroupComponent.cs index 725789b9e..054d9f406 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Light/LightGroupComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Light/LightGroupComponent.cs @@ -42,7 +42,12 @@ public List Lights { Lights.Select(l => l.GetApi(player)).ToArray() ); public IEnumerable LightSources => Lights.SelectMany(l => l.LightSources).ToArray(); - + public Color LampColor { + get { + return _lights.Length > 0 ? (_lights.First() as ILampDeviceComponent)!.LampColor : Color.blue; + } + } + public LampStatus LampStatus => _lights.Length > 0 ? (_lights.First() as ILampDeviceComponent)!.LampStatus : LampStatus.Off; [NonSerialized] private LightGroupApi _api; diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Table/TableComponent.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Table/TableComponent.cs index 11b576ef1..9289e6b09 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Table/TableComponent.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Table/TableComponent.cs @@ -137,9 +137,9 @@ public Bounds GetTableBounds() public void RepopulateHardware(IGamelogicEngine gle) { MappingConfig.Clear(false); - MappingConfig.PopulateSwitches(gle.AvailableSwitches, this); - MappingConfig.PopulateLamps(gle.AvailableLamps, this); - MappingConfig.PopulateCoils(gle.AvailableCoils, this); + MappingConfig.PopulateSwitches(gle.RequestedSwitches, this); + MappingConfig.PopulateLamps(gle.RequestedLamps, this); + MappingConfig.PopulateCoils(gle.RequestedCoils, this); MappingConfig.PopulateWires(gle.AvailableWires, this); // hook up plunger diff --git a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trough/TroughApi.cs b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trough/TroughApi.cs index 3b275059c..ac19c42d1 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VPT/Trough/TroughApi.cs +++ b/VisualPinball.Unity/VisualPinball.Unity/VPT/Trough/TroughApi.cs @@ -16,12 +16,11 @@ using System; using System.Collections.Generic; +using NLog; using UnityEngine; using VisualPinball.Engine.VPT; using VisualPinball.Engine.VPT.Trough; -using NLog; using Logger = NLog.Logger; -using UnityEditor; namespace VisualPinball.Unity { diff --git a/VisualPinball.Unity/VisualPinball.Unity/VisualPinball.Unity.csproj b/VisualPinball.Unity/VisualPinball.Unity/VisualPinball.Unity.csproj index 7317fe223..1a854c591 100644 --- a/VisualPinball.Unity/VisualPinball.Unity/VisualPinball.Unity.csproj +++ b/VisualPinball.Unity/VisualPinball.Unity/VisualPinball.Unity.csproj @@ -76,6 +76,6 @@ - +