368 lines
7.0 KiB
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;
|
|
}
|
|
}
|