package games.strategy.triplea.delegate;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import games.strategy.debug.ClientLogger;
import games.strategy.engine.data.Change;
import games.strategy.engine.data.GameData;
import games.strategy.engine.data.NamedAttachable;
import games.strategy.engine.data.PlayerID;
import games.strategy.engine.data.ProductionFrontier;
import games.strategy.engine.data.TechnologyFrontier;
import games.strategy.engine.data.changefactory.ChangeFactory;
import games.strategy.engine.delegate.IDelegateBridge;
import games.strategy.triplea.attachments.TechAttachment;
import games.strategy.util.Tuple;
public abstract class TechAdvance extends NamedAttachable {
private static final long serialVersionUID = -1076712297024403156L;
private static final Class<?>[] preDefinedTechConstructorParameter = new Class<?>[] {GameData.class};
public static final String TECH_NAME_SUPER_SUBS = "Super subs";
public static final String TECH_PROPERTY_SUPER_SUBS = "superSub";
public static final String TECH_NAME_JET_POWER = "Jet Power";
public static final String TECH_PROPERTY_JET_POWER = "jetPower";
public static final String TECH_NAME_IMPROVED_SHIPYARDS = "Shipyards";
public static final String TECH_PROPERTY_IMPROVED_SHIPYARDS = "shipyards";
public static final String TECH_NAME_AA_RADAR = "AA Radar";
public static final String TECH_PROPERTY_AA_RADAR = "aARadar";
public static final String TECH_NAME_LONG_RANGE_AIRCRAFT = "Long Range Aircraft";
public static final String TECH_PROPERTY_LONG_RANGE_AIRCRAFT = "longRangeAir";
public static final String TECH_NAME_HEAVY_BOMBER = "Heavy Bomber";
public static final String TECH_PROPERTY_HEAVY_BOMBER = "heavyBomber";
public static final String TECH_NAME_IMPROVED_ARTILLERY_SUPPORT = "Improved Artillery Support";
public static final String TECH_PROPERTY_IMPROVED_ARTILLERY_SUPPORT = "improvedArtillerySupport";
public static final String TECH_NAME_ROCKETS = "Rockets Advance";
public static final String TECH_PROPERTY_ROCKETS = "rocket";
public static final String TECH_NAME_PARATROOPERS = "Paratroopers";
public static final String TECH_PROPERTY_PARATROOPERS = "paratroopers";
public static final String TECH_NAME_INCREASED_FACTORY_PRODUCTION = "Increased Factory Production";
public static final String TECH_PROPERTY_INCREASED_FACTORY_PRODUCTION = "increasedFactoryProduction";
public static final String TECH_NAME_WAR_BONDS = "War Bonds";
public static final String TECH_PROPERTY_WAR_BONDS = "warBonds";
public static final String TECH_NAME_MECHANIZED_INFANTRY = "Mechanized Infantry";
public static final String TECH_PROPERTY_MECHANIZED_INFANTRY = "mechanizedInfantry";
public static final String TECH_NAME_INDUSTRIAL_TECHNOLOGY = "Industrial Technology";
public static final String TECH_PROPERTY_INDUSTRIAL_TECHNOLOGY = "industrialTechnology";
public static final String TECH_NAME_DESTROYER_BOMBARD = "Destroyer Bombard";
public static final String TECH_PROPERTY_DESTROYER_BOMBARD = "destroyerBombard";
public static final List<String> s_allPreDefinedTechnologyNames = Collections.unmodifiableList(
Arrays.asList(TECH_NAME_SUPER_SUBS, TECH_NAME_JET_POWER, TECH_NAME_IMPROVED_SHIPYARDS, TECH_NAME_AA_RADAR,
TECH_NAME_LONG_RANGE_AIRCRAFT, TECH_NAME_HEAVY_BOMBER, TECH_NAME_IMPROVED_ARTILLERY_SUPPORT,
TECH_NAME_ROCKETS, TECH_NAME_PARATROOPERS, TECH_NAME_INCREASED_FACTORY_PRODUCTION, TECH_NAME_WAR_BONDS,
TECH_NAME_MECHANIZED_INFANTRY, TECH_NAME_INDUSTRIAL_TECHNOLOGY, TECH_NAME_DESTROYER_BOMBARD));
private static final Map<String, Class<? extends TechAdvance>> s_allPreDefinedTechnologies =
createPreDefinedTechnologyMap();
private static Map<String, Class<? extends TechAdvance>> createPreDefinedTechnologyMap() {
final HashMap<String, Class<? extends TechAdvance>> preDefinedTechMap =
new HashMap<>();
preDefinedTechMap.put(TECH_PROPERTY_SUPER_SUBS, SuperSubsAdvance.class);
preDefinedTechMap.put(TECH_PROPERTY_JET_POWER, JetPowerAdvance.class);
preDefinedTechMap.put(TECH_PROPERTY_IMPROVED_SHIPYARDS, ImprovedShipyardsAdvance.class);
preDefinedTechMap.put(TECH_PROPERTY_AA_RADAR, AARadarAdvance.class);
preDefinedTechMap.put(TECH_PROPERTY_LONG_RANGE_AIRCRAFT, LongRangeAircraftAdvance.class);
preDefinedTechMap.put(TECH_PROPERTY_HEAVY_BOMBER, HeavyBomberAdvance.class);
preDefinedTechMap.put(TECH_PROPERTY_IMPROVED_ARTILLERY_SUPPORT, ImprovedArtillerySupportAdvance.class);
preDefinedTechMap.put(TECH_PROPERTY_ROCKETS, RocketsAdvance.class);
preDefinedTechMap.put(TECH_PROPERTY_PARATROOPERS, ParatroopersAdvance.class);
preDefinedTechMap.put(TECH_PROPERTY_INCREASED_FACTORY_PRODUCTION, IncreasedFactoryProductionAdvance.class);
preDefinedTechMap.put(TECH_PROPERTY_WAR_BONDS, WarBondsAdvance.class);
preDefinedTechMap.put(TECH_PROPERTY_MECHANIZED_INFANTRY, MechanizedInfantryAdvance.class);
preDefinedTechMap.put(TECH_PROPERTY_INDUSTRIAL_TECHNOLOGY, IndustrialTechnologyAdvance.class);
preDefinedTechMap.put(TECH_PROPERTY_DESTROYER_BOMBARD, DestroyerBombardTechAdvance.class);
return Collections.unmodifiableMap(preDefinedTechMap);
}
public TechAdvance(final String name, final GameData data) {
super(name, data);
}
public abstract String getProperty();
public abstract void perform(PlayerID id, IDelegateBridge bridge);
public abstract boolean hasTech(TechAttachment ta);
private static void createWW2V1Advances(final TechnologyFrontier tf) {
tf.addAdvance(new JetPowerAdvance(tf.getData()));
tf.addAdvance(new SuperSubsAdvance(tf.getData()));
tf.addAdvance(new LongRangeAircraftAdvance(tf.getData()));
tf.addAdvance(new RocketsAdvance(tf.getData()));
tf.addAdvance(new IndustrialTechnologyAdvance(tf.getData()));
tf.addAdvance(new HeavyBomberAdvance(tf.getData()));
}
private static void createWW2V2Advances(final TechnologyFrontier tf) {
tf.addAdvance(new JetPowerAdvance(tf.getData()));
tf.addAdvance(new SuperSubsAdvance(tf.getData()));
tf.addAdvance(new LongRangeAircraftAdvance(tf.getData()));
tf.addAdvance(new RocketsAdvance(tf.getData()));
tf.addAdvance(new DestroyerBombardTechAdvance(tf.getData()));
tf.addAdvance(new HeavyBomberAdvance(tf.getData()));
// tf.addAdvance(new IndustrialTechnologyAdvance(tf.getData()));
}
private static void createWW2V3Advances(final TechnologyFrontier tf) {
tf.addAdvance(new SuperSubsAdvance(tf.getData()));
tf.addAdvance(new JetPowerAdvance(tf.getData()));
tf.addAdvance(new ImprovedShipyardsAdvance(tf.getData()));
tf.addAdvance(new AARadarAdvance(tf.getData()));
tf.addAdvance(new LongRangeAircraftAdvance(tf.getData()));
tf.addAdvance(new HeavyBomberAdvance(tf.getData()));
tf.addAdvance(new ImprovedArtillerySupportAdvance(tf.getData()));
tf.addAdvance(new RocketsAdvance(tf.getData()));
tf.addAdvance(new ParatroopersAdvance(tf.getData()));
tf.addAdvance(new IncreasedFactoryProductionAdvance(tf.getData()));
tf.addAdvance(new WarBondsAdvance(tf.getData()));
tf.addAdvance(new MechanizedInfantryAdvance(tf.getData()));
}
/**
* For the game parser only.
*/
public static void createDefaultTechAdvances(final GameData data) {
final TechnologyFrontier tf = data.getTechnologyFrontier();
final boolean ww2v2 = games.strategy.triplea.Properties.getWW2V2(data);
final boolean ww2v3 = games.strategy.triplea.Properties.getWW2V3(data);
if (ww2v2) {
createWW2V2Advances(tf);
} else if (ww2v3) {
createWW2V3Advances(tf);
} else {
createWW2V1Advances(tf);
}
// now create player tech frontiers
final List<TechnologyFrontier> frontiers = new ArrayList<>();
if (ww2v3) {
final TechnologyFrontier an = new TechnologyFrontier("Air and Naval Advances", data);
final TechnologyFrontier lp = new TechnologyFrontier("Land and Production Advances", data);
final Tuple<List<TechAdvance>, List<TechAdvance>> ww2v3advances = getWW2v3CategoriesWithTheirAdvances(data);
an.addAdvance(ww2v3advances.getFirst());
lp.addAdvance(ww2v3advances.getSecond());
frontiers.add(an);
frontiers.add(lp);
} else {
final TechnologyFrontier tas = new TechnologyFrontier("Technology Advances", data);
tas.addAdvance(new ArrayList<>(tf.getTechs()));
frontiers.add(tas);
}
// add the frontiers
for (final PlayerID player : data.getPlayerList().getPlayers()) {
for (final TechnologyFrontier frontier : frontiers) {
player.getTechnologyFrontierList().addTechnologyFrontier(new TechnologyFrontier(frontier));
}
}
}
public static TechAdvance findDefinedAdvanceAndCreateAdvance(final String s, final GameData data) {
final Class<? extends TechAdvance> clazz = s_allPreDefinedTechnologies.get(s);
if (clazz == null) {
throw new IllegalArgumentException(s + " is not a valid technology");
}
final TechAdvance ta;
Constructor<? extends TechAdvance> constructor;
try {
constructor = clazz.getConstructor(preDefinedTechConstructorParameter);
ta = constructor.newInstance(data);
} catch (final Exception e) {
ClientLogger.logQuietly(e);
throw new IllegalStateException(s + " is not a valid technology or could not be instantiated");
}
if (ta == null) {
throw new IllegalStateException(s + " is not a valid technology or could not be instantiated");
}
return ta;
}
public static TechAdvance findAdvance(final String propertyString, final GameData data, final PlayerID player) {
for (final TechAdvance t : getTechAdvances(data, player)) {
if (t.getProperty().equals(propertyString)) {
return t;
}
}
throw new IllegalArgumentException(propertyString + " is not a valid technology");
}
/**
* @return first is air&naval, second is land&production.
*/
private static Tuple<List<TechAdvance>, List<TechAdvance>> getWW2v3CategoriesWithTheirAdvances(final GameData data) {
List<TechAdvance> allAdvances;
data.acquireReadLock();
try {
allAdvances = new ArrayList<>(data.getTechnologyFrontier().getTechs());
} finally {
data.releaseReadLock();
}
final List<TechAdvance> airAndNaval = new ArrayList<>();
final List<TechAdvance> landAndProduction = new ArrayList<>();
for (final TechAdvance ta : allAdvances) {
final String propertyString = ta.getProperty();
if (propertyString.equals(TECH_PROPERTY_SUPER_SUBS) || propertyString.equals(TECH_PROPERTY_JET_POWER)
|| propertyString.equals(TECH_PROPERTY_IMPROVED_SHIPYARDS) || propertyString.equals(TECH_PROPERTY_AA_RADAR)
|| propertyString.equals(TECH_PROPERTY_LONG_RANGE_AIRCRAFT)
|| propertyString.equals(TECH_PROPERTY_HEAVY_BOMBER)) {
airAndNaval.add(ta);
} else if (propertyString.equals(TECH_PROPERTY_IMPROVED_ARTILLERY_SUPPORT)
|| propertyString.equals(TECH_PROPERTY_ROCKETS) || propertyString.equals(TECH_PROPERTY_PARATROOPERS)
|| propertyString.equals(TECH_PROPERTY_INCREASED_FACTORY_PRODUCTION)
|| propertyString.equals(TECH_PROPERTY_WAR_BONDS)
|| propertyString.equals(TECH_PROPERTY_MECHANIZED_INFANTRY)) {
landAndProduction.add(ta);
} else {
throw new IllegalStateException(
"We should not be using ww2v3 categories if we have custom techs: " + propertyString);
}
}
return Tuple.of(airAndNaval, landAndProduction);
}
/**
* Returns all tech advances possible in this game.
*/
public static List<TechAdvance> getTechAdvances(final GameData data) {
return getTechAdvances(data, null);
}
/**
* Returns all tech advances that this player can possibly research. (Or if Player is null, returns all techs
* available in the game).
*/
public static List<TechAdvance> getTechAdvances(final GameData data, final PlayerID player) {
final TechnologyFrontier technologyFrontier = data.getTechnologyFrontier();
if (technologyFrontier != null && !technologyFrontier.isEmpty()) {
if (player != null) {
return player.getTechnologyFrontierList().getAdvances();
} else {
return technologyFrontier.getTechs();
}
}
// the game has no techs, just return empty list
return new ArrayList<>();
}
/**
* Returns all possible tech categories for this player.
*/
public static List<TechnologyFrontier> getPlayerTechCategories(final GameData data, final PlayerID player) {
if (player != null) {
return player.getTechnologyFrontierList().getFrontiers();
}
throw new IllegalStateException("Player cannot be null");
}
@Override
public boolean equals(final Object o) {
if (o == null || !(o instanceof TechAdvance)) {
return false;
}
final TechAdvance other = (TechAdvance) o;
if (other.getName() == null || getName() == null) {
return false;
}
return getName().equals(other.getName());
}
@Override
public int hashCode() {
return getName().hashCode();
}
@Override
public String toString() {
return getName();
}
}
class SuperSubsAdvance extends TechAdvance {
private static final long serialVersionUID = -5469354766630425933L;
public SuperSubsAdvance(final GameData data) {
super(TECH_NAME_SUPER_SUBS, data);
}
@Override
public String getProperty() {
return TECH_PROPERTY_SUPER_SUBS;
}
@Override
public void perform(final PlayerID id, final IDelegateBridge bridge) {}
@Override
public boolean hasTech(final TechAttachment ta) {
return ta.getSuperSub();
}
}
class HeavyBomberAdvance extends TechAdvance {
private static final long serialVersionUID = -1743063539572684675L;
public HeavyBomberAdvance(final GameData data) {
super(TECH_NAME_HEAVY_BOMBER, data);
}
@Override
public String getProperty() {
return TECH_PROPERTY_HEAVY_BOMBER;
}
@Override
public void perform(final PlayerID id, final IDelegateBridge bridge) {}
@Override
public boolean hasTech(final TechAttachment ta) {
return ta.getHeavyBomber();
}
}
class IndustrialTechnologyAdvance extends TechAdvance {
private static final long serialVersionUID = -21252592806022090L;
public IndustrialTechnologyAdvance(final GameData data) {
super(TECH_NAME_INDUSTRIAL_TECHNOLOGY, data);
}
@Override
public String getProperty() {
return TECH_PROPERTY_INDUSTRIAL_TECHNOLOGY;
}
@Override
public void perform(final PlayerID id, final IDelegateBridge bridge) {
final ProductionFrontier current = id.getProductionFrontier();
// they already have it
if (current.getName().endsWith("IndustrialTechnology")) {
return;
}
final String industrialTechName = current.getName() + "IndustrialTechnology";
final ProductionFrontier advancedTech =
bridge.getData().getProductionFrontierList().getProductionFrontier(industrialTechName);
// it doesnt exist, dont crash
if (advancedTech == null) {
Logger.getLogger(TechAdvance.class.getName()).log(Level.WARNING,
"No tech named:" + industrialTechName + " not adding tech");
return;
}
final Change prodChange = ChangeFactory.changeProductionFrontier(id, advancedTech);
bridge.addChange(prodChange);
}
@Override
public boolean hasTech(final TechAttachment ta) {
return ta.getIndustrialTechnology();
}
}
class JetPowerAdvance extends TechAdvance {
private static final long serialVersionUID = -9124162661008361132L;
public JetPowerAdvance(final GameData data) {
super(TECH_NAME_JET_POWER, data);
}
@Override
public String getProperty() {
return TECH_PROPERTY_JET_POWER;
}
@Override
public void perform(final PlayerID id, final IDelegateBridge bridge) {}
@Override
public boolean hasTech(final TechAttachment ta) {
return ta.getJetPower();
}
}
class RocketsAdvance extends TechAdvance {
private static final long serialVersionUID = 1526117896586201770L;
public RocketsAdvance(final GameData data) {
super(TECH_NAME_ROCKETS, data);
}
@Override
public String getProperty() {
return TECH_PROPERTY_ROCKETS;
}
@Override
public void perform(final PlayerID id, final IDelegateBridge bridge) {}
@Override
public boolean hasTech(final TechAttachment ta) {
return ta.getRocket();
}
}
class DestroyerBombardTechAdvance extends TechAdvance {
private static final long serialVersionUID = -4977423636387126617L;
public DestroyerBombardTechAdvance(final GameData data) {
super(TECH_NAME_DESTROYER_BOMBARD, data);
}
@Override
public String getProperty() {
return TECH_PROPERTY_DESTROYER_BOMBARD;
}
@Override
public void perform(final PlayerID id, final IDelegateBridge bridge) {}
@Override
public boolean hasTech(final TechAttachment ta) {
return ta.getDestroyerBombard();
}
}
class LongRangeAircraftAdvance extends TechAdvance {
private static final long serialVersionUID = 1986380888336238652L;
public LongRangeAircraftAdvance(final GameData data) {
super(TECH_NAME_LONG_RANGE_AIRCRAFT, data);
}
@Override
public String getProperty() {
return TECH_PROPERTY_LONG_RANGE_AIRCRAFT;
}
@Override
public void perform(final PlayerID id, final IDelegateBridge bridge) {}
@Override
public boolean hasTech(final TechAttachment ta) {
return ta.getLongRangeAir();
}
}
// Beginning of AA 50 rules
/*
* Artillery can support multiple infantry
*/
class ImprovedArtillerySupportAdvance extends TechAdvance {
private static final long serialVersionUID = 3946378995070209879L;
public ImprovedArtillerySupportAdvance(final GameData data) {
super(TECH_NAME_IMPROVED_ARTILLERY_SUPPORT, data);
}
@Override
public String getProperty() {
return TECH_PROPERTY_IMPROVED_ARTILLERY_SUPPORT;
}
@Override
public void perform(final PlayerID id, final IDelegateBridge bridge) {}
@Override
public boolean hasTech(final TechAttachment ta) {
return ta.getImprovedArtillerySupport();
}
}
/*
* Support paratroops
*/
class ParatroopersAdvance extends TechAdvance {
private static final long serialVersionUID = 1457384348499672184L;
public ParatroopersAdvance(final GameData data) {
super(TECH_NAME_PARATROOPERS, data);
}
@Override
public String getProperty() {
return TECH_PROPERTY_PARATROOPERS;
}
@Override
public void perform(final PlayerID id, final IDelegateBridge bridge) {}
@Override
public boolean hasTech(final TechAttachment ta) {
return ta.getParatroopers();
}
}
/*
* Increased Factory Production
*/
class IncreasedFactoryProductionAdvance extends TechAdvance {
private static final long serialVersionUID = 987606878563485763L;
public IncreasedFactoryProductionAdvance(final GameData data) {
super(TECH_NAME_INCREASED_FACTORY_PRODUCTION, data);
}
@Override
public String getProperty() {
return TECH_PROPERTY_INCREASED_FACTORY_PRODUCTION;
}
@Override
public void perform(final PlayerID id, final IDelegateBridge bridge) {}
@Override
public boolean hasTech(final TechAttachment ta) {
return ta.getIncreasedFactoryProduction();
}
}
/*
* War Bonds
*/
class WarBondsAdvance extends TechAdvance {
private static final long serialVersionUID = -9048146216351059811L;
public WarBondsAdvance(final GameData data) {
super(TECH_NAME_WAR_BONDS, data);
}
@Override
public String getProperty() {
return TECH_PROPERTY_WAR_BONDS;
}
@Override
public void perform(final PlayerID id, final IDelegateBridge bridge) {}
@Override
public boolean hasTech(final TechAttachment ta) {
return ta.getWarBonds();
}
}
/*
* Mechanized Infantry
*/
class MechanizedInfantryAdvance extends TechAdvance {
private static final long serialVersionUID = 3040670614877450791L;
public MechanizedInfantryAdvance(final GameData data) {
super(TECH_NAME_MECHANIZED_INFANTRY, data);
}
@Override
public String getProperty() {
return TECH_PROPERTY_MECHANIZED_INFANTRY;
}
@Override
public void perform(final PlayerID id, final IDelegateBridge bridge) {}
@Override
public boolean hasTech(final TechAttachment ta) {
return ta.getMechanizedInfantry();
}
}
/*
* AA Radar
*/
class AARadarAdvance extends TechAdvance {
private static final long serialVersionUID = 6464021231625252901L;
public AARadarAdvance(final GameData data) {
super(TECH_NAME_AA_RADAR, data);
}
@Override
public String getProperty() {
return TECH_PROPERTY_AA_RADAR;
}
@Override
public void perform(final PlayerID id, final IDelegateBridge bridge) {}
@Override
public boolean hasTech(final TechAttachment ta) {
return ta.getAARadar();
}
}
class ImprovedShipyardsAdvance extends TechAdvance {
private static final long serialVersionUID = 7613381831727736711L;
public ImprovedShipyardsAdvance(final GameData data) {
super(TECH_NAME_IMPROVED_SHIPYARDS, data);
}
@Override
public String getProperty() {
return TECH_PROPERTY_IMPROVED_SHIPYARDS;
}
@Override
public void perform(final PlayerID id, final IDelegateBridge bridge) {
final GameData data = bridge.getData();
if (!games.strategy.triplea.Properties.getUse_Shipyards(data)) {
return;
}
final ProductionFrontier current = id.getProductionFrontier();
// they already have it
if (current.getName().endsWith("Shipyards")) {
return;
}
final String industrialTechName = current.getName() + "Shipyards";
final ProductionFrontier advancedTech = data.getProductionFrontierList().getProductionFrontier(industrialTechName);
// it doesnt exist, dont crash
if (advancedTech == null) {
Logger.getLogger(TechAdvance.class.getName()).log(Level.WARNING,
"No tech named:" + industrialTechName + " not adding tech");
return;
}
final Change prodChange = ChangeFactory.changeProductionFrontier(id, advancedTech);
bridge.addChange(prodChange);
}
@Override
public boolean hasTech(final TechAttachment ta) {
return ta.getShipyards();
}
}
// End of AA 50 rules