2a9d03e9e05e393682f8b4c6d2a5c7c50828a5b1
[supertux.git] / src / badguy / dispenser.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.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  02111-1307, USA.
19
20 #include <config.h>
21
22 #include "dispenser.hpp"
23 #include "object/bullet.hpp"
24 #include "badguy/bouncing_snowball.hpp"
25 #include "badguy/snowball.hpp"
26 #include "badguy/mrbomb.hpp"
27 #include "badguy/mriceblock.hpp"
28 #include "badguy/mrrocket.hpp"
29 #include "badguy/poisonivy.hpp"
30 #include "badguy/snail.hpp"
31 #include "badguy/skullyhop.hpp"
32 #include "badguy/captainsnowball.hpp"
33 #include "badguy/kamikazesnowball.hpp"
34 #include "random_generator.hpp"
35
36 Dispenser::Dispenser(const lisp::Lisp& reader)
37         : BadGuy(reader, "images/creatures/dispenser/dispenser.sprite")
38 {
39   set_colgroup_active(COLGROUP_MOVING_STATIC);
40   sound_manager->preload("sounds/squish.wav");
41   reader.get("cycle", cycle);
42   reader.get("badguy", badguy);
43   autotarget = false;
44   swivel = false;
45   broken = false;
46   if (badguy == "mrrocket") {
47      sprite->set_action(dir == LEFT ? "working-left" : "working-right");
48      set_colgroup_active(COLGROUP_MOVING); //if this were COLGROUP_MOVING_STATIC MrRocket would explode on launch.
49      if( start_dir == AUTO ){
50       autotarget = true;
51      }
52   } else if ( badguy == "kamikazesnowball" ||  badguy == "captainsnowball" ) {
53      sprite->set_action("working");
54   } else {
55     sprite->set_action("dropper");
56   }
57   bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
58   countMe = false;
59 }
60
61 void
62 Dispenser::write(lisp::Writer& writer)
63 {
64   writer.start_list("dispenser");
65
66   writer.write_float("x", start_position.x);
67   writer.write_float("y", start_position.y);
68   writer.write_float("cycle", cycle);
69   writer.write_string("badguy", badguy);
70
71   writer.end_list("dispenser");
72 }
73
74 void
75 Dispenser::activate()
76 {
77    if( broken ){
78      return;
79    }
80    if( autotarget && !swivel ){ // auto cannon sprite might be wrong
81       Player* player = this->get_nearest_player();
82       if( player ){
83         dir = (player->get_pos().x > get_pos().x) ? RIGHT : LEFT;
84         sprite->set_action(dir == LEFT ? "working-left" : "working-right");
85       }
86    }
87    dispense_timer.start(cycle, true);
88    launch_badguy();
89 }
90
91 void
92 Dispenser::deactivate()
93 {
94    dispense_timer.stop();
95 }
96
97 //TODO: Add launching velocity to certain badguys
98 bool
99 Dispenser::collision_squished(GameObject& object)
100 {
101   //Cannon launching MrRocket can be broken by jumping on it
102   //other dispencers are not that fragile.
103   if (broken || badguy != "mrrocket") {
104     return false;
105   }
106   sprite->set_action(dir == LEFT ? "broken-left" : "broken-right");
107   dispense_timer.start(0);
108   set_colgroup_active(COLGROUP_MOVING_STATIC); // Tux can stand on broken cannon.
109   Player* player = dynamic_cast<Player*>(&object);
110   if (player){
111     player->bounce(*this);
112   }
113   sound_manager->play("sounds/squish.wav", get_pos());
114   broken = true;
115   return true;
116 }
117
118 HitResponse
119 Dispenser::collision(GameObject& other, const CollisionHit& hit)
120 {
121   Player* player = dynamic_cast<Player*> (&other);
122   if(player) {
123     // hit from above?
124     if (player->get_bbox().p2.y < (bbox.p1.y + 16)) {
125       collision_squished(*player);
126       return FORCE_MOVE;
127     }
128     if(frozen){
129       unfreeze();
130     }
131     return FORCE_MOVE;
132   }
133
134   Bullet* bullet = dynamic_cast<Bullet*> (&other);
135   if(bullet){
136     return collision_bullet(*bullet, hit);
137   }
138
139   return FORCE_MOVE;
140 }
141
142
143 void
144 Dispenser::active_update(float )
145 {
146   if (dispense_timer.check()) {
147     // auto always shoots in Tux's direction
148     if( autotarget ){ 
149       if( sprite->animation_done()) {
150         sprite->set_action(dir == LEFT ? "working-left" : "working-right");
151         swivel = false;
152       }
153
154       Player* player = this->get_nearest_player();
155       if( player && !swivel ){
156         Direction targetdir = (player->get_pos().x > get_pos().x) ? RIGHT : LEFT;
157         if( dir != targetdir ){ // no target: swivel cannon 
158           swivel = true;
159           dir = targetdir;
160           sprite->set_action(dir == LEFT ? "swivel-left" : "swivel-right", 1);
161         } else { // tux in sight: shoot
162           launch_badguy();
163         }
164       }
165     } else {
166       launch_badguy();
167     }
168   }
169 }
170
171 //      Add themed randomizer
172 void
173 Dispenser::launch_badguy()
174 {
175   //FIXME: Does is_offscreen() work right here?
176   if (!is_offscreen()) {
177     Direction launchdir = dir;
178     if( !autotarget && start_dir == AUTO ){
179       Player* player = this->get_nearest_player();
180       if( player ){
181         launchdir = (player->get_pos().x > get_pos().x) ? RIGHT : LEFT;
182       } 
183     } 
184     if (badguy == "snowball")
185       Sector::current()->add_object(new SnowBall(Vector(get_pos().x, get_pos().y+32), launchdir));
186     else if (badguy == "bouncingsnowball")
187       Sector::current()->add_object(new BouncingSnowball(Vector(get_pos().x, get_pos().y+32), launchdir));
188     else if (badguy == "mrbomb")
189       Sector::current()->add_object(new MrBomb(Vector(get_pos().x, get_pos().y+32), launchdir));
190     else if (badguy == "mriceblock")
191       Sector::current()->add_object(new MrIceBlock(Vector(get_pos().x, get_pos().y+32), launchdir));
192     else if (badguy == "snail")
193       Sector::current()->add_object(new Snail(Vector(get_pos().x, get_pos().y+32), launchdir));
194     else if (badguy == "mrrocket") 
195       Sector::current()->add_object(new MrRocket(Vector(get_pos().x+(launchdir == LEFT ? -32 : 32), get_pos().y), launchdir));
196     else if (badguy == "captainsnowball")
197       Sector::current()->add_object(new CaptainSnowball(Vector(get_pos().x+(launchdir == LEFT ? -32 : 32), get_pos().y), launchdir));
198     else if (badguy == "kamikazesnowball")
199       Sector::current()->add_object(new KamikazeSnowball(Vector(get_pos().x+(launchdir == LEFT ? -32 : 32), get_pos().y), launchdir));
200     else if (badguy == "poisonivy")
201       Sector::current()->add_object(new PoisonIvy(Vector(get_pos().x, get_pos().y+32), launchdir));
202     else if (badguy == "skullyhop")
203       Sector::current()->add_object(new SkullyHop(Vector(get_pos().x, get_pos().y+44), launchdir));
204     else if (badguy == "random")
205     {
206       switch (systemRandom.rand(7))
207       {
208         case 0: Sector::current()->add_object(new SnowBall(Vector(get_pos().x, get_pos().y+32), launchdir)); break;
209         case 1: Sector::current()->add_object(new BouncingSnowball(Vector(get_pos().x, get_pos().y+32), launchdir)); break;
210         case 2: Sector::current()->add_object(new MrBomb(Vector(get_pos().x, get_pos().y+32), launchdir)); break;
211         case 3: Sector::current()->add_object(new MrIceBlock(Vector(get_pos().x, get_pos().y+32), launchdir)); break;
212         case 4: Sector::current()->add_object(new PoisonIvy(Vector(get_pos().x, get_pos().y+32), launchdir)); break;
213         case 5: Sector::current()->add_object(new Snail(Vector(get_pos().x, get_pos().y+32), launchdir)); break;
214         case 6: Sector::current()->add_object(new SkullyHop(Vector(get_pos().x, get_pos().y+44), launchdir)); break;
215       }
216     }
217   }
218 }
219
220 void
221 Dispenser::freeze()
222 {
223   BadGuy::freeze();
224   dispense_timer.stop();
225 }
226
227 void
228 Dispenser::unfreeze()
229 {
230   BadGuy::unfreeze();
231   activate();
232 }
233
234 bool
235 Dispenser::is_freezable() const
236 {
237   return true;
238 }
239 IMPLEMENT_FACTORY(Dispenser, "dispenser")