diff --git a/C7/Game.cs b/C7/Game.cs index 6879f8df..ec8e1131 100644 --- a/C7/Game.cs +++ b/C7/Game.cs @@ -183,6 +183,16 @@ public void processEngineMessages(GameData gameData) { } } break; + case MsgUpdateUiAfterMove mUUAM: + // The unit finished moving and still has moves left, so we need to + // mark it as the selected unit again. + // + // Among other things, this will refresh the UI and ensure that the + // unit action buttons are correct. + if (CurrentlySelectedUnit != MapUnit.NONE) { + setSelectedUnit(CurrentlySelectedUnit); + } + break; } } } @@ -526,7 +536,6 @@ private void ProcessAction(string currentAction) { if (dir.HasValue) { new MsgMoveUnit(CurrentlySelectedUnit.id, dir.Value).send(); - setSelectedUnit(CurrentlySelectedUnit); //also triggers updating the lower-left info box } } @@ -608,6 +617,10 @@ private void ProcessAction(string currentAction) { new MsgBuildRoad(CurrentlySelectedUnit.id).send(); } + if (currentAction == C7Action.UnitBuildMine && CurrentlySelectedUnit.canBuildMine()) { + new MsgBuildMine(CurrentlySelectedUnit.id).send(); + } + } private void GetNextAutoselectedUnit(GameData gameData) { diff --git a/C7/Text/c7-static-map-save.json b/C7/Text/c7-static-map-save.json index 2e6d5a45..4d252ab5 100644 --- a/C7/Text/c7-static-map-save.json +++ b/C7/Text/c7-static-map-save.json @@ -58936,6 +58936,7 @@ ], "actions": [ "unit_build_road", + "unit_build_mine", "unit_hold", "unit_wait", "unit_fortify", diff --git a/C7/UIElements/UnitButtons/UnitButtons.cs b/C7/UIElements/UnitButtons/UnitButtons.cs index 19a3ed92..17d13bef 100644 --- a/C7/UIElements/UnitButtons/UnitButtons.cs +++ b/C7/UIElements/UnitButtons/UnitButtons.cs @@ -61,7 +61,7 @@ public override void _Ready() { AddNewButton(specializedControls, new UnitControlButton("fortress", 0, 3, onButtonPressed)); AddNewButton(specializedControls, new UnitControlButton("barricade", 4, 4, onButtonPressed)); - AddNewButton(specializedControls, new UnitControlButton("mine", 1, 3, onButtonPressed)); + AddNewButton(specializedControls, new UnitControlButton(C7Action.UnitBuildMine, 1, 3, onButtonPressed)); AddNewButton(specializedControls, new UnitControlButton("irrigate", 2, 3, onButtonPressed)); AddNewButton(specializedControls, new UnitControlButton("chopForest", 3, 3, onButtonPressed)); AddNewButton(specializedControls, new UnitControlButton("chopJungle", 4, 3, onButtonPressed)); diff --git a/C7/project.godot b/C7/project.godot index 86b76760..f039f0ab 100644 --- a/C7/project.godot +++ b/C7/project.godot @@ -183,6 +183,11 @@ unit_build_road={ "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":82,"key_label":0,"unicode":114,"echo":false,"script":null) ] } +unit_build_mine={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":77,"key_label":0,"unicode":109,"echo":false,"script":null) +] +} [mono] diff --git a/C7Engine/EntryPoints/CityInteractions.cs b/C7Engine/EntryPoints/CityInteractions.cs index 768710c1..ff4b835f 100644 --- a/C7Engine/EntryPoints/CityInteractions.cs +++ b/C7Engine/EntryPoints/CityInteractions.cs @@ -19,7 +19,11 @@ public static void BuildCity(int x, int y, ID playerID, string name) { gameData.cities.Add(newCity); owner.cities.Add(newCity); tileWithNewCity.cityAtTile = newCity; + + // Cities are treated as though they have a road, but if + // a city is build on a mine, the mine should be removed. tileWithNewCity.overlays.road = true; + tileWithNewCity.overlays.mine = false; } public static void DestroyCity(int x, int y) { diff --git a/C7Engine/EntryPoints/MessageToEngine.cs b/C7Engine/EntryPoints/MessageToEngine.cs index a85323cf..8b2c9403 100644 --- a/C7Engine/EntryPoints/MessageToEngine.cs +++ b/C7Engine/EntryPoints/MessageToEngine.cs @@ -56,6 +56,12 @@ public MsgMoveUnit(ID unitID, TileDirection dir) { public override void process() { MapUnit unit = EngineStorage.gameData.GetUnit(unitID); unit?.move(dir); + + // The unit moved to a new tile - if it still has movement points, + // update the UI to reflect this new position and movement points. + if (unit?.movementPoints.canMove == true) { + new MsgUpdateUiAfterMove().send(); + } } } @@ -73,6 +79,12 @@ public MsgSetUnitPath(ID unitID, Tile tile) { public override void process() { MapUnit unit = EngineStorage.gameData.GetUnit(unitID); unit?.setUnitPath(EngineStorage.gameData.map.tileAt(destX, destY)); + + // The unit moved to a new tile - if it still has movement points, + // update the UI to reflect this new position and movement points. + if (unit?.movementPoints.canMove == true) { + new MsgUpdateUiAfterMove().send(); + } } } @@ -130,6 +142,19 @@ public override void process() { } } + public class MsgBuildMine : MessageToEngine { + private ID unitID; + + public MsgBuildMine(ID unitID) { + this.unitID = unitID; + } + + public override void process() { + MapUnit unit = EngineStorage.gameData.GetUnit(unitID); + unit?.buildMine(); + } + } + public class MsgChooseProduction : MessageToEngine { private ID cityID; private string producibleName; diff --git a/C7Engine/EntryPoints/MessageToUI.cs b/C7Engine/EntryPoints/MessageToUI.cs index a4979811..2fc7dc8c 100644 --- a/C7Engine/EntryPoints/MessageToUI.cs +++ b/C7Engine/EntryPoints/MessageToUI.cs @@ -38,6 +38,7 @@ public MsgStartEffectAnimation(Tile tile, AnimatedEffect effect, AutoResetEvent public class MsgStartTurn : MessageToUI { } + public class MsgUpdateUiAfterMove : MessageToUI { } public class MsgCityDestroyed : MessageToUI { public City city; diff --git a/C7Engine/EntryPoints/UnitInteractions.cs b/C7Engine/EntryPoints/UnitInteractions.cs index 8fee0500..e20d41d8 100644 --- a/C7Engine/EntryPoints/UnitInteractions.cs +++ b/C7Engine/EntryPoints/UnitInteractions.cs @@ -54,6 +54,10 @@ public static List GetAvailableActions(MapUnit unit) { result.Add(C7Action.UnitBuildRoad); } + if (unit.canBuildMine()) { + result.Add(C7Action.UnitBuildMine); + } + // Eventually we will have advanced actions too, whose availability will rely on their base actions' availability. // unit.availableActions.Add("rename"); diff --git a/C7Engine/MapUnitExtensions.cs b/C7Engine/MapUnitExtensions.cs index 96b15700..ae53d8ba 100644 --- a/C7Engine/MapUnitExtensions.cs +++ b/C7Engine/MapUnitExtensions.cs @@ -452,5 +452,27 @@ public static void buildRoad(this MapUnit unit) { unit.movementPoints.onConsumeAll(); } + public static bool canBuildMine(this MapUnit unit) { + // Mines can only be built on land, if there is no mine already there, + // and if there isn't a city. + // + // Volcanos also cannot be mined. + return unit.unitType.actions.Contains(C7Action.UnitBuildMine) && + unit.location.IsLand() && + !unit.location.IsVolcano() && + !unit.location.overlays.mine && + unit.location.cityAtTile == null; + } + + public static void buildMine(this MapUnit unit) { + if (!unit.canBuildMine()) { + log.Warning($"can't build mine by {unit}"); + return; + } + + // TODO add animation and long process of building + unit.location.overlays.mine = true; + unit.movementPoints.onConsumeAll(); + } } } diff --git a/C7GameData/Actions.cs b/C7GameData/Actions.cs index 8be21d09..43a616e7 100644 --- a/C7GameData/Actions.cs +++ b/C7GameData/Actions.cs @@ -1,4 +1,5 @@ namespace C7GameData { + // The strings for each action correspond to values in project.godot for keyboard shortcuts public static class C7Action { public const string EndTurn = "end_turn"; public const string Escape = "escape"; @@ -16,6 +17,7 @@ public static class C7Action { public const string UnitBombard = "unit_bombard"; public const string UnitBuildCity = "unit_build_city"; public const string UnitBuildRoad = "unit_build_road"; + public const string UnitBuildMine = "unit_build_mine"; public const string UnitDisband = "unit_disband"; public const string UnitExplore = "unit_explore"; public const string UnitFortify = "unit_fortify"; diff --git a/C7GameData/ImportCiv3.cs b/C7GameData/ImportCiv3.cs index 1f18e9a4..5f8c157e 100644 --- a/C7GameData/ImportCiv3.cs +++ b/C7GameData/ImportCiv3.cs @@ -464,6 +464,9 @@ private void ImportUnitPrototypes() { if (prto.BuildRoad) { prototype.actions.Add(C7Action.UnitBuildRoad); } + if (prto.BuildMine) { + prototype.actions.Add(C7Action.UnitBuildMine); + } if (prto.Bombard) { prototype.actions.Add(C7Action.UnitBombard); } diff --git a/C7GameData/TerrainType.cs b/C7GameData/TerrainType.cs index 9cfe72a3..ea807d9c 100644 --- a/C7GameData/TerrainType.cs +++ b/C7GameData/TerrainType.cs @@ -27,6 +27,10 @@ public bool isHilly() { return false; } + public bool isVolcano() { + return Key.Equals("volcano"); + } + //TODO: Once we have IDs, this should *not* rely on the display name. //That will be after issue 58, which will be after PR 70. public bool isWater() { diff --git a/C7GameData/Tile.cs b/C7GameData/Tile.cs index 9fb92b0f..d0dc8986 100644 --- a/C7GameData/Tile.cs +++ b/C7GameData/Tile.cs @@ -136,6 +136,10 @@ public bool IsAllowCities() { return overlayTerrainType.allowCities; } + public bool IsVolcano() { + return overlayTerrainType.isVolcano(); + } + public TileDirection directionTo(Tile other) { // TODO: Consider edge wrapping, the direction should point along the shortest path as the crow flies.