/** * @brief Updates this entity. */ void Hookshot::update() { MapEntity::update(); if (suspended) { return; } uint32_t now = System::now(); if (now >= next_sound_date) { Sound::play("hookshot"); next_sound_date = now + 150; } if (entity_reached == NULL) { if (!going_back) { if (has_to_go_back) { going_back = true; Movement *movement = new TargetMovement(&get_hero(), 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() != NULL && get_movement()->is_finished())) { remove_from_map(); get_hero().start_state_from_ground(); } } }
/** * \brief This function is called when the entity has just moved. * * If it is an NPC, its sprite's direction is updated. */ void Npc::notify_position_changed() { Entity::notify_position_changed(); if (subtype == USUAL_NPC) { const SpritePtr& sprite = get_sprite(); if (get_movement() != nullptr) { // The NPC is moving. if (sprite != nullptr) { if (sprite->get_current_animation() != "walking") { sprite->set_current_animation("walking"); } int direction4 = get_movement()->get_displayed_direction4(); sprite->set_current_direction(direction4); } } if (get_hero().get_facing_entity() == this && get_commands_effects().get_action_key_effect() == CommandsEffects::ACTION_KEY_SPEAK && !get_hero().is_facing_point_in(get_bounding_box())) { get_commands_effects().set_action_key_effect(CommandsEffects::ACTION_KEY_NONE); } } }
/** * \brief This function is called repeatedly. */ void Bomb::update() { Detector::update(); if (is_suspended()) { return; } // check the explosion date uint32_t now = System::now(); if (now >= explosion_date) { explode(); } else if (now >= explosion_date - 1500 && get_sprite().get_current_animation() != "stopped_explosion_soon") { get_sprite().set_current_animation("stopped_explosion_soon"); } // destroy the movement once finished if (get_movement() != nullptr && get_movement()->is_finished()) { clear_movement(); } // check collision with explosions, streams, etc. check_collision_with_detectors(); }
/** * \brief Returns whether the arrow has just hit the map border. * \return true if the arrow has just hit the map border */ bool Arrow::has_reached_map_border() const { if (get_sprite().get_current_animation() != "flying" || get_movement() == NULL) { return false; } return get_map().test_collision_with_border(get_movement()->get_last_collision_box_on_obstacle()); }
char movement( int* x, int* y ) { char direction = get_movement(); if (direction == '\033') { getch(); // skip [ direction = getch(); } if (direction == '6' || direction == 'C') { *x = *x+1; } else if (direction == '4' || direction == 'D') { *x = *x-1; } else if (direction == '2' || direction == 'A') { *y = *y+1; } else if (direction == '8' || direction == 'B') { *y = *y-1; } return direction; }
/** * @brief This function is called when the player tries to push or pull this block. * @return true if the player is allowed to move this block */ bool Block::start_movement_by_hero() { Hero& hero = get_hero(); bool pulling = hero.is_grabbing_or_pulling(); int allowed_direction = get_direction(); int hero_direction = hero.get_animation_direction(); if (pulling) { // the movement direction is backwards hero_direction = (hero_direction + 2) % 4; } if (get_movement() != NULL // the block is already moving || maximum_moves == 0 // the block cannot move anymore || System::now() < when_can_move // the block cannot move for a while || (pulling && !can_be_pulled) // the hero tries to pull a block that cannot be pulled || (!pulling && !can_be_pushed) // the hero tries to push a block that cannot be pushed || (allowed_direction != -1 && hero_direction != allowed_direction)) { // incorrect direction return false; } int dx = get_x() - hero.get_x(); int dy = get_y() - hero.get_y(); set_movement(new FollowMovement(&hero, dx, dy, false)); sound_played = false; return true; }
/** * \brief Suspends or resumes the animation. * * Nothing is done if the parameter specified does not change. * * \param suspended true to suspend the animation, false to resume it */ void Sprite::set_suspended(bool suspended) { if (suspended != this->suspended && !ignore_suspend) { this->suspended = suspended; // compte next_frame_date if the animation is being resumed if (!suspended) { uint32_t now = System::now(); next_frame_date = now + get_frame_delay(); blink_next_change_date = now; } else { blink_is_sprite_visible = true; } // Also suspend or resumed the transition effect and the movement if any. Transition* transition = get_transition(); if (transition != NULL) { transition->set_suspended(suspended); } Movement* movement = get_movement(); if (movement != NULL) { movement->set_suspended(suspended); } } }
/** * \brief Destroys the item while it is being thrown. */ void CarriedItem::break_item() { if (is_throwing && throwing_direction != 3) { // destroy the item where it is actually drawn set_y(get_y() - item_height); } get_movement()->stop(); if (!can_explode()) { if (!destruction_sound_id.empty()) { Sound::play(destruction_sound_id); } if (get_sprite().has_animation("destroy")) { get_sprite().set_current_animation("destroy"); } else { remove_from_map(); } } else { get_entities().add_entity(std::make_shared<Explosion>( "", get_layer(), get_xy(), true )); Sound::play("explosion"); if (is_throwing) { remove_from_map(); // because if the item was still carried by the hero, then the hero class will destroy it } } is_throwing = false; is_breaking = true; }
/** * @brief This function is called when the movement of the entity is finished. */ void CustomEnemy::notify_movement_finished() { Enemy::notify_movement_finished(); if (!is_being_hurt()) { script->event_movement_finished(*get_movement()); } }
/** * \brief Notifies the block that it has just moved. */ void Block::notify_position_changed() { Entity::notify_position_changed(); // Now we know that the block moves at least of 1 pixel: // we can play the sound. if (get_movement() != nullptr && !sound_played) { Sound::play("hero_pushes"); sound_played = true; } }
/** * \brief Returns whether the hero is currently considered as an obstacle by this entity. * \param hero the hero * \return true if the hero is an obstacle for this entity. */ bool Block::is_hero_obstacle(Hero& hero) { // The block is not an obstacle when the hero is already overlapping it, // which is easily possible with blocks created dynamically. if (hero.overlaps(*this)) { return false; } // When the block is moved by the hero, one pixel can overlap. return get_movement() == nullptr; }
/** * @brief Notifies this entity that it has just failed to change its position * because of obstacles. */ void Hookshot::notify_obstacle_reached() { if (is_flying()) { if (!get_map().test_collision_with_border( get_movement()->get_last_collision_box_on_obstacle())) { // play a sound unless the obstacle is the map border Sound::play("sword_tapping"); } go_back(); } }
/** * @brief Starts a movement handled by this script and removes it from the list of unassigned movements. * * This function is called when the movement is assigned to an object. * * @param movement_handle handle of the movement * @return the corresponding movement */ Movement& Script::start_movement(int movement_handle) { Movement &movement = get_movement(movement_handle); if (unassigned_movements.count(movement_handle) > 0) { // the movemnt is still stored by the script: detach it movement.set_suspended(false); unassigned_movements.erase(movement_handle); } return movement; }
/** * @brief Resets the block at its initial position. */ void Block::reset() { if (get_movement() != NULL) { // the block was being pushed or pulled by the hero clear_movement(); when_can_move = System::now() + moving_delay; } set_xy(initial_position); last_position.set_xy(initial_position); this->maximum_moves = initial_maximum_moves; }
/** * \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 Resets the block at its initial position. */ void Block::reset() { if (get_movement() != nullptr) { // the block was being pushed or pulled by the hero clear_movement(); when_can_move = System::now() + moving_delay; } last_position = initial_position; this->maximum_moves = initial_maximum_moves; set_xy(initial_position); notify_position_changed(); }
/** * \brief Notifies the block that it has just moved. */ void Block::notify_position_changed() { // now we know that the block moves at least of 1 pixel: // we can play the sound if (get_movement() != NULL && !sound_played) { Sound::play("hero_pushes"); sound_played = true; } check_collision_with_detectors(false); update_ground_below(); if (are_movement_notifications_enabled()) { get_lua_context().entity_on_position_changed(*this, get_xy(), get_layer()); } }
bool InvisibleBlock::collides(GameObject& other, const CollisionHit& ) const { if(visible) return true; // if we're not visible, only register a collision if this will make us visible auto player = dynamic_cast<Player*> (&other); if ((player) && (player->get_movement().y <= 0) && (player->get_bbox().get_top() > get_bbox().get_bottom() - SHIFT_DELTA)) { return true; } return false; }
/** * \brief This function is called when a conveyor belt detects a collision with this entity. * \param conveyor_belt a conveyor belt * \param dx direction of the x move in pixels (0, 1 or -1) * \param dy direction of the y move in pixels (0, 1 or -1) */ void Bomb::notify_collision_with_conveyor_belt(ConveyorBelt& conveyor_belt, int dx, int dy) { if (get_movement() == NULL) { // check that a significant part of the bomb is on the conveyor belt Rectangle center = get_center_point(); center.add_xy(-1, -1); center.set_size(2, 2); if (conveyor_belt.overlaps(center)) { set_xy(conveyor_belt.get_xy()); std::string path = " "; path[0] = path[1] = '0' + conveyor_belt.get_direction(); clear_movement(); set_movement(new PathMovement(path, 64, false, false, false)); } } }
/** * \brief Destroys the item after it finishes its thrown movement. * * How the item breaks depends on the ground where it lands. */ void CarriedItem::break_item_on_ground() { get_movement()->stop(); Ground ground = get_ground_below(); switch (ground) { case Ground::EMPTY: // Nothing here: fall one layer below. { int layer = get_layer(); if (layer == 0) { // Cannot fall lower. break_item(); } else { get_entities().set_entity_layer(*this, layer - 1); break_item_on_ground(); // Do this again on the next layer. } break; } case Ground::HOLE: Sound::play("jump"); remove_from_map(); break; case Ground::DEEP_WATER: case Ground::LAVA: Sound::play("walk_on_water"); remove_from_map(); break; default: // Break the item normally. break_item(); break; } is_throwing = false; is_breaking = true; }
HitResponse Kugelblitz::collision_player(Player& player, const CollisionHit& ) { if(player.is_invincible()) { explode(); return ABORT_MOVE; } // hit from above? if(player.get_movement().y - get_movement().y > 0 && player.get_bbox().p2.y < (get_bbox().p1.y + get_bbox().p2.y) / 2) { // if it's not is it possible to squish us, then this will hurt if(!collision_squished(player)) player.kill(false); explode(); return FORCE_MOVE; } player.kill(false); explode(); return FORCE_MOVE; }
/** * \copydoc MapEntity::notify_collision_with_stream */ void Bomb::notify_collision_with_stream(Stream& stream, int /* dx */, int /* dy */) { if (get_movement() == nullptr) { // TODO use a StreamAction, since it now works with any entity and not only the hero. // Check that a significant part of the bomb is on the stream. Rectangle center(get_center_point(), Size(2, 2)); center.add_xy(-1, -1); if (stream.overlaps(center)) { set_xy(stream.get_xy()); std::string path = " "; path[0] = path[1] = '0' + stream.get_direction(); clear_movement(); set_movement(std::make_shared<PathMovement>( path, 64, false, false, false )); } } }
/* -------------------------------------------------------------------- */ int fpc1020_input_task(fpc1020_data_t* fpc1020) { u8 zones; #ifdef DEBUG_TIME ktime_t capture_start_time; #endif bool isReverse = false; int dx = 0; int dy = 0; int dy_bak = 0; int sumX = 0; int sumY = 0; int error = 0; unsigned char *prevBuffer = NULL; unsigned char *curBuffer = NULL; unsigned long diffTime = 0; fpc1020_dynamic_debug("%s %d\n", __func__, __LINE__); //set_cpus_allowed_ptr(current, cpumask_of(3)); error = fpc1020_write_nav_setup(fpc1020); while (!fpc1020->worker.stop_request && fpc1020->nav.enabled && (error >= 0)) { fpc1020_dynamic_debug("\n%s %d\n", __func__, __LINE__); error = fpc1020_capture_nav_wait_finger_down(fpc1020); if (error < 0) { break; } error = fpc1020_write_nav_setup(fpc1020); if (error < 0) { break; } error = fpc1020_check_finger_present_raw(fpc1020); process_navi_event(fpc1020, 0, 0, FNGR_ST_DETECTED); fpc1020->nav.click_start = jiffies; zones = fpc1020_subzrea_zones_sum(error); fpc1020->nav.detect_zones = zones; fpc1020_dynamic_debug("[FPC] report touch zones=%d\n", fpc1020->nav.detect_zones); #ifdef DEBUG_TIME capture_start_time = ktime_get(); #endif // DEBUG_TIME #ifdef FPC1020_NAV_HEAP_BUF error = capture_nav_image(fpc1020); #else error = fpc1020_capture_buffer(fpc1020, p_prev_img, 0, NAV_IMG_SIZE); #endif /*FPC1020_NAV_HEAP_BUF*/ #ifdef DEBUG_TIME dev_dbg(&fpc1020->spi->dev, "Navigation, init capture time: %lld ns\n", (ktime_get().tv64 - capture_start_time.tv64)); #endif // DEBUG_TIME if (error < 0) { break; } #ifdef FPC1020_NAV_HEAP_BUF memcpy(fpc1020->prev_img_buf, fpc1020->huge_buffer, NAV_IMG_SIZE); #endif while (!fpc1020->worker.stop_request && (error >= 0)) { fpc1020_dynamic_debug("\n%s %d\n", __func__, __LINE__); hrtimer_start(&fpc1020->finger_up_timer, ktime_set(0, 50000000), HRTIMER_MODE_REL); error = fpc1020_check_finger_present_sum(fpc1020); if (error < fpc1020->setup.capture_finger_up_threshold + 1) { #ifdef FPC1020_NAV_DBG pr_info("[FPC] prepare report finger up\n"); #endif process_navi_event(fpc1020, 0, 0, FNGR_ST_LOST); fpc1020->nav.throw_event = 0; fpc1020->nav.report_keys = 0; sumX = 0; sumY = 0; fpc1020->nav.time = 0; isReverse = false; break; } #ifdef FPC1020_NAV_HEAP_BUF if (isReverse) { prevBuffer = fpc1020->cur_img_buf; curBuffer = fpc1020->prev_img_buf; } else { prevBuffer = fpc1020->prev_img_buf; curBuffer = fpc1020->cur_img_buf; } #else u8* p_tmp_curr_img = p_curr_img; p_curr_img = p_prev_img; p_prev_img = p_tmp_curr_img; #endif #ifdef DEBUG_TIME capture_start_time = ktime_get(); #endif // DEBUG_TIME #ifdef FPC1020_NAV_HEAP_BUF error = capture_nav_image(fpc1020); #else error = fpc1020_capture_buffer(fpc1020, p_curr_img, 0, NAV_IMG_SIZE); #endif //fpc1020_debug_store_image(p_curr_img, NAV_IMG_W, NAV_IMG_H); #ifdef DEBUG_TIME dev_dbg(&fpc1020->spi->dev, "Navigation, capture time: %lld ns\n", (ktime_get().tv64 - capture_start_time.tv64)); #endif if (error < 0) { break; } #ifdef FPC1020_NAV_HEAP_BUF memcpy(curBuffer, fpc1020->huge_buffer, NAV_IMG_SIZE); #endif //fpc1020_debug_store_image(fpc1020->cur_img_buf, NAV_IMG_W, NAV_IMG_H); error = fpc1020_check_finger_present_sum(fpc1020); fpc1020_dynamic_debug("%s %d\n", __func__, __LINE__); if (error < fpc1020->setup.capture_finger_up_threshold + 1) { #ifdef FPC1020_NAV_DBG pr_info("[FPC] prepare report finger up\n"); #endif process_navi_event(fpc1020, 0, 0, FNGR_ST_LOST); fpc1020->nav.throw_event = 0; fpc1020->nav.report_keys = 0; sumX = 0; sumY = 0; fpc1020->nav.time = 0; isReverse = false; break; } #ifdef FPC1020_NAV_HEAP_BUF isReverse = !isReverse; #endif #ifdef DEBUG_TIME capture_start_time = ktime_get(); #endif // DEBUG_TIME #ifdef FPC1020_NAV_HEAP_BUF //preempt_disable(); get_movement(prevBuffer, curBuffer, &dx, &dy); //preempt_enable(); #else //preempt_disable(); get_movement(p_prev_img, p_curr_img, &dx, &dy); //preempt_enable(); #endif #ifdef DEBUG_TIME dev_dbg(&fpc1020->spi->dev, "Navigation, caculate time: %lld ns\n", (ktime_get().tv64 - capture_start_time.tv64)); #endif if (dx!=0 && dy!=0) { fpc1020->nav.poll_filter++; } fpc1020->nav.poll_counter++; sumX += dx; #if 1 if ((dy_bak > 0 && dy < 0) || (dy_bak < 0 && dy > 0)) sumY = 0; #endif sumY += dy; if(dx > 48 || dx < -48) dx = 0; if(dy > 48 || dy < -48) dy = 0; dy_bak = dy; fpc1020_dynamic_debug("[FPC] get_movement dx=%d, dy=%d, sumx=%d, sumy=%d\n", dx, dy, sumX, sumY); diffTime = abs(jiffies - fpc1020->nav.time); if (diffTime > 0) { diffTime = diffTime * 1000000 / HZ; if (diffTime >= FPC1020_INPUT_POLL_INTERVAL) { #ifdef FPC1020_NAV_DBG pr_info("[FPC] move poll \n"); #endif if (fpc1020->nav.poll_counter > 1 && fpc1020->nav.poll_filter == 0) process_navi_event(fpc1020, sumX, sumY, FNGR_ST_MOVING); else process_navi_event(fpc1020, 0, 0, FNGR_ST_MOVING); fpc1020->nav.poll_counter = 0; fpc1020->nav.poll_filter = 0; sumX = 0; sumY = 0; fpc1020->nav.time = jiffies; } } } } if (error < 0) { dev_err(&fpc1020->spi->dev, "%s %s (%d)\n\n", __func__, (error == -EINTR) ? "TERMINATED" : "FAILED", error); } fpc1020->nav.enabled = false; hrtimer_cancel(&fpc1020->finger_up_timer); fpc1020_dynamic_debug("\n%s %d\n", __func__, __LINE__); return error; }
/** * \brief Returns whether the hero is currently considered as an obstacle by this entity. * \param hero the hero * \return true if the hero is an obstacle for this entity. */ bool Block::is_hero_obstacle(const Hero& hero) const { return get_movement() == NULL; }
/** * \brief This function is called repeatedly. */ void CarriedItem::update() { // update the sprite and the position Entity::update(); if (is_suspended()) { return; } // when the hero finishes lifting the item, start carrying it if (is_lifting && get_movement()->is_finished()) { is_lifting = false; // make the item follow the hero clear_movement(); set_movement(std::make_shared<RelativeMovement>( std::static_pointer_cast<Hero>(hero.shared_from_this()), 0, -18, true )); } // when the item has finished flying, destroy it else if (can_explode() && !is_breaking) { uint32_t now = System::now(); if (now >= explosion_date) { break_item(); } else if (will_explode_soon()) { std::string animation = get_sprite().get_current_animation(); if (animation == "stopped") { get_sprite().set_current_animation("stopped_explosion_soon"); } else if (animation == "walking") { get_sprite().set_current_animation("walking_explosion_soon"); } } } if (is_broken()) { remove_from_map(); } else if (is_throwing) { shadow_sprite->update(); if (break_one_layer_above) { break_item(); int layer = get_layer(); if (layer != get_map().get_highest_layer()) { get_entities().set_entity_layer(*this, layer + 1); } break_one_layer_above = false; } else if (get_movement()->is_stopped() || y_increment >= 7) { // Interrupt the movement. break_item_on_ground(); } else { uint32_t now = System::now(); while (now >= next_down_date) { next_down_date += 40; item_height -= y_increment; y_increment++; } } } }
/** * @brief This function is called repeatedly. */ void CarriedItem::update() { // update the sprite and the position MapEntity::update(); if (suspended) { return; } // when the hero finishes lifting the item, start carrying it if (is_lifting && get_movement()->is_finished()) { is_lifting = false; // make the item follow the hero clear_movement(); set_movement(new FollowMovement(&hero, 0, -18, true)); } // when the item has finished flying, destroy it else if (can_explode() && !is_breaking) { uint32_t now = System::now(); if (now >= explosion_date) { break_item(); } else if (will_explode_soon()) { std::string animation = get_sprite().get_current_animation(); if (animation == "stopped") { get_sprite().set_current_animation("stopped_explosion_soon"); } else if (animation == "walking") { get_sprite().set_current_animation("walking_explosion_soon"); } } } if (is_throwing) { shadow_sprite->update(); if (is_broken()) { remove_from_map(); } else if (break_on_intermediate_layer) { break_item(); get_entities().set_entity_layer(*this, LAYER_INTERMEDIATE); break_on_intermediate_layer = false; } else if (get_movement()->is_stopped() || y_increment >= 7) { break_item(); } else { uint32_t now = System::now(); while (now >= next_down_date) { next_down_date += 40; item_height -= y_increment; y_increment++; } } } }
/** * @brief Returns whether the hero is currently considered as an obstacle by this entity. * @param hero the hero * @return true if the hero is an obstacle for this entity. */ bool Block::is_hero_obstacle(Hero& hero) { return get_movement() == NULL; }
/** * \brief Returns whether the arrow is stopped. * \return true if the arrow is stopped */ bool Arrow::is_stopped() const { return get_movement() == NULL || get_movement()->is_finished(); }
/** * \brief Reacts to the ground of the pickable. * * It is removed it is on water, lava or a hole. * It goes to the lower layer if the ground is empty. */ void Pickable::check_bad_ground() { if (is_being_removed()) { // Be silent if the pickable was already removed by a script. return; } if (get_entity_followed() != nullptr) { // We are attached to a hookshot or boomerang: don't fall. return; } if (get_y() < shadow_xy.y) { // The pickable is above the ground for now, let it fall first. return; } if (get_movement() != nullptr && !get_movement()->is_finished()) { // The falling movement is not finished yet. return; } if (System::now() <= appear_date + 200) { // The pickable appeared very recently, let the user see it for // a short time at least. return; } Ground ground = get_ground_below(); switch (ground) { case Ground::EMPTY: { // Fall to a lower layer. int layer = get_layer(); if (layer > 0) { --layer; get_entities().set_entity_layer(*this, layer); } } break; case Ground::HOLE: { Sound::play("jump"); remove_from_map(); } break; case Ground::DEEP_WATER: case Ground::LAVA: { Sound::play("splash"); remove_from_map(); } break; default: break; } }