def drop_slot(self, slot=None, drop_stack=False): old_index = getattr(slot, 'slot_nr', slot) action_id = self.inventory.drop_slot(slot, drop_stack) if not action_id: raise TaskFailed('Drop slot failed: not clicked') yield 'inventory_click_response', check_key('action_id', action_id) new_slot = self.inventory.window.slots[old_index] if drop_stack and old_index is not None and new_slot.amount > 0: raise TaskFailed('Drop slot failed: slot %i not empty' % old_index)
def drop_slot(self, slot=None, *args, **kwargs): # TODO drop_slot is untested old_slot = getattr(slot, 'slot_nr', slot) action_id = self.inventory.drop_slot(slot, *args, **kwargs) if not action_id: raise TaskFailed('Drop slot failed: not clicked') yield 'inventory_click_response', check_key('action_id', action_id) new_slot = self.inventory.window.slots[old_slot] if old_slot is not None and new_slot.amount > 0: raise TaskFailed('Drop slot failed: slot %i not empty' % old_slot)
def creative_set_slot(self, slot_nr=None, slot_dict=None, slot=None): self.inventory.creative_set_slot(slot_nr, slot_dict, slot) slot_nr = slot_nr if slot is None else slot.slot_nr e, data = yield ('inventory_set_slot', lambda e, data: data['slot'].slot_nr == slot_nr) if False: # TODO implement check, for now just assume it worked raise TaskFailed('Creative set slot failed: not set')
def store_or_drop(self): """ Stores the cursor item or drops it if the inventory is full. Tip: look directly up or down before calling this, so you can pick up the dropped item when the inventory frees up again. Returns: Slot: The slot used to store it, or None if dropped. """ inv = self.inventory if inv.cursor_slot.is_empty: # nothing to drop raise StopIteration(None) storage = inv.inv_slots_preferred if inv.window.is_storage: storage += inv.window.window_slots first_empty_slot = inv.find_slot(constants.INV_ITEMID_EMPTY, storage) if first_empty_slot is not None: yield self.click_slot(first_empty_slot) else: yield self.drop_slot(drop_stack=True) if not inv.cursor_slot.is_empty: raise TaskFailed('Store or Drop failed: cursor is not empty') raise StopIteration(first_empty_slot)
def click_slots(self, *slots): slots = unpack_slots_list(slots) for i, slot in enumerate(slots): try: yield self.click_slot(slot) except TaskFailed as e: raise TaskFailed('Clicking %i failed (step %i/%i): %s' % (slot, i + 1, len(slots), e.args))
def transfer_slots(self, source_slots, target_slots): for slot in source_slots: if slot.is_empty: continue item_id_empty = constants.INV_ITEMID_EMPTY target = self.inventory.find_slot(item_id_empty, target_slots) if target is None: raise TaskFailed('Transfer slots failed: target slots full') yield self.swap_slots(slot, target)
def hold_item(self, wanted): found = self.inventory.find_slot(wanted) if not found: raise TaskFailed('Could not hold item: not found') elif found in self.inventory.window.hotbar_slots: self.inventory.select_active_slot(found) raise StopIteration('Found item in hotbar') else: yield self.swap_slots(found, self.inventory.active_slot) raise StopIteration('Found item in inventory')
def timeout(t, other_events): timer_event = 'eat_timeout_%s' % random.random def cb(*_): self.event.emit(timer_event, {}) self.timers.reg_event_timer(t, cb, runs=1) event, _ = yield timer_event, other_events if event == timer_event: raise TaskFailed('Timed out after %s' % t)
def task_deep_failure(): def task_deepest_failure(): last_data[0] = yield 'cccc' raise TaskFailed('Low level error!') def task_deeper_failure(): yield task_deepest_failure() # error falls through try: yield task_deeper_failure() except TaskFailed as error: raise TaskFailed('High level error!').with_error(error)
def click_slot(self, slot, *args, **kwargs): if isinstance(slot, int): slot = self.inventory.window.slots[slot] old_slot = slot.copy() old_cursor = self.inventory.cursor_slot.copy() action_id = self.inventory.click_slot(slot, *args, **kwargs) if not action_id: raise TaskFailed('Click slot failed: not clicked') yield 'inventory_click_response', check_key('action_id', action_id) # TODO make sure window is not closed while clicking empty_cursor = old_cursor.is_empty if old_slot.amount == old_slot.item.stack_size and not empty_cursor \ or old_slot.is_empty and empty_cursor: return # no need to check new_slot = self.inventory.window.slots[old_slot.slot_nr] new_cursor = self.inventory.cursor_slot if new_slot.matches(old_slot) and new_cursor.matches(old_cursor): raise TaskFailed('Click slot failed: slot %i did not change (%s)' % (old_slot.slot_nr, old_slot))
def drop_items_task(amount_left): while amount_left > 0: found_slot = self.inv.find_slot(item) if found_slot is None: raise TaskFailed('No %s stored anymore' % item) if amount_left >= found_slot.amount: amount_left -= found_slot.amount yield self.inv. async .drop_slot(found_slot, drop_stack=True) else: logger.debug('Dropping %s single', amount_left) for i in range(amount_left): yield self.inv. async .drop_slot(found_slot) amount_left -= 1
def swap_slots(self, a, b): def slot(i): return self.inventory.window.slots[i] a = getattr(a, 'slot_nr', a) b = getattr(b, 'slot_nr', b) a_old, b_old = slot(a).copy(), slot(b).copy() if not slot(a).is_empty or not self.inventory.cursor_slot.is_empty: yield self.click_slot(a) if not slot(b).is_empty or not self.inventory.cursor_slot.is_empty: yield self.click_slot(b) if not slot(a).is_empty or not self.inventory.cursor_slot.is_empty: yield self.click_slot(a) if not slot(a).matches(b_old) or not slot(b).matches(a_old): raise TaskFailed('Failed to swap slots %i and %i' % (a, b))
def craft_task(self, recipe, amount=1): """ A task that crafts ``amount`` items with ``recipe``. """ if not recipe: raise TaskFailed('[Craft] No recipe given: %s' % recipe) if amount <= 0: raise TaskFailed('[Craft] Nothing to craft, amount=%s' % amount) inv = self.inventory craft_times = int(ceil(amount / recipe.result.amount)) try: # check if open window supports crafting grid_slots = inv.window.craft_grid_slots result_slot = inv.window.craft_result_slot except AttributeError: raise TaskFailed('[Craft] %s is no crafting window' % inv.window.__class__.__name__) num_grid_slots = len(grid_slots) try: grid_width = {4: 2, 9: 3}[num_grid_slots] except KeyError: raise TaskFailed('Crafting grid has unsupported size of' ' %i instead of 4 or 9' % num_grid_slots) grid_height = num_grid_slots / grid_width row1 = recipe.in_shape[0] if len(recipe.in_shape) > grid_height or len(row1) > grid_width: raise TaskFailed('Recipe for %s does not fit in a %ix%i grid' % (recipe.result, grid_width, grid_height)) storage_slots = inv.window.persistent_slots # check ingredients for recipe total_amounts_needed = total_ingredient_amounts(recipe) for ingredient, needed in total_amounts_needed.items(): needed *= craft_times stored = inv.total_stored(ingredient, storage_slots) if needed > stored: raise TaskFailed('Missing %s not stored, have %s of %i' % ('%s:%s' % ingredient, stored, needed)) # put ingredients into crafting grid for ingredient, p in ingredient_positions(recipe).items(): for (x, y, ingredient_amount) in p: slot = grid_slots[x + y * grid_width] for i in range(ingredient_amount * craft_times): if inv.cursor_slot.is_empty: ingr_slot = inv.find_slot(ingredient, storage_slots) if not ingr_slot: # should not occur, as we checked raise TaskFailed('Craft: No %s:%s found' ' in inventory' % ingredient) yield inv. async .click_slot(ingr_slot) # TODO speed up mass crafting with left+right clicking yield inv. async .click_slot(slot, right=True) # done putting in that item, put away if not inv.cursor_slot.is_empty: yield inv. async .store_or_drop() # TODO check if all items are in place # otherwise we will get the wrong crafting result # take crafted items prev_cursor_amt = inv.cursor_slot.amount crafted_amt = 0 while amount > crafted_amt + inv.cursor_slot.amount: yield inv. async .click_slot(result_slot) # TODO check that cursor is non-empty, otherwise we did not craft result_stack_size = inv.cursor_slot.stack_size if inv.cursor_slot.amount in (prev_cursor_amt, result_stack_size): # cursor full, put away crafted_amt += inv.cursor_slot.amount yield inv. async .store_or_drop() prev_cursor_amt = inv.cursor_slot.amount if not inv.cursor_slot.is_empty: # cursor still has items left from crafting, put away yield inv. async .store_or_drop() # put ingredients left from crafting back into inventory yield inv. async .move_to_inventory(grid_slots)
def task_deepest_failure(): last_data[0] = yield 'cccc' raise TaskFailed('Low level error!')
def task_with_failure(): last_data[0] = yield 'bbbb' raise TaskFailed('Some error!')