├── Tools ├── .gitignore ├── logs_to_manifest.py └── package_release.ps1 ├── .gitattributes ├── .idea └── .idea.DrizzleEdit │ └── .idea │ ├── .name │ ├── encodings.xml │ ├── misc.xml │ ├── vcs.xml │ ├── indexLayout.xml │ └── .gitignore ├── Drizzle.Logic.Tests ├── Usings.cs ├── LruCacheTest.cs └── Drizzle.Logic.Tests.csproj ├── .gitmodules ├── LingoSource ├── inptBox.lingo ├── goback.lingo ├── massRenderLoop.lingo ├── stop.lingo ├── changeSize.lingo ├── cameraEditorStart.lingo ├── renderEffectsStart.lingo ├── testDrawLevel.lingo ├── getExtraTile.lingo ├── envEditorStart.lingo ├── renderPropsStart.lingo ├── exportAllBitmaps.lingo ├── sliderBehav.lingo ├── lightEditorStart.lingo ├── levelEditStart.lingo ├── loadLevelStart.lingo ├── massRenderStart.lingo ├── finished.lingo ├── renderLightStart2.lingo ├── envEditorLoop.lingo ├── LOstart.lingo ├── unify.lingo ├── levelEditor.lingo ├── globals.lingo ├── tileEditorStart.lingo ├── saveProject.lingo ├── effectsEditorStart.lingo ├── renderLight2.lingo ├── saveFile.lingo └── propEditorStart.lingo ├── Drizzle.Editor ├── Assets │ └── avalonia-logo.ico ├── FodyWeavers.xml ├── ViewModels │ ├── Render │ │ ├── RenderStageCompletedViewModel.cs │ │ ├── RenderStageViewModelBase.cs │ │ ├── RenderStageErrorViewModel.cs │ │ ├── RenderStageLightViewModel.cs │ │ ├── RenderStageLayersViewModel.cs │ │ └── RenderStageEffectsViewModel.cs │ ├── EditorTabs │ │ ├── EditorTabViewModelBase.cs │ │ ├── TabTileEditorViewModel.cs │ │ ├── TabLevelOverviewViewModel.cs │ │ └── TabGeometryEditorViewModel.cs │ ├── ViewModelBase.cs │ ├── MainEditorTabViewModel.cs │ └── EditorContentViewModel.cs ├── Views │ ├── LingoStatus.axaml.cs │ ├── EditorContentView.axaml.cs │ ├── EditorTabs │ │ ├── TabTileEditorView.axaml.cs │ │ ├── TabLevelOverviewView.axaml.cs │ │ ├── TabGeometryEditorView.axaml.cs │ │ ├── TabTileEditorView.axaml │ │ └── TabLevelOverviewView.axaml │ ├── Render │ │ ├── RenderStageErrorView.axaml.cs │ │ ├── RenderStageLayersView.axaml.cs │ │ ├── RenderStageLightView.axaml.cs │ │ ├── RenderStageCompletedView.axaml.cs │ │ ├── RenderStageEffectsView.axaml.cs │ │ ├── RenderStageCompletedView.axaml │ │ ├── RenderStageErrorView.axaml │ │ ├── RenderStageLightView.axaml │ │ ├── RenderStageLayersView.axaml │ │ ├── RenderWindow.axaml.cs │ │ ├── RenderStageEffectsView.axaml │ │ └── RenderWindow.axaml │ ├── MainEditorTabView.axaml.cs │ ├── MainEditorTabView.axaml │ ├── LingoCastViewer.axaml.cs │ ├── AboutWindow.axaml │ ├── EditorContentView.axaml │ ├── LingoStatus.axaml │ ├── AboutWindow.axaml.cs │ ├── MainWindow.axaml.cs │ ├── LevelPreviewControl.cs │ ├── EditorRendering.cs │ └── LingoCastViewer.axaml ├── Helpers │ ├── Swap.cs │ ├── EnumBoolConverter.cs │ ├── TrickHitTestOperation.cs │ ├── Bresenham.cs │ └── LingoImageAvaloniaHelper.cs ├── KeyMap.cs ├── App.axaml ├── ViewLocator.cs ├── Program.cs ├── FodyWeavers.xsd ├── Drizzle.Editor.csproj ├── App.axaml.cs ├── CommandLineArgs.cs └── AvaloniaSeriLogger.cs ├── .vscode └── extensions.json ├── Drizzle.Lingo.Runtime ├── Scripting │ ├── CompiledScript.cs │ ├── LingoScriptRuntime.cs │ └── Binders.cs ├── Cast │ ├── CastMember.Script.cs │ ├── CastMember.Shape.cs │ ├── CastMember.Text.cs │ ├── CastMember.Bitmap.cs │ └── CastMember.cs ├── Data │ ├── ILingoListDuplicate.cs │ ├── ILingoVector.cs │ ├── LingoFormat.cs │ ├── LingoSymbol.cs │ ├── LingoPropertyList.cs │ ├── LingoColor.cs │ └── LingoPoint.cs ├── ISliceable.cs ├── LingoScriptRuntimeBase.cs ├── AssemblyInfo.cs ├── Xtra │ ├── BaseXtra.cs │ ├── ImgXtra.cs │ └── FileIOXtra.cs ├── Parser │ └── DebugPrint.cs ├── LingoGlobal.Mouse.cs ├── LingoGlobal.Random.cs ├── Utils │ └── CultureFix.cs ├── Drizzle.Lingo.Runtime.csproj.DotSettings ├── LingoMask.cs ├── LingoGlobal.Key.cs ├── LingoGlobal.System.cs ├── Drizzle.Lingo.Runtime.csproj ├── LingoGlobal.Init.cs ├── Attributes.cs ├── LingoGlobal.Global.cs ├── Utility │ └── ImageSharpExt.cs ├── LingoGlobal.Scripting.cs ├── LingoSprite.cs ├── LingoGlobal.Movie.cs ├── LingoRuntime.FileSystem.cs ├── lingo notes.md ├── LingoRuntime.Random.cs ├── LingoCastLib.cs ├── LingoRuntime.Clone.cs └── LingoImage.Silhouette.cs ├── Drizzle.Lingo.Tests ├── Images │ └── effectmask │ │ ├── layer9.png │ │ ├── dumpimage.png │ │ └── flattenedgradientB.png ├── LingoNumberTest.cs ├── ParseTest.cs ├── Drizzle.Lingo.Tests.csproj ├── LingoImagePixelOpsTest.cs ├── LingoOperatorTest.cs └── RngTest.cs ├── Drizzle.Ported ├── LingoParentScript.cs ├── LingoBehaviorScript.cs ├── AssemblyInfo.cs ├── Drizzle.Ported.csproj ├── MovieScript.cs ├── LingoScriptBase.cs ├── MovieScript.CacheLoadImage.cs ├── Constants.cs └── LruCache.cs ├── .editorconfig ├── Directory.Build.props ├── .gitignore ├── Drizzle.Transpiler └── Drizzle.Transpiler.csproj ├── Drizzle.Logic ├── Rendering │ ├── RenderStage.cs │ ├── RenderPreview.cs │ ├── RenderStatus.cs │ └── RenderCmd.cs ├── Drizzle.Logic.csproj ├── MathHelper.cs ├── ILingoRuntimeManager.cs ├── EditorRuntimeHelpers.cs └── Vector2i.cs ├── Drizzle.Benchmarks ├── Program.cs ├── Drizzle.Benchmarks.csproj └── ImageQuadCopy.cs ├── .github └── workflows │ ├── dotnet.yml │ ├── render-all-levels.yml │ └── publish-release.yml ├── Drizzle.ConsoleApp └── Drizzle.ConsoleApp.csproj ├── LICENSE.txt ├── README.md └── DrizzleEdit.sln.DotSettings /Tools/.gitignore: -------------------------------------------------------------------------------- 1 | Logs/ -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.idea/.idea.DrizzleEdit/.idea/.name: -------------------------------------------------------------------------------- 1 | DrizzleEdit -------------------------------------------------------------------------------- /Drizzle.Logic.Tests/Usings.cs: -------------------------------------------------------------------------------- 1 | global using NUnit.Framework; 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Data"] 2 | path = Data 3 | url = https://github.com/PJB3005/Drizzle.Data.git 4 | branch = master -------------------------------------------------------------------------------- /LingoSource/inptBox.lingo: -------------------------------------------------------------------------------- 1 | 2 | global levelName 3 | 4 | on change me 5 | levelName = sprite(me.spriteNum).text 6 | end 7 | -------------------------------------------------------------------------------- /Drizzle.Editor/Assets/avalonia-logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PJB3005/Drizzle/HEAD/Drizzle.Editor/Assets/avalonia-logo.ico -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "ms-dotnettools.csharp", 4 | "editorconfig.editorconfig" 5 | ] 6 | } -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Scripting/CompiledScript.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Lingo.Runtime.Scripting; 2 | 3 | public sealed class CompiledScript 4 | { 5 | } -------------------------------------------------------------------------------- /Drizzle.Lingo.Tests/Images/effectmask/layer9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PJB3005/Drizzle/HEAD/Drizzle.Lingo.Tests/Images/effectmask/layer9.png -------------------------------------------------------------------------------- /Drizzle.Ported/LingoParentScript.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Ported; 2 | 3 | public abstract class LingoParentScript : LingoScriptBase 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Cast/CastMember.Script.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Lingo.Runtime.Cast; 2 | 3 | public sealed partial class CastMember 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Drizzle.Lingo.Tests/Images/effectmask/dumpimage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PJB3005/Drizzle/HEAD/Drizzle.Lingo.Tests/Images/effectmask/dumpimage.png -------------------------------------------------------------------------------- /Drizzle.Lingo.Tests/Images/effectmask/flattenedgradientB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PJB3005/Drizzle/HEAD/Drizzle.Lingo.Tests/Images/effectmask/flattenedgradientB.png -------------------------------------------------------------------------------- /Drizzle.Editor/FodyWeavers.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /Drizzle.Ported/LingoBehaviorScript.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Ported; 2 | 3 | public class LingoBehaviorScript : LingoScriptBase 4 | { 5 | public int spritenum { get; set; } 6 | } -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Data/ILingoListDuplicate.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Lingo.Runtime; 2 | 3 | public interface ILingoListDuplicate 4 | { 5 | ILingoListDuplicate duplicate(); 6 | } -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/ISliceable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Drizzle.Lingo.Runtime; 4 | 5 | public interface ISliceable 6 | { 7 | public object this[Range idx] { get; } 8 | } -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Data/ILingoVector.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Lingo.Runtime; 2 | 3 | public interface ILingoVector 4 | { 5 | int CountElems { get; } 6 | object? this[int index] { get; } 7 | } -------------------------------------------------------------------------------- /Drizzle.Editor/ViewModels/Render/RenderStageCompletedViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Editor.ViewModels.Render; 2 | 3 | public sealed class RenderStageCompletedViewModel : RenderStageViewModelBase 4 | { 5 | 6 | } -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/LingoScriptRuntimeBase.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Lingo.Runtime; 2 | 3 | public abstract class LingoScriptRuntimeBase 4 | { 5 | public abstract void Init(object movieScript, LingoGlobal global); 6 | } -------------------------------------------------------------------------------- /Drizzle.Ported/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | // WHEE I throw the magic performance word and hope my code is blessed to go ever so slightly faster. 4 | [module: SkipLocalsInit] 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/.idea.DrizzleEdit/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Drizzle.Editor/ViewModels/EditorTabs/EditorTabViewModelBase.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Editor.ViewModels.EditorTabs; 2 | 3 | public abstract class EditorTabViewModelBase : ViewModelBase 4 | { 5 | public abstract string Title { get; } 6 | } -------------------------------------------------------------------------------- /LingoSource/goback.lingo: -------------------------------------------------------------------------------- 1 | on exitFrame me 2 | -- member("dpImage").image = image() 3 | global gMassRenderL 4 | 5 | if gMassRenderL = [] then 6 | _movie.go(9) 7 | else 8 | _movie.go(90) 9 | end if 10 | 11 | end -------------------------------------------------------------------------------- /Drizzle.Editor/ViewModels/EditorTabs/TabTileEditorViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Editor.ViewModels.EditorTabs; 2 | 3 | public sealed class TabTileEditorViewModel : EditorTabViewModelBase 4 | { 5 | public override string Title => "Tiles"; 6 | } -------------------------------------------------------------------------------- /Drizzle.Editor/ViewModels/ViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using ReactiveUI; 5 | 6 | namespace Drizzle.Editor.ViewModels; 7 | 8 | public class ViewModelBase : ReactiveObject 9 | { 10 | } -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | // WHEE I throw the magic performance word and hope my code is blessed to go ever so slightly faster. 4 | [module: SkipLocalsInit] 5 | [assembly: InternalsVisibleTo("Drizzle.Lingo.Tests")] 6 | -------------------------------------------------------------------------------- /Drizzle.Editor/ViewModels/Render/RenderStageViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using Drizzle.Logic; 2 | 3 | namespace Drizzle.Editor.ViewModels.Render; 4 | 5 | public abstract class RenderStageViewModelBase : ViewModelBase 6 | { 7 | public virtual (int max, int current)? Progress => default; 8 | } -------------------------------------------------------------------------------- /.idea/.idea.DrizzleEdit/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/.idea.DrizzleEdit/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | 6 | [*.cs] 7 | indent_size = 4 8 | indent_style = space 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | [*.lingo] 13 | indent_size = 2 14 | indent_style = space 15 | 16 | [*.md] 17 | indent_size = 2 18 | -------------------------------------------------------------------------------- /Drizzle.Editor/Views/LingoStatus.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Drizzle.Editor.Views; 5 | 6 | public sealed partial class LingoStatus : UserControl 7 | { 8 | public LingoStatus() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Drizzle.Editor/Views/EditorContentView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Drizzle.Editor.Views; 5 | 6 | public sealed partial class EditorContentView : UserControl 7 | { 8 | public EditorContentView() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.idea/.idea.DrizzleEdit/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | LingoSource 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 0.1.7.2 4 | $(DefineConstants);FULL_RELEASE 5 | net7.0 6 | 11 7 | 8 | -------------------------------------------------------------------------------- /Drizzle.Editor/Views/EditorTabs/TabTileEditorView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Drizzle.Editor.Views.EditorTabs; 5 | 6 | public partial class TabTileEditorView : UserControl 7 | { 8 | public TabTileEditorView() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Data/LingoFormat.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Lingo.Runtime; 2 | 3 | public static class LingoFormat 4 | { 5 | public static string LingoToString(object? obj) 6 | { 7 | if (obj is string str) 8 | return $"\"{str}\""; 9 | 10 | return obj?.ToString() ?? ""; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.suo 2 | *.user 3 | .vs/ 4 | [Bb]in/ 5 | [Oo]bj/ 6 | _UpgradeReport_Files/ 7 | [Pp]ackages/ 8 | 9 | Thumbs.db 10 | Desktop.ini 11 | .DS_Store 12 | .desktop 13 | /Drizzle.Ported/Translated 14 | 15 | # Temporary directory used when making release builds. 16 | ReleaseBuild/ 17 | # Directory containing release builds. 18 | Release/ -------------------------------------------------------------------------------- /Drizzle.Editor/Views/Render/RenderStageErrorView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Drizzle.Editor.Views.Render; 5 | 6 | public partial class RenderStageErrorView : UserControl 7 | { 8 | public RenderStageErrorView() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Drizzle.Editor/Views/Render/RenderStageLayersView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Drizzle.Editor.Views.Render; 5 | 6 | public partial class RenderStageLayersView : UserControl 7 | { 8 | public RenderStageLayersView() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Drizzle.Editor/Views/Render/RenderStageLightView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Drizzle.Editor.Views.Render; 5 | 6 | public partial class RenderStageLightView : UserControl 7 | { 8 | public RenderStageLightView() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Xtra/BaseXtra.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Lingo.Runtime.Xtra; 2 | 3 | public abstract class BaseXtra 4 | { 5 | public abstract BaseXtra Duplicate(); 6 | 7 | // Editor does a pattern where it does xtra().new(), work around this. 8 | public dynamic @new() 9 | { 10 | return Duplicate(); 11 | } 12 | } -------------------------------------------------------------------------------- /Drizzle.Editor/Views/EditorTabs/TabLevelOverviewView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Drizzle.Editor.Views.EditorTabs; 5 | 6 | public partial class TabLevelOverviewView : UserControl 7 | { 8 | public TabLevelOverviewView() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Drizzle.Editor/Views/EditorTabs/TabGeometryEditorView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Drizzle.Editor.Views.EditorTabs; 5 | 6 | public partial class TabGeometryEditorView : UserControl 7 | { 8 | public TabGeometryEditorView() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Drizzle.Editor/Views/Render/RenderStageCompletedView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | 4 | namespace Drizzle.Editor.Views.Render; 5 | 6 | public partial class RenderStageCompletedView : UserControl 7 | { 8 | public RenderStageCompletedView() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Drizzle.Transpiler/Drizzle.Transpiler.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Drizzle.Logic/Rendering/RenderStage.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Logic.Rendering; 2 | 3 | public enum RenderStage 4 | { 5 | Start, 6 | CameraSetup, 7 | RenderLayers, 8 | RenderPropsPreEffects, 9 | RenderEffects, 10 | RenderPropsPostEffects, 11 | RenderLight, 12 | Finalize, 13 | RenderColors, 14 | Finished, 15 | SaveFile, 16 | } -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Parser/DebugPrint.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace Drizzle.Lingo.Runtime.Parser; 4 | 5 | public static class DebugPrint 6 | { 7 | public static string PrintAstNode(AstNode.Base node) 8 | { 9 | var sb = new StringBuilder(); 10 | 11 | node.DebugPrint(sb, 0); 12 | 13 | return sb.ToString(); 14 | } 15 | } -------------------------------------------------------------------------------- /.idea/.idea.DrizzleEdit/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Rider ignored files 5 | /contentModel.xml 6 | /projectSettingsUpdater.xml 7 | /.idea.DrizzleEdit.iml 8 | /modules.xml 9 | # Datasource local storage ignored files 10 | /dataSources/ 11 | /dataSources.local.xml 12 | # Editor-based HTTP Client requests 13 | /httpRequests/ 14 | -------------------------------------------------------------------------------- /Drizzle.Editor/Views/MainEditorTabView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Markup.Xaml; 3 | using JetBrains.Annotations; 4 | 5 | namespace Drizzle.Editor.Views; 6 | 7 | [UsedImplicitly] 8 | public sealed partial class MainEditorTabView : UserControl 9 | { 10 | public MainEditorTabView() 11 | { 12 | InitializeComponent(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Drizzle.Editor/ViewModels/Render/RenderStageErrorViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Drizzle.Editor.ViewModels.Render; 4 | 5 | public sealed class RenderStageErrorViewModel : RenderStageViewModelBase 6 | { 7 | public string ExceptionMessage { get; } 8 | 9 | public RenderStageErrorViewModel(Exception exception) 10 | { 11 | ExceptionMessage = exception.ToString(); 12 | } 13 | } -------------------------------------------------------------------------------- /Drizzle.Logic/Drizzle.Logic.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | enable 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/LingoGlobal.Mouse.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Lingo.Runtime; 2 | 3 | public sealed partial class LingoGlobal 4 | { 5 | public Mouse _mouse { get; private set; } = default!; 6 | 7 | public sealed class Mouse 8 | { 9 | public LingoPoint mouseloc => default; 10 | public LingoNumber mousedown => 0; 11 | public LingoNumber rightmousedown => 0; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /LingoSource/massRenderLoop.lingo: -------------------------------------------------------------------------------- 1 | global gMassRenderL, gViewRender 2 | 3 | on exitFrame me 4 | gMassRenderL.deleteAt(1) 5 | 6 | if gMassRenderL.count = 0 then 7 | alert("Mass Render Finished") 8 | _movie.go(1) 9 | else 10 | put "started rendering:" && gMassRenderL[1] 11 | script("loadLevel").loadLevel(gMassRenderL[1], 1) 12 | gViewRender = 0 13 | _movie.go(42) 14 | end if 15 | end -------------------------------------------------------------------------------- /Drizzle.Editor/ViewModels/EditorTabs/TabLevelOverviewViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Editor.ViewModels.EditorTabs; 2 | 3 | public sealed class TabLevelOverviewViewModel : EditorTabViewModelBase 4 | { 5 | public EditorContentViewModel ParentVm { get; } 6 | public override string Title => "Overview"; 7 | 8 | public TabLevelOverviewViewModel(EditorContentViewModel parentVm) 9 | { 10 | ParentVm = parentVm; 11 | } 12 | } -------------------------------------------------------------------------------- /Drizzle.Ported/Drizzle.Ported.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | CS8981 4 | true 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/LingoGlobal.Random.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Lingo.Runtime; 2 | 3 | public sealed partial class LingoGlobal 4 | { 5 | public LingoNumber the_randomSeed 6 | { 7 | get => (int) LingoRuntime.RngSeed; 8 | set => LingoRuntime.RngSeed = (uint) value.IntValue; 9 | } 10 | 11 | public LingoNumber random(LingoNumber max) 12 | { 13 | return LingoRuntime.Random(max.IntValue); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Drizzle.Logic/MathHelper.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Logic; 2 | 3 | public static class MathHelper 4 | { 5 | public static void SatAdd(ref byte val, byte add) 6 | { 7 | val = SatAdd(val, add); 8 | } 9 | 10 | public static byte SatAdd(byte val, byte add) 11 | { 12 | var added = val + add; 13 | if (added > byte.MaxValue) 14 | return byte.MaxValue; 15 | 16 | return (byte)added; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Utils/CultureFix.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Threading; 3 | 4 | namespace Drizzle.Lingo.Runtime.Utils; 5 | 6 | public static class CultureFix 7 | { 8 | public static void FixCulture() 9 | { 10 | var enUs = CultureInfo.GetCultureInfoByIetfLanguageTag("en-US"); 11 | CultureInfo.DefaultThreadCurrentCulture = enUs; 12 | CultureInfo.DefaultThreadCurrentUICulture = enUs; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Drizzle.Editor/Helpers/Swap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Drizzle.Editor.Helpers; 4 | 5 | public static class Swap 6 | { 7 | /// 8 | /// Swap and if necessary, so that <= . 9 | /// 10 | public static void Asc(ref T a, ref T b) where T : IComparable 11 | { 12 | if (a.CompareTo(b) > 0) 13 | (a, b) = (b, a); 14 | } 15 | } -------------------------------------------------------------------------------- /Drizzle.Editor/Views/MainEditorTabView.axaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /LingoSource/stop.lingo: -------------------------------------------------------------------------------- 1 | global gSaveProps 2 | 3 | on stopMovie me 4 | -- repeat with q = 1 to 2000 then 5 | -- (member q of castLib 2).erase() 6 | -- end repeat 7 | 8 | -- wdth = baScreenInfo("width") 9 | -- if wdth <> gSaveProps[1] then 10 | -- changeBack = baSetDisplay(gSaveProps[1], gSaveProps[2], gSaveProps[3], "perm", FALSE) 11 | -- end if 12 | 13 | changeBack = baSetDisplay(gSaveProps[1], gSaveProps[2], gSaveProps[3], "perm", FALSE) 14 | end 15 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Drizzle.Lingo.Runtime.csproj.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | True -------------------------------------------------------------------------------- /Drizzle.Ported/MovieScript.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Drizzle.Lingo.Runtime; 3 | 4 | namespace Drizzle.Ported; 5 | 6 | [MovieScript] 7 | public sealed partial class MovieScript : LingoScriptBase 8 | { 9 | public void Init(LingoGlobal global) 10 | { 11 | Init(this, global); 12 | } 13 | } 14 | 15 | public static class MovieScriptExt 16 | { 17 | public static MovieScript MovieScript(this LingoRuntime runtime) => (MovieScript)runtime.MovieScriptInstance; 18 | } -------------------------------------------------------------------------------- /Drizzle.Editor/KeyMap.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Avalonia.Input; 3 | 4 | namespace Drizzle.Editor; 5 | 6 | /// 7 | /// Avalonia -> Lingo key map. 8 | /// 9 | public static class KeyMap 10 | { 11 | public static readonly Dictionary Map = new() 12 | { 13 | { Key.Return, 36 }, 14 | { Key.Up, 126 }, 15 | { Key.Down, 125 }, 16 | { Key.Left, 123 }, 17 | { Key.Right, 124 }, 18 | }; 19 | } -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/LingoMask.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Lingo.Runtime; 2 | 3 | public sealed class LingoMask 4 | { 5 | // Currently 1-bit masks are assumed, higher-bit transparency masks not supported. 6 | 7 | public int Width { get; } 8 | public int Height { get; } 9 | public byte[] Data { get; } 10 | 11 | public LingoMask(int width, int height, byte[] data) 12 | { 13 | Width = width; 14 | Height = height; 15 | Data = data; 16 | } 17 | } -------------------------------------------------------------------------------- /Drizzle.Benchmarks/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Reflection; 3 | using BenchmarkDotNet.Running; 4 | using Drizzle.Benchmarks; 5 | 6 | var benchmark = new ImageQuadCopy(); 7 | benchmark.Setup(); 8 | 9 | for (var i = 0; i < 10; i++) 10 | { 11 | benchmark.Bench(); 12 | } 13 | 14 | var sw = new Stopwatch(); 15 | for (var i = 0; i < 30; i++) 16 | { 17 | sw.Restart(); 18 | benchmark.Bench(); 19 | sw.Stop(); 20 | Console.WriteLine(sw.Elapsed); 21 | } 22 | -------------------------------------------------------------------------------- /Drizzle.Editor/Views/Render/RenderStageEffectsView.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Controls.Templates; 3 | using Avalonia.Layout; 4 | using Avalonia.Markup.Xaml; 5 | using Avalonia.Markup.Xaml.Templates; 6 | using Drizzle.Editor.ViewModels.Render; 7 | 8 | namespace Drizzle.Editor.Views.Render; 9 | 10 | public partial class RenderStageEffectsView : UserControl 11 | { 12 | public RenderStageEffectsView() 13 | { 14 | InitializeComponent(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Tests/LingoNumberTest.cs: -------------------------------------------------------------------------------- 1 | using Drizzle.Lingo.Runtime; 2 | using NUnit.Framework; 3 | 4 | namespace Drizzle.Lingo.Tests; 5 | 6 | [TestFixture] 7 | [Parallelizable(ParallelScope.All)] 8 | public sealed class LingoNumberTest 9 | { 10 | [Test] 11 | [TestCase("2", ExpectedResult = "1")] 12 | [TestCase("2.0", ExpectedResult = "1.4142")] 13 | public string TestSqrt(string num) 14 | { 15 | return LingoNumber.Sqrt(LingoNumber.Parse(num)).ToString(); 16 | } 17 | } -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Cast/CastMember.Shape.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Lingo.Runtime.Cast; 2 | 3 | public sealed partial class CastMember 4 | { 5 | public LingoNumber _lineDirection; 6 | 7 | public LingoNumber linedirection 8 | { 9 | get 10 | { 11 | AssertType(CastMemberType.Shape); 12 | return _lineDirection; 13 | } 14 | set 15 | { 16 | AssertType(CastMemberType.Shape); 17 | _lineDirection = value; 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Drizzle.Editor/Views/EditorTabs/TabTileEditorView.axaml: -------------------------------------------------------------------------------- 1 | 7 | tile editor idk 8 | 9 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/LingoGlobal.Key.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Serilog; 3 | 4 | namespace Drizzle.Lingo.Runtime; 5 | 6 | public sealed partial class LingoGlobal 7 | { 8 | public Key _key { get; private set; } = default!; 9 | 10 | public sealed class Key 11 | { 12 | public LingoNumber keypressed(object keyName) 13 | { 14 | return 0; 15 | } 16 | 17 | public LingoNumber keypressed(int keyCode) 18 | { 19 | return 0; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Drizzle.Editor/ViewModels/Render/RenderStageLightViewModel.cs: -------------------------------------------------------------------------------- 1 | using Drizzle.Logic; 2 | using Drizzle.Logic.Rendering; 3 | 4 | namespace Drizzle.Editor.ViewModels.Render; 5 | 6 | public class RenderStageLightViewModel : RenderStageViewModelBase 7 | { 8 | public int CurrentLayer { get; } 9 | public override (int max, int current)? Progress { get; } 10 | 11 | public RenderStageLightViewModel(RenderStageStatusLight status) 12 | { 13 | CurrentLayer = status.CurrentLayer; 14 | Progress = (30, status.CurrentLayer); 15 | } 16 | } -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/LingoGlobal.System.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Drizzle.Lingo.Runtime; 4 | 5 | public sealed partial class LingoGlobal 6 | { 7 | public System _system { get; private set; } = default!; 8 | 9 | public sealed class System 10 | { 11 | private readonly LingoGlobal _global; 12 | 13 | public System(LingoGlobal global) 14 | { 15 | _global = global; 16 | } 17 | 18 | public LingoNumber milliseconds => (int)_global.LingoRuntime.Stopwatch.ElapsedMilliseconds; 19 | } 20 | } -------------------------------------------------------------------------------- /Drizzle.Logic.Tests/LruCacheTest.cs: -------------------------------------------------------------------------------- 1 | using Drizzle.Ported; 2 | 3 | namespace Drizzle.Logic.Tests; 4 | 5 | [TestFixture] 6 | [Parallelizable(ParallelScope.All)] 7 | [TestOf(typeof(LruCache<,>))] 8 | public sealed class LruCacheTest 9 | { 10 | [Test] 11 | public void Test() 12 | { 13 | var cache = new LruCache(2); 14 | cache.Get(1, Load); 15 | cache.Get(2, Load); 16 | cache.Get(1, Load); 17 | cache.Get(3, Load); 18 | 19 | static string Load(int key) => key.ToString(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Drizzle.Editor/ViewModels/Render/RenderStageLayersViewModel.cs: -------------------------------------------------------------------------------- 1 | using Drizzle.Logic; 2 | using Drizzle.Logic.Rendering; 3 | 4 | namespace Drizzle.Editor.ViewModels.Render; 5 | 6 | public sealed class RenderStageLayersViewModel : RenderStageViewModelBase 7 | { 8 | public override (int max, int current)? Progress { get; } 9 | 10 | public int CurrentLayer { get; } 11 | 12 | public RenderStageLayersViewModel(RenderStageStatusLayers status) 13 | { 14 | CurrentLayer = status.CurrentLayer; 15 | Progress = (3, 3 - status.CurrentLayer); 16 | } 17 | } -------------------------------------------------------------------------------- /Drizzle.Ported/LingoScriptBase.cs: -------------------------------------------------------------------------------- 1 | using Drizzle.Lingo.Runtime; 2 | 3 | namespace Drizzle.Ported; 4 | 5 | public abstract class LingoScriptBase : LingoScriptRuntimeBase 6 | { 7 | protected MovieScript _movieScript; 8 | protected LingoGlobal _global; 9 | 10 | public void Init(MovieScript movieScript, LingoGlobal global) 11 | { 12 | _movieScript = movieScript; 13 | _global = global; 14 | } 15 | 16 | public sealed override void Init(object movieScript, LingoGlobal global) 17 | { 18 | Init((MovieScript) movieScript, global); 19 | } 20 | } -------------------------------------------------------------------------------- /LingoSource/changeSize.lingo: -------------------------------------------------------------------------------- 1 | 2 | global gLOProps, newSize, extraBufferTiles, gLEprops 3 | on exitFrame me 4 | if _key.keyPressed("A") and _movie.window.sizeState <> #minimized then 5 | if (gLOprops.size <> point(newSize[1], newSize[2]))or(newSize[3]>0)or(newSize[4]>0) then 6 | resizeLevel(point(newSize[1], newSize[2]),newSize[3],newSize[4] ) 7 | end if 8 | gLOProps.extraTiles = extraBufferTiles.duplicate() 9 | _movie.go(9) 10 | else if _key.keyPressed("C") and _movie.window.sizeState <> #minimized then 11 | _movie.go(9) 12 | else 13 | go the frame 14 | end if 15 | end -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Xtra/ImgXtra.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using SixLabors.ImageSharp; 3 | 4 | namespace Drizzle.Lingo.Runtime.Xtra; 5 | 6 | public sealed class ImgXtra : BaseXtra 7 | { 8 | public override BaseXtra Duplicate() 9 | { 10 | return new ImgXtra(); 11 | } 12 | 13 | public int ix_saveimage(LingoPropertyList props) 14 | { 15 | var img = (LingoImage)props["image"]!; 16 | var fileName = (string)props["filename"]!; 17 | 18 | using var file = File.Create(fileName); 19 | img.SaveAsPng(file); 20 | 21 | return 1; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Drizzle.Logic/Rendering/RenderPreview.cs: -------------------------------------------------------------------------------- 1 | using Drizzle.Lingo.Runtime; 2 | 3 | namespace Drizzle.Logic.Rendering; 4 | 5 | /// 6 | /// Represents the necessary state to render a preview for the editor window. 7 | /// 8 | public abstract record RenderPreview; 9 | 10 | public sealed record RenderPreviewEffects( 11 | LingoImage[] Layers, 12 | LingoImage BlackOut1, 13 | LingoImage BlackOut2) : 14 | RenderPreview; 15 | 16 | public sealed record RenderPreviewProps(LingoImage[] Layers) : RenderPreview; 17 | public sealed record RenderPreviewLights(LingoImage[] Layers) : RenderPreview; 18 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Drizzle.Lingo.Runtime.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | enable 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/LingoGlobal.Init.cs: -------------------------------------------------------------------------------- 1 | using Drizzle.Lingo.Runtime.Scripting; 2 | 3 | namespace Drizzle.Lingo.Runtime; 4 | 5 | public sealed partial class LingoGlobal 6 | { 7 | public LingoRuntime LingoRuntime { get; } 8 | 9 | public LingoGlobal(LingoRuntime lingoRuntime) 10 | { 11 | LingoRuntime = lingoRuntime; 12 | } 13 | 14 | public void Init() 15 | { 16 | _system = new System(this); 17 | _key = new Key(); 18 | _mouse = new Mouse(); 19 | _movie = new Movie(this); 20 | _global = new Global(this); 21 | ScriptRuntime = new LingoScriptRuntime(this); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Drizzle.Editor/App.axaml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /LingoSource/cameraEditorStart.lingo: -------------------------------------------------------------------------------- 1 | 2 | global gCameraProps, gLOprops, gLeProps 3 | 4 | on exitFrame me 5 | 6 | 7 | 8 | cols: number = gLOprops.size.loch 9 | rows: number = gLOprops.size.locv 10 | 11 | -- member("TEimg1").image = image(cols*16, rows*16, 16) 12 | -- member("TEimg2").image = image(cols*16, rows*16, 16) 13 | -- member("TEimg3").image = image(cols*16, rows*16, 16) 14 | 15 | member("levelEditImageShortCuts").image = image(cols*5, rows*5, 1) 16 | drawShortCutsImg(rect(1,1,cols,rows), 5, 1) 17 | 18 | repeat with q = 1 to 3 then 19 | miniLvlEditDraw(q) 20 | end repeat 21 | 22 | script("cameraEditor").drawAll() 23 | end -------------------------------------------------------------------------------- /Drizzle.Editor/Helpers/EnumBoolConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using Avalonia.Data; 4 | using Avalonia.Data.Converters; 5 | 6 | namespace Drizzle.Editor.Helpers; 7 | 8 | public sealed class EnumBoolConverter : IValueConverter 9 | { 10 | public static EnumBoolConverter Instance { get; } = new(); 11 | 12 | public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture) 13 | { 14 | return value?.Equals(parameter) ?? false; 15 | } 16 | 17 | public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture) 18 | { 19 | return value?.Equals(true) == true ? parameter : BindingOperations.DoNothing; 20 | } 21 | } -------------------------------------------------------------------------------- /Drizzle.Editor/Views/LingoCastViewer.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia; 3 | using Avalonia.Controls; 4 | using Avalonia.Markup.Xaml; 5 | using Drizzle.Editor.ViewModels; 6 | 7 | namespace Drizzle.Editor.Views; 8 | 9 | public sealed partial class LingoCastViewer : Window 10 | { 11 | public LingoCastViewer() 12 | { 13 | InitializeComponent(); 14 | #if DEBUG 15 | this.AttachDevTools(); 16 | #endif 17 | } 18 | 19 | private void OnClosed(object? sender, EventArgs e) 20 | { 21 | (DataContext as LingoCastViewerViewModel)?.Closed(); 22 | } 23 | 24 | private void OpOpened(object? sender, EventArgs e) 25 | { 26 | (DataContext as LingoCastViewerViewModel)?.Opened(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Attributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Drizzle.Lingo.Runtime; 4 | 5 | [AttributeUsage(AttributeTargets.Class, Inherited = false)] 6 | public sealed class MovieScriptAttribute : Attribute 7 | { 8 | } 9 | 10 | [AttributeUsage(AttributeTargets.Class, Inherited = false)] 11 | public sealed class ParentScriptAttribute : Attribute 12 | { 13 | } 14 | 15 | [AttributeUsage(AttributeTargets.Class, Inherited = false)] 16 | public sealed class BehaviorScriptAttribute : Attribute 17 | { 18 | } 19 | 20 | [AttributeUsage(AttributeTargets.Field)] 21 | public sealed class LingoGlobalAttribute : Attribute 22 | { 23 | } 24 | 25 | [AttributeUsage(AttributeTargets.Field)] 26 | public sealed class LingoPropertyAttribute : Attribute 27 | { 28 | } -------------------------------------------------------------------------------- /LingoSource/renderEffectsStart.lingo: -------------------------------------------------------------------------------- 1 | global vertRepeater, r, gLEprops, gEEprops, gTEprops, gTiles, keepLooping, gLOprops 2 | 3 | on exitFrame me 4 | type tm: number 5 | type val: number 6 | 7 | 8 | tm = _system.milliseconds 9 | 10 | repeat with q = 0 to 29 then 11 | sprite(50-q).loc = point((1024/2)-q, (768/2)-q) 12 | val = (q.float+1.0)/30.0 13 | -- put val 14 | sprite(50-q).color = color(val*255, val*255, val*255) 15 | end repeat 16 | 17 | sprite(57).visibility = 0 18 | sprite(58).visibility = 0 19 | 20 | vertRepeater = 100000 21 | 22 | 23 | 24 | 25 | if gEEprops.effects.count > 0 then 26 | r = 0 27 | keepLooping = 1 28 | else 29 | go(56) 30 | end if 31 | end 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Drizzle.Ported/MovieScript.CacheLoadImage.cs: -------------------------------------------------------------------------------- 1 | using Drizzle.Lingo.Runtime; 2 | 3 | namespace Drizzle.Ported; 4 | 5 | public sealed partial class MovieScript 6 | { 7 | private readonly LruCache _imageCache = new(64); 8 | 9 | public LingoImage cacheloadimage(string fileName) 10 | { 11 | return _imageCache.Get(fileName, this, static (state, fileName) => state.CacheLoadImageLoad(fileName)); 12 | } 13 | 14 | private LingoImage CacheLoadImageLoad(string fileName) 15 | { 16 | var member = _global.member("previewImprt")!; 17 | member.importfileinto(fileName); 18 | member.name = "previewImprt"; 19 | return member.image!; 20 | } 21 | 22 | public void ImageCacheClear() => _imageCache.Clear(); 23 | } 24 | -------------------------------------------------------------------------------- /Drizzle.Logic/ILingoRuntimeManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Drizzle.Lingo.Runtime; 4 | 5 | namespace Drizzle.Logic; 6 | 7 | /// 8 | /// Represents an "owner" of an isolated Lingo runtime. 9 | /// Provides some thread-safe APIs for stuff like cast inspection. 10 | /// 11 | /// 12 | /// At least, thread-safe from the main thread. 13 | /// 14 | public interface ILingoRuntimeManager 15 | { 16 | /// 17 | /// Executes an action on the thread of the lingo runtime. 18 | /// 19 | Task Exec(Action action); 20 | 21 | /// 22 | /// Executes an action on the thread of the lingo runtime. 23 | /// 24 | Task Exec(Func func); 25 | } -------------------------------------------------------------------------------- /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: build & test 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | with: 17 | submodules: true 18 | - name: Setup .NET 19 | uses: actions/setup-dotnet@v1 20 | with: 21 | dotnet-version: 7.0.x 22 | - name: Restore dependencies 23 | run: dotnet restore 24 | - name: Run transpiler 25 | run: dotnet run --project Drizzle.Transpiler/Drizzle.Transpiler.csproj -- LingoSource Drizzle.Ported/Translated 26 | - name: Build 27 | run: dotnet build --no-restore 28 | - name: Test 29 | run: dotnet test --no-build --verbosity normal 30 | -------------------------------------------------------------------------------- /LingoSource/testDrawLevel.lingo: -------------------------------------------------------------------------------- 1 | global gSkyColor, lightRects, gFullRender 2 | on exitFrame me 3 | gFullRender = 0 4 | lightRects = [rect(0,0,0,0), rect(0,0,0,0)] 5 | drawTestLevel() 6 | member("finalfg").image.setPixel(0, 0, gSkyColor)--skyColor 7 | member("finalfg").image.setPixel(1, 0, gSkyColor)--fogColor 8 | member("finalfg").image.setPixel(2, 0, color(10,10,10))--blackColor 9 | member("finalfg").image.setPixel(3, 0, color(10,10,10))--itemcolorColor 10 | 11 | member("finalfg").image.setPixel(0, 1, color(10,10,10)) 12 | member("finalfg").image.setPixel(1, 1, color(10,10,10)) 13 | global gLoadedName, levelName 14 | levelName = gLoadedName 15 | member("TextInput").text = gLoadedName 16 | 17 | put "I'M DOING A TEST RENDER!" 18 | alert("I'M DOING A TEST RENDER!") 19 | 20 | go(76) 21 | end -------------------------------------------------------------------------------- /Drizzle.Editor/ViewLocator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.Templates; 4 | using Drizzle.Editor.ViewModels; 5 | 6 | namespace Drizzle.Editor; 7 | 8 | public class ViewLocator : IDataTemplate 9 | { 10 | public bool SupportsRecycling => false; 11 | 12 | public IControl Build(object data) 13 | { 14 | var name = data.GetType().FullName!.Replace("ViewModel", "View"); 15 | var type = Type.GetType(name); 16 | 17 | if (type != null) 18 | { 19 | return (Control)Activator.CreateInstance(type)!; 20 | } 21 | else 22 | { 23 | return new TextBlock { Text = "Not Found: " + name }; 24 | } 25 | } 26 | 27 | public bool Match(object data) 28 | { 29 | return data is ViewModelBase; 30 | } 31 | } -------------------------------------------------------------------------------- /Drizzle.Editor/Views/AboutWindow.axaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /Drizzle.ConsoleApp/Drizzle.ConsoleApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | enable 6 | true 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/LingoGlobal.Global.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Serilog; 3 | 4 | namespace Drizzle.Lingo.Runtime; 5 | 6 | public sealed partial class LingoGlobal 7 | { 8 | public Global _global { get; private set; } = default!; 9 | 10 | public sealed class Global 11 | { 12 | private readonly LingoGlobal _global; 13 | 14 | public Global(LingoGlobal global) 15 | { 16 | _global = global; 17 | } 18 | 19 | public void clearglobals() 20 | { 21 | Log.Debug("Clearing globals"); 22 | var movieScript = _global.MovieScriptInstance; 23 | foreach (var field in movieScript.GetType().GetFields()) 24 | { 25 | if (Attribute.IsDefined(field, typeof(LingoGlobalAttribute))) 26 | field.SetValue(movieScript, null); 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Drizzle.Editor/Views/Render/RenderStageCompletedView.axaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Drizzle.Editor/Views/EditorContentView.axaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Drizzle.Ported/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Ported; 2 | 3 | public enum TileGeometry 4 | { 5 | Air = 0, 6 | SolidWall = 1, 7 | SlopeBL = 2, 8 | SlopeBR = 3, 9 | SlopeTL = 4, 10 | SlopeTR = 5, 11 | Floor = 6, 12 | // I found reference to these two in a random cast member but they don't seem used anywhere. 13 | //Shortcut = 7, 14 | //Nn = 8, 15 | /// 16 | /// Invisible wall. 17 | /// 18 | Glass = 9 19 | } 20 | 21 | public enum TileFeature 22 | { 23 | BeamHorizontal = 1, 24 | BeamVertical = 2, 25 | Hive = 3, 26 | ShortcutEntrance = 4, 27 | Shortcut = 5, 28 | Entrance = 6, 29 | DragonDen = 7, 30 | Rock = 9, 31 | Spear = 10, 32 | Crack = 11, 33 | ForbidFlyChains = 12, 34 | GarbageWormHole = 13, 35 | Waterfall = 18, 36 | WhackAMoleHole = 19, 37 | WormGrass = 20, 38 | ScavengerHole = 21, 39 | } -------------------------------------------------------------------------------- /Drizzle.Editor/Views/EditorTabs/TabLevelOverviewView.axaml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Drizzle.Editor/Views/Render/RenderStageErrorView.axaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Drizzle.Logic.Tests/Drizzle.Logic.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | 8 | false 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /LingoSource/getExtraTile.lingo: -------------------------------------------------------------------------------- 1 | global extraBufferTiles, newSize 2 | 3 | on change me 4 | --put "HEJ" & me.spriteNum 5 | --extraBufferTiles[me.spriteNum-49] = value(sprite(me.spriteNum).text) 6 | if(me.spriteNum = 48) then 7 | newSize[1] = value(sprite(me.spriteNum).text) 8 | else if(me.spriteNum = 49) then 9 | newSize[2] = value(sprite(me.spriteNum).text) 10 | else if(me.spriteNum = 50) then 11 | extraBufferTiles[1] = value(sprite(me.spriteNum).text) 12 | else if(me.spriteNum = 51) then 13 | extraBufferTiles[2] = value(sprite(me.spriteNum).text) 14 | else if(me.spriteNum = 52) then 15 | extraBufferTiles[3] = value(sprite(me.spriteNum).text) 16 | else if(me.spriteNum = 53) then 17 | extraBufferTiles[4] = value(sprite(me.spriteNum).text) 18 | else if(me.spriteNum = 54) then 19 | newSize[3] = value(sprite(me.spriteNum).text) 20 | else if(me.spriteNum = 55) then 21 | newSize[4] = value(sprite(me.spriteNum).text) 22 | end if 23 | end -------------------------------------------------------------------------------- /Drizzle.Editor/Views/Render/RenderStageLightView.axaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Drizzle.Editor/Views/Render/RenderStageLayersView.axaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /LingoSource/envEditorStart.lingo: -------------------------------------------------------------------------------- 1 | global gLOprops, gLeProps, gEnvEditButtons, gLastEnvEditButtons 2 | 3 | 4 | on exitFrame me 5 | cols = gLOprops.size.loch 6 | rows = gLOprops.size.locv 7 | 8 | -- member("TEimg1").image = image(cols*5, rows*4, 16) 9 | -- member("TEimg2").image = image(cols*5, rows*4, 16) 10 | -- member("TEimg3").image = image(cols*5, rows*4, 16) 11 | -- 12 | -- member("levelEditImageShortCuts").image = image(cols*5, rows*5, 1) 13 | 14 | repeat with l = 1 to 3 then 15 | miniLvlEditDraw(l) 16 | end repeat 17 | 18 | gEnvEditButtons = [#w:0, #f:0] 19 | gLastEnvEditButtons = gEnvEditButtons.duplicate() 20 | end 21 | 22 | 23 | 24 | on checkKey me, key 25 | rtrn = 0 26 | gEnvEditButtons[symbol(key)] = _key.keyPressed(key) and _movie.window.sizeState <> #minimized 27 | if (gEnvEditButtons[symbol(key)])and(gLastEnvEditButtons[symbol(key)]=0) then 28 | rtrn = 1 29 | end if 30 | gLastEnvEditButtons[symbol(key)] = gEnvEditButtons[symbol(key)] 31 | return rtrn 32 | end -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Cast/CastMember.Text.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | 4 | namespace Drizzle.Lingo.Runtime.Cast; 5 | 6 | public sealed partial class CastMember 7 | { 8 | private string _text = ""; 9 | 10 | public string text 11 | { 12 | get 13 | { 14 | AssertType(CastMemberType.Text); 15 | return _text; 16 | } 17 | set 18 | { 19 | AssertType(CastMemberType.Text); 20 | _text = value; 21 | } 22 | } 23 | 24 | public LingoSymbol alignment { get; set; } 25 | 26 | private void ImportFileImplText(string path) 27 | { 28 | using var sr = new StreamReader(path); 29 | var sb = new StringBuilder(); 30 | 31 | while (true) 32 | { 33 | var line = sr.ReadLine(); 34 | if (line == null) 35 | break; 36 | 37 | sb.Append(line); 38 | sb.Append('\r'); 39 | } 40 | 41 | text = sb.ToString(); 42 | } 43 | } -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Data/LingoSymbol.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Drizzle.Lingo.Runtime; 4 | 5 | public readonly struct LingoSymbol : IEquatable 6 | { 7 | public string Value { get; } 8 | 9 | public LingoSymbol(string value) 10 | { 11 | Value = value; 12 | } 13 | 14 | public override string ToString() => $"#{Value}"; 15 | 16 | 17 | public static bool operator ==(LingoSymbol a, LingoSymbol b) 18 | { 19 | return a.Value.Equals(b.Value, StringComparison.OrdinalIgnoreCase); 20 | } 21 | 22 | public static bool operator !=(LingoSymbol a, LingoSymbol b) 23 | { 24 | return !(a == b); 25 | } 26 | 27 | public bool Equals(LingoSymbol other) 28 | { 29 | return Value == other.Value; 30 | } 31 | 32 | public override bool Equals(object? obj) 33 | { 34 | return obj is LingoSymbol other && Equals(other); 35 | } 36 | 37 | public override int GetHashCode() 38 | { 39 | return Value.GetHashCode(); 40 | } 41 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2021 Pieter-Jan Briers 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Tests/ParseTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using Drizzle.Lingo.Runtime.Parser; 6 | using NUnit.Framework; 7 | using Pidgin; 8 | 9 | namespace Drizzle.Lingo.Tests; 10 | 11 | [Parallelizable(ParallelScope.All)] 12 | [TestFixture] 13 | public sealed class ParseTest 14 | { 15 | private static readonly string SourcesRoot = Path.Combine("..", "..", "..", "..", "LingoSource"); 16 | 17 | public static IEnumerable GetSources() 18 | { 19 | return Directory.EnumerateFiles(SourcesRoot, "*.lingo").Select(Path.GetFileName); 20 | } 21 | 22 | [Test] 23 | public void Test([ValueSource(nameof(GetSources))] string fileName) 24 | { 25 | var fullPath = Path.Combine(SourcesRoot, fileName); 26 | 27 | var reader = new StreamReader(fullPath); 28 | var result = LingoParser.Script.ParseOrThrow(reader); 29 | 30 | TestContext.Out.Write("Parsed AST:"); 31 | TestContext.Out.Write(DebugPrint.PrintAstNode(result)); 32 | } 33 | } -------------------------------------------------------------------------------- /Drizzle.Editor/ViewModels/MainEditorTabViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Drizzle.Lingo.Runtime; 3 | using Drizzle.Logic; 4 | using ReactiveUI.Fody.Helpers; 5 | using Serilog; 6 | 7 | namespace Drizzle.Editor.ViewModels; 8 | 9 | public sealed class MainEditorTabViewModel : ViewModelBase 10 | { 11 | [Reactive] public string LevelName { get; private set; } 12 | [Reactive] public EditorContentViewModel? Content { get; private set; } 13 | 14 | public MainEditorTabViewModel(string levelName) 15 | { 16 | LevelName = levelName; 17 | } 18 | 19 | public async void InitLoad(Task zygote, string fullPath) 20 | { 21 | var zygoteInstance = await zygote; 22 | var runtime = await Task.Run(() => 23 | { 24 | var cloned = zygoteInstance.Clone(); 25 | 26 | Log.Debug("Loading level..."); 27 | 28 | EditorRuntimeHelpers.RunLoadLevel(cloned, fullPath); 29 | 30 | return cloned; 31 | }); 32 | 33 | Content = new EditorContentViewModel(runtime); 34 | } 35 | } -------------------------------------------------------------------------------- /Drizzle.Logic/Rendering/RenderStatus.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Drizzle.Logic.Rendering; 4 | 5 | public record RenderStatus(int CameraIndex, int CountCamerasDone, bool IsPaused, RenderStageStatus Stage); 6 | 7 | public record RenderStageStatus(RenderStage Stage); 8 | 9 | public record RenderStageStatusLayers(int CurrentLayer) : RenderStageStatus(RenderStage.RenderLayers); 10 | 11 | // Pass in stage here due to pre/post effects distinction. 12 | public record RenderStageStatusProps(RenderStage Stage) : RenderStageStatus(Stage); 13 | 14 | public record RenderStageStatusEffects( 15 | int TotalEffectsCount, 16 | int CurrentEffect, 17 | int VertRepeater, 18 | IReadOnlyList EffectNames) 19 | : RenderStageStatus(RenderStage.RenderEffects); 20 | 21 | public record RenderStageStatusLight(int CurrentLayer) : RenderStageStatus(RenderStage.RenderLight); 22 | 23 | public record RenderStageStatusFinalize() : RenderStageStatus(RenderStage.Finalize); 24 | 25 | public record RenderStageStatusRenderColors() : RenderStageStatus(RenderStage.SaveFile); -------------------------------------------------------------------------------- /Drizzle.Editor/Views/LingoStatus.axaml: -------------------------------------------------------------------------------- 1 | 5 | 17 | -------------------------------------------------------------------------------- /Drizzle.Benchmarks/Drizzle.Benchmarks.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | enable 6 | enable 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Drizzle, a Rain World level editor 2 | 3 | Drizzle is a port and gradual rewrite of the official Rain World level editor (RWLE). Primary goals are to **make renders faster** and to **have a better interface**. 4 | 5 | ## Compiling and running 6 | 7 | To run drizzle, you currently need to: 8 | 1. `git submodule update --init` to initialize the `Data/` submodule. 9 | 2. run `Drizzle.Transpiler` to transpile the Lingo code to C#. 10 | 3. run `Drizzle.Editor` or `Drizzle.ConsoleApp`, off you go! 11 | 12 | ## Project structure 13 | 14 | The project is organized as such: 15 | * `Drizzle.Lingo.Runtime`: Includes core logic necessary to run Lingo code required by RWLE. 16 | * `Drizzle.Transpiler`: Transpiles Lingo into extremely messy, `dynamic` heavy C#. Requires `Drizzle.Lingo.Runtime` to parse Lingo. 17 | * `Drizzle.Ported`: Contains transpiled C# code output by `Drizzle.Transpiler`. 18 | * `Drizzle.Logic`: Contains C# logic shared between console app and GUI renderer, interfacing with the transpiled code. 19 | * `Drizzle.ConsoleApp`: Console application for headless renders. 20 | * `Drizzle.Editor`: GUI editor using Avalonia. -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Utility/ImageSharpExt.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using SixLabors.ImageSharp; 5 | using SixLabors.ImageSharp.PixelFormats; 6 | 7 | namespace Drizzle.Lingo.Runtime; 8 | 9 | public static class ImageSharpExt 10 | { 11 | private const string KRITA = @"C:\Program Files\Krita (x64)\bin\krita.exe"; 12 | 13 | public static void ShowImage(this Image img) 14 | { 15 | var tmp = Path.Combine(Path.GetTempPath(), $"{Guid.NewGuid()}.png"); 16 | { 17 | using var file = File.Create(tmp); 18 | img.SaveAsPng(file); 19 | } 20 | 21 | Process.Start(new ProcessStartInfo(KRITA) 22 | { 23 | UseShellExecute = true, 24 | ArgumentList = { tmp } 25 | }); 26 | } 27 | 28 | public static Span GetSinglePixelSpan(this Image img) where T : unmanaged, IPixel 29 | { 30 | if (!img.DangerousTryGetSinglePixelMemory(out var memory)) 31 | throw new InvalidOperationException("Unable to get single pixel span!"); 32 | 33 | return memory.Span; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LingoSource/renderPropsStart.lingo: -------------------------------------------------------------------------------- 1 | global c, keepLooping, afterEffects, gLastImported, gRenderTrashProps, gCurrentlyRenderingTrash, softProp, propsToRender, gPEprops 2 | 3 | 4 | on exitFrame me 5 | type val: number 6 | c = 1 7 | keepLooping = 1 8 | --Set by LevelRenderer.cs now. 9 | --afterEffects = (_movie.frame > 51) 10 | gLastImported = "" 11 | gCurrentlyRenderingTrash = false 12 | if(gRenderTrashProps.count > 0)and(afterEffects=0)then 13 | gCurrentlyRenderingTrash = true 14 | end if 15 | 16 | repeat with q = 0 to 29 then 17 | sprite(50-q).loc = point((1024/2)-q, (768/2)-q) 18 | val = (q.float+1.0)/30.0 19 | sprite(50-q).color = color(val*255, val*255, val*255) 20 | end repeat 21 | 22 | propsToRender = [] 23 | repeat with a = 1 to gPEprops.props.count then 24 | propsToRender.add(gPEprops.props[a]) 25 | propsToRender[propsToRender.count].addAt(1, propsToRender[propsToRender.count][5].settings.renderOrder) 26 | end repeat 27 | propsToRender.sort() 28 | repeat with a = 1 to propsToRender.count then 29 | propsToRender[a].deleteAt(1) 30 | end repeat 31 | 32 | softProp = void 33 | end -------------------------------------------------------------------------------- /DrizzleEdit.sln.DotSettings: -------------------------------------------------------------------------------- 1 | 2 | BR 3 | IO 4 | TL 5 | VM 6 | True 7 | True 8 | True 9 | True -------------------------------------------------------------------------------- /LingoSource/exportAllBitmaps.lingo: -------------------------------------------------------------------------------- 1 | on saveImages savePath 2 | retVal = 1 3 | -- check savePath 4 | if (not stringP(savePath)) and (not savePath contains the dirSeparator) then 5 | retVal = -1 6 | return retVal 7 | else 8 | if the last char of savePath <> the dirSeparator then 9 | savePath = savePath & the dirSeparator 10 | end if 11 | end if 12 | 13 | -- create instance of ImgXtra 14 | imgObj = xtra("ImgXtra").new() 15 | 16 | -- iterate through the Internatl Cast 17 | -- and save as bmp using the member name, 18 | -- or if empty then "member_#" 19 | m = castLib(4).member.count 20 | repeat with i = 1 to m 21 | if member(i, 4).type = #bitmap then 22 | fName = member(i, 4).name & ".bmp" 23 | if fName = ".bmp" then fName = "member_" & i & ".bmp" 24 | put "savePath: " & savePath & fName 25 | ixErr = imgObj.ix_saveImage(["image": member(i, 4).image, "filename": savePath & fName, "format": "BMP"]) 26 | if ixErr = 0 then 27 | retVal = -2 28 | end if 29 | end if 30 | end repeat 31 | 32 | -- destroy instance of ImgXtra 33 | imgXtra = 0 34 | 35 | return retVal 36 | end -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/LingoGlobal.Scripting.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Drizzle.Lingo.Runtime.Parser; 4 | using Drizzle.Lingo.Runtime.Scripting; 5 | using Pidgin; 6 | 7 | namespace Drizzle.Lingo.Runtime; 8 | 9 | [SuppressMessage("ReSharper", "InconsistentNaming")] 10 | public sealed partial class LingoGlobal 11 | { 12 | public LingoScriptRuntime ScriptRuntime { get; private set; } = default!; 13 | 14 | public dynamic? value(string a) 15 | { 16 | var trimmed = a.AsSpan().Trim(); 17 | if (trimmed.IsEmpty) 18 | return new LingoNumber(0); // value() returns zero on empty string. 19 | 20 | // NOTE: This uses ExpressionNoOps, so expressions like "5 + 10" aren't gonna be parsed correctly. 21 | // This is fine for the level editor, but if you ever do something funny, you've been warned. 22 | try 23 | { 24 | var parsedExpression = LingoParser.ExpressionNoOps.ParseOrThrow(trimmed); 25 | return Interpreter.Evaluate(parsedExpression, LingoRuntime); 26 | } 27 | catch 28 | { 29 | return null; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /LingoSource/sliderBehav.lingo: -------------------------------------------------------------------------------- 1 | 2 | global gLOprops, gEditLizard, gLevel 3 | 4 | on mouseWithin(me) 5 | sprite(me.spriteNum).color = color(255,0,0) 6 | -- if(gLOprops.mouseClick) then 7 | -- put sprite(me.spriteNum).member.name 8 | -- script("levelOverview").buttonClicked(sprite(me.spriteNum).member.name) 9 | -- gLOprops.mouseClick = 0 10 | -- end if 11 | if _mouse.mouseDown then 12 | -- put me.spriteNum 13 | val = restrict(_mouse.mouseLoc.locH, 50, 450)-50 14 | sprite(me.spriteNum).loch = val+50 15 | 16 | case sprite(me.spriteNum).member.name of 17 | "tileSeedSlider": 18 | gLOprops.tileSeed = val 19 | put "seed changed!" 20 | the randomSeed = gLOprops.tileSeed 21 | end case 22 | end if 23 | 24 | case sprite(me.spriteNum).member.name of 25 | 26 | "tileSeedSlider": 27 | member("buttonText").text = "Tile random seed:" && string(gLOprops.tileSeed) 28 | end case 29 | end mouseWithin 30 | 31 | on mouseLeave(me) 32 | sprite(me.spriteNum).color = color(0,0,0) 33 | member("buttonText").text = "" 34 | sprite(20).quad = [point(-100,-100), point(-100,-100), point(-100,-100), point(-100,-100)] 35 | end mouseLeave -------------------------------------------------------------------------------- /Drizzle.Editor/Helpers/TrickHitTestOperation.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Media; 3 | using Avalonia.Media.Immutable; 4 | using Avalonia.Platform; 5 | using Avalonia.Rendering.SceneGraph; 6 | 7 | namespace Drizzle.Editor.Helpers; 8 | 9 | public sealed class TrickHitTestOperation : ICustomDrawOperation 10 | { 11 | /* 12 | private readonly ImmutablePen _penRed = new ImmutablePen(Brushes.Red); 13 | private readonly ImmutableSolidColorBrush _brushBlue = new ImmutableSolidColorBrush(new Color(0x80, 0x80, 0x80, 0xff)); 14 | */ 15 | 16 | public TrickHitTestOperation(Rect bounds) 17 | { 18 | Bounds = bounds; 19 | } 20 | 21 | public void Dispose() 22 | { 23 | } 24 | 25 | public bool HitTest(Point p) 26 | { 27 | return Bounds.Contains(p); 28 | } 29 | 30 | public void Render(IDrawingContextImpl context) 31 | { 32 | // context.DrawRectangle(_brushBlue, _penRed, new RoundedRect(Bounds)); 33 | } 34 | 35 | public Rect Bounds { get; } 36 | 37 | public bool Equals(ICustomDrawOperation? other) 38 | { 39 | return other is TrickHitTestOperation { Bounds: var b } && b == Bounds; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LingoSource/lightEditorStart.lingo: -------------------------------------------------------------------------------- 1 | global gTEprops, gTiles, gEEprops, gLightEProps, firstFrame, geverysecond, glgtimgQuad, gDirectionKeys,gLOprops 2 | 3 | on exitFrame me 4 | firstFrame = 1 5 | l = [ #m1:1, m2:0, #w:0, #a:0, #s:0, #d:0, #r:0, f:0] 6 | gLightEProps.lastKeys = l.duplicate() 7 | gLightEProps.keys = l.duplicate() 8 | 9 | repeat with l = 1 to 3 then 10 | miniLvlEditDraw(l) 11 | end repeat 12 | 13 | geverysecond = 0 14 | 15 | gDirectionKeys = [0,0,0,0] 16 | 17 | glgtimgQuad = [point(0,0), point(member("lightImage").image.width,0), point(member("lightImage").image.width,member("lightImage").image.height), point(0,member("lightImage").image.height)] 18 | 19 | gLightEProps.lastTm = _system.milliseconds 20 | 21 | sprite(11).member = member("pxl") 22 | sprite(12).member = member("pxl") 23 | gLightEProps.paintShape = "pxl" 24 | 25 | sprite(5).rect = rect(0,0,gLOprops.size.loch*20,gLOprops.size.locv*20) 26 | 27 | sprite(8).rect = rect(0,0,gLOprops.size.loch*20,gLOprops.size.locv*20) 28 | 29 | sprite(9).member = member("lightImage") 30 | sprite(10).member = member("lightImage") 31 | sprite(6).member = member("lightImage") 32 | end 33 | 34 | 35 | -------------------------------------------------------------------------------- /Drizzle.Editor/ViewModels/Render/RenderStageEffectsViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Avalonia.Media; 5 | using Drizzle.Logic; 6 | using Drizzle.Logic.Rendering; 7 | 8 | namespace Drizzle.Editor.ViewModels.Render; 9 | 10 | public sealed class RenderStageEffectsViewModel : RenderStageViewModelBase 11 | { 12 | public IReadOnlyList Effects { get; } 13 | 14 | public override (int max, int current)? Progress { get; } 15 | 16 | public RenderStageEffectsViewModel(RenderStageStatusEffects status) 17 | { 18 | Effects = status.EffectNames 19 | .Select((x, i) => new RenderSingleEffectViewModel(x, i == status.CurrentEffect - 1)) 20 | .ToArray(); 21 | 22 | Progress = (status.TotalEffectsCount * 60, (status.CurrentEffect - 1) * 60 + status.VertRepeater); 23 | } 24 | } 25 | 26 | public sealed class RenderSingleEffectViewModel : ViewModelBase 27 | { 28 | public string Name { get; } 29 | public bool Current { get; } 30 | 31 | public RenderSingleEffectViewModel(string name, bool current) 32 | { 33 | Name = name; 34 | Current = current; 35 | } 36 | } -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Scripting/LingoScriptRuntime.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Dynamic; 3 | using System.Linq.Expressions; 4 | 5 | namespace Drizzle.Lingo.Runtime.Scripting; 6 | 7 | public sealed class LingoScriptRuntime 8 | { 9 | private readonly Dictionary _getMemberBinders = new(); 10 | private readonly Dictionary _binaryOperationBinders = new(); 11 | 12 | public LingoScriptRuntime(LingoGlobal global) 13 | { 14 | Global = global; 15 | } 16 | 17 | public LingoGlobal Global { get; } 18 | 19 | public GetMemberBinder GetGetMemberBinder(string memberName) 20 | { 21 | if (!_getMemberBinders.TryGetValue(memberName, out var binder)) 22 | _getMemberBinders[memberName] = binder = new LingoGetMemberBinder(memberName); 23 | 24 | return binder; 25 | } 26 | 27 | public BinaryOperationBinder GetBinaryOperationBinder(ExpressionType type) 28 | { 29 | if (!_binaryOperationBinders.TryGetValue(type, out var binder)) 30 | _binaryOperationBinders[type] = binder = new LingoBinaryOperationBinder(type); 31 | 32 | return binder; 33 | } 34 | } -------------------------------------------------------------------------------- /Drizzle.Editor/Helpers/Bresenham.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Drizzle.Logic; 3 | 4 | namespace Drizzle.Editor.Helpers; 5 | 6 | public static class Bresenham 7 | { 8 | public static IEnumerable PlotLine(Vector2i from, Vector2i to) 9 | { 10 | // From https://circuitcellar.com/resources/bresenhams-algorithm/ 11 | var (x0, y0) = from; 12 | var (x1, y1) = to; 13 | 14 | var dx = x1 >= x0 ? x1 - x0 : x0 - x1; 15 | var dy = y1 >= y0 ? y0 - y1 : y1 - y0; 16 | var sx = x0 < x1 ? 1 : -1; 17 | var sy = y0 < y1 ? 1 : -1; 18 | var err = dx + dy; 19 | var x = x0; 20 | var y = y0; 21 | 22 | while (true) 23 | { 24 | yield return (x, y); 25 | if (x == x1 && y == y1) 26 | break; 27 | 28 | var e2 = 2 * err; 29 | if (e2 >= dy) 30 | { 31 | // step x 32 | err += dy; 33 | x += sx; 34 | } 35 | 36 | if (e2 <= dx) 37 | { 38 | // step y 39 | err += dx; 40 | y += sy; 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Drizzle.Editor/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.ReactiveUI; 3 | using Drizzle.Lingo.Runtime.Utils; 4 | using Serilog; 5 | using Serilog.Sinks.SystemConsole.Themes; 6 | using SixLabors.ImageSharp; 7 | 8 | namespace Drizzle.Editor; 9 | 10 | class Program 11 | { 12 | // Initialization code. Don't use any Avalonia, third-party APIs or any 13 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 14 | // yet and stuff might break. 15 | public static void Main(string[] args) 16 | { 17 | CultureFix.FixCulture(); 18 | 19 | Configuration.Default.PreferContiguousImageBuffers = true; 20 | 21 | Log.Logger = new LoggerConfiguration() 22 | .MinimumLevel.Debug() 23 | .WriteTo.Console(theme: AnsiConsoleTheme.Literate) 24 | .CreateLogger(); 25 | 26 | BuildAvaloniaApp() 27 | .StartWithClassicDesktopLifetime(args); 28 | } 29 | 30 | // Avalonia configuration, don't remove; also used by visual designer. 31 | public static AppBuilder BuildAvaloniaApp() 32 | => AppBuilder.Configure() 33 | .UsePlatformDetect() 34 | .LogToTrace() 35 | .UseReactiveUI(); 36 | } 37 | -------------------------------------------------------------------------------- /Drizzle.Logic/EditorRuntimeHelpers.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Drizzle.Lingo.Runtime; 3 | using Drizzle.Ported; 4 | 5 | namespace Drizzle.Logic; 6 | 7 | public static class EditorRuntimeHelpers 8 | { 9 | public static void RunStartup(LingoRuntime runtime) 10 | { 11 | var startUp = runtime.CreateScript(); 12 | 13 | startUp.exitframe(); 14 | } 15 | 16 | public static void RunLoadLevel(LingoRuntime runtime, string filePath) 17 | { 18 | var abs = Path.GetFullPath(filePath); 19 | 20 | var withoutExt = Path.Combine( 21 | Path.GetDirectoryName(abs)!, 22 | Path.GetFileNameWithoutExtension(abs)); 23 | 24 | runtime.CreateScript().loadlevel(withoutExt, new LingoNumber(1)); 25 | } 26 | 27 | // Effectively afaMvLvlEdit in spelrelaterat 28 | public static TileGeometry GetTileGeomBordered(LingoRuntime runtime, Vector2i vec, int layer) 29 | { 30 | var mv = runtime.MovieScript(); 31 | var size = (LingoPoint) mv.gLOprops.size; 32 | if (vec.X < 1 || vec.Y < 1 || vec.X > size.loch || vec.Y > size.locv) 33 | return TileGeometry.SolidWall; 34 | 35 | return (TileGeometry)(int)(LingoNumber)mv.gLEProps.matrix[vec.X][vec.Y][layer][1]; 36 | } 37 | } -------------------------------------------------------------------------------- /Drizzle.Editor/Views/Render/RenderWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia; 3 | using Avalonia.Controls; 4 | using Avalonia.Markup.Xaml; 5 | using Avalonia.Threading; 6 | using Drizzle.Editor.ViewModels.Render; 7 | 8 | namespace Drizzle.Editor.Views.Render; 9 | 10 | public partial class RenderWindow : Window 11 | { 12 | private IDisposable? _renderTimer; 13 | 14 | public RenderWindow() 15 | { 16 | InitializeComponent(); 17 | #if DEBUG 18 | this.AttachDevTools(); 19 | #endif 20 | } 21 | 22 | private void TopLevel_OnClosed(object? sender, EventArgs e) 23 | { 24 | (DataContext as RenderViewModel)?.StopRender(); 25 | 26 | _renderTimer?.Dispose(); 27 | } 28 | 29 | private void TopLevel_OnOpened(object? sender, EventArgs e) 30 | { 31 | _renderTimer = DispatcherTimer.Run(() => 32 | { 33 | UpdateElapsedText(); 34 | return true; 35 | }, TimeSpan.FromMilliseconds(50), DispatcherPriority.Background); 36 | } 37 | 38 | private void UpdateElapsedText() 39 | { 40 | if (DataContext is not RenderViewModel vm) 41 | return; 42 | 43 | this.FindControl("ElapsedText").Text = vm.RenderTimeElapsed.ToString(@"mm\:ss\.f"); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Drizzle.Editor/ViewModels/EditorTabs/TabGeometryEditorViewModel.cs: -------------------------------------------------------------------------------- 1 | using ReactiveUI.Fody.Helpers; 2 | 3 | namespace Drizzle.Editor.ViewModels.EditorTabs; 4 | 5 | public sealed class TabGeometryEditorViewModel : EditorTabViewModelBase 6 | { 7 | public override string Title => "Geometry"; 8 | 9 | public EditorContentViewModel ParentVm { get; } 10 | 11 | [Reactive] public GeometryPlacementTool PlacingTool { get; set; } = GeometryPlacementTool.Wall; 12 | 13 | [Reactive] public bool Layer1Visible { get; set; } = true; 14 | [Reactive] public bool Layer2Visible { get; set; } = true; 15 | [Reactive] public bool Layer3Visible { get; set; } = true; 16 | 17 | public TabGeometryEditorViewModel(EditorContentViewModel parentVm) 18 | { 19 | ParentVm = parentVm; 20 | } 21 | } 22 | 23 | public enum GeometryPlacementTool : byte 24 | { 25 | // Tile geometry 26 | Wall, 27 | Slope, 28 | Floor, 29 | Glass, 30 | 31 | // Tile features 32 | BeamHorizontal, 33 | BeamVertical, 34 | Hive, 35 | ShortcutEntrance, 36 | Shortcut, 37 | Entrance, 38 | DragonDen, 39 | Rock, 40 | Spear, 41 | Crack, 42 | ForbidFlyChains, 43 | GarbageWormHole, 44 | Waterfall, 45 | WhackAMoleHole, 46 | WormGrass, 47 | ScavengerHole, 48 | } -------------------------------------------------------------------------------- /Drizzle.Logic/Rendering/RenderCmd.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Drizzle.Logic.Rendering; 4 | 5 | public abstract record RenderCmd; 6 | 7 | public record RenderCmdSetPaused(bool Paused) : RenderCmd; 8 | 9 | public record RenderCmdSingleStep : RenderCmd; 10 | 11 | public record RenderCmdCancel : RenderCmd; 12 | 13 | public record RenderCmdExec(Action Action) : RenderCmd; 14 | 15 | /// 16 | /// The rendering thread may send a set of preview images for display in the UI. 17 | /// 18 | public record RenderCmdReqPreview : RenderCmd; 19 | 20 | [Serializable] 21 | public class RenderCancelledException : OperationCanceledException 22 | { 23 | public RenderCancelledException() 24 | { 25 | } 26 | 27 | public RenderCancelledException(string message) : base(message) 28 | { 29 | } 30 | 31 | public RenderCancelledException(string message, Exception inner) : base(message, inner) 32 | { 33 | } 34 | } 35 | 36 | [Serializable] 37 | public class RenderCameraException : Exception 38 | { 39 | public RenderCameraException() 40 | { 41 | } 42 | 43 | public RenderCameraException(string message) : base(message) 44 | { 45 | } 46 | 47 | public RenderCameraException(string message, Exception inner) : base(message, inner) 48 | { 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Tests/Drizzle.Lingo.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | 6 | 7 | 8 | 9 | all 10 | runtime; build; native; contentfiles; analyzers; buildtransitive 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Drizzle.Editor/FodyWeavers.xsd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. 12 | 13 | 14 | 15 | 16 | A comma-separated list of error codes that can be safely ignored in assembly verification. 17 | 18 | 19 | 20 | 21 | 'false' to turn off automatic generation of the XML Schema file. 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Drizzle.Editor/ViewModels/EditorContentViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Drizzle.Editor.ViewModels.EditorTabs; 5 | using Drizzle.Lingo.Runtime; 6 | using Drizzle.Logic; 7 | using Drizzle.Ported; 8 | 9 | namespace Drizzle.Editor.ViewModels; 10 | 11 | public sealed class EditorContentViewModel : ViewModelBase, ILingoRuntimeManager 12 | { 13 | public LingoRuntime Runtime { get; } 14 | 15 | public IReadOnlyList EditorTabs { get; } 16 | public int CountCameras => (int) MovieScript.gCameraProps.cameras.count; 17 | private MovieScript MovieScript => (MovieScript)Runtime.MovieScriptInstance; 18 | 19 | public EditorContentViewModel(LingoRuntime runtime) 20 | { 21 | Runtime = runtime; 22 | 23 | EditorTabs = new EditorTabViewModelBase[] 24 | { 25 | new TabLevelOverviewViewModel(this), 26 | new TabGeometryEditorViewModel(this), 27 | new TabTileEditorViewModel() 28 | }; 29 | } 30 | 31 | public Task Exec(Action action) 32 | { 33 | action(Runtime); 34 | return Task.CompletedTask; 35 | } 36 | 37 | public Task Exec(Func func) 38 | { 39 | return Task.FromResult(func(Runtime)); 40 | } 41 | } -------------------------------------------------------------------------------- /LingoSource/levelEditStart.lingo: -------------------------------------------------------------------------------- 1 | global gLEProps, gLOprops, gDirectionKeys 2 | 3 | on exitFrame me 4 | 5 | cols = gLOprops.size.loch 6 | rows = gLOprops.size.locv 7 | member("levelEditImage1").image = image(52*16, 40*16, 16) 8 | member("levelEditImage2").image = image(52*16, 40*16, 16) 9 | member("levelEditImage3").image = image(52*16, 40*16, 16) 10 | member("levelEditImageShortCuts").image = image(52*16, 40*16, 16) 11 | lvlEditDraw(rect(1,1,cols,rows), 1) 12 | lvlEditDraw(rect(1,1,cols,rows), 2) 13 | lvlEditDraw(rect(1,1,cols,rows), 3) 14 | drawShortCutsImg(rect(1,1,cols,rows), 16) 15 | 16 | gDirectionKeys = [0,0,0,0] 17 | 18 | repeat with q = 800 to 820 then 19 | sprite(q).visibility = 1 20 | end repeat 21 | sprite(2).visibility = 1 22 | sprite(8).visibility = 1 23 | 24 | member("toolsImage").image = image(gLEProps.toolMatrix[1].count*32, gLEProps.toolMatrix.count*32, 16) 25 | repeat with q = 1 to gLEProps.toolMatrix.count then 26 | repeat with c = 1 to gLEProps.toolMatrix[1].count then 27 | rct = rect((c-1)*32, (q-1)*32, c*32, q*32) 28 | nm = "icon"&gLEProps.toolMatrix[q][c] 29 | member("toolsImage").image.copyPixels(member("icon"&gLEProps.toolMatrix[q][c]).image, rct, rect(0,0,32,32)) 30 | end repeat 31 | end repeat 32 | 33 | gLEprops.levelEditors[1].p.mirrorPos = gLOprops.size.loch/2 34 | end -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/LingoSprite.cs: -------------------------------------------------------------------------------- 1 | using Drizzle.Lingo.Runtime.Cast; 2 | 3 | namespace Drizzle.Lingo.Runtime; 4 | 5 | public sealed class LingoSprite 6 | { 7 | public LingoRect rect { get; set; } 8 | 9 | public LingoNumber locv 10 | { 11 | get => loc.locv; 12 | set => loc = new LingoPoint(loch, value); 13 | } 14 | 15 | public LingoNumber loch 16 | { 17 | get => loc.loch; 18 | set => loc = new LingoPoint(value, loch); 19 | } 20 | 21 | public dynamic? visibility { get; set; } // Fairly certain this is invalid. 22 | public LingoNumber visible { get; set; } 23 | 24 | public CastMember? member { get; set; } 25 | 26 | public LingoNumber blend { get; set; } = 100; 27 | 28 | public LingoColor color { get; set; } 29 | public LingoColor bgcolor { get; set; } 30 | 31 | public LingoColor forecolor 32 | { 33 | get => color; 34 | set => color = value; 35 | } 36 | public LingoColor backcolor 37 | { 38 | get => bgcolor; 39 | set => bgcolor = value; 40 | } 41 | 42 | // This is the REGISTRATION POINT 43 | public LingoPoint loc { get; set; } 44 | 45 | public LingoList quad { get; set; } = new(); 46 | 47 | public LingoNumber linesize { get; set; } 48 | 49 | public string text { get; set; } = ""; 50 | 51 | public LingoSprite() 52 | { 53 | 54 | } 55 | } -------------------------------------------------------------------------------- /LingoSource/loadLevelStart.lingo: -------------------------------------------------------------------------------- 1 | global projects, ldPrps, gLOADPATH 2 | 3 | on exitFrame me 4 | projects = [] 5 | 6 | pth = the moviePath & "LevelEditorProjects" & the dirSeparator 7 | repeat with f in gLOADPATH then 8 | pth = pth & the dirSeparator & f 9 | end repeat 10 | 11 | fileList = [ ] 12 | repeat with i = 1 to 300 then 13 | n = getNthFileNameInFolder(pth, i) 14 | if n = EMPTY then exit repeat 15 | if (char n.length-3 of n <> ".")then 16 | projects.add("#" & n) 17 | else 18 | fileList.append(n) 19 | end if 20 | end repeat 21 | 22 | 23 | 24 | 25 | repeat with l in fileList then 26 | if chars(l, l.length-3, l.length) = ".txt" then 27 | projects.add( chars(l, 1, l.length-4)) 28 | end if 29 | end repeat 30 | 31 | txt = "Use the arrow keys to select a project. Use enter to open it." 32 | put RETURN after txt 33 | repeat with f in gLOADPATH then 34 | put f & "/" after txt 35 | end repeat 36 | put RETURN after txt 37 | put RETURN after txt 38 | repeat with q in projects then 39 | put q after txt 40 | put RETURN after txt 41 | end repeat 42 | 43 | ldPrps = [#lstUp:1, lstDwn:1, #lft:1, #rgth:1, #currProject:1, #listScrollPos:1, #listShowTotal:30] 44 | 45 | member("ProjectsL").text = txt 46 | 47 | member("PalName").text = "Press 'N' to create a new level. Use left and right arrows to step in and out of subfolders" 48 | end -------------------------------------------------------------------------------- /Drizzle.Lingo.Tests/LingoImagePixelOpsTest.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System.Runtime.Intrinsics; 3 | using Drizzle.Lingo.Runtime; 4 | using NUnit.Framework; 5 | using SixLabors.ImageSharp.PixelFormats; 6 | 7 | namespace Drizzle.Lingo.Tests; 8 | 9 | [TestFixture] 10 | [Parallelizable(ParallelScope.All)] 11 | [TestOf(typeof(LingoImage))] 12 | public sealed class LingoImagePixelOpsTest 13 | { 14 | [Test] 15 | public void TestL8Read8() 16 | { 17 | var data = new byte[16]; 18 | data[3] = 123; 19 | 20 | var result = LingoImage.PixelOpsL8.Read8( 21 | MemoryMarshal.Cast(data), 22 | 0, 23 | Vector256.AllBitsSet); 24 | } 25 | 26 | [Test] 27 | public void TestL8Write8() 28 | { 29 | var data = new byte[16]; 30 | data[3] = 123; 31 | 32 | LingoImage.PixelOpsL8.Write8( 33 | MemoryMarshal.Cast(data), 34 | 1, 35 | Vector256.AllBitsSet, 36 | Vector256.AllBitsSet.WithElement(3, 0).WithElement(2, 0)); 37 | } 38 | 39 | [Test] 40 | public void TestB8G8R8A8Read8() 41 | { 42 | var data = new byte[32]; 43 | data[3] = 123; 44 | 45 | var result = LingoImage.PixelOpsBgra32.Read8( 46 | MemoryMarshal.Cast(data), 47 | 0, 48 | Vector256.AllBitsSet); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/LingoGlobal.Movie.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Dynamic; 3 | 4 | namespace Drizzle.Lingo.Runtime; 5 | 6 | public sealed partial class LingoGlobal 7 | { 8 | public Movie _movie { get; private set; } = default!; 9 | 10 | public void go(LingoNumber frame) => _movie.go(frame); 11 | public LingoNumber the_frame => _movie.frame; 12 | 13 | public sealed class Movie 14 | { 15 | private readonly LingoGlobal _global; 16 | 17 | public Window window { get; } 18 | 19 | public Movie(LingoGlobal global) 20 | { 21 | _global = global; 22 | window = new Window(global); 23 | } 24 | 25 | public LingoNumber frame => 0; 26 | 27 | public string path => _global.the_moviePath; 28 | 29 | public dynamic stage => throw new NotImplementedException(); 30 | 31 | public void go(LingoNumber newFrame) 32 | { 33 | // score not implemented. 34 | } 35 | } 36 | 37 | public sealed class Window 38 | { 39 | public Window(LingoGlobal lingoGlobal) 40 | { 41 | 42 | } 43 | 44 | // Literally unused except for one set, just return it. 45 | public dynamic appearanceoptions => new ExpandoObject(); 46 | public LingoNumber resizable { get; set; } 47 | public LingoRect rect { get; set; } 48 | public LingoSymbol sizestate => new ("normal"); 49 | } 50 | } -------------------------------------------------------------------------------- /LingoSource/massRenderStart.lingo: -------------------------------------------------------------------------------- 1 | global projects, ldPrps, gLOADPATH, massRenderSelectL 2 | 3 | on exitFrame me 4 | pth = the moviePath & the dirSeparator & "LevelEditorProjects" 5 | repeat with f in gLOADPATH then 6 | pth = pth & the dirSeparator & f 7 | end repeat 8 | 9 | projects = [] 10 | fileList = [ ] 11 | repeat with i = 1 to 300 then 12 | n = getNthFileNameInFolder(pth, i) 13 | if n = EMPTY then exit repeat 14 | if (char n.length-3 of n <> ".")then 15 | projects.add("#" & n) 16 | else 17 | fileList.append(n) 18 | end if 19 | end repeat 20 | 21 | 22 | 23 | 24 | repeat with l in fileList then 25 | if chars(l, l.length-3, l.length) = ".txt" then 26 | projects.add( chars(l, 1, l.length-4)) 27 | end if 28 | end repeat 29 | 30 | txt = "Use the arrow keys to select a project. Use enter to open it." 31 | put RETURN after txt 32 | repeat with f in gLOADPATH then 33 | put f & "/" after txt 34 | end repeat 35 | put RETURN after txt 36 | put RETURN after txt 37 | repeat with q in projects then 38 | put q after txt 39 | put RETURN after txt 40 | end repeat 41 | 42 | ldPrps = [#lstUp:1, lstDwn:1, #lft:1, #rgth:1, #lstEnter:0, #currProject:1, #listScrollPos:1, #listShowTotal:30] 43 | 44 | member("ProjectsL").text = txt 45 | 46 | member("PalName").text = "Press 'A' to select all in folder. Press 'C' to deselect all. Press ENTER to start rendering." 47 | end -------------------------------------------------------------------------------- /LingoSource/finished.lingo: -------------------------------------------------------------------------------- 1 | global levelName, gSkyColor, gLoadedName, gFullRender, gViewRender, gDecalColors 2 | 3 | 4 | on exitFrame me 5 | if (_key.keyPressed(48) and _movie.window.sizeState <> #minimized)and(gViewRender) then 6 | _movie.go(9) 7 | end if 8 | 9 | if gViewRender = 0 then 10 | levelName = gLoadedName 11 | end if 12 | 13 | repeat with q = 0 to gDecalColors.count-1 then 14 | member("finalImage").image.setPixel(q, 0, gDecalColors[q+1]) 15 | end repeat 16 | -- sprite(1).color = gSkyColor 17 | -- put member("levelName").text 18 | -- if (_key.keyPressed(36))or(gViewRender=0) then 19 | -- put levelName 20 | -- if gFullRender then 21 | -- member("finalfg").image.setPixel(0, 0, member("newPalette").image.getPixel(8,0))--skyColor 22 | -- member("finalfg").image.setPixel(1, 0, member("newPalette").image.getPixel(8,1))--fogColor 23 | -- member("finalfg").image.setPixel(2, 0, member("newPalette").image.getPixel(8,2))--blackColor 24 | -- member("finalfg").image.setPixel(3, 0, member("newPalette").image.getPixel(8,3))--itemcolorColor 25 | -- member("finalfg").image.setPixel(2, 1, member("newPalette").image.getPixel(8,4))--menuColor 26 | -- 27 | -- member("finalfg").image.setPixel(0, 1, member("effectColor1").image.getPixel(4,0)) 28 | -- member("finalfg").image.setPixel(1, 1, member("effectColor2").image.getPixel(4,0)) 29 | -- end if 30 | -- else 31 | -- go the frame 32 | --end if 33 | end 34 | 35 | -------------------------------------------------------------------------------- /Drizzle.Benchmarks/ImageQuadCopy.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | using Drizzle.Lingo.Runtime; 3 | 4 | namespace Drizzle.Benchmarks; 5 | 6 | [SimpleJob] 7 | public class ImageQuadCopy 8 | { 9 | private readonly LingoImage _srcImage = new(2000, 1200, 32); 10 | private readonly LingoImage _dstImage = new(2000, 1200, 32); 11 | private readonly LingoList _quad = new LingoList 12 | { 13 | new LingoPoint(-64.7406, -62.5193), 14 | new LingoPoint(1911.9667, -18.7121), 15 | new LingoPoint(2087.3266, 1221.7730), 16 | new LingoPoint(-60.2218, 1266.8830) 17 | }; 18 | 19 | [GlobalSetup] 20 | public void Setup() 21 | { 22 | const int checkerSize = 20; 23 | var pxl = MakePxl(true); 24 | for (var y = 0; y < 1200/checkerSize; y++) 25 | for (var x = 0; x < 2000/checkerSize; x++) 26 | { 27 | if (x % 2 == 0 ^ y % 2 == 0) 28 | _srcImage.copypixels(pxl, new LingoRect(x * checkerSize, y * checkerSize, (x + 1) * checkerSize, (y + 1) * checkerSize), pxl.rect); 29 | } 30 | } 31 | 32 | [Benchmark] 33 | public void Bench() 34 | { 35 | _dstImage.copypixels(_srcImage, _quad, _srcImage.rect); 36 | } 37 | 38 | private static LingoImage MakePxl(bool markAsSuch = false) 39 | { 40 | var img = new LingoImage(1, 1, 32); 41 | img.setpixel(0, 0, LingoColor.Black); 42 | img.IsPxl = markAsSuch; 43 | return img; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/LingoRuntime.FileSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | 5 | namespace Drizzle.Lingo.Runtime; 6 | 7 | public partial class LingoRuntime 8 | { 9 | public static readonly string MovieBasePath; 10 | 11 | static LingoRuntime() 12 | { 13 | #if FULL_RELEASE 14 | var path = Path.Combine(Assembly.GetEntryAssembly()!.Location, "..", "Data"); 15 | #else 16 | var path = Path.Combine(Assembly.GetEntryAssembly()!.Location, "..", "..", "..", "..", "..", "Data"); 17 | #endif 18 | MovieBasePath = Path.GetFullPath(path) + Path.DirectorySeparatorChar; 19 | CastPath = Path.Combine(MovieBasePath, "Cast"); 20 | } 21 | 22 | public string GetFilePath(string relPath) 23 | { 24 | var fullPath = Path.Combine(MovieBasePath, relPath); 25 | if (File.Exists(fullPath)) 26 | return fullPath; 27 | 28 | // Try case sensitive compare. 29 | var dir = Path.GetDirectoryName(fullPath); 30 | if (dir == null || !Directory.Exists(dir)) 31 | return fullPath; 32 | 33 | var origFileName = Path.GetFileName(fullPath.AsSpan()); 34 | foreach (var dirFile in Directory.EnumerateFiles(dir)) 35 | { 36 | var dirFileName = Path.GetFileName(dirFile.AsSpan()); 37 | if (origFileName.Equals(dirFileName, StringComparison.InvariantCultureIgnoreCase)) 38 | return dirFile; 39 | } 40 | 41 | return fullPath; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Drizzle.Editor/Drizzle.Editor.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Exe 4 | enable 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Cast/CastMember.Bitmap.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Lingo.Runtime.Cast; 2 | 3 | public partial class CastMember 4 | { 5 | private LingoImage? _image; 6 | 7 | public LingoImage? image 8 | { 9 | get 10 | { 11 | AssertType(CastMemberType.Bitmap); 12 | return _image; 13 | } 14 | set 15 | { 16 | AssertType(CastMemberType.Bitmap); 17 | _image = value; 18 | } 19 | } 20 | 21 | public LingoRect rect 22 | { 23 | get 24 | { 25 | AssertType(CastMemberType.Bitmap); 26 | return _image!.rect; 27 | } 28 | } 29 | 30 | public LingoNumber width 31 | { 32 | get 33 | { 34 | AssertType(CastMemberType.Bitmap); 35 | return image!.width; 36 | } 37 | } 38 | 39 | public LingoNumber height 40 | { 41 | get 42 | { 43 | AssertType(CastMemberType.Bitmap); 44 | return image!.height; 45 | } 46 | } 47 | 48 | public LingoColor getpixel(int x, int y) 49 | { 50 | AssertType(CastMemberType.Bitmap); 51 | return image!.getpixel(x, y); 52 | } 53 | 54 | public LingoColor getpixel(LingoNumber x, LingoNumber y) => getpixel((int)x, (int)y); 55 | 56 | public LingoPoint regpoint { get; set; } 57 | 58 | private void ImportFileImplBitmap(string path) 59 | { 60 | image = LingoImage.LoadFromPath(path).Trimmed(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Tools/logs_to_manifest.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import json 4 | from collections import defaultdict 5 | from typing import Dict, DefaultDict 6 | 7 | LOG_FILE_NAME_RE = re.compile(r"\d+_build \((.+?)\).txt") 8 | LOG_CHECKSUM_RE = re.compile(r"checksum\s+(.+?)\s+cam\s+(\d+):\s+([A-F0-9]+)", re.IGNORECASE) 9 | 10 | checksums_parsed: Dict[str, DefaultDict[str, Dict[str, str]]] = {} 11 | 12 | for f in os.listdir("Logs"): 13 | if not (m := LOG_FILE_NAME_RE.match(f)): 14 | continue 15 | 16 | this_file: DefaultDict[str, Dict[str, str]] = defaultdict(dict) 17 | checksums_parsed[m.group(1)] = this_file 18 | 19 | print(f"Opening file {f}") 20 | with open(os.path.join("Logs", f), "r") as log: 21 | while line := log.readline(): 22 | if not (m := LOG_CHECKSUM_RE.search(line)): 23 | continue 24 | 25 | room_name = m.group(1) 26 | room_cam = m.group(2) 27 | room_checksum = m.group(3) 28 | 29 | print(f" {room_name}#{room_cam}:\t{room_checksum}") 30 | this_file[room_name][room_cam] = room_checksum 31 | 32 | dataDir = os.path.join("..", "Data", "LevelEditorProjects") 33 | for name, data in checksums_parsed.items(): 34 | if name.startswith("World"): 35 | name = os.path.join("World", name[5:]) 36 | 37 | checksums_manifest = os.path.join(dataDir, name, "checksums.json") 38 | 39 | with open(checksums_manifest, "w") as manifest: 40 | json.dump(data, manifest, sort_keys=True, indent=4) 41 | 42 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Tests/LingoOperatorTest.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Drizzle.Lingo.Runtime; 3 | using NUnit.Framework; 4 | 5 | namespace Drizzle.Lingo.Tests; 6 | 7 | [TestFixture] 8 | public sealed class LingoOperatorTest 9 | { 10 | [Test] 11 | [TestCase("5", "7", ExpectedResult = "12")] 12 | [TestCase("point(100, 10)", "point(200, 20)", ExpectedResult = "point(300, 30)")] 13 | [TestCase("point(100, 10)", "5", ExpectedResult = "point(105, 15)")] 14 | [TestCase("5", "point(100, 10)", ExpectedResult = "point(105, 15)")] 15 | [TestCase("5", "rect(10, 100, 1000, 1)", ExpectedResult = "rect(15, 105, 1005, 6)")] 16 | [TestCase("rect(7, 77, 777, 7777)", "rect(10, 100, 1000, 1)", ExpectedResult = "rect(17, 177, 1777, 7778)")] 17 | [TestCase("point(10, 20)", "rect(5, 7, 10, 10)", ExpectedResult = "[15, 27]")] 18 | [TestCase("[1, 2]", "point(100, 200)", ExpectedResult = "[101, 202]")] 19 | [TestCase("[1, [2, 3]]", "100", ExpectedResult = "[101, [102, 103]]")] 20 | [TestCase("[1, [2, 3]]", "[100, 200]", ExpectedResult = "[101, [202, 203]]")] 21 | [TestCase("[1, [2, 3]]", "[100, [200, 300]]", ExpectedResult = "[101, [202, 303]]")] 22 | [TestCase("[1, 2, 3]", "rect(100, 200, 300, 400)", ExpectedResult = "[101, 202, 303]")] 23 | public string TestAdd(string a, string b) 24 | { 25 | var global = new LingoRuntime(Assembly.GetExecutingAssembly()).Global; 26 | 27 | var aVal = global.value(a); 28 | var bVal = global.value(b); 29 | 30 | return global.@string(LingoGlobal.op_add(aVal, bVal).ToString()); 31 | } 32 | } -------------------------------------------------------------------------------- /Drizzle.Lingo.Tests/RngTest.cs: -------------------------------------------------------------------------------- 1 | using Drizzle.Lingo.Runtime; 2 | using NUnit.Framework; 3 | 4 | namespace Drizzle.Lingo.Tests; 5 | 6 | [TestFixture] 7 | public class RngTest 8 | { 9 | [Test] 10 | public void Test() 11 | { 12 | LingoRuntime.RngState state = default; 13 | LingoRuntime.InitRng(ref state); 14 | state.Seed = 5; 15 | Assert.That(LingoRuntime.Random(ref state, 5), Is.EqualTo(3)); 16 | Assert.That(LingoRuntime.Random(ref state, 5), Is.EqualTo(1)); 17 | Assert.That(LingoRuntime.Random(ref state, 5), Is.EqualTo(1)); 18 | Assert.That(LingoRuntime.Random(ref state, 5), Is.EqualTo(1)); 19 | Assert.That(LingoRuntime.Random(ref state, 5), Is.EqualTo(3)); 20 | Assert.That(LingoRuntime.Random(ref state, 5), Is.EqualTo(2)); 21 | Assert.That(LingoRuntime.Random(ref state, 5), Is.EqualTo(5)); 22 | } 23 | 24 | [Test] 25 | public void TestNoClamp() 26 | { 27 | LingoRuntime.RngState state = default; 28 | LingoRuntime.InitRng(ref state); 29 | state.Seed = 5; 30 | Assert.That(LingoRuntime.Random(ref state, 5), Is.EqualTo(3)); 31 | Assert.That(LingoRuntime.Random(ref state, 5), Is.EqualTo(1)); 32 | Assert.That(LingoRuntime.Random(ref state, 5), Is.EqualTo(1)); 33 | Assert.That(LingoRuntime.Random(ref state, 5), Is.EqualTo(1)); 34 | Assert.That(LingoRuntime.Random(ref state, 5), Is.EqualTo(3)); 35 | Assert.That(LingoRuntime.Random(ref state, 5), Is.EqualTo(2)); 36 | Assert.That(LingoRuntime.Random(ref state, 5), Is.EqualTo(5)); 37 | } 38 | } -------------------------------------------------------------------------------- /Drizzle.Editor/Views/AboutWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | using System.Runtime.Intrinsics.X86; 4 | using Avalonia; 5 | using Avalonia.Controls; 6 | using Avalonia.Markup.Xaml; 7 | 8 | namespace Drizzle.Editor.Views; 9 | 10 | public sealed partial class AboutWindow : Window 11 | { 12 | public AboutWindow() 13 | { 14 | AvaloniaXamlLoader.Load(this); 15 | 16 | #if DEBUG 17 | this.AttachDevTools(); 18 | #endif 19 | 20 | var i = 0; 21 | var grid = this.FindControl("InfoGrid"); 22 | AddLine("Version:", Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? ""); 23 | AddLine("Runtime:", RuntimeInformation.FrameworkDescription); 24 | AddLine("Platform:", RuntimeInformation.RuntimeIdentifier); 25 | AddLine("OS:", RuntimeInformation.OSDescription); 26 | AddLine("CPU features:", Avx2.IsSupported ? "AVX2" : "None"); 27 | 28 | for (var j = 0; j < i; j++) 29 | { 30 | grid.RowDefinitions.Add(new RowDefinition(GridLength.Auto)); 31 | } 32 | 33 | void AddLine(string desc, string value) 34 | { 35 | var txtDesc = new TextBlock { Text = desc }; 36 | Grid.SetColumn(txtDesc, 0); 37 | Grid.SetRow(txtDesc, i); 38 | 39 | var txtValue = new TextBlock { Text = value }; 40 | Grid.SetColumn(txtValue, 1); 41 | Grid.SetRow(txtValue, i); 42 | 43 | grid.Children.Add(txtDesc); 44 | grid.Children.Add(txtValue); 45 | 46 | i += 1; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Drizzle.Editor/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia; 3 | using Avalonia.Controls; 4 | using Avalonia.Controls.ApplicationLifetimes; 5 | using Avalonia.Logging; 6 | using Avalonia.Markup.Xaml; 7 | using Drizzle.Editor.ViewModels; 8 | using Drizzle.Editor.Views; 9 | using Serilog; 10 | using Serilog.Sinks.SystemConsole.Themes; 11 | using LogEventLevel = Serilog.Events.LogEventLevel; 12 | 13 | namespace Drizzle.Editor; 14 | 15 | public class App : Application 16 | { 17 | public override void Initialize() 18 | { 19 | AvaloniaXamlLoader.Load(this); 20 | } 21 | 22 | public override void OnFrameworkInitializationCompleted() 23 | { 24 | #if DEBUG 25 | Logger.Sink = new AvaloniaSeriLogger(new LoggerConfiguration() 26 | .MinimumLevel.Is(LogEventLevel.Warning) 27 | .Enrich.FromLogContext() 28 | .WriteTo.Console( 29 | outputTemplate: "[{Area}] {Message} ({SourceType} #{SourceHash})\n", 30 | theme: AnsiConsoleTheme.Literate) 31 | .CreateLogger()); 32 | #endif 33 | 34 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 35 | { 36 | if (!CommandLineArgs.TryParse(desktop.Args, out var parsed)) 37 | Environment.Exit(1); 38 | 39 | var viewModel = new MainWindowViewModel(); 40 | desktop.MainWindow = new MainWindow 41 | { 42 | DataContext = viewModel, 43 | }; 44 | desktop.ShutdownMode = ShutdownMode.OnMainWindowClose; 45 | 46 | viewModel.Init(parsed); 47 | } 48 | 49 | base.OnFrameworkInitializationCompleted(); 50 | } 51 | } -------------------------------------------------------------------------------- /LingoSource/renderLightStart2.lingo: -------------------------------------------------------------------------------- 1 | global q, c, tm, dptsL, mvL, gLightEProps 2 | 3 | on exitFrame me 4 | 5 | q = 1 6 | c = 1 7 | 8 | 9 | tm = _system.milliseconds 10 | 11 | repeat with q = 0 to 19 then 12 | sprite(40-q).loc = sprite(40-q).loc + point(-q, -q) 13 | member("layer"&string(q)&"sh").image = image(1040,800,32) 14 | end repeat 15 | 16 | 17 | 18 | member("dpImage").image = image(1040,800,32) 19 | member("dpImage").image.copyPixels(member("pxl").image, rect(0,0,1040,800), rect(0,0,1,1), {#color:255}) 20 | 21 | smpl = image(4,1,32) 22 | smplPs = 0 23 | dptsL = [] 24 | 25 | repeat with q = 1 to 20 then 26 | dp = 20-q-5 27 | 28 | 29 | 30 | pstRct = rect(depthPnt(point(0,0),dp),depthPnt(point(1040,800),dp)) 31 | member("dpImage").image.copyPixels(member("layer"&string(20-q)).image, pstRct, rect(0,0,1040,800), {#ink:36, #color:color(255,255,255)}) 32 | smpl.copyPixels(member("pxl").image, rect(smplPs,0,4,1), rect(0,0,1,1), {#color:0}) 33 | 34 | if (dp+5=12)or(dp+5=8)or(dp+5=4)then 35 | 36 | smpl.copyPixels(member("pxl").image, rect(0,0,4,1), rect(0,0,1,1), {#blend:10, #color:255}) 37 | smplPs = smplPs + 1 38 | member("dpImage").image.copyPixels(member("pxl").image, rect(0,0,1040,800), rect(0,0,1,1), {#blend:10, #color:255}) 39 | end if 40 | 41 | end repeat 42 | 43 | 44 | repeat with q = 1 to 4 then 45 | dptsL.add(smpl.getPixel(4-q, 0)) 46 | end repeat 47 | --put dptsL 48 | 49 | 50 | ang = gLightEProps.lightAngle 51 | ang = degToVec(ang)*2.8 52 | flatness = 1 53 | 54 | mvL = [[ang.locH, ang.locV,1]] 55 | repeat with q = 1 to gLightEProps.flatness then 56 | mvL.add([ang.locH, ang.locV,0]) 57 | end repeat 58 | end 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Drizzle.Editor/Views/Render/RenderStageEffectsView.axaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /LingoSource/envEditorLoop.lingo: -------------------------------------------------------------------------------- 1 | global gLOprops, gEnvEditorProps 2 | 3 | 4 | on exitFrame me 5 | script("levelOverview").goToEditor() 6 | 7 | if gLOprops.size.locH > gLOprops.size.locV then 8 | fac = 1024.0/gLOprops.size.locH 9 | else 10 | fac = 768.0/gLOprops.size.locV 11 | end if 12 | rct = rect(1024/2, 768/2, 1024/2, 768/2) + rect(-gLOprops.size.locH*0.5*fac, -gLOprops.size.locV*0.5*fac, gLOprops.size.locH*0.5*fac, gLOprops.size.locV*0.5*fac) 13 | 14 | 15 | 16 | repeat with q = 1 to 2 then 17 | sprite(q).rect = rct 18 | end repeat 19 | 20 | 21 | sprite(4).rect = rct 22 | 23 | if gEnvEditorProps.waterLevel >= 0 then 24 | sprite(3).visibility = true 25 | sprite(5).visibility = true 26 | h = rct.bottom - (gEnvEditorProps.waterLevel+gLOprops.extraTiles[4]+0.5)*fac 27 | sprite(3).rect = rect(rct.left-2, h-1, rct.right+2, 768) 28 | sprite(5).rect = rect(rct.left-2, h-1, rct.right+2, 768) 29 | if gEnvEditorProps.waterInFront then 30 | sprite(3).blend = 5 31 | sprite(5).blend = 50 32 | else 33 | sprite(3).blend = 50 34 | sprite(5).blend = 5 35 | end if 36 | else 37 | sprite(3).visibility = false 38 | sprite(5).visibility = false 39 | end if 40 | 41 | if (_key.keyPressed("l") and _movie.window.sizeState <> #minimized)then 42 | waterLevel = gLOprops.size.locv - gLOprops.extraTiles[2] - gLOprops.extraTiles[4] - (_mouse.mouseLoc.locV/fac).integer 43 | gEnvEditorProps.waterLevel = waterLevel 44 | end if 45 | 46 | if(script("envEditorStart").checkKey("w")) then 47 | if gEnvEditorProps.waterLevel = -1 then 48 | gEnvEditorProps.waterLevel = gLOprops.size.locv/2 49 | else 50 | gEnvEditorProps.waterLevel = -1 51 | end if 52 | end if 53 | 54 | if(script("envEditorStart").checkKey("f")) then gEnvEditorProps.waterInFront = 1 - gEnvEditorProps.waterInFront 55 | go the frame 56 | end 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /LingoSource/LOstart.lingo: -------------------------------------------------------------------------------- 1 | global gLOprops, gEditLizard, gLevel, gLeProps, gPrioCam 2 | 3 | on exitFrame me 4 | cols = gLOprops.size.loch 5 | rows = gLOprops.size.locv 6 | -- member("levelEditImage1").image = image(cols*5, rows*5, 1) 7 | -- member("levelEditImage2").image = image(cols*5, rows*5, 1) 8 | -- member("levelEditImage3").image = image(cols*5, rows*5, 1) 9 | 10 | 11 | -- sprite(21).member = member("libPal" & string(gLOprops.pal)) 12 | --sprite(22).member = member("ecol" & string(gLOprops.eCol1)) 13 | --sprite(23).member = member("ecol" & string(gLOprops.eCol2)) 14 | 15 | gEditLizard = ["pink", 0, 0, 0] 16 | script("levelOverview").nextHole() 17 | member("addLizardTime").text = "0" 18 | member("addLizardFlies").text = "0" 19 | sprite(43).color = color(255, 0, 255) 20 | 21 | sprite(2).loc = point(312,312)+point(-1000+1000*gLevel.defaultTerrain, 0) 22 | 23 | sprite(56).visibility = 1 24 | sprite(57).visibility = 1 25 | sprite(58).visibility = 1 26 | sprite(59).visibility = 1 27 | 28 | sprite(67).loch = (gLEVEL.waterDrips*8)+50 29 | sprite(68).loch = (gLEVEL.maxFlies*10)+50 30 | sprite(69).loch = (gLEVEL.flySpawnRate*4)+50 31 | member("lightTypeText").text = gLevel.lightType 32 | sprite(70).loch = (gLOProps.tileseed)+50 33 | 34 | script("levelOverview").updateLizardsList() 35 | 36 | repeat with q = 0 to 29 then 37 | member("layer"&q).image = image(1,1,1) 38 | member("layer"&q&"sh").image = image(1,1,1) 39 | end repeat 40 | 41 | l = ["Dull", "Reflective", "Superflourescent"] 42 | member("color glow effects").text = l[gLOprops.colGlows[1]+1] && return && l[gLOprops.colGlows[2]+1] 43 | 44 | sprite(22).rect = rect(-100, -100, -100, -100) 45 | if(gPrioCam = 0) then 46 | member("PrioCamText").text = "" 47 | else 48 | member("PrioCamText").text = "Will render camera " & gPrioCam & " first" 49 | end if 50 | 51 | the randomSeed = _system.milliseconds 52 | end -------------------------------------------------------------------------------- /Drizzle.Editor/Helpers/LingoImageAvaloniaHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia; 3 | using Avalonia.Media.Imaging; 4 | using Avalonia.Platform; 5 | using Drizzle.Lingo.Runtime; 6 | using SixLabors.ImageSharp.PixelFormats; 7 | 8 | namespace Drizzle.Editor.Helpers; 9 | 10 | public static class LingoImageAvaloniaHelper 11 | { 12 | public static unsafe Bitmap LingoImageToBitmap(LingoImage img, bool thumbnail) 13 | { 14 | var finalImg = img; 15 | 16 | if (thumbnail) 17 | { 18 | var copyImg = new LingoImage(50, 50, 32); 19 | copyImg.copypixels(img, copyImg.rect, img.rect); 20 | finalImg = copyImg; 21 | } 22 | else if (img.Depth != 32) 23 | { 24 | var copyImg = new LingoImage(img.Width, img.Height, 32); 25 | copyImg.copypixels(img, img.rect, img.rect); 26 | finalImg = copyImg; 27 | } 28 | 29 | var bgra = finalImg; 30 | 31 | fixed (byte* data = finalImg.ImageBuffer) 32 | { 33 | return new Bitmap( 34 | PixelFormat.Bgra8888, 35 | AlphaFormat.Unpremul, 36 | (nint)data, 37 | new PixelSize(bgra.Width, bgra.Height), 38 | new Vector(96, 96), 39 | sizeof(Bgra32) * bgra.Width); 40 | } 41 | } 42 | 43 | public static unsafe void CopyToBitmap(LingoImage image, WriteableBitmap bitmap) 44 | { 45 | using var locked = bitmap.Lock(); 46 | if (locked.Format != PixelFormat.Bgra8888 || image.Depth != 32) 47 | throw new InvalidOperationException(); 48 | 49 | if (locked.Size.Width != image.Width || locked.Size.Height != image.Height) 50 | throw new InvalidOperationException(); 51 | 52 | // Wow I can't believe that worked. 53 | var dstSpan = new Span((void*)locked.Address, locked.RowBytes * locked.Size.Height); 54 | image.ImageBufferNoPadding.CopyTo(dstSpan); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/lingo notes.md: -------------------------------------------------------------------------------- 1 | # Notes about Lingo/Director 2 | 3 | This just contains various bits of detail I figured I should write down about lingo/director. 4 | 5 | ## Math 6 | 7 | Lingo has two number types, integers and floats. 8 | * Integers are standard 32 bit signed integers. 9 | * Floats are IEEE 754 double precision. They can contain Infinity, -Infinity and NaN (`INF`, `-INF` and `NAN` is used when printed). 10 | 11 | Doing operations on int generally returns int (operators do including div, `sqrt()` does, `tan()` doesn't which is probably fair). Float returns float. 12 | 13 | `point` and `rect` remember which type their components are constructed out of. So `point(0, 0)` is different from `point(0.0, 0)` in calculations (accessing `locH` will give a float instead of a number, and all the consequences of that...). This is absolutely insane but hey. 14 | 15 | Constructing a rect from two points rounds the points' components though. Yeah. *Sigh*. 16 | 17 | ## Images 18 | 19 | * Images are 0-indexed, so 0,0 is the top left corner. Stuff like `copyPixels()` rectangles all uses sane indexing. 20 | * The bitmap view in Director doesn't put 0,0 at the top left corner, you have to scroll to the top left to view the whole image. 21 | * **image depths:** 22 | * 1/2/4/8 are all indexed color. 23 | * 16 is RGBA5551 or similar. 24 | * 32 is RGBA32 with alpha channel only one bit still. 25 | * out-of-bounds reads with `copyPixels()` will basically be treated as if the out of bounds region is empty. 26 | * Copying to a 1-bit image seems somewhat intelligent about whether to pick white or black based on input color. Primary colors (like pure red) seem to be black, but secondaries like yellow are white. Probably just a specific case of palettization, but still. 27 | * Masked copies position the mask at the top left corner at the source image, and scale the mask 1:1 with the source image. 28 | * If you have a mask of equal size as a source image and sample the right half of that image, you get the right half of the mask -------------------------------------------------------------------------------- /Drizzle.Logic/Vector2i.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Drizzle.Lingo.Runtime; 4 | 5 | namespace Drizzle.Logic; 6 | 7 | public struct Vector2i : IEquatable 8 | { 9 | public int X; 10 | public int Y; 11 | 12 | public Vector2i(int x, int y) 13 | { 14 | X = x; 15 | Y = y; 16 | } 17 | 18 | public Vector2i(int val) 19 | { 20 | X = val; 21 | Y = val; 22 | } 23 | 24 | public static Vector2i operator +(Vector2i a, Vector2i b) => new(a.X + b.X, a.Y + b.Y); 25 | public static Vector2i operator -(Vector2i a, Vector2i b) => new(a.X - b.X, a.Y - b.Y); 26 | public static Vector2i operator *(Vector2i a, Vector2i b) => new(a.X * b.X, a.Y * b.Y); 27 | public static Vector2i operator /(Vector2i a, Vector2i b) => new(a.X / b.X, a.Y / b.Y); 28 | public static implicit operator Vector2i((int x, int y) tuple) => new(tuple.x, tuple.y); 29 | public static implicit operator LingoPoint(Vector2i v) => new(v.X, v.Y); 30 | public static explicit operator Vector2i(LingoPoint p) => new((int) p.loch, (int) p.locv); 31 | 32 | public void Deconstruct(out int x, out int y) 33 | { 34 | x = X; 35 | y = Y; 36 | } 37 | 38 | public bool Equals(Vector2i other) 39 | { 40 | return X == other.X && Y == other.Y; 41 | } 42 | 43 | public override bool Equals(object? obj) 44 | { 45 | return obj is Vector2i other && Equals(other); 46 | } 47 | 48 | // ReSharper in charge of understanding basic concepts of the language with their analysis. 49 | [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] 50 | public override int GetHashCode() 51 | { 52 | return HashCode.Combine(X, Y); 53 | } 54 | 55 | public static bool operator ==(Vector2i left, Vector2i right) 56 | { 57 | return left.Equals(right); 58 | } 59 | 60 | public static bool operator !=(Vector2i left, Vector2i right) 61 | { 62 | return !left.Equals(right); 63 | } 64 | } -------------------------------------------------------------------------------- /.github/workflows/render-all-levels.yml: -------------------------------------------------------------------------------- 1 | name: render all levels 2 | 3 | concurrency: 4 | group: render_all 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | fail-fast: false 16 | matrix: 17 | folder: 18 | - "000tests" 19 | - "00_GATE" 20 | - "demos" 21 | - "EditorSamples" 22 | - "templates" 23 | - "testFolder" 24 | - "World/CC" 25 | - "World/DS" 26 | - "World/DU" 27 | - "World/GW" 28 | - "World/HI" 29 | - "World/LF" 30 | - "World/Multi" 31 | - "World/SB" 32 | - "World/SH" 33 | - "World/SI" 34 | - "World/SL" 35 | - "World/SS" 36 | - "World/SU" 37 | - "World/UW" 38 | 39 | steps: 40 | - uses: actions/checkout@v2 41 | with: 42 | submodules: true 43 | - name: Setup .NET 44 | uses: actions/setup-dotnet@v1 45 | with: 46 | dotnet-version: 7.0.x 47 | - name: Restore dependencies 48 | run: dotnet restore 49 | - name: Run transpiler 50 | run: dotnet run --project Drizzle.Transpiler/Drizzle.Transpiler.csproj -- LingoSource Drizzle.Ported/Translated 51 | - name: Build 52 | run: dotnet build --no-restore -c Release 53 | - name: Render levels 54 | shell: pwsh 55 | run: | 56 | $env:COMPlus_gcServer=1 57 | $blackList = @{ 58 | #"World/Multi" = @{"Sky Island"=1} 59 | } 60 | $folder = "${{ matrix.folder }}" 61 | $path = join-path "Data/LevelEditorProjects" $folder "*.txt" 62 | $checksum = join-path "Data/LevelEditorProjects" $folder "checksums.json" 63 | $files = (get-childitem -Recurse $path) | where-object { $blackList[$folder] -eq $null -or $blackList[$folder][[System.IO.Path]::GetFileNameWithoutExtension($_)] -ne 1 } 64 | dotnet run --no-build -c Release --project Drizzle.ConsoleApp/Drizzle.ConsoleApp.csproj -- render --parallelism 2 --compare-checksums $checksum $files 65 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/LingoRuntime.Random.cs: -------------------------------------------------------------------------------- 1 | namespace Drizzle.Lingo.Runtime; 2 | 3 | public partial class LingoRuntime 4 | { 5 | // Yeah, I reverse engineered Director for this. 6 | // Please don't sue me, Adobe. 7 | 8 | private RngState _rngState; 9 | 10 | public uint RngSeed 11 | { 12 | get => _rngState.Seed; 13 | set 14 | { 15 | InitRng(ref _rngState); 16 | _rngState.Seed = value; 17 | } 18 | } 19 | 20 | public int Random(int clamp) 21 | { 22 | return Random(ref _rngState, clamp); 23 | } 24 | 25 | private static int RandomDerive(uint param) 26 | { 27 | var var1 = (int) ((param << 0xd ^ param) - ((int) param >> 0x15)); 28 | var var2 = (uint)(((var1 * var1 * 0x3d73 + 0xc0ae5) * var1 + 0xd208dd0d & 0x7fffffff) + var1); 29 | return (int) ((var2 * 0x2000 ^ var2) - ((int) var2 >> 0x15)); 30 | } 31 | 32 | internal static void InitRng(ref RngState state) 33 | { 34 | state.Seed = 1; 35 | // A note here: 36 | // This init field is actually initialized based to another parameter to this function in Director's code. 37 | // It's calculated by substringing a region of the string "Macromedia Director", based on said param. 38 | // I simply hardcoded the value calculated for the Lingo RNG here instead. 39 | state.Init = 0xA3000000; 40 | } 41 | 42 | internal static int Random(ref RngState state, int clamp) 43 | { 44 | if (state.Seed == 0) 45 | InitRng(ref state); 46 | 47 | if ((state.Seed & 1) == 0) 48 | state.Seed = (uint)state.Seed >> 1; 49 | else 50 | state.Seed = (uint)state.Seed >> 1 ^ state.Init; 51 | 52 | int var1 = RandomDerive((uint) (state.Seed * 0x47)); 53 | if (clamp > 0) 54 | var1 = (int)((long)(ulong)(var1 & 0x7FFFFFFF) % (long) clamp); 55 | 56 | return var1 + 1; 57 | } 58 | 59 | internal struct RngState 60 | { 61 | public uint Seed; 62 | public uint Init; 63 | } 64 | } -------------------------------------------------------------------------------- /Drizzle.Editor/Views/MainWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reactive.Disposables; 5 | using Avalonia; 6 | using Avalonia.Controls; 7 | using Avalonia.Input; 8 | using Avalonia.Interactivity; 9 | using Avalonia.Markup.Xaml; 10 | using Avalonia.ReactiveUI; 11 | using Drizzle.Editor.ViewModels; 12 | using ReactiveUI; 13 | 14 | namespace Drizzle.Editor.Views; 15 | 16 | public partial class MainWindow : ReactiveWindow 17 | { 18 | public MainWindow() 19 | { 20 | InitializeComponent(); 21 | #if DEBUG 22 | this.AttachDevTools(); 23 | #endif 24 | 25 | this.WhenActivated(disposables => 26 | { 27 | this.WhenAnyValue(x => x.ViewModel!.TabContent!.CountCameras) 28 | .Subscribe(cameras => 29 | { 30 | var menu = this.FindControl("MenuRenderCamera");/*= Enumerable.Range(0, cameras);*/ 31 | menu.Items = Enumerable.Range(0, cameras).Select(c => new MenuItem 32 | { 33 | Header = $"Camera {c+1}", 34 | Command = ReactiveCommand.Create(() => ViewModel!.RenderCamera(c)) 35 | }).ToList(); 36 | }) 37 | .DisposeWith(disposables); 38 | }); 39 | } 40 | 41 | public async void OpenProject() 42 | { 43 | var dialog = new OpenFileDialog 44 | { 45 | AllowMultiple = true, 46 | Filters = new List 47 | { 48 | new() 49 | { 50 | Name = "Level editor projects", 51 | Extensions = { "txt" } 52 | } 53 | }, 54 | }; 55 | 56 | var result = await dialog.ShowAsync(this); 57 | 58 | if (result == null || result.Length == 0) 59 | return; 60 | 61 | ViewModel!.OpenProjects(result); 62 | } 63 | 64 | private void OpenAbout(object? sender, RoutedEventArgs e) 65 | { 66 | new AboutWindow().Show(this); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/Xtra/FileIOXtra.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace Drizzle.Lingo.Runtime.Xtra; 5 | 6 | public sealed class FileIOXtra : BaseXtra 7 | { 8 | private FileStream? _file; 9 | 10 | public void openfile(string filePath) => openfile(filePath, 0); 11 | 12 | public void openfile(string filePath, LingoNumber mode) 13 | { 14 | var access = mode.IntValue switch 15 | { 16 | 0 => FileAccess.ReadWrite, 17 | 1 => FileAccess.Read, 18 | 2 => FileAccess.Write, 19 | _ => throw new ArgumentException("Invalid file mode!") 20 | }; 21 | 22 | _file = File.Open(filePath, FileMode.Open, access); 23 | } 24 | 25 | public void closefile() 26 | { 27 | _file?.Dispose(); 28 | _file = null; 29 | } 30 | 31 | public void delete() 32 | { 33 | if (_file == null) 34 | throw new InvalidOperationException("File not open!"); 35 | 36 | var name = _file.Name; 37 | closefile(); 38 | 39 | File.Delete(name); 40 | } 41 | 42 | public void createfile(string path) 43 | { 44 | File.Create(path).Dispose(); 45 | } 46 | 47 | public void writestring(string str) 48 | { 49 | if (_file == null) 50 | throw new InvalidOperationException("File not open!"); 51 | 52 | using var writer = new StreamWriter(_file, leaveOpen: true); 53 | writer.Write(str); 54 | } 55 | 56 | public void writereturn(LingoSymbol type) 57 | { 58 | if (_file == null) 59 | throw new InvalidOperationException("File not open!"); 60 | 61 | using var writer = new StreamWriter(_file, leaveOpen: true); 62 | writer.Write("\r\n"); 63 | } 64 | 65 | public string readfile() 66 | { 67 | if (_file == null) 68 | throw new InvalidOperationException("File not open!"); 69 | 70 | var reader = new StreamReader(_file, leaveOpen: true); 71 | return reader.ReadToEnd(); 72 | } 73 | 74 | public override BaseXtra Duplicate() 75 | { 76 | return new FileIOXtra(); 77 | } 78 | } -------------------------------------------------------------------------------- /LingoSource/unify.lingo: -------------------------------------------------------------------------------- 1 | global c, gSkyColor, lightRects, gLevel 2 | 3 | on exitFrame me 4 | -- if _key.keyPressed(48) then 5 | -- _movie.go(9) 6 | -- end if 7 | -- if (gLevel.lightType<>"No Light")and(1<>1) then 8 | -- repeat with c = 1 to 800 then 9 | -- repeat with q = 1 to 1040 then 10 | -- cl = member("shadowImage").image.getPixel(q-1, c-1) 11 | -- if cl = color( 255, 255, 255 ) then 12 | -- repeat with dir in [point(-1, 0), point(0,-1), point(1,0), point(0,1)]then--,point(-1, -1), point(1,-1), point(1,1), point(-1,1)]then 13 | -- if member("shadowImage").image.getPixel(q-1 + dir.locH*2, c-1 + dir.locV*2) = cl then 14 | -- -- if member("shadowImg").image.getPixel(q-1 + dir.locH, c-1 + dir.locV) <> color(255, 255, 255) then 15 | -- member("shadowImage").image.setPixel(q-1 + dir.locH, c-1 + dir.locV, cl) 16 | -- -- end if 17 | -- end if 18 | -- end repeat 19 | -- end if 20 | -- 21 | -- end repeat 22 | -- 23 | -- end repeat 24 | -- end if 25 | -- 26 | -- sprite(3).rect = rect(0, c-8, 1040, c+1-8) 27 | -- 28 | -- 29 | -- -- c = c + 1 + (1000*(gLevel.lightType="No Light")) 30 | -- member("rainBowImage").image = image(1040, 800, 32) 31 | -- member("rainBowMask").image = image(1040, 800, 1) 32 | -- member("tempRainBowImage").image = image(1040, 800, 32) 33 | -- 34 | -- member("rainBowImage").image.copyPixels(member("pxl").image, rect(0,0,1040,800),rect(0,0,1,1), {#color:color(255,255,255)}) 35 | -- member("rainBowMask").image.copyPixels(member("pxl").image, rect(0,0,1040,800),rect(0,0,1,1), {#color:color(0,0,0)}) 36 | -- 37 | -- 38 | -- -- if c > 800 then 39 | -- c = 1 40 | -- -- gSkyColor = color(122, 122, 122) 41 | -- sprite(1).color = gSkyColor 42 | -- 43 | -- member("blurredLight").image = image(1040,800,32) 44 | -- -- member("blurredLight").image.copypixels(member("pxl").image, rect(0,0,1040,800), rect(0,0,1,1)) 45 | -- 46 | -- lightRects = [rect(1000, 1000, -1000, -1000), rect(1000, 1000, -1000, -1000)] 47 | -- -- else 48 | -- -- go the frame 49 | -- -- end if 50 | 51 | end 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /.github/workflows/publish-release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*' 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@v2 15 | with: 16 | submodules: 'recursive' 17 | - name: Setup .NET Core 18 | uses: actions/setup-dotnet@v1 19 | with: 20 | dotnet-version: 7.0.* 21 | 22 | - name: Restore dependencies 23 | run: dotnet restore 24 | 25 | - name: Run transpiler 26 | run: dotnet run --project Drizzle.Transpiler/Drizzle.Transpiler.csproj -- LingoSource Drizzle.Ported/Translated 27 | 28 | - name: Create build (Linux) 29 | run: ./Tools/package_release.ps1 -Rid linux-x64 30 | 31 | - name: Create build (Windows) 32 | run: ./Tools/package_release.ps1 -Rid win-x64 33 | 34 | - name: Create Release 35 | id: create_release 36 | uses: actions/create-release@v1 37 | env: 38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 39 | with: 40 | tag_name: ${{ github.ref }} 41 | release_name: Release ${{ github.ref }} 42 | draft: false 43 | prerelease: false 44 | 45 | # Upload Linux 46 | - name: Upload Release Asset 47 | uses: actions/upload-release-asset@v1 48 | env: 49 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 50 | with: 51 | upload_url: ${{ steps.create_release.outputs.upload_url }} 52 | asset_path: ./Release/Drizzle.base.Release.linux-x64.zip 53 | asset_name: Drizzle.base.Release.linux-x64.zip 54 | asset_content_type: application/zip 55 | 56 | # Upload Windows 57 | - name: Upload Release Asset 58 | uses: actions/upload-release-asset@v1 59 | env: 60 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 61 | with: 62 | upload_url: ${{ steps.create_release.outputs.upload_url }} 63 | asset_path: ./Release/Drizzle.base.Release.win-x64.zip 64 | asset_name: Drizzle.base.Release.win-x64.zip 65 | asset_content_type: application/zip 66 | -------------------------------------------------------------------------------- /LingoSource/levelEditor.lingo: -------------------------------------------------------------------------------- 1 | 2 | 3 | global gLEProps, gDirectionKeys, gLOprops, gEnvEditorProps 4 | 5 | 6 | on exitFrame me 7 | 8 | repeat with q = 1 to 4 then 9 | if (_key.keyPressed([86, 91, 88, 84][q]))and(gDirectionKeys[q] = 0) and _movie.window.sizeState <> #minimized then 10 | gLEProps.camPos = gLEProps.camPos + [point(-1, 0), point(0,-1), point(1,0), point(0,1)][q] * (1 + 9 * _key.keyPressed(83) + 34 * _key.keyPressed(85)) 11 | if not _key.keyPressed(92) then 12 | if gLEProps.camPos.loch < -1 then 13 | gLEProps.camPos.loch = -1 14 | end if 15 | if gLEProps.camPos.locv < -1 then 16 | gLEProps.camPos.locv = -1 17 | end if 18 | if gLEProps.camPos.loch > gLEprops.matrix.count-51 then 19 | gLEProps.camPos.loch = gLEprops.matrix.count-51 20 | end if 21 | if gLEProps.camPos.locv > gLEprops.matrix[1].count-39 then 22 | gLEProps.camPos.locv = gLEprops.matrix[1].count-39 23 | end if 24 | end if 25 | lvlEditDraw(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 1) 26 | lvlEditDraw(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 2) 27 | lvlEditDraw(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 3) 28 | drawShortCutsImg(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 16) 29 | end if 30 | gDirectionKeys[q] = _key.keyPressed([86, 91, 88, 84][q]) 31 | end repeat 32 | 33 | call(#newUpdate, gLEProps.levelEditors) 34 | 35 | rct = rect(0,0,gLOprops.size.loch, gLOprops.size.locv) + rect(gLOProps.extraTiles[1], gLOProps.extraTiles[2], -gLOProps.extraTiles[3], -gLOProps.extraTiles[4]) - rect(gLEProps.camPos, gLEProps.camPos) 36 | sprite(71).rect = (rct.intersect(rect(0,0,52,40))+rect(11, 1, 11, 1))*rect(16,16,16,16) 37 | 38 | if gEnvEditorProps.waterLevel = -1 then 39 | sprite(9).rect = rect(0,0,0,0) 40 | else 41 | rct = rect(0, gLOprops.size.locv-gEnvEditorProps.waterLevel-gLOProps.extraTiles[4], gLOprops.size.loch, gLOprops.size.locv) - rect(gLEProps.camPos, gLEProps.camPos) 42 | sprite(9).rect = ((rct.intersect(rect(0,0,52,40))+rect(11, 1, 11, 1))*rect(16,16,16,16))+rect(0, -8, 0, 0) 43 | end if 44 | 45 | script("levelOverview").goToEditor() 46 | go the frame 47 | end 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /Drizzle.Editor/CommandLineArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using Drizzle.Logic; 5 | using Drizzle.Logic.Rendering; 6 | using C = System.Console; 7 | 8 | namespace Drizzle.Editor; 9 | 10 | public sealed record CommandLineArgs(bool Render, string? Project, bool AutoPause, RenderStage? RenderStage) 11 | { 12 | public static bool TryParse(IReadOnlyList args, [NotNullWhen(true)] out CommandLineArgs? parsed) 13 | { 14 | parsed = null; 15 | var render = false; 16 | var autoPause = false; 17 | string? project = null; 18 | RenderStage? renderStage = null; 19 | 20 | using var enumerator = args.GetEnumerator(); 21 | 22 | while (enumerator.MoveNext()) 23 | { 24 | var arg = enumerator.Current; 25 | if (arg == "--render") 26 | { 27 | render = true; 28 | } 29 | else if (arg == "--pause") 30 | { 31 | autoPause = true; 32 | } 33 | else if (arg == "--project") 34 | { 35 | if (!enumerator.MoveNext()) 36 | { 37 | C.WriteLine("Missing project name."); 38 | return false; 39 | } 40 | 41 | project = enumerator.Current; 42 | } 43 | else if (arg == "--render-stage") 44 | { 45 | if (!enumerator.MoveNext()) 46 | { 47 | C.WriteLine("Missing render stage."); 48 | return false; 49 | } 50 | 51 | renderStage = Enum.Parse(enumerator.Current); 52 | } 53 | else if (arg == "--help") 54 | { 55 | PrintHelp(); 56 | return false; 57 | } 58 | } 59 | 60 | parsed = new CommandLineArgs(render, project, autoPause, renderStage); 61 | 62 | return true; 63 | } 64 | 65 | private static void PrintHelp() 66 | { 67 | C.WriteLine(@" 68 | Options: 69 | --project Which project to load. 70 | --render Immediately render the loaded project. 71 | --render-stage Render stage to run to before pausing. 72 | --pause Automatically pause the lingo runtime on load to allow stepping. 73 | "); 74 | } 75 | } -------------------------------------------------------------------------------- /LingoSource/globals.lingo: -------------------------------------------------------------------------------- 1 | -- Used by some render stages to keep track of progress. 2 | type c: number 3 | 4 | -- Appears to just be a loop variable that doesn't need to be type. 5 | type q: number 6 | 7 | -- Used by effects render stage to track which effect is being rendered. 8 | type r: number 9 | 10 | -- Used by effects render stage to track vertical pass position. 11 | type vertRepeater: number 12 | 13 | type gCurrentRenderCamera: number 14 | type gRenderCameraTilePos: point 15 | type gRenderCameraPixelPos: point 16 | 17 | -- Prop rendering stage: before or after effects? 18 | type afterEffects: number 19 | 20 | type closestProp: number 21 | 22 | -- Used by prop rendering to store the current prop color or something. 23 | type colr: color 24 | 25 | -- Something in effect rendering. 26 | type daddyCorruptionHoles: list 27 | 28 | -- Something related to depth rendering 29 | type dptsL: list 30 | type fogDptsL: list 31 | 32 | -- Some effects 33 | type effectIn3D: number 34 | 35 | type effectSeed: number 36 | 37 | type gAnyDecals: number 38 | 39 | type gCurrentlyRenderingTrash: number 40 | 41 | type gDecalColors: list 42 | 43 | type gdLayer: string 44 | 45 | type gEffects: list 46 | 47 | -- Are we rendering a soft prop? 48 | type gESoftProp: number 49 | 50 | type gLastImported: string 51 | 52 | type gLastImportedImage: image 53 | 54 | type glgtimgQuad: list 55 | 56 | type gProps: list 57 | 58 | type gRenderTrashProps: list 59 | 60 | type gTiles: list 61 | 62 | type gTinySignsDrawn: number 63 | 64 | type gTrashPropOptions: list 65 | 66 | type gViewRender: number 67 | 68 | type keepLooping: number 69 | 70 | type pos: point 71 | 72 | type propsToRender: list 73 | 74 | type slagHoles: list 75 | 76 | type solidMtrx: list 77 | 78 | type tileSetIndex: list 79 | 80 | type tm: number 81 | 82 | type wireBunchSav: list 83 | 84 | type gSaveProps: list -------------------------------------------------------------------------------- /Tools/package_release.ps1: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env pwsh 2 | 3 | param( 4 | [parameter(Mandatory=$true)] 5 | [string]$Rid, 6 | 7 | [Parameter()] 8 | [string]$Configuration="Release"); 9 | 10 | $CURRENT_TFM="net7.0" 11 | 12 | $baseDir = Join-Path -Resolve $PSScriptRoot .. 13 | 14 | Push-Location $baseDir 15 | 16 | $releaseBuildPath = Join-Path $basedir "ReleaseBuild" "$Configuration.$Rid" 17 | $releaseDir = Join-Path $basedir "Release" 18 | $publishDirEditor = Join-Path $baseDir "Drizzle.Editor" "bin" $Configuration $CURRENT_TFM $Rid "publish" 19 | $publishDirConsole = Join-Path $baseDir "Drizzle.ConsoleApp" "bin" $Configuration $CURRENT_TFM $Rid "publish" 20 | $baseDataDir = Join-Path $baseDir "Data" 21 | $releaseDataDir = Join-Path $releaseBuildPath "Data" 22 | 23 | # Create directory if not exist. 24 | New-Item -Force $(Join-Path $basedir "Release") -ItemType Directory | Out-Null 25 | New-Item -Force $releaseBuildPath -ItemType Directory | Out-Null 26 | 27 | # Clear directories of previous stuff 28 | Remove-Item -Recurse $(Join-Path $publishDirEditor "*") 29 | Remove-Item -Recurse $(Join-Path $publishDirConsole "*") 30 | Remove-Item -Recurse $(Join-Path $releaseBuildPath "*") 31 | 32 | Write-Host "Starting build $Configuration" 33 | 34 | # Build projects. 35 | dotnet publish $(Join-Path "Drizzle.Editor" "Drizzle.Editor.csproj") -c $Configuration -r $Rid ` 36 | --self-contained /p:FullRelease=true /p:BuiltInComInteropSupport=true /p:NoWarn=IL2026 37 | dotnet publish $(Join-Path "Drizzle.ConsoleApp" "Drizzle.ConsoleApp.csproj") -c $Configuration -r $Rid ` 38 | --self-contained /p:FullRelease=true /p:NoWarn=IL2026 39 | 40 | # Copy to intermediate build directory. 41 | Copy-Item $(Join-Path $publishDirConsole "*") $releaseBuildPath 42 | Copy-Item $(Join-Path $publishDirEditor "*") $releaseBuildPath 43 | 44 | # Create data directory folders/folders 45 | New-Item -ItemType Directory $(Join-Path $releaseBuildPath "Data") | Out-Null 46 | New-Item -ItemType Directory $(Join-Path $releaseBuildPath "Data" "Levels") | Out-Null 47 | New-Item -ItemType File $(Join-Path $releaseBuildPath "Data" "rendered_levels_go_here.txt") | Out-Null 48 | 49 | # Copy data files 50 | Copy-Item -Recurse $(Join-Path $baseDataDir "Graphics") $releaseDataDir 51 | Copy-Item -Recurse $(Join-Path $baseDataDir "Props") $releaseDataDir 52 | Copy-Item -Recurse $(Join-Path $baseDataDir "Cast") $releaseDataDir 53 | 54 | # Compress final zip 55 | Compress-Archive $(Join-Path $releaseBuildPath "*") -DestinationPath $(Join-Path $releaseDir "Drizzle.base.$Configuration.$Rid.zip") -CompressionLevel Optimal -Force 56 | 57 | Pop-Location -------------------------------------------------------------------------------- /LingoSource/tileEditorStart.lingo: -------------------------------------------------------------------------------- 1 | global gTEprops, gTiles, gLEprops, gLOprops, gDirectionKeys 2 | 3 | on exitFrame me 4 | -- repeat with q = 1 to 3 then 5 | -- member("TEimg"&string(q)).image = image(16*gLOprops.size.loch, 16*gLOprops.size.locV, 16) 6 | -- end repeat 7 | 8 | member("tileMenu").alignment = #left 9 | 10 | member("TEimg1").image = image(52*16, 40*16, 16) 11 | member("TEimg2").image = image(52*16, 40*16, 16) 12 | member("TEimg3").image = image(52*16, 40*16, 16) 13 | member("levelEditImage1").image = image(52*16, 40*16, 16) 14 | member("levelEditImage2").image = image(52*16, 40*16, 16) 15 | member("levelEditImage3").image = image(52*16, 40*16, 16) 16 | member("levelEditImageShortCuts").image = image(52*16, 40*16, 16) 17 | 18 | TEdraw(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 1) 19 | TEdraw(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 2) 20 | TEdraw(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 3) 21 | lvlEditDraw(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 1) 22 | lvlEditDraw(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 2) 23 | lvlEditDraw(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 3) 24 | drawShortCutsImg(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 16) 25 | 26 | gDirectionKeys = [0,0,0,0] 27 | 28 | sprite(1).blend = 10 29 | sprite(2).blend = 10 30 | sprite(3).blend = 60 31 | sprite(4).blend = 10 32 | sprite(5).blend = 70 33 | sprite(6).blend = 100 34 | 35 | --gTEprops.workLayer = 1 36 | 37 | sprite(8).blend = 80 38 | sprite(8).visibility = 0 39 | -- sprite(8).loc = point(432, 336) --+ point(-5, 5)--*(gTEprops.workLayer = 2) 40 | -- pos = 2 - gTEprops.workLayer 41 | -- sprite(1).loc = point(432, 336) + point(pos+1,-pos-1)*3 42 | -- sprite(2).loc = point(432, 336) + point(pos+1,-pos-1)*3 43 | -- sprite(3).loc = point(432, 336) + point(pos,-pos)*3 44 | -- sprite(4).loc = point(432, 336) + point(pos,-pos)*3 45 | -- sprite(5).loc = point(432, 336) + point(pos-1,-pos+1)*3 46 | -- sprite(6).loc = point(432, 336) + point(pos-1,-pos+1)*3 47 | script("tileEditor").changeLayer() 48 | 49 | member("default material").text = "Default material:" && gTEprops.defaultMaterial && "(Press 'E' to change)" 50 | sprite(19).visibility = 1 51 | l = [#L:0, #m1:0, m2:0, #w:0, #a:0, #s:0, #d:0, #c:0, #q:0] 52 | gTEprops.lastKeys = l.duplicate() 53 | gTEprops.keys = l.duplicate() 54 | 55 | script("tileEditor").updateTileMenu(point(0,0)) 56 | --call(#updateTileMenu, point(0,0)) 57 | 58 | gTEprops.tmSavPosL = [] 59 | repeat with q = 1 to gTiles.count then 60 | gTEprops.tmSavPosL.add(1) 61 | end repeat 62 | end 63 | 64 | 65 | -------------------------------------------------------------------------------- /LingoSource/saveProject.lingo: -------------------------------------------------------------------------------- 1 | global levelName, gTEprops, gTiles, gLEProps, gEEprops, gLightEProps 2 | global gLEVEL, gLOprops, gLoadedName, gCameraProps, gEnvEditorProps, gPEprops, gLOADPATH 3 | 4 | 5 | 6 | on exitFrame me 7 | if _key.keyPressed(36) and _movie.window.sizeState <> #minimized then 8 | 9 | -- l = [#mtrx:gLEProps.matrix, #gTEProps:gTEprops, #gEEprops:gEEprops, #gLightEProps:gLightEProps, #gLEVEL:gLEVEL, #gLOprops:gLOprops] 10 | str = "" 11 | put gLEProps.matrix after str 12 | put RETURN after str 13 | put gTEProps after str 14 | put RETURN after str 15 | put gEEprops after str 16 | put RETURN after str 17 | put gLightEProps after str 18 | put RETURN after str 19 | put gLEVEL after str 20 | put RETURN after str 21 | put gLOprops after str 22 | put RETURN after str 23 | put gCameraProps after str 24 | put RETURN after str 25 | put gEnvEditorProps after str 26 | put RETURN after str 27 | put gPEprops after str 28 | put RETURN after str 29 | 30 | objFileio = new xtra("fileio") 31 | pth = the moviePath & "LevelEditorProjects" & the dirSeparator 32 | repeat with f in gLOADPATH then 33 | pth = pth & f & the dirSeparator 34 | end repeat 35 | createFile (objFileio, pth&levelName&".txt") 36 | objFileio.openFile(pth&levelName&".txt", 0) 37 | objFileio.writeString(str) 38 | objFileio.closeFile() 39 | 40 | -- member("lightImageExport").image = image(1040+200, 800+200, 32) 41 | -- member("lightImageExport").image.copyPixels(member("lightImage").image, rect(0, 0, 1040+200, 800+200), rect(0, 0, 1040+200, 800+200)) 42 | member("lightImage").image.setPixel(0,0,color(0,0,0)) 43 | member("lightImage").image.setPixel(member("lightImage").rect.width-1, member("lightImage").rect.height-1, color(0,0,0)) 44 | -- exportAnImage( member("lightImageExport").image, "\LevelEditorProjects\" & levelName) 45 | 46 | gImgXtra = xtra("ImgXtra").new() 47 | nwImg = image(member("lightImage").image.rect.width, member("lightImage").image.rect.height, 32) 48 | nwImg.copypixels(member("lightImage").image, rect(0,0,member("lightImage").image.rect.width, member("lightImage").image.rect.height), rect(0,0,member("lightImage").image.rect.width, member("lightImage").image.rect.height)) 49 | props = ["image": nwImg, "filename":pth & levelName & ".png"] 50 | ok = gImgXtra.ix_saveImage(props) 51 | 52 | -- member("lightImageExport").image = image(1, 1, 1) 53 | 54 | gLoadedName = levelName 55 | member("Level Name").text = gLoadedName 56 | 57 | _movie.go(7) 58 | else 59 | go the frame 60 | end if 61 | end -------------------------------------------------------------------------------- /Drizzle.Lingo.Runtime/LingoCastLib.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Drizzle.Lingo.Runtime.Cast; 4 | 5 | namespace Drizzle.Lingo.Runtime; 6 | 7 | public sealed class LingoCastLib 8 | { 9 | private readonly CastMember[] _cast; 10 | private readonly Dictionary _names = new(StringComparer.InvariantCultureIgnoreCase); 11 | 12 | private readonly LingoRuntime _runtime; 13 | public int Offset; 14 | 15 | public string name { get; } 16 | private bool _nameIndexDirty; 17 | 18 | public dynamic member => throw new NotImplementedException(); 19 | 20 | public int NumMembers => _cast.Length; 21 | 22 | public LingoCastLib(LingoRuntime runtime, string name, int offset) 23 | { 24 | this.name = name; 25 | _runtime = runtime; 26 | Offset = offset; 27 | 28 | _cast = new CastMember[1000]; 29 | for (var i = 0; i < _cast.Length; i++) 30 | { 31 | _cast[i] = new CastMember(runtime, this, i + 1 + Offset, name); 32 | } 33 | } 34 | 35 | public CastMember? GetMember(object nameOrNum) 36 | { 37 | if (nameOrNum is string name) 38 | { 39 | if (_nameIndexDirty) 40 | UpdateNameIndex(); 41 | 42 | if (_names.TryGetValue(name, out var castMember)) 43 | { 44 | return _cast[castMember]; 45 | } 46 | } 47 | 48 | if (nameOrNum is int num) 49 | { 50 | var numMember = GetMember(num); 51 | if (numMember != null) 52 | return numMember; 53 | } 54 | 55 | if (nameOrNum is LingoNumber lingoNum) 56 | { 57 | var numMember = GetMember(lingoNum.IntValue); 58 | if (numMember != null) 59 | return numMember; 60 | } 61 | 62 | return null; 63 | } 64 | 65 | public CastMember? GetMember(int num) 66 | { 67 | if (num > 0 && num <= _cast.Length) 68 | return _cast[num - 1]; 69 | 70 | var idx = num - Offset; 71 | if (idx > 0 && idx < _cast.Length) 72 | return _cast[idx - 1]; 73 | 74 | return null; 75 | } 76 | 77 | public void NameIndexDirty() 78 | { 79 | _nameIndexDirty = true; 80 | _runtime.NameIndexDirty(); 81 | } 82 | 83 | private void UpdateNameIndex() 84 | { 85 | _names.Clear(); 86 | 87 | for (var i = 0; i < _cast.Length; i++) 88 | { 89 | var castMember = _cast[i]; 90 | 91 | if (castMember.name != null) 92 | _names.TryAdd(castMember.name, i); 93 | } 94 | 95 | _nameIndexDirty = false; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /LingoSource/effectsEditorStart.lingo: -------------------------------------------------------------------------------- 1 | global gTEprops, gTiles, gEEprops, gLEprops, gLOprops, gEnvEditorProps 2 | 3 | on exitFrame me 4 | 5 | sprite(15).color = color(255, 255, 255) 6 | 7 | member("effectsMatrix").image = image(52,40,32) 8 | 9 | sprite(3).blend = 30 10 | sprite(4).blend = 50 11 | sprite(5).blend = 70 12 | sprite(6).blend = 100 13 | 14 | repeat with spr = 3 to 9 then 15 | sprite(spr).rect = rect(16,16,53*16, 41*16) 16 | end repeat 17 | 18 | 19 | 20 | repeat with q = 1 to gLOprops.size.loch then 21 | repeat with c = 1 to gLOprops.size.locv then 22 | member("effectsMatrix").image.setPixel(q-1, c-1, color(0, 0, 0)) 23 | end repeat 24 | end repeat 25 | 26 | member("TEimg1").image = image(52*16, 40*16, 16) 27 | member("TEimg2").image = image(52*16, 40*16, 16) 28 | member("TEimg3").image = image(52*16, 40*16, 16) 29 | member("levelEditImage1").image = image(52*16, 40*16, 16) 30 | member("levelEditImage2").image = image(52*16, 40*16, 16) 31 | member("levelEditImage3").image = image(52*16, 40*16, 16) 32 | member("levelEditImageShortCuts").image = image(52*16, 40*16, 16) 33 | 34 | TEdraw(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 1) 35 | TEdraw(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 2) 36 | TEdraw(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 3) 37 | lvlEditDraw(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 1) 38 | lvlEditDraw(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 2) 39 | lvlEditDraw(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 3) 40 | drawShortCutsImg(rect(1,1,gLOprops.size.loch,gLOprops.size.locv), 16) 41 | 42 | gDirectionKeys = [0,0,0,0] 43 | 44 | sprite(3).blend = 10 45 | sprite(4).blend = 10 46 | sprite(5).blend = 60 47 | sprite(6).blend = 10 48 | sprite(7).blend = 70 49 | sprite(8).blend = 100 50 | 51 | 52 | -- sav = gLeProps.camPos 53 | -- gLeProps.camPos = point(0,0) 54 | -- repeat with l = 1 to 3 then 55 | -- lvlEditDraw(rect(1,1,cols,rows), l, 1) 56 | -- TEdraw(rect(1,1,cols,rows), l, 1) 57 | -- end repeat 58 | -- drawShortCutsImg(rect(1,1,cols,rows)) 59 | -- gLeProps.camPos = sav 60 | 61 | l = [#n:0, #m1:0, m2:0, #w:0, #a:0, #s:0, #d:0, #e:0, #r:0, #f:0] 62 | gEEprops.lastKeys = l.duplicate() 63 | gEEprops.keys = l.duplicate() 64 | 65 | -- call(#updateEffectsMenu, point(0,0)) 66 | --call(#up script("effectsEditor").initMode("createNew") 67 | script("effectsEditor").updateEffectsMenu(point(0,0)) 68 | script("effectsEditor").updateEffectsL(0) 69 | script("effectsEditor").initMode("createNew") 70 | -- script("effectsEditor").initMode("createNew") 71 | 72 | script("effectsEditor").drawEfMtrx(gEEprops.editEffect) 73 | end 74 | 75 | 76 | -------------------------------------------------------------------------------- /LingoSource/renderLight2.lingo: -------------------------------------------------------------------------------- 1 | global q, c, tm, mvL 2 | 3 | on exitFrame me 4 | 5 | repeat with q = 1 to 1040 then 6 | pnt = point(q , c) 7 | d = 0 8 | stp = 0 9 | 10 | mvlPs = 1 11 | 12 | if member("lightImage").getPixel(q-1,c-1) = 0 then 13 | repeat while stp = 0 then 14 | 15 | 16 | if d > 19 then 17 | exit repeat 18 | else if member("layer"&string(d)).image.getPixel(pnt+point(-1,-1)) <> color(255, 255, 255) then 19 | member("layer"&string(d)&"sh").image.setPixel(pnt+point(-1,-1), color(255, 0, 0)) 20 | if d > 0 then 21 | repeat with dir in [point(-1, 0), point(0,-1), point(1,0), point(0,1)]then 22 | if member("layer"&string(d)).image.getPixel(pnt+point(-1,-1)+dir) <> color(255, 255, 255) then 23 | member("layer"&string(d)&"sh").image.setPixel(pnt+point(-1,-1)+dir, color(255, 0, 0)) 24 | end if 25 | end repeat 26 | end if 27 | exit repeat 28 | end if 29 | 30 | 31 | mvlPs = mvlPs +1 32 | if mvlPs > mvL.count then 33 | mvlPs = 1 34 | end if 35 | 36 | pnt = pnt + point(mvL[mvLPs][1], mvL[mvLPs][2]) 37 | d = d + mvL[mvLPs][3] 38 | 39 | end repeat 40 | end if 41 | 42 | end repeat 43 | 44 | c = c + 1 45 | 46 | 47 | member("timeLeft").text = string(((c.float/800.0)*100.0).integer) && "% Rendered, Approx. " && string(((( (_system.milliseconds-tm).float /c.float) * (800-c) )/1000).integer) && "seconds left" 48 | sprite(42).loc = point(10, restrict(c, 30, 700)) 49 | 50 | if c > 800 then 51 | 52 | member("shadowImage").image = image(52*20, 40*20, 32) 53 | 54 | repeat with q = 1 to 20 then 55 | dp = 20-q-5 56 | pstRct = rect(depthPnt(point(0,0),dp),depthPnt(point(1040,800),dp)) 57 | member("shadowImage").image.copyPixels(member("layer"&string(20-q)).image, pstRct, rect(0,0,1040,800), {#ink:36, #color:color(255,255,255)}) 58 | member("shadowImage").image.copyPixels(member("layer"&string(20-q)&"sh").image, pstRct, rect(0,0,1040,800), {#ink:36}) 59 | end repeat 60 | 61 | inv = image(52*20, 40*20, 1) 62 | inv.copyPixels(member("pxl").image, rect(0,0,1040,800), rect(0,0,1,1), {#color:255}) 63 | inv.copyPixels(member("shadowImage").image, rect(0,0,1040,800), rect(0,0,1040,800), {#ink:36, #color:color(255,255,255)}) 64 | member("shadowImage").image.copyPixels(inv, rect(0,0,1040,800), rect(0,0,1040,800)) 65 | 66 | 67 | 68 | -- member("shadowImage").image = image(52*20, 40*20, 32)--ALL LIGHT 69 | else 70 | go the frame 71 | end if 72 | 73 | end 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /Drizzle.Editor/AvaloniaSeriLogger.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Logging; 2 | using Serilog; 3 | using Serilog.Core.Enrichers; 4 | 5 | namespace Drizzle.Editor; 6 | 7 | internal sealed class AvaloniaSeriLogger : ILogSink 8 | { 9 | private readonly ILogger _logger; 10 | 11 | public AvaloniaSeriLogger(ILogger logger) 12 | { 13 | _logger = logger; 14 | } 15 | 16 | private ILogger Context(string area, object? source) 17 | { 18 | if (source == null) 19 | { 20 | return _logger.ForContext(new[] 21 | { 22 | new PropertyEnricher("Area", area), 23 | }); 24 | } 25 | 26 | return _logger.ForContext(new[] 27 | { 28 | new PropertyEnricher("Area", area), 29 | new PropertyEnricher("SourceHash", source.GetHashCode().ToString("X8")), 30 | new PropertyEnricher("SourceType", source.GetType()) 31 | }); 32 | } 33 | 34 | public bool IsEnabled(LogEventLevel level, string area) 35 | { 36 | return _logger.IsEnabled((Serilog.Events.LogEventLevel) level); 37 | } 38 | 39 | public void Log(LogEventLevel level, string area, object? source, string messageTemplate) 40 | { 41 | Context(area, source).Write((Serilog.Events.LogEventLevel) level, messageTemplate); 42 | } 43 | 44 | public void Log( 45 | LogEventLevel level, 46 | string area, 47 | object? source, 48 | string messageTemplate, 49 | T0 propertyValue0) 50 | { 51 | Context(area, source).Write((Serilog.Events.LogEventLevel) level, messageTemplate, propertyValue0); 52 | } 53 | 54 | public void Log( 55 | LogEventLevel level, 56 | string area, 57 | object? source, 58 | string messageTemplate, 59 | T0 propertyValue0, 60 | T1 propertyValue1) 61 | { 62 | Context(area, source).Write((Serilog.Events.LogEventLevel) level, messageTemplate, propertyValue0, propertyValue1); 63 | } 64 | 65 | public void Log( 66 | LogEventLevel level, 67 | string area, 68 | object? source, 69 | string messageTemplate, 70 | T0 propertyValue0, 71 | T1 propertyValue1, 72 | T2 propertyValue2) 73 | { 74 | Context(area, source).Write((Serilog.Events.LogEventLevel) level, messageTemplate, propertyValue0, propertyValue1, 75 | propertyValue2); 76 | } 77 | 78 | public void Log( 79 | LogEventLevel level, 80 | string area, 81 | object? source, 82 | string messageTemplate, 83 | params object?[] propertyValues) 84 | { 85 | Context(area, source).Write((Serilog.Events.LogEventLevel) level, messageTemplate, propertyValues); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Drizzle.Editor/Views/LevelPreviewControl.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Media; 2 | using Avalonia.ReactiveUI; 3 | using Drizzle.Editor.ViewModels; 4 | using Drizzle.Lingo.Runtime; 5 | using Drizzle.Ported; 6 | using ReactiveUI; 7 | 8 | namespace Drizzle.Editor.Views; 9 | 10 | public sealed class LevelPreviewControl : ReactiveUserControl 11 | { 12 | private const float TileSize = 5; 13 | 14 | private Brush _brushLayer3 = new SolidColorBrush(Colors.DarkGray); 15 | private Brush _brushLayer2 = new SolidColorBrush(Colors.Gray); 16 | private Brush _brushLayer1 = new SolidColorBrush(Colors.Black); 17 | private Pen _penNone = new Pen(Brushes.Transparent, 0f); 18 | 19 | public LevelPreviewControl() 20 | { 21 | this.WhenActivated(disposables => 22 | { 23 | var mv = ViewModel!.Runtime.MovieScript(); 24 | Width = (int)mv.gLOprops.size.loch * TileSize; 25 | Height = (int)mv.gLOprops.size.locv * TileSize; 26 | }); 27 | 28 | InvalidateVisual(); 29 | } 30 | 31 | public override void Render(DrawingContext context) 32 | { 33 | base.Render(context); 34 | 35 | var mv = ViewModel!.Runtime.MovieScript(); 36 | 37 | var sizeMaxX = (int)mv.gLOprops.size.loch; 38 | var sizeMaxY = (int)mv.gLOprops.size.locv; 39 | 40 | for (var layer = 3; layer > 0; layer--) 41 | { 42 | var brush = layer switch 43 | { 44 | 1 => _brushLayer1, 45 | 2 => _brushLayer2, 46 | _ => _brushLayer3, 47 | }; 48 | 49 | var geometry = new StreamGeometry(); 50 | var gCtx = geometry.Open(); 51 | 52 | for (var x = 1; x <= sizeMaxX; x++) 53 | { 54 | var column = mv.gLEProps.matrix[x]; 55 | for (var y = 1; y <= sizeMaxY; y++) 56 | { 57 | var offsetX = (x - 1) * TileSize; 58 | var offsetY = (y - 1) * TileSize; 59 | 60 | var dat = column[y][layer]; 61 | 62 | EditorRendering.DrawTileGeometry(offsetX, offsetY, TileSize, gCtx, (TileGeometry)(int)dat[1]); 63 | 64 | var features = (LingoList) dat[2]; 65 | foreach (var feature in features.List) 66 | { 67 | switch ((TileFeature)(int)(LingoNumber)feature!) 68 | { 69 | case TileFeature.BeamHorizontal: 70 | EditorRendering.DrawBeamHorizontal(offsetX, offsetY, TileSize, gCtx); 71 | break; 72 | case TileFeature.BeamVertical: 73 | EditorRendering.DrawBeamVertical(offsetX, offsetY, TileSize, gCtx); 74 | break; 75 | } 76 | } 77 | } 78 | } 79 | 80 | context.DrawGeometry(brush, _penNone, geometry); 81 | } 82 | } 83 | } -------------------------------------------------------------------------------- /Drizzle.Editor/Views/Render/RenderWindow.axaml: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 |