diff --git a/bot.py b/bot.py index 684e306..cfad08a 100644 --- a/bot.py +++ b/bot.py @@ -181,6 +181,7 @@ def bot(global_state): g.chat = ChatManager(g) g.game = game.Game(g) + g.world = game.MCWorld(g) try: while not g.pos: diff --git a/game.py b/game.py index 99c1028..810d892 100644 --- a/game.py +++ b/game.py @@ -1,6 +1,7 @@ import re import time import importlib +from math import hypot from panda3d.core import LPoint3f @@ -15,6 +16,151 @@ importlib.reload(path) import blocks importlib.reload(blocks) +class MCWorld: + def __init__(self, global_state): + self.g = global_state + self.l = self.g.local_state + + def block_at(self, x, y, z): + return self.g.chunks.get_block_at(x, y, z) + + def find_blocks(self, center, distance, block_ids, limit=0): + # search in a spiral from center to all blocks with ID + result = [] + for n in count(): + offset = utils.spiral(n) + check = utils.padd(center, offset) + if self.block_at(*check) in block_ids: + if hypot(*offset) < distance: + result.append(check) + if limit and len(result) == limit: + return result + if offset[0] > distance: + return result + + def find_trees(self, center, distance): + logs = [] + for i in range(5): + check = utils.padd(center, alternate(i, 3)) + logs.extend(self.find_blocks(check, distance, blocks.LOG_IDS, 50)) + + trees = [] + for log in logs: + # crawl to the bottom log + while self.block_at(*utils.padd(log, path.BLOCK_BELOW)) in blocks.LOG_IDS: + log = utils.padd(log, path.BLOCK_BELOW) + + # make sure we are on the ground + if self.block_at(*utils.padd(log, path.BLOCK_BELOW)) in blocks.NON_SOLID_IDS: + continue + + # crawl to the top log to count + log_count = 1 + while self.block_at(*utils.padd(log, path.BLOCK_ABOVE)) in blocks.LOG_IDS: + log = utils.padd(log, path.BLOCK_ABOVE) + log_count += 1 + + # make sure it's a good tree + if self.block_at(*utils.padd(log, path.BLOCK_ABOVE)) in blocks.LEAF_IDS and log_count > 2: + # crawl back to the bottom log + while self.block_at(*utils.padd(log, path.BLOCK_BELOW)) in blocks.LOG_IDS: + log = utils.padd(log, path.BLOCK_BELOW) + trees.append(log) + + trees.sort(key=lambda x: phyp(center, x)) + return trees + + def find_tree_openings(self, tree): + # returns coords in a cardinal direction where we can stand by tree + maze_solver = MazeSolver(self.g.chunks) + result = [] + + # TODO: make sure only non-solid and leaves between + # make sure traversable too + + for distance in range(5): + for direction in CHECK_DIRECTIONS: + offset = pmul(direction, distance+1) + if maze_solver.check_traverse(tree, offset): + result.append(utils.padd(tree, offset)) + return result + + def path_to_place(self, start, place): + maze_solver = MazeSolver(self.g.chunks) + + try: + s = maze_solver.astar(start, place) + return list(s) if s else None + except AStarTimeout: + return None + + def find_bed_areas(self, center, distance): + air = [] + for i in range(5): + check = utils.padd(center, alternate(i, 1)) + air.extend(self.find_blocks(check, distance, [0], 200)) + + areas = [] + for a in air: + # check for ground around the area + if len(self.find_blocks(utils.padd(a, path.BLOCK_BELOW), 2, blocks.NON_SOLID_IDS, 9)): + continue + + # check for air around the area + if len(self.find_blocks(a, 2, [0], 9)) < 9: + continue + + # check for air above the area + if len(self.find_blocks(utils.padd(a, path.BLOCK_ABOVE), 2, [0], 9)) < 9: + continue + + areas.append(a) + + areas.sort(key=lambda x: phyp(center, x)) + return areas + + def sand_adjacent_safe(self, sand): + for direction in CHECK_DIRECTIONS: + if self.block_at(*utils.padd(sand, direction)) in blocks.AVOID_IDS: + return False + return True + + def find_sand(self, center, distance, origin): + sand = [] + for i in range(10): + check = utils.padd(center, alternate(i, 1)) + sand.extend(self.find_blocks(check, distance, [66], 20)) + + safe_sand = [] + for s in sand: + # make sure it has solid below + if self.block_at(*utils.padd(s, path.BLOCK_BELOW)) in blocks.NON_SOLID_IDS: + continue + # make sure it has solid two below - prevent hanging sand + if self.block_at(*utils.padd(s, path.BLOCK_BELOW2)) in blocks.NON_SOLID_IDS: + continue + + # and walkable air above + if self.block_at(*utils.padd(s, path.BLOCK_ABOVE)) not in blocks.NON_SOLID_IDS: + continue + + if not self.sand_adjacent_safe(s): + continue + + safe_sand.append(s) + + safe_sand.sort(key=lambda x: utils.phyp_bias(center, x, origin)) + return safe_sand + + def find_bed_openings(self, area): + # returns coords in a cardinal direction where we can stand by bed + result = [] + + for direction in CHECK_DIRECTIONS: + result.append(utils.padd(area, direction)) + return result + + class Game: def __init__(self, global_state): self.g = global_state @@ -164,6 +310,17 @@ class Game: packet.hand = packet.HAND_MAIN self.g.connection.write_packet(packet) + def place_block(self, location, face): + packet = serverbound.play.PlayerBlockPlacementPacket() + packet.hand = 0 + packet.location = pos + packet.face = face + packet.x = 0.5 + packet.y = 0.5 + packet.z = 0.5 + packet.inside_block = False + self.g.connection.write_packet(packet) + def tick(self): if self.l.breaking: self.animate() diff --git a/path.py b/path.py index e980ffd..d9dde08 100644 --- a/path.py +++ b/path.py @@ -49,6 +49,18 @@ PARKOUR_SOUTH = (0, 0, +2) PARKOUR_EAST = (+2, 0, 0) PARKOUR_WEST = (-2, 0, 0) +CHECK_NORTH = (0, 0, -1) +CHECK_SOUTH = (0, 0, +1) +CHECK_EAST = (+1, 0, 0) +CHECK_WEST = (-1, 0, 0) + +CHECK_DIRECTIONS = [ + CHECK_NORTH, + CHECK_SOUTH, + CHECK_EAST, + CHECK_WEST, +] + TRAVERSE = [ TRAVERSE_NORTH, TRAVERSE_SOUTH,