diff options
author | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
---|---|---|
committer | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
commit | 6352e8e3196f78388b6c771073f9e03eaa612673 (patch) | |
tree | e23772f79a7fbc41bc9108951e9e136857484bf4 /source/game/StarSky.cpp | |
parent | 6741a057e5639280d85d0f88ba26f000baa58f61 (diff) |
everything everywhere
all at once
Diffstat (limited to 'source/game/StarSky.cpp')
-rw-r--r-- | source/game/StarSky.cpp | 655 |
1 files changed, 655 insertions, 0 deletions
diff --git a/source/game/StarSky.cpp b/source/game/StarSky.cpp new file mode 100644 index 0000000..3266b0f --- /dev/null +++ b/source/game/StarSky.cpp @@ -0,0 +1,655 @@ +#include "StarSky.hpp" +#include "StarJsonExtra.hpp" +#include "StarDataStreamExtra.hpp" +#include "StarRoot.hpp" +#include "StarCelestialDatabase.hpp" +#include "StarCelestialGraphics.hpp" +#include "StarAssets.hpp" +#include "StarTime.hpp" +#include "StarRandomPoint.hpp" +#include "StarMixer.hpp" +#include "StarCompression.hpp" + +namespace Star { + +Sky::Sky() { + m_settings = Root::singleton().assets()->json("/sky.config"); + + m_starFrames = m_settings.queryInt("stars.frames"); + m_starList = jsonToStringList(m_settings.query("stars.list")); + m_hyperStarList = jsonToStringList(m_settings.query("stars.hyperlist")); + + m_netInit = false; + + m_netGroup.addNetElement(&m_skyParametersNetState); + m_netGroup.addNetElement(&m_skyTypeNetState); + m_netGroup.addNetElement(&m_timeNetState); + m_netGroup.addNetElement(&m_flyingTypeNetState); + m_netGroup.addNetElement(&m_enterHyperspaceNetState); + m_netGroup.addNetElement(&m_startInWarpNetState); + m_netGroup.addNetElement(&m_worldMoveNetState); + m_netGroup.addNetElement(&m_starMoveNetState); + m_netGroup.addNetElement(&m_warpPhaseNetState); + m_netGroup.addNetElement(&m_flyingTimerNetState); + + m_netGroup.setNeedsLoadCallback(bind(&Sky::readNetStates, this)); + m_netGroup.setNeedsStoreCallback(bind(&Sky::writeNetStates, this)); +} + +Sky::Sky(SkyParameters const& skyParameters, bool inOrbit) : Sky() { + m_skyParameters = skyParameters; + m_skyParametersUpdated = true; + + if (inOrbit) + m_skyType = SkyType::Orbital; + else + m_skyType = m_skyParameters.skyType; +} + +void Sky::startFlying(bool enterHyperspace, bool startInWarp) { + if (startInWarp) + m_flyingType = FlyingType::Warp; + else + m_flyingType = FlyingType::Disembarking; + + m_flyingTimer = 0; + m_enterHyperspace = enterHyperspace; + m_startInWarp = startInWarp; +} + +void Sky::stopFlyingAt(Maybe<SkyParameters> dest) { + m_destWorld = dest; +} + +void Sky::jumpTo(SkyParameters skyParameters) { + m_skyParameters = skyParameters; + m_skyParametersUpdated = true; +} + +pair<ByteArray, uint64_t> Sky::writeUpdate(uint64_t fromVersion) { + return m_netGroup.writeNetState(fromVersion); +} + +void Sky::readUpdate(ByteArray data) { + m_netGroup.readNetState(move(data)); +} + +void Sky::stateUpdate() { + if (m_lastFlyingType != m_flyingType) { + m_flyingTimer = 0.0f; + + if (m_flyingType == FlyingType::Warp) { + m_warpPhase = WarpPhase::SpeedingUp; + if (m_startInWarp) { + if (m_enterHyperspace) + m_warpPhase = WarpPhase::Maintain; + else + m_flyingTimer = speedupTime(); + + m_lastWarpPhase = m_warpPhase; + } + + float maxVelocity = m_settings.queryFloat("flyMaxVelocity"); + m_worldMoveOffset = Vec2F::withAngle(m_pathRotation, maxVelocity / 2.0 * speedupTime()); + m_starMoveOffset = Vec2F::withAngle(0, (maxVelocity * m_settings.queryFloat("starVelocityFactor")) / 2.0 * speedupTime()); + } else if (m_flyingType == FlyingType::Arriving) { + m_sentSFX = false; + + m_worldOffset = {}; + m_starOffset = {}; + } + } + + if (m_lastWarpPhase != m_warpPhase) { + m_flyingTimer = 0.0f; + + if (m_warpPhase == WarpPhase::SpeedingUp) + m_sentSFX = false; + else if (m_warpPhase == WarpPhase::Maintain) + enterHyperspace(); + else if (m_warpPhase == WarpPhase::SlowingDown) + exitHyperspace(); + } + + m_lastFlyingType = m_flyingType; + m_lastWarpPhase = m_warpPhase; +} + +void Sky::update() { + double dt = WorldTimestep; + if (m_referenceClock) { + m_time = m_referenceClock->time(); + if (!m_clockTrackingTime) { + m_clockTrackingTime = m_time; + } else { + // If our reference clock is set, and we have a valid tracking time, then + // the dt should be driven by the reference clock. + dt = m_time - *m_clockTrackingTime; + m_clockTrackingTime = m_time; + } + } else { + m_time += dt; + } + + m_flashTimer = std::max(0.0, m_flashTimer - dt); + + if (flying()) { + m_flyingTimer += dt; + + if (m_flyingType == FlyingType::Disembarking) { + bool finished; + if (m_skyParameters.skyType == SkyType::Space) + finished = controlledMovement(m_settings.getArray("spaceDisembarkPath"), m_settings.get("spaceDisembarkOrigin"), m_flyingTimer); + else + finished = controlledMovement(m_settings.getArray("disembarkPath"), m_settings.get("disembarkOrigin"), m_flyingTimer); + + if (finished) { + m_flyingType = FlyingType::Warp; + } + } else if (m_flyingType == FlyingType::Arriving) { + bool finished; + if (m_skyParameters.skyType == SkyType::Space) + finished = controlledMovement(m_settings.getArray("spaceArrivalPath"), m_settings.get("spaceArrivalOrigin"), m_flyingTimer); + else + finished = controlledMovement(m_settings.getArray("arrivalPath"), m_settings.get("arrivalOrigin"), m_flyingTimer); + + if (finished) { + m_flyingType = FlyingType::None; + } + + m_starOffset -= m_starOffset * m_settings.queryFloat("correctionPower"); + m_worldOffset -= m_worldOffset * m_settings.queryFloat("correctionPower"); + } else if (m_flyingType == FlyingType::Warp) { + float percentage = 0.0; + float dir = (int)m_warpPhase < 0 ? -1.0 : 1.0; + if (m_warpPhase == WarpPhase::SpeedingUp) + percentage = powf(m_flyingTimer / speedupTime(), 2.0); + else if (m_warpPhase == WarpPhase::Maintain) + percentage = 1.0; + else if (m_warpPhase == WarpPhase::SlowingDown) + percentage = powf(1 - m_flyingTimer / slowdownTime(), 2.0); + + if (percentage < 1.0) { + m_starOffset = (dir * m_starMoveOffset * percentage).rotate(-getStarRotation()); + m_worldOffset = (dir * m_worldMoveOffset * percentage).rotate(-getWorldRotation()); + } else { + m_starOffset += Vec2F::withAngle(-getStarRotation(), m_settings.queryFloat("flyMaxVelocity") * dt * m_settings.queryFloat("starVelocityFactor")); + m_worldOffset = m_worldMoveOffset; + } + + if (m_warpPhase == WarpPhase::SpeedingUp && m_flyingTimer >= speedupTime() + && !m_enterHyperspace + && m_destWorld) { + jumpTo(m_destWorld.take()); + m_warpPhase = WarpPhase::SlowingDown; + } else if (m_warpPhase == WarpPhase::SpeedingUp && m_flyingTimer >= speedupTime() && m_enterHyperspace) { + m_warpPhase = WarpPhase::Maintain; + } else if (m_warpPhase == WarpPhase::Maintain && m_flyingTimer >= m_settings.queryFloat("flyingTimer") + && m_destWorld) { + jumpTo(m_destWorld.take()); + m_warpPhase = WarpPhase::SlowingDown; + } else if (m_warpPhase == WarpPhase::SlowingDown && m_flyingTimer >= slowdownTime()) { + m_flyingType = FlyingType::Arriving; + } + } + } else { + m_starOffset = {}; + m_worldOffset = {}; + m_pathOffset = {}; + m_worldRotation = m_pathRotation = 0; + } + + stateUpdate(); + + if (!flying()) + m_starRotation = constrainAngle(m_starRotation + dt / dayLength() * 2 * Constants::pi); + else + m_starRotation = 0; +} + +void Sky::setType(SkyType skyType) { + m_skyType = skyType; +} + +SkyType Sky::type() const { + return m_skyType; +} + +bool Sky::inSpace() const { + return m_skyType == SkyType::Orbital || m_skyType == SkyType::Warp || m_skyType == SkyType::Space; +} + +uint64_t Sky::seed() const { + return m_skyParameters.seed; +} + +float Sky::dayLength() const { + return m_skyParameters.dayLength.value(DefaultDayLength); +} + +uint32_t Sky::day() const { + if (!m_skyParameters.dayLength) + return 0; + return floor(epochTime() / dayLength()); +} + +float Sky::timeOfDay() const { + if (!m_skyParameters.dayLength) + return 0; + return fmod(epochTime(), (double)dayLength()); +} + +double Sky::epochTime() const { + return m_time; +} + +void Sky::setEpochTime(double epochTime) { + m_time = epochTime; +} + +float Sky::altitude() const { + return m_altitude; +} + +void Sky::setAltitude(float altitude) { + m_altitude = altitude; +} + +void Sky::setReferenceClock(ClockConstPtr const& referenceClock) { + m_referenceClock = referenceClock; + m_time = m_referenceClock->time(); + m_clockTrackingTime = {}; +} + +ClockConstPtr Sky::referenceClock() const { + return m_referenceClock; +} + +String Sky::ambientNoise() const { + if (flying()) { + if (m_flyingType == FlyingType::Warp && m_warpPhase == WarpPhase::Maintain) { + return m_settings.queryString("hyperspaceAudio"); + } else { + return m_settings.queryString("engineAudio"); + } + } + + return ""; +} + +List<AudioInstancePtr> Sky::pullSounds() { + List<AudioInstancePtr> res; + if (m_flyingType == FlyingType::Warp) { + if (m_warpPhase == WarpPhase::SpeedingUp && !m_sentSFX) { + float triggerTime = speedupTime() - m_settings.queryFloat("enterHyperspaceAudioLeadIn"); + if (triggerTime < 0 || !m_enterHyperspace) { + m_sentSFX = true; + return res; + } + + if (m_flyingTimer >= triggerTime) { + auto assets = Root::singleton().assets(); + auto SFX = assets->audio(m_settings.queryString("enterHyperspaceAudio")); + m_sentSFX = true; + res.append(make_shared<AudioInstance>(*SFX)); + return res; + } + } else if (m_warpPhase == WarpPhase::Maintain && !m_sentSFX) { + float triggerTime = m_settings.queryFloat("flyingTimer") - m_settings.queryFloat("exitHyperspaceAudioLeadIn"); + if (triggerTime < 0) { + m_sentSFX = true; + return res; + } + + if (m_flyingTimer >= triggerTime) { + auto assets = Root::singleton().assets(); + auto SFX = assets->audio(m_settings.queryString("exitHyperspaceAudio")); + m_sentSFX = true; + res.append(make_shared<AudioInstance>(*SFX)); + return res; + } + } + } else if (m_flyingType == FlyingType::Arriving) { + if (!m_sentSFX) { + auto assets = Root::singleton().assets(); + auto SFX = assets->audio(m_settings.queryString("arrivalAudio")); + m_sentSFX = true; + res.append(make_shared<AudioInstance>(*SFX)); + return res; + } + } + return res; +} + +float Sky::spaceLevel() const { + if (type() == SkyType::Atmospheric && m_skyParameters.spaceLevel && m_skyParameters.surfaceLevel) { + float altitudeRange = *m_skyParameters.spaceLevel - *m_skyParameters.surfaceLevel; + float relativeAltitude = m_altitude - *m_skyParameters.surfaceLevel; + return clamp(relativeAltitude / altitudeRange, 0.0f, 1.0f); + } + return 1.0f; +} + +float Sky::orbitAngle() const { + // TODO: What should we do here? Used to divide by zero. Now is saved by + // DefaultDayLength, but it's a hack + return 2 * Constants::pi * timeOfDay() / dayLength(); +} + +bool Sky::isDayTime() const { + return dayLevel() >= 0.5; +} + +float Sky::dayLevel() const { + // Turn the dayCycle value into a value that blends evenly between 0.0 at + // mid-night and 1.0 at mid-day and then back again. + + float dayCycle = Sky::dayCycle(); + if (dayCycle < 1.0) + return dayCycle / 2 + 0.5f; + else if (dayCycle > 3.0) + return (dayCycle - 3) / 2; + else + return 1.0 - (dayCycle - 1.0) / 2; +} + +float Sky::dayCycle() const { + // Always middle of the night in orbit or warp space. + if (type() == SkyType::Orbital || type() == SkyType::Warp) + return 3.0f; + + // This will misbehave badly if dayTransitionTime is greater than dayLength / + // 2 + + float transitionTime = m_settings.queryFloat("dayTransitionTime") / 2; + float dayLength = Sky::dayLength(); + float timeOfDay = Sky::timeOfDay(); + + // timeOfDay() is defined such that 0.0 is mid-dawn. For convenience, shift + // the time of day forwards such that 0.0 is the beginning of the morning. + float shiftedTime = pfmod(timeOfDay + transitionTime / 2, dayLength); + + // There are 5 times here, beginning of the morning, end of the morning, + // beginning of the evening, end of the evening, and then the beginning of + // the morning again (wrapping around). + Array<float, 5> transitionPositions = { + 0.0f, transitionTime, dayLength / 2, dayLength / 2 + transitionTime, dayLength}; + // The values here are mid-night, mid-day, mid-day, mid-night, mid-night. + Array<float, 5> transitionValues = {-1.0f, 1.0f, 1.0f, 3.0f, 3.0f}; + + return pfmod(parametricInterpolate2( + transitionPositions, transitionValues, shiftedTime, SinWeightOperator<float>(), BoundMode::Clamp), + 4.0f); +} + +float Sky::skyAlpha() const { + if (m_skyType != SkyType::Atmospheric) { + return 0.0f; + } else { + float skyLevel = 1.0f - spaceLevel(); + return clamp(pow(skyLevel, m_settings.getFloat("skyLevelExponent")), 0.0f, 1.0f); + } +} + +Color Sky::environmentLight() const { + if (m_skyType == SkyType::Orbital || m_skyType == SkyType::Warp) + return Color::Black; + + if (m_skyParameters.skyColoring.isLeft()) { + auto skyColoring = m_skyParameters.skyColoring.left(); + + Array<Vec4F, 4> colors; + colors[0] = skyColoring.morningLightColor.toRgbaF(); + colors[1] = skyColoring.dayLightColor.toRgbaF(); + colors[2] = skyColoring.eveningLightColor.toRgbaF(); + colors[3] = skyColoring.nightLightColor.toRgbaF(); + + return Color::rgbaf(listInterpolate2(colors, dayCycle(), SinWeightOperator<float>(), BoundMode::Wrap)); + } else { + return m_skyParameters.skyColoring.right(); + } +} + +Color Sky::mainSkyColor() const { + if (m_skyParameters.skyColoring.isLeft()) + return m_skyParameters.skyColoring.left().mainColor; + + return Color::Black; +} + +pair<Color, Color> Sky::skyRectColors() const { + if (m_skyParameters.skyColoring.isLeft()) { + auto skyColoring = m_skyParameters.skyColoring.left(); + + Array<Vec4F, 4> topColorList; + topColorList[0] = skyColoring.morningColors.first.toRgbaF(); + topColorList[1] = skyColoring.dayColors.first.toRgbaF(); + topColorList[2] = skyColoring.eveningColors.first.toRgbaF(); + topColorList[3] = skyColoring.nightColors.first.toRgbaF(); + + Array<Vec4F, 4> bottomColorList; + bottomColorList[0] = skyColoring.morningColors.second.toRgbaF(); + bottomColorList[1] = skyColoring.dayColors.second.toRgbaF(); + bottomColorList[2] = skyColoring.eveningColors.second.toRgbaF(); + bottomColorList[3] = skyColoring.nightColors.second.toRgbaF(); + + float cycle = dayCycle(); + auto topColor = Color::rgbaf(listInterpolate2(topColorList, cycle, SinWeightOperator<float>(), BoundMode::Wrap)); + auto bottomColor = Color::rgbaf(listInterpolate2(bottomColorList, cycle, SinWeightOperator<float>(), BoundMode::Wrap)); + + float skyAlpha = Sky::skyAlpha(); + topColor.setAlpha(topColor.alpha() * skyAlpha); + bottomColor.setAlpha(bottomColor.alpha() * skyAlpha); + + return {topColor, bottomColor}; + } + + return {Color::Clear, Color::Clear}; +} + +Color Sky::skyFlashColor() const { + Color res = Color::White; + res.setAlphaF(m_flashTimer / m_settings.queryFloat("flashTimer")); + return res; +} + +bool Sky::flying() const { + if (m_flyingType == FlyingType::None) + return false; + return true; +} + +FlyingType Sky::flyingType() const { + return m_flyingType; +} + +float Sky::warpProgress() const { + if (m_flyingType == FlyingType::Warp) { + auto warpTime = speedupTime() + m_settings.queryFloat("flyingTimer") + slowdownTime(); + auto timer = m_flyingTimer; + if (m_warpPhase < WarpPhase::SpeedingUp) + timer += speedupTime(); + if (m_warpPhase < WarpPhase::Maintain) + timer += m_settings.queryFloat("flyingTimer"); + return timer / warpTime; + } + return 0.0; +} + +WarpPhase Sky::warpPhase() const { + return m_warpPhase; +} + +bool Sky::inHyperspace() const { + return m_flyingType == FlyingType::Warp && m_enterHyperspace; +} + +SkyRenderData Sky::renderData() const { + SkyRenderData renderData; + renderData.settings = m_settings; + renderData.skyParameters = m_skyParameters; + renderData.type = m_skyType; + renderData.dayLevel = dayLevel(); + renderData.skyAlpha = skyAlpha(); + + renderData.dayLength = dayLength(); + renderData.timeOfDay = timeOfDay(); + renderData.epochTime = epochTime(); + + renderData.starOffset = getStarOffset(); + renderData.starRotation = getStarRotation(); + renderData.worldOffset = getWorldOffset(); + renderData.worldRotation = getWorldRotation(); + renderData.orbitAngle = orbitAngle(); + + renderData.starFrames = m_starFrames; + renderData.starList = m_starList; + renderData.hyperStarList = m_hyperStarList; + + renderData.environmentLight = environmentLight(); + renderData.mainSkyColor = mainSkyColor(); + tie(renderData.topRectColor, renderData.bottomRectColor) = skyRectColors(); + renderData.flashColor = skyFlashColor(); + + return renderData; +} + +void Sky::writeNetStates() { + if (take(m_skyParametersUpdated)) + m_skyParametersNetState.set(DataStreamBuffer::serialize<Json>(m_skyParameters.toJson())); + + m_skyTypeNetState.set((int)m_skyType); + m_timeNetState.set(m_time); + m_enterHyperspaceNetState.set(m_enterHyperspace); + m_startInWarpNetState.set(m_startInWarp); + + m_flyingTypeNetState.set((unsigned)m_flyingType); + m_warpPhaseNetState.set((int)m_warpPhase); + + m_flyingTimerNetState.set(m_flyingTimer); + m_worldMoveNetState.set(m_worldMoveOffset); + m_starMoveNetState.set(m_starMoveOffset); +} + +void Sky::readNetStates() { + if (m_skyParametersNetState.pullUpdated()) + m_skyParameters = SkyParameters(DataStreamBuffer::deserialize<Json>(m_skyParametersNetState.get())); + + m_skyType = (SkyType)m_skyTypeNetState.get(); + m_time = m_timeNetState.get(); + m_enterHyperspace = m_enterHyperspaceNetState.get(); + m_startInWarp = m_startInWarpNetState.get(); + + m_flyingType = (FlyingType)m_flyingTypeNetState.get(); + m_warpPhase = (WarpPhase)m_warpPhaseNetState.get(); + stateUpdate(); + + if (!m_netInit) { + m_netInit = true; + + m_flyingTimer = m_flyingTimerNetState.get(); + m_worldMoveOffset = m_worldMoveNetState.get(); + m_starMoveOffset = m_starMoveNetState.get(); + } +} + +void Sky::enterHyperspace() { + m_flashTimer = m_settings.queryFloat("flashTimer"); + setType(SkyType::Warp); + + m_sentSFX = false; +} + +void Sky::exitHyperspace() { + m_flashTimer = m_settings.queryFloat("flashTimer"); + setType(SkyType::Orbital); + + m_sentSFX = false; + Json originMap; + Json destMap; + if (m_skyParameters.skyType == SkyType::Space) { + originMap = m_settings.getObject("spaceArrivalOrigin"); + destMap = m_settings.getArray("spaceArrivalPath")[0]; + } else { + originMap = m_settings.getObject("arrivalOrigin"); + destMap = m_settings.getArray("arrivalPath")[0]; + } + + m_pathOffset = jsonToVec2F(originMap.get("offset")); + m_pathRotation = originMap.get("rotation").toFloat() * Constants::deg2rad; + + float maxVelocity = m_settings.queryFloat("flyMaxVelocity"); + float exitDistance = maxVelocity / 2.0 * slowdownTime(); + m_worldMoveOffset = Vec2F::withAngle(0, exitDistance); + m_worldOffset = m_worldMoveOffset; + + exitDistance = (maxVelocity * m_settings.queryFloat("starVelocityFactor")) / 2.0 * slowdownTime(); + m_starMoveOffset = Vec2F::withAngle(0, exitDistance); + m_starOffset = m_starMoveOffset; + + m_worldRotation = m_starRotation = 0; + m_flyingTimer = 0; +} + +bool Sky::controlledMovement(JsonArray const& path, Json const& origin, float timeOffset) { + float previousTime = 0; + Vec2F previousOffset = jsonToVec2F(origin.get("offset")); + float previousRotation = origin.getFloat("rotation") * Constants::deg2rad; + + float stepTime = 0; + Vec2F stepOffset; + float stepRotation; + for (auto const& entry : path) { + stepOffset = jsonToVec2F(entry.get("offset")); + stepRotation = entry.getFloat("rotation") * Constants::deg2rad; + stepTime += entry.getFloat("time"); + + if (timeOffset <= stepTime) { + float percentage = (timeOffset - previousTime) / (stepTime - previousTime); + m_pathOffset = lerp(percentage, previousOffset, stepOffset); + m_pathRotation = lerp(percentage, previousRotation, stepRotation); + return false; + } + + previousTime = stepTime; + previousOffset = stepOffset; + previousRotation = stepRotation; + } + // if loop wasn't broken + // then we're done with this phase of controlled movement + // signal that we're ready to head out of this system + + return true; +} + +Vec2F Sky::getStarOffset() const { + return (m_starOffset + m_pathOffset); +} + +float Sky::getStarRotation() const { + return m_starRotation + m_pathRotation; +} + +Vec2F Sky::getWorldOffset() const { + return m_worldOffset + m_pathOffset; +} + +float Sky::getWorldRotation() const { + return m_worldRotation + m_pathRotation; +} + +float Sky::speedupTime() const { + if (m_enterHyperspace) + return m_settings.queryFloat("hyperspaceSpeedupTime"); + else + return m_settings.queryFloat("speedupTime"); +} + +float Sky::slowdownTime() const { + if (m_enterHyperspace) + return m_settings.queryFloat("hyperspaceSlowdownTime"); + else + return m_settings.queryFloat("slowdownTime"); +} + +} |