/** * \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; }
void process_request(int fd) { if (server.clients[fd] == NULL) { return; } buffer* in_buf = server.clients[fd] -> in_buf; buffer* out_buf = server.clients[fd] -> out_buf; int i, len = buflen(in_buf); int req_len_end_pos = -1; for (i = 0; i < len; i++) { if (in_buf -> buf[i] == SEPARATOR) { req_len_end_pos = i; break; } } if (req_len_end_pos == -1) { return; } int req_len = -1; char tmp; sscanf(in_buf -> buf, "%d%c", &req_len, &tmp); if (len - req_len_end_pos - 1 < req_len) { return; } char op[100], *rest, tmp_buf[1024]; memcpy(tmp_buf, in_buf -> buf + req_len_end_pos + 1, req_len * sizeof(char)); tmp_buf[req_len] = '\0'; sscanf(tmp_buf, "%s", op); rest = tmp_buf + strlen(op) + 1; if (strcmp(op, "GET") == 0) { void* res = get_from_map(server.db, rest); if (res) { append_to_buffer(out_buf, (char*) res); } else { append_to_buffer(out_buf, ""); } } else if (strcmp(op, "SET") == 0) { char key[100], value[100]; sscanf(rest, "%s %s", key, value); free(remove_from_map(server.db, key)); char* kivi_obj = malloc((strlen(value) + 1) * sizeof(char)); memcpy(kivi_obj, value, (strlen(value) + 1) * sizeof(char)); put_in_map(server.db, key, kivi_obj); append_to_buffer(out_buf, "OK"); } else if (strcmp(op, "DEL") == 0) { free(remove_from_map(server.db, rest)); append_to_buffer(out_buf, "OK"); } memcpy(in_buf -> buf, in_buf -> buf + req_len_end_pos + 1 + req_len, (len - req_len_end_pos - 1 - req_len) * sizeof(char)); in_buf -> end_pos -= req_len_end_pos + 1 + req_len; in_buf -> buf[in_buf -> end_pos] = '\0'; }
/** * @brief Updates the item. */ void Destructible::update() { MapEntity::update(); if (suspended) { return; } if (is_being_cut && get_sprite().is_animation_finished()) { if (!features[subtype].can_regenerate) { // remove the item from the map destruction_callback(); remove_from_map(); } else { is_being_cut = false; regeneration_date = System::now() + 10000; } } else if (is_disabled() && System::now() >= regeneration_date && !overlaps(get_hero())) { get_sprite().set_current_animation("regenerating"); is_regenerating = true; regeneration_date = 0; } else if (is_regenerating && get_sprite().is_animation_finished()) { get_sprite().set_current_animation("on_ground"); is_regenerating = false; } }
/** * \brief Updates this entity. */ void Destructible::update() { MapEntity::update(); if (is_suspended()) { return; } if (is_being_cut && get_sprite().is_animation_finished()) { if (!get_can_regenerate()) { // Remove this destructible from the map. remove_from_map(); } else { is_being_cut = false; regeneration_date = System::now() + 10000; } } else if (is_waiting_for_regeneration() && System::now() >= regeneration_date && !overlaps(get_hero())) { get_sprite().set_current_animation("regenerating"); is_regenerating = true; regeneration_date = 0; get_lua_context().destructible_on_regenerating(*this); } else if (is_regenerating && get_sprite().is_animation_finished()) { get_sprite().set_current_animation("on_ground"); is_regenerating = false; } }
/** * @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 Gives the item to the player. */ void Pickable::try_give_item_to_player() { EquipmentItem& item = treasure.get_item(); if (!can_be_picked || given_to_player || get_game().is_dialog_enabled() || !get_hero().can_pick_treasure(item)) { return; } given_to_player = true; remove_from_map(); // play the sound const std::string& sound_id = item.get_sound_when_picked(); if (!sound_id.empty()) { Sound::play(sound_id); } // give the item if (item.get_brandish_when_picked()) { // The treasure is brandished. // on_obtained() will be called after the dialog. get_hero().start_treasure(treasure, ScopedLuaRef()); } else { treasure.give_to_player(); // Call on_obtained() immediately since the treasure is not brandished. get_lua_context().item_on_obtained(item, treasure); get_lua_context().map_on_obtained_treasure(get_map(), treasure); } }
/** * \brief Updates this entity. */ void Explosion::update() { Detector::update(); if (get_sprite().is_animation_finished()) { remove_from_map(); } }
/** * \brief Makes the bomb explode. */ void Bomb::explode() { get_entities().add_entity(std::make_shared<Explosion>( "", get_layer(), get_center_point(), true )); Sound::play("explosion"); remove_from_map(); }
/** * \brief Updates the pickable item. * * This function is called repeatedly by the map. * This is a redefinition of Entity::update() to make * the item blink and then disappear after an amount of time. */ void Pickable::update() { // update the animations and the movement Detector::update(); // update the shadow if (shadow_sprite != nullptr) { shadow_sprite->update(); } shadow_xy.x = get_x(); if (!is_falling()) { shadow_xy.y = get_y(); } if (entity_followed != nullptr && entity_followed->is_being_removed()) { if (entity_followed->get_type() == EntityType::BOOMERANG || entity_followed->get_type() == EntityType::HOOKSHOT) { // The pickable may have been dropped by the boomerang/hookshot // not exactly on the hero so let's fix this. if (get_distance(get_hero()) < 16) { try_give_item_to_player(); } } entity_followed = nullptr; } check_bad_ground(); if (!is_suspended()) { // check the timer uint32_t now = System::now(); // wait 0.7 second before allowing the hero to take the item if (!can_be_picked && now >= allow_pick_date) { can_be_picked = true; falling_height = FALLING_NONE; get_hero().check_collision_with_detectors(); } else { // make the item blink and then disappear if (will_disappear) { if (now >= blink_date && !get_sprite().is_blinking() && entity_followed == nullptr) { set_blinking(true); } if (now >= disappear_date) { remove_from_map(); } } } } }
/** * \brief Updates this entity. */ void Fire::update() { Detector::update(); if (get_sprite().is_animation_finished()) { remove_from_map(); } else { check_collision_with_detectors(true); } }
/** * \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. */ void Destructible::notify_action_command_pressed() { KeysEffect::ActionKeyEffect effect = get_keys_effect().get_action_key_effect(); if ((effect == KeysEffect::ACTION_KEY_LIFT || effect == KeysEffect::ACTION_KEY_LOOK) && features[subtype].can_be_lifted && !is_being_cut && !is_disabled() && !is_regenerating) { int weight = features[subtype].weight; if (get_equipment().has_ability("lift", weight)) { uint32_t explosion_date = can_explode() ? System::now() + 6000 : 0; get_hero().start_lifting(new CarriedItem( get_hero(), *this, get_animation_set_id(), get_destruction_sound_id(), get_damage_on_enemies(), explosion_date) ); // play the sound Sound::play("lift"); // create the pickable item create_pickable(); // remove the item from the map if (!features[subtype].can_regenerate) { destruction_callback(); remove_from_map(); } else { // the item can actually regenerate play_destroy_animation(); } } else { if (features[subtype].can_be_cut && !features[subtype].can_explode && !get_equipment().has_ability("sword", 1)) { get_game().start_dialog("_cannot_lift_should_cut", LUA_REFNIL); } else if (!get_equipment().has_ability("lift", 1)) { get_game().start_dialog("_cannot_lift_too_heavy", LUA_REFNIL); } else { get_game().start_dialog("_cannot_lift_still_too_heavy", LUA_REFNIL); } } } }
/** * \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; }
/** * \copydoc Entity::notify_ground_below_changed */ void Block::notify_ground_below_changed() { Ground ground = get_ground_below(); switch (ground) { case Ground::HOLE: Sound::play("jump"); remove_from_map(); break; case Ground::LAVA: case Ground::DEEP_WATER: Sound::play("splash"); remove_from_map(); break; default: break; } }
/** * \copydoc Entity::notify_action_command_pressed */ bool Npc::notify_action_command_pressed() { Hero& hero = get_hero(); if (hero.is_free() && get_commands_effects().get_action_key_effect() != CommandsEffects::ACTION_KEY_NONE ) { CommandsEffects::ActionKeyEffect effect = get_commands_effects().get_action_key_effect(); get_commands_effects().set_action_key_effect(CommandsEffects::ACTION_KEY_NONE); SpritePtr sprite = get_sprite(); // if this is a usual NPC, look towards the hero if (subtype == USUAL_NPC) { int direction = (get_hero().get_animation_direction() + 2) % 4; if (sprite != nullptr) { sprite->set_current_direction(direction); } } if (effect != CommandsEffects::ACTION_KEY_LIFT) { // start the normal behavior if (behavior == BEHAVIOR_DIALOG) { get_game().start_dialog(dialog_to_show, ScopedLuaRef(), ScopedLuaRef()); } else { call_script_hero_interaction(); } return true; } else { // lift the entity if (get_equipment().has_ability(Ability::LIFT)) { std::string animation_set_id = "stopped"; if (sprite != nullptr) { animation_set_id = sprite->get_animation_set_id(); } hero.start_lifting(std::make_shared<CarriedObject>( hero, *this, animation_set_id, "stone", 2, 0) ); Sound::play("lift"); remove_from_map(); return true; } } } return false; }
/** * \brief This function is called when the movement of the entity is finished. */ void Boomerang::notify_movement_finished() { if (!is_going_back()) { // the maximum distance is reached go_back(); } else { // the boomerang is back remove_from_map(); } }
/** * @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); } } } } }
/** * \copydoc Detector::notify_action_command_pressed */ bool Destructible::notify_action_command_pressed() { KeysEffect::ActionKeyEffect effect = get_keys_effect().get_action_key_effect(); if ((effect == KeysEffect::ACTION_KEY_LIFT || effect == KeysEffect::ACTION_KEY_LOOK) && get_weight() != -1 && !is_being_cut && !is_waiting_for_regeneration() && !is_regenerating) { if (get_equipment().has_ability(Ability::LIFT, get_weight())) { uint32_t explosion_date = get_can_explode() ? System::now() + 6000 : 0; get_hero().start_lifting(std::make_shared<CarriedItem>( get_hero(), *this, get_animation_set_id(), get_destruction_sound(), get_damage_on_enemies(), explosion_date) ); // Play the sound. Sound::play("lift"); // Create the pickable treasure. create_treasure(); if (!get_can_regenerate()) { // Remove this destructible from the map. remove_from_map(); } else { // The item can actually regenerate. play_destroy_animation(); } // Notify Lua. get_lua_context().destructible_on_lifting(*this); } else { // Cannot lift the object. get_hero().start_grabbing(); get_lua_context().destructible_on_looked(*this); } return true; } return false; }
/** * \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 lifts the bomb if possible. */ void 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(new CarriedItem(get_hero(), *this, "entities/bomb", "", 0, explosion_date)); Sound::play("lift"); remove_from_map(); } }
/** * \brief This function is called when a destructible item detects a non-pixel perfect collision with this entity. * \param destructible the destructible item * \param collision_mode the collision mode that detected the event */ void Arrow::notify_collision_with_destructible( Destructible& destructible, CollisionMode collision_mode) { if (destructible.is_obstacle_for(*this) && is_flying()) { if (destructible.get_can_explode()) { destructible.explode(); remove_from_map(); } else { attach_to(destructible); } } }
/** * \brief Notifies this entity that it has just attacked an enemy. * * This function is called even if this attack was not successful. * * \param attack the attack * \param victim the enemy just hurt * \param result indicates how the enemy has reacted to the attack * \param killed indicates that the attack has just killed the enemy */ void Arrow::notify_attacked_enemy(EnemyAttack attack, Enemy& victim, EnemyReaction::Reaction& result, bool killed) { if (result.type == EnemyReaction::PROTECTED) { stop(); attach_to(victim); } else if (result.type != EnemyReaction::IGNORED) { if (killed) { remove_from_map(); } else { attach_to(victim); } } }
/** * \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(); } } }
/** * \copydoc Entity::notify_action_command_pressed */ bool Bomb::notify_action_command_pressed() { CommandsEffects::ActionKeyEffect effect = get_commands_effects().get_action_key_effect(); if (effect == CommandsEffects::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<CarriedObject>( get_hero(), *this, "entities/bomb", "", 0, explosion_date) ); Sound::play("lift"); remove_from_map(); return true; } return false; }
void Pickable::notify_collision(MapEntity& entity_overlapping, CollisionMode collision_mode) { if(entity_overlapping.is_hero() && pickable) { remove_from_map(); hero_pick_item(); } }
/** * @brief Makes the bomb explode. */ void Bomb::explode() { get_entities().add_entity(new Explosion(get_layer(), get_center_point(), true)); Sound::play("explosion"); remove_from_map(); }
/** * \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 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; } }
/** * \brief Updates the enemy. */ void Enemy::update() { MapEntity::update(); if (is_suspended() || !is_enabled()) { return; } uint32_t now = System::now(); if (being_hurt) { // see if we should stop the animation "hurt" if (now >= stop_hurt_date) { being_hurt = false; set_movement_events_enabled(true); if (life <= 0) { kill(); } else if (is_immobilized()) { clear_movement(); set_animation("immobilized"); notify_immobilized(); } else { clear_movement(); restart(); } } } if (life > 0 && invulnerable && now >= vulnerable_again_date && !being_hurt) { invulnerable = false; } if (life > 0 && !can_attack && !is_immobilized() && can_attack_again_date != 0 && now >= can_attack_again_date) { can_attack = true; } if (is_immobilized() && !is_killed() && now >= end_shaking_date && get_sprite().get_current_animation() == "shaking") { restart(); } if (is_immobilized() && !is_killed() && !is_being_hurt() && now >= start_shaking_date && get_sprite().get_current_animation() != "shaking") { end_shaking_date = now + 2000; set_animation("shaking"); } if (exploding) { uint32_t now = System::now(); if (now >= next_explosion_date) { // create an explosion Rectangle xy; xy.set_x(get_top_left_x() + Random::get_number(get_width())); xy.set_y(get_top_left_y() + Random::get_number(get_height())); get_entities().add_entity(new Explosion("", LAYER_HIGH, xy, false)); Sound::play("explosion"); next_explosion_date = now + 200; nb_explosions++; if (nb_explosions >= 15) { exploding = false; } } } if (is_killed() && is_dying_animation_finished()) { // Create the pickable treasure if any. get_entities().add_entity(Pickable::create(get_game(), "", get_layer(), get_x(), get_y(), treasure, FALLING_HIGH, false)); // Remove the enemy. remove_from_map(); // Notify Lua that this enemy is dead. // We need to do this after remove_from_map() so that this enemy is // considered dead in functions like map:has_entities(prefix). notify_dead(); } get_lua_context().enemy_on_update(*this); }
/** * \brief Updates this entity. */ void Arrow::update() { MapEntity::update(); if (is_suspended()) { return; } uint32_t now = System::now(); // stop the movement if necessary (i.e. stop() was called) if (stop_now) { clear_movement(); stop_now = false; if (entity_reached != NULL) { // the arrow just hit an entity (typically an enemy) and this entity may have a movement Rectangle dxy(get_x() - entity_reached->get_x(), get_y() - entity_reached->get_y()); set_movement(new FollowMovement(entity_reached, dxy.get_x(), dxy.get_y(), true)); } } if (entity_reached != NULL) { // see if the entity reached is still valid if (is_stopped()) { // the arrow is stopped because the entity that was reached just disappeared disappear_date = now; } else if (entity_reached->get_type() == ENTITY_DESTRUCTIBLE && !entity_reached->is_obstacle_for(*this)) { disappear_date = now; } else if (entity_reached->get_type() == ENTITY_ENEMY && ((Enemy*) entity_reached)->is_dying()) { // the enemy is dying disappear_date = now; } } // see if the arrow just hit a wall or an entity bool reached_obstacle = false; if (get_sprite().get_current_animation() != "reached_obstacle") { if (entity_reached != NULL) { // the arrow was just attached to an entity reached_obstacle = true; } else if (is_stopped()) { if (has_reached_map_border()) { // the map border was reached: destroy the arrow disappear_date = now; } else { // the arrow has just hit another obstacle reached_obstacle = true; } } } if (reached_obstacle) { // an obstacle or an entity was just reached disappear_date = now + 1500; get_sprite().set_current_animation("reached_obstacle"); Sound::play("arrow_hit"); if (entity_reached == NULL) { clear_movement(); } check_collision_with_detectors(false); } // destroy the arrow when disappear_date is reached if (now >= disappear_date) { remove_from_map(); } }