Files
starruler-linux/scripts/server/empire_ai/weasel/Movement.as
T
2018-07-17 14:15:37 +02:00

355 lines
7.5 KiB
ActionScript

// Movement
// --------
// Manages FTL travel modes, expenditure of FTL energy, and general movement patterns.
//
import empire_ai.weasel.WeaselAI;
import oddity_navigation;
import ftl;
enum FTLReturn {
F_Pass,
F_Continue,
F_Done,
F_Kill,
};
class FTL : AIComponent {
uint order(MoveOrder& order) { return F_Pass; }
uint tick(MoveOrder& order, double time) { return F_Pass; }
};
bool getNearPosition(Object& obj, Object& target, vec3d& pos, bool spread = false) {
if(target !is null) {
if(target.isPlanet) {
Planet@ toPl = cast<Planet>(target);
vec3d dir = obj.position - toPl.position;
dir = dir.normalized(toPl.OrbitSize * 0.9);
if(spread)
dir = quaterniond_fromAxisAngle(vec3d_up(), randomd(-0.15,0.15) * pi) * dir;
pos = toPl.position + dir;
return true;
}
else if(obj.hasLeaderAI && (target.isShip || target.isOrbital)) {
vec3d dir = obj.position - target.position;
dir = dir.normalized(obj.getEngagementRange());
pos = target.position + dir;
return true;
}
else {
Region@ reg = cast<Region>(target);
if(reg is null)
@reg = target.region;
if(reg !is null) {
vec3d dir = obj.position - reg.position;
dir = dir.normalized(reg.radius * 0.85);
if(spread)
dir = quaterniond_fromAxisAngle(vec3d_up(), randomd(-0.15,0.15) * pi) * dir;
pos = reg.position + dir;
return true;
}
}
}
return false;
}
bool targetPosition(MoveOrder& ord, vec3d& toPosition) {
if(ord.target !is null) {
return getNearPosition(ord.obj, ord.target, toPosition);
}
else {
toPosition = ord.position;
return true;
}
}
double usableFTL(AI& ai, MoveOrder& ord) {
double storage = ai.empire.FTLCapacity;
double avail = ai.empire.FTLStored;
double reserved = 0.0;
if(ord.priority < MP_Critical)
reserved += ai.behavior.ftlReservePctCritical;
if(ord.priority < MP_Normal)
reserved += ai.behavior.ftlReservePctNormal;
avail -= reserved * storage;
return avail;
}
enum MovePriority {
MP_Background,
MP_Normal,
MP_Critical
};
class MoveOrder {
int id = -1;
uint priority = MP_Normal;
Object@ obj;
Object@ target;
vec3d position;
bool completed = false;
bool failed = false;
void save(Movement& movement, SaveFile& file) {
file << priority;
file << obj;
file << target;
file << position;
file << completed;
file << failed;
}
void load(Movement& movement, SaveFile& file) {
file >> priority;
file >> obj;
file >> target;
file >> position;
file >> completed;
file >> failed;
}
void cancel() {
failed = true;
obj.clearOrders();
}
bool tick(AI& ai, Movement& movement, double time) {
//Check if we still exist
if(obj is null || !obj.valid || obj.owner !is ai.empire) {
failed = true;
return false;
}
uint ftlMode = F_Pass;
if(movement.ftl !is null) {
ftlMode = movement.ftl.tick(this, time);
if(ftlMode == F_Kill)
return false;
if(ftlMode == F_Done)
return true;
}
//Check if we've arrived
if(target !is null) {
if(!target.valid) {
failed = true;
return false;
}
double targDist = target.radius + 45.0 + obj.radius;
if(target.isRegion)
targDist = target.radius * 0.86 + obj.radius;
if(target.position.distanceTo(obj.position) < targDist) {
completed = true;
return false;
}
}
else {
double targDist = obj.radius * 2.0;
if(obj.position.distanceTo(position) < targDist) {
completed = true;
return false;
}
}
//Fail out if our order failed
if(ftlMode == F_Pass) {
if(!obj.hasOrders) {
failed = true;
return false;
}
}
return true;
}
};
class Movement : AIComponent {
int nextMoveOrderId = 0;
array<MoveOrder@> moveOrders;
array<Oddity@> oddities;
FTL@ ftl;
void create() {
@ftl = cast<FTL>(ai.ftl);
}
void save(SaveFile& file) {
file << nextMoveOrderId;
uint cnt = moveOrders.length;
file << cnt;
for(uint i = 0; i < cnt; ++i) {
saveMoveOrder(file, moveOrders[i]);
moveOrders[i].save(this, file);
}
}
void load(SaveFile& file) {
file >> nextMoveOrderId;
uint cnt = 0;
file >> cnt;
for(uint i = 0; i < cnt; ++i) {
auto@ ord = loadMoveOrder(file);
if(ord !is null) {
ord.load(this, file);
if(ord.obj !is null)
moveOrders.insertLast(ord);
}
else {
MoveOrder().load(this, file);
}
}
}
array<MoveOrder@> loadIds;
MoveOrder@ loadMoveOrder(SaveFile& file) {
int id = -1;
file >> id;
bool failed = false, completed = false;
file >> failed;
file >> completed;
if(id == -1) {
return null;
}
else {
for(uint i = 0, cnt = loadIds.length; i < cnt; ++i) {
if(loadIds[i].id == id)
return loadIds[i];
}
MoveOrder data;
data.id = id;
data.failed = failed;
data.completed = completed;
loadIds.insertLast(data);
return data;
}
}
void saveMoveOrder(SaveFile& file, MoveOrder@ data) {
int id = -1;
bool failed = false, completed = false;
if(data !is null) {
id = data.id;
failed = data.failed;
completed = data.completed;
}
file << id;
file << failed;
file << completed;
}
void postLoad(AI& ai) {
loadIds.length = 0;
getOddityGates(oddities);
}
array<PathNode@> path;
double getPathDistance(const vec3d& fromPosition, const vec3d& toPosition, double accel = 1.0) {
pathOddityGates(oddities, ai.empire, path, fromPosition, toPosition, accel);
return ::getPathDistance(fromPosition, toPosition, path);
}
double eta(Object& obj, Object& toObject, uint priority = MP_Normal) {
return eta(obj, toObject.position, priority);
}
double eta(Object& obj, const vec3d& position, uint priority = MP_Normal) {
//TODO: Use FTL
//TODO: Path through gates/wormholes
return newtonArrivalTime(obj.maxAcceleration, position - obj.position, obj.velocity);
}
void order(MoveOrder& ord) {
if(ord.target !is null && ord.target is ord.obj.region)
return;
bool madeOrder = false;
if(ftl !is null) {
uint mode = ftl.order(ord);
if(mode == F_Kill || mode == F_Done)
return;
madeOrder = (mode == F_Continue);
}
if(ord.target !is null) {
ord.obj.addGotoOrder(ord.target, append=madeOrder);
ord.position = ord.target.position;
}
else
ord.obj.addMoveOrder(ord.position, append=madeOrder);
}
void add(MoveOrder& ord) {
for(uint i = 0, cnt = moveOrders.length; i < cnt; ++i) {
if(moveOrders[i].obj is ord.obj) {
moveOrders[i].failed = true;
moveOrders.removeAt(i);
--i; --cnt;
}
}
moveOrders.insertLast(ord);
order(ord);
}
MoveOrder@ move(Object& obj, Object& toObject, uint priority = MP_Normal, bool spread = false, bool nearOnly = false) {
if(toObject.isRegion) {
if(obj.region is toObject)
nearOnly = false;
else
nearOnly = true;
}
if(nearOnly) {
vec3d pos;
bool canNear = getNearPosition(obj, toObject, pos, spread);
if(canNear)
return move(obj, pos, priority);
}
MoveOrder ord;
ord.id = nextMoveOrderId++;
@ord.obj = obj;
@ord.target = toObject;
ord.priority = priority;
add(ord);
return ord;
}
MoveOrder@ move(Object& obj, const vec3d& position, uint priority = MP_Normal, bool spread = false) {
MoveOrder ord;
ord.id = nextMoveOrderId++;
@ord.obj = obj;
ord.position = position;
ord.priority = priority;
add(ord);
return ord;
}
void tick(double time) override {
for(uint i = 0, cnt = moveOrders.length; i < cnt; ++i) {
if(!moveOrders[i].tick(ai, this, time)) {
moveOrders.removeAt(i);
--i; --cnt;
}
}
}
void focusTick(double time) override {
//Update our gate navigation list
getOddityGates(oddities);
}
};
AIComponent@ createMovement() {
return Movement();
}