export PathNode; import saving; final class PathNode : Serializable, Savable { Object@ pathEntry; Object@ pathExit; vec3d pathTo; float dist = 0; void write(Message& msg) { msg.writeBit(pathEntry !is null); if(pathEntry !is null) { msg << pathEntry; msg << pathExit; } else { msg.writeMedVec3(pathTo); msg << dist; } } void read(Message& msg) { if(msg.readBit()) { msg >> pathEntry; msg >> pathExit; } else { @pathEntry = null; @pathExit = null; pathTo = msg.readMedVec3(); msg >> dist; } } void save(SaveFile& file) { file << pathEntry; file << pathExit; file << pathTo; file << dist; } void load(SaveFile& file) { file >> pathEntry; file >> pathExit; file >> pathTo; if(file >= SV_0086) file >> dist; } vec3d pathOut() { if(pathExit !is null) return pathExit.position; return pathTo; } #section server-side uint get_visionMask() const { if(pathExit !is null) return pathExit.visibleMask; return 0; } #section all bool valid(Object& obj) { if(pathEntry !is null && !pathEntry.valid) return false; if(pathExit !is null && !pathExit.valid) return false; /*Orbital@ orb = cast(pathEntry);*/ /*if(orb !is null && orb.Disabled)*/ /* return false;*/ /*if(pathExit !is null) {*/ /* if(!pathExit.valid)*/ /* return false;*/ /* @orb = cast(pathExit);*/ /* if(orb !is null && orb.Disabled)*/ /* return false;*/ /*}*/ return true; } }; #section server-side ReadWriteMutex mutex; array gates; export addOddityGate; void addOddityGate(Oddity& gate) { WriteLock lock(mutex); gates.insertLast(gate); } export removeOddityGate; void removeOddityGate(Oddity& gate) { WriteLock lock(mutex); gates.remove(gate); } export pathOddityGates; double pathOddityGates(Empire@ emp, array@ path, const vec3d& from, const vec3d& to, double accel = 0.0) { if(path !is null) path.length = 0; ReadLock lock(mutex); double eta = 0.0; doPathing(gates, emp, from, to, 0, path, eta, accel); return eta; } double pathOddityGates(array& gates, Empire@ emp, array@ path, const vec3d& from, const vec3d& to, double accel = 0.0) { if(path !is null) path.length = 0; double eta = 0.0; doPathing(gates, emp, from, to, 0, path, eta, accel); return eta; } export getOddityGates; void getOddityGates(array& output) { ReadLock lock(mutex); output = gates; } export grantOddityGateVision; void grantOddityGateVision(Empire& emp) { ReadLock lck(mutex); for(uint i = 0, cnt = gates.length; i < cnt; ++i) gates[i].donatedVision |= emp.mask; } double dumbETA(double dist, double accel) { return sqrt(4.0 * (dist / accel)); } export hasOddityLink; bool hasOddityLink(Region@ fromRegion, Region@ toRegion, double minDuration = 0.0) { if(fromRegion is null || toRegion is null) return false; ReadLock lck(mutex); for(uint i = 0, cnt = gates.length; i < cnt; ++i) { auto@ obj = gates[i]; if(obj.region !is fromRegion) continue; if(minDuration > 0 && obj.getTimer() < minDuration) continue; vec3d dest = obj.getGateDest(); if(dest.distanceTo(toRegion.position) < toRegion.radius) return true; } return false; } bool hasOddityLink(array& gates, Region@ fromRegion, Region@ toRegion, double minDuration = 0.0) { if(fromRegion is null || toRegion is null) return false; ReadLock lck(mutex); for(uint i = 0, cnt = gates.length; i < cnt; ++i) { auto@ obj = gates[i]; if(obj.region !is fromRegion) continue; if(minDuration > 0 && obj.getTimer() < minDuration) continue; vec3d dest = obj.getGateDest(); if(dest.distanceTo(toRegion.position) < toRegion.radius) return true; } return false; } bool hasOddityLink(Region@ fromRegion, const vec3d& nearPosition, double maxDistance, double minDuration = 0.0) { if(fromRegion is null) return false; ReadLock lck(mutex); for(uint i = 0, cnt = gates.length; i < cnt; ++i) { auto@ obj = gates[i]; if(obj.region !is fromRegion) continue; if(minDuration > 0 && obj.getTimer() < minDuration) continue; vec3d dest = obj.getGateDest(); if(dest.distanceTo(nearPosition) < maxDistance) return true; } return false; } export getPathDistance; double getPathDistance_client(Player& pl, vec3d from, vec3d to) { Empire@ emp = pl.emp; if(emp is null || !emp.valid) return from.distanceTo(to); return getPathDistance(emp, from, to); } double getPathDistance(Empire@ emp, const vec3d& from, const vec3d& to) { array path; pathOddityGates(emp, path, from, to, 1.0); return getPathDistance(from, to, path); } double getPathDistance(const vec3d& startPos, const vec3d& endPos, array@ path = null) { double distance = 0.0; vec3d pos = startPos; if(path !is null) { for(uint i = 0, cnt = path.length; i < cnt; ++i) { auto@ node = path[i]; if(node.pathEntry !is null) distance += node.pathEntry.position.distanceTo(pos); else distance += node.pathTo.distanceTo(pos); if(node.pathExit !is null) pos = node.pathExit.position; else pos = node.pathTo; } } distance += pos.distanceTo(endPos); return distance; } double getPathDistance(array& gates, Empire@ emp, const vec3d& from, const vec3d& to) { array path; pathOddityGates(gates, emp, path, from, to, 1.0); return getPathDistance(from, to, path); } export getPathETA; double getPathETA(const vec3d& startPos, const vec3d& endPos, double accel, array@ path = null) { double eta = 0.0; vec3d pos = startPos; if(path !is null) { for(uint i = 0, cnt = path.length; i < cnt; ++i) { auto@ node = path[i]; if(node.pathEntry !is null) eta += dumbETA(node.pathEntry.position.distanceTo(pos), accel); else eta += dumbETA(node.pathTo.distanceTo(pos), accel); if(node.pathExit !is null) pos = node.pathExit.position; else pos = node.pathTo; } } eta += dumbETA(pos.distanceTo(endPos), accel); return eta; } const double SQRT_2 = sqrt(2.0); uint doPathing(array& gates, Empire@ emp, const vec3d& from, const vec3d& to, uint index, array@ path, double& eta, double accel) { //Check which gate jump makes this journey shorter by the most Oddity@ shortest; Object@ shortestGateIn; Object@ shortestGateOut; double shortestDist = from.distanceTo(to); if(emp.hasStargates()) { Object@ entryGate = emp.getStargate(from); Object@ exitGate = emp.getStargate(to); if(entryGate !is null && exitGate !is null && entryGate !is exitGate) { double gateDist = from.distanceTo(entryGate.position); gateDist += exitGate.position.distanceTo(to); gateDist *= SQRT_2; if(gateDist < shortestDist) { shortestDist = gateDist; @shortestGateIn = entryGate; @shortestGateOut = exitGate; } } } for(uint i = 0, cnt = gates.length; i < cnt; ++i) { Oddity@ gate = gates[i]; if(emp !is null && !gate.isKnownTo(emp)) continue; vec3d enter = gate.position; vec3d exit = gate.getGateDest(); double enterDist = enter.distanceTo(from); double exitDist = exit.distanceTo(to); double dist = (enterDist + exitDist) * SQRT_2; if(accel > 0) { double timer = gate.getTimer(); if(timer >= 0) { double curETA = eta + dumbETA(enterDist, accel); if(curETA >= timer - 20.0) continue; } } if(dist < shortestDist) { shortestDist = dist; @shortest = gate; } } if(shortest !is null) { //Add to path PathNode node; @node.pathEntry = shortest; @node.pathExit = shortest.getLink(); if(index > path.length) path.length = index+1; path.insertAt(index, node); //Recurse into more paths uint amount = 1; amount += doPathing(gates, emp, from, shortest.position, index, path, eta, accel); eta += dumbETA(path[index+amount-1].pathOut().distanceTo(shortest.position), accel); amount += doPathing(gates, emp, shortest.getGateDest(), to, index+amount, path, eta, accel); return amount; } else if(shortestGateIn !is null) { //Add to path PathNode node; @node.pathEntry = shortestGateIn; @node.pathExit = shortestGateOut; if(index > path.length) path.length = index+1; path.insertAt(index, node); //Recurse into more paths uint amount = 1; amount += doPathing(gates, emp, from, shortestGateIn.position, index, path, eta, accel); eta += dumbETA(path[index+amount-1].pathOut().distanceTo(shortestGateIn.position), accel); amount += doPathing(gates, emp, shortestGateOut.position, to, index+amount, path, eta, accel); return amount; } return 0; }