diff options
33 files changed, 849 insertions, 616 deletions
diff --git a/crawl-ref/source/Kills.cc b/crawl-ref/source/Kills.cc index 02c3decb20..380f4c33fd 100644 --- a/crawl-ref/source/Kills.cc +++ b/crawl-ref/source/Kills.cc @@ -14,6 +14,7 @@ #include "mon-util.h" #include "monstuff.h" #include "files.h" +#include "ghost.h" #include "itemname.h" #include "place.h" #include "travel.h" diff --git a/crawl-ref/source/acr.cc b/crawl-ref/source/acr.cc index 89e6d0aad1..a5c7e4ed7b 100644 --- a/crawl-ref/source/acr.cc +++ b/crawl-ref/source/acr.cc @@ -974,7 +974,7 @@ static void handle_wizard_command( void ) if (testbits(env.level_flags, LFLAG_NOT_MAPPABLE) || testbits(get_branch_flags(), BFLAG_NOT_MAPPABLE)) { - if (!yesno("Force level to be mappable? ")) + if (!yesno("Force level to be mappable? ", true, 'n')) { canned_msg( MSG_OK ); return; @@ -1069,7 +1069,7 @@ static bool recharge_rod( item_def &rod, bool wielded ) const int charge = rod.plus / ROD_CHARGE_MULT; int rate = ((charge + 1) * ROD_CHARGE_MULT) / 10; - + rate *= (10 + skill_bump( SK_EVOCATIONS )); rate = div_rand_round( rate, 100 ); @@ -1240,7 +1240,7 @@ static bool cmd_is_repeatable(command_type cmd, bool is_again = false) case CMD_MOVE_DOWN_RIGHT: if (!i_feel_safe()) return yesno("Really repeat movement command while monsters " - "are nearby?"); + "are nearby?", false, 'n'); return true; diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc index 15d3f68a85..29719a3886 100644 --- a/crawl-ref/source/beam.cc +++ b/crawl-ref/source/beam.cc @@ -39,6 +39,7 @@ #include "delay.h" #include "effects.h" #include "enum.h" +#include "fight.h" #include "item_use.h" #include "it_use2.h" #include "items.h" @@ -1591,28 +1592,28 @@ int mons_adjust_flavoured( monsters *monster, bolt &pbolt, // if we're not doing flavored effects, must be preliminary // damage check only; do not print messages or apply any side // effects! - int resist; + int resist = 0; + int original = hurted; switch (pbolt.flavour) { case BEAM_FIRE: case BEAM_STEAM: - resist = mons_res_fire(monster); - if (resist > 1) + hurted = resist_adjust_damage(monster, + monster->res_fire(), + hurted, + true); + if (!hurted) { if (doFlavouredEffects) simple_monster_message(monster, " appears unharmed."); - - hurted = 0; } - else if (resist == 1) + else if (original > hurted) { if (doFlavouredEffects) simple_monster_message(monster, " resists."); - - hurted /= 3; } - else if (resist < 0) + else if (original < hurted) { if (mons_is_icy(monster)) { @@ -1624,75 +1625,70 @@ int mons_adjust_flavoured( monsters *monster, bolt &pbolt, if (doFlavouredEffects) simple_monster_message(monster, " is burned terribly!"); } - - hurted *= 15; - hurted /= 10; } break; - case BEAM_COLD: - resist = mons_res_cold(monster); - if (resist > 1) + hurted = resist_adjust_damage(monster, monster->res_cold(), + hurted, true); + if (!hurted) { if (doFlavouredEffects) simple_monster_message(monster, " appears unharmed."); - - hurted = 0; } - else if (resist == 1) + else if (original > hurted) { if (doFlavouredEffects) simple_monster_message(monster, " resists."); - - hurted /= 3; } - else if (resist < 0) + else if (original < hurted) { if (doFlavouredEffects) simple_monster_message(monster, " is frozen!"); - - hurted *= 15; - hurted /= 10; } break; case BEAM_ELECTRICITY: - if (mons_res_elec(monster) > 0) + hurted = resist_adjust_damage(monster, monster->res_elec(), + hurted, true); + if (!hurted) { if (doFlavouredEffects) simple_monster_message(monster, " appears unharmed."); - - hurted = 0; } break; case BEAM_ACID: - if (mons_res_acid(monster)) + hurted = resist_adjust_damage(monster, mons_res_acid(monster), + hurted, true); + if (!hurted) { if (doFlavouredEffects) simple_monster_message(monster, " appears unharmed."); - - hurted = 0; } break; case BEAM_POISON: - if (mons_res_poison(monster) > 0) + { + int res = mons_res_poison(monster); + hurted = resist_adjust_damage(monster, res, hurted, true); + if (!hurted) { if (doFlavouredEffects) simple_monster_message( monster, " appears unharmed." ); - - hurted = 0; } - else if (doFlavouredEffects && !one_chance_in(3)) + else if (res <= 0 && doFlavouredEffects && !one_chance_in(3)) { poison_monster( monster, whose_kill(pbolt) ); } break; + } case BEAM_POISON_ARROW: - if (mons_res_poison(monster) > 0) + hurted = resist_adjust_damage(monster, + std::min(mons_res_poison(monster), 1), + hurted); + if (hurted < original) { if (doFlavouredEffects) { @@ -1703,8 +1699,6 @@ int mons_adjust_flavoured( monsters *monster, bolt &pbolt, if (mons_has_lifeforce(monster)) poison_monster( monster, whose_kill(pbolt), 2, true ); } - - hurted /= 2; } else if (doFlavouredEffects) { @@ -1730,7 +1724,8 @@ int mons_adjust_flavoured( monsters *monster, bolt &pbolt, pbolt.obvious_effect = true; if (YOU_KILL(pbolt.thrower)) - did_god_conduct(DID_NECROMANCY, 2 + random2(3), pbolt.effect_known); + did_god_conduct(DID_NECROMANCY, 2 + random2(3), + pbolt.effect_known); if (one_chance_in(5)) { @@ -2237,12 +2232,12 @@ void sticky_flame_monster( int mn, kill_category who, int levels ) if (!monster->alive()) return; - if (mons_res_fire(monster) > 0) + if (mons_res_sticky_flame(monster)) return; if (monster->add_ench(mon_enchant(ENCH_STICKY_FLAME, levels, who))) simple_monster_message(monster, " is covered in liquid fire!"); -} // end sticky_flame_monster +} /* * Used by monsters in "planning" which spell to cast. Fires off a "tracer" diff --git a/crawl-ref/source/dat/altar.des b/crawl-ref/source/dat/altar.des index 2fb39ee218..440960e7b6 100644 --- a/crawl-ref/source/dat/altar.des +++ b/crawl-ref/source/dat/altar.des @@ -20,7 +20,6 @@ TAGS: allow_dup CHANCE: 20 MAP cccccccccc -cccccccccc cBcBcBcBcc G.c.c.c.Bc @.......Bc @@ -28,7 +27,6 @@ G.c.c.c.Bc G.c.c.c.Bc cBcBcBcBcc cccccccccc -cccccccccc ENDMAP NAME: jmf_multi_god_temple @@ -124,15 +122,15 @@ TAGS: no_monster_gen TAGS: no_monster_gen no_pool_fixup : end MAP - ......... - ...wwwww... -..wwwwwwwww.. -.wwwwwwwwwww.. -wwwwwwwwwwwww. -wwwwwwCwwwwww. -wwwwwwwwwwwww. -.wwwwwwwwwww.. -..wwwwwwwww.. + ......... + ...wwwww... + ..wwwwwwwww.. +..wwwwwwwwwww.. +.wwwwwwwwwwwww. +.wwwwwwCwwwwww. +.wwwwwwwwwwwww. +..wwwwwwwwwww.. + .wwwwwwwww.. ...wwwww... ........ ENDMAP @@ -228,14 +226,19 @@ MAP ENDMAP NAME: lemuel_statue_altar -DEPTH: 2-18, !Lair, !Hive, !Slime + +# Increased depth from 2-18 because getting a hostile statue can +# instakill a low-level character. + +DEPTH: 10-18, !Lair, !Hive, !Slime + MAP ..... .cFc. .c.c. .c.c. .c.c. -.c.c. +.c>c. .cCc. .ccc. ..... @@ -249,7 +252,7 @@ MAP xxxxxxxxxxxxxxxxxxxx ...................x c...c...c...c...c..x -..................Cx +@.................Cx c...c...c...c...c..x ...................x xxxxxxxxxxxxxxxxxxxx @@ -281,20 +284,25 @@ ENDMAP ###################################### NAME: lemuel_angel_altar -DEPTH: 2-18, !Lair, !Orc, !Hive, !Slime + +# Moved deeper since being teleported next to the Angel will be a +# quick death at shallow levels. + +DEPTH: 9-18, !Lair, !Orc, !Hive, !Slime MONS: angel KFEAT: C = altar_elyvilon / altar_zin / altar_shining_one MAP ..... ..xmx.. - ..xx.xx.. + ..xx>xx.. ..xxx.xxx.. .xxxx1xxxx. -.m...C...m. +.m>..C..>m. .xxxx.xxxx. ..xxx.xxx.. - ..xx.xx.. - ..xmx.. + ..xx>xx.. + ..xmx.. + ..... ENDMAP NAME: lemuel_hellish_altar @@ -340,13 +348,13 @@ ENDMAP NAME: lemuel_blue_sif_altar DEPTH: 2-18, !Lair, !Hive, !Slime, !Orc -TAGS: no_monster_gen +TAGS: no_monster_gen mini_float COLOUR: . = blue KFEAT: C = altar_sif_muna MAP xxxxxxxxxxxxxx ...........xxx -...........+Cx +@..........+Cx ...........xxx xxxxxxxxxxxxxx ENDMAP @@ -361,7 +369,7 @@ MAP xxxxxxxxxxxxxxxx xxxx11111111xxxx xxxxwwwwwwwwxxxx -..............Cx +@.............Cx xxxxwwwwwwwwxxxx xxxx11111111xxxx xxxxxxxxxxxxxxxx diff --git a/crawl-ref/source/debug.cc b/crawl-ref/source/debug.cc index df323e2c23..a0eac1d559 100644 --- a/crawl-ref/source/debug.cc +++ b/crawl-ref/source/debug.cc @@ -48,6 +48,7 @@ #include "fight.h" #include "files.h" #include "food.h" +#include "ghost.h" #ifdef DEBUG_DIAGNOSTICS #include "initfile.h" @@ -469,7 +470,6 @@ void create_spec_monster_name(int x, int y) ghost_demon ghost; ghost.name = "John Doe"; - ghost.values.init(0); char class_str[80]; mpr( "Make player ghost which class? ", MSGCH_PROMPT ); @@ -485,7 +485,7 @@ void create_spec_monster_name(int x, int y) mpr("No such class, making it a Fighter."); class_id = JOB_FIGHTER; } - ghost.values[GVAL_CLASS] = class_id; + ghost.job = static_cast<job_type>(class_id); mon.set_ghost(ghost); @@ -1412,7 +1412,7 @@ void stethoscope(int mwh) // print behaviour information - const habitat_type hab = mons_habitat( menv[i].type ); + const habitat_type hab = mons_habitat( &menv[i] ); mprf(MSGCH_DIAGNOSTICS, "hab=%s beh=%s(%d) foe=%s(%d) mem=%d target=(%d,%d)", @@ -1453,8 +1453,8 @@ void stethoscope(int mwh) ASSERT(menv[i].ghost.get()); const ghost_demon &ghost = *menv[i].ghost; mprf( MSGCH_DIAGNOSTICS, - "Ghost damage: %d; brand: %d", - ghost.values[ GVAL_DAMAGE ], ghost.values[ GVAL_BRAND ] ); + "Ghost damage: %d; brand: %d", + ghost.damage, ghost.brand ); } } // end stethoscope() #endif @@ -2295,14 +2295,14 @@ bool debug_add_mutation(void) else msg = "You are resistant to mutations, remove resistance?"; - if (yesno(msg)) + if (yesno(msg, true, 'n')) { you.mutation[MUT_MUTATION_RESISTANCE] = 0; crawl_state.cancel_cmd_repeat(); } } - bool force = yesno("Force mutation to happen?"); + bool force = yesno("Force mutation to happen?", true, 'n'); if (you.mutation[MUT_MUTATION_RESISTANCE] == 3 && !force) { @@ -3262,7 +3262,7 @@ static void debug_load_map_by_name(std::string name) std::string prompt = "Only match is '"; prompt += matches[0]; prompt += "', use that?"; - if (!yesno(prompt.c_str())) + if (!yesno(prompt.c_str(), true, 'y')) return; map = find_map_by_name(matches[0]); diff --git a/crawl-ref/source/decks.cc b/crawl-ref/source/decks.cc index a37728d045..6ed7a2f148 100644 --- a/crawl-ref/source/decks.cc +++ b/crawl-ref/source/decks.cc @@ -683,7 +683,7 @@ static bool check_buggy_deck(item_def& deck) you.wield_change = true; if (!yesno("Problems might not have been completely fixed; " - "still use deck?")) + "still use deck?", true, 'n')) { crawl_state.zero_turns_taken(); return true; diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc index 72382883b4..b53d77ac3b 100644 --- a/crawl-ref/source/describe.cc +++ b/crawl-ref/source/describe.cc @@ -40,6 +40,7 @@ #include "decks.h" #include "fight.h" #include "food.h" +#include "ghost.h" #include "it_use2.h" #include "itemname.h" #include "itemprop.h" @@ -589,17 +590,17 @@ static std::string describe_demon(const monsters &mons) description << "A powerful demon, " << ghost.name << " has a" << RANDOM_ELEMENT(body_descs) << "body"; - switch (ghost.values[GVAL_DEMONLORD_FLY]) + switch (ghost.fly) { - case 1: + case FL_FLY: description << RANDOM_ELEMENT(wing_names); break; - case 2: // levitation + case FL_LEVITATE: description << RANDOM_ELEMENT(lev_names); break; - default: // does not fly + case FL_NONE: // does not fly if ( !one_chance_in(4) ) description << RANDOM_ELEMENT(nonfly_names); break; @@ -2171,8 +2172,7 @@ std::string ghost_description(const monsters &mons, bool concise) const ghost_demon &ghost = *(mons.ghost); - const species_type gspecies = - static_cast<species_type>(ghost.values[GVAL_SPECIES]); + const species_type gspecies = ghost.species; // We're fudging stats so that unarmed combat gets based off // of the ghost's species, not the player's stats... exact @@ -2207,28 +2207,28 @@ std::string ghost_description(const monsters &mons, bool concise) } gstr << ghost.name << " the " - << skill_title( ghost.values[GVAL_BEST_SKILL], - ghost.values[GVAL_SKILL_LEVEL], + << skill_title( ghost.best_skill, + ghost.best_skill_level, gspecies, str, dex, GOD_NO_GOD ) << ", a" - << ((ghost.values[GVAL_EXP_LEVEL] < 4) ? " weakling" : - (ghost.values[GVAL_EXP_LEVEL] < 7) ? "n average" : - (ghost.values[GVAL_EXP_LEVEL] < 11) ? "n experienced" : - (ghost.values[GVAL_EXP_LEVEL] < 16) ? " powerful" : - (ghost.values[GVAL_EXP_LEVEL] < 22) ? " mighty" : - (ghost.values[GVAL_EXP_LEVEL] < 26) ? " great" : - (ghost.values[GVAL_EXP_LEVEL] < 27) ? "n awesomely powerful" - : " legendary") + << ((ghost.xl < 4) ? " weakling" : + (ghost.xl < 7) ? "n average" : + (ghost.xl < 11) ? "n experienced" : + (ghost.xl < 16) ? " powerful" : + (ghost.xl < 22) ? " mighty" : + (ghost.xl < 26) ? " great" : + (ghost.xl < 27) ? "n awesomely powerful" + : " legendary") << " "; if ( concise ) gstr << get_species_abbrev(gspecies) - << get_class_abbrev(ghost.values[GVAL_CLASS]); + << get_class_abbrev(ghost.job); else gstr << species_name(gspecies, - ghost.values[GVAL_EXP_LEVEL]) + ghost.xl) << " " - << get_class_name(ghost.values[GVAL_CLASS]); + << get_class_name(ghost.job); return gstr.str(); } diff --git a/crawl-ref/source/direct.cc b/crawl-ref/source/direct.cc index 8430d326fb..5e77792713 100644 --- a/crawl-ref/source/direct.cc +++ b/crawl-ref/source/direct.cc @@ -872,7 +872,7 @@ void direction(dist& moves, targeting_type restricts, } // Ask for confirmation if we're quitting for some odd reason else if ( moves.isValid || moves.isCancel || - yesno("Are you sure you want to fizzle?") ) + yesno("Are you sure you want to fizzle?", false, 'n') ) { // Finalize whatever is inside the loop // (moves-internal finalizations can be done later) diff --git a/crawl-ref/source/dungeon.cc b/crawl-ref/source/dungeon.cc index d2b0331ca1..566ba7f665 100644 --- a/crawl-ref/source/dungeon.cc +++ b/crawl-ref/source/dungeon.cc @@ -3384,13 +3384,9 @@ static bool build_minivaults(int level_number, int force_vault, bool building_level, bool clobber, bool make_no_exits, const coord_def &where) { - // for some weird reason can't put a vault on level 1, because monster equip - // isn't generated. int altar_count = 0; FixedVector < object_class_type, 7 > acq_item_class; - // hack - passing chars through '...' promotes them to ints, which - // barfs under gcc in fixvec.h. So don't. acq_item_class[0] = OBJ_WEAPONS; acq_item_class[1] = OBJ_ARMOUR; acq_item_class[2] = OBJ_WEAPONS; @@ -4329,7 +4325,7 @@ bool dgn_place_monster(mons_spec &mspec, if (mons_is_unique(mid) && you.unique_creatures[mid]) return (false); - const habitat_type habitat = mons_habitat(mid); + const habitat_type habitat = mons_habitat_by_type(mid); if (habitat != HT_LAND) grd[vx][vy] = habitat2grid(habitat); } diff --git a/crawl-ref/source/effects.cc b/crawl-ref/source/effects.cc index 4658ddb393..5d0a72054e 100644 --- a/crawl-ref/source/effects.cc +++ b/crawl-ref/source/effects.cc @@ -1689,6 +1689,18 @@ bool recharge_wand(void) return (true); } // end recharge_wand() +// Sets foe target of friendly monsters. +static void set_friendly_foes() +{ + for (int i = 0; i < MAX_MONSTERS; ++i) + { + monsters *mon(&menv[i]); + if (!mon->alive() || !mons_near(mon) || !mons_friendly(mon)) + continue; + mon->foe = you.pet_target; + } +} + void yell(bool force) { bool targ_prev = false; @@ -1723,10 +1735,12 @@ void yell(bool force) if (force) { if (shout_verb == "__NONE" || you.paralysed()) - mprf("You feel a strong urge to %s, but you are unable to make a sound!", + mprf("You feel a strong urge to %s, but " + "you are unable to make a sound!", shout_verb == "__NONE" ? "scream" : shout_verb.c_str()); else - mprf("You feel a %s rip itself from your throat, but you make no sound!", + mprf("You feel a %s rip itself from your throat, " + "but you make no sound!", shout_verb.c_str()); } else @@ -1751,9 +1765,9 @@ void yell(bool force) if (!(you.prev_targ == MHITNOT || you.prev_targ == MHITYOU)) { - struct monsters *target = &menv[you.prev_targ]; - - if (mons_near(target) && player_monster_visible(target)) + monsters *target = &menv[you.prev_targ]; + if (target->alive() && mons_near(target) + && player_monster_visible(target)) { mpr(" p - Order allies to attack your previous target"); targ_prev = true; @@ -1818,7 +1832,11 @@ void yell(bool force) return; } - you.pet_target = mons_targd; + if (mons_targd != MHITNOT) + { + you.pet_target = mons_targd; + set_friendly_foes(); + } noisy( 10, you.x_pos, you.y_pos ); mpr("Attack!"); @@ -2452,7 +2470,7 @@ void update_level( double elapsedTime ) continue; // Don't move non-land or stationary monsters around - if (mons_habitat( mon->type ) != HT_LAND + if (mons_habitat( mon ) != HT_LAND || mons_is_stationary( mon )) { continue; diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index 28369c77ac..476bd92e88 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -1180,39 +1180,6 @@ enum flush_reason_type NUM_FLUSH_REASONS }; -enum ghost_value_type -{ - GVAL_MAX_HP, // 0 - GVAL_EV, - GVAL_AC, - GVAL_SEE_INVIS, - GVAL_RES_FIRE, - GVAL_RES_COLD, // 5 - GVAL_RES_ELEC, - GVAL_DAMAGE, - GVAL_BRAND, - GVAL_SPECIES, - GVAL_BEST_SKILL, // 10 - GVAL_SKILL_LEVEL, - GVAL_EXP_LEVEL, - GVAL_CLASS, - GVAL_SPELL_1, // 14 - GVAL_SPELL_2, - GVAL_SPELL_3, - GVAL_SPELL_4, - GVAL_SPELL_5, - GVAL_SPELL_6, // 19 - GVAL_SPEED, - NUM_GHOST_VALUES, // should always be last value - - // these values are for demonlords, which override the above: - GVAL_DEMONLORD_SPELLCASTER = 9, - GVAL_DEMONLORD_FLY, // 10 - GVAL_DEMONLORD_UNUSED, // 11 - GVAL_DEMONLORD_HIT_DICE, // 12 - GVAL_DEMONLORD_CYCLE_COLOUR // 13 -}; - // The order of this enum must match the order of DNGN_ALTAR_FOO. enum god_type { diff --git a/crawl-ref/source/externs.h b/crawl-ref/source/externs.h index f936981c99..dd382f2aa4 100644 --- a/crawl-ref/source/externs.h +++ b/crawl-ref/source/externs.h @@ -93,6 +93,8 @@ public: virtual bool submerged() const = 0; virtual bool floundering() const = 0; + virtual int get_experience_level() const = 0; + virtual bool can_pass_through(const dungeon_feature_type grid) const = 0; virtual bool can_pass_through(const int x, const int y) const = 0; virtual bool can_pass_through(const coord_def &c) const = 0; @@ -107,6 +109,11 @@ public: virtual item_def *weapon(int which_attack = -1) = 0; virtual item_def *shield() = 0; virtual item_def *slot_item(equipment_type eq) = 0; + virtual const item_def *slot_item(equipment_type eq) const + { + return const_cast<actor*>(this)->slot_item(eq); + } + virtual bool has_equipped(equipment_type eq, int sub_type) const; virtual int hunger_level() const { return HS_ENGORGED; } virtual void make_hungry(int nutrition, bool silent = true) @@ -175,6 +182,7 @@ public: virtual mon_holy_type holiness() const = 0; virtual int res_fire() const = 0; + virtual int res_steam() const = 0; virtual int res_cold() const = 0; virtual int res_elec() const = 0; virtual int res_poison() const = 0; @@ -784,16 +792,16 @@ public: size_type transform_size(int psize = PSIZE_TORSO) const; std::string shout_verb() const; - const item_def *slot_item(equipment_type eq) const; item_def *slot_item(equipment_type eq); // actor int id() const; + int get_experience_level() const; actor_type atype() const { return ACT_PLAYER; } god_type deity() const; bool alive() const; - + coord_def pos() const; bool swimming() const; bool submerged() const; @@ -849,6 +857,7 @@ public: mon_holy_type holiness() const; int res_fire() const; + int res_steam() const; int res_cold() const; int res_elec() const; int res_poison() const; @@ -984,6 +993,7 @@ class monsters : public actor public: monsters(); monsters(const monsters &other); + ~monsters(); monsters &operator = (const monsters &other); @@ -1076,6 +1086,7 @@ public: // actor interface int id() const; + int get_experience_level() const; god_type deity() const; bool alive() const; coord_def pos() const; @@ -1146,6 +1157,7 @@ public: mon_holy_type holiness() const; int res_fire() const; + int res_steam() const; int res_cold() const; int res_elec() const; int res_poison() const; @@ -1379,34 +1391,6 @@ public: extern struct crawl_environment env; -struct ghost_demon -{ -public: - std::string name; - FixedVector< short, NUM_GHOST_VALUES > values; - -public: - ghost_demon(); - void reset(); - void init_random_demon(); - void init_player_ghost(); - -public: - static std::vector<ghost_demon> find_ghosts(); - -private: - static int n_extra_ghosts(); - static void find_extra_ghosts(std::vector<ghost_demon> &ghosts, int n); - static void find_transiting_ghosts(std::vector<ghost_demon> &gs, int n); - static void announce_ghost(const ghost_demon &g); - -private: - void add_spells(); - int translate_spell(int playerspell) const; -}; - -extern std::vector<ghost_demon> ghosts; - struct message_filter { int channel; // Use -1 to match any channel. diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc index 09fa35a48f..632a606213 100644 --- a/crawl-ref/source/fight.cc +++ b/crawl-ref/source/fight.cc @@ -1654,17 +1654,16 @@ bool melee_attack::player_monattk_hit_effects(bool mondied) return (false); } -int melee_attack::resist_adjust_damage(int res, int rawdamage) +int resist_adjust_damage(actor *defender, int res, int rawdamage, + bool ranged) { + if (defender->atype() == ACT_MONSTER && res >= 3) + return (0); + if (res > 0) - { - if (defender->atype() == ACT_MONSTER) - rawdamage = 0; - else - rawdamage /= 1 + res * res; - } + rawdamage /= 1 + res * res; else if (res < 0) - rawdamage *= 2; + rawdamage = rawdamage * (ranged? 15 : 20) / 10; if (rawdamage < 0) rawdamage = 0; @@ -1676,7 +1675,8 @@ void melee_attack::calc_elemental_brand_damage( int res, const char *verb) { - special_damage = resist_adjust_damage(res, random2(damage_done) / 2 + 1); + special_damage = + resist_adjust_damage(defender, res, random2(damage_done) / 2 + 1); if (special_damage > 0 && verb && needs_message) { @@ -2075,28 +2075,32 @@ bool melee_attack::apply_damage_brand() case SPWPN_CONFUSE: { - // FIXME: Generalise. - if (defender->atype() != ACT_MONSTER || attacker->atype() != ACT_PLAYER) - break; - emit_nodmg_hit_message(); // FIXME Currently Confusing Touch is the *only* way to get // here. Generalise. const int hdcheck = (defender->holiness() == MH_NATURAL? random2(30) : random2(22)); - if (hdcheck >= def->hit_dice) + if (hdcheck >= defender->get_experience_level()) { // declaring these just to pass to the enchant function bolt beam_temp; - beam_temp.thrower = KILL_YOU; - beam_temp.flavour = BEAM_CONFUSION; + beam_temp.thrower = + attacker->atype() == ACT_PLAYER? KILL_YOU : KILL_MON; + beam_temp.flavour = BEAM_CONFUSION; + beam_temp.beam_source = + attacker->atype() == ACT_PLAYER? MHITYOU + : monster_index(atk); mons_ench_f2( def, beam_temp ); } - you.duration[DUR_CONFUSING_TOUCH] -= roll_dice(3, 5); + + if (attacker->atype() == ACT_PLAYER) + { + you.duration[DUR_CONFUSING_TOUCH] -= roll_dice(3, 5); - if (you.duration[DUR_CONFUSING_TOUCH] < 1) - you.duration[DUR_CONFUSING_TOUCH] = 1; + if (you.duration[DUR_CONFUSING_TOUCH] < 1) + you.duration[DUR_CONFUSING_TOUCH] = 1; + } break; } } @@ -2231,7 +2235,6 @@ void melee_attack::player_apply_staff_damage() return; const int staff_cost = 2; - if (you.magic_points < staff_cost || random2(15) > you.skills[SK_EVOCATIONS]) { @@ -2244,10 +2247,10 @@ void melee_attack::player_apply_staff_damage() if (damage_done + you.skills[SK_AIR_MAGIC] <= random2(20)) break; - if (mons_res_elec(def)) - break; - - special_damage = player_staff_damage(SK_AIR_MAGIC); + special_damage = + resist_adjust_damage(defender, + defender->res_elec(), + player_staff_damage(SK_AIR_MAGIC)); if (special_damage) special_damage_message = @@ -2257,13 +2260,10 @@ void melee_attack::player_apply_staff_damage() break; case STAFF_COLD: - if (mons_res_cold(def) > 0) - break; - - special_damage = player_staff_damage(SK_ICE_MAGIC); - - if (mons_res_cold(def) < 0) - special_damage += player_staff_damage(SK_ICE_MAGIC); + special_damage = + resist_adjust_damage(defender, + defender->res_cold(), + player_staff_damage(SK_ICE_MAGIC)); if (special_damage) { @@ -2287,13 +2287,10 @@ void melee_attack::player_apply_staff_damage() break; case STAFF_FIRE: - if (mons_res_fire(def) > 0) - break; - - special_damage = player_staff_damage(SK_FIRE_MAGIC); - - if (mons_res_fire(def) < 0) - special_damage += player_staff_damage(SK_FIRE_MAGIC); + special_damage = + resist_adjust_damage(defender, + defender->res_fire(), + player_staff_damage(SK_FIRE_MAGIC)); if (special_damage) { @@ -2357,8 +2354,6 @@ void melee_attack::player_apply_staff_damage() if (special_damage > 0) { - dec_mp(staff_cost); - if (!item_type_known(*weapon)) { set_ident_flags( *weapon, ISFLAG_KNOW_TYPE ); @@ -3279,7 +3274,8 @@ void melee_attack::mons_apply_attack_flavour(const mon_attack_def &attk) atk->hit_points = -10; special_damage = - resist_adjust_damage(defender->res_fire(), + resist_adjust_damage(defender, + defender->res_fire(), atk->hit_dice + random2(atk->hit_dice)); if (needs_message && special_damage) mprf("%s %s engulfed in flames%s", @@ -3292,7 +3288,8 @@ void melee_attack::mons_apply_attack_flavour(const mon_attack_def &attk) case AF_COLD: special_damage = - resist_adjust_damage(defender->res_cold(), + resist_adjust_damage(defender, + defender->res_cold(), atk->hit_dice + random2( 2 * atk->hit_dice )); if (needs_message && special_damage) mprf("%s %s %s!", @@ -3304,6 +3301,7 @@ void melee_attack::mons_apply_attack_flavour(const mon_attack_def &attk) case AF_ELEC: special_damage = resist_adjust_damage( + defender, defender->res_elec(), atk->hit_dice + random2( atk->hit_dice / 2 )); diff --git a/crawl-ref/source/fight.h b/crawl-ref/source/fight.h index 03984b03b9..1a62c70b41 100644 --- a/crawl-ref/source/fight.h +++ b/crawl-ref/source/fight.h @@ -36,6 +36,9 @@ struct mon_attack_def; * *********************************************************************** */ int effective_stat_bonus( int wepType = -1 ); +int resist_adjust_damage(actor *defender, int res, int rawdamage, + bool ranged = false); + // added Sept 18, 2000 -- bwr /* *********************************************************************** * called from: describe.cc @@ -164,7 +167,6 @@ private: bool attack_shield_blocked(bool verbose); bool apply_damage_brand(); void calc_elemental_brand_damage(int res, const char *verb); - int resist_adjust_damage(int res, int rawdamage); int fire_res_apply_cerebov_downgrade(int res); void drain_defender(); void drain_player(); diff --git a/crawl-ref/source/files.cc b/crawl-ref/source/files.cc index 78d8edb5ef..932c0a6a2c 100644 --- a/crawl-ref/source/files.cc +++ b/crawl-ref/source/files.cc @@ -63,6 +63,7 @@ #include "direct.h" #include "dungeon.h" #include "effects.h" +#include "ghost.h" #include "initfile.h" #include "itemname.h" #include "itemprop.h" @@ -107,6 +108,8 @@ void save_level(int level_saved, level_area_type lt, #define LEVEL_MINOR_VERSION 1 #define YOU_MINOR_VERSION 1 +const short GHOST_SIGNATURE = static_cast<short>( 0xDC55 ); + static void redraw_all(void) { you.redraw_hit_points = 1; @@ -601,8 +604,41 @@ std::string make_filename( const char *prefix, int level, branch_type where, isGhost ); } +static void write_version( FILE *dataFile, int majorVersion, int minorVersion, + bool extended_version ) +{ + // write version + tagHeader versionTag; + versionTag.offset = 0; + versionTag.tagID = TAG_VERSION; + + marshallByte(versionTag, majorVersion); + marshallByte(versionTag, minorVersion); + + // extended_version just pads the version out to four 32-bit words. + // This makes the bones file compatible with Hearse with no extra + // munging needed. + if (extended_version) + { + // Use a single signature 16-bit word to indicate that this is + // Stone Soup and to disambiguate this (unmunged) bones file + // from the munged bones files offered by the old Crawl-aware + // hearse.pl. Crawl-aware hearse.pl will prefix the bones file + // with the first 16-bits of the Crawl version, and the following + // 7 16-bit words set to 0. + marshallShort(versionTag, GHOST_SIGNATURE); + + // Write the three remaining 32-bit words of padding. + for (int i = 0; i < 3; ++i) + marshallLong(versionTag, 0); + } + + tag_write(versionTag, dataFile); +} + static void write_tagged_file( FILE *dataFile, char majorVersion, - char minorVersion, int fileType ) + char minorVersion, int fileType, + bool extended_version = false ) { struct tagHeader th; @@ -610,13 +646,7 @@ static void write_tagged_file( FILE *dataFile, char majorVersion, char tags[NUM_TAGS]; tag_set_expected(tags, fileType); - // write version - struct tagHeader versionTag; - versionTag.offset = 0; - versionTag.tagID = TAG_VERSION; - marshallByte(versionTag, majorVersion); - marshallByte(versionTag, minorVersion); - tag_write(versionTag, dataFile); + write_version( dataFile, majorVersion, minorVersion, extended_version ); // all other tags for(int i=1; i<NUM_TAGS; i++) @@ -1606,21 +1636,23 @@ static bool determine_ghost_version( FILE *ghostFile, if (read2(ghostFile, buf, 2) != 2) return false; // empty file? - // check for pre-v4 -- simply started right in with ghost name. - if (isprint(buf[0]) && buf[0] > 4) - { - majorVersion = 0; - minorVersion = 0; - rewind(ghostFile); - return true; - } - // otherwise, read version and validate. majorVersion = buf[0]; minorVersion = buf[1]; - if (majorVersion == SAVE_MAJOR_VERSION) - return true; + // check for the DCSS ghost signature. + if (readShort(ghostFile) != GHOST_SIGNATURE) + return (false); + + if (majorVersion == SAVE_MAJOR_VERSION + && minorVersion <= GHOST_MINOR_VERSION) + { + // Discard three more 32-bit words of padding. + for (int i = 0; i < 3; ++i) + readLong(ghostFile); + + return !feof(ghostFile); + } return false; // if its not SAVE_MAJOR_VERSION, no idea! } @@ -1669,7 +1701,8 @@ void save_ghost( bool force ) } write_tagged_file( gfile, SAVE_MAJOR_VERSION, - GHOST_MINOR_VERSION, TAGTYPE_GHOST ); + GHOST_MINOR_VERSION, TAGTYPE_GHOST, + true ); lk_close(gfile, "wb", cha_fil); diff --git a/crawl-ref/source/ghost.cc b/crawl-ref/source/ghost.cc index 2ddde666ff..b5075aaccf 100644 --- a/crawl-ref/source/ghost.cc +++ b/crawl-ref/source/ghost.cc @@ -8,6 +8,8 @@ #include "AppHdr.h" +#include "ghost.h" + #include "externs.h" #include "itemname.h" #include "itemprop.h" @@ -98,7 +100,7 @@ static spell_type search_order_misc[] = { /* Last slot (emergency) can only be teleport self or blink. */ -ghost_demon::ghost_demon() : name(), values() +ghost_demon::ghost_demon() { reset(); } @@ -106,8 +108,22 @@ ghost_demon::ghost_demon() : name(), values() void ghost_demon::reset() { name.clear(); - values.init(0); - values[GVAL_SPEED] = 10; + species = SP_UNKNOWN; + job = JOB_UNKNOWN; + best_skill = SK_FIGHTING; + best_skill_level = 0; + xl = 0; + max_hp = 0; + ev = 0; + ac = 0; + damage = 0; + speed = 10; + see_invis = false; + brand = SPWPN_NORMAL; + resists = mon_resist_def(); + spellcaster = false; + cycle_colours = false; + fly = FL_NONE; } void ghost_demon::init_random_demon() @@ -115,172 +131,166 @@ void ghost_demon::init_random_demon() name = make_name(random_int(), false); // hp - could be defined below (as could ev, AC etc). Oh well, too late: - values[ GVAL_MAX_HP ] = 100 + roll_dice( 3, 50 ); + max_hp = 100 + roll_dice( 3, 50 ); - values[ GVAL_EV ] = 5 + random2(20); - values[ GVAL_AC ] = 5 + random2(20); + ev = 5 + random2(20); + ac = 5 + random2(20); - values[ GVAL_SEE_INVIS ] = (one_chance_in(10) ? 0 : 1); + see_invis = !one_chance_in(10); if (!one_chance_in(3)) - values[ GVAL_RES_FIRE ] = (coinflip() ? 2 : 3); + resists.fire = random_range(1, 2); else { - values[ GVAL_RES_FIRE ] = 0; /* res_fire */ + resists.fire = 0; /* res_fire */ if (one_chance_in(10)) - values[ GVAL_RES_FIRE ] = -1; + resists.fire = -1; } if (!one_chance_in(3)) - values[ GVAL_RES_COLD ] = 2; + resists.cold = random_range(1, 2); else { - values[ GVAL_RES_COLD ] = 0; /* res_cold */ - + resists.cold = 0; if (one_chance_in(10)) - values[ GVAL_RES_COLD ] = -1; + resists.cold = -1; } // demons, like ghosts, automatically get poison res. and life prot. // resist electricity: - values[ GVAL_RES_ELEC ] = !one_chance_in(3); + resists.elec = one_chance_in(3); // HTH damage: - values[ GVAL_DAMAGE ] = 20 + roll_dice( 2, 20 ); + damage = 20 + roll_dice( 2, 20 ); // special attack type (uses weapon brand code): - values[ GVAL_BRAND ] = SPWPN_NORMAL; + brand = SPWPN_NORMAL; if (!one_chance_in(3)) { do { - values[ GVAL_BRAND ] = random2(17); + brand = static_cast<brand_type>( random2(MAX_PAN_LORD_BRANDS) ); /* some brands inappropriate (e.g. holy wrath) */ - } while (values[ GVAL_BRAND ] == SPWPN_HOLY_WRATH - || (values[ GVAL_BRAND ] == SPWPN_ORC_SLAYING + } while (brand == SPWPN_HOLY_WRATH + || (brand == SPWPN_ORC_SLAYING && you.mons_species() != MONS_ORC) - || (values[ GVAL_BRAND ] == SPWPN_DRAGON_SLAYING + || (brand == SPWPN_DRAGON_SLAYING && you.mons_species() != MONS_DRACONIAN) - || values[ GVAL_BRAND ] == SPWPN_PROTECTION - || values[ GVAL_BRAND ] == SPWPN_FLAME - || values[ GVAL_BRAND ] == SPWPN_FROST); + || brand == SPWPN_PROTECTION + || brand == SPWPN_FLAME + || brand == SPWPN_FROST); } // is demon a spellcaster? // upped from one_chance_in(3)... spellcasters are more interesting // and I expect named demons to typically have a trick or two -- bwr - values[GVAL_DEMONLORD_SPELLCASTER] = !one_chance_in(10); - - // does demon fly? (0 = no, 1 = fly, 2 = levitate) - values[GVAL_DEMONLORD_FLY] = (one_chance_in(3) ? 0 : - one_chance_in(5) ? 2 : 1); - - // vacant <ghost best skill level>: - values[GVAL_DEMONLORD_UNUSED] = 0; + spellcaster = !one_chance_in(10); + // does demon fly? + fly = (one_chance_in(3)? FL_NONE : + one_chance_in(5)? FL_LEVITATE : FL_FLY); + // hit dice: - values[GVAL_DEMONLORD_HIT_DICE] = 10 + roll_dice(2, 10); + xl = 10 + roll_dice(2, 10); // does demon cycle colours? - values[GVAL_DEMONLORD_CYCLE_COLOUR] = (one_chance_in(10) ? 1 : 0); + cycle_colours = one_chance_in(10); - for (int i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++) - values[i] = SPELL_NO_SPELL; + spells.init(SPELL_NO_SPELL); /* This bit uses the list of player spells to find appropriate spells for the demon, then converts those spells to the monster spell indices. Some special monster-only spells are at the end. */ - if (values[ GVAL_DEMONLORD_SPELLCASTER ] == 1) + if (spellcaster) { if (coinflip()) - values[GVAL_SPELL_1] = RANDOM_ELEMENT(search_order_conj); + spells[0] = RANDOM_ELEMENT(search_order_conj); // Might duplicate the first spell, but that isn't a problem. if (coinflip()) - values[GVAL_SPELL_2] = RANDOM_ELEMENT(search_order_conj); + spells[1] = RANDOM_ELEMENT(search_order_conj); if (!one_chance_in(4)) - values[GVAL_SPELL_3] = RANDOM_ELEMENT(search_order_third); + spells[2] = RANDOM_ELEMENT(search_order_third); if (coinflip()) { - values[GVAL_SPELL_4] = RANDOM_ELEMENT(search_order_misc); - if ( values[GVAL_SPELL_4] == SPELL_DIG ) - values[GVAL_SPELL_4] = SPELL_NO_SPELL; + spells[3] = RANDOM_ELEMENT(search_order_misc); + if ( spells[3] == SPELL_DIG ) + spells[3] = SPELL_NO_SPELL; } if (coinflip()) - values[GVAL_SPELL_5] = RANDOM_ELEMENT(search_order_misc); - + spells[4] = RANDOM_ELEMENT(search_order_misc); if (coinflip()) - values[ GVAL_SPELL_6 ] = SPELL_BLINK; + spells[5] = SPELL_BLINK; if (coinflip()) - values[ GVAL_SPELL_6 ] = SPELL_TELEPORT_SELF; + spells[5] = SPELL_TELEPORT_SELF; /* Converts the player spell indices to monster spell ones */ - for (int i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++) - values[i] = translate_spell( values[i] ); + for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++) + spells[i] = translate_spell( spells[i] ); /* give demon a chance for some monster-only spells: */ /* and demon-summoning should be fairly common: */ if (one_chance_in(25)) - values[GVAL_SPELL_1] = SPELL_HELLFIRE_BURST; + spells[0] = SPELL_HELLFIRE_BURST; if (one_chance_in(25)) - values[GVAL_SPELL_1] = SPELL_METAL_SPLINTERS; + spells[0] = SPELL_METAL_SPLINTERS; if (one_chance_in(25)) - values[GVAL_SPELL_1] = SPELL_ENERGY_BOLT; /* eye of devas */ + spells[0] = SPELL_ENERGY_BOLT; /* eye of devas */ if (one_chance_in(25)) - values[GVAL_SPELL_2] = SPELL_STEAM_BALL; + spells[1] = SPELL_STEAM_BALL; if (one_chance_in(25)) - values[GVAL_SPELL_2] = SPELL_ISKENDERUNS_MYSTIC_BLAST; + spells[1] = SPELL_ISKENDERUNS_MYSTIC_BLAST; if (one_chance_in(25)) - values[GVAL_SPELL_2] = SPELL_HELLFIRE; + spells[1] = SPELL_HELLFIRE; if (one_chance_in(25)) - values[GVAL_SPELL_3] = SPELL_SMITING; + spells[2] = SPELL_SMITING; if (one_chance_in(25)) - values[GVAL_SPELL_3] = SPELL_HELLFIRE_BURST; + spells[2] = SPELL_HELLFIRE_BURST; if (one_chance_in(12)) - values[GVAL_SPELL_3] = SPELL_SUMMON_GREATER_DEMON; + spells[2] = SPELL_SUMMON_GREATER_DEMON; if (one_chance_in(12)) - values[GVAL_SPELL_3] = SPELL_SUMMON_DEMON; + spells[2] = SPELL_SUMMON_DEMON; if (one_chance_in(20)) - values[GVAL_SPELL_4] = SPELL_SUMMON_GREATER_DEMON; + spells[3] = SPELL_SUMMON_GREATER_DEMON; if (one_chance_in(20)) - values[GVAL_SPELL_4] = SPELL_SUMMON_DEMON; + spells[3] = SPELL_SUMMON_DEMON; /* at least they can summon demons */ - if (values[GVAL_SPELL_4] == SPELL_NO_SPELL) - values[GVAL_SPELL_4] = SPELL_SUMMON_DEMON; + if (spells[3] == SPELL_NO_SPELL) + spells[3] = SPELL_SUMMON_DEMON; if (one_chance_in(15)) - values[GVAL_SPELL_5] = SPELL_DIG; + spells[4] = SPELL_DIG; } } void ghost_demon::init_player_ghost() { name = you.your_name; - values[ GVAL_MAX_HP ] = ((you.hp_max >= 400) ? 400 : you.hp_max); - values[ GVAL_EV ] = player_evasion(); - values[ GVAL_AC ] = player_AC(); + max_hp = ((you.hp_max >= 400) ? 400 : you.hp_max); + ev = player_evasion(); + ac = player_AC(); - if (values[GVAL_EV] > 40) - values[GVAL_EV] = 40; - - values[ GVAL_SEE_INVIS ] = player_see_invis(); - values[ GVAL_RES_FIRE ] = player_res_fire(); - values[ GVAL_RES_COLD ] = player_res_cold(); - values[ GVAL_RES_ELEC ] = player_res_electricity(); - values[ GVAL_SPEED ] = player_ghost_base_movement_speed(); + if (ev > 60) + ev = 60; + + see_invis = player_see_invis(); + resists.fire = player_res_fire(); + resists.cold = player_res_cold(); + resists.elec = player_res_electricity(); + speed = player_ghost_base_movement_speed(); int d = 4; - int e = 0; + int e = SPWPN_NORMAL; const int wpn = you.equip[EQ_WEAPON]; if (wpn != -1) @@ -320,19 +330,18 @@ void ghost_demon::init_player_ghost() if (d > 50) d = 50; - values[ GVAL_DAMAGE ] = d; - values[ GVAL_BRAND ] = e; - values[ GVAL_SPECIES ] = you.species; - values[ GVAL_BEST_SKILL ] = best_skill(SK_FIGHTING, (NUM_SKILLS - 1), 99); - values[ GVAL_SKILL_LEVEL ] = - you.skills[best_skill(SK_FIGHTING, (NUM_SKILLS - 1), 99)]; - values[ GVAL_EXP_LEVEL ] = you.experience_level; - values[ GVAL_CLASS ] = you.char_class; + damage = d; + brand = static_cast<brand_type>( e ); + species = you.species; + best_skill = ::best_skill(SK_FIGHTING, (NUM_SKILLS - 1), 99); + best_skill_level = you.skills[best_skill]; + xl = you.experience_level; + job = you.char_class; add_spells(); } -static int search_first_list(int ignore_spell) +static spell_type search_first_list(int ignore_spell) { for (unsigned i = 0; i < sizeof(search_order_conj) / sizeof(*search_order_conj); i++) @@ -350,7 +359,7 @@ static int search_first_list(int ignore_spell) return SPELL_NO_SPELL; } // end search_first_list() -static int search_second_list(int ignore_spell) +static spell_type search_second_list(int ignore_spell) { for (unsigned i = 0; i < sizeof(search_order_third) / sizeof(*search_order_third); i++) @@ -368,7 +377,7 @@ static int search_second_list(int ignore_spell) return SPELL_NO_SPELL; } // end search_second_list() -static int search_third_list(int ignore_spell) +static spell_type search_third_list(int ignore_spell) { for (unsigned i = 0; i < sizeof(search_order_misc) / sizeof(*search_order_misc); i++) @@ -394,44 +403,43 @@ void ghost_demon::add_spells( ) { int i = 0; - for (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++) - values[i] = SPELL_NO_SPELL; + spells.init(SPELL_NO_SPELL); - values[ GVAL_SPELL_1 ] = search_first_list(SPELL_NO_SPELL); - values[ GVAL_SPELL_2 ] = search_first_list(values[GVAL_SPELL_1]); - values[ GVAL_SPELL_3 ] = search_second_list(SPELL_NO_SPELL); - values[ GVAL_SPELL_4 ] = search_third_list(SPELL_DIG); + spells[ 0 ] = search_first_list(SPELL_NO_SPELL); + spells[ 1 ] = search_first_list(spells[0]); + spells[ 2 ] = search_second_list(SPELL_NO_SPELL); + spells[ 3 ] = search_third_list(SPELL_DIG); - if (values[ GVAL_SPELL_4 ] == SPELL_NO_SPELL) - values[ GVAL_SPELL_4 ] = search_first_list(SPELL_NO_SPELL); + if (spells[ 3 ] == SPELL_NO_SPELL) + spells[ 3 ] = search_first_list(SPELL_NO_SPELL); - values[ GVAL_SPELL_5 ] = search_third_list(values[GVAL_SPELL_4]); + spells[ 4 ] = search_third_list(spells[3]); - if (values[ GVAL_SPELL_5 ] == SPELL_NO_SPELL) - values[ GVAL_SPELL_5 ] = search_first_list(values[GVAL_SPELL_4]); + if (spells[ 4 ] == SPELL_NO_SPELL) + spells[ 4 ] = search_first_list(spells[3]); if (player_has_spell( SPELL_DIG )) - values[ GVAL_SPELL_5 ] = SPELL_DIG; + spells[ 4 ] = SPELL_DIG; /* Looks for blink/tport for emergency slot */ if (player_has_spell( SPELL_CONTROLLED_BLINK ) || player_has_spell( SPELL_BLINK )) { - values[ GVAL_SPELL_6 ] = SPELL_CONTROLLED_BLINK; + spells[ 5 ] = SPELL_CONTROLLED_BLINK; } if (player_has_spell( SPELL_TELEPORT_SELF )) - values[ GVAL_SPELL_6 ] = SPELL_TELEPORT_SELF; + spells[ 5 ] = SPELL_TELEPORT_SELF; - for (i = GVAL_SPELL_1; i <= GVAL_SPELL_6; i++) - values[i] = translate_spell( values[i] ); + for (i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++) + spells[i] = translate_spell( spells[i] ); } // end add_spells() /* When passed the number for a player spell, returns the equivalent monster spell. Returns SPELL_NO_SPELL on failure (no equiv). */ -int ghost_demon::translate_spell(int spel) const +spell_type ghost_demon::translate_spell(spell_type spel) const { switch (spel) { diff --git a/crawl-ref/source/ghost.h b/crawl-ref/source/ghost.h new file mode 100644 index 0000000000..b4f861ca3d --- /dev/null +++ b/crawl-ref/source/ghost.h @@ -0,0 +1,52 @@ +#ifndef GHOST_H +#define GHOST_H + +#include "externs.h" +#include "enum.h" +#include "itemprop.h" +#include "mon-util.h" + +struct ghost_demon +{ +public: + std::string name; + + species_type species; + job_type job; + skill_type best_skill; + short best_skill_level; + short xl; + + short max_hp, ev, ac, damage, speed; + bool see_invis; + brand_type brand; + mon_resist_def resists; + + bool spellcaster, cycle_colours; + flight_type fly; + + monster_spells spells; + +public: + ghost_demon(); + void reset(); + void init_random_demon(); + void init_player_ghost(); + +public: + static std::vector<ghost_demon> find_ghosts(); + +private: + static int n_extra_ghosts(); + static void find_extra_ghosts(std::vector<ghost_demon> &ghosts, int n); + static void find_transiting_ghosts(std::vector<ghost_demon> &gs, int n); + static void announce_ghost(const ghost_demon &g); + +private: + void add_spells(); + spell_type translate_spell(spell_type playerspell) const; +}; + +extern std::vector<ghost_demon> ghosts; + +#endif diff --git a/crawl-ref/source/itemname.cc b/crawl-ref/source/itemname.cc index b018483cb6..afb978dfa1 100644 --- a/crawl-ref/source/itemname.cc +++ b/crawl-ref/source/itemname.cc @@ -987,7 +987,7 @@ std::string item_def::name_aux( description_level_type desc, const bool know_brand = !basename && !qualname && !dbname && !testbits(ignore_flags, ISFLAG_KNOW_TYPE) - && (ident || item_ident(*this, ISFLAG_KNOW_TYPE)); + && (ident || item_type_known(*this)); const bool know_ego = know_brand; @@ -1068,9 +1068,9 @@ std::string item_def::name_aux( description_level_type desc, buff << racial_description_string(*this, terse); if (know_brand && !terse && - (get_weapon_brand(*this) == SPWPN_VAMPIRICISM)) + (get_weapon_brand(*this) == SPWPN_VAMPIRICISM)) { - buff << "vampiric "; + buff << "vampiric "; } buff << item_base_name(*this); @@ -1649,9 +1649,17 @@ bool item_type_known( const item_def& item ) // Artefacts have different descriptions from other items, // so we can't use general item knowledge for them. - if ( is_artefact(item) ) + if (is_artefact(item)) return false; + // Poisoned missiles are always identified. + if (item.base_type == OBJ_MISSILES) + { + int ammo_brand = get_ammo_brand(item); + if (ammo_brand == SPMSL_POISONED || ammo_brand == SPMSL_CURARE) + return (true); + } + const item_type_id_type idt = objtype_to_idtype(item.base_type); if ( idt != NUM_IDTYPE && item.sub_type < 50 ) return ( type_ids[idt][item.sub_type] == ID_KNOWN_TYPE ); diff --git a/crawl-ref/source/itemprop.h b/crawl-ref/source/itemprop.h index 70057a19d8..1c680e69c5 100644 --- a/crawl-ref/source/itemprop.h +++ b/crawl-ref/source/itemprop.h @@ -96,7 +96,10 @@ enum brand_type // equivalent to (you.inv[].special or mitm[].special) % 30 SPWPN_PAIN, // 15 SPWPN_DISTORTION, SPWPN_REACHING, // 17 - SPWPN_RETURNING, + + MAX_PAN_LORD_BRANDS, // 18 + + SPWPN_RETURNING = MAX_PAN_LORD_BRANDS, SPWPN_CONFUSE, SPWPN_RANDART_I = 25, // 25 SPWPN_RANDART_II, diff --git a/crawl-ref/source/libunix.cc b/crawl-ref/source/libunix.cc index a2f1cc2fbc..93ce48d10a 100644 --- a/crawl-ref/source/libunix.cc +++ b/crawl-ref/source/libunix.cc @@ -290,7 +290,10 @@ static int raw_m_getch() } #endif default: - return (c); + // getch() returns -1 on EOF, convert that into an Escape. Evil hack, + // but the alternative is to explicitly check for -1 everywhere where + // we might otherwise spin in a tight keyboard input loop. + return (c == -1? ESCAPE : c); } } @@ -301,7 +304,7 @@ int m_getch() c = raw_m_getch(); while ((c == CK_MOUSE_MOVE || c == CK_MOUSE_CLICK) && !crawl_state.mouse_enabled); - + return (c); } diff --git a/crawl-ref/source/message.cc b/crawl-ref/source/message.cc index 1f36b305db..9c919f1fe5 100644 --- a/crawl-ref/source/message.cc +++ b/crawl-ref/source/message.cc @@ -746,7 +746,7 @@ void more(void) if (Options.show_more_prompt && !suppress_messages) { - char keypress = 0; + int keypress = 0; if (Options.tutorial_left) message_out(crawl_view.msgsz.y - 1, @@ -764,7 +764,8 @@ void more(void) do keypress = getch(); - while (keypress != ' ' && keypress != '\r' && keypress != '\n'); + while (keypress != ' ' && keypress != '\r' && keypress != '\n' + && keypress != -1); } mesclr(true); } // end more() diff --git a/crawl-ref/source/misc.cc b/crawl-ref/source/misc.cc index 7111bc8881..b6df1ef8d5 100644 --- a/crawl-ref/source/misc.cc +++ b/crawl-ref/source/misc.cc @@ -656,7 +656,7 @@ void up_stairs(dungeon_feature_type force_stair, mpr("Warning: level annotation for next level is:", MSGCH_PROMPT); mpr(get_level_annotation(next_level_id).c_str(), MSGCH_PROMPT); - if (!yesno("Enter next level anyways?", true, 0, true, false)) + if (!yesno("Enter next level anyway?", true, 0, true, false)) { interrupt_activity( AI_FORCE_INTERRUPT ); return; diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h index 100d7edd66..5a094e8c72 100644 --- a/crawl-ref/source/mon-data.h +++ b/crawl-ref/source/mon-data.h @@ -1433,7 +1433,7 @@ { MONS_MOTTLED_DRAGON, 'D', MAGENTA, "mottled dragon", M_SPELLCASTER | M_FLIES, - MR_RES_POISON | MR_RES_FIRE, + MR_RES_POISON | MR_RES_FIRE | MR_RES_STICKY_FLAME, 1100, 10, MONS_DRAGON, MONS_MOTTLED_DRAGON, MH_NATURAL, -3, { {AT_BITE, AF_PLAIN, 15}, {AT_CLAW, AF_PLAIN, 6}, AT_NO_ATK, AT_NO_ATK }, { 5, 3, 5, 0 }, @@ -3480,7 +3480,7 @@ { MONS_MOTTLED_DRACONIAN, 'd', LIGHTMAGENTA, "mottled draconian", M_HUMANOID | M_COLD_BLOOD, - MR_RES_FIRE, + MR_RES_FIRE | MR_RES_STICKY_FLAME, 900, 10, MONS_DRACONIAN, MONS_MOTTLED_DRACONIAN, MH_NATURAL, -2, { {AT_HIT, AF_PLAIN, 20}, {AT_HIT, AF_PLAIN, 0}, {AT_HIT, AF_PLAIN, 0} }, { 14, 5, 4, 0 }, diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc index 8fad0cfef9..8a9eef2355 100644 --- a/crawl-ref/source/mon-util.cc +++ b/crawl-ref/source/mon-util.cc @@ -34,6 +34,8 @@ #include "debug.h" #include "delay.h" #include "dgnevent.h" +#include "fight.h" +#include "ghost.h" #include "insult.h" #include "itemname.h" #include "itemprop.h" @@ -256,14 +258,18 @@ void init_monster_symbols() } } -unsigned long get_mons_class_resists(int mc) +const mon_resist_def &get_mons_class_resists(int mc) { - return (smc->resists); + const monsterentry *me = get_monster_data(mc); + return (me? me->resists : get_monster_data(MONS_PROGRAM_BUG)->resists); } -unsigned long get_mons_resists(const monsters *mon) +mon_resist_def get_mons_resists(const monsters *mon) { - unsigned long resists = get_mons_class_resists(mon->type); + if (mon->type == MONS_PLAYER_GHOST || mon->type == MONS_PANDEMONIUM_DEMON) + return (mon->ghost->resists); + + mon_resist_def resists = get_mons_class_resists(mon->type); if ((mons_genus(mon->type) == MONS_DRACONIAN && mon->type != MONS_DRACONIAN) || mon->type == MONS_TIAMAT) @@ -290,11 +296,6 @@ int mons_piety(const monsters *mon) return (mon->hit_dice * 14); } -unsigned long mons_resist(const monsters *mon, unsigned long flags) -{ - return (get_mons_resists(mon) & flags); -} - bool mons_class_flag(int mc, int bf) { const monsterentry *me = smc; @@ -394,7 +395,7 @@ bool mons_class_is_slowable(int mc) bool mons_is_wall_shielded(int mc) { - return (mons_habitat(mc) == HT_ROCK); + return (mons_habitat_by_type(mc) == HT_ROCK); } // returns whether a monster is non-solid @@ -605,7 +606,7 @@ bool mons_sense_invis(const monsters *mon) bool mons_see_invis(const monsters *mon) { if (mon->type == MONS_PLAYER_GHOST || mon->type == MONS_PANDEMONIUM_DEMON) - return (mon->ghost->values[ GVAL_SEE_INVIS ]); + return (mon->ghost->see_invis); else if (mons_class_flag(mon->type, M_SEE_INVIS)) return (true); else if (scan_mon_inv_randarts( mon, RAP_EYESIGHT ) > 0) @@ -739,7 +740,7 @@ mon_attack_def mons_attack_spec(const monsters *mons, int attk_number) if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON) { if (attk_number == 0) - return mon_attack_def::attk(mons->ghost->values[GVAL_DAMAGE]); + return mon_attack_def::attk(mons->ghost->damage); else return mon_attack_def::attk(0, AT_NONE); } @@ -852,14 +853,10 @@ int mons_res_elec( const monsters *mon ) { int mc = mon->type; - if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON) - return (mon->ghost->values[ GVAL_RES_ELEC ]); - /* this is a variable, not a player_xx() function, so can be above 1 */ int u = 0; - if (mons_resist(mon, MR_RES_ELEC)) - u++; + u += get_mons_resists(mon).elec; // don't bother checking equipment if the monster can't use it if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT) @@ -885,26 +882,19 @@ bool mons_res_asphyx( const monsters *mon ) || holiness == MH_DEMONIC || holiness == MH_NONLIVING || holiness == MH_PLANT - || mons_resist(mon, MR_RES_ASPHYX)); + || get_mons_resists(mon).asphyx > 0); } int mons_res_acid( const monsters *mon ) { - const unsigned long f = get_mons_resists(mon); - return ((f & MR_RES_ACID) != 0); + return get_mons_resists(mon).acid; } int mons_res_poison( const monsters *mon ) { int mc = mon->type; - unsigned long u = 0, f = get_mons_resists(mon); - - if (f & MR_RES_POISON) - u++; - - if (f & MR_VUL_POISON) - u--; + int u = get_mons_resists(mon).poison; if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT) { @@ -930,26 +920,27 @@ int mons_res_poison( const monsters *mon ) return (u); } // end mons_res_poison() +bool mons_res_sticky_flame( const monsters *mon ) +{ + return (get_mons_resists(mon).sticky_flame + || mon->has_equipped(EQ_BODY_ARMOUR, ARM_MOTTLED_DRAGON_ARMOUR)); +} + +int mons_res_steam( const monsters *mon ) +{ + int res = get_mons_resists(mon).steam; + if (mon->has_equipped(EQ_BODY_ARMOUR, ARM_STEAM_DRAGON_ARMOUR)) + res += 3; + return (res + mons_res_fire(mon) / 2); +} + int mons_res_fire( const monsters *mon ) { int mc = mon->type; - if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON) - return (mon->ghost->values[ GVAL_RES_FIRE ]); - - int u = 0, f = get_mons_resists(mon); - - // no Big Prize (tm) here either if you set all three flags. It's a pity uh? - // - // Note that natural monster resistance is two levels, this is duplicate - // the fact that having this flag used to be a lot better than armour - // for monsters (it used to make them immune in a lot of cases) -- bwr - if (f & MR_RES_HELLFIRE) - u += 3; - else if (f & MR_RES_FIRE) - u += 2; - else if (f & MR_VUL_FIRE) - u--; + const mon_resist_def res = get_mons_resists(mon); + + int u = std::min(res.fire + res.hellfire * 3, 3); if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT) { @@ -982,18 +973,7 @@ int mons_res_cold( const monsters *mon ) { int mc = mon->type; - if (mc == MONS_PLAYER_GHOST || mc == MONS_PANDEMONIUM_DEMON) - return (mon->ghost->values[ GVAL_RES_COLD ]); - - int u = 0, f = get_mons_resists(mon); - - // Note that natural monster resistance is two levels, this is duplicate - // the fact that having this flag used to be a lot better than armour - // for monsters (it used to make them immune in a lot of cases) -- bwr - if (f & MR_RES_COLD) - u += 2; - else if (f & MR_VUL_COLD) - u--; + int u = get_mons_resists(mon).cold; if (mons_itemuse(mc) >= MONUSE_STARTING_EQUIPMENT) { @@ -1104,9 +1084,9 @@ flight_type mons_class_flies(int mc) flight_type mons_flies(const monsters *mon) { if (mon->type == MONS_PANDEMONIUM_DEMON - && mon->ghost->values[ GVAL_DEMONLORD_FLY ]) + && mon->ghost->fly) { - return (FL_FLY); + return (mon->ghost->fly); } const flight_type ret = mons_class_flies( mon->type ); @@ -1686,9 +1666,16 @@ mon_intel_type mons_intel(int mc) return (smc->intel); } -habitat_type mons_habitat(int mc) +habitat_type mons_habitat_by_type(int mc) +{ + const monsterentry *me = get_monster_data(mc); + return (me? me->habitat : HT_LAND); +} + +habitat_type mons_habitat(const monsters *m) { - return (smc->habitat); + return mons_habitat_by_type( + mons_is_zombified(m)? mons_zombie_base(m) : m->type ); } bool intelligent_ally(const monsters *monster) @@ -1761,7 +1748,7 @@ int mons_offhand_weapon_index(const monsters *m) int mons_base_damage_brand(const monsters *m) { if (m->type == MONS_PLAYER_GHOST || m->type == MONS_PANDEMONIUM_DEMON) - return m->ghost->values[ GVAL_BRAND ]; + return m->ghost->brand; return (SPWPN_NORMAL); } @@ -1986,6 +1973,15 @@ bool ms_waste_of_time( const monsters *mon, spell_type monspell ) int intel, est_magic_resist, power, diff; const actor *foe = mon->get_foe(); + + // Keep friendly summoners from spamming summons constantly. + if (mons_friendly(mon) + && (!foe || foe == &you) + && spell_typematch(monspell, SPTYP_SUMMONING)) + { + return (true); + } + // Eventually, we'll probably want to be able to have monsters // learn which of their elemental bolts were resisted and have those // handled here as well. -- bwr @@ -2337,6 +2333,11 @@ monsters::monsters() { } +// Empty destructor to keep auto_ptr happy with incomplete ghost_demon type. +monsters::~monsters() +{ +} + monsters::monsters(const monsters &mon) { init_with(mon); @@ -2395,7 +2396,7 @@ coord_def monsters::target_pos() const bool monsters::swimming() const { const dungeon_feature_type grid = grd[x][y]; - return (grid_is_watery(grid) && mons_habitat(type) == HT_WATER); + return (grid_is_watery(grid) && mons_habitat(this) == HT_WATER); } bool monsters::submerged() const @@ -2409,7 +2410,7 @@ bool monsters::floundering() const return (grid_is_water(grid) // Can't use monster_habitable_grid because that'll return true // for non-water monsters in shallow water. - && mons_habitat(type) != HT_WATER + && mons_habitat(this) != HT_WATER && !mons_amphibious(type) && !mons_flies(this)); } @@ -2693,6 +2694,10 @@ bool monsters::can_use_missile(const item_def &item) const // grabbing missiles. if (has_spell_of_type(SPTYP_CONJURATION | SPTYP_SUMMONING)) return (false); + + // Blademasters don't want to throw stuff. + if (type == MONS_DEEP_ELF_BLADEMASTER) + return (false); if (item.base_type == OBJ_WEAPONS) return (is_throwable(item)); @@ -3392,6 +3397,11 @@ int monsters::id() const return (type); } +int monsters::get_experience_level() const +{ + return (hit_dice); +} + bool monsters::fumbles_attack(bool verbose) { if (floundering() && one_chance_in(4)) @@ -3434,7 +3444,7 @@ void monsters::go_berserk(bool /* intentional */) simple_monster_message( this, make_stringf(" shakes off %s lethargy.", - name(DESC_NOCAP_YOUR).c_str()).c_str()); + pronoun(PRONOUN_NOCAP_POSSESSIVE).c_str()).c_str()); } del_ench(ENCH_HASTE); del_ench(ENCH_FATIGUE, true); // give no additional message @@ -3556,6 +3566,11 @@ int monsters::res_fire() const return (mons_res_fire(this)); } +int monsters::res_steam() const +{ + return (mons_res_steam(this)); +} + int monsters::res_cold() const { return (mons_res_cold(this)); @@ -3707,12 +3722,12 @@ void monsters::set_ghost(const ghost_demon &g) void monsters::pandemon_init() { - hit_dice = ghost->values[ GVAL_DEMONLORD_HIT_DICE ]; - hit_points = ghost->values[ GVAL_MAX_HP ]; - max_hit_points = ghost->values[ GVAL_MAX_HP ]; - ac = ghost->values[ GVAL_AC ]; - ev = ghost->values[ GVAL_EV ]; - speed = (one_chance_in(3) ? 10 : 6 + roll_dice(2, 9)); + hit_dice = ghost->xl; + hit_points = ghost->max_hp; + max_hit_points = ghost->max_hp; + ac = ghost->ac; + ev = ghost->ev; + speed = (one_chance_in(3) ? 10 : 8 + roll_dice(2, 9)); speed_increment = 70; if (you.char_direction == GDT_ASCENDING && you.level_type == LEVEL_DUNGEON) colour = LIGHTRED; @@ -3724,12 +3739,12 @@ void monsters::pandemon_init() void monsters::ghost_init() { type = MONS_PLAYER_GHOST; - hit_dice = ghost->values[ GVAL_EXP_LEVEL ]; - hit_points = ghost->values[ GVAL_MAX_HP ]; - max_hit_points = ghost->values[ GVAL_MAX_HP ]; - ac = ghost->values[ GVAL_AC]; - ev = ghost->values[ GVAL_EV ]; - speed = ghost->values[ GVAL_SPEED ]; + hit_dice = ghost->xl; + hit_points = ghost->max_hp; + max_hit_points = ghost->max_hp; + ac = ghost->ac; + ev = ghost->ev; + speed = ghost->speed; speed_increment = 70; attitude = ATT_HOSTILE; behaviour = BEH_WANDER; @@ -3896,16 +3911,7 @@ void monsters::load_spells(mon_spellbook_type book) #endif if (book == MST_GHOST) - { - for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++) - { - spells[i] = - static_cast<spell_type>( ghost->values[ GVAL_SPELL_1 + i ] ); -#if DEBUG_DIAGNOSTICS - mprf( MSGCH_DIAGNOSTICS, "spell #%d: %d", i, spells[i] ); -#endif - } - } + spells = ghost->spells; else { for (unsigned int i = 0; i < ARRAYSIZE(mspell_list); ++i) @@ -3918,6 +3924,11 @@ void monsters::load_spells(mon_spellbook_type book) } } } +#if DEBUG_DIAGNOSTICS + for (int i = 0; i < NUM_MONSTER_SPELL_SLOTS; i++) + mprf( MSGCH_DIAGNOSTICS, "Spell #%d: %d (%s)", + i, spells[i], spell_title(spells[i]) ); +#endif } bool monsters::has_ench(enchant_type ench) const @@ -4588,12 +4599,12 @@ void monsters::apply_enchantment(const mon_enchant &me) } // Now we handle the others: - int grid = grd[x][y]; + const dungeon_feature_type grid = grd[x][y]; // Badly injured monsters prefer to stay submerged... // electrical eels and lava snakes have ranged attacks // and are more likely to surface. -- bwr - if (!monster_can_submerge(type, grid)) + if (!monster_can_submerge(this, grid)) del_ench(ENCH_SUBMERGED); // forced to surface else if (hit_points <= max_hit_points / 2) break; @@ -4658,10 +4669,8 @@ void monsters::apply_enchantment(const mon_enchant &me) // assumption: mons_res_fire has already been checked case ENCH_STICKY_FLAME: { - int dam = roll_dice( 2, 4 ) - 1; - - if (mons_res_fire( this ) < 0) - dam += roll_dice( 2, 5 ) - 1; + int dam = + resist_adjust_damage(this, res_fire(), roll_dice( 2, 4 ) - 1); if (dam > 0) { @@ -5002,7 +5011,7 @@ void monsters::apply_location_effects() mons_check_pool(this); if (alive() && has_ench(ENCH_SUBMERGED) - && !monster_can_submerge(type, grd[x][y])) + && !monster_can_submerge(this, grd[x][y])) { del_ench(ENCH_SUBMERGED); } @@ -5430,7 +5439,7 @@ std::string do_mon_str_replacements(const std::string &in_msg, if (mons_shouts(monster->type) >= NUM_SHOUTS) { mpr("Invalid @says@ type.", MSGCH_DIAGNOSTICS); - msg = replace_all(msg, "@says@", "bugilly says"); + msg = replace_all(msg, "@says@", "buggily says"); } else msg = replace_all(msg, "@says@", @@ -5448,7 +5457,7 @@ static mon_body_shape get_ghost_shape(const monsters *mon) { const ghost_demon &ghost = *(mon->ghost); - switch(ghost.values[GVAL_SPECIES]) + switch (ghost.species) { case SP_NAGA: return (MON_SHAPE_NAGA); @@ -5472,9 +5481,10 @@ static mon_body_shape get_ghost_shape(const monsters *mon) case SP_UNK1_DRACONIAN: case SP_BASE_DRACONIAN: return (MON_SHAPE_HUMANOID_TAILED); - } - return (MON_SHAPE_HUMANOID); + default: + return (MON_SHAPE_HUMANOID); + } } mon_body_shape get_mon_shape(const monsters *mon) @@ -5706,3 +5716,71 @@ std::string get_mon_shape_str(const mon_body_shape shape) return (shape_names[shape]); } + +///////////////////////////////////////////////////////////////////////// +// mon_resist_def + +mon_resist_def::mon_resist_def() + : elec(0), poison(0), fire(0), steam(0), cold(0), hellfire(0), + asphyx(0), acid(0), sticky_flame(false), pierce(0), + slice(0), bludgeon(0) +{ +} + +mon_resist_def::mon_resist_def(int flags, short level) + : elec(0), poison(0), fire(0), steam(0), cold(0), hellfire(0), + asphyx(0), acid(0), sticky_flame(false), pierce(0), + slice(0), bludgeon(0) +{ + for (int i = 0; i < 32; ++i) + { + switch (flags & (1 << i)) + { + case MR_RES_STEAM: steam = 3; break; + case MR_RES_ELEC: elec = level; break; + case MR_RES_POISON: poison = level; break; + case MR_RES_FIRE: fire = level; break; + case MR_RES_HELLFIRE: hellfire = level; break; + case MR_RES_COLD: cold = level; break; + case MR_RES_ASPHYX: asphyx = level; break; + case MR_RES_ACID: acid = level; break; + case MR_VUL_ELEC: elec = -level; break; + case MR_VUL_POISON: poison = -level; break; + case MR_VUL_FIRE: fire = -level; break; + case MR_VUL_COLD: cold = -level; break; + + case MR_RES_PIERCE: pierce = level; break; + case MR_RES_SLICE: slice = level; break; + case MR_RES_BLUDGEON: bludgeon = level; break; + case MR_VUL_PIERCE: pierce = -level; break; + case MR_VUL_SLICE: slice = -level; break; + case MR_VUL_BLUDGEON: bludgeon = -level; break; + + case MR_RES_STICKY_FLAME: sticky_flame = true; break; + + default: break; + } + } +} + +const mon_resist_def &mon_resist_def::operator |= (const mon_resist_def &o) +{ + elec += o.elec; + poison += o.poison; + fire += o.fire; + cold += o.cold; + hellfire += o.hellfire; + asphyx += o.asphyx; + acid += o.acid; + pierce += o.pierce; + slice += o.slice; + bludgeon += o.bludgeon; + sticky_flame = sticky_flame || o.sticky_flame; + return (*this); +} + +mon_resist_def mon_resist_def::operator | (const mon_resist_def &o) const +{ + mon_resist_def c(*this); + return (c |= o); +} diff --git a/crawl-ref/source/mon-util.h b/crawl-ref/source/mon-util.h index 4877271260..9aaccb551e 100644 --- a/crawl-ref/source/mon-util.h +++ b/crawl-ref/source/mon-util.h @@ -201,7 +201,11 @@ enum mon_resist_flags MR_VUL_PIERCE = (1<<14), MR_VUL_SLICE = (1<<15), - MR_VUL_BLUDGEON = (1<<16) + MR_VUL_BLUDGEON = (1<<16), + + // immune to stickiness of sticky flame. + MR_RES_STICKY_FLAME = (1 << 17), + MR_RES_STEAM = (1 << 18) }; enum shout_type @@ -275,6 +279,37 @@ struct mon_energy_usage char pickup_percent; }; +struct mon_resist_def +{ + // All values are actually saved as single-bytes, so practical + // range is -128 - 127, and the game only distinguishes values in + // the range -1 to 3. + + short elec; + short poison; + short fire; + short steam; + short cold; + short hellfire; + short asphyx; + short acid; + + bool sticky_flame; + + // Physical damage resists (currently unused) + short pierce; + short slice; + short bludgeon; + + mon_resist_def(); + mon_resist_def(int flags, short level = 1); + + mon_resist_def operator | (const mon_resist_def &other) const; + const mon_resist_def &operator |= (const mon_resist_def &other); +}; + +typedef mon_resist_def mrd; + struct monsterentry { short mc; // monster number @@ -283,7 +318,7 @@ struct monsterentry const char *name; unsigned long bitfields; - unsigned long resists; + mon_resist_def resists; short weight; // [Obsolete] Experience used to be calculated like this: @@ -446,35 +481,18 @@ bool mons_is_summoned(const monsters *m); * *********************************************************************** */ mon_intel_type mons_intel(int mc); -habitat_type mons_habitat(int mc); +// Use mons_habitat(const monster *) wherever possible since the other +// variant does not handle zombies correctly. +habitat_type mons_habitat(const monsters *m); +habitat_type mons_habitat_by_type(int mc); bool intelligent_ally(const monsters *mon); -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: beam - fight - monstuff - * *********************************************************************** */ +bool mons_res_sticky_flame( const monsters *mon ); int mons_res_cold( const monsters *mon ); - - -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: beam - fight - spells4 - * *********************************************************************** */ int mons_res_elec( const monsters *mon ); - - -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: beam - fight - monstuff - * *********************************************************************** */ int mons_res_fire( const monsters *mon ); - - -// last updated 12may2000 {dlb} -/* *********************************************************************** - * called from: beam - monstuff - spells4 - * *********************************************************************** */ +int mons_res_steam( const monsters *mon ); int mons_res_poison( const monsters *mon ); int mons_res_acid( const monsters *mon ); int mons_res_negative_energy( const monsters *mon ); @@ -509,7 +527,8 @@ int mons_speed(int mclass); * called from: dungeon - mon-util - spells2 * *********************************************************************** */ int mons_zombie_size(int mc); - +int mons_zombie_base(const monsters *monster); +bool mons_is_zombified(const monsters *monster); // last updated 12may2000 {dlb} /* *********************************************************************** diff --git a/crawl-ref/source/monplace.cc b/crawl-ref/source/monplace.cc index c2485d3824..ceec4e4890 100644 --- a/crawl-ref/source/monplace.cc +++ b/crawl-ref/source/monplace.cc @@ -15,6 +15,7 @@ #include "branch.h" #include "externs.h" +#include "ghost.h" #include "lev-pand.h" #include "makeitem.h" #include "monstuff.h" @@ -77,7 +78,9 @@ bool grid_compatible(dungeon_feature_type grid_wanted, bool monster_habitable_grid(const monsters *m, dungeon_feature_type actual_grid) { - return (monster_habitable_grid(m->type, actual_grid, mons_flies(m), + // Zombified monsters enjoy the same habitat as their original. + const int type = mons_is_zombified(m)? mons_zombie_base(m) : m->type; + return (monster_habitable_grid(type, actual_grid, mons_flies(m), m->paralysed())); } @@ -101,7 +104,7 @@ bool monster_habitable_grid(int monster_class, bool paralysed) { const dungeon_feature_type preferred_habitat = - habitat2grid( mons_habitat(monster_class) ); + habitat2grid( mons_habitat_by_type(monster_class) ); return (grid_compatible(preferred_habitat, actual_grid) // [dshaligram] Flying creatures are all DNGN_FLOOR, so we // only have to check for the additional valid grids of deep @@ -124,22 +127,20 @@ bool monster_habitable_grid(int monster_class, } // Returns true if the monster can submerge in the given grid -bool monster_can_submerge(int monster_class, int grid) +bool monster_can_submerge(const monsters *mons, dungeon_feature_type grid) { - const habitat_type habitat = mons_habitat(monster_class); - - if (habitat == HT_WATER && - (grid == DNGN_DEEP_WATER || grid == DNGN_BLUE_FOUNTAIN)) + switch (mons_habitat(mons)) { - return true; - } + case HT_WATER: + // Monsters can submerge in shallow water - this is intentional. + return grid_is_watery(grid); - if (habitat == HT_LAVA && grid == DNGN_LAVA) - { - return true; - } + case HT_LAVA: + return (grid == DNGN_LAVA); - return false; + default: + return false; + } } static bool need_super_ood(int lev_mons) @@ -485,7 +486,8 @@ bool place_monster(int &id, int mon_type, int power, beh_type behaviour, // a) not occupied // b) compatible // c) in the 'correct' proximity to the player - dungeon_feature_type grid_wanted = habitat2grid( mons_habitat(mon_type) ); + dungeon_feature_type grid_wanted = + habitat2grid( mons_habitat_by_type(mon_type) ); while(true) { // handled above, won't change anymore @@ -689,7 +691,7 @@ static int place_monster_aux( int mon_type, beh_type behaviour, int target, } else { - grid_wanted = habitat2grid( mons_habitat(mon_type) ); + grid_wanted = habitat2grid( mons_habitat_by_type(mon_type) ); // we'll try 1000 times for a good spot for (i = 0; i < 1000; i++) @@ -773,7 +775,7 @@ static int place_monster_aux( int mon_type, beh_type behaviour, int target, menv[id].flags |= MF_BATTY; } - if (monster_can_submerge(mon_type, grd[fx][fy]) + if (monster_can_submerge(&menv[id], grd[fx][fy]) && !one_chance_in(5)) menv[id].add_ench(ENCH_SUBMERGED); @@ -1570,8 +1572,9 @@ coord_def find_newmons_square(int mons_class, int x, int y) if (mons_class == WANDERING_MONSTER) mons_class = RANDOM_MONSTER; - dungeon_feature_type spcw = ((mons_class == RANDOM_MONSTER) ? DNGN_FLOOR - : habitat2grid( mons_habitat(mons_class) )); + dungeon_feature_type spcw = + ((mons_class == RANDOM_MONSTER) ? DNGN_FLOOR + : habitat2grid( mons_habitat_by_type(mons_class) )); // Might be better if we chose a space and tried to match the monster // to it in the case of RANDOM_MONSTER, that way if the target square diff --git a/crawl-ref/source/monplace.h b/crawl-ref/source/monplace.h index 2b318710ef..0bf912b5d5 100644 --- a/crawl-ref/source/monplace.h +++ b/crawl-ref/source/monplace.h @@ -180,7 +180,7 @@ bool monster_habitable_grid(int monster_class, dungeon_feature_type actual_grid, int flies = -1, bool paralysed = false); -bool monster_can_submerge(int monster_class, int grid); +bool monster_can_submerge(const monsters *mons, dungeon_feature_type grid); coord_def find_newmons_square(int mons_class, int x, int y); void spawn_random_monsters(); diff --git a/crawl-ref/source/monspeak.cc b/crawl-ref/source/monspeak.cc index f1230a6fbd..dbf428ed2a 100644 --- a/crawl-ref/source/monspeak.cc +++ b/crawl-ref/source/monspeak.cc @@ -26,6 +26,7 @@ #include "database.h" #include "debug.h" #include "fight.h" +#include "ghost.h" #include "insult.h" #include "itemname.h" #include "message.h" @@ -127,7 +128,7 @@ static std::string player_ghost_speak_str(const monsters *monster, const std::vector<std::string> prefixes) { const ghost_demon &ghost = *(monster->ghost); - std::string ghost_class = get_class_name(ghost.values[GVAL_CLASS]); + std::string ghost_class = get_class_name(ghost.job); std::string prefix = ""; for (int i = 0, size = prefixes.size(); i < size; i++) diff --git a/crawl-ref/source/monstuff.cc b/crawl-ref/source/monstuff.cc index cf8ddec129..7dce081e7a 100644 --- a/crawl-ref/source/monstuff.cc +++ b/crawl-ref/source/monstuff.cc @@ -38,6 +38,7 @@ #include "describe.h" #include "dgnevent.h" #include "fight.h" +#include "ghost.h" #include "hiscores.h" #include "it_use2.h" #include "itemname.h" @@ -1885,7 +1886,7 @@ static void handle_behaviour(monsters *mon) mon->foe = you.pet_target; } - // instead berserkers attack nearest monsters + // instead berserkers attack nearest monsters. if (mon->has_ench(ENCH_BERSERK) && (mon->foe == MHITNOT || isFriendly && mon->foe == MHITYOU)) { @@ -2431,7 +2432,7 @@ static void handle_nearby_ability(monsters *monster) mons_speaks(monster); } - if (monster_can_submerge(monster->type, grd[monster->x][monster->y]) + if (monster_can_submerge(monster, grd[monster->x][monster->y]) && ( !player_beheld_by(monster) // no submerging if player entranced && (one_chance_in(5) || ((grid_distance( monster->x, monster->y, @@ -2492,7 +2493,7 @@ static void handle_nearby_ability(monsters *monster) break; case MONS_PANDEMONIUM_DEMON: - if (monster->ghost->values[ GVAL_DEMONLORD_CYCLE_COLOUR ]) + if (monster->ghost->cycle_colours) monster->colour = random_colour(); break; @@ -3676,7 +3677,7 @@ static bool handle_spell( monsters *monster, bolt & beem ) return (false); } else if (monster->type == MONS_PANDEMONIUM_DEMON - && !monster->ghost->values[ GVAL_DEMONLORD_SPELLCASTER ]) + && !monster->ghost->spellcaster) { return (false); } @@ -4134,7 +4135,7 @@ static void monster_regenerate(monsters *monster) return; // Non-land creatures out of their element cannot regenerate. - if (mons_habitat(monster->type) != HT_LAND + if (mons_habitat(monster) != HT_LAND && !monster_habitable_grid(monster, grd(monster->pos()))) { return; @@ -4324,7 +4325,7 @@ static void handle_monster_move(int i, monsters *monster) handle_behaviour(monster); // submerging monsters will hide from clouds - if (monster_can_submerge(monster->type, grd[monster->x][monster->y]) + if (monster_can_submerge(monster, grd[monster->x][monster->y]) && env.cgrid[monster->x][monster->y] != EMPTY_CLOUD) { monster->add_ench(ENCH_SUBMERGED); @@ -4988,7 +4989,7 @@ void mons_check_pool(monsters *mons, killer_type killer, int killnum) mons->name(DESC_CAP_THE).c_str(), (grid == DNGN_LAVA ? "lava" : "water")); - if (grid == DNGN_LAVA && mons_res_fire(mons) > 0) + if (grid == DNGN_LAVA && mons_res_fire(mons) >= 2) grid = DNGN_DEEP_WATER; // Even fire resistant monsters perish in lava, but undead can survive @@ -5025,68 +5026,45 @@ void mons_check_pool(monsters *mons, killer_type killer, int killnum) // returns true for monsters that obviously (to the player) feel // "thematically at home" in a branch // currently used for native monsters recognizing traps -static bool is_native_in_branch(const monsters *monster, const branch_type branch) +static bool is_native_in_branch(const monsters *monster, + const branch_type branch) { switch (branch) { - case BRANCH_ELVEN_HALLS: - if (mons_species(monster->type) == MONS_ELF) - return true; - return false; + case BRANCH_ELVEN_HALLS: + return (mons_species(monster->type) == MONS_ELF); - case BRANCH_ORCISH_MINES: - if (mons_species(monster->type) == MONS_ORC) - return true; - return false; + case BRANCH_ORCISH_MINES: + return (mons_species(monster->type) == MONS_ORC); - case BRANCH_SHOALS: - if (mons_species(monster->type) == MONS_CYCLOPS + case BRANCH_SHOALS: + return (mons_species(monster->type) == MONS_CYCLOPS || mons_species(monster->type) == MONS_MERFOLK - || mons_species(monster->type) == MONS_MERMAID) - { - return true; - } - return false; + || mons_species(monster->type) == MONS_MERMAID); - case BRANCH_SLIME_PITS: - if (mons_species(monster->type) == MONS_JELLY) - return true; - return false; + case BRANCH_SLIME_PITS: + return (mons_species(monster->type) == MONS_JELLY); - case BRANCH_SNAKE_PIT: - if (mons_species(monster->type) == MONS_NAGA - || mons_species(monster->type) == MONS_SNAKE) - { - return true; - } - return false; + case BRANCH_SNAKE_PIT: + return (mons_species(monster->type) == MONS_NAGA + || mons_species(monster->type) == MONS_SNAKE); - case BRANCH_HALL_OF_ZOT: - if (mons_species(monster->type) == MONS_DRACONIAN) - return true; - return false; + case BRANCH_HALL_OF_ZOT: + return (mons_species(monster->type) == MONS_DRACONIAN); - case BRANCH_TOMB: - if (mons_species(monster->type) == MONS_MUMMY) - return true; - return false; + case BRANCH_TOMB: + return (mons_species(monster->type) == MONS_MUMMY); - case BRANCH_HIVE: - if (monster->type == MONS_KILLER_BEE_LARVA + case BRANCH_HIVE: + return (monster->type == MONS_KILLER_BEE_LARVA || monster->type == MONS_KILLER_BEE - || monster->type == MONS_QUEEN_BEE) - { - return true; - } - return false; + || monster->type == MONS_QUEEN_BEE); - case BRANCH_HALL_OF_BLADES: - if (monster->type == MONS_DANCING_WEAPON) - return true; - return false; + case BRANCH_HALL_OF_BLADES: + return (monster->type == MONS_DANCING_WEAPON); - default: - return false; + default: + return false; } } @@ -5268,7 +5246,7 @@ bool mon_can_move_to_pos(const monsters *monster, const int count_x, } const dungeon_feature_type target_grid = grd[targ_x][targ_y]; - const habitat_type habitat = mons_habitat(monster->type); + const habitat_type habitat = mons_habitat(monster); // effectively slows down monster movement across water. // Fire elementals can't cross at all. @@ -5405,7 +5383,7 @@ bool mon_can_move_to_pos(const monsters *monster, const int count_x, return (mons_res_miasma(monster) > 0); case CLOUD_FIRE: - if (mons_res_fire(monster) > 0) + if (mons_res_fire(monster) > 1) return true; if (monster->hit_points >= 15 + random2avg(46, 5)) @@ -5422,7 +5400,7 @@ bool mon_can_move_to_pos(const monsters *monster, const int count_x, break; case CLOUD_COLD: - if (mons_res_cold(monster) > 0) + if (mons_res_cold(monster) > 1) return true; if (monster->hit_points >= 15 + random2avg(46, 5)) @@ -5469,7 +5447,7 @@ static bool monster_move(monsters *monster) int count_x, count_y, count; int okmove = DNGN_SHALLOW_WATER; // what does this actually do? - const habitat_type habitat = mons_habitat(monster->type); + const habitat_type habitat = mons_habitat(monster); bool deep_water_available = false; // Berserking monsters make a lot of racket @@ -5917,15 +5895,11 @@ static void mons_in_cloud(monsters *monster) simple_monster_message(monster, " is engulfed in flame!"); - if (mons_res_fire(monster) > 0) - return; - - hurted += ((random2avg(16, 3) + 6) * 10) / speed; - - if (mons_res_fire(monster) < 0) - hurted += (random2(15) * 10) / speed; + hurted += + resist_adjust_damage( monster, + monster->res_fire(), + ((random2avg(16, 3) + 6) * 10) / speed ); - // remember that the above is in addition to the other you.damage. hurted -= random2(1 + monster->ac); break; // to damage routine at end {dlb} @@ -5949,15 +5923,11 @@ static void mons_in_cloud(monsters *monster) case CLOUD_COLD: simple_monster_message(monster, " is engulfed in freezing vapours!"); - if (mons_res_cold(monster) > 0) - return; - - hurted += ((6 + random2avg(16, 3)) * 10) / speed; + hurted += + resist_adjust_damage( monster, + monster->res_cold(), + ((6 + random2avg(16, 3)) * 10) / speed ); - if (mons_res_cold(monster) < 0) - hurted += (random2(15) * 10) / speed; - - // remember that the above is in addition to the other damage. hurted -= random2(1 + monster->ac); break; // to damage routine at end {dlb} @@ -5987,14 +5957,12 @@ static void mons_in_cloud(monsters *monster) simple_monster_message(monster, " is engulfed in steam!"); - if (mons_res_fire(monster) > 0) - return; - const int steam_base_damage = steam_cloud_damage(cloud); - hurted += (random2avg(steam_base_damage, 2) * 10) / speed; - - if (mons_res_fire(monster) < 0) - hurted += (random2(steam_base_damage / 2 + 1) * 10) / speed; + hurted += + resist_adjust_damage( + monster, + monster->res_steam(), + (random2avg(steam_base_damage, 2) * 10) / speed); hurted -= random2(1 + monster->ac); break; // to damage routine at end {dlb} diff --git a/crawl-ref/source/ouch.cc b/crawl-ref/source/ouch.cc index 2d28cd9465..2740c81c40 100644 --- a/crawl-ref/source/ouch.cc +++ b/crawl-ref/source/ouch.cc @@ -923,11 +923,18 @@ static std::string morgue_name(time_t when_crawl_got_even) #endif // SHORT_FILE_NAMES } -void end_game( struct scorefile_entry &se ) +void end_game( scorefile_entry &se ) { - int i; bool dead = true; + if (!dump_char( morgue_name(se.death_time), !dead, true, &se )) + { + mpr("Char dump unsuccessful! Sorry about that."); + if (!crawl_state.seen_hups) + more(); + clrscr(); + } + if (se.death_type == KILLED_BY_LEAVING || se.death_type == KILLED_BY_QUITTING || se.death_type == KILLED_BY_WINNING) @@ -975,7 +982,7 @@ void end_game( struct scorefile_entry &se ) const int num_suffixes = sizeof(suffixes) / sizeof(const char*); - for ( i = 0; i < num_suffixes; ++i ) { + for (int i = 0; i < num_suffixes; ++i) { std::string tmpname = basename + suffixes[i]; unlink( tmpname.c_str() ); } @@ -1000,10 +1007,10 @@ void end_game( struct scorefile_entry &se ) if (!crawl_state.seen_hups) more(); - for (i = 0; i < ENDOFPACK; i++) + for (int i = 0; i < ENDOFPACK; i++) set_ident_flags( you.inv[i], ISFLAG_IDENT_MASK ); - for (i = 0; i < ENDOFPACK; i++) + for (int i = 0; i < ENDOFPACK; i++) { if (you.inv[i].base_type != 0) { @@ -1016,14 +1023,6 @@ void end_game( struct scorefile_entry &se ) textcolor( LIGHTGREY ); clrscr(); - if (!dump_char( morgue_name(se.death_time), !dead, true, &se )) - { - mpr("Char dump unsuccessful! Sorry about that."); - if (!crawl_state.seen_hups) - more(); - clrscr(); - } - clrscr(); cprintf( "Goodbye, %s.", you.your_name ); cprintf( EOL EOL " " ); // Space padding where # would go in list format diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index bb2c592c7d..bc8348e39f 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -5152,6 +5152,12 @@ actor::~actor() { } +bool actor::has_equipped(equipment_type eq, int sub_type) const +{ + const item_def *item = slot_item(eq); + return (item && item->sub_type == sub_type); +} + bool actor::will_trigger_shaft() const { return (!airborne() && total_weight() > 0 && is_valid_shaft_level()); @@ -5674,11 +5680,6 @@ item_def *player::slot_item(equipment_type eq) return (item == -1? NULL : &inv[item]); } -const item_def *player::slot_item(equipment_type eq) const -{ - return const_cast<player*>(this)->slot_item(eq); -} - // Returns the item in the player's weapon slot. item_def *player::weapon(int /* which_attack */) { @@ -5718,6 +5719,11 @@ int player::id() const return (-1); } +int player::get_experience_level() const +{ + return (experience_level); +} + bool player::alive() const { // Simplistic, but if the player dies the game is over anyway, so @@ -5941,6 +5947,11 @@ int player::res_fire() const return (player_res_fire()); } +int player::res_steam() const +{ + return (player_res_steam()); +} + int player::res_cold() const { return (player_res_cold()); diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc index c962301856..041429135a 100644 --- a/crawl-ref/source/tags.cc +++ b/crawl-ref/source/tags.cc @@ -75,6 +75,7 @@ #include "enum.h" #include "externs.h" #include "files.h" +#include "ghost.h" #include "itemname.h" #include "itemprop.h" #include "mapmark.h" @@ -132,6 +133,13 @@ static void tag_read_ghost(tagHeader &th, char minorVersion); static void marshallGhost(tagHeader &th, const ghost_demon &ghost); static ghost_demon unmarshallGhost( tagHeader &th ); + +static void marshallResists(tagHeader &, const mon_resist_def &); +static void unmarshallResists(tagHeader &, mon_resist_def &); + +static void marshallSpells(tagHeader &, const monster_spells &); +static void unmarshallSpells(tagHeader &, monster_spells &); + static void marshall_monster(tagHeader &th, const monsters &m); static void unmarshall_monster(tagHeader &th, monsters &m); @@ -1757,9 +1765,7 @@ static void marshall_monster(tagHeader &th, const monsters &m) for (int j = 0; j < NUM_MONSTER_SLOTS; j++) marshallShort(th, m.inv[j]); - for (int j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j) - marshallShort(th, m.spells[j]); - + marshallSpells(th, m.spells); marshallByte(th, m.god); if (m.type == MONS_PLAYER_GHOST || m.type == MONS_PANDEMONIUM_DEMON) @@ -2022,8 +2028,7 @@ static void unmarshall_monster(tagHeader &th, monsters &m) for (int j = 0; j < NUM_MONSTER_SLOTS; j++) m.inv[j] = unmarshallShort(th); - for (int j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j) - m.spells[j] = static_cast<spell_type>( unmarshallShort(th) ); + unmarshallSpells(th, m.spells); m.god = (god_type) unmarshallByte(th); @@ -2210,24 +2215,74 @@ static void tag_missing_level_tiles() // ------------------------------- ghost tags ---------------------------- // -static void marshallGhost(tagHeader &th, const ghost_demon &ghost) +static void marshallResists(tagHeader &th, const mon_resist_def &res) { - marshallString(th, ghost.name.c_str(), 20); + marshallByte(th, res.elec); + marshallByte(th, res.poison); + marshallByte(th, res.fire); + marshallByte(th, res.steam); + marshallByte(th, res.cold); + marshallByte(th, res.hellfire); + marshallByte(th, res.asphyx); + marshallByte(th, res.acid); + marshallByte(th, res.sticky_flame); + marshallByte(th, res.pierce); + marshallByte(th, res.slice); + marshallByte(th, res.bludgeon); +} - // how many ghost values? - marshallByte(th, NUM_GHOST_VALUES); +static void unmarshallResists(tagHeader &th, mon_resist_def &res) +{ + res.elec = unmarshallByte(th); + res.poison = unmarshallByte(th); + res.fire = unmarshallByte(th); + res.steam = unmarshallByte(th); + res.cold = unmarshallByte(th); + res.hellfire = unmarshallByte(th); + res.asphyx = unmarshallByte(th); + res.acid = unmarshallByte(th); + res.sticky_flame = unmarshallByte(th); + res.pierce = unmarshallByte(th); + res.slice = unmarshallByte(th); + res.bludgeon = unmarshallByte(th); +} - for (int i = 0; i < NUM_GHOST_VALUES; i++) - marshallShort( th, ghost.values[i] ); +static void marshallSpells(tagHeader &th, const monster_spells &spells) +{ + for (int j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j) + marshallShort(th, spells[j]); } -static void tag_construct_ghost(tagHeader &th) +static void unmarshallSpells(tagHeader &th, monster_spells &spells) { - // How many ghosts? - marshallShort(th, ghosts.size()); + for (int j = 0; j < NUM_MONSTER_SPELL_SLOTS; ++j) + spells[j] = static_cast<spell_type>( unmarshallShort(th) ); +} - for (int i = 0, size = ghosts.size(); i < size; ++i) - marshallGhost(th, ghosts[i]); +static void marshallGhost(tagHeader &th, const ghost_demon &ghost) +{ + marshallString(th, ghost.name.c_str(), 20); + + marshallShort(th, ghost.species); + marshallShort(th, ghost.job); + marshallShort(th, ghost.best_skill); + marshallShort(th, ghost.best_skill_level); + marshallShort(th, ghost.xl); + marshallShort(th, ghost.max_hp); + marshallShort(th, ghost.ev); + marshallShort(th, ghost.ac); + marshallShort(th, ghost.damage); + marshallShort(th, ghost.speed); + marshallByte(th, ghost.see_invis); + marshallShort(th, ghost.brand); + + marshallResists(th, ghost.resists); + + marshallByte(th, ghost.spellcaster); + marshallByte(th, ghost.cycle_colours); + marshallShort(th, ghost.fly); + + marshallSpells(th, ghost.spells); } static ghost_demon unmarshallGhost( tagHeader &th ) @@ -2236,18 +2291,39 @@ static ghost_demon unmarshallGhost( tagHeader &th ) ghost.name = unmarshallString(th, 20); - // how many ghost values? - int count_c = unmarshallByte(th); - - if (count_c > NUM_GHOST_VALUES) - count_c = NUM_GHOST_VALUES; + ghost.species = static_cast<species_type>( unmarshallShort(th) ); + ghost.job = static_cast<job_type>( unmarshallShort(th) ); + ghost.best_skill = static_cast<skill_type>( unmarshallShort(th) ); + ghost.best_skill_level = unmarshallShort(th); + ghost.xl = unmarshallShort(th); + ghost.max_hp = unmarshallShort(th); + ghost.ev = unmarshallShort(th); + ghost.ac = unmarshallShort(th); + ghost.damage = unmarshallShort(th); + ghost.speed = unmarshallShort(th); + ghost.see_invis = unmarshallByte(th); + ghost.brand = static_cast<brand_type>( unmarshallShort(th) ); + + unmarshallResists(th, ghost.resists); + + ghost.spellcaster = unmarshallByte(th); + ghost.cycle_colours = unmarshallByte(th); + ghost.fly = static_cast<flight_type>( unmarshallShort(th) ); + + unmarshallSpells(th, ghost.spells); - for (int i = 0; i < count_c; i++) - ghost.values[i] = unmarshallShort(th); - return (ghost); } +static void tag_construct_ghost(tagHeader &th) +{ + // How many ghosts? + marshallShort(th, ghosts.size()); + + for (int i = 0, size = ghosts.size(); i < size; ++i) + marshallGhost(th, ghosts[i]); +} + static void tag_read_ghost(tagHeader &th, char minorVersion) { int nghosts = unmarshallShort(th); diff --git a/crawl-ref/source/view.cc b/crawl-ref/source/view.cc index cf22365b0a..f1a3deb2bc 100644 --- a/crawl-ref/source/view.cc +++ b/crawl-ref/source/view.cc @@ -48,6 +48,7 @@ #include "direct.h" #include "dungeon.h" #include "format.h" +#include "ghost.h" #include "initfile.h" #include "itemprop.h" #include "luadgn.h" @@ -912,7 +913,7 @@ void handle_monster_shouts(monsters* monster, bool force) else if (monster->type == MONS_PLAYER_GHOST) { const ghost_demon &ghost = *(monster->ghost); - std::string ghost_class = get_class_name(ghost.values[GVAL_CLASS]); + std::string ghost_class = get_class_name(ghost.job); key = ghost_class + " player ghost"; |