Split buildings and units to 2 separate files and types - both are now IConstruction, and cityBuildings has been changed to cityConstructions will all that entails

This commit is contained in:
Yair Morgenstern 2017-12-17 19:45:09 +02:00
parent 832191d87e
commit f30ade602d
21 changed files with 447 additions and 307 deletions

View File

@ -21,7 +21,7 @@ android {
applicationId "com.unciv.game"
minSdkVersion 9
targetSdkVersion 25
versionCode 10
versionCode 11
versionName "0.9"
}
buildTypes {

View File

@ -1,241 +0,0 @@
package com.unciv.civinfo;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Predicate;
import com.unciv.game.UnCivGame;
import com.unciv.game.VictoryScreen;
import com.unciv.models.LinqCollection;
import com.unciv.models.gamebasics.Building;
import com.unciv.models.gamebasics.GameBasics;
import com.unciv.models.stats.FullStats;
import java.util.HashMap;
public class CityBuildings
{
static final String Worker="Worker";
static final String Settler="Settler";
public Vector2 cityLocation;
public CityBuildings(){} // for json parsing, we need to have a default constructor
public CityBuildings(CityInfo cityInfo)
{
cityLocation = cityInfo.cityLocation;
}
public LinqCollection<String> builtBuildings = new LinqCollection<String>();
public HashMap<String, Integer> inProgressBuildings = new HashMap<String, Integer>();
public String currentBuilding = Worker; // default starting building!
public Building getCurrentBuilding(){return getGameBuilding(currentBuilding);}
public CityInfo getCity(){return UnCivGame.Current.civInfo.tileMap.get(cityLocation).getCity(); }
public boolean isBuilt(String buildingName) { return builtBuildings.contains(buildingName); }
public boolean isBuilding(String buildingName) { return currentBuilding!=null && currentBuilding.equals(buildingName); }
Building getGameBuilding(String buildingName) { return GameBasics.Buildings.get(buildingName); }
public LinqCollection<Building> getBuiltBuildings(){ return builtBuildings.select(new LinqCollection.Func<String, Building>() {
@Override
public Building GetBy(String arg0) {
return getGameBuilding(arg0);
}
}); }
public void nextTurn(FullStats cityStats)
{
if (getCurrentBuilding()==null) return;
Building gameBuilding = getGameBuilding(currentBuilding);
// Let's try to remove the building from the city, and seee if we can still build it (weneed to remove because of wonders etc.
String saveCurrentBuilding = currentBuilding;
currentBuilding = null;
if(!canBuild(gameBuilding)){
// We can't build this building anymore! (Wonder has been built / resource is gone / etc.)
CivilizationInfo.current().notifications.add("Cannot continue work on "+saveCurrentBuilding);
chooseNextBuilding();
gameBuilding = getGameBuilding(currentBuilding);
}
else currentBuilding = saveCurrentBuilding;
if (!inProgressBuildings.containsKey(currentBuilding)) inProgressBuildings.put(currentBuilding, 0);
inProgressBuildings.put(currentBuilding, inProgressBuildings.get(currentBuilding) + Math.round(cityStats.production));
if (inProgressBuildings.get(currentBuilding) >= gameBuilding.cost)
{
if (currentBuilding.equals(Worker) || currentBuilding.equals(Settler))
UnCivGame.Current.civInfo.tileMap.get(cityLocation).unit = new Unit(currentBuilding,2);
else if("SpaceshipPart".equals(gameBuilding.unique)) {
CivilizationInfo.current().scienceVictory.currentParts.add(currentBuilding, 1);
if(CivilizationInfo.current().scienceVictory.unconstructedParts().isEmpty())
UnCivGame.Current.setScreen(new VictoryScreen(UnCivGame.Current));
}
else
{
builtBuildings.add(currentBuilding);
if (gameBuilding.providesFreeBuilding != null && !builtBuildings.contains(gameBuilding.providesFreeBuilding))
builtBuildings.add(gameBuilding.providesFreeBuilding);
if (gameBuilding.freeTechs != 0) UnCivGame.Current.civInfo.tech.freeTechs += gameBuilding.freeTechs;
if("EmpireEntersGoldenAge".equals(gameBuilding.unique)) CivilizationInfo.current().enterGoldenAge();
}
inProgressBuildings.remove(currentBuilding);
CivilizationInfo.current().notifications.add(currentBuilding+" has been built in "+getCity().name);
chooseNextBuilding();
}
}
private void chooseNextBuilding() {
currentBuilding = getBuildableBuildings().first(new Predicate<String>() {
@Override
public boolean evaluate(String arg0) {
if(arg0.equals(Settler) || arg0.equals(Worker) || getGameBuilding(arg0).isWonder) return false;
return !builtBuildings.contains(arg0);
}
});
if (currentBuilding == null) currentBuilding = Worker;
CivilizationInfo.current().notifications.add("Work has started on "+currentBuilding);
}
public boolean canBuild(final Building building)
{
CivilizationInfo civInfo = UnCivGame.Current.civInfo;
if(isBuilt(building.name)) return false;
if(building.requiredNearbyImprovedResources!=null) {
boolean containsResourceWithImprovement = getCity().getTilesInRange()
.any(new Predicate<TileInfo>() {
@Override
public boolean evaluate(TileInfo tile) {
return tile.resource != null
&& building.requiredNearbyImprovedResources.contains(tile.resource)
&& tile.getTileResource().improvement.equals(tile.improvement);
}
});
if(!containsResourceWithImprovement) return false;
}
if (building.requiredTech != null && !civInfo.tech.isResearched(building.requiredTech)) return false;
if (building.isWonder && civInfo.cities
.any(new Predicate<CityInfo>() {
@Override
public boolean evaluate(CityInfo arg0) {
CityBuildings CB = arg0.cityBuildings;
return CB.isBuilding(building.name) || CB.isBuilt(building.name);
}
}) ) return false;
if (building.requiredBuilding != null && !isBuilt(building.requiredBuilding)) return false;
if (building.requiredBuildingInAllCities != null ||
civInfo.cities.any(new Predicate<CityInfo>() {
@Override
public boolean evaluate(CityInfo arg0) {
return arg0.cityBuildings.isBuilt(building.requiredBuildingInAllCities);
}
}) ) return false;
if(building.cannotBeBuiltWith != null && isBuilt(building.cannotBeBuiltWith)) return false;
if("MustBeNextToDesert".equals(building.unique) &&
!civInfo.tileMap.getTilesInDistance(cityLocation,1).any(new Predicate<TileInfo>() {
@Override
public boolean evaluate(TileInfo arg0) {
return arg0.baseTerrain.equals("Desert");
}
}))
return false;
if(building.requiredResource!=null &&
!civInfo.getCivResources().keySet().contains(GameBasics.TileResources.get(building.requiredResource)))
return false; // Only checks if exists, doesn't check amount - todo
if("SpaceshipPart".equals(building.unique)){
if(!civInfo.getBuildingUniques().contains("ApolloProgram")) return false;
if(civInfo.scienceVictory.requiredParts.get(building.name)==0) return false; // Don't need to build any more of these!
}
return true;
}
public LinqCollection<String> getBuildableBuildings()
{
return new LinqCollection<Building>(GameBasics.Buildings.values())
.where(new Predicate<Building>() {
@Override
public boolean evaluate(Building arg0) { return canBuild(arg0); }
})
.select(new LinqCollection.Func<Building, String>() {
@Override
public String GetBy(Building arg0) {
return arg0.name;
}
});
}
public FullStats getStats()
{
FullStats stats = new FullStats();
for(Building building : getBuiltBuildings()) stats.add(building);
stats.science += getCity().getBuildingUniques().count(new Predicate<String>() {
@Override
public boolean evaluate(String arg0) {
return "SciencePer2Pop".equals(arg0);
}
}) * getCity().population/2; // Library and public school unique (not actualy unique, though...hmm)
return stats;
}
public int getMaintainanceCosts(){
int maintainanceTotal = 0;
for( Building building : getBuiltBuildings()) maintainanceTotal+=building.maintainance;
return maintainanceTotal;
}
public FullStats getStatPercentBonuses(){
FullStats stats = new FullStats();
for(Building building : getBuiltBuildings())
if(building.percentStatBonus != null)
stats.add(building.percentStatBonus);
return stats;
}
public int workDone(String buildingName) {
if (inProgressBuildings.containsKey(buildingName))
return inProgressBuildings.get(buildingName);
return 0;
}
public int turnsToBuilding(String buildingName)
{
float workLeft = getGameBuilding(buildingName).cost - workDone(buildingName); // needs to be float so that we get the cieling properly ;)
FullStats cityStats = getCity().cityStats;
int production = Math.round(cityStats.production);
if (buildingName.equals(Settler)) production += cityStats.food;
return (int) Math.ceil(workLeft / production);
}
public void purchaseBuilding(String buildingName) {
CivilizationInfo.current().civStats.gold -= getGameBuilding(buildingName).getGoldCost();
builtBuildings.add(buildingName);
if(currentBuilding.equals(buildingName)) chooseNextBuilding();
}
public String getCityProductionText(){
String result = currentBuilding;
if(!result.equals("Science") && !result.equals("Gold"))
result+="\r\n"+turnsToBuilding(currentBuilding)+" turns";
return result;
}
public String getAmountConstructedText(){
if(currentBuilding.equals("Science") || currentBuilding.equals("Gold")) return "";
return " ("+ workDone(currentBuilding) + "/"+getCurrentBuilding().cost+")";
}
}

View File

@ -0,0 +1,185 @@
package com.unciv.civinfo;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.utils.Predicate;
import com.unciv.game.UnCivGame;
import com.unciv.models.LinqCollection;
import com.unciv.models.gamebasics.Building;
import com.unciv.models.gamebasics.GameBasics;
import com.unciv.models.stats.FullStats;
import java.util.HashMap;
public class CityConstructions
{
static final String Worker="Worker";
static final String Settler="Settler";
public Vector2 cityLocation;
public CityConstructions(){} // for json parsing, we need to have a default constructor
public CityConstructions(CityInfo cityInfo)
{
cityLocation = cityInfo.cityLocation;
}
public LinqCollection<String> builtBuildings = new LinqCollection<String>();
public HashMap<String, Integer> inProgressConstructions = new HashMap<String, Integer>();
public String currentConstruction = Worker; // default starting building!
public IConstruction getCurrentConstruction(){return getConstruction(currentConstruction);}
public CityInfo getCity(){return UnCivGame.Current.civInfo.tileMap.get(cityLocation).getCity(); }
public boolean isBuilt(String buildingName) { return builtBuildings.contains(buildingName); }
public boolean isBuilding(String buildingName) { return currentConstruction !=null && currentConstruction.equals(buildingName); }
IConstruction getConstruction(String constructionName) {
if(GameBasics.Buildings.containsKey(constructionName))
return GameBasics.Buildings.get(constructionName);
else if(GameBasics.Units.containsKey(constructionName))
return GameBasics.Units.get(constructionName);
return null;
}
public LinqCollection<Building> getBuiltBuildings(){ return builtBuildings.select(new LinqCollection.Func<String, Building>() {
@Override
public Building GetBy(String arg0) {
return GameBasics.Buildings.get(arg0);
}
}); }
public void nextTurn(FullStats cityStats)
{
if (getCurrentConstruction()==null) return;
IConstruction construction = getConstruction(currentConstruction);
// Let's try to remove the building from the city, and see if we can still build it (weneed to remove because of wonders etc.
String saveCurrentConstruction = currentConstruction;
currentConstruction = null;
if(!construction.isBuildable(this)){
// We can't build this building anymore! (Wonder has been built / resource is gone / etc.)
CivilizationInfo.current().notifications.add("Cannot continue work on "+saveCurrentConstruction);
chooseNextConstruction();
construction = getConstruction(currentConstruction);
}
else currentConstruction = saveCurrentConstruction;
if (!inProgressConstructions.containsKey(currentConstruction)) inProgressConstructions.put(currentConstruction, 0);
inProgressConstructions.put(currentConstruction, inProgressConstructions.get(currentConstruction) + Math.round(cityStats.production));
if (inProgressConstructions.get(currentConstruction) >= construction.getProductionCost())
{
construction.postBuildEvent(this);
inProgressConstructions.remove(currentConstruction);
CivilizationInfo.current().notifications.add(currentConstruction +" has been built in "+getCity().name);
chooseNextConstruction();
}
}
private void chooseNextConstruction() {
currentConstruction = getBuildableBuildings().first(new Predicate<String>() {
@Override
public boolean evaluate(String arg0) {
if(((Building)getConstruction(arg0)).isWonder) return false;
return !builtBuildings.contains(arg0);
}
});
if (currentConstruction == null) currentConstruction = Worker;
CivilizationInfo.current().notifications.add("Work has started on "+ currentConstruction);
}
public LinqCollection<String> getBuildableBuildings()
{
final CityConstructions self=this;
return new LinqCollection<Building>(GameBasics.Buildings.values())
.where(new Predicate<Building>() {
@Override
public boolean evaluate(Building arg0) { return (arg0.isBuildable(self)); }
})
.select(new LinqCollection.Func<Building, String>() {
@Override
public String GetBy(Building arg0) {
return arg0.name;
}
});
}
public FullStats getStats()
{
FullStats stats = new FullStats();
for(Building building : getBuiltBuildings()) stats.add(building);
stats.science += getCity().getBuildingUniques().count(new Predicate<String>() {
@Override
public boolean evaluate(String arg0) {
return "SciencePer2Pop".equals(arg0);
}
}) * getCity().population/2; // Library and public school unique (not actualy unique, though...hmm)
return stats;
}
public int getMaintainanceCosts(){
int maintainanceTotal = 0;
for( Building building : getBuiltBuildings()) maintainanceTotal+=building.maintainance;
return maintainanceTotal;
}
public FullStats getStatPercentBonuses(){
FullStats stats = new FullStats();
for(Building building : getBuiltBuildings())
if(building.percentStatBonus != null)
stats.add(building.percentStatBonus);
return stats;
}
public int workDone(String constructionName) {
if (inProgressConstructions.containsKey(constructionName))
return inProgressConstructions.get(constructionName);
return 0;
}
public int turnsToConstruction(String constructionName){
int cost = getConstruction(constructionName).getProductionCost();
float workLeft = cost - workDone(constructionName); // needs to be float so that we get the cieling properly ;)
FullStats cityStats = getCity().cityStats;
int production = Math.round(cityStats.production);
if (constructionName.equals(Settler)) production += cityStats.food;
return (int) Math.ceil(workLeft / production);
}
public void purchaseBuilding(String buildingName) {
CivilizationInfo.current().civStats.gold -= getConstruction(buildingName).getGoldCost();
builtBuildings.add(buildingName);
if(currentConstruction.equals(buildingName)) chooseNextConstruction();
}
public String getCityProductionTextForCityButton(){
String result = currentConstruction;
if(!result.equals("Science") && !result.equals("Gold"))
result+="\r\n"+ turnsToConstruction(currentConstruction)+" turns";
return result;
}
public String getProductionForTileInfo(){
String result = currentConstruction;
if(!result.equals("Science") && !result.equals("Gold"))
result+="\r\nin "+ turnsToConstruction(currentConstruction)+" turns,\r\n";
return result;
}
public String getAmountConstructedText(){
if(currentConstruction.equals("Science") || currentConstruction.equals("Gold")) return "";
return " ("+ workDone(currentConstruction) + "/"+ getCurrentConstruction().getProductionCost()+")";
}
}

View File

@ -5,7 +5,6 @@ import com.badlogic.gdx.utils.Predicate;
import com.unciv.game.UnCivGame;
import com.unciv.models.LinqCollection;
import com.unciv.models.LinqCounter;
import com.unciv.models.LinqHashMap;
import com.unciv.models.gamebasics.Building;
import com.unciv.models.gamebasics.GameBasics;
import com.unciv.models.gamebasics.TileResource;
@ -15,7 +14,7 @@ public class CityInfo {
public final Vector2 cityLocation;
public String name;
public CityBuildings cityBuildings;
public CityConstructions cityConstructions;
public int cultureStored;
private int tilesClaimed;
public int population = 1;
@ -35,7 +34,9 @@ public class CityInfo {
});
}
private String[] CityNames = new String[]{"Assur", "Ninveh", "Nimrud", "Kar-Tukuli-Ninurta", "Dur-Sharrukin"};
private String[] CityNames = new String[]{
"New Bark","Cherrygrove","Violet","Azalea","Goldenrod","Ecruteak","Olivine","Cianwood","Mahogany","Blackthorn",
"Pallet","Viridian","Pewter","Cerulean","Vermillion","Lavender","Celadon","Fuchsia","Saffron","Cinnibar"};
public CityInfo(){
cityLocation = Vector2.Zero;
@ -55,8 +56,8 @@ public class CityInfo {
CityInfo(CivilizationInfo civInfo, Vector2 cityLocation) {
name = CityNames[civInfo.cities.size()];
this.cityLocation = cityLocation;
cityBuildings = new CityBuildings(this);
if(civInfo.cities.size()==0) cityBuildings.builtBuildings.add("Palace");
cityConstructions = new CityConstructions(this);
if(civInfo.cities.size()==0) cityConstructions.builtBuildings.add("Palace");
civInfo.cities.add(this);
for(TileInfo tileInfo : civInfo.tileMap.getTilesInDistance(cityLocation,1)) {
@ -82,7 +83,7 @@ public class CityInfo {
cityResources.add(resource,1);
}
// Remove resources required by buildings
for(Building building : cityBuildings.getBuiltBuildings()){
for(Building building : cityConstructions.getBuiltBuildings()){
if(building.requiredResource!=null){
TileResource resource = GameBasics.TileResources.get(building.requiredResource);
cityResources.add(resource,-1);
@ -119,7 +120,6 @@ public class CityInfo {
//idle ppl
stats.production += getFreePopulation();
stats.food -= population * 2;
if(!isCapital() && isConnectedToCapital(RoadStatus.Road)) {
// Calculated by http://civilization.wikia.com/wiki/Trade_route_(Civ5)
@ -129,35 +129,47 @@ public class CityInfo {
stats.gold += goldFromTradeRoute;
}
stats.add(cityBuildings.getStats());
stats.add(cityConstructions.getStats());
FullStats statPercentBonuses = cityBuildings.getStatPercentBonuses();
FullStats statPercentBonuses = cityConstructions.getStatPercentBonuses();
if(isCapital() || isConnectedToCapital(RoadStatus.Railroad)) statPercentBonuses.production += 25;
if(CivilizationInfo.current().isGoldenAge()) statPercentBonuses.production+=20;
IConstruction currentConstruction = cityConstructions.getCurrentConstruction();
if(currentConstruction instanceof Building && ((Building)currentConstruction).isWonder &&
CivilizationInfo.current().getCivResources().containsKey(GameBasics.TileResources.get("Marble")))
statPercentBonuses.production+=15;
stats.production*=1+statPercentBonuses.production/100; // So they get bonuses for production and gold/science
if(cityBuildings.currentBuilding.equals("Gold")) stats.gold+=stats.production/4;
if(cityBuildings.currentBuilding.equals("Science")) stats.science+=stats.production/4;
stats.food*=1+statPercentBonuses.food/100;
if(cityConstructions.currentConstruction.equals("Gold")) stats.gold+=stats.production/4;
if(cityConstructions.currentConstruction.equals("Science")) stats.science+=stats.production/4;
stats.gold*=1+statPercentBonuses.gold/100;
stats.science*=1+statPercentBonuses.science/100;
stats.culture*=1+statPercentBonuses.culture/100;
stats.gold-=cityBuildings.getMaintainanceCosts(); // this is AFTER the bonus calculation!
if(CivilizationInfo.current().getHappinessForNextTurn() < 0)
stats.food /= 4; // Reduce excess food to 1/4
boolean isUnhappy =CivilizationInfo.current().getHappinessForNextTurn() < 0;
if (!isUnhappy) stats.food*=1+statPercentBonuses.food/100; // Regular food bonus revoked when unhappy per https://forums.civfanatics.com/resources/complete-guide-to-happiness-vanilla.25584/
stats.food -= population * 2; // Food reduced after the bonus
if(isUnhappy) stats.food /= 4; // Reduce excess food to 1/4 per the same
stats.gold-= cityConstructions.getMaintainanceCosts(); // this is AFTER the bonus calculation!
this.cityStats = stats;
}
public int getCityHappiness(){ // needs to be a separate function because we need to know the global happiness state
public float getCityHappiness(){ // needs to be a separate function because we need to know the global happiness state
// in order to determine how much food is produced in a city!
int happiness = -3 - population; // -3 happiness per city and -1 per population
return happiness + (int)cityBuildings.getStats().happiness;
float happiness = -3; // -3 happiness per city
if(CivilizationInfo.current().getBuildingUniques().contains("CitizenUnhappinessDecreased"))
happiness-=population*0.9;
else happiness-=population; //and -1 per population
return happiness + (int) cityConstructions.getStats().happiness;
}
void nextTurn() {
FullStats stats = cityStats;
if (cityBuildings.currentBuilding.equals(CityBuildings.Settler) && stats.food > 0) {
if (cityConstructions.currentConstruction.equals(CityConstructions.Settler) && stats.food > 0) {
stats.production += stats.food;
stats.food = 0;
}
@ -178,7 +190,7 @@ public class CityInfo {
CivilizationInfo.current().notifications.add(name+" has grown!");
}
cityBuildings.nextTurn(stats);
cityConstructions.nextTurn(stats);
cultureStored+=stats.culture;
if(cultureStored>=getCultureToNextTile()){
@ -275,7 +287,7 @@ public class CityInfo {
}
public LinqCollection<String> getBuildingUniques(){
return cityBuildings.getBuiltBuildings().select(new LinqCollection.Func<Building, String>() {
return cityConstructions.getBuiltBuildings().select(new LinqCollection.Func<Building, String>() {
@Override
public String GetBy(Building arg0) {
return arg0.unique;

View File

@ -5,12 +5,12 @@ import com.badlogic.gdx.utils.Predicate;
import com.unciv.game.UnCivGame;
import com.unciv.models.LinqCollection;
import com.unciv.models.LinqCounter;
import com.unciv.models.LinqHashMap;
import com.unciv.models.gamebasics.Building;
import com.unciv.models.gamebasics.GameBasics;
import com.unciv.models.gamebasics.ResourceType;
import com.unciv.models.gamebasics.TileResource;
import com.unciv.models.stats.CivStats;
import com.unciv.models.stats.FullStats;
import java.util.Collection;
@ -26,6 +26,8 @@ public class CivilizationInfo {
public int turnsLeftForCurrentGoldenAge=0;
public String civName = "Babylon";
public FullStats greatPersonPoints = new FullStats();
public CivilizationTech tech = new CivilizationTech();
public int turns = 1;
public LinqCollection<String> notifications = new LinqCollection<String>();
@ -55,7 +57,7 @@ public class CivilizationInfo {
return cities.first(new Predicate<CityInfo>() {
@Override
public boolean evaluate(CityInfo arg0) {
return arg0.cityBuildings.isBuilt("Palace");
return arg0.cityConstructions.isBuilt("Palace");
}
});
}
@ -71,8 +73,9 @@ public class CivilizationInfo {
CivStats nextTurnStats = getStatsForNextTurn();
civStats.add(nextTurnStats);
if(!isGoldenAge())
civStats.happiness += getHappinessForNextTurn();
int happiness = getHappinessForNextTurn();
if(!isGoldenAge() && happiness>0)
civStats.happiness += happiness;
if(cities.size() > 0) tech.nextTurn((int)nextTurnStats.science);
@ -102,6 +105,10 @@ public class CivilizationInfo {
statsForTurn.add(city.cityStats);
}
statsForTurn.happiness=0;
for(TileInfo tile : tileMap.values()) {
if (tile.roadStatus == RoadStatus.Road) statsForTurn.gold += 1;
else if(tile.roadStatus == RoadStatus.Railroad) statsForTurn.gold +=2;
}
return statsForTurn;
}
@ -130,7 +137,7 @@ public class CivilizationInfo {
return cities.selectMany(new LinqCollection.Func<CityInfo, Collection<? extends String>>() {
@Override
public Collection<? extends String> GetBy(CityInfo arg0) {
return arg0.cityBuildings.getBuiltBuildings().select(new LinqCollection.Func<Building, String>() {
return arg0.cityConstructions.getBuiltBuildings().select(new LinqCollection.Func<Building, String>() {
@Override
public String GetBy(Building arg0) {
return arg0.unique;

View File

@ -0,0 +1,10 @@
package com.unciv.civinfo;
import com.unciv.models.stats.INamed;
public interface IConstruction extends INamed {
public int getProductionCost();
public int getGoldCost();
public boolean isBuildable(CityConstructions construction);
public void postBuildEvent(CityConstructions construction); // Yes I'm hilarious.
}

View File

@ -67,7 +67,7 @@ public class TileInfo
if (hasViewableResource())
{
stats.add(resource); // resource base
if(resource.building !=null && city!=null && city.cityBuildings.isBuilt(resource.building))
if(resource.building !=null && city!=null && city.cityConstructions.isBuilt(resource.building))
{
stats.add(resource.GetBuilding().resourceBonusStats); // resource-specific building (eg forge, stable) bonus
}
@ -124,7 +124,7 @@ public class TileInfo
public void nextTurn()
{
if(unit !=null) unit.currentMovement = unit.maxMovement;
if(unit !=null) unit.currentMovement = unit.movement;
if (improvementInProgress == null || unit ==null || !unit.name.equals("Worker")) return;
turnsToImprovement -= 1;
@ -158,14 +158,14 @@ public class TileInfo
public String toString() {
StringBuilder SB = new StringBuilder();
if (isCityCenter()) SB.append(workingCity+",\r\n");
if (isCityCenter()){SB.append(workingCity+",\r\n"+getCity().cityConstructions.getProductionForTileInfo());}
SB.append(this.baseTerrain);
if (terrainFeature != null) SB.append(",\r\n" + terrainFeature);
if (hasViewableResource()) SB.append(",\r\n" + resource);
if (roadStatus!= RoadStatus.None && !isCityCenter()) SB.append(",\r\n" + roadStatus);
if (improvement != null) SB.append(",\r\n" + improvement);
if (improvementInProgress != null) SB.append(",\r\n" + improvementInProgress +" in "+this.turnsToImprovement +" turns");
if (unit !=null) SB.append(",\r\n" + unit.name + "("+ new DecimalFormat("0.#").format(unit.currentMovement)+"/"+ unit.maxMovement +")");
if (unit !=null) SB.append(",\r\n" + unit.name + "("+ new DecimalFormat("0.#").format(unit.currentMovement)+"/"+ unit.movement +")");
return SB.toString();
}

View File

@ -125,5 +125,13 @@ public class TileMap{
}).getRandom();
}
void placeUnitNearTile(Vector2 position, Unit unit){
getTilesInDistance(position,1).first(new Predicate<TileInfo>() {
@Override
public boolean evaluate(TileInfo arg0) {
return arg0.unit==null;
}
}).unit = unit; // And if there's none, then kill me.
}
}

View File

@ -1,15 +1,52 @@
package com.unciv.civinfo;
public class Unit{
import com.unciv.game.UnCivGame;
import com.unciv.models.stats.INamed;
public class Unit implements INamed, IConstruction{
public String name;
public int maxMovement;
public String description;
public int cost;
public int hurryCostModifier;
public int movement;
public float currentMovement;
boolean unbuildable; // for special units likee great people
public Unit(){} // for json parsing, we need to have a default constructor
public Unit(String name, int maxMovement) {
this.name = name;
this.maxMovement = maxMovement;
this.movement = maxMovement;
currentMovement = maxMovement;
}
@Override
public String getName() {
return name;
}
public boolean isConstructable() {
if(unbuildable) return false;
return true;
}
@Override
public int getProductionCost() {
return cost;
}
@Override
public int getGoldCost() {
return (int)( Math.pow(30 * cost,0.75) * (1 + hurryCostModifier/100) / 10 ) * 10;
}
@Override
public boolean isBuildable(CityConstructions construction) {
return !unbuildable;
}
@Override
public void postBuildEvent(CityConstructions construction) {
UnCivGame.Current.civInfo.tileMap.placeUnitNearTile(construction.cityLocation,new Unit(name,movement));
}
}

View File

@ -20,6 +20,7 @@ import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.badlogic.gdx.utils.Align;
import com.unciv.civinfo.CityInfo;
import com.unciv.civinfo.IConstruction;
import com.unciv.civinfo.TileInfo;
import com.unciv.game.pickerscreens.BuildingPickerScreen;
import com.unciv.models.gamebasics.Building;
@ -202,7 +203,7 @@ public class CityScreen extends com.unciv.game.utils.CameraStageBaseScreen {
HashMap<String,String> CityStatsValues = new LinkedHashMap<String, String>();
CityStatsValues.put("Production",Math.round(stats.production)
+cityInfo.cityBuildings.getAmountConstructedText());
+cityInfo.cityConstructions.getAmountConstructedText());
CityStatsValues.put("Food",Math.round(stats.food)
+" ("+cityInfo.foodStored+"/"+cityInfo.foodToNextPopulation()+")");
CityStatsValues.put("Gold",Math.round(stats.gold) +"");
@ -217,10 +218,10 @@ public class CityScreen extends com.unciv.game.utils.CameraStageBaseScreen {
CityStatsTable.row();
}
String CurrentBuilding = game.civInfo.getCurrentCity().cityBuildings.currentBuilding;
String CurrentBuilding = game.civInfo.getCurrentCity().cityConstructions.currentConstruction;
String BuildingText = "Pick building";
if(CurrentBuilding != null) BuildingText = cityInfo.cityBuildings.getCityProductionText();
if(CurrentBuilding != null) BuildingText = cityInfo.cityConstructions.getCityProductionTextForCityButton();
TextButton buildingPickButton = new TextButton(BuildingText,skin);
buildingPickButton.addListener(new ClickListener(){
@Override
@ -236,15 +237,15 @@ public class CityScreen extends com.unciv.game.utils.CameraStageBaseScreen {
// https://forums.civfanatics.com/threads/rush-buying-formula.393892/
Building building = cityInfo.cityBuildings.getCurrentBuilding();
if(building != null && !building.isWonder) {
IConstruction construction = cityInfo.cityConstructions.getCurrentConstruction();
if(construction != null && !(construction instanceof Building && ((Building)construction).isWonder)) {
CityStatsTable.row();
int buildingGoldCost = building.getGoldCost();
int buildingGoldCost = construction.getGoldCost();
TextButton buildingBuyButton = new TextButton("Buy for \r\n"+buildingGoldCost+" gold", skin);
buildingBuyButton.addListener(new ClickListener() {
@Override
public void clicked(InputEvent event, float x, float y) {
cityInfo.cityBuildings.purchaseBuilding(cityInfo.cityBuildings.currentBuilding);
cityInfo.cityConstructions.purchaseBuilding(cityInfo.cityConstructions.currentConstruction);
updateCityTable();
}
});

View File

@ -10,6 +10,7 @@ import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.unciv.civinfo.CivilizationInfo;
import com.unciv.civinfo.RoadStatus;
import com.unciv.civinfo.TileInfo;
import com.unciv.game.utils.ImageGetter;
import com.unciv.models.LinqHashMap;
public class TileGroup extends Group {
@ -18,6 +19,7 @@ public class TileGroup extends Group {
Image resourceImage;
Image unitImage;
Image improvementImage;
String improvementType;
Image populationImage;
LinqHashMap<String,Image> roadImages = new LinqHashMap<String, Image>();
Image hexagon;
@ -30,14 +32,14 @@ public class TileGroup extends Group {
terrainType = tileInfo.getLastTerrain().name;
String terrainFileName = "TerrainIcons/" + terrainType.replace(' ','_') + "_(Civ5).png";
terrainImage = com.unciv.game.utils.ImageGetter.getImageByFilename(terrainFileName);
terrainImage = ImageGetter.getImageByFilename(terrainFileName);
terrainImage.setSize(50,50);
addActor(terrainImage);
}
void addPopulationIcon(){
populationImage = com.unciv.game.utils.ImageGetter.getStatIcon("Population");
populationImage = ImageGetter.getStatIcon("Population");
populationImage.moveBy(0, terrainImage.getHeight()-populationImage.getHeight()); // top left
addActor(populationImage);
}
@ -52,12 +54,12 @@ public class TileGroup extends Group {
if(!terrainType.equals(tileInfo.getLastTerrain().name)) {
terrainType = tileInfo.getLastTerrain().name;
String terrainFileName = "TerrainIcons/" + terrainType.replace(' ', '_') + "_(Civ5).png";
terrainImage.setDrawable(new TextureRegionDrawable(com.unciv.game.utils.ImageGetter.textureRegionByFileName.get(terrainFileName))); // In case we e.g. removed a jungle
terrainImage.setDrawable(new TextureRegionDrawable(ImageGetter.textureRegionByFileName.get(terrainFileName))); // In case we e.g. removed a jungle
}
if (tileInfo.hasViewableResource() && resourceImage == null) { // Need to add the resource image!
String fileName = "ResourceIcons/" + tileInfo.resource + "_(Civ5).png";
Image image = com.unciv.game.utils.ImageGetter.getImageByFilename(fileName);
Image image = ImageGetter.getImageByFilename(fileName);
image.setSize(20,20);
image.moveBy(terrainImage.getWidth()-image.getWidth(), 0); // bottom right
resourceImage = image;
@ -65,7 +67,7 @@ public class TileGroup extends Group {
}
if (tileInfo.unit != null && unitImage == null) {
unitImage = com.unciv.game.utils.ImageGetter.getImageByFilename("StatIcons/" + tileInfo.unit.name + "_(Civ5).png");
unitImage = ImageGetter.getImageByFilename("StatIcons/" + tileInfo.unit.name + "_(Civ5).png");
addActor(unitImage);
unitImage.setSize(20, 20); // not moved - is at bottom left
}
@ -83,12 +85,13 @@ public class TileGroup extends Group {
}
if (tileInfo.improvement != null && improvementImage == null) {
improvementImage = com.unciv.game.utils.ImageGetter.getImageByFilename("ImprovementIcons/" + tileInfo.improvement.replace(' ','_') + "_(Civ5).png");
if (tileInfo.improvement != null &&!tileInfo.improvement.equals(improvementType)) {
improvementImage = ImageGetter.getImageByFilename("ImprovementIcons/" + tileInfo.improvement.replace(' ','_') + "_(Civ5).png");
addActor(improvementImage);
improvementImage.setSize(20, 20);
improvementImage.moveBy(terrainImage.getWidth()-improvementImage.getWidth(),
terrainImage.getHeight() - improvementImage.getHeight()); // top right
improvementType = tileInfo.improvement;
}
if(populationImage!=null){
@ -101,7 +104,7 @@ public class TileGroup extends Group {
if (neighbor == tileInfo || neighbor.roadStatus == RoadStatus.None) continue;
if (roadImages.containsKey(neighbor.position.toString())) continue;
Image image = com.unciv.game.utils.ImageGetter.getImageByFilename("TerrainIcons/road.png");
Image image = ImageGetter.getImageByFilename("TerrainIcons/road.png");
roadImages.put(neighbor.position.toString(), image);
Vector2 relativeHexPosition = tileInfo.position.cpy().sub(neighbor.position);

View File

@ -17,6 +17,8 @@ import com.unciv.models.gamebasics.TechColumn;
import com.unciv.models.gamebasics.Terrain;
import com.unciv.models.gamebasics.TileImprovement;
import com.unciv.models.gamebasics.TileResource;
import com.unciv.models.stats.INamed;
import com.unciv.models.stats.NamedStats;
public class UnCivGame extends Game {
@ -64,9 +66,9 @@ public class UnCivGame extends Game {
return new Json().fromJson(tClass,jsonText);
}
private <T extends com.unciv.models.stats.NamedStats> LinqHashMap<String,T> CreateHashmap(Class<T> tClass, T[] items){
private <T extends INamed> LinqHashMap<String,T> CreateHashmap(Class<T> tClass, T[] items){
LinqHashMap<String,T> hashMap = new LinqHashMap<String, T>();
for(T item:items) hashMap.put(item.GetName(),item);
for(T item:items) hashMap.put(item.getName(),item);
return hashMap;
}
@ -76,6 +78,7 @@ public class UnCivGame extends Game {
GameBasics.TileResources = CreateHashmap(TileResource.class,GetFromJson(TileResource[].class,"TileResources"));
GameBasics.TileImprovements = CreateHashmap(TileImprovement.class,GetFromJson(TileImprovement[].class,"TileImprovements"));
GameBasics.Helps = CreateHashmap(BasicHelp.class,GetFromJson(BasicHelp[].class,"BasicHelp"));
GameBasics.Units = CreateHashmap(Unit.class,GetFromJson(Unit[].class,"Units"));
TechColumn[] TechColumns = GetFromJson(TechColumn[].class, "Techs");
GameBasics.Technologies = new LinqHashMap<String, Technology>();

View File

@ -6,8 +6,6 @@ import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.unciv.game.utils.CameraStageBaseScreen;
import javax.xml.soap.Text;
public class VictoryScreen extends CameraStageBaseScreen{
public VictoryScreen(final UnCivGame game) {

View File

@ -62,7 +62,7 @@ public class WorldTileGroup extends TileGroup {
}
String cityButtonText = city.name +" ("+city.population+")";
// +" ("+city.population+")" + "\r\n" + city.cityBuildings.getCityProductionText();
// +" ("+city.population+")" + "\r\n" + city.cityConstructions.getCityProductionTextForCityButton();
TextButton button = cityButton.getActor();
button.setText(cityButtonText);
button.setSize(button.getPrefWidth(), button.getPrefHeight());

View File

@ -6,7 +6,8 @@ import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.unciv.civinfo.CityBuildings;
import com.unciv.civinfo.CityConstructions;
import com.unciv.civinfo.Unit;
import com.unciv.game.CityScreen;
import com.unciv.game.UnCivGame;
import com.unciv.models.gamebasics.Building;
@ -47,7 +48,7 @@ public class BuildingPickerScreen extends PickerScreen {
rightSideButton.addListener(new ClickListener(){
@Override
public void clicked(InputEvent event, float x, float y) {
game.civInfo.getCurrentCity().cityBuildings.currentBuilding = selectedProduction;
game.civInfo.getCurrentCity().cityConstructions.currentConstruction = selectedProduction;
game.civInfo.getCurrentCity().updateCityStats(); // Because maybe we set/removed the science or gold production options.
game.setScreen(new CityScreen(game));
dispose();
@ -56,21 +57,29 @@ public class BuildingPickerScreen extends PickerScreen {
rightSideButton.setTouchable(Touchable.disabled);
rightSideButton.setColor(Color.GRAY);
CityBuildings cityBuildings = game.civInfo.getCurrentCity().cityBuildings;
CityConstructions cityBuildings = game.civInfo.getCurrentCity().cityConstructions;
VerticalGroup regularBuildings = new VerticalGroup().space(10),
wonders = new VerticalGroup().space(10),
units = new VerticalGroup().space(10),
specials = new VerticalGroup().space(10);
for(final Building building : GameBasics.Buildings.values()) {
if(!cityBuildings.canBuild(building)) continue;
if(!building.isBuildable(cityBuildings)) continue;
TextButton TB = getProductionButton(building.name,
building.name +"\r\n"+cityBuildings.turnsToBuilding(building.name)+" turns",
building.name +"\r\n"+cityBuildings.turnsToConstruction(building.name)+" turns",
building.getDescription(true),
"Build "+building.name);
if(building.isWonder) wonders.addActor(TB);
else regularBuildings.addActor(TB);
}
for(Unit unit : GameBasics.Units.values()){
if(!unit.isConstructable()) continue;
units.addActor(getProductionButton(unit.name,
unit.name+"\r\n"+cityBuildings.turnsToConstruction(unit.name)+" turns",
unit.description, "Train "+unit.name));
}
if(game.civInfo.tech.isResearched("Education"))
specials.addActor(getProductionButton("Science","Produce Science",
"Convert production to science at a rate of 4 to 1", "Produce Science"));
@ -79,6 +88,7 @@ public class BuildingPickerScreen extends PickerScreen {
specials.addActor(getProductionButton("Gold","Produce Gold",
"Convert production to gold at a rate of 4 to 1", "Produce Gold"));
topTable.add(units);
topTable.add(regularBuildings);
topTable.add(wonders);
topTable.add(specials);

View File

@ -37,12 +37,15 @@ public class ImprovementPickerScreen extends PickerScreen {
regularImprovements.space(10);
for(final TileImprovement improvement : GameBasics.TileImprovements.values()) {
if(!tileInfo.canBuildImprovement(improvement) || improvement.name.equals(tileInfo.improvement)) continue;
TextButton TB = new TextButton(improvement.name +"\r\n"+improvement.turnsToBuild +" turns", skin);
int turnsToBuild = improvement.turnsToBuild;
if(CivilizationInfo.current().getBuildingUniques().contains("WorkerConstruction")) turnsToBuild= (int) Math.round(0.75*turnsToBuild);
TextButton TB = new TextButton(improvement.name +"\r\n"+turnsToBuild +" turns", skin);
final int finalTurnsToBuild = turnsToBuild;
TB.addListener(new ClickListener(){
@Override
public void clicked(InputEvent event, float x, float y) {
SelectedImprovement = improvement.name;
TurnsToImprovement = improvement.turnsToBuild;
TurnsToImprovement = finalTurnsToBuild;
rightSideButton.setTouchable(Touchable.enabled);
rightSideButton.setText(improvement.name);
rightSideButton.setColor(Color.WHITE);

View File

@ -1,9 +1,15 @@
package com.unciv.models.gamebasics;
import com.unciv.models.stats.INamed;
import com.unciv.models.stats.NamedStats;
public class BasicHelp extends NamedStats implements ICivilopedia {
public class BasicHelp implements ICivilopedia, INamed {
public String description;
public String name;
public String getName() {
return name;
}
@Override
public String getDescription() {

View File

@ -1,10 +1,18 @@
package com.unciv.models.gamebasics;
import com.badlogic.gdx.utils.Predicate;
import com.unciv.civinfo.CityInfo;
import com.unciv.civinfo.CivilizationInfo;
import com.unciv.civinfo.CityConstructions;
import com.unciv.civinfo.IConstruction;
import com.unciv.civinfo.TileInfo;
import com.unciv.game.UnCivGame;
import com.unciv.game.VictoryScreen;
import com.unciv.models.LinqCollection;
import com.unciv.models.stats.FullStats;
import com.unciv.models.stats.NamedStats;
public class Building extends NamedStats implements ICivilopedia {
public class Building extends NamedStats implements IConstruction, ICivilopedia {
public String description;
public String requiredTech;
@ -14,7 +22,9 @@ public class Building extends NamedStats implements ICivilopedia {
public int cost;
public int maintainance = 0;
public FullStats percentStatBonus = new FullStats();
public FullStats percentStatBonus;
public FullStats specialistSlots;
public FullStats greatPersonPoints;
public int hurryCostModifier; // Extra cost percentage when purchasing
public boolean isWonder = false;
public String requiredBuilding;
@ -54,12 +64,92 @@ public class Building extends NamedStats implements ICivilopedia {
if (maintainance != 0)
stringBuilder.append("Maintainance cost: " + maintainance + " gold\r\n");
stringBuilder.append(description + "\r\n" + stats);
if(this.percentStatBonus!=null){
if(this.percentStatBonus.production!=0) stringBuilder.append("\r\n+"+this.percentStatBonus.production+" production");
if(this.percentStatBonus.gold!=0) stringBuilder.append("\r\n+"+this.percentStatBonus.gold+" gold");
if(this.percentStatBonus.science!=0) stringBuilder.append("\r\n+"+this.percentStatBonus.science+" science");
if(this.percentStatBonus.food!=0) stringBuilder.append("\r\n+"+this.percentStatBonus.food+" food");
if(this.percentStatBonus.culture!=0) stringBuilder.append("\r\n+"+this.percentStatBonus.culture+" culture");
}
return stringBuilder.toString();
}
@Override
public int getProductionCost() {
return cost;
}
public int getGoldCost(){
return (int)( Math.pow(30 * cost,0.75) * (1 + hurryCostModifier/100) / 10 ) * 10;
}
public boolean isBuildable(CityConstructions construction){
CivilizationInfo civInfo = UnCivGame.Current.civInfo;
if(construction.isBuilt(name)) return false;
if(requiredNearbyImprovedResources!=null) {
boolean containsResourceWithImprovement = construction.getCity().getTilesInRange()
.any(new Predicate<TileInfo>() {
@Override
public boolean evaluate(TileInfo tile) {
return tile.resource != null
&& requiredNearbyImprovedResources.contains(tile.resource)
&& tile.getTileResource().improvement.equals(tile.improvement);
}
});
if(!containsResourceWithImprovement) return false;
}
if (requiredTech != null && !civInfo.tech.isResearched(requiredTech)) return false;
if (isWonder && civInfo.cities
.any(new Predicate<CityInfo>() {
@Override
public boolean evaluate(CityInfo arg0) {
CityConstructions CB = arg0.cityConstructions;
return CB.isBuilding(name) || CB.isBuilt(name);
}
}) ) return false;
if (requiredBuilding != null && !construction.isBuilt(requiredBuilding)) return false;
if (requiredBuildingInAllCities != null ||
civInfo.cities.any(new Predicate<CityInfo>() {
@Override
public boolean evaluate(CityInfo arg0) {
return arg0.cityConstructions.isBuilt(requiredBuildingInAllCities);
}
}) ) return false;
if(cannotBeBuiltWith != null && construction.isBuilt(cannotBeBuiltWith)) return false;
if("MustBeNextToDesert".equals(unique) &&
!civInfo.tileMap.getTilesInDistance(construction.cityLocation,1).any(new Predicate<TileInfo>() {
@Override
public boolean evaluate(TileInfo arg0) {
return arg0.baseTerrain.equals("Desert");
}
}))
return false;
if(requiredResource!=null &&
!civInfo.getCivResources().keySet().contains(GameBasics.TileResources.get(requiredResource)))
return false; // Only checks if exists, doesn't check amount - todo
if("SpaceshipPart".equals(unique)){
if(!civInfo.getBuildingUniques().contains("ApolloProgram")) return false;
if(civInfo.scienceVictory.requiredParts.get(name)==0) return false; // Don't need to build any more of these!
}
return true;
}
@Override
public void postBuildEvent(CityConstructions constructions) {
if("SpaceshipPart".equals(unique)) {
CivilizationInfo.current().scienceVictory.currentParts.add(name, 1);
if(CivilizationInfo.current().scienceVictory.unconstructedParts().isEmpty())
UnCivGame.Current.setScreen(new VictoryScreen(UnCivGame.Current));
return;
}
constructions.builtBuildings.add(name);
if (providesFreeBuilding != null && !constructions.builtBuildings.contains(providesFreeBuilding))
constructions.builtBuildings.add(providesFreeBuilding);
if (freeTechs != 0) UnCivGame.Current.civInfo.tech.freeTechs += freeTechs;
if("EmpireEntersGoldenAge".equals(unique)) CivilizationInfo.current().enterGoldenAge();
}
}

View File

@ -1,7 +1,10 @@
package com.unciv.models.gamebasics;
import com.unciv.civinfo.Unit;
import com.unciv.models.LinqHashMap;
import java.util.LinkedHashMap;
public class GameBasics{
public static LinqHashMap<String,Building> Buildings;
public static LinqHashMap<String,Terrain> Terrains;
@ -9,4 +12,5 @@ public class GameBasics{
public static LinqHashMap<String,TileImprovement> TileImprovements;
public static LinqHashMap<String, Technology> Technologies;
public static LinqHashMap<String, BasicHelp> Helps;
public static LinkedHashMap<String,Unit> Units;
}

View File

@ -0,0 +1,3 @@
package com.unciv.models.stats;
public interface INamed{public String getName();}

View File

@ -1,9 +1,10 @@
package com.unciv.models.stats;
public class NamedStats extends FullStats {
public class NamedStats extends FullStats implements INamed {
public String name;
public String GetName() {
public String getName() {
return name;
}