diff --git a/dat/hellfill.lua b/dat/hellfill.lua index 820863a04..45f1f7342 100644 --- a/dat/hellfill.lua +++ b/dat/hellfill.lua @@ -159,6 +159,7 @@ xxxxxx.xxxxxx ]], contents = function() des.non_diggable(selection.area(2,2, 10,8)); des.region(selection.area(4,4, 8,6), "lit"); + des.exclusion({ type = "teleport", region = { 2,2, 10,8 } }); if (coldhell) then des.replace_terrain({ region = {1,1, 11,9}, fromterrain="L", toterrain="P" }); end @@ -228,6 +229,7 @@ BBBBBBB]], contents = function() .......... .......... ..........]], contents = function() + des.exclusion({ type = "teleport", region = { 4,4, 5,5 } }); local mons = { "Angel", "D", "H", "L" }; des.monster(mons[math.random(1, #mons)], 4,4); end }); @@ -245,6 +247,7 @@ BBBBBBB]], contents = function() .}}}}}}}. ......... ]], contents = function(rm) + des.exclusion({ type = "teleport", region = { 3,3, 5,5 } }); des.monster("L",04,04) end }) end, diff --git a/dat/themerms.lua b/dat/themerms.lua index f45acae26..63f3705eb 100644 --- a/dat/themerms.lua +++ b/dat/themerms.lua @@ -662,6 +662,7 @@ xx|.....|xx shuffle(nasty_undead); des.monster(nasty_undead[1], 2, 2); + des.exclusion({ type = "teleport", region = { 2,2, 3,3 } }); end }); end, diff --git a/doc/fixes3-7-0.txt b/doc/fixes3-7-0.txt index 6c8166b78..012621d6f 100644 --- a/doc/fixes3-7-0.txt +++ b/doc/fixes3-7-0.txt @@ -1228,6 +1228,7 @@ when a werecreature in human form attacked hero, it could transform to critter status highlighting for hit points didn't work as intended for up or down HP changes; 'up' rule was used for both, 'down' rule was ignored unhide an unseen water monster using a polymorph trap on land +allow defining random-teleport exclusion zones in lua Fixes to 3.7.0-x General Problems Exposed Via git Repository diff --git a/doc/lua.adoc b/doc/lua.adoc index 6ac5fd24f..96a6f9152 100644 --- a/doc/lua.adoc +++ b/doc/lua.adoc @@ -528,6 +528,18 @@ Example: des.engraving({x,y}, "engrave", "Foo"); +=== exclusion + +Exclude an area of the map from being randomly chosen target when +falling or teleporting into the level. Multiple exclusions per level are allowed. + +* type is one of "teleport", "teleport-up", or "teleport-down". + +Example: + + des.exclusion({ type = "teleport", region = { 0,0, 10,5 } }); + + === feature Create a feature, and set flags for it. diff --git a/include/decl.h b/include/decl.h index 0c8dd6307..79d8f381a 100644 --- a/include/decl.h +++ b/include/decl.h @@ -340,6 +340,7 @@ struct instance_globals_e { struct bubble *ebubbles; /* new stuff */ + struct exclusion_zone *exclusion_zones; int early_raw_messages; /* if raw_prints occurred early prior to gb.beyond_savefile_load */ diff --git a/include/dungeon.h b/include/dungeon.h index e0539d4a8..82fa2b2db 100644 --- a/include/dungeon.h +++ b/include/dungeon.h @@ -58,6 +58,14 @@ typedef struct dest_area { /* non-stairway level change identifier */ coordxy nhx, nhy; /* opposite corner of invalid area */ } dest_area; +/* teleportation exclusion zones in the level */ +typedef struct exclusion_zone { + xint16 zonetype; /* level_region_types */ + coordxy lx, ly; + coordxy hx, hy; + struct exclusion_zone *next; +} exclusion_zone; + typedef struct dungeon { /* basic dungeon identifier */ char dname[24]; /* name of the dungeon (eg. "Hell") */ char proto[15]; /* name of prototype file (eg. "tower") */ diff --git a/include/extern.h b/include/extern.h index 102e41e7c..c4e33ba70 100644 --- a/include/extern.h +++ b/include/extern.h @@ -745,6 +745,9 @@ extern char *stairs_description(stairway *, char *, boolean); extern schar print_dungeon(boolean, schar *, xint16 *); extern char *get_annotation(d_level *); extern int donamelevel(void); +extern void free_exclusions(void); +extern void save_exclusions(NHFILE *); +extern void load_exclusions(NHFILE *); extern int dooverview(void); extern void show_overview(int, int); extern void rm_mapseen(int); diff --git a/include/patchlevel.h b/include/patchlevel.h index 376b90eb5..14291b14a 100644 --- a/include/patchlevel.h +++ b/include/patchlevel.h @@ -17,7 +17,7 @@ * Incrementing EDITLEVEL can be used to force invalidation of old bones * and save files. */ -#define EDITLEVEL 88 +#define EDITLEVEL 89 /* * Development status possibilities. diff --git a/src/decl.c b/src/decl.c index 2ba15b13f..8e4956da7 100644 --- a/src/decl.c +++ b/src/decl.c @@ -342,6 +342,7 @@ const struct instance_globals_e g_init_e = { /* mkmaze.c */ UNDEFINED_PTR, /* ebubbles */ /* new */ + NULL, /* exclusion_zones */ 0, /* early_raw_messages */ TRUE, /* havestate*/ IVMAGIC /* e_magic to validate that structure layout has been preserved */ diff --git a/src/dungeon.c b/src/dungeon.c index 231f4e105..52ca6fbab 100644 --- a/src/dungeon.c +++ b/src/dungeon.c @@ -2675,6 +2675,66 @@ donamelevel(void) return ECMD_OK; } +/* exclusion zones */ +void +free_exclusions(void) +{ + struct exclusion_zone *ez = ge.exclusion_zones; + + while (ez) { + struct exclusion_zone *nxtez = ez->next; + + free(ez); + ez = nxtez; + } + ge.exclusion_zones = (struct exclusion_zone *) 0; +} + +void +save_exclusions(NHFILE *nhfp) +{ + struct exclusion_zone *ez; + int nez; + + for (nez = 0, ez = ge.exclusion_zones; ez; ez = ez->next, ++nez) ; + + if (nhfp->structlevel) + bwrite(nhfp->fd, (genericptr_t) &nez, sizeof nez); + + for (ez = ge.exclusion_zones; ez; ez = ez->next) { + if (nhfp->structlevel) { + bwrite(nhfp->fd, (genericptr_t) &ez->zonetype, sizeof ez->zonetype); + bwrite(nhfp->fd, (genericptr_t) &ez->lx, sizeof ez->lx); + bwrite(nhfp->fd, (genericptr_t) &ez->ly, sizeof ez->ly); + bwrite(nhfp->fd, (genericptr_t) &ez->hx, sizeof ez->hx); + bwrite(nhfp->fd, (genericptr_t) &ez->hy, sizeof ez->hy); + } + } +} + +void +load_exclusions(NHFILE *nhfp) +{ + struct exclusion_zone *ez; + int nez = 0; + + if (nhfp->structlevel) + mread(nhfp->fd, (genericptr_t) &nez, sizeof nez); + + while (nez-- > 0) { + ez = (struct exclusion_zone *) alloc(sizeof *ez); + if (nhfp->structlevel) { + mread(nhfp->fd, (genericptr_t) &ez->zonetype, sizeof ez->zonetype); + mread(nhfp->fd, (genericptr_t) &ez->lx, sizeof ez->lx); + mread(nhfp->fd, (genericptr_t) &ez->ly, sizeof ez->ly); + mread(nhfp->fd, (genericptr_t) &ez->hx, sizeof ez->hx); + mread(nhfp->fd, (genericptr_t) &ez->hy, sizeof ez->hy); + } + ez->next = ge.exclusion_zones; + ge.exclusion_zones = ez; + } +} + /* find the particular mapseen object in the chain; may return null */ static mapseen * find_mapseen(d_level *lev) diff --git a/src/mklev.c b/src/mklev.c index 2b59c3b3f..e70d463fb 100644 --- a/src/mklev.c +++ b/src/mklev.c @@ -822,6 +822,7 @@ clear_level_structures(void) stairway_free_all(); gm.made_branch = FALSE; clear_regions(); + free_exclusions(); reset_xystart_size(); if (gl.lev_message) { free(gl.lev_message); diff --git a/src/mkmaze.c b/src/mkmaze.c index de551bbd9..1a3aec851 100644 --- a/src/mkmaze.c +++ b/src/mkmaze.c @@ -297,6 +297,22 @@ maze0xy(coord *cc) return; } +static boolean +is_exclusion_zone(xint16 type, coordxy x, coordxy y) +{ + struct exclusion_zone *ez = ge.exclusion_zones; + + while (ez) { + if (((type == LR_DOWNTELE && (ez->zonetype == LR_DOWNTELE || ez->zonetype == LR_TELE)) + || (type == LR_UPTELE && (ez->zonetype == LR_UPTELE || ez->zonetype == LR_TELE)) + || type == ez->zonetype) + && within_bounded_area(x, y, ez->lx, ez->ly, ez->hx, ez->hy)) + return TRUE; + ez = ez->next; + } + return FALSE; +} + /* * Bad if: * pos is occupied OR @@ -375,7 +391,7 @@ put_lregion_here( { struct monst *mtmp; - if (bad_location(x, y, nlx, nly, nhx, nhy)) { + if (bad_location(x, y, nlx, nly, nhx, nhy) || is_exclusion_zone(rtype, x, y)) { if (!oneshot) { return FALSE; /* caller should try again */ } else { @@ -389,7 +405,7 @@ put_lregion_here( mtmp->mtrapped = 0; deltrap(t); } - if (bad_location(x, y, nlx, nly, nhx, nhy)) + if (bad_location(x, y, nlx, nly, nhx, nhy) || is_exclusion_zone(rtype, x, y)) return FALSE; } } diff --git a/src/restore.c b/src/restore.c index 8a6f61ea6..9b19a4974 100644 --- a/src/restore.c +++ b/src/restore.c @@ -1141,6 +1141,7 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) restdamage(nhfp); rest_regions(nhfp); rest_bubbles(nhfp); /* for water and air; empty marker on other levels */ + load_exclusions(nhfp); if (ghostly) { stairway *stway = gs.stairs; diff --git a/src/save.c b/src/save.c index babd935e6..7d04cb1fe 100644 --- a/src/save.c +++ b/src/save.c @@ -553,6 +553,7 @@ savelev_core(NHFILE *nhfp, xint8 lev) savedamage(nhfp); /* pending shop wall and/or floor repair */ save_regions(nhfp); save_bubbles(nhfp, lev); /* for water and air */ + save_exclusions(nhfp); if (nhfp->mode != FREEING) { if (nhfp->structlevel) diff --git a/src/sp_lev.c b/src/sp_lev.c index f5fb64ca9..2bd7419c8 100644 --- a/src/sp_lev.c +++ b/src/sp_lev.c @@ -141,6 +141,7 @@ int lspo_ladder(lua_State *); int lspo_level_flags(lua_State *); int lspo_level_init(lua_State *); int lspo_levregion(lua_State *); +int lspo_exclusion(lua_State *); int lspo_map(lua_State *); int lspo_mazewalk(lua_State *); int lspo_message(lua_State *); @@ -6073,6 +6074,35 @@ lspo_levregion(lua_State *L) return 0; } +/* exclusion({ type = "teleport", region = { x1,y1, x2,y2 } }); */ +int +lspo_exclusion(lua_State *L) +{ + static const char *const ez_types[] = { + "teleport", "teleport-up", "teleport-down", NULL + }; + static const int ez_types2i[] = { + LR_TELE, LR_UPTELE, LR_DOWNTELE, 0 + }; + struct exclusion_zone *ez = (struct exclusion_zone *) alloc(sizeof *ez); + lua_Integer x1,y1,x2,y2; + + create_des_coder(); + lcheck_param_table(L); + ez->zonetype = ez_types2i[get_table_option(L, "type", "teleport", + ez_types)]; + get_table_region(L, "region", &x1, &y1, &x2, &y2, FALSE); + ez->lx = x1; + ez->ly = y1; + ez->hx = x2; + ez->hy = y2; + cvt_to_abscoord(&ez->lx, &ez->ly); + cvt_to_abscoord(&ez->hx, &ez->hy); + ez->next = ge.exclusion_zones; + ge.exclusion_zones = ez; + return 0; +} + static void sel_set_lit(coordxy x, coordxy y, genericptr_t arg) { @@ -6960,6 +6990,7 @@ static const struct luaL_Reg nhl_functions[] = { { "drawbridge", lspo_drawbridge }, { "region", lspo_region }, { "levregion", lspo_levregion }, + { "exclusion", lspo_exclusion }, { "wallify", lspo_wallify }, { "wall_property", lspo_wall_property }, { "non_diggable", lspo_non_diggable },