diff --git a/cmd/c/main.go b/cmd/c/main.go index 2d53ae16..74ca0096 100644 --- a/cmd/c/main.go +++ b/cmd/c/main.go @@ -332,9 +332,9 @@ func BuildShips(planetID, shipID, nbr C.int) (errorMsg *C.char) { // TODO: GetProduction(PlanetID) ([]Quantifiable, error) //export ConstructionsBeingBuilt -func ConstructionsBeingBuilt(planetID C.int) (buildingID, buildingCountdown, researchID, researchCountdown C.int) { - a, b, c, d := bot.ConstructionsBeingBuilt(ogame2.CelestialID(planetID)) - return C.int(a), C.int(b), C.int(c), C.int(d) +func ConstructionsBeingBuilt(planetID C.int) (buildingID, buildingCountdown, researchID, researchCountdown, lfBuildingID, lfBuildingCountdown C.int) { + a, b, c, d, e, f := bot.ConstructionsBeingBuilt(ogame2.CelestialID(planetID)) + return C.int(a), C.int(b), C.int(c), C.int(d), C.int(e), C.int(f) } //export CancelBuilding diff --git a/cmd/ogamed/main.go b/cmd/ogamed/main.go index 37506c8c..d5ded0e4 100644 --- a/cmd/ogamed/main.go +++ b/cmd/ogamed/main.go @@ -287,6 +287,7 @@ func start(c *cli.Context) error { e.GET("/bot/get-research", wrapper.GetResearchHandler) e.GET("/bot/buy-offer-of-the-day", wrapper.BuyOfferOfTheDayHandler) e.GET("/bot/price/:ogameID/:nbr", wrapper.GetPriceHandler) + e.GET("/bot/requirements/:ogameID", wrapper.GetRequirementsHandler) e.GET("/bot/moons", wrapper.GetMoonsHandler) e.GET("/bot/moons/:moonID", wrapper.GetMoonHandler) e.GET("/bot/moons/:galaxy/:system/:position", wrapper.GetMoonByCoordHandler) @@ -300,6 +301,7 @@ func start(c *cli.Context) error { e.GET("/bot/planets/:planetID/resource-settings", wrapper.GetResourceSettingsHandler) e.POST("/bot/planets/:planetID/resource-settings", wrapper.SetResourceSettingsHandler) e.GET("/bot/planets/:planetID/resources-buildings", wrapper.GetResourcesBuildingsHandler) + e.GET("/bot/planets/:planetID/lifeform-buildings", wrapper.GetLfBuildingsHandler) e.GET("/bot/planets/:planetID/defence", wrapper.GetDefenseHandler) e.GET("/bot/planets/:planetID/ships", wrapper.GetShipsHandler) e.GET("/bot/planets/:planetID/facilities", wrapper.GetFacilitiesHandler) diff --git a/pkg/extractor/extractor.go b/pkg/extractor/extractor.go index 7d73a19a..fa268a3e 100644 --- a/pkg/extractor/extractor.go +++ b/pkg/extractor/extractor.go @@ -75,7 +75,7 @@ type OverviewExtractorBytes interface { ExtractCancelLfBuildingInfos(pageHTML []byte) (token string, id, listID int64, err error) ExtractCancelResearchInfos(pageHTML []byte) (token string, techID, listID int64, err error) ExtractCharacterClass(pageHTML []byte) (ogame.CharacterClass, error) - ExtractConstructions(pageHTML []byte) (buildingID ogame.ID, buildingCountdown int64, researchID ogame.ID, researchCountdown int64) + ExtractConstructions(pageHTML []byte) (buildingID ogame.ID, buildingCountdown int64, researchID ogame.ID, researchCountdown int64, lfBuildingID ogame.ID, lfBuildingCountdown int64) ExtractDMCosts(pageHTML []byte) (ogame.DMCosts, error) ExtractFleetDeutSaveFactor(pageHTML []byte) float64 ExtractOverviewProduction(pageHTML []byte) ([]ogame.Quantifiable, int64, error) diff --git a/pkg/extractor/v6/extractor.go b/pkg/extractor/v6/extractor.go index 0fe71129..2a25c968 100644 --- a/pkg/extractor/v6/extractor.go +++ b/pkg/extractor/v6/extractor.go @@ -809,7 +809,7 @@ func (e *Extractor) ExtractFederation(pageHTML []byte) url.Values { } // ExtractConstructions ... -func (e *Extractor) ExtractConstructions(pageHTML []byte) (buildingID ogame.ID, buildingCountdown int64, researchID ogame.ID, researchCountdown int64) { +func (e *Extractor) ExtractConstructions(pageHTML []byte) (buildingID ogame.ID, buildingCountdown int64, researchID ogame.ID, researchCountdown int64, lfBuildingID ogame.ID, lfBuildingCountdown int64) { return extractConstructions(pageHTML) } diff --git a/pkg/extractor/v6/extractor_test.go b/pkg/extractor/v6/extractor_test.go index fd160b10..7bd5caba 100644 --- a/pkg/extractor/v6/extractor_test.go +++ b/pkg/extractor/v6/extractor_test.go @@ -1884,7 +1884,7 @@ func TestCancelResearch(t *testing.T) { func TestGetConstructions(t *testing.T) { pageHTMLBytes, _ := ioutil.ReadFile("../../../samples/unversioned/overview_active.html") - buildingID, buildingCountdown, researchID, researchCountdown := NewExtractor().ExtractConstructions(pageHTMLBytes) + buildingID, buildingCountdown, researchID, researchCountdown, _, _ := NewExtractor().ExtractConstructions(pageHTMLBytes) assert.Equal(t, ogame.CrystalMineID, buildingID) assert.Equal(t, int64(731), buildingCountdown) assert.Equal(t, ogame.CombustionDriveID, researchID) diff --git a/pkg/extractor/v6/extracts.go b/pkg/extractor/v6/extracts.go index 5fcefeb5..1332b5fb 100644 --- a/pkg/extractor/v6/extracts.go +++ b/pkg/extractor/v6/extracts.go @@ -1913,7 +1913,7 @@ func extractFederation(pageHTML []byte) url.Values { return payload } -func extractConstructions(pageHTML []byte) (buildingID ogame.ID, buildingCountdown int64, researchID ogame.ID, researchCountdown int64) { +func extractConstructions(pageHTML []byte) (buildingID ogame.ID, buildingCountdown int64, researchID ogame.ID, researchCountdown int64, lfBuildingID ogame.ID, lfBuildingCountdown int64) { buildingCountdownMatch := regexp.MustCompile(`getElementByIdWithCache\("Countdown"\),(\d+),`).FindSubmatch(pageHTML) if len(buildingCountdownMatch) > 0 { buildingCountdown = int64(utils.ToInt(buildingCountdownMatch[1])) diff --git a/pkg/extractor/v7/extractor.go b/pkg/extractor/v7/extractor.go index acb7190f..f215e683 100644 --- a/pkg/extractor/v7/extractor.go +++ b/pkg/extractor/v7/extractor.go @@ -96,7 +96,7 @@ func (e Extractor) ExtractResourcesDetails(pageHTML []byte) (out ogame.Resources } // ExtractConstructions ... -func (e Extractor) ExtractConstructions(pageHTML []byte) (buildingID ogame.ID, buildingCountdown int64, researchID ogame.ID, researchCountdown int64) { +func (e Extractor) ExtractConstructions(pageHTML []byte) (buildingID ogame.ID, buildingCountdown int64, researchID ogame.ID, researchCountdown int64, lfBuildingID ogame.ID, lfBuildingCountdown int64) { return ExtractConstructions(pageHTML, clockwork.NewRealClock()) } diff --git a/pkg/extractor/v7/extractor_test.go b/pkg/extractor/v7/extractor_test.go index 4f99aa91..b1d96539 100644 --- a/pkg/extractor/v7/extractor_test.go +++ b/pkg/extractor/v7/extractor_test.go @@ -313,7 +313,7 @@ func TestExtractFleetSlot_FleetDispatch(t *testing.T) { func TestGetConstructionsV7(t *testing.T) { pageHTMLBytes, _ := ioutil.ReadFile("../../../samples/v7/overview_supplies_in_construction.html") clock := clockwork.NewFakeClockAt(time.Date(2019, 11, 12, 9, 6, 43, 0, time.UTC)) - buildingID, buildingCountdown, researchID, researchCountdown := ExtractConstructions(pageHTMLBytes, clock) + buildingID, buildingCountdown, researchID, researchCountdown, _, _ := ExtractConstructions(pageHTMLBytes, clock) assert.Equal(t, ogame.MetalMineID, buildingID) assert.Equal(t, int64(62), buildingCountdown) assert.Equal(t, ogame.EnergyTechnologyID, researchID) diff --git a/pkg/extractor/v7/extracts.go b/pkg/extractor/v7/extracts.go index 6bfdc879..bdf3309b 100644 --- a/pkg/extractor/v7/extracts.go +++ b/pkg/extractor/v7/extracts.go @@ -37,24 +37,24 @@ func extractPremiumToken(pageHTML []byte, days int64) (token string, err error) func extractResourcesDetailsFromFullPageFromDoc(doc *goquery.Document) ogame.ResourcesDetails { out := ogame.ResourcesDetails{} - out.Metal.Available = utils.ParseInt(strings.Split(doc.Find("span#resources_metal").AttrOr("data-raw", "0"), ".")[0]) - out.Crystal.Available = utils.ParseInt(strings.Split(doc.Find("span#resources_crystal").AttrOr("data-raw", "0"), ".")[0]) - out.Deuterium.Available = utils.ParseInt(strings.Split(doc.Find("span#resources_deuterium").AttrOr("data-raw", "0"), ".")[0]) - out.Energy.Available = utils.ParseInt(doc.Find("span#resources_energy").Text()) - out.Darkmatter.Available = utils.ParseInt(doc.Find("span#resources_darkmatter").Text()) - metalDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(doc.Find("li#metal_box").AttrOr("title", ""))) - crystalDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(doc.Find("li#crystal_box").AttrOr("title", ""))) - deuteriumDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(doc.Find("li#deuterium_box").AttrOr("title", ""))) - energyDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(doc.Find("li#energy_box").AttrOr("title", ""))) - darkmatterDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(doc.Find("li#darkmatter_box").AttrOr("title", ""))) + metalDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(doc.Find("div#metal_box").AttrOr("title", ""))) + crystalDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(doc.Find("div#crystal_box").AttrOr("title", ""))) + deuteriumDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(doc.Find("div#deuterium_box").AttrOr("title", ""))) + energyDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(doc.Find("div#energy_box").AttrOr("title", ""))) + darkmatterDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(doc.Find("div#darkmatter_box").AttrOr("title", ""))) + out.Metal.Available = utils.ParseInt(metalDoc.Find("table tr").Eq(0).Find("td").Eq(0).Text()) out.Metal.StorageCapacity = utils.ParseInt(metalDoc.Find("table tr").Eq(1).Find("td").Eq(0).Text()) out.Metal.CurrentProduction = utils.ParseInt(metalDoc.Find("table tr").Eq(2).Find("td").Eq(0).Text()) + out.Crystal.Available = utils.ParseInt(crystalDoc.Find("table tr").Eq(0).Find("td").Eq(0).Text()) out.Crystal.StorageCapacity = utils.ParseInt(crystalDoc.Find("table tr").Eq(1).Find("td").Eq(0).Text()) out.Crystal.CurrentProduction = utils.ParseInt(crystalDoc.Find("table tr").Eq(2).Find("td").Eq(0).Text()) + out.Deuterium.Available = utils.ParseInt(deuteriumDoc.Find("table tr").Eq(0).Find("td").Eq(0).Text()) out.Deuterium.StorageCapacity = utils.ParseInt(deuteriumDoc.Find("table tr").Eq(1).Find("td").Eq(0).Text()) out.Deuterium.CurrentProduction = utils.ParseInt(deuteriumDoc.Find("table tr").Eq(2).Find("td").Eq(0).Text()) + out.Energy.Available = utils.ParseInt(energyDoc.Find("table tr").Eq(0).Find("td").Eq(0).Text()) out.Energy.CurrentProduction = utils.ParseInt(energyDoc.Find("table tr").Eq(1).Find("td").Eq(0).Text()) out.Energy.Consumption = utils.ParseInt(energyDoc.Find("table tr").Eq(2).Find("td").Eq(0).Text()) + out.Darkmatter.Available = utils.ParseInt(darkmatterDoc.Find("table tr").Eq(0).Find("td").Eq(0).Text()) out.Darkmatter.Purchased = utils.ParseInt(darkmatterDoc.Find("table tr").Eq(1).Find("td").Eq(0).Text()) out.Darkmatter.Found = utils.ParseInt(darkmatterDoc.Find("table tr").Eq(2).Find("td").Eq(0).Text()) return out @@ -220,7 +220,7 @@ func extractResourcesDetails(pageHTML []byte) (out ogame.ResourcesDetails, err e return } -func ExtractConstructions(pageHTML []byte, clock clockwork.Clock) (buildingID ogame.ID, buildingCountdown int64, researchID ogame.ID, researchCountdown int64) { +func ExtractConstructions(pageHTML []byte, clock clockwork.Clock) (buildingID ogame.ID, buildingCountdown int64, researchID ogame.ID, researchCountdown int64, lfBuildingID ogame.ID, lfBuildingCountdown int64) { buildingCountdownMatch := regexp.MustCompile(`var restTimebuilding = (\d+) -`).FindSubmatch(pageHTML) if len(buildingCountdownMatch) > 0 { buildingCountdown = int64(utils.ToInt(buildingCountdownMatch[1])) - clock.Now().Unix() diff --git a/pkg/extractor/v71/extracts.go b/pkg/extractor/v71/extracts.go index 097aa322..c68e73d4 100644 --- a/pkg/extractor/v71/extracts.go +++ b/pkg/extractor/v71/extracts.go @@ -45,6 +45,14 @@ type resourcesResp struct { Amount float64 `json:"amount"` Tooltip string `json:"tooltip"` } `json:"darkmatter"` + Population struct { + Amount float64 `json:"amount"` + Tooltip string `json:"tooltip"` + } `json:"population"` + Food struct { + Amount float64 `json:"amount"` + Tooltip string `json:"tooltip"` + } `json:"food"` } `json:"resources"` HonorScore int64 `json:"honorScore"` Techs struct { @@ -172,18 +180,38 @@ func extractResourcesDetails(pageHTML []byte) (out ogame.ResourcesDetails, err e out.Deuterium.StorageCapacity = int64(res.Resources.Deuterium.Storage) out.Energy.Available = int64(res.Resources.Energy.Amount) out.Darkmatter.Available = int64(res.Resources.Darkmatter.Amount) + out.Population.Available = int64(res.Resources.Population.Amount) + out.Food.Available = int64(res.Resources.Food.Amount) metalDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(res.Resources.Metal.Tooltip)) crystalDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(res.Resources.Crystal.Tooltip)) deuteriumDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(res.Resources.Deuterium.Tooltip)) darkmatterDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(res.Resources.Darkmatter.Tooltip)) energyDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(res.Resources.Energy.Tooltip)) + populationDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(res.Resources.Population.Tooltip)) + foodDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(res.Resources.Food.Tooltip)) + out.Metal.StorageCapacity = utils.ParseInt(metalDoc.Find("table tr").Eq(1).Find("td").Eq(0).Text()) out.Metal.CurrentProduction = utils.ParseInt(metalDoc.Find("table tr").Eq(2).Find("td").Eq(0).Text()) + out.Crystal.StorageCapacity = utils.ParseInt(crystalDoc.Find("table tr").Eq(1).Find("td").Eq(0).Text()) out.Crystal.CurrentProduction = utils.ParseInt(crystalDoc.Find("table tr").Eq(2).Find("td").Eq(0).Text()) + out.Deuterium.StorageCapacity = utils.ParseInt(deuteriumDoc.Find("table tr").Eq(1).Find("td").Eq(0).Text()) out.Deuterium.CurrentProduction = utils.ParseInt(deuteriumDoc.Find("table tr").Eq(2).Find("td").Eq(0).Text()) out.Energy.CurrentProduction = utils.ParseInt(energyDoc.Find("table tr").Eq(1).Find("td").Eq(0).Text()) out.Energy.Consumption = utils.ParseInt(energyDoc.Find("table tr").Eq(2).Find("td").Eq(0).Text()) out.Darkmatter.Purchased = utils.ParseInt(darkmatterDoc.Find("table tr").Eq(1).Find("td").Eq(0).Text()) out.Darkmatter.Found = utils.ParseInt(darkmatterDoc.Find("table tr").Eq(2).Find("td").Eq(0).Text()) + out.Food.Available = utils.ParseInt(foodDoc.Find("table tr").Eq(0).Find("td").Eq(0).Text()) + out.Food.StorageCapacity = utils.ParseInt(foodDoc.Find("table tr").Eq(1).Find("td").Eq(0).Text()) + out.Food.Overproduction = utils.ParseInt(foodDoc.Find("table tr").Eq(2).Find("td").Eq(0).Text()) + out.Food.ConsumedIn = utils.ParseInt(foodDoc.Find("table tr").Eq(3).Find("td").Eq(0).Text()) + out.Food.TimeTillFoodRunsOut = utils.ParseInt(foodDoc.Find("table tr").Eq(4).Find("td").Eq(0).Text()) + out.Population.Available = utils.ParseInt(populationDoc.Find("table tr").Eq(0).Find("td").Eq(0).Text()) + out.Population.T2Lifeforms = utils.ParseInt(populationDoc.Find("table tr").Eq(1).Find("td").Eq(0).Text()) + out.Population.T3Lifeforms = utils.ParseInt(populationDoc.Find("table tr").Eq(2).Find("td").Eq(0).Text()) + out.Population.LivingSpace = utils.ParseInt(populationDoc.Find("table tr").Eq(3).Find("td").Eq(0).Text()) + out.Population.Satisfied = utils.ParseInt(populationDoc.Find("table tr").Eq(4).Find("td").Eq(0).Text()) + out.Population.Hungry, _ = strconv.ParseFloat(populationDoc.Find("table tr").Eq(5).Find("td").Eq(0).Text(), 64) + out.Population.GrowthRate, _ = strconv.ParseFloat(strings.TrimPrefix(populationDoc.Find("table tr").Eq(6).Find("td").Eq(0).Text(), "±"), 64) + out.Population.BunkerSpace = utils.ParseInt(populationDoc.Find("table tr").Eq(7).Find("td").Eq(0).Text()) return } diff --git a/pkg/extractor/v9/extractor.go b/pkg/extractor/v9/extractor.go index 6ce7cea2..6b0de589 100644 --- a/pkg/extractor/v9/extractor.go +++ b/pkg/extractor/v9/extractor.go @@ -81,7 +81,7 @@ func (e *Extractor) ExtractResourcesDetailsFromFullPageFromDoc(doc *goquery.Docu } // ExtractConstructions ... -func (e *Extractor) ExtractConstructions(pageHTML []byte) (buildingID ogame.ID, buildingCountdown int64, researchID ogame.ID, researchCountdown int64) { +func (e *Extractor) ExtractConstructions(pageHTML []byte) (buildingID ogame.ID, buildingCountdown int64, researchID ogame.ID, researchCountdown int64, lfBuildingID ogame.ID, lfBuildingCountdown int64) { return ExtractConstructions(pageHTML, clockwork.NewRealClock()) } diff --git a/pkg/extractor/v9/extractor_test.go b/pkg/extractor/v9/extractor_test.go index 2b6bd797..9ad283b7 100644 --- a/pkg/extractor/v9/extractor_test.go +++ b/pkg/extractor/v9/extractor_test.go @@ -30,6 +30,14 @@ func TestExtractResourcesDetailsFromFullPage(t *testing.T) { assert.Equal(t, int64(8000), res.Darkmatter.Found) } +func TestExtractResourcesDetailsFromFullPagePopulation(t *testing.T) { + pageHTMLBytes, _ := ioutil.ReadFile("../../../samples/v9.0.4/en/lifeform/overview.html") + res := NewExtractor().ExtractResourcesDetailsFromFullPage(pageHTMLBytes) + assert.Equal(t, int64(1974118), res.Population.Available) + assert.Equal(t, 0.233, res.Population.Hungry) + assert.Equal(t, 61.983, res.Population.GrowthRate) +} + func TestExtractResources(t *testing.T) { pageHTMLBytes, _ := ioutil.ReadFile("../../../samples/v9.0.0/en/overview.html") res := NewExtractor().ExtractResources(pageHTMLBytes) @@ -102,7 +110,7 @@ func TestGetConstructions(t *testing.T) { // Without lifeform pageHTMLBytes, _ := ioutil.ReadFile("../../../samples/v9.0.2/en/overview_all_queues.html") clock := clockwork.NewFakeClockAt(time.Date(2022, 8, 20, 12, 43, 11, 0, time.UTC)) - buildingID, buildingCountdown, researchID, researchCountdown := ExtractConstructions(pageHTMLBytes, clock) + buildingID, buildingCountdown, researchID, researchCountdown, _, _ := ExtractConstructions(pageHTMLBytes, clock) assert.Equal(t, ogame.MetalMineID, buildingID) assert.Equal(t, int64(5413), buildingCountdown) assert.Equal(t, ogame.ComputerTechnologyID, researchID) @@ -111,7 +119,7 @@ func TestGetConstructions(t *testing.T) { // With lifeform pageHTMLBytes, _ = ioutil.ReadFile("../../../samples/v9.0.2/en/lifeform/overview_all_queues2.html") clock = clockwork.NewFakeClockAt(time.Date(2022, 8, 28, 17, 22, 26, 0, time.UTC)) - buildingID, buildingCountdown, researchID, researchCountdown = ExtractConstructions(pageHTMLBytes, clock) + buildingID, buildingCountdown, researchID, researchCountdown, _, _ = ExtractConstructions(pageHTMLBytes, clock) assert.Equal(t, ogame.MetalStorageID, buildingID) assert.Equal(t, int64(33483), buildingCountdown) assert.Equal(t, ogame.ComputerTechnologyID, researchID) diff --git a/pkg/extractor/v9/extracts.go b/pkg/extractor/v9/extracts.go index ed64ebb9..33754e9b 100644 --- a/pkg/extractor/v9/extracts.go +++ b/pkg/extractor/v9/extracts.go @@ -3,6 +3,7 @@ package v9 import ( "errors" "regexp" + "strconv" "strings" "time" @@ -15,7 +16,9 @@ import ( "github.com/alaingilbert/ogame/pkg/utils" ) -func ExtractConstructions(pageHTML []byte, clock clockwork.Clock) (buildingID ogame.ID, buildingCountdown int64, researchID ogame.ID, researchCountdown int64) { +func ExtractConstructions(pageHTML []byte, clock clockwork.Clock) (buildingID ogame.ID, buildingCountdown int64, + researchID ogame.ID, researchCountdown int64, + lfBuildingID ogame.ID, lfBuildingCountdown int64) { buildingCountdownMatch := regexp.MustCompile(`var restTimebuilding = (\d+) -`).FindSubmatch(pageHTML) if len(buildingCountdownMatch) > 0 { buildingCountdown = int64(utils.ToInt(buildingCountdownMatch[1])) - clock.Now().Unix() @@ -28,6 +31,12 @@ func ExtractConstructions(pageHTML []byte, clock clockwork.Clock) (buildingID og researchIDInt := utils.ToInt(regexp.MustCompile(`onclick="cancelresearch\((\d+),`).FindSubmatch(pageHTML)[1]) researchID = ogame.ID(researchIDInt) } + lfBuildingCountdownMatch := regexp.MustCompile(`var restTimelfbuilding = (\d+) -`).FindSubmatch(pageHTML) + if len(lfBuildingCountdownMatch) > 0 { + lfBuildingCountdown = int64(utils.ToInt(lfBuildingCountdownMatch[1])) - clock.Now().Unix() + lfBuildingIDInt := utils.ToInt(regexp.MustCompile(`onclick="cancellfbuilding\((\d+),`).FindSubmatch(pageHTML)[1]) + lfBuildingID = ogame.ID(lfBuildingIDInt) + } return } @@ -218,6 +227,8 @@ func extractResourcesDetailsFromFullPageFromDoc(doc *goquery.Document) ogame.Res deuteriumDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(doc.Find("div#deuterium_box").AttrOr("title", ""))) energyDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(doc.Find("div#energy_box").AttrOr("title", ""))) darkmatterDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(doc.Find("div#darkmatter_box").AttrOr("title", ""))) + populationDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(doc.Find("div#population_box").AttrOr("title", ""))) + foodDoc, _ := goquery.NewDocumentFromReader(strings.NewReader(doc.Find("div#food_box").AttrOr("title", ""))) out.Metal.Available = utils.ParseInt(metalDoc.Find("table tr").Eq(0).Find("td").Eq(0).Text()) out.Metal.StorageCapacity = utils.ParseInt(metalDoc.Find("table tr").Eq(1).Find("td").Eq(0).Text()) out.Metal.CurrentProduction = utils.ParseInt(metalDoc.Find("table tr").Eq(2).Find("td").Eq(0).Text()) @@ -233,6 +244,19 @@ func extractResourcesDetailsFromFullPageFromDoc(doc *goquery.Document) ogame.Res out.Darkmatter.Available = utils.ParseInt(darkmatterDoc.Find("table tr").Eq(0).Find("td").Eq(0).Text()) out.Darkmatter.Purchased = utils.ParseInt(darkmatterDoc.Find("table tr").Eq(1).Find("td").Eq(0).Text()) out.Darkmatter.Found = utils.ParseInt(darkmatterDoc.Find("table tr").Eq(2).Find("td").Eq(0).Text()) + out.Food.Available = utils.ParseInt(foodDoc.Find("table tr").Eq(0).Find("td").Eq(0).Text()) + out.Food.StorageCapacity = utils.ParseInt(foodDoc.Find("table tr").Eq(1).Find("td").Eq(0).Text()) + out.Food.Overproduction = utils.ParseInt(foodDoc.Find("table tr").Eq(2).Find("td").Eq(0).Text()) + out.Food.ConsumedIn = utils.ParseInt(foodDoc.Find("table tr").Eq(3).Find("td").Eq(0).Text()) + out.Food.TimeTillFoodRunsOut = utils.ParseInt(foodDoc.Find("table tr").Eq(4).Find("td").Eq(0).Text()) + out.Population.Available = utils.ParseInt(populationDoc.Find("table tr").Eq(0).Find("td").Eq(0).Text()) + out.Population.T2Lifeforms = utils.ParseInt(populationDoc.Find("table tr").Eq(1).Find("td").Eq(0).Text()) + out.Population.T3Lifeforms = utils.ParseInt(populationDoc.Find("table tr").Eq(2).Find("td").Eq(0).Text()) + out.Population.LivingSpace = utils.ParseInt(populationDoc.Find("table tr").Eq(3).Find("td").Eq(0).Text()) + out.Population.Satisfied = utils.ParseInt(populationDoc.Find("table tr").Eq(4).Find("td").Eq(0).Text()) + out.Population.Hungry, _ = strconv.ParseFloat(populationDoc.Find("table tr").Eq(5).Find("td").Eq(0).Text(), 64) + out.Population.GrowthRate, _ = strconv.ParseFloat(strings.TrimPrefix(populationDoc.Find("table tr").Eq(6).Find("td").Eq(0).Text(), "±"), 64) + out.Population.BunkerSpace = utils.ParseInt(populationDoc.Find("table tr").Eq(7).Find("td").Eq(0).Text()) return out } diff --git a/pkg/ogame/baseBuilding.go b/pkg/ogame/baseBuilding.go index 53f106b1..7bfde3ef 100644 --- a/pkg/ogame/baseBuilding.go +++ b/pkg/ogame/baseBuilding.go @@ -16,10 +16,11 @@ func (b BaseBuilding) DeconstructionPrice(level int64, techs IResearches) Resour return int64(math.Floor(float64(baseCost)*math.Pow(increaseFactor, float64(level-1))) * (1 - 0.04*float64(techs.GetIonTechnology()))) } return Resources{ - Metal: tmp(b.BaseCost.Metal, b.IncreaseFactor, level), - Crystal: tmp(b.BaseCost.Crystal, b.IncreaseFactor, level), - Deuterium: tmp(b.BaseCost.Deuterium, b.IncreaseFactor, level), - Energy: tmp(b.BaseCost.Energy, b.IncreaseFactor, level), + Metal: tmp(b.BaseCost.Metal, b.IncreaseFactor, level), + Crystal: tmp(b.BaseCost.Crystal, b.IncreaseFactor, level), + Deuterium: tmp(b.BaseCost.Deuterium, b.IncreaseFactor, level), + Energy: tmp(b.BaseCost.Energy, b.IncreaseFactor, level), + Population: tmp(b.BaseCost.Population, b.IncreaseFactor, level), } } diff --git a/pkg/ogame/id.go b/pkg/ogame/id.go index b0f441f4..b29bacd1 100644 --- a/pkg/ogame/id.go +++ b/pkg/ogame/id.go @@ -164,7 +164,7 @@ func (o ID) String() string { // IsValid returns either or not the id is valid func (o ID) IsValid() bool { - return o.IsDefense() || o.IsShip() || o.IsTech() || o.IsBuilding() + return o.IsDefense() || o.IsShip() || o.IsTech() || o.IsBuilding() || o.IsLfBuilding() } // IsFacility returns either or not the id is a facility diff --git a/pkg/ogame/lfBuildings.go b/pkg/ogame/lfBuildings.go index 3caae53f..d5871839 100644 --- a/pkg/ogame/lfBuildings.go +++ b/pkg/ogame/lfBuildings.go @@ -170,7 +170,8 @@ func (b LfBuildings) ByID(id ID) int64 { // BaseLfBuilding base struct for Lifeform buildings type BaseLfBuilding struct { BaseBuilding - energyIncreaseFactor float64 + energyIncreaseFactor float64 + populationIncreaseFactor float64 } // GetPrice returns the price to build the given level @@ -178,14 +179,19 @@ func (b BaseLfBuilding) GetPrice(level int64) Resources { tmp := func(baseCost int64, increaseFactor float64, level int64) int64 { return int64(float64(baseCost) * math.Pow(increaseFactor, float64(level-1)) * float64(level)) } + tmp2 := func(baseCost int64, increaseFactor float64, level int64) int64 { + return int64(float64(baseCost) * math.Pow(increaseFactor, float64(level-1))) + } return Resources{ - Metal: tmp(b.BaseCost.Metal, b.IncreaseFactor, level), - Crystal: tmp(b.BaseCost.Crystal, b.IncreaseFactor, level), - Deuterium: tmp(b.BaseCost.Deuterium, b.IncreaseFactor, level), - Energy: tmp(b.BaseCost.Energy, b.energyIncreaseFactor, level), + Metal: tmp(b.BaseCost.Metal, b.IncreaseFactor, level), + Crystal: tmp(b.BaseCost.Crystal, b.IncreaseFactor, level), + Deuterium: tmp(b.BaseCost.Deuterium, b.IncreaseFactor, level), + Energy: tmp(b.BaseCost.Energy, b.energyIncreaseFactor, level), + Population: tmp2(b.BaseCost.Population, b.populationIncreaseFactor, level), } } +// Humans type residentialSector struct { BaseLfBuilding } @@ -194,7 +200,7 @@ func newResidentialSector() *residentialSector { b := new(residentialSector) b.Name = "residential sector" b.ID = ResidentialSectorID - b.IncreaseFactor = 1.2 + b.IncreaseFactor = 1.20 b.BaseCost = Resources{Metal: 7, Crystal: 2} b.Requirements = map[ID]int64{} return b @@ -237,8 +243,9 @@ func newAcademyOfSciences() *academyOfSciences { b := new(academyOfSciences) b.Name = "academy of sciences" b.ID = AcademyOfSciencesID - b.IncreaseFactor = 1 // TODO - b.BaseCost = Resources{Metal: 5000, Crystal: 3200, Deuterium: 1500, Lifeform: 20000000} + b.IncreaseFactor = 1.70 + b.populationIncreaseFactor = 1.10 + b.BaseCost = Resources{Metal: 5000, Crystal: 3200, Deuterium: 1500, Population: 20000000} b.Requirements = map[ID]int64{ResidentialSectorID: 40} return b } @@ -251,8 +258,9 @@ func newNeuroCalibrationCentre() *neuroCalibrationCentre { b := new(neuroCalibrationCentre) b.Name = "neuro calibration centre" b.ID = NeuroCalibrationCentreID - b.IncreaseFactor = 1 // TODO - b.BaseCost = Resources{Metal: 50000, Crystal: 40000, Deuterium: 50000, Lifeform: 100000000} + b.IncreaseFactor = 1.70 + b.populationIncreaseFactor = 1.10 + b.BaseCost = Resources{Metal: 50000, Crystal: 40000, Deuterium: 50000, Population: 100000000} b.Requirements = map[ID]int64{ResidentialSectorID: 40, AcademyOfSciencesID: 1, FusionPoweredProductionID: 1, SkyscraperID: 5} return b } @@ -265,7 +273,7 @@ func newHighEnergySmelting() *highEnergySmelting { b := new(highEnergySmelting) b.Name = "high energy smelting" b.ID = HighEnergySmeltingID - b.IncreaseFactor = 1 // TODO + b.IncreaseFactor = 1.50 b.BaseCost = Resources{Metal: 7500, Crystal: 5000, Deuterium: 3000} b.Requirements = map[ID]int64{ResidentialSectorID: 12, BiosphereFarmID: 13, ResearchCentreID: 5} return b @@ -279,7 +287,7 @@ func newFoodSilo() *foodSilo { b := new(foodSilo) b.Name = "food silo" b.ID = FoodSiloID - b.IncreaseFactor = 1 // TODO + b.IncreaseFactor = 1.09 b.BaseCost = Resources{Metal: 25000, Crystal: 13000, Deuterium: 7000} b.Requirements = map[ID]int64{ResidentialSectorID: 12, BiosphereFarmID: 13, ResearchCentreID: 5, HighEnergySmeltingID: 3} return b @@ -293,7 +301,7 @@ func newFusionPoweredProduction() *fusionPoweredProduction { b := new(fusionPoweredProduction) b.Name = "fusion powered production" b.ID = FusionPoweredProductionID - b.IncreaseFactor = 1 // TODO + b.IncreaseFactor = 1.50 b.BaseCost = Resources{Metal: 50000, Crystal: 25000, Deuterium: 25000} b.Requirements = map[ID]int64{ResidentialSectorID: 40, AcademyOfSciencesID: 1} return b @@ -307,7 +315,7 @@ func newSkyscraper() *skyscraper { b := new(skyscraper) b.Name = "skyscraper" b.ID = SkyscraperID - b.IncreaseFactor = 1 // TODO + b.IncreaseFactor = 1.09 b.BaseCost = Resources{Metal: 75000, Crystal: 20000, Deuterium: 25000} b.Requirements = map[ID]int64{ResidentialSectorID: 40, AcademyOfSciencesID: 1, FusionPoweredProductionID: 1} return b @@ -321,7 +329,7 @@ func newBiotechLab() *biotechLab { b := new(biotechLab) b.Name = "biotech lab" b.ID = BiotechLabID - b.IncreaseFactor = 1 // TODO + b.IncreaseFactor = 1.12 b.BaseCost = Resources{Metal: 150000, Crystal: 30000, Deuterium: 15000} b.Requirements = map[ID]int64{ResidentialSectorID: 40, AcademyOfSciencesID: 1, FusionPoweredProductionID: 2} return b @@ -335,7 +343,7 @@ func newMetropolis() *metropolis { b := new(metropolis) b.Name = "metropolis" b.ID = MetropolisID - b.IncreaseFactor = 1 // TODO + b.IncreaseFactor = 1.12 b.BaseCost = Resources{Metal: 80000, Crystal: 35000, Deuterium: 60000} b.Requirements = map[ID]int64{ResidentialSectorID: 40, AcademyOfSciencesID: 1, FusionPoweredProductionID: 1, SkyscraperID: 5, NeuroCalibrationCentreID: 1} return b @@ -349,7 +357,7 @@ func newPlanetaryShield() *planetaryShield { b := new(planetaryShield) b.Name = "planetary shield" b.ID = PlanetaryShieldID - b.IncreaseFactor = 1 // TODO + b.IncreaseFactor = 1.20 b.BaseCost = Resources{Metal: 250000, Crystal: 125000, Deuterium: 125000} b.Requirements = map[ID]int64{ ResidentialSectorID: 40, @@ -369,3 +377,519 @@ func newPlanetaryShield() *planetaryShield { type BaseLfTechnology struct { BaseLevelable } + +// Rocktal +type meditationEnclave struct { + BaseLfBuilding +} + +func newMeditationEnclave() *meditationEnclave { + b := new(meditationEnclave) + b.Name = "meditation enclave" + b.ID = MeditationEnclaveID + b.IncreaseFactor = 1.20 + b.BaseCost = Resources{Metal: 9, Crystal: 3} + b.Requirements = map[ID]int64{} + return b +} + +type crystalFarm struct { + BaseLfBuilding +} + +func newCrystalFarm() *crystalFarm { + b := new(crystalFarm) + b.Name = "crystal farm" + b.ID = CrystalFarmID + b.IncreaseFactor = 1.20 + b.energyIncreaseFactor = 1.03 + b.BaseCost = Resources{Metal: 7, Crystal: 2, Energy: 10} + b.Requirements = map[ID]int64{} + return b +} + +type runeTechnologium struct { + BaseLfBuilding +} + +func newRuneTechnologium() *runeTechnologium { + b := new(runeTechnologium) + b.Name = "rune technologium" + b.ID = RuneTechnologiumID + b.IncreaseFactor = 1.30 + b.BaseCost = Resources{Metal: 40000, Crystal: 10000, Deuterium: 15000} + b.Requirements = map[ID]int64{MeditationEnclaveID: 21, CrystalFarmID: 22} + return b +} + +type runeForge struct { + BaseLfBuilding +} + +func newRuneForge() *runeForge { + b := new(runeForge) + b.Name = "rune forge" + b.ID = RuneForgeID + b.IncreaseFactor = 1.70 + b.populationIncreaseFactor = 1.14 + b.BaseCost = Resources{Metal: 5000, Crystal: 3800, Deuterium: 1000, Population: 16000000} + b.Requirements = map[ID]int64{MeditationEnclaveID: 41} + return b +} + +type oriktorium struct { + BaseLfBuilding +} + +func newOriktorium() *oriktorium { + b := new(oriktorium) + b.Name = "oriktorium" + b.ID = OriktoriumID + b.IncreaseFactor = 1.70 + b.populationIncreaseFactor = 1.65 + b.BaseCost = Resources{Metal: 50000, Crystal: 40000, Deuterium: 50000, Population: 90000000} + b.Requirements = map[ID]int64{MeditationEnclaveID: 41, RuneForgeID: 1, MegalithID: 1, CrystalRefineryID: 5} + return b +} + +type magmaForge struct { + BaseLfBuilding +} + +func newMagmaForge() *magmaForge { + b := new(magmaForge) + b.Name = "magma forge" + b.ID = MagmaForgeID + b.IncreaseFactor = 1.40 + b.BaseCost = Resources{Metal: 10000, Crystal: 8000, Deuterium: 1000} + b.Requirements = map[ID]int64{MeditationEnclaveID: 21, CrystalFarmID: 22, RuneTechnologiumID: 5} + return b +} + +type disruptionChamber struct { + BaseLfBuilding +} + +func newDisruptionChamber() *disruptionChamber { + b := new(disruptionChamber) + b.Name = "disruption chamber" + b.ID = DisruptionChamberID + b.IncreaseFactor = 1.20 + b.BaseCost = Resources{Metal: 20000, Crystal: 15000, Deuterium: 10000} + b.Requirements = map[ID]int64{MeditationEnclaveID: 21, CrystalFarmID: 22, RuneTechnologiumID: 5, MagmaForgeID: 3} + return b +} + +type megalith struct { + BaseLfBuilding +} + +func newMegalith() *megalith { + b := new(megalith) + b.Name = "megalith" + b.ID = MegalithID + b.IncreaseFactor = 1.50 + b.BaseCost = Resources{Metal: 50000, Crystal: 35000, Deuterium: 15000} + b.Requirements = map[ID]int64{MeditationEnclaveID: 41, RuneForgeID: 1} + return b +} + +type crystalRefinery struct { + BaseLfBuilding +} + +func newCrystalRefinery() *crystalRefinery { + b := new(crystalRefinery) + b.Name = "crystal refinery" + b.ID = CrystalRefineryID + b.IncreaseFactor = 1.40 + b.BaseCost = Resources{Metal: 85000, Crystal: 44000, Deuterium: 25000} + b.Requirements = map[ID]int64{MeditationEnclaveID: 41, RuneForgeID: 1, MegalithID: 1} + return b +} + +type deuteriumSynthesiser struct { + BaseLfBuilding +} + +func newDeuteriumSynthesiser() *deuteriumSynthesiser { + b := new(deuteriumSynthesiser) + b.Name = "deuterium synthesiser" + b.ID = DeuteriumSynthesiserID + b.IncreaseFactor = 1.40 + b.BaseCost = Resources{Metal: 120000, Crystal: 50000, Deuterium: 20000} + b.Requirements = map[ID]int64{MeditationEnclaveID: 41, RuneForgeID: 1, MegalithID: 2} + return b +} + +type mineralResearchCentre struct { + BaseLfBuilding +} + +func newMineralResearchCentre() *mineralResearchCentre { + b := new(mineralResearchCentre) + b.Name = "mineral research centre" + b.ID = MineralResearchCentreID + b.IncreaseFactor = 1.80 + b.BaseCost = Resources{Metal: 250000, Crystal: 150000, Deuterium: 100000} + b.Requirements = map[ID]int64{MeditationEnclaveID: 41, RuneForgeID: 1, MegalithID: 1, CrystalRefineryID: 6, OriktoriumID: 1} + return b +} + +type metalRecyclingPlant struct { + BaseLfBuilding +} + +func newMetalRecyclingPlant() *metalRecyclingPlant { + b := new(metalRecyclingPlant) + b.Name = "metal recycling plant" + b.ID = MetalRecyclingPlantID + b.IncreaseFactor = 1.50 + b.BaseCost = Resources{Metal: 250000, Crystal: 125000, Deuterium: 125000} + b.Requirements = map[ID]int64{MeditationEnclaveID: 41, CrystalFarmID: 22, RuneForgeID: 1, MegalithID: 5, CrystalRefineryID: 6, OriktoriumID: 5, RuneTechnologiumID: 5, MagmaForgeID: 3, DisruptionChamberID: 4, MineralResearchCentreID: 5} + return b +} + +// Mechas +type assemblyLine struct { + BaseLfBuilding +} + +func newAssemblyLine() *assemblyLine { + b := new(assemblyLine) + b.Name = "assembly line" + b.ID = AssemblyLineID + b.IncreaseFactor = 1.21 + b.BaseCost = Resources{Metal: 6, Crystal: 2} + b.Requirements = map[ID]int64{} + return b +} + +type fusionCellFactory struct { + BaseLfBuilding +} + +func newFusionCellFactory() *fusionCellFactory { + b := new(fusionCellFactory) + b.Name = "fusion cell factory" + b.ID = FusionCellFactoryID + b.IncreaseFactor = 1.18 + b.energyIncreaseFactor = 1.02 + b.BaseCost = Resources{Metal: 5, Crystal: 2, Energy: 8} + b.Requirements = map[ID]int64{} + return b +} + +type roboticsResearchCentre struct { + BaseLfBuilding +} + +func newRoboticsResearchCentre() *roboticsResearchCentre { + b := new(roboticsResearchCentre) + b.Name = "robotics research centre" + b.ID = RoboticsResearchCentreID + b.IncreaseFactor = 1.30 + b.BaseCost = Resources{Metal: 30000, Crystal: 20000, Deuterium: 10000} + b.Requirements = map[ID]int64{AssemblyLineID: 20, FusionCellFactoryID: 17} + return b +} + +type updateNetwork struct { + BaseLfBuilding +} + +func newUpdateNetwork() *updateNetwork { + b := new(updateNetwork) + b.Name = "update network" + b.ID = UpdateNetworkID + b.IncreaseFactor = 1.80 + b.populationIncreaseFactor = 1.10 + b.BaseCost = Resources{Metal: 5000, Crystal: 3800, Deuterium: 1000, Population: 40000000} + b.Requirements = map[ID]int64{AssemblyLineID: 41} + return b +} + +type quantumComputerCentre struct { + BaseLfBuilding +} + +func newQuantumComputerCentre() *quantumComputerCentre { + b := new(quantumComputerCentre) + b.Name = "quantum computer centre" + b.ID = QuantumComputerCentreID + b.IncreaseFactor = 1.80 + b.populationIncreaseFactor = 1.10 + b.BaseCost = Resources{Metal: 50000, Crystal: 40000, Deuterium: 50000, Population: 130000000} + b.Requirements = map[ID]int64{AssemblyLineID: 41, UpdateNetworkID: 1, MicrochipAssemblyLineID: 1, ProductionAssemblyHallID: 5} + return b +} + +type automatisedAssemblyCentre struct { + BaseLfBuilding +} + +func newAutomatisedAssemblyCentre() *automatisedAssemblyCentre { + b := new(automatisedAssemblyCentre) + b.Name = "automatised assembly centre" + b.ID = AutomatisedAssemblyCentreID + b.IncreaseFactor = 1.30 + b.BaseCost = Resources{Metal: 7500, Crystal: 7000, Deuterium: 1000} + b.Requirements = map[ID]int64{AssemblyLineID: 17, FusionCellFactoryID: 20, RoboticsResearchCentreID: 5} + return b +} + +type highPerformanceTransformer struct { + BaseLfBuilding +} + +func newHighPerformanceTransformer() *highPerformanceTransformer { + b := new(highPerformanceTransformer) + b.Name = "high performance transformer" + b.ID = HighPerformanceTransformerID + b.IncreaseFactor = 1.50 + b.BaseCost = Resources{Metal: 35000, Crystal: 15000, Deuterium: 10000} + b.Requirements = map[ID]int64{AssemblyLineID: 17, FusionCellFactoryID: 20, RoboticsResearchCentreID: 5, AutomatisedAssemblyCentreID: 3} + return b +} + +type microchipAssemblyLine struct { + BaseLfBuilding +} + +func newMicrochipAssemblyLine() *microchipAssemblyLine { + b := new(microchipAssemblyLine) + b.Name = "microchip assembly line" + b.ID = MicrochipAssemblyLineID + b.IncreaseFactor = 1.07 + b.BaseCost = Resources{Metal: 50000, Crystal: 20000, Deuterium: 30000} + b.Requirements = map[ID]int64{AssemblyLineID: 41, UpdateNetworkID: 1} + return b +} + +type productionAssemblyHall struct { + BaseLfBuilding +} + +func newProductionAssemblyHall() *productionAssemblyHall { + b := new(productionAssemblyHall) + b.Name = "production assembly hall" + b.ID = ProductionAssemblyHallID + b.IncreaseFactor = 1.14 + b.BaseCost = Resources{Metal: 100000, Crystal: 10000, Deuterium: 3000} + b.Requirements = map[ID]int64{AssemblyLineID: 41, UpdateNetworkID: 1, MicrochipAssemblyLineID: 1} + return b +} + +type highPerformanceSynthesiser struct { + BaseLfBuilding +} + +func newHighPerformanceSynthesiser() *highPerformanceSynthesiser { + b := new(highPerformanceSynthesiser) + b.Name = "high performance synthesiser" + b.ID = HighPerformanceSynthesiserID + b.IncreaseFactor = 1.50 + b.BaseCost = Resources{Metal: 100000, Crystal: 40000, Deuterium: 20000} + b.Requirements = map[ID]int64{AssemblyLineID: 41, UpdateNetworkID: 1, MicrochipAssemblyLineID: 2} + return b +} + +type chipMassProduction struct { + BaseLfBuilding +} + +func newChipMassProduction() *chipMassProduction { + b := new(chipMassProduction) + b.Name = "chip mass production" + b.ID = ChipMassProductionID + b.IncreaseFactor = 1.50 + b.BaseCost = Resources{Metal: 55000, Crystal: 50000, Deuterium: 30000} + b.Requirements = map[ID]int64{AssemblyLineID: 41, UpdateNetworkID: 1, MicrochipAssemblyLineID: 1, ProductionAssemblyHallID: 6, QuantumComputerCentreID: 1} + return b +} + +type nanoRepairBots struct { + BaseLfBuilding +} + +func newNanoRepairBots() *nanoRepairBots { + b := new(nanoRepairBots) + b.Name = "nano repair bots" + b.ID = NanoRepairBotsID + b.IncreaseFactor = 1.40 + b.BaseCost = Resources{Metal: 250000, Crystal: 125000, Deuterium: 125000} + b.Requirements = map[ID]int64{AssemblyLineID: 41, FusionCellFactoryID: 20, MicrochipAssemblyLineID: 5, RoboticsResearchCentreID: 5, HighPerformanceTransformerID: 4, ProductionAssemblyHallID: 6, QuantumComputerCentreID: 5, ChipMassProductionID: 11} + return b +} + +// Kaelesh +type sanctuary struct { + BaseLfBuilding +} + +func newSanctuary() *sanctuary { + b := new(sanctuary) + b.Name = "sanctuary" + b.ID = SanctuaryID + b.IncreaseFactor = 1.21 + b.BaseCost = Resources{Metal: 4, Crystal: 3} + b.Requirements = map[ID]int64{} + return b +} + +type antimatterCondenser struct { + BaseLfBuilding +} + +func newAntimatterCondenser() *antimatterCondenser { + b := new(antimatterCondenser) + b.Name = "antimatter condenser" + b.ID = AntimatterCondenserID + b.IncreaseFactor = 1.21 + b.energyIncreaseFactor = 1.02 + b.BaseCost = Resources{Metal: 6, Crystal: 3, Energy: 9} + b.Requirements = map[ID]int64{} + return b +} + +type vortexChamber struct { + BaseLfBuilding +} + +func newVortexChamber() *vortexChamber { + b := new(vortexChamber) + b.Name = "vortex chamber" + b.ID = VortexChamberID + b.IncreaseFactor = 1.30 + b.BaseCost = Resources{Metal: 20000, Crystal: 20000, Deuterium: 30000} + b.Requirements = map[ID]int64{SanctuaryID: 20, AntimatterCondenserID: 21} + return b +} + +type hallsOfRealisation struct { + BaseLfBuilding +} + +func newHallsOfRealisation() *hallsOfRealisation { + b := new(hallsOfRealisation) + b.Name = "halls of realisation" + b.ID = HallsOfRealisationID + b.IncreaseFactor = 1.80 + b.populationIncreaseFactor = 1.10 + b.BaseCost = Resources{Metal: 7500, Crystal: 5000, Deuterium: 800, Population: 30000000} + b.Requirements = map[ID]int64{SanctuaryID: 42} + return b +} + +type forumOfTranscendence struct { + BaseLfBuilding +} + +func newForumOfTranscendence() *forumOfTranscendence { + b := new(forumOfTranscendence) + b.Name = "forum of transcendence" + b.ID = ForumOfTranscendenceID + b.IncreaseFactor = 1.80 + b.populationIncreaseFactor = 1.10 + b.BaseCost = Resources{Metal: 60000, Crystal: 30000, Deuterium: 50000, Population: 100000000} + b.Requirements = map[ID]int64{SanctuaryID: 42, HallsOfRealisationID: 1, ChrysalisAcceleratorID: 1, BioModifierID: 5} + return b +} + +type antimatterConvector struct { + BaseLfBuilding +} + +func newAntimatterConvector() *antimatterConvector { + b := new(antimatterConvector) + b.Name = "antimatter convector" + b.ID = AntimatterConvectorID + b.IncreaseFactor = 1.25 + b.BaseCost = Resources{Metal: 8500, Crystal: 5000, Deuterium: 3000} + b.Requirements = map[ID]int64{SanctuaryID: 20, AntimatterCondenserID: 21, VortexChamberID: 5} + return b +} + +type cloningLaboratory struct { + BaseLfBuilding +} + +func newCloningLaboratory() *cloningLaboratory { + b := new(cloningLaboratory) + b.Name = "cloning laboratory" + b.ID = CloningLaboratoryID + b.IncreaseFactor = 1.20 + b.BaseCost = Resources{Metal: 15000, Crystal: 15000, Deuterium: 20000} + b.Requirements = map[ID]int64{SanctuaryID: 20, AntimatterCondenserID: 21, VortexChamberID: 5, AntimatterConvectorID: 3} + return b +} + +type chrysalisAccelerator struct { + BaseLfBuilding +} + +func newChrysalisAccelerator() *chrysalisAccelerator { + b := new(chrysalisAccelerator) + b.Name = "chrysalis accelerator" + b.ID = ChrysalisAcceleratorID + b.IncreaseFactor = 1.05 + b.BaseCost = Resources{Metal: 75000, Crystal: 25000, Deuterium: 30000} + b.Requirements = map[ID]int64{SanctuaryID: 42, HallsOfRealisationID: 1} + return b +} + +type bioModifier struct { + BaseLfBuilding +} + +func newBioModifier() *bioModifier { + b := new(bioModifier) + b.Name = "bio modifier" + b.ID = BioModifierID + b.IncreaseFactor = 1.20 + b.BaseCost = Resources{Metal: 87500, Crystal: 25000, Deuterium: 30000} + b.Requirements = map[ID]int64{SanctuaryID: 42, HallsOfRealisationID: 1, ChrysalisAcceleratorID: 1} + return b +} + +type psionicModulator struct { + BaseLfBuilding +} + +func newPsionicModulator() *psionicModulator { + b := new(psionicModulator) + b.Name = "psionic modulator" + b.ID = PsionicModulatorID + b.IncreaseFactor = 1.50 + b.BaseCost = Resources{Metal: 150000, Crystal: 30000, Deuterium: 30000} + b.Requirements = map[ID]int64{SanctuaryID: 42, HallsOfRealisationID: 1, ChrysalisAcceleratorID: 2} + return b +} + +type shipManufacturingHall struct { + BaseLfBuilding +} + +func newShipManufacturingHall() *shipManufacturingHall { + b := new(shipManufacturingHall) + b.Name = "ship manufacturing hall" + b.ID = ShipManufacturingHallID + b.IncreaseFactor = 1.20 + b.BaseCost = Resources{Metal: 75000, Crystal: 50000, Deuterium: 55000} + b.Requirements = map[ID]int64{SanctuaryID: 42, HallsOfRealisationID: 1, ChrysalisAcceleratorID: 1, BioModifierID: 6, ForumOfTranscendenceID: 1} + return b +} + +type supraRefractor struct { + BaseLfBuilding +} + +func newSupraRefractor() *supraRefractor { + b := new(supraRefractor) + b.Name = "suprarefractor" + b.ID = SupraRefractorID + b.IncreaseFactor = 1.40 + b.BaseCost = Resources{Metal: 500000, Crystal: 250000, Deuterium: 250000} + b.Requirements = map[ID]int64{SanctuaryID: 42, AntimatterCondenserID: 21, VortexChamberID: 5, AntimatterConvectorID: 3, CloningLaboratoryID: 4, HallsOfRealisationID: 1, ChrysalisAcceleratorID: 5, BioModifierID: 6, ForumOfTranscendenceID: 5, ShipManufacturingHallID: 5} + return b +} diff --git a/pkg/ogame/objs.go b/pkg/ogame/objs.go index 206a7329..38a2d21d 100644 --- a/pkg/ogame/objs.go +++ b/pkg/ogame/objs.go @@ -67,7 +67,7 @@ var ( PlasmaTechnology = register[*plasmaTechnology](newPlasmaTechnology) ShieldingTechnology = register[*shieldingTechnology](newShieldingTechnology) WeaponsTechnology = register[*weaponsTechnology](newWeaponsTechnology) - ResidentialSector = register[*residentialSector](newResidentialSector) // Lifeform + ResidentialSector = register[*residentialSector](newResidentialSector) // Humans BiosphereFarm = register[*biosphereFarm](newBiosphereFarm) ResearchCentre = register[*researchCentre](newResearchCentre) AcademyOfSciences = register[*academyOfSciences](newAcademyOfSciences) @@ -79,6 +79,42 @@ var ( BiotechLab = register[*biotechLab](newBiotechLab) Metropolis = register[*metropolis](newMetropolis) PlanetaryShield = register[*planetaryShield](newPlanetaryShield) + MeditationEnclave = register[*meditationEnclave](newMeditationEnclave) //Rocktal + CrystalFarm = register[*crystalFarm](newCrystalFarm) + RuneTechnologium = register[*runeTechnologium](newRuneTechnologium) + RuneForge = register[*runeForge](newRuneForge) + Oriktorium = register[*oriktorium](newOriktorium) + MagmaForge = register[*magmaForge](newMagmaForge) + DisruptionChamber = register[*disruptionChamber](newDisruptionChamber) + Megalith = register[*megalith](newMegalith) + CrystalRefinery = register[*crystalRefinery](newCrystalRefinery) + DeuteriumSynthesiser = register[*deuteriumSynthesiser](newDeuteriumSynthesiser) + MineralResearchCentre = register[*mineralResearchCentre](newMineralResearchCentre) + MetalRecyclingPlant = register[*metalRecyclingPlant](newMetalRecyclingPlant) + AssemblyLine = register[*assemblyLine](newAssemblyLine) //Mechas + FusionCellFactory = register[*fusionCellFactory](newFusionCellFactory) + RoboticsResearchCentre = register[*roboticsResearchCentre](newRoboticsResearchCentre) + UpdateNetwork = register[*updateNetwork](newUpdateNetwork) + QuantumComputerCentre = register[*quantumComputerCentre](newQuantumComputerCentre) + AutomatisedAssemblyCentre = register[*automatisedAssemblyCentre](newAutomatisedAssemblyCentre) + HighPerformanceTransformer = register[*highPerformanceTransformer](newHighPerformanceTransformer) + MicrochipAssemblyLine = register[*microchipAssemblyLine](newMicrochipAssemblyLine) + ProductionAssemblyHall = register[*productionAssemblyHall](newProductionAssemblyHall) + HighPerformanceSynthesiser = register[*highPerformanceSynthesiser](newHighPerformanceSynthesiser) + ChipMassProduction = register[*chipMassProduction](newChipMassProduction) + NanoRepairBots = register[*nanoRepairBots](newNanoRepairBots) + Sanctuary = register[*sanctuary](newSanctuary) //Kaelesh + AntimatterCondenser = register[*antimatterCondenser](newAntimatterCondenser) + VortexChamber = register[*vortexChamber](newVortexChamber) + HallsOfRealisation = register[*hallsOfRealisation](newHallsOfRealisation) + ForumOfTranscendence = register[*forumOfTranscendence](newForumOfTranscendence) + AntimatterConvector = register[*antimatterConvector](newAntimatterConvector) + CloningLaboratory = register[*cloningLaboratory](newCloningLaboratory) + ChrysalisAccelerator = register[*chrysalisAccelerator](newChrysalisAccelerator) + BioModifier = register[*bioModifier](newBioModifier) + PsionicModulator = register[*psionicModulator](newPsionicModulator) + ShipManufacturingHall = register[*shipManufacturingHall](newShipManufacturingHall) + SupraRefractor = register[*supraRefractor](newSupraRefractor) ) type ObjsStruct struct{ m map[ID]BaseOgameObj } diff --git a/pkg/ogame/resources.go b/pkg/ogame/resources.go index 35fc6098..f6b0779d 100644 --- a/pkg/ogame/resources.go +++ b/pkg/ogame/resources.go @@ -28,6 +28,23 @@ type ResourcesDetails struct { CurrentProduction int64 // DenCapacity int } + Food struct { + Available int64 + StorageCapacity int64 + Overproduction int64 + ConsumedIn int64 + TimeTillFoodRunsOut int64 + } + Population struct { + Available int64 + T2Lifeforms int64 + T3Lifeforms int64 + LivingSpace int64 + Satisfied int64 + Hungry float64 + GrowthRate float64 + BunkerSpace int64 + } Energy struct { Available int64 CurrentProduction int64 @@ -46,6 +63,8 @@ func (r ResourcesDetails) Available() Resources { Metal: r.Metal.Available, Crystal: r.Crystal.Available, Deuterium: r.Deuterium.Available, + Food: r.Food.Available, + Population: r.Population.Available, Energy: r.Energy.Available, Darkmatter: r.Darkmatter.Available, } @@ -58,7 +77,7 @@ type Resources struct { Deuterium int64 Energy int64 Darkmatter int64 - Lifeform int64 + Population int64 Food int64 } diff --git a/pkg/parser/overviewPage.go b/pkg/parser/overviewPage.go index 8fd5d372..23774849 100644 --- a/pkg/parser/overviewPage.go +++ b/pkg/parser/overviewPage.go @@ -12,7 +12,7 @@ func (p OverviewPage) ExtractDMCosts() (ogame.DMCosts, error) { return p.e.ExtractDMCosts(p.content) } -func (p OverviewPage) ExtractConstructions() (ogame.ID, int64, ogame.ID, int64) { +func (p OverviewPage) ExtractConstructions() (ogame.ID, int64, ogame.ID, int64, ogame.ID, int64) { return p.e.ExtractConstructions(p.content) } diff --git a/pkg/wrapper/handlers.go b/pkg/wrapper/handlers.go index a15b5ee0..4914d9b0 100644 --- a/pkg/wrapper/handlers.go +++ b/pkg/wrapper/handlers.go @@ -538,6 +538,20 @@ func SetResourceSettingsHandler(c echo.Context) error { return c.JSON(http.StatusOK, SuccessResp(nil)) } +// GetLfBuildingsHandler ... +func GetLfBuildingsHandler(c echo.Context) error { + bot := c.Get("bot").(*OGame) + planetID, err := utils.ParseI64(c.Param("planetID")) + if err != nil { + return c.JSON(http.StatusBadRequest, ErrorResp(400, "invalid planet id")) + } + res, err := bot.GetLfBuildings(ogame.CelestialID(planetID)) + if err != nil { + return c.JSON(http.StatusInternalServerError, ErrorResp(500, err.Error())) + } + return c.JSON(http.StatusOK, SuccessResp(res)) +} + // GetResourcesBuildingsHandler ... func GetResourcesBuildingsHandler(c echo.Context) error { bot := c.Get("bot").(*OGame) @@ -750,18 +764,22 @@ func ConstructionsBeingBuiltHandler(c echo.Context) error { if err != nil { return c.JSON(http.StatusBadRequest, ErrorResp(400, "invalid planet id")) } - buildingID, buildingCountdown, researchID, researchCountdown := bot.ConstructionsBeingBuilt(ogame.CelestialID(planetID)) + buildingID, buildingCountdown, researchID, researchCountdown, lfBuildingID, lfBuildingCountdown := bot.ConstructionsBeingBuilt(ogame.CelestialID(planetID)) return c.JSON(http.StatusOK, SuccessResp( struct { - BuildingID int64 - BuildingCountdown int64 - ResearchID int64 - ResearchCountdown int64 + BuildingID int64 + BuildingCountdown int64 + ResearchID int64 + ResearchCountdown int64 + LfBuildingID int64 + LfBuildingCountdown int64 }{ - BuildingID: int64(buildingID), - BuildingCountdown: buildingCountdown, - ResearchID: int64(researchID), - ResearchCountdown: researchCountdown, + BuildingID: int64(buildingID), + BuildingCountdown: buildingCountdown, + ResearchID: int64(researchID), + ResearchCountdown: researchCountdown, + LfBuildingID: int64(lfBuildingID), + LfBuildingCountdown: lfBuildingCountdown, }, )) } @@ -806,6 +824,20 @@ func GetResourcesHandler(c echo.Context) error { return c.JSON(http.StatusOK, SuccessResp(res)) } +// GetRequirementsHandler ... +func GetRequirementsHandler(c echo.Context) error { + ogameID, err := utils.ParseI64(c.Param("ogameID")) + if err != nil { + return c.JSON(http.StatusBadRequest, ErrorResp(400, "invalid ogameID")) + } + ogameObj := ogame.Objs.ByID(ogame.ID(ogameID)) + if ogameObj != nil { + requirements := ogameObj.GetRequirements() + return c.JSON(http.StatusOK, SuccessResp(requirements)) + } + return c.JSON(http.StatusBadRequest, ErrorResp(400, "invalid ogameID")) +} + // GetPriceHandler ... func GetPriceHandler(c echo.Context) error { ogameID, err := utils.ParseI64(c.Param("ogameID")) diff --git a/pkg/wrapper/interfaces.go b/pkg/wrapper/interfaces.go index e1e5dfaf..e82197b3 100644 --- a/pkg/wrapper/interfaces.go +++ b/pkg/wrapper/interfaces.go @@ -24,7 +24,7 @@ type Celestial interface { CancelBuilding() error CancelLfBuilding() error CancelResearch() error - ConstructionsBeingBuilt() (ogame.ID, int64, ogame.ID, int64) + ConstructionsBeingBuilt() (ogame.ID, int64, ogame.ID, int64, ogame.ID, int64) EnsureFleet([]ogame.Quantifiable, ogame.Speed, ogame.Coordinate, ogame.MissionID, ogame.Resources, int64, int64) (ogame.Fleet, error) GetDefense(...Option) (ogame.DefensesInfos, error) GetFacilities(...Option) (ogame.Facilities, error) @@ -116,7 +116,7 @@ type Prioritizable interface { CancelBuilding(ogame.CelestialID) error CancelLfBuilding(ogame.CelestialID) error CancelResearch(ogame.CelestialID) error - ConstructionsBeingBuilt(ogame.CelestialID) (buildingID ogame.ID, buildingCountdown int64, researchID ogame.ID, researchCountdown int64) + ConstructionsBeingBuilt(ogame.CelestialID) (buildingID ogame.ID, buildingCountdown int64, researchID ogame.ID, researchCountdown int64, lfBuildingID ogame.ID, lfBuildingCountdown int64) EnsureFleet(celestialID ogame.CelestialID, ships []ogame.Quantifiable, speed ogame.Speed, where ogame.Coordinate, mission ogame.MissionID, resources ogame.Resources, holdingTime, unionID int64) (ogame.Fleet, error) GetDefense(ogame.CelestialID, ...Option) (ogame.DefensesInfos, error) GetFacilities(ogame.CelestialID, ...Option) (ogame.Facilities, error) diff --git a/pkg/wrapper/moon.go b/pkg/wrapper/moon.go index d64bfe6d..d6e10a24 100644 --- a/pkg/wrapper/moon.go +++ b/pkg/wrapper/moon.go @@ -18,7 +18,7 @@ func (m Moon) GetProduction() ([]ogame.Quantifiable, int64, error) { } // ConstructionsBeingBuilt returns the building & research being built, and the time remaining (secs) -func (m Moon) ConstructionsBeingBuilt() (ogame.ID, int64, ogame.ID, int64) { +func (m Moon) ConstructionsBeingBuilt() (ogame.ID, int64, ogame.ID, int64, ogame.ID, int64) { return m.ogame.ConstructionsBeingBuilt(ogame.CelestialID(m.ID)) } diff --git a/pkg/wrapper/ogame.go b/pkg/wrapper/ogame.go index a9012812..204c61ae 100644 --- a/pkg/wrapper/ogame.go +++ b/pkg/wrapper/ogame.go @@ -3048,7 +3048,7 @@ func (b *OGame) build(celestialID ogame.CelestialID, id ogame.ID, nbr int64) err } func (b *OGame) buildCancelable(celestialID ogame.CelestialID, id ogame.ID) error { - if !id.IsBuilding() && !id.IsTech() { + if !id.IsBuilding() && !id.IsTech() && !id.IsLfBuilding() { return errors.New("invalid id " + id.String()) } return b.build(celestialID, id, 0) @@ -3089,10 +3089,10 @@ func (b *OGame) buildShips(celestialID ogame.CelestialID, shipID ogame.ID, nbr i return b.buildProduction(celestialID, shipID, nbr) } -func (b *OGame) constructionsBeingBuilt(celestialID ogame.CelestialID) (ogame.ID, int64, ogame.ID, int64) { +func (b *OGame) constructionsBeingBuilt(celestialID ogame.CelestialID) (ogame.ID, int64, ogame.ID, int64, ogame.ID, int64) { page, err := getPage[parser.OverviewPage](b, ChangePlanet(celestialID)) if err != nil { - return ogame.ID(0), 0, ogame.ID(0), 0 + return ogame.ID(0), 0, ogame.ID(0), 0, ogame.ID(0), 0 } return page.ExtractConstructions() } @@ -3149,6 +3149,8 @@ func (b *OGame) getResources(celestialID ogame.CelestialID) (ogame.Resources, er Deuterium: res.Deuterium.Available, Energy: res.Energy.Available, Darkmatter: res.Darkmatter.Available, + Population: res.Population.Available, + Food: res.Food.Available, }, nil } @@ -4509,7 +4511,7 @@ func (b *OGame) BuildShips(celestialID ogame.CelestialID, shipID ogame.ID, nbr i } // ConstructionsBeingBuilt returns the building & research being built, and the time remaining (secs) -func (b *OGame) ConstructionsBeingBuilt(celestialID ogame.CelestialID) (ogame.ID, int64, ogame.ID, int64) { +func (b *OGame) ConstructionsBeingBuilt(celestialID ogame.CelestialID) (ogame.ID, int64, ogame.ID, int64, ogame.ID, int64) { return b.WithPriority(taskRunner.Normal).ConstructionsBeingBuilt(celestialID) } @@ -4673,7 +4675,8 @@ func (b *OGame) RegisterHTMLInterceptor(fn func(method, url string, params, payl // Phalanx scan a coordinate from a moon to get fleets information // IMPORTANT: My account was instantly banned when I scanned an invalid coordinate. // IMPORTANT: This function DOES validate that the coordinate is a valid planet in range of phalanx -// and that you have enough deuterium. +// +// and that you have enough deuterium. func (b *OGame) Phalanx(moonID ogame.MoonID, coord ogame.Coordinate) ([]ogame.Fleet, error) { return b.WithPriority(taskRunner.Normal).Phalanx(moonID, coord) } diff --git a/pkg/wrapper/planet.go b/pkg/wrapper/planet.go index b2959684..e6a66437 100644 --- a/pkg/wrapper/planet.go +++ b/pkg/wrapper/planet.go @@ -109,7 +109,7 @@ func (p Planet) EnsureFleet(ships []ogame.Quantifiable, speed ogame.Speed, where } // ConstructionsBeingBuilt returns the building & research being built, and the time remaining (secs) -func (p Planet) ConstructionsBeingBuilt() (ogame.ID, int64, ogame.ID, int64) { +func (p Planet) ConstructionsBeingBuilt() (ogame.ID, int64, ogame.ID, int64, ogame.ID, int64) { return p.ogame.ConstructionsBeingBuilt(ogame.CelestialID(p.ID)) } diff --git a/pkg/wrapper/prioritize.go b/pkg/wrapper/prioritize.go index 3423cef6..5dc9926a 100644 --- a/pkg/wrapper/prioritize.go +++ b/pkg/wrapper/prioritize.go @@ -377,7 +377,7 @@ func (b *Prioritize) BuildShips(celestialID ogame.CelestialID, shipID ogame.ID, } // ConstructionsBeingBuilt returns the building & research being built, and the time remaining (secs) -func (b *Prioritize) ConstructionsBeingBuilt(celestialID ogame.CelestialID) (ogame.ID, int64, ogame.ID, int64) { +func (b *Prioritize) ConstructionsBeingBuilt(celestialID ogame.CelestialID) (ogame.ID, int64, ogame.ID, int64, ogame.ID, int64) { b.begin("ConstructionsBeingBuilt") defer b.done() return b.bot.constructionsBeingBuilt(celestialID) diff --git a/samples/v9.0.4/en/lifeform/overview.html b/samples/v9.0.4/en/lifeform/overview.html new file mode 100644 index 00000000..c940efb0 --- /dev/null +++ b/samples/v9.0.4/en/lifeform/overview.html @@ -0,0 +1,1913 @@ + + + + + Halley OGame + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + +
+ + +
+ +
+ + + + + + + + +
+
+ + + + + + +
+ +
+ + + + + +
+
+
+
+
+ +
+
+
+ +
+ +
+
+
+ + 310,443 + +
+
+
+
+
+ + 87,170 + +
+
+
+
+
+ + 137,717 + +
+
+
+
+
+ + -2,248 + +
+
+
+
+
+ + 1,974,118.233 + +
+
+
+
+
+ + 0 + +
+
+
+
+ + +
+
+ + 8,000 + +
+
+
+ +
+ +
+
+ + + 25 + + + + + 0 + + +
+ +
+ ajax spinner + load... +
+ +
+
+ +
+
+
+ +
+
+
+
+ ? +
+
+
+
+
+
+ +
+ + + + + + +
+
+ +
+
+
+
+
+
+
+ + +

Events

+
+ + + +
+
+
+
+ + + +
+
+ +
+
+
+ + +
+
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+ + + +
+ + +
+ + +
+
+ + +
+
+ +
+
+ +
+ +
+ + +
+
+
+
+
+
+
+

Buildings

+
+ + +
+ +
+
+
+

Lifeform Buildings

+
+
+ + + + + + + + + + + + + + + + + + + +
Residential Sector
+ + Improve to + Level 34 +
Duration:
+ load... +
+ +
+ + Halve time + + + Costs: + 9,000 DM + + Purchase Dark Matter +
+
+
+ +
+ +
+
+
+
+ +
+
+ +
+
+
+ +
+
+

Shipyard

+
+ + +
+ +
+
+
+ + + +
+
+ +
+
+
+ + + + + +
+
+ +
+ +
+ + + +
+
+ + + +