Fixes colony tile production so that the values matches the classic.

This commit is contained in:
Stian Grenborgen 2022-12-11 09:11:57 +01:00
parent 73fcf5cf13
commit d20dedb592
7 changed files with 106 additions and 30 deletions

View File

@ -144,6 +144,7 @@ public class Modifier extends Feature {
public static final int RESOURCE_PRODUCTION_INDEX = 10;
public static final int COLONY_PRODUCTION_INDEX = 20;
public static final int EXPERT_PRODUCTION_INDEX = 30;
public static final int COLONYTILE_PRODUCTION_INDEX = 35;
public static final int FATHER_PRODUCTION_INDEX = 40;
public static final int IMPROVEMENT_PRODUCTION_INDEX = 50;
public static final int AUTO_PRODUCTION_INDEX = 60;

View File

@ -1788,10 +1788,12 @@ public final class Tile extends UnitLocation implements Named, Ownable {
// Try applying all possible non-natural improvements.
final List<TileImprovementType> improvements
= getSpecification().getTileImprovementTypeList();
for (TileImprovementType ti : transform(improvements, ti ->
(!ti.isNatural() && ti.isTileTypeAllowed(tileType)
&& ti.getBonus(goodsType) > 0))) {
potential = ti.getProductionModifier(goodsType).applyTo(potential);
if (canProduce(goodsType, unitType)) {
for (TileImprovementType ti : transform(improvements, ti ->
(!ti.isNatural() && ti.isTileTypeAllowed(tileType)
&& ti.getBonus(goodsType) > 0))) {
potential = ti.getProductionModifier(goodsType).applyTo(potential);
}
}
return (int)potential;
}

View File

@ -580,17 +580,37 @@ public class TileImprovement extends TileItem {
* {@inheritDoc}
*/
@Override
public Stream<Modifier> getProductionModifiers(GoodsType goodsType,
UnitType unitType) {
public Stream<Modifier> getProductionModifiers(GoodsType goodsType, UnitType unitType) {
if (goodsType == null || !isComplete()) {
return Stream.<Modifier>empty();
}
final Specification spec = getSpecification();
Modifier m;
return (goodsType != null && isComplete()
&& !(/* unattended */ !isNatural() && unitType == null
if (!isNatural()
&& unitType == null
&& !goodsType.isFoodType()
&& spec.getBoolean(GameOptions.ONLY_NATURAL_IMPROVEMENTS))
&& (m = getProductionModifier(goodsType)) != null)
? Stream.of(m)
: Stream.<Modifier>empty();
&& spec.getBoolean(GameOptions.ONLY_NATURAL_IMPROVEMENTS)) {
return Stream.<Modifier>empty();
}
Modifier m = getProductionModifier(goodsType);
if (m == null) {
return Stream.<Modifier>empty();
}
if (unitType != null
&& unitType.getExpertProduction() != null
&& unitType.getExpertProduction().equals(goodsType)) {
final Stream<Modifier> expertMultiplicativeModifier = unitType.getModifiers(goodsType.getId(), tile.getType(), null)
.filter(mod -> mod.getType() == Modifier.ModifierType.MULTIPLICATIVE);
final float expertBonus = FeatureContainer.applyModifiers(m.getValue(), null, expertMultiplicativeModifier);
m = Modifier.makeModifier(m);
m.setValue(expertBonus);
}
return Stream.of(m);
}
/**

View File

@ -367,7 +367,7 @@ public class BuildingProductionCalculator {
return (unitType != null)
// With a unit, unit specific bonuses apply
? concat(buildingType.getModifiers(id, unitType, turn),
ProductionUtils.getRebelProductionModifiers(colonyProductionBonus, goodsType, buildingType),
ProductionUtils.getRebelProductionModifiersForBuilding(buildingType, colonyProductionBonus, goodsType, unitType),
buildingType.getCompetenceModifiers(id, unitType, turn),
(owner == null) ? null : owner.getModifiers(id, unitType, turn))
// With no unit, only the building-specific bonuses

View File

@ -3,9 +3,12 @@ package net.sf.freecol.common.model.production;
import java.util.stream.Stream;
import net.sf.freecol.common.model.BuildingType;
import net.sf.freecol.common.model.FeatureContainer;
import net.sf.freecol.common.model.GoodsType;
import net.sf.freecol.common.model.Modifier;
import net.sf.freecol.common.model.Specification;
import net.sf.freecol.common.model.Tile;
import net.sf.freecol.common.model.UnitType;
public final class ProductionUtils {
@ -16,21 +19,58 @@ public final class ProductionUtils {
* Gets the current production {@code Modifier}, which is
* generated from the current production bonus.
*
* @param colonyProductionBonus The production bonus in the colony.
* @param goodsType The {@code GoodsType} to produce.
* @param buildingType A {@code BuildingType} for getting a rebel factor. Use {@code null}
* for a tile.
* @param colonyProductionBonus The production bonus in the colony.
* @param goodsType The {@code GoodsType} to produce.
* @param unitType The unit that is working in the building
* @return A stream of suitable {@code Modifier}s.
*/
public static Stream<Modifier> getRebelProductionModifiers(int colonyProductionBonus,
GoodsType goodsType, BuildingType buildingType) {
public static Stream<Modifier> getRebelProductionModifiersForBuilding(BuildingType buildingType, int colonyProductionBonus, GoodsType goodsType, UnitType unitType) {
if (colonyProductionBonus == 0) {
return Stream.<Modifier>empty();
}
final float rebelFactor = (buildingType != null) ? buildingType.getRebelFactor() : 1.0F;
if (colonyProductionBonus == 0) return Stream.<Modifier>empty();
int bonus = (int)Math.floor(colonyProductionBonus * rebelFactor);
final int bonus = (int) Math.floor(colonyProductionBonus * rebelFactor);
Modifier mod = new Modifier(goodsType.getId(), bonus,
Modifier.ModifierType.ADDITIVE,
Specification.SOL_MODIFIER_SOURCE);
mod.setModifierIndex(Modifier.COLONY_PRODUCTION_INDEX);
return Stream.of(mod);
}
/**
* Gets the current production {@code Modifier}, which is
* generated from the current production bonus.
*
* @param colonyProductionBonus The production bonus in the colony.
* @param goodsType The {@code GoodsType} to produce.
* @param unitType The unit that is working on the tile.
* @return A stream of suitable {@code Modifier}s.
*/
public static Stream<Modifier> getRebelProductionModifiersForTile(Tile tile, int colonyProductionBonus, GoodsType goodsType, UnitType unitType) {
if (colonyProductionBonus == 0) {
return Stream.<Modifier>empty();
}
float rebelFactor = 1.0F;
if (unitType != null
&& unitType.getExpertProduction() != null
&& unitType.getExpertProduction().equals(goodsType)) {
rebelFactor *= 2.0F;
}
if (tile.getResource() != null) {
final Stream<Modifier> multiplicativeResourceModifiers = tile.getResource().getProductionModifiers(goodsType, unitType)
.filter(m -> m.getType() == Modifier.ModifierType.MULTIPLICATIVE);
rebelFactor = FeatureContainer.applyModifiers(rebelFactor, tile.getGame().getTurn(), multiplicativeResourceModifiers);
}
int bonus = (int) Math.max(colonyProductionBonus, Math.floor(colonyProductionBonus * rebelFactor));
Modifier mod = new Modifier(goodsType.getId(), bonus,
Modifier.ModifierType.ADDITIVE,
Specification.SOL_MODIFIER_SOURCE);
mod.setModifierIndex(Modifier.COLONYTILE_PRODUCTION_INDEX);
return Stream.of(mod);
}
}

View File

@ -179,10 +179,10 @@ public class TileProductionCalculator {
}
return concat(tile.getProductionModifiers(goodsType, unitType),
ProductionUtils.getRebelProductionModifiers(colonyProductionBonus, goodsType, null),
unitType.getModifiers(goodsType.getId(), tile.getType(), turn),
((owner == null) ? null
: owner.getModifiers(goodsType.getId(), unitType, turn)));
unitType.getModifiers(goodsType.getId(), tile.getType(), turn),
((owner == null) ? null
: owner.getModifiers(goodsType.getId(), unitType, turn)),
ProductionUtils.getRebelProductionModifiersForTile(tile, colonyProductionBonus, goodsType, unitType));
}
/**
@ -198,7 +198,7 @@ public class TileProductionCalculator {
return Stream.<Modifier>empty();
}
return concat(tile.getProductionModifiers(goodsType, null),
ProductionUtils.getRebelProductionModifiers(colonyProductionBonus, goodsType, null),
ProductionUtils.getRebelProductionModifiersForTile(tile, colonyProductionBonus, goodsType, null),
// This does not seem to influence center tile production, but was present in the old code.
//colony.getModifiers(id, null, turn),
((owner == null) ? null

View File

@ -23,9 +23,14 @@ import static net.sf.freecol.common.util.CollectionUtils.any;
import static net.sf.freecol.common.util.CollectionUtils.count;
import static net.sf.freecol.common.util.CollectionUtils.matchKeyEquals;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.sf.freecol.common.model.production.TileProductionCalculator;
import net.sf.freecol.common.model.production.WorkerAssignment;
import net.sf.freecol.util.test.FreeColTestCase;
import net.sf.freecol.util.test.FreeColTestUtils;
@ -330,7 +335,7 @@ public class TileTest extends FreeColTestCase {
tile2.getPotentialProduction(grain, colonistType));
assertEquals("Plains/grain/colonist max", 6,
tile2.getMaximumPotential(grain, colonistType));
assertEquals("Plains/grain/expertFarmer", 8,
assertEquals("Plains/grain/expertFarmer", 7,
tile2.getPotentialProduction(grain, expertFarmerType));
tile2.addResource(new Resource(game, tile2, grainResource));
assertEquals("Plains+Resource/grain", 7,
@ -341,9 +346,9 @@ public class TileTest extends FreeColTestCase {
tile2.getPotentialProduction(grain, colonistType));
assertEquals("Plains+Resource/grain/colonist max", 8,
tile2.getMaximumPotential(grain, colonistType));
assertEquals("Plains+Resource/grain/expertFarmer", 12,
assertEquals("Plains+Resource/grain/expertFarmer", 11,
tile2.getPotentialProduction(grain, expertFarmerType));
assertEquals("Plains+Resource/grain/expertFarmer max", 13,
assertEquals("Plains+Resource/grain/expertFarmer max", 12,
tile2.getMaximumPotential(grain, expertFarmerType));
Tile tile3 = new Tile(game, plainsForest, 1, 1);
@ -351,6 +356,14 @@ public class TileTest extends FreeColTestCase {
tile3.getPotentialProduction(grain, null));
assertEquals("Forest/grain max", 6,
tile3.getMaximumPotential(grain, null));
assertEquals("Forest/lumber/colonist", 6,
tile3.getPotentialProduction(lumber, colonistType));
assertEquals("Forest/lumber/colonist max", 8,
tile3.getMaximumPotential(lumber, colonistType));
assertEquals("Forest/lumber/expertLumberJack", 12,
tile3.getPotentialProduction(lumber, expertLumberJack));
assertEquals("Forest/lumber/expertLumberJack max", 14,
tile3.getMaximumPotential(lumber, expertLumberJack));
}
public void testIsTileTypeAllowed() {
@ -677,8 +690,8 @@ public class TileTest extends FreeColTestCase {
unit.setLocation(ct);
unit.changeWorkType(lumber);
int result = base * expertBonus;
if (t.hasRiver()) result += riverBonus;
if (t.hasRoad()) result += roadBonus;
if (t.hasRiver()) result += riverBonus * expertBonus;
if (t.hasRoad()) result += roadBonus * expertBonus;
if (t.hasResource()) result += resourceBonus * expertBonus;
assertEquals("Expert lumber production at tile " + i, result,
ct.getTotalProductionOf(lumber));