nexus/attack.c

368 lines
7.0 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "attack.h"
#include "defs.h"
#include "flag.h"
#include "lf.h"
#include "objects.h"
extern lifeform_t *player;
int doattack(lifeform_t *lf, lifeform_t *victim) {
int dam;
char buf[BUFLEN];
char attackername[BUFLEN];
char victimname[BUFLEN];
int fatal = B_FALSE;
flag_t *f;
object_t *wep;
enum DAMTYPE damtype;
obpile_t *op;
int attacktime;
int acc;
int hit = B_FALSE;
double dampct;
int unarmed = B_FALSE;
char wepname[BUFLEN];
int ev;
// get names
getlfname(lf, attackername);
getlfname(victim, victimname);
// get weapon
op = addobpile(lf, NULL); // for use if we are unarmed
wep = getweapon(lf);
if (!wep) {
unarmed = B_TRUE;
// ie. unarmed
getunarmedweapon(lf, op);
if (op->first) {
wep = op->first;
} else {
// cannot attack!
if (lf->controller == C_PLAYER) {
msg("You cannot attack!");
} else if (haslos(player, lf->cell)) {
sprintf(buf, "%s",attackername);
capitalise(buf);
msg("%s looks like it wants to attack!",buf);
}
if (lf->controller != C_PLAYER) {
// take some time to avoid infinite loops!
taketime(lf, 1);
}
free(op);
return B_TRUE;
}
}
getobname(wep, wepname, 1);
f = hasflag(wep->flags, F_DAMTYPE);
if (f) {
damtype = f->val[0];
} else {
// default - you are just bashing with whatever
// you weilded.
damtype = DT_BASH;
}
// depends on weapon, race attackspeed modifier flag, etc
attacktime = getobattackspeed(wep) + getlfattackspeed(lf);
taketime(lf, attacktime);
// TODO: figure out if you hit
// TODO: figure out if you do damage, and how much
acc = 100; // base accuracy of 100%
// modify for weapon's (lack of) accuracy
f = hasflag(wep->flags, F_ACCURACY);
if (f) {
// ie. accuracy of 100% means no penalty
// ie. accuracy of 75% means 25% penalty
// etc
acc -= (100 - f->val[0]);
}
// modify for defender's evasion
ev = getevasion(victim);
acc -= ev;
// modify for attacker's level
acc += (lf->level * 2);
if (acc < 0) acc = 0;
if (acc > 100) acc = 100;
// did you hit?
if (rnd(1,100) <= acc) {
hit = B_TRUE;
} else {
hit = B_FALSE;
}
if (hit) {
int reducepct;
int ar;
int reduceamt;
// determine damage
dam = getdamroll(wep);
// modify for attacker's armour
ar = getarmour(victim);
reducepct = ar * 2;
reduceamt = (int)(((float)reducepct / 100.0) * (float)dam);
dam -= reduceamt;
if (dam < 0) dam = 0;
// TODO: armour gets damaged
losehp(victim, dam, damtype, lf, attackername);
if (victim->hp <= 0) {
fatal = B_TRUE;
}
// announce it
if (lf->controller == C_PLAYER) {
msg("You %s %s%s%s",
fatal ? getkillverb(damtype, dam) : getattackverb(damtype, dam),
victimname,
(dam == 0) ? " but do no damage" : "",
fatal ? "!" : ".");
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 {
if (haslos(player, lf->cell)) {
char withwep[BUFLEN];
// capitalise first letter
sprintf(buf, "%s",attackername);
capitalise(buf);
if (wep && !unarmed) {
sprintf(withwep, " with %s", wepname);
} else {
strcpy(withwep, "");
}
msg("%s %ss %s%s%s.", buf, getattackverb(damtype,dam), victimname,withwep,
(dam == 0) ? " but does no damage" : "" );
}
// TODO: else {if (haslineofhearing() etc
}
} else { // miss!
// announce it
if (lf->controller == C_PLAYER) {
msg("You miss %s.", victimname);
} else {
if (haslos(player, lf->cell)) {
// capitalise first letter
sprintf(buf, "%s",attackername);
capitalise(buf);
msg("%s misses %s.", buf, victimname);
}
}
fightback(victim, lf);
}
// get rid of temp unarmed object
if (op->first) {
killob(op->first);
}
free(op);
return B_FALSE;
}
char *getattackverb(enum DAMTYPE damtype, int dam) {
if (damtype == DT_PROJECTILE) {
return "hit";
} else if (damtype == DT_HOLY) {
return "smite";
} else if (damtype == DT_PIERCE) {
if (dam == 0) {
return "poke";
} else if (dam <= 5) {
return "nick";
} else if (dam <= 10) {
return "stab";
} else if (dam <= 15) {
return "pierce";
} else if (dam <= 20) {
return "spear";
} else {
return "deeply stab";
}
} else if (damtype == DT_CLAW) {
if (dam == 0) {
return "scratch";
} else if (dam <= 5) {
return "claw";
} else if (dam <= 15) {
return "rake";
} else if (dam <= 30) {
return "gouge";
} else {
return "eviscerate";
}
} else if (damtype == DT_SLASH) {
if (dam == 0) {
return "scratch";
} else if (dam <= 10) {
return "hit";
} else if (dam <= 20) {
return "slash";
} else {
return "slice";
}
} else if (damtype == DT_CHOP) {
if (dam == 0) {
return "hit";
} else if (dam <= 10) {
return "hack";
} else {
return "chop";
}
} else if (damtype == DT_BASH) {
if (dam == 0) {
return "whack";
} else if (dam <= 5) {
return "hit";
} else if (dam <= 10) {
return "bash";
} else if (dam <= 15) {
return "pummel";
} else if (dam <= 20) {
return "pound";
} else {
return "flatten";
}
} else if (damtype == DT_BITE) {
if (dam == 0) {
return "gnaw";
} else if (dam <= 15) {
return "bite";
} else {
return "savage";
}
}
return "hit";
}
char *getkillverb(enum DAMTYPE damtype, int dam) {
if (damtype == DT_HOLY) {
return "smite";
}
if (dam >= 50) {
if (damtype == DT_PIERCE) return "fatally stab";
if (damtype == DT_BITE) return "gore";
if (damtype == DT_CLAW) return "disembowel";
if (damtype == DT_SLASH) return "behead"; // TODO: only if they have a head! otherwise "bisect"
}
return "kill";
}
// return TRUE if no damage
int getdamrange(object_t *o, int *min, int *max) {
int mindam,maxdam;
flag_t *f;
f = hasflag(o->flags, F_DAM);
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 {
mindam = 0;
maxdam = 0;
}
if (min) *min = mindam;
if (max) *max = maxdam;
}
// roll for damage
int getdamroll(object_t *o) {
int dam;
int ndice, sides, mod;
flag_t *f;
f = hasflag(o->flags, F_DAM);
if (f) {
ndice = f->val[0];
sides = f->val[1];
if (f->val[2] != NA) {
mod = f->val[2];
} else {
mod = 0;
}
dam = rolldie(ndice, sides) + mod;
} else {
// weapon does no damage
dam = 0;
}
return dam;
}
// determine attack type for lifeform.
// add an object of this kind to the pile 'op'
// return its type;
objecttype_t *getunarmedweapon(lifeform_t *lf,obpile_t *op) {
int nposs;
flag_t *f;
int sel;
char poss[MAXPILEOBS][BUFLEN];
// pick a random attack type.
nposs = 0;
for (f = lf->flags->first ; f ; f = f->next) {
if (f->id == F_UNARMEDATTACKOB) {
strcpy(poss[nposs],f->text);
nposs++;
}
}
if (nposs > 0) {
sel = rnd(0,nposs-1);
addob(op, poss[sel]);
}
if (op->first) {
return op->first->type;
} else {
return NULL;
}
}