first commit

This commit is contained in:
Your Name
2026-05-09 17:39:45 +02:00
commit b2a0dd59a8
101 changed files with 1930 additions and 0 deletions

4
.editorconfig Normal file
View File

@@ -0,0 +1,4 @@
root = true
[*]
charset = utf-8

2
.gitattributes vendored Normal file
View File

@@ -0,0 +1,2 @@
# Normalize EOL for all files that Git considers text files.
* text=auto eol=lf

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
# Godot 4+ specific ignores
.godot/
/android/

19
LICENSE Normal file
View File

@@ -0,0 +1,19 @@
Copyright (C) 2025 Gil Barbosa Reis.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,161 @@
# Changelog
## [Unreleased](https://github.com/gilzoide/lua-gdextension/compare/0.6.0...HEAD)
## [0.6.0](https://github.com/gilzoide/lua-gdextension/releases/tag/0.6.0)
### Added
- Support for constructing typed arrays in Lua using the idiom `Array[some_type]()`
- Support for constructing typed dictionaries in Lua using the idiom `Dictionary[key_type][value_type]()`
- Support for typed arrays, typed dictionaries and classes in exported properties:
```lua
MyScript.exported_node_array = export(Array[Node])
MyScript.exported_int_valued_dict = export(Dictionary[Variant][int])
MyScript.exported_texture_property = export(Texture)
-- or
MyScript.exported_node_array = export({ type = Array[Node] })
MyScript.exported_int_valued_dict = export({ type = Dictionary[Variant][int] })
MyScript.exported_texture_property = export({ type = Texture })
```
- `is_instance_valid` utility function when opening `GODOT_UTILITY_FUNCTIONS` library
- Support for older Linux distros using GLIBC on par with Ubuntu 22.04
- Parser API based on Tree Sitter
+ Adds the `LuaParser`, `LuaAST`, `LuaASTNode` and `LuaASTQuery` classes
### Changed
- `LuaScriptInstance`'s data table is passed as `self` to methods instead of their owner `Object`
+ For this to work, the table now has a metatable to access its owner when necessary
- `LuaScript`s now have a "Import Behavior" property, defaulting to "Automatic"
+ In "Automatic" behavior, Lua code is evaluated only if it looks like a Godot script.
Lua code that looks like a Godot script is one that ends by returning a named variable (`return MyClassVariable`) or a table constructed inline (`return {...}`)
+ In "Always Evaluate" behavior, Lua code will always be evaluated
+ In "Don't Load" behavior, Lua code will not be loaded nor evaluated at all
+ Note that only evaluated scripts can be attached to Godot Objects.
- Variant and `LuaScriptInstance` methods are now converted to Callable, so they can be more easily passed to Godot APIs such as `Signal.connect`
```lua
-- Before this change, we had to manually instantiate Callable
some_signal:connect(Callable(self, "method_name"))
-- Now we can pass the method directly
some_signal:connect(self.method_name)
```
### Fixed
- Fixed cyclic references from `LuaScriptInstance` <-> `LuaState`, avoiding leaks of `LuaScript`s
- Fixed cyclic references from `LuaScriptProperty` <-> `LuaState`, avoiding memory leaks
- Support for built-in Variant types in exported properties when passed directly to `export`:
```lua
MyScript.exported_dictionary = export(Dictionary)
```
- Convert null Object Variants (`<Object#null>`) to `nil` when passing them to Lua
- Convert freed Object Variants (`<Freed Object>`) to `nil` when passing them to Lua
- Fixed `LuaJIT core/library version mismatch` errors in LuaJIT builds
- `LuaScriptResourceFormatLoader::_load` now respects the cache mode, fixing "Another resource is loaded from path 'res://...' (possible cyclic resource inclusion)." errors
- Error messages from Lua code using the wrong stack index
- Crashes when passing Lua primitives to `typeof`, `Variant.is`, `Variant.get_type`, `Variant.booleanize`, `Variant.duplicate`, `Variant.get_type_name`, `Variant.hash`, `Variant.recursive_hash` and `Variant.hash_compare`
- The `addons/lua-gdextension/build/.gdignore` file was added to the distributed build.
This fixes import errors when opening the Godot editor with the LuaJIT build.
## [0.5.0](https://github.com/gilzoide/lua-gdextension/releases/tag/0.5.0)
### Added
- Support for Linux arm64
- `LuaTable.get_metatable` and `LuaTable.set_metatable` methods
- Support for building with LuaJIT
- `LuaState.get_lua_runtime`, `LuaState.get_lua_version_num` and `LuaState.get_lua_version_string` methods
## [0.4.0](https://github.com/gilzoide/lua-gdextension/releases/tag/0.4.0)
### Added
- `LuaCoroutine.completed` and `LuaCoroutine.failed` signals
- `await` function similar to GDScript's, allowing coroutines to yield and resume automatically when a signal is emitted
- Support for Web exports
- Support for Windows arm64
- Support for calling static methods from Godot classes, like `FileAccess.open`
- Custom [Lua 5.4+ warning function](https://www.lua.org/manual/5.4/manual.html#lua_setwarnf) that sends messages to `push_warning`
- `LuaThread` class as a superclass for `LuaCoroutine`.
This new class is used when converting a LuaState's main thread to Variant.
- `LuaState.main_thread` property for getting a Lua state's main thread of execution
- Support for setting hooks to `LuaThread`s, including the main thread
### Changed
- `LuaObject` instances are reused when wrapping the same Lua object, so that `==` and `is_same` can be used properly
- The following methods of LuaScripts run in pooled coroutines, so that `await` can be used in them: regular method calls, setter functions, `_init`, `_notification`
- Godot 4.4 is now the minimum version necessary to use this addon
### Fixed
- Use `xcframework` instead of `dylib` in iOS exports
- Crash when Lua errors, but the error object is not a string
- Crash when reloading the GDExtension
## [0.3.0](https://github.com/gilzoide/lua-gdextension/releases/tag/0.3.0)
### Added
- Editor plugin that registers the Lua REPL tab, where you can try Lua code using an empty `LuaState`
- Support for calling Godot String methods using Lua strings
- Optional support for `res://` and `user://` relative paths in package searchers, `loadfile` and `dofile`.
Open the `GODOT_LOCAL_PATHS` library to activate this behavior.
- `LuaState.LoadMode` enum for specifying the Lua load mode: text, binary or any
- `LuaState.do_buffer` and `LuaState.load_buffer` methods for loading Lua code from possibly binary chunks
- `LuaState.package_path` and `LuaState.package_cpath` properties for accessing the value of Lua's [`package.path`](https://www.lua.org/manual/5.4/manual.html#pdf-package.path) and [`package.cpath`](https://www.lua.org/manual/5.4/manual.html#pdf-package.cpath)
- `LuaState.get_lua_exec_dir` static method to get the executable directory used to replace "!" when setting `package_path` and `package_cpath` properties.
When running in the Godot editor, it returns the globalized version of `res://` path.
Otherwise, it returns the base directory of the executable.
- Advanced project settings for setting the `LuaScriptLanguage` state's `package_path` and `package_cpath` properties
- `LuaState.are_libraries_opened` method for checking if a subset of libraries were already opened
- `LuaState.create_function` method for creating a `LuaFunction` from a `Callable`
- API documentation is now available in the Godot editor
### Changed
- The GDExtension is now marked as reloadable
- Renamed `LuaCoroutine::LuaCoroutineStatus` to `LuaCoroutine::Status`
- `LuaState.load_file` and `LuaState.do_file` now receive the load mode instead of buffer size
- `Callable` values when passed to Lua are wrapped as Lua functions when `GODOT_VARIANT` library is not opened, making it possible to call them in sandboxed environments
- Lua is now compiled as C++
### Removed
- `VariantType::has_static_method` internal method
### Fixed
- Bind `LuaCoroutine::status` property with correct enum type
- Bind `LuaError::status` property as int with correct enum type
- Crash when calling utility functions from Lua
- Compilation for Windows using MSVC
## [0.2.0](https://github.com/gilzoide/lua-gdextension/releases/tag/0.2.0)
### Added
- Lua is now available as a scripting language for Godot objects, so that you can create your games entirely in Lua!
- `LuaObject.get_lua_state` method for getting the `LuaState` of a Lua object
- `LuaTable.clear` method
- `LuaTable.rawget` and `LuaTable.rawset` methods that don't trigger metamethods
- `LuaFunction.to_callable` method to easily turn Lua functions to Callable
- `LuaState.load_string` and `LuaState.load_files` for loading Lua code without executing it
- Support for passing a `LuaTable` as `_ENV` in `LuaState.do_string` and `LuaState.do_file`
- Support for `PackedVector4Array` variant type
- "Bouncing Logo" sample scene
### Changed
- Minimum Godot version supported is now 4.3
- Android target API changed to 21 (Android Lollipop 5.0)
- In Lua, `print` is now bound to Godot's `printt` to match Lua's behavior of adding `\t` between passed arguments
### Removed
- `LuaTable.get_value` and `LuaTable.set_value`, use `LuaTable.get` and `LuaTable.set` instead
### Fixed
- Use `PROPERTY_USAGE_NONE` for `LuaState.globals` and `LuaState.registry`, fixing instance leaks
- Lua stack handling in `LuaTable` and utility function wrapper code, fixing crashes
- `typeof` utility function now returns a `VariantType` instead of a value unusable by Lua
- Lua objects coming from a different `LuaState` are passed as Variants to Lua instead of being unwrapped, fixing crashes
## [0.1.0](https://github.com/gilzoide/lua-gdextension/releases/tag/0.1.0)
### Added
- `LuaState` class for holding a Lua state and interacting with it.
You may create as many instances as you want, each one representing an independent Lua state.
- `LuaCoroutine`, `LuaFunction`, `LuaLightUserdata`, `LuaTable` and `LuaUserdata` classes that wrap instances from a Lua state in Godot.
- `LuaError` class that represents errors from Lua code.
- Support for registering `Variant` type in Lua states, so that any Godot data can be manipulated in Lua.
- Support for registering Godot classes in Lua, so you can create instances and access integer constants.
- Support for adding access to Godot singleton objects in Lua, accessible directly by name.
- Support for registering Godot utility functions in Lua, like `print`, `lerp` and `is_same`.
- Support for adding access to Godot global enums in Lua, like `OK`, `TYPE_STRING` and `SIDE_LEFT`.

View File

@@ -0,0 +1,19 @@
Copyright (C) 2025 Gil Barbosa Reis.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,3 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M28 13.3333C21.9884 13.3333 16.2231 15.7214 11.9722 19.9722C7.72142 24.2231 5.33333 29.9884 5.33333 36C5.33333 42.0116 7.72142 47.7769 11.9722 52.0278C16.2231 56.2786 21.9884 58.6667 28 58.6667C30.9766 58.6667 33.9241 58.0804 36.6742 56.9413C39.4242 55.8022 41.923 54.1325 44.0278 52.0278C46.1325 49.923 47.8022 47.4242 48.9413 44.6742C50.0804 41.9241 50.6667 38.9766 50.6667 36C50.6667 29.9884 48.2786 24.2231 44.0278 19.9722C39.7769 15.7214 34.0116 13.3333 28 13.3333ZM36 34.6667C34.2319 34.6667 32.5362 33.9643 31.2859 32.714C30.0357 31.4638 29.3333 29.7681 29.3333 28C29.3333 26.2319 30.0357 24.5362 31.2859 23.286C32.5362 22.0357 34.2319 21.3333 36 21.3333C37.7681 21.3333 39.4638 22.0357 40.714 23.286C41.9643 24.5362 42.6667 26.2319 42.6667 28C42.6667 29.7681 41.9643 31.4638 40.714 32.714C39.4638 33.9643 37.7681 34.6667 36 34.6667ZM52 5.33333C50.2319 5.33333 48.5362 6.03571 47.2859 7.28595C46.0357 8.5362 45.3333 10.2319 45.3333 12C45.3333 13.7681 46.0357 15.4638 47.2859 16.714C48.5362 17.9643 50.2319 18.6667 52 18.6667C52.8755 18.6667 53.7424 18.4942 54.5512 18.1592C55.3601 17.8242 56.095 17.3331 56.714 16.714C57.3331 16.095 57.8242 15.3601 58.1592 14.5512C58.4942 13.7424 58.6667 12.8755 58.6667 12C58.6667 10.2319 57.9643 8.5362 56.714 7.28595C55.4638 6.03571 53.7681 5.33333 52 5.33333Z" fill="#E0E0E0"/>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://de52k4kdp6s7y"
path="res://.godot/imported/LuaScript_icon.svg-e936c81a8c0f686739c63104e1bb3341.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/lua-gdextension/LuaScript_icon.svg"
dest_files=["res://.godot/imported/LuaScript_icon.svg-e936c81a8c0f686739c63104e1bb3341.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@@ -0,0 +1,239 @@
# Lua GDExtension
[![Godot Asset Library page](https://img.shields.io/static/v1?logo=godotengine&label=asset%20library%20%28Lua%205.4%29&color=478CBF&message=0.6.0)](https://godotengine.org/asset-library/asset/2330)
[![Godot Asset Library page](https://img.shields.io/static/v1?logo=godotengine&label=asset%20library%20%28LuaJIT%29&color=478CBF&message=0.6.0)](https://godotengine.org/asset-library/asset/2330)
[![Build and Test workflow](https://github.com/gilzoide/lua-gdextension/actions/workflows/build.yml/badge.svg)](https://github.com/gilzoide/lua-gdextension/actions/workflows/build.yml)
<img src="addons/lua-gdextension/icon.png" alt="Lua GDExtension icon" width="150" height="150"/>
Extension for using the [Lua programming language](https://www.lua.org/) in Godot 4.4+
With this addon, you can program your game or application directly in Lua.
You can also create sandboxed Lua states for external modding/scripting support, as many as necessary.
This plugin is available in the Asset Library as:
- [Lua GDExtension](https://godotengine.org/asset-library/asset/2330) (Lua 5.4 version)
- [Lua GDExtension + LuaJIT](https://godotengine.org/asset-library/asset/4119) (LuaJIT version)
## Features
- Create Godot scripts directly in Lua, making it possible to use Lua as an alternative to GDScript or C#
- Create additional Lua states for external modding/scripting support, as many as necessary
- Manage Lua tables, functions and coroutines directly from GDScript, C# or any other scripting language in Godot
- Select which Lua libraries and Godot APIs will be available per Lua state, making sandboxing easier:
+ Lua libraries are the same as Lua/C, like `base`, `package` and `io` libraries
+ Godot APIs:
+ Create and manipulate Variant values
+ Instantiate objects and access class constants
+ Access singleton objects by name
+ Global utility functions, like Godot's `print_rich`, `lerp` and `is_same`.
Also adds an `await` function that mimics GDScript's `await` keyword (only supported when running in a coroutine).
+ Global enums, like `OK`, `TYPE_STRING` and `SIDE_LEFT`
+ Patch Lua `package.searchers`, `require`, `loadfile` and `dofile` to accept paths relative to `res://` and `user://`
- Editor plugin with Lua REPL for testing out Lua snippets
- Choose between Lua 5.4 or LuaJIT v2.1 runtimes (distributed as separate addons)
+ Note: LuaJIT does not support WebAssebly, so Lua 5.4 is always used in Web platform
## Lua scripting in Godot
This addon registers a [ScriptLanguageExtension](https://docs.godotengine.org/en/stable/classes/class_scriptlanguageextension.html) so that Godot objects can be scripted directly in Lua.
For Lua scripts to be usable in Nodes and Resources, they must return a table with the script metadata containing methods, properties, signals, etc...
```lua
-- This is our script metadata table.
--
-- It stores metadata such as its base class, global class_name, icon,
-- as well as any declared properties, methods and signals
local LuaBouncingLogo = {
-- base class (optional, defaults to RefCounted)
extends = Sprite2D,
-- if true, allow the script to be executed by the editor (optional)
tool = false,
-- global class name (optional)
class_name = "LuaBouncingLogo",
-- Declare properties
linear_velocity = export(100),
initial_angle = export({
type = float,
default = 0,
hint = PROPERTY_HINT_RANGE,
hint_string = "0,360,degrees"
}),
-- Declare signals
bounced = signal(),
}
-- Called when the node enters the scene tree for the first time.
function LuaBouncingLogo:_ready()
self.position = self:get_viewport():get_size() / 2
self.movement = Vector2(self.linear_velocity, 0):rotated(deg_to_rad(self.initial_angle))
-- To connect a signal in Lua, you can use the method name just like in GDScript
self.bounced:connect(self._on_bounced)
end
-- Called every frame. 'delta' is the elapsed time since the previous frame.
function LuaBouncingLogo:_process(delta)
local viewport_size = self:get_viewport():get_size()
local viewport_rect = Rect2(Vector2(), viewport_size)
if not viewport_rect:encloses(self.global_transform * self:get_rect()) then
self.movement = self.movement:rotated(deg_to_rad(90))
self.bounced:emit()
end
self.position = self.position + self.movement * delta
end
function LuaBouncingLogo:_on_bounced()
print("Bounced =D")
end
-- Return the metadata table for the script to be usable by Godot objects
return LuaBouncingLogo
```
## Calling Lua from Godot
The following classes are registered in Godot for creating Lua states and interacting with them: `LuaState`, `LuaTable`, `LuaUserdata`, `LuaLightUserdata`, `LuaFunction`, `LuaCoroutine`, `LuaThread`, `LuaDebug` and `LuaError`.
Usage example in GDScript:
```gdscript
# 1. Create a Lua state
var lua = LuaState.new()
# 2. Import Lua and Godot APIs into the state
# Optionally pass which libraries should be opened to the method
lua.open_libraries()
# 3. Run Lua code using `LuaState.do_string` or `LuaState.do_file`
var result = lua.do_string("""
local vector = Vector2(1, 2)
return {
this_is_a_table = true,
vector = vector,
}
""")
# 4. Access results from Lua code directly in Godot
# When errors occur, instances of `LuaError` will be returned
if result is LuaError:
printerr("Error in Lua code: ", result)
else:
print(result) # [LuaTable:0x556069ee50ab]
print(result["this_is_a_table"]) # true
print(result["vector"]) # (1, 2)
print(result["invalid key"]) # <null>
# 5. Access the global _G table via `LuaState.globals` property
assert(lua.globals is LuaTable)
lua.globals["a_godot_callable"] = func(): print("Hello from GDScript!")
lua.do_string("""
a_godot_callable() -- 'Hello from GDScript!'
""")
```
## Calling Godot from Lua
- Instantiate and manipulate Godot objects, just like in GDScript.
```lua
local v = Vector3(1, 2, 3)
print(v.x) -- 1
-- Note: use ":" instead of "." to call methods in Lua
print(v:length()) -- 3.74165749549866
local n = Node:new()
print(n:is_inside_tree()) -- false
n:queue_free()
```
- Typed Arrays and Dictionaries are also supported
```lua
-- Element type: int
local int_array = Array[int]()
-- Element type: Node
local node_array = Array[Node]()
-- Key: int, Value: bool
local int_to_bool_dict = Dictionary[int][bool]()
-- Key: Variant, Value: Node
local node_valued_dict = Dictionary[Variant][Node]()
```
- Call Godot utility functions.
```lua
local d1 = Dictionary()
local d2 = Dictionary()
print(is_same(d1, d2)) -- false
print(is_same(d2, d2)) -- true
print(lerp(5, 10, 0.15)) -- 5.75
```
- Access singleton objects by name.
```lua
assert(OS == Engine:get_singleton("OS"))
```
- Construct Array/Dictionary using Lua tables.
```lua
local array = Array{ "value 0", "value 1" }
-- Godot Arrays are indexed from 0, instead of 1
print(array[0]) -- "value 0"
print(array[1]) -- "value 1"
print(array[2]) -- nil
local dict = Dictionary{ hello = "world" }
print(dict) -- { "hello": "world" }
print(dict["hello"]) -- "world"
print(dict["invalid key"]) -- nil
```
- Iterate over values using `pairs` for types that support it, like Arrays, packed arrays, Dictionaries and some math types.
```lua
local dictionary = Dictionary{ hello = "world", key = "value" }
for key, value in pairs(dictionary) do
print(key .. ": " .. value)
end
```
- Length operator (`#`) as a shortcut for calling the `size()` method in any object that supports it.
```lua
local array = Array{ 1, 2, 3, 4 }
print(#array) -- 4
```
- Runtime type check using the `Variant.is` method.
```lua
local array = Array()
print(Variant.is(array, Array)) -- true
print(Variant.is(array, 'Array')) -- true
-- Also available using the method notation from Variant objects
print(array:is(Array)) -- true
print(array:is(Dictionary)) -- false
print(array:is(RefCounted)) -- false
```
- Making protected calls using `pcall`.
```lua
local v = Vector2(1, 2)
print(v:pcall('length')) -- true 2.2360680103302
print(v:pcall('invalid method')) -- false "Invalid method"
```
## TODO
- [X] Bind Variant types to Lua
- [X] Bind utility functions to Lua
- [X] Bind enums and constants to Lua
- [X] Add support for getting global singletons from Lua
- [X] Add support for getting classes from Lua
- [X] Add optional support for `res://` relative paths in `require`, `loadfile` and `dofile`
- [X] Add support for `await`ing signals
- [X] Submit to Asset Library
- [X] Lua ScriptLanguageExtension
+ [X] Add support for property hints / usage flags (including export)
+ [X] Add support for property getter / setter
+ [ ] Add `export_*` functions mimicking GDScript annotations for better UX
- [X] Support for building with LuaJIT
- [X] Support WebAssembly platform
- [X] Support Windows arm64 platform
- [X] Support Linux arm64 platform
- [ ] Support Linux arm32 and rv64 platform
- [X] Use framework in iOS (possibly a xcframework supporting the iOS simulator as well)
- [X] Automated unit tests
- [X] Automated build and distribution
- [X] Lua REPL editor plugin
## Other projects for using Lua in Godot 4
- https://github.com/WeaselGames/godot_luaAPI
- https://github.com/perbone/luascript

View File

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>BinaryPath</key>
<string>libgodot-cpp.ios.template_debug.universal.a</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>libgodot-cpp.ios.template_debug.universal.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>BinaryPath</key>
<string>libgodot-cpp.ios.template_release.universal.a</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>libgodot-cpp.ios.template_release.universal.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>BinaryPath</key>
<string>libluagdextension.ios.template_debug.universal.a</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>libluagdextension.ios.template_debug.universal.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>

View File

@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>AvailableLibraries</key>
<array>
<dict>
<key>BinaryPath</key>
<string>libluagdextension.ios.template_release.universal.a</string>
<key>LibraryIdentifier</key>
<string>ios-arm64</string>
<key>LibraryPath</key>
<string>libluagdextension.ios.template_release.universal.a</string>
<key>SupportedArchitectures</key>
<array>
<string>arm64</string>
</array>
<key>SupportedPlatform</key>
<string>ios</string>
</dict>
</array>
<key>CFBundlePackageType</key>
<string>XFWK</string>
<key>XCFrameworkFormatVersion</key>
<string>1.0</string>
</dict>
</plist>

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cih6ia8rwpfa3"
path="res://.godot/imported/icon.png-a1b6b69ff83c17f55db0a93e5e3a4f5d.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/lua-gdextension/icon.png"
dest_files=["res://.godot/imported/icon.png-a1b6b69ff83c17f55db0a93e5e3a4f5d.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View File

@@ -0,0 +1,134 @@
# Copyright (C) 2025 Gil Barbosa Reis.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the “Software”), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
extends Node
@onready var _output: RichTextLabel = $Output
@onready var _input: LineEdit = $Footer/Input
@onready var _history_popup: PopupMenu = $Header/HistoryButton.get_popup()
var _lua: LuaState
var _history = PackedStringArray()
var _current_history = 0
func _ready():
_history_popup.about_to_popup.connect(_on_history_popup_about_to_popup)
_history_popup.id_pressed.connect(_on_history_popup_id_pressed)
reset()
func reset():
_lua = LuaState.new()
_lua.open_libraries()
_lua.registry.print = _printn
_lua.do_string(r"print = function(...) debug.getregistry().print(table.concat({...}, '\t')) end")
_history.clear()
_current_history = 0
clear()
func do_string(text: String):
text = text.strip_edges()
if text.is_empty():
return
_history.append(text)
_current_history = _history.size()
_input.clear()
_printn(text)
# support for "= value" idiom from Lua 5.1 REPL
text.trim_prefix("=")
var result = _lua.do_string("return " + text)
if result is LuaError:
result = _lua.do_string(text)
if result is LuaError:
_print_error(result.message)
else:
_printn("Out[%d]: %s" % [_current_history, result])
_prompt()
func clear():
_output.clear()
_prompt()
func set_history(index: int):
if index < 0 or index >= _history.size():
return
_current_history = index
var text = _history[index]
_input.text = text
_input.caret_column = text.length()
func _prompt():
_print("\nIn [%d]: " % [_current_history + 1])
func _print(msg: String):
self._output.add_text(msg)
func _printn(msg: String):
_print(msg)
_print("\n")
func _print_error(msg: String):
var color: Color = EditorInterface.get_editor_settings().get_setting("text_editor/theme/highlighting/brace_mismatch_color")
self._output.append_text("[color=%s]%s[/color]\n" % [color.to_html(), msg.replace("[", "[lb]")])
func _on_history_popup_about_to_popup():
_history_popup.clear()
for line in _history:
_history_popup.add_item(line)
func _on_history_popup_id_pressed(id: int):
set_history(id)
func _on_input_text_submitted(new_text: String):
do_string(new_text)
func _on_run_button_pressed():
do_string(_input.text)
func _on_input_gui_input(event: InputEvent):
var key_event = event as InputEventKey
if not key_event or not key_event.pressed:
return
if key_event.keycode == KEY_UP:
set_history(_current_history - 1)
get_viewport().set_input_as_handled()
elif key_event.keycode == KEY_DOWN:
set_history(_current_history + 1)
get_viewport().set_input_as_handled()

View File

@@ -0,0 +1 @@
uid://bjod0yq2efea8

View File

@@ -0,0 +1,59 @@
[gd_scene load_steps=2 format=3 uid="uid://4lq5s4lnqg8c"]
[ext_resource type="Script" uid="uid://bjod0yq2efea8" path="res://addons/lua-gdextension/lua_repl.gd" id="1_gf8ka"]
[node name="LuaRepl" type="VBoxContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_gf8ka")
[node name="Header" type="HBoxContainer" parent="."]
layout_mode = 2
[node name="Title" type="Label" parent="Header"]
layout_mode = 2
size_flags_horizontal = 3
text = "Lua REPL"
[node name="HistoryButton" type="MenuButton" parent="Header"]
layout_mode = 2
text = "History"
flat = false
[node name="ResetButton" type="Button" parent="Header"]
layout_mode = 2
tooltip_text = "Reset the Lua environment and REPL history"
text = "Reset"
[node name="ClearButton" type="Button" parent="Header"]
layout_mode = 2
tooltip_text = "Clear the output text"
text = "Clear"
[node name="Output" type="RichTextLabel" parent="."]
layout_mode = 2
size_flags_vertical = 3
focus_mode = 2
scroll_following = true
selection_enabled = true
[node name="Footer" type="HBoxContainer" parent="."]
layout_mode = 2
[node name="Input" type="LineEdit" parent="Footer"]
layout_mode = 2
size_flags_horizontal = 3
keep_editing_on_text_submit = true
[node name="RunButton" type="Button" parent="Footer"]
layout_mode = 2
text = "Run"
[connection signal="pressed" from="Header/ResetButton" to="." method="reset"]
[connection signal="pressed" from="Header/ClearButton" to="." method="clear"]
[connection signal="gui_input" from="Footer/Input" to="." method="_on_input_gui_input"]
[connection signal="text_submitted" from="Footer/Input" to="." method="_on_input_text_submitted"]
[connection signal="pressed" from="Footer/RunButton" to="." method="_on_run_button_pressed"]

View File

@@ -0,0 +1,45 @@
[configuration]
entry_symbol = "luagdextension_entrypoint"
compatibility_minimum = "4.4"
reloadable = true
[libraries]
macos.debug = "build/libluagdextension.macos.template_debug.universal.dylib"
macos.release = "build/libluagdextension.macos.template_release.universal.dylib"
ios.debug = "build/libluagdextension.ios.template_debug.universal.xcframework"
ios.release = "build/libluagdextension.ios.template_release.universal.xcframework"
windows.debug.x86_32 = "build/libluagdextension.windows.template_debug.x86_32.dll"
windows.release.x86_32 = "build/libluagdextension.windows.template_release.x86_32.dll"
windows.debug.x86_64 = "build/libluagdextension.windows.template_debug.x86_64.dll"
windows.release.x86_64 = "build/libluagdextension.windows.template_release.x86_64.dll"
windows.debug.arm64 = "build/libluagdextension.windows.template_debug.arm64.dll"
windows.release.arm64 = "build/libluagdextension.windows.template_release.arm64.dll"
linux.debug.x86_32 = "build/libluagdextension.linux.template_debug.x86_32.so"
linux.release.x86_32 = "build/libluagdextension.linux.template_release.x86_32.so"
linux.debug.x86_64 = "build/libluagdextension.linux.template_debug.x86_64.so"
linux.release.x86_64 = "build/libluagdextension.linux.template_release.x86_64.so"
linux.debug.arm64 = "build/libluagdextension.linux.template_debug.arm64.so"
linux.release.arm64 = "build/libluagdextension.linux.template_release.arm64.so"
android.debug.x86_32 = "build/libluagdextension.android.template_debug.x86_32.so"
android.release.x86_32 = "build/libluagdextension.android.template_release.x86_32.so"
android.debug.x86_64 = "build/libluagdextension.android.template_debug.x86_64.so"
android.release.x86_64 = "build/libluagdextension.android.template_release.x86_64.so"
android.debug.arm32 = "build/libluagdextension.android.template_debug.arm32.so"
android.release.arm32 = "build/libluagdextension.android.template_release.arm32.so"
android.debug.arm64 = "build/libluagdextension.android.template_debug.arm64.so"
android.release.arm64 = "build/libluagdextension.android.template_release.arm64.so"
web.debug.threads.wasm32 = "build/libluagdextension.web.template_debug.wasm32.wasm"
web.release.threads.wasm32 = "build/libluagdextension.web.template_release.wasm32.wasm"
web.debug.wasm32 = "build/libluagdextension.web.template_debug.wasm32.nothreads.wasm"
web.release.wasm32 = "build/libluagdextension.web.template_release.wasm32.nothreads.wasm"
[icons]
LuaScript = "LuaScript_icon.svg"
[dependencies]
ios.debug = {
"build/libgodot-cpp.ios.template_debug.universal.xcframework": ""
}
ios.release = {
"build/libgodot-cpp.ios.template_release.universal.xcframework": ""
}

View File

@@ -0,0 +1 @@
uid://d3k6wyksykaj2

View File

@@ -0,0 +1,7 @@
[plugin]
name="Lua GDExtension"
description="Tools for Lua GDExtension: REPL tab"
author="gilzoide"
version="0.6.0"
script="plugin.gd"

View File

@@ -0,0 +1,37 @@
# Copyright (C) 2025 Gil Barbosa Reis.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the “Software”), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
# of the Software, and to permit persons to whom the Software is furnished to do
# so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
@tool
extends EditorPlugin
var _lua_repl: Control
func _enter_tree():
_lua_repl = preload("lua_repl.tscn").instantiate()
add_control_to_bottom_panel(_lua_repl, "Lua REPL")
func _exit_tree():
if _lua_repl:
remove_control_from_bottom_panel(_lua_repl)
_lua_repl.queue_free()
_lua_repl = null

View File

@@ -0,0 +1 @@
uid://cwp2hwkpbitgx

5
assetcfg.json Normal file
View File

@@ -0,0 +1,5 @@
{
"grass":{"allowRandRot":true,"textname":"grass.png","collidable":false},
"wood":{"allowRandRot":false,"textname":"wood.png","collidable":false},
"woodbarrier":{"allowRandRot":false,"textname":"wood.png","collidable":true},
}

29
code/lua_env.lua Normal file
View File

@@ -0,0 +1,29 @@
local lua_env = {
extends = Node,
env = {}
}
-- optional helper to reset environment
function lua_env:reset_env()
self.env = {}
setmetatable(self.env, { __index = _G }) -- fall back to globals
end
-- the core runscript
function lua_env:run_script(code, node)
local chunk, err = load(code, "runscript", "t", self.env)
if not chunk then
print("[LuaRuntime] Syntax error: ", err)
return false
end
-- Call the chunk, optionally passing context
local ok, result = pcall(chunk, node)
if not ok then
print("[LuaRuntime] Runtime error: ", result)
return false
end
return result
end
return lua_env

1
code/lua_env.lua.uid Normal file
View File

@@ -0,0 +1 @@
uid://c01rdot180cne

50
code/player.gd Normal file
View File

@@ -0,0 +1,50 @@
extends CharacterBody2D
var speed = 16*16; # godot transformation per second
# Called when the node enters the scene tree for the first time.
var texts = [];
var lastChange = 0;
var hit = 0;
func _ready() -> void:
RenderingServer.set_default_clear_color(Color.BLACK)
var root = get_node("../")
var texturespace = root.get_meta("resourcepack");
for text in ["player-w1","player-w2"]:
texts.append(load("res://"+texturespace+"/text/"+text+".png"))
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _physics_process(delta: float) -> void:
var movedelta = Vector2.ZERO
if not self.get_meta("can_walk"):
return
if Input.is_action_pressed("movedown"):
movedelta.y += 1
if Input.is_action_pressed("moveup"):
movedelta.y -= 1
if Input.is_action_pressed("moveleft"):
movedelta.x -= 1
if Input.is_action_pressed("moveright"):
movedelta.x += 1
if movedelta.length() > 0:
movedelta = movedelta.normalized() * speed
move_and_collide(Vector2((movedelta * delta).x,0))
move_and_collide(Vector2(0,(movedelta * delta).y))
lastChange += delta
if lastChange > 0.4:
lastChange = 0
hit += 1
if hit > texts.size()-1:
hit = 0
get_node("Sprite2D").texture = texts[hit]
get_node("Walk").play()
func _process(delta: float) -> void:
var camera = get_node("../cam")
var campos: Vector2 = camera.position
camera.position = campos.lerp(self.position, 1.0 - exp(-8 * delta))

1
code/player.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://cc4y82skj2obl

205
code/world.gd Normal file
View File

@@ -0,0 +1,205 @@
extends Area2D
# GLOBAL CFG
@onready var fade_rect: ColorRect = get_node("../Fader/ColorRect")
func fade_out(duration := 1.0):
fade_rect.visible = true
fade_rect.modulate.a = 0.0
var tween = get_tree().create_tween()
tween.tween_property(fade_rect, "modulate:a", 1.0, duration)
func fade_in(duration := 1.0):
fade_rect.visible = true
fade_rect.modulate.a = 1.0
var tween = get_tree().create_tween()
tween.tween_property(fade_rect, "modulate:a", 0.0, duration)
tween.tween_callback(Callable(self, "_on_fade_in_complete"))
func wait(seconds: float) -> void:
await get_tree().create_timer(seconds).timeout
func _on_fade_in_complete():
fade_rect.visible = false
const scalef = 3;
const textsize = 16;
func load_json(path: String): # stolen from reddit then updated to godot 4 lmao
var file := FileAccess.open(path, FileAccess.READ)
var content = file.get_as_text()
file.close()
var parsed_json = JSON.parse_string(content)
return parsed_json
func loadstring(path: String): # stolen from reddit then updated to godot 4 lmao
var file := FileAccess.open(path, FileAccess.READ)
var content = file.get_as_text()
file.close()
return content
var rng = RandomNumberGenerator.new()
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
await fade_in(1.0)
get_node("../Player").set_meta("can_walk",true)
for child in get_children(): child.queue_free()
var root = get_node("../")
var world = load_json("res://worlds/"+self.get_meta("world")+".json");
if world.has("mapscript"):
var script = loadstring("res://worlds/"+self.get_meta("world")+".lua")
get_node("../LuaEnv").reset_env()
get_node("../LuaEnv").run_script(script,self)
var assetcfg = load_json("res://assetcfg.json")
var texturespace = root.get_meta("resourcepack");
var worldassemble = {};
var plr = get_node("../Player")
plr.position = Vector2(world["pspawn"]["w"]*(16*scalef),world["pspawn"]["h"]*(16*scalef))
for y in range(world["height"]+1):
for x in range(world["width"]+1):
if not worldassemble.has(y):
worldassemble[y] = {}
worldassemble[y][x] = {"thing":world["fill"],"action":"none"}
for modifier in world["features"]:
var x = int(modifier["w"])
var y = int(modifier["h"])
worldassemble[y][x] = modifier["t"]
# assuming this script extends Node2D or Area2D that builds the world
for y in range(worldassemble.size()):
for x in range(worldassemble[y].size()):
var along = worldassemble[y][x]
var walpole = along["thing"]
var cfg = assetcfg[walpole]
# combine base data from cfg and instance-specific data from along
var tile_type = along.get("action", cfg.get("action", "default"))
var is_collidable = along.get("collidable", cfg.get("collidable", false))
var tex_path = "res://%s/text/%s" % [texturespace, cfg["textname"]]
var tex = load(tex_path)
# --- choose node type
var tile_node: Node2D
if tile_type == "tp":
tile_node = Area2D.new() # teleporter
elif is_collidable:
tile_node = StaticBody2D.new() # solid
else:
tile_node = Area2D.new() # non-collidable sensor/decor
add_child(tile_node)
tile_node.position = Vector2(x * (scalef * textsize), y * (scalef * textsize))
tile_node.name = "tile_%s_%s" % [x, y]
tile_node.set_meta("collidable", is_collidable)
# --- sprite
var sprite = Sprite2D.new()
sprite.texture = tex
sprite.scale = Vector2(scalef, scalef)
tile_node.add_child(sprite)
# optional random rotation
if cfg.get("allowRandRot", false):
sprite.rotation_degrees = 90 * rng.randi_range(0, 3)
# --- collision shape for solids and sensors
if is_collidable or tile_type == "tp":
var col = CollisionShape2D.new()
var shape = RectangleShape2D.new()
shape.size = Vector2(16 * scalef, 16 * scalef)
col.shape = shape
col.position = Vector2.ZERO
tile_node.add_child(col)
# --- teleporter setup
if tile_type == "tp":
var target_world = along.get("target_world", cfg.get("target_world", "default"))
tile_node.set_meta("world", target_world)
tile_node.connect("body_entered", Callable(self, "_on_tp_entered").bind(tile_node))
_add_world_borders(world["width"], world["height"])
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
pass
func _on_tp_entered(body: Node, source: Node) -> void:
if not body.is_in_group("player"):
return
print("Teleport triggered by:", body.name)
await fade_out(1.0)
get_node("../Player").set_meta("can_walk",false)
await wait(1.0)
if body.has_method("set_velocity"):
body.set_velocity(Vector2.ZERO)
if source.has_meta("world"):
var new_world = source.get_meta("world")
print("Teleporting to world:", new_world)
set_meta("world", new_world)
call_deferred("_ready") # safely rebuild
else:
print("⚠️ Teleporter node has no 'world' meta set!")
func _add_world_borders(width: int, height: int) -> void:
var border_thickness = 32.0 * scalef
var w_px = width * (textsize * scalef)
var h_px = height * (textsize * scalef)
var positions = {
"top": Vector2(w_px / 2, -border_thickness / 2),
"bottom": Vector2(w_px / 2, h_px + border_thickness / 2),
"left": Vector2(-border_thickness / 2, h_px / 2),
"right": Vector2(w_px + border_thickness / 2, h_px / 2)
}
for name in positions.keys():
var wall = StaticBody2D.new()
wall.name = "border_%s" % name
var shape = RectangleShape2D.new()
if name in ["top", "bottom"]:
shape.size = Vector2(w_px + border_thickness, border_thickness)
else:
shape.size = Vector2(border_thickness, h_px + border_thickness)
var col = CollisionShape2D.new()
col.shape = shape
wall.position = positions[name]
wall.add_child(col)
add_child(wall)
@onready var label: RichTextLabel = null
@onready var uitypebox: ColorRect = null
var typing_speed := 0.03 # seconds between characters
var typing = false # to prevent overlapping animations
func ui_showmesg(t: String) -> void:
label = get_node("../UI/bg/RichTextLabel")
uitypebox = get_node("../UI/bg")
if typing:
return # skip if already typing (optional safeguard)
uitypebox.visible = true
typing = true
print(label, " valid? ", is_instance_valid(label))
label.text = t
label.visible_characters = 0
await _type_text(t)
await wait(4)
label = get_node("../UI/bg/RichTextLabel")
uitypebox = get_node("../UI/bg")
label.visible_characters = 0
uitypebox.visible = false
typing = false
func _type_text(t: String) -> void:
var i := 0
while i < t.length():
i += 1
label.visible_characters = i
await wait(typing_speed)

1
code/world.gd.uid Normal file
View File

@@ -0,0 +1 @@
uid://be6h32vns32hm

BIN
default/aud/epik.ogg Normal file

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://s3dk5wcpewdd"
path="res://.godot/imported/epik.ogg-f995cde1eaba93014784f659c6e89a2b.oggvorbisstr"
[deps]
source_file="res://default/aud/epik.ogg"
dest_files=["res://.godot/imported/epik.ogg-f995cde1eaba93014784f659c6e89a2b.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
default/aud/pam.wav Normal file

Binary file not shown.

View File

@@ -0,0 +1,24 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://bbirs8atmuo1k"
path="res://.godot/imported/pam.wav-45b7a37afabf841cc9a8d9efb2a9febf.sample"
[deps]
source_file="res://default/aud/pam.wav"
dest_files=["res://.godot/imported/pam.wav-45b7a37afabf841cc9a8d9efb2a9febf.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=2

BIN
default/aud/pew.ogg Normal file

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://vv4jb2e8f1uo"
path="res://.godot/imported/pew.ogg-c255adcf287c8d5e866c681e2e1494e8.oggvorbisstr"
[deps]
source_file="res://default/aud/pew.ogg"
dest_files=["res://.godot/imported/pew.ogg-c255adcf287c8d5e866c681e2e1494e8.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
default/aud/walk.ogg Normal file

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bcca08s3kqhkf"
path="res://.godot/imported/walk.ogg-7f398fe415d99884ceb48b13ce0ba4c4.oggvorbisstr"
[deps]
source_file="res://default/aud/walk.ogg"
dest_files=["res://.godot/imported/walk.ogg-7f398fe415d99884ceb48b13ce0ba4c4.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
default/aud/wind.ogg Normal file

Binary file not shown.

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://cpxp5qfgsjwuo"
path="res://.godot/imported/wind.ogg-7e6f4a88ed48e7485fb51c0f247f3a0b.oggvorbisstr"
[deps]
source_file="res://default/aud/wind.ogg"
dest_files=["res://.godot/imported/wind.ogg-7e6f4a88ed48e7485fb51c0f247f3a0b.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

BIN
default/text/grass.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://b08pmo4b1vu2v"
path="res://.godot/imported/grass.png-32c0ea7ae306e3e97ce93958a097a086.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://default/text/grass.png"
dest_files=["res://.godot/imported/grass.png-32c0ea7ae306e3e97ce93958a097a086.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
default/text/player-w1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bndf6ulk38x8w"
path="res://.godot/imported/player-w1.png-2d83ddef195d31d2854854be7b5eb929.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://default/text/player-w1.png"
dest_files=["res://.godot/imported/player-w1.png-2d83ddef195d31d2854854be7b5eb929.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
default/text/player-w2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://klft47dygepk"
path="res://.godot/imported/player-w2.png-63721d009b7ff1c6b9572befdcde63c8.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://default/text/player-w2.png"
dest_files=["res://.godot/imported/player-w2.png-63721d009b7ff1c6b9572befdcde63c8.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
default/text/wood.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

View File

@@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://chmxh4w36byso"
path="res://.godot/imported/wood.png-02ad6b4a9c2c95bcd439d5ff9fe93251.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://default/text/wood.png"
dest_files=["res://.godot/imported/wood.png-02ad6b4a9c2c95bcd439d5ff9fe93251.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

45
export_presets.cfg Normal file
View File

@@ -0,0 +1,45 @@
[preset.0]
name="Web"
platform="Web"
runnable=true
advanced_options=true
dedicated_server=false
custom_features=""
export_filter="all_resources"
include_filter=""
exclude_filter=""
export_path="../../../scratch/exports/3/WeedMaker.html"
patches=PackedStringArray()
encryption_include_filters=""
encryption_exclude_filters=""
seed=0
encrypt_pck=false
encrypt_directory=false
script_export_mode=2
[preset.0.options]
custom_template/debug="/scratch/godot.web.template_debug.wasm32.dlink.zip"
custom_template/release="/scratch/godot.web.template_release.wasm32.dlink.zip"
variant/extensions_support=true
variant/thread_support=true
vram_texture_compression/for_desktop=true
vram_texture_compression/for_mobile=false
html/export_icon=true
html/custom_html_shell=""
html/head_include=""
html/canvas_resize_policy=2
html/focus_canvas_on_start=true
html/experimental_virtual_keyboard=false
progressive_web_app/enabled=false
progressive_web_app/ensure_cross_origin_isolation_headers=true
progressive_web_app/offline_page=""
progressive_web_app/display=1
progressive_web_app/orientation=0
progressive_web_app/icon_144x144=""
progressive_web_app/icon_180x180=""
progressive_web_app/icon_512x512=""
progressive_web_app/background_color=Color(0, 0, 0, 1)
threads/emscripten_pool_size=8
threads/godot_pool_size=4

76
game.tscn Normal file
View File

@@ -0,0 +1,76 @@
[gd_scene load_steps=7 format=3 uid="uid://dx3edvokvnhss"]
[ext_resource type="Script" uid="uid://be6h32vns32hm" path="res://code/world.gd" id="1_e2o6t"]
[ext_resource type="Script" uid="uid://cc4y82skj2obl" path="res://code/player.gd" id="1_feb5d"]
[ext_resource type="Texture2D" uid="uid://bndf6ulk38x8w" path="res://default/text/player-w1.png" id="3_feb5d"]
[ext_resource type="AudioStream" uid="uid://bcca08s3kqhkf" path="res://default/aud/walk.ogg" id="4_fc0e3"]
[ext_resource type="Script" uid="uid://c01rdot180cne" path="res://code/lua_env.lua" id="5_7jktm"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_feb5d"]
[node name="Node2D" type="Node2D"]
metadata/resourcepack = "default"
[node name="cam" type="Camera2D" parent="."]
[node name="World" type="Area2D" parent="."]
script = ExtResource("1_e2o6t")
metadata/world = "world"
[node name="Player" type="CharacterBody2D" parent="." groups=["player"]]
script = ExtResource("1_feb5d")
metadata/canWalk = true
[node name="Sprite2D" type="Sprite2D" parent="Player"]
scale = Vector2(3, 3)
texture = ExtResource("3_feb5d")
[node name="CollisionShape2D" type="CollisionShape2D" parent="Player"]
scale = Vector2(2.3, 2.3)
shape = SubResource("RectangleShape2D_feb5d")
[node name="Walk" type="AudioStreamPlayer" parent="Player"]
stream = ExtResource("4_fc0e3")
volume_db = -5.0
[node name="Fader" type="CanvasLayer" parent="."]
[node name="ColorRect" type="ColorRect" parent="Fader"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 2
size_flags_vertical = 0
color = Color(0, 0, 0, 1)
[node name="LuaEnv" type="Node" parent="."]
script = ExtResource("5_7jktm")
[node name="UI" type="CanvasLayer" parent="."]
[node name="bg" type="ColorRect" parent="UI"]
anchors_preset = -1
anchor_top = 0.5
anchor_right = 1.0
anchor_bottom = 1.0
offset_top = -9.0
grow_horizontal = 2
grow_vertical = 0
size_flags_horizontal = 2
size_flags_vertical = 0
color = Color(0, 0, 0.48235294, 0.5529412)
[node name="RichTextLabel" type="RichTextLabel" parent="UI/bg"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_font_sizes/normal_font_size = 50
theme_override_font_sizes/bold_font_size = 50
theme_override_font_sizes/bold_italics_font_size = 50
theme_override_font_sizes/italics_font_size = 50
theme_override_font_sizes/mono_font_size = 50

1
icon.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="128" height="128"><rect width="124" height="124" x="2" y="2" fill="#363d52" stroke="#212532" stroke-width="4" rx="14"/><g fill="#fff" transform="translate(12.322 12.322)scale(.101)"><path d="M105 673v33q407 354 814 0v-33z"/><path fill="#478cbf" d="m105 673 152 14q12 1 15 14l4 67 132 10 8-61q2-11 15-15h162q13 4 15 15l8 61 132-10 4-67q3-13 15-14l152-14V427q30-39 56-81-35-59-83-108-43 20-82 47-40-37-88-64 7-51 8-102-59-28-123-42-26 43-46 89-49-7-98 0-20-46-46-89-64 14-123 42 1 51 8 102-48 27-88 64-39-27-82-47-48 49-83 108 26 42 56 81zm0 33v39c0 276 813 276 814 0v-39l-134 12-5 69q-2 10-14 13l-162 11q-12 0-16-11l-10-65H446l-10 65q-4 11-16 11l-162-11q-12-3-14-13l-5-69z"/><path d="M483 600c0 34 58 34 58 0v-86c0-34-58-34-58 0z"/><circle cx="725" cy="526" r="90"/><circle cx="299" cy="526" r="90"/></g><g fill="#414042" transform="translate(12.322 12.322)scale(.101)"><circle cx="307" cy="532" r="60"/><circle cx="717" cy="532" r="60"/></g></svg>

After

Width:  |  Height:  |  Size: 995 B

43
icon.svg.import Normal file
View File

@@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dmbavwbdqk8qm"
path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://icon.svg"
dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

62
project.godot Normal file
View File

@@ -0,0 +1,62 @@
; Engine configuration file.
; It's best edited using the editor UI and not directly,
; since the parameters that go here are not all obvious.
;
; Format:
; [section] ; section goes between []
; param=value ; assign values to parameters
config_version=5
[animation]
compatibility/default_parent_skeleton_in_mesh_instance_3d=true
[application]
config/name="WeedMaker"
run/main_scene="uid://dx3edvokvnhss"
config/features=PackedStringArray("4.6", "GL Compatibility")
boot_splash/bg_color=Color(0, 0, 0, 1)
config/icon="res://icon.svg"
[editor_plugins]
enabled=PackedStringArray("res://addons/lua-gdextension/plugin.cfg")
[input]
moveup={
"deadzone": 0.2,
"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":-1.0,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194320,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
movedown={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":1,"axis_value":1.0,"script":null)
]
}
moveleft={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":97,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194319,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":1.0,"script":null)
]
}
moveright={
"deadzone": 0.2,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194321,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":100,"location":0,"echo":false,"script":null)
, Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":0,"axis_value":-1.0,"script":null)
]
}
[rendering]
textures/canvas_textures/default_texture_filter=0
renderer/rendering_method="gl_compatibility"
renderer/rendering_method.mobile="gl_compatibility"

195
worlds/exported_world.json Normal file
View File

@@ -0,0 +1,195 @@
{
"width": 20,
"height": 15,
"fill": "grass",
"pspawn": {
"w": 1,
"h": 1
},
"features": [
{
"w": 8,
"h": 5,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 11,
"h": 5,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 13,
"h": 5,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 16,
"h": 5,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 18,
"h": 5,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 8,
"h": 6,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 11,
"h": 6,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 13,
"h": 6,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 14,
"h": 6,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 15,
"h": 6,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 16,
"h": 6,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 8,
"h": 7,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 11,
"h": 7,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 13,
"h": 7,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 16,
"h": 7,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 18,
"h": 7,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 13,
"h": 8,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 16,
"h": 8,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 18,
"h": 8,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 8,
"h": 9,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 11,
"h": 9,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 9,
"h": 10,
"t": {
"thing": "woodbarrier",
"action": "none"
}
},
{
"w": 10,
"h": 10,
"t": {
"thing": "woodbarrier",
"action": "none"
}
}
]
}

11
worlds/totr.json Normal file
View File

@@ -0,0 +1,11 @@
{
"fill":"wood",
"height":50,
"width":50,
"features":[
{"w":5,"h":2,"t":{"thing":"grass","action":"tp","target_world":"world"}},
{"w":8,"h":2,"t":{"thing":"grass","action":"tp","target_world":"exported_world"}},
{"w":5,"h":5,"t":{"thing":"grass","action":"none"}}
],
"pspawn":{"w":5,"h":5}
}

12
worlds/world.json Normal file
View File

@@ -0,0 +1,12 @@
{
"fill":"grass",
"height":50,
"width":50,
"features":[
{"w":5,"h":2,"t":{"thing":"wood","action":"none"}},
{"w":5,"h":5,"t":{"thing":"woodbarrier","action":"none"}},
{"w":15,"h":15,"t":{"thing":"wood","action":"tp","target_world":"totr"}}
],
"pspawn":{"w":14,"h":15},
"mapscript":true
}

6
worlds/world.lua Normal file
View File

@@ -0,0 +1,6 @@
local arg = {...}
local node = arg[1]
print("loo a")
print(node.name)
node:ui_showmesg("hellao")

Some files were not shown because too many files have changed in this diff Show More