summaryrefslogblamecommitdiff
path: root/src/scripting/lua.cpp
blob: e48a274fdd08f936ffcc8cdc65705e1f049f6d4a (plain) (tree)
1
2
3
4
5
6
7
8
  
                   
                                                            
                                           
  
                                         
  
                                                                           



                                                                        
                                                                      




                                                                     
                                                                            

   
 






                    





















                                           
                         
                            
 

                   



                                                                   
                                      

   
   
                                      
                                           
   
                                    



                                      

                                                
     
                                                                             


                                      
                                     

                                       
             


   
                                     
                                             
   
                                   


                                      
                 
     
                                                                            


                                     
                                     

                                                           













                                                         
                                                                                        





                              
         
                                                                                

                     
     



                                
   
                                      
                                                     


                                        







                                                                             
                                     



                                  

                                          
 


                                

                                




                                     
                                      










                                                                            
                                     
                                




             
                                                                          
                                                          
   
                                   
 




                                              


                                                             
                                  


                                  
                                                                


                 
                                



                                                                             



                                

                                

                                      




                                                                         

                                    
                                     
                                
             

 
   
                                   
                                
   
                                 










                                                                            
                                     





                                
                                             
                       
   
                                   




                          
                                    






                
                        
   
                                    











                             
                                                  
                                                      
   
                                 
 


                                

                                      
                                         
     
                                                                          















                                                                 
                                                                        

                 
 






                                                                         

          

                                          
                                             




                                       


             






                                                                               



                                                                              
                                                                     
                                                                    
   
                                       



                                      
                                                                                







                                                                       
                                                                                    

                     
 

                                             
                    



                                                                    
                        










                                      
                                                     

                    
                                                                                   

                         


                                    


                                                    
                                               








                                            
                                                         
   
                                      



                                      
                                                                               




                                     

                   

                                           










                                                                    



                    
   
                                                    
                                                         




                                                                          
   
                                  


                                      
                                         
     





































                                                                                                  
     


                    




                                
                                                                                             
                        

                                  
         
 





                                      
                                                                                             
                            

                                      



                                        

                                              

                      
















                              

 
   
                                                                          
                                                          


                                           
 



                                         




                                                                                    
                                       
             


   
                                  
                                                 


                                            


                                       




                                                                                     
                                  
             



                                                      
                                              


                                         


                                       




                                                                                  
                                                   


             

                                                   
                                                   


                                              


                                       




                                                                                   
                                                       




                                                
                                                             


                                              



                                         




                                                                                   
                                         
             

 
   
                                            
                                
   
                                   
 
                              





                                                                            

                 



                                         
 
   
                                                 
                                                        
                                  
   
                                   
 

                                      
 
                                  
                                       

                           

                                                                     
                                                                      

                                                                   




             
                                
                                  
   
                                  
 


                                                 





                                                                           
 
                                 

                                             


        






                                                                           


                                   
                                                              
   
                                     


                                  


                           





                                                                     



             
   






























                                                              
   

                                                  
   
                                                 
 



                                  

                                               
         

                                                          
         


















                                                                          

                                                                  
                     
         

                                                                              

     
































































                                                                 



                        
                             
   
                                       
 










                                                    
                                  
                               


                                         










                                               
                                  
                                       


                                         





                                  
                                            

     
             


   
                                     
                                  


                                            











                                                  
                                             


                                            

                                  
                                                              





                                 
             


   

                                                                   
                             
 








                                                                   
                             
 






                                            
                                                      
                                              
   
                                       
 


                                              
 


                                                             


                                  
                                                                    

                 
 
                                                               

              

                                                                                            



                                   
                 
                                
                                
 

                                
 

   
                                                










                                                                                       

                                                    
     
                                                                                       








                              

                                                                       
                                                       
   
                                      

                                      




                                                                               

                                           
     
                                                                       
















                                                             




                                               
                                   




















                                                                       
                                   





















                                                                       
                                     

















                                                                         
                                     















                                                                          

                                         
                                                
   
                                      

                                      


                                           
     
                                                                               

                 




                                                                               



                         
   

                                                                             
                                                          
   
                                       
 







                                                  







                                                                                
                                    
 


                                                                            










                                                                            
                                                            





                                       

                                                 

                                                        
   
                                    



                                                                          
                                                       
 
                                      



                                                   
                                                      











                                                                           


                                                    
                                           
   
                                             
 


                                      









                                                             
                                                                            




                                                                            
                                                                           
                                                            










                                                     
 
   

                                  
                                     






                                          




                                                                     






             


                                                     
                              
   
                                       


                                                         
                                                                                








                                                             
                                                                            


                 
                                               


             
 

                                                         
                                
                                 
   
                                      
 


                                                     
                                     








                                                                               
 

















                                                                              



             
 












                                                                                      


                                      
                                





                                                           
                                            
 





                              

                                                        
                                  
   
                                    







                                                                               

                                            









                                                             
                                           
   
                                     







                                                                                


                                                  
 
                                                   



             

                                                              
                                               









                                                                                    
                                          






                                                                                             
                                                





                                             
                                      









                                                                                    
                                          




                                                                  
                                               









                                                                                    
                                          






                                                                                             
                                                





                                            
                                      









                                                                                    
                                          

             

   











                                                                                    
                                       
 
                                            


             















                                                                                

   











                                                                                  
                                            
















                                                                                 
                                            
















                                                                                  
                                            








                                               
                                           
                              













                                                                                
                                                                   
                             
   
                                      
 

                                                      


             




                                                                              
                                      


                          

                                     


                                           
                                   





                                        


















                                              





                                                       
                                    






                                  
             

 


                                    
                                   







                                                             
 

                                     
                                     


                                  



                                            
 
                                               













                                                                     
                                 




             















                                                                              
                       

              
                             
                          
 







                                                                     
                                                               
                                         











                                                                                



                                                                                










































                                                                                
                                                                                





                                                                                

                      
                                             
                                                                   





                                                          




                                                                       
                                        
 
/*
 *  The Mana Server
 *  Copyright (C) 2007-2010  The Mana World Development Team
 *  Copyright (C) 2010  The Mana Developers
 *
 *  This file is part of The Mana Server.
 *
 *  The Mana Server 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 2 of the License, or
 *  any later version.
 *
 *  The Mana Server 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 The Mana Server.  If not, see <http://www.gnu.org/licenses/>.
 */


#include <cassert>

extern "C" {
#include <lualib.h>
#include <lauxlib.h>
}

#include "common/resourcemanager.h"
#include "game-server/accountconnection.h"
#include "game-server/buysell.h"
#include "game-server/character.h"
#include "game-server/collisiondetection.h"
#include "game-server/effect.h"
#include "game-server/gamehandler.h"
#include "game-server/inventory.h"
#include "game-server/item.h"
#include "game-server/itemmanager.h"
#include "game-server/mapcomposite.h"
#include "game-server/mapmanager.h"
#include "game-server/monster.h"
#include "game-server/monstermanager.h"
#include "game-server/npc.h"
#include "game-server/postman.h"
#include "game-server/quest.h"
#include "game-server/state.h"
#include "game-server/trigger.h"
#include "net/messageout.h"
#include "scripting/luautil.h"
#include "scripting/luascript.h"
#include "utils/logger.h"
#include "utils/speedconv.h"

#include <string.h>

/*
 * This file includes all script bindings available to LUA scripts.
 * When you add or change a script binding please document it on
 *
 * http://doc.manasource.org/scripting
 */

/**
 * Callback for sending a NPC_MESSAGE.
 * mana.npc_message(npc, character, string)
 */
static int npc_message(lua_State *s)
{
    NPC *p = getNPC(s, 1);
    Character *q = getCharacter(s, 2);
    size_t l;
    const char *m = luaL_checklstring(s, 3, &l);
    if (!p || !q)
    {
        raiseScriptError(s, "npc_message called with incorrect parameters.");
        return 0;
    }
    MessageOut msg(GPMSG_NPC_MESSAGE);
    msg.writeInt16(p->getPublicID());
    msg.writeString(std::string(m), l);
    gameHandler->sendTo(q, msg);
    return 0;
}

/**
 * Callback for sending a NPC_CHOICE.
 * mana.npc_choice(npc, character, string...)
 */
static int npc_choice(lua_State *s)
{
    NPC *p = getNPC(s, 1);
    Character *q = getCharacter(s, 2);
    if (!p || !q)
    {
        raiseScriptError(s, "npc_choice called with incorrect parameters.");
        return 0;
    }
    MessageOut msg(GPMSG_NPC_CHOICE);
    msg.writeInt16(p->getPublicID());
    for (int i = 3, i_end = lua_gettop(s); i <= i_end; ++i)
    {
        if (lua_isstring(s, i))
        {
            msg.writeString(lua_tostring(s, i));
        }
        else if (lua_istable(s, i))
        {
            lua_pushnil(s);
            while (lua_next(s, i) != 0) {
                if (lua_isstring(s, -1))
                {
                    msg.writeString(lua_tostring(s, -1));
                }
                else
                {
                    raiseScriptError(s, "npc_choice called with incorrect parameters.");
                    return 0;
                }
                lua_pop(s, 1);
            }
        }
        else
        {
            raiseScriptError(s, "npc_choice called with incorrect parameters.");
            return 0;
        }
    }
    gameHandler->sendTo(q, msg);
    return 0;
}

/**
 * Callback for sending a NPC_INTEGER.
 * mana.npc_integer(npc, character, min, max, defaut)
 */
static int npc_ask_integer(lua_State *s)
{
    NPC *p = getNPC(s, 1);
    Character *q = getCharacter(s, 2);
    if (!p || !q)
    {
        raiseScriptError(s, "npc_integer called with incorrect parameters.");
        return 0;
    }
    MessageOut msg(GPMSG_NPC_NUMBER);
    msg.writeInt16(p->getPublicID());

    int min = lua_tointeger(s, 3);
    int max = lua_tointeger(s, 4);
    int default_num = min;
    if (lua_gettop(s) == 5)
        default_num = lua_tointeger(s, 5);

    msg.writeInt32(min);
    msg.writeInt32(max);
    msg.writeInt32(default_num);
    gameHandler->sendTo(q, msg);

    return 0;
}

/**
 * Callback for sending a NPC_STRING.
 * mana.npc_ask_string(npc, character)
 */
static int npc_ask_string(lua_State *s)
{
    NPC *p = getNPC(s, 1);
    Character *q = getCharacter(s, 2);
    if (!p || !q)
    {
        raiseScriptError(s, "npc_string called with incorrect parameters.");
        return 0;
    }
    MessageOut msg(GPMSG_NPC_STRING);
    msg.writeInt16(p->getPublicID());
    gameHandler->sendTo(q, msg);

    return 0;
}

/**
 * Callback for creating a NPC on the current map with the current script.
 * mana.npc_create(string name, int id, int x, int y): npc
 */
static int npc_create(lua_State *s)
{
    const char *name = luaL_checkstring(s, 1);
    const int id = luaL_checkint(s, 2);
    const int x = luaL_checkint(s, 3);
    const int y = luaL_checkint(s, 4);

    lua_pushlightuserdata(s, (void *)&registryKey);
    lua_gettable(s, LUA_REGISTRYINDEX);
    Script *t = static_cast<Script *>(lua_touserdata(s, -1));
    NPC *q = new NPC(name, id, t);
    MapComposite *m = t->getMap();
    if (!m)
    {
        raiseScriptError(s, "npc_create called outside a map.");
        return 0;
    }
    q->setMap(m);
    q->setPosition(Point(x, y));
    bool b = GameState::insert(q);
    /* Do not try to deal with a failure there. There are some serious issues
       if an insertion failed on an almost empty map. */
    assert(b); (void)b;
    lua_pushlightuserdata(s, q);
    return 1;
}

static int npc_end(lua_State *s)
{
    NPC *p = getNPC(s, 1);
    Character *q = getCharacter(s, 2);
    if (!p || !q)
    {
        raiseScriptError(s, "npc_end called with incorrect parameters.");
        return 0;
    }

    MessageOut msg(GPMSG_NPC_CLOSE);
    msg.writeInt16(p->getPublicID());
    gameHandler->sendTo(q, msg);
    return 0;
}

/**
 * Callback for sending a NPC_POST.
 * mana.npc_post(npc, character)
 */
static int npc_post(lua_State *s)
{
    NPC *p = getNPC(s, 1);
    Character *q = getCharacter(s, 2);

    if (!p || !q)
    {
        raiseScriptError(s, "npc_Choice called with incorrect parameters.");
        return 0;
    }

    MessageOut msg(GPMSG_NPC_POST);
    msg.writeInt16(p->getPublicID());
    gameHandler->sendTo(q, msg);

    return 0;
}

/**
 * Enable a NPC if it has previously disabled
 * mana.npc_enable(npc)
 */
static int npc_enable(lua_State *s)
{
    NPC *p = getNPC(s, 1);
    if (p)
    {
        p->enable(true);
        GameState::enqueueInsert(p);
    }

    return 0;
}

/**
 * Disable a NPC
 * mana.npc_disable(npc)
 */
static int npc_disable(lua_State *s)
{
    NPC *p = getNPC(s, 1);
    if (p)
    {
        p->enable(false);
        GameState::remove(p);
    }

    return 0;
}

/**
 * Callback for warping a player to another place.
 * mana.chr_warp(character, nil/int map, int x, int y)
 */
static int chr_warp(lua_State *s)
{
    int x = luaL_checkint(s, 3);
    int y = luaL_checkint(s, 4);

    Character *q = getCharacter(s, 1);
    bool b = lua_isnil(s, 2);
    if (!q || !(b || lua_isnumber(s, 2)))
    {
        raiseScriptError(s, "chr_warp called with incorrect parameters.");
        return 0;
    }
    MapComposite *m;
    if (b)
    {
        lua_pushlightuserdata(s, (void *)&registryKey);
        lua_gettable(s, LUA_REGISTRYINDEX);
        Script *t = static_cast<Script *>(lua_touserdata(s, -1));
        m = t->getMap();
    }
    else
    {
        m = MapManager::getMap(lua_tointeger(s, 2));
    }
    if (!m)
    {
        raiseScriptError(s, "chr_warp called with a non-existing map.");
        return 0;
    }

    Map *map = m->getMap();

    // If the wanted warp place is unwalkable
    if (!map->getWalk(x / map->getTileWidth(), y / map->getTileHeight()))
    {
        int c = 50;
        LOG_INFO("chr_warp called with a non-walkable place.");
        do
        {
            x = rand() % map->getWidth();
            y = rand() % map->getHeight();
        } while (!map->getWalk(x, y) && --c);
        x *= map->getTileWidth();
        y *= map->getTileHeight();
    }
    GameState::enqueueWarp(q, m, x, y);

    return 0;
}

/**
 * Callback for inserting/removing items in inventory.
 * The function can be called several times in a row, but it is better to
 * perform all the changes at once, so as to reduce bandwidth. Removals
 * (negative amount) should be passed first, then insertions (positive amount).
 * If a removal fails, all the previous operations are canceled (except for
 * items dropped on the floor, hence why removals should be passed first), and
 * the function returns false. Otherwise the function will return true.
 * Note that previously when the item identifier was zero, money was modified;
 * however currency is now handled through attributes. This breaks backwards
 * compatibility with old scripts, and so logs a warning.
 * Note: If an insertion fails, extra items are dropped on the floor.
 * mana.chr_inv_change(character, (int id, int nb)...): bool success
 */
static int chr_inv_change(lua_State *s)
{
    Character *q = getCharacter(s, 1);
    if (!q)
    {
        raiseScriptError(s, "chr_inv_change called with incorrect parameters.");
        return 0;
    }
    int nb_items = (lua_gettop(s) - 1) / 2;
    Inventory inv(q, true);
    for (int i = 0; i < nb_items; ++i)
    {
        if (!lua_isnumber(s, i * 2 + 2) || !lua_isnumber(s, i * 2 + 3))
        {
            raiseScriptError(s, "chr_inv_change called with incorrect parameters.");
            return 0;
        }

        int id = lua_tointeger(s, i * 2 + 2);
        int nb = lua_tointeger(s, i * 2 + 3);
        if (id == 0)
        {
            LOG_WARN("mana.chr_inv_change() called with id 0! "
                     "Currency is now handled through attributes!");
        }
        else if (nb < 0)
        {
            nb = inv.remove(id, -nb);
            if (nb)
            {
                inv.cancel();
                lua_pushboolean(s, 0);
                return 1;
            }
        }
        else
        {
            ItemClass *ic = itemManager->getItem(id);
            if (!ic)
            {
                raiseScriptError(s, "chr_inv_change called with an unknown item.");
                continue;
            }
            nb = inv.insert(id, nb);
            if (nb)
            {
                Item *item = new Item(ic, nb);
                item->setMap(q->getMap());
                item->setPosition(q->getPosition());
                GameState::enqueueInsert(item);
            }
        }
    }
    lua_pushboolean(s, 1);
    return 1;
}

/**
 * Callback for counting items in inventory.
 * mana.chr_inv_count(character, int id...): int count...
 */
static int chr_inv_count(lua_State *s)
{
    Character *q = getCharacter(s, 1);
    if (!q)
    {
        raiseScriptError(s, "chr_inv_count called with incorrect parameters.");
        return 0;
    }
    int nb_items = lua_gettop(s) - 1;
    lua_checkstack(s, nb_items);
    Inventory inv(q);

    int id, nb = 0;
    for (int i = 2; i <= nb_items + 1; ++i)
    {
        id = luaL_checkint(s, i);
        if (id == 0)
        {
            LOG_WARN("mana.chr_inv_count() called with id 0! "
                     "Currency is now handled through attributes!");
        }
        else
        {
            nb = inv.count(id);
            lua_pushinteger(s, nb);
        }
    }
    return nb_items;
}

/**
 * Callback for trading between a player and an NPC.
 * mana.npc_trade(npc, character, bool sell, table items)
 * Let the player buy or sell only a subset of predeterminded items.
 * @param table items: a subset of buyable/sellable items.
 * When selling, if the 4 parameter is omitted or invalid,
 * everything in the player inventory can be sold.
 * @return 0 if something to buy/sell, 1 if no items, 2 in case of errors.
 */
static int npc_trade(lua_State *s)
{
    NPC *p = getNPC(s, 1);
    Character *q = getCharacter(s, 2);
    if (!p || !q || !lua_isboolean(s, 3))
    {
        raiseWarning(s, "npc_trade called with incorrect parameters.");
        lua_pushinteger(s, 2); // return value
        return 1; // Returns 1 parameter
    }

    bool sellMode = lua_toboolean(s, 3);
    BuySell *t = new BuySell(q, sellMode);
    if (!lua_istable(s, 4))
    {
        if (sellMode)
        {
            // Can sell everything
            if (!t->registerPlayerItems())
            {
                // No items to sell in player inventory
                t->cancel();
                lua_pushinteger(s, 1);
                return 1;
            }

            if (t->start(p))
            {
              lua_pushinteger(s, 0);
              return 1;
            }
            else
            {
              lua_pushinteger(s, 1);
              return 1;
            }
        }
        else
        {
            raiseWarning(s, "npc_trade[Buy] called with invalid or empty items table parameter.");
            t->cancel();
            lua_pushinteger(s, 2);
            return 1;
        }
    }

    int nbItems = 0;

    lua_pushnil(s);
    while (lua_next(s, 4))
    {
        if (!lua_istable(s, -1))
        {
            raiseWarning(s, "npc_trade called with invalid or empty items table parameter.");
            t->cancel();
            lua_pushinteger(s, 2);
            return 1;
        }

        int v[3];
        for (int i = 0; i < 3; ++i)
        {
            lua_rawgeti(s, -1, i + 1);
            if (!lua_isnumber(s, -1))
            {
                raiseWarning(s, "npc_trade called with incorrect parameters in item table.");
                t->cancel();
                lua_pushinteger(s, 2);
                return 1;
            }
            v[i] = lua_tointeger(s, -1);
            lua_pop(s, 1);
        }
        if (t->registerItem(v[0], v[1], v[2]))
            nbItems++;
        lua_pop(s, 1);
    }

    if (nbItems == 0)
    {
        t->cancel();
        lua_pushinteger(s, 1);
        return 1;
    }
    if (t->start(p))
    {
      lua_pushinteger(s, 0);
      return 1;
    }
    else
    {
      lua_pushinteger(s, 1);
      return 1;
    }
}

/**
 * Applies a status effect with id to the being given for a amount of time
 * mana.being_apply_status(Being *being, int id, int time)
 */

static int being_apply_status(lua_State *s)
{
    const int id = luaL_checkint(s, 2);
    const int time = luaL_checkint(s, 3);

    if (!lua_isuserdata(s, 1))
    {
        raiseScriptError(s, "being_apply_status called with incorrect parameters.");
        return 0;
    }
    Being *being = getBeing(s, 1);
    being->applyStatusEffect(id, time);
    return 0;
}

/**
 * Removes the given status effect
 * mana.being_remove_status(Being *being, int id)
 */
static int being_remove_status(lua_State *s)
{
    const int id = luaL_checkint(s, 2);

    if (!lua_isuserdata(s, 1))
    {
        raiseScriptError(s, "being_remove_status called with incorrect parameters.");
        return 0;
    }
    Being *being = getBeing(s, 1);
    being->removeStatusEffect(id);
    return 0;
}

/**
 * Returns true if a being has the given status effect
 * mana.being_has_status(Being *being, int id)
 */
static int being_has_status(lua_State *s)
{
    const int id = luaL_checkint(s, 2);

    if (!lua_isuserdata(s, 1))
    {
        raiseScriptError(s, "being_has_status called with incorrect parameters.");
        return 0;
    }
    Being *being = getBeing(s, 1);
    lua_pushboolean(s, being->hasStatusEffect(id));
    return 1;
}

/**
 * Returns the time left on the given status effect
 * mana.being_get_status_time(Being *being, int id)
 */
static int being_get_status_time(lua_State *s)
{
    const int id = luaL_checkint(s, 2);

    if (!lua_isuserdata(s, 1))
    {
        raiseScriptError(s, "being_time_status called with incorrect parameters.");
        return 0;
    }
    Being *being = getBeing(s, 1);
    lua_pushinteger(s, being->getStatusEffectTime(id));
    return 1;
}

/**
 * Sets the time left on the given status effect
 * mana.being_set_status_time(Being *being, int id, int time)
 */
static int being_set_status_time(lua_State *s)
{
    const int id = luaL_checkint(s, 2);
    const int time = luaL_checkint(s, 3);

    if (!lua_isuserdata(s, 1))
    {
        raiseScriptError(s, "being_time_status called with incorrect parameters.");
        return 0;
    }
    Being *being = getBeing(s, 1);
    being->setStatusEffectTime(id, time);
    return 0;
}

/**
 * Returns the Thing type of the given Being
 * mana.being_type(Being *being)
 */
static int being_type(lua_State *s)
{
    if (!lua_isuserdata(s, 1))
    {
        raiseScriptError(s, "being_type called with incorrect parameters.");
        return 0;
    }

    Being *being = getBeing(s, 1);
    if (!being)
        return 0;
    lua_pushinteger(s, being->getType());
    return 1;
}


/**
 * Function for making a being walk to a position
 * being_walk(Being *being, int x, int y[, float speed])
 * The speed is in tile per second
 */
static int being_walk(lua_State *s)
{
    const int x = luaL_checkint(s, 2);
    const int y = luaL_checkint(s, 3);

    Being *being = getBeing(s, 1);
    being->setDestination(Point(x, y));

    if (lua_isnumber(s, 4))
    {
        being->setAttribute(ATTR_MOVE_SPEED_TPS, lua_tonumber(s, 4));
        being->setAttribute(ATTR_MOVE_SPEED_RAW, utils::tpsToRawSpeed(
                being->getModifiedAttribute(ATTR_MOVE_SPEED_TPS)));
    }

    return 0;
}

/**
 * Makes the being say something
 * mana.being_say(source, message)
 */
static int being_say(lua_State *s)
{
    const char *message = luaL_checkstring(s, 2);

    if (!lua_isuserdata(s, 1))
    {
        raiseScriptError(s, "being_say called with incorrect parameters.");
        return 0;
    }

    Being *being = getBeing(s, 1);

    if (being && message[0] != 0)
    {
        GameState::sayAround(being, message);
    }
    else
    {
        raiseScriptError(s, "being_say called with incorrect parameters.");
        return 0;
    }

    return 0;
}


/**
 * Applies combat damage to a being
 * mana.being_damage(victim, value, delta, cth, type, element)
 */
static int being_damage(lua_State *s)
{
    Being *being = getBeing(s, 1);

    if (!being->canFight())
        return 0;

    Damage dmg((unsigned short)    lua_tointeger(s, 2), /* base */
               (unsigned short) lua_tointeger(s, 3),    /* delta */
               (unsigned short) lua_tointeger(s, 4),    /* cth */
               (unsigned char)  lua_tointeger(s, 6),    /* element */
               DAMAGE_PHYSICAL);                        /* type */
    being->damage(NULL, dmg);

    return 0;
}

/**
 * Restores hit points of a being
 * mana.being_heal(being[, value])
 * Without a value it heals fully.
 */
static int being_heal(lua_State *s)
{
    Being *being = getBeing(s, 1);
    if (!being)
    {
        raiseScriptError(s,
            "being_heal called for nonexistent being.");
        return 0;
    }

    if (lua_gettop(s) == 1) // when there is only one argument
    {
        being->heal();
    }
    else if (lua_isnumber(s, 2))
    {
        being->heal(lua_tointeger(s, 2));
    }
    else
    {
        raiseScriptError(s,
            "being_heal called with illegal healing value.");
    }

    return 0;
}

/**
 * Gets the base attribute of a being
 * mana.being_get_base_attribute(being, attribute)
 */
static int being_get_base_attribute(lua_State *s)
{
    Being *being = getBeing(s, 1);

    if (being)
    {
        lua_Integer attr = lua_tointeger(s, 2);
        if (attr)
        {
            lua_pushinteger(s, being->getAttribute(attr));
            return 1;
        }
        raiseScriptError(s,
            "being_get_base_attribute called with incorrect parameters.");
    }

    return 0;
}

/**
 * Gets the modified attribute of a being
 * mana.being_get_modified_attribute(being, attribute)
 */
static int being_get_modified_attribute(lua_State *s)
{
    Being *being = getBeing(s, 1);

    if (being)
    {
        lua_Integer attr = lua_tointeger(s, 2);
        if (attr)
        {
            lua_pushinteger(s, being->getModifiedAttribute(attr));
            return 1;
        }
        raiseScriptError(s,
            "being_get_modified_attribute called with incorrect parameters.");
    }

    return 0;
}

/**
 * Sets the base attribute of a being
 * mana.being_set_base_attribute(being, attribute, value)
 */
static int being_set_base_attribute(lua_State *s)
{
    Being *being = getBeing(s, 1);
    if (!being)
        return 0;

    lua_Integer attr = lua_tointeger(s, 2);
    lua_Number value = lua_tonumber(s, 3);

    being->setAttribute(attr, value);

    return 0;
}

/**
 * Applies an attribute modifier to a being.
 * mana.being_apply_attribute_modifier(
 *     attribute, value, layer, [duration, [effect-id]])
 */
static int being_apply_attribute_modifier(lua_State *s)
{
    Being *being = getBeing(s, 1);
    if (!being)
        return 0;

    lua_Integer attr  = lua_tointeger(s,2);
    lua_Number value  = lua_tonumber(s, 3);
    lua_Integer layer = lua_tonumber(s, 4);
    lua_Integer duration = 0;
    lua_Integer effectId = 0;
    if (lua_isnumber(s, 5))
    {
        duration = lua_tointeger(s, 5);
        if (lua_isnumber(s, 6))
            effectId = lua_tointeger(s, 6);
    }

    being->applyModifier(attr, value, layer, duration, effectId);

    return 0;
}

static int being_remove_attribute_modifier(lua_State *s)
{
    Being *being = getBeing(s, 1);
    if (!being)
        return 0;

    lua_Integer attr  = lua_tointeger(s,2);
    lua_Number value  = lua_tonumber(s, 3);
    lua_Integer layer = lua_tonumber(s, 4);
    lua_Integer effectId = 0;
    if (lua_isnumber(s, 5))
        effectId = lua_tointeger(s, 5);

    being->removeModifier(attr, value, layer, effectId);

    return 0;
}

/**
 * Gets the being's name
 * mana.being_get_name(being)
 */
static int being_get_name(lua_State *s)
{
    Being *being = getBeing(s, 1);

    if (being)
    {
        lua_pushstring(s, being->getName().c_str());
    }

    return 1;
}

/**
 * Gets the being's current action
 * mana.being_get_action(being)
 */
static int being_get_action(lua_State *s)
{
    Being *being = getBeing(s, 1);

    if (being)
    {
        lua_pushinteger(s, being->getAction());
    }

    return 1;
}

/**
 * Sets the being's current action
 * mana.being_set_action(being, action)
 */
static int being_set_action(lua_State *s)
{
    Being *being = getBeing(s, 1);

    int act = lua_tointeger(s, 2);

    if (being)
    {
        being->setAction((BeingAction) act);
    }

    return 0;
}

/**
 * Gets the being's current direction
 * mana.being_get_direction(being)
 */
static int being_get_direction(lua_State *s)
{
    Being *being = getBeing(s, 1);

    if (being)
    {
        lua_pushinteger(s, being->getDirection());
    }

    return 1;
}

/**
 * Sets the being's current direction
 * mana.being_set_direction(being, direction)
 */
static int being_set_direction(lua_State *s)
{
    Being *being = getBeing(s, 1);

    BeingDirection dir = (BeingDirection) lua_tointeger(s, 2);

    if (being)
    {
        being->setDirection(dir);
    }

    return 0;
}

/**
 * Function for getting the x-coordinate of the position of a being
 */
static int posX(lua_State *s)
{
    int x = getBeing(s, 1)->getPosition().x;
    lua_pushinteger(s, x);

    return 1;
}

/**
 * Function for getting the y-coordinate of the position of a being
 */
static int posY(lua_State *s)
{
    int y = getBeing(s, 1)->getPosition().y;
    lua_pushinteger(s, y);

    return 1;
}

/**
 * Callback for creating a monster on the current map.
 * mana.monster_create(int type, int x, int y)
 */
static int monster_create(lua_State *s)
{
    const int monsterId = luaL_checkint(s, 1);
    const int x = luaL_checkint(s, 2);
    const int y = luaL_checkint(s, 3);

    lua_pushlightuserdata(s, (void *)&registryKey);
    lua_gettable(s, LUA_REGISTRYINDEX);
    Script *t = static_cast<Script *>(lua_touserdata(s, -1));
    MapComposite *m = t->getMap();
    if (!m)
    {
        raiseScriptError(s, "monster_create called outside a map.");
        return 0;
    }

    MonsterClass *spec = monsterManager->getMonster(monsterId);
    if (!spec)
    {
        raiseScriptError(s, "monster_create called with invalid monster ID: %d", monsterId);
        //LOG_WARN("LuaMonster_Create invalid monster ID: " << monsterId);
        return 0;
    }

    Monster *q = new Monster(spec);
    q->setMap(m);
    q->setPosition(Point(x, y));
    GameState::enqueueInsert(q);

    lua_pushlightuserdata(s, q);
    return 1;
}

/**
 * mana.monster_load_script(mob, scriptfilename)
 * loads a LUA script given for mob
 */
static int monster_load_script(lua_State *s)
{
    Monster *m = static_cast< Monster* >(getBeing(s, 1));
    if (!m)
    {
         raiseScriptError(s, "monster_load_script called for a nonexistance monster.");
         return 0;
    }

    const char *scriptName = luaL_checkstring(s, 2);
    if (scriptName[0] == 0)
    {
        raiseScriptError(s, "monster_load_script called with empty script file name.");
        return 0;
    }

    m->loadScript(scriptName);
    return 0;
}


/**
 * Callback for getting a quest variable. Starts a recovery and returns
 * immediatly, if the variable is not known yet.
 * mana.chr_get_chest(character, string): nil or string
 */
static int chr_get_quest(lua_State *s)
{
    Character *q = getCharacter(s, 1);
    if (!q)
    {
        raiseScriptError(s, "chr_get_quest called for nonexistent character.");
    }

    const char *m = luaL_checkstring(s, 2);
    if (m[0] == 0)
    {
        raiseScriptError(s, "chr_get_quest called with empty string.");
        return 0;
    }
    std::string value, name = m;
    bool res = getQuestVar(q, name, value);
    if (res)
    {
        lua_pushstring(s, value.c_str());
        return 1;
    }
    lua_pushlightuserdata(s, (void *)&registryKey);
    lua_gettable(s, LUA_REGISTRYINDEX);
    Script *t = static_cast<Script *>(lua_touserdata(s, -1));
    QuestCallback f = { &LuaScript::getQuestCallback, t };
    recoverQuestVar(q, name, f);
    return 0;
}


/**
 * gets the value of a persistent map variable.
 * mana.getvar_map(string): string
 */
static int getvar_map(lua_State *s)
{
    const char *m = luaL_checkstring(s, 1);
    if (m[0] == 0)
    {
        raiseScriptError(s, "getvar_map called for unnamed variable.");
        return 0;
    }

    lua_pushlightuserdata(s, (void *)&registryKey);
    lua_gettable(s, LUA_REGISTRYINDEX);
    Script *script = static_cast<Script *>(lua_touserdata(s, -1));
    std::string value = script->getMap()->getVariable(m);

    lua_pushstring(s, value.c_str());
    return 1;
}

/**
 * sets the value of a persistent map variable.
 * mana.setvar_map(string, string)
 */
static int setvar_map(lua_State *s)
{
    const char *m = luaL_checkstring(s, 1);
    if (m[0] == 0)
    {
        raiseScriptError(s, "getvar_map called for unnamed variable.");
        return 0;
    }

    lua_pushlightuserdata(s, (void *)&registryKey);
    lua_gettable(s, LUA_REGISTRYINDEX);
    Script *script = static_cast<Script *>(lua_touserdata(s, -1));
    std::string key = lua_tostring(s, 1);
    std::string value = lua_tostring(s, 2);
    script->getMap()->setVariable(key, value);

    return 0;
}

/**
 * gets the value of a persistent global variable.
 * mana.getvar_world(string): string
 */
static int getvar_world(lua_State *s)
{
    const char *m = luaL_checkstring(s, 1);
    if (m[0] == 0)
    {
        raiseScriptError(s, "getvar_world called for unnamed variable.");
        return 0;
    }

    std::string value = GameState::getVariable(m);

    lua_pushstring(s, value.c_str());
    return 1;
}

/**
 * sets the value of a persistent global variable.
 * mana.setvar_world(string, string)
 */
static int setvar_world(lua_State *s)
{
    const char *m = luaL_checkstring(s, 1);
    if (m[0] == 0)
    {
        raiseScriptError(s, "setvar_world called with unnamed variable.");
        return 0;
    }

    std::string key = lua_tostring(s, 1);
    std::string value = lua_tostring(s, 2);
    GameState::setVariable(key, value);

    return 0;
}


/**
 * Callback for setting a quest variable.
 * mana.chr_set_chest(character, string, string)
 */
static int chr_set_quest(lua_State *s)
{
    Character *q = getCharacter(s, 1);
    const char *m = luaL_checkstring(s, 2);
    const char *n = luaL_checkstring(s, 3);
    if (m[0] == 0 || strlen(m) == 0)
    {
        raiseScriptError(s, "chr_set_quest called with incorrect parameters.");
        return 0;
    }
    if (!q)
    {
        raiseScriptError(s, "chr_set_quest called for nonexistent character.");
        return 0;
    }
    setQuestVar(q, m, n);
    return 0;
}

/**
 * Creates a trigger area. Whenever an actor enters this area, a Lua function
 * is called.
 * mana.trigger_create (x, y, width, height, function, id)
 */
static int trigger_create(lua_State *s)
{
    const int x = luaL_checkint(s, 1);
    const int y = luaL_checkint(s, 2);
    const int width = luaL_checkint(s, 3);
    const int height = luaL_checkint(s, 4);
    const char *function = luaL_checkstring(s, 5);
    const int id = luaL_checkint(s, 6);

    if (!lua_isboolean(s, 7))
    {
        raiseScriptError(s, "trigger_create called with incorrect parameters.");
        return 0;
    }

    lua_pushlightuserdata(s, (void *)&registryKey);
    lua_gettable(s, LUA_REGISTRYINDEX);
    Script *script = static_cast<Script *>(lua_touserdata(s, -1));
    bool once = lua_toboolean(s, 7);

    LOG_INFO("Created script trigger at " << x << ":" << y
             << " (" << width << "x" << height << ") function: " << function
             << " (" << id << ")");

    MapComposite *m = script->getMap();

    if (!m)
    {
        raiseScriptError(s, "trigger_create called for nonexistent a map.");
        return 0;
    }

    ScriptAction *action = new ScriptAction(script, function, id);
    Rectangle r = { x, y, width, height };
    TriggerArea *area = new TriggerArea(m, r, action, once);

    bool ret = GameState::insert(area);
    lua_pushboolean(s, ret);
    return 1;
}

/**
 * Creates a chat message in the users chatlog(s)
 * global message: mana.chatmessage (message)
 * private massage: mana.chatmessage (recipent, message)
 */
static int chatmessage(lua_State *s)
{
    if (lua_gettop(s) == 2 && lua_isuserdata(s, 1) && lua_isstring(s, 2) )
    {
        Being *being = getBeing(s, 1);
        const std::string message = lua_tostring(s, 2);

        if (being && !message.empty())
        {
            GameState::sayTo(being, NULL, message);
        }
    }
    else if (lua_gettop(s) == 1 && lua_isstring(s, 1))
    {
        // TODO: make chatserver send a global message
    }
    else
    {
        raiseScriptError(s, "being_say called with incorrect parameters.");
        return 0;
    }

    return 0;
}

/**
 * Gets a LUA table with the being IDs of all beings
 * inside of a circular area of the current map.
 * mana.get_beings_in_circle (x, y, radius)
 */
static int get_beings_in_circle(lua_State *s)
{
    const int x = luaL_checkint(s, 1);
    const int y = luaL_checkint(s, 2);
    const int r = luaL_checkint(s, 3);

    lua_pushlightuserdata(s, (void *)&registryKey);
    lua_gettable(s, LUA_REGISTRYINDEX);
    Script *t = static_cast<Script *>(lua_touserdata(s, -1));
    MapComposite *m = t->getMap();

    //create a lua table with the beings in the given area.
    lua_newtable(s);
    int tableStackPosition = lua_gettop(s);
    int tableIndex = 1;
    for (BeingIterator i(m->getAroundPointIterator(Point(x, y), r)); i; ++i)
    {
        char t = (*i)->getType();
        if (t == OBJECT_NPC || t == OBJECT_CHARACTER || t == OBJECT_MONSTER)
        {
            Being *b = static_cast<Being *> (*i);
            if (Collision::circleWithCircle(b->getPosition(), b->getSize(),
                                            Point(x, y), r))
            {
                lua_pushinteger(s, tableIndex);
                lua_pushlightuserdata (s, b);
                lua_settable (s, tableStackPosition);
                tableIndex++;
            }
        }
    }

    return 1;
}

/**
 * Gets the post for the character
 */
static int chr_get_post(lua_State *s)
{
    if (lua_isuserdata(s, 1))
    {
        Character *c = getCharacter(s, 1);

        if (c)
        {
            lua_pushlightuserdata(s, (void *)&registryKey);
            lua_gettable(s, LUA_REGISTRYINDEX);
            Script *t = static_cast<Script *>(lua_touserdata(s, -1));
            PostCallback f = { &LuaScript::getPostCallback, t };
            postMan->getPost(c, f);
        }
    }

    return 0;
}

/**
 * Makes the server call the lua functions deathEvent
 * and removeEvent when the being dies or is removed
 * from the map.
 * mana.being_register (being)
 */
static int being_register(lua_State *s)
{
    if (!lua_islightuserdata(s, 1) || lua_gettop(s) != 1)
    {
        raiseScriptError(s, "being_register called with incorrect parameters.");
        return 0;
    }

    lua_pushlightuserdata(s, (void *)&registryKey);
    lua_gettable(s, LUA_REGISTRYINDEX);
    Script *t = static_cast<Script *>(lua_touserdata(s, -1));
    Being *being = getBeing(s, 1);
    if (!being)
    {
        raiseScriptError(s, "being_register called for nonexistent being.");
        return 0;
    }

    being->addListener(t->getScriptListener());
    return 0;
}


/**
 * Triggers a special effect from the clients effects.xml
 * mana.effect_create (id, x, y)
 * mana.effect_create (id, being)
 */
static int effect_create(lua_State *s)
{
    const int id = luaL_checkint(s, 1);

    if (((!lua_isnumber(s, 2) || !lua_isnumber(s, 3))
         && (!lua_isuserdata(s, 2))))
    {
        raiseScriptError(s, "effect_create called with incorrect parameters.");
        return 0;
    }
    lua_pushlightuserdata(s, (void *)&registryKey);
    lua_gettable(s, LUA_REGISTRYINDEX);
    Script *t = static_cast<Script *>(lua_touserdata(s, -1));

    MapComposite *m = t->getMap();

    if (lua_isuserdata(s, 2))
    {
        // being mode
        Being *b = getBeing(s, 2);
        if (!b)
        {
            raiseScriptError(s, "effect_create called on non-existent being");
            return 0;
        }
        Effects::show(id, m, b);
    }
    else
    {
        // positional mode
        int x = lua_tointeger(s, 2);
        int y = lua_tointeger(s, 3);
        Effects::show(id, m, Point(x, y));
    }

    return 0;
}


/**
 *
 * mana.chr_shake_screen(
 */
static int chr_shake_screen(lua_State *s)
{
    Character *c = getCharacter(s, 1);
    if (!c)
    {
        raiseScriptError(s, "lua chr_shake_screen called for nonexistent character.");
        return 0;
    }

    const int x = luaL_checkint(s, 2);
    const int y = luaL_checkint(s, 3);

    MessageOut msg(GPMSG_SHAKE);
    msg.writeInt16(x);
    msg.writeInt16(y);

    if (lua_isnumber(s, 4))
        msg.writeInt16((int) (lua_tonumber(s, 4) * 10000));
    if (lua_isnumber(s, 5))
        msg.writeInt16(lua_tointeger(s, 5));

    c->getClient()->send(msg);

    return 0;
}


/**
 * Gets the exp total in a skill of a specific character
 * mana.chr_get_exp (being, skill)
 */
static int chr_get_exp(lua_State *s)
{
    Character *c = getCharacter(s, 1);
    if (!c)
    {
        raiseScriptError(s, "luaChr_GetExp called for nonexistent character.");
        return 0;
    }

    const int skill = luaL_checkint(s, 2);
    const int exp = c->getExperience(skill);

    lua_pushinteger(s, exp);
    return 1;
}


/**
 * Gives the character a certain amount of experience points
 * in a skill. Can also be used to reduce the exp amount when
 * desired.
 * mana.chr_give_exp (being, skill, amount)
 */
static int chr_give_exp(lua_State *s)
{
    Character *c = getCharacter(s, 1);
    if (!c)
    {
        raiseScriptError(s, "luaChr_GiveExp called for nonexistent character.");
        return 0;
    }

    const int skill = luaL_checkint(s, 2);
    const int exp = luaL_checkint(s, 3);
    const int optimalLevel = luaL_optint(s, 4, 0);

    c->receiveExperience(skill, exp, optimalLevel);

    return 0;
}

/**
 * Sets the given character's hair style to the given style id
 * mana.chr_set_hair_style (character, styleid)
 */
static int chr_set_hair_style(lua_State *s)
{
    Character *c = getCharacter(s, 1);
    if (!c)
    {
        raiseScriptError(s, "chr_set_hair_style called for nonexistent character.");
        return 0;
    }

    const int style = luaL_checkint(s, 2);
    if (style < 0)
    {
        raiseScriptError(s, "chr_set_hair_style called for nonexistent style id %d.", style);
        return 0;
    }

    c->setHairStyle(style);
    c->raiseUpdateFlags(UPDATEFLAG_LOOKSCHANGE);

    return 0;
}

/**
 * Gets the hair style of the given character
 * mana.chr_get_hair_style (character)
 */
static int chr_get_hair_style(lua_State *s)
{
    Character *c = getCharacter(s, 1);
    if (!c)
    {
        raiseScriptError(s, "chr_get_hair_style called for nonexistent character.");
        return 0;
    }

    lua_pushinteger(s, c->getHairStyle());
    return 1;
}

/**
 * Set the hair color of the given character to the given color id
 * mana.chr_set_hair_color (character, colorid)
 */
static int chr_set_hair_color(lua_State *s)
{
    Character *c = getCharacter(s, 1);
    if (!c)
    {
        raiseScriptError(s, "chr_set_hair_color called for nonexistent character.");
        return 0;
    }

    const int color = luaL_checkint(s, 2);
    if (color < 0)
    {
        raiseScriptError(s, "chr_set_hair_color called for nonexistent style id %d.", color);
        return 0;
    }

    c->setHairColor(color);
    c->raiseUpdateFlags(UPDATEFLAG_LOOKSCHANGE);

    return 0;
}

/**
 * Get the hair color of the given character
 * mana.chr_get_hair_color (character)
 */
static int chr_get_hair_color(lua_State *s)
{
    Character *c = getCharacter(s, 1);
    if (!c)
    {
        raiseScriptError(s, "chr_get_hair_color called for nonexistent character.");
        return 0;
    }

    lua_pushinteger(s, c->getHairColor());
    return 1;
}

/**
 * Get the number of monsters the player killed of a type
 * mana.chr_get_kill_count (character, monsterType)
 */
static int chr_get_kill_count(lua_State *s)
{
    Character *c = getCharacter(s, 1);
    if (!c)
    {
        raiseScriptError(s, "chr_get_kill_count called for nonexistent character.");
        return 0;
    }

    const int id = luaL_checkint(s, 2);

    lua_pushinteger(s, c->getKillCount(id));
    return 1;
}

/**
 * Get the gender of the character
 * mana.chr_get_gender (character)
 */
static int chr_get_gender(lua_State *s)
{
    Character *c = getCharacter(s, 1);
    if (!c)
    {
        raiseScriptError(s, "chr_get_gender called for nonexistent character.");
        return 0;
    }

    lua_pushinteger(s, c->getGender());
    return 1;
}

/**
 * Enables a special for a character
 * mana.chr_give_special (character, special)
 */
static int chr_give_special(lua_State *s)
{
    // cost_type is ignored until we have more than one cost type
    Character *c = getCharacter(s, 1);
    if (!c)
    {
        raiseScriptError(s, "chr_give_special called for nonexistent character.");
        return 0;
    }
    const int special = luaL_checkint(s, 2);

    c->giveSpecial(special);
    return 0;
}

/**
 * Checks if a character has a special and returns true or false
 * mana.chr_has_special (character, special)
 */
static int chr_has_special(lua_State *s)
{
    Character *c = getCharacter(s, 1);
    if (!c)
    {
        raiseScriptError(s, "chr_has_special called for nonexistent character.");
        return 0;
    }
    const int special = luaL_checkint(s, 2);

    lua_pushboolean(s, c->hasSpecial(special));
    return 1;
}

/**
 * Removes a special from a character
 * mana.chr_take_special (character, special)
 */
static int chr_take_special(lua_State *s)
{
    Character *c = getCharacter(s, 1);
    if (!c)
    {
        raiseScriptError(s, "chr_take_special called for nonexistent character.");
        return 0;
    }
    const int special = luaL_checkint(s, 2);

    lua_pushboolean(s, c->hasSpecial(special));
    c->takeSpecial(special);
    return 1;
}



/**
 * Returns the rights level of a character.
 * mana.chr_get_rights (being)
 */
static int chr_get_rights(lua_State *s)
{
    Character *c = getCharacter(s, 1);
    if (!c)
    {
        raiseScriptError(s, "chr_get_rights called for nonexistent character.");
        return 0;
    }
    lua_pushinteger(s, c->getAccountLevel());
    return 1;
}

/**
 * Returns the exp total necessary to reach a specific skill level.
 * mana.exp_for_level (level)
 */
static int exp_for_level(lua_State *s)
{
    const int level = luaL_checkint(s, 1);
    lua_pushinteger(s, Character::expForLevel(level));
    return 1;
}

/**
 * Returns four useless tables for testing the STL container push wrappers.
 * This function can be removed when there are more useful functions which use
 * them.
 */
static int test_tableget(lua_State *s)
{

    std::list<float> list;
    std::vector<std::string> svector;
    std::vector<int> ivector;
    std::map<std::string, std::string> map;
    std::set<int> set;

    LOG_INFO("Pushing Float List");
    list.push_back(12.636);
    list.push_back(0.0000000045656);
    list.push_back(185645445634566.346);
    list.push_back(7835458.11);
    pushSTLContainer<float>(s, list);

    LOG_INFO("Pushing String Vector");
    svector.push_back("All");
    svector.push_back("your");
    svector.push_back("base");
    svector.push_back("are");
    svector.push_back("belong");
    svector.push_back("to");
    svector.push_back("us!");
    pushSTLContainer<std::string>(s, svector);

    LOG_INFO("Pushing Integer Vector");
    ivector.resize(10);
    for (int i = 1; i < 10; i++)
    {
        ivector[i-1] = i * i;
    }
    pushSTLContainer<int>(s, ivector);

    LOG_INFO("Pushing String/String Map");
    map["Apple"] = "red";
    map["Banana"] = "yellow";
    map["Lime"] = "green";
    map["Plum"] = "blue";
    pushSTLContainer<std::string, std::string>(s, map);

    LOG_INFO("Pushing Integer Set");
    set.insert(12);
    set.insert(8);
    set.insert(14);
    set.insert(10);
    pushSTLContainer<int>(s, set);


    return 5;
}

/**
 * Returns the ID of the current map
 */
static int get_map_id(lua_State *s)
{
    lua_pushlightuserdata(s, (void *)&registryKey);
    lua_gettable(s, LUA_REGISTRYINDEX);
    Script *t = static_cast<Script *>(lua_touserdata(s, -1));
    int id = t->getMap()->getID();
    lua_pushinteger(s, id);
    return 1;
}

/**
 * Creates an item stack on the floor
 * mana.drop_item(x, y, id[, number])
 */
static int item_drop(lua_State *s)
{
    const int x = luaL_checkint(s, 1);
    const int y = luaL_checkint(s, 2);
    const int type = luaL_checkint(s, 3);
    const int number = luaL_optint(s, 4, 1);

    ItemClass *ic = itemManager->getItem(type);
    if (!ic)
    {
        raiseScriptError(s, "item_drop called with unknown item ID");
    }
    Item *i = new Item(ic, number);

    lua_pushlightuserdata(s, (void *)&registryKey);
    lua_gettable(s, LUA_REGISTRYINDEX);
    Script *t = static_cast<Script *>(lua_touserdata(s, -1));
    MapComposite* map = t->getMap();

    i->setMap(map);
    Point pos(x, y);
    i->setPosition(pos);
    GameState::insertOrDelete(i);

    return 0;
}


static int require_loader(lua_State *s)
{
    // Add .lua extension (maybe only do this when it doesn't have it already)
    std::string filename = luaL_checkstring(s, 1);
    filename.append(".lua");

    const std::string path = ResourceManager::resolve(filename);
    if (!path.empty())
        luaL_loadfile(s, path.c_str());
    else
        lua_pushstring(s, "File not found");

    return 1;
}


LuaScript::LuaScript():
    nbArgs(-1)
{
    mState = luaL_newstate();
    luaL_openlibs(mState);

    // Register package loader that goes through the resource manager
    // table.insert(package.loaders, 2, require_loader)
    lua_getglobal(mState, "package");
    lua_getfield(mState, -1, "loaders");
    lua_pushcfunction(mState, require_loader);
    lua_rawseti(mState, -2, 2);
    lua_pop(mState, 2);

    // Put the callback functions in the scripting environment.
    static luaL_Reg const callbacks[] = {
        { "npc_create",                      &npc_create                      },
        { "npc_message",                     &npc_message                     },
        { "npc_choice",                      &npc_choice                      },
        { "npc_trade",                       &npc_trade                       },
        { "npc_post",                        &npc_post                        },
        { "npc_enable",                      &npc_enable                      },
        { "npc_disable",                     &npc_disable                     },
        { "chr_warp",                        &chr_warp                        },
        { "chr_inv_change",                  &chr_inv_change                  },
        { "chr_inv_count",                   &chr_inv_count                   },
        { "chr_get_quest",                   &chr_get_quest                   },
        { "chr_set_quest",                   &chr_set_quest                   },
        { "getvar_map",                      &getvar_map                      },
        { "setvar_map",                      &setvar_map                      },
        { "getvar_world",                    &getvar_world                    },
        { "setvar_world",                    &setvar_world                    },
        { "chr_get_post",                    &chr_get_post                    },
        { "chr_get_exp",                     &chr_get_exp                     },
        { "chr_give_exp",                    &chr_give_exp                    },
        { "chr_get_rights",                  &chr_get_rights                  },
        { "chr_set_hair_style",              &chr_set_hair_style              },
        { "chr_get_hair_style",              &chr_get_hair_style              },
        { "chr_set_hair_color",              &chr_set_hair_color              },
        { "chr_get_hair_color",              &chr_get_hair_color              },
        { "chr_get_kill_count",              &chr_get_kill_count              },
        { "chr_get_gender",                  &chr_get_gender                  },
        { "chr_give_special",                &chr_give_special                },
        { "chr_has_special",                 &chr_has_special                 },
        { "chr_take_special",                &chr_take_special                },
        { "exp_for_level",                   &exp_for_level                   },
        { "monster_create",                  &monster_create                  },
        { "monster_load_script",             &monster_load_script             },
        { "being_apply_status",              &being_apply_status              },
        { "being_remove_status",             &being_remove_status             },
        { "being_has_status",                &being_has_status                },
        { "being_set_status_time",           &being_set_status_time           },
        { "being_get_status_time",           &being_get_status_time           },
        { "being_type",                      &being_type                      },
        { "being_walk",                      &being_walk                      },
        { "being_say",                       &being_say                       },
        { "being_damage",                    &being_damage                    },
        { "being_heal",                      &being_heal                      },
        { "being_get_name",                  &being_get_name                  },
        { "being_get_action",                &being_get_action                },
        { "being_set_action",                &being_set_action                },
        { "being_get_direction",             &being_get_direction             },
        { "being_set_direction",             &being_set_direction             },
        { "being_apply_attribute_modifier",  &being_apply_attribute_modifier  },
        { "being_remove_attribute_modifier", &being_remove_attribute_modifier },
        { "being_set_base_attribute",        &being_set_base_attribute        },
        { "being_get_modified_attribute",    &being_get_modified_attribute    },
        { "being_get_base_attribute",        &being_get_base_attribute        },
        { "posX",                            &posX                            },
        { "posY",                            &posY                            },
        { "trigger_create",                  &trigger_create                  },
        { "chatmessage",                     &chatmessage                     },
        { "get_beings_in_circle",            &get_beings_in_circle            },
        { "being_register",                  &being_register                  },
        { "effect_create",                   &effect_create                   },
        { "chr_shake_screen",                &chr_shake_screen                },
        { "test_tableget",                   &test_tableget                   },
        { "get_map_id",                      &get_map_id                      },
        { "item_drop",                       &item_drop                       },
        { "npc_ask_integer",                 &npc_ask_integer                 },
        { "npc_end",                         &npc_end                         },
        { "npc_ask_string",                  &npc_ask_string                  },
        { NULL, NULL }
    };
    luaL_register(mState, "mana", callbacks);
    lua_pop(mState, 1);                     // pop the 'mana' table

    // Make script object available to callback functions.
    lua_pushlightuserdata(mState, (void *)&registryKey);
    lua_pushlightuserdata(mState, this);
    lua_settable(mState, LUA_REGISTRYINDEX);

    // Push the error handler to first index of the stack
    lua_getglobal(mState, "debug");
    lua_getfield(mState, -1, "traceback");
    lua_remove(mState, 1);                  // remove the 'debug' table

    loadFile("scripts/lua/libmana.lua");
}