// ============================================================================= // 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()