void Sector::draw(DrawingContext& context) { context.set_ambient_color( ambient_light ); context.push_transform(); context.set_translation(camera->get_translation()); for(auto i = gameobjects.begin(); i != gameobjects.end(); ++i) { GameObjectPtr& object = *i; if(!object->is_valid()) continue; if (draw_solids_only) { TileMap* tm = dynamic_cast<TileMap*>(object.get()); if (tm && !tm->is_solid()) continue; } object->draw(context); } if(show_collrects) { Color color(1.0f, 0.0f, 0.0f, 0.75f); for(auto i = moving_objects.begin(); i != moving_objects.end(); ++i) { MovingObject* object = *i; const Rectf& rect = object->get_bbox(); context.draw_filled_rect(rect, color, LAYER_FOREGROUND1 + 10); } } context.pop_transform(); }
HitResponse BonusBlock::collision(GameObject& other, const CollisionHit& hit_){ Player* player = dynamic_cast<Player*> (&other); if (player) { if (player->does_buttjump) try_drop(player); } BadGuy* badguy = dynamic_cast<BadGuy*> (&other); if(badguy) { // hit contains no information for collisions with blocks. // Badguy's bottom has to be below the top of the block // SHIFT_DELTA is required to slide over one tile gaps. if( badguy->can_break() && ( badguy->get_bbox().get_bottom() > bbox.get_top() + SHIFT_DELTA ) ){ try_open(player); } } Portable* portable = dynamic_cast<Portable*> (&other); if(portable) { MovingObject* moving = dynamic_cast<MovingObject*> (&other); if(moving->get_bbox().get_top() > bbox.get_bottom() - SHIFT_DELTA) { try_open(player); } } return Block::collision(other, hit_); }
void FlipLevelTransformer::transform_moving_object(float height, MovingObject& object) { Vector pos = object.get_pos(); pos.y = height - pos.y - object.get_bbox().get_height(); object.set_pos(pos); }
HitResponse Brick::collision(GameObject& other, const CollisionHit& hit_){ Player* player = dynamic_cast<Player*> (&other); if (player) { if (player->does_buttjump) try_break(player); if (player->is_stone() && player->get_velocity().y >= 280) try_break(player); // stoneform breaks through bricks } BadGuy* badguy = dynamic_cast<BadGuy*> (&other); if(badguy) { // hit contains no information for collisions with blocks. // Badguy's bottom has to be below the top of the brick // SHIFT_DELTA is required to slide over one tile gaps. if( badguy->can_break() && ( badguy->get_bbox().get_bottom() > bbox.get_top() + SHIFT_DELTA ) ){ try_break(player); } } Portable* portable = dynamic_cast<Portable*> (&other); if(portable) { MovingObject* moving = dynamic_cast<MovingObject*> (&other); if(moving->get_bbox().get_top() > bbox.get_bottom() - SHIFT_DELTA) { try_break(player); } } Explosion* explosion = dynamic_cast<Explosion*> (&other); if(explosion && explosion->hurts()) { try_break(player); } IceCrusher* icecrusher = dynamic_cast<IceCrusher*> (&other); if(icecrusher && coin_counter == 0) try_break(player); return Block::collision(other, hit_); }
void Sector::collision_tilemap(collision::Constraints* constraints, const Vector& movement, const Rectf& dest, MovingObject& object) const { // calculate rectangle where the object will move float x1 = dest.get_left(); float x2 = dest.get_right(); float y1 = dest.get_top(); float y2 = dest.get_bottom(); for(auto i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) { TileMap* solids = *i; // test with all tiles in this rectangle Rect test_tiles = solids->get_tiles_overlapping(Rectf(x1, y1, x2, y2)); for(int x = test_tiles.left; x < test_tiles.right; ++x) { for(int y = test_tiles.top; y < test_tiles.bottom; ++y) { const Tile* tile = solids->get_tile(x, y); if(!tile) continue; // skip non-solid tiles if(!tile->is_solid ()) continue; Rectf tile_bbox = solids->get_tile_bbox(x, y); /* If the tile is a unisolid tile, the "is_solid()" function above * didn't do a thorough check. Calculate the position and (relative) * movement of the object and determine whether or not the tile is * solid with regard to those parameters. */ if(tile->is_unisolid ()) { Vector relative_movement = movement - solids->get_movement(/* actual = */ true); if (!tile->is_solid (tile_bbox, object.get_bbox(), relative_movement)) continue; } /* if (tile->is_unisolid ()) */ if(tile->is_slope ()) { // slope tile AATriangle triangle; int slope_data = tile->getData(); if (solids->get_drawing_effect() & VERTICAL_FLIP) slope_data = AATriangle::vertical_flip(slope_data); triangle = AATriangle(tile_bbox, slope_data); collision::rectangle_aatriangle(constraints, dest, triangle, solids->get_movement(/* actual = */ false)); } else { // normal rectangular tile check_collisions(constraints, movement, dest, tile_bbox, NULL, NULL, solids->get_movement(/* actual = */ false)); } } } } }
bool Igel::can_see(const MovingObject& o) const { Rectf ob = o.get_bbox(); bool inReach_left = ((ob.p2.x < bbox.p1.x) && (ob.p2.x >= bbox.p1.x-((dir == LEFT) ? RANGE_OF_VISION : 0))); bool inReach_right = ((ob.p1.x > bbox.p2.x) && (ob.p1.x <= bbox.p2.x+((dir == RIGHT) ? RANGE_OF_VISION : 0))); bool inReach_top = (ob.p2.y >= bbox.p1.y); bool inReach_bottom = (ob.p1.y <= bbox.p2.y); return ((inReach_left || inReach_right) && inReach_top && inReach_bottom); }
HitResponse PneumaticPlatform::collision(GameObject& other, const CollisionHit& ) { // somehow the hit parameter does not get filled in, so to determine (hit.top == true) we do this: MovingObject* mo = dynamic_cast<MovingObject*>(&other); if (!mo) return FORCE_MOVE; if ((mo->get_bbox().p2.y) > (get_bbox().p1.y + 2)) return FORCE_MOVE; Player* pl = dynamic_cast<Player*>(mo); if (pl) { if (pl->is_big()) contacts.insert(0); Portable* po = pl->get_grabbed_object(); MovingObject* pomo = dynamic_cast<MovingObject*>(po); if (pomo) contacts.insert(pomo); } contacts.insert(&other); return FORCE_MOVE; }
HitResponse Brick::collision(GameObject& other, const CollisionHit& hit){ BadGuy* badguy = dynamic_cast<BadGuy*> (&other); if(badguy) { // hit contains no information for collisions with blocks. // Badguy's bottom has to be below the top of the brick // +7 is required to slide over one tile gaps. if( badguy->can_break() && ( badguy->get_bbox().get_bottom() > get_bbox().get_top() + 7.0 ) ){ try_break(false); } } Portable* portable = dynamic_cast<Portable*> (&other); if(portable) { MovingObject* moving = dynamic_cast<MovingObject*> (&other); if(moving->get_bbox().get_top() > get_bbox().get_bottom() - 7.0) { try_break(); } } return Block::collision(other, hit); }
HitResponse BicyclePlatform::collision(GameObject& other, const CollisionHit& ) { // somehow the hit parameter does not get filled in, so to determine (hit.top == true) we do this: MovingObject* mo = dynamic_cast<MovingObject*>(&other); if (!mo) return FORCE_MOVE; if ((mo->get_bbox().p2.y) > (bbox.p1.y + 2)) return FORCE_MOVE; Player* pl = dynamic_cast<Player*>(mo); if (pl) { if (pl->is_big()) momentum += 0.1 * Sector::current()->get_gravity(); Portable* po = pl->get_grabbed_object(); MovingObject* pomo = dynamic_cast<MovingObject*>(po); if (contacts.insert(pomo).second) momentum += 0.1 * Sector::current()->get_gravity(); } if (contacts.insert(&other).second) momentum += 0.1 * Sector::current()->get_gravity(); return FORCE_MOVE; }
void AngryStone::active_update(float elapsed_time) { BadGuy::active_update(elapsed_time); if (frozen) { return; } switch (state) { case IDLE: { MovingObject* player = get_nearest_player(); if(player) { MovingObject* badguy = this; const Vector playerPos = player->get_pos(); const Vector badguyPos = badguy->get_pos(); float dx = (playerPos.x - badguyPos.x); float dy = (playerPos.y - badguyPos.y); float playerHeight = player->get_bbox().get_height(); float badguyHeight = badguy->get_bbox().get_height(); float playerWidth = player->get_bbox().get_width(); float badguyWidth = badguy->get_bbox().get_width(); if ((dx > -playerWidth) && (dx < badguyWidth)) { if (dy > 0) { attackDirection.x = 0; attackDirection.y = 1; } else { attackDirection.x = 0; attackDirection.y = -1; } if ((attackDirection.x != oldWallDirection.x) || (attackDirection.y != oldWallDirection.y)) { sprite->set_action("charging"); timer.start(CHARGE_TIME); state = CHARGING; } } else if ((dy > -playerHeight) && (dy < badguyHeight)) { if (dx > 0) { attackDirection.x = 1; attackDirection.y = 0; } else { attackDirection.x = -1; attackDirection.y = 0; } if ((attackDirection.x != oldWallDirection.x) || (attackDirection.y != oldWallDirection.y)) { sprite->set_action("charging"); timer.start(CHARGE_TIME); state = CHARGING; } } } } break; case CHARGING: { if (timer.check()) { sprite->set_action("attacking"); timer.start(ATTACK_TIME); state = ATTACKING; physic.enable_gravity(false); physic.set_velocity_x(CHARGE_SPEED * attackDirection.x); physic.set_velocity_y(CHARGE_SPEED * attackDirection.y); oldWallDirection.x = 0; oldWallDirection.y = 0; } } break; case ATTACKING: { if (timer.check()) { timer.start(RECOVER_TIME); state = RECOVERING; sprite->set_action("idle"); physic.enable_gravity(true); physic.set_velocity_x(0); physic.set_velocity_y(0); } } break; case RECOVERING: { if (timer.check()) { state = IDLE; sprite->set_action("idle"); physic.enable_gravity(true); physic.set_velocity_x(0); physic.set_velocity_y(0); } } break; } }
void Explosion::explode() { if (state != STATE_WAITING) return; state = STATE_EXPLODING; set_action(hurt ? "default" : "pop", 1); sprite->set_animation_loops(1); //TODO: this is necessary because set_action will not set "loops" when "action" is the default action sprite->set_angle(graphicsRandom.randf(0, 360)); // a random rotation on the sprite to make explosions appear more random SoundManager::current()->play(hurt ? "sounds/explosion.wav" : "sounds/firecracker.ogg", get_pos()); #if 0 // spawn some particles // TODO: provide convenience function in MovingSprite or MovingObject? for (int i = 0; i < 100; i++) { Vector ppos = bbox.get_middle(); float angle = graphicsRandom.randf(-M_PI_2, M_PI_2); float velocity = graphicsRandom.randf(450, 900); float vx = sin(angle)*velocity; float vy = -cos(angle)*velocity; Vector pspeed = Vector(vx, vy); Vector paccel = Vector(0, 1000); Sector::current()->add_object(new SpriteParticle("images/objects/particles/explosion.sprite", "default", ppos, ANCHOR_MIDDLE, pspeed, paccel, LAYER_OBJECTS-1)); } #endif if (push) { Vector center = get_bbox ().get_middle (); std::vector<MovingObject*> near_objects = Sector::current()->get_nearby_objects (center, 10.0 * 32.0); for (size_t i = 0; i < near_objects.size (); i++) { MovingObject *obj = near_objects[i]; Vector obj_vector = obj->get_bbox ().get_middle (); Vector direction = obj_vector - center; float distance = direction.norm (); /* If the distance is very small, for example because "obj" is the badguy * causing the explosion, skip this object. */ if (distance <= 1.0) continue; /* The force decreases with the distance squared. In the distance of one * tile (32 pixels) you will have a speed increase of 150 pixels/s. */ float force = 150.0 * 32.0*32.0 / (distance * distance); if (force > 200.0) force = 200.0; Vector add_speed = direction.unit () * force; Player *player = dynamic_cast<Player *> (obj); if (player) { player->add_velocity (add_speed); } WalkingBadguy *badguy = dynamic_cast<WalkingBadguy *> (obj); if (badguy) { badguy->add_velocity (add_speed); } } /* for (i = 0 ... near_objects) */ } /* if (push) */ }
void Sector::collision_static_constrains(MovingObject& object) { using namespace collision; float infinity = (std::numeric_limits<float>::has_infinity ? std::numeric_limits<float>::infinity() : std::numeric_limits<float>::max()); Constraints constraints; Vector movement = object.get_movement(); Rect& dest = object.dest; float owidth = object.get_bbox().get_width(); float oheight = object.get_bbox().get_height(); for(int i = 0; i < 2; ++i) { collision_static(&constraints, Vector(0, movement.y), dest, object); if(!constraints.has_constraints()) break; // apply calculated horizontal constraints if(constraints.bottom < infinity) { float height = constraints.bottom - constraints.top; if(height < oheight) { // we're crushed, but ignore this for now, we'll get this again // later if we're really crushed or things will solve itself when // looking at the vertical constraints } dest.p2.y = constraints.bottom - DELTA; dest.p1.y = dest.p2.y - oheight; } else if(constraints.top > -infinity) { dest.p1.y = constraints.top + DELTA; dest.p2.y = dest.p1.y + oheight; } } if(constraints.has_constraints()) { if(constraints.hit.bottom) { dest.move(constraints.ground_movement); } if(constraints.hit.top || constraints.hit.bottom) { constraints.hit.left = false; constraints.hit.right = false; object.collision_solid(constraints.hit); } } constraints = Constraints(); for(int i = 0; i < 2; ++i) { collision_static(&constraints, movement, dest, object); if(!constraints.has_constraints()) break; // apply calculated vertical constraints if(constraints.right < infinity) { float width = constraints.right - constraints.left; if(width + SHIFT_DELTA < owidth) { printf("Object %p crushed horizontally... L:%f R:%f\n", &object, constraints.left, constraints.right); CollisionHit h; h.left = true; h.right = true; h.crush = true; object.collision_solid(h); } else { dest.p2.x = constraints.right - DELTA; dest.p1.x = dest.p2.x - owidth; } } else if(constraints.left > -infinity) { dest.p1.x = constraints.left + DELTA; dest.p2.x = dest.p1.x + owidth; } } if(constraints.has_constraints()) { if( constraints.hit.left || constraints.hit.right || constraints.hit.top || constraints.hit.bottom || constraints.hit.crush ) object.collision_solid(constraints.hit); } // an extra pass to make sure we're not crushed horizontally constraints = Constraints(); collision_static(&constraints, movement, dest, object); if(constraints.bottom < infinity) { float height = constraints.bottom - constraints.top; if(height + SHIFT_DELTA < oheight) { printf("Object %p crushed vertically...\n", &object); CollisionHit h; h.top = true; h.bottom = true; h.crush = true; object.collision_solid(h); } } }
void Sector::collision_static_constrains(MovingObject& object) { using namespace collision; float infinity = (std::numeric_limits<float>::has_infinity ? std::numeric_limits<float>::infinity() : std::numeric_limits<float>::max()); Constraints constraints; Vector movement = object.get_movement(); Vector pressure = Vector(0,0); Rectf& dest = object.dest; for(int i = 0; i < 2; ++i) { collision_static(&constraints, Vector(0, movement.y), dest, object); if(!constraints.has_constraints()) break; // apply calculated horizontal constraints if(constraints.get_position_bottom() < infinity) { float height = constraints.get_height (); if(height < object.get_bbox().get_height()) { // we're crushed, but ignore this for now, we'll get this again // later if we're really crushed or things will solve itself when // looking at the vertical constraints pressure.y += object.get_bbox().get_height() - height; } else { dest.p2.y = constraints.get_position_bottom() - DELTA; dest.p1.y = dest.p2.y - object.get_bbox().get_height(); } } else if(constraints.get_position_top() > -infinity) { dest.p1.y = constraints.get_position_top() + DELTA; dest.p2.y = dest.p1.y + object.get_bbox().get_height(); } } if(constraints.has_constraints()) { if(constraints.hit.bottom) { dest.move(constraints.ground_movement); } if(constraints.hit.top || constraints.hit.bottom) { constraints.hit.left = false; constraints.hit.right = false; object.collision_solid(constraints.hit); } } constraints = Constraints(); for(int i = 0; i < 2; ++i) { collision_static(&constraints, movement, dest, object); if(!constraints.has_constraints()) break; // apply calculated vertical constraints float width = constraints.get_width (); if(width < infinity) { if(width + SHIFT_DELTA < object.get_bbox().get_width()) { // we're crushed, but ignore this for now, we'll get this again // later if we're really crushed or things will solve itself when // looking at the horizontal constraints pressure.x += object.get_bbox().get_width() - width; } else { float xmid = constraints.get_x_midpoint (); dest.p1.x = xmid - object.get_bbox().get_width()/2; dest.p2.x = xmid + object.get_bbox().get_width()/2; } } else if(constraints.get_position_right() < infinity) { dest.p2.x = constraints.get_position_right() - DELTA; dest.p1.x = dest.p2.x - object.get_bbox().get_width(); } else if(constraints.get_position_left() > -infinity) { dest.p1.x = constraints.get_position_left() + DELTA; dest.p2.x = dest.p1.x + object.get_bbox().get_width(); } } if(constraints.has_constraints()) { if( constraints.hit.left || constraints.hit.right || constraints.hit.top || constraints.hit.bottom || constraints.hit.crush ) object.collision_solid(constraints.hit); } // an extra pass to make sure we're not crushed vertically if (pressure.y > 0) { constraints = Constraints(); collision_static(&constraints, movement, dest, object); if(constraints.get_position_bottom() < infinity) { float height = constraints.get_height (); if(height + SHIFT_DELTA < object.get_bbox().get_height()) { CollisionHit h; h.top = true; h.bottom = true; h.crush = pressure.y > 16; object.collision_solid(h); } } } // an extra pass to make sure we're not crushed horizontally if (pressure.x > 0) { constraints = Constraints(); collision_static(&constraints, movement, dest, object); if(constraints.get_position_right() < infinity) { float width = constraints.get_width (); if(width + SHIFT_DELTA < object.get_bbox().get_width()) { CollisionHit h; h.top = true; h.bottom = true; h.left = true; h.right = true; h.crush = pressure.x > 16; object.collision_solid(h); } } } }