Open source Star Ruler 2 source code!
This commit is contained in:
@@ -0,0 +1,354 @@
|
||||
// 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();
|
||||
}
|
||||
Reference in New Issue
Block a user