예제 #1
0
void MapMode::_UpdateExplore()
{
    // First go to menu mode if the user requested it
    if(_menu_enabled && InputManager->MenuPress()) {
        MenuMode *MM = new MenuMode();
        ModeManager->Push(MM);
        return;
    }

    // Only update the status effect supervisor in Exploration mode
    // and if they are allowed.
    if (_status_effects_enabled)
        _status_effect_supervisor.UpdateEffects();

    // Update the running state of the camera object. Check if the character is running and if so,
    // update the stamina value if the operation is permitted
    _camera->is_running = false;
    if(_camera->moved_position && !_running_disabled && InputManager->CancelState() &&
            (InputManager->UpState() || InputManager->DownState() || InputManager->LeftState() || InputManager->RightState())) {
        if(_unlimited_stamina) {
            _camera->is_running = true;
        } else if(_run_stamina > SystemManager->GetUpdateTime() * 2) {
            _run_stamina -= (SystemManager->GetUpdateTime() * 2);
            _camera->is_running = true;
        } else {
            _run_stamina = 0;
        }
    }
    // Regenerate the stamina at 1/2 the consumption rate
    else if(_run_stamina < 10000) {
        _run_stamina += SystemManager->GetUpdateTime();
        if(_run_stamina > 10000)
            _run_stamina = 10000;
    }

    // If the user requested a confirm event, check if there is a nearby object that the player may interact with
    // Interactions are currently limited to dialogue with sprites and opening of treasures
    if(InputManager->ConfirmPress()) {
        MapObject *obj = _object_supervisor->FindNearestInteractionObject(_camera);

        if(obj != NULL) {
            if(obj->GetType() == PHYSICAL_TYPE) {
                PhysicalObject *phs = reinterpret_cast<PhysicalObject *>(obj);

                if(!phs->GetEventIdWhenTalking().empty()) {
                    _camera->moving = false;
                    _camera->is_running = false;
                    if (!_event_supervisor->IsEventActive(phs->GetEventIdWhenTalking()))
                        _event_supervisor->StartEvent(phs->GetEventIdWhenTalking());
                    return;
                }
            }
            else if(obj->GetType() == SPRITE_TYPE) {
                MapSprite *sp = reinterpret_cast<MapSprite *>(obj);

                if(sp->HasAvailableDialogue()) {
                    _camera->moving = false;
                    _camera->is_running = false;
                    sp->InitiateDialogue();
                    return;
                }
            } else if(obj->GetType() == TREASURE_TYPE) {
                TreasureObject *treasure_object = reinterpret_cast<TreasureObject *>(obj);

                if(!treasure_object->GetTreasure()->IsTaken()) {
                    _camera->moving = false;
                    treasure_object->Open();
                }
            } else if(obj->GetType() == SAVE_TYPE && _save_points_enabled) {
                // Make sure the character will be centered in the save point
                SaveMode *save_mode = new SaveMode(true, obj->GetXPosition(), obj->GetYPosition() - 1.0f);
                ModeManager->Push(save_mode, false, false);
            }
        }
    }

    // Detect movement input from the user
    if(InputManager->UpState() || InputManager->DownState() || InputManager->LeftState() || InputManager->RightState()) {
        _camera->moving = true;
    } else {
        _camera->moving = false;
    }

    // Determine the direction of movement. Priority of movement is given to: up, down, left, right.
    // In the case of diagonal movement, the direction that the sprite should face also needs to be deduced.
    if(_camera->moving == true) {
        if(InputManager->UpState()) {
            if(InputManager->LeftState())
                _camera->SetDirection(MOVING_NORTHWEST);
            else if(InputManager->RightState())
                _camera->SetDirection(MOVING_NORTHEAST);
            else
                _camera->SetDirection(NORTH);
        } else if(InputManager->DownState()) {
            if(InputManager->LeftState())
                _camera->SetDirection(MOVING_SOUTHWEST);
            else if(InputManager->RightState())
                _camera->SetDirection(MOVING_SOUTHEAST);
            else
                _camera->SetDirection(SOUTH);
        } else if(InputManager->LeftState()) {
            _camera->SetDirection(WEST);
        } else if(InputManager->RightState()) {
            _camera->SetDirection(EAST);
        }
    } // if (_camera->moving == true)
} // void MapMode::_UpdateExplore()
void VirtualSprite::_SetNextPosition()
{

    // Next sprite's position holders
    float next_pos_x = GetXPosition();
    float next_pos_y = GetYPosition();
    float distance_moved = CalculateDistanceMoved();

    // Move the sprite the appropriate distance in the appropriate Y and X direction
    if(_direction & (NORTH | MOVING_NORTHWEST | MOVING_NORTHEAST))
        next_pos_y -= distance_moved;
    else if(_direction & (SOUTH | MOVING_SOUTHWEST | MOVING_SOUTHEAST))
        next_pos_y += distance_moved;
    if(_direction & (WEST | MOVING_NORTHWEST | MOVING_SOUTHWEST))
        next_pos_x -= distance_moved;
    else if(_direction & (EAST | MOVING_NORTHEAST | MOVING_SOUTHEAST))
        next_pos_x += distance_moved;

    // When not moving, do not check anything else.
    if(next_pos_x == GetXPosition() && next_pos_y == GetYPosition())
        return;

    // We've got the next position, let's check whether the next position
    // should be revised.

    // Used to know whether we could fall back to a straight move
    // in case of collision.
    bool moving_diagonally = (_direction & (MOVING_NORTHWEST | MOVING_NORTHEAST
                                           | MOVING_SOUTHEAST | MOVING_SOUTHWEST));

    // Handle collision with the first object encountered
    MapObject* collision_object = nullptr;
    MapMode* map_mode = MapMode::CurrentInstance();
    ObjectSupervisor* object_supervisor = map_mode->GetObjectSupervisor();
    COLLISION_TYPE collision_type = object_supervisor->DetectCollision(this, next_pos_x,
                     next_pos_y,
                     &collision_object);
    // Try to fall back to straight direction
    if(moving_diagonally && collision_type != NO_COLLISION) {
        // Try on x axis
        if(object_supervisor->DetectCollision(this, _tile_position.x, next_pos_y, &collision_object) == NO_COLLISION) {
            next_pos_x = _tile_position.x;
            collision_type = NO_COLLISION;
        } // and then on y axis
        else if(object_supervisor->DetectCollision(this, next_pos_x, _tile_position.y, &collision_object) == NO_COLLISION) {
            next_pos_y = _tile_position.y;
            collision_type = NO_COLLISION;
        }
    }

    // Handles special collision handling first
    if(_control_event) {
        switch(_control_event->GetEventType()) {
            // Don't stuck the player's character or a sprite being controlled by a prepared path.
            // Plus, it's better not to change a path with encountered beings once started
            // for simplification purpose.
        case PATH_MOVE_SPRITE_EVENT:
            collision_type = NO_COLLISION;
            break;
            // Change the direction whenever something blocking is in the way.
        case RANDOM_MOVE_SPRITE_EVENT:
            if(collision_type != NO_COLLISION) {
                SetRandomDirection();
                return;
            }
        default:
            break;
        }
    }

    // Try to handle wall and physical collisions after a failed straight or diagonal move
    switch(collision_type) {
    case NO_COLLISION:
    default:
        break;
    case WALL_COLLISION:
        // When being blocked and moving diagonally, the npc is stuck.
        if(moving_diagonally)
            return;

        // Don't consider physical objects with an event to avoid sliding on their edges,
        // making them harder to "talk with".
        if (collision_object && this == map_mode->GetCamera()) {
            PhysicalObject *phs = reinterpret_cast<PhysicalObject *>(collision_object);
            if(phs && !phs->GetEventIdWhenTalking().empty())
                return;
        }

        // Fix the direction and destination to walk-around obstacles
        if (_HandleWallEdges(next_pos_x, next_pos_y, distance_moved, collision_object))
            break;
        // We don't do any other checks for the player sprite.
        else if (this == map_mode->GetCamera())
            return;

        // NPC sprites:

        // When it's a true wall, try against the collision grid
        if(!collision_object) {
            // Try a random diagonal to avoid the wall in straight direction
            if(_direction & (NORTH | SOUTH))
                _direction |= vt_utils::RandomBoundedInteger(0, 1) ? EAST : WEST;
            else if(_direction & (EAST | WEST))
                _direction |= vt_utils::RandomBoundedInteger(0, 1) ? NORTH : SOUTH;
            return;
        }
        // Physical and treasure objects are the only other matching "fake" walls
        else {
            // Try a diagonal to avoid the sprite in straight direction by comparing
            // each one coords.
            float diff_x = GetXPosition() - collision_object->GetXPosition();
            float diff_y = GetYPosition() - collision_object->GetYPosition();
            if(_direction & (NORTH | SOUTH))
                _direction |= diff_x >= 0.0f ? EAST : WEST;
            else if(_direction & (EAST | WEST))
                _direction |= diff_y >= 0.0f ? SOUTH : NORTH;
            return;
        }
        // Other cases shouldn't happen.
        break;
    case ENEMY_COLLISION:
        // Check only whether the player has collided with a monster
        if(this == map_mode->GetCamera() &&
                collision_object && collision_object->GetObjectType() == ENEMY_TYPE) {
            EnemySprite* enemy = reinterpret_cast<EnemySprite *>(collision_object);

            // Check whether the player is actually playing. If not, we don't want to start a battle.
            if (map_mode->CurrentState() == STATE_EXPLORE)
                map_mode->StartEnemyEncounter(enemy);
            return;
        }

        break;
    case CHARACTER_COLLISION:
        // Check whether the sprite is tangled with another character, even without moving
        // For instance, when colliding with a path follower npc.
        // And let it through in that case.
        if(object_supervisor->CheckObjectCollision(GetGridCollisionRectangle(), collision_object)) {
            collision_type = NO_COLLISION;
            break;
        }

        // When the sprite is controlled by the camera, let the player handle the position correction.
        if(this == map_mode->GetCamera())
            return;

        // Check whether an enemy has collided with the player
        if(this->GetType() == ENEMY_TYPE && collision_object == map_mode->GetCamera()) {
            EnemySprite* enemy = reinterpret_cast<EnemySprite *>(this);

            // Check whether the player is actually playing. If not, we don't want to start a battle.
            if (map_mode->CurrentState() == STATE_EXPLORE)
                map_mode->StartEnemyEncounter(enemy, false, true); // The enemy gets a boost in stamina.
            return;
        }

        // When being blocked and moving diagonally, the npc is stuck.
        if(moving_diagonally)
            return;

        if(!collision_object)  // Should never happen
            return;

        // Try a diagonal to avoid the sprite in straight direction by comparing
        // each one coords.
        float diff_x = GetXPosition() - collision_object->GetXPosition();
        float diff_y = GetYPosition() - collision_object->GetYPosition();
        if(_direction & (NORTH | SOUTH))
            _direction |= diff_x >= 0.0f ? EAST : WEST;
        else if(_direction & (EAST | WEST))
            _direction |= diff_y >= 0.0f ? SOUTH : NORTH;
        return;
    }

    // Inform the overlay system of the parallax movement done if needed
    if(this == map_mode->GetCamera()) {
        float x_parallax = !map_mode->IsCameraXAxisInMapCorner() ?
                           (GetXPosition() - next_pos_x)
                               / SCREEN_GRID_X_LENGTH
                               * vt_video::VIDEO_STANDARD_RES_WIDTH :
                           0.0f;
        float y_parallax = !map_mode->IsCameraYAxisInMapCorner() ?
                           (GetYPosition() - next_pos_y)
                               / SCREEN_GRID_Y_LENGTH
                               * vt_video::VIDEO_STANDARD_RES_HEIGHT :
                           0.0f;

        map_mode->GetEffectSupervisor().AddParallax(x_parallax, y_parallax);
        map_mode->GetIndicatorSupervisor().AddParallax(x_parallax, y_parallax);
    }

    // Make the sprite advance at the end
    SetPosition(next_pos_x, next_pos_y);
    _moved_position = true;
}