├── .github └── workflows │ ├── deploy.yml │ └── tests.yml ├── .gitignore ├── .gitmodules ├── CSharpScriptsTable.json ├── GodotFixedVolatilePhysics.csproj ├── GodotFixedVolatilePhysics.sln ├── LICENSE ├── README.md ├── ReadmeAssets ├── FullPhysicsTest.gif ├── KinematicBody.gif └── PhysicsTest.gif ├── addons ├── GodotFixedVolatilePhysics │ ├── Assets │ │ ├── SimpleForce.svg │ │ ├── SimpleForce.svg.import │ │ ├── VoltArea.svg │ │ ├── VoltArea.svg.import │ │ ├── VoltKinematicBody.svg │ │ ├── VoltKinematicBody.svg.import │ │ ├── VoltNode2D.svg │ │ ├── VoltNode2D.svg.import │ │ ├── VoltPolygon.svg │ │ ├── VoltPolygon.svg.import │ │ ├── VoltRect.svg │ │ ├── VoltRect.svg.import │ │ ├── VoltRigidBody.svg │ │ ├── VoltRigidBody.svg.import │ │ ├── VoltStaticBody.svg │ │ ├── VoltStaticBody.svg.import │ │ ├── VoltWorld.svg │ │ └── VoltWorld.svg.import │ ├── Core │ │ ├── Palette.cs │ │ ├── SimpleForce.cs │ │ ├── Test.cs │ │ ├── TypeSerializers │ │ │ ├── ArraySerializer.cs │ │ │ ├── TypeSerializer.cs │ │ │ └── TypeSerializers.cs │ │ ├── Types │ │ │ ├── VoltMatrix.cs │ │ │ ├── VoltRect2.cs │ │ │ └── VoltTransform2D.cs │ │ ├── Utils.cs │ │ ├── VolatileBodies │ │ │ ├── VolatileArea.cs │ │ │ ├── VolatileBody.cs │ │ │ ├── VolatileKinematicBody.cs │ │ │ ├── VolatileRigidBody.cs │ │ │ └── VolatileStaticBody.cs │ │ ├── VolatileExtensions.cs │ │ ├── VolatileShapes │ │ │ ├── VolatileCircle.cs │ │ │ ├── VolatilePolygon.cs │ │ │ ├── VolatileRect.cs │ │ │ └── VolatileShape.cs │ │ ├── VolatileWorld.cs │ │ ├── VoltNode2D.cs │ │ └── VoltType.cs │ ├── Libraries │ │ ├── FixedMath.Net │ │ │ ├── LICENSE.txt │ │ │ ├── README.txt │ │ │ └── src │ │ │ │ ├── Fix64.cs │ │ │ │ ├── Fix64SinLut.cs │ │ │ │ └── Fix64TanLut.cs │ │ └── VolatilePhysics │ │ │ ├── Extensions │ │ │ └── VoltExplosion.cs │ │ │ ├── Internals │ │ │ ├── Axis.cs │ │ │ ├── Broadphase │ │ │ │ ├── IBroadphase.cs │ │ │ │ ├── NaiveBroadphase.cs │ │ │ │ └── TreeBroadphase.cs │ │ │ ├── CheapList.cs │ │ │ ├── Collision │ │ │ │ ├── Collision.cs │ │ │ │ ├── Contact.cs │ │ │ │ └── Manifold.cs │ │ │ ├── History │ │ │ │ ├── HistoryBuffer.cs │ │ │ │ └── HistoryRecord.cs │ │ │ └── IIndexedValue.cs │ │ │ ├── RayCast │ │ │ ├── VoltRayCast.cs │ │ │ └── VoltRayResult.cs │ │ │ ├── Shapes │ │ │ ├── VoltCircle.cs │ │ │ └── VoltPolygon.cs │ │ │ ├── Structs │ │ │ └── VoltShapeCollision.cs │ │ │ ├── Util │ │ │ ├── Debug │ │ │ │ └── VoltDebug.cs │ │ │ ├── Pooling │ │ │ │ ├── IVoltPoolable.cs │ │ │ │ └── VoltPool.cs │ │ │ └── VoltUtil.cs │ │ │ ├── VoltAABB.cs │ │ │ ├── VoltBody.cs │ │ │ ├── VoltBuffer.cs │ │ │ ├── VoltCollisionFilters.cs │ │ │ ├── VoltConfig.cs │ │ │ ├── VoltMath.cs │ │ │ ├── VoltShape.cs │ │ │ ├── VoltVector2.cs │ │ │ └── VoltWorld.cs │ ├── Plugin.cs │ ├── Plugins │ │ ├── VolatileNodesPlugin │ │ │ └── VolatileNodesPlugin.cs │ │ ├── VolatileShapePlugins │ │ │ ├── PointsEditorPlugin.cs │ │ │ ├── VolatileCirclePlugin.cs │ │ │ ├── VolatilePolygonPlugin.cs │ │ │ ├── VolatileRectPlugin.cs │ │ │ └── VolatileShapeEditorPlugin.cs │ │ └── VoltTypesPlugin │ │ │ ├── EditorProperties │ │ │ ├── CompoundSerializedEditorProperty.cs │ │ │ ├── EditorPropertyExtens.cs │ │ │ ├── ExtendedEditorProperty.cs │ │ │ ├── ExtendedEditorPropertyParser.cs │ │ │ ├── Fix64EditorProperty.cs │ │ │ ├── SerializedEditorProperty.cs │ │ │ ├── SerializedEditorPropertyParser.cs │ │ │ ├── VoltArrayEditorProperty.cs │ │ │ ├── VoltRect2EditorProperty.cs │ │ │ ├── VoltTransform2DEditorProperty.cs │ │ │ └── VoltVector2EditorProperty.cs │ │ │ ├── VoltPropertyHint.cs │ │ │ ├── VoltTypesInspectorPlugin.cs │ │ │ └── VoltTypesPlugin.cs │ └── plugin.cfg ├── GodotVisualStudioStub │ └── StubProject.csproj ├── VirtualJoystick │ ├── VirtualJoystick.cs │ ├── textures │ │ ├── joystick_base_outline.png │ │ ├── joystick_base_outline.png.import │ │ ├── joystick_tip.png │ │ ├── joystick_tip.png.import │ │ ├── joystick_tip_arrows.png │ │ └── joystick_tip_arrows.png.import │ ├── virtual_joystick.gd │ └── virtual_joystick.tscn └── WAT │ ├── LICENSE │ ├── assertions │ ├── .idea │ │ └── .idea.assertions.dir │ │ │ └── .idea │ │ │ ├── encodings.xml │ │ │ ├── indexLayout.xml │ │ │ ├── projectSettingsUpdater.xml │ │ │ ├── vcs.xml │ │ │ └── workspace.xml │ ├── assertion.gd │ ├── assertions.gd │ ├── boolean.gd │ ├── collections.gd │ ├── double.gd │ ├── equality.gd │ ├── file.gd │ ├── folder.gd │ ├── is.gd │ ├── is_not.gd │ ├── null.gd │ ├── object.gd │ ├── property.gd │ ├── range.gd │ ├── signal.gd │ ├── string.gd │ └── utility.gd │ ├── assets │ ├── debug_failed.png │ ├── debug_failed.png.import │ ├── docs.svg │ ├── docs.svg.import │ ├── failed.png │ ├── failed.png.import │ ├── folder.png │ ├── folder.png.import │ ├── function.png │ ├── function.png.import │ ├── issue.svg │ ├── issue.svg.import │ ├── kofi.png │ ├── kofi.png.import │ ├── label.png │ ├── label.png.import │ ├── passed.png │ ├── passed.png.import │ ├── play.png │ ├── play.png.import │ ├── play_debug.png │ ├── play_debug.png.import │ ├── play_failed.png │ ├── play_failed.png.import │ ├── request_docs.svg │ ├── request_docs.svg.import │ ├── script.png │ ├── script.png.import │ ├── timer.png │ └── timer.png.import │ ├── cli.tscn │ ├── double │ ├── factory.gd │ ├── method.gd │ ├── methods.gd │ ├── registry.gd │ ├── scene_director.gd │ ├── script_director.gd │ └── script_writer.gd │ ├── filesystem │ ├── directory.gd │ ├── failed.gd │ ├── filesystem.gd │ ├── method.gd │ ├── script.gd │ ├── tagged.gd │ ├── tracker.gd │ ├── validator.gd │ └── yield_calculator.gd │ ├── gui.tscn │ ├── io │ ├── junit_xml.gd │ └── metadata.gd │ ├── mono │ ├── Attributes.cs │ ├── BuildScene.cs │ ├── BuildScene.tscn │ ├── GDScriptWrapper.cs │ ├── Test.cs │ └── assertions │ │ ├── Assertion.cs │ │ ├── Assertions.cs │ │ ├── Boolean.cs │ │ ├── Equality.cs │ │ ├── File.cs │ │ ├── Is.cs │ │ ├── Null.cs │ │ ├── ObjectX.cs │ │ ├── Property.cs │ │ ├── Range.cs │ │ ├── Signal.cs │ │ ├── StringX.cs │ │ └── Utility.cs │ ├── namespace.gd │ ├── network │ ├── test_client.gd │ ├── test_network.gd │ └── test_server.gd │ ├── plugin.cfg │ ├── plugin.gd │ ├── runner │ ├── TestRunner.gd │ ├── TestRunner.tscn │ ├── splitter.gd │ └── test_controller.gd │ ├── settings.gd │ ├── test │ ├── any.gd │ ├── case.gd │ ├── parameters.gd │ ├── recorder.gd │ ├── test.gd │ ├── watcher.gd │ └── yielder.gd │ └── ui │ ├── cli.gd │ ├── docker.gd │ ├── gui.gd │ ├── links.gd │ ├── results │ ├── assertion.gd │ ├── counter.gd │ ├── method.gd │ ├── script.gd │ ├── tab.gd │ ├── tab_container.gd │ └── tree.gd │ ├── scaling │ ├── dynamic_size_spinbox.gd │ ├── icons.gd │ ├── plugin_assets_registry.gd │ └── scene_tree_adjuster.gd │ ├── summary.gd │ └── test_menu.gd ├── bootsplash.png ├── bootsplash.png.import ├── default_env.tres ├── icon.png ├── icon.png.import ├── project.godot └── tests ├── Manual ├── Physics │ ├── CompleteTest.tscn │ ├── FPSCounter.cs │ ├── GDKinematicMovement.cs │ ├── GDPhysics2DTesting.tscn │ ├── PhysicsHighEndTest.tscn │ ├── PhysicsLowEndTest.tscn │ ├── PolygonTests.tscn │ └── Prefabs │ │ ├── DispersedDynamicGroupPrefab.tscn │ │ ├── DynamicGroupPrefab.tscn │ │ ├── VolatileAreaTestPrefab.tscn │ │ └── VolatileKinematicMovementPrefab.tscn └── VoltNode2D │ ├── FixedMovement.cs │ ├── VolatileAreaTest.cs │ ├── VolatileKinematicBodyMovement.cs │ └── VoltNode2DTest.tscn ├── VoltMatrixTests.cs ├── VoltNode2DTests.cs └── VoltTransform2DTests.cs /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: 🚀 Deploy Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - "v*.*.*" 9 | 10 | jobs: 11 | deploy: 12 | name: Deploy 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout Repository 16 | uses: actions/checkout@v3 17 | 18 | - name: Update Submodules 19 | run: git submodule update --init 20 | 21 | # Deploy to local repo 22 | - name: Deploy to Release Branch 23 | uses: s0/git-publish-subdir-action@develop 24 | env: 25 | REPO: self 26 | BRANCH: release 27 | FOLDER: addons/GodotFixedVolatilePhysics 28 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 29 | 30 | - name: Create Release 31 | run: | 32 | mkdir release 33 | mv addons/GodotFixedVolatilePhysics release 34 | pushd release 35 | zip -r ../Release.zip * 36 | popd 37 | 38 | - name: Upload Release 39 | uses: actions/upload-artifact@v2 40 | with: 41 | name: Release 42 | path: release 43 | 44 | - name: Tagged Release 45 | uses: softprops/action-gh-release@v1 46 | if: startsWith(github.ref, 'refs/tags/') 47 | with: 48 | files: Release.zip 49 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: 🧪 Run Unit Tests 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | Tests: 7 | name: Run All Tests on 3.5 8 | runs-on: ubuntu-latest 9 | container: barichello/godot-ci:mono-3.5 10 | steps: 11 | - name: Checkout Repository 12 | uses: actions/checkout@v3 13 | with: 14 | submodules: recursive 15 | 16 | - name: Install Dependencies 17 | run: | 18 | nuget restore 19 | mkdir -p .mono/assemblies/Debug 20 | cp /usr/local/bin/GodotSharp/Api/Release/* .mono/assemblies/Debug 21 | 22 | - name: Compile 23 | run: msbuild 24 | 25 | - name: Run 26 | run: godot addons/WAT/cli.tscn run=all 27 | 28 | - name: Upload Test Results 29 | if: always() 30 | uses: actions/upload-artifact@v2 31 | with: 32 | name: Test Results 33 | path: tests/results.xml 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Godot-specific ignores 2 | .import/ 3 | export.cfg 4 | export_presets.cfg 5 | 6 | # Mono-specific ignores 7 | .mono/ 8 | data_*/ 9 | 10 | # VSCode Settings 11 | .vscode/ 12 | 13 | # Visual studio 14 | .vs/ 15 | 16 | Builds/ 17 | 18 | # Ignore stub bin and obj folders 19 | bin/ 20 | obj/ 21 | 22 | # Tests 23 | tests/results.xml 24 | tests/metadata.json -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "addons/FracturalCommons"] 2 | path = addons/FracturalCommons 3 | url = https://github.com/Fractural/FracturalCommons.git 4 | branch = release 5 | -------------------------------------------------------------------------------- /GodotFixedVolatilePhysics.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net472 4 | 5 | 6 | 7 | $(DefineConstants);GODOT_3_4_0_OR_NEWER 8 | $(DefineConstants);GODOT_3_4_1_OR_NEWER 9 | $(DefineConstants);GODOT_3_4_2_OR_NEWER 10 | $(DefineConstants);GODOT_3_4_3_OR_NEWER 11 | $(DefineConstants);GODOT_3_4_4_OR_NEWER 12 | $(DefineConstants);GODOT_3_4_5_OR_NEWER 13 | $(DefineConstants);GODOT_3_5_0_OR_NEWER 14 | $(DefineConstants);GODOT_3_0_0_OR_NEWER 15 | $(DefineConstants);GODOT_3_0_1_OR_NEWER 16 | $(DefineConstants);GODOT_3_0_2_OR_NEWER 17 | $(DefineConstants);GODOT_3_0_3_OR_NEWER 18 | $(DefineConstants);GODOT_3_0_4_OR_NEWER 19 | $(DefineConstants);GODOT_3_0_5_OR_NEWER 20 | $(DefineConstants);GODOT_3_0_6_OR_NEWER 21 | $(DefineConstants);GODOT_3_1_0_OR_NEWER 22 | $(DefineConstants);GODOT_3_1_1_OR_NEWER 23 | $(DefineConstants);GODOT_3_1_2_OR_NEWER 24 | $(DefineConstants);GODOT_3_2_0_OR_NEWER 25 | $(DefineConstants);GODOT_3_2_1_OR_NEWER 26 | $(DefineConstants);GODOT_3_2_2_OR_NEWER 27 | $(DefineConstants);GODOT_3_2_3_OR_NEWER 28 | $(DefineConstants);GODOT_3_3_0_OR_NEWER 29 | $(DefineConstants);GODOT_3_3_1_OR_NEWER 30 | $(DefineConstants);GODOT_3_3_2_OR_NEWER 31 | $(DefineConstants);GODOT_3_3_3_OR_NEWER 32 | $(DefineConstants);GODOT_3_5 33 | $(DefineConstants);GODOT_3 34 | $(DefineConstants);GODOT_3_5_0 35 | 36 | -------------------------------------------------------------------------------- /GodotFixedVolatilePhysics.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.32630.194 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GodotFixedVolatilePhysics", "GodotFixedVolatilePhysics.csproj", "{0BB5CFC8-534D-4408-B29A-50C9FDEE65C1}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StubProject", "addons\GodotVisualStudioStub\StubProject.csproj", "{B83A5A30-7D9A-4BCC-BB56-2974A2AAE767}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | ExportDebug|Any CPU = ExportDebug|Any CPU 14 | ExportRelease|Any CPU = ExportRelease|Any CPU 15 | EndGlobalSection 16 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 17 | {0BB5CFC8-534D-4408-B29A-50C9FDEE65C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 18 | {0BB5CFC8-534D-4408-B29A-50C9FDEE65C1}.Debug|Any CPU.Build.0 = Debug|Any CPU 19 | {0BB5CFC8-534D-4408-B29A-50C9FDEE65C1}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU 20 | {0BB5CFC8-534D-4408-B29A-50C9FDEE65C1}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU 21 | {0BB5CFC8-534D-4408-B29A-50C9FDEE65C1}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU 22 | {0BB5CFC8-534D-4408-B29A-50C9FDEE65C1}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU 23 | {B83A5A30-7D9A-4BCC-BB56-2974A2AAE767}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {B83A5A30-7D9A-4BCC-BB56-2974A2AAE767}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {B83A5A30-7D9A-4BCC-BB56-2974A2AAE767}.ExportDebug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {B83A5A30-7D9A-4BCC-BB56-2974A2AAE767}.ExportDebug|Any CPU.Build.0 = Debug|Any CPU 27 | {B83A5A30-7D9A-4BCC-BB56-2974A2AAE767}.ExportRelease|Any CPU.ActiveCfg = Debug|Any CPU 28 | {B83A5A30-7D9A-4BCC-BB56-2974A2AAE767}.ExportRelease|Any CPU.Build.0 = Debug|Any CPU 29 | EndGlobalSection 30 | GlobalSection(SolutionProperties) = preSolution 31 | HideSolutionNode = FALSE 32 | EndGlobalSection 33 | GlobalSection(ExtensibilityGlobals) = postSolution 34 | SolutionGuid = {EC9A5306-CD19-4736-B1F2-7CF06944D688} 35 | EndGlobalSection 36 | EndGlobal 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Fractural 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Godot Fixed Volatile Physics 💥 2 | 3 | ![Deploy](https://github.com/Fractural/GodotFixedVolatilePhysics/actions/workflows/deploy.yml/badge.svg) ![Unit Tests](https://github.com/Fractural/GodotFixedVolatilePhysics/actions/workflows/tests.yml/badge.svg) 4 | 5 | Fixed-point 2D physics in Godot C# using a modified version of [cathei's Fixed point fork of VolatilePhysics](https://github.com/cathei/VolatilePhysics-FixedMath). 6 | 7 | ## Features 8 | - Dynamic rigid bodies 9 | - Kinematic bodies 10 | - Static bodies 11 | - Area bodies 12 | - Shape editors 13 | - Debug drawing 14 | - Collision layers and masks 15 | 16 | ## Examples 17 | 18 | ![Physics Test](ReadmeAssets/PhysicsTest.gif) 19 | ![Kinematic Body](ReadmeAssets/KinematicBody.gif) 20 | ![Full Physics Test](ReadmeAssets/FullPhysicsTest.gif) 21 | -------------------------------------------------------------------------------- /ReadmeAssets/FullPhysicsTest.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/ReadmeAssets/FullPhysicsTest.gif -------------------------------------------------------------------------------- /ReadmeAssets/KinematicBody.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/ReadmeAssets/KinematicBody.gif -------------------------------------------------------------------------------- /ReadmeAssets/PhysicsTest.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/ReadmeAssets/PhysicsTest.gif -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/SimpleForce.svg: -------------------------------------------------------------------------------- 1 | Layer 1 -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/SimpleForce.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/SimpleForce.svg-ddab30616003e6d80a476d7ee8ad700e.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/GodotFixedVolatilePhysics/Assets/SimpleForce.svg" 13 | dest_files=[ "res://.import/SimpleForce.svg-ddab30616003e6d80a476d7ee8ad700e.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/VoltArea.svg: -------------------------------------------------------------------------------- 1 | Layer 1 -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/VoltArea.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/VoltArea.svg-6f308530d7fb20a6323be50a206fbcef.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/GodotFixedVolatilePhysics/Assets/VoltArea.svg" 13 | dest_files=[ "res://.import/VoltArea.svg-6f308530d7fb20a6323be50a206fbcef.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/VoltKinematicBody.svg: -------------------------------------------------------------------------------- 1 | Layer 1 -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/VoltKinematicBody.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/VoltKinematicBody.svg-876a52416679eaa23c269e98bd58a9d9.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/GodotFixedVolatilePhysics/Assets/VoltKinematicBody.svg" 13 | dest_files=[ "res://.import/VoltKinematicBody.svg-876a52416679eaa23c269e98bd58a9d9.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/VoltNode2D.svg: -------------------------------------------------------------------------------- 1 | Layer 1 -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/VoltNode2D.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/VoltNode2D.svg-b53f68da92fe909dd218e942582e08b7.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/GodotFixedVolatilePhysics/Assets/VoltNode2D.svg" 13 | dest_files=[ "res://.import/VoltNode2D.svg-b53f68da92fe909dd218e942582e08b7.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/VoltPolygon.svg: -------------------------------------------------------------------------------- 1 | Layer 1 -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/VoltPolygon.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/VoltPolygon.svg-6e383c4108bed18e7d7bac33f302de9f.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/GodotFixedVolatilePhysics/Assets/VoltPolygon.svg" 13 | dest_files=[ "res://.import/VoltPolygon.svg-6e383c4108bed18e7d7bac33f302de9f.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/VoltRect.svg: -------------------------------------------------------------------------------- 1 | Layer 1 -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/VoltRect.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/VoltRect.svg-cf93039269bef17d4e32c7ec486e9bd6.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/GodotFixedVolatilePhysics/Assets/VoltRect.svg" 13 | dest_files=[ "res://.import/VoltRect.svg-cf93039269bef17d4e32c7ec486e9bd6.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/VoltRigidBody.svg: -------------------------------------------------------------------------------- 1 | Layer 1 -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/VoltRigidBody.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/VoltRigidBody.svg-9c53786d162056b3bd2a3f5caab2a5a6.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/GodotFixedVolatilePhysics/Assets/VoltRigidBody.svg" 13 | dest_files=[ "res://.import/VoltRigidBody.svg-9c53786d162056b3bd2a3f5caab2a5a6.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/VoltStaticBody.svg: -------------------------------------------------------------------------------- 1 | Layer 1 -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/VoltStaticBody.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/VoltStaticBody.svg-dfc5982df2af1c208528a225925cc8e7.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/GodotFixedVolatilePhysics/Assets/VoltStaticBody.svg" 13 | dest_files=[ "res://.import/VoltStaticBody.svg-dfc5982df2af1c208528a225925cc8e7.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/VoltWorld.svg: -------------------------------------------------------------------------------- 1 | Layer 1 -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Assets/VoltWorld.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/VoltWorld.svg-5b858ec866258a9839b45c7193974ce1.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/GodotFixedVolatilePhysics/Assets/VoltWorld.svg" 13 | dest_files=[ "res://.import/VoltWorld.svg-5b858ec866258a9839b45c7193974ce1.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Core/Palette.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | 3 | namespace Volatile.GodotEngine 4 | { 5 | public static class Palette 6 | { 7 | public static readonly Color Main = new Color("c93294"); 8 | public static readonly Color Accent = new Color("c91850"); 9 | public static readonly Color Blank = Colors.White; 10 | 11 | public static readonly Color DynamicBody = new Color("c93294"); 12 | public static readonly Color KinematicBody = new Color("eb3144"); 13 | public static readonly Color StaticBody = new Color("b935de"); 14 | public static readonly Color AreaBody = new Color("3299c9"); 15 | } 16 | } -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Core/SimpleForce.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | 3 | namespace Volatile.GodotEngine 4 | { 5 | [Tool] 6 | public class SimpleForce : VoltNode2D 7 | { 8 | #region Force 9 | private VoltVector2 force; 10 | public VoltVector2 Force 11 | { 12 | get 13 | { 14 | #if TOOLS 15 | if (Engine.EditorHint) 16 | return VoltType.DeserializeOrDefault(_force); 17 | else 18 | #endif 19 | return force; 20 | } 21 | set 22 | { 23 | #if TOOLS 24 | if (Engine.EditorHint) 25 | _force = VoltType.Serialize(value); 26 | else 27 | #endif 28 | force = value; 29 | } 30 | } 31 | [Export(hintString: VoltPropertyHint.VoltVector2)] 32 | private byte[] _force; 33 | #endregion 34 | 35 | private IVolatileRigidBody body; 36 | 37 | public override void _Ready() 38 | { 39 | base._Ready(); 40 | Force = VoltType.DeserializeOrDefault(_force); 41 | 42 | if (Engine.EditorHint) 43 | { 44 | SetPhysicsProcess(false); 45 | return; 46 | } 47 | body = GetParent(); 48 | } 49 | 50 | public override string _GetConfigurationWarning() 51 | { 52 | if (!(GetParent() is IVolatileRigidBody)) 53 | return $"{nameof(SimpleForce)} must be a child of a {nameof(IVolatileRigidBody)}!"; 54 | return ""; 55 | } 56 | 57 | public override void _PhysicsProcess(float delta) 58 | { 59 | if (body != null) body.AddForce(Force); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Core/Test.cs: -------------------------------------------------------------------------------- 1 | using FixMath.NET; 2 | using Godot; 3 | 4 | namespace Volatile.GodotEngine 5 | { 6 | public class Test : Node2D 7 | { 8 | [Export(hintString: VoltPropertyHint.Fix64)] 9 | public byte[] fix64Number = VoltType.Serialize(Fix64.From("3384.4390")); 10 | [Export(hintString: VoltPropertyHint.VoltVector2)] 11 | public byte[] fixedVector = VoltType.Serialize(VoltVector2.From("3.430", "-234.03999")); 12 | [Export(hintString: VoltPropertyHint.Array + "," + VoltPropertyHint.Fix64)] 13 | public byte[] fixed64Array = VoltType.Serialize(new[] 14 | { 15 | Fix64.From("303.43003"), 16 | Fix64.From("40.34939"), 17 | Fix64.From("9348.2340"), 18 | Fix64.From("23.34029"), 19 | }); 20 | [Export(hintString: VoltPropertyHint.Array + "," + VoltPropertyHint.VoltVector2)] 21 | public byte[] fixedVectorArray = VoltType.Serialize(new[] 22 | { 23 | VoltVector2.From("303.43003", "230.3409"), 24 | VoltVector2.From("40.34939", "9348.2340"), 25 | VoltVector2.From("23.34029", "24.34038"), 26 | }); 27 | [Export] 28 | public byte[] regularByteArray = new byte[0]; 29 | [Export(hintString: VoltPropertyHint.Array + "," + VoltPropertyHint.VoltRect2)] 30 | public byte[] voltRectArray = VoltType.Serialize(new VoltRect2[] { }); 31 | [Export(hintString: VoltPropertyHint.VoltRect2)] 32 | public byte[] voltRect = VoltType.Serialize(new VoltRect2()); 33 | [Export] 34 | public string tests; 35 | [Export] 36 | public Rect2 rect; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Core/TypeSerializers/ArraySerializer.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | 4 | namespace Volatile.GodotEngine 5 | { 6 | public class ArraySerializer 7 | { 8 | public static readonly ArraySerializer Global = new ArraySerializer(); 9 | 10 | public Array Deserialize(Type elementType, byte[] data) 11 | { 12 | var buffer = new StreamPeerBuffer(); 13 | buffer.PutData(data); 14 | buffer.Seek(0); 15 | return Deserialize(elementType, buffer); 16 | } 17 | 18 | public byte[] Serialize(Type elementType, Array value) 19 | { 20 | var buffer = new StreamPeerBuffer(); 21 | Serialize(elementType, buffer, value); 22 | return buffer.DataArray; 23 | } 24 | 25 | public Array Deserialize(Type elementType, StreamPeerBuffer buffer) 26 | { 27 | var length = buffer.GetU32(); 28 | var array = Array.CreateInstance(elementType, length); 29 | for (int i = 0; i < length; i++) 30 | { 31 | // We don't to use the bytes for anything. 32 | // We can store at most 255 bytes per type. 33 | buffer.GetU8(); 34 | array.SetValue(VoltType.Deserialize(elementType, buffer), i); 35 | } 36 | return array; 37 | } 38 | 39 | public Array Default(Type elementType) => Array.CreateInstance(elementType, 0); 40 | public byte[] DefaultAsBytes(Type elementType) => Serialize(elementType, Default(elementType)); 41 | 42 | public void Serialize(Type elementType, StreamPeerBuffer buffer, Array array) 43 | { 44 | buffer.PutU32((uint)array.Length); 45 | for (int i = 0; i < array.Length; i++) 46 | { 47 | var byteArray = VoltType.Serialize(elementType, array.GetValue(i)); 48 | // By storing the byte length per element, we can dynamically omit 49 | // unecessary data in each type serializer. But the caveat is that 50 | // every element will take up an extra byte. 51 | buffer.PutU8((byte)byteArray.Length); 52 | buffer.PutData(byteArray); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Core/TypeSerializers/TypeSerializers.cs: -------------------------------------------------------------------------------- 1 | using FixMath.NET; 2 | using Godot; 3 | 4 | namespace Volatile.GodotEngine 5 | { 6 | public class Fix64Serializer : TypeSerializer 7 | { 8 | public static readonly Fix64Serializer Global = new Fix64Serializer(); 9 | 10 | public override void Serialize(StreamPeerBuffer buffer, Fix64 value) => buffer.PutFix64(value); 11 | public override Fix64 Deserialize(StreamPeerBuffer buffer) => buffer.GetFix64(); 12 | public override Fix64 Default() => Fix64.Zero; 13 | } 14 | 15 | public class VoltVector2Serializer : TypeSerializer 16 | { 17 | public static readonly VoltVector2Serializer Global = new VoltVector2Serializer(); 18 | 19 | public override void Serialize(StreamPeerBuffer buffer, VoltVector2 value) => buffer.PutVoltVector2(value); 20 | public override VoltVector2 Deserialize(StreamPeerBuffer buffer) => buffer.GetVoltVector2(); 21 | public override VoltVector2 Default() => VoltVector2.Zero; 22 | } 23 | 24 | public class VoltTransform2DSerializer : TypeSerializer 25 | { 26 | public static readonly VoltTransform2DSerializer Global = new VoltTransform2DSerializer(); 27 | 28 | public override void Serialize(StreamPeerBuffer buffer, VoltTransform2D value) => buffer.PutVoltTransform2D(value); 29 | public override VoltTransform2D Deserialize(StreamPeerBuffer buffer) => buffer.GetVoltTransform2D(); 30 | public override VoltTransform2D Default() => VoltTransform2D.Default(); 31 | } 32 | 33 | public class VoltRect2Serializer : TypeSerializer 34 | { 35 | public static readonly VoltRect2Serializer Global = new VoltRect2Serializer(); 36 | 37 | public override void Serialize(StreamPeerBuffer buffer, VoltRect2 value) => buffer.PutVoltRect2(value); 38 | public override VoltRect2 Deserialize(StreamPeerBuffer buffer) => buffer.GetVoltRect2(); 39 | public override VoltRect2 Default() => new VoltRect2(); 40 | } 41 | 42 | public class VoltMatrixSerializer : TypeSerializer 43 | { 44 | public static readonly VoltMatrixSerializer Global = new VoltMatrixSerializer(); 45 | 46 | public override void Serialize(StreamPeerBuffer buffer, VoltMatrix value) => buffer.PutVoltMatrix(value); 47 | public override VoltMatrix Deserialize(StreamPeerBuffer buffer) => buffer.GetVoltMatrix(); 48 | public override VoltMatrix Default() => new VoltMatrix(1, 1); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Core/Types/VoltRect2.cs: -------------------------------------------------------------------------------- 1 | using FixMath.NET; 2 | using System.Text; 3 | using Volatile; 4 | 5 | namespace Volatile.GodotEngine 6 | { 7 | public struct VoltRect2 8 | { 9 | public VoltRect2(VoltVector2 position, VoltVector2 size) 10 | { 11 | Size = size; 12 | Position = position; 13 | } 14 | 15 | public VoltVector2 Size { get; set; } 16 | public VoltVector2 Position { get; set; } 17 | 18 | public VoltVector2 TopLeft => Position; 19 | public VoltVector2 TopRight => new VoltVector2(Position.x + Size.x, Position.y); 20 | public VoltVector2 BottomRight => new VoltVector2(Position.x + Size.x, Position.y + Size.y); 21 | public VoltVector2 BottomLeft => new VoltVector2(Position.x, Position.y + Size.y); 22 | 23 | public VoltVector2[] Points => new VoltVector2[] 24 | { 25 | TopLeft, 26 | TopRight, 27 | BottomRight, 28 | BottomLeft 29 | }; 30 | 31 | private static readonly Fix64 FIX_2 = Fix64.From(2); 32 | 33 | public VoltVector2 GetCenter() 34 | { 35 | return Position + Size / FIX_2; 36 | } 37 | 38 | public override string ToString() 39 | { 40 | return $"[Position: {Position}, Size: {Size}]"; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Core/Utils.cs: -------------------------------------------------------------------------------- 1 | using FixMath.NET; 2 | using System.Linq; 3 | 4 | namespace Volatile.GodotEngine 5 | { 6 | public static class Utils 7 | { 8 | public static Fix64 SignedArea(this VoltVector2[] points) 9 | { 10 | Fix64 sum = Fix64.Zero; 11 | 12 | for (int i = 0; i < points.Length; i++) 13 | { 14 | VoltVector2 v = points[i]; 15 | VoltVector2 u = points[(i + 1) % points.Length]; 16 | VoltVector2 w = points[(i + 2) % points.Length]; 17 | 18 | sum += u.x * (v.y - w.y); 19 | } 20 | 21 | return sum / (Fix64)2; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Core/VolatileBodies/VolatileKinematicBody.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | 3 | namespace Volatile.GodotEngine 4 | { 5 | public interface IVolatileKinematicBody : IVolatileBody 6 | { 7 | VoltKinematicCollisionResult MoveAndCollide(VoltVector2 linearVelocity); 8 | VoltVector2 MoveAndSlide(VoltVector2 linearVelocity, int maxSlides = 4); 9 | } 10 | 11 | [Tool] 12 | public class VolatileKinematicBody : VolatileBody, IVolatileKinematicBody 13 | { 14 | protected override VoltBody CreateBody(VoltWorld world, VoltShape[] shapes) 15 | => world.CreateKinematicBody(GlobalFixedPosition, GlobalFixedRotation, shapes, Layer, Mask); 16 | 17 | public VoltKinematicCollisionResult MoveAndCollide(VoltVector2 linearVelocity) 18 | { 19 | return Body.MoveAndCollide(linearVelocity); 20 | } 21 | 22 | public VoltVector2 MoveAndSlide(VoltVector2 linearVelocity, int maxSlides = 4) 23 | { 24 | return Body.MoveAndSlide(linearVelocity, maxSlides); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Core/VolatileBodies/VolatileRigidBody.cs: -------------------------------------------------------------------------------- 1 | using FixMath.NET; 2 | using Godot; 3 | 4 | namespace Volatile.GodotEngine 5 | { 6 | public interface IVolatileRigidBody : IVolatileBody 7 | { 8 | void AddForce(VoltVector2 force); 9 | void AddTorque(Fix64 radians); 10 | 11 | void Set(VoltVector2 position, Fix64 radians); 12 | 13 | void SetVelocity(VoltVector2 linearVelocity, Fix64 angularVelocity); 14 | 15 | void SetForce(VoltVector2 force, Fix64 torque, VoltVector2 biasVelocity, Fix64 biasRotation); 16 | } 17 | 18 | [Tool] 19 | public class VolatileRigidBody : VolatileBody, IVolatileRigidBody 20 | { 21 | protected override VoltBody CreateBody(VoltWorld world, VoltShape[] shapes) 22 | => world.CreateDynamicBody(GlobalFixedPosition, GlobalFixedRotation, shapes, Layer, Mask); 23 | 24 | public void AddForce(VoltVector2 force) 25 | { 26 | Body.AddForce(force); 27 | } 28 | 29 | public void AddTorque(Fix64 radians) 30 | { 31 | Body.AddTorque(radians); 32 | } 33 | 34 | public void Set(VoltVector2 position, Fix64 radians) 35 | { 36 | Body.Set(position, radians); 37 | } 38 | 39 | public void SetVelocity(VoltVector2 linearVelocity, Fix64 angularVelocity) 40 | { 41 | Body.LinearVelocity = linearVelocity; 42 | Body.AngularVelocity = angularVelocity; 43 | } 44 | 45 | public void SetForce(VoltVector2 force, Fix64 torque, VoltVector2 biasVelocity, Fix64 biasRotation) 46 | { 47 | Body.SetForce(force, torque, biasVelocity, biasRotation); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Core/VolatileBodies/VolatileStaticBody.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | 3 | namespace Volatile.GodotEngine 4 | { 5 | public interface IVolatileStaticBody : IVolatileBody { } 6 | 7 | [Tool] 8 | public class VolatileStaticBody : VolatileBody, IVolatileStaticBody 9 | { 10 | protected override VoltBody CreateBody(VoltWorld world, VoltShape[] shapes) 11 | => world.CreateStaticBody(GlobalFixedPosition, GlobalFixedRotation, shapes, Layer, Mask); 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Core/VolatileShapes/VolatileCircle.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using Godot.Collections; 3 | using Fractural; 4 | using FixMath.NET; 5 | #if TOOLS 6 | using Volatile.GodotEngine.Plugin; 7 | #endif 8 | 9 | namespace Volatile.GodotEngine 10 | { 11 | [Tool] 12 | public class VolatileCircle : VolatileShape 13 | { 14 | public override VoltShape PrepareShape(VoltWorld world) 15 | { 16 | return world.CreateCircleWorldSpace(GlobalFixedPosition, Radius); 17 | } 18 | 19 | public override Vector2 ComputeLocalCenterOfMass() 20 | { 21 | return Vector2.Zero; 22 | } 23 | 24 | public override void _Ready() 25 | { 26 | base._Ready(); 27 | _OnRadiusSet = VoltType.DeserializeOrDefault(_radius); 28 | if (!Engine.EditorHint) 29 | Radius = VoltType.DeserializeOrDefault(_radius); 30 | } 31 | 32 | #region Radius 33 | protected Fix64 radius; 34 | public Fix64 Radius 35 | { 36 | get 37 | { 38 | #if TOOLS 39 | if (Engine.EditorHint) 40 | return VoltType.DeserializeOrDefault(_radius); 41 | else 42 | #endif 43 | return radius; 44 | } 45 | set 46 | { 47 | #if TOOLS 48 | if (Engine.EditorHint) 49 | _radius = VoltType.Serialize(value); 50 | else 51 | #endif 52 | radius = value; 53 | } 54 | } 55 | [Export(hintString: VoltPropertyHint.Fix64 + ",set:" + nameof(_OnRadiusSet))] 56 | public byte[] _radius = VoltType.Serialize(Fix64.From(1)); 57 | 58 | public float EditorRadius { get; set; } 59 | private Fix64 _OnRadiusSet 60 | { 61 | set 62 | { 63 | EditorRadius = (float)value; 64 | Update(); 65 | } 66 | } 67 | #endregion 68 | 69 | public override void _Draw() 70 | { 71 | base._Draw(); 72 | if (!DebugDraw && (!Engine.EditorHint || EditorRadius == 0)) return; 73 | var radius = EditorRadius; 74 | 75 | var color = GetShapeDrawColor(); 76 | var fill = color; 77 | fill.a = 0.075f; 78 | 79 | var circumference = 2 * Mathf.Pi * radius; 80 | var points = Mathf.Max((int)(circumference * 10), 10); 81 | DrawLine(Vector2.Zero, new Vector2(radius, 0), color); 82 | DrawArc(Vector2.Zero, radius, 0, 2 * Mathf.Pi, points, color); 83 | DrawCircle(Vector2.Zero, radius, fill); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Core/VolatileWorld.cs: -------------------------------------------------------------------------------- 1 | using FixMath.NET; 2 | using Godot; 3 | 4 | namespace Volatile.GodotEngine 5 | { 6 | public interface IVolatileWorld 7 | { 8 | VoltWorld World { get; } 9 | } 10 | 11 | // Tool tag is required to make VolatileWorld discoverable in the editor by VolatileBody, 12 | // which checks if it's a child of a VolatileWorld when generating configuration warnings. 13 | [Tool] 14 | public class VolatileWorld : VoltNode2D, IVolatileWorld 15 | { 16 | [Export] 17 | public int HistoryLength = 0; 18 | [Export] 19 | public bool ProcessSelf { get; set; } = true; 20 | [Export] 21 | public bool DebugDraw { get; set; } = false; 22 | public VoltWorld World { get; private set; } 23 | 24 | public override void _EnterTree() 25 | { 26 | base._EnterTree(); 27 | 28 | if (Engine.EditorHint) 29 | { 30 | SetPhysicsProcess(false); 31 | return; 32 | } 33 | World = new VoltWorld(HistoryLength); 34 | SetPhysicsProcess(ProcessSelf); 35 | World.DeltaTime = Fix64.One / (Fix64)Engine.IterationsPerSecond; 36 | } 37 | 38 | public override void _Ready() 39 | { 40 | base._Ready(); 41 | 42 | if (Engine.EditorHint) 43 | { 44 | SetPhysicsProcess(false); 45 | return; 46 | } 47 | } 48 | 49 | public override void _PhysicsProcess(float delta) 50 | { 51 | World.Update(); 52 | Update(); 53 | } 54 | 55 | public override void _Draw() 56 | { 57 | base._Draw(); 58 | if (Engine.EditorHint || !DebugDraw) return; 59 | var color = Palette.Accent; 60 | foreach (var body in World.Bodies) 61 | { 62 | DrawRect(new Rect2(body.AABB.BottomLeft.ToGDVector2(), body.AABB.Size.ToGDVector2()), color, false); 63 | for (int i = 0; i < body.shapeCount; i++) 64 | { 65 | var AABB = body.shapes[i].AABB; 66 | DrawRect(new Rect2(AABB.BottomLeft.ToGDVector2(), AABB.Size.ToGDVector2()), color, false); 67 | } 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Libraries/FixedMath.Net/README.txt: -------------------------------------------------------------------------------- 1 | This library implements "Fix64", a 64 bit fixed point 31.32 numeric type and transcendent operations on it (square root, trig, etc). It is well covered by unit tests. However, it is still missing some operations; in particular, Tangent is not well tested yet. 2 | 3 | In the unit tests you'll find implementations for Int32-based (Q15.16) and Byte-based (Q3.4) numeric types. These were used for exploration of boundary conditions etc., but I'm keeping the code there only for reference. 4 | 5 | This project started as a port of libfixmath (http://code.google.com/p/libfixmath/). 6 | 7 | Note that the type requires explicit casts to convert to floating point and this is intentional, the difference between fixed point and floating point math is as important as the one between floating point and integral math. 8 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Libraries/VolatilePhysics/Internals/Axis.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * VolatilePhysics - A 2D Physics Library for Networked Games 3 | * Copyright (c) 2015-2016 - Alexander Shoulson - http://ashoulson.com 4 | * 5 | * This software is provided 'as-is', without any express or implied 6 | * warranty. In no event will the authors be held liable for any damages 7 | * arising from the use of this software. 8 | * Permission is granted to anyone to use this software for any purpose, 9 | * including commercial applications, and to alter it and redistribute it 10 | * freely, subject to the following restrictions: 11 | * 12 | * 1. The origin of this software must not be misrepresented; you must not 13 | * claim that you wrote the original software. If you use this software 14 | * in a product, an acknowledgment in the product documentation would be 15 | * appreciated but is not required. 16 | * 2. Altered source versions must be plainly marked as such, and must not be 17 | * misrepresented as being the original software. 18 | * 3. This notice may not be removed or altered from any source distribution. 19 | */ 20 | 21 | using FixMath.NET; 22 | using System; 23 | using System.Collections.Generic; 24 | 25 | #if UNITY 26 | using UnityEngine; 27 | #endif 28 | 29 | namespace Volatile 30 | { 31 | /// 32 | /// The Axis data structure represents a "slab" between the given edge and 33 | /// a parallel edge drawn at the origin. The "width" value gives the width 34 | /// of that axis slab, defined as follows: For an edge AB with normal N, 35 | /// this width w is given by Dot(A, N). If you take edge AB, and draw an 36 | /// edge CD parallel to AB that intersects the origin, the width w is equal 37 | /// to the minimum distance between edges AB and CD. 38 | /// 39 | /// | 40 | /// | C 41 | /// | / 42 | /// | / A 43 | /// | /ヽ / 44 | /// | / ヽ / 45 | /// |/ w ヽ / 46 | /// -----------+---------ヽ/---- 47 | /// /| / 48 | /// D | / 49 | /// | / 50 | /// | B 51 | /// | 52 | /// 53 | /// 54 | internal struct Axis 55 | { 56 | internal VoltVector2 Normal { get { return this.normal; } } 57 | internal Fix64 Width { get { return this.width; } } 58 | 59 | private readonly VoltVector2 normal; 60 | private readonly Fix64 width; 61 | 62 | internal Axis(VoltVector2 normal, Fix64 width) 63 | { 64 | this.normal = normal; 65 | this.width = width; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Libraries/VolatilePhysics/Internals/Broadphase/IBroadphase.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * VolatilePhysics - A 2D Physics Library for Networked Games 3 | * Copyright (c) 2015-2016 - Alexander Shoulson - http://ashoulson.com 4 | * 5 | * This software is provided 'as-is', without any express or implied 6 | * warranty. In no event will the authors be held liable for any damages 7 | * arising from the use of this software. 8 | * Permission is granted to anyone to use this software for any purpose, 9 | * including commercial applications, and to alter it and redistribute it 10 | * freely, subject to the following restrictions: 11 | * 12 | * 1. The origin of this software must not be misrepresented; you must not 13 | * claim that you wrote the original software. If you use this software 14 | * in a product, an acknowledgment in the product documentation would be 15 | * appreciated but is not required. 16 | * 2. Altered source versions must be plainly marked as such, and must not be 17 | * misrepresented as being the original software. 18 | * 3. This notice may not be removed or altered from any source distribution. 19 | */ 20 | 21 | using FixMath.NET; 22 | using System; 23 | using System.Collections.Generic; 24 | 25 | #if UNITY 26 | using UnityEngine; 27 | #endif 28 | 29 | namespace Volatile 30 | { 31 | internal interface IBroadPhase 32 | { 33 | void AddBody(VoltBody body); 34 | void RemoveBody(VoltBody body); 35 | void UpdateBody(VoltBody body); 36 | 37 | // Note that these should return bodies that meet the criteria within the 38 | // spaces defined by the structure itself. These tests should not test the 39 | // actual body's bounding box, as that will happen in the beginning of the 40 | // narrowphase test. 41 | void QueryOverlap(VoltAABB aabb, VoltBuffer outBuffer); 42 | void QueryPoint(VoltVector2 point, VoltBuffer outBuffer); 43 | void QueryCircle(VoltVector2 point, Fix64 radius, VoltBuffer outBuffer); 44 | void RayCast(ref VoltRayCast ray, VoltBuffer outBuffer); 45 | void CircleCast(ref VoltRayCast ray, Fix64 radius, VoltBuffer outBuffer); 46 | } 47 | } -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Libraries/VolatilePhysics/Internals/IIndexedValue.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * VolatilePhysics - A 2D Physics Library for Networked Games 3 | * Copyright (c) 2015-2016 - Alexander Shoulson - http://ashoulson.com 4 | * 5 | * This software is provided 'as-is', without any express or implied 6 | * warranty. In no event will the authors be held liable for any damages 7 | * arising from the use of this software. 8 | * Permission is granted to anyone to use this software for any purpose, 9 | * including commercial applications, and to alter it and redistribute it 10 | * freely, subject to the following restrictions: 11 | * 12 | * 1. The origin of this software must not be misrepresented; you must not 13 | * claim that you wrote the original software. If you use this software 14 | * in a product, an acknowledgment in the product documentation would be 15 | * appreciated but is not required. 16 | * 2. Altered source versions must be plainly marked as such, and must not be 17 | * misrepresented as being the original software. 18 | * 3. This notice may not be removed or altered from any source distribution. 19 | */ 20 | 21 | using System; 22 | using System.Collections.Generic; 23 | 24 | namespace Volatile 25 | { 26 | internal interface IIndexedValue 27 | { 28 | int Index { get; set; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Libraries/VolatilePhysics/RayCast/VoltRayCast.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * VolatilePhysics - A 2D Physics Library for Networked Games 3 | * Copyright (c) 2015-2016 - Alexander Shoulson - http://ashoulson.com 4 | * 5 | * This software is provided 'as-is', without any express or implied 6 | * warranty. In no event will the authors be held liable for any damages 7 | * arising from the use of this software. 8 | * Permission is granted to anyone to use this software for any purpose, 9 | * including commercial applications, and to alter it and redistribute it 10 | * freely, subject to the following restrictions: 11 | * 12 | * 1. The origin of this software must not be misrepresented; you must not 13 | * claim that you wrote the original software. If you use this software 14 | * in a product, an acknowledgment in the product documentation would be 15 | * appreciated but is not required. 16 | * 2. Altered source versions must be plainly marked as such, and must not be 17 | * misrepresented as being the original software. 18 | * 3. This notice may not be removed or altered from any source distribution. 19 | */ 20 | 21 | using FixMath.NET; 22 | using System; 23 | using System.Collections.Generic; 24 | 25 | #if UNITY 26 | using UnityEngine; 27 | #endif 28 | 29 | namespace Volatile 30 | { 31 | /// 32 | /// A semi-precomputed ray optimized for fast AABB tests. 33 | /// 34 | public struct VoltRayCast 35 | { 36 | internal readonly VoltVector2 origin; 37 | internal readonly VoltVector2 direction; 38 | internal readonly VoltVector2 invDirection; 39 | internal readonly Fix64 distance; 40 | internal readonly bool signX; 41 | internal readonly bool signY; 42 | 43 | public VoltRayCast(VoltVector2 origin, VoltVector2 destination) 44 | { 45 | VoltVector2 delta = destination - origin; 46 | 47 | this.origin = origin; 48 | this.direction = delta.Normalized; 49 | this.distance = delta.Magnitude; 50 | this.signX = direction.x < Fix64.Zero; 51 | this.signY = direction.y < Fix64.Zero; 52 | this.invDirection = 53 | new VoltVector2(Fix64.One / direction.x, Fix64.One / direction.y); 54 | } 55 | 56 | public VoltRayCast(VoltVector2 origin, VoltVector2 direction, Fix64 distance) 57 | { 58 | this.origin = origin; 59 | this.direction = direction; 60 | this.distance = distance; 61 | this.signX = direction.x < Fix64.Zero; 62 | this.signY = direction.y < Fix64.Zero; 63 | this.invDirection = 64 | new VoltVector2(Fix64.One / direction.x, Fix64.One / direction.y); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Libraries/VolatilePhysics/Util/Pooling/IVoltPoolable.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * VolatilePhysics - A 2D Physics Library for Networked Games 3 | * Copyright (c) 2015-2016 - Alexander Shoulson - http://ashoulson.com 4 | * 5 | * This software is provided 'as-is', without any express or implied 6 | * warranty. In no event will the authors be held liable for any damages 7 | * arising from the use of this software. 8 | * Permission is granted to anyone to use this software for any purpose, 9 | * including commercial applications, and to alter it and redistribute it 10 | * freely, subject to the following restrictions: 11 | * 12 | * 1. The origin of this software must not be misrepresented; you must not 13 | * claim that you wrote the original software. If you use this software 14 | * in a product, an acknowledgment in the product documentation would be 15 | * appreciated but is not required. 16 | * 2. Altered source versions must be plainly marked as such, and must not be 17 | * misrepresented as being the original software. 18 | * 3. This notice may not be removed or altered from any source distribution. 19 | */ 20 | 21 | namespace Volatile 22 | { 23 | public interface IVoltPoolable 24 | where T : IVoltPoolable 25 | { 26 | IVoltPool Pool { get; set; } 27 | void Reset(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Libraries/VolatilePhysics/Util/VoltUtil.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * VolatilePhysics - A 2D Physics Library for Networked Games 3 | * Copyright (c) 2015-2016 - Alexander Shoulson - http://ashoulson.com 4 | * 5 | * This software is provided 'as-is', without any express or implied 6 | * warranty. In no event will the authors be held liable for any damages 7 | * arising from the use of this software. 8 | * Permission is granted to anyone to use this software for any purpose, 9 | * including commercial applications, and to alter it and redistribute it 10 | * freely, subject to the following restrictions: 11 | * 12 | * 1. The origin of this software must not be misrepresented; you must not 13 | * claim that you wrote the original software. If you use this software 14 | * in a product, an acknowledgment in the product documentation would be 15 | * appreciated but is not required. 16 | * 2. Altered source versions must be plainly marked as such, and must not be 17 | * misrepresented as being the original software. 18 | * 3. This notice may not be removed or altered from any source distribution. 19 | */ 20 | 21 | using System; 22 | 23 | namespace Volatile 24 | { 25 | public static class VoltUtil 26 | { 27 | public static void Swap(ref T a, ref T b) 28 | { 29 | T temp = b; 30 | b = a; 31 | a = temp; 32 | } 33 | 34 | public static int ExpandArray(ref T[] oldArray, int minSize = 1) 35 | { 36 | // TODO: Revisit this using next-largest primes like built-in lists do 37 | int newCapacity = Math.Max(oldArray.Length * 2, minSize); 38 | T[] newArray = new T[newCapacity]; 39 | Array.Copy(oldArray, newArray, oldArray.Length); 40 | oldArray = newArray; 41 | return newCapacity; 42 | } 43 | 44 | public static bool Filter_StaticOnly(VoltBody body) 45 | { 46 | return body.IsStatic; 47 | } 48 | 49 | public static bool Filter_DynamicOnly(VoltBody body) 50 | { 51 | return (body.IsStatic == false); 52 | } 53 | 54 | public static bool Filter_All(VoltBody body) 55 | { 56 | return true; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Libraries/VolatilePhysics/VoltBuffer.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * VolatilePhysics - A 2D Physics Library for Networked Games 3 | * Copyright (c) 2015-2016 - Alexander Shoulson - http://ashoulson.com 4 | * 5 | * This software is provided 'as-is', without any express or implied 6 | * warranty. In no event will the authors be held liable for any damages 7 | * arising from the use of this software. 8 | * Permission is granted to anyone to use this software for any purpose, 9 | * including commercial applications, and to alter it and redistribute it 10 | * freely, subject to the following restrictions: 11 | * 12 | * 1. The origin of this software must not be misrepresented; you must not 13 | * claim that you wrote the original software. If you use this software 14 | * in a product, an acknowledgment in the product documentation would be 15 | * appreciated but is not required. 16 | * 2. Altered source versions must be plainly marked as such, and must not be 17 | * misrepresented as being the original software. 18 | * 3. This notice may not be removed or altered from any source distribution. 19 | */ 20 | 21 | using System; 22 | using System.Collections; 23 | using System.Collections.Generic; 24 | 25 | namespace Volatile 26 | { 27 | public class VoltBuffer : IEnumerable 28 | { 29 | public int Count { get { return this.count; } } 30 | public T this[int key] { get { return this.items[key]; } } 31 | 32 | private T[] items; 33 | private int count; 34 | 35 | public VoltBuffer(int capacity = 256) 36 | { 37 | this.items = new T[capacity]; 38 | this.count = 0; 39 | } 40 | 41 | /// 42 | /// Adds a new element to the end of the list. Returns the index of the 43 | /// newly-indexed object. 44 | /// 45 | internal void Add(T body) 46 | { 47 | if (this.count >= this.items.Length) 48 | VoltUtil.ExpandArray(ref this.items); 49 | 50 | this.items[this.count] = body; 51 | this.count++; 52 | } 53 | 54 | internal void Add(T[] bodies, int count) 55 | { 56 | if ((this.count + count) >= this.items.Length) 57 | VoltUtil.ExpandArray(ref this.items, (this.count + count)); 58 | 59 | Array.Copy(bodies, 0, this.items, this.count, count); 60 | this.count += count; 61 | } 62 | 63 | public void Clear() 64 | { 65 | this.count = 0; 66 | } 67 | 68 | public IEnumerator GetEnumerator() 69 | { 70 | for (int i = 0; i < this.count; i++) 71 | yield return this.items[i]; 72 | } 73 | 74 | IEnumerator IEnumerable.GetEnumerator() 75 | { 76 | for (int i = 0; i < this.count; i++) 77 | yield return this.items[i]; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Libraries/VolatilePhysics/VoltConfig.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * VolatilePhysics - A 2D Physics Library for Networked Games 3 | * Copyright (c) 2015-2016 - Alexander Shoulson - http://ashoulson.com 4 | * 5 | * This software is provided 'as-is', without any express or implied 6 | * warranty. In no event will the authors be held liable for any damages 7 | * arising from the use of this software. 8 | * Permission is granted to anyone to use this software for any purpose, 9 | * including commercial applications, and to alter it and redistribute it 10 | * freely, subject to the following restrictions: 11 | * 12 | * 1. The origin of this software must not be misrepresented; you must not 13 | * claim that you wrote the original software. If you use this software 14 | * in a product, an acknowledgment in the product documentation would be 15 | * appreciated but is not required. 16 | * 2. Altered source versions must be plainly marked as such, and must not be 17 | * misrepresented as being the original software. 18 | * 3. This notice may not be removed or altered from any source distribution. 19 | */ 20 | 21 | using FixMath.NET; 22 | using System; 23 | using System.Collections.Generic; 24 | 25 | #if UNITY 26 | using UnityEngine; 27 | #endif 28 | 29 | namespace Volatile 30 | { 31 | public static class VoltConfig 32 | { 33 | public static Fix64 ResolveSlop = (Fix64)0.01M; 34 | public static Fix64 ResolveRate = (Fix64)0.1M; 35 | public static Fix64 AreaMassRatio = (Fix64)0.01M; 36 | 37 | // Defaults 38 | public static readonly Fix64 DEFAULT_DENSITY = Fix64.One; 39 | public static readonly Fix64 DEFAULT_RESTITUTION = (Fix64)0.5M; 40 | public static readonly Fix64 DEFAULT_FRICTION = (Fix64)0.8M; 41 | 42 | internal static readonly Fix64 DEFAULT_DELTA_TIME = (Fix64)0.02M; 43 | internal static readonly Fix64 DEFAULT_DAMPING = (Fix64)0.999M; 44 | internal const int DEFAULT_ITERATION_COUNT = 20; 45 | 46 | // AABB extension for the dynamic tree 47 | internal static readonly Fix64 AABB_EXTENSION = (Fix64)0.2M; 48 | 49 | // Maximum contacts for collision resolution. 50 | internal const int MAX_CONTACTS = 3; 51 | 52 | // Used for initializing timesteps 53 | internal const int INVALID_TIME = -1; 54 | 55 | // The minimum mass a dynamic object can have before it is 56 | // converted to a static object 57 | internal static readonly Fix64 MINIMUM_DYNAMIC_MASS = (Fix64)0.00001M; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Plugin.cs: -------------------------------------------------------------------------------- 1 | using Fractural.Plugin; 2 | using Fractural.Plugin.AssetsRegistry; 3 | using Godot; 4 | 5 | #if TOOLS 6 | namespace Volatile.GodotEngine.Plugin 7 | { 8 | [Tool] 9 | public class Plugin : ExtendedPlugin 10 | { 11 | public override string PluginName => "Godot Fixed Volatile Physics"; 12 | 13 | protected override void Load() 14 | { 15 | AssetsRegistry = new EditorAssetsRegistry(this); 16 | AddSubPlugin(new VolatilePolygonPlugin()); 17 | AddSubPlugin(new VolatileRectPlugin()); 18 | AddSubPlugin(new VolatileCirclePlugin()); 19 | AddSubPlugin(new VoltTypesPlugin()); 20 | AddSubPlugin(new VolatileNodesPlugin()); 21 | } 22 | 23 | protected override void Unload() 24 | { 25 | 26 | } 27 | } 28 | } 29 | #endif -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Plugins/VolatileShapePlugins/VolatileShapeEditorPlugin.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | 3 | #if TOOLS 4 | namespace Volatile.GodotEngine.Plugin 5 | { 6 | [Tool] 7 | public abstract class VolatileShapeEditorPlugin : PointsEditorPlugin 8 | { 9 | public override void AddAndDrawAnchor(Anchor anchor) 10 | { 11 | if (EditedTarget is VolatileShape shape) 12 | AddAndDrawAnchor(anchor, CIRCLE_RADIUS, STROKE_RADIUS, FILL_COLOR, shape.GetShapeDrawColor()); 13 | } 14 | } 15 | } 16 | #endif -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Plugins/VoltTypesPlugin/EditorProperties/CompoundSerializedEditorProperty.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | 3 | #if TOOLS 4 | namespace Volatile.GodotEngine.Plugin 5 | { 6 | [Tool] 7 | public abstract class CompoundSerializedEditorProperty : SerializedEditorProperty where TTypeSerializer : TypeSerializer, new() 8 | { 9 | public const int INDENTATION = 10; 10 | 11 | protected VBoxContainer propertiesVBox; 12 | 13 | public CompoundSerializedEditorProperty() { } 14 | public CompoundSerializedEditorProperty(float scale) 15 | { 16 | propertiesVBox = new VBoxContainer(); 17 | 18 | var marginContainer = new MarginContainer(); 19 | marginContainer.AddChild(propertiesVBox); 20 | marginContainer.AddConstantOverride("margin_left", (int)(INDENTATION * scale)); 21 | 22 | InitSubProperties(); 23 | 24 | AddChild(marginContainer); 25 | AddFocusable(marginContainer); 26 | SetBottomEditor(marginContainer); 27 | } 28 | 29 | protected abstract void InitSubProperties(); 30 | protected void AddProperty(SerializedEditorProperty property, string label, string manualEditedProperty = "", bool supressFocusable = true, bool useManualValue = true) where LTTypeSerializer : TypeSerializer, new() 31 | { 32 | if (label != "" && manualEditedProperty == "") 33 | manualEditedProperty = label; 34 | property.SupressFocusable = true; 35 | property.UseManualValue = true; 36 | property.Label = label; 37 | property.ManualEditedProperty = manualEditedProperty; 38 | property.Connect("property_changed", this, nameof(OnPropertyChanged)); 39 | propertiesVBox.AddChild(property); 40 | } 41 | 42 | private void OnPropertyChanged(string property, object value, string field, bool changing) 43 | { 44 | if (updating) return; 45 | OnSubPropertyChanged(property); 46 | SerializeWorkingValueToEditor(); 47 | } 48 | 49 | protected abstract void OnSubPropertyChanged(string property); 50 | } 51 | } 52 | #endif -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Plugins/VoltTypesPlugin/EditorProperties/EditorPropertyExtens.cs: -------------------------------------------------------------------------------- 1 | #if TOOLS 2 | namespace Volatile.GodotEngine.Plugin 3 | { 4 | public static class EditorPropertyExtens 5 | { 6 | /// 7 | /// Configures the to override regular editor functionality. This is used for embedding this editor property inside of another editor proeprty. 8 | /// 9 | /// 10 | /// 11 | /// 12 | /// 13 | public static void ConfigureOverrides(this IExtendedEditorProperty property, string manualEditedProperty = "", Godot.Object manualEditedObject = null, bool suppressFocusable = false, string label = "") 14 | { 15 | property.ManualEditedProperty = manualEditedProperty; 16 | property.ManualEditedObject = manualEditedObject; 17 | property.SupressFocusable = suppressFocusable; 18 | property.Label = label; 19 | } 20 | 21 | /// 22 | /// Configures the to override regular editor functionality. This is used for embedding this editor property inside of another editor proeprty. 23 | /// 24 | /// 25 | /// 26 | /// 27 | /// 28 | /// 29 | public static void ConfigureOverrides(this ISerializedEditorProperty property, string manualEditedProperty = "", Godot.Object manualEditedObject = null, bool suppressFocusable = false, string label = "", bool useManualValue = false) 30 | { 31 | ((IExtendedEditorProperty)property).ConfigureOverrides(manualEditedProperty, manualEditedObject, suppressFocusable, label); 32 | property.UseManualValue = useManualValue; 33 | } 34 | } 35 | } 36 | #endif -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Plugins/VoltTypesPlugin/EditorProperties/ExtendedEditorProperty.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using GDC = Godot.Collections; 3 | 4 | #if TOOLS 5 | namespace Volatile.GodotEngine.Plugin 6 | { 7 | public interface IEditorProperty 8 | { 9 | string GetEditedProperty(); 10 | Godot.Object GetEditedObject(); 11 | void AddFocusable(Control control); 12 | void UpdateProperty(); 13 | string Label { get; set; } 14 | Error Connect(string signal, Godot.Object target, string method, GDC.Array binds = null, uint flags = 0); 15 | } 16 | 17 | public interface IExtendedEditorProperty : IEditorProperty 18 | { 19 | bool SupressFocusable { get; set; } 20 | string ManualEditedProperty { get; set; } 21 | Godot.Object ManualEditedObject { get; set; } 22 | } 23 | 24 | [Tool] 25 | public abstract class ExtendedEditorProperty : EditorProperty, IExtendedEditorProperty 26 | { 27 | public bool SupressFocusable { get; set; } = false; 28 | public string ManualEditedProperty { get; set; } = ""; 29 | public Godot.Object ManualEditedObject { get; set; } 30 | public new string GetEditedProperty() 31 | { 32 | if (ManualEditedProperty != "") 33 | return ManualEditedProperty; 34 | return base.GetEditedProperty(); 35 | } 36 | 37 | public new Godot.Object GetEditedObject() 38 | { 39 | if (ManualEditedObject != null) 40 | return ManualEditedObject; 41 | return base.GetEditedObject(); 42 | } 43 | 44 | public new void AddFocusable(Control control) 45 | { 46 | // We don't want to add focus if we're 47 | // not using this as an editor property 48 | // inside the inspector. (ie. being 49 | // using inside of an array editor 50 | // property) 51 | if (SupressFocusable) 52 | base.AddFocusable(control); 53 | } 54 | 55 | protected bool updating; 56 | 57 | public override void UpdateProperty() 58 | { 59 | updating = true; 60 | InternalUpdateProperty(); 61 | updating = false; 62 | } 63 | 64 | protected abstract void InternalUpdateProperty(); 65 | 66 | public void ConfigureOverrides(string manualEditedProperty = "", Object manualEditedObject = null, bool supressFocusable = false) 67 | { 68 | ManualEditedProperty = manualEditedProperty; 69 | ManualEditedObject = manualEditedObject; 70 | SupressFocusable = supressFocusable; 71 | } 72 | } 73 | } 74 | #endif -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Plugins/VoltTypesPlugin/EditorProperties/ExtendedEditorPropertyParser.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | 3 | #if TOOLS 4 | namespace Volatile.GodotEngine.Plugin 5 | { 6 | /// 7 | /// Parser for . It can return the default object for a type, and can also parse properties for the type. 8 | /// 9 | [Tool] 10 | public abstract class ExtendedEditorPropertyParser : Godot.Reference 11 | { 12 | public virtual ExtendedEditorProperty ParseProperty(Godot.Object @object, int type, string path, int hint, string hintText, int usage, string[] args) 13 | { 14 | var property = ParseProperty(args); 15 | if (property != null) 16 | return property; 17 | return null; 18 | } 19 | public abstract ExtendedEditorProperty ParseProperty(string[] args); 20 | 21 | public abstract object GetDefaultObject(string[] args); 22 | } 23 | } 24 | #endif -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Plugins/VoltTypesPlugin/EditorProperties/VoltRect2EditorProperty.cs: -------------------------------------------------------------------------------- 1 | using Fractural.Utils; 2 | using Godot; 3 | 4 | #if TOOLS 5 | namespace Volatile.GodotEngine.Plugin 6 | { 7 | [Tool] 8 | public class VoltRect2EditorProperty : CompoundSerializedEditorProperty 9 | { 10 | private VoltVector2EditorProperty positionProperty; 11 | private VoltVector2EditorProperty sizeProperty; 12 | 13 | public VoltRect2EditorProperty() { } 14 | public VoltRect2EditorProperty(float scale) : base(scale) { } 15 | 16 | protected override void InitSubProperties() 17 | { 18 | positionProperty = new VoltVector2EditorProperty(); 19 | AddProperty(positionProperty, "Position"); 20 | 21 | sizeProperty = new VoltVector2EditorProperty(); 22 | AddProperty(sizeProperty, "Size"); 23 | } 24 | 25 | protected override void InternalUpdateProperty() 26 | { 27 | positionProperty.UpdateProperty(workingValue.Position); 28 | sizeProperty.UpdateProperty(workingValue.Size); 29 | } 30 | 31 | protected override void OnSubPropertyChanged(string property) 32 | { 33 | switch (property) 34 | { 35 | case "Position": 36 | workingValue.Position = positionProperty.Value; 37 | break; 38 | case "Size": 39 | workingValue.Size = sizeProperty.Value; 40 | break; 41 | } 42 | } 43 | } 44 | 45 | [Tool] 46 | public class VoltRect2EditorPropertyParser : SerializedEditorPropertyParser 47 | { 48 | private float scale; 49 | 50 | public VoltRect2EditorPropertyParser() { } 51 | public VoltRect2EditorPropertyParser(float scale) 52 | { 53 | this.scale = scale; 54 | } 55 | 56 | public override ISerializedEditorProperty ParseSerializedProperty(string[] args) 57 | { 58 | if (args.TryGet(0) == VoltPropertyHint.VoltRect2) 59 | return new VoltRect2EditorProperty(scale); 60 | return null; 61 | } 62 | 63 | public override object GetDefaultObject(string[] args) 64 | { 65 | if (args.TryGet(0) == VoltPropertyHint.VoltRect2) 66 | return VoltRect2Serializer.Global.Default(); 67 | return null; 68 | } 69 | } 70 | } 71 | #endif -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Plugins/VoltTypesPlugin/VoltPropertyHint.cs: -------------------------------------------------------------------------------- 1 | using FixMath.NET; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace Volatile.GodotEngine 6 | { 7 | public static class VoltPropertyHint 8 | { 9 | static VoltPropertyHint() 10 | { 11 | HintToType = new Dictionary(); 12 | HintToType.Add(Fix64, typeof(Fix64)); 13 | HintToType.Add(VoltVector2, typeof(VoltVector2)); 14 | HintToType.Add(VoltTransform2D, typeof(VoltTransform2D)); 15 | HintToType.Add(VoltRect2, typeof(VoltRect2)); 16 | } 17 | 18 | public static readonly Dictionary HintToType; 19 | 20 | public const string Array = nameof(Array); 21 | public const string Fix64 = nameof(Fix64); 22 | public const string VoltVector2 = nameof(VoltVector2); 23 | public const string VoltTransform2D = nameof(VoltTransform2D); 24 | public const string VoltRect2 = nameof(VoltRect2); 25 | 26 | public static readonly string[] Values = 27 | { 28 | Array, 29 | Fix64, 30 | VoltVector2, 31 | VoltTransform2D, 32 | VoltRect2 33 | }; 34 | } 35 | } -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/Plugins/VoltTypesPlugin/VoltTypesPlugin.cs: -------------------------------------------------------------------------------- 1 | using FixMath.NET; 2 | using Fractural.Plugin; 3 | using Godot; 4 | 5 | #if TOOLS 6 | namespace Volatile.GodotEngine.Plugin 7 | { 8 | [Tool] 9 | public class VoltTypesPlugin : SubPlugin 10 | { 11 | public override string PluginName => nameof(VoltTypesPlugin); 12 | 13 | public override void Load() 14 | { 15 | Plugin.AddManagedInspectorPlugin(new VoltTypesInspectorPlugin(Plugin)); 16 | } 17 | } 18 | } 19 | #endif -------------------------------------------------------------------------------- /addons/GodotFixedVolatilePhysics/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="GodotFixedVoltailePhysics" 4 | description="Deterministic 2D physics" 5 | author="github/Fractural" 6 | version="1" 7 | script="Plugin.cs" 8 | -------------------------------------------------------------------------------- /addons/VirtualJoystick/VirtualJoystick.cs: -------------------------------------------------------------------------------- 1 | using Fractural; 2 | using Godot; 3 | using System; 4 | 5 | namespace VirtualJoystickAddon 6 | { 7 | public class VirtualJoystick : GDScriptWrapper 8 | { 9 | public Vector2 Output => (Vector2)Source.Call("get_output"); 10 | 11 | public VirtualJoystick() { } 12 | public VirtualJoystick(Godot.Object source) : base(source) { } 13 | } 14 | } -------------------------------------------------------------------------------- /addons/VirtualJoystick/textures/joystick_base_outline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/addons/VirtualJoystick/textures/joystick_base_outline.png -------------------------------------------------------------------------------- /addons/VirtualJoystick/textures/joystick_base_outline.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/joystick_base_outline.png-8bce810e25ebae6c114da91476c57ba4.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/VirtualJoystick/textures/joystick_base_outline.png" 13 | dest_files=[ "res://.import/joystick_base_outline.png-8bce810e25ebae6c114da91476c57ba4.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/VirtualJoystick/textures/joystick_tip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/addons/VirtualJoystick/textures/joystick_tip.png -------------------------------------------------------------------------------- /addons/VirtualJoystick/textures/joystick_tip.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/joystick_tip.png-ac60c6cfef593f7dae0e1ae25a43d07c.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/VirtualJoystick/textures/joystick_tip.png" 13 | dest_files=[ "res://.import/joystick_tip.png-ac60c6cfef593f7dae0e1ae25a43d07c.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/VirtualJoystick/textures/joystick_tip_arrows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/addons/VirtualJoystick/textures/joystick_tip_arrows.png -------------------------------------------------------------------------------- /addons/VirtualJoystick/textures/joystick_tip_arrows.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/joystick_tip_arrows.png-6c3dca4e1ae282e1a063fa009c1ecb1c.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/VirtualJoystick/textures/joystick_tip_arrows.png" 13 | dest_files=[ "res://.import/joystick_tip_arrows.png-6c3dca4e1ae282e1a063fa009c1ecb1c.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/VirtualJoystick/virtual_joystick.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=2] 2 | 3 | [ext_resource path="res://addons/VirtualJoystick/textures/joystick_base_outline.png" type="Texture" id=1] 4 | [ext_resource path="res://addons/VirtualJoystick/textures/joystick_tip_arrows.png" type="Texture" id=2] 5 | [ext_resource path="res://addons/VirtualJoystick/virtual_joystick.gd" type="Script" id=3] 6 | 7 | [node name="Virtual joystick" type="Control"] 8 | modulate = Color( 1, 1, 1, 0.509804 ) 9 | anchor_top = 1.0 10 | anchor_bottom = 1.0 11 | margin_left = 50.0 12 | margin_top = -250.0 13 | margin_right = 250.0 14 | margin_bottom = -50.0 15 | script = ExtResource( 3 ) 16 | 17 | [node name="Base" type="TextureRect" parent="."] 18 | anchor_left = 0.5 19 | anchor_top = 0.5 20 | anchor_right = 0.5 21 | anchor_bottom = 0.5 22 | margin_left = -100.0 23 | margin_top = -100.0 24 | margin_right = 100.0 25 | margin_bottom = 100.0 26 | grow_horizontal = 2 27 | grow_vertical = 2 28 | rect_pivot_offset = Vector2( 100, 100 ) 29 | size_flags_horizontal = 4 30 | size_flags_vertical = 4 31 | texture = ExtResource( 1 ) 32 | stretch_mode = 6 33 | __meta__ = { 34 | "_edit_use_anchors_": false 35 | } 36 | 37 | [node name="Tip" type="TextureRect" parent="Base"] 38 | anchor_left = 0.5 39 | anchor_top = 0.5 40 | anchor_right = 0.5 41 | anchor_bottom = 0.5 42 | margin_left = -50.0 43 | margin_top = -50.0 44 | margin_right = 50.0 45 | margin_bottom = 50.0 46 | grow_horizontal = 2 47 | grow_vertical = 2 48 | rect_pivot_offset = Vector2( 50, 50 ) 49 | size_flags_horizontal = 4 50 | size_flags_vertical = 4 51 | texture = ExtResource( 2 ) 52 | stretch_mode = 6 53 | __meta__ = { 54 | "_edit_use_anchors_": false 55 | } 56 | -------------------------------------------------------------------------------- /addons/WAT/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 CodeDarigan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /addons/WAT/assertions/.idea/.idea.assertions.dir/.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /addons/WAT/assertions/.idea/.idea.assertions.dir/.idea/indexLayout.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /addons/WAT/assertions/.idea/.idea.assertions.dir/.idea/projectSettingsUpdater.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /addons/WAT/assertions/.idea/.idea.assertions.dir/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /addons/WAT/assertions/.idea/.idea.assertions.dir/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 17 | 18 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 1614470179608 34 | 39 | 40 | 41 | 42 | 43 | 45 | -------------------------------------------------------------------------------- /addons/WAT/assertions/assertion.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | 3 | var success: bool 4 | var expected: String = "NULL" 5 | var result: String 6 | var notes: String = "No Notes" 7 | var context 8 | 9 | static func _result(success: bool, expected: String, actual: String, context: String, notes: String = "No Notes"): 10 | return { 11 | "success": success, 12 | "expected": expected, 13 | "actual": actual, 14 | "context": context 15 | } 16 | 17 | static func type2str(property) -> String: 18 | match typeof(property): 19 | TYPE_NIL: 20 | return "null" 21 | TYPE_BOOL: 22 | return "bool" 23 | TYPE_INT: 24 | return "int" 25 | TYPE_REAL: 26 | return "float" 27 | TYPE_STRING: 28 | return "String" 29 | TYPE_VECTOR2: 30 | return "Vector2" 31 | TYPE_RECT2: 32 | return "Rect2" 33 | TYPE_VECTOR3: 34 | return "Vector3" 35 | TYPE_TRANSFORM2D: 36 | return "Transform2D" 37 | TYPE_PLANE: 38 | return "Plane" 39 | TYPE_QUAT: 40 | return "Quat" 41 | TYPE_AABB: 42 | return "AABB" 43 | TYPE_BASIS: 44 | return "Basis" 45 | TYPE_TRANSFORM: 46 | return "Transform" 47 | TYPE_COLOR: 48 | return "Color" 49 | TYPE_NODE_PATH: 50 | return "NodePath" 51 | TYPE_RID: 52 | return "RID" 53 | TYPE_OBJECT: 54 | return "Object" 55 | TYPE_DICTIONARY: 56 | return "Dictionary" 57 | TYPE_ARRAY: 58 | return "Array" 59 | TYPE_RAW_ARRAY: 60 | return "PoolByteArray" 61 | TYPE_INT_ARRAY: 62 | return "PoolIntArray" 63 | TYPE_REAL_ARRAY: 64 | return "PoolRealArray" 65 | TYPE_STRING_ARRAY: 66 | return "PoolStringArray" 67 | TYPE_VECTOR2_ARRAY: 68 | return "PoolVector2Array" 69 | TYPE_VECTOR3_ARRAY: 70 | return "PoolVector3Array" 71 | TYPE_COLOR_ARRAY: 72 | return "PoolColorArray" 73 | return "OutOfBounds" 74 | -------------------------------------------------------------------------------- /addons/WAT/assertions/boolean.gd: -------------------------------------------------------------------------------- 1 | extends "assertion.gd" 2 | 3 | static func is_true(value, context: String) -> Dictionary: 4 | var type = type2str(value) 5 | var passed: String = "|%s| %s == true" % [type, value] 6 | var failed: String = "|%s| %s != true" % [type, value] 7 | var success: bool = (value == true) 8 | var result = passed if success else failed 9 | return _result(success, passed, result, context) 10 | 11 | static func is_false(value, context: String) -> Dictionary: 12 | var type = type2str(value) 13 | var passed: String = "|%s| %s == false" % [type, value] 14 | var failed: String = "|%s| %s != false" % [type, value] 15 | var success = (value == false) 16 | var expected = passed 17 | var result = passed if success else failed 18 | return _result(success, expected, result, context) 19 | -------------------------------------------------------------------------------- /addons/WAT/assertions/collections.gd: -------------------------------------------------------------------------------- 1 | extends "assertion.gd" 2 | 3 | static func is_empty(value, context: String) -> Dictionary: 4 | var passed: String = "Array is empty" 5 | var failed: String = "Collection is not empty (%s)" % str(value) 6 | var success = value.empty() 7 | var expected = passed 8 | var result = passed if success else failed 9 | return _result(success, expected, result, context) 10 | -------------------------------------------------------------------------------- /addons/WAT/assertions/double.gd: -------------------------------------------------------------------------------- 1 | extends "assertion.gd" 2 | 3 | static func called_with_arguments(double, method: String, args: Array, context: String) -> Dictionary: 4 | var passed: String = "method: %s was called with arguments: %s" % [method, args] 5 | var failed: String = "method: %s was not called with arguments: %s" % [method, args] 6 | var alt_failed: String = "method: %s was not called at all" % method 7 | var expected = passed 8 | 9 | var success: bool 10 | var result: String 11 | if double.call_count(method) == 0: 12 | success = false 13 | result = alt_failed 14 | elif double.found_matching_call(method, args): 15 | success = true 16 | result = passed 17 | else: 18 | success = false 19 | result = failed 20 | return _result(success, expected, result, context) 21 | 22 | static func was_called(double, method: String, context: String) -> Dictionary: 23 | var passed: String = "%s was called" % method 24 | var failed: String = "%s was not called" % method 25 | var success = double.call_count(method) > 0 26 | var expected = passed 27 | var result = passed if success else failed 28 | return _result(success, expected, result, context) 29 | 30 | static func was_not_called(double, method: String, context: String) -> Dictionary: 31 | var passed: String = "%s was not called" % method 32 | var failed: String = "%s was called" % method 33 | var success = double.call_count(method) <= 0 34 | var expected = passed 35 | var result = passed if success else failed 36 | return _result(success, expected, result, context) 37 | -------------------------------------------------------------------------------- /addons/WAT/assertions/file.gd: -------------------------------------------------------------------------------- 1 | extends "assertion.gd" 2 | 3 | static func exists(path: String, context: String) -> Dictionary: 4 | var passed: String = "%s exists" % path 5 | var failed: String = "%s does not exist" % path 6 | var success = File.new().file_exists(path) 7 | var expected = "%s exists" % path 8 | var result = passed if success else failed 9 | return _result(success, expected, result, context) 10 | 11 | static func does_not_exist(path: String, context: String) -> Dictionary: 12 | var passed: String = "%s does not exist" % path 13 | var failed: String = "%s exists" % path 14 | var success = not File.new().file_exists(path) 15 | var expected = "%s does not exist" % path 16 | var result = passed if success else failed 17 | return _result(success, expected, result, context) 18 | -------------------------------------------------------------------------------- /addons/WAT/assertions/folder.gd: -------------------------------------------------------------------------------- 1 | extends "assertion.gd" 2 | 3 | static func exists(path: String, context: String) -> Dictionary: 4 | var passed: String = "%s exists" % path 5 | var failed: String = "%s does not exist" % path 6 | var success = not path.empty() and Directory.new().dir_exists(path) 7 | var expected = "%s exists" % path 8 | var result = passed if success else failed 9 | return _result(success, expected, result, context) 10 | 11 | static func does_not_exist(path: String, context: String) -> Dictionary: 12 | var passed: String = "%s does not exist" % path 13 | var failed: String = "%s exists" % path 14 | var success = path.empty() or not Directory.new().dir_exists(path) 15 | var expected = "%s does not exist" % path 16 | var result = passed if success else failed 17 | return _result(success, expected, result, context) 18 | -------------------------------------------------------------------------------- /addons/WAT/assertions/null.gd: -------------------------------------------------------------------------------- 1 | extends "assertion.gd" 2 | 3 | 4 | static func is_null(value, context: String) -> Dictionary: 5 | var type = type2str(value) 6 | var passed: String = "|%s| %s == null" % [type, value] 7 | var failed: String = "|%s| %s != null" % [type, value] 8 | var success = (value == null) 9 | var expected = passed 10 | var result = passed if success else failed 11 | return _result(success, expected, result, context) 12 | 13 | static func is_not_null(value, context: String) -> Dictionary: 14 | var type = type2str(value) 15 | var passed: String = "|%s| %s != null" % [type, value] 16 | var failed: String = "|%s| %s == null" % [type, value] 17 | var success = (value != null) 18 | var expected = passed 19 | var result = passed if success else failed 20 | return _result(success, expected, result, context) 21 | -------------------------------------------------------------------------------- /addons/WAT/assertions/property.gd: -------------------------------------------------------------------------------- 1 | extends "assertion.gd" 2 | 3 | # ONLY FOR CONTAINER CLASSES. NOT FOR OBJECTS 4 | static func has(value, container, context: String) -> Dictionary: 5 | var passed: String = "%s has %s" % [container, value] 6 | var failed: String = "%s has %s" % [container, value] 7 | var success = container.has(value) 8 | var expected = "%s has %s" % [container, value] 9 | var result = passed if success else failed 10 | return _result(success, expected, result, context) 11 | 12 | static func does_not_have(value, container, context: String) -> Dictionary: 13 | var passed: String = "%s does not have %s" % [container, value] 14 | var failed: String = "%s has %s" % [container, value] 15 | var success = not container.has(value) 16 | var expected = "%s does not have %s" % [container, value] 17 | var result = passed if success else failed 18 | return _result(success, expected, result, context) 19 | -------------------------------------------------------------------------------- /addons/WAT/assertions/range.gd: -------------------------------------------------------------------------------- 1 | extends "assertion.gd" 2 | 3 | static func is_in_range(value, low, high, context: String) -> Dictionary: 4 | var passed: String = "%s is in range(%s, %s)" % [value, low, high] 5 | var failed: String = "%s is not in range(%s, %s)" % [value, low, high] 6 | var success = low <= value and value < high 7 | var expected = passed 8 | var result = passed if success else failed 9 | return _result(success, expected, result, context) 10 | 11 | static func is_not_in_range(value, low, high, context: String) -> Dictionary: 12 | var passed: String = "%s is not in range(%s, %s)" % [value, low, high] 13 | var failed: String = "%s is in range(%s, %s)" % [value, low, high] 14 | var success = low > value or value >= high 15 | var expected = passed 16 | var result = passed if success else failed 17 | return _result(success, expected, result, context) 18 | -------------------------------------------------------------------------------- /addons/WAT/assertions/signal.gd: -------------------------------------------------------------------------------- 1 | extends "assertion.gd" 2 | 3 | static func was_emitted(emitter, event: String, context: String) -> Dictionary: 4 | var passed: String = "signal: %s was emitted from %s" % [event, emitter] 5 | var failed: String = "signal: %s was not emitted from %s" % [event, emitter] 6 | var success = emitter.get_meta("watcher").watching[event].emit_count > 0 7 | var expected = passed 8 | var result = passed if success else failed 9 | return _result(success, expected, result, context) 10 | 11 | static func was_not_emitted(emitter, _signal: String, context: String) -> Dictionary: 12 | var success = emitter.get_meta("watcher").watching[_signal].emit_count <= 0 13 | var passed = "Signal: %s was not emitted from %s" % [_signal, emitter] 14 | var failed = "Signal: %s was emitted from %s" % [_signal, emitter] 15 | var expected = passed 16 | var result = passed if success else failed 17 | return _result(success, expected, result, context) 18 | 19 | static func was_emitted_x_times(emitter, event: String, times: int, context: String) -> Dictionary: 20 | var passed: String = "signal: %s was emitted from %s %s" % [event, emitter, times as String] 21 | var failed: String = "signal: %s was not emitted from %s %s" % [event, emitter, times as String] 22 | var success = emitter.get_meta("watcher").watching[event].emit_count == times 23 | var expected = passed 24 | var result = passed if success else failed 25 | return _result(success, expected, result, context) 26 | 27 | static func was_emitted_with_args(emitter: Object, event: String, arguments: Array, context: String) -> Dictionary: 28 | var passed: String = "Signal: %s was emitted from %s with arguments: %s" % [event, emitter, arguments] 29 | var failed: String = "Signal: %s was not emitted from %s with arguments: %s" % [event, emitter, arguments] 30 | var alt_failure: String = "Signal: %s was not emitted from %s" % [event, emitter] 31 | var expected = passed 32 | 33 | var success: bool 34 | var result: String 35 | var data = emitter.get_meta("watcher").watching[event] 36 | if data.emit_count <= 0: 37 | success = false 38 | result = alt_failure 39 | 40 | elif _found_matching_call(arguments, data.calls): 41 | success = true 42 | result = passed 43 | 44 | else: 45 | success = false 46 | result = failed 47 | 48 | return _result(success, expected, result, context) 49 | 50 | static func _found_matching_call(args, calls) -> bool: 51 | for call in calls: 52 | if _match(args, call.args): 53 | return true 54 | return false 55 | 56 | static func _match(args, call_args) -> bool: 57 | for i in args.size(): 58 | if args[i] != call_args[i]: 59 | return false 60 | return true 61 | -------------------------------------------------------------------------------- /addons/WAT/assertions/string.gd: -------------------------------------------------------------------------------- 1 | extends "assertion.gd" 2 | 3 | static func begins_with(value: String, string: String, context: String) -> Dictionary: 4 | var passed: String = "%s begins with %s" % [string, value] 5 | var failed: String = "%s does not begins with %s" % [string, value] 6 | var success = string.begins_with(value) 7 | var expected = passed 8 | var result = passed if success else failed 9 | return _result(success, expected, result, context) 10 | 11 | static func contains(value: String, string: String, context: String) -> Dictionary: 12 | var passed: String = "%s contains %s" % [string, value] 13 | var failed: String = "%s does not contain %s" % [string, value] 14 | var success = value in string 15 | var expected = passed 16 | var result = passed if success else failed 17 | return _result(success, expected, result, context) 18 | 19 | static func does_not_begin_with(value: String, string: String, context: String) -> Dictionary: 20 | var passed: String = "%s does not begin with %s" % [string, value] 21 | var failed: String = "%s begins with %s" % [string, value] 22 | var success = not string.begins_with(value) 23 | var expected = passed 24 | var result = passed if success else failed 25 | return _result(success, expected, result, context) 26 | 27 | static func does_not_contain(value: String, string: String, context: String) -> Dictionary: 28 | var passed: String = "%s does not contain %s" % [string, value] 29 | var failed: String = "%s contains %s" % [string, value] 30 | var success = not value in string 31 | var expected = passed 32 | var result = passed if success else failed 33 | return _result(success, expected, result, context) 34 | 35 | static func does_not_end_with(value: String, string: String, context: String) -> Dictionary: 36 | var passed: String = "%s does not end with %s" % [string, value] 37 | var failed: String = "%s ends with %s" % [string, value] 38 | var success = not string.ends_with(value) 39 | var expected = passed 40 | var result = passed if success else failed 41 | return _result(success, expected, result, context) 42 | 43 | static func ends_with(value: String, string: String, context: String) -> Dictionary: 44 | var passed: String = "%s ends with %s" % [string, value] 45 | var failed: String = "%s does not end with %s" % [string, value] 46 | var success = string.ends_with(value) 47 | var expected = passed 48 | var result = passed if success else failed 49 | return _result(success, expected, result, context) 50 | 51 | static func is_empty(value: String, context: String) -> Dictionary: 52 | var passed: String = "String is empty" 53 | var failed: String = "String %s is not empty" % [value] 54 | var success = value.empty() 55 | var expected = passed 56 | var result = passed if success else failed 57 | return _result(success, expected, result, context) 58 | 59 | -------------------------------------------------------------------------------- /addons/WAT/assertions/utility.gd: -------------------------------------------------------------------------------- 1 | extends "assertion.gd" 2 | 3 | static func fail(context: String = "Test Not Implemented") -> Dictionary: 4 | # Intentionally Fails Test 5 | return _result(false, "N/A", "N/A", context) 6 | 7 | static func auto_pass(context: String = "Auto Pass") -> Dictionary: 8 | return _result(true, "N/A", "N/A", context) 9 | 10 | # Callv does not work on virtual classes (Array etc) 11 | static func that(obj, method: String, arguments: Array = [], context: String = "", passed: String = "", failed: String = "") -> Dictionary: 12 | var success = obj.callv(method, arguments) 13 | var expected = passed 14 | var result = passed if success else failed 15 | return _result(success, expected, result, context) 16 | 17 | # asserts.that(array, "empty", [], "Array is empty", "Empty", "Found %s items" % array.size()) 18 | -------------------------------------------------------------------------------- /addons/WAT/assets/debug_failed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/addons/WAT/assets/debug_failed.png -------------------------------------------------------------------------------- /addons/WAT/assets/debug_failed.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/debug_failed.png-0deb1d1782c19daaffc5af47aba4eafc.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/WAT/assets/debug_failed.png" 13 | dest_files=[ "res://.import/debug_failed.png-0deb1d1782c19daaffc5af47aba4eafc.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=true 25 | flags/anisotropic=false 26 | flags/srgb=1 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/WAT/assets/docs.svg: -------------------------------------------------------------------------------- 1 | 2 | 14 | 16 | 34 | 38 | 39 | -------------------------------------------------------------------------------- /addons/WAT/assets/docs.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/docs.svg-a649391eeb4006a0bb13c9007d30760f.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/WAT/assets/docs.svg" 13 | dest_files=[ "res://.import/docs.svg-a649391eeb4006a0bb13c9007d30760f.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/WAT/assets/failed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/addons/WAT/assets/failed.png -------------------------------------------------------------------------------- /addons/WAT/assets/failed.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/failed.png-18188bee7d18ad72bc7cbc6222c355b1.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/WAT/assets/failed.png" 13 | dest_files=[ "res://.import/failed.png-18188bee7d18ad72bc7cbc6222c355b1.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=true 25 | flags/anisotropic=true 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/WAT/assets/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/addons/WAT/assets/folder.png -------------------------------------------------------------------------------- /addons/WAT/assets/folder.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/folder.png-f6fa55e0795097315a7c899b7b15ee27.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/WAT/assets/folder.png" 13 | dest_files=[ "res://.import/folder.png-f6fa55e0795097315a7c899b7b15ee27.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/WAT/assets/function.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/addons/WAT/assets/function.png -------------------------------------------------------------------------------- /addons/WAT/assets/function.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/function.png-b5f751bc93a3ee5a29c4d32864c250fc.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/WAT/assets/function.png" 13 | dest_files=[ "res://.import/function.png-b5f751bc93a3ee5a29c4d32864c250fc.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/WAT/assets/issue.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/WAT/assets/issue.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/issue.svg-0e50d01edf27a6e481699d3b4373a6f9.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/WAT/assets/issue.svg" 13 | dest_files=[ "res://.import/issue.svg-0e50d01edf27a6e481699d3b4373a6f9.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/WAT/assets/kofi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/addons/WAT/assets/kofi.png -------------------------------------------------------------------------------- /addons/WAT/assets/kofi.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/kofi.png-25130db51d1b80d6426df72322b3f642.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/WAT/assets/kofi.png" 13 | dest_files=[ "res://.import/kofi.png-25130db51d1b80d6426df72322b3f642.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/WAT/assets/label.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/addons/WAT/assets/label.png -------------------------------------------------------------------------------- /addons/WAT/assets/label.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/label.png-6b04b72410cd91559f10027900e1c444.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/WAT/assets/label.png" 13 | dest_files=[ "res://.import/label.png-6b04b72410cd91559f10027900e1c444.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/WAT/assets/passed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/addons/WAT/assets/passed.png -------------------------------------------------------------------------------- /addons/WAT/assets/passed.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/passed.png-1606da9dc4505c2bac72d61e0f5f988c.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/WAT/assets/passed.png" 13 | dest_files=[ "res://.import/passed.png-1606da9dc4505c2bac72d61e0f5f988c.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/WAT/assets/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/addons/WAT/assets/play.png -------------------------------------------------------------------------------- /addons/WAT/assets/play.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/play.png-2962300029223d688c79411a6cda4a6d.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/WAT/assets/play.png" 13 | dest_files=[ "res://.import/play.png-2962300029223d688c79411a6cda4a6d.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=true 25 | flags/anisotropic=false 26 | flags/srgb=0 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/WAT/assets/play_debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/addons/WAT/assets/play_debug.png -------------------------------------------------------------------------------- /addons/WAT/assets/play_debug.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/play_debug.png-b9aaa3991495e16a0dfe9e46268ae0a2.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/WAT/assets/play_debug.png" 13 | dest_files=[ "res://.import/play_debug.png-b9aaa3991495e16a0dfe9e46268ae0a2.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/WAT/assets/play_failed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/addons/WAT/assets/play_failed.png -------------------------------------------------------------------------------- /addons/WAT/assets/play_failed.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/play_failed.png-206503f73ae232b267852de2008ff514.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/WAT/assets/play_failed.png" 13 | dest_files=[ "res://.import/play_failed.png-206503f73ae232b267852de2008ff514.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/WAT/assets/request_docs.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /addons/WAT/assets/request_docs.svg.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/request_docs.svg-66a60c4eba54402104d46cbe772a490e.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/WAT/assets/request_docs.svg" 13 | dest_files=[ "res://.import/request_docs.svg-66a60c4eba54402104d46cbe772a490e.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/WAT/assets/script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/addons/WAT/assets/script.png -------------------------------------------------------------------------------- /addons/WAT/assets/script.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/script.png-6590c0f9543eaeb2a6b5b8ca50aae436.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/WAT/assets/script.png" 13 | dest_files=[ "res://.import/script.png-6590c0f9543eaeb2a6b5b8ca50aae436.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/WAT/assets/timer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/addons/WAT/assets/timer.png -------------------------------------------------------------------------------- /addons/WAT/assets/timer.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/timer.png-16c233793d4e507805f02aeac7ec3915.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://addons/WAT/assets/timer.png" 13 | dest_files=[ "res://.import/timer.png-16c233793d4e507805f02aeac7ec3915.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /addons/WAT/cli.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://addons/WAT/ui/cli.gd" type="Script" id=1] 4 | 5 | [node name="Commandline" type="Node"] 6 | script = ExtResource( 1 ) 7 | -------------------------------------------------------------------------------- /addons/WAT/double/factory.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | const ScriptDirector = preload("script_director.gd") 4 | const SceneDirector = preload("scene_director.gd") 5 | var registry 6 | 7 | func clear() -> void: 8 | # Allow users to clear registry in the post hook if they want fresh values between runs 9 | registry.clear() 10 | for director in registry.test_directors.values() : 11 | director.free() 12 | registry.test_directors.clear() 13 | 14 | func script(path, inner: String = "", deps: Array = []) -> ScriptDirector: 15 | if path is GDScript: path = path.resource_path 16 | return ScriptDirector.new(registry, path, inner, deps) 17 | 18 | func scene(tscn) -> SceneDirector: 19 | # Must be String.tscn or PackedScene 20 | var scene: PackedScene = load(tscn) if tscn is String else tscn 21 | var instance: Node = scene.instance() 22 | var nodes: Dictionary = {} 23 | var frontier: Array = [] 24 | frontier.append(instance) 25 | while not frontier.empty(): 26 | var next: Node = frontier.pop_front() 27 | if next.name.begins_with("@@"): 28 | # Don't double engine-generated classes (usually begin with @@) 29 | continue 30 | frontier += next.get_children() 31 | var path: String = instance.get_path_to(next) 32 | if next.get_script() != null: 33 | nodes[path] = script(next.get_script().resource_path) 34 | elif ClassDB.class_exists(next.get_class()): 35 | nodes[path] = script(next.get_class()) 36 | _set_nodepath_variables(instance, nodes) 37 | var export_values = _get_exported_variable_values(instance) 38 | instance.queue_free() 39 | return SceneDirector.new(nodes, export_values) 40 | 41 | func _set_nodepath_variables(instance, nodes: Dictionary): 42 | # Nodepath Variables Are Exported 43 | # Which means they are typically not shared by instances 44 | # Therefore we got to do it manually 45 | for path in nodes: 46 | var source = instance.get_node(path) 47 | var copy = nodes[path] 48 | for prop in source.get_property_list(): 49 | if(prop.type == TYPE_NODE_PATH and prop.name != "_import_path"): 50 | var exported_value = source.get(prop.name) as NodePath 51 | copy.nodepaths[prop.name] = exported_value as NodePath 52 | 53 | # We need this script to be inside the tree to prevent some errors... 54 | # ..everything should be fine if the instance isn't a tool script I think.. 55 | # ..at least in editor 56 | func _get_exported_variable_values(instance) -> Dictionary: 57 | var exported: Dictionary = {} 58 | add_child(instance) 59 | for prop in instance.get_property_list(): 60 | if _is_exported_variable(prop.usage): 61 | exported[prop.name] = instance.get(prop.name) 62 | remove_child(instance) 63 | return exported 64 | 65 | func _is_exported_variable(usage: int) -> bool: 66 | return usage == PROPERTY_USAGE_DEFAULT + PROPERTY_USAGE_SCRIPT_VARIABLE 67 | -------------------------------------------------------------------------------- /addons/WAT/double/method.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | 3 | var name: String = "" 4 | var spying: bool = false 5 | var stubbed: bool = false 6 | var calls_super: bool = false 7 | var args: String = "" 8 | var args_with_defaults: String = "" 9 | var keyword: String = "" 10 | var calls: Array = [] 11 | var stubs: Array = [] 12 | var supers: Array = [] 13 | var callables: Array = [] 14 | var default 15 | var double 16 | 17 | func _init(name: String, keyword: String, args: String, defaults: String) -> void: 18 | self.name = name 19 | self.keyword = keyword 20 | self.args = args 21 | self.args_with_defaults = defaults 22 | 23 | func dummy() -> Reference: 24 | stubbed = true 25 | default = null 26 | return self 27 | 28 | func spy() -> Reference: 29 | push_warning("Deprecated. Spying on Methods is now Automatic. Please Remove") 30 | spying = true 31 | return self 32 | 33 | func stub(return_value, arguments: Array = []): 34 | stubbed = true 35 | if arguments.empty(): 36 | default = return_value 37 | else: 38 | stubs.append({args = arguments, "return_value": return_value}) 39 | return self 40 | 41 | func primary(args: Array): 42 | if stubbed: 43 | return get_stub(args) 44 | elif calls.size() > 0: 45 | for call in callables: 46 | return call.call_func(double, args) 47 | else: 48 | return null 49 | 50 | func add_call(args: Array = []) -> void: 51 | calls.append(args) 52 | 53 | func subcall(function: Object, deprecated_var = null) -> void: 54 | if deprecated_var != null: 55 | push_warning("Users no longer need to pass in the return boolean") 56 | callables.append(function) 57 | 58 | func get_stub(args: Array = []): 59 | for stub in stubs: 60 | if _pattern_matched(stub.args, args): 61 | return stub.return_value 62 | return default 63 | 64 | func executes(args: Array) -> bool: 65 | for s in supers: 66 | if _pattern_matched(s, args): 67 | return true 68 | for s in stubs: 69 | if _pattern_matched(s.args, args): 70 | return false 71 | if supers.has([]): 72 | return true 73 | return false 74 | 75 | func call_super(args: Array = []) -> void: 76 | supers.append(args) 77 | calls_super = true 78 | 79 | func found_matching_call(expected_args: Array = []) -> bool: 80 | for call in calls: 81 | if _pattern_matched(expected_args, call): 82 | return true 83 | return false 84 | 85 | func _pattern_matched(pattern: Array = [], args: Array = []) -> bool: 86 | var indices: Array = [] 87 | if pattern.size() != args.size(): 88 | return false 89 | for index in pattern.size(): 90 | if pattern[index] is Object and pattern[index].get_class() == "Any": 91 | continue 92 | indices.append(index) 93 | for i in indices: 94 | # We check based on type first otherwise some errors occur (ie object can't be compared to int) 95 | if typeof(pattern[i]) != typeof(args[i]) or pattern[i] != args[i]: 96 | return false 97 | return true 98 | -------------------------------------------------------------------------------- /addons/WAT/double/methods.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | 3 | var base: Array = [] 4 | 5 | func set_base_methods(director: Reference) -> void: 6 | pass 7 | 8 | func call_count(): 9 | pass 10 | -------------------------------------------------------------------------------- /addons/WAT/double/registry.gd: -------------------------------------------------------------------------------- 1 | extends Object 2 | 3 | var test_directors: Dictionary = {} 4 | 5 | func register(director) -> void: 6 | # This probably doesn't need to exist as a singleton? 7 | # We could likely store a cache within each test 8 | # This way we can isolate it from other caches 9 | # (We could even probably store it directly within the factory) 10 | if director.get_instance_id() in test_directors: 11 | push_warning("Director Object is already registered") 12 | return 13 | test_directors[director.get_instance_id()] = director 14 | 15 | func method(instance_id: int, method: String) -> Object: 16 | return test_directors[instance_id].methods[method] 17 | 18 | func clear() -> void: 19 | var directors = test_directors.values() 20 | while not directors.empty(): 21 | var director = directors.pop_back() 22 | director.clear() 23 | 24 | func _notification(what): 25 | if what == NOTIFICATION_PREDELETE: 26 | clear() 27 | -------------------------------------------------------------------------------- /addons/WAT/double/scene_director.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | tool 3 | # We don't seem to need tool here yet but I'm keeping this comment JIC 4 | 5 | var nodes: Dictionary = {} 6 | var _created: bool = false 7 | var cache: Array = [] 8 | var _export_vars = {} 9 | 10 | func _init(nodes: Dictionary = {}, export_vars = {}) -> void: 11 | self.nodes = nodes 12 | _export_vars = export_vars 13 | 14 | func get_node(path: String) -> Node: 15 | return nodes[path] 16 | 17 | func double() -> Node: 18 | if _created: 19 | push_error("WAT: You can only create one instance of a double" 20 | + "Create a new doubler Object for new Test Doubles") 21 | return nodes["."] 22 | _created = true 23 | var root: Node = nodes["."].double() 24 | for nodepath in nodes: 25 | var path: PoolStringArray = nodepath.split("/") 26 | if nodepath == ".": 27 | # Skip if root node since already defined 28 | continue 29 | elif path.size() == 1: 30 | _add_child(path, nodepath, root) 31 | elif path.size() > 1: 32 | _add_grandchild(path, nodepath, root) 33 | for prop_name in _export_vars: 34 | root.set(prop_name, _export_vars[prop_name]) 35 | return root 36 | 37 | func _add_child(path: PoolStringArray, nodepath: String, root: Node) -> void: 38 | var node: Node = nodes[nodepath].double() 39 | node.name = path[0] 40 | root.add_child(node) 41 | 42 | func _add_grandchild(path: PoolStringArray, nodepath: String, root: Node) -> void: 43 | var node: Node = nodes[nodepath].double() 44 | var p = Array(path) 45 | node.name = p.pop_back() 46 | var parent = "" 47 | for element in p: 48 | parent += "%s/" % element 49 | parent = parent.rstrip("/") 50 | var grandparent = root.get_node(parent) 51 | grandparent.add_child(node) 52 | 53 | func clear() -> void: 54 | nodes = {} 55 | -------------------------------------------------------------------------------- /addons/WAT/double/script_writer.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | 3 | 4 | func write(double) -> String: 5 | var source: String = "" 6 | source += _extension_to_string(double) 7 | source += "\nconst WATRegistry = []\n" 8 | if double.base_methods.has("_init"): 9 | source += _constructor_to_string(double.base_methods["_init"].arguments) 10 | 11 | for name in double.methods: 12 | var m = double.methods[name] 13 | source += _method_to_string(double.get_instance_id(), m) 14 | for klass in double.klasses: 15 | source += _inner_class(klass) 16 | source = source.replace(",)", ")") 17 | return source 18 | 19 | func _extension_to_string(double) -> String: 20 | if double.is_built_in: 21 | return 'extends %s' % double.klass 22 | if double.inner_klass != "": 23 | return 'extends "%s".%s\n' % [double.klass, double.inner_klass] 24 | return 'extends "%s"\n' % double.klass 25 | 26 | func _constructor_to_string(parameters: String) -> String: 27 | var constructor: String = "" 28 | constructor += "\nfunc _init(%s).(%s):" % [parameters, parameters] 29 | constructor += "\n\tpass\n" 30 | return constructor 31 | 32 | func _method_to_string(id: int, method: Object) -> String: 33 | var text: String 34 | text += "{keyword}func {name}({args_with_defaults}):" 35 | text += "\n\tvar args = [{args}]" 36 | text += "\n\tvar method = WATRegistry[0].method({id}, '{name}')" 37 | text += "\n\tmethod.add_call(args)" 38 | text += "\n\tif method.executes(args):" 39 | text += "\n\t\treturn .{name}({args})" # We may want to add a retval check here 40 | text += "\n\treturn method.primary(args)\n\n" 41 | text = text.format({"id": id, "keyword": method.keyword, 42 | "name": method.name, "args_with_defaults": method.args_with_defaults, 43 | "args": method.args}) 44 | return text 45 | 46 | func _inner_class(klass: Dictionary) -> String: 47 | return "\nclass %s extends '%s'.%s:\n\tconst PLACEHOLDER = 0" % [klass.name, klass.director.klass, klass.name] 48 | -------------------------------------------------------------------------------- /addons/WAT/filesystem/directory.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | 3 | var is_root: bool = false 4 | var name: String setget ,_get_sanitized_name 5 | var path: String setget ,_get_path 6 | var relative_subdirs: Array 7 | var nested_subdirs: Array 8 | var tests: Array = [] 9 | 10 | func _get_sanitized_name() -> String: 11 | # Required for interface compability 12 | return path 13 | 14 | func _get_path() -> String: 15 | # res:/// should be res://f 16 | return path.replace("///", "//") # 17 | 18 | func get_tests() -> Array: 19 | var requested: Array = [] 20 | if is_root: 21 | for subdir in nested_subdirs: 22 | requested += subdir.get_tests() 23 | for script in tests: 24 | requested += script.get_tests() 25 | return requested 26 | 27 | func is_empty() -> bool: 28 | return tests.empty() 29 | -------------------------------------------------------------------------------- /addons/WAT/filesystem/failed.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | 3 | var paths: Array = [] 4 | var _tests: Array = [] 5 | 6 | func update(results: Array) -> void: 7 | paths.clear() 8 | for result in results: 9 | if not result["success"]: 10 | paths.append(result["path"]) 11 | 12 | func set_tests(root: Reference) -> void: 13 | _tests.clear() 14 | for test in root.get_tests(): 15 | if paths.has(test["path"]): 16 | _tests.append(test) 17 | 18 | func get_tests() -> Array: 19 | return _tests 20 | -------------------------------------------------------------------------------- /addons/WAT/filesystem/method.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | 3 | var path: String 4 | var dir: String setget ,_get_path 5 | var name: String setget ,_get_sanitized_name 6 | 7 | func _init(method_path: String = "", method_name: String = ""): 8 | path = method_path 9 | name = method_name 10 | 11 | # Method Name != test name 12 | func get_tests() -> Array: 13 | return [{"dir": dir, "name": name, "path": self.path, "methods": [name], "time": 0.0}] 14 | 15 | func _get_sanitized_name() -> String: 16 | var n: String = name.replace("test_", "").replace("_", " ") 17 | return n 18 | 19 | func _get_path() -> String: 20 | # res:/// should be res:// 21 | return path.replace("///", "//") # 22 | -------------------------------------------------------------------------------- /addons/WAT/filesystem/script.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | 3 | var name: String setget ,_get_sanitized_name 4 | var dir: String 5 | var path: String setget ,_get_path 6 | var methods: Array # TestMethods 7 | var names: Array # MethodNames 8 | var time: float = 0.0 # YieldTime 9 | var parse: int # stores error code (int) of last load() 10 | 11 | # Constructor for script.gd reference. 12 | # script_path: Resource path of the script. 13 | # load_result: Error code when resource path is reloaded. 14 | func _init(script_path: String = "", load_result: int = OK): 15 | path = script_path 16 | parse = load_result 17 | 18 | func _get_sanitized_name() -> String: 19 | var n: String = path.substr(path.find_last("/") + 1) 20 | n = n.replace(".gd", "").replace(".gdc", "").replace(".cs", "") 21 | n = n.replace(".test", "").replace("test", "").replace("_", " ") 22 | n[0] = n[0].to_upper() 23 | return n 24 | 25 | func _get_path() -> String: 26 | # res:/// should be res:// 27 | return path.replace("///", "//") # 28 | 29 | func get_tests() -> Array: 30 | return [{"dir": dir, "name": self.name, "path": self.path, "methods": names, "time": time, "parse": parse}] 31 | -------------------------------------------------------------------------------- /addons/WAT/filesystem/tagged.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | # Tag / Resource Path 3 | var tagged: Dictionary = {} 4 | var _settings: GDScript 5 | var _tests: Array = [] 6 | 7 | func _init(settings: GDScript) -> void: 8 | _settings = settings 9 | update() 10 | 11 | func tag(tag: String, path: String) -> void: 12 | update() 13 | if not tagged[tag].has(path): 14 | tagged[tag].append(path) 15 | 16 | func untag(tag: String, path: String) -> void: 17 | update() 18 | if tagged[tagged].has(path): 19 | tagged[tag].erase(path) 20 | 21 | func is_tagged(tag: String, path: String) -> bool: 22 | update() 23 | return tagged[tag].has(path) 24 | 25 | func swap(old: String, new: String) -> void: 26 | for tag in tagged: 27 | if tagged[tag].has(old): 28 | tagged[tag].erase(old) 29 | tagged[tag].append(new) 30 | 31 | func update() -> void: 32 | for tag in _settings.tags(): 33 | if not tagged.has(tag): 34 | tagged[tag] = [] 35 | 36 | func set_tests(tag: String, root: Reference) -> void: 37 | _tests.clear() 38 | for test in root.get_tests(): 39 | if tagged[tag].has(test["path"]): 40 | _tests.append(test) 41 | 42 | func get_tests() -> Array: 43 | return _tests 44 | 45 | 46 | -------------------------------------------------------------------------------- /addons/WAT/filesystem/tracker.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Reference 3 | 4 | ### This only works in the Editor Context. There are no similair methods.. 5 | ### ..available for the Scene Context. 6 | 7 | const Settings: GDScript = preload("res://addons/WAT/settings.gd") 8 | var _file_system 9 | 10 | func _init(filesystem) -> void: 11 | _file_system = filesystem 12 | 13 | func start_tracking_files(plugin: EditorPlugin) -> void: 14 | var dock: FileSystemDock = plugin.get_editor_interface().get_file_system_dock() 15 | for event in ["file_removed", "files_moved", "folder_moved", "folder_removed"]: 16 | var callback: String = "_on_%s" % event 17 | if not dock.is_connected(event, self, callback): 18 | dock.connect(event, self, callback) 19 | if not plugin.is_connected("resource_saved", self, "_on_resource_saved"): 20 | plugin.connect("resource_saved", self, "_on_resource_saved") 21 | 22 | func _stop_tracking_files(plugin: EditorPlugin) -> void: 23 | var dock: FileSystemDock = plugin.get_editor_interface().get_file_system_dock() 24 | for event in ["file_removed", "files_moved", "folder_moved", "folder_removed"]: 25 | var callback: String = "_on_%s" % event 26 | if dock.is_connected(event, self, callback): 27 | dock.disconnect(event, self, callback) 28 | if plugin.is_connected("resource_saved", self, "_on_resource_saved"): 29 | plugin.disconnect("resource_saved", self, "_on_resource_saved") 30 | 31 | func _on_filesystem_changed(has_changed: bool) -> void: 32 | if has_changed: 33 | _file_system.changed = true 34 | 35 | func _on_file_removed(file: String) -> void: 36 | _on_filesystem_changed(file.begins_with(Settings.test_directory())) 37 | 38 | func _on_files_moved(old: String, new: String) -> void: 39 | if old.begins_with(Settings.test_directory()) and new.begins_with(Settings.test_directory()): 40 | _file_system.tagged.swap(old, new) 41 | _on_filesystem_changed( 42 | old.begins_with(Settings.test_directory()) 43 | or new.begins_with(Settings.test_directory())) 44 | 45 | 46 | func _on_folder_moved(old: String, new: String) -> void: 47 | _on_filesystem_changed( 48 | old.begins_with(Settings.test_directory()) 49 | or new.begins_with(Settings.test_directory())) 50 | 51 | func _on_folder_removed(old: String) -> void: 52 | _on_filesystem_changed(old.begins_with(Settings.test_directory())) 53 | 54 | func _on_resource_saved(resource: Resource) -> void: 55 | _on_filesystem_changed( 56 | resource.resource_path.begins_with(Settings.test_directory())) 57 | -------------------------------------------------------------------------------- /addons/WAT/filesystem/yield_calculator.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | 3 | const COMMENT: String = "#" 4 | const PRE_HOOK: String = "func pre()" 5 | const POST_HOOK: String = "func post()" 6 | const FUNCTION: String = "func" 7 | const OPENING_STRING_QUOTE: String = '"' 8 | 9 | static func calculate_yield_time(gdscript: Script, test_method_count: int) -> float: 10 | var time: float = 0.0 11 | var floats: PoolRealArray = PoolRealArray() 12 | var in_hook: bool = false 13 | for line in gdscript.source_code.split("\n"): 14 | if line.begins_with(PRE_HOOK) or line.begins_with(POST_HOOK): 15 | # Yielding pre or post requires counting for each test method 16 | in_hook = true 17 | elif line.begins_with(FUNCTION): 18 | in_hook = false 19 | if "YIELD" in line and not line.begins_with(COMMENT) and not line.begins_with(OPENING_STRING_QUOTE): 20 | var f: PoolRealArray = PoolRealArray() 21 | f += line.split_floats("(") 22 | f += line.split_floats(",") 23 | if in_hook: 24 | f = duplicate(f, test_method_count) 25 | floats += f 26 | for real in floats: 27 | time += real 28 | return time 29 | 30 | static func duplicate(source: PoolRealArray, count: int) -> PoolRealArray: 31 | var floats: PoolRealArray = PoolRealArray() 32 | for real in source: 33 | for i in count: 34 | floats.append(real) 35 | return floats 36 | -------------------------------------------------------------------------------- /addons/WAT/io/junit_xml.gd: -------------------------------------------------------------------------------- 1 | extends Script 2 | 3 | static func write(results, settings: Reference, time: float = 0.0) -> void: 4 | var path: String 5 | if not settings.results_directory(): 6 | push_warning("WAT: Cannot find results directory. Defaulting to root to write Junit XML") 7 | path = "res://" 8 | else: 9 | path = settings.results_directory() 10 | if not Directory.new().dir_exists(path): 11 | Directory.new().make_dir_recursive(path) 12 | var tests: int = results.size() 13 | var failures: int = 0 14 | for i in results: 15 | if not i.success: 16 | failures += 1 17 | var output: String = "" 18 | output += '' 19 | output += '\n' % [failures, tests, time] 20 | for result in results: 21 | output += '\n\t' % [result.context, result.total - result.passed, result.total, result.time_taken] 22 | if result.methods.empty(): 23 | output += '\n\t\t' 24 | output += '' % result.path 25 | output += "" 26 | for case in result.methods: 27 | output += '\n\t\t' % [case.context, case.time] 28 | for assertion in case.assertions: 29 | if not assertion.success: 30 | output += '\n\t\t\t' % [assertion.expected, assertion.actual] 31 | # I think these are unnecessary. Will revisit on CLI creation. 32 | output += '' 33 | output += "\n\t\n" 34 | output += '\n' 35 | var XML = File.new() 36 | var xml_file: String = "%s/results.xml" % path 37 | var err = XML.open(xml_file, File.WRITE) 38 | if err: 39 | push_warning("Error saving result xml to %s : %s" % [xml_file, err as String]) 40 | XML.store_string(output) 41 | XML.close() 42 | -------------------------------------------------------------------------------- /addons/WAT/io/metadata.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | 3 | static func load_metadata(filesystem: Reference) -> void: 4 | var path: String 5 | if not filesystem.Settings.metadata_directory(): 6 | path = filesystem.Settings.test_directory() 7 | else: 8 | path = filesystem.Settings.metadata_directory() 9 | path += "/metadata.json" 10 | if not Directory.new().file_exists(path): 11 | return 12 | 13 | var file = File.new() 14 | file.open(path, File.READ) 15 | var content: Dictionary = JSON.parse(file.get_as_text()).result 16 | file.close() 17 | 18 | for key in content: 19 | if key == "failed": 20 | filesystem.failed.paths = content[key] 21 | else: 22 | filesystem.tagged.tagged[key] = content[key] 23 | 24 | static func save_metadata(filesystem: Reference) -> void: 25 | var path: String 26 | if not filesystem.Settings.metadata_directory(): 27 | push_warning("WAT: Cannot find metadata directory. Defaulting to test directory to save metadata") 28 | path = filesystem.Settings.test_directory() 29 | else: 30 | path = filesystem.Settings.metadata_directory() 31 | if not Directory.new().dir_exists(path): 32 | Directory.new().make_dir_recursive(path) 33 | 34 | path += "/metadata.json" 35 | var data = {"failed": filesystem.failed.paths} 36 | for tag in filesystem.tagged.tagged: 37 | var paths: Array = filesystem.tagged.tagged[tag] 38 | if not paths.empty(): 39 | data[tag] = paths 40 | 41 | var file = File.new() 42 | file.open(path, File.WRITE) 43 | file.store_string(JSON.print(data, "\t", true)) 44 | file.close() 45 | -------------------------------------------------------------------------------- /addons/WAT/mono/Attributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Godot; 3 | 4 | namespace WAT 5 | { 6 | public partial class Test: Node 7 | { 8 | [AttributeUsage(AttributeTargets.Class)] 9 | protected class TitleAttribute : Attribute 10 | { 11 | public readonly string Title; 12 | 13 | public TitleAttribute(string title) 14 | { 15 | Title = title; 16 | } 17 | } 18 | 19 | [AttributeUsage(AttributeTargets.Class)] 20 | protected class HookAttribute : Attribute 21 | { 22 | public readonly string Method; 23 | protected HookAttribute(string method) => Method = method; 24 | } 25 | 26 | [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] 27 | protected class TestAttribute : Attribute 28 | { 29 | public readonly object[] Arguments; 30 | public TestAttribute(params object[] args) { Arguments = args; } 31 | } 32 | 33 | protected class StartAttribute : HookAttribute { public StartAttribute(string method) : base(method) { } } 34 | protected class PreAttribute : HookAttribute { public PreAttribute(string method) : base(method) { } } 35 | protected class PostAttribute : HookAttribute { public PostAttribute(string method) : base(method) { } } 36 | protected class EndAttribute : HookAttribute { public EndAttribute(string method) : base(method) { } } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /addons/WAT/mono/BuildScene.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | 4 | public class BuildScene : Node 5 | { 6 | public override void _Ready() 7 | { 8 | GetTree().Quit(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /addons/WAT/mono/BuildScene.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=2 format=2] 2 | 3 | [ext_resource path="res://addons/WAT/mono/BuildScene.cs" type="Script" id=1] 4 | 5 | [node name="BuildScene" type="Node"] 6 | script = ExtResource( 1 ) 7 | -------------------------------------------------------------------------------- /addons/WAT/mono/GDScriptWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Godot; 6 | using Array = Godot.Collections.Array; 7 | using Object = Godot.Object; 8 | // ReSharper disable InconsistentNaming 9 | 10 | 11 | namespace WAT 12 | { 13 | 14 | public partial class Test: Node 15 | { 16 | [Signal] public delegate void test_method_started(); 17 | [Signal] public delegate void asserted(); 18 | [Signal] public delegate void test_method_finished(); 19 | [Signal] public delegate void test_script_finished(); 20 | 21 | private const bool IS_WAT_TEST = true; 22 | 23 | private void OnAssertion(Godot.Collections.Dictionary assertion) 24 | { 25 | EmitSignal(nameof(asserted), assertion); 26 | } 27 | 28 | public String title() { return Title(); } 29 | 30 | public Array get_test_methods() 31 | { 32 | return new Array 33 | (GetType().GetMethods(). 34 | Where(m => m.IsDefined(typeof(TestAttribute))). 35 | Select(m => (string) m.Name).ToList()); 36 | } 37 | 38 | public Test setup(string directory, string filepath, IEnumerable methods) 39 | { 40 | _methods = GenerateTestMethods(methods); 41 | _case = (Object) GD.Load("res://addons/WAT/test/case.gd").New(directory, filepath, Title(), this); 42 | return this; 43 | } 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /addons/WAT/mono/assertions/Assertion.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Dynamic; 3 | using Godot; 4 | using Godot.Collections; 5 | 6 | namespace WAT 7 | { 8 | public class Assertion 9 | { 10 | 11 | protected static Dictionary Result(bool success, string expected, string actual, string context, string notes = "") 12 | { 13 | return new Dictionary 14 | { 15 | {"success", success}, 16 | {"expected", expected}, 17 | {"actual", actual}, 18 | {"context", context} 19 | }; 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /addons/WAT/mono/assertions/Boolean.cs: -------------------------------------------------------------------------------- 1 | using Godot.Collections; 2 | 3 | namespace WAT 4 | { 5 | public class Boolean: Assertion 6 | { 7 | public static Dictionary IsTrue(bool value, string context) 8 | { 9 | string passed = $"|boolean| {value.ToString()} == true"; 10 | string failed = $"|boolean| {value.ToString()} == false"; 11 | string result = value ? passed : failed; 12 | return Result(value, passed, result, context); 13 | } 14 | 15 | public static Dictionary IsFalse(bool value, string context) 16 | { 17 | string passed = $"|boolean| {value.ToString()} == false"; 18 | string failed = $"|boolean| {value.ToString()} == true"; 19 | bool success = !value; 20 | string result = success ? passed : failed; 21 | return Result(success, passed, result, context); 22 | } 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /addons/WAT/mono/assertions/File.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Godot; 3 | using Godot.Collections; 4 | 5 | namespace WAT 6 | { 7 | public class File: Assertion 8 | { 9 | public static Dictionary Exists(string path, string context) 10 | { 11 | string passed = $"{path} exists"; 12 | string failed = $"{path} does not exist"; 13 | bool success = new Godot.File().FileExists(path); 14 | string result = success ? passed : failed; 15 | return Result(success, passed, result, context); 16 | } 17 | 18 | public static Dictionary DoesNotExist(string path, string context) 19 | { 20 | string passed = $"{path} does not exist"; 21 | string failed = $"{path} exists"; 22 | bool success = !new Godot.File().FileExists(path); 23 | string result = success ? passed : failed; 24 | return Result(success, passed, result, context); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /addons/WAT/mono/assertions/Is.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | using Godot.Collections; 4 | 5 | namespace WAT { 6 | 7 | public class Is : Assertion 8 | { 9 | public static Dictionary IsType(object value, string context) 10 | { 11 | string passed = $"{value} is builtin {typeof(T)}"; 12 | string failed = $"{value} is not builtin {typeof(T)}"; 13 | bool success = value is T; 14 | string result = success ? passed : failed; 15 | return Result(success, passed, result, context); 16 | } 17 | 18 | public static Dictionary IsNotType(object value, string context) 19 | { 20 | string passed = $"{value} is builtin {typeof(T)}"; 21 | string failed = $"{value} is not builtin {typeof(T)}"; 22 | bool success = !(value is T); 23 | string result = success ? passed : failed; 24 | return Result(success, passed, result, context); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /addons/WAT/mono/assertions/Null.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | using Godot.Collections; 4 | 5 | 6 | namespace WAT 7 | { 8 | public class Null : Assertion 9 | { 10 | // Godot Objects that are freed don't become null immediatly 11 | // so they may still be valid instances 12 | public static Dictionary IsNull(object obj, string context) 13 | { 14 | const string passed = "object is null"; 15 | const string failed = "object is not null"; 16 | bool success = obj is null; 17 | if (!success) 18 | { 19 | //failed = $"{obj} is not null"; 20 | } 21 | 22 | string result = success ? passed : failed; 23 | return Result(success, passed, result, context); 24 | } 25 | 26 | public static Dictionary IsNotNull(object obj, string context) 27 | { 28 | string passed = "object is not null"; 29 | const string failed = "object is null"; 30 | bool success = !(obj is null); 31 | if (success) 32 | { 33 | passed = $"{obj} is not null"; 34 | } 35 | 36 | string result = success ? passed : failed; 37 | return Result(success, passed, result, context); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /addons/WAT/mono/assertions/Property.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using Godot.Collections; 3 | 4 | namespace WAT 5 | { 6 | public class Property: Assertion 7 | { 8 | public static Dictionary Contains(object value, T container, string context) 9 | { 10 | string passed = $"{container.GetType()} contains |{value.GetType()}|{value}"; 11 | string failed = $"{container.GetType()} does not contain |{value.GetType()}|{value}"; 12 | MethodInfo method = container.GetType().GetMethod("Contains"); 13 | bool success = false; 14 | if (method != null) 15 | { 16 | success = (bool) method.Invoke(container, new object[] {value}); 17 | } 18 | 19 | string result = success ? passed : failed; 20 | return Result(success, passed, result, context); 21 | } 22 | 23 | public static Dictionary DoesNotContain(object value, T container, string context) 24 | { 25 | string passed = $"{container.GetType()} does not contain |{value.GetType()}|{value}"; 26 | string failed = $"{container.GetType()} contains |{value.GetType()}|{value}"; 27 | MethodInfo method = container.GetType().GetMethod("Contains"); 28 | bool success = false; 29 | if (method != null) 30 | { 31 | success = !(bool) method.Invoke(container, new object[] {value}); 32 | } 33 | 34 | string result = success ? passed : failed; 35 | return Result(success, passed, result, context); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /addons/WAT/mono/assertions/Range.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Godot.Collections; 3 | 4 | namespace WAT 5 | { 6 | public class Range: Assertion 7 | { 8 | public static Dictionary IsInRange(double val, double low, double high, string context) 9 | { 10 | string passed = $"{val} is in range {low}-{high}"; 11 | string failed = $"{val} is not in range {low}-{high}"; 12 | bool success = val >= low && val < high; 13 | string result = success ? passed : failed; 14 | return Result(success, passed, result, context); 15 | } 16 | 17 | public static Dictionary IsNotInRange(double val, double low, double high, string context) 18 | { 19 | string passed = $"{val} is not in range {low}-{high}"; 20 | string failed = $"{val} is in range {low}-{high}"; 21 | bool success = val < low || val >= high; 22 | string result = success ? passed : failed; 23 | return Result(success, passed, result, context); 24 | } 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /addons/WAT/mono/assertions/StringX.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using Godot.Collections; 3 | 4 | namespace WAT 5 | { 6 | public class StringX: Assertion 7 | { 8 | public static Dictionary BeginsWith(string value, string str, string context) 9 | { 10 | string passed = $"{str} begins with {value}"; 11 | string failed = $"{str} does not begin with {value}"; 12 | bool success = str.BeginsWith(value); 13 | string result = success ? passed : failed; 14 | return Result(success, passed, result, context); 15 | } 16 | 17 | public static Dictionary DoesNotBeginWith(string value, string str, string context) 18 | { 19 | string passed = $"{str} does not begin with {value}"; 20 | string failed = $"{str} begins with {value}"; 21 | bool success = !str.BeginsWith(value); 22 | string result = success ? passed : failed; 23 | return Result(success, passed, result, context); 24 | } 25 | 26 | public static Dictionary Contains(string value, string str, string context) 27 | { 28 | string passed = $"{str} contains {value}"; 29 | string failed = $"{str} does not contain {value}"; 30 | bool success = str.Contains(value); 31 | string result = success ? passed : failed; 32 | return Result(success, passed, result, context); 33 | } 34 | 35 | public static Dictionary DoesNotContain(string value, string str, string context) 36 | { 37 | string passed = $"{str} does not contain {value}"; 38 | string failed = $"{str} contains {value}"; 39 | bool success = !str.Contains(value); 40 | string result = success ? passed : failed; 41 | return Result(success, passed, result, context); 42 | } 43 | 44 | public static Dictionary EndsWith(string value, string str, string context) 45 | { 46 | string passed = $"{str} ends with {value}"; 47 | string failed = $"{str} does not end with {value}"; 48 | bool success = str.EndsWith(value); 49 | string result = success ? passed : failed; 50 | return Result(success, passed, result, context); 51 | } 52 | 53 | public static Dictionary DoesNotEndWith(string value, string str, string context) 54 | { 55 | string passed = $"{str} does not end with {value}"; 56 | string failed = $"{str} end with {value}"; 57 | bool success = !str.EndsWith(value); 58 | string result = success ? passed : failed; 59 | return Result(success, passed, result, context); 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /addons/WAT/mono/assertions/Utility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Godot; 4 | using Godot.Collections; 5 | using Object = Godot.Object; 6 | 7 | namespace WAT 8 | { 9 | public class Utility: Assertion 10 | { 11 | public static Dictionary Fail(string context) 12 | { 13 | return Result(false, "N/A", "N/A", context); 14 | } 15 | 16 | public static Dictionary AutoPass(string context) 17 | { 18 | return Result(true, "N/A", "N/A", context); 19 | } 20 | 21 | public static Dictionary Throws(Action function, string context) 22 | { 23 | try 24 | { 25 | function(); 26 | const string fail = "No exception was thrown"; 27 | return Result(false, "Exception was thrown", fail, context); 28 | } 29 | catch (Exception e) 30 | { 31 | string pass = $"Threw Exception {e} with Message: {e.Message}"; 32 | return Result(true, "Exception was thrown", pass, context); 33 | } 34 | } 35 | 36 | public static Dictionary DoesNotThrow(Action function, string context) 37 | { 38 | try 39 | { 40 | function(); 41 | string pass = $"No exception was thrown"; 42 | return Result(true, "No Exception was thrown", pass, context); 43 | } 44 | catch (Exception e) 45 | { 46 | string fail = $"Threw {e} with Message: {e.Message}"; 47 | return Result(true, "No Exception was thrown", fail, context); 48 | } 49 | } 50 | 51 | public static Dictionary Throws(Action function, string context) 52 | { 53 | string expected = $"{typeof(T)} was thrown"; 54 | try 55 | { 56 | function(); 57 | const string fail = "No Exception was thrown"; 58 | return Result(false, expected, fail, context); 59 | } 60 | catch (Exception e) 61 | { 62 | if (e is T) 63 | { 64 | string pass = $"Threw {e} with Message: {e.Message}"; 65 | return Result(true, expected, pass, context); 66 | } 67 | 68 | string fail = $"Threw {e} with Message: {e.Message}"; 69 | return Result(false, expected, fail, context); 70 | } 71 | } 72 | 73 | public static Dictionary DoesNotThrow(Action function, string context) 74 | { 75 | string expected = $"{typeof(T)} was not thrown"; 76 | try 77 | { 78 | function(); 79 | string pass = $"Did not throw {typeof(T)}"; 80 | return Result(true, expected, pass, context); 81 | } 82 | catch (Exception e) 83 | { 84 | string pass = ""; 85 | if (e is T) 86 | { 87 | string fail = $"Threw {e} with Message: {e.Message}"; 88 | return Result(false, expected, fail, context); 89 | } 90 | 91 | pass = $"Threw {e} with Message: {e.Message}"; 92 | return Result(true, expected, pass, context); 93 | } 94 | } 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /addons/WAT/namespace.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Reference 3 | class_name WAT 4 | 5 | 6 | const COMPLETED: String = "completed" 7 | const Test: Script = preload("res://addons/WAT/test/test.gd") 8 | const TestRunnerScene: PackedScene = preload("res://addons/WAT/runner/TestRunner.tscn") 9 | -------------------------------------------------------------------------------- /addons/WAT/network/test_client.gd: -------------------------------------------------------------------------------- 1 | extends "res://addons/WAT/network/test_network.gd" 2 | 3 | func _ready() -> void: 4 | custom_multiplayer.connect("connection_failed", self, "_on_connection_failed") 5 | if _error(_peer.create_client(IPAddress, PORT)) == OK: 6 | custom_multiplayer.network_peer = _peer 7 | 8 | func _on_connection_failed() -> void: 9 | push_warning("TestClient could not connect to TestServer") 10 | 11 | puppet func _on_tests_received_from_server(tests: Array, repeat: int, thread_count: int) -> void: 12 | var results: Array = yield(get_parent().run(tests, repeat, thread_count, self), "completed") 13 | rpc_id(MASTER, "_on_results_received_from_client", results) 14 | 15 | # LiveWire Functions 16 | func on_test_script_started(data: Dictionary) -> void: 17 | rpc_id(MASTER, "_on_test_script_started", data) 18 | 19 | func on_test_script_finished(data: Dictionary) -> void: 20 | rpc_id(MASTER, "_on_test_script_finished", data) 21 | 22 | func on_test_method_started(data: Dictionary) -> void: 23 | rpc_id(MASTER, "_on_test_method_started", data) 24 | 25 | func on_test_method_finished(data: Dictionary) -> void: 26 | rpc_id(MASTER, "_on_test_method_finished", data) 27 | 28 | func on_asserted(data: Dictionary) -> void: 29 | rpc_id(MASTER, "_on_asserted", data) 30 | 31 | func on_test_method_described(data: Dictionary) -> void: 32 | rpc_id(MASTER, "_on_test_method_described", data) 33 | -------------------------------------------------------------------------------- /addons/WAT/network/test_network.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Node 3 | 4 | const IPAddress: String = "127.0.0.1" 5 | const PORT: int = 6019 6 | const MAXCLIENTS: int = 1 7 | const MASTER: int = 1 8 | var _peer: NetworkedMultiplayerENet 9 | var _id: int 10 | 11 | func _init() -> void: 12 | _close() 13 | custom_multiplayer = MultiplayerAPI.new() 14 | custom_multiplayer.root_node = self 15 | custom_multiplayer.allow_object_decoding = true 16 | _peer = NetworkedMultiplayerENet.new() 17 | 18 | func _process(delta: float) -> void: 19 | if custom_multiplayer.has_network_peer(): 20 | custom_multiplayer.poll() 21 | 22 | func _close() -> void: 23 | if is_instance_valid(_peer): 24 | if _is_connected(): 25 | _peer.close_connection() 26 | _peer = null 27 | 28 | func _error(err: int) -> int: 29 | if err != OK: 30 | match err: 31 | ERR_ALREADY_IN_USE: 32 | push_warning("Network Peer is already in use") 33 | ERR_CANT_CREATE: 34 | push_warning("Network Peer cannot be created") 35 | _: 36 | push_warning(err as String) 37 | return err 38 | 39 | func _is_connected() -> bool: 40 | return _peer.get_connection_status() == NetworkedMultiplayerENet.CONNECTION_CONNECTED 41 | 42 | func _exit_tree() -> void: 43 | _close() 44 | -------------------------------------------------------------------------------- /addons/WAT/network/test_server.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends "res://addons/WAT/network/test_network.gd" 3 | 4 | signal network_peer_connected 5 | signal results_received 6 | 7 | enum STATE { SENDING, RECEIVING, DISCONNECTED } 8 | 9 | var _peer_id: int 10 | # Store incoming cases from client in case of abrupt termination. 11 | var caselist: Array = [] 12 | var results_view: TabContainer 13 | var status: int = STATE.DISCONNECTED 14 | 15 | func _ready() -> void: 16 | if not Engine.is_editor_hint(): 17 | return 18 | custom_multiplayer.connect("network_peer_connected", self, "_on_network_peer_connected") 19 | custom_multiplayer.connect("network_peer_disconnected", self, "_on_network_peer_disconnected") 20 | if _error(_peer.create_server(PORT, MAXCLIENTS)) == OK: 21 | custom_multiplayer.network_peer = _peer 22 | 23 | func _on_network_peer_connected(id: int) -> void: 24 | _peer_id = id 25 | _peer.set_peer_timeout(id, 1000, 2000, 3000) 26 | emit_signal("network_peer_connected") 27 | 28 | func _on_network_peer_disconnected(_id: int) -> void: 29 | if status == STATE.SENDING: 30 | emit_signal("results_received", caselist) 31 | caselist.clear() 32 | status = STATE.DISCONNECTED 33 | 34 | func send_tests(testdir: Array, repeat: int, thread_count: int) -> void: 35 | status = STATE.SENDING 36 | rpc_id(_peer_id, "_on_tests_received_from_server", testdir, repeat, thread_count) 37 | 38 | master func _on_results_received_from_client(results: Array = []) -> void: 39 | status = STATE.RECEIVING 40 | emit_signal("results_received", results) 41 | _peer.disconnect_peer(_peer_id, true) 42 | 43 | master func _on_test_script_started(data: Dictionary) -> void: 44 | results_view.on_test_script_started(data) 45 | 46 | master func _on_test_script_finished(data: Dictionary) -> void: 47 | results_view.on_test_script_finished(data) 48 | caselist.append(data) 49 | 50 | master func _on_test_method_started(data: Dictionary) -> void: 51 | results_view.on_test_method_started(data) 52 | 53 | master func _on_test_method_finished(data: Dictionary) -> void: 54 | results_view.on_test_method_finished(data) 55 | 56 | master func _on_asserted(data: Dictionary) -> void: 57 | results_view.on_asserted(data) 58 | 59 | master func _on_test_method_described(data: Dictionary) -> void: 60 | results_view.on_test_method_described(data) 61 | -------------------------------------------------------------------------------- /addons/WAT/plugin.cfg: -------------------------------------------------------------------------------- 1 | [plugin] 2 | 3 | name="WAT" 4 | description="Unit Testing Plugin" 5 | author="github/AlexDarigan" 6 | version="6" 7 | script="plugin.gd" 8 | -------------------------------------------------------------------------------- /addons/WAT/plugin.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends EditorPlugin 3 | 4 | const Title: String = "Tests" 5 | const GUI: PackedScene = preload("res://addons/WAT/gui.tscn") 6 | const TestPanel: GDScript = preload("res://addons/WAT/ui/gui.gd") 7 | const TestPanelDocker: GDScript = preload("res://addons/WAT/ui/docker.gd") 8 | const FileSystem: GDScript = preload("res://addons/WAT/filesystem/filesystem.gd") 9 | const FileTracker: GDScript = preload("res://addons/WAT/filesystem/tracker.gd") 10 | const Settings: GDScript = preload("res://addons/WAT/settings.gd") 11 | const Metadata: GDScript = preload("res://addons/WAT/io/metadata.gd") 12 | const PluginAssetsRegistry: GDScript = preload("res://addons/WAT/ui/scaling/plugin_assets_registry.gd") 13 | var _test_panel: TestPanel 14 | var _file_system: FileSystem 15 | var _file_tracker: FileTracker 16 | var _assets_registiry: PluginAssetsRegistry 17 | var _panel_docker: TestPanelDocker 18 | 19 | func _enter_tree() -> void: 20 | Settings.initialize() 21 | var build: FuncRef = funcref(self, "_build_function") 22 | _file_system = FileSystem.new(build) 23 | _file_tracker = FileTracker.new(_file_system) 24 | Metadata.load_metadata(_file_system) 25 | _assets_registiry = PluginAssetsRegistry.new(self) 26 | _file_tracker.start_tracking_files(self) 27 | _test_panel = GUI.instance() 28 | _test_panel.setup_editor_context(self, build, funcref(self, "goto_function"), _file_system) 29 | _panel_docker = TestPanelDocker.new(self, _test_panel) 30 | add_child(_panel_docker) 31 | 32 | func _exit_tree() -> void: 33 | Metadata.save_metadata(_file_system) 34 | _panel_docker.queue_free() 35 | if(is_instance_valid(_test_panel)): 36 | _test_panel.queue_free() 37 | 38 | func _build_function() -> bool: 39 | _test_panel.Results.clear() 40 | var text: String = "FileSystem has been changed since last build." 41 | text += "\nTriggering a Build by launching an Empty Scene." 42 | text += "\nPlease select your option again after the scene quits." 43 | OS.alert(text, "Build Required") 44 | var editor: EditorInterface = get_editor_interface() 45 | editor.play_custom_scene("res://addons/WAT/mono/BuildScene.tscn") 46 | while editor.is_playing_scene(): 47 | yield(get_tree(), "idle_frame") 48 | yield(get_tree(), "idle_frame") 49 | _file_system.update() 50 | _file_system.changed = false 51 | make_bottom_panel_item_visible(_test_panel) 52 | return true 53 | 54 | func goto_function(path: String, function: String) -> void: 55 | var script: Script = load(path) 56 | var script_editor: ScriptEditor = get_editor_interface().get_script_editor() 57 | get_editor_interface().edit_resource(script) 58 | var idx: int = 0 59 | for line in script.source_code.split("\n"): 60 | idx += 1 61 | if function in line and line.begins_with("func"): 62 | script_editor.goto_line(idx) 63 | return 64 | -------------------------------------------------------------------------------- /addons/WAT/runner/TestRunner.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Node 3 | 4 | const Settings: GDScript = preload("res://addons/WAT/settings.gd") 5 | const Splitter: GDScript = preload("splitter.gd") 6 | const COMPLETED: String = "completed" 7 | signal completed 8 | 9 | func _ready() -> void: 10 | name = "TestRunner" 11 | if not Engine.is_editor_hint(): 12 | OS.window_size = Settings.window_size() 13 | if Settings.minimize_window_when_running_tests(): 14 | OS.window_minimized = true 15 | 16 | func run(tests: Array, repeat: int, threads: int, results_view: Node = null) -> Array: 17 | var results: Array = [] 18 | tests = _repeat(tests, repeat) 19 | var testthreads = Splitter.split(tests, threads) 20 | for thread in testthreads: 21 | thread.controller.results = results_view 22 | add_child(thread.controller) 23 | thread.start(self, "_run", thread) 24 | thread.wait_to_finish() 25 | for count in testthreads: 26 | results += yield(self, COMPLETED) 27 | return results 28 | 29 | func _run(thread: Thread) -> void: 30 | var results: Array = [] 31 | for test in thread.tests: 32 | results.append(yield(thread.controller.run(test), COMPLETED)) 33 | thread.controller.queue_free() 34 | emit_signal(COMPLETED, results) 35 | 36 | func _repeat(tests: Array, repeat: int) -> Array: 37 | var duplicates: Array = [] 38 | for idx in repeat: 39 | for test in tests: 40 | duplicates.append(test) 41 | duplicates += tests 42 | return duplicates 43 | 44 | -------------------------------------------------------------------------------- /addons/WAT/runner/TestRunner.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=3 format=2] 2 | 3 | [ext_resource path="res://addons/WAT/runner/TestRunner.gd" type="Script" id=1] 4 | [ext_resource path="res://addons/WAT/network/test_client.gd" type="Script" id=2] 5 | 6 | [node name="TestRunner" type="Node"] 7 | script = ExtResource( 1 ) 8 | 9 | [node name="TestClient" type="Node" parent="."] 10 | script = ExtResource( 2 ) 11 | -------------------------------------------------------------------------------- /addons/WAT/runner/splitter.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | static func split(tests: Array, threads: int = 1) -> Array: 4 | tests.sort_custom(YieldTimeSorter, "sort_ascending") 5 | threads = calibrate_threads(tests.size(), threads) 6 | return _testthreads(_distribute(_testpools(threads), tests, threads)) 7 | 8 | static func calibrate_threads(tests: int, threads: int) -> int: 9 | if tests < threads: 10 | push_warning("Readjusting thread count to match low test size") 11 | threads = tests 12 | return threads 13 | 14 | static func _testpools(count: int) -> Array: 15 | var pools: Array = [] 16 | for pool in count: 17 | pools.append([]) 18 | return pools 19 | 20 | static func _distribute(pools: Array, tests: Array, count: int) -> Array: 21 | var idx: int = 0 22 | for test in tests: 23 | pools[idx].append(test) 24 | idx = 0 if idx == count - 1 else idx + 1 25 | return pools 26 | 27 | static func _testthreads(pools: Array) -> Array: 28 | # Our tests are sorted from slowest to quickest.. 29 | # ..so we loop through each pool in order to disaparate time 30 | var threads: Array = [] 31 | for pool in pools: 32 | threads.append(TestThread.new(pool)) 33 | return threads 34 | 35 | class TestThread extends Thread: 36 | var tests: Array 37 | var controller: Node = load("res://addons/WAT/runner/test_controller.gd").new() 38 | 39 | func _init(_tests): 40 | tests = _tests 41 | 42 | class YieldTimeSorter: 43 | 44 | static func sort_ascending(a: Dictionary, b: Dictionary) -> bool: 45 | return a["time"] < b["time"] 46 | 47 | 48 | -------------------------------------------------------------------------------- /addons/WAT/settings.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Reference 3 | 4 | 5 | static func initialize() -> void: 6 | if not ProjectSettings.has_setting("WAT/Test_Directory"): 7 | push_warning("Test Directory was set to project root.\nYou may change any setting for WAT in Project -> ProjectSettings -> General -> WAT") 8 | _add_setting("Test_Directory", TYPE_STRING, "res://") 9 | _add_setting("Results_Directory", TYPE_STRING, "res://") 10 | _add_setting("Test_Metadata_Directory", TYPE_STRING, "res://") 11 | _add_setting("Tags", TYPE_STRING_ARRAY, PoolStringArray()) 12 | _add_setting("Cache_Tests", TYPE_BOOL, true) 13 | _add_setting("Window_Size", TYPE_VECTOR2, Vector2(1280, 720)) 14 | _add_setting("Minimize_Window_When_Running_Tests", TYPE_BOOL, false) 15 | _add_setting("Port", TYPE_INT, 6008) 16 | _add_setting("Tags", TYPE_STRING_ARRAY, PoolStringArray()) 17 | 18 | # Set this to true if using external editors 19 | ProjectSettings.save() 20 | 21 | static func _add_setting(title: String, type: int, value, hint_type: int = -1, hint_string = "") -> void: 22 | title = title.insert(0, "WAT/") 23 | if ProjectSettings.has_setting(title): 24 | return 25 | ProjectSettings.set(title, value) 26 | var prop: Dictionary = {} 27 | prop["name"] = title 28 | prop["type"] = type 29 | if hint_type > -1: 30 | prop["hint"] = hint_type 31 | prop["hint_string"] = hint_string 32 | ProjectSettings.add_property_info(prop) 33 | 34 | static func test_directory() -> String: 35 | return ProjectSettings.get_setting("WAT/Test_Directory") 36 | 37 | static func results_directory() -> String: 38 | return ProjectSettings.get_setting("WAT/Results_Directory") 39 | 40 | static func metadata_directory() -> String: 41 | return ProjectSettings.get_setting("WAT/Test_Metadata_Directory") 42 | 43 | static func window_size() -> Vector2: 44 | return ProjectSettings.get_setting("WAT/Window_Size") 45 | 46 | static func tags() -> PoolStringArray: 47 | return ProjectSettings.get_setting("WAT/Tags") 48 | 49 | static func cache_tests() -> bool: 50 | return ProjectSettings.get_setting("WAT/Cache_Tests") 51 | 52 | static func minimize_window_when_running_tests() -> bool: 53 | return ProjectSettings.get_setting("WAT/Minimize_Window_When_Running_Tests") 54 | 55 | static func port() -> int: 56 | return ProjectSettings.get_setting("WAT/Port") 57 | 58 | static func is_bottom_panel() -> int: 59 | return ProjectSettings.get_setting("WAT/Display") == 8 60 | -------------------------------------------------------------------------------- /addons/WAT/test/any.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | 3 | # I hate this 4 | # Used for do not care values in stubs 5 | # Probably a better way to handle it 6 | 7 | func get_class() -> String: 8 | return "Any" 9 | -------------------------------------------------------------------------------- /addons/WAT/test/case.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | var _total: int = 0 4 | var _passed: int = 0 5 | var _directory: String 6 | var _title: String 7 | var _path: String 8 | var _methods: Array = [] 9 | var _success: bool = false 10 | var _time_taken: float = 0.0 11 | var _test: Node 12 | var _names: Array = [] 13 | 14 | func _init(directory, filepath, title, test) -> void: 15 | _test = test 16 | _title = title 17 | _path = filepath 18 | _directory = directory 19 | 20 | func add_method(name: String) -> void: 21 | if name in _names: 22 | # We're running a test with multiple arguments 23 | return 24 | _names.append(name) 25 | var contxt = name.replace("_", " ").lstrip("test") 26 | _methods.append({fullname = name, context = contxt, assertions = [], total = 0, passed = 0, success = false, time = 0.0}) 27 | 28 | func _on_test_method_described(description: String) -> void: 29 | _methods.back().context = description 30 | 31 | # We pass this to the controller who passes it to the runner who passes 32 | func _on_asserted(assertion: Dictionary) -> void: 33 | assertion["method"] = _methods.back()["fullname"] 34 | assertion["script"] = _path 35 | # Handle on demand so we can pass it on to live updaters 36 | _methods.back().assertions.append(assertion) 37 | _methods.back()["total"] += 1 38 | _methods.back()["passed"] += assertion["success"] as int # false = 0, true = 1 39 | 40 | func calculate() -> void: 41 | for method in _methods: 42 | method.success = method.total > 0 and method.total == method.passed 43 | _passed += method.success as int 44 | _total = _methods.size() 45 | _success = _total > 0 and _total == _passed 46 | 47 | func to_dictionary() -> Dictionary: 48 | return { total = _total, 49 | passed = _passed, 50 | context = _title, 51 | methods = _methods, 52 | success = _success, 53 | path = _path, 54 | time_taken = _time_taken, 55 | dir = _directory, 56 | } 57 | -------------------------------------------------------------------------------- /addons/WAT/test/parameters.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | 3 | var _keys: Array = [] 4 | var _values: Array = [] 5 | 6 | var parameters: Dictionary = {} 7 | #parameters([["a", "b", "expected"], [2, 2, 4], [5, 5, 10], [7, 7, 14]]) 8 | func parameters(list: Array) -> bool: 9 | if _keys.empty() or _values.empty(): 10 | # Keys aren't empty, so we'll be updating this implicilty every time a call is made instead 11 | _keys = list.pop_front() 12 | _values = list 13 | return _update() 14 | 15 | func _update() -> bool: 16 | parameters.clear() 17 | var values = _values.pop_front() 18 | for i in _keys.size(): 19 | parameters[_keys[i]] = values[i] 20 | return not _values.empty() 21 | -------------------------------------------------------------------------------- /addons/WAT/test/recorder.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | var _recording: Object 4 | var _properties: Dictionary 5 | var _is_recording: bool = false 6 | 7 | func record(what: Object, properties: Array) -> void: 8 | _recording = what 9 | for property in properties: 10 | _properties[property] = [] 11 | 12 | func start() -> void: 13 | _is_recording = true 14 | 15 | func stop() -> void: 16 | _is_recording = false 17 | 18 | func _process(delta: float) -> void: 19 | if _is_recording: 20 | _capture() 21 | 22 | func _capture() -> void: 23 | if not is_instance_valid(_recording): 24 | return 25 | for property in _properties: 26 | _properties[property].append(_recording.get(property)) 27 | 28 | func get_property_timeline(property: String): 29 | return _properties[property] 30 | 31 | func get_property_map() -> Dictionary: 32 | return _properties 33 | -------------------------------------------------------------------------------- /addons/WAT/test/watcher.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | 3 | var watching: Dictionary = {} 4 | var _objects: Array = [] 5 | 6 | func watch(emitter, event: String) -> void: 7 | _objects.append(emitter) 8 | if emitter.is_connected(event, self, "_add_emit"): 9 | return 10 | emitter.set_meta("watcher", self) 11 | emitter.connect(event, self, "_add_emit", [emitter, event]) 12 | watching[event] = {emit_count = 0, calls = []} 13 | 14 | 15 | func _add_emit(a = null, b = null, c = null, d = null, e = null, f = null, g = null, h = null, i = null, j = null, k = null): 16 | var arguments: Array = [a, b, c, d, e, f, g, h, i, j, k] 17 | var event 18 | while not event: 19 | event = arguments.pop_back() 20 | var obj = arguments.pop_back() 21 | watching[event].emit_count += 1 22 | watching[event].calls.append({emitter = obj, args = arguments}) 23 | 24 | func unwatch(emitter, event: String) -> void: 25 | if emitter.is_connected(event, self, "_add_emit"): 26 | emitter.disconnect(event, self, "_add_emit") 27 | watching.erase(event) 28 | emitter.set_meta("watcher", null) 29 | 30 | func get_emit_count(event: String) -> int: 31 | return watching[event]["emit_count"] 32 | 33 | func get_data(event: String) -> Dictionary: 34 | return watching[event] 35 | 36 | func clear() -> void: 37 | for object in _objects: 38 | if is_instance_valid(object): 39 | object.set_meta("watcher", null) 40 | 41 | #func _notification(what): 42 | # if what == NOTIFICATION_PREDELETE: 43 | # clear() 44 | -------------------------------------------------------------------------------- /addons/WAT/test/yielder.gd: -------------------------------------------------------------------------------- 1 | extends Timer 2 | 3 | signal finished 4 | var _emitter: Object 5 | var _event: String 6 | 7 | func _init() -> void: 8 | one_shot = true 9 | connect("timeout", self, "_on_resume", [], CONNECT_DEFERRED) 10 | 11 | func until_timeout(time: float) -> Timer: 12 | start(time) 13 | return self 14 | 15 | func until_signal(time: float, emitter: Object, event: String) -> Timer: 16 | _emitter = emitter 17 | _event = event 18 | _emitter.connect(event, self, "_on_resume", [], CONNECT_DEFERRED) 19 | start(time) 20 | return self 21 | 22 | func _on_resume(a = null, b = null, c = null, d = null, e = null, f = null) -> void: 23 | stop() 24 | if is_instance_valid(_emitter) and _emitter.is_connected(_event, self, "_on_resume"): 25 | _emitter.disconnect(_event, self, "_on_resume") 26 | # Our adapter is connected to this. When this is emitted our adapter 27 | # ..will call "_next" which call defers _change_state. Since it is a deferred 28 | # ..call the test will resume first. Therefore if a new yield gets constructed 29 | # ..in the interim we will be able to check if we have restarted the yield clock 30 | emit_signal("finished", [a, b, c, d, e, f]) 31 | 32 | func is_active() -> bool: 33 | return not is_stopped() and time_left > 0 34 | -------------------------------------------------------------------------------- /addons/WAT/ui/docker.gd: -------------------------------------------------------------------------------- 1 | extends Node 2 | 3 | const BOTTOM_PANEL: int = 8 4 | const displays: Dictionary = { 5 | 0: "Left.UL Dock", 1: "Left.BL Dock", 2: "Left.UR Dock", 6 | 3: "Left.BR Dock", 4: "Right.UL Dock", 5: "Right.BL Dock", 7 | 6: "Right.UR Dock", 7: "Right.BR Dock", 8: "Bottom Panel", 8 | } 9 | 10 | var _plugin: EditorPlugin 11 | var _scene: Control 12 | var _state: int 13 | 14 | func _init(plugin: EditorPlugin, scene: Control) -> void: 15 | _plugin = plugin 16 | _scene = scene 17 | add_setting() 18 | _state = ProjectSettings.get_setting("WAT/Display") 19 | construct() 20 | 21 | func _process(delta: float) -> void: 22 | var state = ProjectSettings.get_setting("WAT/Display") 23 | if state != _state: 24 | deconstruct() 25 | _state = state 26 | construct() 27 | 28 | func construct() -> void: 29 | if _state == BOTTOM_PANEL: 30 | _plugin.add_control_to_bottom_panel(_scene, "Tests") 31 | else: 32 | _plugin.add_control_to_dock(_state, _scene) 33 | 34 | func deconstruct() -> void: 35 | if _state == BOTTOM_PANEL and is_instance_valid(_scene): 36 | _plugin.remove_control_from_bottom_panel(_scene) 37 | elif is_instance_valid(_scene): 38 | _plugin.remove_control_from_docks(_scene) 39 | 40 | func _notification(what) -> void: 41 | if what == NOTIFICATION_PREDELETE: 42 | ProjectSettings.set_setting("WAT/Display", _state) 43 | ProjectSettings.save() 44 | deconstruct() 45 | 46 | func add_setting() -> void: 47 | if not ProjectSettings.has_setting("WAT/Display"): 48 | ProjectSettings.set_setting("WAT/Display", BOTTOM_PANEL) 49 | ProjectSettings.save() 50 | var property = {} 51 | property.name = "WAT/Display" 52 | property.type = TYPE_INT 53 | property.hint = PROPERTY_HINT_ENUM 54 | property.hint_string = PoolStringArray(displays.values()).join(",") 55 | ProjectSettings.add_property_info(property) 56 | ProjectSettings.save() 57 | -------------------------------------------------------------------------------- /addons/WAT/ui/links.gd: -------------------------------------------------------------------------------- 1 | extends MenuButton 2 | tool 3 | 4 | func _ready() -> void: 5 | set_focus_mode(FOCUS_ALL) 6 | var p: PopupMenu = get_popup() 7 | p.set_item_metadata(0, "https://ko-fi.com/alexanddraw") 8 | p.set_item_metadata(1, "https://github.com/AlexDarigan/WAT/issues/new") 9 | p.connect("index_pressed", self, "_on_idx_pressed") 10 | 11 | func _on_idx_pressed(idx: int) -> void: 12 | OS.shell_open(get_popup().get_item_metadata(idx)) 13 | -------------------------------------------------------------------------------- /addons/WAT/ui/results/assertion.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | 3 | var component: TreeItem 4 | var context: String = "" 5 | var success: bool = false 6 | 7 | func _init(_component: TreeItem, data: Dictionary) -> void: 8 | component = _component 9 | context = data["context"] 10 | success = data["success"] 11 | if context != "": 12 | component.set_text(0, context) 13 | -------------------------------------------------------------------------------- /addons/WAT/ui/results/counter.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | # Counter TreeItem component for tests passed over total executed. 3 | 4 | signal counter_changed 5 | 6 | var component: TreeItem 7 | var path: String 8 | var title: String setget set_title 9 | var passed: int = 0 setget set_passed 10 | var total: int = 0 setget set_total 11 | var show: bool = true 12 | 13 | func _init(_component: TreeItem) -> void: 14 | component = _component 15 | connect("counter_changed", self, "display") 16 | 17 | func set_passed(_passed: int) -> void: 18 | passed = _passed 19 | emit_signal("counter_changed") 20 | 21 | func set_total(_total: int) -> void: 22 | total = _total 23 | emit_signal("counter_changed") 24 | 25 | func set_title(_title: String) -> void: 26 | title = _title 27 | component.set_text(0, _title) 28 | 29 | # Displays (total / passed) in the component text. 30 | func display() -> void: 31 | if show: 32 | component.set_text(0, "(%s/%s) %s" % [passed, total, title]) 33 | -------------------------------------------------------------------------------- /addons/WAT/ui/results/method.gd: -------------------------------------------------------------------------------- 1 | extends "res://addons/WAT/ui/results/counter.gd" 2 | 3 | const AssertionTreeItem: GDScript = preload("res://addons/WAT/ui/results/assertion.gd") 4 | 5 | var scriptpath: String 6 | var name: String 7 | var assertions: Array = [] 8 | 9 | func _init(_component: TreeItem, _title: String, _script: String).(_component) -> void: 10 | scriptpath = _script 11 | path = _title 12 | name = _title # func name 13 | set_title(_title.replace("test_", "").replace("_", " ")) 14 | show = false # Modify if test method results should display number of assertions passed. 15 | 16 | func add_assertion(tree: Tree, data: Dictionary): 17 | set_total(total + 1) 18 | if data["assertion"]["success"]: 19 | set_passed(passed + 1) 20 | if data["assertion"]["context"] == "": 21 | component.collapsed = true 22 | var expected: TreeItem = tree.create_item(component) 23 | var actual: TreeItem = tree.create_item(component) 24 | expected.set_text(0, "EXPECTED: %s" % data["assertion"]["expected"]) 25 | actual.set_text(0, "RESULTED: %s" % data["assertion"]["actual"]) 26 | else: 27 | var assertion: AssertionTreeItem = AssertionTreeItem.new(tree.create_item(component), data["assertion"]) 28 | assertion.component.collapsed = true 29 | var expected: TreeItem = tree.create_item(assertion.component) 30 | var actual: TreeItem = tree.create_item(assertion.component) 31 | expected.set_text(0, "EXPECTED: %s" % data["assertion"]["expected"]) 32 | actual.set_text(0, "RESULTED: %s" % data["assertion"]["actual"]) 33 | assertion.component.set_custom_color(0, tree.PASSED if assertion.success else tree.FAILED) 34 | assertion.component.set_icon(0, tree.icons.passed if assertion.success else tree.icons.failed) 35 | tree.scroll_to_item(assertion.component) 36 | assertions.append(assertion) 37 | return assertion 38 | -------------------------------------------------------------------------------- /addons/WAT/ui/results/script.gd: -------------------------------------------------------------------------------- 1 | extends "res://addons/WAT/ui/results/counter.gd" 2 | 3 | const MethodTreeItem: GDScript = preload("res://addons/WAT/ui/results/method.gd") 4 | 5 | var methods: Dictionary = {} 6 | var method_names: PoolStringArray 7 | 8 | func _init(_component: TreeItem, data: Dictionary).(_component) -> void: 9 | set_title(data["name"] if data["title"] == "" else data["title"]) 10 | path = data["path"] 11 | method_names = data["methods"] 12 | 13 | func add_method(tree: Tree, data: Dictionary) -> void: 14 | var method = MethodTreeItem.new(tree.create_item(component), data["method"], path) 15 | methods[method.path] = method 16 | set_total(total + 1) 17 | tree.scroll_to_item(method.component) 18 | 19 | func on_method_finished(data: Dictionary) -> void: 20 | if data["success"]: 21 | set_passed(passed + 1) 22 | -------------------------------------------------------------------------------- /addons/WAT/ui/results/tab.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | 3 | var tree: Tree 4 | var title: String 5 | var idx: int = 0 6 | var count: int = 0 7 | var passed: int = 0 8 | var _multiplier: int = 0 9 | var _expected_total: int = 0 10 | var success: bool = false 11 | var expected setget ,_get_expected 12 | 13 | func _init(_tree: Tree, _title: String, multiplier: int) -> void: 14 | tree = _tree 15 | title = _title 16 | _multiplier = multiplier + 1 17 | 18 | func increment_expected_total() -> void: 19 | # Multiplier tracks how many times we repeat tests 20 | # count should always be 1 21 | _expected_total += 1 * _multiplier 22 | 23 | func on_test_script_finished(successful: bool) -> void: 24 | if successful: 25 | passed += 1 26 | if count == _expected_total and passed == count: 27 | success = true 28 | 29 | func _get_expected() -> int: 30 | return _expected_total 31 | -------------------------------------------------------------------------------- /addons/WAT/ui/scaling/dynamic_size_spinbox.gd: -------------------------------------------------------------------------------- 1 | extends SpinBox 2 | tool 3 | 4 | var oldText 5 | 6 | func _ready(): 7 | get_line_edit().expand_to_text_length = true 8 | get_line_edit().connect("text_changed", self, "_on_text_edit_changed") 9 | get_line_edit().connect("focus_exited", self, "_on_focus_exited") 10 | connect("value_changed", self, "_on_value_changed") 11 | oldText = get_line_edit().text 12 | 13 | # Set's the text to itself in order to shoot an update to the line edit 14 | # This is needed or else the line edit starts off off-centered at startup 15 | get_line_edit().text = oldText 16 | 17 | func _on_focus_exited(): 18 | # Disgusting frame waiting workaround because there is currently no way to 19 | # listen for when a spinbox is changed and the change is rejected. 20 | # (ie. inputting 20 in the spinbox when the spinbox already has a value of 20) 21 | yield(get_tree(),"idle_frame") 22 | yield(get_tree(),"idle_frame") 23 | set_size(get_minimum_size()) 24 | pass 25 | 26 | func _on_value_changed(newValue): 27 | set_size(get_minimum_size()) 28 | 29 | func _on_text_edit_changed(newText): 30 | # Using regex since you cannot reliably detect if a string is not an int 31 | # (ie. String(invalid text) = 0, when 0 might be a value we want) 32 | var regex = RegEx.new() 33 | regex.compile("^(0|[1-9][0-9]*)$") 34 | 35 | # Remove prefix and suffixes 36 | newText = newText.trim_prefix(prefix + " ").trim_suffix(" " + suffix) 37 | 38 | # Try and parse the inputted text in the middle 39 | var result = regex.search(newText) 40 | var invalidInput = false 41 | 42 | # If input is not an integer (excluding empty input), then revert the input back 43 | if not newText.empty() and not result: 44 | get_line_edit().text = oldText 45 | invalidInput = true 46 | # If input is out of bounds, bring it back to the nearest bound 47 | elif result: 48 | if int(result.get_string()) > max_value: 49 | get_line_edit().text = str(max_value) 50 | get_line_edit().caret_position = get_line_edit().text.length() 51 | invalidInput = true 52 | elif int(result.get_string()) < min_value: 53 | get_line_edit().text = str(min_value) 54 | get_line_edit().caret_position = get_line_edit().text.length() 55 | invalidInput = true 56 | 57 | if not invalidInput: 58 | oldText = prefix + " " + newText + " " + suffix 59 | 60 | set_size(get_minimum_size()) 61 | -------------------------------------------------------------------------------- /addons/WAT/ui/scaling/icons.gd: -------------------------------------------------------------------------------- 1 | extends Reference 2 | 3 | var debug_failed = preload("res://addons/WAT/assets/debug_failed.png") 4 | var docs = preload("res://addons/WAT/assets/docs.svg") 5 | var failed = preload("res://addons/WAT/assets/failed.png") 6 | var folder = preload("res://addons/WAT/assets/folder.png") 7 | var function = preload("res://addons/WAT/assets/function.png") 8 | var issue = preload("res://addons/WAT/assets/issue.svg") 9 | var kofi = preload("res://addons/WAT/assets/kofi.png") 10 | var label = preload("res://addons/WAT/assets/label.png") 11 | var passed = preload("res://addons/WAT/assets/passed.png") 12 | var play = preload("res://addons/WAT/assets/play.png") 13 | var play_debug = preload("res://addons/WAT/assets/play_debug.png") 14 | var play_failed = preload("res://addons/WAT/assets/play_failed.png") 15 | var request_docs = preload("res://addons/WAT/assets/request_docs.svg") 16 | var scriptx = preload("res://addons/WAT/assets/script.png") 17 | var timer = preload("res://addons/WAT/assets/timer.png") 18 | -------------------------------------------------------------------------------- /addons/WAT/ui/scaling/scene_tree_adjuster.gd: -------------------------------------------------------------------------------- 1 | tool 2 | extends Reference 3 | 4 | const PluginAssetsRegistry: GDScript = preload("res://addons/WAT/ui/scaling/plugin_assets_registry.gd") 5 | 6 | static func adjust(runner: PanelContainer, icons: Reference, plugin = null) -> void: 7 | var registry: PluginAssetsRegistry = PluginAssetsRegistry.new(plugin) 8 | 9 | # Scale Icons 10 | icons.debug_failed = registry.load_asset("assets/debug_failed.png") 11 | icons.docs = registry.load_asset("assets/docs.svg") 12 | icons.failed = registry.load_asset("assets/failed.png") 13 | icons.folder = registry.load_asset("assets/folder.png") 14 | icons.function = registry.load_asset("assets/function.png") 15 | icons.issue = registry.load_asset("assets/issue.svg") 16 | icons.kofi = registry.load_asset("assets/kofi.png") 17 | icons.label = registry.load_asset("assets/label.png") 18 | icons.passed = registry.load_asset("assets/passed.png") 19 | icons.play = registry.load_asset("assets/play.png") 20 | icons.play_debug = registry.load_asset("assets/play_debug.png") 21 | icons.play_failed = registry.load_asset("assets/play_failed.png") 22 | icons.request_docs = registry.load_asset("assets/request_docs.svg") 23 | icons.scriptx = registry.load_asset("assets/script.png") 24 | icons.timer = registry.load_asset("assets/timer.png") 25 | 26 | # Scale Icons already in the editor 27 | runner.RunAll.icon = registry.load_asset(runner.RunAll.icon) 28 | runner.DebugAll.icon = registry.load_asset(runner.DebugAll.icon) 29 | 30 | # Scale summary icons 31 | runner.Summary.Time.icon = registry.load_asset(runner.Summary.Time.icon) 32 | runner.Summary.Tests.icon = registry.load_asset(runner.Summary.Tests.icon) 33 | runner.Summary.Passing.icon = registry.load_asset(runner.Summary.Passing.icon) 34 | runner.Summary.Failing.icon = registry.load_asset(runner.Summary.Failing.icon) 35 | runner.Summary.Runs.icon = registry.load_asset(runner.Summary.Runs.icon) 36 | -------------------------------------------------------------------------------- /addons/WAT/ui/summary.gd: -------------------------------------------------------------------------------- 1 | extends HBoxContainer 2 | tool 3 | 4 | var time: float = 0 5 | var runcount: int = 0 6 | var running = false 7 | var time_taken: float = 0.0 8 | 9 | onready var Time: Button = $Time 10 | onready var Tests: Button = $Tests 11 | onready var Passing: Button = $Passing 12 | onready var Failing: Button = $Failing 13 | onready var Runs: Button = $Runs 14 | 15 | func _ready() -> void: 16 | for child in get_children(): 17 | child.set_focus_mode(FOCUS_NONE) 18 | 19 | func time() -> void: 20 | time_taken = 0.0 21 | runcount += 1 22 | time = OS.get_ticks_msec() 23 | running = true 24 | 25 | func _process(delta): 26 | if running: 27 | Time.text = str((OS.get_ticks_msec() - time) / 1000) 28 | 29 | func summarize(caselist: Array) -> void: 30 | running = false 31 | time_taken = (OS.get_ticks_msec() - time) / 1000 32 | var passed = 0 33 | var failed = 0 34 | var total = 0 35 | for case in caselist: 36 | total += 1 37 | if case.success: 38 | passed += 1 39 | else: 40 | failed += 1 41 | Time.text = time_taken as String 42 | Tests.text = total as String 43 | Passing.text = passed as String 44 | Failing.text = failed as String 45 | Runs.text = runcount as String 46 | 47 | func _setup_editor_assets(assets_registry): 48 | Time.icon = assets_registry.load_asset(Time.icon) 49 | Tests.icon = assets_registry.load_asset(Tests.icon) 50 | Passing.icon = assets_registry.load_asset(Passing.icon) 51 | Failing.icon = assets_registry.load_asset(Failing.icon) 52 | Runs.icon = assets_registry.load_asset(Runs.icon) 53 | -------------------------------------------------------------------------------- /bootsplash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/bootsplash.png -------------------------------------------------------------------------------- /bootsplash.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/bootsplash.png-30e2c9990faa42442c2d12c3b0f7ed9a.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://bootsplash.png" 13 | dest_files=[ "res://.import/bootsplash.png-30e2c9990faa42442c2d12c3b0f7ed9a.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | stream=false 32 | size_limit=0 33 | detect_3d=true 34 | svg/scale=1.0 35 | -------------------------------------------------------------------------------- /default_env.tres: -------------------------------------------------------------------------------- 1 | [gd_resource type="Environment" load_steps=2 format=2] 2 | 3 | [sub_resource type="ProceduralSky" id=1] 4 | 5 | [resource] 6 | background_mode = 2 7 | background_sky = SubResource( 1 ) 8 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Fractural/GodotFixedVolatilePhysics/9a4daf5a28cb4893fa91e42734719f4504382598/icon.png -------------------------------------------------------------------------------- /icon.png.import: -------------------------------------------------------------------------------- 1 | [remap] 2 | 3 | importer="texture" 4 | type="StreamTexture" 5 | path="res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" 6 | metadata={ 7 | "vram_texture": false 8 | } 9 | 10 | [deps] 11 | 12 | source_file="res://icon.png" 13 | dest_files=[ "res://.import/icon.png-487276ed1e3a0c39cad0279d744ee560.stex" ] 14 | 15 | [params] 16 | 17 | compress/mode=0 18 | compress/lossy_quality=0.7 19 | compress/hdr_mode=0 20 | compress/bptc_ldr=0 21 | compress/normal_map=0 22 | flags/repeat=0 23 | flags/filter=true 24 | flags/mipmaps=false 25 | flags/anisotropic=false 26 | flags/srgb=2 27 | process/fix_alpha_border=true 28 | process/premult_alpha=false 29 | process/HDR_as_SRGB=false 30 | process/invert_color=false 31 | process/normal_map_invert_y=false 32 | stream=false 33 | size_limit=0 34 | detect_3d=true 35 | svg/scale=1.0 36 | -------------------------------------------------------------------------------- /project.godot: -------------------------------------------------------------------------------- 1 | ; Engine configuration file. 2 | ; It's best edited using the editor UI and not directly, 3 | ; since the parameters that go here are not all obvious. 4 | ; 5 | ; Format: 6 | ; [section] ; section goes between [] 7 | ; param=value ; assign values to parameters 8 | 9 | config_version=4 10 | 11 | _global_script_classes=[ { 12 | "base": "Control", 13 | "class": "VirtualJoystick", 14 | "language": "GDScript", 15 | "path": "res://addons/VirtualJoystick/virtual_joystick.gd" 16 | }, { 17 | "base": "Reference", 18 | "class": "WAT", 19 | "language": "GDScript", 20 | "path": "res://addons/WAT/namespace.gd" 21 | }, { 22 | "base": "Node", 23 | "class": "WATTest", 24 | "language": "GDScript", 25 | "path": "res://addons/WAT/test/test.gd" 26 | } ] 27 | _global_script_class_icons={ 28 | "VirtualJoystick": "", 29 | "WAT": "", 30 | "WATTest": "" 31 | } 32 | 33 | [FracturalCommons] 34 | 35 | "Generate CSharp Scripts Table"=false 36 | "Generate Version Preprocessor Defines"=false 37 | 38 | [MonoCustomResourceRegistry] 39 | 40 | ClassPrefix="" 41 | SearchType=0 42 | ResourceScriptDirectories=[ "res://" ] 43 | 44 | [WAT] 45 | 46 | Test_Directory="res://tests" 47 | Results_Directory="res://tests" 48 | Test_Metadata_Directory="res://tests" 49 | Tags=PoolStringArray( ) 50 | Window_Size=Vector2( 1280, 720 ) 51 | Minimize_Window_When_Running_Tests=false 52 | Port=6008 53 | Display=8 54 | Cache_Tests=true 55 | 56 | [application] 57 | 58 | config/name="GodotFixedVolatilePhysics" 59 | run/main_scene="res://tests/Manual/Physics/CompleteTest.tscn" 60 | boot_splash/image="res://bootsplash.png" 61 | boot_splash/bg_color=Color( 0, 0, 0, 1 ) 62 | config/icon="res://icon.png" 63 | 64 | [display] 65 | 66 | window/stretch/mode="viewport" 67 | window/stretch/aspect="expand" 68 | 69 | [editor_plugins] 70 | 71 | enabled=PoolStringArray( "res://addons/FracturalCommons/plugin.cfg", "res://addons/GodotFixedVolatilePhysics/plugin.cfg", "res://addons/WAT/plugin.cfg" ) 72 | 73 | [input_devices] 74 | 75 | pointing/emulate_touch_from_mouse=true 76 | 77 | [logging] 78 | 79 | file_logging/enable_file_logging=true 80 | file_logging/max_log_files=10 81 | 82 | [mono] 83 | 84 | project/assembly_name="GodotFixedVolatilePhysics" 85 | 86 | [physics] 87 | 88 | common/physics_fps=20 89 | common/enable_pause_aware_picking=true 90 | 91 | [rendering] 92 | 93 | quality/driver/driver_name="GLES2" 94 | vram_compression/import_etc=true 95 | environment/default_environment="res://default_env.tres" 96 | -------------------------------------------------------------------------------- /tests/Manual/Physics/FPSCounter.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | 4 | namespace Tests 5 | { 6 | public class FPSCounter : Label 7 | { 8 | public override void _Process(float delta) 9 | { 10 | Text = Engine.GetFramesPerSecond() + " FPS"; 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /tests/Manual/Physics/GDKinematicMovement.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using System; 3 | using VirtualJoystickAddon; 4 | using Fractural.Utils; 5 | 6 | namespace Tests 7 | { 8 | public class GDKinematicMovement : KinematicBody2D 9 | { 10 | [Export] 11 | public float speed; 12 | [Export] 13 | private NodePath joystickPath; 14 | private VirtualJoystick joystick; 15 | 16 | public override void _Ready() 17 | { 18 | joystick = this.GetNodeAsWrapper(joystickPath); 19 | } 20 | 21 | public override void _PhysicsProcess(float delta) 22 | { 23 | if (joystick.Output != Vector2.Zero) 24 | MoveAndSlide(joystick.Output * speed); 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /tests/Manual/Physics/Prefabs/VolatileAreaTestPrefab.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=2] 2 | 3 | [ext_resource path="res://tests/Manual/VoltNode2D/VolatileAreaTest.cs" type="Script" id=1] 4 | [ext_resource path="res://addons/GodotFixedVolatilePhysics/Core/VolatileShapes/VolatileRect.cs" type="Script" id=2] 5 | [ext_resource path="res://icon.png" type="Texture" id=3] 6 | 7 | [node name="VolatileAreaTest" type="Node2D"] 8 | position = Vector2( -280.631, -220.769 ) 9 | script = ExtResource( 1 ) 10 | AutoQuery = true 11 | hasEntityInsideSpritePath = NodePath("Sprite") 12 | _fixedTransform = PoolByteArray( 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 112, 94, 231, 254, 255, 255, 0, 0, 11, 59, 35, 255, 255, 255 ) 13 | 14 | [node name="VolatileRect" type="Node2D" parent="."] 15 | script = ExtResource( 2 ) 16 | Editing = true 17 | DebugDraw = true 18 | _extents = PoolByteArray( 0, 0, 112, 252, 48, 0, 0, 0, 0, 0, 57, 89, 85, 0, 0, 0 ) 19 | _fixedTransform = PoolByteArray( 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ) 20 | 21 | [node name="Sprite" type="Sprite" parent="."] 22 | self_modulate = Color( 1, 1, 1, 0.223529 ) 23 | scale = Vector2( 0.297486, 0.297486 ) 24 | texture = ExtResource( 3 ) 25 | -------------------------------------------------------------------------------- /tests/Manual/Physics/Prefabs/VolatileKinematicMovementPrefab.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=2] 2 | 3 | [ext_resource path="res://tests/Manual/VoltNode2D/VolatileKinematicBodyMovement.cs" type="Script" id=1] 4 | [ext_resource path="res://addons/GodotFixedVolatilePhysics/Core/VolatileShapes/VolatileCircle.cs" type="Script" id=2] 5 | [ext_resource path="res://icon.png" type="Texture" id=3] 6 | 7 | [node name="VolatileKinematicBodyMovement" type="Node2D"] 8 | position = Vector2( 261.629, -50.7239 ) 9 | script = ExtResource( 1 ) 10 | _speed = PoolByteArray( 0, 0, 0, 0, 10, 0, 0, 0 ) 11 | joystickPath = NodePath("") 12 | _fixedTransform = PoolByteArray( 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 234, 160, 5, 1, 0, 0, 0, 0, 172, 70, 205, 255, 255, 255 ) 13 | 14 | [node name="VolatileCircle" type="Node2D" parent="."] 15 | script = ExtResource( 2 ) 16 | Editing = true 17 | DebugDraw = true 18 | _radius = PoolByteArray( 0, 0, 168, 211, 23, 0, 0, 0 ) 19 | _fixedTransform = PoolByteArray( 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ) 20 | 21 | [node name="Sprite" type="Sprite" parent="."] 22 | scale = Vector2( 0.124448, 0.124448 ) 23 | texture = ExtResource( 3 ) 24 | -------------------------------------------------------------------------------- /tests/Manual/VoltNode2D/FixedMovement.cs: -------------------------------------------------------------------------------- 1 | using FixMath.NET; 2 | using Godot; 3 | using Volatile; 4 | using Volatile.GodotEngine; 5 | 6 | namespace Tests 7 | { 8 | [Tool] 9 | public class FixedMovement : VoltNode2D 10 | { 11 | private Fix64 speed; 12 | public Fix64 Speed 13 | { 14 | get 15 | { 16 | 17 | #if TOOLS 18 | if (Engine.EditorHint) 19 | return VoltType.DeserializeOrDefault(_speed); 20 | else 21 | #endif 22 | return speed; 23 | } 24 | set 25 | { 26 | #if TOOLS 27 | if (Engine.EditorHint) 28 | _speed = VoltType.Serialize(value); 29 | else 30 | #endif 31 | speed = value; 32 | } 33 | } 34 | [Export(hintString: VoltPropertyHint.Fix64)] 35 | private byte[] _speed; 36 | 37 | public override void _Ready() 38 | { 39 | base._Ready(); 40 | speed = VoltType.DeserializeOrDefault(_speed); 41 | } 42 | 43 | public static readonly Fix64 FIX_0_0001 = Fix64.From("0.0001"); 44 | 45 | public override void _PhysicsProcess(float delta) 46 | { 47 | var movementInput = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down").ToVoltVector2(); 48 | if (movementInput != VoltVector2.Zero) 49 | FixedPosition += movementInput * speed; 50 | var rotation = Input.GetAxis("ui_left", "ui_right"); 51 | if (rotation != 0) 52 | FixedRotation += (Fix64)rotation * speed * Fix64.Deg2Rad; 53 | var scale = Input.GetAxis("ui_up", "ui_down"); 54 | if (scale != 0) 55 | FixedScale += VoltVector2.One * (Fix64)scale * speed * FIX_0_0001; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /tests/Manual/VoltNode2D/VolatileAreaTest.cs: -------------------------------------------------------------------------------- 1 | using Godot; 2 | using Volatile.GodotEngine; 3 | 4 | namespace Tests 5 | { 6 | [Tool] 7 | public class VolatileAreaTest : VolatileArea 8 | { 9 | [Export] 10 | private NodePath hasEntityInsideSpritePath; 11 | private Sprite hasEntityInsideSprite; 12 | 13 | public override void _Ready() 14 | { 15 | base._Ready(); 16 | if (Engine.EditorHint) return; 17 | hasEntityInsideSprite = GetNode(hasEntityInsideSpritePath); 18 | } 19 | 20 | protected override void OnBodyEntered(IVolatileBody body) 21 | { 22 | base.OnBodyEntered(body); 23 | OnBodyCollidingWithChanged(); 24 | } 25 | 26 | protected override void OnBodyExited(IVolatileBody body) 27 | { 28 | base.OnBodyExited(body); 29 | OnBodyCollidingWithChanged(); 30 | } 31 | 32 | private void OnBodyCollidingWithChanged() 33 | { 34 | hasEntityInsideSprite.Visible = CurrCollidingWith.Count > 0; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /tests/Manual/VoltNode2D/VolatileKinematicBodyMovement.cs: -------------------------------------------------------------------------------- 1 | using FixMath.NET; 2 | using Fractural.Utils; 3 | using Godot; 4 | using VirtualJoystickAddon; 5 | using Volatile; 6 | using Volatile.GodotEngine; 7 | 8 | namespace Tests 9 | { 10 | [Tool] 11 | public class VolatileKinematicBodyMovement : VolatileKinematicBody 12 | { 13 | private Fix64 speed; 14 | public Fix64 Speed 15 | { 16 | get 17 | { 18 | 19 | #if TOOLS 20 | if (Engine.EditorHint) 21 | return VoltType.DeserializeOrDefault(_speed); 22 | else 23 | #endif 24 | return speed; 25 | } 26 | set 27 | { 28 | #if TOOLS 29 | if (Engine.EditorHint) 30 | _speed = VoltType.Serialize(value); 31 | else 32 | #endif 33 | speed = value; 34 | } 35 | } 36 | [Export(hintString: VoltPropertyHint.Fix64)] 37 | private byte[] _speed; 38 | 39 | [Export] 40 | private NodePath joystickPath; 41 | private VirtualJoystick joystick; 42 | 43 | public override void _Ready() 44 | { 45 | base._Ready(); 46 | if (Engine.EditorHint) return; 47 | speed = VoltType.DeserializeOrDefault(_speed); 48 | joystick = this.GetNodeAsWrapper(joystickPath); 49 | } 50 | 51 | public override void _PhysicsProcess(float delta) 52 | { 53 | base._PhysicsProcess(delta); 54 | if (Engine.EditorHint) return; 55 | if (joystick.Output != Vector2.Zero) 56 | MoveAndSlide(joystick.Output.ToVoltVector2() * speed); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /tests/Manual/VoltNode2D/VoltNode2DTest.tscn: -------------------------------------------------------------------------------- 1 | [gd_scene load_steps=4 format=2] 2 | 3 | [ext_resource path="res://icon.png" type="Texture" id=1] 4 | [ext_resource path="res://tests/Manual/VoltNode2D/FixedMovement.cs" type="Script" id=2] 5 | [ext_resource path="res://addons/GodotFixedVolatilePhysics/Core/Test.cs" type="Script" id=3] 6 | 7 | [node name="VoltNode2DTest" type="Node2D"] 8 | 9 | [node name="Test" type="Node2D" parent="."] 10 | position = Vector2( 46.6128, 100.26 ) 11 | script = ExtResource( 3 ) 12 | fixed64Array = PoolByteArray( 4, 0, 0, 0, 8, 0, 0, 0, 0, 10, 0, 0, 0, 8, 0, 0, 0, 0, 4, 0, 0, 0, 8, 0, 0, 0, 0, 3, 0, 0, 0, 8, 0, 0, 0, 0, 223, 187, 11, 2 ) 13 | fixedVectorArray = PoolByteArray( 3, 0, 0, 0, 16, 50, 114, 22, 110, 47, 1, 0, 0, 239, 56, 69, 87, 230, 0, 0, 0, 16, 127, 159, 113, 89, 40, 0, 0, 0, 139, 108, 231, 59, 132, 36, 0, 0, 16, 9, 104, 34, 60, 9, 0, 0, 0, 200, 36, 35, 87, 24, 0, 0, 0 ) 14 | voltRectArray = PoolByteArray( 2, 0, 0, 0, 32, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 61, 10, 215, 99, 92, 0, 0, 0, 0, 0, 0, 0, 45, 9, 0, 0, 0, 0, 0, 0, 243, 16, 0, 0, 0, 0, 0, 0, 195, 207, 255, 255 ) 15 | voltRect = PoolByteArray( 160, 240, 35, 60, 29, 0, 0, 0, 204, 204, 204, 76, 214, 3, 0, 0, 0, 0, 0, 64, 166, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ) 16 | tests = "asdfasdf" 17 | 18 | [node name="FixedMovement" type="Node2D" parent="."] 19 | position = Vector2( 154.101, -25.905 ) 20 | rotation = 0.226891 21 | script = ExtResource( 2 ) 22 | _speed = PoolByteArray( 0, 0, 0, 0, 1, 0, 0, 0 ) 23 | _fixedTransform = PoolByteArray( 0, 90, 112, 249, 0, 0, 0, 0, 128, 63, 150, 57, 0, 0, 0, 0, 128, 192, 105, 198, 255, 255, 255, 255, 0, 90, 112, 249, 0, 0, 0, 0, 0, 0, 212, 25, 154, 0, 0, 0, 0, 128, 81, 24, 230, 255, 255, 255 ) 24 | 25 | [node name="Sprite" type="Sprite" parent="FixedMovement"] 26 | texture = ExtResource( 1 ) 27 | 28 | [node name="Camera2D" type="Camera2D" parent="FixedMovement"] 29 | current = true 30 | zoom = Vector2( 5, 5 ) 31 | 32 | [node name="Sprite2" type="Sprite" parent="."] 33 | self_modulate = Color( 1, 1, 1, 0.329412 ) 34 | position = Vector2( -103.881, 54.7806 ) 35 | texture = ExtResource( 1 ) 36 | --------------------------------------------------------------------------------