* [+] mark more spells as AI_CASTTOFLEE

- [+] simplify AI spell code
- [+] sense surroundings - use power for radius!
- [+] OT_A_LEARN for dietys
* [+] getspellname(spellid, power)
* [+] max powers
- [+] Created monsters need f_xpval = 0
- [+] createmonster - let player choose the type at high power (ie. 8)
- [+] peaceful monsters are still attacking other ones!
- [+] explosions knock you back from centre of it!
- [+] manuals - teach you a skill, or enhance it.
- [+] wand of weakness not being identified.
* [+] more wizard mp reegn fixes
- [+] if you walk into a wall which you can't see, you DO lose hp.
- [+] maybe make it you CANT always see your cell?
- [+] "you feel" instead of "you see" if you have no light source ?
- [+] make it so you dont need LOS to cast spells
* [+] if you arrive in a dark cell after movement show msg
- [+] cannot INSPECT when blind.
* [+] reduce mp heal skill check difficulty since it takes longer
      begore triggering.
- [+] replace gamestarted/loading with gamemode.
- [+] fix spellpower calculation during validation of races
- [+] let "produceslight" go non orthogonal for radius=1
* [+] more frequently appearing light objects
* [+] allow use of oil potion to unjam doors too.
- [+] don't behead tiny/small things
- [+] rats / batsshould be hostile!
This commit is contained in:
Rob Pearce 2011-03-18 01:25:18 +00:00
parent 8d05746bf1
commit cc969bb800
23 changed files with 9098 additions and 13961 deletions

360
ai.c
View File

@ -1,5 +1,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ai.h"
#include "attack.h"
#include "defs.h"
@ -31,67 +32,11 @@ enum OBTYPE aigetattackspell(lifeform_t *lf, lifeform_t *victim) {
for (f = lf->flags->first ; f ; f = f->next) {
if ((f->id == F_CANCAST) || (f->id == F_CANWILL)) {
objecttype_t *ot;
ot = findot(f->val[0]);
if (cancast(lf, f->val[0], NULL)) {
int ok = B_FALSE;
if (hasflag(ot->flags, F_AICASTATVICTIM)) {
int range;
range = getspellrange(f->val[0], getspellpower(lf, f->val[0]));
if ((range == UNLIMITED) || (getcelldist(lf->cell, victim->cell) <= range)) {
if (db) {
dblog(".oO { spell possibility: %s }", ot ? ot->name : "?unkownspell?");
}
ok = B_TRUE;
}
} else if (hasflag(ot->flags, F_AICASTATSELF) || hasflag(ot->flags, F_AICASTANYWHERE)) {
if (db) {
dblog(".oO { spell possibility: %s }", ot ? ot->name : "?unkownspell?");
}
ok = B_TRUE;
} else if (hasflag(ot->flags, F_AICASTATADJVICTIM) &&
(getcelldist(lf->cell,victim->cell) == 1)) {
if (ot->id == OT_A_GRAB) {
if (lfhasflag(lf, F_GRABBING) || lfhasflag(lf, F_GRABBEDBY) ||
lfhasflag(victim, F_GRABBING) || lfhasflag(victim, F_GRABBEDBY)) {
} else {
ok = B_TRUE;
}
} else if (ot->id == OT_A_CRUSH) {
// can only crush if you first grab something
if (lfhasflag(lf, F_GRABBING)) {
ok = B_TRUE;
}
} else {
ok = B_TRUE;
}
} else if (hasflag(ot->flags, F_AICASTNEXTTOVICTIM) &&
(getcelldist(lf->cell,victim->cell) == 1)) {
ok = B_TRUE;
} else {
if (db) {
dblog(".oO { cant cast %s - dont know where to target it }", ot ? ot->name : "?unkownspell?");
}
}
if (ok) {
if (aispellok(lf, ot, victim)) {
if (aispellok(lf, f->val[0], victim, F_AICASTTOATTACK)) {
poss[nposs] = f->val[0];
nposs++;
}
}
} else {
if (db) {
if (ot) {
dblog(".oO { can't cast %s right now (mpcost=%d, i have %d) }",
ot ? ot->name : "?unkownspell?",
getmpcost(ot->id), lf->mp);
} else {
dblog(".oO { can't cast ?unknownspell? right now }");
}
}
}
}
}
// select a random one
@ -109,42 +54,25 @@ enum OBTYPE aigetfleespell(lifeform_t *lf) {
enum OBTYPE poss[MAXPILEOBS];
int nposs = 0;
int db = B_FALSE;
lifeform_t *fleefrom;
if (lfhasflag(lf, F_DEBUG)) {
db = B_TRUE;
}
for (f = lf->flags->first ; f ; f = f->next) {
if ((f->id == F_CANCAST) || (f->id == F_CANWILL)) {
objecttype_t *ot;
ot = findot(f->val[0]);
if (cancast(lf, f->val[0], NULL)) {
int ok = B_FALSE;
if (hasflag(ot->flags, F_AICASTTOFLEE)) {
if (db) {
dblog(".oO { flee spell possibility: %s }", ot ? ot->name : "?unkownspell?");
}
ok = B_TRUE;
f = lfhasflag(lf, F_FLEEFROM);
if (f) {
fleefrom = findlf(lf->cell->map, f->val[0]);
}
if (ok) {
if (aispellok(lf, ot, lf)) {
for (f = lf->flags->first ; f ; f = f->next) {
if ((f->id == F_CANCAST) || (f->id == F_CANWILL)) {
if (aispellok(lf, f->val[0], fleefrom, F_AICASTTOFLEE)) {
poss[nposs] = f->val[0];
nposs++;
}
}
} else {
if (db) {
if (ot) {
dblog(".oO { can't cast %s right now (mpcost=%d, i have %d) }",
ot ? ot->name : "?unkownspell?",
getmpcost(ot->id), lf->mp);
} else {
dblog(".oO { can't cast ?unknownspell? right now }");
}
}
}
}
}
// select a random one
@ -157,40 +85,92 @@ enum OBTYPE aigetfleespell(lifeform_t *lf) {
return OT_NONE;
}
void aigetspelltarget(lifeform_t *lf, objecttype_t *spelltype, lifeform_t *victim, lifeform_t **spelllf, cell_t **spellcell, object_t **spellob) {
if (hasflag(spelltype->flags, F_AICASTATVICTIM)) {
if (spelllf) *spelllf = victim;
if (spellcell) *spellcell = victim->cell;
if (spellob) *spellob = NULL;
} else if (hasflag(spelltype->flags, F_AICASTNEXTTOVICTIM)) {
if (getcelldist(lf->cell, victim->cell) <= 1) {
if (spelllf) *spelllf = NULL;
if (spellcell) *spellcell = lf->cell;
if (spellob) *spellob = NULL;
}
} else if (hasflag(spelltype->flags, F_AICASTATADJVICTIM)) {
if (getcelldist(lf->cell, victim->cell) <= 1) {
if (spelllf) *spelllf = victim;
if (spellcell) *spellcell = victim->cell;
if (spellob) *spellob = NULL;
}
} else if (hasflag(spelltype->flags, F_AICASTATSELF)) {
if (spelllf) *spelllf = lf;
if (spellcell) *spellcell = lf->cell;
if (spellob) *spellob = NULL;
} else if (hasflag(spelltype->flags, F_AICASTANYWHERE)) {
if (spelllf) *spelllf = NULL;
if (spellcell) *spellcell = NULL;
if (spellob) *spellob = NULL;
} else {
void aigetspelltarget(lifeform_t *lf, objecttype_t *spelltype, lifeform_t *victim, lifeform_t **spelllf, cell_t **spellcell, object_t **spellob, enum FLAG purpose) {
int specialcase = B_FALSE;
flag_t *f;
// default - at victim.
if (spelllf) *spelllf = victim;
if (spellcell) *spellcell = victim->cell;
if (spellob) *spellob = NULL;
f = hasflag(spelltype->flags, purpose);
if (f) {
switch (f->val[0]) {
case ST_VICTIM:
// at victim.
if (spelllf) *spelllf = victim;
if (spellcell) *spellcell = victim->cell;
if (spellob) *spellob = NULL;
break;
case ST_ADJSELF: // cast at myself when next to victim
if (getcelldist(lf->cell, victim->cell) <= 1) {
if (spelllf) *spelllf = lf;
if (spellcell) *spellcell = lf->cell;
if (spellob) *spellob = NULL;
}
break;
case ST_ADJVICTIM: // cast at victim when next to victim
if (getcelldist(lf->cell, victim->cell) <= 1) {
if (spelllf) *spelllf = victim;
if (spellcell) *spellcell = victim->cell;
if (spellob) *spellob = NULL;
}
break;
case ST_SELF:
if (spelllf) *spelllf = lf;
if (spellcell) *spellcell = lf->cell;
if (spellob) *spellob = NULL;
break;
case ST_ANYWHERE:
if (spelllf) *spelllf = NULL;
if (spellcell) *spellcell = NULL;
if (spellob) *spellob = NULL;
break;
case ST_SPECIAL:
specialcase = B_TRUE;
break;
}
if (specialcase) {
if (spelltype->id == OT_S_TELEKINESIS) {
float maxweight;
object_t *poss[MAXPILEOBS];
int nposs;
int i;
// find nearest object which can be picked up
// this is copied out of the telekenesis spell code!
maxweight = getlfweight(lf, B_NOOBS) +
(getlfweight(lf, B_NOOBS) * (getstatmod(lf, A_IQ) / 100));
nposs = 0;
for (i = 0; i < lf->nlos; i++) {
object_t *o;
for (o = lf->los[i]->obpile->first ; o ; o = o->next) {
if (!hasflag(o->flags, F_NOPICKUP) &&
getobweight(o) <= maxweight) {
poss[nposs] = o;
nposs++;
if (nposs >= MAXPILEOBS) break;
}
}
if (nposs >= MAXPILEOBS) break;
}
// should always be true since we check this in aispellok
if (nposs > 0) {
if (spellob) *spellob = poss[rnd(0,nposs-1)];
}
// cast spell at the victim
if (spelllf) *spelllf = victim;
if (spellcell) *spellcell = victim->cell;
}
}
}
}
object_t *aigetwand(lifeform_t *lf) {
object_t *aigetwand(lifeform_t *lf, enum FLAG purpose) {
object_t *o;
object_t *poss[MAXPILEOBS];
int nposs = 0;
@ -198,7 +178,7 @@ object_t *aigetwand(lifeform_t *lf) {
// wand with charges left?
if ((o->type->obclass->id == OC_WAND) && (getcharges(o) > 0)) {
// do we know how to use it?
if (hasflag(o->flags, F_AICASTATVICTIM) || hasflag(o->flags, F_AICASTATSELF) || hasflag(o->flags, F_AICASTANYWHERE)) {
if (hasflag(o->flags, purpose)) {
// TODO: if castatself, check whether we actually need to (ie. healing, invis, etc)
poss[nposs] = o;
nposs++;
@ -399,7 +379,7 @@ void aimove(lifeform_t *lf) {
spellcell = target->cell;
} else {
// pick targets based on spell flags
aigetspelltarget(lf, st, target, &spelllf, &spellcell, &spellob);
aigetspelltarget(lf, st, target, &spelllf, &spellcell, &spellob, F_AICASTTOATTACK);
}
if (spellfailed) {
@ -457,14 +437,24 @@ void aimove(lifeform_t *lf) {
}
// do we have a wand we can zap?
o = aigetwand(lf);
if (lfhasflag(lf, F_FLEEFROM)) {
o = aigetwand(lf, F_AICASTTOFLEE);
} else {
o = aigetwand(lf, F_AICASTTOATTACK);
}
if (o) {
objecttype_t *st;
cell_t *zapcell = NULL;
st = getlinkspell(o);
if (st) {
aigetspelltarget(lf, st, target, NULL, &zapcell, NULL);
enum FLAG purpose;
if (lfhasflag(lf, F_FLEEFROM)) {
purpose = F_AICASTTOFLEE;
} else {
purpose = F_AICASTTOATTACK;
}
aigetspelltarget(lf, st, target, NULL, &zapcell, NULL, purpose);
} else {
// no linkspell - just zap it.
zapcell = NULL;
@ -686,17 +676,151 @@ int aiobok(lifeform_t *lf, object_t *o, lifeform_t *target) {
return B_TRUE;
}
int aispellok(lifeform_t *lf, objecttype_t *st, lifeform_t *victim) {
if ((st->id == OT_S_BLINDNESS) && isblind(victim)) {
// is the spell 'spellid' okay for AI lifeform 'lf' to cast at 'victim', for given purpose.
// purpose could be F_AICASTTOFLEE or F_ATCASTTOATTACK
int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG purpose) {
objecttype_t *ot;
flag_t *f;
int db = B_FALSE;
int ok = B_FALSE;
int specialcase = B_FALSE;
if (lfhasflag(lf, F_DEBUG)) {
db = B_TRUE;
}
ot = findot(spellid);
// enough mp etc?
if (!cancast(lf, spellid, NULL)) {
if (db) {
char why[BUFLEN];
if (reason == E_NOMP) {
strcpy(why, "not enough mp");
} else if (reason == E_TOOPOWERFUL) {
strcpy(why, "spell too powerful");
} else if (reason == E_NOTREADY) {
strcpy(why, "abil not ready");
} else {
strcpy(why, "unknown reason");
}
if (ot) {
dblog(".oO { can't cast %s right now (%s) (mpcost=%d, i have %d) }",
ot ? ot->name : "?unkownspell?", why,
getmpcost(ot->id), lf->mp);
} else {
dblog(".oO { can't cast ?unknownspell? right now }");
}
}
return B_FALSE;
}
if ((st->id == OT_S_INVISIBILITY) && lfhasflag(victim, F_INVISIBLE)) {
f = hasflag(ot->flags, purpose);
if (f) {
int range;
switch (f->val[0]) {
case ST_VICTIM:
range = getspellrange(spellid, getspellpower(lf, spellid));
if ((range == UNLIMITED) || (getcelldist(lf->cell, victim->cell) <= range)) {
if (db) {
dblog(".oO { spell possibility: %s }", ot ? ot->name : "?unkownspell?");
}
ok = B_TRUE;
}
break;
case ST_SELF:
case ST_ANYWHERE:
if (db) {
dblog(".oO { spell possibility: %s }", ot ? ot->name : "?unkownspell?");
}
ok = B_TRUE;
break;
case ST_ADJVICTIM:
if (getcelldist(lf->cell,victim->cell) == 1) {
if (ot->id == OT_A_GRAB) {
if (lfhasflag(lf, F_GRABBING) || lfhasflag(lf, F_GRABBEDBY) ||
lfhasflag(victim, F_GRABBING) || lfhasflag(victim, F_GRABBEDBY)) {
} else {
ok = B_TRUE;
}
} else if (ot->id == OT_A_CRUSH) {
// can only crush if you first grab something
if (lfhasflag(lf, F_GRABBING)) {
ok = B_TRUE;
}
} else {
ok = B_TRUE;
}
}
break;
case ST_ADJSELF:
if (getcelldist(lf->cell,victim->cell) == 1) {
ok = B_TRUE;
}
break;
case ST_SPECIAL:
specialcase = B_TRUE;
break;
}
} else {
// invalid spell for this purpose
dblog(".oO { cant cast %s - not valid for given purpose }", ot ? ot->name : "?unkownspell?");
return B_FALSE;
}
if ((st->id == OT_S_PAIN) && lfhasflag(victim, F_PAIN)) {
if (specialcase) {
if (ot->id == OT_S_TELEKINESIS) {
int i,nposs;
float maxweight;
maxweight = getlfweight(lf, B_NOOBS) +
(getlfweight(lf, B_NOOBS) * (getstatmod(lf, A_IQ) / 100));
nposs = 0;
for (i = 0; i < lf->nlos; i++) {
object_t *o;
for (o = lf->los[i]->obpile->first ; o ; o = o->next) {
if (!hasflag(o->flags, F_NOPICKUP) &&
getobweight(o) <= maxweight) {
ok = B_TRUE;
break;
}
if (ok) break;
}
}
} else {
dblog(".oO { cant cast %s - specialcase conditions not yet coded }", ot ? ot->name : "?unkownspell?");
return B_FALSE;
}
if ((st->id == OT_A_SPRINT) && lfhasflag(lf, F_SPRINTING)) {
}
if (!ok) {
dblog(".oO { cant cast %s - targetting conditions cannot be met }", ot ? ot->name : "?unkownspell?");
return B_FALSE;
}
// now check whether it meets spellcasting conditions
if ((ot->id == OT_S_BLINDNESS) && isblind(victim)) {
return B_FALSE;
}
if ((ot->id == OT_S_HASTE) && lfhasflag(lf, F_FASTACT)) {
return B_FALSE;
}
if ((ot->id == OT_S_INVISIBILITY) && lfhasflag(victim, F_INVISIBLE)) {
return B_FALSE;
}
if ((ot->id == OT_S_PAIN) && lfhasflag(victim, F_PAIN)) {
return B_FALSE;
}
if ((ot->id == OT_S_HEALING) && (lf->hp >= lf->maxhp)) {
return B_FALSE;
}
if ((ot->id == OT_S_HEALINGMIN) && (lf->hp >= lf->maxhp)) {
return B_FALSE;
}
if ((ot->id == OT_S_SLOW) && lfhasflag(victim, F_SLOWACT)) {
return B_FALSE;
}
if ((ot->id == OT_A_SPRINT) && lfhasflag(lf, F_SPRINTING)) {
return B_FALSE;
}

6
ai.h
View File

@ -2,12 +2,12 @@
enum OBTYPE aigetattackspell(lifeform_t *lf, lifeform_t *victim);
enum OBTYPE aigetfleespell(lifeform_t *lf);
void aigetspelltarget(lifeform_t *lf, objecttype_t *spelltype, lifeform_t *victim, lifeform_t **spelllf, cell_t **spellcell, object_t **spellob);
object_t * aigetwand(lifeform_t *lf);
void aigetspelltarget(lifeform_t *lf, objecttype_t *spelltype, lifeform_t *victim, lifeform_t **spelllf, cell_t **spellcell, object_t **spellob, enum FLAG purpose);
object_t * aigetwand(lifeform_t *lf, enum FLAG purpose);
void aimove(lifeform_t *lf);
int aipickup(lifeform_t *lf, object_t *o);
int aiobok(lifeform_t *lf, object_t *o, lifeform_t *target);
int aispellok(lifeform_t *lf, objecttype_t *st, lifeform_t *victim);
int aispellok(lifeform_t *lf, enum OBTYPE spellid, lifeform_t *victim, enum FLAG purpose);
object_t *hasbetterarmour(lifeform_t *lf, obpile_t *op);
object_t *hasbetterweapon(lifeform_t *lf, obpile_t *op);
int lookforobs(lifeform_t *lf, int covetsonly);

View File

@ -433,7 +433,7 @@ int attacklf(lifeform_t *lf, lifeform_t *victim) {
f = addflag(victim->flags, F_NOTIME, B_TRUE, NA, NA, NULL);
adj = getrandomadjcell(victim->cell, WE_NOTSOLID);
moveto(lf, adj);
moveto(lf, adj, B_FALSE);
msg("%s dodge%s!",victimname,isplayer(victim) ? "" : "s");
killflag(f);
@ -813,7 +813,7 @@ char *getkillverb(lifeform_t *victim, enum DAMTYPE damtype, int dam, int maxhp)
if (lfhasflagval(victim, F_NOBODYPART, BP_HEAD, NA, NA, NULL)) {
return "bisect";
} else {
if (rnd(1,3) == 1) {
if ((getlfsize(victim) >= SZ_MEDIUM) && (rnd(1,3) == 1)) {
return "behead";
} else {
return "bisect";

112
defs.h
View File

@ -17,6 +17,15 @@
#define ALLCONFERRED (-9873)
enum GAMEMODE {
GM_FIRST,
GM_INIT,
GM_VALIDATION,
GM_LOADING,
GM_LOADED,
GM_GAMESTARTED,
};
enum ATTRIB {
A_NONE = -1,
A_STR = 0,
@ -477,6 +486,13 @@ enum JOB {
J_WIZARD,
};
enum MATSTATE {
MS_SOLID,
MS_LIQUID,
MS_GAS,
MS_OTHER,
};
// Object Materials
enum MATERIAL {
MT_NOTHING = 0,
@ -501,6 +517,7 @@ enum MATERIAL {
MT_MAGIC = 19,
MT_GAS = 20,
MT_SLIME = 21,
MT_WAX = 22,
};
// Object Types
@ -593,6 +610,35 @@ enum OBTYPE {
OT_SCR_TELEPORT,
OT_SCR_TURNUNDEAD,
OT_SCR_WISH,
// BOOKS
OT_MAN_ATHLETICS,
OT_MAN_FIRSTAID,
OT_MAN_LOCKPICKING,
OT_MAN_RESEARCH,
OT_MAN_SPELLCASTING,
// manuals of weaponry
OT_MAN_AXES,
OT_MAN_CLUBS,
OT_MAN_LONGBLADES,
OT_MAN_POLEARMS,
OT_MAN_SHORTBLADES,
OT_MAN_STAVES,
OT_MAN_UNARMED,
// manuals of spells
OT_MAN_SS_ALLOMANCY,
OT_MAN_SS_AIR,
OT_MAN_SS_DEATH,
OT_MAN_SS_DIVINATION,
OT_MAN_SS_FIRE,
OT_MAN_SS_ICE,
OT_MAN_SS_GRAVITY,
OT_MAN_SS_LIFE,
OT_MAN_SS_MODIFICATION,
OT_MAN_SS_MENTAL,
OT_MAN_SS_SUMMONING,
OT_MAN_SS_TRANSLOCATION,
OT_MAN_SS_WILD,
OT_MAN_TECHUSAGE,
// SPELLBOOKS
// allomancy can't be learned from books
// -- death
@ -720,12 +766,13 @@ enum OBTYPE {
// -- divine powers
OT_S_WISH,
OT_S_GIFT,
OT_A_DEBUG,
OT_A_LEARN,
// abilities
OT_A_GRAB,
OT_A_CRUSH,
OT_A_JUMP,
OT_A_SPRINT,
OT_A_DEBUG,
OT_A_EMPLOY,
OT_A_HEAVYBLOW,
OT_A_INSPECT,
@ -746,10 +793,12 @@ enum OBTYPE {
// tools
OT_BLINDFOLD,
OT_BUGLAMP,
OT_CANDLE,
OT_GUNPOWDER,
OT_LANTERNOIL,
OT_LOCKPICK,
OT_PICKAXE,
OT_TORCH,
// tech
OT_POCKETWATCH,
OT_C4,
@ -782,6 +831,7 @@ enum OBTYPE {
OT_BLOODSTAIN,
OT_BLOODSPLASH,
OT_BLOODPOOL,
OT_MELTEDWAX,
OT_SOGGYPAPER,
OT_FLESHCHUNK,
// effects
@ -984,6 +1034,8 @@ enum FLAG {
F_NOBLESS, // can't be blessed or cursed
F_CORPSEOF, // this is a corpse of montype val0.
F_DTCONVERT, // damtype val0 converts this to f->text
F_NODTCONVERT, // overrides DTCONVERT .
F_NOMATCONVERT, // overrides MATCONVERT .
F_MATCONVERT, // touching material id val0 converts this to f->text
F_MATCONVERTTEXT, // description when F_MATCONVERT happens. ie. "is consumed in flame"
F_MATCONVERTTEXTPL, // plural for matconverttext.
@ -1020,6 +1072,11 @@ enum FLAG {
// v0 is target requirements (los/lof)
// text is prompt
F_OPERNEEDDIR, // need to ask a direction when operating this. text is prompt
// tool flags
F_LIGHTSOURCE, // a light source like a torch, lantern etc
F_CHARGELOWMSG, // text = msg when charges are nearly out
F_CHARGEOUTMSG, // text = msg when charges are gone
// technology flags
F_TECHLEVEL, // v0 is a PR_xxx enum for tech usage skill
// what can you do with this object?
@ -1058,6 +1115,7 @@ enum FLAG {
F_SHODDY, // weps do less damage, armour protects less.
// weapon flags
F_OBATTACKDELAY, // how long weapon takes to attack
F_USESSKILL, // weapon needs skill sk_v0
F_DAMTYPE, // val0 = damage type
F_DAM, // val0 = ndice, val1 = nsidesondie, val2 = mod
F_MISSILEDAM, // val0 = dam if it hits (without speed multiplier)
@ -1094,6 +1152,8 @@ enum FLAG {
// scroll flags
F_LINKSPELL, // val0 = spell this scroll will cast when read
// v1 = spell power
// book flags
F_MANUALOF, // val0 = spellschool this book trains
// ob identification flags
F_HASHIDDENNAME, // whether this object class has a hidden name
F_IDENTIFIED, // whether this object is fully identified
@ -1105,12 +1165,10 @@ enum FLAG {
F_MAXPOWER, // val0 = max power of this spell (1-10)
F_MPCOST, // v0=mp cost of spell. if missing, mpcost if splev^2
//F_SPELLLETTER, // text[0] = letter to cast this spell
F_AICASTATVICTIM, // hints for AI to cast spells
F_AICASTATADJVICTIM, // hints for AI to cast spells
F_AICASTATSELF, // hints for AI to cast spells
F_AICASTNEXTTOVICTIM, // hints for AI to cast spells
F_AICASTANYWHERE, // hints for AI to cast spells
F_AICASTTOFLEE, // hints for AI to cast spells
F_AICASTTOFLEE, // AI can cast this spell to help flee
// v0 is who to target
F_AICASTTOATTACK, // AI can cast this spell to help flee
// v0 is who to target
F_AIBOOSTITEM, // ai will use this item to boost/buff itself.
// if using this on wands, update aiobok() !
F_AIHEALITEM, // ai will use this item when low on hp
@ -1119,6 +1177,8 @@ enum FLAG {
// object _AND_ lifeform flags
F_NOSTRDAMMOD, // this object/lf does not have attacks modified
// using their strength
// player only flags
F_DONEDARKMSG, // tells the game not to say 'it is very dark here'
// lifeform flags
F_DEBUG, // debugging enabled
F_ATTRMOD, // modify attribute val0 by val1. ie. 0=A_STR,1=-3
@ -1148,6 +1208,7 @@ enum FLAG {
// ABILITY FLAGS
F_FAILEDINSPECT, // lf has failed an inspect check for item id v0
// MONSTER AI FLAGS
F_XPVAL, // force xp val for killing this lf to v0
F_HOSTILE, // lf will attack the player if in sight
F_FRIENDLY, // lf will attack all non-players if in sight
F_WANTS, // lf will try to pick up object type val0. if
@ -1326,6 +1387,17 @@ enum LIGHTLEV {
};
// spell targets
enum SPELLTARGET {
ST_VICTIM, // cast at victim
ST_ADJVICTIM, // cast at victim who is next to us
ST_SELF, // cast at myself
ST_ADJSELF, // cast at self, while next to victim
ST_ANYWHERE, // cast anywere
ST_SPECIAL, // spell targetting will be hardcoded
};
#define B_DIEONFAIL (-1)
#define B_BLUNTONFAIL (-2)
@ -1603,14 +1675,38 @@ typedef struct material_s {
} material_t;
#define SK_NONE -1
enum SKILL {
SK_ATHLETICS,
SK_FIRSTAID,
SK_LOCKPICKING,
SK_RESEARCH,
SK_SPELLCASTING,
// weaponry
SK_AXES,
SK_CLUBS,
SK_LONGBLADES,
SK_POLEARMS,
SK_SHORTBLADES,
SK_STAVES,
SK_UNARMED,
// spell schools
SK_SS_ALLOMANCY,
SK_SS_AIR,
SK_SS_DEATH,
SK_SS_DIVINATION,
SK_SS_FIRE,
SK_SS_ICE,
SK_SS_GRAVITY,
SK_SS_LIFE,
SK_SS_MODIFICATION,
SK_SS_MENTAL,
SK_SS_SUMMONING,
SK_SS_TRANSLOCATION,
SK_SS_WILD,
SK_TECHUSAGE,
};
#define MAXSKILLS 5
#define MAXSKILLS 31
// proficiency levels
enum SKILLLEVEL {

View File

@ -4,5 +4,8 @@ defs.h:
objects.c:
add an addmaterial() line
update adjustdammaterial() as required
update getmaterialvalue()
update issolidmat()
update ismetal()

View File

@ -1,6 +1,11 @@
defs.h:
add SK_xxx
inc MAXSKILLS
add OT_MAN_xxx for this skill
objects.c:
add a manual for this skill
lf.c:
add addskill()

View File

@ -1,5 +1,10 @@
defs.h:
update enum SPELLSCHOOL
add a SK_ skill for specialising in this school
objects.c:
update getschoolname
spell.c:
update getschoolskill to return the skill for this school

10
flag.c
View File

@ -8,7 +8,7 @@
#include "objects.h"
#include "text.h"
extern int gamestarted;
extern enum GAMEMODE gamemode;
extern int needredraw;
extern int statdirty;
@ -90,7 +90,7 @@ flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3,
// notify
if (gamestarted) {
if ((gamemode == GM_GAMESTARTED)) {
if (f->pile->owner) {
if (announceflaggain(f->pile->owner, f)) {
interrupt(f->pile->owner);
@ -151,7 +151,7 @@ flag_t *addflag_real(flagpile_t *fp, enum FLAG id, int val1, int val2, int val3,
}
}
if (gamestarted && doredraw) {
if ((gamemode == GM_GAMESTARTED) && doredraw) {
statdirty = B_TRUE;
needredraw = B_TRUE;
drawscreen();
@ -303,7 +303,7 @@ void killflag(flag_t *f) {
}
// notify
if (gamestarted) {
if ((gamemode == GM_GAMESTARTED)) {
if (f->pile->owner) {
if (announceflagloss(f->pile->owner, f)) {
interrupt(f->pile->owner);
@ -357,7 +357,7 @@ void killflag(flag_t *f) {
lastone->next = nextone;
}
if (gamestarted && doredraw) {
if ((gamemode == GM_GAMESTARTED) && doredraw) {
statdirty = B_TRUE;
needredraw = B_TRUE;
drawscreen();

66
io.c
View File

@ -48,7 +48,7 @@ extern objecttype_t *objecttype;
extern command_t *firstcommand;
extern skill_t *firstskill;
extern int gamestarted;
extern enum GAMEMODE gamemode;
extern long curtime;
char msgbuf[HUGEBUFLEN];
@ -1170,7 +1170,7 @@ int announceflagloss(lifeform_t *lf, flag_t *f) {
}
break;
case F_PRODUCESLIGHT:
msg("%s stop%s glowing.", lfname, isplayer(lf) ? "" : "s");
msg("%s %s no longer producing light.", lfname, isplayer(lf) ? "are" : "is");
donesomething = B_TRUE;
break;
case F_REGENERATES:
@ -2460,6 +2460,7 @@ void describespell(objecttype_t *ot) {
int y;
flag_t *f;
int i;
int power;
cls();
@ -2489,6 +2490,12 @@ void describespell(objecttype_t *ot) {
y++;
}
y++;
power = getspellpower(player, ot->id);
mvwprintw(mainwin, y, 0, "You can cast it at power level %d (maximum %d).",power, getspellmaxpower(ot->id));
y++;
wrefresh(mainwin);
// wait for key
@ -2925,7 +2932,8 @@ void dolook(cell_t *where) {
char seeverb[BUFLEN];
int seensomething = B_FALSE;
if (isblind(player)) {
//if (isblind(player)) {
if (!haslos(player, player->cell)) {
strcpy(seeverb, "feel");
} else {
strcpy(seeverb, "see");
@ -3081,12 +3089,7 @@ void makespellchoicelist(prompt_t *pr, lifeform_t *lf, char *ques, char *ques2)
addheading(pr, getschoolname(lastschool));
}
strcpy(buf2, ot->name);
capitalise(buf2);
if ((power > 1) && (lastschool != SS_ABILITY)) {
strcat(buf2, " ");
strcat(buf2, roman(power));
}
getspellname(ot->id, player, buf2);
if (validspell[i]) {
strcpy(costbuf, "");
@ -3124,7 +3127,7 @@ void domagic(enum OBTYPE spellid, int cellx, int celly) {
// ask for spell if required
if (spellid == OT_NONE) {
if (prompt.nchoices > 0) {
getchoicestr(&prompt, B_TRUE);
getchoicestr(&prompt, B_TRUE, B_FALSE);
ot = prompt.result;
if (ot) {
@ -3192,7 +3195,7 @@ void domemmagic(void) {
ch = askchar("Memorise in which slot (1-9)", "1234567890","", B_FALSE);
slot = ch - '0';
getchoicestr(&prompt, B_FALSE);
getchoicestr(&prompt, B_FALSE, B_FALSE);
ot = (objecttype_t *)prompt.result;
if (ot) {
@ -3608,7 +3611,8 @@ void dorest(void) {
if (!willtrain) {
if (player->hp >= player->maxhp) {
char norestmsg[BUFLEN];
if (lfhasflag(player, F_RESTHEALMPAMT)) {
strcpy(norestmsg, "");
if (player->maxmp > 0) {
if (player->mp >= player->maxmp) {
// no need to rest
strcpy(norestmsg, "Not resting - already at full health and mana.");
@ -3617,10 +3621,12 @@ void dorest(void) {
// no need to rest
strcpy(norestmsg, "Not resting - already at full health.");
}
if (strlen(norestmsg)) {
msg(norestmsg);
return;
}
}
}
startresting(player, willtrain);
// do the first one right away
rest(player, B_TRUE);
@ -4081,9 +4087,10 @@ char getchoice(prompt_t *prompt) {
if (ch != 27) {
// set result pointer
prompt->result = prompt->choice[i].data;
prompt->selection = i;
}
if (gamestarted) {
if ((gamemode == GM_GAMESTARTED)) {
drawscreen();
} else {
cls();
@ -4096,7 +4103,7 @@ char getchoice(prompt_t *prompt) {
return prompt->choice[i].ch;
}
char getchoicestr(prompt_t *prompt, int useshortcuts) {
char getchoicestr(prompt_t *prompt, int useshortcuts, int showallatstart) {
int i;
int y;
char ch;
@ -4110,9 +4117,10 @@ char getchoicestr(prompt_t *prompt, int useshortcuts) {
int doneheading;
int nvalid;
char promptstr[BUFLEN];
int showall = B_FALSE;
int showall;
int autochar = 0;
showall = showallatstart;
strcpy(inpstring, "");
@ -4322,11 +4330,13 @@ char getchoicestr(prompt_t *prompt, int useshortcuts) {
// set result pointer
if (sel == -1) {
prompt->result = NULL;
prompt->selection = -1;
} else {
prompt->result = prompt->choice[i].data;
prompt->selection = i;
}
if (gamestarted) {
if ((gamemode == GM_GAMESTARTED)) {
drawscreen();
} else {
cls();
@ -4376,7 +4386,7 @@ void handleinput(void) {
} else if (!canmove(player, dir, NULL)) { // can't move anymore?
stoprunning(player);
} else {
if (trymove(player, dir)) {
if (trymove(player, dir, B_TRUE)) {
stoprunning(player);
} else {
// moved ok - dont wait for input
@ -4490,7 +4500,7 @@ void handleinput(void) {
case 'u':
case 'b':
case 'n':
trymove(player, chartodir(ch));
trymove(player, chartodir(ch), B_TRUE);
break;
case 'H':
case 'J':
@ -4731,8 +4741,13 @@ void dblog(char *format, ... ) {
fflush(stdout);
}
*/
if (logfile) {
fprintf(logfile, "%s\n", buf);
fflush(logfile);
} else {
fprintf(stdout, "%s\n", buf);
fflush(stdout);
}
}
// force a '--more--' prompt
@ -4939,7 +4954,7 @@ void drawstatus(void) {
iq = getattr(player, A_IQ);
con = getattr(player, A_CON);
//redraw();
sprintf(buf, "HP:%d/%d MP:%d/%d $:%d St:%2d%c Dx:%2d%c Iq:%2d%c Cn:%2d%c DLev:%2d",
sprintf(buf, "HP:%d/%d MP:%d/%d $:%d St:%d%c Dx:%d%c Iq:%d%c Cn:%d%c DLev:%d",
player->hp,player->maxhp,
player->mp, player->maxmp,
countmoney(player),
@ -5573,6 +5588,7 @@ void showlfstats(lifeform_t *lf, int showall) {
// diff materials?
if (lf->race->material->id != MT_FLESH) {
mvwprintw(mainwin, y, 0, "%s %s made out of %s.",you(lf), isplayer(lf) ? "are" : "is", lf->race->material->name);
y++;
}
if (showall) {
@ -5830,11 +5846,7 @@ void showlfstats(lifeform_t *lf, int showall) {
power = getspellpower(lf, ot->id);
sprintf(spellname, "%s", ot->name);
if ((power > 1) && (getspellschool(ot->id) != SS_ABILITY)) {
strcat(spellname, " ");
strcat(spellname, roman(power));
}
getspellname(ot->id, lf, spellname);
sprintf(buf, " %-4d%-25s%-22s%s",thislev, spellname, getschoolname(getspellschool(ot->id)), mpbuf);
mvwprintw(mainwin, y, 0, "%s\n", buf);
anyfound = B_TRUE;
@ -5848,7 +5860,7 @@ void showlfstats(lifeform_t *lf, int showall) {
}
}
if (!anyfound) {
mvwprintw(mainwin, y, 0, "You cannot cast any spells.");
mvwprintw(mainwin, y, 0, "%s cannot cast any spells.", you(lf));
}
} else if (mode == 'e') {
x = 0; // override
@ -6073,6 +6085,7 @@ void showlfstats(lifeform_t *lf, int showall) {
cls();
centre(mainwin, 0, "INVENTORY");
y = 2;
if (countobs(lf->pack)) {
mvwprintw(mainwin, y, 0, "It is carrying:");
y += 2;
for (o = lf->pack->first ; o ; o = o->next) {
@ -6085,6 +6098,9 @@ void showlfstats(lifeform_t *lf, int showall) {
break;
}
}
} else {
mvwprintw(mainwin, y, 0, "It is not carrying anything.");
}
}
// wait for key

2
io.h
View File

@ -70,7 +70,7 @@ void drawscreen(void);
void drawstatus(void);
int drop(object_t *o, int count);
char getchoice(prompt_t *prompt);
char getchoicestr(prompt_t *prompt, int useshortcuts);
char getchoicestr(prompt_t *prompt, int useshortcuts, int showlallatstart);
int getkey(void);
void handleinput(void);
void doheading(WINDOW *win, int *y, int x, char *what);

220
lf.c
View File

@ -28,6 +28,8 @@ extern int needredraw;
extern prompt_t prompt;
extern enum GAMEMODE gamemode;
extern long nextlfid;
extern WINDOW *msgwin;
@ -35,8 +37,6 @@ extern WINDOW *statwin;
extern int statdirty;
extern int needredraw;
extern int gamestarted;
extern int loading;
extern enum ERROR reason;
@ -115,7 +115,7 @@ lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) {
a->alive = B_TRUE;
a->lastdam = strdup("nothing");
a->lastdamtype = DT_NONE;
if (gamestarted && a->prev) {
if ((gamemode == GM_GAMESTARTED) && a->prev) {
a->timespent = a->prev->timespent + 1;
} else {
a->timespent = 0;
@ -155,7 +155,7 @@ lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) {
cell->lf = a;
// give start objetcs
if (!loading) {
if ((gamemode != GM_LOADING) && (gamemode != GM_VALIDATION)) {
outfitlf(a);
}
a->created = B_TRUE;
@ -358,6 +358,20 @@ lifeform_t *makezombie(object_t *o) {
return lf;
}
// make sure player has at least novice skill in their start weapon
void autoskill(lifeform_t *lf) {
object_t *wep;
wep = getweapon(lf);
if (wep) {
skill_t *sk;
sk = getobskill(wep);
if (sk && !getskill(lf, sk->id)) {
giveskill(lf, sk->id);
}
} else {
// TODO: unarmed skill
}
}
void autotarget(lifeform_t *lf) {
object_t *gun;
@ -470,6 +484,9 @@ void autoweild(lifeform_t *lf) {
// make sure it doesn't take any time
lf->timespent = pretimespent;
// make sure we are skilled in this.
autoskill(lf);
}
int appearsrandomly(enum RACE rid) {
@ -521,6 +538,15 @@ int calcxp(lifeform_t *lf) {
float xpconstant = 1;
if (db) dblog("calcxp: calculating xpval for %s",lf->race->name);
f = lfhasflag(lf, F_XPVAL);
if (f) {
if (db) dblog("calcxp: got F_XPVAL, forcing result to %d\n",f->val[0]);
return f->val[0];
}
// attack
// - get average attack damage
@ -1027,7 +1053,7 @@ void die(lifeform_t *lf) {
//int dropobs = B_TRUE;
int vaporised = B_FALSE;
if (isplayer(lf) && (lf->race->id == J_GOD)) {
if (isplayer(lf) && hasjob(lf, J_GOD)) {
char ch;
ch = askchar("Die", "yn", "n", B_TRUE);
if (ch == 'n') {
@ -1736,7 +1762,12 @@ int flee(lifeform_t *lf) {
// if AI, try to use specific spells like teleport self
spell = aigetfleespell(lf);
if (spell != OT_NONE) {
return castspell(lf, spell, lf, NULL, lf->cell);
lifeform_t *targlf;
cell_t *targcell;
object_t *targob;
aigetspelltarget(lf, findot(spell), fleefrom, &targlf, &targcell, &targob, F_AICASTTOFLEE);
return castspell(lf, spell, targlf, targob, targcell);
}
// if AI, use helpful fleeing items
if (!useitemwithflag(lf, F_AIFLEEITEM)) {
@ -2409,9 +2440,40 @@ int getlfaccuracy(lifeform_t *lf) {
// cannot attack
acc = 0;
}
killobpile(op);
}
// modify for skill level
if (wep) {
skill_t *sk;
sk = getobskill(wep);
if (sk) {
switch (getskill(lf, sk->id)) {
case PR_INEPT:
acc -= 75;
break;
case PR_NOVICE:
acc -= 25;
break;
case PR_BEGINNER:
acc -= 5;
break;
case PR_ADEPT:
break;
case PR_SKILLED:
acc += 25;
break;
case PR_EXPERT:
acc += 50;
break;
case PR_MASTER:
acc += 75;
break;
}
}
}
if (op) killobpile(op);
// modify with dexterity
acc += getstatmod(lf, A_DEX);
@ -2453,7 +2515,7 @@ enum LFCONDITION getlfcondition(lifeform_t *lf) {
int getnightvisrange(lifeform_t *lf) {
int range = 0; // default
int range = -1; // default
flag_t *f;
f = lfhasflag(lf, F_SEEINDARK);
@ -3427,7 +3489,7 @@ void givejob(lifeform_t *lf, enum JOB jobid) {
}
}
if (!gamestarted) {
if ((gamemode != GM_GAMESTARTED)) {
autoweild(lf);
}
}
@ -3519,13 +3581,13 @@ int giveskill(lifeform_t *lf, enum SKILL id) {
if (f->val[1] < PR_MASTER) {
f->val[1]++;
}
if (isplayer(lf) && gamestarted) {
if (isplayer(lf) && (gamemode == GM_GAMESTARTED)) {
msg("You have learned the %s %s skill!", getskilllevelname(f->val[1]), getskillname(sk->id));
}
} else {
// gaining a new skill
f = addflag(lf->flags, F_HASSKILL, id, PR_NOVICE, NA, NULL);
if (isplayer(lf) && gamestarted) {
if (isplayer(lf) && (gamemode == GM_GAMESTARTED)) {
msg("You have learned the %s %s skill!", getskilllevelname(PR_NOVICE), getskillname(sk->id));
}
@ -3855,7 +3917,7 @@ int lockpick(lifeform_t *lf, object_t *target, object_t *device) {
taketime(lf, getactspeed(lf) );
// TODO: different difficulty based on doors (maybe part of F_LOCKED flag)
if (skillcheck(lf, SC_OPENLOCKS, 15, f->val[0])) {
if (skillcheck(lf, SC_OPENLOCKS, 20, f->val[0])) {
// success!
// announce
if (isplayer(lf) || cansee(player, lf)) {
@ -3953,10 +4015,12 @@ int haslof(lifeform_t *viewer, cell_t *dest) {
reason = E_OK;
// must have line of sight to fire...
/*
if (!haslos(viewer, dest)) {
reason = E_NOLOS;
return B_FALSE;
}
*/
if (viewer->cell->map != dest->map) {
reason = E_NOLOS;
@ -4098,6 +4162,7 @@ int haslos(lifeform_t *viewer, cell_t *dest) {
map_t *map;
object_t *o;
if (!viewer) return B_FALSE;
if (!dest) return B_FALSE;
// let the player see when dead, otherwise the screen wil
@ -4129,10 +4194,13 @@ int haslos(lifeform_t *viewer, cell_t *dest) {
if (deltay < 0) deltay = -deltay;
// can always see your own cell
/*
if ((deltax == 0) && (deltay == 0)) {
//if (viewer->controller == C_HUMAN) wreck->mazelev[z].maze[y2*MAZEW+x2].known = B_PERM;
return B_TRUE;
}
*/
// can't see if you're blind
if (lfhasflag(viewer, F_BLIND)) {
return B_FALSE;
@ -4328,6 +4396,7 @@ void initjobs(void) {
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "10 blessed potions of experience");
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "ring of invulnerability");
addflag(lastjob->flags, F_STARTSKILL, SK_SPELLCASTING, PR_MASTER, NA, NULL);
addflag(lastjob->flags, F_STARTSKILL, SK_SS_DEATH, PR_MASTER, NA, NULL);
addflag(lastjob->flags, F_STARTSKILL, SK_RESEARCH, PR_NOVICE, NA, NULL);
// gods may use all abilities and cast any spell at will
for (i = SS_NONE+1; i < SS_LAST; i++) {
@ -4424,7 +4493,7 @@ void initjobs(void) {
addflag(lastjob->flags, F_STARTATT, A_IQ, IQ_ENLIGHTENED, NA, NULL);
// wizard heals slowly, but regenerates mp
addflag(lastjob->flags, F_RESTHEALTIME, 6, NA, NA, NULL);
addflag(lastjob->flags, F_MPREGEN, 1, NA, NA, NULL);
//addflag(lastjob->flags, F_MPREGEN, 1, SK_SPELLCASTING, 35, NULL);
// can detect magic objects
addflag(lastjob->flags, F_DETECTMAGIC, B_TRUE, NA, NA, NULL);
addflag(lastjob->flags, F_STARTSKILL, SK_RESEARCH, PR_BEGINNER, NA, NULL);
@ -5204,7 +5273,9 @@ void initrace(void) {
addflag(lastrace->flags, F_SPELLCASTTEXT, NA, NA, NA, "gestures");
addflag(lastrace->flags, F_HASATTACK, 1, 3, 0, "claws");
addflag(lastrace->flags, F_PRODUCESLIGHT, 2, NA, NA, NULL);
addflag(lastrace->flags, F_DTIMMUNE, DT_FIRE, NA, NA, NULL);
addflag(lastrace->flags, F_HASSKILL, SK_SPELLCASTING, PR_NOVICE, NA, NULL);
addflag(lastrace->flags, F_HASSKILL, SK_SS_FIRE, PR_BEGINNER, NA, NULL);
addrace(R_TROLL, "troll", 100, 't', MT_FLESH);
@ -5236,9 +5307,10 @@ void initrace(void) {
// end monsters
// small animals
addrace(R_NEWT, "newt", 0.1, ':', MT_FLESH);
addrace(R_NEWT, "giant newt", 0.1, ':', MT_FLESH);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 100, NA, NULL);
addflag(lastrace->flags, F_COLDBLOOD, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_ANIMAL, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL);
addflag(lastrace->flags, F_HITDICE, 1, 0, NA, NULL);
@ -5255,6 +5327,7 @@ void initrace(void) {
addflag(lastrace->flags, F_DTRESIST, DT_FIRE, B_TRUE, NA, NULL);
addrace(R_BAT, "bat", 1, 'B', MT_FLESH);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, 90, NA, "");
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_ANIMAL, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_SMALL, NA, NA, NULL);
@ -5274,6 +5347,7 @@ void initrace(void) {
addflag(lastrace->flags, F_SEEINDARK, UNLIMITED, NA, NA, NULL);
addrace(R_RAT, "giant rat", 0.2, 'r', MT_FLESH);
addflag(lastrace->flags, F_ANIMAL, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, ST_VWEAK, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
@ -5595,6 +5669,7 @@ void killlf(lifeform_t *lf) {
// if so, stop targetting us now that
// we are dead.
// TODO: check on all maps?
if ((gamemode == GM_GAMESTARTED)) {
for (l = m->lf ; l ; l = l->next) {
f = lfhasflagval(l, F_TARGET, lf->id, NA, NA, NULL);
if (f) killflag(f);
@ -5603,6 +5678,7 @@ void killlf(lifeform_t *lf) {
f = lfhasflagval(l, F_GRABBING, lf->id, NA, NA, NULL);
if (f) killflag(f);
}
}
// free mem
if (lf->lastdam) free(lf->lastdam);
@ -5867,7 +5943,7 @@ int losehp_real(lifeform_t *lf, int amt, enum DAMTYPE damtype, lifeform_t *froml
// if they died
if (lf->hp <= 0) {
//if (!fromlf || (fromlf == player)) {
if (fromlf && isplayer(fromlf)) {
if (fromlf && (isplayer(fromlf) || isfriendly(fromlf))) {
addflag(lf->flags, F_KILLEDBYPLAYER, B_TRUE, NA, NA, NULL);
}
} else {
@ -5996,7 +6072,7 @@ int modattr(lifeform_t *lf, enum ATTRIB attr, int amt) {
if (lf->att[attr] > 18) lf->att[attr] = 18;
if (lf->att[attr] < 0) lf->att[attr] = 0;
if ((amt != 0) && (isplayer(lf) || cansee(player, lf))) {
if ((gamemode == GM_GAMESTARTED) && (amt != 0) && (isplayer(lf) || cansee(player, lf))) {
char lfname[BUFLEN], verb[BUFLEN], adverb[BUFLEN];
getlfname(lf, lfname);
if (isplayer(lf)) strcpy(verb, "feel");
@ -6290,7 +6366,7 @@ int push(lifeform_t *lf, object_t *o, int dir) {
}
// move player
moveto(lf, obcell);
moveto(lf, obcell, B_TRUE);
// take time - twice normal
taketime(lf, getactspeed(lf) * 2);
@ -6645,11 +6721,11 @@ int setammo(lifeform_t *lf, object_t *ammo) {
if (ammo) {
addflag(ammo->flags, F_CURAMMO, B_TRUE, NA, NA, NULL);
if (gamestarted && isplayer(lf)) {
if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) {
msg("Using %s as ammo for %s.", ammoname, gunname);
}
} else {
if (gamestarted && isplayer(lf)) {
if ((gamemode == GM_GAMESTARTED) && isplayer(lf)) {
msg("No ammo equipped for %s.", gunname);
}
}
@ -6709,7 +6785,7 @@ void setrace(lifeform_t *lf, enum RACE rid) {
newrace = findrace(rid);
if (gamestarted && lf->race) {
if ((gamemode == GM_GAMESTARTED) && lf->race) {
race_t *origrace;
statdirty = B_TRUE;
@ -6786,7 +6862,7 @@ void setrace(lifeform_t *lf, enum RACE rid) {
// new race can hold objects (F_NOPACK xx)
// TODO: new race can use magic (F_NOSPELLS)
// new race can still hold all the items which you have
if (gamestarted) {
if ((gamemode == GM_GAMESTARTED)) {
enum BODYPART bp;
object_t *o,*nexto;
@ -6857,11 +6933,34 @@ void setlastdam(lifeform_t *lf, char *buf) {
}
void initskills(void) {
addskill(SK_ATHLETICS, "Athletics", "Assists with sprinting and recovery.");
addskill(SK_ATHLETICS, "Athletics", "Assists with sprinting and exhaustion recovery.");
addskill(SK_FIRSTAID, "First Aid", "Increases the rate at which you regain health.");
addskill(SK_LOCKPICKING, "Lockpicking", "Enhances your ability to pick locks.");
addskill(SK_RESEARCH, "Research", "Allows you a chance of recognising unknown objects.");
addskill(SK_SPELLCASTING, "Spellcasting", "Determines your ability to cast magical spells.");
addskill(SK_TECHUSAGE, "Tech Usage", "Lets you comprehend use modern technological items.");
addskill(SK_SPELLCASTING, "Spellcasting", "Determines your ability to cast spells from all schools.");
// weaponry
addskill(SK_AXES, "Axes", "Helps you use chopping weapons like axes.");
addskill(SK_CLUBS, "Clubs", "Helps you use bashing weapons like maces or clubs.");
addskill(SK_LONGBLADES, "Long Blades", "Helps you use long swords, scimitars, etc.");
addskill(SK_POLEARMS, "Polearms", "Helps you use long bladed weapons like halberds.");
addskill(SK_SHORTBLADES, "Short Blades", "Helps you use daggers, short swords, etc.");
addskill(SK_STAVES, "Staves", "Helps you use quarterstaffs, staffs, etc.");
addskill(SK_UNARMED, "Unarmed Combat", "Helps you fight using your bare hands.");
// spell schools
addskill(SK_SS_ALLOMANCY, "Allomancy", "Boosts casting of spells from this school.");
addskill(SK_SS_AIR, "Air Magic", "Boosts casting of spells from this school.");
addskill(SK_SS_DEATH, "Necromancy", "Boosts casting of spells from this school.");
addskill(SK_SS_DIVINATION, "Divination", "Boosts casting of spells from this school.");
addskill(SK_SS_FIRE, "Fire Magic", "Boosts casting of spells from this school.");
addskill(SK_SS_ICE, "Ice Magic", "Boosts casting of spells from this school.");
addskill(SK_SS_GRAVITY, "Gravitation Magic", "Boosts casting of spells from this school.");
addskill(SK_SS_LIFE, "Life Magic", "Boosts casting of spells from this school.");
addskill(SK_SS_MODIFICATION, "Modification", "Boosts casting of spells from this school.");
addskill(SK_SS_MENTAL, "Psionics", "Boosts casting of spells from this school.");
addskill(SK_SS_SUMMONING, "Summoning", "Boosts casting of spells from this school.");
addskill(SK_SS_TRANSLOCATION, "Translocation", "Boosts casting of spells from this school.");
addskill(SK_SS_WILD, "Wild Magic", "Boosts casting of spells from this school.");
addskill(SK_TECHUSAGE, "Tech Usage", "Lets you comprehend modern technological items.");
}
void interrupt(lifeform_t *lf) {
@ -7243,7 +7342,7 @@ int takeoff(lifeform_t *lf, object_t *o) {
killflag(f);
taketime(lf, getactspeed(lf));
if (gamestarted) {
if ((gamemode == GM_GAMESTARTED)) {
if (lf->controller == C_PLAYER) {
msg("You take off %s.", obname);
} else if (cansee(player, lf)) {
@ -7281,7 +7380,7 @@ void taketime(lifeform_t *lf, long howlong) {
assert(howlong > 0);
if (db && gamestarted && cansee(player, lf)) {
if (db && (gamemode == GM_GAMESTARTED) && cansee(player, lf)) {
dblog("lfid %d (%s) spending %d time\n",lf->id,lf->race->name, howlong);
}
// inc timespent
@ -7409,10 +7508,10 @@ void turneffectslf(lifeform_t *lf) {
}
// MP regeneration
sumflags(lf->flags, F_MPREGEN, &i, NULL, NULL);
if (i > 0) {
// heal mp
gainmp(lf, i);
for (f = lf->flags->first ; f ; f = f->next) {
if (f->id == F_MPREGEN) {
gainmp(lf, f->val[0]);
}
}
if (lfhasflag(lf, F_MAGSHIELD)) {
@ -7597,7 +7696,7 @@ int touch(lifeform_t *lf, object_t *o) {
char obname[BUFLEN];
char lfname[BUFLEN];
if (!gamestarted) return B_FALSE;
if ((gamemode != GM_GAMESTARTED)) return B_FALSE;
getlfname(lf, lfname);
getobname(o, obname, o->amt);
@ -7701,7 +7800,7 @@ int unweild(lifeform_t *lf, object_t *o) {
killflagsofid(o->flags, F_EQUIPPED);
// unweilding doesn't take any time
if (gamestarted) {
if (gamemode == GM_GAMESTARTED) {
if (lf->controller == C_PLAYER) {
msg("You are no longer weilding %s.", obname);
} else if (cansee(player, lf)) {
@ -7847,7 +7946,7 @@ int usestairs(lifeform_t *lf, object_t *o) {
if (f) f->known = B_KNOWN;
}
// move player to new map
moveto(lf, newcell);
moveto(lf, newcell, B_TRUE);
taketime(lf, getmovespeed(lf));
} else {
dblog("ERROR - can't find opposite end of stairs/portal!");
@ -7869,6 +7968,16 @@ int validateraces(void) {
int goterror = B_FALSE;
race_t *r;
flag_t *f;
skill_t *sk;
int i;
cell_t fakecell;
map_t fakemap;
// make a fake cell
setcelltype(&fakecell, CT_CORRIDOR);
fakecell.lf = NULL;
fakecell.map = &fakemap;
for (r = firstrace ; r ; r = r->next) {
if (!hasflag(r->flags, F_SIZE)) {
printf("ERROR in race '%s' - missing F_SIZE.\n", r->name);
@ -7886,7 +7995,11 @@ int validateraces(void) {
ff = hasflagval(r->flags, F_HASSKILL, SK_SPELLCASTING, NA, NA, NULL);
if (ff) {
int power;
power = (1 + ff->val[1]) / getspelllevel(f->val[0]);
lifeform_t *lf;
// add a fake lf
lf = addlf(&fakecell, r->id, 1);
//power = (1 + ff->val[1]) / getspelllevel(f->val[0]);
power = getspellpower(lf, f->val[0]);
if (power <= 0) {
objecttype_t *sp;
sp = findot(f->val[0]);
@ -7894,7 +8007,7 @@ int validateraces(void) {
r->name, sp->name,getspelllevel(sp->id));
goterror = B_TRUE;
}
killlf(lf);
} else {
objecttype_t *sp;
sp = findot(f->val[0]);
@ -7904,7 +8017,18 @@ int validateraces(void) {
}
}
}
i = 0;
for (sk = firstskill ; sk ; sk = sk->next) {
i++;
}
if (i >= MAXSKILLS) {
printf("ERROR - MAXSKILLS is %d but found %d skills.\n",MAXSKILLS,i);
goterror = B_TRUE;
}
return goterror;
}
int rest(lifeform_t *lf, int onpurpose) {
@ -7960,12 +8084,19 @@ int rest(lifeform_t *lf, int onpurpose) {
if (f->val[0] >= healtime) {
//if (isplayer(lf)) msg("hp given.");
if (lf->hp < lf->maxhp) {
// pass a skill check to regain hp
// ie. at con <= 4 and no first aid skill, you NEVER Heal!
if (skillcheck(lf, SC_CON, 25, getskill(lf, SK_FIRSTAID))) {
gainhp(lf, hpheal);
}
}
if (lf->mp < lf->maxmp) {
// pass a skill check to regain mp
if (skillcheck(lf, SC_IQ, 25, getskill(lf, SK_SPELLCASTING))) {
gainmp(lf, mpheal);
}
}
/*
// heal mp too?
ff = lfhasflag(lf, F_RESTHEALMPAMT);
@ -8104,7 +8235,7 @@ int wear(lifeform_t *lf, object_t *o) {
while (!canwear(lf, o, bp)) {
int errresolved = B_FALSE;
if (gamestarted && lf->created) {
if ((gamemode == GM_GAMESTARTED) && lf->created) {
switch (reason) {
case E_ALREADYUSING:
if (isplayer(lf)) {
@ -8197,7 +8328,7 @@ int wear(lifeform_t *lf, object_t *o) {
}
}
if (gamestarted && lf->created) {
if ((gamemode == GM_GAMESTARTED) && lf->created) {
if (lf->controller == C_PLAYER) {
msg("You are now wearing %s.", obname);
} else if (cansee(player, lf)) {
@ -8237,7 +8368,7 @@ int weild(lifeform_t *lf, object_t *o) {
}
if (!canweild(lf, o)) {
if (gamestarted && lf->created) {
if ((gamemode == GM_GAMESTARTED) && lf->created) {
if (lf->controller == C_PLAYER) {
switch (reason) {
case E_ALREADYUSING:
@ -8385,7 +8516,7 @@ int weild(lifeform_t *lf, object_t *o) {
// if we asked to just unweild our weapon, exit now
// with no error.
if (!o) {
if (gamestarted && lf->created && (lf->race->id != R_DANCINGWEAPON)) {
if ((gamemode == GM_GAMESTARTED) && lf->created && (lf->race->id != R_DANCINGWEAPON)) {
if (isplayer(lf)) {
msg("You are now fighting unarmed.");
} else if (cansee(player, lf)) {
@ -8402,8 +8533,8 @@ int weild(lifeform_t *lf, object_t *o) {
addflag(o->flags, F_EQUIPPED, otherloc, -1, -1, NULL);
}
taketime(lf, getactspeed(lf));
if (gamestarted && lf->created && (lf->race->id != R_DANCINGWEAPON)) {
if (lf->controller == C_PLAYER) {
if ((gamemode == GM_GAMESTARTED) && lf->created && (lf->race->id != R_DANCINGWEAPON)) {
if (isplayer(lf)) {
char buf2[BUFLEN];
sprintf(buf2, "You are now weilding %c - %s", o->letter, buf);
@ -8417,6 +8548,13 @@ int weild(lifeform_t *lf, object_t *o) {
// warn if it won't do any damage
if (!hasflag(o->flags, F_DAM) && !hasflag(o->flags, F_FIREARM)) {
msg("You have a feeling that this weapon will not be very effective...");
} else {
// warn if unskilled in this weapon
skill_t *sk;
sk = getobskill(o);
if (sk && !getskill(lf, sk->id)) {
msg("You feel rather inept with this weapon.");
}
}
} else if (cansee(player, lf)) {
char buf2[BUFLEN];

1
lf.h
View File

@ -6,6 +6,7 @@ job_t *addjob(enum JOB id, char *name);
race_t *addrace(enum RACE id, char *name, float weight, char glyph, enum MATERIAL mat);
skill_t *addskill(enum SKILL id, char *name, char *desc);
void adjustdamlf(lifeform_t *lf, int *amt, enum DAMTYPE damtype);
void autoskill(lifeform_t *lf);
void autotarget(lifeform_t *lf);
void autoweild(lifeform_t *lf);
int appearsrandomly(enum RACE rid);

20845
log.txt

File diff suppressed because it is too large Load Diff

45
map.c
View File

@ -388,17 +388,18 @@ void calclight(map_t *map) {
}
// objects in hands or on body...
for (o = c->lf->pack->first ; o ; o = o->next) {
if (obproduceslight(o) && isequipped(o)) {
sumflags(o->flags, F_PRODUCESLIGHT, &radius, NULL, NULL);
if (isequipped(o)) {
radius = obproduceslight(o);
if (radius) {
makelitradius(c, radius, L_TEMP, -1);
}
}
}
}
// has light-producing object on ground?
for (o = c->obpile->first ; o ; o = o->next) {
if (obproduceslight(o)) {
sumflags(o->flags, F_PRODUCESLIGHT, &radius, NULL, NULL);
radius = obproduceslight(o);
if (radius) {
makelitradius(c, radius, L_TEMP, -1);
}
}
@ -1478,13 +1479,29 @@ void explodecells(cell_t *c, int dam, int killwalls, object_t *o, int range, int
cell_t *cc;
cc = getcellat(c->map, x,y);
if (cc && (getcelldist(c, cc) <= range)) {
explodesinglecell(cc, dam, killwalls, o);
explodesinglecell(cc, dam, killwalls, o, c);
}
}
}
// lfs up to 1 cell away are knocked back
for (y = c->y - range-1 ; y <= c->y + range+1 ; y++) {
for (x = c->x - range-1 ; x <= c->x + range+1 ; x++) {
cell_t *cc;
cc = getcellat(c->map, x,y);
if (cc && (getcelldist(c, cc) <= (range+1))) {
if (cc->lf && !isdead(cc->lf)) {
// move away from centre of explosion
knockback(cc->lf, getdiraway(cc, c, B_FALSE), 2, NULL);
}
}
}
}
}
void explodesinglecell(cell_t *c, int dam, int killwalls, object_t *o) {
// this should never be called directly - only from explodecells().
// (otherwise knockback effect won't happen)
void explodesinglecell(cell_t *c, int dam, int killwalls, object_t *o, cell_t *centre) {
char obname[BUFLEN];
if (c->lf) {
@ -1495,8 +1512,13 @@ void explodesinglecell(cell_t *c, int dam, int killwalls, object_t *o) {
} else {
sprintf(buf, "an explosion");
}
// take damage
losehp(c->lf, dam, DT_EXPLOSIVE, NULL, buf);
}
damageallobs(o, c->obpile, dam, DT_EXPLOSIVE);
if (killwalls) {
@ -2079,13 +2101,20 @@ void makelit(cell_t *c, enum LIGHTLEV how, int howlong) {
void makelitradius(cell_t *c, int radius, enum LIGHTLEV how, int howlong) {
int x,y;
cell_t *c2;
int (*distfunc)(cell_t *, cell_t *);
if (radius <= 0) return;
if (radius == 1) {
distfunc = getcelldist;
} else {
distfunc = getcelldistorth;
}
for (y = c->y - radius ; y <= c->y + radius; y++) {
for (x = c->x - radius ; x <= c->x + radius; x++) {
c2 = getcellat(c->map, x, y);
if (c2 && (getcelldistorth(c, c2) <= radius)) {
if (c2 && (distfunc(c, c2) <= radius)) {
if (cellhaslos(c,c2)) {
makelit(c2, how,howlong);
}

2
map.h
View File

@ -21,7 +21,7 @@ void createroom(map_t *map, int minx, int miny, int w, int h, int roomid);
int dirtox(int dt, int dir);
int dirtoy(int dt, int dir);
void dumpmap(map_t *map);
void explodesinglecell(cell_t *c, int dam, int killwalls, object_t *o);
void explodesinglecell(cell_t *c, int dam, int killwalls, object_t *o, cell_t *centre);
void explodecells(cell_t *c, int dam, int killwalls, object_t *o, int range, int wantannounce);
map_t *findmap(int mid);
map_t *findmapofdepth(int depth);

148
move.c
View File

@ -18,7 +18,7 @@ extern lifeform_t *player;
extern int statdirty;
extern int needredraw;
extern int gamestarted;
extern enum GAMEMODE gamemode;
extern enum ERROR reason;
extern void *rdata;
@ -247,16 +247,25 @@ void dorandommove(lifeform_t *lf, int badmovesok) {
}
}
}
trymove(lf, dir);
trymove(lf, dir, B_TRUE);
}
// src is where something is
// dst is what we are going away from
// wantcheck is whether to check for dangerous things before considering a direction valid
int getdiraway(cell_t *src, cell_t *dst, int wantcheck) {
int d;
cell_t *c;
int maxdist=-1,bestdir=D_NONE;
int dist[MAXDIR_COMPASS];
int poss[MAXDIR_COMPASS];
int nposs;
for (d = DC_N; d <= DC_NW; d++) {
dist[d - DC_N] = -1;
}
for (d = DC_N; d <= DC_NW; d++) {
int thisdist = -1;
int ok = B_FALSE;
@ -265,7 +274,7 @@ int getdiraway(cell_t *src, cell_t *dst, int wantcheck) {
if (c == dst) {
// destination is the thing we're fleeing from!
thisdist = -1;
thisdist = 0;
} else {
if (wantcheck) {
if (src->lf && canandwillmove(src->lf, d, NULL)) {
@ -281,22 +290,36 @@ int getdiraway(cell_t *src, cell_t *dst, int wantcheck) {
}
}
if (ok) {
thisdist = getcelldistorth(c, dst);
//thisdist = getcelldistorth(c, dst);
thisdist = getcelldist(c, dst);
} else {
thisdist = -1;
}
}
dist[d - DC_N] = thisdist;
if (thisdist > maxdist) {
maxdist = thisdist;
bestdir = d;
}
}
nposs = 0;
for (d = DC_N; d <= DC_NW; d++) {
if (dist[d - DC_N] != -1) {
if (dist[d - DC_N] == maxdist) {
poss[nposs] = d;
nposs++;
}
}
}
// TODO: handle ties
if (bestdir != D_NONE) {
reason = E_OK;
if (nposs <= 0) {
return D_NONE;
}
bestdir = poss[rnd(0,nposs-1)];
reason = E_OK;
return bestdir;
}
@ -305,14 +328,22 @@ int getdirtowards(cell_t *src, cell_t *dst, lifeform_t *srclf, int wantcheck) {
int d;
cell_t *c;
int mindist=9999,bestdir=D_NONE;
int dist[MAXDIR_COMPASS];
int poss[MAXDIR_COMPASS];
int nposs;
for (d = DC_N; d <= DC_NW; d++) {
dist[d - DC_N] = -1;
}
for (d = DC_N; d <= DC_NW; d++) {
int ok = B_FALSE;
c = getcellindir(src, d);
if (!c) continue;
if (c == dst) {
// destination is adjacent!
bestdir = d;
dist[d - DC_N] = 0;
mindist = 0;
break;
}
@ -331,15 +362,33 @@ int getdirtowards(cell_t *src, cell_t *dst, lifeform_t *srclf, int wantcheck) {
}
if (ok) {
int thisdist;
thisdist = getcelldistorth(c, dst);
//thisdist = getcelldistorth(c, dst);
thisdist = getcelldist(c, dst);
dist[d - DC_N] = thisdist;
if (thisdist < mindist) {
mindist = thisdist;
bestdir = d;
}
} else {
dist[d - DC_N] = -1; // ie. invalid
}
}
// handle ties
nposs = 0;
for (d = DC_N; d <= DC_NW; d++) {
if (dist[d - DC_N] != -1) {
if (dist[d - DC_N] == mindist) {
poss[nposs] = d;
nposs++;
}
}
}
// TODO: handle ties
if (nposs <= 0) {
return D_NONE;
}
bestdir = poss[rnd(0,nposs-1)];
reason = E_OK;
return bestdir;
}
@ -371,7 +420,7 @@ int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher) {
if ((i == 0) && seen) {
msg("%s %s knocked backwards!",lfname,isplayer(lf) ? "are" : "is");
}
trymove(lf, dir);
trymove(lf, dir, B_FALSE);
}
if (reason != E_OK) {
// failed to move
@ -399,12 +448,12 @@ int moveawayfrom(lifeform_t *lf, cell_t *dst ) {
return B_FALSE;
}
// move towards them
// move away from them
dir = getdiraway(lf->cell, dst, B_TRUE);
if (dir == D_NONE) {
rv = B_TRUE;
} else {
rv = trymove(lf, dir);
rv = trymove(lf, dir, B_TRUE);
}
return rv;
}
@ -534,7 +583,7 @@ int movelf(lifeform_t *lf, cell_t *newcell) {
}
// does anyone else see you?
if (gamestarted) {
if ((gamemode == GM_GAMESTARTED)) {
for (l = newcell->map->lf ; l ; l = l->next) {
if (l != lf) {
if (haslos(l, newcell)) {
@ -558,19 +607,44 @@ int movelf(lifeform_t *lf, cell_t *newcell) {
// basically this is a warpper for 'movelf' which
// does other game things like telling the player
// what is here.
int moveto(lifeform_t *lf, cell_t *newcell) {
return real_moveto(lf,newcell,B_FALSE);
}
int real_moveto(lifeform_t *lf, cell_t *newcell, int dontclearmsg) {
int moveto(lifeform_t *lf, cell_t *newcell, int onpurpose) {
char lfname[BUFLEN];
int didmsg;
int dontclearmsg = B_FALSE;
int predark = B_FALSE,postdark = B_FALSE;
if (!onpurpose || !isplayer(lf)) {
dontclearmsg = B_TRUE;
}
getlfname(lf, lfname);
// is current cell dark?
if (isplayer(lf)) {
if (!haslos(lf, lf->cell) && !isblind(lf)) {
predark = B_TRUE;
}
}
// actually do the move
didmsg = movelf(lf, newcell);
if (isplayer(lf)) {
// is new cell dark?
if (!haslos(lf, lf->cell) && !isblind(lf)) {
postdark = B_TRUE;
} else {
killflagsofid(lf->flags, F_DONEDARKMSG);
}
// just moved into a dark area - announce it.
if (postdark && !predark && !lfhasflag(lf, F_DONEDARKMSG)) {
msg("It is pitch black!");
addflag(lf->flags, F_DONEDARKMSG, B_TRUE, NA, NA, NULL);
dontclearmsg = B_TRUE;
}
}
if (dontclearmsg) {
didmsg = B_TRUE;
}
@ -633,7 +707,7 @@ int movetowards(lifeform_t *lf, cell_t *dst) {
// move towards them
dir = getdirtowards(lf->cell, dst, lf, B_TRUE);
if (dir != D_NONE) {
rv = trymove(lf, dir);
rv = trymove(lf, dir, B_TRUE);
}
return rv;
}
@ -790,9 +864,9 @@ int closedoor(lifeform_t *lf, object_t *o) {
return B_TRUE;
}
// any object other than the door?
// any solid object other than the door?
for (oo = cell->obpile->first ; oo ; oo = oo->next) {
if (oo != o) {
if ((oo != o) && (getmaterialstate(oo->material->id) == MS_SOLID)) {
if (lf && isplayer(lf)) {
char inwayname[BUFLEN];
getobname(oo, inwayname, oo->amt);
@ -836,7 +910,7 @@ int closedoor(lifeform_t *lf, object_t *o) {
}
int tryrun(lifeform_t *lf, int dir) {
if (!trymove(lf, dir)) {
if (!trymove(lf, dir, B_TRUE)) {
addflag(lf->flags, F_RUNNING, dir, NA, NA, NULL);
}
return B_FALSE;
@ -901,6 +975,11 @@ int teleportto(lifeform_t *lf, cell_t *c, int wantsmoke) {
}
movelf(lf, c);
// addob(lf->cell->obpile, "cloud of smoke");
if (cansee(player, lf)) {
redraw(); // redraw screen
}
if (isplayer(lf)) {
msg("Suddenly, your surroundings appear different!");
} else if (cansee(player, lf)) {
@ -917,7 +996,7 @@ int teleportto(lifeform_t *lf, cell_t *c, int wantsmoke) {
}
int trymove(lifeform_t *lf, int dir) {
int trymove(lifeform_t *lf, int dir, int onpurpose) {
cell_t *cell;
enum ERROR errcode;
char buf[BUFLEN];
@ -960,7 +1039,7 @@ int trymove(lifeform_t *lf, int dir) {
// move to new cell
reason = E_OK;
moveto(lf, cell);
moveto(lf, cell, onpurpose);
taketime(lf, getmovespeed(lf));
// slip on blood in new cell?
@ -983,12 +1062,14 @@ int trymove(lifeform_t *lf, int dir) {
getlfname(lf, buf);
msg("%s %ss into a wall.", buf, getmoveverb(lf));
}
if (isblind(lf)) {
//if (isblind(lf) || !haslos(lf, cell)) {
if (!haslos(lf, cell)) {
if (isplayer(lf)) {
// only take damage if we didn't know about this
if (!cell->known) {
sprintf(buf, "%sing into a wall", getmoveverb(lf));
losehp(lf, 1, DT_BASH, NULL, buf);
// we now know there is a wall there.
cell->known = B_TRUE;
taketime(lf, getmovespeed(lf));
}
@ -1095,7 +1176,7 @@ int trymove(lifeform_t *lf, int dir) {
killflagsofid(lf->flags, F_GRABBEDBY);
killflagsofid(grabbedby->flags, F_GRABBING);
// move - don't clear the 'you break free from' msg
real_moveto(lf, cell, B_TRUE);
moveto(lf, cell, B_TRUE);
} else {
if (isplayer(lf)) {
msg("You cannot get away from %s!",gbname);
@ -1144,12 +1225,19 @@ int willmove(lifeform_t *lf, int dir, enum ERROR *error) {
// don't attack other monsters
if (cell->lf) {
if (!isplayer(lf)) {
if (!isplayer(lf)) { // if we are a monster
// friendly monsters: don't hit other friendlies
if (isfriendly(lf) && (isfriendly(cell->lf) || isplayer(cell->lf)) ) {
return B_FALSE;
} else if (ispeaceful(lf)) { // peaceful mosnters: don't hit anyone
return B_FALSE;
} else { // hostile/nonfriendly monsters - don't hit other monsters
if (!isplayer(cell->lf) && !isfriendly(cell->lf)) {
return B_FALSE;
}
}
}
}
return B_TRUE;
}

5
move.h
View File

@ -13,13 +13,12 @@ int getdirtowards(cell_t *src, cell_t *dst, lifeform_t *srclf, int wantcheck);
int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher);
int moveawayfrom(lifeform_t *lf, cell_t *dst);
int movelf(lifeform_t *lf, cell_t *newcell);
int moveto(lifeform_t *lf, cell_t *newcell);
int real_moveto(lifeform_t *lf, cell_t *newcell, int dontclearmsg);
int moveto(lifeform_t *lf, cell_t *newcell, int onpurpose);
int movetowards(lifeform_t *lf, cell_t *dst);
int opendoorat(lifeform_t *lf, cell_t *c);
int opendoor(lifeform_t *lf, object_t *o);
int pullnextto(lifeform_t *lf, cell_t *c);
int teleportto(lifeform_t *lf, cell_t *c, int wantsmoke);
int trymove(lifeform_t *lf, int dir);
int trymove(lifeform_t *lf, int dir, int onpurpose);
int tryrun(lifeform_t *lf, int dir);
int willmove(lifeform_t *lf, int dir, enum ERROR *error);

23
nexus.c
View File

@ -54,7 +54,7 @@ void *rdata; // globel for returning data
lifeform_t *player = NULL;
int gameover;
int gamestarted = B_FALSE;
enum GAMEMODE gamemode = GM_FIRST;
long curtime = 0;
long timeleft = 0;
@ -147,7 +147,7 @@ int main(int argc, char **argv) {
// start game - this will cause debug messages to now
// go to the log file instead of stdout.
gamestarted = B_TRUE;
gamemode = GM_GAMESTARTED;
timeleft = 0; // reset game timer
// calculate initial light
@ -339,6 +339,7 @@ void donextturn(map_t *map) {
if (isplayer(who) || cansee(player, who)) {
needredraw = B_TRUE;
drawscreen();
}
// update gun targets
@ -439,6 +440,22 @@ char *getdirname(int dir) {
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";
}
return "?errordir?";
}
@ -459,6 +476,7 @@ int init(void) {
srand(time(NULL));
gamemode = GM_INIT;
initcommands();
initobjects();
@ -475,6 +493,7 @@ int init(void) {
//addcelltype(CT_DOOROPEN, "wooden door", '-', B_EMPTY, B_TRANS, MT_WOOD);
//addcelltype(CT_DOORCLOSED, "wooden door", '+', B_SOLID, B_OPAQUE, MT_WOOD);
gamemode = GM_VALIDATION;
if (validateobs()) {
return B_TRUE;
}

712
objects.c

File diff suppressed because it is too large Load Diff

View File

@ -45,9 +45,11 @@ void genhiddennames(void);
int getcharges(object_t *o);
int geteffecttime(int min, int max, enum BLESSTYPE isblessed);
objecttype_t *getlinkspell(object_t *o);
enum MATSTATE getmaterialstate(enum MATERIAL mat);
int getmissileaccuracy(lifeform_t *thrower, cell_t *where, object_t *missile, object_t *firearm, enum ATTRIB whichatt);
int getobaccuracy(object_t *wep);
int getobbonus(object_t *o);
skill_t *getobskill(object_t *o);
int getobvalue(object_t *o);
//int getobtypevalue(objecttype_t *ot);
char *getaccuracyname(int accpct);
@ -151,7 +153,7 @@ int obfits(object_t *o, obpile_t *op);
enum DAMTYPE oblastdamtype(object_t *o);
int brandappliesto(brand_t *om, objecttype_t *ot);
int obmatchescondition(object_t *o, long opts);
flag_t *obproduceslight(object_t *o);
int obproduceslight(object_t *o);
int obpropsmatch(object_t *a, object_t *b);
int obotpropsmatch(object_t *a, objecttype_t *b);
int operate(lifeform_t *lf, object_t *o, cell_t *where);

6
save.c
View File

@ -20,13 +20,13 @@ extern lifeform_t *player;
extern map_t *firstmap;
extern knowledge_t *knowledge;
int loading = B_FALSE;
extern enum GAMEMODE gamemode;
int loadall(void) {
DIR *dir;
struct dirent *ent;
loading = B_TRUE;
gamemode = GM_LOADING;
dir = opendir(MAPDIR);
if (!dir) {
@ -50,7 +50,7 @@ int loadall(void) {
loadsavegame();
loading = B_FALSE;
gamemode = GM_LOADED;
return B_FALSE;
}

193
spell.c
View File

@ -17,6 +17,9 @@
#include "text.h"
extern lifeform_t *player;
extern skill_t *firstskill, *lastskill;
extern prompt_t prompt;
extern WINDOW *msgwin;
@ -273,6 +276,35 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef
}
addtempflag(user->flags, F_SPRINTING, B_TRUE, NA, NA, NULL, howlong);
} else if (abilid == OT_A_LEARN) {
skill_t *sk;
char ch = 'a';
initprompt(&prompt, "Which skill will you learn?");
ch = 'a';
for (sk = firstskill ; sk ; sk = sk->next) {
sprintf(buf, "%s (%s)",getskillname(sk->id), getskilldesc(sk->id));
addchoice(&prompt, ch++, getskillname(sk->id), buf, sk);
}
getchoicestr(&prompt, B_FALSE, B_TRUE);
sk = (skill_t *)prompt.result;
if (sk) {
int i;
ch = 'a';
initprompt(&prompt, "How much will you learn this skill?");
for (i = getskill(user, sk->id) + 1 ; i <= PR_MASTER; i++) {
sprintf(buf, "%s",getskilllevelname(i));
addchoice(&prompt, ch++, buf, buf, NULL);
}
getchoice(&prompt);
while (strcmp(getskilllevelname(getskill(user, sk->id)), prompt.choice[prompt.selection].text)) {
giveskill(user, sk->id);
}
} else {
msg("Cancelled.");
}
return B_FALSE;
} else if (abilid == OT_A_DEBUG) {
cell_t *where;
where = askcoords("Debug who?", TT_MONSTER);
@ -373,6 +405,10 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef
if (!isplayer(user)) {
return B_TRUE;
}
if (isplayer(user)) {
msg("You can't inspect anything, since you can't see!");
return B_TRUE;
}
// blind?
@ -422,7 +458,7 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef
getobname(o, obname, o->amt);
msgnocap("This seems to be %s!", obname);
} else {
msg("You cannot determine what this is.");
msg("You have no idea what this is.");
addflag(user->flags, F_FAILEDINSPECT, o->type->id, NA, NA, NULL);
}
taketime(user, getactspeed(user));
@ -703,7 +739,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_
if (c->lf) {
char buf[BUFLEN];
getlfname(c->lf,buf);
if (!isimmuneto(c->lf->flags, DT_FIRE)) {
msg("%s burn%s!",buf,isplayer(c->lf) ? "" : "s");
}
losehp(c->lf, rolldie(2,10), DT_FIRE, caster, "a wave of fire");
}
}
@ -764,9 +802,9 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_
job_t *forcejob = NULL;
race_t *r = NULL;
int randomjobsok = B_TRUE;
// create a monster nearby
if (blessed) {
// create a monster nearby
if (blessed || (power >= 5)) {
// ask what kind of monster
askstring("Create what kind of monster", '?', buf, BUFLEN, NULL);
r = findracebyname(buf);
@ -807,20 +845,18 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_
}
return B_TRUE;
}
// create random monster in cell
// create random monster in cell
if (forcejob) {
randomjobsok = B_FALSE;
} else {
randomjobsok = B_TRUE;
}
if (blessed) {
//newlf = addlf(where, r->id, getrandommonlevel(where->map->depth));
newlf = addmonster(where, r->id, randomjobsok, 1);
} else {
newlf = addmonster(where, R_RANDOM, randomjobsok, 1);
if (!blessed) {
r = getreallyrandomrace();
}
newlf = addmonster(where, r->id, randomjobsok, 1);
if (newlf) {
// assign job if required
if (forcejob) {
@ -841,6 +877,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_
free(newbuf);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
// not worth any xp
addflag(newlf->flags, F_XPVAL, 0, NA, NA, NULL);
rv = B_FALSE;
} else {
// didn't work for some reason
@ -1694,14 +1732,26 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_
} else if (spellid == OT_S_MAPPING) {
int x,y;
int range;
map_t *m;
m = caster->cell->map;
if (power == 10) {
range = UNLIMITED;
} else {
range = power * 6;
}
// reveal map
for (y = 0; y < m->h; y++) {
for (x = 0; x < m->w; x++) {
cell_t *c;
c = getcellat(m, x, y);
if (c) {
if (range == UNLIMITED) {
c->known = B_TRUE;
} else if (getcelldist(caster->cell, c) <= range) {
c->known = B_TRUE;
}
}
}
}
@ -2168,19 +2218,26 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_
} else if (spellid == OT_S_LIGHT) {
lifeform_t *l;
if (!targcell) targcell = caster->cell;
// at power 3, you can control where the light appears
// at power 8, the light is permenant
if (power >= 3) {
if (!validatespellcell(caster, &targcell,TT_NONE, B_FALSE, spellid, power)) return B_TRUE;
} else {
targcell = caster->cell;
}
// centre light on the caster
if (haslos(player, targcell)) {
msg("The area is lit by a magical light!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
if (blessed) {
if (blessed || (power >= 8)) {
// permenant light
makelitradius(targcell, power*2, L_PERMLIGHT, -1);
} else {
// temporary light
makelitradius(targcell, power*2, L_PERMLIGHT, rnd(5,10) );
makelitradius(targcell, power*2, L_PERMLIGHT, rnd(5,10)+(power*2) );
}
// blind anyone with nightvis who sees it
@ -2210,9 +2267,14 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_
}
}
// anyone there glows
if ((targcell != caster->cell) && targcell->lf) {
addtempflag(targcell->lf->flags, F_PRODUCESLIGHT, B_TRUE, NA, NA, NULL, rnd(10,20));
// anyone there glows if the spell was controlled
if (targcell->lf && (power >= 3)) {
if (power >= 8) {
// permenant!
addflag(targcell->lf->flags, F_PRODUCESLIGHT, B_TRUE, NA, NA, NULL);
} else {
addtempflag(targcell->lf->flags, F_PRODUCESLIGHT, B_TRUE, NA, NA, NULL, rnd(10,20)+(power*2) );
}
}
} else if (spellid == OT_S_GRAVBOOST) {
@ -2587,6 +2649,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_
int howlong = 15;
howlong = getspellduration(10,20,blessed) + power;
addtempflag(target->flags, F_ATTRMOD, A_STR, -6, NA, NULL, howlong);
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else {
fizzle(caster);
}
@ -2670,6 +2733,39 @@ void fizzle(lifeform_t *caster) {
}
}
enum SKILL getschoolskill(enum SPELLSCHOOL ss) {
switch (ss) {
case SS_ALLOMANCY:
return SK_SS_ALLOMANCY;
case SS_AIR:
return SK_SS_AIR;
case SS_DEATH:
return SK_SS_DEATH;
case SS_DIVINATION:
return SK_SS_DIVINATION;
case SS_FIRE:
return SK_SS_FIRE;
case SS_ICE:
return SK_SS_ICE;
case SS_GRAVITY:
return SK_SS_GRAVITY;
case SS_LIFE:
return SK_SS_LIFE;
case SS_MODIFICATION:
return SK_SS_MODIFICATION;
case SS_MENTAL:
return SK_SS_MENTAL;
case SS_SUMMONING:
return SK_SS_SUMMONING;
case SS_TRANSLOCATION:
return SK_SS_TRANSLOCATION;
case SS_WILD:
return SK_SS_WILD;
default:
break;
}
return SK_NONE;
}
// returns a number between min & max
// if blessed, always the max
@ -2718,6 +2814,42 @@ int getspellmaxpower(enum OBTYPE spellid) {
return max;
}
char *getspellname(enum OBTYPE spellid, lifeform_t *lf, char *buf) {
int power;
enum SPELLSCHOOL ss;
objecttype_t *ot;
ot = findot(spellid);
strcpy(buf, ot->name);
capitalise(buf);
power = getspellpower(lf, spellid);
ss = getspellschool(spellid);
if (spellid == OT_S_TELEPORT) {
if (power >= 5) {
strcat(buf, "(controlled)");
}
} else if (spellid == OT_S_POLYMORPH) {
if (power >= 5) {
strcat(buf, "(controlled)");
}
} else if (spellid == OT_S_CREATEMONSTER) {
if (power >= 5) {
strcat(buf, "(controlled)");
}
} else if (spellid == OT_S_LIGHT) {
if (power >= 8) {
strcat(buf, "(perm,controlled)");
} else if (power >= 3) {
strcat(buf, "(controlled)");
}
} else {
if ((power > 1) && (ss != SS_ABILITY)) {
strcat(buf, " ");
strcat(buf, roman(power));
}
}
return buf;
}
int getspellpower(lifeform_t *lf, enum OBTYPE spellid) {
int power;
int statmod;
@ -2736,7 +2868,6 @@ int getspellpower(lifeform_t *lf, enum OBTYPE spellid) {
}
skill = getskill(lf, SK_SPELLCASTING);
if (skill == PR_INEPT) {
return 0;
}
@ -2745,14 +2876,20 @@ int getspellpower(lifeform_t *lf, enum OBTYPE spellid) {
// ie. at level 30 you get +5 power
power = (lf->level/6) + statmod + skill;
spelllev = getspelllevel(spellid);
if (spelllev > 0) {
power /= spelllev;
}
max = getspellmaxpower(spellid);
// specialised spellcasting - apply this AFTER dividing by spell level?
skill = getskill(lf, getschoolskill(getspellschool(spellid)));
if (skill != PR_INEPT) {
power += skill;
}
// enforce maximum
max = getspellmaxpower(spellid);
if (power > max) power = max;
return power;
}
@ -2849,7 +2986,9 @@ cell_t *validatespellcell(lifeform_t *caster, cell_t **targcell, int targtype, i
maxrange = getspellrange(spellid, power);
while (!done) {
if (*targcell == NULL) {
if (*targcell) {
done = B_TRUE;
} else {
// ask for a target cell
if (isplayer(caster)) {
cell_t *where;
@ -2863,7 +3002,8 @@ cell_t *validatespellcell(lifeform_t *caster, cell_t **targcell, int targtype, i
}
where = askcoords(buf, targtype);
if (where && haslos(caster, where)) {
if (where) {
//if (haslos(caster, where) || (where == caster->cell)) {
if (needlof && !haslof(caster, where)) {
// no line of sight
fizzle(caster);
@ -2880,7 +3020,9 @@ cell_t *validatespellcell(lifeform_t *caster, cell_t **targcell, int targtype, i
if (getiqname(getattr(caster, A_IQ), NULL) >= IQ_AVERAGE) {
objecttype_t *sp;
sp = findot(spellid);
if (sp && hasflag(sp->flags, F_AICASTATVICTIM)) {
if (sp) {
if (hasflagval(sp->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL) ||
hasflagval(sp->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL)) {
int ch;
ch = askchar("Really target yourself","yn","n", B_TRUE);
if (ch != 'y') {
@ -2891,6 +3033,15 @@ cell_t *validatespellcell(lifeform_t *caster, cell_t **targcell, int targtype, i
}
}
}
}
/*
} else {
msg("You can't see there!");
more();
*targcell = NULL;
done = B_FALSE;
}
*/
} else {
// no line of sight or invalid cell
fizzle(caster);

View File

@ -7,9 +7,11 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_
void fizzle(lifeform_t *caster);
//int getiqreq(enum OBTYPE oid);
int getmpcost(enum OBTYPE oid);
enum SKILL getschoolskill(enum SPELLSCHOOL ss);
int getspellduration(int min,int max,int blessed);
int getspelllevel(enum OBTYPE spellid);
int getspellmaxpower(enum OBTYPE spellid);
char *getspellname(enum OBTYPE spellid, lifeform_t *lf, char *buf);
int getspellpower(lifeform_t *lf, enum OBTYPE spellid);
enum SPELLSCHOOL getspellschool(enum OBTYPE spellid);
int getspellrange(enum OBTYPE spellid, int power);