Skip to content

Commit c3dae54

Browse files
authored
Merge pull request #759 from C7-Game/twrner/forest-shields
Award shields after chopping a forest
2 parents a8a27b8 + 7e3276a commit c3dae54

File tree

10 files changed

+85
-9
lines changed

10 files changed

+85
-9
lines changed

C7/Game.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,12 @@ public void processEngineMessagesLocked(GameData gameData) {
308308
// interesting happens.
309309
turnsLeftToFastForward = 0;
310310
break;
311+
case MsgShowTemporaryPopup mSTP:
312+
TemporaryPopup popup = new(mSTP.message, 1);
313+
popup.SetPosition(mapView.screenLocationOfTile(mSTP.location, true) + new Vector2(0, -64));
314+
AddChild(popup);
315+
popup.ShowPopup();
316+
break;
311317
}
312318
}
313319
}

C7/Text/c7-static-map-save.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75877,6 +75877,7 @@
7587775877
"maximumResearchTime": 50,
7587875878
"minimumResearchTime": 4,
7587975879
"shieldValueInGold": 4,
75880+
"forestValueInShields": 10,
7588075881
"citizenValueInShields": 20,
7588175882
"turnPenaltyForEachHurrySacrifice": 20,
7588275883
"maximumLevel1CitySize": 6,

C7Engine/C7GameData/ImportCiv3.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,7 @@ private void ImportRules() {
13091309
save.Rules.MaximumLevel1CitySize = rule.MaximumLevel1CitySize;
13101310
save.Rules.MaximumLevel2CitySize = rule.MaximumLevel2CitySize;
13111311
save.Rules.ShieldValueInGold = rule.ShieldValueInGold;
1312+
save.Rules.ForestValueInShields = rule.ForestValueInShields;
13121313
save.Rules.CitizenValueInShields = rule.CitizenValueInShields;
13131314
save.Rules.TurnPenaltyForEachHurrySacrifice = rule.TurnPenaltyForEachHurrySacrifice;
13141315
save.GameDifficulty = save.Difficulties[rule.DefaultDifficultyLevel];

C7Engine/C7GameData/MapUnit.cs

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public int maxHitPoints {
4646

4747
public TileDirection facingDirection;
4848

49-
public int WorkerProgressTowardsJob { get; set; }
49+
public float WorkerProgressTowardsJob { get; set; }
5050
public Terraform WorkerJob { get; set; }
5151

5252

@@ -144,8 +144,14 @@ public bool DeservesPlayerAttention() {
144144
private const int JOB_PROGRESS_WORKER = 2;
145145
private const int JOB_PROGRESS_SLAVE = 1;
146146

147-
private static int GetWorkerJobCost(Terraform workerJob) {
148-
return workerJob.TurnsToComplete;
147+
private static int GetWorkerJobCost(Tile tile, Terraform workerJob) {
148+
// For the movement cost multiplier, see note 7
149+
// (https://apolyton.net/forum/civilization-series/civilization-iii/59815-civilization-iii-bic-file-format-2nd-thread?p=1362768#post1362768)
150+
// For example, clearing a forest has a cost of 4, but with a nomral
151+
// worker that would take 2 turns. In order for the job to take the
152+
// expected 4 turns we need to multiply by the movement cost of the
153+
// terrain. This also makes roading hills/mountains more expensive.
154+
return workerJob.TurnsToComplete * tile.overlayTerrainType.movementCost;
149155
}
150156

151157
public void animate(MapUnit.AnimatedAction action, bool wait, AnimationEnding ending = AnimationEnding.Stop) {
@@ -571,8 +577,8 @@ public bool move(TileDirection dir, bool wait = false) {
571577
return true;
572578
}
573579

574-
private int SumWorkerProgress(Tile tile, Terraform workerJob) {
575-
int result = 0;
580+
private float SumWorkerProgress(Tile tile, Terraform workerJob) {
581+
float result = 0;
576582
foreach (MapUnit unit in tile.unitsOnTile) {
577583
if (WorkerJob == workerJob) {
578584
result += WorkerProgressTowardsJob;
@@ -598,7 +604,7 @@ public void PerformBusyAction() {
598604
updateWorkerJob();
599605

600606
// See if this worker finished the job.
601-
if (SumWorkerProgress(location, WorkerJob) >= GetWorkerJobCost(WorkerJob)) {
607+
if ((int)SumWorkerProgress(location, WorkerJob) >= GetWorkerJobCost(location, WorkerJob)) {
602608
location.FinishWorkerJob(WorkerJob);
603609
}
604610
}
@@ -677,7 +683,12 @@ public void PerformTerraformAction(Terraform terraform) {
677683
}
678684

679685
public void updateWorkerJob() {
680-
WorkerProgressTowardsJob += JOB_PROGRESS_WORKER;
686+
float progressPerTurn = JOB_PROGRESS_WORKER;
687+
if (owner.civilization.traits.Contains(Civilization.Trait.Industrious)) {
688+
progressPerTurn *= 1.5f;
689+
}
690+
691+
WorkerProgressTowardsJob += progressPerTurn;
681692
movementPoints.onConsumeAll();
682693
}
683694

C7Engine/C7GameData/Rules.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ public class Rules {
33
public int MaximumResearchTime;
44
public int MinimumResearchTime;
55
public int ShieldValueInGold;
6+
public int ForestValueInShields;
67
public int CitizenValueInShields;
78
public int TurnPenaltyForEachHurrySacrifice;
89
public int MaximumLevel1CitySize;

C7Engine/C7GameData/Save/SaveTile.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ public SaveTile(Tile tile) {
3838
if (tile.hasBarbarianCamp) {
3939
features.Add("barbarianCamp");
4040
}
41+
if (tile.hasHadForestCleared) {
42+
features.Add("hasHadForestCleared");
43+
}
4144

4245
overlays.AddRange(tile.overlays.GetImprovements().Select(i => i.key));
4346
}
@@ -53,6 +56,7 @@ public Tile ToTile(List<TerrainType> terrainTypes, List<Resource> resources) {
5356
baseTerrainType = terrainTypes.Find(tt => tt.Key == baseTerrain),
5457
overlayTerrainType = terrainTypes.Find(tt => tt.Key == overlayTerrain),
5558
hasBarbarianCamp = features.Contains("barbarianCamp"),
59+
hasHadForestCleared = features.Contains("hasHadForestCleared"),
5660
// TODO: load working tile
5761
ResourceKey = resource is null ? Resource.NONE.Key : resource,
5862
riverNorth = features.Contains("riverNorth"),

C7Engine/C7GameData/Save/SaveUnit.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class SaveUnit : IHasID {
1515
public string action; // "fortified"
1616
public TileDirection facingDirection;
1717
public string experience;
18-
public int WorkerProgressTowardsJob;
18+
public float WorkerProgressTowardsJob;
1919
public ID WorkerJob;
2020

2121
// True for multiple types of automation, including worker automation

C7Engine/C7GameData/Terraform.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ public static class TerraformRules {
1414
{UnitAction.BuildRailroad, (_, tile) => tile.overlays.Add(TerrainImprovement.railroad)},
1515
{UnitAction.ClearWetlands, (_, tile) => tile.ClearTerrainOverlay()},
1616
// TODO: add bonus shields to the nearest city - should only happen the first time a forest is cleared
17-
{UnitAction.ClearForest, (_, tile) => tile.ClearTerrainOverlay()},
17+
{UnitAction.ClearForest, (_, tile) => {
18+
tile.MaybeAwardForestClearingShields();
19+
tile.ClearTerrainOverlay();
20+
}},
1821
};
1922

2023
public static readonly Dictionary<UnitAction, Func<Player, Tile, bool>> ActionValidators = new() {

C7Engine/C7GameData/Tile.cs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ public City cityAtTile {
115115
public bool riverWest;
116116
public bool riverNorthwest;
117117

118+
// The first time a forest is cleared on a tile it can award shields to
119+
// a nearby city.
120+
public bool hasHadForestCleared = false;
121+
118122
public TileOverlays overlays;
119123

120124
public Tile(ID id) {
@@ -476,6 +480,39 @@ public string YieldString(Player player) {
476480
return null;
477481
}
478482

483+
public void MaybeAwardForestClearingShields() {
484+
if (hasHadForestCleared) {
485+
return;
486+
}
487+
hasHadForestCleared = true;
488+
489+
// Shields can only be awarded if the forest is within some city's
490+
// borders.
491+
if (OwningPlayer() == null) {
492+
return;
493+
}
494+
495+
// Check all the tiles within rank 2 of the forest.
496+
foreach (Tile other in GetTilesWithinRankDistance(2)) {
497+
if (other.cityAtTile == null) {
498+
continue;
499+
}
500+
501+
// Shields aren't awarded to wonders.
502+
if (other.cityAtTile.itemBeingProduced is Building b
503+
&& (b.greatWonderProperties != null || b.isSmallWonder)) {
504+
continue;
505+
}
506+
507+
City c = other.cityAtTile;
508+
int shieldsAwarded = EngineStorage.gameData.rules.ForestValueInShields;
509+
c.shieldsStored += shieldsAwarded;
510+
c.shieldsStored = Math.Min(c.shieldsStored, c.owner.ShieldCost(c.itemBeingProduced));
511+
new MsgShowTemporaryPopup($"{shieldsAwarded} shields awarded for clearing forests", other).send();
512+
return;
513+
}
514+
}
515+
479516
// Returns the X and Y coordinates of the neighbor in the specified direction.
480517
public static TileLocation NeighborCoordinate(TileLocation location, TileDirection direction) {
481518
switch (direction) {
@@ -581,6 +618,8 @@ public Tile GetTileAtNeighborIndex(int neighborIndex) {
581618
return map.tileAt(XCoordinate + xDelta, YCoordinate + yDelta);
582619
}
583620

621+
// Returns the tiles in the spiral ordering defined by
622+
// GetTileAtNeighborIndex(i).
584623
public List<Tile> GetTilesWithinRankDistance(int rank) {
585624
List<Tile> result = new();
586625
for (int i = 0; i < (rank * 2 + 1) * (rank * 2 + 1); ++i) {

C7Engine/EntryPoints/MessageToUI.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,4 +105,14 @@ public MsgShowMilitaryAdvisorPopup(string message, bool happy) {
105105
this.happy = happy;
106106
}
107107
}
108+
109+
public class MsgShowTemporaryPopup : MessageToUI {
110+
public string message;
111+
public Tile location;
112+
113+
public MsgShowTemporaryPopup(string message, Tile location) {
114+
this.message = message;
115+
this.location = location;
116+
}
117+
}
108118
}

0 commit comments

Comments
 (0)