/** * \brief This function is called by the engine when an entity overlaps the pickable item. * * If the entity is the player, we give him the item, and the map is notified * to destroy it. * \param entity_overlapping the entity overlapping the detector * \param collision_mode the collision mode that detected the collision */ void Pickable::notify_collision(MapEntity& entity_overlapping, CollisionMode collision_mode) { if (entity_overlapping.is_hero()) { try_give_item_to_player(); } else if (entity_followed == NULL) { if (entity_overlapping.get_type() == ENTITY_BOOMERANG) { Boomerang& boomerang = static_cast<Boomerang&>(entity_overlapping); if (!boomerang.is_going_back()) { boomerang.go_back(); } entity_followed = &boomerang; } else if (entity_overlapping.get_type() == ENTITY_HOOKSHOT) { Hookshot& hookshot = static_cast<Hookshot&>(entity_overlapping); if (!hookshot.is_going_back()) { hookshot.go_back(); } entity_followed = &hookshot; } if (entity_followed != NULL) { clear_movement(); set_movement(new FollowMovement(entity_followed, 0, 0, true)); falling_height = FALLING_NONE; set_blinking(false); } } }
/** * \brief This function is called by the engine when an entity overlaps the pickable item. * * If the entity is the player, we give him the item, and the map is notified * to destroy it. * \param entity_overlapping the entity overlapping the detector * \param collision_mode the collision mode that detected the collision */ void Pickable::notify_collision(MapEntity& entity_overlapping, CollisionMode /* collision_mode */) { if (entity_overlapping.is_hero()) { try_give_item_to_player(); } else if (entity_followed == nullptr) { MapEntityPtr shared_entity_overlapping = std::static_pointer_cast<MapEntity>(entity_overlapping.shared_from_this()); if (entity_overlapping.get_type() == ENTITY_BOOMERANG) { Boomerang& boomerang = static_cast<Boomerang&>(entity_overlapping); if (!boomerang.is_going_back()) { boomerang.go_back(); } entity_followed = shared_entity_overlapping; } else if (entity_overlapping.get_type() == ENTITY_HOOKSHOT) { Hookshot& hookshot = static_cast<Hookshot&>(entity_overlapping); if (!hookshot.is_going_back()) { hookshot.go_back(); } entity_followed = shared_entity_overlapping; } if (entity_followed != nullptr) { clear_movement(); set_movement(std::make_shared<FollowMovement>( entity_followed, 0, 0, true )); falling_height = FALLING_NONE; set_blinking(false); } } }
/** * \brief Returns the destination point specified by the last call to * set_destination(). * * Returns NULL if the destination point was set to a special value * ("_same", "_side0", "_side1", "_side2" or "_side3"). * * \return The destination point previously set, or NULL. */ Destination* Map::get_destination() { if (destination_name == "_same" || destination_name.substr(0,5) == "_side") { return NULL; } Debug::check_assertion(is_loaded(), "This map is not loaded"); Destination* destination = NULL; std::string destination_name = this->destination_name; if (!destination_name.empty()) { // Use the destination whose name was specified. MapEntity* entity = get_entities().get_entity(destination_name); if (entity->get_type() != ENTITY_DESTINATION) { Debug::die(std::string("Map '") + get_id() + "': entity '" + destination_name + "' is not a destination"); } destination = static_cast<Destination*>(entity); } else { // No destination name was set: use the default one. destination = get_entities().get_default_destination(); if (destination == NULL) { Debug::die(std::string("Map '") + get_id() + "' has no destination entity"); } } return destination; }
/** * @brief Notifies this entity that another sprite is overlapping it. * * This function is called by check_collision(MapEntity*, Sprite*) when another entity's * sprite overlaps a sprite of this detector. * * @param other_entity the entity overlapping this detector * @param other_sprite the sprite of other_entity that is overlapping this detector * @param this_sprite the sprite of this detector that is overlapping the other entity's sprite */ void Destructible::notify_collision(MapEntity& other_entity, Sprite& other_sprite, Sprite& this_sprite) { if (features[subtype].can_be_cut && !is_being_cut && !is_disabled() && !is_regenerating && other_entity.is_hero() && other_sprite.contains("sword")) { Hero& hero = static_cast<Hero&>(other_entity); if (hero.is_striking_with_sword(*this)) { play_destroy_animation(); hero.check_position(); // to update the ground under the hero create_pickable(); if (can_explode()) { explode(); } } } // TODO use dynamic dispatch if (other_entity.get_type() == EXPLOSION && can_explode() && !is_being_cut && !is_disabled() && !is_regenerating) { play_destroy_animation(); create_pickable(); explode(); } }
/** * \brief Removes any boomerang from the map. */ void MapEntities::remove_arrows() { // TODO this function may be slow if there are a lot of entities: store the arrows? std::list<MapEntity*>::iterator it; for (it = all_entities.begin(); it != all_entities.end(); it++) { MapEntity* entity = *it; if (entity->get_type() == ARROW) { remove_entity(entity); } } }
/** * \brief Sets whether this custom entity can be traversed by the specified * entity. * \param entity The entity to test. * \return \c true if the entity can traverse this custom entity. */ bool CustomEntity::is_traversable_by_entity(MapEntity& entity) { // Find the obstacle settings. const TraversableInfo& info = get_traversable_by_entity_info(entity.get_type()); if (info.is_empty()) { // Nothing was set: make the custom entity traversable by default. return true; } return info.is_traversable(*this, entity); }
/** * @brief Returns the entity with the specified type and name, or NULL if it doesn't exist. * @param type type of entity * @param name name of the entity to get * @return the entity requested, or NULL if there is no entity with the specified type and name */ MapEntity* MapEntities::find_entity(EntityType type, const std::string &name) { list<MapEntity*>::iterator i; for (i = all_entities.begin(); i != all_entities.end(); i++) { MapEntity *entity = *i; if (entity->get_type() == type && entity->get_name() == name && !entity->is_being_removed()) { return entity; } } return NULL; }
/** * @brief Returns the entities of the map with the specified type and having the specified name prefix. * @param type type of entity * @param prefix prefix of the name * @return the entities of this type and having this prefix in their name */ list<MapEntity*> MapEntities::get_entities_with_prefix(EntityType type, const std::string &prefix) { list<MapEntity*> entities; list<MapEntity*>::iterator i; for (i = all_entities.begin(); i != all_entities.end(); i++) { MapEntity *entity = *i; if (entity->get_type() == type && entity->has_prefix(prefix) && !entity->is_being_removed()) { entities.push_back(entity); } } return entities; }
/** * @brief Returns all entities of the map with the specified type. * @param type type of entity * @return the entities of this type */ list<MapEntity*> MapEntities::get_entities(EntityType type) { list<MapEntity*> entities; list<MapEntity*>::iterator i; for (i = all_entities.begin(); i != all_entities.end(); i++) { MapEntity *entity = *i; if (entity->get_type() == type && !entity->is_being_removed()) { entities.push_back(entity); } } return entities; }
/** * \copydoc Detector::notify_collision(MapEntity&, Sprite&, Sprite&) */ void Destructible::notify_collision( MapEntity& other_entity, Sprite& /* this_sprite */, Sprite& other_sprite ) { if (get_can_be_cut() && !is_being_cut && !is_waiting_for_regeneration() && !is_regenerating && other_entity.is_hero()) { Hero& hero = static_cast<Hero&>(other_entity); if (other_sprite.get_animation_set_id() == hero.get_hero_sprites().get_sword_sprite_id() && hero.is_striking_with_sword(*this)) { play_destroy_animation(); hero.check_position(); // To update the ground under the hero. create_treasure(); get_lua_context().destructible_on_cut(*this); if (get_can_explode()) { explode(); } } } // TODO use dynamic dispatch if (other_entity.get_type() == EntityType::EXPLOSION && get_can_explode() && !is_being_cut && !is_waiting_for_regeneration() && !is_regenerating) { play_destroy_animation(); create_treasure(); explode(); } }
/** * \brief Returns the destination point specified by the last call to * set_destination(). * * If the destination point was set to a special value * ("_same", "_side0", "_side1", "_side2" or "_side3"), returns nullptr. * * If the destination name is empty, returns the default destination if any, * or nullptr. * * Otherwise, if there is no destination entity with this name on the map, * prints an error message and returns the default destination if any or nullptr. * * \return The destination point previously set, or nullptr. */ Destination* Map::get_destination() { if (destination_name == "_same" || destination_name.substr(0,5) == "_side") { return nullptr; } Debug::check_assertion(is_loaded(), "This map is not loaded"); Destination* destination = nullptr; if (!destination_name.empty()) { // Use the destination whose name was specified. MapEntity* entity = get_entities().find_entity(destination_name); if (entity == nullptr || entity->get_type() != EntityType::DESTINATION) { Debug::error( std::string("Map '") + get_id() + "': No such destination: '" + destination_name + "'" ); // Perhaps the game was saved with a destination that no longer exists // or whose name was changed during the development of the quest. // This is not a fatal error: we will use the default destination // instead. It is up to quest makers to avoid this situation once their // quest is released. } else { destination = static_cast<Destination*>(entity); } } if (destination == nullptr) { // No valid destination name was set: use the default one if any. destination = get_entities().get_default_destination(); } return destination; }
/** * \brief Removes and destroys the entities placed in the entities_to_remove list. */ void MapEntities::remove_marked_entities() { list<MapEntity*>::iterator it; // remove the marked entities for (it = entities_to_remove.begin(); it != entities_to_remove.end(); it++) { MapEntity* entity = *it; Layer layer = entity->get_layer(); // remove it from the obstacle entities list if present if (entity->can_be_obstacle()) { if (entity->has_layer_independent_collisions()) { for (int i = 0; i < LAYER_NB; i++) { obstacle_entities[i].remove(entity); } } else { obstacle_entities[layer].remove(entity); } } // remove it from the detectors list if present if (entity->is_detector()) { detectors.remove(static_cast<Detector*>(entity)); } // remove it from the ground obsevers list if present if (entity->is_ground_observer()) { ground_observers[layer].remove(entity); } // remove it from the ground modifiers list if present if (entity->is_ground_modifier()) { ground_modifiers[layer].remove(entity); } // remove it from the sprite entities list if present if (entity->is_drawn_in_y_order()) { entities_drawn_y_order[layer].remove(entity); } else if (entity->can_be_drawn()) { entities_drawn_first[layer].remove(entity); } // remove it from the whole list all_entities.remove(entity); const std::string& name = entity->get_name(); if (!name.empty()) { named_entities.erase(name); } // update the specific entities lists switch (entity->get_type()) { case STAIRS: stairs[layer].remove(static_cast<Stairs*>(entity)); break; case CRYSTAL_BLOCK: crystal_blocks[layer].remove(static_cast<CrystalBlock*>(entity)); break; case SEPARATOR: separators.remove(static_cast<Separator*>(entity)); break; case BOOMERANG: this->boomerang = NULL; break; default: break; } // destroy it destroy_entity(entity); } entities_to_remove.clear(); }
/** * \brief Returns whether this entity is an obstacle for another one * when it is enabled. * \param other another entity * \return true if this entity is an obstacle for the other one */ bool Wall::is_obstacle_for(MapEntity& other) { std::set<EntityType>::const_iterator it = entity_types_stopped.find(other.get_type()); return it != entity_types_stopped.end(); }
/** * \brief Returns whether this entity is an obstacle for another one * when it is enabled. * \param other another entity * \return true if this entity is an obstacle for the other one */ bool Wall::is_obstacle_for(MapEntity &other) { return entity_types_stopped[other.get_type()]; }
/** * \brief Returns whether this entity is an obstacle for another one * when it is enabled. * \param other another entity * \return true if this entity is an obstacle for the other one */ bool Wall::is_obstacle_for(MapEntity& other) { const auto it = entity_types_stopped.find(other.get_type()); return it != entity_types_stopped.end(); }
/** * @brief Notifies this detector that a pixel-perfect collision was just detected with another sprite. * * This function is called by check_collision(MapEntity&, Sprite&) when another entity's * sprite overlaps a sprite of this detector. * * @param other_entity the entity overlapping this detector * @param other_sprite the sprite of other_entity that is overlapping this detector * @param this_sprite the sprite of this detector that is overlapping the other entity's sprite */ void Door::notify_collision(MapEntity& other_entity, Sprite& other_sprite, Sprite& this_sprite) { if (other_entity.get_type() == EXPLOSION) { notify_collision_with_explosion(static_cast<Explosion&>(other_entity), other_sprite); } }