/****************************************************************************
 * [S]imulated [M]edieval [A]dventure multi[U]ser [G]ame      |   \\._.//   *
 * -----------------------------------------------------------|   (0...0)   *
 * SMAUG 1.4 (C) 1994, 1995, 1996, 1998  by Derek Snider      |    ).:.(    *
 * -----------------------------------------------------------|    {o o}    *
 * SMAUG code team: Thoric, Altrag, Blodkai, Narn, Haus,      |   / ' ' \   *
 * Scryn, Rennard, Swordbearer, Gorog, Grishnakh, Nivek,      |~'~.VxvxV.~'~*
 * Tricops and Fireblade                                      |             *
 * ------------------------------------------------------------------------ *
 * Merc 2.1 Diku Mud improvments copyright (C) 1992, 1993 by Michael        *
 * Chastain, Michael Quan, and Mitchell Tse.                                *
 * Original Diku Mud copyright (C) 1990, 1991 by Sebastian Hammer,          *
 * Michael Seifert, Hans Henrik St{rfeldt, Tom Madsen, and Katja Nyboe.     *
 * ------------------------------------------------------------------------ *
 *			 Lua Scripting Module     by Nick Gammon                  			    *
 ****************************************************************************/
 
/*

Lua scripting written by Nick Gammon
Date: 8th July 2007

You are welcome to incorporate this code into your MUD codebases.

Post queries at: http://www.gammon.com.au/forum/

*/

#include <stdio.h>
#include <string.h>
#include "mud.h"

#include <lualib.h>
#include <lauxlib.h>

#define CHARACTER_STATE "character.state"
#define MUD_LIBRARY "mud"

// number of items in an array
#define NUMITEMS(arg) (sizeof (arg) / sizeof (arg [0]))

/* void RegisterLuaCommands (lua_State *L); */ /* Implemented in lua_commands.c */
LUALIB_API int luaopen_bits(lua_State *L);  /* Implemented in lua_bits.c */

static int optboolean (lua_State *L, const int narg, const int def) 
  {
  /* that argument not present, take default  */
  if (lua_gettop (L) < narg)
    return def;

  /* nil will default to the default  */
  if (lua_isnil (L, narg))
    return def;

  if (lua_isboolean (L, narg))
    return lua_toboolean (L, narg);

  return luaL_checknumber (L, narg) != 0;
}

static int check_vnum (lua_State *L)
  {

  int vnum = luaL_checknumber (L, 1);
  if ( vnum < 1 || vnum > MAX_VNUM )
    luaL_error (L, "Vnum %d is out of range 1 to %d", vnum, MAX_VNUM);

  return vnum;
  }  /* end of check_vnum */


/* Given a Lua state, return the character it belongs to */

CHAR_DATA * L_getchar (lua_State *L)
 {
 /* retrieve our character */
 
  CHAR_DATA * ch;
  
  /* retrieve the character */
  lua_pushstring(L, CHARACTER_STATE);  /* push address */
  lua_gettable(L, LUA_ENVIRONINDEX);  /* retrieve value */

  ch = (CHAR_DATA *) lua_touserdata(L, -1);  /* convert to data */
  lua_pop(L, 1);  /* pop result */

  return ch;
} /* end of L_getchar */
  

static CHAR_DATA * L_find_character (lua_State *L)
{
  CHAR_DATA * ch = L_getchar (L);
  const char * name;
  CHAR_DATA *wch = NULL;
  
  /* given a number, assume a mob vnum */
  if (lua_type (L, 1) == LUA_TNUMBER)
    {
    int vnum = luaL_checknumber (L, 1);
      
   /*
    * check the room for an exact match 
    */
    for( wch = ch->in_room->first_person; wch; wch = wch->next_in_room )
     {
     if ( IS_NPC( wch ) && wch->pIndexData->vnum == vnum )
           return wch;
     }  /* end of for loop */

   /* if 2nd argument is true, try the world */
        
   if (lua_type (L, 2) == LUA_TBOOLEAN && lua_toboolean (L, 2))
     {
     for( wch = first_char; wch; wch = wch->next ) 
       {
       if ( IS_NPC( wch ) && wch->pIndexData->vnum == vnum )
             return wch;
       }  /* end of for loop */
     }
     
    return NULL;  /* can't find it */   
    }  /* end of numeric mob wanted */
  
  name = luaL_optstring (L, 1, "self");
  
  if (strcasecmp (name, "self") == 0)
    return ch;
  
  /*
  * check the room for an exact match 
  */
  for( wch = ch->in_room->first_person; wch; wch = wch->next_in_room )
    if( can_see( ch, wch ) && 
       !IS_NPC( wch ) && 
       ( strcasecmp ( name, wch->name ) == 0 ))
      break;  /* found it! */
  
  /*
  * check the world for an exact match 
  */
  if (!wch)
   {
   for( wch = first_char; wch; wch = wch->next )
      if( can_see( ch, wch ) && 
          !IS_NPC( wch ) &&
          ( strcasecmp( name, wch->name ) == 0 ) )
        break;        
    }  /* end of checking entire world */   
  
  return wch;  /* use target character, not self */
   
}  /* end of L_find_character */

/* For debugging, show traceback information */

static void GetTracebackFunction (lua_State *L)
  {
  lua_pushliteral (L, LUA_DBLIBNAME);     /* "debug"   */
  lua_rawget      (L, LUA_GLOBALSINDEX);    /* get debug library   */

  if (!lua_istable (L, -1))
    {
    lua_pop (L, 2);   /* pop result and debug table  */
    lua_pushnil (L);
    return;
    }

  /* get debug.traceback  */
  lua_pushstring(L, "traceback");  
  lua_rawget    (L, -2);               /* get traceback function  */
  
  if (!lua_isfunction (L, -1))
    {
    lua_pop (L, 2);   /* pop result and debug table  */
    lua_pushnil (L);
    return;
    }

  lua_remove (L, -2);   /* remove debug table, leave traceback function  */
  }  /* end of GetTracebackFunction */

static int CallLuaWithTraceBack (lua_State *L, const int iArguments, const int iReturn)
  {

  int error;
  int base = lua_gettop (L) - iArguments;  /* function index */
  GetTracebackFunction (L);
  if (lua_isnil (L, -1))
    {
    lua_pop (L, 1);   /* pop non-existent function  */
    error = lua_pcall (L, iArguments, iReturn, 0);
    }  
  else
    {
    lua_insert (L, base);  /* put it under chunk and args */
    error = lua_pcall (L, iArguments, iReturn, base);
    lua_remove (L, base);  /* remove traceback function */
    }

  return error;
  }  /* end of CallLuaWithTraceBack  */

  
/* let scripters find our directories and file names */

#define INFO_STR_ITEM(arg) \
  lua_pushstring (L, arg);  \
  lua_setfield (L, -2, #arg)
  
#define INFO_NUM_ITEM(arg) \
  lua_pushnumber (L, arg);  \
  lua_setfield (L, -2, #arg)
    
static int L_system_info (lua_State *L)
{
  lua_newtable(L);  /* table for the info */
  
  /* directories */
  
  INFO_STR_ITEM (PLAYER_DIR);
  INFO_STR_ITEM (BACKUP_DIR);
  INFO_STR_ITEM (GOD_DIR);
  INFO_STR_ITEM (BOARD_DIR);
  INFO_STR_ITEM (CLAN_DIR);
  INFO_STR_ITEM (COUNCIL_DIR);
  INFO_STR_ITEM (DEITY_DIR);
  INFO_STR_ITEM (BUILD_DIR);
  INFO_STR_ITEM (SYSTEM_DIR);
  INFO_STR_ITEM (PROG_DIR);
  INFO_STR_ITEM (CORPSE_DIR);
  INFO_STR_ITEM (CLASS_DIR);
  INFO_STR_ITEM (RACE_DIR);
  INFO_STR_ITEM (WATCH_DIR);
  INFO_STR_ITEM (LUA_DIR);
  INFO_STR_ITEM (AREA_LIST);
  INFO_STR_ITEM (WATCH_LIST);
  INFO_STR_ITEM (BAN_LIST);
  INFO_STR_ITEM (RESERVED_LIST);
  INFO_STR_ITEM (CLAN_LIST);
  INFO_STR_ITEM (COUNCIL_LIST);
  INFO_STR_ITEM (GUILD_LIST);
  INFO_STR_ITEM (GOD_LIST);
  INFO_STR_ITEM (DEITY_LIST);
  INFO_STR_ITEM (CLASS_LIST);
  INFO_STR_ITEM (RACE_LIST);
  INFO_STR_ITEM (MORPH_FILE);
  INFO_STR_ITEM (BOARD_FILE);
  INFO_STR_ITEM (SHUTDOWN_FILE);
  INFO_STR_ITEM (IMM_HOST_FILE);
  INFO_STR_ITEM (RIPSCREEN_FILE);
  INFO_STR_ITEM (RIPTITLE_FILE);
  INFO_STR_ITEM (ANSITITLE_FILE);
  INFO_STR_ITEM (ASCTITLE_FILE);
  INFO_STR_ITEM (BOOTLOG_FILE);
  INFO_STR_ITEM (PBUG_FILE);
  INFO_STR_ITEM (IDEA_FILE);
  INFO_STR_ITEM (TYPO_FILE);
  INFO_STR_ITEM (FIXED_FILE);
  INFO_STR_ITEM (LOG_FILE);
  INFO_STR_ITEM (MOBLOG_FILE);
  INFO_STR_ITEM (WIZLIST_FILE);
  INFO_STR_ITEM (WHO_FILE);
  INFO_STR_ITEM (WEBWHO_FILE);
  INFO_STR_ITEM (REQUEST_PIPE);
  INFO_STR_ITEM (SKILL_FILE);
  INFO_STR_ITEM (HERB_FILE);
  INFO_STR_ITEM (TONGUE_FILE);
  INFO_STR_ITEM (SOCIAL_FILE);
  INFO_STR_ITEM (COMMAND_FILE);
  INFO_STR_ITEM (PROJECTS_FILE);
  INFO_STR_ITEM (PLANE_FILE);
  INFO_STR_ITEM (LUA_STARTUP);
  
  /* other stuff */
  
  INFO_STR_ITEM (SMAUG_VERSION_MAJOR);
  INFO_STR_ITEM (SMAUG_VERSION_MINOR);

  /* levels */
  INFO_NUM_ITEM (MAX_LEVEL);
  INFO_NUM_ITEM (MAX_CLAN);
  INFO_NUM_ITEM (MAX_DEITY);
  INFO_NUM_ITEM (MAX_CPD);
  INFO_NUM_ITEM (MAX_HERB);
  INFO_NUM_ITEM (MAX_DISEASE);
  INFO_NUM_ITEM (MAX_PERSONAL);
  INFO_NUM_ITEM (MAX_WHERE_NAME);
  INFO_NUM_ITEM (LEVEL_HERO);
  INFO_NUM_ITEM (LEVEL_IMMORTAL);
  INFO_NUM_ITEM (LEVEL_SUPREME);
  INFO_NUM_ITEM (LEVEL_INFINITE);
  INFO_NUM_ITEM (LEVEL_ETERNAL);
  INFO_NUM_ITEM (LEVEL_IMPLEMENTOR);
  INFO_NUM_ITEM (LEVEL_SUB_IMPLEM);
  INFO_NUM_ITEM (LEVEL_ASCENDANT);
  INFO_NUM_ITEM (LEVEL_GREATER);
  INFO_NUM_ITEM (LEVEL_GOD);
  INFO_NUM_ITEM (LEVEL_LESSER);
  INFO_NUM_ITEM (LEVEL_TRUEIMM);
  INFO_NUM_ITEM (LEVEL_DEMI);
  INFO_NUM_ITEM (LEVEL_SAVIOR);
  INFO_NUM_ITEM (LEVEL_CREATOR);
  INFO_NUM_ITEM (LEVEL_ACOLYTE);
  INFO_NUM_ITEM (LEVEL_NEOPHYTE);
  INFO_NUM_ITEM (LEVEL_AVATAR);
  INFO_NUM_ITEM (LEVEL_LOG);
  INFO_NUM_ITEM (LEVEL_HIGOD);

  /* other numbers */
  
  INFO_NUM_ITEM (SECONDS_PER_TICK);
  INFO_NUM_ITEM (PULSE_PER_SECOND);
  INFO_NUM_ITEM (PULSE_VIOLENCE	);
  INFO_NUM_ITEM (PULSE_MOBILE		);
  INFO_NUM_ITEM (PULSE_TICK		  );
  INFO_NUM_ITEM (PULSE_AREA			);
  INFO_NUM_ITEM (PULSE_AUCTION		);

  return 1;  /* the table itself */
}  /* end of L_system_info */

#define SYSDATA_STR_ITEM(arg) \
  if (sysdata.arg)  \
  {   \
  lua_pushstring (L, sysdata.arg);  \
  lua_setfield (L, -2, #arg); \
  }
  
#define SYSDATA_NUM_ITEM(arg) \
  lua_pushnumber (L, sysdata.arg);  \
  lua_setfield (L, -2, #arg)
  
#define SYSDATA_BOOL_ITEM(arg) \
  lua_pushboolean (L, sysdata.arg != 0);  \
  lua_setfield (L, -2, #arg)
  
static int L_sysdata (lua_State *L)
{
  lua_newtable(L);  /* table for the info */
    
  SYSDATA_NUM_ITEM (maxplayers);   /* Maximum players this boot   */
  SYSDATA_NUM_ITEM (alltimemax);   /* Maximum players ever   */
  SYSDATA_NUM_ITEM (global_looted);   /* Gold looted this boot */
  SYSDATA_NUM_ITEM (upill_val); /* Used pill value */
  SYSDATA_NUM_ITEM (upotion_val);  /* Used potion value */
  SYSDATA_NUM_ITEM (brewed_used);  /* Brewed potions used */
  SYSDATA_NUM_ITEM (scribed_used); /* Scribed scrolls used */
  SYSDATA_STR_ITEM (time_of_max);   /* Time of max ever */
  SYSDATA_STR_ITEM (mud_name);   /* Name of mud */
  SYSDATA_BOOL_ITEM (NO_NAME_RESOLVING); /* Hostnames are not resolved  */
  SYSDATA_BOOL_ITEM (DENY_NEW_PLAYERS);  /* New players cannot connect  */
  SYSDATA_BOOL_ITEM (WAIT_FOR_AUTH);  /* New players must be auth'ed */
  SYSDATA_NUM_ITEM (read_all_mail); /* Read all player mail(was 54) */
  SYSDATA_NUM_ITEM (read_mail_free);   /* Read mail for free (was 51) */
  SYSDATA_NUM_ITEM (write_mail_free);  /* Write mail for free(was 51) */
  SYSDATA_NUM_ITEM (take_others_mail); /* Take others mail (was 54)   */
  SYSDATA_NUM_ITEM (muse_level); /* Level of muse channel */
  SYSDATA_NUM_ITEM (think_level);   /* Level of think channel LEVEL_HIGOD */
  SYSDATA_NUM_ITEM (build_level);   /* Level of build channel LEVEL_BUILD */
  SYSDATA_NUM_ITEM (log_level);  /* Level of log channel LEVEL LOG */
  SYSDATA_NUM_ITEM (level_modify_proto);  /* Level to modify prototype stuff LEVEL_LESSER */
  SYSDATA_NUM_ITEM (level_override_private); /* override private flag */
  SYSDATA_NUM_ITEM (level_mset_player);   /* Level to mset a player */
  SYSDATA_NUM_ITEM (bash_plr_vs_plr);  /* Bash mod player vs. player */
  SYSDATA_NUM_ITEM (bash_nontank);  /* Bash mod basher != primary attacker */
  SYSDATA_NUM_ITEM (gouge_plr_vs_plr); /* Gouge mod player vs. player */
  SYSDATA_NUM_ITEM (gouge_nontank); /* Gouge mod player != primary attacker */
  SYSDATA_NUM_ITEM (stun_plr_vs_plr);  /* Stun mod player vs. player */
  SYSDATA_NUM_ITEM (stun_regular);  /* Stun difficult */
  SYSDATA_NUM_ITEM (dodge_mod);  /* Divide dodge chance by */
  SYSDATA_NUM_ITEM (parry_mod);  /* Divide parry chance by */
  SYSDATA_NUM_ITEM (tumble_mod); /* Divide tumble chance by */
  SYSDATA_NUM_ITEM (dam_plr_vs_plr);   /* Damage mod player vs. player */
  SYSDATA_NUM_ITEM (dam_plr_vs_mob);   /* Damage mod player vs. mobile */
  SYSDATA_NUM_ITEM (dam_mob_vs_plr);   /* Damage mod mobile vs. player */
  SYSDATA_NUM_ITEM (dam_mob_vs_mob);   /* Damage mod mobile vs. mobile */
  SYSDATA_NUM_ITEM (level_getobjnotake);  /* Get objects without take flag */
  SYSDATA_NUM_ITEM (level_forcepc); /* The level at which you can use force on players. */
  SYSDATA_NUM_ITEM (bestow_dif); /* Max # of levels between trust and command level for a bestow to work --Blodkai */
  SYSDATA_NUM_ITEM (max_sn);  /* Max skills */
  SYSDATA_STR_ITEM (guild_overseer);   /* Pointer to char containing the name of the */
  SYSDATA_STR_ITEM (guild_advisor); /* guild overseer and advisor. */
  SYSDATA_NUM_ITEM (save_flags);   /* Toggles for saving conditions */
  SYSDATA_NUM_ITEM (save_frequency);   /* How old to autosave someone */
  SYSDATA_NUM_ITEM (check_imm_host);   /* Do we check immortal's hosts? */
  SYSDATA_NUM_ITEM (morph_opt);  /* Do we optimize morph's? */
  SYSDATA_NUM_ITEM (save_pets);  /* Do pets save? */
  SYSDATA_NUM_ITEM (ban_site_level);   /* Level to ban sites */
  SYSDATA_NUM_ITEM (ban_class_level);  /* Level to ban classes */
  SYSDATA_NUM_ITEM (ban_race_level);   /* Level to ban races */
  SYSDATA_NUM_ITEM (ident_retries); /* Number of times to retry broken pipes. */
  SYSDATA_NUM_ITEM (pk_loot); /* Pkill looting allowed? */  
    
  return 1;  /* the table itself */
}  /* end of L_sysdata */

#define CH_STR_ITEM(arg) \
  if (ch->arg)  \
  {   \
  lua_pushstring (L, ch->arg);  \
  lua_setfield (L, -2, #arg); \
  }
  
#define CH_NUM_ITEM(arg) \
  lua_pushnumber (L, ch->arg);  \
  lua_setfield (L, -2, #arg)
    
#define PC_STR_ITEM(arg) \
if (pc->arg)  \
  {   \
  lua_pushstring (L, pc->arg);  \
  lua_setfield (L, -2, #arg); \
  }
  
#define PC_NUM_ITEM(arg) \
  lua_pushnumber (L, pc->arg);  \
  lua_setfield (L, -2, #arg)
    
static int L_character_info (lua_State *L)
{
  CHAR_DATA * ch = L_find_character (L);
     
  if (!ch)
    return 0;
 
  PC_DATA *pc = ch->pcdata;
  
  lua_newtable(L);  /* table for the info */
  
  /* strings */
  
  CH_STR_ITEM (name);
  CH_STR_ITEM (short_descr);
  CH_STR_ITEM (long_descr);
  CH_STR_ITEM (description);
  
  /* numbers */
  
  CH_NUM_ITEM (num_fighting);
  CH_NUM_ITEM (substate);
  CH_NUM_ITEM (sex);
  CH_NUM_ITEM (Class);
  CH_NUM_ITEM (race);
  CH_NUM_ITEM (level);
  CH_NUM_ITEM (trust);
  CH_NUM_ITEM (played);
  CH_NUM_ITEM (logon);
  CH_NUM_ITEM (save_time);
  CH_NUM_ITEM (timer);
  CH_NUM_ITEM (wait);
  CH_NUM_ITEM (hit);
  CH_NUM_ITEM (max_hit);
  CH_NUM_ITEM (mana);
  CH_NUM_ITEM (max_mana);
  CH_NUM_ITEM (move);
  CH_NUM_ITEM (max_move);
  CH_NUM_ITEM (practice);
  CH_NUM_ITEM (numattacks);
  CH_NUM_ITEM (gold);
  CH_NUM_ITEM (exp);
  CH_NUM_ITEM (carry_weight);
  CH_NUM_ITEM (carry_number);
  CH_NUM_ITEM (xflags);
  CH_NUM_ITEM (no_immune);
  CH_NUM_ITEM (no_resistant);
  CH_NUM_ITEM (no_susceptible);
  CH_NUM_ITEM (immune);
  CH_NUM_ITEM (resistant);
  CH_NUM_ITEM (susceptible);
  CH_NUM_ITEM (speaks);
  CH_NUM_ITEM (speaking);
  CH_NUM_ITEM (saving_poison_death);
  CH_NUM_ITEM (saving_wand);
  CH_NUM_ITEM (saving_para_petri);
  CH_NUM_ITEM (saving_breath);
  CH_NUM_ITEM (saving_spell_staff);
  CH_NUM_ITEM (alignment);
  CH_NUM_ITEM (barenumdie);
  CH_NUM_ITEM (baresizedie);
  CH_NUM_ITEM (mobthac0);
  CH_NUM_ITEM (hitroll);
  CH_NUM_ITEM (damroll);
  CH_NUM_ITEM (hitplus);
  CH_NUM_ITEM (damplus);
  CH_NUM_ITEM (position);
  CH_NUM_ITEM (defposition);
  CH_NUM_ITEM (style);
  CH_NUM_ITEM (height);
  CH_NUM_ITEM (weight);
  CH_NUM_ITEM (armor);
  CH_NUM_ITEM (wimpy);
  CH_NUM_ITEM (deaf);
  CH_NUM_ITEM (perm_str);
  CH_NUM_ITEM (perm_int);
  CH_NUM_ITEM (perm_wis);
  CH_NUM_ITEM (perm_dex);
  CH_NUM_ITEM (perm_con);
  CH_NUM_ITEM (perm_cha);
  CH_NUM_ITEM (perm_lck);
  CH_NUM_ITEM (mod_str);
  CH_NUM_ITEM (mod_int);
  CH_NUM_ITEM (mod_wis);
  CH_NUM_ITEM (mod_dex);
  CH_NUM_ITEM (mod_con);
  CH_NUM_ITEM (mod_cha);
  CH_NUM_ITEM (mod_lck);
  CH_NUM_ITEM (mental_state);  
  CH_NUM_ITEM (emotional_state);  
     
  /* player characters have extra stuff (in "pc" sub table)  */
  if (pc)
  {
   lua_newtable(L);  /* table for the info */
      
   PC_STR_ITEM (homepage);
   PC_STR_ITEM (clan_name);
   PC_STR_ITEM (council_name);
   PC_STR_ITEM (deity_name);
   PC_STR_ITEM (bamfin);
   PC_STR_ITEM (bamfout);
   PC_STR_ITEM (filename);   
   PC_STR_ITEM (rank);
   PC_STR_ITEM (title);
   PC_STR_ITEM (bestowments);     
   PC_STR_ITEM (helled_by);
   PC_STR_ITEM (bio);  
   PC_STR_ITEM (authed_by); 
   PC_STR_ITEM (prompt);    
   PC_STR_ITEM (fprompt);   
   PC_STR_ITEM (subprompt); 
   
   PC_NUM_ITEM (flags);     
   PC_NUM_ITEM (pkills);    
   PC_NUM_ITEM (pdeaths);   
   PC_NUM_ITEM (mkills);    
   PC_NUM_ITEM (mdeaths);   
   PC_NUM_ITEM (illegal_pk);   
   PC_NUM_ITEM (outcast_time); 
   PC_NUM_ITEM (restore_time); 
   PC_NUM_ITEM (r_range_lo); 
   PC_NUM_ITEM (r_range_hi);
   PC_NUM_ITEM (m_range_lo); 
   PC_NUM_ITEM (m_range_hi);
   PC_NUM_ITEM (o_range_lo); 
   PC_NUM_ITEM (o_range_hi);
   PC_NUM_ITEM (wizinvis);   
   PC_NUM_ITEM (min_snoop);  
   PC_NUM_ITEM (quest_number); 
   PC_NUM_ITEM (quest_curr);   
   PC_NUM_ITEM (quest_accum);  
   PC_NUM_ITEM (favor);        
   PC_NUM_ITEM (charmies);     
   PC_NUM_ITEM (auth_state);
   PC_NUM_ITEM (release_date); 
   PC_NUM_ITEM (pagerlen);     
   lua_setfield (L, -2, "pc");  
  }
     
  return 1;  /* the table itself */
}  /* end of L_character_info */
 
#define OBJ_STR_ITEM(arg) \
if (obj->arg)  \
  {   \
  lua_pushstring (L, obj->arg);  \
  lua_setfield (L, -2, #arg); \
  }
  
#define OBJ_NUM_ITEM(arg) \
  lua_pushnumber (L, obj->arg);  \
  lua_setfield (L, -2, #arg)
  
  
static void build_inventory (lua_State *L, OBJ_DATA * obj );

static void add_object_item (lua_State *L, OBJ_DATA * obj, const int item)
  {
int i;
  lua_newtable(L);  /* table for the info */
          
  OBJ_STR_ITEM (name);
  OBJ_STR_ITEM (short_descr);
  OBJ_STR_ITEM (description);
  OBJ_STR_ITEM (action_desc);
   
  OBJ_NUM_ITEM (item_type);
  OBJ_NUM_ITEM (magic_flags);  
  OBJ_NUM_ITEM (wear_flags);
  OBJ_NUM_ITEM (wear_loc);
  OBJ_NUM_ITEM (weight);
  OBJ_NUM_ITEM (cost);
  OBJ_NUM_ITEM (level);
  OBJ_NUM_ITEM (timer);
  OBJ_NUM_ITEM (count);    
  OBJ_NUM_ITEM (serial);   
  OBJ_NUM_ITEM (room_vnum);
  
  if (obj->pIndexData)
    {
    lua_pushnumber (L, obj->pIndexData->vnum); 
    lua_setfield (L, -2, "vnum");
    }
    
  lua_newtable(L);  /* table for the values */
  
  /* do 6 values */
  for (i = 0; i < 6; i++)
    {
    lua_pushnumber (L, obj->value [i]); 
    lua_rawseti (L, -2, i + 1);       
    }
  lua_setfield (L, -2, "value");    
  
  if( obj->first_content )   /* node has a child? */
    {
    lua_newtable(L);  /* table for the inventory */
    build_inventory(L,  obj->first_content );
    lua_setfield (L, -2, "contents");
    }
  lua_rawseti (L, -2, item);         
  
    
  } /* end of add_object_item */
 
/* do one inventory level */
  
static void build_inventory (lua_State *L, OBJ_DATA * obj )
{
  int item = 1;
   
  for ( ; obj; obj = obj->next_content)
    {
      
    /* if carrying it, add to table */
    
    if( obj->wear_loc == WEAR_NONE)
      add_object_item (L, obj, item++);
      
  }  /* end of for loop */
  
}  /* end of build_inventory */

static int L_inventory (lua_State *L)
{
  CHAR_DATA * ch = L_find_character (L);
   
  if (!ch)
    return 0;
    
  lua_newtable(L);  /* table for the inventory */
    
  /* recursively build inventory */
  
  build_inventory (L, ch->first_carrying);
  
  return 1;  /* the table itself */
}  /* end of L_inventory */

static int L_level (lua_State *L)
{
  CHAR_DATA * ch = L_find_character (L);
   
  if (!ch)
    return 0;
  
  lua_pushnumber (L, ch->level);
  return 1;
}  /* end of L_level */
    
static int L_race (lua_State *L)
{
  CHAR_DATA * ch = L_find_character (L);
   
  if (!ch)
    return 0;
  
  lua_pushstring (L, get_race (ch));
  return 1;
}  /* end of L_race */

static int L_class (lua_State *L)
{
  CHAR_DATA * ch = L_find_character (L);
   
  if (!ch)
    return 0;
  
  lua_pushstring (L, get_class (ch));
  return 1;
}  /* end of L_class */
    
static int L_equipped (lua_State *L)
  {
  int item = 1;
  CHAR_DATA * ch = L_find_character (L);
  OBJ_DATA * obj;
  
  if (!ch)
    return 0;
    
  lua_newtable(L);  /* table for the inventory */
    
  for (obj = ch->first_carrying ; obj; obj = obj->next_content)
    {
    if (ch == obj->carried_by && 
        obj->wear_loc > WEAR_NONE)
      add_object_item (L, obj, item++);
     }  /* end of for loop */

  return 1;  /* the table itself */
}  /* end of L_equipped */

static bool check_inventory (CHAR_DATA * ch, OBJ_DATA * obj, const int vnum )
{
  /* check all siblings */   
  for ( ; obj; obj = obj->next_content)
    {
    if( obj->wear_loc == WEAR_NONE)
      {
      if (obj->pIndexData && obj->pIndexData->vnum == vnum)
        return TRUE;

      }  /* end of carrying it */

    if( obj->first_content )   /* node has a child? */
      {
      if (check_inventory(ch, obj->first_content, vnum ))
        return TRUE;
      }
    
  }  /* end of for loop */
  
  return FALSE;
}  /* end of check_inventory */

/* argument: vnum of item */

static int L_carryingvnum (lua_State *L)
{
  CHAR_DATA * ch = L_getchar (L);
   
  int vnum = check_vnum (L);
     
  /* recursively check inventory */
  
  lua_pushboolean (L, check_inventory (ch, ch->first_carrying, vnum));
  
  return 1;  /* the result */
}  /* end of L_carryingvnum */

static int count_inventory (CHAR_DATA * ch, OBJ_DATA * obj, 
                            const int vnum, const int recurse )
{
  int count = 0;    /* count at this level */
  
  /* check all siblings */   
  for ( ; obj; obj = obj->next_content)
    {
    if( obj->wear_loc == WEAR_NONE)
      {
      if (obj->pIndexData && obj->pIndexData->vnum == vnum)
        count += obj->count;
      }  /* end of carrying it */

    if (recurse)
      if( obj->first_content )   /* node has a child? */
        count += count_inventory(ch, obj->first_content, vnum, recurse);
    
  }  /* end of for loop */
  
  return count;
}  /* end of count_inventory */

/* argument: vnum of item */

static int L_possessvnum (lua_State *L)
{
  CHAR_DATA * ch = L_getchar (L);
   
  int vnum = check_vnum (L);
  int recurse = optboolean (L, 2, FALSE);
     
  /* recursively check inventory unless 2nd argument is false */
  
  lua_pushnumber (L, count_inventory (ch, ch->first_carrying, vnum, recurse));
  
  return 1;  /* the result */
}  /* end of L_possessvnum */

/* argument: location of item (eg. finger, legs, see item_w_flags) */
 
static int L_wearing (lua_State *L)
{
CHAR_DATA * ch = L_getchar (L);
OBJ_DATA * obj;
const char * sloc = luaL_checkstring (L, 1);  /* location, eg. finger */
int loc;

  /* translate location into a number */
  for (loc = 0; loc < MAX_WEAR; loc++)
    if (strcasecmp (sloc, item_w_flags [loc]) == 0)
       break;
     
   if (loc >= MAX_WEAR)
     luaL_error (L, "Bad wear location '%s' to 'wearing'", sloc);
    
   for (obj = ch->first_carrying ; obj; obj = obj->next_content)
    {
    if (ch == obj->carried_by && loc == obj->wear_loc)
       {
       lua_pushnumber (L, obj->pIndexData->vnum);
       return 1;
       }  /* end of if worn at desired location */
    }  /* end of for loop */

  lua_pushboolean (L, FALSE);
  return 1;  /* the result */
}  /* end of L_wearing */
    
/* argument: vnum of item */
 
static int L_wearingvnum (lua_State *L)
{
CHAR_DATA * ch = L_getchar (L);
OBJ_DATA * obj;
bool found = FALSE;

  int vnum = check_vnum (L);
      
  for (obj = ch->first_carrying ; obj; obj = obj->next_content)
    {
    if (ch == obj->carried_by && 
        obj->wear_loc > WEAR_NONE && 
        obj->pIndexData && obj->pIndexData->vnum == vnum)
       {
       found = TRUE;
       break;
       }  /* end of if carried this vnum */
    }  /* end of for loop */

  lua_pushboolean (L, found);
     
  return 1;  /* the result */
}  /* end of L_wearingvnum */

#define MOB_STR_ITEM(arg) \
if (mob->arg)  \
  {   \
  lua_pushstring (L, mob->arg);  \
  lua_setfield (L, -2, #arg); \
  }
  
#define MOB_NUM_ITEM(arg) \
  lua_pushnumber (L, mob->arg);  \
  lua_setfield (L, -2, #arg)
  
static int L_mob_info (lua_State *L)
  {
  MOB_INDEX_DATA *mob = get_mob_index( check_vnum (L) );
  if (!mob)
    return 0;
    
 lua_newtable(L);  /* table for the info */
  
  /* strings */
     
  MOB_STR_ITEM (player_name);
  MOB_STR_ITEM (short_descr);
  MOB_STR_ITEM (long_descr);
  MOB_STR_ITEM (description);
  MOB_STR_ITEM (spec_funname);
  
  MOB_NUM_ITEM (vnum);
  MOB_NUM_ITEM (count);
  MOB_NUM_ITEM (killed);
  MOB_NUM_ITEM (sex);
  MOB_NUM_ITEM (level);
  MOB_NUM_ITEM (alignment);
  MOB_NUM_ITEM (mobthac0);
  MOB_NUM_ITEM (ac);
  MOB_NUM_ITEM (hitnodice);
  MOB_NUM_ITEM (hitsizedice);
  MOB_NUM_ITEM (hitplus);
  MOB_NUM_ITEM (damnodice);
  MOB_NUM_ITEM (damsizedice);
  MOB_NUM_ITEM (damplus);
  MOB_NUM_ITEM (numattacks);
  MOB_NUM_ITEM (gold);
  MOB_NUM_ITEM (exp);
  MOB_NUM_ITEM (xflags);
  MOB_NUM_ITEM (immune);
  MOB_NUM_ITEM (resistant);
  MOB_NUM_ITEM (susceptible);
  MOB_NUM_ITEM (speaks);
  MOB_NUM_ITEM (speaking);
  MOB_NUM_ITEM (position);
  MOB_NUM_ITEM (defposition);
  MOB_NUM_ITEM (height);
  MOB_NUM_ITEM (weight);
  MOB_NUM_ITEM (race);
  MOB_NUM_ITEM (Class);
  MOB_NUM_ITEM (hitroll);
  MOB_NUM_ITEM (damroll);
  MOB_NUM_ITEM (perm_str);
  MOB_NUM_ITEM (perm_int);
  MOB_NUM_ITEM (perm_wis);
  MOB_NUM_ITEM (perm_dex);
  MOB_NUM_ITEM (perm_con);
  MOB_NUM_ITEM (perm_cha);
  MOB_NUM_ITEM (perm_lck);
  MOB_NUM_ITEM (saving_poison_death);
  MOB_NUM_ITEM (saving_wand);
  MOB_NUM_ITEM (saving_para_petri);
  MOB_NUM_ITEM (saving_breath);
  MOB_NUM_ITEM (saving_spell_staff);

  return 1;  /* the table itself */ 
  }  /* end of L_mob_info */

  
static int L_object_info (lua_State *L)
  {
OBJ_INDEX_DATA *obj = get_obj_index ( check_vnum (L) );
int i;

  if (!obj)
    return 0;
    
 lua_newtable(L);  /* table for the info */
  
  /* strings */
     
   OBJ_STR_ITEM (name);
   OBJ_STR_ITEM (short_descr);
   OBJ_STR_ITEM (description);
   OBJ_STR_ITEM (action_desc);

   OBJ_NUM_ITEM (vnum);
   OBJ_NUM_ITEM (level);
   OBJ_NUM_ITEM (item_type);
   OBJ_NUM_ITEM (magic_flags);  
   OBJ_NUM_ITEM (wear_flags);
   OBJ_NUM_ITEM (count);
   OBJ_NUM_ITEM (weight);
   OBJ_NUM_ITEM (cost);
   OBJ_NUM_ITEM (serial);
   OBJ_NUM_ITEM (layers);

  lua_newtable(L);  /* table for the values */
  
  /* do 6 values */
  for (i = 0; i < 6; i++)
    {
    lua_pushnumber (L, obj->value [i]); 
    lua_rawseti (L, -2, i + 1);       
    }
  lua_setfield (L, -2, "value");    

   
  return 1;  /* the table itself */ 
  }  /* end of L_object_info */
  
static int L_object_name (lua_State *L)
  {
OBJ_INDEX_DATA *obj = get_obj_index ( check_vnum (L) );

  if (!obj)
    return 0;  

  lua_pushstring (L, obj->short_descr);
  lua_pushstring (L, obj->description);
  
  return 2;
} /* end of L_object_name */

static int L_mob_name (lua_State *L)
  {
MOB_INDEX_DATA *mob = get_mob_index ( check_vnum (L) );

  if (!mob)
    return 0;  

  lua_pushstring (L, mob->short_descr);
  lua_pushstring (L, mob->description);
  
  return 2;
} /* end of L_mob_name */

#define ROOM_STR_ITEM(arg) \
if (room->arg)  \
  {   \
  lua_pushstring (L, room->arg);  \
  lua_setfield (L, -2, #arg); \
  }
  
#define ROOM_NUM_ITEM(arg) \
  lua_pushnumber (L, room->arg);  \
  lua_setfield (L, -2, #arg)
    
#define EXIT_STR_ITEM(arg) \
if (pexit->arg)  \
  {   \
  lua_pushstring (L, pexit->arg);  \
  lua_setfield (L, -2, #arg); \
  }
  
#define  EXIT_NUM_ITEM(arg) \
  lua_pushnumber (L, pexit->arg);  \
  lua_setfield (L, -2, #arg)
        
static int L_room_info (lua_State *L)
{
  CHAR_DATA * ch = L_getchar (L); /* get character pointer */
  ROOM_INDEX_DATA * room = ch->in_room;  /* which room s/he is in */
  EXIT_DATA * pexit;
  
  if (lua_isnumber (L, 1))
    room = get_room_index( check_vnum (L) );
 
  if (room == NULL) 
    return 0;  /* oops - not in room or specified room does not exist */
    
  
  lua_newtable(L);  /* table for the info */
  
  /* strings */
  
  ROOM_STR_ITEM (name);
  ROOM_STR_ITEM (description);
  
  /* numbers */
  
  ROOM_NUM_ITEM (vnum);
  ROOM_NUM_ITEM (room_flags);
  ROOM_NUM_ITEM (light);   
  ROOM_NUM_ITEM (sector_type);
  ROOM_NUM_ITEM (tele_vnum);
  ROOM_NUM_ITEM (tele_delay);
  ROOM_NUM_ITEM (tunnel);  
       
  /* now do the exits */
  
  lua_newtable(L);  /* table for all exits */
  
  for( pexit = room->first_exit; pexit; pexit = pexit->next )
    {
    lua_newtable(L);  /* table for the info */
  
    EXIT_STR_ITEM (keyword);    
    EXIT_STR_ITEM (description);
        
    EXIT_NUM_ITEM (vnum);       
    EXIT_NUM_ITEM (rvnum);      
    EXIT_NUM_ITEM (exit_info);  
    EXIT_NUM_ITEM (key);        
    EXIT_NUM_ITEM (distance);   
    EXIT_NUM_ITEM (pull);       
    EXIT_NUM_ITEM (pulltype);   
    
    lua_rawseti (L, -2, pexit->vdir);  /* key is direction */
    }

  lua_setfield (L, -2, "exits");
  
  return 1;  /* the table itself */
}  /* end of L_room_info */

static int L_room_name (lua_State *L)
{
  CHAR_DATA * ch = L_getchar (L); /* get character pointer */
  ROOM_INDEX_DATA * room = ch->in_room;  /* which room s/he is in */
   
  if (lua_isnumber (L, 1))
    room = get_room_index( check_vnum (L) );
 
  if (room == NULL) 
    return 0;  /* oops - not in room or specified room does not exist */
    
  lua_pushstring (L, room->name);
  lua_pushstring (L, room->description);
  return 2;
  } /* end of L_room_name */
  
static char *dir_text[] = { "n", "e", "s", "w", "u", "d", "ne", "nw", "se", "sw", "?" };

static int L_room_exits (lua_State *L)
{
  ROOM_INDEX_DATA * room = get_room_index( check_vnum (L) );
  EXIT_DATA * pexit;
  int iExceptFlag = luaL_optnumber (L, 2, 0);  /* eg. except locked */
  
  if (room == NULL) 
    return 0;  /*  specified room does not exist */
       
  /* now do the exits */
  
  lua_newtable(L);  /* table for all exits */
  
  for( pexit = room->first_exit; pexit; pexit = pexit->next )
    {
    if ((pexit->exit_info & iExceptFlag) != 0)
      continue;  /* ignore, eg. locked exit */
    
    if (pexit->vdir < 0 || pexit->vdir > NUMITEMS (dir_text))
      continue;  /* can't get exit code */
      
    // key is exit name, value is exit direction
    lua_pushnumber (L, pexit->vnum);
    lua_setfield (L, -2, dir_text [pexit->vdir]) ;    
    }  /* end for loop */
   
  return 1;  /* the table itself */
}  /* end of L_room_exits */

static int L_send_to_char (lua_State *L)
{
  send_to_char(luaL_checkstring (L, 1), L_getchar (L));
  return 0;
}  /* end of L_send_to_char */

/* act as if character had typed the command */
static int L_interpret (lua_State *L)
{
  char command [MAX_INPUT_LENGTH];  
  strncpy (command, luaL_checkstring (L, 1), sizeof (command));  
  command [sizeof (command) - 1] = 0;  
  
  interpret (L_getchar (L), command);
  return 0;
}  /* end of L_interpret */

/* force any character, or a mob in this room, to do something */
static int L_force (lua_State *L)
{
  CHAR_DATA * ch = L_find_character (L);
  int iCommand = 2;
  
  if (!ch)
    luaL_error (L, "Cannot find character/mob to force.");
    
 /* if character was (vnum, boolean) then command is 3rd argument */
 if (lua_type (L, 1) == LUA_TNUMBER && 
     lua_type (L, 2) == LUA_TBOOLEAN)
    iCommand++;
       
  char command [MAX_INPUT_LENGTH];  
  strncpy (command, luaL_checkstring (L, iCommand), sizeof (command));  
  command [sizeof (command) - 1] = 0;  
  
  interpret (ch, command);
  return 0;
}  /* end of L_force */

/* transfer character or mob to somewhere (default is here) */
static int L_transfer (lua_State *L)
{
CHAR_DATA * ch = L_getchar (L);
CHAR_DATA * victim = L_find_character (L);
int iCommand = 2;
ROOM_INDEX_DATA *location;
int vnum;
  
 /* if character was (vnum, boolean) then room is 3rd argument */
 if (lua_type (L, 1) == LUA_TNUMBER && 
     lua_type (L, 2) == LUA_TBOOLEAN)
    iCommand++;
  
 vnum = luaL_optnumber (L, iCommand, ch->in_room->vnum);

  if ( vnum < 1 || vnum > MAX_VNUM )
    luaL_error (L, "Vnum %d is out of range 1 to %d", vnum, MAX_VNUM);
  
  if (!victim)
    luaL_error (L, "Cannot find character/mob to transfer.");
  
  location = get_room_index(vnum );
   
  if (!location)
    luaL_error (L, "Cannot find destination room: %d", vnum);
    
  if (victim->fighting)
      stop_fighting( victim, TRUE );
      
  char_from_room( victim );
  char_to_room( victim, location );
    
  return 0;
  } /* end of L_transfer */
  
/* stop the nominated character fighting */
static int L_stop_fighting (lua_State *L)
{
  CHAR_DATA * ch = L_find_character (L);
  
  if (!ch)
    luaL_error (L, "Cannot find character/mob to stop fighting.");
  
  if (ch->fighting)
      stop_fighting( ch, TRUE );
   
  return 0;
  } /* end of L_stop_fighting */

/* is the nominated character fighting? */
static int L_fighting (lua_State *L)
{
  CHAR_DATA * ch = L_find_character (L);
  
  if (!ch)
    luaL_error (L, "Cannot find character/mob.");
  
  lua_pushboolean (L, ch->fighting != NULL);
   
  return 1;
  } /* end of L_fighting */

  
static int L_gain_exp (lua_State *L)
{
  CHAR_DATA * ch = L_getchar (L); /* get character pointer */
  gain_exp (ch, luaL_checknumber (L, 1));
  lua_pushnumber (L, ch->exp);
  return 1;  /* return new amount */
}  /* end of L_gain_exp */

static int L_gain_gold (lua_State *L)
{
  CHAR_DATA * ch = L_getchar (L); /* get character pointer */
  ch->gold += luaL_checknumber (L, 1);
  lua_pushnumber (L, ch->gold);
  return 1;
}  /* end of L_gain_gold */

/* remove an item from the character's top-level inventory */
static int L_destroy_item (lua_State *L)
{
  CHAR_DATA * ch = L_getchar (L); /* get character pointer */
  OBJ_DATA * obj;
  int vnum = check_vnum (L);
  int count = luaL_optnumber (L, 2, 1);
  
  int done = 0;    /* number we destroyed so far */
  
  /* check all siblings */   
  for (obj = ch->first_carrying; obj && done < count; obj = obj->next_content)
    {
    if( obj->wear_loc == WEAR_NONE)
      {
      if (obj->pIndexData && obj->pIndexData->vnum == vnum)
        {
        if( (done + obj->count) > count )   /* if too many, split them */
            split_obj( obj, count - done );
         done += obj->count;    /* we have destroyed this many */
         extract_obj ( obj );  /* remove it */
        }
      }  /* end of carrying it */
   
  }  /* end of for loop */
  
  /* return how many we destroyed */
  lua_pushnumber (L, done);
  return 1;
}  /* end of L_destroy_item */

static int L_oinvoke (lua_State *L)
{
CHAR_DATA * ch = L_getchar (L); /* get character pointer */
OBJ_INDEX_DATA *pObjIndex;
OBJ_DATA * obj;
bool taken = FALSE;

  int vnum = check_vnum (L);
  int level = luaL_optnumber (L, 2, 1);
  if ( level < 1 || level > MAX_LEVEL )
    luaL_error (L, "Level %d is out of range 1 to %d", level, MAX_LEVEL);

  pObjIndex = get_obj_index ( vnum );

  if (!pObjIndex)
   luaL_error (L, "Cannot invoke object vnum %d - it does not exist", vnum);
   
   obj = create_object( pObjIndex, level );
   if( CAN_WEAR( obj, ITEM_TAKE ) )
     {
     obj = obj_to_char( obj, ch );
     taken = TRUE;
   }
   else
      obj = obj_to_room( obj, ch->in_room );
      
  lua_pushboolean (L, taken);
  
  return 1;  /* true if we took the item */
} /* end of L_oinvoke */
   

/* invoke a mob and equip it */

static int L_minvoke (lua_State *L)
{
CHAR_DATA * ch = L_getchar (L); /* get character pointer */
MOB_INDEX_DATA *pMobIndex;
CHAR_DATA * mob;
int i;             
int n = lua_gettop(L);  /* number of arguments */

  int vnum = check_vnum (L);

  pMobIndex = get_mob_index(vnum);

  if (!pMobIndex)
    luaL_error (L, "Cannot invoke mob vnum %d - it does not exist", vnum);

  mob = create_mobile( pMobIndex );
  char_to_room ( mob, ch->in_room );
  
  /* equip this mob */
  
  for (i = 2; i <= n; i++)
    {
    OBJ_INDEX_DATA *pObjIndex;
    OBJ_DATA * obj;
       
    int ovnum = luaL_checknumber (L, i);
    pObjIndex = get_obj_index ( ovnum );
    if (!pObjIndex)
      luaL_error (L, "Cannot equip mob %d with object vnum %d - object does not exist",
                 vnum, ovnum);
     
     obj = create_object( pObjIndex, mob->level );
     if( CAN_WEAR( obj, ITEM_TAKE ) )
       obj = obj_to_char( obj, mob );
     else
       obj = obj_to_room( obj, ch->in_room );
                     
    }
  
  return 0;  
} /* end of L_minvoke */


   
static int L_mobinworld (lua_State *L)
{
MOB_INDEX_DATA *m_index;
int world_count = 0;
  
  m_index = get_mob_index ( check_vnum (L) );

  if (m_index)
    world_count = m_index->count;
  
  if (world_count)
    lua_pushnumber (L, world_count);
  else
    lua_pushboolean (L, FALSE);
  return 1;
}  /* end of L_mobinworld */

static int L_mobinarea (lua_State *L)
{
CHAR_DATA * ch = L_getchar (L);
CHAR_DATA *tmob;
MOB_INDEX_DATA *m_index;
int world_count = 0;
int found_count = 0;
int result = 0;
int vnum = check_vnum (L);

  m_index = get_mob_index ( vnum );

  if (m_index )
    world_count = m_index->count;
  
  for( tmob = first_char; tmob && found_count != world_count; tmob = tmob->next )
   {
   if( IS_NPC( tmob ) && tmob->pIndexData->vnum == vnum )
     {
      found_count++;
      if( tmob->in_room->area == ch->in_room->area )
         result++;
     }
   }
   
  if (result)
    lua_pushnumber (L, result);
  else
    lua_pushboolean (L, FALSE);
  return 1;
}  /* end of L_mobinarea */

static int L_mobinroom (lua_State *L)
{
CHAR_DATA * ch = L_getchar (L);
CHAR_DATA *tmob;
int result = 0;
  
  int vnum = check_vnum (L);

  for( tmob = ch->in_room->first_person; tmob; tmob = tmob->next_in_room )
   {
   if ( IS_NPC( tmob ) && tmob->pIndexData->vnum == vnum )
         result++;
   }
   
  if (result)
    lua_pushnumber (L, result);
  else
    lua_pushboolean (L, FALSE);
  return 1;
}  /* end of L_mobinroom */

static int L_players_in_room (lua_State *L)
{
  
CHAR_DATA * ch = L_getchar (L); /* get character pointer */
ROOM_INDEX_DATA * room = ch->in_room;  /* which room s/he is in */
 
CHAR_DATA * wch;
int count = 1;
  
 if (lua_isnumber (L, 1))
    room = get_room_index( check_vnum (L) );
 
  if (room == NULL) 
    return 0;  /* oops - not in room or specified room does not exist */
  
  lua_newtable(L);  /* table for the info */
    
  for( wch = room->first_person; wch; wch = wch->next_in_room )
    if( can_see( ch, wch ) && 
       !IS_NPC( wch ))
       {
       lua_pushstring (L, wch->name); 
       lua_rawseti (L, -2, count++);  /* key is count */
       }
   
  return 1;
}  /* end of L_players_in_room */

static int L_players_in_game (lua_State *L)
{
  
CHAR_DATA * ch = L_getchar (L); /* get character pointer */
 
CHAR_DATA * wch;
int count = 1;
 
  lua_newtable(L);  /* table for the info */
    
   for( wch = first_char; wch; wch = wch->next )
      if( can_see( ch, wch ) && 
          !IS_NPC( wch ))
       {
       lua_pushstring (L, wch->name); 
       lua_rawseti (L, -2, count++);  /* key is count */
       }
   
  return 1;
}  /* end of L_players_in_game */


static int L_mobs_in_room (lua_State *L)
{
  
CHAR_DATA * ch = L_getchar (L); /* get character pointer */
ROOM_INDEX_DATA * room = ch->in_room;  /* which room s/he is in */
 
CHAR_DATA * wch;
int count = 1;
  
 if (lua_isnumber (L, 1))
    room = get_room_index( check_vnum (L) );
 
  if (room == NULL) 
    return 0;  /* oops - not in room or specified room does not exist */
  
  lua_newtable(L);  /* table for the info */
    
  for( wch = room->first_person; wch; wch = wch->next_in_room )
    if( can_see( ch, wch ) && 
       IS_NPC( wch ))
       {
       lua_pushnumber (L, wch->pIndexData->vnum); 
       lua_rawseti (L, -2, count++);  /* key is count */
       }
   
  return 1;
}  /* end of L_mobs_in_room */

static int L_room (lua_State *L)
{
  
CHAR_DATA * ch = L_find_character (L); /* get character pointer */

  if (!ch)
    return 0;
    
ROOM_INDEX_DATA * room = ch->in_room;  /* which room s/he is in */
 
  lua_pushnumber (L, room->vnum); 
  return 1;
}  /* end of L_room */

static int L_position (lua_State *L)
{
 
CHAR_DATA * ch = L_find_character (L); /* get character pointer */
const char * sPosition;

  if (!ch)
    return 0;
   
   switch ( ch->position )
   {
      case POS_DEAD:
         sPosition =  "slowly decomposing" ;
         break;
      case POS_MORTAL:
         sPosition =  "mortally wounded" ;
         break;
      case POS_INCAP:
         sPosition =  "incapacitated" ;
         break;
      case POS_STUNNED:
         sPosition =  "stunned" ;
         break;
      case POS_SLEEPING:
         sPosition =  "sleeping" ;
         break;
      case POS_RESTING:
         sPosition =  "resting" ;
         break;
      case POS_STANDING:
         sPosition =  "standing" ;
         break;
      case POS_FIGHTING:
         sPosition =  "fighting" ;
         break;
      case POS_EVASIVE:
         sPosition =  "fighting (evasive)" ; 
         break;
      case POS_DEFENSIVE:
         sPosition =  "fighting (defensive)" ;
         break;
      case POS_AGGRESSIVE:
         sPosition =  "fighting (aggressive)" ;
         break;
      case POS_BERSERK:
         sPosition =  "fighting (berserk)" ;
         break;
      case POS_MOUNTED:
         sPosition =  "mounted" ;
         break;
      case POS_SITTING:
         sPosition =  "sitting" ;
         break;

      default:
         sPosition =  "unknown" ;
         break;
   }
    
  lua_pushstring (L, sPosition);
  lua_pushnumber (L, ch->position); 
  return 2;
}  /* end of L_position */

static int L_char_name (lua_State *L)
{
  
CHAR_DATA * ch = L_find_character (L); /* get character pointer */

  if (!ch)
    return 0;
 
  lua_pushstring (L, ch->name); 
  return 1;
}  /* end of L_char_name */

static int L_char_exists (lua_State *L)
{
  
CHAR_DATA * ch = L_find_character (L); /* get character pointer */

  lua_pushboolean (L, ch != NULL);
  
  return 1;
}  /* end of L_char_exists */

static const struct luaL_reg mudlib [] = 
  {
  {"system_info", L_system_info},
  {"sysdata", L_sysdata},
  {"character_info", L_character_info},
  {"mob_info", L_mob_info},
  {"room_info", L_room_info},
  {"room_name", L_room_name},
  {"room_exits", L_room_exits},
  {"object_info", L_object_info},  /* object prototype */
  {"inventory", L_inventory},      /* invoked objects */
  {"equipped", L_equipped},        /* invoked objects */
  {"object_name", L_object_name},   /* short, long object name */
  {"mob_name", L_mob_name},   /* short, long mob name */
  {"players_in_room", L_players_in_room},  /* table of players in the room */
  {"players_in_game", L_players_in_game},  /* table of players in the game */
  {"mobs_in_room", L_mobs_in_room},  /* table of mobs in the room */
  {"level", L_level},                /* what is my level? */
  {"race", L_race},                  /* what is my race? */
  {"class", L_class},                /* what is my class? */
  {"room", L_room},                  /* what room am I in? */
  {"position", L_position},          /* character position */
  {"char_name", L_char_name},        /* what is my name? */
  {"char_exists", L_char_exists},    /* does character exist (now)? */
  
  /* do stuff to the character or others */
  
  {"send_to_char", L_send_to_char},  /* send text to character */
  {"interpret", L_interpret},        /* interpret command, as if we typed it */
  {"force", L_force},                /* force another character or mob to do something */
  {"transfer", L_transfer},          /* transfer arg1 to arg2 location */
      
  /* alter character state */
  
  {"gain_exp", L_gain_exp},         /* give character xp */
  {"gain_gold", L_gain_gold},       /* give character gold */
  {"stop_fighting", L_stop_fighting},  /* stop the character fighting */
  {"destroy_item", L_destroy_item},   /* destroy inventory item */
  
  /* make new things */
  
  {"oinvoke", L_oinvoke},           /* create an object */
  {"minvoke", L_minvoke},           /* create a mobile instance */
  
  /* if checks */
  
  {"mobinworld", L_mobinworld},
  {"mobinarea", L_mobinarea},
  {"mobinroom", L_mobinroom},
  {"carryingvnum", L_carryingvnum},  /* are they carrying at least one of vnum? */
  {"possessvnum", L_possessvnum},    /* how many of vnum they possess */
  {"wearing", L_wearing},            /* do they have something equipped at this location? */
  {"wearingvnum", L_wearingvnum},    /* is vnum equipped? */
  {"fighting", L_fighting},
  
   {NULL, NULL}
  };  /* end of mudlib */
     

static int RegisterLuaRoutines (lua_State *L)
  {

  lua_newtable (L);  /* environment */
  lua_replace (L, LUA_ENVIRONINDEX);

  /* this makes environment variable "character.state" by the pointer to our character */
  lua_settable(L, LUA_ENVIRONINDEX);

  /* register all mud.xxx routines */
  luaL_register (L, MUD_LIBRARY, mudlib);
  
  /* using interpret now
  RegisterLuaCommands (L);
  */

  luaopen_bits (L);     /* bit manipulation */

  return 0;
  
}  /* end of RegisterLuaRoutines */

void open_lua  ( CHAR_DATA * ch)
  {
  lua_State *L = luaL_newstate ();   /* opens Lua */
  ch->L = L;
  
  if (ch->L == NULL)
    {
    bug ("Cannot open Lua state");
    return;  /* catastrophic failure */
    }

  luaL_openlibs (L);    /* open all standard libraries */

  /* call as Lua function because we need the environment  */
  lua_pushcfunction(L, RegisterLuaRoutines);
  lua_pushstring(L, CHARACTER_STATE);  /* push address */
  lua_pushlightuserdata(L, (void *)ch);    /* push value */
  lua_call(L, 2, 0);
 
  /* run initialiation script */
  if (luaL_loadfile (L, LUA_STARTUP) ||
      CallLuaWithTraceBack (L, 0, 0))
      {
      bug ( "Error loading Lua startup file:\n %s", 
              lua_tostring(L, -1));
      }

  lua_settop (L, 0);    /* get rid of stuff lying around */
        
  }  /* end of open_lua */

void close_lua ( CHAR_DATA * ch)
  {
 
  if (ch->L == NULL)
   return;  /* never opened */

  lua_close (ch->L);
  ch->L = NULL;
   
  }/* end of close_lua */

static lua_State * find_lua_function (CHAR_DATA * ch, const char * fname)
  {
  lua_State *L = ch->L;
  
  if (!L)
    return NULL;  /* can't do it */
    
  /* find requested function */
  
  lua_getglobal (L, fname);  
  if (!lua_isfunction (L, -1))
    {
    lua_pop (L, 1);
    bug ("Warning: Lua script function '%s' does not exist", fname);
    return NULL;  /* not there */
    }
      
  return L;  
  }
  
extern char lastplayercmd[MAX_INPUT_LENGTH * 2];
  
static void call_lua_function (CHAR_DATA * ch, 
                                lua_State *L, 
                                const char * fname, 
                                const int nArgs)

{
  
  if (CallLuaWithTraceBack (L, nArgs, 0))
    {
     bug ("Error executing Lua function %s:\n %s\n** Last player command: %s", 
         fname, 
         lua_tostring(L, -1),
         lastplayercmd);
  
    if (!IS_IMMORTAL(ch))
      {
      set_char_color( AT_YELLOW, ch );
      ch_printf (ch, "** A server scripting error occurred, please notify an administrator.\n"); 
      }  /* end of not immortal */
    
    
    lua_pop(L, 1);  /* pop error */
    
    }  /* end of error */
    
}  /* end of call_lua_function */


void call_lua (CHAR_DATA * ch, const char * fname, const char * argument)
  {
    
  int nArgs = 0;
  lua_State *L = find_lua_function (ch, fname);
   
  if (!L || !fname)
    return;
  
  /* if they want to send an argument, push it now */  
  if (argument)
    {
    lua_pushstring(L, argument);  /* push argument, if any */
    nArgs++;
    }
  
  call_lua_function (ch, L, fname, nArgs);
    
  }  /* end of call_lua */

void call_lua_num (CHAR_DATA * ch, const char * fname, const int argument)
  {
  lua_State *L = find_lua_function (ch, fname);
 
  if (!L || !fname)
    return;
       
  lua_pushnumber (L, argument);  
  
  call_lua_function (ch, L, fname, 1);
    
  }  /* end of call_lua_num */

/* call lua function with a victim name and numeric argument */
void call_lua_char_num (CHAR_DATA * ch, 
                        const char * fname, 
                        const char * victim, 
                        const int argument)
  {
  lua_State *L = find_lua_function (ch, fname);
 
  if (!L || !fname || !victim)
    return;
       
  lua_pushstring (L, victim);  /* push victim */
  lua_pushnumber (L, argument);  
  
  call_lua_function (ch, L, fname, 2);
    
  }  /* end of call_lua_char_num */

/* call lua function with a mob vnum and numeric argument */
void call_lua_mob_num (CHAR_DATA * ch, 
                        const char * fname, 
                        const int victim, 
                        const int argument)
  {
  lua_State *L = find_lua_function (ch, fname);
 
  if (!L || !fname || !victim)
    return;
       
  lua_pushnumber (L, victim);  /* push victim */
  lua_pushnumber (L, argument);  
  
  call_lua_function (ch, L, fname, 2);
    
  }  /* end of call_lua_mob_num */
