Renamed namespaces to all lowercase
[supertux.git] / src / badguy / willowisp.cpp
1 //  SuperTux - "Will-O-Wisp" Badguy
2 //  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "badguy/willowisp.hpp"
18
19 #include "audio/sound_manager.hpp"
20 #include "audio/sound_source.hpp"
21 #include "object/lantern.hpp"
22 #include "object/path_walker.hpp"
23 #include "object/player.hpp"
24 #include "scripting/squirrel_util.hpp"
25 #include "sprite/sprite.hpp"
26 #include "supertux/game_session.hpp"
27 #include "supertux/object_factory.hpp"
28 #include "supertux/sector.hpp"
29 #include "util/reader.hpp"
30
31 static const float FLYSPEED = 64; /**< speed in px per second */
32 static const float TRACK_RANGE = 384; /**< at what distance to start tracking the player */
33 static const float VANISH_RANGE = 512; /**< at what distance to stop tracking and vanish */
34 static const std::string SOUNDFILE = "sounds/willowisp.wav";
35
36 WillOWisp::WillOWisp(const Reader& reader) :
37   BadGuy(reader, "images/creatures/willowisp/willowisp.sprite", LAYER_FLOATINGOBJECTS), 
38   mystate(STATE_IDLE), 
39   target_sector("main"), 
40   target_spawnpoint("main"),
41   hit_script(),
42   sound_source(),
43   path(),
44   walker(),
45   flyspeed(),
46   track_range(),
47   vanish_range()
48 {
49   bool running = false;
50   flyspeed     = FLYSPEED;
51   track_range  = TRACK_RANGE;
52   vanish_range = VANISH_RANGE;
53
54   reader.get("sector", target_sector);
55   reader.get("spawnpoint", target_spawnpoint);
56   reader.get("name", name);
57   reader.get("flyspeed", flyspeed);
58   reader.get("track-range", track_range);
59   reader.get("vanish-range", vanish_range);
60   reader.get("hit-script", hit_script);
61   reader.get("running", running);
62
63   const lisp::Lisp* pathLisp = reader.get_lisp("path");
64   if(pathLisp != NULL) {
65     path.reset(new Path());
66     path->read(*pathLisp);
67     walker.reset(new PathWalker(path.get(), running));
68     if(running)
69       mystate = STATE_PATHMOVING_TRACK;
70   }
71
72   countMe = false;
73   sound_manager->preload(SOUNDFILE);
74   sound_manager->preload("sounds/warp.wav");
75
76   sprite->set_action("idle");
77 }
78
79 void
80 WillOWisp::draw(DrawingContext& context)
81 {
82   sprite->draw(context, get_pos(), layer);
83
84   context.push_target();
85   context.set_target(DrawingContext::LIGHTMAP);
86
87   sprite->draw(context, get_pos(), layer);
88
89   context.pop_target();
90 }
91
92 void
93 WillOWisp::active_update(float elapsed_time)
94 {
95   Player* player = get_nearest_player();
96   if (!player) return;
97   Vector p1 = this->get_pos() + (this->get_bbox().p2 - this->get_bbox().p1) / 2;
98   Vector p2 = player->get_pos() + (player->get_bbox().p2 - player->get_bbox().p1) / 2;
99   Vector dist = (p2 - p1);
100
101   switch(mystate) {
102     case STATE_STOPPED:
103       break;
104
105     case STATE_IDLE:
106       if (dist.norm() <= track_range) {
107         mystate = STATE_TRACKING;
108       }
109       break;
110
111     case STATE_TRACKING:
112       if (dist.norm() > vanish_range) {
113         vanish();
114       } else if (dist.norm() >= 1) {
115         Vector dir = dist.unit();
116         movement = dir * elapsed_time * flyspeed;
117       } else {
118         /* We somehow landed right on top of the player without colliding.
119          * Sit tight and avoid a division by zero. */
120       }
121       sound_source->set_position(get_pos());
122       break;
123
124     case STATE_WARPING:
125       if(sprite->animation_done()) {
126         remove_me();
127       }
128
129     case STATE_VANISHING: {
130       Vector dir = dist.unit();
131       movement = dir * elapsed_time * flyspeed;
132       if(sprite->animation_done()) {
133         remove_me();
134       }
135       break;
136     }
137
138     case STATE_PATHMOVING:
139     case STATE_PATHMOVING_TRACK:
140       if(walker.get() == NULL)
141         return;
142       movement = walker->advance(elapsed_time) - get_pos();
143       if(mystate == STATE_PATHMOVING_TRACK && dist.norm() <= track_range) {
144         mystate = STATE_TRACKING;
145       }
146       break;
147
148     default:
149       assert(false);
150   }
151 }
152
153 void
154 WillOWisp::activate()
155 {
156   sound_source.reset(sound_manager->create_sound_source(SOUNDFILE));
157   sound_source->set_position(get_pos());
158   sound_source->set_looping(true);
159   sound_source->set_gain(2.0);
160   sound_source->set_reference_distance(32);
161   sound_source->play();
162 }
163
164 void
165 WillOWisp::deactivate()
166 {
167   sound_source.reset(NULL);
168
169   switch (mystate) {
170     case STATE_STOPPED:
171     case STATE_IDLE:
172     case STATE_PATHMOVING:
173     case STATE_PATHMOVING_TRACK:
174       break;
175     case STATE_TRACKING:
176       mystate = STATE_IDLE;
177       break;
178     case STATE_WARPING:
179     case STATE_VANISHING:
180       remove_me();
181       break;
182   }
183 }
184
185 void
186 WillOWisp::vanish()
187 {
188   mystate = STATE_VANISHING;
189   sprite->set_action("vanishing", 1);
190   set_colgroup_active(COLGROUP_DISABLED);
191 }
192
193 bool
194 WillOWisp::collides(GameObject& other, const CollisionHit& ) {
195   Lantern* lantern = dynamic_cast<Lantern*>(&other);
196
197   if (lantern && lantern->is_open())
198     return true;
199
200   if (dynamic_cast<Player*>(&other))
201     return true;
202
203   return false;
204 }
205
206 HitResponse
207 WillOWisp::collision_player(Player& player, const CollisionHit& ) {
208   if(player.is_invincible())
209     return ABORT_MOVE;
210
211   if (mystate != STATE_TRACKING)
212     return ABORT_MOVE;
213
214   mystate = STATE_WARPING;
215   sprite->set_action("warping", 1);
216
217   if(hit_script != "") {
218     std::istringstream stream(hit_script);
219     Sector::current()->run_script(stream, "hit-script");
220   } else {
221     GameSession::current()->respawn(target_sector, target_spawnpoint);
222   }
223   sound_manager->play("sounds/warp.wav");
224
225   return CONTINUE;
226 }
227
228 void
229 WillOWisp::goto_node(int node_no)
230 {
231   walker->goto_node(node_no);
232   if(mystate != STATE_PATHMOVING && mystate != STATE_PATHMOVING_TRACK) {
233     mystate = STATE_PATHMOVING;
234   }
235 }
236
237 void
238 WillOWisp::start_moving()
239 {
240   walker->start_moving();
241 }
242
243 void
244 WillOWisp::stop_moving()
245 {
246   walker->stop_moving();
247 }
248
249 void
250 WillOWisp::set_state(const std::string& new_state)
251 {
252   if(new_state == "stopped") {
253     mystate = STATE_STOPPED;
254   } else if(new_state == "idle") {
255     mystate = STATE_IDLE;
256   } else if(new_state == "move_path") {
257     mystate = STATE_PATHMOVING;
258     walker->start_moving();
259   } else if(new_state == "move_path_track") {
260     mystate = STATE_PATHMOVING_TRACK;
261     walker->start_moving();
262   } else if(new_state == "normal") {
263     mystate = STATE_IDLE;
264   } else if(new_state == "vanish") {
265     vanish();
266   } else {
267     std::ostringstream msg;
268     msg << "Can't set unknown willowisp state '" << new_state << "', should "
269       "be stopped, move_path, move_path_track or normal";
270     throw new std::runtime_error(msg.str());
271   }
272 }
273
274 void
275 WillOWisp::expose(HSQUIRRELVM vm, SQInteger table_idx)
276 {
277   if (name.empty())
278     return;
279
280   std::cout << "Expose me '" << name << "'\n";
281   scripting::WillOWisp* interface = static_cast<scripting::WillOWisp*> (this);
282   expose_object(vm, table_idx, interface, name);
283 }
284   
285 void
286 WillOWisp::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
287 {
288   if (name.empty())
289     return;
290
291   std::cout << "UnExpose me '" << name << "'\n";
292   scripting::unexpose_object(vm, table_idx, name);
293 }
294
295 IMPLEMENT_FACTORY(WillOWisp, "willowisp");
296
297 /* EOF */