import empire_ai.weasel.WeaselAI; import empire_ai.weasel.Movement; import empire_ai.weasel.Development; import empire_ai.weasel.Fleets; import ftl; import system_flags; import regions.regions; import systems; from orders import OrderType; const double REJUMP_MIN_DIST = 8000.0; class Jumpdrive : FTL { Development@ development; Fleets@ fleets; int safetyFlag = -1; array safeRegions; void create() override { @development = cast(ai.development); @fleets = cast(ai.fleets); safetyFlag = getSystemFlag("JumpdriveSafety"); } void save(SaveFile& file) { uint cnt = safeRegions.length; file << cnt; for(uint i = 0; i < cnt; ++i) file << safeRegions[i]; } void load(SaveFile& file) { uint cnt = 0; file >> cnt; safeRegions.length = cnt; for(uint i = 0; i < cnt; ++i) file >> safeRegions[i]; } double jdETA(Object& obj, const vec3d& position) { double charge = JUMPDRIVE_CHARGE_TIME; return charge; } double subETA(Object& obj, const vec3d& position) { return newtonArrivalTime(obj.maxAcceleration, position - obj.position, vec3d()); } bool shouldJD(Object& obj, const vec3d& position, uint priority) { //This makes me sad if(position.distanceTo(obj.position) < 3000) return false; return true; /*double factor = 0.8;*/ /*if(priority == MP_Critical)*/ /* factor = 1.0;*/ /*return jdETA(obj, position) <= factor * subETA(obj, position);*/ } uint order(MoveOrder& ord) override { return order(ord, ord.obj.position, false); } uint order(MoveOrder& ord, const vec3d& fromPos, bool secondary) { if(!canJumpdrive(ord.obj)) return F_Pass; double avail = usableFTL(ai, ord); if(avail > 0) { vec3d toPosition; if(targetPosition(ord, toPosition)) { double maxRange = jumpdriveRange(ord.obj); double dist = toPosition.distanceTo(fromPos); bool isSafe = false; Region@ reg = getRegion(toPosition); if(reg !is null) isSafe = reg.getSystemFlag(ai.empire, safetyFlag); if(dist > maxRange && !isSafe) { //See if we should jump to a safe region first if(!secondary) { double bestHop = INFINITY; Region@ hopRegion; vec3d bestPos; for(uint i = 0, cnt = safeRegions.length; i < cnt; ++i) { if(!safeRegions[i].getSystemFlag(ai.empire, safetyFlag)) continue; vec3d hopPos = safeRegions[i].position; hopPos = hopPos + (fromPos-hopPos).normalized(safeRegions[i].radius * 0.85); double d = hopPos.distanceTo(toPosition); if(d < bestHop) { bestHop = d; @hopRegion = safeRegions[i]; bestPos = hopPos; } } if(bestHop < dist * 0.8) { double cost = jumpdriveCost(ord.obj, fromPos, bestPos); if(avail >= cost) { ord.obj.addJumpdriveOrder(bestPos); order(ord, bestPos, true); return F_Continue; } } } //Shorten our jump if(ord.priority < MP_Normal) return F_Pass; toPosition = fromPos + (toPosition - fromPos).normalized(maxRange); } if(shouldJD(ord.obj, toPosition, ord.priority)) { double cost = jumpdriveCost(ord.obj, toPosition); if(avail >= cost) { ord.obj.addJumpdriveOrder(toPosition, append=secondary); return F_Continue; } } } } return F_Pass; } uint tick(MoveOrder& ord, double time) { if(ord.priority == MP_Critical && canJumpdrive(ord.obj) && ord.obj.firstOrderType != OT_Jumpdrive) { vec3d toPosition; if(targetPosition(ord, toPosition)) { double dist = ord.obj.position.distanceToSQ(toPosition); if(dist > REJUMP_MIN_DIST * REJUMP_MIN_DIST) { double maxRange = jumpdriveRange(ord.obj); dist = sqrt(dist); bool isSafe = false; Region@ reg = getRegion(toPosition); if(reg !is null) isSafe = reg.getSystemFlag(ai.empire, safetyFlag); if(dist > maxRange && !isSafe) toPosition = ord.obj.position + (toPosition - ord.obj.position).normalized(maxRange); if(shouldJD(ord.obj, toPosition, ord.priority)) { double avail = usableFTL(ai, ord); double cost = jumpdriveCost(ord.obj, toPosition); if(avail >= cost) { cast(ai.movement).order(ord); return F_Continue; } } } } } return F_Pass; } uint sysChk = 0; void start() { for(uint i = 0, cnt = systemCount; i < cnt; ++i) { Region@ reg = getSystem(i).object; if(reg.getSystemFlag(ai.empire, safetyFlag)) safeRegions.insertLast(reg); } } void focusTick(double time) override { //Try to get enough ftl storage that we can ftl our largest fleet a fair distance and have some remaining double highestCost = 0.0; for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) { auto@ flAI = fleets.fleets[i]; if(flAI.fleetClass != FC_Combat) continue; double dist = jumpdriveRange(flAI.obj); vec3d toPosition = flAI.obj.position + vec3d(0, 0, dist); highestCost = max(highestCost, double(jumpdriveCost(flAI.obj, toPosition))); } development.aimFTLStorage = highestCost / (1.0 - ai.behavior.ftlReservePctCritical - ai.behavior.ftlReservePctNormal); //Disable systems that are no longer safe for(uint i = 0, cnt = safeRegions.length; i < cnt; ++i) { if(!safeRegions[i].getSystemFlag(ai.empire, safetyFlag)) { safeRegions.removeAt(i); --i; --cnt; } } //Try to find regions that are safe for us { sysChk = (sysChk+1) % systemCount; auto@ reg = getSystem(sysChk).object; if(reg.getSystemFlag(ai.empire, safetyFlag)) { if(safeRegions.find(reg) == -1) safeRegions.insertLast(reg); } } } }; AIComponent@ createJumpdrive() { return Jumpdrive(); }