From e407af447754c700967d75b2d400a1662c6103d7 Mon Sep 17 00:00:00 2001 From: Pasi Kallinen Date: Thu, 24 Aug 2023 17:42:06 +0300 Subject: [PATCH] Allow defining random-teleport exclusion zones in lua Adds a new lua command des.exclusion({ type = "teleport", region = { x1,y1, x2,y2 } }); which allows defining "exclusion zones" in the level, areas where random teleports (or falling into the level) will never place the hero. Does not prevent targeted teleportation into the area. Breaks saves and bones. --- dat/hellfill.lua | 3 +++ dat/themerms.lua | 1 + doc/fixes3-7-0.txt | 1 + doc/lua.adoc | 12 +++++++++ include/decl.h | 1 + include/dungeon.h | 8 ++++++ include/extern.h | 3 +++ include/patchlevel.h | 2 +- src/decl.c | 1 + src/dungeon.c | 60 ++++++++++++++++++++++++++++++++++++++++++++ src/mklev.c | 1 + src/mkmaze.c | 20 +++++++++++++-- src/restore.c | 1 + src/save.c | 1 + src/sp_lev.c | 31 +++++++++++++++++++++++ 15 files changed, 143 insertions(+), 3 deletions(-) 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 },