Skip to content
This repository has been archived by the owner on Jun 3, 2024. It is now read-only.

Commit

Permalink
Merge pull request #7 from QuBerto/Qu-Features
Browse files Browse the repository at this point in the history
Qu features
  • Loading branch information
QuBerto authored May 3, 2024
2 parents 889af3a + 3103671 commit 0ac9450
Show file tree
Hide file tree
Showing 5 changed files with 264 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/model/osrs/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from .combat.combat import OSRSCombat
from .walkerExample import OSRSWalkingExample
from .woodcutter import OSRSWoodcutter
29 changes: 29 additions & 0 deletions src/model/osrs/walkerExample.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import utilities.api.locations as loc
from model.osrs.osrs_bot import OSRSBot
from utilities.walker import Walking


class OSRSWalkingExample(OSRSBot):
def __init__(self):
self.walk_to = "VARROCK_SQUARE"
super().__init__(bot_title="Walk", description="Walk almost anywhere")

def create_options(self):
locations = [name for name in vars(loc) if not name.startswith("__")]
self.options_builder.add_dropdown_option("walk_to", "Walk to?", locations)

def save_options(self, options: dict):
for option in options:
if option == "walk_to":
self.log_msg(f"walk_to: {options[option]}")
self.walk_to = options[option]
self.log_msg("Options set successfully.")
self.options_set = True

def main_loop(self):
while True:
walker = Walking(self)
if walker.walk_to(self.walk_to):
self.log_msg("Arrived at destination")
self.stop()
self.stop()
26 changes: 26 additions & 0 deletions src/utilities/api/locations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
LUMBRIDGE_CASTLE = (3221, 3218)
VARROCK_SQUARE = (3210, 3424)
FALADOR_PARK = (2998, 3374)
GRAND_EXCHANGE = (3164, 3487)
EDGEVILLE_BANK = (3093, 3493)
AL_KHARID_PALACE = (3304, 3175)
DRAYNOR_VILLAGE = (3092, 3248)
ARDOUGNE_MARKET = (2655, 3283)
CAMELOT_CASTLE = (2757, 3477)
SEERS_VILLAGE_BANK = (2725, 3491)
YANILLE_AGILITY_DUNGEON_ENTRANCE = (2586, 9436)
BARBARIAN_VILLAGE = (3080, 3424)
TREE_GNOME_STRONGHOLD = (2449, 3420)
PORT_SARIM = (3012, 3196)
CATHERBY_BANK = (2805, 3441)
SHILO_VILLAGE = (2827, 2996)
BRIMHAVEN = (2772, 3234)
FISHING_GUILD = (2597, 3418)
CASTLE_WARS = (2435, 3090)
TAVERLEY = (2888, 3443)
CAMELOT_HERB = (2811, 3462)
ARDOUGNE_HERB = (2673, 3375)
ECTO_HERB = (3604, 3529)
EXPLORERS_RING_HERB = (3057, 3311)
HOSIDIUS_HERB = (1740, 3550)
FARMING_GUILD_HERB = (1240, 3727)
52 changes: 52 additions & 0 deletions src/utilities/api/pathfinding_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import json

import requests

# Example usage
# start_position = (3163, 3474)
# end_position = (2998, 3374)

# pathfinder = Pathfinder()
# print(pathfinder.get_path(start_position, end_position))

API_URL = "https://explv-map.siisiqf.workers.dev/"

ERROR_MESSAGE_MAPPING = {
"UNMAPPED_REGION": "Unmapped region",
"BLOCKED": "Tile is blocked",
"EXCEEDED_SEARCH_LIMIT": "Exceeded search limit",
"UNREACHABLE": "Unreachable tile",
"NO_WEB_PATH": "No web path",
"INVALID_CREDENTIALS": "Invalid credentials",
"RATE_LIMIT_EXCEEDED": "Rate limit exceeded",
"NO_RESPONSE_FROM_SERVER": "No response from server",
"UNKNOWN": "Unknown",
}


class Pathfinder:
def __init__(self):
pass

@staticmethod
def get_path(start, end):
start_z = start[2] if len(start) > 2 else 0
end_z = end[2] if len(end) > 2 else 0
payload = {"start": {"x": start[0], "y": start[1], "z": start_z}, "end": {"x": end[0], "y": end[1], "z": end_z}, "player": {"members": True}}

headers = {
"Content-Type": "application/json",
"Origin": "https://explv.github.io",
}
response = requests.post(API_URL, data=json.dumps(payload), headers=headers)
if response.status_code == 200:
data = response.json()
if data["pathStatus"] != "SUCCESS":
error_message = ERROR_MESSAGE_MAPPING.get(data["pathStatus"], "Unknown error")
return {"status": "error", "data": None, "error": error_message}
else:
path = data["path"]
path_positions = [(pos["x"], pos["y"], pos["z"]) for pos in path]
return {"status": "success", "data": path_positions, "error": None}
else:
return {"status": "error", "data": None, "error": "Error occurred while communicating with the server"}
156 changes: 156 additions & 0 deletions src/utilities/walker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
import math
from typing import List

import utilities.api.locations as loc
from utilities.api.morg_http_client import MorgHTTPSocket
from utilities.api.pathfinding_api import Pathfinder


class Walking:
DEGREESPERYAW: float = 360 / 2048
TILES_PIXELS = 4

def __init__(self, runeLiteBot):
self.bot = runeLiteBot
self.api_m = MorgHTTPSocket()
self.run_bool = False

def update_run_energy(self):
self.run_energy = self.api_m.get_run_energy()

def update_position(self):
self.position = self.api_m.get_player_position()

def update_camera_angle(self) -> None:
self.camera_angle = self.api_m.get_camera_position().get("yaw")

def compute_tiles(self, new_x: int, new_y: int) -> List[float]:
"""Returns the range to click from the minimap center in amount of tiles."""
# Get live camera data.
self.update_camera_angle()
# Account for anticlockwise OSRS minimap.
degrees = 360 - self.DEGREESPERYAW * self.camera_angle
# Turn degrees into pi-radians.
theta = math.radians(degrees)
# Turn position difference into pixels difference.
self.update_position()
x_reg = (new_x - self.position[0]) * self.TILES_PIXELS
y_reg = (self.position[1] - new_y) * self.TILES_PIXELS
# Formulas to compute norm of a vector in a rotated coordinate system.
tiles_x = x_reg * math.cos(theta) + y_reg * math.sin(theta)
tiles_y = -x_reg * math.sin(theta) + y_reg * math.cos(theta)
return [round(tiles_x, 1), round(tiles_y, 1)]

def change_position(self, new_pos: List[int]) -> None:
"""Clicks the minimap to change position"""
self.update_position()
tiles = self.compute_tiles(new_pos[0], new_pos[1])
if tiles != []:
minimap_center = self.bot.win.minimap.get_center()
new_x = round(minimap_center[0] + tiles[0] - 1)
new_y = round(minimap_center[1] + tiles[1] - 1)
self.bot.mouse.move_to([new_x, new_y], mouseSpeed="fast")
self.bot.mouse.click()
while abs(self.position[0] - new_pos[0]) > 5 or abs(self.position[1] - new_pos[1]) > 5:
self.update_position()
continue

def get_target_pos(self, path) -> List[int]:
"""Returns furthest possible coord."""
self.update_position()
idx = next(i for i in range(len(path) - 1, -1, -1) if (abs(path[i][0] - self.position[0]) <= 12 and abs(path[i][1] - self.position[1]) <= 12))
self.bot.log_msg(f"Walking progress: {idx}/{len(path)}")
new_pos = path[idx]
return new_pos

def turn_run_on(self) -> None:
"""Turns on run energy."""
self.bot.mouse.move_to(self.bot.win.run_orb.random_point(), duration=0.2)
self.bot.mouse.click()

def check_if_at_destination(self, area_destination) -> bool:
"""Returns whether the player reached his destination."""
self.update_position()

bool_x = self.position[0] in range(area_destination[0] - 1, area_destination[2] + 1)
bool_y = self.position[1] in range(area_destination[1] - 1, area_destination[3] + 1)

return bool_x and bool_y

def handle_running(self) -> None:
"""Turns on run if run energy is higher than 60."""
# If run is off and run energy is larger than 60, turn on run.
self.run_energy = self.api_m.get_run_energy()
if self.run_energy < 5000 or self.run_energy == 10000:
self.run_bool = False
if self.run_energy > 6000 and self.run_bool is False:
self.turn_run_on()
self.run_bool = True

def walk(self, path, area_destination) -> None:
"""Walks a path by clicking on the minimap"""
while True:
# Turn on running if needed
self.handle_running()
# Get live position.
new_pos = self.get_target_pos(path)
if self.check_if_at_destination(area_destination):
self.bot.mouse.move_to(self.bot.win.game_view.get_center(), mouseSpeed="fastest")
if self.bot.mouseover_text("Walk"):
self.bot.mouse.click()
return True
self.change_position(new_pos)
if new_pos == path[-1]:
while not self.check_if_at_destination(area_destination):
self.update_position()
continue

def walk_to(self, end_location):
current_location = self.api_m.get_player_position()
new_current_position = [current_location[0], current_location[1]]
if isinstance(end_location, str):
end_coordinates = getattr(loc, end_location)
new_end_coordinates = [end_coordinates[0] - 1, end_coordinates[1] - 1, end_coordinates[0] + 1, end_coordinates[1] + 1]
if isinstance(end_location, list):
end_coordinates = end_location
new_end_coordinates = [end_coordinates[0] - 1, end_coordinates[1] - 1, end_coordinates[0] + 1, end_coordinates[1] + 1]
if isinstance(end_location, tuple):
end_coordinates = end_location
new_end_coordinates = [end_coordinates[0] - 1, end_coordinates[1] - 1, end_coordinates[0] + 1, end_coordinates[1] + 1]
path = []
while path == []:
path = self.api_path(new_current_position, end_coordinates)
if path == []:
self.bot.take_break(4, 5)

self.walk(path, new_end_coordinates)

def api_path(self, start_coordinates, end_coordinates):
pathfinder = Pathfinder()
steps = pathfinder.get_path(start_coordinates, end_coordinates)

if steps.get("status") == "success":
processed_data = [[item[0], item[1]] for item in steps.get("data")]
waypoints = self.add_waypoints(processed_data)
return waypoints
return []

def distance(self, p1, p2):
return math.sqrt((p2[0] - p1[0]) ** 2 + (p2[1] - p1[1]) ** 2)

def add_waypoints(self, coordinates):
new_coordinates = [coordinates[0]] # Start with the first coordinate
for i in range(len(coordinates) - 1):
p1 = coordinates[i]
p2 = coordinates[i + 1]
d = self.distance(p1, p2) # Calculate distance between consecutive waypoints

if d > 10:
num_waypoints = math.ceil(d / 11) # Calculate number of waypoints needed
dx = (p2[0] - p1[0]) / num_waypoints
dy = (p2[1] - p1[1]) / num_waypoints
for j in range(1, num_waypoints):
new_coordinates.append([round(p1[0] + j * dx), round(p1[1] + j * dy)]) # Add intermediate waypoints
new_coordinates.append(p2) # Add the next waypoint

return new_coordinates

0 comments on commit 0ac9450

Please sign in to comment.