3229 lines
71 KiB
C
3229 lines
71 KiB
C
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "attack.h"
|
|
#include "defs.h"
|
|
#include "flag.h"
|
|
#include "lf.h"
|
|
#include "map.h"
|
|
#include "move.h"
|
|
#include "nexus.h"
|
|
#include "objects.h"
|
|
#include "text.h"
|
|
|
|
extern long curtime;
|
|
|
|
|
|
extern lifeform_t *player;
|
|
|
|
extern enum GAMEMODE gamemode;
|
|
extern enum WINGAMETYPE wintype;
|
|
|
|
extern material_t *material,*lastmaterial;
|
|
extern plural_t *firstplural,*lastplural;
|
|
|
|
plural_t *addplural(char *singulartext, char *pluraltext, int stopafter) {
|
|
plural_t *a;
|
|
char buf[BUFLEN];
|
|
|
|
// add to the end of the list
|
|
if (firstplural == NULL) {
|
|
firstplural = malloc(sizeof(celltype_t));
|
|
a = firstplural;
|
|
a->prev = NULL;
|
|
} else {
|
|
// go to end of list
|
|
a = lastplural;
|
|
a->next = malloc(sizeof(plural_t));
|
|
a->next->prev = a;
|
|
a = a->next;
|
|
}
|
|
lastplural = a;
|
|
a->next = NULL;
|
|
|
|
// set props - add spaces at the end of words
|
|
sprintf(buf, "%s ",singulartext);
|
|
a->singular = strdup(buf);
|
|
sprintf(buf, "%s ",pluraltext);
|
|
a->plural = strdup(buf);
|
|
a->stopafter = stopafter;
|
|
|
|
return a;
|
|
}
|
|
|
|
void addengineeringinfo(lifeform_t *lf, char *buf, cell_t *c) {
|
|
enum SKILLLEVEL slev;
|
|
char newbuf[BUFLEN];
|
|
strcpy(newbuf, "");
|
|
slev = getskill(lf, SK_ENGINEERING);
|
|
if (slev >= PR_NOVICE) {
|
|
int cdiff = NA;
|
|
int slip = 0;
|
|
slip = getslipperyness(c, NULL);
|
|
if (slip > 0) {
|
|
char tempbuf[BUFLEN];
|
|
if (strlen(newbuf)) strcat(newbuf, ",");
|
|
sprintf(tempbuf, "slippery:%d%%",slip);
|
|
strcat(newbuf, tempbuf);
|
|
} else if (slip < 0) {
|
|
if (strlen(newbuf)) strcat(newbuf, ",");
|
|
char tempbuf[BUFLEN];
|
|
sprintf(tempbuf, "stable:%d%%",abs(slip));
|
|
strcat(newbuf, tempbuf);
|
|
}
|
|
|
|
if (c->type->solid) {
|
|
cdiff = getcellclimbdifficulty(c);
|
|
}
|
|
if (cdiff == NA) {
|
|
object_t *o;
|
|
o = hasobwithflag(c->obpile, F_CLIMBOBSTACLE);
|
|
if (o) {
|
|
flag_t *f;
|
|
f = hasflag(o->flags, F_CLIMBOBSTACLE);
|
|
if (f) { // should always be true
|
|
cdiff = f->val[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
if (cdiff != NA) {
|
|
char tempbuf[BUFLEN];
|
|
if (strlen(newbuf)) strcat(newbuf, ",");
|
|
sprintf(tempbuf, "climb diff:%d%%",cdiff);
|
|
strcat(newbuf, tempbuf);
|
|
}
|
|
|
|
}
|
|
if (slev >= PR_BEGINNER) {
|
|
if (c->hp != -1) {
|
|
char tempbuf[BUFLEN];
|
|
if (strlen(newbuf)) strcat(newbuf, ",");
|
|
sprintf(tempbuf, "hp:%d",c->hp);
|
|
strcat(newbuf, tempbuf);
|
|
}
|
|
}
|
|
|
|
if (strlen(newbuf)) {
|
|
strcat(buf, "(");
|
|
strcat(buf, newbuf);
|
|
strcat(buf, ")");
|
|
}
|
|
}
|
|
|
|
int needan(char *text) {
|
|
if (isvowel(tolower(text[0]))) {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
char *capitalise(char *text) {
|
|
if (strlen(text)) {
|
|
char *p;
|
|
p = text;
|
|
while (*p == '^') {
|
|
p++; // go past the ^
|
|
if (!(*p)) return text; // do nothing
|
|
p++; // go past the colour char
|
|
if (!(*p)) return text; // do nothing
|
|
while (*p && isdigit(*p)) {
|
|
p++; // go past any digits (in case this was ^12 rather than ^b)
|
|
}
|
|
if (!(*p)) return text; // do nothing
|
|
|
|
}
|
|
*p = toupper(*p);
|
|
}
|
|
return text;
|
|
}
|
|
|
|
// capitalise all words
|
|
char *capitaliseall(char *text) {
|
|
if (strlen(text) > 0) {
|
|
char *p;
|
|
for (p = text ; *p; p++) {
|
|
if (p == text) { // first letter
|
|
*p = toupper(*p);
|
|
} else if (*(p-1) == ' ') { // first letter after a space
|
|
*p = toupper(*p);
|
|
}
|
|
}
|
|
}
|
|
return text;
|
|
}
|
|
|
|
enum COLOUR chartocol(char ch) {
|
|
switch (ch) {
|
|
case 'w': // warning
|
|
return C_YELLOW;
|
|
case 'W': // extra warning
|
|
return C_MAGENTA;
|
|
case 'b': // bad
|
|
return C_DARKYELLOW;
|
|
case 'B': // v.bad
|
|
return C_RED;
|
|
case 'T': // terrible
|
|
return C_ORANGE;
|
|
case 'g': // good
|
|
return C_GREEN;
|
|
case 'G': // v.good
|
|
return C_CYAN;
|
|
case 'E': // excllent
|
|
return C_LIGHTCYAN;
|
|
case 'h': // 'hilite'
|
|
return C_WHITE;
|
|
case 'l': // 'bLue'
|
|
return C_BLUE;
|
|
case 'n': // normal
|
|
default:
|
|
break;
|
|
}
|
|
return C_GREY;
|
|
}
|
|
|
|
char *construct_hit_string(lifeform_t *lf, lifeform_t *victim, char *attackername, char *victimname, char *victimbpname, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp, int idx, int backstab, int critical, int fatal, int isunarmed, char *retbuf) {
|
|
int usecrittext = B_FALSE;
|
|
char wepname[BUFLEN],buf[BUFLEN];
|
|
char locvictimname[BUFLEN];
|
|
enum HELPLESSTYPE helpless;
|
|
|
|
// default
|
|
strcpy(retbuf, "");
|
|
|
|
if (wep) {
|
|
getobname(wep, wepname, 1);
|
|
} else {
|
|
strcpy(wepname, "?noweapon?"); // should never be displayed
|
|
}
|
|
|
|
// modify victimname if required
|
|
//if (helpless && !isbehind(lf, victim)) {
|
|
if (victim && !isplayer(victim) && ishelplessvictim(victim, lf, &helpless)) {
|
|
char *vn;
|
|
// strip "the" from "the xxx"
|
|
vn = strdup(victimname);
|
|
strrep(&vn, "the ", "", NULL);
|
|
switch (helpless) {
|
|
case HL_CANTSEE:
|
|
sprintf(locvictimname, "the helpless %s", vn);
|
|
break;
|
|
case HL_FLEEING:
|
|
sprintf(locvictimname, "the fleeing %s", vn);
|
|
break;
|
|
default: break;
|
|
}
|
|
free(vn);
|
|
} else {
|
|
strcpy(locvictimname, victimname);
|
|
}
|
|
|
|
// initial hit...
|
|
if (idx == 0) {
|
|
if (critical && !fatal) usecrittext = B_TRUE;
|
|
|
|
if (isplayer(lf)) {
|
|
char extradambuf[BUFLEN];
|
|
char withwep[BUFLEN],adjective[BUFLEN],hitwhere[BUFLEN];
|
|
char *verb;
|
|
int needfree = B_FALSE;
|
|
int knownnodam = B_FALSE;
|
|
int col;
|
|
|
|
strcpy(extradambuf, "");
|
|
|
|
if (wep && !ismeleeweapon(wep)) {
|
|
snprintf(withwep, BUFLEN, " with %s", wepname);
|
|
} else {
|
|
strcpy(withwep, "");
|
|
}
|
|
|
|
strcpy(extradambuf, "");
|
|
if ((dam == 0) && (damtype != DT_TOUCH)) {
|
|
if (!victim || getlorelevel(lf, victim->race->raceclass->id)) {
|
|
if (!lfhasflag(lf, F_PHANTASM)) {
|
|
//strcpy(extradambuf, " but do no damage");
|
|
if (onein(2)) {
|
|
strcpy(extradambuf, " ineffectually");
|
|
} else {
|
|
strcpy(extradambuf, ", but do no damage.");
|
|
}
|
|
knownnodam = B_TRUE;
|
|
}
|
|
}
|
|
} else if (lfhasflag(player, F_EXTRAINFO) || lfhasflag(player, F_OMNIPOTENT) ) {
|
|
snprintf(extradambuf, BUFLEN, " [%d dmg]",dam);
|
|
}
|
|
|
|
if (backstab && (idx == 0)) {
|
|
verb = strdup("backstab");
|
|
needfree = B_TRUE;
|
|
} else if (fatal) {
|
|
verb = getkillverb(victim, wep, damtype, dam, maxhp);
|
|
if (!isplayer(victim)) {
|
|
if (strstr(verb, "knock out") || strstr(verb, "disable")) knownnodam = B_TRUE;
|
|
}
|
|
} else {
|
|
if (!victim || // atacking an object
|
|
(getlorelevel(lf, victim->race->raceclass->id) >= PR_BEGINNER) || // know about the race
|
|
!ismeleedam(damtype)) { // non-physical damage
|
|
verb = getattackverb(lf, wep, damtype, dam, maxhp);
|
|
} else {
|
|
// always use verb for 10%
|
|
verb = getattackverb(lf, wep, damtype, pctof(10, maxhp), maxhp);
|
|
}
|
|
}
|
|
if (knownnodam) {
|
|
col = C_GREY;
|
|
} else if (fatal) {
|
|
col = C_GREEN;
|
|
} else {
|
|
col = C_LIGHTGREEN; // normal hit
|
|
}
|
|
|
|
strcpy(adjective, "");
|
|
if (isexhausted(lf)) {
|
|
if (onein(2)) strcat(adjective, "tiredly ");
|
|
else strcat(adjective, "wearily ");
|
|
}
|
|
if (usecrittext) {
|
|
if (strlen(adjective)) {
|
|
strcat(adjective, "but critically ");
|
|
} else {
|
|
strcat(adjective, "critically ");
|
|
}
|
|
strcpy(hitwhere, victimbpname);
|
|
} else {
|
|
strcpy(hitwhere, locvictimname);
|
|
}
|
|
snprintf(retbuf, BUFLEN, "^%dYou %s%s %s%s%s%s", col, adjective, verb,
|
|
hitwhere, withwep,extradambuf, (fatal || backstab) ? "!" : ".");
|
|
|
|
if (needfree) {
|
|
free(verb);
|
|
}
|
|
} else { // ie. the attacker is a monster
|
|
if (cansee(player, lf) || (victim && isplayer(victim))) {
|
|
int needfree = B_FALSE;
|
|
char *verb;
|
|
char withwep[BUFLEN],adjective[BUFLEN],hitwhere[BUFLEN];
|
|
char nodamstr[BUFLEN];
|
|
int nodam = B_FALSE;
|
|
int col;
|
|
|
|
// capitalise first letter
|
|
strcpy(buf, attackername);
|
|
capitalise(buf);
|
|
|
|
if (wep && !isunarmed && (lf->race->baseid != R_DANCINGWEAPON) && cansee(player, lf)) {
|
|
snprintf(withwep, BUFLEN, " with %s", wepname);
|
|
} else {
|
|
strcpy(withwep, "");
|
|
}
|
|
|
|
verb = getattackverb(lf, wep, damtype,dam,maxhp);
|
|
// when a monster is attacking someone, we want:
|
|
// "the xxx hits yyy. yyy dies."
|
|
// rather than
|
|
// "the xxx kills yyy."
|
|
/*
|
|
if (fatal && !isplayer(victim)) {
|
|
verb = getkillverb(victim, wep, damtype, dam, maxhp);
|
|
} else {
|
|
verb = getattackverb(lf, wep, damtype,dam,maxhp);
|
|
}
|
|
*/
|
|
verb = getattackverb(lf, wep, damtype,dam,maxhp);
|
|
|
|
strcpy(nodamstr, "");
|
|
if ((dam == 0) && (damtype != DT_TOUCH) && !lfhasflag(lf, F_PHANTASM)) {
|
|
nodam = B_TRUE;
|
|
if (onein(2)) {
|
|
strcpy(nodamstr, " ineffectually");
|
|
} else {
|
|
strcpy(nodamstr, ", but does no damage");
|
|
}
|
|
}
|
|
|
|
if (backstab && (idx == 0)) {
|
|
verb = strdup("backstab");
|
|
needfree = B_TRUE;
|
|
}
|
|
|
|
if (victim && isplayer(victim) && !nodam) {
|
|
col = C_YELLOW;
|
|
} else {
|
|
//col = C_GREY;
|
|
col = chartocol(getlfcol(victim, CC_BAD));
|
|
}
|
|
|
|
strcpy(adjective, "");
|
|
if (usecrittext) {
|
|
strcat(adjective, "critically ");
|
|
strcpy(hitwhere, victimbpname);
|
|
} else {
|
|
strcpy(hitwhere, locvictimname);
|
|
}
|
|
|
|
snprintf(retbuf, BUFLEN, "^%d%s %s%s%s %s%s%s%c", col, buf, adjective,
|
|
verb, needses(verb) ? "es" : "s",
|
|
hitwhere, withwep, nodamstr, backstab ? '!' : '.');
|
|
if (needfree) {
|
|
free(verb);
|
|
}
|
|
}
|
|
}
|
|
} else { // follow-up weapon damage (ie from a flaming sword etc)
|
|
if (victim && cansee(player, victim)) {
|
|
if (dam == 0) { // ineffectual
|
|
switch (damtype) {
|
|
case DT_COLD:
|
|
snprintf(retbuf, BUFLEN, "^n%s %s cold.", locvictimname, isplayer(victim) ? "don't feel" : "doesn't look");
|
|
break;
|
|
case DT_HEAT:
|
|
case DT_FIRE:
|
|
snprintf(retbuf, BUFLEN, "^n%s %s hot.", locvictimname, isplayer(victim) ? "don't feel" : "doesn't look");
|
|
break;
|
|
//case DT_MAGIC:
|
|
//snprintf(retbuf, BUFLEN, "^n%s shrug%s off the effects.", locvictimname, isplayer(victim) ? "" : "s");
|
|
//break;
|
|
default:
|
|
strcpy(retbuf, "");
|
|
break;
|
|
}
|
|
} else if (fatal) { // fatal
|
|
switch (damtype) {
|
|
case DT_COLD:
|
|
snprintf(retbuf, BUFLEN, "^%c%s %s chilled to the bone!", getlfcol(victim, CC_BAD), locvictimname, is(victim));
|
|
break;
|
|
case DT_HEAT:
|
|
case DT_FIRE:
|
|
snprintf(retbuf, BUFLEN, "^%c%s %s incinerated!", getlfcol(victim, CC_BAD), locvictimname, is(victim));
|
|
break;
|
|
case DT_MAGIC:
|
|
snprintf(retbuf, BUFLEN, "^%c%s %s blasted with magic!", getlfcol(victim, CC_BAD), locvictimname, is(victim));
|
|
break;
|
|
default:
|
|
snprintf(retbuf, BUFLEN, "^%c%s %s killed!", getlfcol(victim, CC_BAD), locvictimname, is(victim));
|
|
break;
|
|
}
|
|
} else { // normal
|
|
switch (damtype) {
|
|
case DT_COLD:
|
|
snprintf(retbuf, BUFLEN, "^%c%s %s chilled!", getlfcol(victim, CC_BAD), locvictimname, is(victim));
|
|
break;
|
|
case DT_ELECTRIC:
|
|
snprintf(retbuf, BUFLEN, "^%c%s %s zapped!", getlfcol(victim, CC_BAD), locvictimname, is(victim));
|
|
break;
|
|
case DT_EXPLOSIVE:
|
|
snprintf(retbuf, BUFLEN, "^%c%s %s blasted!", getlfcol(victim, CC_BAD), locvictimname, is(victim));
|
|
break;
|
|
case DT_HEAT:
|
|
case DT_FIRE:
|
|
snprintf(retbuf, BUFLEN, "^%c%s %s burned!", getlfcol(victim, CC_BAD), locvictimname, is(victim));
|
|
break;
|
|
case DT_MAGIC:
|
|
snprintf(retbuf, BUFLEN, "^%cMagical energy sears %s!", getlfcol(victim, CC_BAD), locvictimname);
|
|
break;
|
|
default:
|
|
//snprintf(retbuf, BUFLEN, "^n%s %s hurt!", locvictimname, is(victim));
|
|
strcpy(retbuf, "");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
capitalise(retbuf);
|
|
return retbuf;
|
|
}
|
|
|
|
char *dicetotext(int ndice, int nsides, int bonus, int *min, int *max, char *dicebuf, char *minmaxbuf) {
|
|
int localmin, localmax;
|
|
|
|
if (ndice == NA) ndice = 0;
|
|
if (nsides == NA) nsides = 0;
|
|
if (bonus == NA) bonus = 0;
|
|
|
|
// ie. rolled a 1 on all dice
|
|
localmin = (ndice * 1) + bonus;
|
|
// ie. rolled max on all dice
|
|
localmax = (ndice * nsides) + bonus;
|
|
|
|
if (min) {
|
|
*min = localmin;
|
|
}
|
|
if (max) {
|
|
*max = localmax;
|
|
}
|
|
|
|
if (dicebuf) {
|
|
if ((ndice == 0) || (nsides == 0)) {
|
|
snprintf(dicebuf, BUFLEN, "%d", bonus);
|
|
} else {
|
|
if (bonus) {
|
|
snprintf(dicebuf, BUFLEN, "%dd%d%c%d", ndice, nsides,
|
|
(bonus > 0) ? '+' : '-',
|
|
abs(bonus));
|
|
} else {
|
|
snprintf(dicebuf, BUFLEN, "%dd%d", ndice, nsides);
|
|
}
|
|
}
|
|
}
|
|
if (minmaxbuf) {
|
|
if (localmin == localmax) {
|
|
snprintf(minmaxbuf, BUFLEN, "%d", localmin);
|
|
} else {
|
|
snprintf(minmaxbuf, BUFLEN, "%d-%d", localmin, localmax);
|
|
}
|
|
}
|
|
return dicebuf;
|
|
}
|
|
|
|
int flip(int ch) {
|
|
switch (ch) {
|
|
case 'a': return 0x0250;
|
|
case 'b': return 'q';
|
|
case 'c': return 0x0254;
|
|
case 'd': return 'p';
|
|
case 'e': return 0x01dd;
|
|
case 'f': return 0x025f;
|
|
case 'g': return 0x0183;
|
|
case 'h': return 0x0265;
|
|
case 'i': return 0x0131;
|
|
case 'j': return 0x027e;
|
|
case 'k': return 0x029e;
|
|
case 'l': return 0x0283;
|
|
case 'm': return 0x026f;
|
|
case 'n': return 'u';
|
|
case 'r': return 0x0279;
|
|
case 't': return 0x0287;
|
|
case 'v': return 0x028c;
|
|
case 'w': return 0x028d;
|
|
case 'y': return 0x028e;
|
|
case '.': return 0x02d9;
|
|
case '[': return ']';
|
|
case '(': return ')';
|
|
case '{': return '}';
|
|
case '?': return 0x00bf;
|
|
case '!': return 0x00a1;
|
|
case '<': return '>';
|
|
case '_': return 0x203e;
|
|
}
|
|
return ch;
|
|
}
|
|
|
|
// ie. "it has xxx accuracy"
|
|
char *getaccuracyname(int accpct) {
|
|
if (accpct >= 200) {
|
|
return "incredible";
|
|
} else if (accpct >= 150) {
|
|
return "excellent";
|
|
} else if (accpct >= 120) {
|
|
return "great";
|
|
} else if (accpct >= 100) {
|
|
return "good";
|
|
} else if (accpct >= 80) {
|
|
return "average";
|
|
} else if (accpct >= 70) {
|
|
return "mediocre";
|
|
} else if (accpct >= 50) {
|
|
return "poor";
|
|
} else if (accpct >= 30) {
|
|
return "very poor";
|
|
} else if (accpct >= 20) {
|
|
return "extremely poor";
|
|
} else {
|
|
return "a complete lack of";
|
|
}
|
|
}
|
|
|
|
int getaccuracymodnum(int accpctmod) {
|
|
return accpctmod / 5;
|
|
}
|
|
|
|
int getaccuracynum(int accpct) {
|
|
int num;
|
|
num = (accpct - 100) / 5; //
|
|
return num;
|
|
}
|
|
|
|
char *getalignmentname(enum ALIGNMENT al) {
|
|
switch (al) {
|
|
case AL_NONE: return "none";
|
|
case AL_GOOD: return "good";
|
|
case AL_NEUTRAL: return "neutral";
|
|
case AL_EVIL: return "evil";
|
|
default: break;
|
|
}
|
|
return "?unknown_align?";
|
|
}
|
|
|
|
// returns a const char *
|
|
char *getattackverb(lifeform_t *lf, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp) {
|
|
//float pct;
|
|
enum LFSIZE ownersize = SZ_HUMAN;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags = 0;
|
|
|
|
if (lf) {
|
|
ownersize = getlfsize(lf);
|
|
}
|
|
|
|
//pct = (int)(((float) dam / (float) maxhp) * 100.0);
|
|
// base verb on amount of damage, not percentage of target.
|
|
// reasoning: just because a monster has lots of hp, doesn't mean
|
|
// you can only "scratch" it rather than "slice" it.
|
|
//pct = (int)(((float) dam / 50.0) * 100.0);
|
|
|
|
if (wep) {
|
|
int i;
|
|
flag_t *f;
|
|
getflags(wep->flags, retflag, &nretflags, F_ATTACKVERB, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if ((f->val[0] == NA) && (f->val[1] == NA)) {
|
|
return f->text;
|
|
} else if (f->val[0]) {
|
|
if (dam >= f->val[0]) {
|
|
if (f->val[1] == NA) {
|
|
return f->text;
|
|
} else if (dam <= f->val[1]) {
|
|
return f->text;
|
|
}
|
|
}
|
|
} else if (f->val[1]) {
|
|
if (dam <= f->val[1]) {
|
|
return f->text;
|
|
}
|
|
}
|
|
}
|
|
|
|
// whips deal normal damagetype(s), but have diferent attack
|
|
// verbs.
|
|
if (hasflagval(wep->flags, F_USESSKILL, SK_WHIPS, NA, NA, NULL)) {
|
|
if (dam <= 4) {
|
|
return "whip";
|
|
} else if (dam <= 8) {
|
|
return "thrash";
|
|
} else {
|
|
return "flay";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (damtype == DT_ACID) {
|
|
return "burn";
|
|
} else if (damtype == DT_BASH) {
|
|
// chance of special monk attack verb
|
|
if (lf && hasjob(lf, J_MONK) && wep && hasflag(wep->flags, F_UNARMEDWEP) && onein(2)) {
|
|
switch (rnd(1,7)) {
|
|
case 1: return "punch";
|
|
case 2: return "palmstrike";
|
|
case 3: return "elbow";
|
|
case 4: return "kick";
|
|
case 5: return "roundhouse kick";
|
|
case 6: return "karate chop";
|
|
case 7: return "strike";
|
|
}
|
|
} else {
|
|
if (dam <= 2) {
|
|
return "whack";
|
|
} else if (dam <= 6) {
|
|
if (onein(2)) {
|
|
return "hit";
|
|
} else {
|
|
return "bash";
|
|
}
|
|
} else if (dam <= 12) {
|
|
return "pound";
|
|
} else if (dam <= 16) {
|
|
return "slam";
|
|
} else if (dam <= 20) {
|
|
return "smash";
|
|
} else {
|
|
return "pulverise";
|
|
}
|
|
}
|
|
} else if (damtype == DT_BITE) {
|
|
if (lf && (ownersize <= SZ_SMALL)) {
|
|
if (dam <= 2) {
|
|
return "nip";
|
|
} else {
|
|
return "bite";
|
|
}
|
|
} else {
|
|
if (dam <= 4) {
|
|
return "gnaw";
|
|
} else if (dam <= 8) {
|
|
return "bite";
|
|
} else {
|
|
return "savage";
|
|
}
|
|
}
|
|
} else if (damtype == DT_CHOP) {
|
|
if (dam <= 4) {
|
|
return "chop";
|
|
} else if (dam <= 8) {
|
|
return "hack";
|
|
} else {
|
|
return "cleave";
|
|
}
|
|
} else if (damtype == DT_COLD) {
|
|
if (dam <= 4) {
|
|
return "chill";
|
|
} else {
|
|
return "freeze";
|
|
}
|
|
} else if (damtype == DT_CRUSH) {
|
|
return "crush";
|
|
} else if (damtype == DT_ELECTRIC) {
|
|
if (dam <= 2) {
|
|
return "zap";
|
|
} else if (dam <= 6) {
|
|
return "jolt";
|
|
} else if (dam <= 12) {
|
|
return "shock";
|
|
} else if (dam <= 18) {
|
|
return "electrify";
|
|
} else {
|
|
return "electrocute";
|
|
}
|
|
} else if ((damtype == DT_FIRE) || (damtype == DT_HEAT)) {
|
|
if (dam <= 2) {
|
|
return "scorch";
|
|
} else if (dam <= 6) {
|
|
return "singe";
|
|
} else if (dam <= 12) {
|
|
return "burn";
|
|
} else if (dam <= 18) {
|
|
return "scald";
|
|
} else {
|
|
return "incinerate";
|
|
}
|
|
} else if (damtype == DT_HOLY) {
|
|
switch (rnd(1,3)) {
|
|
case 1:
|
|
return "smite";
|
|
case 2:
|
|
return "cleanse";
|
|
case 3:
|
|
return "purify";
|
|
}
|
|
} else if (damtype == DT_MAGIC) {
|
|
if (dam <= 2) {
|
|
return "zap";
|
|
} else if (dam <= 6) {
|
|
return "sear";
|
|
} else {
|
|
return "blast";
|
|
}
|
|
} else if (damtype == DT_PIERCE) {
|
|
if (dam <= 2) {
|
|
return "poke";
|
|
} else if (dam <= 6) {
|
|
return "stab";
|
|
} else if (dam <= 12) {
|
|
return "pierce";
|
|
} else if (dam <= 18) {
|
|
return "spear";
|
|
} else {
|
|
return "deeply stab";
|
|
}
|
|
} else if (damtype == DT_POISONGAS) {
|
|
return "poison";
|
|
} else if (damtype == DT_PROJECTILE) {
|
|
return "hit";
|
|
} else if (damtype == DT_SLASH) {
|
|
if (dam <= 2) {
|
|
return "scratch";
|
|
} else if (dam <= 6) {
|
|
return "cut";
|
|
} else if (dam <= 12) {
|
|
return "slash";
|
|
} else {
|
|
return "slice";
|
|
}
|
|
} else if (damtype == DT_TOUCH) {
|
|
return "touch";
|
|
} else if (damtype == DT_WATER) {
|
|
// for when water-vulnerable things go into water
|
|
return "hurt";
|
|
} else if (damtype == DT_UNARMED) {
|
|
if (onein(2)) {
|
|
return "punch";
|
|
} else {
|
|
return "hit";
|
|
}
|
|
}
|
|
return "hit";
|
|
}
|
|
|
|
|
|
char *getattrabbrev(enum ATTRIB att) {
|
|
switch (att) {
|
|
case A_NONE:
|
|
return "??";
|
|
case A_CHA:
|
|
return "Ch";
|
|
case A_CON:
|
|
return "Ft";
|
|
case A_AGI:
|
|
return "Ag";
|
|
case A_IQ:
|
|
return "Iq";
|
|
case A_STR:
|
|
return "St";
|
|
case A_WIS:
|
|
return "Wi";
|
|
}
|
|
return "??";
|
|
}
|
|
|
|
char *getattrbracketname(enum ATTRIB whichatt, enum ATTRBRACKET brack) {
|
|
switch (brack) {
|
|
case AT_EXLOW:
|
|
switch (whichatt) {
|
|
case A_CHA:
|
|
return "hideous";
|
|
case A_CON:
|
|
return "frail";
|
|
case A_AGI:
|
|
return "uncoordinated";
|
|
case A_IQ:
|
|
return "vegetable";
|
|
case A_STR:
|
|
return "helpless";
|
|
case A_WIS:
|
|
return "witless";
|
|
default:
|
|
return "?extralow?";
|
|
}
|
|
break;
|
|
case AT_VLOW:
|
|
switch (whichatt) {
|
|
case A_CHA:
|
|
return "repulsive";
|
|
case A_CON:
|
|
return "sickly";
|
|
case A_AGI:
|
|
return "oafish";
|
|
case A_IQ:
|
|
return "animal";
|
|
case A_STR:
|
|
return "feeble";
|
|
case A_WIS:
|
|
return "reckless";
|
|
default:
|
|
return "?verylow?";
|
|
}
|
|
break;
|
|
case AT_LOW:
|
|
switch (whichatt) {
|
|
case A_CHA:
|
|
return "ugly";
|
|
case A_CON:
|
|
return "unhealthy";
|
|
case A_AGI:
|
|
return "clumsy";
|
|
case A_IQ:
|
|
return "dim-witted";
|
|
case A_STR:
|
|
return "very weak";
|
|
case A_WIS:
|
|
return "foolish";
|
|
default:
|
|
return "?low?";
|
|
}
|
|
break;
|
|
case AT_LTAVERAGE:
|
|
switch (whichatt) {
|
|
case A_CHA:
|
|
return "unattractive";
|
|
case A_CON:
|
|
return "unfit";
|
|
case A_AGI:
|
|
return "awkward";
|
|
case A_IQ:
|
|
return "dopey";
|
|
case A_STR:
|
|
return "weak";
|
|
case A_WIS:
|
|
return "naive";
|
|
default:
|
|
return "?lt_average?";
|
|
}
|
|
break;
|
|
case AT_AVERAGE:
|
|
switch (whichatt) {
|
|
case A_CHA:
|
|
case A_CON:
|
|
case A_AGI:
|
|
case A_STR:
|
|
case A_IQ:
|
|
case A_WIS:
|
|
return "average";
|
|
default:
|
|
return "?average?";
|
|
}
|
|
break;
|
|
case AT_GTAVERAGE:
|
|
switch (whichatt) {
|
|
case A_CHA:
|
|
return "attractive";
|
|
case A_CON:
|
|
return "healthy";
|
|
case A_AGI:
|
|
return "dextrous";
|
|
case A_IQ:
|
|
return "smart";
|
|
case A_STR:
|
|
return "strong";
|
|
case A_WIS:
|
|
return "prudent";
|
|
default:
|
|
return "?gt_average?";
|
|
}
|
|
break;
|
|
case AT_HIGH:
|
|
switch (whichatt) {
|
|
case A_CHA:
|
|
return "alluring";
|
|
case A_CON:
|
|
return "very fit";
|
|
case A_AGI:
|
|
return "nimble";
|
|
case A_IQ:
|
|
return "enlightened";
|
|
case A_STR:
|
|
return "mighty";
|
|
case A_WIS:
|
|
return "astute";
|
|
default:
|
|
return "?high?";
|
|
}
|
|
break;
|
|
case AT_VHIGH:
|
|
switch (whichatt) {
|
|
case A_CHA:
|
|
return "beautiful";
|
|
case A_CON:
|
|
return "hardy";
|
|
case A_AGI:
|
|
return "agile";
|
|
case A_IQ:
|
|
return "genius";
|
|
case A_STR:
|
|
return "powerful";
|
|
case A_WIS:
|
|
return "wise";
|
|
default:
|
|
return "?veryhigh?";
|
|
}
|
|
break;
|
|
case AT_EXHIGH:
|
|
switch (whichatt) {
|
|
case A_CHA:
|
|
return "stunning";
|
|
case A_CON:
|
|
return "very hardy";
|
|
case A_AGI:
|
|
return "very agile";
|
|
case A_IQ:
|
|
return "supergenius";
|
|
case A_STR:
|
|
return "titanic";
|
|
case A_WIS:
|
|
return "sagely";
|
|
default:
|
|
return "?exhigh?";
|
|
}
|
|
break;
|
|
case AT_RANDOM:
|
|
return "random";
|
|
}
|
|
return "?unknown?";
|
|
}
|
|
|
|
char getattrletter(enum ATTRIB att) {
|
|
switch (att) {
|
|
case A_NONE:
|
|
return '?';
|
|
case A_CHA:
|
|
return 'c';
|
|
case A_CON:
|
|
return 'f';
|
|
case A_AGI:
|
|
return 'a';
|
|
case A_IQ:
|
|
return 'i';
|
|
case A_STR:
|
|
return 's';
|
|
case A_WIS:
|
|
return 'w';
|
|
}
|
|
return '?';
|
|
}
|
|
|
|
char *getattrname(enum ATTRIB att) {
|
|
switch (att) {
|
|
case A_NONE:
|
|
return "?attrib_none?";
|
|
case A_CHA:
|
|
return "charisma";
|
|
case A_CON:
|
|
return "fitness";
|
|
case A_AGI:
|
|
return "agility";
|
|
case A_IQ:
|
|
return "intellect";
|
|
case A_STR:
|
|
return "strength";
|
|
case A_WIS:
|
|
return "wisdom";
|
|
}
|
|
return "?badattrib?";
|
|
}
|
|
|
|
char *getcolname(enum COLOUR c) {
|
|
switch (c) {
|
|
case C_RANDOM: return "__randomcolour__";
|
|
case C_BLACK: return "black";
|
|
case C_AQUA: return "aqua";
|
|
case C_RED: return "red";
|
|
case C_GREEN: return "green";
|
|
case C_BROWN: return "brown";
|
|
case C_BLUE: return "blue";
|
|
case C_MAGENTA: return "magenta";
|
|
case C_INDIGO: return "indigo";
|
|
case C_PINK: return "pink";
|
|
case C_CYAN: return "cyan";
|
|
case C_GREY: return "grey";
|
|
case C_YELLOW: return "yellow";
|
|
case C_WHITE: return "white";
|
|
case C_BONE: return "bone";
|
|
case C_BRICK: return "brick";
|
|
case C_MOSS: return "moss";
|
|
case C_FLESH: return "flesh";
|
|
case C_FOG: return "fog";
|
|
case C_CARPET1: return "carpet1";
|
|
case C_CARPET2: return "carpet2";
|
|
case C_METAL: return "metal";
|
|
case C_SMOKE: return "smoke";
|
|
case C_WOOD: return "wood";
|
|
case C_DARKCYAN: return "darkcyan";
|
|
case C_DARKBLUE: return "darkblue";
|
|
case C_DARKMAGENTA: return "darkmagenta";
|
|
case C_DARKYELLOW: return "darkyellow";
|
|
case C_ORANGE: return "orange";
|
|
case C_DARKGREEN: return "darkgreen";
|
|
case C_DARKGREY: return "darkgrey";
|
|
case C_DARKBROWN: return "darkbrown";
|
|
case C_DARKRED: return "darkred";
|
|
case C_VDARKGREY: return "vdarkgrey";
|
|
case C_LIGHTRED: return "lightred";
|
|
case C_LIGHTBLUE: return "lightblue";
|
|
case C_LIGHTBROWN: return "lightbrown";
|
|
case C_LIGHTCYAN: return "lightcyan";
|
|
case C_LIGHTGREEN: return "lightgreen";
|
|
case C_LIGHTMAGENTA: return "lightmagenta";
|
|
case C_LIGHTYELLOW: return "lightyellow";
|
|
case C_LAST: return "last_colour";
|
|
}
|
|
return "?unknowncolour?";
|
|
}
|
|
|
|
char *getdamname(enum DAMTYPE damtype) {
|
|
switch (damtype) {
|
|
case DT_ALL: return "all damage";
|
|
case DT_ACID: return "acid";
|
|
case DT_BASH: return "bludgeoning";
|
|
case DT_BITE: return "biting";
|
|
case DT_CHOP: return "chopping";
|
|
case DT_COLD: return "cold";
|
|
case DT_CRUSH: return "crushing";
|
|
case DT_DIRECT: return "direct";
|
|
case DT_DECAY: return "decay";
|
|
case DT_ELECTRIC: return "electricity";
|
|
case DT_EXPLOSIVE: return "explosive";
|
|
case DT_FALL: return "falling";
|
|
case DT_FIRE: return "fire";
|
|
case DT_HEAT: return "heat";
|
|
case DT_HOLY: return "holy damage";
|
|
case DT_LIGHT: return "light";
|
|
case DT_MAGIC: return "magical";
|
|
case DT_MELT: return "melting";
|
|
case DT_NECROTIC: return "lifedrain";
|
|
case DT_PETRIFY: return "petrification";
|
|
case DT_PIERCE: return "piercing";
|
|
case DT_POISON: return "poison";
|
|
case DT_POISONGAS: return "gas";
|
|
case DT_PROJECTILE: return "projectile";
|
|
case DT_SLASH: return "slashing";
|
|
case DT_SONIC: return "sonic";
|
|
case DT_TOUCH: return "touch";
|
|
case DT_UNARMED: return "unarmed";
|
|
case DT_WATER: return "water";
|
|
default: return "unknown";
|
|
}
|
|
return "unknown";
|
|
}
|
|
|
|
char *getdamnamenoun(enum DAMTYPE damtype) {
|
|
switch (damtype) {
|
|
case DT_ALL: return "all damage";
|
|
case DT_ACID: return "acid";
|
|
case DT_MELT: return "melting";
|
|
case DT_PETRIFY: return "petrification";
|
|
case DT_PIERCE: return "piercing damage";
|
|
case DT_POISONGAS: return "gas";
|
|
case DT_POISON: return "poison";
|
|
case DT_SLASH: return "slashing damage";
|
|
case DT_ELECTRIC: return "electricity";
|
|
case DT_EXPLOSIVE: return "explosives";
|
|
case DT_FIRE: return "fire";
|
|
case DT_HEAT: return "heat";
|
|
case DT_BITE: return "bite";
|
|
case DT_BASH: return "bludgeoning damage";
|
|
case DT_CHOP: return "chopping damage";
|
|
case DT_COLD: return "cold";
|
|
case DT_PROJECTILE: return "projectiles";
|
|
case DT_HOLY: return "holy damage";
|
|
case DT_DIRECT: return "direct damage";
|
|
case DT_DECAY: return "decay damage";
|
|
case DT_WATER: return "water";
|
|
case DT_MAGIC: return "magical damage";
|
|
case DT_NECROTIC: return "lifedrain damage";
|
|
case DT_TOUCH: return "touch effects";
|
|
case DT_UNARMED: return "unarmed damage";
|
|
case DT_LIGHT: return "light damage";
|
|
case DT_CRUSH: return "crushing damage";
|
|
case DT_SONIC: return "sonic damage";
|
|
case DT_FALL: return "damage from falling";
|
|
default: return "unknown";
|
|
}
|
|
return "unkmown";
|
|
}
|
|
|
|
|
|
char *getdirname(int dir) {
|
|
switch (dir) {
|
|
case D_N:
|
|
return "North";
|
|
case D_E:
|
|
return "East";
|
|
case D_S:
|
|
return "South";
|
|
case D_W:
|
|
return "West";
|
|
case D_UP:
|
|
return "up";
|
|
case D_DOWN:
|
|
return "down";
|
|
case D_UNKNOWN:
|
|
return "D_UNKNOWN";
|
|
case D_NONE:
|
|
return "D_NONE";
|
|
case DC_N:
|
|
return "North";
|
|
case DC_NE:
|
|
return "Northeast";
|
|
case DC_E:
|
|
return "East";
|
|
case DC_SE:
|
|
return "Southeast";
|
|
case DC_S:
|
|
return "South";
|
|
case DC_SW:
|
|
return "Southwest";
|
|
case DC_W:
|
|
return "West";
|
|
case DC_NW:
|
|
return "Northwest";
|
|
case D_IN:
|
|
return "into";
|
|
}
|
|
return "?errordir?";
|
|
}
|
|
|
|
char *getdirnameshort(int dir) {
|
|
switch (dir) {
|
|
case D_N:
|
|
return "N";
|
|
case D_E:
|
|
return "E";
|
|
case D_S:
|
|
return "S";
|
|
case D_W:
|
|
return "W";
|
|
case D_UP:
|
|
return "U";
|
|
case D_DOWN:
|
|
return "D";
|
|
case D_UNKNOWN:
|
|
return "?";
|
|
case D_NONE:
|
|
return "-";
|
|
case DC_N:
|
|
return "N";
|
|
case DC_NE:
|
|
return "NE";
|
|
case DC_E:
|
|
return "E";
|
|
case DC_SE:
|
|
return "SE";
|
|
case DC_S:
|
|
return "S";
|
|
case DC_SW:
|
|
return "SW";
|
|
case DC_W:
|
|
return "W";
|
|
case DC_NW:
|
|
return "NW";
|
|
}
|
|
return "?";
|
|
}
|
|
|
|
void getdisttext(cell_t *src, cell_t *dst,char *distbuf, char *distbufapprox, char *dirbuf) {
|
|
int dist;
|
|
int dir;
|
|
dist = getcelldist(src, dst);
|
|
dir = getdirtowards(src, dst, NULL, B_FALSE, DT_ORTH);
|
|
|
|
if (dirbuf) {
|
|
strcpy(dirbuf, getdirname(dir));
|
|
dirbuf[0] = tolower(dirbuf[0]);
|
|
}
|
|
|
|
if (dist >= 20) { // 20+
|
|
if (distbuf) strcpy(distbuf, " very far away");
|
|
if (distbufapprox) strcpy(distbufapprox, " far away");
|
|
} else if (dist >= 10) { // 10 - 19
|
|
if (distbuf) strcpy(distbuf, " far away");
|
|
if (distbufapprox) strcpy(distbufapprox, " far away");
|
|
} else if (dist >= 5) { // 5 - 9
|
|
if (distbuf) strcpy(distbuf, " nearby");
|
|
if (distbufapprox) strcpy(distbufapprox, " nearby");
|
|
} else if (dist >= 2) { // 2 - 4
|
|
if (distbuf) strcpy(distbuf, " very nearby");
|
|
if (distbufapprox) strcpy(distbufapprox, " nearby");
|
|
} else { // 1
|
|
if (distbuf) strcpy(distbuf, " right beside you");
|
|
if (distbufapprox) strcpy(distbufapprox, " nearby");
|
|
}
|
|
|
|
}
|
|
|
|
// ie. "the apple is xxx"
|
|
// ie. "the apples are xxx"
|
|
char *getfillingname(int nutrition) {
|
|
if (nutrition > 100) {
|
|
return "extremely substantial";
|
|
} else if (nutrition >= 90) {
|
|
return "very filling";
|
|
} else if (nutrition >= 70) {
|
|
return "ample for a meal";
|
|
} else if (nutrition >= 50) {
|
|
return "enough for a light meal";
|
|
} else if (nutrition >= 25) {
|
|
return "snack-sized";
|
|
} else if (nutrition > 0) {
|
|
return "barely worth eating";
|
|
}
|
|
// ie. < 0
|
|
return "of zero nutritional substance";
|
|
}
|
|
|
|
char *getsourcetext(int src) {
|
|
switch (src) {
|
|
case FROMSKILL: return "skill perk";
|
|
case FROMJOB: return "job perk";
|
|
case FROMINJURY: return "from injury";
|
|
case FROMGODGIFT: return "god gift";
|
|
case FROMGODPIETY: return "piety bonus";
|
|
case FROMSPELL: return "from spell";
|
|
case FROMABIL: return "from ability";
|
|
default: break;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
char *getflagsourcetext(flag_t *f, char *buf) {
|
|
char obname[BUFLEN];
|
|
char action[BUFLEN];
|
|
strcpy(buf, "");
|
|
if (f->pile->owner && !isplayer(f->pile->owner)) return buf;
|
|
switch (f->lifetime) {
|
|
case FROMSKILL:
|
|
case FROMJOB:
|
|
case FROMGODGIFT:
|
|
case FROMGODPIETY:
|
|
case FROMSPELL:
|
|
case FROMABIL:
|
|
case FROMINJURY:
|
|
sprintf(buf," (%s)",getsourcetext(f->lifetime));
|
|
break;
|
|
case FROMOBEQUIP:
|
|
case FROMOBHOLD:
|
|
case FROMOBACTIVATE:
|
|
if (f->lifetime == FROMOBEQUIP) strcpy(action, "equipping");
|
|
else if (f->lifetime == FROMOBEQUIP) strcpy(action, "holding");
|
|
else strcpy(action, "activating");
|
|
|
|
if (f->pile->ob) {
|
|
getobname(f->pile->ob, obname, f->pile->ob->amt);
|
|
} else {
|
|
strcpy(obname, "an object");
|
|
}
|
|
sprintf(buf," (from %s %s)", action, obname);
|
|
break;
|
|
default: break;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
int gethitconferlifetime(char *text, int *min, int *max) {
|
|
int howlong;
|
|
int localmin = -1,localmax = -1;
|
|
if (text && strlen(text)) {
|
|
char loctext[BUFLEN];
|
|
char *word, *dummy;
|
|
strcpy(loctext,text);
|
|
word = strtok_r(loctext, "-", &dummy);
|
|
if (word) {
|
|
localmin = atoi(word);
|
|
word = strtok_r(NULL, "-", &dummy);
|
|
if (word) {
|
|
localmax = atoi(word);
|
|
howlong = rnd(localmin,localmax);
|
|
} else {
|
|
howlong = PERMENANT;
|
|
}
|
|
} else {
|
|
localmin = -1;
|
|
localmax = -1;
|
|
howlong = PERMENANT;
|
|
}
|
|
} else {
|
|
localmin = -1;
|
|
localmax = -1;
|
|
howlong = PERMENANT;
|
|
}
|
|
if (min) *min = localmin;
|
|
if (max) *max = localmax;
|
|
return howlong;
|
|
}
|
|
|
|
char *getjobcatname(enum JOBCATEGORY jc) {
|
|
switch (jc) {
|
|
case JC_GENERAL:
|
|
return "Generalist";
|
|
case JC_FIGHTER:
|
|
return "Fighter";
|
|
case JC_FIGHTERMAGE:
|
|
return "Fighter/Mage";
|
|
case JC_FIGHTERTHIEF:
|
|
return "Fighter/Thief";
|
|
case JC_FIGHTERRANGED:
|
|
return "Ranged Fighter";
|
|
case JC_FIGHTERSPEC:
|
|
return "Specialist Fighter";
|
|
case JC_MAGE:
|
|
return "Mage";
|
|
case JC_THIEF:
|
|
return "Thief";
|
|
default:
|
|
break;
|
|
}
|
|
return "None";
|
|
}
|
|
|
|
char *getkillverb(lifeform_t *victim, object_t *wep, enum DAMTYPE damtype, int dam, int maxhp) {
|
|
float pct;
|
|
flag_t *f;
|
|
pct = (int)(((float) dam / (float) maxhp) * 100.0);
|
|
|
|
f = lfhasflag(victim, F_GETKILLEDVERB);
|
|
if (f) {
|
|
return f->text;
|
|
}
|
|
|
|
if (!isunconscious(victim) && lfcanbekod(victim)) {
|
|
int willko = B_FALSE;
|
|
if (wep && hasflag(wep->flags, F_MERCIFUL)) {
|
|
willko = B_TRUE;
|
|
}
|
|
if (!willko && wep && wep->pile->owner && lfhasflag(wep->pile->owner, F_STRIKETOKO)) {
|
|
willko = B_TRUE;
|
|
}
|
|
if (willko) {
|
|
if (getraceclass(victim) == RC_ROBOT) return "disable";
|
|
else return "knock out";
|
|
}
|
|
}
|
|
|
|
if (wep) {
|
|
flag_t *f;
|
|
int i;
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
int nretflags = 0;
|
|
getflags(wep->flags, retflag, &nretflags, F_KILLVERB, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
f = retflag[i];
|
|
if (f->id == F_KILLVERB) {
|
|
if ((f->val[0] == NA) && (f->val[1] == NA)) {
|
|
return f->text;
|
|
} else if (f->val[0]) {
|
|
if (pct >= f->val[0]) {
|
|
if (f->val[1] == NA) {
|
|
return f->text;
|
|
} else if (pct <= f->val[1]) {
|
|
return f->text;
|
|
}
|
|
}
|
|
} else if (f->val[1]) {
|
|
if (pct <= f->val[1]) {
|
|
return f->text;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((damtype == DT_BASH) && ismadeofice(victim)) {
|
|
return "shatter";
|
|
}
|
|
|
|
if (damtype == DT_CRUSH) {
|
|
return "crush";
|
|
}
|
|
|
|
if (damtype == DT_HOLY) {
|
|
return "smite";
|
|
}
|
|
|
|
if (getraceclass(victim) == RC_UNDEAD) {
|
|
// can't "kill" the undead
|
|
return "destroy";
|
|
}
|
|
|
|
// was 80
|
|
if (pct >= 100) {
|
|
if (damtype == DT_PIERCE) return "impale";
|
|
if (damtype == DT_BASH) {
|
|
if (isunconscious(victim)) {
|
|
return "kill";
|
|
} else {
|
|
return "flatten";
|
|
}
|
|
}
|
|
if (damtype == DT_BITE) return "gore";
|
|
if (damtype == DT_SLASH) {
|
|
skill_t *sk;
|
|
int canbehead = B_TRUE;
|
|
|
|
if (wep) {
|
|
sk = getobskill(wep->flags);
|
|
if (sk && (sk->id != SK_LONGBLADES) && (sk->id != SK_EXOTICWEPS)) {
|
|
// only long blades can behead/bisect.
|
|
canbehead = B_FALSE;
|
|
}
|
|
}
|
|
// can't behead multiheaded things at the moment...
|
|
if (victim && hasbp(victim, BP_HEAD) && hasbp(victim, BP_HEAD2)) {
|
|
canbehead = B_FALSE;
|
|
}
|
|
|
|
if (canbehead) {
|
|
if (victim && (victim->race->id == R_EARTHWYRM)) {
|
|
return "bisect";
|
|
} else if (hasbp(victim, BP_HEAD) && (getlfsize(victim) >= SZ_MEDIUM) && onein(3)) {
|
|
return "behead";
|
|
} else {
|
|
if (onein(2) && (getraceclass(victim) == RC_HUMANOID)) return "dismember";
|
|
else return "bisect";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// never use 'kill' for bashing since you might just knock them out
|
|
if (damtype == DT_BASH) {
|
|
return "clobber";
|
|
}
|
|
return "kill";
|
|
}
|
|
|
|
char *getnthtext(int num) {
|
|
switch (num) {
|
|
case 0:
|
|
case 1:
|
|
return "first";
|
|
case 2: return "second";
|
|
case 3: return "third";
|
|
case 4: return "fourth";
|
|
case 5: return "fifth";
|
|
case 6: return "sixth";
|
|
case 7: return "seventh";
|
|
case 8: return "eighth";
|
|
case 9: return "ninth";
|
|
case 10: return "tenth";
|
|
case 11: return "eleventh";
|
|
case 12: return "twelvth";
|
|
case 13: return "thirteenth";
|
|
case 14: return "fourteenth";
|
|
case 15: return "fifteenth";
|
|
case 16: return "sixteenth";
|
|
case 17: return "seventeenth";
|
|
case 18: return "eighteenth";
|
|
case 19: return "nineteenth";
|
|
case 20: return "twentieth";
|
|
default:
|
|
break;
|
|
}
|
|
return "upteenth";
|
|
}
|
|
|
|
char *getpossessive(char *text) {
|
|
char lastchar;
|
|
// you -> your
|
|
if (!strcasecmp(text, "you")) {
|
|
return "r";
|
|
}
|
|
|
|
// xxxs -> xxxs'
|
|
lastchar = text[strlen(text)-1];
|
|
if (tolower(lastchar) == 's') {
|
|
return "'";
|
|
}
|
|
// default: 's
|
|
return "'s";
|
|
}
|
|
|
|
char *getdrunkdesc(lifeform_t *lf, flag_t *drunkflag, char *buf) {
|
|
int agimod, wismod;
|
|
char agibuf[BUFLEN], wisbuf[BUFLEN], dambuf[BUFLEN];
|
|
|
|
strcpy(buf, "");
|
|
// agi / wis mod
|
|
agimod = getdrunkattrmod(lf, A_AGI, drunkflag->val[0]);
|
|
wismod = getdrunkattrmod(lf, A_WIS, drunkflag->val[0]);
|
|
sprintf(agibuf, "%s%d Agi", (agimod >= 0) ? "+" : "", agimod);
|
|
sprintf(wisbuf, "%s%d Wis", (wismod >= 0) ? "+" : "", wismod);
|
|
|
|
// damage mod
|
|
if (drunkflag->val[0] == 1) {
|
|
sprintf(dambuf, "1 damage reduction");
|
|
} else {
|
|
sprintf(dambuf, "1-%d damage reduction", drunkflag->val[0]);
|
|
}
|
|
|
|
// full
|
|
sprintf(buf, "%s, %s, %s, mental immunity", dambuf, agibuf, wisbuf);
|
|
|
|
return buf;
|
|
}
|
|
|
|
char *getdrunktext(flag_t *drunkflag) {
|
|
int bracket;
|
|
bracket = (drunkflag->lifetime / TM_DRUNKTIME) + 1;
|
|
if (bracket == 1) {
|
|
return "tipsy";
|
|
} else if (bracket == 2) {
|
|
return "drunk";
|
|
} else {
|
|
return "very drunk";
|
|
}
|
|
return "??drunk??";
|
|
}
|
|
|
|
char *getilluminationdesc(enum ILLUMINATION il) {
|
|
switch (il) {
|
|
case IL_FULLLIT: return "This area is fully lit.";
|
|
case IL_WELLLIT: return "The light here is a little dim.";
|
|
case IL_DIM: return "The light here is very dim.";
|
|
case IL_SHADOWY: return "It is almost completely dark here.";
|
|
case IL_FULLDARK: return "It is completely dark here.";
|
|
}
|
|
return "xxx_unknown_illumination_xxx";
|
|
}
|
|
|
|
char *getinjuredbpname(enum BODYPART bp) {
|
|
switch (bp) {
|
|
case BP_HEAD: return "head";
|
|
case BP_HANDS: return "arm";
|
|
case BP_LEGS: return "leg";
|
|
default: break;
|
|
}
|
|
return "body";
|
|
}
|
|
char *getinjuryname(enum DAMTYPE dt) {
|
|
switch (dt) {
|
|
case DT_BASH: return "bruised";
|
|
case DT_SLASH: return "bleeding";
|
|
default: break;
|
|
|
|
}
|
|
return "injured";
|
|
}
|
|
char *getinjurydesc(enum BODYPART where, enum DAMTYPE dt) {
|
|
if (dt == DT_SLASH) {
|
|
if (where == BP_LEGS) {
|
|
return " (moving causes damage)";
|
|
} else if (where == BP_HANDS) {
|
|
return " (attacking causes damage)";
|
|
} else if (where == BP_BODY) {
|
|
return " (take extra damage from melee hits)";
|
|
}
|
|
} else if (dt == DT_BASH) {
|
|
if (where == BP_LEGS) {
|
|
return " (penalty to movement speed)";
|
|
} else if (where == BP_HANDS) {
|
|
return " (penalty to attack accuracy)";
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
// IMPORTANT:
|
|
// all strings returned here must also be defined as an obmod altprefix!
|
|
//
|
|
char *getobmodprefix(object_t *o, obmod_t *om) {
|
|
int enoughperception = B_TRUE;
|
|
int enoughwepskill = B_TRUE;
|
|
|
|
if (gamemode == GM_GAMESTARTED) {
|
|
if ( (getskill(player, SK_PERCEPTION) < PR_BEGINNER)) {
|
|
enoughperception = B_FALSE;
|
|
}
|
|
if (!getweaponskill(player, o)) {
|
|
enoughwepskill = B_FALSE;
|
|
}
|
|
}
|
|
|
|
// masterwork/shoddy doors have names based on material.
|
|
if (isdoor(o, NULL)) {
|
|
// player perceptive enough to notice?
|
|
if (om->id == OM_MASTERWORK) {
|
|
if (!enoughperception) return NULL;
|
|
switch (o->material->id) {
|
|
case MT_STONE: return "reinforced ";
|
|
case MT_METAL: return "reinforced ";
|
|
default: return "sturdy ";
|
|
}
|
|
} else if (om->id == OM_SHODDY) {
|
|
if (!enoughperception) return NULL;
|
|
switch (o->material->id) {
|
|
case MT_STONE: return "crumbling ";
|
|
case MT_METAL: return "rusted ";
|
|
default: return "rotted ";
|
|
}
|
|
}
|
|
} else if (isweapon(o)) {
|
|
skill_t *sk;
|
|
sk = getobskill(o->flags);
|
|
if (sk) {
|
|
if (!player || enoughwepskill || enoughperception) {
|
|
if (om->id == OM_MASTERWORK) {
|
|
switch (sk->id) {
|
|
case SK_CLUBS:
|
|
case SK_STAVES:
|
|
return "reinforced ";
|
|
default: break;
|
|
}
|
|
} else if (om->id == OM_SHODDY) {
|
|
switch (sk->id) {
|
|
case SK_LONGBLADES:
|
|
case SK_SHORTBLADES:
|
|
case SK_AXES:
|
|
return "blunted ";
|
|
case SK_CLUBS:
|
|
return "cracked ";
|
|
case SK_POLEARMS:
|
|
return "notched ";
|
|
case SK_STAVES:
|
|
return "blunted ";
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if (isshield(o)) {
|
|
if (!player || enoughperception || getskill(player, SK_SHIELDS)) {
|
|
if (om->id == OM_MASTERWORK) {
|
|
switch (o->material->id) {
|
|
case MT_LEATHER:
|
|
return "studded ";
|
|
case MT_METAL:
|
|
case MT_WOOD:
|
|
case MT_DRAGONWOOD:
|
|
return "reinforced ";
|
|
default: break;
|
|
}
|
|
} else if (om->id == OM_SHODDY) {
|
|
switch (o->material->id) {
|
|
case MT_LEATHER:
|
|
case MT_RUBBER:
|
|
case MT_PAPER:
|
|
return "torn ";
|
|
case MT_CLOTH:
|
|
case MT_SILK:
|
|
return "frayed ";
|
|
case MT_GLASS:
|
|
case MT_STONE:
|
|
case MT_BONE:
|
|
return "chipped ";
|
|
case MT_METAL:
|
|
return "dented ";
|
|
case MT_WOOD:
|
|
case MT_DRAGONWOOD:
|
|
return "splintered ";
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
} else if (isarmour(o)) {
|
|
if (!player || enoughperception || getskill(player, SK_ARMOUR)) {
|
|
if (om->id == OM_MASTERWORK) {
|
|
switch (o->material->id) {
|
|
case MT_LEATHER:
|
|
return "studded ";
|
|
case MT_CLOTH:
|
|
case MT_SILK:
|
|
return "tailored ";
|
|
default: break;
|
|
}
|
|
} else if (om->id == OM_SHODDY) {
|
|
switch (o->material->id) {
|
|
case MT_LEATHER:
|
|
case MT_RUBBER:
|
|
case MT_PAPER:
|
|
return "torn ";
|
|
case MT_CLOTH:
|
|
case MT_SILK:
|
|
return "frayed ";
|
|
case MT_GLASS:
|
|
case MT_STONE:
|
|
case MT_BONE:
|
|
return "chipped ";
|
|
case MT_METAL:
|
|
return "dented ";
|
|
case MT_WOOD:
|
|
case MT_DRAGONWOOD:
|
|
return "splintered ";
|
|
default: break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return om->prefix;
|
|
}
|
|
|
|
char *getrarityname(enum RARITY rr) {
|
|
switch (rr) {
|
|
case RR_UNIQUE: return "Unique";
|
|
case RR_NEVER: return "Never";
|
|
case RR_VERYRARE: return "Very Rare";
|
|
case RR_RARE: return "Rare";
|
|
case RR_UNCOMMON: return "Uncommon";
|
|
case RR_COMMON: return "Common";
|
|
case RR_FREQUENT: return "Frequent";
|
|
case RR_NONE: return "None";
|
|
}
|
|
|
|
return "?unknownrarity?";
|
|
}
|
|
|
|
// converts text to rarity value
|
|
enum RARITY getrarityval(char *name) {
|
|
enum RARITY wantrarity = RR_NONE;
|
|
if (streq(name,"frequent")) {
|
|
wantrarity = RR_FREQUENT;
|
|
} else if (streq(name,"common")) {
|
|
wantrarity = RR_COMMON;
|
|
} else if (streq(name,"uncommon")) {
|
|
wantrarity = RR_UNCOMMON;
|
|
} else if (streq(name,"rare")) {
|
|
wantrarity = RR_RARE;
|
|
} else if (streq(name,"very rare")) {
|
|
wantrarity = RR_VERYRARE;
|
|
}
|
|
return wantrarity;
|
|
}
|
|
|
|
// pass in EITHER m or r, not both.
|
|
//
|
|
// if how is anything other than RF_SHORT, "m" should be passed.
|
|
char *getregionname(char *buf, map_t *m, region_t *r, enum REGIONNAMEFORMAT how) {
|
|
int x = NA,y = NA;
|
|
if (!r) {
|
|
r = m->region;
|
|
}
|
|
|
|
if (m) {
|
|
flag_t *f;
|
|
f = hasflag(m->flags, F_MAPCOORDS);
|
|
if (f) {
|
|
x = f->val[0];
|
|
y = f->val[1];
|
|
}
|
|
}
|
|
|
|
if (m && (m->habitat->id == H_SWAMP)) {
|
|
if (how == RF_WITHLEVEL) {
|
|
snprintf(buf, BUFLEN, "the swamp");
|
|
} else if (how == RF_LONG) {
|
|
snprintf(buf, BUFLEN, "in the swamp");
|
|
} else { // ie. short
|
|
strcpy(buf, "the swamp");
|
|
}
|
|
} else if ((how == RF_WITHLEVEL) && m) {
|
|
switch (r->rtype->id) {
|
|
case BH_CAVE:
|
|
snprintf(buf, BUFLEN, "goblin caves L%d", m->depth);
|
|
break;
|
|
case BH_ICECAVERNS:
|
|
snprintf(buf, BUFLEN, "ice caverns L%d", m->depth);
|
|
break;
|
|
case BH_WOODS:
|
|
snprintf(buf, BUFLEN, "sylvan woods L%d", m->depth);
|
|
break;
|
|
case BH_WORLDMAP:
|
|
snprintf(buf, BUFLEN, "the surface(%d,%d)",x,y);
|
|
break;
|
|
case BH_MAINDUNGEON:
|
|
snprintf(buf, BUFLEN, "dungeon L%d", m->depth);
|
|
break;
|
|
case BH_MASTERVAULTS:
|
|
if (m->depth == 1) {
|
|
snprintf(buf, BUFLEN, "outer vault");
|
|
} if (m->depth == 2) {
|
|
snprintf(buf, BUFLEN, "inner vault");
|
|
} else {
|
|
snprintf(buf, BUFLEN, "master vault");
|
|
}
|
|
break;
|
|
case BH_HEAVEN:
|
|
snprintf(buf, BUFLEN, "the realm of gods");
|
|
break;
|
|
case BH_PIT:
|
|
snprintf(buf, BUFLEN, "a pit L%d", m->depth);
|
|
break;
|
|
case BH_SEWER:
|
|
snprintf(buf, BUFLEN, "a sewer L%d", m->depth);
|
|
break;
|
|
case BH_STOMACH:
|
|
snprintf(buf, BUFLEN, "a stomach");
|
|
break;
|
|
case BH_BABAYAGAHUT:
|
|
snprintf(buf, BUFLEN, "baba yaga's hut");
|
|
break;
|
|
}
|
|
} else if ((how == RF_LONG) && m) {
|
|
switch (r->rtype->id) {
|
|
case BH_CAVE:
|
|
snprintf(buf, BUFLEN, "on level %d of the goblin caves", m->depth);
|
|
break;
|
|
case BH_ICECAVERNS:
|
|
snprintf(buf, BUFLEN, "on level %d of the ice caverns", m->depth);
|
|
break;
|
|
case BH_WOODS:
|
|
snprintf(buf, BUFLEN, "on level %d of the sylvan woods", m->depth);
|
|
break;
|
|
case BH_WORLDMAP:
|
|
snprintf(buf, BUFLEN, "on the surface(%d,%d)",x,y);
|
|
break;
|
|
case BH_MAINDUNGEON:
|
|
snprintf(buf, BUFLEN, "on level %d of the dungeon", m->depth);
|
|
break;
|
|
case BH_MASTERVAULTS:
|
|
if (m->depth == 1) {
|
|
snprintf(buf, BUFLEN, "in the outer vault");
|
|
} if (m->depth == 2) {
|
|
snprintf(buf, BUFLEN, "in the inner vault");
|
|
} else {
|
|
snprintf(buf, BUFLEN, "in the master vault");
|
|
}
|
|
break;
|
|
case BH_HEAVEN:
|
|
snprintf(buf, BUFLEN, "in the realm of gods");
|
|
break;
|
|
case BH_PIT:
|
|
snprintf(buf, BUFLEN, "in a pit");
|
|
break;
|
|
case BH_SEWER:
|
|
snprintf(buf, BUFLEN, "in a sewer");
|
|
break;
|
|
case BH_STOMACH:
|
|
snprintf(buf, BUFLEN, "inside a worm's stomach"); // TODO: " in a stomach of of xxx"
|
|
break;
|
|
case BH_BABAYAGAHUT:
|
|
snprintf(buf, BUFLEN, "in baba yaga's hut");
|
|
break;
|
|
}
|
|
} else { // ie. RF_SHORT
|
|
switch (r->rtype->id) {
|
|
case BH_CAVE:
|
|
strcpy(buf, "the goblin caves");
|
|
break;
|
|
case BH_ICECAVERNS:
|
|
strcpy(buf, "the ice caverns");
|
|
break;
|
|
case BH_WOODS:
|
|
strcpy(buf, "the sylvan woods");
|
|
break;
|
|
case BH_WORLDMAP:
|
|
strcpy(buf, "the surface");
|
|
break;
|
|
case BH_MAINDUNGEON:
|
|
strcpy(buf, "the dungeon");
|
|
break;
|
|
case BH_MASTERVAULTS:
|
|
snprintf(buf, BUFLEN, "the master vaults");
|
|
break;
|
|
case BH_HEAVEN:
|
|
snprintf(buf, BUFLEN, "the realm of gods");
|
|
break;
|
|
case BH_PIT:
|
|
snprintf(buf, BUFLEN, "a pit");
|
|
break;
|
|
case BH_SEWER:
|
|
snprintf(buf, BUFLEN, "a sewer");
|
|
break;
|
|
case BH_STOMACH:
|
|
snprintf(buf, BUFLEN, "a stomach");
|
|
break;
|
|
case BH_BABAYAGAHUT:
|
|
snprintf(buf, BUFLEN, "baba yaga's hut");
|
|
break;
|
|
}
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
char *getreldirname(int reldir) {
|
|
switch (reldir) {
|
|
case RD_FORWARDS:
|
|
return "forwards";
|
|
case RD_BACKWARDS:
|
|
return "backwards";
|
|
case RD_SIDEWAYS:
|
|
return "sideways";
|
|
default: break;
|
|
}
|
|
return "away";
|
|
}
|
|
|
|
char *getsizetext(enum LFSIZE sz) {
|
|
switch (sz) {
|
|
case SZ_MAX:
|
|
case SZ_ENORMOUS:
|
|
return "enormous";
|
|
case SZ_HUGE:
|
|
return "huge";
|
|
case SZ_LARGE:
|
|
return "large";
|
|
case SZ_HUMAN:
|
|
return "human-sized";
|
|
case SZ_MEDIUM:
|
|
return "medium";
|
|
case SZ_SMALL:
|
|
return "small";
|
|
case SZ_TINY:
|
|
return "tiny";
|
|
case SZ_MIN:
|
|
case SZ_MINI:
|
|
return "miniscule";
|
|
default:
|
|
return "unknown-sized";
|
|
}
|
|
return "unknown-sized";
|
|
}
|
|
|
|
char *getschoolname(enum SPELLSCHOOL sch) {
|
|
switch (sch) {
|
|
case SS_ABILITY: return "Abilities";
|
|
case SS_ALLOMANCY: return "Allomancy";
|
|
case SS_DIVINE: return "Divine Powers";
|
|
case SS_WILD: return "Wild Magic";
|
|
case SS_MENTAL: return "Psionic Powers";
|
|
case SS_AIR: return "Air Magic";
|
|
case SS_FIRE: return "Fire Magic";
|
|
case SS_COLD: return "Cold Magic";
|
|
case SS_DEATH: return "Necromancy";
|
|
case SS_NATURE: return "Enviromancy";
|
|
case SS_LIFE: return "Life Magic";
|
|
case SS_DIVINATION: return "Divination Magic";
|
|
case SS_TRANSLOCATION: return "Translocation Magic";
|
|
case SS_SUMMONING: return "Summoning Magic";
|
|
default:
|
|
break;
|
|
}
|
|
return "badschool";
|
|
}
|
|
|
|
char *getschoolnameshort(enum SPELLSCHOOL sch) {
|
|
switch (sch) {
|
|
case SS_ABILITY: return "Abilities";
|
|
case SS_ALLOMANCY: return "Allomancy";
|
|
case SS_DIVINE: return "Divine Powers";
|
|
case SS_WILD: return "Wild Magic";
|
|
case SS_AIR: return "Air Magic";
|
|
case SS_FIRE: return "Fire Magic";
|
|
case SS_COLD: return "Cold Magic";
|
|
case SS_DEATH: return "Necromancy";
|
|
case SS_LIFE: return "Life Magic";
|
|
case SS_MENTAL: return "Psionic Powers";
|
|
case SS_NATURE: return "Nature";
|
|
case SS_DIVINATION: return "Divination";
|
|
case SS_TRANSLOCATION: return "Translocation";
|
|
case SS_SUMMONING: return "Summoning";
|
|
case SS_LAST: return "!invalid school!";
|
|
default:
|
|
break;
|
|
}
|
|
return "unknown school";
|
|
}
|
|
|
|
char *getskillcheckname(enum CHECKTYPE ct) {
|
|
switch (ct) {
|
|
case SC_STR:
|
|
return "Strength";
|
|
case SC_DEX:
|
|
return "Agility";
|
|
case SC_IQ:
|
|
return "IQ";
|
|
case SC_CON:
|
|
return "Fitness";
|
|
case SC_CHA:
|
|
return "Charisma";
|
|
case SC_WIS:
|
|
return "Wisdom";
|
|
case SC_CLIMB:
|
|
return "Climb";
|
|
case SC_DISARM:
|
|
return "Disarm";
|
|
case SC_DODGE:
|
|
return "Dodge";
|
|
case SC_SHIELDBLOCK:
|
|
return "Shieldblock";
|
|
case SC_FALL:
|
|
return "Fall";
|
|
case SC_SLIP:
|
|
return "Slip";
|
|
case SC_LEARNMAGIC:
|
|
return "Learnmagic";
|
|
case SC_LISTEN:
|
|
return "Listen";
|
|
case SC_MORALE:
|
|
return "Morale";
|
|
case SC_OPENLOCKS:
|
|
return "Open locks";
|
|
case SC_POISON:
|
|
return "Poison";
|
|
case SC_RESISTMAG:
|
|
return "Resistmag";
|
|
case SC_SEARCH:
|
|
return "Search";
|
|
case SC_SPEECH:
|
|
return "Speech";
|
|
case SC_STEAL:
|
|
return "Thievery/Steal";
|
|
case SC_STEALTH:
|
|
return "Stealth";
|
|
case SC_TUMBLE:
|
|
return "Tumble";
|
|
case SC_WILL:
|
|
return "Will";
|
|
default:
|
|
break;
|
|
}
|
|
return "?unknown_skillcheck?";
|
|
}
|
|
|
|
char *gettimephasename(enum TIMEPHASE tp) {
|
|
switch (tp) {
|
|
case TP_SUNRISE: return "sunrise";
|
|
case TP_MORNING: return "morning";
|
|
case TP_MORNINGLATE: return "late morning";
|
|
case TP_NOON: return "Noon";
|
|
case TP_AFTERNOON: return "afternoon";
|
|
case TP_DUSK: return "dusk";
|
|
case TP_SUNSET: return "sunset";
|
|
case TP_EVENING: return "evening";
|
|
case TP_NIGHT: return "night";
|
|
case TP_MIDNIGHT: return "Midnight";
|
|
case TP_NIGHTLATE: return "late night";
|
|
case TP_TWILIGHTMORN: return "morning twilight";
|
|
case TP_DAWN: return "dawn";
|
|
default: break;
|
|
}
|
|
return "unknown_timephase";
|
|
}
|
|
|
|
char *gettemperaturename(enum TEMPERATURE temp) {
|
|
switch (temp) {
|
|
case T_VCOLD: return "extremely cold";
|
|
case T_COLD: return "cold";
|
|
case T_CHILLY: return "a bit chilly";
|
|
case T_NORMAL: return "comfortable";
|
|
case T_WARM: return "a bit warm";
|
|
case T_HOT: return "hot";
|
|
case T_VHOT: return "extremely hot";
|
|
}
|
|
return "?unknowntemp?";
|
|
}
|
|
|
|
char *gettimetext(char *retbuf) {
|
|
int hours,mins,secs;
|
|
splittime(&hours, &mins, &secs);
|
|
|
|
snprintf(retbuf, BUFLEN, "%02d:%02d:%02d",hours,mins,secs);
|
|
return retbuf;
|
|
}
|
|
|
|
char *gettimetextfuzzy(char *retbuf, int wantpm) {
|
|
int hours,mins,secs;
|
|
int pm = B_FALSE;
|
|
splittime(&hours, &mins, &secs);
|
|
|
|
if (hours > 12) {
|
|
hours -= 12;
|
|
pm = B_TRUE;
|
|
}
|
|
|
|
if (hours == 0) hours = 12;
|
|
|
|
if (mins == 0) {
|
|
snprintf(retbuf, BUFLEN, "exactly %d o'clock", hours);
|
|
} else if (mins <= 15) {
|
|
snprintf(retbuf, BUFLEN, "a little after %d o'clock", hours);
|
|
} else if (mins <= 25) {
|
|
snprintf(retbuf, BUFLEN, "nearly half past %d", hours);
|
|
} else if (mins <= 35) {
|
|
snprintf(retbuf, BUFLEN, "around half past %d", hours);
|
|
} else if (mins <= 45) {
|
|
snprintf(retbuf, BUFLEN, "coming up to %d o'clock", (hours == 12) ? 1 : (hours+1));
|
|
} else {
|
|
snprintf(retbuf, BUFLEN, "nearly %d o'clock", (hours == 12) ? 1 : (hours+1));
|
|
}
|
|
|
|
if (wantpm) {
|
|
strcat(retbuf, " in the ");
|
|
if (pm) {
|
|
strcat(retbuf, "afternoon");
|
|
} else {
|
|
strcat(retbuf, "morning");
|
|
}
|
|
}
|
|
|
|
return retbuf;
|
|
}
|
|
|
|
char *getwaterdepthname(enum DEPTH d) {
|
|
switch (d) {
|
|
case DP_NONE:
|
|
return "shallow";
|
|
case DP_TOE:
|
|
return "toe-deep";
|
|
case DP_ANKLE:
|
|
return "ankle-deep";
|
|
case DP_FEET:
|
|
return "foot-deep";
|
|
case DP_CALF:
|
|
return "calf-deep";
|
|
case DP_KNEE:
|
|
return "knee-deep";
|
|
case DP_THIGH:
|
|
return "thigh-deep";
|
|
case DP_WAIST:
|
|
return "waist-deep";
|
|
case DP_BELLY:
|
|
return "belly-deep";
|
|
case DP_CHEST:
|
|
return "chest-deep";
|
|
case DP_SHOULDERS:
|
|
return "shoulder-deep";
|
|
default:
|
|
return "very deep";
|
|
}
|
|
return "?unknowndepth?";
|
|
}
|
|
|
|
char *getweighttext(float weight, char *buf, int shortfmt) {
|
|
if (weight == 0) {
|
|
if (shortfmt) snprintf(buf, BUFLEN, "0kg");
|
|
else snprintf(buf, BUFLEN, "nothing");
|
|
} else if (weight >= 1) {
|
|
if ((int)weight == weight) { // ie. is weight an integer?
|
|
snprintf(buf, BUFLEN, "%0.0f%skg",weight, shortfmt ? "" : " ");
|
|
} else {
|
|
snprintf(buf, BUFLEN, "%0.1f%skg",weight, shortfmt ? "" : " ");
|
|
}
|
|
} else {
|
|
snprintf(buf, BUFLEN, "%0.0f%s", weight * 1000, shortfmt ? "g" : " grams");
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
int hoursto12(int h) {
|
|
int twelveh;
|
|
if (h > 12) {
|
|
twelveh = h - 12;
|
|
} else {
|
|
twelveh = h;
|
|
}
|
|
if (twelveh == 0) twelveh = 12;
|
|
return twelveh;
|
|
}
|
|
|
|
char *is(lifeform_t *lf) {
|
|
if (isplayer(lf)) return "are";
|
|
else return "is";
|
|
}
|
|
|
|
char *it(lifeform_t *lf) {
|
|
if (isplayer(lf)) return "you";
|
|
|
|
switch (getgender(lf)) {
|
|
case G_MALE: return "him";
|
|
case G_FEMALE: return "her";
|
|
default: break;
|
|
}
|
|
|
|
return "it";
|
|
}
|
|
|
|
int isplural(char *text) {
|
|
if (text[strlen(text)-1] == 's') {
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
int isvowel (char c) {
|
|
switch (c) {
|
|
case 'a':
|
|
case 'e':
|
|
case 'i':
|
|
case 'o':
|
|
case 'u':
|
|
return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
void killplural(plural_t *w) {
|
|
plural_t *nextone, *lastone;
|
|
|
|
// free mem
|
|
if (w->singular) free(w->singular);
|
|
if (w->plural) free(w->plural);
|
|
|
|
// remove from list
|
|
nextone = w->next;
|
|
if (nextone != NULL) {
|
|
nextone->prev = w->prev;
|
|
} else { /* last */
|
|
lastplural = w->prev;
|
|
}
|
|
|
|
if (w->prev == NULL) {
|
|
/* first */
|
|
nextone = w->next;
|
|
free(firstplural);
|
|
firstplural = nextone;
|
|
} else {
|
|
lastone = w->prev;
|
|
free (lastone->next );
|
|
lastone->next = nextone;
|
|
}
|
|
}
|
|
|
|
// return text for player's F_GUNTARGET flag eg. "goblin [acc:50%]"
|
|
void makegunaimstring(lifeform_t *lf, int lfid, char *retbuf) {
|
|
char accbuf[BUFLEN];
|
|
char targname[BUFLEN];
|
|
flag_t *f;
|
|
lifeform_t *targ;
|
|
targ = findlf(lf->cell->map, lfid);
|
|
if (!targ) {
|
|
strcpy(retbuf, "");
|
|
return;
|
|
}
|
|
|
|
getlfname(targ, targname);
|
|
|
|
f = addflag(lf->flags, F_THROWING, B_TRUE, NA, NA, NULL);
|
|
makethrowaccstring(lf, targ->cell, f, accbuf);
|
|
killflagsofid(lf->flags, F_THROWING);
|
|
|
|
sprintf(retbuf, "%s%s", noprefix(targname), accbuf);
|
|
}
|
|
|
|
char *makekillertext(char *retbuf, char *killverb, char *lastdam, map_t *where, int wantextra, int wantlocation) {
|
|
char *p, *dummy;
|
|
char regionbuf[BUFLEN];
|
|
|
|
if (wintype) {
|
|
flag_t *winflag;
|
|
winflag = lfhasflag(player, F_WINNER);
|
|
if (winflag->val[0] == WT_GOD) {
|
|
sprintf(retbuf, "Crowned the God of %s.", winflag->text); // ie. "Became the God of Fire & Destruction"
|
|
} else if (winflag->val[0] == WT_DEMIGOD) {
|
|
sprintf(retbuf, "Ascended to Demigod-hood.");
|
|
}
|
|
return retbuf;
|
|
}
|
|
|
|
p = strtok_r(lastdam,"^", &dummy);
|
|
if (p) {
|
|
if (streq(p, "you")) {
|
|
strcpy(retbuf, "Committed suicide");
|
|
} else {
|
|
snprintf(retbuf, BUFLEN, "%s %s %s",killverb,
|
|
streq(killverb, "Drowned") ? "in" : "by",
|
|
p);
|
|
}
|
|
if (wantextra) {
|
|
p = strtok_r(NULL, "^", &dummy);
|
|
while (p) {
|
|
strcat(retbuf, "\n(");
|
|
strcat(retbuf, p);
|
|
strcat(retbuf, ")");
|
|
p = strtok_r(NULL, "^", &dummy);
|
|
}
|
|
}
|
|
} else {
|
|
sprintf(retbuf, "%s by something unknown", killverb);
|
|
}
|
|
|
|
if (wantlocation) {
|
|
// now include WHERE they died.
|
|
getregionname(regionbuf, where, NULL, RF_LONG);
|
|
strcat(retbuf, " ");
|
|
strcat(retbuf, regionbuf);
|
|
}
|
|
strcat(retbuf, ".");
|
|
return retbuf;
|
|
}
|
|
|
|
char *makelowercase(char *text) {
|
|
if (strlen(text) > 0) {
|
|
char *p;
|
|
for (p = text ; *p; p++) {
|
|
*p = tolower(*p);
|
|
}
|
|
}
|
|
return text;
|
|
}
|
|
|
|
// allocates and returns new string
|
|
char *makeplural(char *text) {
|
|
char lastlet;
|
|
char *newtext;
|
|
int rv;
|
|
plural_t *pl;
|
|
|
|
newtext = strdup(text);
|
|
|
|
for (pl = firstplural ; pl ; pl = pl->next) {
|
|
strrep(&newtext, pl->singular, pl->plural, &rv);
|
|
if (rv && pl->stopafter) return newtext;
|
|
}
|
|
|
|
// default
|
|
lastlet = text[strlen(text)-1];
|
|
switch (lastlet) {
|
|
char *temptext;
|
|
case 'y': // change to 'ies'
|
|
temptext = strdup(text);
|
|
temptext[strlen(temptext)-1] = '\0';
|
|
asprintf(&newtext, "%sies",temptext);
|
|
free(temptext);
|
|
break;
|
|
case 's':
|
|
case 'o': // append "es"
|
|
asprintf(&newtext, "%ses",text);
|
|
break;
|
|
default: // append "s"
|
|
asprintf(&newtext, "%ss",text);
|
|
break;
|
|
}
|
|
return newtext;
|
|
}
|
|
|
|
// throwflag should be either a F_THROWING or a F_FIRING flag.
|
|
char *makethrowaccstring(lifeform_t *lf, cell_t *c, flag_t *throwflag, char *retbuf) {
|
|
object_t *o = NULL, *gun = NULL;
|
|
int acc = 0;
|
|
strcpy(retbuf, "");
|
|
if (strlen(throwflag->text)) {
|
|
// get the object being thrown
|
|
o = findobbyid(lf->pack, atol(throwflag->text));
|
|
} else { // ie. firing a gun
|
|
gun = getfirearm(lf);
|
|
if (!gun) return NULL;
|
|
o = getammo(gun);
|
|
}
|
|
if (!o) return NULL;
|
|
|
|
acc = getmissileaccuracy(lf, c, o, gun, lfhasflag(player, F_TKTHROW)) ;
|
|
if (lfhasflag(lf, F_EXTRAINFO)) {
|
|
sprintf(retbuf, "^%d [acc:%d%%]^n", getpctcol(acc,100), acc);
|
|
} else {
|
|
sprintf(retbuf, "^%d [acc:%c]^n", getpctcol(acc,100), getpctletter(acc,100));
|
|
}
|
|
return retbuf;
|
|
}
|
|
|
|
char *makeuppercase(char *text) {
|
|
if (strlen(text) > 0) {
|
|
char *p;
|
|
for (p = text ; *p; p++) {
|
|
*p = toupper(*p);
|
|
}
|
|
}
|
|
return text;
|
|
}
|
|
|
|
// return something like:
|
|
// "over the shoulders"
|
|
// or
|
|
// "on your body, over your eyes and on your legs"
|
|
char *makewearstring(lifeform_t *lf, object_t *o, int wantyour, char *posbuf) {
|
|
flag_t *retflag[MAXCANDIDATES];
|
|
char yourbuf[BUFLEN];
|
|
int nretflags = 0,i;
|
|
if (wantyour) {
|
|
if (!lf) {
|
|
strcpy(yourbuf, "the ");
|
|
} else if (isplayer(lf)) {
|
|
strcpy(yourbuf, "your ");
|
|
} else {
|
|
strcpy(yourbuf, "its ");
|
|
}
|
|
} else {
|
|
strcpy(yourbuf, "");
|
|
}
|
|
getflags(o->flags, retflag, &nretflags, F_EQUIPPED, F_NONE);
|
|
for (i = 0; i < nretflags; i++) {
|
|
char thisposbuf[BUFLEN];
|
|
|
|
makewearstringsingle(lf, retflag[i], yourbuf, thisposbuf);
|
|
|
|
if (i == 0) {
|
|
strcpy(posbuf, thisposbuf);
|
|
} else if (i == (nretflags - 1)) {
|
|
strcat(posbuf, " and ");
|
|
strcat(posbuf, thisposbuf);
|
|
} else {
|
|
strcat(posbuf, ", ");
|
|
strcat(posbuf, thisposbuf);
|
|
}
|
|
}
|
|
return posbuf;
|
|
}
|
|
|
|
// return something like:
|
|
// "on your body"
|
|
// OR
|
|
// "over the eyes"
|
|
char *makewearstringsingle(lifeform_t *lf, flag_t *f, char *yourbuf , char *posbuf) {
|
|
enum BODYPART bp;
|
|
bp = f->val[0];
|
|
sprintf(posbuf, "%s %s%s", getbodypartequipname(bp), yourbuf, getbodypartname(lf, bp));
|
|
return posbuf;
|
|
}
|
|
|
|
int needses(char *text) {
|
|
if (text[strlen(text)-1] == 's') {
|
|
return B_TRUE;
|
|
}
|
|
|
|
if (strlen(text) >= 2) {
|
|
if ((text[strlen(text)-2] == 'c') &&
|
|
(text[strlen(text)-1] == 'h')) {
|
|
return B_TRUE;
|
|
}
|
|
}
|
|
|
|
return B_FALSE;
|
|
}
|
|
|
|
// remove 'the ', 'an ', 'a ', '1 ' etc
|
|
char *noprefix(char *obname) {
|
|
char *p;
|
|
int donesomething = B_TRUE;
|
|
p = obname;
|
|
while (donesomething) {
|
|
donesomething = B_FALSE;
|
|
if (strcasestarts(p, "your ")) {
|
|
p += strlen("your ");
|
|
donesomething = B_TRUE;
|
|
}
|
|
if (strcasestarts(p, "the ")) {
|
|
p += strlen("the ");
|
|
donesomething = B_TRUE;
|
|
}
|
|
if (strcasestarts(p, "an ")) {
|
|
p += strlen("an ");
|
|
donesomething = B_TRUE;
|
|
}
|
|
if (strcasestarts(p, "a ")) {
|
|
p += strlen("a ");
|
|
donesomething = B_TRUE;
|
|
}
|
|
if (isdigit(*p)) {
|
|
// skip to after the first space
|
|
while (isdigit(*p) || (*p == ' ')) {
|
|
p++;
|
|
}
|
|
donesomething = B_TRUE;
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
|
|
char *numtotext(int num, char *buf) {
|
|
switch (num) {
|
|
case 1:
|
|
snprintf(buf, BUFLEN, "a");
|
|
break;
|
|
case 2:
|
|
snprintf(buf, BUFLEN, "two");
|
|
break;
|
|
case 3:
|
|
snprintf(buf, BUFLEN, "three");
|
|
break;
|
|
case 4:
|
|
snprintf(buf, BUFLEN, "four");
|
|
break;
|
|
case 5:
|
|
snprintf(buf, BUFLEN, "five");
|
|
break;
|
|
case 6:
|
|
snprintf(buf, BUFLEN, "six");
|
|
break;
|
|
case 7:
|
|
snprintf(buf, BUFLEN, "seven");
|
|
break;
|
|
case 8:
|
|
snprintf(buf, BUFLEN, "eight");
|
|
break;
|
|
case 9:
|
|
snprintf(buf, BUFLEN, "nine");
|
|
break;
|
|
case 10:
|
|
snprintf(buf, BUFLEN, "ten");
|
|
break;
|
|
case 11:
|
|
snprintf(buf, BUFLEN, "eleven");
|
|
break;
|
|
case 12:
|
|
snprintf(buf, BUFLEN, "twelve");
|
|
break;
|
|
case 13:
|
|
snprintf(buf, BUFLEN, "thirteen");
|
|
break;
|
|
case 14:
|
|
snprintf(buf, BUFLEN, "fourteen");
|
|
break;
|
|
case 15:
|
|
snprintf(buf, BUFLEN, "fifteen");
|
|
break;
|
|
case 16:
|
|
snprintf(buf, BUFLEN, "sixteen");
|
|
break;
|
|
case 17:
|
|
snprintf(buf, BUFLEN, "seventeen");
|
|
break;
|
|
case 18:
|
|
snprintf(buf, BUFLEN, "eighteen");
|
|
break;
|
|
case 19:
|
|
snprintf(buf, BUFLEN, "nineteen");
|
|
break;
|
|
case 20:
|
|
snprintf(buf, BUFLEN, "twenty");
|
|
break;
|
|
default:
|
|
snprintf(buf, BUFLEN, "%d",num);
|
|
break;
|
|
}
|
|
return buf;
|
|
}
|
|
|
|
// returns a value in the range specified by rangetext ("xx-yy")
|
|
int parserange(char *rangetext) {
|
|
int min,max;
|
|
char buf[BUFLEN];
|
|
char *p;
|
|
p = readuntil(buf, rangetext, '-');
|
|
min = atoi(buf);
|
|
readuntil(buf, p, '^'); // really EOL
|
|
max = atoi(buf);
|
|
return rnd(min,max);
|
|
}
|
|
|
|
// returns posiiton AFTER end of copied text, or NULL on failure.
|
|
char *readuntil(char *retbuf, char *src, char delim) {
|
|
char *bp,*p;
|
|
bp = retbuf;
|
|
for (p=src; *p && (*p != delim); p++) {
|
|
*bp = *p;
|
|
bp++;
|
|
}
|
|
*bp = '\0'; // nul-terminate buffer
|
|
if (*p == delim) {
|
|
p++; // go past delimiter
|
|
}
|
|
return p;
|
|
}
|
|
|
|
// convert number to roman numerals
|
|
// only copes with 1-10
|
|
char *roman(int num) {
|
|
switch (num) {
|
|
case 1:
|
|
return "I";
|
|
case 2:
|
|
return "II";
|
|
case 3:
|
|
return "III";
|
|
case 4:
|
|
return "IV";
|
|
case 5:
|
|
return "V";
|
|
case 6:
|
|
return "VI";
|
|
case 7:
|
|
return "VII";
|
|
case 8:
|
|
return "VIII";
|
|
case 9:
|
|
return "IX";
|
|
case 10:
|
|
return "X";
|
|
}
|
|
return "";
|
|
}
|
|
|
|
int speedtokph(int speed) {
|
|
return speed * speed;
|
|
}
|
|
|
|
void splittime(int *hours, int *mins, int *secs) {
|
|
long left;
|
|
left = curtime;
|
|
|
|
*hours = left / 3600;
|
|
left -= (*hours * 3600);
|
|
*mins = left / 60;
|
|
left -= (*mins * 60);
|
|
*secs = left;
|
|
}
|
|
|
|
/*
|
|
char *strrep(char *text, char *oldtok, char *newtok, int *rv) {
|
|
char *temp;
|
|
temp = strdup(" "); // ooooooo is this bad??
|
|
dostrrep(text, &temp, oldtok, newtok, rv);
|
|
// swap
|
|
text = realloc(text, strlen(temp)+1); // extra space for NUL
|
|
strcpy(text, temp);
|
|
free(temp);
|
|
return text;
|
|
}
|
|
|
|
// returns TRUE if any replacements made
|
|
char *dostrrep(char* in, char** out, char* oldtok, char* newtok, int *rv) {
|
|
char *temp;
|
|
char *found;
|
|
char *newout = NULL;
|
|
int idx;
|
|
found = strstr(in, oldtok);
|
|
if(!found) {
|
|
newout = calloc(1, strlen(in)+1);
|
|
strcpy(newout, in);
|
|
|
|
free(*out);
|
|
*out = newout;
|
|
|
|
if (rv) *rv = B_FALSE;
|
|
return *out;
|
|
}
|
|
|
|
idx = found - in;
|
|
|
|
newout = calloc(1, strlen(in) - strlen(oldtok) + strlen(newtok) + 1);
|
|
|
|
strncpy(newout, in, idx);
|
|
strcpy(newout + idx, newtok);
|
|
strcpy(newout + idx + strlen(newtok), in + idx + strlen(oldtok));
|
|
|
|
temp = malloc(idx+strlen(newtok)+1);
|
|
strncpy(temp,newout,idx+strlen(newtok));
|
|
temp[idx + strlen(newtok)] = '\0';
|
|
|
|
free(*out);
|
|
*out = newout;
|
|
|
|
|
|
dostrrep(found + strlen(oldtok), out, oldtok, newtok, rv);
|
|
|
|
|
|
newout = calloc(1,strlen(temp) + strlen(*out) + 1);
|
|
strcpy(newout, temp);
|
|
strcat(newout,*out);
|
|
free(temp);
|
|
|
|
free(*out);
|
|
*out = newout;
|
|
|
|
if (rv) *rv = B_TRUE;
|
|
return *out;
|
|
}
|
|
*/
|
|
|
|
|
|
// returns a NEW string: copy of s1 but with occurences of s2 replaced by s3.
|
|
// Taken from https://gist.github.com/975639
|
|
// Written in 2011 by Drew Hess <dhess-src@bothan.net>.
|
|
//
|
|
|
|
char *do_strrep(const char *s1, const char *s2, const char *s3) {
|
|
if (!s1 || !s2 || !s3)
|
|
return NULL;
|
|
size_t s1_len = strlen(s1);
|
|
if (!s1_len)
|
|
//return (char *)s1;
|
|
return NULL;
|
|
size_t s2_len = strlen(s2);
|
|
if (!s2_len)
|
|
//return (char *)s1;
|
|
return NULL;
|
|
|
|
/*
|
|
* Two-pass approach: figure out how much space to allocate for
|
|
* the new string, pre-allocate it, then perform replacement(s).
|
|
*/
|
|
|
|
size_t count = 0;
|
|
const char *p = s1;
|
|
assert(s2_len); /* otherwise, strstr(s1,s2) will return s1. */
|
|
do {
|
|
p = strstr(p, s2);
|
|
if (p) {
|
|
p += s2_len;
|
|
++count;
|
|
}
|
|
} while (p);
|
|
|
|
if (!count)
|
|
return (char *)s1;
|
|
|
|
/*
|
|
* The following size arithmetic is extremely cautious, to guard
|
|
* against size_t overflows.
|
|
*/
|
|
assert(s1_len >= count * s2_len);
|
|
assert(count);
|
|
size_t s1_without_s2_len = s1_len - count * s2_len;
|
|
size_t s3_len = strlen(s3);
|
|
size_t newstr_len = s1_without_s2_len + count * s3_len;
|
|
if (s3_len &&
|
|
((newstr_len <= s1_without_s2_len) || (newstr_len + 1 == 0)))
|
|
/* Overflow. */
|
|
return 0;
|
|
|
|
char *newstr = (char *)malloc(newstr_len + 1); /* w/ terminator */
|
|
if (!newstr)
|
|
/* ENOMEM, but no good way to signal it. */
|
|
return 0;
|
|
|
|
char *dst = newstr;
|
|
const char *start_substr = s1;
|
|
size_t i;
|
|
for (i = 0; i != count; ++i) {
|
|
const char *end_substr = strstr(start_substr, s2);
|
|
assert(end_substr);
|
|
size_t substr_len = end_substr - start_substr;
|
|
memcpy(dst, start_substr, substr_len);
|
|
dst += substr_len;
|
|
memcpy(dst, s3, s3_len);
|
|
dst += s3_len;
|
|
start_substr = end_substr + s2_len;
|
|
}
|
|
|
|
/* copy remainder of s1, including trailing '\0' */
|
|
size_t remains = s1_len - (start_substr - s1) + 1;
|
|
assert(dst + remains == newstr + newstr_len + 1);
|
|
memcpy(dst, start_substr, remains);
|
|
assert(strlen(newstr) == newstr_len);
|
|
return newstr;
|
|
}
|
|
|
|
|
|
// reallocates text
|
|
// returns TRUE if replacements were made.
|
|
char *strrep(char **text, char *oldtok, char *newtok, int *rv) {
|
|
char *temp;
|
|
if (rv) *rv = B_FALSE;
|
|
temp = do_strrep(*text, oldtok, newtok);
|
|
if (!temp) {
|
|
// failed
|
|
return *text;
|
|
}
|
|
if (streq(temp, *text)) {
|
|
// no change
|
|
return *text;
|
|
}
|
|
// there WAS a change. free original pointer contents and
|
|
// point at new text instead.
|
|
if (rv) *rv = B_TRUE;
|
|
free(*text);
|
|
*text = temp;
|
|
return *text;
|
|
}
|
|
|
|
int streq(char *a, char *b) {
|
|
if (!a || !b) return B_FALSE;
|
|
return !strcmp(a,b);
|
|
}
|
|
|
|
char *strends(char *a, char *suffix) {
|
|
char *ep;
|
|
if (!a || !suffix) return NULL;
|
|
|
|
ep = strstr(a, suffix);
|
|
if (ep) {
|
|
if ((ep - a) + strlen(ep) == strlen(a)) {
|
|
return ep;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
material_t *strmatchesmaterial(char *p) {
|
|
material_t *m;
|
|
char searchfor[BUFLEN];
|
|
for (m = material ; m ; m = m->next) {
|
|
sprintf(searchfor, "%s ", m->name);
|
|
if (strstarts(p, searchfor)) return m;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
char *strcasestarts(char *a, char *prefix) {
|
|
if (!a || !prefix) return NULL;
|
|
|
|
if (strcasestr(a, prefix) == a) {
|
|
return a;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
char *strstarts(char *a, char *prefix) {
|
|
if (!a || !prefix) return NULL;
|
|
|
|
if (strstr(a, prefix) == a) {
|
|
return a;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
// if string starts with 'a ', 'an ' or 'the ', then return the position after it,
|
|
// and write the prefix into 'prefix'.
|
|
// otherwise return null.
|
|
//
|
|
char *strstartswitha(char *text, char *retprefix) {
|
|
char *prefix[] = {
|
|
"the ",
|
|
"an ",
|
|
"a "
|
|
};
|
|
int nprefixes = 3,i;
|
|
for (i = 0; i < nprefixes; i++) {
|
|
if (strstarts(text, prefix[i])) {
|
|
if (retprefix) {
|
|
strcpy(retprefix, prefix[i]);
|
|
}
|
|
return text + strlen(prefix[i]);
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int strlen_without_colours(char *buf) {
|
|
char *p;
|
|
int len = 0;
|
|
enum {
|
|
S_NORM,
|
|
S_COLOURDEF,
|
|
S_COLOURDEFNUM,
|
|
} state = S_NORM;
|
|
|
|
for (p = buf ; *p; p++) {
|
|
if (state == S_NORM) {
|
|
if (*p == '^') {
|
|
state = S_COLOURDEF;
|
|
} else {
|
|
len++;
|
|
}
|
|
} else if (state == S_COLOURDEF) {
|
|
if (isdigit(*p)) {
|
|
state = S_COLOURDEFNUM;
|
|
} else {
|
|
state = S_NORM;
|
|
}
|
|
} else { // ie. colourdefnum
|
|
if (!isdigit(*p)) {
|
|
state = S_NORM;
|
|
len++;
|
|
}
|
|
}
|
|
}
|
|
return len;
|
|
}
|
|
|
|
int strpixmatch(char *haystack, char *needle) {
|
|
int matched = B_FALSE;
|
|
char *hword, *nword, *hcont,*ncont;
|
|
|
|
if (strchr(needle, ' ') || strchr(haystack, ' ')) {
|
|
char lochaystack[BUFLEN], locneedle[BUFLEN];
|
|
strcpy(lochaystack, haystack);
|
|
strcpy(locneedle, needle);
|
|
|
|
// match word for word
|
|
nword = strtok_r(locneedle, " ", &ncont);
|
|
hword = strtok_r(lochaystack, " ", &hcont);
|
|
while (nword && hword) {
|
|
// all typed words must match
|
|
if (strcasestr(hword, nword)) {
|
|
matched = B_TRUE;
|
|
} else {
|
|
matched = B_FALSE;
|
|
break;
|
|
}
|
|
|
|
nword = strtok_r(NULL, " ", &ncont);
|
|
hword = strtok_r(NULL, " ", &hcont);
|
|
if (nword && !hword) {
|
|
matched = B_FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
if (!matched && !strchr(needle, ' ')) {
|
|
// now try matching typed word against second word in spellname
|
|
strcpy(lochaystack, haystack);
|
|
hword = strtok_r(lochaystack, " ", &hcont);
|
|
while (hword) {
|
|
if (strcasestr(hword, needle)) {
|
|
matched = B_TRUE;
|
|
break;
|
|
} else {
|
|
matched = B_FALSE;
|
|
}
|
|
|
|
hword = strtok_r(NULL, " ", &hcont);
|
|
if (!hword) {
|
|
matched = B_FALSE;
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
} else {
|
|
if (strcasestr(haystack, needle)) {
|
|
matched = B_TRUE;
|
|
}
|
|
}
|
|
return matched;
|
|
}
|
|
|
|
enum VAULTTHING strtovt(char *text) {
|
|
enum VAULTTHING tt;
|
|
if (streq(text, "ob")) {
|
|
tt = VT_OB;
|
|
} else if (streq(text, "mon")) {
|
|
tt = VT_LF;
|
|
} else if (streq(text, "cell")) {
|
|
tt = VT_CELL;
|
|
} else {
|
|
tt = VT_NONE;
|
|
}
|
|
return tt;
|
|
}
|
|
|
|
int texttodice(char *text, int *ndice, int *nsides, int *bonus) {
|
|
char *dummy;
|
|
char *localtext;
|
|
char *p,*plusloc;
|
|
localtext = strdup(text);
|
|
// number of dice
|
|
if (strchr(localtext, 'd')) {
|
|
p = strtok_r(localtext, "d", &dummy);
|
|
} else {
|
|
// assume it's just a single number
|
|
*ndice = 0;
|
|
*nsides = 0;
|
|
*bonus = atoi(text);
|
|
free(localtext);
|
|
return B_FALSE;
|
|
}
|
|
if (ndice) {
|
|
*ndice = atoi(p);
|
|
}
|
|
// sides on each die
|
|
p = strtok_r(NULL, "d", &dummy);
|
|
if (!p) {
|
|
free(localtext);
|
|
return B_TRUE;
|
|
}
|
|
|
|
// strip out bonus
|
|
plusloc = strchr(p, '+');
|
|
if (plusloc) *plusloc = '\0';
|
|
plusloc = strchr(p, '-');
|
|
if (plusloc) *plusloc = '\0';
|
|
|
|
if (nsides) {
|
|
*nsides = atoi(p);
|
|
}
|
|
|
|
|
|
free(localtext);
|
|
localtext = strdup(text);
|
|
// bonus/plus
|
|
if (bonus) {
|
|
p = strchr(localtext, '+');
|
|
if (p) {
|
|
*bonus = atoi(p+1);
|
|
} else {
|
|
p = strchr(localtext, '-');
|
|
if (p) {
|
|
*bonus = -(atoi(p+1));
|
|
} else {
|
|
*bonus = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
free(localtext);
|
|
return B_FALSE;
|
|
}
|
|
|
|
/*
|
|
void texttospellopts(char *text, int *power, char *damstr, int *needgrab, int *range, char *racestr) {
|
|
char *p;
|
|
int n;
|
|
char *argname[] = {
|
|
"pw:",
|
|
"dam:",
|
|
"needgrab:",
|
|
"range:",
|
|
"race:",
|
|
NULL,
|
|
};
|
|
void *argval[] = {
|
|
power,
|
|
damstr,
|
|
needgrab,
|
|
range,
|
|
racestr,
|
|
NULL,
|
|
};
|
|
char argtype[] = {
|
|
'i',
|
|
's',
|
|
'b',
|
|
'i',
|
|
's',
|
|
'\0',
|
|
};
|
|
|
|
// defaults
|
|
if (power) *power = 0;
|
|
if (damstr) strcpy(damstr, "");
|
|
if (needgrab) *needgrab = B_FALSE;
|
|
if (range) *range = 0;
|
|
if (racestr) strcpy(racestr, "");
|
|
|
|
if (!strlen(text)) {
|
|
return;
|
|
}
|
|
|
|
// for each arg
|
|
for (n = 0; argname[n]; n++) {
|
|
// search for it in text...
|
|
for (p = text ; *p ; p++) {
|
|
if (!strncmp(p, argname[n], strlen(argname[n])) ) {
|
|
char localval[BUFLEN];
|
|
char *valfull;
|
|
|
|
strcpy(localval, p + strlen(argname[n]));
|
|
valfull = strtok(localval, ";");
|
|
if (valfull) {
|
|
if (argval[n]) {
|
|
if (argtype[n] == 'i') {
|
|
*((int *)argval[n]) = atoi(valfull);
|
|
} else if (argtype[n] == 'b') {
|
|
*((int *)argval[n]) = atoi(valfull) ? B_TRUE : B_FALSE;
|
|
} else if (argtype[n] == 's') {
|
|
strcpy((char *)argval[n], valfull);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
|
|
// returns # opts filled in
|
|
int texttospellopts(char *text, ... ) {
|
|
char *p;
|
|
va_list args;
|
|
int nfilled = 0;
|
|
char *validname[] = {
|
|
"pw:",
|
|
"count:",
|
|
"dam:",
|
|
"needgrab:",
|
|
"range:",
|
|
"race:",
|
|
"stamcost:",
|
|
NULL,
|
|
};
|
|
int argdefault[] = {
|
|
0,
|
|
1,
|
|
-1, // string
|
|
B_FALSE,
|
|
0,
|
|
-1, // string
|
|
0,
|
|
-99, // last
|
|
};
|
|
char argtype[] = {
|
|
'i',
|
|
'i',
|
|
's',
|
|
'b',
|
|
'i',
|
|
's',
|
|
'i',
|
|
'\0',
|
|
};
|
|
char *wantname = NULL;
|
|
void *writeto = NULL;
|
|
|
|
va_start(args, text);
|
|
wantname = va_arg(args, char *);
|
|
if (wantname) writeto = va_arg(args, void *);
|
|
while (wantname) { // process this one
|
|
if (writeto) {
|
|
int foundidx = -1,i;
|
|
|
|
// validate 'wantname' - must match one of 'validname[]'
|
|
for (i = 0; validname[i]; i++) {
|
|
if (streq(validname[i], wantname)) {
|
|
foundidx = i;
|
|
break;
|
|
}
|
|
}
|
|
assert(foundidx != -1);
|
|
|
|
// blank our dest buffer
|
|
if (argtype[foundidx] == 'i') {
|
|
*((int *)writeto) = argdefault[foundidx];
|
|
} else if (argtype[foundidx] == 'b') {
|
|
*((int *)writeto) = argdefault[foundidx];
|
|
} else if (argtype[foundidx] == 's') {
|
|
strcpy((char *)writeto, "");
|
|
} else {
|
|
assert("texttospellopts() error - argtype not found" == 0);
|
|
}
|
|
|
|
// look for 'wantname' within 'text'
|
|
for (p = text ; *p ; p++) {
|
|
if (!strncmp(p, wantname, strlen(wantname)) ) { // found it!
|
|
char localval[BUFLEN];
|
|
char *valfull;
|
|
|
|
// extract value from text
|
|
// p will point to "pw:xxx;"
|
|
strcpy(localval, p + strlen(wantname)); // localval is "xxx;"
|
|
valfull = strtok(localval, ";"); // valfull is "xxx"
|
|
if (valfull) {
|
|
// if it's there, write the value into 'writeto'
|
|
if (argtype[foundidx] == 'i') {
|
|
*((int *)writeto) = atoi(valfull);
|
|
} else if (argtype[foundidx] == 'b') {
|
|
*((int *)writeto) = atoi(valfull) ? B_TRUE : B_FALSE;
|
|
} else if (argtype[foundidx] == 's') {
|
|
strcpy((char *)writeto, valfull);
|
|
}
|
|
nfilled++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// get next one
|
|
wantname = va_arg(args, char *);
|
|
if (wantname) writeto = va_arg(args, void *);
|
|
}
|
|
va_end(args);
|
|
return nfilled;
|
|
}
|
|
|
|
int timeisbetween(int h, int start, int end) {
|
|
int hh = start;
|
|
if (h == hh) return B_TRUE;
|
|
while (hh != end) {
|
|
// increment hour being checked
|
|
if (++hh >= 24) {
|
|
hh = 0;
|
|
}
|
|
// check...
|
|
if (h == hh) return B_TRUE;
|
|
}
|
|
return B_FALSE;
|
|
}
|
|
|
|
char *you(lifeform_t *lf) {
|
|
if (isplayer(lf)) {
|
|
return "You";
|
|
}
|
|
return "It";
|
|
}
|
|
|
|
char *you_l(lifeform_t *lf) {
|
|
if (isplayer(lf)) {
|
|
return "you";
|
|
}
|
|
return "it";
|
|
}
|
|
|
|
char *your(lifeform_t *lf) {
|
|
if (isplayer(lf)) {
|
|
return "Your";
|
|
}
|
|
return "Its";
|
|
}
|
|
|
|
char *your_l(lifeform_t *lf) {
|
|
if (isplayer(lf)) {
|
|
return "your";
|
|
}
|
|
return "its";
|
|
}
|