Jump to content

Ape Atoll Agility


vsun

Recommended Posts

I've been interested in writing a bot for a while but doing 2000 laps of the Ape Atoll course is what finally broke me.

Here's the completed script I wrote for doing the course. It'll only work if you run it at the start or end tile of an obstacle. I tried to add code to get back on the course if the player went off, but I ran into a bunch of issues navigating to the start/end tiles and gave up. Please let me know about any Java best practices I missed and how I can improve/clean this up since it's my first script.

import com.epicbot.api.shared.APIContext;
import com.epicbot.api.shared.GameType;
import com.epicbot.api.shared.entity.ItemWidget;
import com.epicbot.api.shared.entity.SceneObject;
import com.epicbot.api.shared.methods.*;
import com.epicbot.api.shared.model.Tile;
import com.epicbot.api.shared.script.LoopScript;
import com.epicbot.api.shared.script.ScriptManifest;
import com.epicbot.api.shared.util.Random;
import com.epicbot.api.shared.util.paint.frame.PaintFrame;
import com.epicbot.api.shared.util.time.Time;

import java.awt.*;

@ScriptManifest(name = "ApeAtollAgility", gameType = GameType.OS)
public class ApeAtollAgility extends LoopScript {

    private long startTime;
    private int laps;
    private int originalExperience;

    private ICameraAPI camera() { return getAPIContext().camera(); }
    private IInventoryAPI inventory() { return getAPIContext().inventory(); }
    private ILocalPlayerAPI localPlayer() { return getAPIContext().localPlayer(); }
    private IMouseAPI mouse() { return getAPIContext().mouse(); }
    private IObjectsAPI objects() { return getAPIContext().objects(); }
    private IWebWalkingAPI webWalking() { return getAPIContext().webWalking(); }
    private IWalkingAPI walking() { return getAPIContext().walking(); }

    private final String[] COURSE = { "Stepping stone", "Tropical tree", "Monkeybars", "Skull slope", "Rope", "Tropical tree" };
    private final String[] ACTIONS = { "Jump-to", "Climb", "Swing Across", "Climb-up", "Swing", "Climb-down" };
    private final Tile[] STARTS = { new Tile(2755, 2742, 0),
                                    new Tile(2753, 2742, 0),
                                    new Tile(2752, 2742, 2),
                                    new Tile(2747, 2741, 0),
                                    new Tile(2751, 2731, 0),
                                    new Tile(2758, 2734, 0) };
    private final Tile[] ENDS = { new Tile(2753, 2742, 0),
                                  new Tile(2753, 2742, 2),
                                  new Tile(2747, 2741, 0),
                                  new Tile(2742, 2741, 0),
                                  new Tile(2756, 2731, 0),
                                  new Tile(2770, 2747, 0) };

    private int missed = 0;

    private boolean isIdle() {
        return !localPlayer().isAnimating() && !localPlayer().isMoving();
    }

    private boolean isOnTile(Tile t) {
        Tile currTile = localPlayer().getLocation();
        return currTile.equals(t);
    }

    private boolean needStaminaDose() {
        return walking().getRunEnergy() < Math.random() * 40 && !localPlayer().isStaminaActive();
    }

    private ItemWidget findStaminaDose() {
        String fmt = "Stamina potion(%d)";
        for (int i = 1; i <= 4; i++) {
            ItemWidget stam = inventory().getItem(String.format(fmt, i));
            if (stam != null) {
                return stam;
            }
        }

        return null;
    }

    private boolean shouldExit() {
        if (!getAPIContext().client().isLoggedIn()) {
            return false;
        }

        return findStaminaDose() == null && walking().getRunEnergy() < 20;
    }

    private void doHumanWait() {
        double p = Math.random();
        if (p < 0.01) {
            int secs = Random.nextInt(90, 150);
            getLogger().info(String.format("Long sleeping for %d seconds", secs));
            mouse().moveOffScreen();
            Time.sleep(secs * 1000);
        } else if (p < 0.04) {
            int secs = Random.nextInt(10, 20);
            getLogger().info(String.format("Sleeping for %d seconds", secs));
            mouse().moveOffScreen();
            Time.sleep(secs * 1000);
        }
    }

    private int findCurrentStep(Tile t) {
        for (int i = 0; i < STARTS.length; i++) {
            Tile s = STARTS[i];
            if (t.equals(s)) {
                return i;
            }
        }

        for (int i = 0; i < ENDS.length; i++) {
            Tile e = ENDS[i];
            if (t.equals(e)) {
                return (i + 1) % ENDS.length;
            }
        }

        return -1;
    }

    private boolean doCourseStep() throws Exception {
        Tile curr = localPlayer().getLocation();
        int i = findCurrentStep(curr);
        if (i == -1) {
            if (missed >= 10) {
                throw new Exception("Missed too many obstacles");
            }
            // usually only logs on the stepping stone since it "wakes" when on the stone
            getLogger().info("Did not find current step");
            missed++;
            return false;
        }

        missed = 0;

        String name = COURSE[i];
        String action = ACTIONS[i];
        Tile s = STARTS[i];
        Tile e = ENDS[i];

        getLogger().info(String.format("%s %s: %d", action, name, i));

        SceneObject obstacle = objects().query().named(name).results().nearest();
        if (obstacle == null) {  // if we don't see it, move to the start
            walking().walkOnMap(s);
            Time.sleep(4000, () -> isOnTile(s) && isIdle());
        } else {  // if we see it, do the action on it
            if (obstacle.interact(action)) {
                doHumanWait();
                Time.sleep(3000, () -> isOnTile(e) && isIdle());
            }
        }

        if (i == COURSE.length - 1) {
            laps++;
        }

//        Time.sleep(100);
        return isOnTile(e);
    }

    private String convertToHHMMSS(long ms) {
        int total = (int) (ms / 1000);

        int h = total / 3600;
        int m = (total % 3600) / 60;
        int s = total % 60;

        return String.format("%02d:%02d:%02d", h, m, s);
    }

    private int getExpGained() {
        return getAPIContext().skills().agility().getExperience() - originalExperience;
    }

    @Override
    protected int loop() {
        if (shouldExit()) {
            return -1;
        }

        if (!isIdle()) {
            Time.sleep(600, () -> isIdle());
        } else if (needStaminaDose()) {
            ItemWidget stam = findStaminaDose();
            if (stam != null) {
                stam.interact("Drink");
            }
        } else {
            try {
                doCourseStep();
            } catch (Exception e) {
                getLogger().error(e.toString());
                return -1;
            }
        }

        return 690;
    }

    @Override
    public boolean onStart(String... strings) {
        if (!getAPIContext().client().isLoggedIn()) {
            return false;
        }

        startTime = System.currentTimeMillis();
        laps = 0;
        originalExperience = getAPIContext().skills().agility().getExperience();
        getLogger().info("Starting script");

        ICameraAPI cam = camera();
        cam.setPitch(98, false);
        cam.setYawDeg(0, false);
        return true;
    }

    @Override
    protected void onPaint(Graphics2D g, APIContext ctx) {
        if (!getAPIContext().client().isLoggedIn()) {
            Time.sleep(15000, () -> getAPIContext().client().isLoggedIn());
        }

        long totalMillis = System.currentTimeMillis() - startTime;

        PaintFrame frame = new PaintFrame();
        frame.setTitle("Stats");
        frame.addLine("Time elapsed: ", convertToHHMMSS(totalMillis));
        frame.addLine("Total laps: ", laps);
        frame.addLine("Exp gained: ", getExpGained());
        frame.draw(g, 0, 90, ctx);
    }
}

 

  • Like 2
Link to comment
Share on other sites

  • 7 months later...

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...