Jump to content
View in the app

A better way to browse. Learn more.

OSBot :: 2007 OSRS Botting

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

Select Entities through Paint

Featured Replies

Painter classes that allows a user to select any entities through the paint. 
Capture.jpg

Capture-pickpocket.jpg

 

1. Copy this class into your project, Adjust the package if needed.

package Paint;

import org.osbot.rs07.api.EntityAPI;
import org.osbot.rs07.api.filter.Filter;
import org.osbot.rs07.api.map.Position;
import org.osbot.rs07.api.model.Entity;
import org.osbot.rs07.canvas.paint.Painter;
import org.osbot.rs07.input.mouse.BotMouseListener;
import org.osbot.rs07.script.MethodProvider;
import org.osbot.rs07.script.Script;

import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.geom.Area;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.stream.Collectors;

public abstract class EntitySelectionPainter<T extends Entity> extends BotMouseListener implements Painter {
    private final EntityAPI<T> entityAPI;
    private final Color FINISH_SELECTION_BG = new Color(25, 240, 25, 156);
    private final Color ALPHA_GREEN = new Color(25, 240, 25, 20);
    private final Color ALPHA_RED = new Color(250, 25, 25, 20);
    private final Filter<T> validEntityFilter;
    private final Script script;
    private List<T> visibleEntities;
    private final HashSet<T> selectedEntities;

    private final boolean isSingleSelection;
    private int frameCounter = 0;
    private Rectangle finishSelectionRect;
    private boolean isSelectionComplete = false;
    private final static String CONFIRM_SELECTION = "Confirm Selections";

    protected EntitySelectionPainter(Script script, Filter<T> validEntityFilter, EntityAPI<T> entityAPI, boolean isSingleSelection) {
        this.entityAPI = entityAPI;
        this.script = script;
        this.validEntityFilter = validEntityFilter;
        this.selectedEntities = new HashSet<>();
        this.isSingleSelection = isSingleSelection;

        script.getBot().addPainter(this);
        script.getBot().addMouseListener(this);
    }

    @Override
    public void onPaint(Graphics2D g2d) {
        setFinishSelectionRectangle(g2d);
        frameCounter += 1;
        if (frameCounter % 100 == 0) {
            queryValidEntities();
            script.log("Found " + visibleEntities.size());
        }
        for (T entity : visibleEntities) {
            if(entity == null)
                continue;

            Area entityOutline = getEntityOutline(entity);
            if(entityOutline != null) {
                g2d.setColor(selectedEntities.contains(entity) ? Color.GREEN : Color.RED);
                g2d.draw(entityOutline);
                g2d.setColor(selectedEntities.contains(entity) ? ALPHA_GREEN : ALPHA_RED);
                g2d.fill(entityOutline);
                continue;
            }

            Shape positionOutline = getEntityPositionShape(entity);
            if(positionOutline != null) {
                g2d.setColor(selectedEntities.contains(entity) ? Color.GREEN : Color.RED);
                g2d.draw(positionOutline);
            }
        }
        drawFinishSelectionRectangle(g2d);
    }

    @Override
    public void checkMouseEvent(MouseEvent mouseEvent) {
        if (mouseEvent.getID() != MouseEvent.MOUSE_PRESSED || mouseEvent.getButton() != MouseEvent.BUTTON1) {
            return;
        }

        Point clickPt = mouseEvent.getPoint();
        if (finishSelectionRect != null && finishSelectionRect.contains(clickPt)) {
            isSelectionComplete = true;
            mouseEvent.consume();
            return;
        }
        for (T entity : visibleEntities) {
            Rectangle entityBoundingBox = getEntityBoundingBox(entity);
            // Draw the outline of the tile (position) for an NPC. If too many players interact with a NPC at any point
            // It will disappear. ex: splashing host @ Ardy knights.
            boolean cannotGetBB = entityBoundingBox == null;
            if (cannotGetBB)
                script.warn(String.format("Entity bounding box is null (%s @ %s). Attempting to use Position Shape.", entity.getName(), entity.getPosition()));

            if (!cannotGetBB && entityBoundingBox.contains(clickPt)) {
                mouseEvent.consume();
                userClickedEntityHelper(entity);
                continue;
            }

            Shape npcPositionOutline = getEntityPositionShape(entity);
            if(npcPositionOutline == null) {
                script.warn(String.format("Unable to get the Entity Position Shape for %s @ %s", entity.getName(), entity.getPosition()));
                continue;
            }

            if(npcPositionOutline.contains(clickPt)) {
                mouseEvent.consume();
                userClickedEntityHelper(entity);
            }
        }
    }

    public ArrayList<T> awaitSelectedEntities() throws InterruptedException {
        while (!isSelectionComplete) {
            MethodProvider.sleep(1000);
        }
        if (selectedEntities.isEmpty()) {
            script.warn("Nothing was selected!");
        }
        cleanup();
        return new ArrayList<>(selectedEntities);
    }

    private void userClickedEntityHelper(T entity) {
        if(isSingleSelection) {
            boolean isDeselect = selectedEntities.contains(entity);
            selectedEntities.clear();
            if(!isDeselect)
                selectedEntities.add(entity);
            return;
        }
        if (selectedEntities.contains(entity)) {
            script.log(String.format("Removing entity (id: %d)", entity.getId()));
            selectedEntities.remove(entity);
        } else {
            script.log(String.format("Added entity (id: %d)", entity.getId()));
            selectedEntities.add(entity);
        }
    }

    private void setFinishSelectionRectangle(Graphics2D g2d) {
        if(finishSelectionRect != null) {
            return;
        }
        FontMetrics metrics = g2d.getFontMetrics();

        int width = metrics.stringWidth(CONFIRM_SELECTION) + 30;
        int numLines = CONFIRM_SELECTION.split("\n").length;
        int height = metrics.getHeight() * numLines + 30;

        finishSelectionRect = new Rectangle(0, 0, width, height);
    }

    private void drawFinishSelectionRectangle(Graphics2D g2d) {
        g2d.setColor(FINISH_SELECTION_BG);
        g2d.fill(finishSelectionRect);

        FontMetrics metrics = g2d.getFontMetrics();
        int textX = finishSelectionRect.x + 15;
        int textY = finishSelectionRect.y + 15 + metrics.getAscent();

        g2d.setColor(Color.WHITE);
        g2d.drawString(CONFIRM_SELECTION, textX, textY);
    }

    private void queryValidEntities() {
        //noinspection unchecked
        visibleEntities = entityAPI.filter(validEntityFilter).stream().distinct().collect(Collectors.toList());
        if (visibleEntities.isEmpty()) {
            script.log("Found no NPCs with supplied filter");
        }
    }

    private Rectangle getEntityBoundingBox(T entity) {
        return entity.getModel().getBoundingBox(entity.getGridX(), entity.getGridY(), entity.getZ());
    }

    private Shape getEntityPositionShape(T entity) {
        Position position = entity.getPosition();
        if (position != null) {
            return position.getPolygon(script.getBot());
        }
        return null;
    }

    private Area getEntityOutline(T entity) {
        return entity.getModel().getArea(entity.getGridX(), entity.getGridY(), entity.getZ());
    }

    public void cleanup() {
        script.getBot().removePainter(this);
        script.getBot().removeMouseListener(this);
    }
}

2. Extend EntitySelectionPainter. 

ex: For RS2Objects (and its implementations GroundDecoration, InteractableObject, WallDecoration, WallObject)

package Paint;

import org.osbot.rs07.api.Objects;
import org.osbot.rs07.api.filter.Filter;
import org.osbot.rs07.api.model.RS2Object;
import org.osbot.rs07.script.Script;

public class Rs2ObjectSelectionPainter extends EntitySelectionPainter<RS2Object> {
    public Rs2ObjectSelectionPainter(Script script, Filter<RS2Object> validEntityFilter, Objects objects, boolean isSingleSelection) {
        super(script, validEntityFilter, objects, isSingleSelection);
    }
}

ex: For NPCs

package Paint;

import org.osbot.rs07.api.NPCS;
import org.osbot.rs07.api.filter.Filter;
import org.osbot.rs07.api.model.NPC;
import org.osbot.rs07.script.Script;

public class NPCSelectionPainter extends EntitySelectionPainter<NPC> {
    public NPCSelectionPainter(Script script, Filter<NPC> validEntityFilter, NPCS npcs, boolean isSingleSelection) {
        super(script, validEntityFilter, npcs, isSingleSelection);
    }
}

3. Use in your project. I put this in onStart().
You will need a filter to only highlight applicable Entities for your script. This below example is from a construction script, therefore only objects that have the "Build" action will be highlighted. 

The isSingleSelection boolean controls if the user can only select 1 entity at a time. ex: A construction script may only want to select 1 larder build space. 

Rs2ObjectSelectionPainter buildSpotSelectionPainter;
@Override
public void onStart() throws InterruptedException {
    super.onStart();
    buildSpotSelectionPainter = new Rs2ObjectSelectionPainter(this, object -> object.hasAction("Build"), objects, false);
    ArrayList<RS2Object> selectedObjects = buildSpotSelectionPainter.awaitSelectedEntities();
    log("# objects: " + selectedObjects.size());
    for (RS2Object e: selectedObjects) {
        log(e.getName() + " " + e.getPosition());
    }

}

Edited by yfoo

  • yfoo changed the title to Select Entities through Paint
  • Author

Don't forget to call cleanup() in onStop(), this function removes the painter as both a mouse listener and a painter from osbot.

@Override
public void onStop() throws InterruptedException {
    super.onStop();
    if(buildSpotSelectionPainter != null)
        buildSpotSelectionPainter.cleanup();

Instantiation of the painter object should be at the top of your onStart().

There can be a race condition where script.stop() is called before painter construction. script.stop() then triggers onStop before onStart creates that painter. The result is that the painter is never removed from the canvas. 

  • Author
4 hours ago, Czar said:

Amazing, great work. I did mine a different way but I appreciate how you queried the entities in onPaint, I was always wary of placing code in there since it's run on a different thread

That's a good idea, Thanks. I never really liked having to preform queries every 100 frames as it was a hack to prevent lag. Probably going to change it to use a periodic executor service in the constructor that does the query. 

  • 1 month later...

Awesome! 
I have to take a look at my shopper how I handled it, probably very wacky...

nice going :D 

Create an account or sign in to comment

Recently Browsing 0

  • No registered users viewing this page.

Account

Navigation

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.