diff --git a/blocks.py b/blocks.py index 8a19a11..82978f4 100644 --- a/blocks.py +++ b/blocks.py @@ -59,6 +59,12 @@ AVOID = [ 'sandstone_wall', 'end_stone_brick_wall', 'diorite_wall', + 'oak_sapling', # saplings can grow up and hurt + 'spruce_sapling', + 'birch_sapling', + 'jungle_sapling', + 'acacia_sapling', + 'dark_oak_sapling', ] NON_SOLID = [ @@ -194,12 +200,6 @@ NON_SOLID = [ 'cave_air', 'lantern', 'soul_torch', - #'oak_sapling', # saplings can grow up and hurt - #'spruce_sapling', - #'birch_sapling', - #'jungle_sapling', - #'acacia_sapling', - #'dark_oak_sapling', ] LOGS = [ diff --git a/game.py b/game.py index b821aee..9da659a 100644 --- a/game.py +++ b/game.py @@ -41,6 +41,12 @@ class MCWorld: def block_at(self, x, y, z): return self.g.chunks.get_block_at(x, y, z) + def find_blocks_3d(self, center, block_ids, distance=0, y_limit=0): + for offset in utils.search_3d(distance, y_limit): + check = utils.padd(center, offset) + if self.block_at(*check) in block_ids: + yield check + def find_blocks(self, center, distance, block_ids, limit=0): # search in a spiral from center to all blocks with ID result = [] @@ -112,16 +118,10 @@ class MCWorld: return None def find_bed_areas(self, center, distance): - air = [] - for i in range(5): - check = utils.padd(center, utils.alternate(i, 1)) - air.extend(self.find_blocks(check, distance, [0], 0)) - bed_clearance = 25 # 5x5 area clear_distance = 3 - areas = [] - for a in air: + for a in self.find_blocks_3d(center, [0], distance, 10): # check for air around the area if len(self.find_blocks(a, clear_distance, [0], bed_clearance)) < bed_clearance: continue @@ -134,10 +134,7 @@ class MCWorld: if len(self.find_blocks(utils.padd(a, path.BLOCK_ABOVE), clear_distance, [0], bed_clearance)) < bed_clearance: continue - areas.append(a) - - areas.sort(key=lambda x: utils.phyp(center, x)) - return areas + yield a def find_cache_areas(self, center, distance): return self.find_bed_areas(center, distance) @@ -264,15 +261,19 @@ class Game: def handle_chat(self, message): source, text = message reply = None + private = False if source == 'SYSTEM': - print('unlocking command...') self.g.command_lock = False return - match = re.match(r'<(\w+)> (.*)', text) - if match: - sender, text = match.groups() + match1 = re.match(r'<(\w+)> (.*)', text) + match2 = re.match(r'\[(\w+) -> me] (.*)', text) + if match1: + sender, text = match1.groups() + elif match2: + sender, text = match2.groups() + private = True else: return @@ -350,15 +351,12 @@ class Game: reply = 'ok' if command == 'inv': - print(self.g.inv) inv_list = [] for i in self.g.inv.values(): if i.present: inv_list.append('{}:{} x {}'.format(items.ITEM_NAMES[i.item_id], str(i.item_id), i.item_count)) - reply = ', '.join(inv_list) - - if not reply: - reply = 'empty' + result = '\n'.join(inv_list) + print(result or 'Empty') if command == 'drop': self.drop_stack() @@ -416,9 +414,16 @@ class Game: if data and v.item_id != int(data): continue print(str(k) + ':', v) + if command == 'cache': + self.g.job.state = self.g.job.cache_items + reply = 'ok' + if reply: print(reply) - self.g.chat.send(reply) + if private and not reply.startswith('/'): + self.g.chat.send('/m ' + sender + ' ' + reply) + else: + self.g.chat.send(reply) def handle_time_update(self, packet): self.g.time = packet.time_of_day % 24000 @@ -578,7 +583,7 @@ class Game: def handle_spawn_object(self, packet): #return if packet.type_id != 37: return - print(packet) + #print(packet) self.g.objects[packet.entity_id] = Munch( entity_id=packet.entity_id, x=packet.x, diff --git a/items.py b/items.py index 9a22134..4e0e935 100644 --- a/items.py +++ b/items.py @@ -47,4 +47,5 @@ CHEST_ID = set([ITEMS['minecraft:chest']['protocol_id']]) GAPPLE_ID = set([ITEMS['minecraft:enchanted_golden_apple']['protocol_id']]) -USEFUL_ITEMS = BED_IDS | CHEST_ID +NEEDED_ITEMS = BED_IDS | CHEST_ID +WANTED_ITEMS = SAPLING_IDS diff --git a/jobs.py b/jobs.py index 1879151..bd8bdb5 100644 --- a/jobs.py +++ b/jobs.py @@ -385,20 +385,16 @@ class SleepWithBedStates: w = self.g.world p = utils.pint(self.g.pos) - areas = w.find_bed_areas(p, 100) - print('Found areas:', areas) - - if len(areas): - while areas[0] in self.bad_areas: - areas.pop(0) - self.area = areas[0] - elif self.last_area: - self.area = self.last_area - else: - print('Unable to find area, and no last area') + for area in w.find_bed_areas(p, 100): + print('Found area:', area) + if area not in self.bad_areas: + break + else: # for + print('Unable to find area') self.state = self.cleanup return + self.area = area openings = w.find_bed_openings(self.area) for o in openings: @@ -413,7 +409,6 @@ class SleepWithBedStates: self.g.path = navpath self.state = self.going_to_area - self.last_area = self.area def going_to_area(self): if utils.pint(self.g.pos) == self.opening: @@ -479,7 +474,6 @@ class SleepWithBedStates: self.area = None self.opening = None self.bad_areas = [] - self.last_area = None self.wait_time = 0 def run(self): @@ -491,8 +485,11 @@ class CacheItemsStates: return None def init(self): + self.skip_slots = [] + self.skip_items = [] + num_stacks = len([x for x in self.g.inv.values() if x.present]) - print('inventory amount:', num_stacks) + print('Inventory amount:', num_stacks) if num_stacks >= 27: self.state = self.find_cache_spot else: @@ -504,20 +501,16 @@ class CacheItemsStates: w = self.g.world p = utils.pint(self.g.pos) - areas = w.find_cache_areas(p, 100) - print('Found areas:', areas) - - if len(areas): - while areas[0] in self.bad_areas: - areas.pop(0) - self.area = areas[0] - elif self.last_area: - self.area = self.last_area - else: - print('Unable to find area, and no last area') + for area in w.find_cache_areas(p, 100): + print('Found area:', area) + if area not in self.bad_areas: + break + else: # for + print('Unable to find area') self.state = self.cleanup return + self.area = area openings = w.find_cache_openings(self.area) for o in openings: @@ -532,7 +525,6 @@ class CacheItemsStates: self.g.path = navpath self.state = self.going_to_area - self.last_area = self.area def going_to_area(self): if utils.pint(self.g.pos) == self.opening: @@ -571,6 +563,8 @@ class CacheItemsStates: w_info = data.WINDOWS[w.data.window_type] w_inventory_slots = w_info.inventory + slot_list = [] + for slot_num in w_inventory_slots: if slot_num not in w.contents: continue @@ -580,14 +574,25 @@ class CacheItemsStates: if not slot.present: continue - if slot.item_id in items.USEFUL_ITEMS: + if slot.item_id in self.needed_items: + continue + + if slot_num in self.skip_slots: + continue + + slot_list.append((slot_num, slot)) + + slot_list.sort(key=lambda x: x[1].item_count, reverse=True) + + for slot_num, slot in slot_list: + if slot.item_id in self.wanted_items and slot.item_id not in self.skip_items: + print('skipping wanted item', slot) + self.skip_slots.append(slot_num) + self.skip_items.append(slot.item_id) continue print('moving', slot) - #inv_slot_num = slot_num - w_info.slot_diff - #self.g.inv.pop(inv_slot_num, None) - self.g.item_lock = True self.g.game.click_window(slot_num, 0, 1, slot) return @@ -613,10 +618,17 @@ class CacheItemsStates: self.g = global_state self.state = self.idle + # keep all needed items + self.needed_items = items.NEEDED_ITEMS + # keep one stack of wanted items + self.wanted_items = items.WANTED_ITEMS + + self.skip_slots = [] + self.skip_items = [] + self.area = None self.opening = None self.bad_areas = [] - self.last_area = None self.wait_time = 0 def run(self): @@ -681,19 +693,16 @@ class PlantTreeStates: w = self.g.world p = utils.pint(self.g.pos) - areas = w.find_cache_areas(p, 20) - print('Found areas:', areas) - - try: - while areas[0] in self.bad_areas: - areas.pop(0) - self.area = areas[0] - except IndexError: - print('No good areas left, aborting.') - self.bad_areas = [] + for area in w.find_cache_areas(p, 20): + print('Found area:', area) + if area not in self.bad_areas: + break + else: # for + print('Unable to find area') self.state = self.cleanup return + self.area = area navpath = w.path_to_place(p, self.area) if not navpath: @@ -803,6 +812,16 @@ class JobStates: def idle(self): return None + def cache_items(self): + s1 = self.cache_items_states + + if s1.state == s1.idle: + s1.state = s1.init + elif s1.state == s1.done: + self.state = self.idle + + s1.run() + def find_gapple(self): s1 = self.find_gapple_states diff --git a/utils.py b/utils.py index b68343d..05e5b77 100644 --- a/utils.py +++ b/utils.py @@ -1,4 +1,5 @@ import importlib +import collections from math import floor, ceil, sqrt, hypot import blocks @@ -94,3 +95,50 @@ def break_time(block_id, held_item=0, in_water=False, on_ground=True, enchantmen if not on_ground: time *= 5.0 return time + +def search_3d(distance=0, y_limit=0): + def get_neighbors(x,y,z): + return [ + (x+1, y+1, z+0), + (x+1, y-1, z+0), + (x+1, y+1, z+1), + (x+1, y+0, z+1), + (x+1, y-1, z+1), + (x+1, y+1, z-1), + (x+1, y+0, z-1), + (x+1, y-1, z-1), + (x+1, y+0, z+0), + (x+0, y+1, z+0), + (x+0, y-1, z+0), + (x+0, y+1, z+1), + (x+0, y+0, z+1), + (x+0, y-1, z+1), + (x+0, y+1, z-1), + (x+0, y+0, z-1), + (x+0, y-1, z-1), + (x-1, y+1, z+0), + (x-1, y-1, z+0), + (x-1, y+1, z+1), + (x-1, y+0, z+1), + (x-1, y-1, z+1), + (x-1, y+1, z-1), + (x-1, y+0, z-1), + (x-1, y-1, z-1), + (x-1, y+0, z+0), + ] + + to_visit = collections.deque([(0, 0, 0)]) + visited = set() + + while to_visit: + cur = to_visit.pop() + if cur in visited: + continue + if y_limit and abs(cur[1]) > y_limit: + continue + if distance and hypot(*cur) > distance: + continue + for neighbor in get_neighbors(*cur): + to_visit.appendleft(neighbor) + visited.add(cur) + yield cur