Merged changes from branches/supertux-milestone2-grumbel/ to trunk/supertux/
[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
30 static const float FLYSPEED = 64; /**< speed in px per second */
31 static const float TRACK_RANGE = 384; /**< at what distance to start tracking the player */
32 static const float VANISH_RANGE = 512; /**< at what distance to stop tracking and vanish */
33 static const std::string SOUNDFILE = "sounds/willowisp.wav";
34
35 WillOWisp::WillOWisp(const Reader& reader) :
36   BadGuy(reader, "images/creatures/willowisp/willowisp.sprite", LAYER_FLOATINGOBJECTS), 
37   mystate(STATE_IDLE), 
38   target_sector("main"), 
39   target_spawnpoint("main"),
40   hit_script(),
41   sound_source(),
42   path(),
43   walker(),
44   flyspeed(),
45   track_range(),
46   vanish_range()
47 {
48   bool running = false;
49   flyspeed     = FLYSPEED;
50   track_range  = TRACK_RANGE;
51   vanish_range = VANISH_RANGE;
52
53   reader.get("sector", target_sector);
54   reader.get("spawnpoint", target_spawnpoint);
55   reader.get("name", name);
56   reader.get("flyspeed", flyspeed);
57   reader.get("track-range", track_range);
58   reader.get("vanish-range", vanish_range);
59   reader.get("hit-script", hit_script);
60   reader.get("running", running);
61
62   const lisp::Lisp* pathLisp = reader.get_lisp("path");
63   if(pathLisp != NULL) {
64     path.reset(new Path());
65     path->read(*pathLisp);
66     walker.reset(new PathWalker(path.get(), running));
67     if(running)
68       mystate = STATE_PATHMOVING_TRACK;
69   }
70
71   countMe = false;
72   sound_manager->preload(SOUNDFILE);
73   sound_manager->preload("sounds/warp.wav");
74
75   sprite->set_action("idle");
76 }
77
78 void
79 WillOWisp::draw(DrawingContext& context)
80 {
81   sprite->draw(context, get_pos(), layer);
82
83   context.push_target();
84   context.set_target(DrawingContext::LIGHTMAP);
85
86   sprite->draw(context, get_pos(), layer);
87
88   context.pop_target();
89 }
90
91 void
92 WillOWisp::active_update(float elapsed_time)
93 {
94   Player* player = get_nearest_player();
95   if (!player) return;
96   Vector p1 = this->get_pos() + (this->get_bbox().p2 - this->get_bbox().p1) / 2;
97   Vector p2 = player->get_pos() + (player->get_bbox().p2 - player->get_bbox().p1) / 2;
98   Vector dist = (p2 - p1);
99
100   switch(mystate) {
101     case STATE_STOPPED:
102       break;
103
104     case STATE_IDLE:
105       if (dist.norm() <= track_range) {
106         mystate = STATE_TRACKING;
107       }
108       break;
109
110     case STATE_TRACKING:
111       if (dist.norm() > vanish_range) {
112         vanish();
113       } else if (dist.norm() >= 1) {
114         Vector dir = dist.unit();
115         movement = dir * elapsed_time * flyspeed;
116       } else {
117         /* We somehow landed right on top of the player without colliding.
118          * Sit tight and avoid a division by zero. */
119       }
120       sound_source->set_position(get_pos());
121       break;
122
123     case STATE_WARPING:
124       if(sprite->animation_done()) {
125         remove_me();
126       }
127
128     case STATE_VANISHING: {
129       Vector dir = dist.unit();
130       movement = dir * elapsed_time * flyspeed;
131       if(sprite->animation_done()) {
132         remove_me();
133       }
134       break;
135     }
136
137     case STATE_PATHMOVING:
138     case STATE_PATHMOVING_TRACK:
139       if(walker.get() == NULL)
140         return;
141       movement = walker->advance(elapsed_time) - get_pos();
142       if(mystate == STATE_PATHMOVING_TRACK && dist.norm() <= track_range) {
143         mystate = STATE_TRACKING;
144       }
145       break;
146
147     default:
148       assert(false);
149   }
150 }
151
152 void
153 WillOWisp::activate()
154 {
155   sound_source.reset(sound_manager->create_sound_source(SOUNDFILE));
156   sound_source->set_position(get_pos());
157   sound_source->set_looping(true);
158   sound_source->set_gain(2.0);
159   sound_source->set_reference_distance(32);
160   sound_source->play();
161 }
162
163 void
164 WillOWisp::deactivate()
165 {
166   sound_source.reset(NULL);
167
168   switch (mystate) {
169     case STATE_STOPPED:
170     case STATE_IDLE:
171     case STATE_PATHMOVING:
172     case STATE_PATHMOVING_TRACK:
173       break;
174     case STATE_TRACKING:
175       mystate = STATE_IDLE;
176       break;
177     case STATE_WARPING:
178     case STATE_VANISHING:
179       remove_me();
180       break;
181   }
182 }
183
184 void
185 WillOWisp::vanish()
186 {
187   mystate = STATE_VANISHING;
188   sprite->set_action("vanishing", 1);
189   set_colgroup_active(COLGROUP_DISABLED);
190 }
191
192 bool
193 WillOWisp::collides(GameObject& other, const CollisionHit& ) {
194   Lantern* lantern = dynamic_cast<Lantern*>(&other);
195
196   if (lantern && lantern->is_open())
197     return true;
198
199   if (dynamic_cast<Player*>(&other))
200     return true;
201
202   return false;
203 }
204
205 HitResponse
206 WillOWisp::collision_player(Player& player, const CollisionHit& ) {
207   if(player.is_invincible())
208     return ABORT_MOVE;
209
210   if (mystate != STATE_TRACKING)
211     return ABORT_MOVE;
212
213   mystate = STATE_WARPING;
214   sprite->set_action("warping", 1);
215
216   if(hit_script != "") {
217     std::istringstream stream(hit_script);
218     Sector::current()->run_script(stream, "hit-script");
219   } else {
220     GameSession::current()->respawn(target_sector, target_spawnpoint);
221   }
222   sound_manager->play("sounds/warp.wav");
223
224   return CONTINUE;
225 }
226
227 void
228 WillOWisp::goto_node(int node_no)
229 {
230   walker->goto_node(node_no);
231   if(mystate != STATE_PATHMOVING && mystate != STATE_PATHMOVING_TRACK) {
232     mystate = STATE_PATHMOVING;
233   }
234 }
235
236 void
237 WillOWisp::start_moving()
238 {
239   walker->start_moving();
240 }
241
242 void
243 WillOWisp::stop_moving()
244 {
245   walker->stop_moving();
246 }
247
248 void
249 WillOWisp::set_state(const std::string& new_state)
250 {
251   if(new_state == "stopped") {
252     mystate = STATE_STOPPED;
253   } else if(new_state == "idle") {
254     mystate = STATE_IDLE;
255   } else if(new_state == "move_path") {
256     mystate = STATE_PATHMOVING;
257     walker->start_moving();
258   } else if(new_state == "move_path_track") {
259     mystate = STATE_PATHMOVING_TRACK;
260     walker->start_moving();
261   } else if(new_state == "normal") {
262     mystate = STATE_IDLE;
263   } else if(new_state == "vanish") {
264     vanish();
265   } else {
266     std::ostringstream msg;
267     msg << "Can't set unknown willowisp state '" << new_state << "', should "
268       "be stopped, move_path, move_path_track or normal";
269     throw new std::runtime_error(msg.str());
270   }
271 }
272
273 void
274 WillOWisp::expose(HSQUIRRELVM vm, SQInteger table_idx)
275 {
276   if (name.empty())
277     return;
278
279   std::cout << "Expose me '" << name << "'\n";
280   Scripting::WillOWisp* interface = static_cast<Scripting::WillOWisp*> (this);
281   expose_object(vm, table_idx, interface, name);
282 }
283   
284 void
285 WillOWisp::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
286 {
287   if (name.empty())
288     return;
289
290   std::cout << "UnExpose me '" << name << "'\n";
291   Scripting::unexpose_object(vm, table_idx, name);
292 }
293
294 IMPLEMENT_FACTORY(WillOWisp, "willowisp");
295
296 /* EOF */