X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=src%2Fobject%2Ficecrusher.cpp;h=1e94d656e23b924b50384760a9a545ea9234a353;hb=b699d5a6303e164aab04f1a1d0990b978c15d87d;hp=873428c14d8d2e34e2dc94e61b20a2564ad5e40d;hpb=e850a1093052b6a848a6d0725d0c6fe4762a781e;p=supertux.git diff --git a/src/object/icecrusher.cpp b/src/object/icecrusher.cpp index 873428c14..1e94d656e 100644 --- a/src/object/icecrusher.cpp +++ b/src/object/icecrusher.cpp @@ -17,45 +17,72 @@ #include "object/icecrusher.hpp" +#include + +#include "audio/sound_manager.hpp" #include "badguy/badguy.hpp" -#include "sprite/sprite.hpp" +#include "object/camera.hpp" +#include "object/particles.hpp" #include "object/player.hpp" +#include "sprite/sprite.hpp" +#include "sprite/sprite_manager.hpp" #include "supertux/object_factory.hpp" #include "supertux/sector.hpp" namespace { /* Maximum movement speed in pixels per LOGICAL_FPS */ const float MAX_DROP_SPEED = 10.0; -const float RECOVER_SPEED = -3.125; -const float ACTIVATION_DISTANCE = 4.0; -const float PAUSE_TIME = 0.5; +const float RECOVER_SPEED_NORMAL = -3.125; +const float RECOVER_SPEED_LARGE = -2.0; +const float DROP_ACTIVATION_DISTANCE = 4.0; +const float PAUSE_TIME_NORMAL = 0.5; +const float PAUSE_TIME_LARGE = 1.0; } IceCrusher::IceCrusher(const Reader& reader) : - MovingSprite(reader, "images/creatures/icecrusher/icecrusher.sprite", LAYER_OBJECTS, COLGROUP_STATIC), - state(IDLE), + MovingSprite(reader, "images/creatures/icecrusher/icecrusher.sprite", LAYER_OBJECTS, COLGROUP_STATIC), + state(IDLE), start_position(), physic(), - cooldown_timer(0.0) + cooldown_timer(0.0), + lefteye(), + righteye(), + whites(), + ic_size(NORMAL) { + // TODO: icecrusher hitting deserves its own sounds- + // one for hitting the ground, one for hitting Tux + SoundManager::current()->preload("sounds/brick.wav"); + start_position = get_bbox().p1; set_state(state, true); + + float sprite_width = sprite->get_width (); + if (sprite_width >= 128.0) + ic_size = LARGE; + + lefteye = SpriteManager::current()->create(sprite_name); + lefteye->set_action("lefteye"); + righteye = SpriteManager::current()->create(sprite_name); + righteye->set_action("righteye"); + whites = SpriteManager::current()->create(sprite_name); + whites->set_action("whites"); } /* IceCrusher::IceCrusher(const IceCrusher& other) - : MovingSprite(other), - state(other.state), speed(other.speed) + : MovingSprite(other), + state(other.state), speed(other.speed) { start_position = get_bbox().p1; set_state(state, true); } */ -void -IceCrusher::set_state(IceCrusherState state, bool force) +void +IceCrusher::set_state(IceCrusherState state_, bool force) { - if ((this->state == state) && (!force)) return; - switch(state) { + if ((this->state == state_) && (!force)) return; + switch(state_) { case IDLE: set_group(COLGROUP_STATIC); physic.enable_gravity (false); @@ -76,7 +103,7 @@ IceCrusher::set_state(IceCrusherState state, bool force) log_debug << "IceCrusher in invalid state" << std::endl; break; } - this->state = state; + this->state = state_; } HitResponse @@ -87,6 +114,7 @@ IceCrusher::collision(GameObject& other, const CollisionHit& hit) /* If the other object is the player, and the collision is at the bottom of * the ice crusher, hurt the player. */ if (player && hit.bottom) { + SoundManager::current()->play("sounds/brick.wav"); if(player->is_invincible()) { if (state == CRUSHING) set_state(RECOVERING); @@ -103,8 +131,8 @@ IceCrusher::collision(GameObject& other, const CollisionHit& hit) } return FORCE_MOVE; } - -void + +void IceCrusher::collision_solid(const CollisionHit& hit) { switch(state) { @@ -112,7 +140,40 @@ IceCrusher::collision_solid(const CollisionHit& hit) break; case CRUSHING: if (hit.bottom) { - cooldown_timer = PAUSE_TIME; + if (ic_size == LARGE) { + cooldown_timer = PAUSE_TIME_LARGE; + Sector::current()->camera->shake (/* frequency = */ .125f, /* x = */ 0.0, /* y = */ 16.0); + SoundManager::current()->play("sounds/brick.wav"); + // throw some particles, bigger and more for large icecrusher + for(int j = 0; j < 9; j++) + { + Sector::current()->add_object(std::make_shared( + Vector(get_bbox().p2.x - j*8 - 4, get_bbox().p2.y), + 0, 90-5*j, Vector(140, -380), Vector(0, 300), + 1, Color(.6f, .6f, .6f), 5, 1.8f, LAYER_OBJECTS+1)); + Sector::current()->add_object(std::make_shared( + Vector(get_bbox().p1.x + j*8 + 4, get_bbox().p2.y), + 270+5*j, 360, Vector(140, -380), Vector(0, 300), + 1, Color(.6f, .6f, .6f), 5, 1.8f, LAYER_OBJECTS+1)); + } + } + else { + cooldown_timer = PAUSE_TIME_NORMAL; + Sector::current()->camera->shake (/* frequency = */ .1f, /* x = */ 0.0, /* y = */ 8.0); + SoundManager::current()->play("sounds/brick.wav"); + // throw some particles + for(int j = 0; j < 5; j++) + { + Sector::current()->add_object(std::make_shared( + Vector(get_bbox().p2.x - j*8 - 4, get_bbox().p2.y), + 0, 90+10*j, Vector(140, -260), Vector(0, 300), + 1, Color(.6f, .6f, .6f), 4, 1.6f, LAYER_OBJECTS+1)); + Sector::current()->add_object(std::make_shared( + Vector(get_bbox().p1.x + j*8 + 4, get_bbox().p2.y), + 270+10*j, 360, Vector(140, -260), Vector(0, 300), + 1, Color(.6f, .6f, .6f), 4, 1.6f, LAYER_OBJECTS+1)); + } + } set_state(RECOVERING); } break; @@ -153,11 +214,17 @@ IceCrusher::update(float elapsed_time) if (get_bbox().p1.y <= start_position.y+1) { set_pos(start_position); movement = Vector (0, 0); - cooldown_timer = PAUSE_TIME; + if (ic_size == LARGE) + cooldown_timer = PAUSE_TIME_LARGE; + else + cooldown_timer = PAUSE_TIME_NORMAL; set_state(IDLE); } else { - movement = Vector (0, RECOVER_SPEED); + if (ic_size == LARGE) + movement = Vector (0, RECOVER_SPEED_LARGE); + else + movement = Vector (0, RECOVER_SPEED_NORMAL); } break; default: @@ -166,6 +233,23 @@ IceCrusher::update(float elapsed_time) } } +void +IceCrusher::draw(DrawingContext& context) +{ + context.push_target(); + context.set_target(DrawingContext::NORMAL); + sprite->draw(context, get_pos(), layer+2); + if(!(state == CRUSHING) && sprite->has_action("whites")) + { + // draw icecrusher's eyes slightly behind + lefteye->draw(context, get_pos()+eye_position(false), layer+1); + righteye->draw(context, get_pos()+eye_position(true), layer+1); + // draw the whites of icecrusher's eyes even further behind + whites->draw(context, get_pos(), layer); + } + context.pop_target(); +} + bool IceCrusher::found_victim() { @@ -174,12 +258,57 @@ IceCrusher::found_victim() const Rectf& player_bbox = player->get_bbox(); const Rectf& crusher_bbox = get_bbox(); + Rectf crush_area = Rectf(crusher_bbox.p1.x+1, crusher_bbox.p2.y, crusher_bbox.p2.x-1, std::max(crusher_bbox.p2.y,player_bbox.p1.y-1)); if ((player_bbox.p1.y >= crusher_bbox.p2.y) /* player is below crusher */ - && (player_bbox.p2.x > (crusher_bbox.p1.x - ACTIVATION_DISTANCE)) - && (player_bbox.p1.x < (crusher_bbox.p2.x + ACTIVATION_DISTANCE))) + && (player_bbox.p2.x > (crusher_bbox.p1.x - DROP_ACTIVATION_DISTANCE)) + && (player_bbox.p1.x < (crusher_bbox.p2.x + DROP_ACTIVATION_DISTANCE)) + && (Sector::current()->is_free_of_statics(crush_area, this, false))/* and area to player is free of objects */) return true; else return false; } +Vector +IceCrusher::eye_position(bool right) +{ + if(state == IDLE) + { + Player* player = Sector::current()->get_nearest_player (this->get_bbox ()); + if(player) + { + // Icecrusher focuses on approximate position of player's head + const float player_focus_x = (player->get_bbox().p2.x + player->get_bbox().p1.x) * 0.5; + const float player_focus_y = player->get_bbox().p2.y * 0.25 + player->get_bbox().p1.y * 0.75; + // Icecrusher's approximate origin of line-of-sight + const float crusher_origin_x = (get_bbox().p2.x + get_bbox().p1.x) * 0.5; + const float crusher_origin_y = (get_bbox().p2.y + get_bbox().p1.y) * 0.5; + // Line-of-sight displacement from icecrusher to player + const float displacement_x = player_focus_x - crusher_origin_x; + const float displacement_y = player_focus_y - crusher_origin_y; + const float displacement_mag = pow(pow(displacement_x, 2.0) + pow(displacement_y, 2.0), 0.5); + // Determine weighting for eye displacement along x given icecrusher eye shape + int weight_x = sprite->get_width()/64 * (((displacement_x > 0) == right) ? 1 : 4); + int weight_y = sprite->get_width()/64 * 2; + + return Vector(displacement_x/displacement_mag * weight_x, displacement_y/displacement_mag * weight_y - weight_y); + } + } + else if(state == RECOVERING) + { + // Eyes spin while icecrusher is recovering, giving a dazed impression + return Vector(sin((right ? 1 : -1) * // X motion of each eye is opposite of the other + (get_pos().y/13 - // Phase factor due to y position + (ic_size==NORMAL ? RECOVER_SPEED_NORMAL : RECOVER_SPEED_LARGE) + cooldown_timer*13)) * //Phase factor due to cooldown timer + sprite->get_width()/64 * 2 - (right ? 1 : -1) * // Amplitude dependent on size + sprite->get_width()/64 * 2, // Offset to keep eyes visible + cos((right ? 3.1415 : 0) + // Eyes spin out of phase of eachother + get_pos().y/13 - // Phase factor due to y position + (ic_size==NORMAL ? RECOVER_SPEED_NORMAL : RECOVER_SPEED_LARGE) + cooldown_timer*13) * //Phase factor due to cooldown timer + sprite->get_width()/64 * 2 - // Amplitude dependent on size + sprite->get_width()/64 * 2); // Offset to keep eyes visible + } + + return Vector(0,0); +} + /* EOF */