/** * \copydoc Detector::notify_action_command_pressed */ bool ShopTreasure::notify_action_command_pressed() { if (get_hero().is_free() && get_keys_effect().get_action_key_effect() == KeysEffect::ACTION_KEY_LOOK) { LuaContext& lua_context = get_lua_context(); lua_context.notify_shop_treasure_interaction(*this); return true; } return false; }
/** * @brief Attachs the hookshot to an entity and makes the hero move towards this entity. * @param entity_reached the entity to attach the hookshot to */ void Hookshot::attach_to(MapEntity& entity_reached) { Debug::check_assertion(this->entity_reached == NULL, "The hookshot is already attached to an entity"); this->entity_reached = &entity_reached; clear_movement(); int direction = get_sprite().get_current_direction(); std::string path = " "; path[0] = '0' + (direction * 2); get_hero().set_movement(new PathMovement(path, 192, true, false, false)); }
/** * \brief Starts this state. * \param previous_state The previous state. */ void Hero::HurtState::start(const HeroState* previous_state) { HeroState::start(previous_state); Equipment& equipment = get_equipment(); Sound::play("hero_hurt"); Hero& hero = get_hero(); const uint32_t invincibility_duration = 2000; hero.set_invincible(true, invincibility_duration); get_sprites().set_animation_hurt(); get_sprites().blink(invincibility_duration); if (has_source) { double angle = Geometry::get_angle(source_xy, hero.get_xy()); std::shared_ptr<StraightMovement> movement = std::make_shared<StraightMovement>(false, true); movement->set_max_distance(24); movement->set_speed(120); movement->set_angle(angle); hero.set_movement(movement); } end_hurt_date = System::now() + 200; // See if the script customizes how the hero takes damages. bool handled = get_lua_context().hero_on_taking_damage(get_hero(), damage); if (!handled && damage != 0) { // No customized damage: perform the default calculation. // The level of the tunic reduces the damage, // but we remove at least 1 life point. int life_points = std::max(1, damage / (equipment.get_ability(Ability::TUNIC))); equipment.remove_life(life_points); if (equipment.has_ability(Ability::TUNIC)) { equipment.notify_ability_used(Ability::TUNIC); } } }
/** * \brief Updates this entity. */ void Hookshot::update() { MapEntity::update(); if (is_suspended()) { return; } uint32_t now = System::now(); if (now >= next_sound_date) { Sound::play("hookshot"); next_sound_date = now + 150; } if (entity_reached == nullptr) { if (!going_back) { if (has_to_go_back) { going_back = true; std::shared_ptr<Movement> movement = std::make_shared<TargetMovement>( std::static_pointer_cast<Hero>(get_hero().shared_from_this()), 0, 0, 192, true ); clear_movement(); set_movement(movement); } else if (get_distance(get_hero()) >= 120) { go_back(); } } else if (get_distance(get_hero()) == 0 || (get_movement() != nullptr && get_movement()->is_finished())) { remove_from_map(); get_hero().start_state_from_ground(); } } }
/** * @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 this state. * \param previous_state the previous state */ void Hero::BoomerangState::start(const HeroState* previous_state) { HeroState::start(previous_state); if (get_map().get_entities().is_boomerang_present()) { Hero& hero = get_hero(); hero.set_state(new FreeState(hero)); } else { get_sprites().set_animation_boomerang(tunic_preparing_animation); this->direction_pressed8 = get_commands().get_wanted_direction8(); } }
/** * \brief Updates this state. */ void Hero::HurtState::update() { State::update(); Hero& hero = get_hero(); if (hero.get_movement()->is_finished() || System::now() >= end_hurt_date) { // we have end_hurt_date because the movement may never finish if there is an obstacle hero.clear_movement(); hero.start_state_from_ground(); } }
/** * \brief Draws this state. */ void Hero::TreasureState::draw_on_map() { State::draw_on_map(); const Hero& hero = get_hero(); int x = hero.get_x(); int y = hero.get_y(); const Rectangle &camera_position = get_map().get_camera_position(); treasure.draw(get_map().get_visible_surface(), x - camera_position.get_x(), y - 24 - camera_position.get_y()); }
/** * @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 Starts this state. * \param previous_state the previous state */ void Hero::LiftingState::start(const State* previous_state) { State::start(previous_state); // initialize the entity that will be lifted lifted_item->set_map(get_map()); get_keys_effect().set_action_key_effect(KeysEffect::ACTION_KEY_THROW); get_sprites().set_animation_lifting(); get_sprites().set_lifted_item(lifted_item); get_hero().set_facing_entity(nullptr); get_equipment().notify_ability_used(ABILITY_LIFT); }
/** * \copydoc Detector::notify_action_command_pressed */ bool Crystal::notify_action_command_pressed() { if (get_hero().is_free() && get_keys_effect().get_action_key_effect() == KeysEffect::ACTION_KEY_LOOK ) { get_keys_effect().set_action_key_effect(KeysEffect::ACTION_KEY_NONE); // start a dialog get_game().start_dialog("_crystal", ScopedLuaRef(), ScopedLuaRef()); return true; } return false; }
/** * \brief Updates this state. */ void Hero::HurtState::update() { HeroState::update(); Hero& hero = get_hero(); if ((hero.get_movement() != nullptr && hero.get_movement()->is_finished()) || System::now() >= end_hurt_date) { // The movement may be finished, or the end date may be reached // when there is an obstacle or when there is no movement at all. hero.clear_movement(); hero.start_state_from_ground(); } }
/** * \copydoc Detector::notify_action_command_pressed */ bool Bomb::notify_action_command_pressed() { KeysEffect::ActionKeyEffect effect = get_keys_effect().get_action_key_effect(); if (effect == KeysEffect::ACTION_KEY_LIFT && get_hero().get_facing_entity() == this && get_hero().is_facing_point_in(get_bounding_box())) { get_hero().start_lifting(std::make_shared<CarriedItem>( get_hero(), *this, "entities/bomb", "", 0, explosion_date) ); Sound::play("lift"); remove_from_map(); return true; } return false; }
/** * \brief Updates this state. */ void Hero::LiftingState::update() { State::update(); lifted_item->update(); if (!is_suspended() && !lifted_item->is_being_lifted()) { // the item has finished being lifted Hero& hero = get_hero(); std::shared_ptr<CarriedItem> carried_item = lifted_item; lifted_item = nullptr; // we do not take care of the carried item from this state anymore hero.set_state(new CarryingState(hero, carried_item)); } }
/** * \brief Notifies this state that the hero has just failed to change its * position because of obstacles. */ void Hero::SwordLoadingState::notify_obstacle_reached() { PlayerMovementState::notify_obstacle_reached(); Hero& hero = get_hero(); Detector* facing_entity = hero.get_facing_entity(); if (hero.is_facing_point_on_obstacle() // he is really facing an obstacle && get_wanted_movement_direction8() == get_sprites().get_animation_direction8() // he is trying to move towards the obstacle && (facing_entity == nullptr || !facing_entity->is_sword_ignored())) { // the obstacle allows him to tap with his sword hero.set_state(new SwordTappingState(hero)); } }
/** * @brief Activates this sensor. * * This function is called when the hero overlaps the sensor. * * @param hero the hero */ void Sensor::activate(Hero& hero) { if (!activated_by_hero) { activated_by_hero = true; switch (subtype) { case CUSTOM: // we notify the scripts notifying_script = true; get_map_script().event_hero_on_sensor(get_name()); notifying_script = false; get_hero().reset_movement(); break; case CHANGE_LAYER: // we change the hero's layer get_entities().set_entity_layer(hero, get_layer()); break; case RETURN_FROM_BAD_GROUND: // we indicate to the hero a location to return // after falling into a hole or some other ground get_hero().set_target_solid_ground_coords(get_xy(), get_layer()); break; } } else { if (subtype == CUSTOM && !notifying_script && !get_game().is_suspended()) { notifying_script = true; get_map_script().event_hero_still_on_sensor(get_name()); notifying_script = false; } } }
/** * check_heros_KK * @val: value to compare KK with * * This function, like hero_check_KK_unused, is buggy! * It does not check if the first slot is a valid hero. */ short check_heros_KK(short val) { Bit8u *hero; signed short sum; hero = get_hero(0); /* Orig-BUG: not checked if hero is valid */ sum = host_readbs(hero + 0x47) + host_readbs(hero + 0x48); hero = get_hero(1); /* check class, group and dead status of hero in slot 2*/ if (host_readb(hero + 0x21) && host_readb(hero + 0x87) == ds_readb(CURRENT_GROUP) && (!hero_dead(hero))) { sum += host_readbs(hero + 0x47) + host_readbs(hero + 0x48); } #if !defined(__BORLANDC__) D1_INFO("Pruefe KK der ersten beiden Helden (%d) >= %d: ", sum, val); D1_INFO("%s\n", sum >= val ? "gelungen" : "mislungen"); #endif return (sum >= val) ? 1 : 0; }
/** * \brief Updates this state. */ void Hero::PullingState::update() { State::update(); Hero& hero = get_hero(); if (!is_moving_grabbed_entity()) { int wanted_direction8 = get_commands().get_wanted_direction8(); int opposite_direction8 = (get_sprites().get_animation_direction8() + 4) % 8; // stop pulling if the action key is released or if there is no more obstacle if (!get_commands().is_command_pressed(GameCommand::ACTION) || !hero.is_facing_obstacle()) { hero.set_state(new FreeState(hero)); } // stop pulling the obstacle if the player changes his direction else if (wanted_direction8 != opposite_direction8) { hero.set_state(new GrabbingState(hero)); } // see if the obstacle is an entity that the hero can pull else { Detector* facing_entity = hero.get_facing_entity(); if (facing_entity != nullptr) { if (facing_entity->get_type() == EntityType::BLOCK) { // TODO use dynamic binding hero.try_snap_to_facing_entity(); } if (facing_entity->start_movement_by_hero()) { std::string path = " "; path[0] = path[1] = '0' + opposite_direction8; pulling_movement = std::make_shared<PathMovement>( path, 40, false, false, false ); hero.set_movement(pulling_movement); pulled_entity = facing_entity; pulled_entity->notify_moving_by(hero); } } } } }
/** * \brief Updates this state. */ void Hero::VictoryState::update() { HeroState::update(); if (!finished && System::now() >= end_victory_date) { finished = true; if (!callback_ref.is_empty()) { // The behavior is defined by Lua. callback_ref.clear_and_call("hero victory callback"); } else { // By default, get back to the normal state. Hero& hero = get_hero(); hero.set_state(new FreeState(hero)); } } }
/** * \brief Starts this state. * \param previous_state The previous state. */ void Hero::UsingItemState::start(State* previous_state) { State::start(previous_state); bool interaction = false; Detector* facing_entity = get_hero().get_facing_entity(); if (facing_entity != NULL && !facing_entity->is_being_removed()) { // Maybe the facing entity (e.g. an NPC) accepts an interaction with this // particular item. interaction = facing_entity->interaction_with_item(item_usage.get_item()); } if (!interaction) { // no interaction occurred with the facing entity: use the item normally item_usage.start(); } }
/** * \brief Notifies this state that the hero has just failed to change its * position because of obstacles. */ void Hero::FreeState::notify_obstacle_reached() { PlayerMovementState::notify_obstacle_reached(); Hero& hero = get_hero(); if (hero.is_facing_point_on_obstacle()) { // he is really facing an obstacle uint32_t now = System::now(); if (pushing_direction4 == -1) { start_pushing_date = now + 800; // start animation "pushing" after 800 ms pushing_direction4 = hero.get_animation_direction(); } else if (now >= start_pushing_date) { hero.set_state(new PushingState(hero)); } } }
/** * \brief Starts this state. * \param previous_state the previous state */ void Hero::StairsState::start(const State* previous_state) { State::start(previous_state); // movement int speed = stairs.is_inside_floor() ? 40 : 24; std::string path = stairs.get_path(way); std::shared_ptr<PathMovement> movement = std::make_shared<PathMovement>( path, speed, false, true, false ); // sprites and sound HeroSprites& sprites = get_sprites(); if (carried_item == nullptr) { sprites.set_animation_walking_normal(); } else { sprites.set_lifted_item(carried_item); sprites.set_animation_walking_carrying(); } sprites.set_animation_direction((path[0] - '0') / 2); get_keys_effect().set_action_key_effect(KeysEffect::ACTION_KEY_NONE); Hero& hero = get_hero(); if (stairs.is_inside_floor()) { if (way == Stairs::NORMAL_WAY) { // Towards an upper layer: change the layer now Layer layer = stairs.get_layer(); Debug::check_assertion(layer != LAYER_HIGH, "Invalid stairs layer"); get_entities().set_entity_layer(hero, Layer(layer + 1)); } } else { sprites.set_clipping_rectangle(stairs.get_clipping_rectangle(way)); if (way == Stairs::REVERSE_WAY) { Point dxy = movement->get_xy_change(); int fix_y = 8; if (path[path.size() - 1] == '2') { fix_y *= -1; } hero.set_xy(hero.get_x() - dxy.x, hero.get_y() - dxy.y + fix_y); } } hero.set_movement(movement); }
/** * \brief Updates this state. */ void Hero::SpinAttackState::update() { // check the animation Hero& hero = get_hero(); if (get_sprites().is_animation_finished()) { hero.set_state(new FreeState(hero)); } // check the movement if any if (hero.get_movement() != NULL && hero.get_movement()->is_finished()) { hero.clear_movement(); if (!being_pushed) { // end of a super spin attack hero.set_state(new FreeState(hero)); } } }
/** * \brief Updates this state. */ void Hero::SwordTappingState::update() { State::update(); Hero& hero = get_hero(); if (hero.get_movement() == NULL) { // the hero is not being pushed after hitting an enemy const Rectangle& facing_point = hero.get_facing_point(); if (!get_commands().is_command_pressed(GameCommands::ATTACK) || get_commands().get_wanted_direction8() != get_sprites().get_animation_direction8() || !get_map().test_collision_with_obstacles(hero.get_layer(), facing_point.get_x(), facing_point.get_y(), hero)) { // the sword key has been released, the player has moved or the obstacle is gone if (get_sprites().get_current_frame() >= 5) { // when the animation is ok, stop tapping the wall, go back to loading the sword hero.set_state(new SwordLoadingState(hero)); } } else { // play the sound every 100 ms uint32_t now = System::now(); if (get_sprites().get_current_frame() == 3 && now >= next_sound_date) { Detector* facing_entity = hero.get_facing_entity(); std::string sound_id; if (facing_entity != NULL) { sound_id = facing_entity->get_sword_tapping_sound(); } else { sound_id = "sword_tapping"; } Sound::play(sound_id); next_sound_date = now + 100; } } } else if (hero.get_movement()->is_finished()) { // the hero was pushed by an enemy hero.set_state(new FreeState(hero)); } }
int run_epikong() { t_map map; char *path; if ((path = init_screen()) == NULL) return (EXIT_FAILURE); if (start_music() == EXIT_FAILURE) return (EXIT_FAILURE); if (start_parsing(path, &map) == EXIT_FAILURE) return (EXIT_FAILURE); if (get_monster(&map) == EXIT_FAILURE) return (EXIT_FAILURE); if (get_hero(&map) == EXIT_FAILURE) return (EXIT_FAILURE); if (parse_map(&map) == EXIT_FAILURE) return (EXIT_FAILURE); return (EXIT_SUCCESS); }
/** * \brief Notifies this state that the hero has just changed its * position. */ void Hero::PullingState::notify_position_changed() { if (is_moving_grabbed_entity()) { // if the entity has made more than 8 pixels and is aligned on the grid, // we stop the movement PathMovement* movement = static_cast<PathMovement*>(get_hero().get_movement()); bool horizontal = get_sprites().get_animation_direction() % 2 == 0; bool has_reached_grid = movement->get_total_distance_covered() >= 16 && ((horizontal && pulled_entity->is_aligned_to_grid_x()) || (!horizontal && pulled_entity->is_aligned_to_grid_y())); if (has_reached_grid) { pulled_entity->update(); stop_moving_pulled_entity(); } } }
/** * \brief Notifies this state that the hero has just attacked an enemy. * \param attack the attack * \param victim the enemy just hurt * \param result indicates how the enemy has reacted to the attack (see Enemy.h) * \param killed indicates that the attack has just killed the enemy */ void Hero::SwordTappingState::notify_attacked_enemy(EnemyAttack attack, Enemy& victim, EnemyReaction::Reaction& result, bool killed) { if (result.type != EnemyReaction::IGNORED && attack == ATTACK_SWORD) { if (victim.get_push_hero_on_sword()) { Hero& hero = get_hero(); double angle = Geometry::get_angle(victim.get_x(), victim.get_y(), hero.get_x(), hero.get_y()); StraightMovement* movement = new StraightMovement(false, true); movement->set_max_distance(24); movement->set_speed(120); movement->set_angle(angle); hero.set_movement(movement); get_sprites().set_animation_walking_normal(); } } }
/** * \brief Notifies this state that the action command was just pressed. */ void Hero::FreeState::notify_action_command_pressed() { Hero& hero = get_hero(); if (get_keys_effect().is_action_key_acting_on_facing_entity()) { // action on the facing entity hero.get_facing_entity()->notify_action_command_pressed(); } else if (hero.is_facing_point_on_obstacle()) { // grab an obstacle hero.set_state(new GrabbingState(hero)); } else if (get_equipment().has_ability("run")) { // run hero.start_running(); } }
/** * \brief Makes the hero stop pulling the entity he is facing. * * This function is called while moving the entity, when the * hero or the entity collides with an obstacle or when * the hero's movement is finished. */ void Hero::PullingState::stop_moving_pulled_entity() { Hero& hero = get_hero(); if (pulled_entity != nullptr) { pulled_entity->stop_movement_by_hero(); // the hero may have moved one or several pixels too much // because he moved before the block, not knowing that the block would not follow him int direction4 = get_sprites().get_animation_direction(); switch (direction4) { case 0: // east hero.set_x(pulled_entity->get_x() - 16); break; case 1: // north hero.set_y(pulled_entity->get_y() + 16); break; case 2: // west hero.set_x(pulled_entity->get_x() + 16); break; case 3: // south hero.set_y(pulled_entity->get_y() - 16); break; } hero.clear_movement(); pulling_movement = nullptr; MapEntity* entity_just_moved = pulled_entity; pulled_entity = nullptr; entity_just_moved->notify_moved_by(hero); } hero.set_state(new GrabbingState(hero)); }