Compare commits

..
10 Commits
73 changed files with 4624 additions and 483 deletions
+1 -2
View File
@@ -25,11 +25,10 @@ dependencies {
developmentOnly 'org.springframework.boot:spring-boot-devtools' developmentOnly 'org.springframework.boot:spring-boot-devtools'
testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.yaml:snakeyaml:2.2'
implementation 'org.ojalgo:ojalgo:54.0.0' implementation 'org.ojalgo:ojalgo:54.0.0'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' 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' implementation 'org.json:json:20240303'
} }
tasks.named('test') { tasks.named('test') {
@@ -8,6 +8,7 @@ import java.util.stream.Collectors;
import org.json.JSONObject; import org.json.JSONObject;
import com.bernard.greposimu.controller.JSONReader;
import com.bernard.greposimu.model.game.GameConfig; import com.bernard.greposimu.model.game.GameConfig;
public class GrepoSimu { public class GrepoSimu {
@@ -24,7 +25,7 @@ public class GrepoSimu {
JSONObject obj = new JSONObject(json); JSONObject obj = new JSONObject(json);
return new GameConfig(obj); return JSONReader.makeGameConfig(obj);
} }
} }
} }
@@ -1,19 +1,36 @@
package com.bernard.greposimu; package com.bernard.greposimu;
import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Map;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import com.bernard.greposimu.model.game.GameConfig; 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 @SpringBootApplication
public class GrepoSimuApplication { public class GrepoSimuApplication {
public static GameConfig GREPOLIS_GC; public static GameConfig GREPOLIS_GC;
public static Map<String, TownObjective> OBJECTIVES;
public static void main(String[] args) throws IOException { public static void main(String[] args) throws IOException {
GREPOLIS_GC = GrepoSimu.makeGameData(); 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); SpringApplication.run(GrepoSimuApplication.class, args);
} }
+29 -1
View File
@@ -2,11 +2,15 @@ package com.bernard.greposimu;
import java.util.AbstractMap; import java.util.AbstractMap;
import java.util.AbstractSet; import java.util.AbstractSet;
import java.util.EnumSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
import com.bernard.greposimu.model.game.Identified; import com.bernard.greposimu.model.game.util.Identified;
public class Utils { public class Utils {
@@ -23,6 +27,25 @@ public class Utils {
public static final <T extends Identified> T getIdentified(Set<T> set, String id) { 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); 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){ public static final <T> Map<T,Boolean> setToMap(Set<T> set){
return new AbstractMap<T,Boolean>() { return new AbstractMap<T,Boolean>() {
@@ -71,4 +94,9 @@ 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,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";
}
}
@@ -4,20 +4,23 @@ import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Random;
import java.util.Set; import java.util.Set;
import org.springframework.stereotype.Controller; import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam;
import com.bernard.greposimu.GrepoSimuApplication; import com.bernard.greposimu.GrepoSimuApplication;
import com.bernard.greposimu.Utils; import com.bernard.greposimu.Utils;
import com.bernard.greposimu.engine.game.Fight; import com.bernard.greposimu.engine.game.Fight;
import com.bernard.greposimu.model.DefContext; import com.bernard.greposimu.model.DefContext;
import com.bernard.greposimu.model.FightStats; 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.GameConfig;
import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.units.Unit; import com.bernard.greposimu.model.game.units.Unit;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.SerializationFeature;
@@ -26,90 +29,248 @@ import com.fasterxml.jackson.databind.SerializationFeature;
public class SimulatorController { public class SimulatorController {
@GetMapping("/simulator") @GetMapping("/simulator")
public String simulator(Model model) throws IOException { public String simulator(Model model, @RequestParam boolean random) throws IOException {
GameConfig gc = GrepoSimuApplication.GREPOLIS_GC; GameConfig gc = GrepoSimuApplication.GREPOLIS_GC;
model.addAttribute("heroes", gc.getHeroes()); model.addAttribute("heroes", gc.getHeroes());
model.addAttribute("defUnits", Fight.relevantDefUnits(gc)); model.addAttribute("defUnits", Fight.relevantDefUnits(gc));
model.addAttribute("defCounsellors",Fight.relevantDefCounsellors(gc)); model.addAttribute("defCounsellors",DefContext.COUNSELLORS);
model.addAttribute("defResearches",Fight.relevantDefResearch(gc)); model.addAttribute("defResearches",DefContext.RESEARCHES);
model.addAttribute("defPowers",Fight.relevantDefPowers(gc)); model.addAttribute("defPowers",DefContext.POWERS);
model.addAttribute("ctx",new DefSimulatorParams()); 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"; return "simulator";
} }
@PostMapping("/simulate")
@GetMapping("/simulate") @GetMapping("/simulate")
public String simulate(@ModelAttribute DefSimulatorParams defParams, Model model) throws IOException { public String simulate(@ModelAttribute SimulatorParams params, Model model) throws IOException {
if(defParams == null) if(params == null)
defParams = new DefSimulatorParams(); params = new SimulatorParams();
GameConfig gc = GrepoSimuApplication.GREPOLIS_GC; GameConfig gc = GrepoSimuApplication.GREPOLIS_GC;
DefContext defCtx = defParams.asDefContext(gc); DefContext defCtx = params.asDefContext(gc);
OffContext offCtx = params.asOffContext(gc);
FightStats cityStats = Fight.computeDefStats(gc,defCtx); FightStats defStats = Fight.computeDefStats(gc,defCtx);
FightStats offStats = Fight.computeOffStats(gc,offCtx);
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
model.addAttribute("content",cityStats.toString()+"\n"+mapper.writerWithDefaultPrettyPrinter().writeValueAsString(defCtx)); model.addAttribute("content",
defStats.toString()+"\n"+
offStats.toString()+"\n"+
mapper.writerWithDefaultPrettyPrinter().writeValueAsString(defCtx)+"\n"+
mapper.writerWithDefaultPrettyPrinter().writeValueAsString(offCtx)
);
return "debug"; return "debug";
} }
public static class DefSimulatorParams { public static class SimulatorParams {
// unitID -> number of units // unitID -> number of units
public Map<String, Integer> units = new HashMap<>(); public Map<String, Integer> defUnits = new HashMap<>();
public Map<String, Integer> offUnits = new HashMap<>();
public String hero = ""; public String defHero = "";
public int heroLevel = 0; public int defHeroLevel = 0;
public String offHero = "";
public int offHeroLevel = 0;
public int wallLevel = 0; public int wallLevel = 0;
public boolean hasTower = false; public boolean hasTower = false;
public Set<String> powers = new HashSet<>(); public Set<String> defPowers = new HashSet<>();
public Set<String> researches = new HashSet<>(); public Set<String> offPowers = new HashSet<>();
public Set<String> defResearches = new HashSet<>();
public Set<String> counsellors = new HashSet<>(); public Set<String> offResearches = new HashSet<>();
public Set<String> defCounsellors = new HashSet<>();
public Set<String> offCounsellors = new HashSet<>();
public boolean nightBonus = false; 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) { public DefContext asDefContext(GameConfig gc) {
Map<Unit,Integer> unitsU = new HashMap<>(units.size()); Map<Unit,Integer> unitsU = new HashMap<>(defUnits.size());
for(String u : units.keySet()) for(String u : defUnits.keySet())
unitsU.put(gc.getUnit(u), units.get(u)); unitsU.put(gc.getUnit(u), defUnits.get(u));
return new DefContext( return new DefContext(
unitsU, unitsU,
gc.getHero(hero), gc.getHero(defHero),
heroLevel, defHeroLevel,
wallLevel, wallLevel,
hasTower, hasTower,
powers, defPowers,
researches, defOlympicSensesGrepolympiaSummerLevel, olympicTorchGrepolympiaSummerLevel, soteriasShrineLevel,
counsellors, defResearches,
defCounsellors,
nightBonus nightBonus
); );
} }
public Map<String, Integer> getUnits() { public static Object random() {
return units; // TODO Auto-generated method stub
return null;
} }
public void setUnits(Map<String, Integer> units) { public OffContext asOffContext(GameConfig gc) {
this.units = units; 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 String getHero() { public void randomize(Random r,GameConfig gc) {
return hero;
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);
} }
public void setHero(String hero) { wallLevel = r.nextInt(0, 26);
this.hero = hero; 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);
} }
public int getHeroLevel() { olympicSwordGrepolympiaSummerLevel = r.nextInt(1, 5);
return heroLevel; 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);
} }
public void setHeroLevel(int heroLevel) { strategyBreach = (r.nextDouble()<0.02);
this.heroLevel = heroLevel; 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() { public int getWallLevel() {
@@ -128,20 +289,96 @@ public static class DefSimulatorParams {
this.hasTower = hasTower; this.hasTower = hasTower;
} }
public Set<String> getPowers() { public Set<String> getDefPowers() {
return powers; return defPowers;
} }
public Map<String,Boolean> getPowersAsMap() { public void setDefPowers(Set<String> defPowers) {
return Utils.setToMap(powers); this.defPowers = defPowers;
} }
public Map<String,Boolean> getResearchesAsMap() { public Set<String> getOffPowers() {
return Utils.setToMap(researches); return offPowers;
} }
public Map<String,Boolean> getCounsellorsAsMap() { public void setOffPowers(Set<String> offPowers) {
return Utils.setToMap(counsellors); 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() { public boolean isNightBonus() {
@@ -152,6 +389,5 @@ public static class DefSimulatorParams {
this.nightBonus = nightBonus; this.nightBonus = nightBonus;
} }
} }
} }
@@ -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;
}
}
@@ -7,15 +7,29 @@ import com.bernard.greposimu.model.DefContext;
import com.bernard.greposimu.model.FightStats; import com.bernard.greposimu.model.FightStats;
import com.bernard.greposimu.model.OffContext; import com.bernard.greposimu.model.OffContext;
import com.bernard.greposimu.model.game.GameConfig; import com.bernard.greposimu.model.game.GameConfig;
import com.bernard.greposimu.model.game.researches.Research; import com.bernard.greposimu.model.game.units.FightType;
import com.bernard.greposimu.model.game.units.NavalUnit; import com.bernard.greposimu.model.game.units.NavalUnit;
import com.bernard.greposimu.model.game.units.TerrestrialUnit; import com.bernard.greposimu.model.game.units.TerrestrialUnit;
import com.bernard.greposimu.model.game.units.Unit; import com.bernard.greposimu.model.game.units.Unit;
public class Fight { public class Fight {
public FightResult simulateFight(GameConfig gc, OffContext off, DefContext def) {
FightStats offStats = computeOffStats(gc, off);
FightStats defStats = computeDefStats(gc, def);
// Combat Naval
if(offStats.getShip() > defStats.getShip()) {
// Off wins ship
} else {
}
//TODO simulateFight
throw new UnsupportedOperationException("Simulator not created");
}
public static FightStats computeDefStats(GameConfig gc, DefContext def) { public static FightStats computeDefStats(GameConfig gc, DefContext def) {
//TODO replace def getters with more complex getters (.getCounsellors.contains -> .hasCounsellor; .getUnits.get -> .getUnitCount)
FightStats everyoneStatsBonus = FightStats.zero(); FightStats everyoneStatsBonus = FightStats.zero();
Map<Unit,FightStats> unitsBonuses; Map<Unit,FightStats> unitsBonuses;
@@ -25,43 +39,56 @@ public class Fight {
unitsBonuses = Heroes.heroFightBonuses(gc, def.getHero(), def.getHeroLevel(), false); unitsBonuses = Heroes.heroFightBonuses(gc, def.getHero(), def.getHeroLevel(), false);
// Tower & wall // Tower & wall
if(def.hasTrojanDefense()) {
cityBaseStats = Buildings.cityBaseStats(def.getWallLevel()+1);
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, Buildings.wallBonus(def.getWallLevel()+1));
} else {
cityBaseStats = Buildings.cityBaseStats(def.getWallLevel()); cityBaseStats = Buildings.cityBaseStats(def.getWallLevel());
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, Buildings.wallBonus(def.getWallLevel())); everyoneStatsBonus = FightStats.add(everyoneStatsBonus, Buildings.wallBonus(def.getWallLevel()));
}
if(def.hasTower()) if(def.hasTower())
// Add 10% to all units // Add 10% to all units
everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.cst(0.1)); everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.cst(0.1));
// Powers
//TODO powers
// Researches // Researches
if(def.getCounsellors().contains("divine_selection")) if(def.hasDivineSelection())
for(Unit u : gc.getUnits()) for(Unit u : gc.getUnits())
if(u.isMythological()) if(u.isMythological())
unitsBonuses.put(u, FightStats.add(unitsBonuses.getOrDefault(u, FightStats.zero()), FightStats.cst(0.1))); unitsBonuses.put(u, FightStats.add(unitsBonuses.getOrDefault(u, FightStats.zero()), FightStats.cst(0.1)));
if(def.getCounsellors().contains("phalanx")) if(def.hasPhalanx())
for(Unit u : gc.getUnits()) everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(0.1));
if(u.isGround()) if(def.hasRam())
unitsBonuses.put(u, FightStats.add(unitsBonuses.getOrDefault(u, FightStats.zero()), FightStats.cst(0.1))); everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.naval(0.1));
if(def.getCounsellors().contains("ram"))
for(Unit u : gc.getUnits())
if(u.isNaval())
unitsBonuses.put(u, FightStats.add(unitsBonuses.getOrDefault(u, FightStats.zero()), FightStats.cst(0.1)));
// Counsellors // Counsellors
if(def.getCounsellors().contains("priest")) if(def.hasPriest())
for(Unit u : gc.getUnits()) for(Unit u : gc.getUnits())
if(u.isMythological()) if(u.isMythological())
unitsBonuses.put(u, FightStats.add(unitsBonuses.getOrDefault(u, FightStats.zero()), FightStats.cst(0.2))); unitsBonuses.put(u, FightStats.add(unitsBonuses.getOrDefault(u, FightStats.zero()), FightStats.cst(0.2)));
if(def.getCounsellors().contains("commander")) if(def.hasCommander())
for(Unit u : gc.getUnits()) everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.terrestre(0.2));
if(u.isGround()) if(def.hasCaptain())
unitsBonuses.put(u, FightStats.add(unitsBonuses.getOrDefault(u, FightStats.zero()), FightStats.cst(0.2))); everyoneStatsBonus = FightStats.add(everyoneStatsBonus, FightStats.naval(0.2));
if(def.getCounsellors().contains("captain"))
for(Unit u : gc.getUnits()) // Powers
if(u.isNaval()) if(def.hasMyrmidionAttack())
unitsBonuses.put(u, FightStats.add(unitsBonuses.getOrDefault(u, FightStats.zero()), FightStats.cst(0.2))); 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 // Night Bonus
if(def.isNightBonus()) if(def.isNightBonus())
@@ -71,15 +98,24 @@ public class Fight {
FightStats total = cityBaseStats.clone(); FightStats total = cityBaseStats.clone();
for(Unit u : gc.getUnits()) { for(Unit u : gc.getUnits()) {
// total = total + ucount * ((1+bonus+bonus) * ustats) // total = total + ucount * ((1+bonus+bonus) * ustats)
if(def.getUnits().containsKey(u) && def.getUnits().get(u) != null)
total = FightStats.add(total, total = FightStats.add(total,
FightStats.prod(def.getUnits().get(u), FightStats.prod(def.unitCount(u),
FightStats.mul( FightStats.mul(
FightStats.add(FightStats.one(),everyoneStatsBonus,unitsBonuses.getOrDefault(u, FightStats.zero())) FightStats.add(FightStats.one(),everyoneStatsBonus,unitsBonuses.getOrDefault(u, FightStats.zero()))
, makeDefStats(u)) , 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; return total;
} }
@@ -94,34 +130,136 @@ public class Fight {
throw new UnsupportedOperationException("I don't know how to manage units of type "+u.getClass().getName()); throw new UnsupportedOperationException("I don't know how to manage units of type "+u.getClass().getName());
} }
public static List<Object> relevantDefPowers(GameConfig gd) { public static FightStats computeOffStats(GameConfig gc, OffContext off) {
return List.of();
/*List.of("acumen", "divine_senses", "myrmidion_attack", "trojan_defense", "defense_boost", FightStats everyoneStatsBonus = FightStats.zero();
"defense_penalty", "longterm_defense_boost", "assassins_acumen", "rare_defense_boost", Map<Unit,FightStats> unitsBonuses;
"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", // Heroes
"naval_battle_strategy_epic", "land_battle_strategy_rare", "land_battle_strategy_epic", unitsBonuses = Heroes.heroFightBonuses(gc, off.getHero(), off.getHeroLevel(), false);
"soterias_shrine.not_cast").stream().map(gd.powers::get).map(p -> (Power)p).toList();*/
// 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))
));
} }
public static List<Research> relevantDefResearch(GameConfig gd) { if(off.getAresArmyFurySpent()!=0)
return List.of("divine_selection","phalanx","ram") // Ading aresarmy/25 spartiates
.stream().map(gd::getResearch).toList(); 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) { public static List<Unit> relevantDefUnits(GameConfig gc) {
return gc.getUnits().stream().toList(); return gc.getUnits().stream().toList();
} }
public static List<String> relevantDefCounsellors(GameConfig data) { public static List<Unit> relevantOffUnits(GameConfig gc) {
return List.of("priest","commander","captain"); return gc.getUnits().stream().toList();
}
public FightStats computeOffStats(DefContext off) {
//TODO computeOffStats
throw new UnsupportedOperationException("Simulator not created");
} }
public FightResult simulateFight(OffContext off, DefContext def) {
//TODO simulateFight
throw new UnsupportedOperationException("Simulator not created");
}
public static class FightResult { public static class FightResult {
@@ -1,6 +1,8 @@
package com.bernard.greposimu.model; package com.bernard.greposimu.model;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import com.bernard.greposimu.model.game.units.Hero; import com.bernard.greposimu.model.game.units.Hero;
@@ -8,38 +10,127 @@ import com.bernard.greposimu.model.game.units.Unit;
public class DefContext { public class DefContext {
// unitID -> number of units // UNITS
Map<Unit, Integer> 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;
Hero hero; // HEROS
int heroLevel; Hero hero = null;
int heroLevel = 0;
int wallLevel; // BUILDINGS
boolean hasTower; int wallLevel = 0;
boolean hasTower=false;
Set<String> powers; // RESEARCHES
Set<String> researches; boolean divineSelection= false, phalanx = false, ram=false;
Set<String> counsellors; // COUNSELLORS
boolean commander= false;
boolean priest = false;
boolean captain = false;
boolean nightBonus; // 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, public DefContext(Map<Unit, Integer> units, Hero hero, int heroLevel, int wallLevel, boolean hasTower,
Set<String> powers, Set<String> researches, Set<String> counsellors, boolean nightBonus) { Set<String> powers, int soteriasShrinePowerLevel, int olympicTorchGrepolympiaSummerLevel, int olympicSensesGrepolympiaSummerLevel,
this.units = units; 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 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.hero = hero;
this.heroLevel = heroLevel; this.heroLevel = heroLevel;
this.wallLevel = wallLevel; this.wallLevel = wallLevel;
this.hasTower = hasTower; this.hasTower = hasTower;
this.powers = powers; if(powers.contains("acumen"))this.acumen = true;
this.researches = researches; if(powers.contains("divine_senses"))this.divineSenses = true;
this.counsellors = counsellors; 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; this.nightBonus = nightBonus;
} }
public Map<Unit, Integer> getUnits() { public int unitCount(Unit u) {
return units; 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() { public Hero getHero() {
return hero; return hero;
@@ -53,28 +144,123 @@ public class DefContext {
public boolean hasTower() { public boolean hasTower() {
return hasTower; return hasTower;
} }
public Set<String> getPowers() {
return powers;
}
public Set<String> getResearches() {
return researches;
}
public Set<String> getCounsellors() {
return counsellors;
}
public boolean isNightBonus() { public boolean isNightBonus() {
return nightBonus; return nightBonus;
} }
public boolean hasDivineSelection() {
return divineSelection;
@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 hasPhalanx() {
return phalanx;
}
public boolean hasRam() {
return ram;
}
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 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");
} }
@@ -2,6 +2,8 @@ package com.bernard.greposimu.model;
import java.util.Arrays; import java.util.Arrays;
import com.bernard.greposimu.model.game.units.FightType;
public class FightStats implements Cloneable{ public class FightStats implements Cloneable{
public double hack; public double hack;
public double pierce; public double pierce;
@@ -15,6 +17,19 @@ public class FightStats implements Cloneable{
this.ship = ship; 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() { public static final FightStats zero() {
return new FightStats(0, 0, 0, 0); return new FightStats(0, 0, 0, 0);
} }
@@ -27,6 +42,10 @@ public class FightStats implements Cloneable{
return new FightStats(value, value, value, 0.0); 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) { public static final FightStats cst(double value) {
return new FightStats(value, value, value, 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); 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 @Override
public FightStats clone() { public FightStats clone() {
return new FightStats(hack, pierce, distance, ship); return new FightStats(hack, pierce, distance, ship);
@@ -1,23 +1,288 @@
package com.bernard.greposimu.model; package com.bernard.greposimu.model;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import com.bernard.greposimu.model.game.units.Hero;
import com.bernard.greposimu.model.game.units.Unit;
public class OffContext { public class OffContext {
// unitID -> number of units
public Map<String, Integer> units;
public String heros; Map<Unit, Integer> units;
public int herosLevel;
public int luck; Hero hero;
public int morale; int heroLevel;
public Set<String> powers; int luck;
public Set<String> researches; 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,23 +1,22 @@
package com.bernard.greposimu.model.game; package com.bernard.greposimu.model.game;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.List;
import java.util.Map; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.json.JSONObject;
import com.bernard.greposimu.Utils; 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.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;
import com.bernard.greposimu.model.game.units.Hero.HeroCategory;
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; 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 { public class GameConfig {
@@ -30,171 +29,279 @@ public class GameConfig {
Set<Research> researches; 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() { public Set<Unit> getUnits() {
return Collections.unmodifiableSet(this.units); return Collections.unmodifiableSet(this.units);
} }
public Unit getUnit(String id) { public Unit getUnit(String id) {
return Utils.getIdentified(this.units, id); return Utils.throwingGetIdentified("unit",this.units, id);
} }
public Set<Hero> getHeroes() { public Set<Hero> getHeroes() {
return Collections.unmodifiableSet(this.heroes); return Collections.unmodifiableSet(this.heroes);
} }
public Hero getHero(String id) { public Hero getHero(String id) {
return Utils.getIdentified(this.heroes, id); return Utils.throwingGetIdentified("hero",this.heroes, id);
} }
public Set<Research> getResearches() { public Set<Research> getResearches() {
return Collections.unmodifiableSet(this.researches); return Collections.unmodifiableSet(this.researches);
} }
public Research getResearch(String id) { public Research getResearch(String id) {
return Utils.getIdentified(this.researches, id); return Utils.throwingGetIdentified("research",this.researches, id);
} }
public Building getBuilding(String bid) {
public GameConfig(JSONObject json) { return Utils.throwingGetIdentified("building", Set.of(Building.values()), bid);
JSONObject godsJ = json.getJSONObject("gods");
this.gods = new HashSet<>();
for(String g : godsJ.keySet()) {
JSONObject godJ = godsJ.getJSONObject(g);
gods.add(new God(godJ.getString("id"), godJ.getString("name")));
} }
JSONObject researchesJ = json.getJSONObject("researches"); public God getGod(String god) {
this.researches = new HashSet<>(); return Utils.throwingGetIdentified("god", this.gods, god);
for(String r : researchesJ.keySet()) {
JSONObject research = researchesJ.getJSONObject(r);
this.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 -> this.getResearch((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"); public Set<Building> getBuildings() {
this.units = new HashSet<>(); return Set.of(Building.values());
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 -> this.getResearch((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 -> this.getResearch((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; public Set<Power> getPowers() {
switch(unit.getString("attack_type")) { return powers;
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"), public Power getPower(String pid){
unit.getString("name"), System.out.println(powers.stream().map(Power::getId).sorted().collect(Collectors.joining("\n")));
unit.getString("description"), return Utils.throwingGetIdentified("power", powers, pid);
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 -> this.getResearch((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()
));
} }
@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));
} }
JSONObject heroesJ = json.getJSONObject("heroes"); public long getBuildingBuildingTime(Building b, int toLevel, int senateLevel, Set<CastedPower> powers, Set<Research> researches, Hero hero, int heroLevel){
this.heroes = new HashSet<>(); //TODO check this function is correct
for(String h : heroesJ.keySet()) { double modification_factor = 1.0;
JSONObject hero = heroesJ.getJSONObject(h); //TODO take availability into consideration
FightType ft = null; // models/heroes/player_hero.js:165
switch(hero.getString("attack_type")) { if (heroLevel != 0 && hero.getId().equals("christopholus"))
case "pierce": ft = FightType.PIERCE;break; modification_factor *= (1 - heroBonus(hero, heroLevel));
case "hack": ft = FightType.HACK;break;
case "distance": ft = FightType.DISTANCE;break; if (researches.contains(this.getResearch("building_crane"))) {
modification_factor -= craneBonus;
} }
JSONObject descargs = hero.getJSONObject("description_args").getJSONObject("1"); Optional<CastedPower> power = powers.stream().filter(p -> p.getPower().getId().endsWith("building_order_boost")).findAny();
this.heroes.add(new Hero( if (power.isPresent())
hero.getString("id"), modification_factor *= (1 - Integer.parseInt((String)power.get().getConfiguration().get("percent"), 10) / 100.0);
hero.getString("name"),
hero.getString("description"), long time = (long) (b.getBuildTime(toLevel) * senateReduction.get(senateLevel-1));
hero.getInt("speed"), time = (long) Math.floor(time * modification_factor);
hero.getInt("attack"), if (time < 1) time = 1;
ft,
hero.getInt("def_pierce"), return time;
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")
));
} }
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,22 +0,0 @@
package com.bernard.greposimu.model.game;
public class God implements Identified{
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;
}
}
@@ -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,27 +0,0 @@
package com.bernard.greposimu.model.game;
public class Resources {
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;
}
}
@@ -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;
}
}
@@ -3,8 +3,8 @@ package com.bernard.greposimu.model.game.researches;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import com.bernard.greposimu.model.game.Identified; import com.bernard.greposimu.model.game.util.Identified;
import com.bernard.greposimu.model.game.Resources; import com.bernard.greposimu.model.game.util.Resources;
public class Research implements Identified{ public class Research implements Identified{
@@ -12,10 +12,10 @@ public class Research implements Identified{
String name; String name;
String description; String description;
Set<Research> researchDependencies; Set<Research> researchDependencies;
Map<String,Integer> building_dependencies; Map<String,Integer> buildingDependencies;
Resources resources; Resources resources;
int required_time; int requiredTime;
int research_points; int researchPoints;
public Research(String id, String name, String description, Set<Research> researchDependencies, public Research(String id, String name, String description, Set<Research> researchDependencies,
Map<String, Integer> building_dependencies, Resources resources, int required_time, int research_points) { Map<String, Integer> building_dependencies, Resources resources, int required_time, int research_points) {
@@ -23,10 +23,10 @@ public class Research implements Identified{
this.name = name; this.name = name;
this.description = description; this.description = description;
this.researchDependencies = researchDependencies; this.researchDependencies = researchDependencies;
this.building_dependencies = building_dependencies; this.buildingDependencies = building_dependencies;
this.resources = resources; this.resources = resources;
this.required_time = required_time; this.requiredTime = required_time;
this.research_points = research_points; this.researchPoints = research_points;
} }
@Override @Override
@@ -46,21 +46,20 @@ public class Research implements Identified{
return researchDependencies; return researchDependencies;
} }
public Map<String, Integer> getBuilding_dependencies() { public Map<String, Integer> getBuildingDependencies() {
return building_dependencies; return buildingDependencies;
} }
public Resources getResources() { public Resources getResources() {
return resources; return resources;
} }
public int getRequired_time() { public int getRequiredTime() {
return required_time; return requiredTime;
} }
public int getResearch_points() { public int getResearchpoints() {
return research_points; return researchPoints;
} }
} }
@@ -1,13 +1,12 @@
package com.bernard.greposimu.model.game.units; package com.bernard.greposimu.model.game.units;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import com.bernard.greposimu.model.game.God; import com.bernard.greposimu.model.game.util.Identified;
import com.bernard.greposimu.model.game.Identified;
import com.bernard.greposimu.model.game.Resources;
public class Hero extends TerrestrialUnit{ public class Hero extends TerrestrialUnit implements Comparable<Hero>{
// Zero population // Zero population
// Non mythological // Non mythological
@@ -76,6 +75,34 @@ public class Hero extends TerrestrialUnit{
return powerValuePerLevel; 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 + "]";
}
} }
@@ -3,9 +3,9 @@ package com.bernard.greposimu.model.game.units;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import com.bernard.greposimu.model.game.God; import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.Resources;
import com.bernard.greposimu.model.game.researches.Research; import com.bernard.greposimu.model.game.researches.Research;
import com.bernard.greposimu.model.game.util.Resources;
public class NavalUnit extends Unit { public class NavalUnit extends Unit {
@@ -3,9 +3,9 @@ package com.bernard.greposimu.model.game.units;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import com.bernard.greposimu.model.game.God; import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.Resources;
import com.bernard.greposimu.model.game.researches.Research; import com.bernard.greposimu.model.game.researches.Research;
import com.bernard.greposimu.model.game.util.Resources;
public class TerrestrialUnit extends Unit{ public class TerrestrialUnit extends Unit{
@@ -3,9 +3,9 @@ package com.bernard.greposimu.model.game.units;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import com.bernard.greposimu.model.game.God; import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.Resources;
import com.bernard.greposimu.model.game.researches.Research; import com.bernard.greposimu.model.game.researches.Research;
import com.bernard.greposimu.model.game.util.Resources;
public class TransportUnit extends NavalUnit { public class TransportUnit extends NavalUnit {
int capacity; int capacity;
@@ -3,10 +3,10 @@ package com.bernard.greposimu.model.game.units;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import com.bernard.greposimu.model.game.God; import com.bernard.greposimu.model.game.gods.God;
import com.bernard.greposimu.model.game.Identified;
import com.bernard.greposimu.model.game.Resources;
import com.bernard.greposimu.model.game.researches.Research; 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{ public abstract class Unit implements Identified{
@@ -92,4 +92,9 @@ public abstract class Unit implements Identified{
return !this.isGround(); return !this.isGround();
} }
@Override
public String toString() {
return name;
}
} }
@@ -1,4 +1,4 @@
package com.bernard.greposimu.model.game; package com.bernard.greposimu.model.game.util;
public interface Identified { public interface Identified {
@@ -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.application.name=GrepoSimu
spring.devtools.restart.pollInterval=10s spring.devtools.restart.pollInterval=10s
spring.mvc.favicon.enabled=false 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> </head>
<body> <body>
<pre th:text="${content}"></pre> <pre th:text="${content}"></pre>
<main th:utext="${raw}"></main>
</body> </body>
</html> </html>
+98 -35
View File
@@ -85,6 +85,17 @@ span.fixed50px {
border: 0px; border: 0px;
margin: auto; margin: auto;
} }
.container {
display: inline-block;
}
.columns {
display: flex;
}
.formcol {
flex: 50%;
}
</style> </style>
@@ -92,16 +103,82 @@ span.fixed50px {
<body> <body>
<form action="#" th:object="${ctx}" 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"> <fieldset id="herosFields">
<legend>Héros</legend> <legend>Héros</legend>
<select name="heros" id="heros" th:field="*{hero}"> <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>
<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}">
<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})}"/>
<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="*{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 value="none">Aucun</option>
<option th:each="hero : ${heroes}" th:value="${hero.id}"><span th:text="${hero.name}"/></option> <option th:each="hero : ${heroes}" th:value="${hero.id}"><span th:text="${hero.name}"/></option>
</select> </select>
<label for="heroLevelSlider">Niveau du héros: (<span id="heroLevelSliderInfo" class="fixed50px"></span>)</label> <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}"> <input type="range" min="1" max="20" value="1" class="slider" id="heroLevelSlider" th:field="*{defHeroLevel}">
<script> <script>
var heroSlider = document.getElementById("heroLevelSlider"); var heroSlider = document.getElementById("heroLevelSlider");
@@ -113,16 +190,13 @@ span.fixed50px {
} }
</script> </script>
</fieldset> </fieldset>
<fieldset> <fieldset id="unitesFS">
<legend>Unités</legend> <legend>Unités</legend>
<table cellspacing="0" cellpadding="0"> <div th:each="unite : ${defUnits}" class="container">
<tr>
<td th:each="unite : ${defUnits}" class="squareContainer">
<img class="squareImage" th:for="'unite-'+${unite.id}" th:src="@{/images/units/{uname}.png(uname=${unite.id})}"/> <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}__]}"/> <br/>
</td> <input type="number" class="squareNumber" th:id="'unite-'+${unite.id}" th:field="*{defUnits[__${unite.id}__]}"/>
</tr> </div>
</table>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Batiments</legend> <legend>Batiments</legend>
@@ -144,43 +218,32 @@ span.fixed50px {
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Pouvoirs</legend> <legend>Pouvoirs</legend>
<table cellspacing="0" cellpadding="0"> <div th:each="power : ${defPowers}" class="container">
<tr> <img class="squareImage" th:for="'power-'+${power}" th:src="@{/images/powers/{pname}.png(pname=${power})}"/>
<td th:each="power : ${defPowers}" class="squareContainer"> <input type="checkbox" th:id="'power-'+${power}" th:field="*{defPowersAsMap[__${power}__]}" th:value="true"/>
<img class="squareImage" th:for="'power-'+${power.id}" th:src="@{/images/powers/{pname}.png(pname=${power.id})}"/> </div>
<input type="checkbox" th:id="'power-'+${power.id}" th:field="*{powersAsMap[__${power.id}__]}" th:value="true"/>
</td>
</tr>
</table>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Recherches</legend> <legend>Recherches</legend>
<table cellspacing="0" cellpadding="0"> <div th:each="research : ${defResearches}" class="container">
<tr> <img class="squareImage" th:for="'research-'+${research}" th:src="@{/images/researches/{rname}.png(rname=${research})}"/>
<td th:each="research : ${defResearches}" class="squareContainer"> <input type="checkbox" th:id="'research-'+${research}" th:field="*{defResearchesAsMap[__${research}__]}" th:value="true"/>
<img class="squareImage" th:for="'research-'+${research.id}" th:src="@{/images/researches/{rname}.png(rname=${research.id})}"/> </div>
<input type="checkbox" th:id="'research-'+${research.id}" th:field="*{researchesAsMap[__${research.id}__]}" th:value="true"/>
</td>
</tr>
</table>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Conseillers</legend> <legend>Conseillers</legend>
<table cellspacing="0" cellpadding="0"> <div th:each="counsellor : ${defCounsellors}" class="container">
<tr>
<td th:each="counsellor : ${defCounsellors}" class="squareContainer">
<img class="squareImage" th:for="'counsellor-'+${counsellor}" th:src="@{/images/counsellors/{cname}.png(cname=${counsellor})}"/> <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"/> <input type="checkbox" th:id="'counsellor-'+${counsellor}" th:field="*{defCounsellorsAsMap[__${counsellor}__]}" th:value="true"/>
</td> </div>
</tr>
</table>
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Bonus de jeu</legend> <legend>Bonus de jeu</legend>
<label for="nightBonus">Bonus de nuit :</label> <label for="nightBonus">Bonus de nuit :</label>
<input type="checkbox" id="nightBonus" th:field="*{nightBonus}" th:value="true"/><br/> <input type="checkbox" id="nightBonus" th:field="*{nightBonus}" th:value="true"/><br/>
</fieldset> </fieldset>
</fieldset>
</div>
<button type="button" id="compute">Calculer</button> <button type="button" id="compute">Calculer</button>
</form> </form>
@@ -1,16 +1,92 @@
package com.bernard.greposimu; package com.bernard.greposimu;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException; 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.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest; 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 @SpringBootTest
class GrepoSimuApplicationTests { class GrepoSimuApplicationTests {
@Test @Test
void loadGameData() throws IOException { void loadGameData() throws IOException {
System.out.println(GrepoSimu.makeGameData()); // 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"