/** * @brief Notifies this detector that the player is interacting with it by * pressing the action command. * * This function is called when the player presses the action command * while the hero is facing this detector, and the action command effect lets * him do this. * The hero opens the door if possible, otherwise a message is shown. */ void Door::notify_action_command_pressed() { if (get_hero().is_free() && is_closed()) { if (can_open()) { Sound::play("door_unlocked"); Sound::play("door_open"); if (is_saved()) { get_savegame().set_boolean(savegame_variable, true); } if (is_opening_condition_consumed()) { consume_opening_condition(); } set_opening(); get_hero().check_position(); } else if (!cannot_open_dialog_id.empty()) { Sound::play("wrong"); get_dialog_box().start_dialog(cannot_open_dialog_id); } } }
/** * @brief Updates the entity. */ void Door::update() { Detector::update(); if (!initialized) { update_dynamic_tiles(); initialized = true; } if (is_closed() && get_opening_method() == OPENING_BY_EXPLOSION && get_equipment().has_ability("detect_weak_walls") && Geometry::get_distance(get_center_point(), get_hero().get_center_point()) < 40 && !is_suspended() && System::now() >= next_hint_sound_date) { Sound::play("cane"); next_hint_sound_date = System::now() + 500; } if (is_changing() && get_sprite().is_animation_finished()) { // Toggle door_open when the changing animation finishes. set_open(is_opening()); } if (is_saved() && !is_changing()) { bool open_in_savegame = get_savegame().get_boolean(savegame_variable); if (open_in_savegame && is_closed()) { set_opening(); } else if (!open_in_savegame && is_open()) { set_closing(); } } }
/** * \brief Saves the low-level keyboard command where the specified game key is * mapped. * \param command A game command. * \param keyboard_key The keyboard key to map to this game command in the * savegame. */ void GameCommands::set_saved_keyboard_binding( Command command, InputEvent::KeyboardKey keyboard_key) { const std::string& savegame_variable = get_keyboard_binding_savegame_variable(command); const std::string& keyboard_key_name = InputEvent::get_keyboard_key_name(keyboard_key); get_savegame().set_string(savegame_variable, keyboard_key_name); }
/** * \brief Determines from the savegame the low-level keyboard key where the * specified game command is mapped. * \param command A game command. * \return The keyboard key mapped to this game command in the savegame. */ InputEvent::KeyboardKey GameCommands::get_saved_keyboard_binding( GameCommand command) const { const std::string& savegame_variable = get_keyboard_binding_savegame_variable(command); const std::string& keyboard_key_name = get_savegame().get_string(savegame_variable); return name_to_enum(keyboard_key_name, InputEvent::KEY_NONE); }
/** * @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 Updates the chest. * * This function is called repeatedly by the map. * This is a redefinition of MapEntity::update() * the handle the chest opening. */ void Chest::update() { if (is_open() && !is_suspended()) { if (!treasure_given && treasure_date != 0 && System::now() >= treasure_date) { treasure_date = 0; treasure.ensure_obtainable(); // Make the chest empty if the treasure is not allowed. if (!treasure.is_empty()) { // Give a treasure to the player. get_hero().start_treasure(treasure, ScopedLuaRef()); treasure_given = true; } else { // The chest is empty. if (treasure.is_saved()) { // Mark the treasure as found in the savegame. get_savegame().set_boolean(treasure.get_savegame_variable(), true); } treasure_given = true; bool done = get_lua_context().chest_on_empty(*this); if (!done) { // The script does not define any behavior: // by default, do nothing and unfreeze the hero. get_hero().start_free(); } } } } MapEntity::update(); }
static int a_save(int index, int subindex) { if (index == M_INIT) { b_loadgame(save); b_loadsave(savemenu[2], save[0]); b_loadsave(savemenu[3], save[1]); b_loadsave(savemenu[4], save[2]); backoff = shopmenu; return 0; } // Player wants to save a game. switch (index) { case 2: case 3: case 4: // Save game. save[index-2] = *get_savegame(); b_updatesave(save[index-2]); b_savegame(save); b_loadsave(savemenu[index], save[index-2], "saved"); break; case 5: // Back to main menu; buildmenu(shopmenu); break; } return 0; }
/** * \brief Determines from the savegame the low-level keyboard key where the * specified game command is mapped. * \param command A game command. * \return The keyboard key mapped to this game command in the savegame. */ InputEvent::KeyboardKey GameCommands::get_saved_keyboard_binding( Command command) const { const std::string& savegame_variable = get_keyboard_binding_savegame_variable(command); const std::string& keyboard_key_name = get_savegame().get_string(savegame_variable); return InputEvent::get_keyboard_key_by_name(keyboard_key_name); }
/** * \brief Ends this game. */ void Game::stop() { if (current_map != NULL && current_map->is_started()) { current_map->leave(); } get_lua_context().game_on_finished(*this); get_savegame().notify_game_finished(); started = false; }
/** * \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 Updates the entity. */ void ShopItem::update() { if (is_looking_item && !get_game().is_dialog_enabled()) { // the description message has just finished const std::string question_dialog_id = "_shop.question"; get_dialog_box().start_dialog(question_dialog_id); get_dialog_box().set_variable(question_dialog_id, price); is_asking_question = true; is_looking_item = false; } else if (is_asking_question && !get_game().is_dialog_enabled()) { // the question has just finished is_asking_question = false; int answer = get_dialog_box().get_last_answer(); if (answer == 0) { // the player wants to buy the item Equipment& equipment = get_equipment(); EquipmentItem& item = treasure.get_item(); if (equipment.get_money() < price) { // not enough rupees Sound::play("wrong"); get_dialog_box().start_dialog("_shop.not_enough_money"); } else if (item.has_amount() && item.get_amount() >= item.get_max_amount()) { // the player already has the maximum amount of this item Sound::play("wrong"); get_dialog_box().start_dialog("_shop.amount_full"); } else { bool can_buy = get_lua_context().shop_item_on_buying(*this); if (can_buy) { // give the treasure equipment.remove_money(price); get_hero().start_treasure(treasure, LUA_REFNIL); if (treasure.is_saved()) { remove_from_map(); get_savegame().set_boolean(treasure.get_savegame_variable(), true); } get_lua_context().shop_item_on_bought(*this); } } } } }
/** * @brief Starts opening the door and plays the corresponding animations. * * Nothing is done if the door is already in the process of being open. */ void Door::open() { if (is_open() || is_opening()) { // The door is already open or being open: nothing to do. return; } set_opening(); if (is_saved()) { get_savegame().set_boolean(savegame_variable, true); } }
/** * @brief Starts closing the door and plays the corresponding animations. * * Nothing is done if the door is already in the process of being closed. */ void Door::close() { if (is_closed() || is_closing()) { // The door is already closed or being closed: nothing to do. return; } set_closing(); if (is_saved()) { get_savegame().set_boolean(savegame_variable, false); } }
/** * @brief Returns whether the player is able to open this chest now. * @return true if this is a small chest or if the player has the big key. */ bool Chest::can_open() { switch (get_opening_method()) { case OPENING_BY_INTERACTION: // No condition: the hero can always open the chest. return true; case OPENING_BY_INTERACTION_IF_SAVEGAME_VARIABLE: { // The hero can open the chest if a savegame variable is set. const std::string& required_savegame_variable = get_opening_condition(); if (required_savegame_variable.empty()) { return false; } Savegame& savegame = get_savegame(); if (savegame.is_boolean(required_savegame_variable)) { return savegame.get_boolean(required_savegame_variable); } if (savegame.is_integer(required_savegame_variable)) { return savegame.get_integer(required_savegame_variable) > 0; } if (savegame.is_string(required_savegame_variable)) { return !savegame.get_string(required_savegame_variable).empty(); } return false; } case OPENING_BY_INTERACTION_IF_ITEM: { // The hero can open the chest if he has an item. const std::string& required_item_name = get_opening_condition(); if (required_item_name.empty()) { return false; } const EquipmentItem& item = get_equipment().get_item(required_item_name); return item.is_saved() && item.get_variant() > 0 && (!item.has_amount() || item.get_amount() > 0); } default: return false; } }
/** * @brief Consumes the savegame variable or the equipment item that was required * to open the door. */ void Door::consume_opening_condition() { switch (get_opening_method()) { case OPENING_BY_INTERACTION_IF_SAVEGAME_VARIABLE: { // Reset or decrement the savegame variable that was required. const std::string& required_savegame_variable = get_opening_condition(); Savegame& savegame = get_savegame(); if (!required_savegame_variable.empty()) { if (savegame.is_boolean(required_savegame_variable)) { savegame.set_boolean(required_savegame_variable, false); } else if (savegame.is_integer(required_savegame_variable)) { int current_value = savegame.get_integer(required_savegame_variable); savegame.set_integer(required_savegame_variable, current_value - 1); } else if (savegame.is_string(required_savegame_variable)) { savegame.set_string(required_savegame_variable, ""); } } break; } case OPENING_BY_INTERACTION_IF_ITEM: { // Remove the equipment item that was required. if (!opening_condition.empty()) { EquipmentItem& item = get_equipment().get_item(opening_condition); if (item.is_saved() && item.get_variant() > 0) { if (item.has_amount()) { item.set_amount(item.get_amount() - 1); } else { item.set_variant(0); } } } break; } default: // Ignored. break; } }
/** * @brief Updates the chest. * * This function is called repeatedly by the map. * This is a redefinition of MapEntity::update() * the handle the chest opening. */ void Chest::update() { if (is_open() && !suspended) { if (!treasure_given && treasure_date != 0 && System::now() >= treasure_date) { treasure_date = 0; if (!treasure.is_empty()) { // give a treasure to the player get_hero().start_treasure(treasure, LUA_REFNIL); treasure_given = true; } else { // the chest is empty // mark the treasure as found in the savegame if (treasure.is_saved()) { get_savegame().set_boolean(treasure.get_savegame_variable(), true); } treasure_given = true; bool done = get_lua_context().chest_on_empty(*this); if (!done) { // the script does not define any behavior: // by default, we tell the player the chest is empty Sound::play("wrong"); get_dialog_box().start_dialog("_empty_chest"); get_hero().start_free(); } } } } MapEntity::update(); }
/** * @brief Updates the chest. * * This function is called repeatedly by the map. * This is a redefinition of MapEntity::update() * the handle the chest opening. */ void Chest::update() { if (is_open() && !suspended) { if (!treasure_given && treasure_date != 0 && System::now() >= treasure_date) { treasure_date = 0; if (treasure.get_item_name() != "_none") { // give a treasure to the player get_hero().start_treasure(treasure); treasure_given = true; } else { // the chest is empty // mark the treasure as found in the savegame int savegame_variable = treasure.get_savegame_variable(); if (savegame_variable != -1) { get_savegame().set_boolean(savegame_variable, true); } treasure_given = true; if (!get_map_script().event_chest_empty(get_name())) { // the script does not define any behavior: // by default, we tell the player the chest is empty Sound::play("wrong"); get_dialog_box().start_dialog("_empty_chest"); get_hero().start_free(); } } } } MapEntity::update(); }
/** * \brief Saves the low-level joypad action where the specified game command * is mapped. * \param command A game command. * \return The joypad action to map to this game command in the savegame. */ void GameCommands::set_saved_joypad_binding( Command command, const std::string& joypad_string) { const std::string& savegame_variable = get_joypad_binding_savegame_variable(command); get_savegame().set_string(savegame_variable, joypad_string); }
/** * \brief Determines from the savegame the low-level joypad action where the * specified game command is mapped. * \param command A game command. * \return The joypad action mapped to this game command in the savegame. */ const std::string& GameCommands::get_saved_joypad_binding( Command command) const { const std::string& savegame_variable = get_joypad_binding_savegame_variable(command); return get_savegame().get_string(savegame_variable); }
/** * \brief Starts this game. */ void Game::start() { started = true; get_savegame().notify_game_started(); get_lua_context().game_on_started(*this); get_lua_context().hero_on_state_changed(get_hero(), get_hero().get_state_name()); }
/** * \brief Handles the transitions. * * This functions changes the map when needed and plays the * transitions between the two maps. This function is called * by the update() function. * Note that the two maps can actually be the same. */ void Game::update_transitions() { if (transition != NULL) { transition->update(); } // if the map has just changed, close the current map if any and play an out transition if (next_map != NULL && transition == NULL) { // the map has changed (i.e. set_current_map has been called) if (current_map == NULL) { // special case: no map was playing, so we don't have any out transition to do current_map = next_map; next_map = NULL; } else { // normal case: stop the control and play an out transition before leaving the current map transition = Transition::create( transition_style, Transition::TRANSITION_CLOSING, current_map->get_visible_surface(), this); transition->start(); } } Rectangle previous_map_location = current_map->get_location(); // if a transition was playing and has just been finished if (transition != NULL && transition->is_finished()) { Transition::Direction transition_direction = transition->get_direction(); bool needs_previous_surface = transition->needs_previous_surface(); delete transition; transition = NULL; MainLoop& main_loop = get_main_loop(); if (restarting) { current_map->unload(); main_loop.set_game(new Game(main_loop, savegame)); RefCountable::unref(savegame); savegame = NULL; // The new game is the owner. } else if (transition_direction == Transition::TRANSITION_CLOSING) { if (next_map == current_map) { // same map hero->place_on_destination(*current_map, previous_map_location); transition = Transition::create( transition_style, Transition::TRANSITION_OPENING, current_map->get_visible_surface(), this); transition->start(); next_map = NULL; } else { // change the map current_map->leave(); // special treatments for a transition between two different worlds // (e.g. outside world to a dungeon) if (!next_map->has_world() || next_map->get_world() != current_map->get_world()) { // reset the crystal blocks crystal_state = false; // Save the location except if this is a special destination. const std::string& destination_name = next_map->get_destination_name(); if (destination_name != "_same" && destination_name.substr(0,5) != "_side") { get_savegame().set_string(Savegame::KEY_STARTING_MAP, next_map->get_id()); get_savegame().set_string(Savegame::KEY_STARTING_POINT, destination_name); } } // before closing the map, draw it on a backup surface for transition effects // that want to display both maps at the same time if (needs_previous_surface) { previous_map_surface = Surface::create( Video::get_quest_size() ); previous_map_surface->set_software_destination(false); RefCountable::ref(previous_map_surface); current_map->draw(); current_map->get_visible_surface().draw(*previous_map_surface); } // set the next map current_map->unload(); RefCountable::unref(current_map); current_map = next_map; next_map = NULL; } } else { current_map->notify_opening_transition_finished(); RefCountable::unref(previous_map_surface); previous_map_surface = NULL; } } // if a map has just been set as the current map, start it and play the in transition if (started && !current_map->is_started()) { Debug::check_assertion(current_map->is_loaded(), "This map is not loaded"); transition = Transition::create( transition_style, Transition::TRANSITION_OPENING, current_map->get_visible_surface(), this); if (previous_map_surface != NULL) { // some transition effects need to display both maps simultaneously transition->set_previous_surface(previous_map_surface); } hero->place_on_destination(*current_map, previous_map_location); transition->start(); current_map->start(); notify_map_changed(); } }
/** * \brief Returns the equipment of the player. * * It is equivalent to get_savegame().get_equipment(). * * \return The equipment. */ const Equipment& Game::get_equipment() const { return get_savegame().get_equipment(); }