/** * @brief Sets the x speed. * @param x_speed the x speed of the object in pixels per second */ void StraightMovement::set_x_speed(double x_speed) { if (std::fabs(x_speed) <= 1E-6) { x_speed = 0; } this->x_speed = x_speed; uint32_t now = System::now(); // compute x_delay, x_move and next_move_date_x if (x_speed == 0) { x_move = 0; } else { if (x_speed > 0) { x_delay = (uint32_t) (1000 / x_speed); x_move = 1; } else { x_delay = (uint32_t) (1000 / (-x_speed)); x_move = -1; } set_next_move_date_x(now + x_delay); } angle = Geometry::get_angle(0, 0, (int) (x_speed * 100), (int) (y_speed * 100)); initial_xy.set_xy(get_xy()); finished = false; if (get_entity() != NULL) { get_entity()->notify_movement_changed(); } }
/** * \brief Updates this state. */ void Hero::BoomerangState::update() { State::update(); Hero& hero = get_entity(); if (hero.is_animation_finished()) { if (direction_pressed8 == -1) { // the player can press the diagonal arrows before or after the boomerang key direction_pressed8 = get_commands().get_wanted_direction8(); } int boomerang_direction8; if (direction_pressed8 == -1 || direction_pressed8 % 2 == 0) { boomerang_direction8 = get_sprites().get_animation_direction() * 2; } else { boomerang_direction8 = direction_pressed8; } double angle = Geometry::degrees_to_radians(boomerang_direction8 * 45); get_entities().add_entity(std::make_shared<Boomerang>( std::static_pointer_cast<Hero>(get_entity().shared_from_this()), max_distance, speed, angle, sprite_name )); hero.set_state(new FreeState(hero)); } }
void StraightMovement::set_speed_y(double speed_yy) { if(std::fabs(speed_yy) <= 1E-6) { speed_yy = 0; } this->speed_y = speed_yy; uint32_t now = System::now(); // compute delay_x, move_x, next_move_date_x. if(speed_y > 0) { this->delay_y = (uint32_t) (1000 / this->speed_y); move_y = 1; set_next_move_date_y(now + delay_y); } else if(speed_y < 0) { this->delay_y = (uint32_t) (-1000 / this->speed_y); move_y = -1; set_next_move_date_y(now + delay_y); } else { move_y = 0; } this->angle = Geometry::get_angle(0.f, 0.f, speed_x * 100, speed_y * 100); start_xy.set_xy(get_xy()); this->finished = false; if(get_entity() != NULL) { get_entity()->notify_movement_changed(); } }
/** * Tries to move the top-left corner of the entity towards an intersection of the 8*8 grid of the map. * This function is called only when the entity is not already aligned to the grid. */ void PathMovement::snap() { // calculate the coordinates of the closest grid intersection from the top-left corner of the entity int x = get_entity()->get_top_left_x(); int y = get_entity()->get_top_left_y(); int snapped_x = x + 4; int snapped_y = y + 4; snapped_x -= snapped_x % 8; snapped_y -= snapped_y % 8; uint32_t now = System::now(); if (!snapping) { // if we haven't started to move the entity towards an intersection of the grid, do it now set_snapping_trajectory(Rectangle(x, y), Rectangle(snapped_x, snapped_y)); snapping = true; stop_snapping_date = now + 500; // timeout } else { // the entity is currently trying to move towards the closest grid intersection uint32_t now = System::now(); if (now >= stop_snapping_date) { // we could not snap the entity after the timeout: // this is possible when there is an (unlikely) collision with an obstacle that is not aligned to the grid // (typically a statue that is being moved); // let's try the opposite grid intersection instead snapped_x += (snapped_x < x) ? 8 : -8; snapped_y += (snapped_y < y) ? 8 : -8; set_snapping_trajectory(Rectangle(x, y), Rectangle(snapped_x, snapped_y)); stop_snapping_date = now + 500; } } }
/** * \brief Calculates the direction and the speed of the movement * depending on the target. */ void PathFindingMovement::recompute_movement() { if (target != nullptr) { PathFinding path_finding(get_entity()->get_map(), *get_entity(), *target); std::string path = path_finding.compute_path(); uint32_t min_delay; if (path.size() == 0) { // the target is too far or there is no path path = create_random_path(); // no path was found: no need to try again very soon // (note that the A* algorithm is very costly when it explores all nodes without finding a solution) min_delay = 3000; } else { // a path was found: we need to update it frequently (and the A* algorithm is much faster in general when there is a solution) min_delay = 300; } // compute a new path every random delay to avoid // having all path-finding entities of the map compute a path at the same time next_recomputation_date = System::now() + min_delay + Random::get_number(200); set_path(path); } }
void AvatarGameObj::update_geom_offsets() { // Update the orientation of our physical geom to match _uprightness dMatrix3 grot; dRFromAxisAndAngle(grot, 1, 0, 0, _uprightness*M_PI_2); dGeomSetOffsetRotation(get_entity().get_geom("physical"), grot); // Update the position of the sticky_attach geom to match _uprightness dGeomSetOffsetPosition(get_entity().get_geom("sticky_attach"), 0, -sin(_uprightness*M_PI_2) + RUNNING_MAX_DELTA_Y_POS, 0); }
void StraightMovement::set_angle(double angle) { double speed = get_speed(); set_speed_x(speed * std::cos(angle)); set_speed_y(-speed * std::sin(angle)); this->angle = angle; if(get_entity() != NULL) { get_entity()->notify_movement_changed(); } }
/** * \brief Determines the direction defined by the directional keys currently pressed * and computes the corresponding movement. */ void PlayerMovement::set_wanted_direction() { if (get_entity() != NULL && get_entity()->is_on_map()) { GameCommands& commands = get_entity()->get_game().get_commands(); direction8 = commands.get_wanted_direction8(); } else { direction8 = -1; } }
void StraightMovement::stop() { double old_angle = this->angle; set_speed_x(0); set_speed_y(0); this->move_x = 0; this->move_y = 0; this->angle = old_angle; if(get_entity() != NULL) { get_entity()->notify_movement_finished(); } }
/** * @brief Sets the speed to zero. */ void StraightMovement::stop() { double old_angle = this->angle; set_x_speed(0); set_y_speed(0); x_move = 0; y_move = 0; this->angle = old_angle; if (get_entity() != NULL) { get_entity()->notify_movement_changed(); } }
/** * @brief Changes the direction of the movement vector, keeping the same speed. * * x_speed and y_speed are recomputed so that the total speed is unchanged. * Warning: if x_speed and y_speed are both equal to zero, this function * stops the program on an error message. * * @param angle the new movement direction in radians */ void StraightMovement::set_angle(double angle) { if (!is_stopped()) { double speed = get_speed(); set_x_speed(speed * std::cos(angle)); set_y_speed(-speed * std::sin(angle)); } this->angle = angle; if (get_entity() != NULL) { get_entity()->notify_movement_changed(); } }
fn void update() { auto root = get_entity(0); if(root->dirty) { root->world_matrix = mat4::compose(root->position, root->scale, root->rotation); } for(i32 i = 1; i < scene::ctx->num_entities; ++i) { auto entity = get_entity(i); update_entity(entity); } }
void StraightMovement::set_speed(double speed) { if(speed_y == 0 && speed_x == 0) { speed_x = 1; } //compute the new speed vector. double old_angle = this->angle; set_speed_x(speed * std::cos(old_angle)); set_speed_y(-speed * std::sin(old_angle)); this->angle = old_angle; if(get_entity() != NULL) { get_entity()->notify_movement_changed(); } }
bool MatchPool::pop(int size_type, MatchRes &res) { std::vector<MatchEntity*> camps; for (int i = 0;i < _camp_size; i++) { MatchEntity *ent = E_NEW MatchEntity; ent->camp = i; camps.push_back(ent); } int success = pop(size_type, camps); // merge result res.type = _type; for (size_t i = 0;i < camps.size(); i++) { TeamSet members; camps[i]->get_members(members); if (!success) { // restore TeamSet::iterator itr = members.begin(); for (;itr != members.end(); ++itr) { MatchEntity *ent = get_entity(*itr); if (ent) { ent->status = MATCH_PENDING; } } } else { res.teams.push_back(members); } S_DELETE(camps[i]); } return success; }
std::string InputOutputException::get_message(ModelObject *o) const { std::ostringstream oss; switch (get_entity()) { case DERIVATIVE: if (o->get_model()->get_stage() == BEFORE_EVALUATING) { oss << "Derivatives cannot be read before evaluating."; break; } default: switch (get_operation()) { case GET: oss << "Not in input list."; break; case SET: case ADD: case REMOVE: oss << "Not in output list."; break; default: // should not exist oss << "Unknown read/write error"; } break; }; oss << " Violating object: \"" << o->get_name() << "\"."; if (particle_index_ >= 0) { oss << " Attribute " << get_key_name() << " of particle \"" << o->get_model() ->get_particle(ParticleIndex(get_particle_index())) ->get_name() << "\" with id " << get_particle_index(); } else { oss << "Container \"" << container_name_ << "\"."; } return oss.str(); }
fn void set_parent(Scene* scene, Entity* entity, Entity* parent) { if(parent->id == entity->parent) return; if(entity->parent != 0) //remove from children { auto current_parent = get_entity(entity->parent); for(i32 i = 0; i < 8; ++i) { auto child = current_parent->children[i]; if(child == entity->id) { child = -1; break; } } } entity->parent = parent->id; for(i32 i = 0; i < 8; ++i) { auto child = parent->children[i]; if(child == -1) { child = entity->id; break; } } }
/** * \brief Changes the name of an entity. * \param index Index of the entity on the map. * \param name The new name. An empty string means no name. * \return \c true in case of success, \c false if the name was already used. */ bool MapData::set_entity_name(const EntityIndex& index, const std::string& name) { EntityData& entity = get_entity(index); const std::string& old_name = entity.get_name(); if (name == old_name) { // Nothing to do. return true; } if (!name.empty()) { if (entity_exists(name)) { // This name is already used by another entity. return false; } } // Do the change. entity.set_name(name); if (!old_name.empty()) { named_entities.erase(old_name); } if (!name.empty()) { named_entities[name] = index; } return false; }
/** * \brief Updates this state. */ void Hero::SwordLoadingState::update() { PlayerMovementState::update(); if (is_suspended() || !is_current_state()) { return; } uint32_t now = System::now(); // detect when the sword is loaded (i.e. ready for a spin attack) if (!sword_loaded && now >= sword_loaded_date) { play_load_sound(); sword_loaded = true; } if (!get_commands().is_command_pressed(GameCommand::ATTACK)) { // the player has just released the sword key // stop loading the sword, go to the normal state or make a spin attack Hero& hero = get_entity(); if (!sword_loaded) { // the sword was not loaded yet: go to the normal state hero.set_state(new FreeState(hero)); } else { // the sword is loaded: release a spin attack hero.set_state(new SpinAttackState(hero)); } } }
/** * \brief Returns control to the hero after its hookshot movement. * * This function is called when the hero has finished the hookshot movement. * It checks the validity of the destination position. */ void Hero::HookshotState::finish_movement() { Hero& hero = get_entity(); const Rectangle& hero_position = hero.get_bounding_box(); int layer = hero.get_layer(); Map& map = get_map(); MapEntities& entities = get_entities(); if (layer == 0 || !map.has_empty_ground(layer, hero_position)) { // the hero is totally on the same layer: no problem hero.start_state_from_ground(); } else { // a part of the hero is on empty tiles: this is often illegal, especially // if there are jumpers; allow this only if tiles on // the lower layer are not obstacles, and go to this layer --layer; if (!map.test_collision_with_obstacles(layer, hero_position, hero)) { Sound::play("hero_lands"); entities.set_entity_layer(hero, layer); hero.start_state_from_ground(); } else { // illegal position: get back to the start point // TODO: get back to the closest valid point from the destination instead Sound::play("hero_hurt"); hero.set_state(new BackToSolidGroundState(hero, false, 0, true)); } } }
/** * \brief Starts this state. * \param previous_state the previous state */ void Hero::SpinAttackState::start(const State* previous_state) { State::start(previous_state); // play the sound play_spin_attack_sound(); // start the animation Hero& hero = get_entity(); if (get_equipment().has_ability(Ability::SWORD_KNOWLEDGE)) { get_sprites().set_animation_super_spin_attack(); std::shared_ptr<CircleMovement> movement = std::make_shared<CircleMovement>(false); movement->set_center(hero.get_xy()); movement->set_radius_speed(128); movement->set_radius(24); movement->set_angle_speed(540); movement->set_max_rotations(3); movement->set_clockwise(true); hero.set_movement(movement); } else { get_sprites().set_animation_spin_attack(); } }
/** * \brief Stops this state. * \param next_state the next state */ void Hero::JumpingState::stop(const State* next_state) { State::stop(next_state); get_entity().clear_movement(); if (carried_item != nullptr) { switch (next_state->get_previous_carried_item_behavior()) { case CarriedItem::BEHAVIOR_THROW: carried_item->throw_item(get_sprites().get_animation_direction()); get_entities().add_entity(carried_item); carried_item = nullptr; get_sprites().set_lifted_item(nullptr); break; case CarriedItem::BEHAVIOR_DESTROY: carried_item = nullptr; get_sprites().set_lifted_item(nullptr); break; case CarriedItem::BEHAVIOR_KEEP: // The next state is now the owner and has incremented the refcount. carried_item = nullptr; break; default: Debug::die("Invalid carried item behavior"); } } }
/** * \brief Updates this state. */ void Hero::PlungingState::update() { HeroState::update(); if (get_sprites().is_animation_finished()) { Hero& hero = get_entity(); int drown = 0; if (hero.get_ground_below() == Ground::DEEP_WATER) { if (get_equipment().has_ability(Ability::SWIM)) { hero.set_state(new SwimmingState(hero)); } else { drown = 1; } } else if (hero.get_ground_below() == Ground::LAVA) { drown = 4; } else { hero.set_state(new FreeState(hero)); } if (drown > 0) { get_equipment().remove_life(drown); hero.set_state(new BackToSolidGroundState(hero, true, 300)); } } }
/** * \brief Tests whether the hero is cutting with his sword the specified detector * for which a collision was detected. * \param detector the detector to check * \return true if the sword is cutting this detector */ bool Hero::RunningState::is_cutting_with_sword(Detector& detector) { // check the distance to the detector const int distance = 8; Point tested_point = get_entity().get_facing_point(); switch (get_sprites().get_animation_direction()) { case 0: // right tested_point.x += distance; break; case 1: // up tested_point.y -= distance; break; case 2: // left tested_point.x -= distance; break; case 3: // down tested_point.y += distance; break; } return detector.overlaps(tested_point); }
/** * \brief Returns whether a teletransporter is considered as an obstacle in this state. * \param teletransporter a teletransporter * \return true if the teletransporter is an obstacle in this state */ bool Hero::SpinAttackState::is_teletransporter_obstacle( const Teletransporter& /* teletransporter */) const { // if the hero is pushed by an enemy or making a super spin attack, // don't go on a teletransporter return get_entity().get_movement() != nullptr; }
/** * \brief Updates the position. */ void PathFindingMovement::update() { PathMovement::update(); if (target != nullptr && target->is_being_removed()) { target = nullptr; } if (is_suspended()) { return; } if (PathMovement::is_finished()) { // there was a collision or the path was made if (target != nullptr && System::now() >= next_recomputation_date && get_entity()->is_aligned_to_grid()) { recompute_movement(); } else { set_path(create_random_path()); } } }
/** * \copydoc Entity::State::notify_attacked_enemy */ void Hero::SpinAttackState::notify_attacked_enemy( EnemyAttack attack, Enemy& victim, const Sprite* victim_sprite, EnemyReaction::Reaction& result, bool /* killed */) { Hero& hero = get_entity(); if (result.type != EnemyReaction::ReactionType::IGNORED && attack == EnemyAttack::SWORD) { if (victim.get_push_hero_on_sword()) { if (hero.get_movement() != nullptr) { // interrupting a super spin attack: finish with a normal one hero.clear_movement(); get_sprites().set_animation_spin_attack(); } being_pushed = true; double angle = victim.get_angle(hero, victim_sprite, nullptr); 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); } } }
/** * @brief Computes the position of the object controlled by this movement. * * This function should be called whenever the angle, the radius or the center changes. */ void CircleMovement::recompute_position() { Rectangle center = this->center_point; if (center_entity != NULL) { center.add_xy(center_entity->get_xy()); } const Rectangle &xy = Geometry::get_xy(center, Geometry::degrees_to_radians(current_angle), current_radius); if (get_entity() == NULL || !test_collision_with_obstacles(xy.get_x() - get_entity()->get_x(), xy.get_y() - get_entity()->get_y())) { set_xy(xy); notify_position_changed(); } else { notify_obstacle_reached(); } }
/** * \brief Notifies this state that a directional key was just pressed. * \param direction4 direction of the key (0 to 3) */ void Hero::RunningState::notify_direction_command_pressed(int direction4) { if (!is_bouncing() && direction4 != get_sprites().get_animation_direction()) { Hero& hero = get_entity(); hero.set_state(new FreeState(hero)); } }
/** * \brief Stops this state. */ void Hero::RunningState::stop(const State* next_state) { State::stop(next_state); if (phase != 0) { get_entity().clear_movement(); } }
/** * \brief Starts this state. * \param previous_state the previous state */ void Hero::HookshotState::start(const State* previous_state) { State::start(previous_state); get_sprites().set_animation("hookshot"); hookshot = std::make_shared<Hookshot>(get_entity()); get_entities().add_entity(hookshot); }