Compare commits

...
12 Commits
Author SHA1 Message Date
Mysaa 6410de196f Refactored Powers, moved packages, addded CastedPower 2024-11-02 19:30:13 +01:00
Mysaa acab58d0a3 Removing runtime old classes 2024-11-02 00:43:35 +01:00
Mysaa 0bcc1e3591 Added command metadata 2024-11-02 00:39:17 +01:00
Mysaa 0686ab99c5 Added link with website and tampermonkey script 2024-11-01 23:23:15 +01:00
Mysaa fa7d0362c7 Added Town Objectives 2024-10-31 16:23:49 +01:00
Mysaa 6bc9d4df9d Des bouts de code faits ... ouais 2024-10-20 19:55:36 +02:00
Mysaa 26755f9ecd A bit of fight simulation 2024-08-24 17:18:57 +02:00
Mysaa 6e2f916927 Added offencive frontend 2024-07-09 19:06:30 +02:00
Mysaa 8454e3a711 Added effects to simulator 2024-07-08 13:51:18 +02:00
Mysaa b783361264 Serialized powers for the simulatoxr 2024-07-08 13:07:10 +02:00
Mysaa f9bc9e4fa5 Added research, removed old code 2024-07-07 03:47:04 +02:00
Mysaa a08c6e1c64 More OOP classes 2024-07-06 02:48:35 +02:00
94 changed files with 5231 additions and 1837 deletions
+2 -1
View File
@@ -25,9 +25,10 @@ dependencies {
developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.yaml:snakeyaml:2.2'
implementation 'org.ojalgo:ojalgo:54.0.0'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1'
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.17.1'
implementation 'org.json:json:20240303'
}
tasks.named('test') {
@@ -4,117 +4,29 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import com.bernard.greposimu.engine.Registerar;
import com.bernard.greposimu.engine.game.Buildings;
import com.bernard.greposimu.engine.json.MapJsonDeserializer;
import com.bernard.greposimu.model.Dieu;
import com.bernard.greposimu.model.Heros;
import com.bernard.greposimu.model.OffDefStats;
import com.bernard.greposimu.model.game.GameData;
import com.bernard.greposimu.model.game.Power;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.StreamReadFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.json.JSONObject;
import com.bernard.greposimu.controller.JSONReader;
import com.bernard.greposimu.model.game.GameConfig;
public class GrepoSimu {
public static GameData readGameData() throws IOException {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
public static GameConfig makeGameData() throws IOException {
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
try (InputStream is = classLoader.getResourceAsStream("gamedata.json")) {
if (is == null) return null;
try (InputStreamReader isr = new InputStreamReader(is);
BufferedReader reader = new BufferedReader(isr)) {
// Reading the file
String json = reader.lines().collect(Collectors.joining(System.lineSeparator()));
// De-serialize to an object
JsonFactory jsonFactory = JsonFactory.builder()
.enable(StreamReadFeature.INCLUDE_SOURCE_IN_LOCATION)
.build();
SimpleModule module = new SimpleModule();
module.addDeserializer(Map.class, new MapJsonDeserializer());
ObjectMapper mapper = new ObjectMapper(jsonFactory);
mapper.registerModule(module);
GameData data = mapper.readValue(json, GameData.class);
JSONObject obj = new JSONObject(json);
// Adapting power map
Map<String,Power> pmap = new HashMap<>();
for(String id : data.powers.keySet()) {
Set<String> types = data.powers.get(id).getTypes();
if(types.isEmpty()) {
pmap.put(id, new Power(data.powers.get(id)));
} else {
for(String type : types) {
pmap.put(id+"."+type, new Power(data.powers.get(id), type));
}
}
}
data.powers = pmap;
System.out.println(data.powers.get("effort_of_the_huntress"));
return data;
return JSONReader.makeGameConfig(obj);
}
}
}
public static void main(String[] args) {
for(int i=0;i<26;i++)
System.out.println(i+"->"+Buildings.wallBonus(i));
}
public static void mainZ(String[] args) {
Registerar.regiter();
System.out.println(Registerar.unites.size());
OffDefStats scoring = new OffDefStats(0, 0, 0, 1.0, 0,0,0,0);
for(Heros h : withNull(Registerar.heros)) {
if(h!= null && h.getNom()!="Agamemnon")continue;
for(int l = 1;l<=20&&h!=null;l+=10-(l%10)) {
for(Dieu d : withNull(Arrays.asList(Dieu.values()))) {
//System.out.println("Héros: "+((c.h==null)?"aucun":("%s (lvl %d)".formatted(h.getNom(),l)))+" - Dieu: "+((d==null)?"aucun":d.getNom()));
//up.print(h, l, 1000);
}
}
}
}
public static final <T> Iterable<T> withNull(Iterable<T> itrble) {
return (Iterable<T>) new Iterable<T>() {
@Override
public Iterator<T> iterator() {
Iterator<T> it = itrble.iterator();
return new Iterator<T>() {
boolean pastNull = false;
@Override
public boolean hasNext() {
return !pastNull || it.hasNext();
}
@Override
public T next() {
if(pastNull)
return it.next();
else {
pastNull = true;
return null;
}
}
};
};
};
}
}
@@ -1,15 +1,36 @@
package com.bernard.greposimu;
import java.io.File;
import java.io.IOException;
import java.util.Map;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.bernard.greposimu.engine.Registerar;
import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.game.GrepoYaml;
import com.bernard.greposimu.model.simulator.objective.TownObjective;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator.Feature;
@SpringBootApplication
public class GrepoSimuApplication {
public static void main(String[] args) {
Registerar.regiter();
public static GameConfig GREPOLIS_GC;
public static Map<String, TownObjective> OBJECTIVES;
public static void main(String[] args) throws IOException {
GREPOLIS_GC = GrepoSimu.makeGameData();
File objectiveFile = new File("/home/mysaa/Documents/Projets/eclipse-workspace/GrepoSimu/src/test/resources/objectives.yml");
ObjectMapper om = new ObjectMapper(new YAMLFactory().disable(Feature.WRITE_DOC_START_MARKER));
om.registerModule(new GrepoYaml(GREPOLIS_GC));
JavaType objType = om.getTypeFactory().constructParametricType(Map.class, String.class,TownObjective.class);
OBJECTIVES = om.readValue(objectiveFile, objType);
SpringApplication.run(GrepoSimuApplication.class, args);
}
@@ -1,14 +0,0 @@
/*package com.bernard.greposimu;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(GrepoSimuApplication.class);
}
}
*/
@@ -2,9 +2,15 @@ package com.bernard.greposimu;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.bernard.greposimu.model.game.util.Identified;
public class Utils {
@@ -18,6 +24,29 @@ public class Utils {
a5 * x * x * x * x * x;
}
public static final <T extends Identified> T getIdentified(Set<T> set, String id) {
return set.stream().filter(x -> x.getId().equals(id)).findAny().orElse(null);
}
public static final <T extends Identified> T throwingGetIdentified(String type, Set<T> set,String id){
if(id == null)return null;
T out = Utils.getIdentified(set, id);
if(out==null) throw new IllegalArgumentException("Could not find "+type+" of id "+id);
return out;
}
public static final <E extends Enum<E>> EnumSet<E> toEnumSet(Class<E> c,Set<E> set){
if(set.isEmpty())
return EnumSet.noneOf(c);
else
return EnumSet.copyOf(set);
}
public static final <K,E,F> Map<K,F> mapValue(Map<K,E> map,Function<E,F> f){
return map.keySet().stream().collect(Collectors.toMap(
Function.identity(),
k -> f.apply(map.get(k))));
}
public static final <T> Map<T,Boolean> setToMap(Set<T> set){
return new AbstractMap<T,Boolean>() {
@@ -66,4 +95,8 @@ public class Utils {
};
}
public static <E> E randFromSet(Random r, Set<E> set) {
E el = set.stream().sorted().skip(r.nextInt(set.size())).findFirst().get();
return el;
}
}
@@ -0,0 +1,19 @@
package com.bernard.greposimu.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import com.bernard.greposimu.GrepoSimuApplication;
@Controller
public class GameDataController {
@GetMapping("/gamedata")
public String gamedata(Model model) {
System.out.println("Reading game data");
model.addAttribute("content",GrepoSimuApplication.GREPOLIS_GC);
return "debug";
}
}
@@ -1,291 +0,0 @@
package com.bernard.greposimu.controller;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import com.bernard.greposimu.GrepoSimu;
import com.bernard.greposimu.engine.Optimizer;
import com.bernard.greposimu.engine.Optimizer.UnitesProportions;
import com.bernard.greposimu.engine.Registerar;
import com.bernard.greposimu.engine.game.Fight;
import com.bernard.greposimu.engine.game.Game;
import com.bernard.greposimu.model.DefContext;
import com.bernard.greposimu.model.Dieu;
import com.bernard.greposimu.model.FightStats;
import com.bernard.greposimu.model.Heros;
import com.bernard.greposimu.model.OffDefStats;
import com.bernard.greposimu.model.Unite;
import com.bernard.greposimu.model.game.GameData;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
@Controller
public class GrepoSimuController {
@GetMapping("/gamedata")
public String gamedata(Model model) {
System.out.println("Reading game data");
GameData data;
try {
data = GrepoSimu.readGameData();
model.addAttribute("content",data.toString());
} catch (IOException e) {
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
model.addAttribute("content",writer.toString());
}
return "debug";
}
@GetMapping("/simulator")
public String simulator(Model model) throws IOException {
GameData data = GrepoSimu.readGameData();
model.addAttribute("heroes", data.heroes.values());
model.addAttribute("defUnits", Fight.relevantDefUnits(data));
model.addAttribute("defCounsellors",Fight.relevantDefCounsellors(data));
model.addAttribute("defResearches",Fight.relevantDefResearch(data));
model.addAttribute("defPowers",Fight.relevantDefPowers(data));
model.addAttribute("defCtx",new DefContext());
return "simulator";
}
@PostMapping("/simulate")
@GetMapping("/simulate")
public String simulate(@ModelAttribute DefContext defCtx, Model model) throws IOException {
if(defCtx == null)
defCtx = new DefContext();
GameData data = GrepoSimu.readGameData();
Game g = new Game(data);
FightStats cityStats = g.fight.computeDefStats(defCtx);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
model.addAttribute("content",cityStats.toString()+"\n"+mapper.writerWithDefaultPrettyPrinter().writeValueAsString(defCtx));
return "debug";
}
@GetMapping("/optimizer")
public String greeting(Model model) {
model.addAttribute("heros", Registerar.heros);
// Liste des unités
Map<String,List<Unite>> godUnites = Registerar.unites.stream()
.filter(u -> u.getDieu() != null)
.collect(Collectors.groupingBy(u -> u.getDieu().getPname()));
List<Unite> otherUnites = Registerar.unites.stream()
.filter(u -> u.getDieu() == null)
.collect(Collectors.toList());
System.out.println(godUnites);
model.addAttribute("otherunites", otherUnites);
model.addAttribute("godunites", godUnites);
model.addAttribute("dieux", Arrays.stream(Dieu.values()).map(Dieu::getPname).collect(Collectors.toList()));
model.addAttribute("optinput", new OptimizerInput());
return "optimizer";
}
@PostMapping("/optimizer")
public String greetingSubmit(@ModelAttribute OptimizerInput optinput, Model model) {
Optimizer.Contexte ctx = new Optimizer.Contexte();
ctx.h = Registerar.getHeros(optinput.heros);
ctx.level = optinput.herosLvl;
ctx.scoring = optinput.getOffDefStats();
ctx.d = null;
Set<Unite> unites = Registerar.unites.stream().filter(u -> optinput.unites.get(u.getPname())).collect(Collectors.toSet());
Map<String,OptimizerOutput> outputs = new HashMap<>();
outputs.put("none", new OptimizerOutput(Optimizer.optimize(ctx,unites), ctx.h, ctx.level, optinput.population));
for(Dieu d : Dieu.values()) {
ctx.d = d;
outputs.put(d.getPname(), new OptimizerOutput(Optimizer.optimize(ctx,unites), ctx.h, ctx.level, optinput.population));
}
model.addAttribute("dieux", Arrays.stream(Dieu.values()).map(Dieu::getPname).collect(Collectors.toList()));
model.addAttribute("outputs", outputs);
model.addAttribute("requestedPop",optinput.population);
return "optimizerResult";
}
public static class OptimizerInput {
public String heros = Registerar.heros.stream().map(Heros::getPname).min(Comparator.comparing(Function.identity())).get();
public int herosLvl = 1;
public double att_hack_proportion = 0.0;
public double att_pierce_proportion = 0.0;
public double att_distance_proportion = 0.0;
public double ship_attack_proportion = 0.0;
public double def_hack_proportion = 1.0;
public double def_pierce_proportion = 1.0;
public double def_distance_proportion = 1.0;
public double ship_defense_proportion = 1.0;
public int population = 1000;
public Map<String,Boolean> unites = Registerar.unites.stream().collect(Collectors.toMap(Unite::getPname, u -> true));
public String getHeros() {
return heros;
}
public void setHeros(String heros) {
this.heros = heros;
}
public int getHerosLvl() {
return herosLvl;
}
public void setHerosLvl(int herosLvl) {
this.herosLvl = herosLvl;
}
public double getAtt_hack_proportion() {
return att_hack_proportion;
}
public void setAtt_hack_proportion(double att_hack_proportion) {
this.att_hack_proportion = att_hack_proportion;
}
public double getAtt_pierce_proportion() {
return att_pierce_proportion;
}
public void setAtt_pierce_proportion(double att_pierce_proportion) {
this.att_pierce_proportion = att_pierce_proportion;
}
public double getAtt_distance_proportion() {
return att_distance_proportion;
}
public void setAtt_distance_proportion(double att_distance_proportion) {
this.att_distance_proportion = att_distance_proportion;
}
public double getShip_attack_proportion() {
return ship_attack_proportion;
}
public void setShip_attack_proportion(double ship_attack_proportion) {
this.ship_attack_proportion = ship_attack_proportion;
}
public double getDef_hack_proportion() {
return def_hack_proportion;
}
public void setDef_hack_proportion(double def_hack_proportion) {
this.def_hack_proportion = def_hack_proportion;
}
public double getDef_pierce_proportion() {
return def_pierce_proportion;
}
public void setDef_pierce_proportion(double def_pierce_proportion) {
this.def_pierce_proportion = def_pierce_proportion;
}
public double getDef_distance_proportion() {
return def_distance_proportion;
}
public void setDef_distance_proportion(double def_distance_proportion) {
this.def_distance_proportion = def_distance_proportion;
}
public double getShip_defense_proportion() {
return ship_defense_proportion;
}
public void setShip_defense_proportion(double ship_defense_proportion) {
this.ship_defense_proportion = ship_defense_proportion;
}
public int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
public Map<String, Boolean> getUnites() {
return unites;
}
public void setUnites(Map<String, Boolean> unites) {
this.unites = unites;
}
public OffDefStats getOffDefStats() {
return new OffDefStats(att_hack_proportion, att_pierce_proportion, att_distance_proportion, ship_attack_proportion, def_hack_proportion, def_pierce_proportion, def_distance_proportion, ship_defense_proportion);
}
@Override
public String toString() {
return "OptimizerInput [heros=" + heros + ", herosLvl=" + herosLvl + ", att_hack_proportion="
+ att_hack_proportion + ", att_pierce_proportion=" + att_pierce_proportion
+ ", att_distance_proportion=" + att_distance_proportion + ", ship_attack_proportion="
+ ship_attack_proportion + ", def_hack_proportion=" + def_hack_proportion
+ ", def_pierce_proportion=" + def_pierce_proportion + ", def_distance_proportion="
+ def_distance_proportion + ", ship_defense_proportion=" + ship_defense_proportion + ", population="
+ population + ", unites=" + unites + "]";
}
}
public static class OptimizerOutput {
OffDefStats total;
int totalpop;
List<Unite> unites;
Map<Unite,Integer> ucounts;
public OptimizerOutput(UnitesProportions data, Heros h, int level, int pop) {
this.ucounts = new HashMap<>(data.getData().size());
for(Unite u : data.getData().keySet())
this.ucounts.put(u, (int) Math.floor((data.getData().get(u) * pop) / u.getPopulation()));
this.unites = Registerar.unites.stream().filter(u -> ucounts.containsKey(u)).toList();
this.totalpop = this.ucounts.entrySet().stream().mapToInt(e -> e.getKey().getPopulation() * e.getValue()).sum();
this.total = this.ucounts.entrySet().stream()
.map(e -> ((h==null)?e.getKey().getStats():h.applyToUnit(e.getKey(), level)).times(e.getValue()))
.reduce(OffDefStats.zero,(a,b) -> a.plus(b));
}
public OffDefStats getTotal() {
return total;
}
public int getTotalpop() {
return totalpop;
}
public Map<Unite, Integer> getUcounts() {
return ucounts;
}
public List<Unite> getUnites() {
return unites;
}
}
}
@@ -0,0 +1,313 @@
package com.bernard.greposimu.controller;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.json.JSONObject;
import org.springframework.lang.Nullable;
import com.bernard.greposimu.Utils;
import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.powers.FuryPower;
import com.bernard.greposimu.model.game.powers.GodPower;
import com.bernard.greposimu.model.game.powers.Power;
import com.bernard.greposimu.model.game.powers.Power.AreaOfEffect;
import com.bernard.greposimu.model.game.powers.Power.Effect;
import com.bernard.greposimu.model.game.powers.Power.Target;
import com.bernard.greposimu.model.game.researches.Research;
import com.bernard.greposimu.model.game.units.FightType;
import com.bernard.greposimu.model.game.units.Hero;
import com.bernard.greposimu.model.game.units.Hero.HeroCategory;
import com.bernard.greposimu.model.game.util.Resources;
import com.bernard.greposimu.model.game.units.NavalUnit;
import com.bernard.greposimu.model.game.units.TerrestrialUnit;
import com.bernard.greposimu.model.game.units.TransportUnit;
import com.bernard.greposimu.model.game.units.Unit;
public class JSONReader {
public static GameConfig makeGameConfig(JSONObject json) {
JSONObject godsJ = json.getJSONObject("gods");
Set<God> gods = new HashSet<>();
for(String g : godsJ.keySet()) {
JSONObject godJ = godsJ.getJSONObject(g);
gods.add(new God(godJ.getString("id"), godJ.getString("name")));
}
JSONObject powersJ = json.getJSONObject("powers");
Set<Power> powers = new HashSet<>();
for(String p : powersJ.keySet()) {
JSONObject power = powersJ.getJSONObject(p);
JSONObject metadefaults = power.isNull("meta_defaults")?null:power.getJSONObject("meta_defaults");
String id = power.getString("id");
EnumSet<Target> targets = Utils.toEnumSet(Power.Target.class,power.getJSONArray("targets").toList().stream().map(k -> getPowerTarget((String)k)).collect(Collectors.toSet()));
EnumSet<Target> seedsTo = Utils.toEnumSet(Power.Target.class,power.getJSONArray("seeds_to").toList().stream().map(k -> getPowerTarget((String)k)).collect(Collectors.toSet()));
String shortEffect = power.isNull("short_effect")?null:power.getString("short_effect");
Power.Group powerGroup = getPowerGroup(power.getString("power_group"));
int powerGroupLevel = power.getInt("power_group_level");
//name
Set<String> metaFields = power.getJSONArray("meta_fields").toList().stream().map(v -> (String)v).collect(Collectors.toSet());
Map<String,Object> metaDefaults = metadefaults==null?Map.of():metadefaults.toMap();
int lifetime = power.getInt("lifetime");
EnumSet<Power.Tag> tags = getPowerTags(power);
EnumSet<Effect> effects = Utils.toEnumSet(Power.Effect.class,power.getJSONArray("effects").toList().stream().map(k -> getPowerEffect((String)k)).collect(Collectors.toSet()));
Set<String> compatiblePowers = power.isNull("compatible_powers")?Set.of():power.getJSONArray("compatible_powers").toList().stream().map(v -> (String)v).collect(Collectors.toSet());
EnumSet<AreaOfEffect> areaOfEffect = Utils.toEnumSet(Power.AreaOfEffect.class,power.getJSONArray("area_of_effect").toList().stream().map(k -> getPowerAOE((String)k)).collect(Collectors.toSet()));
boolean dependent =
power.optJSONObject("name")!=null ||
power.optJSONObject("effect")!=null ||
power.optJSONObject("description")!=null;
String configType = dependent?
(metadefaults != null && metadefaults.has("type"))?"type":
(metadefaults != null && metadefaults.has("god"))?"god":"unknown"
:null;
// for each X, either X or XM should be null. if one XM is not null, configType should be set
String name = (power.optJSONObject("name")==null)?power.optString("name"):null;
String effect = (power.optJSONObject("effect")==null)?power.optString("effect"):null;
String description = (power.optJSONObject("description")==null)?power.optString("description"):null;
Map<String,String> nameM = (name==null)?Utils.mapValue(power.getJSONObject("name").getJSONObject(configType).toMap(),v -> (String)v):null;
Map<String,String> effectM = (effect==null)?Utils.mapValue(power.getJSONObject("effect").getJSONObject(configType).toMap(),v -> (String)v):null;
Map<String,String> descriptionM = (description==null)?Utils.mapValue(power.getJSONObject("description").getJSONObject(configType).toMap(),v -> (String)v):null;
if(!power.isNull("god_id") && !power.getString("god_id").isEmpty()) {
God god = gods.stream().filter(g -> g.getId().equals(power.getString("god_id"))).findAny().get();
int favorCost = power.getInt("favor");
int templeLevelSumDepedency = power.isNull("temple_level_sum_dependency")?0:power.getInt("temple_level_sum_dependency");
if(power.getInt("fury_percentage_cost") != 0) {
// FuryPower
int furyPercentageCost = power.getInt("fury_percentage_cost");
powers.add(new FuryPower(id, targets, seedsTo, shortEffect, powerGroup, powerGroupLevel, metaFields, metaDefaults, lifetime, effects, tags, compatiblePowers, areaOfEffect, name, effect, description, configType, nameM, effectM, descriptionM,
god, favorCost, templeLevelSumDepedency, furyPercentageCost));
} else {
// GodPower
powers.add(new GodPower(id, targets, seedsTo, shortEffect, powerGroup, powerGroupLevel, metaFields, metaDefaults, lifetime, effects, tags, compatiblePowers, areaOfEffect, name, effect, description, configType, nameM, effectM, descriptionM, god, favorCost, templeLevelSumDepedency));
}
} else {
powers.add(new Power(id, targets, seedsTo, shortEffect, powerGroup, powerGroupLevel, metaFields, metaDefaults, lifetime, effects, tags, compatiblePowers, areaOfEffect, name, effect, description, configType, nameM, effectM, descriptionM));
}
}
JSONObject researchesJ = json.getJSONObject("researches");
Set<Research> researches = new HashSet<>();
for(String r : researchesJ.keySet()) {
JSONObject research = researchesJ.getJSONObject(r);
researches.add(new Research(
research.getString("id"),
research.getString("name"),
research.getString("description"),
research.isNull("research_dependencies")?Set.of():research.getJSONArray("research_dependencies").toList().stream().map(k -> Utils.getIdentified(researches,(String)k)).collect(Collectors.toSet()),
research.isNull("building_dependencies")?Map.of():research.getJSONObject("building_dependencies").keySet().stream()
.collect(Collectors.toMap(Function.identity(), b -> research.getJSONObject("building_dependencies").getInt(b))),
research.isNull("resources")?null:new Resources(
research.getJSONObject("resources").getInt("wood"),
research.getJSONObject("resources").getInt("stone"),
research.getJSONObject("resources").getInt("iron")),
research.getInt("required_time"),
research.getInt("research_points")
));
}
JSONObject unitsJ = json.getJSONObject("units");
Set<Unit> units = new HashSet<>();
for(String u : unitsJ.keySet()) {
JSONObject unit = unitsJ.getJSONObject(u);
if(unit.getBoolean("is_naval")) {
if(unit.getInt("capacity")>0) {
units.add(new TransportUnit(
unit.getString("id"),
unit.getString("name"),
unit.getString("description"),
unit.getInt("population"),
unit.getInt("speed"),
unit.getString("category").equals("mythological_ground") || unit.getString("category").equals("mythological_naval"),
unit.isNull("god_id")?null:gods.stream().filter(g -> g.getId().equals(unit.getString("god_id"))).findAny().orElse(null),
unit.isNull("resources")?null:new Resources(
unit.getJSONObject("resources").getInt("wood"),
unit.getJSONObject("resources").getInt("stone"),
unit.getJSONObject("resources").getInt("iron")),
unit.getInt("favor"),
unit.getInt("build_time"),
unit.isNull("research_dependencies")?Set.of():unit.getJSONArray("research_dependencies").toList().stream().map(k -> Utils.getIdentified(researches, (String)k)).collect(Collectors.toSet()),
unit.isNull("building_dependencies")?Map.of():unit.getJSONObject("building_dependencies").keySet().stream()
.collect(Collectors.toMap(Function.identity(), b -> unit.getJSONObject("building_dependencies").getInt(b))),
unit.getInt("attack"),
unit.getInt("defense"),
unit.getInt("capacity")
));
} else {
units.add(new NavalUnit(
unit.getString("id"),
unit.getString("name"),
unit.getString("description"),
unit.getInt("population"),
unit.getInt("speed"),
unit.getString("category").equals("mythological_ground") || unit.getString("category").equals("mythological_naval"),
unit.isNull("god_id")?null:gods.stream().filter(g -> g.getId().equals(unit.getString("god_id"))).findAny().orElse(null),
unit.isNull("resources")?null:new Resources(
unit.getJSONObject("resources").getInt("wood"),
unit.getJSONObject("resources").getInt("stone"),
unit.getJSONObject("resources").getInt("iron")),
unit.getInt("favor"),
unit.getInt("build_time"),
unit.isNull("research_dependencies")?Set.of():unit.getJSONArray("research_dependencies").toList().stream().map(k -> Utils.getIdentified(researches,(String)k)).collect(Collectors.toSet()),
unit.isNull("building_dependencies")?Map.of():unit.getJSONObject("building_dependencies").keySet().stream()
.collect(Collectors.toMap(Function.identity(), b -> unit.getJSONObject("building_dependencies").getInt(b))),
unit.getInt("attack"),
unit.getInt("defense")
));
}
} else {
FightType ft = null;
switch(unit.getString("attack_type")) {
case "pierce": ft = FightType.PIERCE;break;
case "hack": ft = FightType.HACK;break;
case "distance": ft = FightType.DISTANCE;break;
}
units.add(new TerrestrialUnit(
unit.getString("id"),
unit.getString("name"),
unit.getString("description"),
unit.getInt("population"),
unit.getInt("speed"),
unit.getString("category").equals("mythological_ground") || unit.getString("category").equals("mythological_naval"),
unit.isNull("god_id")?null:gods.stream().filter(g -> g.getId().equals(unit.getString("god_id"))).findAny().orElse(null),
unit.isNull("resources")?null:new Resources(
unit.getJSONObject("resources").getInt("wood"),
unit.getJSONObject("resources").getInt("stone"),
unit.getJSONObject("resources").getInt("iron")),
unit.getInt("favor"),
unit.getInt("build_time"),
unit.isNull("research_dependencies")?Set.of():unit.getJSONArray("research_dependencies").toList().stream().map(k -> Utils.getIdentified(researches, (String)k)).collect(Collectors.toSet()),
unit.isNull("building_dependencies")?Map.of():unit.getJSONObject("building_dependencies").keySet().stream()
.collect(Collectors.toMap(Function.identity(), b -> unit.getJSONObject("building_dependencies").getInt(b))),
unit.getInt("attack"),
ft,
unit.getInt("def_pierce"),
unit.getInt("def_hack"),
unit.getInt("def_distance"),
(unit.has("booty"))?unit.getInt("booty"):0,
unit.getJSONArray("special_abilities").toList().stream().filter(o -> o.equals("flying")).findAny().isPresent()
));
}
}
JSONObject heroesJ = json.getJSONObject("heroes");
Set<Hero> heroes = new HashSet<>();
for(String h : heroesJ.keySet()) {
JSONObject hero = heroesJ.getJSONObject(h);
FightType ft = null;
switch(hero.getString("attack_type")) {
case "pierce": ft = FightType.PIERCE;break;
case "hack": ft = FightType.HACK;break;
case "distance": ft = FightType.DISTANCE;break;
}
JSONObject descargs = hero.getJSONObject("description_args").getJSONObject("1");
heroes.add(new Hero(
hero.getString("id"),
hero.getString("name"),
hero.getString("description"),
hero.getInt("speed"),
hero.getInt("attack"),
ft,
hero.getInt("def_pierce"),
hero.getInt("def_hack"),
hero.getInt("def_distance"),
hero.getInt("booty"),
hero.getString("category").equals("war")?HeroCategory.WAR:HeroCategory.WISDOM,
hero.getInt("cost"),
hero.getString("short_description"),
descargs.getDouble("value"),
descargs.getDouble("level_mod")
));
}
return new GameConfig(gods, units, heroes, researches, powers);
}
private static EnumSet<Power.Tag> getPowerTags(JSONObject o) {
Set<Power.Tag> out = new HashSet<Power.Tag>();
if(o.getBoolean("extendible"))out.add(Power.Tag.EXTENDIBLE);
if(o.getBoolean("display_amount"))out.add(Power.Tag.DISPLAY_AMOUNT);
if(o.getBoolean("destructive"))out.add(Power.Tag.DESTRUCTIVE);
if(o.getBoolean("is_capped"))out.add(Power.Tag.CAPPED);
if(o.getBoolean("is_fake_power"))out.add(Power.Tag.FAKE_POWER);
if(o.getBoolean("is_onetime_power"))out.add(Power.Tag.ONETIME_POWER);
if(o.getBoolean("is_ritual"))out.add(Power.Tag.RITUAL);
if(o.getBoolean("is_upgradable"))out.add(Power.Tag.UPGRADABLE);
if(o.getBoolean("is_valid_for_happenings"))out.add(Power.Tag.VALID_FOR_HAPPENINGS);
if(o.getBoolean("transfer_to_casual_world"))out.add(Power.Tag.TRANSFER_TO_CASUAL_WORLD);
if(o.getBoolean("wasteable"))out.add(Power.Tag.WASTEABLE);
if(o.getBoolean("needs_level"))out.add(Power.Tag.NEEDS_LEVEL);
if(o.getBoolean("passive"))out.add(Power.Tag.PASSIVE);
if(o.getBoolean("recreate_on_restart"))out.add(Power.Tag.RECREATE_ON_RESTART);
if(o.getBoolean("removed_on_target_loss"))out.add(Power.Tag.REMOVED_ON_TARGET_LOSS);
if(o.getBoolean("requires_god"))out.add(Power.Tag.REQUIRES_GOD);
if(o.getBoolean("no_lifetime"))out.add(Power.Tag.NO_LIFETIME);
if(o.getBoolean("only_own_towns"))out.add(Power.Tag.ONLY_OWN_TOWNS);
if(o.getBoolean("negative"))out.add(Power.Tag.NEGATIVE);
if(o.getBoolean("ignores_democritus"))out.add(Power.Tag.IGNORES_DEMOCRITUS);
if(o.getBoolean("boost"))out.add(Power.Tag.BOOST);
return Utils.toEnumSet(Power.Tag.class,out);
}
private static Power.AreaOfEffect getPowerAOE(String s){
switch(s) {
case "area_of_effect_build_time": return Power.AreaOfEffect.BUILDTIME;
case "area_of_effect_commands": return Power.AreaOfEffect.COMMANDS;
case "area_of_effect_favor": return Power.AreaOfEffect.FAVOR;
case "area_of_effect_militia": return Power.AreaOfEffect.MILITIA;
case "area_of_effect_resources": return Power.AreaOfEffect.RESOURCES;
}
throw new IllegalArgumentException("I don't know Power AreaOfEffect "+s);
}
private static Power.Effect getPowerEffect(String s){
switch(s) {
case "effects_ground": return Power.Effect.GROUND;
case "effects_naval": return Power.Effect.NAVAL;
case "effects_wall": return Power.Effect.WALL;
}
throw new IllegalArgumentException("I don't know Power Effect "+s);
}
private static Power.Group getPowerGroup(String s){
switch(s) {
case "attack_boost_group": return Power.Group.ATTACK_BOOST;
case "attack_ship_attack_boost_group": return Power.Group.ATTACK_SHIP_ATTACK_BOOST;
case "battle_point_boost_group": return Power.Group.BATTLE_POINT_BOOST;
case "building_boost_group": return Power.Group.BUILDING_BOOST;
case "defense_boost_group": return Power.Group.DEFENSE_BOOST;
case "favor_boost_group": return Power.Group.FAVOR_BOOST;
case "resource_boost_group": return Power.Group.RESOURCE_BOOST;
case "unit_boost_group": return Power.Group.UNIT_BOOST;
case "": return null;
}
throw new IllegalArgumentException("I don't know Power Group "+s);
}
private static Power.Target getPowerTarget(String s){
switch(s) {
case "target_alliance": return Power.Target.ALLIANCE;
case "target_command": return Power.Target.COMMAND;
case "target_player": return Power.Target.PLAYER;
case "target_support_command": return Power.Target.SUPPORT_COMMAND;
case "target_town": return Power.Target.TOWN;
}
throw new IllegalArgumentException("I don't know Power Target "+s);
}
}
@@ -0,0 +1,7 @@
package com.bernard.greposimu.controller;
public class Randomizer {
}
@@ -0,0 +1,76 @@
package com.bernard.greposimu.controller;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import com.bernard.greposimu.GrepoSimuApplication;
import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.simulator.command.Command;
import com.bernard.greposimu.model.simulator.command.TownCommand;
import com.bernard.greposimu.model.simulator.data.SimulatorData;
import com.bernard.greposimu.model.simulator.data.Ville;
import com.bernard.greposimu.model.simulator.objective.TownObjective;
import com.bernard.greposimu.source.JSONSourcer;
@Controller
public class SchedulerController {
Map<UUID,SimulatorData> registered = new HashMap<>();
@CrossOrigin
@PostMapping(value = "/registerScheduler", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.TEXT_PLAIN_VALUE)
//@GetMapping(value = "/registerScheduler")
public @ResponseBody String register(Model model,@RequestBody String data) {
//System.out.println(data.toString());
GameConfig gc = GrepoSimuApplication.GREPOLIS_GC;
SimulatorData sd = JSONSourcer.makeSimulationData(data.toString(),gc);
UUID uuid = UUID.randomUUID();
registered.put(uuid, sd);
return uuid.toString();
}
@GetMapping(value = "/scheduler/{uuid}")
public String simulator(Model model, @PathVariable("uuid") String uuidStr) {
UUID uuid = UUID.fromString(uuidStr);
GameConfig gc = GrepoSimuApplication.GREPOLIS_GC;
SimulatorData sd = registered.get(uuid);
TownObjective defObjective = GrepoSimuApplication.OBJECTIVES.get("defTerTown");
defObjective.setGc(gc);
StringBuilder out = new StringBuilder();
for(Ville v : sd.getVilles().values()) {
out.append("<h3>==== Ville "+v.getNom()+" ====</h3>\n");
out.append("<ul>");
for(Command c : defObjective.getDifferences(v)){
out.append("<li>");
out.append(c.toString());
out.append("===>");
out.append(c.timeNeeded(sd));
if(c instanceof TownCommand)
out.append("///"+((TownCommand)c).neededResources(sd).toString());
out.append("</li>\n");
}
out.append(defObjective.getDifferences(v).stream().map(Command::toString).sorted().collect(Collectors.joining("<br/>\n")));
out.append("</ul>");
}
model.addAttribute("raw",out.toString());
return "debug";
}
}
@@ -0,0 +1,393 @@
package com.bernard.greposimu.controller;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestParam;
import com.bernard.greposimu.GrepoSimuApplication;
import com.bernard.greposimu.Utils;
import com.bernard.greposimu.engine.game.Fight;
import com.bernard.greposimu.model.DefContext;
import com.bernard.greposimu.model.FightStats;
import com.bernard.greposimu.model.OffContext;
import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.units.Unit;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
@Controller
public class SimulatorController {
@GetMapping("/simulator")
public String simulator(Model model, @RequestParam boolean random) throws IOException {
GameConfig gc = GrepoSimuApplication.GREPOLIS_GC;
model.addAttribute("heroes", gc.getHeroes());
model.addAttribute("defUnits", Fight.relevantDefUnits(gc));
model.addAttribute("defCounsellors",DefContext.COUNSELLORS);
model.addAttribute("defResearches",DefContext.RESEARCHES);
model.addAttribute("defPowers",DefContext.POWERS);
model.addAttribute("offUnits", Fight.relevantOffUnits(gc));
model.addAttribute("offCounsellors",OffContext.COUNSELLORS);
model.addAttribute("offResearches",OffContext.RESEARCHES);
model.addAttribute("offPowers",OffContext.POWERS);
SimulatorParams params = new SimulatorParams();
if(random)
params.randomize(new Random(), gc);
model.addAttribute("ctx",params);
return "simulator";
}
@GetMapping("/simulate")
public String simulate(@ModelAttribute SimulatorParams params, Model model) throws IOException {
if(params == null)
params = new SimulatorParams();
GameConfig gc = GrepoSimuApplication.GREPOLIS_GC;
DefContext defCtx = params.asDefContext(gc);
OffContext offCtx = params.asOffContext(gc);
FightStats defStats = Fight.computeDefStats(gc,defCtx);
FightStats offStats = Fight.computeOffStats(gc,offCtx);
ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
model.addAttribute("content",
defStats.toString()+"\n"+
offStats.toString()+"\n"+
mapper.writerWithDefaultPrettyPrinter().writeValueAsString(defCtx)+"\n"+
mapper.writerWithDefaultPrettyPrinter().writeValueAsString(offCtx)
);
return "debug";
}
public static class SimulatorParams {
// unitID -> number of units
public Map<String, Integer> defUnits = new HashMap<>();
public Map<String, Integer> offUnits = new HashMap<>();
public String defHero = "";
public int defHeroLevel = 0;
public String offHero = "";
public int offHeroLevel = 0;
public int wallLevel = 0;
public boolean hasTower = false;
public Set<String> defPowers = new HashSet<>();
public Set<String> offPowers = new HashSet<>();
public Set<String> defResearches = new HashSet<>();
public Set<String> offResearches = new HashSet<>();
public Set<String> defCounsellors = new HashSet<>();
public Set<String> offCounsellors = new HashSet<>();
public boolean nightBonus = false;
int luck, moral;
int olympicSwordGrepolympiaSummerLevel = 1;
int offOlympicSensesGrepolympiaSummerLevel = 1;
int aresRageLevel = 1;
int aresArmyFurySpent = 0;
int bloodlustFurySpent = 0;
int defOlympicSensesGrepolympiaSummerLevel = 1;
int olympicTorchGrepolympiaSummerLevel = 1;
int soteriasShrineLevel = 1;
boolean strategyBreach = false;
boolean allianceModifier = false;
public DefContext asDefContext(GameConfig gc) {
Map<Unit,Integer> unitsU = new HashMap<>(defUnits.size());
for(String u : defUnits.keySet())
unitsU.put(gc.getUnit(u), defUnits.get(u));
return new DefContext(
unitsU,
gc.getHero(defHero),
defHeroLevel,
wallLevel,
hasTower,
defPowers,
defOlympicSensesGrepolympiaSummerLevel, olympicTorchGrepolympiaSummerLevel, soteriasShrineLevel,
defResearches,
defCounsellors,
nightBonus
);
}
public static Object random() {
// TODO Auto-generated method stub
return null;
}
public OffContext asOffContext(GameConfig gc) {
Map<Unit,Integer> unitsU = new HashMap<>(offUnits.size());
for(String u : offUnits.keySet())
unitsU.put(gc.getUnit(u), offUnits.get(u));
return new OffContext(
unitsU,
gc.getHero(defHero),
defHeroLevel,
luck, moral, defPowers,
olympicSwordGrepolympiaSummerLevel, offOlympicSensesGrepolympiaSummerLevel, aresRageLevel, aresArmyFurySpent, bloodlustFurySpent, defResearches,
defCounsellors, allianceModifier, allianceModifier
);
}
public void randomize(Random r,GameConfig gc) {
offUnits = new HashMap<>();
God g = null;
if(r.nextDouble()<0.8)
g = Utils.randFromSet(r, gc.getGods());
for(Unit u : Fight.relevantOffUnits(gc))
if(u.getGod() == null || u.getGod().equals(g))
if(r.nextDouble() < 0.7)
offUnits.put(u.getId(), (int) ((r.nextExponential()*50+10)/Math.max(u.getPopulation(),1)));
defUnits = new HashMap<>();
g = null;
if(r.nextDouble()<0.8)
g = Utils.randFromSet(r, gc.getGods());
for(Unit u : Fight.relevantDefUnits(gc))
if(u.getGod() == null || u.getGod().equals(g))
if(r.nextDouble() < 0.7)
defUnits.put(u.getId(), (int) ((r.nextExponential()*50+10)/Math.max(u.getPopulation(),1)));
offHero = null;
offHeroLevel = 0;
if(r.nextDouble()<.7) {
offHero = Utils.randFromSet(r, gc.getHeroes()).getId();
offHeroLevel = r.nextInt(1, 21);
}
defHero = null;
defHeroLevel = 0;
if(r.nextDouble()<.7) {
defHero = Utils.randFromSet(r, gc.getHeroes()).getId();
defHeroLevel = r.nextInt(1, 21);
}
wallLevel = r.nextInt(0, 26);
hasTower = (wallLevel>20) && (r.nextDouble()<0.2);
luck = (int) (Math.tanh(r.nextGaussian())*20);
moral = 100;
if(r.nextDouble()<.7)
moral = (int) Math.max(100-(r.nextExponential()*20),0);
offPowers = new HashSet<>();
defPowers = new HashSet<>();
for(String p : OffContext.POWERS) {
if(r.nextDouble()<0.05)
offPowers.add(p);
if(r.nextDouble()<0.05)
defPowers.add(p);
}
olympicSwordGrepolympiaSummerLevel = r.nextInt(1, 5);
offOlympicSensesGrepolympiaSummerLevel = r.nextInt(1, 5);
aresRageLevel = r.nextInt(1, 11);
aresArmyFurySpent = r.nextInt(1, 5000);
bloodlustFurySpent = r.nextInt(1, 5000);
defOlympicSensesGrepolympiaSummerLevel = r.nextInt(1, 5);
olympicTorchGrepolympiaSummerLevel = r.nextInt(1, 5);
soteriasShrineLevel = r.nextInt(1, 11);
offResearches = new HashSet<>();
defResearches = new HashSet<>();
for(String p : OffContext.RESEARCHES) {
if(r.nextDouble()<0.20)
offResearches.add(p);
if(r.nextDouble()<0.20)
defResearches.add(p);
}
offCounsellors = new HashSet<>();
defCounsellors = new HashSet<>();
for(String p : OffContext.COUNSELLORS) {
if(r.nextDouble()<0.05)
offCounsellors.add(p);
if(r.nextDouble()<0.05)
defCounsellors.add(p);
}
strategyBreach = (r.nextDouble()<0.02);
allianceModifier = (r.nextDouble()<0.001);
nightBonus = (r.nextDouble()<0.02);
}
public Map<String, Integer> getDefUnits() {
return defUnits;
}
public void setDefUnits(Map<String, Integer> defUnits) {
this.defUnits = defUnits;
}
public Map<String, Integer> getOffUnits() {
return offUnits;
}
public void setOffUnits(Map<String, Integer> offUnits) {
this.offUnits = offUnits;
}
public String getDefHero() {
return defHero;
}
public void setDefHero(String defHero) {
this.defHero = defHero;
}
public int getDefHeroLevel() {
return defHeroLevel;
}
public void setDefHeroLevel(int defHeroLevel) {
this.defHeroLevel = defHeroLevel;
}
public String getOffHero() {
return offHero;
}
public void setOffHero(String offHero) {
this.offHero = offHero;
}
public int getOffHeroLevel() {
return offHeroLevel;
}
public void setOffHeroLevel(int offHeroLevel) {
this.offHeroLevel = offHeroLevel;
}
public int getWallLevel() {
return wallLevel;
}
public void setWallLevel(int wallLevel) {
this.wallLevel = wallLevel;
}
public boolean isHasTower() {
return hasTower;
}
public void setHasTower(boolean hasTower) {
this.hasTower = hasTower;
}
public Set<String> getDefPowers() {
return defPowers;
}
public void setDefPowers(Set<String> defPowers) {
this.defPowers = defPowers;
}
public Set<String> getOffPowers() {
return offPowers;
}
public void setOffPowers(Set<String> offPowers) {
this.offPowers = offPowers;
}
public Set<String> getDefResearches() {
return defResearches;
}
public void setDefResearches(Set<String> defResearches) {
this.defResearches = defResearches;
}
public Set<String> getOffResearches() {
return offResearches;
}
public void setOffResearches(Set<String> offResearches) {
this.offResearches = offResearches;
}
public Set<String> getDefCounsellors() {
return defCounsellors;
}
public void setDefCounsellors(Set<String> defCounsellors) {
this.defCounsellors = defCounsellors;
}
public Set<String> getOffCounsellors() {
return offCounsellors;
}
public void setOffCounsellors(Set<String> offCounsellors) {
this.offCounsellors = offCounsellors;
}
public int getLuck() {
return luck;
}
public void setLuck(int luck) {
this.luck = luck;
}
public int getMoral() {
return moral;
}
public void setMoral(int moral) {
this.moral = moral;
}
public boolean isStrategyBreach() {
return strategyBreach;
}
public void setStrategyBreach(boolean strategyBreach) {
this.strategyBreach = strategyBreach;
}
public boolean isAllianceModifier() {
return allianceModifier;
}
public void setAllianceModifier(boolean allianceModifier) {
this.allianceModifier = allianceModifier;
}
public Map<String, Boolean> getDefPowersAsMap() {
return Utils.setToMap(defPowers);
}
public Map<String, Boolean> getDefResearchesAsMap() {
return Utils.setToMap(defResearches);
}
public Map<String, Boolean> getDefCounsellorsAsMap() {
return Utils.setToMap(defCounsellors);
}
public boolean isNightBonus() {
return nightBonus;
}
public void setNightBonus(boolean nightBonus) {
this.nightBonus = nightBonus;
}
}
}
@@ -1,116 +0,0 @@
package com.bernard.greposimu.engine;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.ojalgo.optimisation.Expression;
import org.ojalgo.optimisation.ExpressionsBasedModel;
import org.ojalgo.optimisation.Optimisation;
import org.ojalgo.optimisation.Optimisation.Result;
import org.ojalgo.optimisation.Variable;
import com.bernard.greposimu.model.Dieu;
import com.bernard.greposimu.model.Heros;
import com.bernard.greposimu.model.OffDefStats;
import com.bernard.greposimu.model.Unite;
public class Optimizer {
public static UnitesProportions optimize(Contexte c,Set<Unite> uniteList) {
// Computing the stats of the unités in this context
Map<Unite,OffDefStats> stats = uniteList.stream()
.filter(u -> u.getDieu() == null || u.getDieu().equals(c.d))
.collect(Collectors.toMap(
Function.identity(),
u -> (OffDefStats)((c.h==null)?u.getStats():c.h.applyToUnit(u, c.level)).div(u.getPopulation())
));
// Clean list of unités
List<Unite> unites = stats.keySet().stream().sorted((u,v) -> u.getNom().compareTo(v.getNom())).toList();
// Création du modèle
ExpressionsBasedModel model = new ExpressionsBasedModel();
// Variable à optimiser
Variable y = model.addVariable("y").weight(1.0);
// Création des variables correspondant aux proportions, elles ne sont pas «optimisées» (weight 0.0) et sont positives
List<Variable> varz = new ArrayList<>(unites.size());
for(Unite u : unites) {
Variable xi = model.addVariable(u.getNom()).weight(0.0);
model.addExpression().lower(0.0).set(xi, 1.0); // 0 <= x_i
varz.add(xi);
}
// Les proportions doivent être maximalement 1 (en pratique, 1, parce que croissante)
Expression esum = model.addExpression().upper(1.0);
// y doit se comparer à chaque somme suivant ce que le scoring précise
Expression eAttqCont = model.addExpression().lower(0.0).set(y, -c.scoring.getAttqCont()); // y <= sum(a_1_i * x_i)
Expression eAttqBlan = model.addExpression().lower(0.0).set(y, -c.scoring.getAttqBlan()); // y <= sum(a_1_i * x_i)
Expression eAttqJet = model.addExpression().lower(0.0).set(y, -c.scoring.getAttqJet() ); // y <= sum(a_1_i * x_i)
Expression eAttqNav = model.addExpression().lower(0.0).set(y, -c.scoring.getAttqNav() ); // y <= sum(a_1_i * x_i)
Expression eDefCont = model.addExpression().lower(0.0).set(y, -c.scoring.getDefCont() ); // y <= sum(a_1_i * x_i)
Expression eDefBlan = model.addExpression().lower(0.0).set(y, -c.scoring.getDefBlan() ); // y <= sum(a_1_i * x_i)
Expression eDefJet = model.addExpression().lower(0.0).set(y, -c.scoring.getDefJet() ); // y <= sum(a_1_i * x_i)
Expression eDefNav = model.addExpression().lower(0.0).set(y, -c.scoring.getDefNav() ); // y <= sum(a_1_i * x_i)
for(int i=0;i<varz.size();i++) {
esum.set(varz.get(i), 1.0);
eAttqCont.set(varz.get(i), stats.get(unites.get(i)).getAttqCont());
eAttqBlan.set(varz.get(i), stats.get(unites.get(i)).getAttqBlan());
eAttqJet .set(varz.get(i), stats.get(unites.get(i)).getAttqJet());
eAttqNav .set(varz.get(i), stats.get(unites.get(i)).getAttqNav());
eDefCont .set(varz.get(i), stats.get(unites.get(i)).getDefCont());
eDefBlan .set(varz.get(i), stats.get(unites.get(i)).getDefBlan());
eDefJet .set(varz.get(i), stats.get(unites.get(i)).getDefJet());
eDefNav .set(varz.get(i), stats.get(unites.get(i)).getDefNav());
}
Result r = model.maximise(); // maximize y
return new UnitesProportions(unites, r);
}
public static class UnitesProportions {
Map<Unite,Double> data;
public UnitesProportions(List<Unite> unites, Optimisation.Result result) {
this.data = new HashMap<>();
for(int i=0;i<unites.size();i++) {
double val = result.doubleValue(1+i);
if(val>=0.0001) {
this.data.put(unites.get(i), val);
}
}
}
public void print(Heros h, int level, int pop) {
Map<Unite,Integer> ucounts = new HashMap<>(data.size());
for(Unite u : data.keySet())
ucounts.put(u, (int) Math.floor((data.get(u) * pop) / u.getPopulation()));
int totalpop = ucounts.entrySet().stream().mapToInt(e -> e.getKey().getPopulation() * e.getValue()).sum();
OffDefStats t = ucounts.entrySet().stream()
.map(e -> h.applyToUnit(e.getKey(), level).times(e.getValue()))
.reduce(OffDefStats.zero,(a,b) -> a.plus(b));
System.out.println("Population totale de %d".formatted(totalpop));
for(Unite u : data.keySet())
System.out.println("|-- %d %s (%.1f%% de la population)".formatted(ucounts.get(u),u.getNom(),data.get(u)*100));
System.out.println("--- Attaque (%d,%d,%d,%d) Défense (%d,%d,%d,%d)".formatted(
(int)t.getAttqCont(),(int)t.getAttqBlan(),(int)t.getAttqJet(),(int)t.getAttqNav(),(int)t.getDefCont(),(int)t.getDefBlan(),(int)t.getDefJet(),(int)t.getDefNav()
));
}
public Map<Unite, Double> getData() {
return data;
}
}
public static class Contexte {
public Dieu d;
public Heros h;
public int level;
public OffDefStats scoring;
}
}
@@ -1,114 +0,0 @@
package com.bernard.greposimu.engine;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import com.bernard.greposimu.model.Dieu;
import com.bernard.greposimu.model.Heros;
import com.bernard.greposimu.model.OffDefStats;
import com.bernard.greposimu.model.Unite;
public class Registerar {
public static List<Unite> unites;
public static List<Heros> heros;
public static Heros getHeros(String pname) {
return heros.stream().filter(h -> h.getPname().equals(pname)).findAny().orElse(null);
}
public static List<Unite> getUnites(Dieu dieu) {
return unites.stream().filter(u -> u.getDieu() == null || u.getDieu().equals(dieu)).collect(Collectors.toList());
}
public static void addHeros(
String nom,
String pname,
Set<String> appliedToNames,
boolean appTerrestre,
boolean appNavale,
boolean appMythique,
double attqContZero,
double attqContPlus,
double attqBlanZero,
double attqBlanPlus,
double attqJetZero,
double attqJetPlus,
double attqNavZero,
double attqNavPlus,
double defContZero,
double defContPlus,
double defBlanZero,
double defBlanPlus,
double defJetZero,
double defJetPlus,
double defNavZero,
double defNavPlus) {
heros.add(new Heros(
nom, pname, appliedToNames, appTerrestre, appNavale, appMythique,
new OffDefStats(attqContZero, attqBlanZero, attqJetZero, attqNavZero, defContZero, defBlanZero, defJetZero, defNavZero),
new OffDefStats(attqContPlus, attqBlanPlus, attqJetPlus, attqNavPlus, defContPlus, defBlanPlus, defJetPlus, defNavPlus)
));
}
public static void addUnite(String nom, String pname, int population, int speed, int butin, double attqCont, double attqBlan, double attqJet, double attqNav, double defCont, double defBlan,
double defJet, double defNav, boolean terrestre, boolean navale,
boolean mythique, Dieu dieu) {
unites.add(new Unite(
nom, pname, population, speed, butin,
new OffDefStats(attqCont, attqBlan, attqJet, attqNav, defCont, defBlan, defJet, defNav),
terrestre, navale, mythique, dieu
));
}
public static void regiter() {
unites = new ArrayList<Unite>();
addUnite("Combattant à l'épée","sword",1,8,16,5.0,0.0,0.0,0.0,14.0,8.0,30.0,0.0,true,false,false,null);
addUnite("Frondeur","slinger",1,14,8,0.0,0.0,23.0,0.0,7.0,8.0,2.0,0.0,true,false,false,null);
addUnite("Archer","archer",1,12,24,0.0,0.0,8.0,0.0,7.0,25.0,13.0,0.0,true,false,false,null);
addUnite("Hoplite","hoplite",1,6,8,0.0,16.0,0.0,0.0,18.0,12.0,7.0,0.0,true,false,false,null);
addUnite("Cavalier","rider",3,22,72,60.0,0.0,0.0,0.0,18.0,1.0,24.0,0.0,true,false,false,null);
addUnite("Char","chariot",4,18,64,0.0,56.0,0.0,0.0,76.0,16.0,56.0,0.0,true,false,false,null);
addUnite("Catapulte","catapult",15,2,400,0.0,0.0,100.0,0.0,30.0,30.0,30.0,0.0,true,false,false,null);
addUnite("Envoyé divin","godsent",3,16,5,45.0,0.0,0.0,0.0,40.0,40.0,40.0,0.0,true,false,true,null);
addUnite("Centaure","centaur",12,18,200,0.0,0.0,134.0,0.0,195.0,585.0,80.0,0.0,true,false,true,Dieu.ATHENA);
addUnite("Cerbère","cerberus",30,4,240,210.0,0.0,0.0,0.0,825.0,300.0,1575.0,0.0,true,false,true,Dieu.HADES);
addUnite("Cyclope","zyklop",40,8,320,0.0,0.0,1035.0,0.0,1050.0,10.0,1450.0,0.0,true,false,true,Dieu.POSEIDON);
addUnite("Érinye","fury",55,10,440,0.0,0.0,1700.0,0.0,460.0,460.0,595.0,0.0,true,false,true,Dieu.HADES);
addUnite("Méduse","medusa",18,6,400,0.0,425.0,0.0,0.0,480.0,345.0,290.0,0.0,true,false,true,Dieu.HERA);
addUnite("Minotaure","minotaur",30,10,480,650.0,0.0,0.0,0.0,750.0,330.0,640.0,0.0,true,false,true,Dieu.ZEUS);
addUnite("Sanglier","calydonian_boar",20,16,240,0.0,180.0,0.0,0.0,700.0,700.0,100.0,0.0,true,false,true,Dieu.ARTEMIS);
addUnite("Satyre","satyr",16,136,335,0.0,385.0,0.0,0.0,55.0,105.0,170.0,0.0,true,false,true,Dieu.APHRODITE);
addUnite("Sparte","spartoi",10,16,275,205.0,0.0,0.0,0.0,100.0,100.0,150.0,0.0,true,false,true,Dieu.ARES);
addUnite("Harpie","harpy",14,28,340,295.0,0.0,0.0,0.0,105.0,70.0,1.0,0.0,true,false,true,Dieu.HERA);
addUnite("Manticore","manticore",45,22,360,0.0,1010.0,0.0,0.0,170.0,225.0,505.0,0.0,true,false,true,Dieu.ZEUS);
addUnite("Pégase","pegasus",20,35,160,0.0,100.0,0.0,0.0,750.0,275.0,275.0,0.0,true,false,true,Dieu.ATHENA);
addUnite("Griffon","griffin",35,18,350,900.0,0.0,0.0,0.0,320.0,330.0,100.0,0.0,true,false,true,Dieu.ARTEMIS);
addUnite("Ladon","ladon",85,40,1000,0.0,0.0,1195.0,0.0,478.0,390.0,420.0,0.0,true,false,true,Dieu.ARES);
addUnite("Birème","bireme",8,15,0,0.0,0.0,0.0,24.0,0.0,0.0,0.0,160.0,false,true,false,null);
addUnite("Bateau-feu","attack_ship",10,13,0,0.0,0.0,0.0,200.0,0.0,0.0,0.0,60.0,false,true,false,null);
addUnite("Trière","trireme",16,15,0,0.0,0.0,0.0,250.0,0.0,0.0,0.0,250.0,false,true,false,null);
addUnite("Hydre","sea_monster",50,8,0,0.0,0.0,0.0,1310.0,0.0,0.0,0.0,1400.0,false,false,true,Dieu.POSEIDON);
addUnite("Sirène","siren",16,22,0,0.0,0.0,0.0,180.0,0.0,0.0,0.0,170.0,false,false,true,Dieu.APHRODITE);
//addUnite("Ladon (max)","ladon",85000,40,1000,0.0,0.0,2988.0,0.0,478.0,390.0,420.0,0.0,true,false,true,Dieu.ARES);
heros = new ArrayList<Heros>();
addHeros("Agamemnon","agamemnon",Set.of("hoplite","archer"),false,false,false,0.100,0.010,0.100,0.010,0.100,0.010,0.000,0.000,0.100,0.010,0.100,0.010,0.100,0.010,0.000,0.000);
addHeros("Ajax","ajax",Set.of("hoplites"),false,false,false,0.150,0.015,0.150,0.015,0.150,0.015,0.000,0.000,0.150,0.015,0.150,0.015,0.150,0.015,0.000,0.000);
addHeros("Alexandrios","alexandrios",Set.of("archers"),false,false,false,0.150,0.010,0.150,0.010,0.150,0.010,0.000,0.000,0.150,0.010,0.150,0.010,0.150,0.010,0.000,0.000);
addHeros("Déimos","deimos",Set.of(),true,true,true,0.050,0.005,0.050,0.005,0.050,0.005,0.050,0.005,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000);
addHeros("Hector","hector",Set.of("sword","slinger"),false,false,false,0.100,0.010,0.100,0.010,0.100,0.010,0.000,0.000,0.100,0.010,0.100,0.010,0.100,0.010,0.000,0.000);
addHeros("Lysippe","lysippe",Set.of("rider"),false,false,false,0.150,0.010,0.150,0.010,0.150,0.010,0.000,0.000,0.150,0.010,0.150,0.010,0.150,0.010,0.000,0.000);
addHeros("Léonidas","leonidas",Set.of(),true,true,true,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.050,0.005,0.050,0.005,0.050,0.005,0.050,0.005);
addHeros("Mihalis","mihalis",Set.of(),true,false,false,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.000,0.100,0.010,0.100,0.010,0.000,0.000,0.000,0.000);
addHeros("Médée","medea",Set.of("slinger"),false,false,false,0.150,0.015,0.150,0.015,0.150,0.015,0.000,0.000,0.150,0.015,0.150,0.015,0.150,0.015,0.000,0.000);
addHeros("Mélousa","melousa",Set.of("chariot"),false,false,false,0.150,0.010,0.150,0.010,0.150,0.010,0.000,0.000,0.150,0.010,0.150,0.010,0.150,0.010,0.000,0.000);
addHeros("Pélops","pelops",Set.of("hoplite","chariot"),false,false,false,0.100,0.010,0.100,0.010,0.100,0.010,0.000,0.000,0.100,0.010,0.100,0.010,0.100,0.010,0.000,0.000);
addHeros("Thémistocle","themistokles",Set.of("godsent","rider"),false,false,false,0.100,0.010,0.100,0.010,0.100,0.010,0.000,0.000,0.100,0.010,0.100,0.010,0.100,0.010,0.000,0.000);
addHeros("Télémaque","telemachos",Set.of("sword"),false,false,false,0.150,0.015,0.150,0.015,0.150,0.015,0.000,0.000,0.150,0.015,0.150,0.015,0.150,0.015,0.000,0.000);
addHeros("Urephon","urephon",Set.of(),false,false,true,0.050,0.005,0.050,0.005,0.050,0.005,0.050,0.005,0.050,0.005,0.050,0.005,0.050,0.005,0.050,0.005);
addHeros("Zuretha","zuretha",Set.of(),false,true,false,0.000,0.000,0.000,0.000,0.000,0.000,0.050,0.005,0.000,0.000,0.000,0.000,0.000,0.000,0.050,0.005);
}
}
@@ -0,0 +1,26 @@
package com.bernard.greposimu.engine.game;
public class Effects {
/*
* PC boost per level
*/
public static final double olympicSensesGrepolympiaSummerBoost(int level) {
return 0.1*level;
}
/*
* Defense boost per level
*/
public static final double olympicTorchGrepolympiaSummerBoost(int level) {
return 0.05*level;
}
/*
* Defense boost per level
*/
public static final double soteriasShrineBoost(int level) {
return 0.007*level;
}
}
@@ -6,116 +6,261 @@ import java.util.Map;
import com.bernard.greposimu.model.DefContext;
import com.bernard.greposimu.model.FightStats;
import com.bernard.greposimu.model.OffContext;
import com.bernard.greposimu.model.game.GameData;
import com.bernard.greposimu.model.game.Power;
import com.bernard.greposimu.model.game.Research;
import com.bernard.greposimu.model.game.Unit;
import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.game.units.FightType;
import com.bernard.greposimu.model.game.units.NavalUnit;
import com.bernard.greposimu.model.game.units.TerrestrialUnit;
import com.bernard.greposimu.model.game.units.Unit;
public class Fight {
Game g;
public FightResult simulateFight(GameConfig gc, OffContext off, DefContext def) {
FightStats offStats = computeOffStats(gc, off);
FightStats defStats = computeDefStats(gc, def);
public Fight(Game g) {
this.g = g;
// Combat Naval
if(offStats.getShip() > defStats.getShip()) {
// Off wins ship
} else {
}
//TODO simulateFight
throw new UnsupportedOperationException("Simulator not created");
}
public FightStats computeDefStats(DefContext def) {
public static FightStats computeDefStats(GameConfig gc, DefContext def) {
FightStats everyoneStatsBonus = FightStats.zero();
Map<String,FightStats> unitsBonuses;
Map<Unit,FightStats> unitsBonuses;
FightStats cityBaseStats;
// Heroes
unitsBonuses = g.heroes.heroFightBonuses(def.hero, def.heroLevel, false);
unitsBonuses = Heroes.heroFightBonuses(gc, def.getHero(), def.getHeroLevel(), false);
// Tower & wall
cityBaseStats = Buildings.cityBaseStats(def.wallLevel);
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, Buildings.wallBonus(def.wallLevel));
if(def.hasTower)
if(def.hasTrojanDefense()) {
cityBaseStats = Buildings.cityBaseStats(def.getWallLevel()+1);
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, Buildings.wallBonus(def.getWallLevel()+1));
} else {
cityBaseStats = Buildings.cityBaseStats(def.getWallLevel());
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, Buildings.wallBonus(def.getWallLevel()));
}
if(def.hasTower())
// Add 10% to all units
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.cst(0.1));
// Powers
//TODO powers
// Researches
if(def.counsellors.contains("divine_selection"))
for(String uid : g.data.units.keySet())
if(g.data.units.get(uid).isMythological())
unitsBonuses.put(uid, FightStats.add(unitsBonuses.getOrDefault(uid, FightStats.zero()), FightStats.cst(0.1)));
if(def.counsellors.contains("phalanx"))
for(String uid : g.data.units.keySet())
if(g.data.units.get(uid).isGround())
unitsBonuses.put(uid, FightStats.add(unitsBonuses.getOrDefault(uid, FightStats.zero()), FightStats.cst(0.1)));
if(def.counsellors.contains("ram"))
for(String uid : g.data.units.keySet())
if(g.data.units.get(uid).isNaval())
unitsBonuses.put(uid, FightStats.add(unitsBonuses.getOrDefault(uid, FightStats.zero()), FightStats.cst(0.1)));
if(def.hasDivineSelection())
for(Unit u : gc.getUnits())
if(u.isMythological())
unitsBonuses.put(u, FightStats.add(unitsBonuses.getOrDefault(u, FightStats.zero()), FightStats.cst(0.1)));
if(def.hasPhalanx())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(0.1));
if(def.hasRam())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.naval(0.1));
// Counsellors
if(def.counsellors.contains("priest"))
for(String uid : g.data.units.keySet())
if(g.data.units.get(uid).isMythological())
unitsBonuses.put(uid, FightStats.add(unitsBonuses.getOrDefault(uid, FightStats.zero()), FightStats.cst(0.2)));
if(def.counsellors.contains("commander"))
for(String uid : g.data.units.keySet())
if(g.data.units.get(uid).isGround())
unitsBonuses.put(uid, FightStats.add(unitsBonuses.getOrDefault(uid, FightStats.zero()), FightStats.cst(0.2)));
if(def.counsellors.contains("captain"))
for(String uid : g.data.units.keySet())
if(g.data.units.get(uid).isNaval())
unitsBonuses.put(uid, FightStats.add(unitsBonuses.getOrDefault(uid, FightStats.zero()), FightStats.cst(0.2)));
if(def.hasPriest())
for(Unit u : gc.getUnits())
if(u.isMythological())
unitsBonuses.put(u, FightStats.add(unitsBonuses.getOrDefault(u, FightStats.zero()), FightStats.cst(0.2)));
if(def.hasCommander())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(0.2));
if(def.hasCaptain())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.naval(0.2));
// Powers
if(def.hasMyrmidionAttack())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(-0.1));
if(def.hasDefenseBoost())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(+0.05));
if(def.hasDefensePenalty())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(-0.1));
if(def.hasLongtermDefenseBoost())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(+0.05));
if(def.hasRareDefenseBoost())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(+0.05));
if(def.hasEpicDefenseBoost())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(+0.1));
if(def.getOlympicTorchGrepolympiaSummerLevel()!=0)
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(+0.05*def.getOlympicTorchGrepolympiaSummerLevel()));
if(def.getSoteriasShrineLevel()!=0)
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(+0.007*def.getSoteriasShrineLevel()));
if(def.hasNarcissism())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(-0.1));
// Night Bonus
if(def.nightBonus)
if(def.isNightBonus())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.cst(1.0));
// Units
FightStats total = cityBaseStats.clone();
for(Unit u : g.data.units.values()) {
for(Unit u : gc.getUnits()) {
// total = total + ucount * ((1+bonus+bonus) * ustats)
if(def.units.containsKey(u.id) && def.units.get(u.id) != null)
total = FightStats.add(total,
FightStats.prod(def.units.get(u.id),
FightStats.mul(
FightStats.add(FightStats.one(),everyoneStatsBonus,unitsBonuses.getOrDefault(u.id, FightStats.zero()))
, u.getDefStats())
));
total = FightStats.add(total,
FightStats.prod(def.unitCount(u),
FightStats.mul(
FightStats.add(FightStats.one(),everyoneStatsBonus,unitsBonuses.getOrDefault(u, FightStats.zero()))
, makeDefStats(u))
));
}
// HeroStat
if(def.getHero() != null) {
total = FightStats.add(total, new FightStats(
def.getHero().getHackDef() * (1.0+0.1*def.getHeroLevel()),
def.getHero().getPierceDef() * (1.0+0.1*def.getHeroLevel()),
def.getHero().getDistanceDef() * (1.0+0.1*def.getHeroLevel()),
0.0
));
}
return total;
}
public static List<Power> relevantDefPowers(GameData gd) {
System.out.println(gd.powers);
return List.of("acumen", "divine_senses", "myrmidion_attack", "trojan_defense", "defense_boost",
"defense_penalty", "longterm_defense_boost", "assassins_acumen", "rare_defense_boost",
"epic_defense_boost", "olympic_torch.grepolympia_summer", "olympic_senses.grepolympia_summer", "missions_power_4.missions_dionysia",
"divine_battle_strategy_rare", "divine_battle_strategy_epic", "naval_battle_strategy_rare",
"naval_battle_strategy_epic", "land_battle_strategy_rare", "land_battle_strategy_epic",
"soterias_shrine.not_cast").stream().map(gd.powers::get).map(p -> (Power)p).toList();
}
public static List<Research> relevantDefResearch(GameData gd) {
return List.of("divine_selection","phalanx","ram")
.stream().map(gd.researches::get).toList();
}
public static List<Unit> relevantDefUnits(GameData data) {
return data.units.values().stream().toList();
}
public static List<String> relevantDefCounsellors(GameData data) {
return List.of("priest","commander","captain");
}
public FightStats computeOffStats(DefContext off) {
//TODO computeOffStats
throw new UnsupportedOperationException("Simulator not created");
public static FightStats makeDefStats(Unit u) {
if(u instanceof TerrestrialUnit) {
TerrestrialUnit tu = (TerrestrialUnit)u;
return new FightStats(tu.getHackDef(), tu.getPierceDef(), tu.getDistanceDef(), 0.0);
}else if(u instanceof NavalUnit) {
NavalUnit nu = (NavalUnit)u;
return new FightStats(0.0, 0.0, 0.0, nu.getDefense());
}
throw new UnsupportedOperationException("I don't know how to manage units of type "+u.getClass().getName());
}
public FightResult simulateFight(OffContext off, DefContext def) {
//TODO simulateFight
throw new UnsupportedOperationException("Simulator not created");
public static FightStats computeOffStats(GameConfig gc, OffContext off) {
FightStats everyoneStatsBonus = FightStats.zero();
Map<Unit,FightStats> unitsBonuses;
// Heroes
unitsBonuses = Heroes.heroFightBonuses(gc, off.getHero(), off.getHeroLevel(), false);
// Researches
if(off.hasDivineSelection())
for(Unit u : gc.getUnits())
if(u.isMythological())
unitsBonuses.put(u, FightStats.add(unitsBonuses.getOrDefault(u, FightStats.zero()), FightStats.cst(0.1)));
if(off.hasPhalanx())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(0.1));
if(off.hasRam())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.naval(0.1));
// Counsellors
if(off.hasPriest())
for(Unit u : gc.getUnits())
if(u.isMythological())
unitsBonuses.put(u, FightStats.add(unitsBonuses.getOrDefault(u, FightStats.zero()), FightStats.cst(0.2)));
if(off.hasCommander())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(0.2));
if(off.hasCaptain())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.naval(0.2));
// Powers
if(off.hasMyrmidionAttack())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(+0.1));
if(off.hasAttackBoost())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(+0.1));
if(off.hasAttackPenalty())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(-0.1));
if(off.hasLongtermAttackBoost())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(+0.1));
if(off.hasLuxurious_residence())
;//XXX Unimplemented
if(off.hasAttack_ship_attack_boost_small())
unitsBonuses.put(gc.getUnit("attack_ship"), FightStats.add(unitsBonuses.getOrDefault(gc.getUnit("attack_ship"), FightStats.zero()), FightStats.cst(0.1)));
if(off.hasAttack_ship_attack_boost_medium())
unitsBonuses.put(gc.getUnit("attack_ship"), FightStats.add(unitsBonuses.getOrDefault(gc.getUnit("attack_ship"), FightStats.zero()), FightStats.cst(0.2)));
if(off.hasAttack_ship_attack_boost_large())
unitsBonuses.put(gc.getUnit("attack_ship"), FightStats.add(unitsBonuses.getOrDefault(gc.getUnit("attack_ship"), FightStats.zero()), FightStats.cst(0.3)));
if(off.hasRareAttackBoost())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(+0.1));
if(off.hasEpicAttackBoost())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(+0.2));
if(off.getOlympicSwordGrepolympiaSummerLevel()!=0)
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(+0.05*off.getOlympicSwordGrepolympiaSummerLevel()));
if(off.getAresRageLevel()!=0)
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(+0.01*off.getAresRageLevel()));
if(off.getBloodlust()!=0)
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(0.05+0.01*Math.floorDiv(off.getBloodlust(),200)));
if(off.hasFairWind())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.naval(0.1));
if(off.hasDesire())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(-0.1));
if(off.hasStrengthOfHeroes())
//XXX check if working
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(0.1));
if(off.hasEffortOfTheHuntress())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.ofType(FightType.DISTANCE,0.15));
if(off.hasStrategyBreach())
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.naval(-0.5));
// Units
FightStats total = FightStats.zero();
for(Unit u : gc.getUnits()) {
// total = total + ucount * ((1+bonusA+bonusB) * ustats)
total = FightStats.add(total,
FightStats.prod(off.unitCount(u),
FightStats.mul(
FightStats.add(FightStats.one(),everyoneStatsBonus,unitsBonuses.getOrDefault(u, FightStats.zero()))
, makeOffStats(u))
));
}
if(off.getAresArmyFurySpent()!=0)
// Ading aresarmy/25 spartiates
total = FightStats.add(total,
FightStats.prod(Math.floorDiv(off.getAresArmyFurySpent(), 25),
FightStats.mul(
FightStats.add(FightStats.one(),everyoneStatsBonus,unitsBonuses.getOrDefault(gc.getUnit("spartoi"), FightStats.zero()))
, makeOffStats(gc.getUnit("spartoi")))
));
// HeroStat
if(off.getHero() != null) {
total = FightStats.add(total, new FightStats(
off.getHero().getHackDef() * (1.0+0.1*off.getHeroLevel()),
off.getHero().getPierceDef() * (1.0+0.1*off.getHeroLevel()),
off.getHero().getDistanceDef() * (1.0+0.1*off.getHeroLevel()),
0.0
));
}
// Bonuses
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.cst(off.getLuck()/100.0));
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.cst(-(100.0-off.getMorale())/100.0));
return total;
}
public static FightStats makeOffStats(Unit u) {
if(u instanceof TerrestrialUnit) {
TerrestrialUnit tu = (TerrestrialUnit)u;
return FightStats.ofType(tu.getAttackType(), tu.getAttack());
}else if(u instanceof NavalUnit) {
NavalUnit nu = (NavalUnit)u;
return new FightStats(0.0, 0.0, 0.0, nu.getAttack());
}
throw new UnsupportedOperationException("I don't know how to manage units of type "+u.getClass().getName());
}
public static List<Unit> relevantDefUnits(GameConfig gc) {
return gc.getUnits().stream().toList();
}
public static List<Unit> relevantOffUnits(GameConfig gc) {
return gc.getUnits().stream().toList();
}
public static class FightResult {
Map<String, Integer> def_losses;
@@ -1,20 +1,14 @@
package com.bernard.greposimu.engine.game;
import com.bernard.greposimu.model.game.GameData;
import com.bernard.greposimu.model.game.GameConfig;
public class Game {
GameData data;
public Heroes heroes;
public Buildings buildings;
public Fight fight;
GameConfig data;
public Game(GameData data) {
public Game(GameConfig data) {
this.data = data;
this.heroes = new Heroes(this);
this.buildings = new Buildings(this);
this.fight = new Fight(this);
}
}
@@ -2,47 +2,33 @@ package com.bernard.greposimu.engine.game;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
import com.bernard.greposimu.model.FightStats;
import com.bernard.greposimu.model.game.Hero;
import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.game.units.Hero;
import com.bernard.greposimu.model.game.units.Unit;
public class Heroes {
Game g;
public Heroes(Game g) {
this.g = g;
}
/**
* Compute the bonus applied to each unit
* @param hero The uid of the hero
* @param hero The hero object to consider
* @param level The level of the hero
* @param off true for the off stat, false for the def stat
* @return For each unit, the bonus that should be applied to it
*/
public Map<String,FightStats> heroFightBonuses(String hero, int level, boolean off){
Map<String,FightStats> bonuses = new HashMap<>();
public static Map<Unit, FightStats> heroFightBonuses(GameConfig gc, Hero hero, int level, boolean off){
if(hero == null)
return new HashMap<>();
double bonus = 0.0;
Hero heroD = g.data.heroes.get(hero);
if(heroD != null && heroD.description_args != null && heroD.description_args.containsKey("1")) {
Hero.DescriptionArgs args = heroD.description_args.get("1");
if(args.unit.equals("%"))
bonus = args.value + level * args.level_mod;
else
throw new UnsupportedOperationException("I don't know about unit "+args.unit);
}
Map<String,FightStats> bonuses = new HashMap<>();
double bonus = hero.getPowerBaseValue() + level * hero.getPowerValuePerLevel();
FightStats appliedBonus = null;
switch(hero) {
switch(hero.getId()) {
case "agamemnon":
appliedBonus = FightStats.terrestre(bonus);
bonuses.put("hoplite", appliedBonus);
@@ -59,7 +45,7 @@ public class Heroes {
case "deimos":
appliedBonus = FightStats.cst(bonus);
if(off)
for(String uid : g.data.units.keySet())bonuses.put(uid,appliedBonus);
for(Unit u : gc.getUnits())bonuses.put(u.getId(),appliedBonus);
break;
case "hector":
appliedBonus = FightStats.terrestre(bonus);
@@ -73,11 +59,11 @@ public class Heroes {
case "leonidas":
appliedBonus = FightStats.terrestre(bonus);
if(!off)
for(String uid : g.data.units.keySet())bonuses.put(uid,appliedBonus);
for(Unit u : gc.getUnits())bonuses.put(u.getId(),appliedBonus);
break;
case "mihalis":
appliedBonus = new FightStats(bonus, bonus, 0.0, 0.0);
for(String uid : g.data.units.keySet())bonuses.put(uid,appliedBonus);
for(Unit u : gc.getUnits())bonuses.put(u.getId(),appliedBonus);
break;
case "medea":
appliedBonus = FightStats.terrestre(bonus);
@@ -103,17 +89,17 @@ public class Heroes {
break;
case "urephon":
appliedBonus = FightStats.terrestre(bonus);
for(String uid : g.data.units.keySet())if(g.data.units.get(uid).isMythological())bonuses.put(uid,appliedBonus);
for(Unit u : gc.getUnits())if(u.isMythological())bonuses.put(u.getId(),appliedBonus);
break;
case "zuretha":
appliedBonus = new FightStats(0.0, 0.0, 0.0, bonus);
for(String uid : g.data.units.keySet())bonuses.put(uid,appliedBonus);
for(Unit u : gc.getUnits())bonuses.put(u.getId(),appliedBonus);
break;
default:
// No buff
break;
}
return bonuses;
return bonuses.keySet().stream().collect(Collectors.toMap(k -> gc.getUnit(k), k -> bonuses.get(k)));
}
}
@@ -1,57 +0,0 @@
package com.bernard.greposimu.engine.json;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.ContextualDeserializer;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
public class MapJsonDeserializer extends StdDeserializer<Map<String,?>> implements ContextualDeserializer{
private static final long serialVersionUID = -888029778299077908L;
private JavaType type;
public MapJsonDeserializer() {
super(Object.class);
}
@Override
public JsonDeserializer<?> createContextual(DeserializationContext ctxt, BeanProperty property) throws JsonMappingException{
MapJsonDeserializer deserializer = new MapJsonDeserializer();
if(property != null)
deserializer.type = property.getType().containedType(1);
else
deserializer.type = ctxt.getContextualType();
return deserializer;
}
@Override
public Map<String,?> deserialize(JsonParser parser, DeserializationContext deserializationContext) throws IOException {
JsonNode node = parser.getCodec().readTree(parser);
if(node.isNull() || (node.isArray() && node.isEmpty()))
return new HashMap<>();
else if(node.isObject()) {
ObjectCodec codec = parser.getCodec();
Map<String,Object> output = new HashMap<>();
java.util.Iterator<Entry<String, JsonNode>> it = node.fields();
while(it.hasNext()) {
Entry<String, JsonNode> entry = it.next();
output.put(entry.getKey(), entry.getValue().traverse(codec).readValueAs(this.type.getRawClass()));
}
return output;
} else
throw MismatchedInputException.from(parser, Map.class, "JSON is neither an empty array nor a map");
}
}
@@ -1,118 +1,266 @@
package com.bernard.greposimu.model;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import com.bernard.greposimu.Utils;
import com.bernard.greposimu.model.game.units.Hero;
import com.bernard.greposimu.model.game.units.Unit;
public class DefContext {
// unitID -> number of units
public Map<String, Integer> units;
public String hero;
public int heroLevel;
// UNITS
// Units unrelated to the attacker
Map<Unit, Integer> otherUnits;
// Units owned by allies of the attacker
Map<Unit, Integer> alliedUnits;
// Units owned by the attacher
Map<Unit, Integer> selfUnits;
public int wallLevel;
public boolean hasTower;
// HEROS
Hero hero = null;
int heroLevel = 0;
public Set<String> powers;
public Set<String> researches;
// BUILDINGS
int wallLevel = 0;
boolean hasTower=false;
public Set<String> counsellors;
// RESEARCHES
boolean divineSelection= false, phalanx = false, ram=false;
public boolean nightBonus;
// COUNSELLORS
boolean commander= false;
boolean priest = false;
boolean captain = false;
public DefContext() {
this.units = new HashMap<>();
this.hero = null;
this.heroLevel = 0;
this.wallLevel = 0;
this.hasTower = false;
this.powers = new HashSet<>();
this.researches = new HashSet<>();
this.counsellors = new HashSet<>();
this.nightBonus = false;
// EFFECTS
// PC x2
boolean acumen = false;
// PC x4
boolean divineSenses= false;
// Attq +10%, Def -10%
boolean myrmidionAttack= false;
// Remparts +1, Milice+5/farm (level max 25)
boolean trojanDefense= false;
// Def +5%
boolean defenseBoost= false;
// Def -10%
boolean defensePenalty= false;
// Def +5%
boolean longtermDefenseBoost= false;
// PC +50%
boolean assassinsAcumen= false;
// Def +5%
boolean rareDefenseBoost= false;
// Def +10%
boolean epicDefenseBoost= false;
// +50% PC
boolean missionsPower4= false;
// PC+50% (sauf BC, transport)
boolean divineBattleStrategyRare= false;
// PC+100% (sauf BC, transport)
boolean divineBattleStrategyEpic= false;
// PC+50% against naval (sauf BC,transports)
boolean navalBattleStrategyRare= false;
// PC+100% against naval (sauf BC, transport)
boolean navalBattleStrategyEpic= false;
// PC+50% against terrestres
boolean landBattleStrategyRare= false;
// PC+100% against terrestre
boolean landBattleStrategyEpic= false;
// Def -10%
boolean narcissism= false;
// PC+10%*level
int olympicSensesGrepolympiaSummerLevel = 0;
// Def +5%*level
int olympicTorchGrepolympiaSummerLevel = 0;
// Df +0.7%*level
int soteriasShrineLevel = 0;
// BONUSES
boolean nightBonus = false;
public DefContext(Map<Unit, Integer> units, Hero hero, int heroLevel, int wallLevel, boolean hasTower,
Set<String> powers, int soteriasShrinePowerLevel, int olympicTorchGrepolympiaSummerLevel, int olympicSensesGrepolympiaSummerLevel,
Set<String> researches, Set<String> counsellors, boolean nightBonus) {
this(units,Map.of(),Map.of(),hero,heroLevel,wallLevel,hasTower,powers,soteriasShrinePowerLevel,olympicTorchGrepolympiaSummerLevel,olympicSensesGrepolympiaSummerLevel,researches,counsellors,nightBonus);
}
public Map<String, Integer> getUnits() {
return units;
public DefContext(Map<Unit, Integer> otherUnits, Map<Unit, Integer> alliedUnits, Map<Unit, Integer> selfUnits, Hero hero, int heroLevel, int wallLevel, boolean hasTower,
Set<String> powers, int soteriasShrinePowerLevel, int olympicTorchGrepolympiaSummerLevel, int olympicSensesGrepolympiaSummerLevel, Set<String> researches, Set<String> counsellors, boolean nightBonus) {
this.otherUnits = otherUnits;
this.alliedUnits = alliedUnits;
this.selfUnits = selfUnits;
this.hero = hero;
this.heroLevel = heroLevel;
this.wallLevel = wallLevel;
this.hasTower = hasTower;
if(powers.contains("acumen"))this.acumen = true;
if(powers.contains("divine_senses"))this.divineSenses = true;
if(powers.contains("myrmidion_attack"))this.myrmidionAttack = true;
if(powers.contains("trojan_defense"))this.trojanDefense = true;
if(powers.contains("defense_boost"))this.defenseBoost = true;
if(powers.contains("defense_penalty"))this.defensePenalty = true;
if(powers.contains("longterm_defense_boost"))this.longtermDefenseBoost = true;
if(powers.contains("assassins_acumen"))this.assassinsAcumen = true;
if(powers.contains("rare_defense_boost"))this.rareDefenseBoost = true;
if(powers.contains("epic_defense_boost"))this.epicDefenseBoost = true;
if(powers.contains("olympic_torch"))this.olympicTorchGrepolympiaSummerLevel = olympicTorchGrepolympiaSummerLevel;
if(powers.contains("olympic_senses"))this.olympicSensesGrepolympiaSummerLevel = olympicSensesGrepolympiaSummerLevel;
if(powers.contains("missions_power_4"))this.missionsPower4 = true;
if(powers.contains("divine_battle_strategy_rare"))this.divineBattleStrategyRare = true;
if(powers.contains("divine_battle_strategy_epic"))this.divineBattleStrategyEpic = true;
if(powers.contains("naval_battle_strategy_rare"))this.navalBattleStrategyRare = true;
if(powers.contains("naval_battle_strategy_epic"))this.navalBattleStrategyEpic = true;
if(powers.contains("land_battle_strategy_rare"))this.landBattleStrategyRare = true;
if(powers.contains("land_battle_strategy_epic"))this.landBattleStrategyEpic = true;
if(powers.contains("soterias_shrine"))this.soteriasShrineLevel = soteriasShrinePowerLevel;
if(powers.contains("narcissism"))this.narcissism = true;
if(researches.contains("divine_selection"))this.divineSelection = true;
if(researches.contains("phalanx"))this.phalanx = true;
if(researches.contains("ram"))this.ram = true;
if(counsellors.contains("commander"))this.commander = true;
if(counsellors.contains("priest"))this.priest = true;
if(counsellors.contains("captain"))this.captain = true;
this.nightBonus = nightBonus;
}
public String getHero() {
public int unitCount(Unit u) {
return
Optional.ofNullable(otherUnits.getOrDefault(u,0)).orElse(0) +
Optional.ofNullable(alliedUnits.getOrDefault(u,0)).orElse(0) +
Optional.ofNullable(selfUnits.getOrDefault(u,0)).orElse(0);
}
public Hero getHero() {
return hero;
}
public int getHeroLevel() {
return heroLevel;
}
public int getWallLevel() {
return wallLevel;
}
public boolean isHasTower() {
public boolean hasTower() {
return hasTower;
}
public Set<String> getPowers() {
return powers;
}
public Set<String> getResearches() {
return researches;
}
public Set<String> getCounsellors() {
return counsellors;
}
public Map<String, Boolean> getPowersAsMap() {
return Utils.setToMap(powers);
}
public Map<String, Boolean> getResearchesAsMap() {
return Utils.setToMap(researches);
}
public Map<String, Boolean> getCounsellorsAsMap() {
return Utils.setToMap(counsellors);
}
public boolean isNightBonus() {
return nightBonus;
}
public void setHero(String hero) {
this.hero = hero;
public boolean hasDivineSelection() {
return divineSelection;
}
public void setHeroLevel(int heroLevel) {
this.heroLevel = heroLevel;
public boolean hasPhalanx() {
return phalanx;
}
public void setWallLevel(int wallLevel) {
this.wallLevel = wallLevel;
public boolean hasRam() {
return ram;
}
public void setHasTower(boolean hasTower) {
this.hasTower = hasTower;
public boolean hasCommander() {
return commander;
}
public void setNightBonus(boolean nightBonus) {
this.nightBonus = nightBonus;
public boolean hasPriest() {
return priest;
}
@Override
public String toString() {
return "DefContext [units=" + units + ", heros=" + hero + ", herosLevel=" + heroLevel + ", wallLevel="
+ wallLevel + ", hasTower=" + hasTower + ", powers=" + powers + ", researches=" + researches
+ ", counsellors=" + counsellors + ", nightBonus=" + nightBonus + "]";
public boolean hasCaptain() {
return captain;
}
public boolean hasAcumen() {
return acumen;
}
public boolean hasDivineSenses() {
return divineSenses;
}
public boolean hasMyrmidionAttack() {
return myrmidionAttack;
}
public boolean hasTrojanDefense() {
return trojanDefense;
}
public boolean hasDefenseBoost() {
return defenseBoost;
}
public boolean hasDefensePenalty() {
return defensePenalty;
}
public boolean hasLongtermDefenseBoost() {
return longtermDefenseBoost;
}
public boolean hasAssassinsAcumen() {
return assassinsAcumen;
}
public boolean hasRareDefenseBoost() {
return rareDefenseBoost;
}
public boolean hasEpicDefenseBoost() {
return epicDefenseBoost;
}
public boolean hasMhassionsPower4() {
return missionsPower4;
}
public boolean hasDivineBattleStrategyRare() {
return divineBattleStrategyRare;
}
public boolean hasDivineBattleStrategyEpic() {
return divineBattleStrategyEpic;
}
public boolean hasNavalBattleStrategyRare() {
return navalBattleStrategyRare;
}
public boolean hasNavalBattleStrategyEpic() {
return navalBattleStrategyEpic;
}
public boolean hasLandBattleStrategyRare() {
return landBattleStrategyRare;
}
public boolean hasLandBattleStrategyEpic() {
return landBattleStrategyEpic;
}
public int getOlympicSensesGrepolympiaSummerLevel() {
return olympicSensesGrepolympiaSummerLevel;
}
public int getOlympicTorchGrepolympiaSummerLevel() {
return olympicTorchGrepolympiaSummerLevel;
}
public int getSoteriasShrineLevel() {
return soteriasShrineLevel;
}
public boolean hasNarcissism() {
return narcissism;
}
public static final List<String> POWERS = List.of("acumen", "divine_senses", "myrmidion_attack", "trojan_defense",
"defense_boost", "defense_penalty", "longterm_defense_boost", "assassins_acumen", "rare_defense_boost",
"epic_defense_boost", "olympic_torch", "olympic_senses", "missions_power_4", "divine_battle_strategy_rare",
"divine_battle_strategy_epic", "naval_battle_strategy_rare", "naval_battle_strategy_epic",
"land_battle_strategy_rare", "land_battle_strategy_epic", "soterias_shrine", "narcissism");
public static final List<String> RESEARCHES = List.of("divine_selection","phalanx","ram");
public static final List<String> COUNSELLORS = List.of("priest","commander","captain");
}
@@ -1,28 +0,0 @@
package com.bernard.greposimu.model;
public enum Dieu {
ZEUS("Zeus","zeus"),
POSEIDON("Poséidon","poseidon"),
HERA("Héra","hera"),
ATHENA("Athéna","athena"),
HADES("Hadès","hades"),
ARTEMIS("Artémis","artemis"),
APHRODITE("Aphrodite","aphrodite"),
ARES("Arès","ares");
String nom;
String pname;
Dieu(String nom,String pname){
this.nom = nom;
this.pname = pname;
}
public String getNom() {
return nom;
}
public String getPname() {
return pname;
}
}
@@ -2,6 +2,8 @@ package com.bernard.greposimu.model;
import java.util.Arrays;
import com.bernard.greposimu.model.game.units.FightType;
public class FightStats implements Cloneable{
public double hack;
public double pierce;
@@ -15,6 +17,19 @@ public class FightStats implements Cloneable{
this.ship = ship;
}
public static final FightStats ofType(FightType type, double value) {
switch(type) {
case HACK:
return new FightStats(value, 0.0, 0.0, 0.0);
case PIERCE:
return new FightStats(0.0, value, 0.0, 0.0);
case DISTANCE:
return new FightStats(0.0, 0.0, value, 0.0);
default:
return null;
}
}
public static final FightStats zero() {
return new FightStats(0, 0, 0, 0);
}
@@ -27,6 +42,10 @@ public class FightStats implements Cloneable{
return new FightStats(value, value, value, 0.0);
}
public static final FightStats naval(double value) {
return new FightStats(0.0, 0.0, 0.0, value);
}
public static final FightStats cst(double value) {
return new FightStats(value, value, value, value);
}
@@ -51,6 +70,22 @@ public class FightStats implements Cloneable{
return new FightStats(k * b.hack, k * b.pierce, k * b.distance, k * b.ship);
}
public double getHack() {
return hack;
}
public double getPierce() {
return pierce;
}
public double getDistance() {
return distance;
}
public double getShip() {
return ship;
}
@Override
public FightStats clone() {
return new FightStats(hack, pierce, distance, ship);
@@ -1,87 +0,0 @@
package com.bernard.greposimu.model;
import java.util.Set;
public class Heros {
String nom;
String pname;
Set<String> appliedToNames;
boolean appTerrestre,appNavale,appMythique;
OffDefStats statsZero;
OffDefStats statsPlus;
public Heros(String nom, String pname, Set<String> appliedToNames, boolean appTerrestre, boolean appNavale, boolean appMythique,
OffDefStats statsZero, OffDefStats statsPlus) {
this.nom = nom;
this.pname = pname;
this.appliedToNames = appliedToNames;
this.appTerrestre = appTerrestre;
this.appNavale = appNavale;
this.appMythique = appMythique;
this.statsZero = statsZero;
this.statsPlus = statsPlus;
}
public OffDefStats applyToUnit(Unite unite,int level) {
if((appTerrestre && unite.terrestre) ||
(appNavale && unite.navale) ||
(appMythique && unite.mythique) ||
appliedToNames.contains(unite.pname)) {
return unite.stats.plus(unite.stats.prod(statsZero.plus(statsPlus.times(level))));
}
return unite.stats;
}
public String getNom() {
return nom;
}
public String getPname() {
return pname;
}
public Set<String> getAppliedToNames() {
return appliedToNames;
}
public boolean isAppTerrestre() {
return appTerrestre;
}
public boolean isAppNavale() {
return appNavale;
}
public boolean isAppMythique() {
return appMythique;
}
public OffDefStats getStatsZero() {
return statsZero;
}
public OffDefStats getStatsPlus() {
return statsPlus;
}
}
@@ -1,23 +1,288 @@
package com.bernard.greposimu.model;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import com.bernard.greposimu.model.game.units.Hero;
import com.bernard.greposimu.model.game.units.Unit;
public class OffContext {
// unitID -> number of units
public Map<String, Integer> units;
public String heros;
public int herosLevel;
Map<Unit, Integer> units;
public int luck;
public int morale;
Hero hero;
int heroLevel;
public Set<String> powers;
public Set<String> researches;
int luck;
int moral;
public Set<String> counsellors;
// RESEARCHES
boolean divineSelection= false, phalanx = false, ram=false, combatExperience=false;
// COUNSELLORS
boolean commander= false;
boolean priest = false;
boolean captain = false;
// EFFECTS
// PC x2
boolean acumen = false;
// PC x4
boolean divineSenses= false;
// Attq +10%, Def -10%
boolean myrmidionAttack= false;
// Attq +10%
boolean attackBoost= false;
// Attq -10%
boolean attackPenalty= false;
// Attq +10%
boolean longtermAttackBoost = false;
//XXX implement this :/
boolean luxuriousResidence = false;
// BF attq +10%
boolean attack_ship_attack_boost_small = false;
// BF attq +20%
boolean attack_ship_attack_boost_medium = false;
// BF attq +30%
boolean attack_ship_attack_boost_large = false;
// PC +50%
boolean assassinsAcumen= false;
// Attq +10%
boolean rareAttackBoost= false;
// Attq +20%
boolean epicAttackBoost= false;
// Attq +5%*level
int olympicSwordGrepolympiaSummerLevel = 0;
// PC+10%*level
int olympicSensesGrepolympiaSummerLevel = 0;
// +50% PC
boolean missionsPower4= false;
// PC+50% (sauf BC, transport)
boolean divineBattleStrategyRare= false;
// PC+100% (sauf BC, transport)
boolean divineBattleStrategyEpic= false;
// PC+50% against naval (sauf BC,transports)
boolean navalBattleStrategyRare= false;
// PC+100% against naval (sauf BC, transport)
boolean navalBattleStrategyEpic= false;
// PC+50% against terrestres
boolean landBattleStrategyRare= false;
// PC+100% against terrestre
boolean landBattleStrategyEpic= false;
// Attq +1%*level
int aresRageLevel = 0;
// SPELLS
// Add one sparte for every 25 fury used
int aresArmyFurySpent = 0;
// Attq +5% +(1% for each 200 fury spent)
// PC + 1% for each 100 fury spent
int bloodlustFurySpent = 0;
// Attq naval +10%
boolean fairWind = false;
// Attq -10%
boolean desire = false;
// Terr + aer +10% attq
boolean strengthOfHeroes = false;
// Attq distance +15%
boolean effortOfTheHuntress = false;
boolean strategyBreach = false;
boolean allianceModifier = false;
public OffContext(Map<Unit, Integer> units, Hero hero, int heroLevel, int luck, int moral,
Set<String> powers, int olympicSwordGrepolympiaSummerLevel, int olympicSensesGrepolympiaSummerLevel,
int aresRageLevel, int aresArmyFurySpent, int bloodlustFurySpent, Set<String> researches, Set<String> counsellors,
boolean strategyBreach, boolean allianceModifier) {
this.units = units;
this.hero = hero;
this.heroLevel = heroLevel;
this.luck = luck;
this.moral = moral;
if(powers.contains("acumen"))this.acumen = true;
if(powers.contains("divine_senses"))this.divineSenses = true;
if(powers.contains("myrmidion_attack"))this.myrmidionAttack = true;
if(powers.contains("attack_boost"))this.attackBoost = true;
if(powers.contains("attack_penalty"))this.attackPenalty = true;
if(powers.contains("longterm_attack_boost"))this.longtermAttackBoost = true;
if(powers.contains("attack_ship_attack_boost_small"))this.attack_ship_attack_boost_small = true;
if(powers.contains("attack_ship_attack_boost_medium"))this.attack_ship_attack_boost_medium = true;
if(powers.contains("attack_ship_attack_boost_large"))this.attack_ship_attack_boost_large = true;
if(powers.contains("assassins_acumen"))this.assassinsAcumen = true;
if(powers.contains("rare_attack_boost"))this.rareAttackBoost = true;
if(powers.contains("epic_attack_boost"))this.epicAttackBoost = true;
if(powers.contains("olympic_sword"))this.olympicSwordGrepolympiaSummerLevel = olympicSwordGrepolympiaSummerLevel;
if(powers.contains("olympic_senses"))this.olympicSensesGrepolympiaSummerLevel = olympicSensesGrepolympiaSummerLevel;
if(powers.contains("missions_power_4"))this.missionsPower4 = true;
if(powers.contains("divine_battle_strategy_rare"))this.divineBattleStrategyRare = true;
if(powers.contains("divine_battle_strategy_epic"))this.divineBattleStrategyEpic = true;
if(powers.contains("naval_battle_strategy_rare"))this.navalBattleStrategyRare = true;
if(powers.contains("naval_battle_strategy_epic"))this.navalBattleStrategyEpic = true;
if(powers.contains("land_battle_strategy_rare"))this.landBattleStrategyRare = true;
if(powers.contains("land_battle_strategy_epic"))this.landBattleStrategyEpic = true;
if(powers.contains("ares_rage"))this.aresRageLevel = aresRageLevel;
if(powers.contains("ares_army"))this.aresArmyFurySpent = aresArmyFurySpent;
if(powers.contains("bloodlust"))this.bloodlustFurySpent = bloodlustFurySpent;
if(powers.contains("fair_wind"))this.fairWind = true;
if(powers.contains("desire"))this.desire = true;
if(powers.contains("effort_of_the_huntress"))this.effortOfTheHuntress = true;
if(powers.contains("strength_of_heroes"))this.strengthOfHeroes = true;
if(researches.contains("divine_selection"))this.divineSelection = true;
if(researches.contains("phalanx"))this.phalanx = true;
if(researches.contains("ram"))this.ram = true;
if(researches.contains("combat_experience"))this.combatExperience = true;
if(counsellors.contains("commander"))this.commander = true;
if(counsellors.contains("priest"))this.priest = true;
if(counsellors.contains("captain"))this.captain = true;
this.strategyBreach = strategyBreach;
this.allianceModifier = allianceModifier;
}
public int unitCount(Unit u) {
return Optional.ofNullable(units.getOrDefault(u,0)).orElse(0);
}
public Map<Unit, Integer> getUnits() {
return units;
}
public Hero getHero() {
return hero;
}
public int getHeroLevel() {
return heroLevel;
}
public int getLuck() {
return luck;
}
public int getMorale() {
return moral;
}
public boolean hasDivineSelection() {
return divineSelection;
}
public boolean hasPhalanx() {
return phalanx;
}
public boolean hasRam() {
return ram;
}
public boolean hasCombatExperience() {
return combatExperience;
}
public boolean hasCommander() {
return commander;
}
public boolean hasPriest() {
return priest;
}
public boolean hasCaptain() {
return captain;
}
public boolean hasAcumen() {
return acumen;
}
public boolean hasDivineSenses() {
return divineSenses;
}
public boolean hasMyrmidionAttack() {
return myrmidionAttack;
}
public boolean hasAttackBoost() {
return attackBoost;
}
public boolean hasAttackPenalty() {
return attackPenalty;
}
public boolean hasLongtermAttackBoost() {
return longtermAttackBoost;
}
public boolean hasLuxurious_residence() {
return luxuriousResidence;
}
public boolean hasAttack_ship_attack_boost_small() {
return attack_ship_attack_boost_small;
}
public boolean hasAttack_ship_attack_boost_medium() {
return attack_ship_attack_boost_medium;
}
public boolean hasAttack_ship_attack_boost_large() {
return attack_ship_attack_boost_large;
}
public boolean hasAssassinsAcumen() {
return assassinsAcumen;
}
public boolean hasRareAttackBoost() {
return rareAttackBoost;
}
public boolean hasEpicAttackBoost() {
return epicAttackBoost;
}
public int getOlympicSwordGrepolympiaSummerLevel() {
return olympicSwordGrepolympiaSummerLevel;
}
public int getOlympicSensesGrepolympiaSummerLevel() {
return olympicSensesGrepolympiaSummerLevel;
}
public boolean hasMhassionsPower4() {
return missionsPower4;
}
public boolean hasDivineBattleStrategyRare() {
return divineBattleStrategyRare;
}
public boolean hasDivineBattleStrategyEpic() {
return divineBattleStrategyEpic;
}
public boolean hasNavalBattleStrategyRare() {
return navalBattleStrategyRare;
}
public boolean hasNavalBattleStrategyEpic() {
return navalBattleStrategyEpic;
}
public boolean hasLandBattleStrategyRare() {
return landBattleStrategyRare;
}
public boolean hasLandBattleStrategyEpic() {
return landBattleStrategyEpic;
}
public int getAresRageLevel() {
return aresRageLevel;
}
public int getAresArmyFurySpent() {
return aresArmyFurySpent;
}
public int getBloodlust() {
return bloodlustFurySpent;
}
public boolean hasFairWind() {
return fairWind;
}
public boolean hasDesire() {
return desire;
}
public boolean hasStrengthOfHeroes() {
return strengthOfHeroes;
}
public boolean hasEffortOfTheHuntress() {
return effortOfTheHuntress;
}
public boolean hasStrategyBreach() {
return strategyBreach;
}
public boolean hasAllianceModifier() {
return allianceModifier;
}
public static final List<String> POWERS = List.of("acumen", "divine_senses", "myrmidion_attack", "attack_boost",
"attack_penalty", "longterm_attack_boost", "attack_ship_attack_boost_small",
"attack_ship_attack_boost_medium", "attack_ship_attack_boost_large", "assassins_acumen",
"rare_attack_boost", "epic_attack_boost", "olympic_sword", "olympic_senses", "missions_power_4",
"divine_battle_strategy_rare", "divine_battle_strategy_epic", "naval_battle_strategy_rare",
"naval_battle_strategy_epic", "land_battle_strategy_rare", "land_battle_strategy_epic", "ares_rage",
"ares_army", "bloodlust", "fair_wind", "desire", "effort_of_the_huntress", "strength_of_heroes");
public static final List<String> RESEARCHES = List.of("divine_selection","phalanx","ram","combat_experience");
public static final List<String> COUNSELLORS = List.of("priest","commander","captain");
public boolean strategy_breach;
public boolean alliance_modifier;
}
@@ -1,182 +0,0 @@
package com.bernard.greposimu.model;
public class OffDefStats {
double att_hack;
double att_pierce;
double att_distance;
double ship_attack;
double def_hack;
double def_pierce;
double def_distance;
double ship_defense;
public OffDefStats(double attqCont, double attqBlan, double attqJet, double attqNav, double defCont, double defBlan,
double defJet, double defNav) {
this.att_hack = attqCont;
this.att_pierce = attqBlan;
this.att_distance = attqJet;
this.ship_attack = attqNav;
this.def_hack = defCont;
this.def_pierce = defBlan;
this.def_distance = defJet;
this.ship_defense = defNav;
}
public static final OffDefStats zero = new OffDefStats(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
public OffDefStats plus(OffDefStats other) {
return new OffDefStats(
this.att_hack + other.att_hack,
this.att_pierce + other.att_pierce,
this.att_distance + other.att_distance ,
this.ship_attack + other.ship_attack ,
this.def_hack + other.def_hack ,
this.def_pierce + other.def_pierce ,
this.def_distance + other.def_distance ,
this.ship_defense + other.ship_defense
);
}
public OffDefStats prod(OffDefStats other) {
return new OffDefStats(
this.att_hack * other.att_hack,
this.att_pierce * other.att_pierce,
this.att_distance * other.att_distance ,
this.ship_attack * other.ship_attack ,
this.def_hack * other.def_hack ,
this.def_pierce * other.def_pierce ,
this.def_distance * other.def_distance ,
this.ship_defense * other.ship_defense
);
}
public OffDefStats times(int factor) {
return new OffDefStats(
this.att_hack * factor,
this.att_pierce * factor,
this.att_distance * factor,
this.ship_attack * factor,
this.def_hack * factor,
this.def_pierce * factor,
this.def_distance * factor,
this.ship_defense * factor
);
}
public OffDefStats div(int factor) {
return new OffDefStats(
this.att_hack / factor,
this.att_pierce / factor,
this.att_distance / factor,
this.ship_attack / factor,
this.def_hack / factor,
this.def_pierce / factor,
this.def_distance / factor,
this.ship_defense / factor
);
}
public double getAttqCont() {
return att_hack;
}
public void setAttqCont(double attqCont) {
this.att_hack = attqCont;
}
public double getAttqBlan() {
return att_pierce;
}
public void setAttqBlan(double attqBlan) {
this.att_pierce = attqBlan;
}
public double getAttqJet() {
return att_distance;
}
public void setAttqJet(double attqJet) {
this.att_distance = attqJet;
}
public double getAttqNav() {
return ship_attack;
}
public void setAttqNav(double attqNav) {
this.ship_attack = attqNav;
}
public double getDefCont() {
return def_hack;
}
public void setDefCont(double defCont) {
this.def_hack = defCont;
}
public double getDefBlan() {
return def_pierce;
}
public void setDefBlan(double defBlan) {
this.def_pierce = defBlan;
}
public double getDefJet() {
return def_distance;
}
public void setDefJet(double defJet) {
this.def_distance = defJet;
}
public double getDefNav() {
return ship_defense;
}
public void setDefNav(double defNav) {
this.ship_defense = defNav;
}
public double getAtt_hack() {
return att_hack;
}
public double getAtt_pierce() {
return att_pierce;
}
public double getAtt_distance() {
return att_distance;
}
public double getShip_attack() {
return ship_attack;
}
public double getDef_hack() {
return def_hack;
}
public double getDef_pierce() {
return def_pierce;
}
public double getDef_distance() {
return def_distance;
}
public double getShip_defense() {
return ship_defense;
}
public static OffDefStats getZero() {
return zero;
}
}
@@ -1,74 +0,0 @@
package com.bernard.greposimu.model;
public class Unite {
String nom;
String pname;
int population;
int speed;
int butin;
OffDefStats stats;
boolean terrestre,navale,mythique;
Dieu dieu;
public Unite(String nom, String pname, int population, int speed, int butin, OffDefStats stats, boolean terrestre, boolean navale,
boolean mythique, Dieu dieu) {
this.nom = nom;
this.pname = pname;
this.population = population;
this.speed = speed;
this.butin = butin;
this.stats = stats;
this.terrestre = terrestre;
this.navale = navale;
this.mythique = mythique;
this.dieu = dieu;
}
public String getNom() {
return nom;
}
public String getPname() {
return pname;
}
public int getPopulation() {
return population;
}
public int getSpeed() {
return speed;
}
public int getButin() {
return butin;
}
public OffDefStats getStats() {
return stats;
}
public boolean isTerrestre() {
return terrestre;
}
public boolean isNavale() {
return navale;
}
public boolean isMythique() {
return mythique;
}
public Dieu getDieu() {
return dieu;
}
}
@@ -1,53 +0,0 @@
package com.bernard.greposimu.model.game;
import java.util.List;
import java.util.Map;
public class Building {
public String id;
public String name;
public String controller;
public Object image;
public String description;
public Object level;
public int max_level;
public int min_level;
public Object requiredBuildings;
public String coordinates;
public Resources resources;
public int pop;
public double wood_factor;
public double stone_factor;
public double iron_factor;
public double pop_factor;
public Object hide_factor;
public int points;
public double points_factor;
public int build_time;
public double build_time_factor;
public double build_time_reduction;
public Object bolt_protected;
public List<Integer> image_levels;
public Map<String,Integer> dependencies;
public Map<Integer,Integer> fixed_building_times;
public Map<Integer, LeveledFactor> level_build_time_factors;
public boolean special;
public Object resourcesFor;
public List<Object> resourcesForLevelFixed;
public Map<Integer, Double> resourcesForLevelFactor;
public List<Object> resourcesForLevelReduceFactor;
public List<Object> offset_value_map;
public double catapult_factor;
public double catapult_power;
public double def_factor_per_level;
public double storage_factor;
public double storage_pow;
public double farm_pow;
public double farm_factor;
public double thermal_pow;
public static class LeveledFactor {
public int level;
public double factor;
}
}
@@ -0,0 +1,307 @@
package com.bernard.greposimu.model.game;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import com.bernard.greposimu.Utils;
import com.bernard.greposimu.model.game.buildings.Building;
import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.powers.Power;
import com.bernard.greposimu.model.game.researches.Research;
import com.bernard.greposimu.model.game.units.Hero;
import com.bernard.greposimu.model.game.units.Unit;
import com.bernard.greposimu.model.game.util.Identified;
import com.bernard.greposimu.model.game.util.Resources;
import com.bernard.greposimu.model.game.util.UnitResources;
import com.bernard.greposimu.model.simulator.data.CastedPower;
public class GameConfig {
Set<God> gods;
// Non-hero units
Set<Unit> units;
Set<Hero> heroes;
Set<Research> researches;
Set<Power> powers;
public GameConfig(Set<God> gods, Set<Unit> units, Set<Hero> heroes, Set<Research> researches, Set<Power> powers) {
this.gods = gods;
this.units = units;
this.heroes = heroes;
this.researches = researches;
this.powers = powers;
}
public Set<God> getGods() {
return gods;
}
public Set<Unit> getUnits() {
return Collections.unmodifiableSet(this.units);
}
public Unit getUnit(String id) {
return Utils.throwingGetIdentified("unit",this.units, id);
}
public Set<Hero> getHeroes() {
return Collections.unmodifiableSet(this.heroes);
}
public Hero getHero(String id) {
return Utils.throwingGetIdentified("hero",this.heroes, id);
}
public Set<Research> getResearches() {
return Collections.unmodifiableSet(this.researches);
}
public Research getResearch(String id) {
return Utils.throwingGetIdentified("research",this.researches, id);
}
public Building getBuilding(String bid) {
return Utils.throwingGetIdentified("building", Set.of(Building.values()), bid);
}
public God getGod(String god) {
return Utils.throwingGetIdentified("god", this.gods, god);
}
public Set<Building> getBuildings() {
return Set.of(Building.values());
}
public Set<Power> getPowers() {
return powers;
}
public Power getPower(String pid){
System.out.println(powers.stream().map(Power::getId).sorted().collect(Collectors.joining("\n")));
return Utils.throwingGetIdentified("power", powers, pid);
}
@SuppressWarnings("unchecked")
public <T extends Identified> T getIdentified(Class<T> clazz, String id) {
if(Unit.class.isAssignableFrom(clazz))
return (T)this.getUnit(id);
if(Hero.class.isAssignableFrom(clazz))
return (T)this.getHero(id);
if(Research.class.isAssignableFrom(clazz))
return (T)this.getResearch(id);
if(God.class.isAssignableFrom(clazz))
return (T)this.getGod(id);
if(Building.class.isAssignableFrom(clazz))
return (T)this.getBuilding(id);
if(Power.class.isAssignableFrom(clazz))
return (T)this.getPower(id);
throw new UnsupportedOperationException("Cannot get identified object of class "+clazz.getName());
}
public static int getTotalPop(int farmLevel, boolean thermal, boolean charrue, boolean pygmallion, int bonus) {
int tot = Building.getFarmPopulation(farmLevel);
if(thermal)tot = (int)Math.floor(tot*1.1);
if(charrue)tot += 200;
if(pygmallion)tot += 5*farmLevel;
tot += bonus;
return tot;
}
/*********************/
/* COMPUTING METHODS */
/*********************/
private double callOfTheOceanBonus = 0.5;
private double fertilityImprovementBonus = 0.5;
private double shipwrightBonus = 0.1;
private double instructorBonus = 0.1;
private double conscriptionBonus = 0.1;
private double mathematicsBonus = 0.1;
private double architectureBonus = 0.1;
private double craneBonus = 0.15;
private double heroBonus(Hero h, int level) { return h.getPowerBaseValue() + level*h.getPowerValuePerLevel();}
private List<Double> senateReduction = List.of(1.000 , 0.986 , 0.970 , 0.953 , 0.935 , 0.915 , 0.895 , 0.874 , 0.852 , 0.830 , 0.808 , 0.785 , 0.761 , 0.737 , 0.712 , 0.685 , 0.661 , 0.636 , 0.620 , 0.584 , 0.561 , 0.537 , 0.502 , 0.476 , 0.450);
public long getUnitBuildTime(Unit u, int barracksLevel, int navalLevel, Set<CastedPower> powers, Set<Research> researches, Hero hero, int heroLevel){
// From helpers/general_modifications.js:169
double build_time = (u.getBuildTime() * (1 - Math.pow((u.isNaval()?navalLevel:barracksLevel) - 1, 1.1) / 100));
double modification_factor_by_powers = 1;
double modification_factor_by_researches = 1;
for(CastedPower p : powers){
if (u.isNaval() && p.getPower().getId().equals("call_of_the_ocean")) {
modification_factor_by_powers -= this.callOfTheOceanBonus;
} else if (!u.isNaval() && p.getPower().getId().equals("fertility_improvement")) {
modification_factor_by_powers -= this.fertilityImprovementBonus;
} else if ( Set.of("unit_order_boost", "longterm_unit_order_boost", "assassins_unit_order_boost", "mourning", "missions_power_2")
.contains(p.getPower().getId())) {
modification_factor_by_powers -= Integer.parseInt((String)p.getConfiguration().get("percent"), 10) / 100.0;
} else if (p.getPower().getId().equals("mourning")) {
modification_factor_by_powers += Integer.parseInt((String)p.getConfiguration().get("percent"), 10) / 100.0;
} else if(p.getPower().getId().equals("great_arming")) {
modification_factor_by_powers *= (1 - Integer.parseInt((String)p.getConfiguration().get("percent"), 10) / 100.0);
}
// Alliance boost powers
if (p.getPower().getId().equals("unit_order_boost_alliance") ||
p.getPower().getId().equals("unit_order_boost_alliance_hera")) {
String type = (String)p.getConfiguration().getOrDefault("type", "");
// The configured type must either be "all" or match the type of the current unit
if (type.equals("all") || (u.isNaval() ? type.equals("naval") : type.equals("ground"))) {
modification_factor_by_powers *= 0.01 * (100 - ((Integer)p.getConfiguration().getOrDefault("percent", 0)));
}
}
}
if (u.isNaval() && researches.contains(this.getResearch("shipwright"))) {
modification_factor_by_researches -= shipwrightBonus;
}
if (!u.isNaval() && researches.contains(this.getResearch("instructor"))) {
modification_factor_by_researches -= instructorBonus;
}
build_time *= modification_factor_by_powers * modification_factor_by_researches;
//TODO implement augmentation bonus from benefits
//build_time *= this.getCollection("benefits").getAugmentationBonusForUnitBuildTime();
// Code in features/benefits/collections/benefits.js:12
//TODO implement augmentation bonus from world boosts
//build_time *= this.getCollection('world_boosts').getWorldBoostFactorForUnitRecruitTime(u);
// Code in collections/world_boosts.js:16
return Math.max(1, Math.round(build_time));
}
public UnitResources getUnitBuildResources(Unit u, int barracksLevel, int navalLevel, Set<CastedPower> powers, Set<Research> researches,Hero hero,int heroLevel){
//TODO use the real application computation (helpers/general_modifications.js:221)
Resources base = u.getBuildCost();
double modification_factor = 1;
Optional<CastedPower> power;
//finished_wonders = us.last(MM.getCollections().Wonder);
if (!u.isNaval() && researches.contains(this.getResearch("conscription")))
modification_factor *= (1 - conscriptionBonus);
if (!u.isNaval()) {
power = powers.stream().filter(p -> p.getPower().getId().equals("passionate_training")).findAny();
if (power.isPresent())
modification_factor *= (1 - Integer.parseInt((String)power.get().getConfiguration().get("percent"), 10) / 100.0);
}
if (u.isNaval()) {
power = powers.stream().filter(p -> p.getPower().getId().equals("help_of_the_nereids")).findAny();
if (power.isPresent())
modification_factor *= (1 - Integer.parseInt((String)power.get().getConfiguration().get("percent"), 10) / 100.0);
}
power = powers.stream().filter(p -> p.getPower().getId().equals("great_arming")).findAny();
if (power.isPresent())
modification_factor *= (1 - Integer.parseInt((String)power.get().getConfiguration().get("percent"), 10) / 100.0);
if (u.isNaval() && researches.contains(this.getResearch("mathematics")))
modification_factor *= (1 - mathematicsBonus);
// TODO take wonders into account
// finished_wonders.hasWonder('mausoleum_of_halicarnassus')
if (u.getId().equals("hoplite") && heroLevel != 0 && hero.getId().equals("cheiron"))
modification_factor *= (1 - heroBonus(hero, heroLevel));
if (u.getId().equals("archer") && heroLevel != 0 && hero.getId().equals("philoctetes"))
modification_factor *= (1 - heroBonus(hero, heroLevel));
if (u.getId().equals("sword") && heroLevel != 0 && hero.getId().equals("odysseus"))
modification_factor *= (1 - heroBonus(hero, heroLevel));
if (u.getId().equals("attack_ship") && heroLevel != 0 && hero.getId().equals("aristotle"))
modification_factor *= (1 - heroBonus(hero, heroLevel));
if (u.getId().equals("bireme") && heroLevel != 0 && hero.getId().equals("daidalos"))
modification_factor *= (1 - heroBonus(hero, heroLevel));
if (u.getId().equals("trireme") && heroLevel != 0 && hero.getId().equals("eurybia"))
modification_factor *= (1 - heroBonus(hero, heroLevel));
if(u.isMythological()){
int favor_cost_modifier = 0;
power = powers.stream().filter(p -> p.getPower().getId().equals("favor_boost_alliance")).findAny();
if (power.isPresent())
favor_cost_modifier += (int)power.get().getConfiguration().get("percent");
if (heroLevel != 0 && hero.getId().equals("anysia"))
favor_cost_modifier += 10 + heroLevel * 1;
return new UnitResources(base.prod(modification_factor),
u.getGod(),(int)Math.ceil(u.getFavorCost() * (1 - (favor_cost_modifier/100))));//TODO modifier on favor cost
} else
return new UnitResources(base.prod(modification_factor));
}
public long getBuildingBuildingTime(Building b, int toLevel, int senateLevel, Set<CastedPower> powers, Set<Research> researches, Hero hero, int heroLevel){
//TODO check this function is correct
double modification_factor = 1.0;
//TODO take availability into consideration
// models/heroes/player_hero.js:165
if (heroLevel != 0 && hero.getId().equals("christopholus"))
modification_factor *= (1 - heroBonus(hero, heroLevel));
if (researches.contains(this.getResearch("building_crane"))) {
modification_factor -= craneBonus;
}
Optional<CastedPower> power = powers.stream().filter(p -> p.getPower().getId().endsWith("building_order_boost")).findAny();
if (power.isPresent())
modification_factor *= (1 - Integer.parseInt((String)power.get().getConfiguration().get("percent"), 10) / 100.0);
long time = (long) (b.getBuildTime(toLevel) * senateReduction.get(senateLevel-1));
time = (long) Math.floor(time * modification_factor);
if (time < 1) time = 1;
return time;
}
public Resources getBuildingBuildingResources(Building b, int toLevel, int senateLevel, Set<CastedPower> powers, Set<Research> researches, Hero hero, int heroLevel){
//TODO check this function is correct
double modification_factor = 1;
if (researches.contains(this.getResearch("architecture"))) {
modification_factor -= architectureBonus;
}
return b.getRequiredResources(toLevel).prod(modification_factor);
}
public long getBuildingTearDownTime(Building b, int toLevel, int senateLevel, Set<CastedPower> powers, Set<Research> researches, Hero hero, int heroLevel){
return 0;
}
public long getResearchTime(Research r, int academyLevel, Set<CastedPower> powers, Hero hero, int heroLevel){
double modification_factor = 1.0;
//TODO take availability into consideration
// models/heroes/player_hero.js:165
if (heroLevel != 0 && hero.getId().equals("apheledes"))
modification_factor *= (1 - heroBonus(hero, heroLevel));
long time = (long) (r.getRequiredTime() * ((100 - Math.pow(academyLevel, 1.1)) / 100));
time = (long) Math.floor(time * modification_factor);
if (time < 1) time = 1;
return time;
}
public Resources getResearchResources(Research r, int academyLevel, Set<CastedPower> powers, Hero hero, int heroLevel){
double modification_factor = 1.0;
//TODO take availability into consideration
// models/heroes/player_hero.js:165
if (heroLevel != 0 && hero.getId().equals("apheledes"))
modification_factor *= (1 - heroBonus(hero, heroLevel));
return r.getResources().prod(modification_factor);
}
}
@@ -1,37 +0,0 @@
package com.bernard.greposimu.model.game;
import java.io.StringWriter;
import java.util.Map;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.DumperOptions.FlowStyle;
import org.yaml.snakeyaml.Yaml;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public class GameData {
public Map<String,Unit> units;
public Map<String, ? extends JsonPower> powers;
public Map<String, God> gods;
public Map<String, Hero> heroes;
public Map<String, Research> researches;
public Map<String, Building> buildings;
@Override
public String toString() {
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(FlowStyle.BLOCK);
options.setPrettyFlow(true);
Yaml yaml = new Yaml(options);
StringWriter writer = new StringWriter();
yaml.dump(this, writer);
return writer.toString();
}
}
@@ -1,12 +0,0 @@
package com.bernard.greposimu.model.game;
import java.util.List;
public class God {
public String name;
public String id;
public List<Unit> units;
public List<String> powers;
public String topic;
public String description;
}
@@ -0,0 +1,81 @@
package com.bernard.greposimu.model.game;
import java.io.IOException;
import com.bernard.greposimu.model.game.buildings.Building;
import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.researches.Research;
import com.bernard.greposimu.model.game.units.Hero;
import com.bernard.greposimu.model.game.units.Unit;
import com.bernard.greposimu.model.game.util.Identified;
import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.KeyDeserializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
public class GrepoYaml extends SimpleModule {
GameConfig gc;
public GrepoYaml(GameConfig gc) {
this.gc = gc;
registerId(Unit.class);
registerId(Research.class);
registerId(Building.class);
registerId(God.class);
registerId(Hero.class);
}
private <T extends Identified> void registerId(Class<T> clazz){
this.addSerializer(clazz, new IdSerializer<T>());
this.addKeySerializer(clazz, new IdKeySerializer<T>());
this.addDeserializer(clazz, new IdDeserializer<T>(clazz));
this.addKeyDeserializer(clazz, new IdKeyDeserializer<T>(clazz));
}
public class IdSerializer<T extends Identified> extends JsonSerializer<T>{
@Override
public void serialize(T value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(value.getId());
}
}
public class IdKeySerializer<T extends Identified> extends JsonSerializer<T>{
@Override
public void serialize(T value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeFieldName(value.getId());
}
}
public class IdDeserializer<T extends Identified> extends JsonDeserializer<T> {
Class<T> clazz;
public IdDeserializer(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public T deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
String id = p.getText();
return gc.getIdentified(clazz, id);
}
}
public class IdKeyDeserializer<T extends Identified> extends KeyDeserializer {
Class<T> clazz;
public IdKeyDeserializer(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public Object deserializeKey(String key, DeserializationContext ctxt) throws IOException {
return gc.getIdentified(clazz, key);
}
}
}
@@ -1,36 +0,0 @@
package com.bernard.greposimu.model.game;
import java.util.List;
import java.util.Map;
public class Hero {
public String id;
public String category;
public String name;
public String description;
public Map<String,DescriptionArgs> description_args;
public String short_description;
public int default_level;
public int cost;
public List<Object> award_requirements;
public boolean is_naval;
public boolean exclusive;
public boolean hidden;
public String attack_type;
public int attack;
public int def_hack;
public int def_pierce;
public int def_distance;
public int speed;
public int booty;
public List<Object> preconditions;
public int max_per_attack;
public int max_per_support;
public static class DescriptionArgs {
public double value;
public double level_mod;
public String unit;
}
}
@@ -1,8 +0,0 @@
package com.bernard.greposimu.model.game;
public class Image {
public String mini;
public String small;
public String medium;
public String large;
}
@@ -1,115 +0,0 @@
package com.bernard.greposimu.model.game;
import java.math.BigInteger;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class JsonPower {
public Object effect;
public Object name;
public Object description;
public int lifetime;
public String id;
public String short_effect;
public int favor;
public int fury_percentage_cost;
public String god_id;
public BigInteger temple_level_sum_dependency;
public List<String> targets;
public boolean only_own_towns;
public boolean boost;
public boolean is_fake_power;
public List<String> area_of_effect;
public boolean destructive;
public boolean negative;
public boolean extendible;
public String power_group;
public int power_group_level;
public List<String> seeds_to;
public Image images;
public List<String> effects;
public boolean is_valid_for_happenings;
public List<String> meta_fields;
public Object meta_defaults;
public boolean removed_on_target_loss;
public boolean needs_level;
public boolean requires_god;
public boolean ignores_democritus;
public boolean display_amount;
public boolean wasteable;
public boolean is_ritual;
public boolean recreate_on_restart;
public boolean transfer_to_casual_world;
public boolean is_onetime_power;
public boolean is_upgradable;
public boolean is_capped;
public List<String> compatible_powers;
public boolean no_lifetime;
public boolean passive;
@SuppressWarnings("unchecked")
public Set<String> getTypes() {
Set<String> types = new HashSet<>();
if(name instanceof Map) {
if(((Map<String,Map<String,String>>)name).containsKey("type"))
types.addAll(((Map<String,Map<String,String>>)name).get("type").keySet());
else
name = ((Map<String,Map<String,String>>)name).get("god").get("athena");
}
if(description instanceof Map)
types.addAll(((Map<String,Map<String,String>>)description).get("type").keySet());
if(effect instanceof Map)
types.addAll(((Map<String,Map<String,String>>)effect).get("type").keySet());
return types;
}
@Override
public String toString() {
return "JsonPower [effect=" + effect + ", lifetime=" + lifetime + ", id=" + id + ", name=" + name
+ ", description=" + description + ", short_effect=" + short_effect + ", favor=" + favor
+ ", fury_percentage_cost=" + fury_percentage_cost + ", god_id=" + god_id
+ ", temple_level_sum_dependency=" + temple_level_sum_dependency + ", targets=" + targets
+ ", only_own_towns=" + only_own_towns + ", boost=" + boost + ", is_fake_power=" + is_fake_power
+ ", area_of_effect=" + area_of_effect + ", destructive=" + destructive + ", negative=" + negative
+ ", extendible=" + extendible + ", power_group=" + power_group + ", power_group_level="
+ power_group_level + ", seeds_to=" + seeds_to + ", images=" + images + ", effects=" + effects
+ ", is_valid_for_happenings=" + is_valid_for_happenings + ", meta_fields=" + meta_fields
+ ", meta_defaults=" + meta_defaults + ", removed_on_target_loss=" + removed_on_target_loss
+ ", needs_level=" + needs_level + ", requires_god=" + requires_god + ", ignores_democritus="
+ ignores_democritus + ", display_amount=" + display_amount + ", wasteable=" + wasteable
+ ", is_ritual=" + is_ritual + ", recreate_on_restart=" + recreate_on_restart
+ ", transfer_to_casual_world=" + transfer_to_casual_world + ", is_onetime_power=" + is_onetime_power
+ ", is_upgradable=" + is_upgradable + ", is_capped=" + is_capped + ", compatible_powers="
+ compatible_powers + ", no_lifetime=" + no_lifetime + ", passive=" + passive + "]";
}
public Object getEffect() {
return effect;
}
public void setEffect(Object effect) {
this.effect = effect;
}
public Object getName() {
return name;
}
public void setName(Object name) {
this.name = name;
}
public Object getDescription() {
return description;
}
public void setDescription(Object description) {
this.description = description;
}
}
@@ -1,126 +0,0 @@
package com.bernard.greposimu.model.game;
import java.util.Map;
public class Power extends JsonPower {
public String effect;
public String name;
public String description;
@SuppressWarnings("unchecked")
public Power(JsonPower power, String type) {
this.effect = (power.effect instanceof String)?(String)power.effect:((Map<String,Map<String,String>>)power.effect).get("type").get(type);
this.lifetime = power.lifetime;
this.id = power.id+"."+type;
this.name = (power.name instanceof String)?(String)power.name:((Map<String,Map<String,String>>)power.name).get("type").get(type);
this.description = (power.description instanceof String)?(String)power.description:((Map<String,Map<String,String>>)power.description).get("type").get(type);
this.short_effect = power.short_effect;
this.favor = power.favor;
this.fury_percentage_cost = power.fury_percentage_cost;
this.god_id = power.god_id;
this.temple_level_sum_dependency = power.temple_level_sum_dependency;
this.targets = power.targets;
this.only_own_towns = power.only_own_towns;
this.boost = power.boost;
this.is_fake_power = power.is_fake_power;
this.area_of_effect = power.area_of_effect;
this.destructive = power.destructive;
this.negative = power.negative;
this.extendible = power.extendible;
this.power_group = power.power_group;
this.power_group_level = power.power_group_level;
this.seeds_to = power.seeds_to;
this.images = power.images;
this.effects = power.effects;
this.is_valid_for_happenings = power.is_valid_for_happenings;
this.meta_fields = power.meta_fields;
this.meta_defaults = power.meta_defaults;
this.removed_on_target_loss = power.removed_on_target_loss;
this.needs_level = power.needs_level;
this.requires_god = power.requires_god;
this.ignores_democritus = power.ignores_democritus;
this.display_amount = power.display_amount;
this.wasteable = power.wasteable;
this.is_ritual = power.is_ritual;
this.recreate_on_restart = power.recreate_on_restart;
this.transfer_to_casual_world = power.transfer_to_casual_world;
this.is_onetime_power = power.is_onetime_power;
this.is_upgradable = power.is_upgradable;
this.is_capped = power.is_capped;
this.compatible_powers = power.compatible_powers;
this.no_lifetime = power.no_lifetime;
this.passive = power.passive;
}
public Power(JsonPower power) {
this.effect = (String) power.effect;
this.lifetime = power.lifetime;
this.id = power.id;
this.name = (String) power.name;
this.description = (String) power.description;
this.short_effect = power.short_effect;
this.favor = power.favor;
this.fury_percentage_cost = power.fury_percentage_cost;
this.god_id = power.god_id;
this.temple_level_sum_dependency = power.temple_level_sum_dependency;
this.targets = power.targets;
this.only_own_towns = power.only_own_towns;
this.boost = power.boost;
this.is_fake_power = power.is_fake_power;
this.area_of_effect = power.area_of_effect;
this.destructive = power.destructive;
this.negative = power.negative;
this.extendible = power.extendible;
this.power_group = power.power_group;
this.power_group_level = power.power_group_level;
this.seeds_to = power.seeds_to;
this.images = power.images;
this.effects = power.effects;
this.is_valid_for_happenings = power.is_valid_for_happenings;
this.meta_fields = power.meta_fields;
this.meta_defaults = power.meta_defaults;
this.removed_on_target_loss = power.removed_on_target_loss;
this.needs_level = power.needs_level;
this.requires_god = power.requires_god;
this.ignores_democritus = power.ignores_democritus;
this.display_amount = power.display_amount;
this.wasteable = power.wasteable;
this.is_ritual = power.is_ritual;
this.recreate_on_restart = power.recreate_on_restart;
this.transfer_to_casual_world = power.transfer_to_casual_world;
this.is_onetime_power = power.is_onetime_power;
this.is_upgradable = power.is_upgradable;
this.is_capped = power.is_capped;
this.compatible_powers = power.compatible_powers;
this.no_lifetime = power.no_lifetime;
this.passive = power.passive;
}
public String getEffect() {
return effect;
}
public void setEffect(String effect) {
this.effect = effect;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
@@ -1,15 +0,0 @@
package com.bernard.greposimu.model.game;
import java.util.List;
import java.util.Map;
public class Research {
public String id;
public String name;
public String description;
public List<Object> research_dependencies;
public Map<String,Integer> building_dependencies;
public Resources resources;
public int required_time;
public int research_points;
}
@@ -1,7 +0,0 @@
package com.bernard.greposimu.model.game;
public class Resources {
public int wood;
public int stone;
public int iron;
}
@@ -1,70 +0,0 @@
package com.bernard.greposimu.model.game;
import java.util.List;
import java.util.Map;
import com.bernard.greposimu.model.FightStats;
public class Unit {
public String id;
public String name;
public String name_plural;
public int speed;
public int attack;
public String description;
public Resources resources;
public int favor;
public int population;
public int build_time;
public String god_id;
public List<String> research_dependencies;
public Map<String, Integer> building_dependencies;
public boolean is_naval;
public int max_per_attack;
public int max_per_support;
public String unit_function;
public String category;
public List<Object> special_abilities;
public String passive;
public boolean is_npc_unit_only;
public int def_hack;
public int def_pierce;
public int def_distance;
public int booty;
public Object infantry;
public boolean flying;
public String attack_type;
// Naval
public int defense;
public boolean transport;
public int capacity;
public FightStats getDefStats() {
return new FightStats(def_hack, def_pierce, def_distance, defense);
}
public FightStats getAttStats() {
switch(attack_type) {
case "hack":
return new FightStats(attack, 0.0, 0.0, 0.0);
case "pierce":
return new FightStats(0.0, attack, 0.0, 0.0);
case "distance":
return new FightStats(0.0, 0.0, attack, 0.0);
}
if(is_naval)
return new FightStats(0.0, 0.0, 0.0, attack);
throw new IllegalStateException("This unit has no known attack type, and is not a ship");
}
public boolean isMythological() {
return category.equals("mythological_ground") || category.equals("mythological_naval");
}
public boolean isGround() {
return category.equals("regular_ground") || category.equals("mythological_ground");
}
public boolean isNaval() {
return category.equals("regular_naval") || category.equals("mythological_naval");
}
}
@@ -0,0 +1,91 @@
package com.bernard.greposimu.model.game.buildings;
import java.util.Map;
import com.bernard.greposimu.model.game.util.Identified;
import com.bernard.greposimu.model.game.util.Resources;
public enum Building implements Identified{
/*
* var s = ""
for(var b in GameData.buildings){
var bb = GameData.buildings[b];
s += b.toUpperCase()+'("'+b+'",'+bb.resources.wood+","+bb.wood_factor+","+bb.resources.stone+","+bb.stone_factor+","+bb.resources.iron+","+bb.iron_factor+","+bb.pop+","+bb.pop_factor+","+bb.points+","+bb.points_factor+bb.build_time+","+bb.build_time_factor+","+bb.build_time_reduction+"),\n";
}
*/
MAIN("main",6.0,2.15,2.0,2.53,2.0,2.3,1.0,1.5,100.0,1.1300,1.8,0.3,25),
HIDE("hide",200.0,1.3,400.0,1,700.0,0.9,3.0,0.5,50.0,1.2420,1.456,0,10),
PLACE("place",10.0,2,0.0,2,0.0,2,1.0,0,30.0,1.111,2.17,0,1),
LUMBER("lumber",2.6,1.9,2.0,2.1,1.49,2.1,1.0,1.25,20.0,1.1120,1.9,1,40),
STONER("stoner",1.3,2.1,2.6,1.9,2.4,2.1,1.0,1.25,20.0,1.1300,1.62,1,40),
IRONER("ironer",5.0,1.9,2.0,2,4.0,1.8,1.0,1.25,20.0,1.1600,1.41,0.5,40),
MARKET("market",50.0,1.48,20.0,1.62,5.0,1.98,2.0,1.1,100.0,1.08480,1.6,0.4,30),
DOCKS("docks",400.0,0.9,200.0,0.98,100.0,1.1,4.0,1,60.0,1.11500,1.1,0,30),
BARRACKS("barracks",70.0,1.22,20.0,1.67,40.0,1.54,1.0,1.3,30.0,1.115900,1.25,0.5,30),
WALL("wall",400.0,0,350.0,1,200.0,1.1,2.0,1.16,30.0,1.12900,1.3,0,25),
STORAGE("storage",35.0,1.52,55.0,1.52,15.0,1.66,0.0,1,13.0,1.141800,1.23,0.4,35),
FARM("farm",8.0,1.87,5.0,2.03,1.0,2.4,0.0,0,15.0,1.12300,1.7,1,45),
ACADEMY("academy",100.0,1.21,200.0,1.1,120.0,1.2,3.0,1,60.0,1.121200,1.25,0,36),
TEMPLE("temple",500.0,0.865,900.0,0.7,600.0,0.82,5.0,1,200.0,1.083600,1.145,0,30),
THEATER("theater",8000.0,2,8000.0,2,8000.0,2,60.0,1,500.0,164800,2.17,0,1),
THERMAL("thermal",9000.0,2,6000.0,2,9000.0,2,60.0,1,500.0,164800,2.17,0,1),
LIBRARY("library",9500.0,2,7500.0,2,7000.0,2,60.0,1,500.0,164800,2.17,0,1),
LIGHTHOUSE("lighthouse",6000.0,2,10000.0,2,8000.0,2,60.0,1,500.0,164800.0,2.17,0,1),
TOWER("tower",8000.0,2,10000.0,2,6000.0,2,60.0,1,500.0,164800.0,2.17,0,1),
STATUE("statue",6000.0,2,10500.0,2,7500.0,2,60.0,1,500.0,164800.0,2.17,0,1),
ORACLE("oracle",6500.0,2,7000.0,2,9500.0,2,60.0,1,500.0,164800.0,2.17,0,1),
TRADE_OFFICE("trade_office",10500.0,2,7000.0,2,6500.0,2,60.0,1,500.0,164800.0,1,0,1);
String id;
private double wood0,woodF,stone0,stoneF,iron0,ironF,pop0,popF,pts0,ptsF,build0,buildF,buildR;
int maxLevel;
private Building(String id, double wood0, double woodF, double stone0, double stoneF, double iron0, double ironF,
double pop0, double popF, double pts0, double ptsF, double build0, double buildF, int maxLevel) {
this.id = id;
this.wood0 = wood0;
this.woodF = woodF;
this.stone0 = stone0;
this.stoneF = stoneF;
this.iron0 = iron0;
this.ironF = ironF;
this.pop0 = pop0;
this.popF = popF;
this.pts0 = pts0;
this.ptsF = ptsF;
this.build0 = build0;
this.buildF = buildF;
this.maxLevel = maxLevel;
}
@Override
public String getId() {
return id;
}
public static int getFarmPopulation(int farm) {
return (int) Math.floor(Math.pow(farm, 1.455)*14);
}
public int getRequiredPop(int level) {
return (int) Math.floor(pop0*Math.pow(level, popF));
}
public Resources getRequiredResources(int level) {
return new Resources(
(int) Math.floor(wood0*Math.pow(level, woodF)),
(int) Math.floor(stone0*Math.pow(level, stoneF)),
(int) Math.floor(iron0*Math.pow(level, ironF)));
}
public long getBuildTime(int level) {
//TODO check this, is build_time_reduction used ?
return (long) Math.floor(build0*Math.pow(level, buildF));
}
public int popInBuildings(Map<Building,Integer> buildings) {
return buildings.entrySet().stream().mapToInt(e -> e.getKey().getRequiredPop(e.getValue())).sum();
}
}
@@ -0,0 +1,54 @@
package com.bernard.greposimu.model.game.gods;
import java.util.Objects;
import com.bernard.greposimu.model.game.util.Identified;
public class God implements Identified,Comparable<God>{
String id;
String name;
public God(String id, String name) {
this.id = id;
this.name = name;
}
@Override
public String getId() {
return id;
}
public String getName() {
return name;
}
@Override
public int compareTo(God o) {
return this.getId().compareTo(o.getId());
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
God other = (God) obj;
return Objects.equals(id, other.id);
}
@Override
public String toString() {
return "God [id=" + id + ", name=" + name + "]";
}
}
@@ -0,0 +1,29 @@
package com.bernard.greposimu.model.game.powers;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import com.bernard.greposimu.model.game.gods.God;
public class FuryPower extends GodPower {
double furyProportionCost;
public FuryPower(String id, EnumSet<Target> targets, EnumSet<Target> seedsTo, String shortEffect, Power.Group powerGroup,
int powerGroupLevel, Set<String> metaFields, Map<String, Object> metaDefaults, int lifetime, EnumSet<Effect> effects,
EnumSet<Tag> tags,
Set<String> compatiblePowers, EnumSet<AreaOfEffect> areaOfEffect, String name, String effect,
String description, String configType, Map<String, String> nameM, Map<String, String> effectM,
Map<String, String> descriptionM, God god2, int favorCost, int templeLevelSumDependency,
double furyProportionCost) {
super(id, targets, seedsTo, shortEffect, powerGroup, powerGroupLevel, metaFields, metaDefaults, lifetime,
effects, tags, compatiblePowers, areaOfEffect, name,
effect, description, configType, nameM, effectM, descriptionM, god2, favorCost,
templeLevelSumDependency);
this.furyProportionCost = furyProportionCost;
}
public double getFuryProportionCost() {
return furyProportionCost;
}
}
@@ -0,0 +1,44 @@
package com.bernard.greposimu.model.game.powers;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import com.bernard.greposimu.model.game.gods.God;
public class GodPower extends Power {
God god;
int favorCost;
int templeLevelSumDependency;
public GodPower(String id, EnumSet<Target> targets, EnumSet<Target> seedsTo, String shortEffect, Power.Group powerGroup,
int powerGroupLevel, Set<String> metaFields, Map<String, Object> metaDefaults, int lifetime, EnumSet<Effect> effects,
EnumSet<Tag> tags,
Set<String> compatiblePowers, EnumSet<AreaOfEffect> areaOfEffect, String name, String effect,
String description, String configType, Map<String, String> nameM, Map<String, String> effectM,
Map<String, String> descriptionM, God god2, int favorCost, int templeLevelSumDependency) {
super(id, targets, seedsTo, shortEffect, powerGroup, powerGroupLevel, metaFields, metaDefaults, lifetime,
effects, tags, compatiblePowers, areaOfEffect, name,
effect, description, configType, nameM, effectM, descriptionM);
god = god2;
this.favorCost = favorCost;
this.templeLevelSumDependency = templeLevelSumDependency;
}
public God getGod() {
return god;
}
public int getFavorCost() {
return favorCost;
}
public int getTempleLevelSumDependency() {
return templeLevelSumDependency;
}
}
@@ -0,0 +1,164 @@
package com.bernard.greposimu.model.game.powers;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import org.springframework.lang.Nullable;
import com.bernard.greposimu.model.game.util.Identified;
public class Power implements Identified {
String id;
/**
* The target it applies to
*/
EnumSet<Target> targets;
/**
* The targets it is seeded to. For example, attack boost is targeted to towns, and is seeded to commands.
*/
EnumSet<Target> seedsTo;
@Nullable
String shortEffect;
Power.Group powerGroup;
int powerGroupLevel;
//name
Set<String> metaFields;
Map<String,Object> metaDefaults;
int lifetime;
EnumSet<Tag> tags;
EnumSet<Effect> effects;
Set<String> compatiblePowers;
EnumSet<AreaOfEffect> areaOfEffect;
// for each X, either X or XM should be null. if one XM is not null, configType should be set
String name,effect,description;
String configType;
Map<String,String> nameM,effectM,descriptionM;
//display_amount -> ignore
public static enum Tag {
EXTENDIBLE,
DISPLAY_AMOUNT,
DESTRUCTIVE,
CAPPED,
FAKE_POWER,
ONETIME_POWER,
RITUAL,
UPGRADABLE,
VALID_FOR_HAPPENINGS,
TRANSFER_TO_CASUAL_WORLD,
WASTEABLE,
NEEDS_LEVEL,
PASSIVE,
RECREATE_ON_RESTART,
REMOVED_ON_TARGET_LOSS,
REQUIRES_GOD,
NO_LIFETIME,
ONLY_OWN_TOWNS,
NEGATIVE,
IGNORES_DEMOCRITUS,
BOOST;
}
public Power(String id, EnumSet<Target> targets, EnumSet<Target> seedsTo, String shortEffect, Power.Group powerGroup,
int powerGroupLevel, Set<String> metaFields, Map<String, Object> metaDefaults, int lifetime,
EnumSet<Effect> effects, EnumSet<Tag> tags,
Set<String> compatiblePowers, EnumSet<AreaOfEffect> areaOfEffect, String name, String effect,
String description, String configType, Map<String, String> nameM, Map<String, String> effectM,
Map<String, String> descriptionM) {
this.id = id;
this.targets = targets;
this.seedsTo = seedsTo;
this.shortEffect = shortEffect;
this.powerGroup = powerGroup;
this.powerGroupLevel = powerGroupLevel;
this.metaFields = metaFields;
this.metaDefaults = metaDefaults;
this.lifetime = lifetime;
this.effects = effects;
this.tags = tags;
this.compatiblePowers = compatiblePowers;
this.areaOfEffect = areaOfEffect;
this.name = name;
this.effect = effect;
this.description = description;
this.configType = configType;
this.nameM = nameM;
this.effectM = effectM;
this.descriptionM = descriptionM;
}
public static enum AreaOfEffect {
BUILDTIME,COMMANDS,FAVOR,MILITIA,RESOURCES;
}
public static enum Effect {
GROUND,NAVAL,WALL;
}
public static enum Group {
ATTACK_BOOST,ATTACK_SHIP_ATTACK_BOOST,BATTLE_POINT_BOOST,BUILDING_BOOST,DEFENSE_BOOST,FAVOR_BOOST,RESOURCE_BOOST,UNIT_BOOST;
}
public static enum Target {
ALLIANCE,COMMAND,PLAYER,SUPPORT_COMMAND,TOWN;
}
public String getId() {
return id;
}
public EnumSet<Target> getTargets() {
return targets;
}
public EnumSet<Target> getSeedsTo() {
return seedsTo;
}
public String getShortEffect() {
return shortEffect;
}
public Power.Group getPowerGroup() {
return powerGroup;
}
public int getPowerGroupLevel() {
return powerGroupLevel;
}
public Set<String> getMetaFields() {
return metaFields;
}
public Map<String, Object> getMetaDefaults() {
return metaDefaults;
}
public int getLifetime() {
return lifetime;
}
public EnumSet<Effect> getEffects() {
return effects;
}
public EnumSet<Tag> getTags() {
return tags;
}
public Set<String> getCompatiblePowers() {
return compatiblePowers;
}
public EnumSet<AreaOfEffect> getAreaOfEffect() {
return areaOfEffect;
}
public String getName(Map<String,Object> configuration) {
return name!=null?name:(nameM.get(configuration.get(configType)));
}
public String getEffect(Map<String,Object> configuration) {
return effect!=null?effect:(effectM.get(configuration.get(configType)));
}
public String getDescription(Map<String,Object> configuration) {
return description!=null?description:(descriptionM.get(configuration.get(configType)));
}
}
@@ -0,0 +1,55 @@
package com.bernard.greposimu.model.game.queues;
import com.bernard.greposimu.model.game.buildings.Building;
import com.bernard.greposimu.model.game.util.Resources;
import com.bernard.greposimu.model.game.util.Timestamp;
public class BuildingQueueItem extends QueueItem {
public BuildingQueueItem(long buildingTime, Building building, boolean tearingDown, Timestamp beginTime,
Timestamp endTime, Resources refund, Resources cost) {
this.buildingTime = buildingTime;
this.building = building;
this.tearingDown = tearingDown;
this.beginTime = beginTime;
this.endTime = endTime;
this.refund = refund;
this.cost = cost;
}
long buildingTime;
// the building being built
Building building;
boolean tearingDown;
Timestamp beginTime;
Timestamp endTime;
Resources refund;
Resources cost;
public long getBuildingTime() {
return buildingTime;
}
public Building getBuilding() {
return building;
}
public boolean isTearingDown() {
return tearingDown;
}
public Timestamp getBeginTime() {
return beginTime;
}
public Timestamp getEndTime() {
return endTime;
}
public Resources getRefund() {
return refund;
}
public Resources getCost() {
return cost;
}
}
@@ -0,0 +1,5 @@
package com.bernard.greposimu.model.game.queues;
public class QueueItem {
}
@@ -0,0 +1,43 @@
package com.bernard.greposimu.model.game.queues;
import com.bernard.greposimu.model.game.units.Unit;
import com.bernard.greposimu.model.game.util.Timestamp;
import com.bernard.greposimu.model.game.util.UnitResources;
public class RecruitmentQueueItem extends QueueItem {
RecruitmentKind kind;
Unit unit;
// Number of units requested
int count;
// Number of units that have been done
int done;
Timestamp beginTime;
Timestamp endTime;
UnitResources refund;
UnitResources cost;
public RecruitmentQueueItem(RecruitmentKind kind, Unit unit, int count, int done, Timestamp beginTime,
Timestamp endTime, UnitResources refund, UnitResources cost) {
this.kind = kind;
this.unit = unit;
this.count = count;
this.done = done;
this.beginTime = beginTime;
this.endTime = endTime;
this.refund = refund;
this.cost = cost;
}
public static enum RecruitmentKind {
GROUND,NAVAL;
}
}
@@ -0,0 +1,44 @@
package com.bernard.greposimu.model.game.queues;
import com.bernard.greposimu.model.game.researches.Research;
import com.bernard.greposimu.model.game.util.Resources;
import com.bernard.greposimu.model.game.util.Timestamp;
public class ResearchQueueItem extends QueueItem {
// the building being built
Research research;
Timestamp beginTime;
Timestamp endTime;
Resources refund;
public ResearchQueueItem(Research research, Timestamp beginTime, Timestamp endTime, Resources refund) {
this.research = research;
this.beginTime = beginTime;
this.endTime = endTime;
this.refund = refund;
}
public Research getResearch() {
return research;
}
public Timestamp getBeginTime() {
return beginTime;
}
public Timestamp getEndTime() {
return endTime;
}
public Resources getRefund() {
return refund;
}
}
@@ -0,0 +1,65 @@
package com.bernard.greposimu.model.game.researches;
import java.util.Map;
import java.util.Set;
import com.bernard.greposimu.model.game.util.Identified;
import com.bernard.greposimu.model.game.util.Resources;
public class Research implements Identified{
String id;
String name;
String description;
Set<Research> researchDependencies;
Map<String,Integer> buildingDependencies;
Resources resources;
int requiredTime;
int researchPoints;
public Research(String id, String name, String description, Set<Research> researchDependencies,
Map<String, Integer> building_dependencies, Resources resources, int required_time, int research_points) {
this.id = id;
this.name = name;
this.description = description;
this.researchDependencies = researchDependencies;
this.buildingDependencies = building_dependencies;
this.resources = resources;
this.requiredTime = required_time;
this.researchPoints = research_points;
}
@Override
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public Set<Research> getResearchDependencies() {
return researchDependencies;
}
public Map<String, Integer> getBuildingDependencies() {
return buildingDependencies;
}
public Resources getResources() {
return resources;
}
public int getRequiredTime() {
return requiredTime;
}
public int getResearchpoints() {
return researchPoints;
}
}
@@ -0,0 +1,5 @@
package com.bernard.greposimu.model.game.units;
public enum FightType {
PIERCE,HACK,DISTANCE;
}
@@ -0,0 +1,108 @@
package com.bernard.greposimu.model.game.units;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import com.bernard.greposimu.model.game.util.Identified;
public class Hero extends TerrestrialUnit implements Comparable<Hero>{
// Zero population
// Non mythological
// No building cost/time
// no research/building dependencies
// No flight
// Terrestrial
public Hero(String id, String name, String description, int speed,
int attack, FightType attackType, int pierceDef, int hackDef,
int distanceDef, int booty, HeroCategory category, int cost, String shortDescription, double powerBaseValue, double powerValuePerLevel) {
super(id, name, description, 0, speed, false, null, null, 0, 0,
Set.of(), Map.of(), attack, attackType, pierceDef, hackDef, distanceDef, booty, false);
this.category = category;
this.cost = cost;
this.shortDescription = shortDescription;
this.powerBaseValue = powerBaseValue;
this.powerValuePerLevel = powerValuePerLevel;
}
HeroCategory category;
int cost;
String shortDescription;
double powerBaseValue;
double powerValuePerLevel;
public static enum HeroCategory implements Identified{
WAR("war"),WISDOM("wisdom");
String id;
HeroCategory(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
}
public HeroCategory getCategory() {
return category;
}
public int getCost() {
return cost;
}
public String getShortDescription() {
return shortDescription;
}
public double getPowerBaseValue() {
return powerBaseValue;
}
public double getPowerValuePerLevel() {
return powerValuePerLevel;
}
@Override
public int compareTo(Hero o) {
return this.getId().compareTo(o.getId());
}
@Override
public int hashCode() {
return Objects.hash(id);
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Hero other = (Hero) obj;
return Objects.equals(id, other.id);
}
@Override
public String toString() {
return "Hero [category=" + category + ", cost=" + cost + ", shortDescription=" + shortDescription
+ ", powerBaseValue=" + powerBaseValue + ", powerValuePerLevel=" + powerValuePerLevel + "]";
}
}
@@ -0,0 +1,47 @@
package com.bernard.greposimu.model.game.units;
import java.util.Map;
import java.util.Set;
import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.researches.Research;
import com.bernard.greposimu.model.game.util.Resources;
public class NavalUnit extends Unit {
int attack;
int defense;
public NavalUnit(String id, String name, String description, int population, int speed, boolean mythological, God god,
Resources buildCost, int favorCost, int buildTime, Set<Research> research_dependencies,
Map<String, Integer> building_dependencies,int attack, int defense) {
this.id = id;
this.name = name;
this.description = description;
this.population = population;
this.speed = speed;
this.mythological = mythological;
this.god = god;
this.buildCost = buildCost;
this.favorCost = favorCost;
this.buildTime = buildTime;
this.research_dependencies = research_dependencies;
this.building_dependencies = building_dependencies;
this.attack = attack;
this.defense = defense;
}
public int getAttack() {
return attack;
}
public int getDefense() {
return defense;
}
@Override
public boolean isGround() {
return false;
}
}
@@ -0,0 +1,79 @@
package com.bernard.greposimu.model.game.units;
import java.util.Map;
import java.util.Set;
import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.researches.Research;
import com.bernard.greposimu.model.game.util.Resources;
public class TerrestrialUnit extends Unit{
int attack;
FightType attackType;
int pierceDef;
int hackDef;
int distanceDef;
int booty;
boolean flight;
public TerrestrialUnit(String id, String name, String description, int population, int speed, boolean mythological, God god,
Resources buildCost, int favorCost, int buildTime, Set<Research> research_dependencies,
Map<String, Integer> building_dependencies,int attack, FightType attackType, int pierceDef, int hackDef, int distanceDef, int booty,
boolean flight) {
this.id = id;
this.name = name;
this.description = description;
this.population = population;
this.speed = speed;
this.mythological = mythological;
this.god = god;
this.buildCost = buildCost;
this.favorCost = favorCost;
this.buildTime = buildTime;
this.research_dependencies = research_dependencies;
this.building_dependencies = building_dependencies;
this.attack = attack;
this.attackType = attackType;
this.pierceDef = pierceDef;
this.hackDef = hackDef;
this.distanceDef = distanceDef;
this.booty = booty;
this.flight = flight;
}
public int getAttack() {
return attack;
}
public FightType getAttackType() {
return attackType;
}
public int getPierceDef() {
return pierceDef;
}
public int getHackDef() {
return hackDef;
}
public int getDistanceDef() {
return distanceDef;
}
public int getBooty() {
return booty;
}
public boolean isFlight() {
return flight;
}
@Override
public boolean isGround() {
return true;
}
}
@@ -0,0 +1,20 @@
package com.bernard.greposimu.model.game.units;
import java.util.Map;
import java.util.Set;
import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.researches.Research;
import com.bernard.greposimu.model.game.util.Resources;
public class TransportUnit extends NavalUnit {
int capacity;
public TransportUnit(String id, String name, String description, int population, int speed, boolean mythological,
God god, Resources buildCost, int favorCost, int buildTime, Set<Research> research_dependencies,
Map<String, Integer> building_dependencies, int attack, int defense, int capacity) {
super(id, name, description, population, speed, mythological, god, buildCost, favorCost, buildTime,
research_dependencies, building_dependencies, attack, defense);
this.capacity = capacity;
}
}
@@ -0,0 +1,100 @@
package com.bernard.greposimu.model.game.units;
import java.util.Map;
import java.util.Set;
import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.researches.Research;
import com.bernard.greposimu.model.game.util.Identified;
import com.bernard.greposimu.model.game.util.Resources;
public abstract class Unit implements Identified{
String id;
String name;
String description;
int population;
int speed;
boolean mythological;
God god;
Resources buildCost;
int favorCost;
int buildTime;
Set<Research> research_dependencies;
Map<String, Integer> building_dependencies;
@Override
public String getId() {
return id;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public int getPopulation() {
return population;
}
public int getSpeed() {
return speed;
}
public boolean isMythological() {
return mythological;
}
public God getGod() {
return god;
}
public Resources getBuildCost() {
return buildCost;
}
public int getFavorCost() {
return favorCost;
}
public int getBuildTime() {
return buildTime;
}
public Set<Research> getResearch_dependencies() {
return research_dependencies;
}
public Map<String, Integer> getBuilding_dependencies() {
return building_dependencies;
}
public abstract boolean isGround();
public boolean isNaval() {
return !this.isGround();
}
@Override
public String toString() {
return name;
}
}
@@ -0,0 +1,7 @@
package com.bernard.greposimu.model.game.util;
public interface Identified {
public String getId();
}
@@ -0,0 +1,38 @@
package com.bernard.greposimu.model.game.util;
public class Resources {
public static final Resources empty = new Resources(0,0,0);
int wood;
int stone;
int iron;
public Resources(int wood, int stone, int iron) {
this.wood = wood;
this.stone = stone;
this.iron = iron;
}
public int getWood() {
return wood;
}
public int getStone() {
return stone;
}
public int getIron() {
return iron;
}
public Resources prod(double p){
return new Resources((int)Math.ceil(p*wood), (int)Math.ceil(p*stone), (int)Math.ceil(p*iron));
}
@Override
public String toString() {
return "["+this.wood+";"+this.stone+";"+this.iron+"]";
}
}
@@ -0,0 +1,12 @@
package com.bernard.greposimu.model.game.util;
public class Timestamp {
long timestamp;
public Timestamp(long timestamp) {
this.timestamp = timestamp;
}
}
@@ -0,0 +1,33 @@
package com.bernard.greposimu.model.game.util;
import com.bernard.greposimu.model.game.gods.God;
public class UnitResources extends Resources {
God god;
int favor;
public UnitResources(int wood, int stone, int iron, God god, int favor) {
super(wood, stone, iron);
this.god = god;
this.favor = favor;
}
public UnitResources(int wood, int stone, int iron) {
this(wood, stone, iron, null, 0);
}
public UnitResources(Resources r, God god, int favor) {
this(r.getWood(), r.getStone(), r.getIron(),god,favor);
}
public UnitResources(Resources r) {
this(r.getWood(), r.getStone(), r.getIron(),null,0);
}
public God getGod() {
return god;
}
public int getFavor() {
return favor;
}
}
@@ -0,0 +1,70 @@
package com.bernard.greposimu.model.simulator.command;
import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.game.buildings.Building;
import com.bernard.greposimu.model.game.util.Resources;
import com.bernard.greposimu.model.simulator.data.SimulatorData;
import com.bernard.greposimu.model.simulator.data.Ville;
public class BuildCommand extends TownCommand {
Building building;
// Destination level of the build
int level;
boolean tearingDown;
public BuildCommand(GameConfig gc, int town, Building building, int level, boolean tearingDown) {
super(gc,town);
this.building = building;
this.level = level;
this.tearingDown = tearingDown;
}
@Override
public String toString() {
if(tearingDown)
return "[%d] Destroy %s from %d -> %d".formatted(this.town,this.building.name(),this.level+1,this.level);
else
return "[%d] Build %s from %d -> %d".formatted(this.town,this.building.name(),this.level-1,this.level);
}
@Override
public Resources neededResources(SimulatorData sd) {
if(this.tearingDown)return Resources.empty;
Ville v = sd.getVille(town);
return gc.getBuildingBuildingResources(
this.building,
this.level,
v.getBatiments().get(Building.MAIN),
v.getPowers(),
v.getResearches(),
v.getHero(),
v.getHeroLevel());
}
@Override
public long timeNeeded(SimulatorData sd) {
Ville v = sd.getVille(town);
if(this.tearingDown)
return gc.getBuildingTearDownTime(
this.building,
this.level,
v.getBatiments().get(Building.MAIN),
v.getPowers(),
v.getResearches(),
v.getHero(),
v.getHeroLevel());
else
return gc.getBuildingBuildingTime(
this.building,
this.level,
v.getBatiments().get(Building.MAIN),
v.getPowers(),
v.getResearches(),
v.getHero(),
v.getHeroLevel());
}
}
@@ -0,0 +1,16 @@
package com.bernard.greposimu.model.simulator.command;
import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.simulator.data.SimulatorData;
public abstract class Command {
GameConfig gc;
public Command(GameConfig gc) {
this.gc = gc;
}
public abstract long timeNeeded(SimulatorData sd);
}
@@ -0,0 +1,31 @@
package com.bernard.greposimu.model.simulator.command;
import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.game.util.Resources;
import com.bernard.greposimu.model.simulator.data.SimulatorData;
public class HideStoreCommand extends TownCommand {
int amount;
public HideStoreCommand(GameConfig gc, int town, int amount) {
super(gc,town);
this.amount = amount;
}
@Override
public String toString() {
return "[%d] Store %d iron in the hide".formatted(this.town,this.amount);
}
@Override
public Resources neededResources(SimulatorData sd) {
return new Resources(0, 0, this.amount);
}
@Override
public long timeNeeded(SimulatorData sd) {
return 0;
}
}
@@ -0,0 +1,52 @@
package com.bernard.greposimu.model.simulator.command;
import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.game.buildings.Building;
import com.bernard.greposimu.model.game.units.Unit;
import com.bernard.greposimu.model.game.util.Resources;
import com.bernard.greposimu.model.simulator.data.SimulatorData;
import com.bernard.greposimu.model.simulator.data.Ville;
public class RecruitCommand extends TownCommand{
Unit unit;
int count;
public RecruitCommand(GameConfig gc,int town, Unit unit, int count) {
super(gc,town);
this.unit = unit;
this.count = count;
}
@Override
public String toString() {
return "[%d] Recruit %d %s".formatted(this.town,this.count, this.unit.getName());
}
@Override
public Resources neededResources(SimulatorData sd) {
Ville v = sd.getVille(town);
return gc.getUnitBuildResources(
this.unit,
v.getBatiments().get(Building.BARRACKS),
v.getBatiments().get(Building.DOCKS),
v.getPowers(),
v.getResearches(),
v.getHero(),
v.getHeroLevel()).prod(this.count);
}
@Override
public long timeNeeded(SimulatorData sd) {
Ville v = sd.getVille(town);
return gc.getUnitBuildTime(
this.unit,
v.getBatiments().get(Building.BARRACKS),
v.getBatiments().get(Building.DOCKS),
v.getPowers(),
v.getResearches(),
v.getHero(),
v.getHeroLevel()) * this.count;
}
// Store favor cost somewhere
}
@@ -0,0 +1,54 @@
package com.bernard.greposimu.model.simulator.command;
import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.game.buildings.Building;
import com.bernard.greposimu.model.game.researches.Research;
import com.bernard.greposimu.model.game.util.Resources;
import com.bernard.greposimu.model.simulator.data.SimulatorData;
import com.bernard.greposimu.model.simulator.data.Ville;
public class ResearchCommand extends TownCommand {
Research research;
boolean forget;
public ResearchCommand(GameConfig gc, int town, Resources need, Research research, boolean forget) {
super(gc,town);
this.research = research;
this.forget = forget;
}
@Override
public String toString() {
if(forget)
return "[%d] Forget research %s".formatted(this.town,this.research.getName());
else
return "[%d] Research %s".formatted(this.town,this.research.getName());
}
@Override
public Resources neededResources(SimulatorData sd) {
if(this.forget)return Resources.empty;
Ville v = sd.getVille(town);
return gc.getResearchResources(
this.research,
v.getBatiments().get(Building.ACADEMY),
v.getPowers(),
v.getHero(),
v.getHeroLevel());
}
@Override
public long timeNeeded(SimulatorData sd) {
Ville v = sd.getVille(town);
if(this.forget)
//TODO check this implementation correct
return 0;
else
return gc.getResearchTime(
this.research,
v.getBatiments().get(Building.ACADEMY),
v.getPowers(),
v.getHero(),
v.getHeroLevel());
}
}
@@ -0,0 +1,17 @@
package com.bernard.greposimu.model.simulator.command;
import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.game.util.Resources;
import com.bernard.greposimu.model.simulator.data.SimulatorData;
public abstract class TownCommand extends Command {
int town;
public TownCommand(GameConfig gc, int town) {
super(gc);
this.town = town;
}
public abstract Resources neededResources(SimulatorData sd);
}
@@ -0,0 +1,47 @@
package com.bernard.greposimu.model.simulator.data;
import java.util.Map;
import com.bernard.greposimu.model.game.powers.Power;
import com.bernard.greposimu.model.game.util.Identified;
import com.bernard.greposimu.model.game.util.Timestamp;
public class CastedPower implements Identified{
long originPlayer;
Power power;
Timestamp end;
String id;
Integer level;
int extended;
// Replace with real OOP, depending on the power
Map<String,Object> configuration;
public CastedPower(long originPlayer, Power power, Timestamp end, String id, Integer level, int extended,
Map<String, Object> configuration) {
this.originPlayer = originPlayer;
this.power = power;
this.end = end;
this.id = id;
this.level = level;
this.extended = extended;
this.configuration = configuration;
}
public long getOriginPlayer() {
return originPlayer;
}
public Power getPower() {
return power;
}
public Timestamp getEnd() {
return end;
}
public String getId() {
return id;
}
public Map<String, Object> getConfiguration() {
return configuration;
}
}
@@ -0,0 +1,102 @@
package com.bernard.greposimu.model.simulator.data;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.util.Timestamp;
public class Joueureuse {
// God
Map<God,Integer> favor;
int rage;
int gold;
// Heroes
// herosId -> townId
Map<String,Integer> heroes;
// herosId -> timeOfArrival
Map<String,Timestamp> heroesArrival;
// herosid -> level
Map<String,Integer> heroesLevel;
List<String> inventory;
// Remparts
Troupes slainAsOff,slainAsDef,lostAsOff,lostAsDef;
Set<Movements> movements;
public Joueureuse(Map<God, Integer> favor, int rage, int gold, Map<String, Integer> heroes,
Map<String, Timestamp> heroesArrival, Map<String, Integer> heroesLevel, List<String> inventory,
Troupes slainAsOff, Troupes slainAsDef, Troupes lostAsOff, Troupes lostAsDef, Set<Movements> movements) {
this.favor = favor;
this.rage = rage;
this.gold = gold;
this.heroes = heroes;
this.heroesArrival = heroesArrival;
this.heroesLevel = heroesLevel;
this.inventory = inventory;
this.slainAsOff = slainAsOff;
this.slainAsDef = slainAsDef;
this.lostAsOff = lostAsOff;
this.lostAsDef = lostAsDef;
this.movements = movements;
}
public Map<God, Integer> getFavor() {
return favor;
}
public int getRage() {
return rage;
}
public int getGold() {
return gold;
}
public Map<String, Integer> getHeroes() {
return heroes;
}
public Map<String, Timestamp> getHeroesArrival() {
return heroesArrival;
}
public Map<String, Integer> getHeroesLevel() {
return heroesLevel;
}
public List<String> getInventory() {
return inventory;
}
public Troupes getSlainAsDef() {
return slainAsDef;
}
public Troupes getSlainAsOff() {
return slainAsOff;
}
public Troupes getLostAsDef() {
return lostAsDef;
}
public Troupes getLostAsOff() {
return lostAsOff;
}
public Set<Movements> getMovements() {
return movements;
}
//TODO quêtes
//TODO messages/rapports
//TODO profile
//TODO alliance + allianceMessages
}
@@ -0,0 +1,23 @@
package com.bernard.greposimu.model.simulator.data;
import com.bernard.greposimu.model.game.util.Timestamp;
public class Movements {
Timestamp arrival;
Timestamp started;
Timestamp cancelableUntil;
Timestamp invisibleUntil;
boolean destIsAttack;
boolean origIsAttack;
int home;
int player;
int target;
String type;
Troupes troupes;
}
@@ -0,0 +1,27 @@
package com.bernard.greposimu.model.simulator.data;
import java.util.Map;
public class SimulatorData {
Map<Integer,Ville> villes;
Joueureuse joueureuse;
public SimulatorData(Map<Integer, Ville> villes, Joueureuse joueureuse) {
this.villes = villes;
this.joueureuse = joueureuse;
}
public Map<Integer, Ville> getVilles() {
return villes;
}
public Ville getVille(int id) {
return villes.get(id);
}
public Joueureuse getJoueureuse() {
return joueureuse;
}
}
@@ -0,0 +1,40 @@
package com.bernard.greposimu.model.simulator.data;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.bernard.greposimu.model.game.units.Unit;
public class Troupes {
Map<Unit,Integer> unites;
public Troupes(Map<Unit, Integer> unites) {
this.unites = unites;
}
public Map<Unit, Integer> getUnites() {
return unites;
}
public static final Troupes add(Troupes a, Troupes b) {
return new Troupes(
Stream.concat(a.unites.keySet().stream(), b.unites.keySet().stream())
.distinct().collect(Collectors.toMap(Function.identity(),
u -> a.unites.getOrDefault(u, 0)+b.unites.getOrDefault(u, 0)))
);
}
public int pop() {
return unites.entrySet().stream().mapToInt(e -> e.getValue()*e.getKey().getPopulation()).sum();
}
@Override
public String toString() {
return "Troupes [unites=" + unites + "]";
}
}
@@ -0,0 +1,236 @@
package com.bernard.greposimu.model.simulator.data;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.bernard.greposimu.model.game.buildings.Building;
import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.queues.BuildingQueueItem;
import com.bernard.greposimu.model.game.queues.RecruitmentQueueItem;
import com.bernard.greposimu.model.game.queues.ResearchQueueItem;
import com.bernard.greposimu.model.game.researches.Research;
import com.bernard.greposimu.model.game.units.Hero;
import com.bernard.greposimu.model.game.util.Resources;
import com.bernard.greposimu.model.game.util.Timestamp;
public class Ville {
int id;
String nom;
Map<Building,Integer> batiments;
Troupes troupes;
// origin town id -> troupes
Map<Integer,Troupes> soutiens;
// Destination town -> troupes
Map<Integer,Troupes> soutenus;
Set<CastedPower> powers;
// Agora
Timestamp festivalEnd,olympiquesEnd,marchEnd,theaterEnd;
// Academie
Set<Research> researches;
List<ResearchQueueItem> researchQueue;
// Senat
List<BuildingQueueItem> buildingQueue;
// Farm
// End time of militia
Timestamp miliceUntil;
// Entrepôt
Resources storage;
// Caserne
List<RecruitmentQueueItem> terrestrialQueue;
// Temple
God god;
// Port
List<RecruitmentQueueItem> navalQueue;
// Grotte
int piecesStoquees;
/* Ordres */
Set<UnitOrder> ordresMilitaires;
Set<TradeOrder> incomingTrade;
Set<TradeOrder> outgoingTrade;
public Ville(int id, String nom, Map<Building, Integer> batiments, Troupes troupes, Map<Integer, Troupes> soutiens,
Map<Integer, Troupes> soutenus, Set<CastedPower> powers, Timestamp festivalEnd,
Timestamp olympiquesEnd, Timestamp marchEnd, Timestamp theaterEnd, Set<Research> researches,
List<ResearchQueueItem> researchQueue, List<BuildingQueueItem> buildingQueue, Timestamp miliceUntil,
Resources storage, List<RecruitmentQueueItem> terrestrialQueue, God god,
List<RecruitmentQueueItem> navalQueue, int piecesStoquees, Set<UnitOrder> ordresMilitaires, Set<TradeOrder> incomingTrade,
Set<TradeOrder> outgoingTrade) {
super();
this.id = id;
this.nom = nom;
this.batiments = batiments;
this.troupes = troupes;
this.soutiens = soutiens;
this.soutenus = soutenus;
this.powers = powers;
this.festivalEnd = festivalEnd;
this.olympiquesEnd = olympiquesEnd;
this.marchEnd = marchEnd;
this.theaterEnd = theaterEnd;
this.researches = researches;
this.researchQueue = researchQueue;
this.buildingQueue = buildingQueue;
this.miliceUntil = miliceUntil;
this.storage = storage;
this.terrestrialQueue = terrestrialQueue;
this.god = god;
this.navalQueue = navalQueue;
this.piecesStoquees = piecesStoquees;
this.ordresMilitaires = ordresMilitaires;
this.incomingTrade = incomingTrade;
this.outgoingTrade = outgoingTrade;
}
public static class TradeOrder {
Resources resources;
Integer other;
public TradeOrder(Resources resources, Integer other) {
this.resources = resources;
this.other = other;
}
}
public static class UnitOrder {
Troupes attq;
Timestamp arrivee;
OrderType type;
Set<String> sortileges;
}
public static enum OrderType {
ATTACK,
SUPPORT,
ATTACK_CANCELED,
ATTACK_RETURN;
}
public static class QueueItem {
String buildingId;
Timestamp startTime;
Timestamp endTime;
long duration;
}
public int getId() {
return id;
}
public String getNom() {
return nom;
}
public Map<Building, Integer> getBatiments() {
return batiments;
}
public Troupes getTroupes() {
return troupes;
}
public Map<Integer, Troupes> getSoutiens() {
return soutiens;
}
public Map<Integer, Troupes> getSoutenus() {
return soutenus;
}
public Set<CastedPower> getPowers() {
return powers;
}
public Timestamp getFestivalEnd() {
return festivalEnd;
}
public Timestamp getOlympiquesEnd() {
return olympiquesEnd;
}
public Timestamp getMarchEnd() {
return marchEnd;
}
public Timestamp getTheaterEnd() {
return theaterEnd;
}
public Set<Research> getResearches() {
return researches;
}
public boolean hasResearch(Research r) {
return researches.contains(r);
}
public List<ResearchQueueItem> getResearchQueue() {
return researchQueue;
}
public List<BuildingQueueItem> getBuildingQueue() {
return buildingQueue;
}
public Timestamp getMiliceUntil() {
return miliceUntil;
}
public Resources getStorage() {
return storage;
}
public List<RecruitmentQueueItem> getTerrestrialQueue() {
return terrestrialQueue;
}
public God getGod() {
return god;
}
public List<RecruitmentQueueItem> getNavalQueue() {
return navalQueue;
}
public int getPiecesStoquees() {
return piecesStoquees;
}
public Set<UnitOrder> getOrdresMilitaires() {
return ordresMilitaires;
}
public Set<TradeOrder> getIncomingTrade() {
return incomingTrade;
}
public Set<TradeOrder> getOutgoingTrade() {
return outgoingTrade;
}
public Hero getHero(){
//TODO implement this
return null;
}
public int getHeroLevel() {
//TODO implement this
return 0;
}
}
@@ -0,0 +1,136 @@
package com.bernard.greposimu.model.simulator.objective;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.game.buildings.Building;
import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.researches.Research;
import com.bernard.greposimu.model.game.units.Unit;
import com.bernard.greposimu.model.simulator.command.BuildCommand;
import com.bernard.greposimu.model.simulator.command.Command;
import com.bernard.greposimu.model.simulator.command.HideStoreCommand;
import com.bernard.greposimu.model.simulator.command.RecruitCommand;
import com.bernard.greposimu.model.simulator.command.ResearchCommand;
import com.bernard.greposimu.model.simulator.data.Troupes;
import com.bernard.greposimu.model.simulator.data.Ville;
public class TownObjective {
GameConfig gc;
Map<Building,Integer> buildings;
Map<Unit,Double> unitsProportions;
Set<Research> researches;
int hide;
God god;
TownObjective() {}
public TownObjective(GameConfig gc, Map<Building, Integer> buildings, Map<Unit, Double> unitsProportions,
Set<Research> researches, int hide, God god) {
this.gc = gc;
this.buildings = buildings;
this.unitsProportions = unitsProportions;
this.researches = researches;
this.hide = hide;
this.god = god;
}
public static final int villeMaxPop(GameConfig gc, Ville v) {
return GameConfig.getTotalPop(
v.getBatiments().getOrDefault(Building.FARM,0),
v.getBatiments().getOrDefault(Building.THERMAL, 0)>=1,
v.getResearches().contains(gc.getResearch("plow")),
v.getGod().getId().equals("aphrodite"),
0); //TODO take popultion_boost into account
}
public Troupes targetTroupes() {
int popRest = GameConfig.getTotalPop(
this.buildings.getOrDefault(Building.FARM,0),
this.buildings.getOrDefault(Building.THERMAL, 0)>=1,
this.researches.contains(gc.getResearch("plow")),
this.god.getId().equals("aphrodite"),
0) //TODO take popultion_boost into account
-
this.buildings.entrySet().stream().mapToInt(e -> e.getKey().getRequiredPop(e.getValue())).sum();
double totalProp = this.unitsProportions.values().stream().collect(Collectors.summarizingDouble(d -> d)).getSum();
return new Troupes(this.unitsProportions.entrySet().stream().collect(Collectors.toMap(
e -> e.getKey(),
e -> (int)Math.floor(e.getValue()*popRest/totalProp/e.getKey().getPopulation()))));
}
public Set<Command> getDifferences(Ville v) {
Set<Command> commands = new HashSet<>();
for(Building b :gc.getBuildings()) {
int cur = v.getBatiments().getOrDefault(b, 0);
int obj = this.buildings.getOrDefault(b, 0);
if(cur != obj) {
if(obj>cur)
for(int i=cur+1;i<=obj;i++)
commands.add(new BuildCommand(gc,v.getId(), b, i, false));
else
for(int i=cur-1;i>=obj;i--)
commands.add(new BuildCommand(gc,v.getId(), b, i, true));
}
//TODO check queue
}
for(Research r : gc.getResearches()) {
boolean cur = v.getResearches().contains(r);
boolean obj = this.researches.contains(r);
if(cur && !obj)
commands.add(new ResearchCommand(gc,v.getId(),null,r,true));
else if (!cur && obj)
commands.add(new ResearchCommand(gc,v.getId(),null,r,false));
//TODO check queue
}
Troupes tot = Troupes.add(v.getTroupes(),v.getSoutiens().values().stream().reduce(Troupes::add).orElse(new Troupes(Map.of())));
Troupes target = this.targetTroupes();
for(Unit u : gc.getUnits()) {
int diff = target.getUnites().getOrDefault(u,0) - tot.getUnites().getOrDefault(u,0);
if(diff > 0)
commands.add(new RecruitCommand(gc,v.getId(), u, diff));
//TODO manage unit removal planned
}
if(v.getPiecesStoquees() < hide)
commands.add(new HideStoreCommand(gc,v.getId(), hide - v.getPiecesStoquees()));
return commands;
}
public Map<Building, Integer> getBuildings() {
return buildings;
}
public Map<Unit, Double> getUnitsProportions() {
return unitsProportions;
}
public Set<Research> getResearches() {
return researches;
}
public int getHide() {
return hide;
}
public God getGod() {
return god;
}
public void setGc(GameConfig gc) {
this.gc = gc;
}
@Override
public String toString() {
return "TownObjective [gc=" + gc + ", buildings=" + buildings + ", unitsProportions=" + unitsProportions
+ ", researches=" + researches + ", hide=" + hide + ", god=" + god + "]";
}
}
@@ -0,0 +1,191 @@
package com.bernard.greposimu.source;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.game.buildings.Building;
import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.powers.Power;
import com.bernard.greposimu.model.game.queues.BuildingQueueItem;
import com.bernard.greposimu.model.game.queues.RecruitmentQueueItem;
import com.bernard.greposimu.model.game.queues.RecruitmentQueueItem.RecruitmentKind;
import com.bernard.greposimu.model.game.queues.ResearchQueueItem;
import com.bernard.greposimu.model.game.researches.Research;
import com.bernard.greposimu.model.game.units.Unit;
import com.bernard.greposimu.model.game.util.Resources;
import com.bernard.greposimu.model.game.util.Timestamp;
import com.bernard.greposimu.model.game.util.UnitResources;
import com.bernard.greposimu.model.simulator.data.CastedPower;
import com.bernard.greposimu.model.simulator.data.Joueureuse;
import com.bernard.greposimu.model.simulator.data.SimulatorData;
import com.bernard.greposimu.model.simulator.data.Troupes;
import com.bernard.greposimu.model.simulator.data.Ville;
import com.bernard.greposimu.model.simulator.data.Ville.TradeOrder;
import com.fasterxml.jackson.core.exc.StreamReadException;
import com.fasterxml.jackson.databind.DatabindException;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JSONSourcer {
public static final void readSource(File file) {
ObjectMapper om = new ObjectMapper();
try {
SourcedData sd = om.readValue(file, SourcedData.class);
System.out.println(sd);
} catch (StreamReadException e) {
e.printStackTrace();
} catch (DatabindException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static final Troupes getTroupes(GameConfig gc, Map<String,Integer> count) {
Map<Unit,Integer> troup = new HashMap<>();
for(String u : count.keySet())troup.put(gc.getUnit(u), count.get(u));
return new Troupes(troup);
}
public static final Resources getResources(SourcedRessources res) {
return new Resources(res.wood, res.stone, res.iron);
}
public static final UnitResources getUnitResources(God god, SourcedFavRessources res) {
return new UnitResources(res.wood, res.stone, res.iron,god,res.favor);
}
public static final SimulatorData makeSimulationData(String json, GameConfig gc) {
ObjectMapper om = new ObjectMapper();
try {
SourcedData sd = om.readValue(json, SourcedData.class);
Map<Integer,Ville> villes = new HashMap<>();
for(String idS : sd.towns.keySet()) {
Integer id = Integer.parseInt(idS);
SourcedTown st = sd.getTowns().get(idS);
Map<Building,Integer> buildings = new HashMap<>();
for(String bid : st.buildings.keySet())buildings.put(gc.getBuilding(bid), st.buildings.get(bid));
Set<Research> researches = new HashSet<>();
for(String rid : st.researches.keySet())if(!rid.equals("id") && st.researches.get(rid))researches.add(gc.getResearch(rid));
Map<Integer,Troupes> soutiens = new HashMap<Integer, Troupes>();
Map<Integer,Troupes> soutenus = new HashMap<Integer, Troupes>();
for(SourcedUnits uts : sd.units) {
if(uts.orig == id && uts.curr != id)
soutiens.put(uts.curr, getTroupes(gc, uts.getUnits()));
if(uts.curr == id && uts.orig != id)
soutenus.put(uts.orig, getTroupes(gc, uts.getUnits()));
}
List<BuildingQueueItem> buildingQueue = new ArrayList<>();
for(SourcedBuildingOrder sbo : st.buildingOrders)
buildingQueue.add(new BuildingQueueItem(
sbo.getBuildingTime(),
gc.getBuilding(sbo.building),
sbo.isTearingDown(),
new Timestamp(sbo.getBeginTime()),
new Timestamp(sbo.getEndTime()),
getResources(sbo.getRefund()),
getResources(sbo.getCost())
));
List<RecruitmentQueueItem> terrestrialQueue = new ArrayList<>();
List<RecruitmentQueueItem> navalQueue = new ArrayList<>();
for(SourcedRecruitmentOrder sro : st.recruitingOrders) {
Unit u = gc.getUnit(sro.unit);
RecruitmentQueueItem rqi = new RecruitmentQueueItem(
sro.getKind().equals("ground")?RecruitmentKind.GROUND:RecruitmentKind.NAVAL,
u,
sro.getCount(),
sro.getDone(),
new Timestamp(sro.getBeginTime()),
new Timestamp(sro.getEndTime()),
getUnitResources(u.isMythological()?u.getGod():null, sro.getRefund()),
getUnitResources(null, sro.getCost())
);
if(sro.kind.equals("ground"))
terrestrialQueue.add(rqi);
else if(sro.kind.equals("naval"))
navalQueue.add(rqi);
}
List<ResearchQueueItem> researchQueue = new ArrayList<>();
for(SourcedResearchOrder sro : st.researchOrders)
researchQueue.add(new ResearchQueueItem(
gc.getResearch(sro.getResearch()),
new Timestamp(sro.getBeginTime()),
new Timestamp(sro.getEndTime()),
getResources(sro.getRefund())
));
Set<CastedPower> powers = new HashSet<>();
for(SourcedCastedPower sp : st.castedPowers)
powers.add(new CastedPower(
sp.getOriginPlayer(),
gc.getPower(sp.getPower()),
sp.getEndTime()==null?null:new Timestamp(sp.getEndTime()),
sp.getId(),
sp.getLevel(),
sp.getExtended(),
sp.getConfiguration()));
Set<TradeOrder> incomingTrade = new HashSet<>();
Set<TradeOrder> outgoingTrade = new HashSet<>();
for(SourcedTrades str : sd.trades) {
//TODO support farmtowns
if(str.destType.equals("town_trade") && str.origType.equals("town_trade")){
if(Integer.parseInt(str.dest) == id)
incomingTrade.add(new TradeOrder(new Resources(str.getWood(), str.getStone(), str.getIron()), Integer.parseInt(str.orig)));
if(Integer.parseInt(str.orig) == id)
incomingTrade.add(new TradeOrder(new Resources(str.getWood(), str.getStone(), str.getIron()), Integer.parseInt(str.dest)));
}
}
Set<SourcedCelebration> townCele = sd.celebrations.stream().filter(c -> c.getTown() == id).collect(Collectors.toSet());
villes.put(id, new Ville(
id,
st.name,
buildings,
getTroupes(gc, st.getUnitsTotal()),
soutiens,
soutenus,
powers,
townCele.stream().filter(c -> c.type.equals("party")).map(SourcedCelebration::getEnd).map(Timestamp::new).findAny().orElse(null),
townCele.stream().filter(c -> c.type.equals("games")).map(SourcedCelebration::getEnd).map(Timestamp::new).findAny().orElse(null),
townCele.stream().filter(c -> c.type.equals("triumph")).map(SourcedCelebration::getEnd).map(Timestamp::new).findAny().orElse(null),
townCele.stream().filter(c -> c.type.equals("theater")).map(SourcedCelebration::getEnd).map(Timestamp::new).findAny().orElse(null),
researches,
researchQueue,
buildingQueue,
st.getMilitia()==null?null:new Timestamp(st.getMilitia().getEnd()),
new Resources(st.getResources().wood, st.getResources().stone, st.getResources().iron),
terrestrialQueue,
gc.getGod(st.god),
navalQueue,
st.espstorage,
null,//ordresMilitaires,
incomingTrade,
outgoingTrade));
}
Joueureuse joueureuse = new Joueureuse(null, 0, 0, null, null, null, null, null, null, null, null, null);
return new SimulatorData(villes, joueureuse);
} catch (StreamReadException e) {
e.printStackTrace();
} catch (DatabindException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
@@ -0,0 +1,46 @@
package com.bernard.greposimu.source;
public class SourcedBuildingOrder {
@Override
public String toString() {
return "SourcedBuildingOrder [buildingTime=" + buildingTime + ", building=" + building + ", beginTime="
+ beginTime + ", tearingDown=" + tearingDown + ", endTime=" + endTime + ", refund=" + refund + ", cost="
+ cost + "]";
}
long buildingTime;
// the id of the building being built
String building;
long beginTime;
boolean tearingDown;
long endTime;
SourcedRessources refund;
SourcedRessources cost;
public long getBuildingTime() {
return buildingTime;
}
public String getBuilding() {
return building;
}
public long getBeginTime() {
return beginTime;
}
public boolean isTearingDown() {
return tearingDown;
}
public long getEndTime() {
return endTime;
}
public SourcedRessources getRefund() {
return refund;
}
public SourcedRessources getCost() {
return cost;
}
}
@@ -0,0 +1,37 @@
package com.bernard.greposimu.source;
import java.util.Map;
public class SourcedCastedPower {
Map<String,Object> configuration;
Long endTime;
int extended;
Integer level;
Long originPlayer;
String power;
String id;
public Map<String,Object> getConfiguration() {
return configuration;
}
public Long getEndTime() {
return endTime;
}
public int getExtended() {
return extended;
}
public Integer getLevel() {
return level;
}
public Long getOriginPlayer() {
return originPlayer;
}
public String getPower() {
return power;
}
public String getId() {
return id;
}
}
@@ -0,0 +1,18 @@
package com.bernard.greposimu.source;
public class SourcedCelebration {
int town;
String type;
long end;
public int getTown() {
return town;
}
public String getType() {
return type;
}
public long getEnd() {
return end;
}
}
@@ -0,0 +1,40 @@
package com.bernard.greposimu.source;
import java.util.List;
import java.util.Map;
public class SourcedData {
Map<String,SourcedTown> towns;
List<SourcedCelebration> celebrations;
List<SourcedMilitaryOrders> movements;
List<SourcedTrades> trades;
Map<String,Integer> favors;
List<SourcedUnits> units;
public Map<String, SourcedTown> getTowns() {
return towns;
}
public List<SourcedCelebration> getCelebrations() {
return celebrations;
}
public List<SourcedMilitaryOrders> getMovements() {
return movements;
}
public List<SourcedTrades> getTrades() {
return trades;
}
public Map<String, Integer> getFavors() {
return favors;
}
public List<SourcedUnits> getUnits() {
return units;
}
@Override
public String toString() {
return "SourcedData [towns=" + towns + ", celebrations=" + celebrations + ", movements=" + movements
+ ", trades=" + trades + ", favors=" + favors + "]";
}
}
@@ -0,0 +1,31 @@
package com.bernard.greposimu.source;
public class SourcedFavRessources {
int wood;
int iron;
int stone;
int favor;
int pop;
public int getWood() {
return wood;
}
public int getIron() {
return iron;
}
public int getStone() {
return stone;
}
public int getFavor() {
return favor;
}
public int getPop() {
return pop;
}
@Override
public String toString() {
return "SourcedFavRessources [wood=" + wood + ", iron=" + iron + ", stone=" + stone + ", favor=" + favor
+ ", pop=" + pop + "]";
}
}
@@ -0,0 +1,51 @@
package com.bernard.greposimu.source;
public class SourcedMilitaryOrders {
long start;
long arrival;
long cancelableUntil;
long invisibleUntil;
boolean destIsAttackSpot;
boolean origIsAttackSpot;
int player;
int homeId;
int targetId;
String type;
public long getArrival() {
return arrival;
}
public long getCancelableUntil() {
return cancelableUntil;
}
public long getInvisibleUntil() {
return invisibleUntil;
}
public boolean isDestIsAttackSpot() {
return destIsAttackSpot;
}
public boolean isOrigIsAttackSpot() {
return origIsAttackSpot;
}
public int getHomeId() {
return homeId;
}
public int getPlayer() {
return player;
}
public long getStart() {
return start;
}
public int getTargetId() {
return targetId;
}
public String getType() {
return type;
}
}
@@ -0,0 +1,13 @@
package com.bernard.greposimu.source;
public class SourcedMilitiaTimes {
long start;
long end;
public long getStart() {
return start;
}
public long getEnd() {
return end;
}
}
@@ -0,0 +1,49 @@
package com.bernard.greposimu.source;
public class SourcedRecruitmentOrder {
int count;
int done;
String kind;
// the id of the building being built
String unit;
long beginTime;
long endTime;
SourcedFavRessources refund;
SourcedFavRessources cost;
public int getCount() {
return count;
}
public int getDone() {
return done;
}
public String getKind() {
return kind;
}
public String getUnit() {
return unit;
}
public long getBeginTime() {
return beginTime;
}
public long getEndTime() {
return endTime;
}
public SourcedFavRessources getRefund() {
return refund;
}
public SourcedFavRessources getCost() {
return cost;
}
@Override
public String toString() {
return "SourcedRecruitmentOrder [count=" + count + ", done=" + done + ", kind=" + kind + ", unit=" + unit
+ ", beginTime=" + beginTime + ", endTime=" + endTime + ", refund=" + refund + ", cost=" + cost + "]";
}
}
@@ -0,0 +1,31 @@
package com.bernard.greposimu.source;
public class SourcedResearchOrder {
// the id of the building being built
String research;
long beginTime;
long endTime;
SourcedRessources refund;
public String getResearch() {
return research;
}
public long getBeginTime() {
return beginTime;
}
public long getEndTime() {
return endTime;
}
public SourcedRessources getRefund() {
return refund;
}
}
@@ -0,0 +1,22 @@
package com.bernard.greposimu.source;
public class SourcedRessources {
int wood;
int iron;
int stone;
@Override
public String toString() {
return "SourcedRessources [wood=" + wood + ", iron=" + iron + ", stone=" + stone + "]";
}
public int getWood() {
return wood;
}
public int getIron() {
return iron;
}
public int getStone() {
return stone;
}
}
@@ -0,0 +1,95 @@
package com.bernard.greposimu.source;
import java.util.List;
import java.util.Map;
public class SourcedTown {
String name;
int points;
String god;
SourcedTownResources resources;
int espstorage;
/* GLOBAL UNITS */
// unit_id -> number of units
// TOTAL units defending this city
Map<String,Integer> unitsTotal;
// TOTAL units from this city defending another city
Map<String,Integer> outerTotal;
// TOTAL units from other cities defending this city
Map<String,Integer> supportTotal;
// building_id -> level
Map<String,Integer> buildings;
// research_id -> hasBeenResearched
Map<String,Boolean> researches;
List<SourcedCastedPower> castedPowers;
List<SourcedBuildingOrder> buildingOrders;
List<SourcedRecruitmentOrder> recruitingOrders;
List<SourcedResearchOrder> researchOrders;
SourcedMilitiaTimes militia;
public String getName() {
return name;
}
public int getPoints() {
return points;
}
public Map<String, Integer> getUnitsTotal() {
return unitsTotal;
}
public Map<String, Integer> getOuterTotal() {
return outerTotal;
}
public Map<String, Integer> getSupportTotal() {
return supportTotal;
}
public Map<String, Integer> getBuildings() {
return buildings;
}
public Map<String, Boolean> getResearches() {
return researches;
}
public List<SourcedCastedPower> getCastedPowers() {
return castedPowers;
}
public List<SourcedBuildingOrder> getBuildingOrders() {
return buildingOrders;
}
public List<SourcedRecruitmentOrder> getRecruitingOrders() {
return recruitingOrders;
}
public List<SourcedResearchOrder> getResearchOrders() {
return researchOrders;
}
public SourcedTownResources getResources() {
return resources;
}
public int getEspstorage() {
return espstorage;
}
public SourcedMilitiaTimes getMilitia() {
return militia;
}
public String getGod() {
return god;
}
@Override
public String toString() {
return "SourcedTown [name=" + name + ", points=" + points + ", god=" + god + ", resources=" + resources
+ ", espstorage=" + espstorage + ", unitsTotal=" + unitsTotal + ", outerTotal=" + outerTotal
+ ", supportTotal=" + supportTotal + ", buildings=" + buildings + ", researches=" + researches
+ ", castedPowers=" + castedPowers + ", buildingOrders=" + buildingOrders + ", recruitingOrders="
+ recruitingOrders + ", researchOrders=" + researchOrders + ", militia=" + militia + "]";
}
}
@@ -0,0 +1,39 @@
package com.bernard.greposimu.source;
public class SourcedTownResources {
int wood,stone,iron,storage,population,favor;
public int getWood() {
return wood;
}
public int getStone() {
return stone;
}
public int getIron() {
return iron;
}
public int getStorage() {
return storage;
}
public int getPopulation() {
return population;
}
@Override
public String toString() {
return "SourcedTownResources [wood=" + wood + ", stone=" + stone + ", iron=" + iron + ", storage=" + storage
+ ", population=" + population + ", favor=" + favor + "]";
}
public int getFavor() {
return favor;
}
}
@@ -0,0 +1,66 @@
package com.bernard.greposimu.source;
public class SourcedTrades {
long start;
long arrival;
String dest;
String destType;
String orig;
String origType;
int gold,stone,wood,iron;
boolean exchange;
public long getStart() {
return start;
}
public long getArrival() {
return arrival;
}
public String getDest() {
return dest;
}
public String getDestType() {
return destType;
}
public String getOrig() {
return orig;
}
public String getOrigType() {
return origType;
}
public int getGold() {
return gold;
}
public int getStone() {
return stone;
}
public int getWood() {
return wood;
}
public int getIron() {
return iron;
}
public boolean isExchange() {
return exchange;
}
@Override
public String toString() {
return "SourcedTrades [start=" + start + ", arrival=" + arrival + ", dest=" + dest + ", destType=" + destType
+ ", orig=" + orig + ", origType=" + origType + ", gold=" + gold + ", stone=" + stone + ", wood=" + wood
+ ", iron=" + iron + ", exchange=" + exchange + "]";
}
}
@@ -0,0 +1,23 @@
package com.bernard.greposimu.source;
import java.util.Map;
public class SourcedUnits {
int orig;
int curr;
Map<String,Integer> units;
boolean sameIsland;
public int getOrig() {
return orig;
}
public int getCurr() {
return curr;
}
public Map<String, Integer> getUnits() {
return units;
}
public boolean isSameIsland() {
return sameIsland;
}
}
@@ -1,3 +1,4 @@
spring.application.name=GrepoSimu
spring.devtools.restart.pollInterval=10s
spring.mvc.favicon.enabled=false
spring.web.resources.static-locations=classpath:/static/
@@ -0,0 +1 @@
missions_power_4.missions_dionysia.disabled.png
@@ -0,0 +1 @@
missions_power_4.missions_dionysia.hover.png
@@ -0,0 +1 @@
missions_power_4.missions_dionysia.png
+218
View File
@@ -0,0 +1,218 @@
// ==UserScript==
// @name GrepoSimu
// @namespace greposimu
// @version 1.0
// @author Mysaa Java
// @homepage https://greposimu.bernard.com.de
// @updateURL https://greposimu.bernard.com.de/userscript.js
// @downloadURL https://greposimu.bernard.com.de/userscript.js
// @description This script sends information on the town to greposimu
// @include https://*.grepolis.com/game/*
// @exclude view-source://*
// @icon https://greposimu.bernard.com.de/favicon.ico
// ==/UserScript==
function downloadObjectAsJson(exportObj, exportName){
var dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(exportObj,null,2));
var downloadAnchorNode = document.createElement('a');
downloadAnchorNode.setAttribute("href", dataStr);
downloadAnchorNode.setAttribute("download", exportName + ".json");
document.body.appendChild(downloadAnchorNode); // required for firefox
downloadAnchorNode.click();
downloadAnchorNode.remove();
}
function getAllFragments() {
out = {}
for(var storename in ITowns){
if(typeof ITowns[storename] === 'object' && "fragments" in ITowns[storename]){
out[storename] = {}
store = ITowns[storename].fragments
for(var townid in store) {
out[storename][townid] = []
for(var i = 0; i < store[townid].models.length;i++){
out[storename][townid].push(store[townid].models[i].attributes)
}
}
}
}
return out
}
function makeObject() {
out = {}
for(var townid in ITowns.towns) {
out[townid] = {}
out[townid].name = ITowns.towns[townid].name
out[townid].points = ITowns.towns[townid].points
out[townid].god = ITowns.towns[townid].god()
out[townid].unitsTotal = ITowns.towns[townid].units()
out[townid].supportTotal = ITowns.towns[townid].unitsSupport()
out[townid].outerTotal = ITowns.towns[townid].unitsOuter()
out[townid].resources = ITowns.towns[townid].resources()
out[townid].buildings = ITowns.towns[townid].getBuildings().getBuildings()
out[townid].researches = ITowns.towns[townid].researches().attributes
out[townid].espstorage = ITowns.towns[townid].getEspionageStorage()
orderz = ITowns.towns[townid].buildingOrders()
out[townid].buildingOrders = []
for(var i = 0; i<orderz.length;i++) {
order = orderz.models[i].attributes
out[townid].buildingOrders.push({
buildingTime: order.building_time,
building: order.building_type,
beginTime: order.created_at,
tearingDown: order.tear_down,
endTime: order.to_be_completed_at,
refund: order.cancel_refund,
cost: {wood: order.wood, stone: order.stone, iron: order.iron}
})
}
recruitments = ITowns.all_remaining_unit_orders.fragments[townid]
out[townid].recruitingOrders = []
for(var i = 0; i<recruitments.models.length; i++) {
order = recruitments.models[i].attributes
out[townid].recruitingOrders.push({
count: order.count,
beginTime: order.created_at,
kind: order.kind,
done: order.parts_done,
refund: order.refund_for_single_unit,
endTime: order.to_be_completed_at,
unit: order.unit_type,
cost: {wood: order.wood, stone: order.stone, iron: order.iron, favor: order.favor}
})
}
castedPowers = ITowns.all_casted_powers.fragments[townid]
out[townid].castedPowers = []
for(var i = 0; i<castedPowers.models.length; i++) {
order = castedPowers.models[i].attributes
out[townid].castedPowers.push({
configuration: order.configuration,
endTime: order.to_be_completed_at,
extended: order.extended,
level: order.level,
originPlayer: order.origin_player_id,
power: order.power_id,
id: order.id
})
}
researches = MM.getTownAgnosticCollectionsByName("ResearchOrder")[0].fragments[townid]
out[townid].researchOrders = []
for(var i = 0; i<researches.models.length; i++) {
order = researches.models[i].attributes
out[townid].researchOrders.push({
research: order.research_type,
beginTime: order.created_at,
endTime: order.to_be_completed_at,
refund: order.cancel_refund
})
}
/*
militia = MM.getModels().Militia
if(townid in militia){
out[townid].militia = {
start : militia[townid].attributes.started_at,
end: militia[townid].attributes.finished_at
}
} else {
out[townid].militia = null
}*/
out[townid].militia = null
}
celebrations = MM.getModels().Celebration
outCele = []
for (var i in celebrations) {
outCele.push({
town: celebrations[i].attributes.townid,
type: celebrations[i].attributes.celebration_type,
end: celebrations[i].attributes.finished_at,
})
}
mvts = MM.getModels().MovementsUnits
outMvts = []
for (var i in mvts) {
mvt = mvts[i].attributes
outMvts.push({
arrival: mvt.arrival_at,
cancelableUntil: mvt.cancelable_until,
invisibleUntil: mvt.cap_of_invisibility_effective_until,
destIsAttackSpot: mvt.destination_is_attack_spot,
origIsAttackSpot: mvt.origin_is_attack_spot,
homeId: mvt.home_town_id ,
player: mvt.player_id,
start: mvt.started_at,
targetId: mvt.target_town_id,
type: mvt.type,
})
}
trdz = MM.getModels().Trade
outTrd = []
for (var i in trdz) {
trd = trdz[i].attributes
outTrd.push({
start: trd.started_at,
arrival: trd.arrival_at,
dest: trd.destination_town_id,
destType: trd.destination_town_type,
gold: trd.gold,
stone: trd.stone,
wood: trd.wood,
iron: trd.iron,
orig: trd.origin_town_id,
origType: trd.origin_town_type,
exchange: trd.in_exchange
})
}
fav = MM.getModels().PlayerGods[Object.keys(MM.getModels().PlayerGods)[0]].attributes
outFav = {
aphrodite: fav.aphrodite_favor,
ares: fav.ares_favor,
artemis: fav.artemis_favor,
athena: fav.athena_favor,
fury: fav.fury,
hades: fav.hades_favor,
hera: fav.hera_favor,
poseidon: fav.poseidon_favor,
zeus: fav.zeus_favor
}
utz = MM.getModels().Units
outUts = []
for (var i in utz) {
outUts.push({
orig: utz[i].getOriginTownId(),
curr: utz[i].getCurrentTownId(),
units: utz[i].getUnits(),
sameIsland: utz[i].isSameIsland()
})
}
return {towns: out,celebrations: outCele, movements: outMvts, trades: outTrd, favors: outFav, units: outUts}
}
function greposimu() {
GREPOSIMURL="http://localhost:8080"
$.ajax({
type: 'POST',
crossDomain: true,
contentType : "application/json; charset=utf-8",
data: JSON.stringify(makeObject()),
datatype : "application/json",
url: GREPOSIMURL+'/registerScheduler',
success: function(uuid){
window.open(GREPOSIMURL+'/scheduler/'+uuid, '_blank').focus();
}
})
}
function doc_keyUp(e) {
console.log("Keyup de greposimu")
if (e.keyCode == 79)
greposimu();
}
console.log("GrepoSimu loaded")
document.addEventListener('keyup', doc_keyUp, false)
+1
View File
@@ -16,6 +16,7 @@ body {
</head>
<body>
<pre th:text="${content}"></pre>
<main th:utext="${raw}"></main>
</body>
</html>
+148 -85
View File
@@ -85,102 +85,165 @@ span.fixed50px {
border: 0px;
margin: auto;
}
.container {
display: inline-block;
}
.columns {
display: flex;
}
.formcol {
flex: 50%;
}
</style>
</head>
<body>
<form action="#" th:object="${defCtx}" method="post" id="simuform" >
<form action="#" th:object="${ctx}" method="post" id="simuform" >
<div class="columns">
<fieldset class="formcol">
<legend>Attaque</legend>
<fieldset id="herosFields">
<legend>Héros</legend>
<select name="offHeros" id="offHeros" th:field="*{offHero}">
<option value="none">Aucun</option>
<option th:each="hero : ${heroes}" th:value="${hero.id}"><span th:text="${hero.name}"/></option>
</select>
<fieldset id="herosFields">
<legend>Héros</legend>
<select name="heros" id="heros" th:field="*{hero}">
<option value="none">Aucun</option>
<option th:each="hero : ${heroes}" th:value="${hero.id}"><span th:text="${hero.name}"/></option>
</select>
<label for="offHeroLevelSlider">Niveau du héros: (<span id="offHeroLevelSliderInfo" class="fixed50px"></span>)</label>
<input type="range" min="1" max="20" value="1" class="slider" id="offHeroLevelSlider" th:field="*{offHeroLevel}">
<label for="heroLevelSlider">Niveau du héros: (<span id="heroLevelSliderInfo" class="fixed50px"></span>)</label>
<input type="range" min="1" max="20" value="1" class="slider" id="heroLevelSlider" th:field="*{heroLevel}">
<script>
var heroSlider = document.getElementById("heroLevelSlider");
var heroOutput = document.getElementById("heroLevelSliderInfo");
heroOutput.innerHTML = "lvl. "+heroSlider.value;
// Update the current slider value (each time you drag the slider handle)
heroSlider.oninput = function() {
heroOutput.innerHTML = "lvl. "+this.value;
}
</script>
</fieldset>
<fieldset>
<legend>Unités</legend>
<table cellspacing="0" cellpadding="0">
<tr>
<td th:each="unite : ${defUnits}" class="squareContainer">
<script>
var offHeroSlider = document.getElementById("offHeroLevelSlider");
var offHeroOutput = document.getElementById("offHeroLevelSliderInfo");
offHeroOutput.innerHTML = "lvl. "+offHeroSlider.value;
// Update the current slider value (each time you drag the slider handle)
offHeroSlider.oninput = function() {
offHeroOutput.innerHTML = "lvl. "+this.value;
}
</script>
</fieldset>
<fieldset id="unitesFS">
<legend>Unités</legend>
<div th:each="unite : ${defUnits}" class="container">
<img class="squareImage" th:for="'unite-'+${unite.id}" th:src="@{/images/units/{uname}.png(uname=${unite.id})}"/>
<input type="number" class="squareNumber" th:id="'unite-'+${unite.id}" th:field="*{units[__${unite.id}__]}"/>
</td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>Batiments</legend>
<label for="hasTower">Tour :</label>
<input type="checkbox" id="hasTower" th:field="*{hasTower}"/><br/>
<label for="wallLevelSlider">Niveau des remparts: (<span id="wallLevelSliderInfo" class="fixed50px"></span>)</label>
<input type="range" min="0" max="25" value="1" class="slider" id="wallLevelSlider" th:field="*{wallLevel}">
<script>
var wallSlider = document.getElementById("wallLevelSlider");
var wallOutput = document.getElementById("wallLevelSliderInfo");
wallOutput.innerHTML = "lvl. "+wallSlider.value;
// Update the current slider value (each time you drag the slider handle)
wallSlider.oninput = function() {
wallOutput.innerHTML = "lvl. "+this.value;
}
</script>
</fieldset>
<fieldset>
<legend>Pouvoirs</legend>
<table cellspacing="0" cellpadding="0">
<tr>
<td th:each="power : ${defPowers}" class="squareContainer">
<img class="squareImage" th:for="'power-'+${power.id}" th:src="@{/images/powers/{pname}.png(pname=${power.id})}"/>
<input type="checkbox" th:id="'power-'+${power.id}" th:field="*{powersAsMap[__${power.id}__]}" th:value="true"/>
</td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>Recherches</legend>
<table cellspacing="0" cellpadding="0">
<tr>
<td th:each="research : ${defResearches}" class="squareContainer">
<img class="squareImage" th:for="'research-'+${research.id}" th:src="@{/images/researches/{rname}.png(rname=${research.id})}"/>
<input type="checkbox" th:id="'research-'+${research.id}" th:field="*{researchesAsMap[__${research.id}__]}" th:value="true"/>
</td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>Conseillers</legend>
<table cellspacing="0" cellpadding="0">
<tr>
<td th:each="counsellor : ${defCounsellors}" class="squareContainer">
<br/>
<input type="number" class="squareNumber" th:id="'unite-'+${unite.id}" th:field="*{offUnits[__${unite.id}__]}"/>
</div>
</fieldset>
<fieldset>
<legend>Pouvoirs</legend>
<div th:each="power : ${offPowers}" class="container">
<img class="squareImage" th:for="'power-'+${power}" th:src="@{/images/powers/{pname}.png(pname=${power})}"/>
<input type="checkbox" th:id="'power-'+${power}" th:field="*{defPowersAsMap[__${power}__]}" th:value="true"/>
</div>
</fieldset>
<fieldset>
<legend>Recherches</legend>
<div th:each="research : ${offResearches}" class="container">
<img class="squareImage" th:for="'research-'+${research}" th:src="@{/images/researches/{rname}.png(rname=${research})}"/>
<input type="checkbox" th:id="'research-'+${research}" th:field="*{defResearchesAsMap[__${research}__]}" th:value="true"/>
</div>
</fieldset>
<fieldset>
<legend>Conseillers</legend>
<div th:each="counsellor : ${offCounsellors}" class="container">
<img class="squareImage" th:for="'counsellor-'+${counsellor}" th:src="@{/images/counsellors/{cname}.png(cname=${counsellor})}"/>
<input type="checkbox" th:id="'counsellor-'+${counsellor}" th:field="*{counsellorsAsMap[__${counsellor}__]}" th:value="true"/>
</td>
</tr>
</table>
</fieldset>
<fieldset>
<legend>Bonus de jeu</legend>
<label for="nightBonus">Bonus de nuit :</label>
<input type="checkbox" id="nightBonus" th:field="*{nightBonus}" th:value="true"/><br/>
</fieldset>
<input type="checkbox" th:id="'counsellor-'+${counsellor}" th:field="*{defCounsellorsAsMap[__${counsellor}__]}" th:value="true"/>
</div>
</fieldset>
<fieldset>
<legend>Paramètres</legend>
<label for="moralInput">Moral:</label>
<input type="number" min="0" max="100" value="1" id="moralInput" th:field="*{moral}">
<label for="luckInput">Chance:</label>
<input type="number" min="-20" max="20" value="1" id="luckInput" th:field="*{luck}">
<label for="breachInput">Percée:</label>
<input type="checkbox" id="breachInput" th:field="*{strategyBreach}">
<label for="allianceInput">Alliance:</label>
<input type="checkbox" id="allianceInput" th:field="*{allianceModifier}">
</fieldset>
</fieldset>
<fieldset class="formcol">
<legend>Défense</legend>
<fieldset id="herosFields">
<legend>Héros</legend>
<select name="defHeros" id="defHeros" th:field="*{defHero}">
<option value="none">Aucun</option>
<option th:each="hero : ${heroes}" th:value="${hero.id}"><span th:text="${hero.name}"/></option>
</select>
<label for="heroLevelSlider">Niveau du héros: (<span id="heroLevelSliderInfo" class="fixed50px"></span>)</label>
<input type="range" min="1" max="20" value="1" class="slider" id="heroLevelSlider" th:field="*{defHeroLevel}">
<script>
var heroSlider = document.getElementById("heroLevelSlider");
var heroOutput = document.getElementById("heroLevelSliderInfo");
heroOutput.innerHTML = "lvl. "+heroSlider.value;
// Update the current slider value (each time you drag the slider handle)
heroSlider.oninput = function() {
heroOutput.innerHTML = "lvl. "+this.value;
}
</script>
</fieldset>
<fieldset id="unitesFS">
<legend>Unités</legend>
<div th:each="unite : ${defUnits}" class="container">
<img class="squareImage" th:for="'unite-'+${unite.id}" th:src="@{/images/units/{uname}.png(uname=${unite.id})}"/>
<br/>
<input type="number" class="squareNumber" th:id="'unite-'+${unite.id}" th:field="*{defUnits[__${unite.id}__]}"/>
</div>
</fieldset>
<fieldset>
<legend>Batiments</legend>
<label for="hasTower">Tour :</label>
<input type="checkbox" id="hasTower" th:field="*{hasTower}"/><br/>
<label for="wallLevelSlider">Niveau des remparts: (<span id="wallLevelSliderInfo" class="fixed50px"></span>)</label>
<input type="range" min="0" max="25" value="1" class="slider" id="wallLevelSlider" th:field="*{wallLevel}">
<script>
var wallSlider = document.getElementById("wallLevelSlider");
var wallOutput = document.getElementById("wallLevelSliderInfo");
wallOutput.innerHTML = "lvl. "+wallSlider.value;
// Update the current slider value (each time you drag the slider handle)
wallSlider.oninput = function() {
wallOutput.innerHTML = "lvl. "+this.value;
}
</script>
</fieldset>
<fieldset>
<legend>Pouvoirs</legend>
<div th:each="power : ${defPowers}" class="container">
<img class="squareImage" th:for="'power-'+${power}" th:src="@{/images/powers/{pname}.png(pname=${power})}"/>
<input type="checkbox" th:id="'power-'+${power}" th:field="*{defPowersAsMap[__${power}__]}" th:value="true"/>
</div>
</fieldset>
<fieldset>
<legend>Recherches</legend>
<div th:each="research : ${defResearches}" class="container">
<img class="squareImage" th:for="'research-'+${research}" th:src="@{/images/researches/{rname}.png(rname=${research})}"/>
<input type="checkbox" th:id="'research-'+${research}" th:field="*{defResearchesAsMap[__${research}__]}" th:value="true"/>
</div>
</fieldset>
<fieldset>
<legend>Conseillers</legend>
<div th:each="counsellor : ${defCounsellors}" class="container">
<img class="squareImage" th:for="'counsellor-'+${counsellor}" th:src="@{/images/counsellors/{cname}.png(cname=${counsellor})}"/>
<input type="checkbox" th:id="'counsellor-'+${counsellor}" th:field="*{defCounsellorsAsMap[__${counsellor}__]}" th:value="true"/>
</div>
</fieldset>
<fieldset>
<legend>Bonus de jeu</legend>
<label for="nightBonus">Bonus de nuit :</label>
<input type="checkbox" id="nightBonus" th:field="*{nightBonus}" th:value="true"/><br/>
</fieldset>
</fieldset>
</div>
<button type="button" id="compute">Calculer</button>
</form>
@@ -1,17 +1,92 @@
package com.bernard.greposimu;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.game.GrepoYaml;
import com.bernard.greposimu.model.game.units.Unit;
import com.bernard.greposimu.model.simulator.data.SimulatorData;
import com.bernard.greposimu.model.simulator.objective.TownObjective;
import com.bernard.greposimu.source.JSONSourcer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator.Feature;
@SpringBootTest
class GrepoSimuApplicationTests {
@Test
void loadGameData() throws IOException {
// System.out.println("Reading game data");
// System.out.println(GrepoSimu.readGameData());
// System.out.println(GrepoSimu.makeGameData());
}
@Test
void testJsonRead() throws IOException {
System.out.println("Test de sérialisation :");
GameConfig gc = GrepoSimu.makeGameData();
FileInputStream fis = new FileInputStream(new File("/home/mysaa/Downloads/greposimu.json"));
String data = new String(fis.readAllBytes());
fis.close();
SimulatorData sd = JSONSourcer.makeSimulationData(data,gc);
//System.out.println(sd);
}
TownObjective testObjective(GameConfig gc) {
return new TownObjective(gc, Map.of(
gc.getBuilding("main"), 24,
gc.getBuilding("wall"), 25,
gc.getBuilding("farm"), 45,
gc.getBuilding("academy"), 20,
gc.getBuilding("docks"), 1,
gc.getBuilding("barracks"), 30),
Map.of(
gc.getUnit("sword"), 8.0,
gc.getUnit("archer"), 8.0,
gc.getUnit("hoplite"), 16.0,
gc.getUnit("big_transporter"),7.0),
Set.of(
gc.getResearch("hoplite"),
gc.getResearch("archer"),
gc.getResearch("conscription")),
100_000,
gc.getGod("poseidon"));
}
@Test
void testDifferences() throws IOException {
GameConfig gc = GrepoSimu.makeGameData();
FileInputStream fis = new FileInputStream(new File("/home/mysaa/Downloads/greposimu.json"));
String data = new String(fis.readAllBytes());
fis.close();
SimulatorData sd = JSONSourcer.makeSimulationData(data,gc);
System.out.println(gc.getUnits().stream().map(Unit::getId).collect(Collectors.joining(",")));
TownObjective defObjective = testObjective(gc);
/*
for(Ville v : sd.getVilles().values()) {
System.out.println("==== Ville "+v.getNom()+" ====");
System.out.println(defObjective.getDifferences(v).stream().map(Command::toString).sorted().collect(Collectors.joining("\n")));
}*/
}
@Test
void writeTestObjective() throws IOException {
ObjectMapper om = new ObjectMapper(new YAMLFactory().disable(Feature.WRITE_DOC_START_MARKER));
GameConfig gc = GrepoSimu.makeGameData();
om.registerModule(new GrepoYaml(gc));
om.writeValue(new File("/tmp/out.yml"), testObjective(gc));
TownObjective newObjective = om.readValue(new File("/tmp/out.yml"), TownObjective.class);
System.out.println("READ VALUE");
System.out.println(newObjective);
}
}
+67
View File
@@ -0,0 +1,67 @@
defTerTown:
buildings:
main: 24
lumber: 40
farm: 45
stoner: 40
storage: 35
ironer: 40
barracks: 30
temple: 10
market: 15
docks: 1
academy: 25
wall: 25
hide: 10
tower: 1
thermal: 1
unitsProportions:
sword: 8.0
big_transporter: 7.0
hoplite: 16.0
archer: 8.0
researches:
- "archer"
- "town_guard"
- "hoplite"
- "pottery"
- "instructor"
- "building_crane"
- "conscription"
- "cryptography"
- "plow"
- "berth"
- "phalanx"
hide: 100000
god: "ares"
defNavTown:
buildings:
main: 24
lumber: 40
farm: 45
stoner: 40
storage: 35
ironer: 40
barracks: 1
temple: 10
market: 15
docks: 30
academy: 28
wall: 0
hide: 10
thermal: 1
unitsProportions:
bireme: 1.0
researches:
- "town_guard"
- "pottery"
- "bireme"
- "shipwright"
- "cryptography"
- "plow"
- "mathematics"
- "ram"
- "cartography"
hide: 100000
god: "poseidon"