3 #include "spl-damage.h"
9 #include "godconduct.h"
20 static bool _airtight(coord_def c
)
22 return grd(c
) <= DNGN_MAXWALL
;
25 /* Explanation of the algorithm:
26 http://en.wikipedia.org/wiki/Biconnected_component
27 We include everything up to and including the first articulation vertex,
28 the center is never considered to be one.
33 SquareArray
<int, TORNADO_RADIUS
+1> depth
;
34 SquareArray
<bool, TORNADO_RADIUS
+1> cut
, wind
;
35 int visit(coord_def c
, int d
, coord_def parent
);
36 void pass_wind(coord_def c
);
38 WindSystem(coord_def _org
);
39 bool has_wind(coord_def c
);
42 WindSystem::WindSystem(coord_def _org
)
47 visit(org
, 0, coord_def(0,0));
48 cut(coord_def(0,0)) = false;
53 int WindSystem::visit(coord_def c
, int d
, coord_def parent
)
59 for (adjacent_iterator
ai(c
); ai
; ++ai
)
61 if ((*ai
- org
).abs() > dist_range(TORNADO_RADIUS
) || _airtight(*ai
))
63 if (depth(*ai
- org
) == -1)
65 int sonlow
= visit(*ai
, d
+1, c
);
66 low
= std::min(low
, sonlow
);
67 sonmax
= std::max(sonmax
, sonlow
);
69 else if (*ai
!= parent
)
70 low
= std::min(low
, depth(*ai
- org
));
73 cut(c
- org
) = (sonmax
>= d
);
77 void WindSystem::pass_wind(coord_def c
)
84 for (adjacent_iterator
ai(c
); ai
; ++ai
)
85 if (depth(*ai
- org
) != -1)
89 bool WindSystem::has_wind(coord_def c
)
91 ASSERT(grid_distance(c
, org
) <= TORNADO_RADIUS
); // might say no instead
95 static void _set_tornado_durations(int powc
)
97 int dur
= 40 + powc
/ 6;
98 you
.duration
[DUR_TORNADO
] = dur
;
99 you
.duration
[DUR_LEVITATION
] =
100 std::max(dur
, you
.duration
[DUR_LEVITATION
]);
101 you
.duration
[DUR_CONTROLLED_FLIGHT
] =
102 std::max(dur
, you
.duration
[DUR_CONTROLLED_FLIGHT
]);
103 you
.attribute
[ATTR_LEV_UNCANCELLABLE
] = 1;
106 bool cast_tornado(int powc
)
108 if (you
.duration
[DUR_TORNADO
])
110 _set_tornado_durations(powc
);
111 mpr("The winds around you grow in strength.");
115 bool friendlies
= false;
116 for (radius_iterator
ri(you
.pos(), TORNADO_RADIUS
, C_ROUND
); ri
; ++ri
)
118 const monster_info
* m
= env
.map_knowledge(*ri
).monsterinfo();
121 if (mons_att_wont_attack(m
->attitude
)
122 && mons_class_res_wind(m
->type
) <= 0
123 && !mons_is_projectile(m
->type
))
130 && !yesno("There are friendlies around, are you sure you want to hurt them?",
136 mprf("A great vortex of raging winds %s.",
137 you
.is_levitating() ? "appears around you"
138 : "appears and lifts you up");
141 merfolk_stop_swimming();
143 _set_tornado_durations(powc
);
149 void tornado_damage(actor
*caster
, int dur
)
155 // Not stored so unwielding that staff will reduce damage.
156 if (caster
->atype() == ACT_PLAYER
)
157 pow
= calc_spell_power(SPELL_TORNADO
, true);
159 pow
= caster
->as_monster()->hit_dice
* 4;
161 pow
= div_rand_round(pow
* dur
, 10);
162 dprf("Doing tornado, dur %d, effective power %d", dur
, pow
);
163 const coord_def org
= caster
->pos();
164 WindSystem
winds(org
);
166 std::stack
<actor
*> move_act
;
170 distance_iterator
count_i(org
, false);
171 distance_iterator
dam_i(org
, true);
172 for (int r
= 1; r
<= TORNADO_RADIUS
; r
++)
174 while (count_i
&& count_i
.radius() == r
)
176 if (winds
.has_wind(*count_i
))
181 int rpow
= pow
* cnt_open
/ cnt_all
;
182 dprf("at dist %d pow is %d", r
, rpow
);
186 std::vector
<coord_def
> clouds
;
187 for (; dam_i
&& dam_i
.radius() == r
; dam_i
++)
189 if (feat_is_tree(grd(*dam_i
)) && dur
> 0 && one_chance_in(20))
191 grd(*dam_i
) = DNGN_FLOOR
;
192 set_terrain_changed(*dam_i
);
193 if (you
.see_cell(*dam_i
))
194 mpr("A tree falls to the hurricane!");
195 did_god_conduct(DID_KILL_PLANT
, 1);
198 if (!winds
.has_wind(*dam_i
))
201 if (actor
* victim
= actor_at(*dam_i
))
203 if (victim
->submerged())
206 if (!victim
->res_wind())
208 if (victim
->atype() == ACT_MONSTER
)
210 // levitate the monster so you get only one attempt at
211 // tossing them into water/lava
212 monster
*mon
= victim
->as_monster();
213 mon_enchant
ench(ENCH_LEVITATION
, 0,
214 caster
->kill_alignment(), 20);
215 if (mon
->has_ench(ENCH_LEVITATION
))
216 mon
->update_ench(ench
);
219 behaviour_event(mon
, ME_ANNOY
, caster
->mindex());
220 if (mons_is_mimic(mon
->type
))
225 bool standing
= !you
.airborne();
227 mpr("The vortex of raging winds lifts you up.");
228 you
.attribute
[ATTR_LEV_UNCANCELLABLE
] = 1;
229 you
.duration
[DUR_LEVITATION
]
230 = std::max(you
.duration
[DUR_LEVITATION
], 20);
234 int dmg
= roll_dice(6, rpow
) / 8;
237 dprf("damage done: %d", dmg
);
238 if (victim
->atype() == ACT_PLAYER
)
239 ouch(dmg
, caster
->mindex(), KILLED_BY_BEAM
,
242 victim
->hurt(caster
, dmg
);
246 move_act
.push(victim
);
248 if ((env
.cgrid(*dam_i
) == EMPTY_CLOUD
249 || env
.cloud
[env
.cgrid(*dam_i
)].type
== CLOUD_TORNADO
)
250 && x_chance_in_y(rpow
, 20))
252 place_cloud(CLOUD_TORNADO
, *dam_i
, 2 + random2(2), caster
);
254 clouds
.push_back(*dam_i
);
255 swap_clouds(clouds
[random2(clouds
.size())], *dam_i
);
260 void cancel_tornado()
262 if (!you
.duration
[DUR_TORNADO
])
265 dprf("Aborting tornado.");
266 if (you
.duration
[DUR_TORNADO
] == you
.duration
[DUR_LEVITATION
])
268 you
.duration
[DUR_LEVITATION
] = 0;
269 you
.duration
[DUR_CONTROLLED_FLIGHT
] = 0;
270 you
.attribute
[ATTR_LEV_UNCANCELLABLE
] = 0;
272 // NO checking for water, since this is called only during level
273 // change, and being, say, banished from above water shouldn't
276 you
.duration
[DUR_TORNADO
] = 0;