From 040b9bf052267634c781b677ece55c7cb595df7b Mon Sep 17 00:00:00 2001 From: Rob Pearce Date: Wed, 4 Jan 2012 15:54:28 +0000 Subject: [PATCH] - [+] don't place draininge grates on low floor!! - [+] gate spell should always take you to a location wihtout water!! - [+] enhance speak with dead spell (SPEAKDEAD) - [+] may ask "how did you die" plus one of the below: - [+] tell me about the area - [+] mark stairs on the map - [+] mark shops on the map - [+] tell about very rare objects - [+] tell me about nearby dangers - [+] tell about odd monsters - [+] tell about traps - [+] test - [+] let you ask peaceful lfs about the surroundings too - [+] move code into io.c "askaboutarea(char *who)" - [+] test - [+] the first time you ask someone for info, randomly determine whether they know about: - [+] stairs (high chance) - [+] shops (medium chance) - [+] traps (low chance) - [+] areacomms_danger should include out of depth monsters - need isoutofdepth(lf) - [+] room floors should take the entry type from the habitat, NOT just use ct_room!!!! - [+] sometimes generate fixed SEWER regionthings - [+] done - [+] BUG: canT enter goblin caves again!!!! - [+] getting "ERROR - can't find opposite end of stairs/portal!" - [+] FAILED to link stiars: 'tunnel leading up' - [+] make maps direct you to regionthings. - [+] region things need unique IDs !!! - [+] change F_MAPTO - [+] v0 = src region id - [+] v1 = src depth - [+] v2 = regionthing with whatkind = RT_REGIONLINK - [+] when generating map objects, fill in a random branch entry - [+] fix getobname code for unknown maps. - [+] TEST - [+] with "map to the goblin caves" - [+] with "map" - [+] with "map to" - [+] use "the goblin caves are to the north", not "is to the north" - [+] is - [+] isn't - [+] test reading a map when on the wrong branch - [+] test reading a map when on the wrong level - [+] test reading a map when on the right level - [+] move staircase generation to END of map create - [+] finalisemap() - [+] countobswithflagval(obpile, flagid, val0...) - [+] countmapobswithflagval(map, flagid, v0...) - [+] jimbo's map should lead you to one of the major branches. - [+] crit pos shouldn't be higher than you can reach! - [+] same size or bigger = can reach anything - [+] 1 size smaller = you can't reach the head - [+] 2 sizes smaller = can't reach head or hands - [+] 3 sizes smaller = can't reach head, hands or body - [+] jimbo's room should contain all the staircases - [+] make you have to pass a speech check before people will tell you about the area - [+] bug in recruitment skillchecks - using wrong attribute, and too easy. - [+] rename dwarf to delver - [+] somtimes say "dons" instead of "puts on" --- attack.c | 2 +- data.c | 8 +- defs.h | 44 ++++-- io.c | 178 ++++++++++++++++++++- io.h | 2 + lf.c | 339 +++++++++++++++++++++++++++++++++++---- lf.h | 7 +- map.c | 400 +++++++++++++++++++++++++++++++++-------------- map.h | 12 +- move.c | 2 +- nexus.c | 6 +- objects.c | 382 ++++++++++++++++++++++++++------------------ objects.h | 2 + save.c | 8 +- spell.c | 89 +++++++---- text.c | 23 ++- text.h | 3 +- vaults/jimbo.vlt | 14 +- 18 files changed, 1141 insertions(+), 380 deletions(-) diff --git a/attack.c b/attack.c index 38ed569..3f4a5f4 100644 --- a/attack.c +++ b/attack.c @@ -672,7 +672,7 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) if (critical) { object_t *armour; char noun[BUFLEN]; - critpos = getrandomcorebp(victim); + critpos = getrandomcorebp(victim, lf); if (critpos != BP_NONE) { armour = getequippedob(victim->pack, critpos); if (armour) { diff --git a/data.c b/data.c index b21efba..48de6be 100644 --- a/data.c +++ b/data.c @@ -2340,6 +2340,9 @@ void initobjects(void) { addflag(lastot->flags, F_RARITY, H_ALL, 100, RR_UNCOMMON, NULL); addot(OT_MAP, "map", "A visual representation of the area.", MT_PAPER, 0.5, OC_SCROLL, SZ_SMALL); + addflag(lastot->flags, F_RARITY, H_ALL, 100, RR_UNCOMMON, NULL); + addflag(lastot->flags, F_VALUE, 100, NA, NA, NULL); + addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); addot(OT_SCR_CREATEMONSTER, "scroll of create monster", "Summons a (probably hostile) monster to a nearby location.", MT_PAPER, 0.5, OC_SCROLL, SZ_SMALL); addflag(lastot->flags, F_LINKSPELL, OT_S_CREATEMONSTER, 4, NA, NULL); @@ -6973,7 +6976,8 @@ void initrace(void) { addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "good armour"); addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "good armour"); addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "armour"); - //addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "dungeon exit orb"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "map"); + addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "dungeon exit orb"); addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "concealing powder"); addflag(lastrace->flags, F_FLEEONHPPCT, 40, NA, NA, NULL); addflag(lastrace->flags, F_STARTSKILL, SK_POLEARMS, PR_ADEPT, NA, NULL); @@ -7154,7 +7158,7 @@ void initrace(void) { addflag(lastrace->flags, F_TAMABLE, 25, NA, NA, NULL); - addrace(R_DWARF, "dwarf", 60, 'h', C_BROWN, MT_FLESH, RC_HUMANOID, "Dwarves are short, hardy creatures who generally spend their entire lives mining underground. As such they have great fitness and enhanced low-light vision, but generally lack great reasoning skills and magical ability."); + addrace(R_DWARF, "delver", 60, 'h', C_BROWN, MT_FLESH, RC_HUMANOID, "Delvers are short, hardy creatures who generally spend their entire lives mining underground. As such they have great fitness and enhanced low-light vision, but generally lack great reasoning skills and magical ability."); setbodytype(lastrace, BT_HUMANOID); addflag(lastrace->flags, F_PLAYABLE, B_TRUE, NA, NA, NULL); addflag(lastrace->flags, F_STARTATT, A_STR, AT_GTAVERAGE, NA, NULL); diff --git a/defs.h b/defs.h index 3e90c29..edcd002 100644 --- a/defs.h +++ b/defs.h @@ -132,7 +132,11 @@ #define B_BIG (-1) // Limits -#define MAXCANDIDATES 400 // must be >= max # of spells/abilities + +// must be >= max # of spells/abilities AND +// >= max # of cells on the map +#define MAXCANDIDATES 2048 + #define MAXCHOICES 400 #define MAXDEPTH 25 // max dungeon depth #define MAXDIR_ORTH 4 @@ -456,6 +460,11 @@ enum SAYPHRASE { SP_DIE, SP_DRUNK, SP_MERCYACCEPT, + SP_INFO_ACCEPT, + SP_INFO_ASKPRICE, + SP_INFO_REFUSE, + SP_INFO_REFUSE_AGAIN, + SP_INFO_DECLINE_WONTPAY, SP_PAYWARN, SP_PAYTHREAT, SP_PAYTHANKS, @@ -1859,20 +1868,20 @@ enum BODYPART { BP_SECWEAPON = 1, BP_EARS = 2, BP_EYES = 3, - BP_HEAD = 4, + BP_HEAD = 4, // <- core bodypart BP_SHOULDERS = 5, - BP_BODY = 6, - BP_HANDS = 7, + BP_BODY = 6, // <- core bodypart + BP_HANDS = 7, // <- core bodypart BP_WAIST = 8, - BP_LEGS = 9, + BP_LEGS = 9, // <- core bodypart BP_FEET = 10, BP_RIGHTFINGER = 11, BP_LEFTFINGER = 12, // others... BP_BACKLEGS = 13, BP_FRONTLEGS = 14, - BP_WINGS = 15, - BP_TAIL = 16, + BP_WINGS = 15, // <- core bodypart + BP_TAIL = 16, // <- core bodypart }; #define MAXBODYPARTS (17) @@ -1995,9 +2004,11 @@ enum FLAG { F_NO_A, // this obname doesn't need to start with 'a' for singular (eg. gold) F_CONTAINSOB, // for vending machiens. v0 is ob letter // text is an object it contains. - F_MAPTO, // this object is a map to the world map at xy=v0/v1. - // optional v2 = obtypeid of target - // text = what this is a map to ie. "the nearest village" + F_MAPTO, // this object is a map to a regionlink.: + // v0=region containing entrance + // v1=depth of entrance + // v2=regionthing ID of RT_REGIONLINK thing + // text = what this is a map to ie. "the goblin caves" F_SIGNTEXT, // for 'sign' objects. f->text is what is says. F_IDWHENUSED, // fully identify an object when worn/weilded/operated/etc F_STARTBLESSED, // v0 = b_blessed or b_cursed @@ -2211,8 +2222,7 @@ enum FLAG { // stairs / teleporters / portals F_CLIMBABLE, // this is a stiarcase, v0 = up/down/in // also use this for portals - // OPTIONAL v1 = id of regiontype to link to. - // (ie. RG_xxx) + // OPTIONAL v1 = id of region to link to. F_PIT, // this is a pit which we can fall down. // v0 = up/down //F_STAIRDIR//, // val0 = direcion @@ -2564,6 +2574,13 @@ enum FLAG { // must pass SC_SPEECH of difficulty v0 F_HIREPRICE, // how much it costs to hire this lf. F_NOHIRE, // this lf will not be hired. + F_INFOPRICE, // v0=price this lf wants to share info + F_NOINFO, // this lf will NOT share info + F_KNOWSABOUT, // text is a list of things this lf knows about: + // e = exits + // s = shops + // t = traps + // eg: text=="st" means they know of shops+traps F_NOJOBTEXT, // this lf's name is 'a xxx', not 'a xxx wizard' etc F_LASTDIR, // this is the last direction we moved. //F_OWNERLASTDIR, // for pets, this it the last dir our owner moved @@ -3258,6 +3275,7 @@ typedef struct regiontype_s { enum REGIONTYPE id; enum HABITAT defaulthabitat; char *name; + int pluralname; int maxdepth; int stairsperlev; int deeperdir; @@ -3276,6 +3294,8 @@ enum REGIONTHING { }; typedef struct regionthing_s { + struct regionoutline_s *parent; + int id; int depth; // only need depth OR x,y int x,y; enum REGIONTHING whatkind; diff --git a/io.c b/io.c index 1a00c1b..f960839 100644 --- a/io.c +++ b/io.c @@ -3712,6 +3712,14 @@ void docomms(lifeform_t *lf) { } } + // TODO: allow you to ask this of allies, but only on the level they started on. + if (!areallies(player, lf)) { + if (ispeaceful(lf) && cantalk(lf)) { + addchoice(&prompt, 'i', "What can you tell me about this area?", NULL, NULL, NULL); + addchoice(&prompt, 'x', "Any dangers nearby that I should look out for?", NULL, NULL, NULL); + } + } + if (areenemies(player, lf)) { addchoice(&prompt, 'm', "Have mercy!", NULL, NULL, NULL); } @@ -3945,6 +3953,13 @@ void docomms(lifeform_t *lf) { } aigoto(lf, c, MR_OTHER, NULL, DEF_AIFOLLOWTIME); break; + case 'i': + msg("You say \"What can you tell me about this area?\" to %s.", lfname); + if (askforinfo(lf)) { + genareaknowledge(lf->flags, 0); + docomms_areainfo(lfname, lf->flags, lf); + } + break; case 'j': // charisma check to see if they'll join you. msg("You say \"Join me on my quest!\" to %s.", lfname); @@ -4069,6 +4084,13 @@ void docomms(lifeform_t *lf) { return; } break; + case 'x': + msg("You say \"Any dangers nearby that I should look out for?\" to %s.", lfname); + if (askforinfo(lf)) { + genareaknowledge(lf->flags, 0); + docomms_areadangers(lfname, lf->flags, lf); + } + break; case 'y': msg("You shout at %s!", lfname); noise(where, player, NC_OTHER, 3, "someone shouting!", NULL); @@ -4093,6 +4115,154 @@ void docomms(lifeform_t *lf) { taketime(player, getactspeed(player)); } +// lf is the person (if any) who you are talking to +void docomms_areainfo(char *who, flagpile_t *fp, lifeform_t *lf) { + int x,y,ndone; + object_t *o; + cell_t *c; + flag_t *f,*knowflag; + + knowflag = hasflag(fp, F_KNOWSABOUT); + if (!knowflag) { + // should never happen + msg("\"I don't know anything about that!\""); + return; + } + + // shops + if (strchr(knowflag->text, 's')) { + ndone = 0; + for (y = 0; y < player->cell->map->h ; y++) { + for (x = 0; x < player->cell->map->w; x++) { + c = getcellat(player->cell->map, x,y); + if (hasobwithflag(c->obpile, F_SHOP)) { + setcellknownradius(c, PR_BEGINNER, 3, DT_ORTH); + ndone++; + } + } + } + if (ndone) { + needredraw = B_TRUE; + msg("\"There are some shops nearby...\""); more(); + } + } + // veryrare objects + ndone = 0; + for (y = 0; y < player->cell->map->h ; y++) { + for (x = 0; x < player->cell->map->w; x++) { + c = getcellat(player->cell->map, x,y); + for (o = c->obpile->first ; o ; o = o->next) { + if (hasflag(o->flags, F_SHOP)) continue; // shops were already handled + f = hasflag(o->type->flags, F_RARITY); + if (f && (f->val[2] == RR_VERYRARE)) { + msg("\"I hear there is a rare %s nearby...\"", o->type->name); more(); + setcellknown(c, PR_MASTER); + ndone++; + } + } + } + } + if (ndone) needredraw = B_TRUE; + // staircases + if (strchr(knowflag->text, 'e')) { + ndone = 0; + for (y = 0; y < player->cell->map->h ; y++) { + for (x = 0; x < player->cell->map->w; x++) { + c = getcellat(player->cell->map, x,y); + if (hasobwithflag(c->obpile, F_CLIMBABLE)) { + setcellknownradius(c, PR_BEGINNER, 3, DT_ORTH); + ndone++; + } + } + } + if (ndone) { + needredraw = B_TRUE; + msg("\"I can show you the ways out of this level...\""); more(); + } + } + msg("\"That's all I know about this area.\""); +} + +// lf is the person (if any) who you are talking to +void docomms_areadangers(char *who, flagpile_t *fp, lifeform_t *lf) { + int ndone = 0; + int totdone = 0; + int x,y,min,max; + cell_t *c; + flag_t *f,*knowflag; + object_t *o; + + knowflag = hasflag(fp, F_KNOWSABOUT); + if (!knowflag) { + // should never happen + msg("\"I don't know anything about that!\""); + return; + } + + // traps or trapped objects + if (strchr(knowflag->text, 't')) { + ndone = 0; + for (y = 0; y < player->cell->map->h ; y++) { + for (x = 0; x < player->cell->map->w; x++) { + c = getcellat(player->cell->map, x,y); + for (o = c->obpile->first ; o ; o = o->next) { + if (killflagsofid(o->flags, F_TRAP)) { + setcellknown(c, PR_MASTER); + ndone++; + } else { + f = hasflag(o->flags, F_TRAPPED); + if (f && (f->val[2] != B_TRUE)) { + f->val[2] = B_TRUE; + setcellknown(c, PR_MASTER); + ndone++; + } + } + } + } + } + if (ndone) { + needredraw = B_TRUE; + msg("\"There are hidden traps nearby...\""); more(); + } + totdone += ndone; + } + // veryrare monsters + gethitdicerange(getmapdifficulty(player->cell->map), &min,&max, RARITYVARIANCELF, B_FALSE); + + ndone = 0; + for (y = 0; y < player->cell->map->h ; y++) { + for (x = 0; x < player->cell->map->w; x++) { + c = getcellat(player->cell->map, x,y); + if (c->lf && !isplayer(c->lf) && (c->lf != lf) && areenemies(c->lf, player)) { + int showit = B_FALSE; + enum RARITY rr; + getracerarity(NULL, c->lf->race->id, &rr); + if (rr == RR_VERYRARE) { + showit = B_TRUE; + } else { + // out of depth monsters? + int hd; + hd = gethitdicerace(c->lf->race); + if (hd > max) { + showit = B_TRUE; + } + } + + if (showit) { + char lfname[BUFLEN]; + real_getlfnamea(c->lf, lfname, B_FALSE); + msg("\"There is %s living nearby...\"", lfname); more(); + ndone++; + } + } + } + } + totdone += ndone; + + + msg("\"I know of no %sdangers in this area.\"", (totdone) ? "other " : ""); +} + void dodrop(obpile_t *op, int wantmulti, obpile_t *dst) { object_t *o; char buf[BUFLEN]; @@ -8432,7 +8602,7 @@ void handleinput(void) { // certain objects in the current cell will stop us from running. for (o = player->cell->obpile->first ; o ; o = o->next) { - if ( !hasflag(o->flags, F_COSMETIC)) { + if ( !hasflag(o->flags, F_COSMETIC) && !hasflag(o->flags, F_TRAIL)) { stopnow = B_TRUE; break; } @@ -9461,7 +9631,7 @@ void drawstatus(void) { //wprintw(statwin, "DLev:%d", player->cell->map->depth); setcol(statwin, C_BROWN); - getregionname(buf, player->cell->map, B_TRUE); + getregionname(buf, player->cell->map, NULL, B_TRUE); capitalise(buf); wprintw(statwin, "%s", buf); unsetcol(statwin, C_BROWN); @@ -11773,10 +11943,10 @@ void tombstone(lifeform_t *lf) { centre(mainwin, C_GREY, y, "%s (%ld points)",pname, playerscore); y++; if (player->cell->map->region->rtype->id == RG_WORLDMAP) { - getregionname(buf, player->cell->map, B_TRUE); + getregionname(buf, player->cell->map, NULL, B_TRUE); centre(mainwin, C_GREY, y, "Died on %s.", buf); y++; } else { - getregionname(buf, player->cell->map, B_FALSE); + getregionname(buf, player->cell->map, NULL, B_FALSE); centre(mainwin, C_GREY, y, "Died on level %d of %s.", player->cell->map->depth, buf); y++; } diff --git a/io.h b/io.h index 9adcbc1..3a5cd15 100644 --- a/io.h +++ b/io.h @@ -44,6 +44,8 @@ void describespell(objecttype_t *ot); void doattackcell(char dirch); void doclose(void); void docomms(lifeform_t *target); +void docomms_areainfo(char *who, flagpile_t *fp, lifeform_t *lf); +void docomms_areadangers(char *who, flagpile_t *fp, lifeform_t *lf); void dodrop(obpile_t *op, int wantmulti, obpile_t *dst); void doeat(obpile_t *op); void doenter(lifeform_t *lf); diff --git a/lf.c b/lf.c index 7d463a3..a38f517 100644 --- a/lf.c +++ b/lf.c @@ -994,6 +994,8 @@ int canquaff(lifeform_t *lf, object_t *o) { return B_TRUE; } + +// can lf reach victim to attack them? int canreach(lifeform_t *lf, lifeform_t *victim, int *reachpenalty) { enum LFSIZE sizetoreach = SZ_ANY; // harder to hit flying/levitating enemies, unless you are @@ -1030,6 +1032,71 @@ int canreach(lifeform_t *lf, lifeform_t *victim, int *reachpenalty) { return B_FALSE; } +// can lf reach victim's bodypart bp? +int canreachbp(lifeform_t *lf, lifeform_t *victim, enum BODYPART bp) { + int howmuchsmaller; + int lfsize, victimsize; + + // can't reach them at all! + if (!canreach(lf, victim, NULL)) { + return B_FALSE; + } + + lfsize = getlfsize(lf); + switch (isairborne(lf)) { + case F_FLYING: lfsize += 2; break; + case F_LEVITATING: lfsize++; break; + default: break; + } + victimsize = getlfsize(victim); + switch (isairborne(victim)) { + case F_FLYING: victimsize += 2; break; + case F_LEVITATING: victimsize++; break; + default: break; + } + + howmuchsmaller = victimsize - lfsize; + if (howmuchsmaller <= 0) return B_TRUE; + + switch (bp) { + // upper + case BP_EYES: + case BP_HEAD: + case BP_EARS: + case BP_SHOULDERS: + if (howmuchsmaller >= 1) { + return B_FALSE; + } + break; + case BP_WEAPON: + case BP_SECWEAPON: + case BP_HANDS: + case BP_RIGHTFINGER: + case BP_LEFTFINGER: + if (howmuchsmaller >= 2) { + return B_FALSE; + } + break; + case BP_BODY: + case BP_WAIST: + case BP_WINGS: + case BP_TAIL: + if (howmuchsmaller >= 3) { + return B_FALSE; + } + break; + case BP_LEGS: + case BP_FEET: + case BP_BACKLEGS: + case BP_FRONTLEGS: + if (howmuchsmaller >= 4) { + return B_FALSE; + } + break; + } + return B_TRUE; +} + int cansee(lifeform_t *viewer, lifeform_t *viewee) { return cansee_real(viewer, viewee, B_TRUE); } @@ -1449,6 +1516,9 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar case E_LOWIQ: msg("You are not smart enough to cast spells."); break; + case E_NOTREADY: + msg("This ability is not recharged yet."); + break; case E_PRONE: msg("You can't cast spells while prone."); break; @@ -2499,6 +2569,9 @@ void die(lifeform_t *lf) { colourmatchob(corpse, lf); } + // inherit lifeform knowledge in case we raise it + copyflag(corpse->flags, lf->flags, F_KNOWSABOUT); + // corpse of a player pet? if (ispetof(lf, player)) { addflag(corpse->flags, F_PETOF, player->id, NA, NA, NULL); @@ -2622,6 +2695,28 @@ void dumpmonsters(void) { dblog("END MONSTER DUMP (%d found)",count); } +void genareaknowledge(flagpile_t *fp, int chancemod) { + char knownstuff[BUFLENSMALL]; + + if (hasflag(fp, F_KNOWSABOUT)) return; + + strcpy(knownstuff, ""); + // determine what someone knows about + // stairs? (high chance) + if (pctchance(75 + chancemod)) { + strcat(knownstuff, "e"); + } + // shops? (med chance) + if (pctchance(50 + chancemod)) { + strcat(knownstuff, "s"); + } + // traps? (low chance) + if (pctchance(25 + chancemod)) { + strcat(knownstuff, "t"); + } + addflag(fp, F_KNOWSABOUT, NA, NA, NA, knownstuff); +} + void genxplist(void) { race_t *r; race_t *racetemp; @@ -2673,7 +2768,7 @@ void dumpxp(void) { // dump dblog("%-10s%-30s%s","XP", "Race", "Rarity"); for (i = 0; i < xplistlen; i++) { - dblog("%-10d%-30s%d",xpposs[i], raceposs[i]->name,getracerarity(NULL, raceposs[i]->id)); + dblog("%-10d%-30s%d",xpposs[i], raceposs[i]->name,getracerarity(NULL, raceposs[i]->id, NULL)); } // free mem @@ -3920,7 +4015,7 @@ int fall_from_air(lifeform_t *lf) { } } if (willfall) fall(lf, NULL, B_TRUE); - if (willinjure) injure(lf, getrandomcorebp(lf), DT_BASH); + if (willinjure) injure(lf, getrandomcorebp(lf, NULL), DT_BASH); if (willfall || willinjure) { return B_TRUE; } @@ -7230,9 +7325,13 @@ int getraceclass(lifeform_t *lf) { return lf->race->raceclass->id; } -int getracerarity(map_t *map, enum RACE rid) { +// returns rarity number. if optional rr is passed, this is will be returned too. +int getracerarity(map_t *map, enum RACE rid, enum RARITY *rr) { race_t *r; int rarity = -1; + + if (rr) *rr = RR_NONE; + r = findrace(rid); if (r) { flag_t *f = NULL; @@ -7253,10 +7352,10 @@ int getracerarity(map_t *map, enum RACE rid) { if (f) { // ignore habitat for now! rarity = f->val[1]; + if (rr) *rr = f->val[2]; } } - return rarity; } @@ -7297,15 +7396,46 @@ object_t *getrandomarmour(lifeform_t *lf) { } // pick a random major body part -// ie. head, body, arms/hands, legs -enum BODYPART getrandomcorebp(lifeform_t *lf) { +// ie. head, body, arms/hands, legs, wings, tail +enum BODYPART getrandomcorebp(lifeform_t *lf, lifeform_t *attacker) { int cutoff[4],nparts = 0,i,max = 0,num; enum BODYPART bp[4],selbp = BP_NONE; - if (hasbp(lf, BP_BODY)) bp[nparts++] = BP_BODY; - if (hasbp(lf, BP_HANDS)) bp[nparts++] = BP_HANDS; - if (hasbp(lf, BP_HEAD)) bp[nparts++] = BP_HEAD; + + if (hasbp(lf, BP_BODY)) { + if (attacker && !canreachbp(attacker, lf, BP_BODY)) { + } else { + bp[nparts++] = BP_BODY; + } + } + if (hasbp(lf, BP_HANDS)) { + if (attacker && !canreachbp(attacker, lf, BP_HANDS)) { + } else { + bp[nparts++] = BP_HANDS; + } + } + if (hasbp(lf, BP_HEAD)) { + if (attacker && !canreachbp(attacker, lf, BP_HEAD)) { + } else { + bp[nparts++] = BP_HEAD; + } + } if (hasbp(lf, BP_LEGS) || hasbp(lf, BP_FRONTLEGS) || hasbp(lf, BP_BACKLEGS)) { - bp[nparts++] = BP_LEGS; + if (attacker && !canreachbp(attacker, lf, BP_LEGS)) { + } else { + bp[nparts++] = BP_LEGS; + } + } + if (hasbp(lf, BP_WINGS)) { + if (attacker && !canreachbp(attacker, lf, BP_WINGS)) { + } else { + bp[nparts++] = BP_WINGS; + } + } + if (hasbp(lf, BP_TAIL)) { + if (attacker && !canreachbp(attacker, lf, BP_TAIL)) { + } else { + bp[nparts++] = BP_TAIL; + } } if (!nparts) { @@ -9018,30 +9148,12 @@ void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp) { killflagsofid(fp, F_STARTOBCLASS); killflagsofid(fp, F_STARTOBWEPSK); - // SPECIAL CASES if (lf) { - /* + // SPECIAL CASES GO HERE. if (lf->race->id == R_JAILER) { - regionoutline_t *ro; - region_t *r; - regionthing_t *rt = NULL; - int i; - o = addobfast(lf->pack, OT_MAP); + o = addob(lf->pack, "map to the goblin caves"); assert(o); - // find first worldmap village - r = findregion(RG_WORLDMAP); - ro = r->outline; - for (i = 0; i < ro->nthings; i++) { - regionthing_t *thisthing; - thisthing = &ro->thing[i]; - if ((thisthing->whatkind == RT_HABITAT) && (thisthing->value == H_VILLAGE)) { - rt = thisthing; - break; - } - } - addflag(o->flags, F_MAPTO, rt->x, rt->y, OT_GATEWOOD, "a village"); } - */ // make sure lf doesn't start off burdened! while (isburdened(lf)) { @@ -11415,6 +11527,96 @@ int armourfits(lifeform_t *lf, object_t *o, enum ERROR *reason) { return B_TRUE; } +// returns TRUE if 'lf' agrees to share knowledge with the player +int askforinfo(lifeform_t *lf) { + if (lfhasflag(lf, F_NOINFO)) { + // refusing to give info + sayphrase(lf, SP_INFO_REFUSE_AGAIN, SV_TALK, NA, NULL); + return B_FALSE; + } else { + int doinfo = B_FALSE; + int askingprice = -1; + char lfname[BUFLEN]; + char buf[BUFLEN]; + flag_t *f; + + getlfname(lf, lfname); + + if (areallies(player, lf)) { + askingprice = 0; + } else { + // they will consider it - now negotiate a price + f = lfhasflag(lf, F_INFOPRICE); + if (f) { + askingprice = f->val[0]; + } else { + int result; + int difficulty; + + difficulty = 20 + ((gethitdice(player) - gethitdice(lf))*2); + if (real_skillcheck(player, SC_SPEECH, difficulty, 0, &result)) { + askingprice = 0; + // passed - free! + } else { + if (difficulty - result >= 10) { + // will not help! + askingprice = -1; + } else { + // will help for gold + askingprice = rnd(gethitdice(lf)*5, gethitdice(lf)*15 ); + } + } + + if (askingprice == -1) { + addflag(lf->flags, F_NOINFO, B_TRUE, NA, NA, NULL); + sayphrase(lf, SP_INFO_REFUSE, SV_TALK, NA, NULL); + return B_FALSE; + } else { + addflag(lf->flags, F_INFOPRICE, askingprice, NA, NA, NULL); + } + } + } + + if (askingprice != 0) { + // modify for same job + if (getjob(player) == getjob(lf)) { + askingprice = pctof(50, askingprice); + } else if (player->race->baseid == lf->race->baseid) { // modify for same race + askingprice = pctof(80, askingprice); + } + // modify by charisma + askingprice = pctof(100 - getstatmod(player, A_CHA), askingprice); + limit(&askingprice, 0, NA); + } + + if (askingprice > 0) { + char ch; + sayphrase(lf, SP_INFO_ASKPRICE, SV_TALK, askingprice, NULL); + more(); + + if (askingprice > countmoney(player->pack)) { + msg("You can't afford to pay $%d.", askingprice); + return B_FALSE; + } else { + snprintf(buf, BUFLEN, "Pay $%d for information", askingprice); + ch = askchar(buf, "yn","n", B_TRUE, B_FALSE); + if (ch == 'y') { + doinfo = B_TRUE; + } + } + } else { + doinfo = B_TRUE; + } + + if (doinfo) { + sayphrase(lf, SP_INFO_ACCEPT, SV_TALK, NA, NULL); + return B_TRUE; + } else { + sayphrase(lf, SP_INFO_DECLINE_WONTPAY, SV_TALK, askingprice, NULL); + } + } // end if !nohire + return B_FALSE; +} int askforpayment(lifeform_t *shk, lifeform_t *lf) { char saybuf[BUFLEN]; @@ -13080,6 +13282,8 @@ void petify(lifeform_t *lf, lifeform_t *owner) { makefriendly(lf, PERMENANT); addflag(lf->flags, F_PETOF, owner->id, owner->cell->x, owner->cell->y, NULL); killflagsofid(lf->flags, F_STAYINROOM); + killflagsofid(lf->flags, F_NOINFO); + killflagsofid(lf->flags, F_INFOPRICE); } @@ -13651,8 +13855,8 @@ int recruit(lifeform_t *lf) { int result; int difficulty; int minmult,maxmult; - difficulty = 20 + ((gethitdice(player) - gethitdice(lf))*2); - if (real_skillcheck(player, A_CHA, difficulty, 0, &result)) { + difficulty = 25 + ((gethitdice(player) - gethitdice(lf))*2); + if (real_skillcheck(player, SC_SPEECH, difficulty, 0, &result)) { minmult = 10; maxmult = 20; // passed @@ -14200,6 +14404,69 @@ int sayphrase(lifeform_t *lf, enum SAYPHRASE what, int volume, int val0, char *t } rv = say(lf, buf, volume); break; + case SP_INFO_ACCEPT: + switch (rnd(1,2)) { + case 1: snprintf(buf, BUFLEN, "Okay, here's what I know..."); break; + case 2: snprintf(buf, BUFLEN, "Listen carefully..."); break; + } + rv = say(lf, buf, volume); + break; + case SP_INFO_ASKPRICE: + switch (rnd(1,3)) { + case 1: + snprintf(buf, BUFLEN, "I'll tell you for $%d...",val0); + break; + case 2: + snprintf(buf, BUFLEN, "Is the info worth $%d to you?",val0); + break; + case 3: + snprintf(buf, BUFLEN, "$%d and I'll tell you...",val0); + break; + } + rv = say(lf, buf, volume); + break; + case SP_INFO_REFUSE: + switch (rnd(1,4)) { + case 1: + snprintf(buf, BUFLEN, "What do you think I am, a library?"); + break; + case 2: + snprintf(buf, BUFLEN, "Can't help, sorry."); + break; + case 3: + snprintf(buf, BUFLEN, "Get lost!"); + break; + case 4: + snprintf(buf, BUFLEN, "No time to talk!"); + break; + } + rv = say(lf, buf, volume); + break; + case SP_INFO_REFUSE_AGAIN: + switch (rnd(1,3)) { + case 1: + snprintf(buf, BUFLEN, "Asking twice isn't going to change the answer!"); + break; + case 2: + snprintf(buf, BUFLEN, "Still can't help, sorry."); + break; + case 3: + snprintf(buf, BUFLEN, "I already told you to go away!"); + break; + case 4: + snprintf(buf, BUFLEN, "Stop pestering me!"); + break; + } + rv = say(lf, buf, volume); + break; + case SP_INFO_DECLINE_WONTPAY: + switch (rnd(1,3)) { + case 1: snprintf(buf, BUFLEN, "Cheapskate."); break; + case 2: snprintf(buf, BUFLEN, "Well, I'll be here if you change your mind."); break; + case 3: snprintf(buf, BUFLEN, "Your loss."); break; + } + rv = say(lf, buf, volume); + break; case SP_PAYWARN: switch (rnd(1,3)) { case 1: snprintf(buf, BUFLEN, "Hey! Where do you think you're going?"); break; @@ -16793,6 +17060,7 @@ void timeeffectslf(lifeform_t *lf) { if (willfall) { usestairs(lf, o, B_FALSE, B_FALSE); + taketime(lf, getactspeed(lf)); // to avoid infinte loops donesomething = B_TRUE; o = hasobwithflagval(lf->cell->obpile, F_PIT, dir, NA, NA, NULL); } @@ -18163,9 +18431,14 @@ int wear(lifeform_t *lf, object_t *o) { msg("You are now wearing %s.", obname); } } else if (cansee(player, lf)) { + char verb[BUFLEN]; getlfname(lf, buf); capitalise(buf); - msg("%s puts on %s.", buf, obname); + switch (rnd(1,2)) { + case 1: strcpy(verb, "puts on"); break; + case 2: strcpy(verb, "dons"); break; + } + msg("%s %s %s.", buf, obname); } diff --git a/lf.h b/lf.h index 8116e31..271eb9f 100644 --- a/lf.h +++ b/lf.h @@ -17,6 +17,7 @@ void applywalkdam(lifeform_t *lf, int dam, enum DAMTYPE damtype, object_t *o); int areallies(lifeform_t *lf1, lifeform_t *lf2); int areenemies(lifeform_t *lf1, lifeform_t *lf2); int armourfits(lifeform_t *lf, object_t *o, enum ERROR *reason); +int askforinfo(lifeform_t *lf); int askforpayment(lifeform_t *shk, lifeform_t *lf); char *assignnpcname(lifeform_t *lf); void autoskill(lifeform_t *lf); @@ -48,6 +49,7 @@ int canpolymorphto(enum RACE rid); int canpush(lifeform_t *lf, object_t *o, int dir); int canquaff(lifeform_t *lf, object_t *o); int canreach(lifeform_t *lf, lifeform_t *victim, int *reachpenalty); +int canreachbp(lifeform_t *lf, lifeform_t *victim, enum BODYPART bp); int cansee(lifeform_t *viewer, lifeform_t *viewee); int cansee_real(lifeform_t *viewer, lifeform_t *viewee, int uselos); int cansleep(lifeform_t *lf); @@ -110,6 +112,7 @@ void gainhp(lifeform_t *lf, int amt); void gainlevel(lifeform_t *lf, int autotrain); void gainmp(lifeform_t *lf, int amt); void gainxp(lifeform_t *lf, long amt); +void genareaknowledge(flagpile_t *fp, int chancemod); void genxplist(void); int get_adjacent_quadrants(int dir, enum QUADRANT *start, enum QUADRANT *end); int get_circular_fov_endpoints(lifeform_t *lf, int maxvisrange, int *endx, int *endy, int *nendcells); @@ -214,9 +217,9 @@ char *getpoisondesc(enum POISONTYPE ptype); char *getpoisonname(enum POISONTYPE ptype); enum POISONSEVERITY getpoisonseverity(enum POISONTYPE ptype); int getraceclass(lifeform_t *lf); -int getracerarity(map_t *map, enum RACE rid); +int getracerarity(map_t *map, enum RACE rid, enum RARITY *rr); object_t *getrandomarmour(lifeform_t *lf); -enum BODYPART getrandomcorebp(lifeform_t *lf); +enum BODYPART getrandomcorebp(lifeform_t *lf, lifeform_t *attacker); race_t *getrandomcorpserace(cell_t *c); job_t *getrandomjob(int onlyplayerjobs); int getrandommonlevel(race_t *r, map_t *m); diff --git a/map.c b/map.c index ed599b5..d4a1371 100644 --- a/map.c +++ b/map.c @@ -31,6 +31,8 @@ extern race_t *firstrace; extern int viewx,viewy,vieww,viewh; extern lifeform_t *player; +extern int nextregionthingid; + extern lifeform_t *godlf[]; extern int ngodlfs; @@ -761,6 +763,9 @@ regionoutline_t *addregionoutline(enum REGIONTYPE rtype) { regionthing_t *addregionthing(regionoutline_t *ro, int depth, int x, int y, enum REGIONTHING whatkind, int value, char *what) { regionthing_t *rt; rt = &(ro->thing[ro->nthings]); + rt->id = nextregionthingid; + nextregionthingid++; + rt->parent = ro; rt->depth = depth; rt->x = x; rt->y = y; @@ -782,7 +787,7 @@ regionthing_t *addregionthing(regionoutline_t *ro, int depth, int x, int y, enum return rt; } -regiontype_t *addregiontype(enum REGIONTYPE id, char *name, enum HABITAT defaulthabitat, int maxdepth, int stairsperlev, int deeperdir, int major, int depthmod) { +regiontype_t *addregiontype(enum REGIONTYPE id, char *name, int pluralname, enum HABITAT defaulthabitat, int maxdepth, int stairsperlev, int deeperdir, int major, int depthmod) { regiontype_t *a; // add to the end of the list @@ -803,6 +808,7 @@ regiontype_t *addregiontype(enum REGIONTYPE id, char *name, enum HABITAT default // props a->id = id; a->name = strdup(name); + a->pluralname = pluralname; a->defaulthabitat = defaulthabitat; a->maxdepth = maxdepth; a->stairsperlev = stairsperlev; @@ -1910,14 +1916,51 @@ int countmapobs(map_t *m, enum OBTYPE oid) { return count; } +int countmapobswithflag(map_t *m, enum FLAG flagid) { + cell_t *c; + int count = 0,x,y; + + for (y = 0; y < m->h; y++) { + for (x = 0; x < m->w; x++) { + c = getcellat(m, x, y); + if (c) { + count += countobswithflag(c->obpile, flagid); + } + } + } + return count; +} + +int countmapobswithflagval(map_t *m, enum FLAG flagid, int val0, int val1, int val2, char *text) { + cell_t *c; + int count = 0,x,y; + + for (y = 0; y < m->h; y++) { + for (x = 0; x < m->w; x++) { + c = getcellat(m, x, y); + if (c) { + count += countobswithflagval(c->obpile, flagid, val0, val1, val2, text); + } + } + } + return count; +} + +int countstairs(map_t *m, int dir) { + int count; + count = countmapobswithflagval(m, F_CLIMBABLE, dir, NA, NA, NULL); + // don't include pits + count -= countmapobswithflagval(m, F_PIT, dir, NA, NA, NULL); + return count; +} void createcave(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob) { int wantrooms = B_TRUE; int x,y,i; int numrooms = 0; - int entrylinked = B_FALSE; cell_t *c; - object_t *o; + //int entrylinked = B_FALSE; + //object_t *o; //int db = B_TRUE; enum CELLTYPE emptycell,solidcell; @@ -2003,37 +2046,6 @@ void createcave(map_t *map, int depth, map_t *parentmap, int exitdir, object_t * } */ - // UP STAIRS - for (i = 0; i < map->region->rtype->stairsperlev; i++) { - c = NULL; - while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { - c = getrandomcell(map); - } - o = addobfast(c->obpile, OT_TUNNELUP); - assert(o); - if (entryob && !entrylinked) { // first cave level - linkstairs(o, entryob); - entrylinked = B_TRUE; - } else { - linkstairs(o, NULL); - } - } - // make sure we have at least one up stairs - assert(findobinmap(map, OT_TUNNELUP)); - - // DOWN STAIRS - if (map->depth < map->region->rtype->maxdepth) { - for (i = 0; i < map->region->rtype->stairsperlev; i++) { - c = NULL; - while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { - c = getrandomcell(map); - } - o = addobfast(c->obpile, OT_TUNNELDOWN); - assert(o); - linkstairs(o, NULL); - } - } - // now do a border y = 0; for (x = 0; x < map->w; x++) { @@ -2068,7 +2080,7 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ int lastdir; int numrooms = 0; cell_t *cell, *c; - object_t *o; + //object_t *o; int db = B_TRUE; int digdb = B_FALSE; @@ -2305,70 +2317,6 @@ void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_ } } - // add staircases. - // first dungeon level has 1 up stairs, 3 down. - // subsequent levels always have 3 up and down stairs - - // UP STAIRS - if (map->depth == 1) { - flag_t *f; - // first region level. just one exit stairs - c = NULL; - while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { - c = getrandomroomcell(map, ANYROOM); - } - o = addobfast(c->obpile, OT_STAIRSUP); - if (entryob) { - linkstairs(o, entryob); - } else { - // have to force these stairs to go back to a different region. - f = hasflag(o->flags, F_CLIMBABLE); - f->val[1] = map->region->parentregion->id; - linkstairs(o, NULL); - } - - // special case: first dungeon level has barriers over the exit stairs - if (map->region->rtype->id == RG_MAINDUNGEON) { - if (c->lf) killlf(c->lf); - addobfast(c->obpile, OT_MAGICBARRIER); - // also clear all the cells around it to prevent reachability errors - for (d = DC_N; d <= DC_NW; d++) { - cell_t *c2; - c2 = getcellindir(c, d); - if (c2) setcelltype(c2, map->habitat->emptycelltype); - } - } - } else { - for (i = 0; i < map->region->rtype->stairsperlev; i++) { - c = NULL; - while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { - c = getrandomroomcell(map, ANYROOM); - if (!c) { - // ANY cell at all, doesn't have to be a room. - c = getrandomcell(map); - } - } - o = addobfast(c->obpile, OT_STAIRSUP); - assert(o); - linkstairs(o, NULL); - } - } - // make sure we have at least one up stairs - assert(findobinmap(map, OT_STAIRSUP)); - - // DOWN STAIRS - if (map->depth < map->region->rtype->maxdepth) { - for (i = 0; i < map->region->rtype->stairsperlev; i++) { - c = NULL; - while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { - c = getrandomroomcell(map, ANYROOM); - } - o = addobfast(c->obpile, OT_STAIRSDOWN); - assert(o); - linkstairs(o, NULL); - } - } - // add pillars & objects & monsters to rooms if (wantrooms && (numrooms > 0)) { for (i = 0; i < map->nrooms; i++) { @@ -2683,7 +2631,7 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex map->region = region; if (db) { - getregionname(buf, map, B_FALSE); + getregionname(buf, map, NULL, B_FALSE); dblog("Creating new map of region '%s'",buf); } map->habitat = findhabitat(habitat); @@ -2821,7 +2769,7 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging // we now have the map name! - getregionname(buf2, map, B_TRUE); + getregionname(buf2, map, NULL, B_TRUE); snprintf(buf, BUFLEN, "%s (id #%d)",buf2, map->id); map->name = strdup(buf); @@ -2959,12 +2907,16 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex } //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging + if (map->habitat->id == H_CAVE) { // expand the cave a little more now that we've fixed reachability. // this will help make any new corridors look more 'cave-like'. expand_cave(map, 2); } + // add any required stairs + finalisemap(map, entryob); + // special cases // village - add town walls and clear it out if (db) dblog(" finalising village creation..."); @@ -3119,7 +3071,7 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex } } else { if (db) { - dblog(" FAILED to link stiars: '%s'",o->type->name); + dblog(" FAILED to link stairs: '%s'",o->type->name); } } @@ -4199,12 +4151,13 @@ void createregionlink(map_t *m, cell_t *c, object_t *o, char *obname, enum REGIO if (newregiontype != RG_MAINDUNGEON) { basedepth = getmapdifficulty(m); } + // does the new region exist yet ? // create a new region. r = addregion(newregiontype, m->region, -1, basedepth); // add stairs going to the new region, if required if (!c) { c = NULL; - while (!c || !cellwalkable(NULL, c, NULL)) { + while (!c || !cellwalkable(NULL, c, NULL) || getcellwaterdepth(c, NULL)) { c = getrandomcell(m); } } @@ -4290,6 +4243,7 @@ void createroom(map_t *map, int roomid, int x1, int y1, int x2, int y2, int forc int x,y; room_t *thisroom = NULL; cell_t *cell; + map->room[map->nrooms].id = roomid; map->room[map->nrooms].x1 = x1; map->room[map->nrooms].y1 = y1; @@ -4312,11 +4266,11 @@ void createroom(map_t *map, int roomid, int x1, int y1, int x2, int y2, int forc // ie. if you haven't forced walls then if this room overlaps // with another one, no walls will be created. if (forcewalls || (!forcewalls && cell->type->solid)) { - setcelltype(cell, CT_ROOMWALL); + setcelltype(cell, cell->habitat->solidcelltype); } //} } else { - setcelltype(cell, CT_ROOM); + setcelltype(cell, cell->habitat->emptycelltype); } cell->room = thisroom; } @@ -4554,7 +4508,6 @@ void explodecells(cell_t *c, int dam, int killwalls, object_t *o, int range, int // critical hit? 100% chance in middle, 60 at one cell, 20 at two cells critchance = 100 - (mydist*40); if (pctchance(critchance)) { - //criticalhit(NULL, cc->lf, getrandomcorebp(cc->lf), DT_EXPLOSION); criticalhit(NULL, cc->lf, BP_HANDS, pctof(critchance, dam), DT_EXPLOSIVE); } @@ -4622,6 +4575,108 @@ void explodesinglecell(cell_t *c, int dam, int killwalls, object_t *o, cell_t *c } } +void finalisemap(map_t *map, object_t *entryob) { + enum OBTYPE upstairtype, downstairtype; + int i,d; + int linkedentry = B_FALSE; + cell_t *c; + object_t *o; + // add staircases. + // first dungeon level has 1 up stairs, 3 down. + // subsequent levels always have 3 up and down stairs + switch (map->habitat->id) { + case H_CAVE: + upstairtype = OT_TUNNELUP; + downstairtype = OT_TUNNELDOWN; + break; + case H_DUNGEON: + upstairtype = OT_STAIRSUP; + downstairtype = OT_STAIRSDOWN; + break; + default: + upstairtype = OT_NONE; + downstairtype = OT_NONE; + break; + } + + // UP STAIRS + if (upstairtype != OT_NONE) { + if ((map->habitat->id == H_DUNGEON) && (map->depth == 1)) { + flag_t *f; + // first dungeon level. just one exit stairs + c = NULL; + while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { + c = getrandomroomcell(map, ANYROOM); + } + o = addobfast(c->obpile, upstairtype); + if (entryob) { + linkstairs(o, entryob); + } else { + // have to force these stairs to go back to a different region. + f = hasflag(o->flags, F_CLIMBABLE); + f->val[1] = map->region->parentregion->id; + linkstairs(o, NULL); + } + + // special case: first dungeon level has barriers over the exit stairs + if (map->region->rtype->id == RG_MAINDUNGEON) { + if (c->lf) killlf(c->lf); + addobfast(c->obpile, OT_MAGICBARRIER); + // also clear all the cells around it to prevent reachability errors + for (d = DC_N; d <= DC_NW; d++) { + cell_t *c2; + c2 = getcellindir(c, d); + if (c2) setcelltype(c2, map->habitat->emptycelltype); + } + } + } else { + // up stairs on all other levels + int nneeded; + nneeded = map->region->rtype->stairsperlev - countstairs(map, D_UP); + for (i = 0; i < nneeded; i++) { + c = NULL; + while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { + c = getrandomroomcell(map, ANYROOM); + if (!c) { + // ANY cell at all, doesn't have to be a room. + c = getrandomcell(map); + } + } + o = addobfast(c->obpile, upstairtype); + assert(o); + if (entryob && !linkedentry) { + linkstairs(o, entryob); + linkedentry = B_TRUE; + } else { + linkstairs(o, NULL); + } + } + } + // make sure we have at least one up stairs + assert(findobinmap(map, upstairtype)); + } + + // DOWN STAIRS + if ((downstairtype != OT_NONE) && (map->depth < map->region->rtype->maxdepth)) { + int nneeded; + nneeded = map->region->rtype->stairsperlev - countstairs(map, D_DOWN); + for (i = 0; i < nneeded; i++) { + c = NULL; + while (!c || !isempty(c) || countobs(c->obpile, B_TRUE)) { + c = getrandomroomcell(map, ANYROOM); + if (!c) { + // ANY cell at all, doesn't have to be a room. + c = getrandomcell(map); + } + } + o = addobfast(c->obpile, downstairtype); + assert(o); + linkstairs(o, NULL); + } + } + +} + celltype_t *findcelltype(enum CELLTYPE cid) { celltype_t *ct; for (ct = firstcelltype ; ct ; ct = ct->next) { @@ -4670,6 +4725,40 @@ map_t *findmapofdepth(int depth) { return NULL; } +object_t *findmapobwithflag(map_t *m, enum FLAG flagid) { + cell_t *c; + object_t *o; + int x,y; + + for (y = 0; y < m->h; y++) { + for (x = 0; x < m->w; x++) { + c = getcellat(m, x, y); + if (c) { + o = hasobwithflag(c->obpile, flagid); + if (o) return o; + } + } + } + return NULL; +} + +object_t *findmapobwithflagval(map_t *m, enum FLAG flagid, int val0, int val1, int val2, char *text) { + cell_t *c; + object_t *o; + int x,y; + + for (y = 0; y < m->h; y++) { + for (x = 0; x < m->w; x++) { + c = getcellat(m, x, y); + if (c) { + o = hasobwithflagval(c->obpile, flagid, val0, val1, val2, text); + if (o) return o; + } + } + } + return NULL; +} + cell_t *findmapentrypoint(map_t *m, int side, lifeform_t *lf) { int x,y,xinc,yinc; @@ -4774,6 +4863,21 @@ regionoutline_t *findoutline(int id) { return NULL; } +regiontype_t *findrandomregiontypewithname(char *name) { + regiontype_t *rt,*poss[MAXCANDIDATES]; + int nposs = 0; + char buf[BUFLEN]; + for (rt = firstregiontype ; rt ; rt = rt->next) { + if (streq(buf, rt->name)) { + poss[nposs++] = rt; + } + } + if (nposs) { + return poss[rnd(0,nposs-1)]; + } + return NULL; +} + region_t *findregion(int regionid) { region_t *r; for (r = firstregion ; r ; r = r->next) { @@ -4792,7 +4896,6 @@ region_t *findregionbytype(enum REGIONTYPE rtid) { return NULL; } - map_t *findregionmap(int regionid, int depth) { map_t *m; for (m = firstmap ; m ; m = m->next) { @@ -4801,6 +4904,42 @@ map_t *findregionmap(int regionid, int depth) { return NULL; } +// returns the RT_REGIONLINK regionthing which links to region type rtid (eg. RG_CAVE) +regionthing_t *findregionlink(enum REGIONTYPE rtid) { + region_t *r; + regionthing_t *rt; + int i; + for (r = firstregion ; r ; r = r->next) { + if (!r->outline) continue; + for (i = 0; i < r->outline->nthings; i++ ){ + rt = &r->outline->thing[i]; + if ((rt->whatkind == RT_REGIONLINK) && (rt->value == rtid)) { + return rt; + } + } + } + return NULL; +} + +// find the region thing with the given id. +// if optional 'retregion' is supplied, fill it in with the region which contains the thing. +regionthing_t *findregionthing(int id, region_t **retregion) { + region_t *r; + regionthing_t *rt; + int i; + for (r = firstregion ; r ; r = r->next) { + if (!r->outline) continue; + for (i = 0; i < r->outline->nthings; i++ ){ + rt = &r->outline->thing[i]; + if (rt->id == id) { + if (retregion) *retregion = r; + return rt; + } + } + } + return NULL; +} + regiontype_t *findregiontype(enum REGIONTYPE rtype) { regiontype_t *rt; for (rt = firstregiontype ; rt ; rt = rt->next) { @@ -4809,6 +4948,14 @@ regiontype_t *findregiontype(enum REGIONTYPE rtype) { return NULL; } +regiontype_t *findregiontypebyname(char *name) { + regiontype_t *rt; + for (rt = firstregiontype ; rt ; rt = rt->next) { + if (!strcasecmp(rt->name, name)) return rt; + } + return NULL; +} + room_t *findroom(map_t *m, int roomid) { int i; for (i = 0; i < m->nrooms; i++) { @@ -5432,7 +5579,6 @@ void initmap(void) { // cell types - solid addcelltype(CT_WALL, "rock wall", UNI_SHADEDARK, C_GREY, B_SOLID, B_OPAQUE, MT_STONE, 0, 100); - addcelltype(CT_ROOMWALL, "rock wall", UNI_SHADEDARK, C_GREY, B_SOLID, B_OPAQUE, MT_STONE, 0, 100); addcelltype(CT_WALLDIRT, "dirt wall", UNI_SHADEDARK, C_BROWN, B_SOLID, B_OPAQUE, MT_STONE, 0, 50); addcelltype(CT_WALLWOOD, "wooden wall", UNI_SOLID, C_BROWN, B_SOLID, B_OPAQUE, MT_WOOD, 0, 50); addcelltype(CT_WALLFLESH, "flesh wall", UNI_SOLID, C_RED, B_SOLID, B_OPAQUE, MT_FLESH, 0, 50); @@ -5445,20 +5591,19 @@ void initmap(void) { addcelltype(CT_FLOORWOOD, "wood floor", '.', C_BROWN, B_EMPTY, B_TRANS, MT_WOOD, 0, -1); addcelltype(CT_FLOORFLESH, "flesh floor", '.', C_RED, B_EMPTY, B_TRANS, MT_FLESH, 0, -1); addcelltype(CT_FLOORSHOP, "shop floor", '.', C_BROWN, B_EMPTY, B_TRANS, MT_WOOD, 0, -1); - addcelltype(CT_ROOM, "rock floor", '.', C_GREY, B_EMPTY, B_TRANS, MT_STONE, 0, -1); addcelltype(CT_GRASS, "grass", '.', C_GREEN, B_EMPTY, B_TRANS, MT_PLANT, 0, -1); addcelltype(CT_DIRT, "dirt", '.', C_BROWN, B_EMPTY, B_TRANS, MT_STONE, 0, -1); addcelltype(CT_LOWFLOOR, "low rock floor", '.', C_GREY, B_EMPTY, B_TRANS, MT_STONE, -1, -1); addcelltype(CT_VLOWFLOOR, "very low rock floor", '.', C_GREY, B_EMPTY, B_TRANS, MT_STONE, -2, -1); // region types - addregiontype(RG_WORLDMAP, "World map", H_FOREST, 10, 0, D_NONE, B_TRUE, 0); - addregiontype(RG_MAINDUNGEON, "First Dungeon", H_DUNGEON, 25, 3, D_DOWN, B_TRUE, 0); - addregiontype(RG_CAVE, "Goblin Caves", H_CAVE, 6, 1, D_DOWN, B_TRUE, 5); - addregiontype(RG_HEAVEN, "Realm of Gods", H_HEAVEN, 1, 0, D_NONE, B_FALSE, 0); - addregiontype(RG_PIT, "Pit", H_PIT, 1, 1, D_DOWN, B_FALSE, 0); - addregiontype(RG_SEWER, "Sewer", H_SEWER, 1, 0, D_NONE, B_FALSE, 2); - addregiontype(RG_STOMACH, "Stomach", H_STOMACH, 1, 0, D_NONE, B_FALSE, 0); + addregiontype(RG_WORLDMAP, "The Surface", B_FALSE, H_FOREST, 10, 0, D_NONE, B_TRUE, 0); + addregiontype(RG_MAINDUNGEON, "The Main Dungeon", B_FALSE, H_DUNGEON, 25, 3, D_DOWN, B_TRUE, 0); + addregiontype(RG_CAVE, "The Goblin Caves", B_TRUE, H_CAVE, 6, 1, D_DOWN, B_TRUE, 5); + addregiontype(RG_HEAVEN, "The Realm of Gods", B_FALSE, H_HEAVEN, 1, 0, D_NONE, B_FALSE, 0); + addregiontype(RG_PIT, "A Pit", B_FALSE, H_PIT, 1, 1, D_DOWN, B_FALSE, 0); + addregiontype(RG_SEWER, "A Sewer", B_FALSE, H_SEWER, 1, 0, D_NONE, B_FALSE, 2); + addregiontype(RG_STOMACH, "A Stomach", B_FALSE, H_STOMACH, 1, 0, D_NONE, B_FALSE, 0); // MAPMAPMAPMAP // region definitions (outlines) @@ -5502,6 +5647,15 @@ void initmap(void) { // l25: last level addregionthing(lastregionoutline, 25, NA, NA, RT_RNDVAULTWITHFLAG, F_VAULTISSHRINE, NULL); // godstone on last floor + // 1-3 fixed sewers + addregionthing(lastregionoutline, rnd(1,25), NA, NA, RT_REGIONLINK, RG_SEWER, "drainage grate"); + for (i = 0; i < 2; i++) { + if (onein(2)) { + addregionthing(lastregionoutline, rnd(1,25), NA, NA, RT_REGIONLINK, RG_SEWER, "drainage grate"); + } + } + + // forced shops: addregionthing(lastregionoutline, rnd(2,4), NA, NA, RT_OBJECT, NA, "random building"); addregionthing(lastregionoutline, rnd(5,7), NA, NA, RT_OBJECT, NA, "random building"); @@ -5512,6 +5666,10 @@ void initmap(void) { addregionthing(lastregionoutline, rnd(20,22), NA, NA, RT_OBJECT, NA, "random building"); addregionthing(lastregionoutline, rnd(23,25), NA, NA, RT_OBJECT, NA, "random building"); addregionoutline(RG_CAVE); + + // add initial regions + addregion(RG_WORLDMAP, NULL, -1, 0); + addregion(RG_HEAVEN, NULL, -1, 0); } int isadjacent(cell_t *src, cell_t *dst) { @@ -5534,8 +5692,12 @@ int isdark(cell_t *c) { int isdiggable(cell_t *c) { switch (c->type->id) { - case CT_WALL: return B_TRUE; - case CT_ROOMWALL: return B_TRUE; + case CT_WALLFLESH: + case CT_WALLDIRT: + case CT_WALL: + return B_TRUE; + default: + break; } return B_FALSE; } @@ -5879,7 +6041,7 @@ object_t *linkportal(object_t *srcportal, int wantdepth) { // find a random cell there newcell = getrandomcell(newmap); - while (!cellwalkable(NULL, newcell, NULL) || hasenterableobject(newcell)) { + while (!cellwalkable(NULL, newcell, NULL) || hasenterableobject(newcell) || getcellwaterdepth(newcell, NULL)) { newcell = getrandomcell(newmap); } // add the dst portal diff --git a/map.h b/map.h index 52885e5..165672e 100644 --- a/map.h +++ b/map.h @@ -10,7 +10,7 @@ int addrandomthing(cell_t *c, int obchance, int *nadded); region_t *addregion(enum REGIONTYPE rtype, region_t *parent, int outlineid, int depthmod); regionoutline_t *addregionoutline(enum REGIONTYPE rtype); regionthing_t *addregionthing(regionoutline_t *ro, int depth, int x, int y, enum REGIONTHING whatkind, int value, char *what); -regiontype_t *addregiontype(enum REGIONTYPE id, char *name, enum HABITAT defaulthabitat, int maxdepth, int stairsperlev, int deeperdir, int major, int depthmod); +regiontype_t *addregiontype(enum REGIONTYPE id, char *name, int pluralname, enum HABITAT defaulthabitat, int maxdepth, int stairsperlev, int deeperdir, int major, int depthmod); void adjustcellglyphforlight(cell_t *c, glyph_t *col); int autodoors(map_t *map, int roomid, int minx, int miny, int maxx, int maxy, int doorpct, int dooropenchance); int cellhaslos(cell_t *c1, cell_t *dest); @@ -46,6 +46,9 @@ int countadjwalls(cell_t *cell); int countcellexits(cell_t *cell, int dirtype); int countcellexitsfor(lifeform_t *lf); int countmapobs(map_t *m, enum OBTYPE oid); +int countmapobswithflag(map_t *m, enum FLAG flagid); +int countmapobswithflagval(map_t *m, enum FLAG flagid, int val0, int val1, int val2, char *text); +int countstairs(map_t *m, int dir); void createcave(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob); void createdungeon(map_t *map, int depth, map_t *parentmap, int exitdir, object_t *entryob); void createfakes(map_t *map, cell_t *cell); @@ -69,20 +72,27 @@ void dumpmap(map_t *map, int showrooms); void expand_cave(map_t *map, int numpasses); void explodesinglecell(cell_t *c, int dam, int killwalls, object_t *o, cell_t *centre); void explodecells(cell_t *c, int dam, int killwalls, object_t *o, int range, int dirtype, int wantannounce); +void finalisemap(map_t *map, object_t *entryob); celltype_t *findcelltype(enum CELLTYPE cid); celltype_t *findcelltypebyname(char *name); habitat_t *findhabitat(enum HABITAT id); habitat_t *findhabitatbyname(char *name); map_t *findmap(int mid); map_t *findmapofdepth(int depth); +object_t *findmapobwithflag(map_t *m, enum FLAG flagid); +object_t *findmapobwithflagval(map_t *m, enum FLAG flagid, int val0, int val1, int val2, char *text); cell_t *findmapentrypoint(map_t *m, int side, lifeform_t *lf); object_t *findobidinmap(map_t *m, long id); cell_t *findobinmap(map_t *m, enum OBTYPE oid); regionoutline_t *findoutline(int id); +regiontype_t *findrandomregiontypewithname(char *name); region_t *findregion(int regionid); region_t *findregionbytype(enum REGIONTYPE rtid); +regionthing_t *findregionlink(enum REGIONTYPE rtid); map_t *findregionmap(int regionid, int depth); +regionthing_t *findregionthing(int id, region_t **retregion); regiontype_t *findregiontype(enum REGIONTYPE rtype); +regiontype_t *findregiontypebyname(char *name); room_t *findroom(map_t *m, int roomid); map_t *findsurfaceexitmap(map_t *m); void forgetcells(map_t *map, int amt); diff --git a/move.c b/move.c index 957cba2..7506fbd 100644 --- a/move.c +++ b/move.c @@ -876,7 +876,7 @@ int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher, int fallc i = howfar; // don't fall mightfall = B_FALSE; - if (onein(3)) criticalhit(NULL, lf, getrandomcorebp(lf), dam, DT_BASH); + if (onein(3)) criticalhit(NULL, lf, getrandomcorebp(lf, NULL), dam, DT_BASH); break; case E_SWIMMING: case E_LFINWAY: diff --git a/nexus.c b/nexus.c index 406ceaa..aac1a15 100644 --- a/nexus.c +++ b/nexus.c @@ -44,6 +44,8 @@ hiddenname_t *firsthiddenname = NULL, *lasthiddenname = NULL; npcname_t *npcname; int numnpcnames; +int nextregionthingid = 0; + buildingusage_t buildingusage[MAXBUILDINGTYPES]; int nbuildingusage = 0; @@ -245,7 +247,7 @@ int main(int argc, char **argv) { newworld = B_TRUE; // create world map. - wregion = addregion(RG_WORLDMAP, NULL, -1, 0); + wregion = findregionbytype(RG_WORLDMAP); assert(wregion); addmap(); createmap(firstmap, 1, wregion, NULL, D_NONE, NULL); @@ -255,7 +257,7 @@ int main(int argc, char **argv) { dmap = addmap(); createmap(dmap, 1, dregion, firstmap, D_DOWN, NULL); // create heaven - hregion = addregion(RG_HEAVEN, NULL, -1, 0); + hregion = findregionbytype(RG_HEAVEN); assert(hregion); heaven = addmap(); createmap(heaven, 1, hregion, NULL, D_NONE, NULL); diff --git a/objects.c b/objects.c index b229d4a..1eda5c8 100644 --- a/objects.c +++ b/objects.c @@ -27,6 +27,7 @@ extern obmod_t *firstobmod,*lastobmod; extern material_t *material,*lastmaterial; extern recipe_t *firstrecipe,*lastrecipe; extern skill_t *firstskill, *lastskill; +extern region_t *firstregion; extern buildingusage_t buildingusage[]; extern int nbuildingusage; @@ -453,6 +454,7 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes brand_t *br; obmod_t *om; obmod_t *wantom[MAXOBMODS]; + regionthing_t *wantregionthing = NULL; int bonus = 0; int nom = 0; int n; @@ -766,6 +768,22 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes corpserace = findracebyname(racename); ot = findot(OT_CORPSE); + } else if (strstr(p, "map to ")) { + regiontype_t *rt; + char regionname[BUFLEN]; + p2 = strstr(p, "map to "); + p2 += strlen("map to"); + p2++; // go past the space + // grab name of region this map leads to + strcpy(regionname, p2); + // find the regiontype which matches this. + // if not found, it'll be randoml selected later. + rt = findregiontypebyname(regionname); + if (rt) { + // find the regionthing ID of the RT_REGIONLINK entrance + wantregionthing = findregionlink(rt->id); + } + ot = findot(OT_MAP); } else if (strstr(p, "statue of ")) { char racename[BUFLEN]; @@ -930,6 +948,13 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes ot = findot(OT_BLOODSTAIN); } } + + // don't put floor gratings on low floors + if (ot->id == OT_GRATINGFLOOR) { + if (where->where && (where->where->type->id == CT_LOWFLOOR)) { + setcelltype(where->where, where->where->map->habitat->emptycelltype); + } + } if (gamemode != GM_LOADING) { if (hasflag(ot->flags, F_ONEPERCELL)) { @@ -1429,6 +1454,49 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes cf->val[1] = sizetonutrition(rf->val[0]); } } + } else if (o->type->id == OT_MAP) { + region_t *srcregion; + regiontype_t *dstrt = NULL; + int srcdepth; + char buf[BUFLEN]; + // fill in map destination. + if (!wantregionthing) { + region_t *r; + regionthing_t *rthing,*poss[MAXCANDIDATES]; + int nposs = 0,i; + + for (r = firstregion ; r ; r = r->next) { + if (!r->outline) continue; + for (i = 0; i < r->outline->nthings; i++ ){ + // pick a random regionlink thing. + rthing = &r->outline->thing[i]; + if (rthing->whatkind == RT_REGIONLINK) { + regiontype_t *rtype; + rtype = findregiontype(rthing->value); + if ( (rtype->id != RG_MAINDUNGEON) && + (rtype->id != RG_WORLDMAP)) { + poss[nposs++] = rthing; + } + } + } + } + if (nposs) { + wantregionthing = poss[rnd(0,nposs-1)]; + } + } + assert(wantregionthing); + // we now have the destination regionlink thing which the + // map will lead to. + + // just using this to fill in srcregion + findregionthing(wantregionthing->id, &srcregion); + srcdepth = wantregionthing->depth; + dstrt = findregiontype(wantregionthing->value); + + strcpy(buf, dstrt->name); + makelowercase(buf); + + addflag(o->flags, F_MAPTO, srcregion->id, srcdepth, wantregionthing->id, buf); } else if (o->type->id == OT_STATUE) { flag_t *f, *rf; float ratio; @@ -2724,6 +2792,24 @@ int countobsoftype(obpile_t *op, enum OBTYPE oid) { return count; } +int countobswithflag(obpile_t *op, enum FLAG flagid) { + object_t *o; + int count = 0; + for (o = op->first ; o ; o = o->next) { + if (hasflag(o->flags, flagid)) count++; + } + return count; +} + +int countobswithflagval(obpile_t *op, enum FLAG flagid, int val0, int val1, int val2, char *text) { + object_t *o; + int count = 0; + for (o = op->first ; o ; o = o->next) { + if (hasflagval(o->flags, flagid, val0, val1, val2, text)) count++; + } + return count; +} + int countnoncosmeticobs(obpile_t *op, int onlyifknown) { object_t *o; @@ -4597,7 +4683,7 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan } // end if sight/smell } else if ((o->type->id == OT_SIGN) && !hasflag(o->flags, F_SIGNTEXT)) { strcpy(basename, "blank sign"); - } else if (o->type->id == OT_MAP) { + } else if ((o->type->id == OT_MAP) && isknown(o)) { flag_t *f; f = hasflag(o->flags, F_MAPTO); if (f && getskill(player, SK_CARTOGRAPHY)) { @@ -5008,7 +5094,7 @@ char *real_getobname(object_t *o, char *buf, int count, int wantpremods, int wan } else { char buf2[BUFLEN]; strcat(localbuf, " to "); - getregionname(buf2, newmap, B_FALSE); + getregionname(buf2, newmap, NULL, B_FALSE); strcat(localbuf, buf2); } } @@ -10056,162 +10142,146 @@ int readsomething(lifeform_t *lf, object_t *o) { } } else if (o->type->id == OT_MAP) { if (isplayer(lf)) { - if (!getskill(lf, SK_CARTOGRAPHY)) { - msg("You can't comprehend this map."); - } else if (lf->cell->map->region->rtype->id == RG_WORLDMAP) { - int lfmx,lfmy,tmx,tmy; - int dist; - cell_t *c; - enum SKILLLEVEL slev; - - f = hasflag(o->flags, F_MAPTO); - getmapcoords(lf->cell->map, &lfmx, &lfmy); - tmx = f->val[0]; - tmy = f->val[1]; - - dist = abs(tmx - lfmx) + abs(tmy - lfmy); - slev = getskill(lf, SK_CARTOGRAPHY); - switch (slev) { - default: - msg("You can't comprehend this map."); - break; - case PR_NOVICE: - // here/not here - if (dist == 0) { - msg("%s is in this area!", f->text); - } else { - msg("%s isn't in this area.", f->text); - } - break; - case PR_BEGINNER: - // near/far dist to that map - if (dist == 0) { - msg("%s is in this area!", f->text); - } else if (dist == 1) { - msg("%s is very nearby.", f->text); - } else { - msg("%s isn't nearby.", f->text); - } - break; - case PR_ADEPT: - // x areas away - if (dist == 0) { - msg("%s is in this area!", f->text); - } else if (dist == 1) { - msg("%s is one area away.", f->text); - } else { - msg("%s is %d areas away.", f->text, dist); - } - break; - case PR_SKILLED: - // x areas away - // plus direction. - if (dist == 0) { - msg("%s is in this area!", f->text); - } else { - char dirbuf[BUFLEN]; - if (dist == 1) { - snprintf(buf, BUFLEN, "%s is one area away to the ", f->text); - } else { - snprintf(buf, BUFLEN, "%s is %d areas away to the ", f->text, dist); - } - strcpy(dirbuf, ""); - if (tmy < lfmy) { - strcpy(dirbuf, "north"); - } else if (tmy > lfmy) { - strcpy(dirbuf, "south"); - } - if (tmx > lfmx) { - strcat(dirbuf, "east"); - } else if (tmx < lfmx) { - strcat(dirbuf, "west"); - } - strcat(buf, dirbuf); - strcat(buf, "."); - msg("%s", buf); - } - break; - case PR_EXPERT: - // x areas away - // plus direction. - // plus distance within area. - if (dist == 0) { - int dist2; - char distbuf[BUFLEN]; - c = findobinmap(lf->cell->map, f->val[2]); - dist2 = getcelldist(lf->cell, c); - if (dist2 >= 20) { - strcpy(distbuf, "(very far away)"); - } else if (dist2 >= 10) { - strcpy(distbuf, "(far away)"); - } else if (dist2 >= 5) { - strcpy(distbuf, "(nearby)"); - } else { - strcpy(distbuf, "(very nearby)"); - } - msg("%s is in this area %s!", f->text, distbuf); - } else { - char dirbuf[BUFLEN]; - if (dist == 1) { - snprintf(buf, BUFLEN, "%s is one area away to the ", f->text); - } else { - snprintf(buf, BUFLEN, "%s is %d areas away to the ", f->text, dist); - } - strcpy(dirbuf, ""); - if (tmy < lfmy) { - strcpy(dirbuf, "north"); - } else if (tmy > lfmy) { - strcpy(dirbuf, "south"); - } - if (tmx > lfmx) { - strcat(dirbuf, "east"); - } else if (tmx < lfmx) { - strcat(dirbuf, "west"); - } - strcat(buf, dirbuf); - strcat(buf, "."); - msg("%s", buf); - } - break; - case PR_MASTER: - // x,y coords. - // plus direction. - // plus show on map if in area. - if (dist == 0) { - msg("You have located %s in this area.", f->text); - c = findobinmap(lf->cell->map, f->val[2]); - setcellknownradius(c, slev, 5, DT_ORTH); - } else { - char dirbuf[BUFLEN]; - char buf2[BUFLEN]; - snprintf(buf, BUFLEN, "%s is at %d,%d",f->text, tmx,tmy); - if (dist == 1) { - snprintf(buf2, BUFLEN, " (one area away to the "); - } else { - snprintf(buf2, BUFLEN, " (%d areas away to the ", dist); - } - strcpy(dirbuf, ""); - if (tmy < lfmy) { - strcpy(dirbuf, "north"); - } else if (tmy > lfmy) { - strcpy(dirbuf, "south"); - } - if (tmx > lfmx) { - strcat(dirbuf, "east"); - } else if (tmx < lfmx) { - strcat(dirbuf, "west"); - } - strcat(buf2, dirbuf); - strcat(buf2, ")"); - - msg("%s%s", buf, buf2); - } - break; + region_t *srcregion = NULL; + regionthing_t *destthing = NULL; + regiontype_t *destregiontype = NULL; + int srcdepth = -1, plural = B_FALSE; + char isare[BUFLENSMALL]; + f = hasflag(o->flags, F_MAPTO); + if (f) { + srcregion = findregion(f->val[0]); + srcdepth = f->val[1]; + destthing = findregionthing(f->val[2], NULL); + destregiontype = findregiontype(destthing->value); + plural = destregiontype->pluralname; + if (plural) { + strcpy(isare, "are"); + } else { + strcpy(isare, "is"); } - } else { - msg("You need to be outside to get your bearings first."); } - } + if (f && srcregion) { + enum SKILLLEVEL slev; + slev = getskill(lf, SK_CARTOGRAPHY); + if (!slev) { + msg("You can't comprehend this map."); + } else { + if (lf->cell->map->region == srcregion) { + if (lf->cell->map->depth == srcdepth) { + // on the correct map. at this point the f_climbable flag should + // have been filled in correctly, and the destination region created. + cell_t *destcell; + object_t *destob; + char distbuf[BUFLEN],distbufbad[BUFLEN]; + char dirbuf[BUFLEN]; + region_t *destregion = NULL; + int dist; + destregion = findregionbytype(destthing->value); + + destob = findmapobwithflagval(lf->cell->map, F_CLIMBABLE, NA, destregion->id, NA, NULL); + destcell = getoblocation(destob); + dist = getcelldist(lf->cell, destcell); + getdisttext(lf->cell, destcell, distbuf, distbufbad, dirbuf); + + switch (slev) { + default: break; // should never happen + case PR_NOVICE: + // here/not here + msg("%s %s in this area!", f->text, isare); + break; + case PR_BEGINNER: + // direction to cell + msg("%s %s to the %s.", f->text, isare, dirbuf); + break; + case PR_ADEPT: + // direction + + // near/far dist to cell + msg("%s %s %s to the %s.", f->text, isare, distbufbad, dirbuf); + break; + case PR_SKILLED: + // direction + + // good dist to cell + msg("%s %s %s to the %s.", f->text, isare, distbuf, dirbuf); + break; + case PR_EXPERT: + // direction + + // exact dist to cell + msg("%s %s %d metres away to the %s.", f->text, isare, dist, dirbuf); + break; + case PR_MASTER: + // reveal it + msg("You have located %s in this area.", f->text); + setcellknownradius(destcell, PR_MASTER, 5, DT_ORTH); + needredraw = B_TRUE; + break; + } + } else { + // correct region, wrong map + int dist; + char buf[BUFLEN]; + dist = abs(lf->cell->map->depth - srcdepth); + switch (slev) { + default: break; // should never happen + case PR_NOVICE: + case PR_BEGINNER: + // here/not here + msg("%s %s in this area.", f->text, plural ? "aren't" : "isn't"); + break; + case PR_ADEPT: + // up/down + if (lf->cell->map->depth > srcdepth) { + msg("%s %s somewhere above you.", f->text, isare); + } else { + msg("%s %s somewhere below you.", f->text, isare); + } + break; + case PR_SKILLED: + // up/down and how far + if (dist == 1) { + strcpy(buf, "just"); + } else if (dist <= 3) { + strcpy(buf, "somewhere"); + } else { + strcpy(buf, "very far"); + } + msg("%s %s %s %s you.", f->text, isare, buf, + (lf->cell->map->depth > srcdepth) ? "above" : "below"); + break; + case PR_EXPERT: + case PR_MASTER: + // level number + msg("%s %s on level %d.", f->text, isare, srcdepth); + break; + } + } // end correct map or not? + } else { + // wrong region and wrong map + switch (slev) { + default: break; // should never happen + case PR_NOVICE: + case PR_BEGINNER: + // here/not here + msg("%s %s in this area.", f->text, plural ? "aren't" : "isn't"); + break; + case PR_ADEPT: + case PR_SKILLED: + // tell which area it is in (without level) + getregionname(buf, NULL, srcregion, B_FALSE); + msg("%s %s somewhere within %s.", f->text, isare, buf); + break; + case PR_EXPERT: + case PR_MASTER: + // which area it is in, plus which floor + getregionname(buf, NULL, srcregion, B_TRUE); + msg("%s %s in %s.", f->text, isare, buf); + break; + } + } // end if correct region etc + } // if slev + } else { + msg("This map doesn't seem to be finished."); + } + } //end if isplayer } else if (o->type->id == OT_SCR_AWARENESS) { addtempflag(lf->flags, F_AWARENESS, B_TRUE, NA, NA, NULL, getspellduration(30,60,o->blessed)); if (isplayer(lf)) msg("The scroll crumbles to dust."); diff --git a/objects.h b/objects.h index 3a10c30..c185008 100644 --- a/objects.h +++ b/objects.h @@ -42,6 +42,8 @@ int countmoney(obpile_t *op); int countnames(char **list); int countobs(obpile_t *op, int onlyifknown); int countobsoftype(obpile_t *op, enum OBTYPE oid); +int countobswithflag(obpile_t *op, enum FLAG flagid); +int countobswithflagval(obpile_t *op, enum FLAG flagid, int val0, int val1, int val2, char *text); int countnoncosmeticobs(obpile_t *op, int onlyifknown); int curseob(object_t *o); void damageallobs(object_t *srcob, obpile_t *op, int howmuch, int damtype); diff --git a/save.c b/save.c index 272ed0e..f0bee29 100644 --- a/save.c +++ b/save.c @@ -637,16 +637,18 @@ int loadregions(void) { fscanf(f, "numoutlines:%d\n",&numoutlines); if (db) dblog("Found %d region outlines.\n",numoutlines); for (n = 0; n < numoutlines; n++) { + regionthing_t *thing; fscanf(f, "startro\n"); fscanf(f, "rtypeid:%d\n",&rtid); // region type id addregionoutline(rtid); fscanf(f, "nthings:%d\n",&nthings); for (i = 0; i < nthings; i++) { - int depth,x,y,val; + int depth,x,y,val,id; enum REGIONTHING whatkind; char buf[BUFLEN],*p; fscanf(f, "startthing\n"); + fscanf(f, " thingid:%d\n",&id); fscanf(f, " thingdepth:%d,%d,%d\n",&depth, &x, &y); fscanf(f, " thingkind:%d\n",(int *)&whatkind); fscanf(f, " thingval:%d\n",&val); @@ -657,7 +659,8 @@ int loadregions(void) { for (p = buf ; *p; p++) { if (*p == '^') *p = ' '; } - addregionthing(lastregionoutline, depth, x, y, whatkind, val, streq(buf, "NULL") ? NULL : buf); + thing = addregionthing(lastregionoutline, depth, x, y, whatkind, val, streq(buf, "NULL") ? NULL : buf); + thing->id = id; } fscanf(f, "endro\n"); if (db) dblog("Loaded regionoutline #%d / %d",n+1, numoutlines); @@ -1028,6 +1031,7 @@ int saveregions(void) { fprintf(f, "nthings:%d\n",ro->nthings); for (i = 0; i < ro->nthings; i++) { fprintf(f, "startthing\n"); + fprintf(f, " thingid:%d\n",(int)ro->thing[i].id); fprintf(f, " thingdepth:%d,%d,%d\n",ro->thing[i].depth, ro->thing[i].x, ro->thing[i].y); fprintf(f, " thingkind:%d\n",(int)ro->thing[i].whatkind); fprintf(f, " thingval:%d\n",(int)ro->thing[i].value); diff --git a/spell.c b/spell.c index ee9a131..19f31e3 100644 --- a/spell.c +++ b/spell.c @@ -5537,7 +5537,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ getlfname(target, targetname); msg("Unseen forces rip into %s%s flesh!", targetname, getpossessive(targetname)); } - criticalhit(caster, target, getrandomcorebp(target), rnd(1,6), DT_SLASH); + criticalhit(caster, target, getrandomcorebp(target, NULL), rnd(1,6), DT_SLASH); pleasegodmaybe(R_GODDEATH, 3); } else if (spellid == OT_S_GLYPHWARDING) { char buf[BUFLEN]; @@ -7829,7 +7829,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ char ch = 'a'; char mapname[BUFLEN]; - getregionname(mapname, m, B_TRUE); + getregionname(mapname, m, NULL, B_TRUE); capitalise(mapname); if (m->habitat->id == H_HEAVEN) continue; @@ -9038,41 +9038,64 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (corpse) { char buf[BUFLEN],corpsename[BUFLEN]; char ch; + int done = B_FALSE; getobname(corpse, corpsename, 1); - snprintf(buf, BUFLEN, "What will you ask %s?", corpsename); - initprompt(&prompt, buf); - addchoice(&prompt, 'a', "How did you die?", NULL, NULL, NULL); - addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL); + msg("An ghostly spirit rises from %s!", corpsename); more(); + while (!done) { + snprintf(buf, BUFLEN, "What will you ask %s?", corpsename); + initprompt(&prompt, buf); + addchoice(&prompt, 'a', "How did you die?", NULL, NULL, NULL); + addchoice(&prompt, 'b', "Tell me about this area", NULL, NULL, NULL); + addchoice(&prompt, 'c', "Are there any hidden dangers nearby?", NULL, NULL, NULL); + addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL); + prompt.maycancel = B_TRUE; - ch = getchoice(&prompt); - if (ch == 'a') { - flag_t *f; - char *p; - - snprintf(buf, BUFLEN, "%s whispers:", corpsename); - msg(buf); - - f = hasflag(corpse->flags, F_CORPSEOF); - if (f && strlen(f->text)) { - char killer[BUFLEN]; - char weapon[BUFLEN]; - p = readuntil(killer, f->text, '^'); - if (strstr(p, "weilding")) { - p = readuntil(weapon, p, '^'); - } else { - strcpy(weapon, ""); - } - snprintf(buf, BUFLEN, "\"I was killed by %s", killer); - if (strlen(weapon)) { - strcat(buf, ", "); - strcat(buf, weapon); - } - strcat(buf, ".\""); + + ch = getchoice(&prompt); + if ((ch != '\0') && (ch != '-')) { + snprintf(buf, BUFLEN, "%s whispers:", corpsename); msg(buf); - } else { - msg("\"I do not know what killed me.\""); } - } + if (ch == 'a') { + flag_t *f; + char *p; + + f = hasflag(corpse->flags, F_CORPSEOF); + if (f && strlen(f->text)) { + char killer[BUFLEN]; + char weapon[BUFLEN]; + p = readuntil(killer, f->text, '^'); + if (strstr(p, "weilding")) { + p = readuntil(weapon, p, '^'); + } else { + strcpy(weapon, ""); + } + snprintf(buf, BUFLEN, "\"I was killed by %s", killer); + if (strlen(weapon)) { + strcat(buf, ", "); + strcat(buf, weapon); + } + strcat(buf, ".\""); + msg(buf); + } else { + msg("\"I do not know what killed me.\""); + } + } else if (ch == 'b') { + genareaknowledge(corpse->flags, 50); + docomms_areainfo(corpsename, corpse->flags, NULL); + done = B_TRUE; + } else if (ch == 'c') { + genareaknowledge(corpse->flags, 50); + docomms_areadangers(corpsename, corpse->flags, NULL); + done = B_TRUE; + } else { + done = B_TRUE; + } + } // end while !done + + // destroy the corpse. + msg("%s crumbles to dust.", corpsename); + removeob(corpse, ALL); } } else if (spellid == OT_S_STENCH) { int howlong; diff --git a/text.c b/text.c index 48a3c70..f22ac9d 100644 --- a/text.c +++ b/text.c @@ -925,12 +925,15 @@ char *getrarityname(enum RARITY rr) { return "?unknownrarity?"; } -char *getregionname(char *buf, map_t *m, int withlevel) { - region_t *r; +// pass in EITHER m or r, not both. +// +// if withlevel is TRUE, "m" should be passed. +char *getregionname(char *buf, map_t *m, region_t *r, int withlevel) { + if (!r) { + r = m->region; + } - r = m->region; - - if (withlevel) { + if (withlevel && m) { flag_t *f; int x,y; f = hasflag(m->flags, F_MAPCOORDS); @@ -1187,6 +1190,16 @@ char *makekillertext(char *retbuf, char *killverb, char *lastdam, int wantextra) return retbuf; } +char *makelowercase(char *text) { + if (strlen(text) > 0) { + char *p; + for (p = text ; *p; p++) { + *p = tolower(*p); + } + } + return text; +} + // allocates and returns new string char *makeplural(char *text) { char lastlet; diff --git a/text.h b/text.h index f6a4950..9778356 100644 --- a/text.h +++ b/text.h @@ -27,7 +27,7 @@ char *getinjuredbpname(enum BODYPART bp); char *getinjuryname(enum DAMTYPE dt); char *getinjurydesc(enum BODYPART bp, enum DAMTYPE dt); char *getrarityname(enum RARITY rr); -char *getregionname(char *buf, map_t *m, int withlevel); +char *getregionname(char *buf, map_t *m, region_t *r, int withlevel); char *getreldirname(int reldir); char *getsizetext(enum LFSIZE sz); char *gettimetext(char *retbuf); @@ -38,6 +38,7 @@ char *is(lifeform_t *lf); int isvowel(char c); void makegunaimstring(lifeform_t *lf, int lfid, char *retbuf); char *makekillertext(char *retbuf, char *killverb, char *lastdam, int wantextra); +char *makelowercase(char *text); char *makeplural(char *text); char *makethrowaccstring(lifeform_t *lf, cell_t *c, flag_t *throwflag, char *retbuf); char *makeuppercase(char *text); diff --git a/vaults/jimbo.vlt b/vaults/jimbo.vlt index ed9dde8..e890608 100644 --- a/vaults/jimbo.vlt +++ b/vaults/jimbo.vlt @@ -3,11 +3,11 @@ @map ############ -#....c...|,# -#....-/-.### -+......@.|,# -#..-/-...### -#....c...|,# +#>+..c...|,# +###..-/-.### +X......@>|,# +###-/-...### +#>+..c...|,# ############ @end @@ -16,8 +16,10 @@ |:ob:locked iron gate ,:ob:1-4 bones:50 ,:mon:prisoner:50 +X:ob:wooden door +X:exit +>:ob:staircase going down +:ob:wooden door -+:exit /:ob:wooden table -:ob:wooden footstool c:ob:lit candelabrum