diff options
Diffstat (limited to 'crawl-ref')
-rw-r--r-- | crawl-ref/source/beam.cc | 24 | ||||
-rw-r--r-- | crawl-ref/source/dat/database/monspell.txt | 8 | ||||
-rw-r--r-- | crawl-ref/source/enum.h | 6 | ||||
-rw-r--r-- | crawl-ref/source/fight.cc | 3 | ||||
-rw-r--r-- | crawl-ref/source/main.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/makefile.obj | 1 | ||||
-rw-r--r-- | crawl-ref/source/mgen_data.h | 2 | ||||
-rw-r--r-- | crawl-ref/source/mon-act.cc | 9 | ||||
-rw-r--r-- | crawl-ref/source/mon-cast.cc | 9 | ||||
-rw-r--r-- | crawl-ref/source/mon-data.h | 13 | ||||
-rw-r--r-- | crawl-ref/source/mon-project.cc | 251 | ||||
-rw-r--r-- | crawl-ref/source/mon-project.h | 17 | ||||
-rw-r--r-- | crawl-ref/source/mon-util.cc | 3 | ||||
-rw-r--r-- | crawl-ref/source/spl-book.cc | 2 | ||||
-rw-r--r-- | crawl-ref/source/spl-cast.cc | 8 | ||||
-rw-r--r-- | crawl-ref/source/spl-data.h | 13 |
16 files changed, 363 insertions, 8 deletions
diff --git a/crawl-ref/source/beam.cc b/crawl-ref/source/beam.cc index f2e247b4b8..23f727ddec 100644 --- a/crawl-ref/source/beam.cc +++ b/crawl-ref/source/beam.cc @@ -1353,7 +1353,23 @@ const zap_info zap_data[] = { false, false, 0 - } + }, + + { + ZAP_IOOD, + "0", + 200, + NULL, + new tohit_calculator<AUTOMATIC_HIT>, + WHITE, + false, + BEAM_NUKE, + DCHAR_FIRED_ZAP, + true, + true, + false, + 0 + }, }; static void _zappy(zap_type z_type, int power, bolt &pbolt) @@ -5988,6 +6004,12 @@ bool bolt::nasty_to(const monsters *mon) const if (flavour == BEAM_HOLY) return (mon->res_holy_energy(agent()) <= 0); + // The orbs are made of pure disintegration energy. This also has the side + // effect of not stopping us from firing further orbs when the previous one + // is still flying. + if (flavour == BEAM_DISINTEGRATION || flavour == BEAM_NUKE) + return (mon->type != MONS_ORB_OF_DESTRUCTION); + // Take care of other non-enchantments. if (!is_enchantment()) return (true); diff --git a/crawl-ref/source/dat/database/monspell.txt b/crawl-ref/source/dat/database/monspell.txt index 0ce22a12e5..ce3a29d30e 100644 --- a/crawl-ref/source/dat/database/monspell.txt +++ b/crawl-ref/source/dat/database/monspell.txt @@ -92,6 +92,14 @@ Symbol of Torment cast unseen Symbol of Torment cast @The_something@ calls on the powers of Hell! +%%%% +Iskenderun's Orb of Destruction cast + +@The_monster@ conjures a glowing orb. + +@The_monster@ conjures an orb of pure magic. + +@The_monster@ launches a ball of destructive magic. ##################################################### # Individual innate spells (breathing and spitting). ##################################################### diff --git a/crawl-ref/source/enum.h b/crawl-ref/source/enum.h index 94d2b6328d..fe079d395f 100644 --- a/crawl-ref/source/enum.h +++ b/crawl-ref/source/enum.h @@ -1671,8 +1671,8 @@ enum monster_type // (int) menv[].type MONS_KOBOLD_DEMONOLOGIST, MONS_ORC_WIZARD, MONS_ORC_KNIGHT, // 55 - //MONS_WORM_TAIL = 56, // deprecated and now officially removed {dlb} - MONS_WYVERN = 57, // 57 + MONS_ORB_OF_DESTRUCTION, // a projectile, not a real mon + MONS_WYVERN, MONS_BIG_KOBOLD, MONS_GIANT_EYEBALL, MONS_WIGHT, // 60 @@ -2912,6 +2912,7 @@ enum spell_type SPELL_BLINK_CLOSE, SPELL_BLINK_RANGE, SPELL_BLINK_AWAY, + SPELL_IOOD, NUM_SPELLS }; @@ -3159,6 +3160,7 @@ enum zap_type ZAP_SLIME, ZAP_PORKALATOR, ZAP_SLEEP, + ZAP_IOOD, NUM_ZAPS }; diff --git a/crawl-ref/source/fight.cc b/crawl-ref/source/fight.cc index 3ddd3ba835..fe28e2ade4 100644 --- a/crawl-ref/source/fight.cc +++ b/crawl-ref/source/fight.cc @@ -4291,7 +4291,8 @@ bool melee_attack::mons_attack_mons() bool melee_attack::mons_self_destructs() { if (attacker->id() == MONS_GIANT_SPORE - || attacker->id() == MONS_BALL_LIGHTNING) + || attacker->id() == MONS_BALL_LIGHTNING + || attacker->id() == MONS_ORB_OF_DESTRUCTION) { attacker_as_monster()->hit_points = -1; // Do the explosion right now. diff --git a/crawl-ref/source/main.cc b/crawl-ref/source/main.cc index d3feb08c00..2a68a7f624 100644 --- a/crawl-ref/source/main.cc +++ b/crawl-ref/source/main.cc @@ -4618,7 +4618,7 @@ static void _compile_time_asserts() COMPILE_CHECK(SP_VAMPIRE == 30 , c3); COMPILE_CHECK(SPELL_DEBUGGING_RAY == 103 , c4); COMPILE_CHECK(SPELL_RETURNING_AMMUNITION == 162 , c5); - COMPILE_CHECK(NUM_SPELLS == 211 , c6); + COMPILE_CHECK(NUM_SPELLS == 212 , c6); //jmf: NEW ASSERTS: we ought to do a *lot* of these COMPILE_CHECK(NUM_SPECIES < SP_UNKNOWN , c7); diff --git a/crawl-ref/source/makefile.obj b/crawl-ref/source/makefile.obj index fd38cf9783..633a9d2a54 100644 --- a/crawl-ref/source/makefile.obj +++ b/crawl-ref/source/makefile.obj @@ -112,6 +112,7 @@ mon-movetarget.o \ mon-pathfind.o \ mon-pick.o \ mon-place.o \ +mon-project.o \ mon-util.o \ mon-speak.o \ mon-stuff.o \ diff --git a/crawl-ref/source/mgen_data.h b/crawl-ref/source/mgen_data.h index 6934100ede..d2794796ec 100644 --- a/crawl-ref/source/mgen_data.h +++ b/crawl-ref/source/mgen_data.h @@ -132,7 +132,7 @@ struct mgen_data non_actor_summoner(nas), props() { ASSERT(summon_type == 0 || (abj >= 1 && abj <= 6) - || mt == MONS_BALL_LIGHTNING); + || mt == MONS_BALL_LIGHTNING || mt == MONS_ORB_OF_DESTRUCTION); } bool permit_bands() const { return (flags & MG_PERMIT_BANDS); } diff --git a/crawl-ref/source/mon-act.cc b/crawl-ref/source/mon-act.cc index 16c025e217..62df9c963b 100644 --- a/crawl-ref/source/mon-act.cc +++ b/crawl-ref/source/mon-act.cc @@ -33,6 +33,7 @@ #include "mon-cast.h" #include "mon-iter.h" #include "mon-place.h" +#include "mon-project.h" #include "mgen_data.h" #include "coord.h" #include "mon-stuff.h" @@ -1708,6 +1709,14 @@ static void _handle_monster_move(monsters *monster) } old_energy = monster->speed_increment; + if (mons_is_projectile(monster->type)) + { + if (iood_act(*monster)) + return; + monster->lose_energy(EUT_MOVE); + continue; + } + monster->shield_blocks = 0; cloud_type cl_type; diff --git a/crawl-ref/source/mon-cast.cc b/crawl-ref/source/mon-cast.cc index 8a6397d99f..4f86ede2f8 100644 --- a/crawl-ref/source/mon-cast.cc +++ b/crawl-ref/source/mon-cast.cc @@ -20,6 +20,7 @@ #include "mon-behv.h" #include "mon-iter.h" #include "mon-place.h" +#include "mon-project.h" #include "terrain.h" #include "mgen_data.h" #include "coord.h" @@ -669,6 +670,11 @@ bolt mons_spells( monsters *mons, spell_type spell_cast, int power, beam.is_beam = true; break; + case SPELL_IOOD: // tracer only + beam.flavour = BEAM_NUKE; + beam.is_beam = true; + break; + default: if (check_validity) { @@ -2168,6 +2174,9 @@ void mons_cast(monsters *monster, bolt &pbolt, spell_type spell_cast, monster->foe, 0, god)); } return; + case SPELL_IOOD: + cast_iood(monster, 6 * monster->hit_dice, &pbolt); + return; } // If a monster just came into view and immediately cast a spell, diff --git a/crawl-ref/source/mon-data.h b/crawl-ref/source/mon-data.h index e9094329bd..c9f8f8d90d 100644 --- a/crawl-ref/source/mon-data.h +++ b/crawl-ref/source/mon-data.h @@ -4283,6 +4283,19 @@ static monsterentry mondata[] = { MONUSE_NOTHING, MONEAT_NOTHING, SIZE_LITTLE }, +{ + MONS_ORB_OF_DESTRUCTION, '*', WHITE, "orb of destruction", + M_INSUBSTANTIAL | M_GLOWS | M_NO_EXP_GAIN, + mrd(MR_RES_FIRE | MR_RES_HELLFIRE | MR_RES_POISON | MR_RES_COLD, 3) + | MR_RES_ELEC | MR_RES_STICKY_FLAME | MR_RES_ACID, + 0, 0, MONS_ORB_OF_DESTRUCTION, MONS_ORB_OF_DESTRUCTION, MH_NONLIVING, MAG_IMMUNE, + { AT_NO_ATK, AT_NO_ATK, AT_NO_ATK, AT_NO_ATK }, + { 5, 0, 0, 50 }, + 0, 10, MST_NO_SPELLS, CE_NOCORPSE, Z_NOZOMBIE, S_SILENT, + I_PLANT, HT_LAND, FL_LEVITATE, 30, DEFAULT_ENERGY, + MONUSE_NOTHING, MONEAT_NOTHING, SIZE_LITTLE +}, + // other symbols { MONS_VAPOUR, '#', LIGHTGREY, "vapour", diff --git a/crawl-ref/source/mon-project.cc b/crawl-ref/source/mon-project.cc new file mode 100644 index 0000000000..32259e6b10 --- /dev/null +++ b/crawl-ref/source/mon-project.cc @@ -0,0 +1,251 @@ +/* + * File: mon-project.cc + * Summary: Slow projectiles, done as monsters. + * Written by: Adam Borowski + */ + +#include "AppHdr.h" + +#include "mon-project.h" + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <cmath> + +#include "externs.h" + +#include "cloud.h" +#include "directn.h" +#include "coord.h" +#include "env.h" +#include "mgen_data.h" +#include "mon-place.h" +#include "mon-stuff.h" +#include "shout.h" +#include "stuff.h" +#include "terrain.h" +#include "viewchar.h" + +bool mons_is_projectile(monster_type mt) +{ + return (mt == MONS_ORB_OF_DESTRUCTION); +} + +bool cast_iood(actor *caster, int pow, bolt *beam) +{ + int mtarg = mgrd(beam->target); + + int mind = mons_place(mgen_data(MONS_ORB_OF_DESTRUCTION, + (caster->atype() == ACT_PLAYER) ? BEH_FRIENDLY : + ((monsters*)caster)->wont_attack() ? BEH_FRIENDLY : BEH_HOSTILE, + caster, + 0, + SPELL_IOOD, + coord_def(-1, -1), + (mtarg != NON_MONSTER) ? mtarg : + (you.pos() == beam->target) ? MHITYOU : MHITNOT, + 0, + GOD_NO_GOD)); + if (mind == -1) + { + canned_msg(MSG_NOTHING_HAPPENS); + return (false); + } + + monsters &mon = menv[mind]; + const coord_def pos = caster->pos(); + mon.props["iood_x"] = (float)pos.x; + mon.props["iood_y"] = (float)pos.y; + mon.props["iood_vx"] = (float)(beam->target.x - pos.x); + mon.props["iood_vy"] = (float)(beam->target.y - pos.y); + mon.props["iood_kc"].get_byte() = (caster->atype() == ACT_PLAYER) ? KC_YOU : + ((monsters*)caster)->wont_attack() ? KC_FRIENDLY : KC_OTHER; + mon.flags &= ~MF_JUST_SUMMONED; + + // Move away from the caster's square. + iood_act(mon, true); + mon.lose_energy(EUT_MOVE); + return (true); +} + +static void _normalize(float &x, float &y) +{ + const float d = sqrt(x*x + y*y); + if (d <= 0.000001) + return; + x/=d; + y/=d; +} + +// angle measured in chord length +static bool _in_front(float vx, float vy, float dx, float dy, float angle) +{ + return ((dx-vx)*(dx-vx) + (dy-vy)*(dy-vy) <= (angle*angle)); +} + +void _iood_dissipate(monsters &mon) +{ + simple_monster_message(&mon, " dissipates."); + dprf("iood: dissipating"); + monster_die(&mon, KILL_DISMISSED, NON_MONSTER); +} + +bool _iood_hit(monsters &mon, const coord_def &pos, bool big_boom = false) +{ + bolt beam; + beam.name = "orb of destruction"; + beam.flavour = BEAM_NUKE; + beam.attitude = mon.attitude; + beam.thrower = (mon.props["iood_kc"].get_byte() == KC_YOU) + ? KILL_YOU_MISSILE : KILL_MON_MISSILE; + beam.colour = WHITE; + beam.type = dchar_glyph(DCHAR_FIRED_BURST); + beam.range = 1; + beam.source = pos; + beam.target = pos; + beam.hit = AUTOMATIC_HIT; + beam.damage = dice_def(3, 20); + beam.ex_size = 1; + if (big_boom) + beam.explode(true, true); + else + beam.fire(); + + monster_die(&mon, KILL_DISMISSED, NON_MONSTER); + return (true); +} + +// returns true if the orb is gone +bool iood_act(monsters &mon, bool no_trail) +{ + ASSERT(mons_is_projectile(mon.type)); + + float x = mon.props["iood_x"]; + float y = mon.props["iood_y"]; + float vx = mon.props["iood_vx"]; + float vy = mon.props["iood_vy"]; + + dprf("iood_act: pos (%d,%d) rpos (%f,%f) v (%f,%f)", + mon.pos().x, mon.pos().y, + x, y, vx, vy); + + if (!vx && !vy) // not initialized + { + _iood_dissipate(mon); + return (true); + } + + coord_def target(-1, -1); + if (mon.foe == MHITYOU) + target = you.pos(); + else if (invalid_monster_index(mon.foe)) + ; + else if (invalid_monster_type(menv[mon.foe].type)) + { + // Our target is gone. Since picking a new one would require + // intelligence, the orb continues on a ballistic course. + mon.foe = MHITNOT; + } + else + target = menv[mon.foe].pos(); + + _normalize(vx, vy); + + if (target != coord_def(-1, -1)) + { + float dx = target.x - x; + float dy = target.y - y; + _normalize(dx, dy); + + // Special case: + // Moving diagonally when the orb is just about to hit you + // 2 + // ->*1 + // (from 1 to 2) would be a guaranteed escape. This may be + // realistic (strafing!), but since the game has no non-cheesy + // means of waiting a small fraction of a turn, we don't want it. + const int old_t_pos = mon.props["iood_tpos"].get_short(); + if (old_t_pos && old_t_pos != (256 * target.x + target.y) + && (coord_def(round(x), round(y)) - target).rdist() <= 1 + // ... but following an orb is ok. + && _in_front(vx, vy, dx, dy, 1.5)) // ~97 degrees + { + vx = dx; + vy = dy; + } + mon.props["iood_tpos"].get_short() = 256 * target.x + target.y; + + if (!_in_front(vx, vy, dx, dy, 0.5)) // ~29 degrees + { + float ax, ay; + if (dy*vx < dx*vy) + ax = vy, ay = -vx, dprf("iood: veering left"); + else + ax = -vy, ay = vx, dprf("iood: veering right"); + vx += ax * 0.3; + vy += ay * 0.3; + } + else + dprf("iood: keeping course"); + + _normalize(vx, vy); + mon.props["iood_vx"] = vx; + mon.props["iood_vy"] = vy; + } + + x += vx; + y += vy; + + mon.props["iood_x"] = x; + mon.props["iood_y"] = y; + + coord_def pos(round(x), round(y)); + if (!in_bounds(pos)) + { + _iood_dissipate(mon); + return (true); + } + + if (pos == mon.pos()) + return (false); + + actor *victim = actor_at(pos); + if (cell_is_solid(pos) || victim) + { + if (cell_is_solid(pos)) + { + if (you.see_cell(pos)) + mprf("%s hits %s", mon.name(DESC_CAP_THE, true).c_str(), + feature_description(pos, false, DESC_NOCAP_A).c_str()); + } + if (victim && mons_is_projectile(victim->id())) + { + if (mon.observable()) + mpr("The orbs collide in a blinding explosion!"); + else + noisy(40, pos, "You hear a loud magical explosion!"); + monster_die((monsters*)victim, KILL_DISMISSED, NON_MONSTER); + _iood_hit(mon, pos, true); + return (true); + } + + if (_iood_hit(mon, pos)) + return (true); + } + + if (!no_trail) + { + place_cloud(CLOUD_TLOC_ENERGY, mon.pos(), + 2 + random2(3), mon.kill_alignment(), + KILL_MON_MISSILE); + } + + if (!mon.move_to_pos(pos)) + { + _iood_dissipate(mon); + return (true); + } + + return (false); +} diff --git a/crawl-ref/source/mon-project.h b/crawl-ref/source/mon-project.h new file mode 100644 index 0000000000..a57afbbd80 --- /dev/null +++ b/crawl-ref/source/mon-project.h @@ -0,0 +1,17 @@ +/* + * File: mon-project.cc + * Summary: Slow projectiles, done as monsters. + * Written by: Adam Borowski + */ + +#ifndef MON_PROJECT_H +#define MON_PROJECT_H + +#include "beam.h" + +bool mons_is_projectile(monster_type mt); + +bool cast_iood(actor *caster, int pow, bolt *beam); +bool iood_act(monsters &mon, bool no_trail = false); + +#endif diff --git a/crawl-ref/source/mon-util.cc b/crawl-ref/source/mon-util.cc index 9420d1d78c..17ed88bd8e 100644 --- a/crawl-ref/source/mon-util.cc +++ b/crawl-ref/source/mon-util.cc @@ -2010,7 +2010,8 @@ bool mons_wields_two_weapons(const monsters *mon) bool mons_self_destructs(const monsters *m) { - return (m->type == MONS_GIANT_SPORE || m->type == MONS_BALL_LIGHTNING); + return (m->type == MONS_GIANT_SPORE || m->type == MONS_BALL_LIGHTNING + || m->type == MONS_ORB_OF_DESTRUCTION); } int mons_base_damage_brand(const monsters *m) diff --git a/crawl-ref/source/spl-book.cc b/crawl-ref/source/spl-book.cc index f5f89f44b6..d5810bc106 100644 --- a/crawl-ref/source/spl-book.cc +++ b/crawl-ref/source/spl-book.cc @@ -518,12 +518,12 @@ static spell_type spellbook_template_array[][SPELLBOOK_SIZE] = // Book of Annihilations - Vehumet special {SPELL_ISKENDERUNS_MYSTIC_BLAST, SPELL_POISON_ARROW, + SPELL_IOOD, SPELL_CHAIN_LIGHTNING, SPELL_LEHUDIBS_CRYSTAL_SPEAR, SPELL_ICE_STORM, SPELL_FIRE_STORM, SPELL_NO_SPELL, - SPELL_NO_SPELL, }, // Book of Demonology - Vehumet special diff --git a/crawl-ref/source/spl-cast.cc b/crawl-ref/source/spl-cast.cc index 7a65c84f2e..21f7c7f290 100644 --- a/crawl-ref/source/spl-cast.cc +++ b/crawl-ref/source/spl-cast.cc @@ -37,6 +37,7 @@ #include "message.h" #include "mon-cast.h" #include "mon-place.h" +#include "mon-project.h" #include "mon-stuff.h" #include "mutation.h" #include "ouch.h" @@ -1512,6 +1513,13 @@ spret_type your_spells(spell_type spell, int powc, bool allow_fail) return (SPRET_ABORT); break; + case SPELL_IOOD: + if (!player_tracer(ZAP_IOOD, powc, beam)) + return (SPRET_ABORT); + if (!cast_iood(&you, powc, &beam)) + return (SPRET_ABORT); + break; + // Clouds and explosions. case SPELL_MEPHITIC_CLOUD: if (!stinking_cloud(powc, beam)) diff --git a/crawl-ref/source/spl-data.h b/crawl-ref/source/spl-data.h index 27f1ea2632..52a94a8786 100644 --- a/crawl-ref/source/spl-data.h +++ b/crawl-ref/source/spl-data.h @@ -2590,6 +2590,19 @@ }, { + SPELL_IOOD, "Iskenderun's Orb of Destruction", + SPTYP_CONJURATION, + SPFLAG_DIR_OR_TARGET | SPFLAG_NOT_SELF, + 7, + 200, + 9, 9, + 0, + NULL, + true, + false +}, + +{ SPELL_NO_SPELL, "nonexistent spell", 0, SPFLAG_TESTING, |