One solution is to simulate bit fields using EnumSets. An EnumSet uses bit-wise arithmetic for operations that involve the whole set, which means it is very fast. If you are using a language that does not have these features, you could implement something like this using regular bit fields.
Implementation 1
First, have an enum like this:
public enum TechType { NONE, VEGETATION, STRAWBERRIES, BIODOME, LIVESTOCK, BIOMASS; }
Then you can implement the individual Tech classes:
public class VegetationTech implements Tech {
public TechType type = TechType.VEGETATION;
public static Set<TechType> prerequisites = EnumSet.of(TechType.NONE);
@Override
public Set getPrerequisites() {
return prerequisites;
}
@Override
public TechType getType() {
return type;
}
}
public class BiomassTech implements Tech {
public TechType type = TechType.BIOMASS;
public static Set<TechType> prerequisites = EnumSet.of(TechType.VEGETATION,
TechType.LIVESTOCK);
@Override
public Set getPrerequisites() {
return prerequisites;
}
@Override
public TechType getType() {
return type;
}
}
Then set up your Player class to check the tech tree for prerequisites before adding a tech or whatever you want:
public class Player {
public Set<TechType> currentTechs;
public Player() {
currentTechs = EnumSet.of(TechType.NONE);
}
public boolean addTech(Tech tech) {
if (currentTechs.containsAll(tech.getPrerequisites())) {
currentTechs.add(tech.getType());
return true;
}
return false;
}
}
Implementation 2
Use case: when techs do not have to be dynamic class objects.
public enum Tech {
NONE (null, 0),
VEGETATION (EnumSet.of(NONE), 1),
LIVESTOCK (EnumSet.of(NONE), 1),
STRAWBERRIES (EnumSet.of(VEGETATION), 3),
BIODOME (EnumSet.of(STRAWBERRIES), 5),
BIOMASS (EnumSet.of(VEGETATION,
LIVESTOCK), 4);
private final Set<Tech> prerequisites;
private final int turnsRequired;
Tech(Set<Tech> prerequisites, int turnsRequired) {
this.prerequisites = prerequisites;
this.turnsRequired = turnsRequired;
}
public Set<Tech> prerequisites() {
return prerequisites;
}
public int turnsRequired() {
return turnsRequired;
}
}
Here you can add as many parameters as you want, such as a thumbnail icon and description string or whatever.
Then you could schedule research jobs through the Player class:
public class Player {
private Set<Tech> techsKnown;
public Player() {
techsKnown = EnumSet.of(Tech.NONE);
}
public boolean researchTech(Tech tech) {
if (techsKnown.containsAll(tech.prerequisites())) {
addResearchJob(tech, tech.turnsRequired());
return true;
}
return false;
}
...
public void researchJobDone(Tech tech) {
techsKnown.add(tech);
}
}
A benefit to this is you could easily combine the methods into a generic "Constructable/Researchable" interface and have the Tech enum and Building class implement it. Then the player could "startWorkOn" either polymorphically.