/// scr_handle_lua_error(msg, state) var state = argument1; show_message("A Lua error occurred: " + argument0);
lua_error_handler = scr_handle_lua_error;
state = lua_state_create();
lua_global_set(state, "io", undefined);
lua_state_destroy(state);
lua_add_code(state, "print('Hello!')");
lua_add_file(state, "some.lua");
lua_add_code(state, "test = 'Hello!'"); show_message(lua_global_get(state, "test"));
lua_global_set(state, "test", "Hello!"); lua_add_code(state, "print(test) -- 'Hello!'");
if (lua_global_typeof(state, "test") == "function") { lua_call(state, "test"); } else show_message("The state does not have a function called `test`!");
lua_add_code(state, "function greet(s) return 'Hello, ' .. s end"); show_message(lua_call(state, "greet", "GameMaker"));
/// scr_alert(text) show_message(argument0);
lua_add_function(state, "alert", scr_alert);
lua_add_function(state, "game_alert", scr_alert); lua_add_code(state, ' game = { alert: game_alert } ');
game.alert("Hello!")on Lua side of things.
/// lengthdir_xy(len, dir) var len = argument0, dir = argument1; return lua_return(lengthdir_x(len, dir), lengthdir_y(len, dir));
lua_add_function(state, "lengthdir_xy", lengthdir_xy);
local x, y = lengthdir_xy(30, 45) print(x, y) -- 21.21, -21.21
/// instance_find_all(obj) with (argument0) lua_return_add(id); return lua_return_add();
While Lua has a separate boolean type, GameMaker uses 1 as true-value and 0 as false-value. This makes it hard to tell whether you were meaning to send 1 or true.
So there's this function, which returns either a lua_true or a lua_false depending on argument, which can be told apart by the extension explicitly, and will become the according Lua values once sent to Lua.
If you are building a medium-scale scripting API, you may find yourself needing to expose a large number of scripts (and/or built-in functions), as well as introducing argument type checking to prevent GML-side errors.
To save you from having to deal with that, Apollo includes a small utility that generates wrapper and loader scripts.
It accepts function definitions in funcname(arg1:type1, arg2:type2, ...):rtype,
Constants can be defined either as name# (uses the value of same-named constant/variable) or name = value (computes the given GML value at inclusion time).
The tool is included with the extension as ApolloGen.exe;
A web-based version is available below:
Whenever the contents of above field are changed, updated loader script will be output into the field below:
You can then save them into a .gml file and import it to your project.
"getting started" page on the Lua' website houses a large collection of links to tutorials, wikis, and other learning materials.
Lua Manual provides detailed explanations on how internal elements and all standard functions lf the language work.
If you have pre-existing GML code that you'd like to quickly port for use with Apollo, I have also developed an online GML->Lua compiler.
While automatic conversion won't make extensive use of Lua-specific language features, it produces functional code in vast majority of cases and the output is clean enough to tweak it manually if needed.
thread = lua_thread_create(state);
lua_add_code(state, " function test(num) for i = 1, num do coroutine.yield(i) end return 'rad!' end "); th = lua_thread_create(state); if (lua_call_start(th, "test", 4)) { while (lua_call_next(th)) { show_debug_message("yield: " + string(lua_call_result)); } show_debug_message("result: " + string(lua_call_result)); } lua_thread_destroy(th);
While these are roughly equivalent to GM's ds_maps, the two work very differently - ds_maps are passed by-index and managed manually (ds_map_destroy), Lua' tables are passed by-reference and managed by garbage collector.
While future iterations on GML should make it possible to automatically convert between tables and lightweight data structures, this would only allow to return a new table/structure rather than modifying an existing one.
The issue can be approached in several ways:
Lua supports several additional reference types (such as Lua function references), but these cannot be safely sent to GML as pointers as they are garbage-collected, and thus may get recycled while still referenced on the GML side of things (resulting in hard crash when trying to use the passed back value).
A good way to deal with this is to make a pair of lookup tables - since Lua allows table indexes to be of any type, you can do something like the following:
ref = { __r2i = { }, __i2r = { }, __next = 0 } function ref.toid(fn) local id = ref.__r2i[fn] if (id == nil) then id = ref.__next ref.__next = id + 1 ref.__r2i[fn] = id ref.__i2r[id] = fn end return id end function ref.fromid(id) return ref.__i2r[id] end function ref.free(fn) local id if (type(fn) == "number") then id = fn fn = ref.__i2r[id] else id = ref.__r2i[fn] end ref.__r2i[fn] = nil ref.__i2r[id] = nil end
Which allow you to use ref.toid(some_reference) to return/create a numeric ID for a reference, ref.fromid(index) to convert one of those back to a reference, and ref.free(index_or_reference) to remove the lookup pairs (allowing Lua to safely recycle the reference when it is no longer used).