diff --git a/ai.c b/ai.c index a8ecd95..6fee197 100644 --- a/ai.c +++ b/ai.c @@ -759,6 +759,7 @@ int ai_bored(lifeform_t *lf, lifeform_t *master, int icanattack) { } if (pctchance(chance)) { + flag_t *f; if (lfhasflag(lf, F_HATESALL) || lfhasflag(lf, F_RAGE)) { if (nhateposs < MAXCANDIDATES) { if (db) dblog(".oO { hate everything - found lfid %d (%s) ! }",who->id, who->race->name); @@ -767,11 +768,18 @@ int ai_bored(lifeform_t *lf, lifeform_t *master, int icanattack) { break; } else if (lfhasflagval(lf, F_HATESRACE, who->race->id, NA, NA, NULL) || lfhasflagval(lf, F_HATESRACE, who->race->baseid, NA, NA, NULL) ) { - if (nhateposs < MAXCANDIDATES) { + if ((nhateposs < MAXCANDIDATES) && !areallies(lf, who)) { if (db) dblog(".oO { found a hated target - lfid %d (%s) ! }",who->id, who->race->name); hateposs[nhateposs++] = who; } break; + } else if ( ((f = lfhasflag(lf, F_TERRITORIAL)) != NULL) && + (getcelldist(who->cell, lf->cell) <= f->val[0]) ) { + if ((nhateposs < MAXCANDIDATES) && !areallies(lf, who)) { + if (db) dblog(".oO { territorial and found target in range - lfid %d (%s) ! }",who->id, who->race->name); + hateposs[nhateposs++] = who; + } + break; } else if (!nhateposs && areenemies(lf, who)) { // dont check if we've already found a hated target if (nposs < MAXCANDIDATES) { if (db) dblog(".oO { found an enemy target - lfid %d (%s) ! }",who->id, who->race->name); @@ -780,7 +788,9 @@ int ai_bored(lifeform_t *lf, lifeform_t *master, int icanattack) { } else { getflags(lf->flags, retflag, &nretflags, F_HATESRACEWITHFLAG, F_NONE); for (i = 0; i < nretflags; i++) { - if (lfhasflag(who, retflag[i]->id)) { + if (lfhasflag(who, retflag[i]->id) && + !areallies(lf, who)) { + if (db) dblog(".oO { found a target with hated flags - lfid %d (%s) ! }",who->id, who->race->name); hateposs[nhateposs++] = who; } diff --git a/attack.c b/attack.c index 12b67ba..de11cca 100644 --- a/attack.c +++ b/attack.c @@ -1542,38 +1542,11 @@ int attackwall(lifeform_t *lf, cell_t *c, object_t *wep, flag_t *damflag) { dam[i] += rnd(1,6); } - // adjust dam - adjustdammaterial(&dam[i], damtype[i], c->type->material->id); - if (dam[i] > 0) { - // wall loses hp - c->hp -= dam[i]; - if (c->hp <= 0) { - char cellname[BUFLEN]; - int shattered = B_FALSE; - enum MATERIAL cellmat; - // remember cell properties - sprintf(cellname, "%s %s", needan(c->type->name) ? "An" : "A", c->type->name); - cellmat = c->type->material->id; - // cell dies (have to do this before calling fragments()) - setcelltype(c, c->map->habitat->emptycelltype); - // announce - if (haslos(player, c)) { - msg("%s %s!", cellname, willshatter(cellmat) ? "shatters" : "is destroyed"); - } - // shatter? - if (willshatter(cellmat)) { - char what[BUFLEN]; - shattered = B_TRUE; - noise(c, NULL, NC_OTHER, SV_CAR, "something shattering.", NULL); - if (getshardobname(cellmat, what)) { - fragments(c, what, 3, 3); - } - } - break; - } + damagecell(c, dam[i], damtype[i]); + // don't deal any more damage types + break; } - } // end foreach damtype // no special weapon effects on cells. diff --git a/data.c b/data.c index 2ccee55..3e2da67 100644 --- a/data.c +++ b/data.c @@ -67,6 +67,7 @@ option_t *addoption(enum OPTION id, char *text, int def) { a->next = NULL; // set props + a->id = id; a->text = strdup(text); a->def = def; a->enabled = def; @@ -490,6 +491,7 @@ void initjobs(void) { addflag(lastjob->flags, F_STARTSKILL, SK_RANGED, PR_NOVICE, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_SWIMMING, PR_BEGINNER, NA, NULL); addflag(lastjob->flags, F_STARTSKILL, SK_STEALTH, PR_NOVICE, NA, NULL); + addflag(lastjob->flags, F_STARTSKILL, SK_THROWING, PR_NOVICE, NA, NULL); // learnable skills addflag(lastjob->flags, F_CANLEARN, SK_ATHLETICS, NA, NA, NULL); addflag(lastjob->flags, F_CANLEARN, SK_CLUBS, NA, NA, NULL); @@ -1425,8 +1427,8 @@ void initobjects(void) { addflag(lastot->flags, F_OPPOSITESTAIRS, OT_TUNNELUP, NA, NA, NULL); addflag(lastot->flags, F_NOPICKUP, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOBLESS, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_MAKESNOISE, 33, 3, NA, "a strange echoing."); - addflag(lastot->flags, F_MAKESNOISE, 33, 3, NA, "an echoing drip."); + addflag(lastot->flags, F_MAKESNOISE, 33, 1, NA, "a strange echoing."); + addflag(lastot->flags, F_MAKESNOISE, 33, 1, NA, "an echoing drip."); addot(OT_TUNNELUP, "tunnel leading up", "A wide tunnel leading upwards.", MT_STONE, 3000, OC_DFEATURE, SZ_HUGE); addflag(lastot->flags, F_GLYPH, C_BROWN, '<', NA, NULL); addflag(lastot->flags, F_CLIMBABLE, D_UP, NA, NA, NULL); @@ -3908,6 +3910,8 @@ void initobjects(void) { addflag(lastot->flags, F_RANGE, 1, NA, NA, NULL); addot(OT_A_STUDYSCROLL, "study scroll", "Attempt to learn a spell directly from a scroll.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); + addot(OT_A_STRIKETOKO, "merciful fighting", "Try to knock out your opponents rather than killing them.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_SUCKBLOOD, "suck blood", "You can suck the blood from enemies after attaching to them.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJVICTIM, NA, NA, NULL); @@ -4422,8 +4426,8 @@ void initobjects(void) { addflag(lastot->flags, F_RECHARGEWHENOFF, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_EXPLODEONDEATH, NA, NA, B_IFACTIVATED, "8d2"); - addflag(lastot->flags, F_EXPLODEONDAM, NA, NA, B_IFACTIVATED, "5d2"); + addflag(lastot->flags, F_EXPLODEONDEATH, NA, 1, B_IFACTIVATED, "16d2"); + addflag(lastot->flags, F_EXPLODEONDAM, NA, 1, B_IFACTIVATED, "13d2"); addflag(lastot->flags, F_GRENADE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_TECHLEVEL, PR_BEGINNER, NA, NA, NULL); addflag(lastot->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL); @@ -4457,7 +4461,7 @@ void initobjects(void) { addflag(lastot->flags, F_RECHARGEWHENOFF, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOOBDAMTEXT, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_NOOBDIETEXT, B_TRUE, NA, NA, NULL); - addflag(lastot->flags, F_EXPLODEONDEATH, NA, B_BIG, B_IFACTIVATED, "90d2"); // 90 - 180 damage + addflag(lastot->flags, F_EXPLODEONDEATH, NA, 2, B_IFACTIVATED, "90d2"); // 90 - 180 damage addflag(lastot->flags, F_GRENADE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_TECHLEVEL, PR_BEGINNER, NA, NA, NULL); addflag(lastot->flags, F_HASHIDDENNAME, B_TRUE, NA, NA, NULL); @@ -4946,7 +4950,7 @@ void initobjects(void) { addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL); addflag(lastot->flags, F_OBHP, 30, 30, NA, NULL); - addot(OT_FIREPLACE, "fireplace", "A roaring fireplace.", MT_STONE, 200, OC_FURNITURE, SZ_LARGE); + addot(OT_FIREPLACE, "brazier", "A heavy iron bowl filled with fire.", MT_METAL, 200, OC_FURNITURE, SZ_LARGE); addflag(lastot->flags, F_RARITY, H_DUNGEON, 100, RR_COMMON, NULL); addflag(lastot->flags, F_RARITY, H_CAVE, 100, RR_RARE, NULL); addflag(lastot->flags, F_GLYPH, C_RED, '\\', NA, NULL); @@ -6154,6 +6158,7 @@ void initobjects(void) { addflag(lastot->flags, F_USESSKILL, SK_AXES, NA, NA, NULL); addflag(lastot->flags, F_ATTREQ, A_STR, 9, 10, NULL); addflag(lastot->flags, F_CRITCHANCE, 5, NA, NA, NULL); + addot(OT_BATTLEAXE, "battleaxe", "An large axe specifically designed for combat.", MT_METAL, 8, OC_WEAPON, SZ_MEDIUM); addflag(lastot->flags, F_RARITY, H_DUNGEON, 70, NA, NULL); addflag(lastot->flags, F_RARITY, H_CAVE, 70, NA, NULL); @@ -7001,6 +7006,7 @@ void initobjects(void) { void initoptions(void) { addoption(OPT_ALWAYSSHOWTRAILS, "always show trail objects", B_FALSE); + addoption(OPT_AUTORELOAD, "automatically reload empty firearms", B_TRUE); addoption(OPT_STOPRUNONNOISE, "stop running if sound heard", B_TRUE); } @@ -8975,6 +8981,7 @@ void initrace(void) { addflag(lastrace->flags, F_MORALE, 5, NA, NA, NULL); addflag(lastrace->flags, F_CASTCHANCE, 30, NA, NA, NULL); addflag(lastrace->flags, F_NOSLEEP, B_TRUE, NA, NA, NULL); + addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL); addrace(R_SPRITEICE, "ice sprite", 5, 'n', C_WHITE, MT_ICE, RC_MAGIC, "A small magical creature made from freezing ice."); setbodytype(lastrace, BT_HUMANOID); @@ -11756,6 +11763,7 @@ void initskills(void) { if (isweaponskill(sk->id) || (sk->id == SK_UNARMED)) { addskilldesc(sk->id, PR_INEPT, "This skill increases your accuracy and damage when using matching weapons.", B_FALSE); addskilldesc(sk->id, PR_NOVICE, "^g-2 accuracy penalty.^n", B_FALSE); + if (sk->id == SK_CLUBS) addskilldesc(sk->id, PR_NOVICE, "^gYou gain the 'merciful fighting' ability.^n", B_FALSE); addskilldesc(sk->id, PR_BEGINNER, "^g-1 accuracy penalty^n", B_FALSE); addskilldesc(sk->id, PR_BEGINNER, "^gYou gain the 'wild strike' ability.^n", B_FALSE); addskilldesc(sk->id, PR_ADEPT, "^g+10% damage bonus.^n", B_FALSE); diff --git a/defs.h b/defs.h index 5e6bc08..65567d7 100644 --- a/defs.h +++ b/defs.h @@ -1503,6 +1503,7 @@ enum OBTYPE { OT_A_SPRINT, OT_A_STUDYSCROLL, OT_A_STINGACID, // need to define dam in f_canwill + OT_A_STRIKETOKO, OT_A_SUCKBLOOD, OT_A_SWALLOW, OT_A_SWOOP, @@ -2125,12 +2126,12 @@ enum FLAG { F_ACTIVATED, // val0 = is this object turned on? F_GRENADE, // this object will drain charge when activated, then die F_EXPLODEONDEATH, // explodes when it dies, deals TEXT damage. - // val1 = BIG means hit surrounding cells + // val1 = explosion radius (dtorth) // val2 = ifactivated, only explodes if activated. F_EXPLODEONDAM, // explodes when it is damaged, deals TEXT damage. // v0 = damage type which makes it explode. // NA means 'any damage type' - // val1 = BIG means hit surrounding cells + // val1 = explosion radius (dtorth) // val2 = ifactivated, only explodes if activated. F_FLASHONDEATH, // produce a bright flash when it dies,v0=range F_FLASHONDAM, // produces a bright flash when it is damaged,v0=range,v2=ifacctivated @@ -2638,6 +2639,7 @@ enum FLAG { F_HATESRACE, // lf will attack lfs with race=v0 or baseid=v0 on // sight F_HATESRACEWITHFLAG, // lf will attack lfs with flag v0 on sight + F_TERRITORIAL, // lf will attack ALL other visible lfs within range v0 F_HARMLESS, // it is safe to rest around this lf F_RNDHOSTILE, // v0% chance of being hostile. F_HOSTILE, // lf will attack the player if in sight @@ -2772,6 +2774,7 @@ enum FLAG { F_COMBOSTRIKE, // lf is performing a combination strike F_HEAVYBLOW, // next attack is a heavy blow F_QUIVERINGPALM, // your next strike will be a quivpalm attack + F_STRIKETOKO, // your attacks will never kill, just KO. F_TKTHROW, // when you throw an object, use your // attrib = v0 and skilltype = v1 // rather than AGI and SK_THROWING like normal @@ -3287,6 +3290,7 @@ typedef struct warning_s { enum OPTION { OPT_ALWAYSSHOWTRAILS, + OPT_AUTORELOAD, OPT_STOPRUNONNOISE, }; diff --git a/io.c b/io.c index d2a1191..079b830 100644 --- a/io.c +++ b/io.c @@ -775,16 +775,6 @@ cell_t *askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *src strcat(extrainfo, buf2); } - - // need a certain amount of race knowledge to recognise ai traits - if (lorelev >= PR_SKILLED) { - f = lfhasflag(c->lf, F_BEHAVIOUR); - if (f) { - if (strlen(extrainfo)) strcat(extrainfo, ", "); - strcat(extrainfo, f->text); - } - } - if ((getallegiance(c->lf) == AL_HOSTILE) && (lorelev >= PR_ADEPT)) { char dangerbuf[BUFLEN]; @@ -960,7 +950,7 @@ cell_t *askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *src cell_t *newcell; int bad = B_FALSE; if (srclf) { - if (!haslof_real(srclf->cell, c, LOFTYPE, &newcell, srclf)) { + if (!haslof_real(srclf->cell, c, LOFTYPE, &newcell, srclf, B_TRUE)) { bad = B_TRUE; } } else { @@ -1346,8 +1336,9 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { if (ot) { enum SPELLSCHOOL school; school = getspellschoolknown(lf, ot->id); - msg("^gYou have learned the %s '%s'.", - (school == SS_MENTAL) ? "psionic power" : "spell", ot->name); + msg("^gYou have learned the %s '%s'%s.", + (school == SS_MENTAL) ? "psionic power" : "spell", ot->name, + getflagsourcetext(f)); donesomething = B_TRUE; } } @@ -1358,7 +1349,8 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { ot = findot(f->val[0]); if (ot && (!hasflag(ot->flags, F_NOANNOUNCE))) { char buf[BUFLEN]; - snprintf(buf, BUFLEN, "^gYou have gained the ability '%s'.", ot->name); + snprintf(buf, BUFLEN, "^gYou have gained the ability '%s'%s.", ot->name, + getflagsourcetext(f)); /* if (f->val[2] != NA) { char turnbuf[BUFLEN]; @@ -1758,6 +1750,12 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { msg("%s start%s emitting a foul odour!",lfname, isplayer(lf) ? "" : "s" ); donesomething = B_TRUE; break; + case F_STRIKETOKO: + if (isplayer(lf)) { // don't know if monsters get it + msg("Your attacks will now be non-lethal."); + donesomething = B_TRUE; + } + break; case F_STUNNED: msg("%s %s stunned!",lfname, is(lf)); donesomething = B_TRUE; @@ -2353,6 +2351,12 @@ int announceflagloss(lifeform_t *lf, flag_t *f) { msg("%s no longer smell%s bad.",lfname, isplayer(lf) ? "" : "s" ); donesomething = B_TRUE; break; + case F_STRIKETOKO: + if (isplayer(lf)) { // don't know if monsters lose it + msg("Your attacks will no longer be non-lethal."); + donesomething = B_TRUE; + } + break; case F_STUNNED: msg("%s %s no longer stunned.",lfname, is(lf)); donesomething = B_TRUE; @@ -6012,14 +6016,19 @@ char *makedesc_ob(object_t *o, char *retbuf) { if (usable && isweapon(o)) { if (pctmod > 0) { - sprintf(buf, "^%dYour high %s will increase your %s with this weapon.^n\n", C_GREEN, + sprintf(buf, "^%dYour high %s will increase your %s with this weapon by %d%s.^n\n", + C_GREEN, getattrname(f->val[0]), - (f->val[0] == A_AGI) ? "accuracy" : "damage"); + (f->val[0] == A_AGI) ? "accuracy" : "damage", + (f->val[0] == A_AGI) ? getaccuracynum(pctmod) : pctmod, + (f->val[0] == A_AGI) ? "" : "%"); strncat(retbuf, buf, HUGEBUFLEN); } else if (pctmod < 0) { - sprintf(buf, "^%dYour low %s will decrease your %s with this weapon.^n\n", C_BROWN, + sprintf(buf, "^%dYour low %s will decrease your %s with this weapon by %d%s.^n\n", C_BROWN, getattrname(f->val[0]), - (f->val[0] == A_AGI) ? "accuracy" : "damage"); + (f->val[0] == A_AGI) ? "accuracy" : "damage", + (f->val[0] == A_AGI) ? getaccuracynum(abs(pctmod)) : abs(pctmod), + (f->val[0] == A_AGI) ? "" : "%"); strncat(retbuf, buf, HUGEBUFLEN); } } @@ -7513,18 +7522,21 @@ int dotakeoff(obpile_t *op) { return rv; } -void dothrow(obpile_t *op) { - object_t *o; +// specify EITHER op or o. +// returns B_TRUE on failure. +int dothrow(obpile_t *op, object_t *o) { char buf[BUFLEN],buf2[BUFLEN]; flag_t *f; if (!hasbp(player, BP_HANDS)) { msg("You have no hands to throw with!"); - return; + return B_TRUE; } // ask which object to throw - o = askobject(op, "Throw what", NULL, 't', AO_NONE); + if (!o) { + o = askobject(op, "Throw what", NULL, 't', AO_NONE); + } if (o) { int maxdist; char subprompt[BUFLEN],oidbuf[BUFLENSMALL]; @@ -7534,7 +7546,7 @@ void dothrow(obpile_t *op) { f = hasflag(o->flags, F_EQUIPPED); if (f && (f->val[0] != BP_WEAPON)) { msg("You'll need to take it off first."); - return; + return B_TRUE; } // calculate throw range @@ -7558,14 +7570,14 @@ void dothrow(obpile_t *op) { if (ch == 'y') { // update destination cell. where = newwhere; - } else return; + } else return B_TRUE; } else { if (reason == E_NOLOS) { msg("You can't see there!"); } else { // ie. E_NOLOF and no new cell msg("You don't have a clear line of fire to there."); } - return; + return B_TRUE; } } @@ -7577,9 +7589,11 @@ void dothrow(obpile_t *op) { } } else { throwat(player, o, where); + return B_FALSE; } } - } + } + return B_TRUE; } // returns TRUE if escape pressed @@ -8683,8 +8697,8 @@ void handleinput(void) { // something here? if (stopnow) { - } else if (!ihaveturned && moveclear(player, lastdir, NULL)) { - // haven't turned yet? + } else if (!ihaveturned && moveclear(player, lastdir, NULL) && isroom(player->cell)) { + // haven't turned yet and in a room? // if walls to our l+r have changed... if (getleftrightwalls(player) != walls) { stopnow = B_TRUE; @@ -9040,7 +9054,7 @@ void handleinput(void) { doquaff(player->pack); break; case 't': // throw - dothrow(player->pack); + dothrow(player->pack, NULL); break; case 'P': // Pour dopour(player->pack); @@ -11771,6 +11785,12 @@ void showlfstats(lifeform_t *lf, int showall) { y++; } + f = lfhasflag(lf, F_STRIKETOKO); + if (f && (f->known)) { + mvwprintw(mainwin, y, 0, "%s %s attacking in a non-lethal manner.", you(lf), is(lf)); + y++; + } + f = lfhasflag(lf, F_STUNNED); if (f && (f->known)) { mvwprintw(mainwin, y, 0, "%s %s stunned and cannot attack, cast spells or use abilities.", you(lf), is(lf)); diff --git a/io.h b/io.h index 867d022..e6d94c1 100644 --- a/io.h +++ b/io.h @@ -72,7 +72,7 @@ void dorest(void); int doselguntarget(void); void dostairs(int dir); int dotakeoff(obpile_t *op); -void dothrow(obpile_t *op); +int dothrow(obpile_t *op, object_t *o); int dowear(obpile_t *op); int doweild(obpile_t *op); int downline(int *y, int h, char *heading, char *subheading, char *bottomstring, char *cmdchars, char *retchar); diff --git a/lf.c b/lf.c index 89737b5..6cdeb1c 100644 --- a/lf.c +++ b/lf.c @@ -765,6 +765,17 @@ int caneat(lifeform_t *lf, object_t *o) { return B_TRUE; } + +int canhaverandombehaviour(lifeform_t *lf) { + if (isundead(lf)) return B_FALSE; + if (isgod(lf)) return B_FALSE; + if (getraceclass(lf) == RC_HUMANOID) { + return B_TRUE; + } + return B_FALSE; +} + +// ie. will sound from 'dest' reach the ears of 'lf' int canhear(lifeform_t *lf, cell_t *dest, int volume) { int numpixels; int i; @@ -1049,17 +1060,9 @@ int canreachbp(lifeform_t *lf, lifeform_t *victim, enum BODYPART bp) { } lfsize = getlfsize(lf); - switch (isairborne(lf)) { - case F_FLYING: lfsize += 2; break; - case F_LEVITATING: lfsize++; break; - default: break; - } + lfsize += getflightsizemod(lf); victimsize = getlfsize(victim); - switch (isairborne(victim)) { - case F_FLYING: victimsize += 2; break; - case F_LEVITATING: victimsize++; break; - default: break; - } + victimsize += getflightsizemod(victim); howmuchsmaller = victimsize - lfsize; if (howmuchsmaller <= 0) return B_TRUE; @@ -5421,6 +5424,7 @@ int getavgdam(lifeform_t *lf, int forxp) { // modify for accuracy acc = getlfaccuracy(lf, o); + limitf(&acc, 0, 100); thisavg = pctof(acc, thisavg); avgdam += thisavg; @@ -5899,8 +5903,13 @@ int getguntargetid(lifeform_t *lf) { } int gethearingrange(lifeform_t *lf) { - int range = 8; // deafult - range = 2 + (getskill(lf, SK_LISTEN)*2); + int range = 2; // default + + if (!isasleep(lf)) { + // if awake, your listen skills helps + range += (getskill(lf, SK_LISTEN)*2); + } + return range; } @@ -6326,6 +6335,17 @@ enum LFCONDITION getlfcondition(lifeform_t *lf) { return C_DEAD; } +// how much taller are you due to your flight? +int getflightsizemod(lifeform_t *lf) { + int howmuch = 0; + switch (isairborne(lf)) { + case F_FLYING: howmuch += 2; break; + case F_LEVITATING: howmuch++; break; + default: break; + } + return howmuch; +} + enum SKILLLEVEL getmaxskilllevel(lifeform_t *lf, enum SKILL skid) { flag_t *f; enum SKILLLEVEL maxlev = PR_MASTER; @@ -7098,6 +7118,7 @@ char *real_getlfname(lifeform_t *lf, char *buf, int usevis, int showall) { job_t *j; flag_t *f; enum LFSIZE size,racesize; + int dobehaviour = B_TRUE; // 'the' or 'your' ? @@ -7133,11 +7154,27 @@ char *real_getlfname(lifeform_t *lf, char *buf, int usevis, int showall) { strcat(descstring, " "); } + + // need a certain amount of race knowledge to recognise ai traits + if (getlorelevel(player, lf->race->raceclass->id) < PR_SKILLED) { + dobehaviour = B_FALSE; + } + // frozen/headless trump behavioural descriptions like "insane" if (lfhasflag(lf, F_FROZEN)) { strcat(descstring, "frozen "); + dobehaviour = B_FALSE; } if (lfhasflag(lf, F_HEADLESS)) { strcat(descstring, "headless "); + dobehaviour = B_FALSE; + } + + if (dobehaviour) { + f = lfhasflag(lf, F_BEHAVIOUR); + if (f) { + strcat(descstring, f->text); + strcat(descstring, " "); + } } // construct job string @@ -8778,14 +8815,17 @@ flag_t *giveskill(lifeform_t *lf, enum SKILL id) { if (id == SK_ATHLETICS) { newf = hasflagval(lf->flags, F_CANWILL, OT_A_SPRINT, NA, NA, NULL); if (!newf) { - newf = addflag(lf->flags, F_CANWILL, OT_A_SPRINT, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_SPRINT, NA, NA, NULL, FROMSKILL); } } else if (id == SK_CLIMBING) { newf = hasflagval(lf->flags, F_CANWILL, OT_A_CLIMB, NA, NA, NULL); if (!newf || (newf->lifetime > 0)) { - newf = addflag(lf->flags, F_CANWILL, OT_A_CLIMB, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_CLIMB, NA, NA, NULL, FROMSKILL); + } + } else if (id == SK_CLUBS) { + newf = hasflagval(lf->flags, F_CANWILL, OT_A_STRIKETOKO, NA, NA, NULL); + if (!newf || (newf->lifetime > 0)) { + newf = addtempflag(lf->flags, F_CANWILL, OT_A_STRIKETOKO, NA, NA, NULL, FROMSKILL); } } else if (id == SK_COOKING) { if (isplayer(lf)) { @@ -8795,42 +8835,35 @@ flag_t *giveskill(lifeform_t *lf, enum SKILL id) { } newf = hasflagval(lf->flags, F_CANWILL, OT_A_COOK, NA, NA, NULL); if (!newf) { - newf = addflag(lf->flags, F_CANWILL, OT_A_COOK, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_COOK, NA, NA, NULL, FROMSKILL); } } else if (id == SK_LORE_ARCANA) { newf = hasflagval(lf->flags, F_CANWILL, OT_A_INSPECT, NA, NA, NULL); if (!newf) { - newf = addflag(lf->flags, F_CANWILL, OT_A_INSPECT, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_INSPECT, NA, NA, NULL, FROMSKILL); } } else if (id == SK_LOCKPICKING) { newf = hasflagval(lf->flags, F_CANWILL, OT_A_PICKLOCK, NA, NA, NULL); if (!newf) { - newf = addflag(lf->flags, F_CANWILL, OT_A_PICKLOCK, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_PICKLOCK, NA, NA, NULL, FROMSKILL); } } else if (id == SK_METALWORK) { newf = hasflagval(lf->flags, F_CANWILL, OT_A_REPAIR, NA, NA, NULL); if (!newf) { - newf = addflag(lf->flags, F_CANWILL, OT_A_REPAIR, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_REPAIR, NA, NA, NULL, FROMSKILL); } } else if (id == SK_SEWING) { newf = hasflagval(lf->flags, F_CANWILL, OT_A_REPAIR, NA, NA, NULL); if (!newf) { - newf = addflag(lf->flags, F_CANWILL, OT_A_REPAIR, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_REPAIR, NA, NA, NULL, FROMSKILL); } } else if (id == SK_THIEVERY) { newf = hasflagval(lf->flags, F_CANWILL, OT_A_STEAL, NA, NA, NULL); if (!newf) { - newf = addflag(lf->flags, F_CANWILL, OT_A_STEAL, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_STEAL, NA, NA, NULL, FROMSKILL); } } else if (id == SK_TRAPS) { - newf = addflag(lf->flags, F_CANWILL, OT_A_DISARM, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_DISARM, NA, NA, NULL, FROMSKILL); } // learning a new spell school skill will grant you a random first level spell from @@ -8867,20 +8900,17 @@ flag_t *giveskill(lifeform_t *lf, enum SKILL id) { if (id == SK_ATHLETICS) { if (f->val[1] == PR_ADEPT) { - newf = addflag(lf->flags, F_CANWILL, OT_A_TUMBLE, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_TUMBLE, NA, NA, NULL, FROMSKILL); } else if (f->val[1] == PR_EXPERT) { - newf = addflag(lf->flags, F_CANWILL, OT_A_JUMP, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_JUMP, NA, NA, NULL, FROMSKILL); } } else if (id == SK_CARTOGRAPHY) { if (f->val[1] == PR_SKILLED) { - addflag(lf->flags, F_PHOTOMEM, B_TRUE, NA, NA, NULL); + addtempflag(lf->flags, F_PHOTOMEM, B_TRUE, NA, NA, NULL, FROMSKILL); } if (f->val[1] == PR_MASTER) { if (!hasflagval(lf->flags, F_CANWILL, OT_S_MAPPING, NA, NA, NULL)) { - newf = addflag(lf->flags, F_CANWILL, OT_S_MAPPING, 50, 50, "pw:1;"); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_S_MAPPING, 50, 50, "pw:1;", FROMSKILL); } } } else if (id == SK_COOKING) { @@ -8893,16 +8923,14 @@ flag_t *giveskill(lifeform_t *lf, enum SKILL id) { } else if (id == SK_EVASION) { if (f->val[1] == PR_ADEPT) { if (isplayer(lf)) { - newf = addflag(lf->flags, F_CANWILL, OT_A_SNATCH, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_SNATCH, NA, NA, NULL, FROMSKILL); } } } else if (id == SK_LORE_ARCANA) { if (f->val[1] == PR_ADEPT) { newf = hasflagval(lf->flags, F_CANWILL, OT_A_STUDYSCROLL, NA, NA, NULL); if (!newf) { - newf = addflag(lf->flags, F_CANWILL, OT_A_STUDYSCROLL, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_STUDYSCROLL, NA, NA, NULL, FROMSKILL); } } } else if (id == SK_LORE_NATURE) { @@ -8916,14 +8944,12 @@ flag_t *giveskill(lifeform_t *lf, enum SKILL id) { if (f->val[1] == PR_SKILLED) { newf = hasflagval(lf->flags, F_CANWILL, OT_A_RESIZE, NA, NA, NULL); if (!newf) { - newf = addflag(lf->flags, F_CANWILL, OT_A_RESIZE, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_RESIZE, NA, NA, NULL, FROMSKILL); } } else if (f->val[1] == PR_MASTER) { newf = hasflagval(lf->flags, F_CANWILL, OT_A_ENHANCEOB, NA, NA, NULL); if (!newf) { - newf = addflag(lf->flags, F_CANWILL, OT_A_ENHANCEOB, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_ENHANCEOB, NA, NA, NULL, FROMSKILL); } } } else if (id == SK_PERCEPTION) { @@ -8936,25 +8962,21 @@ flag_t *giveskill(lifeform_t *lf, enum SKILL id) { if (f->val[1] == PR_SKILLED) { newf = hasflagval(lf->flags, F_CANWILL, OT_A_RESIZE, NA, NA, NULL); if (!newf) { - newf = addflag(lf->flags, F_CANWILL, OT_A_RESIZE, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_RESIZE, NA, NA, NULL, FROMSKILL); } } else if (f->val[1] == PR_MASTER) { newf = hasflagval(lf->flags, F_CANWILL, OT_A_ENHANCEOB, NA, NA, NULL); if (!newf) { - newf = addflag(lf->flags, F_CANWILL, OT_A_ENHANCEOB, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_ENHANCEOB, NA, NA, NULL, FROMSKILL); } } } else if (id == SK_SHIELDS) { if (f->val[1] == PR_BEGINNER) { - newf = addflag(lf->flags, F_CANWILL, OT_A_SHIELDBASH, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_SHIELDBASH, NA, NA, NULL, FROMSKILL); } } else if (id == SK_STEALTH) { if (f->val[1] == PR_BEGINNER) { - newf = addflag(lf->flags, F_CANWILL, OT_A_HIDE, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_HIDE, NA, NA, NULL, FROMSKILL); } } else if (id == SK_TECHUSAGE) { if (isplayer(lf)) { @@ -9008,21 +9030,19 @@ flag_t *giveskill(lifeform_t *lf, enum SKILL id) { */ } + if (isweaponskill(id)) { if (f->val[1] == PR_BEGINNER) { if (!hasflagval(lf->flags, F_CANWILL, OT_A_EXPOSEDSTRIKE, NA, NA, NULL)) { - newf = addflag(lf->flags, F_CANWILL, OT_A_EXPOSEDSTRIKE, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_EXPOSEDSTRIKE, NA, NA, NULL, FROMSKILL); } } else if (f->val[1] == PR_ADEPT) { if (!hasflagval(lf->flags, F_CANWILL, OT_A_ALTERATTACK, NA, NA, NULL)) { - newf = addflag(lf->flags, F_CANWILL, OT_A_ALTERATTACK, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_ALTERATTACK, NA, NA, NULL, FROMSKILL); } } else if (f->val[1] == PR_MASTER) { if (!hasflagval(lf->flags, F_CANWILL, OT_A_COMBOSTRIKE, NA, NA, NULL)) { - newf = addflag(lf->flags, F_CANWILL, OT_A_COMBOSTRIKE, NA, NA, NULL); - newf->lifetime = FROMSKILL; + newf = addtempflag(lf->flags, F_CANWILL, OT_A_COMBOSTRIKE, NA, NA, NULL, FROMSKILL); } } } @@ -9412,11 +9432,14 @@ int gotosleep(lifeform_t *lf, int onpurpose) { flag_t *hasbleedinginjury(lifeform_t *lf, enum BODYPART bp) { flag_t *f, *retflag[MAXCANDIDATES]; int nretflags,i; - getflags(player->flags, retflag, &nretflags, F_INJURY, F_NONE); + getflags(lf->flags, retflag, &nretflags, F_INJURY, F_NONE); for (i = 0;i < nretflags; i++) { f = retflag[i]; - // only temporary flags count - not permenant ones - if ((f->lifetime > 0) && (f->val[1] == bp) && (f->val[2] == DT_SLASH)) { + if (isplayer(lf) && (f->lifetime < 0)) { + // for the player, only temporary flags count - not permenant ones + // this is so that losing a whole finger etc doesn't completely + // cripple you by making you always bleed. + } else if ((f->val[1] == bp) && (f->val[2] == DT_SLASH)) { return f; } } @@ -10127,13 +10150,16 @@ flag_t *hasactivespell(lifeform_t *lf, enum OBTYPE sid) { } int haslof(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest) { - return haslof_real(src, dest, loftype, newdest, NULL); + return haslof_real(src, dest, loftype, newdest, NULL, B_TRUE); } // got line of fire to dest? if lof is blocked, return last cell in 'newdest' // if 'srclf' is set, we are checking whether 'srclf' THINKS they have line of fire. ie invisible/unseed // lifeforms don't block lof. -int haslof_real(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest, lifeform_t *srclf) { +// +// if 'walllfsok' is set, then we ARE allowed to have lineoffire to walls (ie. solid cells) if there is a lifeform +// there. +int haslof_real(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest, lifeform_t *srclf, int walllfsok) { int numpixels; int i; int x1,y1,x2,y2; @@ -10192,7 +10218,7 @@ int haslof_real(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdes if (cell->type->solid) { if (i == 0) { // ok - } else if (cell->lf && (i == (numpixels-1)) ) { + } else if (walllfsok && cell->lf && (i == (numpixels-1)) ) { // ok } else { reason = E_NOLOF; @@ -11497,6 +11523,7 @@ void makepeaceful(lifeform_t *who) { killflagsofid(who->flags, F_HOSTILE); killflagsofid(who->flags, F_HATESALL); killflagsofid(who->flags, F_HATESRACE); + killflagsofid(who->flags, F_TERRITORIAL); killflagsofid(who->flags, F_HATESRACEWITHFLAG); loseaitargets(who); } @@ -12446,7 +12473,6 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml if (fromob) { f = hasflag(fromob->flags, F_MERCIFUL); if (f && (amt >= lf->hp)) { - amt = lf->hp - 1; // ie end up at 1hp ko = B_TRUE; if (fromob->pile->owner && cansee(player, fromob->pile->owner)) { f->known = B_TRUE; @@ -12463,11 +12489,23 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml if ((lf->hp > 1) && (hpleftafterdam >= -5) && (hpleftafterdam <= 0)) { if (onein(2)) { ko = B_TRUE; - amt = lf->hp - 1; // ie end up at 1hp } } } } + + if (!ko) { + if (fromlf && lfhasflag(fromlf, F_STRIKETOKO)) { + if (cansee(fromlf, lf)) { + ko = B_TRUE; + } + } + } + + // just knock them out. + if (ko) { + amt = lf->hp - 1; // ie end up at 1hp + } } // large damage will be stopped by a ring of miracles @@ -12648,24 +12686,24 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml } // low hitpoint warning - if (amt > 0) { - if (isplayer(lf)) { - int warnthresh; - warnthresh = (int)((float)0.25 * (float)lf->maxhp); - if ((lf->hp <= warnthresh) && (lf->hp > 0)) { - warn("*** LOW HITPOINT WARNING ***"); - more(); - } + if (isplayer(lf) && (amt > 0)) { + int warnthresh; + warnthresh = (int)((float)0.25 * (float)lf->maxhp); + if ((lf->hp <= warnthresh) && (lf->hp > 0)) { + warn("*** LOW HITPOINT WARNING ***"); + more(); } } if (ko) { - // you are knocked unconscious for a _long_ time - fallasleep(lf, ST_KO, rnd(50,100)); - if (fromlf && isplayer(fromlf)) { - pleasegodmaybe(R_GODMERCY, 5); + if (!isunconscious(lf)) { + // you are knocked unconscious for a _long_ time + fallasleep(lf, ST_KO, rnd(50,100)); + if (fromlf && isplayer(fromlf)) { + pleasegodmaybe(R_GODMERCY, 5); + } + breakgrabs(lf, B_TRUE, B_FALSE); } - breakgrabs(lf, B_TRUE, B_FALSE); } else { // you wake up if you were hit, unless you were unconscious! f = lfhasflag(lf, F_ASLEEP); @@ -13284,8 +13322,6 @@ int noise(cell_t *c, lifeform_t *noisemaker, enum NOISECLASS nclass, int volume, dist = getcelldist(l->cell, c); - - // listen check difficulty is based on sound distance vs max hearing distance if ((nclass == NC_SPEECH) && isplayer(l)) { // you always hear it, as long as you're in range @@ -13296,7 +13332,7 @@ int noise(cell_t *c, lifeform_t *noisemaker, enum NOISECLASS nclass, int volume, difficulty = 9999; } else { //difficulty = (int) ( ((float)getcelldist(l->cell, c) / (float)gethearingrange(l)) * 20); - difficulty = (int) ( ((float)getcelldist(l->cell, c) / ((float)gethearingrange(l) + volume)) * 14); + difficulty = (int) ( ((float) dist / ((float)gethearingrange(l) + volume)) * 14); } // listen bonus is based on sound volume @@ -15400,11 +15436,14 @@ int shoot(lifeform_t *lf) { fireat(lf, ammo, 1, targ->cell, firespeed, gun); if (!getammo(gun)) { - if (loadfirearmfast(lf, B_FALSE)) { - if (isplayer(lf)) { - char obname[BUFLEN]; - getobname(gun, obname, 1); - msg("^bYour %s is now out of ammo!", noprefix(obname)); + if (!isplayer(lf) || getoption(OPT_AUTORELOAD)) { + // automatically try to reload our empty firearm/ranged weapon. + if (loadfirearmfast(lf, B_FALSE)) { + if (isplayer(lf)) { + char obname[BUFLEN]; + getobname(gun, obname, 1); + msg("^bYour %s is now out of ammo!", noprefix(obname)); + } } } } @@ -16650,7 +16689,7 @@ void startlfturn(lifeform_t *lf) { // effects for/on your own flags getflags(lf->flags, retflag, &nretflags, F_ATTACHEDTO, F_CANWILL, F_CHARMEDBY, F_CLIMBING, F_FEIGNFOOLEDBY,F_FLEEFROM, F_GRABBEDBY, F_GRABBING, F_GUNTARGET, F_BOOSTSPELL, F_FEIGNINGDEATH, F_HPDRAIN, F_INJURY, - F_NOFLEEFROM, F_PETOF, F_SPOTTED, F_STABBEDBY, F_TARGETCELL, F_TARGETLF, F_NONE); + F_NOFLEEFROM, F_PETOF, F_SPOTTED, F_STABBEDBY, F_STRIKETOKO, F_TARGETCELL, F_TARGETLF, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; // remove impossible flags @@ -16752,6 +16791,15 @@ void startlfturn(lifeform_t *lf) { } } + if (f->id == F_STRIKETOKO) { + skill_t *sk; + sk = getobskill(getweapon(lf)); + if (!sk || (sk->id != SK_CLUBS)) { + killflag(f); + continue; + } + } + // recharge abilities if (f->id == F_CANWILL) { if (f->val[2] != NA) { @@ -16760,6 +16808,7 @@ void startlfturn(lifeform_t *lf) { } } } + // remove invalid targets if ((f->id == F_TARGETLF) || (f->id == F_TARGETCELL)) { lifeform_t *targ = NULL; diff --git a/lf.h b/lf.h index cb7f5ea..1d50e6d 100644 --- a/lf.h +++ b/lf.h @@ -40,6 +40,7 @@ int cancook(lifeform_t *lf, recipe_t *rec, enum ERROR *reason); int canclimb(lifeform_t *lf, enum ERROR *reason); int candrink(lifeform_t *lf, object_t *o); int caneat(lifeform_t *lf, object_t *o); +int canhaverandombehaviour(lifeform_t *lf); int canhear(lifeform_t *lf, cell_t *c, int volume); int canlearn(lifeform_t *lf, enum SKILL skid); int canmakerecipe(lifeform_t *lf, recipe_t *rec); @@ -165,6 +166,7 @@ int getleftrightwalls(lifeform_t *lf); int getlfaccuracy(lifeform_t *lf, object_t *wep); char getlfcol(lifeform_t *lf, enum MSGCHARCOL cc); enum LFCONDITION getlfcondition(lifeform_t *lf); +int getflightsizemod(lifeform_t *lf); enum SKILLLEVEL getmaxskilllevel(lifeform_t *lf, enum SKILL skid); int getminions(lifeform_t *lf, lifeform_t **minion, int *nminions); int getmiscastchance(lifeform_t *lf); @@ -273,7 +275,7 @@ void loseobflags(lifeform_t *lf, object_t *o, int kind); int hasbp(lifeform_t *lf, enum BODYPART bp); flag_t *hasactivespell(lifeform_t *lf, enum OBTYPE sid); int haslof(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest); -int haslof_real(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest, lifeform_t *srclf); +int haslof_real(cell_t *src, cell_t *dest, enum LOFTYPE loftype, cell_t **newdest, lifeform_t *srclf, int walllfsok); int haslos(lifeform_t *viewer, cell_t *dest); int haslosdark(lifeform_t *viewer, cell_t *dest); int haslos_fast(lifeform_t *viewer, cell_t *dest); diff --git a/map.c b/map.c index 77b5dcb..773bf99 100644 --- a/map.c +++ b/map.c @@ -1154,6 +1154,41 @@ void clearcell_exceptflags(cell_t *c, ... ) { } } +int damagecell(cell_t *c, int amt, enum DAMTYPE damtype) { + if (!c->type->solid) return B_TRUE; + // adjust dam + adjustdammaterial(&amt, damtype, c->type->material->id); + if (amt <= 0) return B_TRUE; + + // wall loses hp + c->hp -= amt; + if (c->hp <= 0) { + char cellname[BUFLEN]; + int shattered = B_FALSE; + enum MATERIAL cellmat; + c->hp = 0; + // remember cell properties + sprintf(cellname, "%s %s", needan(c->type->name) ? "An" : "A", c->type->name); + cellmat = c->type->material->id; + // cell dies (have to do this before calling fragments()) + setcelltype(c, c->map->habitat->emptycelltype); + // announce + if (haslos(player, c)) { + msg("%s %s!", cellname, willshatter(cellmat) ? "shatters" : "is destroyed"); + } + // shatter? + if (willshatter(cellmat)) { + char what[BUFLEN]; + shattered = B_TRUE; + noise(c, NULL, NC_OTHER, SV_CAR, "something shattering.", NULL); + if (getshardobname(cellmat, what)) { + fragments(c, what, 3, 3); + } + } + } + return B_FALSE; +} + // returns true if something happened int doelementspread(cell_t *c) { float thisdepth; @@ -4707,11 +4742,12 @@ void explodecells(cell_t *c, int dam, int killwalls, object_t *o, int range, int inrange = B_TRUE; } if (inrange) { - cell_t *stopcell = NULL; + //cell_t *stopcell = NULL; // if a door stops the explosion, the door will // take the damage. - haslof(c, cc, LOF_WALLSTOP, &stopcell); - explodesinglecell(stopcell, dam, killwalls, o, c); + //haslof(c, cc, LOF_WALLSTOP, &stopcell); + // don't check LOF for explosions. + explodesinglecell(cc, dam, killwalls, o, c); } } } @@ -4789,10 +4825,7 @@ void explodesinglecell(cell_t *c, int dam, int killwalls, object_t *o, cell_t *c if (killwalls) { if (c->type->solid) { - // make it empty! - setcelltype(c, c->habitat->emptycelltype); - // add some rubble - addob(c->obpile, "10-50 stones"); + damagecell(c, dam, DT_EXPLOSIVE); } } } @@ -5010,11 +5043,11 @@ void finalisemonster(lifeform_t *lf, lifeform_t *leader, flagpile_t *wantflags) } // random monster behaviours - if (!isundead(lf) && !isgod(lf) && onein(4)) { + if (canhaverandombehaviour(lf) && onein(6)) { switch (rnd(0,6)) { case 0: // insane addflag(lf->flags, F_BEHAVIOUR, NA, NA, NA, "insane"); - addflag(lf->flags, F_HATESALL, B_TRUE, NA, NA, NULL); + addflag(lf->flags, F_TERRITORIAL, 2, NA, NA, NULL); // attack anything within 1 cells break; case 1: // hungry addflag(lf->flags, F_BEHAVIOUR, NA, NA, NA, "hungry"); @@ -5974,7 +6007,7 @@ void initmap(void) { 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); - addcelltype(CT_WALLGLASS, "glass wall", UNI_SOLID, C_CYAN, B_SOLID, B_TRANS, MT_GLASS, 0, 150); + addcelltype(CT_WALLGLASS, "glass wall", UNI_SOLID, C_CYAN, B_SOLID, B_TRANS, MT_GLASS, 0, 25); addcelltype(CT_WALLMETAL, "metal wall", UNI_SOLID, C_WHITE, B_SOLID, B_OPAQUE, MT_METAL, 0, 200); // cell types - non-solid addcelltype(CT_FAKE, "fake cell", '.', C_GREEN, B_EMPTY, B_TRANS, MT_STONE, 0, -1); diff --git a/map.h b/map.h index 3aca3d1..5e1a1e7 100644 --- a/map.h +++ b/map.h @@ -18,6 +18,7 @@ int cellisfixedvaultwall(cell_t *c); int cellokforreachability(cell_t *startcell, cell_t *c, int srcroomid, int dir, int wantfilled, int *insameroom); void clearcell(cell_t *c); void clearcell_exceptflags(cell_t *c, ...); +int damagecell(cell_t *c, int amt, enum DAMTYPE damtype); int doelementspread(cell_t *c); int fix_reachability(map_t *m); int fix_unreachable_cell(cell_t *badcell); diff --git a/objects.c b/objects.c index 9f3ab89..5f3f4b7 100644 --- a/objects.c +++ b/objects.c @@ -3062,7 +3062,7 @@ void explodeob(object_t *o, flag_t *f, int bigness) { } else if (haslos(player, c)) { msg("%s explode%s!", obname,OBS1(o)); } - explodecells(c, dam * o->amt, bigness ? B_TRUE : B_FALSE, o, bigness ? 1 : 0, DT_COMPASS, B_FALSE); + explodecells(c, dam * o->amt, bigness ? B_TRUE : B_FALSE, o, bigness , DT_ORTH, B_FALSE); // object dies. removeob(o, o->amt); @@ -7901,10 +7901,10 @@ void obdie(object_t *o) { if (f) { if (f->val[2] == B_IFACTIVATED) { if (isactivated(o)) { - explodeob(o, f, (f->val[1] == B_BIG) ? 1 : 0); + explodeob(o, f, f->val[1]); } } else { - explodeob(o, f, (f->val[1] == B_BIG) ? 1 : 0); + explodeob(o, f, f->val[1]); } return; } @@ -10828,6 +10828,8 @@ int sethiddenname(objecttype_t *ot, char *text) { addflag(ot->flags, F_PRODUCESLIGHT, 1, NA, NA, NULL); } else if (strstr(text, "luminous")) { addflag(ot->flags, F_PRODUCESLIGHT, 1, NA, NA, NULL); + } else if (strstr(text, "sparkling")) { + addflag(ot->flags, F_PRODUCESLIGHT, 1, NA, NA, NULL); } // set colour based on hiddenname @@ -11360,7 +11362,7 @@ int real_takedamage(object_t *o, int howmuch, int damtype, int wantannounce) { } else { // explode if ((f->val[0] == NA) || (f->val[0] == damtype)) { - explodeob(o, f, (f->val[1] == B_BIG) ? 1 : 0); + explodeob(o, f, f->val[1]); return howmuch; } } @@ -11372,7 +11374,7 @@ int real_takedamage(object_t *o, int howmuch, int damtype, int wantannounce) { } else { if ((f->val[0] == NA) || (f->val[0] == damtype)) { // explode - explodeob(o, f, (f->val[1] == B_BIG) ? 1 : 0); + explodeob(o, f, f->val[1]); return howmuch; } } @@ -11888,7 +11890,17 @@ int real_fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int sp } } if (db && thrower) { - dblog("%s is throwing %s - acc = %d, speed = %d\n", realthrowername, obname, acc, speed); + char fatext[BUFLEN]; + + if (firearm) { + char faname[BUFLEN]; + getobname(firearm, faname, 1); + sprintf(fatext," (from %s)",faname); + } else { + strcpy(fatext,""); + } + dblog("%s is %s %s%s - acc = %d, speed = %d\n", realthrowername, + firearm ? "firing" : "throwing", obname, fatext, acc, speed); } // roll for hit @@ -12178,6 +12190,16 @@ int real_fireat(lifeform_t *thrower, object_t *o, int amt, cell_t *where, int sp } } + // if we were throwing at a lifeform inside a wall, we need to make sure we don't + // now place the object inside the wall. + if (where->type->solid) { + newloc = NULL; + haslof_real(srcloc, where, LOF_NEED, &newloc, NULL, B_FALSE); + if (newloc) { + where = newloc; + } + } + // move the object to the cell then take dam or kill it. don't stack. newob = real_moveob(o, where->obpile, amt, B_FALSE); // fake its birth time so that it can be damaged @@ -13062,12 +13084,18 @@ void turnon(lifeform_t *lf, object_t *o) { addflag(newob->flags, F_ACTIVATED, B_TRUE, NA, NA, NULL); getobname(newob, newobname, 1); if (lf && isplayer(lf)) { + // announce. if (held) { msgnocap("%c - %s [activated].",newob->letter, newobname); + more(); + // ask where to throw it + dothrow(NULL, newob); } else { msgnocap("You activate %s.",newob->letter, newobname); } } + + o = newob; } else { if (isplayer(lf)) { msg("You don't have room for an activated %s!",noprefix(obname)); @@ -13671,6 +13699,7 @@ int getmissileaccuracy(lifeform_t *thrower, cell_t *where, object_t *missile, ob acc -= (cellpenalty*howfar); + // modify based on attributes if (whichatt != A_NONE) { int mod; mod = getstatmod(thrower, whichatt); // -50 to 50 @@ -13693,6 +13722,14 @@ int getmissileaccuracy(lifeform_t *thrower, cell_t *where, object_t *missile, ob if (where->lf && isprone(where->lf)) { acc -= 30; } + + // 10% bonus for being higher than your target + if (where->lf) { + if (getflightsizemod(thrower) > getflightsizemod(where->lf)) { + acc += 15; + } + } + limit(&acc, 0, NA); return acc; } diff --git a/spell.c b/spell.c index 453b889..97f7f6c 100644 --- a/spell.c +++ b/spell.c @@ -1893,6 +1893,20 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef // now kill the scroll msg("The scroll crumbles to dust."); removeob(o, 1); + } else if (abilid == OT_A_STRIKETOKO) { + skill_t *sk; + sk = getobskill(getweapon(user)); + if (!sk || (sk->id != SK_CLUBS)) { + if (isplayer(user)) msg("You can only use bashing weapons to fight mercifully."); + return B_TRUE; + } + + if (lfhasflag(user, F_STRIKETOKO)) { + killflagsofid(user->flags, F_STRIKETOKO); + } else { + addflag(user->flags, F_STRIKETOKO, B_TRUE, NA, NA, NULL); + } + // note: takes no time. } else if (abilid == OT_A_SUCKBLOOD) { int dam = 0; diff --git a/text.c b/text.c index 8b1ffd0..885405b 100644 --- a/text.c +++ b/text.c @@ -131,6 +131,7 @@ char *construct_hit_string(lifeform_t *lf, lifeform_t *victim, char *attackernam needfree = B_TRUE; } else if (fatal) { verb = getkillverb(victim, wep, damtype, dam, maxhp); + if (strstr(verb, "knock out") && !isplayer(victim)) knownnodam = B_TRUE; } else { if (!victim || // atacking an object (getlorelevel(lf, victim->race->raceclass->id) >= PR_BEGINNER) || // know about the race @@ -682,6 +683,21 @@ void getdisttext(cell_t *src, cell_t *dst,char *distbuf, char *distbufapprox, ch } +char *getflagsourcetext(flag_t *f) { + switch (f->lifetime) { + case FROMSKILL: return " (skill perk)"; + case FROMJOB: return " (job perk)"; + case FROMOBEQUIP: + case FROMOBHOLD: + case FROMOBACTIVATE: + return " (conferred perk)"; + case FROMGODGIFT: + return " (god gift)"; + default: break; + } + return ""; +} + int gethitconferlifetime(char *text, int *min, int *max) { int howlong; int localmin = -1,localmax = -1; @@ -721,7 +737,9 @@ char *getkillverb(lifeform_t *victim, object_t *wep, enum DAMTYPE damtype, int d if (wep && hasflag(wep->flags, F_MERCIFUL)) { return "knock out"; } - + if (wep && wep->pile->owner && lfhasflag(wep->pile->owner, F_STRIKETOKO)) { + return "knock out"; + } if (victim->race->id == R_DANCINGWEAPON) { return "defeat"; } @@ -942,6 +960,98 @@ char *getobmodprefix(object_t *o, obmod_t *om) { default: return "rotted "; } } + } else if (isweapon(o)) { + flag_t *f = NULL; + skill_t *sk; + sk = getobskill(o); + if (sk) { + if (om->id == OM_MASTERWORK) { + switch (sk->id) { + case SK_CLUBS: + case SK_STAVES: + return "reinforced "; + default: break; + } + } else if (om->id == OM_SHODDY) { + switch (sk->id) { + case SK_LONGBLADES: + case SK_SHORTBLADES: + case SK_AXES: + return "blunted "; + case SK_CLUBS: + return "cracked "; + case SK_POLEARMS: + return "notched "; + case SK_STAVES: + return "blunted "; + default: break; + } + } + } + if (f) return f->text; + } else if (isshield(o)) { + if (om->id == OM_MASTERWORK) { + switch (o->material->id) { + case MT_LEATHER: + return "studded "; + case MT_METAL: + case MT_WOOD: + case MT_DRAGONWOOD: + return "reinforced "; + default: break; + } + } else if (om->id == OM_SHODDY) { + switch (o->material->id) { + case MT_LEATHER: + case MT_RUBBER: + case MT_PAPER: + return "torn "; + case MT_CLOTH: + case MT_SILK: + return "frayed "; + case MT_GLASS: + case MT_STONE: + case MT_BONE: + return "chipped "; + case MT_METAL: + return "dented "; + case MT_WOOD: + case MT_DRAGONWOOD: + return "splintered "; + default: break; + } + } + } else if (isarmour(o)) { + if (om->id == OM_MASTERWORK) { + switch (o->material->id) { + case MT_LEATHER: + return "studded "; + case MT_CLOTH: + case MT_SILK: + return "tailored "; + default: break; + } + } else if (om->id == OM_SHODDY) { + switch (o->material->id) { + case MT_LEATHER: + case MT_RUBBER: + case MT_PAPER: + return "torn "; + case MT_CLOTH: + case MT_SILK: + return "frayed "; + case MT_GLASS: + case MT_STONE: + case MT_BONE: + return "chipped "; + case MT_METAL: + return "dented "; + case MT_WOOD: + case MT_DRAGONWOOD: + return "splintered "; + default: break; + } + } } return om->prefix; } diff --git a/text.h b/text.h index ff81992..5f1b43c 100644 --- a/text.h +++ b/text.h @@ -16,6 +16,7 @@ char *getattrname(enum ATTRIB att); char *getdirname(int dir); char *getdirnameshort(int dir); void getdisttext(cell_t *src, cell_t *dst,char *distbuf, char *distbufapprox, char *dirbuf); +char *getflagsourcetext(flag_t *f); int gethitconferlifetime(char *text, int *min, int *max); char *getkillverb(lifeform_t *victim, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp); char *getpoisondamverb(enum POISONTYPE ptype); diff --git a/vaults/pub.vlt b/vaults/pub.vlt index c68d58c..b4e6890 100644 --- a/vaults/pub.vlt +++ b/vaults/pub.vlt @@ -14,7 +14,7 @@ p:cell:shop floor +:cell:dirt +:ob:wooden door s:ob:sign "Village Pub" -f:ob:fireplace +f:ob:brazier @end @flags