CollisionDirection CollisionHandler::DetectCollisionWithObj(MovingObject& _obj, DisplayableObject& _ref) { if (_obj.GetID() == _ref.GetID()) return NO_COL; return DetectCollisionWithRect(_obj.GetCoordinates(), _ref.GetCoordinates()); }
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); }
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 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_); }
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_); }
bool MrIceBlock::collision_squished(GameObject& object) { switch(ice_state) { case ICESTATE_KICKED: case ICESTATE_NORMAL: squishcount++; if(squishcount >= MAXSQUISHES) { kill_fall(); return true; } set_state(ICESTATE_FLAT); nokick_timer.start(NOKICK_TIME); break; case ICESTATE_FLAT: { MovingObject* movingobject = dynamic_cast<MovingObject*>(&object); if (movingobject && (movingobject->get_pos().x < get_pos().x)) { dir = RIGHT; } else { dir = LEFT; } } if (nokick_timer.check()) set_state(ICESTATE_KICKED); break; case ICESTATE_GRABBED: assert(false); break; } Player* player = dynamic_cast<Player*>(&object); if (player) player->bounce(*this); return true; }
void NewCharacterReadListener::onEvent(const std::string &_eventType, Event* _event) { MovingObject *character = _event->GetMovingObject(); if (character->GetName() == "Mario") m_gameEngine->SetMarioInitialPosition(character->GetPosition()); m_gameEngine->AddCharacterToArray(character); m_gameEngine->AddForegroundItemToArray(character); }
void GameEngine::UpdateCharacterPosition(MovingObject& _character, float _dt) { unsigned int id = _character.GetID(); _character.UpdatePosition(_dt); sf::Vector2f pos = _character.GetPosition(); m_listForegroundItems[id]->SetX(pos.x); m_listForegroundItems[id]->SetY(pos.y); }
void CollisionHandler::ReactToCollisionsWithObj(MovingObject& _obj, DisplayableObject& _ref, CollisionDirection _direction) { ReactToCollision(_obj, _ref.GetCoordinates(), _direction); _obj.UpdateAfterCollision(Util::OppositeCollisionDirection(_direction), _ref.GetClass()); // Updates the state of the object, not its coordinates _obj.SetPosition(m_gameEngine->GetCoordinatesOfForegroundItem(_obj.GetID())); _ref.UpdateAfterCollision(_direction, _obj.GetClass()); SendNewObjectPositionToGFX(_ref); }
void Game::getInput() { m_player->doSomething(); for (list<MovingObject*>::iterator it = m_movingobjects.begin(); it != m_movingobjects.end(); it++) { MovingObject* current = *it; current->doSomething(); if (m_reset) return; } }
bool MrIceBlock::collision_squished(GameObject& object) { Player* player = dynamic_cast<Player*>(&object); if(player && (player->does_buttjump || player->is_invincible())) { player->bounce(*this); kill_fall(); return true; } switch(ice_state) { case ICESTATE_KICKED: { BadGuy* badguy = dynamic_cast<BadGuy*>(&object); if (badguy) { badguy->kill_fall(); break; } } // fall through case ICESTATE_NORMAL: { squishcount++; if (squishcount >= MAXSQUISHES) { kill_fall(); return true; } } set_state(ICESTATE_FLAT); nokick_timer.start(NOKICK_TIME); break; case ICESTATE_FLAT: { MovingObject* movingobject = dynamic_cast<MovingObject*>(&object); if (movingobject && (movingobject->get_pos().x < get_pos().x)) { dir = RIGHT; } else { dir = LEFT; } } if (nokick_timer.check()) set_state(ICESTATE_KICKED); break; case ICESTATE_GRABBED: assert(false); break; } if (player) player->bounce(*this); return true; }
bool Snail::collision_squished(GameObject& object) { if (m_frozen) return WalkingBadguy::collision_squished(object); Player* player = dynamic_cast<Player*>(&object); if (player && (player->m_does_buttjump || player->is_invincible())) { kill_fall(); player->bounce(*this); return true; } switch (state) { case STATE_KICKED: case STATE_NORMAL: // Can't stomp in midair if (!on_ground()) break; squishcount++; if (squishcount >= MAX_SNAIL_SQUISHES) { kill_fall(); return true; } SoundManager::current()->play("sounds/stomp.wav", get_pos()); be_flat(); break; case STATE_FLAT: SoundManager::current()->play("sounds/kick.wav", get_pos()); { MovingObject* movingobject = dynamic_cast<MovingObject*>(&object); if (movingobject && (movingobject->get_pos().x < get_pos().x)) { m_dir = Direction::RIGHT; } else { m_dir = Direction::LEFT; } } be_kicked(); break; case STATE_GRABBED: case STATE_KICKED_DELAY: break; } if (player) player->bounce(*this); return true; }
void Row::addMovingObjects(std::string type, int distanceBetween, int num ){ MovingObject* first = getMovingObject(type); distanceBetweenMovingObjects = distanceBetween; float x = first->getX(); int width = first->getWidth(); movingObjects.push_back(first); for(int i =1; i < num; i++){ x -= (distanceBetween + width); MovingObject* entry = getMovingObject(type); entry->setX(x); movingObjects.push_back(entry); } }
void Row::addMovingObject(std::string type){ MovingObject * newMovingObject = getMovingObject(type); if(movingObjects.size() >0){ MovingObject * lastMovingObject = movingObjects.at(movingObjects.size()-1); float x = lastMovingObject->getX(); int width = lastMovingObject->getWidth(); newMovingObject->setX(x - distanceBetweenMovingObjects -width ); } movingObjects.push_back(newMovingObject); }
Vector4 SteeringBehavior::evade(Handle &p_pursuer_handle){ IHasHandle* tmp; MovingObject* pursuer; Vector4 toPursuer; float look_ahead_time; tmp = theWorld.get(p_pursuer_handle); if (pursuer = dynamic_cast<MovingObject*>(tmp)){ toPursuer = pursuer->getPosition() - owner->getPosition(); look_ahead_time = toPursuer.length() / (owner->getMaxSpeed() + pursuer->getSpeed()); return flee(pursuer->getPosition() + pursuer->getVelocity() * look_ahead_time); } else { off(BehaviorType::evade); } return Vector4(); }
// Broadcast character's position void GameEngine::SendCharacterPosition(int _indexCharacter) { MovingObject *character = m_characters[_indexCharacter]; if (character == NULL || character->IsDead()) return; InfoForDisplay info = character->GetInfoForDisplay(); Event posInfo(&info); m_eventEngine->dispatch(CHAR_POS_UPDATED, &posInfo); #ifdef DEBUG_MODE if (_indexCharacter == m_indexMario) { *m_debugInfo = character->GetDebugInfo(); Event debugInfo(m_debugInfo); m_eventEngine->dispatch(DEBUG_INFO_UPDATED, &debugInfo); } #endif }
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)); } } } } }
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; }
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; }
void Bomb::ungrab(MovingObject& object, Direction dir_) { this->dir = dir_; // portable objects are usually pushed away from Tux when dropped, but we // don't want that, so we set the position //FIXME: why don't we want that? shouldn't behavior be consistent? set_pos(object.get_pos() + Vector(dir_ == LEFT ? -16 : 16, get_bbox().get_height()*0.66666 - 32)); set_colgroup_active(COLGROUP_MOVING); grabbed = 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); }
Vector4 SteeringBehavior::offsetPursuit(Handle &p_leader, Vector4 &offset) { MovingObject* leader = dynamic_cast<MovingObject*>(theWorld.get(p_leader)); if (leader == nullptr) { // Leader does not exist anymore off(BehaviorType::offset_pursuit); return Vector4(0, 0, 0); } // calculate the offset's position in world space Vector4 WorldOffsetPos = leader->getPosition() + offset; Vector4 ToOffset = WorldOffsetPos - owner->getPosition(); // The lookahead time is propotional to the distance between the leader // and the pursuer; and is inversely proportional to the sum of both // agent's velocities float LookAheadTime = ToOffset.length() / (owner->getMaxSpeed() + leader->getSpeed()); // Arrive at the predicted future position of the offset return arrive(WorldOffsetPos + leader->getVelocity() * LookAheadTime, fast); }
void CollisionHandler::HandleCollisionsWithMapEdges(MovingObject& _obj) { if (_obj.GetPosition().x < 0) { _obj.UpdateAfterCollisionWithMapEdge(CollisionDirection::LEFT, _obj.GetPosition().x); } float gapRightEdge = _obj.GetPosition().x - (m_levelSize.x - _obj.GetCoordinates().width); if (gapRightEdge > 0) { _obj.UpdateAfterCollisionWithMapEdge(CollisionDirection::RIGHT, gapRightEdge); } if (_obj.GetPosition().y > m_levelSize.y) _obj.Kill(); }
//TODO: fix shoot direciton and, if possible amount of objects created void NPC::ShootMissile() { if (!m_flags[eAttacking]) { //TODO: this isn't properly cleaned up. Also need to add it to Level's object lists //All in all we need a better way to create projectiles MovingObject* missile = new MovingObject(m_graphicContext, *m_resourceManager, m_position.x, m_position.y, "Missile"); missile->ConnectSlots(*m_drawSignal, *m_updateSignal); missile->SetEnvList(m_environment); missile->SetDirection(m_direction); if (missile->GetDirection() == eLeft) { missile->SetSpeed(eXSpeed, -missile->GetSpeed(eXSpeed)); } StartAttack(); missile->StartMoving(m_direction); m_timeSinceLastAttack = CL_System::get_time(); } }
void NPC::ShootArrow() { if (!m_flags[eAttacking]) { //TODO: this isn't properly cleaned up. MovingObject* arrow = new MovingObject(m_graphicContext, *m_resourceManager, m_position.x, m_position.y, "Arrow"); arrow->ConnectSlots(*m_drawSignal, *m_updateSignal); arrow->SetEnvList(m_environment); double launchAngle = Utility::CalculateTrajectory(arrow->GetPosition(), m_target->GetPosition(), arrow->GetSpeed(eCompositeSpeed), true); arrow->SetSpeed(eXSpeed, arrow->GetSpeed(eCompositeSpeed) * sin(launchAngle)); arrow->SetSpeed(eYSpeed, arrow->GetSpeed(eCompositeSpeed) * cos(launchAngle)); StartAttack(); arrow->StartMoving(m_direction); } }
// Handles collisions between the object and all the DisplayableObjects in m_listForegroundItems and with the map edges: detection and reaction. void GameEngine::HandleCollisions(MovingObject& _obj) { if (m_listForegroundItems.size() <= 1) return; CollisionDirection tmpDirection = NO_COL; if (_obj.CanCollide()) { // What happens if there is a collision so _obj is moved and there is another one and _obj is moved again ? The first collision would need to be handled again for (std::map<unsigned int, DisplayableObject*>::iterator it = m_listForegroundItems.begin(); it != m_listForegroundItems.end(); ++it) { tmpDirection = m_collisionHandler->DetectCollisionWithObj(_obj, *(it->second)); if (tmpDirection != NO_COL) { m_collisionHandler->ReactToCollisionsWithObj(_obj, *(m_listForegroundItems[it->first]), tmpDirection); } } } m_collisionHandler->HandleCollisionsWithMapEdges(_obj); }
Vector4 SteeringBehavior::pursuit(Handle &p_evader_handle){ IHasHandle* tmp; MovingObject* evader; Vector4 toEvader; float relativeHeading, look_ahead_time; tmp = theWorld.get(p_evader_handle); if (evader = dynamic_cast<MovingObject*>(tmp)){ toEvader = evader->getPosition() - owner->getPosition(); relativeHeading = evader->getHeading().dot(owner->getHeading()); if (toEvader.dot(owner->getHeading()) > 0 && relativeHeading < -0.95){ return this->seek(evader->getPosition()); } look_ahead_time = toEvader.length() / (owner->getMaxSpeed() + evader->getSpeed()); return seek(evader->getPosition() + evader->getVelocity() * look_ahead_time); } else { off(BehaviorType::pursuit); } return Vector4(0, 0, 0); }
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) */ }