penetration (not phasing) for launchers and shadow, penetration, dispersal, exploding, steel and silver for ammo. Never randomly generated.
If a launcher of venom is used to launch flame or ice ammo then the resulting bolt will be poisoned, just like poisoned ammo launched from a launcer of flame or frost.
Put missile beam setup code that's common to monsters and the player in setup_missile_beam().
Removed mons_thrown_object_destroyed(), thrown_object_destroyed() is now used for both monsters and the player.
The bolt struct has several new callback fields that can be set to alter the beam's behaviour; currently only used by the brands implemented in this commit, but they should be general enough to be used by anything.
The bolt struct has the new field "special_explosion" which can be used to cause an explosion with flavour and/or damage dice different than the rest of the beam.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@8449 c06c8d41-db1a-0410-9941-cceddc491573
V633AMMPMHJB5ZLP5IHFXCT6FCENVTPX25TY7T5MF2QZLDDN24HAC
O2JZ3E7K4CHLODQA23ZAP7CTX636PTFLOE4CLQL6AJPN4ON5SGLQC
2P6H6IUP2MYDWNF4VZFITNCNBCVDFHJXOPTV2TQXAPOOLPJKIDNQC
MFP3WAGO2JIOT4QSEYPM4OJV5NVXMUOPL75OIE7YZ5RWOYQPIZ7AC
6RKGEX37UA6R2NYUFNTYUJXXJOI67YTRB6TGOHMPG2IFXD6SVGFAC
7CZXCSI3A4XRQ5RK2B7AZOS3JIQBD2EBSRIWBNMUU3WMVQ6IRFNQC
UDDZ7BNFAK3BDG5UIOMEDWGH4XRJOWV4AERTZOJ7VT6IOKBP4EIQC
JLCVRRM3BR37DZJV7ED4CRRWXFYIOFTIM32DYM3DXBOS3WYZNWMQC
Y3CZBT22Z4SMP4H2HW2U4F4X5KP2I5SGOBXATDEIDV6S3ANGQ2HAC
SKIHTFLZCN33YOLY6DBH5AD45EIUFX3CS44CMITAP7JVHRZN5RSQC
OQECLU7MBB6HZQPTVNUE27GDBZZ2IRND4FREQ2JUX5I3B6WZXEPQC
XBXO44GTW26ELSWP4ZRCRIFOZO7JULKLV3IXAMGR2YZ5B7KVSIDAC
QFWRKRPFS7TWZYFMIXWU24RS6DAUQPJS47O4HY456PXQJDOYNDWQC
Y756PLIQ6IS4CY6R5PNBKI5PORSGSXP2KWO4J22A4DV6VSDDKZGAC
NRIZKLUO26UHNKB4IERXI6ECMD2IJYZACQNIUU3SH6BPLGHAJYVAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
PBKRKGKARGZSLVFVDTK5NWDXQD26NHNN67LDSSB75CLEWBR6TLEQC
TGJZXTUIAKCFZQJ54ZQEBGFBVZSJCAX6AWDRSH3TP7UJRLGUM5SAC
W74555HMPXUQ72AGKBXC5P3STMMX5DZAW6ZESUDLNVJBCAG43PLAC
FA2V3G4NYTWJWUT7EWH75L3YOUX6YVOZ5LNRSFLO2XF273JKSUTAC
Y2NYY7HWFZ2LQDK3ACSLGS37F2J2IJ5LRGCIMZYXLEOSVPD3A4DAC
SUWIERONPDATHPDMZRYO6GYIXSW6XIS5V5MK5IV23DWQH2LL7VIAC
CDFS7Z74W5HKPQIHQICOG442KQFXUSBGGLDDQLE3MG74T44YU6AQC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
AYU5OVG2HZO46KDAPKUWAVHS5HTYFKUWIMIRMTHAXVVFEDJE7YPAC
JYEEOUYQ7ZPKOGWUV7VCORBVSOLF2UCBFBH3TR75RGOSS6PNKYUAC
CUM44NOPIB7LGTRY2O2R5MYXAB27R3TFM6LUBEQHZFXLZWJ55QZAC
SIDH2P7NBIG5KEOE27XHD3ZT2NQ2OJZFN6VZXWNWYFFY5YVXSSVQC
SH5BS5AJPUFPXQHM5MZPIY3CCVT4EAIRLSAQWZYL5WX2J7FPBSQQC
2OY3EXIBFR22QCCKPZOA37YUI7CX7BEKRRYSDSBDKQN6VTDBD7LAC
YOH32TMLN6QJG4ZFLYWPJF3YUEGIMZPVPYN57RTB26QBBHMICV3AC
ZNMT5CZHP2FC4HTLNA7KYEDGFBXSCUE5QHJOALVPE6RDPHSEDXRQC
NNG27Y5ZQAZX6UD7F7M4F6KEZBEDFXPEEC3LFUSX4ESKT7K6UJQAC
3DQXSE4YGFBBDUWK4YEOFWW4UPWILWELFSLP37SL6BERGAZJC5YAC
NLQNXH3SVJ52CWXEV35FSSZP32VHC4QFGN3HINF4KO5GZHZMOBKQC
QDTVLBRGHDTRUVT7I3O72K6TMOYAUSAJBZUHGOEFU2RKJNUPWZSQC
KAOE5HB3THUKVGFZRO5EZESHEB3Q34WUO5DFMLWIKOBF47LZTIYAC
MSMWAL6JZAWNGZXCNXPATUMAU6TVXBWWFY666P7UBSZ5LPYJYUCQC
WT66JDIRTLLP37SHTV4GI3V64JFJ4D25LNRLGCHFG6CLEFKJ3QGQC
T6TL6NTIOBYNUIONGK3JFZJ5ONWV6S4CTIRDC5JMKMCBGG5IY3EAC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
3ZWALZFSTSIVYXY4BAY6ANGINTDACZC6RSSJTEMQSTSUIE66YOBQC
6GDKXNFXPKQ6AVNOSMJECN7CLELM2KCMVRM2A7BARLK2NNILN6SAC
BWAQ3FHBBM6G3K3KYP75CRTR343RDQZJRYX5ZGYUEXYBAC3APDLAC
LDBTCT5WIPLJPZWXS2RUQ26QKISCUUTLO77M464WOE6VSYSNPKYAC
VNHFP63ZLLZU3A3PLXP4BITX57DUIYDHFOHQYK3BOBHV3S64G26QC
6JBULLXOSQVDJZP2R73BFMPV2OHWFT4V3KBPU5CTGN7REHBOB5NQC
SDLKLUNFGVKDS55DDJZCBAVIB7NL3RRYPTACAY65SCUQKV6APFSAC
3FRPKD2JSN7RA2HKWAO3XV7MMKCIAMWIMU6JNJ452VZEUSXM6PWQC
77H4BWWPPGLM3PLZH4QTAJRXIZTSDVNCOKZE223I437FN2UJ34RQC
ASCTVJSN3NXYQHRVXAORA43CV6H5V2572IMK4UGRHKBAGJOWHC4AC
S34LKQDIQJLIWVIPASOJBBZ6ZCXDHP5KPS7TRBZJSCDRVNCLK6UAC
3EUPIYJNWOMOQBP2Z5SGSMWK453BXJD6KL2WFTR3NM565MEBYASAC
QZM4EDMUKU7QWQKWXHTRINNWH3GQQ6RSAX7WIX6PQAEM5V2Y2BQQC
ACSERNMWRXP4VUEUVKDBSDGTBZRI34P2PUVUDTKSAHNTCEDDCCVAC
RDZUMV3A5TREQHLPPJWDWVXBNIOWC3CQJJ35TYFBQQVQNTU7SPXQC
UZ5623MOLKBTGBSRBJ4OBOEI4IEZSPV3NCV2DRMUZ3CHHJQVHIIAC
5KJCHLIUFKRPMIVWUAYT6EOF7SW4PTQF6Y5OPEFWXGLE7DUGYLZAC
5V47S4NNTHWTSAHV3YLO2VGH7JTUIYJ3GBPDN5ZM4UQALT2ZEXDQC
FLAGBNUNSIQNFDN53CDWABJRTTFWDL4PG34AI474ZKPXDEPYHOAQC
CIPVRZGLOZHCERK6YPOBV3P2E4IAB4H6D5EHLRQE2O5E4P4VCBUAC
X2RVB2O2TUAT336F5ZN3SRNB2EKPHSJKRD6FHI6YM3AYRDV7XJUAC
ASLW3Z5PAVZSWJEMMMVZT226P44EKSAD47QS72JIFJESAI3RPN3AC
AIVXE6QBRVCZAASKQRZO6LBDGTYEYSSD2DZCWRX4VLSKE3GCNIDAC
LACOILN7WCNIM3GTLY56TQOMC3MFPW2XX3RHDWXRSMBQQPI4AQXQC
UADYVV3UD5ERJTZZJGY4EUQ4NJ2JSBG7YYUJ75ZRBIXRQXQKOJPAC
BTHEX36BHZOAGTT5SNO6OPWDAZJQSUUNDXSJ77FBEFZE3NGJ7CAQC
E46VBOJB5K4GCWG4BBMJZOSCHWRAS5JMHSFVANRS2DEESAWYVD4QC
E6NTYF4PVIESKDOXXSDV6DFBAGOLBHE66HAJIGG76ZWPNHURKIIAC
PI5BATR2SER3RFE76IUGHM2AGXVFOUM3PLU7WC2K2Q2BA5K2E73QC
PHBACPMH3F34GODHVDKNCMXWU373RJQGVTDLBFCCDLLWDXVYOLTAC
ISUJEAPPWKP2UIYPT6BJUUNSVH52NEXWGXNUATL7I3IO7TPO32HAC
5BJPWUPLJFS34FUTFJVKA4A52YMIGV6EWDXLNSDCWBJWBGVSQFGQC
TCZNXMPFNAWJYUIBTUEOTT73KLB4CU5S6HBKTHRT6KCD4ZVGBGUQC
SJP5BHX6MFWF3OSQPEF4WUWZWPUGMOVURTT2CUVT6H3A66LETXUAC
MFCHFDPW4YX4QZWUOJIT3UG6OHIJPWZRXLYZQCSAUQFCEPYOYHEAC
5K2ANIEXD3CPJM4XNKNPZINP2G4NT7SJBKRN62WNBUKJXFERTILQC
MZXKH5NHXRMX5Y5FG4XXNHZQ7HTQZFX3ARQU55DO4RM5NR3GIAIQC
7YUGK5Q64KG5O7GJGTUBRRLHAHBCJ5YOE23YUPT6UBKUSB67CYAQC
5FHWTG7M6FW4B3I33YI7QSM3OZIB6ZGC6TI6JISSLY5Y43HI56VAC
M53SMQWSTQBB24VSHBX7KJAAMMWGVKKPKV4TLMFHONWYYCNEDRGAC
GSJA56E3ORVIBCBA6T6WU2HE4DCLJ6NZPW76O7L54N4CYPKLJOWQC
WFMQVPMMOPG5SBJD5LUBOIYWRMXVWK3FXENK7SAEGZ5T6XWFKERQC
64VBM7SGUX7CVO5TMVOFU4A26BDOFQXKS6G5K7BXCSWKCCXEETOAC
SNSU5AMDAZNG55NQ4JDHU2YBK62MXPIE2QTSYS7PGV7Y2N3VJBFQC
3U7R5PBYTKTXZIIC2UE3MWMJSQSDK2EIZKSPR3PF434ZWK7A76JQC
LCDK2CK4O2RBLF2MAX75S2Q6ZKAX46WD6GOQDVIXUGHHXXZXMHRQC
AJHVP42Y67SB4NKFMZB24524PGX2XA5WLFNEFV52MIGTS47ALMVQC
pbolt.range = LOS_RADIUS;
pbolt.beam_source = monster_index(monster);
pbolt.type = dchar_glyph(DCHAR_FIRED_MISSILE);
pbolt.colour = item.colour;
pbolt.flavour = BEAM_MISSILE;
pbolt.thrower = KILL_MON_MISSILE;
pbolt.item = &item;
pbolt.aux_source.clear();
pbolt.range = LOS_RADIUS;
exDamBonus += 6;
pbolt.flavour = BEAM_CHAOS;
pbolt.name = "bolt of chaos";
pbolt.colour = EC_RANDOM;
pbolt.type = dchar_glyph(DCHAR_FIRED_ZAP);
}
// WEAPON or AMMO of FIRE
else if (bow_brand == SPWPN_FLAME && ammo_brand != SPMSL_ICE
|| ammo_brand == SPMSL_FLAME && bow_brand != SPWPN_FROST)
{
baseHit += 2;
exDamBonus += 6;
pbolt.flavour = BEAM_FIRE;
pbolt.name = "bolt of ";
if (poison)
pbolt.name += "poison ";
pbolt.name += "flame";
pbolt.colour = RED;
pbolt.type = dchar_glyph(DCHAR_FIRED_ZAP);
}
// WEAPON or AMMO of FROST
else if (bow_brand == SPWPN_FROST && ammo_brand != SPMSL_FLAME
|| ammo_brand == SPMSL_ICE && bow_brand != SPWPN_FLAME)
{
baseHit += 2;
return (true);
}
bool mons_thrown_object_destroyed( item_def *item, const coord_def& where,
bool returning, int midx )
{
ASSERT( item != NULL );
bool destroyed = (item->base_type == OBJ_MISSILES
&& item->sub_type != MI_THROWING_NET && coinflip());
bool hostile_grid = grid_destroys_items(grd(where));
if (pbolt.special_explosion != NULL)
delete pbolt.special_explosion;
// Non-returning items thrown into item-destroying grids are always
// destroyed. Returning items are only destroyed if they would have
// been randomly destroyed anyway.
if (returning && !destroyed)
hostile_grid = false;
// Summoned items go poof if they're not returning.
if (hostile_grid || !returning && (item->flags & ISFLAG_SUMMONED))
{
// No destruction sound here. Too much message spam otherwise.
item_was_destroyed(*item, midx);
destroyed = true;
}
return destroyed;
return (true);
void monster_die(monsters *monster, killer_type killer,
int killer_index, bool silent = false, bool wizard = false);
int monster_die(monsters *monster, killer_type killer,
int killer_index, bool silent = false, bool wizard = false);
}
static int _item_to_skill_level(const item_def *item)
{
skill_type type = range_skill(*item);
if (type == SK_DARTS || type == SK_SLINGS)
return (you.skills[type] + you.skills[SK_THROWING]);
return (2 * you.skills[type]);
}
static bool _poison_hit_victim(bolt& beam, actor* victim, int dmg, int corpse)
{
if (!victim->alive() || victim->res_poison())
return (false);
if (beam.is_tracer)
return (true);
int levels = 0;
actor* agent = beam.agent();
if (agent->atype() == ACT_MONSTER)
{
if (dmg > 0 || beam.ench_power == AUTOMATIC_HIT
&& x_chance_in_y(90 - 3 * victim->armour_class(), 100))
{
levels = 1 + random2(3);
}
}
else
{
if (beam.ench_power == AUTOMATIC_HIT
&& x_chance_in_y(90 - 3 * victim->armour_class(), 100))
{
levels = 2;
}
else if (random2(dmg) > random2(victim->armour_class()))
levels = 1;
int num_success = 0;
if (YOU_KILL(beam.thrower))
{
const int skill_level = _item_to_skill_level(beam.item);
if (x_chance_in_y(skill_level + 25, 50))
num_success++;
if (x_chance_in_y(skill_level, 50))
num_success++;
}
else
num_success = 1;
if (num_success == 0)
return (false);
else
{
if (num_success == 2)
levels++;
}
}
if (levels <= 0)
return (false);
victim->poison(agent, levels);
return (true);
}
static bool _item_penetrates_victim(const bolt &beam, const actor *victim,
int &used)
{
if (beam.aimed_at_feet)
return (false);
used = 0;
if (!beam.is_tracer && you.can_see(victim))
mprf("The %s passes through %s!", beam.name.c_str(),
victim->name(DESC_NOCAP_THE).c_str());
return (true);
}
static bool _silver_damages_victim(bolt &beam, actor* victim, int &dmg,
std::string &dmg_msg)
{
bool shifter;
if (victim->atype() == ACT_MONSTER)
{
monsters* mon = dynamic_cast<monsters*>(victim);
shifter = mons_is_shapeshifter(mon);
}
else
shifter = transform_changed_physiology();
mon_holy_type holiness = victim->holiness();
if (shifter || holiness == MH_UNDEAD || holiness == MH_DEMONIC)
{
dmg *= 2;
if (!beam.is_tracer && you.can_see(victim))
dmg_msg = "The silver sears " + victim->name(DESC_NOCAP_THE) + "!";
}
return (false);
}
static bool _shadow_hit_victim(bolt& beam, actor* victim, int dmg, int corpse)
{
if (beam.is_tracer || victim->alive() || corpse == -1
|| corpse == NON_ITEM)
{
return (false);
}
actor* agent = beam.agent();
beh_type beh;
unsigned short hitting;
if (agent->atype() == ACT_PLAYER)
{
hitting = you.pet_target;
beh = BEH_FRIENDLY;
}
else
{
monsters *mon = dynamic_cast<monsters*>(agent);
beh = SAME_ATTITUDE(mon);
// Get a new foe for the zombie to target.
behaviour_event(mon, ME_EVAL);
hitting = mon->foe;
}
int midx = NON_MONSTER;
if (!animate_remains(victim->pos(), CORPSE_BODY, beh, hitting,
GOD_NO_GOD, true, true, &midx))
{
return (false);
}
monsters *zomb = &menv[midx];
if (you.can_see(victim))
mprf("%s turns into a zombie!", victim->name(DESC_CAP_THE).c_str());
else if (you.can_see(zomb))
mprf("%s appears out of thin air!", zomb->name(DESC_CAP_THE).c_str());
return (true);
if (!victim->alive() || victim == agent)
return (false);
if (beam.is_tracer)
return (true);
const bool was_seen = you.can_see(victim);
const bool no_sanct = victim->kill_alignment() == KC_OTHER;
coord_def pos, pos2;
int tries = 0;
do
{
if (!random_near_space(victim->pos(), pos, false, true, no_sanct))
return (false);
} while (!victim->is_habitable(pos) && tries++ < 100);
if (!victim->is_habitable(pos))
return (false);
tries = 0;
do
{
random_near_space(victim->pos(), pos2, false, true, no_sanct);
} while (!victim->is_habitable(pos2) && tries++ < 100);
if (!victim->is_habitable(pos2))
return (false);
// Pick the square further away from the agent.
const coord_def from = agent->pos();
if (in_bounds(pos2)
&& grid_distance(pos2, from) > grid_distance(pos, from))
{
pos = pos2;
}
if (pos == victim->pos())
return (false);
const coord_def oldpos = victim->pos();
if (victim->atype() == ACT_PLAYER)
{
clear_trapping_net();
victim->moveto(pos);
mpr("You blink!");
}
else
{
monsters *mon = dynamic_cast<monsters*>(victim);
mons_clear_trapping_net(mon);
mon->check_redraw(oldpos);
mon->move_to_pos(pos);
mon->apply_location_effects(pos);
mon->check_redraw(pos);
const bool seen = you.can_see(mon);
const std::string name = mon->name(DESC_CAP_THE);
if (was_seen && seen)
mprf("%s blinks!", name.c_str());
else if (was_seen && !seen)
mprf("%s vanishes!", name.c_str());
else if (!was_seen && seen)
mprf("%s appears from out of thin air!", name.c_str());
}
return (true);
}
void setup_missile_beam(const actor *agent, bolt &beam, item_def &item,
std::string &ammo_name, bool &returning)
{
dungeon_char_type zapsym = DCHAR_SPACE;
switch (item.base_type)
{
case OBJ_WEAPONS: zapsym = DCHAR_FIRED_WEAPON; break;
case OBJ_MISSILES: zapsym = DCHAR_FIRED_MISSILE; break;
case OBJ_ARMOUR: zapsym = DCHAR_FIRED_ARMOUR; break;
case OBJ_WANDS: zapsym = DCHAR_FIRED_STICK; break;
case OBJ_FOOD: zapsym = DCHAR_FIRED_CHUNK; break;
case OBJ_UNKNOWN_I: zapsym = DCHAR_FIRED_BURST; break;
case OBJ_SCROLLS: zapsym = DCHAR_FIRED_SCROLL; break;
case OBJ_JEWELLERY: zapsym = DCHAR_FIRED_TRINKET; break;
case OBJ_POTIONS: zapsym = DCHAR_FIRED_FLASK; break;
case OBJ_UNKNOWN_II: zapsym = DCHAR_FIRED_ZAP; break;
case OBJ_BOOKS: zapsym = DCHAR_FIRED_BOOK; break;
case OBJ_STAVES: zapsym = DCHAR_FIRED_STICK; break;
default: break;
}
beam.type = dchar_glyph(zapsym);
returning = get_weapon_brand(item) == SPWPN_RETURNING
|| get_ammo_brand(item) == SPMSL_RETURNING;
if (agent->atype() == ACT_PLAYER)
{
beam.attitude = ATT_FRIENDLY;
beam.beam_source = NON_MONSTER;
beam.smart_monster = true;
beam.thrower = KILL_YOU_MISSILE;
}
else
{
const monsters *mon = dynamic_cast<const monsters*>(agent);
beam.attitude = mons_attitude(mon);
beam.beam_source = mon->mindex();
beam.smart_monster = (mons_intel(mon) >= I_NORMAL);
beam.thrower = KILL_MON_MISSILE;
}
beam.item = &item;
beam.source = agent->pos();
beam.colour = item.colour;
beam.flavour = BEAM_MISSILE;
beam.is_beam = false;
beam.aux_source.clear();
beam.can_see_invis = agent->can_see_invisible();
item_def *launcher = const_cast<actor*>(agent)->weapon(0);
if (launcher && !item.launched_by(*launcher))
launcher = NULL;
int bow_brand = SPWPN_NORMAL;
if (launcher != NULL)
bow_brand = get_weapon_brand(*launcher);
int ammo_brand = get_ammo_brand(item);
bool poisoned = ammo_brand == SPMSL_POISONED;
if (bow_brand == SPWPN_VENOM && ammo_brand != SPMSL_CURARE)
{
if (ammo_brand == SPMSL_NORMAL)
item.special = SPMSL_POISONED;
poisoned = true;
}
const bool exploding = ammo_brand == SPMSL_EXPLODING;
const bool penetrating = !exploding
&& (bow_brand == SPWPN_PENETRATION
|| ammo_brand == SPMSL_PENETRATION);
const bool silver = ammo_brand == SPMSL_SILVER;
const bool disperses = ammo_brand == SPMSL_DISPERSAL;
const bool shadow = bow_brand == SPWPN_SHADOW
|| ammo_brand == SPMSL_SHADOW;
ASSERT(!exploding || !is_artefact(item));
beam.name = item.name(DESC_PLAIN, false, false, false);
// Print type of item as influenced by launcher.
item_def ammo = item;
// The chief advantage here is the extra damage this does
// against susceptible creatures.
// Note: weapons & ammo of eg fire are not cumulative
// ammo of fire and weapons of frost don't work together,
// and vice versa.
// Note that bow_brand is known since the bow is equipped.
// Chaos overides flame and frost/ice.
if (bow_brand == SPWPN_CHAOS || ammo_brand == SPMSL_CHAOS)
{
// Chaos can't be poisoned, since that might conflict with
// the random healing effect or overlap with the random
// poisoning effect.
poisoned = false;
if (item.special == SPWPN_VENOM || item.special == SPMSL_CURARE)
item.special = SPMSL_NORMAL;
beam.effect_known = false;
beam.flavour = BEAM_CHAOS;
beam.name = "chaos";
beam.colour = EC_RANDOM;
ammo.special = SPMSL_CHAOS;
}
else if ((bow_brand == SPWPN_FLAME || ammo_brand == SPMSL_FLAME)
&& ammo_brand != SPMSL_ICE && bow_brand != SPWPN_FROST)
{
beam.flavour = BEAM_FIRE;
beam.name = "flame";
beam.colour = RED;
ammo.special = SPMSL_FLAME;
}
else if ((bow_brand == SPWPN_FROST || ammo_brand == SPMSL_ICE)
&& ammo_brand != SPMSL_FLAME && bow_brand != SPWPN_FLAME)
{
beam.flavour = BEAM_COLD;
beam.name = "frost";
beam.colour = WHITE;
ammo.special = SPMSL_ICE;
}
ASSERT(beam.flavour == BEAM_MISSILE || !is_artefact(item));
ammo_name = ammo.name(DESC_PLAIN);
if (silver)
beam.damage_funcs.push_back(_silver_damages_victim);
if (poisoned)
beam.hit_funcs.push_back(_poison_hit_victim);
if (penetrating)
beam.range_funcs.push_back(_item_penetrates_victim);
if (shadow)
beam.hit_funcs.push_back(_shadow_hit_victim);
if (disperses)
beam.hit_funcs.push_back(_dispersal_hit_victim);
if (shadow && ammo.special != SPMSL_SHADOW)
{
beam.name = "shadowy " + beam.name;
ammo_name = "shadowy " + ammo_name;
}
if (disperses && ammo.special != SPMSL_DISPERSAL)
{
beam.name = "dispersing " + beam.name;
ammo_name = "dispersing " + ammo_name;
}
if (poisoned && ammo.special != SPMSL_POISONED)
{
beam.name = "poison " + beam.name;
ammo_name = "poisoned " + ammo_name;
}
if (penetrating && ammo.special != SPMSL_PENETRATION)
{
beam.name = "penetrating " + beam.name;
ammo_name = "penetrating " + ammo_name;
}
if (silver && ammo.special != SPMSL_SILVER)
{
beam.name = "silvery " + beam.name;
ammo_name = "silvery " + ammo_name;
}
// Do this here so that we get all the name mods except for a
// redundant "exploding".
if (exploding)
{
bolt *expl = new bolt(beam);
expl->is_explosion = true;
expl->damage = dice_def(2, 5);
expl->ex_size = 1;
if (beam.flavour == BEAM_MISSILE)
{
expl->flavour = BEAM_FRAG;
expl->name += " fragments";
const std::string short_name =
ammo.name(DESC_PLAIN, false, false, false, false,
ISFLAG_IDENT_MASK | ISFLAG_COSMETIC_MASK
| ISFLAG_RACIAL_MASK);
expl->name = replace_all(expl->name, ammo.name(DESC_PLAIN),
short_name);
}
beam.special_explosion = expl;
}
if (exploding && ammo.special != SPMSL_EXPLODING)
{
beam.name = "exploding " + beam.name;
ammo_name = "exploding " + ammo_name;
}
if (beam.flavour != BEAM_MISSILE)
{
returning = false;
beam.type = dchar_glyph(DCHAR_FIRED_BOLT);
beam.name = "bolt of " + beam.name;
}
if (!is_artefact(item))
ammo_name = article_a(ammo_name, true);
}
// Hack the quantity to 1 while getting the name.
const int old_quantity = thrown.quantity;
thrown.quantity = 1;
pbolt.name = thrown.name(DESC_PLAIN, false, false, false);
thrown.quantity = old_quantity;
pbolt.thrower = KILL_YOU_MISSILE;
pbolt.source = you.pos();
pbolt.colour = thrown.colour;
pbolt.flavour = BEAM_MISSILE;
pbolt.aux_source.clear();
dungeon_char_type zapsym = DCHAR_SPACE;
switch (item.base_type)
{
case OBJ_WEAPONS: zapsym = DCHAR_FIRED_WEAPON; break;
case OBJ_MISSILES: zapsym = DCHAR_FIRED_MISSILE; break;
case OBJ_ARMOUR: zapsym = DCHAR_FIRED_ARMOUR; break;
case OBJ_WANDS: zapsym = DCHAR_FIRED_STICK; break;
case OBJ_FOOD: zapsym = DCHAR_FIRED_CHUNK; break;
case OBJ_UNKNOWN_I: zapsym = DCHAR_FIRED_BURST; break;
case OBJ_SCROLLS: zapsym = DCHAR_FIRED_SCROLL; break;
case OBJ_JEWELLERY: zapsym = DCHAR_FIRED_TRINKET; break;
case OBJ_POTIONS: zapsym = DCHAR_FIRED_FLASK; break;
case OBJ_UNKNOWN_II: zapsym = DCHAR_FIRED_ZAP; break;
case OBJ_BOOKS: zapsym = DCHAR_FIRED_BOOK; break;
case OBJ_STAVES: zapsym = DCHAR_FIRED_STICK; break;
default: break;
}
pbolt.type = dchar_glyph(zapsym);
// Special cases for flame, frost, poison, etc.
// check for venom brand.
if (bow_brand == SPWPN_VENOM && ammo_brand == SPMSL_NORMAL)
{
// Poison brand the ammo.
set_item_ego_type( item, OBJ_MISSILES, SPMSL_POISONED );
pbolt.name = item.name(DESC_PLAIN);
}
if (ammo_brand == SPMSL_STEEL)
dice_mult = dice_mult * 150 / 100;
// The chief advantage here is the extra damage this does
// against susceptible creatures.
// Note: weapons & ammo of eg fire are not cumulative
// ammo of fire and weapons of frost don't work together,
// and vice versa.
// Note that bow_brand is known since the bow is equipped.
// Chaos overides flame and frost/ice.
if (bow_brand == SPWPN_CHAOS || ammo_brand == SPMSL_CHAOS)
{
// Chaos can't be poisoned, since that might conflict with
// the random healing effect or overlap with the random
// poisoning effect.
poisoned = false;
// [dshaligram] Branded arrows are much stronger.
dice_mult = (dice_mult * 150) / 100;
pbolt.effect_known = false;
pbolt.flavour = BEAM_CHAOS;
pbolt.name = "bolt of chaos";
pbolt.colour = EC_RANDOM;
pbolt.type = dchar_glyph(DCHAR_FIRED_BOLT);
pbolt.thrower = KILL_YOU_MISSILE;
pbolt.aux_source.clear();
}
else if ((bow_brand == SPWPN_FLAME || ammo_brand == SPMSL_FLAME)
&& ammo_brand != SPMSL_ICE && bow_brand != SPWPN_FROST)
{
if (pbolt.flavour != BEAM_MISSILE)
pbolt.flavour = BEAM_FIRE;
pbolt.name = "bolt of ";
if (poisoned)
pbolt.name += "poison ";
pbolt.name += "flame";
pbolt.colour = RED;
pbolt.type = dchar_glyph(DCHAR_FIRED_BOLT);
pbolt.thrower = KILL_YOU_MISSILE;
pbolt.aux_source.clear();
}
else if ((bow_brand == SPWPN_FROST || ammo_brand == SPMSL_ICE)
&& ammo_brand != SPMSL_FLAME && bow_brand != SPWPN_FLAME)
{
// [dshaligram] Branded arrows are much stronger.
dice_mult = (dice_mult * 150) / 100;
pbolt.flavour = BEAM_COLD;
pbolt.name = "bolt of ";
if (poisoned)
pbolt.name += "poison ";
pbolt.name += "frost";
pbolt.colour = WHITE;
pbolt.type = dchar_glyph(DCHAR_FIRED_BOLT);
pbolt.thrower = KILL_YOU_MISSILE;
pbolt.aux_source.clear();
}
// Print type of item as influenced by launcher.
item_def ammo = item;
if (ammo.base_type == OBJ_MISSILES)
{
if (pbolt.flavour == BEAM_CHAOS)
{
ammo.special = SPMSL_CHAOS;
}
else if (pbolt.flavour == BEAM_FIRE)
{
if (ammo.special != SPMSL_FLAME)
ammo.special = SPMSL_FLAME;
}
else if (pbolt.flavour == BEAM_COLD)
{
if (ammo.special != SPMSL_ICE)
ammo.special = SPMSL_ICE;
}
else
{
if (ammo.special != SPMSL_NORMAL && ammo.special != SPMSL_POISONED)
ammo.special = SPMSL_NORMAL;
}
}
if (ammo_name.empty())
ammo_name = ammo.name(DESC_NOCAP_A);
bool bolt::apply_dmg_funcs(actor* victim, int &dmg,
std::vector<std::string> &messages)
{
for (unsigned int i = 0; i < damage_funcs.size(); i++)
{
std::string dmg_msg;
if ( (*damage_funcs[i])(*this, victim, dmg, dmg_msg) )
return (false);
if (!dmg_msg.empty())
messages.push_back(dmg_msg);
}
return (true);
}
static void _undo_tracer(bolt &orig, bolt ©)
{
// FIXME: we should have a better idea of what gets changed!
orig.target = copy.target;
orig.source = copy.source;
orig.aimed_at_spot = copy.aimed_at_spot;
orig.range_used = copy.range_used;
orig.auto_hit = copy.auto_hit;
orig.ray = copy.ray;
orig.colour = copy.colour;
orig.flavour = copy.flavour;
orig.real_flavour = copy.real_flavour;
orig.seen = copy.seen;
}
// FIXME: we should have a better idea of what gets changed!
target = boltcopy.target;
source = boltcopy.source;
aimed_at_spot = boltcopy.aimed_at_spot;
range_used = boltcopy.range_used;
auto_hit = boltcopy.auto_hit;
ray = boltcopy.ray;
colour = boltcopy.colour;
flavour = boltcopy.flavour;
real_flavour = boltcopy.real_flavour;
seen = boltcopy.seen;
if (special_explosion != NULL)
{
seen = seen || special_explosion->seen;
foe_info += special_explosion->foe_info;
friend_info += special_explosion->friend_info;
_undo_tracer(*special_explosion, *boltcopy.special_explosion);
delete boltcopy.special_explosion;
}
_undo_tracer(*this, boltcopy);
range_used += range_used_on_hit();
std::vector<std::string> messages;
int dummy = 0;
apply_dmg_funcs(&you, dummy, messages);
for (unsigned int i = 0; i < messages.size(); i++)
mpr(messages[i].c_str(), MSGCH_WARN);
range_used += range_used_on_hit(&you);
apply_hit_funcs(&you, 0);
else if (item->special == SPMSL_POISONED)
{
if (!player_res_poison()
&& (hurted || ench_power == AUTOMATIC_HIT
&& x_chance_in_y(90 - 3 * player_AC(), 100)))
{
poison_player(1 + random2(3));
was_affected = true;
}
}
if (name.find("dart") != std::string::npos)
type = SK_DARTS;
else if (name.find("needle") != std::string::npos)
type = SK_DARTS;
else if (name.find("bolt") != std::string::npos)
type = SK_CROSSBOWS;
else if (name.find("arrow") != std::string::npos)
type = SK_BOWS;
else if (name.find("stone") != std::string::npos)
type = SK_SLINGS;
if (type == SK_DARTS || type == SK_SLINGS)
return (you.skills[type] + you.skills[SK_THROWING]);
return (2 * you.skills[type]);
}
&& x_chance_in_y(90 - 3 * mon->ac, 100))
{
num_levels = 2;
}
else if (random2(dmg) - random2(mon->ac) > 0)
num_levels = 1;
int num_success = 0;
if (YOU_KILL(thrower))
{
const int skill_level = _name_to_skill_level(name);
if (x_chance_in_y(skill_level + 25, 50))
num_success++;
if (x_chance_in_y(skill_level, 50))
num_success++;
}
else
num_success = 1;
if (num_success)
{
if (num_success == 2)
num_levels++;
poison_monster(mon, whose_kill(), num_levels);
}
}
else if (item->special == SPMSL_CURARE)
{
if (ench_power == AUTOMATIC_HIT
return (BEAM_STOP);
if (is_enchantment())
return (flavour == BEAM_DIGGING ? 0 : BEAM_STOP);
used = BEAM_STOP;
else if (is_enchantment())
used = (flavour == BEAM_DIGGING ? 0 : BEAM_STOP);
if (flavour == BEAM_ACID)
return (BEAM_STOP);
else if (flavour == BEAM_ACID)
used = BEAM_STOP;
// Lava doesn't go far, but it goes through most stuff.
else if (flavour == BEAM_LAVA)
used = 1;
// Lightning goes through things.
else if (flavour == BEAM_ELECTRICITY)
used = 0;
else
used = 2;
// Lava doesn't go far, but it goes through most stuff.
if (flavour == BEAM_LAVA)
return (1);
if (in_explosion_phase)
return (used);
// tmp needed so that what c_str() points to doesn't go out of scope
// before the function ends.
std::string tmp;
if (item != NULL)
{
tmp = "The " + item->name(DESC_PLAIN, false, false, false)
+ " explodes!";
seeMsg = tmp.c_str();
hearMsg = "You hear an explosion.";
type = dchar_glyph(DCHAR_FIRED_BURST);
}
effect_known(true), draw_delay(15), obvious_effect(false),
seen(false), path_taken(), range_used(0), is_tracer(false),
aimed_at_feet(false), msg_generated(false),
effect_known(true), draw_delay(15), special_explosion(NULL),
range_funcs(), damage_funcs(), hit_funcs(),
obvious_effect(false), seen(false), path_taken(), range_used(0),
is_tracer(false), aimed_at_feet(false), msg_generated(false),