diff --git a/attack.c b/attack.c index b2972cc..efeb954 100644 --- a/attack.c +++ b/attack.c @@ -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; diff --git a/data.c b/data.c index bd489e3..a5c2087 100644 --- a/data.c +++ b/data.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -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); diff --git a/data/hiscores.db b/data/hiscores.db index c4dec0a..7967d60 100644 Binary files a/data/hiscores.db and b/data/hiscores.db differ diff --git a/defs.h b/defs.h index 64e7a75..8f2f352 100644 --- a/defs.h +++ b/defs.h @@ -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, diff --git a/doc/glyphs.txt b/doc/glyphs.txt index 4329a87..24c3ad3 100644 --- a/doc/glyphs.txt +++ b/doc/glyphs.txt @@ -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 diff --git a/io.c b/io.c index 5e60d27..d955629 100644 --- a/io.c +++ b/io.c @@ -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'; @@ -8920,7 +8987,7 @@ void dumpweps(void) { } } - + void forceredraw(void) { needredraw = B_TRUE; statdirty = B_TRUE; @@ -9694,28 +9761,29 @@ void handleinput(void) { if (hasactivespell(player, OT_S_SLIDE)) stopspell(player, OT_S_SLIDE); 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 { diff --git a/io.h b/io.h index 345d318..86a4e9f 100644 --- a/io.h +++ b/io.h @@ -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); diff --git a/lf.c b/lf.c index b79b9a5..a6e61c0 100644 --- a/lf.c +++ b/lf.c @@ -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); diff --git a/map.c b/map.c index 7995f9c..1ccf472 100644 --- a/map.c +++ b/map.c @@ -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); } diff --git a/move.c b/move.c index 09a1b53..98dfb09 100644 --- a/move.c +++ b/move.c @@ -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); diff --git a/nexus.c b/nexus.c index fcb26a2..27c7cda 100644 --- a/nexus.c +++ b/nexus.c @@ -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) { diff --git a/nexus.h b/nexus.h index 45bcdd1..19e79a0 100644 --- a/nexus.h +++ b/nexus.h @@ -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); diff --git a/spell.c b/spell.c index cb9b3ce..2ffdc26 100644 --- a/spell.c +++ b/spell.c @@ -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; } diff --git a/spell.h b/spell.h index 4fa2b66..e6c853b 100644 --- a/spell.h +++ b/spell.h @@ -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); diff --git a/text.c b/text.c index 6fd14ba..1fa47c1 100644 --- a/text.c +++ b/text.c @@ -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";