extract the scroll position into WorldCamera
This commit is contained in:
@@ -199,7 +199,7 @@ GameWorldView::GameWorldView(Simulation* sim, const GameConfig* config,
|
||||
, m_commandManager(*sim)
|
||||
, m_gameSpeedMultiplier(1.0)
|
||||
, m_prevNonZeroSpeed(1.0)
|
||||
, m_scrollXTiles(0.0f)
|
||||
, m_camera(config->world.scroll, config->world.regions)
|
||||
, m_ghostRotation(Rotation::East)
|
||||
, m_ghostValid(false)
|
||||
, m_dragging(false)
|
||||
@@ -366,18 +366,19 @@ void GameWorldView::onFrame()
|
||||
|
||||
// Apply held scroll
|
||||
{
|
||||
// Pan speed depends on where the view is centered (REQ-UI-SCROLL-SPEED).
|
||||
const float viewCenterX = m_scrollXTiles;
|
||||
const float delta = panSpeedTilesPerSecondAt(viewCenterX)
|
||||
* static_cast<float>(elapsed) / 1000.0f;
|
||||
const float scrollBefore = m_scrollXTiles;
|
||||
if (m_scrollLeft) { m_scrollXTiles -= delta; }
|
||||
if (m_scrollRight) { m_scrollXTiles += delta; }
|
||||
clampScroll();
|
||||
// Holding both keys cancels out, as it did when each key moved the view
|
||||
// independently.
|
||||
PanDirection direction = PanDirection::None;
|
||||
if (m_scrollLeft != m_scrollRight)
|
||||
{
|
||||
direction = m_scrollLeft ? PanDirection::Left : PanDirection::Right;
|
||||
}
|
||||
|
||||
const bool viewMoved = m_camera.advance(direction, elapsed, getScrollBounds());
|
||||
|
||||
// While the view scrolls, the tile under a stationary cursor changes,
|
||||
// so refresh the box-select rectangle even though no mouse move fires.
|
||||
if (m_boxSelecting && m_scrollXTiles != scrollBefore)
|
||||
if (m_boxSelecting && viewMoved)
|
||||
{
|
||||
m_boxCurrentTile =
|
||||
getCoordinates().widgetToTile(mapFromGlobal(QCursor::pos()));
|
||||
@@ -526,7 +527,7 @@ void GameWorldView::paintGL()
|
||||
WorldCoordinates GameWorldView::getCoordinates() const
|
||||
{
|
||||
return WorldCoordinates::scrolling(size(), m_config->world.heightTiles,
|
||||
m_scrollXTiles);
|
||||
m_camera.getViewCenterXTiles());
|
||||
}
|
||||
|
||||
float GameWorldView::getAsteroidLeftEdge() const
|
||||
@@ -562,47 +563,13 @@ float GameWorldView::getEnemyStationRightEdge() const
|
||||
return rightX;
|
||||
}
|
||||
|
||||
namespace
|
||||
ScrollBounds GameWorldView::getScrollBounds() const
|
||||
{
|
||||
// Linearly blend from valueAt0 (for x <= x0) to valueAt1 (for x >= x1), clamped
|
||||
// outside [x0, x1]. A zero- or negative-width band collapses to a hard step at x1.
|
||||
float lerpClamped(float valueAt0, float valueAt1, float x0, float x1, float x)
|
||||
{
|
||||
if (x1 <= x0) { return x < x1 ? valueAt0 : valueAt1; }
|
||||
const float t = std::max(0.0f, std::min(1.0f, (x - x0) / (x1 - x0)));
|
||||
return valueAt0 + (valueAt1 - valueAt0) * t;
|
||||
}
|
||||
}
|
||||
|
||||
float GameWorldView::panSpeedTilesPerSecondAt(float viewCenterXTiles) const
|
||||
{
|
||||
// Slow near the asteroid/player buffer, fast across the contest zone, with a
|
||||
// linear ramp straddling each contest-zone boundary (REQ-UI-SCROLL-SPEED). The
|
||||
// contest zone spans from the player buffer's right edge to the enemy stations,
|
||||
// the latter tracked live so the ramp follows the front line as it is pushed.
|
||||
const float slow = static_cast<float>(m_config->world.scroll.panSpeedSlow_tps);
|
||||
const float fast = static_cast<float>(m_config->world.scroll.panSpeedFast_tps);
|
||||
const float half = static_cast<float>(m_config->world.scroll.panRampBandWidth_tiles) / 2.0f;
|
||||
const float leftEdge = static_cast<float>(m_config->world.regions.playerBufferWidth_tiles);
|
||||
const float rightEdge = getEnemyStationRightEdge();
|
||||
|
||||
// Rising ramp at the left boundary (slow -> fast) and falling ramp at the right
|
||||
// boundary (fast -> slow); their minimum yields flat-slow outside, flat-fast in
|
||||
// the middle, and — if the bands overlap in a narrow contest zone — a single peak
|
||||
// below the fast speed where the two ramps cross.
|
||||
const float leftRamp = lerpClamped(slow, fast, leftEdge - half, leftEdge + half, viewCenterXTiles);
|
||||
const float rightRamp = lerpClamped(fast, slow, rightEdge - half, rightEdge + half, viewCenterXTiles);
|
||||
return std::min(leftRamp, rightRamp);
|
||||
}
|
||||
|
||||
void GameWorldView::clampScroll()
|
||||
{
|
||||
// m_scrollXTiles is the view center, so the pan limits are the edges themselves:
|
||||
// the view can pan left until the buildable/asteroid edge is centered, and right
|
||||
// until the enemy stations are centered (revealing a little space beyond them).
|
||||
const float leftBound = getAsteroidLeftEdge();
|
||||
const float rightBound = getEnemyStationRightEdge();
|
||||
m_scrollXTiles = std::max(leftBound, std::min(m_scrollXTiles, rightBound));
|
||||
// The camera clamps its view center to these, so the pan limits are the edges
|
||||
// themselves: the view can pan left until the buildable/asteroid edge is
|
||||
// centered, and right until the enemy stations are centered (revealing a little
|
||||
// space beyond them).
|
||||
return ScrollBounds{getAsteroidLeftEdge(), getEnemyStationRightEdge()};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -3102,7 +3069,7 @@ void GameWorldView::resetForNewGame()
|
||||
m_copiedConfig = std::nullopt;
|
||||
m_copyConfigFlashes.clear();
|
||||
m_boxSelecting = false;
|
||||
m_scrollXTiles = 0.0f;
|
||||
m_camera.reset();
|
||||
m_scrollLeft = false;
|
||||
m_scrollRight = false;
|
||||
m_gameOverShown = false;
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
#include "TickDriver.h"
|
||||
#include "TunnelCompletion.h"
|
||||
#include "VisualsConfig.h"
|
||||
#include "WorldCamera.h"
|
||||
#include "WorldCoordinates.h"
|
||||
|
||||
struct Command;
|
||||
@@ -184,9 +185,9 @@ private:
|
||||
|
||||
float getAsteroidLeftEdge() const;
|
||||
float getEnemyStationRightEdge() const;
|
||||
// Horizontal pan speed at a given view-center X, in tiles/s (REQ-UI-SCROLL-SPEED).
|
||||
float panSpeedTilesPerSecondAt(float viewCenterXTiles) const;
|
||||
void clampScroll();
|
||||
// The camera's current pan limits, read fresh from the simulation each frame:
|
||||
// both edges move with asteroid expansion and with pushes (REQ-GW-SCROLL-LIMIT).
|
||||
ScrollBounds getScrollBounds() const;
|
||||
|
||||
bool isValidPlacement(BuildingType type, QPoint anchor, Rotation rot) const;
|
||||
std::optional<BuildingId> buildingAtTile(QPoint tile) const;
|
||||
@@ -333,8 +334,9 @@ private:
|
||||
std::mt19937 m_rng;
|
||||
double m_gameSpeedMultiplier;
|
||||
double m_prevNonZeroSpeed;
|
||||
// World-X (tiles) at the center of the viewport (see getViewLeftTiles()).
|
||||
float m_scrollXTiles;
|
||||
// Horizontal view position (REQ-UI-SCROLL). Owns the scroll position itself;
|
||||
// this widget only supplies the pan intent and the simulation-derived bounds.
|
||||
WorldCamera m_camera;
|
||||
|
||||
QTimer* m_renderTimer;
|
||||
|
||||
@@ -390,6 +392,11 @@ private:
|
||||
QPoint m_boxStartTile;
|
||||
QPoint m_boxCurrentTile;
|
||||
|
||||
// Held-key state for the A / D pan controls (REQ-UI-SCROLL, REQ-UI-HOTKEYS),
|
||||
// collapsed into a PanDirection for the camera each frame. Kept here rather
|
||||
// than on the camera because it is key state, not view state: once controls
|
||||
// are rebindable this becomes an input mapper publishing the direction, and
|
||||
// the camera's interface does not change.
|
||||
bool m_scrollLeft;
|
||||
bool m_scrollRight;
|
||||
bool m_gameOverShown;
|
||||
|
||||
Reference in New Issue
Block a user