Make Badguy activation dependent of Player position, not currently-visible screen
[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   sound_manager->preload("sounds/warp.wav");
63
64   sprite->set_action("idle");
65 }
66
67 void
68 WillOWisp::draw(DrawingContext& context)
69 {
70   sprite->draw(context, get_pos(), layer);
71
72   context.push_target();
73   context.set_target(DrawingContext::LIGHTMAP);
74
75   sprite->draw(context, get_pos(), layer);
76
77   context.pop_target();
78 }
79
80 void
81 WillOWisp::active_update(float elapsed_time)
82 {
83   Player* player = get_nearest_player();
84   if (!player) return;
85   Vector p1 = this->get_pos() + (this->get_bbox().p2 - this->get_bbox().p1) / 2;
86   Vector p2 = player->get_pos() + (player->get_bbox().p2 - player->get_bbox().p1) / 2;
87   Vector dist = (p2 - p1);
88
89   switch(mystate) {
90   case STATE_STOPPED:
91     break;
92
93   case STATE_IDLE:
94     if (dist.norm() <= track_range) {
95       mystate = STATE_TRACKING;
96     }
97     break;
98
99   case STATE_TRACKING:
100     if (dist.norm() <= vanish_range) {
101       Vector dir = dist.unit();
102       movement = dir * elapsed_time * flyspeed;
103     } else {
104       vanish();
105     }
106     sound_source->set_position(get_pos());
107     break;
108
109   case STATE_WARPING:
110     if(sprite->animation_done()) {
111       remove_me();
112     }
113
114   case STATE_VANISHING: {
115     Vector dir = dist.unit();
116     movement = dir * elapsed_time * flyspeed;
117     if(sprite->animation_done()) {
118       remove_me();
119     }
120     break;
121   }
122
123   case STATE_PATHMOVING:
124   case STATE_PATHMOVING_TRACK:
125     if(walker.get() == NULL)
126       return;
127     movement = walker->advance(elapsed_time) - get_pos();
128     if(mystate == STATE_PATHMOVING_TRACK && dist.norm() <= track_range) {
129       mystate = STATE_TRACKING;
130     }
131     break;
132
133   default:
134     assert(false);
135   }
136 }
137
138 void
139 WillOWisp::activate()
140 {
141   sound_source.reset(sound_manager->create_sound_source(SOUNDFILE));
142   sound_source->set_position(get_pos());
143   sound_source->set_looping(true);
144   sound_source->set_gain(2.0);
145   sound_source->set_reference_distance(32);
146   sound_source->play();
147 }
148
149 void
150 WillOWisp::deactivate()
151 {
152   sound_source.reset(NULL);
153
154   switch (mystate) {
155     case STATE_STOPPED:
156     case STATE_IDLE:
157     case STATE_PATHMOVING:
158     case STATE_PATHMOVING_TRACK:
159       break;
160     case STATE_TRACKING:
161       mystate = STATE_IDLE;
162       break;
163     case STATE_WARPING:
164     case STATE_VANISHING:
165       remove_me();
166       break;
167   }
168 }
169
170 void
171 WillOWisp::vanish()
172 {
173   mystate = STATE_VANISHING;
174   sprite->set_action("vanishing", 1);
175   set_colgroup_active(COLGROUP_DISABLED);
176 }
177
178 bool
179 WillOWisp::collides(GameObject& other, const CollisionHit& ) {
180   Lantern* lantern = dynamic_cast<Lantern*>(&other);
181
182   if (lantern && lantern->is_open())
183     return true;
184
185   if (dynamic_cast<Player*>(&other))
186     return true;
187
188   return false;
189 }
190
191 HitResponse
192 WillOWisp::collision_player(Player& player, const CollisionHit& ) {
193   if(player.is_invincible())
194     return ABORT_MOVE;
195
196   if (mystate != STATE_TRACKING)
197     return ABORT_MOVE;
198
199   mystate = STATE_WARPING;
200   sprite->set_action("warping", 1);
201
202   if(hit_script != "") {
203     std::istringstream stream(hit_script);
204     Sector::current()->run_script(stream, "hit-script");
205   } else {
206     GameSession::current()->respawn(target_sector, target_spawnpoint);
207   }
208   sound_manager->play("sounds/warp.wav");
209
210   return CONTINUE;
211 }
212
213 void
214 WillOWisp::goto_node(int node_no)
215 {
216   walker->goto_node(node_no);
217   if(mystate != STATE_PATHMOVING && mystate != STATE_PATHMOVING_TRACK) {
218     mystate = STATE_PATHMOVING;
219   }
220 }
221
222 void
223 WillOWisp::start_moving()
224 {
225   walker->start_moving();
226 }
227
228 void
229 WillOWisp::stop_moving()
230 {
231   walker->stop_moving();
232 }
233
234 void
235 WillOWisp::set_state(const std::string& new_state)
236 {
237   if(new_state == "stopped") {
238     mystate = STATE_STOPPED;
239   } else if(new_state == "idle") {
240     mystate = STATE_IDLE;
241   } else if(new_state == "move_path") {
242     mystate = STATE_PATHMOVING;
243     walker->start_moving();
244   } else if(new_state == "move_path_track") {
245     mystate = STATE_PATHMOVING_TRACK;
246     walker->start_moving();
247   } else if(new_state == "normal") {
248     mystate = STATE_IDLE;
249   } else if(new_state == "vanish") {
250     vanish();
251   } else {
252     std::ostringstream msg;
253     msg << "Can't set unknown willowisp state '" << new_state << "', should "
254                 "be stopped, move_path, move_path_track or normal";
255     throw new std::runtime_error(msg.str());
256   }
257 }
258
259 void
260 WillOWisp::expose(HSQUIRRELVM vm, SQInteger table_idx)
261 {
262   if (name.empty())
263     return;
264
265   std::cout << "Expose me '" << name << "'\n";
266   Scripting::WillOWisp* interface = static_cast<Scripting::WillOWisp*> (this);
267   expose_object(vm, table_idx, interface, name);
268 }
269   
270 void
271 WillOWisp::unexpose(HSQUIRRELVM vm, SQInteger table_idx)
272 {
273   if (name.empty())
274     return;
275
276   std::cout << "UnExpose me '" << name << "'\n";
277   Scripting::unexpose_object(vm, table_idx, name);
278 }
279
280 IMPLEMENT_FACTORY(WillOWisp, "willowisp")