diff --git a/data/human/deep missions.txt b/data/human/deep missions.txt index e90920428f45..eb5c7c74ab20 100644 --- a/data/human/deep missions.txt +++ b/data/human/deep missions.txt @@ -1952,7 +1952,7 @@ mission "Deep: Scientist Rescue 0" attributes "deep" not attributes "station" destination "Valhalla" - waypoint "Betelgeuse" + mark "Betelgeuse" to offer has "event: deep: scientist rescue timer" or @@ -1989,8 +1989,11 @@ mission "Deep: Scientist Rescue 0" accept ` "Sorry, but this doesn't sound worth it to me."` decline - + on enter "Betelgeuse" + unmark "Betelgeuse" + dialog `Upon entering Betelgeuse, you fail to see any trace of the Star Queen. The pirates must have captured it and escaped to a nearby system.` on enter "Arneb" + unmark "Betelgeuse" set "found star queen" dialog `As you enter Arneb, your scanners pick up the Star Queen landing on Haven. Unfortunately, there are also a large number of pirate vessels headed in your direction. You should report back to Paris on for further instruction.` on visit diff --git a/data/human/free worlds 0 prologue.txt b/data/human/free worlds 0 prologue.txt index 31bd1609565e..550704d19c85 100644 --- a/data/human/free worlds 0 prologue.txt +++ b/data/human/free worlds 0 prologue.txt @@ -142,20 +142,19 @@ mission "Pact Recon 2" mission "Pact Recon 3" landing name "Pirate Reconnaissance" - description "Fly through the systems, searching for a gathering pirate fleet. Report to the Pact on when you are done." + description "Fly through the systems, searching for a gathering pirate fleet. Report to the Pact on once you have located them." source attributes "dirt belt" "near earth" "rim" "south" not government "Pirate" destination "Glaze" - waypoint "Boral" - waypoint "Hintar" - waypoint "Naper" - waypoint "Orvala" + mark "Boral" + mark "Hintar" + mark "Naper" + mark "Orvala" to offer random < 40 has "Pact Recon 2: done" not "event: war begins" - on offer dialog `Once again, you receive a message from Freya Winters. "We have another mission for you," she says, "this one a bit more dangerous. We've heard rumors of a pirate fleet amassing in one of the seldom-visited systems in the Dirt Belt, and we need to know where they are. Interested in helping to track them down? As before, you don't need to fight, just observe them."` @@ -165,10 +164,26 @@ mission "Pact Recon 3" system "Naper" fleet "Small Southern Pirates" 3 fleet "Large Northern Pirates" - + + on enter "Boral" + unmark "Boral" + on enter "Hintar" + unmark "Hintar" + on enter "Orvala" + unmark "Orvala" + on enter "Naper" + unmark "Boral" + unmark "Hintar" + unmark "Orvala" + unmark "Naper" + set "fw located pirate fleet" + dialog `You have located the pirate fleet. Time to head back to to report to Freya what you found.` on visit - dialog phrase "generic waypoint on visit" + dialog `You have reached , but you haven't located the pirate fleet yet! Better depart and make sure you have checked .` + to complete + has "fw located pirate fleet" on complete + clear "fw located pirate fleet" payment 80000 dialog `You report to Freya that you found a sizable pirate fleet in the Naper system, as well as spotting some pirates elsewhere. She thanks you, and pays you . "Thank you for your assistance," she says. "Again, I'll be in touch if we need you."` diff --git a/source/AI.cpp b/source/AI.cpp index 8cd4190930c7..67b27b330832 100644 --- a/source/AI.cpp +++ b/source/AI.cpp @@ -1897,7 +1897,8 @@ void AI::MoveEscort(Ship &ship, Command &command) const // If the parent is in-system and planning to jump, non-staying escorts should follow suit. else if(parent.Commands().Has(Command::JUMP) && parent.GetTargetSystem() && !isStaying) { - SelectRoute(ship, parent.GetTargetSystem()); + if(parent.GetTargetSystem() != ship.GetTargetSystem()) + SelectRoute(ship, parent.GetTargetSystem()); if(ship.GetTargetSystem()) { diff --git a/source/Engine.cpp b/source/Engine.cpp index 8fe56f7abc8b..2ecc57e4cb48 100644 --- a/source/Engine.cpp +++ b/source/Engine.cpp @@ -260,6 +260,30 @@ namespace { const double RADAR_SCALE = .00625; const double MAX_FUEL_DISPLAY = 5000.; + + const double CAMERA_VELOCITY_TRACKING = 0.1; + const double CAMERA_POSITION_CENTERING = 0.01; + + pair NewCenter(const Point &oldCenter, const Point &oldCenterVelocity, + const Point &baseCenter, const Point &baseVelocity) + { + if(Preferences::CameraAcceleration() == Preferences::CameraAccel::OFF) + return make_pair(baseCenter, baseVelocity); + + double cameraAccelMultiplier = Preferences::CameraAcceleration() == Preferences::CameraAccel::REVERSED ? -1. : 1.; + + // Flip the velocity offset if cameraAccelMultiplier is negative to simplify logic. + const Point absoluteOldCenterVelocity = baseVelocity.Lerp(oldCenterVelocity, cameraAccelMultiplier); + + const Point newAbsVelocity = absoluteOldCenterVelocity.Lerp(baseVelocity, CAMERA_VELOCITY_TRACKING); + + const Point newCenter = (oldCenter + newAbsVelocity).Lerp(baseCenter, CAMERA_POSITION_CENTERING); + + // Flip the velocity back over the baseVelocity + const Point newVelocity = baseVelocity.Lerp(newAbsVelocity, cameraAccelMultiplier); + + return make_pair(newCenter, newVelocity); + } } @@ -522,12 +546,21 @@ void Engine::Step(bool isActive) } else if(flagship) { - center = flagship->Center(); - centerVelocity = flagship->Velocity(); - Preferences::ExtendedJumpEffects jumpEffectState = Preferences::GetExtendedJumpEffects(); - if(flagship->IsHyperspacing() && jumpEffectState != Preferences::ExtendedJumpEffects::OFF) - centerVelocity *= 1. + pow(flagship->GetHyperspacePercentage() / - (jumpEffectState == Preferences::ExtendedJumpEffects::MEDIUM ? 40. : 20.), 2); + const auto newCamera = NewCenter(center, centerVelocity, + flagship->Center(), flagship->Velocity()); + + if(flagship->IsHyperspacing()) + { + hyperspacePercentage = flagship->GetHyperspacePercentage() / 100.; + center = newCamera.first.Lerp(flagship->Center(), pow(hyperspacePercentage, .5)); + centerVelocity = flagship->Velocity(); + } + else if(isActive) + { + center = newCamera.first; + centerVelocity = newCamera.second; + } + if(doEnterLabels) { doEnterLabels = false; @@ -617,7 +650,7 @@ void Engine::Step(bool isActive) // Add the flagship outline last to distinguish the flagship from other ships. if(flagship && !flagship->IsDestroyed() && Preferences::Has("Highlight player's flagship")) { - outlines.emplace_back(flagship->GetSprite(), (flagship->Position() - center) * zoom, flagship->Unit() * zoom, + outlines.emplace_back(flagship->GetSprite(), (flagship->Center() - center) * zoom, flagship->Unit() * zoom, flagship->GetFrame(), *GameData::Colors().Get("flagship highlight")); } @@ -1166,8 +1199,15 @@ list &Engine::Events() // Draw a frame. void Engine::Draw() const { - GameData::Background().Draw(center, Preferences::Has("Render motion blur") ? centerVelocity : Point(), - zoom, (player.Flagship() ? player.Flagship()->GetSystem() : player.GetSystem())); + Point motionBlur = Preferences::Has("Render motion blur") ? centerVelocity : Point(); + + Preferences::ExtendedJumpEffects jumpEffectState = Preferences::GetExtendedJumpEffects(); + if(jumpEffectState != Preferences::ExtendedJumpEffects::OFF) + motionBlur *= 1. + pow(hyperspacePercentage * + (jumpEffectState == Preferences::ExtendedJumpEffects::MEDIUM ? 2.5 : 5.), 2); + + GameData::Background().Draw(center, motionBlur, zoom, + (player.Flagship() ? player.Flagship()->GetSystem() : player.GetSystem())); static const Set &colors = GameData::Colors(); const Interface *hud = GameData::Interfaces().Get("hud"); @@ -1527,6 +1567,9 @@ void Engine::EnterSystem() newVisuals.clear(); newFlotsam.clear(); + + center = flagship->Center(); + // Help message for new players. Show this message for the first four days, // since the new player ships can make at most four jumps before landing. if(today <= player.StartData().GetDate() + 4) @@ -1709,8 +1752,22 @@ void Engine::CalculateStep() Point newCenterVelocity; if(flagship) { - newCenter = flagship->Center(); - newCenterVelocity = flagship->Velocity(); + const auto newCamera = NewCenter(center, centerVelocity, + flagship->Center(), flagship->Velocity()); + + if(flagship->IsHyperspacing()) + { + hyperspacePercentage = + flagship->GetHyperspacePercentage() / 100.; + newCenter = newCamera.first.Lerp( + flagship->Center(), pow(hyperspacePercentage, .5)); + newCenterVelocity = flagship->Velocity(); + } + else + { + newCenter = newCamera.first; + newCenterVelocity = newCamera.second; + } } draw[currentCalcBuffer].SetCenter(newCenter, newCenterVelocity); batchDraw[currentCalcBuffer].SetCenter(newCenter); diff --git a/source/Engine.h b/source/Engine.h index b7c92ec4672a..ab4680b01771 100644 --- a/source/Engine.h +++ b/source/Engine.h @@ -244,6 +244,7 @@ class Engine { std::vector> ammo; int jumpCount = 0; const System *jumpInProgress[2] = {nullptr, nullptr}; + double hyperspacePercentage = 0.; int step = 0; diff --git a/source/Point.cpp b/source/Point.cpp index b96b44cf6f73..cf81b09422b3 100644 --- a/source/Point.cpp +++ b/source/Point.cpp @@ -299,6 +299,13 @@ double Point::DistanceSquared(const Point &point) const +Point Point::Lerp(const Point &to, const double c) const +{ + return *this + (to - *this) * c; +} + + + // Absolute value of both coordinates. Point abs(const Point &p) { diff --git a/source/Point.h b/source/Point.h index ddfeff472220..6faf1ab5223b 100644 --- a/source/Point.h +++ b/source/Point.h @@ -75,6 +75,8 @@ class Point { double Distance(const Point &point) const; double DistanceSquared(const Point &point) const; + Point Lerp(const Point &to, const double c) const; + // Take the absolute value of both coordinates. friend Point abs(const Point &p); // Use the min of each x and each y coordinates. diff --git a/source/Preferences.cpp b/source/Preferences.cpp index fb9a9e612503..7e95bb3234cd 100644 --- a/source/Preferences.cpp +++ b/source/Preferences.cpp @@ -54,6 +54,9 @@ namespace { const vector VSYNC_SETTINGS = {"off", "on", "adaptive"}; int vsyncIndex = 1; + const vector CAMERA_ACCELERATION_SETTINGS = {"off", "on", "reversed"}; + int cameraAccelerationIndex = 1; + class OverlaySetting { public: OverlaySetting() = default; @@ -192,6 +195,8 @@ void Preferences::Load() zoomIndex = max(0., node.Value(1)); else if(node.Token(0) == "vsync") vsyncIndex = max(0, min(node.Value(1), VSYNC_SETTINGS.size() - 1)); + else if(node.Token(0) == "camera acceleration") + cameraAccelerationIndex = max(0, min(node.Value(1), CAMERA_ACCELERATION_SETTINGS.size() - 1)); else if(node.Token(0) == "Show all status overlays") statusOverlaySettings[OverlayType::ALL].SetState(node.Value(1)); else if(node.Token(0) == "Show flagship overlay") @@ -271,6 +276,7 @@ void Preferences::Save() out.Write("Flotsam collection", flotsamIndex); out.Write("view zoom", zoomIndex); out.Write("vsync", vsyncIndex); + out.Write("camera acceleration", cameraAccelerationIndex); out.Write("date format", dateFormatIndex); out.Write("Show all status overlays", statusOverlaySettings[OverlayType::ALL].ToInt()); out.Write("Show flagship overlay", statusOverlaySettings[OverlayType::FLAGSHIP].ToInt()); @@ -531,6 +537,27 @@ const string &Preferences::VSyncSetting() +void Preferences::ToggleCameraAcceleration() +{ + cameraAccelerationIndex = (cameraAccelerationIndex + 1) % CAMERA_ACCELERATION_SETTINGS.size(); +} + + + +Preferences::CameraAccel Preferences::CameraAcceleration() +{ + return static_cast(cameraAccelerationIndex); +} + + + +const string &Preferences::CameraAccelerationSetting() +{ + return CAMERA_ACCELERATION_SETTINGS[cameraAccelerationIndex]; +} + + + void Preferences::CycleStatusOverlays(Preferences::OverlayType type) { // Calling OverlaySetting::Increment when the state is ON_HIT will cycle to off. diff --git a/source/Preferences.h b/source/Preferences.h index fda90a946e8d..a3a7d79ebc64 100644 --- a/source/Preferences.h +++ b/source/Preferences.h @@ -30,6 +30,12 @@ class Preferences { adaptive, }; + enum class CameraAccel : int_fast8_t { + OFF = 0, + ON, + REVERSED, + }; + enum class DateFormat : int_fast8_t { DMY = 0, // Day-first format. (Sat, 4 Oct 1941) MDY, // Month-first format. (Sat, Oct 4, 1941) @@ -142,6 +148,10 @@ class Preferences { static VSync VSyncState(); static const std::string &VSyncSetting(); + static void ToggleCameraAcceleration(); + static CameraAccel CameraAcceleration(); + static const std::string &CameraAccelerationSetting(); + static void CycleStatusOverlays(OverlayType type); static OverlayState StatusOverlaysState(OverlayType type); static const std::string &StatusOverlaysSetting(OverlayType type); diff --git a/source/PreferencesPanel.cpp b/source/PreferencesPanel.cpp index 9528b7af5d48..929ae380e083 100644 --- a/source/PreferencesPanel.cpp +++ b/source/PreferencesPanel.cpp @@ -57,6 +57,7 @@ namespace { const string AUTO_FIRE_SETTING = "Automatic firing"; const string SCREEN_MODE_SETTING = "Screen mode"; const string VSYNC_SETTING = "VSync"; + const string CAMERA_ACCELERATION = "Camera acceleration"; const string CLOAK_OUTLINE = "Cloaked ship outlines"; const string STATUS_OVERLAYS_ALL = "Show status overlays"; const string STATUS_OVERLAYS_FLAGSHIP = " Show flagship overlay"; @@ -638,6 +639,7 @@ void PreferencesPanel::DrawSettings() VIEW_ZOOM_FACTOR, SCREEN_MODE_SETTING, VSYNC_SETTING, + CAMERA_ACCELERATION, "", "Performance", "Show CPU / GPU load", @@ -773,6 +775,11 @@ void PreferencesPanel::DrawSettings() text = Preferences::StatusOverlaysSetting(Preferences::OverlayType::ALL); isOn = text != "off"; } + else if(setting == CAMERA_ACCELERATION) + { + text = Preferences::CameraAccelerationSetting(); + isOn = text != "off"; + } else if(setting == STATUS_OVERLAYS_FLAGSHIP) { text = Preferences::StatusOverlaysSetting(Preferences::OverlayType::FLAGSHIP); @@ -1207,6 +1214,8 @@ void PreferencesPanel::HandleSettingsString(const string &str, Point cursorPosit GetUI()->Push(new Dialog( "Unable to change VSync state. (Your system's graphics settings may be controlling it instead.)")); } + else if(str == CAMERA_ACCELERATION) + Preferences::ToggleCameraAcceleration(); else if(str == STATUS_OVERLAYS_ALL) Preferences::CycleStatusOverlays(Preferences::OverlayType::ALL); else if(str == STATUS_OVERLAYS_FLAGSHIP)