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 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
|
/**
* @file
* @brief Functions related to ranged attacks.
**/
#pragma once
#include <vector>
#include "ac-type.h"
#include "beam-type.h"
#include "enchant-type.h"
#include "externs.h"
#include "killer-type.h"
#include "mon-ai-action.h"
#include "mon-attitude-type.h"
#include "random.h"
#include "ray.h"
#include "spl-cast.h"
#include "zap-type.h"
using std::string;
// The mid of the source for a pending hostile teleport.
// (Can be the player, if caused by teleportitis.)
#define TELEPORTITIS_SOURCE "TELEPORTITIS_SOURCE"
using std::vector;
#define BEAM_STOP 1000 // all beams stopped by subtracting this
// from remaining range
class monster;
class ranged_attack;
enum mon_resist_type
{
MON_RESIST, // monster resisted
MON_UNAFFECTED, // monster unaffected
MON_AFFECTED, // monster was affected
MON_OTHER, // monster unaffected, but for other reasons
};
class dist;
typedef FixedArray<int, 19, 19> explosion_map;
struct tracer_info
{
int count; // # of times something "hit"
int power; // total levels/hit dice affected
tracer_info();
void reset();
};
struct bolt;
struct beam_tracer
{
// Is this tracer collecting warnings to prompt the player with?
virtual bool is_collecting_warnings() noexcept { return false; }
// Has the tracer hit an enemy worth caring about? Only needs to be
// accurate when firing if `is_collecting_warnings` returns true.
virtual bool has_hit_foe() noexcept { return false; }
// Should the beam stop at the target if there is a friendly monster
// after it?
virtual bool should_stop() noexcept { return false; }
// How many times the player was hit by this tracer.
virtual int player_hit_count() noexcept { return 0; }
// Called when a meaningful target is affected
virtual void actor_affected(bool friendly_fire, int power)
{
UNUSED(friendly_fire, power);
}
virtual void player_hit(bool was_friendly = true)
{
UNUSED(was_friendly);
}
virtual void monster_hit(const bolt& bolt, const monster& mon)
{
UNUSED(bolt, mon);
}
virtual void blocked(string message)
{
UNUSED(message);
}
};
// Used when casting a spell to check if the spell should be aborted
struct player_beam_tracer : beam_tracer
{
struct bad_target
{
const monster* mon;
string adj;
string suffix;
bool penance;
};
vector<bad_target> bad_attack_targets;
const monster* bad_charm_target = nullptr;
const monster* god_hated_target = nullptr;
int hit_self_count = 0;
int foe_count = 0;
string blocked_message;
int blocked_count = 0;
player_beam_tracer() {}
bool is_collecting_warnings() noexcept override
{
return true;
}
bool has_hit_foe() noexcept override;
bool should_stop() noexcept override;
void actor_affected(bool friendly_fire, int power) noexcept override;
void player_hit(bool was_friendly) noexcept override;
void monster_hit(const bolt& bolt, const monster& mon) override;
void blocked(string message) noexcept override;
bool has_any_warnings() noexcept;
};
// Used to check if casting a spell might be useful
struct targeting_tracer : beam_tracer
{
tracer_info foe_info;
tracer_info friend_info;
bool abort_for_player = false;
int hurt_player_count = 0;
targeting_tracer() {}
bool has_hit_foe() noexcept override;
void actor_affected(bool friendly_fire, int power) noexcept override;
void player_hit(bool was_friendly) noexcept override;
int player_hit_count() noexcept override;
ai_action::goodness good_to_fire(int foe_ratio) const;
};
struct bolt
{
bolt();
// INPUT parameters set by caller
spell_type origin_spell = SPELL_NO_SPELL; // may remain SPELL_NO_SPELL for
// non-spell beams.
int range = -2;
char32_t glyph = '*'; // missile gfx
colour_t colour = BLACK;
beam_type flavour = BEAM_MAGIC;
beam_type real_flavour = BEAM_MAGIC; // for random and chaos beams this
// will remain the same while flavour
// changes
bool drop_item = false; // should drop an item when done
coord_def source = {0,0}; // beam origin
coord_def target = {0,0}; // intended target
dice_def damage = dice_def(0,0);
int ench_power = 0, hit = 0;
killer_type thrower = KILL_NON_ACTOR; // what kind of thing threw this?
int ex_size = 0; // explosion radius (0==none)
mid_t source_id = MID_NOBODY;// The mid of the source (remains
// MID_NOBODY if not coming from a player
// or a monster).
string source_name = ""; // The name of the source, should it
// be different from agent->name(),
// or if the actor dies prematurely.
string name = "";
string short_name = "";
string hit_verb = ""; // The verb to use when this beam hits
// something. If not set, will use
// "engulfs" if an explosion or cloud
// and "hits" otherwise.
int loudness = 0; // Noise level on hitting or exploding.
string hit_noise_msg = ""; // Message to give player for each hit
// monster that isn't in view.
string explode_noise_msg = ""; // Message to give player if the explosion
// isn't in view.
bool pierce = false; // Can the beam pass through a target and
// hit another target behind the first?
bool is_explosion = false;
bool is_death_effect = false; // effect of e.g. ballistomycete spore
bool aimed_at_spot = false; // aimed at (x, y), should not cross
bool stop_at_allies = false; // Should beam automatically stop before reaching allies
bool safe_to_user = false; //
string aux_source = ""; // source of KILL_NON_ACTOR beams
bool affects_nothing = false; // should not hit monsters or features
bool effect_known = true; // did we _know_ this would happen?
bool effect_wanton = false; // could we have guessed it would happen?
bool no_saving_throw = false; // whether to ignore any saving throw
// this beam might otherwise have
int draw_delay = 15; // delay used when drawing beam.
int explode_delay = 50; // delay when drawing explosions.
bool redraw_per_cell = true; // whether to force a redraw after every cell
// drawn during an animation. Not for
// explosions.
// TODO: why can't this behavior follow
// from draw_delay == 0?
bolt* special_explosion = nullptr; // For exploding with a different
// flavour/damage/etc than the beam
// itself.
// Do we draw animations?
bool animate;
ac_type ac_rule = ac_type::normal; // How defender's AC affects damage.
#ifdef DEBUG_DIAGNOSTICS
bool quiet_debug = false; // Disable any debug spam.
#endif
// OUTPUT parameters (tracing, ID)
bool obvious_effect = false; // is this a non-enchantment, or did it already
// show some effect or message? (Otherwise, we'll
// print the canned 'nothing happened.)
bool seen = false; // Has player seen the beam?
bool heard = false; // Has the player heard the beam?
vector<coord_def> path_taken = {}; // Path beam took.
// INTERNAL use - should not usually be set outside of beam.cc
int extra_range_used = 0;
bool aimed_at_feet = false; // this was aimed at self!
bool msg_generated = false; // an appropriate msg was already mpr'd
bool noise_generated = false; // a noise has already been generated at this pos
bool passed_target = false; // Beam progressed beyond target.
bool in_explosion_phase = false; // explosion phase (as opposed to beam phase)
bool enchant_chaining_done = false; // wheather we have already chained to nearby actors
mon_attitude_type attitude = ATT_HOSTILE; // attitude of whoever fired the bolt
int foe_ratio = 0; // 100* foe ratio (see mons_should_fire())
map<mid_t, int> hit_count; // how many times targets were affected
int foes_hurt = 0; // number of foes actually hurt
int foes_helped = 0; // number of foes actually helped
int friends_hurt = 0; // number of friends actually hurt
int friends_helped = 0; // number of friends actually helped
// Ranged attack to perform on each actor reached (for ranged attack beams)
ranged_attack* ranged_atk = nullptr;
beam_tracer* tracer = nullptr;
bool chose_ray = false; // do we want a specific ray?
bool overshoot_prompt = true; // warn when an ally is past the target
bool friendly_past_target = false; // we fired and found something past the target
int bounces = 0; // # times beam bounced off walls
coord_def bounce_pos = {0,0}; // position of latest wall bounce,
// reset if a reflection happens
coord_def last_affected_actor_pos = {0,0}; // position of the last actor affected by this beam
int reflections = 0; // # times beam reflected off shields
mid_t reflector = MID_NOBODY; // latest thing to reflect beam
bool use_target_as_pos = false; // pos() should return ::target()
ray_def ray; // shoot on this specific ray
// only used if USE_TILE is defined
tileidx_t tile_beam = 0;
tileidx_t tile_explode = 0;
private:
bool can_see_invis = false;
bool nightvision = false;
bool can_trigger_bullseye = false;
public:
bool is_enchantment() const; // no block/dodge, use willpower
void set_target(const dist &targ);
void set_agent(const actor *agent);
void setup_retrace();
void precalc_agent_properties();
// Returns YOU_KILL or MON_KILL, depending on the source of the beam.
killer_type killer() const;
kill_category whose_kill() const;
actor* agent(bool ignore_reflections = false) const;
void fire();
void fire(beam_tracer& tracer);
void fire_as_ranged_attack(ranged_attack& atk);
// Returns member short_name if set, otherwise some reasonable string
// for a short name, most likely the name of the beam's flavour.
string get_short_name() const;
string get_source_name() const;
bool visible() const;
bool can_affect_actor(const actor *act) const;
bool can_affect_wall(const coord_def& p, bool map_knowledge = false) const;
bool harmless_to_player() const;
bool ignores_monster(const monster* mon) const;
bool ignores_player() const;
bool can_knockback(int dam = -1) const;
bool can_pull(const actor &act, int dam = -1) const;
bool god_cares() const; // Will the god be unforgiving about this beam?
bool is_harmless(const monster* mon) const;
bool nasty_to(const monster* mon) const;
bool nice_to(const monster_info& mi) const;
bool has_saving_throw() const;
void draw(const coord_def& p, bool force_refresh=true);
// Various explosion-related stuff.
bool explode(bool show_more = true, bool hole_in_the_middle = false);
bool explode(beam_tracer& tracer, bool show_more = true,
bool hole_in_the_middle = false);
void refine_for_explosion(const string& explode_msg = "");
bool explosion_draw_cell(const coord_def& p);
void explosion_affect_cell(const coord_def& p);
void determine_affected_cells(explosion_map& m, const coord_def& delta,
int count, int r,
bool stop_at_statues, bool stop_at_walls);
void special_explode();
bool self_targeted() const;
coord_def get_last_affected_pos(int step = 0);
// Setup.
void fake_flavour();
private:
void do_fire();
void initialise_fire();
// Lots of properties of the beam.
coord_def pos() const;
coord_def leg_source() const;
cloud_type get_cloud_type() const;
int get_cloud_pow() const;
int get_cloud_size(bool min = false, bool max = false) const;
bool is_blockable() const;
bool is_omnireflectable() const;
bool is_fiery() const;
bool can_burn_trees() const;
bool is_bouncy(dungeon_feature_type feat) const;
bool stop_at_target() const;
bool is_reflectable(const actor &whom) const;
bool found_player() const;
bool need_regress() const;
bool is_big_cloud() const; // expands into big_cloud at endpoint
int range_used_on_hit() const;
bool bush_immune(const monster &mons) const;
bool at_blocking_monster() const;
int apply_lighting(int base_hit, const actor &target) const;
void do_ranged_attack(actor& target);
set<string> message_cache;
void emit_message(const char* msg);
int apply_AC(const actor* victim, int hurted);
bool determine_damage(monster* mon, int& preac, int& postac, int& final);
// Functions which handle actually affecting things. They all
// operate on the beam's current position (i.e., whatever pos()
// returns.)
void step();
public:
void affect_cell();
void affect_endpoint();
void affect_endpoint(beam_tracer& tracer);
private:
void affect_wall();
void digging_wall_effect();
void sporangium_wall_effect();
void burn_wall_effect();
void affect_ground();
void affect_place_clouds();
void affect_place_explosion_clouds();
int range_used(bool leg_only = false) const;
void finish_beam();
void drop_object();
// These methods make the beam affect a specific actor, not
// necessarily what's at pos().
public:
void affect_actor(actor *act);
private:
// for monsters
void affect_monster(monster* m);
void kill_monster(monster &m);
bool check_for_friendly_past_target(monster* mon);
bool attempt_block(monster* mon);
void update_hurt_or_helped(monster* mon);
void enchantment_affect_monster(monster* mon);
public:
mon_resist_type try_enchant_monster(monster* mon, int &res_margin);
mon_resist_type apply_enchantment_to_monster(monster* mon);
void apply_beam_conducts();
private:
void apply_bolt_paralysis(monster* mons);
void apply_bolt_petrify(monster* mons);
void handle_enchant_chaining(coord_def centre);
void monster_post_hit(monster* mon, int dmg);
// for players
void affect_player();
bool misses_player();
public:
void affect_player_enchantment(bool resistible = true);
private:
void internal_ouch(int dam);
// for both
void knockback_actor(actor *act, int dam);
void pull_actor(actor *act, int dam);
// tracers
void tracer_affect_player();
void tracer_affect_monster(monster* mon);
void tracer_enchantment_affect_monster(monster* mon);
void tracer_nonenchantment_affect_monster(monster* mon);
bool has_relevant_side_effect(monster* mon);
// methods to change the path
void bounce();
void reflect();
public:
void choose_ray();
bool is_tracer() const noexcept { return tracer != nullptr; }
void set_is_tracer(bool value) noexcept;
};
int mons_adjust_flavoured(monster* mons, bolt &pbolt, int hurted,
bool doFlavouredEffects = true);
// Return whether the effect was visible.
bool enchant_actor_with_flavour(actor* victim, const actor *atk,
beam_type flavour, int powc = 0);
bool enchant_monster_invisible(monster* mon, const string &how);
bool ench_flavour_affects_monster(actor *agent, beam_type flavour,
const monster* mon, bool intrinsic_only = false);
spret mass_enchantment(enchant_type wh_enchant, int pow,
bool fail = false);
int ench_power_stepdown(int pow);
bool poison_monster(monster* mons, const actor* who, int levels = 1,
bool force = false, bool verbose = true);
bool miasma_monster(monster* mons, const actor* who);
bool sticky_flame_monster(monster* mons, const actor* who, int dur,
bool verbose = true);
bool curare_actor(actor* source, actor* target, string name,
string source_name, int bonus_poison = 0);
int silver_damages_victim(actor* victim, int damage, string &dmg_msg);
void fire_tracer(const monster* mons, targeting_tracer& tracer,
bolt &pbolt, bool explode_only = false,
bool explosion_hole = false);
spret zapping(zap_type ztype, int power, bolt &pbolt,
bool needs_tracer = false, const char* msg = nullptr,
bool fail = false);
void fire_partial_player_tracer(zap_type ztype, int power,
player_beam_tracer& tracer,
bolt &pbolt, int range = 0);
bool player_tracer(zap_type ztype, int power, bolt &pbolt, int range = 0);
set<coord_def> create_feat_splash(coord_def center, int radius, int num, int dur,
dungeon_feature_type new_feat = DNGN_SHALLOW_WATER);
void init_zap_index();
void clear_zap_info_on_exit();
bool zap_explodes(zap_type ztype);
bool zap_is_enchantment(zap_type ztype);
int zap_ench_power(zap_type z_type, int pow, bool is_monster);
int zap_to_hit(zap_type z_type, int power, bool is_monster);
dice_def zap_damage(zap_type z_type, int power, bool is_monster, bool random = true);
colour_t zap_colour(zap_type z_type);
dice_def combustion_breath_damage(int pow, bool allow_random = true);
void zappy(zap_type z_type, int power, bool is_monster, bolt &pbolt);
void bolt_parent_init(const bolt &parent, bolt &child);
int explosion_noise(int rad);
int omnireflect_chance_denom(int SH);
void glaciate_freeze(monster* mon, killer_type englaciator,
int kindex);
void fill_chain_targets(const bolt& beam, coord_def centre,
vector<coord_def> &targs, bool random);
bolt setup_targeting_beam(const monster &mons);
bool cancel_beam_prompt(const bolt& beam, const player_beam_tracer& tracer,
int beams_fired = 1);
int apply_willpower_bypass(const actor& source, int willpower);
int apply_willpower_bypass(const monster_info& source, int willpower);
int guile_will_reduction(bool max = false);
|