#include #include #include #include #include #include "ai.h" #include "attack.h" #include "defs.h" #include "flag.h" #include "god.h" #include "io.h" #include "lf.h" #include "map.h" #include "move.h" #include "nexus.h" #include "objects.h" #include "text.h" extern lifeform_t *player; extern lifeform_t *godlf[]; extern prompt_t prompt; extern int needredraw; extern int statdirty; extern enum ERROR reason; int applyarmourdamage(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damtype, lifeform_t *attacker) { object_t *armour = NULL; int damtaken = 0; // first of all, only apply some of the damage dam /= 2; if (dam == 0) { return 0; } // special case - missiles always hit flak jacket if (damtype == DT_PROJECTILE) { object_t *o; o = getequippedob(lf->pack, BP_BODY); if (o && (o->type->id == OT_FLAKJACKET)) { armour = o; } } // if attacker is directly behind us, tortoiseshell is what was hit. /* if (attacker) { if (getcellindir(lf->cell, diropposite(lf->facing)) == attacker->cell) { object_t *o; o = hasequippedob(lf->pack, OT_TORTOISESHELL); if (o) { armour = o; } } } */ // figure out what bit of armour was hit if (!armour) { // pick a random piece of armour armour = getrandomarmour(lf, attacker); } if (armour) { int actualdam; int ar = 0; flag_t *rust, *f; f = hasflag(armour->flags, F_ARMOURRATING); if (f) { ar = f->val[0]; } rust = hasflag(armour->flags, F_RUSTED); actualdam = dam; /* // adjust how much damage to do to armour if ( ((armour->type->id == OT_FLAKJACKET) && (damtype == DT_PROJECTILE)) || (damtype == DT_ACID) || rust ) { // ALL of damage reduction goes towards armour } else { // SOME of the damage reduction goes towards the armour // damage taken by armour is reduced by _UP TO_ half its armour rating if (ar) { int maxreduction; maxreduction = ar/2; if (maxreduction >= 1) { actualdam -= rnd(0,maxreduction); limit(&actualdam, 0, NA); } } } */ // modify for rust if (rust) { int multiplier = 1; switch (rust->val[0]) { case R_RUSTY: multiplier = 2; case R_VRUSTY: multiplier = 3; case R_TRUSTY: multiplier = 4; } actualdam *= multiplier; } if (actualdam > 0) { // actually apply the damage to the armour damtaken = takedamage(armour,actualdam, damtype); } } return damtaken; } void applyarmourdamreduction(lifeform_t *lf, object_t *wep, int reduceamt, int *dam, enum DAMTYPE damtype) { int db = B_FALSE; int newdam = 0; // figure out reduced damage value if (dam) { int ar; newdam = *dam; ar = getarmourrating(lf, NULL, NULL, NULL, NULL); // if you did at least one damage... if ((*dam >= 1) && (reduceamt >= 0)) { int lowerlimit = 0,divideby; // reduce it. newdam -= reduceamt; // you will always take at least 1 hp of damage for every (ar/2) damage. // ie. if you took ar/2 damage, you take at least 1. // ie. if you took ar damage, you take at least 2. // ie. if you took ar*2 damage, you take at least 3. // stop at 3. divideby = ar/2; if (divideby <= 0) divideby = 1; lowerlimit = (*dam / divideby); limit(&lowerlimit, 1, NA); limit(&newdam, lowerlimit, NA); // don't reduce too far. } if (db) { if ((*dam >= 1) && (reduceamt > 0)) { dblog("Armour reduces dam=%d by %d to %d.",*dam,reduceamt,newdam); } else { dblog("No armour dam reduction."); } } *dam = newdam; } } int attackcell(lifeform_t *lf, cell_t *c, int force) { int validwep[MAXCANDIDATES]; object_t *wep[MAXCANDIDATES]; flag_t *damflag[MAXCANDIDATES]; obpile_t *op = NULL; enum { AT_NONE = 0, AT_LF = 1, AT_OB = 2, AT_WALL = 3, } attacktype = AT_NONE; void *attacktarget; int attacklfid = -1; int nweps = 0; int innateattacks = 0; int i; int attacktime; int gotweapon = B_FALSE; int maxattacks = ALL; int attacksdone = 0; int lastweaponidx = -1; int saysorry = B_FALSE; int attackedhelpless = B_FALSE; int attackedfriend = B_FALSE; int attackedpeaceful = B_FALSE; // warn if attacking will cause injury if (!force && isplayer(lf) && haslos(lf, c)) { if (!confirm_injury_action(BP_HANDS, DT_SLASH, "attack")) { return B_TRUE; } } stoprunning(lf); // anyone there? if so just attack. if (c->lf) { // warnings if (!force && isplayer(lf)) { if (isprone(lf)) { if (!warnabout("Really attack while prone (-4 accuracy)?")) { return B_TRUE; } } if (!areenemies(lf,c->lf) && (getraceclass(c->lf) != RC_PLANT) && cansee(lf, c->lf) && !lfhasflag(lf, F_RAGE) ) { char ch; char victimname[BUFLEN]; char buf[BUFLEN]; getlfname(c->lf, victimname); strcpy(buf, ""); switch (getallegiance(c->lf)) { case AL_PEACEFUL: if (getlorelevel(lf, getraceclass(c->lf) >= PR_NOVICE) || getskill(lf, SK_SPEECH)) { // need this to KNOW whether they're peaceful snprintf(buf, BUFLEN, "Really attack the peaceful %s?",noprefix(victimname)); } break; case AL_FRIENDLY: snprintf(buf, BUFLEN, "Really attack the allied %s?",noprefix(victimname)); break; default: snprintf(buf, BUFLEN, "Really attack the allied %s?",noprefix(victimname)); break; } if (strlen(buf)) { ch = askchar(buf, "yn","n", B_TRUE, B_FALSE); if (ch == 'n') { // cancel. return B_TRUE; } } attackedpeaceful = B_TRUE; // non-evil players get no xp for attacking peaceful lfs if ((isplayer(lf) || areallies(player, lf)) && (getalignment(lf) != AL_EVIL)) { killflagsofid(c->lf->flags, F_XPVAL); addflag(c->lf->flags, F_XPVAL, 0, NA, NA, NULL); real_warnabout(TEXT_WARN_NOXP_GOODVSPEACEFUL, PERMENANT, B_FALSE); } } // average wisdom will prvent you from annoying your god if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_AVERAGE) { enum HELPLESSTYPE how; if (ishelplessvictim(c->lf, lf, &how)) { int dowarning = B_FALSE; if (godprayedto(R_GODPURITY) && (getalignment(c->lf) != AL_EVIL)) { dowarning = B_TRUE; } else if (godprayedto(R_GODMERCY)) { dowarning = B_TRUE; } if (dowarning) { char victimname[BUFLEN],buf[BUFLEN]; getlfname(c->lf, victimname); snprintf(buf, BUFLEN, "Really attack the %s %s?", (how == HL_FLEEING) ? "fleeing" : "helpless", noprefix(victimname)); if (!warnabout(buf)) { return B_TRUE; } } } if ((getraceclass(c->lf) == RC_PLANT) && godprayedto(R_GODNATURE)) { char victimname[BUFLEN],buf[BUFLEN]; getlfname(c->lf, victimname); snprintf(buf, BUFLEN, "Really attack %s?",victimname); if (!warnabout(buf)) { return B_TRUE; } } } // above average wisdom will prevent you from starting fires, or rusting your weapon if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_GTAVERAGE) { if (hasflag(c->type->material->flags, F_FLAMMABLE)) { if (getlorelevel(player, c->lf->race->raceclass->id) >= PR_BEGINNER) { if (c->lf->race->id == R_FIREBUG) { char victimname[BUFLEN],buf[BUFLEN]; getlfname(c->lf, victimname); snprintf(buf, BUFLEN, "Attacking %s might start a fire - proceed anyway?",victimname); if (!warnabout(buf)) { return B_TRUE; } } } } if (c->lf->material->id == MT_WATER) { object_t *priwep; priwep = getweapon(lf); if (priwep && willrust(priwep)) { char victimname[BUFLEN],wepname[BUFLEN],buf[BUFLEN]; getlfname(c->lf, victimname); real_getobname(priwep, wepname, priwep->amt, B_NOPREMODS, B_NOCONDITION, B_BLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL); snprintf(buf, BUFLEN, "Attacking %s might rust your %s - proceed anyway?",victimname, wepname); if (!warnabout(buf)) { return B_TRUE; } } } } if (lfhasflag(lf, F_HASNEWLEVEL)) { if (!warnabout(TEXT_WARN_ATTACK_NOXP)) { return B_TRUE; } } // player walked into someone who was feigning death? if (lfhasflag(c->lf, F_FEIGNINGDEATH)) { char vicname[BUFLEN]; killflagsofid(c->lf->flags, F_FEIGNINGDEATH); getlfname(c->lf, vicname); capitalise(vicname); if (cansee(lf, c->lf)) { msg("Hey! %s was just feigning death!", vicname); } else { msg("You bump into someone!"); } killflagsofid(c->lf->flags, F_PRONE); // still counts as a move! addflagifneeded(lf->flags, F_TOOKACTION, B_TRUE, NA, NA, NULL); taketime(lf, getmovespeed(lf)); return B_FALSE; } } attacktype = AT_LF; attacktarget = c->lf; attacklfid = c->lf->id; // remember for later if (areallies(lf, attacktarget)) attackedfriend = B_TRUE; attackedhelpless = ishelplessvictim(attacktarget, lf, NULL); } else { object_t *o; // has an impassable object? o = hasobwithflag(c->obpile, F_IMPASSABLE); if (o) { object_t *priwep; attacktype = AT_OB; attacktarget = o; priwep = getweapon(lf); // confirm ? if (!force && isplayer(lf) && wepdullable(priwep) && (getattrbracket(getattr(player, A_IQ), A_IQ, NULL) >= AT_GTAVERAGE) && !lfhasflag(lf, F_RAGE)) { if (!hasflagknown(priwep->flags, F_IMMUTABLE)) { char obname[BUFLEN],wepname[BUFLEN],buf[BUFLEN]; char ch; real_getobname(o, obname, o->amt, B_NOPREMODS, B_NOCONDITION, B_BLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL); getobname(priwep, wepname, priwep->amt); snprintf(buf, BUFLEN, "Attacking %s might damage your %s. Proceed?", obname, noprefix(wepname)); ch = askchar(buf, "yn","n", B_TRUE, B_FALSE); if (ch == 'n') { // cancel. return B_TRUE; } } }; } else { object_t *poss[MAXPILEOBS]; int nposs = 0; for (o = c->obpile->first ; o ; o = o->next) { if (hasflag(o->flags, F_ATTACKABLE)) { poss[nposs++] = o; } } if (nposs == 1) { attacktype = AT_OB; attacktarget = poss[0]; } else if (nposs) { o = NULL; if (isplayer(lf) && !lfhasflag(lf, F_HURRICANESTRIKE)) { // ask which one to attack char ch = 'a'; initprompt(&prompt, "What will you attack?"); for (i = 0; i < nposs; i++) { char obname[BUFLEN]; getobname(poss[i], obname, poss[i]->amt); addchoice(&prompt, ch, obname, obname, o, NULL); if (ch == 'z') ch = 'A'; else ch++; } addchoice(&prompt, '-', "(nothing)", "(nothing)", NULL, NULL); prompt.maycancel = B_TRUE; ch = getchoice(&prompt); if (ch != '\0') o = (object_t *)prompt.result; } else { // pick one randomly o = poss[rnd(0,nposs-1)]; } if (o) { attacktype = AT_OB; attacktarget = o; } else { if (isplayer(lf)) msg("Cancelled."); return B_TRUE; } } else { if (!lfhasflag(lf, F_HURRICANESTRIKE)) { if (c->type->solid) { attacktype = AT_WALL; attacktarget = c; } else { if (isplayer(lf)) { msg("There is nothing there to attack!"); } return B_TRUE; } } // end if !hurricanestrike } } } // can you actually attack? if (!canattack(lf)) { if (isplayer(lf)) { switch (reason) { case E_NOSTAM: msg("You are too tired to fight at the moment."); break; case E_STUNNED: msg("You are too stunned to fight at the moment."); break; case E_IMPOSSIBLE: msg("You have no way of attacking!"); break; default: msg("For some reason, you cannot attack."); break; } } return B_TRUE; } // ai code... if (lfhasflag(lf, F_DEMANDSBRIBE)) { if (!isplayer(lf) && (attacktype == AT_LF) && isplayer((lifeform_t *)attacktarget)) { if (demandbribe(lf)) { // ie. player paid. taketime(lf, getactspeed(lf)); return B_FALSE; } } } // monsters won't attack with non-melee weapons like bows gotweapon = getweapons(lf, isplayer(lf) ? B_FALSE : B_MELEEONLY, wep, damflag, &lastweaponidx, &op, &nweps); for (i = 0; i < nweps; i++) { validwep[i] = B_TRUE; } innateattacks = countinnateattacks(lf); attacktime = getattackspeed(lf); if (nweps <= 0) { if (isplayer(lf)) { msg("You cannot attack!"); } if (op) killobpile(op); return B_TRUE; } //maxattacks = nweps; // ie. all maxattacks = getattacks(lf, NULL, NULL); /* // if we have a weapon, this takes the place of one of our // attacks. // - for monsters, pick which one to replace randomly. // - for players, never pick the weapon to replace randomly. */ // # valid attacks higher than our allowed attacks? if (nweps > maxattacks) { int nvalid; int first; // player never invalidates their equipped weapons //if (isplayer(lf) && gotweapon) { if (gotweapon) { first = lastweaponidx+1; } else { first = 0; } nvalid = 0; for (i = 0; i < nweps; i++) { if (validwep[i]) nvalid++; } while (nvalid > maxattacks) { int sel; // mark a random one as invalid sel = rnd(first,nweps-1); while (!validwep[sel]) { sel = rnd(first,nweps-1); } validwep[sel] = B_FALSE; // re-count... nvalid = 0; for (i = 0; i < nweps; i++) { if (validwep[i]) nvalid++; } } } // lore about this race will tell you if you will do no damage. if ((attacktype == AT_LF) && isplayer(lf) && wep[0]) { lifeform_t *victim; victim = (lifeform_t *)attacktarget; if (getlorelevel(player, victim->race->raceclass->id) >= PR_BEGINNER) { enum DAMTYPE dt; char buf[BUFLEN],victimname[BUFLEN]; getlfname(victim, victimname); dt = getdamtype(wep[0]); if (isimmuneto(victim->flags, dt, B_FALSE)) { snprintf(buf, BUFLEN, "%s is immune to %s damage. Really attack?",victimname, getdamname(dt)); if (!warnabout(buf)) { return B_TRUE; } } else if (isresistantto(victim->flags, dt, B_FALSE)) { snprintf(buf, BUFLEN, "%s is resistant to %s damage. Really attack?",victimname, getdamname(dt)); if (!warnabout(buf)) { return B_TRUE; } } } } if (maxattacks) { addflagifneeded(lf->flags, F_TOOKACTION, B_TRUE, NA, NA, NULL); if (!lfhasflag(lf, F_COMBOSTRIKE)) { taketime(lf, attacktime); } } attacksdone = 0; while (attacksdone < maxattacks) { for (i = 0; (i < nweps) && (attacksdone < maxattacks); i++) { if (validwep[i]) { if (attacktype == AT_LF) { if (!isdead((lifeform_t *)attacktarget)) { lifeform_t *victim; victim = (lifeform_t *)attacktarget; if (i == 0) { // did we just attack someone by accident? if (!isplayer(lf) && !areenemies(lf, victim) && (lf->race->raceclass->id == RC_HUMANOID) && (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) >= A_LOW) ) { if (lf != victim) { saysorry = B_TRUE; } } // announce attacks from behind which aren't backstabs. if (isplayer(lf) && attackedhelpless && !willbackstab(lf, victim, wep[i])) { char vname[BUFLEN]; getlfname(victim, vname); if (isbehind(lf, victim)) { msg("You attack %s from behind!", vname); } } } if (attacklf(lf, victim, wep[i], damflag[i])) { // failed, or victim died/dodged attacksdone = maxattacks; break; } } } else if (attacktype == AT_OB) { if (attackob(lf, (object_t *)attacktarget, wep[i], damflag[i])) { // failed attacksdone = maxattacks; break; } } else if (attacktype == AT_WALL) { if (attackwall(lf, (cell_t *)attacktarget, wep[i], damflag[i])) { // failed attacksdone = maxattacks; break; } } attacksdone++; // stop attacking if they somehow got out of range // (eg. dodging) if (attacktype == AT_LF) { if (getcelldist(lf->cell, ((lifeform_t *)attacktarget)->cell) > 1) { attacksdone = maxattacks; break; } } } } } // now kill all temp obs if (op) { killobpile(op); } if (attacktype == AT_LF) { // in case the lf disappered.... attacktarget = findlf(lf->cell->map, attacklfid); } // now stop hiding killflagsofid(lf->flags, F_HIDING); if (saysorry && attacktarget) { sayphrase(lf, SP_SORRY, -1, NA, NULL); } if (hasbleedinginjury(lf, BP_HANDS)) { if (!bleedfrom(lf, BP_HANDS, B_FALSE)) { if (isplayer(lf)) msg("^BYou bleed!"); losehp(lf, 1, DT_DIRECT, NULL, "blood loss"); } } // god effects... if (isplayer(lf) && attacktarget) { if (attacktype == AT_LF) { if (!isgod(attacktarget)) { if (attackedfriend) { angergodmaybe(R_GODMERCY, 25, GA_ATTACKALLY); angergodmaybe(R_GODPURITY, 100, GA_ATTACKALLY); switch (getalignment(attacktarget)) { case AL_GOOD: angergodmaybe(R_GODPURITY, 20, GA_ATTACKALLY); // even more break; default: break; } } else if (attackedpeaceful) { if (getraceclass(attacktarget) == RC_ANIMAL) { // attacking helpless animals is fine } else { angergodmaybe(R_GODMERCY, 15, GA_ASSAULT); angergodmaybe(R_GODLIFE, 15, GA_ASSAULT); angergodmaybe(R_GODPURITY, 50, GA_ASSAULT); switch (getalignment(attacktarget)) { case AL_EVIL: //angergodmaybe(R_GODDEATH, 20, GA_ASSAULT); // even more break; case AL_GOOD: angergodmaybe(R_GODPURITY, 20, GA_ASSAULT); // even more break; default: break; } } } else if (attackedhelpless) { if (getraceclass(attacktarget) == RC_ANIMAL) { // attacking helpless animals is fine } else { angergodmaybe(R_GODMERCY, 15, GA_ATTACKHELPLESS); } if (getalignment(attacktarget) != AL_EVIL) { angergodmaybe(R_GODPURITY, 50, GA_ATTACKHELPLESS); pleasegodmaybe(R_GODTHIEVES, 5); pleasegodmaybe(R_GODDEATH, 10); } } if ( ((lifeform_t *)attacktarget)->race->raceclass->id == RC_PLANT) { angergodmaybe(R_GODNATURE, 25, GA_ATTACKOBJECT); } if (lfhasflag(lf, F_USEDPOISON)) { killflagsofid(lf->flags, F_USEDPOISON); if (isplayer(lf)) god_usepoison_response(); } } } else if (attacktype == AT_OB) { angergodmaybe(R_GODNATURE, 10, GA_ATTACKOBJECT); } } if (isplayer(lf)) { // lose a bit of stamina modstamina(lf, -getattackstamloss(lf)); } // stop sprinting stopsprinting(lf); return B_FALSE; } int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) { int dam[100]; enum DAMTYPE damtype[100]; int ndam = 0; char buf[BUFLEN]; char attackername[BUFLEN]; char victimname[BUFLEN],victimbpname[BUFLEN]; int fatal = B_FALSE; int waskod = B_FALSE; int feigneddeath = B_FALSE; int deflected = B_FALSE; int weppassthrough = B_FALSE; int firstisbackstab = B_FALSE; int blocked = B_FALSE,dodged = B_FALSE; flag_t *magicarm = NULL; int hit = B_FALSE; int critical = 0; int fumble = B_FALSE; char wepname[BUFLEN]; //int acc; //int ev; int i; int willheal = B_FALSE; int isunarmed = B_FALSE; skill_t *wepsk = NULL; flag_t *retflag[MAXCANDIDATES]; int nretflags = 0; int aidb = B_FALSE; flag_t *f; enum BODYPART critpos = BP_NONE; if (wep) { wepsk = getobskill(wep->flags); } if (lfhasflag(lf, F_DEBUG)) { aidb = B_TRUE; } if (hasflag(wep->flags, F_UNARMEDWEP)) { isunarmed = B_TRUE; } moveeffects(lf, B_FALSE); if (isdead(lf)) { return B_TRUE; } // if you have somehow attacked someone who was // hiding (bump into them?) then you have now // spotted them. if (ishidingfrom(victim, lf)) { spot_hiding_lf(lf, victim); } // get names getlfname(lf, attackername); if (lf == victim) { if (isplayer(lf)) { strcpy(victimname, "yourself"); } else { strcpy(victimname, "itself"); } } else { getlfname(victim, victimname); } if (aidb) dblog(".oO { trying to attack %s }", victimname); getobname(wep, wepname, 1); if (aidb) dblog(".oO { my weapon is %s }", wepname); if (lf->race->raceclass->id == RC_INSECT) { if (hasactivespell(victim, OT_S_REPELINSECTS)) { if (isplayer(lf)) { msg("^wSomething prevents you from attacking %s!", victimname); } else if (cansee(player, lf)) { msg("^wSomething prevents %s from attacking %s!", attackername, victimname); } //taketime(lf, getattackspeed(lf)); return B_FALSE; } } getflags(lf->flags, retflag, &nretflags, F_PROTALIGN, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->val[1] == getalignment(lf)) { if (isplayer(lf)) { msg("^wA %s force prevents you from attacking %s!", (f->val[1] == AL_GOOD) ? "demonic" : "holy", victimname); } else if (cansee(player, lf)) { msg("^wA %s force prevents %s from attacking %s!", (f->val[1] == AL_GOOD) ? "demonic" : "holy", attackername, victimname); } f->val[0]--; if (f->val[0] <= 0) { killflag(f); } return B_FALSE; } } if (wep && lfhasflagval(victim, F_ASLEEP, NA, ST_KO, NA, NULL)) { f = hasflag(wep->flags, F_MERCIFUL); if (f) { if (isplayer(lf)) { msg("^wYour %s refuses to attack %s!", noprefix(wepname), victimname); if (!f->known) f->known = B_TRUE; } else if (cansee(player, lf)) { msg("^w%s%s %s refuses to attack %s!", attackername, getpossessive(attackername), noprefix(wepname), victimname); if (!f->known) f->known = B_TRUE; } //taketime(lf, getattackspeed(lf)); return B_FALSE; } } getflags(lf->flags, retflag, &nretflags, F_NONCORPOREAL, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; // ie. you have been made noncorporeal if ((f->id == F_NONCORPOREAL) && (f->lifetime != FROMRACE)) { if (isplayer(lf)) { msg("^wYour attack passes straight through %s.", victimname); } else if (cansee(player, lf)) { msg("^w%s%s attack passes straight through %s!", attackername, getpossessive(attackername), victimname); } //taketime(lf, getattackspeed(lf)); return B_FALSE; } } // long weapon in an enclosed space? if (wep && hasflag(wep->flags, F_NEEDSSPACE) && (getdamtype(wep) != DT_PIERCE)) { if (countcellexits(lf->cell, DT_COMPASS) < 3) { if (pctchance(75)) { if (isplayer(lf)) { msg("^wYour %s glances off a nearby wall.", noprefix(wepname)); } else if (cansee(player, lf)) { msg("^w%s%s %s glances off a nearby wall.", attackername, getpossessive(attackername), noprefix(wepname)); } //taketime(lf, getattackspeed(lf)); return B_FALSE; } } } // did you hit? ndam = 0; hit = rolltohit(lf, victim, wep, &critical, &fumble); if (critical && !lfhasflag(lf, F_PHANTASM)) { object_t *armour; char noun[BUFLEN]; if (lfhasflag(victim, F_CANSEVER) && wep && (getdamtype(wep) == DT_SLASH)) { flag_t *retflag[MAXCANDIDATES],*poss[MAXCANDIDATES],*f; int nretflags = 0,nposs = 0; // select a random sever-able body part getflags(victim->flags, retflag, &nretflags, F_CANSEVER, F_NONE); for (i = 0; i < nretflags; i++) { if (hasbp(victim, retflag[i]->val[0])) { poss[nposs++] = retflag[i]; } } f = poss[rnd(0,nposs-1)]; critpos = f->val[0]; } else { critpos = getrandomcorebp(victim, lf); } if (critpos == BP_NONE) { strcpy(victimbpname, victimname); } else { armour = getequippedob(victim->pack, critpos); if (armour) { char armname[BUFLEN]; real_getobname(armour, armname, 1, B_NOPREMODS, B_NOCONDITION, B_BLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL); sprintf(noun, "%s", noprefix(armname)); } else { sprintf(noun, "%s", getbodypartname(victim, critpos)); } // replace victicname to include body part if ((lf == victim) && !isplayer(lf)) { snprintf(victimbpname, BUFLEN, "its %s", noun); } else { getlfname(victim, buf); snprintf(victimbpname, BUFLEN, "%s%s %s", buf, getpossessive(buf), noun); } } } else { strcpy(victimbpname, ""); } if (lf == victim) { if (isplayer(lf)) { strcpy(victimname, "yourself"); } else { strcpy(victimname, "itself"); } } else { getlfname(victim, victimname); } // weapon passing through ghosts etc? if (hit) { if (lfhasflag(victim, F_NONCORPOREAL) && !lfhasflag(lf, F_NONCORPOREAL) ) { // using a magical or blessed weapon? if so you're ok. if (wep && (ismagical(wep) || isblessed(wep) || (wep->material->id == MT_SILVER)) ) { } else { weppassthrough = B_TRUE; hit = B_FALSE; ndam = 0; } } } // deflection? if (hit) { object_t *dwep; dwep = isdualweilding(victim); if (dwep && (getskill(victim, SK_TWOWEAPON) >= PR_MASTER)) { if (onein(4)) { deflected = B_TRUE; hit = B_FALSE; } } } if (hit) { if (aidb) dblog(".oO { i hit! }"); if (!cansee(victim, lf)) { addflag(lf->flags, F_UNSEENATTACKER, victim->id, NA, NA, NULL); if (isplayer(victim) && !isplayer(lf)) needredraw = B_TRUE; } // special case if (isplayer(lf) && (victim->race->id == R_GLOWBUG)) { if ((wep->type->id == OT_EMPTYFLASK) || (wep->type->id == OT_EMPTYVIAL)) { object_t *o; // catch the glowbug! msg("You catch %s in your %s.", victimname, noprefix(wepname)); removeob(wep, 1); killlf(victim); // don't leave a corpse o = addob(lf->pack, "glowing flask"); if (o) { getobname(o, buf, o->amt); msgnocap("%c - %s.",o->letter, buf); } else { // add to the ground o = addob(lf->cell->obpile, "glowing flask"); if (o) { getobname(o, buf, o->amt); msg("%s drops to the ground.", buf); } } // stop all further attacks return B_TRUE; } } // determine base damage // determine damage dam[0] = getdamroll(wep, victim, damflag); if (critical) { // critical hit means an extra damage roll. dam[0] += getdamroll(wep, victim, damflag); } if (aidb) dblog("rolled dam[%d] = %d",0,dam[0]); if (dam[0] < 0) { willheal = B_TRUE; } // damtype? damtype[0] = getdamtype(wep); if (!willheal) { enum SKILLLEVEL slev; float loreadd = 0; // blessed vs undead adjustdamforblessings(&(dam[0]), victim, wep->blessed); // modify for weapon skill, strength etc applylfdammod(&dam[0], lf, wep); // modify for size modifyforsize(&dam[0], lf, victim, 10, M_PCT); // backstab? if (willbackstab(lf, victim, wep)) { //addflag(victim->flags, F_STABBEDBY, lf->id, NA, NA, NULL); dam[0] *= (getskill(lf, SK_BACKSTAB)); firstisbackstab = B_TRUE; } // target asleep? if (lfhasflag(victim, F_ASLEEP)) { dam[0] *= 2; } // bonus for knowledge about the other lf's race? applied LAST. slev = getlorelevel(lf, victim->race->raceclass->id); if (slev == PR_INEPT) { loreadd = 0; } else { loreadd = slev; } dam[0] = (int) ( (float)dam[0] + loreadd ); } if (lfhasflag(lf, F_PHANTASM)) { dam[0] = 0; if (aidb) dblog(".oO { adjusting phantasm dam to 0 }"); } if (aidb) dblog(".oO { dealing %d %s damage }", dam[0], getdamname(damtype[0])); ndam = 1; // determine extra damage for flaming etc. // getextradam from USER too if (!willheal) { getextradamwep(wep, &dam[0], &damtype[0], &ndam, B_FALSE); getextradamlf(lf, &dam[0], &damtype[0], &ndam, B_FALSE); } } else { hit = B_FALSE; ndam = 0; } if (ndam > 0) { flag_t *f; for (i = 0; i < ndam; i++) { int damreducedbyarmour = 0; int backstab = B_FALSE; int prebleed = B_FALSE; int stopnow = B_FALSE; if (firstisbackstab && (i == 0)) backstab = B_TRUE; // slightly more damage for heavy blows if (lfhasflag(lf, F_HEAVYBLOW) || hasflag(wep->flags, F_HEAVYBLOW)) { dam[i] = (int)pctof(110,dam[i]); } // modify for rusted weapon. switch (damtype[i]) { case DT_PIERCE: case DT_SLASH: case DT_CHOP: dam[i] = pctof(getrustdampct(wep), dam[i]); break; default: break; } // blocked by defender's shield? if (i == 0) { int difficulty; char attackname[BUFLEN]; if (lfhasflag(lf, F_HOLYAURA) && isvulnto(victim->flags, DT_HOLY, B_FALSE)) { damtype[i] = DT_HOLY; } sprintf(attackname, "%s%s attack", attackername, getpossessive(attackername)); difficulty = 24 + gettr(victim) - gettr(lf); if (check_for_block(lf, victim, dam[i], damtype[i], difficulty, attackname)) { blocked = B_TRUE; break; // stop processing damage now. } } // modify damage based on defender's resistances adjustdamlf(victim, &dam[i], damtype[i]); //dblog("adjusted for lf to dam[%d] = %d",i,dam[i]); // can't do damage to phantasms if (lfhasflag(lf, F_PHANTASM)) dam[i] = 0; // check for protective spells like heavenly armour if (dam[i] > 0) { if (isphysicaldam(damtype[i])) { getflags(victim->flags, retflag, &nretflags, F_HEAVENARM, F_MAGICARMOUR, F_NONE); if (nretflags) { magicarm = retflag[0]; } } } if (!magicarm) { // armour doesn't reduce damage for backstabs or critical hits. // BUT in the case of a critical hit, the armour might get // damaged during the call to criticalhit() later on. if ((dam[i] > 0) && !backstab && !critical && ismeleedam(damtype[i])) { // modify for defender's armour damreducedbyarmour = getarmourdamreduction(victim, wep, dam[i], damtype[i]); applyarmourdamreduction(victim, wep, damreducedbyarmour, &dam[i], damtype[i]); } // if damage has been reduced zero, it's not a critical hit anymore. if (dam[i] <= 0) { critical = B_FALSE; critpos = BP_NONE; } // make sure quivering palm damage isn't fatal // becquse we want them to explode if (lfhasflag(lf, F_QUIVERINGPALM)) { if (dam[i] >= victim->hp) { dam[i] = victim->hp - 1; } } // at this point, is the damage enough to be fatal? we need to know so we // can determine whether monsters will use feign death or dodge abilities. // NOTE: whether or not the attack is fatal is re-calculated again // later on after damage is applied to ensure that the correct // attack string is displayed ("you hit xxx" vs "you kill xxx"). if (dam[i] >= victim->hp) { fatal = B_TRUE; } // another check for phantasms if (lfhasflag(lf, F_PHANTASM)) dam[0] = 0; // is the victim feigning death? if so, stop now. if (lfhasflag(victim, F_FEIGNINGDEATH)) { killflagsofid(victim->flags, F_FEIGNINGDEATH); } else if (!fatal && !isplayer(victim) && cancast(victim, OT_A_FEIGNDEATH, NULL)) { // monsters might pretend to be dead. if (onein(2) || islowhp(victim)) { // do it! useability(victim, OT_A_FEIGNDEATH, lf, lf->cell); feigneddeath = B_TRUE; } } // did the defender use the Reflexive Dodging skill to dodge the attack? if (fatal && !feigneddeath && lfhasflag(victim, F_DODGES) && cansee(victim, lf) && hasfreeaction(victim)) { cell_t *adj; int candodge = B_FALSE; if (isplayer(victim)) { if (getstamina(victim)) { candodge = B_TRUE; } } else if (onein(3)) { candodge = B_TRUE; } if (candodge) { adj = getdodgecell(victim); if (adj) { flag_t *f; if (isplayer(victim) || cansee(player, victim)) { if (cansee(player, lf)) { msg("^w%s dodge%s %s%s attack!",victimname,isplayer(victim) ? "" : "s", attackername, getpossessive(attackername)); } else { msg("^w%s dodge%s an attack!",victimname,isplayer(victim) ? "" : "s"); } } else if (isplayer(lf)) { msg("You attack something, but it dodges!"); } else if (cansee(player, lf)) { msg("%s attacks something, but it dodges!", attackername); } f = addflag(victim->flags, F_NOTIME, B_TRUE, NA, NA, NULL); moveto(victim, adj, B_FALSE, B_FALSE); killflag(f); if (isplayer(victim)) { setstamina(victim, 0); } // remember that we dodged, to avoid otehr attack effects like // heavy blow, etc. dodged = B_TRUE; // stop processing now. break; } } } } // end if !magicarm prebleed = isbleeding(victim); // Now handle the actual hit. if (magicarm) { // if you have a forcefield/magic armour, you can never be killed until // it vanishes. fatal = B_FALSE; } else if (willheal) { // some magical weapons will heal instead of doing damage if (cansee(player, victim)) { flag_t *f; if (areallies(player, victim)) { msg("^g%s %s healed!",victimname, isplayer(victim) ? "are" : "is"); } else { msg("^w%s %s healed!",victimname, isplayer(victim) ? "are" : "is"); } f = hasflag(wep->flags, F_BALANCE); if (f) { f->known = B_TRUE; } } gainhp(victim, dam[i]); damreducedbyarmour = 0; } else if (lfhasflag(lf, F_QUIVERINGPALM)) { // victim explodes! losehp_real(victim, victim->hp, DT_EXPLOSIVE, lf, "a quivering palm strike", B_FALSE, NULL, B_FALSE, NULL, B_FALSE); damreducedbyarmour = 0; } else { // actually deal the melee damage! char attackername2[BUFLEN]; real_getlfname(lf, attackername2, NULL, B_SHOWALL, B_REALRACE); if (lf->race->raceclass->id == RC_GOD) { flag_t *gf; gf = lfhasflag(lf, F_GODOF); if (getgender(lf) == G_FEMALE) { strcat(attackername2, " the Goddess of "); } else { strcat(attackername2, " the God of "); } strcat(attackername2, gf->text); } // get name of weapon/attacker, for "killedby" text if (wep && !isunarmed) { char wepname[BUFLEN]; real_getobname(wep, wepname, 1, B_PREMODS, B_NOCONDITION, B_NOBLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL); /* snprintf(buf, BUFLEN, "%s^%s %s",attackername2, (lf == victim) ? "using" : "weilding", wepname); */ // ie. killed by "an orc's dagger" snprintf(buf, BUFLEN, "%s%s %s",attackername2, getpossessive(attackername2), noprefix(wepname)); } else { strcpy(buf, attackername2); } // apply damage to victim // note: we delay extra effects from the damage so that we get a chance to first annoiunce // the hit. // // otherwise the messages are in the wrong order, eg: // "the fire sets you alight!" // "xxx hits you with a flaming sword." // don't adjust damage for resistences - we've already done that losehp_real(victim, dam[i], damtype[i], lf, buf, B_NODAMADJUST, wep, B_NORETALIATE, &waskod, B_NODAMEFFECTS); } // was it fatal ? override previously calculated value. if ((victim->hp <= 0) && !waskod) { fatal = B_TRUE; } else { fatal = B_FALSE; } // announce the hit if (!feigneddeath) { if (isplayer(lf) || isplayer(victim) || cansee(player, lf) || cansee(player, victim)) { construct_hit_string(lf, victim, attackername, victimname, victimbpname, wep, damtype[i], dam[i], victim->maxhp, i, backstab, critical, lfhasflag(victim, F_NOFATALTEXT) ? B_FALSE : fatal, isunarmed, buf); if (strlen(buf)) { warn("%s", buf); } } if (fatal) { if (strstr(buf, "behead")) { // we'll need to place the severed head object addflag(victim->flags, F_BEHEADED, B_TRUE, NA, NA, NULL); addflag(victim->flags, F_MUTILATED, B_TRUE, NA, NA, NULL); if (damtypecausesbleed(damtype[i], wep)) { bleed(victim, B_SPLATTER); } } else if (strstr(buf, "bisect")) { if (victim->race->id != R_EARTHWYRM) { addflag(victim->flags, F_MUTILATED, B_TRUE, NA, NA, NULL); } if (damtypecausesbleed(damtype[i], wep)) { bleed(victim, B_SPLATTER); } } else { if (damtypecausesbleed(damtype[i], wep)) { int bloodamt = 0,n; switch (getlfsize(victim)) { case SZ_MINI: if (onein(3)) bloodamt = 1; break; case SZ_TINY: bloodamt = rnd(0,1); break; case SZ_SMALL: bloodamt = rnd(0,2); break; case SZ_MEDIUM: bloodamt = rnd(1,3); break; case SZ_HUMAN: bloodamt = rnd(3,5); break; case SZ_LARGE: bloodamt = rnd(5,10); break; case SZ_HUGE: bloodamt = rnd(10,15); break; case SZ_ENORMOUS: case SZ_MAX: bloodamt = rnd(15,20); break; default: bloodamt = 0; break; } for (n = 0; n < bloodamt; n++) { bleed(victim, B_NOSPLATTER); } } } if (isplayer(lf) && cansee(player, victim) && !hasflag(victim->flags, F_NODEATHANNOUNCE)) { if (!hasflag(victim->flags, F_PHANTASM)) { // don't also say "the xx dies" addflag(victim->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL); } } } } // end if !feigneddeath // now magic armour will pulse and maybe vanish. if (magicarm) { int damprevented; // prevent all damage if possible damprevented = dam[i]; magicarm->val[0] -= damprevented; if (magicarm->val[0] <= 0) { // did magic armour come from a spell? if (magicarm->lifetime == FROMSPELL) { flag_t *spellflag; spellflag = hasactivespell(victim, magicarm->obfrom); if (spellflag) { killflag(spellflag); } } // magic armour vanishes now. killflag(magicarm); } else { if (cansee(player, victim)) { msg("^%c%s%s %s pulses!^n", CC_GOOD, victimname, getpossessive(victimname), magicarm->text); } } } else { // victim's armour loses hp. note that it loses the full // amount of damage dealt, not just what it reduced. if (damreducedbyarmour && !critical) { applyarmourdamage(victim, wep, dam[i] + damreducedbyarmour, damtype[i], lf); // train armour practice(victim, SK_ARMOUR, 1); } } // make noise noise(lf->cell, lf, NC_FIGHTING, SV_SHOUT, "fighting.", NULL); if (backstab) { practice(lf, SK_BACKSTAB, 1); pleasegodmaybe(R_GODTHIEVES, 8); } // now handle the extra hp loss effects which we postponed above. losehpeffects(victim, dam[i], damtype[i], lf, wep, B_NORETALIATE, waskod, &waskod, prebleed); if (fatal || waskod || dodged || stopnow) break; // stop now, don't process further damtypes! } // end foreach damtype if (waskod) { loseconsciousness(victim, waskod, lf); } // other effects if ((victim->material->id == MT_WATER) && wep && !isunarmed) { makewet(wep, 1); } if (!isdead(victim) && !blocked && !dodged) { // special weapon effects, as long as you're not doing a heavy blow if (!lfhasflag(lf, F_HEAVYBLOW) && dam[0]) { wepeffects(wep->flags, victim->cell, damflag, dam[0]); } if (isunarmed) { f = lfhasflag(lf, F_FREEZINGTOUCH); if (f) { int diff; diff = f->val[2]; if (isimmuneto(victim->flags, DT_COLD, B_FALSE) || skillcheck(victim, SC_RESISTMAG, diff, 0)) { if (isplayer(victim)) { msg("You feel mildly chilly."); } } else { // victim turns to ice for a while! freezelf(victim, lf, f->val[1]); } killflag(f); } } // critical hit effects if (critical && damtypecausescriteffects(damtype[0])) { criticalhit(lf, victim, critpos, wep, dam[0], damtype[0]); } // confer flags from attacker? if (dam[0]) { wepeffects(lf->flags, victim->cell, damflag, dam[0]); } // special lifeform-based effects if ((lf->race->id == R_COCKATRICE) && dam[0]) { // first saving throw... if (skillcheck(victim, SC_CON, 25, 0)) { // slowed addtempflag(victim->flags, F_SLOWACTMOVE, 15, NA, NA, NULL, 2); } else { // second saving throw... if (skillcheck(victim, SC_CON, 25, 0)) { // paralyzed addtempflag(victim->flags, F_PARALYZED, B_TRUE, NA, NA, NULL, 5); } else { if (!lfhasflag(lf, F_BEINGSTONED)) { // stoned! addflag(victim->flags, F_BEINGSTONED, 2, NA, NA, NULL); } } } } else if ((lf->race->id == R_STIRGE) || (lf->race->id == R_LEECH)) { if (getexposedlimbs(victim)) { // automatically latch on if (!lfhasflag(victim, F_NONCORPOREAL) && !hasflag(lf->flags, F_ATTACHEDTO)) { addflag(lf->flags, F_ATTACHEDTO, victim->id, NA, NA, NULL); } } } // if victim was flying and took >= 40% of its hit points, it drops to the ground. if (isphysicaldam(damtype[i]) && (dam[i] >= pctof(40, victim->maxhp))) { fall_from_air(victim); } // if victim can still move... if (hasfreeaction(victim)) { fightback(victim, lf); } } // end if !isdead(victim) if (!blocked && !dodged) { // adhesion? f = lfhasflag(victim, F_ADHESIVE); if (f && wep && !isunarmed && !skillcheck(lf, SC_STR, f->val[0], 0)) { // attacker's weapon sticks to it! if (cansee(player, lf) || cansee(player, victim)) { char wepname[BUFLEN]; real_getobname(wep, wepname, 1, B_PREMODS, B_NOCONDITION, B_NOBLINDADJUST, B_BLESSINGS, B_NOUSED, B_NOSHOWALL); msg("^%c%s%s %s %s to %s!", getlfcol(lf, CC_BAD), attackername, getpossessive(attackername), noprefix(wepname), (wep->amt == 1) ? "sticks" : "stick", victimname); } moveob(wep, victim->pack, wep->amt); } // retaliation happens even if victim died getflags(victim->flags, retflag, &nretflags, F_RETALIATE, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if ((f->id == F_RETALIATE) && (getcelldist(victim->cell, lf->cell) == 1)) { int rdam; char damstring[BUFLEN]; rdam = rolldie(f->val[0], f->val[1]); if (cansee(player, victim)) { msg("^%c%s%s %s %s %s!", isplayer(lf) ? 'b' : 'n', victimname, getpossessive(victimname), noprefix(f->text), getattackverb(victim, NULL, f->val[2], rdam, lf->maxhp), attackername); } snprintf(damstring, BUFLEN, "%s%s %s", victimname, getpossessive(victimname), noprefix(f->text)); losehp_real(lf, rdam, f->val[2], victim, damstring, B_TRUE, NULL, B_TRUE, NULL, B_TRUE); } } } } else { // miss! if (aidb) dblog(".oO { i missed! }"); // announce it if (weppassthrough) { if (cansee(player, lf)) { msg("^w%s%s attack passes straight through %s!", attackername, getpossessive(attackername), victimname); } } else if (deflected) { if (cansee(player, lf)) { msg("^w%s deflect%s %s%s attack.", victimname, isplayer(victim) ? "" : "s",attackername, getpossessive(attackername)); } } else if (lfhasflag(victim, F_MAGSHIELD) && ismetal(wep->material->id)) { if (isplayer(lf) || cansee(player, lf)) { snprintf(buf, BUFLEN, "%s",attackername); msg("^w%s%s magnetic shield repels %s%s attack.", victimname, getpossessive(victimname), buf, getpossessive(buf)); } } else { int anticipated = B_FALSE; if (lfhasflagval(victim, F_ANTICIPATE, lf->id, NA, NA, NULL)) { anticipated = B_TRUE; } if (isplayer(lf)) { msg("You %smiss %s.", anticipated ? "wildly " : "", victimname); } else { if (cansee(player, lf)) { // capitalise first letter snprintf(buf, BUFLEN, "%s",attackername); msg("%s %smisses %s.", buf, anticipated ? "wildly " : "", victimname); } } // victim trains evasion practice(victim, SK_EVASION, 1); // chance to fumble attack. if (fumble) { if (wep && !isunarmed) { if (isplayer(lf)) { msg("^%cYou fumble your attack!", getlfcol(lf, CC_BAD)); } else if (cansee(player, lf)) { msg("^%c%s fumbles its attack!", attackername, getlfcol(lf, CC_BAD)); } drop(wep, ALL); wep = NULL; } } // chance that ai will give up if we can't reach the victim if (!isplayer(lf) && !canreach(lf, victim, NULL)) { if (pctchance(90)) { // TODO: announce this. // the xxx loses interest loseaitargets(lf); if (isplayer(victim) && cansee(player, lf)) { msg("%s seems to have lost interest in you.", attackername); } } } } } // practice? if (hit) { if (wepsk) { practice(lf, wepsk->id, 1); } if (isdualweilding(lf)) { practice(lf, SK_TWOWEAPON, 1); } } // induction of fear? if (!isdead(victim)) { if (lfhasflag(victim, F_INDUCEFEAR)) { if (cansee(lf, victim)) { scare(lf, victim, rnd(2,3), 0); } } } // twoweapon? if (hit && !blocked) { enum SKILLLEVEL slev; slev = getskill(lf, SK_TWOWEAPON); if (slev >= PR_SKILLED) { object_t *secwep; secwep = getsecmeleeweapon(lf); // ie. if we are using two weapons, and the current one // is the first... if (secwep && (secwep != wep)) { int bonus = 0; // next hit will have enhanced accuracy if (slev == PR_SKILLED) { bonus = 10; } else if (slev == PR_EXPERT) { bonus = 25; } else if (slev == PR_MASTER) { bonus = 40; } if (bonus) { addtempflag(lf->flags, F_ACCURACYMOD, bonus, NA, NA, NULL, 1); } } } } if (fatal || waskod || dodged || fumble) { // don't keep attacking if the victim is dead, or moved! // also don't keep attacking if we fumbled. return B_TRUE; } if (aidb) dblog(".oO { doattack about to return B_FALSE }"); return B_FALSE; } int attackob(lifeform_t *lf, object_t *o, object_t *wep, flag_t *damflag) { int dam[100]; enum DAMTYPE damtype[100]; int ndam = 0; char attackername[BUFLEN]; char obname[BUFLEN]; flag_t *f; int isunarmed = B_FALSE; cell_t *obloc = NULL; char wepname[BUFLEN],buf[BUFLEN]; int i; //int aidb = B_TRUE; int maxhp; moveeffects(lf, B_FALSE); if (isdead(lf)) return B_TRUE; // get names getlfname(lf, attackername); getobname(o, obname, o->amt); // get target object details obloc = o->pile->where; f = hasflag(o->flags, F_OBHP); if (f) { maxhp = f->val[1]; } else { maxhp = 1; } if (hasflag(wep->flags, F_UNARMEDWEP)) { isunarmed = B_TRUE; } getobname(wep, wepname, 1); // don't need to figure out accuracy - we always hit. // determine damage ndam = 0; //if (unarmedflag && (unarmedflag->val[0] != NA)) { dam[ndam] = getdamroll(wep, NULL, damflag); // modify for strength if (!hasflag(wep->flags, F_NOSTRDAMMOD) && !lfhasflag(lf, F_NOSTRDAMMOD)) { dam[ndam] += getstrdammod(lf); } // damtype? damtype[ndam] = getdamtype(wep); ndam++; // don't need to check for blessed vs mosnters // determine extra damage getextradamwep(wep, &dam[0], &damtype[0], &ndam, B_FALSE); getextradamlf(lf, &dam[0], &damtype[0], &ndam, B_FALSE); for (i = 0; i < ndam; i++) { // announce the hit construct_hit_string(lf, NULL, attackername, obname, NULL, wep, damtype[i], dam[i], maxhp, i, B_FALSE, B_FALSE, B_FALSE, isunarmed, buf); if (strlen(buf)) { msg("%s", buf); } if (!isplayer(lf) && !cansee(player, lf)) { char noisebuf[BUFLEN]; int vol; switch (o->material->id) { case MT_METAL: strcpy(noisebuf, "a metallic clanging."); vol = 4; break; case MT_GLASS: strcpy(noisebuf, "cracking glass."); vol = 4; break; case MT_WOOD: case MT_DRAGONWOOD: strcpy(noisebuf, "splintering wood."); vol = 4; break; case MT_BONE: case MT_STONE: strcpy(noisebuf, "a dull thumping."); vol = 3; break; case MT_GOLD: case MT_SILVER: case MT_LEATHER: strcpy(noisebuf, "a dull thumping."); vol = 2; break; case MT_PAPER: case MT_WETPAPER: case MT_RUBBER: strcpy(noisebuf, "a dull thumping."); vol = 1; break; default: strcpy(noisebuf, "something being hit."); vol = 3; break; } noise(obloc, NULL, NC_OTHER, vol, noisebuf, NULL); } if ((i == 0) && (wep->type->id == OT_FISTS) && hasflag(o->flags, F_HARDNESS)) { object_t *gloves; gloves = getequippedob(lf->pack, BP_HANDS); if (gloves && hasflag(gloves->flags, F_HARDNESS)) { // ok } else if ((o->material->id == MT_WOOD) && (getskill(lf, SK_UNARMED) >= PR_ADEPT)) { // ok } else { char buf[BUFLEN]; snprintf(buf, BUFLEN, "punching %s", obname); if ( losehp(lf, 1, DT_BASH, lf, buf)) { if (isplayer(lf)) { msg("^bOw!"); } } } } // smash wood bonus if ((wep->type->id == OT_FISTS) && (o->material->id == MT_WOOD) && (getskill(lf, SK_UNARMED) >= PR_ADEPT)) { dam[i] += rnd(1,6); } // object loses hp takedamage(o, dam[i], damtype[i]); if (isplayer(lf) && hasflag(o->flags, F_LOCKED)) { angergodmaybe(R_GODTHIEVES, 25, GA_MONEY); } if (isdeadob(o)) { break; } } // end foreach damtype if (!isdeadob(o)) { // special weapon effects, as long as you're not doing a heavy blow if (!lfhasflag(lf, F_HEAVYBLOW) && dam[0]) { wepeffects(wep->flags, obloc, damflag, dam[0]); } } if (isunarmed) { // touch effects touch(lf, o); } else if (hasflag(o->flags, F_IMPASSABLE)) { // weapon gets damaged ? if (wep && (ndam > 0)) { if (wepdullable(wep)) { // weapon gets duller if (rnd(1,2)) makeduller(wep, 1); } } } // still not dead? more checks if (!isdeadob(o)) { f = hasflag(o->flags, F_TRAPPED); if (f && pctchance(75)) { doobtraps(o, lf); } else { // if a trap didn't go off, you might break the lock f = hasflag(o->flags, F_LOCKED); if (f && (damtype[i] == DT_BASH)) { int difficulty; int unlockit = B_FALSE; difficulty = f->val[0]; if (rnd(0,getattr(lf, A_STR)) + dam[0] >= difficulty) { // hit it hard enough unlockit = B_TRUE; } else if ( pctchance(dam[0]*3)) { // did enough damage unlockit = B_TRUE; } if (unlockit) { // lock breaks! if (isplayer(lf)) { msg("You break the lock!"); } killflagsofid(o->flags, F_LOCKED); } } } } return B_FALSE; } int attackwall(lifeform_t *lf, cell_t *c, object_t *wep, flag_t *damflag) { int dam[100]; enum DAMTYPE damtype[100]; int ndam = 0; char attackername[BUFLEN]; int isunarmed = B_FALSE; char buf[BUFLEN]; int i; int maxhp; moveeffects(lf, B_FALSE); if (isdead(lf)) return B_TRUE; maxhp = c->type->hp; // get names getlfname(lf, attackername); // don't need to figure out accuracy - we always hit. // determine damage ndam = 0; //if (unarmedflag && (unarmedflag->val[0] != NA)) { dam[ndam] = getdamroll(wep, NULL, damflag); // modify for strength if (!hasflag(wep->flags, F_NOSTRDAMMOD) && !lfhasflag(lf, F_NOSTRDAMMOD)) { dam[ndam] += getstrdammod(lf); } // damtype? damtype[ndam] = getdamtype(wep); ndam++; // don't need to check for blessed vs mosnters // determine extra damage getextradamwep(wep, &dam[0], &damtype[0], &ndam, B_FALSE); getextradamlf(lf, &dam[0], &damtype[0], &ndam, B_FALSE); for (i = 0; i < ndam; i++) { char cellname[BUFLEN]; sprintf(cellname, "%s %s", needan(c->type->name) ? "an" : "a", c->type->name); // announce the hit construct_hit_string(lf, NULL, attackername, cellname, NULL, wep, damtype[i], dam[i], maxhp, i, B_FALSE, B_FALSE, B_FALSE, isunarmed, buf); if (strlen(buf)) { msg("%s", buf); } if (!isplayer(lf) && !cansee(player, lf)) { char noisebuf[BUFLEN]; int vol; switch (c->type->material->id) { case MT_METAL: strcpy(noisebuf, "a metallic clanging."); vol = 4; break; case MT_GLASS: strcpy(noisebuf, "cracking glass."); vol = 4; break; case MT_WOOD: case MT_DRAGONWOOD: strcpy(noisebuf, "splintering wood."); vol = 4; break; case MT_BONE: case MT_STONE: strcpy(noisebuf, "a dull thumping."); vol = 3; break; case MT_GOLD: case MT_SILVER: case MT_LEATHER: strcpy(noisebuf, "a dull thumping."); vol = 2; break; case MT_PAPER: case MT_WETPAPER: case MT_RUBBER: strcpy(noisebuf, "a dull thumping."); vol = 1; break; default: strcpy(noisebuf, "something being hit."); vol = 3; break; } noise(c, NULL, NC_OTHER, vol, noisebuf, NULL); } if ((i == 0) && (wep->type->id == OT_FISTS) && hasflag(c->type->material->flags, F_HARDNESS)) { object_t *gloves; gloves = getequippedob(lf->pack, BP_HANDS); if (gloves && hasflag(gloves->flags, F_HARDNESS)) { // ok } else if ((c->type->material->id == MT_WOOD) && (getskill(lf, SK_UNARMED) >= PR_ADEPT)) { // ok } else { char buf[BUFLEN]; snprintf(buf, BUFLEN, "punching %s", cellname); if ( losehp(lf, 1, DT_BASH, lf, buf)) { if (isplayer(lf)) { msg("^bOw!"); } } } } // smash wood bonus if ((wep->type->id == OT_FISTS) && (c->type->material->id == MT_WOOD) && (getskill(lf, SK_UNARMED) >= PR_ADEPT)) { dam[i] += rnd(1,6); } if (dam[i] > 0) { damagecell(c, dam[i], damtype[i]); // don't deal any more damage types break; } } // end foreach damtype // no special weapon effects on cells. // weapon gets damaged ? if (wep && (ndam > 0)) { if (wepdullable(wep)) { // weapon gets duller if (rnd(1,2)) makeduller(wep, 1); } } return B_FALSE; } enum DAMTYPE basedamagetype(enum DAMTYPE dt) { switch (dt) { case DT_HEAT: dt = DT_FIRE; break; default: break; } return dt; } // returns B_TRUE if victim blocked lf's attack int check_for_block(lifeform_t *lf, lifeform_t *victim, int dam, enum DAMTYPE damtype, int difficulty, char *attackname) { object_t *shield[MAXPILEOBS]; int checkmod[MAXPILEOBS]; int nshields,i; if (lf && !cansee(victim, lf)) return B_FALSE; // get all usable shields for this damtype getallshields(victim, damtype, shield, checkmod, &nshields); for (i = 0; i < nshields; i++) { // did we block with this object? if (skillcheck(victim, SC_SHIELDBLOCK, difficulty, checkmod[i])) { char shname[BUFLEN]; char victimname[BUFLEN]; getlfname(victim, victimname); // announce real_getobname(shield[i], shname, 1, B_PREMODS, B_NOCONDITION, B_BLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL); if (lf && isplayer(lf)) { // player is atatcking msg("%s blocks %s with %s.", victimname, attackname, shname); } else if ((lf && cansee(player, lf)) || cansee(player, victim)) { // monster is attacking msg("%s block%s %s with %s.", victimname, isplayer(victim) ? "" : "s", attackname, shname); } if (isshield(shield[i])) { // apply all damage to shield. // (blocking with weapons won't damage them) takedamage(shield[i], dam, damtype); practice(victim, SK_SHIELDS, 1); } if (!isdeadob(shield[i])) { flag_t *f; f = hasflag(lf->flags, F_ADHESIVE); if (f && !skillcheck(victim, SC_STR, f->val[0], 0)) { if (cansee(player, lf) || cansee(player, victim)) { char attname[BUFLEN]; getlfname(lf, attname); msg("^%c%s%s %s %s to %s!", getlfcol(victim, CC_BAD), victimname, getpossessive(victimname), noprefix(shname), (shield[i]->amt == 1) ? "sticks" : "stick", attname); } moveob(shield[i], lf->pack, shield[i]->amt); } } // stop checking. return B_TRUE; } } return B_FALSE; } void criticalhit(lifeform_t *lf, lifeform_t *victim, enum BODYPART hitpos, object_t *wep, int dam, enum DAMTYPE damtype) { object_t *o,*armour; int protected = B_FALSE; char lfname[BUFLEN],victimname[BUFLEN]; if (hitpos == BP_NONE) return; // replace some dam types if (damtype == DT_UNARMED) damtype = DT_BASH; if (damtype == DT_BITE) damtype = DT_SLASH; if (damtype == DT_PIERCE) damtype = DT_SLASH; if (damtype == DT_CHOP) damtype = DT_SLASH; // special case - beheading multiheaded monsters if (lf && victim && (damtype == DT_SLASH)) { flag_t *selflag; selflag = lfhasflagval(victim, F_CANSEVER, hitpos, NA, NA, NULL); if (selflag) { int i,nretflags,num; char dambuf[BUFLEN]; char bpname[BUFLEN]; flag_t *retflag[MAXCANDIDATES]; strcpy(bpname, getbodypartname(victim, hitpos)); // special case if (!victim->race->id == R_HYDRA) { // remove it addflag(victim->flags, F_NOBODYPART, hitpos, NA, NA, NULL); // remove hasattack flags getflags(victim->flags, retflag, &nretflags, F_CANCAST, F_HASATTACK, F_NONE); for (i = 0; i < nretflags; i++) { if (retflag[i]->id == F_CANCAST) { if (retflag[i]->val[0] == selflag->val[2]) { killflag(retflag[i]); continue; } } else if (retflag[i]->id == F_HASATTACK) { if (retflag[i]->val[2] == selflag->val[1]) { killflag(retflag[i]); continue; } } } } if (cansee(player, victim)) { char lfname[BUFLEN],vname[BUFLEN]; getlfname(victim, vname); if (lf && cansee(player, lf)) { // can see who did it getlfname(lf, lfname); } else { strcpy(lfname, "Something"); } msg("^%c%s slice%s off %s%s %s!", getlfcol(victim, CC_VBAD), lfname, isplayer(lf) ? "" : "s", vname, getpossessive(vname), bpname); } // take extra damage based on number of severable limbs num = countflagsofid(victim->race->flags, F_CANSEVER); if (victim->race->id == R_HYDRA) { int regrow = B_TRUE; if (wep) { if (hasflag(wep->flags, F_ONFIRE) || (wep->material->id == MT_SILVER)) { regrow = B_FALSE; } } if (regrow) { growhydrahead(victim, B_TRUE); } else { // lose a head losehydrahead(victim); } } else { sprintf(dambuf, "a severed %s", bpname); losehp(victim, pctof(100/num, victim->maxhp), DT_DIRECT, lf, dambuf); } // drop the head if (strlen(selflag->text)) { addob(victim->cell->obpile, selflag->text); } return; } } if (damtype == DT_BASH) { switch (hitpos) { default: case BP_BODY: if (pctchance(40)) { // some kind of non-injury effect switch (rnd(1,2)) { case 1: fall(victim, lf, B_TRUE); break; case 2: if (lf) { if (cansee(player, lf) || cansee(player, victim)) { getlfname(lf, lfname); getlfname(victim, victimname); setfacing(victim, getrandomdirexcept(DT_COMPASS, victim->facing)); msg("%s%s blow spins %s around!", lfname, getpossessive(lfname),victimname); } } else { if (isplayer(victim) || cansee(player, victim)) { getlfname(victim, victimname); setfacing(victim, getrandomdirexcept(DT_COMPASS, victim->facing)); msg("%s is spun around!", victimname); } } break; } } if ((armour = getarmour(victim, BP_BODY)) != NULL) { protected = checkcritprotection(victim,armour); takedamage(armour, dam, damtype); } if (!protected) injure(victim, BP_BODY, damtype, IJ_NONE); break; case BP_HEAD: if (pctchance(80)) fall(victim, lf, B_TRUE); stun(victim, 2); // chance of your helmet falling off o = getarmour(victim, BP_HEAD); if (o) { if (onein(2)) { if (isplayer(victim)) { char buf[BUFLEN]; getobname(o, buf, o->amt); msg("Your %s falls off!", noprefix(buf)); } else if (cansee(player, victim)) { char buf[BUFLEN], lfname[BUFLEN]; getobname(o, buf, o->amt); getlfname(victim, lfname); msg("%s%s %s falls off!", lfname, getpossessive(lfname), noprefix(buf)); } moveob(o, victim->cell->obpile, o->amt); } else { /* if (isplayer(victim)) { msg("Your %s protects you.", noprefix(obname)); } else if (cansee(player, victim)) { getlfname(victim, victimname); msg("%s%s %s protects it.", victimname, getpossessive(victimname), noprefix(obname)); } */ takedamage(o, dam, damtype); } } else { injure(victim, BP_HEAD, damtype, IJ_NONE); } break; case BP_HANDS: if ((armour = getarmour(victim, BP_HANDS)) != NULL) { protected = checkcritprotection(victim,armour); takedamage(armour, dam, damtype); } if (!protected) injure(victim, BP_HANDS, damtype, IJ_NONE); if (onein(2)) { // drop your weapon! o = getweapon(victim); if (o) { drop(o, ALL); } break; } case BP_LEGS: if (pctchance(70)) fall(victim, lf, B_TRUE); if ((armour = getarmour(victim, BP_LEGS)) != NULL) { /* getobname(armour, obname, armour->amt); if (isplayer(victim)) { msg("Your %s protects you.", noprefix(obname)); } else if (cansee(player, victim)) { getlfname(victim, victimname); msg("%s%s %s protects it.", victimname, getpossessive(victimname), noprefix(obname)); } */ takedamage(armour, dam, damtype); } else { injure(victim, BP_LEGS, damtype, IJ_NONE); } break; } } else if (damtype == DT_SLASH) { if ((armour = getarmour(victim, hitpos)) != NULL) { protected = checkcritprotection(victim,armour); takedamage(armour, dam, damtype); } if (!protected) injure(victim, hitpos, damtype, IJ_NONE); } else if (damtype == DT_EXPLOSIVE) { if ((armour = getarmour(victim, hitpos)) != NULL) { int min,max; protected = checkcritprotection(victim,armour); max = getobmaxhp(armour); min = max / 2; limit(&min, 1, NA); takedamage(armour, rnd(min,max), DT_EXPLOSIVE); } if (!protected) injure(victim, hitpos, damtype, IJ_NONE); } if (lf) { if (lfhasflag(lf, F_CRITKNOCKDOWN) && !isprone(victim)) { fall(victim, lf, B_TRUE); } } } int damtypecausesbleed(enum DAMTYPE dt, object_t *fromob) { switch (dt) { case DT_PIERCE: case DT_SLASH: case DT_BITE: case DT_CHOP: case DT_EXPLOSIVE: return B_TRUE; case DT_PROJECTILE: if (fromob && hasflag(fromob->flags, F_MISSILEDAM)) return B_TRUE; break; default: break; } return B_FALSE; } int damtypecausescriteffects(enum DAMTYPE dt) { switch (dt) { case DT_BASH: case DT_UNARMED: case DT_SLASH: case DT_PIERCE: case DT_BITE: case DT_CHOP: /* case DT_EXPLOSIVE: case DT_FALL: */ return B_TRUE; default: break; } return B_FALSE; } // returns the amount of damage the armour blocked... int getarmourdamreduction(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damtype) { int reduceamt = 0; int ar,min,max; //int pctrange; object_t *o; flag_t *pierce = NULL; if (hasflag(wep->flags, F_ARMOURIGNORE)) { reduceamt = 0; } ar = getarmourrating(lf, NULL, NULL, NULL, NULL); // between 25% and 75% of AR. // ie. with AR of 20, all damage is reduced by 5-15. //pctrange = rnd(25,75); //reduceamt = pctof(pctrange, ar); getarrange(ar, &min, &max); reduceamt = rnd(min, max); // special case if (damtype == DT_PROJECTILE) { o = getequippedob(lf->pack, BP_BODY); if (o && (o->type->id == OT_FLAKJACKET)) { // stop ALL missile damage reduceamt = dam; } } // and if weapon is armour piercing, you always take at least its // armourpiercing valut. pierce = hasflag(wep->flags, F_ARMOURPIERCE); if (pierce) { reduceamt -= pierce->val[0]; } if (reduceamt < 0) reduceamt = 0; return reduceamt; } // for a given ArmourRating (AR), return range of damage which it might reduce void getarrange(int arating, int *min, int *max) { *min = pctof(20,arating); *max = pctof(60, arating); } /* object_t *getattackwep(lifeform_t *lf, obpile_t **unarmedpile, flag_t **unarmedflag) { object_t *wep; wep = getweapon(lf); if (!wep) { // ie. unarmed *unarmedpile = getunarmedweapon(lf, unarmedflag); if ((*unarmedpile)->first) { wep = (*unarmedpile)->first; } else { wep = NULL; } } return wep; } */ enum DAMTYPE getdamtype(object_t *wep) { flag_t *f; enum DAMTYPE dt = DT_NONE; f = hasflag(wep->flags, F_DAM); if (f) { dt = f->val[0]; } else { // default - you are just bashing with whatever // you weilded. dt = DT_BASH; } return dt; } int getextradamlf(lifeform_t *lf, int *dam, enum DAMTYPE *damtype, int *ndam, int wantmax) { flag_t *f; int i; flag_t *retflag[MAXCANDIDATES]; int nretflags = 0; if (lf && lfhasflag(lf, F_PHANTASM)) { return *dam; } // special case - EXTRADAM goes onto INITIAL dam[] if the same type, rather than // adding a new one. getflags(lf->flags, retflag, &nretflags, F_EXTRADAM, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_EXTRADAM) { int *damwhere; int *damtypewhere; int doinc = B_FALSE; if ((f->val[0] == NA) || (f->val[0] == *damtype)) { // addition to the first one damwhere = dam; damtypewhere = damtype; *(damwhere) += real_roll(f->text, wantmax); // addition } else { // add a new damtype damwhere = (dam + *ndam); damtypewhere = (damtype + *ndam); doinc = B_TRUE; *(damwhere) = real_roll(f->text, wantmax); // set *(damtypewhere) = f->val[0]; } if ((f->lifetime == FROMOBEQUIP) || (f->lifetime == FROMOBHOLD) || (f->lifetime == FROMOBACTIVATE) ) { object_t *obfrom; obfrom = findobbyid(lf->pack, f->obfrom); if (obfrom) { int bonusdam = 0; sumflags(obfrom->flags, F_BONUS, &bonusdam, NULL, NULL); *(damwhere) += bonusdam; } } if (doinc) { (*ndam)++; } } } return *dam; } int getextradamwep(object_t *wep, int *dam, enum DAMTYPE *damtype, int *ndam, int fordisplay) { flag_t *f; int i; flag_t *retflag[MAXCANDIDATES]; int nretflags = 0; lifeform_t *owner; owner = wep->pile->owner; if (owner && lfhasflag(owner, F_PHANTASM)) { return *dam; } // weapons like cattle prod deal extra damage if they have charges left f = hasflag(wep->flags, F_EXTRADAMWITHCHARGES); if (f) { int charges; charges = getcharges(wep); if (fordisplay && !chargesknown(wep)) { // ie don't display the extra damage charges = 0; } if (charges > 0) { if (!fordisplay) { // use up a charge usecharge(wep); } // deal extra electric damage *(dam + *ndam) = real_roll(f->text, fordisplay); *(damtype + *ndam) = f->val[0]; (*ndam)++; } } // enchanted weapons only deal magic damage if the user has remaining mp. if (owner && owner->mp) { f = hasflag(wep->flags, F_ENCHANTED); if (f) { if (strlen(f->text)) { *(dam + *ndam) = real_roll(f->text, fordisplay); } else { *(dam + *ndam) = real_roll("1d2", fordisplay); // default: 1d2 extra damage } *(damtype + *ndam) = DT_MAGIC; (*ndam)++; } } getflags(wep->flags, retflag, &nretflags, F_EXTRADAM, F_FROZEN, F_ONFIRE, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_EXTRADAM) { int *damwhere; int *damtypewhere; int doinc = B_FALSE; if ((f->val[0] == NA) || (f->val[0] == *damtype)) { // addition to the first one damwhere = dam; damtypewhere = damtype; *(damwhere) += real_roll(f->text, fordisplay); // addition } else { // add a new damtype damwhere = (dam + *ndam); damtypewhere = (damtype + *ndam); doinc = B_TRUE; *(damwhere) = real_roll(f->text, fordisplay); // set *(damtypewhere) = f->val[0]; } if (doinc) { (*ndam)++; } } if (f->id == F_ONFIRE) { if (strlen(f->text)) { *(dam + *ndam) = real_roll(f->text, fordisplay); } else { *(dam + *ndam) = real_roll("1d4", fordisplay); } *(damtype + *ndam) = DT_FIRE; (*ndam)++; } else if (f->id == F_HOT) { if (strlen(f->text)) { *(dam + *ndam) = real_roll(f->text, fordisplay); } else { *(dam + *ndam) = real_roll("1d2", fordisplay); } *(damtype + *ndam) = DT_HEAT; (*ndam)++; } else if (f->id == F_FROZEN) { *(dam + *ndam) = real_roll("1d4", fordisplay); *(damtype + *ndam) = DT_COLD; (*ndam)++; } } return *dam; } // if damflag isn't passed in, it will be taken from the object void getdamrange(object_t *o, flag_t *f, int *min, int *max) { int mindam,maxdam; if (!f) { f = hasflag(o->flags, F_DAM); } if (f) { if (hasflag(o->flags, F_MASTERWORK)) { // 85%-100% mindam = pctof(85,f->val[1]); maxdam = f->val[1]; } else if (hasflag(o->flags, F_SHODDY)) { // 25% - 75% mindam = pctof(25, f->val[1]); maxdam = pctof(75, f->val[1]); } else { // 50%-100% mindam = f->val[1] / 2; maxdam = f->val[1]; } } else { // TODO wepaon does damage based on weight mindam = 0; maxdam = 0; } limit(&mindam, 0, NA); limit(&maxdam, mindam, NA); if (min) *min = mindam; if (max) *max = maxdam; } // roll for damage int getdamroll(object_t *o, lifeform_t *victim, flag_t *damflag) { int dam; int bonusdam = 0; flag_t *f; lifeform_t *owner; owner = o->pile->owner; if (owner && lfhasflag(owner, F_PHANTASM)) { return 0; } if (damflag) { int min,max; getdamrange(o, damflag, &min, &max); dam = rnd(min,max); if (isblessed(o)) { int dam2; // blessed weapons get two rolls, and take the best dam2 = rnd(min,max); if (dam2 > dam) dam = dam2; } else if (iscursed(o)) { int dam2; // cursed weapons get two rolls, and take the worst dam2 = rnd(min,max); if (dam2 < dam) dam = dam2; } } else { // TODO weapon does bashing damage based on weight dam = rnd(1,2); } // modify for bonus sumflags(o->flags, F_BONUS, &bonusdam, NULL, NULL); dam += bonusdam; if (dam < 0) dam = 0; // special effects ? f = hasflag(o->flags, F_BALANCE); if (f) { if (owner && victim) { float ratio; ratio = (float)owner->maxhp / (float)victim->maxhp; if (ratio >= 1.25) { // heals instead! dam = -dam; } else if (ratio <= 0.75) { // extra dam! dam = (int) ((float)dam * ratio); } } } if (victim) { if (hasbleedinginjury(victim, BP_BODY)) { if (willbleedfrom(victim, BP_BODY)) { // extra damage dam += rnd(1,2); } } } if (o->pile->owner) { int pct; sumflags(o->pile->owner->flags, F_MELEEDAMPCT, &pct, NULL, NULL); if (pct != 0) { dam = pctof(pct, dam); } } return dam; } // returns amt of hp to modify damage by. (in range -3 to 3) int getstrdammod(lifeform_t *lf) { int mod = 0; int base; base = getattr(lf, A_STR); if ((base >= 45) && (base <= 60)) { mod = 0; } else if (base > 60) { base -= 60; // ie. 0 - 40 mod = base / 13; } else { // ie. 0 through 44 base = 45 - base; mod = base / 13; } return mod; } // ie. caused by hitting something with a melee weapon int ismeleedam(enum DAMTYPE damtype) { switch (damtype) { case DT_PIERCE: case DT_SLASH: case DT_BASH: case DT_BITE: case DT_CHOP: case DT_PROJECTILE: case DT_UNARMED: case DT_CRUSH: return B_TRUE; default: break; } return B_FALSE; } int isphysicaldam(enum DAMTYPE damtype) { switch (damtype) { case DT_BASH: case DT_BITE: case DT_CHOP: case DT_COLD: case DT_CRUSH: case DT_ELECTRIC: case DT_EXPLOSIVE: case DT_FALL: case DT_FIRE: case DT_MAGIC: case DT_PIERCE: case DT_PROJECTILE: case DT_SLASH: case DT_UNARMED: return B_TRUE; default: break; } return B_FALSE; } // 'howmuch' is the amount to adjust 'val' by for every size bracket // difference. // // how can be M_PCT (adjust by this val% per size bracket) // how can be M_VAL (adjust by this number per size bracket) // // if lf is bigger than victim, ADD howmuch. // if lf is smaller than victim, SUBTRACT howmuch. void modifyforsize(int *val, lifeform_t *lf, lifeform_t *victim, int howmuch, enum MODTYPE how) { enum LFSIZE szlf,szvictim; assert(val); szlf = getlfsize(lf); szvictim = getlfsize(victim); if (szvictim < szlf) { // if defender is smaller... if (how == M_VAL) { // +howmuch per size difference *val += (howmuch * (szlf - szvictim)); } else { // +(howmuch*sizediff)% of original value *val += (pctof(howmuch * (szlf - szvictim), *val)); } } else if (szvictim > szlf) { // if defender is bigger... if (how == M_VAL) { // -howmuch per size difference *val -= (howmuch * (szvictim - szlf)); } else { // +(howmuch*sizediff)% of original value *val -= (pctof(howmuch * (szlf - szvictim), *val)); } } } // returns true if we hit. also sets 'critical' if passed int rolltohit(lifeform_t *lf, lifeform_t *victim, object_t *wep, int *critical, int *fumble) { int acc,baseacc,ev; int gothit = B_FALSE; enum SKILLLEVEL lorelev = PR_INEPT; flag_t *f; // default if (critical) *critical = 0; if (fumble) *fumble = B_FALSE; // anticipate action spell? if (lfhasflagval(victim, F_ANTICIPATE, lf->id, NA, NA, NULL)) { return B_FALSE; } // remember lore about victim... lorelev = getlorelevel(lf, victim->race->raceclass->id); f = lfhasflag(lf, F_TRUESTRIKE); if (f) { if (f->val[0] > 1) { f->val[0]--; } else { killflag(f); } gothit = B_TRUE; } else if (critical && *critical) { // forced critical? gothit = B_TRUE; } else { int reachpenalty = 0; // actually roll... baseacc = getlfaccuracy(lf, wep); acc = baseacc; // size difference (penalty for attacking smaller ones) modifyforsize(&acc, lf, victim, -5, M_VAL); // easier to hit victims who are prone. if (isprone(victim)) { acc += 30; } if (lfhasflag(lf, F_AIMEDSTRIKE)) { acc -= 40; } // easier to hit things which have grabbed you if (lfhasflagval(lf, F_GRABBEDBY, victim->id, NA, NA, NULL)) { acc += 30; } if (!canreach(lf, victim, &reachpenalty)) { acc -= (10*reachpenalty); } // modify for defender's evasion if (isprone(victim) || !cansee(victim, lf)) { ev = 0; } else { ev = getevasion(victim); } acc -= ev; // modify if we can't see the victim if (!cansee(lf, victim)) acc -= 30; // metal weapon versus magnetic shield? if (lfhasflag(victim, F_MAGSHIELD) && ismetal(wep->material->id)) acc -= 45; // victim immobile or asleep? if (isimmobile(victim) || lfhasflag(victim, F_EATING)) acc += 50; // modify for lore level if (lorelev != PR_INEPT) acc += (lorelev*10); // modify for attacking while climbing if (isclimbing(lf) && !lfhasflag(lf, F_SPIDERCLIMB)) { switch (getskill(lf, SK_CLIMBING)) { case PR_INEPT: acc -= 50; break; case PR_NOVICE: acc -= 50; break; case PR_BEGINNER: acc -= 30; break; case PR_ADEPT: acc -= 20; break; case PR_SKILLED: acc -= 10; break; case PR_EXPERT: acc -= 5; break; default: case PR_MASTER: break; } } limit(&acc, 0, 100); //if (aidb) dblog(".oO { my modified chance to hit is %d %% }", acc); if (pctchance(acc)) gothit = B_TRUE; } // critical chance if (critical) { if (gothit) { if (lfhasflag(lf, F_AIMEDSTRIKE)) { *critical = 1; } else { int critroll; critroll = rnd(1,100); // modify for lore level > pr_novice if (lorelev > PR_NOVICE) { int lorebonus; lorebonus = ((lorelev-1)*5); // ie. up to 25% bonus critroll -= lorebonus; } limit(&critroll, 1, 100); if (critroll <= getcritchance(lf, wep,victim)) *critical = 1; } } } if (fumble && !gothit) { if ((baseacc <= 60) || !getweaponskill(lf, wep)) { int nfailed = 0, i; int nrolls = 1; // chance to fumble // if you miss, make more attack rolls. if you fail // all of them, you fumble.. for (i = 0; i < nrolls; i++) { if (!pctchance(acc)) nfailed++; } if (nfailed >= nrolls) { *fumble = B_TRUE; } } } return gothit; } void wepeffects(flagpile_t *fp, cell_t *where, flag_t *damflag, int dam) { flag_t *f; lifeform_t *victim; lifeform_t *owner = NULL; object_t *wep; int i; flag_t *retflag[MAXCANDIDATES]; int nretflags = 0; char frombuf[BUFLEN]; if (!where) return; wep = fp->ob; if (wep) { cell_t *c; c = getoblocation(wep); if (c && c->lf) { owner = c->lf; } } else { owner = fp->owner; } victim = where->lf; // calculate 'frombuf' string strcpy(frombuf, "something unknown"); if (wep) { if (owner) { char lfname[BUFLEN]; char wepname[BUFLEN]; getlfnamea(owner, lfname); getobname(wep, wepname, 1); // ie. "a goblin's poisoned short sword" snprintf(frombuf, BUFLEN, "%s%s %s",lfname,getpossessive(lfname), wepname); } else { char wepname[BUFLEN]; getobname(wep, wepname, 1); // ie "a poisoned short sword" snprintf(frombuf, BUFLEN, "%s", wepname); } } getflags(fp, retflag, &nretflags, F_FLAMESTRIKE, F_HEAVYBLOW, F_HITCONFER, F_RACESLAY, F_REVENGE, F_RUSTED, F_NONE); for (i = 0; i < nretflags; i++) { f = retflag[i]; if (f->id == F_FLAMESTRIKE) { if (!hasob(where->obpile, OT_FIRESMALL)) { // ignite! addobfast(where->obpile, OT_FIRESMALL); // announce if (haslos(player, where)) { msg("^wA burst of fire erupts from the ground!"); f->known = B_KNOWN; } } } else if ((f->id == F_RACESLAY) && victim && !isdead(victim) && (getraceclass(victim) == f->val[0])) { char ownername[BUFLEN]; char wepname[BUFLEN]; char damstring[BUFLEN]; if (haslos(player, where)) { char vname[BUFLEN]; getlfname(victim, vname); msg("^wA pulse of lethal power blasts %s!", vname); f->known = B_KNOWN; } real_getlfname(owner, ownername, NULL, B_SHOWALL, B_REALRACE); getobname(wep, wepname, 1); snprintf(damstring, BUFLEN, "%s%s %s",ownername, getpossessive(ownername), wepname); losehp(victim, dam*3, DT_DIRECT, owner, damstring); } else if ((f->id == F_REVENGE) && victim && !isdead(victim)) { if (dam) { // only works if we did damage lifeform_t *owner; owner = wep->pile->owner; if (owner && victim) { float ratio; float dampct; int maxdam; int extradam; // figure out hp percentage ratio = 1.0 - ((float)owner->hp / (float)owner->maxhp); dampct = (ratio * 100); // ie. lower hp% = higher dampct if (dampct >= 50) { getdamrange(wep, NULL, NULL, &maxdam); extradam = (int)((dampct/100) * (float)maxdam); if (extradam > 0) { char buf[BUFLEN]; char buf2[BUFLEN]; char obname[BUFLEN]; char damstring[BUFLEN]; char victimname[BUFLEN]; getlfname(owner, buf); real_getlfname(owner, buf2, NULL, B_SHOWALL, B_REALRACE); getlfname(victim, victimname); getobname(wep, obname, 1); // announce if (isplayer(owner)) { msg("^wYour %s blasts %s!",noprefix(obname),victimname); f->known = B_TRUE; } else if (cansee(player, owner)) { msg("^w%s%s %s blasts %s!",buf, getpossessive(buf),noprefix(obname),victimname); f->known = B_TRUE; } snprintf(damstring, BUFLEN, "%s%s blast of revenge",buf2, getpossessive(buf2)); losehp(victim, extradam, DT_DIRECT, owner, damstring); } } // end if dampct > 50 } } } else if ((f->id == F_HEAVYBLOW) && victim && owner) { int dir; int chance; // lifeform flag works all the time. object flag only works sometimes. if (wep) chance = 33; else chance = 100; if (pctchance(chance)) { // knock back victim dir = getdirtowards(owner->cell, victim->cell, victim, B_FALSE, DT_COMPASS); knockback(victim, dir , 2, owner, 30, B_TRUE); if (cansee(player, owner)) { f->known = B_TRUE; } } } else if ((f->id == F_HITCONFER) && victim ) { // only works if we did damage enum FLAG fid; int howlong; char *ftext; flag_t *valflag = NULL; fid = f->val[0]; ftext = f->text; // the f_poisoned flag stacks, others don't. if (!lfhasflag(victim, fid) || (fid == F_POISONED)) { int passedcheck = B_FALSE; // do they get a saving throw? if (f->val[1] != NA) { int scdiff; if (f->val[2] == NA) { scdiff = 30; // default } else { scdiff = f->val[2]; } if (skillcheck(victim, f->val[1], scdiff, 0)) { passedcheck = B_TRUE; } } if (!passedcheck) { howlong = gethitconferlifetime(f->text, NULL, NULL); // get conferred flag values valflag = hasflag(f->pile, F_HITCONFERVALS); if (fid == F_POISONED) { // need to fill in the name of what poisoned us enum POISONTYPE ptype; int ppower; if (valflag) { ptype = valflag->val[0]; if (valflag->val[1] == NA) { ppower = 1; } else { ppower = valflag->val[1]; } } else { // should never happen. ptype = P_VENOM; ppower = 1; } if (!wep && strlen(ftext)) { strcpy(frombuf, ftext); } poison(victim, howlong, ptype, ppower, frombuf, owner ? owner->race->id : R_NONE); } else { // flag values if (valflag) { addtempflag(victim->flags, fid, valflag->val[0], valflag->val[1], valflag->val[2], valflag->text, howlong); } else { addtempflag(victim->flags, fid, NA, NA, NA, NULL, howlong); } } } // end if passedcheck } // end (if victim doesn't already have the flag) // was this from a poisoned weapon? if so the poison vanishes if ((f->val[0] == F_POISONED) && (f->lifetime == FROMOBMOD)) { killflag(f); if (owner && isplayer(owner)) { addflag(owner->flags, F_USEDPOISON, B_TRUE, NA, NA, NULL); } } } else if ((f->id == F_RUSTED) && victim ) { int pct; pct = f->val[0] * 10; if (pctchance(pct)) { poison(victim, PERMENANT, P_TETANUS, 1, frombuf, owner ? owner->race->id : R_NONE); } } // end if (fid == xxx) } if (wep && owner && victim) { enum DRAINTYPE draintype = DR_NONE; if ((wep->type->id == OT_TEETH) && lfhasflag(owner, F_VAMPIRIC) && islowhp(victim)) { draintype = DR_FROMBITE; } else if (hasflag(wep->flags, F_VAMPIRIC)) { draintype = DR_FROMWEP; } if (draintype && !isimmuneto(victim->flags, DT_NECROTIC, B_FALSE)) { int hpgain; // drain life! if (isresistantto(victim->flags, DT_NECROTIC, B_FALSE)) { hpgain = dam; } else { hpgain = (dam/2); } if (hpgain && (owner->hp < owner->maxhp)) { gainhp(owner, hpgain); if (draintype == DR_FROMBITE) { if (isplayer(owner)) { char lfname[BUFLEN]; char victimname[BUFLEN]; getlfname(owner,lfname); getlfname(victim, victimname); msg("You suck %s%s blood!", victimname, getpossessive(victimname)); } else if (cansee(player, owner)) { char lfname[BUFLEN]; char victimname[BUFLEN]; getlfname(owner,lfname); getlfname(victim, victimname); msg("%s sucks %s%s blood!", lfname, victimname, getpossessive(victimname)); } } else { if (isplayer(owner)) { msg("Life force surges into you!"); } } } } } }