re-arranges all stairs on the level, but now is just used to test the code used to move stairs in LOS away or towards the player.
Added beam flavour BEAM_VISUAL for animating someting moving in a line which doesn't have any effects on its own, and removed BEAM_LINE_OF_SIGHT, which was no longer being used. Added bolt structure field "delay", which can be used to change the delay between animating each square in a beam from the default of 15 msec.
For eventual use as a Xom effect added the stairs-are-avoiding-you durations DUR_REPEL_STAIRS_MOVE and DUR_REPEL_STAIRS_CLIMB. If DUR_REPEL_STAIRS_MOVE alone is set then stepping onto a stairs/portal/etc has a 50% chance of making it move away in a random direction. If DUR_REPEL_STAIRS_CLIMB alone is set then attempting to climb a stairs/etc has a 50% chance of making it move away before you can get through it (and ending your turn in the process). If both are set then moving onto it and attempting to climb each have a 29% chance of making it move, for a combined chance of 49.59% of a move-and-climb making the stairs move away. Once a stair is successfully taken there's a 50% chance of the stair on the other end moving away from the player, and then both durations are reset to 0.
These can be tested by drawing The Stairs card ("&c stair"), which will set DUR_REPEL_STAIRS_CLIMB if you're on top of a stair or DUR_REPEL_STAIRS_MOVE if your'e not.
Added a few wizard targetting command placeholders.
git-svn-id: https://crawl-ref.svn.sourceforge.net/svnroot/crawl-ref/trunk@7865 c06c8d41-db1a-0410-9941-cceddc491573
6DNNPEMZGBQDMA7YG4LCTQUVZ7LYPC3R4A2XBYT5SDQ65GYOLJVAC
3PBG6FJ6WGSR4UM3I7UQUYYCSYU3UBCKCIKYKUEEANIK72TUL4XAC
UPA65AL4JXYLIHH4D42IWJHRTOAF2BPOVZOAKOXBLZBYIMDZDFFQC
ZFRNNC6KLYRMKAQSRUIFAJR5LAOTKAULQB2D4ABIZKGZHGOA2XGQC
SVY2PTCLXR3KNPQAWXVXTTGCC5DR334HOAKHYO3VDDRWM2BWMALAC
DKRSOHZXL6EPSLKOKHF7GJXSZEJVY7CXGACSHWLM5B5FTRETWWCAC
45EMD3KLQPMERNMIKU5G76H6556XOMIW352TSBP7VLWJX2YYGS7AC
NVSFIV2ZKP44XHCSCXG6OZVGL67OIFINC34J2EMKTA4KULCERUEAC
WDQ3ST3BQ7BMW2IXQMSJVRG7GK2EG444JLKII24FB7IJ5YF5KT5QC
AV3TMWHWB3XBXQCT34UPMZBSIIKVXIGWQPNEFU4CZSBS3ZOF2CUQC
2UO6ZOW7UCP5XJ2TJ26PJNQABILK2BZ4J3BAKLOKMZPJDQHW24MAC
J7GPW2YXLT6FGSKJ24FGQ24GTRZ6A2BDYM6CIXV4R6YBEHP6BGPAC
VCG3BRIYRTNNWYC3LOXD6KFGXOX37HAFW2HNV7WXVG2V7EUHLDZQC
CK7CT5TUFUL2AQY7FUHB5JI3FC2KSPWUWHXC6VEUJJ7G4OWUQFTAC
RPOZZWKG5GLPHVZZ7ZKMKS64ZMV2LDCQSARBJFJ6FZOTOKCQO7FAC
K2CS6TCX2NDVL2ASEHGP4J4K4IJ6FP3ANNKTSIWVG43HPYSBX6ZQC
7Y5HSDFKA5TPLS2TWTRFMQVX6UXUDHXU5MUMXQSDFAIY4THQ3BIQC
GZ4FB5VKL4C22KK3GSKQPLJYMRGLFXUDCOBNEBC2OKK6KVZL3OSQC
Y3NLZQBRMBULLMTLNCG5VSLRZFUAD4MI6VMYN7Q3L5YMIVDN5DAQC
M27JU3PI7UXV4S4LTHOCGVASHX2RBXL5ZU4MLFWCQCSMGQC53IAAC
AVCMVFA3MKCXHO6H44UK5KJNIHTGQV7UA7GYXM26VI6TXXU5ZN6QC
KSM4H3SBM6FIQTUEGHXXYATJXEOJ4EKUBAFCRMFKSHY7N2HWECRQC
OFH2B2UZBK43QLFQBZ54FOCFLFNE54BZVDNCEUGDTBCUGNVZULIQC
SOCJXX6MMOXLBEWBID4QN5FW2YNYULNNN7K3IRL7RSWK5EUNAZLQC
JYEEOUYQ7ZPKOGWUV7VCORBVSOLF2UCBFBH3TR75RGOSS6PNKYUAC
5FHWTG7M6FW4B3I33YI7QSM3OZIB6ZGC6TI6JISSLY5Y43HI56VAC
JLAGD2FAV3CWVO7PLXTHLT7MBI23TUSYLSNHFAWP5IB5UO77VKJAC
T4FNOPMWYYJHJBTTY33PB43HTJPKEC46L62YERTWIX73HYZSELXQC
CCRQESB4ADT4WA7FGLNZZXAJ6G5QMCTYCZIWORBN45P6ZPILC34AC
3DQXSE4YGFBBDUWK4YEOFWW4UPWILWELFSLP37SL6BERGAZJC5YAC
34LSODHJVOGATMHLJN67YGGWOV2PYDGAY3OAZGCU6J2ESULN6S3QC
2G7MZ653N3JUHJ4DA5Q7VRO3S5T27DLPKDCJEKB6DGYSTXULUVWAC
JI4NDSOXGGZ7QHXXFB3ZTHAKHABXYBZXPDGLUFV5SKYEOL5FT7JQC
EGV2HM7SD7UQSWJGLR65NQJTUBAJ7WHLM67FMH4UFP7JRSFKREPAC
B7MSPF6X2RLGWN4M6ZZF3WSOPKGYPTTD7LIJVST7DXN27DG6JHNAC
CUB27EJDQG66FF2YCKOV4HU3LAJVJIHUJ5QYLURRDIEVGPK666DQC
LUH6GAJODIQXNPBEHMSUTKH3KA3DYVRCKZ6GJJ4HHNHOIMMUEP6QC
NS3KXJXQSN33UQSOBDK3WXXKA3KY5YOUJL67NBZKGQAJYDYZ2COQC
CDFS7Z74W5HKPQIHQICOG442KQFXUSBGGLDDQLE3MG74T44YU6AQC
AJHVP42Y67SB4NKFMZB24524PGX2XA5WLFNEFV52MIGTS47ALMVQC
UAJN2CFA2QHYDHW2UFAVPPHDQFCD54RKM6V2UC4AMEDJUBBLNWIQC
PI5BATR2SER3RFE76IUGHM2AGXVFOUM3PLU7WC2K2Q2BA5K2E73QC
SIDH2P7NBIG5KEOE27XHD3ZT2NQ2OJZFN6VZXWNWYFFY5YVXSSVQC
RBKHIKKQ5LSNWJA7ZIJ6B7HF36UA6LPZLVEWOJ3X5CUSQUOJGFZQC
YHC3WZ5ATCJCQQ4CMWJA2IDPSY6FQZP7HJL6JI5WXETTY54BJU2AC
25CH7HH4LKXFIZ75YNMXS3TSXO6O27DYSOPLOD45K4OCNFWLS4LQC
TGJZXTUIAKCFZQJ54ZQEBGFBVZSJCAX6AWDRSH3TP7UJRLGUM5SAC
FLAGBNUNSIQNFDN53CDWABJRTTFWDL4PG34AI474ZKPXDEPYHOAQC
K33CV7EYR37TTSEXQWQ6QPHSEUFO545AIPUZOA2C47QTUCUWFPAAC
AYU5OVG2HZO46KDAPKUWAVHS5HTYFKUWIMIRMTHAXVVFEDJE7YPAC
}
bool grid_is_staircase(dungeon_feature_type grid)
{
if (grid_is_stone_stair(grid))
return (true);
// All branch entries/exits are staircases, except for Zot.
if (grid == DNGN_ENTER_ZOT || grid == DNGN_RETURN_FROM_ZOT)
return (false);
return (grid >= DNGN_ENTER_FIRST_BRANCH && grid <= DNGN_ENTER_LAST_BRANCH
|| grid >= DNGN_RETURN_FROM_FIRST_BRANCH
&& grid <= DNGN_RETURN_FROM_LAST_BRANCH);
{
monsters *mons = &menv[mindex];
if (mons->has_ench(ENCH_SUBMERGED)
&& !monster_can_submerge(mons, grd(pos)))
{
mons->del_ench(ENCH_SUBMERGED);
}
if (grid_is_solid(grd(pos)))
monster_teleport(mons, true, false);
else
mons_check_pool(mons, mons->pos(), KILL_MISC, -1);
}
menv[mindex].apply_location_effects(pos);
if (!you.airborne())
{
// If the monster can't stay submerged in the new terrain
// and there aren't any adjacent squares where it can
// stay submerged then move it.
if (mgrd(you.pos()) != NON_MONSTER
&& !mons_is_submerged( &menv[ mgrd(you.pos()) ] ))
{
monster_teleport( &menv[ mgrd(you.pos()) ], true, false);
}
move_player_to_grid(pos, false, true, false);
}
// If the monster can't stay submerged in the new terrain and
// there aren't any adjacent squares where it can stay
// submerged then move it.
const int midx = mgrd(you.pos());
if ( midx != NON_MONSTER && !mons_is_submerged( &menv[midx] ) )
monster_teleport( &menv[midx], true, false);
move_player_to_grid(pos, false, true, true);
ASSERT(in_bounds(pos1) && in_bounds(pos2));
ASSERT(pos1 != pos2);
dungeon_feature_type orig_feat = grd(dest_pos);
std::string orig_name =
feature_description(dest_pos, false,
see_grid(orig_pos) ? DESC_CAP_THE : DESC_CAP_A,
false);
std::string prep = grid_preposition(orig_feat, true);
std::string orig_actor, dest_actor;
if (orig_pos == you.pos())
orig_actor = "you";
else if (mgrd(orig_pos) != NON_MONSTER)
{
monsters &mon(menv[mgrd(orig_pos)]);
if (you.can_see(&mon))
orig_actor = mon.name(DESC_NOCAP_THE);
}
if (dest_pos == you.pos())
dest_actor = "you";
else if (mgrd(dest_pos) != NON_MONSTER)
{
monsters &mon(menv[mgrd(dest_pos)]);
if (you.can_see(&mon))
dest_actor = mon.name(DESC_NOCAP_THE);
}
std::ostringstream str;
str << orig_name << " ";
if (see_grid(orig_pos) && !see_grid(dest_pos))
{
str << "suddenly disappears";
if (!orig_actor.empty())
str << " from " << prep << " " << orig_actor;
}
else if (!see_grid(orig_pos) && see_grid(dest_pos))
{
str << "suddenly appears";
if (!dest_actor.empty())
str << " " << prep << " " << dest_actor;
}
else
{
str << "moves";
if (!orig_actor.empty())
str << " from " << prep << " " << orig_actor;
if (!dest_actor.empty())
str << " to " << prep << " " << dest_actor;
}
str << "!";
mpr(str.str().c_str());
}
static void _announce_swap(coord_def pos1, coord_def pos2)
{
if (!see_grid(pos1) && !see_grid(pos2))
return;
if (feat1 == feat2)
return;
const bool notable_seen1 = is_notable_terrain(feat1) && see_grid(pos1);
const bool notable_seen2 = is_notable_terrain(feat2) && see_grid(pos2);
coord_def orig_pos, dest_pos;
if (notable_seen1 && notable_seen2)
{
_announce_swap_real(pos1, pos2);
_announce_swap_real(pos2, pos1);
}
else if (notable_seen1)
_announce_swap_real(pos2, pos1);
else if (notable_seen2)
_announce_swap_real(pos1, pos2);
else if (see_grid(pos2))
_announce_swap_real(pos1, pos2);
else
_announce_swap_real(pos2, pos1);
}
bool swap_features(const coord_def &pos1, const coord_def &pos2,
bool swap_everything, bool announce)
{
ASSERT(in_bounds(pos1) && in_bounds(pos2));
ASSERT(pos1 != pos2);
return (false);
if (is_notable_terrain(dest_feat))
return (false);
actor* dest_actor = NULL;
if (dest_pos == you.pos())
dest_actor = dynamic_cast<actor*>(&you);
else if (mgrd(dest_pos) != NON_MONSTER)
dest_actor = dynamic_cast<actor*>(&menv[mgrd(dest_pos)]);
if (orig_actor && !orig_actor->is_habitable_feat(dest_feat))
return (false);
if (dest_actor && !dest_actor->is_habitable_feat(orig_feat))
return (false);
return (true);
}
bool slide_feature_over(const coord_def &src, coord_def prefered_dest,
bool announce)
{
ASSERT(in_bounds(src));
const dungeon_feature_type orig_feat = grd(src);
actor* orig_actor = NULL;
if (src == you.pos())
orig_actor = dynamic_cast<actor*>(&you);
else if (mgrd(src) != NON_MONSTER)
orig_actor = dynamic_cast<actor*>(&menv[mgrd(src)]);
if (in_bounds(prefered_dest)
&& _ok_dest_grid(orig_actor, orig_feat, prefered_dest))
{
ASSERT(prefered_dest != src);
}
else
{
radius_iterator ri(src, 1, true, false, true);
int squares = 0;
for (; ri; ++ri)
{
if (_ok_dest_grid(orig_actor, orig_feat, *ri))
{
if (one_chance_in(++squares))
prefered_dest = *ri;
}
}
}
if (!in_bounds(prefered_dest))
return (false);
ASSERT(prefered_dest != src);
return swap_features(src, prefered_dest, false, announce);
std::string grid_preposition(dungeon_feature_type grid, bool active,
const actor* who)
{
const bool airborne = !who || who->airborne();
const command_type dir = grid_stair_direction(grid);
if (dir == CMD_NO_CMD)
{
if (grid == DNGN_STONE_ARCH)
return "beside";
else if (grid_is_solid(grid))
{
if (active)
return "around";
else
return "inside";
}
else if (!airborne && grid >= DNGN_LAVA && grid <= DNGN_WATER_STUCK)
{
if (active)
return "around";
else
return "in";
}
else
{
if (active)
return "over";
else
return "above";
}
}
if (dir == CMD_GO_DOWNSTAIRS
&& (grid_is_staircase(grid) || grid_is_escape_hatch(grid)))
{
if (active)
return "over";
else
return "above";
}
else if (grid_is_escape_hatch(grid))
{
if (active)
return "under";
else
return "beneath";
}
else
return "beside";
}
std::string stair_climb_verb(dungeon_feature_type grid)
{
ASSERT(grid_stair_direction(grid) != CMD_NO_CMD);
if (grid_is_staircase(grid))
return "climb";
else if (grid_is_escape_hatch(grid))
return "use";
else
return "pass through";
}
command_type stair_dir = grid_stair_direction(new_grid);
if (stepped && stair_dir != CMD_NO_CMD
&& you.duration[DUR_REPEL_STAIRS_MOVE])
{
int pct;
if (you.duration[DUR_REPEL_STAIRS_CLIMB])
pct = 29;
else
pct = 50;
if (x_chance_in_y(pct, 100))
{
if (slide_feature_over(you.pos(), coord_def(-1, -1), false))
{
std::string stair_str =
feature_description(new_grid, NUM_TRAPS, false,
DESC_CAP_THE, false);
std::string prep = grid_preposition(new_grid, true, &you);
}
static bool _stair_moves_pre(dungeon_feature_type stair)
{
if (crawl_state.prev_cmd == CMD_WIZARD)
return (false);
if (stair != grd(you.pos()))
return (false);
if (grid_stair_direction(stair) == CMD_NO_CMD)
return (false);
if (!you.duration[DUR_REPEL_STAIRS_CLIMB])
return (false);
int pct;
if (you.duration[DUR_REPEL_STAIRS_MOVE])
pct = 29;
else
pct = 50;
if (!x_chance_in_y(pct, 100))
return (false);
// Get feature name before sliding stair over.
std::string stair_str =
feature_description(you.pos(), false, DESC_CAP_THE, false);
if (!slide_feature_over(you.pos(), coord_def(-1, -1), false))
return (false);
std::string verb = stair_climb_verb(stair);
mprf("%s moves away as you attempt to %s it!", stair_str.c_str(),
verb.c_str());
you.turn_is_over = true;
return (true);
// 50% chance of repelling the stair you just came through.
if (you.duration[DUR_REPEL_STAIRS_MOVE]
|| you.duration[DUR_REPEL_STAIRS_CLIMB])
{
dungeon_feature_type feat = grd(you.pos());
if (feat != DNGN_ENTER_SHOP
&& grid_stair_direction(feat) != CMD_NO_CMD
&& grid_stair_direction(stair_taken) != CMD_NO_CMD
&& coinflip()
&& slide_feature_over(you.pos(), coord_def(-1, -1), false))
{
std::string stair_str =
feature_description(feat, NUM_TRAPS, false,
DESC_CAP_THE, false);
std::string verb = stair_climb_verb(feat);
mprf("%s slides away from you right after you %s through it!",
stair_str.c_str(), verb.c_str());
}
}
// Stairs running from you is done now that you actually caught one.
you.duration[DUR_REPEL_STAIRS_MOVE] = 0;
you.duration[DUR_REPEL_STAIRS_CLIMB] = 0;
}
static bool _ok_dest_grid(const coord_def &dest)
{
dungeon_feature_type feat = grd(dest);
if (grid_destroys_items(feat) || grid_is_solid(feat)
|| grid_is_water(feat) || grid_is_trap(feat, true)
|| is_notable_terrain(feat))
{
return (false);
}
return (true);
}
if (!in_bounds(dest))
{
radius_iterator ri(attacker->pos(), 1, true, false, true);
int squares = 0;
for (; ri; ++ri)
{
if (_ok_dest_grid(*ri))
{
if (one_chance_in(++squares))
dest = *ri;
}
}
}
if (!in_bounds(dest))
return (false);
ASSERT(dest != orig_pos);
if (!swap_features(orig_pos, dest))
return (false);
// Is player aware of it happening?
if (!see_grid(orig_pos) && !see_grid(dest))
return (true);
std::string orig_actor, dest_actor;
if (orig_pos == you.pos())
orig_actor = "you";
else if (mgrd(orig_pos) != NON_MONSTER)
{
monsters &mon(menv[mgrd(orig_pos)]);
if (you.can_see(&mon))
orig_actor = mon.name(DESC_NOCAP_THE);
}
if (dest == you.pos())
dest_actor = "you";
else if (mgrd(dest) != NON_MONSTER)
{
monsters &mon(menv[mgrd(dest)]);
if (you.can_see(&mon))
dest_actor = mon.name(DESC_NOCAP_THE);
}
std::string stair_name =
feature_description(dest, false,
see_grid(orig_pos) ? DESC_CAP_THE : DESC_CAP_A,
false);
std::string prep;
if (grid_stair_direction(stair_feat) == CMD_GO_DOWNSTAIRS
&& (stair_name.find("stair") || grid_is_escape_hatch(stair_feat)))
{
prep = "beneath";
}
else if (grid_is_escape_hatch(stair_feat))
prep = "above";
else
prep = "beside";
std::ostringstream str;
str << stair_name << " ";
if (see_grid(orig_pos) && !see_grid(dest))
{
str << "suddenly disappears";
if (!orig_actor.empty())
str << " from " << prep << " " << orig_actor;
}
else if (!see_grid(orig_pos) && see_grid(dest))
{
str << "suddenly appears";
if (!dest_actor.empty())
str << " " << prep << " " << dest_actor;
}
else
{
str << "moves";
if (!orig_actor.empty())
str << " from " << prep << " " << orig_actor;
if (!dest_actor.empty())
str << " to " << prep << " " << dest_actor;
}
str << "!";
mpr(str.str().c_str());
return (true);
return slide_feature_over(attacker->pos(), dest);
static int stair_draw_count = 0;
static void _move_stair(coord_def stair_pos, bool away)
{
ASSERT(stair_pos != you.pos());
dungeon_feature_type feat = grd(stair_pos);
ASSERT(grid_stair_direction(feat) != CMD_NO_CMD);
coord_def begin, towards;
if (away)
{
begin = you.pos();
towards = stair_pos;
}
else
{
// Can't move towards player if it's already adjacent.
if (adjacent(you.pos(), stair_pos))
return;
begin = stair_pos;
towards = you.pos();
}
ray_def ray;
if (!find_ray(begin, towards, true, ray, 0, true))
{
mpr("Couldn't find ray between player and stairs.", MSGCH_ERROR);
return;
}
// Don't start off under the player.
if (away)
ray.advance();
bool found_stairs = false;
int past_stairs = 0;
while ( in_bounds(ray.pos()) && see_grid(ray.pos())
&& !grid_is_solid(ray.pos()) && ray.pos() != you.pos() )
{
if (ray.pos() == stair_pos)
found_stairs = true;
if (found_stairs)
past_stairs++;
ray.advance();
}
past_stairs--;
if (!away && grid_is_solid(ray.pos()))
// Transparent wall between stair and player.
return;
if (away && !found_stairs)
{
if (grid_is_solid(ray.pos()))
// Transparent wall between stair and player.
return;
mpr("Ray didn't cross stairs.", MSGCH_ERROR);
}
if (away && past_stairs <= 0)
// Stairs already at edge, can't move further away.
return;
if ( !in_bounds(ray.pos()) || ray.pos() == you.pos() )
ray.regress();
while (!see_grid(ray.pos()) || grd(ray.pos()) != DNGN_FLOOR)
{
ray.regress();
if (!in_bounds(ray.pos()) || ray.pos() == you.pos()
|| ray.pos() == stair_pos)
{
// No squares in path are a plain floor.
return;
}
}
ASSERT(stair_pos != ray.pos());
std::string stair_str =
feature_description(stair_pos, false, DESC_CAP_THE, false);
mprf("%s slides %s you!", stair_str.c_str(),
away ? "away from" : "towards");
// Animate stair moving.
const feature_def &feat_def = get_feature_def(feat);
bolt beam;
beam.range = INFINITE_DISTANCE;
beam.flavour = BEAM_VISUAL;
beam.type = feat_def.symbol;
beam.colour = feat_def.colour;
beam.source = stair_pos;
beam.target = ray.pos();
beam.delay = 50; // Make beam animation slower than normal.
beam.name = "STAIR BEAM";
beam.aimed_at_spot = true;
fire_beam(beam);
// Clear out "missile trails"
viewwindow(true, false);
if (!swap_features(stair_pos, ray.pos(), false, false))
mprf(MSGCH_ERROR, "_move_stair(): failed to move %s",
stair_str.c_str());
}
static void _stairs_card(int power, deck_rarity_type rarity)
{
UNUSED(power);
UNUSED(rarity);
you.duration[DUR_REPEL_STAIRS_MOVE] = 0;
you.duration[DUR_REPEL_STAIRS_CLIMB] = 0;
if (grid_stair_direction(grd(you.pos())) == CMD_NO_CMD)
you.duration[DUR_REPEL_STAIRS_MOVE] = 1000;
else
you.duration[DUR_REPEL_STAIRS_CLIMB] = 1000;
std::vector<coord_def> stairs_avail;
radius_iterator ri(you.pos(), LOS_RADIUS, false, true, true);
for (; ri; ++ri)
{
dungeon_feature_type feat = grd(*ri);
if (grid_stair_direction(feat) != CMD_NO_CMD
&& feat != DNGN_ENTER_SHOP)
{
stairs_avail.push_back(*ri);
}
}
if (stairs_avail.size() == 0)
{
mpr("No stairs available to move.");
return;
}
std::random_shuffle(stairs_avail.begin(), stairs_avail.end());
for (unsigned int i = 0; i < stairs_avail.size(); i++)
_move_stair(stairs_avail[i], stair_draw_count % 2);
stair_draw_count++;
}
if (pbolt.flavour != BEAM_LINE_OF_SIGHT)
{
mprf( MSGCH_DIAGNOSTICS, "%s%s%s [%s] (%d,%d) to (%d,%d): "
"ty=%d col=%d flav=%d hit=%d dam=%dd%d range=%d",
(pbolt.is_beam) ? "beam" : "missile",
(pbolt.is_explosion) ? "*" :
(pbolt.is_big_cloud) ? "+" : "",
(pbolt.is_tracer) ? " tracer" : "",
pbolt.name.c_str(),
pbolt.source.x, pbolt.source.y,
pbolt.target.x, pbolt.target.y,
pbolt.type, pbolt.colour, pbolt.flavour,
pbolt.hit, pbolt.damage.num, pbolt.damage.size,
pbolt.range);
}
mprf( MSGCH_DIAGNOSTICS, "%s%s%s [%s] (%d,%d) to (%d,%d): "
"ty=%d col=%d flav=%d hit=%d dam=%dd%d range=%d",
(pbolt.is_beam) ? "beam" : "missile",
(pbolt.is_explosion) ? "*" :
(pbolt.is_big_cloud) ? "+" : "",
(pbolt.is_tracer) ? " tracer" : "",
pbolt.name.c_str(),
pbolt.source.x, pbolt.source.y,
pbolt.target.x, pbolt.target.y,
pbolt.type, pbolt.colour, pbolt.flavour,
pbolt.hit, pbolt.damage.num, pbolt.damage.size,
pbolt.range);
if (beam.flavour == BEAM_LINE_OF_SIGHT)
{
if (beam.thrower != KILL_YOU_MISSILE)
beam.foe_count++;
return (true);
}