mirror of
https://github.com/yairm210/Unciv.git
synced 2025-09-22 02:42:16 -04:00
Split CityInfo into areas of concern - separate files for Stats, Expansion and Population
This commit is contained in:
parent
3bb849726a
commit
a992dff6fe
@ -122,7 +122,7 @@ public class CityConstructions
|
||||
public boolean evaluate(String arg0) {
|
||||
return "SciencePer2Pop".equals(arg0);
|
||||
}
|
||||
}) * getCity().population/2; // Library and public school unique (not actualy unique, though...hmm)
|
||||
}) * getCity().population.population/2; // Library and public school unique (not actualy unique, though...hmm)
|
||||
return stats;
|
||||
}
|
||||
|
||||
@ -153,7 +153,7 @@ public class CityConstructions
|
||||
|
||||
float workLeft = cost - workDone(constructionName); // needs to be float so that we get the cieling properly ;)
|
||||
|
||||
FullStats cityStats = getCity().cityStats;
|
||||
FullStats cityStats = getCity().cityStats.currentCityStats;
|
||||
int production = Math.round(cityStats.production);
|
||||
if (constructionName.equals(Settler)) production += cityStats.food;
|
||||
|
||||
@ -164,7 +164,7 @@ public class CityConstructions
|
||||
CivilizationInfo.current().gold -= getConstruction(buildingName).getGoldCost();
|
||||
getConstruction(buildingName).postBuildEvent(this);
|
||||
if(currentConstruction.equals(buildingName)) chooseNextConstruction();
|
||||
getCity().updateCityStats();
|
||||
getCity().cityStats.update();
|
||||
}
|
||||
|
||||
public String getCityProductionTextForCityButton(){
|
||||
|
65
core/src/com/unciv/logic/city/CityExpansionManager.java
Normal file
65
core/src/com/unciv/logic/city/CityExpansionManager.java
Normal file
@ -0,0 +1,65 @@
|
||||
package com.unciv.logic.city;
|
||||
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
import com.badlogic.gdx.utils.Predicate;
|
||||
import com.unciv.logic.civilization.CivilizationInfo;
|
||||
import com.unciv.logic.map.TileInfo;
|
||||
import com.unciv.models.linq.Linq;
|
||||
import com.unciv.ui.UnCivGame;
|
||||
|
||||
public class CityExpansionManager{
|
||||
|
||||
transient public CityInfo cityInfo;
|
||||
public int cultureStored;
|
||||
private int tilesClaimed;
|
||||
|
||||
private void addNewTile(){
|
||||
cultureStored -= getCultureToNextTile();
|
||||
tilesClaimed++;
|
||||
Linq<Vector2> possibleNewTileVectors = new Linq<Vector2>();
|
||||
|
||||
for (int i = 2; i <4 ; i++) {
|
||||
Linq<TileInfo> tiles = CivilizationInfo.current().tileMap.getTilesInDistance(cityInfo.cityLocation,i);
|
||||
tiles = tiles.where(new Predicate<TileInfo>() {
|
||||
@Override
|
||||
public boolean evaluate(TileInfo arg0) {
|
||||
return arg0.owner == null;
|
||||
}
|
||||
});
|
||||
if(tiles.size()==0) continue;
|
||||
|
||||
TileInfo TileChosen=null;
|
||||
double TileChosenRank=0;
|
||||
for(TileInfo tile : tiles){
|
||||
double rank = cityInfo.rankTile(tile);
|
||||
if(rank>TileChosenRank){
|
||||
TileChosenRank = rank;
|
||||
TileChosen = tile;
|
||||
}
|
||||
}
|
||||
TileChosen.owner = UnCivGame.Current.civInfo.civName;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public int getCultureToNextTile(){
|
||||
// This one has conflicting sources -
|
||||
// http://civilization.wikia.com/wiki/Mathematics_of_Civilization_V says it's 20+(10(t-1))^1.1
|
||||
// https://www.reddit.com/r/civ/comments/58rxkk/how_in_gods_name_do_borders_expand_in_civ_vi/ has it
|
||||
// (per game XML files) at 6*(t+0.4813)^1.3
|
||||
// The second seems to be more based, so I'll go with that
|
||||
double a = 6*Math.pow(tilesClaimed+1.4813,1.3);
|
||||
if(CivilizationInfo.current().getBuildingUniques().contains("NewTileCostReduction")) a *= 0.75; //Speciality of Angkor Wat
|
||||
if(CivilizationInfo.current().policies.isAdopted("Tradition")) a *= 0.75;
|
||||
return (int)Math.round(a);
|
||||
}
|
||||
|
||||
public void nextTurn(float culture) {
|
||||
|
||||
cultureStored+=culture;
|
||||
if(cultureStored>=getCultureToNextTile()){
|
||||
addNewTile();
|
||||
CivilizationInfo.current().addNotification(cityInfo.name+" has expanded its borders!",cityInfo.cityLocation);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,7 +9,6 @@ import com.unciv.logic.map.TileMap;
|
||||
import com.unciv.ui.UnCivGame;
|
||||
import com.unciv.models.linq.Linq;
|
||||
import com.unciv.models.linq.LinqCounter;
|
||||
import com.unciv.models.linq.LinqHashMap;
|
||||
import com.unciv.models.gamebasics.Building;
|
||||
import com.unciv.models.gamebasics.GameBasics;
|
||||
import com.unciv.models.gamebasics.TileResource;
|
||||
@ -19,14 +18,10 @@ public class CityInfo {
|
||||
public final Vector2 cityLocation;
|
||||
public String name;
|
||||
|
||||
public PopulationManager population = new PopulationManager();
|
||||
public CityConstructions cityConstructions;
|
||||
public int cultureStored;
|
||||
private int tilesClaimed;
|
||||
public int population = 1;
|
||||
public int foodStored = 0;
|
||||
public LinqHashMap<String,FullStats> buildingsSpecialists = new LinqHashMap<String, FullStats>();
|
||||
|
||||
public FullStats cityStats; // This is so we won't have to calculate this multiple times - takes a lot of time, especially on phones!
|
||||
public CityExpansionManager expansion = new CityExpansionManager();
|
||||
public CityStats cityStats = new CityStats();
|
||||
|
||||
private TileMap getTileMap(){return UnCivGame.Current.civInfo.tileMap; }
|
||||
|
||||
@ -48,19 +43,14 @@ public class CityInfo {
|
||||
cityLocation = Vector2.Zero;
|
||||
} // for json parsing, we need to have a default constructor
|
||||
|
||||
public int getCultureToNextTile(){
|
||||
// This one has conflicting sources -
|
||||
// http://civilization.wikia.com/wiki/Mathematics_of_Civilization_V says it's 20+(10(t-1))^1.1
|
||||
// https://www.reddit.com/r/civ/comments/58rxkk/how_in_gods_name_do_borders_expand_in_civ_vi/ has it
|
||||
// (per game XML files) at 6*(t+0.4813)^1.3
|
||||
// The second seems to be more based, so I'll go with that
|
||||
double a = 6*Math.pow(tilesClaimed+1.4813,1.3);
|
||||
if(CivilizationInfo.current().getBuildingUniques().contains("NewTileCostReduction")) a *= 0.75; //Speciality of Angkor Wat
|
||||
if(CivilizationInfo.current().policies.isAdopted("Tradition")) a *= 0.75;
|
||||
return (int)Math.round(a);
|
||||
}
|
||||
|
||||
|
||||
public CityInfo(CivilizationInfo civInfo, Vector2 cityLocation) {
|
||||
|
||||
population.cityInfo = this;
|
||||
expansion.cityInfo = this;
|
||||
cityStats.cityInfo = this;
|
||||
|
||||
name = CityNames[civInfo.cities.size()];
|
||||
this.cityLocation = cityLocation;
|
||||
civInfo.cities.add(this);
|
||||
@ -83,7 +73,7 @@ public class CityInfo {
|
||||
tile.terrainFeature=null;
|
||||
|
||||
autoAssignWorker();
|
||||
updateCityStats();
|
||||
cityStats.update();
|
||||
}
|
||||
|
||||
public LinqCounter<TileResource> getCityResources(){
|
||||
@ -105,231 +95,20 @@ public class CityInfo {
|
||||
}
|
||||
|
||||
|
||||
public FullStats getSpecialists(){
|
||||
FullStats allSpecialists = new FullStats();
|
||||
for(FullStats stats : buildingsSpecialists.values())
|
||||
allSpecialists.add(stats);
|
||||
return allSpecialists;
|
||||
}
|
||||
|
||||
public int getNumberOfSpecialists(){
|
||||
FullStats specialists = getSpecialists();
|
||||
return (int) (specialists.science+specialists.production+specialists.culture+specialists.gold);
|
||||
}
|
||||
|
||||
public int getFreePopulation() {
|
||||
int workingPopulation = getTilesInRange().count(new Predicate<TileInfo>() {
|
||||
@Override
|
||||
public boolean evaluate(TileInfo arg0) {
|
||||
return name.equals(arg0.workingCity);
|
||||
}
|
||||
})-1; // 1 is the city center
|
||||
return population - workingPopulation - getNumberOfSpecialists();
|
||||
}
|
||||
|
||||
public boolean hasNonWorkingPopulation() {
|
||||
return getFreePopulation() > 0;
|
||||
}
|
||||
|
||||
public void updateCityStats() {
|
||||
CivilizationInfo civInfo = CivilizationInfo.current();
|
||||
FullStats stats = new FullStats();
|
||||
stats.science += population;
|
||||
|
||||
// Working ppl
|
||||
for (TileInfo cell : getTilesInRange())
|
||||
if (name.equals(cell.workingCity))
|
||||
stats.add(cell.getTileStats(this));
|
||||
|
||||
// Specialists
|
||||
FullStats specialists = getSpecialists();
|
||||
stats.culture+=specialists.culture*3;
|
||||
stats.production+=specialists.production*2;
|
||||
stats.science+=specialists.science*3;
|
||||
stats.gold+=specialists.gold*2;
|
||||
if(civInfo.policies.isAdopted("Commerce Complete")) stats.gold+=getNumberOfSpecialists();
|
||||
if(civInfo.policies.isAdopted("Secularism")) stats.science+=getNumberOfSpecialists()*2;
|
||||
|
||||
//idle ppl
|
||||
stats.production += getFreePopulation();
|
||||
|
||||
if(!isCapital() && isConnectedToCapital(RoadStatus.Road)) {
|
||||
// Calculated by http://civilization.wikia.com/wiki/Trade_route_(Civ5)
|
||||
double goldFromTradeRoute = civInfo.getCapital().population * 0.15
|
||||
+ population * 1.1 - 1;
|
||||
if(civInfo.policies.isAdopted("Trade Unions")) goldFromTradeRoute+=2;
|
||||
if(civInfo.getBuildingUniques().contains("TradeRouteGoldIncrease")) goldFromTradeRoute*=1.25; // Machu Pichu speciality
|
||||
stats.gold += goldFromTradeRoute;
|
||||
}
|
||||
|
||||
stats.add(cityConstructions.getStats());
|
||||
if(civInfo.policies.isAdopted("Tradition") && isCapital())
|
||||
stats.culture+=3;
|
||||
if(civInfo.policies.isAdopted("Landed Elite") && isCapital())
|
||||
stats.food+=2;
|
||||
if(CivilizationInfo.current().policies.isAdopted("Tradition Complete"))
|
||||
stats.food+=2;
|
||||
if(CivilizationInfo.current().policies.isAdopted("Monarchy") && isCapital())
|
||||
stats.gold+=population/2;
|
||||
if(CivilizationInfo.current().policies.isAdopted("Liberty"))
|
||||
stats.culture+=1;
|
||||
if(CivilizationInfo.current().policies.isAdopted("Republic"))
|
||||
stats.production+=1;
|
||||
if(CivilizationInfo.current().policies.isAdopted("Universal Suffrage"))
|
||||
stats.production+=population/5;
|
||||
if(CivilizationInfo.current().policies.isAdopted("Free Speech"))
|
||||
stats.culture+=population/2;
|
||||
|
||||
FullStats statPercentBonuses = cityConstructions.getStatPercentBonuses();
|
||||
if( civInfo.tech.isResearched ("Combustion") &&
|
||||
(isCapital() || isConnectedToCapital(RoadStatus.Railroad))) statPercentBonuses.production += 25;
|
||||
if(civInfo.goldenAges.isGoldenAge()) statPercentBonuses.production+=20;
|
||||
IConstruction currentConstruction = cityConstructions.getCurrentConstruction();
|
||||
if(currentConstruction instanceof Building && ((Building)currentConstruction).isWonder){
|
||||
if(civInfo.getCivResources().containsKey(GameBasics.TileResources.get("Marble")))
|
||||
statPercentBonuses.production+=15;
|
||||
if(civInfo.policies.isAdopted("Aristocracy"))
|
||||
statPercentBonuses.production+=15;
|
||||
}
|
||||
|
||||
if(civInfo.tech.isResearched("Computers")){
|
||||
statPercentBonuses.production+=10;
|
||||
statPercentBonuses.science+=10;
|
||||
}
|
||||
|
||||
if(civInfo.policies.isAdopted("Collective Rule") && isCapital()
|
||||
&& "Settler".equals(cityConstructions.currentConstruction))
|
||||
statPercentBonuses.production+=50;
|
||||
if(civInfo.policies.isAdopted("Republic") && currentConstruction instanceof Building)
|
||||
statPercentBonuses.production+=5;
|
||||
if(civInfo.policies.isAdopted("Reformation") && cityConstructions.builtBuildings.any(new Predicate<String>() {
|
||||
@Override
|
||||
public boolean evaluate(String arg0) {
|
||||
return GameBasics.Buildings.get(arg0).isWonder;
|
||||
}
|
||||
}))
|
||||
statPercentBonuses.culture+=33;
|
||||
if(civInfo.policies.isAdopted("Commerce") && isCapital())
|
||||
statPercentBonuses.gold+=25;
|
||||
if(civInfo.policies.isAdopted("Sovereignty") && civInfo.getHappinessForNextTurn() >= 0)
|
||||
statPercentBonuses.science+=15;
|
||||
|
||||
stats.production*=1+statPercentBonuses.production/100; // So they get bonuses for production and gold/science
|
||||
if("Gold".equals(cityConstructions.currentConstruction)) stats.gold+=stats.production/4;
|
||||
if("Science".equals(cityConstructions.currentConstruction)) {
|
||||
float scienceProduced=stats.production/4;
|
||||
if (civInfo.getBuildingUniques().contains("ScienceConversionIncrease"))
|
||||
scienceProduced*=1.33;
|
||||
if(civInfo.policies.isAdopted("Rationalism")) scienceProduced*=1.33;
|
||||
stats.science += scienceProduced;
|
||||
}
|
||||
|
||||
stats.gold*=1+statPercentBonuses.gold/100;
|
||||
stats.science*=1+statPercentBonuses.science/100;
|
||||
stats.culture*=1+statPercentBonuses.culture/100;
|
||||
|
||||
boolean isUnhappy = civInfo.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(CivilizationInfo.current().policies.isAdopted("Civil Society"))
|
||||
stats.food+=getNumberOfSpecialists();
|
||||
|
||||
if(isUnhappy) stats.food /= 4; // Reduce excess food to 1/4 per the same
|
||||
if(civInfo.policies.isAdopted("Landed Elite") && isCapital())
|
||||
stats.food*=1.1;
|
||||
if(CivilizationInfo.current().policies.isAdopted("Tradition Complete"))
|
||||
stats.food*=1.15;
|
||||
|
||||
stats.gold-= cityConstructions.getMaintainanceCosts(); // this is AFTER the bonus calculation!
|
||||
this.cityStats = stats;
|
||||
}
|
||||
|
||||
public float getCityHappiness(){ // needs to be a separate function because we need to know the global happiness state
|
||||
CivilizationInfo civInfo = CivilizationInfo.current();
|
||||
// in order to determine how much food is produced in a city!
|
||||
float happiness = -3; // -3 happiness per city
|
||||
float unhappinessFromCitizens = population;
|
||||
if(civInfo.policies.isAdopted("Democracy")) unhappinessFromCitizens-=getNumberOfSpecialists()*0.5f;
|
||||
if(civInfo.getBuildingUniques().contains("CitizenUnhappinessDecreased"))
|
||||
unhappinessFromCitizens*=0.9;
|
||||
if(civInfo.policies.isAdopted("Aristocracy"))
|
||||
unhappinessFromCitizens*=0.95;
|
||||
happiness-=unhappinessFromCitizens;
|
||||
|
||||
if(civInfo.policies.isAdopted("Aristocracy"))
|
||||
happiness+=population/10;
|
||||
if(civInfo.policies.isAdopted("Monarchy") && isCapital())
|
||||
happiness+=population/2;
|
||||
if(civInfo.policies.isAdopted("Meritocracy") && isConnectedToCapital(RoadStatus.Road))
|
||||
happiness+=1;
|
||||
|
||||
happiness+=(int) cityConstructions.getStats().happiness;
|
||||
|
||||
return happiness;
|
||||
}
|
||||
|
||||
public void nextTurn() {
|
||||
FullStats stats = cityStats;
|
||||
FullStats stats = cityStats.currentCityStats;
|
||||
if (cityConstructions.currentConstruction.equals(CityConstructions.Settler) && stats.food > 0) {
|
||||
stats.production += stats.food;
|
||||
stats.food = 0;
|
||||
}
|
||||
|
||||
foodStored += stats.food;
|
||||
if (foodStored < 0) // starvation!
|
||||
{
|
||||
population--;
|
||||
foodStored = 0;
|
||||
CivilizationInfo.current().addNotification(name+" is starving!",cityLocation);
|
||||
}
|
||||
if (foodStored >= foodToNextPopulation()) // growth!
|
||||
{
|
||||
foodStored -= foodToNextPopulation();
|
||||
if(getBuildingUniques().contains("FoodCarriesOver")) foodStored+=0.4f*foodToNextPopulation(); // Aqueduct special
|
||||
population++;
|
||||
autoAssignWorker();
|
||||
CivilizationInfo.current().addNotification(name+" has grown!",cityLocation);
|
||||
}
|
||||
|
||||
population.nextTurn(stats.food);
|
||||
cityConstructions.nextTurn(stats);
|
||||
|
||||
cultureStored+=stats.culture;
|
||||
if(cultureStored>=getCultureToNextTile()){
|
||||
addNewTile();
|
||||
CivilizationInfo.current().addNotification(name+" has expanded its borders!",cityLocation);
|
||||
}
|
||||
expansion.nextTurn(stats.culture);
|
||||
}
|
||||
|
||||
private void addNewTile(){
|
||||
cultureStored -= getCultureToNextTile();
|
||||
tilesClaimed++;
|
||||
Linq<Vector2> possibleNewTileVectors = new Linq<Vector2>();
|
||||
|
||||
for (int i = 2; i <4 ; i++) {
|
||||
Linq<TileInfo> tiles = getTileMap().getTilesInDistance(cityLocation,i);
|
||||
tiles = tiles.where(new Predicate<TileInfo>() {
|
||||
@Override
|
||||
public boolean evaluate(TileInfo arg0) {
|
||||
return arg0.owner == null;
|
||||
}
|
||||
});
|
||||
if(tiles.size()==0) continue;
|
||||
|
||||
TileInfo TileChosen=null;
|
||||
double TileChosenRank=0;
|
||||
for(TileInfo tile : tiles){
|
||||
double rank = rankTile(tile);
|
||||
if(rank>TileChosenRank){
|
||||
TileChosenRank = rank;
|
||||
TileChosen = tile;
|
||||
}
|
||||
}
|
||||
TileChosen.owner = UnCivGame.Current.civInfo.civName;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void autoAssignWorker() {
|
||||
void autoAssignWorker() {
|
||||
double maxValue = 0;
|
||||
TileInfo toWork = null;
|
||||
for (TileInfo tileInfo : getTilesInRange()) {
|
||||
@ -345,7 +124,7 @@ public class CityInfo {
|
||||
toWork.workingCity = name;
|
||||
}
|
||||
|
||||
private double rankTile(TileInfo tile){
|
||||
double rankTile(TileInfo tile){
|
||||
FullStats stats = tile.getTileStats(this);
|
||||
double rank=0;
|
||||
if(stats.food <2) rank+=stats.food;
|
||||
@ -358,9 +137,9 @@ public class CityInfo {
|
||||
return rank;
|
||||
}
|
||||
|
||||
private boolean isCapital(){ return CivilizationInfo.current().getCapital() == this; }
|
||||
boolean isCapital(){ return CivilizationInfo.current().getCapital() == this; }
|
||||
|
||||
private boolean isConnectedToCapital(RoadStatus roadType){
|
||||
boolean isConnectedToCapital(RoadStatus roadType){
|
||||
if(CivilizationInfo.current().getCapital()==null) return false;// first city!
|
||||
TileInfo capitalTile = CivilizationInfo.current().getCapital().getTile();
|
||||
Linq<TileInfo> tilesReached = new Linq<TileInfo>();
|
||||
@ -382,12 +161,6 @@ public class CityInfo {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int foodToNextPopulation()
|
||||
{
|
||||
// civ v math,civilization.wikia
|
||||
return 15 + 6 * (population - 1) + (int)Math.floor(Math.pow(population - 1, 1.8f));
|
||||
}
|
||||
|
||||
public Linq<String> getBuildingUniques(){
|
||||
return cityConstructions.getBuiltBuildings().select(new Linq.Func<Building, String>() {
|
||||
@Override
|
||||
@ -403,7 +176,7 @@ public class CityInfo {
|
||||
}
|
||||
|
||||
public FullStats getGreatPersonPoints(){
|
||||
FullStats greatPersonPoints = getSpecialists().multiply(3);
|
||||
FullStats greatPersonPoints = population.getSpecialists().multiply(3);
|
||||
CivilizationInfo civInfo = CivilizationInfo.current();
|
||||
|
||||
for(Building building : cityConstructions.getBuiltBuildings())
|
||||
@ -420,8 +193,4 @@ public class CityInfo {
|
||||
|
||||
return greatPersonPoints;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
244
core/src/com/unciv/logic/city/CityStats.java
Normal file
244
core/src/com/unciv/logic/city/CityStats.java
Normal file
@ -0,0 +1,244 @@
|
||||
package com.unciv.logic.city;
|
||||
|
||||
import com.badlogic.gdx.utils.Predicate;
|
||||
import com.unciv.logic.civilization.CivilizationInfo;
|
||||
import com.unciv.logic.civilization.PolicyManager;
|
||||
import com.unciv.logic.map.RoadStatus;
|
||||
import com.unciv.logic.map.TileInfo;
|
||||
import com.unciv.models.gamebasics.Building;
|
||||
import com.unciv.models.gamebasics.GameBasics;
|
||||
import com.unciv.models.stats.FullStats;
|
||||
|
||||
/**
|
||||
* Created by LENOVO on 1/13/2018.
|
||||
*/
|
||||
|
||||
public class CityStats{
|
||||
|
||||
public FullStats currentCityStats; // This is so we won't have to calculate this multiple times - takes a lot of time, especially on phones!
|
||||
|
||||
public transient CityInfo cityInfo;
|
||||
|
||||
|
||||
private FullStats getStatsFromTiles(){
|
||||
FullStats stats = new FullStats();
|
||||
for (TileInfo cell : cityInfo.getTilesInRange())
|
||||
if (cityInfo.name.equals(cell.workingCity))
|
||||
stats.add(cell.getTileStats(cityInfo));
|
||||
return stats;
|
||||
}
|
||||
|
||||
private FullStats getStatsFromSpecialists(){
|
||||
FullStats stats = new FullStats();
|
||||
|
||||
// Specialists
|
||||
FullStats specialists = cityInfo.population.getSpecialists();
|
||||
stats.culture+=specialists.culture*3;
|
||||
stats.production+=specialists.production*2;
|
||||
stats.science+=specialists.science*3;
|
||||
stats.gold+=specialists.gold*2;
|
||||
int numOfSpecialists = cityInfo.population.getNumberOfSpecialists();
|
||||
if(CivilizationInfo.current().policies.isAdopted("Commerce Complete")) stats.gold+=numOfSpecialists;
|
||||
if(CivilizationInfo.current().policies.isAdopted("Secularism")) stats.science+=numOfSpecialists*2;
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
private FullStats getStatsFromTradeRoute(){
|
||||
FullStats stats = new FullStats();
|
||||
if(!cityInfo.isCapital() && cityInfo.isConnectedToCapital(RoadStatus.Road)) {
|
||||
CivilizationInfo civInfo = CivilizationInfo.current();
|
||||
// Calculated by http://civilization.wikia.com/wiki/Trade_route_(Civ5)
|
||||
double goldFromTradeRoute = civInfo.getCapital().population.population * 0.15
|
||||
+ cityInfo.population.population * 1.1 - 1;
|
||||
if(civInfo.policies.isAdopted("Trade Unions")) goldFromTradeRoute+=2;
|
||||
if(civInfo.getBuildingUniques().contains("TradeRouteGoldIncrease")) goldFromTradeRoute*=1.25; // Machu Pichu speciality
|
||||
stats.gold += goldFromTradeRoute;
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
private FullStats getStatsFromPolicies(){
|
||||
FullStats stats = new FullStats();
|
||||
PolicyManager policies = CivilizationInfo.current().policies;
|
||||
|
||||
if(policies.isAdopted("Tradition") && cityInfo.isCapital())
|
||||
stats.culture+=3;
|
||||
if(policies.isAdopted("Landed Elite") && cityInfo.isCapital())
|
||||
stats.food+=2;
|
||||
if(policies.isAdopted("Tradition Complete"))
|
||||
stats.food+=2;
|
||||
if(policies.isAdopted("Monarchy") && cityInfo.isCapital())
|
||||
stats.gold+=cityInfo.population.population/2;
|
||||
if(policies.isAdopted("Liberty"))
|
||||
stats.culture+=1;
|
||||
if(policies.isAdopted("Republic"))
|
||||
stats.production+=1;
|
||||
if(policies.isAdopted("Universal Suffrage"))
|
||||
stats.production+=cityInfo.population.population/5;
|
||||
if(policies.isAdopted("Free Speech"))
|
||||
stats.culture+=cityInfo.population.population/2;
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
|
||||
private FullStats getStatsFromProduction(){
|
||||
FullStats stats = new FullStats();
|
||||
|
||||
if("Gold".equals(cityInfo.cityConstructions.currentConstruction)) stats.gold+=stats.production/4;
|
||||
if("Science".equals(cityInfo.cityConstructions.currentConstruction)) {
|
||||
float scienceProduced=stats.production/4;
|
||||
if (CivilizationInfo.current().getBuildingUniques().contains("ScienceConversionIncrease"))
|
||||
scienceProduced*=1.33;
|
||||
if(CivilizationInfo.current().policies.isAdopted("Rationalism")) scienceProduced*=1.33;
|
||||
stats.science += scienceProduced;
|
||||
}
|
||||
return stats;
|
||||
}
|
||||
|
||||
|
||||
private FullStats getStatPercentBonusesFromRailroad(){
|
||||
FullStats stats = new FullStats();
|
||||
if( CivilizationInfo.current().tech.isResearched ("Combustion") &&
|
||||
(cityInfo.isCapital() || cityInfo.isConnectedToCapital(RoadStatus.Railroad)))
|
||||
stats.production += 25;
|
||||
return stats;
|
||||
}
|
||||
|
||||
private FullStats getStatPercentBonusesFromGoldenAge(){
|
||||
FullStats stats = new FullStats();
|
||||
if(CivilizationInfo.current().goldenAges.isGoldenAge())
|
||||
stats.production+=20;
|
||||
return stats;
|
||||
}
|
||||
|
||||
private FullStats getStatPercentBonusesFromPolicies(){
|
||||
FullStats stats = new FullStats();
|
||||
PolicyManager policies = CivilizationInfo.current().policies;
|
||||
|
||||
CityConstructions cityConstructions = cityInfo.cityConstructions;
|
||||
if(policies.isAdopted("Collective Rule") && cityInfo.isCapital()
|
||||
&& "Settler".equals(cityConstructions.currentConstruction))
|
||||
stats.production+=50;
|
||||
if(policies.isAdopted("Republic") && cityConstructions.getCurrentConstruction() instanceof Building)
|
||||
stats.production+=5;
|
||||
if(policies.isAdopted("Reformation") && cityConstructions.builtBuildings.any(new Predicate<String>() {
|
||||
@Override
|
||||
public boolean evaluate(String arg0) {
|
||||
return GameBasics.Buildings.get(arg0).isWonder;
|
||||
}
|
||||
}))
|
||||
stats.culture+=33;
|
||||
if(policies.isAdopted("Commerce") && cityInfo.isCapital())
|
||||
stats.gold+=25;
|
||||
if(policies.isAdopted("Sovereignty") && CivilizationInfo.current().getHappinessForNextTurn() >= 0)
|
||||
stats.science+=15;
|
||||
if(policies.isAdopted("Aristocracy")
|
||||
&& cityConstructions.getCurrentConstruction() instanceof Building
|
||||
&& ((Building)cityConstructions.getCurrentConstruction()).isWonder)
|
||||
stats.production+=15;
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
private FullStats getStatPercentBonusesFromMarble() {
|
||||
FullStats stats = new FullStats();
|
||||
IConstruction construction = cityInfo.cityConstructions.getCurrentConstruction();
|
||||
|
||||
if (construction instanceof Building
|
||||
&& ((Building) construction).isWonder
|
||||
&& CivilizationInfo.current().getCivResources().containsKey(GameBasics.TileResources.get("Marble")))
|
||||
stats.production += 15;
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
private FullStats getStatPercentBonusesFromComputers() {
|
||||
FullStats stats = new FullStats();
|
||||
|
||||
if (CivilizationInfo.current().tech.isResearched("Computers")) {
|
||||
stats.production += 10;
|
||||
stats.science += 10;
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
private float getGrowthBonusFromPolicies(){
|
||||
float bonus = 0;
|
||||
if(CivilizationInfo.current().policies.isAdopted("Landed Elite") && cityInfo.isCapital())
|
||||
bonus+=0.1;
|
||||
if(CivilizationInfo.current().policies.isAdopted("Tradition Complete"))
|
||||
bonus+=0.15;
|
||||
return bonus;
|
||||
}
|
||||
|
||||
|
||||
public void update() {
|
||||
CivilizationInfo civInfo = CivilizationInfo.current();
|
||||
|
||||
FullStats stats = new FullStats();
|
||||
stats.science += cityInfo.population.population;
|
||||
stats.add(getStatsFromTiles());
|
||||
stats.add(getStatsFromSpecialists());
|
||||
stats.production += cityInfo.population.getFreePopulation();
|
||||
stats.add(getStatsFromTradeRoute());
|
||||
stats.add(cityInfo.cityConstructions.getStats());
|
||||
stats.add(getStatsFromPolicies());
|
||||
|
||||
FullStats statPercentBonuses = cityInfo.cityConstructions.getStatPercentBonuses();
|
||||
statPercentBonuses.add(getStatPercentBonusesFromGoldenAge());
|
||||
statPercentBonuses.add(getStatPercentBonusesFromPolicies());
|
||||
statPercentBonuses.add(getStatPercentBonusesFromRailroad());
|
||||
statPercentBonuses.add(getStatPercentBonusesFromMarble());
|
||||
statPercentBonuses.add(getStatPercentBonusesFromComputers());
|
||||
|
||||
stats.production*=1+statPercentBonuses.production/100; // So they get bonuses for production and gold/science
|
||||
|
||||
stats.add(getStatsFromProduction());
|
||||
|
||||
|
||||
stats.gold*=1+statPercentBonuses.gold/100;
|
||||
stats.science*=1+statPercentBonuses.science/100;
|
||||
stats.culture*=1+statPercentBonuses.culture/100;
|
||||
|
||||
boolean isUnhappy = civInfo.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 -= cityInfo.population.population * 2; // Food reduced after the bonus
|
||||
if(CivilizationInfo.current().policies.isAdopted("Civil Society"))
|
||||
stats.food+=cityInfo.population.getNumberOfSpecialists();
|
||||
|
||||
if(isUnhappy) stats.food /= 4; // Reduce excess food to 1/4 per the same
|
||||
stats.food *= (1+getGrowthBonusFromPolicies());
|
||||
|
||||
stats.gold-= cityInfo.cityConstructions.getMaintainanceCosts(); // this is AFTER the bonus calculation!
|
||||
this.currentCityStats = stats;
|
||||
}
|
||||
|
||||
public float getCityHappiness(){ // needs to be a separate function because we need to know the global happiness state
|
||||
CivilizationInfo civInfo = CivilizationInfo.current();
|
||||
// in order to determine how much food is produced in a city!
|
||||
float happiness = -3; // -3 happiness per city
|
||||
float unhappinessFromCitizens = cityInfo.population.population;
|
||||
if(civInfo.policies.isAdopted("Democracy"))
|
||||
unhappinessFromCitizens -= cityInfo.population.getNumberOfSpecialists()*0.5f;
|
||||
if(civInfo.getBuildingUniques().contains("CitizenUnhappinessDecreased"))
|
||||
unhappinessFromCitizens*=0.9;
|
||||
if(civInfo.policies.isAdopted("Aristocracy"))
|
||||
unhappinessFromCitizens*=0.95;
|
||||
happiness-=unhappinessFromCitizens;
|
||||
|
||||
if(civInfo.policies.isAdopted("Aristocracy"))
|
||||
happiness+=cityInfo.population.population/10;
|
||||
if(civInfo.policies.isAdopted("Monarchy") && cityInfo.isCapital())
|
||||
happiness+=cityInfo.population.population/2;
|
||||
if(civInfo.policies.isAdopted("Meritocracy") && cityInfo.isConnectedToCapital(RoadStatus.Road))
|
||||
happiness+=1;
|
||||
|
||||
happiness+=(int) cityInfo.cityConstructions.getStats().happiness;
|
||||
|
||||
return happiness;
|
||||
}
|
||||
|
||||
}
|
65
core/src/com/unciv/logic/city/PopulationManager.java
Normal file
65
core/src/com/unciv/logic/city/PopulationManager.java
Normal file
@ -0,0 +1,65 @@
|
||||
package com.unciv.logic.city;
|
||||
|
||||
import com.badlogic.gdx.utils.Predicate;
|
||||
import com.unciv.logic.civilization.CivilizationInfo;
|
||||
import com.unciv.logic.map.TileInfo;
|
||||
import com.unciv.models.linq.LinqHashMap;
|
||||
import com.unciv.models.stats.FullStats;
|
||||
|
||||
public class PopulationManager {
|
||||
|
||||
transient public CityInfo cityInfo;
|
||||
public int population = 1;
|
||||
public int foodStored = 0;
|
||||
|
||||
public LinqHashMap<String,FullStats> buildingsSpecialists = new LinqHashMap<String, FullStats>();
|
||||
|
||||
public FullStats getSpecialists(){
|
||||
FullStats allSpecialists = new FullStats();
|
||||
for(FullStats stats : buildingsSpecialists.values())
|
||||
allSpecialists.add(stats);
|
||||
return allSpecialists;
|
||||
}
|
||||
|
||||
public int getNumberOfSpecialists(){
|
||||
FullStats specialists = getSpecialists();
|
||||
return (int) (specialists.science+specialists.production+specialists.culture+specialists.gold);
|
||||
}
|
||||
|
||||
|
||||
public int foodToNextPopulation()
|
||||
{
|
||||
// civ v math,civilization.wikia
|
||||
return 15 + 6 * (population - 1) + (int)Math.floor(Math.pow(population - 1, 1.8f));
|
||||
}
|
||||
|
||||
|
||||
public int getFreePopulation() {
|
||||
int workingPopulation = cityInfo.getTilesInRange().count(new Predicate<TileInfo>() {
|
||||
@Override
|
||||
public boolean evaluate(TileInfo arg0) {
|
||||
return cityInfo.name.equals(arg0.workingCity);
|
||||
}
|
||||
})-1; // 1 is the city center
|
||||
return population - workingPopulation - getNumberOfSpecialists();
|
||||
}
|
||||
|
||||
public void nextTurn(float food) {
|
||||
|
||||
foodStored += food;
|
||||
if (foodStored < 0) // starvation!
|
||||
{
|
||||
population--;
|
||||
foodStored = 0;
|
||||
CivilizationInfo.current().addNotification(cityInfo.name+" is starving!",cityInfo.cityLocation);
|
||||
}
|
||||
if (foodStored >= foodToNextPopulation()) // growth!
|
||||
{
|
||||
foodStored -= foodToNextPopulation();
|
||||
if(cityInfo.getBuildingUniques().contains("FoodCarriesOver")) foodStored+=0.4f*foodToNextPopulation(); // Aqueduct special
|
||||
population++;
|
||||
cityInfo.autoAssignWorker();
|
||||
CivilizationInfo.current().addNotification(cityInfo.name+" has grown!",cityInfo.cityLocation);
|
||||
}
|
||||
}
|
||||
}
|
@ -109,14 +109,14 @@ public class CivilizationInfo {
|
||||
|
||||
goldenAges.nextTurn(happiness);
|
||||
|
||||
for (CityInfo city : cities) city.updateCityStats();
|
||||
for (CityInfo city : cities) city.cityStats.update();
|
||||
turns++;
|
||||
}
|
||||
|
||||
public CivStats getStatsForNextTurn() {
|
||||
CivStats statsForTurn = new CivStats();
|
||||
for (CityInfo city : cities) {
|
||||
statsForTurn.add(city.cityStats);
|
||||
statsForTurn.add(city.cityStats.currentCityStats);
|
||||
}
|
||||
statsForTurn.happiness=0;
|
||||
|
||||
@ -145,7 +145,7 @@ public class CivilizationInfo {
|
||||
}
|
||||
}) * happinessPerUniqueLuxury;
|
||||
for (CityInfo city : cities) {
|
||||
happiness += city.getCityHappiness();
|
||||
happiness += city.cityStats.getCityHappiness();
|
||||
}
|
||||
if(getBuildingUniques().contains("HappinessPerSocialPolicy"))
|
||||
happiness+=policies.getAdoptedPolicies().count(new Predicate<String>() {
|
||||
|
@ -119,15 +119,15 @@ public class CityScreen extends CameraStageBaseScreen {
|
||||
specialist.addListener(new ClickListener(){
|
||||
@Override
|
||||
public void clicked(InputEvent event, float x, float y) {
|
||||
if(isFilled) getCity().buildingsSpecialists.get(building).add(specialistType.minus()); //unassign
|
||||
else if(getCity().getFreePopulation()==0) return;
|
||||
if(isFilled) getCity().population.buildingsSpecialists.get(building).add(specialistType.minus()); //unassign
|
||||
else if(getCity().population.getFreePopulation()==0) return;
|
||||
else {
|
||||
if(!getCity().buildingsSpecialists.containsKey(building))
|
||||
getCity().buildingsSpecialists.put(building,new FullStats());
|
||||
getCity().buildingsSpecialists.get(building).add(specialistType); //assign!}
|
||||
if(!getCity().population.buildingsSpecialists.containsKey(building))
|
||||
getCity().population.buildingsSpecialists.put(building,new FullStats());
|
||||
getCity().population.buildingsSpecialists.get(building).add(specialistType); //assign!}
|
||||
}
|
||||
|
||||
getCity().updateCityStats();
|
||||
getCity().cityStats.update();
|
||||
update();
|
||||
}
|
||||
});
|
||||
@ -162,9 +162,9 @@ public class CityScreen extends CameraStageBaseScreen {
|
||||
BuildingsTable.add(new Label(building.name, skin)).pad(5);
|
||||
Table specialists = new Table();
|
||||
specialists.row().size(20).pad(5);
|
||||
if (!getCity().buildingsSpecialists.containsKey(building.name))
|
||||
getCity().buildingsSpecialists.put(building.name, new FullStats());
|
||||
FullStats currentBuildingSpecialists = getCity().buildingsSpecialists.get(building.name);
|
||||
if (!getCity().population.buildingsSpecialists.containsKey(building.name))
|
||||
getCity().population.buildingsSpecialists.put(building.name, new FullStats());
|
||||
FullStats currentBuildingSpecialists = getCity().population.buildingsSpecialists.get(building.name);
|
||||
for (int i = 0; i < building.specialistSlots.production; i++) {
|
||||
specialists.add(getSpecialistIcon("StatIcons/populationBrown.png", building.name,
|
||||
currentBuildingSpecialists.production > i, new FullStats() {{
|
||||
@ -281,9 +281,9 @@ public class CityScreen extends CameraStageBaseScreen {
|
||||
group.populationImage.addListener(new ClickListener() {
|
||||
@Override
|
||||
public void clicked(InputEvent event, float x, float y) {
|
||||
if(tileInfo.workingCity ==null && cityInfo.getFreePopulation() > 0) tileInfo.workingCity = cityInfo.name;
|
||||
if(tileInfo.workingCity ==null && cityInfo.population.getFreePopulation() > 0) tileInfo.workingCity = cityInfo.name;
|
||||
else if(cityInfo.name.equals(tileInfo.workingCity)) tileInfo.workingCity = null;
|
||||
cityInfo.updateCityStats();
|
||||
cityInfo.cityStats.update();
|
||||
update();
|
||||
}
|
||||
});
|
||||
@ -329,7 +329,7 @@ public class CityScreen extends CameraStageBaseScreen {
|
||||
|
||||
private void updateCityTable() {
|
||||
final CityInfo cityInfo = getCity();
|
||||
FullStats stats = cityInfo.cityStats;
|
||||
FullStats stats = cityInfo.cityStats.currentCityStats;
|
||||
CityStatsTable.pad(20);
|
||||
CityStatsTable.columnDefaults(0).padRight(10);
|
||||
CityStatsTable.clear();
|
||||
@ -344,12 +344,12 @@ public class CityScreen extends CameraStageBaseScreen {
|
||||
CityStatsValues.put("Production",Math.round(stats.production)
|
||||
+cityInfo.cityConstructions.getAmountConstructedText());
|
||||
CityStatsValues.put("Food",Math.round(stats.food)
|
||||
+" ("+cityInfo.foodStored+"/"+cityInfo.foodToNextPopulation()+")");
|
||||
+" ("+cityInfo.population.foodStored+"/"+cityInfo.population.foodToNextPopulation()+")");
|
||||
CityStatsValues.put("Gold",Math.round(stats.gold) +"");
|
||||
CityStatsValues.put("Science",Math.round(stats.science) +"");
|
||||
CityStatsValues.put("Culture",Math.round(stats.culture)
|
||||
+" ("+cityInfo.cultureStored+"/"+cityInfo.getCultureToNextTile()+")");
|
||||
CityStatsValues.put("Population",cityInfo.getFreePopulation()+"/"+cityInfo.population);
|
||||
+" ("+cityInfo.expansion.cultureStored+"/"+cityInfo.expansion.getCultureToNextTile()+")");
|
||||
CityStatsValues.put("Population",cityInfo.population.getFreePopulation()+"/"+cityInfo.population.population);
|
||||
|
||||
for(String key : CityStatsValues.keySet()){
|
||||
CityStatsTable.add(com.unciv.ui.utils.ImageGetter.getStatIcon(key)).align(Align.right);
|
||||
|
@ -20,6 +20,7 @@ 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.ui.utils.GameSaver;
|
||||
|
||||
public class UnCivGame extends Game {
|
||||
|
||||
@ -31,11 +32,11 @@ public class UnCivGame extends Game {
|
||||
public void create() {
|
||||
SetupGameBasics();
|
||||
Current = this;
|
||||
if(com.unciv.ui.utils.GameSaver.GetSave("Autosave").exists()) {
|
||||
if(GameSaver.GetSave("Autosave").exists()) {
|
||||
try {
|
||||
com.unciv.ui.utils.GameSaver.LoadGame(this, "Autosave");
|
||||
GameSaver.LoadGame(this, "Autosave");
|
||||
for (CityInfo city : this.civInfo.cities) {
|
||||
if(city.cityStats == null) city.updateCityStats();
|
||||
if(city.cityStats == null) city.cityStats.update();
|
||||
}
|
||||
} catch(Exception ex){ // silent fail if we can't read the autosave
|
||||
startNewGame();
|
||||
|
@ -615,7 +615,7 @@ public class WorldScreen extends CameraStageBaseScreen {
|
||||
new ClickListener() {
|
||||
@Override
|
||||
public void clicked(InputEvent event, float x, float y) {
|
||||
selectedTile.getCity().cityConstructions.addConstruction(300 + (30 * selectedTile.getCity().population)); //http://civilization.wikia.com/wiki/Great_engineer_(Civ5)
|
||||
selectedTile.getCity().cityConstructions.addConstruction(300 + (30 * selectedTile.getCity().population.population)); //http://civilization.wikia.com/wiki/Great_engineer_(Civ5)
|
||||
selectedTile.unit = null; // destroy!
|
||||
update();
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ public class WorldTileGroup extends TileGroup {
|
||||
setZIndex(getParent().getChildren().size); // so this tile is rendered over neighboring tiles
|
||||
}
|
||||
|
||||
String cityButtonText = city.name +" ("+city.population+")";
|
||||
String cityButtonText = city.name +" ("+city.population.population+")";
|
||||
TextButton button = cityButton.getActor();
|
||||
button.setText(cityButtonText);
|
||||
button.setSize(button.getPrefWidth(), button.getPrefHeight());
|
||||
|
@ -42,7 +42,7 @@ public class ConstructionPickerScreen extends PickerScreen {
|
||||
@Override
|
||||
public void clicked(InputEvent event, float x, float y) {
|
||||
game.civInfo.getCurrentCity().cityConstructions.currentConstruction = selectedProduction;
|
||||
game.civInfo.getCurrentCity().updateCityStats(); // Because maybe we set/removed the science or gold production options.
|
||||
game.civInfo.getCurrentCity().cityStats.update(); // Because maybe we set/removed the science or gold production options.
|
||||
game.setScreen(new CityScreen());
|
||||
dispose();
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package com.unciv.ui.utils;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.utils.Json;
|
||||
import com.unciv.logic.city.CityInfo;
|
||||
import com.unciv.logic.civilization.CivilizationInfo;
|
||||
import com.unciv.ui.UnCivGame;
|
||||
|
||||
@ -19,5 +20,10 @@ public class GameSaver {
|
||||
|
||||
public static void LoadGame(UnCivGame game, String GameName) {
|
||||
game.civInfo = new Json().fromJson(CivilizationInfo.class, GetSave(GameName).readString());
|
||||
for (CityInfo cityInfo : game.civInfo.cities){
|
||||
cityInfo.population.cityInfo = cityInfo;
|
||||
cityInfo.expansion.cityInfo = cityInfo;
|
||||
cityInfo.cityStats.cityInfo = cityInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user