Skip to content

Commit

Permalink
Gnomish Mines changes involving "Orctown" level variant
Browse files Browse the repository at this point in the history
 Changes to be committed:
	modified:   include/decl.h
	modified:   include/dungeon.h
	modified:   include/extern.h
	modified:   include/hack.h
	modified:   src/decl.c
	modified:   src/do_name.c
	modified:   src/dog.c
	modified:   src/dokick.c
	modified:   src/makemon.c
	modified:   src/mkmaze.c
	modified:   src/mkobj.c
	modified:   src/pager.c

This commit is an attempt to address the complaints about
the orc town variation taking away lots of stuff that is
normally available in mine town. The statement in the level
description says "A tragic accident has occurred in Frontier
Town...It has been overrun by orcs."

The changes in this commit attempt to uphold that premise,
while making things a bit more interesting and perhaps
more palatable for the player.

This update does the following in keeping with the mythos:
- While many of the orcs still remain to wander about the
  level, many of the orcs took off deeper into the mines with
  some of the stuff that they plundered. You may now be
  able to hunt some of it down.

- Adds some appearance of this particular horde of marauding
  orcs working as part of a larger collective.

- This evolves the Orc Town mine town variation into a
  a feature over multiple levels of The Gnomish Mines,
  rather than just the single-level "feature" that it was
  previously.

- You may have to work longer and a bit harder for some
  things than other mine town variations, but at least with
  these changes, there is hope that some of it may be found
  elsewhere.

Game mechanics notes (maybe spoily?)

- Add mechanism to place objects into limbo (okay, really
  place them onto the migrating_objs list for transferring
  between levels etc.) and destine them
  to become part of the monster inventory of a particular
  species. In this particular usage case, it's using the
  M2_ORC flag setting to identify the recipients.

- At present, there is no mechanism in the level compiler
  for placing objects onto the migrating objects, nor
  with more sophisticated landing logic, so a somewhat
  kludgy hard-coded fixup and supporting routines were used.
  Some day the need for that might change if additional
  capabilities move to the level compiler.

This is a NetHack-3.6.2-beta01 update. Please give it a workout.

Fixes #127
  • Loading branch information
nhmall committed Sep 18, 2018
1 parent b344000 commit 9eb7830
Show file tree
Hide file tree
Showing 12 changed files with 357 additions and 16 deletions.
1 change: 1 addition & 0 deletions include/decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ E NEARDATA struct sinfo {
} program_state;

E boolean restoring;
E boolean ransacked;

E const char quitchars[];
E const char vowels[];
Expand Down
3 changes: 2 additions & 1 deletion include/dungeon.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ typedef struct branch {
#define MIGR_WITH_HERO 9 /* mon: followers; obj: trap door */
#define MIGR_NOBREAK 1024 /* bitmask: don't break on delivery */
#define MIGR_NOSCATTER 2048 /* don't scatter on delivery */

#define MIGR_TO_SPECIES 4096 /* migrating to species as they are made */
#define MIGR_LEFTOVERS 8192 /* grab remaining MIGR_TO_SPECIES objects */
/* level information (saved via ledger number) */

struct linfo {
Expand Down
6 changes: 5 additions & 1 deletion include/extern.h
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,8 @@ E const char *NDECL(roguename);
E struct obj *FDECL(realloc_obj,
(struct obj *, int, genericptr_t, int, const char *));
E char *FDECL(coyotename, (struct monst *, char *));
E char *FDECL(rndorcname, (char *));
E struct monst *FDECL(christen_orc, (struct monst *, char *));
E const char *FDECL(noveltitle, (int *));
E const char *FDECL(lookup_novel, (const char *, int *));

Expand Down Expand Up @@ -519,6 +521,7 @@ E void FDECL(container_impact_dmg, (struct obj *, XCHAR_P, XCHAR_P));
E int NDECL(dokick);
E boolean FDECL(ship_object, (struct obj *, XCHAR_P, XCHAR_P, BOOLEAN_P));
E void FDECL(obj_delivery, (BOOLEAN_P));
E void FDECL(deliver_obj_to_mon, (struct monst *mtmp, unsigned long));
E schar FDECL(down_gate, (XCHAR_P, XCHAR_P));
E void FDECL(impact_drop, (struct obj *, XCHAR_P, XCHAR_P, XCHAR_P));

Expand Down Expand Up @@ -1283,6 +1286,7 @@ E void FDECL(new_omailcmd, (struct obj *, const char *));
E void FDECL(free_omailcmd, (struct obj *));
E struct obj *FDECL(mkobj_at, (CHAR_P, int, int, BOOLEAN_P));
E struct obj *FDECL(mksobj_at, (int, int, int, BOOLEAN_P, BOOLEAN_P));
E struct obj *FDECL(mksobj_migr_to_species, (int, unsigned, BOOLEAN_P, BOOLEAN_P));
E struct obj *FDECL(mkobj, (CHAR_P, BOOLEAN_P));
E int NDECL(rndmonnum);
E boolean FDECL(bogon_is_pname, (CHAR_P));
Expand Down Expand Up @@ -1753,7 +1757,7 @@ E char *FDECL(self_lookat, (char *));
E void FDECL(mhidden_description, (struct monst *, BOOLEAN_P, char *));
E boolean FDECL(object_from_map, (int,int,int,struct obj **));
E int FDECL(do_screen_description, (coord, BOOLEAN_P, int, char *,
const char **));
const char **, struct permonst **));
E int FDECL(do_look, (int, coord *));
E int NDECL(dowhatis);
E int NDECL(doquickwhatis);
Expand Down
6 changes: 6 additions & 0 deletions include/hack.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,12 @@ typedef struct sortloot_item Loot;
#define SHIFT_SEENMSG 0x01 /* put out a message if in sight */
#define SHIFT_MSG 0x02 /* always put out a message */

/* flags for deliver_obj_to_mon */
#define DF_NONE 0x00
#define DF_RANDOM2 0x01
#define DF_RANDOM3 0x02
#define DF_ALL 0x04

/* special mhpmax value when loading bones monster to flag as extinct or
* genocided */
#define DEFUNCT_MONSTER (-100)
Expand Down
1 change: 1 addition & 0 deletions src/decl.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ NEARDATA char pl_fruit[PL_FSIZ] = DUMMY;
NEARDATA struct fruit *ffruit = (struct fruit *) 0;

NEARDATA char tune[6] = DUMMY;
NEARDATA boolean ransacked = 0;

const char *occtxt = DUMMY;
const char quitchars[] = " \r\n\033";
Expand Down
46 changes: 44 additions & 2 deletions src/do_name.c
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,7 @@ int cx, cy;

cc.x = cx;
cc.y = cy;
if (do_screen_description(cc, TRUE, sym, tmpbuf, &firstmatch)) {
if (do_screen_description(cc, TRUE, sym, tmpbuf, &firstmatch, (struct permonst **)0)) {
(void) coord_desc(cx, cy, tmpbuf, iflags.getpos_coords);
custompline(SUPPRESS_HISTORY,
"%s%s%s%s%s", firstmatch, *tmpbuf ? " " : "", tmpbuf,
Expand Down Expand Up @@ -593,7 +593,8 @@ int gloc;
any.a_int = i + 1;
tmpcc.x = garr[i].x;
tmpcc.y = garr[i].y;
if (do_screen_description(tmpcc, TRUE, sym, tmpbuf, &firstmatch)) {
if (do_screen_description(tmpcc, TRUE, sym, tmpbuf,
&firstmatch, (struct permonst **)0)) {
(void) coord_desc(garr[i].x, garr[i].y, tmpbuf,
iflags.getpos_coords);
Sprintf(fullbuf, "%s%s%s", firstmatch,
Expand Down Expand Up @@ -2067,6 +2068,47 @@ char *buf;
return buf;
}

char *
rndorcname(s)
char *s;
{
int i;
const char *v[] = {"a", "ai", "og", "u"};
const char *snd[] = {"gor", "gris", "un", "bane", "ruk",
"oth","ul", "z", "thos","akh","hai"};
int vstart = rn2(2);

if (s) {
*s = '\0';
for (i = 0; i < rn2(2) + 3; ++i) {
vstart = 1 - vstart; /* 0 -> 1, 1 -> 0 */
if (!rn2(30) && i > 0)
(void) strcat(s, "-");
(void) sprintf(eos(s), "%s", vstart ? v[rn2(SIZE(v))] :
snd[rn2(SIZE(snd))]);
}
}
return s;
}

struct monst *
christen_orc(mtmp, gang)
struct monst *mtmp;
char *gang;
{
size_t sz = 0;
char buf[BUFSZ], buf2[BUFSZ], *orcname;

orcname = rndorcname(buf2);
sz = strlen(gang) + strlen(orcname) + strlen(" of ");
if (buf && gang && orcname && (sz < (BUFSZ - 1))) {
Sprintf(buf, "%s of %s",
upstart(orcname), upstart(gang));
mtmp = christen_monst(mtmp, buf);
}
return mtmp;
}

/* make sure "The Colour of Magic" remains the first entry in here */
static const char *const sir_Terry_novels[] = {
"The Colour of Magic", "The Light Fantastic", "Equal Rites", "Mort",
Expand Down
6 changes: 6 additions & 0 deletions src/dog.c
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,12 @@ boolean with_you;
break;
}

if ((mtmp->mspare1 & MIGR_LEFTOVERS) != 0L) {
/* Pick up the rest of the MIGR_TO_SPECIES objects */
if (migrating_objs)
deliver_obj_to_mon(mtmp, DF_ALL);
}

if (xlocale && wander) {
/* monster moved a bit; pick a nearby location */
/* mnearto() deals w/stone, et al */
Expand Down
45 changes: 45 additions & 0 deletions src/dokick.c
Original file line number Diff line number Diff line change
Expand Up @@ -1612,6 +1612,9 @@ boolean near_hero;
continue;

where = (int) (otmp->owornmask & 0x7fffL); /* destination code */
if ((where & MIGR_TO_SPECIES) != 0)
continue;

nobreak = (where & MIGR_NOBREAK) != 0;
noscatter = (where & MIGR_WITH_HERO) != 0;
where &= ~(MIGR_NOBREAK | MIGR_NOSCATTER);
Expand Down Expand Up @@ -1667,6 +1670,48 @@ boolean near_hero;
}
}

void
deliver_obj_to_mon(mtmp, deliverflags)
struct monst *mtmp;
unsigned long deliverflags;
{
struct obj *otmp, *otmp2;
int where, cnt = 0, maxobj = 0;

if (deliverflags & DF_RANDOM3)
maxobj = rn2(3) + 1;
else if (deliverflags & DF_RANDOM2)
maxobj = rn2(2) + 1;
else if (deliverflags == DF_NONE)
maxobj = 1;

for (otmp = migrating_objs; otmp; otmp = otmp2) {
otmp2 = otmp->nobj;
where = (int) (otmp->owornmask & 0x7fffL); /* destination code */
if ((where & MIGR_TO_SPECIES) == 0)
continue;

if ((mtmp->data->mflags2 & otmp->corpsenm) != 0) {
obj_extract_self(otmp);
otmp->owornmask = 0L;
otmp->ox = otmp->oy = 0;

/* special treatment for orcs and their kind */
if ((otmp->corpsenm & M2_ORC) != 0 && has_oname(otmp)) {
if (!has_mname(mtmp))
mtmp = christen_orc(mtmp, ONAME(otmp));
free_oname(otmp);
}
otmp->corpsenm = 0;
(void) add_to_minv(mtmp, otmp);
cnt++;
if (maxobj && cnt >= maxobj)
break;
/* getting here implies DF_ALL */
}
}
}

STATIC_OVL void
otransit_msg(otmp, nodrop, num)
register struct obj *otmp;
Expand Down
3 changes: 3 additions & 0 deletions src/makemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -1390,6 +1390,9 @@ int mmflags;
mtmp->mstrategy |= STRAT_APPEARMSG;
}

if (allow_minvent && migrating_objs)
deliver_obj_to_mon(mtmp, DF_RANDOM3); /* in case there's waiting items */

if (!in_mklev)
newsym(mtmp->mx, mtmp->my); /* make sure the mon shows up */

Expand Down
142 changes: 142 additions & 0 deletions src/mkmaze.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ STATIC_DCL boolean FDECL(put_lregion_here, (XCHAR_P, XCHAR_P, XCHAR_P,
STATIC_DCL void NDECL(baalz_fixup);
STATIC_DCL void NDECL(setup_waterlevel);
STATIC_DCL void NDECL(unsetup_waterlevel);
STATIC_DCL void FDECL(check_ransacked, (char *));
STATIC_DCL void FDECL(migr_booty_item, (int, const char *));
STATIC_DCL void FDECL(migrate_orc, (struct monst *, unsigned long));
STATIC_DCL void NDECL(stolen_booty);

/* adjust a coordinate one step in the specified direction */
#define mz_move(X, Y, dir) \
Expand Down Expand Up @@ -610,13 +614,150 @@ fixup_special()
} else if (on_level(&u.uz, &baalzebub_level)) {
/* custom wallify the "beetle" potion of the level */
baalz_fixup();
} else if (u.uz.dnum == mines_dnum && ransacked) {
stolen_booty();
}

if (lregions)
free((genericptr_t) lregions), lregions = 0;
num_lregions = 0;
}

void
check_ransacked(s)
char *s;
{
/* this kludge only works as long as orctown is minetn-1 */
if (dungeons[u.uz.dnum].dname
&& !strcmp(dungeons[u.uz.dnum].dname, "The Gnomish Mines")
&& !strcmp(s, "minetn-1"))
ransacked = 1;
else
ransacked = 0;
}

#define ORC_LEADER 1

void
migrate_orc(mtmp, flags)
struct monst *mtmp;
unsigned long flags;
{
int nlev, max_depth, cur_depth;
d_level dest;

cur_depth = (int) depth(&u.uz);
max_depth = dunlevs_in_dungeon(&u.uz) +
(dungeons[u.uz.dnum].depth_start - 1);
if (flags == ORC_LEADER) {
/* Note that the orc leader will take possession of any
* remaining stuff not already delivered to other
* orcs between here and the bottom of the mines.
*/
nlev = max_depth;
mtmp->mspare1 = MIGR_LEFTOVERS;
} else {
nlev = rn2(max_depth - cur_depth) + cur_depth + 1;
if (nlev == cur_depth)
nlev++;
if (nlev > max_depth)
nlev = max_depth;
mtmp->mspare1 = 0L;
}
get_level(&dest, nlev);
migrate_to_level(mtmp, ledger_no(&dest), MIGR_RANDOM, (coord *) 0);
}

void
migr_booty_item(otyp, gang)
int otyp;
const char *gang;
{
struct obj *otmp;
otmp = mksobj_migr_to_species(otyp, (unsigned long) M2_ORC, FALSE, FALSE);
if (otmp && gang) {
new_oname(otmp, strlen(gang) + 1); /* removes old name if one is present */
Strcpy(ONAME(otmp), gang);
}
}

void
stolen_booty(VOID_ARGS)
{
char *gang, gang_name[BUFSZ];
struct monst *mtmp;
int cnt, i, otyp;

/*
* --------------------------------------------------------
* Mythos:
*
* A tragic accident has occurred in Frontier Town...
* It has been overrun by orcs.
*
* The booty that the orcs took from the town is now
* in the possession of the orcs that did this and
* have long since fled the level.
* --------------------------------------------------------
*/

gang = rndorcname(gang_name);
/* create the leader of the orc gang */
mtmp = makemon(&mons[PM_ORC_CAPTAIN], 0, 0, MM_NONAME);
if (mtmp) {
mtmp = christen_monst(mtmp, upstart(gang));
mtmp->mpeaceful = 0;
migrate_orc(mtmp, ORC_LEADER);
}
/* create the stuff that the rest of the gang took */
cnt = rn2(3) + 1;
for (i = 0; i < cnt; ++i)
migr_booty_item(rn2(4) ? TALLOW_CANDLE : WAX_CANDLE, gang);
cnt = rn2(2) + 1;
for (i = 0; i < cnt; ++i)
migr_booty_item(SKELETON_KEY, gang);
migr_booty_item(rn2(2) ? LONG_SWORD : SILVER_SABER, gang);
otyp = rn2((GAUNTLETS_OF_DEXTERITY - LEATHER_GLOVES) + 1) + LEATHER_GLOVES;
migr_booty_item(otyp, gang);
cnt = rn2(9) + 1;
for (i = 0; i < cnt; ++i) {
/* Food items - but no lembas! (or some other weird things) */
otyp = rn2((TIN - TRIPE_RATION) + 1) + TRIPE_RATION;
if (otyp != LEMBAS_WAFER && otyp != GLOB_OF_GRAY_OOZE &&
otyp != GLOB_OF_BROWN_PUDDING && otyp != GLOB_OF_GREEN_SLIME &&
otyp != GLOB_OF_BLACK_PUDDING && otyp != MEAT_STICK &&
otyp != MEATBALL && otyp != MEAT_STICK && otyp != MEAT_RING &&
otyp != HUGE_CHUNK_OF_MEAT && otyp != CORPSE)
migr_booty_item(otyp, gang);
}
/* Make most of the orcs on the level be part of the invading gang */
for (mtmp = fmon; mtmp; mtmp = mtmp->nmon) {
if (DEADMONSTER(mtmp))
continue;

if (is_orc(mtmp->data) && !has_mname(mtmp) && rn2(10))
mtmp = christen_orc(mtmp, upstart(gang));
}
/* Lastly, ensure there's a few more orcs from the gang along the way
* The mechanics are such that they aren't actually identified as
* members of the invading gang until they get their spoils assigned
* to the inventory; handled during that assignment.
*/
cnt = rn2(6) + 1;
for (i = 0; i < cnt; ++i) {
int mtyp;

mtyp = rn2((PM_ORC_SHAMAN - PM_ORC) + 1) + PM_ORC;
mtmp = makemon(&mons[mtyp], 0, 0, MM_NONAME);
if (mtmp)
migrate_orc(mtmp, 0L);
}

ransacked = 0;
}

#undef ORC_LEADER

boolean
maze_inbounds(x, y)
int x, y;
Expand Down Expand Up @@ -819,6 +960,7 @@ const char *s;
}

if (*protofile) {
check_ransacked(protofile);
Strcat(protofile, LEV_EXT);
if (load_special(protofile)) {
/* some levels can end up with monsters
Expand Down
Loading

0 comments on commit 9eb7830

Please sign in to comment.