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