- [+] chance to resist knockback?

- [+] "%s staggers backwards, but stands its ground."
    - [+] every size over human gives 10% chance to avoid.
- [+] monster jobs
    - [+] demonologist - can summon demons
    - [+] shaman - summon animals
    - [+] bezerker - can use rage
    - [+] necromancer 
    - [+] add f_startjobs to monsters
- [+] in makedesc_race, combine skills up until screen width. ie:
    - [+] Novice Listen, Novice CLimbing
    - [+] Also combine abilities, ie:
        - [+] Ability: jump
        - [+] Ability: xxx
        - [+] becomes:
        - [+] Abilities: jump, xxx
- [+] more psionic spells!
    - [+] psionic blast ? daels direct damage to intelligent creatures
          ( 1 to iq/10 )
    - [+] anticipate action: next xxx attacks from target lf against lf
          always miss
        - [+] "you easily dodge %s%s attack."
- [+] fire should spread on carpetted floors?  how to stop it spreading
      to the whole dungeon ?
    - [+] implement
    - [+] test
- [+] new poison type:
    - [+] migraine. sound causes pain (1 per volume?). light spells
          cause pain too.
        - [+] can get this from food too. (instead of gastro ?)
        - [+] mental spell to give a migraine - "brainache"
- [+] eating raw meat can give you migraine, or gastro.
- [+] make makedesc_race take player lore skills into account
    - [+] in describerace(), make title be:
        - [+] Race::glowbug (beginner level knowledge)
    - [+] LORE LEVELS:
        - [+] NOVICE: common knowledge
            - [+] breaths water 
        - [+] BEGINNER: only known if you've studied it a bit
            - [+] nocturnal, damage resistances/vulns
            - [+] silentmove
            - [+] stability
        - [+] ADEPT:
            - [+] only know it if you've studied it a LOT
                - [+] wantsobs
                - [+] spells
                - [+] morale
                - [+] eating habits
        - [+] when attacking something which is immune to your weapon, 
              warn you.
            - [+] (if your lorelev >= beginner)
- [+] change io.c to use command_t table
- [+] when selecting your starting weapon, show damage and accuracy 
- [+] scourge gains nullify at high levels
- [+] bug: ur-gnats not flying
    - [+] had f_nospells AND f_canwill flight
    - [+] made f_nospells not affect F_CANWILL, just F_CANCAST
- [+] shouldn't be able to cook firebug corpses
- [+] fire shoudl make crackling noises
- [+] nullify should anger god of magic, and not upset god of battle
- [+] nullify shouldn't affect natural flight fof birds
- [+] shouldn't remember your surroundings while raging
- [+] lfs shouldn't flee from themselves!
- [+] change attackverb for touch attacks.
- [+] eyebat gaze
    - [+] "your pair of sunglasses protects you"
    - [+] but the spellcast is never announced!
    - [+] fixed.
- [+] stun() should make lf lose concentration
- [+] fix a few logic errors in gaze protection code.
- [+] when i go up level as a scourge, I'm getting "You have gained the
      ability 'Nullify' (job perk)."
    - [+] i should be getting You have gained the ability 'Nullify VII'
          (job perk).
    - [+] why isn't 'hte power appearing
- [+] also when i start typing nullify, it says "It is too powerful for
      you to cast"
    - [+] because levabil isn't keeping pw:xxx text
    - [+] BUG in LEVABIL.
This commit is contained in:
Rob Pearce 2012-02-28 11:02:02 +00:00
parent afb0d30b23
commit 8186db9f5a
15 changed files with 867 additions and 426 deletions

185
attack.c
View File

@ -189,46 +189,46 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) {
// anyone there? if so just attack.
if (c->lf) {
if (!force && isplayer(lf) && isprone(lf)) {
if (!warnabout("Really attack while prone (-4 accuracy)?")) {
return B_TRUE;
// warnings
if (!force && isplayer(lf)) {
if (isprone(lf)) {
if (!warnabout("Really attack while prone (-4 accuracy)?")) {
return B_TRUE;
}
}
}
if (!force && isplayer(lf) && !areenemies(lf,c->lf) && (getraceclass(c->lf) != RC_PLANT) && cansee(lf, c->lf)
&& !lfhasflag(lf, F_RAGE)) {
char ch;
char victimname[BUFLEN];
char buf[BUFLEN];
getlfname(c->lf, victimname);
switch (getallegiance(c->lf)) {
case AL_PEACEFUL:
snprintf(buf, BUFLEN, "Really attack the peaceful %s?",noprefix(victimname));
break;
case AL_FRIENDLY:
snprintf(buf, BUFLEN, "Really attack the allied %s?",noprefix(victimname));
break;
default:
snprintf(buf, BUFLEN, "Really attack the allied %s?",noprefix(victimname));
break;
if (!areenemies(lf,c->lf) && (getraceclass(c->lf) != RC_PLANT) && cansee(lf, c->lf)
&& !lfhasflag(lf, F_RAGE)) {
char ch;
char victimname[BUFLEN];
char buf[BUFLEN];
getlfname(c->lf, victimname);
switch (getallegiance(c->lf)) {
case AL_PEACEFUL:
snprintf(buf, BUFLEN, "Really attack the peaceful %s?",noprefix(victimname));
break;
case AL_FRIENDLY:
snprintf(buf, BUFLEN, "Really attack the allied %s?",noprefix(victimname));
break;
default:
snprintf(buf, BUFLEN, "Really attack the allied %s?",noprefix(victimname));
break;
}
ch = askchar(buf, "yn","n", B_TRUE, B_FALSE);
if (ch == 'n') {
// cancel.
return B_TRUE;
}
attackedpeaceful = B_TRUE;
// non-evil players get no xp for attacking peaceful lfs
if ((isplayer(lf) || areallies(player, lf)) && (getalignment(lf) != AL_EVIL)) {
killflagsofid(c->lf->flags, F_XPVAL);
addflag(c->lf->flags, F_XPVAL, 0, NA, NA, NULL);
real_warnabout(TEXT_WARN_NOXP_GOODVSPEACEFUL, PERMENANT, B_FALSE);
}
}
ch = askchar(buf, "yn","n", B_TRUE, B_FALSE);
if (ch == 'n') {
// cancel.
return B_TRUE;
}
attackedpeaceful = B_TRUE;
// non-evil players get no xp for attacking peaceful lfs
if ((isplayer(lf) || areallies(player, lf)) && (getalignment(lf) != AL_EVIL)) {
killflagsofid(c->lf->flags, F_XPVAL);
addflag(c->lf->flags, F_XPVAL, 0, NA, NA, NULL);
real_warnabout(TEXT_WARN_NOXP_GOODVSPEACEFUL, PERMENANT, B_FALSE);
}
}
// above average wisdom will prvent you from annoying your god
if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_GTAVERAGE) {
if (!force && isplayer(lf)) {
// above average wisdom will prvent you from annoying your god
if (getattrbracket(getattr(lf, A_WIS), A_WIS, NULL) >= AT_GTAVERAGE) {
enum HELPLESSTYPE how;
if (ishelplessvictim(c->lf, lf, &how)) {
int dowarning = B_FALSE;
@ -248,41 +248,41 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) {
}
}
}
if ((getraceclass(c->lf) == RC_PLANT) && godprayedto(R_GODNATURE)) {
char victimname[BUFLEN],buf[BUFLEN];
getlfname(c->lf, victimname);
snprintf(buf, BUFLEN, "Really attack %s?",victimname);
if (!warnabout(buf)) {
return B_TRUE;
}
}
}
if (!force && isplayer(lf) && (getraceclass(c->lf) == RC_PLANT) && godprayedto(R_GODNATURE)) {
char victimname[BUFLEN],buf[BUFLEN];
getlfname(c->lf, victimname);
snprintf(buf, BUFLEN, "Really attack %s?",victimname);
if (!warnabout(buf)) {
if (lfhasflag(lf, F_HASNEWLEVEL)) {
if (!warnabout(TEXT_WARN_ATTACK_NOXP)) {
return B_TRUE;
}
}
}
if (!force && isplayer(lf) && lfhasflag(lf, F_HASNEWLEVEL)) {
if (!warnabout(TEXT_WARN_ATTACK_NOXP)) {
return B_TRUE;
// player walked into someone who was feigning death?
if (lfhasflag(c->lf, F_FEIGNINGDEATH)) {
char vicname[BUFLEN];
killflagsofid(c->lf->flags, F_FEIGNINGDEATH);
getlfname(c->lf, vicname);
capitalise(vicname);
if (cansee(lf, c->lf)) {
msg("Hey! %s was just feigning death!", vicname);
} else {
msg("You bump into someone!");
}
killflagsofid(c->lf->flags, F_PRONE);
// still counts as a move!
addflagifneeded(lf->flags, F_TOOKACTION, B_TRUE, NA, NA, NULL);
taketime(lf, getmovespeed(lf));
return B_FALSE;
}
}
// player walked into someone who was feigning death?
if (isplayer(lf) && lfhasflag(c->lf, F_FEIGNINGDEATH) && !force) {
char vicname[BUFLEN];
killflagsofid(c->lf->flags, F_FEIGNINGDEATH);
getlfname(c->lf, vicname);
capitalise(vicname);
if (cansee(lf, c->lf)) {
msg("Hey! %s was just feigning death!", vicname);
} else {
msg("You bump into someone!");
}
killflagsofid(c->lf->flags, F_PRONE);
// still counts as a move!
addflagifneeded(lf->flags, F_TOOKACTION, B_TRUE, NA, NA, NULL);
taketime(lf, getmovespeed(lf));
return B_FALSE;
}
attacktype = AT_LF;
attacktarget = c->lf;
attacklfid = c->lf->id; // remember for later
@ -368,13 +368,8 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) {
innateattacks = countinnateattacks(lf);
// take time
attacktime = getattackspeed(lf);
if (!lfhasflag(lf, F_COMBOSTRIKE)) {
taketime(lf, attacktime);
}
if (nweps <= 0) {
if (isplayer(lf)) {
msg("You cannot attack!");
@ -430,8 +425,39 @@ int attackcell(lifeform_t *lf, cell_t *c, int force) {
}
}
// lore about this race will tell you if you will do no damage.
if ((attacktype == AT_LF) && isplayer(lf) && wep[0]) {
lifeform_t *victim;
victim = (lifeform_t *)attacktarget;
if (getlorelevel(player, victim->race->raceclass->id) >= PR_BEGINNER) {
enum DAMTYPE dt;
char buf[BUFLEN],victimname[BUFLEN];
getlfname(victim, victimname);
dt = getdamtype(wep[0]);
if (isimmuneto(victim->flags, dt, B_FALSE)) {
snprintf(buf, BUFLEN, "%s is immune to %s damage. Really attack?",victimname,
getdamname(dt));
if (!warnabout(buf)) {
return B_TRUE;
}
} else if (isresistantto(victim->flags, dt, B_FALSE)) {
snprintf(buf, BUFLEN, "%s is resistant to %s damage. Really attack?",victimname,
getdamname(dt));
if (!warnabout(buf)) {
return B_TRUE;
}
}
}
}
if (maxattacks) {
addflagifneeded(lf->flags, F_TOOKACTION, B_TRUE, NA, NA, NULL);
if (!lfhasflag(lf, F_COMBOSTRIKE)) {
taketime(lf, attacktime);
}
}
attacksdone = 0;
@ -1373,14 +1399,18 @@ int attacklf(lifeform_t *lf, lifeform_t *victim, object_t *wep, flag_t *damflag)
buf, getpossessive(buf));
}
} else {
int anticipated = B_FALSE;
if (lfhasflagval(victim, F_ANTICIPATE, lf->id, NA, NA, NULL)) {
anticipated = B_TRUE;
}
if (isplayer(lf)) {
msg("You miss %s.", victimname);
msg("You %smiss %s.", anticipated ? "wildly " : "", victimname);
} else {
if (cansee(player, lf)) {
// capitalise first letter
snprintf(buf, BUFLEN, "%s",attackername);
msg("%s misses %s.", buf, victimname);
msg("%s %smisses %s.", buf, anticipated ? "wildly " : "", victimname);
}
}
@ -2368,10 +2398,17 @@ int rolltohit(lifeform_t *lf, lifeform_t *victim, object_t *wep, int *critical)
enum SKILLLEVEL lorelev = PR_INEPT;
flag_t *f;
// default
if (critical) *critical = 0;
// anticipate action spell?
if (lfhasflagval(victim, F_ANTICIPATE, lf->id, NA, NA, NULL)) {
return B_FALSE;
}
// remember lore about victim...
lorelev = getlorelevel(lf, victim->race->raceclass->id);
f = lfhasflag(lf, F_TRUESTRIKE);
if (f) {
if (f->val[0] > 1) {
@ -2381,6 +2418,7 @@ int rolltohit(lifeform_t *lf, lifeform_t *victim, object_t *wep, int *critical)
}
gothit = B_TRUE;
} else if (critical && *critical) {
// forced critical?
gothit = B_TRUE;
} else {
int reachpenalty = 0;
@ -2448,9 +2486,6 @@ int rolltohit(lifeform_t *lf, lifeform_t *victim, object_t *wep, int *critical)
// critical chance
if (critical) {
// default
*critical = 0;
if (gothit) {
if (lfhasflag(lf, F_AIMEDSTRIKE)) {
*critical = 1;

190
data.c
View File

@ -1,3 +1,4 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -81,6 +82,8 @@ option_t *addoption(enum OPTION id, char *text, int def) {
command_t *addcommand(enum COMMAND id, char ch, char *desc) {
command_t *a;
assert(!findcommand(id));
// add to the end of the list
if (firstcommand == NULL) {
firstcommand = malloc(sizeof(command_t));
@ -105,6 +108,31 @@ command_t *addcommand(enum COMMAND id, char ch, char *desc) {
}
void initcommands(void) {
// Movement
addcommand(CMD_MOVE_N, 'k', "Walk north.");
addcommand(CMD_MOVE_NE, 'u', "Walk northeast.");
addcommand(CMD_MOVE_E, 'l', "Walk east.");
addcommand(CMD_MOVE_SE, 'n', "Walk southeast.");
addcommand(CMD_MOVE_S, 'j', "Walk south.");
addcommand(CMD_MOVE_SW, 'b', "Walk southwest");
addcommand(CMD_MOVE_W, 'h', "Walk west.");
addcommand(CMD_MOVE_NW, 'y', "Walk northwest");
addcommand(CMD_RUN_N, 'K', "Autowalk north.");
addcommand(CMD_RUN_NE, 'U', "Autowalk northeast.");
addcommand(CMD_RUN_E, 'L', "Autowalk east.");
addcommand(CMD_RUN_SE, 'N', "Autowalk southeast.");
addcommand(CMD_RUN_S, 'J', "Autowalk south.");
addcommand(CMD_RUN_SW, 'B', "Autowalk southwest.");
addcommand(CMD_RUN_W, 'H', "Autowalk west.");
addcommand(CMD_RUN_NW, 'Y', "Autowalk northwest.");
addcommand(CMD_TURN_N, CMD_TURN_N, "Turn to face North.");
addcommand(CMD_TURN_NE, CMD_TURN_NE, "Turn to face Northeast.");
addcommand(CMD_TURN_E, CMD_TURN_E, "Turn to face East.");
addcommand(CMD_TURN_SE, CMD_TURN_SE, "Turn to face Southeast.");
addcommand(CMD_TURN_S, CMD_TURN_S, "Turn to face South.");
addcommand(CMD_TURN_SW, CMD_TURN_SW, "Turn to face Southwest.");
addcommand(CMD_TURN_W, CMD_TURN_W, "Turn to face West.");
addcommand(CMD_TURN_NW, CMD_TURN_NW, "Turn to face Northwest.");
// Actions
addcommand(CMD_UP, '<', "Go up stairs.");
addcommand(CMD_DOWN, '>', "Go down stairs, enter a shop/portal.");
@ -115,7 +143,6 @@ void initcommands(void) {
//addcommand(CMD_DROP, 'd', "Drop an item.");
addcommand(CMD_DROPMULTI, 'd', "Drop one or more items.");
addcommand(CMD_EAT, 'e', "Eat something.");
addcommand(CMD_EAT, 'E', "Enhance your skills.");
addcommand(CMD_MAGIC, 'm', "Use magic or abilities.");
addcommand(CMD_MEMMAGIC, 'M', "Memorise a hotkey for magic or abilities.");
addcommand(CMD_OFFER, 'O', "Offer a sacrifice to the gods.");
@ -124,7 +151,7 @@ void initcommands(void) {
addcommand(CMD_QUAFF, 'q', "Quaff (drink) a potion.");
addcommand(CMD_READ, 'r', "Read a scroll/book.");
addcommand(CMD_RESTFULL, 'R', "Rest until healed, or train your skills.");
addcommand(CMD_THROW, 's', "Step carefully.");
addcommand(CMD_SLOWWALK, 's', "Step carefully.");
addcommand(CMD_THROW, 't', "Throw an object.");
addcommand(CMD_TAKEOFF, 'T', "Take off an item of clothing/jewelery.");
addcommand(CMD_WEILD, 'w', "Weild a weapon.");
@ -134,6 +161,8 @@ void initcommands(void) {
addcommand(CMD_FIRE, 'f', "Fire your firearm/bow at your current target.");
addcommand(CMD_FIRENEW, 'F', "Fire your firearm/bow at a new target.");
addcommand(CMD_AIM, 'a', "Aim your current firearm/bow at a new target.");
addcommand(CMD_GUNRELOAD, 'a', "Reload current firearm/bow with current ammo.");
addcommand(CMD_NEXTTARGET, '\'', "Cycle to next firearm target.");
// Information
addcommand(CMD_HELP, '?', "Display this text.");
addcommand(CMD_INFOPLAYER, '@', "Display player stats.");
@ -143,8 +172,10 @@ void initcommands(void) {
addcommand(CMD_LOOKAROUND, '/', "Look at a remote cell.");
addcommand(CMD_INFOKNOWLEDGE, '\\', "Display known items.");
addcommand(CMD_MSGHIST, '|', "Display message history.");
addcommand(CMD_MSGHIST2, CH_HISTORY, "Display message history.");
addcommand(CMD_INV, 'i', "Display your inventory.");
// GAME FUNCTIONS
addcommand(CMD_OPTIONS, '=', "Change game options.");
addcommand(CMD_QUIT, 'Q', "Quit the game.");
addcommand(CMD_SAVEQUIT, 'S', "Save and quit the game.");
@ -891,7 +922,7 @@ void initjobs(void) {
addflag(lastjob->flags, F_CANHAVESUBJOB, SJ_NECROMANCER, NA, NA, NULL);
addflag(lastjob->flags, F_CANHAVESUBJOB, SJ_WILDMAGE, NA, NA, NULL);
// non-player jobs
// monster jobs
addjob(J_GUARD, "Guard", "Guards are paid mercenaries employed to protect a certain area. Accordingly, they are generally outfitetd with high quality armour.");
addflag(lastjob->flags, F_NOPLAYER, B_TRUE, NA, NA, NULL);
addflag(lastjob->flags, F_STARTOB, 100, NA, NA, "random good armour");
@ -902,6 +933,28 @@ void initjobs(void) {
addflag(lastjob->flags, F_STARTSKILL, SK_ARMOUR, PR_SKILLED, NA, NULL);
// 50% of guards are bribable
f = addflag(lastjob->flags, F_WANTS, OT_GOLD, NA, NA, NULL); addcondition(f, FC_IFMONSTER, 50);
addjob(J_DEMONOLOGIST, "Demonologist", "Demonologists frequently dabble into evil forces, summoning forth horrors from other planes of existence.");
addflag(lastjob->flags, F_NOPLAYER, B_TRUE, NA, NA, NULL);
addflag(lastjob->flags, F_STARTOB, 40, NA, NA, "bone helmet");
addflag(lastjob->flags, F_STARTOB, 70, NA, NA, "robes");
addflag(lastjob->flags, F_CANWILL, OT_S_SUMMONDEMON, 10, 10, "pw:2;");
addjob(J_SHAMAN, "Shaman", "Shamans call on natural magics to heal allies or summon hordes of angry animals.");
addflag(lastjob->flags, F_NOPLAYER, B_TRUE, NA, NA, NULL);
addflag(lastjob->flags, F_STARTOB, 40, NA, NA, "bone helmet");
addflag(lastjob->flags, F_STARTOB, 70, NA, NA, "robes");
addflag(lastjob->flags, F_STARTSKILL, SK_SS_NATURE, PR_ADEPT, NA, NULL);
addflag(lastjob->flags, F_CANWILL, OT_S_HEALINGMIN, 10, 10, "pw:5;");
f = addflag(lastjob->flags, F_CANWILL, OT_S_BLINDNESS, 10, 10, "pw:5;"); addcondition(f, FC_IFMONSTER, 30);
f = addflag(lastjob->flags, F_CANWILL, OT_S_SUMMONANIMALSSM, 10, 10, "pw:5;"); addcondition(f, FC_IFMONSTER, 50);
f = addflag(lastjob->flags, F_CANWILL, OT_S_SUMMONANIMALSMD, 10, 10, "pw:5;"); addcondition(f, FC_IFMONSTER, 30);
f = addflag(lastjob->flags, F_CANWILL, OT_S_SUMMONANIMALSLG, 10, 10, "pw:5;"); addcondition(f, FC_IFMONSTER, 15);
addjob(J_BERZERKER, "Berzerker", "Berzerkers can enter a start of berzerk rage for short periods.");
addflag(lastjob->flags, F_NOPLAYER, B_TRUE, NA, NA, NULL);
addflag(lastjob->flags, F_CANWILL, OT_A_RAGE, 20, 20, NULL);
/*
addjob(J_SHOPKEEPER, "Shopkeeper", "Shopkeepers make a living by selling goods to others. Always wary of thieves, most of them keep a shotgun under the counter.");
addflag(lastjob->flags, F_NOPLAYER, B_TRUE, NA, NA, NULL);
@ -919,6 +972,7 @@ void initobjects(void) {
// init poison types
addpoisontype(P_MIGRAINE, "a migraine", "Sick", "", OT_NONE, 0, 0, PS_DISEASE);
addpoisontype(P_COLD, "hypothermia", "Sick", "^bYOU cough#S violently.", OT_NONE, 1, 25, PS_DISEASE);
addpoisontype(P_FOOD, "gastroenteritis", "Poisoned", "^bYOU vomit#S violently.", OT_VOMITPOOL, 1, 25, PS_POISON);
addpoisontype(P_FOODBAD, "salmonella poisoning", "Poisoned", "^bYOU vomit#S violently.", OT_VOMITPOOL, 2, 33, PS_POISON);
@ -3636,6 +3690,14 @@ void initobjects(void) {
addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL);
addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL);
// l2
addot(OT_S_ANTICIPATE, "anticipate action", "Allows the caster to automatically dodge the target's attacks.", MT_NOTHING, 0, OC_SPELL, SZ_TINY);
addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power detemines the number of attacks dodged.");
addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL);
addflag(lastot->flags, F_MAXPOWER, 3, NA, NA, NULL);
addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL);
addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL);
addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL);
addot(OT_S_DISORIENT, "disorient", "Spins the target around to face away from the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY);
addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL);
@ -3664,6 +3726,13 @@ void initobjects(void) {
addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL);
addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL);
// l3
addot(OT_S_PSIBLAST, "psionic blast", "Assaults the target's brain with a mental feedback loop, dealing damage based on their intelligence.", MT_NOTHING, 0, OC_SPELL, SZ_TINY);
addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Creatures with higher intelligence will take more damage.");
addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL);
addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL);
addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL);
addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL);
addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL);
addot(OT_S_PSYARMOUR, "psychic armour", "Mentally block incoming attacks.", MT_NOTHING, 0, OC_SPELL, SZ_TINY);
addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The psychic armour's Armour Rating is ^bpower*4^n.");
addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL);
@ -4033,6 +4102,7 @@ void initobjects(void) {
// l4
addot(OT_S_NULLIFY, "nullify", "Permenantly removes the target's ability to use one or more spells/abilities.", MT_NOTHING, 0, OC_SPELL, SZ_TINY);
addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines the amount of spells nullified.");
addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "This spell will not anger gods who dislike magic.");
addflag(lastot->flags, F_SPELLSCHOOL, SS_WILD, NA, NA, NULL);
addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER|TT_DOOR, NA, NA, NULL);
addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL);
@ -5289,7 +5359,7 @@ void initobjects(void) {
addflag(lastot->flags, F_ONFIRE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_ONLYINROOM, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_IMPASSABLE, SZ_MIN, SZ_MAX, NA, NULL);
addflag(lastot->flags, F_MAKESNOISE, 33, 3, NA, "crackling flames.");
addot(OT_WEAPONRACK, "weapon rack", "A large matel frame, made to store weapons.", MT_METAL, 150, OC_FURNITURE, SZ_HUMAN);
addflag(lastot->flags, F_RARITY, H_ALL, 80, RR_UNCOMMON, NULL);
@ -5401,6 +5471,7 @@ void initobjects(void) {
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_PRODUCESLIGHT, 10, NA, NA, NULL);
addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!");
addflag(lastot->flags, F_MAKESNOISE, 33, 3, NA, "roaring flames.");
addot(OT_FIREMED, "medium fire", "A medium-sized roaring fire.", MT_FIRE, 0, OC_EFFECT, SZ_MEDIUM);
addflag(lastot->flags, F_GLYPH, C_RED, '}', NA, NULL);
addflag(lastot->flags, F_DIECONVERT, NA, NA, NA, "small fire");
@ -5412,6 +5483,7 @@ void initobjects(void) {
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_PRODUCESLIGHT, 7, NA, NA, NULL);
addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!");
addflag(lastot->flags, F_MAKESNOISE, 33, 3, NA, "crackling flames.");
addot(OT_FIRESMALL, "small fire", "A small blaze.", MT_FIRE, 0, OC_EFFECT, SZ_SMALL);
addflag(lastot->flags, F_GLYPH, C_RED, '}', NA, NULL);
addflag(lastot->flags, F_OBDIETEXT, B_TRUE, NA, NA, "goes out");
@ -5422,6 +5494,7 @@ void initobjects(void) {
addflag(lastot->flags, F_DAMAGABLE, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_PRODUCESLIGHT, 5, NA, NA, NULL);
addflag(lastot->flags, F_THEREISHERE, B_TRUE, NA, NA, "!");
addflag(lastot->flags, F_MAKESNOISE, 33, 3, NA, "crackling flames.");
addot(OT_STEAMCLOUD, "cloud of steam", "A thick cloud of scalding steam.", MT_GAS, 0, OC_EFFECT, SZ_HUMAN);
addflag(lastot->flags, F_GLYPH, C_WHITE, UNI_SHADEMED, NA, NULL);
@ -6343,13 +6416,13 @@ void initobjects(void) {
addot(OT_ACIDATTACK, "acidattack", "acid attack object", MT_WATER, 0, OC_WEAPON, SZ_TINY);
addflag(lastot->flags, F_DAM, DT_ACID, 2, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 75, NA, NA, NULL);
addflag(lastot->flags, F_ATTACKVERB, NA, NA, NA, "touch");
addflag(lastot->flags, F_ATTACKVERB, NA, NA, NA, "sting");
addflag(lastot->flags, F_NOSTRDAMMOD, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_USESSKILL, SK_NONE, NA, NA, NULL);
addflag(lastot->flags, F_UNARMEDWEP, B_TRUE, NA, NA, NULL);
addot(OT_TOUCHBURN, "burning touch", "burning touch object", MT_BONE, 0, OC_WEAPON, SZ_TINY);
addflag(lastot->flags, F_DAM, DT_FIRE, 1, NA, NULL);
addflag(lastot->flags, F_ATTACKVERB, NA, NA, NA, "touch");
addflag(lastot->flags, F_ATTACKVERB, NA, NA, NA, "burn");
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
addflag(lastot->flags, F_NOSTRDAMMOD, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_USESSKILL, SK_NONE, NA, NA, NULL);
@ -6357,7 +6430,7 @@ void initobjects(void) {
addot(OT_TOUCHCHILL, "chilling touch", "chilling touch object", MT_BONE, 0, OC_WEAPON, SZ_TINY);
addflag(lastot->flags, F_DAM, DT_COLD, 1, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
addflag(lastot->flags, F_ATTACKVERB, NA, NA, NA, "touch");
addflag(lastot->flags, F_ATTACKVERB, NA, NA, NA, "freeze");
addflag(lastot->flags, F_NOSTRDAMMOD, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_USESSKILL, SK_NONE, NA, NA, NULL);
addflag(lastot->flags, F_UNARMEDWEP, B_TRUE, NA, NA, NULL);
@ -6365,13 +6438,13 @@ void initobjects(void) {
addflag(lastot->flags, F_DAM, DT_HOLY, 1, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
addflag(lastot->flags, F_NOSTRDAMMOD, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_ATTACKVERB, NA, NA, NA, "touch");
addflag(lastot->flags, F_ATTACKVERB, NA, NA, NA, "smite");
addflag(lastot->flags, F_USESSKILL, SK_NONE, NA, NA, NULL);
addflag(lastot->flags, F_UNARMEDWEP, B_TRUE, NA, NA, NULL);
addot(OT_TOUCHNECROTIC, "necrotic touch", "generic undead touch object", MT_BONE, 0, OC_WEAPON, SZ_TINY);
addflag(lastot->flags, F_DAM, DT_NECROTIC, 1, NA, NULL);
addflag(lastot->flags, F_ACCURACY, 100, NA, NA, NULL);
addflag(lastot->flags, F_ATTACKVERB, NA, NA, NA, "touch");
addflag(lastot->flags, F_ATTACKVERB, NA, NA, NA, "drain");
addflag(lastot->flags, F_NOSTRDAMMOD, B_TRUE, NA, NA, NULL);
addflag(lastot->flags, F_USESSKILL, SK_NONE, NA, NA, NULL);
addflag(lastot->flags, F_UNARMEDWEP, B_TRUE, NA, NA, NULL);
@ -8381,6 +8454,8 @@ void initrace(void) {
addflag(lastrace->flags, F_SEEINDARK, 3, NA, NA, NULL);
addflag(lastrace->flags, F_CANWILL, OT_A_HEAVYBLOW, NA, NA, NULL);
addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTJOB, 33, J_GUARD, NA, NULL);
addflag(lastrace->flags, F_STARTJOB, 33, J_WARRIOR, NA, NULL);
addflag(lastrace->flags, F_STARTSKILL, SK_PERCEPTION, PR_ADEPT, NA, NULL);
addflag(lastrace->flags, F_MINIONS, 50, 1, 3, "goblin");
addflag(lastrace->flags, F_MINIONS, 20, 1, 3, "goblin warrior");
@ -8598,7 +8673,7 @@ void initrace(void) {
addflag(lastrace->flags, F_EATCONFER, F_ATTRMOD, A_STR, 5, "50");
addflag(lastrace->flags, F_EATCONFER, F_DTRESIST, DT_FIRE, NA, "50");
addrace(R_GIANTFIREFC, "flame giant shaman", 160, 'H', C_RED, MT_FLESH, RC_HUMANOID, "A subspecies of flame giant who have developed the ability to command the primal volcanic fires around them.");
addrace(R_GIANTFIREFC, "flame giant firemaster", 160, 'H', C_RED, MT_FLESH, RC_HUMANOID, "A subspecies of flame giant who have developed the ability to command the primal volcanic fires around them.");
setbodytype(lastrace, BT_HUMANOID);
lastrace->baseid = R_GIANTFIRE;
addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL);
@ -8711,48 +8786,10 @@ void initrace(void) {
addflag(lastrace->flags, F_MORALE, 10, NA, NA, NULL);
addflag(lastrace->flags, F_CANINE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_FATALFOOD, OT_CHOCOLATE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTJOB, 33, J_HUNTER, NA, NULL);
addflag(lastrace->flags, F_STARTJOB, 33, J_GUARD, NA, NULL);
addrace(R_GNOLLHM, "gnoll hunter", 130, 'h', C_BROWN, MT_FLESH, RC_HUMANOID, "Hunters are gnolls tasked with obtaining food, but can also turn their ranged skills to combat.");
setbodytype(lastrace, BT_HUMANOID);
setbodypartname(lastrace, BP_HANDS, "claws");
setbodypartname(lastrace, BP_RIGHTFINGER, "right foreclaw");
setbodypartname(lastrace, BP_LEFTFINGER, "left foreclaw");
lastrace->baseid = R_GNOLL;
addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL);
addflag(lastrace->flags, F_CORPSETYPE, NA, NA, NA, "gnoll corpse");
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_UNCOMMON, NULL);
addflag(lastrace->flags, F_RARITY, H_CAVE, NA, RR_UNCOMMON, NULL);
addflag(lastrace->flags, F_RARITY, H_FOREST, NA, RR_UNCOMMON, NULL);
addflag(lastrace->flags, F_RARITY, H_SWAMP, NA, RR_UNCOMMON, NULL);
addflag(lastrace->flags, F_HITDICE, NA, NA, NA, "10d4");
addflag(lastrace->flags, F_ARMOURRATING, 9, NA, NA, NULL);
addflag(lastrace->flags, F_ENHANCESMELL, 5, NA, NA, NULL);
addflag(lastrace->flags, F_EVASION, 0, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, NULL);
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_STARTATT, A_IQ, AT_LTAVERAGE, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_AGI, AT_GTAVERAGE, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, AT_GTAVERAGE, NA, NULL);
addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 10, NA, NULL);
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "leather armour");
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "longbow");
addflag(lastrace->flags, F_STARTOB, 100, NA, NA, "10-20 arrows");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "hand axe");
addflag(lastrace->flags, F_STARTOB, 50, NA, NA, "1-40 gold coins");
addflag(lastrace->flags, F_NOISETEXT, N_GETANGRY, 3, NA, "shouts^a shout");
addflag(lastrace->flags, F_SEEINDARK, 2, NA, NA, NULL);
addflag(lastrace->flags, F_PACKATTACK, 3, NA, 2, NULL);
addflag(lastrace->flags, F_MINIONS, 75, 1, 2, "gnoll");
addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTSKILL, SK_PERCEPTION, PR_SKILLED, NA, NULL);
addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_MORALE, 12, NA, NA, NULL);
addflag(lastrace->flags, F_CANINE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_FATALFOOD, OT_CHOCOLATE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTJOB, 33, J_WARRIOR, NA, NULL);
addflag(lastrace->flags, F_STARTJOB, 10, J_BERZERKER, NA, NULL);
addrace(R_GOBLIN, "goblin", 25, 'g', C_BROWN, MT_FLESH, RC_HUMANOID, "Small humanoids with flat faces, broad noses, pointed ears, and small, sharp fangs.");
setbodytype(lastrace, BT_HUMANOID);
@ -8783,10 +8820,11 @@ void initrace(void) {
addflag(lastrace->flags, F_DODGES, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_PACKATTACK, 2, DT_SLASH, 3, NULL);
addflag(lastrace->flags, F_HUMANOID, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTJOB, 10, J_ROGUE, NA, NULL);
addflag(lastrace->flags, F_STARTSKILL, SK_PERCEPTION, PR_NOVICE, NA, NULL);
addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_MORALE, 0, NA, NA, NULL);
addflag(lastrace->flags, F_STARTJOB, 10, J_ROGUE, NA, NULL);
addflag(lastrace->flags, F_STARTJOB, 5, J_DEMONOLOGIST, NA, NULL);
addrace(R_GOBLINR, "froglin", 25, 'g', C_BLUE, MT_FLESH, RC_HUMANOID, "River goblins (more commonly known as 'froglins') are blueish goblins with sleek, leathery skin. They seems constantly wet, and can leap like a frog.");
setbodytype(lastrace, BT_HUMANOID);
@ -8870,7 +8908,7 @@ void initrace(void) {
addflag(lastrace->flags, F_MORALE, 3, NA, NA, NULL);
addrace(R_GOBLINWAR, "goblin warrior", 30, 'g', C_BROWN, MT_FLESH, RC_HUMANOID, "Goblin Warriors are uncommon goblins with sufficient mental control to ungergo formal combat training (rather than just hack away mindlessly at their foes).");
addrace(R_GOBLINWAR, "goblin warlord", 30, 'g', C_BROWN, MT_FLESH, RC_HUMANOID, "Goblin Warriors are uncommon goblins with sufficient mental control to ungergo formal combat training (rather than just hack away mindlessly at their foes).");
setbodytype(lastrace, BT_HUMANOID);
lastrace->baseid = R_GOBLIN;
addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL);
@ -8938,7 +8976,7 @@ void initrace(void) {
addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_MORALE, 2, NA, NA, NULL);
addrace(R_GOBLINHEXER, "goblin shaman", 20, 'g', C_BROWN, MT_FLESH, RC_HUMANOID, "When a goblin develops an affinity for magic, they become known as shamans. Shamans aim to weaken their foes with hexs, providing easy kills for their comrades.");
addrace(R_GOBLINHEXER, "goblin witchdoctor", 20, 'g', C_BROWN, MT_FLESH, RC_HUMANOID, "When a goblin develops an affinity for magic, they become known as witchdoctor. Shamans aim to weaken their foes with hexs, providing easy kills for their comrades.");
setbodytype(lastrace, BT_HUMANOID);
lastrace->baseid = R_GOBLIN;
addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL);
@ -9067,6 +9105,7 @@ void initrace(void) {
addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_MORALE, 5, NA, NA, NULL);
addflag(lastrace->flags, F_STARTJOB, 33, J_GUARD, NA, NULL);
addflag(lastrace->flags, F_STARTJOB, 15, J_BERZERKER, NA, NULL);
addrace(R_HOBGOBLINWAR, "hobgoblin elite", 90, 'g', C_GREEN, MT_FLESH, RC_HUMANOID, "An exceptional hobgoblin commander who has achieved command of its own unit.");
setbodytype(lastrace, BT_HUMANOID);
@ -9231,6 +9270,8 @@ void initrace(void) {
addflag(lastrace->flags, F_STABILITY, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_MORALE, 5, NA, NA, NULL);
addflag(lastrace->flags, F_ALIGNMENT, AL_NONE, NA, NA, "gne");
addflag(lastrace->flags, F_STARTJOB, 15, J_WARRIOR, NA, NULL);
addflag(lastrace->flags, F_STARTJOB, 15, J_DRUID, NA, NULL);
addrace(R_MINOTAUR, "minotaur", 130, 'H', C_BROWN, MT_FLESH, RC_HUMANOID, "Legendary creatures with the head of a bull, with a strength and temperament to match.");
setbodytype(lastrace, BT_HUMANOID);
@ -9298,6 +9339,7 @@ void initrace(void) {
addflag(lastrace->flags, F_MORALE, 20, NA, NA, NULL);
addflag(lastrace->flags, F_STARTSKILL, SK_PERCEPTION, PR_NOVICE, NA, NULL);
addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTJOB, 20, J_WIZARD, SJ_RANDOM, NULL);
addrace(R_OGREWARHULK, "warhulk", 160, 'O', C_BROWN, MT_FLESH, RC_HUMANOID, "Warhulks are huge ogres, even angrier than their comrades.");
setbodytype(lastrace, BT_HUMANOID);
@ -9403,6 +9445,8 @@ void initrace(void) {
addflag(lastrace->flags, F_STARTSKILL, SK_PERCEPTION, PR_BEGINNER, NA, NULL);
addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_MORALE, 13, NA, NA, NULL);
addflag(lastrace->flags, F_STARTJOB, 20, J_DEMONOLOGIST, NA, NULL);
addflag(lastrace->flags, F_STARTJOB, 20, J_SHAMAN, NA, NULL);
addrace(R_ORCN, "norc", 90, 'o', C_BLUE, MT_FLESH, RC_HUMANOID, "While all orcs prefer the darkness, night orcs (or 'norcs') can actually _create_ it, spewing darkness from their bodies and blotting out all that is good and holy.");
setbodytype(lastrace, BT_HUMANOID);
@ -9484,7 +9528,7 @@ void initrace(void) {
addflag(lastrace->flags, F_NOCTURNAL, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_MORALE, 16, NA, NA, NULL);
addrace(R_ORCGRAND, "gnorc", 120, 'o', C_MAGENTA, MT_FLESH, RC_HUMANOID, "Even more powerful than blood orcs, grand orcs (or 'gnorcs') are both extremely rare and extremely powerful.");
addrace(R_ORCGRAND, "grorc", 120, 'o', C_MAGENTA, MT_FLESH, RC_HUMANOID, "Even more powerful than blood orcs, grand orcs (or 'grorcs') are both extremely rare and extremely powerful.");
setbodytype(lastrace, BT_HUMANOID);
addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
@ -9882,7 +9926,7 @@ void initrace(void) {
addflag(lastrace->flags, F_NOSLEEP, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_EATCONFER, F_DTRESIST, DT_FIRE, NA, "25");
addrace(R_SPRITEGRAVE, "grave sprite", 5, 'n', C_BLUE, MT_FLESH, RC_MAGIC, "A small magical creature made from corpse dust.");
addrace(R_SPRITEGRAVE, "grave sprite", 5, 'n', C_GREY, MT_FLESH, RC_MAGIC, "A small magical creature made from corpse dust.");
setbodytype(lastrace, BT_HUMANOID);
addflag(lastrace->flags, F_ALIGNMENT, AL_EVIL, NA, NA, NULL);
addflag(lastrace->flags, F_NOCORPSE, NA, NA, NA, NULL);
@ -10748,7 +10792,7 @@ void initrace(void) {
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, AT_GTAVERAGE, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_NATURALFLIGHT, B_TRUE, NA, NA, "");
@ -10773,7 +10817,7 @@ void initrace(void) {
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, AT_GTAVERAGE, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_MEDIUM, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_NATURALFLIGHT, B_TRUE, NA, NA, "");
@ -10786,7 +10830,7 @@ void initrace(void) {
addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_CANWILL, OT_A_SWOOP, NA, NA, NULL);
addflag(lastrace->flags, F_SWOOPRANGE, 8, NA, NA, NULL);
addflag(lastrace->flags, F_SWOOPRANGE, 5, NA, NA, NULL);
addflag(lastrace->flags, F_CANWILL, OT_S_COLDBURST, 2, 2, "pw:2;");
addflag(lastrace->flags, F_SPELLCASTTEXT, OT_NONE, NA, NA, "screeches");
addflag(lastrace->flags, F_MORALE, 10, NA, NA, NULL);
@ -10794,6 +10838,34 @@ void initrace(void) {
addflag(lastrace->flags, F_NOISETEXT, N_LOWHP, 3, NA, "screeches in pain^screeches of pain");
addflag(lastrace->flags, F_AVIAN, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_DTIMMUNE, DT_COLD, B_TRUE, NA, NULL);
addflag(lastrace->flags, F_EATCONFER, F_DTRESIST, DT_COLD, NA, "20");
addrace(R_GYRFALCON, "gyrfalcon", 1, 'A', C_WHITE, MT_FLESH, RC_ANIMAL, "An anormous falcon, commonly found in arctic climates."); // 'A' for Avian
setbodytype(lastrace, BT_BIRD);
addflag(lastrace->flags, F_RARITY, H_DUNGEON, NA, RR_UNCOMMON, NULL);
addflag(lastrace->flags, F_HOSTILE, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_IQ, IQ_ANIMAL, NA, NULL);
addflag(lastrace->flags, F_STARTATT, A_STR, AT_GTAVERAGE, NA, NULL);
addflag(lastrace->flags, F_SIZE, SZ_HUMAN, NA, NA, NULL);
addflag(lastrace->flags, F_MOVESPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_ACTIONSPEED, SP_NORMAL, NA, NA, "");
addflag(lastrace->flags, F_NATURALFLIGHT, B_TRUE, NA, NA, "");
addflag(lastrace->flags, F_CANWILL, OT_S_FLIGHT, NA, NA, NULL);
addflag(lastrace->flags, F_SPELLCASTTEXT, OT_S_FLIGHT, NA, NA, NULL);
addflag(lastrace->flags, F_HITDICE, NA, NA, NA, "11d4");
addflag(lastrace->flags, F_HASATTACK, OT_CLAWS, 14, NA, NULL);
addflag(lastrace->flags, F_EVASION, 10, NA, NA, NULL);
addflag(lastrace->flags, F_NOPACK, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_CANWILL, OT_A_SWOOP, NA, NA, NULL);
addflag(lastrace->flags, F_SWOOPRANGE, 8, NA, NA, NULL);
addflag(lastrace->flags, F_SPELLCASTTEXT, OT_NONE, NA, NA, "screeches");
addflag(lastrace->flags, F_MORALE, 15, NA, NA, NULL);
addflag(lastrace->flags, F_SEEINDARK, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_NOISETEXT, N_LOWHP, 3, NA, "screeches in pain^screeches of pain");
addflag(lastrace->flags, F_AVIAN, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_CANEATRAW, B_TRUE, NA, NA, NULL);
addflag(lastrace->flags, F_DTRESIST, DT_COLD, B_TRUE, NA, NULL);
addflag(lastrace->flags, F_EATCONFER, F_DTRESIST, DT_COLD, NA, "20");
addrace(R_LEECH, "giant leech", 10, 'j', C_MAGENTA, MT_FLESH, RC_ANIMAL, "A boneless blood-sucking creature. Quite dangerous until it eats it becomes satiated with blood, at which point it will slither off and fall asleep.");
addbodypart(lastrace, BP_BODY, NULL);

Binary file not shown.

45
defs.h
View File

@ -969,7 +969,6 @@ enum RACE {
R_GIANTFIREFC,
R_GIANTFIRETITAN,
R_GNOLL,
R_GNOLLHM,
R_GOBLIN,
R_GOBLINR,
R_GOBLINS,
@ -1043,6 +1042,7 @@ enum RACE {
R_DOGBLINK,
R_DOGDEATH,
R_DOGWAR,
R_GYRFALCON,
R_HAWK,
R_HAWKYOUNG,
R_HAWKBLOOD,
@ -1136,13 +1136,17 @@ enum JOB {
J_ROGUE,
//J_SHOPKEEPER,
J_WIZARD,
// monster jobs
// monster-only jobs
J_BERZERKER,
J_DEMONOLOGIST,
J_GUARD,
J_SHAMAN,
};
#define J_RANDOM J_NONE
enum SUBJOB {
SJ_NONE,
SJ_RANDOM,
// mage
SJ_AIRMAGE,
SJ_ICEMAGE,
@ -1502,6 +1506,7 @@ enum OBTYPE {
OT_S_SPEAKDEAD,
OT_S_TURNUNDEAD,
// -- mental / psionic
OT_S_ANTICIPATE,
OT_S_BAFFLE,
OT_S_CHARM,
OT_S_DISORIENT,
@ -1511,6 +1516,7 @@ enum OBTYPE {
OT_S_MINDSCAN,
OT_S_MIRRORIMAGE,
OT_S_PACIFY,
OT_S_PSIBLAST,
OT_S_PSYARMOUR,
OT_S_SLEEP,
OT_S_STUN,
@ -2123,6 +2129,7 @@ enum POISONTYPE {
P_FOOD,
P_FOODBAD,
P_GAS,
P_MIGRAINE,
P_ROT,
P_VENOM,
P_WEAKNESS,
@ -2980,6 +2987,8 @@ enum FLAG {
F_SHORTCUT, // spell keyboard shortcut.
// v0=slot (0-9)
// text=spell text
F_ANTICIPATE, // next v1 attacks from lfid v0 will auto miss.
// when v1 drops to 0, flag vanishes.
// for monsters
F_MPMOD, // this race gains/loses v0 mp each level
F_DOESNTMOVE, // this race doesn't move (but can still attack)
@ -3480,6 +3489,33 @@ enum ERROR {
enum COMMAND {
CMD_NONE,
// movement
CMD_MOVE_N,
CMD_MOVE_NE,
CMD_MOVE_E,
CMD_MOVE_SE,
CMD_MOVE_S,
CMD_MOVE_SW,
CMD_MOVE_W,
CMD_MOVE_NW,
CMD_RUN_N,
CMD_RUN_NE,
CMD_RUN_E,
CMD_RUN_SE,
CMD_RUN_S,
CMD_RUN_SW,
CMD_RUN_W,
CMD_RUN_NW,
CMD_TURN_N,
CMD_TURN_NE,
CMD_TURN_E,
CMD_TURN_SE,
CMD_TURN_S,
CMD_TURN_SW,
CMD_TURN_W,
CMD_TURN_NW,
//
CMD_AIM,
CMD_CLOSE,
CMD_COMMS,
@ -3490,6 +3526,7 @@ enum COMMAND {
CMD_FIRE,
CMD_FIRENEW,
CMD_FORCEATTACK,
CMD_GUNRELOAD,
CMD_HELP,
CMD_INFOARMOUR,
CMD_INFOKNOWLEDGE,
@ -3500,8 +3537,11 @@ enum COMMAND {
CMD_MAGIC,
CMD_MEMMAGIC,
CMD_MSGHIST,
CMD_MSGHIST2, // ie. ctrl-p
CMD_NEXTTARGET,
CMD_OFFER,
CMD_OPERATE,
CMD_OPTIONS,
CMD_PICKUP,
CMD_POUR,
CMD_QUAFF,
@ -3510,6 +3550,7 @@ enum COMMAND {
CMD_REST,
CMD_RESTFULL,
CMD_SAVEQUIT,
CMD_SLOWWALK,
CMD_TAKEOFF,
CMD_THROW,
CMD_UP,

View File

@ -39,6 +39,7 @@ r = rodent
R = robot
s = snake
S = spider
T = walkingtree-like monster (dryad, treant)
U = unearthly/horrific creature
V = vampire
w = small wyrm

361
io.c
View File

@ -1217,6 +1217,16 @@ int announceflaggain(lifeform_t *lf, flag_t *f) {
}
switch (f->id) {
case F_ANTICIPATE:
if (isplayer(lf)) {
lf2 = findlf(NULL, f->val[0]);
if (lf2) {
getlfname(lf2, buf);
msg("%s%s intentions enter your mind!", buf, getpossessive(buf));
donesomething = B_TRUE;
}
}
break;
case F_ARBOOST:
if (isplayer(lf)) {
msg("You feel %s!", (f->val[0] >= 0) ? "protected" : "vulnerable");
@ -1940,6 +1950,16 @@ int announceflagloss(lifeform_t *lf, flag_t *f) {
return B_FALSE;
}
switch (f->id) {
case F_ANTICIPATE:
if (isplayer(lf)) {
lf2 = findlf(NULL, f->val[0]);
if (lf2) {
getlfname(lf2, buf);
msg("You no longer know %s%s intentions.", buf, getpossessive(buf));
donesomething = B_TRUE;
}
}
break;
case F_ARBOOST:
if (isplayer(lf)) {
msg("You no longer feel so %s.", (f->val[0] >= 0) ? "protected" : "vulnerable");
@ -3481,6 +3501,13 @@ void centre(WINDOW *win, enum COLOUR col, int y, char *format, ... ) {
if (col != C_NONE) unsetcol(win, col);
}
enum COMMAND chartocmd(char ch) {
command_t *c;
for (c = firstcommand ; c ; c = c->next) {
if (c->ch == ch) return c->id;
}
return CMD_NONE;
}
int chartodir(char c) {
switch (tolower(c)) {
@ -3727,7 +3754,13 @@ void describerace(enum RACE rid) {
if (!r) return;
// title
snprintf(buf, BUFLEN, "Race::%s",r->name);
if (gamemode == GM_GAMESTARTED) {
enum SKILLLEVEL slev;
slev = getlorelevel(player, rid);
snprintf(buf, BUFLEN, "Race::%s (%s level lore)",r->name, getskilllevelname(slev));
} else {
snprintf(buf, BUFLEN, "Race::%s",r->name);
}
wattron(mainwin, A_BOLD);
mvwprintw(mainwin, 0, 0, "%s", buf);
wattroff(mainwin, A_BOLD);
@ -6751,6 +6784,25 @@ char *makedesc_race(enum RACE rid, char *retbuf, int showextra, int forplayersel
flag_t *retflag[MAXCANDIDATES],*f;
int nretflags,i;
flagpile_t *doneflags;
enum SKILLLEVEL lorelev;
if (forplayersel) {
lorelev = PR_MASTER;
} else {
lorelev = getlorelevel(player, rid);
}
// Your Lore skill for this race will determine how much information is shown.
//
// NOVICE:
// common knowledge
// eg: whether it breathes water, can fly, etc.
// BEGINNER:
// spells/powers
// knowledge known by studying this creature a little.
// eg: sleeping times, damage resist/vuln, silentmovement, morale
// ADEPT:
// everything.
doneflags = addflagpile(NULL, NULL);
@ -6827,35 +6879,37 @@ char *makedesc_race(enum RACE rid, char *retbuf, int showextra, int forplayersel
}
// abilities?
spellorabil[0] = OC_ABILITY;
spellorabil[1] = OC_SPELL;
getflags(r->flags, retflag, &nretflags, F_CANWILL, F_NONE);
for (n = 0; n <= 1; n++) {
strcpy(buf, "");
for (i = 0; i < nretflags; i++) {
objecttype_t *ot;
int power = 1;
f = retflag[i];
ot = findot(f->val[0]);
if (ot && (ot->obclass->id == spellorabil[n])) {
if (i == 0) {
sprintf(buf, "%s: %s", (spellorabil[n] == OC_ABILITY) ? "Ability" : "Spell",
ot->name);
} else {
sprintf(buf, ", %s", ot->name);
}
texttospellopts(f->text, "pw:", &power, NULL);
if (power) {
strcat(buf, " (power ");
strcat(buf, roman(power));
strcat(buf, ")");
if (lorelev >= PR_BEGINNER) {
spellorabil[0] = OC_ABILITY;
spellorabil[1] = OC_SPELL;
getflags(r->flags, retflag, &nretflags, F_CANWILL, F_NONE);
for (n = 0; n <= 1; n++) {
strcpy(buf, "");
for (i = 0; i < nretflags; i++) {
objecttype_t *ot;
int power = 1;
f = retflag[i];
ot = findot(f->val[0]);
if (ot && (ot->obclass->id == spellorabil[n])) {
if (i == 0) {
sprintf(buf, "%s: %s", (spellorabil[n] == OC_ABILITY) ? "Ability" : "Spell",
ot->name);
} else {
sprintf(buf, ", %s", ot->name);
}
texttospellopts(f->text, "pw:", &power, NULL);
if (power) {
strcat(buf, " (power ");
strcat(buf, roman(power));
strcat(buf, ")");
}
}
}
}
if (strlen(buf)) {
strncat(retbuf, "@- ", HUGEBUFLEN);
strcat(buf, "\n");
strncat(retbuf, buf, HUGEBUFLEN);
if (strlen(buf)) {
strncat(retbuf, "@- ", HUGEBUFLEN);
strcat(buf, "\n");
strncat(retbuf, buf, HUGEBUFLEN);
}
}
}
@ -6884,16 +6938,18 @@ char *makedesc_race(enum RACE rid, char *retbuf, int showextra, int forplayersel
strcpy(buf, "");
switch (f->id) {
case F_AUTOCREATEOB:
p = makeplural(f->text);
sprintf(buf, "Automatically creates %s around itself.", p);
free(p);
if (lorelev >= PR_NOVICE) {
p = makeplural(f->text);
sprintf(buf, "Automatically creates %s around itself.", p);
free(p);
}
break;
case F_AQUATIC: strcpy(buf, "Moves normally through water"); break;
case F_AWARENESS: strcpy(buf, "Can see in all directions."); break;
case F_CANEATRAW: strcpy(buf, "Can safely digest raw meat."); break;
case F_DODGES: strcpy(buf, "Can dodge fatal attacks into adjacent locations"); break;
case F_AQUATIC: if (lorelev >= PR_NOVICE) strcpy(buf, "Moves normally through water"); break;
case F_AWARENESS: if (lorelev >= PR_BEGINNER) strcpy(buf, "Can see in all directions."); break;
case F_CANEATRAW: if (lorelev >= PR_ADEPT) strcpy(buf, "Can safely digest raw meat."); break;
case F_DODGES: if (lorelev >= PR_ADEPT) strcpy(buf, "Can dodge fatal attacks into adjacent locations"); break;
case F_DTIMMUNE:
if (!hasflag(doneflags, F_DTIMMUNE)) {
if ((lorelev >= PR_BEGINNER) && !hasflag(doneflags, F_DTIMMUNE)) {
if (f->val[0] == DT_ALL) {
sprintf(buf, "Immune to %s.", getdamname(DT_ALL));
} else {
@ -6917,7 +6973,7 @@ char *makedesc_race(enum RACE rid, char *retbuf, int showextra, int forplayersel
}
break;
case F_DTRESIST:
if (!hasflag(doneflags, F_DTRESIST)) {
if ((lorelev >= PR_BEGINNER) && !hasflag(doneflags, F_DTRESIST)) {
if (f->val[0] == DT_ALL) {
sprintf(buf, "Resistant to %s.", getdamname(DT_ALL));
} else {
@ -6940,35 +6996,37 @@ char *makedesc_race(enum RACE rid, char *retbuf, int showextra, int forplayersel
addflag(doneflags, F_DTRESIST, B_TRUE, NA, NA, NULL);
}
break;
case F_ENHANCESMELL: sprintf(buf, "Enhanced sense of smell (range %d)", f->val[0]); break;
case F_FLYING: sprintf(buf, "Can fly at will"); break;
case F_HEAVYBLOW: sprintf(buf, "Attacks will knock enemies backwards"); break;
case F_ENHANCESMELL: if (lorelev >= PR_BEGINNER) sprintf(buf, "Enhanced sense of smell (range %d)", f->val[0]); break;
case F_FLYING: if (lorelev >= PR_NOVICE) sprintf(buf, "Can fly at will"); break;
case F_HEAVYBLOW: if (lorelev >= PR_ADEPT) sprintf(buf, "Attacks will knock enemies backwards"); break;
case F_HITCONFER:
if (f->val[0] == F_POISONED) {
poisontype_t *pt;
pt = findpoisontype(f->val[1]);
sprintf(buf, "Attacks inflict %s.", pt->name);
if (lorelev >= PR_ADEPT) {
if (f->val[0] == F_POISONED) {
poisontype_t *pt;
pt = findpoisontype(f->val[1]);
sprintf(buf, "Attacks inflict %s.", pt->name);
}
}
break;
case F_HUMANOID: if (!forplayersel) sprintf(buf, "Can use weapons and armour."); break;
case F_LEVITATING: sprintf(buf, "Can levitate at will"); break;
case F_MEDITATES: sprintf(buf, "Meditates to retain awareness while sleeping."); break;
case F_LEVITATING: if (lorelev >= PR_NOVICE) sprintf(buf, "Can levitate at will"); break;
case F_MEDITATES: if (lorelev >= PR_ADEPT) sprintf(buf, "Meditates to retain awareness while sleeping."); break;
case F_MPMOD: if (f->val[0] > 0) sprintf(buf, "+%d Mana", f->val[0]); break;
case F_NOSLEEP: sprintf(buf, "Does not sleep"); break;
case F_PACKATTACK: sprintf(buf, "Deals extra damage when in a pack."); break;
case F_PHALANX: sprintf(buf, "Gains extra defence when in a pack."); break;
case F_NOSLEEP: if (lorelev >= PR_BEGINNER) sprintf(buf, "Does not sleep"); break;
case F_PACKATTACK: if (lorelev >= PR_ADEPT) sprintf(buf, "Deals extra damage when in a pack."); break;
case F_PHALANX: if (lorelev >= PR_ADEPT) sprintf(buf, "Gains extra defence when in a pack."); break;
case F_PHOTOMEM: sprintf(buf, "Photographic memory"); break;
case F_QUICKBITE: sprintf(buf, "Can bite wounded enemies for extra damage"); break;
case F_REGENERATES: sprintf(buf, "Automatically regenerates health."); break;
case F_RESISTMAG: sprintf(buf, "Magic-resistant"); break;
case F_SEEINDARK: sprintf(buf, "Darkvision (range %d)", f->val[0]); break;
case F_SEEINVIS: sprintf(buf, "Can see invisible things"); break;
case F_SILENTMOVE: sprintf(buf, "Moves silently"); break;
case F_SPIDERCLIMB: sprintf(buf, "Adheres to walls"); break;
case F_STABILITY: sprintf(buf, "Will not fall on slippery ground."); break;
case F_QUICKBITE: if (lorelev >= PR_ADEPT) sprintf(buf, "Can bite wounded enemies for extra damage"); break;
case F_REGENERATES: if (lorelev >= PR_BEGINNER) sprintf(buf, "Automatically regenerates health."); break;
case F_RESISTMAG: if (lorelev >= PR_BEGINNER) sprintf(buf, "Magic-resistant"); break;
case F_SEEINDARK: if (lorelev >= PR_BEGINNER) sprintf(buf, "Darkvision (range %d)", f->val[0]); break;
case F_SEEINVIS: if (lorelev >= PR_ADEPT) sprintf(buf, "Can see invisible things"); break;
case F_SILENTMOVE: if (lorelev >= PR_BEGINNER) sprintf(buf, "Moves silently"); break;
case F_SPIDERCLIMB: if (lorelev >= PR_NOVICE) sprintf(buf, "Adheres to walls"); break;
case F_STABILITY: if (lorelev >= PR_BEGINNER) sprintf(buf, "Will not fall on slippery ground."); break;
case F_STENCH: sprintf(buf, "Emits a foul odour which affects others"); break;
case F_TREMORSENSE: sprintf(buf, "Can sense vibrations (range %d)", f->val[0]); break;
case F_VISRANGEMOD: if (f->val[0] > 0) sprintf(buf, "Enhanced vision range (+%d)", f->val[0]); break;
case F_TREMORSENSE: if (lorelev >= PR_BEGINNER) sprintf(buf, "Can sense vibrations (range %d)", f->val[0]); break;
case F_VISRANGEMOD: if (lorelev >= PR_BEGINNER) if (f->val[0] > 0) sprintf(buf, "Enhanced vision range (+%d)", f->val[0]); break;
default:
break;
}
@ -7016,11 +7074,11 @@ char *makedesc_race(enum RACE rid, char *retbuf, int showextra, int forplayersel
material_t *mt;
strcpy(buf, "");
switch (f->id) {
case F_CARNIVORE: sprintf(buf, "Will only eat meat."); break;
case F_DEAF: sprintf(buf, "Deaf"); break;
case F_DIURNAL: if (!forplayersel) sprintf(buf, "Sleeps at night."); break;
case F_CARNIVORE: if (lorelev >= PR_BEGINNER) sprintf(buf, "Will only eat meat."); break;
case F_DEAF: if (lorelev >= PR_BEGINNER) sprintf(buf, "Deaf"); break;
case F_DIURNAL: if ((lorelev >= PR_BEGINNER) && !forplayersel) sprintf(buf, "Sleeps at night."); break;
case F_DTVULN:
if (!hasflag(doneflags, F_DTVULN)) {
if ((lorelev >= PR_BEGINNER) && !hasflag(doneflags, F_DTVULN)) {
if (f->val[0] == DT_ALL) {
sprintf(buf, "Vulnerable to %s.", getdamname(DT_ALL));
} else {
@ -7043,14 +7101,16 @@ char *makedesc_race(enum RACE rid, char *retbuf, int showextra, int forplayersel
addflag(doneflags, F_DTVULN, B_TRUE, NA, NA, NULL);
}
break;
case F_FASTMETAB: sprintf(buf, "Fast metabolism (needs to eat often)"); break;
case F_FASTMETAB: if (lorelev >= PR_BEGINNER) sprintf(buf, "Fast metabolism (needs to eat often)"); break;
case F_MATVULN:
mt = findmaterial(f->val[0]);
sprintf(buf, "Takes %d%% damage from weapons made of %s.", f->val[1], mt->name);
if (lorelev >= PR_ADEPT) {
mt = findmaterial(f->val[0]);
sprintf(buf, "Takes %d%% damage from weapons made of %s.", f->val[1], mt->name);
}
break;
case F_MPMOD: if (f->val[0] < 0) sprintf(buf, "%d Mana", f->val[0]); break;
case F_NEEDSWATER: sprintf(buf, "Will suffocate without water"); break;
case F_NOCTURNAL: if (!forplayersel) sprintf(buf, "Sleeps during the day."); break;
case F_NEEDSWATER: if (lorelev >= PR_NOVICE) sprintf(buf, "Will suffocate without water"); break;
case F_NOCTURNAL: if ((lorelev >= PR_BEGINNER) && !forplayersel) sprintf(buf, "Sleeps during the day."); break;
case F_NOPACK: sprintf(buf, "Cannot carry objects."); break;
case F_SIZE:
if (hasflag(r->flags, F_HUMANOID) && (f->val[0] != SZ_HUMAN)) {
@ -7058,16 +7118,21 @@ char *makedesc_race(enum RACE rid, char *retbuf, int showextra, int forplayersel
}
break;
case F_STAYINROOM:
sprintf(buf, "Will not leave its home territory."); break;
if (lorelev >= PR_ADEPT) {
sprintf(buf, "Will not leave its home territory."); break;
}
break;
case F_TAMABLE:
if (!forplayersel) {
if ((lorelev >= PR_ADEPT) && !forplayersel) {
sprintf(buf, "Susceptible to bribery.");
}
break;
case F_VEGETARIAN: sprintf(buf, "Will not eat meat."); break;
case F_VISRANGEMOD: if (f->val[0] < 0) sprintf(buf, "Reduced vision range (%d)", f->val[0]); break;
case F_PARTVEGETARIAN: sprintf(buf, "Will only eat meat when hungry."); break;
case F_VEGETARIAN: if (lorelev >= PR_ADEPT) sprintf(buf, "Will not eat meat."); break;
case F_VISRANGEMOD:
if (lorelev >= PR_BEGINNER) {
if (f->val[0] < 0) sprintf(buf, "Reduced vision range (%d)", f->val[0]); break;
}
case F_PARTVEGETARIAN: if (lorelev >= PR_ADEPT) sprintf(buf, "Will only eat meat when hungry."); break;
default:
break;
}
@ -7424,10 +7489,12 @@ void makespellchoicelist(prompt_t *pr, lifeform_t *lf, char *ques, char *ques2,
} // end foreach spell school
/*
if (lfhasflag(lf, F_NOSPELLS) && (nposs == 0)) {
msg("%ss cannot use magic!", lf->race->name);
return;
}
*/
// list player's magic...
ch = 'a';
@ -9695,27 +9762,28 @@ void handleinput(void) {
break;
}
switch (ch) {
// HANDLE COMMANDS
switch (chartocmd(ch)) {
// movement
case 'h':
case 'j':
case 'k':
case 'l':
case 'y':
case 'u':
case 'b':
case 'n':
case CMD_MOVE_N:
case CMD_MOVE_NE:
case CMD_MOVE_E:
case CMD_MOVE_SE:
case CMD_MOVE_S:
case CMD_MOVE_SW:
case CMD_MOVE_W:
case CMD_MOVE_NW:
trymove(player, chartodir(ch), B_TRUE, B_FALSE);
break;
// run / strafe
case 'H':
case 'J':
case 'K':
case 'L':
case 'Y':
case 'U':
case 'B':
case 'N':
case CMD_RUN_N:
case CMD_RUN_NE:
case CMD_RUN_E:
case CMD_RUN_SE:
case CMD_RUN_S:
case CMD_RUN_SW:
case CMD_RUN_W:
case CMD_RUN_NW:
dir = chartodir(ch);
if (dir == player->facing) { // shift+samedir = run
tryrun(player, dir);
@ -9724,14 +9792,14 @@ void handleinput(void) {
}
break;
// turn
case CH_TURN_N:
case CH_TURN_E:
case CH_TURN_S:
case CH_TURN_W:
case CH_TURN_NE:
case CH_TURN_SE:
case CH_TURN_SW:
case CH_TURN_NW:
case CMD_TURN_N:
case CMD_TURN_NE:
case CMD_TURN_E:
case CMD_TURN_SE:
case CMD_TURN_S:
case CMD_TURN_SW:
case CMD_TURN_W:
case CMD_TURN_NW:
dir = chartodir(ch);
if (dir != player->facing) {
takerotationtime(player);
@ -9739,10 +9807,10 @@ void handleinput(void) {
drawscreen();
}
break;
case 's': // slowwalk
case CMD_SLOWWALK: // slowwalk
trysneak(player, D_NONE);
break;
case '.': // wait
case CMD_REST: // wait
addflag(player->flags, F_LASTCMD, NA, NA, NA, temp);
if (count > 1) {
addflag(player->flags, F_AUTOCMD, count, NA, NA, ".");
@ -9750,21 +9818,8 @@ void handleinput(void) {
rest(player, B_TRUE);
}
break;
// testing
/*
case '1':
gettimetext(buf);
msg("The current time is %s",buf);
break;
case '2':
msg("Something happens.");
msg("Something else happens.");
msg("Another thing is about to happen now.");
msg("Too many things are happening!");
break;
*/
// player commands
case 'A': // attack
case CMD_FORCEATTACK: // attack
addflag(player->flags, F_LASTCMD, NA, NA, NA, temp);
if (wantrepeat) {
doattackcell(repeatflag.val[2]);
@ -9772,127 +9827,127 @@ void handleinput(void) {
doattackcell('\0');
}
break;
case 'i': // inventory
case CMD_INV: // inventory
doinventory(player->pack);
break;
case '?': // help
case CMD_HELP: // help
dohelp('?');
break;
case '@': // display player stats
case CMD_INFOPLAYER: // display player stats
showlfstats(player, B_FALSE);
break;
case ']': // display armour
case CMD_INFOARMOUR: // display armour
showlfarmour(player);
break;
case ':': // look at what's here
case CMD_LOOKHERE: // look at what's here
dolook(player->cell, B_TRUE);
break;
case CH_HISTORY:
case '|': // msg history
case CMD_MSGHIST:
case CMD_MSGHIST2:
domsghist();
break;
case '/': // explain object
case CMD_LOOKAROUND: // explain object
doexplain("Select glyph to explain (v for info, ESC to cancel):");
break;
case '\\': // list knowledge
case CMD_INFOKNOWLEDGE: // list knowledge
doknowledgelist();
break;
case 'R': // rest
case CMD_RESTFULL: // rest
dorest();
break;
case 'm': // 'm'agic/abilities (magic)
case CMD_MAGIC: // 'm'agic/abilities (magic)
addflag(player->flags, F_LASTCMD, NA, NA, NA, temp);
domagic(OT_NONE, NA, NA);
break;
case 'M': // 'M'emorise magic/ability shortcut
case CMD_MEMMAGIC: // 'M'emorise magic/ability shortcut
domemmagic();
break;
case '<': // go up
case CMD_UP: // go up
if (isprone(player)) {
standup(player);
} else {
dostairs(D_UP);
}
break;
case '>': // go down
case CMD_DOWN: // go down
doenter(player);
break;
// firearm functions
case 'f': // fire gun
case CMD_FIRE: // fire gun
addflag(player->flags, F_LASTCMD, NA, NA, NA, temp);
dofire();
break;
case 'a': // aim
doselguntarget();
break;
case 'F': // aim then fire
case CMD_FIRENEW: // aim then fire
if (!doselguntarget()) {
dofire();
}
break;
case 'G': // reload Gun with current ammo
case CMD_AIM: // aim
doselguntarget();
break;
case CMD_GUNRELOAD: // reload Gun with current ammo
loadfirearmfast(player, B_TRUE);
break;
case '\'':
case CMD_NEXTTARGET:
donextguntarget();
break;
// object functions
case 'c': // close
case CMD_CLOSE: // close
addflag(player->flags, F_LASTCMD, NA, NA, NA, temp);
doclose();
break;
case 'C': // communicate
case CMD_COMMS: // communicate
docomms(NULL);
break;
case 'e': // eat
case CMD_EAT: // eat
doeat(player->pack);
break;
case 'd': // drop multiple things
case CMD_DROPMULTI: // drop multiple things
//dodrop(player->pack, B_SINGLE, player->cell->obpile);
dodrop(player->pack, B_MULTIPLE, player->cell->obpile);
break;
case 'o': // operate
case CMD_OPERATE: // operate
addflag(player->flags, F_LASTCMD, NA, NA, NA, temp);
dooperate(player->pack);
break;
case 'O':
case CMD_OFFER:
dooffer();
break;
case 'P': // Pour
case CMD_POUR: // Pour
dopour(player->pack);
break;
case 'W': // wear
case CMD_WEAR: // wear
dowear(player->pack);
break;
case 'w': // weild
case CMD_WEILD: // weild
doweild(player->pack);
break;
case 'T': // takeoff
case CMD_TAKEOFF: // takeoff
dotakeoff(player->pack);
break;
case ',': // pickup
case CMD_PICKUP: // pickup
dopickup(player->cell->obpile, B_FALSE);
break;
case 'r': // read
case CMD_READ: // read
doread(player->pack);
break;
case 'q': // quaff
case CMD_QUAFF: // quaff
doquaff(player->pack);
break;
case 't': // throw
case CMD_THROW: // throw
dothrow(player->pack, NULL);
break;
case 'x': // eXchange wepaon for secondary
case CMD_EXCHANGE: // eXchange wepaon for secondary
exchangeweapon(player);
break;
// GAME FUNCTIONS
case '=': // options
case CMD_OPTIONS: // options
dooptions();
break;
case 'Q': // quit
case CMD_QUIT: // quit
doquit();
break;
case 'S': // save + quit
case CMD_SAVEQUIT: // save + quit
if (savegame()) {
msg("Save failed.");
} else {

1
io.h
View File

@ -28,6 +28,7 @@ cell_t *askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *src
char *askstring(char *prompt, char punc, char *retbuf, int retbuflen, char *def);
vault_t *askvault(char *prompttext);
void centre(WINDOW *win, enum COLOUR col, int y, char *format, ... );
enum COMMAND chartocmd(char ch);
int chartodir(char ch);
char checkforkey(void);
int cleanupgfx(void);

255
lf.c
View File

@ -584,10 +584,6 @@ int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost) {
reason = E_OK;
ot = findot(oid);
if ((ot->obclass->id == OC_SPELL) && lfhasflag(lf, F_NOSPELLS)) {
reason = E_NOSPELLS;
return B_FALSE;
}
f = lfhasflagval(lf, F_CANWILL, oid, NA, NA, NULL);
if (f) {
@ -626,6 +622,12 @@ int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost) {
} else if (lfhasflagval(lf, F_CANCAST, oid, NA, NA, NULL)) {
int cost,power;
// override!
if ((ot->obclass->id == OC_SPELL) && lfhasflag(lf, F_NOSPELLS)) {
reason = E_NOSPELLS;
return B_FALSE;
}
// need >animal intelligence to cast spells
if (ot->obclass->id == OC_SPELL) {
if (getattrbracket(getattr(lf, A_IQ), A_IQ, NULL) <= IQ_ANIMAL) {
@ -653,7 +655,6 @@ int cancast(lifeform_t *lf, enum OBTYPE oid, int *mpcost) {
reason = E_NOMP;
return B_FALSE;
}
}
// do we have enough stamina to do this?
@ -1806,6 +1807,41 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar
}
}
// eye protection will stop some spells!
if (targcell && targcell->lf) {
flag_t *casttype;
lifeform_t *victim;
victim = targcell->lf;
casttype = lfhasflag(lf, F_CASTTYPE);
if (casttype) {
object_t *protob = NULL;
switch (casttype->val[0]) {
case CT_EYESPIT:
protob = getarmour(victim, BP_EYES);
break;
case CT_GAZE:
protob = eyesshaded(victim);
break;
default:
protob = NULL;
break;
}
if (protob) {
if (isplayer(victim)) {
char gbuf[BUFLEN];
getobname(protob, gbuf, protob->amt);
msg("Your %s protects you.", noprefix(gbuf));
} else if (cansee(player, victim)) {
char lfname[BUFLEN],gbuf[BUFLEN];
getobname(protob, gbuf, protob->amt);
getlfname(victim, lfname);
msg("%s%s %s protects it.", lfname, getpossessive(lfname), noprefix(gbuf) );
}
return B_FALSE;
}
}
}
if (!fromob) {
// willing this spell? reset counter!
// do this _before_ casting the spell,
@ -1883,8 +1919,10 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar
objecttype_t *ot;
flag_t *retflag[MAXCANDIDATES];
int nretflags,i;
// god of magic likes all spells
pleasegodmaybe(R_GODMAGIC, getspelllevel(sid));
if (!fromob) {
// god of battle hates all spells except nullify
if (!fromob && (sid != OT_S_NULLIFY)) {
angergodmaybe(R_GODBATTLE, 25, GA_SPELL);
}
ot = findot(sid);
@ -3703,7 +3741,11 @@ int eat(lifeform_t *lf, object_t *o) {
timemax = 50;
} else {
checkdiff = 20;
ptid = P_FOOD;
if (onein(3)) {
ptid = P_FOOD;
} else {
ptid = P_MIGRAINE;
}
timemin = 20;
timemax = 40;
}
@ -4277,29 +4319,29 @@ void enhanceskills(lifeform_t *lf) {
f = levelabilityready(lf);
while (f) {
if (f->id == F_LEVABIL) {
flag_t *abilflag[MAXCANDIDATES],*thisabil;
flag_t *abilflag[MAXCANDIDATES];
int nabilflags = 0;
int origborn,i;
thisabil = addtempflag(lf->flags, F_CANWILL, f->val[1], f->val[2], f->val[2], f->text, FROMJOB);
origborn = lf->born;
// already had this power with different options?
// already had this power with different options? remove it.
getflags(lf->flags, abilflag, &nabilflags, F_CANWILL, F_NONE);
for (i = 0;i < nabilflags; i++) {
if (abilflag[i] == thisabil) continue;
if ((abilflag[i]->val[0] == f->val[1]) && (abilflag[i]->lifetime == FROMJOB)) {
if ((abilflag[i]->val[0] == f->val[1]) && (abilflag[i]->lifetime == FROMJOB)) {
lf->born = B_FALSE; // stop flag loss from being announced
killflag(f);
killflag(abilflag[i]);
lf->born = origborn;
}
}
// now add the new one
addtempflag(lf->flags, F_CANWILL, f->val[1], f->val[2], f->val[2], f->text, FROMJOB);
} else if (f->id == F_LEVFLAG) {
addtempflag(lf->flags, f->val[1], f->val[2], NA, NA, f->text, FROMJOB);
} else if (f->id == F_LEVSKILL) {
giveskill(lf, f->val[1]);
} else if (f->id == F_LEVSPELL) {
} else if ((f->id == F_LEVSPELL) && !lfhasflag(lf, F_NOSPELLS)) {
addtempflag(lf->flags, F_CANCAST, f->val[1], NA, NA, NULL, FROMJOB);
} else if (f->id == F_LEVSPELLSCHOOL) { // select a spell from school
} else if ((f->id == F_LEVSPELLSCHOOL) && !lfhasflag(lf, F_NOSPELLS)) { // select a spell from school
if (isplayer(lf)) {
int done = B_FALSE;
char qbuf[BUFLEN];
@ -4335,7 +4377,7 @@ void enhanceskills(lifeform_t *lf) {
}
}
}
} else if (f->id == F_LEVSPELLSCHOOLFROMX) { // select from X spells from given school
} else if ((f->id == F_LEVSPELLSCHOOLFROMX) && !lfhasflag(lf, F_NOSPELLS)) { // select from X spells from given school
int nleft,highestlev = -1,n,i;
enum SPELLSCHOOL wantschool;
int possidx[MAXCANDIDATES],nposs;
@ -4473,49 +4515,52 @@ void enhanceskills(lifeform_t *lf) {
}
// allomancy sometimes lets you learn spells
slev = getskill(lf, SK_SS_ALLOMANCY);
if (pctchance(slev*20)) {
char qbuf[BUFLEN];
sprintf(qbuf, "Learn which allomantic ability (maxmp=%d):", getmaxmp(player));
// construct list of castable mental spells
makespellchoicelist(&prompt, lf, qbuf, "Describe which allomantic ability:", SS_ALLOMANCY, B_TRUE, B_FALSE, B_FALSE, player->maxmp);
if (prompt.nchoices > 0) {
objecttype_t *ot;
msg("Your body has attuned itself to a new allomantic ability!"); more();
getchoicestr(&prompt, B_TRUE, B_TRUE);
ot = prompt.result;
if (ot) {
if (prompt.whichq == 0) { // learn the spell
addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL);
} else {
describespell(ot);
if (!lfhasflag(lf, F_NOSPELLS)) {
slev = getskill(lf, SK_SS_ALLOMANCY);
if (pctchance(slev*20)) {
char qbuf[BUFLEN];
sprintf(qbuf, "Learn which allomantic ability (maxmp=%d):", getmaxmp(player));
// construct list of castable mental spells
makespellchoicelist(&prompt, lf, qbuf, "Describe which allomantic ability:", SS_ALLOMANCY, B_TRUE, B_FALSE, B_FALSE, player->maxmp);
if (prompt.nchoices > 0) {
objecttype_t *ot;
msg("Your body has attuned itself to a new allomantic ability!"); more();
getchoicestr(&prompt, B_TRUE, B_TRUE);
ot = prompt.result;
if (ot) {
if (prompt.whichq == 0) { // learn the spell
addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL);
} else {
describespell(ot);
}
}
}
}
}
// psionics sometimes lets you learn spells
slev = getskill(lf, SK_SS_MENTAL);
if (pctchance(slev*20)) {
char qbuf[BUFLEN];
sprintf(qbuf, "Learn which psionic power (maxmp=%d):", getmaxmp(player));
// construct list of castable mental spells
makespellchoicelist(&prompt, lf, qbuf, "Describe which psionic power:", SS_MENTAL, B_TRUE, B_FALSE, B_FALSE, player->maxmp);
if (prompt.nchoices > 0) {
objecttype_t *ot;
msg("Your brain has unlocked a new psionic power!"); more();
getchoicestr(&prompt, B_TRUE, B_TRUE);
ot = prompt.result;
if (ot) {
if (prompt.whichq == 0) { // learn the spell
addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL);
} else {
describespell(ot);
}
// psionics sometimes lets you learn spells
slev = getskill(lf, SK_SS_MENTAL);
if (pctchance(slev*20)) {
char qbuf[BUFLEN];
sprintf(qbuf, "Learn which psionic power (maxmp=%d):", getmaxmp(player));
// construct list of castable mental spells
makespellchoicelist(&prompt, lf, qbuf, "Describe which psionic power:", SS_MENTAL, B_TRUE, B_FALSE, B_FALSE, player->maxmp);
if (prompt.nchoices > 0) {
objecttype_t *ot;
msg("Your brain has unlocked a new psionic power!"); more();
getchoicestr(&prompt, B_TRUE, B_TRUE);
ot = prompt.result;
if (ot) {
if (prompt.whichq == 0) { // learn the spell
addflag(lf->flags, F_CANCAST, ot->id, NA, NA, NULL);
} else {
describespell(ot);
}
}
}
}
}
}
} // end if !hasflag nospells
killflagsofid(lf->flags, F_HASNEWLEVEL);
// ready for another level?
if (lf->xp >= getxpforlev(lf->level + 1)) {
@ -4946,7 +4991,6 @@ int flee(lifeform_t *lf) {
flag_t *retflag[MAXCANDIDATES];
int nretflags;
if (lfhasflag(lf, F_DEBUG)) db = B_TRUE;
real_getlfname(lf, lfname, B_FALSE, B_FALSE);
@ -5070,6 +5114,8 @@ int flee(lifeform_t *lf) {
void fleefrom(lifeform_t *lf, lifeform_t *enemy, int howlong, int onpurpose) {
flag_t *f;
if (lf == enemy) return;
if (!onpurpose) {
// in recovery from fleeing?
// this is to prevent constant usage of war cry!
@ -9194,7 +9240,6 @@ void givejob(lifeform_t *lf, enum JOB jobid) {
if ((gamemode == GM_CHARGEN) && isplayer(lf)) {
subjob_t *sub;
enum SUBJOB sj = SJ_NONE;
char ch;
flag_t *retflag[MAXCANDIDATES];
int nretflags;
getflags(j->flags, retflag, &nretflags, F_CANHAVESUBJOB, F_NONE);
@ -9207,7 +9252,7 @@ void givejob(lifeform_t *lf, enum JOB jobid) {
addchoice(&prompt, '-', "(none)", NULL, NULL, NULL);
if (prompt.nchoices > 1) {
ch = getchoice(&prompt);
getchoicestr(&prompt, B_FALSE, B_TRUE);
sub = (subjob_t *)prompt.result;
if (sub) {
sj = sub->id;
@ -9260,8 +9305,8 @@ void givejob(lifeform_t *lf, enum JOB jobid) {
}
void givesubjob(lifeform_t *lf, enum SUBJOB sj) {
flag_t *jobflag;
object_t *sb1,*o;
flag_t *jobflag,*f;
object_t *sb1 = NULL,*o;
int i;
if (sj == SJ_NONE) return;
@ -9357,14 +9402,27 @@ void givesubjob(lifeform_t *lf, enum SUBJOB sj) {
break;
case SJ_SCOURGE:
addtempflag(lf->flags, F_RESISTMAG, 5, NA, NA, NULL, FROMJOB);
// no mp.
// no mp other other magic.
killflagsofid(lf->flags, F_MPDICE);
killflagsofid(lf->flags, F_CANCAST);
f = lfhasflagval(lf, F_HASSKILL, SK_SS_ALLOMANCY, NA, NA, NULL);
if (f) killflag(f);
//
addtempflag(lf->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL, FROMJOB);
addtempflag(lf->flags, F_LEVABIL, 2, OT_S_NULLIFY, NA, "pw:1;", FROMJOB);
addtempflag(lf->flags, F_LEVABIL, 4, OT_S_NULLIFY, NA, "pw:2;", FROMJOB);
addtempflag(lf->flags, F_LEVABIL, 6, OT_S_NULLIFY, NA, "pw:3;", FROMJOB);
addtempflag(lf->flags, F_LEVABIL, 8, OT_S_NULLIFY, NA, "pw:4;", FROMJOB);
addtempflag(lf->flags, F_LEVABIL, 10, OT_S_NULLIFY, NA, "pw:5;", FROMJOB);
addtempflag(lf->flags, F_LEVABIL, 12, OT_S_NULLIFY, NA, "pw:6;", FROMJOB);
addtempflag(lf->flags, F_LEVABIL, 14, OT_S_NULLIFY, NA, "pw:7;", FROMJOB);
addtempflag(lf->flags, F_LEVABIL, 16, OT_S_NULLIFY, NA, "pw:8;", FROMJOB);
addtempflag(lf->flags, F_LEVABIL, 18, OT_S_NULLIFY, NA, "pw:9;", FROMJOB);
addtempflag(lf->flags, F_LEVABIL, 20, OT_S_NULLIFY, NA, "pw:10;", FROMJOB);
break;
//
default:
sb1 = NULL;
break;
}
@ -9900,6 +9958,7 @@ void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp) {
// handle autoweapon
if (lf && hasflag(fp, F_SELECTWEAPON)) {
skill_t *sk;
flag_t *f2;
objecttype_t *poss[MAXSKILLS];
int nposs = 0, i;
// find all the weapon skills this lf can learn
@ -9926,7 +9985,18 @@ void givestartobs(lifeform_t *lf, object_t *targob, flagpile_t *fp) {
initprompt(&prompt, buf);
for (i = 0; i < nposs; i++) {
addchoice(&prompt, ch++, poss[i]->name, NULL, poss[i], NULL);
char thisdesc[BUFLEN];
int dam,acc;
enum DAMTYPE dt;
f2 = hasflag(poss[i]->flags, F_ACCURACY);
acc = f2->val[0];
f2 = hasflag(poss[i]->flags, F_DAM);
dt = f2->val[0];
dam = f2->val[1];
sprintf(thisdesc, "%s (Damage: %d %s, Accuracy: %s)", poss[i]->name,
dam, getdamname(dt), getaccuracyname(acc));
addchoice(&prompt, ch++, thisdesc, NULL, poss[i], NULL);
}
if (prompt.nchoices == 1) {
@ -14574,6 +14644,13 @@ int noise(cell_t *c, lifeform_t *noisemaker, enum NOISECLASS nclass, int volume,
addflag(l->flags, F_DONELISTEN, B_TRUE, NA, NA, NULL);
practice(l, SK_LISTEN, 1);
}
// migraine?
if ((volume > 1) && lfhasflagval(l, F_POISONED, P_MIGRAINE, NA, NA, NULL)) {
losehp(l, (volume-1), DT_SONIC, NULL, "a migraine");
if (isplayer(l)) {
msg("Your head explodes in pain at the sound!");
}
}
}
} // end if isplayer and not asleep
@ -14932,6 +15009,10 @@ void poison(lifeform_t *lf, int howlong, enum POISONTYPE ptype, int power, char
case P_VENOM:
default:
break;
case P_MIGRAINE:
f = addtempflag(lf->flags, F_DTVULN, DT_SONIC, NA, NA, NULL, FROMPOISON); f->obfrom = ptype;
f = addtempflag(lf->flags, F_DTVULN, DT_LIGHT, NA, NA, NULL, FROMPOISON); f->obfrom = ptype;
break;
case P_WEAKNESS:
f = addtempflag(lf->flags, F_ATTRMOD, A_STR, -(power*10), NA, NULL, FROMPOISON);
f->obfrom = ptype; // poison type
@ -17508,6 +17589,8 @@ void startlfturn(lifeform_t *lf) {
gainmp(lf, f->val[0]);
}
}
// druid gains mp from plants
if (hasjob(lf, J_DRUID)) {
int chance = 0;
@ -17945,22 +18028,22 @@ void startlfturn(lifeform_t *lf) {
fall_from_air(lf);
}
f = hasflag(lf->flags, F_POISONED);
if (f) {
getflags(lf->flags, retflag, &nretflags, F_POISONED, F_NONE);
for (i = 0; i < nretflags; i++) {
poisontype_t *pt;
f = retflag[i];
pt = findpoisontype(f->val[0]);
// chance of fighting it off - gets easier over time.
//
if ((f->lifetime > 0) && skillcheck(lf, SC_POISON, (f->lifetime * 9), 0 )) {
killflag(f);
} else {
flag_t *asleep;
// being asleep helps.
asleep = hasflag(lf->flags, F_ASLEEP);
// chance of losing hp
if (pctchance(pt->dampct)) {
char buf[BUFLEN];
flag_t *asleep;
// being asleep helps.
asleep = hasflag(lf->flags, F_ASLEEP);
if (!asleep && (isplayer(lf) || cansee(player, lf))) {
char *p;
char lfname[BUFLEN],lfnameposs[BUFLEN];
@ -18015,10 +18098,27 @@ void startlfturn(lifeform_t *lf) {
loseconcentration(lf);
}
} else if (f->val[0] == P_MIGRAINE) {
if (!asleep) {
int amt;
amt = lfproduceslight(lf);
if (amt) {
int dam;
// note: amt will be doubled due to light vulnerability,
// so half it here.
dam = amt/2;
limit(&dam, 1, NA);
losehp(lf, amt, DT_LIGHT, NULL, "a migraine");
if (isplayer(lf)) {
msg("Your head explodes in pain at your light!");
}
}
}
}
}
}
f = hasflag(lf->flags, F_NAUSEATED);
if (f) {
// chance of being delayed
@ -18170,12 +18270,22 @@ void startlfturn(lifeform_t *lf) {
if (isdead(lf)) return;
// effects for/on your own flags
getflags(lf->flags, retflag, &nretflags, F_ATTACHEDTO, F_CANWILL, F_CHARMEDBY, F_CLIMBING, F_FEIGNFOOLEDBY,F_FLEEFROM,
getflags(lf->flags, retflag, &nretflags, F_ANTICIPATE, F_ATTACHEDTO, F_CANWILL, F_CHARMEDBY, F_CLIMBING, F_FEIGNFOOLEDBY,F_FLEEFROM,
F_GRABBEDBY, F_GRABBING, F_HIDING, F_BOOSTSPELL, F_FEIGNINGDEATH, F_HPDRAIN, F_INJURY,
F_NOFLEEFROM, F_PETOF, F_SIZETIMER, F_SPOTTED, F_STABBEDBY, F_STRIKETOKO, F_TARGETCELL, F_TARGETLF, F_NONE);
for (i = 0; i < nretflags; i++) {
f = retflag[i];
// remove impossible flags
// remove impossible/expired flags
if (f->id == F_ANTICIPATE) {
if (f->val[1] <= 0) {
killflag(f);
continue;
} else if (!findlf(lf->cell->map, f->val[0])) {
killflag(f);
continue;
}
}
if ((f->id == F_BOOSTSPELL) && (f->val[0] == OT_S_PASSWALL)) {
if (!lfhasflag(lf, F_NONCORPOREAL)) {
killflag(f);
@ -18630,6 +18740,7 @@ int stun(lifeform_t *lf, int nturns) {
}
addtempflag(lf->flags, F_STUNNED, B_TRUE, NA, NA, NULL, nturns);
loseaitargets(lf);
loseconcentration(lf);
return B_FALSE;
}
@ -20681,6 +20792,12 @@ int wear(lifeform_t *lf, object_t *o) {
}
}
// special case: make ring of invis fully known - the HPDRAIN flag
// won't be announced, so since we don't know all the flags we would
// otherwise get "you turn invisible!" but still have the ring known
// as "a blue ring" (or whatever)
if (isplayer(lf) && (o->type->id == OT_RING_INVIS)) makeknown(o->type->id);
// give flags
giveobflags(lf, o, F_EQUIPCONFER);

31
map.c
View File

@ -280,6 +280,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int jobok, int
// has a job?
if (f->id == F_STARTJOB) {
if (rnd(1,100) <= f->val[0]) {
job_t *j;
if (f->val[1] == J_RANDOM) {
job_t *j;
j = getrandomjob(B_TRUE);
@ -288,8 +289,20 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int jobok, int
wantjob = f->val[1];
}
givejob(lf, wantjob);
if (f->val[2] != NA) {
givesubjob(lf, f->val[2]);
j = findjob(wantjob);
// subjob ?
if (j && (f->val[2] != NA)) {
// cope with random
if (f->val[2] == SJ_RANDOM) {
// find a subjob which applies
flag_t *retflag[MAXCANDIDATES];
int nretflags;
if (nretflags) {
givesubjob(lf, retflag[rnd(0,nretflags-1)]->val[0]);
}
} else {
givesubjob(lf, f->val[2]);
}
}
break;
}
@ -1423,7 +1436,6 @@ int doelementspread(cell_t *c) {
int celldone = B_FALSE;
for (oo = retcell[i]->obpile->first ; oo ; oo = oo->next) {
flag_t *f;
if (hasobofmaterial(retcell[i]->obpile, MT_FIRE)) {
// there's already a fire here.
// f_onfire flags won't expire if there is fire there.
@ -1458,6 +1470,14 @@ int doelementspread(cell_t *c) {
}
}
}
// flammable cell floor (and no doors, solid walls, etc)
if (cellwalkable(NULL, retcell[i], NULL) && !hasobofmaterial(retcell[i]->obpile, MT_FIRE)) {
if (hasflag(retcell[i]->type->material->flags, F_FLAMMABLE)) {
addobfast(retcell[i]->obpile, fireob->type->id);
nspread++;
celldone = B_TRUE;
}
}
}
}
return B_FALSE;
@ -3418,6 +3438,7 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex
// special cases
// village - add town walls and clear it out
/*
if (db) dblog(" finalising village creation...");
if (map->habitat->id == H_VILLAGE) {
int x1 = 999,y1 = 999,x2 = -1,y2 = -1,x,y;
@ -3549,6 +3570,7 @@ void createmap(map_t *map, int depth, region_t *region, map_t *parentmap, int ex
}
}
}
*/
//if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging
// try to join up any unlinked staircases in this map.
@ -7515,6 +7537,9 @@ void updateknowncells(void) {
if (isairborne(player) && !lfhasflag(player, F_PHOTOMEM)) {
return;
}
if (lfhasflag(player, F_RAGE)) {
return;
}
for (i = 0; i < player->nlos; i++) {
setcellknown(player->los[i], B_FALSE);
}

20
move.c
View File

@ -817,6 +817,8 @@ int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher, int fallc
int seen;
int mightfall = B_TRUE;
lifeform_t *newlf;
int preventchance = 0;
enum LFSIZE lfsize;
if (lfhasflag(lf, F_GRAVLESSENED)) {
howfar *= 2;
@ -846,6 +848,24 @@ int knockback(lifeform_t *lf, int dir, int howfar, lifeform_t *pusher, int fallc
// if levitating (not flying), knocked back further.
if (lfhasflag(lf, F_LEVITATING)) {
howfar *= 2;
preventchance -= 30;
}
// avoid being moved?
lfsize = getlfsize(lf);
if (lfhasflag(lf, F_GRAVBOOSTED)) {
preventchance += 100;
}
if (lfsize > SZ_HUMAN ){
// 15% chance of avoidance per size > human.
preventchance += ((lfsize - SZ_HUMAN) * 15);
}
if (pctchance(preventchance)) {
msg("%s stagger%s %s but hold%s %s %s.",lfname,isplayer(lf) ? "" : "s",
getreldirname(getrelativedir(lf, dir)),
isplayer(lf) ? "" : "s", isplayer(lf) ? "your" : "its",
isairborne(lf) ? "position" : "ground");
return B_TRUE;
}
breakgrabs(lf, B_TRUE, B_TRUE);

View File

@ -944,6 +944,14 @@ void donextturn(map_t *map) {
}
}
command_t *findcommand(enum COMMAND id) {
command_t *c;
for (c = firstcommand ; c ; c = c->next) {
if (c->id == id) return c;
}
return NULL;
}
warning_t *findwarning(char *text) {
warning_t *w;
for (w = firstwarning ; w ; w = w->next) {

View File

@ -10,6 +10,7 @@ void dbtimeend(char *text);
void dbtimestart(char *text);
void dobresnham(int d, int xinc1, int yinc1, int dinc1, int xinc2, int yinc2, int dinc2, int *xinc, int *yinc, int *dinc);
void donextturn(map_t *map);
command_t *findcommand(enum COMMAND id);
warning_t *findwarning(char *text);
void gethitdicerange(int depth, int *min, int *max, int range, int oodok);
int getoption(enum OPTION id);

181
spell.c
View File

@ -584,6 +584,8 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef
char obname[BUFLEN];
int donesomething = B_TRUE;
int ncooked = 0;
int cooktime = 0;
if (!isplayer(user)) return B_TRUE;
if (isswimming(user) && !lfhasflag(user, F_AQUATIC)) {
@ -610,19 +612,28 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef
}
if (corpse) {
preparecorpse(user, corpse);
if (isimmuneto(corpse->flags, DT_FIRE, B_FALSE)) {
msg("You attempt to cook %s, but it won't heat up.");
cooktime += getactspeed(user);
ncooked++;
} else {
preparecorpse(user, corpse);
if (isresistantto(corpse->flags, DT_FIRE, B_FALSE)) {
// takes longer
cooktime += (getactspeed(user)*2);
} else {
cooktime += getactspeed(user);
}
practice(user, SK_COOKING, 1);
ncooked++;
}
// set donesomething even if the actual cooking failed.
donesomething = B_TRUE;
ncooked++;
corpse = NULL;
}
} // end while donesomething
if (ncooked) {
if (ncooked > 1) {
// takes longer
taketime(user, getactspeed(user) * (ncooked-1));
}
practice(user, SK_COOKING, 1);
return B_FALSE;
}
@ -3850,6 +3861,13 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_
fizzle(caster);
return B_TRUE;
}
} else if (spellid == OT_S_ANTICIPATE) {
if (!target) target = targcell->lf;
if (!target) {
fizzle(caster);
return B_TRUE;
}
addflag(caster->flags, F_ANTICIPATE, target->id, power, NA, NULL);
} else if (spellid == OT_S_APPORTATION) {
int failed = B_FALSE;
float maxweight;
@ -7357,21 +7375,58 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_
}
if (isplayer(target)) {
msg("^wYou are engulfed in an anti-magic field!");
msg("^BYou are engulfed in an anti-magic field!");
if (seenbyplayer) *seenbyplayer = B_TRUE;
} else if (cansee(player, target)) {
char lfname[BUFLEN];
getlfname(target, lfname);
msg("^w%s is engulfed in an anti-magic field!", lfname);
msg("^B%s is engulfed in an anti-magic field!", lfname);
if (seenbyplayer) *seenbyplayer = B_TRUE;
}
// lose all mana
// resist ?
if (spellresisted(target, caster, spellid, power, seenbyplayer, B_FALSE)) {
if (isplayer(target)) {
msg("Luckily, you shrug off its effects.");
} else if (cansee(player, target)) {
char lfname[BUFLEN];
getlfname(target, lfname);
msg("%s seems unaffected.", lfname);
}
}
// lose some mana
if (target->mp > 0) {
losemp(target, target->mp);
int howmuch;
howmuch = pctof(power*10, target->maxmp);
losemp(target, howmuch);
if (isplayer(target)) msg("^wYour mana drains away!");
ndone++;
}
// now stop active spells
while (ndone < power) {
nposs = 0;
getflags(target->flags, retflag, &nretflags, F_BOOSTSPELL, F_NONE);
for (i = 0; i < nretflags; i++) {
int ok = B_TRUE;
// exception: don't stop animals etc from flying
if ((retflag[i]->val[0] == OT_S_FLIGHT) && lfhasflag(target, F_NATURALFLIGHT)) {
ok = B_FALSE;
}
if (ok) {
poss[nposs++] = retflag[i];
}
}
if (nposs) {
stopspell(target, poss[rnd(0,nposs-1)]->val[0]);
ndone++;
} else {
break;
}
}
// now remove the ability to cast them!
while (ndone < power) {
// for player:
// remove memorised spells
@ -7394,24 +7449,29 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_
ok = B_FALSE;
}
}
// for player, only CANCAST SPELLs can be nullified.
if (ok && isplayer(target)) {
objecttype_t *ot;
ot = findot(retflag[i]->val[0]);
if (ot->obclass->id != OC_SPELL) {
ok = B_FALSE;
if (retflag[i]->id == F_CANCAST) {
objecttype_t *ot;
ot = findot(retflag[i]->val[0]);
if (ot->obclass->id != OC_SPELL) {
ok = B_FALSE;
}
}
}
if (ok) {
poss[nposs++] = retflag[i];
}
}
if (!nposs) {
if (nposs) {
f = poss[rnd(0,nposs-1)];
killflag(f);
ndone++;
} else {
break;
}
f = poss[rnd(0,nposs-1)];
killflag(f);
ndone++;
}
if (isplayer(target)) {
if (!ndone) {
@ -7421,11 +7481,12 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_
char lfname[BUFLEN];
getlfname(target, lfname);
if (ndone) {
msg("%s%s powers are nullified!", lfname, getpossessive(lfname));
msg("^%c%s%s powers are nullified!", getlfcol(target, CC_BAD), lfname, getpossessive(lfname));
} else {
msg("%s is unaffected.", lfname);
}
}
if (isplayer(caster)) angergodmaybe(R_GODMAGIC, 10, GA_HERESY);
} else if (spellid == OT_S_OBJECTGROWTH) {
enum OBTYPE newoid = OT_NONE;
enum LFSIZE newsize = SZ_ANY;
@ -7815,7 +7876,32 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_
// now kill the caster!
die(caster);
}
} else if (spellid == OT_S_PSIBLAST) {
int dam,iq;
enum ATTRBRACKET iqb;
char targetname[BUFLEN];
if (!target) {
target = targcell->lf;
}
if (!target) {
fizzle(caster);
return B_TRUE;
}
iq = getattr(target, A_IQ);
iqb = getattrbracket(iq, A_IQ, NULL);
// not smart enough
if (iqb <= AT_EXLOW) {
fizzle(caster);
return B_TRUE;
}
dam = rnd(1,(iq/5));
losehp(target, dam, DT_DIRECT, caster, "a psionic blast");
getlfname(target, targetname);
if (isplayer(target)) {
msg("Your brain is blasted!");
} else if (cansee(player, target)) {
msg("%s%s brain is blasted!",targetname, getpossessive(targetname));
}
} else if (spellid == OT_S_PSYARMOUR) {
flag_t *f;
// always targetted at caster
@ -12260,22 +12346,25 @@ void pullobto(object_t *o, lifeform_t *lf) {
}
void stopallspells(lifeform_t *lf) {
int stopallspells(lifeform_t *lf) {
flag_t *f,*nextf;
int nstopped = 0;
for (f = lf->flags->first ; f ; f = nextf) {
nextf = f->next;
if (f->id == F_BOOSTSPELL) {
stopspell(lf, f->val[0]);
if (!stopspell(lf, f->val[0])) nstopped++;
}
}
return nstopped;
}
void stopallspellsexcept(lifeform_t *lf, ...) {
int stopallspellsexcept(lifeform_t *lf, ...) {
flag_t *f,*nextf;
va_list args;
enum OBTYPE exception[MAXCANDIDATES];
int nexceptions = 0;
int nstopped = 0;
va_start(args, lf);
exception[nexceptions] = va_arg(args, enum OBTYPE);
@ -12297,10 +12386,11 @@ void stopallspellsexcept(lifeform_t *lf, ...) {
}
}
if (stopthis) {
stopspell(lf, f->val[0]);
if (!stopspell(lf, f->val[0])) nstopped++;
}
}
}
return nstopped;
}
int schoolappearsinbooks(enum SPELLSCHOOL ss) {
@ -12385,6 +12475,10 @@ int spellresisted(lifeform_t *target, lifeform_t *caster, int spellid, int power
if ((spellid == OT_S_SLEEP) && lfhasflag(target, F_RAGE)) {
bonus += 10;
}
if (hassubjob(caster, SJ_SCOURGE) && (spellid == OT_S_NULLIFY)) {
// cancel out the difficulty from NULLIFY being a level 4 spell
bonus += 8;
}
if (skillcheck(target, SC_RESISTMAG, getmrdiff(spellid,power), bonus)) {
if (isplayer(target) || haslos(player, target->cell)) {
if (announce) {
@ -12397,13 +12491,14 @@ int spellresisted(lifeform_t *target, lifeform_t *caster, int spellid, int power
return B_FALSE;
}
void stopspell(lifeform_t *caster, enum OBTYPE spellid) {
// returns true on error
int stopspell(lifeform_t *caster, enum OBTYPE spellid) {
flag_t *f,*nextf;
object_t *o, *nexto;
objecttype_t *sp;
sp = findot(spellid);
if (!sp) return;
if (!sp) return B_TRUE;
for (f = caster->flags->first ; f ; f = nextf) {
nextf = f->next;
@ -12449,6 +12544,7 @@ void stopspell(lifeform_t *caster, enum OBTYPE spellid) {
}
}
}
return B_FALSE;
}
@ -12718,37 +12814,6 @@ cell_t *validatespellcell(lifeform_t *caster, cell_t **targcell, int targtype, e
}
}
// eye protection will stop some spells!
if (*targcell && (*targcell)->lf) {
flag_t *casttype;
lifeform_t *victim;
victim = (*targcell)->lf;
casttype = lfhasflag(caster, F_CASTTYPE);
if (casttype) {
object_t *glasses = NULL;
switch (casttype->val[0]) {
case CT_EYESPIT:
case CT_GAZE:
glasses = eyesshaded(victim);
if (glasses) {
if (isplayer(victim)) {
char gbuf[BUFLEN];
getobname(glasses, gbuf, glasses->amt);
msg("Your %s protects you.", noprefix(gbuf));
} else if (cansee(player, victim)) {
char lfname[BUFLEN],gbuf[BUFLEN];
getobname(glasses, gbuf, glasses->amt);
getlfname(caster, lfname);
msg("%s%s %s protects it.", lfname, getpossessive(lfname), noprefix(gbuf) );
}
*targcell = NULL;
}
break;
default:
break;
}
}
}
return *targcell;
}

View File

@ -38,9 +38,9 @@ int schoolappearsinbooks(enum SPELLSCHOOL ss);
void spellcloud(cell_t *srcloc, int radius, int ch, enum COLOUR col, enum OBTYPE sid, int power, int frompot, char *seetext, char *noseetext);
int spellisfromschool(int spellid, enum SPELLSCHOOL school);
int spellresisted(lifeform_t *target, lifeform_t *caster, int spellid, int power, int *seenbyplayer, int announce);
void stopspell(lifeform_t *caster, enum OBTYPE spellid);
void stopallspells(lifeform_t *lf);
void stopallspellsexcept(lifeform_t *lf, ...);
int stopspell(lifeform_t *caster, enum OBTYPE spellid);
int stopallspells(lifeform_t *lf);
int stopallspellsexcept(lifeform_t *lf, ...);
int summonlfs(lifeform_t *caster, cell_t *where, enum RACE wantrace, enum RACECLASS wantrc, enum LFSIZE wantsize, enum ALIGNMENT wantalign, int howmany, int lifetime, int friendly);
lifeform_t *validateabillf(lifeform_t *user, enum OBTYPE aid, lifeform_t **target);
cell_t *validatespellcell(lifeform_t *caster, cell_t **targcell, int targtype, enum OBTYPE spellid, int power, int frompot);

4
text.c
View File

@ -835,8 +835,8 @@ char *getdamnamenoun(enum DAMTYPE damtype) {
case DT_FIRE: return "fire";
case DT_HEAT: return "heat";
case DT_BITE: return "bite";
case DT_BASH: return "bludgeoning";
case DT_CHOP: return "chopping";
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";