440 lines
16 KiB
Plaintext
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() |