diff --git a/dat/themerms.lua b/dat/themerms.lua index c1353df6d..75145eefb 100644 --- a/dat/themerms.lua +++ b/dat/themerms.lua @@ -220,6 +220,19 @@ themeroom_fills = { end; locs:iterate(func); end, + + -- Teleportation hub + function(rm) + local locs = selection.room():filter_mapchar("."); + for i = 1, 2 + nh.rn2(3) do + local pos = locs:rndcoord(1); + if (pos.x > 0) then + pos.x = pos.x + rm.region.x1 - 1; + pos.y = pos.y + rm.region.y1; + table.insert(postprocess, { handler = make_a_trap, data = { type = "teleport", seen = true, coord = pos, teledest = 1 } }); + end + end + end, }; themerooms = { @@ -871,6 +884,17 @@ function make_garden_walls(data) des.replace_terrain({ selection = sel, fromterrain="w", toterrain = "T" }); end +-- postprocess callback: make a trap +function make_a_trap(data) + if (data.teledest == 1 and data.type == "teleport") then + local locs = selection.negate():filter_mapchar("."); + repeat + data.teledest = locs:rndcoord(1); + until (data.teledest.x ~= data.coord.x and data.teledest.y ~= data.coord.y); + end + des.trap(data); +end + -- called once after the whole level has been generated function post_level_generate() for i, v in ipairs(postprocess) do diff --git a/doc/lua.adoc b/doc/lua.adoc index c7f9803ce..84758dc3f 100644 --- a/doc/lua.adoc +++ b/doc/lua.adoc @@ -1002,7 +1002,8 @@ Example: === trap -Create a trap. The `launchfrom` is relative to the rolling boulder trap coord. +Create a trap. The `launchfrom` is relative to the rolling boulder trap coord, +but `teledest` is absolute. Example: @@ -1011,6 +1012,7 @@ Example: des.trap({ type = "web", coord = {2, 2}, spider_on_web = false, seen = true }); des.trap({ type = "falling rock", victim = false }); des.trap({ type = "rolling boulder", coord = {7, 5}, launchfrom = {-2, -2} }); + des.trap({ type = "teleport", coord = {7, 5}, teledest = {2, 2} }); des.trap("hole", 3, 4); des.trap("level teleport", {5, 8}); des.trap("rust") diff --git a/include/trap.h b/include/trap.h index 463fe9413..abfb0833f 100644 --- a/include/trap.h +++ b/include/trap.h @@ -20,6 +20,7 @@ struct trap { coordxy tx, ty; d_level dst; /* destination for portals/holes/trapdoors */ coord launch; +#define teledest launch /* x,y destination for teleport traps, if > 0 */ Bitfield(ttyp, 5); Bitfield(tseen, 1); Bitfield(once, 1); diff --git a/src/sp_lev.c b/src/sp_lev.c index 444407e63..817623089 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -4362,7 +4362,19 @@ lspo_trap(lua_State *L) lua_pop(L, 1); gl.launchplace.x = lx; gl.launchplace.y = ly; - } + } else + lua_pop(L, 1); + + lua_getfield(L, -1, "teledest"); + if (lua_type(L, -1) == LUA_TTABLE) { + lua_Integer lx = -1, ly = -1; + + (void) get_coord(L, -1, &lx, &ly); + lua_pop(L, 1); + gl.launchplace.x = lx; + gl.launchplace.y = ly; + } else + lua_pop(L, 1); } if (tmptrap.type == NO_TRAP) diff --git a/src/teleport.c b/src/teleport.c index df8c1e29b..e50fb79aa 100644 --- a/src/teleport.c +++ b/src/teleport.c @@ -1098,6 +1098,8 @@ dotele( if (next_to_u()) { if (trap && trap_once) vault_tele(); + else if (trap && isok(trap->teledest.x, trap->teledest.y)) + teleds(trap->teledest.x, trap->teledest.y, TELEDS_TELEPORT); else tele(); (void) next_to_u(); @@ -1450,6 +1452,20 @@ tele_trap(struct trap *trap) deltrap(trap); newsym(u.ux, u.uy); /* get rid of trap symbol */ vault_tele(); + } else if (isok(trap->teledest.x, trap->teledest.y)) { + coord cc; + struct monst *mtmp = m_at(trap->teledest.x, trap->teledest.y); + + if (mtmp) { + if (!enexto(&cc, mtmp->mx, mtmp->my, mtmp->data)) { + /* could not find some other place to put mtmp; the level must + * be nearly or completely full */ + You1(shudder_for_moment); + return; + } + rloc_to(mtmp, cc.x, cc.y); + } + teleds(trap->teledest.x, trap->teledest.y, TELEDS_TELEPORT); } else tele(); } @@ -1882,7 +1898,16 @@ mtele_trap(struct monst *mtmp, struct trap *trap, int in_sight) */ if (trap->once) mvault_tele(mtmp); - else + else if (isok(trap->teledest.x, trap->teledest.y)) { + /* monster teleporting onto hero's or another monster's spot does + * not work the same as hero teleporting onto monster's spot where + * the incoming monster displaces the resident to the nearest + * possible space - instead it just doesn't work. */ + if (!(m_at(trap->teledest.x, trap->teledest.y) + || u_at(trap->teledest.x, trap->teledest.y))) { + rloc_to_core(mtmp, trap->teledest.x, trap->teledest.y, RLOC_MSG); + } + } else (void) rloc(mtmp, RLOC_NONE); if (in_sight) { diff --git a/src/trap.c b/src/trap.c index 5ea801b20..ab99adf5e 100644 --- a/src/trap.c +++ b/src/trap.c @@ -541,6 +541,15 @@ maketrap(coordxy x, coordxy y, int typ) unearth_objs(x, y); break; + case TELEP_TRAP: + if (isok(gl.launchplace.x, gl.launchplace.y)) { + ttmp->teledest.x = gx.xstart + gl.launchplace.x; + ttmp->teledest.y = gy.ystart + gl.launchplace.y; + if (ttmp->teledest.x == x && ttmp->teledest.y == y) { + impossible("making fixed-dest tele trap pointing to itself"); + } + } + break; } if (!oldplace) {