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