2 // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
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.
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.
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/>.
17 #include "badguy/badguy.hpp"
19 #include "audio/sound_manager.hpp"
20 #include "object/bullet.hpp"
21 #include "object/player.hpp"
22 #include "supertux/level.hpp"
23 #include "supertux/sector.hpp"
24 #include "supertux/tile.hpp"
25 #include "util/reader.hpp"
30 static const float SQUISH_TIME = 2;
32 static const float X_OFFSCREEN_DISTANCE = 1280;
33 static const float Y_OFFSCREEN_DISTANCE = 800;
35 BadGuy::BadGuy(const Vector& pos, const std::string& sprite_name_, int layer_) :
36 MovingSprite(pos, sprite_name_, layer_, COLGROUP_DISABLED),
39 is_initialized(false),
50 on_ground_flag(false),
52 colgroup_active(COLGROUP_MOVING)
54 start_position = bbox.p1;
56 SoundManager::current()->preload("sounds/squish.wav");
57 SoundManager::current()->preload("sounds/fall.wav");
58 SoundManager::current()->preload("sounds/splash.ogg");
60 dir = (start_dir == AUTO) ? LEFT : start_dir;
63 BadGuy::BadGuy(const Vector& pos, Direction direction, const std::string& sprite_name_, int layer_) :
64 MovingSprite(pos, sprite_name_, layer_, COLGROUP_DISABLED),
67 is_initialized(false),
78 on_ground_flag(false),
80 colgroup_active(COLGROUP_MOVING)
82 start_position = bbox.p1;
84 SoundManager::current()->preload("sounds/squish.wav");
85 SoundManager::current()->preload("sounds/fall.wav");
86 SoundManager::current()->preload("sounds/splash.ogg");
88 dir = (start_dir == AUTO) ? LEFT : start_dir;
91 BadGuy::BadGuy(const Reader& reader, const std::string& sprite_name_, int layer_) :
92 MovingSprite(reader, sprite_name_, layer_, COLGROUP_DISABLED),
95 is_initialized(false),
106 on_ground_flag(false),
108 colgroup_active(COLGROUP_MOVING)
110 start_position = bbox.p1;
112 std::string dir_str = "auto";
113 reader.get("direction", dir_str);
114 start_dir = str2dir( dir_str );
117 reader.get("dead-script", dead_script);
119 SoundManager::current()->preload("sounds/squish.wav");
120 SoundManager::current()->preload("sounds/fall.wav");
121 SoundManager::current()->preload("sounds/splash.ogg");
123 dir = (start_dir == AUTO) ? LEFT : start_dir;
127 BadGuy::draw(DrawingContext& context)
131 if(state == STATE_INIT || state == STATE_INACTIVE)
133 if(state == STATE_FALLING) {
134 DrawingEffect old_effect = context.get_drawing_effect();
135 context.set_drawing_effect(old_effect | VERTICAL_FLIP);
136 sprite->draw(context, get_pos(), layer);
137 context.set_drawing_effect(old_effect);
139 sprite->draw(context, get_pos(), layer);
144 BadGuy::update(float elapsed_time)
146 if(!Sector::current()->inside(bbox)) {
147 is_active_flag = false;
150 // get badguy name from sprite_name ignoring path and extension
151 std::string badguy = sprite_name.substr(0, sprite_name.length() - 7);
152 int path_chars = badguy.rfind("/",badguy.length());
153 badguy = badguy.substr(path_chars + 1, badguy.length() - path_chars);
154 // log warning since badguys_killed can no longer reach total_badguys
155 std::string current_level = "[" + Sector::current()->get_level()->filename + "] ";
156 log_warning << current_level << "Counted badguy " << badguy << " starting at " << start_position << " has left the sector" <<std::endl;;
160 if ((state != STATE_INACTIVE) && is_offscreen()) {
161 if (state == STATE_ACTIVE) deactivate();
162 set_state(STATE_INACTIVE);
167 is_active_flag = true;
168 active_update(elapsed_time);
172 is_active_flag = false;
173 inactive_update(elapsed_time);
177 is_active_flag = false;
178 if(state_timer.check()) {
182 movement = physic.get_movement(elapsed_time);
185 is_active_flag = false;
186 movement = physic.get_movement(elapsed_time);
190 on_ground_flag = false;
194 BadGuy::str2dir( std::string dir_str )
196 if( dir_str == "auto" )
198 if( dir_str == "left" )
200 if( dir_str == "right" )
204 log_warning << "Badguy::str2dir: unknown direction \"" << dir_str << "\"" << std::endl;;
224 BadGuy::active_update(float elapsed_time)
226 movement = physic.get_movement(elapsed_time);
228 sprite->stop_animation();
232 BadGuy::inactive_update(float )
237 BadGuy::collision_tile(uint32_t tile_attributes)
239 // Don't kill badguys that have already been killed
240 if (!is_active()) return;
242 if(tile_attributes & Tile::WATER && !is_in_water())
245 SoundManager::current()->play("sounds/splash.ogg", get_pos());
247 if(!(tile_attributes & Tile::WATER) && is_in_water())
252 if(tile_attributes & Tile::HURTS) {
253 if (tile_attributes & Tile::FIRE) {
254 if (is_flammable()) ignite();
256 else if (tile_attributes & Tile::ICE) {
257 if (is_freezable()) freeze();
266 BadGuy::collision(GameObject& other, const CollisionHit& hit)
268 if (!is_active()) return ABORT_MOVE;
270 BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
271 if(badguy && badguy->is_active() && badguy->get_group() == COLGROUP_MOVING) {
273 /* Badguys don't let badguys squish other badguys. It's bad. */
276 if (badguy->get_bbox().p2.y < (bbox.p1.y + 16)) {
277 if(collision_squished(*badguy)) {
283 return collision_badguy(*badguy, hit);
286 Player* player = dynamic_cast<Player*> (&other);
290 if (player->get_bbox().p2.y < (bbox.p1.y + 16)) {
291 if(player->is_stone()) {
295 if(collision_squished(*player)) {
300 if(player->is_stone()) {
301 collision_solid(hit);
305 return collision_player(*player, hit);
308 Bullet* bullet = dynamic_cast<Bullet*> (&other);
310 return collision_bullet(*bullet, hit);
316 BadGuy::collision_solid(const CollisionHit& hit)
318 physic.set_velocity_x(0);
319 physic.set_velocity_y(0);
320 update_on_ground_flag(hit);
324 BadGuy::collision_player(Player& player, const CollisionHit& )
326 if(player.is_invincible()) {
331 //TODO: unfreeze timer
341 BadGuy::collision_badguy(BadGuy& , const CollisionHit& )
347 BadGuy::collision_squished(GameObject& object)
349 // frozen badguys can be killed with butt-jump
352 Player* player = dynamic_cast<Player*>(&object);
353 if(player && (player->does_buttjump)) {
354 player->bounce(*this);
355 kill_fall();//TODO: shatter frozen badguys
363 BadGuy::collision_bullet(Bullet& bullet, const CollisionHit& hit)
366 if(bullet.get_type() == FIRE_BONUS) {
367 // fire bullet thaws frozen badguys
372 // other bullets ricochet
373 bullet.ricochet(*this, hit);
377 else if (is_ignited()) {
378 if(bullet.get_type() == ICE_BONUS) {
379 // ice bullets extinguish ignited badguys
384 // other bullets are absorbed by ignited badguys
389 else if(bullet.get_type() == FIRE_BONUS && is_flammable()) {
390 // fire bullets ignite flammable badguys
395 else if(bullet.get_type() == ICE_BONUS && is_freezable()) {
396 // ice bullets freeze freezable badguys
402 // in all other cases, bullets ricochet
403 bullet.ricochet(*this, hit);
409 BadGuy::kill_squished(GameObject& object)
411 if (!is_active()) return;
413 SoundManager::current()->play("sounds/squish.wav", get_pos());
414 physic.enable_gravity(true);
415 physic.set_velocity_x(0);
416 physic.set_velocity_y(0);
417 set_state(STATE_SQUISHED);
418 set_group(COLGROUP_MOVING_ONLY_STATIC);
419 Player* player = dynamic_cast<Player*>(&object);
421 player->bounce(*this);
431 if (!is_active()) return;
433 SoundManager::current()->play("sounds/fall.wav", get_pos());
434 physic.set_velocity_y(0);
435 physic.set_acceleration_y(0);
436 physic.enable_gravity(true);
437 set_state(STATE_FALLING);
439 // Set the badguy layer to be the foremost, so that
440 // this does not reveal secret tilemaps:
441 layer = Sector::current()->get_foremost_layer() + 1;
448 BadGuy::run_dead_script()
451 Sector::current()->get_level()->stats.badguys++;
456 if(dead_script != "") {
457 std::istringstream stream(dead_script);
458 Sector::current()->run_script(stream, "dead-script");
463 BadGuy::set_state(State state_)
465 if(this->state == state_)
468 State laststate = this->state;
469 this->state = state_;
472 state_timer.start(SQUISH_TIME);
475 set_group(colgroup_active);
476 //bbox.set_pos(start_position);
479 // was the badguy dead anyway?
480 if(laststate == STATE_SQUISHED || laststate == STATE_FALLING) {
483 set_group(COLGROUP_DISABLED);
486 set_group(COLGROUP_DISABLED);
494 BadGuy::is_offscreen()
496 Player* player = get_nearest_player();
497 if (!player) return false;
498 Vector dist = player->get_bbox().get_middle() - get_bbox().get_middle();
499 // In SuperTux 0.1.x, Badguys were activated when Tux<->Badguy center distance was approx. <= ~668px
500 // This doesn't work for wide-screen monitors which give us a virt. res. of approx. 1066px x 600px
501 if ((fabsf(dist.x) <= X_OFFSCREEN_DISTANCE) && (fabsf(dist.y) <= Y_OFFSCREEN_DISTANCE)) {
508 BadGuy::try_activate()
510 // Don't activate if player is dying
511 Player* player = get_nearest_player();
514 if (!is_offscreen()) {
515 set_state(STATE_ACTIVE);
516 if (!is_initialized) {
518 // if starting direction was set to AUTO, this is our chance to re-orient the badguy
519 if (start_dir == AUTO) {
520 Player* player_ = get_nearest_player();
521 if (player_ && (player_->get_bbox().p1.x > get_bbox().p2.x)) {
529 is_initialized = true;
536 BadGuy::might_fall(int height)
538 // make sure we check for at least a 1-pixel fall
543 float y1 = bbox.p2.y + 1;
544 float y2 = bbox.p2.y + 1 + height;
552 return Sector::current()->is_free_of_statics(Rectf(x1, y1, x2, y2));
556 BadGuy::get_nearest_player()
558 return Sector::current()->get_nearest_player (this->get_bbox ());
562 BadGuy::update_on_ground_flag(const CollisionHit& hit)
565 on_ground_flag = true;
566 floor_normal = hit.slope_normal;
573 return on_ground_flag;
579 return is_active_flag;
583 BadGuy::get_floor_normal()
591 set_group(COLGROUP_MOVING_STATIC);
594 if(sprite->has_action("iced-left"))
595 sprite->set_action(dir == LEFT ? "iced-left" : "iced-right", 1);
596 // when no iced action exists, default to shading badguy blue
599 sprite->set_color(Color(0.60, 0.72, 0.88f));
600 sprite->stop_animation();
607 set_group(colgroup_active);
610 // restore original color if needed
611 if(!sprite->has_action("iced-left"))
613 sprite->set_color(Color(1.00, 1.00, 1.00f));
614 sprite->set_animation_loops();
619 BadGuy::is_freezable() const
625 BadGuy::is_frozen() const
631 BadGuy::is_in_water() const
648 BadGuy::is_flammable() const
654 BadGuy::is_ignited() const
660 BadGuy::set_colgroup_active(CollisionGroup group_)
662 this->colgroup_active = group_;
663 if (state == STATE_ACTIVE) set_group(group_);