* Launcher can shoot two new types of snowballs.
[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 "badguy/bouncing_snowball.hpp"
24 #include "badguy/snowball.hpp"
25 #include "badguy/mrbomb.hpp"
26 #include "badguy/mriceblock.hpp"
27 #include "badguy/mrrocket.hpp"
28 #include "badguy/poisonivy.hpp"
29 #include "badguy/snail.hpp"
30 #include "badguy/skullyhop.hpp"
31 #include "badguy/captainsnowball.hpp"
32 #include "badguy/kamikazesnowball.hpp"
33 #include "random_generator.hpp"
34
35 Dispenser::Dispenser(const lisp::Lisp& reader)
36         : BadGuy(reader, "images/creatures/dispenser/dispenser.sprite")
37 {
38   reader.get("cycle", cycle);
39   reader.get("badguy", badguy);
40   autotarget = false;
41   swivel = false;
42   if (badguy == "mrrocket") {
43      sprite->set_action(dir == LEFT ? "working-left" : "working-right");
44      if( start_dir == AUTO ){
45       autotarget = true;
46      }
47   } else if ( badguy == "kamikazesnowball" ||  badguy == "captainsnowball" ) {
48      sprite->set_action("working");
49   } else {
50     sprite->set_action("dropper");
51   }
52   bbox.set_size(sprite->get_current_hitbox_width(), sprite->get_current_hitbox_height());
53   countMe = false;
54 }
55
56 void
57 Dispenser::write(lisp::Writer& writer)
58 {
59   writer.start_list("dispenser");
60
61   writer.write_float("x", start_position.x);
62   writer.write_float("y", start_position.y);
63   writer.write_float("cycle", cycle);
64   writer.write_string("badguy", badguy);
65
66   writer.end_list("dispenser");
67 }
68
69 void
70 Dispenser::activate()
71 {
72    if( autotarget && !swivel ){ // auto canon sprite might be wrong
73       Player* player = this->get_nearest_player();
74       if( player ){
75         dir = (player->get_pos().x > get_pos().x) ? RIGHT : LEFT;
76         sprite->set_action(dir == LEFT ? "working-left" : "working-right");
77       }
78    }
79    dispense_timer.start(cycle, true);
80    launch_badguy();
81 }
82
83 void
84 Dispenser::deactivate()
85 {
86    dispense_timer.stop();
87 }
88
89 //TODO: Add launching velocity to certain badguys
90 bool
91 Dispenser::collision_squished(GameObject& object)
92 {
93   //TODO: Should it act like a normal tile when killed?
94   sprite->set_action(dir == LEFT ? "broken-left" : "broken-right");
95   dispense_timer.start(0);
96   Player* player = dynamic_cast<Player*>(&object);
97   if (player) player->bounce(*this);
98   kill_squished(object);
99   return true;
100 }
101
102 void
103 Dispenser::active_update(float )
104 {
105   if (dispense_timer.check()) {
106     // auto always shoots in Tux's direction
107     if( autotarget ){ 
108       if( sprite->animation_done()) {
109         sprite->set_action(dir == LEFT ? "working-left" : "working-right");
110         swivel = false;
111       }
112
113       Player* player = this->get_nearest_player();
114       if( player && !swivel ){
115         Direction targetdir = (player->get_pos().x > get_pos().x) ? RIGHT : LEFT;
116         if( dir != targetdir ){ // no target: swivel cannon 
117           swivel = true;
118           dir = targetdir;
119           sprite->set_action(dir == LEFT ? "swivel-left" : "swivel-right", 1);
120         } else { // tux in sight: shoot
121           launch_badguy();
122         }
123       }
124     } else {
125       launch_badguy();
126     }
127   }
128 }
129
130 //      Add themed randomizer
131 void
132 Dispenser::launch_badguy()
133 {
134   //FIXME: Does is_offscreen() work right here?
135   if (!is_offscreen()) {
136     Direction launchdir = dir;
137     if( !autotarget && start_dir == AUTO ){
138       Player* player = this->get_nearest_player();
139       if( player ){
140         launchdir = (player->get_pos().x > get_pos().x) ? RIGHT : LEFT;
141       } 
142     } 
143     if (badguy == "snowball")
144       Sector::current()->add_object(new SnowBall(Vector(get_pos().x, get_pos().y+32), launchdir));
145     else if (badguy == "bouncingsnowball")
146       Sector::current()->add_object(new BouncingSnowball(Vector(get_pos().x, get_pos().y+32), launchdir));
147     else if (badguy == "mrbomb")
148       Sector::current()->add_object(new MrBomb(Vector(get_pos().x, get_pos().y+32), launchdir));
149     else if (badguy == "mriceblock")
150       Sector::current()->add_object(new MrIceBlock(Vector(get_pos().x, get_pos().y+32), launchdir));
151     else if (badguy == "snail")
152       Sector::current()->add_object(new Snail(Vector(get_pos().x, get_pos().y+32), launchdir));
153     else if (badguy == "mrrocket") 
154       Sector::current()->add_object(new MrRocket(Vector(get_pos().x+(launchdir == LEFT ? -32 : 32), get_pos().y), launchdir));
155     else if (badguy == "captainsnowball")
156       Sector::current()->add_object(new CaptainSnowball(Vector(get_pos().x+(launchdir == LEFT ? -32 : 32), get_pos().y), launchdir));
157     else if (badguy == "kamikazesnowball")
158       Sector::current()->add_object(new KamikazeSnowball(Vector(get_pos().x+(launchdir == LEFT ? -32 : 32), get_pos().y), launchdir));
159     else if (badguy == "poisonivy")
160       Sector::current()->add_object(new PoisonIvy(Vector(get_pos().x, get_pos().y+32), launchdir));
161     else if (badguy == "skullyhop")
162       Sector::current()->add_object(new SkullyHop(Vector(get_pos().x, get_pos().y+44), launchdir));
163     else if (badguy == "random")
164     {
165       switch (systemRandom.rand(7))
166       {
167         case 0: Sector::current()->add_object(new SnowBall(Vector(get_pos().x, get_pos().y+32), launchdir)); break;
168         case 1: Sector::current()->add_object(new BouncingSnowball(Vector(get_pos().x, get_pos().y+32), launchdir)); break;
169         case 2: Sector::current()->add_object(new MrBomb(Vector(get_pos().x, get_pos().y+32), launchdir)); break;
170         case 3: Sector::current()->add_object(new MrIceBlock(Vector(get_pos().x, get_pos().y+32), launchdir)); break;
171         case 4: Sector::current()->add_object(new PoisonIvy(Vector(get_pos().x, get_pos().y+32), launchdir)); break;
172         case 5: Sector::current()->add_object(new Snail(Vector(get_pos().x, get_pos().y+32), launchdir)); break;
173         case 6: Sector::current()->add_object(new SkullyHop(Vector(get_pos().x, get_pos().y+44), launchdir)); break;
174       }
175     }
176   }
177 }
178
179 void
180 Dispenser::freeze()
181 {
182   BadGuy::freeze();
183   dispense_timer.stop();
184 }
185
186 void
187 Dispenser::unfreeze()
188 {
189   BadGuy::unfreeze();
190   activate();
191 }
192
193 bool
194 Dispenser::is_freezable() const
195 {
196   return true;
197 }
198 IMPLEMENT_FACTORY(Dispenser, "dispenser")