diff --git a/src/main/java/com/deeme/modules/genericgate/Config.java b/src/main/java/com/deeme/modules/genericgate/Config.java new file mode 100644 index 00000000..83dd9572 --- /dev/null +++ b/src/main/java/com/deeme/modules/genericgate/Config.java @@ -0,0 +1,40 @@ +package com.deeme.modules.genericgate; + +import eu.darkbot.api.config.annotations.Configuration; +import eu.darkbot.api.config.annotations.Option; +import eu.darkbot.api.config.annotations.Number; + +@Configuration("generic_gate") +public class Config { + @Option("travel_map") + public TravelMap travelMap = new TravelMap(); + + @Option("generic_gate.min_radius") + @Number(min = 300, max = 1000, step = 10) + public int radioMin = 560; + + @Option("generic_gate.away_distance") + @Number(min = 600, max = 6000, step = 10) + public int awayDistance = 600; + + @Option("generic_gate.repair") + public boolean repairLogic = false; + + @Option("generic_gate.repair_config") + public boolean useRepairConfigWhenNeedRepair = false; + + @Option("generic_gate.attack_closest") + public boolean alwaysTheClosestNPC = false; + + @Option("generic_gate.travel_to_next") + public boolean travelToNextMap = false; + + @Option("generic_gate.enable_collector") + public boolean collectorActive = false; + + @Option("generic_gate.roaming") + public boolean roaming = false; + + @Option("generic_gate.attack_all_npcs") + public boolean attackAllNpcs = false; +} diff --git a/src/main/java/com/deeme/modules/genericgate/ExtraNpcFlagsEnum.java b/src/main/java/com/deeme/modules/genericgate/ExtraNpcFlagsEnum.java new file mode 100644 index 00000000..8ed96196 --- /dev/null +++ b/src/main/java/com/deeme/modules/genericgate/ExtraNpcFlagsEnum.java @@ -0,0 +1,33 @@ +package com.deeme.modules.genericgate; + +import com.github.manolo8.darkbot.config.NpcExtraFlag; + +public enum ExtraNpcFlagsEnum implements NpcExtraFlag { + MOVE_AWAY("AWAY", "Move Away", "Generic Gate - Will try to dodge this NPC"); + + private final String shortName; + private final String name; + private final String description; + + ExtraNpcFlagsEnum(String shortName, String name, String description) { + this.shortName = shortName; + this.name = name; + this.description = description; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getShortName() { + return shortName; + } + +} \ No newline at end of file diff --git a/src/main/java/com/deeme/modules/genericgate/GenericGateDummy.java b/src/main/java/com/deeme/modules/genericgate/GenericGateDummy.java index 64908135..ef93d2af 100644 --- a/src/main/java/com/deeme/modules/genericgate/GenericGateDummy.java +++ b/src/main/java/com/deeme/modules/genericgate/GenericGateDummy.java @@ -1,28 +1,384 @@ package com.deeme.modules.genericgate; -import eu.darkbot.api.managers.AuthAPI; -import eu.darkbot.api.managers.ExtensionsAPI; - import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; import com.deeme.types.VerifierChecker; import com.deeme.types.backpage.Utils; -import com.deemeplus.modules.genericgate.GenericGate; +import com.github.manolo8.darkbot.config.Config.ShipConfig; +import com.github.manolo8.darkbot.config.NpcExtraFlag; +import com.github.manolo8.darkbot.core.itf.NpcExtraProvider; -import eu.darkbot.api.PluginAPI; +import eu.darkbot.api.config.types.PercentRange; +import eu.darkbot.api.extensions.Configurable; import eu.darkbot.api.extensions.Feature; +import eu.darkbot.api.extensions.FeatureInfo; +import eu.darkbot.api.PluginAPI; +import eu.darkbot.api.config.ConfigSetting; +import eu.darkbot.api.config.types.NpcFlag; +import eu.darkbot.api.game.entities.Npc; +import eu.darkbot.api.game.entities.Portal; +import eu.darkbot.api.game.enums.EntityEffect; +import eu.darkbot.api.game.other.GameMap; +import eu.darkbot.api.game.other.Locatable; +import eu.darkbot.api.game.other.Location; +import eu.darkbot.api.game.other.Lockable; +import eu.darkbot.api.game.other.Movable; +import eu.darkbot.api.managers.AttackAPI; +import eu.darkbot.api.managers.AuthAPI; +import eu.darkbot.api.managers.ConfigAPI; +import eu.darkbot.api.managers.EntitiesAPI; +import eu.darkbot.api.managers.ExtensionsAPI; +import eu.darkbot.api.managers.HeroAPI; +import eu.darkbot.shared.modules.CollectorModule; +import eu.darkbot.shared.modules.MapModule; + +@Feature(name = "Generic Gate", description = "For any map, event") +public class GenericGateDummy extends CollectorModule implements Configurable, NpcExtraProvider { + private final HeroAPI heroapi; + private final AttackAPI attacker; + + private Collection npcs; + + private ConfigSetting maxCircleIterations; + private final ConfigSetting repairHpRange; + private final ConfigSetting repairConfig; + + private boolean repair = false; + private boolean backwards = false; + private long nextWaveCheck = 0; + + private Config gateConfig; + + private State currentStatus = State.WAIT; + + private enum State { + WAIT("Waiting"), + ATTACKING("Attacking"), + WAITING_WAVE("Waiting for the wave"); + + private final String message; + + State(String message) { + this.message = message; + } + } -@Feature(name = "Generic Gate [PLUS]", description = "For any map, event") -public class GenericGateDummy extends GenericGate { public GenericGateDummy(PluginAPI api) { super(api); - AuthAPI auth = api.requireAPI(AuthAPI.class); if (!Arrays.equals(VerifierChecker.class.getSigners(), getClass().getSigners())) { throw new SecurityException(); } + + this.heroapi = api.requireAPI(HeroAPI.class); + this.attacker = api.requireAPI(AttackAPI.class); + + EntitiesAPI entities = api.requireAPI(EntitiesAPI.class); + this.npcs = entities.getNpcs(); + + ConfigAPI configApi = api.requireAPI(ConfigAPI.class); + + this.maxCircleIterations = configApi.requireConfig("loot.max_circle_iterations"); + this.repairHpRange = configApi.requireConfig("general.safety.repair_hp_range"); + this.repairConfig = configApi.requireConfig("general.safety.repair"); + this.currentStatus = State.WAIT; + + setup(); + } + + @Override + public NpcExtraFlag[] values() { + return ExtraNpcFlagsEnum.values(); + } + + @Override + public void setConfig(ConfigSetting arg0) { + this.gateConfig = arg0.getValue(); + + setup(); + } + + @Override + public String getStatus() { + return "Generic Gate | " + currentStatus.message + " | " + npcs.size() + " | " + attacker.getStatus(); + } + + @Override + public boolean canRefresh() { + return !attacker.isAttacking() && (!gateConfig.collectorActive || super.canRefresh()); + } + + @Override + public void onTickModule() { + pet.setEnabled(true); + repair = needsRepairing(); + + if (gateConfig.collectorActive && !super.canRefresh()) { + return; + } + + if (!isTheCorrectMap()) { + goToTheWorkingMap(); + return; + } + + if (findTarget()) { + nextWaveCheck = System.currentTimeMillis() + 30000; + this.currentStatus = State.ATTACKING; + attacker.tryLockAndAttack(); + npcMove(); + } else if (npcs.isEmpty() && nextWaveCheck < System.currentTimeMillis()) { + heroapi.setRoamMode(); + if (isCollecting()) { + return; + } + + if (gateConfig.roaming && (!movement.isMoving() || !canMove(movement.getDestination()))) { + movement.moveRandom(); + } + + jumpToTheBestPortal(); + } + } + + private void setup() { + if (this.api == null || this.gateConfig == null) { + return; + } + + AuthAPI auth = this.api.requireAPI(AuthAPI.class); VerifierChecker.requireAuthenticity(auth); + ExtensionsAPI extensionsAPI = api.requireAPI(ExtensionsAPI.class); - Utils.discordDonorCheck(extensionsAPI.getFeatureInfo(this.getClass()), auth.getAuthId()); + FeatureInfo feature = extensionsAPI.getFeatureInfo(this.getClass()); + Utils.discordCheck(feature, auth.getAuthId()); + Utils.showDonateDialog(feature, auth.getAuthId()); + } + + private boolean isTheCorrectMap() { + if (!gateConfig.travelMap.active) { + return true; + } + + return portals.isEmpty() || heroapi.getMap() != null && heroapi.getMap().getId() == gateConfig.travelMap.map; + } + + private void goToTheWorkingMap() { + try { + GameMap map = starSystem.getOrCreateMap(gateConfig.travelMap.map); + if (map != null && !portals.isEmpty() && map != starSystem.getCurrentMap()) { + this.bot.setModule(api.requireInstance(MapModule.class)).setTarget(map); + } + } catch (Exception e) { + System.out.println("Map not found" + e.getMessage()); + } + } + + private void jumpToTheBestPortal() { + if (!gateConfig.travelToNextMap || portals.isEmpty()) { + return; + } + + Portal portal = portals.stream().filter(p -> p.getTypeId() != 1).findFirst().orElse(null); + + if (portal == null) { + portal = portals.stream().filter(p -> p.getTypeId() == 1).findFirst().orElse(null); + } + + if (portal != null) { + if (heroapi.distanceTo(portal) < 200) { + movement.jumpPortal(portal); + } else { + movement.moveTo(portal); + } + } + } + + private boolean isCollecting() { + if (gateConfig.collectorActive) { + super.findBox(); + if (super.currentBox != null) { + super.tryCollectNearestBox(); + return true; + } + } + return false; + } + + private boolean needsRepairing() { + return heroapi.getHealth().hpPercent() < repairHpRange.getValue().getMin(); + } + + private boolean findTarget() { + if (!npcs.isEmpty()) { + if (!gateConfig.alwaysTheClosestNPC && !allLowLifeOrISH(true)) { + Npc target = bestNpc(); + if (target != null) { + attacker.setTarget(target); + } + } else { + Npc target = closestNpc(); + if (target != null) { + attacker.setTarget(target); + } + } + } else { + resetTarget(); + } + + return attacker.getTarget() != null; + } + + private void resetTarget() { + attacker.setTarget(null); + } + + private boolean allLowLifeOrISH(boolean countISH) { + int npcsLowLife = 0; + + for (Npc n : npcs) { + if (countISH && (n.hasEffect(EntityEffect.ISH) || n.hasEffect(EntityEffect.NPC_ISH))) { + return true; + } + if (isLowHealh(n)) { + npcsLowLife++; + } + } + + return npcsLowLife >= npcs.size(); + } + + private boolean isLowHealh(Npc n) { + return n.getHealth().hpPercent() < 0.25; + } + + private boolean shouldKill(Npc n) { + return (gateConfig.attackAllNpcs || n.getInfo().getShouldKill()) && !n.isBlacklisted() + && !n.hasEffect(EntityEffect.ISH) && + !n.hasEffect(EntityEffect.NPC_ISH); + } + + private Npc bestNpc() { + return this.npcs.stream() + .filter(n -> shouldKill(n) && n.getHealth().hpPercent() > 0.25) + .min(Comparator.comparingDouble(n -> (n.getInfo().getPriority())) + .thenComparing(n -> (n.getLocationInfo().getCurrent().distanceTo(heroapi)))) + .orElse(null); + } + + private Npc closestNpc() { + return this.npcs.stream() + .filter(this::shouldKill) + .min(Comparator.comparingDouble(n -> n.getLocationInfo().getCurrent().distanceTo(heroapi)) + .thenComparing(n -> n.getInfo().getPriority()) + .thenComparing(n -> n.getHealth().hpPercent())) + .orElse(null); + } + + private double getRadius(Lockable target) { + if (repair && gateConfig.repairLogic) { + return 1500; + } + + if (!(target instanceof Npc)) { + return gateConfig.radioMin; + } + + double npcRadius = ((Npc) target).getInfo().getRadius(); + + if (npcRadius < gateConfig.radioMin) { + npcRadius = gateConfig.radioMin; + } + + return attacker.modifyRadius(npcRadius); + } + + private void npcMove() { + if (!attacker.hasTarget()) { + return; + } + Lockable target = attacker.getTarget(); + + Location direction = movement.getDestination(); + Location targetLoc = target.getLocationInfo().destinationInTime(400); + + double distance = heroapi.distanceTo(attacker.getTarget()); + double angle = targetLoc.angleTo(heroapi); + double radius = getRadius(target); + double speed = target instanceof Movable ? ((Movable) target).getSpeed() : 0; + boolean noCircle = attacker.hasExtraFlag(NpcFlag.NO_CIRCLE); + + if (repair && gateConfig.useRepairConfigWhenNeedRepair) { + heroapi.setMode(this.repairConfig.getValue()); + } else { + heroapi.setAttackMode((Npc) target); + } + + double angleDiff; + if (noCircle) { + double dist = targetLoc.distanceTo(direction); + double minRad = Math.max(0, Math.min(radius - 200, radius * 0.5)); + if (dist <= radius && dist >= minRad) { + return; + } + distance = minRad + Math.random() * (radius - minRad - 10); + angleDiff = (Math.random() * 0.1) - 0.05; + } else { + double maxRadFix = radius / 2; + double radiusFix = (int) Math.max(Math.min(radius - distance, maxRadFix), -maxRadFix); + distance = (radius += radiusFix); + angleDiff = Math.max((heroapi.getSpeed() * 0.625) + (Math.max(200, speed) * 0.625) + - heroapi.distanceTo(Location.of(targetLoc, angle, radius)), 0) / radius; + } + direction = getBestDir(targetLoc, angle, angleDiff, distance); + + while (!canMove(direction) && distance < 10000) { + direction.toAngle(targetLoc, angle += backwards ? -0.3 : 0.3, distance += 2); + } + + if (distance >= 10000) { + direction.toAngle(targetLoc, angle, 500); + } + + movement.moveTo(direction); + } + + private boolean canMove(Location direction) { + Npc npc = this.npcs.stream() + .filter(n -> n.getInfo().hasExtraFlag(ExtraNpcFlagsEnum.MOVE_AWAY)) + .min(Comparator.comparingDouble(n -> n.getLocationInfo().getCurrent().distanceTo(direction))) + .orElse(null); + + if (npc != null && npc.getLocationInfo().getCurrent().distanceTo(direction) < gateConfig.awayDistance) { + return false; + } + + return movement.canMove(direction); + } + + private Location getBestDir(Locatable targetLoc, double angle, double angleDiff, double distance) { + int maxCircleIterationsValue = this.maxCircleIterations.getValue(); + int iteration = 1; + double forwardScore = 0; + double backScore = 0; + do { + forwardScore += score(Locatable.of(targetLoc, angle + (angleDiff * iteration), distance)); + backScore += score(Locatable.of(targetLoc, angle - (angleDiff * iteration), distance)); + if (forwardScore < 0 != backScore < 0 || Math.abs(forwardScore - backScore) > 300) + break; + } while (iteration++ < maxCircleIterationsValue); + + if (iteration <= maxCircleIterationsValue) + backwards = backScore > forwardScore; + return Location.of(targetLoc, angle + angleDiff * (backwards ? -1 : 1), distance); + } + + private double score(Locatable loc) { + return (movement.canMove(loc) ? 0 : -1000) - npcs.stream() + .filter(n -> attacker.getTarget() != n) + .mapToDouble(n -> Math.max(0, n.getInfo().getRadius() - n.distanceTo(loc))) + .sum(); } } diff --git a/src/main/java/com/deeme/modules/genericgate/TravelMap.java b/src/main/java/com/deeme/modules/genericgate/TravelMap.java new file mode 100644 index 00000000..7bd8fd1a --- /dev/null +++ b/src/main/java/com/deeme/modules/genericgate/TravelMap.java @@ -0,0 +1,41 @@ +package com.deeme.modules.genericgate; + +import eu.darkbot.api.config.annotations.Configuration; +import eu.darkbot.api.config.annotations.Dropdown; +import eu.darkbot.api.config.annotations.Option; +import java.util.List; +import java.util.stream.Collectors; + +import com.github.manolo8.darkbot.core.manager.StarManager; + +@Configuration("travel_map") +public class TravelMap { + @Option("travel_map.enabled") + public boolean active = false; + + @Option("travel_map.map") + @Dropdown(options = MapsDropdown.class) + public int map = 8; + + public static class MapsDropdown implements Dropdown.Options { + private final StarManager star; + private final List allMaps; + + public MapsDropdown(StarManager star) { + this.star = star; + this.allMaps = star.getMaps().stream().map(m -> m.id).collect(Collectors.toList()); + } + + @Override + public List options() { + return allMaps; + } + + @Override + public String getText(Integer option) { + if (option == null) + return ""; + return star.byId(option).getName(); + } + } +} diff --git a/src/main/resources/plugin.json b/src/main/resources/plugin.json index c0564112..d3c301e2 100644 --- a/src/main/resources/plugin.json +++ b/src/main/resources/plugin.json @@ -1,7 +1,7 @@ { "name": "DmPlugin", "author": "Dm94Dani", - "version": "2.3.6", + "version": "2.3.7", "minVersion": "1.131", "supportedVersion": "1.131.2", "basePackage": "com.deeme", @@ -35,6 +35,6 @@ ], "donation": "https://ko-fi.com/deeme", "discord": "https://discord.gg/GPRTRRZJPw", - "download": "https://github.com/darkbot-reloaded/DmPlugin/releases/download/v2.3.6/DmPlugin.jar", + "download": "https://github.com/darkbot-reloaded/DmPlugin/releases/download/v2.3.7/DmPlugin.jar", "update": "https://raw.githubusercontent.com/darkbot-reloaded/DmPlugin/master/src/main/resources/plugin.json" }