1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
|
/**
* @file
* @brief Cloud creating spells.
**/
#include "AppHdr.h"
#include "spl-clouds.h"
#include <algorithm>
#include "cloud.h"
#include "coord.h"
#include "coordit.h"
#include "corpse.h"
#include "directn.h"
#include "english.h"
#include "env.h"
#include "fprop.h"
#include "fight.h"
#include "fineff.h" // explosion_fineff, etc
#include "items.h"
#include "level-state-type.h"
#include "message.h"
#include "mon-behv.h" // ME_WHACK
#include "ouch.h"
#include "random-pick.h"
#include "shout.h"
#include "spl-util.h"
#include "target.h"
#include "terrain.h"
spret cast_putrefaction(monster* target, int pow, bool fail)
{
fail_check();
// Place one miasma cloud immediately beneath the target.
place_cloud(CLOUD_MIASMA, target->pos(), random_range(5, 9), &you);
// Then start a spread of fiant miasma that will become proper miasma a
// turn later.
map_cloud_spreader_marker *marker =
new map_cloud_spreader_marker(target->pos(), CLOUD_FAINT_MIASMA, 7,
random_range(18, 28), 5, 2, &you);
// Start the cloud at radius 1, regardless of the speed of the killing blow
marker->speed_increment -= you.time_taken - 7;
env.markers.add(marker);
env.markers.clear_need_activate();
mprf("Rot billows forth from %s wounds!", target->name(DESC_ITS).c_str());
drain_player(75 - div_rand_round(pow * 4, 10), true, true);
return spret::success;
}
spret kindle_blastmotes(int pow, bool fail)
{
if (cloud_at(you.pos()))
{
mpr("There's already a cloud here!");
return spret::abort;
}
fail_check();
// Really should be per-cloud, but skeptical people are changing power
// between successive blastmote casts that often.
you.props[BLASTMOTE_POWER_KEY] = pow;
// Longish duration to support setting up silly traps.
you.props[BLASTMOTE_IMMUNE_KEY] = true;
place_cloud(CLOUD_BLASTMOTES, you.pos(), random_range(20, 30), &you);
mpr("A cloud of volatile blastmotes flares up around you! Run!");
return spret::success;
}
void explode_blastmotes_at(coord_def p)
{
// Assumes all blastmotes are created by the player.
// We could fix this in future by checking the 'killer'
// associated with the cloud being deleted.
delete_cloud(p);
bolt beam;
zappy(ZAP_BLASTMOTE, you.props[BLASTMOTE_POWER_KEY], false, beam);
beam.target = p;
beam.source = p;
beam.source_id = MID_PLAYER;
beam.attitude = ATT_FRIENDLY;
beam.thrower = KILL_YOU_MISSILE;
beam.is_explosion = true;
beam.ex_size = 1;
const string boom = "The cloud of blastmotes explodes!";
const string sanct = "By Zin's power, the fiery explosion is contained.";
schedule_explosion_fineff(beam, boom, sanct, EXPLOSION_FINEFF_CONCUSSION,
nullptr, "");
}
spret cast_freezing_cloud(int pow, bolt &beam, bool fail)
{
if (grid_distance(beam.target, you.pos()) > beam.range
|| !in_bounds(beam.target))
{
mpr("That is beyond the maximum range.");
return spret::abort;
}
if (cell_is_solid(beam.target))
{
const char *feat = feat_type_name(env.grid(beam.target));
mprf("You can't place clouds on %s.", article_a(feat).c_str());
return spret::abort;
}
if (is_sanctuary(beam.target))
{
mpr("You can't place harmful clouds in a sanctuary.");
return spret::abort;
}
beam.thrower = KILL_YOU;
beam.hit = AUTOMATIC_HIT;
beam.damage = CONVENIENT_NONZERO_DAMAGE;
beam.use_target_as_pos = true;
beam.origin_spell = SPELL_FREEZING_CLOUD;
beam.flavour = BEAM_COLD;
beam.name = "freezing blast";
player_beam_tracer tracer;
beam.affect_endpoint(tracer);
if (cancel_beam_prompt(beam, tracer))
return spret::abort;
fail_check();
big_cloud(CLOUD_COLD, &you, beam.target, pow, 8 + random2(3), -1);
noisy(spell_effect_noise(SPELL_FREEZING_CLOUD), beam.target);
return spret::success;
}
/*
* A cloud_func that places an individual cloud as part of a cloud area. This
* function is called by apply_area_cloud();
*
* @param where The location of the cloud.
* @param pow The spellpower of the spell placing the clouds, which
* determines how long the cloud will last.
* @param spread_rate How quickly the cloud spreads.
* @param ctype The type of cloud to place.
* @param agent Any agent that may have caused the cloud. If this is the
* player, god conducts are applied.
* @param excl_rad How large of an exclusion radius to make around the
* cloud.
* @returns The number of clouds made, which is always 1.
*
*/
static int _make_a_normal_cloud(coord_def where, int pow, int spread_rate,
cloud_type ctype, const actor *agent,
int excl_rad)
{
place_cloud(ctype, where,
(3 + random2(pow / 4) + random2(pow / 4) + random2(pow / 4)),
agent, spread_rate, excl_rad);
return 1;
}
/*
* Make a large area of clouds centered on a given place. This never creates
* player exclusions.
*
* @param ctype The type of cloud to place.
* @param agent Any agent that may have caused the cloud. If this is the
* player, god conducts are applied.
* @param where The location of the cloud.
* @param pow The spellpower of the spell placing the clouds, which
* determines how long the cloud will last.
* @param size How large a radius of clouds to place from the `where'
* argument.
* @param spread_rate How quickly the cloud spreads.
*/
void big_cloud(cloud_type cl_type, const actor *agent,
const coord_def& where, int pow, int size, int spread_rate)
{
// The starting point _may_ be a place no cloud can be placed on.
apply_area_cloud(_make_a_normal_cloud, where, pow, size,
cl_type, agent, spread_rate, -1);
}
void holy_flames(monster* caster, actor* defender)
{
const coord_def pos = defender->pos();
int cloud_count = 0;
const int dur = 8 + random2avg(caster->get_hit_dice() * 3, 2);
for (adjacent_iterator ai(pos); ai; ++ai)
{
if (monster_at(*ai))
continue;
if (place_cloud(CLOUD_HOLY, *ai, dur, caster))
cloud_count++;
}
if (cloud_count)
{
if (defender->is_player())
mpr("Blessed fire suddenly surrounds you!");
else
simple_monster_message(*defender->as_monster(),
" is surrounded by blessed fire!");
}
}
spret scroll_of_poison(bool scroll_unknown)
{
int created = 0;
bool unknown_unseen = false;
for (radius_iterator ri(you.pos(), LOS_NO_TRANS); ri; ++ri)
{
if (const actor* act = actor_at(*ri))
{
unknown_unseen = unknown_unseen || !you.can_see(*act);
continue;
}
if (place_cloud(CLOUD_POISON, *ri, 10 + random2(11), &you))
++created;
}
if (created > 0)
{
mpr("The air fills with toxic fumes!");
return spret::success;
}
if (!scroll_unknown && !unknown_unseen)
{
mpr("There's no open space to fill with poison.");
return spret::abort;
}
canned_msg(MSG_NOTHING_HAPPENS);
return spret::fail;
}
|