Page MenuHomePhabricator (Chris)

No OneTemporary

Authored By
Unknown
Size
114 KB
Referenced Files
None
Subscribers
None
diff --git a/docs/ScriptAPI.md b/docs/ScriptAPI.md
index 51bc6a3..ba536ef 100644
--- a/docs/ScriptAPI.md
+++ b/docs/ScriptAPI.md
@@ -1,688 +1,705 @@
Me and My Shadow Script API Reference
=====================================
(draft)
The script language is Lua 5.2 (later we may bump it to 5.3).
Always check `ScriptAPI.cpp` for the newest API changed unmentioned in this document.
How to edit script
==================
To edit the script of a block, right click the block and select "Scripting".
To edit the script of the level, right click the empty space of the level and select "Scripting".
Currently the scenery block doesn't support scripting.
Available event types of block:
Event type | Description
----------------------|--------------
"playerWalkOn" | Fired once when the player walks on. (For example this is used in fragile block.)
"playerIsOn" | Fired every frame when the player is on.
"playerLeave" | Fired once when the player leaves.
"onCreate" | Fired when object creates.
"onEnterFrame" | Fired every frame.
"onPlayerInteraction" | Fired when the player press DOWN key. Currently this event only fires when the block type is TYPE_SWITCH.
"onToggle" | Fired when the block receives "toggle" from a switch/button. NOTE: The switch/button itself will also receive this event. This is used in an old example found on my old computer.
"onSwitchOn" | Fired when the block receives "switch on" from a switch/button. NOTE: The switch/button itself will also receive this event. This is used in an old example found on my old computer.
"onSwitchOff" | Fired when the block receives "switch off" from a switch/button. NOTE: The switch/button itself will also receive this event. This is used in an old example found on my old computer.
NOTE: During the event execution the global variable `this` temporarily points to current block. (Ad-hoc workaround!)
When the event execution ends the global variable `this` is reset to its previous value.
The block event may return an integer value (default is 0) to alter the game logic:
Return value | Description
-------------|--------------
0 | Skip the default game logic for this event.
1 | Do the default game logic for this event.
Available event types of level:
Event type | Description
-----------|--------------
"onCreate" | Fired when the level is created or the game is reset. This happens after all the blocks are created and their `onCreate` is called.
"onSave" | Fired when the game is saved.
"onLoad" | Fired when the game is loaded.
For the newest lists of event types, see `init()` function in `Functions.cpp`.
NOTE: the following methods to specify scripts can be used:
* Specify scripts for each events in the block script editing dialog.
* Only specify `onCreate` script in the block script editing dialog,
and use `setEventHandler()` function in script to specify scripts for other events dynamically.
* Only specify `onCreate` script in the level script editing dialog,
and use `setEventHandler()` function in script to specify scripts for other events for level/blocks dynamically.
Script API reference
====================
The "block" library
-------------------
### Static functions:
* getBlockById(id)
Returns the first block with specified id. If not found, returns `nil`.
Example:
~~~lua
local b=block.getBlockById("1")
local x,y=b:getLocation()
print(x..","..y)
~~~
* getBlocksById(id)
Returns the list of all blocks with specified id.
Example:
~~~lua
local l=block.getBlocksById("1")
for i,b in ipairs(l) do
local x,y=b:getLocation()
print(x..","..y)
end
~~~
* removeAll()
Remove all blocks.
* addBlock(string,[x],[y],[w],[h])
Add a new block (optionally give it a new position and size) and return the newly created block.
The `string` is the text representation of a block which is used in `.map` file.
The new block can have scripts and the `onCreate` script will be executed immediately.
Example:
~~~lua
-- Assume we have moving blocks of id 1,
-- then this newly added switch can operate existing moving blocks.
block.addBlock([[
tile(Switch,0,0,50,50){
behaviour=toggle
id=1
script(onCreate){
script="print('Hello world from onCreate of dynamically added block')"
}
}]],250,300)
~~~
Another example:
~~~lua
local b=block.addBlock('tile(MovingBlock,0,0)')
b:setBaseLocation(
math.random()*level.getWidth(),
math.random()*level.getHeight())
local bx,by=b:getBaseLocation()
for i=1,10 do
b:addMovingPos({
math.random()*level.getWidth()-bx,
math.random()*level.getHeight()-by,
math.random()*80+40
})
end
b:addMovingPos({0,0,math.random()*80+40})
~~~
+* addBlocks(string,[positions]) / addBlocks(string,offsetX,offsetY)
+
+Add new blocks (optionally give them new positions and sizes) and return an array the newly created blocks.
+
+The `string` is the text representation of blocks which is used in `.map` file.
+
+In the first form,
+If `string` contains only one block, then it will be created repeatedly using the specified positions.
+If it contains more than one block, then each block will use corresponding position in `positions`.
+The `positions` is an array of new positions whose entry is of format `{[x],[y],[w],[h]}`.
+
+In the second form,
+the `string` can contain one or more blocks,
+and the position of each block will be offset by two numbers `offsetX` and `offsetY`.
+
+The new blocks can have scripts and the `onCreate` script will be executed immediately.
+
### Member functions:
* isValid() -- check the object is valid (i.e. not deleted, etc.)
* moveTo(x,y)
Move the block to the new position, update the velocity of block according to the position changed.
Example:
~~~lua
local b=block.getBlockById("1")
local x,y=b:getLocation()
b:moveTo(x+1,y)
~~~
* getLocation()
Returns the position of the block.
Example: see the example for moveTo().
* setLocation(x,y)
Move the block to the new position without updating the velocity of block.
Example: omitted since it's almost the same as moveTo().
* getBaseLocation() / setBaseLocation(x,y)
Get or set the base position of the block. Mainly used for moving blocks.
* growTo(w,h)
Resize the block, update the velocity of block according to the size changed.
NOTE: I don't think the velocity need to be updated when resizing block, so don't use this function.
Example: omitted since it's almost the same as setSize().
* getSize()
Returns the size of the block.
Example:
~~~lua
local b=block.getBlockById("1")
local w,h=b:getSize()
print(w..","..h)
~~~
* setSize(w,h)
Resize the block without updating the velocity of block.
Example:
~~~lua
local b=block.getBlockById("1")
local w,h=b:getSize()
b:setSize(w+1,h)
~~~
* getBaseSize() / setBaseSize(x,y)
Get or set the base size of the block. Mainly used for moving blocks.
* getType()
Returns the type of the block (which is a string).
Example:
~~~lua
local b=block.getBlockById("1")
local s=b:getType()
print(s)
~~~
* changeThemeState(new_state)
Change the state of the block to new_state (which is a string).
Example:
~~~lua
local b=block.getBlockById("1")
b:changeThemeState("activated")
~~~
* setVisible(b)
Set the visibility the block.
NOTE: The default value is `true`. If set to `false` the block is hidden completely,
the animation is stopped, can't receive any event, can't execute any scripts (except for `onCreate`),
can't be used as a portal destination,
doesn't participate in collision check and game logic, etc...
NOTE: This is a newly added feature.
If you find any bugs (e.g. if an invisible block still affects the game logic)
please report the bugs to GitHub issue tracker.
Example:
~~~lua
local b=block.getBlockById("1")
if b:isVisible() then
b:setVisible(false)
else
b:setVisible(true)
end
~~~
* isVisible()
Returns whether the block is visible.
Example: see the example for setVisible().
* getEventHandler(event_type)
Returns the event handler of event_type (which is a string).
Example:
~~~lua
local b=block.getBlockById("1")
local f=b:getEventHandler("onSwitchOn")
b:setEventHandler("onSwitchOff",f)
~~~
* setEventHandler(event_type,handler)
Set the handler of event_type (which is a string). The handler should be a function or `nil`.
Returns the previous event handler.
Example:
~~~lua
local b=block.getBlockById("1")
b:setEventHandler("onSwitchOff",function()
print("I am switched off.")
end)
~~~
* onEvent(eventType)
Fire an event to specified block.
NOTE: The event will be processed immediately.
Example:
~~~lua
local b=block.getBlockById("1")
b:onEvent("onToggle")
~~~
NOTE: Be careful not to write infinite recursive code! Bad example:
~~~lua
-- onToggle event of a moving block
this:onEvent("onToggle")
~~~
* isActivated() / setActivated(bool)
Get/set a boolean indicates if the block is activated.
The block should be one of TYPE_MOVING_BLOCK, TYPE_MOVING_SHADOW_BLOCK, TYPE_MOVING_SPIKES,
TYPE_CONVEYOR_BELT, TYPE_SHADOW_CONVEYOR_BELT.
* isAutomatic() / setAutomatic(bool)
Get/set a boolean indicates if the portal is automatic.
The block should be TYPE_PORTAL.
* getBehavior() / setBehavior(str)
Get/set a string (must be "on", "off" or "toggle"),
representing the behavior of the block.
The block should be TYPE_BUTTON, TYPE_SWITCH.
* getState() / setState(num)
Get/set a number (must be 0,1,2 or 3),
representing the state of a fragile block.
The block should be TYPE_FRAGILE.
* isPlayerOn()
Get a boolean indicates if the player is on.
Currently only works for TYPE_BUTTON.
* getPathMaxTime()
Get the total time of the path of a moving block.
* getPathTime() / setPathTime(num)
Get/set the current time of the path of a moving block.
* isLooping() / setLooping(bool)
Get/set the looping property of a moving block.
* getSpeed() / setSpeed(num)
Get/set the speed of a conveyor belt.
NOTE: 1 Speed = 0.08 block/s = 0.1 pixel/frame.
* getAppearance() / setAppearance(str)
Get/set the custom appearance of a block.
The `str` is the name of the custom appearance, either `"<blockName>_Scenery"` or name of a scenery block.
Empty string or nil means the default appearance.
* getId() / setId(str)
Get/set the id of a block.
* getDestination() / setDestination(str)
Get/set the destination of a portal.
The block should be TYPE_PORTAL.
* getMessage() / setMessage(str)
Get/set the message of a notify block.
The block should be TYPE_NOTIFICATION_BLOCK.
* getMovingPosCount()
Get the number of moving positions of a moving block.
* getMovingPos() / getMovingPos(index) / getMovingPos(start, length)
Get the array of moving positions or the moving position at specified index
(the array index starts with 1 in Lua).
The individual point is of format `{x,y,t}`.
* setMovingPos(array) / setMovingPos(index, point) / setMovingPos(start, length, array)
Set the array of moving positions or modify the moving position at specified index
(the array index starts with 1 in Lua).
NOTE: the last two forms won't change the number of points,
while the first form will overwrite the list of points completely.
* addMovingPos(p) / addMovingPos(index, p)
Insert points to the array of moving positions at the end or at the specified index.
The `p` can be one point or a list of points.
* removeMovingPos() / removeMovingPos(index) / removeMovingPos(listOfIndices) / removeMovingPos(start, length)
Remove points in the array of moving positions: remove all points,
or remove a point at specified index, or remove points at specified indices,
or remove points in given range.
* remove()
Remove current block.
The "playershadow" library
--------------------------
### Global constants:
* player
The player object.
* shadow
The shadow object.
### Member functions:
* getLocation()
Returns the location of player/shadow.
Example:
~~~lua
local x,y=player:getLocation()
print("player: "..x..","..y)
x,y=shadow:getLocation()
print("shadow: "..x..","..y)
~~~
* setLocation(x,y)
Set the location of player/shadow.
Example:
~~~lua
local x,y=player:getLocation()
player:setLocation(x+1,y)
~~~
* jump([strength=13])
Let the player/shadow jump if it's allowed.
strength: Jump strength.
Example:
~~~lua
player:jump(20)
~~~
* isShadow()
Returns whether the current object is shadow.
Example:
~~~lua
print(player:isShadow())
print(shadow:isShadow())
~~~
* getCurrentStand()
Returns the block on which the player/shadow is standing on. Can be `nil`.
Example:
~~~lua
local b=player:getCurrentStand()
if b then
print(b:getType())
else
print("The player is not standing on any blocks")
end
~~~
* isInAir() -- returns a boolean indicating if the player is in air
* canMove() -- returns a boolean indicating if the player can move (i.e. not standing on shadow)
* isDead() -- returns a boolean indicating if the player is dead
* isHoldingOther() -- returns a boolean indicating if the player is holding other
The "level" library
-------------------
### Static functions:
* getSize() -- get the level size
* getWidth() -- get the level width
* getHeight() -- get the level height
* getName() -- get the level name
* getEventHandler(event_type) -- get the event handler
* setEventHandler(event_type,handler) -- set the event handler, return the old handler
* win() -- win the game
* getTime() -- get the game time (in frames)
* getRecordings() -- get the game recordings
* broadcastObjectEvent(eventType,[objectType=nil],[id=nil],[target=nil])
Broadcast the event to blocks satisfying the specified condition.
NOTE: The event will be processed in next frame.
Argument name | Description
--------------|-------------
eventType | string.
objectType | string or nil. If this is set then the event is only received by the block with specified type.
id | string or nil. If this is set then the event is only received by the block with specified id.
target | block or nil. If this is set then the event is only received by the specified block.
Example:
~~~lua
level.broadcastObjectEvent("onToggle",nil,"1")
~~~
The "delayExecution" library
----------------------------
### Static functions:
* schedule(func,time,[repeatCount=1],[repeatInterval],[enabled=true],[arguments...])
Schedule a delay execution of a given function after the given time.
Argument name | Description
---------------|-------------
func | A function to be executed.
time | Time, given in frames (NOTE: 40 frames = 1 second). NOTE: If <=0 it is the same as =1.
repeatCount | The number of times the function will be executed. After such number of times executed, the delay execution will be removed from the list and get deleted. If =0 the delay execution object will be deleted soon. If <0 the function will be executed indefinitely.
repeatInterval | The repeat interval. If it is `nil` then the `time` argument will be used instead. NOTE: If <=0 the repeat execution will be disabled at all and the repeatCount will be set to 1.
enabled | Enabled.
arguments | Optional arguments passed to the function.
Return value: the delayExecution object.
NOTE: If you want to update time/repeatCount during the function execution,
notice that the time/repeatCount is updated BEFORE the function execution.
NOTE: During the execution the global variable `this`
temporarily points to current delay execution object. (Ad-hoc workaround!)
When the execution ends the global variable `this` is reset to its previous value.
Example:
~~~lua
local f=function()
local a
a=0
return(function(b)
shadow:jump()
print('obj1 '..this:getExecutionTime()..' '..a..' '..tostring(b))
a=a+2
end)
end
local obj1=delayExecution.schedule(f(),40*2,5,nil,nil,100)
local obj2=delayExecution.schedule(
function(o)
print('obj2 '..tostring(o:isValid()))
if not o:isValid() then
this:setFunc(f())
end
end,40*1,-1,nil,nil,obj1)
local obj3=delayExecution.schedule(
function(o)
o:cancel()
end,40*30,1,nil,nil,obj2)
~~~
### Member functions:
* isValid() -- Check if it's valid, i.e. not removed from list.
* cancel() -- Cancels a delay execution. The canceled delay execution will be removed from the list and can not be restored.
* isEnabled()/setEnabled(bool) -- get/set enabled of a delay execution. A disabled one will not count down its timer.
* getTime()/setTime(integer) -- get/set the remaining time until the next execution. NOTE: If <=0 it is the same as =1.
* getRepeatCount()/setRepeatCount(integer) -- get/set the remaining repeat count. If =0 the object will get deleted soon. If <0 the function will be executed indefinitely.
* getRepeatInterval()/setRepeatInterval(integer) -- get/set the repeat interval. NOTE: If <=0 then nothing happens.
* getFunc()/setFunc(func) -- get/set the function to be executed. NOTE: The setFunc will return the original function.
* getArguments()/setArguments(args...) -- get/set the arguments
* getExecutionTime()/setExecutionTime(integer) -- get/set the number of times the function being executed. NOTE: this execution time doesn't affect the default logic.
The "camera" library
--------------------
### Static functions:
* setMode(mode) -- set the camera mode, which is "player" or "shadow"
* lookAt(x,y) -- set the camera mode to "custom" and set the new center of camera
The "audio" library
-------------------
NOTE: the following functions are not going to work if the sound/music volume is 0.
### Static functions:
* playSound(name[,concurrent=-1[,force=false[,fade=-1]]])
Play a sound effect.
Argument name | Description
--------------|-------------
name | The name of the sound effect. Currently available: "jump", "hit", "checkpoint", "swap", "toggle", "error", "collect", "achievement".
concurrent | The number of times the same sfx can be played at once, -1 is unlimited. NOTE: there are 64 channels.
force | If the sound must be played even if all channels are used. In this case the sound effect in the first channel will be stopped.
fade | A factor to temporarily turn the music volume down (0-128). -1 means don't use this feature.
Return value: The channel of the sfx. -1 means failed (channel is full, invalid sfx name, sfx volume is 0, etc.)
* playMusic(name[,fade=true])
Play a music.
Argument name | Description
--------------|-------------
name | The name of the song, e.g. "default/neverending" or "menu".
fade | Boolean if it should fade the current one out or not.
* pickMusic() - pick a song from the current music list.
* getMusicList()/setMusicList(name_of_the_music_list) - get/set the music list. Example: "default".
* currentMusic() - get the current music.
The "gettext" library
--------------------
This library is used for translation support.
NOTE: Currently this library only uses the dictionary for current level pack.
This means it doesn't work for individual level which doesn't contain in a level pack,
and it can't make use of the translations of the core game.
### Global functions:
* `_(msgid)` -- translate the string using default context
* `__(msgid)` -- does nothing, just outputs the original string. However, it will be scanned by `xgettext`.
Mainly used in block:setMessage() since the block message will always be passed to gettext().
Also used in construction of an array of strings, which will be translated dynamically.
### Static functions:
* gettext(msgid) -- translate the string using default context
* pgettext(msgctxt,msgid) -- translate the string using specified context
* ngettext(msgid,msgid_plural,n) -- translate the string using default context, taking plural form into consideration
* npgettext(msgctxt,msgid,msgid_plural,n) -- translate the string using specified context, taking plural form into consideration
diff --git a/src/ScriptAPI.cpp b/src/ScriptAPI.cpp
index c90fd4c..a3576f9 100644
--- a/src/ScriptAPI.cpp
+++ b/src/ScriptAPI.cpp
@@ -1,2780 +1,2992 @@
/*
* Copyright (C) 2012-2013 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ScriptAPI.h"
#include "ScriptExecutor.h"
#include "SoundManager.h"
#include "Functions.h"
#include "Block.h"
#include "Game.h"
#include "MusicManager.h"
#include "ScriptDelayExecution.h"
#include "Globals.h"
#include "TreeStorageNode.h"
#include "POASerializer.h"
#include <iostream>
#include <sstream>
#include <algorithm>
using namespace std;
/////////////////////////// HELPER MACRO ///////////////////////////
#define HELPER_GET_AND_CHECK_ARGS(ARGS) \
const int args = lua_gettop(state); \
if(args != ARGS) { \
return luaL_error(state, "Incorrect number of arguments for %s, expected %d.", __FUNCTION__, ARGS); \
}
#define HELPER_GET_AND_CHECK_ARGS_RANGE(ARGS1, ARGS2) \
const int args = lua_gettop(state); \
if(args < ARGS1 || args > ARGS2) { \
return luaL_error(state, "Incorrect number of arguments for %s, expected %d-%d.", __FUNCTION__, ARGS1, ARGS2); \
}
#define HELPER_GET_AND_CHECK_ARGS_2(ARGS1, ARGS2) \
const int args = lua_gettop(state); \
if(args != ARGS1 && args != ARGS2) { \
return luaL_error(state, "Incorrect number of arguments for %s, expected %d or %d.", __FUNCTION__, ARGS1, ARGS2); \
}
#define HELPER_GET_AND_CHECK_ARGS_AT_LEAST(ARGS) \
const int args = lua_gettop(state); \
if(args < ARGS) { \
return luaL_error(state, "Incorrect number of arguments for %s, expected at least %d.", __FUNCTION__, ARGS); \
}
#define HELPER_GET_AND_CHECK_ARGS_AT_MOST(ARGS) \
const int args = lua_gettop(state); \
if(args > ARGS) { \
return luaL_error(state, "Incorrect number of arguments for %s, expected at most %d.", __FUNCTION__, ARGS); \
}
//================================================================
#define HELPER_CHECK_ARGS_TYPE(INDEX, TYPE) \
if(!lua_is##TYPE(state,INDEX)) { \
return luaL_error(state, "Invalid type for argument %d of %s, should be %s.", INDEX, __FUNCTION__, #TYPE); \
}
#define HELPER_CHECK_ARGS_TYPE_NO_HINT(INDEX, TYPE) \
if(!lua_is##TYPE(state,INDEX)) { \
return luaL_error(state, "Invalid type for argument %d of %s.", INDEX, __FUNCTION__); \
}
#define HELPER_CHECK_ARGS_TYPE_2(INDEX, TYPE1, TYPE2) \
if(!lua_is##TYPE1(state,INDEX) && !lua_is##TYPE2(state,INDEX)) { \
return luaL_error(state, "Invalid type for argument %d of %s, should be %s or %s.", INDEX, __FUNCTION__, #TYPE1, #TYPE2); \
}
#define HELPER_CHECK_ARGS_TYPE_2_NO_HINT(INDEX, TYPE1, TYPE2) \
if(!lua_is##TYPE1(state,INDEX) && !lua_is##TYPE2(state,INDEX)) { \
return luaL_error(state, "Invalid type for argument %d of %s.", INDEX, __FUNCTION__); \
}
#define HELPER_CHECK_ARGS_TYPE_OR_NIL(INDEX, TYPE) \
HELPER_CHECK_ARGS_TYPE_2(INDEX, TYPE, nil)
#define HELPER_CHECK_ARGS_TYPE_OR_NIL_NO_HINT(INDEX, TYPE) \
HELPER_CHECK_ARGS_TYPE_2_NO_HINT(INDEX, TYPE, nil)
//================================================================
#define HELPER_CHECK_OPTIONAL_ARGS_TYPE(INDEX, TYPE) \
if(args>=INDEX && !lua_is##TYPE(state,INDEX)) { \
return luaL_error(state, "Invalid type for argument %d of %s, should be %s.", INDEX, __FUNCTION__, #TYPE); \
}
#define HELPER_CHECK_OPTIONAL_ARGS_TYPE_NO_HINT(INDEX, TYPE) \
if(args>=INDEX && !lua_is##TYPE(state,INDEX)) { \
return luaL_error(state, "Invalid type for argument %d of %s.", INDEX, __FUNCTION__); \
}
#define HELPER_CHECK_OPTIONAL_ARGS_TYPE_2(INDEX, TYPE1, TYPE2) \
if(args>=INDEX && !lua_is##TYPE1(state,INDEX) && !lua_is##TYPE2(state,INDEX)) { \
return luaL_error(state, "Invalid type for argument %d of %s, should be %s or %s.", INDEX, __FUNCTION__, #TYPE1, #TYPE2); \
}
#define HELPER_CHECK_OPTIONAL_ARGS_TYPE_2_NO_HINT(INDEX, TYPE1, TYPE2) \
if(args>=INDEX && !lua_is##TYPE1(state,INDEX) && !lua_is##TYPE2(state,INDEX)) { \
return luaL_error(state, "Invalid type for argument %d of %s.", INDEX, __FUNCTION__); \
}
#define HELPER_CHECK_OPTIONAL_ARGS_TYPE_OR_NIL(INDEX, TYPE) \
HELPER_CHECK_OPTIONAL_ARGS_TYPE_2(INDEX, TYPE, nil)
#define HELPER_CHECK_OPTIONAL_ARGS_TYPE_OR_NIL_NO_HINT(INDEX, TYPE) \
HELPER_CHECK_OPTIONAL_ARGS_TYPE_2_NO_HINT(INDEX, TYPE, nil)
//================================================================
#define HELPER_REGISTER_IS_VALID_FUNCTION(CLASS) \
int isValid(lua_State* state){ \
HELPER_GET_AND_CHECK_ARGS(1); \
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata); \
CLASS* object = CLASS::getObjectFromUserData(state, 1); \
lua_pushboolean(state, object ? 1 : 0); \
return 1; \
}
//================================================================
#define _F(FUNC) { #FUNC, _L::FUNC }
#define _FG(FUNC) _F(get##FUNC)
#define _FI(FUNC) _F(is##FUNC)
#define _FS(FUNC) _F(set##FUNC)
#define _FGS(FUNC) _F(get##FUNC), _F(set##FUNC)
#define _FIS(FUNC) _F(is##FUNC), _F(set##FUNC)
///////////////////////////BLOCK SPECIFIC///////////////////////////
class BlockScriptAPI {
public:
static int getFlags(const Block* block) {
return block->flags;
}
static void setFlags(Block* block, int flags) {
block->flags = flags;
}
static void fragileUpdateState(Block* block, int state) {
state &= 0x3;
block->flags = (block->flags & ~0x3) | state;
const char* s = (state == 0) ? "default" : ((state == 1) ? "fragile1" : ((state == 2) ? "fragile2" : "fragile3"));
block->appearance.changeState(s);
}
static int getTemp(const Block* block) {
return block->temp;
}
static void setTemp(Block* block, int value) {
block->temp = value;
}
static int getSpeed(const Block* block) {
return block->speed;
}
static void setSpeed(Block* block, int value) {
block->speed = value;
}
static void invalidatePathMaxTime(Block* block) {
block->movingPosTime = -1;
}
static std::vector<SDL_Rect>& getMovingPos(Block* block) {
return block->movingPos;
}
};
namespace block {
HELPER_REGISTER_IS_VALID_FUNCTION(Block);
int getBlockById(lua_State* state){
//Get the number of args, this MUST be one.
HELPER_GET_AND_CHECK_ARGS(1);
//Make sure the given argument is an id (string).
HELPER_CHECK_ARGS_TYPE(1, string);
//Check if the currentState is the game state.
Game* game = dynamic_cast<Game*>(currentState);
if (game == NULL) return 0;
//Get the actual game object.
string id = lua_tostring(state, 1);
std::vector<Block*>& levelObjects = game->levelObjects;
Block* object = NULL;
for (unsigned int i = 0; i < levelObjects.size(); i++){
if (levelObjects[i]->getEditorProperty("id") == id){
object = levelObjects[i];
break;
}
}
if (object == NULL){
//Unable to find the requested object.
//Return nothing, will result in a nil in the script.
return 0;
}
//Create the userdatum.
object->createUserData(state, "block");
//We return one object, the userdatum.
return 1;
}
int getBlocksById(lua_State* state){
//Get the number of args, this MUST be one.
HELPER_GET_AND_CHECK_ARGS(1);
//Make sure the given argument is an id (string).
HELPER_CHECK_ARGS_TYPE(1, string);
//Check if the currentState is the game state.
Game* game = dynamic_cast<Game*>(currentState);
if (game == NULL) return 0;
//Get the actual game object.
string id = lua_tostring(state, 1);
std::vector<Block*>& levelObjects = game->levelObjects;
std::vector<Block*> result;
for (unsigned int i = 0; i < levelObjects.size(); i++){
if (levelObjects[i]->getEditorProperty("id") == id){
result.push_back(levelObjects[i]);
}
}
//Create the table that will hold the result.
lua_createtable(state, result.size(), 0);
//Loop through the results.
for (unsigned int i = 0; i < result.size(); i++){
//Create the userdatum.
result[i]->createUserData(state, "block");
//And set the table.
lua_rawseti(state, -2, i + 1);
}
//We return one object, the userdatum.
return 1;
}
int moveTo(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(3);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, number);
HELPER_CHECK_ARGS_TYPE(3, number);
//Now get the pointer to the object.
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
int x = lua_tonumber(state, 2);
int y = lua_tonumber(state, 3);
object->moveTo(x, y);
return 0;
}
int getLocation(lua_State* state){
//Make sure there's only one argument and that argument is an userdatum.
HELPER_GET_AND_CHECK_ARGS(1);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
//Get the object.
SDL_Rect r = object->getBox();
lua_pushinteger(state, r.x);
lua_pushinteger(state, r.y);
return 2;
}
int getBaseLocation(lua_State* state){
//Make sure there's only one argument and that argument is an userdatum.
HELPER_GET_AND_CHECK_ARGS(1);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
//Get the object.
SDL_Rect r = object->getBox(BoxType_Base);
lua_pushinteger(state, r.x);
lua_pushinteger(state, r.y);
return 2;
}
int setLocation(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(3);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, number);
HELPER_CHECK_ARGS_TYPE(3, number);
//Now get the pointer to the object.
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
int x = lua_tonumber(state, 2);
int y = lua_tonumber(state, 3);
object->setLocation(x, y);
return 0;
}
int setBaseLocation(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(3);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, number);
HELPER_CHECK_ARGS_TYPE(3, number);
//Now get the pointer to the object.
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
int x = lua_tonumber(state, 2);
int y = lua_tonumber(state, 3);
object->setBaseLocation(x, y);
return 0;
}
int growTo(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(3);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, number);
HELPER_CHECK_ARGS_TYPE(3, number);
//Now get the pointer to the object.
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
int w = lua_tonumber(state, 2);
int h = lua_tonumber(state, 3);
object->growTo(w, h);
return 0;
}
int getSize(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
//Get the object.
lua_pushinteger(state, object->getBox().w);
lua_pushinteger(state, object->getBox().h);
return 2;
}
int getBaseSize(lua_State* state){
//Make sure there's only one argument and that argument is an userdatum.
HELPER_GET_AND_CHECK_ARGS(1);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
//Get the object.
SDL_Rect r = object->getBox(BoxType_Base);
lua_pushnumber(state, r.w);
lua_pushnumber(state, r.h);
return 2;
}
int setSize(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(3);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, number);
HELPER_CHECK_ARGS_TYPE(3, number);
//Now get the pointer to the object.
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
int w = lua_tonumber(state, 2);
int h = lua_tonumber(state, 3);
object->setSize(w, h);
return 0;
}
int setBaseSize(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(3);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, number);
HELPER_CHECK_ARGS_TYPE(3, number);
//Now get the pointer to the object.
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
int w = lua_tonumber(state, 2);
int h = lua_tonumber(state, 3);
object->setBaseSize(w, h);
return 0;
}
int getType(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL || object->type < 0 || object->type >= TYPE_MAX) return 0;
lua_pushstring(state, Game::blockName[object->type]);
return 1;
}
int changeThemeState(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, string);
Block* object = Block::getObjectFromUserData(state, 1);
object->appearance.changeState(lua_tostring(state, 2));
return 0;
}
int setVisible(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, boolean);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL)
return 0;
BlockScriptAPI::setFlags(object,
(BlockScriptAPI::getFlags(object) & ~0x80000000) | (lua_toboolean(state, 2) ? 0 : 0x80000000)
);
return 0;
}
int isVisible(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL)
return 0;
lua_pushboolean(state, (BlockScriptAPI::getFlags(object) & 0x80000000) ? 0 : 1);
return 1;
}
int getEventHandler(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, string);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
//Check event type
string eventType = lua_tostring(state, 2);
map<string, int>::iterator it = Game::gameObjectEventNameMap.find(eventType);
if (it == Game::gameObjectEventNameMap.end()) return 0;
//Check compiled script
map<int, int>::iterator script = object->compiledScripts.find(it->second);
if (script == object->compiledScripts.end()) return 0;
//Get event handler
lua_rawgeti(state, LUA_REGISTRYINDEX, script->second);
return 1;
}
//It will return old event handler.
int setEventHandler(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(3);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, string);
HELPER_CHECK_ARGS_TYPE_OR_NIL(3, function);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
//Check event type
string eventType = lua_tostring(state, 2);
map<string, int>::const_iterator it = Game::gameObjectEventNameMap.find(eventType);
if (it == Game::gameObjectEventNameMap.end()){
lua_pushfstring(state, "Unknown block event type: '%s'.", eventType.c_str());
return lua_error(state);
}
//Check compiled script
int scriptIndex = LUA_REFNIL;
{
map<int, int>::iterator script = object->compiledScripts.find(it->second);
if (script != object->compiledScripts.end()) scriptIndex = script->second;
}
//Set new event handler
object->compiledScripts[it->second] = luaL_ref(state, LUA_REGISTRYINDEX);
if (scriptIndex == LUA_REFNIL) return 0;
//Get old event handler and unreference it
lua_rawgeti(state, LUA_REGISTRYINDEX, scriptIndex);
luaL_unref(state, LUA_REGISTRYINDEX, scriptIndex);
return 1;
}
int onEvent(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, string);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
//Check event type
string eventType = lua_tostring(state, 2);
map<string, int>::const_iterator it = Game::gameObjectEventNameMap.find(eventType);
if (it == Game::gameObjectEventNameMap.end()){
lua_pushfstring(state, "Unknown block event type: '%s'.", eventType.c_str());
return lua_error(state);
}
object->onEvent(it->second);
return 0;
}
int isActivated(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_MOVING_BLOCK:
case TYPE_MOVING_SHADOW_BLOCK:
case TYPE_MOVING_SPIKES:
case TYPE_CONVEYOR_BELT:
case TYPE_SHADOW_CONVEYOR_BELT:
lua_pushboolean(state, (BlockScriptAPI::getFlags(object) & 0x1) ? 0 : 1);
return 1;
default:
return 0;
}
}
int setActivated(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, boolean);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_MOVING_BLOCK:
case TYPE_MOVING_SHADOW_BLOCK:
case TYPE_MOVING_SPIKES:
case TYPE_CONVEYOR_BELT:
case TYPE_SHADOW_CONVEYOR_BELT:
BlockScriptAPI::setFlags(object,
(BlockScriptAPI::getFlags(object) & ~1) | (lua_toboolean(state, 2) ? 0 : 1)
);
break;
}
return 0;
}
int isAutomatic(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_PORTAL:
lua_pushboolean(state, (BlockScriptAPI::getFlags(object) & 0x1) ? 1 : 0);
return 1;
default:
return 0;
}
}
int setAutomatic(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, boolean);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_PORTAL:
BlockScriptAPI::setFlags(object,
(BlockScriptAPI::getFlags(object) & ~1) | (lua_toboolean(state, 2) ? 1 : 0)
);
break;
}
return 0;
}
int getBehavior(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_BUTTON:
case TYPE_SWITCH:
switch (BlockScriptAPI::getFlags(object) & 0x3) {
default:
lua_pushstring(state, "toggle");
break;
case 1:
lua_pushstring(state, "on");
break;
case 2:
lua_pushstring(state, "off");
break;
}
return 1;
default:
return 0;
}
}
int setBehavior(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, string);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_BUTTON:
case TYPE_SWITCH:
{
int newFlags = BlockScriptAPI::getFlags(object) & ~3;
std::string s = lua_tostring(state, 2);
if (s == "on") newFlags |= 1;
else if (s == "off") newFlags |= 2;
BlockScriptAPI::setFlags(object, newFlags);
}
break;
}
return 0;
}
int getState(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_FRAGILE:
lua_pushinteger(state, BlockScriptAPI::getFlags(object) & 0x3);
return 1;
default:
return 0;
}
}
int setState(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, number);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_FRAGILE:
{
int oldState = BlockScriptAPI::getFlags(object) & 0x3;
int newState = (int)lua_tonumber(state, 2);
if (newState < 0) newState = 0;
else if (newState > 3) newState = 3;
if (newState != oldState) {
BlockScriptAPI::fragileUpdateState(object, newState);
}
}
break;
}
return 0;
}
int isPlayerOn(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_BUTTON:
lua_pushboolean(state, (BlockScriptAPI::getFlags(object) & 0x4) ? 1 : 0);
return 1;
default:
return 0;
}
}
int getPathMaxTime(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_MOVING_BLOCK:
case TYPE_MOVING_SHADOW_BLOCK:
case TYPE_MOVING_SPIKES:
lua_pushinteger(state, object->getPathMaxTime());
return 1;
default:
return 0;
}
}
int getPathTime(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_MOVING_BLOCK:
case TYPE_MOVING_SHADOW_BLOCK:
case TYPE_MOVING_SPIKES:
lua_pushinteger(state, BlockScriptAPI::getTemp(object));
return 1;
default:
return 0;
}
}
int setPathTime(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, number);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_MOVING_BLOCK:
case TYPE_MOVING_SHADOW_BLOCK:
case TYPE_MOVING_SPIKES:
BlockScriptAPI::setTemp(object, (int)lua_tonumber(state, 2));
break;
}
return 0;
}
int isLooping(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_MOVING_BLOCK:
case TYPE_MOVING_SHADOW_BLOCK:
case TYPE_MOVING_SPIKES:
lua_pushboolean(state, (BlockScriptAPI::getFlags(object) & 0x2) ? 0 : 1);
return 1;
default:
return 0;
}
}
int setLooping(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, boolean);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_MOVING_BLOCK:
case TYPE_MOVING_SHADOW_BLOCK:
case TYPE_MOVING_SPIKES:
BlockScriptAPI::setFlags(object,
(BlockScriptAPI::getFlags(object) & ~2) | (lua_toboolean(state, 2) ? 0 : 2)
);
break;
}
return 0;
}
int getSpeed(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_CONVEYOR_BELT:
case TYPE_SHADOW_CONVEYOR_BELT:
lua_pushinteger(state, BlockScriptAPI::getSpeed(object));
return 1;
default:
return 0;
}
}
int setSpeed(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, number);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_CONVEYOR_BELT:
case TYPE_SHADOW_CONVEYOR_BELT:
BlockScriptAPI::setSpeed(object, (int)lua_tonumber(state, 2));
break;
}
return 0;
}
int getAppearance(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
lua_pushstring(state, object->customAppearanceName.c_str());
return 1;
}
int setAppearance(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE_OR_NIL(2, string);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
if (lua_isnil(state, 2)) {
object->setEditorProperty("appearance", "");
} else {
object->setEditorProperty("appearance", lua_tostring(state, 2));
}
return 0;
}
int getId(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
lua_pushstring(state, object->id.c_str());
return 1;
}
int setId(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE_OR_NIL(2, string);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
if (lua_isnil(state, 2)) {
object->id.clear();
} else {
object->id = lua_tostring(state, 2);
}
return 0;
}
int getDestination(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_PORTAL:
lua_pushstring(state, object->destination.c_str());
return 1;
default:
return 0;
}
return 1;
}
int setDestination(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE_OR_NIL(2, string);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_PORTAL:
if (lua_isnil(state, 2)) {
object->destination.clear();
} else {
object->destination = lua_tostring(state, 2);
}
break;
}
return 0;
}
int getMessage(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_NOTIFICATION_BLOCK:
lua_pushstring(state, object->message.c_str());
return 1;
default:
return 0;
}
return 1;
}
int setMessage(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE_OR_NIL(2, string);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
std::string newMessage;
switch (object->type) {
case TYPE_NOTIFICATION_BLOCK:
if (!lua_isnil(state, 2)) {
newMessage = lua_tostring(state, 2);
}
if (newMessage != object->message) {
object->message = newMessage;
//Invalidate the notification texture
if (Game* game = dynamic_cast<Game*>(currentState)) {
game->invalidateNotificationTexture(object);
}
}
break;
}
return 0;
}
int getMovingPosCount(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_MOVING_BLOCK:
case TYPE_MOVING_SHADOW_BLOCK:
case TYPE_MOVING_SPIKES:
lua_pushinteger(state, BlockScriptAPI::getMovingPos(object).size());
return 1;
default:
return 0;
}
}
void _pushAMovingPos(lua_State* state, const SDL_Rect& r) {
lua_createtable(state, 3, 0);
lua_pushinteger(state, r.x);
lua_rawseti(state, -2, 1);
lua_pushinteger(state, r.y);
lua_rawseti(state, -2, 2);
lua_pushinteger(state, r.w);
lua_rawseti(state, -2, 3);
}
int getMovingPos(lua_State* state) {
//Available overloads:
//getMovingPos()
//getMovingPos(index)
//getMovingPos(start, length)
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS_RANGE(1, 3);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_OPTIONAL_ARGS_TYPE(2, number);
HELPER_CHECK_OPTIONAL_ARGS_TYPE(3, number);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_MOVING_BLOCK:
case TYPE_MOVING_SHADOW_BLOCK:
case TYPE_MOVING_SPIKES:
break;
default:
return 0;
}
const std::vector<SDL_Rect> &movingPos = BlockScriptAPI::getMovingPos(object);
const int m = movingPos.size();
int start = 0, length = -1;
if (args >= 2) start = lua_tonumber(state, 2) - 1;
if (args >= 3) length = lua_tonumber(state, 3);
//Length<0 means get all of remaining points
if (length < 0) length = m - start;
//Some sanity check
if (start < 0) return 0;
if (start + length > m) length = m - start;
if (length < 0) length = 0;
if (args == 2) {
//Get single point
//Sanity check
if (start >= m) return 0;
_pushAMovingPos(state, movingPos[start]);
} else {
//Get array of points
lua_createtable(state, length, 0);
for (int i = 0; i < length; i++) {
_pushAMovingPos(state, movingPos[start + i]);
lua_rawseti(state, -2, i + 1);
}
}
return 1;
}
SDL_Rect _getAMovingPos(lua_State* state, int index) {
SDL_Rect ret = { 0, 0, 0, 0 };
if (lua_istable(state, index) && lua_rawlen(state, index) >= 3) {
lua_rawgeti(state, index, 1);
ret.x = lua_tonumber(state, -1);
lua_pop(state, 1);
lua_rawgeti(state, index, 2);
ret.y = lua_tonumber(state, -1);
lua_pop(state, 1);
lua_rawgeti(state, index, 3);
ret.w = lua_tonumber(state, -1);
lua_pop(state, 1);
}
return ret;
}
void _getArrayOfMovingPos(lua_State* state, int index, std::vector<SDL_Rect>& ret, int maxLength = -1) {
if (lua_istable(state, index)) {
int m = lua_rawlen(state, index);
if (maxLength >= 0 && m > maxLength) m = maxLength;
for (int i = 0; i < m; i++) {
lua_rawgeti(state, index, i + 1);
ret.push_back(_getAMovingPos(state, -1));
lua_pop(state, 1);
}
}
}
int setMovingPos(lua_State* state) {
//Available overloads:
//setMovingPos(array)
//setMovingPos(index, point)
//setMovingPos(start, length, array)
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS_RANGE(2, 4);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
for (int i = 2; i < args; i++) {
HELPER_CHECK_ARGS_TYPE(i, number);
}
HELPER_CHECK_ARGS_TYPE(args, table);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_MOVING_BLOCK:
case TYPE_MOVING_SHADOW_BLOCK:
case TYPE_MOVING_SPIKES:
break;
default:
return 0;
}
std::vector<SDL_Rect> &movingPos = BlockScriptAPI::getMovingPos(object);
if (args == 2) {
//Overwrite the whole array
movingPos.clear();
_getArrayOfMovingPos(state, args, movingPos);
BlockScriptAPI::invalidatePathMaxTime(object);
return 0;
}
const int m = movingPos.size();
int start = 0, length = -1;
if (args >= 3) start = lua_tonumber(state, 2) - 1;
if (args >= 4) length = lua_tonumber(state, 3);
//Length<0 means set all of remaining points
if (length < 0) length = m - start;
//Some sanity check
if (start < 0) return 0;
if (start + length > m) length = m - start;
if (length < 0) length = 0;
if (args == 3) {
//Set single point
//Sanity check
if (start >= m) return 0;
movingPos[start] = _getAMovingPos(state, args);
BlockScriptAPI::invalidatePathMaxTime(object);
} else if (length > 0) {
//Set array of points
std::vector<SDL_Rect> newPos;
_getArrayOfMovingPos(state, args, newPos, length);
length = newPos.size();
for (int i = 0; i < length; i++) {
movingPos[start + i] = newPos[i];
}
if (length > 0) {
BlockScriptAPI::invalidatePathMaxTime(object);
}
}
return 0;
}
int addMovingPos(lua_State* state) {
//Available overloads:
//addMovingPos(p)
//addMovingPos(index, p)
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS_RANGE(2, 3);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
for (int i = 2; i < args; i++) {
HELPER_CHECK_ARGS_TYPE(i, number);
}
HELPER_CHECK_ARGS_TYPE(args, table);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_MOVING_BLOCK:
case TYPE_MOVING_SHADOW_BLOCK:
case TYPE_MOVING_SPIKES:
break;
default:
return 0;
}
std::vector<SDL_Rect> &movingPos = BlockScriptAPI::getMovingPos(object);
const int m = movingPos.size();
int start = m;
if (args >= 3) start = lua_tonumber(state, 2) - 1;
//Some sanity check
if (start < 0) start = 0;
if (start > m) start = m;
//Get the list of points
std::vector<SDL_Rect> newPos;
bool singlePoint = false;
if (lua_istable(state, args) && lua_rawlen(state, args) >= 3) {
lua_rawgeti(state, args, 1);
lua_rawgeti(state, args, 2);
lua_rawgeti(state, args, 3);
if (lua_isnumber(state, -3) && lua_isnumber(state, -2) && lua_isnumber(state, -1)) {
newPos.push_back(SDL_Rect{
lua_tonumber(state, -3),
lua_tonumber(state, -2),
lua_tonumber(state, -1),
0
});
singlePoint = true;
}
lua_pop(state, 3);
}
if (!singlePoint) {
_getArrayOfMovingPos(state, args, newPos);
}
if (!newPos.empty()) {
movingPos.insert(movingPos.begin() + start, newPos.begin(), newPos.end());
BlockScriptAPI::invalidatePathMaxTime(object);
}
return 0;
}
void _getArrayOfInteger(lua_State* state, int index, std::vector<int>& ret) {
if (lua_istable(state, index)) {
int m = lua_rawlen(state, index);
for (int i = 0; i < m; i++) {
lua_rawgeti(state, index, i + 1);
if (lua_isnumber(state, -1)) {
ret.push_back(lua_tonumber(state, -1));
}
lua_pop(state, 1);
}
}
}
int removeMovingPos(lua_State* state) {
//Available overloads:
//removeMovingPos()
//removeMovingPos(index)
//removeMovingPos(listOfIndices)
//removeMovingPos(start, length)
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS_RANGE(1, 3);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
switch (args) {
case 2:
HELPER_CHECK_ARGS_TYPE_2(2, number, table);
break;
case 3:
HELPER_CHECK_ARGS_TYPE(2, number);
HELPER_CHECK_ARGS_TYPE(3, number);
break;
}
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
switch (object->type) {
case TYPE_MOVING_BLOCK:
case TYPE_MOVING_SHADOW_BLOCK:
case TYPE_MOVING_SPIKES:
break;
default:
return 0;
}
std::vector<SDL_Rect> &movingPos = BlockScriptAPI::getMovingPos(object);
if (args == 1) {
movingPos.clear();
BlockScriptAPI::invalidatePathMaxTime(object);
return 0;
}
const int m = movingPos.size();
if (args == 3) {
int start = lua_tonumber(state, 2) - 1;
int length = lua_tonumber(state, 3);
//Length<0 means remove all of remaining points
if (length < 0) length = m - start;
//Some sanity check
if (start < 0 || start >= m) return 0;
if (start + length > m) length = m - start;
if (length < 0) length = 0;
if (length > 0) {
movingPos.erase(movingPos.begin() + start, movingPos.begin() + (start + length));
BlockScriptAPI::invalidatePathMaxTime(object);
}
return 0;
}
if (lua_isnumber(state, 2)) {
int start = lua_tonumber(state, 2) - 1;
//Some sanity check
if (start < 0 || start >= m) return 0;
movingPos.erase(movingPos.begin() + start);
BlockScriptAPI::invalidatePathMaxTime(object);
return 0;
}
std::vector<int> indices;
_getArrayOfInteger(state, 2, indices);
std::sort(indices.begin(), indices.end());
int i2 = 0, j = 0;
const int m2 = indices.size();
for (int i = 0; i < m; i++) {
// find the first index which is >= current
while (i2 < m2 && indices[i2] < i + 1) i2++;
if (i2 < m2 && indices[i2] == i + 1) {
// this point will be removed
j++;
} else {
// this point is preserved
if (j > 0) {
movingPos[i - j] = movingPos[i];
}
}
}
if (j > 0) {
movingPos.resize(m - j);
BlockScriptAPI::invalidatePathMaxTime(object);
}
return 0;
}
int remove(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Block* object = Block::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
object->deleteMe();
return 0;
}
int removeAll(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(0);
//Check if the currentState is the game state.
Game* game = dynamic_cast<Game*>(currentState);
if (game == NULL) return 0;
for (auto o : game->levelObjects) {
if (o) o->deleteMe();
}
return 0;
}
int addBlock(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS_RANGE(1, 5);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE(1, string);
for (int i = 2; i <= args; i++) {
HELPER_CHECK_ARGS_TYPE(i, number);
}
//Check if the currentState is the game state.
Game* game = dynamic_cast<Game*>(currentState);
if (game == NULL) return 0;
TreeStorageNode root;
//Load from the string.
{
POASerializer objSerializer;
istringstream stream(lua_tostring(state, 1));
if (!objSerializer.readNode(stream, &root, true)) {
return luaL_error(state, "Failed to load node from string in %s", __FUNCTION__);
}
}
//Load the first valid block in the subnodes.
for (auto obj1 : root.subNodes) {
if (obj1 == NULL) continue;
if (obj1->name == "tile"){
Block* block = new Block(game);
if (!block->loadFromNode(getImageManager(), getRenderer(), obj1)) {
delete block;
continue;
}
//Reposition the block if necessary
SDL_Rect r = block->getBox(BoxType_Base);
if (args >= 2) {
r.x = lua_tonumber(state, 2);
if (args >= 3) r.y = lua_tonumber(state, 3);
block->setBaseLocation(r.x, r.y);
}
if (args >= 4) {
r.w = lua_tonumber(state, 4);
if (args >= 5) r.h = lua_tonumber(state, 5);
block->setBaseSize(r.w, r.h);
}
//If the type is collectable, increase the number of totalCollectables
if (block->type == TYPE_COLLECTABLE) {
game->totalCollectables++;
}
//Add the block to the levelObjects vector.
game->levelObjects.push_back(block);
//Enable the access to this block from script.
block->setActive();
//Compile the block script.
for (auto it = block->scripts.begin(); it != block->scripts.end(); ++it){
int index = game->getScriptExecutor()->compileScript(it->second);
block->compiledScripts[it->first] = index;
}
//Trigger the onCreate event.
block->onEvent(GameObjectEvent_OnCreate);
//Return the newly created block.
block->createUserData(state, "block");
return 1;
}
}
return 0;
}
+ SDL_Rect _getAnSDLRect(lua_State* state, int index) {
+ SDL_Rect ret = { 0x80000000, 0x80000000, 0x80000000, 0x80000000 };
+
+ if (lua_istable(state, index)) {
+ const int m = lua_rawlen(state, index);
+ if (m >= 1) {
+ lua_rawgeti(state, index, 1);
+ int n, b; n = lua_tonumberx(state, -1, &b);
+ if (b) ret.x = n;
+ lua_pop(state, 1);
+ }
+ if (m >= 2) {
+ lua_rawgeti(state, index, 2);
+ int n, b; n = lua_tonumberx(state, -1, &b);
+ if (b) ret.y = n;
+ lua_pop(state, 1);
+ }
+ if (m >= 3) {
+ lua_rawgeti(state, index, 3);
+ int n, b; n = lua_tonumberx(state, -1, &b);
+ if (b) ret.w = n;
+ lua_pop(state, 1);
+ }
+ if (m >= 4) {
+ lua_rawgeti(state, index, 4);
+ int n, b; n = lua_tonumberx(state, -1, &b);
+ if (b) ret.h = n;
+ lua_pop(state, 1);
+ }
+ }
+
+ return ret;
+ }
+
+ void _getArrayOfSDLRect(lua_State* state, int index, std::vector<SDL_Rect>& ret) {
+ if (lua_istable(state, index)) {
+ int m = lua_rawlen(state, index);
+ for (int i = 0; i < m; i++) {
+ lua_rawgeti(state, index, i + 1);
+ ret.push_back(_getAnSDLRect(state, -1));
+ lua_pop(state, 1);
+ }
+ }
+ }
+
+ int addBlocks(lua_State* state) {
+ //Available overloads:
+ //addBlocks(string)
+ //addBlocks(string,positions)
+ //addBlocks(string,offsetX,offsetY)
+
+ //Check the number of arguments.
+ HELPER_GET_AND_CHECK_ARGS_RANGE(1, 3);
+
+ //Check if the arguments are of the right type.
+ HELPER_CHECK_ARGS_TYPE(1, string);
+ switch (args) {
+ case 2:
+ HELPER_CHECK_ARGS_TYPE(2, table);
+ break;
+ case 3:
+ HELPER_CHECK_ARGS_TYPE(2, number);
+ HELPER_CHECK_ARGS_TYPE(3, number);
+ break;
+ }
+
+ //Check if the currentState is the game state.
+ Game* game = dynamic_cast<Game*>(currentState);
+ if (game == NULL) return 0;
+
+ TreeStorageNode root;
+
+ //Load from the string.
+ {
+ POASerializer objSerializer;
+ istringstream stream(lua_tostring(state, 1));
+ if (!objSerializer.readNode(stream, &root, true)) {
+ return luaL_error(state, "Failed to load node from string in %s", __FUNCTION__);
+ }
+ }
+
+ std::vector<TreeStorageNode*> blockNodes;
+
+ //Get available blocks.
+ for (auto obj1 : root.subNodes) {
+ if (obj1 == NULL) continue;
+ if (obj1->name == "tile") blockNodes.push_back(obj1);
+ }
+
+ std::vector<SDL_Rect> positions;
+ std::vector<Block*> blocks;
+ int offsetX = 0, offsetY = 0;
+
+ //Check if we should get positions.
+ if (args == 2) {
+ _getArrayOfSDLRect(state, 2, positions);
+
+ //Check if we should load block repeatedly.
+ if (blockNodes.size() == 1 && positions.size() >= 1) {
+ Block* blockTemplate = new Block(game);
+ if (!blockTemplate->loadFromNode(getImageManager(), getRenderer(), blockNodes[0])) {
+ delete blockTemplate;
+
+ //Just return an empty array.
+ lua_createtable(state, 0, 0);
+ return 1;
+ }
+
+ //Compile the block script.
+ for (auto it = blockTemplate->scripts.begin(); it != blockTemplate->scripts.end(); ++it){
+ int index = game->getScriptExecutor()->compileScript(it->second);
+ blockTemplate->compiledScripts[it->first] = index;
+ }
+
+ for (int i = 0, m = positions.size(); i < m; i++) {
+ Block *block;
+ if (i < m - 1) {
+ block = new Block(*blockTemplate);
+ //Ad-hoc code to make the block proxy unique
+ block->proxy.reset(new Block::Proxy);
+ } else {
+ block = blockTemplate;
+ }
+
+ //Reposition the block if necessary
+ SDL_Rect r = block->getBox(BoxType_Base);
+ SDL_Rect r1 = positions[i];
+ if (r1.x != 0x80000000 || r1.y != 0x80000000) {
+ if (r1.x != 0x80000000) r.x = r1.x;
+ if (r1.y != 0x80000000) r.y = r1.y;
+ block->setBaseLocation(r.x, r.y);
+ }
+ if (r1.w != 0x80000000 || r1.h != 0x80000000) {
+ if (r1.w != 0x80000000) r.w = r1.w;
+ if (r1.h != 0x80000000) r.h = r1.h;
+ block->setBaseSize(r.w, r.h);
+ }
+
+ //Add it to the temp array
+ blocks.push_back(block);
+ }
+ }
+ }
+
+ //Check if we should use offsets.
+ if (args == 3) {
+ offsetX = lua_tonumber(state, 2);
+ offsetY = lua_tonumber(state, 3);
+ }
+
+ //Check if we should load block in a regular way.
+ if (blocks.empty()) {
+ for (int i = 0, m = blockNodes.size(); i < m; i++) {
+ Block* block = new Block(game);
+ if (!block->loadFromNode(getImageManager(), getRenderer(), blockNodes[i])) {
+ delete block;
+ continue;
+ }
+
+ //Reposition the block if necessary
+ SDL_Rect r = block->getBox(BoxType_Base);
+ SDL_Rect r1 = (args == 3) ? SDL_Rect{ r.x + offsetX, r.y + offsetY, 0x80000000, 0x80000000 } :
+ (i < (int)positions.size()) ? positions[i] : SDL_Rect{ 0x80000000, 0x80000000, 0x80000000, 0x80000000 };
+ if (r1.x != 0x80000000 || r1.y != 0x80000000) {
+ if (r1.x != 0x80000000) r.x = r1.x;
+ if (r1.y != 0x80000000) r.y = r1.y;
+ block->setBaseLocation(r.x, r.y);
+ }
+ if (r1.w != 0x80000000 || r1.h != 0x80000000) {
+ if (r1.w != 0x80000000) r.w = r1.w;
+ if (r1.h != 0x80000000) r.h = r1.h;
+ block->setBaseSize(r.w, r.h);
+ }
+
+ //Compile the block script.
+ for (auto it = block->scripts.begin(); it != block->scripts.end(); ++it){
+ int index = game->getScriptExecutor()->compileScript(it->second);
+ block->compiledScripts[it->first] = index;
+ }
+
+ //Add it to the temp array
+ blocks.push_back(block);
+ }
+ }
+
+ for (auto block : blocks) {
+ //If the type is collectable, increase the number of totalCollectables
+ if (block->type == TYPE_COLLECTABLE) {
+ game->totalCollectables++;
+ }
+
+ //Add the block to the levelObjects vector.
+ game->levelObjects.push_back(block);
+
+ //Enable the access to this block from script.
+ block->setActive();
+
+ //Trigger the onCreate event.
+ block->onEvent(GameObjectEvent_OnCreate);
+ }
+
+ lua_createtable(state, blocks.size(), 0);
+
+ for (int i = 0, m = blocks.size(); i < m; i++) {
+ blocks[i]->createUserData(state, "block");
+ lua_rawseti(state, -2, i + 1);
+ }
+
+ return 1;
+ }
+
}
#define _L block
//Array with the methods for the block library.
static const luaL_Reg blocklib_m[]={
_FI(Valid),
_FG(BlockById),
_FG(BlocksById),
_F(moveTo),
_FGS(Location),
_FGS(BaseLocation),
_F(growTo),
_FGS(Size),
_FGS(BaseSize),
_FG(Type),
_F(changeThemeState),
_FIS(Visible),
_FGS(EventHandler),
_F(onEvent),
_FIS(Activated),
_FIS(Automatic),
_FGS(Behavior),
_FGS(State),
_FI(PlayerOn),
_FG(PathMaxTime),
_FGS(PathTime),
_FIS(Looping),
_FGS(Speed),
_FGS(Appearance),
_FGS(Id),
_FGS(Destination),
_FGS(Message),
_FG(MovingPosCount),
_FGS(MovingPos),
_F(addMovingPos),
_F(removeMovingPos),
_F(remove),
_F(removeAll),
_F(addBlock),
+ _F(addBlocks),
{ NULL, NULL }
};
#undef _L
int luaopen_block(lua_State* state){
luaL_newlib(state,blocklib_m);
//Create the metatable for the block userdata.
luaL_newmetatable(state,"block");
lua_pushstring(state,"__index");
lua_pushvalue(state,-2);
lua_settable(state,-3);
Block::registerMetatableFunctions(state,-3);
//Register the functions and methods.
luaL_setfuncs(state,blocklib_m,0);
return 1;
}
//////////////////////////PLAYER SPECIFIC///////////////////////////
class PlayerScriptAPI {
public:
static bool isInAir(Player* player) {
return player->inAir;
}
static bool canMode(Player* player) {
return player->canMove;
}
static bool isDead(Player* player) {
return player->dead;
}
static bool isHoldingOther(Player* player) {
return player->holdingOther;
}
};
struct PlayerUserDatum{
char sig1,sig2,sig3,sig4;
};
Player* getPlayerFromUserData(lua_State* state,int idx){
PlayerUserDatum* ud=(PlayerUserDatum*)lua_touserdata(state,1);
//Make sure the user datum isn't null.
if(!ud) return NULL;
//Get the game state.
Game* game=dynamic_cast<Game*>(currentState);
if(game==NULL) return NULL;
Player* player=NULL;
//Check the signature to see if it's the player or the shadow.
if(ud->sig1=='P' && ud->sig2=='L' && ud->sig3=='Y' && ud->sig4=='R')
player=&game->player;
else if(ud->sig1=='S' && ud->sig2=='H' && ud->sig3=='D' && ud->sig4=='W')
player=&game->shadow;
return player;
}
namespace playershadow {
int getLocation(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Player* player = getPlayerFromUserData(state, 1);
if (player == NULL) return 0;
//Get the object.
lua_pushinteger(state, player->getBox().x);
lua_pushinteger(state, player->getBox().y);
return 2;
}
int setLocation(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(3);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, number);
HELPER_CHECK_ARGS_TYPE(3, number);
//Get the player.
Player* player = getPlayerFromUserData(state, 1);
if (player == NULL) return 0;
//Get the new location.
int x = lua_tonumber(state, 2);
int y = lua_tonumber(state, 3);
player->setLocation(x, y);
return 0;
}
int jump(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS_2(1, 2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_OPTIONAL_ARGS_TYPE(2, number);
//Get the player.
Player* player = getPlayerFromUserData(state, 1);
if (player == NULL) return 0;
//Get the new location.
if (args == 2){
int yVel = lua_tonumber(state, 2);
player->jump(yVel);
} else{
//Use default jump strength.
player->jump();
}
return 0;
}
int isShadow(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Player* player = getPlayerFromUserData(state, 1);
if (player == NULL) return 0;
lua_pushboolean(state, player->isShadow());
return 1;
}
int getCurrentStand(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Player* player = getPlayerFromUserData(state, 1);
if (player == NULL) return 0;
//Get the actual game object.
Block* object = player->getObjCurrentStand();
if (object == NULL){
return 0;
}
//Create the userdatum.
object->createUserData(state, "block");
//We return one object, the userdatum.
return 1;
}
int isInAir(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Player* player = getPlayerFromUserData(state, 1);
if (player == NULL) return 0;
lua_pushboolean(state, PlayerScriptAPI::isInAir(player));
return 1;
}
int canMove(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Player* player = getPlayerFromUserData(state, 1);
if (player == NULL) return 0;
lua_pushboolean(state, PlayerScriptAPI::canMode(player));
return 1;
}
int isDead(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Player* player = getPlayerFromUserData(state, 1);
if (player == NULL) return 0;
lua_pushboolean(state, PlayerScriptAPI::isDead(player));
return 1;
}
int isHoldingOther(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
Player* player = getPlayerFromUserData(state, 1);
if (player == NULL) return 0;
lua_pushboolean(state, PlayerScriptAPI::isHoldingOther(player));
return 1;
}
}
#define _L playershadow
//Array with the methods for the player and shadow library.
static const luaL_Reg playerlib_m[]={
_FGS(Location),
_F(jump),
_FI(Shadow),
_FG(CurrentStand),
_FI(InAir),
_F(canMove),
_FI(Dead),
_FI(HoldingOther),
{ NULL, NULL }
};
#undef _L
int luaopen_player(lua_State* state){
luaL_newlib(state,playerlib_m);
//Create the metatable for the player userdata.
luaL_newmetatable(state,"player");
lua_pushstring(state,"__index");
lua_pushvalue(state,-2);
lua_settable(state,-3);
//Now create two default player user data, one for the player and one for the shadow.
PlayerUserDatum* ud=(PlayerUserDatum*)lua_newuserdata(state,sizeof(PlayerUserDatum));
ud->sig1='P';ud->sig2='L';ud->sig3='Y';ud->sig4='R';
luaL_getmetatable(state,"player");
lua_setmetatable(state,-2);
lua_setglobal(state,"player");
ud=(PlayerUserDatum*)lua_newuserdata(state,sizeof(PlayerUserDatum));
ud->sig1='S';ud->sig2='H';ud->sig3='D';ud->sig4='W';
luaL_getmetatable(state,"player");
lua_setmetatable(state,-2);
lua_setglobal(state,"shadow");
//Register the functions and methods.
luaL_setfuncs(state,playerlib_m,0);
return 1;
}
//////////////////////////LEVEL SPECIFIC///////////////////////////
namespace level {
int getSize(lua_State* state){
//NOTE: this function accepts 0 arguments, but we ignore the argument count.
//Returns level size.
lua_pushinteger(state, LEVEL_WIDTH);
lua_pushinteger(state, LEVEL_HEIGHT);
return 2;
}
int getWidth(lua_State* state){
//NOTE: this function accepts 0 arguments, but we ignore the argument count.
//Returns level size.
lua_pushinteger(state, LEVEL_WIDTH);
return 1;
}
int getHeight(lua_State* state){
//NOTE: this function accepts 0 arguments, but we ignore the argument count.
//Returns level size.
lua_pushinteger(state, LEVEL_HEIGHT);
return 1;
}
int getName(lua_State* state){
//NOTE: this function accepts 0 arguments, but we ignore the argument count.
//Check if the currentState is the game state.
Game* game = dynamic_cast<Game*>(currentState);
if (game == NULL) return 0;
//Returns level name.
lua_pushstring(state, game->getLevelName().c_str());
return 1;
}
int getEventHandler(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE(1, string);
//Check if the currentState is the game state.
Game* game = dynamic_cast<Game*>(currentState);
if (game == NULL) return 0;
//Check event type
string eventType = lua_tostring(state, 1);
map<string, int>::iterator it = Game::levelEventNameMap.find(eventType);
if (it == Game::levelEventNameMap.end()) return 0;
//Check compiled script
map<int, int>::iterator script = game->compiledScripts.find(it->second);
if (script == game->compiledScripts.end()) return 0;
//Get event handler
lua_rawgeti(state, LUA_REGISTRYINDEX, script->second);
return 1;
}
//It will return old event handler.
int setEventHandler(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE(1, string);
HELPER_CHECK_ARGS_TYPE_OR_NIL(2, function);
//Check if the currentState is the game state.
Game* game = dynamic_cast<Game*>(currentState);
if (game == NULL) return 0;
//Check event type
string eventType = lua_tostring(state, 1);
map<string, int>::const_iterator it = Game::levelEventNameMap.find(eventType);
if (it == Game::levelEventNameMap.end()){
lua_pushfstring(state, "Unknown level event type: '%s'.", eventType.c_str());
return lua_error(state);
}
//Check compiled script
int scriptIndex = LUA_REFNIL;
{
map<int, int>::iterator script = game->compiledScripts.find(it->second);
if (script != game->compiledScripts.end()) scriptIndex = script->second;
}
//Set new event handler
game->compiledScripts[it->second] = luaL_ref(state, LUA_REGISTRYINDEX);
if (scriptIndex == LUA_REFNIL) return 0;
//Get old event handler and unreference it
lua_rawgeti(state, LUA_REGISTRYINDEX, scriptIndex);
luaL_unref(state, LUA_REGISTRYINDEX, scriptIndex);
return 1;
}
int win(lua_State* state){
//NOTE: this function accepts 0 arguments, but we ignore the argument count.
//Check if the currentState is the game state.
if (stateID == STATE_LEVEL_EDITOR)
return 0;
Game* game = dynamic_cast<Game*>(currentState);
if (game == NULL) return 0;
game->won = true;
return 0;
}
int getTime(lua_State* state){
//NOTE: this function accepts 0 arguments, but we ignore the argument count.
//Check if the currentState is the game state.
Game* game = dynamic_cast<Game*>(currentState);
if (game == NULL) return 0;
//Returns level size.
lua_pushinteger(state, game->time);
return 1;
}
int getRecordings(lua_State* state){
//NOTE: this function accepts 0 arguments, but we ignore the argument count.
//Check if the currentState is the game state.
Game* game = dynamic_cast<Game*>(currentState);
if (game == NULL) return 0;
//Returns level size.
lua_pushinteger(state, game->recordings);
return 1;
}
int broadcastObjectEvent(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS_RANGE(1, 4);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE(1, string);
HELPER_CHECK_OPTIONAL_ARGS_TYPE_OR_NIL(2, string);
HELPER_CHECK_OPTIONAL_ARGS_TYPE_OR_NIL(3, string);
HELPER_CHECK_OPTIONAL_ARGS_TYPE_OR_NIL_NO_HINT(4, userdata);
//Check event type
int eventType = 0;
{
string s = lua_tostring(state, 1);
auto it = Game::gameObjectEventNameMap.find(s);
if (it == Game::gameObjectEventNameMap.end()){
lua_pushfstring(state, "Unknown block event type: '%s'.", s.c_str());
return lua_error(state);
} else {
eventType = it->second;
}
}
//Check object type
int objType = -1;
if (args >= 2 && lua_isstring(state, 2)) {
string s = lua_tostring(state, 2);
auto it = Game::blockNameMap.find(s);
if (it == Game::blockNameMap.end()){
lua_pushfstring(state, "Unknown object type: '%s'.", s.c_str());
return lua_error(state);
} else {
objType = it->second;
}
}
//Check id
const char* id = NULL;
if (args >= 3 && lua_isstring(state, 3)) {
id = lua_tostring(state, 3);
}
//Check target
Block *target = NULL;
if (args >= 4) {
target = Block::getObjectFromUserData(state, 4);
}
//Check if the currentState is the game state.
Game* game = dynamic_cast<Game*>(currentState);
if (game == NULL) return 0;
game->broadcastObjectEvent(eventType, objType, id, target);
return 0;
}
}
#define _L level
//Array with the methods for the level library.
static const luaL_Reg levellib_m[]={
_FG(Size),
_FG(Width),
_FG(Height),
_FG(Name),
_FGS(EventHandler),
_F(win),
_FG(Time),
_FG(Recordings),
_F(broadcastObjectEvent),
{NULL,NULL}
};
#undef _L
int luaopen_level(lua_State* state){
luaL_newlib(state,levellib_m);
//Register the functions and methods.
luaL_setfuncs(state,levellib_m,0);
return 1;
}
/////////////////////////CAMERA SPECIFIC///////////////////////////
//FIXME: I can't define namespace camera since there is already a global variable named camera.
//Therefore I use struct camera for a workaround.
struct camera {
static int setMode(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE(1, string);
string mode = lua_tostring(state, 1);
//Get the game for setting the camera.
Game* game = dynamic_cast<Game*>(currentState);
if (game == NULL) return 0;
//Check which mode.
if (mode == "player"){
game->cameraMode = Game::CAMERA_PLAYER;
} else if (mode == "shadow"){
game->cameraMode = Game::CAMERA_SHADOW;
} else{
//Unknown OR invalid camera mode.
return luaL_error(state, "Unknown or invalid camera mode for %s: '%s'.", __FUNCTION__, mode.c_str());
}
//Returns nothing.
return 0;
}
static int lookAt(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE(1, number);
HELPER_CHECK_ARGS_TYPE(2, number);
//Get the point.
int x = lua_tonumber(state, 1);
int y = lua_tonumber(state, 2);
//Get the game for setting the camera.
Game* game = dynamic_cast<Game*>(currentState);
if (game == NULL) return 0;
game->cameraMode = Game::CAMERA_CUSTOM;
game->cameraTarget.x = x;
game->cameraTarget.y = y;
return 0;
}
};
#define _L camera
//Array with the methods for the camera library.
static const luaL_Reg cameralib_m[]={
_FS(Mode),
_F(lookAt),
{NULL,NULL}
};
#undef _L
int luaopen_camera(lua_State* state){
luaL_newlib(state,cameralib_m);
//Register the functions and methods.
luaL_setfuncs(state,cameralib_m,0);
return 1;
}
/////////////////////////AUDIO SPECIFIC///////////////////////////
namespace audio {
int playSound(lua_State* state){
//Get the number of args, this can be anything from one to three.
HELPER_GET_AND_CHECK_ARGS_RANGE(1, 4);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE(1, string);
HELPER_CHECK_OPTIONAL_ARGS_TYPE(2, number);
HELPER_CHECK_OPTIONAL_ARGS_TYPE(3, boolean);
HELPER_CHECK_OPTIONAL_ARGS_TYPE(4, number);
//Default values for concurrent and force.
//See SoundManager.h
int concurrent = -1;
bool force = false;
int fadeMusic = -1;
//If there's a second one it should be an integer.
if (args > 1){
concurrent = lua_tonumber(state, 2);
}
//If there's a third one it should be a boolean.
if (args > 2){
force = lua_toboolean(state, 3);
}
if (args > 3){
fadeMusic = lua_tonumber(state, 4);
}
//Get the name of the sound.
string sound = lua_tostring(state, 1);
//Try to play the sound.
int channel = getSoundManager()->playSound(sound, concurrent, force, fadeMusic);
//Returns whether the operation is successful.
lua_pushboolean(state, channel >= 0 ? 1 : 0);
return 1;
}
int playMusic(lua_State* state){
//Get the number of args, this can be either one or two.
HELPER_GET_AND_CHECK_ARGS_2(1, 2);
//Make sure the first argument is a string.
HELPER_CHECK_ARGS_TYPE(1, string);
HELPER_CHECK_OPTIONAL_ARGS_TYPE(2, boolean);
//Default value of fade for playMusic.
//See MusicManager.h.
bool fade = true;
//If there's a second one it should be a boolean.
if (args > 1){
fade = lua_toboolean(state, 2);
}
//Get the name of the music.
string music = lua_tostring(state, 1);
//Try to switch to the new music.
getMusicManager()->playMusic(music, fade);
//Returns nothing.
return 0;
}
int pickMusic(lua_State* state){
//NOTE: this function accepts 0 arguments, but we ignore the argument count.
//Let the music manager pick a song from the current music list.
getMusicManager()->pickMusic();
return 0;
}
int setMusicList(lua_State* state){
//Get the number of args, this MUST be one.
HELPER_GET_AND_CHECK_ARGS(1);
//Make sure the given argument is a string.
HELPER_CHECK_ARGS_TYPE(1, string);
//And set the music list in the music manager.
string list = lua_tostring(state, 1);
getMusicManager()->setMusicList(list);
return 0;
}
int getMusicList(lua_State* state){
//NOTE: this function accepts 0 arguments, but we ignore the argument count.
//Return the name of the song (contains list prefix).
lua_pushstring(state, getMusicManager()->getCurrentMusicList().c_str());
return 1;
}
int currentMusic(lua_State* state){
//NOTE: this function accepts 0 arguments, but we ignore the argument count.
//Return the name of the song (contains list prefix).
lua_pushstring(state, getMusicManager()->getCurrentMusic().c_str());
return 1;
}
}
#define _L audio
//Array with the methods for the audio library.
static const luaL_Reg audiolib_m[]={
_F(playSound),
_F(playMusic),
_F(pickMusic),
_FGS(MusicList),
_F(currentMusic),
{NULL,NULL}
};
#undef _L
int luaopen_audio(lua_State* state){
luaL_newlib(state,audiolib_m);
//Register the functions and methods.
luaL_setfuncs(state,audiolib_m,0);
return 1;
}
/////////////////////////DELAY EXECUTION SPECIFIC///////////////////////////
namespace delayExecution {
HELPER_REGISTER_IS_VALID_FUNCTION(ScriptDelayExecution);
int schedule(lua_State* state) {
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS_AT_LEAST(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE_OR_NIL(1, function);
HELPER_CHECK_ARGS_TYPE(2, number);
HELPER_CHECK_OPTIONAL_ARGS_TYPE_OR_NIL(3, number);
HELPER_CHECK_OPTIONAL_ARGS_TYPE_OR_NIL(4, number);
HELPER_CHECK_OPTIONAL_ARGS_TYPE_OR_NIL(5, boolean);
//Check if the currentState is the game state.
Game* game = dynamic_cast<Game*>(currentState);
if (game == NULL) return 0;
//Create the delay execution object.
ScriptDelayExecution *obj = new ScriptDelayExecution(game->getScriptExecutor()->getDelayExecutionList());
obj->setActive();
obj->time = (int)lua_tonumber(state, 2);
obj->repeatCount = (args >= 3 && lua_isnumber(state, 3)) ? (int)lua_tonumber(state, 3) : 1;
obj->repeatInterval = (args >= 4 && lua_isnumber(state, 4)) ? (int)lua_tonumber(state, 4) : obj->time;
obj->enabled = ((args >= 5 && lua_isboolean(state, 5)) ? lua_toboolean(state, 5) : 1) != 0;
//Get arguments.
for (int i = 6; i <= args; i++) {
obj->arguments.push_back(luaL_ref(state, LUA_REGISTRYINDEX));
}
std::reverse(obj->arguments.begin(), obj->arguments.end());
//Get the function.
lua_settop(state, 1);
obj->func = luaL_ref(state, LUA_REGISTRYINDEX);
//Create the userdatum.
obj->createUserData(state, "delayExecution");
//We return one object, the userdatum.
return 1;
}
int cancel(lua_State* state){
HELPER_GET_AND_CHECK_ARGS(1);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
auto object = ScriptDelayExecution::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
//Delete the object.
delete object;
return 0;
}
int isEnabled(lua_State* state){
HELPER_GET_AND_CHECK_ARGS(1);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
auto object = ScriptDelayExecution::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
lua_pushboolean(state, object->enabled ? 1 : 0);
return 1;
}
int setEnabled(lua_State* state) {
HELPER_GET_AND_CHECK_ARGS(2);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, boolean);
auto object = ScriptDelayExecution::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
object->enabled = lua_toboolean(state, 2) != 0;
return 0;
}
int getTime(lua_State* state){
HELPER_GET_AND_CHECK_ARGS(1);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
auto object = ScriptDelayExecution::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
lua_pushinteger(state, object->time);
return 1;
}
int setTime(lua_State* state) {
HELPER_GET_AND_CHECK_ARGS(2);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, number);
auto object = ScriptDelayExecution::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
object->time = (int)lua_tonumber(state, 2);
return 0;
}
int getRepeatCount(lua_State* state){
HELPER_GET_AND_CHECK_ARGS(1);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
auto object = ScriptDelayExecution::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
lua_pushinteger(state, object->repeatCount);
return 1;
}
int setRepeatCount(lua_State* state) {
HELPER_GET_AND_CHECK_ARGS(2);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, number);
auto object = ScriptDelayExecution::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
object->repeatCount = (int)lua_tonumber(state, 2);
return 0;
}
int getRepeatInterval(lua_State* state){
HELPER_GET_AND_CHECK_ARGS(1);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
auto object = ScriptDelayExecution::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
lua_pushinteger(state, object->repeatInterval);
return 1;
}
int setRepeatInterval(lua_State* state) {
HELPER_GET_AND_CHECK_ARGS(2);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, number);
auto object = ScriptDelayExecution::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
//Set the repeat interval (should >=1).
int i = (int)lua_tonumber(state, 2);
if (i > 0) object->repeatInterval = i;
return 0;
}
int getFunc(lua_State* state){
HELPER_GET_AND_CHECK_ARGS(1);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
auto object = ScriptDelayExecution::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
if (object->func == LUA_REFNIL) return 0;
lua_rawgeti(state, LUA_REGISTRYINDEX, object->func);
return 1;
}
int setFunc(lua_State* state) {
HELPER_GET_AND_CHECK_ARGS(2);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE_OR_NIL(2, function);
auto object = ScriptDelayExecution::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
int oldFunc = object->func;
object->func = luaL_ref(state, LUA_REGISTRYINDEX);
if (oldFunc == LUA_REFNIL) return 0;
lua_rawgeti(state, LUA_REGISTRYINDEX, oldFunc);
luaL_unref(state, LUA_REGISTRYINDEX, oldFunc);
return 1;
}
int getArguments(lua_State* state){
HELPER_GET_AND_CHECK_ARGS(1);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
auto object = ScriptDelayExecution::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
for (int a : object->arguments) {
lua_rawgeti(state, LUA_REGISTRYINDEX, a);
}
return object->arguments.size();
}
int setArguments(lua_State* state) {
HELPER_GET_AND_CHECK_ARGS_AT_LEAST(1);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
auto object = ScriptDelayExecution::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
//Remove old arguments.
for (int a : object->arguments) {
luaL_unref(state, LUA_REGISTRYINDEX, a);
}
object->arguments.clear();
//Get arguments.
for (int i = 2; i <= args; i++) {
object->arguments.push_back(luaL_ref(state, LUA_REGISTRYINDEX));
}
std::reverse(object->arguments.begin(), object->arguments.end());
return 0;
}
int getExecutionTime(lua_State* state){
HELPER_GET_AND_CHECK_ARGS(1);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
auto object = ScriptDelayExecution::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
lua_pushinteger(state, object->executionTime);
return 1;
}
int setExecutionTime(lua_State* state) {
HELPER_GET_AND_CHECK_ARGS(2);
HELPER_CHECK_ARGS_TYPE_NO_HINT(1, userdata);
HELPER_CHECK_ARGS_TYPE(2, number);
auto object = ScriptDelayExecution::getObjectFromUserData(state, 1);
if (object == NULL) return 0;
object->executionTime = (int)lua_tonumber(state, 2);
return 0;
}
}
#define _L delayExecution
//Array with the methods for the block library.
static const luaL_Reg delayExecutionLib_m[] = {
_FI(Valid),
_F(schedule),
_F(cancel),
_FIS(Enabled),
_FGS(Time),
_FGS(RepeatCount),
_FGS(RepeatInterval),
_FGS(Func),
_FGS(Arguments),
_FGS(ExecutionTime),
{ NULL, NULL }
};
#undef _L
int luaopen_delayExecution(lua_State* state){
luaL_newlib(state, delayExecutionLib_m);
//Create the metatable for the delay execution userdata.
luaL_newmetatable(state, "delayExecution");
lua_pushstring(state, "__index");
lua_pushvalue(state, -2);
lua_settable(state, -3);
ScriptDelayExecution::registerMetatableFunctions(state, -3);
//Register the functions and methods.
luaL_setfuncs(state, delayExecutionLib_m, 0);
return 1;
}
/////////////////////////GETTEXT SPECIFIC///////////////////////////
namespace gettext {
int gettext(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(1);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE(1, string); //msgid
if (levels) {
auto dm = levels->getDictionaryManager();
if (dm) {
lua_pushstring(state, dm->get_dictionary().translate(lua_tostring(state, 1)).c_str());
return 1;
}
}
//If we failed to find dictionay manager, we just return the original string.
lua_pushvalue(state, 1);
return 1;
}
int pgettext(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(2);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE(1, string); //msgctxt
HELPER_CHECK_ARGS_TYPE(2, string); //msgid
if (levels) {
auto dm = levels->getDictionaryManager();
if (dm) {
lua_pushstring(state, dm->get_dictionary().translate_ctxt(lua_tostring(state, 1), lua_tostring(state, 2)).c_str());
return 1;
}
}
//If we failed to find dictionay manager, we just return the original string.
lua_pushvalue(state, 2);
return 1;
}
int ngettext(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(3);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE(1, string); //msgid
HELPER_CHECK_ARGS_TYPE(2, string); //msgid_plural
HELPER_CHECK_ARGS_TYPE(3, number);
if (levels) {
auto dm = levels->getDictionaryManager();
if (dm) {
lua_pushstring(state, dm->get_dictionary().translate_plural(
lua_tostring(state, 1),
lua_tostring(state, 2),
lua_tonumber(state, 3)
).c_str());
return 1;
}
}
//If we failed to find dictionay manager, we just return the original string.
if (lua_tonumber(state, 3) == 1) {
lua_pushvalue(state, 1);
} else {
lua_pushvalue(state, 2);
}
return 1;
}
int npgettext(lua_State* state){
//Check the number of arguments.
HELPER_GET_AND_CHECK_ARGS(4);
//Check if the arguments are of the right type.
HELPER_CHECK_ARGS_TYPE(1, string); //msgctxt
HELPER_CHECK_ARGS_TYPE(2, string); //msgid
HELPER_CHECK_ARGS_TYPE(3, string); //msgid_plural
HELPER_CHECK_ARGS_TYPE(4, number);
if (levels) {
auto dm = levels->getDictionaryManager();
if (dm) {
lua_pushstring(state, dm->get_dictionary().translate_ctxt_plural(
lua_tostring(state, 1),
lua_tostring(state, 2),
lua_tostring(state, 3),
lua_tonumber(state, 4)
).c_str());
return 1;
}
}
//If we failed to find dictionay manager, we just return the original string.
if (lua_tonumber(state, 4) == 1) {
lua_pushvalue(state, 2);
} else {
lua_pushvalue(state, 3);
}
return 1;
}
}
#define _L gettext
static const luaL_Reg gettextlib_m[] = {
_F(gettext),
_F(pgettext),
_F(ngettext),
_F(npgettext),
{ NULL, NULL }
};
#undef _L
int luaopen_gettext(lua_State* state){
//Register the global shortcut function _() and __().
luaL_loadstring(state,
"function _(s)\n"
" return gettext.gettext(s)\n"
"end\n"
"function __(s)\n"
" return s\n"
"end\n"
);
lua_pcall(state, 0, 0, 0);
luaL_newlib(state, gettextlib_m);
//Register the functions and methods.
luaL_setfuncs(state, gettextlib_m, 0);
return 1;
}
diff --git a/src/ScriptUserData.cpp b/src/ScriptUserData.cpp
index 79ccfbd..93b80e9 100644
--- a/src/ScriptUserData.cpp
+++ b/src/ScriptUserData.cpp
@@ -1,25 +1,30 @@
#include "ScriptUserData.h"
#include <stdio.h>
//Some debug functions
void scriptUserClassDebugCreate(char sig1,char sig2,char sig3,char sig4,const void* p1,const void* p2) {
#if defined(DISABLED_DEBUG_STUFF)
printf("ScriptUserClass '%c%c%c%c' (%p) created userdata: %p\n",
sig1,sig2,sig3,sig4,p1,p2);
#endif
}
void scriptUserClassDebugInvalidate(char sig1,char sig2,char sig3,char sig4,const void* p1,const void* p2) {
#if defined(DISABLED_DEBUG_STUFF)
printf("ScriptUserClass '%c%c%c%c' (%p) invalidated userdata: %p\n",
sig1,sig2,sig3,sig4,p1,p2);
#endif
}
void scriptUserClassDebugUnlink(char sig1,char sig2,char sig3,char sig4,const void* p1,const void* p2) {
#if defined(DISABLED_DEBUG_STUFF)
printf("ScriptUserClass '%c%c%c%c' (%p) unlinked userdata: %p\n",
sig1,sig2,sig3,sig4,p1,p2);
#endif
}
+void scriptProxyUserClassCreateUserDataFailed(char sig1, char sig2, char sig3, char sig4, const void* pThis, const void* pActive) {
+ fprintf(stderr, "ERROR: ScriptProxyUserClass '%c%c%c%c' (%p) refused to create userdata because the active object is %p which is not equal to this object!\n"
+ "Maybe (1) there is a bug in code or (2) the newly created object get deleted immediately by script\n",
+ sig1, sig2, sig3, sig4, pThis, pActive);
+}
diff --git a/src/ScriptUserData.h b/src/ScriptUserData.h
index 756d625..4265728 100644
--- a/src/ScriptUserData.h
+++ b/src/ScriptUserData.h
@@ -1,366 +1,371 @@
/*
* Copyright (C) 2012 Me and My Shadow
*
* This file is part of Me and My Shadow.
*
* Me and My Shadow is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Me and My Shadow is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Me and My Shadow. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SCRIPTUSERDATA_H
#define SCRIPTUSERDATA_H
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
#include <string>
#include <memory>
#include <assert.h>
//NOTE: Enable this you'll see a lot of annoying script debug messages which will lag the game.
//#define DISABLED_DEBUG_STUFF
#if defined(DISABLED_DEBUG_STUFF)
//Some debug functions
void scriptUserClassDebugCreate(char sig1,char sig2,char sig3,char sig4,const void* p1,const void* p2);
void scriptUserClassDebugInvalidate(char sig1,char sig2,char sig3,char sig4,const void* p1,const void* p2);
void scriptUserClassDebugUnlink(char sig1,char sig2,char sig3,char sig4,const void* p1,const void* p2);
#endif
+void scriptProxyUserClassCreateUserDataFailed(char sig1, char sig2, char sig3, char sig4, const void* pThis, const void* pActive);
//A struct represents the Lua user data.
struct ScriptUserData{
char sig1,sig2,sig3,sig4;
void* data;
ScriptUserData* prev;
ScriptUserData* next;
};
//A helper class to bind C++ class to Lua user data.
template<char sig1,char sig2,char sig3,char sig4,class T>
class ScriptUserClass{
public:
ScriptUserClass():scriptUserDataHead(NULL){
}
ScriptUserClass(const ScriptUserClass& other) = delete;
ScriptUserClass& operator=(const ScriptUserClass& other) = delete;
//Create a Lua user data pointed to this object. (-0,+1,e)
//state: Lua state.
//metatableName: Metatable name.
void createUserData(lua_State* state,const char* metatableName){
//Create user data.
ScriptUserData* ud=(ScriptUserData*)lua_newuserdata(state,sizeof(ScriptUserData));
//Add it to the linked list.
linkUserData(ud);
//Set matatable and we are done.
luaL_getmetatable(state,metatableName);
lua_setmetatable(state,-2);
}
//Destroys all Lua user data associated to this object.
void destroyUserData(){
while(scriptUserDataHead){
#if defined(DISABLED_DEBUG_STUFF)
scriptUserClassDebugInvalidate(sig1,sig2,sig3,sig4,this,scriptUserDataHead);
#endif
scriptUserDataHead->data=NULL;
scriptUserDataHead=scriptUserDataHead->next;
}
}
//Convert a Lua user data in Lua stack to object. (-0,+0,e)
//state: Lua state.
//idx: Index.
//Returns: The object. NULL if this user data is invalid.
//NOTE: This data should be a user data.
static T* getObjectFromUserData(lua_State* state,int idx){
ScriptUserData* ud=(ScriptUserData*)lua_touserdata(state,idx);
return getObjectFromUserData(ud);
}
//Convert a ScriptUserData to object.
static T* getObjectFromUserData(ScriptUserData* ud) {
if (ud && ud->sig1 == sig1 && ud->sig2 == sig2 && ud->sig3 == sig3 && ud->sig4 == sig4)
return reinterpret_cast<T*>(ud->data);
return NULL;
}
//Register __gc, __eq to given table. (-0,+0,e)
//state: Lua state.
//idx: Index.
static void registerMetatableFunctions(lua_State *state,int idx){
lua_pushstring(state,"__gc");
lua_pushcfunction(state,&garbageCollectorFunction);
lua_rawset(state,idx);
lua_pushstring(state,"__eq");
lua_pushcfunction(state,&checkEqualFunction);
lua_rawset(state,idx);
}
virtual ~ScriptUserClass(){
destroyUserData();
}
class ObservePointer;
friend class ObservePointer;
//Reinventing the wheel of std::weak_ptr.
class ObservePointer {
private:
ScriptUserData* ud;
public:
ObservePointer() : ud(NULL) {}
ObservePointer(const ObservePointer& other) : ud(NULL) {
(*this) = other;
}
ObservePointer(const T* obj) : ud(NULL) {
(*this) = obj;
}
~ObservePointer() {
ScriptUserClass::unlinkUserData(ud);
if (ud) delete ud;
}
ObservePointer& operator=(const T* obj) {
if (ScriptUserClass::getObjectFromUserData(ud) == obj) return *this;
//Unlink old one
ScriptUserClass::unlinkUserData(ud);
//Sanity check
assert(ud == NULL || ud->data == NULL);
//Link new one
if (obj) {
if (ud == NULL) ud = new ScriptUserData;
const_cast<T*>(obj)->linkUserData(ud);
} else if (ud) {
ud->data = NULL;
}
return *this;
}
ObservePointer& operator=(const ObservePointer& other) {
if (this == &other) return *this;
(*this) = const_cast<ObservePointer&>(other).get();
return *this;
}
T* get() const {
return ScriptUserClass::getObjectFromUserData(const_cast<ScriptUserData*>(ud));
}
void swap(ObservePointer& other) {
std::swap(ud, other.ud);
}
};
private:
ScriptUserData* scriptUserDataHead;
//Fill the user data and add it to the linked list.
void linkUserData(ScriptUserData* ud) {
//Convert this object to T.
//NOTE: we omit the runtime safety check, only leave the compile time check (by static_cast).
T* obj = static_cast<T*>(this);
ud->sig1 = sig1;
ud->sig2 = sig2;
ud->sig3 = sig3;
ud->sig4 = sig4;
ud->data = obj;
//Add it to the linked list.
ud->next = scriptUserDataHead;
ud->prev = NULL;
if (scriptUserDataHead) scriptUserDataHead->prev = ud;
scriptUserDataHead = ud;
#if defined(DISABLED_DEBUG_STUFF)
scriptUserClassDebugCreate(sig1, sig2, sig3, sig4, this, ud);
#endif
}
//Unlink the user data from the linked list.
static void unlinkUserData(ScriptUserData* ud) {
if (ud) {
if (ud->data) {
//It should be impossible unless there is a bug in code
assert(ud->sig1 == sig1 && ud->sig2 == sig2 && ud->sig3 == sig3 && ud->sig4 == sig4);
//Unlink it
if (ud->next) ud->next->prev = ud->prev;
if (ud->prev) ud->prev->next = ud->next;
else {
ScriptUserClass* owner = static_cast<ScriptUserClass*>(reinterpret_cast<T*>(ud->data));
owner->scriptUserDataHead = ud->next;
}
#if defined(DISABLED_DEBUG_STUFF)
scriptUserClassDebugUnlink(sig1, sig2, sig3, sig4,
static_cast<ScriptUserClass*>(reinterpret_cast<T*>(ud->data)), ud);
#endif
}
ud->data = NULL;
ud->next = NULL;
ud->prev = NULL;
}
}
//The garbage collector (__gc) function.
static int garbageCollectorFunction(lua_State* state){
//Check if it's a user data. It can be a table (the library itself)
if(!lua_isuserdata(state,1)) return 0;
ScriptUserData* ud=(ScriptUserData*)lua_touserdata(state,1);
unlinkUserData(ud);
return 0;
}
//The 'operator==' (__eq) function.
static int checkEqualFunction(lua_State* state){
//Check if it's a user data. It can be a table (the library itself)
if(!lua_isuserdata(state,1) || !lua_isuserdata(state,2)) return 0;
ScriptUserData* ud1=(ScriptUserData*)lua_touserdata(state,1);
ScriptUserData* ud2=(ScriptUserData*)lua_touserdata(state,2);
if(ud1!=NULL && ud2!=NULL){
//It should be impossible unless there is a bug in code
assert(ud1->sig1==sig1 && ud1->sig2==sig2 && ud1->sig3==sig3 && ud1->sig4==sig4);
assert(ud2->sig1==sig1 && ud2->sig2==sig2 && ud2->sig3==sig3 && ud2->sig4==sig4);
lua_pushboolean(state,ud1->data==ud2->data);
return 1;
}
return 0;
}
};
//Another helper class to bind C++ class to Lua user data.
//This allows dynamic changes of the pointer which pointing to the actual C++ class.
//Typical use case is a class which can dynamically create/delete during game running and has save/load feature.
template<char sig1, char sig2, char sig3, char sig4, class T>
class ScriptProxyUserClass {
public:
//The default constructor, which creates a new proxy object.
ScriptProxyUserClass() : proxy(new Proxy()) {
}
//The copy constructor, which reuses proxy object from existing one.
//NOTE: You must call this function in your copy constructor!!!
ScriptProxyUserClass(const ScriptProxyUserClass& other) : proxy(other.proxy) {
}
ScriptProxyUserClass& operator=(const ScriptProxyUserClass& other) = delete;
virtual ~ScriptProxyUserClass() {
if (proxy->object == static_cast<T*>(this)) {
proxy->object = NULL;
}
}
//Set current object as active object, i.e. accessible from Lua.
//Usually called when the object is created at the first time, or when the game is loaded.
void setActive() {
proxy->object = static_cast<T*>(this);
}
//Create a Lua user data pointed to this object. (-0,+1,e)
//state: Lua state.
//metatableName: Metatable name.
void createUserData(lua_State* state, const char* metatableName) {
- assert(proxy->object == static_cast<T*>(this));
- proxy->createUserData(state, metatableName);
+ if (proxy->object == static_cast<T*>(this)) {
+ proxy->createUserData(state, metatableName);
+ } else {
+ scriptProxyUserClassCreateUserDataFailed(sig1, sig2, sig3, sig4, static_cast<T*>(this), proxy->object);
+ lua_pushnil(state);
+ }
}
//Convert a Lua user data in Lua stack to object. (-0,+0,e)
//state: Lua state.
//idx: Index.
//Returns: The object. NULL if this user data is invalid.
//NOTE: This data should be a user data.
static T* getObjectFromUserData(lua_State* state, int idx) {
Proxy *p = Proxy::getObjectFromUserData(state, idx);
if (p == NULL) return NULL;
return p->object;
}
//Register __gc, __eq to given table. (-0,+0,e)
//state: Lua state.
//idx: Index.
static void registerMetatableFunctions(lua_State *state, int idx) {
Proxy::registerMetatableFunctions(state, idx);
}
class Proxy : public ScriptUserClass<sig1, sig2, sig3, sig4, Proxy> {
friend class ScriptProxyUserClass;
public:
Proxy() : object(NULL) {}
virtual ~Proxy() {}
T *get() {
return object;
}
public:
T *object;
};
std::shared_ptr<Proxy> proxy;
class ObservePointer;
friend class ObservePointer;
//Reinventing the wheel of std::weak_ptr.
class ObservePointer {
private:
typename Proxy::ObservePointer proxy;
public:
ObservePointer() {}
ObservePointer(const ObservePointer& other) : proxy(other.proxy) {}
ObservePointer(const T* obj) : proxy(obj ? obj->proxy.get() : NULL) {}
~ObservePointer() {}
ObservePointer& operator=(const T* obj) {
if (obj) {
proxy = obj->proxy.get();
} else {
proxy = NULL;
}
return *this;
}
ObservePointer& operator=(const ObservePointer& other) {
if (this == &other) return *this;
proxy = other.proxy;
return *this;
}
T* get() const {
Proxy *p = proxy.get();
if (p) return p->get();
return NULL;
}
void swap(ObservePointer& other) {
proxy.swap(other.proxy);
}
};
};
#endif

File Metadata

Mime Type
text/x-diff
Expires
Sat, May 16, 7:14 PM (1 d, 9 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
63196
Default Alt Text
(114 KB)

Event Timeline