/** * @brief Makes the door immediately open or closed. * @param door_open true to make it opened, false to make it closed. */ void Door::set_open(bool door_open) { state = door_open ? OPEN : CLOSED; if (door_open) { set_collision_modes(COLLISION_NONE); // to avoid being the hero's facing entity } else { get_sprite().set_current_animation("closed"); set_collision_modes(COLLISION_FACING_POINT | COLLISION_SPRITE); // ensure that we are not closing the door on the hero if (is_on_map() && overlaps(get_hero())) { get_hero().avoid_collision(*this, (get_direction() + 2) % 4); } } if (is_on_map()) { update_dynamic_tiles(); if (is_saved()) { get_savegame().set_boolean(savegame_variable, door_open); } if (door_open) { get_lua_context().door_on_opened(*this); } else { get_lua_context().door_on_closed(*this); } } }
/** * \brief Constructor. * \param name Name of the teletransporter. * \param layer Layer of the teletransporter. * \param xy Coordinates where to create the entity. * \param size Size of the teletransporter's rectangle. * \param sprite_name Sprite animation set id to use, or an empty string. * \param sound_id Sound to play when using the teletransporter, * or an empty string. * \param transition_style Style of transition between the two maps. * \param destination_map_id Id of the destination map. * \param destination_name Location on the destination map, * or "_same" to keep the hero's coordinates, * or "_side" to place the hero on the appropriate side of the map. * An empty string means the default destination entity of the map. */ Teletransporter::Teletransporter( const std::string& name, int layer, const Point& xy, const Size& size, const std::string& sprite_name, const std::string& sound_id, Transition::Style transition_style, const std::string& destination_map_id, const std::string& destination_name): Entity(name, 0, layer, xy, size), sound_id(sound_id), transition_style(transition_style), destination_map_id(destination_map_id), destination_name(destination_name), destination_side(-1), transition_direction(0), transporting_hero(false) { set_collision_modes(CollisionMode::COLLISION_CUSTOM); if (!sprite_name.empty()) { create_sprite(sprite_name); } }
/** * \brief Creates a block. * \param name name identifying this block * \param layer layer of the entity to create * \param xy Coordinate of the entity to create. * \param direction the only direction where the block can be moved * or -1 to allow it to be pushed in any direction * \param sprite_name animation set id of the sprite for this block * \param can_be_pushed true to allow the hero to push this block * \param can_be_pulled true to allow the hero to pull this block * \param maximum_moves indicates how many times the block can * be moved (0: none, 1: once, 2: infinite) */ Block::Block( const std::string& name, int layer, const Point& xy, int direction, const std::string& sprite_name, bool can_be_pushed, bool can_be_pulled, int maximum_moves ): Entity(name, direction, layer, xy, Size(16, 16)), maximum_moves(maximum_moves), sound_played(false), when_can_move(System::now()), last_position(xy), initial_position(xy), initial_maximum_moves(maximum_moves), can_be_pushed(can_be_pushed), can_be_pulled(can_be_pulled) { Debug::check_assertion(maximum_moves >= 0 && maximum_moves <= 2, "maximum_moves must be between 0 and 2"); set_collision_modes(CollisionMode::COLLISION_FACING); set_origin(8, 13); set_direction(direction); const SpritePtr& sprite = create_sprite(sprite_name); set_drawn_in_y_order(sprite->get_size().height > 16); }
/** * \brief Constructor. * * Creates a bomb. * * \param name Name identifying the entity on the map or an empty string. * \param layer layer of the entity to create * \param xy Coordinates of the entity to create. */ Bomb::Bomb(const std::string& name, int layer, const Point& xy): Entity(name, 0, layer, xy, Size(16, 16)), explosion_date(System::now() + 6000) { set_collision_modes(CollisionMode::COLLISION_FACING); const SpritePtr& sprite = create_sprite("entities/bomb"); sprite->enable_pixel_collisions(); set_size(16, 16); set_origin(8, 13); set_drawn_in_y_order(true); }
/** * \brief Kills the enemy. * * This function is called when the enemy has no more health points. */ void Enemy::kill() { // if the enemy is immobilized, give some money if (rank == RANK_NORMAL && is_immobilized() && !treasure.is_saved()) { // TODO choose random money (can we do this from scripts?) } // stop any movement and disable attacks set_collision_modes(COLLISION_NONE); clear_movement(); invulnerable = true; can_attack = false; can_attack_again_date = 0; dying_animation_started = true; if (hurt_style == HURT_BOSS) { // A boss: create some explosions. exploding = true; nb_explosions = 0; next_explosion_date = System::now() + 2000; } else { // Replace the enemy sprites. clear_sprites(); switch (get_ground_below()) { case GROUND_HOLE: // TODO animation of falling into a hole. Sound::play("jump"); break; case GROUND_DEEP_WATER: // TODO water animation. Sound::play("splash"); break; case GROUND_LAVA: // TODO lava animation. Sound::play("splash"); break; default: create_sprite("enemies/enemy_killed"); Sound::play("enemy_killed"); break; } } // save the enemy state if required if (is_saved()) { get_savegame().set_boolean(savegame_variable, true); } }
/** * \brief Sets the appropriate collisions modes. * * This depends on whether the object is an obstacle, can be cut * or can explode. */ void Destructible::update_collision_modes() { // Reset previous collision modes. set_collision_modes(0); // Sets the new ones. if (get_modified_ground() == Ground::WALL) { // The object is an obstacle. // Set the facing collision mode to allow the hero to look at it. add_collision_mode(COLLISION_FACING); } if (get_can_be_cut() || get_can_explode()) { add_collision_mode(COLLISION_SPRITE); } }
/** * \brief Creates an NPC. * \param game the game * \param name name identifying this NPC * \param layer layer of the entity to create * \param xy Coordinates of the entity to create. * \param subtype the subtype of interaction * \param sprite_name sprite animation set of the entity, or an empty string to create no sprite * \param direction for a generalized NPC: direction for which the interactions are allowed * (0 to 4, or -1 for any direction), for a usual NPC: initial direction of the NPC's sprite * \param behavior_string indicates what happens when the hero interacts with this NPC: * "dialog#XXX" to start the dialog XXX, "map" to call the map script * (with an event_hero_interaction() call) or "item#XXX" to call the script * of item XXX (with an event_hero_interaction() call) */ Npc::Npc( Game& /* game */, const std::string& name, int layer, const Point& xy, Subtype subtype, const std::string& sprite_name, int direction, const std::string& behavior_string ): Entity(name, 0, layer, xy, Size(0, 0)), subtype(subtype), behavior(BEHAVIOR_MAP_SCRIPT), traversable(false), dialog_to_show(""), item_name("") { set_collision_modes(CollisionMode::COLLISION_FACING | CollisionMode::COLLISION_OVERLAPPING); initialize_sprite(sprite_name, direction); set_size(16, 16); set_origin(8, 13); set_direction(direction); // Usual NPCs are displayed like the hero whereas generalized NPCs are // not necessarily people. set_drawn_in_y_order(subtype == USUAL_NPC); // behavior if (behavior_string == "map") { behavior = BEHAVIOR_MAP_SCRIPT; } else if (behavior_string.substr(0, 5) == "item#") { behavior = BEHAVIOR_ITEM_SCRIPT; item_name = behavior_string.substr(5); } else if (behavior_string.substr(0, 7) == "dialog#") { behavior = BEHAVIOR_DIALOG; dialog_to_show = behavior_string.substr(7); } else { Debug::die(std::string("Invalid behavior string for NPC '") + name + "': '" + behavior_string + "'"); } }
/** * \brief Creates a new crystal block. * \param game The current game. * \param name Name identifying the entity on the map or an empty string. * \param layer Layer of the entity to create on the map. * \param xy Coordinates of the entity to create. * \param size Size of the block (the pattern can be repeated). * \param subtype subtype of raised block */ CrystalBlock::CrystalBlock(Game& game, const std::string& name, int layer, const Point& xy, const Size& size, Subtype subtype): Entity(name, 0, layer, xy, size), subtype(subtype) { set_collision_modes(CollisionMode::COLLISION_OVERLAPPING); Sprite& sprite = *create_sprite("entities/crystal_block"); // Don't pause the sprite animation when the crystal block is far from the // camera. Otherwise it looks weird if the players comes back using a // teletransporter. sprite.set_ignore_suspend(true); this->orange_raised = game.get_crystal_state(); if (subtype == ORANGE) { sprite.set_current_animation(orange_raised ? "orange_raised" : "orange_lowered"); } else { sprite.set_current_animation(orange_raised ? "blue_lowered" : "blue_raised"); } sprite.set_current_frame(sprite.get_nb_frames() - 1); // to avoid the animations at the map beginning }
/** * \brief Adds a collision mode to this detector. * \param collision_mode the collision mode to add */ void Detector::add_collision_mode(CollisionMode collision_mode) { set_collision_modes(this->collision_modes | collision_mode); }
/** * \brief Unregisters all collision test functions. */ void CustomEntity::clear_collision_tests() { // Disable all collisions checks. collision_tests.clear(); set_collision_modes(COLLISION_FACING); }