#include #include #include #include #include #include #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_BOLDMAGENTA; case 'b': // bad return C_BROWN; 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_BOLDCYAN; 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"); strcpy(extradambuf, " ineffectually"); 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 (strstr(verb, "knock out") && !isplayer(victim)) 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_BROWN; // 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->id != 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; strcpy(nodamstr, " ineffectually"); } if (backstab && (idx == 0)) { verb = strdup("backstab"); needfree = B_TRUE; } if (victim && isplayer(victim) && !nodam) { col = C_YELLOW; } else { col = C_GREY; } 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 *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 *getflagsourcetext(flag_t *f) { switch (f->lifetime) { case FROMSKILL: return " (skill perk)"; case FROMJOB: return " (job perk)"; case FROMOBEQUIP: case FROMOBHOLD: case FROMOBACTIVATE: return " (conferred perk)"; case FROMGODGIFT: return " (god gift)"; case FROMGODPIETY: return " (piety bonus)"; default: break; } return ""; } 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; pct = (int)(((float) dam / (float) maxhp) * 100.0); if (!isunconscious(victim)) { if (wep && hasflag(wep->flags, F_MERCIFUL)) { return "knock out"; } if (wep && wep->pile->owner && lfhasflag(wep->pile->owner, F_STRIKETOKO)) { return "knock out"; } } if (victim->race->id == R_DANCINGWEAPON) { return "defeat"; } if (getraceclass(victim) == RC_PLANT) { return "destroy"; } 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?"; } // 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_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_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_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 *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 . // 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"; }