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/StarSystemWorld.cpp | |
parent | 6741a057e5639280d85d0f88ba26f000baa58f61 (diff) |
everything everywhere
all at once
Diffstat (limited to 'source/game/StarSystemWorld.cpp')
-rw-r--r-- | source/game/StarSystemWorld.cpp | 638 |
1 files changed, 638 insertions, 0 deletions
diff --git a/source/game/StarSystemWorld.cpp b/source/game/StarSystemWorld.cpp new file mode 100644 index 0000000..97f948d --- /dev/null +++ b/source/game/StarSystemWorld.cpp @@ -0,0 +1,638 @@ +#include "StarSystemWorld.hpp" +#include "StarRoot.hpp" +#include "StarCelestialDatabase.hpp" +#include "StarClientContext.hpp" +#include "StarJsonExtra.hpp" +#include "StarSystemWorldServer.hpp" +#include "StarNameGenerator.hpp" + +namespace Star { + +CelestialOrbit CelestialOrbit::fromJson(Json const& json) { + return CelestialOrbit { + CelestialCoordinate(json.get("target")), + (int)json.getInt("direction"), + json.getDouble("enterTime"), + jsonToVec2F(json.get("enterPosition")) + }; +} + +Json CelestialOrbit::toJson() const { + JsonObject json; + json.set("target", target.toJson()); + json.set("direction", direction); + json.set("enterTime", enterTime); + json.set("enterPosition", jsonFromVec2F(enterPosition)); + return json; +} + +void CelestialOrbit::write(DataStream& ds) const { + ds.write(target); + ds.write(direction); + ds.write(enterTime); + ds.write(enterPosition); +} + +void CelestialOrbit::read(DataStream& ds) { + target = ds.read<CelestialCoordinate>(); + direction = ds.read<int>(); + enterTime = ds.read<double>(); + enterPosition = ds.read<Vec2F>(); +} + +bool CelestialOrbit::operator==(CelestialOrbit const& rhs) const { + return target == rhs.target + && direction == rhs.direction + && enterTime == rhs.enterTime + && enterPosition == rhs.enterPosition; +} + +DataStream& operator>>(DataStream& ds, CelestialOrbit& orbit) { + orbit.read(ds); + return ds; +} + +DataStream& operator<<(DataStream& ds, CelestialOrbit const& orbit) { + orbit.write(ds); + return ds; +} + +SystemLocation jsonToSystemLocation(Json const& json) { + if (auto location = json.optArray()) { + if (location->get(0).type() == Json::Type::String) { + String type = location->get(0).toString(); + if (type == "coordinate") + return CelestialCoordinate(location->get(1)); + else if (type == "orbit") + return CelestialOrbit::fromJson(location->get(1)); + else if (type == "object") + return Uuid(location->get(1).toString()); + } else if (auto position = jsonToMaybe<Vec2F>(*location, jsonToVec2F)) { + return *position; + } + } + return {}; +} + +Json jsonFromSystemLocation(SystemLocation const& location) { + if (auto coordinate = location.maybe<CelestialCoordinate>()) + return JsonArray{"coordinate", coordinate->toJson()}; + else if (auto orbit = location.maybe<CelestialOrbit>()) + return JsonArray{"orbit", orbit->toJson()}; + else if(auto uuid = location.maybe<Uuid>()) + return JsonArray{"object", uuid->hex()}; + else + return jsonFromMaybe<Vec2F>(location.maybe<Vec2F>(), jsonFromVec2F); +} + +SystemWorldConfig SystemWorldConfig::fromJson(Json const& json) { + SystemWorldConfig config; + config.starGravitationalConstant = json.getFloat("starGravitationalConstant"); + config.planetGravitationalConstant = json.getFloat("planetGravitationalConstant"); + + for (auto size : json.getArray("planetSizes")) + config.planetSizes.set(size.getUInt(0), size.getFloat(1)); + config.emptyOrbitSize = json.getFloat("emptyOrbitSize"); + config.unvisitablePlanetSize = json.getFloat("unvisitablePlanetSize"); + for (auto p : json.getObject("floatingDungeonWorldSizes")) + config.floatingDungeonWorldSizes.set(p.first, p.second.toFloat()); + + config.starSize = json.getFloat("starSize"); + config.planetaryOrbitPadding = jsonToVec2F(json.get("planetaryOrbitPadding")); + config.satelliteOrbitPadding = jsonToVec2F(json.get("satelliteOrbitPadding")); + + config.arrivalRange = jsonToVec2F(json.get("arrivalRange")); + + config.objectSpawnPadding = json.getFloat("objectSpawnPadding"); + config.clientObjectSpawnPadding = json.getFloat("clientObjectSpawnPadding"); + config.objectSpawnInterval = jsonToVec2F(json.get("objectSpawnInterval")); + config.objectSpawnCycle = json.getDouble("objectSpawnCycle"); + config.minObjectOrbitTime = json.getFloat("minObjectOrbitTime"); + + config.asteroidBeamDistance = json.getFloat("asteroidBeamDistance"); + + config.emptySkyParameters = SkyParameters(json.get("emptySkyParameters")); + return config; +} + +SystemWorld::SystemWorld(ClockConstPtr universeClock, CelestialDatabasePtr celestialDatabase) + : m_celestialDatabase(move(celestialDatabase)), m_universeClock(move(universeClock)) { + m_config = SystemWorldConfig::fromJson(Root::singleton().assets()->json("/systemworld.config")); +} + +SystemWorldConfig const& SystemWorld::systemConfig() const { + return m_config; +} + +double SystemWorld::time() const { + return m_universeClock->time(); +} + +Vec3I SystemWorld::location() const { + return m_location; +} + +List<CelestialCoordinate> SystemWorld::planets() const { + return m_celestialDatabase->children(CelestialCoordinate(m_location)); +} + +uint64_t SystemWorld::coordinateSeed(CelestialCoordinate const& coordinate, String const& seedMix) const { + auto satellite = coordinate.isSatelliteBody() ? coordinate.orbitNumber() : 0; + auto planet = coordinate.isSatelliteBody() ? coordinate.parent().orbitNumber() : coordinate.isPlanetaryBody() && coordinate.orbitNumber() || 0; + return staticRandomU64(coordinate.location()[0], coordinate.location()[1], coordinate.location()[2], planet, satellite, seedMix); +} + +float SystemWorld::planetOrbitDistance(CelestialCoordinate const& coordinate) const { + RandomSource random(coordinateSeed(coordinate, "PlanetOrbitDistance")); + + if (coordinate.isSystem() || coordinate.isNull()) + return 0; + + float distance = planetSize(coordinate.parent()) / 2.0; + for (int i = 0; i < coordinate.orbitNumber(); i++) { + if (i > 0 && i < coordinate.orbitNumber()) + distance += clusterSize(coordinate.parent().child(i)); + + if (coordinate.isPlanetaryBody()) + distance += random.randf(m_config.planetaryOrbitPadding[0], m_config.planetaryOrbitPadding[1]); + else if (coordinate.isSatelliteBody()) + distance += random.randf(m_config.satelliteOrbitPadding[0], m_config.satelliteOrbitPadding[1]); + } + + distance += clusterSize(coordinate) / 2.0; + + return distance; +} + +float SystemWorld::orbitInterval(float distance, bool isMoon) const { + float gravityConstant = isMoon ? m_config.planetGravitationalConstant : m_config.starGravitationalConstant; + float speed = sqrt(gravityConstant / distance); + return (distance * 2 * Constants::pi) / speed; +} + +Vec2F SystemWorld::orbitPosition(CelestialOrbit const& orbit) const { + Vec2F targetPosition = orbit.target.isPlanetaryBody() || orbit.target.isSatelliteBody() ? planetPosition(orbit.target) : Vec2F(0, 0); + float distance = orbit.enterPosition.magnitude(); + float interval = orbitInterval(distance, false); + + float timeOffset = fmod(time() - orbit.enterTime, interval) / interval; + float angle = (orbit.enterPosition * -1).angle() + (orbit.direction * timeOffset * (Constants::pi * 2)); + return targetPosition + Vec2F::withAngle(angle, distance); +} + +float SystemWorld::clusterSize(CelestialCoordinate const& coordinate) const { + if (coordinate.isPlanetaryBody() && m_celestialDatabase->childOrbits(coordinate.parent()).contains(coordinate.orbitNumber())) { + auto childOrbits = m_celestialDatabase->childOrbits(coordinate).sorted(); + if (childOrbits.size() > 0) { + CelestialCoordinate outer = coordinate.child(childOrbits.get(childOrbits.size() - 1)); + return (planetOrbitDistance(outer) * 2) + planetSize(outer); + } else { + return planetSize(coordinate); + } + } else { + return planetSize(coordinate); + } +}; + +float SystemWorld::planetSize(CelestialCoordinate const& coordinate) const { + if (coordinate.isNull()) + return 0; + + if (coordinate.isSystem()) + return m_config.starSize; + + if (!m_celestialDatabase->childOrbits(coordinate.parent()).contains(coordinate.orbitNumber())) + return m_config.emptyOrbitSize; + + if (auto parameters = m_celestialDatabase->parameters(coordinate)) { + if (auto visitableParameters = parameters->visitableParameters()) { + float size = 0; + if (is<FloatingDungeonWorldParameters>(visitableParameters)) { + if (auto s = m_config.floatingDungeonWorldSizes.maybe(visitableParameters->typeName)) + return *s; + } + for (auto s : m_config.planetSizes) { + if (visitableParameters->worldSize[0] >= s.first) + size = s.second; + else + break; + } + return size; + } + } + return m_config.unvisitablePlanetSize; +} + +Vec2F SystemWorld::planetPosition(CelestialCoordinate const& coordinate) const { + if (coordinate.isNull() || coordinate.isSystem()) + return {0.0, 0.0}; + + RandomSource random(coordinateSeed(coordinate, "PlanetSystemPosition")); + + Vec2F parentPosition = planetPosition(coordinate.parent()); + float distance = planetOrbitDistance(coordinate); + float interval = orbitInterval(distance, coordinate.isSatelliteBody()); + + double start = random.randf(); + double offset = (fmod(m_universeClock->time(), interval) / interval); + int direction = random.randf() > 0.5 ? 1 : -1; + float angle = (start + direction * offset) * (Constants::pi * 2); + + return parentPosition + Vec2F(cos(angle), sin(angle)) * distance; +} + +SystemObjectConfig SystemWorld::systemObjectConfig(String const& name, Uuid const& uuid) const { + RandomSource rand(staticRandomU64(uuid.hex())); + + SystemObjectConfig object; + auto config = systemObjectTypeConfig(name); + auto orbitRange = jsonToVec2F(config.get("orbitRange")); + auto lifeTimeRange = jsonToVec2F(config.get("lifeTime")); + + object.name = name; + + object.moving = config.getBool("moving"); + object.speed = config.getFloat("speed"); + object.orbitDistance = Random::randf(orbitRange[0], orbitRange[1]); + object.lifeTime = Random::randf(lifeTimeRange[0], lifeTimeRange[1]); + + object.permanent = config.getBool("permanent", false); + + object.warpAction = parseWarpAction(config.getString("warpAction")); + object.threatLevel = config.optFloat("threatLevel"); + object.skyParameters = SkyParameters(config.get("skyParameters")); + object.parameters = config.getObject("parameters"); + + if (config.contains("generatedParameters")) { + for (auto p : config.getObject("generatedParameters")) + object.generatedParameters[p.first] = p.second.toString(); + } + + return object; +} + +Json SystemWorld::systemObjectTypeConfig(String const& name) { + return Root::singleton().assets()->json(strf("/system_objects.config:%s", name)); +} + +Maybe<Vec2F> SystemWorld::systemLocationPosition(SystemLocation const& location) const { + if (auto coordinate = location.maybe<CelestialCoordinate>()) { + return planetPosition(*coordinate); + } else if (auto orbit = location.maybe<CelestialOrbit>()) { + return orbitPosition(*orbit); + } else if (auto objectUuid = location.maybe<Uuid>()) { + if (auto object = getObject(*objectUuid)) + return object->position(); + } else if (auto position = location.maybe<Vec2F>()) { + return Vec2F(*position); + } + return {}; +} + +Vec2F SystemWorld::randomArrivalPosition() const { + RandomSource rand; + float range = m_config.arrivalRange[0] + (rand.randf() * (m_config.arrivalRange[1] - m_config.arrivalRange[0])); + float angle = rand.randf() * Constants::pi * 2; + return Vec2F::withAngle(angle, range); +} + +Maybe<WarpAction> SystemWorld::objectWarpAction(Uuid const& uuid) const { + if (auto object = getObject(uuid)) { + WarpAction warpAction = object->warpAction(); + if (auto warpToWorld = warpAction.ptr<WarpToWorld>()) { + if (auto instanceWorldId = warpToWorld->world.ptr<InstanceWorldId>()) { + instanceWorldId->uuid = object->uuid(); + if (auto parameters = m_celestialDatabase->parameters(CelestialCoordinate(m_location))) { + Maybe<float> systemThreatLevel = parameters->getParameter("spaceThreatLevel").optFloat(); + instanceWorldId->level = object->threatLevel().orMaybe(systemThreatLevel); + } else { + return {}; + } + } + } + return warpAction; + } else { + return {}; + } +} + +SystemObject::SystemObject(SystemObjectConfig config, Uuid uuid, Vec2F const& position, JsonObject parameters) + : m_config(move(config)), m_uuid(move(uuid)), m_spawnTime(0.0f), m_parameters(move(parameters)) { + setPosition(position); + init(); +} + +SystemObject::SystemObject(SystemObjectConfig config, Uuid uuid, Vec2F const& position, double spawnTime, JsonObject parameters) + : m_config(move(config)), m_uuid(move(uuid)), m_spawnTime(move(spawnTime)), m_parameters(move(parameters)) { + setPosition(position); + for (auto p : m_config.generatedParameters) { + if (!m_parameters.contains(p.first)) + m_parameters[p.first] = Root::singleton().nameGenerator()->generateName(p.second); + } + init(); +} + +SystemObject::SystemObject(SystemWorld* system, Json const& diskStore) { + m_uuid = Uuid(diskStore.getString("uuid")); + auto name = diskStore.getString("name"); + m_config = system->systemObjectConfig(name, m_uuid); + m_parameters = diskStore.getObject("parameters", {}); + + m_orbit.set(jsonToMaybe<CelestialOrbit>(diskStore.get("orbit"), [](Json const& json) { + return CelestialOrbit::fromJson(json); + })); + + m_spawnTime = diskStore.getDouble("spawnTime"); + + setPosition(jsonToVec2F(diskStore.get("position"))); + + init(); +} + +void SystemObject::init() { + m_shouldDestroy = false; + + m_xPosition.setInterpolator(lerp<float, float>); + m_yPosition.setInterpolator(lerp<float, float>); + + m_netGroup.addNetElement(&m_xPosition); + m_netGroup.addNetElement(&m_yPosition); + m_netGroup.addNetElement(&m_orbit); +} + +Uuid SystemObject::uuid() const { + return m_uuid; +} + +String SystemObject::name() const { + return m_config.name; +} + +bool SystemObject::permanent() const { + return m_config.permanent; +} + +Vec2F SystemObject::position() const { + return Vec2F(m_xPosition.get(), m_yPosition.get()); +} + +WarpAction SystemObject::warpAction() const { + return m_config.warpAction; +} + +Maybe<float> SystemObject::threatLevel() const { + return m_config.threatLevel; +} + +SkyParameters SystemObject::skyParameters() const { + return m_config.skyParameters; +} + +JsonObject SystemObject::parameters() const { + return jsonMerge(m_config.parameters, m_parameters).toObject(); +} + +bool SystemObject::shouldDestroy() const { + return m_shouldDestroy; +} + +void SystemObject::enterOrbit(CelestialCoordinate const& target, Vec2F const& targetPosition, double time) { + int direction = Random::randf() > 0.5 ? 1 : -1; // random direction + m_orbit.set(CelestialOrbit{target, direction, time, targetPosition - position()}); + m_approach.reset(); +} + +Maybe<CelestialCoordinate> SystemObject::orbitTarget() const { + if (m_orbit.get()) + return m_orbit.get()->target; + else + return {}; +} + +Maybe<CelestialOrbit> SystemObject::orbit() const { + return m_orbit.get(); +} + +void SystemObject::clientUpdate(float dt) { + m_netGroup.tickNetInterpolation(dt); +} + +void SystemObject::serverUpdate(SystemWorldServer* system, float dt) { + if (!m_config.permanent && m_spawnTime > 0.0 && system->time() > m_spawnTime + m_config.lifeTime) + m_shouldDestroy = true; + + if (m_orbit.get()) { + setPosition(system->orbitPosition(*m_orbit.get())); + } else if (m_config.permanent || !m_config.moving) { + // permanent locations always have a solar orbit + enterOrbit(CelestialCoordinate(system->location()), {0.0, 0.0}, system->time()); + } else if (m_approach && !m_approach->isNull()) { + + if (system->shipsAtLocation(m_uuid).size() > 0) + return; + + if (m_approach->isPlanetaryBody()) { + auto approach = system->planetPosition(*m_approach); + auto toApproach = (approach - position()); + auto pos = position(); + setPosition(pos + toApproach.normalized() * m_config.speed * dt); + + if ((approach - position()).magnitude() < system->planetSize(*m_approach) + m_config.orbitDistance) + enterOrbit(*m_approach, approach, system->time()); + } else { + enterOrbit(*m_approach, {0.0, 0.0}, system->time()); + } + } else { + auto planets = system->planets().filtered([system](CelestialCoordinate const& p) { + auto objectsAtPlanet = system->objects().filtered([p](SystemObjectPtr const& o) { return o->orbitTarget() == p; }); + return objectsAtPlanet.size() == 0; + }); + + if (planets.size() > 0) + m_approach = Random::randFrom(planets); + } +} + +pair<ByteArray, uint64_t> SystemObject::writeNetState(uint64_t fromVersion) { + return m_netGroup.writeNetState(fromVersion); +} + +void SystemObject::readNetState(ByteArray data, float interpolationTime) { + m_netGroup.readNetState(move(data), interpolationTime); +} + +ByteArray SystemObject::netStore() const { + DataStreamBuffer ds; + ds.write(m_uuid); + ds.write(m_config.name); + ds.write(position()); + ds.write(m_parameters); + return ds.takeData(); +} + +Json SystemObject::diskStore() const { + JsonObject store; + store.set("uuid", m_uuid.hex()); + store.set("name", m_config.name); + store.set("orbit", jsonFromMaybe<CelestialOrbit>(m_orbit.get(), [](CelestialOrbit const& o) { + return o.toJson(); + })); + store.set("spawnTime", m_spawnTime); + store.set("position", jsonFromVec2F(position())); + store.set("parameters", m_parameters); + return store; +} + +void SystemObject::setPosition(Vec2F const& position) { + m_xPosition.set(position[0]); + m_yPosition.set(position[1]); +} + +SystemClientShip::SystemClientShip(SystemWorld* system, Uuid uuid, float speed, SystemLocation const& location) + : m_uuid(move(uuid)) { + m_systemLocation.set(location); + setPosition(system->systemLocationPosition(location).value({})); + + // temporary + auto shipConfig = Root::singleton().assets()->json("/systemworld.config:clientShip"); + m_config = ClientShipConfig{ + shipConfig.getFloat("orbitDistance"), + shipConfig.getFloat("departTime"), + shipConfig.getFloat("spaceDepartTime") + }; + m_speed = speed; + m_departTimer = 0.0f; + + // systemLocation should not be interpolated + // if it's stale it can point to a removed system object + m_netGroup.addNetElement(&m_systemLocation, false); + m_netGroup.addNetElement(&m_destination); + + m_netGroup.addNetElement(&m_xPosition); + m_netGroup.addNetElement(&m_yPosition); + m_netGroup.enableNetInterpolation(); + + m_xPosition.setInterpolator(lerp<float, float>); + m_yPosition.setInterpolator(lerp<float, float>); +} + +SystemClientShip::SystemClientShip(SystemWorld* system, Uuid uuid, SystemLocation const& location) + : SystemClientShip(system, uuid, 0.0f, location) {} + +Uuid SystemClientShip::uuid() const { + return m_uuid; +} + +Vec2F SystemClientShip::position() const { + return {m_xPosition.get(), m_yPosition.get()}; +} + +SystemLocation SystemClientShip::systemLocation() const { + return m_systemLocation.get(); +} + +SystemLocation SystemClientShip::destination() const { + return m_destination.get(); +} + +void SystemClientShip::setDestination(SystemLocation const& destination) { + auto location = m_systemLocation.get(); + if (location.is<CelestialCoordinate>() || location.is<Uuid>()) + m_departTimer = m_config.departTime; + else if(m_destination.get().empty()) + m_departTimer = m_config.spaceDepartTime; + m_destination.set(destination); + m_systemLocation.set({}); +} + +void SystemClientShip::setSpeed(float speed) { + m_speed = speed; +} + +bool SystemClientShip::flying() const { + return m_systemLocation.get().empty(); +} + +void SystemClientShip::clientUpdate(float dt) { + m_netGroup.tickNetInterpolation(dt); +} + +void SystemClientShip::serverUpdate(SystemWorld* system, float dt) { + // if destination is an orbit we haven't started orbiting yet, update the time + if (auto orbit = m_destination.get().maybe<CelestialOrbit>()) + orbit->enterTime = system->time(); + + auto nearPlanetOrbit = [this,system](CelestialCoordinate const& planet) -> CelestialOrbit { + Vec2F toShip = system->planetPosition(planet) - position(); + return CelestialOrbit { + planet, + 1, + system->time(), + Vec2F::withAngle(toShip.angle(), system->planetSize(planet) / 2.0 + m_config.orbitDistance) + }; + }; + + if (auto coordinate = m_systemLocation.get().maybe<CelestialCoordinate>()) { + if (!m_orbit || m_orbit->target != *coordinate) + m_orbit = nearPlanetOrbit(*coordinate); + } else if (m_systemLocation.get().empty()) { + m_departTimer = max(0.0f, m_departTimer - dt); + if (m_departTimer > 0.0f) + return; + + if (auto coordinate = m_destination.get().maybe<CelestialCoordinate>()) { + if (!m_orbit || m_orbit->target != *coordinate) + m_orbit = nearPlanetOrbit(*coordinate); + } else { + m_orbit.reset(); + } + + Vec2F pos = position(); + Vec2F destination; + if (m_orbit) { + m_orbit->enterTime = system->time(); + destination = system->orbitPosition(*m_orbit); + } else { + destination = system->systemLocationPosition(m_destination.get()).value(pos); + } + + auto toTarget = destination - pos; + pos += toTarget.normalized() * (m_speed * dt); + + if (destination == pos || (destination - pos).normalized() * toTarget.normalized() < 0) { + m_systemLocation.set(m_destination.get()); + m_destination.set({}); + } else { + setPosition(pos); + return; + } + } + + if (m_orbit) { + setPosition(system->systemLocationPosition(*m_orbit).get()); + } else { + setPosition(system->systemLocationPosition(m_systemLocation.get()).get()); + } +} + +pair<ByteArray, uint64_t> SystemClientShip::writeNetState(uint64_t fromVersion) { + return m_netGroup.writeNetState(fromVersion); +} + +void SystemClientShip::readNetState(ByteArray data, float interpolationTime) { + m_netGroup.readNetState(move(data), interpolationTime); +} + +ByteArray SystemClientShip::netStore() const { + DataStreamBuffer ds; + ds.write(m_uuid); + ds.write(m_systemLocation.get()); + return ds.takeData(); +} + +void SystemClientShip::setPosition(Vec2F const& position) { + m_xPosition.set(position[0]); + m_yPosition.set(position[1]); +} + +} |