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