nexus/attack.c

1339 lines
28 KiB
C
Raw Normal View History

2011-02-01 06:16:13 +11:00
#include <assert.h>
#include <math.h>
2010-12-02 12:17:54 +11:00
#include <stdio.h>
#include <stdlib.h>
2010-12-07 18:34:26 +11:00
#include <string.h>
2010-12-02 12:17:54 +11:00
#include "attack.h"
#include "defs.h"
2010-12-07 18:34:26 +11:00
#include "flag.h"
2011-02-01 06:16:13 +11:00
#include "io.h"
2010-12-07 18:34:26 +11:00
#include "lf.h"
2011-03-04 12:22:36 +11:00
#include "map.h"
#include "move.h"
2011-02-01 06:16:13 +11:00
#include "nexus.h"
2010-12-07 18:34:26 +11:00
#include "objects.h"
2011-02-01 06:16:13 +11:00
#include "text.h"
2010-12-02 12:17:54 +11:00
extern lifeform_t *player;
2011-02-01 06:16:13 +11:00
2011-03-04 12:22:36 +11:00
int applyarmourdamage(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damtype) {
object_t *armour;
int adam;
int damtaken = 0;
if ((damtype == DT_ACID) || hasflag(wep->flags, F_ARMOURPIERCE)) {
// ALL of damage reduction goes towards armour
adam = dam;
} else {
// some of damage reduction goes towards armour
adam = (dam / 2);
}
// pick a random piece of armour
armour = getrandomarmour(lf);
if (armour) {
damtaken = takedamage(armour,adam, damtype);
}
return damtaken;
}
void applyarmourdamreduction(lifeform_t *lf, object_t *wep, int reduceamt, int *dam, enum DAMTYPE damtype) {
int db = B_FALSE;
// armour can't stop armour-piercing weapons
if (hasflag(wep->flags, F_ARMOURPIERCE)) {
reduceamt = 0;
}
if (db) {
if (reduceamt > 0) {
dblog("Armour reduces dam by %d to %d.",reduceamt,dam);
} else {
dblog("No armour dam reduction.");
}
}
if (dam && (reduceamt >= 0)) {
*dam -= reduceamt;
if (*dam < 0) *dam = 0;
}
}
2011-02-01 06:16:13 +11:00
int attackcell(lifeform_t *lf, cell_t *c) {
// anyone there? if so just attack.
if (c->lf) {
attacklf(lf, c->lf);
} else {
object_t *o;
// has an mpassable object?
o = hasobwithflag(c->obpile, F_IMPASSABLE);
if (o) {
attackob(lf, o);
} else {
// TODO: attack wall?
return B_TRUE;
}
}
return B_FALSE;
}
int attacklf(lifeform_t *lf, lifeform_t *victim) {
int dam[100];
2011-02-01 06:16:13 +11:00
enum DAMTYPE damtype[100];
int ndam = 0;
2010-12-02 12:17:54 +11:00
char buf[BUFLEN];
char attackername[BUFLEN];
char victimname[BUFLEN];
int fatal = B_FALSE;
2011-02-01 06:16:13 +11:00
flag_t *unarmedflag = NULL;
2010-12-07 18:34:26 +11:00
object_t *wep;
2011-02-01 06:16:13 +11:00
obpile_t *op = NULL;
2010-12-07 18:34:26 +11:00
int attacktime;
int hit = B_FALSE;
2011-03-04 12:22:36 +11:00
int critical = 0;
2010-12-07 18:34:26 +11:00
char wepname[BUFLEN];
2011-03-04 12:22:36 +11:00
//int acc;
//int ev;
2011-02-01 06:16:13 +11:00
int i;
int willheal = B_FALSE;
2011-02-01 06:16:13 +11:00
int aidb = B_FALSE;
2010-12-02 12:17:54 +11:00
if (lfhasflag(lf, F_DEBUG)) {
aidb = B_TRUE;
2011-02-01 06:16:13 +11:00
}
moveeffects(lf);
if (isdead(lf)) {
return B_TRUE;
}
2010-12-07 18:34:26 +11:00
// get names
2010-12-02 12:17:54 +11:00
getlfname(lf, attackername);
2011-03-04 12:22:36 +11:00
if (lf == victim) {
if (isplayer(lf)) {
strcpy(victimname, "yourself");
} else {
strcpy(victimname, "itself");
}
} else {
getlfname(victim, victimname);
}
2010-12-02 12:17:54 +11:00
2011-02-01 06:16:13 +11:00
if (aidb) dblog(".oO { trying to attack %s }", victimname);
2010-12-07 18:34:26 +11:00
// get weapon
2011-02-01 06:16:13 +11:00
//wep = getweapon(lf);
wep = getattackwep(lf, &op, &unarmedflag);
2010-12-07 18:34:26 +11:00
if (!wep) {
2011-02-01 06:16:13 +11:00
if (isplayer(lf)) {
msg("You cannot attack!");
2011-03-16 15:45:46 +11:00
} else if (cansee(player, lf)) {
2011-02-01 06:16:13 +11:00
//msg("%s looks like it wants to attack!",attackername);
}
if (op) killobpile(op);
return B_TRUE;
2010-12-02 12:17:54 +11:00
}
2011-02-01 06:16:13 +11:00
2010-12-02 12:17:54 +11:00
2010-12-07 18:34:26 +11:00
getobname(wep, wepname, 1);
2010-12-02 12:17:54 +11:00
2011-02-01 06:16:13 +11:00
if (aidb) dblog(".oO { my weapon is %s %s }", wepname, unarmedflag ? "(unarmed)" : "");
2010-12-07 18:34:26 +11:00
// depends on weapon, race attackspeed modifier flag, etc
2011-02-01 06:16:13 +11:00
attacktime = getattackspeed(lf);
2010-12-02 12:17:54 +11:00
2011-02-01 06:16:13 +11:00
taketime(lf, attacktime);
2010-12-07 18:34:26 +11:00
// did you hit?
2011-02-01 06:16:13 +11:00
ndam = 0;
2011-03-04 12:22:36 +11:00
if (rolltohit(lf, victim, &critical)) {
int n;
2010-12-07 18:34:26 +11:00
hit = B_TRUE;
2011-02-01 06:16:13 +11:00
if (aidb) dblog(".oO { i hit! }");
// 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);
2011-03-04 12:22:36 +11:00
msgnocap("%c - %s.",o->letter, buf);
2011-02-01 06:16:13 +11:00
} 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);
}
}
if (op) {
killobpile(op);
}
return B_FALSE;
}
}
// determine base damage
// determine damage
//if (unarmed && (unarmedflag->val[0] != NA)) {
2011-03-04 12:22:36 +11:00
// ie. if critical is 0, do this once.
// if critical is 1, do this twice.
// etc.
dam[ndam] = 0;
for (n = 0; n < critical+1; n++) {
if (unarmedflag) {
// this mosnter's unarmed attack will
// override normal damage calculation
dam[ndam] += getdamrollfromflag(unarmedflag);
} else {
dam[ndam] += getdamroll(wep, victim);
}
2011-02-01 06:16:13 +11:00
}
if (aidb) dblog("rolled dam[%d] = %d",ndam,dam[ndam]);
2011-02-01 06:16:13 +11:00
if (dam[ndam] < 0) {
willheal = B_TRUE;
}
if (!willheal) {
// modify for strength
2011-03-11 12:25:38 +11:00
if (!hasflag(wep->flags, F_NOSTRDAMMOD) && !lfhasflag(lf, F_NOSTRDAMMOD)) {
dam[ndam] = (int)((float)dam[ndam] * getstrdammod(lf));
}
}
2011-02-01 06:16:13 +11:00
// damtype?
damtype[ndam] = getdamtype(wep);
if (aidb) dblog(".oO { dealing %d %s damage }", dam[ndam], getdamname(damtype[ndam]));
ndam++;
2011-02-01 06:16:13 +11:00
// blessed vs undead etc?
if (!willheal) {
if (isblessed(wep) && lfhasflagval(victim, F_DTVULN, DT_HOLY, NA, NA, NULL)) {
// a little extra damage
dam[ndam] = (int) ( (float)dam[ndam] * 1.25 );
}
2011-02-01 06:16:13 +11:00
// determine extra damage
2011-03-04 12:22:36 +11:00
getextradam(wep, &dam[0], &damtype[0], &ndam);
}
2010-12-07 18:34:26 +11:00
} else {
hit = B_FALSE;
2011-02-01 06:16:13 +11:00
ndam = 0;
2010-12-07 18:34:26 +11:00
}
2011-02-01 06:16:13 +11:00
if (ndam > 0) {
2011-03-04 12:22:36 +11:00
flag_t *f;
2011-02-01 06:16:13 +11:00
for (i = 0; i < ndam; i++) {
int reduceamt;
2010-12-07 18:34:26 +11:00
2011-02-01 06:16:13 +11:00
dblog("initial dam[%d] = %d",i,dam[i]);
2010-12-07 18:34:26 +11:00
2011-03-04 12:22:36 +11:00
if (lfhasflag(lf, F_HEAVYBLOW)) {
dam[i] = (int)((float)dam[i] * 1.5);
dblog("heavy blow makes dam[%d] = %d",i,dam[i]);
}
2011-02-01 06:16:13 +11:00
// modify based on resistances
adjustdamlf(victim, &dam[i], damtype[i]);
dblog("adjusted for lf to dam[%d] = %d",i,dam[i]);
2010-12-07 18:34:26 +11:00
2011-02-01 06:16:13 +11:00
// modify for defender's armour
2011-03-04 12:22:36 +11:00
reduceamt = getarmourdamreduction(victim, wep, dam[i], damtype[i]);
applyarmourdamreduction(victim, wep, reduceamt, &dam[i], damtype[i]);
2010-12-07 18:34:26 +11:00
2011-02-01 06:16:13 +11:00
dblog("reduced by armour to dam[%d] = %d",i,dam[i]);
2010-12-07 18:34:26 +11:00
2011-02-01 06:16:13 +11:00
// will this hit be fatal?
if (dam[i] >= victim->hp) {
fatal = B_TRUE;
2010-12-07 18:34:26 +11:00
}
2011-02-01 06:16:13 +11:00
// announce it
if (isplayer(lf)) {
char extradambuf[BUFLEN];
char *verb;
2011-02-01 06:16:13 +11:00
if (dam[i] == 0) {
strcpy(extradambuf, " but do no damage");
} else if (lfhasflag(player, F_EXTRAINFO) || lfhasflag(player, F_OMNIPOTENT) ) {
sprintf(extradambuf, " [%d dmg]",dam[i]);
} else {
strcpy(extradambuf, "");
}
if (fatal) {
verb = getkillverb(victim, damtype[i], dam[i], victim->maxhp);
} else {
verb = getattackverb(lf, wep, damtype[i], dam[i], victim->maxhp);
}
2011-02-01 06:16:13 +11:00
warn("You %s %s%s%s",
verb,
2011-02-01 06:16:13 +11:00
victimname, extradambuf,
fatal ? "!" : ".");
if (fatal && strstr(verb, "behead")) {
addflag(victim->flags, F_BEHEADED, B_TRUE, NA, NA, NULL);
}
2011-02-01 06:16:13 +11:00
if (fatal && !hasflag(victim->flags, F_NODEATHANNOUNCE)) {
// don't also say "the xx dies"
addflag(victim->flags, F_NODEATHANNOUNCE, B_TRUE, NA, NA, NULL);
}
} else {
2011-03-16 15:45:46 +11:00
if (cansee(player, lf) || isplayer(victim)) {
2011-02-01 06:16:13 +11:00
char withwep[BUFLEN];
char attackverb[BUFLEN];
char nodamstr[BUFLEN];
// capitalise first letter
sprintf(buf, "%s",attackername);
capitalise(buf);
2011-03-16 15:45:46 +11:00
if (wep && !unarmedflag && (lf->race->id != R_DANCINGWEAPON) && cansee(player, lf)) {
2011-02-01 06:16:13 +11:00
sprintf(withwep, " with %s", wepname);
} else {
strcpy(withwep, "");
}
strcpy(attackverb, getattackverb(lf, wep, damtype[i],dam[i],victim->maxhp));
2011-02-01 06:16:13 +11:00
if ((dam[i] == 0) && (damtype[i] != DT_TOUCH)) {
strcpy(nodamstr, " but does no damage");
} else {
strcpy(nodamstr, "");
}
warn("%s %s%s %s%s%s.", buf, attackverb,
attackverb[strlen(attackverb)-1] == 's' ? "es" : "s",
victimname,withwep, nodamstr);
2010-12-07 18:34:26 +11:00
} else {
2011-02-01 06:16:13 +11:00
youhear(lf->cell, "sounds of fighting");
}
}
2010-12-07 18:34:26 +11:00
if (willheal) {
2011-03-16 15:45:46 +11:00
if (cansee(player, victim)) {
flag_t *f;
msg("%s is healed!",victimname);
f = hasflag(wep->flags, F_BALANCE);
if (f) {
f->known = B_TRUE;
}
gainhp(victim, dam[i]);
}
2011-02-01 06:16:13 +11:00
} else {
2011-03-10 16:47:18 +11:00
char attackername2[BUFLEN];
real_getlfname(lf, attackername2, B_FALSE);
// victim loses hp
// don't adjust damage - we've already done that
if (wep && !unarmedflag) {
char wepname[BUFLEN];
getobname(wep, wepname, 1);
2011-03-10 16:47:18 +11:00
sprintf(buf, "%s^%s %s",attackername2,
2011-03-04 12:22:36 +11:00
(lf == victim) ? "using" : "weilding",
wepname);
} else {
2011-03-10 16:47:18 +11:00
strcpy(buf, attackername2);
}
losehp_real(victim, dam[i], damtype[i], lf, buf, B_FALSE);
// victim's armour loses hp
if (reduceamt) {
2011-03-04 12:22:36 +11:00
applyarmourdamage(victim, wep, reduceamt, damtype[i]);
2011-02-01 06:16:13 +11:00
}
}
} // end foreach damtype
// special weapon effects
if (dam[0]) {
wepeffects(wep->flags, victim->cell);
}
if (!isdead(victim)) {
if (unarmedflag) {
f = lfhasflag(lf, F_FREEZINGTOUCH);
if (f) {
// victim turns to ice for a while!
freezelf(victim, lf, rnd(5,10));
killflag(f);
}
}
2011-03-04 12:22:36 +11:00
f = lfhasflag(lf, F_QUICKBITE);
if (f) {
if (isbleeding(victim)) {
int dam;
char lfname[BUFLEN];
dam = rolldie(f->val[0], f->val[1]) + f->val[2];
2011-03-10 16:47:18 +11:00
real_getlfname(lf, lfname, B_FALSE);
2011-03-04 12:22:36 +11:00
losehp_real(victim, dam, DT_BITE, lf, lfname, B_FALSE);
2011-03-16 15:45:46 +11:00
if (isplayer(victim) || cansee(player, victim)) {
2011-03-04 12:22:36 +11:00
msg("%s bites %s!", lfname, victimname);
}
}
}
f = lfhasflag(lf, F_PACKATTACK);
if (f) {
int dir;
cell_t *c;
int nmatched = 0;
// count adjacent allies of name xx
for (dir = DC_N; dir <= DC_NW; dir++) {
c = getcellindir(victim->cell, dir);
if (c && c->lf) {
if (strcasestr(c->lf->race->name, f->text)) {
nmatched++;
}
}
}
if (nmatched >= f->val[2]) {
char damstring[BUFLEN];
sprintf(damstring, "a %s pack", f->text);
losehp(victim, f->val[0], f->val[1], lf, damstring);
2011-03-16 15:45:46 +11:00
if (isplayer(victim) || cansee(player, victim)) {
2011-03-04 12:22:36 +11:00
msg("The %s pack attacks %s!", f->text, victimname);
}
}
}
// confer flags from attacker?
if (dam[0]) {
wepeffects(lf->flags, victim->cell);
}
// 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_SLOWMOVE, 15, NA, NA, NULL, 2);
addtempflag(victim->flags, F_SLOWACT, 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);
}
}
}
}
}
2010-12-07 18:34:26 +11:00
} else { // miss!
2011-02-01 06:16:13 +11:00
if (aidb) dblog(".oO { i missed! }");
2010-12-07 18:34:26 +11:00
// announce it
2011-02-01 06:16:13 +11:00
if (lfhasflag(victim, F_MAGSHIELD) && ismetal(wep->material->id)) {
2011-03-16 15:45:46 +11:00
if (isplayer(lf) || cansee(player, lf)) {
2010-12-07 18:34:26 +11:00
sprintf(buf, "%s",attackername);
2011-02-01 06:16:13 +11:00
msg("%s%s magnetic shield repels %s%s attack.", victimname, getpossessive(victimname),
buf, getpossessive(buf));
}
} else {
if (isplayer(lf)) {
msg("You miss %s.", victimname);
} else {
2011-03-16 15:45:46 +11:00
if (cansee(player, lf)) {
2011-02-01 06:16:13 +11:00
// capitalise first letter
sprintf(buf, "%s",attackername);
msg("%s misses %s.", buf, victimname);
}
2010-12-07 18:34:26 +11:00
}
2011-03-04 12:22:36 +11:00
if (lfhasflag(victim, F_DODGES)) {
flag_t *f;
cell_t *adj;
f = addflag(victim->flags, F_NOTIME, B_TRUE, NA, NA, NULL);
adj = getrandomadjcell(victim->cell, WE_NOTSOLID);
moveto(lf, adj, B_FALSE);
2011-03-04 12:22:36 +11:00
msg("%s dodge%s!",victimname,isplayer(victim) ? "" : "s");
killflag(f);
}
2010-12-02 12:17:54 +11:00
}
2010-12-07 18:34:26 +11:00
fightback(victim, lf);
}
2011-02-01 06:16:13 +11:00
// get rid of temp unarmed object pile
if (op) {
killobpile(op);
}
// induction of fear?
if (!isdead(victim)) {
if (lfhasflag(victim, F_INDUCEFEAR)) {
scare(lf, victim, rnd(2,3));
}
2010-12-02 12:17:54 +11:00
}
2011-02-01 06:16:13 +11:00
if (aidb) dblog(".oO { doattack about to return B_FALSE }");
return B_FALSE;
}
int attackob(lifeform_t *lf, object_t *o) {
int dam[100];
2011-02-01 06:16:13 +11:00
enum DAMTYPE damtype[100];
int ndam = 0;
char attackername[BUFLEN];
char obname[BUFLEN];
flag_t *f;
flag_t *unarmedflag = NULL;
object_t *wep;
obpile_t *op = NULL;
cell_t *obloc = NULL;
int attacktime;
int unarmed = B_FALSE;
char wepname[BUFLEN];
int i;
//int aidb = B_TRUE;
int maxhp;
moveeffects(lf);
if (isdead(lf)) return B_TRUE;
2011-02-01 06:16:13 +11:00
// 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;
}
// get weapon
wep = getattackwep(lf, &op, &unarmedflag);
if (!wep) {
if (isplayer(lf)) {
msg("You cannot attack!");
2011-03-16 15:45:46 +11:00
} else if (cansee(player, lf)) {
2011-02-01 06:16:13 +11:00
//msg("%s looks like it wants to attack!",attackername);
}
if (!isplayer(lf)) {
// if ai, take some time to avoid infinite loops!
taketime(lf, getactspeed(lf));
2011-02-01 06:16:13 +11:00
}
if (op) killobpile(op);
return B_TRUE;
}
getobname(wep, wepname, 1);
// depends on weapon, race attackspeed modifier flag, etc
attacktime = getattackspeed(lf);
taketime(lf, attacktime);
2010-12-02 12:17:54 +11:00
2011-02-01 06:16:13 +11:00
// don't need to figure out accuracy - we always hit.
// determine damage
ndam = 0;
//if (unarmedflag && (unarmedflag->val[0] != NA)) {
if (unarmedflag) {
// this mosnter's unarmed attack will
// override normal damage calculation
dam[ndam] = getdamrollfromflag(unarmedflag);
} else {
dam[ndam] = getdamroll(wep, NULL);
2011-02-01 06:16:13 +11:00
}
// damtype?
damtype[ndam] = getdamtype(wep);
ndam++;
// don't need to check for blessed vs mosnters
// determine extra damage
2011-03-04 12:22:36 +11:00
getextradam(wep, &dam[0], &damtype[0], &ndam);
2011-02-01 06:16:13 +11:00
for (i = 0; i < ndam; i++) {
// announce the hit
if (isplayer(lf)) {
char extradambuf[BUFLEN];
if (lfhasflag(player, F_EXTRAINFO) || lfhasflag(player, F_OMNIPOTENT) ) {
sprintf(extradambuf, " [%d dmg]",dam[i]);
} else {
strcpy(extradambuf, "");
}
msg("You %s %s.", getattackverb(lf, wep, damtype[i], dam[i], maxhp),
2011-02-01 06:16:13 +11:00
obname, extradambuf);
2011-03-16 15:45:46 +11:00
} else if (cansee(player, lf)) {
2011-02-01 06:16:13 +11:00
char withwep[BUFLEN];
if (wep && !unarmed && !isblind(player)) { // announce weapon used
sprintf(withwep, " with %s", wepname);
} else {
strcpy(withwep, "");
}
msg("%s %ss %s%s.", attackername,
getattackverb(lf, wep, damtype[i],dam[i],maxhp), obname,withwep);
2011-02-01 06:16:13 +11:00
} else {
youhear(lf->cell, "sounds of fighting");
}
2011-03-16 15:45:46 +11:00
if ((i == 0) && unarmedflag && hasflag(o->flags, F_HARD)) {
2011-03-04 12:22:36 +11:00
char buf[BUFLEN];
sprintf(buf, "punching %s", obname);
if ( losehp(lf, 1, DT_BASH, lf, buf)) {
if (isplayer(lf)) {
msg("Ow!");
}
}
}
2011-02-01 06:16:13 +11:00
// object loses hp
takedamage(o, dam[i], damtype[i]);
} // end foreach damtype
// special weapon effects
if (dam[0]) {
wepeffects(wep->flags, obloc);
}
2011-02-01 06:16:13 +11:00
2011-03-16 15:45:46 +11:00
if (unarmedflag) {
// touch effects
touch(lf, o);
} else {
// weapon gets damaged ?
if (wep && (ndam > 0)) {
switch (damtype[0]) {
case DT_PIERCE:
case DT_SLASH:
// weapon gets duller
if (rnd(1,2)) makeduller(wep, 1);
break;
default:
break;
}
2011-03-04 12:22:36 +11:00
}
}
2011-02-01 06:16:13 +11:00
// get rid of temp unarmed object pile
if (op) {
killobpile(op);
}
2010-12-07 18:34:26 +11:00
return B_FALSE;
}
2011-03-04 12:22:36 +11:00
// returns the amount of damage the armour blocked...
int getarmourdamreduction(lifeform_t *lf, object_t *wep, int dam, enum DAMTYPE damtype) {
int reduceamt = 0;
float reducepct;
int ar;
ar = getarmourrating(lf);
reducepct = getdamreducepct(ar);
reduceamt = (int) ceil((reducepct / 100.0) * (float)dam);
if (reduceamt < 0) reduceamt = 0;
return reduceamt;
}
2011-02-01 06:16:13 +11:00
// returns a const char *
char *getattackverb(lifeform_t *lf, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp) {
2011-02-01 06:16:13 +11:00
float pct;
enum LFSIZE ownersize = SZ_HUMAN;
if (lf) {
ownersize = getlfsize(lf);
}
2011-02-01 06:16:13 +11:00
pct = (int)(((float) dam / (float) maxhp) * 100.0);
if (wep) {
flag_t *f;
f = hasflag(wep->flags, F_ATTACKVERB);
if (f) {
return f->text;
}
}
if (damtype == DT_ACID) {
return "burn";
} else if (damtype == DT_BASH) {
2011-02-01 06:16:13 +11:00
if (pct <= 5) {
2011-03-10 16:47:18 +11:00
return "whack";
} else if (pct <= 20) {
if (rnd(1,2) == 1) {
return "hit";
} else {
return "bash";
}
2010-12-07 18:34:26 +11:00
} else {
2011-03-10 16:47:18 +11:00
return "pummel";
2010-12-07 18:34:26 +11:00
}
2011-03-10 16:47:18 +11:00
} else if (damtype == DT_BITE) {
if (lf && (ownersize <= SZ_SMALL)) {
if (pct <= 5) {
return "nip";
} else if (pct <= 30) {
return "bite";
}
2011-02-01 06:16:13 +11:00
} else {
if (pct <= 5) {
return "gnaw";
} else if (pct <= 30) {
return "bite";
} else {
return "savage";
}
2011-02-01 06:16:13 +11:00
}
2010-12-07 18:34:26 +11:00
} else if (damtype == DT_CLAW) {
2011-02-01 06:16:13 +11:00
if (pct <= 5) {
2010-12-07 18:34:26 +11:00
return "scratch";
2011-02-01 06:16:13 +11:00
} else if (pct <= 15) {
2010-12-07 18:34:26 +11:00
return "claw";
2011-02-01 06:16:13 +11:00
} else if (pct <= 30) {
return "tear";
} else if (pct <= 40) {
2010-12-07 18:34:26 +11:00
return "rake";
2011-02-01 06:16:13 +11:00
} else if (pct <= 50) {
2010-12-07 18:34:26 +11:00
return "gouge";
} else {
return "eviscerate";
}
} else if (damtype == DT_CHOP) {
2011-02-01 06:16:13 +11:00
if (pct <= 5) {
2010-12-07 18:34:26 +11:00
return "hit";
2011-02-01 06:16:13 +11:00
} else if (pct <= 15) {
2010-12-07 18:34:26 +11:00
return "hack";
} else {
return "chop";
}
2011-03-10 16:47:18 +11:00
} else if (damtype == DT_COLD) {
if (pct <= 10) {
return "chill";
2010-12-07 18:34:26 +11:00
} else {
2011-03-10 16:47:18 +11:00
return "freeze";
2010-12-07 18:34:26 +11:00
}
2011-03-10 16:47:18 +11:00
} else if (damtype == DT_CRUSH) {
return "crush";
} else if (damtype == DT_ELECTRIC) {
2011-02-01 06:16:13 +11:00
if (pct <= 5) {
2011-03-10 16:47:18 +11:00
return "zap";
} else if (pct <= 15) {
return "jolt";
} else if (pct <= 20) {
return "shock";
2011-02-01 06:16:13 +11:00
} else if (pct <= 30) {
2011-03-10 16:47:18 +11:00
return "electrify";
2010-12-07 18:34:26 +11:00
} else {
2011-03-10 16:47:18 +11:00
return "electrocute";
2010-12-07 18:34:26 +11:00
}
2011-02-01 06:16:13 +11:00
} else if (damtype == DT_FIRE) {
if (pct <= 5) {
return "scorch";
} else if (pct <= 20) {
return "burn";
} else if (pct <= 40) {
return "scald";
} else {
return "incinerate";
}
2011-03-10 16:47:18 +11:00
} else if (damtype == DT_HOLY) {
switch (rnd(1,2)) {
case 1:
return "smite";
case 2:
return "cleanse";
}
} else if (damtype == DT_PIERCE) {
if (pct <= 5) {
return "poke";
} else if (pct <= 15) {
return "stab";
} else if (pct <= 30) {
return "pierce";
} else if (pct <= 40) {
return "spear";
2011-02-01 06:16:13 +11:00
} else {
2011-03-10 16:47:18 +11:00
return "deeply stab";
}
} else if (damtype == DT_POISONGAS) {
return "poison";
} else if (damtype == DT_PROJECTILE) {
return "hit";
} else if (damtype == DT_SLASH) {
if (pct <= 5) {
return "scratch";
} else if (pct <= 15) {
return "hit";
} else if (pct <= 30) {
return "slash";
} else {
return "slice";
2011-02-01 06:16:13 +11:00
}
2011-03-10 16:47:18 +11:00
} else if (damtype == DT_TOUCH) {
return "touch";
2011-03-04 12:22:36 +11:00
} else if (damtype == DT_UNARMED) {
if (rnd(1,2) == 1) {
return "punch";
} else {
return "hit";
}
2010-12-07 18:34:26 +11:00
}
return "hit";
}
2011-02-01 06:16:13 +11:00
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_DAMTYPE);
if (f) {
dt = f->val[0];
} else {
// default - you are just bashing with whatever
// you weilded.
dt = DT_BASH;
}
return dt;
}
2011-03-04 12:22:36 +11:00
int getextradam(object_t *wep, int *dam, enum DAMTYPE *damtype, int *ndam) {
2011-02-01 06:16:13 +11:00
flag_t *f;
for (f = wep->flags->first ; f ; f = f->next) {
if (f->id == F_ONFIRE) {
2011-03-04 12:22:36 +11:00
*(dam + *ndam) = rolldie(2,8);
*(damtype + *ndam) = DT_FIRE;
(*ndam)++;
2011-02-01 06:16:13 +11:00
}
}
return *dam;
}
char *getkillverb(lifeform_t *victim, enum DAMTYPE damtype, int dam, int maxhp) {
2011-02-01 06:16:13 +11:00
float pct;
pct = (int)(((float) dam / (float) maxhp) * 100.0);
if ((damtype == DT_BASH) && lfhasflag(victim, F_FROZEN)) {
return "shatter";
}
2011-03-10 16:47:18 +11:00
if (damtype == DT_CRUSH) {
return "crush";
}
2010-12-07 18:34:26 +11:00
if (damtype == DT_HOLY) {
return "smite";
}
2011-02-01 06:16:13 +11:00
if (pct >= 70) {
if (damtype == DT_PIERCE) return "impale";
if (damtype == DT_BASH) return "flatten";
2010-12-07 18:34:26 +11:00
if (damtype == DT_BITE) return "gore";
if (damtype == DT_CLAW) return "disembowel";
if (damtype == DT_SLASH) {
if (lfhasflagval(victim, F_NOBODYPART, BP_HEAD, NA, NA, NULL)) {
return "bisect";
} else {
if ((getlfsize(victim) >= SZ_MEDIUM) && (rnd(1,3) == 1)) {
return "behead";
} else {
return "bisect";
}
}
}
2010-12-07 18:34:26 +11:00
}
return "kill";
}
2011-02-01 06:16:13 +11:00
void getdamrange(flagpile_t *fp, int *min, int *max) {
2010-12-07 18:34:26 +11:00
int mindam,maxdam;
flag_t *f;
2011-02-01 06:16:13 +11:00
f = hasflag(fp, F_DAM);
2010-12-07 18:34:26 +11:00
if (f) {
int mod,ndice,sides;
ndice = f->val[0];
sides = f->val[1];
if (f->val[2] == NA) {
mod = 0;
} else {
mod = f->val[2];
}
mindam = (ndice * 1) + mod;
maxdam = (ndice * sides) + mod;
} else {
2011-02-01 06:16:13 +11:00
// TODO wepaon does damage based on weight
2010-12-07 18:34:26 +11:00
mindam = 0;
maxdam = 0;
}
if (min) *min = mindam;
if (max) *max = maxdam;
}
void getdamrangeunarmed(flag_t *f, int *min, int *max) {
obpile_t *op;
object_t *o;
op = addobpile(NULL, NULL);
o = addob(op, f->text);
if (o) {
int ndice,nsides,mod;
flag_t *damflag;
damflag = hasflag(o->flags, F_DAM);
if (f->val[0] == NA) {
ndice = damflag->val[0];
} else {
ndice = f->val[0];
}
if (f->val[1] == NA) {
nsides = damflag->val[1];
} else {
nsides = f->val[1];
}
if (f->val[2] == NA) {
if (damflag->val[2] != NA) {
mod = damflag->val[2];
} else {
mod = 0;
}
} else {
mod = f->val[2];
}
if (min) *min = (ndice * 1) + mod;
if (max) *max = (ndice * nsides) + mod;
killob(o);
} else {
if (min) *min = 0;
if (max) *max = 0;
}
free(op);
}
2010-12-07 18:34:26 +11:00
// roll for damage
int getdamroll(object_t *o, lifeform_t *victim) {
2010-12-07 18:34:26 +11:00
int dam;
flag_t *f;
f = hasflag(o->flags, F_DAM);
if (f) {
2011-02-01 06:16:13 +11:00
dam = getdamrollfromflag(f);
if (isblessed(o)) {
int dam2;
// blessed weapons get two rolls, and take the best
dam2 = getdamrollfromflag(f);
if (dam2 > dam) dam = dam2;
} else if (iscursed(o)) {
int dam2;
// cursed weapons get two rolls, and take the worst
dam2 = getdamrollfromflag(f);
if (dam2 < dam) dam = dam2;
2010-12-07 18:34:26 +11:00
}
2011-02-01 06:16:13 +11:00
2010-12-07 18:34:26 +11:00
} else {
2011-03-04 12:22:36 +11:00
// TODO wepaon does bashing damage based on weight
dam = rnd(1,2);
2010-12-07 18:34:26 +11:00
}
2011-02-01 06:16:13 +11:00
// modify for bonus
f = hasflag(o->flags, F_BONUS);
if (f) {
dam += f->val[0];
}
if (dam < 0) dam = 0;
// special effects ?
f = hasflag(o->flags, F_BALANCE);
if (f) {
lifeform_t *owner;
owner = o->pile->owner;
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);
}
}
}
2010-12-07 18:34:26 +11:00
return dam;
}
2011-02-01 06:16:13 +11:00
float getdamreducepct(float armourrating) {
float reducepct;
reducepct = (armourrating * 1.5);
return reducepct;
}
int getdamrollfromflag(flag_t *f) {
objecttype_t *ot;
int dam;
int ndice, sides, mod;
flag_t *damflag = NULL;
ot = findotn(f->text);
if (ot) {
damflag = hasflag(ot->flags, F_DAM);
}
// how many dice?
ndice = f->val[0];
if (ndice == NA) {
// get it from weapon definition
if (damflag && (damflag->val[0] != NA)) {
ndice = damflag->val[0];
} else {
ndice = 1;
}
}
sides = f->val[1];
if (sides == NA) {
// get it from weapon definition
if (damflag && (damflag->val[1] != NA)) {
sides = damflag->val[1];
} else {
sides = 1;
}
}
mod = f->val[2];
if (mod == NA) {
// get it from weapon definition
if (damflag && (damflag->val[2] != NA)) {
mod = damflag->val[2];
} else {
mod = 0;
}
}
dam = rolldie(ndice, sides) + mod;
assert(dam < 1000);
assert(dam >= 0);
return dam;
}
// returns a multiplier
float getstrdammod(lifeform_t *lf) {
float mod = 0;
float base;
// <9 = penalty
// 9,10,11,12 = average
// >12 = bonus
base = getattr(lf, A_STR);
if ((base >= 9) && (base <= 12)) {
mod = 1;
} else if (base > 12) {
base -= 12; // ie. 1 - 6
// 13 = 1 = 1.1
// 14 = 2 = 1.2
// 15 = 3 = 1.3
// 16 = 4 = 1.4
// 17 = 5 = 1.6
// 18 = 6 = 1.5
mod = 1 + (base / 10.0);
} else { // ie. 0 through 8
// 0 = 0.1
// 1 = 0.2
// 2 = 0.3
// 3 = 0.4
// 4 = 0.5
// 5 = 0.6
// 6 = 0.7
// 7 = 0.8
// 8 = 0.9
mod = (base * 0.1); // ie. 8 -> 0.8 or 4 -> 0.4
mod += 0.1; // ie. 8 -> 0.9 or 4 -> 0.5
}
return mod;
}
2010-12-07 18:34:26 +11:00
// determine attack type for lifeform.
2011-02-01 06:16:13 +11:00
// allocate a pile and add weapon to it.
// return the pile. remember to free it!
obpile_t *getunarmedweapon(lifeform_t *lf,flag_t **uflag) {
2010-12-07 18:34:26 +11:00
int nposs;
flag_t *f;
int sel;
char poss[MAXPILEOBS][BUFLEN];
2011-02-01 06:16:13 +11:00
obpile_t *op;
flag_t *possflag[MAXPILEOBS];
op = addobpile(NULL, NULL);
2010-12-07 18:34:26 +11:00
// pick a random attack type.
nposs = 0;
for (f = lf->flags->first ; f ; f = f->next) {
2011-02-01 06:16:13 +11:00
if (f->id == F_HASATTACK) {
2010-12-07 18:34:26 +11:00
strcpy(poss[nposs],f->text);
2011-02-01 06:16:13 +11:00
possflag[nposs] = f;
2010-12-07 18:34:26 +11:00
nposs++;
}
}
if (nposs > 0) {
2011-02-01 06:16:13 +11:00
object_t *uob;
2010-12-07 18:34:26 +11:00
sel = rnd(0,nposs-1);
2011-02-01 06:16:13 +11:00
uob = addob(op, poss[sel]);
assert(uob);
if (uflag) *uflag = possflag[sel];
2010-12-07 18:34:26 +11:00
}
2011-02-01 06:16:13 +11:00
if (!op->first) {
if (uflag) *uflag = NULL;
}
return op;
}
2011-03-04 12:22:36 +11:00
int rolltohit(lifeform_t *lf, lifeform_t *victim, int *critical) {
int acc,ev;
object_t *wep;
obpile_t *op = NULL;
int gothit;
enum LFSIZE szlf,szvictim;
2011-03-04 12:22:36 +11:00
if (critical) {
*critical = 0;
}
acc = getlfaccuracy(lf);
wep = getattackwep(lf, &op, NULL);
// modify for defender's evasion
ev = getevasion(victim);
acc -= ev;
// size difference
szlf = getlfsize(lf);
szvictim = getlfsize(victim);
if (szvictim < szlf) {
// if defender is smaller...
// -7% per size difference
acc -= (7 * (szlf - szvictim));
} else if (szvictim > szlf) {
// if defender is bigger...
// +7% per size difference
acc += (7 * (szvictim - szlf));
}
2011-03-16 15:45:46 +11:00
// modify if we can't see the victim
if (!cansee(lf, victim)) {
acc -= 50;
}
2011-03-04 12:22:36 +11:00
// metal weapon versus magnetic shield?
if (lfhasflag(victim, F_MAGSHIELD) && ismetal(wep->material->id)) {
acc -= 45;
}
if (critical) {
if (rnd(1,20) == 20) *critical = 1;
}
if (acc < 0) acc = 0;
if (acc > 100) acc = 100;
//if (aidb) dblog(".oO { my modified chance to hit is %d %% }", acc);
if (rnd(1,100) <= acc) {
gothit = B_TRUE;
} else {
if (critical && *critical) {
// turn a miss into a hit
gothit = B_TRUE;
} else {
gothit = B_FALSE;
}
}
if (op) killobpile(op);
return gothit;
}
void wepeffects(flagpile_t *fp, cell_t *where) {
2011-02-01 06:16:13 +11:00
flag_t *f;
lifeform_t *victim;
2011-03-04 12:22:36 +11:00
lifeform_t *owner;
object_t *wep;
if (!where) return;
2011-02-01 06:16:13 +11:00
wep = fp->ob;
owner = fp->owner;
2011-02-01 06:16:13 +11:00
victim = where->lf;
for (f = fp->first ; f ; f = f->next) {
2011-02-01 06:16:13 +11:00
if (f->id == F_FLAMESTRIKE) {
if (!hasob(where->obpile, OT_FIRESMALL)) {
// ignite!
addob(where->obpile, "small fire");
// announce
if (haslos(player, where)) {
msg("A column of fire erupts from the ground!");
f->known = B_KNOWN;
}
}
} else if ((f->id == F_REVENGE) && victim && !isdead(victim)) {
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->flags, NULL, &maxdam);
extradam = (int)(dampct * (float)maxdam);
if (extradam > 0) {
char buf[BUFLEN];
2011-03-10 16:47:18 +11:00
char buf2[BUFLEN];
char obname[BUFLEN];
char damstring[BUFLEN];
char victimname[BUFLEN];
getlfname(owner, buf);
2011-03-10 16:47:18 +11:00
real_getlfname(owner, buf2, B_FALSE);
getlfname(victim, victimname);
getobname(wep, obname, 1);
// announce
if (isplayer(owner)) {
msg("Your %s blasts %s!",noprefix(obname),victimname);
f->known = B_TRUE;
2011-03-16 15:45:46 +11:00
} else if (cansee(player, owner)) {
msg("%s%s %s blasts %s!",buf, getpossessive(buf),noprefix(obname),victimname);
f->known = B_TRUE;
}
2011-03-10 16:47:18 +11:00
sprintf(damstring, "%s%s blast of revenge",buf2, getpossessive(buf2));
losehp(victim, extradam, DT_DIRECT, owner, damstring);
}
} // end if dampct > 50
}
2011-03-04 12:22:36 +11:00
} else if ((f->id == F_HEAVYBLOW) && victim && owner) {
int dir;
// knock back victim
dir = getdirtowards(owner->cell, victim->cell, victim, B_FALSE);
knockback(victim, dir , 2, owner);
f->known = B_TRUE;
} else if ((f->id == F_HITCONFER) && victim ) {
enum FLAG fid;
int min,max,howlong;
fid = f->val[0];
if (!lfhasflag(victim, fid)) {
int passedcheck = B_FALSE;
if (!f->val[1] == NA) {
int scdiff;
if (f->val[2] == NA) {
scdiff = 20; // default
} else {
scdiff = f->val[2];
}
if (skillcheck(victim, f->val[1], scdiff, 0)) {
passedcheck = B_TRUE;
}
}
if (!passedcheck) {
int val0;
val0 = f->val[1];
if (f->text) {
char loctext[BUFLEN];
char *word, *dummy;
strcpy(loctext,f->text);
word = strtok_r(loctext, "-", &dummy);
if (word) {
min = atoi(word);
word = strtok_r(NULL, "-", &dummy);
if (word) {
max = atoi(word);
howlong = rnd(min,max);
} else {
howlong = PERMENANT;
}
} else {
howlong = PERMENANT;
}
} else {
howlong = PERMENANT;
}
addtempflag(victim->flags, fid, val0, 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);
}
} // end if (fid == hitconfer)
2010-12-07 18:34:26 +11:00
}
2010-12-02 12:17:54 +11:00
}