2023-06-20 04:33:09 +00:00
|
|
|
#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)
|
2024-02-19 15:55:19 +00:00
|
|
|
: m_celestialDatabase(std::move(celestialDatabase)), m_universeClock(std::move(universeClock)) {
|
2023-06-20 04:33:09 +00:00
|
|
|
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;
|
2023-06-26 18:48:27 +00:00
|
|
|
auto planet = coordinate.isSatelliteBody() ? coordinate.parent().orbitNumber() : (coordinate.isPlanetaryBody() && coordinate.orbitNumber()) || 0;
|
2023-06-20 04:33:09 +00:00
|
|
|
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) {
|
2023-06-27 10:23:44 +00:00
|
|
|
return Root::singleton().assets()->json(strf("/system_objects.config:{}", name));
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2024-02-19 15:55:19 +00:00
|
|
|
: m_config(std::move(config)), m_uuid(std::move(uuid)), m_spawnTime(0.0f), m_parameters(std::move(parameters)) {
|
2023-06-20 04:33:09 +00:00
|
|
|
setPosition(position);
|
|
|
|
init();
|
|
|
|
}
|
|
|
|
|
|
|
|
SystemObject::SystemObject(SystemObjectConfig config, Uuid uuid, Vec2F const& position, double spawnTime, JsonObject parameters)
|
2024-02-19 15:55:19 +00:00
|
|
|
: m_config(std::move(config)), m_uuid(std::move(uuid)), m_spawnTime(std::move(spawnTime)), m_parameters(std::move(parameters)) {
|
2023-06-20 04:33:09 +00:00
|
|
|
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) {
|
2024-02-19 15:55:19 +00:00
|
|
|
m_netGroup.readNetState(std::move(data), interpolationTime);
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
2024-02-19 15:55:19 +00:00
|
|
|
: m_uuid(std::move(uuid)) {
|
2023-06-20 04:33:09 +00:00
|
|
|
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) {
|
2024-02-19 15:55:19 +00:00
|
|
|
m_netGroup.readNetState(std::move(data), interpolationTime);
|
2023-06-20 04:33:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|