Jump to content

[Open Source] Uncooked Pie Maker


Freidrich

Recommended Posts

Hi guys! Thought I'd share my first script with you. Its a simple uncooked pie maker script. Turns out the pie market isn't that lucrative, but hey, scripting was fun nonetheless. To change pie type just change the pieType field. I'd like to thank Zynava, ashgg83 and saturn for supplying some useful code examples to get me started.

Anyways, here's the script. Let me know if you have any issues or feedback!

import com.epicbot.api.shared.APIContext;
import com.epicbot.api.shared.GameType;
import com.epicbot.api.shared.entity.GameEntity;
import com.epicbot.api.shared.entity.ItemWidget;
import com.epicbot.api.shared.entity.NPC;
import com.epicbot.api.shared.entity.SceneObject;
import com.epicbot.api.shared.methods.*;
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.*;
import java.awt.event.KeyEvent;
import java.util.List;


@ScriptManifest(name = "Uncooked Pie Maker", gameType = GameType.OS)
public class UncookedPieMaker extends LoopScript
{
    public enum PieType { BERRY, MEAT, APPLE, BOTANICAL, MUSHROOM, DRAGONFRUIT }
    public final String[] ingredientNames = {"Redberries",         "Cooked meat",       "Cooking apple",       "Golovanova fruit top",   "Sulliscep cap",         "Dragonfruit"};
    public final String[] uncookedPieNames = {"Uncooked berry pie", "Uncooked meat pie", "Uncooked apple pie",  "Uncooked botanical pie", "Uncooked mushroom pie", "Uncooked dragonfruit pie"};

    //MANDATORY! CHANGE THIS TO YOUR FAVORITE KIND OF PIE
    public final PieType pieType = PieType.APPLE;

    public final String pieShellName = "Pie shell";
    public final String ingredientName = ingredientNames[pieType.ordinal()];
    public final String uncookedPieName = uncookedPieNames[pieType.ordinal()];

    public final int inventorySize = 4 * 7;
    public final int gameTickPeriod = 600; // milliseconds
    public final int gameTicksPerPie = 2;

    private long startTime = -1;
    private long lastPieMakeTime = -1;
    private int  lastPiesToMakeInInventory = -1;
    private int piesInBank = -1;
    private int pieShellsInBank = -1;
    private int ingredientsInBank = -1;
    private int piesInPossessionAtStart = -1;

    private long hoverRollTime = -1;

    // Context utility methods
    public IClientAPI    client()    { return getAPIContext().client(); }
    public IBankAPI      bank()      { return getAPIContext().bank(); }
    public IInventoryAPI inventory() { return getAPIContext().inventory(); }
    public IScriptAPI    script()    { return getAPIContext().script(); }
    public IKeyboardAPI  keyboard()  { return getAPIContext().keyboard(); }
    public IWidgetsAPI   widgets()   { return getAPIContext().widgets(); }
    public IMouseAPI     mouse()     { return getAPIContext().mouse(); }

    /**********************************************************
     *********************BANKING METHODS**********************
     **********************************************************/
    // Do I need to bank? (Checks for necessary supplies in inventory)
    public boolean needToBank() {
        return !inventory().contains(pieShellName) || !inventory().contains(ingredientName);
    }

    // Should I deposit?
    public boolean anythingToDeposit() {
        IInventoryAPI inv = inventory();
        return inv.getEmptySlotCount() + inv.getCount(pieShellName, ingredientName) < inventorySize
                || inv.getCount(pieShellName) > inventorySize / 2
                || inv.getCount(ingredientName) > inventorySize / 2;
    }

    // Can withdraw more pies to make?
    public boolean shouldWithdraw() {
        int makeablePiesInInventory = Math.min(inventory().getCount(pieShellName), inventory().getCount(ingredientName));
        int makeablePiesTotal = getTotalPiesLeft();
        return makeablePiesInInventory == 0 && makeablePiesTotal > 0;
    }

    // Withdraw items
    public void withdrawItems() {
        int shellsToWithdraw = inventorySize / 2 - inventory().getCount(pieShellName);
        if (shellsToWithdraw > 0) bank().withdraw(shellsToWithdraw, pieShellName);

        int ingredientsToWithdraw = inventorySize / 2 - inventory().getCount(ingredientName);
        if (ingredientsToWithdraw > 0) bank().withdraw(ingredientsToWithdraw, ingredientName);
        updatePieCounts();
    }

    public boolean hasCountedPiesInBank() { return piesInPossessionAtStart >= 0 && pieShellsInBank >= 0 && ingredientsInBank >= 0; }

    // must be in bank
    public void updatePieCounts() {
        if (!bank().isOpen()) throw new IllegalStateException("Bank must be open to update pie counts.");

        piesInBank = bank().getCount(uncookedPieName);
        pieShellsInBank = bank().getCount(pieShellName);
        ingredientsInBank = bank().getCount(ingredientName);

        if (piesInPossessionAtStart < 0)
            piesInPossessionAtStart = inventory().getCount(uncookedPieName) + piesInBank;
    }

    // Relies on updated pie counts
    public int getTotalPiesLeft() { return Math.min(pieShellsInBank + inventory().getCount(pieShellName), ingredientsInBank + inventory().getCount(ingredientName)); }


    /**********************************************************
     **********************HOVERING METHODS********************
     **********************************************************/

    public static boolean hover(APIContext ctx, GameEntity ge){
        if(!isHovering(ctx, ge)){
            return ge.hover();
        }
        return true;
    }

    private static boolean isHovering(APIContext ctx, GameEntity ge){
        return ge.contains(ctx.mouse().getLocation());
    }

    // Get sample from exponential distribution
    public static double nextExponential(double rate) {
        return  -Math.log(1 - Random.nextDouble()) / rate;
    }

    public long rollHoverTime()
    {
        // Flip coin, either hover sometime later og straight away
        if (Random.nextGamble(3)) {
            // Roll for random time before bot hovers bank, expected time is 8000 milliseconds from now
            hoverRollTime = System.currentTimeMillis() + (long)nextExponential(1d / 8000d);
        } else {
            hoverRollTime = System.currentTimeMillis() + (long)Random.nextInt(500, 1500);
        }
        System.out.println(hoverRollTime - System.currentTimeMillis());
        return hoverRollTime;
    }

    public void hoverBank()
    {
        APIContext ctx = getAPIContext();

        SceneObject booth = ctx.objects().query().nameContains("booth").actions("Bank").results().nearest();
        if(booth != null){
            boolean hovering = hover(ctx, booth);
            return;
        }

        NPC banker = ctx.npcs().query().actions("Bank").results().nearest();
        if(banker != null){
            boolean hovering = hover(ctx, banker);
            return;
        }

        SceneObject bankable = ctx.objects().query().actions("Bank").results().nearest();
        if(bankable != null){
            boolean hovering = hover(ctx, bankable);
            return;
        }
    }
    
    /**********************************************************
     ****************INVENTORY MANAGEMENT METHODS**************
     **********************************************************/
    public double distanceSquared(Point p, Point q)
    {
        double dX = p.getX() - q.getX();
        double dY = p.getY() - q.getY();
        return dX * dX + dY * dY;
    }

    public ItemWidget getClosestItem(String... names)
    {
        Point mouseLoc = mouse().getLocation();
        List<ItemWidget> items = inventory().getItems(names);

        if (items == null || items.isEmpty()) return null;
        double minDistance = Double.MAX_VALUE;
        ItemWidget closestItem = null;

        for (ItemWidget item: items) {
            double dist = distanceSquared(mouseLoc, item.getLocation());
            if (dist < minDistance)
            {
                minDistance = dist;
                closestItem = item;
            }
        }
        return closestItem;
    }

    /**********************************************************
     *********************PIE MAKING METHODS*******************
     **********************************************************/
    // Should I make pies?
    public boolean shouldMakePies() {
        return inventory().contains(pieShellName) && inventory().contains(ingredientName);
    }


    public int getPiesToMakeInInventory() {
        return Math.min(inventory().getCount(pieShellName), inventory().getCount(ingredientName));
    }

    public boolean isMakingPies() {
        //shortcuts
        if (lastPieMakeTime <= 0 || lastPiesToMakeInInventory <= 0 || bank().isOpen() || mustConfirmPieMaking()) return false; // haven't started making

        long t = System.currentTimeMillis();
        int currentPiesToMakeInInventory  = getPiesToMakeInInventory();

        if (currentPiesToMakeInInventory < lastPiesToMakeInInventory)
        {
            lastPieMakeTime = t;
            lastPiesToMakeInInventory = currentPiesToMakeInInventory;
            return currentPiesToMakeInInventory != 0; // is making if not out of ingredients
        }
        else return t - lastPieMakeTime <= gameTickPeriod * gameTicksPerPie * 4; // generous threshold as this is rarely the deciding condition unless lag
    }

    public boolean mustConfirmPieMaking() { return !widgets().query().textContains("How many do you wish to make?").results().isEmpty(); }

    public void confirmPieMaking() {
        lastPiesToMakeInInventory = getPiesToMakeInInventory();
        Time.sleep(0,300);
        keyboard().holdKey(KeyEvent.VK_SPACE, Random.nextInt(0, gameTickPeriod) + gameTickPeriod * 2, true);
        lastPieMakeTime = System.currentTimeMillis() + gameTickPeriod;
        Time.sleep(800,1000);
        rollHoverTime();
        return;
    }



    public void makePies() {
        if (isMakingPies() || !shouldMakePies()) return;

        if (mustConfirmPieMaking()) {
            confirmPieMaking();
            return;
        }

        String itemToInteractWith;
        if (inventory().isItemSelected())
        {   // check if one of the ingredients is selected
            String selectedItem = inventory().getSelectedItemName().trim();
            if (selectedItem.compareToIgnoreCase(pieShellName) == 0) {
                itemToInteractWith = ingredientName;
            } else if (selectedItem.compareToIgnoreCase(ingredientName) == 0) {
                itemToInteractWith = pieShellName;
            } else {
                // Nope. Deselect!
                inventory().deselectItem();
                Time.sleep(500,600);
                return;
            }
            // Yep. Now combine with other component
            ItemWidget item = getClosestItem(itemToInteractWith);
            if(item != null) {
                item.interact();
                confirmPieMaking();
            }
        }
        else //select an ingredient
            inventory().selectItem(ingredientName);
    }


    @Override
    public boolean onStart(String... strings) {
        startTime = System.currentTimeMillis();
        System.out.print("Starting Uncooked Pie Maker...");
        return true;
    }

    @Override
    protected int loop() {
        if(!client().isLoggedIn()) {
            script().stop("Please wait until you are logged in.");
        }
        else if (!bank().isReachable())
        {
            System.out.println("Walking to bank.");
            getAPIContext().webWalking().walkToBank();
        }
        else if (!hasCountedPiesInBank()) // Make initial count for control flow and statistics
        {
            if (!bank().isOpen())
            {
                if (inventory().isItemSelected())
                    inventory().deselectItem();
                else bank().open();
            }
            else
            {
                updatePieCounts();
                System.out.println("Initial count completed.");
                System.out.println("Pies already in possession: " + piesInPossessionAtStart);
                System.out.println("Pies we can currently make: " + getTotalPiesLeft());
            }
        }
        else if (getTotalPiesLeft() <= 0) // no more pies to make
        {
            int piesAccumulated = piesInBank + inventory().getCount(uncookedPieName) - piesInPossessionAtStart;
            System.out.println("No more pies to make. Stopping script.");
            System.out.println("Total runtime: " + Time.getFormattedRuntime(this.startTime));
            System.out.println("Total pies accumulated: " + piesAccumulated);
            System.out.println("Pies cooked: 0");
            script().stop("Job's done.");
        }
        // Check supplies, do we need to open the bank?
        else if(needToBank() && !bank().isOpen()) {
            if (inventory().isItemSelected())
                inventory().deselectItem();
            bank().open();
        }

        // If bank is open:
        //      Deposits and/or Withdraws as needed
        //      Otherwise close bank
        else if(bank().isOpen()) {
            if(anythingToDeposit()) {
                bank().depositInventory();
                updatePieCounts();
            }
            else if (shouldWithdraw()) {
                withdrawItems();
            } else {
                bank().close();
                Time.sleep(300, 500);
            }
        }
        // Make pies if there are pies needing to be made
        else if (!bank().isOpen() && shouldMakePies() && !isMakingPies()) {
            makePies();
        }
        else if (isMakingPies())
        {
            // Check if it is time to hover bank
            if (0 < hoverRollTime && System.currentTimeMillis() > hoverRollTime) {
                hoverBank();
                hoverRollTime = -1;
            }
        }
        return 186 + Random.nextInt(0,29); // minor randomization
    }


    protected void onPaint(Graphics2D g, APIContext ctx) {
        int piesAccumulated = piesInBank + inventory().getCount(uncookedPieName) - piesInPossessionAtStart;
        int piesLeft = Math.min(pieShellsInBank + inventory().getCount(pieShellName), ingredientsInBank + inventory().getCount(ingredientName));
        PaintFrame pf = new PaintFrame();
        pf.setTitle("Uncooked Pie Maker");
        pf.addLine("Runtime: ", Time.getFormattedRuntime(this.startTime));
        pf.addLine("Pies accumulated: ", piesAccumulated);
        pf.addLine("Pies left: ", piesLeft);
        //pf.addLine("Pies cooked: ", 0);
        //pf.addLine("Progress: ", ((100 * piesLeft) / piesToMakeAtStart) + "%" ); // Doesn't work if user changes pie counts during execution
        //pf.addLine("Time until heat death of universe: ", "10³²⁴⁴⁷ years");
        pf.draw(g, 0.0D, 40.0D, ctx);
    }
}



Cheers

UncookedPieMaker$PieType.class UncookedPieMaker.class

  • Like 1
Link to comment
Share on other sites

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...