Apply the new ground_level method.
[crawl.git] / crawl-ref / source / spl-tornado.cc
blob86b1cd62b16829397ffa970d58caf7dceade18ec
1 #include "AppHdr.h"
3 #include "spl-damage.h"
5 #include "cloud.h"
6 #include "coord.h"
7 #include "coordit.h"
8 #include "env.h"
9 #include "godconduct.h"
10 #include "los.h"
11 #include "message.h"
12 #include "misc.h"
13 #include "mon-behv.h"
14 #include "ouch.h"
15 #include "player.h"
16 #include "spl-cast.h"
17 #include "stuff.h"
18 #include "terrain.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.
30 class WindSystem
32 coord_def org;
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);
37 public:
38 WindSystem(coord_def _org);
39 bool has_wind(coord_def c);
42 WindSystem::WindSystem(coord_def _org)
44 org = _org;
45 depth.init(-1);
46 cut.init(false);
47 visit(org, 0, coord_def(0,0));
48 cut(coord_def(0,0)) = false;
49 wind.init(false);
50 pass_wind(org);
53 int WindSystem::visit(coord_def c, int d, coord_def parent)
55 depth(c - org) = d;
56 int low = d;
57 int sonmax = -1;
59 for (adjacent_iterator ai(c); ai; ++ai)
61 if ((*ai - org).abs() > dist_range(TORNADO_RADIUS) || _airtight(*ai))
62 continue;
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);
74 return low;
77 void WindSystem::pass_wind(coord_def c)
79 wind(c - org) = true;
80 depth(c - org) = -1;
81 if (cut(c - org))
82 return;
84 for (adjacent_iterator ai(c); ai; ++ai)
85 if (depth(*ai - org) != -1)
86 pass_wind(*ai);
89 bool WindSystem::has_wind(coord_def c)
91 ASSERT(grid_distance(c, org) <= TORNADO_RADIUS); // might say no instead
92 return wind(c - org);
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.");
112 return true;
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();
119 if (!m)
120 continue;
121 if (mons_att_wont_attack(m->attitude)
122 && mons_class_res_wind(m->type) <= 0
123 && !mons_is_projectile(m->type))
125 friendlies = true;
129 if (friendlies
130 && !yesno("There are friendlies around, are you sure you want to hurt them?",
131 true, 'n'))
133 return false;
136 mprf("A great vortex of raging winds %s.",
137 you.is_levitating() ? "appears around you"
138 : "appears and lifts you up");
140 if (you.fishtail)
141 merfolk_stop_swimming();
143 _set_tornado_durations(powc);
144 burden_change();
146 return true;
149 void tornado_damage(actor *caster, int dur)
151 if (!dur)
152 return;
154 int pow;
155 // Not stored so unwielding that staff will reduce damage.
156 if (caster->atype() == ACT_PLAYER)
157 pow = calc_spell_power(SPELL_TORNADO, true);
158 else
159 pow = caster->as_monster()->hit_dice * 4;
160 if (dur > 0)
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;
167 int cnt_open = 0;
168 int cnt_all = 0;
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))
177 cnt_open++;
178 cnt_all++;
179 count_i++;
181 int rpow = pow * cnt_open / cnt_all;
182 dprf("at dist %d pow is %d", r, rpow);
183 if (!rpow)
184 break;
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))
199 continue;
201 if (actor* victim = actor_at(*dam_i))
203 if (victim->submerged())
204 continue;
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);
217 else
218 mon->add_ench(ench);
219 behaviour_event(mon, ME_ANNOY, caster->mindex());
220 if (mons_is_mimic(mon->type))
221 mimic_alert(mon);
223 else
225 bool standing = !you.airborne();
226 if (standing)
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);
231 if (standing)
232 float_player(false);
234 int dmg = roll_dice(6, rpow) / 8;
235 if (dur < 0)
236 dmg = 0;
237 dprf("damage done: %d", dmg);
238 if (victim->atype() == ACT_PLAYER)
239 ouch(dmg, caster->mindex(), KILLED_BY_BEAM,
240 "tornado");
241 else
242 victim->hurt(caster, dmg);
245 if (victim->alive())
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])
263 return;
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;
271 burden_change();
272 // NO checking for water, since this is called only during level
273 // change, and being, say, banished from above water shouldn't
274 // kill you.
276 you.duration[DUR_TORNADO] = 0;