[Super-tux-commit] supertux/src/badguy badguy.cpp,NONE,1.1 badguy.h,NONE,1.1 bomb.cpp,NONE,1.1 bomb.
Brought to you by:
wkendrick
From: Matze B. <mat...@us...> - 2004-11-20 22:14:50
|
Update of /cvsroot/super-tux/supertux/src/badguy In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv5941/src/badguy Added Files: badguy.cpp badguy.h bomb.cpp bomb.h bouncing_snowball.cpp bouncing_snowball.h flame.cpp flame.h jumpy.cpp jumpy.h mrbomb.cpp mrbomb.h mriceblock.cpp mriceblock.h snowball.cpp snowball.h spiky.cpp spiky.h Log Message: The BIG COMMIT(tm) This is it, expect a broken supertux I only fixed the most annoying issues so far, lots more are still waiting in the TODO list, but I had to bring this stuff to cvs sometime. Changes: - Completely new collision detection scheme, which collides all objects with each other once per frame, instead of single objects manually testing for collisions - New collision detection routines which support slopes and provide additional info on collisions: penetration depth, hit normal vector - Lots of general cleanup/refactoring - Splitted the monolithic badguy class and reimplemented most of them as single classes with a badguy base class. - Splitted the monolithic Special class into several classes - Rewrote the timer and all timing related stuff to work on float and seconds (not like the mixup before where you had frame_ratio, msecs and secs in different parts of the app) - Support for unisolid tiles - Created a flying platform prototype (doesn't work completely yet) - rename InteractiveObjects to triggers and implemented a new sequence trigger, (only supported sequence is endsequence at the moment) - transformed the bonusblocks and bricks into objects --- NEW FILE: badguy.cpp --- #include <config.h> #include "badguy.h" #include "camera.h" static const float SQUISH_TIME = 2; static const float X_OFFSCREEN_DISTANCE = 1600; static const float Y_OFFSCREEN_DISTANCE = 1200; BadGuy::BadGuy() : sprite(0), dir(LEFT), state(STATE_INIT) { } BadGuy::~BadGuy() { delete sprite; } void BadGuy::draw(DrawingContext& context) { if(!sprite) return; if(state == STATE_INIT || state == STATE_INACTIVE) return; sprite->draw(context, get_pos(), LAYER_OBJECTS); } void BadGuy::action(float elapsed_time) { if(!Sector::current()->inside(bbox)) { remove_me(); return; } if(is_offscreen()) { set_state(STATE_INACTIVE); } switch(state) { case STATE_ACTIVE: active_action(elapsed_time); break; case STATE_INIT: case STATE_INACTIVE: inactive_action(elapsed_time); try_activate(); break; case STATE_SQUISHED: if(state_timer.check()) { remove_me(); break; } movement = physic.get_movement(elapsed_time); break; case STATE_FALLING: movement = physic.get_movement(elapsed_time); break; } } void BadGuy::activate() { } void BadGuy::deactivate() { } void BadGuy::active_action(float elapsed_time) { movement = physic.get_movement(elapsed_time); } void BadGuy::inactive_action(float elapsed_time) { } HitResponse BadGuy::collision(GameObject& other, const CollisionHit& hit) { switch(state) { case STATE_INIT: case STATE_INACTIVE: return ABORT_MOVE; case STATE_ACTIVE: { if(other.get_flags() & FLAG_SOLID) return collision_solid(other, hit); BadGuy* badguy = dynamic_cast<BadGuy*> (&other); if(badguy) return collision_badguy(*badguy, hit); Player* player = dynamic_cast<Player*> (&other); if(player) return collision_player(*player, hit); return FORCE_MOVE; } case STATE_SQUISHED: if(other.get_flags() & FLAG_SOLID) return CONTINUE; return FORCE_MOVE; case STATE_FALLING: return FORCE_MOVE; } return ABORT_MOVE; } HitResponse BadGuy::collision_solid(GameObject& other, const CollisionHit& hit) { return FORCE_MOVE; } HitResponse BadGuy::collision_player(Player& player, const CollisionHit& hit) { if(player.is_invincible()) { kill_fall(); return ABORT_MOVE; } if(hit.normal.y > .9) { if(collision_squished(player)) return ABORT_MOVE; } player.kill(Player::SHRINK); return FORCE_MOVE; } HitResponse BadGuy::collision_badguy(BadGuy& other, const CollisionHit& hit) { return FORCE_MOVE; } bool BadGuy::collision_squished(Player& player) { return false; } void BadGuy::kill_squished(Player& player) { SoundManager::get()->play_sound(IDToSound(SND_SQUISH), get_pos(), player.get_pos()); physic.set_velocity_x(0); physic.set_velocity_y(0); set_state(STATE_SQUISHED); player.bounce(*this); } void BadGuy::kill_fall() { physic.set_velocity_y(0); physic.enable_gravity(true); set_state(STATE_FALLING); remove_me(); } void BadGuy::set_state(State state) { if(this->state == state) return; State laststate = this->state; this->state = state; switch(state) { case STATE_SQUISHED: state_timer.start(SQUISH_TIME); break; case STATE_ACTIVE: flags &= ~FLAG_NO_COLLDET; bbox.set_pos(start_position); break; case STATE_INACTIVE: // was the badguy dead anyway? if(laststate == STATE_SQUISHED || laststate == STATE_SQUISHED) { remove_me(); } flags |= FLAG_NO_COLLDET; break; default: break; } } bool BadGuy::is_offscreen() { float scroll_x = Sector::current()->camera->get_translation().x; float scroll_y = Sector::current()->camera->get_translation().y; if(bbox.p2.x < scroll_x - X_OFFSCREEN_DISTANCE || bbox.p1.x > scroll_x + X_OFFSCREEN_DISTANCE || bbox.p2.y < scroll_y - Y_OFFSCREEN_DISTANCE || bbox.p1.y > scroll_y + Y_OFFSCREEN_DISTANCE) return true; return false; } void BadGuy::try_activate() { float scroll_x = Sector::current()->camera->get_translation().x; float scroll_y = Sector::current()->camera->get_translation().y; /* Activate badguys if they're just around the screen to avoid * the effect of having badguys suddenly popping up from nowhere. */ if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE && start_position.x < scroll_x - bbox.get_width() && start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE && start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) { dir = RIGHT; set_state(STATE_ACTIVE); activate(); } else if (start_position.x > scroll_x && start_position.x < scroll_x + X_OFFSCREEN_DISTANCE && start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE && start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) { dir = LEFT; set_state(STATE_ACTIVE); activate(); } else if (start_position.x > scroll_x - X_OFFSCREEN_DISTANCE && start_position.x < scroll_x + X_OFFSCREEN_DISTANCE && ((start_position.y > scroll_y && start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) || (start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE && start_position.y < scroll_y))) { dir = start_position.x < scroll_x ? RIGHT : LEFT; set_state(STATE_ACTIVE); activate(); } else if(state == STATE_INIT && start_position.x > scroll_x - X_OFFSCREEN_DISTANCE && start_position.x < scroll_x + X_OFFSCREEN_DISTANCE && start_position.y > scroll_y - Y_OFFSCREEN_DISTANCE && start_position.y < scroll_y + Y_OFFSCREEN_DISTANCE) { dir = LEFT; set_state(STATE_ACTIVE); activate(); } } --- NEW FILE: snowball.h --- #ifndef __SNOWBALL_H__ #define __SNOWBALL_H__ #include "badguy.h" class SnowBall : public BadGuy { public: SnowBall(LispReader& reader); void activate(); void write(LispWriter& writer); HitResponse collision_solid(GameObject& other, const CollisionHit& hit); protected: bool collision_squished(Player& player); }; #endif --- NEW FILE: spiky.h --- #ifndef __SPIKY_H__ #define __SPIKY_H__ #include "badguy.h" class Spiky : public BadGuy { public: Spiky(LispReader& reader); void activate(); void write(LispWriter& writer); HitResponse collision_solid(GameObject& other, const CollisionHit& hit); }; #endif --- NEW FILE: bomb.h --- #ifndef __BOMB_H__ #define __BOMB_H__ #include "badguy.h" class Bomb : public BadGuy { public: Bomb(const Vector& pos, Direction dir); void write(LispWriter& writer); HitResponse collision_solid(GameObject& other, const CollisionHit& hit); HitResponse collision_player(Player& player, const CollisionHit& hit); void active_action(float elapsed_time); void kill_fall(); private: int state; Timer2 timer; }; #endif --- NEW FILE: mriceblock.cpp --- #include <config.h> #include "mriceblock.h" static const float WALKSPEED = 80; static const float KICKSPEED = 500; static const int MAXSQUISHES = 10; MrIceBlock::MrIceBlock(LispReader& reader) : ice_state(ICESTATE_NORMAL), squishcount(0) { reader.read_float("x", start_position.x); reader.read_float("y", start_position.y); bbox.set_size(32, 32); sprite = sprite_manager->create("mriceblock"); } void MrIceBlock::write(LispWriter& writer) { writer.start_list("mriceblock"); writer.write_float("x", get_pos().x); writer.write_float("y", get_pos().y); writer.end_list("mriceblock"); } void MrIceBlock::activate() { physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED); sprite->set_action(dir == LEFT ? "left" : "right"); } void MrIceBlock::active_action(float elapsed_time) { if(ice_state == ICESTATE_FLAT && flat_timer.check()) { printf("unflat.\n"); ice_state = ICESTATE_NORMAL; physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED); sprite->set_action(dir == LEFT ? "left" : "right"); } BadGuy::active_action(elapsed_time); } HitResponse MrIceBlock::collision_solid(GameObject& other, const CollisionHit& hit) { if(fabsf(hit.normal.y) > .5) { // floor or roof physic.set_velocity_y(0); return CONTINUE; } // hit left or right switch(ice_state) { case ICESTATE_NORMAL: dir = dir == LEFT ? RIGHT : LEFT; sprite->set_action(dir == LEFT ? "left" : "right"); physic.set_velocity_x(-physic.get_velocity_x()); break; case ICESTATE_KICKED: dir = dir == LEFT ? RIGHT : LEFT; sprite->set_action(dir == LEFT ? "flat-left" : "flat-right"); physic.set_velocity_x(-physic.get_velocity_x()); SoundManager::get()->play_sound(IDToSound(SND_RICOCHET), get_pos(), Sector::current()->player->get_pos()); break; case ICESTATE_FLAT: physic.set_velocity_x(0); break; } return CONTINUE; } bool MrIceBlock::collision_squished(Player& player) { switch(ice_state) { case ICESTATE_KICKED: case ICESTATE_NORMAL: squishcount++; if(squishcount >= MAXSQUISHES) { kill_fall(); return true; } // flatten SoundManager::get()->play_sound(IDToSound(SND_STOMP), get_pos(), player.get_pos()); physic.set_velocity_x(0); physic.set_velocity_y(0); sprite->set_action(dir == LEFT ? "flat-left" : "flat-right"); flat_timer.start(4); ice_state = ICESTATE_FLAT; printf("flat.\n"); break; case ICESTATE_FLAT: // kick SoundManager::get()->play_sound(IDToSound(SND_KICK), this, player.get_pos()); if(player.get_pos().x < get_pos().x) { dir = RIGHT; } else { dir = LEFT; } physic.set_velocity_x(dir == LEFT ? -KICKSPEED : KICKSPEED); sprite->set_action(dir == LEFT ? "flat-left" : "flat-right"); ice_state = ICESTATE_KICKED; printf("kicked.\n"); break; } player.bounce(*this); return true; } --- NEW FILE: bouncing_snowball.h --- #ifndef __BOUNCING_SNOWBALL_H__ #define __BOUNCING_SNOWBALL_H__ #include "badguy.h" class BouncingSnowball : public BadGuy { public: BouncingSnowball(LispReader& reader); void activate(); void write(LispWriter& writer); HitResponse collision_solid(GameObject& other, const CollisionHit& hit); protected: bool collision_squished(Player& player); }; #endif --- NEW FILE: jumpy.cpp --- #include <config.h> #include "jumpy.h" static const float JUMPSPEED=600; Jumpy::Jumpy(LispReader& reader) { reader.read_float("x", start_position.x); reader.read_float("y", start_position.y); bbox.set_size(32, 32); sprite = sprite_manager->create("jumpy"); } void Jumpy::write(LispWriter& writer) { writer.start_list("jumpy"); writer.write_float("x", get_pos().x); writer.write_float("y", get_pos().y); writer.end_list("jumpy"); } HitResponse Jumpy::collision_solid(GameObject& other, const CollisionHit& hit) { // hit floor? if(hit.normal.y < -.5) { physic.set_velocity_y(JUMPSPEED); } else if(hit.normal.y < .5) { // bumped on roof physic.set_velocity_y(0); } return CONTINUE; } --- NEW FILE: jumpy.h --- #ifndef __JUMPY_H__ #define __JUMPY_H__ #include "badguy.h" #include "utils/lispreader.h" #include "utils/lispwriter.h" #include "serializable.h" class Jumpy : public BadGuy { public: Jumpy(LispReader& reader); virtual HitResponse collision_solid(GameObject& other, const CollisionHit& hit); virtual void write(LispWriter& writer); }; #endif --- NEW FILE: bouncing_snowball.cpp --- #include <config.h> #include "bouncing_snowball.h" static const float JUMPSPEED = 450; static const float WALKSPEED = 80; BouncingSnowball::BouncingSnowball(LispReader& reader) { reader.read_float("x", start_position.x); reader.read_float("y", start_position.y); bbox.set_size(32, 32); sprite = sprite_manager->create("bouncingsnowball"); } void BouncingSnowball::write(LispWriter& writer) { writer.start_list("bouncingsnowball"); writer.write_float("x", get_pos().x); writer.write_float("y", get_pos().y); writer.end_list("bouncingsnowball"); } void BouncingSnowball::activate() { physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED); sprite->set_action(dir == LEFT ? "left" : "right"); } bool BouncingSnowball::collision_squished(Player& player) { sprite->set_action("squished"); kill_squished(player); return true; } HitResponse BouncingSnowball::collision_solid(GameObject& other, const CollisionHit& hit) { if(hit.normal.y < -.5) { // hit floor physic.set_velocity_y(JUMPSPEED); } else if(hit.normal.y > .5) { // bumped on roof physic.set_velocity_y(0); } else { // left or right collision dir = dir == LEFT ? RIGHT : LEFT; sprite->set_action(dir == LEFT ? "left" : "right"); physic.set_velocity_x(-physic.get_velocity_x()); } return CONTINUE; } --- NEW FILE: spiky.cpp --- #include <config.h> #include "spiky.h" static const float WALKSPEED = 80; Spiky::Spiky(LispReader& reader) { reader.read_float("x", start_position.x); reader.read_float("y", start_position.y); bbox.set_size(32, 32); sprite = sprite_manager->create("spiky"); } void Spiky::write(LispWriter& writer) { writer.start_list("spiky"); writer.write_float("x", get_pos().x); writer.write_float("y", get_pos().y); writer.end_list("spiky"); } void Spiky::activate() { physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED); sprite->set_action(dir == LEFT ? "left" : "right"); } HitResponse Spiky::collision_solid(GameObject& other, const CollisionHit& hit) { if(fabsf(hit.normal.y) > .5) { // hit floor or roof? physic.set_velocity_y(0); } else { // hit right or left dir = dir == LEFT ? RIGHT : LEFT; sprite->set_action(dir == LEFT ? "left" : "right"); physic.set_velocity_x(-physic.get_velocity_x()); } return CONTINUE; } --- NEW FILE: mriceblock.h --- #ifndef __MRICEBLOCK_H__ #define __MRICEBLOCK_H__ #include "badguy.h" class MrIceBlock : public BadGuy { public: MrIceBlock(LispReader& reader); void activate(); void write(LispWriter& writer); HitResponse collision_solid(GameObject& other, const CollisionHit& hit); void active_action(float elapsed_time); protected: bool collision_squished(Player& player); private: enum IceState { ICESTATE_NORMAL, ICESTATE_FLAT, ICESTATE_KICKED }; IceState ice_state; Timer2 flat_timer; int squishcount; }; #endif --- NEW FILE: badguy.h --- #ifndef __BADGUY_H__ #define __BADGUY_H__ // moved them here to make it less typing when implementing new badguys #include <math.h> #include "timer.h" #include "special/moving_object.h" #include "special/sprite.h" #include "math/physic.h" #include "player.h" #include "serializable.h" #include "resources.h" #include "sector.h" #include "utils/lispwriter.h" #include "utils/lispreader.h" #include "video/drawing_context.h" #include "special/sprite_manager.h" using namespace SuperTux; class BadGuy : public MovingObject, public Serializable { public: BadGuy(); ~BadGuy(); //virtual void action_activated(float elapsed_time); virtual void draw(DrawingContext& context); virtual void action(float elapsed_time); virtual HitResponse collision(GameObject& other, const CollisionHit& hit); virtual void kill_fall(); protected: enum State { STATE_INIT, STATE_INACTIVE, STATE_ACTIVE, STATE_SQUISHED, STATE_FALLING }; virtual HitResponse collision_player(Player& player, const CollisionHit& hit); virtual HitResponse collision_solid(GameObject& other, const CollisionHit& hit); virtual HitResponse collision_badguy(BadGuy& other, const CollisionHit& hit); virtual bool collision_squished(Player& player); virtual void active_action(float elapsed_time); virtual void inactive_action(float elapsed_time); /** * called when the badguy has been activated. (As a side effect the dir * variable might have been changed so that it faces towards the player. */ virtual void activate(); /** caleed when the badguy has been deactivated */ virtual void deactivate(); void kill_squished(Player& player); void set_state(State state); State get_state() const { return state; } /** * returns a pointer to the player, try to avoid this function to avoid * problems later when we have multiple players or no player in scripted * sequence. */ Player* get_player(); Sprite* sprite; Physic physic; /// is the enemy activated bool activated; /** * initial position of the enemy. Also the position where enemy respawns when * after being deactivated. */ Vector start_position; Direction dir; private: bool is_offscreen(); void try_activate(); State state; Timer2 state_timer; }; #endif --- NEW FILE: mrbomb.cpp --- #include <config.h> #include "mrbomb.h" #include "bomb.h" static const float WALKSPEED = 80; MrBomb::MrBomb(LispReader& reader) { reader.read_float("x", start_position.x); reader.read_float("y", start_position.y); bbox.set_size(32, 32); sprite = sprite_manager->create("mrbomb"); } void MrBomb::write(LispWriter& writer) { writer.start_list("snowball"); writer.write_float("x", get_pos().x); writer.write_float("y", get_pos().y); writer.end_list("snowball"); } void MrBomb::activate() { physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED); sprite->set_action(dir == LEFT ? "left" : "right"); } bool MrBomb::collision_squished(Player& player) { remove_me(); Sector::current()->add_object(new Bomb(get_pos(), dir)); player.bounce(*this); return true; } HitResponse MrBomb::collision_solid(GameObject& other, const CollisionHit& hit) { if(fabsf(hit.normal.y) > .5) { // hit floor or roof? physic.set_velocity_y(0); } else { // hit right or left dir = dir == LEFT ? RIGHT : LEFT; sprite->set_action(dir == LEFT ? "left" : "right"); physic.set_velocity_x(-physic.get_velocity_x()); } return CONTINUE; } --- NEW FILE: snowball.cpp --- #include <config.h> #include "snowball.h" static const float WALKSPEED = 80; SnowBall::SnowBall(LispReader& reader) { reader.read_float("x", start_position.x); reader.read_float("y", start_position.y); bbox.set_size(32, 32); sprite = sprite_manager->create("snowball"); } void SnowBall::write(LispWriter& writer) { writer.start_list("snowball"); writer.write_float("x", get_pos().x); writer.write_float("y", get_pos().y); writer.end_list("snowball"); } void SnowBall::activate() { physic.set_velocity_x(dir == LEFT ? -WALKSPEED : WALKSPEED); sprite->set_action(dir == LEFT ? "left" : "right"); } bool SnowBall::collision_squished(Player& player) { sprite->set_action(dir == LEFT ? "squished-left" : "squished-right"); kill_squished(player); return true; } HitResponse SnowBall::collision_solid(GameObject& other, const CollisionHit& hit) { if(fabsf(hit.normal.y) > .5) { // hit floor or roof? physic.set_velocity_y(0); } else { // hit right or left dir = dir == LEFT ? RIGHT : LEFT; sprite->set_action(dir == LEFT ? "left" : "right"); physic.set_velocity_x(-physic.get_velocity_x()); } return CONTINUE; } --- NEW FILE: flame.h --- #ifndef __FLAME_H__ #define __FLAME_H__ #include "badguy.h" class Flame : public BadGuy { public: Flame(LispReader& reader); void write(LispWriter& write); void active_action(float elapsed_time); void kill_fall(); private: float angle; float radius; float speed; }; #endif --- NEW FILE: bomb.cpp --- #include <config.h> #include "bomb.h" static const float TICKINGTIME = 1; static const float EXPLOSIONTIME = 1; Bomb::Bomb(const Vector& pos, Direction dir) { start_position = pos; bbox.set_pos(pos); bbox.set_size(32, 32); sprite = sprite_manager->create("bomb"); state = 0; timer.start(TICKINGTIME); this->dir = dir; sprite->set_action(dir == LEFT ? "ticking-left" : "ticking-right"); } void Bomb::write(LispWriter& writer) { // bombs are only temporarily so don't write them out... } HitResponse Bomb::collision_solid(GameObject& other, const CollisionHit& hit) { if(fabsf(hit.normal.y) > .5) physic.set_velocity_y(0); return CONTINUE; } HitResponse Bomb::collision_player(Player& player, const CollisionHit& hit) { if(state == 1) { player.kill(Player::SHRINK); } return ABORT_MOVE; } void Bomb::active_action(float elapsed_time) { switch(state) { case 0: if(timer.check()) { state = 1; sprite->set_action("explosion"); timer.start(EXPLOSIONTIME); } break; case 1: if(timer.check()) { remove_me(); } break; } } void Bomb::kill_fall() { } --- NEW FILE: mrbomb.h --- #ifndef __MRBOMB_H__ #define __MRBOMB_H__ #include "badguy.h" class MrBomb : public BadGuy { public: MrBomb(LispReader& reader); void activate(); void write(LispWriter& writer); HitResponse collision_solid(GameObject& other, const CollisionHit& hit); protected: bool collision_squished(Player& player); }; #endif --- NEW FILE: flame.cpp --- #include <config.h> #include "flame.h" Flame::Flame(LispReader& reader) : angle(0), radius(100), speed(2) { reader.read_float("x", start_position.x); reader.read_float("y", start_position.y); reader.read_float("radius", radius); reader.read_float("speed", speed); bbox.set_pos(Vector(start_position.x + cos(angle) * radius, start_position.y + sin(angle) * radius)); bbox.set_size(32, 32); sprite = sprite_manager->create("flame"); } void Flame::write(LispWriter& writer) { writer.start_list("flame"); writer.write_float("x", start_position.x); writer.write_float("y", start_position.y); writer.write_float("radius", radius); writer.write_float("speed", speed); writer.end_list("flame"); } void Flame::active_action(float elapsed_time) { angle = fmodf(angle + elapsed_time * speed, 2*M_PI); Vector newpos(start_position.x + cos(angle) * radius, start_position.y + sin(angle) * radius); movement = newpos - get_pos(); } void Flame::kill_fall() { } |