From 54e4b4fe2a08166c1c86f8557c3601388e0dcd49 Mon Sep 17 00:00:00 2001 From: test Date: Sat, 25 May 2024 19:21:35 +1000 Subject: [PATCH] WIP web bindings --- .../discord/apiv1/enums/ResourceType.java | 6 +- .../discord/apiv1/enums/city/JavaCity.java | 64 ++++- .../city/building/imp/AResourceBuilding.java | 78 ++++-- .../enums/city/building/imp/FarmBuilding.java | 2 + .../commands/info/optimal/CityBranch.java | 95 ++++--- .../commands/info/optimal/OptimalBuild.java | 11 +- .../v2/binding/annotation/Binding.java | 8 + .../v2/binding/annotation/HtmlOptions.java | 12 + .../manager/v2/command/WebOption.java | 210 ++++++++++++++ .../impl/discord/binding/DiscordBindings.java | 10 +- .../v2/impl/pw/binding/PWBindings.java | 3 +- .../v2/impl/pw/commands/BankCommands.java | 2 +- .../v2/impl/pw/commands/IACommands.java | 9 +- .../v2/impl/pw/commands/UtilityCommands.java | 5 + .../v2/impl/pw/filter/PlaceholdersMap.java | 8 +- .../java/link/locutus/discord/db/WarDB.java | 2 - .../discord/db/entities/TaxBracket.java | 5 + .../locutus/discord/util/search/BFSUtil.java | 2 + .../discord/util/trade/TradeManager.java | 4 - .../util/update/LeavingBeigeAlert.java | 5 +- .../util/update/NationUpdateProcessor.java | 2 +- .../web/commands/binding/JavalinBindings.java | 1 + .../commands/options/WebOptionBindings.java | 263 ++++++++++++++++++ 23 files changed, 687 insertions(+), 120 deletions(-) create mode 100644 src/main/java/link/locutus/discord/commands/manager/v2/binding/annotation/HtmlOptions.java create mode 100644 src/main/java/link/locutus/discord/commands/manager/v2/command/WebOption.java create mode 100644 src/main/java/link/locutus/discord/web/commands/options/WebOptionBindings.java diff --git a/src/main/java/link/locutus/discord/apiv1/enums/ResourceType.java b/src/main/java/link/locutus/discord/apiv1/enums/ResourceType.java index 023ee53b..e9078172 100644 --- a/src/main/java/link/locutus/discord/apiv1/enums/ResourceType.java +++ b/src/main/java/link/locutus/discord/apiv1/enums/ResourceType.java @@ -992,12 +992,12 @@ public String url(Boolean isBuy, boolean shorten) { @Command(desc = "If this is a raw resource") public boolean isRaw() { - return inputs == null || inputs.length == 0 && cap > 0; + return inputs.length == 0 && cap > 0; } @Command(desc = "If this is a manufactured resource") public boolean isManufactured() { - return inputs != null && inputs.length > 0; + return inputs.length > 0; } @Command(desc = "The pollution modifier for this resource's production") @@ -1021,7 +1021,7 @@ public double getBoostFactor() { } public double getInput(Continent continent, double rads, Predicate hasProject, ICity city, int improvements) { - if (inputs == null) return 0; + if (inputs.length == 0) return 0; double base = getBaseProduction(continent, rads, hasProject, city.getLand(), -1); base = (base * baseProductionInverse) * baseInput; diff --git a/src/main/java/link/locutus/discord/apiv1/enums/city/JavaCity.java b/src/main/java/link/locutus/discord/apiv1/enums/city/JavaCity.java index 7e3fbcbe..62533a23 100644 --- a/src/main/java/link/locutus/discord/apiv1/enums/city/JavaCity.java +++ b/src/main/java/link/locutus/discord/apiv1/enums/city/JavaCity.java @@ -2,6 +2,7 @@ import link.locutus.discord.Locutus; import link.locutus.discord.apiv1.domains.City; +import link.locutus.discord.apiv1.enums.city.building.*; import link.locutus.discord.commands.info.optimal.CityBranch; import link.locutus.discord.config.Settings; import link.locutus.discord.db.entities.DBCity; @@ -16,11 +17,6 @@ import link.locutus.discord.util.search.BFSUtil; import link.locutus.discord.apiv1.enums.Continent; import link.locutus.discord.apiv1.enums.ResourceType; -import link.locutus.discord.apiv1.enums.city.building.Building; -import link.locutus.discord.apiv1.enums.city.building.Buildings; -import link.locutus.discord.apiv1.enums.city.building.CommerceBuilding; -import link.locutus.discord.apiv1.enums.city.building.MilitaryBuilding; -import link.locutus.discord.apiv1.enums.city.building.ResourceBuilding; import link.locutus.discord.apiv1.enums.city.project.Project; import link.locutus.discord.apiv1.enums.city.project.Projects; import it.unimi.dsi.fastutil.PriorityQueue; @@ -487,7 +483,7 @@ public JavaCity optimalBuild(Continent continent, double rads, int numCities, Pr }; valueFunction = modifyValueFunc.apply(valueFunction); - return optimalBuild(continent, valueFunction, goal, finalHasProject, timeout); + return optimalBuild(continent, valueFunction, goal, finalHasProject, timeout, rads); } public JavaCity roiBuild(Continent continent, double rads, int numCities, Predicate hasProject, double grossModifier, int days, long timeout) { @@ -499,7 +495,7 @@ public JavaCity roiBuild(Continent continent, double rads, int numCities, Predic origin.setAge(this.getAgeDays() + days / 2); JavaCity zeroed = new JavaCity(origin); - zeroed.zeroNonMilitary(); + zeroed.zeroNonMilitary().setOptimalPower(continent); int maxImp = (int) (this.getInfra() / 50); if (maxImp == zeroed.getNumBuildings()) return zeroed; @@ -525,19 +521,60 @@ public JavaCity roiBuild(Continent continent, double rads, int numCities, Predic valueFunction = modifyValueFunc.apply(valueFunction); - JavaCity optimal = zeroed.optimalBuild(continent, valueFunction, goal, finalHasProject, timeout); + JavaCity optimal = zeroed.optimalBuild(continent, valueFunction, goal, finalHasProject, timeout, rads); return optimal; } - public JavaCity optimalBuild(Continent continent, Function valueFunction, Predicate hasProject, long timeout) { - return optimalBuild(continent, valueFunction, null, hasProject, timeout); + public JavaCity optimalBuild(Continent continent, Function valueFunction, Predicate hasProject, long timeout, double rads) { + return optimalBuild(continent, valueFunction, null, hasProject, timeout, rads); } - public JavaCity optimalBuild(Continent continent, Function valueFunction, Function goal, Predicate hasProject, long timeout) { + public JavaCity zeroPower() { + for (int i = 0; i < buildings.length; i++) { + if (!(Buildings.get(i) instanceof PowerBuilding)) continue; + numBuildings -= buildings[i]; + buildings[i] = 0; + } + return this; + } + + public JavaCity setOptimalPower(Continent continent) { + int nuclear = 0; + int coal = 0; + int oil = 0; + int wind = 0; + double infra = getInfra(); + while (infra > 500 || (getInfra() > 2000 && infra > 250)) { + nuclear++; + infra -= Buildings.NUCLEAR_POWER.getInfraMax(); + } + while (infra > 250) { + if (continent.canBuild(Buildings.COAL_POWER)) { + coal++; + infra -= Buildings.COAL_POWER.getInfraMax(); + } else if (continent.canBuild(Buildings.OIL_POWER)) { + oil++; + infra -= Buildings.OIL_POWER.getInfraMax(); + } else { + break; + } + } + while (infra > 0) { + wind++; + infra -= Buildings.WIND_POWER.getInfraMax(); + } + set(Buildings.NUCLEAR_POWER, nuclear); + set(Buildings.COAL_POWER, coal); + set(Buildings.OIL_POWER, oil); + set(Buildings.WIND_POWER, wind); + return this; + } + + public JavaCity optimalBuild(Continent continent, Function valueFunction, Function goal, Predicate hasProject, long timeout, double rads) { if (goal == null) { goal = javaCity -> javaCity.getFreeInfra() < 50; } - CityBranch searchServices = new CityBranch(continent, -1d, -1d, false, hasProject); + CityBranch searchServices = new CityBranch(this, continent, false, rads, hasProject); Function, Double> valueFunction2 = e -> valueFunction.apply(e.getKey()); Function finalGoal = goal; @@ -567,8 +604,9 @@ public JavaCity optimalBuild(Continent continent, Function val throw new IllegalArgumentException("The city infrastructure (" + MathMan.format(origin.getInfra()) + ") is too low for the required buildings (required infra: " + MathMan.format(origin.getRequiredInfra()) + ", mmr: " + origin.getMMR() + ")"); } - Map.Entry optimized = BFSUtil.search(goal2, valueFunction2, valueCompletionFunction, searchServices, searchServices, new AbstractMap.SimpleEntry<>(origin, 4), queue, timeout); + Map.Entry optimized = BFSUtil.search(goal2, valueFunction2, valueCompletionFunction, searchServices, searchServices, new AbstractMap.SimpleEntry<>(origin, 0), queue, timeout); + System.out.println("Buildings " + optimized.getKey().getNumBuildings()); return optimized == null ? null : optimized.getKey(); } diff --git a/src/main/java/link/locutus/discord/apiv1/enums/city/building/imp/AResourceBuilding.java b/src/main/java/link/locutus/discord/apiv1/enums/city/building/imp/AResourceBuilding.java index f69a1dbc..71af020d 100644 --- a/src/main/java/link/locutus/discord/apiv1/enums/city/building/imp/AResourceBuilding.java +++ b/src/main/java/link/locutus/discord/apiv1/enums/city/building/imp/AResourceBuilding.java @@ -8,9 +8,12 @@ import link.locutus.discord.apiv1.enums.city.JavaCity; import link.locutus.discord.apiv1.enums.city.building.ResourceBuilding; import link.locutus.discord.apiv1.enums.city.project.Project; +import link.locutus.discord.util.PW; import java.util.List; +import java.util.function.Function; import java.util.function.Predicate; +import java.util.function.Supplier; public class AResourceBuilding extends ABuilding implements ResourceBuilding { private final int baseInput; @@ -18,6 +21,8 @@ public class AResourceBuilding extends ABuilding implements ResourceBuilding { private final ResourceType output; private final ResourceType[] inputs; private boolean[] continents; + private Supplier outputValue; + private InputValueFunc inputValueFunc; public AResourceBuilding(BuildingBuilder parent, ResourceType output) { this(parent, output.getBaseInput(), output.getBoostFactor(), output, output.getInputs()); @@ -28,6 +33,52 @@ public AResourceBuilding(BuildingBuilder parent, int baseInput, double boostFact this.boostFactor = boostFactor; this.output = output; this.inputs = inputs; + this.outputValue = () -> { + double value = ResourceType.convertedTotalPositive(output, 1); + outputValue = () -> value; + return value; + }; + if (inputs.length == 0) { + this.inputValueFunc = (c, r, h, b, a, p) -> 0d; + } else { + this.inputValueFunc = (c0, r0, h0, b0, a0, p0) -> { + double factor = 1d / output.getManufacturingMultiplier(); + + InputValueFunc newFunc; + if (inputs.length == 1) { + double value = ResourceType.convertedTotalNegative(inputs[0], 1); + newFunc = (c, r, h, b, a, p) -> { + double inputAmt = factor * p; + return -value * inputAmt; + }; + } else { + double[] values = new double[inputs.length]; + for (int i = 0; i < inputs.length; i++) { + values[i] = ResourceType.convertedTotalNegative(inputs[i], 1); + } + newFunc = (c, r, h, b, a, p) -> { + double inputAmt = factor * p; + double profit = 0; + for (int i = 0; i < values.length; i++) { + profit -= values[i] * inputAmt; + } + return profit; + }; + } + inputValueFunc = (c, r, h, b, a, p) -> { + if (p != 0) { + return newFunc.apply(c, r, h, b, a, p); + } + return 0d; + }; + return newFunc.apply(c0, r0, h0, b0, a0, p0); + }; + + } + } + + private static interface InputValueFunc { + public double apply(Continent continent, double rads, Predicate hasProjects, ICity city, int amt, double production); } public AResourceBuilding continents(Continent... continents) { @@ -58,30 +109,9 @@ public List getResourceTypesConsumed() { @Override public double profitConverted(Continent continent, double rads, Predicate hasProjects, ICity city, int amt) { double profit = super.profitConverted(continent, rads, hasProjects, city, amt); - - int improvements = city.getBuilding(this); - - - double production = output.getProduction(continent, rads, hasProjects, city.getLand(), improvements, -1); - if (production != 0) { - profit += ResourceType.convertedTotalPositive(output, production); - - double inputAmt = output.getInput(continent, rads, hasProjects, city, improvements); - - if (inputAmt > 0) { - switch (inputs.length) { - case 0: - break; - case 1: - profit -= ResourceType.convertedTotalNegative(inputs[0], inputAmt); - break; - default: - for (ResourceType input : inputs) { - profit -= ResourceType.convertedTotalNegative(input, inputAmt); - } - } - } - } + double production = output.getProduction(continent, rads, hasProjects, city.getLand(), amt, -1); + profit += production * outputValue.get(); + profit += inputValueFunc.apply(continent, rads, hasProjects, city, amt, production); return profit; } diff --git a/src/main/java/link/locutus/discord/apiv1/enums/city/building/imp/FarmBuilding.java b/src/main/java/link/locutus/discord/apiv1/enums/city/building/imp/FarmBuilding.java index 663a6c48..68590ef4 100644 --- a/src/main/java/link/locutus/discord/apiv1/enums/city/building/imp/FarmBuilding.java +++ b/src/main/java/link/locutus/discord/apiv1/enums/city/building/imp/FarmBuilding.java @@ -1,7 +1,9 @@ package link.locutus.discord.apiv1.enums.city.building.imp; import link.locutus.discord.apiv1.domains.Nation; +import link.locutus.discord.apiv1.enums.Continent; import link.locutus.discord.apiv1.enums.ResourceType; +import link.locutus.discord.apiv1.enums.city.ICity; import link.locutus.discord.apiv1.enums.city.building.Building; import link.locutus.discord.apiv1.enums.city.project.Project; import link.locutus.discord.apiv1.enums.city.project.Projects; diff --git a/src/main/java/link/locutus/discord/commands/info/optimal/CityBranch.java b/src/main/java/link/locutus/discord/commands/info/optimal/CityBranch.java index df2eb725..55c0c1cb 100644 --- a/src/main/java/link/locutus/discord/commands/info/optimal/CityBranch.java +++ b/src/main/java/link/locutus/discord/commands/info/optimal/CityBranch.java @@ -9,8 +9,7 @@ import link.locutus.discord.apiv1.enums.city.project.Project; import link.locutus.discord.apiv1.enums.city.project.Projects; -import java.util.AbstractMap; -import java.util.Map; +import java.util.*; import java.util.function.BiConsumer; import java.util.function.Consumer; import java.util.function.Predicate; @@ -19,12 +18,42 @@ public class CityBranch implements BiConsumer, Prio private final Continent continent; private final Predicate hasProject; private final ObjectArrayFIFOQueue> pool; + private final Building[] buildings; private boolean usedOrigin = false; private Building originBuilding = null; - public CityBranch(Continent continent, double maxDisease, double minPop, boolean selfSufficient, Predicate hasProject) { + public CityBranch(JavaCity origin, Continent continent, boolean selfSufficient, double rads, Predicate hasProject) { this.continent = continent; this.hasProject = hasProject; + Map rssBuildings = new HashMap<>(); + for (Building building : Buildings.values()) { + if (!building.canBuild(continent)) continue; + if (!(building instanceof ResourceBuilding rssBuild)) continue; + if (rads <= 0 && building == Buildings.FARM) continue; + int cap = rssBuild.cap(hasProject); + double profit = rssBuild.profitConverted(continent, rads, hasProject, origin, cap) / cap; + rssBuildings.put(rssBuild, profit); + } + List rssSorted; + if (selfSufficient) { + List rawSorted = new ArrayList<>(rssBuildings.entrySet().stream().filter(e -> e.getKey().getResourceProduced().isRaw()).sorted(Map.Entry.comparingByValue()).map(Map.Entry::getKey).toList().reversed()); + List manuSorted = new ArrayList<>(rssBuildings.entrySet().stream().filter(e -> !e.getKey().getResourceProduced().isRaw()).sorted(Map.Entry.comparingByValue()).map(Map.Entry::getKey).toList().reversed()); + rssSorted = new ArrayList<>(rawSorted); + rssSorted.addAll(manuSorted); + } else { + rssSorted = new ArrayList<>(rssBuildings.entrySet().stream().sorted(Map.Entry.comparingByValue()).map(Map.Entry::getKey).toList().reversed()); + } + for (Building building : rssSorted) { + System.out.println(building.name() + " " + rssBuildings.get(building) + " profit"); + } + List allBuildings = new ArrayList<>(rssSorted); + for (Building building : Buildings.values()) { + if (building instanceof CommerceBuilding) allBuildings.add(building); + } + for (Building building : Buildings.values()) { + if (building instanceof ServiceBuilding) allBuildings.add(building); + } + this.buildings = allBuildings.toArray(new Building[0]); this.pool = new ObjectArrayFIFOQueue<>(500000); } @@ -65,46 +94,24 @@ public void accept(Map.Entry originPair, PriorityQueue 500 || (unpowered > 250 && origin.getInfra() > 2000)) { - cities.enqueue(create(originPair, Buildings.NUCLEAR_POWER, 4)); - return; - } - if (unpowered > 250) { - if (Buildings.OIL_WELL.canBuild(continent)) { - cities.enqueue(create(originPair, Buildings.OIL_POWER, 4)); - } else { - cities.enqueue(create(originPair, Buildings.COAL_POWER, 4)); - } - return; - } - if (unpowered > 0) { - cities.enqueue(create(originPair, Buildings.WIND_POWER, 4)); - return; - } - } - int freeSlots = origin.getFreeSlots(); if (freeSlots <= 0) return; int minBuilding = originPair.getValue(); - int maxBuilding = Buildings.BUILDINGS.length - 4; - boolean buildMoreRaws = true; - boolean buildMoreManu = true; - boolean buildMoreCommerce = true; + int maxBuilding = this.buildings.length; + Building minBuildingType = buildings[minBuilding]; + boolean buildMore = true; { - Building minBuildingType = Buildings.get(minBuilding); - int amt = origin.getBuildingOrdinal(minBuilding); + int amt = origin.getBuildingOrdinal(minBuildingType.ordinal()); if (amt != 0) { if (minBuildingType instanceof ResourceBuilding rssBuild) { if (amt < minBuildingType.cap(hasProject)) { - if (rssBuild.getResourceProduced().isRaw()) { - buildMoreRaws = false; - } else { - buildMoreManu = false; - } + buildMore = false; +// if (rssBuild.getResourceProduced().isRaw()) { +// buildMoreRaws = false; +// } else { +// buildMoreManu = false; +// } } } else if (minBuildingType instanceof CommerceBuilding) { minBuildingType.cap(hasProject); @@ -123,22 +130,22 @@ public void accept(Map.Entry originPair, PriorityQueue= building.cap(hasProject)) continue; if (building instanceof ResourceBuilding rssBuild) { ResourceType rss = rssBuild.getResourceProduced(); - if (rss.isRaw()) { - if (buildMoreRaws || i == minBuilding) { - cities.enqueue(create(originPair, building, i)); - } - } else if (buildMoreManu || i == minBuilding) { + if (buildMore || i == minBuilding) { cities.enqueue(create(originPair, building, i)); } +// if (rss.isRaw()) { +// +// } else if (buildMoreManu || i == minBuilding) { +// cities.enqueue(create(originPair, building, i)); +// } } else if (building instanceof CommerceBuilding) { - if (origin.calcCommerce(hasProject) >= maxCommerce) continue; { cities.enqueue(create(originPair, building, i)); diff --git a/src/main/java/link/locutus/discord/commands/info/optimal/OptimalBuild.java b/src/main/java/link/locutus/discord/commands/info/optimal/OptimalBuild.java index 730eed09..6cb0b357 100644 --- a/src/main/java/link/locutus/discord/commands/info/optimal/OptimalBuild.java +++ b/src/main/java/link/locutus/discord/commands/info/optimal/OptimalBuild.java @@ -297,6 +297,7 @@ public String onCommand(IMessageIO io, Guild guild, User author, DBNation me, Li if (land != null) origin.setLand(land); if (age != null) origin.setAge(age); if (continent == null) continent = me.getContinent(); + origin.setOptimalPower(continent); if (baseRadiation == -1) { baseRadiation = Locutus.imp().getTradeManager().getGlobalRadiation(continent) + Locutus.imp().getTradeManager().getGlobalRadiation(); @@ -308,7 +309,7 @@ public String onCommand(IMessageIO io, Guild guild, User author, DBNation me, Li double radIndex = baseRadiation; double radsFactor = continent == Continent.ANTARCTICA ? 0.5 : 1; - double rads = (1 + (radIndex / (-1000))) * radsFactor; + double rads = (1 + (Math.min(1000, radIndex) / (-1000))) * radsFactor; int numCities = me.getCities(); @@ -392,9 +393,6 @@ public String onCommand(IMessageIO io, Guild guild, User author, DBNation me, Li }; } - Function finalValueFunc = valueFunc; - Function, Function> modifyValueFunc = f -> finalValueFunc; - Function goal = javaCity -> javaCity.getFreeInfra() < 50; if (diseaseLimit != null) { @@ -482,7 +480,6 @@ public String onCommand(IMessageIO io, Guild guild, User author, DBNation me, Li }; } - if (positiceCash) { Function parentGoal = goal; @@ -493,7 +490,6 @@ public String onCommand(IMessageIO io, Guild guild, User author, DBNation me, Li Arrays.fill(profitBuffer, 0); city.profit(finalContinent, rads, -1L, hasProject, profitBuffer, numCities, finalMe.getGrossModifier(), 12); profitBuffer[0] += 500000d / numCities; - double original = profitBuffer[0]; for (MilitaryUnit unit : MilitaryUnit.values) { MilitaryBuilding building = unit.getBuilding(); @@ -516,6 +512,9 @@ public String onCommand(IMessageIO io, Guild guild, User author, DBNation me, Li GuildDB rootDb = Locutus.imp().getGuildDB(root); long timeout = db.isWhitelisted() && (Roles.ADMIN.hasOnRoot(author) || db.hasCoalitionPermsOnRoot(Coalition.RAIDPERMS)) ? 15000 : 5000; + Function finalValueFunc = valueFunc; + Function, Function> modifyValueFunc = f -> finalValueFunc; + JavaCity optimized; if (days == null) { optimized = origin.optimalBuild(continent, rads, numCities, hasProject, finalMe.getGrossModifier(), timeout, modifyValueFunc, goal); diff --git a/src/main/java/link/locutus/discord/commands/manager/v2/binding/annotation/Binding.java b/src/main/java/link/locutus/discord/commands/manager/v2/binding/annotation/Binding.java index 157e7ee1..34be5fcf 100644 --- a/src/main/java/link/locutus/discord/commands/manager/v2/binding/annotation/Binding.java +++ b/src/main/java/link/locutus/discord/commands/manager/v2/binding/annotation/Binding.java @@ -15,4 +15,12 @@ Class[] types() default {}; boolean multiple() default false; + + /** + * Defer to specific component classes making up this type e.g. Set<MyType> would have MyType as a component + * Leave this blank unless the component type is NOT correctly inferred + * e.g. NationList should defer -> DBNation + * @return + */ + Class[] components() default {}; } \ No newline at end of file diff --git a/src/main/java/link/locutus/discord/commands/manager/v2/binding/annotation/HtmlOptions.java b/src/main/java/link/locutus/discord/commands/manager/v2/binding/annotation/HtmlOptions.java new file mode 100644 index 00000000..40f656ab --- /dev/null +++ b/src/main/java/link/locutus/discord/commands/manager/v2/binding/annotation/HtmlOptions.java @@ -0,0 +1,12 @@ +package link.locutus.discord.commands.manager.v2.binding.annotation; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.PARAMETER, ElementType.METHOD}) +public @interface HtmlOptions { +} + diff --git a/src/main/java/link/locutus/discord/commands/manager/v2/command/WebOption.java b/src/main/java/link/locutus/discord/commands/manager/v2/command/WebOption.java new file mode 100644 index 00000000..2eca6fb9 --- /dev/null +++ b/src/main/java/link/locutus/discord/commands/manager/v2/command/WebOption.java @@ -0,0 +1,210 @@ +package link.locutus.discord.commands.manager.v2.command; + +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.gson.reflect.TypeToken; +import link.locutus.discord.commands.manager.v2.binding.Key; +import link.locutus.discord.commands.manager.v2.binding.ValueStore; +import link.locutus.discord.commands.manager.v2.binding.annotation.Autocomplete; +import link.locutus.discord.commands.manager.v2.binding.bindings.TypedFunction; +import link.locutus.discord.db.GuildDB; +import link.locutus.discord.db.entities.DBNation; +import link.locutus.discord.util.scheduler.TriFunction; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.entities.channel.attribute.ICategorizableChannel; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.*; +import java.util.function.Predicate; + +public class WebOption { + private Key key; + private boolean allowCompletions; + private JsonArray options; + private TriFunction queryOptions; + private boolean requiresGuild; + private boolean requiresNation; + private boolean requiresUser; + + private static Set ignoreClass = new HashSet<>(Arrays.asList( + Set.class, + Map.class, + List.class, + Predicate.class, + TypedFunction.class, + boolean.class, + Boolean.class, + int.class, + Integer.class, + double.class, + Double.class, + long.class, + Long.class, + Number.class, + String.class + )); + + public WebOption setRequiresGuild() { + this.requiresGuild = true; + return this; + } + + public WebOption setRequiresNation() { + this.requiresNation = true; + return this; + } + + public WebOption setRequiresUser() { + this.requiresUser = true; + return this; + } + + public static List getComponentClasses(Type type) { + List components = new ArrayList<>(); + if (type instanceof ParameterizedType) { + ParameterizedType pType = (ParameterizedType) type; + for (Type argType : pType.getActualTypeArguments()) { + components.addAll(getComponentClasses(argType)); + } + } else if (type instanceof Class) { + Class clazz = (Class) type; + if (ignoreClass.contains(clazz)) return components; + components.add(clazz); + } + return components; + } + + public WebOption(Class t) { + this(Key.of(t)); + } + + public WebOption(Key key) { + this.key = key; + } + + public static WebOption fromEnum(Class> enumClass) { + List names = Arrays.stream(enumClass.getEnumConstants()).map(Enum::name).toList(); + return new WebOption(Key.of(enumClass)).setOptions(names); + } + + public Key getKey() { + return key; + } + + public WebOption setOptions(String[] options) { + this.options = new JsonArray(); + for (String o : options) this.options.add(o); + return this; + } + + public WebOption setOptions(Class> enumClass) { + this.options = new JsonArray(); + for (Enum e : enumClass.getEnumConstants()) this.options.add(e.name()); + return this; + } + + public WebOption setOptions(JsonArray arr) { + this.options = arr; + return this; + } + + public WebOption addOption(Map data) { + if (this.options == null) this.options = new JsonArray(); + JsonObject obj = new JsonObject(); + for (Map.Entry entry : data.entrySet()) { + obj.addProperty(entry.getKey(), entry.getValue()); + } + this.options.add(obj); + return this; + } + + public WebOption addOption(JsonObject obj) { + if (this.options == null) this.options = new JsonArray(); + this.options.add(obj); + return this; + } + + public WebOption setOptions(List options) { + this.options = new JsonArray(); + for (String o : options) this.options.add(o); + return this; + } + + public WebOption setQuery(TriFunction queryOptions) { + this.queryOptions = queryOptions; + return this; + } + + public WebOption setQueryMap(TriFunction>> queryOptions) { + this.queryOptions = (guild, user, nation) -> { + JsonArray arr = new JsonArray(); + for (Map map : queryOptions.apply(guild, user, nation)) { + JsonObject obj = new JsonObject(); + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() != null) obj.addProperty(entry.getKey(), entry.getValue()); + } + arr.add(obj); + } + return arr; + }; + return this; + } + + public WebOption allowCompletions() { + this.allowCompletions = true; + return this; + } + + public WebOption checkAllowCompletions(Key key, ValueStore store) { + Key completer = key.append(Autocomplete.class); + if (store.get(completer) != null) allowCompletions(); + return this; + } + + public JsonArray getOptions() { + return options; + } + + public String getName() { + return key.toSimpleString(); + } + + public boolean isAllowQuery() { + return queryOptions != null; + } + + public boolean isAllowCompletions() { + return allowCompletions; + } + + public JsonObject toJson() { + JsonObject json = new JsonObject(); + if (options != null) { + json.add("options", options); + } + if (isAllowQuery()) { + json.addProperty("query", true); + } + if (isAllowCompletions()) { + json.addProperty("completions", true); + } + if (requiresGuild) { + json.addProperty("guild", true); + } + if (requiresNation) { + json.addProperty("nation", true); + } + if (requiresUser) { + json.addProperty("user", true); + } + return json; + } + + public WebOption setType(Class type) { + this.key = Key.of(type); + return this; + } +} diff --git a/src/main/java/link/locutus/discord/commands/manager/v2/impl/discord/binding/DiscordBindings.java b/src/main/java/link/locutus/discord/commands/manager/v2/impl/discord/binding/DiscordBindings.java index eb477776..1aac0092 100644 --- a/src/main/java/link/locutus/discord/commands/manager/v2/impl/discord/binding/DiscordBindings.java +++ b/src/main/java/link/locutus/discord/commands/manager/v2/impl/discord/binding/DiscordBindings.java @@ -2,7 +2,6 @@ import cn.easyproject.easyocr.ImageType; import link.locutus.discord.Locutus; -import link.locutus.discord.apiv1.enums.Continent; import link.locutus.discord.commands.manager.v2.binding.BindingHelper; import link.locutus.discord.commands.manager.v2.binding.ValueStore; import link.locutus.discord.commands.manager.v2.binding.annotation.Binding; @@ -13,10 +12,8 @@ import link.locutus.discord.commands.manager.v2.command.CommandCallable; import link.locutus.discord.commands.manager.v2.command.IMessageIO; import link.locutus.discord.commands.manager.v2.command.ParameterData; -import link.locutus.discord.commands.manager.v2.command.ParametricCallable; import link.locutus.discord.commands.manager.v2.perm.PermissionHandler; import link.locutus.discord.db.entities.DBNation; -import link.locutus.discord.pnw.BeigeReason; import link.locutus.discord.user.Roles; import link.locutus.discord.util.MathMan; import link.locutus.discord.util.StringMan; @@ -34,7 +31,6 @@ import org.json.JSONObject; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -182,7 +178,8 @@ public Guild guild() { throw new IllegalStateException("No guild set in command locals."); } - @Binding(examples = "#channel", value = "A discord channel name or mention") + @Binding(examples = "#channel", value = "A discord channel name or mention", + components = TextChannel.class) public MessageChannel channel(@Me Guild guild, String channel) { MessageChannel GuildMessageChannel = DiscordUtil.getChannel(guild, channel); if (GuildMessageChannel == null) throw new IllegalArgumentException("No channel found for " + channel); @@ -222,7 +219,8 @@ public Map> roleSetMap(@Me Guild guild, String input) { return result; } - @Binding(examples = "#channel", value = "A categorized discord guild channel name or mention") + @Binding(examples = "#channel", value = "A categorized discord guild channel name or mention", + components = TextChannel.class) public ICategorizableChannel categorizableChannel(@Me Guild guild, String input) { MessageChannel channel = DiscordUtil.getChannel(guild, input); if (channel == null) throw new IllegalArgumentException("No channel found for " + null); diff --git a/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/binding/PWBindings.java b/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/binding/PWBindings.java index 4471c384..17b42939 100644 --- a/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/binding/PWBindings.java +++ b/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/binding/PWBindings.java @@ -841,7 +841,8 @@ public static Set nations(ParameterData data, @Default @Me Guild guild return nations; } - @Binding(examples = "borg,AA:Cataclysm,#position>1", value = "A comma separated list of nations, alliances and filters") + @Binding(examples = "borg,AA:Cataclysm,#position>1", value = "A comma separated list of nations, alliances and filters", + components = DBNation.class) public static NationList nationList(ParameterData data, @Default @Me Guild guild, String input, @Default @Me User author, @Default @Me DBNation me) { return new SimpleNationList(nations(data, guild, input, author, me)).setFilter(input); } diff --git a/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/commands/BankCommands.java b/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/commands/BankCommands.java index f690be8f..358afc97 100644 --- a/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/commands/BankCommands.java +++ b/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/commands/BankCommands.java @@ -1553,7 +1553,7 @@ public String revenueSheet(@Me IMessageIO io, @Me GuildDB db, NationList nations double profit = city1.profitConvertedCached(nation.getContinent(), nation.getRads(), nation::hasProject, nation.getCities(), nation.getGrossModifier()); JavaCity origin = new JavaCity(city1); - origin.zeroNonMilitary(); + origin.zeroNonMilitary().setOptimalPower(nation.getContinent()); try { JavaCity optimal = origin.optimalBuild(nation, 0); double profitOptimal = 0; diff --git a/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/commands/IACommands.java b/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/commands/IACommands.java index 6e500317..680ed27a 100644 --- a/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/commands/IACommands.java +++ b/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/commands/IACommands.java @@ -1487,12 +1487,9 @@ public static String setRank(@Me User author, @Me IMessageIO channel, @Me GuildD DBAlliancePosition myPosition = me.getAlliancePosition(); DBAlliancePosition nationPosition = nation.getAlliancePosition(); if (!Roles.ADMIN.hasOnRoot(author)) { - if (me.getAlliance_id() != allianceId || myPosition == null) { - // cannot promote above officer unless admin - if (!Roles.ADMIN.has(author, db.getGuild())) { - if (position.hasAnyOfficerPermissions()) { - return "You do not have permission to grant permissions you currently do not posses in the alliance"; - } + if (me.getAlliance_id() != allianceId || myPosition == null || !db.isAllianceId(myPosition.getAlliance_id())) { + if (position.hasAnyOfficerPermissions()) { + return "You do not have permission to grant permissions you currently do not posses in the alliance"; } } else { if (position.getPosition_level() > myPosition.getPosition_level()) { diff --git a/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/commands/UtilityCommands.java b/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/commands/UtilityCommands.java index 3c6f0f0d..af1285a8 100644 --- a/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/commands/UtilityCommands.java +++ b/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/commands/UtilityCommands.java @@ -2359,6 +2359,9 @@ public String infraROI(DBCity city, @Range(min=600,max=3000) int infraLevel, int numCities = nation.getCities(); if (continent == null) continent = nation.getContinent(); + origin.setOptimalPower(continent); + originMinus50.setOptimalPower(continent); + if (rads == null) rads = nation.getRads(); Predicate hasProject = forceProjects != null ? f -> forceProjects.contains(f) || nation.hasProject(f) : nation::hasProject; double grossModifier = DBNation.getGrossModifier(false, openMarkets, hasProject.test(Projects.GOVERNMENT_SUPPORT_AGENCY)); @@ -2407,6 +2410,8 @@ public String landROI(DBCity city, @Range(min=600,max=10000) double landLevel, int numCities = nation.getCities(); if (continent == null) continent = nation.getContinent(); + origin.setOptimalPower(continent); + originMinus50.setOptimalPower(continent); if (rads == null) rads = nation.getRads(); Predicate hasProject = forceProjects != null ? f -> forceProjects.contains(f) || nation.hasProject(f) : nation::hasProject; double grossModifier = DBNation.getGrossModifier(false, openMarkets, hasProject.test(Projects.GOVERNMENT_SUPPORT_AGENCY)); diff --git a/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/filter/PlaceholdersMap.java b/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/filter/PlaceholdersMap.java index 4d4d3c20..4060c88e 100644 --- a/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/filter/PlaceholdersMap.java +++ b/src/main/java/link/locutus/discord/commands/manager/v2/impl/pw/filter/PlaceholdersMap.java @@ -18,12 +18,7 @@ import link.locutus.discord.apiv1.enums.city.project.Projects; import link.locutus.discord.commands.manager.v2.binding.Key; import link.locutus.discord.commands.manager.v2.binding.ValueStore; -import link.locutus.discord.commands.manager.v2.binding.annotation.Command; -import link.locutus.discord.commands.manager.v2.binding.annotation.Default; -import link.locutus.discord.commands.manager.v2.binding.annotation.Me; -import link.locutus.discord.commands.manager.v2.binding.annotation.NoFormat; -import link.locutus.discord.commands.manager.v2.binding.annotation.Switch; -import link.locutus.discord.commands.manager.v2.binding.annotation.Timestamp; +import link.locutus.discord.commands.manager.v2.binding.annotation.*; import link.locutus.discord.commands.manager.v2.binding.bindings.Placeholders; import link.locutus.discord.commands.manager.v2.binding.bindings.PrimitiveBindings; import link.locutus.discord.commands.manager.v2.binding.bindings.SimplePlaceholders; @@ -442,6 +437,7 @@ public String getName(NationOrAlliance o) { return o.getName(); } + @Binding(value = "A comma separated list of items") @Override public Set parseSet(ValueStore store2, String input) { if (input.contains("#")) { diff --git a/src/main/java/link/locutus/discord/db/WarDB.java b/src/main/java/link/locutus/discord/db/WarDB.java index f79bcb50..a39a606a 100644 --- a/src/main/java/link/locutus/discord/db/WarDB.java +++ b/src/main/java/link/locutus/discord/db/WarDB.java @@ -2357,10 +2357,8 @@ public boolean test(WarAttack v3Attack) { synchronized (attackList) { attackList.add(attack); if (attackList.size() > 1000) { - System.out.println("Save " + attack.getWar_attack_id()); saveAttacks(attackList); attackList.clear(); - System.out.println("Save end"); } } return false; diff --git a/src/main/java/link/locutus/discord/db/entities/TaxBracket.java b/src/main/java/link/locutus/discord/db/entities/TaxBracket.java index 466b2672..be635f8c 100644 --- a/src/main/java/link/locutus/discord/db/entities/TaxBracket.java +++ b/src/main/java/link/locutus/discord/db/entities/TaxBracket.java @@ -122,6 +122,11 @@ public String toString() { return (allianceId > 0 ? DBAlliance.getOrCreate(allianceId).getQualifiedId() + "- " : "") + ((name != null && !name.isEmpty()) ? (name + "- ") : "") + "#" + taxId + " (" + moneyRate + "/" + rssRate + ")"; } + public String getSubText() { + return (allianceId > 0 ? DBAlliance.getOrCreate(allianceId).getQualifiedId() + "- " : "") + "#" + taxId + " (" + moneyRate + "/" + rssRate + ")"; + } + + @Command(desc = "Tax rate object") public TaxRate getTaxRate() { return new TaxRate(moneyRate, rssRate); diff --git a/src/main/java/link/locutus/discord/util/search/BFSUtil.java b/src/main/java/link/locutus/discord/util/search/BFSUtil.java index 723ff60e..59a9d854 100644 --- a/src/main/java/link/locutus/discord/util/search/BFSUtil.java +++ b/src/main/java/link/locutus/discord/util/search/BFSUtil.java @@ -111,6 +111,8 @@ public static T search(Function goal, Function val branch.accept(next, queue); } + long diff = System.currentTimeMillis() - start; + System.out.println("BFS. Searched " + i + " in " + diff + " for a rate of " + (i * 1000d / diff) + " per second"); return max; } } diff --git a/src/main/java/link/locutus/discord/util/trade/TradeManager.java b/src/main/java/link/locutus/discord/util/trade/TradeManager.java index 60458712..ba4e4a80 100644 --- a/src/main/java/link/locutus/discord/util/trade/TradeManager.java +++ b/src/main/java/link/locutus/discord/util/trade/TradeManager.java @@ -601,22 +601,18 @@ public int getPrice(ResourceType type, boolean isBuy) { } public int getHigh(ResourceType type) { - if (type == ResourceType.MONEY) return 1; return high[type.ordinal()]; } public int getLow(ResourceType type) { - if (type == ResourceType.MONEY) return 1; return low[type.ordinal()]; } public double getHighAvg(ResourceType type) { - if (type == ResourceType.MONEY) return 1; return highAvg[type.ordinal()]; } public double getLowAvg(ResourceType type) { - if (type == ResourceType.MONEY) return 1; return lowAvg[type.ordinal()]; } diff --git a/src/main/java/link/locutus/discord/util/update/LeavingBeigeAlert.java b/src/main/java/link/locutus/discord/util/update/LeavingBeigeAlert.java index b8111e18..dba87788 100644 --- a/src/main/java/link/locutus/discord/util/update/LeavingBeigeAlert.java +++ b/src/main/java/link/locutus/discord/util/update/LeavingBeigeAlert.java @@ -156,7 +156,7 @@ public static boolean testBeigeAlertAuto(GuildDB db, Member member, Role beigeAl NationMeta.BeigeAlertRequiredStatus requiredStatus = attacker.getBeigeRequiredStatus(NationMeta.BeigeAlertRequiredStatus.ONLINE); if ((attacker.active_m() <= 15 || requiredStatus.getApplies().test(member) || attacker.getNation_id() == Settings.INSTANCE.NATION_ID)) { - NationMeta.BeigeAlertMode mode = attacker.getBeigeAlertMode(NationMeta.BeigeAlertMode.NONES); + NationMeta.BeigeAlertMode mode = attacker.getBeigeAlertMode(NationMeta.BeigeAlertMode.NO_ALERTS); if (mode == NationMeta.BeigeAlertMode.NO_ALERTS) { if (throwError) throw new IllegalArgumentException("You have disabled beige alerts. See " + CM.alerts.beige.beigeAlertMode.cmd.toSlashMention()); return false; @@ -294,9 +294,8 @@ private void alertNations(long nextTurn, boolean update) { NationMeta.BeigeAlertRequiredStatus requiredStatus = attacker.getBeigeRequiredStatus(NationMeta.BeigeAlertRequiredStatus.ONLINE); - NationMeta.BeigeAlertMode mode = attacker.getBeigeAlertMode(NationMeta.BeigeAlertMode.NONES); + NationMeta.BeigeAlertMode mode = attacker.getBeigeAlertMode(NationMeta.BeigeAlertMode.NO_ALERTS); if (mode == NationMeta.BeigeAlertMode.NO_ALERTS) continue; - if (mode == null) mode = NationMeta.BeigeAlertMode.NONES; double requiredLoot = 15000000; ByteBuffer requiredLootBuf = attacker.getMeta(NationMeta.BEIGE_ALERT_REQUIRED_LOOT); diff --git a/src/main/java/link/locutus/discord/util/update/NationUpdateProcessor.java b/src/main/java/link/locutus/discord/util/update/NationUpdateProcessor.java index 3cc0b70c..9b5282a4 100644 --- a/src/main/java/link/locutus/discord/util/update/NationUpdateProcessor.java +++ b/src/main/java/link/locutus/discord/util/update/NationUpdateProcessor.java @@ -590,7 +590,7 @@ public void accept(MessageChannel channel, GuildDB guildDB) { continue; } - NationMeta.BeigeAlertMode mode = attacker.getBeigeAlertMode(NationMeta.BeigeAlertMode.NONES); + NationMeta.BeigeAlertMode mode = attacker.getBeigeAlertMode(NationMeta.BeigeAlertMode.NO_ALERTS); if (mode == NationMeta.BeigeAlertMode.NO_ALERTS) continue; if (mode == null) mode = NationMeta.BeigeAlertMode.NONES; diff --git a/src/main/java/link/locutus/discord/web/commands/binding/JavalinBindings.java b/src/main/java/link/locutus/discord/web/commands/binding/JavalinBindings.java index 29f36498..cfd6e305 100644 --- a/src/main/java/link/locutus/discord/web/commands/binding/JavalinBindings.java +++ b/src/main/java/link/locutus/discord/web/commands/binding/JavalinBindings.java @@ -9,6 +9,7 @@ public class JavalinBindings extends BindingHelper { @Binding + public Context context() { throw new IllegalStateException("No context set in command locals"); } diff --git a/src/main/java/link/locutus/discord/web/commands/options/WebOptionBindings.java b/src/main/java/link/locutus/discord/web/commands/options/WebOptionBindings.java new file mode 100644 index 00000000..cd1241fe --- /dev/null +++ b/src/main/java/link/locutus/discord/web/commands/options/WebOptionBindings.java @@ -0,0 +1,263 @@ +package link.locutus.discord.web.commands.options; + +import com.google.gson.JsonArray; +import link.locutus.discord.Locutus; +import link.locutus.discord.commands.manager.v2.binding.BindingHelper; +import link.locutus.discord.commands.manager.v2.binding.Parser; +import link.locutus.discord.commands.manager.v2.binding.ValueStore; +import link.locutus.discord.commands.manager.v2.binding.annotation.Binding; +import link.locutus.discord.commands.manager.v2.binding.annotation.HtmlOptions; +import link.locutus.discord.commands.manager.v2.command.CommandCallable; +import link.locutus.discord.commands.manager.v2.command.ParametricCallable; +import link.locutus.discord.commands.manager.v2.command.WebOption; +import link.locutus.discord.commands.manager.v2.impl.pw.refs.CM; +import link.locutus.discord.db.GuildDB; +import link.locutus.discord.db.entities.DBNation; +import link.locutus.discord.db.entities.TaxBracket; +import link.locutus.discord.util.scheduler.TriFunction; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.entities.channel.attribute.ICategorizableChannel; +import net.dv8tion.jda.api.entities.channel.concrete.Category; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel; +import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; + +import java.awt.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class WebOptionBindings extends BindingHelper { +//Parser + @Binding(types = Parser.class) + public WebOption getParser() { + List options = new ArrayList<>(); + ValueStore store = Locutus.cmd().getV2().getStore(); + for (Parser parser : store.getParsers().values()) { + if (!parser.isConsumer(store)) continue; + options.add(parser.getKey().toSimpleString()); + } + + return new WebOption(Parser.class).setOptions(options); + } +//Font + @Binding(types = Font.class) + public WebOption getFont() { + String[] fonts = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames(); + return new WebOption(Font.class).setOptions(fonts); + } +//Guild + @Binding(types = Guild.class) + public WebOption getGuild() { + return new WebOption(Guild.class).setQueryMap((guild, user, nation) -> { + List> data = new ArrayList<>(); + for (Guild g : Locutus.imp().getDiscordApi().getGuilds()) { + data.add(Map.of( + "icon", g.getIconUrl(), + "key", g.getId(), + "text", g.getName(), + "subtext", g.getDescription() + )); + } + return data; + }); + } +//Category + @Binding(types = Category.class) + public WebOption getCategory() { + return new WebOption(Category.class).setRequiresGuild().setQueryMap((db, user, nation) -> { + List> data = new ArrayList<>(); + for (Category c : db.getGuild().getCategories()) { + data.add(Map.of( + "key", c.getId(), + "text", c.getName() + )); + } + return data; + }); + } +//Role + @Binding(types = Role.class) + public WebOption getRole() { + return new WebOption(Role.class).setRequiresGuild().setQueryMap((db, user, nation) -> { + List> data = new ArrayList<>(); + for (Role r : db.getGuild().getRoles()) { + data.add(Map.of( + "key", r.getId(), + "text", r.getName(), + "color", String.format("#%06X", (0xFFFFFF & r.getColorRaw())) + )); + } + return data; + }); + } +//TextChannel + @Binding(types = TextChannel.class) + public WebOption getTextChannel() { + return new WebOption(TextChannel.class).setRequiresGuild().setQueryMap((db, user, nation) -> { + List> data = new ArrayList<>(); + Member member = null; + if (user != null) { + member = db.getGuild().getMember(user); + } + if (member == null) member = db.getGuild().getSelfMember(); + for (TextChannel c : db.getGuild().getTextChannels()) { + if (!c.canTalk(member)) continue; + Category category = c.getParentCategory(); + if (category == null) { + data.add(Map.of( + "key", c.getId(), + "text", c.getName() + )); + } else { + data.add(Map.of( + "key", c.getId(), + "text", c.getName(), + "subtext", category.getName() + )); + + } + } + return data; + }); + } +//ICategorizableChannel + @Binding(types = ICategorizableChannel.class) + public WebOption getICategorizableChannel() { + return getTextChannel(); + } +//Member + @Binding(types = Member.class) + public WebOption getMember() { + return new WebOption(Member.class).setRequiresGuild().setQueryMap((db, user, nation) -> { + List> data = new ArrayList<>(); + for (Member m : db.getGuild().getMembers()) { + data.add(Map.of( + "key", m.getId(), + "text", m.getEffectiveName() + )); + } + return data; + }); + } +//CommandCallable + @Binding(types = CommandCallable.class) + public WebOption getCommandCallable() { + List options = new ArrayList<>(Locutus.imp().getCommandManager().getV2().getCommands().getParametricCallables(f -> true)); + return new WebOption(CommandCallable.class).setOptions((List) options.stream().map(f -> f.getFullPath())); + } +//MessageChannel - just return textChannel() + @Binding(types = MessageChannel.class) + public WebOption getMessageChannel() { + return getTextChannel(); + } +//User + @Binding(types = User.class) + public WebOption getUser() { + return new WebOption(User.class).setQueryMap((guild, user, nation) -> { + List> data = new ArrayList<>(); + for (User u : Locutus.imp().getDiscordApi().getUsers()) { + data.add(Map.of( + "key", u.getId(), + "text", u.getName() + )); + } + return data; + }); + } +//TaxBracket + @Binding(types = TaxBracket.class) + public WebOption getTaxBracket() { + return new WebOption(TaxBracket.class).setRequiresGuild().setQueryMap((db, user, nation) -> { + if (!db.isValidAlliance()) throw new IllegalArgumentException("No alliance is registered. See " + CM.settings_default.registerAlliance.cmd.toSlashMention()); + Map brackets = db.getAllianceList().getTaxBrackets(true); + List> data = new ArrayList<>(); + for (Map.Entry entry : brackets.entrySet()) { + TaxBracket bracket = entry.getValue(); + data.add(Map.of( + "key", String.valueOf(entry.getKey()), + "text", bracket.getName(), + "subtext", bracket.getSubText() + )); + } + return data; + }); + } +//Project - return list Projects.values -> name() +//Building - return Buildings.values -> name() + +//DBLoan - locutus -> loan manager -> getloans +//Report - locutus - report manager -> get reports + + +//NationOrAllianceOrGuild -> same as guild() +//Conflict - locutus -> conflict manager -> conflicts + + +//DBCity +//TaxRate +//NationFilter +//CityRanges +//MMRInt +//MMRDouble +//MMRMatcher +//ParametricCallable +//ICommand +//NationAttribute +//NationPlaceholder +//AGrantTemplate +//GuildOrAlliance +//Newsletter +//DBAlliancePosition +//GuildSetting +//EmbeddingSource +//GPTProvider +//SpreadSheet +//GoogleDoc +//SheetTemplate +//CustomSheet +//TransferSheet +//SelectionAlias +//Class +//DBBounty +//DBTrade +//UserWrapper +//Transaction2 +//TaxDeposit + + //unused +//UUID +//Color +//Message +//DBNation +//DBAlliance +//Treaty +//CityBuild +//DBBan +//DBTreasure +//IAttack +//DBWar +//NationOrAlliance + +//defer + +//NationList -> Set +//DepositTypeInfo -> DepositType +//NationAttributeDouble -> defer to Set +//NationOrAllianceOrGuildOrTaxid -> guild + taxbracket +//GuildDB - return same as guild() + + +// +// compound +//Map[AllianceDepositLimit] +//Map[NationDepositLimit] +//Newsletter[ReportPerms] +//String[GuildCoalition] +//ParametricCallable[NationAttributeCallable] +//Class[PlaceholderType] +//Set[WikiCategory] +}