From 4808d2c9df71b1935542b2aa941daa98a0e3e2ae Mon Sep 17 00:00:00 2001 From: Rob Pearce Date: Fri, 24 Feb 2012 06:45:23 +0000 Subject: [PATCH] - [+] allow user to DISABLE auto dodge fatal attacks ?. enable through "reflexive dodging" ability - [+] first rotation in a turn takes no time - [+] exploit: if you walk diagonally next to a monster, it moves to your previous space! - [+] fix: aigetlastknownpos should return the cell that trails point to, not the cell _with_ the trails. - [+] only recognise weapon quality (masterwork etc) if you are skilled in that weapon or perception - [+] cope with mosnter necromancers, etc - [+] parserace should look for necromancer etc. - [+] if so, set "wantsubjob" to SJ_NECROMANCER etc - [+] move specialty mage code into "givejobspecialty" - [+] Make wizard's job name depend on primary spell school. - [+] "Demonologist", "Firemage", "Icemage", "Necromancer", "Skymage", "Wizard" (wild) - [+] instead of getjob() then j->name, use "getjobname(lf)" - [+] f_jobname - [+] i should never "hear voices chanting" from an abandoned temple - [+] for monsters, show "its bite inflicts poison" in io.c racial knowledge - [+] casting healing on myself pleased Glorana twice! - [+] one from casting a life spell - [+] one from casting healing. - [+] move ones from spell.c to castspell() - [+] new flag: f_pleasesgod v0=godid, v1=howmuch - [+] add to other spell objects: - [+] f_pleasesgod - [+] GODFIRE - spelllevel * 2 - [+] GODDEATH = spelllevel - [+] GODLIFE = spelllevel*2 - [+] GODNATURE: spelllevle*2 - [+] show this in describespell - [+] for god healing effects, use gainhp() so that it says "Your HP is fully restored." - [+] change F_RESISTMAG to be percentage rather than 1-20 - [+] make some gods' pleasure boost power of related spells - [+] 1 per each positive levle of getpietylev() - [+] glorana: life - [+] hecta: death - [+] only apply auto shortcuts for players. - [+] subjob_t - [+] addsubjob() - [+] killsubjob() - [+] show subjobs in job descriptions. - [+] data.c: addsubjob(.... - [+] in job defs: - [+] f_canhavesubjob sj_xxx - [+] use this to determine whether to ask about them - [+] redo getjobname - [+] remove f_job->Text - [+] speak with dead should only work on corpses of races which can talk. - [+] warrior subclasses - [+] "Scourge" - [+] gains magic resistance as you level up - [+] == 5 + (level * 3) % - [+] nospells - [+] no mana - [+] paladin - [+] blessed gear - [+] can will turn undead - [+] healing magic - [+] god = glorana. - [+] cannot use spells if glorana is angry. - [+] battlemage - [+] starts with one spell school (fire/ice/air) - [+] canlearn fire/ice/air spell schools - [+] limited to adept in all schools - [+] slightly less hp - [+] no warrior abilities --- ai.c | 88 ++++++---- data.c | 76 ++++++++- data/hiscores.db | Bin 13312 -> 13312 bytes defs.h | 38 ++++- god.c | 2 +- io.c | 200 ++++++++++++++++++---- io.h | 1 + lf.c | 435 +++++++++++++++++++++++++++++++++++++++++++---- lf.h | 10 ++ map.c | 47 ++++- map.h | 2 +- move.c | 8 +- nexus.c | 102 +---------- objects.c | 34 ++-- save.c | 4 +- spell.c | 243 ++++++++++++++++---------- spell.h | 2 + text.c | 44 ++--- vault.c | 2 +- 19 files changed, 997 insertions(+), 341 deletions(-) diff --git a/ai.c b/ai.c index 41ecdb6..5d7bc45 100644 --- a/ai.c +++ b/ai.c @@ -266,8 +266,9 @@ int aigetchasetime(lifeform_t *lf) { // this function assumes that you can't just SEE the target! cell_t *aigetlastknownpos(lifeform_t *lf, lifeform_t *target, int *lastx, int *lasty, int *lastdir) { flag_t *f, *tflag, *bestflag = NULL; - cell_t *c = NULL; + cell_t *c = NULL,*trailcell = NULL,*finalcell = NULL; int besttime = -1; + int locallastdir = D_NONE; int i; // defaults @@ -275,50 +276,67 @@ cell_t *aigetlastknownpos(lifeform_t *lf, lifeform_t *target, int *lastx, int *l if (lasty) *lasty = NA; if (lastdir) *lastdir = D_NONE; - // check scent/footprints first - for (i = 0; i < lf->nlos; i++) { - if (hastrailof(lf->los[i]->obpile, target, NA, &tflag, lf)) { - if (tflag->lifetime > besttime) { - besttime = tflag->lifetime; - bestflag = tflag; - c = lf->los[i]; - } - } - } - if (bestflag && c) { - if (lastx) *lastx = c->x; - if (lasty) *lasty = c->y; - if (lastdir) { - // can only obtain direction from footprints if your - // tracking skill is high enough. - if (bestflag->val[2] == S_SIGHT) { - if (getskill(lf, SK_PERCEPTION) >= PR_SKILLED) { - *lastdir = bestflag->val[1]; - } else { - *lastdir = D_NONE; - } - } else { - *lastdir = bestflag->val[1]; - } - } - return c; - } - - + // do we remember the player's last known location ? f = ispetortarget(lf, target); if (f) { c = getcellat(lf->cell->map, f->val[1], f->val[2]); if (c) { if (lastx) *lastx = c->x; if (lasty) *lasty = c->y; - if (lastdir && strlen(f->text)) { - *lastdir = atoi(f->text); + if (strlen(f->text)) { + locallastdir = atoi(f->text); } } - return c; + trailcell = c; } - return NULL; + // if not, check for the most recent scent/footprints first + if (!trailcell) { + c = NULL; + for (i = 0; i < lf->nlos; i++) { + if (hastrailof(lf->los[i]->obpile, target, NA, &tflag, lf)) { + if (tflag->lifetime > besttime) { + besttime = tflag->lifetime; + bestflag = tflag; + c = lf->los[i]; + } + } + } + if (bestflag && c) { + if (lastx) *lastx = c->x; + if (lasty) *lasty = c->y; + // can only obtain direction from footprints if your + // tracking skill is high enough. + if (bestflag->val[2] == S_SIGHT) { + if (getskill(lf, SK_PERCEPTION) >= PR_SKILLED) { + locallastdir = bestflag->val[1]; + } else { + locallastdir = D_NONE; + } + } else { + locallastdir = bestflag->val[1]; + } + trailcell = c; + } + } + + // if we found somewhere, follow any trails out of there + if (trailcell) { + if (locallastdir != D_NONE) { + // follow the trail + finalcell = getcellindir(trailcell, locallastdir); + locallastdir = D_NONE; + } else { + finalcell = trailcell; + } + } + + // return last movement direction + if (lastdir) { + *lastdir = locallastdir; + } + + return finalcell; } object_t *aigetrangedattack(lifeform_t *lf, lifeform_t *target, enum RANGEATTACK *ra, int *range) { diff --git a/data.c b/data.c index 0c55584..38c0ea9 100644 --- a/data.c +++ b/data.c @@ -16,6 +16,7 @@ extern race_t *firstrace, *lastrace; extern raceclass_t *firstraceclass, *lastraceclass; extern job_t *firstjob, *lastjob; extern poisontype_t *firstpoisontype,*lastpoisontype; +extern subjob_t *firstsubjob,*lastsubjob; extern skill_t *firstskill, *lastskill; extern objecttype_t *objecttype; extern objectclass_t *objectclass,*lastobjectclass; @@ -153,6 +154,17 @@ void initcommands(void) { void initjobs(void) { int i; flag_t *f; + + // subjob definitions - keep these in alphabetical order + addsubjob(SJ_BATTLEMAGE, "Battlemage", "Unlike other warriors Battlemages are skilled in magic, but at the expense of the regular warrior abilities.", 'b'); + addsubjob(SJ_FIREMAGE, "Firemage", "", 'f'); + addsubjob(SJ_ICEMAGE, "Icemage", "", 'i'); + addsubjob(SJ_NECROMANCER, "Necromancer", "", 'n'); + addsubjob(SJ_PALADIN, "Paladin", "Paladins are holy warriors dedicated to the Goddess of Life. They have access to powerful healing magic, but this power is dependant upon their goddess' approval.", 'p'); + addsubjob(SJ_SCOURGE, "Scourge", "Scourges have dedicated their life to ridding the world of magic. Strict training has granted them an innate immunity to magic, but this immunity also extends to beneficial effects.", 's'); + addsubjob(SJ_AIRMAGE, "Skymage", "", 's'); + addsubjob(SJ_WILDMAGE, "Wizard", "", 'w'); + // job definitions // NOTE: try to always make the job's primary weapon be the first object defined. @@ -807,6 +819,10 @@ void initjobs(void) { addflag(lastjob->flags, F_LEVABIL, 8, OT_A_AIMEDSTRIKE, NA, NULL); addflag(lastjob->flags, F_LEVABIL, 10, OT_A_HURRICANESTRIKE, NA, NULL); addflag(lastjob->flags, F_HIRABLE, B_TRUE, NA, NA, NULL); + // sub jobs + addflag(lastjob->flags, F_CANHAVESUBJOB, SJ_BATTLEMAGE, NA, NA, NULL); + addflag(lastjob->flags, F_CANHAVESUBJOB, SJ_PALADIN, NA, NA, NULL); + addflag(lastjob->flags, F_CANHAVESUBJOB, SJ_SCOURGE, NA, NA, NULL); addjob(J_WIZARD, "Wizard", "Specialising in Arcane lore, high level wizards use their magic to create awe-inspiring effects on the world around them, raining death upon their enemies with eldritch wizardries. Not much is known about low level wizards, as they tend to get mauled to death by newts due to their lack of combat skills."); // stats @@ -871,6 +887,12 @@ void initjobs(void) { f = addflag(lastjob->flags, F_CANCAST, OT_S_HASTE, NA, NA, NULL); addcondition(f, FC_IFMONSTER, 20); f = addflag(lastjob->flags, F_CANCAST, OT_S_HEALING, NA, NA, NULL); addcondition(f, FC_IFMONSTER, 20); addflag(lastjob->flags, F_CASTCHANCE, 30, NA, NA, NULL); + // sub jobs + addflag(lastjob->flags, F_CANHAVESUBJOB, SJ_AIRMAGE, NA, NA, NULL); + addflag(lastjob->flags, F_CANHAVESUBJOB, SJ_FIREMAGE, NA, NA, NULL); + addflag(lastjob->flags, F_CANHAVESUBJOB, SJ_ICEMAGE, NA, NA, NULL); + addflag(lastjob->flags, F_CANHAVESUBJOB, SJ_NECROMANCER, NA, NA, NULL); + addflag(lastjob->flags, F_CANHAVESUBJOB, SJ_WILDMAGE, NA, NA, NULL); // non-player jobs addjob(J_GUARD, "Guard", "Guards are paid mercenaries employed to protect a certain area. Accordingly, they are generally outfitetd with high quality armour."); @@ -898,6 +920,7 @@ void initobjects(void) { flag_t *f; int i,n; + // init poison types 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); @@ -2686,6 +2709,7 @@ void initobjects(void) { 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); + addflag(lastot->flags, F_PLEASESGOD, R_GODDEATH, 1, NA, NULL); // l2 addot(OT_S_BLINDNESS, "blindness", "Temporarily blinds the target.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability and duration."); @@ -2712,6 +2736,7 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODDEATH, 2, NA, NULL); addot(OT_S_SMITEGOOD, "smite good", "Instantly deals 1-^bpower*2^n damage to good creatures.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 6, NA, NA, NULL); @@ -2719,6 +2744,7 @@ void initobjects(void) { 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); + addflag(lastot->flags, F_PLEASESGOD, R_GODDEATH, 2, NA, NULL); // l3 addot(OT_S_POISONBOLT, "venom bolt", "Fires a glob of venom at the target.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how difficult the venom is to dodge, and how powerful the poison is."); @@ -2728,6 +2754,7 @@ void initobjects(void) { 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_FALSE, LOF_NEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODDEATH, 3, NA, NULL); addot(OT_S_DRAINLIFE, "necrobeam", "Draws life force from the victim in order to heal the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The amount of life force drained is ^b1d6+power^n."); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); @@ -2735,6 +2762,7 @@ void initobjects(void) { 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_FALSE, LOF_NEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODDEATH, 3, NA, NULL); addot(OT_S_FLAYFLESH, "flay flesh", "Causes invisible tendrils to strike your foe, causing an automatic slash critical on fleshy creatures.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); @@ -2743,6 +2771,7 @@ void initobjects(void) { addflag(lastot->flags, F_RANGE, 3, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_WALLSTOP, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODDEATH, 3, NA, NULL); addot(OT_S_PAIN, "pain", "Causes extreme pain in the target whenever they move.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines the duration of the pain effect."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); @@ -2751,6 +2780,7 @@ void initobjects(void) { 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); + addflag(lastot->flags, F_PLEASESGOD, R_GODDEATH, 3, NA, NULL); // l4 addot(OT_S_CURSE, "curse", "Bestow a curse upon the target's equipment, applying a -1 penalty and making them impossible to remove.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines how many objects will be cursed."); @@ -2774,6 +2804,7 @@ void initobjects(void) { 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); + addflag(lastot->flags, F_PLEASESGOD, R_GODDEATH, 4, NA, NULL); addot(OT_S_PARALYZE, "paralyze", "Disables the target's muscles, leaving them unable to move for ^bpower*2^n turns.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); @@ -2781,6 +2812,7 @@ void initobjects(void) { 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); + addflag(lastot->flags, F_PLEASESGOD, R_GODDEATH, 4, NA, NULL); addot(OT_S_FEEBLEMIND, "brain freeze", "Temporarily lowers the target's intelligence to that of an animal.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines duration."); addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); @@ -2790,12 +2822,14 @@ void initobjects(void) { 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); + addflag(lastot->flags, F_PLEASESGOD, R_GODDEATH, 4, NA, NULL); addot(OT_S_ANIMATEDEAD, "animate dead", "Imbues nearby corpses with life, creating an undead zombie.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines the chances of the zombies being friendly."); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); // TODO: should be "castnearob ot_corpse" addflag(lastot->flags, F_AICASTTOATTACK, ST_ANYWHERE, NA, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODDEATH, 4, NA, NULL); // l5 addot(OT_S_HECTASSERVANT, "hecta's hand", "Summons an enormous skeletal hand to drag foes to their doom. BEWARE: the hand will attack anything living, including the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); @@ -2808,12 +2842,14 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODDEATH, 6, NA, NULL); addot(OT_S_INFINITEDEATH, "infinite death", "Annihilates all nearby life, including the caster!", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines resistability."); addflag(lastot->flags, F_SPELLSCHOOL, SS_DEATH, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); addflag(lastot->flags, F_CASTINGTIME, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ANYWHERE, NA, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODDEATH, 6, NA, NULL); /////////////////// // divination /////////////////// @@ -2999,6 +3035,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLLEVEL, 1, 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_PLEASESGOD, R_GODFIRE, 1, NA, NULL); addot(OT_S_SPARK, "flambe", "Creates very hot but short lived burst of flame around the target, dealing 2d2 fire damage to creatures and objects.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); @@ -3007,11 +3044,13 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODFIRE, 1, NA, NULL); addot(OT_S_PYROMANIA, "pyromania", "Increases the potency of all fire within the caster's line of sight.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SPECIAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODFIRE, 1, NA, NULL); // l2 addot(OT_S_BLADEBURN, "bladeburn", "Ignites the caster's weapon, causing it to temporarily deal fire damage. The spell's power determines how long it will last.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's power determines its duration."); @@ -3020,6 +3059,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODFIRE, 2, NA, NULL); addot(OT_S_FIREDART, "flame dart", "Fires a medium-sized dart of fire, dealing 1d6+^bpower^n fire damage.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); @@ -3027,6 +3067,7 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_NEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODFIRE, 2, NA, NULL); addot(OT_S_IMMOLATE, "immolate", "If the caster can successfully touch the target, they are instantly engulfed in flames.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); @@ -3035,18 +3076,21 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJVICTIM, NA, NA, NULL); addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_NEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODFIRE, 2, NA, NULL); addot(OT_S_SUPERHEAT, "superheat", "Excites the liquid molecules in a single potion, causing its contents to explode upon impact (8d2 damage).", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SPECIAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_NEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODFIRE, 2, NA, NULL); // l3 addot(OT_S_FLAMEBURST, "flame burst", "Creates a radial blast of fire out from the caster, inflicting 2d6 fire damage to all within.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The size of the radial blast is based on the spell's power."); addflag(lastot->flags, F_SPELLSCHOOL, SS_FIRE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJSELF, NA, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODFIRE, 3, NA, NULL); addot(OT_S_FIREBALL, "fireball", "Creates a huge ball of fire, dealing up to ^bpower^nd3 damage.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The damage is lower for enemies further away from the ball's centre."); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At power level 5, the size of the fireball is slightly increased."); @@ -3056,6 +3100,7 @@ void initobjects(void) { addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); // TODO: should be "near victim" addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_NEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODFIRE, 3, NA, NULL); // l4 addot(OT_S_FLAMEPILLAR, "flame pillar", "Creates a tall pillar of flame.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "Spell power determines the spell's range, and the fire's size."); @@ -3066,6 +3111,7 @@ void initobjects(void) { 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_WALLSTOP, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODFIRE, 4, NA, NULL); // l5 addot(OT_S_BURNINGWAVE, "burning wave", "Fire bursts from the ground in a line towards the target.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's range is based on its power."); @@ -3076,6 +3122,7 @@ void initobjects(void) { addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_RANGE, 3, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_WALLSTOP, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODFIRE, 5, NA, NULL); // l6 addot(OT_S_METEOR, "meteor", "Launches a white-hot meteorite towards the target location, dealing up to ^bpower^nd6+30 damage.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The damage is lower for enemies further away from the ball's centre."); @@ -3084,6 +3131,7 @@ void initobjects(void) { addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); // TODO: should be "near victim" addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_NEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODFIRE, 6, NA, NULL); /////////////////// // elemental - cold /////////////////// @@ -3190,6 +3238,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLLEVEL, 4, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_VICTIM, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODNATURE, 5, NA, NULL); addot(OT_S_SHARDSHOT, "shard shot", "Fires a scattered burst of small, fast moving ice shards. The shot will pass through multiple creatures, but damage is reduced with range.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "The spell's range is determined by its power."); addflag(lastot->flags, F_SPELLSCHOOL, SS_COLD, NA, NA, NULL); @@ -3219,6 +3268,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_MENTAL, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); + // god pleasing for this spell is in spell.c addot(OT_S_DETECTPOISON, "detect poison", "Detects any poisoned object in sight of the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); @@ -3232,6 +3282,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_ENCHANTMENT, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_MAXPOWER, 3, NA, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODNATURE, 1, NA, NULL); addot(OT_S_STICKTOSNAKE, "sticks to snakes", "Transforms all rod-shaped objects in sight into allied snakes.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "This spell does not affect the caster's weapon."); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); @@ -3259,6 +3310,8 @@ void initobjects(void) { addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_TARGETTEDSPELL, TT_ALLY, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODLIFE, 3, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODNATURE, 2, NA, NULL); addot(OT_S_REPELINSECTS, "repel insects", "Surrounds the caster with an insect-repelling smell.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); @@ -3427,6 +3480,7 @@ void initobjects(void) { addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); addflag(lastot->flags, F_CASTINGTIME, 2, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_VICTIM, NA, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODNATURE, 5, NA, NULL); /////////////////// // gravity /////////////////// @@ -3511,9 +3565,11 @@ void initobjects(void) { addflag(lastot->flags, F_TARGETTEDSPELL, TT_ALLY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_SELF, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODLIFE, 1, NA, NULL); addot(OT_S_TURNUNDEAD, "turn undead", "Instills fear in undead creatures.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODLIFE, 1, NA, NULL); // l2 addot(OT_S_SPEAKDEAD, "speak with dead", "Temporarily allow a corpse to answer questions about its former life.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL); @@ -3527,6 +3583,7 @@ void initobjects(void) { 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); + addflag(lastot->flags, F_PLEASESGOD, R_GODLIFE, 2, NA, NULL); // l3 addot(OT_S_HEALING, "healing", "Restores 10-20 health to the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "This spell heals an extra 2 damage per power level."); @@ -3536,6 +3593,7 @@ void initobjects(void) { addflag(lastot->flags, F_TARGETTEDSPELL, TT_ALLY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_SELF, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODLIFE, 3, NA, NULL); addot(OT_S_HOLYAURA, "holy aura", "Surrounds the target with a holy aura, causing their weapon to deal holy damage.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL); addflag(lastot->flags, F_ONGOING, B_TRUE, NA, NA, NULL); @@ -3543,6 +3601,7 @@ void initobjects(void) { addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_SELF, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_FALSE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODLIFE, 3, NA, NULL); // l4 addot(OT_S_PROTEVIL, "protection from evil", "Repels the next 5+(^bpower^n*2) attacks from Evil-aligned creatures. ", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL); @@ -3565,6 +3624,7 @@ void initobjects(void) { addflag(lastot->flags, F_TARGETTEDSPELL, TT_ALLY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOFLEE, ST_SELF, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODLIFE, 4, NA, NULL); addot(OT_S_CUREPOISON, "cure poison", "Cures the target of all poisons.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL); @@ -3572,6 +3632,7 @@ void initobjects(void) { addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_TARGETTEDSPELL, TT_MONSTER, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODLIFE, 4, NA, NULL); // l5 addot(OT_S_RESTORATION, "restoration", "Restore nearly all the target's HP and MP, and remove almost all negative status effects.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL); @@ -3579,6 +3640,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); addflag(lastot->flags, F_TARGETTEDSPELL, TT_ALLY, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODLIFE, 5, NA, NULL); // l6 addot(OT_S_RESSURECTION, "ressurection", "Restore one adjacent corpse to life.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_LIFE, NA, NA, NULL); @@ -3709,6 +3771,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLLEVEL, 1, NA, NA, NULL); addflag(lastot->flags, F_TARGETTEDSPELL, TT_DOOR|TT_IMPASSABLE, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODTHIEVES, 5, NA, NULL); addot(OT_S_LIGHT, "light area", "Creates a temporary light source centred on the caster.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At Power III, you can control where the light appears."); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At Power V, the light will blind creatures with night vision."); @@ -3780,6 +3843,7 @@ void initobjects(void) { addflag(lastot->flags, F_MAXPOWER, 1, NA, NA, NULL); addflag(lastot->flags, F_SPELLLEVEL, 3, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODNATURE, 25, NA, NULL); // l4 addot(OT_S_EXCAVATE, "excavate", "Creates a large open area surrounding the caster. No effect on living creatures.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_MODIFICATION, NA, NA, NULL); @@ -3853,6 +3917,7 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLLEVEL, 2, NA, NA, NULL); addflag(lastot->flags, F_TARGETTEDSPELL, TT_NONE, NA, NA, NULL); addflag(lastot->flags, F_LOSLOF, B_TRUE, LOF_DONTNEED, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODNATURE, 5, NA, NULL); // l3 addot(OT_S_CREATEMONSTER, "create monster", "Summons a (probably hostile) monster to a nearby location.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_EXTRADESC, NA, NA, NA, "At Power V you can control where the monster appears."); @@ -4038,9 +4103,11 @@ void initobjects(void) { addot(OT_S_WISH, "wish", "Grants the caster any item of their choice. Beware - casting this powerful spell will reduce the caster's hit points by 50%.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINE, NA, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODNATURE, 10, NA, NULL); addot(OT_S_WISHLIMITED, "limited wish", "Grants the caster a wish of their choice. Beware - casting this powerful spell will reduce the caster's hit points by 25%.", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLLEVEL, 5, NA, NA, NULL); addflag(lastot->flags, F_SPELLSCHOOL, SS_DIVINE, NA, NA, NULL); + addflag(lastot->flags, F_PLEASESGOD, R_GODNATURE, 5, NA, NULL); // l6 addot(OT_S_GIFT, "gift", "Grants the target any item of their choice (with some limitations).", MT_NOTHING, 0, OC_SPELL, SZ_TINY); addflag(lastot->flags, F_SPELLLEVEL, 6, NA, NA, NULL); @@ -4147,6 +4214,8 @@ void initobjects(void) { addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_PRAY, "pray", "Ask for help from a higher being.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); + addot(OT_A_REFLEXDODGE, "reflexive dodging", "Automatically use all remaining stamina to dodge fatal attacks.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); + addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addot(OT_A_RAGE, "rage", "Enter a state of berzerker rage, gaining attack and defence bonuses.", MT_NOTHING, 0, OC_ABILITY, SZ_TINY); addflag(lastot->flags, F_SPELLSCHOOL, SS_ABILITY, NA, NA, NULL); addflag(lastot->flags, F_AICASTTOATTACK, ST_ADJSELF, NA, NA, NULL); @@ -7998,7 +8067,7 @@ void initrace(void) { addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "killing dragons"); addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "cooking"); addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "creating objects"); - addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "casting nature spells"); + addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "casting certain nature spells"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "damaging or destroying objects"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "the use of poison"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "attacking plants"); @@ -8086,7 +8155,7 @@ void initrace(void) { } addflag(lastrace->flags, F_GODPOISON, B_FALSE, 25, NA, NULL); addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "destroying the undead"); - addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "casting holy spells"); + addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "casting certain holy spells"); addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "natural healing"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "the destruction of healing potions"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "the use of poison"); @@ -8181,7 +8250,7 @@ void initrace(void) { addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "starting fires"); addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "burning objects"); addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "killing with fire"); - addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "using fire-based magic"); + addflag(lastrace->flags, F_GODLIKES, NA, NA, NA, "using destructive fire-based magic"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "creating objects"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "inflicting cold-based damage"); addflag(lastrace->flags, F_GODDISLIKES, NA, NA, NA, "using cold-based magic"); @@ -12813,6 +12882,7 @@ void initskills(void) { addskilldesc(SK_PERCEPTION, PR_NOVICE, "^gYou can now check for trails on staircases before descending.^n", B_TRUE); addskilldesc(SK_PERCEPTION, PR_BEGINNER, "^gYou can now determine the depth and direction of footprints.^n", B_TRUE); addskilldesc(SK_PERCEPTION, PR_BEGINNER, "^gYou now have perception of your blind spots.^n", B_TRUE); + addskilldesc(SK_PERCEPTION, PR_BEGINNER, "^gYou can now recognise the quality of items.^n", B_TRUE); addskilldesc(SK_PERCEPTION, PR_ADEPT, "^gYour field of vision is now wider.^n", B_TRUE); addskilldesc(SK_PERCEPTION, PR_EXPERT, "^gYou can now move without leaving footprints.^n", B_TRUE); addskilldesc(SK_PERCEPTION, PR_MASTER, "^gYou field of vision now extends behind you.^n", B_TRUE); diff --git a/data/hiscores.db b/data/hiscores.db index 4b603d435d6961d29b0d2bacf73b443fff05befa..90903bcfbfc13323a39ad793b54ba544f3357a9f 100644 GIT binary patch delta 383 zcmZq3Xvml#%{z?&0yuya<3^1;%p4rdrXrqs(SMu1$hG5F;2Em^U*gZ|0E+XXXNm1TZiP>lW8ceyA&K?3tIG znU`9WSdyBekW{IVsF0Rfl$unUu8^Onkds=LnxkN-ke{Ydl98&AQks{ZnxCh)d9!>V zkBR_8HvaOTPS!U3B%%LnqP@G_b+Fq$(KF)m@e z#>CEK%hbfQh3O}=HghEN1m>g68qA5zb0%x(Bu~~-U9-7c=OmLl$S{5ebp|F0CLgAH zrXx&Wn3b5rm|K~5FrH$pXEb5B%P@&Soq4i{arWfDM&6T`8AVQxHS(M+0AwFB^qU-_ Lzh-lrfej-7r9wGB diff --git a/defs.h b/defs.h index ac8a4df..106d435 100644 --- a/defs.h +++ b/defs.h @@ -1122,6 +1122,21 @@ enum JOB { }; #define J_RANDOM J_NONE +enum SUBJOB { + SJ_NONE, + // mage + SJ_AIRMAGE, + SJ_ICEMAGE, + SJ_FIREMAGE, + SJ_NECROMANCER, + SJ_WILDMAGE, + // warrior + SJ_BATTLEMAGE, + SJ_PALADIN, + SJ_SCOURGE, + +}; + enum MATSTATE { MS_SOLID, MS_LIQUID, @@ -1599,6 +1614,7 @@ enum OBTYPE { OT_A_CRUSH, OT_A_JUMP, OT_A_PRAY, + OT_A_REFLEXDODGE, OT_A_RAGE, OT_A_REPAIR, OT_A_RESIZE, @@ -2539,6 +2555,7 @@ enum FLAG { // magic F_SPELLSCHOOL, // val0 = SPELLSCHOOL enum F_SPELLLEVEL, // val0 = difficulty level of spell + F_PLEASESGOD, // god id val0 likes this spell. pietymod=v1 F_VARPOWER, // can cast this spell at variable power level // for spells with this flag, the MP cost goes up // based on the power level. @@ -2713,7 +2730,7 @@ enum FLAG { // to take stuff out or put it in. F_HOLDING, // this container is a xxx of holding and makes objects // inside become weightless - F_STARTJOB, // val0 = %chance of starting with it, v1 = jobid + F_STARTJOB, // val0 = %chance of starting with it, v1 = jobid, v2=subjobid F_STARTSKILL, // val0 = skill id F_STARTATT, // val0 = A_xxx, val0 = start bracket (ie. IQ_GENIUS) // if text is set, it overrides val0. @@ -2903,7 +2920,8 @@ enum FLAG { F_HITDICE, // text = xdy+z to roll for maxhp per level. F_MAXHPMOD, // maxhp = pctof(v0, maxhp) F_MPDICE, // val0: # d4 to roll for maxmp per level. val1: +xx - F_JOB, // val0 = player's class/job + F_JOB, // val0 = player's job + // val1 = player's subjob or NA F_GODOF, // text = what this lf is the god of. use capitals. // if v0 is b_true, means this is a goddess F_GODLIKES, // text = something this god likes (ie. incs piety) @@ -3101,7 +3119,7 @@ enum FLAG { F_OMNIPOTENT, // knows extra info F_PHOTOMEM, // you don't forget your surroundings F_REGENERATES, // regenerate HP at val0 per turn - F_RESISTMAG, // immunity to magic effects. v0=amt (1-20) + F_RESISTMAG, // immunity to magic effects. v0= resist% F_MPREGEN, // regenerate MP at val0 per turn F_RAGE, // you are enraged. v0/v1 will be set to player's old hp/maxhp F_REFLECTION, // missiles are reflected back at thrower @@ -3187,6 +3205,7 @@ enum FLAG { F_SELECTWEAPON, // this job gets to pick their starting weapon F_NOPLAYER, // players can't pick this job F_HASPET, // this job starts with a pet of race f->text + F_CANHAVESUBJOB, // this job can have subjob = v0 //F_IFPCT, // only add the NEXT job flag if rnd(1,100) <= v0. //F_ELSE, //F_IFPLAYER, @@ -3394,6 +3413,7 @@ enum ERROR { E_GRAVBOOSTED, E_NOMP, E_NOSTAM, + E_NOSPELLS, E_AVOIDOB, E_FROZEN, E_TOOBIG, @@ -3635,6 +3655,14 @@ typedef struct poisontype_s { struct poisontype_s *next, *prev; } poisontype_t; +typedef struct subjob_s { + enum SUBJOB id; + char *name; + char *desc; + char letter; + struct subjob_s *next, *prev; +} subjob_t; + typedef struct room_s { int id; int x1,y1,x2,y2; @@ -3893,6 +3921,10 @@ typedef struct lifeform_s { float stamlastturn; // for stam bar. not saved. int bartimer; // not saved. + // tracks whether this lf has rotated or not this turn. + // the first rotation in a turn is free, others cost time. + int rotated; + // set to TRUE after lf has being created int born; diff --git a/god.c b/god.c index 604fa17..2b85355 100644 --- a/god.c +++ b/god.c @@ -2027,7 +2027,7 @@ int prayto(lifeform_t *lf, lifeform_t *god) { donesomething = B_TRUE; } if (lf->hp < lf->maxhp) { - lf->hp = lf->maxhp; + gainhp(lf, lf->maxhp); donesomething = B_TRUE; statdirty = B_TRUE; diff --git a/io.c b/io.c index 8099431..89f6113 100644 --- a/io.c +++ b/io.c @@ -670,12 +670,10 @@ cell_t *askcoords(char *prompt, char *subprompt, int targettype, lifeform_t *src strcpy(extrainfo, ""); getlfnamea(c->lf, buf); if (lfhasflag(c->lf, F_NAME)) { - job_t *j; // add on their job - j = getjob(c->lf); - if (j) { + if (getjob(c->lf)) { strcat(buf, " the "); - strcat(buf, j->name); + strcat(buf, getjobname(c->lf)); } } else if (c->lf->race->raceclass->id == RC_GOD) { f = lfhasflag(c->lf, F_GODOF); @@ -1433,6 +1431,12 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { } donesomething = B_TRUE; break; + case F_DODGES: + if (isplayer(lf)) { + msg("You now automatically dodge fatal attacks."); + } + donesomething = B_TRUE; + break; case F_DTIMMUNE: if (isplayer(lf)) { // don't know if monsters get it msg("^gYou feel immune to %s!", getdamnamenoun(f->val[0])); @@ -1481,12 +1485,6 @@ int announceflaggain(lifeform_t *lf, flag_t *f) { donesomething = B_TRUE; } break; - case F_DODGES: - if (isplayer(lf)) { // don't know if monsters get it - msg("You can now dodge attacks."); - donesomething = B_TRUE; - } - break; case F_HEAVENARM: if (isplayer(lf)) { // don't know if monsters get it msg("You are surrounded by a shell of divine armour!"); @@ -2107,6 +2105,12 @@ int announceflagloss(lifeform_t *lf, flag_t *f) { } donesomething = B_TRUE; break; + case F_DODGES: + if (isplayer(lf)) { + msg("You will no longer automatically dodge fatal attacks."); + } + donesomething = B_TRUE; + break; case F_DTIMMUNE: if (isplayer(lf)) { // don't know if monsters lose it msg("You are no longer immune to %s.", getdamnamenoun(f->val[0])); @@ -2199,12 +2203,6 @@ int announceflagloss(lifeform_t *lf, flag_t *f) { msg("%s %s less sick now.", lfname, isplayer(lf) ? "feel" : "looks"); donesomething = B_TRUE; break; - case F_DODGES: - if (isplayer(lf)) { // don't know if monsters lose it - msg("You can no longer dodge attacks."); - donesomething = B_TRUE; - } - break; case F_HEAVENARM: if (isplayer(lf)) { // don't know if monsters get it msg("Your shell of divine armour vanishes!"); @@ -5144,6 +5142,38 @@ char *makedesc_god(lifeform_t *god, char *retbuf) { return retbuf; } +char *makedesc_job(job_t *j, char *retbuf) { + char thisline[BUFLEN]; + flag_t *retflag[MAXCANDIDATES]; + int nretflags,i; + + // start with job description + strcpy(retbuf, j->desc); + + strcat(retbuf, "\n\n"); + + + getflags(j->flags, retflag, &nretflags, F_CANHAVESUBJOB, F_NONE); + if (nretflags > 0) { + int first = B_TRUE; + for (i = 0; i < nretflags; i++) { + subjob_t *sj; + sj = findsubjob(retflag[i]->val[0]); + if (sj) { + if (first) { + sprintf(thisline, "%ss can have the following specialisations:\n",j->name); + strncat(retbuf, thisline, HUGEBUFLEN); + first = B_FALSE; + } + sprintf(thisline, "- %s\n",sj->name); + strncat(retbuf, thisline, HUGEBUFLEN); + } + } + } + + return retbuf; +} + char *makedesc_ob(object_t *o, char *retbuf) { char buf[BIGBUFLEN]; char buf2[BUFLEN]; @@ -6109,6 +6139,10 @@ char *makedesc_ob(object_t *o, char *retbuf) { sprintf(buf2, "%s will detect nearby metal.\n", buf); strncat(retbuf, buf2, HUGEBUFLEN); break; + case F_DODGES: + sprintf(buf2, "%s lets you use remaining stamina to dodge fatal attacks.\n", buf); + strncat(retbuf, buf2, HUGEBUFLEN); + break; case F_ENHANCESEARCH: sprintf(buf2, "%s enhances your searching ability.\n", buf); strncat(retbuf, buf2, HUGEBUFLEN); @@ -6189,10 +6223,6 @@ char *makedesc_ob(object_t *o, char *retbuf) { sprintf(buf2, "%s causes your feet to constantly burn.\n", buf); strncat(retbuf, buf2, HUGEBUFLEN); break; - case F_DODGES: - sprintf(buf2, "%s allows you to dodge attacks.\n", buf); - strncat(retbuf, buf2, HUGEBUFLEN); - break; case F_INVISIBLE: sprintf(buf2, "%s renders you invisible to normal sight.\n", buf); strncat(retbuf, buf2, HUGEBUFLEN); @@ -6650,7 +6680,7 @@ char *makedesc_race(enum RACE rid, char *retbuf, int showextra, int forplayersel strcat(buf, ")"); } break; - case F_DODGES: strcpy(buf, "Can dodge attacks into adjacent locations"); break; + case F_DODGES: strcpy(buf, "Can dodge fatal attacks into adjacent locations"); break; case F_DTIMMUNE: if (!hasflag(doneflags, F_DTIMMUNE)) { if (f->val[0] == DT_ALL) { @@ -6702,6 +6732,13 @@ char *makedesc_race(enum RACE rid, char *retbuf, int showextra, int forplayersel 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_HITCONFER: + 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; @@ -7000,11 +7037,26 @@ char *makedesc_spell(objecttype_t *ot, char *retbuf) { strncat(retbuf, buf, HUGEBUFLEN); strncat(retbuf, "\n", HUGEBUFLEN); + getflags(ot->flags, retflag, &nretflags, F_PLEASESGOD, F_NONE); + for (i = 0; i < nretflags; i++) { + lifeform_t *god; + flag_t *f; + god = findgod(retflag[i]->val[0]); + f = lfhasflag(god, F_GODOF); + sprintf(buf, "Successfully casting this spell will please %s (%s of %s).\n",god->race->name, + (f->val[0] == B_FEMALE) ? "Goddess" : "God", f->text); + strncat(retbuf, buf, BUFLEN); + } + + if (nretflags) { + strncat(retbuf, "\n", HUGEBUFLEN); + } + if (ot->obclass->id == OC_SPELL) { if (power > 0) { - sprintf(buf, "^gYou can cast it at power level %d (maximum %d).\n",power, getspellmaxpower(ot->id)); + sprintf(buf, "^gYou can cast it at power level %d (maximum %d).^n\n",power, getspellmaxpower(ot->id)); } else { - sprintf(buf, "^BIt is too powerful for you to cast (maximum power %d).\n", getspellmaxpower(ot->id)); + sprintf(buf, "^BIt is too powerful for you to cast (maximum power %d).^n\n", getspellmaxpower(ot->id)); } strncat(retbuf, buf, BUFLEN); @@ -7013,6 +7065,8 @@ char *makedesc_spell(objecttype_t *ot, char *retbuf) { strncat(retbuf, buf, BUFLEN); } } + + return retbuf; } @@ -9408,9 +9462,7 @@ void handleinput(void) { case CH_TURN_NW: dir = chartodir(ch); if (dir != player->facing) { - if (getrelativedir(player, dir) != RD_FORWARDS) { - taketime(player, getturnspeed(player)); - } + takerotationtime(player); setfacing(player, dir); drawscreen(); } @@ -10622,9 +10674,9 @@ void showlfstats(lifeform_t *lf, int showall) { if (j) { doheadingsmall(mainwin, y, 0, ftext, "Job"); if (showall) { - snprintf(buf, BUFLEN, "Level %d %s", lf->level, j->name); + snprintf(buf, BUFLEN, "Level %d %s", lf->level, getjobname(lf)); } else { - snprintf(buf, BUFLEN, "%s", j->name); + snprintf(buf, BUFLEN, "%s", getjobname(lf)); } wprintw(mainwin, "%-20s", buf); y++; } @@ -10752,7 +10804,7 @@ void showlfstats(lifeform_t *lf, int showall) { } break; case A_CON: - mod = getstatmod(lf, A_CON); + mod = getstatmod(lf, i); if (mod > 0) { mod *= 2; // note: same code as in rollhitdice snprintf(buf2, BUFLEN, ", +%d%% hp",mod ); @@ -11790,6 +11842,65 @@ void showlfstats(lifeform_t *lf, int showall) { } + // show boosts to spell power + if (showall) { + // note: same code as in getspellpower() + enum SPELLSCHOOL school[5]; + int nschools = 5; + + school[0] = SS_WILD; + school[1] = SS_MENTAL; + school[2] = SS_NATURE; + school[3] = SS_LIFE; + school[4] = SS_ALLOMANCY; + + for (i = 0; i < nschools; i++) { + enum ATTRIB att = A_NONE; + int n,found = B_FALSE; + + // can you cast any spells from this school? + getflags(lf->flags, retflag, &nretflags, F_CANCAST, F_CANWILL, F_NONE); + for (n = 0; n < nretflags; n++) { + enum SPELLSCHOOL thisschool; + thisschool = getspellschool(retflag[n]->val[0]); + if (thisschool == school[i]) { + found = B_TRUE; + break; + } + } + + if (found) { + int mod; + enum RACE godid = R_NONE; + mod = getspellpowerstatmod(lf, school[i], &att); + if (mod) { + mvwprintw(mainwin, y, 0, "%s %s %s will %s the power of %s %s by %d.", + your(lf), + (mod > 0) ? "high" : "low", getattrname(att), + (mod > 0) ? "boost" : "lower", + (school[i] == SS_WILD) ? "arcane" : + (school[i] == SS_MENTAL) ? "Psionic" : + getschoolname(school[i]), + (school[i] == SS_MENTAL) ? "disciplines" : "spells", + abs(mod) ); + y++; + } + + // boosted by god? + mod = getspellpowergodmod(lf, school[i], &godid); + if (mod > 0) { + lifeform_t *god; + god = findgod(godid); + if (god) { + mvwprintw(mainwin, y, 0, "%s is boosting the power of %s %s spells by %d.", + god->race->name, your(lf), mod); + y++; + } + } + } // end if found + } // end foreach school + } + // we DONT show traits based on the monster's racial characteristics // here. leave that to makedesc_race. // @@ -11836,16 +11947,28 @@ void showlfstats(lifeform_t *lf, int showall) { int mr; char adjective[BUFLEN]; mr = getmr(lf); - if (mr <= 5) { - strcpy(adjective, "slightly"); - } else if (mr <= 10) { - strcpy(adjective, "quite"); - } else if (mr <= 15) { - strcpy(adjective, "very"); + if (mr <= 10) { + strcpy(adjective, "very slightly"); } else if (mr <= 20) { + strcpy(adjective, "slightly"); + } else if (mr <= 30) { + strcpy(adjective, "somewhat"); + } else if (mr <= 40) { + strcpy(adjective, "moderately"); + } else if (mr <= 50) { + strcpy(adjective, "quite"); + } else if (mr <= 60) { + strcpy(adjective, "highly"); + } else if (mr <= 70) { + strcpy(adjective, "very highly"); + } else if (mr <= 80) { strcpy(adjective, "extremely"); - } else { // ie. 21 upwards + } else if (mr <= 90) { strcpy(adjective, "incredibly"); + } else if (mr < 100) { + strcpy(adjective, "almost completely"); + } else { // ie. 100 upwards + strcpy(adjective, "completely"); } mvwprintw(mainwin, y, 0, "%s %s %s resistant to magic.", you(lf), is(lf), adjective); y++; @@ -12111,6 +12234,11 @@ void showlfstats(lifeform_t *lf, int showall) { mvwprintw(mainwin, y, 0, "%s can see things which are behind %s.", you(lf), you(lf)); y++; } + f = hasflag_real(lf->flags, F_DODGES, B_TRUE, NULL, FROMRACE); + if (f) { + mvwprintw(mainwin, y, 0, "%s will automatically use your remaining stamina to dodge fatal attacks.", you(lf)); + y++; + } f = lfhasknownflag(lf, F_MAGICARMOUR); if (f && (f->known)) { diff --git a/io.h b/io.h index d65e23a..94261a4 100644 --- a/io.h +++ b/io.h @@ -105,6 +105,7 @@ void initprompt(prompt_t *p, char *q1); int keycodetokey(int keycode, int escseqok); void listobs(WINDOW *win, object_t **mylist, int *sellist, int *selcount, int firstob, int *counter, int lastline, int *y, char *myletters, int forpickup, int showpoints, object_t *sellshop); char *makedesc_god(lifeform_t *god, char *retbuf); +char *makedesc_job(job_t *j, char *retbuf); char *makedesc_ob(object_t *o, char *retbuf); char *makedesc_race(enum RACE rid, char *retbuf, int showextra, int forplayersel); char *makedesc_skill(enum SKILL skid, char *retbuf, enum SKILLLEVEL levhilite); diff --git a/lf.c b/lf.c index 2716bf6..3413a7f 100644 --- a/lf.c +++ b/lf.c @@ -33,6 +33,7 @@ extern raceclass_t *firstraceclass, *lastraceclass; extern job_t *firstjob, *lastjob; extern skill_t *firstskill, *lastskill; extern poisontype_t *firstpoisontype,*lastpoisontype; +extern subjob_t *firstsubjob,*lastsubjob; extern objecttype_t *objecttype; extern lifeform_t *player; @@ -566,6 +567,10 @@ 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) { @@ -1505,16 +1510,8 @@ int cantalk(lifeform_t *lf) { } // if the lf can't explicitly talk, check its race if (!lfhasflag(lf, F_CANTALK)) { - switch (lf->race->raceclass->id) { - case RC_DEMON: - case RC_DRAGON: - case RC_GOD: - case RC_HUMANOID: - // these ones can talk - break; - default: - return B_FALSE; - break; + if (!racecantalk(lf->race->raceclass->id)) { + return B_FALSE; } } @@ -1567,6 +1564,9 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar case E_NOTREADY: msg("This ability is not recharged yet."); break; + case E_NOSPELLS: + msg("You can't cast spells."); + break; case E_PRONE: msg("You can't cast spells while prone."); break; @@ -1688,7 +1688,7 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar return B_FALSE; } } - // casting a nature spell while nature god is angry at you? + // casting a god-related spell while that god is angry at you? if (isplayer(lf) && !willflag) { if (godprayedto(R_GODNATURE) && godisangry(R_GODNATURE)) { if (hasflagval(sp->flags, F_SPELLSCHOOL, SS_NATURE, NA, NA, NULL)) { @@ -1842,28 +1842,33 @@ int castspell(lifeform_t *lf, enum OBTYPE sid, lifeform_t *targlf, object_t *tar } } if (isplayer(lf)) { + objecttype_t *ot; + flag_t *retflag[MAXCANDIDATES]; + int nretflags,i; pleasegodmaybe(R_GODMAGIC, getspelllevel(sid)); if (!fromob) { angergodmaybe(R_GODBATTLE, 25, GA_SPELL); } + ot = findot(sid); + getflags(ot->flags, retflag, &nretflags, F_PLEASESGOD, F_NONE); + for (i = 0; i < nretflags; i++) { + f = retflag[i]; + if (f->id == F_PLEASESGOD) { + pleasegodmaybe(f->val[0], f->val[1]); + } + } + + // anger gods? switch (school) { case SS_COLD: angergodmaybe(R_GODFIRE, getspelllevel(sid)*5, GA_SPELL); break; case SS_DEATH: - pleasegodmaybe(R_GODDEATH, getspelllevel(sid)); angergodmaybe(R_GODLIFE, getspelllevel(sid)*5, GA_SPELL); break; - case SS_FIRE: - pleasegodmaybe(R_GODFIRE, getspelllevel(sid)*2); - break; case SS_LIFE: - pleasegodmaybe(R_GODLIFE, getspelllevel(sid)*2); angergodmaybe(R_GODDEATH, getspelllevel(sid)*5, GA_SPELL); break; - case SS_NATURE: - pleasegodmaybe(R_GODNATURE, getspelllevel(sid)*2); - break; default: break; } @@ -2999,6 +3004,18 @@ void genareaknowledge(flagpile_t *fp, int chancemod) { // if alignment needs to be generated randomly (or selected in the case of the player), do so. void generatealignment(lifeform_t *lf) { flag_t *f; + + // sometimes alignment is forced. + switch (getsubjob(lf)) { + case SJ_NECROMANCER: + setalignment(lf, AL_EVIL); + return; + case SJ_PALADIN: + setalignment(lf, AL_GOOD); + return; + default: break; + } + f = hasflagval(lf->flags, F_ALIGNMENT, AL_NONE, NA, NA, NULL); if (f && strlen(f->text)) { char buf[BUFLEN]; @@ -4870,6 +4887,14 @@ enum SKILLLEVEL findskilllevbyname(char *name) { return PR_INEPT; } +subjob_t *findsubjob(enum SUBJOB sjid) { + subjob_t *j; + for (j = firstsubjob ; j ; j = j->next) { + if (j->id == sjid) return j; + } + return NULL; +} + // try to actually do the 'run away' action for // anyone we are fleeing from. // returns TRUE if we ran away from something @@ -6629,6 +6654,32 @@ job_t *getjob(lifeform_t *lf) { return NULL; } +enum SUBJOB getsubjob(lifeform_t *lf) { + flag_t *f; + f = lfhasflag(lf, F_JOB); + if (f && (f->val[1] != NA)) { + return f->val[1]; + } + return SJ_NONE; +} + +char *getjobname(lifeform_t *lf) { + enum SUBJOB sjid; + job_t *j; + subjob_t *sj; + sjid = getsubjob(lf); + sj = findsubjob(sjid); + if (sj) { + return sj->name; + } + + j = getjob(lf); + if (j) { + return j->name; + } + return ""; +} + int getlastdir(lifeform_t *lf) { flag_t *f; f = lfhasflag(lf, F_LASTDIR); @@ -6933,6 +6984,18 @@ int getmorale(lifeform_t *lf) { return 0; } +int getnextshortcut(lifeform_t *lf) { + flag_t *retflag[MAXCANDIDATES]; + int nretflags,i,min = 0; + getflags(lf->flags, retflag, &nretflags, F_SHORTCUT, F_NONE); + for (i = 0; i < nretflags; i++) { + if (retflag[i]->val[0] > min) { + min = retflag[i]->val[0]; + } + } + return min + 1; +} + int getnightvisrange(lifeform_t *lf) { int range = 0; // default flag_t *f; @@ -7416,6 +7479,10 @@ int getmr(lifeform_t *lf) { sumflags(lf->flags, F_RESISTMAG, &amt, NULL, NULL); + if (hassubjob(lf, SJ_SCOURGE)) { + amt += (gethitdice(lf) * 3); + } + return amt; } @@ -7656,7 +7723,6 @@ char *real_getlfname(lifeform_t *lf, char *buf, int usevis, int showall) { char jobstring[BUFLEN]; char the[6]; char lname[BUFLEN]; - job_t *j; flag_t *f; enum LFSIZE size,racesize; int dobehaviour = B_TRUE; @@ -7722,9 +7788,8 @@ char *real_getlfname(lifeform_t *lf, char *buf, int usevis, int showall) { strcpy(jobstring, ""); if (showall || isplayer(lf) || (getlorelevel(player, lf->race->raceclass->id) >= PR_BEGINNER)) { if (!lfhasflag(lf, F_NOJOBTEXT) && !lfhasflag(lf, F_NAME) && !lfhasflag(lf, F_UNIQUE)) { - j = getjob(lf); - if (j) { - snprintf(jobstring, BUFLEN, " %s", j->name); + if (getjob(lf)) { + snprintf(jobstring, BUFLEN, " %s", getjobname(lf)); jobstring[1] = tolower(jobstring[1]); } } @@ -8000,14 +8065,12 @@ char *getplayername(char *buf) { char *getplayernamefull(char *buf) { char pname[BUFLEN]; - job_t *j; getplayername(pname); if (strlen(pname)) { - j = getjob(player); - if (j) { - snprintf(buf, BUFLEN, "%s the %s %s", pname, player->race->name, j->name); + if (getjob(player)) { + snprintf(buf, BUFLEN, "%s the %s %s", pname, player->race->name, getjobname(player)); } else { snprintf(buf, BUFLEN, "%s the %s", pname, player->race->name); } @@ -9117,6 +9180,7 @@ void givejob(lifeform_t *lf, enum JOB jobid) { switch (f->id) { case F_MAXHPMOD: case F_JOBATTRMOD: + case F_CANHAVESUBJOB: ignorethis = B_TRUE; break; case F_CANLEARN: @@ -9212,9 +9276,213 @@ void givejob(lifeform_t *lf, enum JOB jobid) { */ } + // extra tasks for some jobs + if (j->id == J_DRUID) { + enum OBTYPE spell; + int i; + for (i = 0;i < 3; i++) { + // pick a spell which you don't already have... + spell = getrandomspellfromschool(SS_NATURE, 1); + while (cancast(lf, spell, NULL)) { + spell = getrandomspellfromschool(SS_NATURE, 1); + } + // you can now cast it. + addflag(lf->flags, F_CANCAST, spell, NA, NA, NULL); + } + if (isplayer(lf)) { + lifeform_t *god; + // druids always worship ekrub + god = findgod(R_GODNATURE); + addflag(god->flags, F_PRAYEDTO, B_TRUE, NA, NA, NULL); + } + } + + + // select subjobs + 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); + initprompt(&prompt, "Select your job specialty:"); + for (sub = firstsubjob ; sub ; sub = sub->next) { + if (hasflagval(j->flags, F_CANHAVESUBJOB, sub->id, NA, NA, NULL)) { + addchoice(&prompt, sub->letter, sub->name, NULL, sub, sub->desc); + } + } + + addchoice(&prompt, '-', "(none)", NULL, NULL, NULL); + if (prompt.nchoices > 1) { + ch = getchoice(&prompt); + sub = (subjob_t *)prompt.result; + if (sub) { + sj = sub->id; + } else { + sj = SJ_NONE; + } + } + givesubjob(lf, sj); + + if (j->id == J_WIZARD) { + object_t *sb2; + skill_t *sk; + // wizards now get a secondary school + initprompt(&prompt, "Select your secondary spell school:"); + addchoice(&prompt, 'd', getskillname(SK_SS_DIVINATION), NULL, findskill(SK_SS_DIVINATION), NULL); + addchoice(&prompt, 'g', getskillname(SK_SS_GRAVITY), NULL, findskill(SK_SS_GRAVITY), NULL); + addchoice(&prompt, 'm', getskillname(SK_SS_MODIFICATION), NULL, findskill(SK_SS_MODIFICATION), NULL); + addchoice(&prompt, 's', getskillname(SK_SS_SUMMONING), NULL, findskill(SK_SS_SUMMONING), NULL); + addchoice(&prompt, 't', getskillname(SK_SS_TRANSLOCATION), NULL, findskill(SK_SS_TRANSLOCATION), NULL); + getchoice(&prompt); + sk = (skill_t *) prompt.result; + switch (sk->id) { + case SK_SS_DIVINATION: + sb2 = addob(lf->pack, "spellbook of divination magic"); + break; + case SK_SS_GRAVITY: + sb2 = addob(lf->pack, "spellbook of gravitation magic"); + break; + case SK_SS_MODIFICATION: + sb2 = addob(lf->pack, "spellbook of modification magic"); + break; + case SK_SS_SUMMONING: + sb2 = addob(lf->pack, "spellbook of summoning magic"); + break; + case SK_SS_TRANSLOCATION: + sb2 = addob(lf->pack, "spellbook of translocation magic"); + break; + default: + sb2 = NULL; + break; + } + if (sb2) { + addflag(lf->flags, F_CANCAST, sb2->contents->first->type->id, NA, NA, NULL); + if (isplayer(lf)) { + addflag(lf->flags, F_SHORTCUT, getnextshortcut(lf), NA, NA, sb2->contents->first->type->name); + addflag(sb2->flags, F_NOPOINTS, B_TRUE, NA, NA, NULL); + } + } + identify(sb2); + } + } + generatealignment(lf); } +void givesubjob(lifeform_t *lf, enum SUBJOB sj) { + flag_t *jobflag; + object_t *sb1,*o; + int i; + + if (sj == SJ_NONE) return; + + // remember the subjob + jobflag = lfhasflag(lf, F_JOB); + jobflag->val[1] = sj; + + switch (sj) { + // mage types + case SJ_AIRMAGE: + addflag(lf->flags, F_CANSEETHROUGHMAT, MT_GAS, NA, NA, NULL); + sb1 = addob(lf->pack, "spellbook of air magic"); + break; + case SJ_ICEMAGE: + sb1 = addob(lf->pack, "spellbook of cold magic"); + break; + case SJ_FIREMAGE: + sb1 = addob(lf->pack, "spellbook of fire magic"); + break; + case SJ_NECROMANCER: + sb1 = addob(lf->pack, "spellbook of necromancy"); + break; + case SJ_WILDMAGE: + sb1 = addob(lf->pack, "spellbook of wild magic"); + break; + // warrior types + case SJ_BATTLEMAGE: + // starts off skilled in one school + switch (rnd(1,2)) { + case 1: + giveskilllev(lf, SK_SS_FIRE, PR_NOVICE); + sb1 = addob(lf->pack, "spellbook of fire magic"); + break; + case 2: + giveskilllev(lf, SK_SS_COLD, PR_NOVICE); + sb1 = addob(lf->pack, "spellbook of cold magic"); + break; + } + // can learn some spell schools, but only up to adept level + addtempflag(lf->flags, F_CANLEARN, SK_SS_FIRE, PR_ADEPT, NA, NULL, FROMJOB); + addtempflag(lf->flags, F_CANLEARN, SK_SS_COLD, PR_ADEPT, NA, NULL, FROMJOB); + addtempflag(lf->flags, F_CANLEARN, SK_SS_AIR, PR_ADEPT, NA, NULL, FROMJOB); + addtempflag(lf->flags, F_CANLEARN, SK_SS_GRAVITY, PR_ADEPT, NA, NULL, FROMJOB); + addtempflag(lf->flags, F_CANLEARN, SK_SS_MODIFICATION, PR_ADEPT, NA, NULL, FROMJOB); + addtempflag(lf->flags, F_CANLEARN, SK_SS_TRANSLOCATION, PR_ADEPT, NA, NULL, FROMJOB); + addtempflag(lf->flags, F_CANLEARN, SK_SS_WILD, PR_ADEPT, NA, NULL, FROMJOB); + // raise IQ to average so that we aren't too dumb to cast L1 spells + i = rollattr(AT_AVERAGE); + limit(&i, 50, NA); + lf->baseatt[A_IQ] = i; + lf->att[A_IQ] = i; + // replace mpdice + killflagsofid(lf->flags, F_MPDICE); + addtempflag(lf->flags, F_MPDICE, 1, 0, NA, NULL, FROMJOB); + // remove warrior's hitdice bonus + killflagsofid(lf->flags, F_MAXHPMOD); + // remove warrior's level abilities + killflagsofid(lf->flags, F_LEVABIL); + break; + case SJ_PALADIN: + // healing magic + giveskilllev(lf, SK_SS_LIFE, PR_NOVICE); + sb1 = addob(lf->pack, "spellbook of life magic"); + // must worship glorana + if (isplayer(lf)) { + lifeform_t *god; + god = findgod(R_GODLIFE); + addflag(god->flags, F_PRAYEDTO, B_TRUE, NA, NA, NULL); + } + // raise WIS to gtaverage for life magic + lf->baseatt[A_WIS] = rollattr(AT_GTAVERAGE); + lf->att[A_WIS] = lf->baseatt[A_WIS]; + // can permenantly turn undead for 0 power. + addtempflag(lf->flags, F_CANWILL, OT_S_TURNUNDEAD, NA, NA, NULL, FROMJOB); + if (isplayer(lf)) { + addflag(lf->flags, F_SHORTCUT, getnextshortcut(lf), NA, NA, "turn undead"); + } + // all gear is blessed + for (o = lf->pack->first ; o ; o = o->next) { + if (isequipped(o)) { + blessob(o); + } + } + break; + case SJ_SCOURGE: + addtempflag(lf->flags, F_RESISTMAG, 5, NA, NA, NULL, FROMJOB); + // no mp. + killflagsofid(lf->flags, F_MPDICE); + addtempflag(lf->flags, F_NOSPELLS, B_TRUE, NA, NA, NULL, FROMJOB); + + break; + // + default: + sb1 = NULL; + break; + } + + if (sb1) { + addtempflag(lf->flags, F_CANCAST, sb1->contents->first->type->id, NA, NA, NULL, FROMJOB); + if (isplayer(lf)) { + addtempflag(lf->flags, F_SHORTCUT, getnextshortcut(lf), NA, NA, sb1->contents->first->type->name, FROMJOB); + addflag(sb1->flags, F_NOPOINTS, B_TRUE, NA, NA, NULL); + } + identify(sb1); + } + +} + int givemoney(lifeform_t *from, lifeform_t *to, int amt) { object_t *gold; @@ -9542,7 +9810,10 @@ flag_t *giveskill(lifeform_t *lf, enum SKILL id) { newf = addtempflag(lf->flags, F_CANWILL, OT_A_SNATCH, NA, NA, NULL, FROMSKILL); } } else if (f->val[1] == PR_SKILLED) { - newf = addtempflag(lf->flags, F_DODGES, B_TRUE, NA, NA, NULL, FROMSKILL); + newf = hasflagval(lf->flags, F_CANWILL, OT_A_REFLEXDODGE, NA, NA, NULL); + if (!newf) { + newf = addtempflag(lf->flags, F_CANWILL, OT_A_REFLEXDODGE, NA, NA, NULL, FROMSKILL); + } } } else if (id == SK_LORE_ARCANA) { if (f->val[1] == PR_ADEPT) { @@ -10118,6 +10389,15 @@ int hastempinjuries(lifeform_t *lf) { return count; } +int hassubjob(lifeform_t *lf, enum SUBJOB id) { + flag_t *f; + f = lfhasflag(lf, F_JOB); + if (f && (f->val[1] == id)) { + return B_TRUE; + } + return B_FALSE; +} + void inc_quad_range(enum QUADRANT *start, enum QUADRANT *end, int howmuch) { int i; for (i = 0; i < abs(howmuch); i++) { @@ -11698,6 +11978,7 @@ object_t *isstuck(lifeform_t *lf) { return NULL; } + poisontype_t *addpoisontype(enum POISONTYPE id, char *name, char *desc, char *damverb, enum OBTYPE vomitob, int dam, int dampct, enum POISONSEVERITY severity) { poisontype_t *a; @@ -11831,6 +12112,7 @@ lifeform_t *real_addlf(cell_t *cell, enum RACE rid, int level, int controller) { } else { a->timespent = 0; } + a->rotated = B_FALSE; a->sorted = B_FALSE; a->forgettimer = 0; @@ -12033,6 +12315,33 @@ void addskilldesc(enum SKILL id, enum SKILLLEVEL lev, char *text, int wantmsg) { sk->nskilldesc++; } +subjob_t *addsubjob(enum SUBJOB id, char *name, char *desc, char letter) { + subjob_t *a; + + // add to the end of the list + if (firstsubjob == NULL) { + firstsubjob = malloc(sizeof(subjob_t)); + a = firstsubjob; + a->prev = NULL; + } else { + // go to end of list + a = lastsubjob; + a->next = malloc(sizeof(subjob_t)); + a->next->prev = a; + a = a->next; + } + lastsubjob = a; + a->next = NULL; + + // props + a->id = id; + a->name = strdup(name); + a->desc = strdup(desc); + a->letter = letter; + + return a; +} + object_t *addtrail(lifeform_t *lf, cell_t *where, int dir, int doprints, int doscents) { object_t *footprint, *scent,*retob = NULL; flag_t *fpflag = NULL; @@ -12674,12 +12983,7 @@ void autoshortcut(lifeform_t *lf, enum OBTYPE spellid) { ot = findot(spellid); if (ot) { // set to lowest possible shortcut - getflags(lf->flags, retflag, &nretflags, F_SHORTCUT, F_NONE); - for (i = 0; i < nretflags; i++) { - if (retflag[i]->val[0] > min) { - min = retflag[i]->val[0]; - } - } + min = getnextshortcut(lf); if (min < 10) { addflag(lf->flags, F_SHORTCUT, min, NA, NA, ot->name); } @@ -13003,6 +13307,33 @@ void killrace(race_t *r) { } } +void killsubjob(subjob_t *sj) { + subjob_t *nextone, *lastone; + + // free mem + free(sj->name); + free(sj->desc); + + // remove from list + nextone = sj->next; + if (nextone != NULL) { + nextone->prev = sj->prev; + } else { /* last */ + lastsubjob = sj->prev; + } + + if (sj->prev == NULL) { + /* first */ + nextone = sj->next; + free(firstsubjob); + firstsubjob = nextone; + } else { + lastone = sj->prev; + free (lastone->next ); + lastone->next = nextone; + } +} + flag_t *levelabilityready(lifeform_t *lf) { flag_t *f; int i; @@ -15109,6 +15440,27 @@ int push(lifeform_t *lf, object_t *o, int dir) { } +int racecantalk(enum RACE rid) { + race_t *r; + switch (rid) { + case RC_DEMON: + case RC_DRAGON: + case RC_GOD: + case RC_HUMANOID: + // these ones can talk + return B_TRUE; + break; + default: + break; + } + + r = findrace(rid); + if (r) { + if (hasflag(r->flags, F_CANTALK)) return B_TRUE; + } + return B_FALSE; +} + int readytotrain(lifeform_t *lf) { if (lf->skillpoints || getattpoints(lf) || levelabilityready(lf)) { return B_TRUE; @@ -16740,7 +17092,7 @@ int real_skillcheck(lifeform_t *lf, enum CHECKTYPE ct, int diff, int mod, int *r } break; case SC_RESISTMAG: - attrib = (getattr(lf, A_CON)/30) + (getattr(lf, A_WIS)/30) + getmr(lf); + attrib = (getattr(lf, A_CON)/30) + (getattr(lf, A_WIS)/30) + (getmr(lf)/5); break; case SC_SEARCH: attrib = (getskill(lf, SK_PERCEPTION)*2); @@ -17160,6 +17512,7 @@ void startlfturn(lifeform_t *lf) { if (isplayer(lf) && lfhasflag(lf, F_DRUNK)) statdirty = B_TRUE; // clear one-turn-only flags + lf->rotated = B_FALSE; killflagsofid(lf->flags, F_JUSTENTERED); killflagsofid(lf->flags, F_UNSEENATTACKER); killflagsofid(lf->flags, F_DONELISTEN); @@ -18516,6 +18869,17 @@ int takeoff(lifeform_t *lf, object_t *o) { } + +void takerotationtime(lifeform_t *lf) { + // first rotation in a turn is free + if (lf->rotated) { + taketime(lf, getturnspeed(lf)); + } else { + lf->rotated = B_TRUE; + } +} + + void taketime(lifeform_t *lf, long howlong) { int db = B_FALSE; map_t *map; @@ -19540,6 +19904,7 @@ int useringofmiracles(lifeform_t *lf, int charges) { } // you know know the obejct makeknown(o->type->id); + getobname(o, obname, 1); // refresh its name in case you didnt know what it was. // use a charge if (o->type->id == OT_RING_MIRACLES) { diff --git a/lf.h b/lf.h index de0d7cb..d3405f6 100644 --- a/lf.h +++ b/lf.h @@ -9,6 +9,7 @@ race_t *addrace(enum RACE id, char *name, float weight, char glyph, int glyphcol raceclass_t *addraceclass(enum RACECLASS id, char *name, char *pluralname, enum SKILL skill); skill_t *addskill(enum SKILL id, char *name, char *desc, int traintime); void addskilldesc(enum SKILL id, enum SKILLLEVEL lev, char *text, int wantmsg); +subjob_t *addsubjob(enum SUBJOB id, char *name, char *desc, char letter); object_t *addtrail(lifeform_t *lf, cell_t *where, int dir, int doprints, int doscents); void adjustdamlf(lifeform_t *lf, int *amt, enum DAMTYPE damtype); void adjustspeedforwater(lifeform_t *lf, int *speed); @@ -112,6 +113,7 @@ raceclass_t *findraceclass(enum RACECLASS id); skill_t *findskill(enum SKILL id); skill_t *findskillbyname(char *name); enum SKILLLEVEL findskilllevbyname(char *name); +subjob_t *findsubjob(enum SUBJOB sjid); int flee(lifeform_t *lf); void fleefrom(lifeform_t *lf, lifeform_t *enemy, int howlong, int onpurpose); int fovlist_contains(int *endx, int *endy, int nendcells, int x, int y); @@ -169,6 +171,8 @@ enum HUNGER gethungerlevel(int hunger); char *gethungername(lifeform_t *lf, enum HUNGER hunger, char *buf); int gethungerval(lifeform_t *lf); job_t *getjob(lifeform_t *lf); +enum SUBJOB getsubjob(lifeform_t *lf); +char *getjobname(lifeform_t *lf); int getlastdir(lifeform_t *lf); int getleftrightwalls(lifeform_t *lf); int getlfaccuracy(lifeform_t *lf, object_t *wep); @@ -179,6 +183,7 @@ enum SKILLLEVEL getmaxskilllevel(lifeform_t *lf, enum SKILL skid); int getminions(lifeform_t *lf, lifeform_t **minion, int *nminions); int getmiscastchance(lifeform_t *lf); int getmorale(lifeform_t *lf); +int getnextshortcut(lifeform_t *lf); int getnightvisrange(lifeform_t *lf); int getnoisedetails(lifeform_t *lf, enum NOISETYPE nid, char *heartext,char *seetext, int *volume); char *getlfconditionname(enum LFCONDITION cond); @@ -260,6 +265,7 @@ int getweapons(lifeform_t *lf, object_t **wep, flag_t **damflag, int *lastweapon enum SKILLLEVEL getweaponskill(lifeform_t *lf, object_t *o); long getxpforlev(int level); void givejob(lifeform_t *lf, enum JOB jobid); +void givesubjob(lifeform_t *lf, enum SUBJOB sj); int givemoney(lifeform_t *from, lifeform_t *to, int amt); void giveobflags(lifeform_t *lf, object_t *o, enum FLAG whattype); int giverandomobs(lifeform_t *lf, int amt); @@ -274,6 +280,7 @@ int hasfreeaction(lifeform_t *lf); int real_hasfreeaction(lifeform_t *lf, enum FLAG exception); int hashealableinjuries(lifeform_t *lf); job_t *hasjob(lifeform_t *lf, enum JOB job); +int hassubjob(lifeform_t *lf, enum SUBJOB id); void inc_quad_range(enum QUADRANT *start, enum QUADRANT *end, int howmuch); int injure(lifeform_t *lf, enum BODYPART where, enum DAMTYPE damtype); int lfcanbestoned(lifeform_t *lf); @@ -345,6 +352,7 @@ void killjob(job_t *job); void killlf(lifeform_t *lf); void killpoisontype(poisontype_t *pt); void killrace(race_t *race); +void killsubjob(subjob_t *sj); flag_t *levelabilityready(lifeform_t *lf); int loadfirearm(lifeform_t *lf, object_t *gun, object_t *ammo); int loadfirearmfast(lifeform_t *lf, int onpurpose); @@ -383,6 +391,7 @@ void practice(lifeform_t *lf, enum SKILL skid, int amt); void precalclos(lifeform_t *lf); void preparecorpse(lifeform_t *lf, object_t *corpse); int push(lifeform_t *lf, object_t *o, int dir); +int racecantalk(enum RACE rid); int readytotrain(lifeform_t *lf); int recruit(lifeform_t *lf); void refreshlevelabilities(lifeform_t *lf); @@ -433,6 +442,7 @@ int stun(lifeform_t *lf, int nturns); lifeform_t *summonmonster(lifeform_t *caster, cell_t *c, enum RACE rid, char *racename, int lifetime, int wantfriendly); //int testammo(lifeform_t *lf, object_t *o); int takeoff(lifeform_t *lf, object_t *o); +void takerotationtime(lifeform_t *lf); void taketime(lifeform_t *lf, long howlong); int throwat(lifeform_t *thrower, object_t *o, cell_t *where); void timeeffectslf(lifeform_t *lf); diff --git a/map.c b/map.c index ac32a1f..7995f9c 100644 --- a/map.c +++ b/map.c @@ -193,7 +193,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int jobok, int int db = B_FALSE; flagpile_t *wantflags = NULL; enum JOB wantjob = J_NONE; - + enum SUBJOB wantsubjob = SJ_NONE; if (nadded) *nadded = 0; @@ -220,7 +220,7 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int jobok, int } else { //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging - rid = parserace(racename, wantflags, &wantjob); + rid = parserace(racename, wantflags, &wantjob, &wantsubjob); //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging if (rid == R_RANDOM) { @@ -288,6 +288,9 @@ 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]); + } break; } } @@ -296,6 +299,9 @@ lifeform_t *addmonster(cell_t *c, enum RACE rid, char *racename, int jobok, int } else { givejob(lf, wantjob); } + if (wantsubjob != SJ_NONE) { + givesubjob(lf, wantsubjob); + } generatealignment(lf); //if (gamemode == GM_GAMESTARTED) checkallflags(player->cell->map); // debugging @@ -7166,7 +7172,7 @@ int orthdir(int compassdir) { return D_NONE; } -enum RACE parserace(char *name, flagpile_t *wantflags, enum JOB *wantjob) { +enum RACE parserace(char *name, flagpile_t *wantflags, enum JOB *wantjob, enum SUBJOB *wantsubjob) { int donesomething; char *p; job_t *j; @@ -7199,6 +7205,41 @@ enum RACE parserace(char *name, flagpile_t *wantflags, enum JOB *wantjob) { } } + if (!j) { + int foundsubjob = B_FALSE; + // search for subjob names + char *ep; + ep = strends(name, "Skymage"); + if (ep && !foundsubjob) { + if (wantjob) *wantjob = J_WIZARD; + if (wantsubjob) *wantsubjob = SJ_AIRMAGE; + *ep = '\0'; + foundsubjob = B_TRUE; + } + ep = strends(name, "Icemage"); + if (ep && !foundsubjob) { + if (wantjob) *wantjob = J_WIZARD; + if (wantsubjob) *wantsubjob = SJ_ICEMAGE; + *ep = '\0'; + foundsubjob = B_TRUE; + } + ep = strends(name, "Firemage"); + if (ep && !foundsubjob) { + if (wantjob) *wantjob = J_WIZARD; + if (wantsubjob) *wantsubjob = SJ_FIREMAGE; + *ep = '\0'; + foundsubjob = B_TRUE; + } + ep = strends(name, "Necromancer"); + if (ep && !foundsubjob) { + if (wantjob) *wantjob = J_WIZARD; + if (wantsubjob) *wantsubjob = SJ_NECROMANCER; + *ep = '\0'; + foundsubjob = B_TRUE; + } + + } + // now get raceid if (streq(p, "random")) { return R_RANDOM; diff --git a/map.h b/map.h index c9ff45a..6ecab4e 100644 --- a/map.h +++ b/map.h @@ -164,7 +164,7 @@ void markroomwalls(map_t *m, room_t *r); void mapentereffects(map_t *m); void moveobtoclearcell(object_t *o); int orthdir(int compassdir); -enum RACE parserace(char *name, flagpile_t *wantflags, enum JOB *wantjob); +enum RACE parserace(char *name, flagpile_t *wantflags, enum JOB *wantjob, enum SUBJOB *wantsubjob); int remove_deadends(map_t *m, int howmuch); void set_scanned_glyph(int targettype, void *what, char *descappend, char *desc, glyph_t *glyph); void setcellknown(cell_t *cell, int forcelev); diff --git a/move.c b/move.c index 6c4d768..172ae06 100644 --- a/move.c +++ b/move.c @@ -1447,7 +1447,7 @@ int movelf(lifeform_t *lf, cell_t *newcell) { //precalclos(l); } - if (isplayer(l)) { + if (isplayer(l)) { // player saw someone move if (areenemies(lf, l) && !isplayer(lf)) { if (!preseenbyplayer) { // TODO: also check for isresting(l), if we have allies standing watch @@ -1465,7 +1465,7 @@ int movelf(lifeform_t *lf, cell_t *newcell) { } } } - } else if (isplayer(lf)) { + } else if (isplayer(lf)) { // someone saw the player move. if (areallies(lf, l)) { // remember player's last known loc f = lfhasflag(l, F_PETOF); @@ -2703,7 +2703,10 @@ int trymove(lifeform_t *lf, int dir, int onpurpose, int strafe) { if ((reldir != RD_FORWARDS) && !lfhasflag(lf, F_AWARENESS)) { // if the given dir is behind us, just turn. if (!strafe) { + takerotationtime(lf); setfacing(lf, dir); + drawscreen(); + if (isplayer(lf)) { addflagifneeded(lf->flags, F_TURNED, B_TRUE, NA, NA, NULL); lf->turncounter++; @@ -2712,7 +2715,6 @@ int trymove(lifeform_t *lf, int dir, int onpurpose, int strafe) { addtempflag(lf->flags, F_CONFUSED, B_TRUE, NA, NA, NULL, 3); } } - taketime(lf, getturnspeed(lf)); // check for fleeing... if (isplayer(lf) && !isdead(lf)) { if (prebattle && !isinbattle(lf, B_FALSE) && onpurpose) { diff --git a/nexus.c b/nexus.c index 3f22ca2..fcb26a2 100644 --- a/nexus.c +++ b/nexus.c @@ -34,6 +34,7 @@ raceclass_t *firstraceclass = NULL,*lastraceclass = NULL; recipe_t *firstrecipe = NULL,*lastrecipe = NULL; job_t *firstjob = NULL,*lastjob = NULL; poisontype_t *firstpoisontype = NULL,*lastpoisontype = NULL; +subjob_t *firstsubjob = NULL,*lastsubjob = NULL; skill_t *firstskill = NULL,*lastskill = NULL; habitat_t *firsthabitat = NULL,*lasthabitat = NULL; map_t *firstmap = NULL,*lastmap = NULL; @@ -236,12 +237,16 @@ int main(int argc, char **argv) { if (!j) { // ask for job - initprompt(&prompt, "Select your job:"); + initprompt(&prompt, "Select your base job:"); ch = 'a'; for (j = firstjob ; j ; j = j->next) { + char *longdesc; + longdesc = malloc(HUGEBUFLEN * sizeof(char)); + makedesc_job(j, longdesc); if (!hasflag(j->flags, F_NOPLAYER)) { - addchoice(&prompt, ch++, (j->id == J_GOD) ? "Diety (for debugging)" : j->name, NULL, j, j->desc); + addchoice(&prompt, ch++, (j->id == J_GOD) ? "Diety (for debugging)" : j->name, NULL, j, longdesc); } + free(longdesc); } j = NULL; while (!j) { @@ -333,97 +338,6 @@ int main(int argc, char **argv) { // give the player their job givejob(player, j->id); - // extra choices for some jobs - if (j->id == J_DRUID) { - enum OBTYPE spell; - int i; - lifeform_t *god; - for (i = 0;i < 3; i++) { - // pick a spell which you don't already have... - spell = getrandomspellfromschool(SS_NATURE, 1); - while (cancast(player, spell, NULL)) { - spell = getrandomspellfromschool(SS_NATURE, 1); - } - // you can now cast it. - addflag(player->flags, F_CANCAST, spell, NA, NA, NULL); - } - // druids always worship ekrub - god = findgod(R_GODNATURE); - addflag(god->flags, F_PRAYEDTO, B_TRUE, NA, NA, NULL); - } else if (j->id == J_WIZARD) { - skill_t *sk; - object_t *sb1,*sb2; - initprompt(&prompt, "Select your primary spell specialty:"); - addchoice(&prompt, 'a', getskillname(SK_SS_AIR), NULL, findskill(SK_SS_AIR), NULL); - addchoice(&prompt, 'c', getskillname(SK_SS_COLD), NULL, findskill(SK_SS_COLD), NULL); - addchoice(&prompt, 'f', getskillname(SK_SS_FIRE), NULL, findskill(SK_SS_FIRE), NULL); - addchoice(&prompt, 'n', getskillname(SK_SS_DEATH), NULL, findskill(SK_SS_DEATH), NULL); - addchoice(&prompt, 'w', getskillname(SK_SS_WILD), NULL, findskill(SK_SS_WILD), NULL); - getchoice(&prompt); - sk = (skill_t *) prompt.result; - switch (sk->id) { - case SK_SS_AIR: - addflag(player->flags, F_CANSEETHROUGHMAT, MT_GAS, NA, NA, NULL); - sb1 = addob(player->pack, "spellbook of air magic"); - break; - case SK_SS_COLD: - sb1 = addob(player->pack, "spellbook of cold magic"); - break; - case SK_SS_FIRE: - sb1 = addob(player->pack, "spellbook of fire magic"); - break; - case SK_SS_DEATH: - sb1 = addob(player->pack, "spellbook of necromancy"); - break; - case SK_SS_WILD: - sb1 = addob(player->pack, "spellbook of wild magic"); - break; - default: - sb1 = NULL; - break; - } - if (sb1) { - addflag(player->flags, F_CANCAST, sb1->contents->first->type->id, NA, NA, NULL); - addflag(player->flags, F_SHORTCUT, 1, NA, NA, sb1->contents->first->type->name); - addflag(sb1->flags, F_NOPOINTS, B_TRUE, NA, NA, NULL); - } - - initprompt(&prompt, "Select your secondary spell school:"); - addchoice(&prompt, 'd', getskillname(SK_SS_DIVINATION), NULL, findskill(SK_SS_DIVINATION), NULL); - addchoice(&prompt, 'g', getskillname(SK_SS_GRAVITY), NULL, findskill(SK_SS_GRAVITY), NULL); - addchoice(&prompt, 'm', getskillname(SK_SS_MODIFICATION), NULL, findskill(SK_SS_MODIFICATION), NULL); - addchoice(&prompt, 's', getskillname(SK_SS_SUMMONING), NULL, findskill(SK_SS_SUMMONING), NULL); - addchoice(&prompt, 't', getskillname(SK_SS_TRANSLOCATION), NULL, findskill(SK_SS_TRANSLOCATION), NULL); - getchoice(&prompt); - sk = (skill_t *) prompt.result; - switch (sk->id) { - case SK_SS_DIVINATION: - sb2 = addob(player->pack, "spellbook of divination magic"); - break; - case SK_SS_GRAVITY: - sb2 = addob(player->pack, "spellbook of gravitation magic"); - break; - case SK_SS_MODIFICATION: - sb2 = addob(player->pack, "spellbook of modification magic"); - break; - case SK_SS_SUMMONING: - sb2 = addob(player->pack, "spellbook of summoning magic"); - break; - case SK_SS_TRANSLOCATION: - sb2 = addob(player->pack, "spellbook of translocation magic"); - break; - default: - sb2 = NULL; - break; - } - if (sb2) { - addflag(player->flags, F_CANCAST, sb2->contents->first->type->id, NA, NA, NULL); - addflag(player->flags, F_SHORTCUT, 2, NA, NA, sb2->contents->first->type->name); - addflag(sb2->flags, F_NOPOINTS, B_TRUE, NA, NA, NULL); - } - identify(sb1); - identify(sb2); - } ////////////////////// // read cheat info from player file @@ -468,7 +382,7 @@ int main(int argc, char **argv) { } - // more automated shortcuts + // set up automated shortcuts autoshortcut(player, OT_A_COOK); getplayernamefull(pname); diff --git a/objects.c b/objects.c index 8714181..3020f57 100644 --- a/objects.c +++ b/objects.c @@ -1267,6 +1267,8 @@ object_t *addobject(obpile_t *where, char *name, int canstack, int wantlinkholes dblog("DB: created abandoned temple"); raise(SIGINT); } + // abandoned tamples make no sound + killflagsofid(o->flags, F_MAKESNOISE); } } @@ -2439,20 +2441,28 @@ int blessob(object_t *o) { if (rv == B_FALSE) { int seen = B_FALSE; - owner = o->pile->owner; - if (owner) { - if (isplayer(owner)) { - msg("Your %s is bathed in a divine glow!", noprefix(obname)); - seen = B_TRUE; - } else if (cansee(player, owner)) { - char ownername[BUFLEN]; - msg("%s%s %s is bathed in a divine glow!", ownername, getpossessive(ownername), - noprefix(obname)); + if (gamemode == GM_GAMESTARTED) { + owner = o->pile->owner; + if (owner) { + if (isplayer(owner)) { + msg("Your %s is bathed in a divine glow!", noprefix(obname)); + seen = B_TRUE; + } else if (cansee(player, owner)) { + char ownername[BUFLEN]; + msg("%s%s %s is bathed in a divine glow!", ownername, getpossessive(ownername), + noprefix(obname)); + seen = B_TRUE; + } + } else if (haslos(player, o->pile->where)) { + msg("%s is bathed in a divine glow!", obname); + seen = B_TRUE; + } + } else { + // game not started yet. + owner = o->pile->owner; + if (owner && isplayer(owner)) { seen = B_TRUE; } - } else if (haslos(player, o->pile->where)) { - msg("%s is bathed in a divine glow!", obname); - seen = B_TRUE; } if (seen) { diff --git a/save.c b/save.c index 2ff8865..60b2818 100644 --- a/save.c +++ b/save.c @@ -212,6 +212,7 @@ lifeform_t *loadlf(FILE *f, cell_t *where) { fscanf(f, "eyeadj: %d\n",&l->eyeadjustment); fscanf(f, "facing: %d\n",&l->facing); fscanf(f, "turncounter: %d\n",&l->turncounter); + fscanf(f, "rotated: %d\n",&l->rotated); if (db) dblog("--> Got hp=%d/%d. timespend=%d. sorted=%d. Now loading flags.",l->hp,l->maxhp,l->timespent,l->sorted); @@ -830,6 +831,7 @@ int savelf(FILE *f, lifeform_t *l) { fprintf(f, "eyeadj: %d\n",l->eyeadjustment); fprintf(f, "facing: %d\n",l->facing); fprintf(f, "turncounter: %d\n",l->turncounter); + fprintf(f, "rotated: %d\n",l->rotated); // lf flags saveflagpile(f, l->flags); @@ -1154,7 +1156,7 @@ int writehiscore(lifeform_t *lf, int *rank) { j = getjob(player); strcpy(racename, player->race->name); capitalise(racename); - snprintf(jobname, BUFLEN, "Lv%d %s %s", player->level, racename, j->name); + snprintf(jobname, BUFLEN, "Lv%d %s %s", player->level, racename, getjobname(player)); makekillertext(killedby, lf->killverb, lf->lastdam, lf->cell->map, B_FALSE, B_TRUE); diff --git a/spell.c b/spell.c index 68e9104..50787c6 100644 --- a/spell.c +++ b/spell.c @@ -1400,6 +1400,13 @@ int abilityeffects(lifeform_t *user, enum OBTYPE abilid, cell_t *targcell, lifef practice(user, SK_ATHLETICS, 1); } else if (abilid == OT_A_PICKLOCK) { lockpick(user, NULL, NULL, NULL); + } else if (abilid == OT_A_REFLEXDODGE) { + // stopping? + if (killflagsofid(user->flags, F_DODGES)) { + return B_FALSE; + } + + addflag(user->flags, F_DODGES, B_TRUE, NA, NA, NULL); } else if (abilid == OT_A_RAGE) { int howlong; f = lfhasflag(user, F_RAGE); @@ -3850,7 +3857,6 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ // no corpse after death (so you can't keep reanimating it) addflag(lf->flags, F_NOCORPSE, NA, NA, NA, NULL); if (isplayer(caster)) { - pleasegodmaybe(R_GODNATURE, 25); angergodmaybe(R_GODFIRE, 50, GA_HERESY); } @@ -4365,7 +4371,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } if (ncalmed && isplayer(caster)) { - pleasegod(R_GODMERCY, ncalmed); + pleasegodmaybe(R_GODMERCY, ncalmed); } if (!donesomething) { @@ -4815,7 +4821,6 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (seenbyplayer) *seenbyplayer = B_TRUE; } if (isplayer(caster)) { - pleasegodmaybe(R_GODNATURE, 5); angergodmaybe(R_GODFIRE, 50, GA_HERESY); } } else if (spellid == OT_S_CREATEMONSTER) { @@ -5043,9 +5048,6 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (donesomething && cansee(player, target)) { if (seenbyplayer) *seenbyplayer = B_TRUE; - if (isplayer(caster) && !frompot) { - pleasegodmaybe(R_GODLIFE, 3); - } } else { fizzle(caster); } @@ -5781,7 +5783,6 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ msg("Unseen forces rip into %s%s flesh!", targetname, getpossessive(targetname)); } criticalhit(caster, target, getrandomcorebp(target, NULL), rnd(1,6), DT_SLASH); - if (isplayer(caster)) pleasegodmaybe(R_GODDEATH, 3); } else if (spellid == OT_S_GLYPHWARDING) { char buf[BUFLEN]; sprintf(buf, "^g*WARD%d*^n", power/2); @@ -5846,7 +5847,6 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (seenbyplayer) *seenbyplayer = B_TRUE; } if (isplayer(caster)) { - pleasegodmaybe(R_GODNATURE, 5); angergodmaybe(R_GODFIRE, 75, GA_HERESY); } } else { @@ -6727,9 +6727,6 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } if (donesomething && isplayer(caster)) { - if (!frompot) { - pleasegodmaybe(R_GODLIFE, 3); - } // god of death REALLY doesn't like healing. angergodmaybe(R_GODDEATH, 40, GA_HERESY); } @@ -8171,9 +8168,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } } - if (donesomething) { - if (isplayer(caster)) pleasegodmaybe(R_GODTHIEVES, 5); - } else { + if (!donesomething) { fizzle(caster); return B_TRUE; } @@ -9209,8 +9204,8 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ fizzle(caster); return B_TRUE; } - if (ndone && isplayer(caster) && !frompot) { - pleasegodmaybe(R_GODLIFE, 3); + if (!ndone) { + return B_TRUE; } } else if (spellid == OT_S_LETHARGY) { int amttolose; @@ -9836,7 +9831,6 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ nburnt++; } } - if (isplayer(caster)) pleasegodmaybe(R_GODFIRE, nburnt*2); } else if (spellid == OT_S_SPEAKDEAD) { object_t *corpse = NULL; @@ -9849,64 +9843,84 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ char buf[BUFLEN],corpsename[BUFLEN]; char ch; int done = B_FALSE; - getobname(corpse, corpsename, 1); - msg("An ghostly spirit rises from %s!", corpsename); more(); - while (!done) { - snprintf(buf, BUFLEN, "What will you ask %s?", corpsename); - initprompt(&prompt, buf); - addchoice(&prompt, 'a', "How did you die?", NULL, NULL, NULL); - addchoice(&prompt, 'b', "Tell me about this area", NULL, NULL, NULL); - addchoice(&prompt, 'c', "Are there any hidden dangers nearby?", NULL, NULL, NULL); - addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL); - prompt.maycancel = B_TRUE; + flag_t *cf; + race_t *corpserace = NULL; + cf = hasflag(corpse->flags, F_CORPSEOF); + if (cf) { + corpserace = findrace(cf->val[0]); + } + if (corpserace) { + getobname(corpse, corpsename, 1); + msg("An ghostly spirit rises from %s!", corpsename); more(); + if (racecantalk(corpserace->id)) { + // TODO: check if you speak a common language + while (!done) { + snprintf(buf, BUFLEN, "What will you ask %s?", corpsename); + initprompt(&prompt, buf); + addchoice(&prompt, 'a', "How did you die?", NULL, NULL, NULL); + addchoice(&prompt, 'b', "Tell me about this area", NULL, NULL, NULL); + addchoice(&prompt, 'c', "Are there any hidden dangers nearby?", NULL, NULL, NULL); + addchoice(&prompt, '-', "(nothing)", NULL, NULL, NULL); + prompt.maycancel = B_TRUE; - - ch = getchoice(&prompt); - if ((ch != '\0') && (ch != '-')) { - snprintf(buf, BUFLEN, "%s whispers:", corpsename); - msg(buf); - } - if (ch == 'a') { - flag_t *f; - char *p; - - f = hasflag(corpse->flags, F_CORPSEOF); - if (f && strlen(f->text)) { - char killer[BUFLEN]; - char weapon[BUFLEN]; - p = readuntil(killer, f->text, '^'); - if (strstr(p, "weilding")) { - p = readuntil(weapon, p, '^'); + + ch = getchoice(&prompt); + if ((ch != '\0') && (ch != '-')) { + snprintf(buf, BUFLEN, "%s whispers:", corpsename); + msg(buf); + } + if (ch == 'a') { + flag_t *f; + char *p; + + f = hasflag(corpse->flags, F_CORPSEOF); + if (f && strlen(f->text)) { + char killer[BUFLEN]; + char weapon[BUFLEN]; + p = readuntil(killer, f->text, '^'); + if (strstr(p, "weilding")) { + p = readuntil(weapon, p, '^'); + } else { + strcpy(weapon, ""); + } + snprintf(buf, BUFLEN, "\"I was killed by %s", killer); + if (strlen(weapon)) { + strcat(buf, ", "); + strcat(buf, weapon); + } + strcat(buf, ".\""); + msg(buf); + } else { + msg("\"I do not know what killed me.\""); + } + } else if (ch == 'b') { + genareaknowledge(corpse->flags, 50); + docomms_areainfo(corpsename, corpse->flags, NULL); + if (onein(3)) done = B_TRUE; + } else if (ch == 'c') { + genareaknowledge(corpse->flags, 50); + docomms_areadangers(corpsename, corpse->flags, NULL); + if (onein(3)) done = B_TRUE; } else { - strcpy(weapon, ""); + done = B_TRUE; } - snprintf(buf, BUFLEN, "\"I was killed by %s", killer); - if (strlen(weapon)) { - strcat(buf, ", "); - strcat(buf, weapon); - } - strcat(buf, ".\""); - msg(buf); - } else { - msg("\"I do not know what killed me.\""); - } - } else if (ch == 'b') { - genareaknowledge(corpse->flags, 50); - docomms_areainfo(corpsename, corpse->flags, NULL); - done = B_TRUE; - } else if (ch == 'c') { - genareaknowledge(corpse->flags, 50); - docomms_areadangers(corpsename, corpse->flags, NULL); - done = B_TRUE; + } // end while !done } else { - done = B_TRUE; - } - } // end while !done + // can't talk + msg("Unfortunately, the spirit doesn't seem capable of speech."); + } // end if cantalk - // destroy the corpse. - msg("%s crumbles to dust.", corpsename); - removeob(corpse, ALL); - } + // destroy the corpse. + msg("%s crumbles to dust.", corpsename); + removeob(corpse, ALL); + } else { + fizzle(caster); + return B_TRUE; + } + } else { + fizzle(caster); + return B_TRUE; + } // end if corpse } else if (spellid == OT_S_STENCH) { int howlong; @@ -10536,7 +10550,7 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ f = addtempflag(target->flags, F_NOSTRDAMMOD, B_TRUE, NA, NA, NULL, FROMSPELL); f->obfrom = spellid; } else if (spellid == OT_S_TURNUNDEAD) { - int i; + int i,ndone = 0; lifeform_t *lf; if (!target) { target = caster; @@ -10555,9 +10569,14 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (worked) { // don't use scare() since it will fail due to them being undead. addtempflag(lf->flags, F_FLEEFROM, target->id, NA, NA, NULL,howlong); + ndone++; } } } + if (!ndone) { + fizzle(caster); + return B_TRUE; + } } else if (spellid == OT_S_TWIDDLE) { target = targcell->lf; @@ -10853,7 +10872,6 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (seenbyplayer) *seenbyplayer = B_TRUE; } if (isplayer(caster)) { - pleasegodmaybe(R_GODNATURE, 5); angergodmaybe(R_GODFIRE, 50, GA_HERESY); } } else if (spellid == OT_S_WATERJET) { @@ -11256,7 +11274,6 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ if (seenbyplayer) *seenbyplayer = B_TRUE; } if (o && isplayer(caster)) { - pleasegodmaybe(R_GODNATURE, 5); angergodmaybe(R_GODFIRE, 75, GA_HERESY); } } else if ((spellid == OT_S_WISH) || (spellid == OT_S_GIFT)) { @@ -11323,7 +11340,6 @@ int dospelleffects(lifeform_t *caster, enum OBTYPE spellid, int power, lifeform_ } if (ncreated) { if (isplayer(caster)) { - pleasegodmaybe(R_GODNATURE, 10); angergodmaybe(R_GODFIRE, 75, GA_HERESY); } } @@ -11393,6 +11409,7 @@ enum OBTYPE getfirstwizspell(enum SPELLSCHOOL school) { case SS_MODIFICATION: firstspell = OT_S_OBJECTGROWTH; break; case SS_TRANSLOCATION: firstspell = OT_S_APPORTATION; break; case SS_SUMMONING: firstspell = OT_S_GLYPHWARDING; break; + case SS_LIFE: firstspell = OT_S_HEALINGMIN; break; default: case SS_WILD: firstspell = OT_S_MANASPIKE; break; } @@ -11841,27 +11858,18 @@ int getspellpower(lifeform_t *lf, enum OBTYPE spellid) { // plus your hitdice/3 power += (gethitdice(lf)/3); - // plus intelligence modifier - if (school == SS_MENTAL) { - // +/- 2 for iq - power += (getstatmod(lf, A_IQ) / 25); - } else if (school == SS_NATURE) { - // +/- 1 for wisdom - power += (getstatmod(lf, A_WIS) / 50); - } else if (school == SS_ALLOMANCY) { - // +/- 1 for strength - power += (getstatmod(lf, A_STR) / 50); - // TODO: clerical +/- 2 for wisdom - } else { - // +/- 1 for iq - power += (getstatmod(lf, A_IQ) / 50); - } + // plus stat modifier + power += getspellpowerstatmod(lf, school, NULL); - // plus any extra school levels + // plus god modifier + power += getspellpowergodmod(lf, school, NULL); + + // plus any extra school levels beyond what you need power += (schoolskill - spelllev); } else { // for monsters, just based on hitdice: - power = gethitdice(lf); + power = gethitdice(lf)/2; + limit(&power, 1, NA); } power += boost; @@ -11869,6 +11877,59 @@ int getspellpower(lifeform_t *lf, enum OBTYPE spellid) { return power; } +int getspellpowergodmod(lifeform_t *lf, enum SPELLSCHOOL school, enum RACE *godid) { + int mod = 0; + if (godid) *godid = A_NONE; + + if (!isplayer(lf)) return 0; + + if ((school == SS_LIFE) && godprayedto(R_GODLIFE)) { + int plev; + if (godid) *godid = R_GODLIFE; + plev = getpietylev(R_GODLIFE, NULL, NULL); + if (plev > 0) mod = plev; + } else if ((school == SS_DEATH) && godprayedto(R_GODDEATH)) { + int plev; + if (godid) *godid = R_GODLIFE; + plev = getpietylev(R_GODLIFE, NULL, NULL); + if (plev > 0) mod = plev; + } + + return mod; +} + +int getspellpowerstatmod(lifeform_t *lf, enum SPELLSCHOOL school, enum ATTRIB *att) { + int mod = 0; + if (att) *att = A_NONE; + + if (!isplayer(lf)) return 0; + + if (school == SS_MENTAL) { + if (att) *att = A_IQ; + // +/- 2 for iq + mod += (getstatmod(lf, A_IQ) / 25); + } else if (school == SS_NATURE) { + if (att) *att = A_WIS; + // +/- 1 for wisdom + mod += (getstatmod(lf, A_WIS) / 50); + } else if (school == SS_ALLOMANCY) { + if (att) *att = A_STR; + // +/- 1 for strength + mod += (getstatmod(lf, A_STR) / 50); + } else if (school == SS_LIFE) { + // ie. clerical. + // +/- 2 for wisdom + if (att) *att = A_WIS; + mod += (getstatmod(lf, A_WIS) / 25); + } else { + if (att) *att = A_IQ; + // +/- 1 for iq + mod += (getstatmod(lf, A_IQ) / 50); + } + + return mod; +} + enum SPELLSCHOOL getspellschool(enum OBTYPE spellid) { flag_t *f; objecttype_t *ot; diff --git a/spell.h b/spell.h index a37c779..4fa2b66 100644 --- a/spell.h +++ b/spell.h @@ -23,6 +23,8 @@ int getspelllevel(enum OBTYPE spellid); int getspellmaxpower(enum OBTYPE spellid); char *getspellname(enum OBTYPE spellid, lifeform_t *lf, char *buf, int forcepower); int getspellpower(lifeform_t *lf, enum OBTYPE spellid); +int getspellpowergodmod(lifeform_t *lf, enum SPELLSCHOOL school, enum RACE *godid); +int getspellpowerstatmod(lifeform_t *lf, enum SPELLSCHOOL school, enum ATTRIB *att); enum SPELLSCHOOL getspellschool(enum OBTYPE spellid); enum SPELLSCHOOL getspellschoolknown(lifeform_t *lf, enum OBTYPE spellid); enum SKILLLEVEL getspellskill(lifeform_t *lf, enum OBTYPE spellid); diff --git a/text.c b/text.c index d32892f..8032af8 100644 --- a/text.c +++ b/text.c @@ -955,34 +955,34 @@ char *getobmodprefix(object_t *o, obmod_t *om) { } } } else if (isweapon(o)) { - flag_t *f = NULL; skill_t *sk; sk = getobskill(o->flags); if (sk) { - if (om->id == OM_MASTERWORK) { - switch (sk->id) { - case SK_CLUBS: - case SK_STAVES: - return "reinforced "; - default: break; - } - } else if (om->id == OM_SHODDY) { - switch (sk->id) { - case SK_LONGBLADES: - case SK_SHORTBLADES: - case SK_AXES: - return "blunted "; - case SK_CLUBS: - return "cracked "; - case SK_POLEARMS: - return "notched "; - case SK_STAVES: - return "blunted "; - default: break; + if (!player || (getskill(player, sk->id) >= PR_BEGINNER) || (getskill(player, SK_PERCEPTION) >= PR_BEGINNER)) { + if (om->id == OM_MASTERWORK) { + switch (sk->id) { + case SK_CLUBS: + case SK_STAVES: + return "reinforced "; + default: break; + } + } else if (om->id == OM_SHODDY) { + switch (sk->id) { + case SK_LONGBLADES: + case SK_SHORTBLADES: + case SK_AXES: + return "blunted "; + case SK_CLUBS: + return "cracked "; + case SK_POLEARMS: + return "notched "; + case SK_STAVES: + return "blunted "; + default: break; + } } } } - if (f) return f->text; } else if (isshield(o)) { if (om->id == OM_MASTERWORK) { switch (o->material->id) { diff --git a/vault.c b/vault.c index e3a1948..bfe5985 100644 --- a/vault.c +++ b/vault.c @@ -1661,7 +1661,7 @@ int vaultthingok(enum VAULTTHING vt, char *what) { if (r) { return B_TRUE; } else { - if (parserace(what, NULL, NULL)) { + if (parserace(what, NULL, NULL, NULL)) { return B_TRUE; } }