diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 190bb4db..e80e4875 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -21,22 +21,22 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
- dotnet-version: 6.0.x
+ dotnet-version: 9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Setup GO
uses: actions/setup-go@v2
with:
- go-version: '~1.18.0'
+ go-version: '~1.23.0'
- name: Build TBot win64
- run: dotnet publish TBot -c Release -r win-x64 -o publish/win64/ -p:PublishSingleFile=true --self-contained false
+ run: dotnet publish TBot -c Release -r win-x64 -o publish/win64/ -p:PublishSingleFile=true --self-contained true
- name: Build ogamed win64
run: |
- cd ${{ github.workspace }}/ogame/cmd/ogamed
+ cd ${{ github.workspace }}/ogame/cmd/ogamed-tbot
env GOOS=windows GOARCH=amd64 go build -o ogamed.exe
cd ${{ github.workspace }}
- mv ${{ github.workspace }}/ogame/cmd/ogamed/ogamed.exe ${{ github.workspace }}/publish/win64/
+ mv ${{ github.workspace }}/ogame/cmd/ogamed-tbot/ogamed.exe ${{ github.workspace }}/publish/win64/
cp ${{ github.workspace }}/README.md ${{ github.workspace }}/publish/win64/
- name: Package win64
uses: montudor/action-zip@v1
@@ -44,13 +44,13 @@ jobs:
args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-win64.zip publish/win64/
- name: Build TBot win32
- run: dotnet publish TBot -c Release -r win-x86 -o publish/win32/ -p:PublishSingleFile=true --self-contained false
+ run: dotnet publish TBot -c Release -r win-x86 -o publish/win32/ -p:PublishSingleFile=true --self-contained true
- name: Build ogamed win32
run: |
- cd ${{ github.workspace }}/ogame/cmd/ogamed
+ cd ${{ github.workspace }}/ogame/cmd/ogamed-tbot
env GOOS=windows GOARCH=386 go build -o ogamed.exe
cd ${{ github.workspace }}/
- mv ${{ github.workspace }}/ogame/cmd/ogamed/ogamed.exe ${{ github.workspace }}/publish/win32/
+ mv ${{ github.workspace }}/ogame/cmd/ogamed-tbot/ogamed.exe ${{ github.workspace }}/publish/win32/
cp ${{ github.workspace }}/README.md ${{ github.workspace }}/publish/win32/
- name: Package win32
uses: montudor/action-zip@v1
@@ -58,13 +58,13 @@ jobs:
args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-win32.zip publish/win32/
- name: Build TBot TBot linux64
- run: dotnet publish TBot -c Release -r linux-x64 -o publish/linux64/ -p:PublishSingleFile=true --self-contained false
+ run: dotnet publish TBot -c Release -r linux-x64 -o publish/linux64/ -p:PublishSingleFile=true --self-contained true
- name: Build ogamed linux64
run: |
- cd ${{ github.workspace }}/ogame/cmd/ogamed
+ cd ${{ github.workspace }}/ogame/cmd/ogamed-tbot
env GOOS=linux GOARCH=amd64 go build -o ogamed
cd ${{ github.workspace }}
- mv ${{ github.workspace }}/ogame/cmd/ogamed/ogamed ${{ github.workspace }}/publish/linux64/
+ mv ${{ github.workspace }}/ogame/cmd/ogamed-tbot/ogamed ${{ github.workspace }}/publish/linux64/
chmod +x ${{ github.workspace }}/publish/linux64/TBot
chmod +x ${{ github.workspace }}/publish/linux64/ogamed
cp ${{ github.workspace }}/README.md ${{ github.workspace }}/publish/linux64/
@@ -74,13 +74,13 @@ jobs:
args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-linux64.zip publish/linux64/
- name: Build TBot TBot linuxarm
- run: dotnet publish TBot -c Release -r linux-arm -o publish/linuxarm/ -p:PublishSingleFile=true --self-contained false
+ run: dotnet publish TBot -c Release -r linux-arm -o publish/linuxarm/ -p:PublishSingleFile=true --self-contained true
- name: Build ogamed linuxarm
run: |
- cd ${{ github.workspace }}/ogame/cmd/ogamed
+ cd ${{ github.workspace }}/ogame/cmd/ogamed-tbot
env GOOS=linux GOARCH=arm GOARM=7 go build -o ogamed
cd ${{ github.workspace }}
- mv ${{ github.workspace }}/ogame/cmd/ogamed/ogamed ${{ github.workspace }}/publish/linuxarm/
+ mv ${{ github.workspace }}/ogame/cmd/ogamed-tbot/ogamed ${{ github.workspace }}/publish/linuxarm/
chmod +x ${{ github.workspace }}/publish/linuxarm/TBot
chmod +x ${{ github.workspace }}/publish/linuxarm/ogamed
cp ${{ github.workspace }}/README.md ${{ github.workspace }}/publish/linuxarm/
@@ -90,13 +90,13 @@ jobs:
args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-linuxarm.zip publish/linuxarm/
- name: Build TBot TBot linuxarm64
- run: dotnet publish TBot -c Release -r linux-arm64 -o publish/linuxarm64/ -p:PublishSingleFile=true --self-contained false
+ run: dotnet publish TBot -c Release -r linux-arm64 -o publish/linuxarm64/ -p:PublishSingleFile=true --self-contained true
- name: Build ogamed linuxarm64
run: |
- cd ${{ github.workspace }}/ogame/cmd/ogamed
+ cd ${{ github.workspace }}/ogame/cmd/ogamed-tbot
env GOOS=linux GOARCH=arm64 go build -o ogamed
cd ${{ github.workspace }}
- mv ${{ github.workspace }}/ogame/cmd/ogamed/ogamed ${{ github.workspace }}/publish/linuxarm64/
+ mv ${{ github.workspace }}/ogame/cmd/ogamed-tbot/ogamed ${{ github.workspace }}/publish/linuxarm64/
chmod +x ${{ github.workspace }}/publish/linuxarm64/TBot
chmod +x ${{ github.workspace }}/publish/linuxarm64/ogamed
cp ${{ github.workspace }}/README.md ${{ github.workspace }}/publish/linuxarm64/
@@ -106,13 +106,13 @@ jobs:
args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-linuxarm64.zip publish/linuxarm64/
- name: Build TBot TBot osx64
- run: dotnet publish TBot -c Release -r osx-x64 -o publish/osx64/ -p:PublishSingleFile=true --self-contained false
+ run: dotnet publish TBot -c Release -r osx-x64 -o publish/osx64/ -p:PublishSingleFile=true --self-contained true
- name: Build ogamed osx64
run: |
- cd ${{ github.workspace }}/ogame/cmd/ogamed
+ cd ${{ github.workspace }}/ogame/cmd/ogamed-tbot
env GOOS=darwin GOARCH=amd64 go build -o ogamed
cd ${{ github.workspace }}
- mv ${{ github.workspace }}/ogame/cmd/ogamed/ogamed ${{ github.workspace }}/publish/osx64/
+ mv ${{ github.workspace }}/ogame/cmd/ogamed-tbot/ogamed ${{ github.workspace }}/publish/osx64/
chmod +x ${{ github.workspace }}/publish/osx64/TBot
chmod +x ${{ github.workspace }}/publish/osx64/ogamed
cp ${{ github.workspace }}/README.md ${{ github.workspace }}/publish/osx64/
@@ -122,13 +122,13 @@ jobs:
args: zip -qq -r TBot-${{ env.SNAP_VERSION }}-osx64.zip publish/osx64/
- name: Build TBot TBot osx-arm64
- run: dotnet publish TBot -c Release -r osx-arm64 -o publish/osx-arm64/ -p:PublishSingleFile=true --self-contained false
+ run: dotnet publish TBot -c Release -r osx-arm64 -o publish/osx-arm64/ -p:PublishSingleFile=true --self-contained true
- name: Build ogamed osx-arm64
run: |
- cd ${{ github.workspace }}/ogame/cmd/ogamed
- env GOOS=darwin GOARCH=amd64 go build -o ogamed
+ cd ${{ github.workspace }}/ogame/cmd/ogamed-tbot
+ env GOOS=darwin GOARCH=arm64 go build -o ogamed
cd ${{ github.workspace }}
- mv ${{ github.workspace }}/ogame/cmd/ogamed/ogamed ${{ github.workspace }}/publish/osx-arm64/
+ mv ${{ github.workspace }}/ogame/cmd/ogamed-tbot/ogamed ${{ github.workspace }}/publish/osx-arm64/
chmod +x ${{ github.workspace }}/publish/osx-arm64/TBot
chmod +x ${{ github.workspace }}/publish/osx-arm64/ogamed
cp ${{ github.workspace }}/README.md ${{ github.workspace }}/publish/osx-arm64/
diff --git a/.github/workflows/snapshot.yml b/.github/workflows/snapshot.yml
index eac931ff..afc88037 100644
--- a/.github/workflows/snapshot.yml
+++ b/.github/workflows/snapshot.yml
@@ -22,22 +22,22 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v1
with:
- dotnet-version: 6.0.x
+ dotnet-version: 9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Setup GO
uses: actions/setup-go@v2
with:
- go-version: '~1.18.0'
+ go-version: '~1.23.0'
- name: Build TBot win64
- run: dotnet publish TBot -c Release -r win-x64 -o publish/win64/ -p:PublishSingleFile=true --self-contained false
+ run: dotnet publish TBot -c Release -r win-x64 -o publish/win64/ -p:PublishSingleFile=true --self-contained true
- name: Build ogamed win64
run: |
- cd ${{ github.workspace }}/ogame/cmd/ogamed
+ cd ${{ github.workspace }}/ogame/cmd/ogamed-tbot
env GOOS=windows GOARCH=amd64 go build -o ogamed.exe
cd ${{ github.workspace }}
- mv ${{ github.workspace }}/ogame/cmd/ogamed/ogamed.exe ${{ github.workspace }}/publish/win64/
+ mv ${{ github.workspace }}/ogame/cmd/ogamed-tbot/ogamed.exe ${{ github.workspace }}/publish/win64/
- name: Package win64
uses: montudor/action-zip@v1
with:
@@ -49,13 +49,13 @@ jobs:
path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-win64.zip
- name: Build TBot win32
- run: dotnet publish TBot -c Release -r win-x86 -o publish/win32/ -p:PublishSingleFile=true --self-contained false
+ run: dotnet publish TBot -c Release -r win-x86 -o publish/win32/ -p:PublishSingleFile=true --self-contained true
- name: Build ogamed win32
run: |
- cd ${{ github.workspace }}/ogame/cmd/ogamed
+ cd ${{ github.workspace }}/ogame/cmd/ogamed-tbot
env GOOS=windows GOARCH=386 go build -o ogamed.exe
cd ${{ github.workspace }}/
- mv ${{ github.workspace }}/ogame/cmd/ogamed/ogamed.exe ${{ github.workspace }}/publish/win32/
+ mv ${{ github.workspace }}/ogame/cmd/ogamed-tbot/ogamed.exe ${{ github.workspace }}/publish/win32/
- name: Package win32
uses: montudor/action-zip@v1
with:
@@ -67,13 +67,13 @@ jobs:
path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-win32.zip
- name: Build TBot linux64
- run: dotnet publish TBot -c Release -r linux-x64 -o publish/linux64/ -p:PublishSingleFile=true --self-contained false
+ run: dotnet publish TBot -c Release -r linux-x64 -o publish/linux64/ -p:PublishSingleFile=true --self-contained true
- name: Build ogamed linux64
run: |
- cd ${{ github.workspace }}/ogame/cmd/ogamed
+ cd ${{ github.workspace }}/ogame/cmd/ogamed-tbot
env GOOS=linux GOARCH=amd64 go build -o ogamed
cd ${{ github.workspace }}
- mv ${{ github.workspace }}/ogame/cmd/ogamed/ogamed ${{ github.workspace }}/publish/linux64/
+ mv ${{ github.workspace }}/ogame/cmd/ogamed-tbot/ogamed ${{ github.workspace }}/publish/linux64/
chmod +x ${{ github.workspace }}/publish/linux64/TBot
chmod +x ${{ github.workspace }}/publish/linux64/ogamed
- name: Package linux64
@@ -87,13 +87,13 @@ jobs:
path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-linux64.zip
- name: Build TBot linuxarm
- run: dotnet publish TBot -c Release -r linux-arm -o publish/linuxarm/ -p:PublishSingleFile=true --self-contained false
+ run: dotnet publish TBot -c Release -r linux-arm -o publish/linuxarm/ -p:PublishSingleFile=true --self-contained true
- name: Build ogamed linuxarm
run: |
- cd ${{ github.workspace }}/ogame/cmd/ogamed
+ cd ${{ github.workspace }}/ogame/cmd/ogamed-tbot
env GOOS=linux GOARCH=arm GOARM=7 go build -o ogamed
cd ${{ github.workspace }}
- mv ${{ github.workspace }}/ogame/cmd/ogamed/ogamed ${{ github.workspace }}/publish/linuxarm/
+ mv ${{ github.workspace }}/ogame/cmd/ogamed-tbot/ogamed ${{ github.workspace }}/publish/linuxarm/
chmod +x ${{ github.workspace }}/publish/linuxarm/TBot
chmod +x ${{ github.workspace }}/publish/linuxarm/ogamed
- name: Package linuxarm
@@ -107,13 +107,13 @@ jobs:
path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-linuxarm.zip
- name: Build TBot linuxarm64
- run: dotnet publish TBot -c Release -r linux-arm64 -o publish/linuxarm64/ -p:PublishSingleFile=true --self-contained false
+ run: dotnet publish TBot -c Release -r linux-arm64 -o publish/linuxarm64/ -p:PublishSingleFile=true --self-contained true
- name: Build ogamed linuxarm64
run: |
- cd ${{ github.workspace }}/ogame/cmd/ogamed
+ cd ${{ github.workspace }}/ogame/cmd/ogamed-tbot
env GOOS=linux GOARCH=arm64 go build -o ogamed
cd ${{ github.workspace }}
- mv ${{ github.workspace }}/ogame/cmd/ogamed/ogamed ${{ github.workspace }}/publish/linuxarm64/
+ mv ${{ github.workspace }}/ogame/cmd/ogamed-tbot/ogamed ${{ github.workspace }}/publish/linuxarm64/
chmod +x ${{ github.workspace }}/publish/linuxarm64/TBot
chmod +x ${{ github.workspace }}/publish/linuxarm64/ogamed
- name: Package linuxarm64
@@ -127,13 +127,13 @@ jobs:
path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-linuxarm64.zip
- name: Build TBot osx64
- run: dotnet publish TBot -c Release -r osx-x64 -o publish/osx64/ -p:PublishSingleFile=true --self-contained false
+ run: dotnet publish TBot -c Release -r osx-x64 -o publish/osx64/ -p:PublishSingleFile=true --self-contained true
- name: Build ogamed osx64
run: |
- cd ${{ github.workspace }}/ogame/cmd/ogamed
+ cd ${{ github.workspace }}/ogame/cmd/ogamed-tbot
env GOOS=darwin GOARCH=amd64 go build -o ogamed
cd ${{ github.workspace }}
- mv ${{ github.workspace }}/ogame/cmd/ogamed/ogamed ${{ github.workspace }}/publish/osx64/
+ mv ${{ github.workspace }}/ogame/cmd/ogamed-tbot/ogamed ${{ github.workspace }}/publish/osx64/
chmod +x ${{ github.workspace }}/publish/osx64/TBot
chmod +x ${{ github.workspace }}/publish/osx64/ogamed
- name: Package osx64
@@ -147,13 +147,13 @@ jobs:
path: ${{ github.workspace }}/TBot-${{ env.SNAP_VERSION }}-osx64.zip
- name: Build TBot osx-arm64
- run: dotnet publish TBot -c Release -r osx-arm64 -o publish/osx-arm64/ -p:PublishSingleFile=true --self-contained false
+ run: dotnet publish TBot -c Release -r osx-arm64 -o publish/osx-arm64/ -p:PublishSingleFile=true --self-contained true
- name: Build ogamed osx-arm64
run: |
- cd ${{ github.workspace }}/ogame/cmd/ogamed
+ cd ${{ github.workspace }}/ogame/cmd/ogamed-tbot
env GOOS=darwin GOARCH=arm64 go build -o ogamed
cd ${{ github.workspace }}
- mv ${{ github.workspace }}/ogame/cmd/ogamed/ogamed ${{ github.workspace }}/publish/osx-arm64/
+ mv ${{ github.workspace }}/ogame/cmd/ogamed-tbot/ogamed ${{ github.workspace }}/publish/osx-arm64/
chmod +x ${{ github.workspace }}/publish/osx-arm64/TBot
chmod +x ${{ github.workspace }}/publish/osx-arm64/ogamed
- name: Package osx-arm64
diff --git a/.gitmodules b/.gitmodules
index 02037af9..58de3448 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,4 +1,4 @@
[submodule "ogame"]
path = ogame
- url = https://github.com/ogame-tbot-net/ogame.git
- branch = develop_v11.9
+ url = https://github.com/0xE232FE/ogame.mod.git
+ branch = develop-ogamed-tbot
diff --git a/TBot.Common/Logging/LogSender.cs b/TBot.Common/Logging/LogSender.cs
index 2c0592e0..5683270f 100644
--- a/TBot.Common/Logging/LogSender.cs
+++ b/TBot.Common/Logging/LogSender.cs
@@ -13,6 +13,7 @@ public enum LogSender {
Brain,
//Brain Features
AutoCargo,
+ AutoDefence,
AutoMine,
AutoRepatriate,
AutoResearch,
diff --git a/TBot.Common/Logging/Sinks/TBotColoredConsoleSink.cs b/TBot.Common/Logging/Sinks/TBotColoredConsoleSink.cs
index 80dfe81a..aa47dc65 100644
--- a/TBot.Common/Logging/Sinks/TBotColoredConsoleSink.cs
+++ b/TBot.Common/Logging/Sinks/TBotColoredConsoleSink.cs
@@ -37,6 +37,7 @@ public void Emit(LogEvent logEvent) {
ConsoleColor consoleColor = sender switch {
LogSender.Brain => ConsoleColor.Blue,
LogSender.AutoCargo => ConsoleColor.Blue,
+ LogSender.AutoDefence => ConsoleColor.Blue,
LogSender.AutoMine => ConsoleColor.Blue,
LogSender.AutoRepatriate => ConsoleColor.Blue,
LogSender.AutoResearch => ConsoleColor.Blue,
diff --git a/TBot.Common/TBot.Common.csproj b/TBot.Common/TBot.Common.csproj
index 3f439b8a..f2a0a7fe 100644
--- a/TBot.Common/TBot.Common.csproj
+++ b/TBot.Common/TBot.Common.csproj
@@ -1,18 +1,18 @@
- net6.0
+ net9.0
enable
enable
-
-
-
+
+
+
-
-
+
+
diff --git a/TBot.Ogame.Infrastructure/Enums/Feature.cs b/TBot.Ogame.Infrastructure/Enums/Feature.cs
index d9fdd956..555fd575 100644
--- a/TBot.Ogame.Infrastructure/Enums/Feature.cs
+++ b/TBot.Ogame.Infrastructure/Enums/Feature.cs
@@ -21,6 +21,7 @@ public enum Feature {
BrainLifeformAutoMine = 12,
BrainLifeformAutoResearch = 13,
AutoDiscovery = 14,
+ BrainAutobuildDefence = 15,
BrainCelestialAutoMine = 101,
BrainCelestialLifeformAutoMine = 102,
diff --git a/TBot.Ogame.Infrastructure/Enums/Features.cs b/TBot.Ogame.Infrastructure/Enums/Features.cs
index 32f220ba..5d7dc956 100644
--- a/TBot.Ogame.Infrastructure/Enums/Features.cs
+++ b/TBot.Ogame.Infrastructure/Enums/Features.cs
@@ -9,6 +9,7 @@ public static class Features {
public static readonly List AllFeatures = new List() {
Feature.Defender,
Feature.BrainAutobuildCargo,
+ Feature.BrainAutobuildDefence,
Feature.BrainAutoRepatriate,
Feature.BrainAutoMine,
Feature.BrainLifeformAutoMine,
diff --git a/TBot.Ogame.Infrastructure/Enums/SendFleetCode.cs b/TBot.Ogame.Infrastructure/Enums/SendFleetCode.cs
index 79b83a8e..45df11af 100644
--- a/TBot.Ogame.Infrastructure/Enums/SendFleetCode.cs
+++ b/TBot.Ogame.Infrastructure/Enums/SendFleetCode.cs
@@ -9,6 +9,7 @@ public enum SendFleetCode : int {
GenericError = 0,
AfterSleepTime = -1,
NotEnoughSlots = -2,
- QuickerToWaitForProduction = -3
+ QuickerToWaitForProduction = -3,
+ NotEnoughRessources = -4
}
}
diff --git a/TBot.Ogame.Infrastructure/IOgameService.cs b/TBot.Ogame.Infrastructure/IOgameService.cs
index 1c52da84..230e9811 100644
--- a/TBot.Ogame.Infrastructure/IOgameService.cs
+++ b/TBot.Ogame.Infrastructure/IOgameService.cs
@@ -95,6 +95,8 @@ void Initialize(Credentials credentials,
Task SendMessage(int playerID, string message);
Task SetUserAgent(string userAgent);
Task SolveCaptcha(string challengeID, int answer);
- Task AbandonCelestial(Celestial celestial);
+ Task AbandonCelestial(Celestial celestial);
+ Task> GetPositionsAvailableForDiscoveryFleet(Celestial celestial, Coordinate coordinate);
+ Task GetAvailableDiscoveries(Celestial celestial);
}
}
diff --git a/TBot.Ogame.Infrastructure/Models/AuctionResourcesValue.cs b/TBot.Ogame.Infrastructure/Models/AuctionResourcesValue.cs
index 7046124c..6f01d3ab 100644
--- a/TBot.Ogame.Infrastructure/Models/AuctionResourcesValue.cs
+++ b/TBot.Ogame.Infrastructure/Models/AuctionResourcesValue.cs
@@ -15,7 +15,7 @@ public class AuctionResourcesValue {
[JsonProperty("name")]
public string Name { get; set; }
- public int otherPlanetId { get; set; } = 0;
+ public int? otherPlanetId { get; set; } = 0;
public override string ToString() {
string outStr = "[";
diff --git a/TBot.Ogame.Infrastructure/Models/Defences.cs b/TBot.Ogame.Infrastructure/Models/Defences.cs
index 926cca06..55823f5a 100644
--- a/TBot.Ogame.Infrastructure/Models/Defences.cs
+++ b/TBot.Ogame.Infrastructure/Models/Defences.cs
@@ -19,15 +19,179 @@ public class Defences {
public long AntiBallisticMissiles { get; set; }
public long InterplanetaryMissiles { get; set; }
- public int GetAmount(Buildables defence) {
- int output = 0;
- foreach (PropertyInfo prop in GetType().GetProperties()) {
- if (prop.Name == defence.ToString()) {
- output = (int) prop.GetValue(this);
+ public Defences(
+ long rocketlauncher = 0,
+ long lightlaser = 0,
+ long heavylaser = 0,
+ long gausscannon = 0,
+ long ioncannon = 0,
+ long plasmaturret = 0,
+ long smallshielddome = 0,
+ long largeshielddome = 0,
+ long antiballisticmissiles = 0,
+ long interplanetarymissiles = 0
+ ) {
+ RocketLauncher = rocketlauncher;
+ LightLaser = lightlaser;
+ HeavyLaser = heavylaser;
+ GaussCannon = gausscannon;
+ IonCannon = ioncannon;
+ PlasmaTurret = plasmaturret;
+ SmallShieldDome = smallshielddome;
+ LargeShieldDome = largeshielddome;
+ AntiBallisticMissiles = antiballisticmissiles;
+ InterplanetaryMissiles = interplanetarymissiles;
+ }
+ public bool IsEmpty() {
+ return RocketLauncher == 0
+ && LightLaser == 0
+ && HeavyLaser == 0
+ && GaussCannon == 0
+ && IonCannon == 0
+ && PlasmaTurret == 0
+ && SmallShieldDome == 0
+ && LargeShieldDome == 0
+ && AntiBallisticMissiles == 0
+ && InterplanetaryMissiles == 0;
+ }
+ public long GetDefencePoints() {
+ long output = 0;
+ output += RocketLauncher * 20;
+ output += LightLaser * 20;
+ output += HeavyLaser * 80;
+ output += GaussCannon * 370;
+ output += IonCannon * 80;
+ output += PlasmaTurret * 1300;
+ output += SmallShieldDome * 200;
+ output += LargeShieldDome * 1000;
+ output += AntiBallisticMissiles * 100;
+ output += InterplanetaryMissiles * 245;
+ return output / 10;
+ }
+ public Resources GetDefenceCost() {
+ Resources output = new();
+ output.Metal = RocketLauncher * 2000 + LightLaser * 1500 + HeavyLaser * 6000 + GaussCannon * 20000 + IonCannon * 3000 + PlasmaTurret * 50000 + SmallShieldDome * 10000 + LargeShieldDome * 50000 + AntiBallisticMissiles * 2000 + InterplanetaryMissiles * 5000;
+ output.Crystal = RocketLauncher * 1000 + LightLaser * 500 + HeavyLaser * 2000 + GaussCannon * 15000 + IonCannon * 1000 + PlasmaTurret * 50000 + SmallShieldDome * 5000 + LargeShieldDome * 25000 + AntiBallisticMissiles * 2000 + InterplanetaryMissiles * 2500;
+ output.Deuterium = RocketLauncher * 0 + LightLaser * 0 + HeavyLaser * 0 + GaussCannon * 0 + IonCannon * 0 + PlasmaTurret * 2000 + SmallShieldDome * 0 + LargeShieldDome * 10000 + AntiBallisticMissiles * 0 + InterplanetaryMissiles * 1000;
+ return output;
+ }
+ public Defences Add(String buildable, long quantity) {
+ foreach (PropertyInfo prop in this.GetType().GetProperties()) {
+ if (prop.Name == buildable) {
+ prop.SetValue(this, (long) prop.GetValue(this) + quantity);
+ }
+ }
+ return this;
+ }
+
+ public Defences Remove(String buildable, int quantity) {
+ foreach (PropertyInfo prop in this.GetType().GetProperties()) {
+ if (prop.Name == buildable) {
+ long val = (long) prop.GetValue(this);
+ if (val >= quantity)
+ prop.SetValue(this, val);
+ else
+ prop.SetValue(this, 0);
+ }
+ }
+ return this;
+ }
+
+ public long GetAmount(Defences buildable) {
+ foreach (PropertyInfo prop in this.GetType().GetProperties()) {
+ if (prop.Name == buildable.ToString()) {
+ return (long) prop.GetValue(this);
+ }
+ }
+ return 0;
+ }
+
+ public void SetAmount(Defences buildable, long number) {
+ foreach (PropertyInfo prop in this.GetType().GetProperties()) {
+ if (prop.Name == buildable.ToString()) {
+ prop.SetValue(this, number);
+ return;
}
}
+ }
+
+ public bool HasAtLeast(Defences defences, long times = 1) {
+ foreach (PropertyInfo prop in this.GetType().GetProperties()) {
+ if ((long) prop.GetValue(this) * times < (long) prop.GetValue(defences)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public override string ToString() {
+ string output = "";
+ foreach (PropertyInfo prop in this.GetType().GetProperties()) {
+ if ((long) prop.GetValue(this) == 0)
+ continue;
+ output += $"{prop.Name}: {prop.GetValue(this)}; ";
+ }
return output;
}
- }
+ public Defences Sum(Defences defencesToSum) {
+ Defences output = new();
+ output.RocketLauncher = RocketLauncher + defencesToSum.RocketLauncher;
+ output.LightLaser = LightLaser + defencesToSum.LightLaser;
+ output.HeavyLaser = HeavyLaser + defencesToSum.HeavyLaser;
+ output.GaussCannon = GaussCannon + defencesToSum.GaussCannon;
+ output.IonCannon = IonCannon + defencesToSum.IonCannon;
+ output.PlasmaTurret = PlasmaTurret + defencesToSum.PlasmaTurret;
+ output.SmallShieldDome = SmallShieldDome + defencesToSum.SmallShieldDome;
+ output.LargeShieldDome = LargeShieldDome + defencesToSum.LargeShieldDome;
+ output.AntiBallisticMissiles = AntiBallisticMissiles + defencesToSum.AntiBallisticMissiles;
+ output.InterplanetaryMissiles = InterplanetaryMissiles + defencesToSum.InterplanetaryMissiles;
-}
+ return output;
+ }
+ public Defences Difference(Defences defencesToSubtract) {
+ Defences output = new();
+ output.RocketLauncher = RocketLauncher - defencesToSubtract.RocketLauncher;
+ if (output.RocketLauncher < 0)
+ output.RocketLauncher = 0;
+ output.LightLaser = LightLaser - defencesToSubtract.LightLaser;
+ if (output.LightLaser < 0)
+ output.LightLaser = 0;
+ output.HeavyLaser = HeavyLaser - defencesToSubtract.HeavyLaser;
+ if (output.HeavyLaser < 0)
+ output.HeavyLaser = 0;
+ output.GaussCannon = GaussCannon - defencesToSubtract.GaussCannon;
+ if (output.GaussCannon < 0)
+ output.GaussCannon = 0;
+ output.IonCannon = IonCannon - defencesToSubtract.IonCannon;
+ if (output.IonCannon < 0)
+ output.IonCannon = 0;
+ output.PlasmaTurret = PlasmaTurret - defencesToSubtract.PlasmaTurret;
+ if (output.PlasmaTurret < 0)
+ output.PlasmaTurret = 0;
+ output.SmallShieldDome = SmallShieldDome - defencesToSubtract.SmallShieldDome;
+ if (output.SmallShieldDome < 0)
+ output.SmallShieldDome = 0;
+ output.LargeShieldDome = LargeShieldDome - defencesToSubtract.LargeShieldDome;
+ if (output.LargeShieldDome < 0)
+ output.LargeShieldDome = 0;
+ output.AntiBallisticMissiles = AntiBallisticMissiles - defencesToSubtract.AntiBallisticMissiles;
+ if (output.AntiBallisticMissiles < 0)
+ output.AntiBallisticMissiles = 0;
+ output.InterplanetaryMissiles = InterplanetaryMissiles - defencesToSubtract.InterplanetaryMissiles;
+ if (output.InterplanetaryMissiles < 0)
+ output.InterplanetaryMissiles = 0;
+
+ return output;
+ }
+ public Dictionary GetDefenceTypesWithAmount() {
+ Dictionary defenceTypes = new();
+ foreach (var prop in this.GetType().GetProperties()) {
+ Buildables buildable = (Buildables) Enum.Parse(typeof(Buildables), prop.Name);
+ long amount = (long) prop.GetValue(this);
+ if (amount > 0)
+ defenceTypes.Add(buildable, amount);
+ }
+ return defenceTypes;
+ }
+ }
+}
\ No newline at end of file
diff --git a/TBot.Ogame.Infrastructure/Models/EspionageReport.cs b/TBot.Ogame.Infrastructure/Models/EspionageReport.cs
index 051f5a75..88298908 100644
--- a/TBot.Ogame.Infrastructure/Models/EspionageReport.cs
+++ b/TBot.Ogame.Infrastructure/Models/EspionageReport.cs
@@ -112,8 +112,8 @@ public override string ToString() {
/// Get whether or not the scanned planet has any defence (either ships or defence) against an attack.
///
/// Returns true if the target is defenceless, false otherwise.
- public bool IsDefenceless() {
- if (HasDefensesInformation && HasFleetInformation) {
+ public bool IsDefenceless(bool isUsingProbes = false) {
+ if (HasDefensesInformation && HasFleetInformation && !isUsingProbes) {
return LightFighter == null
&& HeavyFighter == null
&& Cruiser == null
@@ -135,6 +135,29 @@ public bool IsDefenceless() {
&& PlasmaTurret == null
&& SmallShieldDome == null
&& LargeShieldDome == null;
+ } else if (HasDefensesInformation && HasFleetInformation && isUsingProbes) {
+ return SolarSatellite == null
+ && LightFighter == null
+ && HeavyFighter == null
+ && Cruiser == null
+ && Battleship == null
+ && Battlecruiser == null
+ && Bomber == null
+ && Destroyer == null
+ && Deathstar == null
+ && SmallCargo == null
+ && LargeCargo == null
+ && Recycler == null
+ && Reaper == null
+ && Pathfinder == null
+ && RocketLauncher == null
+ && LightLaser == null
+ && HeavyLaser == null
+ && GaussCannon == null
+ && IonCannon == null
+ && PlasmaTurret == null
+ && SmallShieldDome == null
+ && LargeShieldDome == null;
}
return false;
}
diff --git a/TBot.Ogame.Infrastructure/Models/GalaxyInfo.cs b/TBot.Ogame.Infrastructure/Models/GalaxyInfo.cs
index 148062cc..f55ac372 100644
--- a/TBot.Ogame.Infrastructure/Models/GalaxyInfo.cs
+++ b/TBot.Ogame.Infrastructure/Models/GalaxyInfo.cs
@@ -3,12 +3,14 @@
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using TBot.Ogame.Infrastructure.Enums;
namespace TBot.Ogame.Infrastructure.Models {
public class GalaxyInfo {
public int Galaxy { get; set; }
public int System { get; set; }
public List Planets { get; set; }
+ public List AvailableMissions { get; set; }
public ExpeditionDebris ExpeditionDebris { get; set; }
}
}
diff --git a/TBot.Ogame.Infrastructure/Models/LFBonuses.cs b/TBot.Ogame.Infrastructure/Models/LFBonuses.cs
index 42e0b6bb..c3f96945 100644
--- a/TBot.Ogame.Infrastructure/Models/LFBonuses.cs
+++ b/TBot.Ogame.Infrastructure/Models/LFBonuses.cs
@@ -8,6 +8,32 @@
namespace TBot.Ogame.Infrastructure.Models {
public class LFBonuses {
+
+ public LFBonusesResources LfResourceBonuses { get; set; }
+ public LFBonusesCharacterClasses CharacterClassesBonuses { get; set; }
+ public Dictionary LfShipBonuses { get; set; }
+ public Dictionary CostTimeBonuses { get; set; }
+ public LFBonusesMisc MiscBonuses { get; set; }
+ public Dictionary LfShipBonusesInt {
+ get {
+ if (LfShipBonuses == null)
+ return new Dictionary();
+ return LfShipBonuses
+ .Where(kvp => int.TryParse(kvp.Key, out _))
+ .ToDictionary(kvp => int.Parse(kvp.Key), kvp => kvp.Value);
+ }
+ }
+ public Dictionary CostTimeBonusesInt {
+ get {
+ if (CostTimeBonuses == null)
+ return new Dictionary();
+ return CostTimeBonuses
+ .Where(kvp => int.TryParse(kvp.Key, out _))
+ .ToDictionary(kvp => int.Parse(kvp.Key), kvp => kvp.Value);
+ }
+ }
+
+
public LFBonusesProduction Production { get; set; }
public LFBonusesExpeditions Expeditions { get; set; }
public LFBonusesDens Dens { get; set; }
@@ -29,6 +55,12 @@ public class LFBonuses {
public float InactivesLoot { get; set; }
public LFBonuses() {
+ LfResourceBonuses = new LFBonusesResources();
+ CharacterClassesBonuses = new LFBonusesCharacterClasses();
+ LfShipBonuses = new Dictionary();
+ CostTimeBonuses = new Dictionary();
+ MiscBonuses = new LFBonusesMisc();
+
Production = new LFBonusesProduction();
Expeditions = new LFBonusesExpeditions();
Dens = new LFBonusesDens();
@@ -44,24 +76,24 @@ public LFBonuses() {
public float GetShipCargoBonus(Buildables buildable) {
float bonusCargo = 0;
- if (this != null && Ships != null && Ships.Count > 0 && Ships.ContainsKey((int) buildable)) {
- bonusCargo = Ships.GetValueOrDefault((int) buildable).Cargo;
+ if (this != null && LfShipBonusesInt != null && LfShipBonusesInt.Count > 0 && LfShipBonusesInt.ContainsKey((int) buildable)) {
+ bonusCargo = LfShipBonusesInt.GetValueOrDefault((int) buildable).CargoCapacity;
}
return bonusCargo;
}
public float GetShipSpeedBonus(Buildables buildable) {
float bonusSpeed = 0;
- if (this != null && Ships != null && Ships.Count > 0 && Ships.ContainsKey((int) buildable)) {
- bonusSpeed = Ships.GetValueOrDefault((int) buildable).Speed;
+ if (this != null && LfShipBonusesInt != null && LfShipBonusesInt.Count > 0 && LfShipBonusesInt.ContainsKey((int) buildable)) {
+ bonusSpeed = LfShipBonusesInt.GetValueOrDefault((int) buildable).Speed;
}
return bonusSpeed;
}
public float GetShipConsumptionBonus(Buildables buildable) {
float bonusCons = 0;
- if (this != null && Ships != null && Ships.Count > 0 && Ships.ContainsKey((int) buildable)) {
- bonusCons = Ships.GetValueOrDefault((int) buildable).Consumption;
+ if (this != null && LfShipBonusesInt != null && LfShipBonusesInt.Count > 0 && LfShipBonusesInt.ContainsKey((int) buildable)) {
+ bonusCons = LfShipBonusesInt.GetValueOrDefault((int) buildable).FuelConsumption;
}
return bonusCons;
}
diff --git a/TBot.Ogame.Infrastructure/Models/LFBonusesBase.cs b/TBot.Ogame.Infrastructure/Models/LFBonusesBase.cs
index 8cb9e220..229c92f0 100644
--- a/TBot.Ogame.Infrastructure/Models/LFBonusesBase.cs
+++ b/TBot.Ogame.Infrastructure/Models/LFBonusesBase.cs
@@ -9,9 +9,9 @@ public class LFBonusesBase {
public float Cost { get; set; }
public float Duration { get; set; }
- public LFBonusesBase() {
- Cost = 0;
- Duration = 0;
+ public LFBonusesBase(float cost = 0, float duration = 0) {
+ Cost = cost;
+ Duration = duration;
}
}
}
diff --git a/TBot.Ogame.Infrastructure/Models/LFBonusesCharacterClasses.cs b/TBot.Ogame.Infrastructure/Models/LFBonusesCharacterClasses.cs
new file mode 100644
index 00000000..fef8ab6e
--- /dev/null
+++ b/TBot.Ogame.Infrastructure/Models/LFBonusesCharacterClasses.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TBot.Ogame.Infrastructure.Models {
+ public class LFBonusesCharacterClasses {
+ public float Characterclasses3 { get; set; }
+ public LFBonusesCharacterClasses(float characterclasses3 = 0) {
+ Characterclasses3 = characterclasses3;
+ }
+ }
+}
diff --git a/TBot.Ogame.Infrastructure/Models/LFBonusesCrawlers.cs b/TBot.Ogame.Infrastructure/Models/LFBonusesCrawlers.cs
index 3d97aa6d..3dc5987c 100644
--- a/TBot.Ogame.Infrastructure/Models/LFBonusesCrawlers.cs
+++ b/TBot.Ogame.Infrastructure/Models/LFBonusesCrawlers.cs
@@ -10,10 +10,10 @@ public class LFBonusesCrawlers {
public float Production { get; set; }
public float Number { get; set; }
- public LFBonusesCrawlers() {
- EnergyReduction = 0;
- Production = 0;
- Number = 0;
+ public LFBonusesCrawlers(float energyReduction = 0, float production = 0, float number = 0) {
+ EnergyReduction = energyReduction;
+ Production = production;
+ Number = number;
}
}
}
diff --git a/TBot.Ogame.Infrastructure/Models/LFBonusesDens.cs b/TBot.Ogame.Infrastructure/Models/LFBonusesDens.cs
index e9155fd2..351c843d 100644
--- a/TBot.Ogame.Infrastructure/Models/LFBonusesDens.cs
+++ b/TBot.Ogame.Infrastructure/Models/LFBonusesDens.cs
@@ -10,10 +10,10 @@ public class LFBonusesDens {
public float Crystal { get; set; }
public float Deuterium { get; set; }
- public LFBonusesDens() {
- Metal = 0;
- Crystal = 0;
- Deuterium = 0;
+ public LFBonusesDens(float metal = 0, float crystal = 0, float deuterium = 0) {
+ Metal = metal;
+ Crystal = crystal;
+ Deuterium = deuterium;
}
}
}
diff --git a/TBot.Ogame.Infrastructure/Models/LFBonusesExpeditions.cs b/TBot.Ogame.Infrastructure/Models/LFBonusesExpeditions.cs
index 40a44d95..e2f4cbe8 100644
--- a/TBot.Ogame.Infrastructure/Models/LFBonusesExpeditions.cs
+++ b/TBot.Ogame.Infrastructure/Models/LFBonusesExpeditions.cs
@@ -14,14 +14,14 @@ public class LFBonusesExpeditions {
public float Slots { get; set; }
public float LessEnemies {get; set;}
- public LFBonusesExpeditions() {
- Ships = 0;
- Resources = 0;
- Speed = 0;
- DarkMatter = 0;
- FleetLoss = 0;
- Slots = 0;
- LessEnemies = 0;
+ public LFBonusesExpeditions(float ships = 0, float resources = 0, float speed = 0, float darkmatter = 0, float fleetloss = 0, float slots = 0, float lessenemies = 0) {
+ Ships = ships;
+ Resources = resources;
+ Speed = speed;
+ DarkMatter = darkmatter;
+ FleetLoss = fleetloss;
+ Slots = slots;
+ LessEnemies = lessenemies;
}
}
}
diff --git a/TBot.Ogame.Infrastructure/Models/LFBonusesMisc.cs b/TBot.Ogame.Infrastructure/Models/LFBonusesMisc.cs
new file mode 100644
index 00000000..8013f286
--- /dev/null
+++ b/TBot.Ogame.Infrastructure/Models/LFBonusesMisc.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TBot.Ogame.Infrastructure.Models {
+ public class LFBonusesMisc {
+ public float PhalanxRange { get; set; }
+
+ public LFBonusesMisc(float phalanxrange = 0) {
+ PhalanxRange = phalanxrange;
+ }
+ }
+}
diff --git a/TBot.Ogame.Infrastructure/Models/LFBonusesMoons.cs b/TBot.Ogame.Infrastructure/Models/LFBonusesMoons.cs
index 9cb7d919..42e00f1b 100644
--- a/TBot.Ogame.Infrastructure/Models/LFBonusesMoons.cs
+++ b/TBot.Ogame.Infrastructure/Models/LFBonusesMoons.cs
@@ -10,10 +10,10 @@ public class LFBonusesMoons {
public float Size { get; set; }
public float Chance { get; set; }
- public LFBonusesMoons() {
- Fields = 0;
- Size = 0;
- Chance = 0;
+ public LFBonusesMoons(float fields = 0, float size = 0, float chance = 0) {
+ Fields = fields;
+ Size = size;
+ Chance = chance;
}
}
}
diff --git a/TBot.Ogame.Infrastructure/Models/LFBonusesProduction.cs b/TBot.Ogame.Infrastructure/Models/LFBonusesProduction.cs
index 992dbf1e..3461e0de 100644
--- a/TBot.Ogame.Infrastructure/Models/LFBonusesProduction.cs
+++ b/TBot.Ogame.Infrastructure/Models/LFBonusesProduction.cs
@@ -13,13 +13,13 @@ public class LFBonusesProduction {
public float Food { get; set; }
public float Population { get; set; }
- public LFBonusesProduction() {
- Metal = 0;
- Crystal = 0;
- Deuterium = 0;
- Energy = 0;
- Food = 0;
- Population = 0;
+ public LFBonusesProduction(float metal = 0, float crystal = 0, float deuterium = 0, float energy = 0, float food = 0, float population = 0) {
+ Metal = metal;
+ Crystal = crystal;
+ Deuterium = deuterium;
+ Energy = energy;
+ Food = food;
+ Population = population;
}
}
}
diff --git a/TBot.Ogame.Infrastructure/Models/LFBonusesResources.cs b/TBot.Ogame.Infrastructure/Models/LFBonusesResources.cs
new file mode 100644
index 00000000..1c8c57ab
--- /dev/null
+++ b/TBot.Ogame.Infrastructure/Models/LFBonusesResources.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TBot.Ogame.Infrastructure.Models {
+ public class LFBonusesResources {
+ public float ResourcesExpedition { get; set; }
+ public LFBonusesResources(float resourcesexpedition = 0) {
+ ResourcesExpedition = resourcesexpedition;
+ }
+ }
+}
diff --git a/TBot.Ogame.Infrastructure/Models/LFBonusesShip.cs b/TBot.Ogame.Infrastructure/Models/LFBonusesShip.cs
index 25bd6a48..bc266456 100644
--- a/TBot.Ogame.Infrastructure/Models/LFBonusesShip.cs
+++ b/TBot.Ogame.Infrastructure/Models/LFBonusesShip.cs
@@ -6,22 +6,22 @@
namespace TBot.Ogame.Infrastructure.Models {
public class LFBonusesShip {
- public float Armour { get; set; }
- public float Shield { get; set; }
- public float Weapon { get; set; }
- public float Cargo { get; set; }
- public float Speed { get; set; }
- public float Consumption { get; set; }
- public float Duration { get; set; }
+ public int ID { get; set; }
+ public float StructuralIntegrity { get; set; }
+ public float ShieldPower { get; set; }
+ public float WeaponPower { get; set; }
+ public float Speed { get; set; }
+ public float CargoCapacity { get; set; }
+ public float FuelConsumption { get; set; }
- public LFBonusesShip() {
- Armour = 0;
- Shield = 0;
- Weapon = 0;
- Cargo = 0;
- Speed = 0;
- Consumption = 0;
- Duration = 0;
+ public LFBonusesShip(int id = -1, float armour = 0, float shield = 0, float weapon = 0, float speed = 0, float cargo = 0, float consumption = 0) {
+ ID = id;
+ StructuralIntegrity = armour;
+ ShieldPower = shield;
+ WeaponPower = weapon;
+ Speed = speed;
+ CargoCapacity = cargo;
+ FuelConsumption = consumption;
}
}
}
diff --git a/TBot.Ogame.Infrastructure/Models/LFBuildings.cs b/TBot.Ogame.Infrastructure/Models/LFBuildings.cs
index 01368af3..e4c03c6d 100644
--- a/TBot.Ogame.Infrastructure/Models/LFBuildings.cs
+++ b/TBot.Ogame.Infrastructure/Models/LFBuildings.cs
@@ -66,6 +66,58 @@ public class LFBuildings {
public int ShipManufacturingHall { get; set; }
public int SupraRefractor { get; set; }
+ public LFBuildings(int lifeformType = 0, int residentialsector = 0, int biospherefarm = 0, int researchcentre = 0, int academyofsciences = 0, int neurocalibrationcentre = 0, int highenergysmelting = 0, int foodsilo = 0, int fusionpoweredproduction = 0, int skyscraper = 0, int biotechlab = 0, int metropolis = 0, int planetaryshield = 0, int meditationenclave = 0, int crystalfarm = 0, int runetechnologium = 0, int runeforge = 0, int oriktorium = 0, int magmaforge = 0, int disruptionchamber = 0, int megalith = 0, int crystalrefinery = 0, int deuteriumsynthesiser = 0, int mineralresearchcentre = 0, int advancedrecyclingplant = 0, int assemblyline = 0, int fusioncellfactory = 0, int roboticsresearchcentre = 0, int updatenetwork = 0, int quantumcomputercentre = 0, int automatisedassemblycentre = 0, int highperformancetransformer = 0, int microchipassemblyline = 0, int productionassemblyhall = 0, int highperformancesynthesiser = 0, int chipmassproduction = 0, int nanorepairbots = 0, int sanctuary = 0, int antimattercondenser = 0, int vortexchamber = 0, int hallsofrealisation = 0, int forumoftranscendence = 0, int antimatterconvector = 0, int cloninglaboratory = 0, int chrysalisaccelerator = 0, int biomodifier = 0, int psionicmodulator = 0, int shipmanufacturinghall = 0, int suprarefractor = 0) {
+ LifeformType = lifeformType;
+ ResidentialSector = residentialsector;
+ BiosphereFarm = biospherefarm;
+ ResearchCentre = researchcentre;
+ AcademyOfSciences = academyofsciences;
+ NeuroCalibrationCentre = neurocalibrationcentre;
+ HighEnergySmelting = highenergysmelting;
+ FoodSilo = foodsilo;
+ FusionPoweredProduction = fusionpoweredproduction;
+ Skyscraper = skyscraper;
+ BiotechLab = biotechlab;
+ Metropolis = metropolis;
+ PlanetaryShield = planetaryshield;
+ MeditationEnclave = meditationenclave;
+ CrystalFarm = crystalfarm;
+ RuneTechnologium = runetechnologium;
+ RuneForge = runeforge;
+ Oriktorium = oriktorium;
+ MagmaForge = magmaforge;
+ DisruptionChamber = disruptionchamber;
+ Megalith = megalith;
+ CrystalRefinery = crystalrefinery;
+ DeuteriumSynthesiser = deuteriumsynthesiser;
+ MineralResearchCentre = mineralresearchcentre;
+ AdvancedRecyclingPlant = advancedrecyclingplant;
+ AssemblyLine = assemblyline;
+ FusionCellFactory = fusioncellfactory;
+ RoboticsResearchCentre = roboticsresearchcentre;
+ UpdateNetwork = updatenetwork;
+ QuantumComputerCentre = quantumcomputercentre;
+ AutomatisedAssemblyCentre = automatisedassemblycentre;
+ HighPerformanceTransformer = highperformancetransformer;
+ MicrochipAssemblyLine = microchipassemblyline;
+ ProductionAssemblyHall = productionassemblyhall;
+ HighPerformanceSynthesiser = highperformancesynthesiser;
+ ChipMassProduction = chipmassproduction;
+ NanoRepairBots = nanorepairbots;
+ Sanctuary = sanctuary;
+ AntimatterCondenser = antimattercondenser;
+ VortexChamber = vortexchamber;
+ HallsOfRealisation = hallsofrealisation;
+ ForumOfTranscendence = forumoftranscendence;
+ AntimatterConvector = antimatterconvector;
+ CloningLaboratory = cloninglaboratory;
+ ChrysalisAccelerator = chrysalisaccelerator;
+ BioModifier = biomodifier;
+ PsionicModulator = psionicmodulator;
+ ShipManufacturingHall = shipmanufacturinghall;
+ SupraRefractor = suprarefractor;
+ }
+
public int GetLevel(LFBuildables building) {
int output = 0;
foreach (PropertyInfo prop in GetType().GetProperties()) {
diff --git a/TBot.Ogame.Infrastructure/Models/LFTechs.cs b/TBot.Ogame.Infrastructure/Models/LFTechs.cs
index 1adbf90e..c2cf47bf 100644
--- a/TBot.Ogame.Infrastructure/Models/LFTechs.cs
+++ b/TBot.Ogame.Infrastructure/Models/LFTechs.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Text;
@@ -9,96 +10,174 @@
namespace TBot.Ogame.Infrastructure.Models {
public class LFTechs {
//Humans
- public int IntergalacticEnvoys { get; set; }
- public int HighPerformanceExtractors { get; set; }
- public int FusionDrives { get; set; }
- public int StealthFieldGenerator { get; set; }
- public int OrbitalDen { get; set; }
- public int ResearchAI { get; set; }
- public int HighPerformanceTerraformer { get; set; }
- public int EnhancedProductionTechnologies { get; set; }
- public int LightFighterMkII { get; set; }
- public int CruiserMkII { get; set; }
- public int ImprovedLabTechnology { get; set; }
- public int PlasmaTerraformer { get; set; }
- public int LowTemperatureDrives { get; set; }
- public int BomberMkII { get; set; }
- public int DestroyerMkII { get; set; }
- public int BattlecruiserMkII { get; set; }
- public int RobotAssistants { get; set; }
- public int Supercomputer { get; set; }
+ public int? IntergalacticEnvoys { get; set; }
+ public int? HighPerformanceExtractors { get; set; }
+ public int? FusionDrives { get; set; }
+ public int? StealthFieldGenerator { get; set; }
+ public int? OrbitalDen { get; set; }
+ public int? ResearchAI { get; set; }
+ public int? HighPerformanceTerraformer { get; set; }
+ public int? EnhancedProductionTechnologies { get; set; }
+ public int? LightFighterMkII { get; set; }
+ public int? CruiserMkII { get; set; }
+ public int? ImprovedLabTechnology { get; set; }
+ public int? PlasmaTerraformer { get; set; }
+ public int? LowTemperatureDrives { get; set; }
+ public int? BomberMkII { get; set; }
+ public int? DestroyerMkII { get; set; }
+ public int? BattlecruiserMkII { get; set; }
+ public int? RobotAssistants { get; set; }
+ public int? Supercomputer { get; set; }
//Rocktal
- public int VolcanicBatteries { get; set; }
- public int AcousticScanning { get; set; }
- public int HighEnergyPumpSystems { get; set; }
- public int CargoHoldExpansionCivilianShips { get; set; }
- public int MagmaPoweredProduction { get; set; }
- public int GeothermalPowerPlants { get; set; }
- public int DepthSounding { get; set; }
- public int IonCrystalEnhancementHeavyFighter { get; set; }
- public int ImprovedStellarator { get; set; }
- public int HardenedDiamondDrillHeads { get; set; }
- public int SeismicMiningTechnology { get; set; }
- public int MagmaPoweredPumpSystems { get; set; }
- public int IonCrystalModules { get; set; }
- public int OptimisedSiloConstructionMethod { get; set; }
- public int DiamondEnergyTransmitter { get; set; }
- public int ObsidianShieldReinforcement { get; set; }
- public int RuneShields { get; set; }
- public int RocktalCollectorEnhancement { get; set; }
+ public int? VolcanicBatteries { get; set; }
+ public int? AcousticScanning { get; set; }
+ public int? HighEnergyPumpSystems { get; set; }
+ public int? CargoHoldExpansionCivilianShips { get; set; }
+ public int? MagmaPoweredProduction { get; set; }
+ public int? GeothermalPowerPlants { get; set; }
+ public int? DepthSounding { get; set; }
+ public int? IonCrystalEnhancementHeavyFighter { get; set; }
+ public int? ImprovedStellarator { get; set; }
+ public int? HardenedDiamondDrillHeads { get; set; }
+ public int? SeismicMiningTechnology { get; set; }
+ public int? MagmaPoweredPumpSystems { get; set; }
+ public int? IonCrystalModules { get; set; }
+ public int? OptimisedSiloConstructionMethod { get; set; }
+ public int? DiamondEnergyTransmitter { get; set; }
+ public int? ObsidianShieldReinforcement { get; set; }
+ public int? RuneShields { get; set; }
+ public int? RocktalCollectorEnhancement { get; set; }
//Mechas
- public int CatalyserTechnology { get; set; }
- public int PlasmaDrive { get; set; }
- public int EfficiencyModule { get; set; }
- public int DepotAI { get; set; }
- public int GeneralOverhaulLightFighter { get; set; }
- public int AutomatedTransportLines { get; set; }
- public int ImprovedDroneAI { get; set; }
- public int ExperimentalRecyclingTechnology { get; set; }
- public int GeneralOverhaulCruiser { get; set; }
- public int SlingshotAutopilot { get; set; }
- public int HighTemperatureSuperconductors { get; set; }
- public int GeneralOverhaulBattleship { get; set; }
- public int ArtificialSwarmIntelligence { get; set; }
- public int GeneralOverhaulBattlecruiser { get; set; }
- public int GeneralOverhaulBomber { get; set; }
- public int GeneralOverhaulDestroyer { get; set; }
- public int ExperimentalWeaponsTechnology { get; set; }
- public int MechanGeneralEnhancement { get; set; }
+ public int? CatalyserTechnology { get; set; }
+ public int? PlasmaDrive { get; set; }
+ public int? EfficiencyModule { get; set; }
+ public int? DepotAI { get; set; }
+ public int? GeneralOverhaulLightFighter { get; set; }
+ public int? AutomatedTransportLines { get; set; }
+ public int? ImprovedDroneAI { get; set; }
+ public int? ExperimentalRecyclingTechnology { get; set; }
+ public int? GeneralOverhaulCruiser { get; set; }
+ public int? SlingshotAutopilot { get; set; }
+ public int? HighTemperatureSuperconductors { get; set; }
+ public int? GeneralOverhaulBattleship { get; set; }
+ public int? ArtificialSwarmIntelligence { get; set; }
+ public int? GeneralOverhaulBattlecruiser { get; set; }
+ public int? GeneralOverhaulBomber { get; set; }
+ public int? GeneralOverhaulDestroyer { get; set; }
+ public int? ExperimentalWeaponsTechnology { get; set; }
+ public int? MechanGeneralEnhancement { get; set; }
//Kaelesh
- public int HeatRecovery { get; set; }
- public int SulphideProcess { get; set; }
- public int PsionicNetwork { get; set; }
- public int TelekineticTractorBeam { get; set; }
- public int EnhancedSensorTechnology { get; set; }
- public int NeuromodalCompressor { get; set; }
- public int NeuroInterface { get; set; }
- public int InterplanetaryAnalysisNetwork { get; set; }
- public int OverclockingHeavyFighter { get; set; }
- public int TelekineticDrive { get; set; }
- public int SixthSense { get; set; }
- public int Psychoharmoniser { get; set; }
- public int EfficientSwarmIntelligence { get; set; }
- public int OverclockingLargeCargo { get; set; }
- public int GravitationSensors { get; set; }
- public int OverclockingBattleship { get; set; }
- public int PsionicShieldMatrix { get; set; }
- public int KaeleshDiscovererEnhancement { get; set; }
+ public int? HeatRecovery { get; set; }
+ public int? SulphideProcess { get; set; }
+ public int? PsionicNetwork { get; set; }
+ public int? TelekineticTractorBeam { get; set; }
+ public int? EnhancedSensorTechnology { get; set; }
+ public int? NeuromodalCompressor { get; set; }
+ public int? NeuroInterface { get; set; }
+ public int? InterplanetaryAnalysisNetwork { get; set; }
+ public int? OverclockingHeavyFighter { get; set; }
+ public int? TelekineticDrive { get; set; }
+ public int? SixthSense { get; set; }
+ public int? Psychoharmoniser { get; set; }
+ public int? EfficientSwarmIntelligence { get; set; }
+ public int? OverclockingLargeCargo { get; set; }
+ public int? GravitationSensors { get; set; }
+ public int? OverclockingBattleship { get; set; }
+ public int? PsionicShieldMatrix { get; set; }
+ public int? KaeleshDiscovererEnhancement { get; set; }
+
+ public LFTechs(int? intergalacticenvoys = null, int? highperformanceextractors = null, int? fusiondrives = null, int? stealthfieldgenerator = null, int? orbitalden = null, int? researchai = null, int? highperformanceterraformer = null, int? enhancedproductiontechnologies = null, int? lightfightermkii = null, int? cruisermkii = null, int? improvedlabtechnology = null, int? plasmaterraformer = null, int? lowtemperaturedrives = null, int? bombermkii = null, int? destroyermkii = null, int? battlecruisermkii = null, int? robotassistants = null, int? supercomputer = null, int? volcanicbatteries = null, int? acousticscanning = null, int? highenergypumpsystems = null, int? cargoholdexpansioncivilianships = null, int? magmapoweredproduction = null, int? geothermalpowerplants = null, int? depthsounding = null, int? ioncrystalenhancementheavyfighter = null, int? improvedstellarator = null, int? hardeneddiamonddrillheads = null, int? seismicminingtechnology = null, int? magmapoweredpumpsystems = null, int? ioncrystalmodules = null, int? optimisedsiloconstructionmethod = null, int? diamondenergytransmitter = null, int? obsidianshieldreinforcement = null, int? runeshields = null, int? rocktalcollectorenhancement = null, int? catalysertechnology = null, int? plasmadrive = null, int? efficiencymodule = null, int? depotai = null, int? generaloverhaullightfighter = null, int? automatedtransportlines = null, int? improveddroneai = null, int? experimentalrecyclingtechnology = null, int? generaloverhaulcruiser = null, int? slingshotautopilot = null, int? hightemperaturesuperconductors = null, int? generaloverhaulbattleship = null, int? artificialswarmintelligence = null, int? generaloverhaulbattlecruiser = null, int? generaloverhaulbomber = null, int? generaloverhauldestroyer = null, int? experimentalweaponstechnology = null, int? mechangeneralenhancement = null, int? heatrecovery = null, int? sulphideprocess = null, int? psionicnetwork = null, int? telekinetictractorbeam = null, int? enhancedsensortechnology = null, int? neuromodalcompressor = null, int? neurointerface = null, int? interplanetaryanalysisnetwork = null, int? overclockingheavyfighter = null, int? telekineticdrive = null, int? sixthsense = null, int? psychoharmoniser = null, int? efficientswarmintelligence = null, int? overclockinglargecargo = null, int? gravitationsensors = null, int? overclockingbattleship = null, int? psionicshieldmatrix = null, int? kaeleshdiscovererenhancement = null) {
+ IntergalacticEnvoys = intergalacticenvoys ?? 0;
+ HighPerformanceExtractors = highperformanceextractors ?? 0;
+ FusionDrives = fusiondrives ?? 0;
+ StealthFieldGenerator = stealthfieldgenerator ?? 0;
+ OrbitalDen = orbitalden ?? 0;
+ ResearchAI = researchai ?? 0;
+ HighPerformanceTerraformer = highperformanceterraformer ?? 0;
+ EnhancedProductionTechnologies = enhancedproductiontechnologies ?? 0;
+ LightFighterMkII = lightfightermkii ?? 0;
+ CruiserMkII = cruisermkii ?? 0;
+ ImprovedLabTechnology = improvedlabtechnology ?? 0;
+ PlasmaTerraformer = plasmaterraformer ?? 0;
+ LowTemperatureDrives = lowtemperaturedrives ?? 0;
+ BomberMkII = bombermkii ?? 0;
+ DestroyerMkII = destroyermkii ?? 0;
+ BattlecruiserMkII = battlecruisermkii ?? 0;
+ RobotAssistants = robotassistants ?? 0;
+ Supercomputer = supercomputer ?? 0;
+ VolcanicBatteries = volcanicbatteries ?? 0;
+ AcousticScanning = acousticscanning ?? 0;
+ HighEnergyPumpSystems = highenergypumpsystems ?? 0;
+ CargoHoldExpansionCivilianShips = cargoholdexpansioncivilianships ?? 0;
+ MagmaPoweredProduction = magmapoweredproduction ?? 0;
+ GeothermalPowerPlants = geothermalpowerplants ?? 0;
+ DepthSounding = depthsounding ?? 0;
+ IonCrystalEnhancementHeavyFighter = ioncrystalenhancementheavyfighter ?? 0;
+ ImprovedStellarator = improvedstellarator ?? 0;
+ HardenedDiamondDrillHeads = hardeneddiamonddrillheads ?? 0;
+ SeismicMiningTechnology = seismicminingtechnology ?? 0;
+ MagmaPoweredPumpSystems = magmapoweredpumpsystems ?? 0;
+ IonCrystalModules = ioncrystalmodules ?? 0;
+ OptimisedSiloConstructionMethod = optimisedsiloconstructionmethod ?? 0;
+ DiamondEnergyTransmitter = diamondenergytransmitter ?? 0;
+ ObsidianShieldReinforcement = obsidianshieldreinforcement ?? 0;
+ RuneShields = runeshields ?? 0;
+ RocktalCollectorEnhancement = rocktalcollectorenhancement ?? 0;
+ CatalyserTechnology = catalysertechnology ?? 0;
+ PlasmaDrive = plasmadrive ?? 0;
+ EfficiencyModule = efficiencymodule ?? 0;
+ DepotAI = depotai ?? 0;
+ GeneralOverhaulLightFighter = generaloverhaullightfighter ?? 0;
+ AutomatedTransportLines = automatedtransportlines ?? 0;
+ ImprovedDroneAI = improveddroneai ?? 0;
+ ExperimentalRecyclingTechnology = experimentalrecyclingtechnology ?? 0;
+ GeneralOverhaulCruiser = generaloverhaulcruiser ?? 0;
+ SlingshotAutopilot = slingshotautopilot ?? 0;
+ HighTemperatureSuperconductors = hightemperaturesuperconductors ?? 0;
+ GeneralOverhaulBattleship = generaloverhaulbattleship ?? 0;
+ ArtificialSwarmIntelligence = artificialswarmintelligence ?? 0;
+ GeneralOverhaulBattlecruiser = generaloverhaulbattlecruiser ?? 0;
+ GeneralOverhaulBomber = generaloverhaulbomber ?? 0;
+ GeneralOverhaulDestroyer = generaloverhauldestroyer ?? 0;
+ ExperimentalWeaponsTechnology = experimentalweaponstechnology ?? 0;
+ MechanGeneralEnhancement = mechangeneralenhancement ?? 0;
+ HeatRecovery = heatrecovery ?? 0;
+ SulphideProcess = sulphideprocess ?? 0;
+ PsionicNetwork = psionicnetwork ?? 0;
+ TelekineticTractorBeam = telekinetictractorbeam ?? 0;
+ EnhancedSensorTechnology = enhancedsensortechnology ?? 0;
+ NeuromodalCompressor = neuromodalcompressor ?? 0;
+ NeuroInterface = neurointerface ?? 0;
+ InterplanetaryAnalysisNetwork = interplanetaryanalysisnetwork ?? 0;
+ OverclockingHeavyFighter = overclockingheavyfighter ?? 0;
+ TelekineticDrive = telekineticdrive ?? 0;
+ SixthSense = sixthsense ?? 0;
+ Psychoharmoniser = psychoharmoniser ?? 0;
+ EfficientSwarmIntelligence = efficientswarmintelligence ?? 0;
+ OverclockingLargeCargo = overclockinglargecargo ?? 0;
+ GravitationSensors = gravitationsensors ?? 0;
+ OverclockingBattleship = overclockingbattleship ?? 0;
+ PsionicShieldMatrix = psionicshieldmatrix ?? 0;
+ KaeleshDiscovererEnhancement = kaeleshdiscovererenhancement ?? 0;
+ }
public int GetLevel(LFTechno building) {
int output = 0;
foreach (PropertyInfo prop in GetType().GetProperties()) {
if (prop.Name == building.ToString()) {
- output = (int) prop.GetValue(this);
+ object value = prop.GetValue(this, null);
+ if (value is not null) {
+ output = (int) prop.GetValue(this, null);
+ }
}
}
return output;
}
- public LFTechs SetLevel(LFTechno buildable, int level) {
+ public LFTechs SetLevel(LFTechno buildable, int? level) {
foreach (PropertyInfo prop in this.GetType().GetProperties()) {
if (prop.Name == buildable.ToString()) {
prop.SetValue(this, level);
diff --git a/TBot.Ogame.Infrastructure/Models/RankSlotsPriority.cs b/TBot.Ogame.Infrastructure/Models/RankSlotsPriority.cs
new file mode 100644
index 00000000..4cadc455
--- /dev/null
+++ b/TBot.Ogame.Infrastructure/Models/RankSlotsPriority.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Text.RegularExpressions;
+using TBot.Ogame.Infrastructure.Enums;
+
+namespace TBot.Ogame.Infrastructure.Models {
+ public class RankSlotsPriority {
+ public RankSlotsPriority(Feature feature = Feature.Null, int rank = int.MinValue, bool active = false, int maxSlots = 0, int slotsUsed = 0) {
+ Rank = rank < 1 ? int.MinValue : (int) rank;
+ Active = active;
+ MaxSlots = maxSlots;
+ SlotsUsed = slotsUsed;
+ Feature = feature;
+ }
+ public int Rank { get; set; }
+ public bool Active { get; set; }
+ public int MaxSlots { get; set; }
+ public int SlotsUsed { get; set; }
+ public Feature Feature { get; set; }
+ public bool HasPriorityOn(RankSlotsPriority feature) {
+ return this.Rank < feature.Rank;
+ }
+ public override string ToString() {
+ return $"Rank {Rank} -- {Feature.ToString()} is {Active}, using {SlotsUsed}/{MaxSlots}";
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/TBot.Ogame.Infrastructure/Models/Transport.cs b/TBot.Ogame.Infrastructure/Models/Transport.cs
new file mode 100644
index 00000000..9ec16a2a
--- /dev/null
+++ b/TBot.Ogame.Infrastructure/Models/Transport.cs
@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Text.RegularExpressions;
+using TBot.Ogame.Infrastructure.Enums;
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+namespace TBot.Ogame.Infrastructure.Models {
+ public class TransportSettings {
+ public TransportSettings(bool active = false, Buildables cargoType = Buildables.SmallCargo, long deutToLeave = 0, bool roundResources = true, bool sendToTheMoonIfPossible = true, Celestial origin = null, long maxSlots = 0, bool checkMoonOrPlanetFirst = false, bool multipleTransports = false, MultipleOrigins multipleOrigins = null) {
+ Active = active;
+ CargoType = cargoType;
+ DeutToLeave = deutToLeave;
+ RoundResources = roundResources;
+ SendToTheMoonIfPossible = sendToTheMoonIfPossible;
+ Origin = origin ?? new();
+ MaxSlots = maxSlots;
+ CheckMoonOrPlanetFirst = checkMoonOrPlanetFirst;
+ DoMultipleTransportIsNotEnoughShipButSamePosition = multipleTransports;
+ MultipleOrigin = multipleOrigins ?? new();
+ }
+ public bool Active { get; set; }
+ public Buildables CargoType { get; set; }
+ public long DeutToLeave { get; set; }
+ public bool RoundResources { get; set; }
+ public bool SendToTheMoonIfPossible { get; set; }
+ public Celestial Origin { get; set; }
+ public long MaxSlots { get; set; }
+ public bool CheckMoonOrPlanetFirst { get; set; }
+ public bool DoMultipleTransportIsNotEnoughShipButSamePosition { get; set; }
+ public MultipleOrigins MultipleOrigin { get; set; }
+ }
+
+ public class MultipleOrigins {
+ public MultipleOrigins(bool active = false, bool onlyFromMoons = true, long minimumResourcesToSend = 0, bool priorityToP = true, List exclude = null) {
+ Active = active;
+ OnlyFromMoons = onlyFromMoons;
+ MinimumResourcesToSend = minimumResourcesToSend;
+ PriorityToProximityOverQuantity = priorityToP;
+ Exclude = exclude ?? new();
+ }
+ public bool Active { get; set; }
+ public bool OnlyFromMoons { get; set; }
+ public long MinimumResourcesToSend { get; set; }
+ public bool PriorityToProximityOverQuantity { get; set; }
+ public List Exclude { get; set; }
+ }
+
+}
\ No newline at end of file
diff --git a/TBot.Ogame.Infrastructure/OgameService.cs b/TBot.Ogame.Infrastructure/OgameService.cs
index 5edb9e08..744c44f0 100644
--- a/TBot.Ogame.Infrastructure/OgameService.cs
+++ b/TBot.Ogame.Infrastructure/OgameService.cs
@@ -441,7 +441,8 @@ public async Task GetLFBuildings(Celestial celestial) {
}
public async Task GetLFBonuses(Celestial celestial) {
- return await GetAsync($"/bot/planets/{celestial.ID}/lifeform-bonuses");
+ //return await GetAsync($"/bot/planets/{celestial.ID}/lifeform-bonuses");
+ return await GetAsync($"/bot/lfbonuses");
}
public async Task GetLFTechs(Celestial celestial) {
@@ -562,34 +563,38 @@ public async Task CancelResearch(Celestial celestial) {
}
public async Task SendFleet(Celestial origin, Ships ships, Coordinate destination, Missions mission, decimal speed, Resources payload) {
- List> parameters = new List>();
- parameters.Add(new KeyValuePair("galaxy", destination.Galaxy.ToString()));
- parameters.Add(new KeyValuePair("system", destination.System.ToString()));
- parameters.Add(new KeyValuePair("position", destination.Position.ToString()));
- parameters.Add(new KeyValuePair("type", ((int) destination.Type).ToString()));
-
- var request = new RestRequest {
- Resource = $"/bot/planets/{origin.ID}/send-fleet",
- Method = Method.Post,
- };
- foreach (PropertyInfo prop in ships.GetType().GetProperties()) {
- long qty = (long) prop.GetValue(ships, null);
- if (qty == 0)
- continue;
- if (Enum.TryParse(prop.Name, out Buildables buildable)) {
- parameters.Add(new KeyValuePair("ships", (int) buildable + "," + prop.GetValue(ships, null)));
+ try {
+ List> parameters = new List>();
+ parameters.Add(new KeyValuePair("galaxy", destination.Galaxy.ToString()));
+ parameters.Add(new KeyValuePair("system", destination.System.ToString()));
+ parameters.Add(new KeyValuePair("position", destination.Position.ToString()));
+ parameters.Add(new KeyValuePair("type", ((int)destination.Type).ToString()));
+
+ var request = new RestRequest {
+ Resource = $"/bot/planets/{origin.ID}/send-fleet",
+ Method = Method.Post,
+ };
+ foreach (PropertyInfo prop in ships.GetType().GetProperties()) {
+ long qty = (long) prop.GetValue(ships, null);
+ if (qty == 0)
+ continue;
+ if (Enum.TryParse(prop.Name, out Buildables buildable)) {
+ parameters.Add(new KeyValuePair("ships", (int) buildable + "," + prop.GetValue(ships, null)));
+ }
}
- }
- parameters.Add(new KeyValuePair("mission", ((int) mission).ToString()));
+ parameters.Add(new KeyValuePair("mission", ((int) mission).ToString()));
- parameters.Add(new KeyValuePair("speed", speed.ToString()));
- parameters.Add(new KeyValuePair("metal", payload.Metal.ToString()));
- parameters.Add(new KeyValuePair("crystal", payload.Crystal.ToString()));
- parameters.Add(new KeyValuePair("deuterium", payload.Deuterium.ToString()));
- parameters.Add(new KeyValuePair("food", payload.Food.ToString()));
+ parameters.Add(new KeyValuePair("speed", speed.ToString()));
+ parameters.Add(new KeyValuePair("metal", payload.Metal.ToString()));
+ parameters.Add(new KeyValuePair("crystal", payload.Crystal.ToString()));
+ parameters.Add(new KeyValuePair("deuterium", payload.Deuterium.ToString()));
+ parameters.Add(new KeyValuePair("food", payload.Food.ToString()));
- return await PostAsync($"/bot/planets/{origin.ID}/send-fleet", parameters.ToArray());
+ return await PostAsync($"/bot/planets/{origin.ID}/send-fleet", parameters.ToArray());
+ } catch (OgamedException e) when (e.Message.Contains("no ships to send")) {
+ return null;
+ }
}
public async Task CancelFleet(Fleet fleet) {
@@ -728,7 +733,19 @@ public async Task SendDiscovery(Celestial origin, Coordinate coords) {
return success;
}
- public async Task AbandonCelestial(Celestial celestial) {
+ public async Task> GetPositionsAvailableForDiscoveryFleet(Celestial celestial, Coordinate coordinate) {
+ List> parameters = new List>();
+ parameters.Add(new KeyValuePair("galaxy", coordinate.Galaxy.ToString()));
+ parameters.Add(new KeyValuePair("system", coordinate.System.ToString()));
+ parameters.Add(new KeyValuePair("position", coordinate.Position.ToString()));
+ return await PostAsync>($"/bot/planets/{celestial.ID}/get-system-available-discovery", parameters.ToArray());
+ }
+
+ public async Task GetAvailableDiscoveries(Celestial celestial) {
+ return await GetAsync($"/bot/planets/{celestial.ID}/get-available-discoveries");
+ }
+
+ public async Task AbandonCelestial(Celestial celestial) {
bool success = false;
try {
Abandon result = await GetAsync($"/bot/celestials/{celestial.ID}/abandon");
diff --git a/TBot.Ogame.Infrastructure/TBot.Ogame.Infrastructure.csproj b/TBot.Ogame.Infrastructure/TBot.Ogame.Infrastructure.csproj
index 469351a1..239fa33a 100644
--- a/TBot.Ogame.Infrastructure/TBot.Ogame.Infrastructure.csproj
+++ b/TBot.Ogame.Infrastructure/TBot.Ogame.Infrastructure.csproj
@@ -1,17 +1,17 @@
- net6.0
+ net9.0
enable
enable
-
+
-
+
-
+
diff --git a/TBot.WebUI/TBot.WebUI.csproj b/TBot.WebUI/TBot.WebUI.csproj
index d019a0f1..0272c640 100644
--- a/TBot.WebUI/TBot.WebUI.csproj
+++ b/TBot.WebUI/TBot.WebUI.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net9.0
enable
enable
Library
@@ -35,9 +35,9 @@
-
-
-
+
+
+
diff --git a/TBot.WebUI/Views/Shared/_Layout.cshtml b/TBot.WebUI/Views/Shared/_Layout.cshtml
index c4c2a887..f12c42fd 100644
--- a/TBot.WebUI/Views/Shared/_Layout.cshtml
+++ b/TBot.WebUI/Views/Shared/_Layout.cshtml
@@ -20,7 +20,6 @@
T-Bot
-
diff --git a/TBot/Includes/CalculationService.cs b/TBot/Includes/CalculationService.cs
index b6a4690e..f45390c9 100644
--- a/TBot/Includes/CalculationService.cs
+++ b/TBot/Includes/CalculationService.cs
@@ -18,6 +18,7 @@
using Microsoft.CodeAnalysis.Operations;
using System.Reflection.Emit;
using System.Data;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Tbot.Includes {
@@ -154,7 +155,7 @@ public int CalcShipCapacity(Buildables buildable, int hyperspaceTech, ServerData
default:
return 0;
}
- double totalBonus = Math.Round(Math.Round((double) bonus / 100, 2, MidpointRounding.ToZero) + Math.Round((double) buildableCargoBonus / 100, 2, MidpointRounding.ToZero), 2, MidpointRounding.ToZero) - 0.01;
+ double totalBonus = Math.Round(Math.Round((double) bonus / 100, 2, MidpointRounding.ToZero) + Math.Round((double) buildableCargoBonus, 2, MidpointRounding.ToZero), 2, MidpointRounding.ToZero) - 0.01;
int output = (int) Math.Floor((double) baseCargo + (double) (baseCargo * totalBonus));
return output;
}
@@ -310,7 +311,7 @@ public int CalcShipSpeed(Buildables buildable, int combustionDrive, int impulseD
default:
return 0;
}
- var output = ((float) baseSpeed * (lfBonus / 100)) + ((float) baseSpeed * ((float) bonus + 10) / 10);
+ var output = ((float) baseSpeed * (lfBonus)) + ((float) baseSpeed * ((float) bonus + 10) / 10);
return (int) Math.Round(output, MidpointRounding.ToZero);
}
@@ -366,7 +367,7 @@ public int CalcShipConsumption(Buildables buildable, int impulseDrive, int hyper
int baseConsumption;
switch (buildable) {
case Buildables.SmallCargo:
- baseConsumption = 20;
+ baseConsumption = 10;
if (impulseDrive >= 5)
baseConsumption *= 2;
break;
@@ -389,7 +390,7 @@ public int CalcShipConsumption(Buildables buildable, int impulseDrive, int hyper
baseConsumption = 1000;
break;
case Buildables.Recycler:
- baseConsumption = 2000;
+ baseConsumption = 300;
if (hyperspaceDrive >= 15)
baseConsumption *= 3;
else if (impulseDrive >= 17)
@@ -421,7 +422,7 @@ public int CalcShipConsumption(Buildables buildable, int impulseDrive, int hyper
default:
return 0;
}
- double fuelConsumption = (double) deuteriumSaveFactor * (double) baseConsumption * (1 - lfBonus / 100);
+ double fuelConsumption = (double) deuteriumSaveFactor * (double) baseConsumption * (1 - lfBonus);
if (playerClass == CharacterClass.General)
fuelConsumption /= 2;
fuelConsumption = Math.Round(fuelConsumption);
@@ -594,7 +595,7 @@ public Ships CalcIdealExpeditionShips(Buildables buildable, int hyperspaceTech,
float topOnePoints = serverData.TopScore;
float buildableCargoBonus = 0;
if (shipBonus != null && shipBonus.Count > 0 && shipBonus.ContainsKey((int) buildable)) {
- buildableCargoBonus = shipBonus.GetValueOrDefault((int) buildable).Cargo;
+ buildableCargoBonus = shipBonus.GetValueOrDefault((int) buildable).CargoCapacity;
}
int freightCap;
if (topOnePoints < 10000)
@@ -622,7 +623,7 @@ public Ships CalcIdealExpeditionShips(Buildables buildable, int hyperspaceTech,
freightCap *= 2;
if(expeditionResourcesBonus > 0)
- freightCap += (int) Math.Round((float) freightCap * expeditionResourcesBonus / 100, MidpointRounding.ToPositiveInfinity);
+ freightCap += (int) Math.Round((float) freightCap * expeditionResourcesBonus, MidpointRounding.ToPositiveInfinity);
int oneCargoCapacity = CalcShipCapacity(buildable, hyperspaceTech, serverData, buildableCargoBonus, playerClass, probeCargo);
int cargoNumber = (int) Math.Round((float) freightCap / (float) oneCargoCapacity, MidpointRounding.ToPositiveInfinity);
@@ -662,7 +663,8 @@ public Ships CalcExpeditionShips(Ships fleet, Buildables primaryShip, int expedi
long availableVal = (long) prop.GetValue(fleet);
long idealVal = (long) prop.GetValue(ideal);
if (availableVal < idealVal * expeditionsNumber) {
- long realVal = (long) Math.Round(((float) availableVal / (float) expeditionsNumber), MidpointRounding.AwayFromZero);
+ //long realVal = (long) Math.Round(((float) availableVal / (float) expeditionsNumber), MidpointRounding.AwayFromZero);
+ long realVal = (long) Math.Floor(((float) availableVal / (float) expeditionsNumber));
prop.SetValue(ideal, realVal);
}
}
@@ -671,7 +673,7 @@ public Ships CalcExpeditionShips(Ships fleet, Buildables primaryShip, int expedi
}
public Ships CalcExpeditionShips(Ships fleet, Buildables primaryShip, int expeditionsNumber, ServerData serverdata, Researches researches, LFBonuses LFBonuses, CharacterClass playerClass = CharacterClass.NoClass, int probeCargo = 0) {
- return CalcExpeditionShips(fleet, primaryShip, expeditionsNumber, researches.HyperspaceTechnology, LFBonuses.Expeditions.Resources, LFBonuses.Ships, serverdata, playerClass, probeCargo);
+ return CalcExpeditionShips(fleet, primaryShip, expeditionsNumber, researches.HyperspaceTechnology, LFBonuses.LfResourceBonuses.ResourcesExpedition, LFBonuses.LfShipBonusesInt, serverdata, playerClass, probeCargo);
}
public bool MayAddShipToExpedition(Ships fleet, Buildables buildable, int expeditionsNumber) {
@@ -1226,12 +1228,12 @@ public Resources CalcPrice(Buildables buildable, int level, LFBonuses lfBonuses
}
if (lfBonuses != null) {
- float reduction = 0;
- if (lfBonuses.Buildings.Keys.Any(b => b == (int) buildable)) {
- reduction = lfBonuses.Buildings[(int) buildable].Cost;
+ float reduction = 0;
+ if (lfBonuses.CostTimeBonusesInt.Keys.Any(b => b == (int) buildable)) {
+ reduction = lfBonuses.CostTimeBonusesInt[(int) buildable].Cost;
}
- else if (lfBonuses.Researches.Keys.Any(b => b == (int) buildable)) {
- reduction = lfBonuses.Researches[(int) buildable].Cost;
+ else if (lfBonuses.CostTimeBonusesInt.Keys.Any(b => b == (int) buildable)) {
+ reduction = lfBonuses.CostTimeBonusesInt[(int) buildable].Cost;
}
output.Metal = (long) (output.Metal - (output.Metal * reduction));
output.Crystal = (long) (output.Crystal - (output.Crystal * reduction));
@@ -1612,8 +1614,8 @@ public Resources CalcPrice(LFBuildables buildable, int level, double costReducti
metalBaseCost = 6;
crystalbaseCost = 3;
energyBaseCost = 9;
- metalFactor = 1.21;
- crystalFactor = 1.21;
+ metalFactor = 1.20;
+ crystalFactor = 1.20;
energyFactor = 1.02;
break;
case LFBuildables.VortexChamber:
@@ -3027,6 +3029,29 @@ public long CalcMaxBuildableNumber(Buildables buildable, Resources resources) {
return output;
}
+ public Defences CalcmaxDefencesBuildable(Defences defences, Resources resources) {
+ Defences output = new();
+ Dictionary defenceTypes = defences.GetDefenceTypesWithAmount();
+ Resources resourcesLeft = resources;
+
+ bool canBuild = true;
+ while (canBuild) {
+ canBuild = false;
+ foreach (var (defenceType, amountNeeded) in defenceTypes) {
+ if (amountNeeded <= 0)
+ continue;
+ Resources unitCost = CalcPrice(defenceType, 1);
+ if (resourcesLeft.IsEnoughFor(unitCost)) {
+ output.Add(defenceType.ToString(), 1);
+ resourcesLeft = resourcesLeft.Difference(unitCost);
+ defenceTypes[defenceType]--;
+ canBuild = true;
+ }
+ }
+ }
+ return output;
+ }
+
public long GetRequiredEnergyDelta(Buildables buildable, int level) {
if (buildable == Buildables.MetalMine || buildable == Buildables.CrystalMine || buildable == Buildables.DeuteriumSynthesizer) {
if (level > 1) {
@@ -3100,12 +3125,15 @@ public int GetNextLevel(Celestial planet, LFBuildables buildable) {
public int GetNextLevel(Celestial planet, LFTechno buildable) {
int output = 0;
- if (planet is Planet) {
- foreach (PropertyInfo prop in planet.LFTechs.GetType().GetProperties()) {
- if (prop.Name == buildable.ToString()) {
- output = (int) prop.GetValue(planet.LFTechs) + 1;
- }
+ if (buildable is LFTechno.None) {
+ return output;
+ }
+ if (planet is Celestial) {
+ int? level = planet.LFTechs.GetLevel(buildable);
+ if (level is null) {
+ return output;
}
+ output = planet.LFTechs.GetLevel(buildable) + 1;
}
return output;
}
@@ -4041,15 +4069,16 @@ public LFTechno GetNextLFTechToBuild(Celestial celestial, LFTechs MaxReasearchLe
//TODO
//As planets can have any lifeform techs, its complicated to find which techs are existing on a planet if the techs are not at least level 1
//Therefore, for the moment, up only techs that are minimum level 1, its a way to also allows player to chose which research to up
- foreach (PropertyInfo prop in celestial.LFTechs.GetType().GetProperties()) {
- foreach (LFTechno nextLFTech in Enum.GetValues()) {
- if ((int) prop.GetValue(celestial.LFTechs) > 0 && GetNextLevel(celestial, nextLFTech) <= MaxReasearchLevel.GetLevel(nextLFTech) && prop.Name == nextLFTech.ToString()) {
- //Console.WriteLine($"-----------------------------> {nextLFTech}: {GetNextLevel(celestial, nextLFTech)} / {MaxReasearchLevel.GetLevel(nextLFTech)}");
- return nextLFTech;
- }
-
+ foreach (LFTechno nextLFTech in Enum.GetValues()) {
+ int? level = celestial.LFTechs.GetLevel(nextLFTech);
+ if (level is null) {
+ continue;
}
+ if (level > 0 && GetNextLevel(celestial, nextLFTech) <= MaxReasearchLevel.GetLevel(nextLFTech)) {
+ //Console.WriteLine($"-----------------------------> {nextLFTech}: {GetNextLevel(celestial, nextLFTech)} / {MaxReasearchLevel.GetLevel(nextLFTech)}");
+ return nextLFTech;
+ }
}
return LFTechno.None;
}
@@ -4057,20 +4086,24 @@ public LFTechno GetNextLFTechToBuild(Celestial celestial, LFTechs MaxReasearchLe
public LFTechno GetLessExpensiveLFTechToBuild(Celestial celestial, Resources currentcost, LFTechs MaxReasearchLevel, double costReduction = 0) {
LFTechno nextLFtech = LFTechno.None;
Resource nextLFtechcost = new();
- foreach (PropertyInfo prop in celestial.LFTechs.GetType().GetProperties()) {
- foreach (LFTechno next in Enum.GetValues()) {
- if ((int) prop.GetValue(celestial.LFTechs) > 0 && GetNextLevel(celestial, next) <= MaxReasearchLevel.GetLevel(next) && prop.Name == next.ToString()) {
- //Console.WriteLine($"-----------------------------> {next}: {GetNextLevel(celestial, next)} / {MaxReasearchLevel.GetLevel(next)}");
- var nextLFtechlvl = GetNextLevel(celestial, next);
- Resources newcost = CalcPrice(next, nextLFtechlvl, costReduction);
- if (newcost.ConvertedDeuterium < currentcost.ConvertedDeuterium) {
- currentcost = newcost;
- nextLFtech = next;
- }
- }
-
+ foreach (LFTechno next in Enum.GetValues()) {
+ if (next is LFTechno.None) {
+ continue;
+ }
+ int? level = celestial.LFTechs.GetLevel(next);
+ if (level is null) {
+ continue;
}
+ if (level > 0 && GetNextLevel(celestial, next) <= MaxReasearchLevel.GetLevel(next)) {
+ //Console.WriteLine($"-----------------------------> {next}: {GetNextLevel(celestial, next)} / {MaxReasearchLevel.GetLevel(next)}");
+ var nextLFtechlvl = GetNextLevel(celestial, next);
+ Resources newcost = CalcPrice(next, nextLFtechlvl, costReduction);
+ if (newcost.ConvertedDeuterium < currentcost.ConvertedDeuterium) {
+ currentcost = newcost;
+ nextLFtech = next;
+ }
+ }
}
return nextLFtech;
}
@@ -4408,11 +4441,15 @@ public bool ShouldResearchEnergyTech(List planets, Researches researches
return ShouldResearchEnergyTech(planets, researches.EnergyTechnology, maxEnergyTech, playerClass, hasEngineer, hasStaff);
}
- public Buildables GetNextResearchToBuild(Planet celestial, Researches researches, bool prioritizeRobotsAndNanitesOnNewPlanets = false, Slots slots = null, int maxEnergyTechnology = 20, int maxLaserTechnology = 12, int maxIonTechnology = 5, int maxHyperspaceTechnology = 20, int maxPlasmaTechnology = 20, int maxCombustionDrive = 19, int maxImpulseDrive = 17, int maxHyperspaceDrive = 15, int maxEspionageTechnology = 8, int maxComputerTechnology = 20, int maxAstrophysics = 23, int maxIntergalacticResearchNetwork = 12, int maxWeaponsTechnology = 25, int maxShieldingTechnology = 25, int maxArmourTechnology = 25, bool optimizeForStart = true, bool ensureExpoSlots = true, CharacterClass playerClass = CharacterClass.NoClass, bool hasGeologist = false, bool hasAdmiral = false) {
- if (ShouldBuildResearchLab(celestial, 12, researches))
+ public Buildables GetNextResearchToBuild(Planet celestial, Researches researches, bool prioritizeRobotsAndNanitesOnNewPlanets = false, Slots slots = null, int maxEnergyTechnology = 20, int maxLaserTechnology = 12, int maxIonTechnology = 5, int maxHyperspaceTechnology = 20, int maxPlasmaTechnology = 20, int maxCombustionDrive = 19, int maxImpulseDrive = 17, int maxHyperspaceDrive = 15, int maxEspionageTechnology = 8, int maxComputerTechnology = 20, int maxAstrophysics = 23, int maxIntergalacticResearchNetwork = 12, int maxWeaponsTechnology = 25, int maxShieldingTechnology = 25, int maxArmourTechnology = 25, bool optimizeForStart = true, bool ensureExpoSlots = true, bool ForceResearchWhateverTheLabLevel = false, CharacterClass playerClass = CharacterClass.NoClass, bool hasGeologist = false, bool hasAdmiral = false) {
+ if (ShouldBuildResearchLab(celestial, 12, researches) && !ForceResearchWhateverTheLabLevel) {
+ _logger.WriteLog(LogLevel.Information, LogSender.Tbot, $"Skipping AutoResearch: Can't build any research, Research Lab need to be upgraded first (actually cheaper than the next mine).");
return Buildables.Null;
+ }
if (optimizeForStart) {
+ if (researches.ComputerTechnology < 6 && celestial.Facilities.ResearchLab >= 1 && researches.Astrophysics > 1 && researches.ComputerTechnology < maxComputerTechnology)
+ return Buildables.ComputerTechnology;
if (researches.EnergyTechnology == 0 && celestial.Facilities.ResearchLab > 0 && researches.EnergyTechnology < maxEnergyTechnology)
return Buildables.EnergyTechnology;
if (researches.CombustionDrive < 2 && celestial.Facilities.ResearchLab > 0 && researches.EnergyTechnology >= 1 && researches.CombustionDrive < maxCombustionDrive)
@@ -4740,11 +4777,13 @@ public int CalcMaxPlanets(Researches researches) {
return researches == null ? 1 : CalcMaxPlanets(researches.Astrophysics);
}
- public bool CalcLimitAstro(int pos, Researches researches) {
- return ((pos >= 4 && pos <= 13 && (int)researches.Astrophysics >= 4) ||
- (pos >= 2 && pos <= 14 && (int)researches.Astrophysics >= 6) ||
- (pos >= 1 && pos <= 15 && (int)researches.Astrophysics >= 8));
- }
+ public bool IsAstrophysicsPositionValid(int pos, int astrophysics) => astrophysics switch {
+ 8 => pos >= 1 && pos <= 15,
+ 7 or 6 => pos >= 2 && pos <= 14,
+ 5 or 4 => pos >= 3 && pos <= 13,
+ 3 or 2 or 1 => pos >= 4 && pos <= 12,
+ _ => true
+ };
public int CalcMaxCrawlers(Planet planet, CharacterClass userClass, bool hasGeologist) {
if (userClass == CharacterClass.Collector && hasGeologist) {
@@ -4806,5 +4845,224 @@ public int CountPlanetsInRange(List planets, int galaxy, int minSyste
.Where(planet => (planet as Planet).Temperature.Max >= minTemperature && (planet as Planet).Temperature.Max <= maxTemperature)
.Count();
}
+
+ public bool IsThereMoonHere(List planets, Celestial celestial) {
+ Celestial moon = planets.SingleOrDefault(
+ c => c.HasCoords(new(
+ (int) celestial.Coordinate.Galaxy,
+ (int) celestial.Coordinate.System,
+ (int) celestial.Coordinate.Position,
+ Celestials.Moon
+ )
+ )) ?? new() { ID = 0 };
+ /*.Where(c => c.Coordinate.Galaxy == (int) celestial.Coordinate.Galaxy)
+ .Where(c => c.Coordinate.System == (int) celestial.Coordinate.System)
+ .Where(c => c.Coordinate.Position == (int) celestial.Coordinate.Position)
+ .Where(c => c.Coordinate.Type == Celestials.Moon)
+ .SingleOrDefault() ?? new() { ID = 0 };*/
+
+ return moon.ID == 0 ? false: true;
+ }
+
+ public int CalcSlotsPriority(Feature feature, List rankSlotsPriority, Slots slots, List fleets, int slotsToLeaveFree = 0) {
+ LogSender logsender;
+ switch (feature) {
+ case Feature.BrainAutoMine: logsender = LogSender.AutoMine; break;
+ case Feature.BrainAutoResearch: logsender = LogSender.AutoResearch; break;
+ case Feature.BrainLifeformAutoMine: logsender = LogSender.LifeformsAutoMine; break;
+ case Feature.BrainLifeformAutoResearch: logsender = LogSender.LifeformsAutoResearch; break;
+ case Feature.Expeditions: logsender = LogSender.Expeditions; break;
+ case Feature.AutoFarm: logsender = LogSender.AutoFarm; break;
+ case Feature.Colonize: logsender = LogSender.Colonize; break;
+ case Feature.AutoDiscovery: logsender = LogSender.AutoDiscovery; break;
+ case Feature.Harvest: logsender = LogSender.Harvest; break;
+ default: logsender = LogSender.FleetScheduler; break;
+ }
+ RankSlotsPriority actualFeature = rankSlotsPriority.Single(f => f.Feature == feature);
+ int slotsAvailable = 0;
+ int reservedSlots = 0;
+ int usedSlots = 0;
+ int otherSlots = fleets.Count(fleet =>
+ fleet.Mission != Missions.Transport &&
+ fleet.Mission != Missions.Expedition &&
+ fleet.Mission != Missions.Spy &&
+ fleet.Mission != Missions.Attack &&
+ fleet.Mission != Missions.Colonize &&
+ fleet.Mission != Missions.Discovery &&
+ fleet.Mission != Missions.Harvest);
+
+ if (actualFeature.MaxSlots - actualFeature.SlotsUsed <= 0) {
+ _logger.WriteLog(LogLevel.Information, logsender, $"All slots for feature {feature} are already used ({actualFeature.SlotsUsed}/{actualFeature.MaxSlots}).");
+ return 0;
+ }
+
+ if (actualFeature.Rank > 0) {
+ reservedSlots = rankSlotsPriority.Where(fp => fp.Active)
+ .Where(fp => fp.Rank > 0)
+ .Where(fp => fp.Rank < actualFeature.Rank)
+ .Sum(fp => fp.MaxSlots);
+ usedSlots = rankSlotsPriority.Where(fp => fp.Active)
+ .Where(fp => fp.Rank > actualFeature.Rank)
+ .Sum(fp => fp.SlotsUsed);
+ otherSlots += rankSlotsPriority.Where(fp => !fp.Active)
+ .Sum(fp => fp.SlotsUsed);
+ slotsAvailable = slots.Total - slotsToLeaveFree - reservedSlots - usedSlots - otherSlots < 0 ? 0 : slots.Total - slotsToLeaveFree - reservedSlots - usedSlots - otherSlots;
+ } else {
+ slotsAvailable = slots.Free - slotsToLeaveFree < 0 ? 0 : slots.Free - slotsToLeaveFree;
+ }
+ if (slotsAvailable <= 0) {
+ _logger.WriteLog(LogLevel.Information, logsender, $"No slots available for feature {feature}. Slots available: {slotsAvailable}.");
+ return 0;
+ } else {
+ if (actualFeature.Rank > 0) {
+ _logger.WriteLog(LogLevel.Information, logsender, $"Slots Total: {slots.Total}, Reserved: {reservedSlots + slotsToLeaveFree}, Other: {usedSlots + otherSlots}. Available: {slotsAvailable}.");
+ if (slotsAvailable < actualFeature.MaxSlots) {
+ _logger.WriteLog(LogLevel.Information, logsender, $"Less slots available ({slotsAvailable}) than maximum usable slots ({actualFeature.MaxSlots}). Stepping down to usable slots.");
+ actualFeature.MaxSlots = slotsAvailable;
+ }
+ slotsAvailable = Math.Min(slotsAvailable, actualFeature.MaxSlots - actualFeature.SlotsUsed);
+ _logger.WriteLog(LogLevel.Information, logsender, $"{feature} will use {slotsAvailable} slots ({actualFeature.SlotsUsed}/{actualFeature.MaxSlots} already used).");
+ } else {
+ _logger.WriteLog(LogLevel.Information, logsender, $"{feature} has no rank and can use all available slots: {slotsAvailable}.");
+ }
+ }
+
+ return slotsAvailable;
+ }
+
+ public List> CalcMultipleOrigin(Celestial celestialToBuild, List allCelestials, Resources missingResources, TransportSettings transportSettings, List fleets, UserData userData) {
+ if (missingResources.TotalResources < transportSettings.MultipleOrigin.MinimumResourcesToSend) {
+ _logger.WriteLog(LogLevel.Information, LogSender.Brain, $"Skipping transport: missing resources {missingResources.TransportableResources} is less than minimum {transportSettings.MultipleOrigin.MinimumResourcesToSend}");
+ return new();
+ }
+ List> result = new();
+ bool roundRes = transportSettings.RoundResources;
+ Resources resources = new();
+ List closestCelestials = transportSettings.MultipleOrigin.OnlyFromMoons ?
+ allCelestials
+ .Where(planet => !transportSettings.MultipleOrigin.Exclude.Has(planet))
+ .Where(planet => planet.Resources.TotalResources > 0)
+ .Where(planet => planet.Coordinate.Type == Celestials.Moon)
+ .OrderByDescending(planet => planet.Coordinate.Type == Celestials.Moon)
+ .ToList():
+ allCelestials
+ .Where(c => !transportSettings.MultipleOrigin.Exclude.Has(c))
+ .Where(c => c.Resources.TotalResources > 0)
+ .ToList();
+
+ Resources TotalResources = closestCelestials.Aggregate(new Resources(), (total, celestial) => total.Sum(celestial.Resources.Difference(new Resources(0, 0, transportSettings.DeutToLeave))) );
+ if (!TotalResources.IsEnoughFor(missingResources)) {
+ _logger.WriteLog(LogLevel.Information, LogSender.Brain, $"Not enough resources available on all celestials: Needed: {missingResources.TransportableResources} - Available: {TotalResources.TransportableResources}");
+ return new();
+ } else {
+ _logger.WriteLog(LogLevel.Information, LogSender.Brain, $"Enough resources available on all celestials: Needed: {missingResources.TransportableResources} - Available: {TotalResources.TransportableResources}");
+ }
+
+ closestCelestials = transportSettings.MultipleOrigin.PriorityToProximityOverQuantity ?
+ closestCelestials.OrderBy(c => CalcDistance(c.Coordinate, celestialToBuild.Coordinate, userData.serverData)).ToList() :
+ closestCelestials.OrderByDescending(c => c.Resources.TotalResources).ToList();
+
+ Celestial destination = celestialToBuild;
+ if ((bool) transportSettings.SendToTheMoonIfPossible && celestialToBuild.Coordinate.Type == Celestials.Planet && IsThereMoonHere(allCelestials, celestialToBuild) && (!celestialToBuild.Ships.IsEmpty() || celestialToBuild.Resources.TotalResources > 0)) {
+ destination = allCelestials.Unique()
+ .Single(c => c.HasCoords(new(
+ (int) celestialToBuild.Coordinate.Galaxy,
+ (int) celestialToBuild.Coordinate.System,
+ (int) celestialToBuild.Coordinate.Position,
+ Celestials.Moon
+ )
+ ));
+ destination.Resources = roundRes ? destination.Resources.Round() : destination.Resources;
+ if (destination.Resources.IsEnoughFor(missingResources)) {
+ if (destination.Ships.GetAmount(transportSettings.CargoType) >= CalcShipNumberForPayload(missingResources, transportSettings.CargoType, userData.researches.HyperspaceTechnology, userData.serverData, destination.LFBonuses.GetShipCargoBonus(transportSettings.CargoType), userData.userInfo.Class, userData.serverData.ProbeCargo)) {
+ result.Add(new Dictionary { { destination, missingResources } } );
+ return result;
+ } else if (transportSettings.DoMultipleTransportIsNotEnoughShipButSamePosition) {
+ result.Add(new Dictionary { { destination, CalcMaxTransportableResources(destination.Ships, missingResources, userData.researches.HyperspaceTechnology, userData.serverData, destination.LFBonuses, userData.userInfo.Class, 0, userData.serverData.ProbeCargo) } } );
+ return result;
+ } else {
+ _logger.WriteLog(LogLevel.Information, LogSender.Brain, $"Enough Resources on {destination.ToString()}, but not enough transporters and \"DoMultipleTransportIsNotEnoughShipButSamePosition\" is false.");
+ return new();
+ }
+ } else {
+ missingResources = missingResources.Difference(destination.Resources);
+ }
+ } else {
+ destination = celestialToBuild;
+ }
+ closestCelestials = closestCelestials.Where(c => !c.Coordinate.IsSame(destination.Coordinate))
+ .Where(c => !IsThereTransportTowardsCelestial(c, fleets))
+ .ToList();
+ if (closestCelestials.Count() == 0) {
+ _logger.WriteLog(LogLevel.Information, LogSender.Brain, $"Skipping transport: no celestial available");
+ return new();
+ }
+ if (IsThereTransportTowardsCelestial(destination, fleets)) {
+ _logger.WriteLog(LogLevel.Information, LogSender.Brain, $"Skipping transport: there is already a transport incoming in {destination.ToString()}");
+ return new();
+ }
+ TotalResources = closestCelestials.Aggregate(new Resources(), (total, celestial) => {
+ if (celestial.Resources.TotalResources < transportSettings.MultipleOrigin.MinimumResourcesToSend || celestial.Resources.TotalResources == 0)
+ return total;
+ if (celestial.Ships.GetAmount(transportSettings.CargoType) >= CalcShipNumberForPayload(celestial.Resources.Difference(new Resources(0, 0, transportSettings.DeutToLeave)), transportSettings.CargoType, userData.researches.HyperspaceTechnology, userData.serverData, celestial.LFBonuses.GetShipCargoBonus(transportSettings.CargoType), userData.userInfo.Class, userData.serverData.ProbeCargo)) {
+ return total.Sum(celestial.Resources.Difference(new Resources(0, 0, transportSettings.DeutToLeave)));
+ } else {
+ Ships ships = new();
+ ships.Add(transportSettings.CargoType, celestial.Ships.GetAmount(transportSettings.CargoType));
+ Resources res = CalcMaxTransportableResources(ships, missingResources.Difference(total), userData.researches.HyperspaceTechnology, userData.serverData, celestial.LFBonuses, userData.userInfo.Class, 0, userData.serverData.ProbeCargo);
+ return total.Sum(res);
+ }
+ } );
+ if (!TotalResources.IsEnoughFor(missingResources)) {
+ _logger.WriteLog(LogLevel.Information, LogSender.Brain, $"Not enough resources available on all celestials: Needed: {missingResources.TransportableResources} - Available: {TotalResources.TransportableResources}");
+ return new();
+ }
+
+ for (int i = 0; i < closestCelestials.Count(); i++) {
+ Celestial cel = closestCelestials[i];
+ Resources celResources = cel.Resources.Difference(new Resources(0, 0, transportSettings.DeutToLeave));
+ if (resources.Sum(celResources).IsEnoughFor(missingResources)) {
+ celResources = roundRes ? missingResources.Difference(resources).Round() : missingResources.Difference(resources);
+ } else {
+ celResources = roundRes ?
+ celResources.Difference(celResources.Difference(missingResources.Difference(resources))).Round():
+ celResources.Difference(celResources.Difference(missingResources.Difference(resources)));
+ }
+ if (celResources.TotalResources < transportSettings.MultipleOrigin.MinimumResourcesToSend || celResources.TotalResources == 0) {
+ _logger.WriteLog(LogLevel.Information, LogSender.Brain, $"Skipping transport from {cel.ToString()}: resources under minimum threshold ({celResources.TotalResources} < {transportSettings.MultipleOrigin.MinimumResourcesToSend})");
+ continue;
+ }
+ if (cel.Ships.GetAmount(transportSettings.CargoType) >= CalcShipNumberForPayload(celResources, transportSettings.CargoType, userData.researches.HyperspaceTechnology, userData.serverData, cel.LFBonuses.GetShipCargoBonus(transportSettings.CargoType), userData.userInfo.Class, userData.serverData.ProbeCargo)) {
+ resources = resources.Sum(celResources);
+ result.Add(new Dictionary { { cel, celResources } } );
+ } else {
+ if (i < closestCelestials.Count() - 1) {
+ Ships ships = new();
+ ships.Add(transportSettings.CargoType, cel.Ships.GetAmount(transportSettings.CargoType));
+ Resources res = CalcMaxTransportableResources(ships, celResources, userData.researches.HyperspaceTechnology, userData.serverData, cel.LFBonuses, userData.userInfo.Class, 0, userData.serverData.ProbeCargo);
+ resources = resources.Sum(res);
+ celResources = res;
+ if (celResources.TotalResources == 0)
+ continue;
+ result.Add(new Dictionary { { cel, celResources } } );
+ } else {
+ _logger.WriteLog(LogLevel.Information, LogSender.Brain, $"Not enough resources transportable");
+ return new();
+ }
+ }
+ _logger.WriteLog(LogLevel.Information, LogSender.Tbot, $"Sending resources from: {cel.Coordinate.ToString()} to {destination.Coordinate.ToString()} - Resources: {celResources.TransportableResources}");
+ if (resources.IsEnoughFor(missingResources)) {
+ _logger.WriteLog(LogLevel.Information, LogSender.Tbot, $"{result.Count()} transports will be send to {destination.ToString()}");
+ return result;
+ }
+ }
+ if (resources.IsEnoughFor(missingResources)) {
+ _logger.WriteLog(LogLevel.Information, LogSender.Tbot, $"{result.Count()} transports will be send to {destination.ToString()}");
+ return result;
+ } else {
+ _logger.WriteLog(LogLevel.Information, LogSender.Tbot, $"Not enough resources transportable");
+ return new();
+ }
+ }
}
}
diff --git a/TBot/Includes/ICalculationService.cs b/TBot/Includes/ICalculationService.cs
index 0ca15f86..ab84d941 100644
--- a/TBot/Includes/ICalculationService.cs
+++ b/TBot/Includes/ICalculationService.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
+using Tbot.Services;
using TBot.Model;
using TBot.Ogame.Infrastructure;
using TBot.Ogame.Infrastructure.Enums;
@@ -37,12 +38,13 @@ public interface ICalculationService {
Ships CalcFullExpeditionShips(Ships fleet, Buildables primaryShip, int expeditionsNumber, ServerData serverdata, Researches researches, LFBonuses LFBonuses, CharacterClass playerClass = CharacterClass.NoClass, int probeCargo = 0);
Ships CalcIdealExpeditionShips(Buildables buildable, int hyperspaceTech, float expeditionResourcesBonus, Dictionary shipsBonus, ServerData serverData, CharacterClass playerClass = CharacterClass.NoClass, int probeCargo = 0);
long CalcMaxBuildableNumber(Buildables buildable, Resources resources);
+ Defences CalcmaxDefencesBuildable(Defences defences, Resources resources);
int CalcMaxCrawlers(Planet planet, CharacterClass userClass, bool hasGeologist);
int CalcMaxExpeditions(int astrophysics, bool isDiscoverer, bool hasAdmiral);
int CalcMaxExpeditions(Researches researches, CharacterClass playerClass, Staff staff);
int CalcMaxPlanets(int astrophysics);
int CalcMaxPlanets(Researches researches);
- bool CalcLimitAstro(int pos, Researches researches);
+ bool IsAstrophysicsPositionValid(int pos, int astrophysics);
Resources CalcMaxTransportableResources(Ships ships, Resources resources, int hyperspaceTech, ServerData serverData, LFBonuses lfBonuses = null, CharacterClass playerClass = CharacterClass.NoClass, long deutToLeave = 0, int probeCargo = 0);
long CalcMetalProduction(Buildings buildings, int position, int speedFactor, float ratio = 1, Researches researches = null, LFBonuses lfBonuses = null, CharacterClass playerClass = CharacterClass.NoClass, bool hasGeologist = false, bool hasStaff = false, int crawlers = 0, float crawlerRatio = 1);
long CalcMetalProduction(int level, int position, int speedFactor, float ratio = 1, int plasma = 0, float metalLFBonus = 0, CharacterClass playerClass = CharacterClass.NoClass, bool hasGeologist = false, bool hasStaff = false, int crawlers = 0, float crawlerRatio = 1);
@@ -124,7 +126,7 @@ public interface ICalculationService {
Buildables GetNextMineToBuild(Planet planet, int maxMetalMine = 100, int maxCrystalMine = 100, int maxDeuteriumSynthetizer = 100, bool optimizeForStart = true);
Buildables GetNextMineToBuild(Planet planet, Researches researches = null, int speedFactor = 1, int maxMetalMine = 100, int maxCrystalMine = 100, int maxDeuteriumSynthetizer = 100, float ratio = 1, CharacterClass playerClass = CharacterClass.NoClass, bool hasGeologist = false, bool hasStaff = false, bool optimizeForStart = true, float maxDaysOfInvestmentReturn = 36500);
Buildables GetNextMineToBuild(Planet planet, Researches researches, Buildings maxBuildings, Facilities maxFacilities, CharacterClass playerClass, Staff staff, ServerData serverData, AutoMinerSettings settings, float ratio = 1);
- Buildables GetNextResearchToBuild(Planet celestial, Researches researches, bool prioritizeRobotsAndNanitesOnNewPlanets = false, Slots slots = null, int maxEnergyTechnology = 20, int maxLaserTechnology = 12, int maxIonTechnology = 5, int maxHyperspaceTechnology = 20, int maxPlasmaTechnology = 20, int maxCombustionDrive = 19, int maxImpulseDrive = 17, int maxHyperspaceDrive = 15, int maxEspionageTechnology = 8, int maxComputerTechnology = 20, int maxAstrophysics = 23, int maxIntergalacticResearchNetwork = 12, int maxWeaponsTechnology = 25, int maxShieldingTechnology = 25, int maxArmourTechnology = 25, bool optimizeForStart = true, bool ensureExpoSlots = true, CharacterClass playerClass = CharacterClass.NoClass, bool hasGeologist = false, bool hasAdmiral = false);
+ Buildables GetNextResearchToBuild(Planet celestial, Researches researches, bool prioritizeRobotsAndNanitesOnNewPlanets = false, Slots slots = null, int maxEnergyTechnology = 20, int maxLaserTechnology = 12, int maxIonTechnology = 5, int maxHyperspaceTechnology = 20, int maxPlasmaTechnology = 20, int maxCombustionDrive = 19, int maxImpulseDrive = 17, int maxHyperspaceDrive = 15, int maxEspionageTechnology = 8, int maxComputerTechnology = 20, int maxAstrophysics = 23, int maxIntergalacticResearchNetwork = 12, int maxWeaponsTechnology = 25, int maxShieldingTechnology = 25, int maxArmourTechnology = 25, bool optimizeForStart = true, bool ensureExpoSlots = true, bool ForceResearchWhateverTheLabLevel = false, CharacterClass playerClass = CharacterClass.NoClass, bool hasGeologist = false, bool hasAdmiral = false);
long GetProductionEnergyDelta(Buildables buildable, int level, int energyTechnology = 0, float ratio = 1, CharacterClass userClass = CharacterClass.NoClass, bool hasEngineer = false, bool hasStaff = false);
long GetRequiredEnergyDelta(Buildables buildable, int level);
int GetSolarSatelliteOutput(Planet planet, bool isCollector = false, bool hasEngineer = false, bool hasFullStaff = false);
@@ -152,7 +154,9 @@ public interface ICalculationService {
bool ShouldAbandon(Planet celestial, int maxCases, int Temperature, Fields fieldsSettings, Temperature temperaturesSettings);
int CountPlanetsInRange(List planets, int galaxy, int minSystem, int maxSystem, int minPosition, int maxPositions, int minSlots, int minTemperature, int maxTemperature);
int CountPlanetsInRange(List planets, int galaxy, int minSystem, int maxSystem, int minPosition, int maxPositions, int minSlots, int minTemperature, int maxTemperature);
-
+ bool IsThereMoonHere(List planets, Celestial celestial);
+ int CalcSlotsPriority(Feature feature, List rankSlotsPriority, Slots slots, List fleets, int slotsToLeaveFree = 0);
+ List> CalcMultipleOrigin(Celestial celestialToBuild, List allCelestials, Resources missingResources, TransportSettings transportSettings, List fleets, UserData userData);
}
}
diff --git a/TBot/Includes/IFleetScheduler.cs b/TBot/Includes/IFleetScheduler.cs
index 1509e0a2..f029c3ec 100644
--- a/TBot/Includes/IFleetScheduler.cs
+++ b/TBot/Includes/IFleetScheduler.cs
@@ -18,10 +18,10 @@ public interface IFleetScheduler {
Task CancelFleet(Fleet fleet);
Task> UpdateFleets();
void RetireFleet(object fleet);
- Task HandleMinerTransport(Celestial origin, Celestial destination, Resources resources, Buildables buildable = Buildables.Null, Buildings maxBuildings = null, Facilities maxFacilities = null, Facilities maxLunarFacilities = null, AutoMinerSettings autoMinerSettings = null);
- Task HandleMinerTransport(Celestial origin, Celestial destination, Resources resources, LFBuildables buildable = LFBuildables.None, LFBuildings maxLFBuildings = null, bool preventIfMoreExpensiveThanNextMine = false);
- Task Collect();
- Task CollectImpl(bool fromTelegram = false);
+ Task HandleMinerTransport(Celestial origin, Celestial destination, Celestial differentDestination, Resources resources, Buildables buildable = Buildables.Null, Buildings maxBuildings = null, Facilities maxFacilities = null, Facilities maxLunarFacilities = null, AutoMinerSettings autoMinerSettings = null);
+ Task HandleMinerTransport(Celestial origin, Celestial destination, Celestial differentDestination, Resources resources, LFBuildables buildable = LFBuildables.None, LFBuildings maxLFBuildings = null, bool preventIfMoreExpensiveThanNextMine = false);
+ Task Collect(bool noLimit = false);
+ Task CollectImpl(bool fromTelegram = false, bool noLimit = false);
Task CollectDeut(long minAmount = 0);
}
}
diff --git a/TBot/Model/AutoFarmBlacklist.cs b/TBot/Model/AutoFarmBlacklist.cs
new file mode 100644
index 00000000..2a17ff72
--- /dev/null
+++ b/TBot/Model/AutoFarmBlacklist.cs
@@ -0,0 +1,168 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Newtonsoft.Json;
+using TBot.Ogame.Infrastructure.Models;
+
+namespace TBot.Model {
+ public enum BlacklistReason {
+ HasFleet,
+ HasDefense,
+ LowResources,
+ ManuallyAdded
+ }
+
+ public class BlacklistedTarget {
+ public Coordinate Coordinate { get; set; }
+ public BlacklistReason Reason { get; set; }
+ public DateTime BlacklistedAt { get; set; }
+ public DateTime ExpiresAt { get; set; }
+
+ public BlacklistedTarget() {
+ }
+
+ public BlacklistedTarget(Coordinate coordinate, BlacklistReason reason, DateTime expiresAt) {
+ Coordinate = coordinate;
+ Reason = reason;
+ BlacklistedAt = DateTime.UtcNow;
+ ExpiresAt = expiresAt;
+ }
+
+ public bool IsExpired() {
+ return DateTime.UtcNow >= ExpiresAt;
+ }
+ }
+
+ public class AutoFarmBlacklist {
+ private List _blacklistedTargets;
+ private readonly object _lock = new object();
+ private string _filePath;
+
+ public AutoFarmBlacklist() {
+ _blacklistedTargets = new List();
+ }
+
+ public AutoFarmBlacklist(string filePath) {
+ string dataFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "data");
+ if (!Directory.Exists(dataFolder)) {
+ Directory.CreateDirectory(dataFolder);
+ }
+ _filePath = Path.Combine(dataFolder, filePath);
+ _blacklistedTargets = new List();
+ LoadFromFile();
+ }
+
+ public void AddTarget(Coordinate coordinate, BlacklistReason reason, int hoursUntilReset) {
+ lock (_lock) {
+ _blacklistedTargets.RemoveAll(t => t.Coordinate.Galaxy == coordinate.Galaxy
+ && t.Coordinate.System == coordinate.System
+ && t.Coordinate.Position == coordinate.Position);
+
+ DateTime expiresAt = DateTime.UtcNow.AddHours(hoursUntilReset);
+ _blacklistedTargets.Add(new BlacklistedTarget(coordinate, reason, expiresAt));
+ SaveToFile();
+ }
+ }
+
+ public bool IsBlacklisted(Coordinate coordinate) {
+ lock (_lock) {
+ CleanupExpiredTargets();
+
+ return _blacklistedTargets.Any(t =>
+ t.Coordinate.Galaxy == coordinate.Galaxy
+ && t.Coordinate.System == coordinate.System
+ && t.Coordinate.Position == coordinate.Position);
+ }
+ }
+
+ public BlacklistedTarget GetBlacklistedTarget(Coordinate coordinate) {
+ lock (_lock) {
+ CleanupExpiredTargets();
+
+ return _blacklistedTargets.FirstOrDefault(t =>
+ t.Coordinate.Galaxy == coordinate.Galaxy
+ && t.Coordinate.System == coordinate.System
+ && t.Coordinate.Position == coordinate.Position);
+ }
+ }
+
+ public void RemoveTarget(Coordinate coordinate) {
+ lock (_lock) {
+ _blacklistedTargets.RemoveAll(t =>
+ t.Coordinate.Galaxy == coordinate.Galaxy
+ && t.Coordinate.System == coordinate.System
+ && t.Coordinate.Position == coordinate.Position);
+ }
+ SaveToFile();
+ }
+
+ public void ClearAll() {
+ lock (_lock) {
+ _blacklistedTargets.Clear();
+ }
+ SaveToFile();
+ }
+
+ public int GetBlacklistedCount() {
+ lock (_lock) {
+ CleanupExpiredTargets();
+ return _blacklistedTargets.Count;
+ }
+ }
+
+ public List GetAllBlacklisted() {
+ lock (_lock) {
+ CleanupExpiredTargets();
+ return new List(_blacklistedTargets);
+ }
+ }
+
+ private void CleanupExpiredTargets() {
+ _blacklistedTargets.RemoveAll(t => t.IsExpired());
+ }
+
+ public void ManualCleanup() {
+ lock (_lock) {
+ CleanupExpiredTargets();
+ }
+ }
+ private void LoadFromFile() {
+ if (string.IsNullOrEmpty(_filePath) || !File.Exists(_filePath)) {
+ return;
+ }
+
+ try {
+ string json = File.ReadAllText(_filePath);
+ var loaded = JsonConvert.DeserializeObject>(json);
+ if (loaded != null) {
+ lock (_lock) {
+ _blacklistedTargets = loaded;
+ CleanupExpiredTargets();
+ }
+ }
+ } catch (Exception) {
+ lock (_lock) {
+ _blacklistedTargets = new List();
+ }
+}
+ }
+
+ private void SaveToFile() {
+ if (string.IsNullOrEmpty(_filePath)) {
+ return;
+ }
+
+ List snapshot;
+ lock (_lock) {
+ snapshot = _blacklistedTargets.ToList();
+ }
+
+ try {
+ string json = JsonConvert.SerializeObject(snapshot, Formatting.Indented);
+ File.WriteAllText(_filePath, json);
+ } catch (Exception) {
+ }
+ }
+ }
+}
diff --git a/TBot/Model/AutoFarmSuccessfulTargets.cs b/TBot/Model/AutoFarmSuccessfulTargets.cs
new file mode 100644
index 00000000..eef9351b
--- /dev/null
+++ b/TBot/Model/AutoFarmSuccessfulTargets.cs
@@ -0,0 +1,306 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Newtonsoft.Json;
+using TBot.Ogame.Infrastructure.Models;
+
+namespace TBot.Model
+{
+ public class SuccessfulTarget
+ {
+ public string Coordinate { get; set; }
+ public DateTime FirstAttacked { get; set; }
+ public DateTime LastAttacked { get; set; }
+ public int TimesAttacked { get; set; }
+ public Resources TotalLootCollected { get; set; }
+ public Resources AverageLoot { get; set; }
+
+ public SuccessfulTarget()
+ {
+ TotalLootCollected = new Resources();
+ AverageLoot = new Resources();
+ }
+
+ public SuccessfulTarget(string coordinate, Resources loot)
+ {
+ Coordinate = coordinate;
+ FirstAttacked = DateTime.UtcNow;
+ LastAttacked = DateTime.UtcNow;
+ TimesAttacked = 1;
+
+ TotalLootCollected = new Resources
+ {
+ Metal = loot.Metal,
+ Crystal = loot.Crystal,
+ Deuterium = loot.Deuterium
+ };
+
+ AverageLoot = new Resources
+ {
+ Metal = loot.Metal,
+ Crystal = loot.Crystal,
+ Deuterium = loot.Deuterium
+ };
+ }
+
+ public void AddAttack(Resources loot)
+ {
+ LastAttacked = DateTime.UtcNow;
+ TimesAttacked++;
+
+ TotalLootCollected.Metal += loot.Metal;
+ TotalLootCollected.Crystal += loot.Crystal;
+ TotalLootCollected.Deuterium += loot.Deuterium;
+
+ AverageLoot.Metal = TotalLootCollected.Metal / TimesAttacked;
+ AverageLoot.Crystal = TotalLootCollected.Crystal / TimesAttacked;
+ AverageLoot.Deuterium = TotalLootCollected.Deuterium / TimesAttacked;
+ }
+ }
+
+ public class AutoFarmSuccessfulTargets
+ {
+ private List _successfulTargets;
+ private readonly object _lock = new object();
+
+ private string _filePath;
+
+ private DateTime _lastSaveUtc = DateTime.MinValue;
+ private int _pendingChanges = 0;
+
+ private readonly TimeSpan _saveInterval = TimeSpan.FromSeconds(60);
+ private const int SaveAfterChanges = 20;
+
+ private const int MaxTargets = 5000;
+ private readonly TimeSpan _staleCutoff = TimeSpan.FromDays(30);
+
+ public AutoFarmSuccessfulTargets()
+ {
+ _successfulTargets = new List();
+ }
+
+ public AutoFarmSuccessfulTargets(string fileName)
+ {
+ string dataFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "data");
+ if (!Directory.Exists(dataFolder))
+ {
+ Directory.CreateDirectory(dataFolder);
+ }
+
+ string safeFileName = MakeSafeFileName(fileName);
+ _filePath = Path.Combine(dataFolder, safeFileName);
+
+ _successfulTargets = new List();
+ LoadFromFile();
+ }
+
+ public void RecordAttack(Coordinate coordinate, Resources loot)
+ {
+ lock (_lock)
+ {
+ string coordStr = $"{coordinate.Galaxy}:{coordinate.System}:{coordinate.Position}";
+
+ var existing = _successfulTargets.FirstOrDefault(t => t.Coordinate == coordStr);
+ if (existing != null)
+ {
+ existing.AddAttack(loot);
+ }
+ else
+ {
+ _successfulTargets.Add(new SuccessfulTarget(coordStr, loot));
+ }
+
+ _pendingChanges++;
+
+ CleanupUnsafeGrowth_NoThrow();
+
+ SaveIfNeeded_NoThrow();
+ }
+ }
+
+ public SuccessfulTarget GetTarget(Coordinate coordinate)
+ {
+ lock (_lock)
+ {
+ string coordStr = $"{coordinate.Galaxy}:{coordinate.System}:{coordinate.Position}";
+ return _successfulTargets.FirstOrDefault(t => t.Coordinate == coordStr);
+ }
+ }
+
+ public List GetAllTargets()
+ {
+ lock (_lock)
+ {
+ return new List(_successfulTargets);
+ }
+ }
+
+ public int GetTotalCount()
+ {
+ lock (_lock)
+ {
+ return _successfulTargets.Count;
+ }
+ }
+
+ public long GetTotalLootCollected()
+ {
+ lock (_lock)
+ {
+ return _successfulTargets.Sum(t =>
+ t.TotalLootCollected.Metal +
+ t.TotalLootCollected.Crystal +
+ t.TotalLootCollected.Deuterium
+ );
+ }
+ }
+
+
+ public void ForceSave()
+ {
+ lock (_lock)
+ {
+ SaveToFile_NoThrow(force: true);
+ }
+ }
+
+ private void LoadFromFile()
+ {
+ if (string.IsNullOrEmpty(_filePath) || !File.Exists(_filePath))
+ {
+ return;
+ }
+
+ try
+ {
+ string json = File.ReadAllText(_filePath);
+ var loaded = JsonConvert.DeserializeObject>(json);
+ if (loaded != null)
+ {
+ _successfulTargets = loaded;
+ }
+ }
+ catch (Exception ex)
+ {
+ _successfulTargets = new List();
+ WriteError_NoThrow($"LoadFromFile failed: {ex.GetType().Name}: {ex.Message}");
+ }
+ }
+
+ private void SaveIfNeeded_NoThrow()
+ {
+ if (string.IsNullOrEmpty(_filePath))
+ return;
+
+ var now = DateTime.UtcNow;
+
+ bool timeDue = (now - _lastSaveUtc) >= _saveInterval;
+ bool changesDue = _pendingChanges >= SaveAfterChanges;
+
+ if (timeDue || changesDue)
+ {
+ SaveToFile_NoThrow(force: false);
+ }
+ }
+
+ private void SaveToFile_NoThrow(bool force)
+ {
+ if (string.IsNullOrEmpty(_filePath))
+ return;
+
+ try
+ {
+ if (!force && _pendingChanges <= 0)
+ return;
+
+ string json = JsonConvert.SerializeObject(_successfulTargets, Formatting.Indented);
+
+ string tmpPath = _filePath + ".tmp";
+
+ File.WriteAllText(tmpPath, json);
+
+ if (File.Exists(_filePath))
+ {
+ try
+ {
+ File.Replace(tmpPath, _filePath, null);
+ }
+ catch
+ {
+ File.Delete(_filePath);
+ File.Move(tmpPath, _filePath);
+ }
+ }
+ else
+ {
+ File.Move(tmpPath, _filePath);
+ }
+
+ _lastSaveUtc = DateTime.UtcNow;
+ _pendingChanges = 0;
+ }
+ catch (Exception ex)
+ {
+ WriteError_NoThrow($"SaveToFile failed: {ex.GetType().Name}: {ex.Message}");
+ }
+ }
+
+ private void CleanupUnsafeGrowth_NoThrow()
+ {
+ try
+ {
+ var cutoff = DateTime.UtcNow - _staleCutoff;
+ _successfulTargets.RemoveAll(t => t.LastAttacked < cutoff);
+
+ if (_successfulTargets.Count > MaxTargets)
+ {
+ _successfulTargets = _successfulTargets
+ .OrderByDescending(t => t.LastAttacked)
+ .Take(MaxTargets)
+ .ToList();
+ }
+ }
+ catch (Exception ex)
+ {
+ WriteError_NoThrow($"Cleanup failed: {ex.GetType().Name}: {ex.Message}");
+ }
+ }
+
+ private static string MakeSafeFileName(string fileName)
+ {
+ if (string.IsNullOrWhiteSpace(fileName))
+ return "autofarm_successful_targets.json";
+
+ string name = Path.GetFileName(fileName);
+
+ if (!name.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
+ name += ".json";
+
+ foreach (char c in Path.GetInvalidFileNameChars())
+ {
+ name = name.Replace(c, '_');
+ }
+
+ return name;
+ }
+
+ private void WriteError_NoThrow(string message)
+ {
+ try
+ {
+ string baseDir = Path.GetDirectoryName(_filePath);
+ if (string.IsNullOrEmpty(baseDir))
+ return;
+
+ string errPath = Path.Combine(baseDir, "autofarm_successful_targets.errors.log");
+ string line = $"[{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC] {message}{Environment.NewLine}";
+ File.AppendAllText(errPath, line);
+ }
+ catch
+ {
+
+ }
+ }
+ }
+}
diff --git a/TBot/Model/SharedFarmState.cs b/TBot/Model/SharedFarmState.cs
new file mode 100644
index 00000000..299b993c
--- /dev/null
+++ b/TBot/Model/SharedFarmState.cs
@@ -0,0 +1,601 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using Newtonsoft.Json;
+using TBot.Ogame.Infrastructure.Models;
+using TBot.Ogame.Infrastructure.Enums;
+
+namespace TBot.Model
+{
+
+ public class FarmBotInstance
+ {
+ public string Name { get; set; }
+ public DateTime LastHeartbeat { get; set; }
+ public bool IsActive { get; set; }
+
+ public bool IsAlive(TimeSpan timeout)
+ {
+ return (DateTime.UtcNow - LastHeartbeat) < timeout;
+ }
+ }
+
+ public class ScannedTarget
+ {
+ public string Coord { get; set; }
+ public string ScannedBy { get; set; }
+ public DateTime ScannedAt { get; set; }
+ public DateTime ExpiresAt { get; set; }
+ public long TotalResources { get; set; }
+ public bool HasFleet { get; set; }
+ public bool HasDefense { get; set; }
+
+ public bool IsExpired()
+ {
+ return DateTime.UtcNow >= ExpiresAt;
+ }
+
+ public Coordinate GetCoordinate()
+ {
+ if (string.IsNullOrWhiteSpace(Coord))
+ return null;
+
+ var parts = Coord.Split(':');
+ if (parts.Length != 3)
+ return null;
+
+ if (!int.TryParse(parts[0], out var g)) return null;
+ if (!int.TryParse(parts[1], out var s)) return null;
+ if (!int.TryParse(parts[2], out var p)) return null;
+
+ return new Coordinate
+ {
+ Galaxy = g,
+ System = s,
+ Position = p,
+ Type = Celestials.Planet
+ };
+ }
+ }
+
+ public class ClaimedAttack
+ {
+ public string Coord { get; set; }
+ public string ClaimedBy { get; set; }
+ public DateTime ClaimedAt { get; set; }
+ public DateTime? AttackSentAt { get; set; }
+ public DateTime ReturnsAt { get; set; }
+
+ public bool IsExpired()
+ {
+ if (DateTime.UtcNow >= ReturnsAt)
+ return true;
+
+ if (!AttackSentAt.HasValue && (DateTime.UtcNow - ClaimedAt).TotalHours > 2)
+ return true;
+
+ return false;
+ }
+
+ public Coordinate GetCoordinate()
+ {
+ if (string.IsNullOrWhiteSpace(Coord))
+ return null;
+
+ var parts = Coord.Split(':');
+ if (parts.Length != 3)
+ return null;
+
+ if (!int.TryParse(parts[0], out var g)) return null;
+ if (!int.TryParse(parts[1], out var s)) return null;
+ if (!int.TryParse(parts[2], out var p)) return null;
+
+ return new Coordinate
+ {
+ Galaxy = g,
+ System = s,
+ Position = p,
+ Type = Celestials.Planet
+ };
+ }
+ }
+
+ public class SharedFarmState
+ {
+ private const string DEFAULT_FILENAME = "shared_farm_state.json";
+ private const int MAX_RETRY_ATTEMPTS = 5;
+ private const int RETRY_DELAY_MS = 100;
+ private const string MUTEX_GLOBAL_NAME = @"Global\TBot_SharedFarmState_Mutex";
+ private const string MUTEX_LOCAL_NAME = @"Local\TBot_SharedFarmState_Mutex";
+
+ public string Version { get; set; } = "1.1";
+ public DateTime LastUpdated { get; set; }
+ public List Instances { get; set; }
+ public List ScannedTargets { get; set; }
+ public List ClaimedAttacks { get; set; }
+
+ private static readonly object _globalLock = new object();
+ private string _filePath;
+
+ public SharedFarmState()
+ {
+ Instances = new List();
+ ScannedTargets = new List();
+ ClaimedAttacks = new List();
+ LastUpdated = DateTime.UtcNow;
+ }
+
+ public static SharedFarmState Initialize(string instanceName = null)
+ {
+ string dataFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "data");
+ if (!Directory.Exists(dataFolder))
+ Directory.CreateDirectory(dataFolder);
+
+ string filePath = Path.Combine(dataFolder, DEFAULT_FILENAME);
+ var state = LoadFromFile(filePath);
+ state._filePath = filePath;
+
+ if (!string.IsNullOrEmpty(instanceName))
+ {
+ state.UpdateInstanceHeartbeat(instanceName);
+ state.Save();
+ }
+
+ return state;
+ }
+
+ private static SharedFarmState LoadFromFile(string filePath)
+ {
+ lock (_globalLock)
+ {
+ if (!File.Exists(filePath))
+ return new SharedFarmState();
+
+ for (int attempt = 0; attempt < MAX_RETRY_ATTEMPTS; attempt++)
+ {
+ try
+ {
+ string json = File.ReadAllText(filePath);
+ var state = JsonConvert.DeserializeObject(json);
+
+ if (state != null)
+ {
+ state._filePath = filePath;
+ state.CleanupExpiredEntries();
+ return state;
+ }
+ }
+ catch (IOException) when (attempt < MAX_RETRY_ATTEMPTS - 1)
+ {
+ Thread.Sleep(RETRY_DELAY_MS * (attempt + 1));
+ }
+ catch (JsonException)
+ {
+ try
+ {
+ string backupPath = filePath + ".corrupt." + DateTime.UtcNow.Ticks;
+ File.Move(filePath, backupPath);
+ }
+ catch {}
+ return new SharedFarmState();
+ }
+ catch
+ {
+ return new SharedFarmState();
+ }
+ }
+
+ return new SharedFarmState();
+ }
+ }
+
+ private static Mutex CreateNamedMutex()
+ {
+ try
+ {
+ return new Mutex(false, MUTEX_GLOBAL_NAME);
+ }
+ catch
+ {
+ return new Mutex(false, MUTEX_LOCAL_NAME);
+ }
+ }
+
+ private static bool WithCrossProcessMutex(TimeSpan timeout, Action action)
+ {
+ using (var mutex = CreateNamedMutex())
+ {
+ try
+ {
+ if (!mutex.WaitOne(timeout))
+ return false;
+
+ try
+ {
+ action();
+ return true;
+ }
+ finally
+ {
+ try { mutex.ReleaseMutex(); } catch { }
+ }
+ }
+ catch (AbandonedMutexException)
+ {
+ try
+ {
+ action();
+ return true;
+ }
+ finally
+ {
+ try { mutex.ReleaseMutex(); } catch { }
+ }
+ }
+ catch
+ {
+ return false;
+ }
+ }
+ }
+
+ public void Save()
+ {
+ if (string.IsNullOrEmpty(_filePath))
+ return;
+
+ WithCrossProcessMutex(TimeSpan.FromSeconds(5), () =>
+ {
+ SaveNoMutex();
+ });
+ }
+
+ private void SaveNoMutex()
+ {
+ lock (_globalLock)
+ {
+ LastUpdated = DateTime.UtcNow;
+ CleanupExpiredEntries();
+
+ string tempPath = _filePath + ".tmp";
+ string backupPath = _filePath + ".bak";
+
+ try
+ {
+ string json = JsonConvert.SerializeObject(this, Formatting.Indented);
+ File.WriteAllText(tempPath, json);
+
+ try
+ {
+ if (File.Exists(_filePath))
+ File.Replace(tempPath, _filePath, backupPath, ignoreMetadataErrors: true);
+ else
+ File.Move(tempPath, _filePath);
+ }
+ catch
+ {
+ if (File.Exists(_filePath))
+ File.Delete(_filePath);
+
+ File.Move(tempPath, _filePath);
+ }
+ }
+ catch
+ {
+ if (File.Exists(tempPath))
+ {
+ try { File.Delete(tempPath); } catch { }
+ }
+ }
+ }
+ }
+
+ public void Reload()
+ {
+ if (string.IsNullOrEmpty(_filePath))
+ return;
+
+ var fresh = LoadFromFile(_filePath);
+ fresh._filePath = _filePath;
+
+ Version = fresh.Version;
+ LastUpdated = fresh.LastUpdated;
+ Instances = fresh.Instances;
+ ScannedTargets = fresh.ScannedTargets;
+ ClaimedAttacks = fresh.ClaimedAttacks;
+ }
+
+ public void UpdateInstanceHeartbeat(string instanceName)
+ {
+ var instance = Instances.FirstOrDefault(i => i.Name == instanceName);
+ if (instance == null)
+ {
+ instance = new FarmBotInstance
+ {
+ Name = instanceName,
+ IsActive = true
+ };
+ Instances.Add(instance);
+ }
+
+ instance.LastHeartbeat = DateTime.UtcNow;
+ instance.IsActive = true;
+ }
+
+ public List GetActiveBots()
+ {
+ return Instances.Where(i => i.IsAlive(TimeSpan.FromMinutes(5))).ToList();
+ }
+
+ public ScannedTarget GetRecentScan(Coordinate coord, int withinHours = 8)
+ {
+ string coordStr = $"{coord.Galaxy}:{coord.System}:{coord.Position}";
+ return ScannedTargets.FirstOrDefault(s =>
+ s.Coord == coordStr &&
+ (DateTime.UtcNow - s.ScannedAt).TotalHours < withinHours);
+ }
+
+ public void AddScannedTarget(Coordinate coord, string scannedBy, long totalResources,
+ bool hasFleet, bool hasDefense, int expiresInHours = 8)
+ {
+ string coordStr = $"{coord.Galaxy}:{coord.System}:{coord.Position}";
+
+ ScannedTargets.RemoveAll(s => s.Coord == coordStr);
+
+ ScannedTargets.Add(new ScannedTarget
+ {
+ Coord = coordStr,
+ ScannedBy = scannedBy,
+ ScannedAt = DateTime.UtcNow,
+ ExpiresAt = DateTime.UtcNow.AddHours(expiresInHours),
+ TotalResources = totalResources,
+ HasFleet = hasFleet,
+ HasDefense = hasDefense
+ });
+ }
+
+ public void UpdateScannedTarget(Coordinate coord, long totalResources, bool hasFleet, bool hasDefense)
+ {
+ string coordStr = $"{coord.Galaxy}:{coord.System}:{coord.Position}";
+ var existing = ScannedTargets.FirstOrDefault(s => s.Coord == coordStr);
+
+ if (existing != null)
+ {
+ existing.TotalResources = totalResources;
+ existing.HasFleet = hasFleet;
+ existing.HasDefense = hasDefense;
+ }
+ }
+
+ public List GetGoodTargets(long minResources, int maxAgeMinutes = 30)
+ {
+ return ScannedTargets
+ .Where(s => !s.IsExpired())
+ .Where(s => (DateTime.UtcNow - s.ScannedAt).TotalMinutes <= maxAgeMinutes)
+ .Where(s => s.TotalResources > 0)
+ .Where(s => s.TotalResources >= minResources)
+ .Where(s => !s.HasFleet)
+ .Where(s => !s.HasDefense)
+ .Where(s => !IsTargetClaimed(s.GetCoordinate()))
+ .OrderByDescending(s => s.TotalResources)
+ .ToList();
+ }
+
+ public List GetTargetsWithoutReports(string scannedByInstance = null)
+ {
+ var query = ScannedTargets
+ .Where(s => !s.IsExpired())
+ .Where(s => s.TotalResources == 0);
+
+ if (!string.IsNullOrEmpty(scannedByInstance))
+ query = query.Where(s => s.ScannedBy == scannedByInstance);
+
+ return query.OrderBy(s => s.ScannedAt).ToList();
+ }
+
+ public bool ShouldRescan(Coordinate coord, int maxAgeMinutes = 30)
+ {
+ string coordStr = $"{coord.Galaxy}:{coord.System}:{coord.Position}";
+ var scan = ScannedTargets.FirstOrDefault(s => s.Coord == coordStr && !s.IsExpired());
+
+ if (scan == null) return true;
+ if (scan.TotalResources == 0) return true;
+
+ return (DateTime.UtcNow - scan.ScannedAt).TotalMinutes > maxAgeMinutes;
+ }
+
+ public bool TryClaimAttack(Coordinate coord, string claimedBy, DateTime returnsAt)
+ {
+ Reload();
+ return TryClaimAttack_NoReload(coord, claimedBy, returnsAt);
+ }
+
+ private bool TryClaimAttack_NoReload(Coordinate coord, string claimedBy, DateTime returnsAt)
+ {
+ string coordStr = $"{coord.Galaxy}:{coord.System}:{coord.Position}";
+
+ var existingClaim = ClaimedAttacks.FirstOrDefault(c => c.Coord == coordStr && !c.IsExpired());
+ if (existingClaim != null && existingClaim.ClaimedBy != claimedBy)
+ return false;
+
+ ClaimedAttacks.RemoveAll(c => c.Coord == coordStr);
+ ClaimedAttacks.Add(new ClaimedAttack
+ {
+ Coord = coordStr,
+ ClaimedBy = claimedBy,
+ ClaimedAt = DateTime.UtcNow,
+ AttackSentAt = null,
+ ReturnsAt = returnsAt
+ });
+
+ return true;
+ }
+
+ public void MarkAttackSent(Coordinate coord, string claimedBy)
+ {
+ string coordStr = $"{coord.Galaxy}:{coord.System}:{coord.Position}";
+ var claim = ClaimedAttacks.FirstOrDefault(c => c.Coord == coordStr && c.ClaimedBy == claimedBy);
+ if (claim != null)
+ {
+ claim.AttackSentAt = DateTime.UtcNow;
+ }
+ }
+
+ public bool IsTargetClaimed(Coordinate coord)
+ {
+ if (coord == null) return false;
+ string coordStr = $"{coord.Galaxy}:{coord.System}:{coord.Position}";
+ return ClaimedAttacks.Any(c => c.Coord == coordStr && !c.IsExpired());
+ }
+
+ public string GetClaimOwner(Coordinate coord)
+ {
+ if (coord == null) return null;
+ string coordStr = $"{coord.Galaxy}:{coord.System}:{coord.Position}";
+ return ClaimedAttacks.FirstOrDefault(c => c.Coord == coordStr && !c.IsExpired())?.ClaimedBy;
+ }
+
+ private void CleanupExpiredEntries()
+ {
+ ScannedTargets.RemoveAll(s => s.IsExpired());
+ ClaimedAttacks.RemoveAll(c => c.IsExpired());
+
+ foreach (var instance in Instances)
+ {
+ if (!instance.IsAlive(TimeSpan.FromMinutes(5)))
+ instance.IsActive = false;
+ }
+ }
+
+ public string GetStats()
+ {
+ CleanupExpiredEntries();
+ return $"Instances: {Instances.Count(i => i.IsActive)} active, " +
+ $"Scanned: {ScannedTargets.Count}, " +
+ $"Claims: {ClaimedAttacks.Count}";
+ }
+
+ public static void AddScannedTargetAtomic(string filePath, Coordinate coord, string scannedBy,
+ long totalResources, bool hasFleet, bool hasDefense)
+ {
+ WithCrossProcessMutex(TimeSpan.FromSeconds(5), () =>
+ {
+ var state = LoadFromFile(filePath);
+ state._filePath = filePath;
+
+ state.AddScannedTarget(coord, scannedBy, totalResources, hasFleet, hasDefense);
+ state.SaveNoMutex();
+ });
+ }
+
+ public static void UpdateScannedTargetAtomic(string filePath, Coordinate coord, long totalResources, bool hasFleet, bool hasDefense)
+ {
+ WithCrossProcessMutex(TimeSpan.FromSeconds(5), () =>
+ {
+ var state = LoadFromFile(filePath);
+ state._filePath = filePath;
+
+ state.UpdateScannedTarget(coord, totalResources, hasFleet, hasDefense);
+ state.SaveNoMutex();
+ });
+ }
+
+ public static void MarkAttackSentAtomic(string filePath, Coordinate coord, string claimedBy)
+ {
+ WithCrossProcessMutex(TimeSpan.FromSeconds(5), () =>
+ {
+ var state = LoadFromFile(filePath);
+ state._filePath = filePath;
+
+ state.MarkAttackSent(coord, claimedBy);
+ state.SaveNoMutex();
+ });
+ }
+
+ public static bool TryReserveScan(string filePath, Coordinate coord, string scannedBy,
+ int withinHours = 8, int maxAgeMinutes = 30)
+ {
+ bool result = false;
+
+ bool acquired = WithCrossProcessMutex(TimeSpan.FromSeconds(5), () =>
+ {
+ var state = LoadFromFile(filePath);
+ state._filePath = filePath;
+
+ if (state.ShouldRescan(coord, maxAgeMinutes))
+ {
+ state.AddScannedTarget(coord, scannedBy, 0, false, false);
+ state.SaveNoMutex();
+ result = true;
+ return;
+ }
+
+ string coordStr = $"{coord.Galaxy}:{coord.System}:{coord.Position}";
+ var recentScan = state.ScannedTargets.FirstOrDefault(s =>
+ s.Coord == coordStr &&
+ (DateTime.UtcNow - s.ScannedAt).TotalHours < withinHours);
+
+ if (recentScan != null)
+ {
+ if (recentScan.ScannedBy == scannedBy)
+ {
+ if (state.ShouldRescan(coord, maxAgeMinutes))
+ {
+ state.AddScannedTarget(coord, scannedBy, 0, false, false);
+ state.SaveNoMutex();
+ result = true;
+ return;
+ }
+ result = false;
+ return;
+ }
+ else
+ {
+ if ((DateTime.UtcNow - recentScan.ScannedAt).TotalMinutes <= maxAgeMinutes)
+ {
+ result = false;
+ return;
+ }
+
+ state.AddScannedTarget(coord, scannedBy, 0, false, false);
+ state.SaveNoMutex();
+ result = true;
+ return;
+ }
+ }
+
+ state.AddScannedTarget(coord, scannedBy, 0, false, false);
+ state.SaveNoMutex();
+ result = true;
+ });
+
+ if (!acquired)
+ return false;
+
+ return result;
+ }
+
+ public static bool TryClaimAttackAtomic(string filePath, Coordinate coord, string claimedBy, DateTime returnsAt)
+ {
+ bool claimed = false;
+
+ bool acquired = WithCrossProcessMutex(TimeSpan.FromSeconds(5), () =>
+ {
+ var state = LoadFromFile(filePath);
+ state._filePath = filePath;
+
+ claimed = state.TryClaimAttack_NoReload(coord, claimedBy, returnsAt);
+
+ if (claimed)
+ state.SaveNoMutex();
+ });
+
+ if (!acquired)
+ return false;
+
+ return claimed;
+ }
+ }
+}
diff --git a/TBot/Services/InstanceManager.cs b/TBot/Services/InstanceManager.cs
index fc2ac212..f06c6e6c 100644
--- a/TBot/Services/InstanceManager.cs
+++ b/TBot/Services/InstanceManager.cs
@@ -149,6 +149,11 @@ public async void OnSettingsChanged() {
string alias = instanceToBeInited.Alias;
_logger.WriteLog(LogLevel.Information, LogSender.Main, $"Asynchronously initializing instance \"{alias}\" \"{cInstanceSettingPath}\"");
awaitingInstances.Add(StartTBotMain(cInstanceSettingPath, alias));
+ //Generate random sleeptime
+ Random waitTime = new Random();
+ int millisecondsTimeout = waitTime.Next(30, 60) * 1000;
+ //Put the thread to sleep
+ System.Threading.Thread.Sleep(millisecondsTimeout);
}
// Await initialization and add initialized instances
diff --git a/TBot/Services/TBotMain.cs b/TBot/Services/TBotMain.cs
index 6a09f4c5..2f89bc92 100644
--- a/TBot/Services/TBotMain.cs
+++ b/TBot/Services/TBotMain.cs
@@ -135,7 +135,7 @@ private Credentials GetCredentialsFromSettings() {
private Device GetDeviceFromSettings() {
return new() {
- Name = ((string) InstanceSettings.Credentials.DeviceConf.Name).ToUpper(),
+ Name = ((string) InstanceSettings.Credentials.DeviceConf.Name),
System = (string) InstanceSettings.Credentials.DeviceConf.System ?? "",
Browser = (string) InstanceSettings.Credentials.DeviceConf.Browser ?? "",
UserAgent = (string) InstanceSettings.Credentials.DeviceConf.UserAgent ?? "",
@@ -185,7 +185,7 @@ private async Task InitializeOgame() {
_ogameService.Initialize(GetCredentialsFromSettings(), GetDeviceFromSettings(), proxy, (string) host, int.Parse(port), (string) captchaKey);
- await _ogameService.SetUserAgent((string) InstanceSettings.Credentials.DeviceConf.UserAgent);
+ await Task.Delay(RandomizeHelper.CalcRandomInterval(IntervalType.AFewSeconds));
}
private async Task ResolveCaptcha() {
@@ -376,6 +376,7 @@ public async Task InitializeFeature(Feature feat) {
long dueTime = feat switch {
Feature.Defender => RandomizeHelper.CalcRandomInterval(IntervalType.AFewSeconds),
Feature.BrainAutobuildCargo => RandomizeHelper.CalcRandomInterval(IntervalType.AMinuteOrTwo),
+ Feature.BrainAutobuildDefence => RandomizeHelper.CalcRandomInterval(IntervalType.AMinuteOrTwo),
Feature.BrainAutoRepatriate => RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds),
Feature.BrainAutoMine => RandomizeHelper.CalcRandomInterval(IntervalType.AFewSeconds),
Feature.BrainLifeformAutoMine => RandomizeHelper.CalcRandomInterval(IntervalType.AFewSeconds),
@@ -461,10 +462,10 @@ public async Task EditSettings(Celestial celestial = null, Feature feature
}
if (feature == Feature.BrainAutoRepatriate || feature == Feature.Null) {
- jsonObj["Brain"]["AutoRepatriate"]["Target"]["Galaxy"] = (int) celestial.Coordinate.Galaxy;
- jsonObj["Brain"]["AutoRepatriate"]["Target"]["System"] = (int) celestial.Coordinate.System;
- jsonObj["Brain"]["AutoRepatriate"]["Target"]["Position"] = (int) celestial.Coordinate.Position;
- jsonObj["Brain"]["AutoRepatriate"]["Target"]["Type"] = type;
+ jsonObj["Brain"]["AutoRepatriate"]["Target"][0]["Galaxy"] = (int) celestial.Coordinate.Galaxy;
+ jsonObj["Brain"]["AutoRepatriate"]["Target"][0]["System"] = (int) celestial.Coordinate.System;
+ jsonObj["Brain"]["AutoRepatriate"]["Target"][0]["Position"] = (int) celestial.Coordinate.Position;
+ jsonObj["Brain"]["AutoRepatriate"]["Target"][0]["Type"] = type;
}
if (feature == Feature.Expeditions || feature == Feature.Null) {
@@ -841,9 +842,8 @@ await SendTelegramMessage($"Auction done with Resources of Planet {celestial.Nam
}
}
- public void TelegramCollect() {
- _fleetScheduler.Collect();
-
+ public void TelegramCollect(bool noLimit = false) {
+ _fleetScheduler.Collect(noLimit);
return;
}
@@ -899,7 +899,7 @@ public async Task TelegramJumpGate(Celestial origin, Coordinate destination, str
if (mode.Equals("auto")) {
float cargoBonus = 0;
if (origin.LFBonuses != null && origin.LFBonuses.Ships != null && origin.LFBonuses.Ships.Count > 0 && origin.LFBonuses.Ships.ContainsKey((int) Buildables.SmallCargo)) {
- cargoBonus = origin.LFBonuses.Ships.GetValueOrDefault((int) Buildables.SmallCargo).Cargo;
+ cargoBonus = origin.LFBonuses.Ships.GetValueOrDefault((int) Buildables.SmallCargo).CargoCapacity;
}
long idealSmallCargo = _helpersService.CalcShipNumberForPayload(payload, Buildables.SmallCargo, userData.researches.HyperspaceTechnology, userData.serverData, cargoBonus, userData.userInfo.Class, userData.serverData.ProbeCargo);
@@ -908,7 +908,7 @@ public async Task TelegramJumpGate(Celestial origin, Coordinate destination, str
} else {
cargoBonus = 0;
if (origin.LFBonuses != null && origin.LFBonuses.Ships != null && origin.LFBonuses.Ships.Count > 0 && origin.LFBonuses.Ships.ContainsKey((int) Buildables.LargeCargo)) {
- cargoBonus = origin.LFBonuses.Ships.GetValueOrDefault((int) Buildables.LargeCargo).Cargo;
+ cargoBonus = origin.LFBonuses.Ships.GetValueOrDefault((int) Buildables.LargeCargo).CargoCapacity;
}
long idealLargeCargo = _helpersService.CalcShipNumberForPayload(payload, Buildables.LargeCargo, userData.researches.HyperspaceTechnology, userData.serverData, cargoBonus, userData.userInfo.Class, userData.serverData.ProbeCargo);
if (idealLargeCargo <= origin.Ships.GetAmount(Buildables.LargeCargo)) {
@@ -1445,6 +1445,17 @@ public async Task TelegramRetireFleet(int fleetId) {
}
_fleetScheduler.RetireFleet(ToRecallFleet);
}
+ public async Task TelegramRetireFleetM(Missions mission) {
+ userData.fleets = await _fleetScheduler.UpdateFleets();
+ List ToRecallFleet = userData.fleets.Where(f => f.Mission == mission).ToList();
+ if (ToRecallFleet.Count == 0) {
+ await SendTelegramMessage($"Unable to recall fleet ! No mission ?");
+ return;
+ }
+ foreach (var fleet in ToRecallFleet) {
+ _fleetScheduler.RetireFleet(fleet);
+ }
+ }
public async Task TelegramMesgAttacker(string message) {
userData.attacks = await _ogameService.GetAttacks();
diff --git a/TBot/Services/TelegramMessenger.cs b/TBot/Services/TelegramMessenger.cs
index effe657e..1815b268 100644
--- a/TBot/Services/TelegramMessenger.cs
+++ b/TBot/Services/TelegramMessenger.cs
@@ -163,7 +163,8 @@ public async Task RemoveTBotInstance(TBotMain instance) {
public async Task SendMessage(string message, ParseMode parseMode = ParseMode.Html, CancellationToken cancellationToken = default) {
try {
isTyping = false;
- await Client.SendTextMessageAsync(
+ //await Client.SendTextMessageAsync(
+ await Client.SendMessage(
chatId: Channel,
text: message,
parseMode: parseMode,
@@ -178,9 +179,10 @@ public Task SendTyping(CancellationToken cancellationToken) {
isTyping = true;
Task.Run(async () => {
while (isTyping) {
- await Client.SendChatActionAsync(
+ //await Client.SendChatActionAsync(
+ await Client.SendChatAction(
chatId: Channel,
- chatAction: ChatAction.Typing,
+ action: ChatAction.Typing,//ChatAction.Typing,
cancellationToken: cancellationToken);
await Task.Delay(3000);
}
@@ -191,7 +193,8 @@ await Client.SendChatActionAsync(
public async Task SendReplyMarkup(string text, IEnumerable> buttons, CancellationToken ct) {
isTyping = false;
var inlineKeyboard = new InlineKeyboardMarkup(buttons);
- await Client.SendTextMessageAsync(
+ //await Client.SendTextMessageAsync(
+ await Client.SendMessage(
chatId: Channel,
text: text,
replyMarkup: inlineKeyboard,
@@ -202,7 +205,9 @@ await Client.SendTextMessageAsync(
public async Task SendMessage(ITelegramBotClient client, Chat chat, string message, ParseMode parseMode = ParseMode.Html) {
try {
isTyping = false;
- await client.SendTextMessageAsync(chat, message, parseMode);
+ //await client.SendTextMessageAsync(chat, message, parseMode);
+ //await client.SendTextMessageAsync(chat, message, null, parseMode);
+ await client.SendMessage(chat, message, parseMode);
} catch (Exception e) {
_logger.WriteLog(LogLevel.Error, LogSender.Tbot, $"Could not send Telegram message: an exception has occurred: {e.Message}");
}
@@ -235,6 +240,7 @@ public async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update,
"/wakeup",
"/build",
"/collect",
+ "/collectall",
"/collectdeut",
"/minexpecargo",
"/stopexpe",
@@ -253,6 +259,7 @@ public async Task HandleUpdateAsync(ITelegramBotClient botClient, Update update,
"/getinfo",
"/celestial",
"/cancel",
+ "/cancelmission",
"/cancelghostsleep",
"/editsettings",
"/spycrash",
@@ -419,12 +426,14 @@ await SendMessage(botClient, message.Chat,
"/spycrash - Create a debris field by crashing a probe on target or automatically selected planet. Format: /spycrash 2:41:9/auto\n" +
"/recall - Enable/disable fleet auto recall. Format: /recall true/false\n" +
"/collect - Collect planets resources to JSON setting celestial\n" +
+ "/collectall - Collect planets resources to JSON setting celestial with no MinimumResources\n" +
"/build - Try to build buildable on each planet. Build max possible if no number value sent /build LightFighter [100]\n" +
"/collectdeut - Collect planets only deut resources -> to JSON repatriate setting celestial\n" +
"/msg - Send a message to current attacker. Format: /msg hello dude\n" +
"/sleep - Stop bot for the specified amount of hours. Format: /sleep 4h3m or 3m50s\n" +
"/wakeup - Wakeup bot\n" +
"/cancel - Cancel fleet with specified ID. Format: /cancel 65656\n" +
+ "/cancelmission - Cancel all fleets with specified mission. Format: /cancel Deploy or other mission\n" +
"/getcelestials - Return the list of your celestials\n" +
"/attacked - check if you're (still) under attack\n" +
"/celestial - Update program current celestial target. Format: /celestial 2:45:8 Moon/Planet\n" +
@@ -789,6 +798,18 @@ await SendMessage(botClient, message.Chat,
return;
+ case "/cancelmission":
+ if (message.Text.Split(' ').Length != 2) {
+ await SendMessage(botClient, message.Chat, "Mission argument required!");
+ return;
+ }
+ arg = message.Text.Split(' ')[1];
+ Missions.TryParse(arg, out mission);
+
+ await currInstance.TelegramRetireFleetM(mission);
+ return;
+
+
case "/cancelghostsleep":
if (message.Text.Split(' ').Length != 1) {
await SendMessage(botClient, message.Chat, "No argument accepted with this command!");
@@ -899,6 +920,16 @@ await SendMessage(botClient, message.Chat,
return;
+ case "/collectall":
+ if (message.Text.Split(' ').Length != 1) {
+ await SendMessage(botClient, message.Chat, "No argument accepted with this command!");
+ return;
+ }
+
+ currInstance.TelegramCollect(true);
+ return;
+
+
case "/collectdeut":
if (message.Text.Split(' ').Length != 2) {
await SendMessage(botClient, message.Chat, "Need minimum deut amount argument /collectdeut 500000");
@@ -1278,7 +1309,8 @@ await SendMessage(botClient, message.Chat,
async Task HandleErrorAsync(ITelegramBotClient botClient, Exception exception, CancellationToken cancellationToken) {
try {
if (exception is ApiRequestException apiRequestException) {
- await botClient.SendTextMessageAsync(Channel, apiRequestException.ToString());
+ //await botClient.SendTextMessageAsync(Channel, apiRequestException.ToString());
+ await botClient.SendMessage(Channel, apiRequestException.ToString());
}
} catch { }
}
@@ -1291,7 +1323,8 @@ public async Task TelegramBot() {
var receiverOptions = new ReceiverOptions {
AllowedUpdates = Array.Empty(),
- ThrowPendingUpdates = true
+ DropPendingUpdates = false,
+ //ThrowPendingUpdates = true
};
receivingTask = Client.ReceiveAsync(HandleUpdateAsync, HandleErrorAsync, receiverOptions, ct);
diff --git a/TBot/Services/UserData.cs b/TBot/Services/UserData.cs
index 228a821d..2330f71f 100644
--- a/TBot/Services/UserData.cs
+++ b/TBot/Services/UserData.cs
@@ -9,31 +9,33 @@
namespace Tbot.Services {
- // Data required by TBotMain instances
- public class UserData {
- public Server serverInfo = new();
- public ServerData serverData;
- public UserInfo userInfo;
- public AllianceClass allianceClass;
- public List celestials;
- public List fleets;
- public List attacks;
- public Slots slots;
- public Researches researches;
+ public class UserData {
+ public Server serverInfo = new();
+ public ServerData serverData;
+ public UserInfo userInfo;
+ public AllianceClass allianceClass;
+ public List celestials;
+ public List fleets;
+ public List attacks;
+ public Slots slots;
+ public Researches researches;
- public List scheduledFleets;
- public List farmTargets;
- public Dictionary discoveryBlackList;
- public float lastDOIR;
- public float nextDOIR;
- public Staff staff;
- public bool isSleeping = false;
- }
+ public List scheduledFleets;
+ public List farmTargets;
+ public Dictionary discoveryBlackList;
+ public float lastDOIR;
+ public float nextDOIR;
+ public Staff staff;
+ public bool isSleeping = false;
- // Data used by TelegramMessenger binded to TBotMain
- public class TelegramUserData {
- public Celestial CurrentCelestial; // Willingly left to null
- public Celestial CurrentCelestialToSave; // Willingly left to null
- public Missions Mission = Missions.None;
- }
-}
+ public int autoFarmLastGalaxy = 0;
+ public int autoFarmLastSystem = 0;
+ public int autoFarmLastRangeIndex = 0;
+ }
+
+ public class TelegramUserData {
+ public Celestial CurrentCelestial;
+ public Celestial CurrentCelestialToSave;
+ public Missions Mission = Missions.None;
+ }
+}
\ No newline at end of file
diff --git a/TBot/TBot.csproj b/TBot/TBot.csproj
index b6845c3b..3418f260 100644
--- a/TBot/TBot.csproj
+++ b/TBot/TBot.csproj
@@ -2,18 +2,18 @@
Exe
- net6.0
+ net9.0
disable
TBot
- 0.3.4
+ 3.3.9
Tbot.Program
AnyCPU;x64;x86
- 2023 © TBot
+ 2026 © TBot
https://licenses.nuget.org/MIT
https://www.ogame-tbot.net
https://github.com/ogame-tbot/TBot
- 0.3.4.0
- 0.3.4.0
+ 3.3.9
+ 3.3.9
OGame Bot
False
ELK-Lab.pfx
@@ -42,12 +42,12 @@
-
-
+
+
-
-
-
+
+
+
diff --git a/TBot/Workers/AutoDiscoveryWorker.cs b/TBot/Workers/AutoDiscoveryWorker.cs
index ba48bdca..e4b2c261 100644
--- a/TBot/Workers/AutoDiscoveryWorker.cs
+++ b/TBot/Workers/AutoDiscoveryWorker.cs
@@ -21,6 +21,21 @@ public class AutoDiscoveryWorker : WorkerBase {
private readonly IFleetScheduler _fleetScheduler;
private readonly ICalculationService _calculationService;
private readonly ITBotOgamedBridge _tbotOgameBridge;
+
+ private sealed class OriginCursor {
+ public int System;
+ public int NextPosition;
+ }
+ private readonly Dictionary _originCursors = new();
+
+ private sealed class DiscoveryRunContext {
+ public int FleetsToSend;
+ public bool Stop;
+ public int Skips;
+ public int GlobalAttempts;
+ public int MaxGlobalAttempts;
+ }
+
public AutoDiscoveryWorker(ITBotMain parentInstance,
IOgameService ogameService,
IFleetScheduler fleetScheduler,
@@ -33,154 +48,379 @@ public AutoDiscoveryWorker(ITBotMain parentInstance,
_tbotOgameBridge = tbotOgameBridge;
}
+ private sealed class CoordinateComparer : IEqualityComparer {
+ public bool Equals(Coordinate x, Coordinate y) {
+ if (ReferenceEquals(x, y)) return true;
+ if (x is null || y is null) return false;
+ return x.Galaxy == y.Galaxy && x.System == y.System && x.Position == y.Position && x.Type == y.Type;
+ }
+
+ public int GetHashCode(Coordinate obj) {
+ if (obj is null) return 0;
+ unchecked {
+ int hash = 17;
+ hash = hash * 31 + obj.Galaxy.GetHashCode();
+ hash = hash * 31 + obj.System.GetHashCode();
+ hash = hash * 31 + obj.Position.GetHashCode();
+ hash = hash * 31 + obj.Type.GetHashCode();
+ return hash;
+ }
+ }
+ }
+
+ private static readonly IEqualityComparer _coordinateComparer = new CoordinateComparer();
+
+ private void EnsureBlacklistInitialized() {
+ if (_tbotInstance?.UserData == null) return;
+
+ if (_tbotInstance.UserData.discoveryBlackList == null) {
+ _tbotInstance.UserData.discoveryBlackList = new Dictionary(_coordinateComparer);
+ return;
+ }
+
+ if (!ReferenceEquals(_tbotInstance.UserData.discoveryBlackList.Comparer, _coordinateComparer)) {
+ var old = _tbotInstance.UserData.discoveryBlackList;
+ var fresh = new Dictionary(_coordinateComparer);
+ foreach (var kv in old) {
+
+ if (fresh.TryGetValue(kv.Key, out var existing)) {
+ if (kv.Value > existing) fresh[kv.Key] = kv.Value;
+ } else {
+ fresh[kv.Key] = kv.Value;
+ }
+ }
+ _tbotInstance.UserData.discoveryBlackList = fresh;
+ }
+ }
+
+ private void CleanupExpiredBlacklistAll(DateTime now) {
+ if (_tbotInstance?.UserData?.discoveryBlackList == null) return;
+ var toRemove = _tbotInstance.UserData.discoveryBlackList
+ .Where(kv => kv.Value <= now)
+ .Select(kv => kv.Key)
+ .ToList();
+ foreach (var k in toRemove)
+ _tbotInstance.UserData.discoveryBlackList.Remove(k);
+ }
+
+ private bool IsBlacklistedAndActive(Coordinate c, DateTime now) {
+ if (_tbotInstance?.UserData?.discoveryBlackList == null) return false;
+ return _tbotInstance.UserData.discoveryBlackList.TryGetValue(c, out var until) && until > now;
+ }
+
+ private void UpsertBlacklist(Coordinate c, DateTime until) {
+ if (_tbotInstance?.UserData?.discoveryBlackList == null) return;
+ _tbotInstance.UserData.discoveryBlackList[c] = until;
+ }
+
+ private List GetConfiguredOrigins(List celestials) {
+ var result = new List();
+ if (celestials == null || celestials.Count == 0) return result;
+
+ var originsCfg = _tbotInstance?.InstanceSettings?.AutoDiscovery?.Origin;
+ if (originsCfg == null) return result;
+
+ foreach (var o in originsCfg) {
+ int galaxy = Convert.ToInt32(o.Galaxy);
+ int system = Convert.ToInt32(o.System);
+ int position = Convert.ToInt32(o.Position);
+ string typeStr = Convert.ToString(o.Type) ?? "Planet";
+
+ Celestials wantedType = Celestials.Planet;
+ if (Enum.TryParse(typeStr, true, out Celestials parsed))
+ wantedType = parsed;
+
+ var found = celestials.FirstOrDefault(c =>
+ c?.Coordinate != null &&
+ c.Coordinate.Galaxy == galaxy &&
+ c.Coordinate.System == system &&
+ c.Coordinate.Position == position &&
+ c.Coordinate.Type == wantedType
+ );
+
+ if (found != null)
+ result.Add(found);
+ else
+ _tbotInstance.log(LogLevel.Warning, LogSender.AutoDiscovery,
+ $"AutoDiscovery origin not found in user celestials: {galaxy}:{system}:{position} ({typeStr})");
+ }
+
+ return result;
+ }
+
+ private string OriginKey(Celestial origin) {
+
+ return $"{origin.Coordinate.Galaxy}:{origin.Coordinate.System}:{origin.Coordinate.Position}:{origin.Coordinate.Type}";
+ }
+
+ private OriginCursor GetOrInitCursor(Celestial origin) {
+ var key = OriginKey(origin);
+ if (!_originCursors.TryGetValue(key, out var cursor) || cursor == null) {
+ cursor = new OriginCursor {
+ System = origin.Coordinate.System,
+ NextPosition = 1
+ };
+ _originCursors[key] = cursor;
+ }
+
+ int maxSystem = _tbotInstance.UserData.serverData.Systems;
+ if (cursor.System < 1 || cursor.System > maxSystem) cursor.System = origin.Coordinate.System;
+ if (cursor.NextPosition < 1 || cursor.NextPosition > 15) cursor.NextPosition = 1;
+
+ return cursor;
+ }
+
+ private int AdvanceSystem(int system) {
+ int maxSystem = _tbotInstance.UserData.serverData.Systems;
+ system++;
+ if (system > maxSystem) system = 1;
+ return system;
+ }
+
+ private async Task ProcessOneSystemForOrigin(
+ Celestial origin,
+ OriginCursor cursor,
+ int discoveries,
+ DiscoveryRunContext ctx) {
+
+ if (origin?.Coordinate == null) return discoveries;
+ if (discoveries <= 0 || ctx.FleetsToSend <= 0 || ctx.Stop) return discoveries;
+
+ int systemToDo = cursor.System;
+
+ DateTime now = await _tbotOgameBridge.GetDateTime();
+ int resumeNextPos = cursor.NextPosition;
+ for (int pos = cursor.NextPosition; pos <= 15; pos++) {
+ resumeNextPos = pos;
+
+ if (discoveries <= 0 || ctx.FleetsToSend <= 0 || ctx.Stop) break;
+ if (_tbotInstance.UserData.slots.Free <= (int)_tbotInstance.InstanceSettings.General.SlotsToLeaveFree) { ctx.Stop = true; break; }
+
+ if (++ctx.GlobalAttempts > ctx.MaxGlobalAttempts) { ctx.Stop = true; break; }
+
+ var dest = new Coordinate {
+ Galaxy = origin.Coordinate.Galaxy,
+ System = systemToDo,
+ Position = pos,
+ Type = Celestials.Planet
+ };
+
+ if (IsBlacklistedAndActive(dest, now)) { ctx.Skips++; continue; }
+
+ var ok = await _ogameService.SendDiscovery(origin, dest);
+ if (!ok) {
+ DoLog(LogLevel.Warning, $"Failed to send discovery fleet to {dest} from {origin}.");
+ UpsertBlacklist(dest, now.AddDays(7));
+ } else {
+ DoLog(LogLevel.Information, $"Discovery fleet sent to {dest} from {origin}.");
+ UpsertBlacklist(dest, now.AddDays(7));
+ discoveries--;
+ ctx.FleetsToSend--;
+ }
+
+ resumeNextPos = pos + 1;
+
+ _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
+ }
+
+ if (!ctx.Stop && resumeNextPos > 15 && discoveries > 0 && ctx.FleetsToSend > 0 &&
+ _tbotInstance.UserData.slots.Free > (int)_tbotInstance.InstanceSettings.General.SlotsToLeaveFree) {
+
+ cursor.System = AdvanceSystem(systemToDo);
+ cursor.NextPosition = 1;
+ } else {
+
+ if (resumeNextPos < 1) resumeNextPos = 1;
+ if (resumeNextPos > 15) resumeNextPos = 15;
+ cursor.NextPosition = resumeNextPos;
+ }
+
+ return discoveries;
+ }
+
+
+ private long CalcFallbackIntervalMs() {
+ return RandomizeHelper.CalcRandomInterval(
+ (int)_tbotInstance.InstanceSettings.AutoDiscovery.CheckIntervalMin,
+ (int)_tbotInstance.InstanceSettings.AutoDiscovery.CheckIntervalMax
+ );
+ }
+
+ private long CalcNextIntervalFromDiscoveryFleetsOrFallback() {
+ var discFleet = _tbotInstance.UserData.fleets
+ .Where(f => f.Mission == Missions.Discovery && f.BackIn.HasValue)
+ .OrderBy(f => f.BackIn.Value)
+ .FirstOrDefault();
+
+ if (discFleet == null) return CalcFallbackIntervalMs();
+
+ long backInSec = discFleet.BackIn ?? 0;
+ long interval = backInSec * 1000 + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ if (interval <= 0) interval = RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ return interval;
+ }
+
protected override async Task Execute() {
bool delay = false;
bool stop = false;
- int failures = 0;
int skips = 0;
- var rand = new Random();
+
try {
- if (_tbotInstance.UserData.discoveryBlackList == null) {
- _tbotInstance.UserData.discoveryBlackList = new Dictionary();
+ DoLog(LogLevel.Information, $"Starting AutoDiscovery...");
+
+ EnsureBlacklistInitialized();
+
+ if (_tbotInstance.UserData.isSleeping) {
+ stop = true;
+ return;
}
- if (!_tbotInstance.UserData.isSleeping) {
- DoLog(LogLevel.Information, $"Starting AutoDiscovery...");
- _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
- _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
-
- Celestial origin = _tbotInstance.UserData.celestials
- .Unique()
- .Where(c => c.Coordinate.Galaxy == (int) _tbotInstance.InstanceSettings.AutoDiscovery.Origin.Galaxy)
- .Where(c => c.Coordinate.System == (int) _tbotInstance.InstanceSettings.AutoDiscovery.Origin.System)
- .Where(c => c.Coordinate.Position == (int) _tbotInstance.InstanceSettings.AutoDiscovery.Origin.Position)
- .Where(c => c.Coordinate.Type == Enum.Parse((string) _tbotInstance.InstanceSettings.AutoDiscovery.Origin.Type))
- .SingleOrDefault() ?? new() { ID = 0 };
- if (origin.ID == 0) {
+
+ if ((bool)_tbotInstance.InstanceSettings.SleepMode.Active) {
+ DateTime.TryParse((string)_tbotInstance.InstanceSettings.SleepMode.GoToSleep, out DateTime goToSleep);
+ DateTime.TryParse((string)_tbotInstance.InstanceSettings.SleepMode.WakeUp, out DateTime wakeUp);
+ DateTime timeSleep = await _tbotOgameBridge.GetDateTime();
+ if (GeneralHelper.ShouldSleep(timeSleep, goToSleep, wakeUp)) {
+ DoLog(LogLevel.Warning, "Unable to send discovery fleet: bed time has passed");
stop = true;
- DoLog(LogLevel.Warning, "Unable to parse AutoDiscovery origin");
return;
}
-
- if ((bool) _tbotInstance.InstanceSettings.SleepMode.Active) {
- DateTime.TryParse((string) _tbotInstance.InstanceSettings.SleepMode.GoToSleep, out DateTime goToSleep);
- DateTime.TryParse((string) _tbotInstance.InstanceSettings.SleepMode.WakeUp, out DateTime wakeUp);
- DateTime time = await _tbotOgameBridge.GetDateTime();
- if (GeneralHelper.ShouldSleep(time, goToSleep, wakeUp)) {
- DoLog(LogLevel.Warning, "Unable to send discovery fleet: bed time has passed");
- stop = true;
- return;
- }
- }
+ }
- List possibleDestinations = new();
- for (int i = 1; i <= _tbotInstance.UserData.serverData.Systems; i++) {
- for (int j = 1; j <= 15; j++) {
- possibleDestinations.Add(new Coordinate() {
- Galaxy = origin.Coordinate.Galaxy,
- System = i,
- Position = j
- });
- }
- }
- possibleDestinations = possibleDestinations
- .Shuffle()
- .OrderBy(c => _calculationService.CalcDistance(origin.Coordinate, c, _tbotInstance.UserData.serverData))
- .ToList();
-
- while (possibleDestinations.Count > 0 && _tbotInstance.UserData.fleets.Where(s => s.Mission == Missions.Discovery).Count() < (int) _tbotInstance.InstanceSettings.AutoDiscovery.MaxSlots && _tbotInstance.UserData.slots.Free > (int) _tbotInstance.InstanceSettings.General.SlotsToLeaveFree) {
- Coordinate dest = possibleDestinations.First();
- possibleDestinations.Remove(dest);
-
- Coordinate blacklistedCoord = _tbotInstance.UserData.discoveryBlackList.Keys
- .Where(c => c.Galaxy == dest.Galaxy)
- .Where(c => c.System == dest.System)
- .Where(c => c.Position == dest.Position)
- .SingleOrDefault() ?? null;
- if (blacklistedCoord != null) {
- if (_tbotInstance.UserData.discoveryBlackList.Single(d => d.Key.Galaxy == dest.Galaxy && d.Key.System == dest.System && d.Key.Position == dest.Position).Value > DateTime.Now) {
- //DoLog(LogLevel.Information, $"Skipping {dest.ToString()} because it's blacklisted until {_tbotInstance.UserData.discoveryBlackList[blacklistedCoord].ToString()}");
- skips++;
- if (skips >= _tbotInstance.UserData.serverData.Systems * 15) {
- DoLog(LogLevel.Information, $"Galaxy depleted: stopping");
- stop = true;
- break;
- } else {
- continue;
- }
- } else {
- _tbotInstance.UserData.discoveryBlackList.Remove(blacklistedCoord);
- }
- }
-
- origin = await _tbotOgameBridge.UpdatePlanet(origin, UpdateTypes.Resources);
- if (!origin.Resources.IsEnoughFor(new Resources { Metal = 5000, Crystal = 1000, Deuterium = 500 })) {
- DoLog(LogLevel.Warning, $"Failed to send discovery fleet from {origin.ToString()}: not enough resources.");
- return;
- }
-
- var result = await _ogameService.SendDiscovery(origin, dest);
- if (!result) {
- failures++;
- DoLog(LogLevel.Warning, $"Failed to send discovery fleet to {dest.ToString()} from {origin.ToString()}.");
- _tbotInstance.UserData.discoveryBlackList.Add(dest, DateTime.Now.AddDays(1));
- }
- else {
- DoLog(LogLevel.Information, $"Sent discovery fleet to {dest.ToString()} from {origin.ToString()}.");
- _tbotInstance.UserData.discoveryBlackList.Add(dest, DateTime.Now.AddDays(7));
- }
-
- if (failures >= (int) _tbotInstance.InstanceSettings.AutoDiscovery.MaxFailures) {
- DoLog(LogLevel.Warning, $"Max failures reached");
- break;
- }
-
- _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
- _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
- if (_tbotInstance.UserData.slots.Free <= 1) {
- DoLog(LogLevel.Information, $"AutoDiscoveryWorker: No slots left, dealying");
- delay = true;
- break;
- }
- }
+ DateTime now = await _tbotOgameBridge.GetDateTime();
+ CleanupExpiredBlacklistAll(now);
+
+ _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
+ _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
+
+ List rankSlotsPriority = new() {
+ new RankSlotsPriority(Feature.BrainAutoMine, (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.Brain,
+ ((bool)_tbotInstance.InstanceSettings.Brain.Active && (bool)_tbotInstance.InstanceSettings.Brain.Transports.Active &&
+ ((bool)_tbotInstance.InstanceSettings.Brain.AutoMine.Active || (bool)_tbotInstance.InstanceSettings.Brain.AutoResearch.Active ||
+ (bool)_tbotInstance.InstanceSettings.Brain.LifeformAutoMine.Active || (bool)_tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.Active)),
+ (int)_tbotInstance.InstanceSettings.Brain.Transports.MaxSlots,
+ (int)_tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Transport)
+ ),
+ new RankSlotsPriority(Feature.Expeditions, (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.Expeditions,
+ (bool)_tbotInstance.InstanceSettings.Expeditions.Active,
+ (int)_tbotInstance.UserData.slots.ExpTotal,
+ (int)_tbotInstance.UserData.slots.ExpInUse
+ ),
+ new RankSlotsPriority(Feature.AutoFarm, (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoFarm,
+ (bool)_tbotInstance.InstanceSettings.AutoFarm.Active,
+ (int)_tbotInstance.InstanceSettings.AutoFarm.MaxSlots,
+ (int)_tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Attack)
+ ),
+ new RankSlotsPriority(Feature.Colonize, (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.Colonize,
+ (bool)_tbotInstance.InstanceSettings.AutoColonize.Active,
+ (bool)_tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.Active ? (int)_tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.MaxSlots : 1,
+ (int)_tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Colonize)
+ ),
+ new RankSlotsPriority(Feature.AutoDiscovery, (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoDiscovery,
+ (bool)_tbotInstance.InstanceSettings.AutoDiscovery.Active,
+ (int)_tbotInstance.InstanceSettings.AutoDiscovery.MaxSlots,
+ (int)_tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Discovery)
+ ),
+ new RankSlotsPriority(Feature.Harvest, (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoHarvest,
+ (bool)_tbotInstance.InstanceSettings.AutoHarvest.Active,
+ (int)_tbotInstance.InstanceSettings.AutoHarvest.MaxSlots,
+ (int)_tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Harvest)
+ )
+ };
+
+ int fleetsToSend = _calculationService.CalcSlotsPriority(
+ Feature.AutoDiscovery, rankSlotsPriority,
+ _tbotInstance.UserData.slots, _tbotInstance.UserData.fleets,
+ (int)_tbotInstance.InstanceSettings.General.SlotsToLeaveFree
+ );
+
+ if (fleetsToSend <= 0) {
+ delay = true;
+ return;
}
- else {
+
+ var celestials = await _ogameService.GetCelestials();
+ var origins = GetConfiguredOrigins(celestials);
+
+ if (origins == null || origins.Count == 0) {
stop = true;
+ DoLog(LogLevel.Warning, "Unable to parse AutoDiscovery origin.");
+ return;
}
- } catch (Exception ex) {
- DoLog(LogLevel.Error, "AutoDiscovery exception");
- DoLog(LogLevel.Warning, ex.ToString());
- }
- finally {
+
+ var ctx = new DiscoveryRunContext {
+ FleetsToSend = fleetsToSend,
+ Stop = false,
+ Skips = 0,
+ GlobalAttempts = 0,
+ MaxGlobalAttempts = 600
+ };
+
+ foreach (var origin in origins) {
+ if (ctx.Stop) break;
+ if (ctx.FleetsToSend <= 0) break;
+ if (origin?.Coordinate == null) continue;
+
+ int discoveries = await _ogameService.GetAvailableDiscoveries(origin);
+ if (discoveries <= 0) {
+ DoLog(LogLevel.Information, $"No discoveries available from {origin} right now.");
+ continue;
+ }
+
+ var cursor = GetOrInitCursor(origin);
+ DoLog(LogLevel.Information, $"Origin {origin} cursor system={cursor.System}, nextPos={cursor.NextPosition} (discoveries={discoveries})");
+
+ discoveries = await ProcessOneSystemForOrigin(origin, cursor, discoveries, ctx);
+
+ fleetsToSend = ctx.FleetsToSend;
+ stop = ctx.Stop;
+ skips = ctx.Skips;
+ }
+
+ if (skips > 0)
+ DoLog(LogLevel.Information, $"{skips} positions skipped (blacklisted)");
+
+ _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
+
+ long interval = CalcNextIntervalFromDiscoveryFleetsOrFallback();
+ DateTime time = await _tbotOgameBridge.GetDateTime();
+ var nextTime = time.AddMilliseconds(interval);
+
+ ChangeWorkerPeriod(interval);
+ DoLog(LogLevel.Information, $"Next AutoDiscovery check at {nextTime.ToString()}");
+ await _tbotOgameBridge.CheckCelestials();
+ } catch (Exception e) {
+ DoLog(LogLevel.Error, $"An error occured: {e.Message}");
+ DoLog(LogLevel.Debug, e.StackTrace);
+ } finally {
if (stop) {
DoLog(LogLevel.Information, $"Stopping feature.");
await EndExecution();
- } else {
- long interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.AutoDiscovery.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.AutoDiscovery.CheckIntervalMax);
- if (delay) {
- DoLog(LogLevel.Information, $"Delaying...");
- _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
- interval = (_tbotInstance.UserData.fleets.OrderBy(f => f.BackIn).First().BackIn ?? 0) * 1000 + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
- }
- if (interval <= 0)
- interval = RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
- var time = await _tbotOgameBridge.GetDateTime();
- var newTime = time.AddMilliseconds(interval);
- ChangeWorkerPeriod(interval);
- DoLog(LogLevel.Information, $"Next AutoDiscovery check at {newTime.ToString()}");
}
+
+ if (delay) {
+ DoLog(LogLevel.Information, $"Delaying...");
+ var timeDelay = await _tbotOgameBridge.GetDateTime();
+ _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
+
+ long intervalDelay = CalcNextIntervalFromDiscoveryFleetsOrFallback();
+ var newTimeDelay = timeDelay.AddMilliseconds(intervalDelay);
+
+ ChangeWorkerPeriod(intervalDelay);
+ DoLog(LogLevel.Information, $"Next AutoDiscovery check at {newTimeDelay.ToString()}");
+ }
+
await _tbotOgameBridge.CheckCelestials();
- }
+ }
}
public override bool IsWorkerEnabledBySettings() {
try {
- return
- (bool) _tbotInstance.InstanceSettings.AutoDiscovery.Active
- ;
+ return (bool)_tbotInstance.InstanceSettings.AutoDiscovery.Active;
} catch (Exception) {
return false;
}
}
+
public override string GetWorkerName() {
return "AutoDiscovery";
}
diff --git a/TBot/Workers/AutoFarmWorker.cs b/TBot/Workers/AutoFarmWorker.cs
index 2aff82c2..1f544e0c 100644
--- a/TBot/Workers/AutoFarmWorker.cs
+++ b/TBot/Workers/AutoFarmWorker.cs
@@ -21,6 +21,8 @@ public class AutoFarmWorker : WorkerBase {
private readonly IFleetScheduler _fleetScheduler;
private readonly ICalculationService _calculationService;
private readonly ITBotOgamedBridge _tbotOgameBridge;
+ private readonly AutoFarmBlacklist _blacklist;
+ private readonly AutoFarmSuccessfulTargets _successfulTargets;
public AutoFarmWorker(ITBotMain parentInstance,
IOgameService ogameService,
IFleetScheduler fleetScheduler,
@@ -31,6 +33,10 @@ public AutoFarmWorker(ITBotMain parentInstance,
_fleetScheduler = fleetScheduler;
_calculationService = calculationService;
_tbotOgameBridge = tbotOgameBridge;
+ string blacklistPath = $"autofarm_blacklist_{_tbotInstance.InstanceAlias}.json";
+ _blacklist = new AutoFarmBlacklist(blacklistPath);
+ string successfulPath = $"autofarm_successful_{_tbotInstance.InstanceAlias}.json";
+ _successfulTargets = new AutoFarmSuccessfulTargets(successfulPath);
}
public override bool IsWorkerEnabledBySettings() {
try {
@@ -90,11 +96,61 @@ private bool ShouldExcludeSystem(int galaxy, int system) {
}
private async Task> GetScannedTargetsFromGalaxy(int galaxy, int system) {
- var galaxyInfo = await _ogameService.GetGalaxyInfo(galaxy, system);
+ GalaxyInfo galaxyInfo = null;
+ int retryCount = 0;
+ int maxRetries = 5;
+
+ while (retryCount < maxRetries) {
+ try {
+ galaxyInfo = await _ogameService.GetGalaxyInfo(galaxy, system);
+ break;
+ } catch (Exception e) when (e.Message.Contains("system must be within") || e.Message.Contains("503") || e.Message.Contains("Service Unavailable")) {
+ retryCount++;
+ int waitSeconds = retryCount * 3;
+
+ _tbotInstance.log(LogLevel.Debug, LogSender.AutoFarm, $"Exception details: {e.Message}");
+ _tbotInstance.log(LogLevel.Warning, LogSender.AutoFarm, $"Galaxy scan failed. Retry {retryCount}/{maxRetries} in {waitSeconds}s...");
+
+ if (retryCount < maxRetries) {
+ await Task.Delay(waitSeconds * 1000);
+
+ try {
+ _tbotInstance.UserData.serverData = await _ogameService.GetServerData();
+ _tbotInstance.log(LogLevel.Debug, LogSender.AutoFarm, $"ServerData after refresh: Systems={_tbotInstance.UserData.serverData.Systems}");
+ } catch (Exception serverDataEx) {
+ _tbotInstance.log(LogLevel.Error, LogSender.AutoFarm, $"Failed to refresh ServerData: {serverDataEx.Message}");
+ }
+
+ if (retryCount == 4 && _tbotInstance.UserData.serverData.Systems == 0) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.AutoFarm, $"ServerData broken. Root cause: {e.Message}. Restarting ogamed...");
+
+ try {
+ _ogameService.KillOgamedExecutable();
+ await Task.Delay(5000);
+ _ogameService.RerunOgamed();
+ await Task.Delay(10000);
+
+ try {
+ _tbotInstance.UserData.serverData = await _ogameService.GetServerData();
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"ServerData after restart: Systems={_tbotInstance.UserData.serverData.Systems}");
+ } catch (Exception restartEx) {
+ _tbotInstance.log(LogLevel.Error, LogSender.AutoFarm, $"Failed to get ServerData after ogamed restart: {restartEx.Message}");
+ }
+ } catch (Exception ogamedEx) {
+ _tbotInstance.log(LogLevel.Error, LogSender.AutoFarm, $"Failed to restart ogamed: {ogamedEx.Message}");
+ _tbotInstance.log(LogLevel.Warning, LogSender.AutoFarm, $"Stacktrace: {ogamedEx.StackTrace}");
+ }
+ }
+ } else {
+ _tbotInstance.log(LogLevel.Error, LogSender.AutoFarm, $"Galaxy scan failed after {maxRetries} retries.");
+ throw;
+ }
+ }
+ }
+
var planets = galaxyInfo.Planets.Where(p => p != null && p.Inactive && !p.Administrator && !p.Banned && !p.Vacation);
List scannedTargets = planets.Cast().ToList();
await _fleetScheduler.UpdateFleets();
- //Remove all targets that are currently under attack (necessary if bot or instance is restarted)
scannedTargets.RemoveAll(t => _tbotInstance.UserData.fleets.Any(f => f.Destination.IsSame(t.Coordinate) && f.Mission == Missions.Attack));
return scannedTargets;
}
@@ -151,8 +207,6 @@ private void AddMoons(List scannedTargets) {
private FarmTarget CheckDuplicatesAndGetExisting(Celestial planet) {
var exists = _tbotInstance.UserData.farmTargets.Where(t => t != null && t.Celestial.HasCoords(planet.Coordinate)).ToList();
if (exists.Count() > 1) {
- // It can exist if was processed in a previous execution
- //Remove all except the first to be able to continue
var firstExisting = exists.First();
_tbotInstance.UserData.farmTargets.RemoveAll(c => c.Celestial.HasCoords(planet.Coordinate));
_tbotInstance.UserData.farmTargets.Add(firstExisting);
@@ -165,27 +219,32 @@ private FarmTarget CheckDuplicatesAndGetExisting(Celestial planet) {
}
private FarmTarget GetFarmTarget(Celestial planet) {
- // Check if planet with coordinates exists already in _tbotInstance.UserData.farmTargets list.
+ bool blacklistActive = SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "Blacklist") &&
+ SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm.Blacklist, "Active") &&
+ (bool) _tbotInstance.InstanceSettings.AutoFarm.Blacklist.Active;
+
+ if (blacklistActive && _blacklist.IsBlacklisted(planet.Coordinate)) {
+ var blacklistedTarget = _blacklist.GetBlacklistedTarget(planet.Coordinate);
+ _tbotInstance.log(LogLevel.Debug, LogSender.AutoFarm, $"Target {planet.ToString()} is blacklisted (Reason: {blacklistedTarget.Reason}). Skipping...");
+ return null;
+ }
+
var target = CheckDuplicatesAndGetExisting(planet);
if (target == null) {
- // Does not exist, add to _tbotInstance.UserData.farmTargets list, set state to probes pending.
target = new(planet, FarmState.ProbesPending);
_tbotInstance.UserData.farmTargets.Add(target);
} else {
- // Already exists, update _tbotInstance.UserData.farmTargets list with updated planet.
target.Celestial = planet;
if (target.State == FarmState.Idle)
target.State = FarmState.ProbesPending;
- // If target marked not suitable based on a non-expired espionage report, skip probing.
if (target.State == FarmState.NotSuitable && target.Report != null) {
_tbotInstance.log(LogLevel.Debug, LogSender.AutoFarm, $"Target {planet.ToString()} marked as Not Suitable. Skipping...");
return null;
}
- // If probes are already sent or if an attack is pending, skip probing.
if (target.State == FarmState.ProbesSent || target.State == FarmState.AttackPending) {
_tbotInstance.log(LogLevel.Debug, LogSender.AutoFarm, $"Target {planet.ToString()} marked as {target.State.ToString()}. Skipping...");
return null;
@@ -226,25 +285,19 @@ private async Task GetBestOrigin(List closestCelesti
int neededProbes,
int slotsToLeaveFree,
int freeSlots) {
- //Set first celestial as default best Origin
SpyOriginResult bestOrigin = new SpyOriginResult(closestCelestials.First(), int.MaxValue, freeSlots);
foreach (var closest in closestCelestials) {
- // Update ships of the current planet
var tempCelestial = await _tbotOgameBridge.UpdatePlanet(closest, UpdateTypes.Ships);
celestialProbes.Remove(closest.ID);
celestialProbes.Add(closest.ID, tempCelestial.Ships.EspionageProbe);
if (celestialProbes[closest.ID] >= neededProbes) {
- //There are enough probes so it's the best origin and we can stop searching
bestOrigin = new SpyOriginResult(closest, freeSlots);
break;
}
- // No probes available in this celestial
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
- // If there are no free slots, update the minimum time to wait for current missions return.
- // If there are no free slots, wait for probes to come back to current celestial.
if (freeSlots <= slotsToLeaveFree) {
var espionageMissions = _calculationService.GetMissionsInProgress(closest.Coordinate, Missions.Spy, _tbotInstance.UserData.fleets);
if (espionageMissions.Any()) {
@@ -263,12 +316,10 @@ private async Task GetBestOrigin(List closestCelesti
}
}
} else {
- //If no bestOrigin detected, the total number of probes is not enough but there are free slots, then calculate if can be built from this celestial
_tbotInstance.log(LogLevel.Warning, LogSender.AutoFarm, $"Cannot spy {target.Celestial.Coordinate.ToString()} from {closest.Coordinate.ToString()}, insufficient probes ({celestialProbes[closest.ID]}/{neededProbes}).");
if (bestOrigin.BackIn < int.MaxValue)
continue;
- //If there is no bestOrigin, check if can be a good origin (it has enough resources to build probes)
tempCelestial = await _tbotOgameBridge.UpdatePlanet(closest, UpdateTypes.Constructions);
if (tempCelestial.Constructions.BuildingID == (int) Buildables.Shipyard || tempCelestial.Constructions.BuildingID == (int) Buildables.NaniteFactory) {
Buildables buildingInProgress = (Buildables) tempCelestial.Constructions.BuildingID;
@@ -315,7 +366,6 @@ private async Task WaitForFreeSlots(int freeSlots, int slotsToLeaveFree) {
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
while (freeSlots <= slotsToLeaveFree) {
- // No slots available, wait for first fleet of any mission type to return.
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
if (_tbotInstance.UserData.fleets.Any()) {
int interval = (int) ((1000 * _tbotInstance.UserData.fleets.OrderBy(fleet => fleet.BackIn).First().BackIn) + RandomizeHelper.CalcRandomInterval(IntervalType.LessThanASecond));
@@ -325,7 +375,7 @@ private async Task WaitForFreeSlots(int freeSlots, int slotsToLeaveFree) {
freeSlots = _tbotInstance.UserData.slots.Free;
} else {
_tbotInstance.log(LogLevel.Error, LogSender.AutoFarm, "Error: No fleet slots available and no fleets returning!");
- throw new Exception("No fleet slots available and no fleets returning!"); //TODO: Create custom exception
+ throw new Exception("No fleet slots available and no fleets returning!");
}
}
return freeSlots;
@@ -333,14 +383,15 @@ private async Task WaitForFreeSlots(int freeSlots, int slotsToLeaveFree) {
protected override async Task Execute() {
bool stop = false;
+ bool stopAfterFullScan = false;
+ bool finishedFullScan = false;
try {
-
_tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, "Running autofarm...");
-
- if ((bool) _tbotInstance.InstanceSettings.AutoFarm.Active) {
- // If not enough slots are free, the farmer cannot run.
+ stopAfterFullScan = SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "StopAfterFullScan")
+ && (bool)_tbotInstance.InstanceSettings.AutoFarm.StopAfterFullScan;
+ if ((bool) _tbotInstance.InstanceSettings.AutoFarm.Active) {
_tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
-
+
int freeSlots = _tbotInstance.UserData.slots.Free;
int slotsToLeaveFree = (int) _tbotInstance.InstanceSettings.AutoFarm.SlotsToLeaveFree;
if (freeSlots <= slotsToLeaveFree) {
@@ -349,47 +400,141 @@ protected override async Task Execute() {
}
try {
- // Prune all reports older than KeepReportFor and all reports of state AttackSent: information no longer actual.
await PruneOldReports();
- // Keep local record of _tbotInstance.UserData.celestials, to be updated by autofarmer itself, to reduce ogamed calls.
var celestialProbes = await GetCelestialProbes();
- // Keep track of number of targets probed.
int numProbed = 0;
- /// Galaxy scanning + target probing.
_tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, "Detecting farm targets...");
bool stopAutoFarm = false;
- foreach (var range in _tbotInstance.InstanceSettings.AutoFarm.ScanRange) {
+ var scanRanges = ((IEnumerable)_tbotInstance.InstanceSettings.AutoFarm.ScanRange).ToList();
+
+ var allScanRanges = ((IEnumerable)_tbotInstance.InstanceSettings.AutoFarm.ScanRange).ToList();
+ int totalSystemsAcrossAllGalaxies = allScanRanges.Sum(r => (int)r.EndSystem - (int)r.StartSystem + 1);
+ int instanceHash = Math.Abs(_tbotInstance.InstanceAlias.GetHashCode());
+ int minSpacing = 499;
+ int numSlotsGlobal = Math.Max(1, totalSystemsAcrossAllGalaxies / minSpacing);
+ int globalSlotIndex = instanceHash % numSlotsGlobal;
+ int globalOffset = globalSlotIndex * minSpacing;
+
+ int targetGalaxy = 1;
+ int targetSystem = 1;
+ int remainingOffset = globalOffset;
+ foreach (var r in allScanRanges) {
+ int systemsInRange = (int)r.EndSystem - (int)r.StartSystem + 1;
+ if (remainingOffset < systemsInRange) {
+ targetGalaxy = (int)r.Galaxy;
+ targetSystem = (int)r.StartSystem + remainingOffset;
+ break;
+ }
+ remainingOffset -= systemsInRange;
+ }
+
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"[GLOBAL SPACING] Instance: {_tbotInstance.InstanceAlias}");
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"[GLOBAL SPACING] Total systems across all galaxies: {totalSystemsAcrossAllGalaxies}");
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"[GLOBAL SPACING] Num slots globally: {numSlotsGlobal}");
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"[GLOBAL SPACING] Global slot index: {globalSlotIndex}");
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"[GLOBAL SPACING] Global offset: {globalOffset} systems");
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"[GLOBAL SPACING] Target Galaxy: {targetGalaxy}, Target System: {targetSystem}");
+
+ var orderedRanges = scanRanges
+ .OrderBy(r => (int)r.Galaxy)
+ .ThenBy(r => (int)r.StartSystem)
+ .ToList();
+
+ if (!orderedRanges.Any()) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.AutoFarm, "No scan ranges match galaxies with planets. Skipping AutoFarm.");
+ stopAutoFarm = true;
+ }
+
+ int startRangeIndex = _tbotInstance.UserData.autoFarmLastRangeIndex;
+ if (startRangeIndex < 0 || startRangeIndex >= orderedRanges.Count) {
+ startRangeIndex = 0;
+ }
+ if (_tbotInstance.UserData.autoFarmLastGalaxy == 0 && _tbotInstance.UserData.autoFarmLastSystem == 0) {
+ int idx = orderedRanges.FindIndex(r => (int)r.Galaxy == targetGalaxy);
+ if (idx >= 0) startRangeIndex = idx;
+ _tbotInstance.UserData.autoFarmLastRangeIndex = startRangeIndex;
+ }
+
+ bool globalOffsetUsed = false;
+
+ for (int rangeIndex = startRangeIndex; rangeIndex < orderedRanges.Count; rangeIndex++) {
if (stopAutoFarm)
break;
- if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "TargetsProbedBeforeAttack") && ((int) _tbotInstance.InstanceSettings.AutoFarm.TargetsProbedBeforeAttack != 0) && numProbed >= (int) _tbotInstance.InstanceSettings.AutoFarm.TargetsProbedBeforeAttack) {
+ if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "TargetsProbedBeforeAttack") && ((int)_tbotInstance.InstanceSettings.AutoFarm.TargetsProbedBeforeAttack != 0) && numProbed >= (int)_tbotInstance.InstanceSettings.AutoFarm.TargetsProbedBeforeAttack) {
break;
}
+ var range = orderedRanges[rangeIndex];
+
int galaxy = (int) range.Galaxy;
- int startSystem = (int) range.StartSystem;
+ int originalStartSystem = (int) range.StartSystem;
int endSystem = (int) range.EndSystem;
-
- // Loop from start to end system.
- for (var system = startSystem; system <= endSystem; system++) {
+ int startSystem = originalStartSystem;
+ bool isRandomStart = false;
+
+ if (_tbotInstance.UserData.autoFarmLastGalaxy == galaxy &&
+ _tbotInstance.UserData.autoFarmLastSystem >= originalStartSystem &&
+ _tbotInstance.UserData.autoFarmLastSystem <= endSystem) {
+
+ startSystem = _tbotInstance.UserData.autoFarmLastSystem;
+ isRandomStart = false;
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm,
+ $"Resuming scan from Galaxy {galaxy} System {startSystem}");
+ }
+ else {
+ startSystem = originalStartSystem;
+ isRandomStart = false;
+
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm,
+ $"[START] Galaxy {galaxy} - Ordered start at system {startSystem}");
+ }
+
+ int systemsToScan = endSystem - originalStartSystem + 1;
+ int scannedCount = 0;
+
+ for (var offset = 0; offset < systemsToScan && scannedCount < systemsToScan; offset++) {
+ int system = startSystem + offset;
+ if (system > endSystem) {
+ break;
+ }
+ scannedCount++;
if (stopAutoFarm)
break;
if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "TargetsProbedBeforeAttack") && ((int) _tbotInstance.InstanceSettings.AutoFarm.TargetsProbedBeforeAttack != 0) && numProbed >= (int) _tbotInstance.InstanceSettings.AutoFarm.TargetsProbedBeforeAttack) {
_tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, "Maximum number of targets to probe reached, proceeding to attack.");
+ int nextSystem = system + 1;
+ if (nextSystem <= endSystem) {
+ _tbotInstance.UserData.autoFarmLastRangeIndex = rangeIndex;
+ _tbotInstance.UserData.autoFarmLastGalaxy = galaxy;
+ _tbotInstance.UserData.autoFarmLastSystem = nextSystem;
+ } else {
+ int nextRangeIndex = rangeIndex + 1;
+ if (nextRangeIndex < orderedRanges.Count) {
+ var nextRange = orderedRanges[nextRangeIndex];
+ _tbotInstance.UserData.autoFarmLastRangeIndex = nextRangeIndex;
+ _tbotInstance.UserData.autoFarmLastGalaxy = (int)nextRange.Galaxy;
+ _tbotInstance.UserData.autoFarmLastSystem = (int)nextRange.StartSystem;
+ } else {
+ _tbotInstance.UserData.autoFarmLastRangeIndex = 0;
+ _tbotInstance.UserData.autoFarmLastGalaxy = 0;
+ _tbotInstance.UserData.autoFarmLastSystem = 0;
+ finishedFullScan = true;
+ }
+ }
+ stopAutoFarm = true;
break;
}
- // Check excluded system.
bool excludeSystem = ShouldExcludeSystem(galaxy, system);
if (excludeSystem)
continue;
var scannedTargets = await GetScannedTargetsFromGalaxy(galaxy, system);
-
_tbotInstance.log(LogLevel.Debug, LogSender.AutoFarm, $"Found {scannedTargets.Count} targets on System {galaxy}:{system}");
if (!scannedTargets.Any())
@@ -399,11 +544,9 @@ protected override async Task Execute() {
AddMoons(scannedTargets);
}
- // Add each planet that has inactive status to _tbotInstance.UserData.farmTargets.
foreach (Celestial planet in scannedTargets) {
if (stopAutoFarm)
break;
- // Check if target is below set minimum rank.
if (!IsTargetInMinimumRank(planet, scannedTargets)) {
continue;
}
@@ -414,21 +557,24 @@ protected override async Task Execute() {
break;
}
- // Check excluded planet.
if (ShouldExcludePlanet(planet))
continue;
- // Manage existing Target or generate a new one. If should not be processed returns null
var target = GetFarmTarget(planet);
if (target == null)
continue;
- // Send spy probe from closest celestial with available probes to the target.
List tempCelestials = (_tbotInstance.InstanceSettings.AutoFarm.Origin.Length > 0) ? _calculationService.ParseCelestialsList(_tbotInstance.InstanceSettings.AutoFarm.Origin, _tbotInstance.UserData.celestials) : _tbotInstance.UserData.celestials;
+
List closestCelestials = tempCelestials
.OrderByDescending(planet => planet.Coordinate.Type == Celestials.Moon)
.OrderBy(c => _calculationService.CalcDistance(c.Coordinate, target.Celestial.Coordinate, _tbotInstance.UserData.serverData)).ToList();
+ if (!closestCelestials.Any()) {
+ _tbotInstance.log(LogLevel.Debug, LogSender.AutoFarm, $"No origin celestials available. Skipping target {target.Celestial.ToString()}");
+ continue;
+ }
+
int neededProbes = GetNeededProbes(target);
@@ -437,7 +583,6 @@ protected override async Task Execute() {
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
var probesInMission = _tbotInstance.UserData.fleets.Select(c => c.Ships).Sum(c => c.EspionageProbe);
- //Calculate best origin
var bestOrigin = await GetBestOrigin(closestCelestials,
celestialProbes,
target,
@@ -458,7 +603,6 @@ protected override async Task Execute() {
continue;
}
- // If local record indicate not enough espionage probes are available, update record to make sure this is correct.
if (celestialProbes[bestOrigin.Origin.ID] < neededProbes) {
var tempCelestial = await _tbotOgameBridge.UpdatePlanet(bestOrigin.Origin, UpdateTypes.Ships);
celestialProbes.Remove(bestOrigin.Origin.ID);
@@ -468,7 +612,7 @@ protected override async Task Execute() {
if (celestialProbes[bestOrigin.Origin.ID] < neededProbes) {
_tbotInstance.log(LogLevel.Warning, LogSender.AutoFarm, $"Insufficient probes ({celestialProbes[bestOrigin.Origin.ID]}/{neededProbes}).");
if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "BuildProbes") && _tbotInstance.InstanceSettings.AutoFarm.BuildProbes == true) {
- //Check if probes can be built
+
var tempCelestial = await _tbotOgameBridge.UpdatePlanet(bestOrigin.Origin, UpdateTypes.Constructions);
if (tempCelestial.Constructions.BuildingID == (int) Buildables.Shipyard || tempCelestial.Constructions.BuildingID == (int) Buildables.NaniteFactory) {
Buildables buildingInProgress = (Buildables) tempCelestial.Constructions.BuildingID;
@@ -529,6 +673,17 @@ protected override async Task Execute() {
_tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
freeSlots = _tbotInstance.UserData.slots.Free;
freeSlots = await WaitForFreeSlots(freeSlots, slotsToLeaveFree);
+
+ bestOrigin.Origin = await _tbotOgameBridge.UpdatePlanet(bestOrigin.Origin, UpdateTypes.Ships);
+ var availableProbes = bestOrigin.Origin.Ships.EspionageProbe;
+
+ if (availableProbes < neededProbes) {
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm,
+ $"Insufficient probes on {bestOrigin.Origin.ToString()} ({availableProbes}/{neededProbes}). Skipping {target.ToString()}.");
+ fleetId = (int) SendFleetCode.GenericError;
+ break;
+ }
+
fleetId = await _fleetScheduler.SendFleet(bestOrigin.Origin, ships, target.Celestial.Coordinate, Missions.Spy, Speeds.HundredPercent);
if (fleetId == (int)SendFleetCode.NotEnoughSlots) {
_tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Another worker took the slot, waiting again for a free slot... Retry count: {retryCount}/{maxRetryCount}");
@@ -562,13 +717,36 @@ protected override async Task Execute() {
}
}
}
+ if (!stopAutoFarm && (
+ !SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "TargetsProbedBeforeAttack") ||
+ (int)_tbotInstance.InstanceSettings.AutoFarm.TargetsProbedBeforeAttack == 0 ||
+ numProbed <= (int)_tbotInstance.InstanceSettings.AutoFarm.TargetsProbedBeforeAttack)
+ ) {
+ _tbotInstance.UserData.autoFarmLastGalaxy = 0;
+ _tbotInstance.UserData.autoFarmLastSystem = 0;
+
+ _tbotInstance.UserData.autoFarmLastRangeIndex = 0;
+
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm,
+ "Full scan cycle completed, resetting scan position for next cycle");
+
+ if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "StopAfterFullScan") &&
+ (bool)_tbotInstance.InstanceSettings.AutoFarm.StopAfterFullScan) {
+
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm,
+ "StopAfterFullScan=true -> Full scan completed. Stopping AutoFarm and waiting for /startautofarm.");
+
+ stop = true;
+ return;
+ }
+ }
+
} catch (Exception e) {
_tbotInstance.log(LogLevel.Debug, LogSender.AutoFarm, $"Exception: {e.Message}");
_tbotInstance.log(LogLevel.Warning, LogSender.AutoFarm, $"Stacktrace: {e.StackTrace}");
_tbotInstance.log(LogLevel.Warning, LogSender.AutoFarm, "Unable to parse scan range");
}
- // Wait for all espionage fleets to return.
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
Fleet firstReturning = _calculationService.GetLastReturningEspionage(_tbotInstance.UserData.fleets);
if (firstReturning != null) {
@@ -579,10 +757,46 @@ protected override async Task Execute() {
_tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, "Processing espionage reports of found inactives...");
- /// Process reports.
await AutoFarmProcessReports();
- /// Send attacks.
+ _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
+ _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
+ List rankSlotsPriority = new() {
+ new RankSlotsPriority(Feature.BrainAutoMine,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Brain,
+ ((bool) _tbotInstance.InstanceSettings.Brain.Active && (bool) _tbotInstance.InstanceSettings.Brain.Transports.Active && ((bool) _tbotInstance.InstanceSettings.Brain.AutoMine.Active || (bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.Active || (bool) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.Active || (bool) _tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.Active)),
+ (int) _tbotInstance.InstanceSettings.Brain.Transports.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Transport)),
+ new RankSlotsPriority(Feature.Expeditions,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Expeditions,
+ (bool) _tbotInstance.InstanceSettings.Expeditions.Active,
+ (int) _tbotInstance.UserData.slots.ExpTotal,
+ (int)_tbotInstance.UserData.slots.ExpInUse),
+ new RankSlotsPriority(Feature.AutoFarm,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoFarm,
+ (bool) _tbotInstance.InstanceSettings.AutoFarm.Active,
+ (int) _tbotInstance.InstanceSettings.AutoFarm.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Attack)),
+ new RankSlotsPriority(Feature.Colonize,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Colonize,
+ (bool) _tbotInstance.InstanceSettings.AutoColonize.Active,
+ (bool) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.Active ?
+ (int) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.MaxSlots :
+ 1,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Colonize)),
+ new RankSlotsPriority(Feature.AutoDiscovery,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoDiscovery,
+ (bool) _tbotInstance.InstanceSettings.AutoDiscovery.Active,
+ (int) _tbotInstance.InstanceSettings.AutoDiscovery.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Discovery)),
+ new RankSlotsPriority(Feature.Harvest,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoHarvest,
+ (bool) _tbotInstance.InstanceSettings.AutoHarvest.Active,
+ (int) _tbotInstance.InstanceSettings.AutoHarvest.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Harvest))
+ };
+ int MaxSlots = _calculationService.CalcSlotsPriority(Feature.AutoFarm, rankSlotsPriority, _tbotInstance.UserData.slots, _tbotInstance.UserData.fleets, (int) _tbotInstance.InstanceSettings.General.SlotsToLeaveFree);
+
List attackTargets;
if (_tbotInstance.InstanceSettings.AutoFarm.PreferedResource == "Metal")
attackTargets = _tbotInstance.UserData.farmTargets.Where(t => t.State == FarmState.AttackPending).OrderByDescending(t => t.Report.Loot(_tbotInstance.UserData.userInfo.Class).Metal).ToList();
@@ -625,7 +839,7 @@ protected override async Task Execute() {
attackTargetsCount++;
_tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Attacking target {attackTargetsCount}/{attackTargets.Count()} at {target.Celestial.Coordinate.ToString()} for {target.Report.Loot(_tbotInstance.UserData.userInfo.Class).TransportableResources}.");
var loot = target.Report.Loot(_tbotInstance.UserData.userInfo.Class);
- Celestial tempCelestial = _tbotInstance.UserData.celestials.Where(c => c.Coordinate.Type == Celestials.Moon).First();
+ Celestial tempCelestial = _tbotInstance.UserData.celestials.Where(c => c.Coordinate.Type == Celestials.Planet).First();
tempCelestial = await _tbotOgameBridge.UpdatePlanet(tempCelestial, UpdateTypes.LFBonuses);
float cargoBonus = tempCelestial.LFBonuses.GetShipCargoBonus(cargoShip);
var numCargo = _calculationService.CalcShipNumberForPayload(loot, cargoShip, _tbotInstance.UserData.researches.HyperspaceTechnology, _tbotInstance.UserData.serverData, cargoBonus, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo);
@@ -646,9 +860,8 @@ protected override async Task Execute() {
tempCelestial = await _tbotOgameBridge.UpdatePlanet(tempCelestial, UpdateTypes.Resources);
tempCelestial = await _tbotOgameBridge.UpdatePlanet(tempCelestial, UpdateTypes.LFBonuses);
if (tempCelestial.Ships != null && tempCelestial.Ships.GetAmount(cargoShip) >= (numCargo + _tbotInstance.InstanceSettings.AutoFarm.MinCargosToKeep)) {
- // TODO Future: If fleet composition is changed, update ships passed to CalcFlightTime.
speed = 0;
- if (/*cargoShip == Buildables.EspionageProbe &&*/ SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "MinLootFuelRatio") && _tbotInstance.InstanceSettings.AutoFarm.MinLootFuelRatio != 0) {
+ if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "MinLootFuelRatio") && _tbotInstance.InstanceSettings.AutoFarm.MinLootFuelRatio != 0) {
long maxFlightTime = SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "MaxFlightTime") ? (long) _tbotInstance.InstanceSettings.AutoFarm.MaxFlightTime : 86400;
var optimalSpeed = _calculationService.CalcOptimalFarmSpeed(tempCelestial.Coordinate, target.Celestial.Coordinate, attackingShips, target.Report.Loot(_tbotInstance.UserData.userInfo.Class), lootFuelRatio, maxFlightTime, _tbotInstance.UserData.researches, _tbotInstance.UserData.serverData, tempCelestial.LFBonuses, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.allianceClass);
if (optimalSpeed == 0) {
@@ -688,13 +901,11 @@ protected override async Task Execute() {
if (fromCelestial == null) {
_tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"No origin celestial available near destination {target.Celestial.ToString()} with enough cargo ships.");
- // TODO Future: If prefered cargo ship is not available or not sufficient capacity, combine with other cargo type.
foreach (var closest in closestCelestials) {
tempCelestial = closest;
tempCelestial = await _tbotOgameBridge.UpdatePlanet(tempCelestial, UpdateTypes.Ships);
tempCelestial = await _tbotOgameBridge.UpdatePlanet(tempCelestial, UpdateTypes.Resources);
tempCelestial = await _tbotOgameBridge.UpdatePlanet(tempCelestial, UpdateTypes.LFBonuses);
- // TODO Future: If fleet composition is changed, update ships passed to CalcFlightTime.
speed = 0;
if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "FleetSpeed") && _tbotInstance.InstanceSettings.AutoFarm.FleetSpeed > 0) {
speed = (int) _tbotInstance.InstanceSettings.AutoFarm.FleetSpeed / 10;
@@ -704,7 +915,7 @@ protected override async Task Execute() {
}
} else {
speed = 0;
- if (/*cargoShip == Buildables.EspionageProbe &&*/ SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "MinLootFuelRatio") && _tbotInstance.InstanceSettings.AutoFarm.MinLootFuelRatio != 0) {
+ if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "MinLootFuelRatio") && _tbotInstance.InstanceSettings.AutoFarm.MinLootFuelRatio != 0) {
long maxFlightTime = SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "MaxFlightTime") ? (long) _tbotInstance.InstanceSettings.AutoFarm.MaxFlightTime : 86400;
var optimalSpeed = _calculationService.CalcOptimalFarmSpeed(tempCelestial.Coordinate, target.Celestial.Coordinate, attackingShips, target.Report.Loot(_tbotInstance.UserData.userInfo.Class), lootFuelRatio, maxFlightTime, _tbotInstance.UserData.researches, _tbotInstance.UserData.serverData, tempCelestial.LFBonuses, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.allianceClass);
if (optimalSpeed == 0) {
@@ -777,7 +988,6 @@ protected override async Task Execute() {
continue;
}
- // Only execute update slots if our local copy indicates we have run out.
if (freeSlots <= slotsToLeaveFree) {
_tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
freeSlots = _tbotInstance.UserData.slots.Free;
@@ -785,7 +995,6 @@ protected override async Task Execute() {
while (freeSlots <= slotsToLeaveFree) {
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
- // No slots free, wait for first fleet to come back.
if (_tbotInstance.UserData.fleets.Any()) {
int interval = (int) ((1000 * _tbotInstance.UserData.fleets.OrderBy(fleet => fleet.BackIn).First().BackIn) + RandomizeHelper.CalcRandomInterval(IntervalType.AFewSeconds));
if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "MaxWaitTime") && (int) _tbotInstance.InstanceSettings.AutoFarm.MaxWaitTime != 0 && interval > (int) _tbotInstance.InstanceSettings.AutoFarm.MaxWaitTime * 1000) {
@@ -808,13 +1017,37 @@ protected override async Task Execute() {
.Where(fleet => fleet.Mission == Missions.Attack)
.ToList();
- if (_tbotInstance.UserData.slots.Free > slotsToLeaveFree && slotUsed.Count() < (int) _tbotInstance.InstanceSettings.AutoFarm.MaxSlots) {
+ if (_tbotInstance.UserData.slots.Free > slotsToLeaveFree && slotUsed.Count() < MaxSlots) {
+ fromCelestial = await _tbotOgameBridge.UpdatePlanet(fromCelestial, UpdateTypes.Ships);
+ var availableShips = fromCelestial.Ships.GetAmount(cargoShip) - (long) _tbotInstance.InstanceSettings.AutoFarm.MinCargosToKeep;
+ if (availableShips <= 0) {
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"No {cargoShip.ToString()} available on {fromCelestial.ToString()} (all ships already in use). Skipping target.");
+ continue;
+ }
+ if (availableShips < numCargo) {
+ _tbotInstance.log(LogLevel.Debug, LogSender.AutoFarm, $"Only {availableShips} {cargoShip.ToString()} available (needed {numCargo}). Adjusting fleet size.");
+ numCargo = availableShips;
+ attackingShips = new Ships();
+ attackingShips.Add(cargoShip, numCargo);
+
+ var cargoCapacity = _calculationService.CalcFleetCapacity(
+ attackingShips, _tbotInstance.UserData.serverData, _tbotInstance.UserData.researches.HyperspaceTechnology,
+ fromCelestial.LFBonuses, _tbotInstance.UserData.userInfo.Class);
+ var totalLoot = target.Report.Loot(_tbotInstance.UserData.userInfo.Class).TotalResources;
+
+ if (cargoCapacity < totalLoot) {
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm,
+ $"Insufficient cargo space after adjustment. {numCargo} {cargoShip.ToString()} can carry {cargoCapacity:N0} but need {totalLoot:N0}. Skipping target.");
+ continue;
+ }
+ }
+
_tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Attacking {target.ToString()} from {fromCelestial} with {numCargo} {cargoShip.ToString()}.");
Ships ships = new();
fromCelestial = await _tbotOgameBridge.UpdatePlanet(fromCelestial, UpdateTypes.LFBonuses);
speed = 0;
- if (/*cargoShip == Buildables.EspionageProbe &&*/ SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "MinLootFuelRatio") && _tbotInstance.InstanceSettings.AutoFarm.MinLootFuelRatio != 0) {
+ if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "MinLootFuelRatio") && _tbotInstance.InstanceSettings.AutoFarm.MinLootFuelRatio != 0) {
long maxFlightTime = SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "MaxFlightTime") ? (long) _tbotInstance.InstanceSettings.AutoFarm.MaxFlightTime : 86400;
var optimalSpeed = _calculationService.CalcOptimalFarmSpeed(fromCelestial.Coordinate, target.Celestial.Coordinate, attackingShips, target.Report.Loot(_tbotInstance.UserData.userInfo.Class), lootFuelRatio, maxFlightTime, _tbotInstance.UserData.researches, _tbotInstance.UserData.serverData, fromCelestial.LFBonuses, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.allianceClass);
if (optimalSpeed == 0) {
@@ -841,6 +1074,8 @@ protected override async Task Execute() {
if (fleetId > (int) SendFleetCode.GenericError) {
freeSlots--;
+
+ _successfulTargets.RecordAttack(target.Celestial.Coordinate, loot);
} else if (fleetId == (int) SendFleetCode.AfterSleepTime) {
stop = true;
return;
@@ -850,7 +1085,7 @@ protected override async Task Execute() {
target.State = FarmState.AttackSent;
_tbotInstance.UserData.farmTargets.Add(target);
} else {
- _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Unable to attack {target.Celestial.Coordinate}: {slotUsed.Count()} slots used by AutoFarm, {_tbotInstance.InstanceSettings.AutoFarm.MaxSlots} slots usable by AutoFarm, {_tbotInstance.UserData.slots.Free} slots free, {slotsToLeaveFree} must remain free.");
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Unable to attack {target.Celestial.Coordinate}: {slotUsed.Count()} slots used by AutoFarm, {MaxSlots} slots usable by AutoFarm, {_tbotInstance.UserData.slots.Free} slots free, {_tbotInstance.InstanceSettings.General.SlotsToLeaveFree} must remain free.");
return;
}
}
@@ -859,17 +1094,33 @@ protected override async Task Execute() {
_tbotInstance.log(LogLevel.Error, LogSender.AutoFarm, $"AutoFarm Exception: {e.Message}");
_tbotInstance.log(LogLevel.Warning, LogSender.AutoFarm, $"Stacktrace: {e.StackTrace}");
} finally {
+ if (stopAfterFullScan && finishedFullScan) {
+ stop = true;
+ }
+
_tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Attacked targets: {_tbotInstance.UserData.farmTargets.Where(t => t.State == FarmState.AttackSent).Count()}");
- _tbotInstance.UserData.farmTargets.RemoveAll(t => t.State == FarmState.ProbesSent); //At this point no ProbesSent should remain in farmTargets
+ _tbotInstance.UserData.farmTargets.RemoveAll(t => t.State == FarmState.ProbesSent);
if (!_tbotInstance.UserData.isSleeping) {
if (stop) {
_tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Stopping feature.");
await EndExecution();
} else {
var time = await _tbotOgameBridge.GetDateTime();
- var interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.AutoFarm.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.AutoFarm.CheckIntervalMax);
- if (interval <= 0)
- interval = RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
+ List orderedFleets = _tbotInstance.UserData.fleets
+ .Where(fleet => fleet.Mission == Missions.Attack)
+ .ToList();
+ orderedFleets = orderedFleets
+ .OrderByDescending(fleet => fleet.BackIn)
+ .ToList();
+ long interval;
+ try {
+ interval = (int) ((1000 * orderedFleets.First().BackIn) + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds));
+ } catch {
+ interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.AutoFarm.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.AutoFarm.CheckIntervalMax);
+ if (interval <= 0)
+ interval = RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ }
var newTime = time.AddMilliseconds(interval);
ChangeWorkerPeriod(interval);
_tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Next autofarm check at {newTime.ToString()}");
@@ -879,12 +1130,7 @@ protected override async Task Execute() {
}
}
- ///
- /// Checks all received espionage reports and updates _tbotInstance.UserData.farmTargets to reflect latest data retrieved from reports.
- ///
private async Task AutoFarmProcessReports() {
- // TODO Future: Read espionage reports in separate thread (concurently with probing itself).
- // TODO Future: Check if probes were destroyed, blacklist target if so to avoid additional kills.
List summaryReports = await _ogameService.GetEspionageReports();
foreach (var summary in summaryReports) {
if (summary.Type == EspionageReportType.Action)
@@ -901,10 +1147,8 @@ private async Task AutoFarmProcessReports() {
FarmTarget target;
var matchingTarget = _tbotInstance.UserData.farmTargets.Where(t => t.HasCoords(report.Coordinate));
if (matchingTarget.Count() == 0) {
- // Report received of planet not in _tbotInstance.UserData.farmTargets. If inactive: add, otherwise: ignore.
if (!report.IsInactive)
continue;
- //Get corresponding planet. Add to target list.
var galaxyInfo = await _ogameService.GetGalaxyInfo(report.Coordinate.Galaxy, report.Coordinate.System);
var planet = galaxyInfo.Planets.FirstOrDefault(p => p != null && p.Inactive && !p.Administrator && !p.Banned && !p.Vacation && p.HasCoords(report.Coordinate));
if (planet != null) {
@@ -920,11 +1164,13 @@ private async Task AutoFarmProcessReports() {
var newFarmTarget = target;
if (target.Report != null && DateTime.Compare(report.Date, target.Report.Date) < 0) {
- // Target has a more recent report. Delete report.
await _ogameService.DeleteReport(report.ID);
continue;
}
+ Buildables cargoShip;
+ Enum.TryParse((string) _tbotInstance.InstanceSettings.AutoFarm.CargoType, true, out cargoShip);
+ bool isUsingProbes = cargoShip == Buildables.EspionageProbe && _tbotInstance.UserData.serverData.ProbeCargo == 1 ? true : false;
newFarmTarget.Report = report;
if (_tbotInstance.InstanceSettings.AutoFarm.PreferedResource == "Metal" && report.Loot(_tbotInstance.UserData.userInfo.Class).Metal > _tbotInstance.InstanceSettings.AutoFarm.MinimumResources
|| _tbotInstance.InstanceSettings.AutoFarm.PreferedResource == "Crystal" && report.Loot(_tbotInstance.UserData.userInfo.Class).Crystal > _tbotInstance.InstanceSettings.AutoFarm.MinimumResources
@@ -939,23 +1185,94 @@ private async Task AutoFarmProcessReports() {
newFarmTarget.State = FarmState.ProbesRequired;
_tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Need more probes on {report.Coordinate}. Loot: {report.Loot(_tbotInstance.UserData.userInfo.Class)}");
- } else if (report.IsDefenceless()) {
+ } else if (report.IsDefenceless(isUsingProbes)) {
newFarmTarget.State = FarmState.AttackPending;
_tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Attack pending on {report.Coordinate}. Loot: {report.Loot(_tbotInstance.UserData.userInfo.Class)}");
} else {
newFarmTarget.State = FarmState.NotSuitable;
_tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Target {report.Coordinate} not suitable - defences present.");
+ bool blacklistActive = SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "Blacklist") &&
+ SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm.Blacklist, "Active") &&
+ (bool) _tbotInstance.InstanceSettings.AutoFarm.Blacklist.Active;
+ if (blacklistActive) {
+ int hoursUntilReset = SettingsService.GetSetting(_tbotInstance.InstanceSettings.AutoFarm.Blacklist, "ResetAfterHours", 48);
+ _blacklist.AddTarget(report.Coordinate, BlacklistReason.HasDefense, hoursUntilReset);
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Target {report.Coordinate} blacklisted for {hoursUntilReset}h (defenses present).");
+ }
}
} else {
newFarmTarget.State = FarmState.NotSuitable;
_tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Target {report.Coordinate} not suitable - insufficient loot ({report.Loot(_tbotInstance.UserData.userInfo.Class)})");
+ bool blacklistActiveLowRes = SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "Blacklist") &&
+ SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm.Blacklist, "Active") &&
+ (bool) _tbotInstance.InstanceSettings.AutoFarm.Blacklist.Active;
+ if (blacklistActiveLowRes) {
+ long minResources = SettingsService.GetSetting(_tbotInstance.InstanceSettings.AutoFarm.Blacklist, "MinimumResourcesToNotBlacklist", (long)500000);
+ if (report.Loot(_tbotInstance.UserData.userInfo.Class).TotalResources < minResources) {
+ int hoursUntilReset = SettingsService.GetSetting(_tbotInstance.InstanceSettings.AutoFarm.Blacklist, "ResetAfterHours", 48);
+ _blacklist.AddTarget(report.Coordinate, BlacklistReason.LowResources, hoursUntilReset);
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Target {report.Coordinate} blacklisted for {hoursUntilReset}h (low resources: {report.Loot(_tbotInstance.UserData.userInfo.Class)}).");
+ }
+ }
}
_tbotInstance.UserData.farmTargets.Remove(target);
_tbotInstance.UserData.farmTargets.Add(newFarmTarget);
+ } else {
+ bool processAllReports = (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "ProcessAllReports") &&
+ (bool) _tbotInstance.InstanceSettings.AutoFarm.ProcessAllReports) ||
+ (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "Blacklist") &&
+ SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm.Blacklist, "ProcessAllReports") &&
+ (bool) _tbotInstance.InstanceSettings.AutoFarm.Blacklist.ProcessAllReports);
+ if (processAllReports && report.IsInactive) {
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Processing report for {report.Coordinate} not scanned by TBot (ProcessAllReports enabled)...");
+ var galaxyInfo = await _ogameService.GetGalaxyInfo(report.Coordinate.Galaxy, report.Coordinate.System);
+ var planet = galaxyInfo.Planets.FirstOrDefault(p => p != null && p.Inactive && !p.Administrator && !p.Banned && !p.Vacation && p.HasCoords(report.Coordinate));
+ if (planet != null) {
+ var target = GetFarmTarget(planet);
+ if (target != null) {
+ var newFarmTarget = target;
+ Buildables cargoShip;
+ Enum.TryParse((string) _tbotInstance.InstanceSettings.AutoFarm.CargoType, true, out cargoShip);
+ bool isUsingProbes = cargoShip == Buildables.EspionageProbe && _tbotInstance.UserData.serverData.ProbeCargo == 1 ? true : false;
+ newFarmTarget.Report = report;
+ if (report.Loot(_tbotInstance.UserData.userInfo.Class).TotalResources > _tbotInstance.InstanceSettings.AutoFarm.MinimumResources) {
+ if (report.HasFleetInformation && report.HasDefensesInformation) {
+ if (report.IsDefenceless(isUsingProbes)) {
+ newFarmTarget.State = FarmState.AttackPending;
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Attack pending on {report.Coordinate}. Loot: {report.Loot(_tbotInstance.UserData.userInfo.Class)}");
+ } else {
+ newFarmTarget.State = FarmState.NotSuitable;
+ bool blacklistActiveDefense2 = SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "Blacklist") &&
+ (bool) _tbotInstance.InstanceSettings.AutoFarm.Blacklist.Active;
+ if (blacklistActiveDefense2) {
+ int hoursUntilReset = SettingsService.GetSetting(_tbotInstance.InstanceSettings.AutoFarm.Blacklist, "ResetAfterHours", 48);
+ _blacklist.AddTarget(report.Coordinate, BlacklistReason.HasDefense, hoursUntilReset);
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Target {report.Coordinate} blacklisted for {hoursUntilReset}h (defenses present).");
+ }
+ }
+ }
+ } else {
+ newFarmTarget.State = FarmState.NotSuitable;
+ bool blacklistActiveLowRes2 = SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.AutoFarm, "Blacklist") &&
+ (bool) _tbotInstance.InstanceSettings.AutoFarm.Blacklist.Active;
+ if (blacklistActiveLowRes2) {
+ long minResources = SettingsService.GetSetting(_tbotInstance.InstanceSettings.AutoFarm.Blacklist, "MinimumResourcesToNotBlacklist", (long)500000);
+ if (report.Loot(_tbotInstance.UserData.userInfo.Class).TotalResources < minResources) {
+ int hoursUntilReset = SettingsService.GetSetting(_tbotInstance.InstanceSettings.AutoFarm.Blacklist, "ResetAfterHours", 48);
+ _blacklist.AddTarget(report.Coordinate, BlacklistReason.LowResources, hoursUntilReset);
+ _tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Target {report.Coordinate} blacklisted for {hoursUntilReset}h (low resources: {report.Loot(_tbotInstance.UserData.userInfo.Class)}).");
+ }
+ }
+ }
+ _tbotInstance.UserData.farmTargets.Remove(target);
+ _tbotInstance.UserData.farmTargets.Add(newFarmTarget);
+ }
+ }
} else {
_tbotInstance.log(LogLevel.Information, LogSender.AutoFarm, $"Target {report.Coordinate} not scanned by TBot, ignoring...");
}
+ }
} catch (Exception e) {
_tbotInstance.log(LogLevel.Error, LogSender.AutoFarm, $"AutoFarmProcessReports Exception: {e.Message}");
_tbotInstance.log(LogLevel.Warning, LogSender.AutoFarm, $"Stacktrace: {e.StackTrace}");
@@ -963,8 +1280,21 @@ private async Task AutoFarmProcessReports() {
}
}
- await _ogameService.DeleteAllEspionageReports();
+ int deleteRetries = 3;
+ for (int i = 0; i < deleteRetries; i++) {
+ try {
+ await _ogameService.DeleteAllEspionageReports();
+ break;
+ } catch (Exception e) when (e.Message.Contains("503") || e.Message.Contains("Service Unavailable") || e.Message.Contains("Unable to delete")) {
+ if (i < deleteRetries - 1) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.AutoFarm, $"Failed to delete espionage reports (503 error), retry {i + 1}/{deleteRetries}...");
+ await Task.Delay(3000);
+ } else {
+ _tbotInstance.log(LogLevel.Warning, LogSender.AutoFarm, $"Could not delete espionage reports after {deleteRetries} attempts. Will try next cycle.");
+ }
+ }
+ }
}
}
-}
+}
\ No newline at end of file
diff --git a/TBot/Workers/Brain/AutoDefenceWorker.cs b/TBot/Workers/Brain/AutoDefenceWorker.cs
new file mode 100644
index 00000000..c49c2c43
--- /dev/null
+++ b/TBot/Workers/Brain/AutoDefenceWorker.cs
@@ -0,0 +1,157 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.Extensions.Logging;
+using Tbot.Helpers;
+using Tbot.Includes;
+using Tbot.Services;
+using TBot.Common.Logging;
+using TBot.Model;
+using TBot.Ogame.Infrastructure;
+using TBot.Ogame.Infrastructure.Enums;
+using TBot.Ogame.Infrastructure.Models;
+
+namespace Tbot.Workers.Brain {
+ public class AutoDefenceWorker : WorkerBase {
+ private readonly IOgameService _ogameService;
+ private readonly IFleetScheduler _fleetScheduler;
+ private readonly ICalculationService _calculationService;
+ private readonly ITBotOgamedBridge _tbotOgameBridge;
+ public AutoDefenceWorker(ITBotMain parentInstance,
+ IOgameService ogameService,
+ IFleetScheduler fleetScheduler,
+ ICalculationService calculationService,
+ ITBotOgamedBridge tbotOgameBridge) :
+ base(parentInstance) {
+ _ogameService = ogameService;
+ _fleetScheduler = fleetScheduler;
+ _calculationService = calculationService;
+ _tbotOgameBridge = tbotOgameBridge;
+ }
+
+ protected override async Task Execute() {
+ try {
+ DoLog(LogLevel.Information, "Running autodefence...");
+
+ List newCelestials = _tbotInstance.UserData.celestials.ToList();
+ List celestialsToExclude = _calculationService.ParseCelestialsList(_tbotInstance.InstanceSettings.Brain.AutoDefence.Exclude, _tbotInstance.UserData.celestials);
+
+ Defences neededDefences = new(
+ (long) _tbotInstance.InstanceSettings.Brain.AutoDefence.DefenceToReach.RocketLauncher,
+ (long) _tbotInstance.InstanceSettings.Brain.AutoDefence.DefenceToReach.LightLaser,
+ (long) _tbotInstance.InstanceSettings.Brain.AutoDefence.DefenceToReach.HeavyLaser,
+ (long) _tbotInstance.InstanceSettings.Brain.AutoDefence.DefenceToReach.GaussCannon,
+ (long) _tbotInstance.InstanceSettings.Brain.AutoDefence.DefenceToReach.IonCannon,
+ (long) _tbotInstance.InstanceSettings.Brain.AutoDefence.DefenceToReach.PlasmaTurret,
+ (long) ((bool) _tbotInstance.InstanceSettings.Brain.AutoDefence.DefenceToReach.SmallShieldDome ? 1: 0),
+ (long) ((bool) _tbotInstance.InstanceSettings.Brain.AutoDefence.DefenceToReach.LargeShieldDome ? 1: 0),
+ (long) _tbotInstance.InstanceSettings.Brain.AutoDefence.DefenceToReach.AntiBallisticMissiles,
+ (long) _tbotInstance.InstanceSettings.Brain.AutoDefence.DefenceToReach.InterplanetaryMissiles
+ );
+ foreach (Celestial celestial in (bool) _tbotInstance.InstanceSettings.Brain.AutoDefence.RandomOrder ? _tbotInstance.UserData.celestials.Shuffle().ToList() : _tbotInstance.UserData.celestials) {
+ if (celestialsToExclude.Has(celestial)) {
+ DoLog(LogLevel.Information, $"Skipping {celestial.ToString()}: celestial in exclude list.");
+ continue;
+ }
+
+ var tempCelestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Fast);
+
+ tempCelestial = await _tbotOgameBridge.UpdatePlanet(tempCelestial, UpdateTypes.Productions);
+ if (tempCelestial.HasProduction()) {
+ DoLog(LogLevel.Warning, $"Skipping {tempCelestial.ToString()}: there is already a production ongoing.");
+ foreach (Production production in tempCelestial.Productions) {
+ Buildables productionType = (Buildables) production.ID;
+ DoLog(LogLevel.Information, $"Skipping {tempCelestial.ToString()}: {production.Nbr}x{productionType.ToString()} are already in production.");
+ }
+ continue;
+ }
+ tempCelestial = await _tbotOgameBridge.UpdatePlanet(tempCelestial, UpdateTypes.Constructions);
+ if (tempCelestial.Constructions.BuildingID == (int) Buildables.Shipyard || tempCelestial.Constructions.BuildingID == (int) Buildables.NaniteFactory) {
+ Buildables buildingInProgress = (Buildables) tempCelestial.Constructions.BuildingID;
+ DoLog(LogLevel.Information, $"Skipping {tempCelestial.ToString()}: {buildingInProgress.ToString()} is upgrading.");
+ continue;
+ }
+
+ tempCelestial = await _tbotOgameBridge.UpdatePlanet(tempCelestial, UpdateTypes.Ships);
+ tempCelestial = await _tbotOgameBridge.UpdatePlanet(tempCelestial, UpdateTypes.Defences);
+ tempCelestial = await _tbotOgameBridge.UpdatePlanet(tempCelestial, UpdateTypes.Resources);
+ tempCelestial = await _tbotOgameBridge.UpdatePlanet(tempCelestial, UpdateTypes.LFBonuses);
+
+ var capacity = _calculationService.CalcFleetCapacity(tempCelestial.Ships, _tbotInstance.UserData.serverData, _tbotInstance.UserData.researches.HyperspaceTechnology, tempCelestial.LFBonuses, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo);
+ if (tempCelestial.Coordinate.Type == Celestials.Moon && (bool) _tbotInstance.InstanceSettings.Brain.AutoDefence.ExcludeMoons) {
+ DoLog(LogLevel.Information, $"Skipping {tempCelestial.ToString()}: celestial is a moon.");
+ continue;
+ }
+ Defences currentDefences = tempCelestial.Defences;
+ Defences defencesToBuild = neededDefences.Difference(currentDefences);
+ if (defencesToBuild.IsEmpty()) {
+ DoLog(LogLevel.Information, $"Skipping {tempCelestial.ToString()}: all defences are already built.");
+ continue;
+ } else {
+ Resources defenceCost = defencesToBuild.GetDefenceCost();
+ if (!tempCelestial.Resources.IsEnoughFor(defenceCost)) {
+ DoLog(LogLevel.Information, $"{tempCelestial.ToString()}: Not enough resources to build all defences. Will build only what is possible.");
+ defencesToBuild = _calculationService.CalcmaxDefencesBuildable(defencesToBuild, tempCelestial.Resources);
+ }
+ if (defencesToBuild.IsEmpty()) {
+ DoLog(LogLevel.Information, $"{tempCelestial.ToString()}: Not enough resources to build any defence.");
+ continue;
+ }
+ DoLog(LogLevel.Information, $"{tempCelestial.ToString()}: Defences to build: {defencesToBuild.ToString()}");
+ try {
+ foreach (var (defenceType, amountNeeded) in defencesToBuild.GetDefenceTypesWithAmount()) {
+ await _ogameService.BuildDefences(tempCelestial, defenceType, amountNeeded);
+ }
+ DoLog(LogLevel.Information, "Production succesfully started.");
+ } catch {
+ DoLog(LogLevel.Warning, "Unable to start defence production.");
+ }
+ tempCelestial = await _tbotOgameBridge.UpdatePlanet(tempCelestial, UpdateTypes.Productions);
+ foreach (Production production in tempCelestial.Productions) {
+ Buildables productionType = (Buildables) production.ID;
+ DoLog(LogLevel.Information, $"{tempCelestial.ToString()}: {production.Nbr}x{productionType.ToString()} are in production.");
+ }
+ }
+
+ newCelestials.Remove(celestial);
+ newCelestials.Add(tempCelestial);
+ }
+ _tbotInstance.UserData.celestials = newCelestials;
+ } catch (Exception e) {
+ DoLog(LogLevel.Error, $"Unable to complete autodefence: {e.Message}");
+ DoLog(LogLevel.Warning, $"Stacktrace: {e.StackTrace}");
+ } finally {
+ var time = await _tbotOgameBridge.GetDateTime();
+ var interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Brain.AutoDefence.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Brain.AutoDefence.CheckIntervalMax);
+ if (interval <= 0)
+ interval = RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ var newTime = time.AddMilliseconds(interval);
+ ChangeWorkerPeriod(interval);
+ DoLog(LogLevel.Information, $"Next Defence check at {newTime.ToString()}");
+ await _tbotOgameBridge.CheckCelestials();
+ }
+ }
+
+ public override bool IsWorkerEnabledBySettings() {
+ try {
+ return ((bool) _tbotInstance.InstanceSettings.Brain.Active && (bool) _tbotInstance.InstanceSettings.Brain.AutoDefence.Active);
+ } catch(Exception) {
+ return false;
+ }
+ }
+
+ public override string GetWorkerName() {
+ return "AutoDefence";
+ }
+ public override Feature GetFeature() {
+ return Feature.BrainAutobuildDefence;
+ }
+
+ public override LogSender GetLogSender() {
+ return LogSender.AutoDefence;
+ }
+ }
+}
diff --git a/TBot/Workers/Brain/AutoMineCelestialWorker.cs b/TBot/Workers/Brain/AutoMineCelestialWorker.cs
index bc3aa727..83401c54 100644
--- a/TBot/Workers/Brain/AutoMineCelestialWorker.cs
+++ b/TBot/Workers/Brain/AutoMineCelestialWorker.cs
@@ -134,6 +134,7 @@ private async Task AutoMineCelestial(Celestial celestial, Buildings maxBuildings
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.LFBonuses);
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.LFBuildings);
Planet abaCelestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Fast) as Planet;
+ _tbotInstance.UserData.celestials = await _tbotOgameBridge.UpdateCelestials();
if (
(!SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.Brain.AutoMine, "BuildCrawlers") || (bool) _tbotInstance.InstanceSettings.Brain.AutoMine.BuildCrawlers) &&
celestial.Coordinate.Type == Celestials.Planet &&
@@ -203,7 +204,7 @@ private async Task AutoMineCelestial(Celestial celestial, Buildings maxBuildings
DoLog(LogLevel.Debug, $"Days of investment return: {Math.Round(DOIR, 2).ToString()} days.");
}
- Resources xCostBuildable = _calculationService.CalcPrice(buildable, level);
+ Resources xCostBuildable = _calculationService.CalcPrice(buildable, level, celestial.LFBonuses);
if (celestial is Moon)
xCostBuildable.Deuterium += (long) autoMinerSettings.DeutToLeaveOnMoons;
@@ -213,7 +214,7 @@ private async Task AutoMineCelestial(Celestial celestial, Buildings maxBuildings
if ((bool) _tbotInstance.InstanceSettings.Brain.AutoMine.BuildSolarSatellites) {
buildable = Buildables.SolarSatellite;
level = _calculationService.CalcNeededSolarSatellites(celestial as Planet, xCostBuildable.Energy - celestial.ResourcesProduction.Energy.CurrentProduction, _tbotInstance.UserData.userInfo.Class == CharacterClass.Collector, _tbotInstance.UserData.staff.Engineer, _tbotInstance.UserData.staff.IsFull);
- xCostBuildable = _calculationService.CalcPrice(buildable, level);
+ xCostBuildable = _calculationService.CalcPrice(buildable, level, celestial.LFBonuses);
}
else {
DoLog(LogLevel.Information, $"Unable to build SolarSatellites for Terraformer. Stopping AutoMiner for celestial {celestial.ToString()}");
@@ -297,32 +298,258 @@ private async Task AutoMineCelestial(Celestial celestial, Buildings maxBuildings
}
if (buildable == Buildables.SolarSatellite || buildable == Buildables.Crawler) {
DoLog(LogLevel.Information, $"Not enough resources to build: {level.ToString()}x {buildable.ToString()} on {celestial.ToString()}. Needed: {xCostBuildable.TransportableResources} - Available: {celestial.Resources.TransportableResources}");
-
} else {
DoLog(LogLevel.Information, $"Not enough resources to build: {buildable.ToString()} level {level.ToString()} on {celestial.ToString()}. Needed: {xCostBuildable.TransportableResources} - Available: {celestial.Resources.TransportableResources}");
}
if ((bool) _tbotInstance.InstanceSettings.Brain.AutoMine.Transports.Active && (bool) _tbotInstance.InstanceSettings.Brain.Transports.Active) {
+ _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
- if (!_calculationService.IsThereTransportTowardsCelestial(celestial, _tbotInstance.UserData.fleets)) {
- Celestial origin = _tbotInstance.UserData.celestials
- .Unique()
- .Where(c => c.Coordinate.Galaxy == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Galaxy)
- .Where(c => c.Coordinate.System == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.System)
- .Where(c => c.Coordinate.Position == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Position)
- .Where(c => c.Coordinate.Type == Enum.Parse((string) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Type))
- .SingleOrDefault() ?? new() { ID = 0 };
- fleetId = await _fleetScheduler.HandleMinerTransport(origin, celestial, xCostBuildable, buildable, maxBuildings, maxFacilities, maxLunarFacilities, autoMinerSettings);
-
- if (fleetId == (int) SendFleetCode.AfterSleepTime) {
- stop = true;
- return;
- }
- if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
- delay = true;
- return;
+ List rankSlotsPriority = new() {
+ new RankSlotsPriority(Feature.BrainAutoMine,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Brain,
+ ((bool) _tbotInstance.InstanceSettings.Brain.Active && (bool) _tbotInstance.InstanceSettings.Brain.Transports.Active && ((bool) _tbotInstance.InstanceSettings.Brain.AutoMine.Active || (bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.Active || (bool) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.Active || (bool) _tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.Active)),
+ (int) _tbotInstance.InstanceSettings.Brain.Transports.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Transport)),
+ new RankSlotsPriority(Feature.Expeditions,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Expeditions,
+ (bool) _tbotInstance.InstanceSettings.Expeditions.Active,
+ (int) _tbotInstance.UserData.slots.ExpTotal,
+ (int)_tbotInstance.UserData.slots.ExpInUse),
+ new RankSlotsPriority(Feature.AutoFarm,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoFarm,
+ (bool) _tbotInstance.InstanceSettings.AutoFarm.Active,
+ (int) _tbotInstance.InstanceSettings.AutoFarm.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Attack)),
+ new RankSlotsPriority(Feature.Colonize,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Colonize,
+ (bool) _tbotInstance.InstanceSettings.AutoColonize.Active,
+ (bool) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.Active ?
+ (int) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.MaxSlots :
+ 1,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Colonize)),
+ new RankSlotsPriority(Feature.AutoDiscovery,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoDiscovery,
+ (bool) _tbotInstance.InstanceSettings.AutoDiscovery.Active,
+ (int) _tbotInstance.InstanceSettings.AutoDiscovery.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Discovery)),
+ new RankSlotsPriority(Feature.Harvest,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoHarvest,
+ (bool) _tbotInstance.InstanceSettings.AutoHarvest.Active,
+ (int) _tbotInstance.InstanceSettings.AutoHarvest.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Harvest))
+ };
+ int MaxSlots = _calculationService.CalcSlotsPriority(Feature.BrainAutoMine, rankSlotsPriority, _tbotInstance.UserData.slots, _tbotInstance.UserData.fleets, (int) _tbotInstance.InstanceSettings.General.SlotsToLeaveFree);
+
+ if (MaxSlots > 0) {
+ if (!_calculationService.IsThereTransportTowardsCelestial(celestial, _tbotInstance.UserData.fleets)) {
+ Celestial origin = new() { ID = 0 };
+ List allCelestials = _tbotInstance.UserData.celestials;
+ for (int i = 0; i < allCelestials.Count(); i++) {
+ allCelestials[i] = await _tbotOgameBridge.UpdatePlanet(allCelestials[i], UpdateTypes.Resources);
+ allCelestials[i] = await _tbotOgameBridge.UpdatePlanet(allCelestials[i], UpdateTypes.Ships);
+ allCelestials[i] = await _tbotOgameBridge.UpdatePlanet(allCelestials[i], UpdateTypes.LFBonuses);
+ }
+ Resources missingResources = xCostBuildable.Difference(celestial.Resources);
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.CheckMoonOrPlanetFirst) {
+ if (celestial.Coordinate.Type == Celestials.Planet && ((bool) _tbotInstance.InstanceSettings.Brain.Transports.CheckMoonOrPlanetFirst && _calculationService.IsThereMoonHere(allCelestials, celestial))) {
+ origin = allCelestials.Unique()
+ .Where(c => c.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(c => c.Coordinate.System == celestial.Coordinate.System)
+ .Where(c => c.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(c => c.Coordinate.Type == Celestials.Moon)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ }
+ if (celestial.Coordinate.Type == Celestials.Moon) {
+ origin = allCelestials.Unique()
+ .Where(c => c.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(c => c.Coordinate.System == celestial.Coordinate.System)
+ .Where(c => c.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(c => c.Coordinate.Type == Celestials.Planet)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ }
+ if (origin.ID != 0) {
+ if (origin.Resources.IsEnoughFor(missingResources)) {
+ missingResources = _tbotInstance.InstanceSettings.Brain.Transports.RoundResources ? missingResources.Round() : missingResources;
+ Buildables preferredShip = Buildables.SmallCargo;
+ if (!Enum.TryParse((string) _tbotInstance.InstanceSettings.Brain.Transports.CargoType, true, out preferredShip)) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, "Unable to parse CargoType. Falling back to default SmallCargo");
+ preferredShip = Buildables.SmallCargo;
+ }
+ long shipsNeeded = _calculationService.CalcShipNumberForPayload(missingResources, preferredShip, _tbotInstance.UserData.researches.HyperspaceTechnology, _tbotInstance.UserData.serverData, origin.LFBonuses.GetShipCargoBonus(preferredShip), _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo);
+ if (origin.Ships.GetAmount(preferredShip) >= shipsNeeded) {
+ Ships ships = new();
+ ships.Add((Buildables) preferredShip, shipsNeeded);
+ fleetId = await _fleetScheduler.SendFleet(origin, ships, celestial.Coordinate, Missions.Transport, Speeds.HundredPercent, missingResources);
+ if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ stop = true;
+ }
+ if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ delay = true;
+ }
+ } else if (_tbotInstance.InstanceSettings.Brain.Transports.DoMultipleTransportIsNotEnoughShipButSamePosition) {
+ DoLog(LogLevel.Warning, $"Not enough Cargo available. Multiple transports will take place.");
+ Ships ships = new();
+ ships.Add((Buildables) preferredShip, origin.Ships.GetAmount(preferredShip));
+ Resources transportableResources = _calculationService.CalcMaxTransportableResources(ships, missingResources, _tbotInstance.UserData.researches.HyperspaceTechnology, _tbotInstance.UserData.serverData, origin.LFBonuses, _tbotInstance.UserData.userInfo.Class, 0, _tbotInstance.UserData.serverData.ProbeCargo);
+ fleetId = await _fleetScheduler.SendFleet(origin, ships, celestial.Coordinate, Missions.Transport, Speeds.HundredPercent, transportableResources);
+ if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ stop = true;
+ }
+ if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ delay = true;
+ }
+ } else {
+ DoLog(LogLevel.Warning, $"Not enough Cargo available. Skipping CheckMoonOrPlanetFirst.");
+ }
+ } else {
+ DoLog(LogLevel.Warning, $"Not enough resources available on {origin.ToString()} only to send resources to {celestial.ToString()}.");
+ }
+ } else {
+ DoLog(LogLevel.Warning, $"No Moon available on {celestial.ToString()}.");
+ }
+ }
+
+ if (fleetId <= 0) {
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.Active) {
+ origin = _tbotInstance.UserData.celestials
+ .Unique()
+ .Where(c => c.Coordinate.Galaxy == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Galaxy)
+ .Where(c => c.Coordinate.System == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.System)
+ .Where(c => c.Coordinate.Position == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Position)
+ .Where(c => c.Coordinate.Type == Enum.Parse((string) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Type))
+ .SingleOrDefault() ?? new() { ID = 0 };
+ List celestialsToExclude = _calculationService.ParseCelestialsList(_tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.Exclude, allCelestials);
+ Buildables preferredShip = Buildables.SmallCargo;
+ if (!Enum.TryParse((string) _tbotInstance.InstanceSettings.Brain.Transports.CargoType, true, out preferredShip)) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, "Unable to parse CargoType. Falling back to default SmallCargo");
+ preferredShip = Buildables.SmallCargo;
+ }
+ TransportSettings transportsSettings = new((bool) _tbotInstance.InstanceSettings.Brain.Transports.Active,
+ preferredShip,
+ (long) _tbotInstance.InstanceSettings.Brain.Transports.DeutToLeaveOnMoons,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.RoundResources,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.SendToTheMoonIfPossible,
+ origin,
+ (long) _tbotInstance.InstanceSettings.Brain.Transports.MaxSlots,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.CheckMoonOrPlanetFirst,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.DoMultipleTransportIsNotEnoughShipButSamePosition,
+ new MultipleOrigins((bool) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.Active,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.OnlyFromMoons,
+ (long) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.MinimumResourcesToSend,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.PriorityToProximityOverQuantity,
+ celestialsToExclude)
+ );
+
+ Celestial destination;
+ if ((bool) transportsSettings.SendToTheMoonIfPossible && celestial.Coordinate.Type == Celestials.Planet && _calculationService.IsThereMoonHere(allCelestials, celestial)) {
+ destination = allCelestials
+ .Unique()
+ .Where(planet => planet.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(planet => planet.Coordinate.System == celestial.Coordinate.System)
+ .Where(planet => planet.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(planet => planet.Coordinate.Type == Celestials.Moon)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ if (destination.Ships.IsEmpty() || celestial.Resources.TotalResources == 0)
+ destination = celestial;
+ } else {
+ destination = allCelestials
+ .Unique()
+ .Where(planet => planet.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(planet => planet.Coordinate.System == celestial.Coordinate.System)
+ .Where(planet => planet.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(planet => planet.Coordinate.Type == celestial.Coordinate.Type)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ }
+
+ var resultOrigins = _calculationService.CalcMultipleOrigin(celestial, allCelestials, missingResources, transportsSettings, _tbotInstance.UserData.fleets, _tbotInstance.UserData);
+
+ if (resultOrigins.Count() == 0) {
+ DoLog(LogLevel.Information, $"No origin is available. This may be due to a lack of resources or cargo.");
+ return;
+ }
+ if (resultOrigins.Count() > MaxSlots) {
+ DoLog(LogLevel.Information, $"Not enough slots available to send all resources to build: {buildable.ToString()} level {level.ToString()} on {celestial.ToString()}. Slots needed: {resultOrigins.Count().ToString()}/{MaxSlots}.");
+ delay = true;
+ return;
+ }
+
+ Ships ships = new();
+
+ foreach (var item in resultOrigins) {
+ ships = new();
+ ships.Add((Buildables) transportsSettings.CargoType, _calculationService.CalcShipNumberForPayload(item.FirstOrDefault().Value, (Buildables) transportsSettings.CargoType, _tbotInstance.UserData.researches.HyperspaceTechnology, _tbotInstance.UserData.serverData, celestial.LFBonuses.GetShipCargoBonus(transportsSettings.CargoType), _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo));
+ if (item.FirstOrDefault().Key.Coordinate.IsSame(destination.Coordinate) && transportsSettings.SendToTheMoonIfPossible && destination.Coordinate.Type == Celestials.Moon)
+ fleetId= await _fleetScheduler.SendFleet(item.FirstOrDefault().Key, ships, celestial.Coordinate, Missions.Transport, Speeds.HundredPercent, item.FirstOrDefault().Value);
+ else
+ fleetId= await _fleetScheduler.SendFleet(item.FirstOrDefault().Key, ships, destination.Coordinate, Missions.Transport, Speeds.HundredPercent, item.FirstOrDefault().Value);
+
+ if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ stop = true;
+ return;
+ }
+ if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ delay = true;
+ return;
+ }
+ }
+ } else {
+ Celestial destination;
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.SendToTheMoonIfPossible && celestial.Coordinate.Type == Celestials.Planet && _calculationService.IsThereMoonHere(_tbotInstance.UserData.celestials, celestial) && (!celestial.Ships.IsEmpty() || celestial.Resources.TotalResources > 0)) {
+ destination = allCelestials
+ .Unique()
+ .Where(planet => planet.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(planet => planet.Coordinate.System == celestial.Coordinate.System)
+ .Where(planet => planet.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(planet => planet.Coordinate.Type == Celestials.Moon)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ if (destination.Ships.IsEmpty() || celestial.Resources.TotalResources == 0)
+ destination = celestial;
+ xCostBuildable = xCostBuildable.Difference(destination.Resources);
+ } else {
+ destination = allCelestials
+ .Unique()
+ .Where(planet => planet.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(planet => planet.Coordinate.System == celestial.Coordinate.System)
+ .Where(planet => planet.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(planet => planet.Coordinate.Type == celestial.Coordinate.Type)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ }
+ origin = allCelestials
+ .Unique()
+ .Where(c => c.Coordinate.Galaxy == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Galaxy)
+ .Where(c => c.Coordinate.System == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.System)
+ .Where(c => c.Coordinate.Position == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Position)
+ .Where(c => c.Coordinate.Type == Enum.Parse((string) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Type))
+ .SingleOrDefault() ?? new() { ID = 0 };
+ fleetId = await _fleetScheduler.HandleMinerTransport(origin, celestial, destination, xCostBuildable, buildable, maxBuildings, maxFacilities, maxLunarFacilities, autoMinerSettings);
+ if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ stop = true;
+ }
+ if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ delay = true;
+ }
+ }
+ }
+ } else {
+ DoLog(LogLevel.Information, $"Skipping transport: there is already a transport incoming in {celestial.ToString()}");
+ fleetId = _tbotInstance.UserData.fleets.Where(f => f.Mission == Missions.Transport)
+ .Where(f => f.Resources.TotalResources > 0)
+ .Where(f => f.ReturnFlight == false)
+ .Where(f => f.Destination.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(f => f.Destination.System == celestial.Coordinate.System)
+ .Where(f => f.Destination.Position == celestial.Coordinate.Position)
+ .Where(f => f.Destination.Type == celestial.Coordinate.Type)
+ .First().ID;
}
} else {
- DoLog(LogLevel.Information, $"Skipping transport: there is already a transport incoming in {celestial.ToString()}");
+ if (_tbotInstance.InstanceSettings.General.SlotPriorityLevel.Brain > 0) {
+ if (_tbotInstance.InstanceSettings.Brain.Transports.MaxSlots == 0)
+ DoLog(LogLevel.Information, $"Transports.MaxSlots is set to 0, you should increase it.");
+ else
+ DoLog(LogLevel.Information, $"0 slots available.");
+ }
+ DoLog(LogLevel.Information, $"Not enough slots available for Transports.MaxSlots, delaying.");
+ delay = true;
}
}
}
@@ -368,6 +595,7 @@ private async Task AutoMineCelestial(Celestial celestial, Buildings maxBuildings
var time = await _tbotOgameBridge.GetDateTime();
string autoMineTimer = $"AutoMine-{celestial.ID.ToString()}";
DateTime newTime;
+ long interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Brain.AutoMine.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Brain.AutoMine.CheckIntervalMax);
if (stop) {
DoLog(LogLevel.Information, $"Stopping AutoMine check for {celestial.ToString()}.");
await EndExecution();
@@ -376,7 +604,6 @@ private async Task AutoMineCelestial(Celestial celestial, Buildings maxBuildings
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Facilities);
DoLog(LogLevel.Information, $"Delaying...");
time = await _tbotOgameBridge.GetDateTime();
- long interval;
try {
interval = _calculationService.CalcProductionTime((Buildables) celestial.Productions.First().ID, celestial.Productions.First().Nbr, _tbotInstance.UserData.serverData, celestial.Facilities) * 1000 + RandomizeHelper.CalcRandomInterval(IntervalType.AFewSeconds);
} catch {
@@ -389,9 +616,8 @@ private async Task AutoMineCelestial(Celestial celestial, Buildings maxBuildings
DoLog(LogLevel.Information, $"Delaying...");
time = await _tbotOgameBridge.GetDateTime();
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
- long interval;
try {
- interval = (_tbotInstance.UserData.fleets.OrderBy(f => f.BackIn).First().BackIn ?? 0) * 1000 + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ interval = (_tbotInstance.UserData.fleets.Where(f => f.Mission == Missions.Transport).OrderBy(f => f.BackIn).First().BackIn ?? 0) * 1000 + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
} catch {
interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Brain.AutoMine.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Brain.AutoMine.CheckIntervalMax);
}
@@ -399,7 +625,7 @@ private async Task AutoMineCelestial(Celestial celestial, Buildings maxBuildings
ChangeWorkerPeriod(interval);
DoLog(LogLevel.Information, $"Next AutoMine check for {celestial.ToString()} at {newTime.ToString()}");
} else if (started) {
- long interval = (long) celestial.Constructions.BuildingCountdown * (long) 1000 + (long) RandomizeHelper.CalcRandomInterval(IntervalType.AFewSeconds);
+ interval = (long) celestial.Constructions.BuildingCountdown * (long) 1000 + (long) RandomizeHelper.CalcRandomInterval(IntervalType.AFewSeconds);
newTime = time.AddMilliseconds(interval);
ChangeWorkerPeriod(interval);
@@ -414,16 +640,27 @@ private async Task AutoMineCelestial(Celestial celestial, Buildings maxBuildings
DoLog(LogLevel.Information, $"Next AutoMine check for {celestial.ToString()} at {newTime.ToString()}");
} else {
- long interval = await CalcAutoMineTimer(celestial, buildable, level, started, maxBuildings, maxFacilities, maxLunarFacilities, autoMinerSettings);
-
- if (fleetId != 0 && fleetId != -1 && fleetId != -2) {
+ interval = await CalcAutoMineTimer(celestial, buildable, level, started, maxBuildings, maxFacilities, maxLunarFacilities, autoMinerSettings);
+ if (fleetId > 0) {
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
var transportfleet = _tbotInstance.UserData.fleets.Single(f => f.ID == fleetId && f.Mission == Missions.Transport);
interval = (transportfleet.ArriveIn * 1000) + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
-
} else {
interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Brain.AutoMine.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Brain.AutoMine.CheckIntervalMax);
}
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.DoMultipleTransportIsNotEnoughShipButSamePosition) {
+ if (_tbotInstance.UserData.fleets.Where(f => f.Mission == Missions.Transport).Count() > 0) {
+ var transportfleet2 = _tbotInstance.UserData.fleets.Where(f => f.Mission == Missions.Transport)
+ .Where(f => f.Destination.IsSame(celestial.Coordinate))
+ .Where(f => f.Origin.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(f => f.Origin.System == celestial.Coordinate.System)
+ .Where(f => f.Origin.Position == celestial.Coordinate.Position)
+ .Where(f => f.Origin.Type == (celestial.Coordinate.Type == Celestials.Planet ? Celestials.Moon : Celestials.Planet))
+ .ToList();
+ if (transportfleet2.Count() > 0)
+ interval = (long) (transportfleet2.First().BackIn * 1000) + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ }
+ }
if (interval == long.MaxValue || interval == long.MinValue)
interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Brain.AutoMine.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Brain.AutoMine.CheckIntervalMax);
@@ -467,9 +704,10 @@ private async Task CalcAutoMineTimer(Celestial celestial, Buildables build
} else {
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Buildings);
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Facilities);
+ celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.LFBonuses);
if (buildable != Buildables.Null) {
- var price = _calculationService.CalcPrice(buildable, level);
+ var price = _calculationService.CalcPrice(buildable, level, celestial.LFBonuses);
var productionTime = long.MaxValue;
var transportTime = long.MaxValue;
var returningExpoTime = long.MaxValue;
@@ -525,17 +763,19 @@ private async Task CalcAutoMineTimer(Celestial celestial, Buildables build
.Where(c => c.Coordinate.Position == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Position)
.Where(c => c.Coordinate.Type == Enum.Parse((string) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Type))
.SingleOrDefault() ?? new() { ID = 0 };
- var returningExpoOrigin = _calculationService.GetFirstReturningExpedition(origin.Coordinate, _tbotInstance.UserData.fleets);
- if (returningExpoOrigin != null) {
- returningExpoOriginTime = (long) (returningExpoOrigin.BackIn * 1000) + RandomizeHelper.CalcRandomInterval(IntervalType.AMinuteOrTwo);
- //DoLog(LogLevel.Debug, $"Next expedition returning in transport origin celestial by {now.AddMilliseconds(returningExpoOriginTime).ToString()}");
- }
+ if (origin.ID != 0) {
+ var returningExpoOrigin = _calculationService.GetFirstReturningExpedition(origin.Coordinate, _tbotInstance.UserData.fleets);
+ if (returningExpoOrigin != null) {
+ returningExpoOriginTime = (long) (returningExpoOrigin.BackIn * 1000) + RandomizeHelper.CalcRandomInterval(IntervalType.AMinuteOrTwo);
+ //DoLog(LogLevel.Debug, $"Next expedition returning in transport origin celestial by {now.AddMilliseconds(returningExpoOriginTime).ToString()}");
+ }
- var incomingOriginFleets = _calculationService.GetIncomingFleetsWithResources(origin, _tbotInstance.UserData.fleets);
- if (incomingOriginFleets.Any()) {
- var fleet = incomingOriginFleets.First();
- transportOriginTime = ((fleet.Mission == Missions.Transport || fleet.Mission == Missions.Deploy) && !fleet.ReturnFlight ? (long) fleet.ArriveIn : (long) fleet.BackIn) * 1000;
- //DoLog(LogLevel.Debug, $"Next fleet with resources arriving in transport origin celestial by {DateTime.Now.AddMilliseconds(transportOriginTime).ToString()}");
+ var incomingOriginFleets = _calculationService.GetIncomingFleetsWithResources(origin, _tbotInstance.UserData.fleets);
+ if (incomingOriginFleets.Any()) {
+ var fleet = incomingOriginFleets.First();
+ transportOriginTime = ((fleet.Mission == Missions.Transport || fleet.Mission == Missions.Deploy) && !fleet.ReturnFlight ? (long) fleet.ArriveIn : (long) fleet.BackIn) * 1000;
+ //DoLog(LogLevel.Debug, $"Next fleet with resources arriving in transport origin celestial by {DateTime.Now.AddMilliseconds(transportOriginTime).ToString()}");
+ }
}
}
diff --git a/TBot/Workers/Brain/AutoMineWorker.cs b/TBot/Workers/Brain/AutoMineWorker.cs
index 6e428459..0fe83276 100644
--- a/TBot/Workers/Brain/AutoMineWorker.cs
+++ b/TBot/Workers/Brain/AutoMineWorker.cs
@@ -110,13 +110,15 @@ protected override async Task Execute() {
cel = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.LFBuildings);
cel = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.LFBonuses);
Planet abaCelestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Fast) as Planet;
- var nextMine = _calculationService.GetNextMineToBuild(cel as Planet, _tbotInstance.UserData.researches, _tbotInstance.UserData.serverData.Speed, int.MaxValue, int.MaxValue, int.MaxValue, 1, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.staff.Geologist, _tbotInstance.UserData.staff.IsFull, true, int.MaxValue);
- var lv = _calculationService.GetNextLevel(cel, nextMine);
- var DOIR = _calculationService.CalcNextDaysOfInvestmentReturn(cel as Planet, _tbotInstance.UserData.researches, _tbotInstance.UserData.serverData.Speed, 1, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.staff.Geologist, _tbotInstance.UserData.staff.IsFull);
- DoLog(LogLevel.Debug, $"Celestial {cel.ToString()}: Next Mine: {nextMine.ToString()} lv {lv.ToString()}; DOIR: {DOIR.ToString()}.");
- if (DOIR < _tbotInstance.UserData.nextDOIR || _tbotInstance.UserData.nextDOIR == 0) {
- _tbotInstance.UserData.nextDOIR = DOIR;
- }
+ var nextMine = _calculationService.GetNextMineToBuild(cel as Planet, _tbotInstance.UserData.researches, _tbotInstance.UserData.serverData.Speed, maxBuildings.MetalMine, maxBuildings.CrystalMine, maxBuildings.DeuteriumSynthesizer, 1, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.staff.Geologist, _tbotInstance.UserData.staff.IsFull, true, int.MaxValue);
+ if (nextMine != Buildables.Null) {
+ var lv = _calculationService.GetNextLevel(cel, nextMine);
+ var DOIR = _calculationService.CalcNextDaysOfInvestmentReturn(cel as Planet, _tbotInstance.UserData.researches, _tbotInstance.UserData.serverData.Speed, 1, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.staff.Geologist, _tbotInstance.UserData.staff.IsFull);
+ DoLog(LogLevel.Debug, $"Celestial {cel.ToString()}: Next Mine: {nextMine.ToString()} lv {lv.ToString()}; DOIR: {DOIR.ToString()}.");
+ if (DOIR < _tbotInstance.UserData.nextDOIR || _tbotInstance.UserData.nextDOIR == 0) {
+ _tbotInstance.UserData.nextDOIR = DOIR;
+ }
+ }
if (celestial.Coordinate.Type == Celestials.Planet && celestial.Fields.Built == 0 && (bool) _tbotInstance.InstanceSettings.AutoColonize.Abandon.Active) {
if (_calculationService.ShouldAbandon(celestial as Planet, celestial.Fields.Total, abaCelestial.Temperature.Max, fieldsSettings, temperaturesSettings)) {
DoLog(LogLevel.Debug, $"Skipping {celestial.ToString()}: planet should be abandoned.");
diff --git a/TBot/Workers/Brain/AutoResearchWorker.cs b/TBot/Workers/Brain/AutoResearchWorker.cs
index b61f57d4..6476d364 100644
--- a/TBot/Workers/Brain/AutoResearchWorker.cs
+++ b/TBot/Workers/Brain/AutoResearchWorker.cs
@@ -41,6 +41,7 @@ protected override async Task Execute() {
DoLog(LogLevel.Information, "Running autoresearch...");
_tbotInstance.UserData.researches = await _ogameService.GetResearches();
+ _tbotInstance.UserData.celestials = await _tbotOgameBridge.UpdateCelestials();
_tbotInstance.UserData.celestials = await _tbotOgameBridge.UpdatePlanets(UpdateTypes.LFBonuses);
Planet celestial;
var parseSucceded = _tbotInstance.UserData.celestials
@@ -86,9 +87,12 @@ protected override async Task Execute() {
return;
}
_tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
+ celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Buildings) as Planet;
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Facilities) as Planet;
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Resources) as Planet;
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.ResourcesProduction) as Planet;
+ celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.LFBonuses) as Planet;
+ celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Ships) as Planet;
Buildables research;
@@ -133,10 +137,10 @@ protected override async Task Execute() {
) {
research = Buildables.Astrophysics;
} else {
- research = _calculationService.GetNextResearchToBuild(celestial as Planet, _tbotInstance.UserData.researches, (bool) _tbotInstance.InstanceSettings.Brain.AutoMine.PrioritizeRobotsAndNanites, _tbotInstance.UserData.slots, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxEnergyTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxLaserTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxIonTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxHyperspaceTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxPlasmaTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxCombustionDrive, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxImpulseDrive, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxHyperspaceDrive, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxEspionageTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxComputerTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxAstrophysics, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxIntergalacticResearchNetwork, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxWeaponsTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxShieldingTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxArmourTechnology, (bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.OptimizeForStart, (bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.EnsureExpoSlots, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.staff.Geologist, _tbotInstance.UserData.staff.Admiral);
+ research = _calculationService.GetNextResearchToBuild(celestial as Planet, _tbotInstance.UserData.researches, (bool) _tbotInstance.InstanceSettings.Brain.AutoMine.PrioritizeRobotsAndNanites, _tbotInstance.UserData.slots, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxEnergyTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxLaserTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxIonTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxHyperspaceTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxPlasmaTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxCombustionDrive, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxImpulseDrive, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxHyperspaceDrive, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxEspionageTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxComputerTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxAstrophysics, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxIntergalacticResearchNetwork, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxWeaponsTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxShieldingTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxArmourTechnology, (bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.OptimizeForStart, (bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.EnsureExpoSlots, (bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.ForceResearchWhateverTheLabLevel, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.staff.Geologist, _tbotInstance.UserData.staff.Admiral);
}
} else {
- research = _calculationService.GetNextResearchToBuild(celestial as Planet, _tbotInstance.UserData.researches, (bool) _tbotInstance.InstanceSettings.Brain.AutoMine.PrioritizeRobotsAndNanites, _tbotInstance.UserData.slots, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxEnergyTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxLaserTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxIonTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxHyperspaceTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxPlasmaTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxCombustionDrive, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxImpulseDrive, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxHyperspaceDrive, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxEspionageTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxComputerTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxAstrophysics, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxIntergalacticResearchNetwork, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxWeaponsTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxShieldingTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxArmourTechnology, (bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.OptimizeForStart, (bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.EnsureExpoSlots, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.staff.Geologist, _tbotInstance.UserData.staff.Admiral);
+ research = _calculationService.GetNextResearchToBuild(celestial as Planet, _tbotInstance.UserData.researches, (bool) _tbotInstance.InstanceSettings.Brain.AutoMine.PrioritizeRobotsAndNanites, _tbotInstance.UserData.slots, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxEnergyTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxLaserTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxIonTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxHyperspaceTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxPlasmaTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxCombustionDrive, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxImpulseDrive, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxHyperspaceDrive, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxEspionageTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxComputerTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxAstrophysics, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxIntergalacticResearchNetwork, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxWeaponsTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxShieldingTechnology, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.MaxArmourTechnology, (bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.OptimizeForStart, (bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.EnsureExpoSlots, (bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.ForceResearchWhateverTheLabLevel, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.staff.Geologist, _tbotInstance.UserData.staff.Admiral);
}
if (
@@ -160,7 +164,9 @@ protected override async Task Execute() {
int level = _calculationService.GetNextLevel(_tbotInstance.UserData.researches, research);
if (research != Buildables.Null) {
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Resources) as Planet;
- Resources cost = _calculationService.CalcPrice(research, level);
+ celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Ships) as Planet;
+ celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.LFBonuses) as Planet;
+ Resources cost = _calculationService.CalcPrice(research, level, celestial.LFBonuses);
if (celestial.Resources.IsEnoughFor(cost)) {
try {
await _ogameService.BuildCancelable(celestial, research);
@@ -171,35 +177,248 @@ protected override async Task Execute() {
} else {
DoLog(LogLevel.Information, $"Not enough resources to build: {research.ToString()} level {level.ToString()} on {celestial.ToString()}. Needed: {cost.TransportableResources} - Available: {celestial.Resources.TransportableResources}");
if ((bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.Transports.Active && (bool) _tbotInstance.InstanceSettings.Brain.Transports.Active) {
+ _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
- if (!_calculationService.IsThereTransportTowardsCelestial(celestial, _tbotInstance.UserData.fleets)) {
- Celestial origin = _tbotInstance.UserData.celestials
- .Unique()
- .Where(c => c.Coordinate.Galaxy == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Galaxy)
- .Where(c => c.Coordinate.System == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.System)
- .Where(c => c.Coordinate.Position == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Position)
- .Where(c => c.Coordinate.Type == Enum.Parse((string) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Type))
- .SingleOrDefault() ?? new() { ID = 0 };
- fleetId = await _fleetScheduler.HandleMinerTransport(origin, celestial, cost, Buildables.Null);
- if (fleetId == (int) SendFleetCode.AfterSleepTime) {
- stop = true;
- }
- if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
- delay = true;
+ List rankSlotsPriority = new() {
+ new RankSlotsPriority(Feature.BrainAutoResearch,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Brain,
+ ((bool) _tbotInstance.InstanceSettings.Brain.Active && (bool) _tbotInstance.InstanceSettings.Brain.Transports.Active && ((bool) _tbotInstance.InstanceSettings.Brain.AutoMine.Active || (bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.Active || (bool) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.Active || (bool) _tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.Active)),
+ (int) _tbotInstance.InstanceSettings.Brain.Transports.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Transport)),
+ new RankSlotsPriority(Feature.Expeditions,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Expeditions,
+ (bool) _tbotInstance.InstanceSettings.Expeditions.Active,
+ (int) _tbotInstance.UserData.slots.ExpTotal,
+ (int)_tbotInstance.UserData.slots.ExpInUse),
+ new RankSlotsPriority(Feature.AutoFarm,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoFarm,
+ (bool) _tbotInstance.InstanceSettings.AutoFarm.Active,
+ (int) _tbotInstance.InstanceSettings.AutoFarm.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Attack)),
+ new RankSlotsPriority(Feature.Colonize,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Colonize,
+ (bool) _tbotInstance.InstanceSettings.AutoColonize.Active,
+ (bool) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.Active ?
+ (int) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.MaxSlots :
+ 1,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Colonize)),
+ new RankSlotsPriority(Feature.AutoDiscovery,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoDiscovery,
+ (bool) _tbotInstance.InstanceSettings.AutoDiscovery.Active,
+ (int) _tbotInstance.InstanceSettings.AutoDiscovery.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Discovery)),
+ new RankSlotsPriority(Feature.Harvest,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoHarvest,
+ (bool) _tbotInstance.InstanceSettings.AutoHarvest.Active,
+ (int) _tbotInstance.InstanceSettings.AutoHarvest.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Harvest))
+ };
+ int MaxSlots = _calculationService.CalcSlotsPriority(Feature.BrainAutoResearch, rankSlotsPriority, _tbotInstance.UserData.slots, _tbotInstance.UserData.fleets, (int) _tbotInstance.InstanceSettings.General.SlotsToLeaveFree);
+
+ if (MaxSlots > 0) {
+ if (!_calculationService.IsThereTransportTowardsCelestial(celestial, _tbotInstance.UserData.fleets)) {
+ Celestial origin = new() { ID = 0 };
+ List allCelestials = _tbotInstance.UserData.celestials;
+ for (int i = 0; i < allCelestials.Count(); i++) {
+ allCelestials[i] = await _tbotOgameBridge.UpdatePlanet(allCelestials[i], UpdateTypes.Resources);
+ allCelestials[i] = await _tbotOgameBridge.UpdatePlanet(allCelestials[i], UpdateTypes.Ships);
+ allCelestials[i] = await _tbotOgameBridge.UpdatePlanet(allCelestials[i], UpdateTypes.LFBonuses);
+ }
+ Resources missingResources = cost.Difference(celestial.Resources);
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.CheckMoonOrPlanetFirst) {
+ if (celestial.Coordinate.Type == Celestials.Planet && ((bool) _tbotInstance.InstanceSettings.Brain.Transports.CheckMoonOrPlanetFirst && _calculationService.IsThereMoonHere(allCelestials, celestial))) {
+ origin = allCelestials.Unique()
+ .Where(c => c.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(c => c.Coordinate.System == celestial.Coordinate.System)
+ .Where(c => c.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(c => c.Coordinate.Type == Celestials.Moon)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ }
+ if (celestial.Coordinate.Type == Celestials.Moon) {
+ origin = allCelestials.Unique()
+ .Where(c => c.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(c => c.Coordinate.System == celestial.Coordinate.System)
+ .Where(c => c.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(c => c.Coordinate.Type == Celestials.Planet)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ }
+ if (origin.ID != 0) {
+ if (origin.Resources.IsEnoughFor(missingResources)) {
+ missingResources = _tbotInstance.InstanceSettings.Brain.Transports.RoundResources ? missingResources.Round() : missingResources;
+ Buildables preferredShip = Buildables.SmallCargo;
+ if (!Enum.TryParse((string) _tbotInstance.InstanceSettings.Brain.Transports.CargoType, true, out preferredShip)) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, "Unable to parse CargoType. Falling back to default SmallCargo");
+ preferredShip = Buildables.SmallCargo;
+ }
+ long shipsNeeded = _calculationService.CalcShipNumberForPayload(missingResources, preferredShip, _tbotInstance.UserData.researches.HyperspaceTechnology, _tbotInstance.UserData.serverData, origin.LFBonuses.GetShipCargoBonus(preferredShip), _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo);
+ if (origin.Ships.GetAmount(preferredShip) >= shipsNeeded) {
+ Ships ships = new();
+ ships.Add((Buildables) preferredShip, shipsNeeded);
+ fleetId = await _fleetScheduler.SendFleet(origin, ships, celestial.Coordinate, Missions.Transport, Speeds.HundredPercent, missingResources);
+ if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ stop = true;
+ }
+ if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ delay = true;
+ }
+ } else if (_tbotInstance.InstanceSettings.Brain.Transports.DoMultipleTransportIsNotEnoughShipButSamePosition) {
+ DoLog(LogLevel.Warning, $"Not enough Cargo available. Multiple transports will take place.");
+ Ships ships = new();
+ ships.Add((Buildables) preferredShip, origin.Ships.GetAmount(preferredShip));
+ Resources transportableResources = _calculationService.CalcMaxTransportableResources(ships, missingResources, _tbotInstance.UserData.researches.HyperspaceTechnology, _tbotInstance.UserData.serverData, origin.LFBonuses, _tbotInstance.UserData.userInfo.Class, 0, _tbotInstance.UserData.serverData.ProbeCargo);
+ fleetId = await _fleetScheduler.SendFleet(origin, ships, celestial.Coordinate, Missions.Transport, Speeds.HundredPercent, transportableResources);
+ if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ stop = true;
+ }
+ if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ delay = true;
+ }
+ } else {
+ DoLog(LogLevel.Warning, $"Not enough Cargo available. Skipping CheckMoonOrPlanetFirst.");
+ }
+ } else {
+ DoLog(LogLevel.Warning, $"Not enough resources available on {origin.ToString()} only to send resources to {celestial.ToString()}.");
+ }
+ } else {
+ DoLog(LogLevel.Warning, $"No Moon available on {celestial.ToString()}.");
+ }
+ }
+
+ if (fleetId <= 0) {
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.Active) {
+ origin = _tbotInstance.UserData.celestials
+ .Unique()
+ .Where(c => c.Coordinate.Galaxy == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Galaxy)
+ .Where(c => c.Coordinate.System == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.System)
+ .Where(c => c.Coordinate.Position == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Position)
+ .Where(c => c.Coordinate.Type == Enum.Parse((string) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Type))
+ .SingleOrDefault() ?? new() { ID = 0 };
+ List celestialsToExclude = _calculationService.ParseCelestialsList(_tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.Exclude, allCelestials);
+ Buildables preferredShip = Buildables.SmallCargo;
+ if (!Enum.TryParse((string) _tbotInstance.InstanceSettings.Brain.Transports.CargoType, true, out preferredShip)) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, "Unable to parse CargoType. Falling back to default SmallCargo");
+ preferredShip = Buildables.SmallCargo;
+ }
+ TransportSettings transportsSettings = new((bool) _tbotInstance.InstanceSettings.Brain.Transports.Active,
+ preferredShip,
+ (long) _tbotInstance.InstanceSettings.Brain.Transports.DeutToLeaveOnMoons,
+ (bool)_tbotInstance.InstanceSettings.Brain.Transports.RoundResources,
+ (bool)_tbotInstance.InstanceSettings.Brain.Transports.SendToTheMoonIfPossible,
+ origin,
+ (long) _tbotInstance.InstanceSettings.Brain.Transports.MaxSlots,
+ (bool)_tbotInstance.InstanceSettings.Brain.Transports.CheckMoonOrPlanetFirst,
+ (bool)_tbotInstance.InstanceSettings.Brain.Transports.DoMultipleTransportIsNotEnoughShipButSamePosition,
+ new MultipleOrigins((bool) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.Active,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.OnlyFromMoons,
+ (long) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.MinimumResourcesToSend,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.PriorityToProximityOverQuantity,
+ celestialsToExclude)
+ );
+
+ Celestial destination;
+ if ((bool) transportsSettings.SendToTheMoonIfPossible && _calculationService.IsThereMoonHere(allCelestials, celestial)) {
+ destination = allCelestials
+ .Unique()
+ .Where(planet => planet.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(planet => planet.Coordinate.System == celestial.Coordinate.System)
+ .Where(planet => planet.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(planet => planet.Coordinate.Type == Celestials.Moon)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ if (destination.Ships.IsEmpty() || celestial.Resources.TotalResources == 0)
+ destination = celestial;
+ } else {
+ destination = celestial;
+ }
+
+ var resultOrigins = _calculationService.CalcMultipleOrigin(celestial, allCelestials, missingResources, transportsSettings, _tbotInstance.UserData.fleets, _tbotInstance.UserData);
+
+ if (resultOrigins.Count() == 0) {
+ DoLog(LogLevel.Information, $"No origin is available. This may be due to a lack of resources or cargo.");
+ return;
+ }
+ if (resultOrigins.Count() > MaxSlots) {
+ DoLog(LogLevel.Information, $"Not enough slots available to send all resources to build: {research.ToString()} level {level.ToString()} on {celestial.ToString()}. Slots needed: {resultOrigins.Count().ToString()}/{MaxSlots}.");
+ delay = true;
+ return;
+ }
+
+ Ships ships = new();
+
+ foreach (var item in resultOrigins) {
+ ships = new();
+ ships.Add((Buildables) transportsSettings.CargoType, _calculationService.CalcShipNumberForPayload(item.FirstOrDefault().Value, (Buildables) transportsSettings.CargoType, _tbotInstance.UserData.researches.HyperspaceTechnology, _tbotInstance.UserData.serverData, celestial.LFBonuses.GetShipCargoBonus(transportsSettings.CargoType), _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo));
+ if (item.FirstOrDefault().Key.Coordinate.IsSame(destination.Coordinate) && transportsSettings.SendToTheMoonIfPossible && destination.Coordinate.Type == Celestials.Moon)
+ fleetId= await _fleetScheduler.SendFleet(item.FirstOrDefault().Key, ships, celestial.Coordinate, Missions.Transport, Speeds.HundredPercent, item.FirstOrDefault().Value);
+ else
+ fleetId= await _fleetScheduler.SendFleet(item.FirstOrDefault().Key, ships, destination.Coordinate, Missions.Transport, Speeds.HundredPercent, item.FirstOrDefault().Value);
+
+ if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ stop = true;
+ return;
+ }
+ if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ delay = true;
+ return;
+ }
+ }
+ } else {
+ Celestial destination;
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.SendToTheMoonIfPossible && celestial.Coordinate.Type == Celestials.Planet && _calculationService.IsThereMoonHere(_tbotInstance.UserData.celestials, celestial) && (!celestial.Ships.IsEmpty() || celestial.Resources.TotalResources > 0)) {
+ destination = allCelestials
+ .Unique()
+ .Where(planet => planet.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(planet => planet.Coordinate.System == celestial.Coordinate.System)
+ .Where(planet => planet.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(planet => planet.Coordinate.Type == Celestials.Moon)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ if (destination.Ships.IsEmpty() || celestial.Resources.TotalResources == 0)
+ destination = celestial;
+ cost = cost.Difference(destination.Resources);
+ } else {
+ destination = allCelestials
+ .Unique()
+ .Where(planet => planet.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(planet => planet.Coordinate.System == celestial.Coordinate.System)
+ .Where(planet => planet.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(planet => planet.Coordinate.Type == celestial.Coordinate.Type)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ }
+ origin = allCelestials
+ .Unique()
+ .Where(c => c.Coordinate.Galaxy == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Galaxy)
+ .Where(c => c.Coordinate.System == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.System)
+ .Where(c => c.Coordinate.Position == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Position)
+ .Where(c => c.Coordinate.Type == Enum.Parse((string) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Type))
+ .SingleOrDefault() ?? new() { ID = 0 };
+ fleetId = await _fleetScheduler.HandleMinerTransport(origin, celestial, destination, cost, Buildables.Null);
+ if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ stop = true;
+ }
+ if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ delay = true;
+ }
+ }
+ }
+ } else {
+ DoLog(LogLevel.Information, $"Skipping transport: there is already a transport incoming in {celestial.ToString()}");
+ fleetId = _tbotInstance.UserData.fleets.Where(f => f.Mission == Missions.Transport)
+ .Where(f => f.Resources.TotalResources > 0)
+ .Where(f => f.ReturnFlight == false)
+ .Where(f => f.Destination.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(f => f.Destination.System == celestial.Coordinate.System)
+ .Where(f => f.Destination.Position == celestial.Coordinate.Position)
+ .Where(f => f.Destination.Type == celestial.Coordinate.Type)
+ .First().ID;
}
} else {
- DoLog(LogLevel.Information, $"Skipping transport: there is already a transport incoming in {celestial.ToString()}");
- fleetId = (_tbotInstance.UserData.fleets
- .Where(f => f.Mission == Missions.Transport)
- .Where(f => f.Resources.TotalResources > 0)
- .Where(f => f.ReturnFlight == false)
- .Where(f => f.Destination.Galaxy == celestial.Coordinate.Galaxy)
- .Where(f => f.Destination.System == celestial.Coordinate.System)
- .Where(f => f.Destination.Position == celestial.Coordinate.Position)
- .Where(f => f.Destination.Type == celestial.Coordinate.Type)
- .OrderByDescending(f => f.ArriveIn)
- .FirstOrDefault() ?? new() { ID = 0 })
- .ID;
+ if (_tbotInstance.InstanceSettings.General.SlotPriorityLevel.Brain > 0) {
+ if (_tbotInstance.InstanceSettings.Brain.Transports.MaxSlots == 0)
+ DoLog(LogLevel.Information, $"Transports.MaxSlots is set to 0, you should increase it.");
+ else
+ DoLog(LogLevel.Information, $"0 slots available.");
+ }
+ DoLog(LogLevel.Information, $"Not enough slots available for Transports.MaxSlots, delaying.");
+ delay = true;
}
}
}
@@ -218,7 +437,7 @@ protected override async Task Execute() {
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
long interval;
try {
- interval = (_tbotInstance.UserData.fleets.OrderBy(f => f.BackIn).First().BackIn ?? 0) * 1000 + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ interval = (_tbotInstance.UserData.fleets.Where(f => f.Mission == Missions.Transport).OrderBy(f => f.BackIn).First().BackIn ?? 0) * 1000 + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
} catch {
interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.AutoResearch.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.AutoResearch.CheckIntervalMax);
}
@@ -261,7 +480,25 @@ protected override async Task Execute() {
.First();
interval = (((fleet.Mission == Missions.Transport || fleet.Mission == Missions.Deploy) ? (long) fleet.ArriveIn : (long) fleet.BackIn) * 1000) + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
} else {
- interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Brain.AutoResearch.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.CheckIntervalMax);
+ if (fleetId > 0) {
+ _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
+ var transportfleet = _tbotInstance.UserData.fleets.Single(f => f.ID == fleetId && f.Mission == Missions.Transport);
+ interval = (transportfleet.ArriveIn * 1000) + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ } else {
+ interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Brain.AutoResearch.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Brain.AutoResearch.CheckIntervalMax);
+ }
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.DoMultipleTransportIsNotEnoughShipButSamePosition) {
+ var transportfleet2 = _tbotInstance.UserData.fleets.Where(f => f.Mission == Missions.Transport)
+ .Where(f => f.Destination.IsSame(celestial.Coordinate))
+ .Where(f => f.Origin.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(f => f.Origin.System == celestial.Coordinate.System)
+ .Where(f => f.Origin.Position == celestial.Coordinate.Position)
+ .Where(f => f.Origin.Type == (celestial.Coordinate.Type == Celestials.Planet ? Celestials.Moon : Celestials.Planet))
+ .ToList();
+ if (transportfleet2.Count() > 0) {
+ interval = (long) (transportfleet2.First().BackIn * 1000) + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ }
+ }
}
}
if (interval <= 0)
diff --git a/TBot/Workers/Brain/BuyOfferOfTheDayWorker.cs b/TBot/Workers/Brain/BuyOfferOfTheDayWorker.cs
index 7b7255e5..16f7abb5 100644
--- a/TBot/Workers/Brain/BuyOfferOfTheDayWorker.cs
+++ b/TBot/Workers/Brain/BuyOfferOfTheDayWorker.cs
@@ -25,8 +25,6 @@ public BuyOfferOfTheDayWorker(ITBotMain parentInstance,
_tbotOgameBridge = tbotOgameBridge;
}
protected override async Task Execute() {
- bool stop = true;
-
_tbotInstance.log(LogLevel.Information, GetLogSender(), "Buying offer of the day...");
OfferOfTheDayStatus sts = await _ogameService.BuyOfferOfTheDay();
@@ -36,23 +34,16 @@ protected override async Task Execute() {
_tbotInstance.log(LogLevel.Information, GetLogSender(), "Offer of the day already bought.");
} else {
_tbotInstance.log(LogLevel.Information, GetLogSender(), "Error buying Offer of the day. Already bought?");
- stop = false;
}
-
- if (stop) {
- _tbotInstance.log(LogLevel.Information, GetLogSender(), $"Stopping BuyOfferOfTheDay.");
- await EndExecution();
- } else {
- var time = await _tbotOgameBridge.GetDateTime();
- var interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Brain.BuyOfferOfTheDay.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Brain.BuyOfferOfTheDay.CheckIntervalMax);
- if (interval <= 0)
- interval = RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
- var newTime = time.AddMilliseconds(interval);
- ChangeWorkerPeriod(interval);
- _tbotInstance.log(LogLevel.Information, GetLogSender(), $"Next BuyOfferOfTheDay check at {newTime.ToString()}");
- await _tbotOgameBridge.CheckCelestials();
- }
+ var time = await _tbotOgameBridge.GetDateTime();
+ var interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Brain.BuyOfferOfTheDay.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Brain.BuyOfferOfTheDay.CheckIntervalMax);
+ if (interval <= 0)
+ interval = RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ var newTime = time.AddMilliseconds(interval);
+ ChangeWorkerPeriod(interval);
+ _tbotInstance.log(LogLevel.Information, GetLogSender(), $"Next BuyOfferOfTheDay check at {newTime.ToString()}");
+ await _tbotOgameBridge.CheckCelestials();
}
public override bool IsWorkerEnabledBySettings() {
try {
diff --git a/TBot/Workers/Brain/LifeformsAutoMineCelestialWorker.cs b/TBot/Workers/Brain/LifeformsAutoMineCelestialWorker.cs
index 63e285f3..cc68cff9 100644
--- a/TBot/Workers/Brain/LifeformsAutoMineCelestialWorker.cs
+++ b/TBot/Workers/Brain/LifeformsAutoMineCelestialWorker.cs
@@ -101,6 +101,8 @@ private async Task LifeformAutoMineCelestial(Celestial celestial) {
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.LFBuildings);
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Buildings);
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Constructions);
+ celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.LFBonuses);
+ celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Ships);
bool preventTechBuilding = false;
if (celestial.Constructions.LFResearchCountdown > 0) {
@@ -180,37 +182,240 @@ private async Task LifeformAutoMineCelestial(Celestial celestial) {
DoLog(LogLevel.Information, $"Not enough resources to build: {buildable.ToString()} level {level.ToString()} on {celestial.ToString()}. Needed: {xCostBuildable.LFBuildingCostResources.ToString()} - Available: {celestial.Resources.LFBuildingCostResources.ToString()}");
if ((bool) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.Transports.Active && (bool) _tbotInstance.InstanceSettings.Brain.Transports.Active) {
+ _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
- if (!_calculationService.IsThereTransportTowardsCelestial(celestial, _tbotInstance.UserData.fleets)) {
- Celestial origin = _tbotInstance.UserData.celestials
- .Unique()
- .Where(c => c.Coordinate.Galaxy == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Galaxy)
- .Where(c => c.Coordinate.System == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.System)
- .Where(c => c.Coordinate.Position == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Position)
- .Where(c => c.Coordinate.Type == Enum.Parse((string) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Type))
- .SingleOrDefault() ?? new() { ID = 0 };
- fleetId = await _fleetScheduler.HandleMinerTransport(origin, celestial, xCostBuildable, buildable, maxLFBuildings, preventIfMoreExpensiveThanNextMine);
- if (fleetId == (int) SendFleetCode.AfterSleepTime) {
- stop = true;
- return;
- }
- if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
- delay = true;
- return;
+ List rankSlotsPriority = new() {
+ new RankSlotsPriority(Feature.BrainLifeformAutoMine,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Brain,
+ ((bool) _tbotInstance.InstanceSettings.Brain.Active && (bool) _tbotInstance.InstanceSettings.Brain.Transports.Active && ((bool) _tbotInstance.InstanceSettings.Brain.AutoMine.Active || (bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.Active || (bool) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.Active || (bool) _tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.Active)),
+ (int) _tbotInstance.InstanceSettings.Brain.Transports.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Transport)),
+ new RankSlotsPriority(Feature.Expeditions,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Expeditions,
+ (bool) _tbotInstance.InstanceSettings.Expeditions.Active,
+ (int) _tbotInstance.UserData.slots.ExpTotal,
+ (int)_tbotInstance.UserData.slots.ExpInUse),
+ new RankSlotsPriority(Feature.AutoFarm,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoFarm,
+ (bool) _tbotInstance.InstanceSettings.AutoFarm.Active,
+ (int) _tbotInstance.InstanceSettings.AutoFarm.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Attack)),
+ new RankSlotsPriority(Feature.Colonize,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Colonize,
+ (bool) _tbotInstance.InstanceSettings.AutoColonize.Active,
+ (bool) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.Active ?
+ (int) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.MaxSlots :
+ 1,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Colonize)),
+ new RankSlotsPriority(Feature.AutoDiscovery,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoDiscovery,
+ (bool) _tbotInstance.InstanceSettings.AutoDiscovery.Active,
+ (int) _tbotInstance.InstanceSettings.AutoDiscovery.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Discovery)),
+ new RankSlotsPriority(Feature.Harvest,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoHarvest,
+ (bool) _tbotInstance.InstanceSettings.AutoHarvest.Active,
+ (int) _tbotInstance.InstanceSettings.AutoHarvest.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Harvest))
+ };
+ int MaxSlots = _calculationService.CalcSlotsPriority(Feature.BrainLifeformAutoMine, rankSlotsPriority, _tbotInstance.UserData.slots, _tbotInstance.UserData.fleets, (int) _tbotInstance.InstanceSettings.General.SlotsToLeaveFree);
+
+ if (MaxSlots > 0) {
+ if (!_calculationService.IsThereTransportTowardsCelestial(celestial, _tbotInstance.UserData.fleets)) {
+ Celestial origin = new() { ID = 0 };
+ List allCelestials = _tbotInstance.UserData.celestials;
+ for (int i = 0; i < allCelestials.Count(); i++) {
+ allCelestials[i] = await _tbotOgameBridge.UpdatePlanet(allCelestials[i], UpdateTypes.Resources);
+ allCelestials[i] = await _tbotOgameBridge.UpdatePlanet(allCelestials[i], UpdateTypes.Ships);
+ allCelestials[i] = await _tbotOgameBridge.UpdatePlanet(allCelestials[i], UpdateTypes.LFBonuses);
+ }
+ Resources missingResources = xCostBuildable.Difference(celestial.Resources);
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.CheckMoonOrPlanetFirst) {
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.CheckMoonOrPlanetFirst && _calculationService.IsThereMoonHere(allCelestials, celestial)) {
+ origin = allCelestials.Unique()
+ .Where(c => c.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(c => c.Coordinate.System == celestial.Coordinate.System)
+ .Where(c => c.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(c => c.Coordinate.Type == Celestials.Moon)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ }
+ if (origin.ID != 0) {
+ if (origin.Resources.IsEnoughFor(missingResources)) {
+ missingResources = _tbotInstance.InstanceSettings.Brain.Transports.RoundResources ? missingResources.Round() : missingResources;
+ Buildables preferredShip = Buildables.SmallCargo;
+ if (!Enum.TryParse((string) _tbotInstance.InstanceSettings.Brain.Transports.CargoType, true, out preferredShip)) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, "Unable to parse CargoType. Falling back to default SmallCargo");
+ preferredShip = Buildables.SmallCargo;
+ }
+ long shipsNeeded = _calculationService.CalcShipNumberForPayload(missingResources, preferredShip, _tbotInstance.UserData.researches.HyperspaceTechnology, _tbotInstance.UserData.serverData, origin.LFBonuses.GetShipCargoBonus(preferredShip), _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo);
+ if (origin.Ships.GetAmount(preferredShip) >= shipsNeeded) {
+ Ships ships = new();
+ ships.Add((Buildables) preferredShip, shipsNeeded);
+ fleetId = await _fleetScheduler.SendFleet(origin, ships, celestial.Coordinate, Missions.Transport, Speeds.HundredPercent, missingResources);
+ if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ stop = true;
+ }
+ if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ delay = true;
+ }
+ } else if (_tbotInstance.InstanceSettings.Brain.Transports.DoMultipleTransportIsNotEnoughShipButSamePosition) {
+ DoLog(LogLevel.Warning, $"Not enough Cargo available. Multiple transports will take place.");
+ Ships ships = new();
+ ships.Add((Buildables) preferredShip, origin.Ships.GetAmount(preferredShip));
+ Resources transportableResources = _calculationService.CalcMaxTransportableResources(ships, missingResources, _tbotInstance.UserData.researches.HyperspaceTechnology, _tbotInstance.UserData.serverData, origin.LFBonuses, _tbotInstance.UserData.userInfo.Class, 0, _tbotInstance.UserData.serverData.ProbeCargo);
+ fleetId = await _fleetScheduler.SendFleet(origin, ships, celestial.Coordinate, Missions.Transport, Speeds.HundredPercent, transportableResources);
+ if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ stop = true;
+ }
+ if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ delay = true;
+ }
+ } else {
+ DoLog(LogLevel.Warning, $"Not enough Cargo available. Skipping CheckMoonOrPlanetFirst.");
+ }
+ } else {
+ DoLog(LogLevel.Warning, $"Not enough resources available on {origin.ToString()} only to send resources to {celestial.ToString()}.");
+ }
+ } else {
+ DoLog(LogLevel.Warning, $"No Moon available on {celestial.ToString()}.");
+ }
+ }
+
+ if (fleetId <= 0) {
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.Active) {
+ origin = _tbotInstance.UserData.celestials
+ .Unique()
+ .Where(c => c.Coordinate.Galaxy == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Galaxy)
+ .Where(c => c.Coordinate.System == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.System)
+ .Where(c => c.Coordinate.Position == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Position)
+ .Where(c => c.Coordinate.Type == Enum.Parse((string) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Type))
+ .SingleOrDefault() ?? new() { ID = 0 };
+ List celestialsToExclude = _calculationService.ParseCelestialsList(_tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.Exclude, allCelestials);
+ Buildables preferredShip = Buildables.SmallCargo;
+ if (!Enum.TryParse((string) _tbotInstance.InstanceSettings.Brain.Transports.CargoType, true, out preferredShip)) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, "Unable to parse CargoType. Falling back to default SmallCargo");
+ preferredShip = Buildables.SmallCargo;
+ }
+ TransportSettings transportsSettings = new((bool) _tbotInstance.InstanceSettings.Brain.Transports.Active,
+ preferredShip,
+ (long) _tbotInstance.InstanceSettings.Brain.Transports.DeutToLeaveOnMoons,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.RoundResources,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.SendToTheMoonIfPossible,
+ origin,
+ (long) _tbotInstance.InstanceSettings.Brain.Transports.MaxSlots,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.CheckMoonOrPlanetFirst,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.DoMultipleTransportIsNotEnoughShipButSamePosition,
+ new MultipleOrigins((bool) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.Active,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.OnlyFromMoons,
+ (long) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.MinimumResourcesToSend,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.PriorityToProximityOverQuantity,
+ celestialsToExclude)
+ );
+
+ Celestial destination;
+ if ((bool) transportsSettings.SendToTheMoonIfPossible && _calculationService.IsThereMoonHere(allCelestials, celestial)) {
+ destination = allCelestials
+ .Unique()
+ .Where(planet => planet.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(planet => planet.Coordinate.System == celestial.Coordinate.System)
+ .Where(planet => planet.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(planet => planet.Coordinate.Type == Celestials.Moon)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ if (destination.Ships.IsEmpty() || celestial.Resources.TotalResources == 0)
+ destination = celestial;
+ } else {
+ destination = celestial;
+ }
+
+ var resultOrigins = _calculationService.CalcMultipleOrigin(celestial, allCelestials, missingResources, transportsSettings, _tbotInstance.UserData.fleets, _tbotInstance.UserData);
+
+ if (resultOrigins.Count() == 0) {
+ DoLog(LogLevel.Information, $"No origin is available. This may be due to a lack of resources or cargo.");
+ return;
+ }
+ if (resultOrigins.Count() > MaxSlots) {
+ DoLog(LogLevel.Information, $"Not enough slots available to send all resources to build: {buildable.ToString()} level {level.ToString()} on {celestial.ToString()}. Slots needed: {resultOrigins.Count().ToString()}/{MaxSlots}.");
+ delay = true;
+ return;
+ }
+
+ Ships ships = new();
+
+ foreach (var item in resultOrigins) {
+ ships = new();
+ ships.Add((Buildables) transportsSettings.CargoType, _calculationService.CalcShipNumberForPayload(item.FirstOrDefault().Value, (Buildables) transportsSettings.CargoType, _tbotInstance.UserData.researches.HyperspaceTechnology, _tbotInstance.UserData.serverData, celestial.LFBonuses.GetShipCargoBonus(transportsSettings.CargoType), _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo));
+ if (item.FirstOrDefault().Key.Coordinate.IsSame(destination.Coordinate) && transportsSettings.SendToTheMoonIfPossible && destination.Coordinate.Type == Celestials.Moon)
+ fleetId= await _fleetScheduler.SendFleet(item.FirstOrDefault().Key, ships, celestial.Coordinate, Missions.Transport, Speeds.HundredPercent, item.FirstOrDefault().Value);
+ else
+ fleetId= await _fleetScheduler.SendFleet(item.FirstOrDefault().Key, ships, destination.Coordinate, Missions.Transport, Speeds.HundredPercent, item.FirstOrDefault().Value);
+
+ if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ stop = true;
+ return;
+ }
+ if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ delay = true;
+ return;
+ }
+ }
+ } else {
+ Celestial destination;
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.SendToTheMoonIfPossible && _calculationService.IsThereMoonHere(_tbotInstance.UserData.celestials, celestial)) {
+ destination = allCelestials
+ .Unique()
+ .Where(planet => planet.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(planet => planet.Coordinate.System == celestial.Coordinate.System)
+ .Where(planet => planet.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(planet => planet.Coordinate.Type == Celestials.Moon)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ if (destination.Ships.IsEmpty() || celestial.Resources.TotalResources == 0)
+ destination = celestial;
+ xCostBuildable = xCostBuildable.Difference(destination.Resources);
+ } else {
+ destination = allCelestials
+ .Unique()
+ .Where(planet => planet.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(planet => planet.Coordinate.System == celestial.Coordinate.System)
+ .Where(planet => planet.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(planet => planet.Coordinate.Type == celestial.Coordinate.Type)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ }
+ origin = allCelestials
+ .Unique()
+ .Where(c => c.Coordinate.Galaxy == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Galaxy)
+ .Where(c => c.Coordinate.System == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.System)
+ .Where(c => c.Coordinate.Position == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Position)
+ .Where(c => c.Coordinate.Type == Enum.Parse((string) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Type))
+ .SingleOrDefault() ?? new() { ID = 0 };
+ fleetId = await _fleetScheduler.HandleMinerTransport(origin, celestial, destination, xCostBuildable, buildable, maxLFBuildings, preventIfMoreExpensiveThanNextMine);
+ if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ stop = true;
+ }
+ if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ delay = true;
+ }
+ }
+ }
+ } else {
+ DoLog(LogLevel.Information, $"Skipping transport: there is already a transport incoming in {celestial.ToString()}");
+ fleetId = _tbotInstance.UserData.fleets.Where(f => f.Mission == Missions.Transport)
+ .Where(f => f.Resources.TotalResources > 0)
+ .Where(f => f.ReturnFlight == false)
+ .Where(f => f.Destination.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(f => f.Destination.System == celestial.Coordinate.System)
+ .Where(f => f.Destination.Position == celestial.Coordinate.Position)
+ .Where(f => f.Destination.Type == celestial.Coordinate.Type)
+ .First().ID;
}
} else {
- DoLog(LogLevel.Information, $"Skipping transport: there is already a transport incoming in {celestial.ToString()}");
- try {
- fleetId = _tbotInstance.UserData.fleets.Where(f => f.Mission == Missions.Transport)
- .Where(f => f.Resources.TotalResources > 0)
- .Where(f => f.ReturnFlight == false)
- .Where(f => f.Destination.Galaxy == celestial.Coordinate.Galaxy)
- .Where(f => f.Destination.System == celestial.Coordinate.System)
- .Where(f => f.Destination.Position == celestial.Coordinate.Position)
- .Where(f => f.Destination.Type == celestial.Coordinate.Type)
- .First().ID;
+ if (_tbotInstance.InstanceSettings.General.SlotPriorityLevel.Brain > 0) {
+ if (_tbotInstance.InstanceSettings.Brain.Transports.MaxSlots == 0)
+ DoLog(LogLevel.Information, $"Transports.MaxSlots is set to 0, you should increase it.");
+ else
+ DoLog(LogLevel.Information, $"0 slots available.");
}
- catch { }
+ DoLog(LogLevel.Information, $"Not enough slots available for Transports.MaxSlots, delaying.");
+ delay = true;
}
}
}
@@ -244,7 +449,7 @@ private async Task LifeformAutoMineCelestial(Celestial celestial) {
DoLog(LogLevel.Information, $"Delaying...");
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
try {
- interval = (_tbotInstance.UserData.fleets.OrderBy(f => f.BackIn).First().BackIn ?? 0) * 1000 + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ interval = (_tbotInstance.UserData.fleets.Where(f => f.Mission == Missions.Transport).OrderBy(f => f.BackIn).First().BackIn ?? 0) * 1000 + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
} catch {
interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.CheckIntervalMax);
}
@@ -254,7 +459,9 @@ private async Task LifeformAutoMineCelestial(Celestial celestial) {
interval = delayTime;
} else {
if (fleetId == (int) SendFleetCode.QuickerToWaitForProduction) {
- var price = _calculationService.CalcPrice(buildable, level);
+ float costReduction = _calculationService.CalcLFBuildingsResourcesCostBonus(celestial);
+ float popReduction = _calculationService.CalcLFBuildingsPopulationCostBonus(celestial);
+ var price = _calculationService.CalcPrice(buildable, level, costReduction, 0, popReduction);
long productionTime = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.CheckIntervalMax);
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.ResourcesProduction);
DateTime now = await _tbotOgameBridge.GetDateTime();
@@ -283,13 +490,26 @@ private async Task LifeformAutoMineCelestial(Celestial celestial) {
}
}
interval = productionTime + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
- }
- else if (fleetId != 0 && fleetId != -1 && fleetId != -2) {
- _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
- var transportfleet = _tbotInstance.UserData.fleets.Single(f => f.ID == fleetId && f.Mission == Missions.Transport);
- interval = (transportfleet.ArriveIn * 1000) + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
} else {
- interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.CheckIntervalMax);
+ if (fleetId > 0) {
+ _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
+ var transportfleet = _tbotInstance.UserData.fleets.Single(f => f.ID == fleetId && f.Mission == Missions.Transport);
+ interval = (transportfleet.ArriveIn * 1000) + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ } else {
+ interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.CheckIntervalMax);
+ }
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.DoMultipleTransportIsNotEnoughShipButSamePosition) {
+ var transportfleet2 = _tbotInstance.UserData.fleets.Where(f => f.Mission == Missions.Transport)
+ .Where(f => f.Destination.IsSame(celestial.Coordinate))
+ .Where(f => f.Origin.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(f => f.Origin.System == celestial.Coordinate.System)
+ .Where(f => f.Origin.Position == celestial.Coordinate.Position)
+ .Where(f => f.Origin.Type == (celestial.Coordinate.Type == Celestials.Planet ? Celestials.Moon : Celestials.Planet))
+ .ToList();
+ if (transportfleet2.Count() > 0) {
+ interval = (long) (transportfleet2.First().BackIn * 1000) + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ }
+ }
}
}
time = await _tbotOgameBridge.GetDateTime();
diff --git a/TBot/Workers/Brain/LifeformsAutoResearchCelestialWorker.cs b/TBot/Workers/Brain/LifeformsAutoResearchCelestialWorker.cs
index f396d81d..54c38f72 100644
--- a/TBot/Workers/Brain/LifeformsAutoResearchCelestialWorker.cs
+++ b/TBot/Workers/Brain/LifeformsAutoResearchCelestialWorker.cs
@@ -86,12 +86,24 @@ private async Task LifeformAutoResearchCelestial(Celestial celestial) {
bool delayProduction = false;
long delayTime = 0;
long interval = 0;
+ _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
+ List planets = new();
+ foreach (var p in _tbotInstance.UserData.celestials) {
+ if (p.Coordinate.Type == Celestials.Planet) {
+ var newPlanet = await _tbotOgameBridge.UpdatePlanet(p, UpdateTypes.Facilities);
+ newPlanet = await _tbotOgameBridge.UpdatePlanet(p, UpdateTypes.Buildings);
+ newPlanet = await _tbotOgameBridge.UpdatePlanet(p, UpdateTypes.LFBonuses);
+ planets.Add(newPlanet);
+ }
+ }
try {
DoLog(LogLevel.Information, $"Running Lifeform AutoResearch on {celestial.ToString()}");
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Fast);
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Resources);
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.LFTechs);
celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Constructions);
+ celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.LFBonuses);
+ celestial = await _tbotOgameBridge.UpdatePlanet(celestial, UpdateTypes.Ships);
int maxResearchLevel = SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.Brain.LifeformAutoResearch, "MaxResearchLevel") ? (int) _tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.MaxResearchLevel : 1;
int maxTechs11 = SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.Brain.LifeformAutoResearch, "MaxTechs11") ? (int) _tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.MaxTechs11 : maxResearchLevel;
@@ -180,26 +192,240 @@ private async Task LifeformAutoResearchCelestial(Celestial celestial) {
DoLog(LogLevel.Information, $"Not enough resources to build: {buildable.ToString()} level {level.ToString()} on {celestial.ToString()}. Needed: {xCostBuildable.LFBuildingCostResources.ToString()} - Available: {celestial.Resources.LFBuildingCostResources.ToString()}");
if ((bool) _tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.Transports.Active && (bool) _tbotInstance.InstanceSettings.Brain.Transports.Active) {
+ _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
- if (!_calculationService.IsThereTransportTowardsCelestial(celestial, _tbotInstance.UserData.fleets)) {
- Celestial origin = _tbotInstance.UserData.celestials
- .Unique()
- .Where(c => c.Coordinate.Galaxy == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Galaxy)
- .Where(c => c.Coordinate.System == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.System)
- .Where(c => c.Coordinate.Position == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Position)
- .Where(c => c.Coordinate.Type == Enum.Parse((string) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Type))
- .SingleOrDefault() ?? new() { ID = 0 };
- fleetId = await _fleetScheduler.HandleMinerTransport(origin, celestial, xCostBuildable, Buildables.Null);
- if (fleetId == (int) SendFleetCode.AfterSleepTime) {
- stop = true;
- return;
- }
- if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
- delay = true;
- return;
+ List rankSlotsPriority = new() {
+ new RankSlotsPriority(Feature.BrainLifeformAutoResearch,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Brain,
+ ((bool) _tbotInstance.InstanceSettings.Brain.Active && (bool) _tbotInstance.InstanceSettings.Brain.Transports.Active && ((bool) _tbotInstance.InstanceSettings.Brain.AutoMine.Active || (bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.Active || (bool) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.Active || (bool) _tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.Active)),
+ (int) _tbotInstance.InstanceSettings.Brain.Transports.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Transport)),
+ new RankSlotsPriority(Feature.Expeditions,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Expeditions,
+ (bool) _tbotInstance.InstanceSettings.Expeditions.Active,
+ (int) _tbotInstance.UserData.slots.ExpTotal,
+ (int)_tbotInstance.UserData.slots.ExpInUse),
+ new RankSlotsPriority(Feature.AutoFarm,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoFarm,
+ (bool) _tbotInstance.InstanceSettings.AutoFarm.Active,
+ (int) _tbotInstance.InstanceSettings.AutoFarm.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Attack)),
+ new RankSlotsPriority(Feature.Colonize,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Colonize,
+ (bool) _tbotInstance.InstanceSettings.AutoColonize.Active,
+ (bool) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.Active ?
+ (int) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.MaxSlots :
+ 1,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Colonize)),
+ new RankSlotsPriority(Feature.AutoDiscovery,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoDiscovery,
+ (bool) _tbotInstance.InstanceSettings.AutoDiscovery.Active,
+ (int) _tbotInstance.InstanceSettings.AutoDiscovery.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Discovery)),
+ new RankSlotsPriority(Feature.Harvest,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoHarvest,
+ (bool) _tbotInstance.InstanceSettings.AutoHarvest.Active,
+ (int) _tbotInstance.InstanceSettings.AutoHarvest.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Harvest))
+ };
+ int MaxSlots = _calculationService.CalcSlotsPriority(Feature.BrainLifeformAutoResearch, rankSlotsPriority, _tbotInstance.UserData.slots, _tbotInstance.UserData.fleets, (int) _tbotInstance.InstanceSettings.General.SlotsToLeaveFree);
+
+ if (MaxSlots > 0) {
+ if (!_calculationService.IsThereTransportTowardsCelestial(celestial, _tbotInstance.UserData.fleets)) {
+ Celestial origin = new() { ID = 0 };
+ List allCelestials = _tbotInstance.UserData.celestials;
+ for (int i = 0; i < allCelestials.Count(); i++) {
+ allCelestials[i] = await _tbotOgameBridge.UpdatePlanet(allCelestials[i], UpdateTypes.Resources);
+ allCelestials[i] = await _tbotOgameBridge.UpdatePlanet(allCelestials[i], UpdateTypes.Ships);
+ allCelestials[i] = await _tbotOgameBridge.UpdatePlanet(allCelestials[i], UpdateTypes.LFBonuses);
+ }
+ Resources missingResources = xCostBuildable.Difference(celestial.Resources);
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.CheckMoonOrPlanetFirst) {
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.CheckMoonOrPlanetFirst && _calculationService.IsThereMoonHere(allCelestials, celestial)) {
+ origin = allCelestials.Unique()
+ .Where(c => c.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(c => c.Coordinate.System == celestial.Coordinate.System)
+ .Where(c => c.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(c => c.Coordinate.Type == Celestials.Moon)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ }
+ if (origin.ID != 0) {
+ if (origin.Resources.IsEnoughFor(missingResources)) {
+ missingResources = _tbotInstance.InstanceSettings.Brain.Transports.RoundResources ? missingResources.Round() : missingResources;
+ Buildables preferredShip = Buildables.SmallCargo;
+ if (!Enum.TryParse((string) _tbotInstance.InstanceSettings.Brain.Transports.CargoType, true, out preferredShip)) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, "Unable to parse CargoType. Falling back to default SmallCargo");
+ preferredShip = Buildables.SmallCargo;
+ }
+ long shipsNeeded = _calculationService.CalcShipNumberForPayload(missingResources, preferredShip, _tbotInstance.UserData.researches.HyperspaceTechnology, _tbotInstance.UserData.serverData, origin.LFBonuses.GetShipCargoBonus(preferredShip), _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo);
+ if (origin.Ships.GetAmount(preferredShip) >= shipsNeeded) {
+ Ships ships = new();
+ ships.Add((Buildables) preferredShip, shipsNeeded);
+ fleetId = await _fleetScheduler.SendFleet(origin, ships, celestial.Coordinate, Missions.Transport, Speeds.HundredPercent, missingResources);
+ if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ stop = true;
+ }
+ if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ delay = true;
+ }
+ } else if (_tbotInstance.InstanceSettings.Brain.Transports.DoMultipleTransportIsNotEnoughShipButSamePosition) {
+ DoLog(LogLevel.Warning, $"Not enough Cargo available. Multiple transports will take place.");
+ Ships ships = new();
+ ships.Add((Buildables) preferredShip, origin.Ships.GetAmount(preferredShip));
+ Resources transportableResources = _calculationService.CalcMaxTransportableResources(ships, missingResources, _tbotInstance.UserData.researches.HyperspaceTechnology, _tbotInstance.UserData.serverData, origin.LFBonuses, _tbotInstance.UserData.userInfo.Class, 0, _tbotInstance.UserData.serverData.ProbeCargo);
+ fleetId = await _fleetScheduler.SendFleet(origin, ships, celestial.Coordinate, Missions.Transport, Speeds.HundredPercent, transportableResources);
+ if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ stop = true;
+ }
+ if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ delay = true;
+ }
+ } else {
+ DoLog(LogLevel.Warning, $"Not enough Cargo available. Skipping CheckMoonOrPlanetFirst.");
+ }
+ } else {
+ DoLog(LogLevel.Warning, $"Not enough resources available on {origin.ToString()} only to send resources to {celestial.ToString()}.");
+ }
+ } else {
+ DoLog(LogLevel.Warning, $"No Moon available on {celestial.ToString()}.");
+ }
+ }
+
+ if (fleetId <= 0) {
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.Active) {
+ origin = _tbotInstance.UserData.celestials
+ .Unique()
+ .Where(c => c.Coordinate.Galaxy == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Galaxy)
+ .Where(c => c.Coordinate.System == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.System)
+ .Where(c => c.Coordinate.Position == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Position)
+ .Where(c => c.Coordinate.Type == Enum.Parse((string) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Type))
+ .SingleOrDefault() ?? new() { ID = 0 };
+ List celestialsToExclude = _calculationService.ParseCelestialsList(_tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.Exclude, allCelestials);
+ Buildables preferredShip = Buildables.SmallCargo;
+ if (!Enum.TryParse((string) _tbotInstance.InstanceSettings.Brain.Transports.CargoType, true, out preferredShip)) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, "Unable to parse CargoType. Falling back to default SmallCargo");
+ preferredShip = Buildables.SmallCargo;
+ }
+ TransportSettings transportsSettings = new((bool) _tbotInstance.InstanceSettings.Brain.Transports.Active,
+ preferredShip,
+ (long) _tbotInstance.InstanceSettings.Brain.Transports.DeutToLeaveOnMoons,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.RoundResources,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.SendToTheMoonIfPossible,
+ origin,
+ (long) _tbotInstance.InstanceSettings.Brain.Transports.MaxSlots,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.CheckMoonOrPlanetFirst,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.DoMultipleTransportIsNotEnoughShipButSamePosition,
+ new MultipleOrigins((bool) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.Active,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.OnlyFromMoons,
+ (long) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.MinimumResourcesToSend,
+ (bool) _tbotInstance.InstanceSettings.Brain.Transports.MultipleOrigins.PriorityToProximityOverQuantity,
+ celestialsToExclude)
+ );
+
+ Celestial destination;
+ if ((bool) transportsSettings.SendToTheMoonIfPossible && _calculationService.IsThereMoonHere(allCelestials, celestial)) {
+ destination = allCelestials
+ .Unique()
+ .Where(planet => planet.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(planet => planet.Coordinate.System == celestial.Coordinate.System)
+ .Where(planet => planet.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(planet => planet.Coordinate.Type == Celestials.Moon)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ if (destination.Ships.IsEmpty() || celestial.Resources.TotalResources == 0)
+ destination = celestial;
+ } else {
+ destination = celestial;
+ }
+
+ var resultOrigins = _calculationService.CalcMultipleOrigin(celestial, allCelestials, missingResources, transportsSettings, _tbotInstance.UserData.fleets, _tbotInstance.UserData);
+
+ if (resultOrigins.Count() == 0) {
+ DoLog(LogLevel.Information, $"No origin is available. This may be due to a lack of resources or cargo.");
+ return;
+ }
+ if (resultOrigins.Count() > MaxSlots) {
+ DoLog(LogLevel.Information, $"Not enough slots available to send all resources to build: {buildable.ToString()} level {level.ToString()} on {celestial.ToString()}. Slots needed: {resultOrigins.Count().ToString()}/{MaxSlots}.");
+ delay = true;
+ return;
+ }
+
+ Ships ships = new();
+
+ foreach (var item in resultOrigins) {
+ ships = new();
+ ships.Add((Buildables) transportsSettings.CargoType, _calculationService.CalcShipNumberForPayload(item.FirstOrDefault().Value, (Buildables) transportsSettings.CargoType, _tbotInstance.UserData.researches.HyperspaceTechnology, _tbotInstance.UserData.serverData, celestial.LFBonuses.GetShipCargoBonus(transportsSettings.CargoType), _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo));
+ if (item.FirstOrDefault().Key.Coordinate.IsSame(destination.Coordinate) && transportsSettings.SendToTheMoonIfPossible && destination.Coordinate.Type == Celestials.Moon)
+ fleetId= await _fleetScheduler.SendFleet(item.FirstOrDefault().Key, ships, celestial.Coordinate, Missions.Transport, Speeds.HundredPercent, item.FirstOrDefault().Value);
+ else
+ fleetId= await _fleetScheduler.SendFleet(item.FirstOrDefault().Key, ships, destination.Coordinate, Missions.Transport, Speeds.HundredPercent, item.FirstOrDefault().Value);
+
+ if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ stop = true;
+ return;
+ }
+ if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ delay = true;
+ return;
+ }
+ }
+ } else {
+ Celestial destination;
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.SendToTheMoonIfPossible && _calculationService.IsThereMoonHere(_tbotInstance.UserData.celestials, celestial)) {
+ destination = allCelestials
+ .Unique()
+ .Where(planet => planet.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(planet => planet.Coordinate.System == celestial.Coordinate.System)
+ .Where(planet => planet.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(planet => planet.Coordinate.Type == Celestials.Moon)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ if (destination.Ships.IsEmpty() || celestial.Resources.TotalResources == 0)
+ destination = celestial;
+ xCostBuildable = xCostBuildable.Difference(destination.Resources);
+ } else {
+ destination = allCelestials
+ .Unique()
+ .Where(planet => planet.Coordinate.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(planet => planet.Coordinate.System == celestial.Coordinate.System)
+ .Where(planet => planet.Coordinate.Position == celestial.Coordinate.Position)
+ .Where(planet => planet.Coordinate.Type == celestial.Coordinate.Type)
+ .SingleOrDefault() ?? new() { ID = 0 };
+ }
+ origin = allCelestials
+ .Unique()
+ .Where(c => c.Coordinate.Galaxy == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Galaxy)
+ .Where(c => c.Coordinate.System == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.System)
+ .Where(c => c.Coordinate.Position == (int) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Position)
+ .Where(c => c.Coordinate.Type == Enum.Parse((string) _tbotInstance.InstanceSettings.Brain.Transports.Origin.Type))
+ .SingleOrDefault() ?? new() { ID = 0 };
+ fleetId = await _fleetScheduler.HandleMinerTransport(origin, celestial, destination, xCostBuildable, LFBuildables.None);
+ if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ stop = true;
+ }
+ if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ delay = true;
+ }
+ }
+ }
+ } else {
+ DoLog(LogLevel.Information, $"Skipping transport: there is already a transport incoming in {celestial.ToString()}");
+ fleetId = _tbotInstance.UserData.fleets.Where(f => f.Mission == Missions.Transport)
+ .Where(f => f.Resources.TotalResources > 0)
+ .Where(f => f.ReturnFlight == false)
+ .Where(f => f.Destination.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(f => f.Destination.System == celestial.Coordinate.System)
+ .Where(f => f.Destination.Position == celestial.Coordinate.Position)
+ .Where(f => f.Destination.Type == celestial.Coordinate.Type)
+ .First().ID;
}
} else {
- DoLog(LogLevel.Information, $"Skipping transport: there is already a transport incoming in {celestial.ToString()}");
+ if (_tbotInstance.InstanceSettings.General.SlotPriorityLevel.Brain > 0) {
+ if (_tbotInstance.InstanceSettings.Brain.Transports.MaxSlots == 0)
+ DoLog(LogLevel.Information, $"Transports.MaxSlots is set to 0, you should increase it.");
+ else
+ DoLog(LogLevel.Information, $"0 slots available.");
+ }
+ DoLog(LogLevel.Information, $"Not enough slots available for Transports.MaxSlots, delaying.");
+ delay = true;
}
}
}
@@ -225,19 +451,31 @@ private async Task LifeformAutoResearchCelestial(Celestial celestial) {
DoLog(LogLevel.Information, $"Delaying...");
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
try {
- interval = (_tbotInstance.UserData.fleets.OrderBy(f => f.BackIn).First().BackIn ?? 0) * 1000 + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ interval = (_tbotInstance.UserData.fleets.Where(f => f.Mission == Missions.Transport).OrderBy(f => f.BackIn).First().BackIn ?? 0) * 1000 + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
} catch {
interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.CheckIntervalMax);
}
} else if (started) {
interval = ((long) celestial.Constructions.LFResearchCountdown * (long) 1000) + (long) RandomizeHelper.CalcRandomInterval(IntervalType.AFewSeconds);
} else {
- if (fleetId != 0 && fleetId != -1 && fleetId != -2) {
- _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
+ if (fleetId > 0) {
+ _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
var transportfleet = _tbotInstance.UserData.fleets.Single(f => f.ID == fleetId && f.Mission == Missions.Transport);
interval = (transportfleet.ArriveIn * 1000) + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
} else {
- interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.CheckIntervalMax);
+ interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.CheckIntervalMax);
+ }
+ if ((bool) _tbotInstance.InstanceSettings.Brain.Transports.DoMultipleTransportIsNotEnoughShipButSamePosition) {
+ var transportfleet2 = _tbotInstance.UserData.fleets.Where(f => f.Mission == Missions.Transport)
+ .Where(f => f.Destination.IsSame(celestial.Coordinate))
+ .Where(f => f.Origin.Galaxy == celestial.Coordinate.Galaxy)
+ .Where(f => f.Origin.System == celestial.Coordinate.System)
+ .Where(f => f.Origin.Position == celestial.Coordinate.Position)
+ .Where(f => f.Origin.Type == (celestial.Coordinate.Type == Celestials.Planet ? Celestials.Moon : Celestials.Planet))
+ .ToList();
+ if (transportfleet2.Count() > 0) {
+ interval = (long) (transportfleet2.First().BackIn * 1000) + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ }
}
}
diff --git a/TBot/Workers/ColonizeWorker.cs b/TBot/Workers/ColonizeWorker.cs
index b10ec365..213069ee 100644
--- a/TBot/Workers/ColonizeWorker.cs
+++ b/TBot/Workers/ColonizeWorker.cs
@@ -49,10 +49,9 @@ public override LogSender GetLogSender() {
return LogSender.Colonize;
}
- protected override async Task Execute() {
+ protected override async Task Execute() {
await _tbotOgameBridge.CheckCelestials();
bool stop = false;
- bool delay = false;
Fields fieldsSettings = new() {
Total = (int) _tbotInstance.InstanceSettings.AutoColonize.Abandon.MinFields
};
@@ -98,7 +97,44 @@ protected override async Task Execute() {
if (currentPlanets + slotsToLeaveFree < maxPlanets) {
_tbotInstance.log(LogLevel.Information, LogSender.Colonize, "A new planet is needed.");
+ _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
+ List rankSlotsPriority = new() {
+ new RankSlotsPriority(Feature.BrainAutoMine,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Brain,
+ ((bool) _tbotInstance.InstanceSettings.Brain.Active && (bool) _tbotInstance.InstanceSettings.Brain.Transports.Active && ((bool) _tbotInstance.InstanceSettings.Brain.AutoMine.Active || (bool) _tbotInstance.InstanceSettings.Brain.AutoResearch.Active || (bool) _tbotInstance.InstanceSettings.Brain.LifeformAutoMine.Active || (bool) _tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.Active)),
+ (int) _tbotInstance.InstanceSettings.Brain.Transports.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Transport)),
+ new RankSlotsPriority(Feature.Expeditions,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Expeditions,
+ (bool) _tbotInstance.InstanceSettings.Expeditions.Active,
+ (int) _tbotInstance.UserData.slots.ExpTotal,
+ (int)_tbotInstance.UserData.slots.ExpInUse),
+ new RankSlotsPriority(Feature.AutoFarm,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoFarm,
+ (bool) _tbotInstance.InstanceSettings.AutoFarm.Active,
+ (int) _tbotInstance.InstanceSettings.AutoFarm.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Attack)),
+ new RankSlotsPriority(Feature.Colonize,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.Colonize,
+ (bool) _tbotInstance.InstanceSettings.AutoColonize.Active,
+ (bool) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.Active ?
+ (int) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.MaxSlots :
+ 1,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Colonize)),
+ new RankSlotsPriority(Feature.AutoDiscovery,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoDiscovery,
+ (bool) _tbotInstance.InstanceSettings.AutoDiscovery.Active,
+ (int) _tbotInstance.InstanceSettings.AutoDiscovery.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Discovery)),
+ new RankSlotsPriority(Feature.Harvest,
+ (int) _tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoHarvest,
+ (bool) _tbotInstance.InstanceSettings.AutoHarvest.Active,
+ (int) _tbotInstance.InstanceSettings.AutoHarvest.MaxSlots,
+ (int) _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Harvest))
+ };
+ int MaxSlots = _calculationService.CalcSlotsPriority(Feature.Colonize, rankSlotsPriority, _tbotInstance.UserData.slots, _tbotInstance.UserData.fleets, (int) _tbotInstance.InstanceSettings.General.SlotsToLeaveFree);
+
if (
(!(bool) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.Active && _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Colonize && !f.ReturnFlight) >= maxPlanets - currentPlanets)
|| ((bool) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.Active && _tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Colonize && !f.ReturnFlight) > 0)
@@ -107,7 +143,7 @@ protected override async Task Execute() {
interval = (_tbotInstance.UserData.fleets
.OrderBy(f => f.ArriveIn)
.First(f => !f.ReturnFlight)
- .ArriveIn * 1000) + RandomizeHelper.CalcRandomInterval(IntervalType.AFewSeconds);
+ .ArriveIn * 1000) + RandomizeHelper.CalcRandomInterval(IntervalType.LessThanFiveSeconds);
} else {
Coordinate originCoords = new(
(int) _tbotInstance.InstanceSettings.AutoColonize.Origin.Galaxy,
@@ -141,7 +177,7 @@ protected override async Task Execute() {
(int) ii,
Celestials.Planet
);
- if (_calculationService.CalcLimitAstro((int) targetCoords.Position, _tbotInstance.UserData.researches)) {
+ if (_calculationService.IsAstrophysicsPositionValid((int) targetCoords.Position, (int) _tbotInstance.UserData.researches.Astrophysics)) {
targets.Add(targetCoords);
}
}
@@ -169,7 +205,7 @@ protected override async Task Execute() {
}
filteredTargets = (bool) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.Active
? filteredTargetsRdm
- .Take((int) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.MaxSlots)
+ .Take(MaxSlots)
.ToList()
: filteredTargetsRdm
.Take(maxPlanets - currentPlanets)
@@ -178,7 +214,7 @@ protected override async Task Execute() {
filteredTargets = (bool) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.Active
? filteredTargets
.OrderBy(t => _calculationService.CalcDistance(origin.Coordinate, t, _tbotInstance.UserData.serverData))
- .Take((int) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.MaxSlots)
+ .Take(MaxSlots)
.ToList()
: filteredTargets
.OrderBy(t => _calculationService.CalcDistance(origin.Coordinate, t, _tbotInstance.UserData.serverData))
@@ -226,7 +262,14 @@ protected override async Task Execute() {
return;
}
if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
- delay = true;
+ long delayInterval = 0;
+ try {
+ delayInterval = (_tbotInstance.UserData.fleets.OrderBy(f => f.BackIn).First().BackIn ?? 0) * 1000 + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ } catch {
+ delayInterval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.AutoColonize.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.AutoColonize.CheckIntervalMax);
+ }
+ _tbotInstance.log(LogLevel.Information, LogSender.Colonize, $"Not enough fleet slots available. Delaying for {TimeSpan.FromMilliseconds(delayInterval).TotalSeconds}s.");
+ await Task.Delay((int)delayInterval);
return;
}
var minWaitNextFleet = (int) _tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.MinWaitNextFleet;
@@ -324,20 +367,7 @@ protected override async Task Execute() {
_tbotInstance.log(LogLevel.Information, LogSender.Colonize, $"Stopping feature.");
await EndExecution();
}
- if (delay) {
- _tbotInstance.log(LogLevel.Information, LogSender.Colonize, $"Delaying...");
- var time = await _tbotOgameBridge.GetDateTime();
- _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
- long interval;
- try {
- interval = (_tbotInstance.UserData.fleets.OrderBy(f => f.BackIn).First().BackIn ?? 0) * 1000 + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
- } catch {
- interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.AutoColonize.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.AutoColonize.CheckIntervalMax);
- }
- var newTime = time.AddMilliseconds(interval);
- ChangeWorkerPeriod(interval);
- _tbotInstance.log(LogLevel.Information, LogSender.Colonize, $"Next check at {newTime}");
- }
+
await _tbotOgameBridge.CheckCelestials();
}
}
diff --git a/TBot/Workers/DefenderWorker.cs b/TBot/Workers/DefenderWorker.cs
index eaca570c..5d864d90 100644
--- a/TBot/Workers/DefenderWorker.cs
+++ b/TBot/Workers/DefenderWorker.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Collections.Concurrent;
using System.Linq;
using System.Text;
using System.Threading;
@@ -18,6 +19,9 @@
namespace Tbot.Workers {
internal class DefenderWorker : WorkerBase {
+ private static readonly ConcurrentDictionary _handledAttackIds = new();
+ private static readonly TimeSpan _handledAttackTtl = TimeSpan.FromMinutes(60);
+
private readonly IFleetScheduler _fleetScheduler;
private readonly IOgameService _ogameService;
private readonly ITBotOgamedBridge _tbotOgameBridge;
@@ -41,12 +45,11 @@ protected override async Task Execute() {
DateTime time = await _tbotOgameBridge.GetDateTime();
if (isUnderAttack) {
if ((bool) _tbotInstance.InstanceSettings.Defender.Alarm.Active)
- await Task.Factory.StartNew(() => ConsoleHelpers.PlayAlarm(), _ct);
- // UpdateTitle(false, true);
+ await Task.Run(() => ConsoleHelpers.PlayAlarm(), _ct);
DoLog(LogLevel.Warning, "ENEMY ACTIVITY!!!");
_tbotInstance.UserData.attacks = await _ogameService.GetAttacks();
foreach (AttackerFleet attack in _tbotInstance.UserData.attacks) {
- HandleAttack(attack);
+ await HandleAttack(attack);
}
} else {
DoLog(LogLevel.Information, "Your empire is safe");
@@ -55,7 +58,6 @@ protected override async Task Execute() {
if (interval <= 0)
interval = RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
- // Display dateTime for logging
DateTime newTime = time.AddMilliseconds(interval);
ChangeWorkerPeriod(TimeSpan.FromMilliseconds(interval));
DoLog(LogLevel.Information, $"Next check at {newTime.ToString()}");
@@ -93,8 +95,6 @@ public override LogSender GetLogSender() {
private async Task FakeActivity() {
- //checking if under attack by making activity on planet/moon configured in settings (otherwise make acti on latest activated planet)
- // And make activity on one more random planet to fake real player
Celestial celestial;
Celestial randomCelestial;
@@ -124,7 +124,23 @@ private async Task FakeActivity() {
return;
}
- private async void HandleAttack(AttackerFleet attack) {
+ private async Task HandleAttack(AttackerFleet attack) {
+ try {
+ var nowUtc = DateTime.UtcNow;
+ foreach (var kv in _handledAttackIds.ToArray()) {
+ if (nowUtc - kv.Value > _handledAttackTtl)
+ _handledAttackIds.TryRemove(kv.Key, out _);
+ }
+ if (attack != null && attack.ID != 0 &&
+ _handledAttackIds.TryGetValue(attack.ID, out var seenAt) &&
+ (nowUtc - seenAt) <= _handledAttackTtl) {
+ DoLog(LogLevel.Information, $"Attack {attack.ID} already handled recently; skipping duplicate actions.");
+ return;
+ }
+ if (attack != null && attack.ID != 0) {
+ _handledAttackIds[attack.ID] = nowUtc;
+ }
+
if (_tbotInstance.UserData.celestials.Count() == 0) {
DateTime time = await _tbotOgameBridge.GetDateTime();
long interval = RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
@@ -135,20 +151,42 @@ private async void HandleAttack(AttackerFleet attack) {
return;
}
- Celestial attackedCelestial = _tbotInstance.UserData.celestials.Unique().SingleOrDefault(planet => planet.HasCoords(attack.Destination));
+ Celestial attackedCelestial = _tbotInstance.UserData.celestials.Unique().FirstOrDefault(planet => planet.HasCoords(attack.Destination));
+ if (attackedCelestial == null) {
+ DoLog(LogLevel.Warning, $"Unable to handle attack {attack.ID}: attacked celestial not found in account data.");
+ return;
+ }
attackedCelestial = await _tbotOgameBridge.UpdatePlanet(attackedCelestial, UpdateTypes.Ships);
+ try {
+ if ((bool)_tbotInstance.InstanceSettings.Defender.IgnoreAttackIfIHave.Active) {
+ attackedCelestial = await _tbotOgameBridge.UpdatePlanet(attackedCelestial, UpdateTypes.Resources);
+ }
+ } catch {
+ }
+
try {
- if ((_tbotInstance.InstanceSettings.Defender.WhiteList as long[]).Any()) {
- foreach (int playerID in (long[]) _tbotInstance.InstanceSettings.Defender.WhiteList) {
- if (attack.AttackerID == playerID) {
- DoLog(LogLevel.Information, $"Attack {attack.ID.ToString()} skipped: attacker {attack.AttackerName} whitelisted.");
- return;
- }
+ var wlObj = _tbotInstance.InstanceSettings.Defender.WhiteList;
+ IEnumerable whiteListIds = wlObj switch {
+ long[] a => a,
+ int[] a => a.Select(x => (long)x),
+ IEnumerable e => e,
+ IEnumerable e => e.Select(x => (long)x),
+ _ => Enumerable.Empty()
+ };
+
+ if (!whiteListIds.Any() && wlObj != null) {
+ DoLog(LogLevel.Debug, $"Defender WhiteList present but unsupported type: {wlObj.GetType().FullName}");
+ }
+
+ foreach (var playerId in whiteListIds) {
+ if (attack.AttackerID == playerId) {
+ DoLog(LogLevel.Information, $"Attack {attack.ID.ToString()} skipped: attacker {attack.AttackerName} whitelisted.");
+ return;
}
}
- } catch {
- DoLog(LogLevel.Warning, "An error has occurred while checking Defender WhiteList");
+ } catch (Exception ex) {
+ DoLog(LogLevel.Warning, $"An error has occurred while checking Defender WhiteList: {ex.Message}");
}
try {
@@ -170,13 +208,17 @@ private async void HandleAttack(AttackerFleet attack) {
Type = Celestials.Planet
}));
}
- defenderCelestial = await _tbotOgameBridge.UpdatePlanet(attackedCelestial, UpdateTypes.Facilities);
+ if (defenderCelestial == null) {
+ DoLog(LogLevel.Warning, $"Missile attack detected on {attack.Destination.ToString()} but planet celestial was not found in account data. Skipping missile defence.");
+ return;
+ }
+ defenderCelestial = await _tbotOgameBridge.UpdatePlanet(defenderCelestial, UpdateTypes.Facilities);
if (defenderCelestial.Facilities.MissileSilo >= 2) {
- defenderCelestial = await _tbotOgameBridge.UpdatePlanet(attackedCelestial, UpdateTypes.Defences);
- defenderCelestial = await _tbotOgameBridge.UpdatePlanet(attackedCelestial, UpdateTypes.Productions);
+ defenderCelestial = await _tbotOgameBridge.UpdatePlanet(defenderCelestial, UpdateTypes.Defences);
+ defenderCelestial = await _tbotOgameBridge.UpdatePlanet(defenderCelestial, UpdateTypes.Productions);
if (defenderCelestial.Productions.Count == 0) {
var availableSpace = defenderCelestial.Facilities.MissileSilo - defenderCelestial.Defences.AntiBallisticMissiles - (2 * defenderCelestial.Defences.InterplanetaryMissiles);
- defenderCelestial = await _tbotOgameBridge.UpdatePlanet(attackedCelestial, UpdateTypes.Resources);
+ defenderCelestial = await _tbotOgameBridge.UpdatePlanet(defenderCelestial, UpdateTypes.Resources);
if (availableSpace > 0) {
DoLog(LogLevel.Information, $"Building {availableSpace} AntiBallisticMissiles on {defenderCelestial.ToString()}");
await _ogameService.BuildDefences(defenderCelestial, Buildables.AntiBallisticMissiles, availableSpace);
@@ -214,10 +256,14 @@ private async void HandleAttack(AttackerFleet attack) {
} else {
DoLog(LogLevel.Information, "Unable to detect fleet composition.");
}
+ var ignoreAttackIfIHaveActive = (bool) _tbotInstance.InstanceSettings.Defender.IgnoreAttackIfIHave.Active;
+ var totalResources = attackedCelestial.Resources?.TotalResources ?? 0;
+ var fleetPoints = attackedCelestial.Ships?.GetFleetPoints() ?? 0;
+
if (
- (bool) _tbotInstance.InstanceSettings.Defender.IgnoreAttackIfIHave.Active &&
- attackedCelestial.Resources.TotalResources < (long) _tbotInstance.InstanceSettings.Defender.IgnoreAttackIfIHave.MinResourcesToSave &&
- (attackedCelestial.Ships.GetFleetPoints() *1000) < (long) _tbotInstance.InstanceSettings.Defender.IgnoreAttackIfIHave.MinFleetToSave
+ ignoreAttackIfIHaveActive &&
+ totalResources < (long) _tbotInstance.InstanceSettings.Defender.IgnoreAttackIfIHave.MinResourcesToSave &&
+ (fleetPoints * 1000) < (long) _tbotInstance.InstanceSettings.Defender.IgnoreAttackIfIHave.MinFleetToSave
) {
DoLog(LogLevel.Information, $"Attack {attack.ID.ToString()} skipped: it's not worth it.");
return;
@@ -228,14 +274,16 @@ private async void HandleAttack(AttackerFleet attack) {
if ((bool) _tbotInstance.InstanceSettings.Defender.TelegramMessenger.Active) {
await _tbotInstance.SendTelegramMessage($"Player {attack.AttackerName} ({attack.AttackerID}) is attacking your planet {attack.Destination.ToString()} arriving at {attack.ArrivalTime.ToString()}");
- if (attack.Ships != null)
+ if (attack.Ships != null) {
await Task.Delay(1000, _ct);
- await _tbotInstance.SendTelegramMessage($"The attack is composed by: {attack.Ships.ToString()}");
+ await _tbotInstance.SendTelegramMessage($"The attack is composed by: {attack.Ships.ToString()}");
+ }
}
DoLog(LogLevel.Warning, $"Player {attack.AttackerName} ({attack.AttackerID}) is attacking your planet {attackedCelestial.ToString()} arriving at {attack.ArrivalTime.ToString()}");
- if (attack.Ships != null)
+ if (attack.Ships != null) {
await Task.Delay(1000, _ct);
- DoLog(LogLevel.Warning, $"The attack is composed by: {attack.Ships.ToString()}");
+ DoLog(LogLevel.Warning, $"The attack is composed by: {attack.Ships.ToString()}");
+ }
if ((bool) _tbotInstance.InstanceSettings.Defender.SpyAttacker.Active) {
_tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
@@ -246,8 +294,12 @@ private async void HandleAttack(AttackerFleet attack) {
Coordinate destination = attack.Origin;
Ships ships = new() { EspionageProbe = (int) _tbotInstance.InstanceSettings.Defender.SpyAttacker.Probes };
int fleetId = await _fleetScheduler.SendFleet(attackedCelestial, ships, destination, Missions.Spy, Speeds.HundredPercent, new Resources(), _tbotInstance.UserData.userInfo.Class);
- Fleet fleet = _tbotInstance.UserData.fleets.Single(fleet => fleet.ID == fleetId);
- DoLog(LogLevel.Information, $"Spying attacker from {attackedCelestial.ToString()} to {destination.ToString()} with {_tbotInstance.InstanceSettings.Defender.SpyAttacker.Probes} probes. Arrival at {fleet.ArrivalTime.ToString()}");
+ var fleet = _tbotInstance.UserData.fleets.SingleOrDefault(f => f.ID == fleetId);
+ if (fleet == null) {
+ DoLog(LogLevel.Warning, $"SpyAttacker: SendFleet returned id={fleetId}, but fleet was not found in current fleet list (send may have failed or list not updated yet).");
+ } else {
+ DoLog(LogLevel.Information, $"Spying attacker from {attackedCelestial.ToString()} to {destination.ToString()} with {_tbotInstance.InstanceSettings.Defender.SpyAttacker.Probes} probes. Arrival at {fleet.ArrivalTime.ToString()}");
+ }
} catch (Exception e) {
DoLog(LogLevel.Error, $"Could not spy attacker: an exception has occurred: {e.Message}");
DoLog(LogLevel.Warning, $"Stacktrace: {e.StackTrace}");
@@ -287,6 +339,10 @@ private async void HandleAttack(AttackerFleet attack) {
DoLog(LogLevel.Warning, $"Stacktrace: {e.StackTrace}");
}
}
+ } catch (Exception e) {
+ DoLog(LogLevel.Error, $"HandleAttack error for attack {attack?.ID}: {e.Message}");
+ DoLog(LogLevel.Warning, $"Stacktrace: {e.StackTrace}");
+ }
}
}
-}
+}
\ No newline at end of file
diff --git a/TBot/Workers/ExpeditionsWorker.cs b/TBot/Workers/ExpeditionsWorker.cs
index fc7f2063..197ab4bd 100644
--- a/TBot/Workers/ExpeditionsWorker.cs
+++ b/TBot/Workers/ExpeditionsWorker.cs
@@ -34,7 +34,7 @@ public ExpeditionsWorker(ITBotMain parentInstance,
}
public override bool IsWorkerEnabledBySettings() {
try {
- return (bool) _tbotInstance.InstanceSettings.Expeditions.Active;
+ return (bool)_tbotInstance.InstanceSettings.Expeditions.Active;
} catch (Exception) {
return false;
}
@@ -49,18 +49,25 @@ public override Feature GetFeature() {
public override LogSender GetLogSender() {
return LogSender.Expeditions;
}
-
-
+ private int CountActiveExpeditionsFromOrigin(Celestial origin) {
+ return _tbotInstance.UserData.fleets.Count(f =>
+ f.Mission == Missions.Expedition &&
+ f.Origin != null &&
+ f.Origin.Galaxy == origin.Coordinate.Galaxy &&
+ f.Origin.System == origin.Coordinate.System &&
+ f.Origin.Position == origin.Coordinate.Position &&
+ (int)f.Origin.Type == (int)origin.Coordinate.Type
+ );
+}
protected override async Task Execute() {
bool stop = false;
bool delay = false;
try {
- // Wait for the thread semaphore to avoid the concurrency with itself
long interval;
DateTime time;
DateTime newTime;
- if ((bool) _tbotInstance.InstanceSettings.Expeditions.Active) {
+ if ((bool)_tbotInstance.InstanceSettings.Expeditions.Active) {
_tbotInstance.UserData.researches = await _tbotOgameBridge.UpdateResearches();
if (_tbotInstance.UserData.researches.Astrophysics == 0) {
DoLog(LogLevel.Information, "Skipping: Astrophysics not yet researched!");
@@ -75,8 +82,54 @@ protected override async Task Execute() {
_tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
_tbotInstance.UserData.serverData = await _ogameService.GetServerData();
+
+ List rankSlotsPriority = new() {
+ new RankSlotsPriority(Feature.BrainAutoMine,
+ (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.Brain,
+ ((bool)_tbotInstance.InstanceSettings.Brain.Active && (bool)_tbotInstance.InstanceSettings.Brain.Transports.Active &&
+ ((bool)_tbotInstance.InstanceSettings.Brain.AutoMine.Active ||
+ (bool)_tbotInstance.InstanceSettings.Brain.AutoResearch.Active ||
+ (bool)_tbotInstance.InstanceSettings.Brain.LifeformAutoMine.Active ||
+ (bool)_tbotInstance.InstanceSettings.Brain.LifeformAutoResearch.Active)),
+ (int)_tbotInstance.InstanceSettings.Brain.Transports.MaxSlots,
+ (int)_tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Transport)),
+ new RankSlotsPriority(Feature.Expeditions,
+ (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.Expeditions,
+ (bool)_tbotInstance.InstanceSettings.Expeditions.Active,
+ (int)_tbotInstance.UserData.slots.ExpTotal,
+ (int)_tbotInstance.UserData.slots.ExpInUse),
+ new RankSlotsPriority(Feature.AutoFarm,
+ (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoFarm,
+ (bool)_tbotInstance.InstanceSettings.AutoFarm.Active,
+ (int)_tbotInstance.InstanceSettings.AutoFarm.MaxSlots,
+ (int)_tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Attack)),
+ new RankSlotsPriority(Feature.Colonize,
+ (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.Colonize,
+ (bool)_tbotInstance.InstanceSettings.AutoColonize.Active,
+ (bool)_tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.Active ?
+ (int)_tbotInstance.InstanceSettings.AutoColonize.IntensiveResearch.MaxSlots : 1,
+ (int)_tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Colonize)),
+ new RankSlotsPriority(Feature.AutoDiscovery,
+ (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoDiscovery,
+ (bool)_tbotInstance.InstanceSettings.AutoDiscovery.Active,
+ (int)_tbotInstance.InstanceSettings.AutoDiscovery.MaxSlots,
+ (int)_tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Discovery)),
+ new RankSlotsPriority(Feature.Harvest,
+ (int)_tbotInstance.InstanceSettings.General.SlotPriorityLevel.AutoHarvest,
+ (bool)_tbotInstance.InstanceSettings.AutoHarvest.Active,
+ (int)_tbotInstance.InstanceSettings.AutoHarvest.MaxSlots,
+ (int)_tbotInstance.UserData.fleets.Count(f => f.Mission == Missions.Harvest))
+ };
+
+ int MaxSlots = _tbotInstance.UserData.slots.Total
+ - (int)_tbotInstance.InstanceSettings.General.SlotsToLeaveFree;
+
+ if (MaxSlots < 0)
+ MaxSlots = 0;
+
int expsToSend;
- if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.Expeditions, "WaitForAllExpeditions") && (bool) _tbotInstance.InstanceSettings.Expeditions.WaitForAllExpeditions) {
+ if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.Expeditions, "WaitForAllExpeditions")
+ && (bool)_tbotInstance.InstanceSettings.Expeditions.WaitForAllExpeditions) {
if (_tbotInstance.UserData.slots.ExpInUse == 0)
expsToSend = _tbotInstance.UserData.slots.ExpTotal;
else
@@ -84,25 +137,40 @@ protected override async Task Execute() {
} else {
expsToSend = Math.Min(_tbotInstance.UserData.slots.ExpFree, _tbotInstance.UserData.slots.Free);
}
+
DoLog(LogLevel.Debug, $"Expedition slot free: {expsToSend}");
- if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.Expeditions, "WaitForMajorityOfExpeditions") && (bool) _tbotInstance.InstanceSettings.Expeditions.WaitForMajorityOfExpeditions) {
- if ((double) expsToSend < Math.Round((double) _tbotInstance.UserData.slots.ExpTotal / 2D, 0, MidpointRounding.ToZero) + 1D) {
+
+ if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.Expeditions, "WaitForMajorityOfExpeditions")
+ && (bool)_tbotInstance.InstanceSettings.Expeditions.WaitForMajorityOfExpeditions) {
+ if ((double)expsToSend < Math.Round((double)_tbotInstance.UserData.slots.ExpTotal / 2D, 0, MidpointRounding.ToZero) + 1D) {
DoLog(LogLevel.Debug, $"Majority of expedition already in flight, Skipping...");
expsToSend = 0;
}
}
+ expsToSend = expsToSend < MaxSlots ? expsToSend : MaxSlots;
+
+ if (expsToSend <= 0) {
+ var idleInterval = RandomizeHelper.CalcRandomInterval(IntervalType.AboutFiveMinutes);
+ var nowIdle = await _tbotOgameBridge.GetDateTime();
+ DoLog(LogLevel.Information, "Expeditions idle – no free expedition slots");
+ DoLog(LogLevel.Information, $"Next expedition check at {nowIdle.AddMilliseconds(idleInterval)}");
+ ChangeWorkerPeriod(idleInterval);
+ return;
+ }
+
if (expsToSend > 0) {
if (_tbotInstance.UserData.slots.ExpFree > 0) {
if (_tbotInstance.UserData.slots.Free > 0) {
+
List origins = new();
if (_tbotInstance.InstanceSettings.Expeditions.Origin.Length > 0) {
try {
foreach (var origin in _tbotInstance.InstanceSettings.Expeditions.Origin) {
Coordinate customOriginCoords = new(
- (int) origin.Galaxy,
- (int) origin.System,
- (int) origin.Position,
+ (int)origin.Galaxy,
+ (int)origin.System,
+ (int)origin.Position,
Enum.Parse(origin.Type.ToString())
);
Celestial customOrigin = _tbotInstance.UserData.celestials
@@ -121,7 +189,14 @@ protected override async Task Execute() {
_tbotInstance.UserData.celestials = await _tbotOgameBridge.UpdatePlanets(UpdateTypes.LFBonuses);
origins.Add(_tbotInstance.UserData.celestials
.OrderBy(planet => planet.Coordinate.Type == Celestials.Moon)
- .ThenByDescending(planet => _calculationService.CalcFleetCapacity(planet.Ships, _tbotInstance.UserData.serverData, _tbotInstance.UserData.researches.HyperspaceTechnology, null, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo))
+ .ThenByDescending(planet =>
+ _calculationService.CalcFleetCapacity(
+ planet.Ships,
+ _tbotInstance.UserData.serverData,
+ _tbotInstance.UserData.researches.HyperspaceTechnology,
+ null,
+ _tbotInstance.UserData.userInfo.Class,
+ _tbotInstance.UserData.serverData.ProbeCargo))
.First()
);
}
@@ -130,55 +205,121 @@ protected override async Task Execute() {
_tbotInstance.UserData.celestials = await _tbotOgameBridge.UpdatePlanets(UpdateTypes.LFBonuses);
origins.Add(_tbotInstance.UserData.celestials
.OrderBy(planet => planet.Coordinate.Type == Celestials.Moon)
- .ThenByDescending(planet => _calculationService.CalcFleetCapacity(planet.Ships, _tbotInstance.UserData.serverData, _tbotInstance.UserData.researches.HyperspaceTechnology, null, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo))
+ .ThenByDescending(planet =>
+ _calculationService.CalcFleetCapacity(
+ planet.Ships,
+ _tbotInstance.UserData.serverData,
+ _tbotInstance.UserData.researches.HyperspaceTechnology,
+ null,
+ _tbotInstance.UserData.userInfo.Class,
+ _tbotInstance.UserData.serverData.ProbeCargo))
.First()
);
}
- if ((bool) _tbotInstance.InstanceSettings.Expeditions.RandomizeOrder) {
+
+ if ((bool)_tbotInstance.InstanceSettings.Expeditions.RandomizeOrder) {
origins = origins.Shuffle().ToList();
}
- Dictionary originExps = new();
- int quot = (int) Math.Floor((float) expsToSend / (float) origins.Count());
+ LFBonuses lfBonuses = origins.First().LFBonuses;
+
+ _tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
+
+ var expFleets = _tbotInstance.UserData.fleets
+ .Where(f => f.Mission == Missions.Expedition && f.Origin != null)
+ .ToList();
+
+ DoLog(LogLevel.Warning, $"[EXP DEBUG] Active expeditions total = {expFleets.Count}");
+
+ foreach (var f in expFleets) {
+ DoLog(
+ LogLevel.Warning,
+ $"[EXP DEBUG] Fleet origin: G{f.Origin.Galaxy}:{f.Origin.System}:{f.Origin.Position} Type={f.Origin.Type}"
+ );
+ }
+ int maxPerOrigin = int.MaxValue;
+ if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.Expeditions, "MaxExpeditionsPerOrigin")) {
+ maxPerOrigin = (int)_tbotInstance.InstanceSettings.Expeditions.MaxExpeditionsPerOrigin;
+ }
+
+ DoLog(LogLevel.Warning,
+ $"[EXP DEBUG] Origins count={origins.Count}, expsToSend={expsToSend}, maxPerOrigin={maxPerOrigin}");
+
+ var capacity = new Dictionary();
+ foreach (var o in origins) {
+ int active = CountActiveExpeditionsFromOrigin(o);
+ int cap = Math.Max(0, maxPerOrigin - active);
+ capacity[o] = cap;
+
+ DoLog(LogLevel.Warning,
+ $"[EXP DEBUG] Origin {o.Coordinate.Galaxy}:{o.Coordinate.System}:{o.Coordinate.Position} Type={o.Coordinate.Type} " +
+ $"active={active} maxPerOrigin={maxPerOrigin} cap={cap}");
+ }
+
+ var originExps = origins.ToDictionary(o => o, o => 0);
+
+ // Use .Sum(x => (long)x) to ensure the addition happens in 64-bit space
+ long remaining = (long) Math.Min((long) expsToSend, capacity.Values.Sum(x => (long) x));
+
+ while (remaining > 0) {
+ bool progressed = false;
+
+ foreach (var o in origins) {
+ if (remaining <= 0) break;
+
+ if (originExps[o] < capacity[o]) {
+ originExps[o]++;
+ remaining--;
+ progressed = true;
+ }
+ }
+
+ if (!progressed) break;
+ }
+
+ foreach (var o in origins) {
+ DoLog(LogLevel.Warning,
+ $"[EXP DEBUG] PLAN origin {o.Coordinate.Galaxy}:{o.Coordinate.System}:{o.Coordinate.Position} Type={o.Coordinate.Type} " +
+ $"willSend={originExps[o]} (cap={capacity[o]})");
+ }
+
+ int delayExpedition = 0;
foreach (var origin in origins) {
- originExps.Add(origin, quot);
- }
- int rest = (int) Math.Floor((float) expsToSend % (float) origins.Count());
- for (int i = 0; i < rest; i++) {
- originExps[origins[i]]++;
- }
- LFBonuses lfBonuses = origins.First().LFBonuses;
- foreach (var origin in originExps.Keys) {
int expsToSendFromThisOrigin = originExps[origin];
if (expsToSendFromThisOrigin == 0) {
- continue;
+ if (delayExpedition > 0)
+ delayExpedition--;
+ else
+ continue;
}
else if (origin.Ships.IsEmpty()) {
DoLog(LogLevel.Warning, "Unable to send expeditions: no ships available");
+ delayExpedition++;
continue;
} else {
Ships fleet;
- if ((bool) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Active) {
+ if ((bool)_tbotInstance.InstanceSettings.Expeditions.ManualShips.Active) {
fleet = new(
- (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.LightFighter,
- (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.HeavyFighter,
- (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Cruiser,
- (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Battleship,
- (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Battlecruiser,
- (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Bomber,
- (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Destroyer,
- (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Deathstar,
- (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.SmallCargo,
- (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.LargeCargo,
- (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.ColonyShip,
- (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Recycler,
- (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.EspionageProbe,
+ (long)_tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.LightFighter,
+ (long)_tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.HeavyFighter,
+ (long)_tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Cruiser,
+ (long)_tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Battleship,
+ (long)_tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Battlecruiser,
+ (long)_tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Bomber,
+ (long)_tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Destroyer,
+ (long)_tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Deathstar,
+ (long)_tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.SmallCargo,
+ (long)_tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.LargeCargo,
+ (long)_tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.ColonyShip,
+ (long)_tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Recycler,
+ (long)_tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.EspionageProbe,
0,
0,
- (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Reaper,
- (long) _tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Pathfinder
+ (long)_tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Reaper,
+ (long)_tbotInstance.InstanceSettings.Expeditions.ManualShips.Ships.Pathfinder
);
if (!origin.Ships.HasAtLeast(fleet, expsToSendFromThisOrigin)) {
- DoLog(LogLevel.Warning, $"Unable to send expeditions: not enough ships in origin {origin.ToString()}");
+ DoLog(LogLevel.Warning, $"Unable to send expeditions: not enough ships in origin {origin}");
+ delayExpedition++;
continue;
}
} else {
@@ -189,61 +330,39 @@ protected override async Task Execute() {
}
if (primaryShip == Buildables.Null) {
DoLog(LogLevel.Warning, "Unable to send expeditions: primary ship is Null");
+ delayExpedition++;
continue;
}
var availableShips = origin.Ships.GetMovableShips();
- if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.Expeditions, "PrimaryToKeep") && (int) _tbotInstance.InstanceSettings.Expeditions.PrimaryToKeep > 0) {
- availableShips.SetAmount(primaryShip, Math.Max(0, availableShips.GetAmount(primaryShip) - (long) _tbotInstance.InstanceSettings.Expeditions.PrimaryToKeep));
- }
- DoLog(LogLevel.Warning, $"Available {primaryShip.ToString()} in origin {origin.ToString()}: {availableShips.GetAmount(primaryShip)} ({_tbotInstance.InstanceSettings.Expeditions.PrimaryToKeep} must be kept at dock)");
- fleet = _calculationService.CalcFullExpeditionShips(availableShips, primaryShip, expsToSendFromThisOrigin, _tbotInstance.UserData.serverData, _tbotInstance.UserData.researches, lfBonuses, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo);
- if (fleet.GetAmount(primaryShip) < (long) _tbotInstance.InstanceSettings.Expeditions.MinPrimaryToSend) {
- fleet.SetAmount(primaryShip, (long) _tbotInstance.InstanceSettings.Expeditions.MinPrimaryToSend);
- if (!availableShips.HasAtLeast(fleet, expsToSendFromThisOrigin)) {
- DoLog(LogLevel.Warning, $"Unable to send expeditions: available {primaryShip.ToString()} in origin {origin.ToString()} under set min number of {(long) _tbotInstance.InstanceSettings.Expeditions.MinPrimaryToSend}");
- continue;
- }
- }
- Buildables secondaryShip = Buildables.Null;
- if (!Enum.TryParse(_tbotInstance.InstanceSettings.Expeditions.SecondaryShip, true, out secondaryShip)) {
- DoLog(LogLevel.Warning, "Unable to parse SecondaryShip. Falling back to default Null");
- secondaryShip = Buildables.Null;
- }
- if (secondaryShip != Buildables.Null) {
- long secondaryToSend = Math.Min(
- (long) Math.Round(
- availableShips.GetAmount(secondaryShip) / (float) expsToSendFromThisOrigin,
- 0,
- MidpointRounding.ToZero
- ),
- (long) Math.Round(
- fleet.GetAmount(primaryShip) * (float) _tbotInstance.InstanceSettings.Expeditions.SecondaryToPrimaryRatio,
- 0,
- MidpointRounding.ToZero
- )
- );
- if (secondaryToSend < (long) _tbotInstance.InstanceSettings.Expeditions.MinSecondaryToSend) {
- DoLog(LogLevel.Warning, $"Unable to send expeditions: available {secondaryShip.ToString()} in origin {origin.ToString()} under set number of {(long) _tbotInstance.InstanceSettings.Expeditions.MinSecondaryToSend}");
- continue;
- } else {
- fleet.Add(secondaryShip, secondaryToSend);
- if (!availableShips.HasAtLeast(fleet, expsToSendFromThisOrigin)) {
- DoLog(LogLevel.Warning, $"Unable to send expeditions: not enough ships in origin {origin.ToString()}");
- continue;
- }
- }
+ if (SettingsService.IsSettingSet(_tbotInstance.InstanceSettings.Expeditions, "PrimaryToKeep")
+ && (int)_tbotInstance.InstanceSettings.Expeditions.PrimaryToKeep > 0) {
+ availableShips.SetAmount(
+ primaryShip,
+ Math.Max(0, availableShips.GetAmount(primaryShip)
+ - (long)_tbotInstance.InstanceSettings.Expeditions.PrimaryToKeep));
}
+
+ fleet = _calculationService.CalcFullExpeditionShips(
+ availableShips,
+ primaryShip,
+ expsToSendFromThisOrigin,
+ _tbotInstance.UserData.serverData,
+ _tbotInstance.UserData.researches,
+ lfBonuses,
+ _tbotInstance.UserData.userInfo.Class,
+ _tbotInstance.UserData.serverData.ProbeCargo
+ );
}
- DoLog(LogLevel.Information, $"{expsToSendFromThisOrigin.ToString()} expeditions with {fleet.ToString()} will be sent from {origin.ToString()}");
+ DoLog(LogLevel.Information, $"{expsToSendFromThisOrigin} expeditions with {fleet} will be sent from {origin}");
+
List syslist = new();
for (int i = 0; i < expsToSendFromThisOrigin; i++) {
Coordinate destination;
- if ((bool) _tbotInstance.InstanceSettings.Expeditions.SplitExpeditionsBetweenSystems.Active) {
+ if ((bool)_tbotInstance.InstanceSettings.Expeditions.SplitExpeditionsBetweenSystems.Active) {
var rand = new Random();
-
- int range = (int) _tbotInstance.InstanceSettings.Expeditions.SplitExpeditionsBetweenSystems.Range;
+ int range = (int)_tbotInstance.InstanceSettings.Expeditions.SplitExpeditionsBetweenSystems.Range;
while (expsToSendFromThisOrigin > range * 2)
range += 1;
@@ -265,36 +384,58 @@ protected override async Task Execute() {
Type = Celestials.DeepSpace
};
}
+
_tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
- Resources payload = new();
- if ((long) _tbotInstance.InstanceSettings.Expeditions.FuelToCarry > 0) {
- payload.Deuterium = (long) _tbotInstance.InstanceSettings.Expeditions.FuelToCarry;
- }
+ Resources payload = new();
+ if ((long)_tbotInstance.InstanceSettings.Expeditions.FuelToCarry > 0) {
+ payload.Deuterium = (long)_tbotInstance.InstanceSettings.Expeditions.FuelToCarry;
+ }
if (_tbotInstance.UserData.slots.ExpFree > 0) {
- var fleetId = await _fleetScheduler.SendFleet(origin, fleet, destination, Missions.Expedition, Speeds.HundredPercent, payload);
- if (fleetId == (int) SendFleetCode.AfterSleepTime) {
+ var originUpdated = await _tbotOgameBridge.UpdatePlanet(origin, UpdateTypes.Ships);
+
+ _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
+ if (_tbotInstance.UserData.slots.ExpFree <= 0) {
+ DoLog(LogLevel.Information, "Unable to send expeditions: no expedition slots available.");
+ delay = true;
+ return;
+ }
+
+ if (fleet == null || fleet.IsEmpty() || !originUpdated.Ships.HasAtLeast(fleet, 1)) {
+ DoLog(LogLevel.Warning, $"Skipping expedition: no ships available on origin {originUpdated}");
+ delayExpedition++;
+ break;
+ }
+
+ var fleetId = await _fleetScheduler.SendFleet(
+ originUpdated,
+ fleet,
+ destination,
+ Missions.Expedition,
+ Speeds.HundredPercent,
+ payload
+ );
+ if (fleetId == (int)SendFleetCode.AfterSleepTime) {
stop = true;
return;
}
- if (fleetId == (int) SendFleetCode.NotEnoughSlots) {
+ if (fleetId == (int)SendFleetCode.NotEnoughSlots) {
delay = true;
return;
}
-
- var minWaitNextFleet = (int) _tbotInstance.InstanceSettings.Expeditions.MinWaitNextFleet;
- var maxWaitNextFleet = (int) _tbotInstance.InstanceSettings.Expeditions.MaxWaitNextFleet;
- if (minWaitNextFleet < 0)
- minWaitNextFleet = 0;
- if (maxWaitNextFleet < 1)
- maxWaitNextFleet = 1;
+ int minWait = (int)_tbotInstance.InstanceSettings.Expeditions.MinWaitNextFleet;
+ int maxWait = (int)_tbotInstance.InstanceSettings.Expeditions.MaxWaitNextFleet;
- var rndWaitTimeMs = (int) RandomizeHelper.CalcRandomIntervalSecToMs(minWaitNextFleet, maxWaitNextFleet);
+ if (maxWait < minWait) {
+ DoLog(LogLevel.Warning,
+ $"Expeditions wait misconfigured (MinWaitNextFleet={minWait} > MaxWaitNextFleet={maxWait}). Swapping values.");
+ (minWait, maxWait) = (maxWait, minWait);
+ }
+ var rndWaitTimeMs = (int)RandomizeHelper.CalcRandomIntervalSecToMs(minWait, maxWait);
- DoLog(LogLevel.Information, $"Wait {((float) rndWaitTimeMs / 1000).ToString("0.00")}s for next Expedition");
+ DoLog(LogLevel.Information, $"Wait {(rndWaitTimeMs / 1000f):0.00}s for next Expedition");
await Task.Delay(rndWaitTimeMs, _ct);
-
} else {
DoLog(LogLevel.Information, "Unable to send expeditions: no expedition slots available.");
delay = true;
@@ -303,11 +444,7 @@ protected override async Task Execute() {
}
}
}
- } else {
- DoLog(LogLevel.Warning, "Unable to send expeditions: no fleet slots available");
}
- } else {
- DoLog(LogLevel.Warning, "Unable to send expeditions: no expeditions slots available");
}
}
@@ -315,47 +452,42 @@ protected override async Task Execute() {
List orderedFleets = _tbotInstance.UserData.fleets
.Where(fleet => fleet.Mission == Missions.Expedition)
.ToList();
- if ((bool) _tbotInstance.InstanceSettings.Expeditions.WaitForAllExpeditions) {
- orderedFleets = orderedFleets
- .OrderByDescending(fleet => fleet.BackIn)
- .ToList();
+
+ if ((bool)_tbotInstance.InstanceSettings.Expeditions.WaitForAllExpeditions) {
+ orderedFleets = orderedFleets.OrderByDescending(fleet => fleet.BackIn).ToList();
} else {
- orderedFleets = orderedFleets
- .OrderBy(fleet => fleet.BackIn)
- .ToList();
+ orderedFleets = orderedFleets.OrderBy(fleet => fleet.BackIn).ToList();
}
_tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
- if ((orderedFleets.Count() == 0) || (_tbotInstance.UserData.slots.ExpFree > 0 && (!((bool) _tbotInstance.InstanceSettings.Expeditions.WaitForAllExpeditions) && !((bool) _tbotInstance.InstanceSettings.Expeditions.WaitForMajorityOfExpeditions)))) {
- interval = RandomizeHelper.CalcRandomInterval(IntervalType.AboutFiveMinutes);
+ if ((orderedFleets.Count == 0) ||
+ (_tbotInstance.UserData.slots.ExpFree > 0 &&
+ !((bool)_tbotInstance.InstanceSettings.Expeditions.WaitForAllExpeditions) &&
+ !((bool)_tbotInstance.InstanceSettings.Expeditions.WaitForMajorityOfExpeditions))) {
+ interval = RandomizeHelper.CalcRandomInterval(IntervalType.AboutFiveMinutes);
} else {
-
- var minWaitNextRound = (int) _tbotInstance.InstanceSettings.Expeditions.MinWaitNextRound;
- var maxWaitNextRound = (int) _tbotInstance.InstanceSettings.Expeditions.MaxWaitNextRound;
-
- if (minWaitNextRound < 0) minWaitNextRound = 0;
- if (maxWaitNextRound < 1) maxWaitNextRound = 1;
-
- interval = (int) ((1000 * orderedFleets.First().BackIn) + RandomizeHelper.CalcRandomIntervalSecToMs(minWaitNextRound, maxWaitNextRound));
-
+ interval = (int)((1000 * orderedFleets.First().BackIn) +
+ RandomizeHelper.CalcRandomIntervalSecToMs(
+ (int)_tbotInstance.InstanceSettings.Expeditions.MinWaitNextRound,
+ (int)_tbotInstance.InstanceSettings.Expeditions.MaxWaitNextRound));
}
+
time = await _tbotOgameBridge.GetDateTime();
- if (interval <= 0)
- interval = RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
newTime = time.AddMilliseconds(interval);
ChangeWorkerPeriod(interval);
- DoLog(LogLevel.Information, $"Next check at {newTime.ToString()}");
+ DoLog(LogLevel.Information, $"Next check at {newTime}");
await _tbotOgameBridge.CheckCelestials();
}
- } catch (Exception e) {
+ }
+ catch (Exception e) {
DoLog(LogLevel.Warning, $"HandleExpeditions exception: {e.Message}");
DoLog(LogLevel.Warning, $"Stacktrace: {e.StackTrace}");
- long interval = (long) (RandomizeHelper.CalcRandomInterval(IntervalType.AMinuteOrTwo));
+ long interval = RandomizeHelper.CalcRandomInterval(IntervalType.AMinuteOrTwo);
var time = await _tbotOgameBridge.GetDateTime();
- DateTime newTime = time.AddMilliseconds(interval);
ChangeWorkerPeriod(interval);
- DoLog(LogLevel.Information, $"Next check at {newTime.ToString()}");
- } finally {
+ DoLog(LogLevel.Information, $"Next check at {time.AddMilliseconds(interval)}");
+ }
+ finally {
if (!_tbotInstance.UserData.isSleeping) {
if (stop) {
DoLog(LogLevel.Information, $"Stopping feature.");
@@ -367,13 +499,15 @@ protected override async Task Execute() {
_tbotInstance.UserData.fleets = await _fleetScheduler.UpdateFleets();
long interval;
try {
- interval = (_tbotInstance.UserData.fleets.OrderBy(f => f.BackIn).First().BackIn ?? 0) * 1000 + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+ interval = (_tbotInstance.UserData.fleets.OrderBy(f => f.BackIn).First().BackIn ?? 0) * 1000
+ + RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
} catch {
- interval = RandomizeHelper.CalcRandomInterval((int) _tbotInstance.InstanceSettings.Expeditions.CheckIntervalMin, (int) _tbotInstance.InstanceSettings.Expeditions.CheckIntervalMax);
+ interval = RandomizeHelper.CalcRandomInterval(
+ (int)_tbotInstance.InstanceSettings.Expeditions.CheckIntervalMin,
+ (int)_tbotInstance.InstanceSettings.Expeditions.CheckIntervalMax);
}
- var newTime = time.AddMilliseconds(interval);
ChangeWorkerPeriod(interval);
- DoLog(LogLevel.Information, $"Next check at {newTime.ToString()}");
+ DoLog(LogLevel.Information, $"Next check at {time.AddMilliseconds(interval)}");
}
await _tbotOgameBridge.CheckCelestials();
}
diff --git a/TBot/Workers/FleetScheduler.cs b/TBot/Workers/FleetScheduler.cs
index 6ae792d7..244f4dcb 100644
--- a/TBot/Workers/FleetScheduler.cs
+++ b/TBot/Workers/FleetScheduler.cs
@@ -27,7 +27,43 @@ public class FleetScheduler : IFleetScheduler {
private readonly ICalculationService _calcService;
- private Dictionary timers = new();
+ private readonly Dictionary timers = new();
+ private readonly object _timersLock = new();
+
+ private bool TryGetTimer(string key, out Timer timer) {
+ lock (_timersLock) {
+ return timers.TryGetValue(key, out timer);
+ }
+ }
+ private bool ContainsTimer(string key) {
+ lock (_timersLock) {
+ return timers.ContainsKey(key);
+ }
+ }
+ private void AddTimer(string key, Timer timer) {
+ lock (_timersLock) {
+ if (!timers.ContainsKey(key)) timers.Add(key, timer);
+ }
+ }
+ private void ReplaceTimer(string key, Timer timer) {
+ lock (_timersLock) {
+ if (timers.TryGetValue(key, out var oldTimer)) {
+ try { oldTimer.Dispose(); } catch { }
+ timers.Remove(key);
+ }
+ timers[key] = timer;
+ }
+ }
+ private void DisposeAndRemoveTimer(string key) {
+ lock (_timersLock) {
+ if (timers.TryGetValue(key, out var t)) {
+ try { t.Dispose(); } catch { }
+ timers.Remove(key);
+ }
+ }
+ }
+
+
public FleetScheduler(
ICalculationService helpService,
IOgameService ogameService) {
@@ -55,48 +91,65 @@ public async Task SpyCrash(Celestial fromCelestial, Coordinate target = null) {
await _tbotInstance.SendTelegramMessage($"No probes or no Fuel on {fromCelestial.Coordinate.ToString()}!");
return;
}
- // spycrash auto part
- if (target == null) {
- List spycrash = new();
- int playerid = _tbotInstance.UserData.userInfo.PlayerID;
- int sys = 0;
- for (sys = fromCelestial.Coordinate.System - 2; sys <= fromCelestial.Coordinate.System + 2; sys++) {
- sys = GeneralHelper.ClampSystem(sys);
- GalaxyInfo galaxyInfo = await _ogameService.GetGalaxyInfo(fromCelestial.Coordinate.Galaxy, sys);
- foreach (var planet in galaxyInfo.Planets) {
- try {
- if (planet != null && !planet.Administrator && !planet.Inactive && !planet.StrongPlayer && !planet.Newbie && !planet.Banned && !planet.Vacation) {
- if (planet.Player.ID != playerid) { //exclude player planet
- spycrash.Add(new(planet.Coordinate.Galaxy, planet.Coordinate.System, planet.Coordinate.Position, Celestials.Planet));
- }
- }
- } catch (NullReferenceException) {
- continue;
- }
- }
- }
-
- if (spycrash.Count() == 0) {
- await _tbotInstance.SendTelegramMessage($"No planet to spycrash on could be found over system -2 -> +2");
- return;
- } else {
- target = spycrash[random.Next(spycrash.Count())];
- }
- }
- var attackingShips = new Ships().Add(Buildables.EspionageProbe, 1);
-
- int fleetId = await SendFleet(fromCelestial, attackingShips, target, Missions.Attack, speed);
-
- if (fleetId != (int) SendFleetCode.GenericError ||
- fleetId != (int) SendFleetCode.AfterSleepTime ||
- fleetId != (int) SendFleetCode.NotEnoughSlots) {
- _tbotInstance.log(LogLevel.Information, LogSender.FleetScheduler, $"EspionageProbe sent to crash on {target.ToString()}");
-
- await _tbotInstance.SendTelegramMessage($"EspionageProbe sent to crash on {target.ToString()}");
- }
- return;
- }
-
+ List spycrash = new();
+
+ if (target == null) {
+ int playerid = _tbotInstance.UserData.userInfo.PlayerID;
+
+ for (int sys = fromCelestial.Coordinate.System - 2; sys <= fromCelestial.Coordinate.System + 2; sys++) {
+ int clampedSys = GeneralHelper.ClampSystem(sys);
+
+ GalaxyInfo galaxyInfo = await _ogameService.GetGalaxyInfo(fromCelestial.Coordinate.Galaxy, clampedSys);
+ if (galaxyInfo?.Planets == null) continue;
+
+ foreach (var planet in galaxyInfo.Planets) {
+ try {
+ if (planet != null
+ && !planet.Administrator
+ && !planet.Inactive
+ && !planet.StrongPlayer
+ && !planet.Newbie
+ && !planet.Banned
+ && !planet.Vacation
+ && planet.Player != null
+ && planet.Player.ID != playerid) {
+
+ spycrash.Add(new Coordinate(
+ planet.Coordinate.Galaxy,
+ planet.Coordinate.System,
+ planet.Coordinate.Position,
+ Celestials.Planet));
+ }
+ } catch (NullReferenceException) {
+ continue;
+ }
+ }
+ }
+
+ if (spycrash.Count == 0) {
+ await _tbotInstance.SendTelegramMessage("No planet to spycrash on could be found over system -2 -> +2");
+ return;
+ }
+
+ target = spycrash[random.Next(spycrash.Count)];
+ }
+ var attackingShips = new Ships().Add(Buildables.EspionageProbe, 1);
+
+ int fleetId = await SendFleet(fromCelestial, attackingShips, target, Missions.Attack, speed);
+
+ if (fleetId != (int)SendFleetCode.GenericError &&
+ fleetId != (int)SendFleetCode.AfterSleepTime &&
+ fleetId != (int)SendFleetCode.NotEnoughSlots)
+ {
+ _tbotInstance.log(LogLevel.Information, LogSender.FleetScheduler,
+ $"EspionageProbe sent to crash on {target}");
+
+ await _tbotInstance.SendTelegramMessage(
+ $"EspionageProbe sent to crash on {target}");
+ }
+
+ return;
+ }
public async Task AutoFleetSave(Celestial celestial, bool isSleepTimeFleetSave = false, long minDuration = 0, bool WaitFleetsReturn = false, Missions TelegramMission = Missions.None, bool fromTelegram = false, bool saveall = false) {
DateTime departureTime = await _tbotOgameBridge.GetDateTime();
_tbotInstance.SleepDuration = minDuration;
@@ -116,8 +169,7 @@ public async Task AutoFleetSave(Celestial celestial, bool isSleepTimeFleetSave =
interval = 0;
}
- if (interval > 0 && (!timers.TryGetValue("GhostSleepTimer", out Timer value))) {
- //Stop features which are sending fleets
+ if (interval > 0 && (!TryGetTimer("GhostSleepTimer", out Timer value))) {
List features = new List {
Feature.Colonize,
Feature.BrainAutoRepatriate,
@@ -141,15 +193,15 @@ public async Task AutoFleetSave(Celestial celestial, bool isSleepTimeFleetSave =
_tbotInstance.NextWakeUpTime = TimeToGhost.AddMilliseconds(minDuration * 1000);
if (saveall)
- timers.Add("GhostSleepTimer", new Timer(GhostandSleepAfterFleetsReturnAll, null, interval, Timeout.Infinite));
+ AddTimer("GhostSleepTimer", new Timer(GhostandSleepAfterFleetsReturnAll, null, interval, Timeout.Infinite));
else
- timers.Add("GhostSleepTimer", new Timer(GhostandSleepAfterFleetsReturn, null, interval, Timeout.Infinite));
+ AddTimer("GhostSleepTimer", new Timer(GhostandSleepAfterFleetsReturn, null, interval, Timeout.Infinite));
_tbotInstance.log(LogLevel.Information, LogSender.SleepMode, $"Fleets active, Next check at {TimeToGhost.ToString()}");
await _tbotInstance.SendTelegramMessage($"Waiting for fleets return, delaying ghosting at {TimeToGhost.ToString()}");
return;
- } else if (interval == 0 && (!timers.TryGetValue("GhostSleepTimer", out Timer value2))) {
+ } else if (interval == 0 && (!TryGetTimer("GhostSleepTimer", out Timer value2))) {
_tbotInstance.log(LogLevel.Information, LogSender.SleepMode, $"No fleets active, Ghosting now.");
_tbotInstance.NextWakeUpTime = departureTime.AddMilliseconds(minDuration * 1000);
@@ -159,7 +211,7 @@ public async Task AutoFleetSave(Celestial celestial, bool isSleepTimeFleetSave =
GhostandSleepAfterFleetsReturn(null);
return;
- } else if (timers.TryGetValue("GhostSleepTimer", out Timer value3)) {
+ } else if (TryGetTimer("GhostSleepTimer", out Timer value3)) {
await _tbotInstance.SendTelegramMessage($"GhostSleep already planned, try /cancelghostsleep");
return;
}
@@ -204,9 +256,8 @@ public async Task AutoFleetSave(Celestial celestial, bool isSleepTimeFleetSave =
FleetHypotesis possibleFleet = new();
int fleetId = (int) SendFleetCode.GenericError;
- bool AlreadySent = false; //permit to swith to Harvest mission if not enough fuel to Deploy if celestial far away
+ bool AlreadySent = false;
- //Doing DefaultMission or telegram /ghostto mission
Missions mission;
if (!Missions.TryParse(_tbotInstance.InstanceSettings.SleepMode.AutoFleetSave.DefaultMission, out mission)) {
_tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, "Error: Could not parse 'DefaultMission' from settings, value set to Harvest.");
@@ -223,18 +274,17 @@ public async Task AutoFleetSave(Celestial celestial, bool isSleepTimeFleetSave =
if (CheckFuel(fleet, celestial)) {
fleetId = await SendFleet(fleet.Origin, fleet.Ships, fleet.Destination, fleet.Mission, fleet.Speed, payload, _tbotInstance.UserData.userInfo.Class, true);
- if (fleetId != (int) SendFleetCode.GenericError ||
- fleetId != (int) SendFleetCode.AfterSleepTime ||
- fleetId != (int) SendFleetCode.NotEnoughSlots) {
- possibleFleet = fleet;
- AlreadySent = true;
- break;
- }
+ if (fleetId != (int)SendFleetCode.GenericError &&
+ fleetId != (int)SendFleetCode.AfterSleepTime &&
+ fleetId != (int)SendFleetCode.NotEnoughSlots) {
+ possibleFleet = fleet;
+ AlreadySent = true;
+ break;
+}
}
}
}
- //If /ghostto -> leaving function if failed
if (fromTelegram && !AlreadySent && mission == Missions.Harvest && fleetHypotesis.Count() == 0) {
await _tbotInstance.SendTelegramMessage($"No debris field found for {mission}, try to /spycrash.");
return;
@@ -243,7 +293,6 @@ public async Task AutoFleetSave(Celestial celestial, bool isSleepTimeFleetSave =
return;
}
- //Doing Deploy
if (!AlreadySent) {
_tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, $"Fleetsave from {celestial.ToString()} no {mission} possible, checking next mission..");
if (mission == Missions.Harvest) { mission = Missions.Deploy; } else { mission = Missions.Harvest; };
@@ -255,19 +304,18 @@ public async Task AutoFleetSave(Celestial celestial, bool isSleepTimeFleetSave =
if (CheckFuel(fleet, celestial)) {
fleetId = await SendFleet(fleet.Origin, fleet.Ships, fleet.Destination, fleet.Mission, fleet.Speed, payload, _tbotInstance.UserData.userInfo.Class, true);
- if (fleetId != (int) SendFleetCode.GenericError ||
- fleetId != (int) SendFleetCode.AfterSleepTime ||
- fleetId != (int) SendFleetCode.AfterSleepTime ||
- fleetId != (int) SendFleetCode.NotEnoughSlots) {
- possibleFleet = fleet;
- AlreadySent = true;
- break;
- }
+ if (fleetId != (int)SendFleetCode.GenericError &&
+ fleetId != (int)SendFleetCode.AfterSleepTime &&
+ fleetId != (int)SendFleetCode.NotEnoughSlots)
+{
+ possibleFleet = fleet;
+ AlreadySent = true;
+ break;
+}
}
}
}
}
- //Doing colonize
if (!AlreadySent && celestial.Ships.ColonyShip > 0) {
_tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, $"Fleetsave from {celestial.ToString()} no {mission} found, checking Colonize destination...");
mission = Missions.Colonize;
@@ -278,18 +326,20 @@ public async Task AutoFleetSave(Celestial celestial, bool isSleepTimeFleetSave =
if (CheckFuel(fleet, celestial)) {
fleetId = await SendFleet(fleet.Origin, fleet.Ships, fleet.Destination, fleet.Mission, fleet.Speed, payload, _tbotInstance.UserData.userInfo.Class, true);
- if (fleetId != (int) SendFleetCode.GenericError ||
- fleetId != (int) SendFleetCode.AfterSleepTime ||
- fleetId != (int) SendFleetCode.NotEnoughSlots) {
- possibleFleet = fleet;
- AlreadySent = true;
- break;
- }
+ if (fleetId != (int)SendFleetCode.GenericError &&
+ fleetId != (int)SendFleetCode.AfterSleepTime &&
+ fleetId != (int)SendFleetCode.NotEnoughSlots)
+{
+ possibleFleet = fleet;
+ AlreadySent = true;
+ break;
+}
+
}
}
}
}
- //Doing Spy
+
if (!AlreadySent && celestial.Ships.EspionageProbe > 0) {
_tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, $"Fleetsave from {celestial.ToString()} no {mission} found, checking Spy destination...");
mission = Missions.Spy;
@@ -300,25 +350,47 @@ public async Task AutoFleetSave(Celestial celestial, bool isSleepTimeFleetSave =
if (CheckFuel(fleet, celestial)) {
fleetId = await SendFleet(fleet.Origin, fleet.Ships, fleet.Destination, fleet.Mission, fleet.Speed, payload, _tbotInstance.UserData.userInfo.Class, true);
- if (fleetId != (int) SendFleetCode.GenericError ||
- fleetId != (int) SendFleetCode.AfterSleepTime ||
- fleetId != (int) SendFleetCode.NotEnoughSlots) {
- possibleFleet = fleet;
- AlreadySent = true;
- break;
- }
+ if (fleetId != (int)SendFleetCode.GenericError &&
+ fleetId != (int)SendFleetCode.AfterSleepTime &&
+ fleetId != (int)SendFleetCode.NotEnoughSlots)
+{
+ possibleFleet = fleet;
+ AlreadySent = true;
+ break;
+}
+
+ }
+ }
+ }
+ }
+
+ if (!AlreadySent) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, $"Fleetsave from {celestial.ToString()} no {mission} found, checking Transport destination...");
+ mission = Missions.Transport;
+ fleetHypotesis = await GetFleetSaveDestination(_tbotInstance.UserData.celestials, celestial, departureTime, minDuration, mission, maxDeuterium);
+ if (fleetHypotesis.Count > 0) {
+ foreach (FleetHypotesis fleet in fleetHypotesis.OrderBy(pf => pf.Fuel).ThenBy(pf => pf.Duration <= minDuration)) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, $"checking {mission} fleet to: {fleet.Destination}");
+ if (CheckFuel(fleet, celestial)) {
+ fleetId = await SendFleet(fleet.Origin, fleet.Ships, fleet.Destination, fleet.Mission, fleet.Speed, payload, _tbotInstance.UserData.userInfo.Class, true);
+
+ if (fleetId != (int)SendFleetCode.GenericError &&
+ fleetId != (int)SendFleetCode.AfterSleepTime &&
+ fleetId != (int)SendFleetCode.NotEnoughSlots)
+{
+ possibleFleet = fleet;
+ AlreadySent = true;
+ break;
+}
+
}
}
}
}
- //Doing switch
bool hasMoon = _tbotInstance.UserData.celestials.Count(c => c.HasCoords(new Coordinate(celestial.Coordinate.Galaxy, celestial.Coordinate.System, celestial.Coordinate.Position, Celestials.Moon))) == 1;
- if (!AlreadySent && hasMoon && !timers.TryGetValue("GhostSleepTimer", out Timer val)) {
+ if (!AlreadySent && hasMoon && !TryGetTimer("GhostSleepTimer", out Timer val)) {
_tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, $"Fleetsave from {celestial.ToString()} no {mission} possible (missing fuel?), checking for switch if has Moon");
- //var validSpeeds = _tbotInstance.UserData.userInfo.Class == CharacterClass.General ? Speeds.GetGeneralSpeedsList() : Speeds.GetNonGeneralSpeedsList();
- //Random randomSpeed = new Random();
- //decimal speed = validSpeeds[randomSpeed.Next(validSpeeds.Count)];
decimal speed = 10;
AlreadySent = await _tbotInstance.TelegramSwitch(speed, celestial);
}
@@ -330,34 +402,42 @@ public async Task AutoFleetSave(Celestial celestial, bool isSleepTimeFleetSave =
}
- if ((bool) _tbotInstance.InstanceSettings.SleepMode.AutoFleetSave.Recall && AlreadySent) {
- if (fleetId != (int) SendFleetCode.GenericError ||
- fleetId != (int) SendFleetCode.AfterSleepTime ||
- fleetId != (int) SendFleetCode.NotEnoughSlots) {
- Fleet fleet = _tbotInstance.UserData.fleets.Single(fleet => fleet.ID == fleetId);
- DateTime time = await _tbotOgameBridge.GetDateTime();
- var interval = ((minDuration / 2) * 1000) + RandomizeHelper.CalcRandomInterval(IntervalType.AMinuteOrTwo);
- if (interval <= 0)
- interval = RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
- DateTime newTime = time.AddMilliseconds(interval);
- timers.Add($"RecallTimer-{fleetId.ToString()}", new Timer(RetireFleet, fleet, interval, Timeout.Infinite));
- _tbotInstance.log(LogLevel.Information, LogSender.FleetScheduler, $"The fleet will be recalled at {newTime.ToString()}");
- if (fromTelegram)
- await _tbotInstance.SendTelegramMessage($"Fleet {fleetId} send to {possibleFleet.Mission} on {possibleFleet.Destination.ToString()}, fuel consumed: {possibleFleet.Fuel.ToString("#,#", CultureInfo.InvariantCulture)}, recalled at {newTime.ToString()}");
- }
- } else {
- if (fleetId != (int) SendFleetCode.GenericError ||
- fleetId != (int) SendFleetCode.AfterSleepTime ||
- fleetId != (int) SendFleetCode.NotEnoughSlots) {
- Fleet fleet = _tbotInstance.UserData.fleets.Single(fleet => fleet.ID == fleetId);
- DateTime returntime = (DateTime) fleet.BackTime;
- _tbotInstance.log(LogLevel.Information, LogSender.FleetScheduler, $"Fleet {fleetId} send to {possibleFleet.Mission} on {possibleFleet.Destination.ToString()}, arrive at {possibleFleet.Duration} fuel consumed: {possibleFleet.Fuel.ToString("#,#", CultureInfo.InvariantCulture)}");
- if (fromTelegram)
- await _tbotInstance.SendTelegramMessage($"Fleet {fleetId} send to {possibleFleet.Mission} on {possibleFleet.Destination.ToString()}, arrive at {possibleFleet.Duration.ToString()}, returned at {returntime.ToString()} fuel consumed: {possibleFleet.Fuel.ToString("#,#", CultureInfo.InvariantCulture)}");
- }
- }
- }
+ if ((bool)_tbotInstance.InstanceSettings.SleepMode.AutoFleetSave.Recall && AlreadySent)
+{
+ if (fleetId > 0)
+ {
+ Fleet fleet = _tbotInstance.UserData.fleets.SingleOrDefault(f => f.ID == fleetId);
+if (fleet == null) return;
+ DateTime time = await _tbotOgameBridge.GetDateTime();
+ var interval = ((minDuration / 2) * 1000) + RandomizeHelper.CalcRandomInterval(IntervalType.AMinuteOrTwo);
+
+ if (interval <= 0)
+ interval = RandomizeHelper.CalcRandomInterval(IntervalType.SomeSeconds);
+
+ DateTime newTime = time.AddMilliseconds(interval);
+ AddTimer($"RecallTimer-{fleetId}", new Timer(RetireFleet, fleet, interval, Timeout.Infinite));
+ _tbotInstance.log(LogLevel.Information, LogSender.FleetScheduler,
+ $"The fleet will be recalled at {newTime}");
+ }
+}
+
+ else
+{
+ if (fleetId > 0)
+ {
+ Fleet fleet = _tbotInstance.UserData.fleets.Single(f => f.ID == fleetId);
+ DateTime returntime = (DateTime)fleet.BackTime;
+
+ _tbotInstance.log(LogLevel.Information, LogSender.FleetScheduler,
+ $"Fleet {fleetId} send to {possibleFleet.Mission} on {possibleFleet.Destination}, arrive at {possibleFleet.Duration} fuel consumed: {possibleFleet.Fuel.ToString("#,#", CultureInfo.InvariantCulture)}");
+
+ if (fromTelegram)
+ await _tbotInstance.SendTelegramMessage(
+ $"Fleet {fleetId} send to {possibleFleet.Mission} on {possibleFleet.Destination}, arrive at {possibleFleet.Duration}, returned at {returntime} fuel consumed: {possibleFleet.Fuel.ToString("#,#", CultureInfo.InvariantCulture)}");
+ }
+}
+ }
public async Task SendFleet(Celestial origin, Ships ships, Coordinate destination, Missions mission, decimal speed, Resources payload = null, CharacterClass playerClass = CharacterClass.NoClass, bool force = false) {
_tbotInstance.log(LogLevel.Information, LogSender.FleetScheduler, $"Sending fleet from {origin.Coordinate.ToString()} to {destination.ToString()}. Mission: {mission.ToString()}. Speed: {(speed * 10).ToString()}% Ships: {ships.ToString()}");
@@ -393,77 +473,94 @@ public async Task SendFleet(Celestial origin, Ships ships, Coordinate desti
}
}
- /*
- if (
- playerClass != CharacterClass.General && (
- speed == Speeds.FivePercent ||
- speed == Speeds.FifteenPercent ||
- speed == Speeds.TwentyfivePercent ||
- speed == Speeds.ThirtyfivePercent ||
- speed == Speeds.FourtyfivePercent ||
- speed == Speeds.FiftyfivePercent ||
- speed == Speeds.SixtyfivePercent ||
- speed == Speeds.SeventyfivePercent ||
- speed == Speeds.EightyfivePercent ||
- speed == Speeds.NinetyfivePercent
- )
- ) {*/
-
- if (!_calcService.GetValidSpeedsForClass(playerClass).Any(s => s == speed)) {
- _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, "Unable to send fleet: speed not available for your class");
- return (int) SendFleetCode.GenericError;
- }
- origin = await _tbotOgameBridge.UpdatePlanet(origin, UpdateTypes.LFBonuses);
- FleetPrediction fleetPrediction = _calcService.CalcFleetPrediction(origin.Coordinate, destination, ships, mission, speed, _tbotInstance.UserData.researches, _tbotInstance.UserData.serverData, origin.LFBonuses, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.allianceClass);
- _tbotInstance.log(LogLevel.Debug, LogSender.FleetScheduler, $"Calculated flight time (one-way): {TimeSpan.FromSeconds(fleetPrediction.Time).ToString()}");
+if (!_calcService.GetValidSpeedsForClass(playerClass).Any(s => s == speed)) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler,
+ "Unable to send fleet: speed not available for your class");
+ return (int)SendFleetCode.GenericError;
+}
- var flightTime = mission switch {
- Missions.Deploy => fleetPrediction.Time,
- Missions.Expedition => (long) Math.Round((double) (2 * fleetPrediction.Time) + 3600, 0, MidpointRounding.ToPositiveInfinity),
- _ => (long) Math.Round((double) (2 * fleetPrediction.Time), 0, MidpointRounding.ToPositiveInfinity),
- };
- _tbotInstance.log(LogLevel.Debug, LogSender.FleetScheduler, $"Calculated flight time (full trip): {TimeSpan.FromSeconds(flightTime).ToString()}");
- _tbotInstance.log(LogLevel.Debug, LogSender.FleetScheduler, $"Calculated flight fuel: {fleetPrediction.Fuel.ToString()}");
+origin = await _tbotOgameBridge.UpdatePlanet(origin, UpdateTypes.LFBonuses);
+
+FleetPrediction fleetPrediction = _calcService.CalcFleetPrediction(
+ origin.Coordinate,
+ destination,
+ ships,
+ mission,
+ speed,
+ _tbotInstance.UserData.researches,
+ _tbotInstance.UserData.serverData,
+ origin.LFBonuses,
+ _tbotInstance.UserData.userInfo.Class,
+ _tbotInstance.UserData.allianceClass
+);
+
+_tbotInstance.log(LogLevel.Debug, LogSender.FleetScheduler,
+ $"Calculated flight time (one-way): {TimeSpan.FromSeconds(fleetPrediction.Time)}");
+
+var flightTime = mission switch
+{
+ Missions.Deploy => fleetPrediction.Time,
+ Missions.Expedition => (long)Math.Round((double)(2 * fleetPrediction.Time) + 3600, 0, MidpointRounding.ToPositiveInfinity),
+ _ => (long)Math.Round((double)(2 * fleetPrediction.Time), 0, MidpointRounding.ToPositiveInfinity)
+};
+_tbotInstance.log(LogLevel.Debug, LogSender.FleetScheduler,
+ $"Calculated flight time (full trip): {TimeSpan.FromSeconds(flightTime)}");
+_tbotInstance.log(LogLevel.Debug, LogSender.FleetScheduler,
+ $"Calculated flight fuel: {fleetPrediction.Fuel}");
+
+origin = await _tbotOgameBridge.UpdatePlanet(origin, UpdateTypes.Resources);
+
+if (origin.Resources.Deuterium < fleetPrediction.Fuel) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler,
+ "Unable to send fleet: not enough deuterium!");
+ return (int)SendFleetCode.GenericError;
+}
- origin = await _tbotOgameBridge.UpdatePlanet(origin, UpdateTypes.Resources);
- if (origin.Resources.Deuterium < fleetPrediction.Fuel) {
- _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, "Unable to send fleet: not enough deuterium!");
- return (int) SendFleetCode.GenericError;
- }
- if (_calcService.CalcFleetFuelCapacity(ships, _tbotInstance.UserData.serverData, _tbotInstance.UserData.researches.HyperspaceTechnology, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo) != 0 && _calcService.CalcFleetFuelCapacity(ships, _tbotInstance.UserData.serverData, _tbotInstance.UserData.researches.HyperspaceTechnology, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.serverData.ProbeCargo) < fleetPrediction.Fuel) {
- _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, "Unable to send fleet: ships don't have enough fuel capacity!");
- return (int) SendFleetCode.GenericError;
- }
- if (
- (bool) _tbotInstance.InstanceSettings.SleepMode.Active &&
- DateTime.TryParse((string) _tbotInstance.InstanceSettings.SleepMode.GoToSleep, out DateTime goToSleep) &&
- DateTime.TryParse((string) _tbotInstance.InstanceSettings.SleepMode.WakeUp, out DateTime wakeUp) &&
- !force
- ) {
- DateTime time = await _tbotOgameBridge.GetDateTime();
- if (GeneralHelper.ShouldSleep(time, goToSleep, wakeUp)) {
- _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, "Unable to send fleet: bed time has passed");
- return (int) SendFleetCode.AfterSleepTime;
- }
- if (goToSleep >= wakeUp) {
- wakeUp = wakeUp.AddDays(1);
- }
- if (goToSleep < time) {
- goToSleep = goToSleep.AddDays(1);
- }
- if (wakeUp < time) {
- wakeUp = wakeUp.AddDays(1);
- }
- _tbotInstance.log(LogLevel.Debug, LogSender.FleetScheduler, $"goToSleep : {goToSleep.ToString()}");
- _tbotInstance.log(LogLevel.Debug, LogSender.FleetScheduler, $"wakeUp : {wakeUp.ToString()}");
- DateTime returnTime = time.AddSeconds(flightTime);
- _tbotInstance.log(LogLevel.Debug, LogSender.FleetScheduler, $"returnTime : {returnTime.ToString()}");
-
- if (returnTime >= goToSleep && returnTime <= wakeUp) {
- _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, "Unable to send fleet: it would come back during sleep time");
- return (int) SendFleetCode.AfterSleepTime;
- }
- }
+long fuelCapacity = _calcService.CalcFleetFuelCapacity(
+ ships,
+ _tbotInstance.UserData.serverData,
+ _tbotInstance.UserData.researches.HyperspaceTechnology,
+ _tbotInstance.UserData.userInfo.Class,
+ _tbotInstance.UserData.serverData.ProbeCargo
+);
+
+if (fuelCapacity != 0 && fuelCapacity < fleetPrediction.Fuel) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler,
+ "Unable to send fleet: ships don't have enough fuel capacity!");
+ return (int)SendFleetCode.GenericError;
+}
+
+bool ignoreSleepForExpeditions =
+ mission == Missions.Expedition &&
+ (bool)_tbotInstance.InstanceSettings.Expeditions.IgnoreSleep;
+
+if (
+ (bool)_tbotInstance.InstanceSettings.SleepMode.Active &&
+ DateTime.TryParse((string)_tbotInstance.InstanceSettings.SleepMode.GoToSleep, out DateTime goToSleep) &&
+ DateTime.TryParse((string)_tbotInstance.InstanceSettings.SleepMode.WakeUp, out DateTime wakeUp) &&
+ !force
+)
+{
+ DateTime time = await _tbotOgameBridge.GetDateTime();
+
+ if (GeneralHelper.ShouldSleep(time, goToSleep, wakeUp)) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler,
+ "Unable to send fleet: bed time has passed");
+ return (int)SendFleetCode.AfterSleepTime;
+ }
+
+ if (goToSleep >= wakeUp) wakeUp = wakeUp.AddDays(1);
+ if (goToSleep < time) goToSleep = goToSleep.AddDays(1);
+ if (wakeUp < time) wakeUp = wakeUp.AddDays(1);
+
+ DateTime returnTime = time.AddSeconds(flightTime);
+
+ if (returnTime >= goToSleep && returnTime <= wakeUp && !ignoreSleepForExpeditions) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler,
+ "Unable to send fleet: it would come back during sleep time");
+ return (int)SendFleetCode.AfterSleepTime;
+ }
+}
_tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
int slotsToLeaveFree = (int) _tbotInstance.InstanceSettings.General.SlotsToLeaveFree;
if (_tbotInstance.UserData.slots.Free == 0) {
@@ -481,11 +578,15 @@ public async Task SendFleet(Celestial origin, Ships ships, Coordinate desti
payload.Deuterium = 0;
if (payload.Food < 0)
payload.Food = 0;
- Fleet fleet = await _ogameService.SendFleet(origin, ships, destination, mission, speed, payload);
- _tbotInstance.log(LogLevel.Information, LogSender.FleetScheduler, "Fleet succesfully sent");
- _tbotInstance.UserData.fleets = await _ogameService.GetFleets();
+ Fleet newFleet = await _ogameService.SendFleet(origin, ships, destination, mission, speed, payload);
+ if (newFleet == null) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, $"Unable to send fleet from {origin.Coordinate.ToString()}: no ships to send.");
+ return (int) SendFleetCode.GenericError;
+ }
+ _tbotInstance.UserData.fleets.Add(newFleet);
+ _tbotInstance.log(LogLevel.Information, LogSender.FleetScheduler, "Fleet sent successfully!");
_tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
- return fleet.ID;
+ return newFleet.ID;
} catch (Exception e) {
_tbotInstance.log(LogLevel.Error, LogSender.FleetScheduler, $"Unable to send fleet: an exception has occurred: {e.Message}");
_tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, $"Stacktrace: {e.StackTrace}");
@@ -498,14 +599,58 @@ public async Task SendFleet(Celestial origin, Ships ships, Coordinate desti
}
public async Task CancelFleet(Fleet fleet) {
- //_tbotInstance.log(LogLevel.Information, LogSender.FleetScheduler, $"Recalling fleet id {fleet.ID} originally from {fleet.Origin.ToString()} to {fleet.Destination.ToString()} with mission: {fleet.Mission.ToString()}. Start time: {fleet.StartTime.ToString()} - Arrival time: {fleet.ArrivalTime.ToString()} - Ships: {fleet.Ships.ToString()}");
- _tbotInstance.UserData.slots = await _tbotOgameBridge.UpdateSlots();
+ try {
+ bool isUnderAttack = await _ogameService.IsUnderAttack();
+ if (isUnderAttack) {
+ var attacks = await _ogameService.GetAttacks();
+ var attacksOnOrigin = attacks
+ .Where(a => a.Destination.IsSame(fleet.Origin))
+ .OrderByDescending(a => a.ArriveIn)
+ .ToList();
+
+ if (attacksOnOrigin.Any()) {
+ var lastAttack = attacksOnOrigin.First();
+ var currentTime = await _tbotOgameBridge.GetDateTime();
+ var currentFleet = _tbotInstance.UserData.fleets.SingleOrDefault(f => f.ID == fleet.ID);
+ if (currentFleet != null && currentFleet.ArriveIn > 0) {
+ long estimatedReturnSeconds = currentFleet.ArriveIn * 2;
+
+ _tbotInstance.log(LogLevel.Debug, LogSender.FleetScheduler, $"[RECALL-CHECK] Fleet {fleet.ID} would return in ~{estimatedReturnSeconds}s if recalled now");
+ _tbotInstance.log(LogLevel.Debug, LogSender.FleetScheduler, $"[RECALL-CHECK] Last attack on {fleet.Origin} arrives in {lastAttack.ArriveIn}s");
+ if (estimatedReturnSeconds < lastAttack.ArriveIn) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, $"[RECALL-BLOCKED] Fleet {fleet.ID} would return in {estimatedReturnSeconds}s but attack arrives in {lastAttack.ArriveIn}s - DELAYING RECALL!");
+
+ var newRecallDelay = (lastAttack.ArriveIn - estimatedReturnSeconds + (lastAttack.ArriveIn / 100 * 30)) * 1000;
+ if (newRecallDelay < 60000) newRecallDelay = 60000;
+
+ _tbotInstance.log(LogLevel.Information, LogSender.FleetScheduler, $"[RECALL-RESCHEDULED] New recall in {newRecallDelay/1000}s");
+
+ DisposeAndRemoveTimer($"RecallTimer-{fleet.ID.ToString()}");
+ AddTimer($"RecallTimer-{fleet.ID.ToString()}", new Timer(RetireFleet, fleet, (int)newRecallDelay, Timeout.Infinite));
+
+ if ((bool) _tbotInstance.InstanceSettings.Defender.TelegramMessenger.Active) {
+ await _tbotInstance.SendTelegramMessage($"⚠️ Recall delayed! Attack still incoming on {fleet.Origin}. New recall in {newRecallDelay/1000}s");
+ }
+ return;
+ } else {
+ _tbotInstance.log(LogLevel.Information, LogSender.FleetScheduler, $"[RECALL-OK] Fleet {fleet.ID} return time ({estimatedReturnSeconds}s) is AFTER last attack ({lastAttack.ArriveIn}s) - proceeding with recall");
+ }
+ }
+ }
+ }
+ } catch (Exception e) {
+ _tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, $"[RECALL-CHECK] Error checking attacks before recall: {e.Message}");
+ }
+
try {
await Task.Delay((int) RandomizeHelper.CalcRandomInterval(IntervalType.AFewSeconds));
_tbotInstance.log(LogLevel.Debug, LogSender.FleetScheduler, $"Recall Fleet with ID: {fleet.ID}");
await _ogameService.CancelFleet(fleet);
await Task.Delay((int) RandomizeHelper.CalcRandomInterval(IntervalType.AFewSeconds));
- _tbotInstance.UserData.fleets = await UpdateFleets();
+ var updatedFleets = await UpdateFleets();
+ lock (_fleetLock) {
+ _tbotInstance.UserData.fleets = updatedFleets;
+ }
Fleet recalledFleet = _tbotInstance.UserData.fleets.SingleOrDefault(f => f.ID == fleet.ID) ?? new() { ID = (int) SendFleetCode.GenericError };
if (recalledFleet.ID == (int) SendFleetCode.GenericError) {
_tbotInstance.log(LogLevel.Error, LogSender.FleetScheduler, "Unable to recall fleet: an unknon error has occurred, already recalled ?.");
@@ -521,12 +666,8 @@ public async Task CancelFleet(Fleet fleet) {
_tbotInstance.log(LogLevel.Warning, LogSender.FleetScheduler, $"Stacktrace: {e.StackTrace}");
return;
} finally {
- if (timers.TryGetValue($"RecallTimer-{fleet.ID.ToString()}", out Timer value)) {
- value.Dispose();
- timers.Remove($"RecallTimer-{fleet.ID.ToString()}");
- }
-
- }
+ DisposeAndRemoveTimer($"RecallTimer-{fleet.ID.ToString()}");
+}
}
public async Task> UpdateFleets() {
try {
@@ -540,35 +681,40 @@ public async Task> UpdateFleets() {
}
public async void RetireFleet(object fleet) {
- await CancelFleet((Fleet)fleet);
+ try {
+ await CancelFleet((Fleet)fleet);
+ } catch (Exception ex) {
+ _tbotInstance?.log(LogLevel.Error, LogSender.FleetScheduler, $"RetireFleet callback failed: {ex.Message}");
+ }
}
private async void GhostandSleepAfterFleetsReturnAll(object state) {
- if (timers.TryGetValue("GhostSleepTimer", out Timer value))
- value.Dispose();
- timers.Remove("GhostSleepTimer");
-
+ try {
+ DisposeAndRemoveTimer("GhostSleepTimer");
- var celestialsToFleetsave = await _tbotOgameBridge.UpdateCelestials();
- celestialsToFleetsave = celestialsToFleetsave.Where(c => c.Coordinate.Type == Celestials.Moon).ToList();
- if (celestialsToFleetsave.Count == 0)
- celestialsToFleetsave = celestialsToFleetsave.Where(c => c.Coordinate.Type == Celestials.Planet).ToList();
+ var celestialsToFleetsave = await _tbotOgameBridge.UpdateCelestials();
+ var moons = celestialsToFleetsave.Where(c => c.Coordinate.Type == Celestials.Moon).ToList();
+ var targets = moons.Count > 0 ? moons : celestialsToFleetsave.Where(c => c.Coordinate.Type == Celestials.Planet).ToList();
- foreach (Celestial celestial in celestialsToFleetsave)
- await AutoFleetSave(celestial, false, _tbotInstance.SleepDuration, false, _tbotInstance.TelegramUserData.Mission, true);
+ foreach (Celestial celestial in targets)
+ await AutoFleetSave(celestial, false, _tbotInstance.SleepDuration, false, _tbotInstance.TelegramUserData.Mission, true);
- await _tbotInstance.SleepNow(_tbotInstance.NextWakeUpTime);
+ await _tbotInstance.SleepNow(_tbotInstance.NextWakeUpTime);
+ } catch (Exception ex) {
+ _tbotInstance?.log(LogLevel.Error, LogSender.SleepMode, $"GhostSleep(all) timer callback failed: {ex.Message}");
+ }
}
private async void GhostandSleepAfterFleetsReturn(object state) {
- if (timers.TryGetValue("GhostSleepTimer", out Timer value))
- value.Dispose();
- timers.Remove("GhostSleepTimer");
-
- await AutoFleetSave(_tbotInstance.TelegramUserData.CurrentCelestialToSave, false, _tbotInstance.SleepDuration, false, _tbotInstance.TelegramUserData.Mission, true);
+ try {
+ DisposeAndRemoveTimer("GhostSleepTimer");
- await _tbotInstance.SleepNow(_tbotInstance.NextWakeUpTime);
+ await AutoFleetSave(_tbotInstance.TelegramUserData.CurrentCelestialToSave, false, _tbotInstance.SleepDuration, false, _tbotInstance.TelegramUserData.Mission, true);
+ await _tbotInstance.SleepNow(_tbotInstance.NextWakeUpTime);
+ } catch (Exception ex) {
+ _tbotInstance?.log(LogLevel.Error, LogSender.SleepMode, $"GhostSleep timer callback failed: {ex.Message}");
+ }
}
private bool CheckFuel(FleetHypotesis fleetHypotesis, Celestial celestial) {
@@ -593,7 +739,49 @@ private async Task> GetFleetSaveDestination(List
origin = await _tbotOgameBridge.UpdatePlanet(origin, UpdateTypes.LFBonuses);
int sys = 0;
+ int playerid = _tbotInstance.UserData.userInfo.PlayerID;
+
switch (mission) {
+ case Missions.Transport:
+ for (sys = origin.Coordinate.System - 5; sys <= origin.Coordinate.System + 5; sys++) {
+ if (possibleDestinations.Count() == 10) {
+ break;
+ }
+ sys = GeneralHelper.ClampSystem(sys);
+
+ var gi = await _ogameService.GetGalaxyInfo(origin.Coordinate.Galaxy, sys);
+ if (gi?.Planets == null) continue;
+
+ foreach (var planet in gi.Planets) {
+ if (planet != null && !planet.Vacation && !planet.Administrator && !planet.Banned && !planet.StrongPlayer) {
+ possibleDestinations.Add(new(planet.Coordinate.Galaxy, planet.Coordinate.System, planet.Coordinate.Position, Celestials.Planet));
+ }
+ }
+ }
+
+ if (possibleDestinations.Count() > 0) {
+ foreach (var possibleDestination in possibleDestinations) {
+ foreach (var currentSpeed in validSpeeds) {
+ FleetPrediction fleetPrediction = _calcService.CalcFleetPrediction(origin.Coordinate, possibleDestination, origin.Ships.GetMovableShips(), mission, currentSpeed, _tbotInstance.UserData.researches, _tbotInstance.UserData.serverData, origin.LFBonuses, _tbotInstance.UserData.userInfo.Class, _tbotInstance.UserData.allianceClass);
+
+ FleetHypotesis fleetHypotesis = new() {
+ Origin = origin,
+ Destination = possibleDestination,
+ Ships = origin.Ships.GetMovableShips(),
+ Mission = mission,
+ Speed = currentSpeed,
+ Duration = fleetPrediction.Time,
+ Fuel = fleetPrediction.Fuel
+ };
+ if (fleetHypotesis.Duration >= minFlightTime / 2 && fleetHypotesis.Fuel <= maxFuel) {
+ possibleFleets.Add(fleetHypotesis);
+ break;
+ }
+ }
+ }
+ }
+ break;
+
case Missions.Spy:
if (origin.Ships.EspionageProbe == 0) {
_tbotInstance.log(LogLevel.Information, LogSender.FleetScheduler, $"No espionageprobe available, skipping to next mission...");
@@ -630,15 +818,15 @@ private async Task> GetFleetSaveDestination(List
galaxyInfo = await _ogameService.GetGalaxyInfo(origin.Coordinate);
for (sys = origin.Coordinate.System - 5; sys <= origin.Coordinate.System + 5; sys++) {
- int pos = 1;
+ int pos = 0;
sys = GeneralHelper.ClampSystem(sys);
galaxyInfo = await _ogameService.GetGalaxyInfo(origin.Coordinate.Galaxy, sys);
foreach (var planet in galaxyInfo.Planets) {
- if (planet == null) {
- possibleDestinations.Add(new(origin.Coordinate.Galaxy, origin.Coordinate.System, pos));
- }
- pos++;
- }
+ pos++;
+ if (pos >= 1 && pos <= 15 && planet == null) {
+ possibleDestinations.Add(new(origin.Coordinate.Galaxy, sys, pos));
+ }
+ }
}
if (possibleDestinations.Count() > 0) {
@@ -669,7 +857,6 @@ private async Task> GetFleetSaveDestination(List
_tbotInstance.log(LogLevel.Information, LogSender.FleetScheduler, $"No recycler available, skipping to next mission...");
break;
}
- int playerid = _tbotInstance.UserData.userInfo.PlayerID;
for (sys = origin.Coordinate.System - 5; sys <= origin.Coordinate.System + 5; sys++) {
sys = GeneralHelper.ClampSystem(sys);
galaxyInfo = await _ogameService.GetGalaxyInfo(origin.Coordinate.Galaxy, sys);
@@ -751,7 +938,7 @@ private async Task> GetFleetSaveDestination(List
}
}
- public async Task