git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7553 c06c8d41-db1a-0410-9941-cceddc491573
NKONHW4JNY6HP2M63MNPM3H64ZWSUNUT5FX2STW4KTS4AMXJXXVQC
XEESII7USBFCL5OZMNZBVJOI66HRL6BT7CQMAPKZNSLM7YUBBQHQC
Q2AYZGR7UBWG4CKVR6PJQD3UHDEBMAFBQ5PZLHT64CTREG3JYFZAC
7FUW7VHYYS2URT45THZ7NJ7NC6JMNGQXJJZ34SPPTBFQHH6RQOJAC
KSCU43RIVSEIHTN6BRAKXENI66L3IRDZQHUBT5VS4NJBBCQQPHUQC
X7MFMKQTNZ2IWBFVGS6WQV7NRNKJ3DWQAW2X7IQMFQQXW24AHPZQC
ZBFNQZV6XCH4NUTHJU4K7RN4KLUMWNCWWO6UP7TIRASCWD5J5OMQC
WX2VFNANQZ3IRHBXSLKJT3G3OAQREAZISXLOTG6JO7KXFBHQFOYAC
G3UKWY6JNG2ZQO6R2UTAAIQ3ECJIGEKEHTKOJNL5EN4YBC7U44KQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
T6TL6NTIOBYNUIONGK3JFZJ5ONWV6S4CTIRDC5JMKMCBGG5IY3EAC
TFZ4TER7O2Z4FOGF2RCPEPYIHBTUA4LG3ECXLR7XGLCC6GO6OOTAC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
L57WEZMVIHTFRLWVIPWOIOWHCF2I53TVUVYJ2V6IZ64R56FVTZZAC
UZ6N6HOUPGVSPC5NQROEEDWMEGJA5XUWUY2AKH5QG65AZ25PVXDAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
ID373JATLMWAY526Q6Q5FXHRNFWMEOFXPHGPAUUY5OAMPFDN5SJAC
GPEJOT73KMACP33IPAKFR5ROGHCOIP22VXZMQNYTGLEA2OSZUM2AC
3UKFCWWS5BLFQWZRB5FUA46CE2XGX5VRCEWC3K3XH5RCGQK64N2AC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
7YSKYUNV34XIWRTJUHJV4QMQRTXXYDIXM5AZSPSDPAYDW4B4PU6QC
R2DQBWKIW7YUJB5SOQ7J274JIYRVX4H3ISFRPAL5RG2RVVP4G2KAC
APGCKU4AFOV7Z7XIEO5A27H4IFUGDU227I3Z7OIRROYSLOFFBJ5AC
Q3DNEB5OOJ34P5ML4CMK3L6SCP7RLW7DDOZEG24KZBX3C7BJRQDAC
DTO3EUKWHZ5RJNGNCFYXSOVTIPVXPP637F2W7WFGYKJ7JK7VNKNQC
S34LKQDIQJLIWVIPASOJBBZ6ZCXDHP5KPS7TRBZJSCDRVNCLK6UAC
ZLQAAP55CJ77XIJN3DZVPT4GTTVLIBFJLIJJKI6L5UBSHX7VUK6AC
AUXHSGS4EFOPZ6TVZYWNVOUDO7NYKUKE3HBKGQQWTALSVFOE3HAAC
7Y5HSDFKA5TPLS2TWTRFMQVX6UXUDHXU5MUMXQSDFAIY4THQ3BIQC
RGHXFBNIULRVRYLBGG5JZDMYVM2E2JJ2Y5KQPMU6PUS3V26G6ZXQC
TLA5UN6LZPXGKERI27EFY4HKIIU3VU5Y7ZU54WXL6ANBUV2VOTMQC
LDBTCT5WIPLJPZWXS2RUQ26QKISCUUTLO77M464WOE6VSYSNPKYAC
UBQTNLYGD3SNWMUNGWUPX7EXEGQXOXCFCPWIVWBFE7ID7DJLPFWAC
IVVTHLTTLOP5TSULXJWUSSXHOKYWVU3OWKYVK45A7RIB6V34MYQAC
ED62QWGKBPORWVKDFOQRKJXEIWZVNGR3O4KWQBDSRNPT36AYOQYAC
RN242L3YZK35BFY7JRTCSYWD6FWDCRFUFDI7PKYFUCA2UPX6ZEAQC
44YRMW5JNVUBYX3M6UFQOPENNHGO7G2CRZ7SGPIIUTQGYWTRWENAC
ASLW3Z5PAVZSWJEMMMVZT226P44EKSAD47QS72JIFJESAI3RPN3AC
PRG7UT7G56GT4W3FQ3KG5JRPGMKKJBFDLVHDLYFQK6IZW25JQLBQC
G277QSURADDFZIIO5PIMBA4YWHYULQCEGNMNI6TLBWZXHQOOP6FAC
3C2VE43SHCSBY4LTRTFYFLIPRWFUN6DXU6D34QVWDQTSNRBUFG7AC
442VGKMARB6LTQUEBIB5P447EI34BRJL6JALZKXLWPDHWCM6KKCQC
B7MSPF6X2RLGWN4M6ZZF3WSOPKGYPTTD7LIJVST7DXN27DG6JHNAC
SIDH2P7NBIG5KEOE27XHD3ZT2NQ2OJZFN6VZXWNWYFFY5YVXSSVQC
ILN2K6ASDZSMEHOPJ22IZLZJUO6DDGZTKAKXM3YXG6JZZHJNLX4AC
45EMD3KLQPMERNMIKU5G76H6556XOMIW352TSBP7VLWJX2YYGS7AC
SQDS2YBPOYDDDCW3GGARBZ2HQIUHCQKL7SSHKFQWDENOL5YNNVNQC
EH4VJW3I5Y4V6DT3YMLNDA3NW2DEAV4LRE4T5IEXAVB4WB3JJMGAC
JDM27QE4HR52AYFSQE763BFF57ANOTF5MXKMO377PP5EXMN7SAOAC
HNXKX6ZDQJV33E7UKZOLBYWJMRZ4QLEMXVXJZNRCTOIG2KVRTIEAC
W52PCSHX72WAMWKG6L4BPUBVMO6E72KYYBNKAA7554KNOTY6V7WQC
ZJLJGSB2XSBQU42OFQMXL3EG4CXAQGOYAU6YTV2SAWZEJIPFH2CAC
UU5EKED2RA2U3CFZ3UEJQEWSWHQPEU7ZD4KH3I22IIVZFHD4Y67QC
OYTCBRC7LE44EUVRZVYTOOVKQWJ6P6YE3FXTOGUTNKEMLNWPHKSQC
YE7M665QKDGI7Y5WMERCWJNDZ4FUZ6GRUCK4E6GZH4SWCUM6RWLAC
E5DMZFW6WCFAKTKKOQPYTQXZ2CGLWMVH64LRXDUI2UIG4VYUHIVQC
ZGUJWUFJ4NFFJ6PGXLFGQWCWBCZHPWGWI44NJHJEVPRG5L36PADQC
R6XS2HO5QX2FJUGL5UQQRNETKCMYWTUFPHPPS5SYWK3OQA4UDUQQC
BPPMLLPJLP6W2LZSPAMOMYA7YWCIFJTNNL3XBWU2MRHAQBZ5M4XAC
{
switch(place.level_type)
{
case LEVEL_DUNGEON:
level_number = absdungeon_depth(place.branch, place.depth);
break;
case LEVEL_ABYSS:
level_number = 51;
break;
case LEVEL_PANDEMONIUM:
level_number = 52;
break;
default:
level_number = you.your_level;
}
}
level_number = place.absdepth();
{
switch (place.level_type)
{
case LEVEL_DUNGEON:
level_number = absdungeon_depth(place.branch, place.depth);
break;
case LEVEL_ABYSS:
level_number = 51;
break;
case LEVEL_PANDEMONIUM:
level_number = 52;
break;
default:
level_number = you.your_level;
}
}
level_number = place.absdepth();
case LEVEL_PANDEMONIUM:
mg.power = 52; // sigh..
break;
case LEVEL_ABYSS:
mg.power = 51;
break;
case LEVEL_DUNGEON:
default:
mg.power = you.your_level;
break;
case LEVEL_PANDEMONIUM:
case LEVEL_ABYSS:
mg.power = level_id(mg.level_type).absdepth();
break;
case LEVEL_DUNGEON:
default:
mg.power = you.your_level;
break;
error = "Can't set floor to black.";
}
else
{
error = "No such colour as '";
error += s;
error += "'";
std::string error;
if (colour == forbidden_colour)
error = std::string("Can't set floor to ") + s;
else
error = std::string("Unknown colour: '") + s + "'";
return luaL_argerror(ls, 1, error.c_str());
const char *s = luaL_checkstring(ls, 1);
int colour = str_to_colour(s);
if (colour < 0 || colour == BLACK)
{
std::string error;
if (colour == BLACK)
{
error = "Can't set rock to black.";
}
else
{
error = "No such colour as '";
error += s;
error += "'";
}
luaL_argerror(ls, 1, error.c_str());
return (0);
}
const int colour = _lua_colour(ls, 1, BLACK);
return (0);
}
static int dgn_create_monster(lua_State *ls)
{
COORDS(c, 1, 2);
if (const char *spec = lua_tostring(ls, 3))
{
mons_list mlist;
const std::string err = mlist.add_mons(spec);
if (!err.empty())
luaL_error(ls, err.c_str());
for (int i = 0, size = mlist.size(); i < size; ++i)
{
mons_spec mspec = mlist.get_monster(i);
if (dgn_place_monster(mspec, you.your_level, c,
false, false, false))
{
push_monster(ls, &menv[mgrd(c)]);
return (1);
}
}
}
lua_pushnil(ls);
return (1);
}
static int dgn_create_item(lua_State *ls)
{
COORDS(c, 1, 2);
if (const char *spec = lua_tostring(ls, 3))
{
item_list ilist;
const std::string err = ilist.add_item(spec);
if (!err.empty())
luaL_error(ls, err.c_str());
const int level =
lua_isnumber(ls, 4) ? lua_tointeger(ls, 4) : you.your_level;
dgn_place_multiple_items(ilist, c, level);
link_items();
}
#define BRANCH(br, pos) \
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]
#define BRANCHFN(name, type, expr) \
LUAFN(dgn_br_##name) { \
BRANCH(br, 1); \
PLUARET(type, expr); \
}
void lua_push_items(lua_State *ls, int link)
{
lua_newtable(ls);
int index = 0;
for ( ; link != NON_ITEM; link = mitm[link].link)
{
lua_pushlightuserdata(ls, &mitm[link]);
lua_rawseti(ls, -2, ++index);
}
}
if (mindex != -1)
{
if (mspec.items.size() > 0)
_dgn_give_mon_spec_items(mspec, mindex, mid, monster_level);
}
if (mindex != -1 && mspec.items.size() > 0)
_dgn_give_mon_spec_items(mspec, mindex, mid, monster_level);
-- Returns a function that changes the depth in the ziggurat to the depth
-- specified.
local function zig_depth_increment()
return function (...)
zig().depth = zig().depth + 1
end
local wall_colours = {
"blue", "red", "lightblue", "magenta", "green", "white"
}
local function wall_colour()
return util.random_from(wall_colours)
end
local function random_floor_colour()
return wall_colour()
end
local function map_area()
local base_area = 20 + 8 * zig_depth()
return 2 * base_area + crawl.random2(base_area)
end
local function clamp_in(val, low, high)
if val < low then
return low
elseif val > high then
return high
else
return val
end
end
local function clamp_in_bounds(x, y)
return clamp_in(x, 1, dgn.GXM - 2), clamp_in(y, 1, dgn.GYM - 2)
end
local function rectangle_dimensions()
local area = map_area()
local cx, cy = dgn.GXM / 2, dgn.GYM / 2
local asqrt = math.sqrt(area)
local b = crawl.random_range(1 + asqrt / 2, asqrt + 1)
local a = math.floor((area + b - 1) / b)
local a2 = math.floor(a / 2) + (a % 2);
local b2 = math.floor(b / 2) + (b % 2);
local x1, y1 = clamp_in_bounds(cx - a2, cy - b2)
local x2, y2 = clamp_in_bounds(cx + a2, cy + b2)
return x1, y1, x2, y2
end
local mons_populations = {
"Elf:7", "Orc:4", "Vault:8", "Slime:6",
"Snake:5", "Lair:10", "Tomb:3", "Crypt:5",
"Abyss", "Shoal:5", "Pan"
}
local function set_floor_colour(colour)
if not zig().level.floor_colour then
zig().level.floor_colour = colour
dgn.change_floor_colour(colour)
end
end
local function set_random_floor_colour()
set_floor_colour( random_floor_colour() )
end
local function mons_random_gen(x, y, nth)
set_random_floor_colour()
return dgn.create_monster(x, y, "place:" ..
util.random_from(mons_populations))
end
local function mons_drac_gen(x, y, nth)
set_random_floor_colour()
return dgn.create_monster(x, y, "random draconian")
end
local function mons_panlord_gen(x, y, nth)
set_random_floor_colour()
if nth == 1 then
return dgn.create_monster(x, y, "pandemonium lord band")
else
return dgn.create_monster(x, y, "place:Pan")
end
local function monster_creator_fn(arg)
if type(arg) == "string" then
local _, _, branch = string.find(arg, "^(%w+):")
return function (x, y, nth)
if branch then
set_floor_colour(dgn.br_floorcol(branch))
end
return dgn.create_monster(x, y, "place:" .. arg)
end
else
return arg
end
end
local monster_creators =
util.map(monster_creator_fn, util.catlist(mons_populations, mons_generators))
local function ziggurat_vet_monster(fn)
return function (x, y, nth)
while true do
local mons = fn(x, y, nth)
if mons then
if mons.experience == 0 then
mons.dismiss()
else
-- Monster is ok!
return mons
end
end
end
end
end
local function choose_monster_set()
return ziggurat_vet_monster(util.random_from(monster_creators))
end
local function ziggurat_create_monsters(p)
local depth = zig_depth()
local hd_pool = depth * (depth + 8)
local function mons_place_p(point)
return not dgn.mons_at(point.x, point.y)
end
local mfn = choose_monster_set()
local nth = 1
-- No monsters
while hd_pool > 0 do
local place = dgn.find_adjacent_point(p, mons_place_p)
local mons = mfn(place.x, place.y, nth)
if mons then
nth = nth + 1
hd_pool = hd_pool - mons.hd
end
end
end
local function flip_rectangle(x1, y1, x2, y2)
local cx = math.floor((x1 + x2) / 2)
local cy = math.floor((y1 + y2) / 2)
local nx1 = cx + y1 - cy
local nx2 = cx + y2 - cy
local ny1 = cy + x1 - cx
local ny2 = cy + x2 - cx
return { nx1, ny1, nx2, ny2 }
end
local function ziggurat_create_loot(c)
local nloot = zig_depth()
local depth = zig_depth()
local function is_free_space(p)
return dgn.grid(p.x, p.y) == dgn.fnum("floor") and
#dgn.items_at(p.x, p.y) == 0
end
local function free_space_do(fn)
local p = dgn.find_adjacent_point(c, is_free_space)
if p then
fn(p)
end
end
for i = 1, nloot do
if crawl.one_chance_in(depth) then
for j = 1, 4 do
free_space_do(function (p)
dgn.create_item(p.x, p.y, "*", 20)
end)
end
else
free_space_do(function (p)
dgn.create_item(p.x, p.y, "|", 20)
end)
end
end
end
local my = math.floor((y1 + y2) / 2)
zigstair(x1, my, "stone_arch", "stone_stairs_up_i")
zigstair(x2, my, "stone_stairs_down_i", zig_go_deeper)
grid(x2, my + 1, "exit_portal_vault")
dgn.fill_area(unpack( util.catlist(flip_rectangle(x1, y1, x2, y2),
{ "floor" }) ) )
local cy = math.floor((y1 + y2) / 2)
local entry = { x = x1, y = cy }
local exit = { x = x2, y = cy }
if zig_depth() % 2 == 0 then
entry, exit = exit, entry
end
crawl.mpr("Ziggurat depth is now " .. zig_depth())
zigstair(entry.x, entry.y, "stone_arch", "stone_stairs_up_i")
zigstair(exit.x, exit.y, "stone_stairs_down_i", zig_go_deeper)
grid(exit.x, exit.y + 1, "exit_portal_vault")
grid(exit.x, exit.y - 1, "exit_portal_vault")
ziggurat_create_loot(exit)
--ziggurat_create_monsters(exit)
dgn.colour_map(function (x, y)
return dgn.grid(x, y) == dgn.fnum("stone_wall")
end,
zig().colour)
crawl.mpr("Ziggurat depth is now " .. zig_depth(), "diagnostic")
------------------------------------------------------------------------------
-- util.lua
-- Lua utilities.
------------------------------------------------------------------------------
util = { }
function util.catlist(...)
local res = { }
local tables = { ... }
if #tables == 1 then
return tables[1]
else
for _, tab in ipairs(tables) do
for _, val in ipairs(tab) do
table.insert(res, val)
end
end
end
return res
end
function util.cathash(...)
local res = { }
local tables = { ... }
if #tables == 1 then
return tables[1]
else
for _, tab in ipairs(tables) do
for key, val in ipairs(tab) do
res[key] = val
end
end
end
return res
end
function util.map(fn, ...)
local lists = { ... }
local res = { }
if #lists == 0 then
return res
elseif #lists == 1 then
for _, val in ipairs(lists[1]) do
table.insert(res, fn(val))
end
else
for i = 1, #lists[1] do
local args = { }
for _, list in ipairs(lists) do
if not list[i] then
break
end
table.insert(args, list[i])
end
if #args < #lists then
break
end
table.insert(res, fn(unpack(args)))
end
end
return res
end
function util.random_from(list)
return list[ crawl.random2(#list) + 1 ]
end
function dgn.adjacent_points(c, faccept)
local plist = { }
local compass = {
{ 0, -1 }, { 1, 0 }, { 0, 1 }, { -1, 0 },
{ -1, -1 }, { 1, -1 }, { 1, 1 }, { -1, 1 }
}
for _, cp in ipairs(compass) do
local p = { x = c.x + cp[1], y = c.y + cp[2] }
if dgn.in_bounds(p.x, p.y) and faccept(p) then
table.insert(plist, p)
end
end
return plist
end
function dgn.find_adjacent_point(c, fcondition, fpassable)
local mapped_points = { }
local function pstr(p)
return p.x .. "," .. p.y
end
local function seen(p)
return mapped_points[pstr(p)]
end
local function record(p, val)
mapped_points[pstr(p)] = val or 1
end
if not fpassable then
fpassable = function (p)
return dgn.is_passable(p.x, p.y)
end
end
local iter_points = { { }, { } }
local iter = 1
table.insert(iter_points[iter], c)
local function next_iter()
if iter == 1 then
return 2
else
return 1
end
end
local distance = 1
record(c, distance)
while #iter_points[iter] > 0 do
for _, p in ipairs(iter_points[iter]) do
if fcondition(p) then
return p
end
-- Add adjacent points to queue.
for _, np in ipairs(dgn.adjacent_points(p, fpassable)) do
if not seen(np) then
table.insert(iter_points[next_iter()], np)
record(np, distance + 1)
end
end
end
iter_points[iter] = { }
iter = next_iter()
distance = distance + 1
end
-- No suitable point.
return nil
end
function dgn.colour_map(fselect, colour)
for x = 0, dgn.GXM - 1 do
for y = 0, dgn.GYM - 1 do
if fselect(x, y) then
dgn.colour_at(x, y, colour)
end
end
end
end
static int l_mons_name(lua_State *ls, monsters *mons, const char *attr)
#define MDEFN(name, closure) \
static int l_mons_##name(lua_State *ls, monsters *mons, const char *attrs) \
{ \
lua_pushlightuserdata(ls, mons); \
lua_pushcclosure(ls, l_mons_do_dismiss, 1); \
return (1); \
}
#define ASSERT_DLUA \
do { \
if (CLua::get_vm(ls).managed_vm) \
luaL_error(ls, "Operation forbidden in end-user script"); \
} while (false)
MDEF(name)
static int l_mons_x(lua_State *ls, monsters *mons, const char *attr)
MDEF(x)
{
PLUARET(number, int(mons->pos().x) - int(you.pos().x));
}
MDEF(y)
{
PLUARET(number, int(mons->pos().y) - int(you.pos().y));
}
MDEF(hd)
{
PLUARET(number, mons->hit_dice);
}
static int l_mons_do_dismiss(lua_State *ls)
lua_pushnumber(ls, int(mons->pos().x) - int(you.pos().x));
return (1);
// dismiss is only callable from dlua, not from managed VMs (i.e.
// end-user scripts cannot dismiss monsters).
ASSERT_DLUA;
monsters *mons =
util_get_userdata<monsters>(ls, lua_upvalueindex(1));
if (mons->alive())
{
mons->flags |= MF_HARD_RESET;
monster_die(mons, KILL_DISMISSED, NON_MONSTER);
}
return (0);
{
switch(place.level_type)
{
case LEVEL_DUNGEON:
level_number = absdungeon_depth(place.branch, place.depth);
break;
case LEVEL_ABYSS:
level_number = 51;
break;
case LEVEL_PANDEMONIUM:
level_number = 52;
break;
default:
level_number = you.your_level;
}
}
level_number = place.absdepth();
{
switch(place.level_type)
{
case LEVEL_DUNGEON:
level_number = absdungeon_depth(place.branch, place.depth);
break;
case LEVEL_ABYSS:
level_number = 51;
break;
case LEVEL_PANDEMONIUM:
level_number = 52;
break;
default:
level_number = you.your_level;
}
}
level_number = place.absdepth();