1552 lines
37 KiB
ActionScript
1552 lines
37 KiB
ActionScript
import oddity_navigation;
|
|
from regions.regions import getRegion;
|
|
import saving;
|
|
import ftl;
|
|
|
|
const double straighDot = 0.99999;
|
|
|
|
//Rotation rate in radians/s
|
|
const double shipRotSpeed = 0.1;
|
|
const double SQRT_2 = sqrt(2.0);
|
|
|
|
double lowerQuadratic(double a, double b, double c) {
|
|
double d = sqrt(b*b - 4.0 * a * c);
|
|
if(d != d)
|
|
return 10000.0;
|
|
//double r1 = (-b + d) / (2.0 * a);
|
|
double r2 = (-b - d) / (2.0 * a);
|
|
if(r2 > 0.0)
|
|
return r2;
|
|
else
|
|
return 10000.0;
|
|
}
|
|
|
|
double timeToTarg(double a, const vec3d& offset, const vec3d& relVel) {
|
|
double dist = offset.length, speed = relVel.length;
|
|
if(speed < 0.05) {
|
|
return sqrt(4.0 * dist / a);
|
|
}
|
|
|
|
double velDot = 1.0;
|
|
if(dist > 0.001)
|
|
velDot = relVel.dot(offset) / (dist * speed);
|
|
if(velDot > straighDot) {
|
|
//We must accelerate up to the target speed before hitting the point
|
|
|
|
// d = 1/2 a * t^2 (where t = v/a)
|
|
double accelDist = 0.5 * speed * speed / a;
|
|
|
|
if(accelDist > dist) {
|
|
//return (speed / a) + sqrt(4.0 * (accelDist - dist) / a);
|
|
//return (speed / a) + (accelDist - dist) / speed;
|
|
|
|
return lowerQuadratic(-a, 2.0 * speed, dist - accelDist);
|
|
}
|
|
else {
|
|
//We have enough distance
|
|
//Accelerate until we must decelerate
|
|
double totalTime = sqrt(4.0 * (dist - accelDist) / a);
|
|
return totalTime + (speed / a);
|
|
}
|
|
}
|
|
else if(velDot < -straighDot) {
|
|
double deccelTime = speed / a;
|
|
double deccelDist = deccelTime * speed * 0.5;
|
|
|
|
//We either need to slow down, or accelerate to a maximum velocity
|
|
if(deccelDist < dist) {
|
|
double totalTime = sqrt(4.0 * (deccelDist + dist) / a);
|
|
return totalTime - (speed / a);
|
|
}
|
|
else {
|
|
return deccelTime + sqrt(4.0 * (deccelDist - dist) / a);
|
|
}
|
|
}
|
|
else {
|
|
vec3d linearVel = offset.normalized(speed * velDot);
|
|
vec3d latVel = relVel - linearVel;
|
|
vec3d zero;
|
|
|
|
double aToward = 0.7;
|
|
|
|
double rangeLow = 0.0001, rangeHigh = 0.9999;
|
|
double t = 0, leastErr = 99999999.0;
|
|
for(uint i = 0; i < 15; ++i) {
|
|
double aLat = sqrt(1.0 - (aToward * aToward));
|
|
|
|
double tToward = timeToTarg(aToward * a, offset, linearVel);
|
|
double tLat = timeToTarg(aLat * a, zero, latVel);
|
|
|
|
double err = abs(tToward - tLat);
|
|
if(err < leastErr) {
|
|
leastErr = err;
|
|
t = (tToward + tLat) * 0.5;
|
|
}
|
|
|
|
if(err < 0.02)
|
|
break;
|
|
else if(tToward > tLat) {
|
|
rangeLow = aToward;
|
|
aToward = (rangeLow + rangeHigh) * 0.5;
|
|
}
|
|
else {
|
|
rangeHigh = aToward;
|
|
aToward = (rangeLow + rangeHigh) * 0.5;
|
|
}
|
|
}
|
|
|
|
return t;
|
|
}
|
|
}
|
|
|
|
vec3d accToGoal(double a, double& maxTime, const vec3d& offset, const vec3d& relVel) {
|
|
double dist = offset.length, speed = relVel.length;
|
|
|
|
if(speed < 0.05) {
|
|
//Accelerates for half the time, decelerates for half the time
|
|
maxTime = sqrt(4.0 * dist / a) * 0.5;
|
|
return offset.normalized(a);
|
|
}
|
|
else {
|
|
double velDot = 1.0;
|
|
if(dist > 0.001)
|
|
velDot = relVel.dot(offset) / (dist * speed);
|
|
if(velDot > straighDot) {
|
|
//We must accelerate up to the target speed before hitting the point
|
|
|
|
// d = 1/2 a * t^2 (where t = v/a)
|
|
double accelDist = 0.5 * speed * speed / a;
|
|
|
|
if(accelDist > dist) {
|
|
//error(accelDist + " vs " + dist);
|
|
//maxTime = sqrt(4.0 * (accelDist - dist) / a) * 0.5;
|
|
//return offset.normalized(-a);
|
|
//maxTime = (accelDist - dist) / speed;
|
|
//return vec3d();
|
|
maxTime = lowerQuadratic(-a, 2.0 * speed, dist - 0.5 * accelDist);
|
|
return offset.normalized(a);
|
|
}
|
|
else {
|
|
//We have enough distance
|
|
//Accelerate until we must decelerate
|
|
maxTime = speed / a;
|
|
return offset.normalized(a);
|
|
}
|
|
}
|
|
else if(velDot < -straighDot) {
|
|
double deccelTime = speed / a;
|
|
double deccelDist = deccelTime * speed * 0.5;
|
|
if(deccelDist < dist) {
|
|
//Determine our time remaining based on the original null-vel curve
|
|
double totalTime = sqrt(4.0 * (deccelDist + dist) / a);
|
|
maxTime = (totalTime * 0.5) - (speed / a);
|
|
return offset.normalized(a);
|
|
}
|
|
else {
|
|
maxTime = speed / a;
|
|
return relVel.normalized(a);
|
|
}
|
|
}
|
|
else {
|
|
vec3d linearVel = offset.normalized(speed * velDot);
|
|
vec3d latVel = relVel - linearVel;
|
|
vec3d zero;
|
|
|
|
double rangeLow = 0.0001, rangeHigh = 0.9999;
|
|
double aToward = 0.7;
|
|
double aLat;
|
|
|
|
double t = 0, leastErr = 999999999.0;
|
|
|
|
for(uint i = 0; i < 15; ++i) {
|
|
aLat = sqrt(1.0 - (aToward * aToward));
|
|
|
|
double tToward = timeToTarg(aToward * a, offset, linearVel);
|
|
//double tLat = timeToTarg(aLat * a, zero, latVel);
|
|
|
|
double tLat = latVel.length / (aLat * a);
|
|
double latDist = latVel.length * tLat * 0.5;
|
|
tLat += sqrt(4.0 * latDist / (aLat * a));
|
|
|
|
double err = abs(tToward - tLat);
|
|
if(err < leastErr) {
|
|
leastErr = err;
|
|
t = (tToward + tLat) * 0.5;
|
|
}
|
|
|
|
if(err < 0.02)
|
|
break;
|
|
else if(tToward > tLat) {
|
|
rangeLow = aToward;
|
|
aToward = (rangeLow + rangeHigh) * 0.5;
|
|
}
|
|
else {
|
|
rangeHigh = aToward;
|
|
aToward = (rangeLow + rangeHigh) * 0.5;
|
|
}
|
|
}
|
|
aLat = sqrt(1.0 - (aToward * aToward));
|
|
|
|
double maxT1 = 0, maxT2 = 0;
|
|
|
|
vec3d linAcc = accToGoal(aToward * a, maxT1, offset, linearVel);
|
|
//vec3d latAcc = accToGoal(aLat * a, maxT2, zero, latVel);
|
|
vec3d latAcc = latVel.normalize(aLat * a); maxT2 = latVel.length / (aLat * a);
|
|
|
|
maxTime = min(maxT1, maxT2);
|
|
return latAcc + linAcc;
|
|
}
|
|
}
|
|
}
|
|
|
|
tidy class Mover : Component_Mover, Savable {
|
|
Object@ target;
|
|
vec3d destination;
|
|
quaterniond prevFormationDest;
|
|
quaterniond targRot;
|
|
quaterniond targFacing;
|
|
quaterniond combatFacing;
|
|
vec3d compDestination;
|
|
bool inCombat = false;
|
|
double targDist = 0;
|
|
int prevPathId = 0;
|
|
array<PathNode@>@ path;
|
|
|
|
Object@ lockTo;
|
|
bool isLocked = false;
|
|
vec3d lockOffset;
|
|
float rotSpeed = shipRotSpeed;
|
|
bool vectorMovement = false;
|
|
bool fleetRelative = true;
|
|
|
|
const Object@ colliding;
|
|
|
|
double accel = 1.0;
|
|
double accelBonus = 0;
|
|
bool moving = false;
|
|
bool rotating = false;
|
|
bool moverDelta = false;
|
|
bool posDelta = false;
|
|
bool facingDelta = false;
|
|
int moveID = 0;
|
|
int syncedID = 0;
|
|
|
|
bool FTL = false;
|
|
double FTLSpeed = 1.0;
|
|
|
|
Mover() {
|
|
}
|
|
|
|
void load(SaveFile& data) {
|
|
data >> target;
|
|
data >> destination;
|
|
data >> accel;
|
|
if(data >= SV_0100)
|
|
data >> accelBonus;
|
|
data >> moving;
|
|
data >> rotating;
|
|
data >> lockTo;
|
|
data >> isLocked;
|
|
data >> lockOffset;
|
|
data >> moveID;
|
|
data >> FTL;
|
|
data >> FTLSpeed;
|
|
data >> targDist;
|
|
data >> combatFacing;
|
|
data >> targFacing;
|
|
data >> inCombat;
|
|
if(data >= SV_0126)
|
|
data >> vectorMovement;
|
|
if(data >= SV_0048)
|
|
data >> rotSpeed;
|
|
if(data >= SV_0054)
|
|
data >> fleetRelative;
|
|
|
|
if(data >= SV_0009) {
|
|
uint cnt = 0;
|
|
data >> cnt;
|
|
if(cnt > 0) {
|
|
@path = array<PathNode@>(cnt);
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
@path[i] = PathNode();
|
|
data >> path[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
if(data >= SV_0067)
|
|
data >> prevPathId;
|
|
}
|
|
|
|
void save(SaveFile& data) {
|
|
data << target;
|
|
data << destination;
|
|
data << accel;
|
|
data << accelBonus;
|
|
data << moving;
|
|
data << rotating;
|
|
data << lockTo;
|
|
data << isLocked;
|
|
data << lockOffset;
|
|
data << moveID;
|
|
data << FTL;
|
|
data << FTLSpeed;
|
|
data << targDist;
|
|
data << combatFacing;
|
|
data << targFacing;
|
|
data << inCombat;
|
|
data << vectorMovement;
|
|
data << rotSpeed;
|
|
data << fleetRelative;
|
|
|
|
uint cnt = path is null ? 0 : path.length;
|
|
data << cnt;
|
|
for(uint i = 0; i < cnt; ++i)
|
|
data << path[i];
|
|
data << prevPathId;
|
|
}
|
|
|
|
void destroy() {
|
|
@target = null;
|
|
}
|
|
|
|
Object@ getLockedOrbit(bool requireLock = true) {
|
|
if(requireLock && !isLocked)
|
|
return null;
|
|
return lockTo;
|
|
}
|
|
|
|
bool hasLockedOrbit(bool requireLock = true) {
|
|
if(requireLock && !isLocked)
|
|
return false;
|
|
return lockTo !is null;
|
|
}
|
|
|
|
bool isLockedOrbit(Object@ at, bool requireLock = true) {
|
|
if(requireLock && !isLocked)
|
|
return false;
|
|
if(lockTo is at)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
Object@ getAroundLockedOrbit(Object& obj) {
|
|
if(lockTo is null)
|
|
return null;
|
|
if(lockTo.isPlanet) {
|
|
double maxDist = cast<Planet>(lockTo).OrbitSize;
|
|
if(obj.position.distanceToSQ(lockTo.position) > maxDist * maxDist)
|
|
return null;
|
|
}
|
|
return lockTo;
|
|
}
|
|
|
|
double get_ftlSpeed() {
|
|
return FTLSpeed;
|
|
}
|
|
|
|
void set_ftlSpeed(double value) {
|
|
if(FTLSpeed == value)
|
|
return;
|
|
FTLSpeed = value;
|
|
moverDelta = true;
|
|
}
|
|
|
|
bool FTLTo(Object& obj, vec3d target, double speed, int& id) {
|
|
if(id > 0 && id == moveID)
|
|
return !FTL;
|
|
if(FTL)
|
|
return false;
|
|
moveTo(obj, target, id, false);
|
|
FTL = true;
|
|
FTLSpeed = max(speed, 1.0);
|
|
return false;
|
|
}
|
|
|
|
void FTLTo(Object& obj, vec3d target, double speed) {
|
|
if(FTL)
|
|
return;
|
|
int id = -1;
|
|
moveTo(obj, target, id, false);
|
|
FTL = true;
|
|
FTLSpeed = max(speed, 1.0);
|
|
}
|
|
|
|
void FTLDrop(Object& obj) {
|
|
if(!FTL)
|
|
return;
|
|
obj.velocity = vec3d();
|
|
obj.acceleration = vec3d();
|
|
FTL = false;
|
|
FTLSpeed = 0;
|
|
stopMoving(obj);
|
|
moverDelta = true;
|
|
posDelta = true;
|
|
}
|
|
|
|
bool get_isColliding() const {
|
|
return colliding !is null;
|
|
}
|
|
|
|
bool get_inFTL() const {
|
|
return FTL;
|
|
}
|
|
|
|
bool get_isMoving(const Object& obj) const {
|
|
return moving || rotating || FTL;
|
|
}
|
|
|
|
vec3d get_internalDestination() const {
|
|
return destination;
|
|
}
|
|
|
|
vec3d get_computedDestination() const {
|
|
return compDestination;
|
|
}
|
|
|
|
vec3d get_moveDestination(const Object& obj) const {
|
|
if(target !is null) {
|
|
vec3d dir = (target.position - obj.position);
|
|
return obj.position + dir.normalized(dir.length - targDist);
|
|
}
|
|
if(lockTo !is null)
|
|
return lockTo.position + lockOffset;
|
|
if(obj.hasSupportAI) {
|
|
const Ship@ ship = cast<const Ship>(obj);
|
|
Object@ leader = ship.Leader;
|
|
|
|
if(leader !is null) {
|
|
Ship@ leaderShip = cast<Ship>(leader);
|
|
quaterniond formationFacing;
|
|
if(leaderShip !is null)
|
|
formationFacing = leaderShip.formationDest;
|
|
|
|
if(fleetRelative)
|
|
return leader.position + (formationFacing * ship.formationDest.xyz);
|
|
else
|
|
return leader.position + destination;
|
|
}
|
|
}
|
|
return destination;
|
|
}
|
|
|
|
bool get_hasMovePath() const {
|
|
return path !is null && path.length > 0;
|
|
}
|
|
|
|
bool get_hasMovePortal() {
|
|
return path !is null && path.length > 0 && path[0].pathEntry !is null;
|
|
}
|
|
|
|
vec3d getMovePortal() {
|
|
if(path is null || path.length == 0)
|
|
return vec3d();
|
|
return path[0].pathOut();
|
|
}
|
|
|
|
void getMovePath(const Object& obj) const {
|
|
if(path is null)
|
|
return;
|
|
|
|
Object@ prev;
|
|
for(uint i = 0, cnt = path.length; i < cnt; ++i) {
|
|
auto@ node = path[i];
|
|
if(node.pathEntry !is prev)
|
|
yield(node.pathEntry);
|
|
if(node.pathExit !is null)
|
|
yield(node.pathExit);
|
|
}
|
|
}
|
|
|
|
double get_maxAcceleration() const {
|
|
return accel;
|
|
}
|
|
|
|
void set_rotationSpeed(float amt) {
|
|
if(rotSpeed != amt) {
|
|
rotSpeed = amt;
|
|
moverDelta = true;
|
|
}
|
|
}
|
|
|
|
void set_hasVectorMovement(bool value) {
|
|
if(vectorMovement != value) {
|
|
vectorMovement = value;
|
|
moverDelta = true;
|
|
}
|
|
}
|
|
|
|
void set_maxAcceleration(Object& obj, double Accel) {
|
|
if(accel != Accel) {
|
|
moverDelta = true;
|
|
accel = Accel + accelBonus;
|
|
}
|
|
}
|
|
|
|
void modAccelerationBonus(double mod) {
|
|
accel += mod;
|
|
accelBonus += mod;
|
|
moverDelta = true;
|
|
}
|
|
|
|
bool get_leaderLock() {
|
|
return fleetRelative;
|
|
}
|
|
|
|
void set_leaderLock(bool doLock) {
|
|
if(fleetRelative != doLock) {
|
|
fleetRelative = doLock;
|
|
//If we're changing to a locked state, we need a delta (other steps are responsible for this when a lock is released)
|
|
if(doLock)
|
|
moverDelta = true;
|
|
}
|
|
}
|
|
|
|
void impulse(Object& obj, vec3d ForceSeconds) {
|
|
obj.velocity += ForceSeconds;
|
|
moving = true;
|
|
moverDelta = true;
|
|
posDelta = true;
|
|
|
|
if(obj.hasOrbit && obj.inOrbit)
|
|
obj.stopOrbit();
|
|
}
|
|
|
|
void rotate(Object& obj, quaterniond rot) {
|
|
obj.rotation *= rot;
|
|
rotating = true;
|
|
moverDelta = true;
|
|
}
|
|
|
|
double timeToTarget(Object& obj, double a, const vec3d& point, const vec3d& velocity) {
|
|
//NOTE: The engine implements an identical implementation of timeToTarg for performance reasons
|
|
//return timeToTarg(a, point - obj.position, velocity - obj.velocity);
|
|
return newtonArrivalTime(a, point - obj.position, velocity - obj.velocity);
|
|
}
|
|
|
|
void speedBoost(Object& obj, double amount) {
|
|
//Note to self: try to keep any cleaver-like objects away from
|
|
//Reaper for a while after this code gets committed. Maybe go into
|
|
//hiding a few years.
|
|
|
|
if(accel == 0)
|
|
return;
|
|
vec3d acc = obj.acceleration.normalized();
|
|
vec3d dist = get_moveDestination(obj) - obj.position;;
|
|
double distLen = dist.length;
|
|
double velLen = obj.velocity.length;
|
|
if(amount > 0 && distLen < velLen * 2.0)
|
|
return;
|
|
moverDelta = true;
|
|
dist /= distLen;
|
|
|
|
if(amount < 0) {
|
|
if(acc.angleDistance(dist) < pi) {
|
|
//Remove some of our velocity-to-target.
|
|
// The move algorithm will compensate, since we're
|
|
// still accelerating towards it.
|
|
obj.velocity.length = velLen + amount;
|
|
}
|
|
else {
|
|
//We're slowing down. Don't slow down below what would be
|
|
//a sane-ish speed for this ship *ducks*.
|
|
double sane = min(velLen, distLen / accel);
|
|
obj.velocity.length = max(sane, velLen + amount);
|
|
}
|
|
}
|
|
else {
|
|
if(acc.angleDistance(dist) < pi) {
|
|
//Fucking full speed ahead. Who gives a shit.
|
|
obj.velocity += dist * amount;
|
|
}
|
|
}
|
|
}
|
|
|
|
vec3d accelToGoal(Object& obj, double a, double& maxTime, const vec3d& point, const vec3d& velocity) {
|
|
vec3d offset = point - obj.position;
|
|
vec3d relVel = velocity - obj.velocity;
|
|
return accToGoal(a, maxTime, offset, relVel);
|
|
}
|
|
|
|
double moverTick(Object& obj, double time) {
|
|
if(time <= 0)
|
|
return 0.1;
|
|
|
|
if(!moving && !inFTL && obj.hasOrbit && obj.inOrbit) {
|
|
obj.orbitTick(time);
|
|
return 0.25;
|
|
}
|
|
|
|
//Push away from nearby objects
|
|
if(!obj.isPlanet && !obj.noCollide && (!obj.hasSupportAI || !obj.isDetached)) {
|
|
if(colliding is null) {
|
|
const Object@ nearest;
|
|
double dist = 0;
|
|
double myRadius = obj.radius;
|
|
for(int i = 0; i < TARGET_COUNT; ++i) {
|
|
const Object@ other = obj.targets[i];
|
|
if(other is obj || !other.isPhysical || other.noCollide)
|
|
continue;
|
|
//Only push from things larger than me
|
|
if(other.radius < myRadius && !other.isPlanet && !other.isStar)
|
|
continue;
|
|
vec3d off = obj.position - other.position;
|
|
double d = off.lengthSQ;
|
|
if(d <= (obj.radius + other.radius) * (obj.radius + other.radius)) {
|
|
off += random3d(0.1); off.normalize();
|
|
obj.position += off * min(time * other.radius * 0.5, d - other.radius + obj.radius);
|
|
@colliding = other;
|
|
break;
|
|
}
|
|
else if(nearest is null || d < dist) {
|
|
@nearest = other;
|
|
dist = d;
|
|
}
|
|
}
|
|
|
|
if(nearest !is null && colliding is null) {
|
|
for(int i = 0; i < TARGET_COUNT; ++i) {
|
|
const Object@ other = nearest.targets[i];
|
|
if(other is obj || !other.isPhysical || other.noCollide)
|
|
continue;
|
|
//Only push from things larger than me
|
|
if(other.radius < myRadius && !other.isPlanet && !other.isStar)
|
|
continue;
|
|
vec3d off = obj.position - other.position;
|
|
double d = off.lengthSQ;
|
|
if(d <= (obj.radius + other.radius) * (obj.radius + other.radius)) {
|
|
off += random3d(0.1); off.normalize();
|
|
obj.position += off * min(time * other.radius * 0.5, d - other.radius + obj.radius);
|
|
@colliding = other;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if(!colliding.valid)
|
|
@colliding = null;
|
|
else {
|
|
vec3d off = obj.position - colliding.position;
|
|
double d = off.lengthSQ;
|
|
if(d <= (obj.radius + colliding.radius) * (obj.radius + colliding.radius)) {
|
|
off += random3d(0.1); off.normalize();
|
|
obj.position += off * min(time * colliding.radius * 0.5, d - colliding.radius + obj.radius);
|
|
}
|
|
else {
|
|
@colliding = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
vec3d dest, destVel, destAccel;
|
|
PathNode@ pathNode;
|
|
|
|
{
|
|
double dot = targRot.dot(obj.rotation);
|
|
if(dot < 0.999) {
|
|
if(dot < -1.0)
|
|
dot = -1.0;
|
|
double angle = acos(dot);
|
|
double tickRot = rotSpeed * time;
|
|
if(angle > tickRot) {
|
|
obj.rotation = obj.rotation.slerp(targRot, tickRot / angle);
|
|
}
|
|
else {
|
|
obj.rotation = targRot;
|
|
rotating = false;
|
|
}
|
|
}
|
|
else {
|
|
if(dot != 1.0)
|
|
obj.rotation = targRot;
|
|
rotating = false;
|
|
}
|
|
}
|
|
|
|
Object@ leader = obj;
|
|
if(obj.hasSupportAI)
|
|
@leader = cast<Ship>(obj).Leader;
|
|
|
|
double doneRange = obj.radius;
|
|
|
|
if(FTL) {
|
|
vec3d prevPos = obj.position;
|
|
vec3d prevVel = obj.velocity;
|
|
|
|
vec3d movement = (destination - obj.position);
|
|
double speed = FTLSpeed * time;
|
|
double dist = movement.length;
|
|
if(dist <= speed) {
|
|
obj.position = destination;
|
|
obj.velocity = vec3d();
|
|
obj.acceleration = vec3d();
|
|
targRot = (!rotating && inCombat) ? combatFacing : targFacing;
|
|
FTL = false;
|
|
|
|
if(obj.hasLeaderAI)
|
|
playParticleSystem("FTLExit", obj.position, obj.rotation, obj.radius * 4.0, obj.visibleMask);
|
|
}
|
|
else {
|
|
movement.normalize(speed);
|
|
|
|
targRot = quaterniond_fromVecToVec(vec3d_front(), movement, vec3d_up());
|
|
obj.position += movement;
|
|
obj.velocity = movement / time;
|
|
obj.acceleration = vec3d();
|
|
}
|
|
|
|
return 0.1;
|
|
}
|
|
else if(leader is obj) {
|
|
if(!moving) {
|
|
if(lockTo is null) {
|
|
if(!rotating && inCombat)
|
|
targRot = combatFacing;
|
|
else
|
|
targRot = targFacing;
|
|
return 0.5;
|
|
}
|
|
}
|
|
|
|
//Check for path invalidation
|
|
if(prevPathId != 0 && prevPathId != obj.owner.PathId.value)
|
|
updatePath(obj);
|
|
|
|
//Get correct destination
|
|
uint pathLen = path is null ? 0 : path.length;
|
|
if(pathLen > 0) {
|
|
for(uint i = 0; i < pathLen; ++i) {
|
|
if(!path[i].valid(obj)) {
|
|
path.length = 0;
|
|
pathLen = 0;
|
|
|
|
vec3d destPos = destination;
|
|
if(target !is null)
|
|
destPos = (obj.position - target.position).normalize(targDist) + target.position;
|
|
obj.createPathTowards(destPos, target);
|
|
}
|
|
}
|
|
|
|
if(pathLen > 0) {
|
|
@pathNode = path[0];
|
|
if(pathNode.pathEntry !is null)
|
|
dest = pathNode.pathEntry.position;
|
|
else {
|
|
dest = pathNode.pathTo;
|
|
doneRange = max(pathNode.dist, doneRange);
|
|
}
|
|
//double space = pathNode.pathEntry.radius + obj.radius;
|
|
//dest += (obj.position - dest).normalize(space);
|
|
}
|
|
}
|
|
if(pathNode is null) {
|
|
if(lockTo !is null) {
|
|
dest = lockTo.position + lockOffset;
|
|
destVel = lockTo.velocity;
|
|
destAccel = lockTo.acceleration;
|
|
|
|
//Compensate for varying tick times
|
|
double tDiff = (obj.lastTick + time) - lockTo.lastTick;
|
|
if(tDiff != 0.0) {
|
|
dest += (destVel + (destAccel * (tDiff * 0.5))) * tDiff;
|
|
destVel += destAccel * tDiff;
|
|
}
|
|
}
|
|
else if(target !is null) {
|
|
if(target.valid) {
|
|
dest = target.position;
|
|
destVel = target.velocity;
|
|
destAccel = target.acceleration;
|
|
|
|
//Compensate for varying tick times
|
|
double tDiff = (obj.lastTick + time) - target.lastTick;
|
|
if(tDiff != 0.0) {
|
|
dest += (destVel + (destAccel * (tDiff * 0.5))) * tDiff;
|
|
destVel += destAccel * tDiff;
|
|
}
|
|
|
|
//Try to reach the target at a particular distance
|
|
//NOTE: This is inaccurate (the angle of the target will change during target prediction)
|
|
// However, because we iterate over small time steps, the error should be relatively small
|
|
dest += (obj.position - dest).normalize(targDist);
|
|
}
|
|
else {
|
|
destination = target.position;
|
|
@target = null;
|
|
}
|
|
}
|
|
else {
|
|
dest = destination;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
quaterniond formationFacing;
|
|
Ship@ leaderShip = cast<Ship>(leader);
|
|
if(leaderShip !is null)
|
|
formationFacing = leaderShip.formationDest;
|
|
|
|
if(leader !is null) {
|
|
destVel = leader.velocity;
|
|
destAccel = leader.acceleration;
|
|
if(fleetRelative)
|
|
dest = leader.position + (formationFacing * cast<Ship>(obj).formationDest.xyz);
|
|
else
|
|
dest = leader.position + destination;
|
|
|
|
//Compensate for varying tick times
|
|
double tDiff = (obj.lastTick + time) - leader.lastTick;
|
|
if(tDiff != 0) {
|
|
dest += (destVel + (destAccel * (tDiff * 0.5))) * tDiff;
|
|
destVel += destAccel * tDiff;
|
|
}
|
|
}
|
|
else {
|
|
dest = obj.position;
|
|
destVel = vec3d();
|
|
destAccel = vec3d();
|
|
}
|
|
}
|
|
|
|
doneRange *= doneRange;
|
|
compDestination = dest;
|
|
|
|
double a = accel;
|
|
|
|
obj.position += obj.velocity * time;
|
|
|
|
if(obj.owner.ForbidDeepSpace != 0) {
|
|
Region@ reg = obj.region;
|
|
if(reg !is null && !obj.hasSupportAI) {
|
|
double dist = obj.position.distanceTo(reg.position+obj.velocity);
|
|
if(dist > reg.radius && dist <= reg.radius*1.05+obj.velocity.length) {
|
|
vec3d dir = obj.position - reg.position;
|
|
vec3d toPos = reg.position + dir.normalized(reg.radius * 0.999);
|
|
if(obj.hasLeaderAI) {
|
|
obj.teleportTo(toPos, movementPart=true);
|
|
}
|
|
else {
|
|
obj.position = toPos;
|
|
obj.velocity = vec3d();
|
|
obj.acceleration = vec3d();
|
|
}
|
|
stopMoving(obj, enterOrbit=false);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!isLocked && (a <= 0.0000001 || a != a)) {
|
|
double speed = obj.velocity.length;
|
|
double tickAccel = 0.1 * time;
|
|
if(speed < tickAccel) {
|
|
obj.velocity = vec3d();
|
|
if(moving) {
|
|
moverDelta = true;
|
|
moving = false;
|
|
if(obj.hasOrbit)
|
|
obj.remakeStandardOrbit();
|
|
prevPathId = 0;
|
|
}
|
|
return 0.25;
|
|
}
|
|
else {
|
|
if(moving && obj.hasOrbit) {
|
|
moverDelta = true;
|
|
moving = false;
|
|
if(obj.hasOrbit)
|
|
obj.remakeStandardOrbit();
|
|
prevPathId = 0;
|
|
}
|
|
obj.velocity *= (speed - tickAccel)/speed;
|
|
return 0.125;
|
|
}
|
|
}
|
|
|
|
//Check if we can decellerate to our target this tick
|
|
double tGoal = newtonArrivalTime(a, dest - obj.position, destVel - obj.velocity);
|
|
if(tGoal > 1.0e4 || tGoal != tGoal) {
|
|
//We might not be able to reach the target (infinite time), so make sure we're working with a vaguely sensible timeline
|
|
tGoal = 1.0e4;
|
|
}
|
|
|
|
if(pathNode !is null && (tGoal <= time || tGoal <= 1.0 || (obj.position + obj.velocity).distanceToSQ(dest) < doneRange)) {
|
|
if(pathNode.pathEntry !is null) {
|
|
playParticleSystem("GateFlash", obj.position, obj.rotation, obj.radius, obj.visibleMask, false);
|
|
if(obj.hasLeaderAI)
|
|
obj.teleportTo(pathNode.pathOut(), movementPart=true);
|
|
else {
|
|
obj.position = pathNode.pathOut();
|
|
obj.velocity = vec3d();
|
|
obj.acceleration = vec3d();
|
|
}
|
|
playParticleSystem("GateFlash", obj.position, obj.rotation, obj.radius, obj.visibleMask | pathNode.visionMask, false);
|
|
}
|
|
path.remove(pathNode);
|
|
posDelta = true;
|
|
moverDelta = true;
|
|
return 0.125;
|
|
}
|
|
else if(tGoal <= time || (lockTo !is null && isLocked)) {
|
|
obj.position = dest;
|
|
obj.velocity = destVel;
|
|
obj.acceleration = destAccel;
|
|
|
|
if(moving) {
|
|
moverDelta = true;
|
|
moving = false;
|
|
if(obj.hasOrbit)
|
|
obj.remakeStandardOrbit();
|
|
prevPathId = 0;
|
|
}
|
|
|
|
if(lockTo !is null)
|
|
isLocked = true;
|
|
if(rotating)
|
|
targRot = targFacing;
|
|
else if(inCombat)
|
|
targRot = combatFacing;
|
|
else if(leader !is obj && cast<Ship>(leader) !is null)
|
|
targRot = leader.rotation;
|
|
else
|
|
targRot = targFacing;
|
|
return 0.5;
|
|
}
|
|
else {
|
|
//Deal with flux
|
|
if(obj.owner.HasFlux != 0 && !obj.hasSupportAI) {
|
|
Region@ reg = obj.region;
|
|
if(reg !is null) {
|
|
if(dest.distanceToSQ(reg.position) > reg.radius*reg.radius) {
|
|
if(canFluxTo(obj, dest)) {
|
|
commitFlux(obj, dest);
|
|
return 0.25;
|
|
}
|
|
else if(obj.owner.ForbidDeepSpace != 0) {
|
|
double speed = obj.velocity.length;
|
|
if(speed < a)
|
|
obj.velocity = vec3d();
|
|
else
|
|
obj.velocity *= (speed - a)/speed;
|
|
obj.acceleration = vec3d();
|
|
return 0.25;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Do movement
|
|
for(uint i = 0; i < 15; ++i) {
|
|
double tOff = tGoal - time;
|
|
vec3d predDest = dest + (destVel + (destAccel * (tOff * 0.5))) * tOff;
|
|
vec3d predVel = destVel + destAccel * tOff;
|
|
|
|
double requires = newtonArrivalTime(a, predDest - obj.position, predVel - obj.velocity);
|
|
//We may be near a case where we can't reach the target
|
|
if(requires > 1.0e4 || requires != requires)
|
|
break;
|
|
|
|
if(abs(requires - tGoal) < 0.02 || requires <= time) {
|
|
tGoal = requires;
|
|
break;
|
|
}
|
|
else {
|
|
double diff = abs(requires - tGoal) * 0.1;
|
|
vec3d primeDest = predDest + (predVel + (destAccel * (diff * 0.5))) * diff;
|
|
vec3d primeVel = predVel + destAccel * diff;
|
|
|
|
double then = timeToTarget(obj, a, primeDest, primeVel);
|
|
|
|
//Move to a guess for the next 0
|
|
double slope = ((then - requires) / diff) - 1.0;
|
|
double y = then - (requires + diff);
|
|
tGoal = (requires + diff) - y / slope;
|
|
}
|
|
}
|
|
|
|
vec3d prevPos = obj.position;
|
|
vec3d prevVel = obj.velocity;
|
|
|
|
if(tGoal > 1.0) {
|
|
if(leader !is obj && cast<Ship>(leader) !is null) {
|
|
if(obj.isDetached)
|
|
targRot = quaterniond_fromVecToVec(vec3d_front(), dest - obj.position, vec3d_up());
|
|
else
|
|
targRot = leader.rotation;
|
|
}
|
|
else if(inCombat)
|
|
targRot = combatFacing;
|
|
else
|
|
targRot = targFacing;
|
|
}
|
|
|
|
//Perform necessary acceleration at arbitrary accuracy
|
|
if(tGoal > time) {
|
|
//Flagships can only accelerate after they finish rotating
|
|
if((leader !is null && leader !is obj) || !rotating || vectorMovement) {
|
|
double timeLeft = time;
|
|
do {
|
|
double take = 0;
|
|
obj.acceleration = accToGoal(a, take, dest - obj.position, destVel - obj.velocity);
|
|
take = min(timeLeft, max(take, 0.01));
|
|
obj.position += obj.acceleration * (take * take * 0.5);
|
|
obj.velocity += obj.acceleration * take;
|
|
timeLeft -= take;
|
|
} while(timeLeft > 0.0001);
|
|
|
|
if(!vectorMovement) {
|
|
if((leader is obj || cast<Ship>(leader) is null) && obj.acceleration.lengthSQ > 0.01 && tGoal > 1.0)
|
|
targRot = quaterniond_fromVecToVec(vec3d_front(), dest - obj.position, vec3d_up());
|
|
}
|
|
}
|
|
else if(!vectorMovement) {
|
|
targRot = quaterniond_fromVecToVec(vec3d_front(), dest - obj.position, vec3d_up());
|
|
}
|
|
}
|
|
else {
|
|
obj.position = dest;
|
|
obj.velocity = destVel;
|
|
obj.acceleration = destAccel;
|
|
}
|
|
|
|
//double trueAcc = prevVel.distanceTo(obj.velocity) / time;
|
|
//if(trueAcc - a > 0.001)
|
|
// error(trueAcc + " > " + a);
|
|
|
|
isLocked = false;
|
|
return tGoal * 0.5;
|
|
}
|
|
}
|
|
|
|
quaterniond get_targetRotation() {
|
|
return targRot;
|
|
}
|
|
|
|
void forceLockTo(Object@ obj) {
|
|
@lockTo = obj;
|
|
isLocked = true;
|
|
}
|
|
|
|
void checkOrbitObject(Object& obj, vec3d destPoint) {
|
|
Region@ reg = getRegion(destPoint);
|
|
if(reg is null)
|
|
return;
|
|
|
|
Object@ orbit = reg.getOrbitObject(destPoint);
|
|
if(orbit is null)
|
|
return;
|
|
|
|
@lockTo = orbit;
|
|
isLocked = false;
|
|
lockOffset = destPoint - orbit.position;
|
|
|
|
if(syncedID == moveID)
|
|
syncedID = -1;
|
|
}
|
|
|
|
PathNode@ dodge(Object& obj, const line3dd& line, Object@ ignore, Object@& prev) {
|
|
auto@ obstacle = trace(line, 0x1);
|
|
if(obstacle is null || obstacle is ignore || obstacle is prev || !(obstacle.isStar || obstacle.isPlanet))
|
|
return null;
|
|
|
|
double baseDist = line.start.distanceTo(obstacle.position);
|
|
|
|
@prev = obstacle;
|
|
|
|
double dist = (obj.radius + obstacle.radius) * 2.0;
|
|
dist = max(dist, sqrt(baseDist));
|
|
|
|
vec3d pt = line.getClosestPoint(obstacle.position, false);
|
|
if(pt != obstacle.position)
|
|
pt = obstacle.position + (pt - obstacle.position).normalized(dist);
|
|
else
|
|
pt = obstacle.position + quaterniond_fromAxisAngle(line.direction, randomd(-pi,pi)) * line.direction.cross(vec3d_up()).normalized(dist);
|
|
|
|
PathNode node;
|
|
node.pathTo = pt;
|
|
node.dist = (dist + (pt - line.start).normalize().dot(pt - obstacle.position)); //The less perpendicular the course, the further away we can start changing course
|
|
node.dist = min(node.dist, line.start.distanceTo(pt) * 0.5);
|
|
return node;
|
|
}
|
|
|
|
void createPathTowards(Object& obj, vec3d point, Object@ targ = null) {
|
|
auto@ temp = path;
|
|
if(temp is null)
|
|
@temp = array<PathNode@>();
|
|
@path = null;
|
|
pathOddityGates(obj.owner, temp, obj.position, point, maxAcceleration);
|
|
|
|
if(maxAcceleration > 0) {
|
|
Object@ prev;
|
|
vec3d from = obj.position + obj.velocity * (obj.velocity.length / (maxAcceleration * 2.0));
|
|
for(uint i = 0; i < temp.length && temp.length < 50; ++i) {
|
|
if(i > 0) {
|
|
auto@ f = temp[i-1];
|
|
if(f.pathExit !is null)
|
|
from = f.pathExit.position;
|
|
else
|
|
from = f.pathTo;
|
|
}
|
|
vec3d to;
|
|
auto@ node = temp[i];
|
|
if(node.pathEntry !is null)
|
|
to = node.pathEntry.position;
|
|
else
|
|
to = node.pathTo;
|
|
line3dd line(from, to);
|
|
@node = dodge(obj, line, targ, prev);
|
|
if(node !is null)
|
|
temp.insertAt(i, @node);
|
|
}
|
|
|
|
if(temp.length > 0) {
|
|
auto@ f = temp.last;
|
|
if(f.pathExit !is null)
|
|
from = f.pathExit.position;
|
|
else
|
|
from = f.pathTo;
|
|
}
|
|
|
|
while(temp.length < 50) {
|
|
line3dd line(from, point);
|
|
auto@ node = dodge(obj, line, targ, prev);
|
|
if(node is null)
|
|
break;
|
|
temp.insertLast(@node);
|
|
from = node.pathTo;
|
|
}
|
|
}
|
|
|
|
if(temp.length > 0)
|
|
@path = temp;
|
|
prevPathId = obj.owner.PathId.value;
|
|
moverDelta = true;
|
|
}
|
|
|
|
void updatePath(Object& obj) {
|
|
prevPathId = obj.owner.PathId.value;
|
|
vec3d dest = destination;
|
|
if(target !is null)
|
|
dest = (obj.position - target.position).normalize(targDist) + target.position;
|
|
|
|
array<PathNode@> newPath;
|
|
pathOddityGates(obj.owner, newPath, obj.position, dest, maxAcceleration);
|
|
|
|
double prevETA = getPathETA(obj.position, dest, maxAcceleration, path);
|
|
double newETA = getPathETA(obj.position, dest, maxAcceleration, newPath);
|
|
|
|
double vel = obj.velocity.length;
|
|
double t = (vel / maxAcceleration);
|
|
newETA += t + sqrt(t * vel * 0.5 / maxAcceleration);
|
|
|
|
if(newETA < prevETA) {
|
|
@path = newPath;
|
|
moverDelta = true;
|
|
}
|
|
}
|
|
|
|
bool isOnMoveOrder(int id) {
|
|
return !FTL && id == moveID && moving;
|
|
}
|
|
|
|
bool moveTo(Object& obj, vec3d point, int& id, bool doPathing = true, bool enterOrbit = true, bool allowStop = false) {
|
|
if(FTL)
|
|
return false;
|
|
if(id > 0 && id == moveID)
|
|
return !moving;
|
|
|
|
moving = true;
|
|
destination = point;
|
|
@target = null;
|
|
@lockTo = null;
|
|
|
|
if(obj.hasOrbit && obj.inOrbit)
|
|
obj.stopOrbit();
|
|
|
|
if(!vectorMovement || !inCombat) {
|
|
if(allowStop) {
|
|
double d = point.distanceToSQ(obj.position);
|
|
if(d > sqr(obj.radius + obj.velocity.length))
|
|
allowStop = false;
|
|
}
|
|
if(!allowStop) {
|
|
rotating = true;
|
|
targRot = quaterniond_fromVecToVec(vec3d_front(), point - obj.position, vec3d_up());
|
|
}
|
|
}
|
|
|
|
id = ++moveID;
|
|
|
|
if(doPathing) {
|
|
if(path !is null)
|
|
path.length = 0;
|
|
obj.createPathTowards(point);
|
|
if(enterOrbit && !obj.isPlanet)
|
|
obj.checkOrbitObject(destination);
|
|
}
|
|
else {
|
|
@path = null;
|
|
prevPathId = 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool moveTo(Object& obj, Object& targ, int& id, double distance = 0.0, bool doPathing = true, bool enterOrbit = true) {
|
|
if(FTL)
|
|
return false;
|
|
if(id > 0 && id == moveID)
|
|
return !moving;
|
|
|
|
moving = true;
|
|
@target = targ;
|
|
@lockTo = null;
|
|
|
|
if(obj.hasOrbit && obj.inOrbit)
|
|
obj.stopOrbit();
|
|
|
|
if(targ.isRegion)
|
|
targDist = targ.radius * 0.85;
|
|
else
|
|
targDist = max(distance, obj.radius + targ.radius);
|
|
|
|
if(!vectorMovement || !inCombat) {
|
|
rotating = true;
|
|
targRot = quaterniond_fromVecToVec(vec3d_front(), targ.position - obj.position, vec3d_up());
|
|
}
|
|
|
|
id = ++moveID;
|
|
|
|
if(doPathing) {
|
|
if(path !is null)
|
|
path.length = 0;
|
|
vec3d destPos = (obj.position - targ.position).normalize(targDist) + targ.position;
|
|
obj.createPathTowards(destPos, targ);
|
|
if(enterOrbit && !obj.isPlanet)
|
|
obj.checkOrbitObject(destPos);
|
|
}
|
|
else {
|
|
@path = null;
|
|
prevPathId = 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void stopMoving(Object& obj, bool doPathing = false, bool enterOrbit = true) {
|
|
int dummy = -1;
|
|
if(accel > 1.0e-4)
|
|
moveTo(obj, obj.position + obj.velocity * min(abs(obj.velocity.length) * 0.5 / accel, 30.0), dummy, doPathing, enterOrbit, allowStop=true);
|
|
else
|
|
moveTo(obj, obj.position, dummy, doPathing, enterOrbit, allowStop=true);
|
|
}
|
|
|
|
void clearMovement(Object& obj) {
|
|
if(!moving)
|
|
return;
|
|
moving = false;
|
|
moverDelta = true;
|
|
rotating = false;
|
|
if(obj.hasOrbit)
|
|
obj.remakeStandardOrbit();
|
|
}
|
|
|
|
bool rotateTo(Object& obj, quaterniond rotation, int& id) {
|
|
if(obj.hasOrbit && obj.inOrbit)
|
|
return true;
|
|
id = moveID;
|
|
rotation.normalize();
|
|
if(targFacing != rotation) {
|
|
facingDelta = true;
|
|
targFacing = rotation;
|
|
}
|
|
|
|
if(obj.rotation.dot(targFacing) > 0.999) {
|
|
return true;
|
|
}
|
|
else {
|
|
targRot = rotation;
|
|
rotating = true;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void setRotation(Object& obj, quaterniond rotation) {
|
|
rotation.normalize();
|
|
if(targFacing != rotation) {
|
|
targFacing = rotation;
|
|
facingDelta = true;
|
|
}
|
|
}
|
|
|
|
void setCombatFacing(Object& obj, quaterniond& rotation) {
|
|
quaterniond prev = combatFacing;
|
|
bool prevCombat = inCombat;
|
|
|
|
combatFacing = rotation;
|
|
combatFacing.normalize();
|
|
inCombat = true;
|
|
|
|
if(prev != combatFacing || !prevCombat)
|
|
facingDelta = true;
|
|
}
|
|
|
|
void clearCombatFacing(Object& obj) {
|
|
if(inCombat) {
|
|
inCombat = false;
|
|
facingDelta = true;
|
|
}
|
|
}
|
|
|
|
void flagPositionUpdate(Object& obj) {
|
|
moverDelta = true;
|
|
posDelta = true;
|
|
obj.wake();
|
|
}
|
|
|
|
bool writeMoverDelta(const Object& obj, Message& msg) {
|
|
const Ship@ ship = cast<const Ship@>(obj);
|
|
if(syncedID != moveID || moverDelta) {
|
|
msg.write1();
|
|
msg.write1();
|
|
writeMover(obj, msg);
|
|
|
|
if(obj.velocity.lengthSQ < 0.001 || posDelta) {
|
|
msg.write1();
|
|
msg.writeMedVec3(obj.position);
|
|
posDelta = false;
|
|
}
|
|
else {
|
|
msg.write0();
|
|
}
|
|
|
|
syncedID = moveID;
|
|
moverDelta = false;
|
|
facingDelta = false;
|
|
return true;
|
|
}
|
|
else if(facingDelta) {
|
|
msg.write1();
|
|
msg.write0();
|
|
msg.write1();
|
|
msg.writeBit(inCombat);
|
|
if(inCombat)
|
|
msg.writeRotation(combatFacing);
|
|
else {
|
|
msg.writeBit(rotating);
|
|
if(rotating)
|
|
msg.writeRotation(targFacing);
|
|
}
|
|
facingDelta = false;
|
|
return true;
|
|
}
|
|
else if(ship !is null && prevFormationDest != ship.formationDest) {
|
|
msg.write1();
|
|
msg.write0();
|
|
msg.write0();
|
|
if(ship.hasLeaderAI)
|
|
msg.writeRotation(ship.formationDest);
|
|
else
|
|
msg.writeSmallVec3(ship.formationDest.xyz);
|
|
prevFormationDest = ship.formationDest;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void readMoverDelta(Object& obj, Message& msg) {
|
|
if(msg.readBit()) {
|
|
readMover(obj, msg);
|
|
|
|
if(msg.readBit()) {
|
|
obj.position = msg.readMedVec3();
|
|
obj.velocity = vec3d();
|
|
obj.acceleration = vec3d();
|
|
}
|
|
}
|
|
else {
|
|
if(msg.readBit()) {
|
|
inCombat = msg.readBit();
|
|
if(inCombat)
|
|
combatFacing = msg.readRotation();
|
|
else if(msg.readBit()) {
|
|
rotating = true;
|
|
targFacing = msg.readRotation();
|
|
}
|
|
}
|
|
else {
|
|
Ship@ ship = cast<Ship>(obj);
|
|
if(ship.hasLeaderAI)
|
|
ship.formationDest = msg.readRotation();
|
|
else
|
|
ship.formationDest.xyz = msg.readSmallVec3();
|
|
}
|
|
}
|
|
}
|
|
|
|
void writeMover(const Object& obj, Message& msg) {
|
|
msg << float(accel);
|
|
if(rotSpeed == shipRotSpeed) {
|
|
msg.write0();
|
|
}
|
|
else {
|
|
msg.write1();
|
|
msg << float(rotSpeed);
|
|
}
|
|
msg.writeBit(vectorMovement);
|
|
|
|
if(targRot == targFacing) {
|
|
msg.write1();
|
|
msg.writeRotation(targRot);
|
|
}
|
|
else {
|
|
msg.write0();
|
|
msg.writeRotation(targRot);
|
|
msg.writeRotation(targFacing);
|
|
}
|
|
|
|
msg << moving;
|
|
msg << rotating;
|
|
msg << FTL;
|
|
if(FTL)
|
|
msg << float(FTLSpeed);
|
|
msg << inCombat;
|
|
if(inCombat)
|
|
msg.writeRotation(combatFacing);
|
|
if(obj.hasSupportAI)
|
|
msg << fleetRelative;
|
|
|
|
uint pathCnt = path is null ? 0 : path.length;
|
|
msg.writeBit(pathCnt != 0);
|
|
if(pathCnt != 0) {
|
|
msg.writeSmall(pathCnt);
|
|
for(uint i = 0; i < pathCnt; ++i)
|
|
msg << path[i];
|
|
}
|
|
|
|
if(lockTo !is null) {
|
|
msg.write1();
|
|
msg << lockTo;
|
|
msg << isLocked;
|
|
msg.writeSmallVec3(lockOffset);
|
|
}
|
|
else {
|
|
msg.write0();
|
|
}
|
|
|
|
const Ship@ ship = cast<const Ship@>(obj);
|
|
if(ship !is null) {
|
|
msg.write1();
|
|
if(ship.hasLeaderAI)
|
|
msg.writeRotation(ship.formationDest);
|
|
else if(fleetRelative)
|
|
msg.writeSmallVec3(ship.formationDest.xyz);
|
|
}
|
|
else {
|
|
msg.write0();
|
|
}
|
|
|
|
if(target !is null) {
|
|
msg.write1();
|
|
msg << target;
|
|
msg << float(targDist);
|
|
}
|
|
else {
|
|
msg.write0();
|
|
if(!FTL && obj.hasSupportAI) {
|
|
if(!fleetRelative)
|
|
msg.writeSmallVec3(destination);
|
|
}
|
|
else {
|
|
msg.writeMedVec3(destination);
|
|
}
|
|
}
|
|
}
|
|
|
|
void readMover(Object& obj, Message& msg) {
|
|
accel = msg.read_float();
|
|
if(msg.readBit())
|
|
rotSpeed = msg.read_float();
|
|
else
|
|
rotSpeed = shipRotSpeed;
|
|
vectorMovement = msg.readBit();
|
|
|
|
if(msg.readBit()) {
|
|
targFacing = msg.readRotation();
|
|
targRot = targFacing;
|
|
}
|
|
else {
|
|
targRot = msg.readRotation();
|
|
targFacing = msg.readRotation();
|
|
}
|
|
|
|
msg >> moving;
|
|
msg >> rotating;
|
|
++moveID;
|
|
bool prevFTL = FTL;
|
|
msg >> FTL;
|
|
if(FTL)
|
|
FTLSpeed = msg.read_float();
|
|
|
|
msg >> inCombat;
|
|
if(inCombat)
|
|
combatFacing = msg.readRotation();
|
|
if(obj.hasSupportAI)
|
|
msg >> fleetRelative;
|
|
|
|
uint pathCnt = 0;
|
|
if(msg.readBit())
|
|
pathCnt = msg.readSmall();
|
|
if(pathCnt == 0)
|
|
@path = null;
|
|
else {
|
|
if(path is null)
|
|
@path = array<PathNode@>();
|
|
path.length = pathCnt;
|
|
for(uint i = 0; i < pathCnt; ++i) {
|
|
if(path[i] is null)
|
|
@path[i] = PathNode();
|
|
msg >> path[i];
|
|
}
|
|
}
|
|
|
|
if(prevFTL && !FTL)
|
|
obj.velocity = vec3d();
|
|
|
|
if(msg.readBit()) {
|
|
msg >> lockTo;
|
|
msg >> isLocked;
|
|
lockOffset = msg.readSmallVec3();
|
|
}
|
|
else {
|
|
@lockTo = null;
|
|
}
|
|
|
|
if(msg.readBit()) {
|
|
Ship@ ship = cast<Ship>(obj);
|
|
if(ship.hasLeaderAI)
|
|
ship.formationDest = msg.readRotation();
|
|
else if(fleetRelative)
|
|
ship.formationDest.xyz = msg.readSmallVec3();
|
|
}
|
|
|
|
if(msg.readBit()) {
|
|
msg >> target;
|
|
float td = 0;
|
|
msg >> td;
|
|
targDist = td;
|
|
}
|
|
else {
|
|
if(!FTL && obj.hasSupportAI) {
|
|
if(!fleetRelative)
|
|
destination = msg.readSmallVec3();
|
|
}
|
|
else {
|
|
destination = msg.readMedVec3();
|
|
}
|
|
@target = null;
|
|
}
|
|
}
|
|
};
|