src/badguy/badguy.cpp

Go to the documentation of this file.
00001 //  SuperTux
00002 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
00003 //
00004 //  This program is free software: you can redistribute it and/or modify
00005 //  it under the terms of the GNU General Public License as published by
00006 //  the Free Software Foundation, either version 3 of the License, or
00007 //  (at your option) any later version.
00008 //
00009 //  This program is distributed in the hope that it will be useful,
00010 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 //  GNU General Public License for more details.
00013 //
00014 //  You should have received a copy of the GNU General Public License
00015 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
00016 
00017 #include "badguy/badguy.hpp"
00018 
00019 #include "audio/sound_manager.hpp"
00020 #include "object/bullet.hpp"
00021 #include "object/player.hpp"
00022 #include "supertux/level.hpp"
00023 #include "supertux/sector.hpp"
00024 #include "supertux/tile.hpp"
00025 #include "util/reader.hpp"
00026 
00027 #include <math.h>
00028 #include <sstream>
00029 
00030 static const float SQUISH_TIME = 2;
00031   
00032 static const float X_OFFSCREEN_DISTANCE = 1280;
00033 static const float Y_OFFSCREEN_DISTANCE = 800;
00034 
00035 BadGuy::BadGuy(const Vector& pos, const std::string& sprite_name, int layer) :
00036   MovingSprite(pos, sprite_name, layer, COLGROUP_DISABLED), 
00037   physic(),
00038   countMe(true), 
00039   is_initialized(false),
00040   start_position(),
00041   dir(LEFT), 
00042   start_dir(AUTO), 
00043   frozen(false), 
00044   ignited(false),
00045   dead_script(),
00046   state(STATE_INIT), 
00047   is_active_flag(),
00048   state_timer(),
00049   on_ground_flag(false),
00050   floor_normal(),
00051   colgroup_active(COLGROUP_MOVING)
00052 {
00053   start_position = bbox.p1;
00054 
00055   sound_manager->preload("sounds/squish.wav");
00056   sound_manager->preload("sounds/fall.wav");
00057 
00058   dir = (start_dir == AUTO) ? LEFT : start_dir;
00059 }
00060 
00061 BadGuy::BadGuy(const Vector& pos, Direction direction, const std::string& sprite_name, int layer) :
00062   MovingSprite(pos, sprite_name, layer, COLGROUP_DISABLED), 
00063   physic(),
00064   countMe(true), 
00065   is_initialized(false), 
00066   start_position(),
00067   dir(direction), 
00068   start_dir(direction), 
00069   frozen(false), 
00070   ignited(false),
00071   dead_script(),
00072   state(STATE_INIT), 
00073   is_active_flag(),
00074   state_timer(),
00075   on_ground_flag(false), 
00076   floor_normal(),
00077   colgroup_active(COLGROUP_MOVING)
00078 {
00079   start_position = bbox.p1;
00080 
00081   sound_manager->preload("sounds/squish.wav");
00082   sound_manager->preload("sounds/fall.wav");
00083 
00084   dir = (start_dir == AUTO) ? LEFT : start_dir;
00085 }
00086 
00087 BadGuy::BadGuy(const Reader& reader, const std::string& sprite_name, int layer) :
00088   MovingSprite(reader, sprite_name, layer, COLGROUP_DISABLED), 
00089   physic(),
00090   countMe(true), 
00091   is_initialized(false), 
00092   start_position(),
00093   dir(LEFT), 
00094   start_dir(AUTO),
00095   frozen(false), 
00096   ignited(false), 
00097   dead_script(),
00098   state(STATE_INIT), 
00099   is_active_flag(),
00100   state_timer(),
00101   on_ground_flag(false), 
00102   floor_normal(),
00103   colgroup_active(COLGROUP_MOVING)
00104 {
00105   start_position = bbox.p1;
00106 
00107   std::string dir_str = "auto";
00108   reader.get("direction", dir_str);
00109   start_dir = str2dir( dir_str );
00110   dir = start_dir;
00111 
00112   reader.get("dead-script", dead_script);
00113 
00114   sound_manager->preload("sounds/squish.wav");
00115   sound_manager->preload("sounds/fall.wav");
00116 
00117   dir = (start_dir == AUTO) ? LEFT : start_dir;
00118 }
00119 
00120 void
00121 BadGuy::draw(DrawingContext& context)
00122 {
00123   if(!sprite.get())
00124     return;
00125   if(state == STATE_INIT || state == STATE_INACTIVE)
00126     return;
00127   if(state == STATE_FALLING) {
00128     DrawingEffect old_effect = context.get_drawing_effect();
00129     context.set_drawing_effect((DrawingEffect) (old_effect | VERTICAL_FLIP));
00130     sprite->draw(context, get_pos(), layer);
00131     context.set_drawing_effect(old_effect);
00132   } else {
00133     sprite->draw(context, get_pos(), layer);
00134   }
00135 }
00136 
00137 void
00138 BadGuy::update(float elapsed_time)
00139 {
00140   if(!Sector::current()->inside(bbox)) {
00141     is_active_flag = false;
00142     remove_me();
00143     return;
00144   }
00145   if ((state != STATE_INACTIVE) && is_offscreen()) {
00146     if (state == STATE_ACTIVE) deactivate();
00147     set_state(STATE_INACTIVE);
00148   }
00149 
00150   switch(state) {
00151     case STATE_ACTIVE:
00152       is_active_flag = true;
00153       active_update(elapsed_time);
00154       break;
00155     case STATE_INIT:
00156     case STATE_INACTIVE:
00157       is_active_flag = false;
00158       inactive_update(elapsed_time);
00159       try_activate();
00160       break;
00161     case STATE_SQUISHED:
00162       is_active_flag = false;
00163       if(state_timer.check()) {
00164         remove_me();
00165         break;
00166       }
00167       movement = physic.get_movement(elapsed_time);
00168       break;
00169     case STATE_FALLING:
00170       is_active_flag = false;
00171       movement = physic.get_movement(elapsed_time);
00172       break;
00173   }
00174 
00175   on_ground_flag = false;
00176 }
00177 
00178 Direction
00179 BadGuy::str2dir( std::string dir_str )
00180 {
00181   if( dir_str == "auto" )
00182     return AUTO;
00183   if( dir_str == "left" )
00184     return LEFT;
00185   if( dir_str == "right" )
00186     return RIGHT;
00187 
00188   //default to "auto"
00189   log_warning << "Badguy::str2dir: unknown direction \"" << dir_str << "\"" << std::endl;;
00190   return AUTO;
00191 }
00192 
00193 void
00194 BadGuy::initialize()
00195 {
00196 }
00197 
00198 void
00199 BadGuy::activate()
00200 {
00201 }
00202 
00203 void
00204 BadGuy::deactivate()
00205 {
00206 }
00207 
00208 void
00209 BadGuy::active_update(float elapsed_time)
00210 {
00211   movement = physic.get_movement(elapsed_time);
00212 }
00213 
00214 void
00215 BadGuy::inactive_update(float )
00216 {
00217 }
00218 
00219 void
00220 BadGuy::collision_tile(uint32_t tile_attributes)
00221 {
00222   if(tile_attributes & Tile::HURTS) {
00223     if (tile_attributes & Tile::FIRE) {
00224       if (is_flammable()) ignite();
00225     }
00226     else if (tile_attributes & Tile::ICE) {
00227       if (is_freezable()) freeze();
00228     }
00229     else {
00230       kill_fall();
00231     }
00232   }
00233 }
00234 
00235 HitResponse
00236 BadGuy::collision(GameObject& other, const CollisionHit& hit)
00237 {
00238   if (!is_active()) return ABORT_MOVE;
00239 
00240   BadGuy* badguy = dynamic_cast<BadGuy*> (&other);
00241   if(badguy && badguy->is_active() && badguy->get_group() == COLGROUP_MOVING) {
00242 
00243     /* Badguys don't let badguys squish other badguys. It's bad. */
00244 #if 0
00245     // hit from above?
00246     if (badguy->get_bbox().p2.y < (bbox.p1.y + 16)) {
00247       if(collision_squished(*badguy)) {
00248         return ABORT_MOVE;
00249       }
00250     }
00251 #endif
00252 
00253     return collision_badguy(*badguy, hit);
00254   }
00255 
00256   Player* player = dynamic_cast<Player*> (&other);
00257   if(player) {
00258 
00259     // hit from above?
00260     if (player->get_bbox().p2.y < (bbox.p1.y + 16)) {
00261       if(collision_squished(*player)) {
00262         return FORCE_MOVE;
00263       }
00264     }
00265 
00266     return collision_player(*player, hit);
00267   }
00268 
00269   Bullet* bullet = dynamic_cast<Bullet*> (&other);
00270   if(bullet)
00271     return collision_bullet(*bullet, hit);
00272 
00273   return FORCE_MOVE;
00274 }
00275 
00276 void
00277 BadGuy::collision_solid(const CollisionHit& hit)
00278 {
00279   physic.set_velocity_x(0);
00280   physic.set_velocity_y(0);
00281   update_on_ground_flag(hit);
00282 }
00283 
00284 HitResponse
00285 BadGuy::collision_player(Player& player, const CollisionHit& )
00286 {
00287   if(player.is_invincible()) {
00288     kill_fall();
00289     return ABORT_MOVE;
00290   }
00291 
00292   if(frozen)
00293     unfreeze();
00294   player.kill(false);
00295   return FORCE_MOVE;
00296 }
00297 
00298 HitResponse
00299 BadGuy::collision_badguy(BadGuy& , const CollisionHit& )
00300 {
00301   return FORCE_MOVE;
00302 }
00303 
00304 bool
00305 BadGuy::collision_squished(GameObject& )
00306 {
00307   return false;
00308 }
00309 
00310 HitResponse
00311 BadGuy::collision_bullet(Bullet& bullet, const CollisionHit& hit)
00312 {
00313   if (is_frozen()) {
00314     if(bullet.get_type() == FIRE_BONUS) {
00315       // fire bullet thaws frozen badguys
00316       unfreeze();
00317       bullet.remove_me();
00318       return ABORT_MOVE;
00319     } else {
00320       // other bullets ricochet
00321       bullet.ricochet(*this, hit);
00322       return FORCE_MOVE;
00323     }
00324   }
00325   else if (is_ignited()) {
00326     if(bullet.get_type() == ICE_BONUS) {
00327       // ice bullets extinguish ignited badguys
00328       extinguish();
00329       bullet.remove_me();
00330       return ABORT_MOVE;
00331     } else {
00332       // other bullets are absorbed by ignited badguys
00333       bullet.remove_me();
00334       return FORCE_MOVE;
00335     }
00336   }
00337   else if(bullet.get_type() == FIRE_BONUS && is_flammable()) {
00338     // fire bullets ignite flammable badguys
00339     ignite();
00340     bullet.remove_me();
00341     return ABORT_MOVE;
00342   }
00343   else if(bullet.get_type() == ICE_BONUS && is_freezable()) {
00344     // ice bullets freeze freezable badguys
00345     freeze();
00346     bullet.remove_me();
00347     return ABORT_MOVE;
00348   }
00349   else {
00350     // in all other cases, bullets ricochet
00351     bullet.ricochet(*this, hit);
00352     return FORCE_MOVE;
00353   }
00354 }
00355 
00356 void
00357 BadGuy::kill_squished(GameObject& object)
00358 {
00359   sound_manager->play("sounds/squish.wav", get_pos());
00360   physic.enable_gravity(true);
00361   physic.set_velocity_x(0);
00362   physic.set_velocity_y(0);
00363   set_state(STATE_SQUISHED);
00364   set_group(COLGROUP_MOVING_ONLY_STATIC);
00365   Player* player = dynamic_cast<Player*>(&object);
00366   if (player) {
00367     player->bounce(*this);
00368   }
00369 
00370   // start dead-script
00371   run_dead_script();
00372 }
00373 
00374 void
00375 BadGuy::kill_fall()
00376 {
00377   sound_manager->play("sounds/fall.wav", get_pos());
00378   physic.set_velocity_y(0);
00379   physic.set_acceleration_y(0);
00380   physic.enable_gravity(true);
00381   set_state(STATE_FALLING);
00382 
00383   // start dead-script
00384   run_dead_script();
00385 }
00386 
00387 void
00388 BadGuy::run_dead_script()
00389 {
00390   if (countMe)
00391     Sector::current()->get_level()->stats.badguys++;
00392 
00393   countMe = false;
00394    
00395   // start dead-script
00396   if(dead_script != "") {
00397     std::istringstream stream(dead_script);
00398     Sector::current()->run_script(stream, "dead-script");
00399   }
00400 }
00401 
00402 void
00403 BadGuy::set_state(State state)
00404 {
00405   if(this->state == state)
00406     return;
00407 
00408   State laststate = this->state;
00409   this->state = state;
00410   switch(state) {
00411     case STATE_SQUISHED:
00412       state_timer.start(SQUISH_TIME);
00413       break;
00414     case STATE_ACTIVE:
00415       set_group(colgroup_active);
00416       //bbox.set_pos(start_position);
00417       break;
00418     case STATE_INACTIVE:
00419       // was the badguy dead anyway?
00420       if(laststate == STATE_SQUISHED || laststate == STATE_FALLING) {
00421         remove_me();
00422       }
00423       set_group(COLGROUP_DISABLED);
00424       break;
00425     case STATE_FALLING:
00426       set_group(COLGROUP_DISABLED);
00427       break;
00428     default:
00429       break;
00430   }
00431 }
00432 
00433 bool
00434 BadGuy::is_offscreen()
00435 {
00436   Player* player = get_nearest_player();
00437   if (!player) return false;
00438   Vector dist = player->get_bbox().get_middle() - get_bbox().get_middle();
00439   // In SuperTux 0.1.x, Badguys were activated when Tux<->Badguy center distance was approx. <= ~668px
00440   // This doesn't work for wide-screen monitors which give us a virt. res. of approx. 1066px x 600px
00441   if ((fabsf(dist.x) <= X_OFFSCREEN_DISTANCE) && (fabsf(dist.y) <= Y_OFFSCREEN_DISTANCE)) {
00442     return false;
00443   }
00444   return true;
00445 }
00446 
00447 void
00448 BadGuy::try_activate()
00449 {
00450   // Don't activate if player is dying
00451   Player* player = get_nearest_player();
00452   if (!player) return;
00453 
00454   if (!is_offscreen()) {
00455     set_state(STATE_ACTIVE);
00456     if (!is_initialized) {
00457 
00458       // if starting direction was set to AUTO, this is our chance to re-orient the badguy
00459       if (start_dir == AUTO) {
00460         Player* player = get_nearest_player();
00461         if (player && (player->get_bbox().p1.x > get_bbox().p2.x)) {
00462           dir = RIGHT;
00463         } else {
00464           dir = LEFT;
00465         }
00466       }
00467 
00468       initialize();
00469       is_initialized = true;
00470     }
00471     activate();
00472   }
00473 }
00474 
00475 bool
00476 BadGuy::might_fall(int height)
00477 {
00478   // make sure we check for at least a 1-pixel fall
00479   assert(height > 0);
00480 
00481   float x1;
00482   float x2;
00483   float y1 = bbox.p2.y + 1;
00484   float y2 = bbox.p2.y + 1 + height;
00485   if (dir == LEFT) {
00486     x1 = bbox.p1.x - 1;
00487     x2 = bbox.p1.x;
00488   } else {
00489     x1 = bbox.p2.x;
00490     x2 = bbox.p2.x + 1;
00491   }
00492   return Sector::current()->is_free_of_statics(Rectf(x1, y1, x2, y2));
00493 }
00494 
00495 Player*
00496 BadGuy::get_nearest_player()
00497 {
00498   return Sector::current()->get_nearest_player (this->get_bbox ());
00499 }
00500 
00501 void
00502 BadGuy::update_on_ground_flag(const CollisionHit& hit)
00503 {
00504   if (hit.bottom) {
00505     on_ground_flag = true;
00506     floor_normal = hit.slope_normal;
00507   }
00508 }
00509 
00510 bool
00511 BadGuy::on_ground()
00512 {
00513   return on_ground_flag;
00514 }
00515 
00516 bool
00517 BadGuy::is_active()
00518 {
00519   return is_active_flag;
00520 }
00521 
00522 Vector
00523 BadGuy::get_floor_normal()
00524 {
00525   return floor_normal;
00526 }
00527 
00528 void
00529 BadGuy::freeze()
00530 {
00531   set_group(COLGROUP_MOVING_STATIC);
00532   frozen = true;
00533 }
00534 
00535 void
00536 BadGuy::unfreeze()
00537 {
00538   set_group(colgroup_active);
00539   frozen = false;
00540 }
00541 
00542 bool
00543 BadGuy::is_freezable() const
00544 {
00545   return false;
00546 }
00547 
00548 bool
00549 BadGuy::is_frozen() const
00550 {
00551   return frozen;
00552 }
00553 
00554 void
00555 BadGuy::ignite()
00556 {
00557   kill_fall();
00558 }
00559 
00560 void
00561 BadGuy::extinguish()
00562 {
00563 }
00564 
00565 bool
00566 BadGuy::is_flammable() const
00567 {
00568   return true;
00569 }
00570 
00571 bool
00572 BadGuy::is_ignited() const
00573 {
00574   return ignited;
00575 }
00576   
00577 void 
00578 BadGuy::set_colgroup_active(CollisionGroup group)
00579 {
00580   this->colgroup_active = group;
00581   if (state == STATE_ACTIVE) set_group(group); 
00582 }
00583 
00584 /* EOF */

Generated on Mon Jun 9 03:38:16 2014 for SuperTux by  doxygen 1.5.1