diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8190e9c55dd..2dcb9ee1536 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -310,13 +310,39 @@ jobs: auth_header="$(git config --local --get http.https://github.com/.extraheader)" git submodule sync --recursive git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1 - - name: Python install bug workaround # https://github.com/actions/setup-python/issues/577 + # Some extremely messy interactions between default runner setup and what + # brew may or may not do here. Many closed issues around this, here's one + # where a github actions contributor more or less says they won't fix it: + # https://github.com/actions/runner-images/issues/9471. The issues comes + # and goes depending on runner image versions. I believe I originally + # found the workaround step below somewhere in: + # https://github.com/actions/setup-python/issues/577 + # We have the problem because libpng has python as a dep. + # * Scenario 1: libpng forces brew to install python in `/usr/local/bin`. + # Then we need to clear out the pre-provided symlinks or brew will fail. + # Further steps use newly installed python. + # * Scenario 2: libpng is up-to-date in the current runner. For this case, + # clearing out the links is safe because the runner's default path + # should still include + # `/Library/Frameworks/Python.framework/Versions/Current/bin/python` + # (with a lower priority than `/usr/local/bin`) and pre-installed + # python remains available. + # * For both scenarios, we should install pyyaml via pip, not brew, so + # that it is installed for the correct python version. Installing it + # via `brew` doesn't work right in scenario 2. + - name: Python install bug workaround run: | find /usr/local/bin -type l -exec sh -c 'readlink -f "$1" \ | grep -q ^/Library/Frameworks/Python.framework/Versions/' _ {} \; -exec rm -v {} \; - name: Install Dependencies run: | - brew install pkg-config libpng pyyaml + brew install pkg-config libpng + - name: Install pyyaml + run: pip3 install pyyaml --break-system-packages + - name: diagnostic + run: | + echo $PATH + which python - name: Setup ccache uses: hendrikmuhs/ccache-action@v1.2 with: diff --git a/crawl-ref/CREDITS.txt b/crawl-ref/CREDITS.txt index d160c7e8bb1..7f1cb7c1629 100644 --- a/crawl-ref/CREDITS.txt +++ b/crawl-ref/CREDITS.txt @@ -271,6 +271,7 @@ István Markó Gabriel Marks Jean Martel + meckryl James 'Eronarn' Meickle Jacob Meigs mgdelmonte diff --git a/crawl-ref/docs/changelog.txt b/crawl-ref/docs/changelog.txt index 360fc254ddb..0316562b631 100644 --- a/crawl-ref/docs/changelog.txt +++ b/crawl-ref/docs/changelog.txt @@ -1,5 +1,5 @@ Stone Soup 0.31 (20240119) -------------------------- +-------------------------- Highlights ---------- diff --git a/crawl-ref/docs/crawl_manual.rst b/crawl-ref/docs/crawl_manual.rst index b994f673cab..c509f47ad2f 100644 --- a/crawl-ref/docs/crawl_manual.rst +++ b/crawl-ref/docs/crawl_manual.rst @@ -1634,7 +1634,7 @@ Deep Elves (DE) Armataurs (At) The Armataurs are a large, scaled mammalian species, walking on four feet and swinging a powerful tail behind them. Their elephant-back armies - terrorize the lands outside the Dungeon. + terrorise the lands outside the Dungeon. Armataurs instinctively roll when moving toward foes, getting a free move and regenerating magic. They have great aptitudes with armour and shields, though diff --git a/crawl-ref/docs/develop/levels/syntax.txt b/crawl-ref/docs/develop/levels/syntax.txt index 86f082703d0..656aad0bbe1 100644 --- a/crawl-ref/docs/develop/levels/syntax.txt +++ b/crawl-ref/docs/develop/levels/syntax.txt @@ -1411,7 +1411,7 @@ KMASK: Z = no_monster_gen * "no_wall_fixup": Prevents (rock) walls from being converted to the level's default wall type, such as in Vaults. * "opaque": Overrides the "transparent" tag for this particular - symbol. The symbol is considered impassible for level-building + symbol. The symbol is considered impassable for level-building purposes. A "transparent" vault with a deliberately disconnected region must use this mask on the disconnected part. diff --git a/crawl-ref/docs/develop/spells.txt b/crawl-ref/docs/develop/spells.txt index bb3faaec042..4c3f0a420da 100644 --- a/crawl-ref/docs/develop/spells.txt +++ b/crawl-ref/docs/develop/spells.txt @@ -110,8 +110,8 @@ via portals. This includes partial or complete movement of the caster's body, usually in short distances compared to consumables (Blink, Vhi's Electric Charge); the creation of loose or controlled portals (Malign Gateway, Passage of Golubria); -and moving others entirely, whether short distances (Gell's Gravitas) or far -(Teleport Other). +and moving others entirely, whether short distances (Maxwell's Portable +Piledriver) or far (Teleport Other). This also includes spells which cause items to be moved from one location to another, such as Apportation or Dimensional Bullseye. diff --git a/crawl-ref/source/Makefile.obj b/crawl-ref/source/Makefile.obj index f95f45dd0b3..cc2a914b129 100644 --- a/crawl-ref/source/Makefile.obj +++ b/crawl-ref/source/Makefile.obj @@ -146,6 +146,7 @@ message.o \ misc.o \ mon-abil.o \ mon-act.o \ +mon-aura.o \ mon-behv.o \ mon-cast.o \ mon-clone.o \ diff --git a/crawl-ref/source/ability.cc b/crawl-ref/source/ability.cc index 30ad9acd923..83c3fdf8a6f 100644 --- a/crawl-ref/source/ability.cc +++ b/crawl-ref/source/ability.cc @@ -3905,9 +3905,8 @@ bool player_has_ability(ability_type abil, bool include_unusable) && !you.vampire_alive && you.form != transformation::bat; case ABIL_BREATHE_FIRE: - // red draconian handled before the switch return you.form == transformation::dragon - && species::dragon_form(you.species) == MONS_FIRE_DRAGON; + && !species::is_draconian(you.species); case ABIL_BLINKBOLT: return you.form == transformation::storm; case ABIL_SIPHON_ESSENCE: diff --git a/crawl-ref/source/actor.cc b/crawl-ref/source/actor.cc index 37a646b865a..c63c7c8fcbe 100644 --- a/crawl-ref/source/actor.cc +++ b/crawl-ref/source/actor.cc @@ -935,33 +935,6 @@ string actor::describe_props() const return oss.str(); } -/** - * Is the actor currently being slowed by a torpor snail? - */ -bool actor::torpor_slowed() const -{ - if (!props.exists(TORPOR_SLOWED_KEY) || is_sanctuary(pos()) - || is_stationary() - || stasis()) - { - return false; - } - - for (monster_near_iterator ri(pos(), LOS_SOLID_SEE); ri; ++ri) - { - const monster *mons = *ri; - if (mons && mons->type == MONS_TORPOR_SNAIL - && !is_sanctuary(mons->pos()) - && !mons_aligned(mons, this) - && !mons->props.exists(KIKU_WRETCH_KEY)) - { - return true; - } - } - - return false; -} - string actor::resist_margin_phrase(int margin) const { if (willpower() == WILL_INVULN) diff --git a/crawl-ref/source/actor.h b/crawl-ref/source/actor.h index fa6d1bc7844..5e22e12404b 100644 --- a/crawl-ref/source/actor.h +++ b/crawl-ref/source/actor.h @@ -230,9 +230,6 @@ class actor bool temp = true) const = 0; int skill_rdiv(skill_type sk, int mult = 1, int div = 1) const; -#define TORPOR_SLOWED_KEY "torpor_slowed" - bool torpor_slowed() const; - virtual int heads() const = 0; virtual int stat_hp() const = 0; diff --git a/crawl-ref/source/android-project/jni/src/Android.mk b/crawl-ref/source/android-project/jni/src/Android.mk index 4948ee1ecf7..ac747808c3c 100644 --- a/crawl-ref/source/android-project/jni/src/Android.mk +++ b/crawl-ref/source/android-project/jni/src/Android.mk @@ -166,6 +166,7 @@ LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c \ $(CRAWL_PATH)/misc.cc \ $(CRAWL_PATH)/mon-abil.cc \ $(CRAWL_PATH)/mon-act.cc \ + $(CRAWL_PATH)/mon-aura.cc \ $(CRAWL_PATH)/mon-behv.cc \ $(CRAWL_PATH)/mon-cast.cc \ $(CRAWL_PATH)/mon-clone.cc \ diff --git a/crawl-ref/source/areas.cc b/crawl-ref/source/areas.cc index cc1c0aed088..f961babaa51 100644 --- a/crawl-ref/source/areas.cc +++ b/crawl-ref/source/areas.cc @@ -29,11 +29,10 @@ #include "traps.h" #include "travel.h" -/// Bitmasks for area properties +/// Bitmasks for area properties that center on actors enum class areaprop { - sanctuary_1 = (1 << 0), - sanctuary_2 = (1 << 1), + // 0 and 1 were sanctuary, now unused silence = (1 << 2), halo = (1 << 3), liquified = (1 << 4), @@ -42,7 +41,7 @@ enum class areaprop umbra = (1 << 7), quad = (1 << 8), disjunction = (1 << 9), - soul_aura = (1 << 10), + // 10 was lost soul aura, now unused }; /// Bit field for the area properties DEF_BITFIELD(areaprops, areaprop); @@ -237,10 +236,6 @@ static void _update_agrid() static area_centre_type _get_first_area(const coord_def& f) { areaprops a = _agrid(f); - if (a & areaprop::sanctuary_1) - return area_centre_type::sanctuary; - if (a & areaprop::sanctuary_2) - return area_centre_type::sanctuary; if (a & areaprop::silence) return area_centre_type::silence; if (a & areaprop::halo) diff --git a/crawl-ref/source/art-data.txt b/crawl-ref/source/art-data.txt index a22bbfd51c8..d2e5316601e 100644 --- a/crawl-ref/source/art-data.txt +++ b/crawl-ref/source/art-data.txt @@ -1531,7 +1531,7 @@ DBRAND: Omnireflect: It can block and reflect piercing ranged attacks and NAME: amulet of invisibility OBJ: OBJ_JEWELLERY/AMU_NOTHING COLOUR: WHITE -TILE: urand_cekugob +TILE: urand_amulet_invisibility BOOL: inv, nogen ENUM: THERMIC_ENGINE diff --git a/crawl-ref/source/artefact.cc b/crawl-ref/source/artefact.cc index e03cd762bfa..e6cef1af749 100644 --- a/crawl-ref/source/artefact.cc +++ b/crawl-ref/source/artefact.cc @@ -82,7 +82,10 @@ static bool _god_fits_artefact(const god_type which_god, const item_def &item, switch (which_god) { case GOD_ZIN: - // Lawful god: no mutagenics. + // Lawful god: no chaos or mutagenics. + if (brand == SPWPN_CHAOS) + return false; + if (artefact_property(item, ARTP_CONTAM)) return false; break; @@ -150,6 +153,12 @@ static bool _god_fits_artefact(const god_type which_god, const item_def &item, return false; break; + case GOD_IGNIS: + // Fire god. + if (item.base_type == OBJ_WEAPONS && brand != SPWPN_FLAMING) + return false; + break; + default: break; } diff --git a/crawl-ref/source/beam-type.h b/crawl-ref/source/beam-type.h index ed262dc8c72..4ab678d63d4 100644 --- a/crawl-ref/source/beam-type.h +++ b/crawl-ref/source/beam-type.h @@ -95,7 +95,8 @@ enum beam_type // bolt::flavour BEAM_VITRIFYING_GAZE, BEAM_WEAKNESS, BEAM_CURSE_OF_AGONY, - BEAM_LAST_ENCHANTMENT = BEAM_CURSE_OF_AGONY, + BEAM_RIMEBLIGHT, + BEAM_LAST_ENCHANTMENT = BEAM_RIMEBLIGHT, BEAM_MEPHITIC, BEAM_AIR, diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc index f05fc236d71..ce004f24d4e 100644 --- a/crawl-ref/source/beam.cc +++ b/crawl-ref/source/beam.cc @@ -600,6 +600,12 @@ bool bolt::can_affect_actor(const actor *act) const { return false; } + // Xak'krixis' prisms are smart enough not to affect friendlies + else if (origin_spell == SPELL_FULMINANT_PRISM && thrower == KILL_MON + && act->temp_attitude() == attitude) + { + return false; + } auto cnt = hit_count.find(act->mid); if (cnt != hit_count.end() && cnt->second >= 2) { @@ -2629,6 +2635,31 @@ void bolt::affect_endpoint() } break; + case SPELL_FLASHING_BALESTRA: + { + coord_def spot; + int num_found = 0; + for (adjacent_iterator ai(pos()); ai; ++ai) + { + if (feat_is_solid(env.grid(*ai)) || actor_at(*ai)) + continue; + + if (one_chance_in(++num_found)) + spot = *ai; + } + + if (!spot.origin()) + { + create_monster(mgen_data(MONS_DANCING_WEAPON, + SAME_ATTITUDE(agent(true)->as_monster()), + spot, + agent(true)->as_monster()->foe, + MG_FORCE_PLACE) + .set_summoned(agent(true), 1, SPELL_FLASHING_BALESTRA, GOD_NO_GOD)); + } + } + break; + default: break; } @@ -3099,7 +3130,8 @@ bool bolt::harmless_to_player() const return true; if (origin_spell == SPELL_COMBUSTION_BREATH - || origin_spell == SPELL_NULLIFYING_BREATH) + || origin_spell == SPELL_NULLIFYING_BREATH + || origin_spell == SPELL_RIMEBLIGHT) { return true; } @@ -4302,7 +4334,8 @@ bool bolt::ignores_player() const return true; if (origin_spell == SPELL_COMBUSTION_BREATH - || origin_spell == SPELL_NULLIFYING_BREATH) + || origin_spell == SPELL_NULLIFYING_BREATH + || origin_spell == SPELL_RIMEBLIGHT) { return true; } @@ -5046,6 +5079,20 @@ void bolt::monster_post_hit(monster* mon, int dmg) mon->speed_increment += 10; simple_monster_message(*mon, " is empowered."); } + + // Give electroferric vorticies a little more life if the player is shooting them + if (origin_spell == SPELL_MAGNAVOLT && mon->type == MONS_ELECTROFERRIC_VORTEX) + mon->add_ench(mon_enchant(ENCH_SLOWLY_DYING, 0, nullptr, BASELINE_DELAY * 2)); + + if (origin_spell == SPELL_RIMEBLIGHT) + { + if (mon->holiness() & (MH_NATURAL | MH_DEMONIC | MH_HOLY) + && !mon->has_ench(ENCH_RIMEBLIGHT) + && one_chance_in(2)) + { + apply_rimeblight(*mon, ench_power); + } + } } static int _knockback_dist(spell_type origin, int pow) @@ -5540,6 +5587,15 @@ bool bolt::ignores_monster(const monster* mon) const if (flavour == BEAM_WATER && mon->type == MONS_WATER_ELEMENTAL) return true; + // Rimeblight explosions won't hurt allies OR the monster that is exploding + if (origin_spell == SPELL_RIMEBLIGHT) + return mon->friendly() || mon->pos() == source; + + // Casting this destroys the cannon anyway, but this prevents misleading + // targeter prompts. + if (origin_spell == SPELL_SEISMIC_SHOCKWAVE && mon->type == MONS_SEISMIC_CANNON) + return true; + int summon_type = 0; mon->is_summoned(nullptr, &summon_type); if (flavour == BEAM_QAZLAL && summon_type == MON_SUMM_AID) @@ -5591,6 +5647,7 @@ bool bolt::has_saving_throw() const case BEAM_CONCENTRATE_VENOM: case BEAM_ENFEEBLE: case BEAM_VITRIFYING_GAZE: + case BEAM_RIMEBLIGHT: return false; case BEAM_VULNERABILITY: return !one_chance_in(3); // Ignores will 1/3 of the time @@ -5713,6 +5770,11 @@ bool ench_flavour_affects_monster(actor *agent, beam_type flavour, || !mons_invuln_will(*mon); break; + case BEAM_RIMEBLIGHT: + rc = !mon->has_ench(ENCH_RIMEBLIGHT) + && mon->holiness() & (MH_NATURAL | MH_DEMONIC | MH_HOLY); + break; + default: break; } @@ -6401,6 +6463,15 @@ mon_resist_type bolt::apply_enchantment_to_monster(monster* mon) obvious_effect = true; return MON_AFFECTED; + case BEAM_RIMEBLIGHT: + if (apply_rimeblight(*mon, ench_power, true)) + { + mprf("A stygian plague fills %s body.", + apostrophise(mon->name(DESC_THE)).c_str()); + obvious_effect = true; + } + return MON_AFFECTED; + default: break; } @@ -6962,6 +7033,7 @@ bool bolt::nasty_to(const monster* mon) const case BEAM_MINDBURST: case BEAM_VAMPIRIC_DRAINING: case BEAM_ENFEEBLE: + case BEAM_RIMEBLIGHT: return ench_flavour_affects_monster(agent(), flavour, mon); case BEAM_TUKIMAS_DANCE: return tukima_affects(*mon); // XXX: move to ench_flavour_affects? @@ -7257,6 +7329,7 @@ static string _beam_type_name(beam_type type) case BEAM_CRYSTALLIZING: return "crystallizing"; case BEAM_WARPING: return "spatial disruption"; case BEAM_QAZLAL: return "upheaval targetter"; + case BEAM_RIMEBLIGHT: return "rimeblight"; case NUM_BEAMS: die("invalid beam type"); } diff --git a/crawl-ref/source/book-data.h b/crawl-ref/source/book-data.h index c3e8e51374f..00ade2de1a0 100644 --- a/crawl-ref/source/book-data.h +++ b/crawl-ref/source/book-data.h @@ -86,6 +86,7 @@ static const vector spellbook_templates[] = }, { // Book of Hexes + SPELL_SIGIL_OF_BINDING, SPELL_ANGUISH, SPELL_CAUSE_FEAR, SPELL_ENFEEBLE, @@ -206,7 +207,7 @@ static const vector spellbook_templates[] = { // Book of Geomancy SPELL_SANDBLAST, SPELL_STONE_ARROW, - SPELL_BOULDER + SPELL_PASSWALL, }, #if TAG_MAJOR_VERSION == 34 @@ -272,9 +273,9 @@ static const vector spellbook_templates[] = }, { // Book of Transmutation - SPELL_SUBLIMATION_OF_BLOOD, SPELL_PETRIFY, SPELL_IRRADIATE, + SPELL_FULSOME_FUSILLADE, }, { // Book of Beasts @@ -310,7 +311,7 @@ static const vector spellbook_templates[] = { // Book of Spectacle SPELL_DAZZLING_FLASH, SPELL_STARBURST, - SPELL_MAXWELLS_COUPLING, + SPELL_FULSOME_FUSILLADE, }, { // Book of Winter @@ -330,6 +331,7 @@ static const vector spellbook_templates[] = { // Book of Armaments SPELL_STONE_ARROW, SPELL_ANIMATE_ARMOUR, + SPELL_SEISMIC_CANNONADE, SPELL_LEHUDIBS_CRYSTAL_SPEAR, }, @@ -381,7 +383,6 @@ static const vector spellbook_templates[] = { // Book of the Senses SPELL_DAZZLING_FLASH, SPELL_MEPHITIC_CLOUD, - SPELL_CURSE_OF_AGONY, SPELL_SILENCE, }, @@ -398,9 +399,9 @@ static const vector spellbook_templates[] = }, { // Book of Iron - SPELL_SUMMON_LIGHTNING_SPIRE, SPELL_ANIMATE_ARMOUR, SPELL_LRD, + SPELL_MAGNAVOLT, }, #if TAG_MAJOR_VERSION == 34 @@ -503,21 +504,38 @@ static const vector spellbook_templates[] = { // Maxwell's Memoranda SPELL_PILEDRIVER, - SPELL_ARCJOLT, + SPELL_MAGNAVOLT, SPELL_MAXWELLS_COUPLING, }, { // Book of Movement - SPELL_PASSWALL, SPELL_BLASTMOTE, SPELL_ELECTRIC_CHARGE, - SPELL_BOMBARD, + SPELL_GELLS_GAVOTTE, }, { // Book of Wicked Creation - SPELL_SIGIL_OF_BINDING, SPELL_SIMULACRUM, SPELL_DEATH_CHANNEL, + SPELL_RIMEBLIGHT, +}, + +{ // Compendium of Siegecraft + SPELL_BOULDER, + SPELL_BOMBARD, + SPELL_SEISMIC_CANNONADE, +}, + +{ // Book of Maladies + SPELL_SUBLIMATION_OF_BLOOD, + SPELL_CURSE_OF_AGONY, + SPELL_RIMEBLIGHT, +}, + +{ // Codex of Conductivity + SPELL_SUMMON_LIGHTNING_SPIRE, + SPELL_ARCJOLT, + SPELL_MAXWELLS_COUPLING, }, }; diff --git a/crawl-ref/source/book-type.h b/crawl-ref/source/book-type.h index acf4300c50f..2fcfb75f465 100644 --- a/crawl-ref/source/book-type.h +++ b/crawl-ref/source/book-type.h @@ -118,6 +118,9 @@ enum book_type BOOK_MAXWELL, BOOK_MOVEMENT, BOOK_WICKED_CREATION, + BOOK_SIEGECRAFT, + BOOK_MALADIES, + BOOK_CONDUCTIVITY, NUM_BOOKS }; diff --git a/crawl-ref/source/cloud.cc b/crawl-ref/source/cloud.cc index 38e9277e767..108d96542e4 100644 --- a/crawl-ref/source/cloud.cc +++ b/crawl-ref/source/cloud.cc @@ -628,28 +628,15 @@ static void _maybe_leave_water(const coord_def pos) { ASSERT_IN_BOUNDS(pos); - // Rain clouds can occasionally leave shallow water or deepen it. - if (!one_chance_in(5)) + // Rain clouds can occasionally leave shallow water. + if (env.grid(pos) != DNGN_FLOOR || !one_chance_in(5)) return; - dungeon_feature_type feat = env.grid(pos); - - if (env.grid(pos) == DNGN_FLOOR) - feat = DNGN_SHALLOW_WATER; - else if (env.grid(pos) == DNGN_SHALLOW_WATER && you.pos() != pos - && one_chance_in(3) && !crawl_state.game_is_sprint()) - { - // Don't drown the player! - feat = DNGN_DEEP_WATER; - } - - if (env.grid(pos) != feat) - { - if (you.pos() == pos && you.ground_level()) - mpr("The rain has left you waist-deep in water!"); - temp_change_terrain(pos, feat, random_range(500, 1000), - TERRAIN_CHANGE_FLOOD); - } + if (you.pos() == pos && you.ground_level()) + mpr("The rain has left you waist-deep in water!"); + temp_change_terrain(pos, DNGN_SHALLOW_WATER, + random_range(500, 1000), + TERRAIN_CHANGE_FLOOD); } void delete_cloud(coord_def p) @@ -1460,25 +1447,7 @@ static bool _mons_avoids_cloud(const monster* mons, const cloud_struct& cloud, || mons->attitude == ATT_GOOD_NEUTRAL; case CLOUD_RAIN: - // Fiery monsters dislike the rain. - if (mons->is_fiery() && extra_careful) - return true; - - // We don't care about what's underneath the rain cloud if we can fly. - if (mons->airborne()) - return false; - - // These don't care about deep water. - if (monster_habitable_grid(mons, DNGN_DEEP_WATER)) - return false; - - // This position could become deep water, and they might drown. - if (env.grid(cloud.pos) == DNGN_SHALLOW_WATER - && mons_intel(*mons) > I_BRAINLESS) - { - return true; - } - break; + return !mons->is_fiery() || !extra_careful; default: { @@ -1933,8 +1902,7 @@ static bool _is_chaos_slowable(const actor &defender) if (!mon) return true; - // What, no hasting oklobs? Boo. - return !mons_is_firewood(*mon) && !mon->is_stationary(); + return !mons_is_firewood(*mon); } struct chaos_effect diff --git a/crawl-ref/source/dat/database/gizmo.txt b/crawl-ref/source/dat/database/gizmo.txt index b7c68c80fa6..74ba6301f88 100644 --- a/crawl-ref/source/dat/database/gizmo.txt +++ b/crawl-ref/source/dat/database/gizmo.txt @@ -1,7 +1,7 @@ ###################################################### # Gizmo Names # ------------- -# rand_gizmo.txt: keywords for Coglin gizmos +# gizmo.txt: keywords for Coglin gizmos # # This file contains the lists of keywords (and their # weights) that are used to generate the names of @@ -26,6 +26,8 @@ emergency secondary +tertiary + tungsten bismuth @@ -77,6 +79,12 @@ assembler capacitor +inducer + +widget + +tensor + encabulator # Calvin and Hobbes @@ -95,6 +103,8 @@ giga super +ultra + hydro geo diff --git a/crawl-ref/source/dat/database/help.txt b/crawl-ref/source/dat/database/help.txt index 3b7305022ea..0e20ba0b6cc 100644 --- a/crawl-ref/source/dat/database/help.txt +++ b/crawl-ref/source/dat/database/help.txt @@ -328,7 +328,7 @@ To set the colour a (very) ugly thing takes when it is first created, use: The valid colours are brown, cyan, green, purple, red and white. -A mutant beast can be customized by prefacing it with a maturity level and/or +A mutant beast can be customised by prefacing it with a maturity level and/or hyphen-separated set of traits, as follows: mature shock-weird mutant beast @@ -636,7 +636,7 @@ that is on letter `a`. Keymaps are lower-level key substitutions that replace keys entirely with a sequence of keystrokes. They are recommended for changing keys uniformly to deal with alternative keyboard layouts. Keymaps are contextual, and the -default/"regular" context is used anywhere that a non-specialized context is not +default/"regular" context is used anywhere that a non-specialised context is not active. Macros take the result of keymaps as input. Example: In some European keyboard layouts, the § symbol replaces ~. Therefore, diff --git a/crawl-ref/source/dat/database/monname.txt b/crawl-ref/source/dat/database/monname.txt index 202be6a4ca9..a640fbb63b5 100644 --- a/crawl-ref/source/dat/database/monname.txt +++ b/crawl-ref/source/dat/database/monname.txt @@ -41,12 +41,23 @@ w:12 w:45 @_other_orcish_name_@ %%%% -# some specials for orcish priests -orc high priest name +orc apostle warrior name -@orc priest name@ +w:1 +Orconan + +w:50 +@orc name@ +%%%% +orc apostle wizard name + +w:1 +Simorc Magus + +w:50 +@orc name@ %%%% -orc priest name +orc apostle priest name # Watch out! :p w:1 diff --git a/crawl-ref/source/dat/database/monspeak.txt b/crawl-ref/source/dat/database/monspeak.txt index 31f4f10f047..954b2af1c5e 100644 --- a/crawl-ref/source/dat/database/monspeak.txt +++ b/crawl-ref/source/dat/database/monspeak.txt @@ -1801,17 +1801,23 @@ _Amaemon_rare_ ############ ARACHNE ### Half-human half-spider Arachne -@The_monster@ says @to_foe@, "Come to my web." +@The_monster@ says, "Follow my lead, little ones." + +@The_monster@ says, "I resented this body once, but could any human make what I've made?" + +@The_monster@ snarls, "I will not be driven out again." @The_monster@ says @to_foe@, "Come closer, little fly." -@The_monster@ says @to_foe@, "You'll find yourself a bit attached to my weave." +@The_monster@ says @to_foe@, "I'm done suffering for art; you can suffer instead." + +@The_monster@ says @to_foe@, "My silk is a match for both steel and spell." -@The_monster@ says @to_foe@, "No one escapes my webs." +@The_monster@ says @to_foe@, "I will spin a new home for us all." %%%% Arachne triumphant -@The_monster@ says, "All wrapped up." +@The_monster@ shouts, "This is my domain now; anyone trespasses at their own peril!" %%%% ############ ASTERION ### Minotaur of Makhleb Asterion @@ -2006,6 +2012,14 @@ Blork the orc triumphant # Rainbow Raider winning against Batman @The_monster@ shouts, "I believe in me!" %%%% +_blork_colour_ + +w:20 +@_rainbow_colour_@ + +w:1 +@_misc_colour_@ +%%%% _rainbow_colour_ red @@ -2022,6 +2036,119 @@ indigo violet %%%% +# No black, white, or grey here. +_misc_colour_ + +alizarin crimson + +aubergine + +azure + +beige + +bronze + +brown + +burgundy + +cerise + +cerulean + +charcoal + +chartreuse + +cobalt blue + +copper + +cyan + +dun + +erin + +fuchsia + +fulvous + +gamboge + +glaucous + +goblin blue + +gold + +gridelin + +haint blue + +heliotrope + +indanthrone blue + +lavender + +leaf green + +lilac + +lime green + +magenta + +marigold + +mauve + +mulberry + +ochre + +periwinkle + +phlox + +pink + +purple + +rubric + +saffron + +sepia + +scarlet + +silver + +sinopia + +smaragdine + +sorcerer's violet + +taupe + +teal + +turquoise + +ultramarine + +umber + +vermilion + +viridian + +zaffre +%%%% _Blork_common_ # some chance of generic orc speech @@ -2030,9 +2157,9 @@ w:20 VISUAL:@The_monster@ grins serenely. -VISUAL:@The_monster@ is briefly surrounded by a halo of @_rainbow_colour_@ light. +VISUAL:@The_monster@ is briefly surrounded by a halo of @_blork_colour_@ light. -VISUAL:@The_monster@'s eyes glow @_rainbow_colour_@. +VISUAL:@The_monster@'s eyes glow @_blork_colour_@. @The_monster@ says @to_foe@, "You need a new perspective." @@ -2065,7 +2192,7 @@ _Blork_rare_ # Calvin and Hobbes @The_monster@ says @to_foe@, "The problem is, you see everything in terms of black and white." -@The_monster@ mutters, "Why is everything @_rainbow_colour_@ all of a sudden?" +@The_monster@ mutters, "Why is everything @_blork_colour_@ all of a sudden?" @The_monster@ says, "Things are no longer grey! Not since my great insight!" %%%% @@ -2689,6 +2816,7 @@ _dissolution_speech_ @The_monster@ says, "Acid to acid, slime to slime." +w:5 @_dissolution_rare_@ %%%% _dissolution_rare_ @@ -3234,12 +3362,14 @@ _yredelemnul_Donald_ @The_monster@ says, "I hate zombies." -@The_monster@ says, "Zombies? Skeletons? Don't you get bored without any real company?" +@The_monster@ says, "Zombies? Skeletons? Spectral things? Don't you get bored without any real company?" @The_monster@ says, "Angels painted black are so last century." @The_monster@ says, "If you wanted the light to be dimmer, you could have said so." +@The_monster@ says, "What kind of torch can only be lit once per level?" + @The_monster@ says, "How does that torch stay lit when you throw its fire all over?" @The_monster@ says, "I hate being shackled." @@ -5386,7 +5516,7 @@ _Josephina_rare_ VISUAL:@The_monster@ grinds @possessive@ teeth. -VISUAL:One of @The_monster@'s icy fingers falls off. +VISUAL:One of @the_monster@'s icy fingers falls off. @The_monster@ says @to_foe@, "I'll make a rug of your skin." @@ -5580,7 +5710,7 @@ VISUAL:@The_monster@ practices a pose. VISUAL:@The_monster@ smiles, but it's obviously fake. -@The_monster@ says @to_foe@, "Leave now, and tell all of Margery's mercy!" +@The_monster@ says @to_foe@, "Leave now, and tell all of Maggie's mercy!" @The_monster@ says @to_foe@, "Fear not. I will immortalise you in the tale of my first kill." @@ -6826,7 +6956,7 @@ VISUAL:@The_monster@ beckons gently. VISUAL:@The_monster@ smiles kindly. -VISUAL:A flash of anger passes briefly over @The_monster@'s face. +VISUAL:A flash of anger passes briefly over @the_monster@'s face. VISUAL:@The_monster@ draws @possessive@ long claws delicately across @possessive@ gut. %%%% @@ -7138,6 +7268,52 @@ Vashnia triumphant @The_monster@ says, "All clear. Let's check for more." ## END Vashnia ## %%%% +############ Xak'krixis ### A formicid expeditionary alchemist +Xak'krixis + +@The_monster@ ponders what role @player_genus_plural@ plays in the local ecosystem. + +@The_monster@ says, "I'll be sure to include this battle in my report!" + +@The_monster@ says, "We all must contribute to the advancement of science." + +@The_monster@ shouts @to_foe@, "You're giving me such wonderful data!" + +VISUAL:@The_monster@ furiously scribbles notes with a free limb. + +VISUAL:@The_monster@'s mandibles click thoughtfully. + +@The_monster@ says, "{{ + if you.skill("Alchemy") >= 10 then + return "Ah, a fellow alchemist! Surely you'll appreciate what I've learned here." + else + return "Allow me to demonstrate my discoveries." + end }}" @player_only@ + +w:20 +@_Xak'krixis_monster_thoughts_@ + +%%%% +_Xak'krixis_monster_thoughts_ + +@The_monster@ muses, "Oh, to leap as freely as a jumping spider..." + +@The_monster@ muses, "Oh, to fly as furtive as a ghost moth..." + +@The_monster@ muses, "Oh, to shine as brightly as a sun moth..." + +@The_monster@ muses, "Oh, to be as impervious as an emperor scorpion..." + +@The_monster@ muses, "Oh, to inspire such passion as a moth of wrath..." + +@The_monster@ grumbles, "Oh, to ruin all my specimens like a blasted pharaoh ant..." + +%%%% +Xak'krixis triumphant + +@The_monster@ says, "I'll be sure to list you as a co-author when my work is published!" +## END Xak'krixis ## +%%%% ############ XTAHUA ### An ancient fire dragon Xtahua @@ -7435,6 +7611,7 @@ Killer Klown @_Killer_Klown_common_@ +w:5 @_Killer_Klown_rare_@ %%%% _Killer_Klown_common_ @@ -7541,6 +7718,15 @@ default orange crystal statue VISUAL:@The_monster@ glitters topaz in the dim dungeon light. %%%% +################## Seismic cannon ########################## +default seismic cannon + +@The_monster@ rumbles loudly. + +VISUAL:The ground beneath @the_monster@ churns. + +@The_monster@ makes a grinding noise. +%%%% ################## Profane servitor ######################## # Empty, to prevent falling back to holy-specific cap-A # speech. @@ -7621,6 +7807,7 @@ _tormentor_ @_tormentor_common_@ +w:5 @_tormentor_rare_@ %%%% _tormentor_common_ @@ -8693,3 +8880,98 @@ VISUAL:@The_monster@'s armour gleams in the torchlight. w:1 VISUAL:@The_monster@, chosen of Okawaru, glows resplendent with favour. %%%% +################################# +# Monsters who worship Nemelex Xobeh +################################# +goblin sharper + +VISUAL:@The_monster@ fiddles with @possessive@ portable altar. + +VISUAL:@The_monster@ shuffles one of @possessive@ decks. + +VISUAL:@The_monster@ fans some cards from one of @possessive@ decks. + +VISUAL:@The_monster@'s deck briefly glows with a rainbow of weird colours. + +# Kenny Rogers, "The Gambler" +@The_monster@ chants, "Know when to hold 'em, know when to fold 'em..." + +@The_monster@ praises @possessive_God@. + +@The_monster@ grins. "So many cards! Brought to you by @my_God@!" + +@The_monster@ draws Degeneration. "So many ways to mutate things!" + +@The_monster@ draws the Orb. "Orbs of destruction hurt!" + +@The_monster@ draws Pain. "Necromancy hurts!" + +@The_monster@ draws the Storm. "Thunder and lightning everywhere!" + +@The_monster@ draws Wild Magic. "So many ways magic can go wrong!" + +@The_monster@ draws Vitriol. "Acid hurts!" + +@The_monster@ draws the Cloud. "Smoke, smoke everywhere!" + +@The_monster@ draws the Elixir. "Healing and magic? Woohoo!" + +@The_monster@ draws Exile. "The Abyss is a horrible place!" + +@The_monster@ draws the Tomb. "I don't want to be sealed in with no windows!" + +# Kenny Rogers, "The Gambler" +@The_monster@ draws Velocity. "Know when to walk away, know when to run." + +@The_monster@ draws the Dance. "Hope the weapon's aim is true!" + +@The_monster@ draws the Elements. "Elemental beasts everywhere!" + +@The_monster@ draws the Illusion. "I have a twin now!" + +@The_monster@ draws the Pentagram. "Hope the demon is friendly!" + +@The_monster@ draws the Rangers. "New friends who can shoot!" + +@The_monster@ draws the Swarm. "Bees, bees everywhere!" + +@The_monster@ fumbles a card and drops it. "Whoops!" + +@The_monster@ draws @_misplaced_card_@. "Wait, this doesn't belong here!" +%%%% +_misplaced_card_ + +w:20 +@_deck_of_oddities_card_@ + +the Ace of Spades + +the Old Maid + +# Tarot +the Hanged Man + +# Tarot via the Simpsons +the Happy Squirrel + +# Uno +Wild Draw Four +%%%% +# Nemelex's old Deck of Oddities +_deck_of_oddities_card_ + +the Bargain + +the Curse + +Famine + +Focus + +the Feast + +the Genie + +the Helix + +Xom diff --git a/crawl-ref/source/dat/database/monspell.txt b/crawl-ref/source/dat/database/monspell.txt index 76560220ab7..191e63480d1 100644 --- a/crawl-ref/source/dat/database/monspell.txt +++ b/crawl-ref/source/dat/database/monspell.txt @@ -542,7 +542,7 @@ Primal Wave Norris cast %%%% Draining Gaze Norris cast -Something numinous underneath @The_monster@'s waves suddenly gazes at @target@. +Something numinous underneath @the_monster@'s waves suddenly gazes at @target@. @The_monster@'s eyes shimmer, overwhelming @target@ with visions from beyond. %%%% @@ -722,13 +722,13 @@ Crystallizing Shot crystal guardian cast %%%% Rebounding Blaze thermic dynamo cast -A wild whirl of fire shoots out from @The_monster@ @at@ @target@. +A wild whirl of fire shoots out from @the_monster@ @at@ @target@. @The_monster@ overcharges itself and releases excess heat @at@ @target@. %%%% Rebounding Chill thermic dynamo cast -A wild whirl of ice shoots out from @The_monster@ @at@ @target@. +A wild whirl of ice shoots out from @the_monster@ @at@ @target@. @The_monster@ overflows with ice and vents excess cold @at@ @target@. %%%% @@ -936,6 +936,14 @@ Vanquished Vanguard Nergalle cast @The_monster@ cries out, "Spectres! Fight for the home you helped make!" +%%%% +force lance polterguardian cast + +@The_monster@ focuses its will @at@ @target@. +%%%% +flashing balestra undying armoury cast + +A weapon leaps out from @the_monster@ and lunges @at@ @target@! %%%% ######################################################################## # Monster species and genus messages. @@ -1035,6 +1043,10 @@ bombardier beetle cast sun moth cast @The_monster@'s wings beat fiercely. +%%%% +seismic cannon cast + +@The_monster@ launches a salvo of rock. ########################################################################### # Generic messages for priests and wizards which have been polymorphed into # something without hands. diff --git a/crawl-ref/source/dat/database/montitle.txt b/crawl-ref/source/dat/database/montitle.txt index 6bd1c1e3371..db21335062b 100644 --- a/crawl-ref/source/dat/database/montitle.txt +++ b/crawl-ref/source/dat/database/montitle.txt @@ -21,7 +21,7 @@ Antaeus, Guardian of Cocytus %%%% Arachne title -Arachne the Weaver +Arachne the Outcast %%%% Asmodeus title @@ -323,6 +323,10 @@ Vv title Vv the Exile %%%% +Xak'krixis title + +Xak'krixis, Royal Expeditionary Alchemist +%%%% Xtahua title Xtahua the Ancient diff --git a/crawl-ref/source/dat/database/rand_all.txt b/crawl-ref/source/dat/database/rand_all.txt index aab38e0e1af..f4be1e602a3 100644 --- a/crawl-ref/source/dat/database/rand_all.txt +++ b/crawl-ref/source/dat/database/rand_all.txt @@ -144,6 +144,7 @@ _xom_esteem_ # chosen 20% of the time w:40 @divine_esteem@ + Absentmindedness Amusement @@ -178,6 +179,8 @@ Wantonness Whimsicality +Temerity + %%%% _time_name_ Autumn @@ -186,6 +189,8 @@ Dawn Day +Dusk + Eternal Night Eternity @@ -1076,6 +1081,8 @@ Haunted Histrionic +Impassible + Impish Indestructible diff --git a/crawl-ref/source/dat/database/rand_arm.txt b/crawl-ref/source/dat/database/rand_arm.txt index 6a3ad03b572..801370b7ed2 100644 --- a/crawl-ref/source/dat/database/rand_arm.txt +++ b/crawl-ref/source/dat/database/rand_arm.txt @@ -82,6 +82,8 @@ Barracuda Catfish +Cichlid + Mackerel Marlin @@ -157,7 +159,7 @@ Circus Animal Monkey's Uncle -#classical mythical animals +# classical mythical animals Centaur Cockatrice @@ -208,7 +210,9 @@ Mpinguari # Unlikely hybrids -Giraffatuar +Giraffataur + +Elephantaur Mertaur @@ -233,6 +237,10 @@ Dropbear Jackalope +Jersey Devil + +Snallygaster + Wild Haggis Yeti @@ -247,6 +255,8 @@ Lilacs Periwinkles +Purple Dead-Nettles + Tulips Violets @@ -1035,10 +1045,10 @@ Vermicelli Macaroni +Strozzapreti + w:1 the Pastamancer - - %%%% _card_ diff --git a/crawl-ref/source/dat/database/rand_wpn.txt b/crawl-ref/source/dat/database/rand_wpn.txt index c59b58a4b51..7592abdbe68 100644 --- a/crawl-ref/source/dat/database/rand_wpn.txt +++ b/crawl-ref/source/dat/database/rand_wpn.txt @@ -506,6 +506,8 @@ Shark # birds +Cardinal + Dove Hawk diff --git a/crawl-ref/source/dat/database/randbook.txt b/crawl-ref/source/dat/database/randbook.txt index bf55cce754d..592e0e00b7c 100644 --- a/crawl-ref/source/dat/database/randbook.txt +++ b/crawl-ref/source/dat/database/randbook.txt @@ -283,7 +283,7 @@ curling decrepit -disorganized +disorganised dog-eared @@ -2780,7 +2780,7 @@ Septic Spagyric -Synthesized +Synthesised Toxic diff --git a/crawl-ref/source/dat/des/altar/altar.des b/crawl-ref/source/dat/des/altar/altar.des index fb8959a2f71..4fe2bad9255 100644 --- a/crawl-ref/source/dat/des/altar/altar.des +++ b/crawl-ref/source/dat/des/altar/altar.des @@ -230,7 +230,7 @@ NAME: lemuel_hellish_altar DEPTH: Crypt, Geh TAGS: no_monster_gen no_descent MONS: rust devil / orange demon -MONS: hell beast / red devil +MONS: sin beast / red devil MONS: iron imp MONS: hellion KPROP: 1234_ = no_tele_into diff --git a/crawl-ref/source/dat/des/altar/overflow.des b/crawl-ref/source/dat/des/altar/overflow.des index fe9f42d097b..99827ab5fa4 100644 --- a/crawl-ref/source/dat/des/altar/overflow.des +++ b/crawl-ref/source/dat/des/altar/overflow.des @@ -123,7 +123,7 @@ function species_mock(e) elseif you.race() == "Demigod" then return "titan simulacrum" elseif you.race() == "Demonspawn" then - return "hell beast" + return "sin beast" elseif you.race() == "Djinni" then return "crimson imp" elseif you.race():find("Draconian") then @@ -4331,6 +4331,34 @@ xxxXXX+CCCxxx x.@.x ENDMAP +NAME: dolorous_temple_overflow_order_and_chaos_3 +TAGS: temple_overflow_2 temple_overflow_nemelex_xobeh temple_overflow_zin +TAGS: no_item_gen no_monster_gen no_trap_gen transparent decor +KPROP: 12 = no_tele_into +MONS: generate_awake goblin god:nemelex_xobeh name:sharper n_suf n_des n_noc +MONS: generate_awake angel god:zin dbname:zin_angel +KFEAT: A = altar_nemelex_xobeh +KFEAT: B = altar_zin +KFEAT: _ = floor +TILE: b = dngn_crystal_lightmagenta +FTILE: _ = floor_limestone +COLOUR: b = lightmagenta +COLOUR: _ = brown +KMASK: 12 = opaque +: set_feature_name("crystal_wall", "wall of pink crystal") +: interest_check(_G) +MAP + ....bbbb+vvvv.... +..bbbb..._...vvvv.. +.bb....U._.T....vv. +.bmm....._.....mmv. +.b1m..A.._..B..m2v. +.bmm....._.....mmv. +.bb....U._.T....vv. +..bbbb..._...vvvv.. + ....bbbb+vvvv.... +ENDMAP + NAME: grunt_temple_overflow_magic_moments TAGS: temple_overflow_3 temple_overflow_kikubaaqudgha TAGS: temple_overflow_sif_muna temple_overflow_vehumet @@ -4784,6 +4812,43 @@ xxc..cdc..cxx @ ENDMAP +NAME: dolorous_overflow_abandoned_chaos_spawn_temple +TAGS: temple_overflow_3 temple_overflow_xom +TAGS: temple_overflow_makhleb temple_overflow_nemelex_xobeh +TAGS: no_item_gen no_monster_gen no_trap_gen +SHUFFLE: DEF +KFEAT: D = altar_xom +KFEAT: E = altar_makhleb +KFEAT: F = altar_nemelex_xobeh +NSUBST: G = 2:g / *:G +NSUBST: H = 1:+ / *:b +NSUBST: . = 1:i / *:. +KPROP: 1 = no_tele_into +MONS: generate_awake chaos spawn +KMONS: g = pile of debris +KITEM: i = any damaged weapon ego:chaos ident:all +COLOUR: b = lightred +TILE: b = dngn_crystal_lightred +TILE: G = dngn_scintillating_statue +: set_feature_name("crystal_wall", "wall of orange crystal") +: set_feature_name("granite_statue", "scintillating statue") +MAP + bbHbb + bbbb.bbbb +bbb.....bbb +H....G....H +b.........b +b..D...E..b +b...mmm...b +H.G.m1m.G.H +b...mmm...b +b....F....b +b.........b +H....G....H +bbb.....bbb + bbbb.bbbb + bbHbb +ENDMAP ### Variable overflow altars ################################################## diff --git a/crawl-ref/source/dat/des/branches/abyss.des b/crawl-ref/source/dat/des/branches/abyss.des index 8d769c2839b..7b37a1dab48 100644 --- a/crawl-ref/source/dat/des/branches/abyss.des +++ b/crawl-ref/source/dat/des/branches/abyss.des @@ -1032,13 +1032,12 @@ NAME: hangedman_abyss_town TAGS: no_monster_gen patrolling allow_dup WEIGHT: 5 MONS: rakshasa / efreet / smoke demon / occultist / base draconian w:5 / \ - guardian serpent w:1 / vampire w:3 / necromancer w:3 + guardian serpent w:1 / vampire w:3 / necromancer w:3 / death knight w:3 MONS: thrashing horror / blink frog / weeping skull band / \ large abomination / unseen horror / shapeshifter / \ wretched star w:5 / air elemental / raiju / worldbinder MONS: draconian shifter w:5 / shadow demon w:5 / lich / \ - profane servitor w:5 / lorocyproca / deep elf demonologist / \ - deep elf sorcerer / death knight + profane servitor w:5 / deep elf demonologist / deep elf sorcerer MONS: bone dragon / tentacled starspawn / starcursed mass / \ apocalypse crab / dancing weapon / tentacled monstrosity / \ glowing shapeshifter @@ -1430,8 +1429,8 @@ TAGS: abyss_rune unrand MONS: draconian, nonbase draconian MONS: wretched star / starcursed mass / apocalypse crab / lurking horror / \ very ugly thing / storm dragon / shadow dragon -MONS: balrug / hellion / lorocyproca / tormentor / cacodemon / reaper / \ - blizzard demon / hellephant / angel / daeva +MONS: balrug w:12 / hellion / tormentor / cacodemon w:12 / reaper w:12 / \ + blizzard demon w:12 / hellephant / angel / daeva KMONS: R = hellephant / ancient lich / dread lich / \ hell sentinel / profane servitor / executioner KITEM: O = abyssal rune of Zot @@ -1613,8 +1612,8 @@ ENDMAP NAME: evilmike_abyss_rune_siren_acolytes TAGS: abyss_rune unrand patrolling no_pool_fixup -# They really should have bend space instead of blink / other. -MONS: occultist god:Lugonu hd:16 name:mad_acolyte_of_Lugonu n_rpl n_des \ +# They really should have something instead of blink / other. +MONS: occultist god:Lugonu hd:16 name:mad_acolyte_of_Lugonu n_rpl n_des n_noc \ col:lightgreen tile:mons_mad_acolyte_of_lugonu \ spells:smiting.20.priest;malign_gateway.20.priest;\ blink.10.priest;blink_other.10.priest;\ @@ -2099,9 +2098,9 @@ ENDMAP NAME: evilmike_abyss_exit_15 TAGS: abyss_exit -KMONS: O = unseen horror / lorocyproca / nothing -MONS: small abomination / nothing -SUBST: X = x., x=xxc +MONS: small abomination +KMONS: O = unseen horror +SUBST: 1 = 1., X = x., x=xxc : init_abyss_exit(_G, "O") MAP X X X diff --git a/crawl-ref/source/dat/des/branches/dis.des b/crawl-ref/source/dat/des/branches/dis.des index c8fbf8cb134..b6c2c20554b 100644 --- a/crawl-ref/source/dat/des/branches/dis.des +++ b/crawl-ref/source/dat/des/branches/dis.des @@ -486,8 +486,8 @@ D..vvv.......vv. ENDMAP NAME: kennysheep_iron_temple -ORIENT: centre TAGS: transparent no_rotate no_vmirror no_monster_gen +ORIENT: centre MONS: hell sentinel, iron golem MARKER: F = lua:fog_machine { cloud_type = "flame", pow_min = 100, \ pow_max = 100, delay = 10, size = 1, \ diff --git a/crawl-ref/source/dat/des/branches/geh.des b/crawl-ref/source/dat/des/branches/geh.des index 2a72ae5897b..07b5d5314f0 100644 --- a/crawl-ref/source/dat/des/branches/geh.des +++ b/crawl-ref/source/dat/des/branches/geh.des @@ -760,7 +760,7 @@ KMONS: Q = fire crab # The hellephants kept blinking out of their enclosure, they get to roam around KMONS: 1} = balrug w:8 / fire giant w:8 / hellephant w:4 / nothing w:5 KMONS: 2 = salamander tyrant -KMONS: 3 = hell beast / nothing w:5 +KMONS: 3 = sin beast / nothing w:5 KFEAT: } = } KMASK: JKLMNOPQ- = opaque KMASK: JKLMNOPQ- = no_monster_gen diff --git a/crawl-ref/source/dat/des/branches/hell.des b/crawl-ref/source/dat/des/branches/hell.des index 4ef9ab561fb..4b56847a1f4 100644 --- a/crawl-ref/source/dat/des/branches/hell.des +++ b/crawl-ref/source/dat/des/branches/hell.des @@ -74,7 +74,7 @@ NAME: hellmouth_1 ORIENT: float TAGS: no_rotate MONS: sun demon / smoke demon / soul eater / hell hog -MONS: hellion / balrug / reaper / hell beast / hell knight +MONS: hellion / balrug / reaper / sin beast / hell knight ITEM: good_item demon blade / good_item demon whip / \ good_item demon trident ITEM: ring of fire ident:type / Necronomicon / \ @@ -110,7 +110,7 @@ WEIGHT: 6 ORIENT: float TAGS: no_monster_gen MONS: sun demon / smoke demon / soul eater / hell hog / nothing w:20 -MONS: hellion / balrug / reaper / hell beast / hell knight +MONS: hellion / balrug / reaper / sin beast / hell knight ITEM: good_item demon blade / good_item demon whip /\ good_item demon trident ITEM: ring of fire ident:type / Necronomicon / \ @@ -151,7 +151,7 @@ NAME: hellmouth_3 TAGS: transparent ORIENT: float MONS: sun demon / hell knight / hell hog / soul eater / smoke demon -MONS: hellion / balrug w:20 / hell beast +MONS: hellion / balrug w:20 / sin beast ITEM: demon blade / demon whip / demon trident NSUBST: 1 = 1:l / 1:1l / *:1 KMASK: 12Ad = opaque @@ -187,7 +187,7 @@ ORIENT: float TAGS: no_monster_gen no_tele_into MONS: sun demon / smoke demon / soul eater / ynoxinul / sixfirhy \ / neqoxec w:5 / w:50 nothing -MONS: blizzard demon / hell beast / cacodemon / green death / reaper \ +MONS: blizzard demon / sin beast / cacodemon / green death / reaper \ / balrug / hellion w:5 / tormentor w:5 / nothing w:30 ITEM: skeleton KFEAT: b = . @@ -307,7 +307,7 @@ ENDMAP NAME: grunt_mini_vestibule TAGS: nolayout_encompass no_monster_gen uniq_hell_entry_high transparent : hell_entry_feature(_G, 'O') -KMONS: O = hell beast +KMONS: O = sin beast MONS: frost giant / quicksilver dragon simulacrum / freezing wraith MONS: iron dragon / war gargoyle / ancient champion MONS: salamander tyrant / hell hog / balrug @@ -770,7 +770,7 @@ ORIENT: float WEIGHT: 5 KMONS: 1 = sun demon KMONS: 2 = shining eye / eye of devastation / great orb of eyes -KMONS: 3 = hell hound / hell hog / hell beast / hellion +KMONS: 3 = hell hound / hell hog / sin beast / hellion NSUBST: - = 4=1 / 8=1. / 2=2 / 4=2. / 1=3 / 2=3. / . SUBST: b = bc KMASK: O = opaque @@ -1120,7 +1120,7 @@ KFEAT: G=enter_gehenna SUBST: L = l. KMONS: 2 = hellion KMONS: 3 = sun demon / smoke demon -KMONS: 2 = reaper / balrug / hell beast +KMONS: 2 = reaper / balrug / sin beast FTILE: x'5a2V+ = floor_infernal_blank : hell_lava_spawns(_G) MAP @@ -1153,7 +1153,7 @@ SUBST: ce = x, d : xlllLX., f : xlllLX., gi = ....m, m = mm. SUBST: L = l., X = xl KMONS: H = hellion KMONS: m = sun demon / smoke demon -KMONS: ' = hell beast / hell hog / nothing w:140 +KMONS: ' = sin beast / hell hog / nothing w:140 FTILE: AH' = floor_infernal_blank : hell_lava_spawns(_G) MAP @@ -1178,7 +1178,7 @@ ENDMAP NAME: vestibule_geh_grunt_obsidian_fortress TAGS: vestibule_geh KFEAT: G = enter_gehenna -MONS: balrug / hellion, hell beast / hell hog +MONS: balrug / hellion, sin beast / hell hog MONS: sun demon / smoke demon / hell knight NSUBST: 2 = 3:2 / *:. NSUBST: 3 = 3:3 / *:. @@ -1232,7 +1232,7 @@ ENDMAP NAME: vestibule_geh_nicolae_lone_tower TAGS: vestibule_geh -KMONS: D = balrug band / hell beast band / hell hog band / \ +KMONS: D = balrug band / sin beast band / hell hog band / \ hell knight band w:5 KFEAT: D = enter_gehenna SHUFFLE: Pp/Qq/Rr/Ss diff --git a/crawl-ref/source/dat/des/branches/hells.des b/crawl-ref/source/dat/des/branches/hells.des index fabf501d618..b4a3e5bd38f 100644 --- a/crawl-ref/source/dat/des/branches/hells.des +++ b/crawl-ref/source/dat/des/branches/hells.des @@ -191,18 +191,18 @@ DEPTH: Dis, !Dis:$, Geh, !Geh:$, Coc, !Coc:$, Tar, !Tar:$ ORIENT: centre : if you.in_branch("Dis") then KMONS: 1 = hell sentinel -KMONS: 2 = hell beast / hellion / reaper / tormentor / shadow demon / cacodemon +KMONS: 2 = sin beast / hellion / reaper / tormentor / shadow demon / cacodemon : elseif you.in_branch("Geh") then KMONS: 1 = brimstone fiend -KMONS: 2 = hell beast / hellion / reaper / tormentor / shadow demon / balrug +KMONS: 2 = sin beast / hellion / reaper / tormentor / shadow demon / balrug : elseif you.in_branch("Coc") then KMONS: 1 = ice fiend -KMONS: 2 = hell beast / hellion / reaper / tormentor / shadow demon / \ +KMONS: 2 = sin beast / hellion / reaper / tormentor / shadow demon / \ blizzard demon : else KMONS: 1 = tzitzimitl -KMONS: 2 = hell beast / hellion / reaper / tormentor / shadow demon / \ - green death / lorocyproca +KMONS: 2 = sin beast / hellion / reaper / tormentor / shadow demon / \ + green death : end SUBST: D = c. MAP diff --git a/crawl-ref/source/dat/des/branches/lair.des b/crawl-ref/source/dat/des/branches/lair.des index 1e976f31738..0e6d2901c58 100644 --- a/crawl-ref/source/dat/des/branches/lair.des +++ b/crawl-ref/source/dat/des/branches/lair.des @@ -3246,7 +3246,7 @@ ORIENT: southeast WEIGHT: 5 MONS: rime drake, hydra w:15 / dire elephant, arcanist / occultist MONS: red devil / ice devil w:15 / orange demon w:15 -MONS: hellwing / smoke demon, sun demon / sixfirhy / hell beast w:4 +MONS: hellwing / smoke demon, sun demon / sixfirhy / sin beast w:4 KMONS: hH = hell hound KMONS: F = fungus KITEM: A = superb_item no_pickup, any good_item no_pickup \ @@ -3666,10 +3666,12 @@ ENDMAP NAME: hellmonk_lair_end_abyssal_woods PLACE: Lair:$ ORIENT: northwest -MONS: blink frog / basilisk w:3, small abomination +MONS: blink frog / basilisk w:3, small abomination / brain worm w:2 MONS: shapeshifter / chaos spawn / smoke demon / ynoxinul -MONS: raiju, ugly thing / large abomination, thrashing horror +MONS: raiju, ugly thing, thrashing horror / large abomination w:5 KMONS: 7 = spriggan ; rapier ego:distortion +KITEM: O = spriggan skeleton / wolf skeleton w:3 /\ + polar bear skeleton w:2 / black bear skeleton w:2 KITEM: R = earthy gem, % KFEAT: u = demonic_tree KFEAT: N = enter_abyss @@ -3678,18 +3680,32 @@ SUBST: " = '. COLOUR: '%M = magenta FTILE: '%MN_u = floor_nerves_magenta NSUBST: %d = 1:R / 2:| / 3:* / *:% -NSUBST: 1 = 5=1 / 1.., 2 = 4=2 / 2.. -NSUBST: 4 = 4 / 3, ' = 2:6 / 2 = 56 / 3:O / 5 = 5543O. / . +NSUBST: 1 = 5:1 / 1.., 2 = 3:2 / 2:. / * = 2.. +NSUBST: 4 = 4 / *:3, ' = 2:6 / 2 = 56 / 3:O / 5 = 5543O. / *:. SUBST: 7M = 7O -# 33% chance for an additional opening at the south. +# 33% chance each for additional openings at the south or north east. SHUFFLE: yzD / JKL / JKL SUBST: y = @, zD= ., JK = t, L = p -# 33% chance for an additional opening at the north east. SHUFFLE: EFH / JKL / JKL SUBST: E = @, FH= ., JK = t, L = p SUBST: a = tt..., q = pp..., p = tttu, s = rr..., r = tu -KITEM: O = spriggan skeleton / wolf skeleton w:3 /\ - polar bear skeleton w:2 / black bear skeleton w:2 +{{ + local cols = {"dngn_demonic_tree_1 / dngn_demonic_tree_16", + "dngn_demonic_tree_2 / dngn_demonic_tree_3", + "dngn_demonic_tree_4 / dngn_demonic_tree_5", + "dngn_demonic_tree_6 / dngn_demonic_tree_7", + "dngn_demonic_tree_8 / dngn_demonic_tree_9", + "dngn_demonic_tree_10 / dngn_demonic_tree_11", + "dngn_demonic_tree_12 / dngn_demonic_tree_13", + "dngn_demonic_tree_14 / dngn_demonic_tree_15",} + local col1 = util.random_from(cols) + local col2 = col1 + while col2 == col1 do + col2 = util.random_from(cols) + end + + tile("u = " .. col1 .. " / " .. col2) +}} MAP ttttttttttttttttttttttttttttttttttttttttttttttttttt tttttttttttttttttttttttttttttttpppppppppppttpppptt diff --git a/crawl-ref/source/dat/des/branches/pan.des b/crawl-ref/source/dat/des/branches/pan.des index 7b58ba29328..e24c55ec01f 100644 --- a/crawl-ref/source/dat/des/branches/pan.des +++ b/crawl-ref/source/dat/des/branches/pan.des @@ -60,7 +60,8 @@ function pan_lord_setup(_G, name) "deep elf annihilator / titan w:5 / lich w:5 / " .. "demonspawn blood saint", Cerebov = "sun moth / efreet / sun demon / ynoxinul / balrug / " .. - "brimstone fiend w:1 / demonspawn warmonger w:1", + "undying armoury w:1 / brimstone fiend w:1 / " .. + "demonspawn warmonger w:1", ['Gloorx Vloq'] = "juggernaut skeleton w:100 / " .. "caustic shrike skeleton w:100 / " .. "hellephant skeleton w:100 / soul eater w:200 / " .. @@ -253,7 +254,7 @@ function pan_entry_vault_contents(e, glyph) local tierthree = "rust devil w:5 / sixfirhy / smoke demon / " .. "soul eater / sun demon / ynoxinul w:5" local tiertwo = "balrug / blizzard demon / cacodemon w:5 / " .. - "green death / hell beast / hellion / lorocyproca / " .. + "green death / sin beast / hellion / " .. "reaper / shadow demon / tormentor" if e.is_validating() then return end @@ -1185,7 +1186,7 @@ NAME: cerebov_st TAGS: cerebov patrolling uniq_cerebov ORIENT: north MONS: patrolling Cerebov, balrug, brimstone fiend -MONS: red devil, sun demon, sun moth +MONS: red devil, sun demon, sun moth, undying armoury : pan_lord_setup(_G, 'Cerebov') epilogue{{ pan_lord_epilogue('Cerebov') @@ -1209,10 +1210,10 @@ xx....vv.vv||v.......v$$vv+v.................v.+.........3v.$||v.vv....xx xx.....v.vv||v.v.1.v.v$$v4.v.v...2.....2...v.v.v..v..v..v.v.$$$v.v.....xx xx....vv.vv||v.v...v.v$$vO3v.................v.v.....5..5.+.$$$v.vv....xx xx....vv.vv+vv.......vv+vvvv.v.....&.&.....v.v.vvvvvvvvvvvvvvvvv.vv....xx -xx.....v.vv..2.......2..v..+.................+.............+...v.v.....xx +xx.....v.vv..2.......2..v..+........7........+.............+...v.v.....xx xx....vv.vv..2.......2..v..v.v.....&.&.....v.vvvvvvvvvvvvvvv...v.vv....xx xx....vv.vv.............v..v.................v...v..4..v$|$v.6.v.vv....xx -xx.....v.vv....v...v....v..v.v...2.....2...v.v...+..2..+|$|v.2.v.v.....xx +xx.....v.vv....v...v....v..v.v...2.....2...v.v...+..2.7+|$|v.2.v.v.....xx xx....vv.vv...vv+++vv...v..v.................v.4.v..4..v$|$v...v.vv....xx xx....vv.vvvvvv.....vvvvv..v.v.v.v.....v.v.v.v.4.vvvvvvvvvvv...v.vv....xx xx.....v.vvv...............v.......v.v.......v...vvvvvvvvvvv+vvv.v.....xx @@ -1232,7 +1233,8 @@ ENDMAP NAME: evilmike_cerebov_lava TAGS: cerebov patrolling uniq_cerebov ORIENT: northwest -MONS: Cerebov, balrug, brimstone fiend, sun demon, sun moth +MONS: Cerebov, balrug, brimstone fiend, sun demon +MONS: sun moth, undying armoury SUBST: X : v, Y : .v, L : lll. SUBST: U : W., W : V., V : v. SHUFFLE: XY @@ -1263,7 +1265,7 @@ xx.....vv.v...LLLL...llll...v2vvvvvvvvvvvv.. xx..vvvvv.U..LLLL.....llll.....&....vllvvv.. xx..v%%%v...LLvvv......llll...vWvWv.v4...vv. xx..v|||v..LLvv.v.Y....llll...&.....+....... -xx..v|Q|+..LLvOX3.Y...1lllll.4......+.5.2..@ +xx..v|Q|+.6LLvOX3.Y..1.lllll...6....+.5.2..@ xx..v|||v..LLvv.v.Y....llll...&.....+....... xx..v%%%v...LLvvv......llll...vWvWv.v4...vv. xx..vvvvv.U..LLLL.....llll.....&....vllvvv.. @@ -1288,7 +1290,7 @@ NAME: cerebov_grunt TAGS: cerebov uniq_cerebov ORIENT: northwest MONS: patrolling Cerebov, balrug, brimstone fiend -MONS: sun demon, red devil, sun moth +MONS: sun demon, red devil, sun moth, undying armoury NSUBST: 5 = 2:6 / * = 44455 : pan_lord_setup(_G, 'Cerebov') epilogue{{ @@ -1304,13 +1306,13 @@ xxxx............v|||v............ xxxx..........vvv|||vvv.......... xxxx.........vvvvv+vvvvv......... xxxx........vvv...4...vvv........ -xxxx.......vvv.........vvv....... +xxxx.......vvv....7....vvv....... xxxx......vvv2...vvv...2vvv...... xxxx.vvvvvv.....vvOvv.....vvvvvv. xxxx.v|$$v.....vv$3$vv.....v$||v. xxxx.v||$+4..vvv$vvv$vvv..4+$$|v. xxxx.v|$$v...vll.$1$.llv...v$||v. -xxxx.vvvvv...vll$.2.$llv...vvvvv. +xxxx.vvvvv...vll$...$llv...vvvvv. xxxx....vv...vvl.2.2.lvv...vv.... xxxx....vv....vl.vvv.lv....vv.... xxxx....vvv...v.lllll.v...vvv.... @@ -1333,7 +1335,8 @@ ENDMAP NAME: cerebov_grunt_fire_and_steel TAGS: cerebov uniq_cerebov ORIENT: north -MONS: patrolling Cerebov, balrug, brimstone fiend, sun demon, sun moth +MONS: patrolling Cerebov, balrug, brimstone fiend, sun demon +MONS: sun moth, undying armoury NSUBST: O = 1:O / *:|, 4 = 3:5 / *:4 : pan_lord_setup(_G, 'Cerebov') epilogue{{ @@ -1352,7 +1355,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx .lvv..4.....2..vvvvvl.lvvvvvvvl.lvvvvvvl...lvvvllll. .llv...vvvvv...vlllll4lllllllll4lllllvll...llvvvvvl. ...+.4.v|$|v...+.........&.&.........+..4.2..v$|$vl. -...+...v$|$+.2.+...2.........2.......+...1.3.+|O$vl. +...+...v$|$+2.6+...2.........2.......+...1.3.+|O$vl. ...+.4.v|$|v...+.........&.&.........+..4.2..v$|$vl. .llv...vvvvv...vlllll4lllllllll4lllllvll...llvvvvvl. .lvv..4.....2..vvvvvl.lvvvvvvvl.lvvvvvvl...lvvvllll. @@ -1365,7 +1368,6 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ....@..........................................@.... ENDMAP - ############################################################################## # Gloorx Vloq # @@ -2695,7 +2697,7 @@ NAME: regret_index_pan_draining_boxed TAGS: pan_fixed_rune_boxes pan_fixed_lure_boxes transparent ORIENT: float MONS: pandemonium lord, ghost moth -MONS: demonspawn black sun, lorocyproca, any demon +MONS: demonspawn black sun, sin beast, any demon KMONS: 6O = ghost moth : if demonic_rune_vault_is_decoy() then KITEM: O = superb_item / demonic rune of zot mimic diff --git a/crawl-ref/source/dat/des/branches/spider.des b/crawl-ref/source/dat/des/branches/spider.des index 0149e1739e8..d7b42e07608 100644 --- a/crawl-ref/source/dat/des/branches/spider.des +++ b/crawl-ref/source/dat/des/branches/spider.des @@ -1234,7 +1234,7 @@ NAME: nicolae_arachne_temple TAGS: no_item_gen no_monster_gen no_trap_gen DEPTH: Spider:2- MONS: tarantella, wolf spider, redback, jumping spider -KMONS: A = Arachne, orb spider +KMONS: A = Xak'krixis, orb spider KFEAT: -' = web / floor w:40 COLOUR: -2 = brown COLOUR: 'A435 = yellow @@ -2286,13 +2286,13 @@ TAGS: no_pool_fixup WEIGHT: 20 PLACE: Spider:$ ORIENT: float -MONS: ghost moth, moth of wrath / ghost moth, emperor scorpion +MONS: ghost moth, moth of wrath, emperor scorpion MONS: wolf spider / redback / tarantella / jumping spider / culicivora -MONS: orb spider / steelbarb worm, redback +MONS: orb spider / steelbarb worm w:7, redback KITEM: R = milky-white gem KITEM: O = gossamer rune of Zot -NSUBST: 4 = 3:3 / * = 44. -NSUBST: 7 = 3 = 111.. / 1:2 +NSUBST: 4 = 3:3 / * = 44444.. +NSUBST: 7 = 3 = 111.. / 2 = 12 NSUBST: d = 1:R / 3:d / 3:e / *:f SUBST: 5 = 5., 6 = 66... SUBST: W = W.. @@ -2311,7 +2311,7 @@ wW.xcccW666....W5.W....WWWWW..cccxx xxcW-4.wccdd575dwww4d--ccc.-..cccxx WxcW-..5cd4wwwww3Oww474-3c..W5cccxxx xxccW.4.c-74.wwwwwww4ddccc6-.Wcccxx - xxccW4Wcccc...4.dd4---6-6-6ccccxx + xxccW4Wcccc..74.dd4---6-6-6ccccxx xxcW45..-cccc-3cc-66-6-ccccxxxx xcccW-...44cccc6-6-6-ccxxxxxx xxxcccccc4.....-6-ccccxx xx @@ -2480,7 +2480,8 @@ xxcc-.h.....""...g...."".......-"c ENDMAP ########################################## -# Arachne Lair Rune Vault +# Made pre-exile, but still named in her honour, +# and also still named for easier statistics assessment. NAME: johnstein_spider_rune_arachne_lair TAGS: no_monster_gen ORIENT: float @@ -2516,14 +2517,14 @@ KMONS: 3 = emperor scorpion / spark wasp w:5 / jorogumo KMONS: 4 = moth of wrath / ghost moth w:40 / sun moth KMONS: 5 = orb spider w:40 / demonic crawler KMONS: 6 = torpor snail / entropy weaver -KMONS: Z = Arachne, jorogumo +KMONS: Z = jorogumo KITEM: O = gossamer rune of Zot KITEM: o = milky-white gem KFEAT: Y = web / floor w:40 SHUFFLE: Gg / Hh / Ii / Jj / Kk / Ll SUBST: G = O, g = o, HhIiJKL = %, jkl = . # thin out monsters and set up loot -SUBST: 1 = 1.. +SUBST: 1 = 111..... NSUBST: % = 2:d / 2:e / 2:f / * = def... NSUBST: 2 = 6 = 5. / 2:4 / 6 = 2. / *:1 NSUBST: 3 = 4:3 / 1:6 / * = 2. @@ -2533,9 +2534,9 @@ MAP ..cccccc5...5cccccc.. .cc.Gcccc+++ccccH.cc. ccg4.c..2.2.2..c.4hcc - cc..3cc.2.2Z2.2.cc3..cc + cc..3cc.2.2.2.2.cc3..cc xc...cc.2'''''''2.cc...cx - xc.2cc..1c''!''c1..cc2.cx + xc.2cc.Z1c''!''c1Z.cc2.cx xc..c.'1ccc!!!ccc1'.c..cx xxc.1.'''1c--!--c1'''.1.cxx xcc.1''''_"?????"_''''1.ccx @@ -2553,7 +2554,7 @@ xcc.1''''_""???""_''''1.ccx xcc.1.'1ccc'!'ccc1'.1.ccx xcc..c..1c'''''c1..c..ccx xcc..cc.2'''''''2.cc..ccx - xc2.ccc...2.Z.2...ccc.2cx + xc2.ccc..Z2...2Z..ccc.2cx xc..cccc1..222..1cccc..cx xc..cccc.1..2..1.cccc..cx xc2.ccc...........ccc.2cx @@ -2589,7 +2590,7 @@ KITEM: R = milky-white gem KITEM: O = potion of ambrosia ident:type, gossamer rune of Zot KFEAT: M = stone_wall KFEAT: P = granite_statue -NSUBST: S = 4:1 / 6 = 1. /*:., H = 2:5 / 1:6 / 1:7 / K +NSUBST: S = 4:1 / 6 = 1. / *:., H = 4:5 / 1:6 / 1:7 / *:K SUBST: K = 2 5:4 6:2 7:2 E:20, E = 3 4:30 .:20 SUBST: Q = W .:15 NSUBST: d = 2:d / 2:e / 3:f / *=gggge @@ -2650,7 +2651,7 @@ MONS: broodmother KITEM: P = milky-white gem KITEM: O = potion of ambrosia ident:type, gossamer rune of Zot KFEAT: ^ = web -NSUBST: . = 96 = 1... / *:., 2 = 2:2 / *=2... +NSUBST: . = 80 = 1... / *:., 2 = 2:2 / *=2... NSUBST: 6 = 4:6 / 4 = 456. / *= 4.. NSUBST: d = 1:O / 1:P / 3:d / 3:e / 3:f SUBST: 3 = 3.... @@ -2715,7 +2716,7 @@ MONS: emperor scorpion MONS: radroach / spark wasp KITEM: R = milky-white gem KITEM: O = potion of ambrosia ident:type, gossamer rune of Zot -NSUBST: 7 = 4:7 / * = 7.. +NSUBST: 7 = 4:7 / 1:. / * = 7.. # bias the big monsters, randomly : if crawl.one_chance_in(3) then SUBST: 7 = 44456 @@ -2724,8 +2725,8 @@ NSUBST: 7 = 4:7 / * = 7.. : else SUBST: 7 = 45666 : end -NSUBST: . = 16 = 3... / 16 = 2... / 16 = 1... / *:. -NSUBST: ' = 12 = 1... / 16 = 2... / 1:3 / 1:3. / *:. +NSUBST: . = 12 = 3... / 16 = 2... / 16 = 1... / *:. +NSUBST: ' = 10 = 1... / 16 = 2... / 1:3 / 1:3. / *:. # bias the ruination level of the terrain : if crawl.one_chance_in(3) then SUBST: A = cc. diff --git a/crawl-ref/source/dat/des/branches/swamp.des b/crawl-ref/source/dat/des/branches/swamp.des index fbd3fa14345..08e4d94d04b 100644 --- a/crawl-ref/source/dat/des/branches/swamp.des +++ b/crawl-ref/source/dat/des/branches/swamp.des @@ -2196,7 +2196,7 @@ DEPTH: Swamp, !Swamp:$ KMONS: o = oklob plant KFEAT: ^ = permanent teleport trap SHUFFLE: Aa/Bb/Cc/Dd/Ee/Ff/Gg/Hh/Ii, %*$ -NSUBST: ' = ^ / o / ' +NSUBST: ' = 1:^ / 3:o / *:' SUBST: abcde = w, fghi = t, ABCDE = .wW, FGHI = .t, ' = ':100 $$$%% SUBST: _ = .._, . = ......W CLEAR: _ @@ -3404,9 +3404,10 @@ NAME: swamp_vile TAGS: no_pool_fixup no_monster_gen PLACE: Swamp:$ ORIENT: southeast -MONS: bog body / ugly thing, shadow / orange demon / small abomination w:2 +MONS: bog body / ugly thing / small abomination w:2 +MONS: shadow / orange demon w:2 MONS: large abomination / very ugly thing / thrashing horror w:6 -MONS: green death, hell beast, great orb of eyes, titanic slime creature +MONS: green death, sin beast, great orb of eyes, titanic slime creature KMONS: 8 = hydra / swamp dragon KMONS: 9 = tentacled monstrosity KMONS: & = the Lernaean hydra @@ -3616,7 +3617,7 @@ NAME: nzn_swamp_witches_coven PLACE: Swamp:$ ORIENT: float TAGS: no_monster_gen no_rotate no_vmirror -MONS: fenstrider witch, green death / hell beast / blizzard demon +MONS: fenstrider witch, green death / sin beast / blizzard demon MONS: smoke demon / soul eater / sixfirhy MONS: orange demon / hellwing / chaos spawn MONS: spriggan druid / orc sorcerer / ogre mage / deep elf pyromancer w:5 / \ diff --git a/crawl-ref/source/dat/des/branches/zot.des b/crawl-ref/source/dat/des/branches/zot.des index 570d6c331ae..39b4eb324f8 100644 --- a/crawl-ref/source/dat/des/branches/zot.des +++ b/crawl-ref/source/dat/des/branches/zot.des @@ -3772,7 +3772,7 @@ ORIENT: northwest WEIGHT: 5 # Cheap plastic imitations, mostly. Well, maybe not cheap... MONS: hell knight w:6 / very ugly thing / caustic shrike zombie / \ - lorocyproca / thermic dynamo / quicksilver ooze / \ + sin beast / thermic dynamo / quicksilver ooze / \ tentacled starspawn / spark wasp / glowing shapeshifter MONS: guardian serpent / deep elf annihilator / balrug / \ deep elf high priest / will-o-the-wisp / acid blob diff --git a/crawl-ref/source/dat/des/builder/alphashops.des b/crawl-ref/source/dat/des/builder/alphashops.des index 591ba6ca2ef..90c4e53102c 100644 --- a/crawl-ref/source/dat/des/builder/alphashops.des +++ b/crawl-ref/source/dat/des/builder/alphashops.des @@ -80,7 +80,7 @@ "chain mail", "cloak", "crystal plate armour", "potion of cancellation", "staff of cold", "staff of conjuration", "book of callings", "book of chaos", "manual of conjurations", - "potion of curing", "wand of charming" } + "potion of curing", "wand of charming", "codex of conductivity" } elseif s == "D" then i = { "book of death", "book of debilitation", "book of dreams", "book of the dragon", "dagger", "dragon-blood talisman", @@ -130,7 +130,8 @@ "book of minor magic", "book of misfortune", "maw talisman", "morningstar", "mace", "potion of magic", "wand of mindburst", "ring of magical power", "amulet of magic regeneration", "potion of might", - "potion of mutation", "book of the moon", "book of movement" } + "potion of mutation", "book of the moon", "book of movement", + "book of maladies" } elseif s == "N" then i = { "book of necromancy", "necronomicon", "scroll of noise", "manual of necromancy" } @@ -164,7 +165,7 @@ "manual of spellcasting", "manual of summonings", "stone", "book of storms", "book of sloth", "book of the senses", "book of spectacle", "book of the spheres", "book of scorching", - "book of spontaneous combustion" } + "book of spontaneous combustion", "compendium of siegecraft" } elseif s == "T" then i = { "scroll of torment", "scroll of teleportation", "triple sword", "talisman of death", diff --git a/crawl-ref/source/dat/des/builder/rooms.des b/crawl-ref/source/dat/des/builder/rooms.des index b83f164e754..b25b8b030e8 100644 --- a/crawl-ref/source/dat/des/builder/rooms.des +++ b/crawl-ref/source/dat/des/builder/rooms.des @@ -274,9 +274,9 @@ function sroom_mythical_zoo(e) end if Z >= 21 then - lord_mon = "catoblepas / sphinx w:60 / hell beast w:20" + lord_mon = "catoblepas / sphinx w:60 / sin beast w:20" elseif Z >= 16 then - lord_mon = "catoblepas / hydra w:40 / hell beast w:40" + lord_mon = "catoblepas / hydra w:40 / sin beast w:40" else lord_mon = "catoblepas / manticore w:40 / hydra w:40" end @@ -338,10 +338,9 @@ function sroom_demon_pit(e) local weight_d = 5 + runes local tier_twos = { - {"hell_beast", weight_a}, + {"sin_beast", weight_a}, {"green_death", weight_a}, {"reaper", weight_b}, - {"lorocyproca", weight_b}, {"balrug", weight_c}, {"blizzard_demon", weight_c}, {"hellion", weight_d}, diff --git a/crawl-ref/source/dat/des/builder/uniques.des b/crawl-ref/source/dat/des/builder/uniques.des index 630e7d0af0a..d67edccaa93 100644 --- a/crawl-ref/source/dat/des/builder/uniques.des +++ b/crawl-ref/source/dat/des/builder/uniques.des @@ -354,6 +354,16 @@ NAME: uniq_nikola DEPTH: Depths:1-2, Elf, Swamp, Snake, Shoals, Spider, Vaults:1-3 : place_unique(_G, "Nikola") +NAME: uniq_arachne +DEPTH: Vaults:1-4, Elf, Depths:1 +veto {{ + if crawl.game_started() and dgn.br_exists("Spider") then + return true + end + return false +}} +: place_unique(_G, "Arachne") + NAME: uniq_parghit DEPTH: Depths:3-, Zot:1-4 : place_unique(_G, "Parghit") @@ -417,10 +427,10 @@ DEPTH: Shoals:2-, !Shoals:$ WEIGHT: 50 : place_unique(_G, "Ilsuiw band") -NAME: uniq_arachne +NAME: uniq_xakkrixis DEPTH: Spider:2- -WEIGHT: 50 -: place_unique(_G, "Arachne") +WEIGHT: 40 +: place_unique(_G, "Xak'krixis") NAME: uniq_dissolution DEPTH: Slime:2- diff --git a/crawl-ref/source/dat/des/portals/ossuary.des b/crawl-ref/source/dat/des/portals/ossuary.des index 539c8ebe37d..9f7f9eefd68 100644 --- a/crawl-ref/source/dat/des/portals/ossuary.des +++ b/crawl-ref/source/dat/des/portals/ossuary.des @@ -635,9 +635,9 @@ ORIENT: encompass MONS: scorpion, mummy MONS: gnoll zombie / jackal zombie / orc zombie / \ human zombie / ball python zombie / adder zombie / \ - centaur zombie / scorpion zombie w:20 + centaur zombie / scorpion zombie w:30 ITEM: any scroll / any potion / any jewellery w:1 -SUBST: 1 = 11..., 3 = 3., d = d:21 . +SUBST: 1 = 11..., 3 = 33., d = d:21 . : ossuary_setup_features(_G) MAP ccccccccccccccccccccccccc @@ -655,13 +655,13 @@ c3.3cdddn...c..3c3.1c...c cc+ccccccc+ccccccccccc+cc c1...3c.....c...c...c3.1c c.....c.....c...+...G...c -c.....G.....c...c......3c -c...........cc+cccG...Gcc +c..3..G.....c...c......3c +c...........cc+cccG.3.Gcc c3..........c3.3c3.....3c cccG..3..Gccc...c...G...c c3.........3c3.3c1.3c3.1c c...........cc+ccccccc+cc -c.....G.....c...ndddc3.3c +c..3..G..3..c...ndddc3.3c c.....c.....+...nd2d+.3.c c3...3c3...1c...ndddc1.1c ccccccccccccccccccccccccc @@ -803,21 +803,21 @@ MONS: mummy, crocodile zombie MONS: kobold zombie / centaur zombie w:5 / gnoll zombie KFEAT: ~ = net trap / alarm trap w:30 / dispersal trap KFEAT: ^ = net trap w:20 / dispersal trap w:5 -SUBST: M = 111., z = c... +SUBST: M = 11., z = c... NSUBST: 3 = 1:2 / 4:. / * = 333.. NSUBST: ~ = 4:~ / 40:. / * = ~:25 .:200 NSUBST: ^ = 1:^ / 1:. / * = ^. : ossuary_setup_features(_G) MAP - cccccccccccccccccccccc - cccccccccccc3c3c3c3c3c3c3cccddddcc -cc3........~c~~~~~~~~~~~~~c+^.....cc -c..........~~~~~~~~~~~~~~~~+^.....Mcc -c1....A..<.....z...z...z...+^.....2 12 then +: else +SUBST: * = %, | = * +: if you.depth() > 12 then SUBST: 1 = 12 +: end : end COLOUR: G = mist TILE: G = dngn_mystic_cage : set_feature_name("metal_statue", "mystic cage") MAP ccc+cccccc -c.......Gc +cT......Gc c........c c......1.c cG.......c diff --git a/crawl-ref/source/dat/descript/ability.txt b/crawl-ref/source/dat/descript/ability.txt index c6ef58b44a6..74e04d6772a 100644 --- a/crawl-ref/source/dat/descript/ability.txt +++ b/crawl-ref/source/dat/descript/ability.txt @@ -255,7 +255,7 @@ per floor, ever. Hurl Torchlight ability Hurls a gout of umbral fire from the Black Torch, which deals irresistible -damage to living, demonic, and unholy beings. The torchlight will also briefly +damage to living, demonic, and holy beings. The torchlight will also briefly empower any of your undead servants caught in its blast, and is harmless to its user. @@ -438,7 +438,7 @@ ability will leave your health depleted. %%%% Brand Weapon With Distortion ability -Permanently corrupts a weapon of your choice with a localized field of +Permanently corrupts a weapon of your choice with a localised field of distortion. This blessing may only be used once, and it cannot be used on artefact weapons. %%%% @@ -471,7 +471,7 @@ Smiting ability Beogh will not allow you to use this directly against another apostle during a divine trial. %%%% -Recall Orcish Apostles ability +Recall Apostles ability Recalls your apostles from anywhere in the dungeon to your immediate surroundings. diff --git a/crawl-ref/source/dat/descript/clouds.txt b/crawl-ref/source/dat/descript/clouds.txt index f60e76d50ae..c07a7da4da1 100644 --- a/crawl-ref/source/dat/descript/clouds.txt +++ b/crawl-ref/source/dat/descript/clouds.txt @@ -107,9 +107,8 @@ paralysis. %%%% rain cloud -An extremely localized rainstorm. It may produce pools of shallow water, or -temporarily deepen existing pools. Beings made entirely out of fire are averse -to spending much time in the rain. +An extremely localised rainstorm. It may produce short-lived pools of shallow +water. Beings made of fire are averse to spending much time in the rain. %%%% mutagenic fog cloud diff --git a/crawl-ref/source/dat/descript/features.txt b/crawl-ref/source/dat/descript/features.txt index 3ff76057664..9bc7be5b79c 100644 --- a/crawl-ref/source/dat/descript/features.txt +++ b/crawl-ref/source/dat/descript/features.txt @@ -928,6 +928,14 @@ The damp floor The floor. It remains forever damp, perhaps due to the humidity or the constant flow of clouds. %%%% +A wall of orange crystal + + +%%%% +A wall of pink crystal + + +%%%% A wall of white crystal A wall made from low-grade crystal with an opalescent gleam. @@ -1016,55 +1024,55 @@ to intruders like you infiltrating the Dungeon. # Themed magic branches / portals / vaults. An arcane conduit -A strange but benign metal construct, analyzing raw ambient magical energy. If +A strange but benign metal construct, analysing raw ambient magical energy. If destroyed and disrupted by sufficiently mighty magic, its destructive mystic energy will harmlessly disperse. %%%% A misfortune conduit -A strange but benign metal construct, analyzing raw ambient magical energy. If +A strange but benign metal construct, analysing raw ambient magical energy. If destroyed and disrupted by sufficiently mighty magic, its malevolent cursed energy will harmlessly disperse. %%%% A dimensional conduit -A strange but benign metal construct, analyzing raw ambient magical energy. If +A strange but benign metal construct, analysing raw ambient magical energy. If destroyed and disrupted by sufficiently mighty magic, its extraplanar warp energy will harmlessly disperse. %%%% A soul conduit -A strange but benign metal construct, analyzing raw ambient magical energy. If +A strange but benign metal construct, analysing raw ambient magical energy. If destroyed and disrupted by sufficiently mighty magic, its negative energy will harmlessly disperse. %%%% An alchemical conduit -A strange but benign metal construct, analyzing raw ambient magical energy. If +A strange but benign metal construct, analysing raw ambient magical energy. If destroyed and disrupted by sufficiently mighty magic, its venomous churning energy will harmlessly disperse. %%%% A fiery conduit -A strange but benign metal construct, analyzing raw ambient magical energy. If +A strange but benign metal construct, analysing raw ambient magical energy. If destroyed and disrupted by sufficiently mighty magic, its ever-burning energy will harmlessly disperse. %%%% An icy conduit -A strange but benign metal construct, analyzing raw ambient magical energy. If +A strange but benign metal construct, analysing raw ambient magical energy. If destroyed and disrupted by sufficiently mighty magic, its chilling cold energy will harmlessly disperse. %%%% An earthen conduit -A strange but benign metal construct, analyzing raw ambient magical energy. If +A strange but benign metal construct, analysing raw ambient magical energy. If destroyed and disrupted by sufficiently mighty magic, its seismic energy will harmlessly disperse. %%%% A storm conduit -A strange but benign metal construct, analyzing raw ambient magical energy. If +A strange but benign metal construct, analysing raw ambient magical energy. If destroyed and disrupted by sufficiently mighty magic, its crackling energy will harmlessly disperse. %%%% diff --git a/crawl-ref/source/dat/descript/fr/monsters.txt b/crawl-ref/source/dat/descript/fr/monsters.txt index 81263c5cbc3..22b6a219c74 100644 --- a/crawl-ref/source/dat/descript/fr/monsters.txt +++ b/crawl-ref/source/dat/descript/fr/monsters.txt @@ -1013,7 +1013,7 @@ les nagas. Leur absence de bras, ajouté à leur sens extrême du devoir, en fai des gardiens renommés. On dit qu'un garde Serpent préfère mourir au combat plutôt que de laisser quoi que ce soit faire intrusion. %%%% -hell beast +sin beast Un croisement abominable d'une bête et d'un homme qui produit des glapissements épouvantables. @@ -1276,11 +1276,6 @@ lindwurm Un petit dragon ophite avec une paire de membres antérieurs robustes. Ses épaisses écailles brillent d'une lueur verte sinistre. %%%% -lorocyproca - -Une grande silhouette décharné, drapé dans une longue robe s'ecoulant tel une -forme vivante. Elle a soif d'énergies magiques. -%%%% lost soul L'écho d'une âme endeuillé, vacillant dans et hors du monde des mortels. Bien diff --git a/crawl-ref/source/dat/descript/fr/spells.txt b/crawl-ref/source/dat/descript/fr/spells.txt index e021dc96e0b..b082eb1528b 100644 --- a/crawl-ref/source/dat/descript/fr/spells.txt +++ b/crawl-ref/source/dat/descript/fr/spells.txt @@ -753,7 +753,7 @@ Summon Fire Elementals spell Ce sort invoque un ou plusieurs élémentaires de feu. %%%% -Summon Hell Beast spell +Summon Sin Beast spell En utilisant sa corne, Geryon peut convoquer contre vous une meurtrière bête de l'enfer. diff --git a/crawl-ref/source/dat/descript/gods.txt b/crawl-ref/source/dat/descript/gods.txt index 17f90421099..03debb6e9b9 100644 --- a/crawl-ref/source/dat/descript/gods.txt +++ b/crawl-ref/source/dat/descript/gods.txt @@ -237,7 +237,7 @@ Beogh powers Worshippers will gain the power to call down Beogh's wrath upon their oppressors, and other orcs will start to see you as one of their own. Especially -promising adherents will find themselves beset by rivals for Beogh's favor, but +promising adherents will find themselves beset by rivals for Beogh's favour, but overcoming these trials without fleeing will gain you loyal companions whom Beogh will resurrect time and time again - just so long as you continue to fight and avenge in their name. A true chosen one may even rally an entire orcish army @@ -632,7 +632,7 @@ dangerous. the Shining One wrath The Shining One's wrath is straightforward and unmistakable. Its victims find -themselves smote with holy fire and beset with holy warriors. Sinners will be +themselves smitten with holy fire and beset with holy warriors. Sinners will be silenced, and the Shining One will also shout to alert the victim's foes. %%%% Trog wrath diff --git a/crawl-ref/source/dat/descript/items.txt b/crawl-ref/source/dat/descript/items.txt index 99dd5d45ba1..4471ed75656 100644 --- a/crawl-ref/source/dat/descript/items.txt +++ b/crawl-ref/source/dat/descript/items.txt @@ -247,6 +247,12 @@ book of lightning A book that fairly crackles with arcane power. %%%% +book of maladies + +A concise catalogue of afflictions, both magical and mundane, often described +with a disquieting degree of cheerfulness. Most of them operate on timescales +too slow to be useful to an adventurer, but a few sound remarkably efficacious. +%%%% book of minor magic A spellbook containing a variety of simple but useful magical incantations. @@ -345,7 +351,7 @@ dwarves' civilisation into ruin. book of the hunter A mundane book on hunting. Someone has gone through and covered the book in -corrections and scathing criticisms, crossing out entire sections in favor of +corrections and scathing criticisms, crossing out entire sections in favour of spells that accomplish the same task. %%%% book of the moon @@ -462,6 +468,21 @@ A heavy piece of wood. While clubs are rather primitive weapons, bashing someone's skull in with one remains a very popular and effective means of dispute resolution. %%%% +codex of conductivity + +An exhaustive collection of research notes on the effects of electricity upon +everything from sawdust to snozzcumbers, interspersed with spell formulae and +usable technical schematics. The author's handwriting deteriorates shockingly in +the middle of the chapter on spriggan electrophysiology, leading to much +speculation about both their identity and their sense of self-preservation. +%%%% +compendium of siegecraft + +An analytical treatise on siege warfare, written in a clear and readable manner. +A majority of its chapters are concerned with logistics and the maintenance of +supply lines, but several detail spells useful for breaching fortifications +(and also other, softer, things). +%%%% condenser vane A magical device that condenses ambient energy into various destructive clouds. @@ -626,7 +647,7 @@ flux talisman A canister holding an ominously glowing substance. Its user becomes unstable at a fundamental level, inflicting dangerous levels of magical contamination on -foes with each touch. Sufficient contamination causes a highly localized meltdown, +foes with each touch. Sufficient contamination causes a highly localised meltdown, dealing enormous damage which increases with the user's Shapeshifting skill. However, this instability makes the user unable to use weapons, shields or body @@ -888,7 +909,7 @@ forces they could not hope to control. %%%% orb -A sphere of crystal, carried in place of a shield. Magic scintilliates within +A sphere of crystal, carried in place of a shield. Magic scintillates within its interior. %%%% orb of zot diff --git a/crawl-ref/source/dat/descript/ja/spells.txt b/crawl-ref/source/dat/descript/ja/spells.txt index 957a564c43b..9e09358f52a 100644 --- a/crawl-ref/source/dat/descript/ja/spells.txt +++ b/crawl-ref/source/dat/descript/ja/spells.txt @@ -685,7 +685,7 @@ Summon Fire Elementals spell この呪文は一体または複数の火の精霊を召喚する。 %%%% -Summon Hell Beast spell +Summon Sin Beast spell ゲリュオンは彼の角笛を使い、致命的な悪魔の獣人を召喚し、 あなたにけしかけることができる。 diff --git a/crawl-ref/source/dat/descript/ko/monsters.txt b/crawl-ref/source/dat/descript/ko/monsters.txt index ed7f852a07f..b24aabdc382 100644 --- a/crawl-ref/source/dat/descript/ko/monsters.txt +++ b/crawl-ref/source/dat/descript/ko/monsters.txt @@ -976,7 +976,7 @@ harpy 날카로운 발톱과 여러 번 가해지는 일격으로 많은 운없는 여행자들을 갈갈이 찢어놓았기 때문에, 경계하는 자들에게 공포를 심어주었다. %%%% -hell beast +sin beast 인간과 짐승이 교배하여 만들어진 끔찍한 짐승으로, 듣는 것만으로도 공포에 질릴법한 동물의 울음소리를 내고 있다. @@ -1224,11 +1224,6 @@ lindwurm 강력한 한 쌍의 앞발이 달린, 꼭 뱀처럼 생긴 작은 용이다. 두꺼운 비늘에서 기분 나쁜 녹색 광택이 난다. %%%% -lorocyproca - -마치 살아있는 사람처럼 긴 로브를 걸친, 키 크고 마른 형상을 한 악마다. 마력에 -굶주려있는 것처럼 보인다. -%%%% lost soul 애도받지 못한 영혼의 메아리로, 산 자들의 세상에서 깜빡이고 있다. 생전의 삶에 diff --git a/crawl-ref/source/dat/descript/ko/spells.txt b/crawl-ref/source/dat/descript/ko/spells.txt index 880d87fef58..7e32aaaa7e5 100644 --- a/crawl-ref/source/dat/descript/ko/spells.txt +++ b/crawl-ref/source/dat/descript/ko/spells.txt @@ -683,7 +683,7 @@ Summon Fire Elementals spell 이 마법은 하나 이상의 화염 정령을 소환시킨다. %%%% -Summon Hell Beast spell +Summon Sin Beast spell 자신의 뿔을 사용해, 게리욘은 극도로 위험한 지옥의 짐승들을 소환해낼 수 있다. %%%% diff --git a/crawl-ref/source/dat/descript/monsters.txt b/crawl-ref/source/dat/descript/monsters.txt index 77a371de6df..caba948293e 100644 --- a/crawl-ref/source/dat/descript/monsters.txt +++ b/crawl-ref/source/dat/descript/monsters.txt @@ -34,10 +34,14 @@ frozen spite. %%%% Arachne -Once a talented human weaver, Arachne was cursed by being transformed into her -present form: half-human, half-spider. Her sympathies now lie with the inhuman -residents of her new home, and she's eager to dispose of any who would threaten -them. +Once a talented human weaver, Arachne was transformed into a monstrous +half-spider by a jealous rival's curse. Driven out of her village, she found +sanctuary for a time among the jorogumo before a bitter feud forced her to +flee once more. + +Now she wanders other corners of the dungeon with a small retinue of loyal +students in tow, seeking a new domain in which they can spin their peerless +tapestries in peace. %%%% Asmodeus @@ -550,6 +554,18 @@ Her people shelter deep within the living earth, so whatever has forced Vv into the oppressive openness of the Dungeon must be dire indeed. But whatever it is, she's not talking... %%%% +Xak'krixis + +It has been centuries since outsiders were last allowed access to the cavernous +libraries of the formicids, but their emissaries are still seen from time to +time - surveying other lands for resources or scientific discoveries that might +aid the colony. + +This one's study of the magical fibres excreted by orb spiders has already led +to an alchemical breakthrough, allowing them to shape fulminant prisms such that +their explosions are directed at enemies alone. Alas, their enthusiasm to +conduct a field test bodes poorly for your health. +%%%% Xtahua An ancient and mighty dragon. @@ -1241,6 +1257,11 @@ electric eel A type of fish capable of generating a powerful electric field. %%%% +electroferric vortex + +A swirling cluster of magnetic iron framents and lingering electricity crackling +between them. It dissipates rapidly unless re-electrified. +%%%% eleionoma Spirits of marshes and bogs. From the cypresses and willows of the fens stems the @@ -1447,6 +1468,11 @@ A short, impatient and unfriendly humanoid. Goblins first appeared only a few years ago, but are now remarkably numerous. Sadly, no number of raucous goblin parties will put them in a welcoming mood toward adventurers. %%%% +goblin sharper + +A goblin newly converted to Nemelex Xobeh. It's still learning about all the +cards, and, thankfully, isn't inclined to use them against you. +%%%% golden dragon A great winged dragon covered in shining golden scales. Legends say that its @@ -1501,9 +1527,10 @@ harpy A bird-like creature with a human face that roams the coasts endlessly, feared for its sharp claws and harrying strikes. %%%% -hell beast +sin beast -An abominable crossbreed of beast and man that makes horrible barking noises. +A ravenous brute of a demon - as much animal as person. Born from the residue +of mortal depravity, it gorges itself on malice and magic alike. %%%% hell hog @@ -1767,8 +1794,8 @@ reach towards the shore and crush any who draw near. large abomination A hideous conglomeration of mangled body parts, possessed and altered in ways -that challenge sanity. Each varies in durability by the nature of whatever -unfortunates were used to make it. +that challenge sanity. Each varies in speed, strength, or resilience by +the nature of whatever unfortunates were used to make it. %%%% laughing skull @@ -1806,11 +1833,6 @@ living spell A spell, waiting to cast itself at the first foe that enters its range. Once it does, it'll dissipate into the ether. %%%% -lorocyproca - -A tall and gaunt figure, draped in long robes which flow as if alive. It -thirsts for magical energies. -%%%% lost soul The echo of an unmourned soul. Though the memories of its living self are @@ -1837,6 +1859,16 @@ A hideous cross-breed, bearing the features of a human and a lion, with great bat-like wings. Its tail bristles with spikes that can be loosed at potential prey. %%%% +marrowcuda + +The flame-wreathed skeleton of a carnivorous fish, darting through air as easily +as it once parted water. It burns with a hunger it could not satisfy in life and +is singularly driven by in death. + +While a single one may be only a nuisance, the swarm is never far behind, and +every bite it lands will summon more and more of them until every scrap of flesh +on its prey has been devoured. +%%%% martyred shade The wandering shade of a long-dead soul who died for a forgotten cause. Though @@ -2151,6 +2183,18 @@ pile of debris A heavily ruptured stonework, surrounded by rubble. %%%% +pillar of rime + +A spire of strangely tenebrous ice, its brittle tendrils reaching skyward like +the fingers of an outstretched hand. {{ + if spells.memorised("rimeblight") then + return "If only the dying breath of every soul could create such art." + else + return "It might even be beautiful if you didn't know the gruesome " .. + "way it formed." + end +}} +%%%% pillar of salt A pillar of salt shaped like a statue, but worked by no living hands. @@ -2172,6 +2216,16 @@ polar bear A bear covered in glistening white fur, habituated to cold conditions. Like all ursines, it will become enraged if badly hurt. %%%% +polterguardian + +Many poltergeists are fiercely territorial over the domains they choose to +haunt, but these have extended that protection even to the people who share +their domain with them. Some say they must be the lingering souls of those who +guarded the Vaults in life; others suggest the Ironbound Order simply recruited +them in common cause. Whichever is true, their very presence shelters other +creatures from missiles, causing sling and spell alike to veer off-course at the +last moment. +%%%% profane servitor A blasphemous being that was once an angel of the Shining One. It now stands as @@ -2382,6 +2436,17 @@ A member of an order of monks who abandoned their former faith. It now serves a minor god of wind and whispers, who allows it to calm the air and, when in great need, to break the stillness with divine thunder. %%%% +seismic cannon + +A bulky seige weapon, magically hewn from rock and metal, forever drilling into +the ground beneath itself to replenish ammunition. Every shot it fires draws a +portion of the minerals used to fabricate it into its own frame, strengthening +it if damaged. + +When fully assembled, it can turn its stockpiled ammunition to an even more +destructive purpose, unleashing a devastating seismic shockwave that destroys +the cannon in the process. +%%%% shadow A wisp of unliving shadow, slipping in and out of the darkness of the dungeon. @@ -2481,8 +2546,8 @@ to do so in confined spaces. %%%% small abomination -A hideous mass of body parts. Each varies in durability by the nature of -whatever unfortunates were used to make it. +A hideous mass of body parts. Each varies in speed, strength, or resilience by +the nature of whatever unfortunates were used to make it. %%%% smoke demon @@ -2789,6 +2854,12 @@ ufetubus A chattering and shrieking minor demon. They delight in sneaking past and backstabbing their prey. %%%% +undying armoury + +A swirling tempest of souls and steel: knights and blacksmiths and +weaponmasters, all bound to weapons they once held in life. To fight one is like +fighting an entire army. +%%%% ugly thing An ugly thing. According to legend, it is the otherworldly spawn of a wizard's diff --git a/crawl-ref/source/dat/descript/mutations.txt b/crawl-ref/source/dat/descript/mutations.txt index 7ee29ec1e50..aad55fdc2ea 100644 --- a/crawl-ref/source/dat/descript/mutations.txt +++ b/crawl-ref/source/dat/descript/mutations.txt @@ -792,7 +792,7 @@ faith mutation You were embalmed with holy oils and sacred spices and bound about with blessed cloth. Now you have a special connection with the divine, allowing you to gain -favor with your chosen god more quickly. +favour with your chosen god more quickly. %%%% devour on kill mutation diff --git a/crawl-ref/source/dat/descript/quotes.txt b/crawl-ref/source/dat/descript/quotes.txt index e9774ec401d..c725429d4a9 100644 --- a/crawl-ref/source/dat/descript/quotes.txt +++ b/crawl-ref/source/dat/descript/quotes.txt @@ -3169,13 +3169,6 @@ lindwurm Geschichten, Sagen, Legenden, Märchen, Skizzen und Heldenmahlen, Aus allen Gegenden Deutschlands und des österreichischen Kaiserstaates_. 1844. %%%% -lorocyproca - -“There it had assumed a wild, incalculable and incredible shape, twisted into a - fantastic arabesque — invisible to their eyes, but dreadful nonetheless — into - the unfamiliar numeral under whose menace they lived.” - -Bruno Schulz, “The Brilliant Epoch”. 1937. -%%%% lost soul “She walks in the twilight, her steps make no sound, diff --git a/crawl-ref/source/dat/descript/ru/monsters.txt b/crawl-ref/source/dat/descript/ru/monsters.txt index 676b24787a8..1b3fd116678 100644 --- a/crawl-ref/source/dat/descript/ru/monsters.txt +++ b/crawl-ref/source/dat/descript/ru/monsters.txt @@ -783,7 +783,7 @@ guardian serpent чувством долга, делает их прославленными стражами. Говорят, что змей-страж предпочтет умереть в бою, чем отдать хоть что-либо незваному гостю. %%%% -hell beast +sin beast Отвратительный гибрид зверя и человека, издающий жуткие лающие звуки. %%%% @@ -1003,11 +1003,6 @@ lindwurm Небольшой змееподобный дракон с парой сильных передних конечностей. Его толстая чешуя светится зеленоватым светом. %%%% -lorocyproca - -Высокая тощая фигура, замотанная в длинные одежды, парящие словно живые. Она -жаждет магической силы. -%%%% manticore Устращающий гибрид, обладающий чертами человека и льва, огромными кожистыми diff --git a/crawl-ref/source/dat/descript/spells.txt b/crawl-ref/source/dat/descript/spells.txt index 7374bfa27a7..84915a44ca8 100644 --- a/crawl-ref/source/dat/descript/spells.txt +++ b/crawl-ref/source/dat/descript/spells.txt @@ -66,11 +66,6 @@ the caster. The electricity continues to arc through everything in a connected chain, though the caster themself is unaffected. The damage dealt bypasses half of defenders' armour. %%%% -Aura of Brilliance spell - -Empowers the magic of nearby wizard allies of the caster, allowing them to -cast their magic more powerfully and more often. -%%%% Avatar Song spell Sings a powerfully haunting song; in addition to making nearby victims @@ -106,6 +101,11 @@ Berserk Other spell Causes a nearby ally to fly into a berserk rage. Going berserk temporarily increases health, speed, and damage dealt in melee. %%%% +Bestow Arms spell + +Temporarily grants numerous nearby allies a magical weapon from within the +armoury, so long as the souls holding it together remain intact. +%%%% Bind Souls spell Bestows an enchantment on other nearby living creatures that causes them to @@ -615,6 +615,12 @@ Freezes the air around a target, causing significant harm and slowing the target's movement. Half of its damage bypasses cold resistance. It has no effect on targets that are already frozen. %%%% +Flashing Balestra spell + +A single soul from within the armoury leaps out, striking with their weapon and +then duelling independently for a short time before returning to the maelstrom +once more. +%%%% Force Lance spell Fires a shaft of concussive force. If the impact deals damage, it may knock the @@ -667,6 +673,21 @@ Fulminant Prism spell Conjures a prism which unleashes a violent explosion of arcane force after a short duration. If destroyed prematurely, its blast will be much weaker. %%%% +Fulsome Fusillade spell + +A magnum opus of reckless chemistry, this spell conjures up a surfeit of +volatile reagents to rain down upon your enemies. For the next several turns, up +to 3 random enemies in sight will be struck by an exploding beaker that inflicts +either fire, cold, electric, or poison damage. If any of these explosions +overlap each other, the resulting reaction will inflict even greater +irresistible damage and sometimes apply a random deleterious status on whoever +is caught in the blast. + +This spell costs a small amount of the caster's magical energy each turn to +maintain, and while it will never aim an explosion where it could hurt the +caster, the caster's allies (and anything else in the general vicinity) are not +so fortunate. +%%%% Funeral Dirge spell A ritual chant that briefly rouses several nearby undead allies to move more @@ -918,6 +939,16 @@ Magic Dart spell Fires a small bolt of magical energy which never misses. %%%% +Magnavolt spell + +Envelops a target in a thin layer of magnetically charged metal fragments, then +unleashes a powerful surge of electricity that arcs unerringly towards every +visible target coated in these fragments. + +Magnetised creatures are additionally unable to evade attacks, and will leave +behind a short-lived vortex of electrified metal upon death that will continue +to serve as a lightning rod for this spell. +%%%% Major Destruction spell Shoots a random harmful beam or explosion at the targeted creature. @@ -998,8 +1029,7 @@ Maxwell's Portable Piledriver spell Compresses an envelope of space around the caster and an adjacent enemy, then allows it to decompress abruptly, propelling both of them forward until the latter collides with something, inflicting damage proportional to the distance -travelled. The recoil will leave the caster unable to move for a brief while, -and unable to recast the spell until they regain mobility. +travelled. Originally designed for use in construction, it will automatically target the nearest mass with the longest clear path towards a wall or other solid object @@ -1227,6 +1257,11 @@ Portal Projectile spell Teleports a fired or thrown missile at a targeted enemy. %%%% +Prayer of Brilliance spell + +Empowers the magic of nearby wizard allies of the caster, allowing them to +cast their magic more powerfully and more often. +%%%% Primal Wave spell Conjures a torrent of water that may knock back the target, and creates a @@ -1277,6 +1312,19 @@ Calls a surge of power from the earth, striking an enemy within the caster's sight. The spell cascades through nearby constructs, growing in strength with each one standing near the target. %%%% +Rimeblight spell + +Afflicts a living, demonic, or holy creature with a magical plague that slowly +freezes them from the inside out. Victims take AC-ignoring cold damage each +turn, and as their disease progresses, shards of crystalline ice may erupt +violently from their flesh, damaging nearby enemies and even spreading the +affliction to others. + +The disease feeds off the necromantic energies released by its host's suffering, +and as they near death, it may accelerate rapidly, devouring the last +vestiges of their body and leaving nothing behind but a twisted pillar of +stygian ice. +%%%% Roll spell Roll into a ball, granting the boulder beetle tremendous speed as well as @@ -1333,6 +1381,18 @@ Searing Breath spell Breathes a blast of fire at a targeted creature, leaving a cloud of flames at the endpoint. %%%% +Seismic Cannonade spell + +Shapes a trio of alchemical cannons around the caster that drill into the earth +beneath them, transmuting the rocky minerals they find there into razor-sharp +projectiles. Initially created in an incomplete state, each salvo they fire +crystallizes more material into their frame, healing them. + +Once a cannon has fully assembled itself, recasting this spell will allow you to +direct its full power into the ground, unleashing a powerful seismic shockwave +that devastates a targeted area at the cost of sacrificing every cannon in the +process. +%%%% Sentinel's Mark spell Bestows a strong beacon upon a targeted creature. It announces the presence @@ -1519,6 +1579,10 @@ Stone Arrow spell Fires a sharp spine of rock. %%%% +Stone Bullet spell + +Fires a hardened shard of rock over a long distance. +%%%% Strip Willpower spell Greatly reduces the target's willpower, making them more vulnerable to a @@ -1606,9 +1670,9 @@ while still active, the exposed core will violently detonate a moment later. It gains greater explosive damage at higher power. %%%% -Summon Hell Beast spell +Summon Sin Beast spell -Summons a deadly hell beast. +Summons a deadly sin beast. %%%% Summon Hell Sentinel spell @@ -1711,7 +1775,8 @@ never directly lethal. %%%% Teleport Other spell -Randomly translocates a targeted creature. +Attempts to teleport a targeted creature out of the caster's sight, after a +short delay. %%%% Throw Ally spell diff --git a/crawl-ref/source/dat/descript/status.txt b/crawl-ref/source/dat/descript/status.txt index fc6a941f37e..40b3185fb83 100644 --- a/crawl-ref/source/dat/descript/status.txt +++ b/crawl-ref/source/dat/descript/status.txt @@ -507,7 +507,7 @@ Flux status You are unstable at a fundamental level. You inflict dangerous levels of magical contamination on foes with each touch. Sufficient contamination -causes a highly localized meltdown, dealing enormous damage. +causes a highly localised meltdown, dealing enormous damage. However, this instability makes you unable to use weapons, shields, or body armour. You also glow, reducing stealth and preventing invisibility, @@ -934,7 +934,7 @@ will blast you into the level above. %%%% Challenge status -You have been challenged by a chosen Apostle of Beogh, who will know your +You have been challenged by a chosen apostle of Beogh, who will know your location no matter where on the floor you are. Defeat them, and you may recruit them to join you. If you flee the floor, Beogh will punish you by temporarily depriving you of allies. @@ -987,3 +987,12 @@ Stiff status Your legs have stiffened up from lack of motion, making your next move twice as slow as usual. This only affects movement actions. %%%% +Fusillade status + +You are hurling conjured alchemical reagents at nearby monsters. +%%%% +Shockwave status + +One of your seismic cannons is fully assembled and ready to unleash a seismic +shockwave. (Recast Seismic Cannonade to aim it.) +%%%% diff --git a/crawl-ref/source/dat/descript/unrand.txt b/crawl-ref/source/dat/descript/unrand.txt index bbc91cec593..9999e336d2a 100644 --- a/crawl-ref/source/dat/descript/unrand.txt +++ b/crawl-ref/source/dat/descript/unrand.txt @@ -669,7 +669,7 @@ huge body can wear it. %%%% warlock's mirror -A handheld array of hexagonal mirrors, synthesized from a truly exotic +A handheld array of hexagonal mirrors, synthesised from a truly exotic substance and polished to a blinding sheen. %%%% amulet of invisibility diff --git a/crawl-ref/source/dat/descript/zh/monsters.txt b/crawl-ref/source/dat/descript/zh/monsters.txt index f135915e6eb..360504fa4e7 100644 --- a/crawl-ref/source/dat/descript/zh/monsters.txt +++ b/crawl-ref/source/dat/descript/zh/monsters.txt @@ -1355,7 +1355,7 @@ harpy 一个长有人脸的鸟状生物,无休止地在海岸上飞荡,要当心她的利爪和扑打。 %%%% -hell beast +sin beast 一个令人厌恶的人兽杂种,会发出令人恐惧的咆哮。 %%%% @@ -1630,10 +1630,6 @@ living spell 这个法术,在等待着时机,向第一个进入其射程的敌人施放。 一旦发生,它便会消散到以太中。 %%%% -lorocyproca - -一个高大、憔悴的身形,它穿着的长袍仿佛有生命一般飘动着。它渴望着魔力。 -%%%% lost soul 一个无人哀悼的灵魂的回音。尽管它自己生前的记忆已经褪去很久了, diff --git a/crawl-ref/source/dat/descript/zh/spells.txt b/crawl-ref/source/dat/descript/zh/spells.txt index 112da7ce53a..f868a6f3c8c 100644 --- a/crawl-ref/source/dat/descript/zh/spells.txt +++ b/crawl-ref/source/dat/descript/zh/spells.txt @@ -564,12 +564,6 @@ Funeral Dirge spell 这首仪礼歌可以短暂地唤起附近的几个亡灵盟友, 让他们更快地移动,并以更大的力量进行攻击。 %%%% -Gell's Gravitas spell - -短暂地将引力重定向到目标点附近,导致朝着该点“向下”。除了施法者以外, -所有附近的生物都会无助地跌落到该点——通常会互相碰撞, -或撞向站在那儿的受害者。 -%%%% Ghostly Fireball spell 掷出一个负能量爆炸球,将衰竭所吞噬的生物。 @@ -1330,7 +1324,7 @@ Summon Blazeheart Golem spell 能量越高,它的爆炸威力就越强。 %%%% -Summon Hell Beast spell +Summon Sin Beast spell 召唤一个致命的地狱兽。 %%%% diff --git a/crawl-ref/source/dat/dlua/gauntlet.lua b/crawl-ref/source/dat/dlua/gauntlet.lua index cdc2f632e86..2849dd1b326 100644 --- a/crawl-ref/source/dat/dlua/gauntlet.lua +++ b/crawl-ref/source/dat/dlua/gauntlet.lua @@ -430,8 +430,8 @@ tier1_gauntlet_arenas = { second = {mons = "white imp / shadow imp", min = 1, max = 3}, }, { - second = {mons = "large abomination", min = 0, max = 2}, - third = {mons = "small abomination", min = 3, max = 6}, + second = {mons = "large abomination", min = 0, max = 1}, + third = {mons = "small abomination", min = 3, max = 4}, plant = "demonic", }, { @@ -508,7 +508,7 @@ tier2_gauntlet_arenas = { plant = "demonic", }, { - first = {mons = "hell beast", min = 1, max = 1}, + first = {mons = "sin beast", min = 1, max = 1}, second = {mons = "hell hound", min = 0, max = 2}, liquid = "lava", plant = "demonic", @@ -594,7 +594,7 @@ tier2_gauntlet_arenas = { }, { second = {mons = "thrashing horror", min = 1, max = 2}, - third = {mons = "small abomination", min = 2, max = 4}, + third = {mons = "small abomination", min = 2, max = 3}, plant = "demonic", }, { @@ -608,13 +608,13 @@ tier2_gauntlet_arenas = { plant = "demonic", }, { - second = {mons = "large abomination", min = 2, max = 3}, - third = {mons = "small abomination", min = 3, max = 6}, + second = {mons = "large abomination", min = 1, max = 2}, + third = {mons = "small abomination", min = 3, max = 5}, plant = "demonic", }, { first = {mons = "wretched star", min = 1, max = 1}, - second = {mons = "neqoxec / small abomination", min = 1, max = 3}, + second = {mons = "neqoxec / small abomination", min = 1, max = 2}, loot = "potion of mutation ident:type", plant = "demonic", }, diff --git a/crawl-ref/source/dat/dlua/ziggurat.lua b/crawl-ref/source/dat/dlua/ziggurat.lua index 7ff61905b73..318c140c4c7 100644 --- a/crawl-ref/source/dat/dlua/ziggurat.lua +++ b/crawl-ref/source/dat/dlua/ziggurat.lua @@ -275,7 +275,7 @@ mset(spec_fn(function () local e = math.max(1, you.zigs_completed() * 6 + you.depth() / 2 - 11) return "place:Vaults:$ w:" .. d * 2 .. " / place:Vaults:$ 9 w:" .. d .. " / " .. "sphinx w:5 / titan w:" .. e .. " / golden dragon w:" .. e .. " / " .. - "ancient lich w:" .. e / 2 .. " / dread lich w:" .. e / 2 + "ancient lich w:" .. e / 3 .. " / dread lich w:" .. e / 3 end)) mset(spec_fn(function () @@ -374,8 +374,9 @@ mset(with_props(spec_fn(function () "torpor snail w:" .. d .. " / iron golem w:" .. d .. " / " .. "war gargoyle w:" .. d .. " / stone giant w:" .. d .. " / " .. "caustic shrike w:" .. d .. " / entropy weaver w:" .. d .. " / " .. - "crystal guardian w:" .. e .. " / iron dragon w:" .. e .. " / " .. - "iron giant w:" .. f .. " / hell sentinel w:" .. f + "iron dragon w:" .. d .. " / crystal guardian w:" .. e .. " / " .. + "undying armoury w:" .. e .. " / iron giant w:" .. f .. " / " .. + "hell sentinel w:" .. f end), { weight = 2 })) mset(with_props(spec_fn(function () @@ -505,7 +506,7 @@ mset_if(depth_ge(14), with_props(spec_fn(function () local g = 0 + you.zigs_completed() * 2 return "place:Coc:$ w:" .. d .. " / place:Dis:$ w:" .. d .. " / " .. "place:Geh:$ w:" .. d .. " / place:Tar:$ w:" .. d .. " / " .. - "place:Hell w:100 / hell beast w:" .. e .. " / " .. + "place:Hell w:100 / sin beast w:" .. e .. " / " .. "hellion w:5 / tormentor w:5 / greater demon w:" .. f .. " / " .. "shard shrike w:" .. g .. " / quicksilver elemental w:" .. g .. " / " .. "searing wretch w:" .. g .. " / silent spectre w:" .. g diff --git a/crawl-ref/source/dat/mons/abomination-large.yaml b/crawl-ref/source/dat/mons/abomination-large.yaml index 4a9380c543e..bc2bffbc2bf 100644 --- a/crawl-ref/source/dat/mons/abomination-large.yaml +++ b/crawl-ref/source/dat/mons/abomination-large.yaml @@ -1,16 +1,15 @@ name: "large abomination" enum: abomination_large glyph: {char: "X", colour: lightred} -flags: [no_regen] genus: abomination_small holiness: [undead] will: 100 attacks: - - {type: hit, damage: 40} + - {type: hit, damage: 32} hd: 11 hp_10x: 495 -ac: 0 -ev: 0 +ac: 11 +ev: 6 intelligence: brainless size: large shape: misc diff --git a/crawl-ref/source/dat/mons/abomination-small.yaml b/crawl-ref/source/dat/mons/abomination-small.yaml index 00c55bbed52..c6df5090a8d 100644 --- a/crawl-ref/source/dat/mons/abomination-small.yaml +++ b/crawl-ref/source/dat/mons/abomination-small.yaml @@ -1,15 +1,14 @@ name: "small abomination" enum: abomination_small glyph: {char: "x", colour: lightred} -flags: [no_regen] holiness: [undead] will: 40 attacks: - - {type: hit, damage: 23} + - {type: hit, damage: 17} hd: 6 hp_10x: 270 -ac: 0 -ev: 0 +ac: 6 +ev: 9 intelligence: brainless speed: 12 size: large diff --git a/crawl-ref/source/dat/mons/arachne.yaml b/crawl-ref/source/dat/mons/arachne.yaml index 9f9024b3076..efd0c3420ac 100644 --- a/crawl-ref/source/dat/mons/arachne.yaml +++ b/crawl-ref/source/dat/mons/arachne.yaml @@ -2,7 +2,7 @@ name: "Arachne" glyph: {char: "H", colour: lightcyan} flags: [see_invis, speaks, web_immune, warm_blood, unique, female] genus: spider -will: 60 +will: 80 attacks: - {type: hit, damage: 30} hd: 17 diff --git a/crawl-ref/source/dat/mons/electroferric-vortex.yaml b/crawl-ref/source/dat/mons/electroferric-vortex.yaml new file mode 100644 index 00000000000..0ac27b64eb0 --- /dev/null +++ b/crawl-ref/source/dat/mons/electroferric-vortex.yaml @@ -0,0 +1,16 @@ +name: "electroferric vortex" +glyph: {char: "v", colour: lightcyan} +flags: [flies, stationary, no_exp_gain, conjured, remnant, no_threat] +resists: {elec: 3} +holiness: [nonliving] +will: invuln +attacks: +hd: 10 +hp_10x: 500 +ac: 5 +ev: 15 +intelligence: brainless +speed: 10 +size: medium +shape: misc +tile_variance: cycle diff --git a/crawl-ref/source/dat/mons/lorocyproca.yaml b/crawl-ref/source/dat/mons/lorocyproca.yaml deleted file mode 100644 index b3cb775d1b9..00000000000 --- a/crawl-ref/source/dat/mons/lorocyproca.yaml +++ /dev/null @@ -1,18 +0,0 @@ -name: "lorocyproca" -glyph: {char: "2", colour: blue} -flags: [invis, see_invis] -resists: {elec: 1, poison: 1, fire: 1, cold: 1} -xp_mult: 14 -holiness: [demonic] -will: 140 -attacks: - - {type: hit, flavour: antimagic, damage: 40} -hd: 14 -hp_10x: 770 -ac: 10 -ev: 12 -shout: moan -intelligence: human -uses: open_doors -size: medium -shape: humanoid diff --git a/crawl-ref/source/dat/mons/marrowcuda.yaml b/crawl-ref/source/dat/mons/marrowcuda.yaml new file mode 100644 index 00000000000..18ac0cf4a5c --- /dev/null +++ b/crawl-ref/source/dat/mons/marrowcuda.yaml @@ -0,0 +1,18 @@ +name: "marrowcuda" +glyph: {char: "F", colour: lightblue} +resists: {cold: 1} +flags: [flies] +xp_mult: 9 +species: skeleton +holiness: [undead] +will: 1 +attacks: + - {type: bite, flavour: swarm, damage: 8} +hd: 4 +hp_10x: 235 +ac: 4 +ev: 11 +intelligence: brainless +speed: 13 +size: small +shape: fish diff --git a/crawl-ref/source/dat/mons/martyred-shade.yaml b/crawl-ref/source/dat/mons/martyred-shade.yaml index 23617bf306b..66b76b59243 100644 --- a/crawl-ref/source/dat/mons/martyred-shade.yaml +++ b/crawl-ref/source/dat/mons/martyred-shade.yaml @@ -1,6 +1,6 @@ name: "martyred shade" glyph: {char: "W", colour: yellow} -flags: [flies, speaks, insubstantial, fast_regen] +flags: [flies, speaks, insubstantial, fast_regen, has_aura] genus: phantom holiness: [undead] will: 60 diff --git a/crawl-ref/source/dat/mons/ophan.yaml b/crawl-ref/source/dat/mons/ophan.yaml index 322a2f47771..f5bbb7223d2 100644 --- a/crawl-ref/source/dat/mons/ophan.yaml +++ b/crawl-ref/source/dat/mons/ophan.yaml @@ -1,6 +1,6 @@ name: "ophan" glyph: {char: "G", colour: red} -flags: [flies, see_invis] +flags: [flies, see_invis, has_aura] xp_mult: 14 genus: angel holiness: [holy] diff --git a/crawl-ref/source/dat/mons/pillar-of-rime.yaml b/crawl-ref/source/dat/mons/pillar-of-rime.yaml new file mode 100644 index 00000000000..7197d448a90 --- /dev/null +++ b/crawl-ref/source/dat/mons/pillar-of-rime.yaml @@ -0,0 +1,15 @@ +name: "pillar of rime" +glyph: {char: "I", colour: etc_ice} +flags: [stationary, no_exp_gain, remnant, no_threat] +resists: {fire: -1, cold: 3} +holiness: [nonliving] +will: invuln +attacks: +hd: 3 +hp_10x: 375 +ac: 15 +ev: 0 +intelligence: brainless +uses: starting_equipment +size: medium +shape: misc diff --git a/crawl-ref/source/dat/mons/polterguardian.yaml b/crawl-ref/source/dat/mons/polterguardian.yaml new file mode 100644 index 00000000000..8a8492ba744 --- /dev/null +++ b/crawl-ref/source/dat/mons/polterguardian.yaml @@ -0,0 +1,17 @@ +name: "polterguardian" +glyph: {char: "v", colour: lightgreen} +flags: [flies, insubstantial, cautious, has_aura] +resists: {cold: 1} +xp_mult: 7 +holiness: [undead] +will: 80 +attacks: +hd: 11 +hp_10x: 650 +ac: 6 +ev: 18 +spells: polterguardian +intelligence: human +uses: open_doors +size: medium +shape: misc diff --git a/crawl-ref/source/dat/mons/seismic-cannon.yaml b/crawl-ref/source/dat/mons/seismic-cannon.yaml new file mode 100644 index 00000000000..2dc49399846 --- /dev/null +++ b/crawl-ref/source/dat/mons/seismic-cannon.yaml @@ -0,0 +1,16 @@ +name: "seismic cannon" +glyph: {char: "I", colour: yellow} +flags: [speaks, see_invis, stationary, no_regen] +resists: {elec: 1, fire: 2, cold: 2} +holiness: [nonliving] +will: invuln +attacks: +hd: 8 +hp_10x: 440 +ac: 15 +ev: 0 +spells: seismic_cannon +intelligence: brainless +size: large +shape: misc +speed: 5 diff --git a/crawl-ref/source/dat/mons/hell-beast.yaml b/crawl-ref/source/dat/mons/sin-beast.yaml similarity index 60% rename from crawl-ref/source/dat/mons/hell-beast.yaml rename to crawl-ref/source/dat/mons/sin-beast.yaml index bfed74fade8..6a43a8d946a 100644 --- a/crawl-ref/source/dat/mons/hell-beast.yaml +++ b/crawl-ref/source/dat/mons/sin-beast.yaml @@ -1,14 +1,14 @@ -name: "hell beast" -glyph: {char: "2", colour: brown} +name: "sin beast" +glyph: {char: "2", colour: cyan} flags: [fighter] xp_mult: 17 holiness: [demonic] will: 20 attacks: - - {type: bite, damage: 28} + - {type: bite, flavour: antimagic, damage: 28} - {type: trample, flavour: trample, damage: 20} -hd: 7 -hp_10x: 840 +hd: 12 +hp_10x: 810 ac: 5 ev: 14 shout: random @@ -16,4 +16,4 @@ intelligence: human speed: 15 uses: open_doors size: large -shape: quadruped +shape: humanoid diff --git a/crawl-ref/source/dat/mons/torpor-snail.yaml b/crawl-ref/source/dat/mons/torpor-snail.yaml index 0871563ad31..15dc9226adf 100644 --- a/crawl-ref/source/dat/mons/torpor-snail.yaml +++ b/crawl-ref/source/dat/mons/torpor-snail.yaml @@ -1,6 +1,6 @@ name: "torpor snail" glyph: {char: "w", colour: green} -flags: [web_immune, no_skeleton] +flags: [web_immune, no_skeleton, has_aura] xp_mult: 20 genus: elephant_slug will: 80 diff --git a/crawl-ref/source/dat/mons/ufetubus.yaml b/crawl-ref/source/dat/mons/ufetubus.yaml index 5ff2a853a95..8808b8fd64f 100644 --- a/crawl-ref/source/dat/mons/ufetubus.yaml +++ b/crawl-ref/source/dat/mons/ufetubus.yaml @@ -5,8 +5,8 @@ xp_mult: 28 holiness: [demonic] will: 10 attacks: - - {type: hit, flavour: flank, damage: 5} - - {type: hit, damage: 5} + - {type: hit, flavour: flank, damage: 6} + - {type: hit, damage: 6} hd: 1 hp_10x: 90 ac: 2 diff --git a/crawl-ref/source/dat/mons/undying-armoury.yaml b/crawl-ref/source/dat/mons/undying-armoury.yaml new file mode 100644 index 00000000000..148f539620d --- /dev/null +++ b/crawl-ref/source/dat/mons/undying-armoury.yaml @@ -0,0 +1,20 @@ +name: "undying armoury" +glyph: {char: "v", colour: lightmagenta} +flags: [flies, insubstantial, fighter] +resists: {fire: 1, cold: 1, elec: 1} +xp_mult: 11 +holiness: [undead] +will: 120 +attacks: + - {type: hit, damage: 27} + - {type: hit, damage: 27} + - {type: hit, damage: 27} +hd: 17 +hp_10x: 970 +ac: 11 +ev: 19 +spells: undying_armoury +intelligence: human +uses: open_doors +size: medium +shape: misc diff --git a/crawl-ref/source/dat/mons/xakkrixis.yaml b/crawl-ref/source/dat/mons/xakkrixis.yaml new file mode 100644 index 00000000000..7992b19edeb --- /dev/null +++ b/crawl-ref/source/dat/mons/xakkrixis.yaml @@ -0,0 +1,21 @@ +name: "Xak'krixis" +enum: xakkrixis +glyph: {char: "B", colour: lightgreen} +flags: [see_invis, speaks, unique, warm_blood, no_skeleton, gender_neutral, burrows] +genus: formicid +will: 80 +attacks: + - {type: hit, damage: 24} +hd: 17 +hp_10x: 1650 +ac: 3 +ev: 10 +spells: xakkrixis +has_corpse: true +shout: shout +intelligence: human +speed: 10 +uses: weapons_armour +size: medium +shape: humanoid +corpse_tile: formicid diff --git a/crawl-ref/source/delay.cc b/crawl-ref/source/delay.cc index 6be48226cdf..b63b7a5db48 100644 --- a/crawl-ref/source/delay.cc +++ b/crawl-ref/source/delay.cc @@ -56,6 +56,7 @@ #include "religion.h" #include "shout.h" #include "sound.h" +#include "spl-damage.h" #include "spl-selfench.h" #include "spl-transloc.h" // attract_monster #include "spl-util.h" diff --git a/crawl-ref/source/describe-spells.cc b/crawl-ref/source/describe-spells.cc index 30167f44754..f1c6390549a 100644 --- a/crawl-ref/source/describe-spells.cc +++ b/crawl-ref/source/describe-spells.cc @@ -411,6 +411,8 @@ static dice_def _spell_damage(spell_type spell, int hd) return polar_vortex_dice(pow, false); case SPELL_ELECTROLUNGE: return electrolunge_damage(pow); + case SPELL_FULMINANT_PRISM: + return prism_damage(prism_hd(pow, false), true); // This is the per-turn *sticky flame* damage against the player. // The spell has no impact damage and otherwise uses different numbers diff --git a/crawl-ref/source/describe.cc b/crawl-ref/source/describe.cc index 793463f92ff..551b56a6a05 100644 --- a/crawl-ref/source/describe.cc +++ b/crawl-ref/source/describe.cc @@ -1758,10 +1758,10 @@ static string _describe_weapon_brand(const item_def &item) "armour. Undead and demons cannot use this."; case SPWPN_FOUL_FLAME: return "It has been infused with foul flame, dealing an additional " - "three-quarters of damage to holy beings, an additional quarter " - "damage to undead and demons, and an additional half damage to " - "all others. Holy beings and good god worshippers cannot use " - "this."; + "three-quarters damage to holy beings, an additional " + "one-quarter damage to undead and demons, and an additional " + "half damage to all others, so long as it pierces armour. " + "Holy beings and good god worshippers cannot use this."; case SPWPN_ELECTROCUTION: return "It sometimes electrocutes victims (1/4 chance, 8-20 damage)."; case SPWPN_VENOM: @@ -3101,7 +3101,9 @@ string get_item_description(const item_def &item, if (item.base_type == OBJ_ARMOUR || item.base_type == OBJ_WEAPONS) { - if (you.has_mutation(MUT_ARTEFACT_ENCHANTING)) + if (!item_ident(item, ISFLAG_KNOW_PLUSES)) + description << "\nIt is an ancient artefact."; + else if (you.has_mutation(MUT_ARTEFACT_ENCHANTING)) { if (is_unrandom_artefact(item) || (item.base_type == OBJ_ARMOUR @@ -4597,6 +4599,8 @@ static void _get_spell_description(const spell_type spell, description += "\nRange : "; if (spell == SPELL_CALL_DOWN_LIGHTNING) description += stringize_glyph(mons_char(mon_owner->type)) + "..---->"; + else if (spell == SPELL_FLASHING_BALESTRA) + description += stringize_glyph(mons_char(mon_owner->type)) + "..-->"; else description += range_string(range, range, mons_char(mon_owner->type)); description += "\n"; @@ -4965,6 +4969,7 @@ static string _flavour_base_desc(attack_flavour flavour) { AF_DRAG, "drag the defender backwards"}, { AF_FOUL_FLAME, "extra damage, especially to the good" }, { AF_HELL_HUNT, "summon demonic beasts" }, + { AF_SWARM, "summon more of itself" }, { AF_PLAIN, "" }, }; diff --git a/crawl-ref/source/directn.cc b/crawl-ref/source/directn.cc index cdb9e9484c7..12c778a3652 100644 --- a/crawl-ref/source/directn.cc +++ b/crawl-ref/source/directn.cc @@ -104,13 +104,14 @@ static bool _blocked_ray(const coord_def &where); static bool _want_target_monster(const monster *mon, targ_mode_type mode, targeter* hitfunc); static bool _find_monster(const coord_def& where, targ_mode_type mode, - bool need_path, int range, targeter *hitfunc); + bool need_path, int range, targeter *hitfunc, + bool find_preferred = false); static bool _find_monster_expl(const coord_def& where, targ_mode_type mode, bool need_path, int range, targeter *hitfunc, aff_type mon_aff, aff_type allowed_self_aff); static bool _find_shadow_step_mons(const coord_def& where, targ_mode_type mode, bool need_path, int range, - targeter *hitfunc); + targeter *hitfunc, bool find_preferred = false); static bool _find_object(const coord_def& where, bool need_path, int range, targeter *hitfunc); static bool _find_autopickup_object(const coord_def& where, bool need_path, @@ -443,10 +444,13 @@ static bool _mon_exposed(const monster* mon) } static bool _is_target_in_range(const coord_def& where, int range, - targeter *hitfunc) + targeter *hitfunc, bool find_preferred = false) { if (hitfunc) - return hitfunc->valid_aim(where); + { + return find_preferred ? hitfunc->preferred_aim(where) + : hitfunc->valid_aim(where); + } // range == -1 means that range doesn't matter. return range == -1 || grid_distance(you.pos(), where) <= range; } @@ -1095,7 +1099,9 @@ bool direction_chooser::find_default_monster_target(coord_def& result) const const monster* mons_target = _get_current_target(); if (mons_target != nullptr && _want_target_monster(mons_target, mode, hitfunc) - && in_range(mons_target->pos())) + && in_range(mons_target->pos()) + && (!hitfunc || hitfunc->preferred_aim(mons_target->pos())) + && !prefer_farthest) { result = mons_target->pos(); return true; @@ -1111,16 +1117,26 @@ bool direction_chooser::find_default_monster_target(coord_def& result) const // The previous target is no good. Try to find one from scratch. bool success = false; + // Start our search from the player's position most of the time, unless we're + // looking for the farthest target, in which case start from max LoS away + // from the player, since we will be spiraling inward. + // (_find_square already defaulted to starting at the player's left) + result = prefer_farthest ? clamp_in_bounds(you.pos() - coord_def(LOS_RADIUS, 0)) + : you.pos(); + if (Options.simple_targeting) { - success = _find_square_wrapper(result, 1, + success = _find_square_wrapper(result, prefer_farthest ? -1 : 1, bind(_find_monster, placeholders::_1, - mode, needs_path, range, hitfunc), + mode, needs_path, range, hitfunc, + false), hitfunc); } else { - success = hitfunc && _find_square_wrapper(result, 1, + // Search for a new default target, first looking for a 'preferred' target, + // if applicable, and then falling back to any valid target if none are preferred. + success = hitfunc && _find_square_wrapper(result, prefer_farthest ? -1 : 1, bind(_find_monster_expl, placeholders::_1, mode, needs_path, range, @@ -1128,12 +1144,21 @@ bool direction_chooser::find_default_monster_target(coord_def& result) const // First try to bizap AFF_MULTIPLE, AFF_YES), hitfunc) - || _find_square_wrapper(result, 1, + || _find_square_wrapper(result, prefer_farthest ? -1 : 1, bind(restricts == DIR_SHADOW_STEP ? _find_shadow_step_mons : _find_monster, placeholders::_1, mode, - needs_path, range, hitfunc), + needs_path, range, hitfunc, + true), + hitfunc) + || _find_square_wrapper(result, prefer_farthest ? -1 : 1, + bind(restricts == DIR_SHADOW_STEP ? + _find_shadow_step_mons : + _find_monster, + placeholders::_1, mode, + needs_path, range, hitfunc, + false), hitfunc); } @@ -1185,7 +1210,7 @@ bool direction_chooser::find_default_monster_target(coord_def& result) const bind(restricts == DIR_SHADOW_STEP ? _find_shadow_step_mons : _find_monster, placeholders::_1, mode, false, - range, hitfunc), + range, hitfunc, false), hitfunc)) { // Special colouring in tutorial or hints mode. @@ -1399,11 +1424,9 @@ void direction_chooser::object_cycle(int dir) void direction_chooser::monster_cycle(int dir) { - if (prefer_farthest) - dir = -dir; // cycle from furthest to closest if (_find_square_wrapper(monsfind_pos, dir, bind(_find_monster, placeholders::_1, mode, - needs_path, range, hitfunc), + needs_path, range, hitfunc, false), hitfunc)) { set_target(monsfind_pos); @@ -2502,8 +2525,6 @@ bool direction_chooser::choose_direction() : find_default_target()); objfind_pos = monsfind_pos = target(); - if (prefer_farthest && moves.target != you.pos()) - monster_cycle(1); // If requested, show the beam on startup. if (show_beam) @@ -2808,7 +2829,8 @@ static bool _want_target_monster(const monster *mon, targ_mode_type mode, } static bool _find_monster(const coord_def& where, targ_mode_type mode, - bool need_path, int range, targeter *hitfunc) + bool need_path, int range, targeter *hitfunc, + bool find_preferred) { { coord_def dp = grid2player(where); @@ -2824,7 +2846,7 @@ static bool _find_monster(const coord_def& where, targ_mode_type mode, return true; // Don't target out of range - if (!_is_target_in_range(where, range, hitfunc)) + if (!_is_target_in_range(where, range, hitfunc, find_preferred)) return false; const monster* mon = monster_at(where); @@ -2848,7 +2870,7 @@ static bool _find_monster(const coord_def& where, targ_mode_type mode, static bool _find_shadow_step_mons(const coord_def& where, targ_mode_type mode, bool need_path, int range, - targeter *hitfunc) + targeter *hitfunc, bool find_preferred) { { coord_def dp = grid2player(where); @@ -2860,7 +2882,7 @@ static bool _find_shadow_step_mons(const coord_def& where, targ_mode_type mode, } // Need a monster to attack; this checks that the monster is a valid target. - if (!_find_monster(where, mode, need_path, range, hitfunc)) + if (!_find_monster(where, mode, need_path, range, hitfunc, find_preferred)) return false; // Can't step on yourself if (where == you.pos()) @@ -3543,6 +3565,9 @@ static string _describe_monster_weapon(const monster_info& mi, bool ident) desc += " wielding "; desc += name1; + if (mi.is(MB_ARMED)) + desc += " (from an undying armoury)"; + if (!name2.empty()) { desc += " and "; diff --git a/crawl-ref/source/duration-data.h b/crawl-ref/source/duration-data.h index 84795c71822..5b1f833a7a2 100644 --- a/crawl-ref/source/duration-data.h +++ b/crawl-ref/source/duration-data.h @@ -355,6 +355,10 @@ static const duration_def duration_data[] = LIGHTGREY, "Vortex", "in a vortex", "vortex", "You are in the eye of a polar vortex.", D_EXPIRES}, + { DUR_FUSILLADE, + LIGHTGREY, "Fusillade", + "raining reagents", "fusillade", + "You are unleashing a barrage of alchemical reagents.", D_EXPIRES}, { DUR_BLOOD_FOR_BLOOD, LIGHTBLUE, "Pray", "chanting a vengeful prayer", "blood for blood", @@ -614,7 +618,7 @@ static const duration_def duration_data[] = YELLOW, "-Gavotte", "on gavotte cooldown", "gavotte cooldown", "You are unable to cast Gavotte.", D_NO_FLAGS, - {{ "Gravity stabilizes in your vacinity."}}}, + {{ "Gravity stabilises in your vicinity."}}}, { DUR_ANIMATE_DEAD, MAGENTA, "Reap", "animating dead", "animating dead", diff --git a/crawl-ref/source/duration-type.h b/crawl-ref/source/duration-type.h index d65bdb7ff04..4bdf45e550e 100644 --- a/crawl-ref/source/duration-type.h +++ b/crawl-ref/source/duration-type.h @@ -258,5 +258,6 @@ enum duration_type DUR_FATHOMLESS_SHACKLES, DUR_CONSTRICTION_IMMUNITY, DUR_GAVOTTE_COOLDOWN, + DUR_FUSILLADE, NUM_DURATIONS }; diff --git a/crawl-ref/source/enchant-type.h b/crawl-ref/source/enchant-type.h index 88a9d6374a3..c66dc24268c 100644 --- a/crawl-ref/source/enchant-type.h +++ b/crawl-ref/source/enchant-type.h @@ -175,8 +175,8 @@ enum enchant_type ENCH_BONE_ARMOUR, ENCH_CHANT_FIRE_STORM, // chanting the fire storm spell ENCH_CHANT_WORD_OF_ENTROPY, // chanting word of entropy -#endif ENCH_BRILLIANCE_AURA, // emanating a brilliance aura +#endif ENCH_EMPOWERED_SPELLS, // affected by above ENCH_GOZAG_INCITE, ENCH_PAIN_BOND, // affected by above @@ -221,6 +221,9 @@ enum enchant_type ENCH_CHANNEL_SEARING_RAY, ENCH_TOUCH_OF_BEOGH, ENCH_VENGEANCE_TARGET, + ENCH_RIMEBLIGHT, + ENCH_MAGNETISED, + ENCH_ARMED, // Update enchant_names[] in mon-ench.cc when adding or removing // enchantments. NUM_ENCHANTMENTS diff --git a/crawl-ref/source/evoke.cc b/crawl-ref/source/evoke.cc index dfc47493b30..bf8ebf1b1d3 100644 --- a/crawl-ref/source/evoke.cc +++ b/crawl-ref/source/evoke.cc @@ -102,7 +102,7 @@ static bool _evoke_horn_of_geryon() if (random2(adjusted_power) > 7) beh = BEH_FRIENDLY; - mgen_data mg(MONS_HELL_BEAST, beh, you.pos(), MHITYOU, MG_AUTOFOE); + mgen_data mg(MONS_SIN_BEAST, beh, you.pos(), MHITYOU, MG_AUTOFOE); mg.set_summoned(&you, 3, SPELL_NO_SPELL); mg.set_prox(PROX_CLOSE_TO_PLAYER); mon = create_monster(mg); @@ -1083,7 +1083,6 @@ string cannot_evoke_item_reason(const item_def *item, bool temp, bool ident) && silenced(you.pos())) { return "You can't produce a sound!"; - } if (temp && is_xp_evoker(*item) && evoker_charges(item->sub_type) <= 0) diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc index e9383e1d8e5..eb85d0f47aa 100644 --- a/crawl-ref/source/fight.cc +++ b/crawl-ref/source/fight.cc @@ -925,6 +925,9 @@ bool dont_harm(const actor &attacker, const actor &defender) if (mons_aligned(&attacker, &defender)) return true; + if (defender.is_monster()) + return god_protects(&attacker, defender.as_monster(), false); + if (defender.is_player()) return attacker.wont_attack(); diff --git a/crawl-ref/source/fineff.cc b/crawl-ref/source/fineff.cc index cbd902bae3c..1bf0c66133d 100644 --- a/crawl-ref/source/fineff.cc +++ b/crawl-ref/source/fineff.cc @@ -37,6 +37,7 @@ #include "ouch.h" #include "religion.h" #include "spl-damage.h" +#include "spl-monench.h" #include "spl-summoning.h" #include "state.h" #include "stringutil.h" @@ -966,6 +967,17 @@ void dismiss_divine_allies_fineff::fire() dismiss_god_summons(god); } +void death_spawn_fineff::fire() +{ + if (monster *pillar = create_monster(mgen_data(mon_type, + BEH_HOSTILE, posn, + MHITNOT, MG_FORCE_PLACE), + false)) + { + pillar->add_ench(mon_enchant(ENCH_SLOWLY_DYING, 1, &you, duration)); + } +} + // Effects that occur after all other effects, even if the monster is dead. // For example, explosions that would hit other creatures, but we want // to deal with only one creature at a time, so that's handled last. diff --git a/crawl-ref/source/fineff.h b/crawl-ref/source/fineff.h index 70b68f4315c..df1af3a6558 100644 --- a/crawl-ref/source/fineff.h +++ b/crawl-ref/source/fineff.h @@ -575,4 +575,23 @@ class dismiss_divine_allies_fineff : public final_effect const god_type god; }; +class death_spawn_fineff : public final_effect +{ +public: + bool mergeable(const final_effect &) const override { return false; } + void fire() override; + + static void schedule(monster_type mon_type, coord_def pos, int dur) + { + final_effect::schedule(new death_spawn_fineff(mon_type, pos, dur)); + } +protected: + death_spawn_fineff(monster_type type, coord_def pos, int dur) + : final_effect(0, 0, pos), mon_type(type), duration(dur) + { + } + const monster_type mon_type; + const int duration; +}; + void fire_final_effects(); diff --git a/crawl-ref/source/ghost.cc b/crawl-ref/source/ghost.cc index c26231b146b..612482d1a56 100644 --- a/crawl-ref/source/ghost.cc +++ b/crawl-ref/source/ghost.cc @@ -606,7 +606,7 @@ static const vector> priest_spells = { 0, 100, 100, RISE, SPELL_REGENERATE_OTHER }, { 40, 80, 200, SEMI, SPELL_AGONY}, { 40, 100, 60, SEMI, SPELL_STUNNING_BURST}, - { 45, 100, 65, FLAT, SPELL_AURA_OF_BRILLIANCE }, + { 45, 100, 65, FLAT, SPELL_PRAYER_OF_BRILLIANCE }, { 65, 100, 65, RISE, SPELL_CALL_DOWN_LIGHTNING }, { 70, 100, 40, RISE, SPELL_CONJURE_LIVING_SPELLS }, }; @@ -634,7 +634,7 @@ static const map freq_map = {SPELL_MIGHT, 35}, {SPELL_HASTE, 35}, {SPELL_BATTLESPHERE, 80}, - {SPELL_AURA_OF_BRILLIANCE, 60}, + {SPELL_PRAYER_OF_BRILLIANCE, 60}, {SPELL_CALL_DOWN_LIGHTNING, 15}, {SPELL_SEARING_RAY, 30}, {SPELL_CONJURE_LIVING_SPELLS, 40}, diff --git a/crawl-ref/source/hiscores.cc b/crawl-ref/source/hiscores.cc index d6bab0e6426..4b5b863cdd0 100644 --- a/crawl-ref/source/hiscores.cc +++ b/crawl-ref/source/hiscores.cc @@ -1399,7 +1399,11 @@ void scorefile_entry::init_death_cause(int dam, mid_t dsrc, // Setting this is redundant for dancing weapons, however // we do care about the above identification. -- bwr if (!mons_class_is_animated_weapon(mons->type)) + { auxkilldata = env.item[mons->inv[MSLOT_WEAPON]].name(DESC_A); + if (mons->has_ench(ENCH_ARMED)) + auxkilldata += " (from an undying armoury)"; + } } const bool death = (you.hp <= 0 || death_type == KILLED_BY_DRAINING); diff --git a/crawl-ref/source/item-name.cc b/crawl-ref/source/item-name.cc index 42b1b2d99b5..91ed576f01e 100644 --- a/crawl-ref/source/item-name.cc +++ b/crawl-ref/source/item-name.cc @@ -1123,6 +1123,7 @@ static const char* _book_type_name(int booktype) case BOOK_SCORCHING: return "Scorching"; case BOOK_MOVEMENT: return "Movement"; case BOOK_WICKED_CREATION: return "Wicked Creation"; + case BOOK_MALADIES: return "Maladies"; case BOOK_RANDART_LEVEL: return "Fixed Level"; case BOOK_RANDART_THEME: return "Fixed Theme"; default: return "Bugginess"; @@ -1244,6 +1245,10 @@ string sub_type_string(const item_def &item, bool known) return "Trismegistus Codex"; case BOOK_UNRESTRAINED: return "the Unrestrained Analects"; + case BOOK_SIEGECRAFT: + return "Compendium of Siegecraft"; + case BOOK_CONDUCTIVITY: + return "Codex of Conductivity"; #if TAG_MAJOR_VERSION == 34 case BOOK_AKASHIC_RECORD: return "Akashic Record"; diff --git a/crawl-ref/source/l-you.cc b/crawl-ref/source/l-you.cc index 9563acb4707..457bb364e89 100644 --- a/crawl-ref/source/l-you.cc +++ b/crawl-ref/source/l-you.cc @@ -779,7 +779,7 @@ static int l_you_memorise(lua_State *ls) /*** Available abilities * @treturn array An array of ability names. - * @function abils + * @function abilities */ static int l_you_abils(lua_State *ls) { @@ -796,7 +796,7 @@ static int l_you_abils(lua_State *ls) /*** Ability letters in use. * @treturn array An array of ability letters - * @function abil_letters + * @function ability_letters */ static int l_you_abil_letters(lua_State *ls) { @@ -817,7 +817,7 @@ static int l_you_abil_letters(lua_State *ls) /*** Ability table. * @treturn table A map of letters to ability names - * @function abil_table + * @function ability_table */ static int l_you_abil_table(lua_State *ls) { diff --git a/crawl-ref/source/lev-pand.cc b/crawl-ref/source/lev-pand.cc index df19b0b5bcd..a413eb856e8 100644 --- a/crawl-ref/source/lev-pand.cc +++ b/crawl-ref/source/lev-pand.cc @@ -46,7 +46,7 @@ void init_pandemonium() for (int pc = 0; pc < PAN_MONS_ALLOC; ++pc) { env.mons_alloc[pc] = random_choose_weighted(5, MONS_SMOKE_DEMON, - 5, MONS_YNOXINUL, + 4, MONS_YNOXINUL, 5, MONS_ABOMINATION_LARGE, 5, MONS_SOUL_EATER, 5, MONS_DEMONIC_CRAWLER, @@ -54,7 +54,7 @@ void init_pandemonium() 2, MONS_NEQOXEC, 5, MONS_CHAOS_SPAWN, 1, MONS_SHADOW_DEMON, - 1, MONS_LOROCYPROCA, + 2, MONS_SIN_BEAST, 1, MONS_HELLION, 1, MONS_TORMENTOR, 1, MONS_REAPER); diff --git a/crawl-ref/source/makeitem.cc b/crawl-ref/source/makeitem.cc index 1291016ef31..c8e48c04396 100644 --- a/crawl-ref/source/makeitem.cc +++ b/crawl-ref/source/makeitem.cc @@ -1636,16 +1636,9 @@ misc_item_type get_misc_item_type(int force_type, bool exclude) return NUM_MISCELLANY; } -static void _generate_misc_item(item_def& item, int force_type, int item_level) +// May also be called when a wanderer gets assigned a misc evoker at start +void handle_generated_misc(misc_item_type typ) { - const auto typ = get_misc_item_type(force_type); - if (typ == NUM_MISCELLANY) - { - item.base_type = OBJ_WANDS; - generate_wand_item(item, OBJ_RANDOM, item_level); - return; - } - item.sub_type = typ; switch (typ) { case MISC_SACK_OF_SPIDERS: @@ -1663,6 +1656,19 @@ static void _generate_misc_item(item_def& item, int force_type, int item_level) } } +static void _generate_misc_item(item_def& item, int force_type, int item_level) +{ + const auto typ = get_misc_item_type(force_type); + if (typ == NUM_MISCELLANY) + { + item.base_type = OBJ_WANDS; + generate_wand_item(item, OBJ_RANDOM, item_level); + return; + } + item.sub_type = typ; + handle_generated_misc(typ); +} + /** * Alter the inputted item to have no "plusses" (mostly weapon/armour enchantment) * diff --git a/crawl-ref/source/makeitem.h b/crawl-ref/source/makeitem.h index 2ca137c49f8..4099ac3e9ca 100644 --- a/crawl-ref/source/makeitem.h +++ b/crawl-ref/source/makeitem.h @@ -25,6 +25,7 @@ void item_colour(item_def &item); jewellery_type get_random_ring_type(); jewellery_type get_random_amulet_type(); misc_item_type get_misc_item_type(int force_type, bool exclude = true); +void handle_generated_misc(misc_item_type typ); void item_set_appearance(item_def &item); bool is_weapon_brand_ok(int type, int brand, bool strict); diff --git a/crawl-ref/source/melee-attack.cc b/crawl-ref/source/melee-attack.cc index f7589fbe890..90a9b152acf 100644 --- a/crawl-ref/source/melee-attack.cc +++ b/crawl-ref/source/melee-attack.cc @@ -96,6 +96,9 @@ bool melee_attack::bad_attempt() if (!attacker->is_player() || !defender || !defender->is_monster()) return false; + if (god_protects(attacker, defender->as_monster(), false)) + return true; + if (player_unrand_bad_attempt(offhand_weapon())) return true; @@ -3022,7 +3025,7 @@ void melee_attack::mons_apply_attack_flavour() if (one_chance_in(3)) { - if (attk_type != AT_SPORE) + if (attk_type != AT_SPORE && defender_visible) { mprf("%s %s afflicted by dizzying energies!", defender->name(DESC_THE).c_str(), @@ -3397,6 +3400,13 @@ void melee_attack::mons_apply_attack_flavour() break; } + case AF_SWARM: + { + if (!defender->is_monster() || !mons_is_firewood(*defender->as_monster())) + summon_swarm_clone(*attacker->as_monster(), defender->pos()); + break; + } + case AF_BLOODZERK: { if (!defender->can_bleed() || !attacker->can_go_berserk()) diff --git a/crawl-ref/source/mgen-data.h b/crawl-ref/source/mgen-data.h index e3a354b7fb8..8871bce643c 100644 --- a/crawl-ref/source/mgen-data.h +++ b/crawl-ref/source/mgen-data.h @@ -173,6 +173,8 @@ struct mgen_data || cls == MONS_BALL_LIGHTNING || cls == MONS_ORB_OF_DESTRUCTION || cls == MONS_BATTLESPHERE || cls == MONS_BALLISTOMYCETE_SPORE || cls == MONS_BOULDER + || cls == MONS_SEISMIC_CANNON + || cls == MONS_PILE_OF_DEBRIS || summon_type == SPELL_ANIMATE_DEAD || summon_type == SPELL_NECROTISE || summon_type == SPELL_DEATH_CHANNEL diff --git a/crawl-ref/source/mgen-enum.h b/crawl-ref/source/mgen-enum.h index b186c348c6c..eae4dc1dd30 100644 --- a/crawl-ref/source/mgen-enum.h +++ b/crawl-ref/source/mgen-enum.h @@ -45,7 +45,7 @@ enum band_type BAND_HELL_RATS, BAND_DREAM_SHEEP, BAND_GHOULS, - BAND_DEEP_TROLLS, + BAND_PRESERVER, BAND_DEEP_TROLL_SHAMAN, BAND_HOGS, BAND_HELL_HOGS, @@ -131,6 +131,7 @@ enum band_type BAND_LATE_ROKA, BAND_WEEPING_SKULLS, BAND_THERMIC_DYNAMOS, + BAND_ORB_SPIDERS, NUM_BANDS // always last }; diff --git a/crawl-ref/source/mon-abil.cc b/crawl-ref/source/mon-abil.cc index 4d6d8240302..27d2815fdd0 100644 --- a/crawl-ref/source/mon-abil.cc +++ b/crawl-ref/source/mon-abil.cc @@ -1146,10 +1146,6 @@ bool mon_special_ability(monster* mons) _weeping_skull_cloud_aura(mons); break; - case MONS_MARTYRED_SHADE: - martyr_injury_bond(*mons); - break; - default: break; } @@ -1159,17 +1155,3 @@ bool mon_special_ability(monster* mons) return used; } - -void martyr_injury_bond(monster& mons) -{ - for (monster_near_iterator mi(&mons, LOS_NO_TRANS); mi; ++mi) - { - if (mons_aligned(&mons, *mi) - && !mi->has_ench(ENCH_CHARM) - && !mons_is_projectile(**mi) - && *mi != &mons) - { - mi->add_ench(mon_enchant(ENCH_INJURY_BOND, 1, &mons, INFINITE_DURATION)); - } - } -} diff --git a/crawl-ref/source/mon-abil.h b/crawl-ref/source/mon-abil.h index 8ac231795dd..4170087592e 100644 --- a/crawl-ref/source/mon-abil.h +++ b/crawl-ref/source/mon-abil.h @@ -28,5 +28,3 @@ bool lost_soul_revive(monster& mons, killer_type killer); void treant_release_fauna(monster& mons); void check_grasping_roots(actor& act, bool quiet = false); - -void martyr_injury_bond(monster& mons); diff --git a/crawl-ref/source/mon-act.cc b/crawl-ref/source/mon-act.cc index cd88f0c8bb9..c377b47c957 100644 --- a/crawl-ref/source/mon-act.cc +++ b/crawl-ref/source/mon-act.cc @@ -1214,7 +1214,10 @@ bool handle_throw(monster* mons, bolt & beem, bool teleport, bool check_only) // Yes, there is a logic to this ordering {dlb}: if (mons->incapacitated() || mons->caught() - || mons_is_confused(*mons)) + || mons_is_confused(*mons) + || (mons->has_ench(ENCH_ARMED) + && mons->weapon() + && !is_range_weapon(*mons->weapon()))) { return false; } @@ -1227,7 +1230,10 @@ bool handle_throw(monster* mons, bolt & beem, bool teleport, bool check_only) } const bool prefer_ranged_attack - = mons_class_flag(mons->type, M_PREFER_RANGED); + = mons_class_flag(mons->type, M_PREFER_RANGED) + || (mons->has_ench(ENCH_ARMED) + && mons->weapon() + && is_range_weapon(*mons->weapon())); // Don't allow offscreen throwing for now. if (mons->foe == MHITYOU && !you.see_cell(mons->pos())) @@ -1283,7 +1289,11 @@ bool handle_throw(monster* mons, bolt & beem, bool teleport, bool check_only) if (prefer_ranged_attack) { // Master archers are always quite likely to shoot you, if they can. - if (one_chance_in(10)) + // + // (They always fire when in melee, to keep them from rarely swapping + // their launchers away when they inevitably bump attack their target + // anyway) + if (one_chance_in(10) && !adjacent(beem.target, mons->pos())) return false; } else if (launcher) @@ -1836,9 +1846,6 @@ void handle_monster_move(monster* mons) return; } - if (mons->has_ench(ENCH_BRILLIANCE_AURA)) - aura_of_brilliance(mons); - if (you.duration[DUR_GOZAG_GOLD_AURA] && have_passive(passive_t::gold_aura) && you.see_cell(mons->pos()) @@ -2174,48 +2181,6 @@ static void _ancient_zyme_sicken(monster* mons) } } -/** - * Apply the torpor snail slowing effect. - * - * @param mons The snail applying the effect. - */ -static void _torpor_snail_slow(monster* mons) -{ - // XXX: might be nice to refactor together with _ancient_zyme_sicken(). - // XXX: also with torpor_slowed().... so many duplicated checks :( - - if (is_sanctuary(mons->pos()) || mons->props.exists(KIKU_WRETCH_KEY)) - return; - - if (!is_sanctuary(you.pos()) - && !you.stasis() - && !mons->wont_attack() - && cell_see_cell(you.pos(), mons->pos(), LOS_SOLID_SEE)) - { - if (!you.duration[DUR_SLOW]) - { - mprf("Being near %s leaves you feeling lethargic.", - mons->name(DESC_THE).c_str()); - } - - if (you.duration[DUR_SLOW] <= 1) - you.set_duration(DUR_SLOW, 1); - you.props[TORPOR_SLOWED_KEY] = true; - } - - for (monster_near_iterator ri(mons->pos(), LOS_SOLID_SEE); ri; ++ri) - { - monster *m = *ri; - if (m && !mons_aligned(mons, m) && !m->stasis() - && !mons_is_conjured(m->type) && !m->is_stationary() - && !is_sanctuary(m->pos())) - { - m->add_ench(mon_enchant(ENCH_SLOW, 0, mons, 1)); - m->props[TORPOR_SLOWED_KEY] = true; - } - } -} - static void _post_monster_move(monster* mons) { if (invalid_monster(mons)) @@ -2232,8 +2197,7 @@ static void _post_monster_move(monster* mons) if (mons->type == MONS_ANCIENT_ZYME) _ancient_zyme_sicken(mons); - if (mons->type == MONS_TORPOR_SNAIL) - _torpor_snail_slow(mons); + mons_update_aura(*mons); // Check golem distance again at the END of its move (so that it won't go // dormant if it is following a player who was adjacent to it at the start @@ -2277,6 +2241,32 @@ static void _post_monster_move(monster* mons) } } + if (mons->type == MONS_SEISMIC_CANNON) + { + // All that drilling makes an awful racket (and sometimes dust) + noisy(12, mons->pos(), mons->mid); + if (one_chance_in(10)) + mons_speaks(mons); + + if (one_chance_in(2)) + { + coord_def spot; + int num_found = 0; + for (adjacent_iterator ai(mons->pos()); ai; ++ai) + { + if (!feat_is_solid(env.grid(*ai)) && !cloud_at(*ai) + && one_chance_in(++num_found)) + { + spot = *ai; + } + } + + if (!spot.origin()) + place_cloud(CLOUD_DUST, spot, random_range(3, 5), mons); + } + } + + update_mons_cloud_ring(mons); const item_def * weapon = mons->mslot_item(MSLOT_WEAPON); @@ -3626,7 +3616,11 @@ static bool _monster_move(monster* mons, coord_def& delta) } // Dissolution dissolves walls. else if (player_can_hear(mons->pos() + delta)) - mprf(MSGCH_SOUND, "You hear a sizzling sound."); + { + mprf(MSGCH_SOUND, mons->type == MONS_DISSOLUTION + ? "You hear a sizzling sound." + : "You hear a grinding noise."); + } } } diff --git a/crawl-ref/source/mon-aura.cc b/crawl-ref/source/mon-aura.cc new file mode 100644 index 00000000000..037e1d02951 --- /dev/null +++ b/crawl-ref/source/mon-aura.cc @@ -0,0 +1,230 @@ +#include "AppHdr.h" + +#include "mon-aura.h" + +#include "act-iter.h" +#include "fprop.h" +#include "monster.h" +#include "mon-ench.h" + +struct mon_aura_data +{ + monster_type mon_source; + enchant_type ench_type; + int base_duration; + bool is_hostile; + duration_type dur_type; + string player_key; + function valid_target; + function player_msg; + + mon_aura_data(monster_type _mon_source, enchant_type _ench_type, + int _base_duration, bool _is_hostile, + duration_type _dur_type = NUM_DURATIONS, + string _player_key = "", + function _valid_target = nullptr, + function _player_msg = nullptr): + mon_source(_mon_source), ench_type(_ench_type), + base_duration(_base_duration), is_hostile(_is_hostile), + dur_type(_dur_type), player_key(_player_key), + valid_target(_valid_target), + player_msg(_player_msg) + {} +}; + +static const vector aura_map = +{ + {MONS_TORPOR_SNAIL, + ENCH_SLOW, 1, true, DUR_SLOW, TORPOR_SLOWED_KEY, + [](const actor& targ) { return !targ.stasis();}, + [](const monster& source) + { mprf("Being near %s leaves you feeling lethargic.", + source.name(DESC_THE).c_str()); + }}, + + {MONS_OPHAN, + ENCH_NONE, 1, true, DUR_SENTINEL_MARK, OPHAN_MARK_KEY, + nullptr, + [](const monster& source) + { mprf("%s gaze reveals you to all!", + source.name(DESC_ITS).c_str()); + }}, + + {MONS_MARTYRED_SHADE, + ENCH_INJURY_BOND, 30, false}, + + {MONS_POLTERGUARDIAN, + ENCH_REPEL_MISSILES, 1, false}, +}; + +static mon_aura_data _get_aura_for(const monster& mon) +{ + for (mon_aura_data aura : aura_map) + { + if (aura.mon_source == mon.type) + return aura; + } + + ASSERT(false); +} + +static mon_aura_data _get_aura_from_key(string player_key) +{ + for (mon_aura_data aura : aura_map) + { + if (aura.player_key == player_key) + return aura; + } + + ASSERT(false); +} + +bool mons_has_aura(const monster& mon) +{ + return mons_class_flag(mon.type, M_HAS_AURA) + && !mon.props.exists(KIKU_WRETCH_KEY); +} + +bool mons_has_aura_of_type(const monster& mon, enchant_type type) +{ + if (!mons_has_aura(mon)) + return false; + + for (mon_aura_data aura : aura_map) + { + if (aura.ench_type == type && aura.mon_source == mon.type) + return true; + } + + return false; +} + +bool mons_has_aura_of_type(const monster& mon, duration_type type) +{ + if (!mons_has_aura(mon)) + return false; + + for (mon_aura_data aura : aura_map) + { + if (aura.dur_type == type && aura.mon_source == mon.type) + return true; + } + + return false; +} + +// Accounting for monster attitude and other aura properties, could this aura +// affect a given actor? +static bool _aura_could_affect(const mon_aura_data& aura, const monster& source, + const actor& victim) +{ + // Auras do not apply to self + if (victim.is_monster() && victim.as_monster() == &source) + return false; + + // Is the victim the right alignment? + if (mons_aligned(&source, &victim) == aura.is_hostile) + return false; + + // If the aura suppressed by sanctuary? + if (aura.is_hostile && (is_sanctuary(source.pos()) || is_sanctuary(victim.pos()))) + return false; + + // Does the aura have more specific conditions that are invalid? + if (aura.valid_target && !aura.valid_target(victim)) + return false; + + // Looks good! + return true; +} + +void mons_update_aura(const monster& mon) +{ + if (!mons_has_aura(mon)) + return; + + const mon_aura_data aura = _get_aura_for(mon); + + // Hostile auras are suppressed by the source being in a sanctuary + if (aura.is_hostile && is_sanctuary(mon.pos())) + return; + + if (aura.ench_type != ENCH_NONE) + { + for (monster_near_iterator mi(mon.pos(), LOS_NO_TRANS); mi; ++mi) + { + if (_aura_could_affect(aura, mon, **mi)) + { + // Check if the target has a pre-existing version of this enchantment + // with a longer duration. + if (mi->has_ench(aura.ench_type)) + { + mon_enchant en = mi->get_ench(aura.ench_type); + if (en.duration > aura.base_duration) + continue; + } + + // Remove any enchantment that may currently exist, since we don't + // want them to stack. + mi->del_ench(aura.ench_type, true, false); + mi->add_ench(mon_enchant(aura.ench_type, 1, &mon, aura.base_duration, + aura.is_hostile ? AURA_HOSTILE : AURA_FRIENDLY)); + } + } + } + + // End here if this doesn't also affect the player + if (aura.dur_type == NUM_DURATIONS || !mon.see_cell_no_trans(you.pos()) + || !_aura_could_affect(aura, mon, you)) + { + return; + } + + if (you.duration[aura.dur_type] > aura.base_duration) + return; + + if (!you.duration[aura.dur_type]) + aura.player_msg(mon); + + you.duration[aura.dur_type] = aura.base_duration; + you.props[aura.player_key].get_bool() = true; +} + +bool aura_is_active(const monster& affected, enchant_type type) +{ + for (monster_near_iterator mi(affected.pos(), LOS_NO_TRANS); mi; ++mi) + { + if (!mons_has_aura_of_type(**mi, type)) + continue; + + if (!_aura_could_affect(_get_aura_for(**mi), **mi, affected)) + continue; + + return true; + } + + return false; +} + +bool aura_is_active_on_player(string player_key) +{ + if (!you.props.exists(player_key)) + return false; + + mon_aura_data aura = _get_aura_from_key(player_key); + for (monster_near_iterator mi(you.pos(), LOS_NO_TRANS); mi; ++mi) + { + if (!mons_has_aura_of_type(**mi, aura.dur_type)) + continue; + + if (!_aura_could_affect(aura, **mi, you)) + continue; + + return true; + } + + // We didn't find the aura, so delete the key at the same time + you.props.erase(player_key); + + return false; +} diff --git a/crawl-ref/source/mon-aura.h b/crawl-ref/source/mon-aura.h new file mode 100644 index 00000000000..1309205be39 --- /dev/null +++ b/crawl-ref/source/mon-aura.h @@ -0,0 +1,30 @@ +/** + * @file + * @brief Monster passive line-of-sight enchantment functionality. +**/ + +#pragma once + +#include "duration-type.h" +#include "enchant-type.h" + +#define TORPOR_SLOWED_KEY "torpor_slowed" +#define OPHAN_MARK_KEY "ophan_mark" + +class mon_enchant; + +enum ench_aura_type +{ + AURA_NO, // Not from an aura effect + AURA_FRIENDLY, // From a friendly aura effect + AURA_HOSTILE, // From a hostile aura effect +}; + +bool mons_has_aura(const monster& mon); +bool mons_has_aura_of_type(const monster& mon, enchant_type type); +bool mons_has_aura_of_type(const monster& mon, duration_type type); + +bool aura_is_active(const monster& affected, enchant_type type); +bool aura_is_active_on_player(string player_key); + +void mons_update_aura(const monster& mon); diff --git a/crawl-ref/source/mon-behv.cc b/crawl-ref/source/mon-behv.cc index 9e34fa6b022..d3bc79beb32 100644 --- a/crawl-ref/source/mon-behv.cc +++ b/crawl-ref/source/mon-behv.cc @@ -1200,7 +1200,7 @@ void behaviour_event(monster* mon, mon_event_type event, const actor *src, { msg = getSpeakString(mon->name(DESC_PLAIN) + " cornered"); if (msg.empty()) - msg = "PLAIN:Cornered, @The_monster@ turns to fight!"; + msg = "PLAIN:Cornered, @the_monster@ turns to fight!"; } mon->del_ench(ENCH_FEAR, true); mon->behaviour = BEH_SEEK; diff --git a/crawl-ref/source/mon-cast.cc b/crawl-ref/source/mon-cast.cc index 6d54beb2631..7c2559a63ac 100644 --- a/crawl-ref/source/mon-cast.cc +++ b/crawl-ref/source/mon-cast.cc @@ -169,6 +169,7 @@ static int _monster_abjuration(const monster& caster, bool actual); static ai_action::goodness _mons_will_abjure(const monster& mons); static ai_action::goodness _should_irradiate(const monster& mons); static void _whack(const actor &caster, actor &victim); +static bool _mons_cast_prisms(monster& caster, actor& foe, int pow, bool check_only); enum spell_logic_flag { @@ -454,6 +455,9 @@ static const map spell_to_logic = { { SPELL_FIRE_ELEMENTALS, { _always_worthwhile, _mons_summon_elemental } }, { SPELL_STICKS_TO_SNAKES, { _always_worthwhile, _mons_sticks_to_snakes } }, { SPELL_SHEZAS_DANCE, { _always_worthwhile, _mons_summon_dancing_weapons } }, + { SPELL_FLASHING_BALESTRA, { _foe_not_nearby, _fire_simple_beam, + _zap_setup(SPELL_FLASHING_BALESTRA) + } }, { SPELL_DIVINE_ARMAMENT, { _always_worthwhile, _cast_divine_armament } }, { SPELL_HASTE_OTHER, { _always_worthwhile, @@ -1090,7 +1094,8 @@ static bool _flavour_benefits_monster(beam_type flavour, monster& monster) return !monster.has_ench(ENCH_HASTE); case BEAM_MIGHT: - return !monster.has_ench(ENCH_MIGHT); + return !monster.has_ench(ENCH_MIGHT) + && mons_has_attacks(monster); case BEAM_INVISIBILITY: return !monster.has_ench(ENCH_INVIS); @@ -1292,11 +1297,12 @@ static int _mons_power_hd_factor(spell_type spell) case SPELL_IOOD: case SPELL_FREEZE: + case SPELL_FULMINANT_PRISM: + case SPELL_IGNITE_POISON: return 8; case SPELL_MONSTROUS_MENAGERIE: case SPELL_BATTLESPHERE: - case SPELL_IGNITE_POISON: case SPELL_IRRADIATE: case SPELL_FOXFIRE: case SPELL_MANIFOLD_ASSAULT: @@ -1566,6 +1572,7 @@ bolt mons_spell_beam(const monster* mons, spell_type spell_cast, int power, case SPELL_BOLT_OF_DEVASTATION: case SPELL_BORGNJORS_VILE_CLUTCH: case SPELL_CRYSTALLIZING_SHOT: + case SPELL_STONE_BULLET: zappy(spell_to_zap(real_spell), power, true, beam); break; @@ -1845,7 +1852,7 @@ bool setup_mons_cast(const monster* mons, bolt &pbolt, spell_type spell_cast, case SPELL_CALL_IMP: case SPELL_SUMMON_MINOR_DEMON: case SPELL_SUMMON_UFETUBUS: - case SPELL_SUMMON_HELL_BEAST: // Geryon + case SPELL_SUMMON_SIN_BEAST: // Geryon case SPELL_SUMMON_UNDEAD: case SPELL_SUMMON_ICE_BEAST: case SPELL_SUMMON_MUSHROOMS: @@ -1929,7 +1936,7 @@ bool setup_mons_cast(const monster* mons, bolt &pbolt, spell_type spell_cast, case SPELL_ENTROPIC_WEAVE: case SPELL_SUMMON_EXECUTIONERS: case SPELL_DOOM_HOWL: - case SPELL_AURA_OF_BRILLIANCE: + case SPELL_PRAYER_OF_BRILLIANCE: case SPELL_GREATER_SERVANT_MAKHLEB: case SPELL_BIND_SOULS: case SPELL_DREAM_DUST: @@ -1943,6 +1950,8 @@ bool setup_mons_cast(const monster* mons, bolt &pbolt, spell_type spell_cast, case SPELL_FUNERAL_DIRGE: case SPELL_MANIFOLD_ASSAULT: case SPELL_REGENERATE_OTHER: + case SPELL_BESTOW_ARMS: + case SPELL_FULMINANT_PRISM: pbolt.range = 0; pbolt.glyph = 0; return true; @@ -2095,7 +2104,7 @@ static bool _mirrorable(const monster* agent, const monster* mon) && !mons_is_unique(mon->type); } -static bool _valid_aura_of_brilliance_ally(const monster* caster, +static bool _valid_prayer_of_brilliance_ally(const monster* caster, const monster* target) { return mons_aligned(caster, target) && caster != target @@ -3574,35 +3583,22 @@ static void _cast_black_mark(monster* agent) } } -void aura_of_brilliance(monster* agent) +static void _prayer_of_brilliance(monster* agent) { - bool did_something = false; - for (actor_near_iterator ai(agent, LOS_NO_TRANS); ai; ++ai) + for (monster_near_iterator mi(agent, LOS_NO_TRANS); mi; ++mi) { - if (ai->is_player() || !mons_aligned(*ai, agent)) + if (!mons_aligned(*mi, agent)) continue; - monster* mon = ai->as_monster(); - if (_valid_aura_of_brilliance_ally(agent, mon)) + if (_valid_prayer_of_brilliance_ally(agent, *mi)) { - if (!mon->has_ench(ENCH_EMPOWERED_SPELLS) && you.can_see(*mon)) + if (!mi->has_ench(ENCH_EMPOWERED_SPELLS) && you.can_see(**mi)) { - mprf("%s is empowered by %s aura!", - mon->name(DESC_THE).c_str(), - apostrophise(agent->name(DESC_THE)).c_str()); + mprf("%s spells are empowered by the prayer of brilliance!", + mi->name(DESC_ITS).c_str()); } - - mon_enchant ench = mon->get_ench(ENCH_EMPOWERED_SPELLS); - if (ench.ench != ENCH_NONE) - mon->update_ench(ench); - else - mon->add_ench(mon_enchant(ENCH_EMPOWERED_SPELLS, 1, agent)); - - did_something = true; + mi->add_ench(mon_enchant(ENCH_EMPOWERED_SPELLS, 1, agent)); } } - - if (!did_something) - agent->del_ench(ENCH_BRILLIANCE_AURA); } static bool _glaciate_tracer(monster *caster, int pow, coord_def aim) @@ -4413,6 +4409,29 @@ bool handle_mon_spell(monster* mons) return true; } + // Seismic cannons heal themselves with each shot and will be eliable to + // fire a shockwave attack once they reach full health in this way. + if (mons->type == MONS_SEISMIC_CANNON) + { + if (mons->hit_points < mons->max_hit_points) + { + mons->heal(mons->max_hit_points / 10); + if (mons->hit_points == mons->max_hit_points + && !mons->has_ench(ENCH_SPELL_CHARGED)) + { + mons->add_ench(ENCH_SPELL_CHARGED); + simple_monster_message(*mons, " finishes assembling itself."); + + // Give charged cannons a little more duration, to avoid the + // bad-feeling situation of having them immediately vanish once + // shockwave becomes available, but before it can be used. + mons->add_ench(mon_enchant(ENCH_FAKE_ABJURATION, 0, + actor_by_mid(mons->summoner), + random_range(3, 5) * BASELINE_DELAY)); + } + } + } + if (!(flags & MON_SPELL_INSTANT)) { mons->lose_energy(EUT_SPELL); @@ -5866,6 +5885,147 @@ static bool _cast_dirge(monster& caster, bool check_only = false) return true; } +static void _cast_bestow_arms(monster& caster) +{ + vector targs; + + // Compile list of eligable targets (and check if there are viable targets + // for two-handers and ranged weapons while we're at it) + bool two_hand_eligable = false; + bool ranged_eligable = false; + for (monster_near_iterator mi(caster.pos(), LOS_NO_TRANS); mi; ++mi) + { + if (mons_aligned(&caster, *mi) && !mi->has_ench(ENCH_ARMED) + && (mons_itemuse(**mi) >= MONUSE_STARTING_EQUIPMENT)) + { + // Skip enemies with unrandarts, which are special enough to not + // replace. + if (mi->weapon() && is_unrandom_artefact(*mi->weapon())) + continue; + + targs.push_back(*mi); + + if (!mi->shield()) + two_hand_eligable = true; + + // Attempting to give a ranged weapon to a dual-wielder is strange + // and somewhat buggy, so let's not. + if (!mons_wields_two_weapons(**mi)) + ranged_eligable = true; + } + } + + // Pick a weapon to give everyone + item_def wpn; + wpn.base_type = OBJ_WEAPONS; + wpn.sub_type = random_choose_weighted( + two_hand_eligable ? 14 : 0, WPN_BARDICHE, + two_hand_eligable ? 14 : 0, WPN_GLAIVE, + 11, WPN_DEMON_TRIDENT, + 15, WPN_EVENINGSTAR, + 15, WPN_DOUBLE_SWORD, + 6, WPN_QUICK_BLADE, + ranged_eligable && two_hand_eligable ? 8 : 0, WPN_LONGBOW, + ranged_eligable && two_hand_eligable ? 7 : 0, WPN_TRIPLE_CROSSBOW, + ranged_eligable ? 5 : 0, WPN_HAND_CANNON); + + wpn.brand = random_choose_weighted(11, SPWPN_FLAMING, + 11, SPWPN_FREEZING, + 7, SPWPN_SPEED, + 7, SPWPN_VAMPIRISM, + 4, SPWPN_CHAOS, + 1, SPWPN_DISTORTION); + + wpn.plus = random_range(4, 9); + wpn.flags |= (ISFLAG_SUMMONED | ISFLAG_IDENT_MASK); + wpn.quantity = 1; + + if (you.can_see(caster)) + { + mprf(MSGCH_MONSTER_SPELL, "%s arms its allies with %s.", caster.name(DESC_THE).c_str(), + pluralise(wpn.name(DESC_PLAIN, false, true)).c_str()); + } + + int dur = random_range(12, 26) * BASELINE_DELAY; + + shuffle_array(targs); + int count = random_range(5, 7); + + for (monster* mon : targs) + { + // Skip if this monster is not a valid recipient of this weapon + if (mon->hands_reqd(wpn) == HANDS_TWO && mon->shield()) + continue; + if (mons_wields_two_weapons(*mon) && is_ranged_weapon_type(wpn.sub_type)) + continue; + + item_def clone = wpn; + + // Save their previous weapons, if they were using any + if (mon->inv[MSLOT_WEAPON] != NON_ITEM) + { + mon->props[OLD_ARMS_KEY] = env.item[mon->inv[MSLOT_WEAPON]]; + destroy_item(mon->inv[MSLOT_WEAPON]); + } + if (mon->inv[MSLOT_ALT_WEAPON] != NON_ITEM) + { + mon->props[OLD_ARMS_ALT_KEY] = env.item[mon->inv[MSLOT_ALT_WEAPON]]; + destroy_item(mon->inv[MSLOT_ALT_WEAPON]); + } + + give_specific_item(mon, clone); + mon->add_ench(mon_enchant(ENCH_ARMED, 1, &caster, dur)); + flash_tile(mon->pos(), MAGENTA, 50); + + if (--count == 0) + break; + } + + caster.add_ench(mon_enchant(ENCH_ARMED, 1, &caster, dur)); +} + +static bool _mons_cast_prisms(monster& caster, actor& foe, int pow, bool check_only) +{ + // Determine which pair of locations to summon prisms on. The spots must + // both be within 2 tiles of our foe, and also inside our spell range. + // (And ideally not adjacent!) + + vector pos; + for (radius_iterator ri(foe.pos(), 2, C_SQUARE, LOS_NO_TRANS); ri; ++ri) + { + if (grid_distance(caster.pos(), *ri) <= spell_range(SPELL_FULMINANT_PRISM, pow) + && !actor_at(*ri) && !feat_is_solid(env.grid(*ri))) + { + // If we're just looking for a valid position, we found one. + if (check_only) + return true; + + pos.push_back(*ri); + } + } + + // Didn't find any empty space to place it + if (pos.empty()) + return false; + + // Place the first prism + int index = random2(pos.size()); + cast_fulminating_prism(&caster, pow, pos[index], false); + + // Then look at all valid locations that are non-adjacent to the prism we + // placed and pick one of those for the second prism + vector pos2; + for (coord_def p : pos) + { + if (grid_distance(p, pos[index]) > 1) + pos2.push_back(p); + } + + if (!pos2.empty()) + cast_fulminating_prism(&caster, pow, pos2[random2(pos2.size())], false); + + return true; +} /** * Make this monster cast a spell @@ -6260,10 +6420,10 @@ void mons_cast(monster* mons, bolt pbolt, spell_type spell_cast, } return; - case SPELL_SUMMON_HELL_BEAST: // Geryon + case SPELL_SUMMON_SIN_BEAST: // Geryon create_monster( - mgen_data(MONS_HELL_BEAST, SAME_ATTITUDE(mons), mons->pos(), - mons->foe).set_summoned(mons, 4, spell_cast, god)); + mgen_data(MONS_SIN_BEAST, SAME_ATTITUDE(mons), mons->pos(), + mons->foe).set_summoned(mons, 3, spell_cast, god)); return; case SPELL_SUMMON_ICE_BEAST: @@ -6852,10 +7012,8 @@ void mons_cast(monster* mons, bolt pbolt, spell_type spell_cast, _mons_call_of_chaos(*mons); return; - case SPELL_AURA_OF_BRILLIANCE: - simple_monster_message(*mons, " begins emitting a brilliant aura!"); - mons->add_ench(ENCH_BRILLIANCE_AURA); - aura_of_brilliance(mons); + case SPELL_PRAYER_OF_BRILLIANCE: + _prayer_of_brilliance(mons); return; case SPELL_BIND_SOULS: @@ -6946,6 +7104,14 @@ void mons_cast(monster* mons, bolt pbolt, spell_type spell_cast, _cast_regenerate_other(mons); return; + case SPELL_BESTOW_ARMS: + _cast_bestow_arms(*mons); + return; + + case SPELL_FULMINANT_PRISM: + _mons_cast_prisms(*mons, *mons->get_foe(), + mons_spellpower(*mons, SPELL_FULMINANT_PRISM), false); + return; } if (spell_is_direct_explosion(spell_cast)) @@ -8156,16 +8322,16 @@ ai_action::goodness monster_spell_goodness(monster* mon, spell_type spell) case SPELL_CALL_OF_CHAOS: return ai_action::good_or_bad(_mons_call_of_chaos(*mon, true)); - case SPELL_AURA_OF_BRILLIANCE: + case SPELL_PRAYER_OF_BRILLIANCE: if (!foe || !mon->can_see(*foe)) return ai_action::bad(); - if (mon->has_ench(ENCH_BRILLIANCE_AURA)) - return ai_action::impossible(); - for (monster_near_iterator mi(mon, LOS_NO_TRANS); mi; ++mi) - if (_valid_aura_of_brilliance_ally(mon, *mi)) + if (_valid_prayer_of_brilliance_ally(mon, *mi) + && !mi->has_ench(ENCH_EMPOWERED_SPELLS)) + { return ai_action::good(); + } return ai_action::bad(); case SPELL_BIND_SOULS: @@ -8216,13 +8382,43 @@ ai_action::goodness monster_spell_goodness(monster* mon, spell_type spell) return ai_action::good_or_impossible( cast_manifold_assault(*mon, -1, false, false) != spret::abort); + case SPELL_BESTOW_ARMS: + { + // The self-buff acts as a sort of cooldown, also. + if (mon->has_ench(ENCH_ARMED)) + return ai_action::impossible(); + + for (monster_near_iterator mi(mon->pos(), LOS_NO_TRANS); mi; ++mi) + { + if (mons_aligned(mon, *mi) && !mi->has_ench(ENCH_ARMED) + && (mons_itemuse(**mi) >= MONUSE_STARTING_EQUIPMENT)) + { + return ai_action::good(); + } + } + + return ai_action::impossible(); + } + + case SPELL_FULMINANT_PRISM: + { + // Only place prisms if none are already out + for (monster_iterator mi; mi; ++mi) + { + if (mi->type == MONS_FULMINANT_PRISM && mi->summoner == mon->mid) + return ai_action::bad(); + } + + return ai_action::good_or_impossible( + _mons_cast_prisms(*mon, *mon->get_foe(), 100, true)); + } + #if TAG_MAJOR_VERSION == 34 case SPELL_SUMMON_SWARM: case SPELL_INNER_FLAME: case SPELL_ANIMATE_DEAD: case SPELL_SIMULACRUM: case SPELL_DEATHS_DOOR: - case SPELL_FULMINANT_PRISM: case SPELL_DAZZLING_FLASH: case SPELL_OZOCUBUS_ARMOUR: case SPELL_TELEPORT_SELF: diff --git a/crawl-ref/source/mon-cast.h b/crawl-ref/source/mon-cast.h index a9434f91dd0..a9a75fae285 100644 --- a/crawl-ref/source/mon-cast.h +++ b/crawl-ref/source/mon-cast.h @@ -15,10 +15,13 @@ class monster; struct bolt; struct dice_def; +#define OLD_ARMS_KEY "old_arms" +#define OLD_ARMS_ALT_KEY "old_arms_alt" + void init_mons_spells(); bool is_valid_mon_spell(spell_type spell); -void aura_of_brilliance(monster* agent); +void prayer_of_brilliance(monster* agent); bool mons_should_cloud_cone(monster* agent, int power, const coord_def pos); @@ -45,10 +48,8 @@ bool setup_mons_cast(const monster* mons, bolt &pbolt, spell_type spell_cast, bool evoke = false, bool check_validity = false); -void mons_cast_haunt(monster* mons); void mons_cast_flay(monster &caster, mon_spell_slot, bolt&); bool mons_word_of_recall(monster* mons, int recall_target); -void mons_cast_spectral_orcs(monster* mons); void setup_breath_timeout(monster* mons); bool mons_can_bind_soul(monster* binder, monster* bound); diff --git a/crawl-ref/source/mon-death.cc b/crawl-ref/source/mon-death.cc index 4c473b8a030..b4f8cd5ab75 100644 --- a/crawl-ref/source/mon-death.cc +++ b/crawl-ref/source/mon-death.cc @@ -13,6 +13,7 @@ #include "artefact.h" #include "art-enum.h" #include "attitude-change.h" +#include "beam.h" #include "bloodspatter.h" #include "cloud.h" #include "cluautil.h" @@ -58,6 +59,7 @@ #include "religion.h" #include "shout.h" #include "spl-damage.h" +#include "spl-monench.h" #include "spl-other.h" #include "spl-summoning.h" #include "spl-selfench.h" @@ -220,9 +222,10 @@ static bool _explode_corpse(item_def& corpse, const coord_def& where) dprf("Success"); if (corpse.base_type == OBJ_GOLD) - corpse.quantity = div_rand_round(total_gold, nchunks); - if (corpse.quantity) + { + corpse.quantity = max(1, div_rand_round(total_gold, nchunks)); copy_item_to_grid(corpse, cp); + } } return true; @@ -671,6 +674,8 @@ static bool _ely_heal_monster(monster* mons, killer_type killer, int i) return false; } + actor *act = &you; + if (MON_KILL(killer) && !invalid_monster_index(i)) { monster* mon = &env.mons[i]; @@ -679,6 +684,8 @@ static bool _ely_heal_monster(monster* mons, killer_type killer, int i) if (!you.see_cell(mons->pos())) return false; + + act = mon; } else if (!YOU_KILL(killer)) return false; @@ -697,6 +704,8 @@ static bool _ely_heal_monster(monster* mons, killer_type killer, int i) god_speaks(god, msg.c_str()); + behaviour_event(mons, ME_WHACK, act); + lugonu_meddle_fineff::schedule(); return true; @@ -1055,6 +1064,27 @@ static void _monster_die_cloud(const monster* mons, bool corpse, bool silent, cloud = random_smoke_type(); else if (msg.find("chaos") != string::npos) cloud = CLOUD_CHAOS; + else if (msg.find("armoury") != string::npos) + { + cloud = CLOUD_NONE; + + // XXX: This doesn't feel like quite the right place for this code, but + // it *is* about the visual after-effects of a summon going poof... + if (monster* armoury = monster_by_mid(mons->summoner)) + { + if (!silent && armoury->alive() + && armoury->see_cell_no_trans(mons->pos())) + { + bolt visual; + visual.source = mons->pos(); + visual.target = armoury->pos(); + visual.flavour = BEAM_VISUAL; + visual.range = LOS_RADIUS; + visual.aimed_at_spot = true; + visual.fire(); + } + } + } if (!silent) simple_monster_message(*mons, (prefix + msg).c_str()); @@ -1820,6 +1850,9 @@ item_def* monster_die(monster& mons, killer_type killer, // Lose our bullseye target mons.del_ench(ENCH_BULLSEYE_TARGET, true); + // Restore old items, if appropriate + mons.del_ench(ENCH_ARMED, true); + // Clean up any blood from the flayed effect if (mons.has_ench(ENCH_FLAYED)) heal_flayed_effect(&mons, true, true); @@ -1850,9 +1883,9 @@ item_def* monster_die(monster& mons, killer_type killer, const bool was_banished = (killer == KILL_BANISHED); const bool mons_reset = (killer == KILL_RESET || killer == KILL_DISMISSED); - const bool leaves_corpse = !summoned && !fake_abjure && !timeout - && !mons_reset - && !mons_is_tentacle_segment(mons.type); + bool leaves_corpse = !summoned && !fake_abjure && !timeout + && !mons_reset + && !mons_is_tentacle_segment(mons.type); // Award experience for suicide if the suicide was caused by the // player. if (MON_KILL(killer) && monster_killed == killer_index) @@ -1993,6 +2026,27 @@ item_def* monster_die(monster& mons, killer_type killer, silent = true; } + else if (leaves_corpse && mons.has_ench(ENCH_RIMEBLIGHT) + && !silent && !was_banished && !wizard && !mons_reset && !mons_reset + && mons.props.exists(RIMEBLIGHT_DEATH_KEY)) + { + leaves_corpse = false; + did_death_message = true; + if (you.see_cell(mons.pos())) + { + mprf(MSGCH_MONSTER_DAMAGE, MDAM_DEAD, + "Tendrils of ice devour %s body!", mons.name(DESC_ITS).c_str()); + } + death_spawn_fineff::schedule(MONS_PILLAR_OF_RIME, + mons.pos(), + random_range(3, 11) * BASELINE_DELAY); + } + else if (mons.has_ench(ENCH_MAGNETISED) && mons.type != MONS_ELECTROFERRIC_VORTEX) + { + death_spawn_fineff::schedule(MONS_ELECTROFERRIC_VORTEX, + mons.pos(), + random_range(3, 5) * BASELINE_DELAY); + } if (monster_explodes(mons)) { @@ -2011,6 +2065,7 @@ item_def* monster_die(monster& mons, killer_type killer, else if (mons.type == MONS_FIRE_VORTEX || mons.type == MONS_SPATIAL_VORTEX || mons.type == MONS_TWISTER + || mons.type == MONS_ELECTROFERRIC_VORTEX || (mons.type == MONS_FOXFIRE && mons.steps_remaining == 0)) { if (!silent && !mons_reset && !was_banished) @@ -2413,7 +2468,8 @@ item_def* monster_die(monster& mons, killer_type killer, // Necrotise/Animate Dead/Infestation else if (mons.type == MONS_ZOMBIE || mons.type == MONS_SKELETON - || mons.type == MONS_DEATH_SCARAB) + || mons.type == MONS_DEATH_SCARAB + || mons.type == MONS_SEISMIC_CANNON) { simple_monster_message(mons, " crumbles into dust!"); } @@ -3149,6 +3205,10 @@ string summoned_poof_msg(const monster* mons, bool plural) case SPELL_STICKS_TO_SNAKES: msg = "turns back into a lifeless stick"; break; + + case SPELL_FLASHING_BALESTRA: + msg = "returns to the armoury"; + break; } if (valid_mon) diff --git a/crawl-ref/source/mon-ench.cc b/crawl-ref/source/mon-ench.cc index 7f3744d331d..198db9ae093 100644 --- a/crawl-ref/source/mon-ench.cc +++ b/crawl-ref/source/mon-ench.cc @@ -30,16 +30,19 @@ #include "losglobal.h" #include "message.h" #include "mon-abil.h" +#include "mon-aura.h" #include "mon-behv.h" #include "mon-cast.h" #include "mon-death.h" #include "mon-explode.h" +#include "mon-gear.h" #include "mon-place.h" #include "mon-poly.h" #include "mon-tentacle.h" #include "religion.h" #include "spl-clouds.h" #include "spl-damage.h" +#include "spl-monench.h" #include "spl-summoning.h" #include "state.h" #include "stepdown.h" @@ -844,11 +847,6 @@ void monster::remove_enchantment_effect(const mon_enchant &me, bool quiet) simple_monster_message(*this, " is no longer unusually resistant."); break; - case ENCH_BRILLIANCE_AURA: - if (!quiet) - simple_monster_message(*this, " is no longer giving off an aura."); - break; - case ENCH_EMPOWERED_SPELLS: if (!quiet) simple_monster_message(*this, " seems less brilliant."); @@ -965,6 +963,21 @@ void monster::remove_enchantment_effect(const mon_enchant &me, bool quiet) scale_hp(100, touch_of_beogh_hp_mult(*this)); break; + case ENCH_ARMED: + // Restore our previous weapon(s) + drop_item(MSLOT_WEAPON, false); + if (props.exists(OLD_ARMS_KEY)) + { + give_specific_item(this, props[OLD_ARMS_KEY].get_item()); + props.erase(OLD_ARMS_KEY); + } + if (props.exists(OLD_ARMS_ALT_KEY)) + { + give_specific_item(this, props[OLD_ARMS_ALT_KEY].get_item()); + props.erase(OLD_ARMS_ALT_KEY); + } + break; + default: break; } @@ -975,6 +988,11 @@ bool monster::lose_ench_levels(const mon_enchant &e, int lev, bool infinite) if (!lev) return false; + // Check if this enchantment is being sustained by someone, and don't decay + // in that case. + if (e.ench_is_aura && aura_is_active(*this, e.ench)) + return false; + if (e.duration >= INFINITE_DURATION && !infinite) return false; if (e.degree <= lev) @@ -996,6 +1014,11 @@ bool monster::lose_ench_duration(const mon_enchant &e, int dur) if (!dur) return false; + // Check if this enchantment is being sustained by someone, and don't decay + // in that case. + if (e.ench_is_aura && aura_is_active(*this, e.ench)) + return false; + if (e.duration >= INFINITE_DURATION) return false; if (e.duration <= dur) @@ -1046,15 +1069,6 @@ bool monster::decay_enchantment(enchant_type en, bool decay_degree) 10; int actdur = speed_to_duration(spd); - // Don't let ENCH_SLOW time out while a torpor snail is around. - if (en == ENCH_SLOW) - { - if (torpor_slowed()) - actdur = min(actdur, me.duration - 1); - else - props.erase(TORPOR_SLOWED_KEY); - } - if (lose_ench_duration(me, actdur)) return true; @@ -1285,7 +1299,7 @@ void monster::apply_enchantment(const mon_enchant &me) case ENCH_FRENZIED: if (decay_enchantment(en)) { - simple_monster_message(*this, " is no longer in an wild frenzy."); + simple_monster_message(*this, " is no longer in a wild frenzy."); const int duration = random_range(70, 130); add_ench(mon_enchant(ENCH_FATIGUE, 0, 0, duration)); add_ench(mon_enchant(ENCH_SLOW, 0, 0, duration)); @@ -1354,7 +1368,6 @@ void monster::apply_enchantment(const mon_enchant &me) case ENCH_GOLD_LUST: case ENCH_RESISTANCE: case ENCH_HEXED: - case ENCH_BRILLIANCE_AURA: case ENCH_EMPOWERED_SPELLS: case ENCH_BOUND_SOUL: case ENCH_INFESTATION: @@ -1369,6 +1382,8 @@ void monster::apply_enchantment(const mon_enchant &me) case ENCH_INSTANT_CLEAVE: case ENCH_PROTEAN_SHAPESHIFTING: case ENCH_CURSE_OF_AGONY: + case ENCH_MAGNETISED: + case ENCH_REPEL_MISSILES: decay_enchantment(en); break; @@ -1497,6 +1512,9 @@ void monster::apply_enchantment(const mon_enchant &me) switch (type) { + case MONS_ELECTROFERRIC_VORTEX: + mprf("%s dissipates.", name(DESC_THE, false).c_str()); + break; case MONS_PILE_OF_DEBRIS: mprf("%s collapses into dust.", name(DESC_THE, false).c_str()); break; @@ -1643,7 +1661,7 @@ void monster::apply_enchantment(const mon_enchant &me) case ENCH_TP: if (decay_enchantment(en, true) && !no_tele()) - monster_teleport(this, true); + monster_teleport(this, true, false, true); break; case ENCH_AWAKEN_FOREST: @@ -1703,10 +1721,7 @@ void monster::apply_enchantment(const mon_enchant &me) case ENCH_INJURY_BOND: // It's hard to absorb someone else's injuries when you're dead if (!me.agent() || !me.agent()->alive() - || me.agent()->mid == MID_ANON_FRIEND - // XXX: A bit of a hack to end injury bond on allies of a martyred - // shade that became a flayed ghost. - || me.agent()->type == MONS_FLAYED_GHOST) + || me.agent()->mid == MID_ANON_FRIEND) { del_ench(ENCH_INJURY_BOND, true, false); } @@ -1866,6 +1881,27 @@ void monster::apply_enchantment(const mon_enchant &me) } break; + case ENCH_RIMEBLIGHT: + tick_rimeblight(*this); + if (!alive()) + return; + // Instakill at <=20% max hp + if (hit_points * 5 <= max_hit_points) + { + props[RIMEBLIGHT_DEATH_KEY] = true; + monster_die(*this, KILL_YOU, NON_MONSTER); + } + else if (decay_enchantment(en)) + simple_monster_message(*this, " recovers from rimeblight."); + break; + + case ENCH_ARMED: + // Remove this whenever the armoury stops being around; + if (!me.agent()) + del_ench(en); + decay_enchantment(en); + break; + default: break; } @@ -2091,9 +2127,9 @@ static const char *enchant_names[] = "resistant", "hexed", #if TAG_MAJOR_VERSION == 34 "corpse_armour", - "chanting_fire_storm", "chanting_word_of_entropy", + "chanting_fire_storm", "chanting_word_of_entropy", "aura_of_brilliance", #endif - "aura_of_brilliance", "empowered_spells", "gozag_incite", "pain_bond", + "empowered_spells", "gozag_incite", "pain_bond", "idealised", "bound_soul", "infestation", "stilling the winds", "thunder_ringed", #if TAG_MAJOR_VERSION == 34 @@ -2114,6 +2150,9 @@ static const char *enchant_names[] = "protean_shapeshifting", "simulacrum_sculpting", "curse_of_agony", "channel_searing_ray", "touch_of_beogh", "vengeance_target", + "rimeblight", + "magnetised", + "armed", "buggy", // NUM_ENCHANTMENTS }; @@ -2136,8 +2175,8 @@ enchant_type name_to_ench(const char *name) } mon_enchant::mon_enchant(enchant_type e, int deg, const actor* a, - int dur) - : ench(e), degree(deg), duration(dur), maxduration(0) + int dur, ench_aura_type is_aura) + : ench(e), degree(deg), duration(dur), maxduration(0), ench_is_aura(is_aura) { if (a) { @@ -2376,11 +2415,8 @@ int mon_enchant::calc_duration(const monster* mons, case ENCH_FROZEN: cturn = 3 * 10 / _mod_speed(10, mons->speed); break; - case ENCH_BRILLIANCE_AURA: - cturn = 20 * 10 / _mod_speed(10, mons->speed); - break; case ENCH_EMPOWERED_SPELLS: - cturn = 20 * 10 / _mod_speed(10, mons->speed); + cturn = 35 * 10 / _mod_speed(10, mons->speed); break; case ENCH_NECROTISE: return 10; diff --git a/crawl-ref/source/mon-ench.h b/crawl-ref/source/mon-ench.h index 4318451b57e..e527cd18f61 100644 --- a/crawl-ref/source/mon-ench.h +++ b/crawl-ref/source/mon-ench.h @@ -3,6 +3,7 @@ #include "enchant-type.h" #include "externs.h" #include "kill-category.h" +#include "mon-aura.h" #define INFINITE_DURATION 30000 #define MAX_ENCH_DEGREE_DEFAULT 4 @@ -26,10 +27,13 @@ class mon_enchant kill_category who; // Source's alignment. mid_t source; // Who set this enchantment? + ench_aura_type ench_is_aura; // Whether the source is a passive aura + // that needs regular checking. + public: mon_enchant(enchant_type e = ENCH_NONE, int deg = 0, const actor *whose = 0, - int dur = 0); + int dur = 0, ench_aura_type ench_is_aura = AURA_NO); killer_type killer() const; int kill_agent() const; diff --git a/crawl-ref/source/mon-enum.h b/crawl-ref/source/mon-enum.h index 9b95f46c07c..eb39b27fb02 100644 --- a/crawl-ref/source/mon-enum.h +++ b/crawl-ref/source/mon-enum.h @@ -150,6 +150,7 @@ enum attack_flavour AF_DRAG, AF_FOUL_FLAME, AF_HELL_HUNT, + AF_SWARM, }; // Non-spell "summoning" types to give to monster::mark_summoned(), or diff --git a/crawl-ref/source/mon-flags.h b/crawl-ref/source/mon-flags.h index d4a9fbb068c..f1c48109b48 100644 --- a/crawl-ref/source/mon-flags.h +++ b/crawl-ref/source/mon-flags.h @@ -71,7 +71,8 @@ enum monclass_flag_type : uint64_t /// monster digs through rock M_BURROWS = BIT(19), - //BIT(20), + /// monster passively applies an enchantment in LoS of itself + M_HAS_AURA = BIT(20), /// monster is a unique M_UNIQUE = BIT(21), diff --git a/crawl-ref/source/mon-gear.cc b/crawl-ref/source/mon-gear.cc index bb3c1f01a94..dc99c1c3b40 100644 --- a/crawl-ref/source/mon-gear.cc +++ b/crawl-ref/source/mon-gear.cc @@ -1186,6 +1186,17 @@ int make_mons_weapon(monster_type type, int level, bool melee_only) make_item_unrandart(item, UNRAND_SNAKEBITE); break; + case MONS_XAKKRIXIS: + force_item = true; + item.base_type = OBJ_WEAPONS; + item.plus += 1 + random2(4); + item.sub_type = random_choose_weighted(15, WPN_GREAT_MACE, + 10, WPN_LAJATANG, + 10, WPN_QUARTERSTAFF, + 5, WPN_GLAIVE); + set_item_ego_type(item, OBJ_WEAPONS, SPWPN_VENOM); + break; + case MONS_ARACHNE: force_item = true; item.base_type = OBJ_STAVES; @@ -1602,6 +1613,7 @@ static void _give_shield(monster* mon, int level) switch (mon->type) { case MONS_ASTERION: + case MONS_XAKKRIXIS: make_item_for_monster(mon, OBJ_ARMOUR, ARM_KITE_SHIELD, level * 2 + 1, 1); break; diff --git a/crawl-ref/source/mon-info-flag-name.h b/crawl-ref/source/mon-info-flag-name.h index f88f473e3f2..1bf60510f30 100644 --- a/crawl-ref/source/mon-info-flag-name.h +++ b/crawl-ref/source/mon-info-flag-name.h @@ -49,7 +49,6 @@ static const vector monster_info_flag_names = { { MB_STILL_WINDS, "stilling wind", "stilling the winds", "stilling wind"}, { MB_SILENCING, "silencing", "radiating silence", "silencing"}, { MB_READY_TO_HOWL, "can howl", "ready to howl", "can howl"}, - { MB_BRILLIANCE_AURA, "brilliance aura", "brilliance aura", "brilliance auras"}, { MB_VORTEX, "vortex", "surrounded by a freezing vortex", "vortices"}, { MB_VORTEX_COOLDOWN, "gusty", "surrounded by restless winds", "gusty"}, { MB_FRENZIED, "frenzied", "frenzied and wild", "frenzied"}, @@ -146,4 +145,6 @@ static const vector monster_info_flag_names = { { MB_TOUCH_OF_BEOGH, "divinely empowered", "empowered by the touch of Beogh", "divinely empowered"}, { MB_AWAITING_RECRUITMENT, "anointable", "ready to become your apostle", "anointable"}, { MB_VENGEANCE_TARGET, "target of vengeance", "target of orcish vengeance", "targets of vengeance"}, + { MB_MAGNETISED, "magnetised", "covered in magnetic dust", "magnetised"}, + { MB_RIMEBLIGHT, "rimeblight", "afflicted by rimeblight", "rimeblight"}, }; diff --git a/crawl-ref/source/mon-info.cc b/crawl-ref/source/mon-info.cc index 97f848acb2f..721c29dca45 100644 --- a/crawl-ref/source/mon-info.cc +++ b/crawl-ref/source/mon-info.cc @@ -103,7 +103,6 @@ static map trivial_ench_mb_mappings = { { ENCH_REPEL_MISSILES, MB_REPEL_MSL }, { ENCH_RESISTANCE, MB_RESISTANCE }, { ENCH_HEXED, MB_HEXED }, - { ENCH_BRILLIANCE_AURA, MB_BRILLIANCE_AURA }, { ENCH_EMPOWERED_SPELLS, MB_EMPOWERED_SPELLS }, { ENCH_GOZAG_INCITE, MB_GOZAG_INCITED }, { ENCH_PAIN_BOND, MB_PAIN_BOND }, @@ -132,6 +131,9 @@ static map trivial_ench_mb_mappings = { { ENCH_CURSE_OF_AGONY, MB_CURSE_OF_AGONY }, { ENCH_TOUCH_OF_BEOGH, MB_TOUCH_OF_BEOGH }, { ENCH_VENGEANCE_TARGET, MB_VENGEANCE_TARGET }, + { ENCH_MAGNETISED, MB_MAGNETISED }, + { ENCH_RIMEBLIGHT, MB_RIMEBLIGHT }, + { ENCH_ARMED, MB_ARMED }, }; static monster_info_flags ench_to_mb(const monster& mons, enchant_type ench) @@ -813,18 +815,10 @@ monster_info::monster_info(const monster* m, int milev) } /// Player-known max HP information for a monster: "about 55", "243". -string monster_info::get_max_hp_desc() const +int monster_info::get_known_max_hp() const { if (props.exists(KNOWN_MAX_HP_KEY)) - { - const int hp = props[KNOWN_MAX_HP_KEY].get_int(); - - // Indicate that this apostle's HP has been increased by Beogh - if (type == MONS_ORC_APOSTLE && is(MB_TOUCH_OF_BEOGH)) - return make_stringf("*%d*", hp); - - return std::to_string(hp); - } + return props[KNOWN_MAX_HP_KEY].get_int(); const int scale = 100; const int base_avg_hp = mons_class_is_zombified(type) ? @@ -842,20 +836,49 @@ string monster_info::get_max_hp_desc() const mhp *= slime_size; mhp /= scale; - return make_stringf("~%d", mhp); + + return mhp; +} + +/// Player-known max HP information for a monster: "about 55", "243". +string monster_info::get_max_hp_desc() const +{ + int hp = get_known_max_hp(); + if (props.exists(KNOWN_MAX_HP_KEY)) + { + // Indicate that this apostle's HP has been increased by Beogh + if (type == MONS_ORC_APOSTLE && is(MB_TOUCH_OF_BEOGH)) + return make_stringf("*%d*", hp); + + return std::to_string(hp); + } + + return make_stringf("~%d", hp); } /// HP regenerated every (scale) turns. int monster_info::regen_rate(int scale) const { + int rate = 0; if (!can_regenerate() || is(MB_SICK) /* ? */) return 0; - if (mons_class_fast_regen(type) || is(MB_REGENERATION) /* ? */) - return mons_class_regen_amount(type) * scale; - // Duplicates monster::natural_regen_rate. - const int divider = max(((15 - hd) + 2 /*round up*/) / 4, 1); - return min(scale, max(1, hd * scale / (divider * 25))); + if (mons_class_fast_regen(type)) + rate = mons_class_regen_amount(type) * scale; + else + { + // Duplicates monster::natural_regen_rate. + const int divider = max(((15 - hd) + 2 /*round up*/) / 4, 1); + rate = min(scale, max(1, hd * scale / (divider * 25))); + } + + // XXX: This rounds slightly inaccurately, and also doesn't say that it's + // an approximation when monster hp isn't known for sure. Still, it's + // probably close enough? + if (is(MB_REGENERATION)) + rate += 3 + get_known_max_hp() / 20; + + return rate; } /** diff --git a/crawl-ref/source/mon-info.h b/crawl-ref/source/mon-info.h index 6ea730d5cd4..fb3c360c9b7 100644 --- a/crawl-ref/source/mon-info.h +++ b/crawl-ref/source/mon-info.h @@ -169,7 +169,9 @@ enum monster_info_flags MB_CHANT_WORD_OF_ENTROPY, #endif MB_AIRBORNE, +#if TAG_MAJOR_VERSION == 34 MB_BRILLIANCE_AURA, +#endif MB_EMPOWERED_SPELLS, MB_READY_TO_HOWL, MB_PARTIALLY_CHARGED, @@ -227,6 +229,9 @@ enum monster_info_flags MB_TOUCH_OF_BEOGH, MB_AWAITING_RECRUITMENT, MB_VENGEANCE_TARGET, + MB_MAGNETISED, + MB_RIMEBLIGHT, + MB_ARMED, NUM_MB_FLAGS }; @@ -349,6 +354,7 @@ struct monster_info : public monster_info_base return get_damage_level_string(holi, dam); } string get_max_hp_desc() const; + int get_known_max_hp() const; int regen_rate(int scale) const; inline bool neutral() const diff --git a/crawl-ref/source/mon-pick-data.h b/crawl-ref/source/mon-pick-data.h index 2fa5f33e288..af6b20ecf75 100644 --- a/crawl-ref/source/mon-pick-data.h +++ b/crawl-ref/source/mon-pick-data.h @@ -98,13 +98,14 @@ static const vector population[] = { 4, 12, 150, PEAK, MONS_ORC_PRIEST }, { 4, 14, 500, PEAK, MONS_ORC_WIZARD }, { 4, 14, 350, PEAK, MONS_HOWLER_MONKEY }, - { 5, 7, 350, PEAK, MONS_UFETUBUS }, + { 4, 7, 350, PEAK, MONS_UFETUBUS }, + { 4, 7, 300, PEAK, MONS_SHADOW_IMP }, { 5, 8, 500, PEAK, MONS_WHITE_IMP }, - { 5, 8, 300, PEAK, MONS_SHADOW_IMP }, { 5, 8, 500, PEAK, MONS_ICE_BEAST }, { 5, 9, 400, PEAK, MONS_WATER_MOCCASIN }, { 5, 13, 600, PEAK, MONS_CENTAUR }, { 5, 13, 200, PEAK, MONS_GNOLL_SERGEANT }, + { 5, 9, 100, PEAK, MONS_MARROWCUDA }, { 6, 8, 500, PEAK, MONS_SKY_BEAST }, { 6, 9, 1000, PEAK, MONS_BULLFROG }, @@ -483,6 +484,7 @@ static const vector population[] = // Harder enemies: { 1, 4, 1250, FLAT, MONS_VAULT_SENTINEL }, { 1, 4, 150, FLAT, MONS_ENTROPY_WEAVER }, + { 1, 4, 100, FLAT, MONS_POLTERGUARDIAN }, { 1, 4, 1200, FLAT, MONS_IRONBOUND_CONVOKER }, { 1, 4, 1200, FLAT, MONS_IRONBOUND_PRESERVER }, { 1, 4, 725, FLAT, MONS_IRONBOUND_FROSTHEART }, @@ -545,6 +547,7 @@ static const vector population[] = { 5, 12, 330, FALL, MONS_VERY_UGLY_THING }, { 5, 12, 260, FALL, MONS_SPHINX }, { 5, 12, 260, FALL, MONS_WAR_GARGOYLE }, + { 5, 12, 300, FALL, MONS_POLTERGUARDIAN }, // V:5 chaff we should really get rid of { 5, 5, 650, FLAT, MONS_ORC_KNIGHT }, { 5, 5, 60, FLAT, MONS_REDBACK }, @@ -559,7 +562,7 @@ static const vector population[] = // relative to the 'normal' enemies. { 5, 12, 190, RISE, MONS_TITAN }, { 5, 12, 170, RISE, MONS_SHADOW_DRAGON }, - { 5, 12, 120, RISE, MONS_TENTACLED_MONSTROSITY }, + { 5, 12, 120, RISE, MONS_UNDYING_ARMOURY }, { 5, 12, 110, RISE, MONS_STORM_DRAGON }, { 5, 12, 70, RISE, MONS_GOLDEN_DRAGON }, { 5, 12, 50, RISE, MONS_QUICKSILVER_DRAGON }, @@ -623,7 +626,7 @@ POP_DEPTHS, { 1, 1, 89, FLAT, MONS_HELL_RAT }, { 1, 1, 89, FLAT, MONS_DEMONIC_CRAWLER }, { 1, 1, 89, FLAT, MONS_HELL_HOUND }, - { 1, 1, 136, FLAT, MONS_HELL_HOG }, + { 1, 1, 145, FLAT, MONS_HELL_HOG }, { 1, 1, 136, FLAT, MONS_HELL_KNIGHT }, { 1, 1, 136, FLAT, MONS_NECROMANCER }, { 1, 1, 25, FLAT, MONS_HELLEPHANT }, @@ -632,14 +635,13 @@ POP_DEPTHS, { 1, 1, 89, FLAT, MONS_ICE_DEVIL }, { 1, 1, 515, FLAT, MONS_SUN_DEMON }, { 1, 1, 515, FLAT, MONS_SOUL_EATER }, - { 1, 1, 515, FLAT, MONS_REAPER }, - { 1, 1, 89, FLAT, MONS_LOROCYPROCA }, + { 1, 1, 545, FLAT, MONS_REAPER }, { 1, 1, 25, FLAT, MONS_HELLION }, { 1, 1, 25, FLAT, MONS_TORMENTOR }, { 1, 1, 89, FLAT, MONS_RUST_DEVIL }, - { 1, 1, 52, FLAT, MONS_GREEN_DEATH }, - { 1, 1, 52, FLAT, MONS_BLIZZARD_DEMON }, - { 1, 1, 52, FLAT, MONS_BALRUG }, + { 1, 1, 89, FLAT, MONS_GREEN_DEATH }, + { 1, 1, 62, FLAT, MONS_BLIZZARD_DEMON }, + { 1, 1, 62, FLAT, MONS_BALRUG }, { 1, 1, 52, FLAT, MONS_CACODEMON }, }, @@ -929,21 +931,20 @@ POP_DEPTHS, { 6, 10, 10, RISE, MONS_NARGUN }, { 6, 10, 10, RISE, MONS_PUTRID_MOUTH }, // Generic - { 1, 7, 335, FLAT, MONS_WRETCHED_STAR }, - { 1, 7, 335, FLAT, MONS_TENTACLED_STARSPAWN }, - { 1, 7, 335, FLAT, MONS_ANCIENT_ZYME }, + { 1, 7, 340, FLAT, MONS_WRETCHED_STAR }, + { 1, 7, 340, FLAT, MONS_TENTACLED_STARSPAWN }, + { 1, 7, 340, FLAT, MONS_ANCIENT_ZYME }, { 1, 7, 335, FLAT, MONS_STARCURSED_MASS }, { 1, 7, 335, FLAT, MONS_THRASHING_HORROR }, - { 1, 7, 89, FLAT, MONS_LURKING_HORROR }, - { 1, 7, 89, FLAT, MONS_APOCALYPSE_CRAB }, + { 1, 7, 90, FLAT, MONS_LURKING_HORROR }, + { 1, 7, 90, FLAT, MONS_APOCALYPSE_CRAB }, { 1, 7, 225, FLAT, MONS_NEQOXEC }, { 1, 7, 300, FLAT, MONS_SMOKE_DEMON }, - { 1, 7, 25, FLAT, MONS_LOROCYPROCA }, { 1, 7, 25, FLAT, MONS_HELLION }, { 1, 7, 21, FLAT, MONS_TORMENTOR }, - { 1, 7, 12, FLAT, MONS_REAPER }, - { 1, 7, 22, FLAT, MONS_GREEN_DEATH }, + { 1, 7, 15, FLAT, MONS_REAPER }, + { 1, 7, 25, FLAT, MONS_GREEN_DEATH }, { 1, 7, 22, FLAT, MONS_BLIZZARD_DEMON }, { 1, 7, 22, FLAT, MONS_BALRUG }, { 1, 7, 25, FLAT, MONS_CACODEMON }, @@ -951,7 +952,7 @@ POP_DEPTHS, { 1, 7, 10, FLAT, MONS_HELL_SENTINEL }, { 1, 7, 23, FLAT, MONS_SHADOW_WRAITH }, - { 1, 7, 8, FLAT, MONS_SILENT_SPECTRE }, + { 1, 7, 10, FLAT, MONS_SILENT_SPECTRE }, { 1, 7, 8, FLAT, MONS_PHANTASMAL_WARRIOR }, { 1, 7, 80, FLAT, MONS_BONE_DRAGON }, { 1, 7, 9, FLAT, MONS_REVENANT }, @@ -1014,10 +1015,9 @@ POP_DEPTHS, { 1, 1, 1000, FLAT, MONS_CACODEMON }, { 1, 1, 335, FLAT, MONS_HELLION }, { 1, 1, 335, FLAT, MONS_TORMENTOR }, - { 1, 1, 335, FLAT, MONS_REAPER }, - { 1, 1, 335, FLAT, MONS_SHADOW_DEMON }, - { 1, 1, 535, FLAT, MONS_HELL_BEAST }, - { 1, 1, 535, FLAT, MONS_LOROCYPROCA }, + { 1, 1, 535, FLAT, MONS_REAPER }, + { 1, 1, 535, FLAT, MONS_SHADOW_DEMON }, + { 1, 1, 675, FLAT, MONS_SIN_BEAST }, { 1, 1, 1000, FLAT, MONS_EXECUTIONER }, { 1, 1, 335, FLAT, MONS_BRIMSTONE_FIEND }, { 1, 1, 335, FLAT, MONS_ICE_FIEND }, @@ -1207,10 +1207,11 @@ static const vector pop_generic_late_zombie = static const vector population_water[] = { { // Dungeon water monsters + { 5, 13, 90, SEMI, MONS_MARROWCUDA }, { 5, 16, 60, FLAT, MONS_ELECTRIC_EEL }, { 7, 16, 185, PEAK, MONS_ELECTRIC_EEL }, { 11, 27, 600, RISE, MONS_WATER_ELEMENTAL }, - { 5, 22, 130, FLAT, MONS_NO_MONSTER }, + { 5, 22, 110, FLAT, MONS_NO_MONSTER }, { 9, 32, 250, SEMI, MONS_NO_MONSTER }, }, GENERIC_WATER_POP, // Temple diff --git a/crawl-ref/source/mon-place.cc b/crawl-ref/source/mon-place.cc index 796ffc4287b..df03778dbe5 100644 --- a/crawl-ref/source/mon-place.cc +++ b/crawl-ref/source/mon-place.cc @@ -1206,12 +1206,21 @@ static monster* _place_monster_aux(const mgen_data &mg, const monster *leader, if (mg.cls == MONS_GLOWING_SHAPESHIFTER) mon->add_ench(ENCH_GLOWING_SHAPESHIFTER); + if (mg.cls == MONS_ABOMINATION_SMALL || mg.cls == MONS_ABOMINATION_LARGE) + { + enchant_type buff = random_choose(ENCH_MIGHT, ENCH_HASTE, ENCH_REGENERATION); + mon->add_ench(mon_enchant(buff, 0, 0, INFINITE_DURATION)); + } + if (mg.cls == MONS_TWISTER || mg.cls == MONS_DIAMOND_OBELISK) { mon->props[POLAR_VORTEX_KEY].get_int() = you.elapsed_time; mon->add_ench(mon_enchant(ENCH_POLAR_VORTEX, 0, 0, INFINITE_DURATION)); } + if (mg.cls == MONS_ELECTROFERRIC_VORTEX) + mon->add_ench(mon_enchant(ENCH_MAGNETISED, 1, nullptr, INFINITE_DURATION)); + // this MUST follow hd initialization! if (mons_is_hepliaklqana_ancestor(mon->type)) { @@ -1932,7 +1941,7 @@ static const map bands_by_leader = { { MONS_TARANTELLA, { {2}, {{ BAND_TARANTELLA, {1, 5} }}}}, { MONS_VAULT_WARDEN, { {}, {{ BAND_YAKTAURS, {2, 6}, true }, { BAND_VAULT_WARDEN, {2, 5}, true }}}}, - { MONS_IRONBOUND_PRESERVER, { {}, {{ BAND_DEEP_TROLLS, {3, 6}, true }}}}, + { MONS_IRONBOUND_PRESERVER, { {}, {{ BAND_PRESERVER, {3, 6}, true }}}}, { MONS_TENGU_CONJURER, { {2}, {{ BAND_TENGU, {1, 2} }}}}, { MONS_TENGU_WARRIOR, { {2}, {{ BAND_TENGU, {1, 2} }}}}, { MONS_SOJOBO, { {}, {{ BAND_SOJOBO, {2, 3}, true }}}}, @@ -1955,6 +1964,10 @@ static const map bands_by_leader = { return branch_has_monsters(you.where_are_you) || !vault_mon_types.empty(); }}, {{ BAND_RANDOM_SINGLE, {1, 2} }}}}, + { MONS_POLTERGUARDIAN, { {2, 0, []() { + return branch_has_monsters(you.where_are_you) + || !vault_mon_types.empty(); + }}, {{ BAND_RANDOM_SINGLE, {1, 3} }}}}, { MONS_CEREBOV, { {}, {{ BAND_CEREBOV, {5, 8}, true }}}}, { MONS_GLOORX_VLOQ, { {}, {{ BAND_GLOORX_VLOQ, {5, 8}, true }}}}, { MONS_MNOLEG, { {}, {{ BAND_MNOLEG, {5, 8}, true }}}}, @@ -1998,6 +2011,7 @@ static const map bands_by_leader = { { MONS_NORRIS, { {}, {{ BAND_SKYSHARKS, {2, 5}, true }}}}, { MONS_UFETUBUS, { {}, {{ BAND_UFETUBI, {1, 2} }}}}, { MONS_KOBOLD_BLASTMINER, { {}, {{ BAND_BLASTMINER, {0, 2} }}}}, + { MONS_ARACHNE, { {}, {{ BAND_ORB_SPIDERS, {2, 4}}}}}, // special-cased band-sizes { MONS_SPRIGGAN_DRUID, { {3}, {{ BAND_SPRIGGAN_DRUID, {0, 1}, true }}}}, @@ -2241,7 +2255,9 @@ static const map> band_membership = { { BAND_EXECUTIONER, {{{MONS_ABOMINATION_LARGE, 1}}}}, { BAND_VASHNIA, {{{MONS_NAGA_SHARPSHOOTER, 1}}}}, { BAND_PHANTASMAL_WARRIORS, {{{MONS_PHANTASMAL_WARRIOR, 1}}}}, - { BAND_DEEP_TROLLS, {{{MONS_DEEP_TROLL, 1}}}}, + { BAND_PRESERVER, {{{MONS_DEEP_TROLL, 10}, + {MONS_POLTERGUARDIAN, 2}}, + {{MONS_DEEP_TROLL, 1}}}}, { BAND_BONE_DRAGONS, {{{MONS_BONE_DRAGON, 1}}}}, { BAND_SPECTRALS, {{{MONS_SPECTRAL_THING, 1}}}}, { BAND_UFETUBI, {{{MONS_UFETUBUS, 1}}}}, @@ -2390,6 +2406,10 @@ static const map> band_membership = { {MONS_IRONBOUND_THUNDERHULK, 2}, {MONS_VAULT_GUARD, 20}}, + // 25% chance of a polterguardian + {{MONS_POLTERGUARDIAN, 1}, + {MONS_VAULT_GUARD, 4}}, + {{MONS_VAULT_GUARD, 1}}}}, { BAND_FAUN_PARTY, {{{MONS_MERFOLK_SIREN, 1}}, @@ -2503,7 +2523,7 @@ static const map> band_membership = { {MONS_DEMONSPAWN_BLACK_SUN, 1}}}}, { BAND_WARMONGER, {{{MONS_EXECUTIONER, 1}, - {MONS_REAPER, 3}}, + {MONS_SIN_BEAST, 3}}, {{MONS_DEMONSPAWN_BLOOD_SAINT, 1}, {MONS_DEMONSPAWN_WARMONGER, 1}, @@ -2518,7 +2538,7 @@ static const map> band_membership = { {MONS_DEMONSPAWN_CORRUPTER, 1}, {MONS_DEMONSPAWN_BLACK_SUN, 1}}}}, - { BAND_BLACK_SUN, {{{MONS_LOROCYPROCA, 1}, + { BAND_BLACK_SUN, {{{MONS_REAPER, 1}, {MONS_SOUL_EATER, 1}}, {{MONS_DEMONSPAWN_BLOOD_SAINT, 1}, @@ -2529,6 +2549,8 @@ static const map> band_membership = { { BAND_DOOM_HOUNDS, {{{MONS_DOOM_HOUND, 1}}}}, // for Norris { BAND_SKYSHARKS, {{{MONS_SKYSHARK, 1}}}}, + // for Arachne + { BAND_ORB_SPIDERS, {{{MONS_ORB_SPIDER, 1}}}}, }; /** @@ -3056,10 +3078,9 @@ monster_type random_demon_by_tier(int tier) MONS_BLIZZARD_DEMON, MONS_BALRUG, MONS_CACODEMON, - MONS_HELL_BEAST, + MONS_SIN_BEAST, MONS_HELLION, MONS_REAPER, - MONS_LOROCYPROCA, MONS_TORMENTOR, MONS_SHADOW_DEMON); case 1: diff --git a/crawl-ref/source/mon-project.cc b/crawl-ref/source/mon-project.cc index 044fba61f73..133618c4e2c 100644 --- a/crawl-ref/source/mon-project.cc +++ b/crawl-ref/source/mon-project.cc @@ -203,6 +203,9 @@ void cast_iood_burst(int pow, coord_def target) const int n_orbs = random_range(3, 7); dprf("Bursting %d orbs.", n_orbs); + // 2097152 = 2^21. 21 is the greatest n s.t. `(2 ** n) * PI * 2` does not + // exceed 2 ** 24; 24 bits is where `float` (`PI` is a float constant) + // starts to lose precision. const double angle0 = random2(2097152) * PI * 2 / 2097152; for (int i = 0; i < n_orbs; i++) diff --git a/crawl-ref/source/mon-spell.h b/crawl-ref/source/mon-spell.h index ad1490b9e11..40ffdae957e 100644 --- a/crawl-ref/source/mon-spell.h +++ b/crawl-ref/source/mon-spell.h @@ -134,7 +134,7 @@ static const mon_spellbook mspell_list[] = { SPELL_SLOW, 12, MON_SPELL_WIZARD }, { SPELL_CONFUSE, 12, MON_SPELL_WIZARD }, { SPELL_REPEL_MISSILES, 12, MON_SPELL_WIZARD }, - { SPELL_BLINK_AWAY, 36, MON_SPELL_WIZARD | MON_SPELL_SHORT_RANGE }, + { SPELL_BLINK_RANGE, 36, MON_SPELL_WIZARD | MON_SPELL_SHORT_RANGE }, } }, @@ -159,7 +159,7 @@ static const mon_spellbook mspell_list[] = { MST_DEEP_ELF_HIGH_PRIEST, { - { SPELL_AURA_OF_BRILLIANCE, 25, MON_SPELL_PRIEST }, + { SPELL_PRAYER_OF_BRILLIANCE, 25, MON_SPELL_PRIEST }, { SPELL_CALL_DOWN_DAMNATION, 12, MON_SPELL_PRIEST }, { SPELL_MALIGN_OFFERING, 18, MON_SPELL_PRIEST }, { SPELL_SMITING, 18, MON_SPELL_PRIEST }, @@ -423,7 +423,7 @@ static const mon_spellbook mspell_list[] = { MST_ARCANIST, { { SPELL_BOLT_OF_COLD, 12, MON_SPELL_WIZARD }, - { SPELL_FORCE_LANCE, 10, MON_SPELL_WIZARD }, + { SPELL_SEARING_RAY, 16, MON_SPELL_WIZARD }, { SPELL_VITRIFY, 12, MON_SPELL_WIZARD }, { SPELL_HASTE, 14, MON_SPELL_WIZARD }, } @@ -575,7 +575,7 @@ static const mon_spellbook mspell_list[] = { MST_JUMPING_SPIDER, { { SPELL_BLINK_CLOSE, 29, MON_SPELL_NATURAL }, - { SPELL_BLINK_AWAY, 29, MON_SPELL_NATURAL }, + { SPELL_BLINK_RANGE, 29, MON_SPELL_NATURAL }, } }, @@ -631,6 +631,19 @@ static const mon_spellbook mspell_list[] = } }, + { MST_POLTERGUARDIAN, + { + { SPELL_FORCE_LANCE, 40, MON_SPELL_MAGICAL }, + } + }, + + { MST_UNDYING_ARMOURY, + { + { SPELL_BESTOW_ARMS, 40, MON_SPELL_NATURAL }, + { SPELL_FLASHING_BALESTRA, 35, MON_SPELL_NATURAL }, + } + }, + // ('w') Slugs and worms. { MST_DART_SLUG, { @@ -791,6 +804,14 @@ static const mon_spellbook mspell_list[] = } }, + { MST_XAKKRIXIS, + { + { SPELL_VENOM_BOLT, 24, MON_SPELL_WIZARD}, + { SPELL_IGNITE_POISON, 35, MON_SPELL_WIZARD}, + { SPELL_FULMINANT_PRISM, 21, MON_SPELL_WIZARD}, + } + }, + // ('C') Giants. { MST_FIRE_GIANT, { @@ -1204,7 +1225,7 @@ static const mon_spellbook mspell_list[] = { MST_IRONBOUND_THUNDERHULK, { { SPELL_CALL_DOWN_LIGHTNING, 50, MON_SPELL_MAGICAL }, - { SPELL_BLINK_RANGE, 25, MON_SPELL_MAGICAL }, + { SPELL_BLINK_RANGE, 20, MON_SPELL_MAGICAL }, } }, @@ -1640,6 +1661,12 @@ static const mon_spellbook mspell_list[] = } }, + { MST_SEISMIC_CANNON, + { + { SPELL_STONE_BULLET, 200, MON_SPELL_MAGICAL }, + } + }, + { MST_ELECTRIC_GOLEM, { { SPELL_LIGHTNING_BOLT, 44, MON_SPELL_MAGICAL }, @@ -1984,7 +2011,7 @@ static const mon_spellbook mspell_list[] = { SPELL_PORTAL_PROJECTILE, 22, MON_SPELL_WIZARD }, { SPELL_BLINK_OTHER, 11, MON_SPELL_WIZARD | MON_SPELL_EMERGENCY }, { SPELL_BLINK_ALLIES_AWAY, 22, MON_SPELL_WIZARD }, - { SPELL_BLINK_AWAY, 11, MON_SPELL_WIZARD }, + { SPELL_BLINK_RANGE, 11, MON_SPELL_WIZARD }, } }, @@ -2273,7 +2300,7 @@ static const mon_spellbook mspell_list[] = // ('&', mostly) Demon lords. { MST_GERYON, { - { SPELL_SUMMON_HELL_BEAST, 65, MON_SPELL_VOCAL }, + { SPELL_SUMMON_SIN_BEAST, 55, MON_SPELL_VOCAL }, } }, diff --git a/crawl-ref/source/mon-transit.cc b/crawl-ref/source/mon-transit.cc index 4297b565ccb..592699d4485 100644 --- a/crawl-ref/source/mon-transit.cc +++ b/crawl-ref/source/mon-transit.cc @@ -355,7 +355,9 @@ static bool _mons_can_follow_player_from(const monster &mons, if (!mons.alive() || mons.speed_increment < 50 || mons.incapacitated() - || mons.is_stationary()) + || mons.is_stationary() + || mons.is_constricted() + || mons.has_ench(ENCH_BOUND)) { return false; } diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc index cc612a746ad..afa06497baa 100644 --- a/crawl-ref/source/mon-util.cc +++ b/crawl-ref/source/mon-util.cc @@ -95,6 +95,7 @@ static vector species_by_habitat[NUM_HABITATS]; #define MONDATASIZE ARRAYSZ(mondata) +static bool _give_apostle_proper_name(monster& mon, apostle_type type); static int _mons_exp_mod(monster_type mclass); // Macro that saves some typing, nothing more. @@ -794,11 +795,10 @@ bool mons_is_active_ballisto(const monster& mon) */ bool mons_class_is_firewood(monster_type mc) { - return mc == MONS_BOULDER || - (mons_class_is_stationary(mc) + return mons_class_is_stationary(mc) && !mons_class_is_test(mc) && mons_class_flag(mc, M_NO_THREAT) - && !mons_is_tentacle_or_tentacle_segment(mc)); + && !mons_is_tentacle_or_tentacle_segment(mc); } /** @@ -890,6 +890,7 @@ bool mons_is_object(monster_type mc) || mc == MONS_LURKING_HORROR || mc == MONS_DANCING_WEAPON || mc == MONS_LIGHTNING_SPIRE + || mc == MONS_SEISMIC_CANNON || mc == MONS_CREEPING_INFERNO; } @@ -1397,7 +1398,7 @@ static bool _shout_fits_monster(monster_type mc, int shout) // For Pandemonium lords, almost everything is fair game. It's only // used for the shouting verb ("say", "bellow", "roar", etc.) anyway. - if (mc != MONS_HELL_BEAST && mc != MONS_MUTANT_BEAST) + if (mc != MONS_SIN_BEAST && mc != MONS_MUTANT_BEAST) return true; switch (shout) @@ -1937,7 +1938,7 @@ static mon_attack_def _mutant_beast_facet_attack(int facet, int tier) case BF_OX: return { AT_TRAMPLE, AF_TRAMPLE, dam }; case BF_WEIRD: - return { AT_CONSTRICT, AF_CRUSH, dam }; + return { AT_CONSTRICT, AF_CRUSH, dam * 2 / 5}; default: return { }; } @@ -2882,15 +2883,6 @@ void define_monster(monster& mons, bool friendly) switch (mcls) { - // Please keep describe.cc in sync if you change abominations. - case MONS_ABOMINATION_SMALL: - hd = 4 + random2(4); - break; - - case MONS_ABOMINATION_LARGE: - hd = 8 + random2(4); - break; - case MONS_SLIME_CREATURE: // Slime creatures start off as only single un-merged blobs. mons.blob_size = 1; @@ -3015,13 +3007,13 @@ void define_monster(monster& mons, bool friendly) else mons.props[TILE_NUM_KEY].get_short() = 200; - give_monster_proper_name(mons); + _give_apostle_proper_name(mons, type); // Reroll our name until it is different from all player apostle names, // to try and lessen possible confusion if they end up with two that // have identical names. while (!apostle_has_unique_name(mons)) - give_monster_proper_name(mons); + _give_apostle_proper_name(mons, type); break; } @@ -3268,6 +3260,19 @@ bool give_monster_proper_name(monster& mon) return mon.is_named(); } +// Names an orc apostle (will rename it if it already had a name) +static bool _give_apostle_proper_name(monster& mon, apostle_type type) +{ + string apostle_key = "orc apostle " + apostle_type_names[type] + " name"; + mon.mname = getRandMonNameString(apostle_key); + + // XXX: The rest of this is duplicated from give_monster_proper_name(). + if (!mon.props.exists(DBNAME_KEY)) + mon.props[DBNAME_KEY] = mons_class_name(mon.type); + + return mon.is_named(); +} + // See mons_init for initialization of mon_entry array. monsterentry *get_monster_data(monster_type mc) { @@ -3960,7 +3965,7 @@ bool monster_shover(const monster& m) if (!mons_can_use_stairs(m) && !m.is_summoned()) return false; - // Geryon really profits from *not* pushing past hell beasts. + // Geryon really profits from *not* pushing past sin beasts. if (m.type == MONS_GERYON) return false; // Likewise, Robin and her mob. diff --git a/crawl-ref/source/monster-type.h b/crawl-ref/source/monster-type.h index 25167cfc306..d9c254f3f8e 100644 --- a/crawl-ref/source/monster-type.h +++ b/crawl-ref/source/monster-type.h @@ -338,6 +338,7 @@ enum monster_type // env.mons[].type #if TAG_MAJOR_VERSION > 34 MONS_THERMIC_DYNAMO, MONS_WILL_O_THE_WISP, + MONS_ELECTROFERRIC_VORTEX, #else MONS_VAPOUR, @@ -557,6 +558,8 @@ enum monster_type // env.mons[].type MONS_HALAZID_WARLOCK, MONS_SPECTATOR, MONS_METEORAN, + MONS_POLTERGUARDIAN, + MONS_UNDYING_ARMOURY, #endif MONS_KILLER_KLOWN, MONS_SHAPESHIFTER, @@ -641,6 +644,7 @@ enum monster_type // env.mons[].type MONS_LIGHTNING_SPIRE, #if TAG_MAJOR_VERSION > 34 MONS_DIAMOND_OBELISK, + MONS_SEISMIC_CANNON, #endif // Demons: @@ -666,7 +670,9 @@ enum monster_type // env.mons[].type MONS_YNOXINUL, MONS_CHAOS_SPAWN, MONS_HELLION, +#if TAG_MAJOR_VERSION == 34 MONS_LOROCYPROCA, +#endif MONS_TORMENTOR, MONS_REAPER, MONS_SOUL_EATER, @@ -674,7 +680,7 @@ enum monster_type // env.mons[].type #if TAG_MAJOR_VERSION == 34 MONS_BLUE_DEVIL, #endif - MONS_HELL_BEAST, + MONS_SIN_BEAST, MONS_RUST_DEVIL, MONS_EXECUTIONER, MONS_GREEN_DEATH, @@ -748,6 +754,7 @@ enum monster_type // env.mons[].type MONS_FLAMING_CORPSE, #else MONS_BLOATED_HUSK, + MONS_MARROWCUDA, #endif MONS_MUMMY, MONS_BOG_BODY, @@ -936,6 +943,7 @@ enum monster_type // env.mons[].type MONS_ZENATA, MONS_GRUNN, MONS_JEREMIAH, + MONS_XAKKRIXIS, #endif // Sprint uniques: MONS_CHUCK, @@ -963,6 +971,7 @@ enum monster_type // env.mons[].type MONS_PILLAR_OF_SALT, #if TAG_MAJOR_VERSION > 34 MONS_BLOCK_OF_ICE, + MONS_PILLAR_OF_RIME, #endif MONS_HELL_LORD, // genus MONS_MERGED_SLIME_CREATURE, // used only for recolouring @@ -1243,6 +1252,13 @@ enum monster_type // env.mons[].type MONS_THERMIC_DYNAMO, MONS_OBSIDIAN_BAT, MONS_PILE_OF_DEBRIS, + MONS_PILLAR_OF_RIME, + MONS_SEISMIC_CANNON, + MONS_ELECTROFERRIC_VORTEX, + MONS_POLTERGUARDIAN, + MONS_MARROWCUDA, + MONS_UNDYING_ARMOURY, + MONS_XAKKRIXIS, #endif NUM_MONSTERS, // used for polymorph diff --git a/crawl-ref/source/monster.cc b/crawl-ref/source/monster.cc index 59958a79665..1c5e50167a1 100644 --- a/crawl-ref/source/monster.cc +++ b/crawl-ref/source/monster.cc @@ -1532,6 +1532,10 @@ bool monster::pickup_melee_weapon(item_def &item, bool msg) bool monster::wants_weapon(const item_def &weap) const { + // Don't swap out undying armoury weapons for anything else. + if (has_ench(ENCH_ARMED)) + return false; + if (!could_wield(weap)) return false; @@ -3163,12 +3167,6 @@ int monster::base_armour_class() const return _zombie_ac_modifier(type) + base_ac; } - // abominations are weird. - if (type == MONS_ABOMINATION_LARGE) - return min(20, 7 + get_hit_dice() / 2); - if (type == MONS_ABOMINATION_SMALL) - return min(10, 3 + get_hit_dice() * 2 / 3); - // Hepliaklqana ancestors scale with xl. if (mons_is_hepliaklqana_ancestor(type)) { @@ -3299,12 +3297,6 @@ int monster::base_evasion() const return _zombie_ev_modifier(type) + base_ev; } - // abominations are weird. - if (type == MONS_ABOMINATION_LARGE) - return min(20, 2 * get_hit_dice() / 3); - if (type == MONS_ABOMINATION_SMALL) - return min(10, 4 + get_hit_dice()); - const int base_ev = get_monster_data(type)->ev; // draconians combine base & class ac values. @@ -3348,8 +3340,11 @@ int monster::evasion(bool ignore_temporary, const actor* /*act*/) const if (ignore_temporary) return max(ev, 0); - if (paralysed() || petrified() || petrifying() || asleep()) + if (paralysed() || petrified() || petrifying() || asleep() + || has_ench(ENCH_MAGNETISED)) + { return 0; + } if (caught()) ev /= 5; @@ -5120,7 +5115,8 @@ bool monster::is_stationary() const bool monster::can_burrow() const { - return mons_class_flag(type, M_BURROWS); + return mons_class_flag(type, M_BURROWS) + && (type == MONS_DISSOLUTION || behaviour != BEH_WANDER); } /** @@ -5253,6 +5249,7 @@ static bool _mons_is_skeletal(int mc) || mc == MONS_WEEPING_SKULL || mc == MONS_LAUGHING_SKULL || mc == MONS_CURSE_SKULL + || mc == MONS_MARROWCUDA || mc == MONS_MURRAY; } @@ -6466,6 +6463,7 @@ bool monster::angered_by_attacks() const && !mons_class_is_zombified(type) && !is_divine_companion() && type != MONS_SPELLFORGED_SERVITOR + && type != MONS_SEISMIC_CANNON && type != MONS_BLOCK_OF_ICE && !mons_is_conjured(type) && !testbits(flags, MF_DEMONIC_GUARDIAN) diff --git a/crawl-ref/source/mutation-data.h b/crawl-ref/source/mutation-data.h index 72c42f40f11..501c7040ebf 100644 --- a/crawl-ref/source/mutation-data.h +++ b/crawl-ref/source/mutation-data.h @@ -785,7 +785,7 @@ static const mutation_def mut_data[] = {"", "", ""}, }, -{ MUT_NO_JEWELLERY, 0, 1, mutflag::bad, true, +{ MUT_NO_JEWELLERY, 0, 1, mutflag::bad, false, "no jewellery", {"You cannot equip rings or amulets.", @@ -2328,7 +2328,7 @@ static const mutation_def mut_data[] = {"You gain the ability to drink.", "", ""}, }, -{ MUT_FAITH, 0, 1, mutflag::bad, false, +{ MUT_FAITH, 0, 1, mutflag::good, false, "faith", {"You have a special connection with the divine. (Faith)", "", ""}, diff --git a/crawl-ref/source/mutation.cc b/crawl-ref/source/mutation.cc index 537184b53c4..01e4b545c2c 100644 --- a/crawl-ref/source/mutation.cc +++ b/crawl-ref/source/mutation.cc @@ -3072,8 +3072,12 @@ static vector _select_ds_mutations() if (m == MUT_FOUL_STENCH || m == MUT_IGNITE_BLOOD) cloud_producing++; - if (m == MUT_SPINY || m == MUT_FOUL_SHADOW) + if (m == MUT_SPINY + || m == MUT_FOUL_STENCH + || m == MUT_FOUL_SHADOW) + { retaliation++; + } } } @@ -3323,8 +3327,7 @@ void check_demonic_guardian() MONS_SIXFIRHY, MONS_SUN_DEMON); break; case 4: - mt = random_choose(MONS_BALRUG, MONS_LOROCYPROCA, - MONS_CACODEMON, MONS_HELL_BEAST); + mt = random_choose(MONS_BALRUG, MONS_CACODEMON, MONS_SIN_BEAST); break; case 5: mt = random_choose(MONS_EXECUTIONER, MONS_HELL_SENTINEL, diff --git a/crawl-ref/source/ng-setup.cc b/crawl-ref/source/ng-setup.cc index 4aa66e9fa5f..3c6b626c310 100644 --- a/crawl-ref/source/ng-setup.cc +++ b/crawl-ref/source/ng-setup.cc @@ -181,6 +181,9 @@ item_def* newgame_make_item(object_class_type base, if (item.base_type == OBJ_MISSILES) _autopickup_ammo(static_cast(item.sub_type)); + if (item.base_type == OBJ_MISCELLANY) + handle_generated_misc(static_cast(item.sub_type)); + origin_set_startequip(item); if (item.base_type == OBJ_WEAPONS && you.species == SP_COGLIN) name_weapon(item); diff --git a/crawl-ref/source/ng-wanderer.cc b/crawl-ref/source/ng-wanderer.cc index 083f0faa874..8ee533c1b27 100644 --- a/crawl-ref/source/ng-wanderer.cc +++ b/crawl-ref/source/ng-wanderer.cc @@ -132,6 +132,7 @@ static void _assign_wanderer_stats(skill_type sk1, skill_type sk2, case SK_FIGHTING: case SK_EVOCATIONS: case SK_THROWING: + case SK_SHAPESHIFTING: if (coinflip()) str_count++; else @@ -202,14 +203,14 @@ static skill_type _wanderer_role_skill_select(bool defense) SK_SUMMONINGS, SK_NECROMANCY, SK_TRANSLOCATIONS, SK_ALCHEMY, SK_CONJURATIONS, SK_HEXES, SK_FIRE_MAGIC, SK_ICE_MAGIC, SK_SPELLCASTING, - SK_AIR_MAGIC, SK_EARTH_MAGIC, SK_FIGHTING }; + SK_AIR_MAGIC, SK_EARTH_MAGIC, SK_SHAPESHIFTING, SK_FIGHTING }; int offense_size = ARRAYSZ(offense_skills); const skill_type physical_skills[] = { SK_AXES, SK_MACES_FLAILS, SK_RANGED_WEAPONS, SK_POLEARMS, SK_SHORT_BLADES, SK_LONG_BLADES, SK_STAVES, SK_UNARMED_COMBAT, - SK_FIGHTING }; + SK_SHAPESHIFTING, SK_FIGHTING }; int physical_size = ARRAYSZ(physical_skills); @@ -271,7 +272,8 @@ static void _setup_starting_skills(skill_type sk1, skill_type sk2, 2, SK_STEALTH, 1 + magical, SK_SPELLCASTING, 2, SK_EVOCATIONS, - 1, SK_INVOCATIONS); + 1, SK_INVOCATIONS, + 2, SK_SHAPESHIFTING); if (!is_useless_skill(selected) && selected != SK_NONE && you.skills[selected] < 5) @@ -463,7 +465,7 @@ static void _decent_potion_or_scroll() { { OBJ_POTIONS, POT_LIGNIFY }, you.is_lifeless_undead(false) ? 0 : 5 }, { { OBJ_POTIONS, POT_ATTRACTION }, - you.is_lifeless_undead(false) ? 0 : 5 }, + you.has_mutation(MUT_NO_DRINK) ? 0 : 5 }, { { OBJ_POTIONS, POT_MUTATION }, you.is_lifeless_undead(false) ? 0 : 1 }, }; @@ -509,6 +511,7 @@ static void _wanderer_random_evokable() case WAND_QUICKSILVER: case WAND_ICEBLAST: case WAND_ROOTS: + case WAND_WARPING: charges = 2 + random2(3); break; @@ -534,6 +537,15 @@ static void _wanderer_random_evokable() } } +// Create a random low-level talisman in the inventory. +static void _wanderer_random_talisman() +{ + talisman_type selected_talisman = + coinflip() ? TALISMAN_BEAST : TALISMAN_FLUX; + + newgame_make_item(OBJ_TALISMANS, selected_talisman); +} + static void _give_wanderer_aux_armour(int plus = 0) { vector auxs = { ARM_HAT, ARM_GLOVES, ARM_BOOTS, ARM_CLOAK }; @@ -588,17 +600,13 @@ static vector _wanderer_good_equipment(skill_type & skill) break; case SK_ARMOUR: - { - item_def * arm; - if (coinflip()) - arm = newgame_make_item(OBJ_ARMOUR, ARM_SCALE_MAIL, 1, 2); - else - arm = newgame_make_item(OBJ_ARMOUR, ARM_CHAIN_MAIL, 1, 0); - // weird body shape, give scales - if (!arm) + if (you_can_wear(EQ_BODY_ARMOUR) != true) newgame_make_item(OBJ_ARMOUR, ARM_ACID_DRAGON_ARMOUR); + else if (coinflip()) + newgame_make_item(OBJ_ARMOUR, ARM_SCALE_MAIL, 1, 2); + else + newgame_make_item(OBJ_ARMOUR, ARM_CHAIN_MAIL, 1, 0); break; - } case SK_DODGING: // +2 leather armour or +0 leather armour and also 2-4 nets @@ -667,6 +675,12 @@ static vector _wanderer_good_equipment(skill_type & skill) // Random wand or xp evoker _wanderer_random_evokable(); break; + + case SK_SHAPESHIFTING: + // Random low-level talisman + _wanderer_random_talisman(); + break; + default: break; } @@ -678,14 +692,18 @@ static vector _wanderer_decent_equipment(skill_type & skill, set & gift_skills) { // don't give a shield if we filled our hands already - if (skill == SK_SHIELDS && (!you.has_usable_offhand() - || gift_skills.count(SK_RANGED_WEAPONS))) + // or if we got a sling (likely to upgrade to a bow later) + // (except for formicids, obviously) + if (!you.has_mutation(MUT_QUADRUMANOUS) + && skill == SK_SHIELDS + && (!you.has_usable_offhand() || gift_skills.count(SK_RANGED_WEAPONS))) { skill = random_choose(SK_DODGING, SK_ARMOUR); } // or two handers if we have a shield (getting a 2h and a bow is ok) - if (gift_skills.count(SK_SHIELDS) + if (!you.has_mutation(MUT_QUADRUMANOUS) + && gift_skills.count(SK_SHIELDS) && (skill == SK_STAVES || skill == SK_RANGED_WEAPONS)) { skill = SK_FIGHTING; @@ -719,15 +737,15 @@ static vector _wanderer_decent_equipment(skill_type & skill, break; case SK_ARMOUR: - item_def * arm; - if (coinflip()) - arm = newgame_make_item(OBJ_ARMOUR, ARM_SCALE_MAIL); - else - arm = newgame_make_item(OBJ_ARMOUR, ARM_RING_MAIL); - // can train but weird shape, skins and scales are too good for decent - // give a plain aux - if (!arm) + // Dragon scales/tla is too good for "decent" quality + // Just give an aux piece to On/Tr/Sp. Note: armour skill will later be + // converted to dodging skill by reassess_starting_skills in this case. + if (you_can_wear(EQ_BODY_ARMOUR) != true) _give_wanderer_aux_armour(); + else if (coinflip()) + newgame_make_item(OBJ_ARMOUR, ARM_RING_MAIL); + else + newgame_make_item(OBJ_ARMOUR, ARM_SCALE_MAIL); break; case SK_SHIELDS: @@ -752,6 +770,10 @@ static vector _wanderer_decent_equipment(skill_type & skill, 1, 3 + random2(5)); break; + case SK_SHAPESHIFTING: + newgame_make_item(OBJ_TALISMANS, TALISMAN_BEAST); + break; + case SK_STEALTH: case SK_DODGING: _give_wanderer_aux_armour(); @@ -786,7 +808,8 @@ static void _wanderer_cover_equip_holes() you.strength() > you.intel() ? ARM_LEATHER_ARMOUR : ARM_ROBE); } - if (you.equip[EQ_WEAPON] == -1) + // Give weapon, unless we started with some unarmed skill + if (you.equip[EQ_WEAPON] == -1 && you.skills[SK_UNARMED_COMBAT] == 0) { newgame_make_item(OBJ_WEAPONS, you.dex() > you.strength() ? WPN_DAGGER : WPN_CLUB); diff --git a/crawl-ref/source/output.cc b/crawl-ref/source/output.cc index 5a167c79fcc..49fa7b22c87 100644 --- a/crawl-ref/source/output.cc +++ b/crawl-ref/source/output.cc @@ -2529,17 +2529,20 @@ static vector _get_overview_resistances( out += chop_string("HPRegen", cwidth); out += make_stringf("%d.%02d/turn\n", regen/100, regen%100); - out += chop_string("MPRegen", cwidth); + if (!you.has_mutation(MUT_HP_CASTING)) + { + out += chop_string("MPRegen", cwidth); #if TAG_MAJOR_VERSION == 34 - const bool etheric = player_equip_unrand(UNRAND_ETHERIC_CAGE); - const int mp_regen = player_mp_regen() //round up - + (etheric ? 50 : 0); // on average - out += make_stringf("%d.%02d/turn%s\n", mp_regen / 100, mp_regen % 100, - etheric ? "*" : ""); + const bool etheric = player_equip_unrand(UNRAND_ETHERIC_CAGE); + const int mp_regen = player_mp_regen() //round up + + (etheric ? 50 : 0); // on average + out += make_stringf("%d.%02d/turn%s\n", mp_regen / 100, mp_regen % 100, + etheric ? "*" : ""); #else - const int mp_regen = player_mp_regen(); // round up - out += make_stringf("%d.%02d/turn\n", mp_regen / 100, mp_regen % 100); + const int mp_regen = player_mp_regen(); // round up + out += make_stringf("%d.%02d/turn\n", mp_regen / 100, mp_regen % 100); #endif + } cols.add_formatted(0, out, false); diff --git a/crawl-ref/source/player-reacts.cc b/crawl-ref/source/player-reacts.cc index 373ba134caa..a2f3c3cd07a 100644 --- a/crawl-ref/source/player-reacts.cc +++ b/crawl-ref/source/player-reacts.cc @@ -134,6 +134,9 @@ static bool _decrement_a_duration(duration_type dur, int delay, "expiration delay loss %d not less than duration expiration point %d", exploss * BASELINE_DELAY, exppoint); + if (dur == DUR_SENTINEL_MARK && aura_is_active_on_player(OPHAN_MARK_KEY)) + return false; + const int old_dur = you.duration[dur]; you.duration[dur] -= delay; @@ -887,6 +890,9 @@ static void _decrement_durations() if (you.duration[DUR_BLOOD_FOR_BLOOD]) beogh_blood_for_blood_tick(delay); + if (you.duration[DUR_FUSILLADE] && you.time_taken > 0) + fire_fusillade(); + // these should be after decr_ambrosia, transforms, liquefying, etc. for (int i = 0; i < NUM_DURATIONS; ++i) if (duration_decrements_normally((duration_type) i)) diff --git a/crawl-ref/source/player.cc b/crawl-ref/source/player.cc index e37c31dca42..b08816e9600 100644 --- a/crawl-ref/source/player.cc +++ b/crawl-ref/source/player.cc @@ -4880,13 +4880,11 @@ void dec_slow_player(int delay) ? haste_mul(delay) : delay; } - if (you.torpor_slowed()) + if (aura_is_active_on_player(TORPOR_SLOWED_KEY)) { you.duration[DUR_SLOW] = max(1, you.duration[DUR_SLOW]); return; } - if (you.props.exists(TORPOR_SLOWED_KEY)) - you.props.erase(TORPOR_SLOWED_KEY); if (you.duration[DUR_SLOW] <= BASELINE_DELAY) { @@ -6557,7 +6555,7 @@ void player::refresh_rampage_hints() **/ int player::gdr_perc() const { - return max(0, (int)(16 * sqrt(sqrt(you.armour_class())))); + return (int)(16 * sqrt(sqrt(max(0, you.armour_class())))); } /** @@ -8686,7 +8684,7 @@ void player_close_door(coord_def doorpos) if (monster* mon = monster_at(dc)) { const bool mons_unseen = !you.can_see(*mon); - if (mons_unseen || mons_is_object(mon->type)) + if (mons_unseen || (mon->holiness() & MH_NONLIVING)) { mprf("Something is blocking the %s!", waynoun); // No free detection! diff --git a/crawl-ref/source/quiver.cc b/crawl-ref/source/quiver.cc index 2face8dbe8f..481b6535232 100644 --- a/crawl-ref/source/quiver.cc +++ b/crawl-ref/source/quiver.cc @@ -1071,6 +1071,7 @@ namespace quiver // that are clutched, and spell // targeting handles this case. case SPELL_APPORTATION: // Apport doesn't target monsters at all + case SPELL_MAGNAVOLT: return true; default: return _spell_needs_manual_targeting(s); diff --git a/crawl-ref/source/ranged-attack.cc b/crawl-ref/source/ranged-attack.cc index dce8476b891..23ab1b9d4db 100644 --- a/crawl-ref/source/ranged-attack.cc +++ b/crawl-ref/source/ranged-attack.cc @@ -694,23 +694,16 @@ bool ranged_attack::apply_missile_brand() break; } - coord_def pos, pos2; - const bool no_sanct = defender->kill_alignment() == KC_OTHER; - if (!random_near_space(defender, defender->pos(), pos, false, - no_sanct) - || !random_near_space(defender, defender->pos(), pos2, false, - no_sanct)) + if (defender->is_player()) { - break; + if (attacker->is_monster()) + blink_player_away(attacker->as_monster()); + // Specifically to handle reflected darts shot by the player + else + you.blink(); } - const coord_def from = attacker->pos(); - if (grid_distance(pos2, from) > grid_distance(pos, from)) - pos = pos2; - - if (defender->is_player()) - defender->blink_to(pos); else - defender->as_monster()->blink_to(pos, false, false); + blink_away(defender->as_monster(), attacker); break; } case SPMSL_SILVER: diff --git a/crawl-ref/source/religion.cc b/crawl-ref/source/religion.cc index e3b84b386f3..239a4527961 100644 --- a/crawl-ref/source/religion.cc +++ b/crawl-ref/source/religion.cc @@ -1011,6 +1011,7 @@ static const vector> _yred_servants = { { -2, 5, 80, PEAK, MONS_NECROPHAGE }, { -1, 7, 75, PEAK, MONS_PHANTOM }, + { 2, 9, 70, SEMI, MONS_MARROWCUDA }, { 4, 11, 145, SEMI, MONS_WIGHT }, { 6, 13, 90, SEMI, MONS_SHADOW }, { 8, 15, 110, SEMI, MONS_WRAITH }, @@ -1487,7 +1488,8 @@ static bool _give_kiku_gift(bool forced) SPELL_DISPEL_UNDEAD, SPELL_CURSE_OF_AGONY, SPELL_BORGNJORS_VILE_CLUTCH, - SPELL_DEATH_CHANNEL}; + SPELL_DEATH_CHANNEL, + SPELL_RIMEBLIGHT}; } shuffle_array(spell_options); diff --git a/crawl-ref/source/rltiles/item/misc/misc_box.png b/crawl-ref/source/rltiles/UNUSED/misc_box.png similarity index 100% rename from crawl-ref/source/rltiles/item/misc/misc_box.png rename to crawl-ref/source/rltiles/UNUSED/misc_box.png diff --git a/crawl-ref/source/rltiles/mon/demons/lorocyproca.png b/crawl-ref/source/rltiles/UNUSED/monsters/lorocyproca.png similarity index 100% rename from crawl-ref/source/rltiles/mon/demons/lorocyproca.png rename to crawl-ref/source/rltiles/UNUSED/monsters/lorocyproca.png diff --git a/crawl-ref/source/rltiles/dc-icons.txt b/crawl-ref/source/rltiles/dc-icons.txt index a9c4e14fce8..63181f938d0 100644 --- a/crawl-ref/source/rltiles/dc-icons.txt +++ b/crawl-ref/source/rltiles/dc-icons.txt @@ -87,6 +87,8 @@ regeneration REGENERATION retreat RETREAT vengeance VENGEANCE_TARGET beogh_touch TOUCH_OF_BEOGH +rimeblight RIMEBLIGHT +arms UNDYING_ARMS something_under SOMETHING_UNDER new_stair NEW_STAIR diff --git a/crawl-ref/source/rltiles/dc-item.txt b/crawl-ref/source/rltiles/dc-item.txt index c824617103f..ee7f11b5183 100644 --- a/crawl-ref/source/rltiles/dc-item.txt +++ b/crawl-ref/source/rltiles/dc-item.txt @@ -852,7 +852,7 @@ citrine bone platinum jade -flourescent +fluorescent amethyst cameo pearl @@ -1158,13 +1158,15 @@ misc_vane_inert MISC_CONDENSER_VANE_INERT misc_lantern MISC_LANTERN_OF_SHADOWS # end TAG_MAJOR_VERSION misc_horn MISC_HORN_OF_GERYON -misc_box MISC_BOX_OF_BEASTS +misc_box_of_beasts MISC_BOX_OF_BEASTS +misc_box_of_beasts_inert MISC_BOX_OF_BEASTS_INERT %rim 0 misc_sack MISC_SACK_OF_SPIDERS misc_phantom_mirror MISC_PHANTOM_MIRROR misc_zigfig MISC_ZIGGURAT misc_quad MISC_QUAD_DAMAGE misc_tambourine MISC_TAMBOURINE +misc_tambourine_inert MISC_TAMBOURINE_INERT %sdir item/misc/runes generic MISC_RUNE_OF_ZOT rune_tartarus RUNE_TARTARUS diff --git a/crawl-ref/source/rltiles/dc-mon.txt b/crawl-ref/source/rltiles/dc-mon.txt index 8a8ab00ea22..cf9d29ac137 100644 --- a/crawl-ref/source/rltiles/dc-mon.txt +++ b/crawl-ref/source/rltiles/dc-mon.txt @@ -243,6 +243,8 @@ boulder_beetle MONS_BOULDER_BEETLE boulder_beetle_rolling0 MONS_BOULDER_BEETLE_ROLLING boulder_beetle_rolling1 boulder_beetle_rolling2 +%sdir mon/unique +xakkrixis MONS_XAKKRIXIS ## Frogs and flying fish ('F') %sdir mon/demihumanoids @@ -259,8 +261,11 @@ blinking_frog MONS_BLINK_FROG_BLINKING cane_toad MONS_CANE_TOAD goliath_frog MONS_GOLIATH_FROG skyshark MONS_SKYSHARK +%sdir mon/undead +marrowcuda MONS_MARROWCUDA ## Snakes ('S') +%sdir mon/animals ball_python MONS_BALL_PYTHON adder MONS_ADDER black_mamba MONS_BLACK_MAMBA @@ -309,6 +314,10 @@ fire_vortex1 MONS_FIRE_VORTEX /*'v'*/ fire_vortex2 fire_vortex3 fire_vortex4 +electroferric_vortex1 MONS_ELECTROFERRIC_VORTEX /*'v'*/ +electroferric_vortex2 +electroferric_vortex3 +electroferric_vortex4 thermic_dynamo1 MONS_THERMIC_DYNAMO /*'v'*/ thermic_dynamo2 thermic_dynamo3 @@ -341,6 +350,9 @@ living_spell MONS_LIVING_SPELL %rim 1 ball_lightning MONS_BALL_LIGHTNING /*'*'*/ creeping_inferno MONS_CREEPING_INFERNO +%sdir mon/undead +polterguardian MONS_POLTERGUARDIAN +undying_armoury MONS_UNDYING_ARMOURY ### Abyssal horrors ('*') %sdir mon/abyss @@ -379,9 +391,8 @@ ignacio MONS_IGNACIO %sdir mon/demons # type '2' demons -hell_beast MONS_HELL_BEAST +sin_beast MONS_SIN_BEAST ice_devil MONS_ICE_DEVIL -lorocyproca MONS_LOROCYPROCA reaper MONS_REAPER soul_eater MONS_SOUL_EATER sun_demon MONS_SUN_DEMON @@ -531,12 +542,14 @@ pile_of_debris_00 MONS_PILE_OF_DEBRIS pile_of_debris_01 pillar_of_salt MONS_PILLAR_OF_SALT block_of_ice3 MONS_BLOCK_OF_ICE +rime_pillar MONS_PILLAR_OF_RIME training_dummy MONS_TRAINING_DUMMY ice_statue MONS_ICE_STATUE obsidian_statue MONS_OBSIDIAN_STATUE orange_crystal_statue MONS_ORANGE_STATUE wucad_mu_statue MONS_WUCAD_MU_STATUE machine_tukima MONS_STRANGE_MACHINE +seismic_cannon MONS_SEISMIC_CANNON %sdir mon/vault air_elementalist_statue MONS_AIR_ELEMENTALIST_STATUE earth_elementalist_statue MONS_EARTH_ELEMENTALIST_STATUE diff --git a/crawl-ref/source/rltiles/dc-spells.txt b/crawl-ref/source/rltiles/dc-spells.txt index eafead3fc66..299a9333aa9 100644 --- a/crawl-ref/source/rltiles/dc-spells.txt +++ b/crawl-ref/source/rltiles/dc-spells.txt @@ -4,6 +4,7 @@ chain_lightning CHAIN_LIGHTNING conjure_ball_lightning CONJURE_BALL_LIGHTNING lightning_bolt LIGHTNING_BOLT arcjolt ARCJOLT +magnavolt MAGNAVOLT maxwells MAXWELLS_COUPLING repel_missiles REPEL_MISSILES shock SHOCK @@ -89,6 +90,7 @@ permafrost_eruption PERMAFROST_ERUPTION throw_frost THROW_FROST throw_icicle THROW_ICICLE polar_vortex POLAR_VORTEX +rimeblight RIMEBLIGHT %sdir gui/spells/necromancy agony AGONY @@ -115,7 +117,7 @@ sublimation_of_blood SUBLIMATION_OF_BLOOD symbol_of_torment SYMBOL_OF_TORMENT vampiric_draining VAMPIRIC_DRAINING -%sdir gui/spells/poison +%sdir gui/spells/alchemy alistairs_intoxication ALISTAIRS_INTOXICATION mephitic_cloud MEPHITIC_CLOUD olgrebs_toxic_radiance OLGREBS_TOXIC_RADIANCE @@ -124,8 +126,10 @@ poisonous_cloud POISONOUS_CLOUD spider_form SPIDER_FORM sting STING venom_bolt VENOM_BOLT -poisonous_vapours POISONOUS_VAPOURS +mercury_vapours MERCURY_VAPOURS noxious_bog NOXIOUS_BOG +fulsome_fusillade FULSOME_FUSILLADE +seismic_cannonade SEISMIC_CANNONADE %sdir gui/spells/summoning animate_armour ANIMATE_ARMOUR @@ -253,7 +257,7 @@ summon_drakes SUMMON_DRAKES summon_eyeballs SUMMON_EYEBALLS summon_emperor_scorpions SUMMON_EMPEROR_SCORPIONS summon_executioners SUMMON_EXECUTIONERS -summon_hell_beast SUMMON_HELL_BEAST +summon_sin_beast SUMMON_SIN_BEAST summon_hell_sentinel SUMMON_HELL_SENTINEL summon_holies SUMMON_HOLIES summon_illusion SUMMON_ILLUSION diff --git a/crawl-ref/source/rltiles/gui/invocations/yred_recall.png b/crawl-ref/source/rltiles/gui/invocations/yred_recall.png index d76b242992a..2ffdd92d266 100644 Binary files a/crawl-ref/source/rltiles/gui/invocations/yred_recall.png and b/crawl-ref/source/rltiles/gui/invocations/yred_recall.png differ diff --git a/crawl-ref/source/rltiles/gui/spells/air/magnavolt.png b/crawl-ref/source/rltiles/gui/spells/air/magnavolt.png new file mode 100644 index 00000000000..25c48550cc4 Binary files /dev/null and b/crawl-ref/source/rltiles/gui/spells/air/magnavolt.png differ diff --git a/crawl-ref/source/rltiles/gui/spells/poison/alistairs_intoxication.png b/crawl-ref/source/rltiles/gui/spells/alchemy/alistairs_intoxication.png similarity index 100% rename from crawl-ref/source/rltiles/gui/spells/poison/alistairs_intoxication.png rename to crawl-ref/source/rltiles/gui/spells/alchemy/alistairs_intoxication.png diff --git a/crawl-ref/source/rltiles/gui/spells/alchemy/fulsome_fusillade.png b/crawl-ref/source/rltiles/gui/spells/alchemy/fulsome_fusillade.png new file mode 100644 index 00000000000..eebfe93da2d Binary files /dev/null and b/crawl-ref/source/rltiles/gui/spells/alchemy/fulsome_fusillade.png differ diff --git a/crawl-ref/source/rltiles/gui/spells/poison/mephitic_cloud.png b/crawl-ref/source/rltiles/gui/spells/alchemy/mephitic_cloud.png similarity index 100% rename from crawl-ref/source/rltiles/gui/spells/poison/mephitic_cloud.png rename to crawl-ref/source/rltiles/gui/spells/alchemy/mephitic_cloud.png diff --git a/crawl-ref/source/rltiles/gui/spells/alchemy/mercury_vapours.png b/crawl-ref/source/rltiles/gui/spells/alchemy/mercury_vapours.png new file mode 100644 index 00000000000..4125be8fabf Binary files /dev/null and b/crawl-ref/source/rltiles/gui/spells/alchemy/mercury_vapours.png differ diff --git a/crawl-ref/source/rltiles/gui/spells/poison/noxious_bog.png b/crawl-ref/source/rltiles/gui/spells/alchemy/noxious_bog.png similarity index 100% rename from crawl-ref/source/rltiles/gui/spells/poison/noxious_bog.png rename to crawl-ref/source/rltiles/gui/spells/alchemy/noxious_bog.png diff --git a/crawl-ref/source/rltiles/gui/spells/poison/olgrebs_toxic_radiance.png b/crawl-ref/source/rltiles/gui/spells/alchemy/olgrebs_toxic_radiance.png similarity index 100% rename from crawl-ref/source/rltiles/gui/spells/poison/olgrebs_toxic_radiance.png rename to crawl-ref/source/rltiles/gui/spells/alchemy/olgrebs_toxic_radiance.png diff --git a/crawl-ref/source/rltiles/gui/spells/poison/poison_arrow.png b/crawl-ref/source/rltiles/gui/spells/alchemy/poison_arrow.png similarity index 100% rename from crawl-ref/source/rltiles/gui/spells/poison/poison_arrow.png rename to crawl-ref/source/rltiles/gui/spells/alchemy/poison_arrow.png diff --git a/crawl-ref/source/rltiles/gui/spells/poison/poisonous_cloud.png b/crawl-ref/source/rltiles/gui/spells/alchemy/poisonous_cloud.png similarity index 100% rename from crawl-ref/source/rltiles/gui/spells/poison/poisonous_cloud.png rename to crawl-ref/source/rltiles/gui/spells/alchemy/poisonous_cloud.png diff --git a/crawl-ref/source/rltiles/UNUSED/spells/forceful_dismissal.png b/crawl-ref/source/rltiles/gui/spells/alchemy/seismic_cannonade.png similarity index 100% rename from crawl-ref/source/rltiles/UNUSED/spells/forceful_dismissal.png rename to crawl-ref/source/rltiles/gui/spells/alchemy/seismic_cannonade.png diff --git a/crawl-ref/source/rltiles/gui/spells/poison/spider_form.png b/crawl-ref/source/rltiles/gui/spells/alchemy/spider_form.png similarity index 100% rename from crawl-ref/source/rltiles/gui/spells/poison/spider_form.png rename to crawl-ref/source/rltiles/gui/spells/alchemy/spider_form.png diff --git a/crawl-ref/source/rltiles/gui/spells/poison/sting.png b/crawl-ref/source/rltiles/gui/spells/alchemy/sting.png similarity index 100% rename from crawl-ref/source/rltiles/gui/spells/poison/sting.png rename to crawl-ref/source/rltiles/gui/spells/alchemy/sting.png diff --git a/crawl-ref/source/rltiles/gui/spells/poison/venom_bolt.png b/crawl-ref/source/rltiles/gui/spells/alchemy/venom_bolt.png similarity index 100% rename from crawl-ref/source/rltiles/gui/spells/poison/venom_bolt.png rename to crawl-ref/source/rltiles/gui/spells/alchemy/venom_bolt.png diff --git a/crawl-ref/source/rltiles/gui/spells/ice/rimeblight.png b/crawl-ref/source/rltiles/gui/spells/ice/rimeblight.png new file mode 100644 index 00000000000..6aadd4b0d81 Binary files /dev/null and b/crawl-ref/source/rltiles/gui/spells/ice/rimeblight.png differ diff --git a/crawl-ref/source/rltiles/gui/spells/monster/summon_hell_beast.png b/crawl-ref/source/rltiles/gui/spells/monster/summon_hell_beast.png deleted file mode 100644 index 5e3f85f4d4d..00000000000 Binary files a/crawl-ref/source/rltiles/gui/spells/monster/summon_hell_beast.png and /dev/null differ diff --git a/crawl-ref/source/rltiles/gui/spells/monster/summon_sin_beast.png b/crawl-ref/source/rltiles/gui/spells/monster/summon_sin_beast.png new file mode 100644 index 00000000000..1e3a2ecda21 Binary files /dev/null and b/crawl-ref/source/rltiles/gui/spells/monster/summon_sin_beast.png differ diff --git a/crawl-ref/source/rltiles/gui/spells/poison/poisonous_vapours.png b/crawl-ref/source/rltiles/gui/spells/poison/poisonous_vapours.png deleted file mode 100644 index 5bd707dffdf..00000000000 Binary files a/crawl-ref/source/rltiles/gui/spells/poison/poisonous_vapours.png and /dev/null differ diff --git a/crawl-ref/source/rltiles/item/amulet/artefact/urand_amulet_invisibility.png b/crawl-ref/source/rltiles/item/amulet/artefact/urand_amulet_invisibility.png new file mode 100644 index 00000000000..c1046164454 Binary files /dev/null and b/crawl-ref/source/rltiles/item/amulet/artefact/urand_amulet_invisibility.png differ diff --git a/crawl-ref/source/rltiles/item/amulet/flourescent.png b/crawl-ref/source/rltiles/item/amulet/fluorescent.png similarity index 100% rename from crawl-ref/source/rltiles/item/amulet/flourescent.png rename to crawl-ref/source/rltiles/item/amulet/fluorescent.png diff --git a/crawl-ref/source/rltiles/item/misc/misc_box_of_beasts.png b/crawl-ref/source/rltiles/item/misc/misc_box_of_beasts.png new file mode 100644 index 00000000000..a043912e06d Binary files /dev/null and b/crawl-ref/source/rltiles/item/misc/misc_box_of_beasts.png differ diff --git a/crawl-ref/source/rltiles/item/misc/misc_box_of_beasts_inert.png b/crawl-ref/source/rltiles/item/misc/misc_box_of_beasts_inert.png new file mode 100644 index 00000000000..2330c396060 Binary files /dev/null and b/crawl-ref/source/rltiles/item/misc/misc_box_of_beasts_inert.png differ diff --git a/crawl-ref/source/rltiles/item/misc/misc_tambourine.png b/crawl-ref/source/rltiles/item/misc/misc_tambourine.png index f4e0bf2b8d9..e3855925ebb 100644 Binary files a/crawl-ref/source/rltiles/item/misc/misc_tambourine.png and b/crawl-ref/source/rltiles/item/misc/misc_tambourine.png differ diff --git a/crawl-ref/source/rltiles/item/misc/misc_tambourine_inert.png b/crawl-ref/source/rltiles/item/misc/misc_tambourine_inert.png new file mode 100644 index 00000000000..aa3e4522e3b Binary files /dev/null and b/crawl-ref/source/rltiles/item/misc/misc_tambourine_inert.png differ diff --git a/crawl-ref/source/rltiles/misc/icons/arms.png b/crawl-ref/source/rltiles/misc/icons/arms.png new file mode 100644 index 00000000000..f807f42c9dc Binary files /dev/null and b/crawl-ref/source/rltiles/misc/icons/arms.png differ diff --git a/crawl-ref/source/rltiles/misc/icons/rimeblight.png b/crawl-ref/source/rltiles/misc/icons/rimeblight.png new file mode 100644 index 00000000000..9e380334401 Binary files /dev/null and b/crawl-ref/source/rltiles/misc/icons/rimeblight.png differ diff --git a/crawl-ref/source/rltiles/mon/demons/hell_beast.png b/crawl-ref/source/rltiles/mon/demons/hell_beast.png deleted file mode 100644 index 1967f1e4a69..00000000000 Binary files a/crawl-ref/source/rltiles/mon/demons/hell_beast.png and /dev/null differ diff --git a/crawl-ref/source/rltiles/mon/demons/sin_beast.png b/crawl-ref/source/rltiles/mon/demons/sin_beast.png new file mode 100644 index 00000000000..0151bf3c598 Binary files /dev/null and b/crawl-ref/source/rltiles/mon/demons/sin_beast.png differ diff --git a/crawl-ref/source/rltiles/mon/nonliving/electroferric_vortex1.png b/crawl-ref/source/rltiles/mon/nonliving/electroferric_vortex1.png new file mode 100644 index 00000000000..4eed8d75827 Binary files /dev/null and b/crawl-ref/source/rltiles/mon/nonliving/electroferric_vortex1.png differ diff --git a/crawl-ref/source/rltiles/mon/nonliving/electroferric_vortex2.png b/crawl-ref/source/rltiles/mon/nonliving/electroferric_vortex2.png new file mode 100644 index 00000000000..e6040da448d Binary files /dev/null and b/crawl-ref/source/rltiles/mon/nonliving/electroferric_vortex2.png differ diff --git a/crawl-ref/source/rltiles/mon/nonliving/electroferric_vortex3.png b/crawl-ref/source/rltiles/mon/nonliving/electroferric_vortex3.png new file mode 100644 index 00000000000..03f60f2eebc Binary files /dev/null and b/crawl-ref/source/rltiles/mon/nonliving/electroferric_vortex3.png differ diff --git a/crawl-ref/source/rltiles/mon/nonliving/electroferric_vortex4.png b/crawl-ref/source/rltiles/mon/nonliving/electroferric_vortex4.png new file mode 100644 index 00000000000..774cbcab821 Binary files /dev/null and b/crawl-ref/source/rltiles/mon/nonliving/electroferric_vortex4.png differ diff --git a/crawl-ref/source/rltiles/mon/statues/rime_pillar.png b/crawl-ref/source/rltiles/mon/statues/rime_pillar.png new file mode 100644 index 00000000000..086239f25a2 Binary files /dev/null and b/crawl-ref/source/rltiles/mon/statues/rime_pillar.png differ diff --git a/crawl-ref/source/rltiles/mon/statues/seismic_cannon.png b/crawl-ref/source/rltiles/mon/statues/seismic_cannon.png new file mode 100644 index 00000000000..88a9285a0b5 Binary files /dev/null and b/crawl-ref/source/rltiles/mon/statues/seismic_cannon.png differ diff --git a/crawl-ref/source/rltiles/mon/undead/marrowcuda.png b/crawl-ref/source/rltiles/mon/undead/marrowcuda.png new file mode 100644 index 00000000000..ee1e2d371b9 Binary files /dev/null and b/crawl-ref/source/rltiles/mon/undead/marrowcuda.png differ diff --git a/crawl-ref/source/rltiles/mon/undead/polterguardian.png b/crawl-ref/source/rltiles/mon/undead/polterguardian.png new file mode 100644 index 00000000000..e9b40ff17d7 Binary files /dev/null and b/crawl-ref/source/rltiles/mon/undead/polterguardian.png differ diff --git a/crawl-ref/source/rltiles/UNUSED/monsters/grand_avatar.png b/crawl-ref/source/rltiles/mon/undead/undying_armoury.png similarity index 100% rename from crawl-ref/source/rltiles/UNUSED/monsters/grand_avatar.png rename to crawl-ref/source/rltiles/mon/undead/undying_armoury.png diff --git a/crawl-ref/source/rltiles/UNUSED/monsters/formicid_venom_mage.png b/crawl-ref/source/rltiles/mon/unique/xakkrixis.png similarity index 100% rename from crawl-ref/source/rltiles/UNUSED/monsters/formicid_venom_mage.png rename to crawl-ref/source/rltiles/mon/unique/xakkrixis.png diff --git a/crawl-ref/source/rltiles/player/hand1/artefact/order.png b/crawl-ref/source/rltiles/player/hand1/artefact/order.png index 32d30cc8cbf..dc2a9e07f8b 100644 Binary files a/crawl-ref/source/rltiles/player/hand1/artefact/order.png and b/crawl-ref/source/rltiles/player/hand1/artefact/order.png differ diff --git a/crawl-ref/source/rltiles/player/hand1/lajatang1.png b/crawl-ref/source/rltiles/player/hand1/lajatang1.png index 54fce99f7a7..67afaa0c914 100644 Binary files a/crawl-ref/source/rltiles/player/hand1/lajatang1.png and b/crawl-ref/source/rltiles/player/hand1/lajatang1.png differ diff --git a/crawl-ref/source/rltiles/player/hand1/lajatang2.png b/crawl-ref/source/rltiles/player/hand1/lajatang2.png index 46cc1fd2038..38a77833f2f 100644 Binary files a/crawl-ref/source/rltiles/player/hand1/lajatang2.png and b/crawl-ref/source/rltiles/player/hand1/lajatang2.png differ diff --git a/crawl-ref/source/rltiles/player/hand1/lajatang3.png b/crawl-ref/source/rltiles/player/hand1/lajatang3.png index e34f271c60c..21f028c7d8c 100644 Binary files a/crawl-ref/source/rltiles/player/hand1/lajatang3.png and b/crawl-ref/source/rltiles/player/hand1/lajatang3.png differ diff --git a/crawl-ref/source/skills.cc b/crawl-ref/source/skills.cc index 4228d1f9ef4..411cf22d928 100644 --- a/crawl-ref/source/skills.cc +++ b/crawl-ref/source/skills.cc @@ -265,12 +265,18 @@ void reassess_starting_skills() you.skill_points[sk] = you.skills[sk] ? skill_exp_needed(you.skills[sk], sk, SP_HUMAN) + 1 : 0; + item_def* current_armour = you.slot_item(EQ_BODY_ARMOUR); + + // No one who can't wear mundane heavy armour should start with + // the Armour skill -- D:1 dragon armour is too unlikely. + // However, specifically except the special case of Sp/Tr/On + // wanderers starting with acid dragon scales. if (sk == SK_DODGING && you.skills[SK_ARMOUR] && (is_useless_skill(SK_ARMOUR) - || you_can_wear(EQ_BODY_ARMOUR) != true)) + || you_can_wear(EQ_BODY_ARMOUR) != true) + && !(current_armour + && current_armour->sub_type == ARM_ACID_DRAGON_ARMOUR)) { - // No one who can't wear mundane heavy armour should start with - // the Armour skill -- D:1 dragon armour is too unlikely. you.skill_points[sk] += skill_exp_needed(you.skills[SK_ARMOUR], SK_ARMOUR, SP_HUMAN) + 1; you.skills[SK_ARMOUR] = 0; diff --git a/crawl-ref/source/spell-type.h b/crawl-ref/source/spell-type.h index f8e28e46924..acf34596795 100644 --- a/crawl-ref/source/spell-type.h +++ b/crawl-ref/source/spell-type.h @@ -208,7 +208,7 @@ enum spell_type : int #endif SPELL_STEAM_BALL, SPELL_SUMMON_UFETUBUS, - SPELL_SUMMON_HELL_BEAST, + SPELL_SUMMON_SIN_BEAST, SPELL_BOLT_OF_DEVASTATION, SPELL_SPIT_POISON, SPELL_SUMMON_UNDEAD, @@ -464,7 +464,7 @@ enum spell_type : int #if TAG_MAJOR_VERSION == 34 SPELL_AWAKEN_EARTH, #endif - SPELL_AURA_OF_BRILLIANCE, + SPELL_PRAYER_OF_BRILLIANCE, SPELL_ICEBLAST, SPELL_SLUG_DART, SPELL_SPRINT, @@ -578,5 +578,13 @@ enum spell_type : int SPELL_PERMAFROST_ERUPTION, SPELL_PILEDRIVER, SPELL_GELLS_GAVOTTE, + SPELL_MAGNAVOLT, + SPELL_FULSOME_FUSILLADE, + SPELL_RIMEBLIGHT, + SPELL_SEISMIC_CANNONADE, + SPELL_SEISMIC_SHOCKWAVE, // Activated component of Seismic Cannonade + SPELL_STONE_BULLET, + SPELL_BESTOW_ARMS, + SPELL_FLASHING_BALESTRA, NUM_SPELLS }; diff --git a/crawl-ref/source/spl-book.cc b/crawl-ref/source/spl-book.cc index 547ac36beff..7960f39c406 100644 --- a/crawl-ref/source/spl-book.cc +++ b/crawl-ref/source/spl-book.cc @@ -244,7 +244,7 @@ static unordered_set _player_nonbook_spells = SPELL_HURL_TORCHLIGHT, // Ds powers SPELL_HURL_DAMNATION, - // Green Draconian breath + // Draconian breaths SPELL_NOXIOUS_BREATH, SPELL_COMBUSTION_BREATH, SPELL_GLACIAL_BREATH, @@ -253,6 +253,8 @@ static unordered_set _player_nonbook_spells = SPELL_CAUSTIC_BREATH, SPELL_GALVANIC_BREATH, SPELL_MUD_BREATH, + // Activate component of Seismic Cannonade + SPELL_SEISMIC_SHOCKWAVE, }; bool is_player_spell(spell_type which_spell) diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc index ac1c219c20a..dfab2c009ee 100644 --- a/crawl-ref/source/spl-cast.cc +++ b/crawl-ref/source/spl-cast.cc @@ -1398,6 +1398,12 @@ unique_ptr find_spell_targeter(spell_type spell, int pow, int range) case SPELL_GELLS_GAVOTTE: return make_unique(&you); + case SPELL_MAGNAVOLT: + return make_unique(&you, range); + + case SPELL_SEISMIC_SHOCKWAVE: + return make_unique(&you, range); + default: break; } @@ -1656,6 +1662,16 @@ static vector _desc_vampiric_draining_valid(const monster_info& mi) return vector{}; } +static vector _desc_rimeblight_valid(const monster_info& mi) +{ + if (mi.is(MB_RIMEBLIGHT)) + return vector{"already infected"}; + else if (!(mi.holi & (MH_NATURAL | MH_DEMONIC | MH_HOLY))) + return vector{"not susceptible"}; + + return vector{}; +} + static vector _desc_dispersal_chance(const monster_info& mi, int pow) { const int wl = mi.willpower(); @@ -1824,6 +1840,8 @@ desc_filter targeter_addl_desc(spell_type spell, int powc, spell_flags flags, return bind(_desc_meph_chance, placeholders::_1); case SPELL_VAMPIRIC_DRAINING: return bind(_desc_vampiric_draining_valid, placeholders::_1); + case SPELL_RIMEBLIGHT: + return bind(_desc_rimeblight_valid, placeholders::_1); case SPELL_STARBURST: { targeter_starburst* burst_hitf = @@ -1931,6 +1949,13 @@ spret your_spells(spell_type spell, int powc, bool actual_spell, if (!wiz_cast && _spellcasting_aborted(spell, !actual_spell)) return spret::abort; + // XXX: The second mode of cannonade is implemented as a separate spell to + // prevent various buggy behavior regarding one mode bein aimed and + // the other not. To avoid *other* issues, like miscasts happening + // twice, switch which spell we are actually performing at this point. + if (spell == SPELL_SEISMIC_CANNONADE && cannonade_is_fully_charged(you)) + spell = SPELL_SEISMIC_SHOCKWAVE; + const spell_flags flags = get_spell_flags(spell); if (!powc) @@ -2013,6 +2038,9 @@ spret your_spells(spell_type spell, int powc, bool actual_spell, args.top_prompt = title; args.behaviour = &beh; + if (testbits(flags, spflag::prefer_farthest)) + args.prefer_farthest = true; + // if the spell is useless and we have somehow gotten this far, it's // a forced cast. Setting this prevents the direction chooser from // looking for selecting a default target (which doesn't factor in @@ -2563,6 +2591,18 @@ static spret _do_cast(spell_type spell, int powc, const dist& spd, case SPELL_GELLS_GAVOTTE: return cast_gavotte(powc, beam.target - you.pos(), fail); + case SPELL_MAGNAVOLT: + return cast_magnavolt(beam.target, powc, fail); + + case SPELL_FULSOME_FUSILLADE: + return cast_fulsome_fusillade(powc, fail); + + case SPELL_SEISMIC_CANNONADE: + return cast_seismic_cannonade(you, powc, fail); + + case SPELL_SEISMIC_SHOCKWAVE: + return cast_seismic_shockwave(you, beam.target, powc, fail); + // non-player spells that have a zap, but that shouldn't be called (e.g // because they will crash as a player zap). case SPELL_DRAIN_LIFE: @@ -2904,6 +2944,11 @@ string spell_damage_string(spell_type spell, bool evoked, int pow, bool terse) return make_stringf("2d(%d-%d)", collision_damage(gavotte_impact_power(pow, 1), false).size, collision_damage(gavotte_impact_power(pow, 4), false).size); + + case SPELL_FULSOME_FUSILLADE: + return make_stringf("(3-5)d%d", _spell_damage(spell, pow).size); + case SPELL_RIMEBLIGHT: + return describe_rimeblight_damage(pow, terse); default: break; } diff --git a/crawl-ref/source/spl-cast.h b/crawl-ref/source/spl-cast.h index 9d6b1f8f3a7..63d4dbe09d7 100644 --- a/crawl-ref/source/spl-cast.h +++ b/crawl-ref/source/spl-cast.h @@ -24,7 +24,8 @@ enum class spflag none = 0x00000000, dir_or_target = 0x00000001, // use DIR_NONE targeting target = 0x00000002, // use DIR_TARGET targeting - // 0x00000004, + prefer_farthest = 0x00000004, // targets the most distant target + // by default // 0x00000008, // used to test for targeting targeting_mask = spflag::dir_or_target | spflag::target, diff --git a/crawl-ref/source/spl-damage.cc b/crawl-ref/source/spl-damage.cc index d68da0fc364..3d703ff8154 100644 --- a/crawl-ref/source/spl-damage.cc +++ b/crawl-ref/source/spl-damage.cc @@ -19,6 +19,7 @@ #include "cloud.h" #include "colour.h" #include "coordit.h" +#include "delay.h" #include "directn.h" #include "english.h" #include "env.h" @@ -57,6 +58,7 @@ #include "terrain.h" #include "tilepick.h" #include "transform.h" +#include "traps.h" #include "unicode.h" #include "viewchar.h" #include "view.h" @@ -291,7 +293,7 @@ vector chain_lightning_targets() return targets; } -static bool _warn_about_bad_targets(spell_type spell, vector targets) +bool warn_about_bad_targets(spell_type spell, vector targets) { vector bad_targets; for (coord_def p : targets) @@ -332,8 +334,8 @@ static bool _warn_about_bad_targets(spell_type spell, vector targets) spret cast_chain_lightning(int pow, const actor &caster, bool fail) { if (caster.is_player() - && _warn_about_bad_targets(SPELL_CHAIN_LIGHTNING, - chain_lightning_targets())) + && warn_about_bad_targets(SPELL_CHAIN_LIGHTNING, + chain_lightning_targets())) { return spret::abort; } @@ -663,8 +665,8 @@ static int _los_spell_damage_actor(const actor* agent, actor &target, } /** - * Returns the number of monsters adjacent to the given position which other - * monsters can huddle against. (Reducing damage taken from Refrigeration.) + * Returns the number of allied monsters adjacent to the given position which + * other monsters can huddle against. (Reducing damage taken from Refrigeration.) */ int adjacent_huddlers(coord_def pos) { @@ -676,8 +678,11 @@ int adjacent_huddlers(coord_def pos) continue; const monster* mon = act->as_monster(); - if (!mons_is_firewood(*mon) && !mons_is_conjured(mon->type)) + if (!mons_is_firewood(*mon) && !mons_is_conjured(mon->type) + && mons_aligned(act, mon)) + { ++adj_count; + } } return adj_count; } @@ -1128,7 +1133,7 @@ spret cast_permafrost_eruption(actor &caster, int pow, bool fail) maybe_victims.insert(*ai); vector mvv(maybe_victims.begin(), maybe_victims.end()); - if (_warn_about_bad_targets(SPELL_PERMAFROST_ERUPTION, mvv)) + if (warn_about_bad_targets(SPELL_PERMAFROST_ERUPTION, mvv)) return spret::abort; } @@ -2845,7 +2850,7 @@ vector arcjolt_targets(const actor &agent, bool actual) spret cast_arcjolt(int pow, const actor &agent, bool fail) { if (agent.is_player() - && _warn_about_bad_targets(SPELL_ARCJOLT, + && warn_about_bad_targets(SPELL_ARCJOLT, arcjolt_targets(agent, false))) { return spret::abort; @@ -3032,7 +3037,7 @@ spret cast_plasma_beam(int pow, const actor &agent, bool fail) if (agent.is_player()) { vector known_targs = plasma_beam_targets(agent, pow, false); - if (_warn_about_bad_targets(SPELL_PLASMA_BEAM, + if (warn_about_bad_targets(SPELL_PLASMA_BEAM, plasma_beam_paths(you.pos(), known_targs))) { return spret::abort; @@ -4874,3 +4879,310 @@ string describe_collision_dam(dice_def dice) { return make_stringf("%dd%d / collision", dice.num, dice.size); } + +vector get_magnavolt_targets() +{ + vector targets; + for (monster_near_iterator mi(&you); mi; ++mi) + { + if (mi->has_ench(ENCH_MAGNETISED)) + targets.push_back(mi->pos()); + } + + return targets; +} + +vector get_magnavolt_beam_paths(vector& targets) +{ + vector paths; + for (coord_def target : targets) + { + ray_def ray; + if (find_ray(you.pos(), target, ray, opc_solid)) + { + while (ray.advance() && ray.pos() != target) + paths.push_back(ray.pos()); + } + } + + return paths; +} + +spret cast_magnavolt(coord_def target, int pow, bool fail) +{ + // Calculate paths for all existing magnetized enemies and the one we plan + // to magnetize now. + vector targets = get_magnavolt_targets(); + // Add the new target only if it isn't already here, or we'll zap it twice. + if (find(targets.begin(), targets.end(), target) == targets.end()) + targets.push_back(target); + vector paths = get_magnavolt_beam_paths(targets); + + if (warn_about_bad_targets(SPELL_MAGNAVOLT, paths)) + return spret::abort; + + fail_check(); + + // First apply the debuff to the targeted enemy. + monster* mon = monster_at(target); + + if (!mon->has_ench(ENCH_MAGNETISED)) + mprf("Magnetic shrapnel attaches itself to %s", mon->name(DESC_THE).c_str()); + + mon->add_ench(mon_enchant(ENCH_MAGNETISED, 1, &you, + random_range(5, 8) * BASELINE_DELAY)); + + // Then zap all magnetized enemies. + mpr("Electricity arcs towards the magnetite!"); + for (unsigned int i = 0; i < targets.size(); ++i) + { + bolt volt; + zappy(ZAP_MAGNAVOLT, pow, false, volt); + volt.source = you.pos(); + volt.target = targets[i]; + volt.aimed_at_spot = true; + volt.range = LOS_RADIUS; + volt.thrower = KILL_YOU_MISSILE; + volt.fire(); + } + + return spret::success; +} + +spret cast_fulsome_fusillade(int pow, bool fail) +{ + targeter_radius hitfunc(&you, LOS_ARENA); + auto vulnerable = [](const actor *act) -> bool + { + return !act->is_player() + && !god_protects(*act->as_monster()); + }; + if (stop_attack_prompt(hitfunc, "hurl reagents", vulnerable)) + return spret::abort; + + fail_check(); + + mpr("You conjure up an array of volatile reagents!"); + + you.duration[DUR_FUSILLADE] = 5; + you.props[FUSILLADE_POWER_KEY] = pow; + + return spret::success; +} + +static vector concoction_flavours = +{ + BEAM_FIRE, + BEAM_COLD, + BEAM_POISON_ARROW, + BEAM_ELECTRICITY, +}; + +static map concoction_description = +{ + { BEAM_FIRE, "fiery phlogiston" }, + { BEAM_COLD, "frigid brine" }, + { BEAM_POISON_ARROW, "noxious sulfur" }, + { BEAM_ELECTRICITY, "flickering plasma" }, + { BEAM_MMISSILE, "unstable reaction" }, +}; + +static map concoction_colour = +{ + { BEAM_FIRE, RED }, + { BEAM_COLD, BLUE }, + { BEAM_POISON_ARROW, GREEN }, + { BEAM_ELECTRICITY, LIGHTCYAN }, + // This is for the multi-element reaction + { BEAM_MMISSILE, YELLOW }, +}; + +static vector> reaction_effects = +{ + { BEAM_NONE, 200 }, + { BEAM_MALMUTATE, 30 }, + { BEAM_SLOW, 30 }, + { BEAM_WEAKNESS, 30 }, + { BEAM_CONFUSION, 20 }, + { BEAM_ENSNARE, 20 }, + { BEAM_PETRIFY, 10 }, +}; + +static void _show_fusillade_explosion(map& hit_map, + vector& exp_map, + bool quick_anim) +{ + if (!(Options.use_animations & UA_BEAM)) + return; + + for (unsigned int j = 0; j < exp_map.size(); ++j) + { + const coord_def pos = exp_map[j]; + if (you.see_cell(pos)) + { +#ifdef USE_TILE + view_add_tile_overlay(pos, concoction_colour[hit_map[pos]] == YELLOW + ? (tileidx_t)TILE_BOLT_IRRADIATE + : tileidx_zap(concoction_colour[hit_map[pos]])); +#else + flash_tile(pos, concoction_colour[hit_map[pos]], 0); +#endif + } + } + + animation_delay(quick_anim ? 0 : 50, true); +} + +static void _do_fusillade_hit(monster* mon, int power, beam_type flavour) +{ + bolt exp; + + zappy(ZAP_FULSOME_FUSILLADE, power, false, exp); + + // Unstable reactions do more damage + if (flavour == BEAM_MMISSILE) + exp.damage.num = 5; + + exp.source_id = MID_PLAYER; + exp.thrower = KILL_YOU_MISSILE; + exp.origin_spell = SPELL_FULSOME_FUSILLADE; + exp.target = mon->pos(); + exp.source = mon->pos(); + exp.hit_verb = "engulfs"; + exp.aimed_at_spot = true; + exp.flavour = flavour; + exp.name = concoction_description[flavour]; + exp.aux_source = concoction_description[flavour]; + exp.colour = concoction_colour[flavour]; + exp.fire(); + + if (flavour == BEAM_MMISSILE && mon && mon->alive()) + { + // Do reaction here + beam_type effect = *random_choose_weighted(reaction_effects); + + switch (flavour) + { + // Do nothing + case BEAM_NONE: + break; + + case BEAM_ENSNARE: + ensnare(mon); + break; + + default: + enchant_actor_with_flavour(mon, &you, effect, power / 20); + } + } +} + +static void _calc_fusillade_explosion(coord_def center, beam_type flavour, + map& hit_map, + vector& exp_map, + bool quick_anim = false) +{ + for (adjacent_iterator ai(center, false); ai; ++ai) + { + if (feat_is_solid(env.grid(*ai))) + continue; + + exp_map.push_back(*ai); + + // Apply the explosion flavour at all affected tiles, but an unstable + // reaction at any tile which has already been hit this turn. + if (hit_map.count(*ai)) + hit_map[*ai] = BEAM_MMISSILE; + else + hit_map[*ai] = flavour; + } + + noisy(15, center, MID_PLAYER); + + _show_fusillade_explosion(hit_map, exp_map, quick_anim); +} + +void fire_fusillade() +{ + if (!enough_mp(2, true)) + { + mpr("Your magical reserves are too exhausted to conjure more reagents."); + return; + } + + mpr("Flasks of reagents rain from above!"); + + pay_mp(2); + finalize_mp_cost(); + + int pow = you.props[FUSILLADE_POWER_KEY].get_int(); + + map hit_map; // Map of which flavour to apply on which tile + vector exp_map[3]; // Individual explosions, for animation + + // Determine which monster's we're hitting + vector targs; + for (monster_near_iterator mi(you.pos(), LOS_NO_TRANS); mi; ++mi) + { + if (!mi->wont_attack() && !mons_is_firewood(**mi) && you.can_see(**mi) + && grid_distance(mi->pos(), you.pos()) > 1) + { + targs.push_back(*mi); + } + } + + shuffle_array(targs); + shuffle_array(concoction_flavours); + + // Aim up to 3 shots at random valid enemies + int num_shots = 3; + for (unsigned int i = 0; i < targs.size() && num_shots > 0; ++i) + { + _calc_fusillade_explosion(targs[i]->pos(), concoction_flavours[3 - num_shots], + hit_map, exp_map[3 - num_shots]); + num_shots--; + } + + // Animate more quickly if nothing is around + bool quick_anim = num_shots == 3; + + // If we still have shots left, aim them at random empty spaces + if (num_shots > 0) + { + vector empty_spot; + for (radius_iterator ri(you.pos(), LOS_NO_TRANS); ri; ++ri) + { + if (grid_distance(you.pos(), *ri) > 1 && !feat_is_solid(env.grid(*ri)) + && !monster_at(*ri)) + { + empty_spot.push_back(*ri); + } + } + + shuffle_array(empty_spot); + + for (unsigned int i = 0; i < empty_spot.size() && num_shots > 0; ++i) + { + _calc_fusillade_explosion(empty_spot[i], concoction_flavours[3 - num_shots], + hit_map, exp_map[3 - num_shots], quick_anim); + num_shots--; + } + } + + if (Options.use_animations & UA_BEAM) + animation_delay(quick_anim ? 50 : 100, false); + + // Finally, actually apply damage to enemies + map::iterator it; + for (it = hit_map.begin(); it != hit_map.end(); it++) + { + monster* mon = monster_at(it->first); + if (mon) + _do_fusillade_hit(mon, pow, it->second); + } + + you.duration[DUR_FUSILLADE] -= 1; + + if (!you.duration[DUR_FUSILLADE]) + mprf(MSGCH_DURATION, "Your rain of reagents ends."); +} diff --git a/crawl-ref/source/spl-damage.h b/crawl-ref/source/spl-damage.h index 9ff9f21fa47..825c3bb39f9 100644 --- a/crawl-ref/source/spl-damage.h +++ b/crawl-ref/source/spl-damage.h @@ -23,6 +23,7 @@ const int MAX_AIRSTRIKE_BONUS = 8 * AIRSTRIKE_PER_SPACE_BONUS; #define FROZEN_RAMPARTS_POWER_KEY "frozen_ramparts_power" #define TOXIC_RADIANCE_POWER_KEY "toxic_radiance_power" #define VORTEX_POWER_KEY "vortex_power" +#define FUSILLADE_POWER_KEY "fusillade_power" void setup_fire_storm(const actor *source, int pow, bolt &beam); spret cast_fire_storm(int pow, bolt &beam, bool fail); @@ -166,3 +167,12 @@ int get_warp_space_chance(int pow); dice_def collision_damage(int pow, bool random); string describe_collision_dam(dice_def dice); + +vector get_magnavolt_targets(); +vector get_magnavolt_beam_paths(vector& targets); +spret cast_magnavolt(coord_def target, int pow, bool fail); + +spret cast_fulsome_fusillade(int pow, bool fail); +void fire_fusillade(); + +bool warn_about_bad_targets(spell_type spell, vector targets); diff --git a/crawl-ref/source/spl-data.h b/crawl-ref/source/spl-data.h index 8c4dc36b7ef..7ba10b3c893 100644 --- a/crawl-ref/source/spl-data.h +++ b/crawl-ref/source/spl-data.h @@ -1282,7 +1282,7 @@ static const struct spell_desc spelldata[] = { SPELL_FULMINANT_PRISM, "Fulminant Prism", spschool::conjuration | spschool::alchemy, - spflag::target | spflag::area | spflag::not_self, + spflag::target | spflag::area | spflag::not_self | spflag::no_ghost, 4, 200, 4, 4, @@ -1397,14 +1397,14 @@ static const struct spell_desc spelldata[] = }, { - SPELL_SUMMON_HELL_BEAST, "Summon Hell Beast", + SPELL_SUMMON_SIN_BEAST, "Summon Sin Beast", spschool::summoning, spflag::unholy | spflag::monster | spflag::selfench, 4, 0, -1, -1, 0, - TILEG_SUMMON_HELL_BEAST, + TILEG_SUMMON_SIN_BEAST, }, { @@ -2991,11 +2991,11 @@ static const struct spell_desc spelldata[] = { SPELL_GRAVITAS, "Gell's Gravitas", spschool::translocation, - spflag::target | spflag::needs_tracer, + spflag::target | spflag::needs_tracer | spflag::no_ghost, 3, 100, LOS_RADIUS, LOS_RADIUS, - 0, + 8, TILEG_GRAVITAS, }, @@ -3044,7 +3044,7 @@ static const struct spell_desc spelldata[] = }, { - SPELL_AURA_OF_BRILLIANCE, "Aura of Brilliance", + SPELL_PRAYER_OF_BRILLIANCE, "Prayer of Brilliance", spschool::conjuration, spflag::area | spflag::monster, 5, @@ -3195,7 +3195,7 @@ static const struct spell_desc spelldata[] = 50, 3, 3, 0, - TILEG_POISONOUS_VAPOURS, + TILEG_MERCURY_VAPOURS, }, { @@ -3599,7 +3599,7 @@ static const struct spell_desc spelldata[] = { SPELL_SUMMON_SPIDERS, "Summon Spiders", spschool::summoning | spschool::alchemy, - spflag::mons_abjure | spflag::monster, + spflag::monster, 6, 200, -1, -1, @@ -3855,7 +3855,7 @@ static const struct spell_desc spelldata[] = SPELL_PILEDRIVER, "Maxwell's Portable Piledriver", spschool::translocation, spflag::none, - 4, + 3, 100, 5, 5, 0, @@ -3873,6 +3873,95 @@ static const struct spell_desc spelldata[] = TILEG_GAVOTTE, }, +{ + SPELL_MAGNAVOLT, "Magnavolt", + spschool::air | spschool::earth, + spflag::target | spflag::needs_tracer | spflag::destructive + | spflag::prefer_farthest, + 7, + 200, + LOS_RADIUS, LOS_RADIUS, + 0, + TILEG_MAGNAVOLT, +}, + +{ + SPELL_FULSOME_FUSILLADE, "Fulsome Fusillade", + spschool::alchemy | spschool::conjuration, + spflag::area | spflag::destructive, + 8, + 200, + LOS_RADIUS, LOS_RADIUS, + 0, + TILEG_FULSOME_FUSILLADE, +}, + +{ + SPELL_RIMEBLIGHT, "Rimeblight", + spschool::necromancy | spschool::ice, + spflag::dir_or_target | spflag::unclean, + 7, + 200, + 5, 5, + 0, + TILEG_RIMEBLIGHT, +}, + +{ + SPELL_SEISMIC_CANNONADE, "Seismic Cannonade", + spschool::alchemy | spschool::earth, + spflag::none, + 7, + 200, + -1, -1, + 0, + TILEG_SEISMIC_CANNONADE, +}, + +{ + SPELL_SEISMIC_SHOCKWAVE, "Seismic Shockwave", + spschool::alchemy | spschool::earth, + spflag::target, + 7, + 200, + 6, 6, + 0, + TILEG_SUMMON_LIGHTNING_SPIRE, +}, + +{ + SPELL_STONE_BULLET, "Stone Bullet", + spschool::conjuration | spschool::earth, + spflag::dir_or_target | spflag::needs_tracer | spflag::monster, + 5, + 200, + 6, 6, + 0, + TILEG_STONE_ARROW, +}, + +{ + SPELL_FLASHING_BALESTRA, "Flashing Balestra", + spschool::conjuration, + spflag::dir_or_target | spflag::needs_tracer | spflag::monster, + 5, + 200, + 6, 6, + 0, + TILEG_TUKIMAS_DANCE, +}, + +{ + SPELL_BESTOW_ARMS, "Bestow Arms", + spschool::hexes, + spflag::area | spflag::utility | spflag::monster, + 5, + 200, + 6, 6, + 0, + TILEG_SHEZAS_DANCE, +}, + { SPELL_NO_SPELL, "nonexistent spell", spschool::none, diff --git a/crawl-ref/source/spl-goditem.cc b/crawl-ref/source/spl-goditem.cc index b5157abf321..2d46e3fd4c4 100644 --- a/crawl-ref/source/spl-goditem.cc +++ b/crawl-ref/source/spl-goditem.cc @@ -407,6 +407,14 @@ static void _dispellable_player_buffs(player_debuff_effects &buffs) { continue; } + // XXX: Handle special-cases with regard to monster auras. + // (It would be nice if this could be handled automatically, but + // there aren't yet enough of these effects to bother doing such) + if (i == DUR_SLOW && aura_is_active_on_player(TORPOR_SLOWED_KEY)) + continue; + else if (i == DUR_SENTINEL_MARK && aura_is_active_on_player(OPHAN_MARK_KEY)) + continue; + buffs.durations.push_back((duration_type) i); // this includes some buffs that won't be reduced in duration - // anything already at 1 aut, or flight/transform while <= 11 aut diff --git a/crawl-ref/source/spl-goditem.h b/crawl-ref/source/spl-goditem.h index 7c96b8f0035..bb8c6b162d3 100644 --- a/crawl-ref/source/spl-goditem.h +++ b/crawl-ref/source/spl-goditem.h @@ -53,7 +53,6 @@ const enchant_type dispellable_enchantments[] = ENCH_REPEL_MISSILES, ENCH_RESISTANCE, ENCH_HEXED, - ENCH_BRILLIANCE_AURA, ENCH_EMPOWERED_SPELLS, ENCH_BOUND_SOUL, ENCH_INFESTATION, diff --git a/crawl-ref/source/spl-monench.cc b/crawl-ref/source/spl-monench.cc index 8228e5eec7a..694f5ade0ce 100644 --- a/crawl-ref/source/spl-monench.cc +++ b/crawl-ref/source/spl-monench.cc @@ -8,14 +8,18 @@ #include "spl-monench.h" +#include "beam.h" #include "coordit.h" #include "english.h" // apostrophise #include "env.h" +#include "fight.h" #include "losglobal.h" #include "message.h" +#include "mon-tentacle.h" #include "spl-util.h" #include "stringutil.h" // make_stringf #include "terrain.h" +#include "view.h" int englaciate(coord_def where, int pow, actor *agent) { @@ -29,12 +33,19 @@ int englaciate(coord_def where, int pow, actor *agent) monster* mons = victim->as_monster(); - if (victim->res_cold() > 0 - || victim->is_stationary()) + // Skip some ineligable monster categories + if (mons && + (mons_is_conjured(mons->type) || mons_is_firewood(*mons) + || mons_is_tentacle_segment(mons->type))) + { + return 0; + } + + if (victim->res_cold() > 0) { if (!mons) canned_msg(MSG_YOU_UNAFFECTED); - else if (!mons_is_firewood(*mons)) + else simple_monster_message(*mons, " is unaffected."); return 0; } @@ -104,8 +115,7 @@ bool do_slow_monster(monster& mon, const actor* agent, int dur) if (mon.stasis()) return true; - if (!mon.is_stationary() - && mon.add_ench(mon_enchant(ENCH_SLOW, 0, agent, dur))) + if (mon.add_ench(mon_enchant(ENCH_SLOW, 0, agent, dur))) { if (!mon.paralysed() && !mon.petrified() && simple_monster_message(mon, " seems to slow down.")) @@ -201,3 +211,87 @@ bool start_ranged_constriction(actor& caster, actor& target, int duration, return true; } + +dice_def rimeblight_dot_damage(int pow) +{ + return dice_def(2, 3 + div_rand_round(pow, 17)); +} + +string describe_rimeblight_damage(int pow, bool terse) +{ + dice_def dot_damage = rimeblight_dot_damage(pow); + dice_def shards_damage = zap_damage(ZAP_RIMEBLIGHT_SHARDS, pow, false, false); + + if (terse) + { + return make_stringf("%dd%d/%dd%d", dot_damage.num, dot_damage.size, + shards_damage.num, shards_damage.size); + } + + return make_stringf("%dd%d (primary target), %dd%d (explosion)", + dot_damage.num, dot_damage.size, + shards_damage.num, shards_damage.size); +} + +bool apply_rimeblight(monster& victim, int power, bool quiet) +{ + if (victim.has_ench(ENCH_RIMEBLIGHT) + || !(victim.holiness() & (MH_NATURAL | MH_DEMONIC | MH_HOLY))) + { + return false; + } + + int duration = (random_range(5, 9) + div_rand_round(power, 20)) + * BASELINE_DELAY; + victim.add_ench(mon_enchant(ENCH_RIMEBLIGHT, 0, &you, duration)); + victim.props[RIMEBLIGHT_POWER_KEY] = power; + victim.props[RIMEBLIGHT_TICKS_KEY] = random_range(0, 2); + + if (!quiet) + simple_monster_message(victim, " is afflicted with rimeblight."); + + return true; +} + +void do_rimeblight_explosion(coord_def pos, int power, int size) +{ + bolt shards; + zappy(ZAP_RIMEBLIGHT_SHARDS, power, false, shards); + shards.ex_size = size; + shards.source_id = MID_PLAYER; + shards.thrower = KILL_YOU_MISSILE; + shards.origin_spell = SPELL_RIMEBLIGHT; + shards.target = pos; + shards.source = pos; + shards.hit_verb = "hits"; + shards.aimed_at_spot = true; + shards.explode(); +} + +void tick_rimeblight(monster& victim) +{ + const int pow = victim.props[RIMEBLIGHT_POWER_KEY].get_int(); + int ticks = victim.props[RIMEBLIGHT_TICKS_KEY].get_int(); + + // Determine chance to explode with ice (rises over time) + // Never happens below 3, always happens at 4, random chance beyond that + if (ticks == 4 || ticks > 4 && x_chance_in_y(ticks, ticks + 16)) + { + if (you.can_see(victim)) + mprf("Shards of ice erupt from the %s body!", victim.name(DESC_ITS).c_str()); + do_rimeblight_explosion(victim.pos(), pow, 1); + } + + // Injury bond or some other effects may have killed us by now + if (!victim.alive()) + return; + + // Apply direct AC-ignoring cold damage + int dmg = rimeblight_dot_damage(pow).roll(); + dmg = resist_adjust_damage(&victim, BEAM_COLD, dmg); + victim.hurt(&you, dmg, BEAM_COLD, KILLED_BY_FREEZING); + + // Increment how long rimeblight has been active + if (victim.alive()) + victim.props[RIMEBLIGHT_TICKS_KEY] = (++ticks); +} diff --git a/crawl-ref/source/spl-monench.h b/crawl-ref/source/spl-monench.h index dfa378840b0..966c4746eb7 100644 --- a/crawl-ref/source/spl-monench.h +++ b/crawl-ref/source/spl-monench.h @@ -1,6 +1,7 @@ #pragma once #include "constrict-type.h" +#include "random.h" #include "spl-cast.h" struct bolt; @@ -9,6 +10,9 @@ class monster; #define VILE_CLUTCH_POWER_KEY "vile_clutch_power" #define FASTROOT_POWER_KEY "fastroot_power" +#define RIMEBLIGHT_POWER_KEY "rimeblight_power" +#define RIMEBLIGHT_TICKS_KEY "rimeblight_ticks" +#define RIMEBLIGHT_DEATH_KEY "death_by_rimeblight" int englaciate(coord_def where, int pow, actor *agent); spret cast_englaciation(int pow, bool fail); @@ -20,3 +24,9 @@ bool enfeeble_monster(monster &mon, int pow); spret cast_vile_clutch(int pow, bolt &beam, bool fail); bool start_ranged_constriction(actor& caster, actor& target, int duration, constrict_type type); + +dice_def rimeblight_dot_damage(int pow); +string describe_rimeblight_damage(int pow, bool terse); +void do_rimeblight_explosion(coord_def pos, int power, int size); +bool apply_rimeblight(monster& victim, int power, bool quiet = false); +void tick_rimeblight(monster& victim); diff --git a/crawl-ref/source/spl-summoning.cc b/crawl-ref/source/spl-summoning.cc index 86d7a8e5a2e..ff933cd03d9 100644 --- a/crawl-ref/source/spl-summoning.cc +++ b/crawl-ref/source/spl-summoning.cc @@ -41,6 +41,7 @@ #include "mgen-data.h" #include "mon-abil.h" #include "mon-act.h" +#include "mon-aura.h" #include "mon-behv.h" #include "mon-book.h" // MON_SPELL_WIZARD #include "mon-cast.h" @@ -1541,7 +1542,7 @@ spret cast_martyrs_knell(const actor* caster, int pow, god_type god, bool fail) caster->name(DESC_THE).c_str()); } - martyr_injury_bond(*shade); + mons_update_aura(*shade); } else if (caster->is_player()) canned_msg(MSG_NOTHING_HAPPENS); @@ -2149,6 +2150,14 @@ spret cast_fulminating_prism(actor* caster, int pow, } else if (you.can_see(*prism)) mprf("A prism of explosive energy appears from nowhere!"); + + // This looks silly, but prevents the even sillier-looking situation of + // monster-cast prisms displaying as 'unaware of you'. + if (caster->is_monster()) + { + prism->foe = caster->as_monster()->foe; + prism->behaviour = BEH_SEEK; + } } else if (you.can_see(*caster)) canned_msg(MSG_NOTHING_HAPPENS); @@ -2251,7 +2260,7 @@ static const map summonsdata = { SPELL_SHADOW_CREATURES, { 0, 4 } }, { SPELL_SUMMON_SPIDERS, { 0, 5 } }, { SPELL_SUMMON_UFETUBUS, { 0, 8 } }, - { SPELL_SUMMON_HELL_BEAST, { 0, 8 } }, + { SPELL_SUMMON_SIN_BEAST, { 0, 5 } }, { SPELL_SUMMON_UNDEAD, { 0, 8 } }, { SPELL_SUMMON_DRAKES, { 0, 4 } }, { SPELL_SUMMON_MUSHROOMS, { 0, 8 } }, @@ -2283,6 +2292,7 @@ static const map summonsdata = { SPELL_CONJURE_LIVING_SPELLS, { 0, 4 } }, { SPELL_SHEZAS_DANCE, { 0, 6 } }, { SPELL_DIVINE_ARMAMENT, { 0, 1 } }, + { SPELL_FLASHING_BALESTRA, { 0, 2 } }, }; bool summons_are_capped(spell_type spell) @@ -2805,6 +2815,40 @@ bool summon_hell_out_of_bat(const actor &agent, coord_def pos) return false; } +bool summon_swarm_clone(const monster& agent, coord_def target_pos) +{ + // Go up the summon chain to find the highest-level version of ourselves + const monster* parent = &agent; + while (parent->summoner && monster_by_mid(parent->summoner) + && monster_by_mid(parent->summoner)->type == agent.type) + { + parent = monster_by_mid(parent->summoner); + } + + // Apply pseudo-summon cap + int count = 0; + for (monster_iterator mi; mi; ++mi) + { + if (mi->summoner == parent->mid) + ++count; + + if (count > 8) + return false; + } + + mgen_data mg(agent.type, BEH_COPY, target_pos, _auto_autofoe(parent), MG_AUTOFOE); + mg.set_summoned(parent, 2, SPELL_NO_SPELL, GOD_NO_GOD); + + if (monster* spawn = create_monster(mg)) + { + if (you.can_see(*spawn)) + mprf("Another %s is drawn to the feast!", spawn->name(DESC_PLAIN).c_str()); + return true; + } + + return false; +} + bool summon_spider(const actor &agent, coord_def pos, god_type god, spell_type spell, int pow) { @@ -2966,3 +3010,130 @@ spret cast_simulacrum(coord_def target, int pow, bool fail) return spret::success; } + +spret cast_seismic_cannonade(const actor& agent, int pow, bool fail) +{ + if (cannonade_is_active(agent)) + { + // Note that it is impossible to reach this point if a cannon is fully + // charged, as cast_seismic_shockwave will be called instead. + mpr("None of your cannons are fully assembled yet."); + return spret::abort; + } + + fail_check(); + + mgen_data cannon = _pal_data(MONS_SEISMIC_CANNON, 0, GOD_NO_GOD, + SPELL_SEISMIC_CANNONADE); + cannon.flags |= MG_FORCE_PLACE; + cannon.hd = 4 + div_rand_round(pow, 20); + + // Make all cannons share the same duration + const int dur = random_range(20, 30) * BASELINE_DELAY; + + int num_created = 0; + for (int i = 0; i < 3; ++i) + { + // Find a spot for each cannon (at a somewhat larger distance than + // normal summons) + find_habitable_spot_near(you.pos(), MONS_SEISMIC_CANNON, 3, false, + cannon.pos); + + monster* mons = create_monster(cannon); + if (mons) + { + mons->add_ench(mon_enchant(ENCH_FAKE_ABJURATION, 0, &you, dur)); + mons->hit_points = mons->max_hit_points / 2; + ++num_created; + } + } + + if (num_created > 1) + mpr("You sculpt cannons from the earth!"); + else if (num_created == 1) + mpr("You sculpt a cannon from the earth!"); + else + canned_msg(MSG_NOTHING_HAPPENS); + + return spret::success; +} + +bool cannonade_is_active(const actor& agent) +{ + if (!agent.has_spell(SPELL_SEISMIC_CANNONADE)) + return false; + + for (monster_iterator mi; mi; ++mi) + { + if (mi->type == MONS_SEISMIC_CANNON && mi->summoner == agent.mid) + return true; + } + + return false; +} + +bool cannonade_is_fully_charged(const actor& agent) +{ + if (!agent.has_spell(SPELL_SEISMIC_CANNONADE)) + return false; + + for (monster_iterator mi; mi; ++mi) + { + if (mi->type == MONS_SEISMIC_CANNON && mi->summoner == agent.mid + && mi->has_ench(ENCH_SPELL_CHARGED)) + { + return true; + } + } + + return false; +} + +vector get_charged_cannon_pos(const actor& agent) +{ + vector pos; + for (monster_iterator mi; mi; ++mi) + { + if (mi->type == MONS_SEISMIC_CANNON && mi->summoner == agent.mid + && mi->has_ench(ENCH_SPELL_CHARGED)) + { + pos.push_back(mi->pos()); + } + } + + return pos; +} + + +spret cast_seismic_shockwave(const actor& agent, coord_def target, int pow, bool fail) +{ + bolt beam; + zappy(ZAP_SEISMIC_SHOCKWAVE, pow, agent.is_monster(), beam); + beam.source_id = agent.mid; + beam.thrower = agent.is_player() ? KILL_YOU_MISSILE : KILL_MON; + beam.is_tracer = false; + beam.origin_spell = SPELL_SEISMIC_SHOCKWAVE; + beam.ex_size = 2; + beam.source = target; + beam.target = target; + beam.aimed_at_spot = true; + + bolt tracer = beam; + tracer.is_tracer = true; + tracer.explode(false); + if (tracer.beam_cancelled) + return spret::abort; + + fail_check(); + + mpr("Your cannons unleash a shockwave through the ground and break into pieces!"); + for (monster_iterator mi; mi; ++mi) + { + if (mi->type == MONS_SEISMIC_CANNON && mi->summoner == agent.mid) + monster_die(**mi, KILL_DISMISSED, NON_MONSTER, true); + } + + beam.explode(); + + return spret::success; +} diff --git a/crawl-ref/source/spl-summoning.h b/crawl-ref/source/spl-summoning.h index 6b0f9f55c47..06ca117978d 100644 --- a/crawl-ref/source/spl-summoning.h +++ b/crawl-ref/source/spl-summoning.h @@ -116,6 +116,7 @@ bool summon_hell_out_of_bat(const actor &agent, coord_def pos); bool summon_spider(const actor &agent, coord_def pos, god_type god, spell_type spell, int pow); spret summon_spiders(actor &agent, int pow, god_type god, bool fail = false); +bool summon_swarm_clone(const monster& agent, coord_def target_pos); spret summon_butterflies(); @@ -123,3 +124,9 @@ spret cast_broms_barrelling_boulder(actor& agent, coord_def pos, int pow, bool f string mons_simulacrum_immune_reason(const monster *mons); spret cast_simulacrum(coord_def target, int pow, bool fail); + +spret cast_seismic_cannonade(const actor& agent, int pow, bool fail); +bool cannonade_is_active(const actor& agent); +bool cannonade_is_fully_charged(const actor& agent); +vector get_charged_cannon_pos(const actor& agent); +spret cast_seismic_shockwave(const actor& agent, coord_def target, int pow, bool fail); diff --git a/crawl-ref/source/spl-transloc.cc b/crawl-ref/source/spl-transloc.cc index 4476c3f484f..8f49e8ee645 100644 --- a/crawl-ref/source/spl-transloc.cc +++ b/crawl-ref/source/spl-transloc.cc @@ -2067,20 +2067,25 @@ vector possible_piledriver_targets() int piledriver_collision_power(int pow, int dist) { - return (pow + 30) * (2 + dist) / 2; + return (pow + 42) * (1 + (dist * 2)) / 3; } spret cast_piledriver(int pow, bool fail) { + // Calculate all possible valid targets first, so we can prompt the player + // about anything they *might* hit. + vector targs = possible_piledriver_targets(); + vector path = piledriver_beam_paths(targs); + if (warn_about_bad_targets(SPELL_PILEDRIVER, path)) + return spret::abort; + fail_check(); - vector targs = possible_piledriver_targets(); + // Now that they've confirmed, pick the *real* target shuffle_array(targs); targs.resize(1); - monster* mon = monster_at(targs[0]); - - vector path = piledriver_beam_paths(targs); + path = piledriver_beam_paths(targs); mprf("Space contracts around you and %s and then re-expands violently!", mon->name(DESC_THE).c_str()); @@ -2106,13 +2111,10 @@ spret cast_piledriver(int pow, bool fail) // Now trigger location effects (to avoid dispersal traps causing all sorts // of problems with keeping the two of us together in the middle) - mon->apply_location_effects(old_targ_pos); + if (mon->alive()) + mon->apply_location_effects(old_targ_pos); you.apply_location_effects(old_pos); - // Lock player in place proportional to the distance they travelled - you.increase_duration(DUR_NO_MOMENTUM, path.size()); - mpr("You are locked in place by the recoil."); - return spret::success; } @@ -2121,25 +2123,29 @@ int gavotte_impact_power(int pow, int dist) return (pow * 3 / 4 + 35) * (dist + 5) / 2; } -static void _push_actor(actor& victim, coord_def dir, int dist, int pow) +static void _maybe_penance_for_collision(god_conduct_trigger conducts[3], actor& victim) { - const bool god_prot = victim.is_monster() - && god_protects(&you, victim.as_monster()); - if (victim.is_monster() && victim.alive()) { //potentially penance if (!mons_is_conjured(victim.as_monster()->type)) { - god_conduct_trigger conducts[3]; set_attack_conducts(conducts, *victim.as_monster(), you.can_see(*victim.as_monster())); } } +} + +static void _push_actor(actor& victim, coord_def dir, int dist, int pow) +{ + const bool god_prot = victim.is_monster() + && god_protects(&you, victim.as_monster()); + + god_conduct_trigger conducts[3]; if (victim.is_monster() && !god_prot) { - behaviour_event(victim.as_monster(), ME_ANNOY, &you, you.pos()); + behaviour_event(victim.as_monster(), ME_ALERT, &you, you.pos()); victim.as_monster()->speed_increment -= 10; } @@ -2152,6 +2158,7 @@ static void _push_actor(actor& victim, coord_def dir, int dist, int pow) && !victim.is_player()) { victim.collide(next_pos, &you, gavotte_impact_power(pow, i)); + _maybe_penance_for_collision(conducts, victim); break; } else if (actor* act_at_space = actor_at(next_pos)) @@ -2160,6 +2167,8 @@ static void _push_actor(actor& victim, coord_def dir, int dist, int pow) && !act_at_space->is_player()) { victim.collide(next_pos, &you, gavotte_impact_power(pow, i)); + _maybe_penance_for_collision(conducts, victim); + _maybe_penance_for_collision(conducts, *act_at_space); } break; } @@ -2179,6 +2188,17 @@ static void _push_actor(actor& victim, coord_def dir, int dist, int pow) spret cast_gavotte(int pow, const coord_def dir, bool fail) { + vector affected = gavotte_affected_monsters(dir); + if (!affected.empty()) + { + vector spots; + for (unsigned int i = 0; i < affected.size(); ++i) + spots.push_back(affected[i]->pos()); + + if (warn_about_bad_targets(SPELL_GELLS_GAVOTTE, spots)) + return spret::abort; + } + fail_check(); // XXX: Surely there's a better way to do this @@ -2202,12 +2222,19 @@ spret cast_gavotte(int pow, const coord_def dir, bool fail) mprf("Gravity reorients to the %s!", dir_msg.c_str()); - // Gather all monsters we will be moving + if (you.stasis()) + canned_msg(MSG_STRANGE_STASIS); + + // Gather all actors we will be moving vector targs; - targs.push_back(&you); + + // Don't move stationary players (or formicids) + if (!you.is_stationary() && !you.stasis()) + targs.push_back(&you); + for (monster_near_iterator mi(you.pos()); mi; ++mi) { - if (!mi->is_stationary()) + if (!mi->is_stationary() && you.see_cell_no_trans(mi->pos())) targs.push_back(*mi); } @@ -2277,7 +2304,7 @@ vector gavotte_affected_monsters(const coord_def dir) for (monster_near_iterator mi(you.pos()); mi; ++mi) { - if (!mi->is_stationary()) + if (!mi->is_stationary() && you.see_cell_no_trans(mi->pos())) { if (_gavotte_will_wall_slam(*mi, dir)) affected.push_back(*mi); diff --git a/crawl-ref/source/spl-util.cc b/crawl-ref/source/spl-util.cc index ce112e837a6..43b522e0bd8 100644 --- a/crawl-ref/source/spl-util.cc +++ b/crawl-ref/source/spl-util.cc @@ -1170,6 +1170,7 @@ string casting_uselessness_reason(spell_type spell, bool temp) case SPELL_SIMULACRUM: case SPELL_INFESTATION: case SPELL_TUKIMAS_DANCE: + case SPELL_SEISMIC_CANNONADE: if (you.allies_forbidden()) return "you cannot coerce anything to obey you."; break; @@ -1465,6 +1466,10 @@ string spell_uselessness_reason(spell_type spell, bool temp, bool prevent, if (temp && you.duration[DUR_GAVOTTE_COOLDOWN]) return "local gravity is still too unstable to reorient."; + case SPELL_FULSOME_FUSILLADE: + if (temp && you.duration[DUR_FUSILLADE]) + return "you are already unleashing a barrage of alchemical concoctions!"; + default: break; } @@ -1534,6 +1539,7 @@ bool spell_no_hostile_in_range(spell_type spell) case SPELL_OLGREBS_TOXIC_RADIANCE: case SPELL_IGNITION: case SPELL_FROZEN_RAMPARTS: + case SPELL_FULSOME_FUSILLADE: return minRange > you.current_vision; // Special handling for cloud spells. diff --git a/crawl-ref/source/spl-zap.cc b/crawl-ref/source/spl-zap.cc index 5feffaa7e67..39bacc363e3 100644 --- a/crawl-ref/source/spl-zap.cc +++ b/crawl-ref/source/spl-zap.cc @@ -40,6 +40,8 @@ static pair _spl_zaps[] = { SPELL_BLASTMOTE, ZAP_BLASTMOTE }, { SPELL_KISS_OF_DEATH, ZAP_KISS_OF_DEATH }, { SPELL_PERMAFROST_ERUPTION, ZAP_PERMAFROST_ERUPTION_COLD }, + { SPELL_FULSOME_FUSILLADE, ZAP_FULSOME_FUSILLADE }, + { SPELL_RIMEBLIGHT, ZAP_RIMEBLIGHT }, // Wizard mode only. { SPELL_PORKALATOR, ZAP_PORKALATOR }, { SPELL_HURL_DAMNATION, ZAP_HURL_DAMNATION }, @@ -81,6 +83,8 @@ static pair _spl_zaps[] = { SPELL_TREMORSTONE, ZAP_TREMORSTONE }, { SPELL_CURSE_OF_AGONY, ZAP_CURSE_OF_AGONY }, { SPELL_GRAVITAS, ZAP_GRAVITAS }, + { SPELL_SEISMIC_CANNONADE, ZAP_SEISMIC_SHOCKWAVE }, + { SPELL_STONE_BULLET, ZAP_STONE_BULLET }, // monster-specific { SPELL_SLUG_DART, ZAP_SLUG_DART }, @@ -138,6 +142,8 @@ static pair _spl_zaps[] = { SPELL_GLACIAL_BREATH, ZAP_GLACIAL_BREATH }, { SPELL_MUD_BREATH, ZAP_MUD_BREATH }, { SPELL_GALVANIC_BREATH, ZAP_GALVANIC_BREATH }, + { SPELL_MAGNAVOLT, ZAP_MAGNAVOLT }, + { SPELL_FLASHING_BALESTRA, ZAP_FLASHING_BALESTRA }, // These are all for zap -> spell lookup. { SPELL_QUICKSILVER_BOLT, ZAP_QUICKSILVER_BOLT }, diff --git a/crawl-ref/source/status.cc b/crawl-ref/source/status.cc index 3de92813e22..40adac9b891 100644 --- a/crawl-ref/source/status.cc +++ b/crawl-ref/source/status.cc @@ -842,6 +842,19 @@ bool fill_status_info(int status, status_info& inf) _fill_inf_from_ddef(DUR_LOWERED_WL, inf); break; + case DUR_FUSILLADE: + if (!enough_mp(2, true)) + inf.light_colour = DARKGREY; + break; + + case STATUS_CANNONADE: + if (cannonade_is_fully_charged(you)) + { + inf.light_colour = LIGHTCYAN; + inf.light_text = "Shockwave"; + } + break; + default: if (!found) { diff --git a/crawl-ref/source/status.h b/crawl-ref/source/status.h index 98f9e24edee..79a6d497952 100644 --- a/crawl-ref/source/status.h +++ b/crawl-ref/source/status.h @@ -54,6 +54,7 @@ enum status_type STATUS_BLACK_TORCH, STATUS_GEM, STATUS_REV, + STATUS_CANNONADE, STATUS_DRACONIAN_BREATH, STATUS_LAST_STATUS = STATUS_DRACONIAN_BREATH }; diff --git a/crawl-ref/source/tag-version.h b/crawl-ref/source/tag-version.h index b600167e219..f0bb2a0ac8b 100644 --- a/crawl-ref/source/tag-version.h +++ b/crawl-ref/source/tag-version.h @@ -304,6 +304,7 @@ enum tag_minor_version TAG_MINOR_COGLIN_NO_JEWELLERY, // Remove all jewellery from Coglins TAG_MINOR_TALISMANS_SEEN, // Keep track of seen talismans TAG_MINOR_FIX_APOSTLE_DAMAGE, // Fix damage tracking of banished apostles + TAG_MINOR_MON_AURA_REFACTORING,// Mark enchantments from passive auras in mon_enchant #endif NUM_TAG_MINORS, TAG_MINOR_VERSION = NUM_TAG_MINORS - 1 diff --git a/crawl-ref/source/tags.cc b/crawl-ref/source/tags.cc index 42e923e627e..38aab52f527 100644 --- a/crawl-ref/source/tags.cc +++ b/crawl-ref/source/tags.cc @@ -62,6 +62,7 @@ #include "mapmark.h" #include "misc.h" #include "mon-death.h" +#include "mon-ench.h" #if TAG_MAJOR_VERSION == 34 #include "mon-place.h" #include "mon-poly.h" @@ -5988,6 +5989,7 @@ static void marshall_mon_enchant(writer &th, const mon_enchant &me) marshallInt(th, me.source); marshallShort(th, min(me.duration, INFINITE_DURATION)); marshallShort(th, min(me.maxduration, INFINITE_DURATION)); + marshallByte(th, me.ench_is_aura); } static mon_enchant unmarshall_mon_enchant(reader &th) @@ -5999,6 +6001,10 @@ static mon_enchant unmarshall_mon_enchant(reader &th) me.source = unmarshallInt(th); me.duration = unmarshallShort(th); me.maxduration = unmarshallShort(th); +#if TAG_MAJOR_VERSION == 34 + if (th.getMinorVersion() >= TAG_MINOR_MON_AURA_REFACTORING) + me.ench_is_aura = static_cast(unmarshallByte(th)); +#endif return me; } diff --git a/crawl-ref/source/target.cc b/crawl-ref/source/target.cc index af9c8117683..d8e1861d10d 100644 --- a/crawl-ref/source/target.cc +++ b/crawl-ref/source/target.cc @@ -51,6 +51,11 @@ bool targeter::set_aim(coord_def a) return true; } +bool targeter::preferred_aim(coord_def a) +{ + return valid_aim(a); +} + bool targeter::can_affect_outside_range() { return false; @@ -2081,7 +2086,7 @@ targeter_englaciate::targeter_englaciate() bool targeter_englaciate::affects_monster(const monster_info& mon) { return get_resist(mon.resists(), MR_RES_COLD) <= 0 - && !mons_class_flag(mon.type, M_STATIONARY); + && !mons_is_conjured(mon.type) && !mons_class_is_firewood(mon.type); } targeter_fear::targeter_fear() @@ -2381,7 +2386,7 @@ bool targeter_gavotte::set_aim(coord_def a) bolt tempbeam = beam; tempbeam.target = a; tempbeam.aimed_at_spot = false; - tempbeam.range = GAVOTTE_DISTANCE; + tempbeam.range = you.is_stationary() || you.stasis() ? 0 : GAVOTTE_DISTANCE; tempbeam.path_taken.clear(); tempbeam.fire(); path_taken = tempbeam.path_taken; @@ -2424,3 +2429,79 @@ aff_type targeter_gavotte::is_affected(coord_def loc) return AFF_NO; } + +targeter_magnavolt::targeter_magnavolt(const actor* act, int _range) : + targeter_smite(act, _range, 0, 0, false, nullptr) +{ +} + +bool targeter_magnavolt::valid_aim(coord_def a) +{ + if (!targeter_smite::valid_aim(a)) + return false; + + if (!monster_at(a) || !you.can_see(*monster_at(a))) + return notify_fail("You don't see a valid target there."); + + return true; +} + +bool targeter_magnavolt::preferred_aim(coord_def a) +{ + return valid_aim(a) && !monster_at(a)->has_ench(ENCH_MAGNETISED); +} + +bool targeter_magnavolt::set_aim(coord_def a) +{ + beam_targets.clear(); + beam_paths.clear(); + + if (!targeter_smite::set_aim(a)) + return false; + + beam_targets = get_magnavolt_targets(); + beam_targets.push_back(a); + beam_paths = get_magnavolt_beam_paths(beam_targets); + + return true; +} + +aff_type targeter_magnavolt::is_affected(coord_def loc) +{ + for (coord_def pos : beam_targets) + { + if (loc == pos) + return AFF_YES; + } + + for (coord_def pos : beam_paths) + { + if (loc == pos) + return AFF_MAYBE; + } + + return AFF_NO; +} + +targeter_seismic_shockwave::targeter_seismic_shockwave(const actor* act, int _cannon_range) : + targeter_smite(act, LOS_RADIUS, 2, 2, true, nullptr), cannon_range(_cannon_range) +{ + cannon_pos = get_charged_cannon_pos(*act); +} + +bool targeter_seismic_shockwave::valid_aim(coord_def a) +{ + if (!targeter_smite::valid_aim(a)) + return false; + + for (coord_def cannon : cannon_pos) + { + if (grid_distance(a, cannon) <= cannon_range + && cell_see_cell(a, cannon, LOS_NO_TRANS)) + { + return true; + } + } + + return notify_fail("Not in range of any cannon."); +} diff --git a/crawl-ref/source/target.h b/crawl-ref/source/target.h index 809abca9746..68c0d9fc56b 100644 --- a/crawl-ref/source/target.h +++ b/crawl-ref/source/target.h @@ -53,6 +53,7 @@ class targeter virtual bool set_aim(coord_def a); virtual bool valid_aim(coord_def a) = 0; + virtual bool preferred_aim(coord_def a); virtual bool can_affect_outside_range(); virtual bool can_affect_walls(); @@ -638,3 +639,26 @@ class targeter_gavotte : public targeter_beam private: vector affected_monsters; }; + +class targeter_magnavolt : public targeter_smite +{ +public: + targeter_magnavolt(const actor *act, int range); + bool valid_aim(coord_def a) override; + bool preferred_aim(coord_def a) override; + bool set_aim(coord_def a) override; + aff_type is_affected(coord_def loc) override; +private: + vector beam_targets; + vector beam_paths; +}; + +class targeter_seismic_shockwave : public targeter_smite +{ +public: + targeter_seismic_shockwave(const actor *act, int cannon_range); + bool valid_aim(coord_def a) override; +private: + vector cannon_pos; + int cannon_range; +}; diff --git a/crawl-ref/source/teleport.cc b/crawl-ref/source/teleport.cc index 49ece7001e2..82897693555 100644 --- a/crawl-ref/source/teleport.cc +++ b/crawl-ref/source/teleport.cc @@ -182,14 +182,17 @@ bool monster_space_valid(const monster* mons, coord_def target, } static bool _monster_random_space(const monster* mons, coord_def& target, - bool forbid_sanctuary) + bool forbid_sanctuary, bool away_from_player) { int tries = 0; while (tries++ < 1000) { target = random_in_bounds(); - if (monster_space_valid(mons, target, forbid_sanctuary)) + if (monster_space_valid(mons, target, forbid_sanctuary) + && (!away_from_player || !you.see_cell_no_trans(target))) + { return true; + } } return false; @@ -213,7 +216,7 @@ void mons_relocated(monster* mons) } } -void monster_teleport(monster* mons, bool instan, bool silent) +void monster_teleport(monster* mons, bool instan, bool silent, bool away_from_player) { ASSERT(mons); // XXX: change to monster &mons bool was_seen = !silent && you.can_see(*mons); @@ -239,10 +242,16 @@ void monster_teleport(monster* mons, bool instan, bool silent) coord_def newpos; - if (!_monster_random_space(mons, newpos, !mons->wont_attack())) + if (!_monster_random_space(mons, newpos, !mons->wont_attack(), away_from_player)) { - simple_monster_message(*mons, " flickers for a moment."); - return; + // If we're trying to teleport away from the player and failed, try once + // more without that restriction before giving up. + if (!away_from_player + || !_monster_random_space(mons, newpos, !mons->wont_attack(), false)) + { + simple_monster_message(*mons, " flickers for a moment."); + return; + } } if (!silent) @@ -298,6 +307,19 @@ static coord_def random_space_weighted(actor* moved, actor* target, vector dests; const coord_def tpos = target->pos(); + int max_weight = 0; + + // Calculate weight of our starting pos, so we can compare if the weights of + // other positions will improve it. + int stay_weight; + int stay_dist = (tpos - moved->pos()).rdist(); + if (close) + stay_weight = (LOS_RADIUS - stay_dist) * (LOS_RADIUS - stay_dist); + else + stay_weight = stay_dist; + if (stay_weight < 0) + stay_weight = 0; + for (radius_iterator ri(moved->pos(), LOS_NO_TRANS); ri; ++ri) { if (!valid_blink_destination(*moved, *ri, !allow_sanct) @@ -314,9 +336,25 @@ static coord_def random_space_weighted(actor* moved, actor* target, weight = dist; if (weight < 0) weight = 0; + + if (weight > max_weight) + max_weight = weight; + dests.emplace_back(*ri, weight); } + // Prevent choosing spots below a minimum weight threshold, so long as at + // least one spot exists above that threshold. (Mostly to make sure that + // blink away does actually move things away, if it's possible to do so) + if (max_weight > stay_weight) + { + for (unsigned int i = 0; i < dests.size(); ++i) + { + if (dests[i].second <= stay_weight) + dests[i].second = 0; + } + } + coord_def* choice = random_choose_weighted(dests); return choice ? *choice : coord_def(0, 0); } @@ -339,6 +377,17 @@ void blink_other_close(actor* victim, const coord_def &target) victim->blink_to(dest); } +// Blink the player away from a given monster +bool blink_player_away(monster* caster) +{ + coord_def dest = random_space_weighted(&you, caster, false, false, true); + if (dest.origin()) + return false; + bool success = you.blink_to(dest, false); + ASSERT(success || you.is_constricted()); + return success; +} + // Blink a monster away from the caster. bool blink_away(monster* mon, actor* caster, bool from_seen, bool self_cast) { diff --git a/crawl-ref/source/teleport.h b/crawl-ref/source/teleport.h index 87b93bd52a3..46c6a0b96d5 100644 --- a/crawl-ref/source/teleport.h +++ b/crawl-ref/source/teleport.h @@ -4,6 +4,7 @@ class actor; class monster; void blink_other_close(actor* victim, const coord_def& target); +bool blink_player_away(monster* caster); bool blink_away(monster* mon, bool self_cast = false); bool blink_away(monster* mon, actor* caster, bool from_seen = true, bool self_cast = false); void blink_range(monster &mon); @@ -12,7 +13,8 @@ void mons_relocated(monster* mons); bool monster_blink(monster* mons, bool quiet = false); bool monster_space_valid(const monster* mons, coord_def target, bool forbid_sanctuary); -void monster_teleport(monster* mons, bool instan, bool silent = false); +void monster_teleport(monster* mons, bool instan, bool silent = false, + bool away_from_player = false); vector find_blink_targets(); bool valid_blink_destination(const actor &moved, const coord_def& target, diff --git a/crawl-ref/source/terrain.cc b/crawl-ref/source/terrain.cc index 0fb878a84a8..2e7d2b09af5 100644 --- a/crawl-ref/source/terrain.cc +++ b/crawl-ref/source/terrain.cc @@ -2187,7 +2187,7 @@ bool revert_terrain_change(coord_def pos, terrain_change_type ctype) if (ctype == TERRAIN_CHANGE_BOG) env.map_knowledge(pos).set_feature(newfeat, colour); dungeon_terrain_changed(pos, newfeat, false, true, false, false, - newfeat_flv, newfeat_flv_idx); + newfeat_flv, newfeat_flv_idx); env.grid_colours(pos) = colour; return true; } diff --git a/crawl-ref/source/throw.cc b/crawl-ref/source/throw.cc index c0bb96e88aa..e852df1cc9b 100644 --- a/crawl-ref/source/throw.cc +++ b/crawl-ref/source/throw.cc @@ -637,6 +637,7 @@ void throw_it(quiver::action &a) direction_chooser_args args; args.behaviour = &beh; args.mode = TARG_HOSTILE; + args.self = confirm_prompt_type::cancel; direction(a.target, args); } if (!a.target.isValid || a.target.isCancel) diff --git a/crawl-ref/source/tiledgnbuf.cc b/crawl-ref/source/tiledgnbuf.cc index d2e95138172..af5e2146508 100644 --- a/crawl-ref/source/tiledgnbuf.cc +++ b/crawl-ref/source/tiledgnbuf.cc @@ -167,19 +167,15 @@ void DungeonCellBuffer::add_dngn_tile(int tileidx, int x, int y, void DungeonCellBuffer::add_main_tile(int tileidx, int x, int y) { - tileidx_t base = tileidx_known_base_item(tileidx); + const tileidx_t base = tileidx_known_base_item(tileidx); if (base) m_buf_main.add(base, x, y); m_buf_main.add(tileidx, x, y); } -void DungeonCellBuffer::add_main_tile(int tileidx, int x, int y, int ox, int oy) +void DungeonCellBuffer::add_special_tile(int tileidx, int x, int y, int ox, int oy) { - tileidx_t base = tileidx_known_base_item(tileidx); - if (base) - m_buf_main.add(base, x, y, ox, oy, false); - m_buf_main.add(tileidx, x, y, ox, oy, false); } diff --git a/crawl-ref/source/tiledgnbuf.h b/crawl-ref/source/tiledgnbuf.h index 117b223cfad..43da276a486 100644 --- a/crawl-ref/source/tiledgnbuf.h +++ b/crawl-ref/source/tiledgnbuf.h @@ -26,7 +26,7 @@ class DungeonCellBuffer void add_monster(const monster_info &mon, int x, int y); void add_dngn_tile(int tileidx, int x, int y, bool in_water = false); void add_main_tile(int tileidx, int x, int y); - void add_main_tile(int tileidx, int x, int y, int ox, int oy); + void add_special_tile(int tileidx, int x, int y, int ox, int oy); void add_spell_tile(int tileidx, int x, int y); void add_skill_tile(int tileidx, int x, int y); void add_command_tile(int tileidx, int x, int y); diff --git a/crawl-ref/source/tilemcache.cc b/crawl-ref/source/tilemcache.cc index 65ca7272a54..f1291cef41b 100644 --- a/crawl-ref/source/tilemcache.cc +++ b/crawl-ref/source/tilemcache.cc @@ -483,6 +483,7 @@ bool mcache_monster::get_weapon_offset(tileidx_t mon_tile, case TILEP_MONS_DEEP_ELF_ELEMENTALIST_2: case TILEP_MONS_DEEP_ELF_ELEMENTALIST_3: case TILEP_MONS_FORMICID: + case TILEP_MONS_XAKKRIXIS: *ofs_x = -2; *ofs_y = -1; break; @@ -608,6 +609,8 @@ bool mcache_monster::get_weapon_offset(tileidx_t mon_tile, case TILEP_MONS_ORC_SORCERER: case TILEP_MONS_NERGALLE: case TILEP_MONS_ETTIN: + case TILEP_MONS_STONE_GIANT: + case TILEP_MONS_TITAN: case TILEP_MONS_FROST_GIANT: case TILEP_MONS_FIRE_GIANT: case TILEP_MONS_IRON_GIANT: @@ -698,10 +701,6 @@ bool mcache_monster::get_weapon_offset(tileidx_t mon_tile, *ofs_x = 3; *ofs_y = -4; break; - case TILEP_MONS_LOROCYPROCA: - *ofs_x = -4; - *ofs_y = 6; - break; case TILEP_MONS_HELLION: *ofs_x = -1; *ofs_y = 4; @@ -910,6 +909,7 @@ bool mcache_monster::get_shield_offset(tileidx_t mon_tile, case TILEP_MONS_TENGU_REAVER: case TILEP_MONS_SOJOBO: case TILEP_MONS_FORMICID: + case TILEP_MONS_XAKKRIXIS: case TILEP_MONS_VINE_STALKER: case TILEP_MONS_OCTOPODE: case TILEP_MONS_CHERUB: @@ -1117,11 +1117,6 @@ bool mcache_monster::get_shield_offset(tileidx_t mon_tile, *ofs_y = -3; break; - case TILEP_MONS_LOROCYPROCA: - *ofs_x = -3; - *ofs_y = 0; - break; - case TILEP_MONS_HELLION: *ofs_x = 0; *ofs_y = 4; diff --git a/crawl-ref/source/tilepick.cc b/crawl-ref/source/tilepick.cc index d587182273c..f13f63c4cf8 100644 --- a/crawl-ref/source/tilepick.cc +++ b/crawl-ref/source/tilepick.cc @@ -569,7 +569,8 @@ tileidx_t tileidx_feature(const coord_def &gc) && feat != DNGN_PASSAGE_OF_GOLUBRIA && feat != DNGN_MALIGN_GATEWAY && feat != DNGN_BINDING_SIGIL - && feat != DNGN_UNKNOWN_PORTAL; + && feat != DNGN_UNKNOWN_PORTAL + && feat != DNGN_TREE; // summon forest spell if (override && can_override) return override; @@ -589,6 +590,7 @@ tileidx_t tileidx_feature(const coord_def &gc) } // deliberate fall-through case DNGN_ROCK_WALL: + case DNGN_CLEAR_ROCK_WALL: case DNGN_STONE_WALL: case DNGN_CRYSTAL_WALL: case DNGN_PERMAROCK_WALL: @@ -617,6 +619,16 @@ tileidx_t tileidx_feature(const coord_def &gc) unsigned rc = real_colour(colour, gc); return tile_dngn_coloured(base, rc) + spec; // XXX } + // If there's an unseen change here, the old (remembered) flavour is + // available in the terrain change marker + if (!you.see_cell(gc)) + if (map_marker *mark = env.markers.find(gc, MAT_TERRAIN_CHANGE)) + { + map_terrain_change_marker *marker = + dynamic_cast(mark); + if (marker->flv_old_feature) + return marker->flv_old_feature; + } return tileidx_feature_base(feat); } @@ -2272,6 +2284,9 @@ static const map status_icons = { { MB_RETREATING, TILEI_RETREAT }, { MB_TOUCH_OF_BEOGH, TILEI_TOUCH_OF_BEOGH }, { MB_VENGEANCE_TARGET, TILEI_VENGEANCE_TARGET }, + { MB_MAGNETISED, TILEI_BULLSEYE }, // Placeholder + { MB_RIMEBLIGHT, TILEI_RIMEBLIGHT }, + { MB_ARMED, TILEI_UNDYING_ARMS }, }; set status_icons_for(const monster_info &mons) @@ -2835,7 +2850,8 @@ static tileidx_t _tileidx_misc(const item_def &item) return TILE_MISC_HORN_OF_GERYON; case MISC_BOX_OF_BEASTS: - return TILE_MISC_BOX_OF_BEASTS; + return evoker_charges(item.sub_type) ? TILE_MISC_BOX_OF_BEASTS + : TILE_MISC_BOX_OF_BEASTS_INERT; #if TAG_MAJOR_VERSION == 34 case MISC_CRYSTAL_BALL_OF_ENERGY: @@ -2850,7 +2866,8 @@ static tileidx_t _tileidx_misc(const item_def &item) return TILE_MISC_SACK_OF_SPIDERS; case MISC_GRAVITAMBOURINE: - return TILE_MISC_TAMBOURINE; + return evoker_charges(item.sub_type) ? TILE_MISC_TAMBOURINE + : TILE_MISC_TAMBOURINE_INERT; // Default for summary menus case NUM_MISCELLANY: @@ -3333,7 +3350,7 @@ tileidx_t tileidx_bolt(const bolt &bolt) break; case LIGHTGREY: - if (bolt.name == "stone arrow") + if (bolt.name == "stone arrow" || bolt.name == "stone bullet") return TILE_BOLT_STONE_ARROW + dir; break; @@ -3353,8 +3370,11 @@ tileidx_t tileidx_bolt(const bolt &bolt) break; case ETC_MUTAGENIC: - if (bolt.name == "irradiate" || bolt.name == "unravelling") + if (bolt.name == "irradiate" || bolt.name == "unravelling" + || bolt.name == "burst of quintessence") + { return TILE_BOLT_IRRADIATE; + } break; } diff --git a/crawl-ref/source/tilereg-inv.cc b/crawl-ref/source/tilereg-inv.cc index 401292c6c52..6f9fdd697cf 100644 --- a/crawl-ref/source/tilereg-inv.cc +++ b/crawl-ref/source/tilereg-inv.cc @@ -111,7 +111,7 @@ void InventoryRegion::pack_buffers() draw_number(x, y, item.quantity); if (item.special) - m_buf.add_main_tile(item.special, x, y, 0, 0); + m_buf.add_special_tile(item.special, x, y, 0, 0); if (item.flag & TILEI_FLAG_INVALID && !tiles.is_using_small_layout()) m_buf.add_icons_tile(TILEI_MESH, x, y); diff --git a/crawl-ref/source/timed-effects.cc b/crawl-ref/source/timed-effects.cc index 93ac9c7c3d3..3afd54d28f1 100644 --- a/crawl-ref/source/timed-effects.cc +++ b/crawl-ref/source/timed-effects.cc @@ -601,21 +601,8 @@ void monster::timeout_enchantments(int levels) case ENCH_RESISTANCE: case ENCH_HEXED: case ENCH_IDEALISED: case ENCH_BOUND_SOUL: case ENCH_STILL_WINDS: case ENCH_DRAINED: case ENCH_ANGUISH: case ENCH_FIRE_VULN: case ENCH_SPELL_CHARGED: - lose_ench_levels(entry.second, levels); - break; - case ENCH_SLOW: - if (torpor_slowed()) - { - lose_ench_levels(entry.second, - min(levels, entry.second.degree - 1)); - } - else - { - lose_ench_levels(entry.second, levels); - if (props.exists(TORPOR_SLOWED_KEY)) - props.erase(TORPOR_SLOWED_KEY); - } + lose_ench_levels(entry.second, levels); break; case ENCH_INVIS: diff --git a/crawl-ref/source/travel.cc b/crawl-ref/source/travel.cc index c894727e24a..981de0eec1f 100644 --- a/crawl-ref/source/travel.cc +++ b/crawl-ref/source/travel.cc @@ -336,8 +336,7 @@ static inline bool is_stash(const LevelStashes *ls, const coord_def& p) static bool _monster_blocks_travel(const monster_info *mons) { return mons - && (mons_class_is_stationary(mons->type) - || mons->type == MONS_BOULDER /* dubious */) + && mons_class_is_stationary(mons->type) && !fedhas_passthrough(mons); } diff --git a/crawl-ref/source/util/mon-gen/header.txt b/crawl-ref/source/util/mon-gen/header.txt index 5171408e091..a1cc4aacbb0 100644 --- a/crawl-ref/source/util/mon-gen/header.txt +++ b/crawl-ref/source/util/mon-gen/header.txt @@ -153,6 +153,7 @@ static monsterentry mondata[] = AXED_MON(MONS_GELID_DEMONSPAWN, "gelid demonspawn") AXED_MON(MONS_INFERNAL_DEMONSPAWN, "infernal demonspawn") AXED_MON(MONS_TORTUROUS_DEMONSPAWN, "torturous demonspawn") + AXED_MON(MONS_LOROCYPROCA, "lorocyproca") #endif // Real monsters begin here {dlb}: diff --git a/crawl-ref/source/util/monster/monster-main.cc b/crawl-ref/source/util/monster/monster-main.cc index b491b6be002..32d1cd019bf 100644 --- a/crawl-ref/source/util/monster/monster-main.cc +++ b/crawl-ref/source/util/monster/monster-main.cc @@ -1342,7 +1342,7 @@ int main(int argc, char* argv[]) res2(LIGHTRED, miasma, mon.res_miasma()); res2(LIGHTMAGENTA, neg, mon.res_negative_energy(true)); res2(YELLOW, holy, mon.res_holy_energy()); - res2(YELLOW, foul_flame, mon.res_foul_flame()); + res2(LIGHTMAGENTA, foul_flame, mon.res_foul_flame()); res2(LIGHTMAGENTA, torm, mon.res_torment()); res2(LIGHTBLUE, vortex, mon.res_polar_vortex()); res2(LIGHTRED, napalm, mon.res_sticky_flame()); diff --git a/crawl-ref/source/webserver/static/scripts/app.js b/crawl-ref/source/webserver/static/scripts/app.js index d929474624e..feff2b0b8e4 100644 --- a/crawl-ref/source/webserver/static/scripts/app.js +++ b/crawl-ref/source/webserver/static/scripts/app.js @@ -5,7 +5,8 @@ require.config({ }, paths: { 'jquery': '/static/scripts/contrib/jquery' - } + }, + waitSeconds: 0 }); require(['client']); diff --git a/crawl-ref/source/webserver/static/scripts/client.js b/crawl-ref/source/webserver/static/scripts/client.js index 2a557f85bc8..cbeab9a8197 100644 --- a/crawl-ref/source/webserver/static/scripts/client.js +++ b/crawl-ref/source/webserver/static/scripts/client.js @@ -23,6 +23,7 @@ function (exports, $, key_conversion, chat, comm) { var current_hash; var exit_reason, exit_message, exit_dump; var normal_exit = ["saved", "cancel", "quit", "won", "bailed out", "dead"]; + next_loading_img = 0; var send_message = comm.send_message; @@ -1046,14 +1047,28 @@ function (exports, $, key_conversion, chat, comm) { }); } + function select_next_loading_img() + { + var imgs = $("#loader img"); + next_loading_img = Math.floor(Math.random() * imgs.length); + // remove the lazy loading attribute, causing the image to load. + // (N.b. I didn't find standards-level documentation indicating that + // this must do anything, but it does have the desired behavior on + // firefox/chrome at the time of testing.) + // This never bothers to add back the lazy loading attribute, under + // the assumption that all images will be cached. + if ($(imgs[next_loading_img]).attr("loading")) + $(imgs[next_loading_img]).removeAttr("loading"); + } + function show_loading_screen() { - if (current_layer == "loader") return; + if (current_layer == "loader") + return; var imgs = $("#loader img"); + next_loading_img = Math.min(imgs.length - 1, next_loading_img); // sanity check imgs.hide(); - var count = imgs.length; - var rand_index = Math.floor(Math.random() * count); - $(imgs[rand_index]).show(); + $(imgs[next_loading_img]).show(); set_layer("loader"); } @@ -1094,6 +1109,8 @@ function (exports, $, key_conversion, chat, comm) { exit_reason = null; exit_message = null; exit_dump = null; + if (current_user) + select_next_loading_img(); if ( $("#reset_pw").length ) { diff --git a/crawl-ref/source/webserver/templates/client.html b/crawl-ref/source/webserver/templates/client.html index 973c1f8d463..513c1c57199 100644 --- a/crawl-ref/source/webserver/templates/client.html +++ b/crawl-ref/source/webserver/templates/client.html @@ -260,44 +260,44 @@
Loading...
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
> xp_evoker_data = { - { MISC_PHIAL_OF_FLOODS, { "phial_debt", 10, 1 , + { MISC_PHIAL_OF_FLOODS, { "phial_debt", 10, 1, { "You hear a faint sloshing from %s as it returns to readiness.", "Water glimmers in %s, now refilled and ready to use.", }, }}, @@ -39,6 +39,7 @@ static const unordered_map> xp_evoke }}, { MISC_CONDENSER_VANE, { "condenser_debt", 10, 1 } }, { MISC_GRAVITAMBOURINE, { "tambourine_debt", 10, 2, - { "%s jingles faintly as it regains its power." }, + { "%s jingles faintly as it regains its power.", + "%s shakes itself petulantly as it silently regains its power." }, }}, }; diff --git a/crawl-ref/source/zap-data.h b/crawl-ref/source/zap-data.h index 7c1661a724a..a3dfe4ffef9 100644 --- a/crawl-ref/source/zap-data.h +++ b/crawl-ref/source/zap-data.h @@ -309,7 +309,7 @@ static const zap_info zap_data[] = ZAP_PERMAFROST_ERUPTION_COLD, "piercing cold", 200, - new calcdice_calculator<4, 8, 1, 2>, + new calcdice_calculator<4, 7, 2, 5>, new tohit_calculator<40>, nullptr, nullptr, @@ -325,7 +325,7 @@ static const zap_info zap_data[] = ZAP_PERMAFROST_ERUPTION_EARTH, "mass of rock", 200, - new calcdice_calculator<4, 8, 1, 2>, + new calcdice_calculator<4, 7, 2, 5>, new tohit_calculator<40>, nullptr, nullptr, @@ -2048,6 +2048,118 @@ _mon_hex_zap(ZAP_VITRIFY, BEAM_VITRIFY), DCHAR_FIRED_ZAP, false, true, -} +}, + +{ + ZAP_MAGNAVOLT, + "arc of electricity", + 200, + new dicedef_calculator<3, 9, 1, 8>, + new tohit_calculator<11, 1, 20>, + nullptr, + nullptr, + LIGHTCYAN, + false, + BEAM_ELECTRICITY, + DCHAR_FIRED_ZAP, + true, + false, +}, + +{ + ZAP_FULSOME_FUSILLADE, + "volatile concoction", + 200, + new dicedef_calculator<3, 4, 1, 9>, + new tohit_calculator, + nullptr, + nullptr, + WHITE, + false, + BEAM_MMISSILE, + DCHAR_FIRED_ZAP, + false, + false, +}, + +{ + ZAP_RIMEBLIGHT, + "", + 200, + nullptr, + new tohit_calculator<0, 1, 3>, + nullptr, + new tohit_calculator<0, 1, 3>, + BLUE, + true, + BEAM_RIMEBLIGHT, + NUM_DCHAR_TYPES, + false, + false, +}, + +{ + ZAP_RIMEBLIGHT_SHARDS, + "shards of blighted ice", + 200, + new dicedef_calculator<3, 6, 1, 11>, + new tohit_calculator, + nullptr, + nullptr, + ETC_ICE, + false, + BEAM_ICE, + DCHAR_FIRED_BURST, + false, + true, +}, + +{ + ZAP_SEISMIC_SHOCKWAVE, + "seismic shockwave", + 200, + new dicedef_calculator<6, 4, 1, 8>, + new tohit_calculator, + nullptr, + nullptr, + ETC_EARTH, + false, + BEAM_MMISSILE, + DCHAR_FIRED_BURST, + false, + true, +}, + +{ + ZAP_STONE_BULLET, + "stone bullet", + 50, + nullptr, + nullptr, + new dicedef_calculator<3, 2, 1, 18>, + new tohit_calculator<13, 1, 35>, + LIGHTGREY, + false, + BEAM_MMISSILE, + DCHAR_FIRED_MISSILE, + false, + false, +}, + +{ + ZAP_FLASHING_BALESTRA, + "flashing steel", + 200, + nullptr, + nullptr, + new dicedef_calculator<3, 7, 1, 12>, + new tohit_calculator<20, 1, 25>, + LIGHTCYAN, + false, + BEAM_MMISSILE, + DCHAR_FIRED_MISSILE, + false, + false, +}, }; diff --git a/crawl-ref/source/zap-type.h b/crawl-ref/source/zap-type.h index 35603d73f54..06f993f396d 100644 --- a/crawl-ref/source/zap-type.h +++ b/crawl-ref/source/zap-type.h @@ -142,5 +142,12 @@ enum zap_type ZAP_PERMAFROST_ERUPTION_COLD, ZAP_PERMAFROST_ERUPTION_EARTH, ZAP_GRAVITAS, + ZAP_MAGNAVOLT, + ZAP_FULSOME_FUSILLADE, + ZAP_RIMEBLIGHT, + ZAP_RIMEBLIGHT_SHARDS, + ZAP_SEISMIC_SHOCKWAVE, + ZAP_STONE_BULLET, + ZAP_FLASHING_BALESTRA, NUM_ZAPS };