ogame-ninja-scripts/distributed-builder.ank

440 lines
16 KiB
Plaintext

// =============================================================================
// OGame Ninja Multi-Planet Ship Builder Script
// =============================================================================
// This script automates building ships on multiple planets, importing resources
// from a master planet when needed, and parking ships on the main planet.
// =============================================================================
// ========================= CONFIGURATION =========================
// Target ship quantities - using NewShipsInfos() and Set() method
TARGET_SHIPS = NewShipsInfos()
TARGET_SHIPS.Set(LIGHTFIGHTER, 20000) // Light Fighters
TARGET_SHIPS.Set(HEAVYFIGHTER, 15000) // Heavy Fighters
TARGET_SHIPS.Set(CRUISER, 2000) // Cruisers
TARGET_SHIPS.Set(BATTLESHIP, 1000) // Battleships
TARGET_SHIPS.Set(LARGECARGO, 3000) // Large Cargo
TARGET_SHIPS.Set(SMALLCARGO, 3000) // Small Cargo
TARGET_SHIPS.Set(RECYCLER, 500) // Recyclers
TARGET_SHIPS.Set(ESPIONAGEPROBE, 0) // Espionage Probes
// List of planets to build ships on
BUILDER_PLANETS = ["1:2:1", "1:2:2", "1:2:3", "1:2:4", "1:2:5", "1:2:6", "1:2:7"]
// *** FIX: Manually define an array of ship IDs to iterate over ***
SHIP_IDS_TO_BUILD = [LIGHTFIGHTER, HEAVYFIGHTER, CRUISER, BATTLESHIP, LARGECARGO, SMALLCARGO, RECYCLER, ESPIONAGEPROBE]
// Master planet for resource imports
MASTER_PLANET = "2:2:14"
// Main planet where to park all ships
MAIN_PLANET = "2:2:14"
// Resource threshold - minimum resources to keep on master planet
RESOURCE_THRESHOLD = NewResources(1000000, 500000, 100000)
// Minimum resources needed before attempting ship building
MIN_RESOURCES_TO_BUILD = NewResources(100000, 50000, 10000)
// Building check interval (milliseconds)
CHECK_INTERVAL = 5 * 60 * 1000
// Fleet parking interval (milliseconds)
PARKING_INTERVAL = 30 * 60 * 1000
// Transport fleet configuration
TRANSPORT_SHIPS = NewShipsInfos()
TRANSPORT_SHIPS.Set(LARGECARGO, 100)
// ========================= GLOBAL VARIABLES =========================
lastParkingCheck = 0
// ========================= MAIN FUNCTIONS =========================
func main() {
LogInfo("=== Multi-Planet Ship Builder Started ===")
LogInfof("Builder planets count: %d", len(BUILDER_PLANETS))
LogInfof("Master planet: %s", MASTER_PLANET)
LogInfof("Main planet: %s", MAIN_PLANET)
// Main loop
for {
LogInfo("--- Starting build cycle ---")
// Check each builder planet
for i = 0; i < len(BUILDER_PLANETS); i++ {
planetCoord = BUILDER_PLANETS[i]
processPlanet(planetCoord)
Sleep(Random(2000, 5000))
}
// Check if it's time to park fleets
currentTime = GetTimestamp()
if currentTime - lastParkingCheck > PARKING_INTERVAL / 1000 {
parkAllFleets()
lastParkingCheck = currentTime
}
// Print status at the end of each cycle
printStatus()
LogInfo("--- Build cycle complete, sleeping ---")
Sleep(CHECK_INTERVAL)
}
}
func processPlanet(planetCoord) {
LogInfof("Processing planet: %s", planetCoord)
planet, _ = GetPlanet(planetCoord)
if planet == nil {
LogErrorf("Could not get planet %s", planetCoord)
return
}
// Check if planet has shipyard
facilities, _ = planet.GetFacilities()
if facilities.Shipyard == 0 {
LogWarnf("Planet %s has no shipyard", planetCoord)
return
}
// Check if shipyard is busy with buildings or ships
bldgID, shipsCount, _ = planet.GetProduction()
if bldgID != 0 {
LogInfof("Shipyard is blocked by building construction on %s", planetCoord)
return
}
if shipsCount > 0 {
LogInfof("Shipyard already building ships on %s", planetCoord)
return
}
// Get current ships on planet
currentShips, _ = planet.GetShips()
// Check what ships need to be built
shipsToBuild = calculateShipsNeeded(currentShips)
// Check if any ships are needed
totalNeeded = 0
for i = 0; i < len(SHIP_IDS_TO_BUILD); i++ {
shipID = SHIP_IDS_TO_BUILD[i]
totalNeeded = totalNeeded + shipsToBuild.ByID(shipID)
}
if totalNeeded == 0 {
LogInfof("Planet %s - No ships needed", planetCoord)
return
}
// Check if we have enough resources
resources, _ = planet.GetResources()
requiredResources = calculateRequiredResources(shipsToBuild)
neededMetal = 0
neededCrystal = 0
neededDeut = 0
if requiredResources.Metal > resources.Metal { neededMetal = requiredResources.Metal - resources.Metal }
if requiredResources.Crystal > resources.Crystal { neededCrystal = requiredResources.Crystal - resources.Crystal }
if requiredResources.Deuterium > resources.Deuterium { neededDeut = requiredResources.Deuterium - resources.Deuterium }
// If we need resources, our only job this cycle is to try and get them.
if neededMetal > 0 || neededCrystal > 0 || neededDeut > 0 {
LogInfof("Planet %s needs resources. Required: M:%d C:%d D:%d", planetCoord, neededMetal, neededCrystal, neededDeut)
fleets, slots = GetFleets()
isReceivingTransport = false
for i = 0; i < len(fleets); i++ {
f = fleets[i]
if f.Mission == TRANSPORT && f.Destination.String() == planetCoord {
isReceivingTransport = true
break
}
}
if isReceivingTransport {
LogInfof("Waiting for incoming resources on %s.", planetCoord)
} else {
if len(fleets) >= slots.Total {
LogWarnf("No free fleet slot to import resources to %s.", planetCoord)
} else {
missingResources = NewResources(neededMetal, neededCrystal, neededDeut)
LogInfo("Requesting resource import from master planet.")
importResources(planetCoord, missingResources)
}
}
return // End this planet's turn. We either sent res, are waiting, or couldn't send.
}
// If we've reached this point, we have enough resources and can try to build.
LogInfo("Sufficient resources found. Attempting to build ships.")
buildShips(planet, shipsToBuild, resources)
}
func calculateShipsNeeded(currentShips) {
needed = NewShipsInfos()
for i = 0; i < len(SHIP_IDS_TO_BUILD); i++ {
shipID = SHIP_IDS_TO_BUILD[i]
targetAmount = TARGET_SHIPS.ByID(shipID)
currentAmount = currentShips.ByID(shipID)
if currentAmount < targetAmount {
stillNeeded = targetAmount - currentAmount
buildBatch = getBuildBatchSize(shipID, stillNeeded)
if buildBatch > 0 {
needed.Set(shipID, buildBatch)
}
}
}
return needed
}
func getBuildBatchSize(shipID, stillNeeded) {
batchSize = 0
switch shipID {
case LIGHTFIGHTER:
batchSize = 200
case HEAVYFIGHTER:
batchSize = 150
case CRUISER:
batchSize = 50
case BATTLESHIP:
batchSize = 25
case LARGECARGO:
batchSize = 100
case SMALLCARGO:
batchSize = 100
case RECYCLER:
batchSize = 20
case ESPIONAGEPROBE:
batchSize = 0
default:
batchSize = 10
}
if stillNeeded < batchSize {
return stillNeeded
}
return batchSize
}
func calculateRequiredResources(ships) {
totalMetal = 0
totalCrystal = 0
totalDeut = 0
for i = 0; i < len(SHIP_IDS_TO_BUILD); i++ {
shipID = SHIP_IDS_TO_BUILD[i]
quantity = ships.ByID(shipID)
if quantity > 0 {
cost = GetPrice(shipID, quantity)
totalMetal += cost.Metal
totalCrystal += cost.Crystal
totalDeut += cost.Deuterium
}
}
return NewResources(totalMetal, totalCrystal, totalDeut)
}
func importResources(targetPlanetCoord, neededResources) {
LogInfof("Importing resources to %s", targetPlanetCoord)
masterPlanet, _ = GetPlanet(MASTER_PLANET)
if masterPlanet == nil {
LogErrorf("Could not get master planet %s", MASTER_PLANET)
return
}
masterResources, _ = masterPlanet.GetResources()
availableMetal = 0
availableCrystal = 0
availableDeut = 0
if masterResources.Metal > RESOURCE_THRESHOLD.Metal { availableMetal = masterResources.Metal - RESOURCE_THRESHOLD.Metal }
if masterResources.Crystal > RESOURCE_THRESHOLD.Crystal { availableCrystal = masterResources.Crystal - RESOURCE_THRESHOLD.Crystal }
if masterResources.Deuterium > RESOURCE_THRESHOLD.Deuterium { availableDeut = masterResources.Deuterium - RESOURCE_THRESHOLD.Deuterium }
toSendMetal = neededResources.Metal
toSendCrystal = neededResources.Crystal
toSendDeut = neededResources.Deuterium
if toSendMetal > availableMetal { toSendMetal = availableMetal }
if toSendCrystal > availableCrystal { toSendCrystal = availableCrystal }
if toSendDeut > availableDeut { toSendDeut = availableDeut }
if toSendMetal <= 0 && toSendCrystal <= 0 && toSendDeut <= 0 {
LogInfo("Master planet doesn't have enough resources to send")
return
}
LogInfof("Sending: M:%d C:%d D:%d", toSendMetal, toSendCrystal, toSendDeut)
fleet = NewFleet()
fleet.SetOrigin(MASTER_PLANET)
fleet.SetDestination(targetPlanetCoord)
fleet.SetSpeed(HUNDRED_PERCENT)
fleet.SetMission(TRANSPORT)
fleet.SetShips(*TRANSPORT_SHIPS)
fleet.SetResources(NewResources(toSendMetal, toSendCrystal, toSendDeut))
_, err = fleet.SendNow()
if err != nil {
LogErrorf("Failed to send transport: %s", err)
} else {
LogInfof("Transport sent to %s", targetPlanetCoord)
}
}
func buildShips(planet, shipsToBuild, resources) {
coordinate, _ = planet.GetCoordinate()
LogInfof("Building ships on %s", coordinate)
availableMetal = resources.Metal
availableCrystal = resources.Crystal
availableDeuterium = resources.Deuterium
priorityOrder = [LARGECARGO, LIGHTFIGHTER, HEAVYFIGHTER, CRUISER, BATTLESHIP, SMALLCARGO, RECYCLER]
for i = 0; i < len(priorityOrder); i++ {
shipID = priorityOrder[i]
quantity = shipsToBuild.ByID(shipID)
if quantity > 0 {
cost = GetPrice(shipID, quantity)
if availableMetal >= cost.Metal && availableCrystal >= cost.Crystal && availableDeuterium >= cost.Deuterium {
err = planet.BuildShips(shipID, quantity)
if err != nil {
LogErrorf("Failed to build ships: %s", err)
} else {
LogInfof("Building %d %s", quantity, ID2Str(shipID))
availableMetal -= cost.Metal
availableCrystal -= cost.Crystal
availableDeuterium -= cost.Deuterium
}
} else {
LogInfof("Not enough resources for %d %s", quantity, ID2Str(shipID))
}
}
}
}
func parkAllFleets() {
LogInfo("=== Parking fleets on main planet ===")
for i = 0; i < len(BUILDER_PLANETS); i++ {
planetCoord = BUILDER_PLANETS[i]
if planetCoord == MAIN_PLANET { continue }
planet, _ = GetPlanet(planetCoord)
if planet == nil { continue }
ships, _ = planet.GetShips()
shipsToPark = NewShipsInfos()
if ships.ByID(LIGHTFIGHTER) > 100 { shipsToPark.Set(LIGHTFIGHTER, ships.ByID(LIGHTFIGHTER) - 100) }
if ships.ByID(HEAVYFIGHTER) > 50 { shipsToPark.Set(HEAVYFIGHTER, ships.ByID(HEAVYFIGHTER) - 50) }
if ships.ByID(CRUISER) > 0 { shipsToPark.Set(CRUISER, ships.ByID(CRUISER)) }
if ships.ByID(BATTLESHIP) > 0 { shipsToPark.Set(BATTLESHIP, ships.ByID(BATTLESHIP)) }
if ships.ByID(LARGECARGO) > 20 { shipsToPark.Set(LARGECARGO, ships.ByID(LARGECARGO) - 20) }
if ships.ByID(SMALLCARGO) > 20 { shipsToPark.Set(SMALLCARGO, ships.ByID(SMALLCARGO) - 20) }
if ships.ByID(RECYCLER) > 5 { shipsToPark.Set(RECYCLER, ships.ByID(RECYCLER) - 5) }
totalToPark = 0
for j = 0; j < len(SHIP_IDS_TO_BUILD); j++ {
shipID = SHIP_IDS_TO_BUILD[j]
totalToPark += shipsToPark.ByID(shipID)
}
if totalToPark > 0 {
fleets, slots = GetFleets()
if len(fleets) >= slots.Total {
LogWarnf("No free fleet slots to park ships from %s. Skipping.", planetCoord)
continue
}
LogInfof("Parking %d ships from %s", totalToPark, planetCoord)
fleet = NewFleet()
fleet.SetOrigin(planetCoord)
fleet.SetDestination(MAIN_PLANET)
fleet.SetSpeed(HUNDRED_PERCENT)
fleet.SetMission(PARK)
fleet.SetShips(*shipsToPark)
_, err = fleet.SendNow()
if err != nil {
LogErrorf("Failed to park fleet: %s", err)
} else {
LogInfof("Fleet sent to main planet from %s", planetCoord)
}
} else {
LogInfof("No ships to park from %s", planetCoord)
}
Sleep(Random(2000, 5000))
}
}
// ========================= MONITORING FUNCTIONS =========================
func printStatus() {
LogInfo("=== Current Status ===")
totalShips = NewShipsInfos()
uniquePlanets = []
uniquePlanets = uniquePlanets + [MAIN_PLANET] // FIX: Use + operator instead of append
for i = 0; i < len(BUILDER_PLANETS); i++ {
planetCoord = BUILDER_PLANETS[i]
isAlreadyInList = false
for j = 0; j < len(uniquePlanets); j++ {
if planetCoord == uniquePlanets[j] {
isAlreadyInList = true
break
}
}
if !isAlreadyInList {
uniquePlanets = uniquePlanets + [planetCoord] // FIX: Use + operator instead of append
}
}
for i = 0; i < len(uniquePlanets); i++ {
planetCoord = uniquePlanets[i]
planet, _ = GetPlanet(planetCoord)
if planet == nil { continue }
ships, _ = planet.GetShips()
LogInfof("Planet %s: LF:%d HF:%d CR:%d BS:%d LC:%d SC:%d Rec:%d",
planetCoord, ships.ByID(LIGHTFIGHTER), ships.ByID(HEAVYFIGHTER),
ships.ByID(CRUISER), ships.ByID(BATTLESHIP), ships.ByID(LARGECARGO),
ships.ByID(SMALLCARGO), ships.ByID(RECYCLER))
totalShips.Add(ships)
}
LogInfo("---")
LogInfo("Total ships across all planets:")
LogInfof("Light Fighters: %d/%d", totalShips.ByID(LIGHTFIGHTER), TARGET_SHIPS.ByID(LIGHTFIGHTER))
LogInfof("Heavy Fighters: %d/%d", totalShips.ByID(HEAVYFIGHTER), TARGET_SHIPS.ByID(HEAVYFIGHTER))
LogInfof("Cruisers: %d/%d", totalShips.ByID(CRUISER), TARGET_SHIPS.ByID(CRUISER))
LogInfof("Battleships: %d/%d", totalShips.ByID(BATTLESHIP), TARGET_SHIPS.ByID(BATTLESHIP))
LogInfof("Large Cargo: %d/%d", totalShips.ByID(LARGECARGO), TARGET_SHIPS.ByID(LARGECARGO))
LogInfof("Small Cargo: %d/%d", totalShips.ByID(SMALLCARGO), TARGET_SHIPS.ByID(SMALLCARGO))
LogInfof("Recyclers: %d/%d", totalShips.ByID(RECYCLER), TARGET_SHIPS.ByID(RECYCLER))
LogInfo("---")
}
func forceResourceImport() {
LogInfo("=== Forcing resource import to all planets ===")
for i = 0; i < len(BUILDER_PLANETS); i++ {
planetCoord = BUILDER_PLANETS[i]
if planetCoord != MASTER_PLANET {
importResources(planetCoord, NewResources(500000, 250000, 50000))
Sleep(5000)
}
}
}
func forceParkAll() {
LogInfo("=== Forcing fleet parking ===")
parkAllFleets()
}
func showProgress() {
printStatus()
}
// ========================= STARTUP =========================
CronExec("0 0 * * * *", func() { printStatus() })
CronExec("0 0 */2 * * *", func() { parkAllFleets() })
LogInfo("Starting Multi-Planet Ship Builder...")
LogInfo("Commands available:")
LogInfo(" showProgress() - Display current status")
LogInfo(" forceResourceImport() - Manually import resources")
LogInfo(" forceParkAll() - Manually park all fleets")
main()