#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 "spell.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 = hasequippedobid(lf->pack, OT_FLAKJACKET); if (o) { 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 = 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,attacker); } } 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 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; enum SKILLLEVEL slev; int dostamloss = B_TRUE; // 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); stoppathfinding(lf); // anyone there? if so just attack. if (c->lf) { // warnings if (!force && isplayer(lf)) { int h,m,s; splittime(&h,&m,&s); char warnbuf[BUFLEN]; char ch; char victimname[BUFLEN]; getlfname(c->lf, victimname); strcpy(warnbuf, ""); if (godprayedto(R_GODLIFE) && (h == 6) && !lfhasflag(lf, F_STRIKETOKO)) { if (!warnabout("Really attack during Glorana's Peace?")) { return B_TRUE; } } 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) ) { switch (getallegiance(c->lf)) { case AL_PEACEFUL: if ((int)getlorelevel(lf, (int)getraceclass(c->lf) >= PR_NOVICE) || getskill(lf, SK_SPEECH)) { // need this to KNOW whether they're peaceful snprintf(warnbuf, BUFLEN, "Really attack the peaceful %s?",noprefix(victimname)); } break; case AL_FRIENDLY: snprintf(warnbuf, BUFLEN, "Really attack the allied %s?",noprefix(victimname)); break; default: snprintf(warnbuf, BUFLEN, "Really attack the allied %s?",noprefix(victimname)); break; } if (strlen(warnbuf)) { ch = askchar(warnbuf, "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(player) != 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); } } else if ((c->lf->race->raceclass->id == RC_PLANT) && (c->map->region->rtype->id == BH_WOODS)) { int willwarn = B_FALSE; if (lfhasflag(lf, F_SYLVANWARN)) { willwarn = B_TRUE; } else if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_GTAVERAGE) { willwarn = B_TRUE; } if (willwarn) { snprintf(warnbuf, BUFLEN, "Really attack %s while in the Sylvan Woods?",victimname); ch = askchar(warnbuf, "yn","n", B_TRUE, B_FALSE); if (ch == 'n') { // cancel. return B_TRUE; } } } // average wisdom will prevent 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, noprefix(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)) { if (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 if ((o->type->obclass->id == OC_FLORA) && (c->map->region->rtype->id == BH_WOODS)) { int willwarn = B_FALSE; char obname[BUFLEN]; real_getobname(o, obname, o->amt, B_NOPREMODS, B_NOCONDITION, B_BLINDADJUST, B_NOBLESSINGS, B_NOUSED, B_NOSHOWALL); if (lfhasflag(lf, F_SYLVANWARN)) { willwarn = B_TRUE; } else if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_GTAVERAGE) { willwarn = B_TRUE; } if (willwarn) { char ch; char buf[BUFLEN]; snprintf(buf, BUFLEN, "Really attack %s while in the Sylvan Woods?",obname); ch = askchar(buf, "yn","n", B_TRUE, B_FALSE); if (ch == 'n') { // cancel. return B_TRUE; } } } } } else { // no impassable objects here 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 { // no objects here to attack if (!lfhasflag(lf, F_HURRICANESTRIKE)) { if (c->type->solid) { // attacking a wall attacktype = AT_WALL; attacktarget = c; if ((c->type->id == CT_WALLTREE) && (c->map->region->rtype->id == BH_WOODS)) { int willwarn = B_FALSE; if (lfhasflag(lf, F_SYLVANWARN)) { willwarn = B_TRUE; } else if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_GTAVERAGE) { willwarn = B_TRUE; } if (willwarn) { char ch; char buf[BUFLEN]; snprintf(buf, BUFLEN, "Really attack %s while in the Sylvan Woods?",c->type->name); ch = askchar(buf, "yn","n", B_TRUE, B_FALSE); if (ch == 'n') { // cancel. return B_TRUE; } } } } 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; } attacktime = getattackspeed(lf); if (nweps <= 0) { if (isplayer(lf)) { msg("You cannot attack!"); } else if (lfhasflag(lf, F_DEBUG)) { msg("DB: %s cannot attack!",lf->race->name); } if (op) killobpile(op); if (!isplayer(lf)) { // avoid infinite loops taketime(lf, getactspeed(lf)); } return B_TRUE; } //maxattacks = nweps; // ie. all maxattacks = getattacks(lf, NULL, NULL); // cope with special monsters with NO innate attacks, but // which still use weapons (eg. dancing weapon). limit(&maxattacks, 1, NA); /* // 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; flag_t *fleeflag; victim = (lifeform_t *)attacktarget; fleeflag = isfleeingfrom(victim, lf); 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; } // no longer adjacent? if (!isadjacent(lf->cell, victim->cell) || isdead(victim)) { break; } // vicitm started to flee? if (isplayer(lf)) { if (!fleeflag && isfleeingfrom(victim, lf)) { if ( (i+1 < nweps) && (attacksdone+1 < maxattacks)) { char ch,ques[BUFLEN]; char vname[BUFLEN]; getlfname(victim, vname); sprintf(ques, "Continue attacking the fleeing %s?", noprefix(vname)); ch = askchar(ques, "yn","y", B_TRUE, B_FALSE); if (ch == 'n') { 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, attacktarget); } 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 or map effects... if (isplayer(lf) && attacktarget) { int angered = B_FALSE; (void) angered; if (attacktype == AT_LF) { if (!isgod(attacktarget)) { int h,m,s; splittime(&h,&m,&s); if (attackedfriend) { if (angergodmaybe(R_GODMERCY, 25, GA_ATTACKALLY)) angered = B_TRUE; if (angergodmaybe(R_GODPURITY, 100, GA_ATTACKALLY)) angered = B_TRUE; switch (getalignment(attacktarget)) { case AL_GOOD: if (angergodmaybe(R_GODPURITY, 20, GA_ATTACKALLY)) angered = B_TRUE; break; default: break; } } else if (attackedpeaceful) { if (getraceclass(attacktarget) == RC_ANIMAL) { // attacking helpless animals is fine } else { if (angergodmaybe(R_GODMERCY, 15, GA_ASSAULT)) angered = B_TRUE; if (angergodmaybe(R_GODLIFE, 15, GA_ASSAULT)) angered = B_TRUE; if (getalignment(attacktarget) == AL_GOOD ) { if (angergodmaybe(R_GODPURITY, 70, GA_ASSAULT)) angered = B_TRUE; // even more } else { if (angergodmaybe(R_GODPURITY, 50, GA_ASSAULT)) angered = B_TRUE; } } } else if (attackedhelpless && isplayer(lf)) { if (getraceclass(attacktarget) == RC_ANIMAL) { // attacking helpless animals is fine } else { if (angergodmaybe(R_GODMERCY, 15, GA_ATTACKHELPLESS)) angered = B_TRUE; } if (getalignment(attacktarget) != AL_EVIL) { if (angergodmaybe(R_GODPURITY, 50, GA_ATTACKHELPLESS)) angered = B_TRUE; pleasegodmaybe(R_GODTHIEVES, 5); pleasegodmaybe(R_GODDEATH, 10); } } if ( ((lifeform_t *)attacktarget)->race->raceclass->id == RC_PLANT) { lifeform_t *ll; ll = (lifeform_t *)attacktarget; if (angergodmaybe(R_GODNATURE, 25, GA_ATTACKOBJECT)) angered = B_TRUE; if ((ll->race->raceclass->id == RC_PLANT) && !isdeaf(ll) && (lf->cell->map->region->rtype->id == BH_WOODS)) { magicwoods_warn(lf); } } if (lfhasflag(lf, F_USEDPOISON)) { killflagsofid(lf->flags, F_USEDPOISON); if (isplayer(lf)) god_usepoison_response(); } if (godprayedto(R_GODLIFE) && (h == 6) && !lfhasflag(lf, F_STRIKETOKO)) { if (angergodmaybe(R_GODLIFE, 30, GA_PEACEHOUR)) angered = B_TRUE; } } } else if (attacktype == AT_OB) { object_t *oo; if (angergodmaybe(R_GODNATURE, 10, GA_ATTACKOBJECT)) angered = B_TRUE; oo = (object_t *)attacktarget; if (oo && !hasflag(oo->flags, F_DEAD) && (oo->type->obclass->id == OC_FLORA)) { if (lf->cell->map->region->rtype->id == BH_WOODS) { magicwoods_warn(lf); } } } } dostamloss = B_TRUE; // default slev = getskill(lf, SK_COMBAT); if (slev == PR_MASTER) { dostamloss = B_FALSE; } else if (lfhasflagval(lf, F_LASTATTACKHIT, B_FALSE, NA, NA, NULL) && (getskill(lf, SK_ATHLETICS) >= PR_BEGINNER)) { // missed, and we have balance via athletics skill dostamloss = B_FALSE; } else if (pctchance(slev * 10)) { dostamloss = B_FALSE; } if (dostamloss) { object_t *priwep; float loss; priwep = getweapon(lf); loss = getattackstamlosslf(lf, priwep); // lose a bit of stamina modstamina(lf, -loss); } // stop sprinting stopsprinting(lf); return B_FALSE; } int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag) { int dam[MAX_HITS]; enum DAMTYPE damtype[MAX_HITS]; 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; int missby = 0; // init for (i = 0; i < MAX_HITS; i++) { dam[i] = 0; damtype[i] = DT_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)) { int lftr,victr; int protected = B_FALSE; lftr = gettr(lf); victr = gettr(victim); if (lftr > victr) { protected = B_TRUE; } else { // same level = 50% chance of protection. // for each level that victim is higher, -10%. if (pctchance(50 - (victr - lftr))) protected = B_TRUE; } if (protected) { 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); } if (f->val[0] != PERMENANT) { 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, &missby); if (lfhasflag(victim, F_HEAVENARM)) { critical = B_FALSE; } else if (isexhausted(lf)) { critical = B_FALSE; } 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 ((int)critpos == BP_NONE) { strcpy(victimbpname, victimname); } else { armour = getouterequippedob(victim, 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 (isunarmed) { object_t *gloves; gloves = getouterequippedob(lf, BP_HANDS); if (gloves && hasflag(gloves->flags, F_HARDNESS)) { dam[0]++; } } 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]); // half damage if exhausted if (isexhausted(lf)) { dam[0] = pctof(75, 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(lf, &(dam[0]), victim, wep->blessed); // modify for weapon skill, strength, rings of wounding etc applylfdammod(&dam[0], lf, wep); // modify for size modifyforsize(&dam[0], lf, victim, 10, M_PCT); // gods like certain material weapons if (wep && isplayer(lf)) { int matbonus = 0; if ((wep->material->id == MT_BONE) && godprayedto(R_GODDEATH)) { matbonus = rnd(1,2); } else if ((wep->material->id == MT_WOOD) && godprayedto(R_GODNATURE)) { matbonus = rnd(1,2); } dam[0] += matbonus; } // 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; } else if (lfhasflag(victim, F_ASLEEP)) { // target 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 ); // extra damage to fire-based lifeforms if they are cold. if (lf->material->id == MT_FIRE) { enum TEMPERATURE temp; temp = getlftemp(lf); switch (temp) { case T_CHILLY: dam[0] = pctof(125, dam[0]); break; case T_COLD: dam[0] = pctof(160, dam[0]); break; case T_VCOLD: dam[0] = pctof(200, dam[0]); break; default: break; } } } 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; // hit! killflagsofid(lf->flags, F_LASTATTACKHIT); addflag(lf->flags, F_LASTATTACKHIT, B_TRUE, NA, NA, NULL); 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; } snprintf(attackname, BUFLEN, "%s%s attack", attackername, getpossessive(attackername)); difficulty = 100 + (gettr(victim)*5) - (gettr(lf)*5); if (check_for_block(lf, victim, dam[i], damtype[i], difficulty, attackname, isadjacent(lf->cell,victim->cell))) { 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. // // normally armour would be handled by losehp() & losehpeffects(), but // in this case we need to know whether the armour was hit beforehand, // in order to construct the "you hit xxx" string. if ((dam[i] > 0) && !backstab && !critical) { damreducedbyarmour = handlearmour(victim, wep, &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 (getstamina(victim)) { if (isplayer(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, BP_NONE, B_NOCRIT); 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, critpos, critical); } // 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") || strstr(buf, "dismember")) { 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 // special case if (!isdead(victim)) { f = lfhasflag(victim, F_ABSORBKINETIC); if (f) { char damstr[BUFLEN]; sprintf(damstr, "%d", f->val[0]); addtempflag(victim->flags, F_EXTRADAM, f->val[1], NA, NA, damstr,f->val[2]); addtempflag(victim->flags, F_HEAVYBLOW, B_TRUE, NA, NA, NULL, 1); if (cansee(player, victim)) { msg("^%c%s looks stronger!", getlfcol(victim, CC_GOOD), victimname); } } } /// ... used to apply armour damage here... // make noise // UNLESS this fighting involved the player. // This is a hack - should really move this check into noise(), and // implement some way to tell whether a lf is currently fighting the player. if (!isplayer(lf) && !isplayer(victim)) { noise(lf->cell, lf, NC_FIGHTING, SV_SHOUT, "fighting.", NULL); } if (isplayer(lf)) { if (backstab) { practice(lf, SK_BACKSTAB, 1); pleasegodmaybe(R_GODTHIEVES, 8); } else if (lfhasflagval(lf, F_UNSEENATTACKER, victim->id, NA, NA, NULL)) { pleasegodmaybe(R_GODTHIEVES, 4); } } // now handle the extra hp loss effects which we postponed above. losehpeffects(victim, dam[i], damtype[i], lf, wep, B_NORETALIATE, waskod, &waskod, prebleed, BP_NONE, damreducedbyarmour, critical); 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); } // special weapon effects, as long as you're not doing a heavy blow if (!lfhasflag(lf, F_HEAVYBLOW) && dam[0]) { // confer flags from weapon wepeffects(wep->flags, victim->cell, damflag, dam[0], isunarmed); // confer flags from attacker themselves wepeffects(lf->flags, victim->cell, damflag, dam[0], isunarmed); } if (!isdead(victim) && !blocked && !dodged) { f = lfhasflag(lf, F_FREEZINGTOUCH); if (f) { int diff; diff = f->val[2]*20; 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]); } // special lifeform-based effects if ((lf->race->id == R_COCKATRICE) && dam[0]) { // first saving throw... if (skillcheck(victim, SC_CON, 75, 0)) { // slowed addtempflag(victim->flags, F_SLOWACTMOVE, 15, NA, NA, NULL, 2); } else { // second saving throw... if (skillcheck(victim, SC_CON, 75, 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->baseid == 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],dicetext[BUFLEN],obname[BUFLEN]; char *loctext,*p; loctext = strdup(f->text); p = readuntil(dicetext, loctext, '^'); readuntil(obname, p, '^'); rdam = roll(dicetext); if (cansee(player, victim)) { msg("^%c%s%s %s %s %s!", getlfcol(lf, CC_BAD), victimname, getpossessive(victimname), noprefix(obname), getattackverb(victim, NULL, f->val[0], rdam, lf->maxhp), attackername); } snprintf(damstring, BUFLEN, "%s%s %s", victimname, getpossessive(victimname), noprefix(obname)); losehp_real(lf, rdam, f->val[0], victim, damstring, B_TRUE, NULL, B_TRUE, NULL, B_TRUE, critpos, critical); free(loctext); } } } } else { // miss! if (aidb) dblog(".oO { i missed! }"); killflagsofid(lf->flags, F_LASTATTACKHIT); addflag(lf->flags, F_LASTATTACKHIT, B_FALSE, NA, NA, NULL); // 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 { char missamt[BUFLEN]; flag_t *anticipated = NULL; anticipated = lfhasflagval(victim, F_ANTICIPATE, lf->id, NA, NA, NULL); if (anticipated || (missby >= 50)) { strcpy(missamt, "wildly "); } else if (missby <= 10) { strcpy(missamt, "narrowly "); } else { strcpy(missamt, ""); } if (isplayer(lf)) { msg("You %smiss %s.", missamt, victimname); } else { if (cansee(player, lf)) { // capitalise first letter snprintf(buf, BUFLEN, "%s",attackername); msg("%s %smisses %s.", buf, missamt, victimname); } } if (canevade(victim, lf)) { // 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!", getlfcol(lf, CC_BAD), attackername); } drop(wep, ALL); wep = NULL; } } // high chance that ai will give up if we can't reach the victim if (!isplayer(lf) && !canreach(lf, victim, NULL)) { if (pctchance(80)) { loseaitargets(lf); if (isplayer(victim) && cansee(player, lf)) { //msg("%s seems to have lost interest in you.", attackername); msg("%s can't reach you!", attackername); } } } // anticipated spells lose power if (anticipated) { if (--anticipated->val[1] <= 0) { killflag(anticipated); anticipated = NULL; } } } } // 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); if (isdoor(o, NULL)) { // extra damage for engineering skill dam[0] += getengineeringwallmod(lf); } 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 = getouterequippedob(lf, 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], lf); 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], isunarmed); } } 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 makedullermaybe(wep, 1); } } } // still not dead? more checks if (!isdeadob(o)) { object_t *oo; f = hasflag(o->flags, F_TRAPPED); if (f && pctchance(75)) { triggerattachedtraps(o, lf, B_DOANNOUNCE); } else { // if a trap didn't go off, you might break the lock f = hasflag(o->flags, F_LOCKED); if (f && (damtype[0] == DT_BASH)) { int difficulty; int unlockit = B_FALSE; difficulty = f->val[1]; 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); } } } // objects inside might smash if (damtype[0] == DT_BASH) { for (oo = o->contents->first ;oo ; oo = oo->next) { if (willshatter(oo->material->id) && onein(2)) { if (isplayer(lf)) { // since the sound won't work. msg("You hear shattering glass from inside %s.", obname); } // damstring should never be used... shatter(oo, B_FALSE, "shattering damage", lf); } else { int obdam; obdam = dam[0] / 2; limit(&obdam, 1, NA); takedamage(oo, obdam, DT_BASH, NULL); } } } } 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); // extra damage for engineering skill dam[0] += getengineeringwallmod(lf); 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 = getouterequippedob(lf, 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], lf); // 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 makedullermaybe(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 // // Note: sometimes we'll call this function with a check difficulty of IMPOSSIBLE. // This means that it'sonly possible to block the attack if you are in // 'fullshield' mode. int check_for_block(lifeform_t *lf, lifeform_t *victim, int dam, enum DAMTYPE damtype, int difficulty, char *attackname, int ranged) { object_t *shield[MAXPILEOBS]; int checkmod[MAXPILEOBS]; int nshields,i; flag_t *fflag; long shid; fflag = lfhasflag(victim, F_FULLSHIELD); if (fflag) { shid = atol(fflag->text); } else { shid = -1; } if (lf && !cansee(victim, lf)) { // in fullblock mode, you can block even if you can't see // your attacker. if (!fflag) { return B_FALSE; } } if (lfhasflag(victim, F_STUNNED) || !hasfreeaction(victim)) { return B_FALSE; } // need stamina to block if (!getstamina(victim)) return B_FALSE; // get all usable shields for this damtype getallshields(victim, damtype, shield, checkmod, &nshields); for (i = 0; i < nshields; i++) { int blocked = B_FALSE; // did f_fullblock skill work? if (fflag && ranged && (shield[i]->id == shid)) { int fullchance; fullchance = 40 + (getobsize(shield[i])*5); if (pctchance(fullchance)) { blocked = B_TRUE; } } // did we block with this object? if (!blocked && skillcheck(victim, SC_SHIELDBLOCK, difficulty, checkmod[i])) { blocked = B_TRUE; } if (blocked) { 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, lf); practice(victim, SK_SHIELDS, 1); } if (!isdeadob(shield[i])) { if (lf) { 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 ((int)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 { snprintf(dambuf, BUFLEN, "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); msg("%s%s blow spins %s around!", lfname, getpossessive(lfname),victimname); } } else { if (isplayer(victim) || cansee(player, victim)) { getlfname(victim, victimname); msg("%s is spun around!", victimname); } } // actually spin them setfacing(victim, getrandomdirexcept(DT_COMPASS, victim->facing)); breakgrabs(victim, B_TRUE, B_TRUE, B_TRUE); break; } } if ((armour = getarmour(victim, BP_BODY)) != NULL) { protected = checkcritprotection(victim,armour); takedamage(armour, dam, damtype, lf); } 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, lf); } } 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, lf); } 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, lf); } 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, lf); } 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, lf); } 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; if (wep && hasflag(wep->flags, F_ARMOURIGNORE)) { return 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 = hasequippedobid(lf->pack, OT_FLAKJACKET); if (o) { // stop ALL missile damage reduceamt = dam; } } if (wep && reduceamt) { flag_t *pierce = NULL; // and if weapon is armour piercing, you always take at least its // armourpiercing valut. pierce = hasflag(wep->flags, F_ARMOURPIERCE); if (pierce) { if (pierce->val[0] < 0) reduceamt = 0; else reduceamt -= pierce->val[0]; } } limit(&reduceamt, 0, dam); 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(50,arating); *max = pctof(80, 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 of same damtype goes onto INITIAL dam[], rather than // adding a new one. getflags(lf->flags, retflag, &nretflags, F_EXTRADAM, F_NONE); for (i = 0; i < nretflags; i++) { int *damwhere; int *damtypewhere; int doinc = B_FALSE; int amtextra = 0; f = retflag[i]; if (f->val[2] == NA) { amtextra = real_roll(f->text, wantmax); // addition } else { amtextra = f->val[2]; } if ((f->val[0] == NA) || (f->val[0] == *damtype)) { // addition to the first one damwhere = dam; damtypewhere = damtype; *(damwhere) += amtextra; // addition } else { // add a new damtype damwhere = (dam + *ndam); damtypewhere = (damtype + *ndam); doinc = B_TRUE; *(damwhere) = amtextra; // 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 ((f->val[2] == IFACTIVE) && !isactivated(wep) ) { charges = 0; } if (charges > 0) { if (!fordisplay) { // use up a charge usecharge(wep); } // deal extra damage of the given type *(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 / 12); } return mod; } int armourcanstopdam(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_ACID: case DT_EXPLOSIVE: case DT_MAGIC: case DT_CRUSH: return B_TRUE; default: break; } return B_FALSE; } // 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 iskineticdam(enum DAMTYPE damtype) { switch (damtype) { case DT_BASH: case DT_BITE: case DT_CHOP: case DT_CRUSH: case DT_EXPLOSIVE: case DT_FALL: case DT_PIERCE: case DT_PROJECTILE: case DT_SLASH: case DT_UNARMED: 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 *missby) { int acc,baseacc,ev; int gothit = B_FALSE; enum SKILLLEVEL lorelev = PR_INEPT; flag_t *f; int db = B_FALSE; char lfname[BUFLEN]; char vicname[BUFLEN]; int critpossible = B_TRUE; if (lfhasflag(lf, F_DEBUG)) db = B_TRUE; real_getlfname(lf, lfname, NULL, B_SHOWALL, B_CURRACE); real_getlfname(victim, vicname, NULL, B_SHOWALL, B_CURRACE); if (db) dblog("%s: rolling to hit %s", lfname, vicname); // default if (critical) *critical = 0; if (fumble) *fumble = B_FALSE; if (missby) *missby = 0; if (lf && lfhasflag(lf, F_NOGIVECRITS)) { critpossible = B_FALSE; } else if (victim && lfhasflag(victim, F_NOTAKECRITS)) { critpossible = B_FALSE; } // anticipate action spell? if (lfhasflagval(victim, F_ANTICIPATE, lf->id, NA, NA, NULL)) { if (db) dblog("%s: victim has anticipate action - MISS.", lfname); 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; if (db) dblog("%s: we have truestrike - HIT.", lfname); } else if (critical && *critical && critpossible) { // critical already set = forced critical gothit = B_TRUE; if (db) dblog("%s: pre-determined critical - HIT.", lfname); } else { int myroll; int reachpenalty = 0; int vicheight = 0; // actually roll... baseacc = getlfaccuracy(lf, wep); acc = baseacc; if (db) dblog("%s: base accuracy: %d", lfname, baseacc); // size difference (penalty for attacking smaller ones) modifyforsize(&acc, lf, victim, -5, M_VAL); if (db) dblog("%s: modified for victim size -> %d", lfname, acc); // easier to hit victims who are prone. if (isprone(victim)) { acc += 30; if (db) dblog("%s: +30 for prone victim -> %d", lfname, acc); } if (lfhasflag(lf, F_AIMEDSTRIKE)) { acc -= 20; if (db) dblog("%s: -20 for aimed strike -> %d", lfname, acc); } // easier to hit things which have grabbed you if (lfhasflagval(lf, F_GRABBEDBY, victim->id, NA, NA, NULL)) { acc += 30; if (db) dblog("%s: +30 as victim is holding us -> %d", lfname, acc); } // MUCH easier to hit things which you have grabbed if (lfhasflagval(lf, F_GRABBING, victim->id, NA, NA, NULL)) { acc += 50; if (db) dblog("%s: +50 as we are holding victim -> %d", lfname, acc); } if (wep && (getcelldist(lf->cell, victim->cell) <= 1)) { f = hasflag(wep->flags, F_ADJACCMOD); if (f) { acc += f->val[0]; if (db) dblog("%s: %s%d for weapon adjacency modifier -> %d", lfname, (f->val[0] < 0) ? "" : "+", f->val[0], acc); } } if (!canreach(lf, victim, &reachpenalty)) { acc -= (10*reachpenalty); if (db) dblog("%s: -%d reach penalty -> %d", lfname, (10*reachpenalty), acc); } // modify for defender's evasion if (canevade(victim, lf)) { ev = getevasion(victim); } else { ev = 0; } acc -= ev; if (db) dblog("%s: minus victim's modified evasion (%d) -> %d", lfname, ev, acc); // modify if victim is flying and we're not if (isairborne(victim,&vicheight)) { if (!isairborne(lf, NULL)) { acc -= (5 * vicheight); } } // modify if we can't see the victim if (!cansee(lf, victim)) { acc -= 30; if (db) dblog("%s: -30 for not being able to see victim -> %d", lfname, acc); } // metal weapon versus magnetic shield? if (lfhasflag(victim, F_MAGSHIELD) && ismetal(wep->material->id)) { acc -= 45; if (db) dblog("%s: -45 for metal weapon vs magshield -> %d", lfname, acc); } // victim immobile or asleep? if (isimmobile(victim) || lfhasflag(victim, F_EATING)) { acc += 50; if (db) dblog("%s: +50 for immobile victim -> %d", lfname, acc); } // modify for lore level if (lorelev != PR_INEPT) { acc += (lorelev*10); if (db) dblog("%s: +%d for knowledge about victim's race -> %d", lfname, lorelev*10, acc); } // 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; } if (db) dblog("%s: modified for attacking while climbing -> %d", lfname, acc); } limit(&acc, 0, 100); if (db) dblog("%s: FINAL ACCURACY: %d%%", lfname, acc); if (db) { msg("%s vs. %s - %d%% hit chance", lfname, vicname, acc); } //if (aidb) dblog(".oO { my modified chance to hit is %d %% }", acc); myroll = rnd(1,100); if (myroll <= acc) { gothit = B_TRUE; } else { gothit = B_FALSE; if (missby) *missby = myroll - acc; } } // critical chance if (critical && gothit && critpossible) { if (lfhasflag(lf, F_AIMEDSTRIKE)) { *critical = 1; } else { int critroll; int minroll = 1; 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; } if (isplayer(lf)) { f = lfhasflag(lf, F_MINCRITCHANCE); if (f) { minroll = f->val[0]; } } limit(&critroll, minroll, 100); if (critroll <= getcritchance(lf, wep,victim)) *critical = 1; } } if (fumble && !gothit) { if (isexhausted(lf) || (baseacc <= 60) || !getweaponskill(lf, wep)) { int nfailed = 0, i; int nrolls = 1; int newacc; if (getskill(lf, SK_COMBAT) >= PR_BEGINNER) { newacc = pctof(150, baseacc); } else { newacc = baseacc; } // 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(newacc)) nfailed++; } if (nfailed >= nrolls) { *fumble = B_TRUE; if (missby) *missby = 100; } } } return gothit; } void wepeffects(flagpile_t *fp, cell_t *where, flag_t *damflag, int dam, int isunarmed) { 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 && isdeadob(wep)) wep = NULL; 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); } } else { if (owner) { real_getlfname(owner, frombuf, NULL, B_SHOWALL, B_REALRACE); } else { strcpy(frombuf, "something unknown"); } } getflags(fp, retflag, &nretflags, F_AUTOTANGLE, F_DRAINONHIT, F_DRAINATTONHIT, 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_AUTOTANGLE) && victim && !isdead(victim)) { if (pctchance(f->val[0]) && !hasob(where->obpile, OT_VINE)) { dospelleffects(owner, OT_S_ENTANGLE, f->val[1], victim, NULL, where, B_UNCURSED, NULL, B_FALSE, NULL); if (haslos(player, where)) { f->known = B_KNOWN; } } } else if ((f->id == F_DRAINONHIT) && victim && !isdead(victim)) { if (!leveldrain(victim, f->val[0], f->val[1], f->val[2], owner)) { if (strlen(f->text)) { gainhp(owner, roll(f->text)); if (haslos(player, where)) { f->known = B_KNOWN; } } } } else if ((f->id == F_DRAINATTONHIT) && victim && !isdead(victim)) { int amt; amt = roll(f->text); statdrain(victim, f->val[0], amt, f->val[1], f->val[2], owner); if (haslos(player, where)) { f->known = B_KNOWN; } } else 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, 110, B_DOANNOUNCE, B_DODAM); 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; // hitconfer from owner can't be conferred if this was a weapon attack // ie. if something has poisonous claws and hits you with a weapon, you // don't get poisoned. if (fp->owner && !isunarmed) { continue; } // sometimes we can only hitconfer against certain races if (hasflag(fp, F_HITCONFERRC)) { if (!hasflagval(fp, F_HITCONFERRC, getraceclass(victim), NA, NA, NULL)) { continue; } } if (hasflag(fp, F_HITCONFERDEADONLY)) { if (!isdead(victim)) { continue; } } // 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 = 100; // 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) { // defaults - need to fill in the name of what poisoned us enum POISONTYPE ptype = P_VENOM; int ppower = 1; assert(valflag); ptype = valflag->val[0]; if (valflag->val[1] == NA) { ppower = 1; } else { ppower = valflag->val[1]; } if (!wep && strlen(ftext)) { strcpy(frombuf, ftext); } else if (ptype == P_LYCANTHROPY) { char *p; // special case - we need to remember what kind of // creature to change into. since lycanthropy isn't fatal // as such we can use the 'what caused you damage' field for // this information p = readuntil(frombuf, valflag->text, '^'); readuntil(frombuf, p, '^'); } poison(victim, howlong, ptype, ppower, frombuf, owner ? owner->race->id : R_NONE, B_FALSE); } else if (fid == F_ASLEEP) { fallasleep(victim, ST_ASLEEP, howlong); } 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 && !isdead(victim) ) { int pct; pct = f->val[0] * 10; if (pctchance(pct)) { poison(victim, PERMENANT, P_TETANUS, 1, frombuf, owner ? owner->race->id : R_NONE, B_FALSE); } } // end if (fid == xxx) } if (wep && owner && victim) { enum DRAINTYPE draintype = DR_NONE; flag_t *vampflag; vampflag = lfhasflag(owner, F_VAMPIRIC); if ((wep->type->id == OT_TEETH) && vampflag && ((vampflag->val[0] == B_TRUE) || 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!"); } } } } } }