00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
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
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
00244 #if 0
00245
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
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
00316 unfreeze();
00317 bullet.remove_me();
00318 return ABORT_MOVE;
00319 } else {
00320
00321 bullet.ricochet(*this, hit);
00322 return FORCE_MOVE;
00323 }
00324 }
00325 else if (is_ignited()) {
00326 if(bullet.get_type() == ICE_BONUS) {
00327
00328 extinguish();
00329 bullet.remove_me();
00330 return ABORT_MOVE;
00331 } else {
00332
00333 bullet.remove_me();
00334 return FORCE_MOVE;
00335 }
00336 }
00337 else if(bullet.get_type() == FIRE_BONUS && is_flammable()) {
00338
00339 ignite();
00340 bullet.remove_me();
00341 return ABORT_MOVE;
00342 }
00343 else if(bullet.get_type() == ICE_BONUS && is_freezable()) {
00344
00345 freeze();
00346 bullet.remove_me();
00347 return ABORT_MOVE;
00348 }
00349 else {
00350
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
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
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
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
00417 break;
00418 case STATE_INACTIVE:
00419
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
00440
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
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
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
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