LE5U6CTXEIETQN5GOVYF2K2VCISRXR3ULORXDKIKWYDVBG5GS3WAC
RPP2JHUJDTD7XFHCUPPRE7FX6JZBSTJ5IOBW37LN53IMGCO6DU3AC
PJXVVRYJ3TBADNHW4ULXKDO5YDVT3T2M6MSXPNZGA6KXQITBHINAC
WEPYKUZS5NJG5NHBVK5IKE55VD3EOK42JUSURMBTU4MG4SBM5ILQC
KDIKQLVZZKYHRBJFCSVMXNU3J56KVSK4RWMICAXTHHEHKB3OWRMAC
G7DNYFW745Q567EF3TPR2FCQ4ATPN236ON7X5TLYC7TEPZW3BAFAC
ZSGD4RAGTGAXYJCRPTUUZYDDLWEIHIHKQ6RBSRPMS7TZC2JMPIAAC
YFJLINBBEHE7RBETTARSYNWSO6QJ6MSPXFMSPSH2742QFC3L55SQC
UL7XFKMUX3WIU4O2LZANK4ECJ654UZPDBFGNXUEYZYOLKBYBCG6AC
T7CCGLOZ25B7BQKKGR6IA6LWBRKUWTXLTIRXUQ4YKQRVAA7AHZKQC
W52PCSHX72WAMWKG6L4BPUBVMO6E72KYYBNKAA7554KNOTY6V7WQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
7NROUYQFX5ZRHHNGZ5DMVBNQP6H3LWDRVP5WIGG6YS6LDKOJXR3AC
7Y5HSDFKA5TPLS2TWTRFMQVX6UXUDHXU5MUMXQSDFAIY4THQ3BIQC
GQL5SIGBHLU3FMCE54XVGLRY5AZHRM6DUEB722REA2DPLGJSN6EQC
EJ4GIPFSSQCQASUMRF4CR2WPUQOTEHFRGLOYEZ7BH6YEMIR6DN4QC
QEEJFAETO6B2J4IWDIDCJ5UNIFNNHHG22IWF2CUJRTJJBNE47CWQC
MQ62OAMLGJVRW2QIL4PAZRAU6PC52ZVGY2FCOBIY6IWGQIHMU5CAC
2EBEWIV4YHXXAFR4GG2GMZJ2K77NK762HNQ77CZLHI3LDVGX7RJAC
ACZYEIX7WMPIIODKCATBCUE626AJ4ZGGBOMVC6BGXM27EQU2RECAC
AUXHSGS4EFOPZ6TVZYWNVOUDO7NYKUKE3HBKGQQWTALSVFOE3HAAC
AXAM3TX4CUCIPL6ILEJLGJ4D4SMCJYDXGLIB256PBQXXKBIKZ4QAC
HACFCSUNUI74MDKMM4YOQ3JYK2MIEAC4RULIRN2GVRBQEFED4WYQC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
NVSFIV2ZKP44XHCSCXG6OZVGL67OIFINC34J2EMKTA4KULCERUEAC
MT3256427VMCV4JUFWCN7ULY4KXSND5ZL5THDKYNWWYOXXR5DLIQC
RN242L3YZK35BFY7JRTCSYWD6FWDCRFUFDI7PKYFUCA2UPX6ZEAQC
ED62QWGKBPORWVKDFOQRKJXEIWZVNGR3O4KWQBDSRNPT36AYOQYAC
ZLQAAP55CJ77XIJN3DZVPT4GTTVLIBFJLIJJKI6L5UBSHX7VUK6AC
LJPUO3NNFABVOQOBA5LUDNT2R5H4LMP6TPJBNVJ7VJBERCBYZIXQC
JTTHP2BEYEPBQMSDM7IKANTMKRPY6ACGL2JN4D3OBZ7HFXKAYEGQC
R55VDFKFAQM32BY36VJBAAJH2C5WJNNYGAXLAH5VEI4Y6NYRLFDQC
C25ULNTKMNOXT72ERWSFQZQW5E2OIM3TOGAAA33Z7EZQMZHRS2MAC
UBQTNLYGD3SNWMUNGWUPX7EXEGQXOXCFCPWIVWBFE7ID7DJLPFWAC
CPBVQFT7NWEYLYD34D5VYZIEOZ32YADXYTSFMS635YNXX4OFIZVAC
RGHXFBNIULRVRYLBGG5JZDMYVM2E2JJ2Y5KQPMU6PUS3V26G6ZXQC
TLA5UN6LZPXGKERI27EFY4HKIIU3VU5Y7ZU54WXL6ANBUV2VOTMQC
SM6YRPYZS6LMDQA6X3VAOK2PGMUFKPD7JMWJISOQSMX2CBR4ISPAC
OYTCBRC7LE44EUVRZVYTOOVKQWJ6P6YE3FXTOGUTNKEMLNWPHKSQC
B3SRWSFITQMJRVEBHGQQJARETYPSSDV6XKMQSSUTXEHTXRZKIQJQC
WF2DSJGR6PKLGQSXEFF4ZW4EZZFGMHXPXWUYAKYBPFJH6KJKAANQC
ASLW3Z5PAVZSWJEMMMVZT226P44EKSAD47QS72JIFJESAI3RPN3AC
NCDWWDJQLAU5ORSAQGZKJJ5E22VTDGGPJMVVBWQFHQ2B3U3UFHDQC
XALTSNKCNAFBIEBXUD5KFMCTYH65XHKY436W5OR5JNBAKEEUZVAAC
EIZC76IFTDKBNDJOX6YPFBOAOVAY6RSXH3JUWFLLGCZ3PYJMW3GQC
C22455VGUQOSUX2OORA32LROFQ7NNYDMD2ZDTTUZSAQLXK4AD6QAC
5TKGFHC6X2JYR3MVN5T2XU2VWG45V7HGEI45H3BZ4B55WPULZEFAC
X7MFMKQTNZ2IWBFVGS6WQV7NRNKJ3DWQAW2X7IQMFQQXW24AHPZQC
TV7L2BAXA5U3X7EXFQMXSBNEMXJ3TVU5EWLFWEXFEWJ4MZA6ZY6AC
55PFDYPVE6JVGDYPCFUE4XS2523PVSV4CSIFRW6A2GGX4I6VWRWQC
GURIAIJVY6LEADNBTZF3SFBLODYJHWD6OGWFMUGEDI4L5KYO7UFAC
FQ7XALBL6NZAGBKKYIFHYQ7MUWIHR4TEU4TCS33EG4I3JTND5SQAC
77MES6I23O3FEVX45F5L7LGABFIDYZA2CLESQBXALSVIHS5E2G6AC
IVVTHLTTLOP5TSULXJWUSSXHOKYWVU3OWKYVK45A7RIB6V34MYQAC
NKONHW4JNY6HP2M63MNPM3H64ZWSUNUT5FX2STW4KTS4AMXJXXVQC
UU5EKED2RA2U3CFZ3UEJQEWSWHQPEU7ZD4KH3I22IIVZFHD4Y67QC
EERRG6FVPJDZD6UNX2W4UXG6HU7XAU7TDNDHMRCXTA4NN4VHFSAAC
3AKDLI4CTGZ2MUXGKJ5FRCBW75JLTGMR74JUHGRDCH4MPMMFGYOAC
KF5BNN42PTO35TBQD5CNMDXFAXHYCSQTBOOXZV7I3ML5VASUUT5AC
S4EQ7PCNVVEICLN73WSLN2MMW6AJY34AUNFT2D27C4YBVPDRREAQC
M6VWYDIBVTO66B73TOWZ3OPT5XNE4UUWKKRF3BMW4OQED7Y6O3AAC
Z6Q7JVMFQ32SC7FRGOB7CE7JS2HEOPAO3B2VLU3YR3UCUDZFIPFQC
AFE345BJ7IX2YYYX3I5I6RYLXNWJCFE4WMH6F5JMIR6X7WUP75CAC
AVCMVFA3MKCXHO6H44UK5KJNIHTGQV7UA7GYXM26VI6TXXU5ZN6QC
45EMD3KLQPMERNMIKU5G76H6556XOMIW352TSBP7VLWJX2YYGS7AC
TLO257LZSB6ZO36STDUEWJBO2LETXFKTFGXELA6Y4BZBVAEIIINAC
DKISJVKAJJK2SVZSXGJ37K5SOVTDARVX6RPK3MAKVNGC36Z4YAZQC
BW3XFNOS6LDAQLHOZ6RXARCMKCY5JVLVDSXDSSAX4DSYM3FANQBAC
NOUFW6ACJ7KPVHSPF4WLRAVOWI7PZ6OAVYDRMI3WYNPXVMGAB3QAC
6LJ7HNA6M2RVACJM4OASJPQAQ42UZGCXW636AVJ4CPICGQZBKLWQC
23I5KJ245D43B6QYWS4TDQYJO2W2PKTDCLFBWPVAFDWS6L7NMGKQC
TGJZXTUIAKCFZQJ54ZQEBGFBVZSJCAX6AWDRSH3TP7UJRLGUM5SAC
44YRMW5JNVUBYX3M6UFQOPENNHGO7G2CRZ7SGPIIUTQGYWTRWENAC
7IERR3KESLBDHEMGSFRYGA66PZBPGKPUGMFDCSFR3WDX7RXD5OTAC
JPZE44DYTOYGNM2TQFLW33QDX4IOCZYV4B6WZU66TQFAYEXQQD7QC
S34LKQDIQJLIWVIPASOJBBZ6ZCXDHP5KPS7TRBZJSCDRVNCLK6UAC
LDBTCT5WIPLJPZWXS2RUQ26QKISCUUTLO77M464WOE6VSYSNPKYAC
JCBEITRVUXBYKKJCDNMZPIKSOLBH5ZHIQBXTLSUFP2WBQNEHEVLQC
3DQXSE4YGFBBDUWK4YEOFWW4UPWILWELFSLP37SL6BERGAZJC5YAC
SIDH2P7NBIG5KEOE27XHD3ZT2NQ2OJZFN6VZXWNWYFFY5YVXSSVQC
PRG7UT7G56GT4W3FQ3KG5JRPGMKKJBFDLVHDLYFQK6IZW25JQLBQC
FL42U7W4FLFPPSBTLDNPCIRFJG6SU2X3GWWQBQPPE2EHZ72WOSOQC
G277QSURADDFZIIO5PIMBA4YWHYULQCEGNMNI6TLBWZXHQOOP6FAC
OMAUFQNBWGX4FDABHQCVPGDYRVKMDASGQJVRH7AOPPEMHAP2LQSQC
4N5PW5S3OV25HFN634NNWMMYX26NA2TB6TVFG4UMYSZ2VBJWKE4QC
L2PY64KUK7XYXNWKTC6YUN5DZYDNHNQFQHICUVNRGRHBHZAXWH3AC
ZWFOOUUA5VCYOXGAHE76DXW65ME4AH2DVN6JVNGZAKTHZNDTVOJAC
CB7U6IEEN2Z74672EYQQHWTB4GXVNNMAYDRQYAGY6K3QSDYYEHYAC
CI2RMLJLIAZMEGNN6LJN6PSHXHLPG7PXFIDYRGFPVMDPJ2R4S4NQC
IHV7JHD4E67NEGLZEO3FPQGJPJF3IAV6QV5A63FPG4SU2VRFV47QC
FKRLQJYHK3GB6HHSNNFQ7L57ZAR52LSTBG5B6SNJBYZ2LQ36P67AC
N22Z7L4XI5LX7UVPSONQNHHPPZF3GLDHGUFDJU265JQYZ3JZEZTQC
UXAYLVZCSKTOC23AZORWJBQWYXKWC6QD2FPV2DRG7G6J5S7U2QLAC
QAXFREFPBXN7YIBORITM2TAGA4QYF43XMSCLWW2BHONZFGEGMRUQC
2ZZD6EYMSPVCXZTICL4VGYGGQRRWDLZ24KBCDBVIYC54OZ4C6GGAC
JT672SIJK4BOIUAGL2WQ6NR2NF4PSWP3BT6Q4HMNRF25UN6JQ2MAC
JUCA26S3JSQFMZTBL7566ITHCOXXEFTFT7KDOWSUXA5JLZK5QJWAC
KSM4H3SBM6FIQTUEGHXXYATJXEOJ4EKUBAFCRMFKSHY7N2HWECRQC
5IB2YSN5CPEWGGKAWRH52576TKVRJKMNAJT2QP6NGNRZRV5B2MDQC
52J7CYVAW3QCUEWA5OKWPDGOP6JZR5NJSE3JDLZFBCR7B6LH5ASAC
LUH6GAJODIQXNPBEHMSUTKH3KA3DYVRCKZ6GJJ4HHNHOIMMUEP6QC
TQXNC2YQVJZXBPSLDEANMM2EDGHTH4DSN74OJIRMUF7D46SESHZAC
OFTOEJSRSA2EZOAQOJCWWQNDX7IVVAR565JK3DZCBG54TBHHCIDQC
HWTL2S7OVOJRJAAN66ZTT26HJDAOIW6RKAG5NFMNZJU63CTU2U5AC
JDPJS5SNW6ZTY5DYV5QUBG2WVJ45FJ4CAOK6KDJKPEI2HLIHVLRQC
FAGA7XVY7FB5VEMJZ625GU5CPQMTY4KTX2NCIXQWFPH7ZQYJSAFAC
Q2NUCKXB4P7DHRXY764F5AMEJO436GV74AVVVOAVGNWKLMSVMDNQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
#include "describe.h"
#include "directn.h"
#include "dungeon.h"
#include "files.h"
#include "hiscores.h"
#include "l_los.h"
#include "los.h"
#include "luadgn.h"
#include "mapdef.h"
static mons_list _lua_get_mlist(lua_State *ls, int ndx);
static item_list _lua_get_ilist(lua_State *ls, int ndx);
template <class T>
static void _push_object_type(lua_State *ls, const char *meta, const T &data)
{
T **ptr = clua_new_userdata<T*>(ls, meta);
*ptr = new T(data);
}
template <typename list, typename lpush>
static int dlua_gentable(lua_State *ls, const list &strings, lpush push)
static mons_list _lua_get_mlist(lua_State *ls, int ndx)
return (1);
}
inline static void dlua_pushcxxstring(lua_State *ls, const std::string &s)
{
lua_pushstring(ls, s.c_str());
}
int dlua_stringtable(lua_State *ls, const std::vector<std::string> &s)
{
return dlua_gentable(ls, s, dlua_pushcxxstring);
}
static int dlua_compiled_chunk_writer(lua_State *ls, const void *p,
size_t sz, void *ud)
{
std::ostringstream &out = *static_cast<std::ostringstream*>(ud);
out.write((const char *) p, sz);
return (0);
}
///////////////////////////////////////////////////////////////////////////
// dlua_chunk
dlua_chunk::dlua_chunk(const std::string &_context)
: file(), chunk(), compiled(), context(_context), first(-1),
last(-1), error()
{
clear();
}
// Initialises a chunk from the function on the top of stack.
// This function must not be a closure, i.e. must not have any upvalues.
dlua_chunk::dlua_chunk(lua_State *ls)
: file(), chunk(), compiled(), context(), first(-1), last(-1), error()
{
clear();
lua_stack_cleaner cln(ls);
std::ostringstream out;
const int err = lua_dump(ls, dlua_compiled_chunk_writer, &out);
if (err)
{
const char *e = lua_tostring(ls, -1);
error = e? e : "Unknown error compiling chunk";
}
compiled = out.str();
}
dlua_chunk dlua_chunk::precompiled(const std::string &chunk)
{
dlua_chunk dchunk;
dchunk.compiled = chunk;
return (dchunk);
}
void dlua_chunk::write(writer& outf) const
{
if (empty())
{
marshallByte(outf, CT_EMPTY);
return;
}
if (!compiled.empty())
{
marshallByte(outf, CT_COMPILED);
marshallString4(outf, compiled);
}
marshallByte(outf, CT_SOURCE);
marshallString4(outf, chunk);
}
marshallString4(outf, file);
marshallLong(outf, first);
}
mons_list **mlist =
clua_get_userdata<mons_list*>(ls, MONSLIST_METATABLE, ndx);
if (mlist)
return (**mlist);
void dlua_chunk::read(reader& inf)
{
clear();
chunk_t type = static_cast<chunk_t>(unmarshallByte(inf));
switch (type)
{
case CT_EMPTY:
return;
case CT_SOURCE:
unmarshallString4(inf, chunk);
break;
case CT_COMPILED:
unmarshallString4(inf, compiled);
break;
luaL_argerror(ls, ndx, "Expected monster list object or string");
return mons_list();
unmarshallString4(inf, file);
first = unmarshallLong(inf);
}
void dlua_chunk::clear()
{
file.clear();
chunk.clear();
first = last = -1;
error.clear();
compiled.clear();
}
void dlua_chunk::set_file(const std::string &s)
{
file = s;
}
void dlua_chunk::add(int line, const std::string &s)
{
if (first == -1)
first = line;
if (line != last && last != -1)
while (last++ < line)
chunk += '\n';
chunk += " ";
chunk += s;
last = line;
}
void dlua_chunk::set_chunk(const std::string &s)
{
chunk = s;
}
int dlua_chunk::check_op(CLua &interp, int err)
{
error = interp.error;
return (err);
int err = check_op( interp,
interp.loadstring(chunk.c_str(), context.c_str()) );
if (err)
return (err);
std::ostringstream out;
err = lua_dump(interp, dlua_compiled_chunk_writer, &out);
if (err)
{
const char *e = lua_tostring(interp, -1);
error = e? e : "Unknown error compiling chunk";
lua_pop(interp, 2);
item_list ilist;
const std::string err = ilist.add_item(spec);
if (!err.empty())
luaL_error(ls, err.c_str());
return (ilist);
compiled = out.str();
chunk.clear();
return (err);
}
int dlua_chunk::run(CLua &interp)
{
int err = load(interp);
if (err)
return (err);
// callfn returns true on success, but we want to return 0 on success.
return (check_op(interp, !interp.callfn(NULL, 0, 0)));
}
int dlua_chunk::load_call(CLua &interp, const char *fn)
{
int err = load(interp);
if (err == -1000)
return (0);
if (err)
return (err);
return check_op(interp, !interp.callfn(fn, fn? 1 : 0, 0));
}
std::string dlua_chunk::orig_error() const
{
rewrite_chunk_errors(error);
return (error);
}
bool dlua_chunk::empty() const
{
return compiled.empty() && trimmed_string(chunk).empty();
}
bool dlua_chunk::rewrite_chunk_errors(std::string &s) const
{
const std::string contextm = "[string \"" + context + "\"]:";
std::string::size_type dlwhere = s.find(contextm);
if (dlwhere == std::string::npos)
return (false);
if (!dlwhere)
else
// Our chunk is mentioned, go back through and rewrite lines.
std::vector<std::string> lines = split_string("\n", s);
std::string newmsg = lines[0];
bool wrote_prefix = false;
for (int i = 2, size = lines.size() - 1; i < size; ++i)
{
const std::string &st = lines[i];
if (st.find(context) != std::string::npos)
{
if (!wrote_prefix)
{
newmsg = get_chunk_prefix(st) + ": " + newmsg;
wrote_prefix = true;
}
else
newmsg += "\n" + rewrite_chunk_prefix(st);
}
luaL_argerror(ls, ndx, "Expected item list object or string");
return item_list();
std::string s = line;
const std::string contextm = "[string \"" + context + "\"]:";
const std::string::size_type ps = s.find(contextm);
if (ps == std::string::npos)
return (s);
const std::string::size_type lns = ps + contextm.length();
std::string::size_type pe = s.find(':', ps + contextm.length());
if (pe != std::string::npos)
{
const std::string line_num = s.substr(lns, pe - lns);
const int lnum = atoi(line_num.c_str());
const std::string newlnum = make_stringf("%d", lnum + first - 1);
s = s.substr(0, lns) + newlnum + s.substr(pe);
pe = lns + newlnum.length();
}
return s.substr(0, ps) + (file.empty()? context : file) + ":"
+ (skip_body? s.substr(lns, pe - lns)
: s.substr(lns));
clua_register_metatable(ls, MONSLIST_METATABLE, NULL,
lua_object_gc<mons_list>);
clua_register_metatable(ls, ITEMLIST_METATABLE, NULL,
lua_object_gc<item_list>);
#define MAP(ls, n, var) \
map_def *var = *(map_def **) luaL_checkudata(ls, n, MAP_METATABLE)
#define DEVENT(ls, n, var) \
dgn_event *var = *(dgn_event **) luaL_checkudata(ls, n, DEVENT_METATABLE)
#define MAPMARKER(ls, n, var) \
map_marker *var = *(map_marker **) luaL_checkudata(ls, n, MAPMARK_METATABLE)
"unseen", "closed_door", "detected_secret_door", "secret_door",
"wax_wall", "metal_wall", "green_crystal_wall", "rock_wall", "stone_wall",
"permarock_wall",
"clear_rock_wall", "clear_stone_wall", "clear_permarock_wall", "trees",
"open_sea", "orcish_idol", "", "", "", "", "",
"granite_statue", "statue_reserved_1", "statue_reserved_2",
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "lava",
"deep_water", "", "", "shallow_water", "water_stuck", "floor",
"floor_special", "floor_reserved", "exit_hell", "enter_hell",
"open_door", "", "", "trap_mechanical", "trap_magical", "trap_natural",
"undiscovered_trap", "", "enter_shop", "enter_labyrinth",
"stone_stairs_down_i", "stone_stairs_down_ii",
"stone_stairs_down_iii", "escape_hatch_down", "stone_stairs_up_i",
"stone_stairs_up_ii", "stone_stairs_up_iii", "escape_hatch_up", "",
"", "enter_dis", "enter_gehenna", "enter_cocytus",
"enter_tartarus", "enter_abyss", "exit_abyss", "stone_arch",
"enter_pandemonium", "exit_pandemonium", "transit_pandemonium",
"", "", "", "builder_special_wall", "builder_special_floor", "",
"", "", "enter_orcish_mines", "enter_hive", "enter_lair",
"enter_slime_pits", "enter_vaults", "enter_crypt",
"enter_hall_of_blades", "enter_zot", "enter_temple",
"enter_snake_pit", "enter_elven_halls", "enter_tomb",
"enter_swamp", "enter_shoals", "enter_reserved_2",
"enter_reserved_3", "enter_reserved_4", "", "", "",
"return_from_orcish_mines", "return_from_hive",
"return_from_lair", "return_from_slime_pits",
"return_from_vaults", "return_from_crypt",
"return_from_hall_of_blades", "return_from_zot",
"return_from_temple", "return_from_snake_pit",
"return_from_elven_halls", "return_from_tomb",
"return_from_swamp", "return_from_shoals", "return_reserved_2",
"return_reserved_3", "return_reserved_4", "", "", "", "", "", "",
"", "", "", "", "", "", "", "enter_portal_vault", "exit_portal_vault",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "altar_zin", "altar_shining_one", "altar_kikubaaqudgha",
"altar_yredelemnul", "altar_xom", "altar_vehumet",
"altar_okawaru", "altar_makhleb", "altar_sif_muna", "altar_trog",
"altar_nemelex_xobeh", "altar_elyvilon", "altar_lugonu",
"altar_beogh", "altar_jiyva", "altar_feawn", "", "", "", "",
"fountain_blue", "fountain_sparkling", "fountain_blood",
"dry_fountain_blue", "dry_fountain_sparkling", "dry_fountain_blood",
"permadry_fountain", "abandoned_shop"
"unseen", "closed_door", "detected_secret_door", "secret_door",
"wax_wall", "metal_wall", "green_crystal_wall", "rock_wall", "stone_wall",
"permarock_wall",
"clear_rock_wall", "clear_stone_wall", "clear_permarock_wall", "trees",
"open_sea", "orcish_idol", "", "", "", "", "",
"granite_statue", "statue_reserved_1", "statue_reserved_2",
"", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "", "", "", "", "", "", "", "", "", "", "", "lava",
"deep_water", "", "", "shallow_water", "water_stuck", "floor",
"floor_special", "floor_reserved", "exit_hell", "enter_hell",
"open_door", "", "", "trap_mechanical", "trap_magical", "trap_natural",
"undiscovered_trap", "", "enter_shop", "enter_labyrinth",
"stone_stairs_down_i", "stone_stairs_down_ii",
"stone_stairs_down_iii", "escape_hatch_down", "stone_stairs_up_i",
"stone_stairs_up_ii", "stone_stairs_up_iii", "escape_hatch_up", "",
"", "enter_dis", "enter_gehenna", "enter_cocytus",
"enter_tartarus", "enter_abyss", "exit_abyss", "stone_arch",
"enter_pandemonium", "exit_pandemonium", "transit_pandemonium",
"", "", "", "builder_special_wall", "builder_special_floor", "",
"", "", "enter_orcish_mines", "enter_hive", "enter_lair",
"enter_slime_pits", "enter_vaults", "enter_crypt",
"enter_hall_of_blades", "enter_zot", "enter_temple",
"enter_snake_pit", "enter_elven_halls", "enter_tomb",
"enter_swamp", "enter_shoals", "enter_reserved_2",
"enter_reserved_3", "enter_reserved_4", "", "", "",
"return_from_orcish_mines", "return_from_hive",
"return_from_lair", "return_from_slime_pits",
"return_from_vaults", "return_from_crypt",
"return_from_hall_of_blades", "return_from_zot",
"return_from_temple", "return_from_snake_pit",
"return_from_elven_halls", "return_from_tomb",
"return_from_swamp", "return_from_shoals", "return_reserved_2",
"return_reserved_3", "return_reserved_4", "", "", "", "", "", "",
"", "", "", "", "", "", "", "enter_portal_vault", "exit_portal_vault",
"", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "",
"", "", "altar_zin", "altar_shining_one", "altar_kikubaaqudgha",
"altar_yredelemnul", "altar_xom", "altar_vehumet",
"altar_okawaru", "altar_makhleb", "altar_sif_muna", "altar_trog",
"altar_nemelex_xobeh", "altar_elyvilon", "altar_lugonu",
"altar_beogh", "altar_jiyva", "altar_feawn", "", "", "", "",
"fountain_blue", "fountain_sparkling", "fountain_blood",
"dry_fountain_blue", "dry_fountain_sparkling", "dry_fountain_blood",
"permadry_fountain", "abandoned_shop"
"none", "turn", "mons_move", "player_move", "leave_level",
"entering_level", "entered_level", "player_los", "player_climb",
"monster_dies", "item_pickup", "item_moved", "feat_change",
"wall_hit"
"none", "turn", "mons_move", "player_move", "leave_level",
"entering_level", "entered_level", "player_los", "player_climb",
"monster_dies", "item_pickup", "item_moved", "feat_change",
"wall_hit"
lua_isnumber(ls, 2)?
static_cast<description_level_type>(luaL_checkint(ls, 2)) :
description_type_by_name(lua_tostring(ls, 2));
lua_isnumber(ls, 2)?
static_cast<description_level_type>(luaL_checkint(ls, 2)) :
description_type_by_name(lua_tostring(ls, 2));
lua_isnumber(ls, 3)?
static_cast<description_level_type>(luaL_checkint(ls, 3)) :
description_type_by_name(lua_tostring(ls, 3));
lua_isnumber(ls, 3)?
static_cast<description_level_type>(luaL_checkint(ls, 3)) :
description_type_by_name(lua_tostring(ls, 3));
for (int j = -1; j <= 1; j++)
{
if (!map_bounds(x+i, y+j))
continue;
const dungeon_feature_type feat2 = grd[x+i][y+j];
if (feat2 == DNGN_FLOOR
|| feat2 == DNGN_UNDISCOVERED_TRAP)
{
env.grid_colours[x+i][y+j] = colour;
}
}
for (int j = -1; j <= 1; j++)
{
if (!map_bounds(x+i, y+j))
continue;
const dungeon_feature_type feat2 = grd[x+i][y+j];
if (feat2 == DNGN_FLOOR
|| feat2 == DNGN_UNDISCOVERED_TRAP)
{
env.grid_colours[x+i][y+j] = colour;
}
}
const char *branch_name = luaL_checkstring(ls, pos); \
branch_type req_branch_type = str_to_branch(branch_name); \
if (req_branch_type == NUM_BRANCHES) \
luaL_error(ls, "Expected branch name"); \
Branch &br = branches[req_branch_type]
const char *branch_name = luaL_checkstring(ls, pos); \
branch_type req_branch_type = str_to_branch(branch_name); \
if (req_branch_type == NUM_BRANCHES) \
luaL_error(ls, "Expected branch name"); \
Branch &br = branches[req_branch_type]
const char *level_name = luaL_checkstring(ls, pos); \
level_area_type lev = str_to_level_area_type(level_name); \
if (lev == NUM_LEVEL_AREA_TYPES) \
luaL_error(ls, "Expected level name"); \
const char *branch_name = luaL_checkstring(ls, pos); \
branch_type br = str_to_branch(branch_name); \
if (lev == LEVEL_DUNGEON && br == NUM_BRANCHES) \
luaL_error(ls, "Expected branch name");
const char *level_name = luaL_checkstring(ls, pos); \
level_area_type lev = str_to_level_area_type(level_name); \
if (lev == NUM_LEVEL_AREA_TYPES) \
luaL_error(ls, "Expected level name"); \
const char *branch_name = luaL_checkstring(ls, pos); \
branch_type br = str_to_branch(branch_name); \
if (lev == LEVEL_DUNGEON && br == NUM_BRANCHES) \
luaL_error(ls, "Expected branch name");
if (!lua_special_room_spec.created || !in_bounds(lua_special_room_spec.tl)
|| lua_special_room_level == -1)
{
return (0);
}
lua_pushnumber(ls, lua_special_room_level);
dlua_push_coord(ls, lua_special_room_spec.tl);
dlua_push_coord(ls, lua_special_room_spec.br);
return (5);
if (!lua_special_room_spec.created || !in_bounds(lua_special_room_spec.tl)
|| lua_special_room_level == -1)
{
return (0);
}
lua_pushnumber(ls, lua_special_room_level);
dlua_push_coord(ls, lua_special_room_spec.tl);
dlua_push_coord(ls, lua_special_room_spec.br);
return (5);
static const struct luaL_reg dgn_lib[] =
{
{ "dbg_goto_place", dgn_dbg_goto_place },
{ "dbg_flush_map_memory", dgn_dbg_flush_map_memory },
{ "dbg_generate_level", dgn_dbg_generate_level },
{ "dbg_dump_map", dgn_dbg_dump_map },
{ "dbg_test_explore", dgn_dbg_test_explore },
{ "reset_level", _dgn_reset_level },
{ "dismiss_monsters", dgn_dismiss_monsters },
{ "default_depth", dgn_default_depth },
{ "name", dgn_name },
{ "depth", dgn_depth },
{ "place", dgn_place },
{ "tags", dgn_tags },
{ "tags_remove", dgn_tags_remove },
{ "lflags", dgn_lflags },
{ "bflags", dgn_bflags },
{ "chance", dgn_chance },
{ "weight", dgn_weight },
{ "welcome", dgn_welcome },
{ "orient", dgn_orient },
{ "shuffle", dgn_shuffle },
{ "subst", dgn_subst },
{ "nsubst", dgn_nsubst },
{ "colour", dgn_colour },
{ "lfloorcol", dgn_lfloorcol},
{ "lrockcol", dgn_lrockcol},
{ "normalise", dgn_normalise },
{ "map", dgn_map },
{ "mons", dgn_mons },
{ "item", dgn_item },
{ "marker", dgn_marker },
{ "lua_marker", dgn_lua_marker },
{ "kfeat", dgn_kfeat },
{ "kitem", dgn_kitem },
{ "kmons", dgn_kmons },
{ "kmask", dgn_kmask },
{ "mapsize", dgn_map_size },
{ "grid", dgn_grid },
{ "is_wall", _dgn_is_wall },
{ "max_bounds", dgn_max_bounds },
{ "colour_at", dgn_colour_at },
{ "terrain_changed", dgn_terrain_changed },
{ "points_connected", dgn_points_connected },
{ "any_point_connected", dgn_any_point_connected },
{ "has_exit_from", dgn_has_exit_from },
{ "gly_point", dgn_gly_point },
{ "gly_points", dgn_gly_points },
{ "original_map", dgn_original_map },
{ "load_des_file", dgn_load_des_file },
{ "feature_number", dgn_feature_number },
{ "feature_name", dgn_feature_name },
{ "dgn_event_type", dgn_dgn_event },
{ "register_listener", dgn_register_listener },
{ "remove_listener", dgn_remove_listener },
{ "remove_marker", dgn_remove_marker },
{ "num_matching_markers", dgn_num_matching_markers },
{ "feature_desc", dgn_feature_desc },
{ "feature_desc_at", dgn_feature_desc_at },
{ "set_feature_desc_short", dgn_set_feature_desc_short },
{ "set_feature_desc_long", dgn_set_feature_desc_long },
{ "item_from_index", dgn_item_from_index },
{ "mons_from_index", dgn_mons_from_index },
{ "mons_at", dgn_mons_at },
{ "items_at", dgn_items_at },
{ "change_level_flags", dgn_change_level_flags },
{ "change_branch_flags", dgn_change_branch_flags },
{ "set_random_mon_list", dgn_set_random_mon_list },
{ "get_floor_colour", dgn_get_floor_colour },
{ "get_rock_colour", dgn_get_rock_colour },
{ "change_floor_colour", dgn_change_floor_colour },
{ "change_rock_colour", dgn_change_rock_colour },
{ "set_lt_callback", lua_dgn_set_lt_callback },
{ "fixup_stairs", dgn_fixup_stairs },
{ "floor_halo", dgn_floor_halo },
{ "random_walk", dgn_random_walk },
{ "apply_area_cloud", dgn_apply_area_cloud },
// building routines
{ "grd_table", dgn_grd_table },
{ "width", dgn_width },
{ "height", dgn_height },
{ "fill_area", dgn_fill_area },
{ "replace_area", dgn_replace_area },
{ "octa_room", dgn_octa_room },
{ "make_pillars", dgn_make_pillars },
{ "make_square", dgn_make_square },
{ "make_rounded_square", dgn_make_rounded_square },
{ "make_circle", dgn_make_circle },
{ "in_bounds", dgn_in_bounds },
{ "replace_first", dgn_replace_first },
{ "replace_random", dgn_replace_random },
{ "spotty_level", dgn_spotty_level },
{ "smear_feature", dgn_smear_feature },
{ "count_feature_in_box", dgn_count_feature_in_box },
{ "count_antifeature_in_box", dgn_count_antifeature_in_box },
{ "count_neighbours", dgn_count_neighbours },
{ "join_the_dots", dgn_join_the_dots },
{ "fill_disconnected_zones", dgn_fill_disconnected_zones },
{ "is_opaque", _dgn_is_opaque },
{ "is_passable", _dgn_is_passable },
{ "register_feature_marker", dgn_register_feature_marker },
{ "register_lua_marker", dgn_register_lua_marker },
{ "create_monster", dgn_create_monster },
{ "create_item", dgn_create_item },
{ "monster_spec", _dgn_monster_spec },
{ "item_spec", _dgn_item_spec },
{ "max_monsters", _dgn_max_monsters },
{ "with_map_bounds_fn", dgn_with_map_bounds_fn },
{ "with_map_anchors", dgn_with_map_anchors },
{ "br_floorcol", dgn_br_floorcol },
{ "br_rockcol", dgn_br_rockcol },
{ "br_has_shops", dgn_br_has_shops },
{ "br_has_uniques", dgn_br_has_uniques },
{ "br_parent_branch", dgn_br_parent_branch },
{ "level_id", dgn_level_id },
{ "level_name", dgn_level_name },
{ "set_level_type_name", dgn_set_level_type_name },
{ "set_level_type_name_abbrev", dgn_set_level_type_name_abbrev },
{ "set_level_type_origin", dgn_set_level_type_origin },
{ "map_by_tag", dgn_map_by_tag },
{ "map_in_depth", dgn_map_in_depth },
{ "map_by_place", dgn_map_by_place },
{ "place_map", _dgn_place_map },
{ "reuse_map", _dgn_reuse_map },
{ "resolve_map", _dgn_resolve_map },
{ "in_vault", _dgn_in_vault },
{ "find_marker_prop", _dgn_find_marker_prop },
{ "get_special_room_info", dgn_get_special_room_info },
{ "lrocktile", dgn_lrocktile },
{ "lfloortile", dgn_lfloortile },
{ "rtile", dgn_rtile },
{ "ftile", dgn_ftile },
{ "change_rock_tile", dgn_change_rock_tile },
{ "change_floor_tile", dgn_change_floor_tile },
{ "lev_floortile", dgn_lev_floortile },
{ "lev_rocktile", dgn_lev_rocktile },
{ NULL, NULL }
};
LUAFN(_crawl_args)
{
return dlua_stringtable(ls, SysEnv.cmd_args);
}
LUAFN(_crawl_milestone)
{
#ifdef DGL_MILESTONES
mark_milestone(luaL_checkstring(ls, 1),
luaL_checkstring(ls, 2));
#endif
return (0);
}
LUAFN(_crawl_redraw_view)
{
viewwindow(true, false);
return (0);
}
#ifdef UNIX
LUAFN(_crawl_millis)
{
struct timeval tv;
struct timezone tz;
const int error = gettimeofday(&tv, &tz);
if (error)
luaL_error(ls, make_stringf("Failed to get time: %s",
strerror(error)).c_str());
lua_pushnumber(ls, tv.tv_sec * 1000 + tv.tv_usec / 1000);
return (1);
}
#endif
static const struct luaL_reg crawl_lib[] =
{
{ "args", _crawl_args },
{ "mark_milestone", _crawl_milestone },
{ "redraw_view", _crawl_redraw_view },
#ifdef UNIX
{ "millis", _crawl_millis },
#endif
{ NULL, NULL }
};
static int file_marshall(lua_State *ls)
{
if (lua_gettop(ls) != 2)
luaL_error(ls, "Need two arguments: tag header and value");
writer &th(*static_cast<writer*>( lua_touserdata(ls, 1) ));
if (lua_isnumber(ls, 2))
marshallLong(th, luaL_checklong(ls, 2));
else if (lua_isboolean(ls, 2))
marshallByte(th, lua_toboolean(ls, 2));
else if (lua_isstring(ls, 2))
marshallString(th, lua_tostring(ls, 2));
else if (lua_isfunction(ls, 2))
{
dlua_chunk chunk(ls);
marshallString(th, chunk.compiled_chunk());
}
return (0);
}
static int file_unmarshall_boolean(lua_State *ls)
{
if (lua_gettop(ls) != 1)
luaL_error(ls, "Need reader as one argument");
reader &th(*static_cast<reader*>( lua_touserdata(ls, 1) ));
lua_pushboolean(ls, unmarshallByte(th));
return (1);
}
static int file_unmarshall_number(lua_State *ls)
{
if (lua_gettop(ls) != 1)
luaL_error(ls, "Need reader as one argument");
reader &th(*static_cast<reader*>( lua_touserdata(ls, 1) ));
lua_pushnumber(ls, unmarshallLong(th));
return (1);
}
static int file_unmarshall_string(lua_State *ls)
{
if (lua_gettop(ls) != 1)
luaL_error(ls, "Need reader as one argument");
reader &th(*static_cast<reader*>( lua_touserdata(ls, 1) ));
lua_pushstring(ls, unmarshallString(th).c_str());
return (1);
}
static int file_unmarshall_fn(lua_State *ls)
{
if (lua_gettop(ls) != 1)
luaL_error(ls, "Need reader as one argument");
reader &th(*static_cast<reader*>( lua_touserdata(ls, 1) ));
const std::string s(unmarshallString(th, LUA_CHUNK_MAX_SIZE));
dlua_chunk chunk = dlua_chunk::precompiled(s);
if (chunk.load(dlua))
lua_pushnil(ls);
return (1);
}
enum lua_persist_type
{
LPT_NONE,
LPT_NUMBER,
LPT_STRING,
LPT_FUNCTION,
LPT_NIL,
LPT_BOOLEAN
};
static int file_marshall_meta(lua_State *ls)
{
if (lua_gettop(ls) != 2)
luaL_error(ls, "Need two arguments: tag header and value");
writer &th(*static_cast<writer*>( lua_touserdata(ls, 1) ));
lua_persist_type ptype = LPT_NONE;
if (lua_isnumber(ls, 2))
ptype = LPT_NUMBER;
else if (lua_isboolean(ls, 2))
ptype = LPT_BOOLEAN;
else if (lua_isstring(ls, 2))
ptype = LPT_STRING;
else if (lua_isfunction(ls, 2))
ptype = LPT_FUNCTION;
else if (lua_isnil(ls, 2))
ptype = LPT_NIL;
else
luaL_error(ls,
make_stringf("Cannot marshall %s",
lua_typename(ls, lua_type(ls, 2))).c_str());
marshallByte(th, ptype);
if (ptype != LPT_NIL)
file_marshall(ls);
return (0);
}
static int file_unmarshall_meta(lua_State *ls)
{
reader &th(*static_cast<reader*>( lua_touserdata(ls, 1) ));
const lua_persist_type ptype =
static_cast<lua_persist_type>(unmarshallByte(th));
switch (ptype)
{
case LPT_BOOLEAN:
return file_unmarshall_boolean(ls);
case LPT_NUMBER:
return file_unmarshall_number(ls);
case LPT_STRING:
return file_unmarshall_string(ls);
case LPT_FUNCTION:
return file_unmarshall_fn(ls);
case LPT_NIL:
lua_pushnil(ls);
return (1);
default:
luaL_error(ls, "Unexpected type signature.");
}
// Never get here.
return (0);
}
static const struct luaL_reg file_lib[] =
{
{ "marshall", file_marshall },
{ "marshall_meta", file_marshall_meta },
{ "unmarshall_meta", file_unmarshall_meta },
{ "unmarshall_number", file_unmarshall_number },
{ "unmarshall_string", file_unmarshall_string },
{ "unmarshall_fn", file_unmarshall_fn },
{ NULL, NULL }
};
LUARET1(you_can_hear_pos, boolean,
player_can_hear(coord_def(luaL_checkint(ls,1), luaL_checkint(ls, 2))))
LUARET1(you_x_pos, number, you.pos().x)
LUARET1(you_y_pos, number, you.pos().y)
LUARET2(you_pos, number, you.pos().x, you.pos().y)
// see_cell should not be exposed to user scripts. The game should
// never disclose grid coordinates to the player. Therefore we load it
// only into the core Lua interpreter (dlua), never into the user
// script interpreter (clua).
LUARET1(you_see_cell, boolean,
see_cell(luaL_checkint(ls, 1), luaL_checkint(ls, 2)))
LUARET1(you_see_cell_no_trans, boolean,
see_cell_no_trans(luaL_checkint(ls, 1), luaL_checkint(ls, 2)))
LUAFN(you_moveto)
{
const coord_def place(luaL_checkint(ls, 1), luaL_checkint(ls, 2));
ASSERT(map_bounds(place));
you.moveto(place);
return (0);
}
LUAFN(you_random_teleport)
{
you_teleport_now(false, false);
return (0);
}
LUAFN(you_losight)
{
calc_show_los();
return (0);
}
static const struct luaL_reg you_lib[] =
{
{ "hear_pos", you_can_hear_pos },
{ "x_pos", you_x_pos },
{ "y_pos", you_y_pos },
{ "pos", you_pos },
{ "moveto", you_moveto },
{ "see_cell", you_see_cell },
{ "see_cell_no_trans", you_see_cell_no_trans },
{ "random_teleport", you_random_teleport },
{ "losight", you_losight },
{ NULL, NULL }
};
static int dgnevent_type(lua_State *ls)
{
DEVENT(ls, 1, dev);
PLUARET(number, dev->type);
}
static int dgnevent_place(lua_State *ls)
{
DEVENT(ls, 1, dev);
lua_pushnumber(ls, dev->place.x);
lua_pushnumber(ls, dev->place.y);
return (2);
}
static int dgnevent_dest(lua_State *ls)
{
DEVENT(ls, 1, dev);
lua_pushnumber(ls, dev->dest.x);
lua_pushnumber(ls, dev->dest.y);
return (2);
}
static int dgnevent_ticks(lua_State *ls)
{
DEVENT(ls, 1, dev);
PLUARET(number, dev->elapsed_ticks);
}
DEVENT(ls, 1, dev);
PLUARET(number, dev->arg1);
}
{ "dbg_goto_place", dgn_dbg_goto_place },
{ "dbg_flush_map_memory", dgn_dbg_flush_map_memory },
{ "dbg_generate_level", dgn_dbg_generate_level },
{ "dbg_dump_map", dgn_dbg_dump_map },
{ "dbg_test_explore", dgn_dbg_test_explore },
static const struct luaL_reg dgnevent_lib[] =
{
{ "type", dgnevent_type },
{ "pos", dgnevent_place },
{ "dest", dgnevent_dest },
{ "ticks", dgnevent_ticks },
{ "arg1", dgnevent_arg1 },
{ "arg2", dgnevent_arg2 },
{ NULL, NULL }
};
{ "default_depth", dgn_default_depth },
{ "name", dgn_name },
{ "depth", dgn_depth },
{ "place", dgn_place },
{ "tags", dgn_tags },
{ "tags_remove", dgn_tags_remove },
{ "lflags", dgn_lflags },
{ "bflags", dgn_bflags },
{ "chance", dgn_chance },
{ "weight", dgn_weight },
{ "welcome", dgn_welcome },
{ "orient", dgn_orient },
{ "shuffle", dgn_shuffle },
{ "subst", dgn_subst },
{ "nsubst", dgn_nsubst },
{ "colour", dgn_colour },
{ "lfloorcol", dgn_lfloorcol},
{ "lrockcol", dgn_lrockcol},
{ "normalise", dgn_normalise },
{ "map", dgn_map },
{ "mons", dgn_mons },
{ "item", dgn_item },
{ "marker", dgn_marker },
{ "lua_marker", dgn_lua_marker },
{ "kfeat", dgn_kfeat },
{ "kitem", dgn_kitem },
{ "kmons", dgn_kmons },
{ "kmask", dgn_kmask },
{ "mapsize", dgn_map_size },
void luaopen_setmeta(lua_State *ls,
const char *global,
const luaL_reg *lua_lib,
const char *meta)
{
luaL_newmetatable(ls, meta);
lua_setglobal(ls, global);
{ "grid", dgn_grid },
{ "is_wall", _dgn_is_wall },
{ "max_bounds", dgn_max_bounds },
{ "colour_at", dgn_colour_at },
luaL_openlib(ls, global, lua_lib, 0);
{ "terrain_changed", dgn_terrain_changed },
{ "points_connected", dgn_points_connected },
{ "any_point_connected", dgn_any_point_connected },
{ "has_exit_from", dgn_has_exit_from },
{ "gly_point", dgn_gly_point },
{ "gly_points", dgn_gly_points },
{ "original_map", dgn_original_map },
{ "load_des_file", dgn_load_des_file },
{ "feature_number", dgn_feature_number },
{ "feature_name", dgn_feature_name },
{ "dgn_event_type", dgn_dgn_event },
{ "register_listener", dgn_register_listener },
{ "remove_listener", dgn_remove_listener },
{ "remove_marker", dgn_remove_marker },
{ "num_matching_markers", dgn_num_matching_markers },
{ "feature_desc", dgn_feature_desc },
{ "feature_desc_at", dgn_feature_desc_at },
{ "set_feature_desc_short", dgn_set_feature_desc_short },
{ "set_feature_desc_long", dgn_set_feature_desc_long },
{ "item_from_index", dgn_item_from_index },
{ "mons_from_index", dgn_mons_from_index },
{ "mons_at", dgn_mons_at },
{ "items_at", dgn_items_at },
{ "change_level_flags", dgn_change_level_flags },
{ "change_branch_flags", dgn_change_branch_flags },
{ "set_random_mon_list", dgn_set_random_mon_list },
{ "get_floor_colour", dgn_get_floor_colour },
{ "get_rock_colour", dgn_get_rock_colour },
{ "change_floor_colour", dgn_change_floor_colour },
{ "change_rock_colour", dgn_change_rock_colour },
{ "set_lt_callback", lua_dgn_set_lt_callback },
{ "fixup_stairs", dgn_fixup_stairs },
{ "floor_halo", dgn_floor_halo },
{ "random_walk", dgn_random_walk },
{ "apply_area_cloud", dgn_apply_area_cloud },
// Do <global>.__index = <global>
lua_pushstring(ls, "__index");
lua_pushvalue(ls, -2);
lua_settable(ls, -3);
}
static void luaopen_dgnevent(lua_State *ls)
{
luaopen_setmeta(ls, "dgnevent", dgnevent_lib, DEVENT_METATABLE);
}
static int mapmarker_pos(lua_State *ls)
{
MAPMARKER(ls, 1, mark);
lua_pushnumber(ls, mark->pos.x);
lua_pushnumber(ls, mark->pos.y);
return (2);
}
// building routines
{ "grd_table", dgn_grd_table },
{ "width", dgn_width },
{ "height", dgn_height },
{ "fill_area", dgn_fill_area },
{ "replace_area", dgn_replace_area },
{ "octa_room", dgn_octa_room },
{ "make_pillars", dgn_make_pillars },
{ "make_square", dgn_make_square },
{ "make_rounded_square", dgn_make_rounded_square },
{ "make_circle", dgn_make_circle },
{ "in_bounds", dgn_in_bounds },
{ "replace_first", dgn_replace_first },
{ "replace_random", dgn_replace_random },
{ "spotty_level", dgn_spotty_level },
{ "smear_feature", dgn_smear_feature },
{ "count_feature_in_box", dgn_count_feature_in_box },
{ "count_antifeature_in_box", dgn_count_antifeature_in_box },
{ "count_neighbours", dgn_count_neighbours },
{ "join_the_dots", dgn_join_the_dots },
{ "fill_disconnected_zones", dgn_fill_disconnected_zones },
static int mapmarker_move(lua_State *ls)
{
MAPMARKER(ls, 1, mark);
const coord_def dest( luaL_checkint(ls, 2), luaL_checkint(ls, 3) );
env.markers.move_marker(mark, dest);
return (0);
}
{ "is_opaque", _dgn_is_opaque },
{ "is_passable", _dgn_is_passable },
static mons_list _lua_get_mlist(lua_State *ls, int ndx)
{
if (lua_isstring(ls, ndx))
{
const char *spec = lua_tostring(ls, ndx);
mons_list mlist;
const std::string err = mlist.add_mons(spec);
if (!err.empty())
luaL_error(ls, err.c_str());
return (mlist);
}
else
{
mons_list **mlist =
clua_get_userdata<mons_list*>(ls, MONSLIST_METATABLE, ndx);
if (mlist)
return (**mlist);
{ "register_feature_marker", dgn_register_feature_marker },
{ "register_lua_marker", dgn_register_lua_marker },
luaL_argerror(ls, ndx, "Expected monster list object or string");
return mons_list();
}
}
{ "create_monster", dgn_create_monster },
{ "create_item", dgn_create_item },
return (ilist);
}
else
{
item_list **ilist =
clua_get_userdata<item_list*>(ls, ITEMLIST_METATABLE, ndx);
if (ilist)
return (**ilist);
{ "with_map_bounds_fn", dgn_with_map_bounds_fn },
{ "with_map_anchors", dgn_with_map_anchors },
luaL_argerror(ls, ndx, "Expected item list object or string");
return item_list();
}
}
{ "br_floorcol", dgn_br_floorcol },
{ "br_rockcol", dgn_br_rockcol },
{ "br_has_shops", dgn_br_has_shops },
{ "br_has_uniques", dgn_br_has_uniques },
{ "br_parent_branch", dgn_br_parent_branch },
static void _register_mapdef_tables(lua_State *ls)
{
clua_register_metatable(ls, MONSLIST_METATABLE, NULL,
lua_object_gc<mons_list>);
clua_register_metatable(ls, ITEMLIST_METATABLE, NULL,
lua_object_gc<item_list>);
}
{ "level_id", dgn_level_id },
{ "level_name", dgn_level_name },
{ "set_level_type_name", dgn_set_level_type_name },
{ "set_level_type_name_abbrev", dgn_set_level_type_name_abbrev },
{ "set_level_type_origin", dgn_set_level_type_origin },
{ "map_by_tag", dgn_map_by_tag },
{ "map_in_depth", dgn_map_in_depth },
{ "map_by_place", dgn_map_by_place },
{ "place_map", _dgn_place_map },
{ "reuse_map", _dgn_reuse_map },
{ "resolve_map", _dgn_resolve_map },
{ "in_vault", _dgn_in_vault },
static const struct luaL_reg mapmarker_lib[] =
{
{ "pos", mapmarker_pos },
{ "move", mapmarker_move },
{ NULL, NULL }
};
{ "find_marker_prop", _dgn_find_marker_prop },
// Return the integer stored in the table (on the stack) with the key name.
// If the key doesn't exist or the value is the wrong type, return defval.
static int _table_int(lua_State *ls, int idx, const char *name, int defval)
{
lua_pushstring(ls, name);
lua_gettable(ls, idx < 0 ? idx - 1 : idx);
bool nil = lua_isnil(ls, idx);
bool valid = lua_isnumber(ls, idx);
if (!nil && !valid)
luaL_error(ls, "'%s' in table, but not an int.", name);
int ret = (!nil && valid ? lua_tonumber(ls, idx) : defval);
lua_pop(ls, 1);
return (ret);
}
// Return the character stored in the table (on the stack) with the key name.
// If the key doesn't exist or the value is the wrong type, return defval.
static char _table_char(lua_State *ls, int idx, const char *name, char defval)
{
lua_pushstring(ls, name);
lua_gettable(ls, idx < 0 ? idx - 1 : idx);
bool nil = lua_isnil(ls, idx);
bool valid = lua_isstring(ls, idx);
if (!nil && !valid)
luaL_error(ls, "'%s' in table, but not a string.", name);
char ret = defval;
if (!nil && valid)
{
const char *str = lua_tostring(ls, idx);
if (str[0] && !str[1])
ret = str[0];
else
luaL_error(ls, "'%s' has more than one character.", name);
}
lua_pop(ls, 1);
return (ret);
}
// Return the string stored in the table (on the stack) with the key name.
// If the key doesn't exist or the value is the wrong type, return defval.
static const char* _table_str(lua_State *ls, int idx, const char *name, const char *defval)
{
lua_pushstring(ls, name);
lua_gettable(ls, idx < 0 ? idx - 1 : idx);
bool nil = lua_isnil(ls, idx);
bool valid = lua_isstring(ls, idx);
if (!nil && !valid)
luaL_error(ls, "'%s' in table, but not a string.", name);
const char *ret = (!nil && valid ? lua_tostring(ls, idx) : defval);
lua_pop(ls, 1);
return (ret);
}
// Return the boolean stored in the table (on the stack) with the key name.
// If the key doesn't exist or the value is the wrong type, return defval.
static bool _table_bool(lua_State *ls, int idx, const char *name, bool defval)
{
lua_pushstring(ls, name);
lua_gettable(ls, idx < 0 ? idx - 1 : idx);
bool nil = lua_isnil(ls, idx);
bool valid = lua_isboolean(ls, idx);
if (!nil && !valid)
luaL_error(ls, "'%s' in table, but not a bool.", name);
bool ret = (!nil && valid ? lua_toboolean(ls, idx) : defval);
lua_pop(ls, 1);
return (ret);
}
#define BF_INT(ls, val, def) int val = _table_int(ls, -1, #val, def);
#define BF_CHAR(ls, val, def) char val = _table_char(ls, -1, #val, def);
#define BF_STR(ls, val, def) const char *val = _table_str(ls, -1, #val, def);
#define BF_BOOL(ls, val, def) bool val = _table_bool(ls, -1, #val, def);
static void bf_octa_room(lua_State *ls, map_lines &lines)
{
int default_oblique = std::min(lines.width(), lines.height()) / 2 - 1;
BF_INT(ls, oblique, default_oblique);
BF_CHAR(ls, outside, 'x');
BF_CHAR(ls, inside, '.');
BF_STR(ls, replace, ".");
coord_def tl, br;
if (!lines.find_bounds(replace, tl, br))
return;
for (rectangle_iterator ri(tl, br); ri; ++ri)
{
const coord_def mc = *ri;
char glyph = lines(mc);
if (replace[0] && !strchr(replace, glyph))
continue;
int ob = 0;
ob += std::max(oblique + tl.x - mc.x, 0);
ob += std::max(oblique + mc.x - br.x, 0);
bool is_inside = (mc.y >= tl.y + ob && mc.y <= br.y - ob);
lines(mc) = is_inside ? inside : outside;
}
}
static void bf_smear(lua_State *ls, map_lines &lines)
{
BF_INT(ls, iterations, 1);
BF_CHAR(ls, smear, 'x');
BF_STR(ls, onto, ".");
BF_BOOL(ls, boxy, false);
const int max_test_per_iteration = 10;
int sanity = 0;
int max_sanity = iterations * max_test_per_iteration;
for (int i = 0; i < iterations; i++)
{
bool diagonals, straights;
coord_def mc;
do
{
do
{
sanity++;
mc.x = random_range(1, lines.width() - 2);
mc.y = random_range(1, lines.height() - 2);
}
while (onto[0] && !strchr(onto, lines(mc)));
// Prevent too many iterations.
if (sanity > max_sanity)
return;
// Is there a "smear" feature along the diagonal from mc?
diagonals = lines(coord_def(mc.x+1, mc.y+1)) == smear ||
lines(coord_def(mc.x-1, mc.y+1)) == smear ||
lines(coord_def(mc.x-1, mc.y-1)) == smear ||
lines(coord_def(mc.x+1, mc.y-1)) == smear;
// Is there a "smear" feature up, down, left, or right from mc?
straights = lines(coord_def(mc.x+1, mc.y)) == smear ||
lines(coord_def(mc.x-1, mc.y)) == smear ||
lines(coord_def(mc.x, mc.y+1)) == smear ||
lines(coord_def(mc.x, mc.y-1)) == smear;
}
while (!straights && (boxy || !diagonals));
lines(mc) = smear;
}
}
static void bf_extend(lua_State *ls, map_lines &lines)
{
BF_INT(ls, height, 1);
BF_INT(ls, width, 1);
BF_CHAR(ls, fill, 'x');
lines.extend(width, height, fill);
}
typedef void (*bf_func)(lua_State *ls, map_lines &lines);
struct bf_entry
{
const char* name;
bf_func func;
};
// Create a separate list of builder funcs so that we can automatically
// generate a list of closures for them, rather than individually
// and explicitly exposing them to the dgn namespace.
static struct bf_entry bf_map[] =
{
{ "map_octa_room", &bf_octa_room },
{ "map_smear", &bf_smear },
{ "map_extend", &bf_extend }
{ NULL, NULL }
static int dgn_call_builder_func(lua_State *ls)
{
// This function gets called for all the builder functions that
// operate on map_lines.
MAP(ls, 1, map);
if (!lua_istable(ls, 2) && !lua_isfunction(ls, 2))
return luaL_argerror(ls, 2, "Expected table");
bf_func *func = (bf_func *)lua_topointer(ls, lua_upvalueindex(1));
if (!func)
return luaL_error(ls, "Expected C function in closure upval");
// Put the table on top.
lua_settop(ls, 2);
// Call the builder func itself.
(*func)(ls, map->map);
return (0);
}
static void _register_builder_funcs(lua_State *ls)
{
lua_getglobal(ls, "dgn");
const size_t num_entries = sizeof(bf_map) / sizeof(bf_entry);
for (size_t i = 0; i < num_entries; i++)
{
// Push a closure with the C function into the dgn table.
lua_pushlightuserdata(ls, &bf_map[i].func);
lua_pushcclosure(ls, &dgn_call_builder_func, 1);
lua_setfield(ls, -2, bf_map[i].name);
}
lua_pop(ls, 1);
}
void init_dungeon_lua()
{
lua_stack_cleaner clean(dlua);
luaL_openlib(dlua, "dgn", dgn_lib, 0);
// Add additional function to the Crawl module.
luaL_openlib(dlua, "crawl", crawl_lib, 0);
luaL_openlib(dlua, "file", file_lib, 0);
luaL_openlib(dlua, "you", you_lib, 0);
luaL_openlib(dlua, "los", los_lib, 0);
dlua.execfile("clua/dungeon.lua", true, true);
dlua.execfile("clua/luamark.lua", true, true);
lua_getglobal(dlua, "dgn_run_map");
luaopen_debug(dlua);
luaL_newmetatable(dlua, MAP_METATABLE);
luaopen_dgnevent(dlua);
luaopen_mapmarker(dlua);
luaopen_ray(dlua);
_register_builder_funcs(dlua);
_register_mapdef_tables(dlua);
}
// Can be called from within a debugger to look at the current Lua
// call stack. (Borrowed from ToME 3)
void print_dlua_stack(void)
{
struct lua_Debug dbg;
int i = 0;
lua_State *L = dlua.state();
fprintf(stderr, EOL);
while (lua_getstack(L, i++, &dbg) == 1)
{
lua_getinfo(L, "lnuS", &dbg);
char* file = strrchr(dbg.short_src, '/');
if (file == NULL)
file = dbg.short_src;
else
file++;
fprintf(stderr, "%s, function %s, line %d" EOL, file,
dbg.name, dbg.currentline);
}
fprintf(stderr, EOL);
}
#define MAP(ls, n, var) \
map_def *var = *(map_def **) luaL_checkudata(ls, n, MAP_METATABLE)
#define DEVENT(ls, n, var) \
dgn_event *var = *(dgn_event **) luaL_checkudata(ls, n, DEVENT_METATABLE)
#define MAPMARKER(ls, n, var) \
map_marker *var = *(map_marker **) luaL_checkudata(ls, n, MAPMARK_METATABLE)
extern const struct luaL_reg dgn_lib[];
extern const struct luaL_reg los_lib[];
/*
* File: luadgn.cc
* Summary: Dungeon-builder Lua interface.
* Created by: dshaligram on Sat Jun 23 20:02:09 2007 UTC
*/
#include "AppHdr.h"
#include <sstream>
#include <algorithm>
#include <memory>
#include <cmath>
#include "branch.h"
#include "chardump.h"
#include "clua.h"
#include "cloud.h"
#include "describe.h"
#include "directn.h"
#include "dlua.h"
#include "dungeon.h"
#include "files.h"
#include "hiscores.h"
#include "initfile.h"
#include "items.h"
#include "l_los.h"
#include "los.h"
#include "mapdef.h"
#include "mapmark.h"
#include "maps.h"
#include "message.h"
#include "misc.h"
#include "mon-util.h"
#include "monplace.h"
#include "monstuff.h"
#include "place.h"
#include "spells3.h"
#include "spl-util.h"
#include "state.h"
#include "stuff.h"
#include "tags.h"
#include "terrain.h"
#include "view.h"
#ifdef UNIX
#include <sys/time.h>
#include <time.h>
#endif
template <typename list, typename lpush>
static int dlua_gentable(lua_State *ls, const list &strings, lpush push)
{
lua_newtable(ls);
for (int i = 0, size = strings.size(); i < size; ++i)
{
push(ls, strings[i]);
lua_rawseti(ls, -2, i + 1);
}
return (1);
}
inline static void dlua_pushcxxstring(lua_State *ls, const std::string &s)
{
lua_pushstring(ls, s.c_str());
}
int dlua_stringtable(lua_State *ls, const std::vector<std::string> &s)
{
return dlua_gentable(ls, s, dlua_pushcxxstring);
}
static int dlua_compiled_chunk_writer(lua_State *ls, const void *p,
size_t sz, void *ud)
{
std::ostringstream &out = *static_cast<std::ostringstream*>(ud);
out.write((const char *) p, sz);
return (0);
}
///////////////////////////////////////////////////////////////////////////
// dlua_chunk
dlua_chunk::dlua_chunk(const std::string &_context)
: file(), chunk(), compiled(), context(_context), first(-1),
last(-1), error()
{
clear();
}
// Initialises a chunk from the function on the top of stack.
// This function must not be a closure, i.e. must not have any upvalues.
dlua_chunk::dlua_chunk(lua_State *ls)
: file(), chunk(), compiled(), context(), first(-1), last(-1), error()
{
clear();
lua_stack_cleaner cln(ls);
std::ostringstream out;
const int err = lua_dump(ls, dlua_compiled_chunk_writer, &out);
if (err)
{
const char *e = lua_tostring(ls, -1);
error = e? e : "Unknown error compiling chunk";
}
compiled = out.str();
}
dlua_chunk dlua_chunk::precompiled(const std::string &chunk)
{
dlua_chunk dchunk;
dchunk.compiled = chunk;
return (dchunk);
}
void dlua_chunk::write(writer& outf) const
{
if (empty())
{
marshallByte(outf, CT_EMPTY);
return;
}
if (!compiled.empty())
{
marshallByte(outf, CT_COMPILED);
marshallString4(outf, compiled);
}
else
{
marshallByte(outf, CT_SOURCE);
marshallString4(outf, chunk);
}
marshallString4(outf, file);
marshallLong(outf, first);
}
void dlua_chunk::read(reader& inf)
{
clear();
chunk_t type = static_cast<chunk_t>(unmarshallByte(inf));
switch (type)
{
case CT_EMPTY:
return;
case CT_SOURCE:
unmarshallString4(inf, chunk);
break;
case CT_COMPILED:
unmarshallString4(inf, compiled);
break;
}
unmarshallString4(inf, file);
first = unmarshallLong(inf);
}
void dlua_chunk::clear()
{
file.clear();
chunk.clear();
first = last = -1;
error.clear();
compiled.clear();
}
void dlua_chunk::set_file(const std::string &s)
{
file = s;
}
void dlua_chunk::add(int line, const std::string &s)
{
if (first == -1)
first = line;
if (line != last && last != -1)
while (last++ < line)
chunk += '\n';
chunk += " ";
chunk += s;
last = line;
}
void dlua_chunk::set_chunk(const std::string &s)
{
chunk = s;
}
int dlua_chunk::check_op(CLua &interp, int err)
{
error = interp.error;
return (err);
}
int dlua_chunk::load(CLua &interp)
{
if (!compiled.empty())
return check_op( interp,
interp.loadbuffer(compiled.c_str(), compiled.length(),
context.c_str()) );
if (empty())
{
chunk.clear();
return (-1000);
}
int err = check_op( interp,
interp.loadstring(chunk.c_str(), context.c_str()) );
if (err)
return (err);
std::ostringstream out;
err = lua_dump(interp, dlua_compiled_chunk_writer, &out);
if (err)
{
const char *e = lua_tostring(interp, -1);
error = e? e : "Unknown error compiling chunk";
lua_pop(interp, 2);
}
compiled = out.str();
chunk.clear();
return (err);
}
int dlua_chunk::run(CLua &interp)
{
int err = load(interp);
if (err)
return (err);
// callfn returns true on success, but we want to return 0 on success.
return (check_op(interp, !interp.callfn(NULL, 0, 0)));
}
int dlua_chunk::load_call(CLua &interp, const char *fn)
{
int err = load(interp);
if (err == -1000)
return (0);
if (err)
return (err);
return check_op(interp, !interp.callfn(fn, fn? 1 : 0, 0));
}
std::string dlua_chunk::orig_error() const
{
rewrite_chunk_errors(error);
return (error);
}
bool dlua_chunk::empty() const
{
return compiled.empty() && trimmed_string(chunk).empty();
}
bool dlua_chunk::rewrite_chunk_errors(std::string &s) const
{
const std::string contextm = "[string \"" + context + "\"]:";
std::string::size_type dlwhere = s.find(contextm);
if (dlwhere == std::string::npos)
return (false);
if (!dlwhere)
{
s = rewrite_chunk_prefix(s);
return (true);
}
// Our chunk is mentioned, go back through and rewrite lines.
std::vector<std::string> lines = split_string("\n", s);
std::string newmsg = lines[0];
bool wrote_prefix = false;
for (int i = 2, size = lines.size() - 1; i < size; ++i)
{
const std::string &st = lines[i];
if (st.find(context) != std::string::npos)
{
if (!wrote_prefix)
{
newmsg = get_chunk_prefix(st) + ": " + newmsg;
wrote_prefix = true;
}
else
newmsg += "\n" + rewrite_chunk_prefix(st);
}
}
s = newmsg;
return (true);
}
std::string dlua_chunk::rewrite_chunk_prefix(const std::string &line,
bool skip_body) const
{
std::string s = line;
const std::string contextm = "[string \"" + context + "\"]:";
const std::string::size_type ps = s.find(contextm);
if (ps == std::string::npos)
return (s);
const std::string::size_type lns = ps + contextm.length();
std::string::size_type pe = s.find(':', ps + contextm.length());
if (pe != std::string::npos)
{
const std::string line_num = s.substr(lns, pe - lns);
const int lnum = atoi(line_num.c_str());
const std::string newlnum = make_stringf("%d", lnum + first - 1);
s = s.substr(0, lns) + newlnum + s.substr(pe);
pe = lns + newlnum.length();
}
return s.substr(0, ps) + (file.empty()? context : file) + ":"
+ (skip_body? s.substr(lns, pe - lns)
: s.substr(lns));
}
std::string dlua_chunk::get_chunk_prefix(const std::string &sorig) const
{
return rewrite_chunk_prefix(sorig, true);
}
LUAFN(_crawl_args)
{
return dlua_stringtable(ls, SysEnv.cmd_args);
}
LUAFN(_crawl_milestone)
{
#ifdef DGL_MILESTONES
mark_milestone(luaL_checkstring(ls, 1),
luaL_checkstring(ls, 2));
#endif
return (0);
}
LUAFN(_crawl_redraw_view)
{
viewwindow(true, false);
return (0);
}
#ifdef UNIX
LUAFN(_crawl_millis)
{
struct timeval tv;
struct timezone tz;
const int error = gettimeofday(&tv, &tz);
if (error)
luaL_error(ls, make_stringf("Failed to get time: %s",
strerror(error)).c_str());
lua_pushnumber(ls, tv.tv_sec * 1000 + tv.tv_usec / 1000);
return (1);
}
#endif
static const struct luaL_reg crawl_lib[] =
{
{ "args", _crawl_args },
{ "mark_milestone", _crawl_milestone },
{ "redraw_view", _crawl_redraw_view },
#ifdef UNIX
{ "millis", _crawl_millis },
#endif
{ NULL, NULL }
};
static int file_marshall(lua_State *ls)
{
if (lua_gettop(ls) != 2)
luaL_error(ls, "Need two arguments: tag header and value");
writer &th(*static_cast<writer*>( lua_touserdata(ls, 1) ));
if (lua_isnumber(ls, 2))
marshallLong(th, luaL_checklong(ls, 2));
else if (lua_isboolean(ls, 2))
marshallByte(th, lua_toboolean(ls, 2));
else if (lua_isstring(ls, 2))
marshallString(th, lua_tostring(ls, 2));
else if (lua_isfunction(ls, 2))
{
dlua_chunk chunk(ls);
marshallString(th, chunk.compiled_chunk());
}
return (0);
}
static int file_unmarshall_boolean(lua_State *ls)
{
if (lua_gettop(ls) != 1)
luaL_error(ls, "Need reader as one argument");
reader &th(*static_cast<reader*>( lua_touserdata(ls, 1) ));
lua_pushboolean(ls, unmarshallByte(th));
return (1);
}
static int file_unmarshall_number(lua_State *ls)
{
if (lua_gettop(ls) != 1)
luaL_error(ls, "Need reader as one argument");
reader &th(*static_cast<reader*>( lua_touserdata(ls, 1) ));
lua_pushnumber(ls, unmarshallLong(th));
return (1);
}
static int file_unmarshall_string(lua_State *ls)
{
if (lua_gettop(ls) != 1)
luaL_error(ls, "Need reader as one argument");
reader &th(*static_cast<reader*>( lua_touserdata(ls, 1) ));
lua_pushstring(ls, unmarshallString(th).c_str());
return (1);
}
static int file_unmarshall_fn(lua_State *ls)
{
if (lua_gettop(ls) != 1)
luaL_error(ls, "Need reader as one argument");
reader &th(*static_cast<reader*>( lua_touserdata(ls, 1) ));
const std::string s(unmarshallString(th, LUA_CHUNK_MAX_SIZE));
dlua_chunk chunk = dlua_chunk::precompiled(s);
if (chunk.load(dlua))
lua_pushnil(ls);
return (1);
}
enum lua_persist_type
{
LPT_NONE,
LPT_NUMBER,
LPT_STRING,
LPT_FUNCTION,
LPT_NIL,
LPT_BOOLEAN
};
static int file_marshall_meta(lua_State *ls)
{
if (lua_gettop(ls) != 2)
luaL_error(ls, "Need two arguments: tag header and value");
writer &th(*static_cast<writer*>( lua_touserdata(ls, 1) ));
lua_persist_type ptype = LPT_NONE;
if (lua_isnumber(ls, 2))
ptype = LPT_NUMBER;
else if (lua_isboolean(ls, 2))
ptype = LPT_BOOLEAN;
else if (lua_isstring(ls, 2))
ptype = LPT_STRING;
else if (lua_isfunction(ls, 2))
ptype = LPT_FUNCTION;
else if (lua_isnil(ls, 2))
ptype = LPT_NIL;
else
luaL_error(ls,
make_stringf("Cannot marshall %s",
lua_typename(ls, lua_type(ls, 2))).c_str());
marshallByte(th, ptype);
if (ptype != LPT_NIL)
file_marshall(ls);
return (0);
}
static int file_unmarshall_meta(lua_State *ls)
{
reader &th(*static_cast<reader*>( lua_touserdata(ls, 1) ));
const lua_persist_type ptype =
static_cast<lua_persist_type>(unmarshallByte(th));
switch (ptype)
{
case LPT_BOOLEAN:
return file_unmarshall_boolean(ls);
case LPT_NUMBER:
return file_unmarshall_number(ls);
case LPT_STRING:
return file_unmarshall_string(ls);
case LPT_FUNCTION:
return file_unmarshall_fn(ls);
case LPT_NIL:
lua_pushnil(ls);
return (1);
default:
luaL_error(ls, "Unexpected type signature.");
}
// Never get here.
return (0);
}
static const struct luaL_reg file_lib[] =
{
{ "marshall", file_marshall },
{ "marshall_meta", file_marshall_meta },
{ "unmarshall_meta", file_unmarshall_meta },
{ "unmarshall_number", file_unmarshall_number },
{ "unmarshall_string", file_unmarshall_string },
{ "unmarshall_fn", file_unmarshall_fn },
{ NULL, NULL }
};
LUARET1(you_can_hear_pos, boolean,
player_can_hear(coord_def(luaL_checkint(ls,1), luaL_checkint(ls, 2))))
LUARET1(you_x_pos, number, you.pos().x)
LUARET1(you_y_pos, number, you.pos().y)
LUARET2(you_pos, number, you.pos().x, you.pos().y)
// see_cell should not be exposed to user scripts. The game should
// never disclose grid coordinates to the player. Therefore we load it
// only into the core Lua interpreter (dlua), never into the user
// script interpreter (clua).
LUARET1(you_see_cell, boolean,
see_cell(luaL_checkint(ls, 1), luaL_checkint(ls, 2)))
LUARET1(you_see_cell_no_trans, boolean,
see_cell_no_trans(luaL_checkint(ls, 1), luaL_checkint(ls, 2)))
LUAFN(you_moveto)
{
const coord_def place(luaL_checkint(ls, 1), luaL_checkint(ls, 2));
ASSERT(map_bounds(place));
you.moveto(place);
return (0);
}
LUAFN(you_random_teleport)
{
you_teleport_now(false, false);
return (0);
}
LUAFN(you_losight)
{
calc_show_los();
return (0);
}
static const struct luaL_reg you_lib[] =
{
{ "hear_pos", you_can_hear_pos },
{ "x_pos", you_x_pos },
{ "y_pos", you_y_pos },
{ "pos", you_pos },
{ "moveto", you_moveto },
{ "see_cell", you_see_cell },
{ "see_cell_no_trans", you_see_cell_no_trans },
{ "random_teleport", you_random_teleport },
{ "losight", you_losight },
{ NULL, NULL }
};
static int dgnevent_type(lua_State *ls)
{
DEVENT(ls, 1, dev);
PLUARET(number, dev->type);
}
static int dgnevent_place(lua_State *ls)
{
DEVENT(ls, 1, dev);
lua_pushnumber(ls, dev->place.x);
lua_pushnumber(ls, dev->place.y);
return (2);
}
static int dgnevent_dest(lua_State *ls)
{
DEVENT(ls, 1, dev);
lua_pushnumber(ls, dev->dest.x);
lua_pushnumber(ls, dev->dest.y);
return (2);
}
static int dgnevent_ticks(lua_State *ls)
{
DEVENT(ls, 1, dev);
PLUARET(number, dev->elapsed_ticks);
}
static int dgnevent_arg1(lua_State *ls)
{
DEVENT(ls, 1, dev);
PLUARET(number, dev->arg1);
}
static int dgnevent_arg2(lua_State *ls)
{
DEVENT(ls, 1, dev);
PLUARET(number, dev->arg2);
}
static const struct luaL_reg dgnevent_lib[] =
{
{ "type", dgnevent_type },
{ "pos", dgnevent_place },
{ "dest", dgnevent_dest },
{ "ticks", dgnevent_ticks },
{ "arg1", dgnevent_arg1 },
{ "arg2", dgnevent_arg2 },
{ NULL, NULL }
};
void luaopen_setmeta(lua_State *ls,
const char *global,
const luaL_reg *lua_lib,
const char *meta)
{
luaL_newmetatable(ls, meta);
lua_setglobal(ls, global);
luaL_openlib(ls, global, lua_lib, 0);
// Do <global>.__index = <global>
lua_pushstring(ls, "__index");
lua_pushvalue(ls, -2);
lua_settable(ls, -3);
}
static void luaopen_dgnevent(lua_State *ls)
{
luaopen_setmeta(ls, "dgnevent", dgnevent_lib, DEVENT_METATABLE);
}
static int mapmarker_pos(lua_State *ls)
{
MAPMARKER(ls, 1, mark);
lua_pushnumber(ls, mark->pos.x);
lua_pushnumber(ls, mark->pos.y);
return (2);
}
static int mapmarker_move(lua_State *ls)
{
MAPMARKER(ls, 1, mark);
const coord_def dest( luaL_checkint(ls, 2), luaL_checkint(ls, 3) );
env.markers.move_marker(mark, dest);
return (0);
}
static const struct luaL_reg mapmarker_lib[] =
{
{ "pos", mapmarker_pos },
{ "move", mapmarker_move },
{ NULL, NULL }
};
static void luaopen_mapmarker(lua_State *ls)
{
luaopen_setmeta(ls, "mapmarker", mapmarker_lib, MAPMARK_METATABLE);
}
// Return the integer stored in the table (on the stack) with the key name.
// If the key doesn't exist or the value is the wrong type, return defval.
static int _table_int(lua_State *ls, int idx, const char *name, int defval)
{
lua_pushstring(ls, name);
lua_gettable(ls, idx < 0 ? idx - 1 : idx);
bool nil = lua_isnil(ls, idx);
bool valid = lua_isnumber(ls, idx);
if (!nil && !valid)
luaL_error(ls, "'%s' in table, but not an int.", name);
int ret = (!nil && valid ? lua_tonumber(ls, idx) : defval);
lua_pop(ls, 1);
return (ret);
}
// Return the character stored in the table (on the stack) with the key name.
// If the key doesn't exist or the value is the wrong type, return defval.
static char _table_char(lua_State *ls, int idx, const char *name, char defval)
{
lua_pushstring(ls, name);
lua_gettable(ls, idx < 0 ? idx - 1 : idx);
bool nil = lua_isnil(ls, idx);
bool valid = lua_isstring(ls, idx);
if (!nil && !valid)
luaL_error(ls, "'%s' in table, but not a string.", name);
char ret = defval;
if (!nil && valid)
{
const char *str = lua_tostring(ls, idx);
if (str[0] && !str[1])
ret = str[0];
else
luaL_error(ls, "'%s' has more than one character.", name);
}
lua_pop(ls, 1);
return (ret);
}
// Return the string stored in the table (on the stack) with the key name.
// If the key doesn't exist or the value is the wrong type, return defval.
static const char* _table_str(lua_State *ls, int idx, const char *name, const char *defval)
{
lua_pushstring(ls, name);
lua_gettable(ls, idx < 0 ? idx - 1 : idx);
bool nil = lua_isnil(ls, idx);
bool valid = lua_isstring(ls, idx);
if (!nil && !valid)
luaL_error(ls, "'%s' in table, but not a string.", name);
const char *ret = (!nil && valid ? lua_tostring(ls, idx) : defval);
lua_pop(ls, 1);
return (ret);
}
// Return the boolean stored in the table (on the stack) with the key name.
// If the key doesn't exist or the value is the wrong type, return defval.
static bool _table_bool(lua_State *ls, int idx, const char *name, bool defval)
{
lua_pushstring(ls, name);
lua_gettable(ls, idx < 0 ? idx - 1 : idx);
bool nil = lua_isnil(ls, idx);
bool valid = lua_isboolean(ls, idx);
if (!nil && !valid)
luaL_error(ls, "'%s' in table, but not a bool.", name);
bool ret = (!nil && valid ? lua_toboolean(ls, idx) : defval);
lua_pop(ls, 1);
return (ret);
}
#define BF_INT(ls, val, def) int val = _table_int(ls, -1, #val, def);
#define BF_CHAR(ls, val, def) char val = _table_char(ls, -1, #val, def);
#define BF_STR(ls, val, def) const char *val = _table_str(ls, -1, #val, def);
#define BF_BOOL(ls, val, def) bool val = _table_bool(ls, -1, #val, def);
static void bf_octa_room(lua_State *ls, map_lines &lines)
{
int default_oblique = std::min(lines.width(), lines.height()) / 2 - 1;
BF_INT(ls, oblique, default_oblique);
BF_CHAR(ls, outside, 'x');
BF_CHAR(ls, inside, '.');
BF_STR(ls, replace, ".");
coord_def tl, br;
if (!lines.find_bounds(replace, tl, br))
return;
for (rectangle_iterator ri(tl, br); ri; ++ri)
{
const coord_def mc = *ri;
char glyph = lines(mc);
if (replace[0] && !strchr(replace, glyph))
continue;
int ob = 0;
ob += std::max(oblique + tl.x - mc.x, 0);
ob += std::max(oblique + mc.x - br.x, 0);
bool is_inside = (mc.y >= tl.y + ob && mc.y <= br.y - ob);
lines(mc) = is_inside ? inside : outside;
}
}
static void bf_smear(lua_State *ls, map_lines &lines)
{
BF_INT(ls, iterations, 1);
BF_CHAR(ls, smear, 'x');
BF_STR(ls, onto, ".");
BF_BOOL(ls, boxy, false);
const int max_test_per_iteration = 10;
int sanity = 0;
int max_sanity = iterations * max_test_per_iteration;
for (int i = 0; i < iterations; i++)
{
bool diagonals, straights;
coord_def mc;
do
{
do
{
sanity++;
mc.x = random_range(1, lines.width() - 2);
mc.y = random_range(1, lines.height() - 2);
}
while (onto[0] && !strchr(onto, lines(mc)));
// Prevent too many iterations.
if (sanity > max_sanity)
return;
// Is there a "smear" feature along the diagonal from mc?
diagonals = lines(coord_def(mc.x+1, mc.y+1)) == smear ||
lines(coord_def(mc.x-1, mc.y+1)) == smear ||
lines(coord_def(mc.x-1, mc.y-1)) == smear ||
lines(coord_def(mc.x+1, mc.y-1)) == smear;
// Is there a "smear" feature up, down, left, or right from mc?
straights = lines(coord_def(mc.x+1, mc.y)) == smear ||
lines(coord_def(mc.x-1, mc.y)) == smear ||
lines(coord_def(mc.x, mc.y+1)) == smear ||
lines(coord_def(mc.x, mc.y-1)) == smear;
}
while (!straights && (boxy || !diagonals));
lines(mc) = smear;
}
}
static void bf_extend(lua_State *ls, map_lines &lines)
{
BF_INT(ls, height, 1);
BF_INT(ls, width, 1);
BF_CHAR(ls, fill, 'x');
lines.extend(width, height, fill);
}
typedef void (*bf_func)(lua_State *ls, map_lines &lines);
struct bf_entry
{
const char* name;
bf_func func;
};
// Create a separate list of builder funcs so that we can automatically
// generate a list of closures for them, rather than individually
// and explicitly exposing them to the dgn namespace.
static struct bf_entry bf_map[] =
{
{ "map_octa_room", &bf_octa_room },
{ "map_smear", &bf_smear },
{ "map_extend", &bf_extend }
};
static int dgn_call_builder_func(lua_State *ls)
{
// This function gets called for all the builder functions that
// operate on map_lines.
MAP(ls, 1, map);
if (!lua_istable(ls, 2) && !lua_isfunction(ls, 2))
return luaL_argerror(ls, 2, "Expected table");
bf_func *func = (bf_func *)lua_topointer(ls, lua_upvalueindex(1));
if (!func)
return luaL_error(ls, "Expected C function in closure upval");
// Put the table on top.
lua_settop(ls, 2);
// Call the builder func itself.
(*func)(ls, map->map);
return (0);
}
static void _register_builder_funcs(lua_State *ls)
{
lua_getglobal(ls, "dgn");
const size_t num_entries = sizeof(bf_map) / sizeof(bf_entry);
for (size_t i = 0; i < num_entries; i++)
{
// Push a closure with the C function into the dgn table.
lua_pushlightuserdata(ls, &bf_map[i].func);
lua_pushcclosure(ls, &dgn_call_builder_func, 1);
lua_setfield(ls, -2, bf_map[i].name);
}
lua_pop(ls, 1);
}
void init_dungeon_lua()
{
lua_stack_cleaner clean(dlua);
luaL_openlib(dlua, "dgn", dgn_lib, 0);
// Add additional function to the Crawl module.
luaL_openlib(dlua, "crawl", crawl_lib, 0);
luaL_openlib(dlua, "file", file_lib, 0);
luaL_openlib(dlua, "you", you_lib, 0);
luaL_openlib(dlua, "los", los_lib, 0);
dlua.execfile("clua/dungeon.lua", true, true);
dlua.execfile("clua/luamark.lua", true, true);
lua_getglobal(dlua, "dgn_run_map");
luaopen_debug(dlua);
luaL_newmetatable(dlua, MAP_METATABLE);
luaopen_dgnevent(dlua);
luaopen_mapmarker(dlua);
luaopen_ray(dlua);
_register_builder_funcs(dlua);
register_mapdef_tables(dlua);
}
// Can be called from within a debugger to look at the current Lua
// call stack. (Borrowed from ToME 3)
void print_dlua_stack(void)
{
struct lua_Debug dbg;
int i = 0;
lua_State *L = dlua.state();
fprintf(stderr, EOL);
while (lua_getstack(L, i++, &dbg) == 1)
{
lua_getinfo(L, "lnuS", &dbg);
char* file = strrchr(dbg.short_src, '/');
if (file == NULL)
file = dbg.short_src;
else
file++;
fprintf(stderr, "%s, function %s, line %d" EOL, file,
dbg.name, dbg.currentline);
}
fprintf(stderr, EOL);
}