diff --git a/game/magic/city/city.go b/game/magic/city/city.go index b78260fd..77f84f94 100644 --- a/game/magic/city/city.go +++ b/game/magic/city/city.go @@ -827,6 +827,7 @@ func (city *City) ComputeUnrest(garrison []units.StackUnit) int { unrestPercent += city.InteracialUnrest() + // unrest from curses if city.HasEnchantment(data.CityEnchantmentFamine) { unrestPercent += 0.25 } @@ -878,6 +879,11 @@ func (city *City) ComputeUnrest(garrison []units.StackUnit) int { pacification += float64(oraclePacification(city.Race)) } + // pacification from enchantments + if city.HasEnchantment(data.CityEnchantmentGaiasBlessing) { + pacification += 2 + } + total := unrestPercent * float64(city.Citizens()) + unrestAbsolute - pacification - garrisonSupression / 2 return int(math.Max(0, total)) @@ -890,6 +896,10 @@ func (city *City) MaximumCitySize() int { // TODO: 1/2 if famine is active + if city.HasEnchantment(data.CityEnchantmentGaiasBlessing) { + foodAvailability += int(0.5 * float32(foodAvailability)) + } + bonus := 0 if city.Buildings.Contains(buildinglib.BuildingGranary) { @@ -930,6 +940,10 @@ func (city *City) PopulationGrowthRate() int { base += 30 } + if city.HasEnchantment(data.CityEnchantmentGaiasBlessing) { + base += int(2.5 * float32(city.MaximumCitySize()) / 10) * 10 + } + if city.SurplusFood() < 0 { base = 50 * city.SurplusFood() } @@ -987,13 +1001,17 @@ func (city *City) FoodProductionRate() int { } func (city *City) FarmerFoodProduction(farmers int) int { - rate := 2 + food := 2 * farmers - switch city.Race { - case data.RaceHalfling: rate = 3 + if city.Race == data.RaceHalfling { + food += farmers + } + + if city.HasEnchantment(data.CityEnchantmentGaiasBlessing) { + food += int(float32(food) * 0.2) } - return rate * farmers + return food } func (city *City) foodProductionRate(farmers int) int { @@ -1233,9 +1251,10 @@ func (city *City) ProductionTerrain() float32 { catchment := city.CatchmentProvider.GetCatchmentArea(city.X, city.Y) production := float32(0) mineralProduction := float32(0) + hasGaiasBlessing := city.HasEnchantment(data.CityEnchantmentGaiasBlessing) for _, tile := range catchment { - production += float32(tile.ProductionBonus()) / 100 + production += float32(tile.ProductionBonus(hasGaiasBlessing)) / 100 // FIXME: This should be only when producing units mineralProduction += float32(tile.GetBonus().UnitReductionBonus()) / 100 diff --git a/game/magic/data/enchantment.go b/game/magic/data/enchantment.go index b0ba1c9e..16648ccb 100644 --- a/game/magic/data/enchantment.go +++ b/game/magic/data/enchantment.go @@ -398,8 +398,7 @@ func (enchantment CityEnchantment) SoundIndex() int { case CityEnchantmentDarkRituals: return 60 // case CityEnchantmentEarthGate: return 0 // case CityEnchantmentFlyingFortress: return 0 - // case CityEnchantmentGaiasBlessing: return 0 - case CityEnchantmentNaturesEye: return 28 + case CityEnchantmentGaiasBlessing, CityEnchantmentNaturesEye: return 28 // case CityEnchantmentPestilence: return 0 // case CityEnchantmentLifeWard: return 0 // case CityEnchantmentSorceryWard: return 0 diff --git a/game/magic/game/cast.go b/game/magic/game/cast.go index 51e6780d..229efb49 100644 --- a/game/magic/game/cast.go +++ b/game/magic/game/cast.go @@ -100,6 +100,12 @@ func (game *Game) doCastSpell(player *playerlib.Player, spell spellbook.Spell) { game.doCastCityEnchantment(yield, tileX, tileY, player, data.CityEnchantmentInspirations) } + game.Events <- &GameEventSelectLocationForSpell{Spell: spell, Player: player, LocationType: LocationTypeFriendlyCity, SelectedFunc: selected} + case "Gaia's Blessing": + selected := func (yield coroutine.YieldFunc, tileX int, tileY int){ + game.doCastCityEnchantment(yield, tileX, tileY, player, data.CityEnchantmentGaiasBlessing) + } + game.Events <- &GameEventSelectLocationForSpell{Spell: spell, Player: player, LocationType: LocationTypeFriendlyCity, SelectedFunc: selected} case "Cursed Lands": selected := func (yield coroutine.YieldFunc, tileX int, tileY int){ diff --git a/game/magic/game/game.go b/game/magic/game/game.go index 012da2b3..60596740 100644 --- a/game/magic/game/game.go +++ b/game/magic/game/game.go @@ -4726,7 +4726,7 @@ func (game *Game) CityProductionBonus(x int, y int, plane data.Plane) int { production := 0 for _, tile := range catchment { - production += tile.ProductionBonus() + production += tile.ProductionBonus(false) } return production @@ -5976,6 +5976,46 @@ func (game *Game) DissipateEnchantments(player *playerlib.Player, power int) { // FIXME: dissipate unit enchantments } +// apply automatic terraforming and purification to all cities of a player with gaias blessing +func (game *Game) doGaiasBlessing(player *playerlib.Player) { + for _, city := range player.Cities { + if city.HasEnchantment(data.CityEnchantmentGaiasBlessing) { + + mapObject := game.GetMap(city.Plane) + + for dx := -2; dx <= 2; dx++ { + for dy := -2; dy <= 2; dy++ { + mx := player.WrapX(city.X + dx) + my := city.Y + dy + + if mx < 0 || mx >= mapObject.Width() || my < 0 || my >= mapObject.Height() { + continue + } + + tile := mapObject.GetTile(mx, my) + terrainType := tile.Tile.TerrainType() + + // 10% chance to convert volcanos to hills + if mapObject.HasVolcano(mx, my) && rand.IntN(100) < 10 { + mapObject.RemoveVolcano(mx, my) + mapObject.Map.SetTerrainAt(mx, my, terrain.Hill, mapObject.Data, mapObject.Plane) + } + + // 10% chance to convert desert to grassland + if terrainType == terrain.Desert && rand.IntN(100) < 10 { + mapObject.Map.SetTerrainAt(mx, mx, terrain.Grass, mapObject.Data, mapObject.Plane) + } + + // 20% chance to remove corruption + if mapObject.HasCorruption(mx, my) && rand.IntN(100) < 20 { + mapObject.RemoveCorruption(mx, my) + } + } + } + } + } +} + func (game *Game) StartPlayerTurn(player *playerlib.Player) { disbandedMessages := game.DisbandUnits(player) @@ -6185,6 +6225,8 @@ func (game *Game) StartPlayerTurn(player *playerlib.Player) { player.RemoveCity(city) } + game.doGaiasBlessing(player) + game.maybeHireHero(player) game.maybeHireMercenaries(player) game.maybeBuyFromMerchant(player) diff --git a/game/magic/game/surveyor.go b/game/magic/game/surveyor.go index 894f2404..25c8cc22 100644 --- a/game/magic/game/surveyor.go +++ b/game/magic/game/surveyor.go @@ -197,7 +197,7 @@ func (game *Game) doSurveyor(yield coroutine.YieldFunc) { y += float64(whiteFont.Height() * data.ScreenScale) } - productionBonus := tile.ProductionBonus() + productionBonus := tile.ProductionBonus(false) if productionBonus != 0 { whiteFont.PrintCenter(screen, float64(280 * data.ScreenScale), y, float64(data.ScreenScale), ebiten.ColorScale{}, fmt.Sprintf("+%v%% production", productionBonus)) y += float64(whiteFont.Height() * data.ScreenScale) diff --git a/game/magic/maplib/map.go b/game/magic/maplib/map.go index 47dcd767..05b6ea0b 100644 --- a/game/magic/maplib/map.go +++ b/game/magic/maplib/map.go @@ -617,27 +617,20 @@ func (tile *FullTile) GoldBonus(mapObject *Map) int { } // percent bonus increase, 3 = 3% -func (tile *FullTile) ProductionBonus() int { +func (tile *FullTile) ProductionBonus(hasGaiasBlessing bool) int { if tile.Corrupted() { return 0 } - switch tile.Tile.TerrainType() { - case terrain.Ocean: return 0 - case terrain.Grass: return 0 - case terrain.Forest: return 3 - case terrain.Mountain: return 5 - case terrain.Desert: return 3 - case terrain.Swamp: return 0 - case terrain.Tundra: return 0 - case terrain.SorceryNode: return 0 - case terrain.NatureNode: return 3 - case terrain.ChaosNode: return 5 - case terrain.Hill: return 3 - case terrain.Volcano: return 0 - case terrain.Lake: return 0 - case terrain.River: return 0 - case terrain.Shore: return 0 + terrainType := tile.Tile.TerrainType() + switch { + case terrainType == terrain.Forest && hasGaiasBlessing: return 6 + case terrainType == terrain.Forest: return 3 + case terrainType == terrain.Mountain: return 5 + case terrainType == terrain.Desert: return 3 + case terrainType == terrain.NatureNode: return 3 + case terrainType == terrain.ChaosNode: return 5 + case terrainType == terrain.Hill: return 3 } return 0 @@ -1062,6 +1055,11 @@ func (mapObject *Map) SetVolcano(x int, y int, caster Wizard) { mapObject.Map.SetTerrainAt(x, y, terrain.Volcano, mapObject.Data, mapObject.Plane) } +func (mapObject *Map) HasVolcano(x int, y int) bool { + _, exists := mapObject.ExtraMap[image.Pt(x, y)][ExtraKindVolcano] + return exists +} + func (mapObject *Map) RemoveVolcano(x int, y int) { _, exists := mapObject.ExtraMap[image.Pt(x, y)][ExtraKindVolcano] if exists { diff --git a/test/overworld/main.go b/test/overworld/main.go index f663d37b..52419ce6 100644 --- a/test/overworld/main.go +++ b/test/overworld/main.go @@ -993,6 +993,7 @@ func createScenario13(cache *lbx.LbxCache) *gamelib.Game { player.KnownSpells.AddSpell(allSpells.FindByName("Nature's Eye")) player.KnownSpells.AddSpell(allSpells.FindByName("Prosperity")) player.KnownSpells.AddSpell(allSpells.FindByName("Inspirations")) + player.KnownSpells.AddSpell(allSpells.FindByName("Gaia's Blessing")) player.KnownSpells.AddSpell(allSpells.FindByName("Cursed Lands")) player.KnownSpells.AddSpell(allSpells.FindByName("Famine")) player.KnownSpells.AddSpell(allSpells.FindByName("Nature Awareness"))