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; }