Add sign-labeled chest storage

This commit is contained in:
Tanner
2026-11-07 16:35:57 +00:00
parent ab8f59a89b
commit 43b37b76f6
7 changed files with 401 additions and 66 deletions
+19
View File
@@ -83,6 +83,25 @@ EMERALD_ID = get_id('emerald')
BERRIES_ID = get_id('sweet_berries') BERRIES_ID = get_id('sweet_berries')
IRON_INGOT_ID = get_id('iron_ingot') IRON_INGOT_ID = get_id('iron_ingot')
_LABEL_ITEM_IDS = {
'seeds': get_set({
'beetroot', 'beetroot_seeds', 'carrot', 'melon_seeds',
'nether_wart', 'pitcher_pod', 'potato', 'pumpkin_seeds',
'torchflower_seeds', 'wheat_seeds',
}),
'wheat': {WHEAT_ID},
}
def storage_item_ids_for_label(label):
"""Return protocol item IDs assigned to a sign storage label."""
normalized = ' '.join(str(label).casefold().split()).strip(' :')
normalized = (normalized.rstrip('s')
if normalized not in ('seeds', 'beds') else normalized)
return set(_LABEL_ITEM_IDS.get(normalized, ()))
INIT_NEEDED_ITEMS = BED_IDS | FOOD_IDS INIT_NEEDED_ITEMS = BED_IDS | FOOD_IDS
INIT_NEEDED_ITEMS.add(CHEST_ID) INIT_NEEDED_ITEMS.add(CHEST_ID)
NEEDED_ITEMS = INIT_NEEDED_ITEMS NEEDED_ITEMS = INIT_NEEDED_ITEMS
+95 -8
View File
@@ -22,6 +22,8 @@ class CacheItemsStates:
def init(self): def init(self):
self.skip_slots = [] self.skip_slots = []
self.skip_items = [] self.skip_items = []
self.is_labeled_container = False
self.container_item_ids = None
num_stacks = self.g.game.count_inventory_slots() num_stacks = self.g.game.count_inventory_slots()
minimum = getattr( minimum = getattr(
@@ -36,25 +38,54 @@ class CacheItemsStates:
self.state = self.cleanup self.state = self.cleanup
def find_trapped_chests(self): def find_trapped_chests(self):
print('Finding trapped chests...') print('Finding labeled and trapped chests...')
w = self.g.world w = self.g.world
p = utils.pint(self.g.pos) p = utils.pint(self.g.pos)
self.labeled_chests = w.find_labeled_chests(p, 80)
self.checked_labeled_chests = set()
self.trapped_chests = w.find_blocks_indexed(p, blocks.TRAPPED_CHEST_IDS) self.trapped_chests = w.find_blocks_indexed(p, blocks.TRAPPED_CHEST_IDS)
print('Found:', self.trapped_chests) print('Found labeled chests:', len(self.labeled_chests),
'trapped chests:', self.trapped_chests)
self.state = self.choose_trapped_chest self.state = self.choose_trapped_chest
def choose_trapped_chest(self): def choose_trapped_chest(self):
print('Choosing a trapped chest...') print('Choosing a labeled or trapped chest...')
w = self.g.world w = self.g.world
p = utils.pint(self.g.pos) p = utils.pint(self.g.pos)
c = self.g.chunks
for container in self.labeled_chests:
key = container['positions']
if key in self.checked_labeled_chests:
continue
self.checked_labeled_chests.add(key)
if not any(item.present and item.item_id in container['item_ids']
for item in self.g.inv.values()):
continue
self.area = container['position']
self.container_item_ids = container['item_ids']
self.is_labeled_container = True
for opening in w.find_cache_openings(self.area):
navpath = w.path_to_place(p, opening)
if navpath:
print('Going to labeled chest', container['labels'],
self.area)
self.opening = opening
self.g.path = navpath
self.state = self.going_to_trapped_chest
return
print('No path to labeled chest', self.area)
if not len(self.trapped_chests): if not len(self.trapped_chests):
print('No trapped chests') self.is_labeled_container = False
self.container_item_ids = None
print('No labeled or trapped chests')
self.state = self.select_chest self.state = self.select_chest
return return
self.is_labeled_container = False
self.container_item_ids = None
chest = self.trapped_chests[0] chest = self.trapped_chests[0]
self.area = chest self.area = chest
@@ -83,6 +114,8 @@ class CacheItemsStates:
self.state = self.open_chest self.state = self.open_chest
def select_chest(self): def select_chest(self):
self.is_labeled_container = False
self.container_item_ids = None
if self.g.game.select_item([items.CHEST_ID]): if self.g.game.select_item([items.CHEST_ID]):
self.state = self.find_cache_spot self.state = self.find_cache_spot
else: else:
@@ -149,6 +182,41 @@ class CacheItemsStates:
else: else:
self.state = self.move_items self.state = self.move_items
def _needed_inventory_slots_to_keep(self):
needed = set(items.NEEDED_ITEMS)
groups = (
items.BED_IDS, {items.CHEST_ID}, items.FOOD_IDS,
items.AXE_IDS, items.SHOVEL_IDS,
)
keep = set()
covered = set()
for group in groups:
group = needed.intersection(group)
if not group:
continue
covered.update(group)
stacks = [(slot, item) for slot, item in self.g.inv.items()
if 5 <= slot <= 45 and item.present and
item.item_id in group]
if stacks:
keep.add(max(stacks, key=lambda pair: pair[1].item_count)[0])
for item_id in needed - covered:
stacks = [(slot, item) for slot, item in self.g.inv.items()
if 5 <= slot <= 45 and item.present and
item.item_id == item_id]
if stacks:
keep.add(max(stacks, key=lambda pair: pair[1].item_count)[0])
return keep
def _window_player_slot(self, window_slot):
offset = window_slot - self.g.window.container_slots
if 0 <= offset < 27:
return 9 + offset
if 27 <= offset < 36:
return 36 + offset - 27
return None
def move_items(self): def move_items(self):
if self.g.item_lock: return if self.g.item_lock: return
w = self.g.window w = self.g.window
@@ -177,10 +245,13 @@ class CacheItemsStates:
print('Container is too full, aborting') print('Container is too full, aborting')
self.g.game.close_window() self.g.game.close_window()
self.g.look_at = None self.g.look_at = None
self.state = self.cleanup self.state = (self.close_chest if self.is_labeled_container
else self.cleanup)
return return
slot_list = [] slot_list = []
needed_slots = (self._needed_inventory_slots_to_keep()
if self.is_labeled_container else set())
for slot_num in w_inventory_slots: for slot_num in w_inventory_slots:
if slot_num not in w.contents: if slot_num not in w.contents:
@@ -191,7 +262,14 @@ class CacheItemsStates:
if not slot.present: if not slot.present:
continue continue
if slot.item_id in items.NEEDED_ITEMS: if self.is_labeled_container:
if slot.item_id not in self.container_item_ids:
continue
inventory_slot = self._window_player_slot(slot_num)
if (slot.item_id in items.NEEDED_ITEMS and
inventory_slot in needed_slots):
continue
elif slot.item_id in items.NEEDED_ITEMS:
continue continue
if slot_num in self.skip_slots: if slot_num in self.skip_slots:
@@ -222,7 +300,12 @@ class CacheItemsStates:
self.g.game.close_window() self.g.game.close_window()
if not self.silent: if not self.silent:
self.g.chat.send('cache at ' + str(self.area)[1:-1]) self.g.chat.send('cache at ' + str(self.area)[1:-1])
self.state = self.cleanup if self.is_labeled_container:
self.is_labeled_container = False
self.container_item_ids = None
self.state = self.choose_trapped_chest
else:
self.state = self.cleanup
def cleanup(self): def cleanup(self):
self.g.look_at = None self.g.look_at = None
@@ -244,6 +327,10 @@ class CacheItemsStates:
self.skip_slots = [] self.skip_slots = []
self.skip_items = [] self.skip_items = []
self.labeled_chests = []
self.checked_labeled_chests = set()
self.is_labeled_container = False
self.container_item_ids = None
self.area = None self.area = None
self.opening = None self.opening = None
self.trapped_chests = [] self.trapped_chests = []
+47 -51
View File
@@ -30,7 +30,7 @@ class GrabSuppliesStates:
def init(self): def init(self):
print('Started grab supplies states') print('Started grab supplies states')
self.checked_barrels = [] self.checked_supply_chests = set()
used_slots = self.g.game.count_inventory_slots() used_slots = self.g.game.count_inventory_slots()
print('used:', used_slots, 'total:', self.g.maximum_supply_slots) print('used:', used_slots, 'total:', self.g.maximum_supply_slots)
@@ -41,6 +41,7 @@ class GrabSuppliesStates:
self.checked_ground_items = set() self.checked_ground_items = set()
self.checked_ground_groups = set() self.checked_ground_groups = set()
self.checked_supply_chests = set()
self.ground_stack_counts = {} self.ground_stack_counts = {}
self.ground_entity_id = None self.ground_entity_id = None
self.ground_position = None self.ground_position = None
@@ -78,8 +79,8 @@ class GrabSuppliesStates:
self.ground_position = None self.ground_position = None
def check_supplies(self): def check_supplies(self):
# Search every configured item group on the ground before going to a # Search every configured item group on the ground before looking in
# barrel; a missing bed must not hide a nearby chest or food drop. # labeled chests; a missing bed must not hide a nearby chest or food drop.
missing = [] missing = []
for item_ids, limits in self.supplies.items(): for item_ids, limits in self.supplies.items():
minimum, maximum = limits minimum, maximum = limits
@@ -110,9 +111,9 @@ class GrabSuppliesStates:
if missing: if missing:
self.target_items, self.target_minimum, self.target_maximum_stacks = missing[0] self.target_items, self.target_minimum, self.target_maximum_stacks = missing[0]
print('No more ground drops; checking barrels for', print('No more ground drops; checking supply-labeled chests for',
self.target_items) self.target_items)
self.state = self.find_barrels self.state = self.find_supply_chests
return return
print('Aborting, dont need any supplies') print('Aborting, dont need any supplies')
@@ -122,7 +123,11 @@ class GrabSuppliesStates:
stack_count = self.ground_stack_counts.get(self.target_items, 0) stack_count = self.ground_stack_counts.get(self.target_items, 0)
if self.target_maximum_stacks and stack_count >= self.target_maximum_stacks: if self.target_maximum_stacks and stack_count >= self.target_maximum_stacks:
self.checked_ground_groups.add(self.target_items) self.checked_ground_groups.add(self.target_items)
self.state = self.check_supplies if (self.g.game.count_items(self.target_items) <
self.target_minimum):
self.state = self.find_supply_chests
else:
self.state = self.check_supplies
return return
print('Looking for supplies on the ground...') print('Looking for supplies on the ground...')
@@ -199,60 +204,52 @@ class GrabSuppliesStates:
self.ground_position = None self.ground_position = None
self.state = self.find_ground_items self.state = self.find_ground_items
def find_barrels(self): def find_supply_chests(self):
print('Finding barrels...') print('Finding chests labeled for this bot\'s supplies...')
w = self.g.world
p = utils.pint(self.g.pos) p = utils.pint(self.g.pos)
self.supply_chests = self.g.world.find_supply_chests(
p, self.g.name, 80)
print('Found:', len(self.supply_chests), 'supply-labeled chests')
self.state = self.choose_supply_chest
self.barrels = w.find_blocks_indexed(p, blocks.BARREL_IDS, 80) def choose_supply_chest(self):
print('Found:', self.barrels) print('Choosing a supply-labeled chest...')
self.state = self.choose_barrel for container in self.supply_chests:
key = container['positions']
def choose_barrel(self): if key in self.checked_supply_chests:
print('Choosing a barrel...')
for barrel in self.barrels:
if barrel in self.checked_barrels:
continue continue
if barrel in self.bad_barrels: self.checked_supply_chests.add(key)
continue self.chest = container['position']
self.area = self.chest
print('Chose:', barrel) print('Chose supply chest:', self.chest)
self.barrel = barrel self.state = self.path_to_supply_chest
self.state = self.path_to_barrel
return return
print('No barrels') print('No more supply-labeled chests')
self.state = self.cleanup self.state = self.cleanup
def path_to_barrel(self): def path_to_supply_chest(self):
print('Finding path to barrel') print('Finding path to supply chest')
w = self.g.world
p = utils.pint(self.g.pos) p = utils.pint(self.g.pos)
c = self.g.chunks navpath = self.g.world.path_to_place_faked(p, self.chest)
navpath = w.path_to_place_faked(p, self.barrel)
print('navpath:', navpath) print('navpath:', navpath)
if navpath: if navpath:
self.g.path = navpath[:-1] self.g.path = navpath[:-1]
self.opening = self.g.path[-1] self.opening = self.g.path[-1]
self.checked_barrels.append(self.barrel) self.state = self.going_to_supply_chest
self.area = self.barrel
self.state = self.going_to_barrel
self.checked_supplies = [] self.checked_supplies = []
return
else: else:
print('Barrel is unreachable; ending supply task') print('Supply chest is unreachable; trying another labeled chest')
self.bad_barrels.append(self.barrel) self.state = self.choose_supply_chest
self.state = self.cleanup
def going_to_barrel(self): def going_to_supply_chest(self):
if utils.pint(self.g.pos) == self.opening: if utils.pint(self.g.pos) == self.opening:
self.g.look_at = self.area self.g.look_at = self.area
self.state = self.open_barrel self.state = self.open_supply_chest
def open_barrel(self): def open_supply_chest(self):
print('Opening barrel') print('Opening supply chest')
self.g.game.open_container(self.area) self.g.game.open_container(self.area)
self.wait_time = 1 self.wait_time = 1
self.state = self.wait self.state = self.wait
@@ -288,8 +285,8 @@ class GrabSuppliesStates:
self.state = self.grab_items self.state = self.grab_items
return return
print('Aborting, dont need any more supplies') print('No matching items in this supply chest')
self.state = self.close_barrel self.state = self.close_supply_chest
def grab_items(self): def grab_items(self):
if self.g.item_lock: return if self.g.item_lock: return
@@ -299,7 +296,7 @@ class GrabSuppliesStates:
print('Didnt get a window, aborting') print('Didnt get a window, aborting')
self.state = self.cleanup self.state = self.cleanup
return return
if w.get('container_slots', 0) != 27: if w.get('container_slots', 0) not in (27, 54):
print('Got wrong container size, aborting') print('Got wrong container size, aborting')
self.state = self.cleanup self.state = self.cleanup
return return
@@ -338,11 +335,11 @@ class GrabSuppliesStates:
self.wait_time = 0.25 self.wait_time = 0.25
self.state = self.wait self.state = self.wait
def close_barrel(self): def close_supply_chest(self):
print('Closing barrel') print('Closing supply chest')
self.g.game.close_window() self.g.game.close_window()
self.g.look_at = None self.g.look_at = None
self.state = self.choose_barrel self.state = self.choose_supply_chest
def cleanup(self): def cleanup(self):
self.state = self.done self.state = self.done
@@ -356,10 +353,9 @@ class GrabSuppliesStates:
self.state = self.idle self.state = self.idle
self.supplies = self.default_supplies() self.supplies = self.default_supplies()
self.barrels = [] self.supply_chests = []
self.checked_barrels = [] self.checked_supply_chests = set()
self.bad_barrels = [] self.supply_chest = None
self.barrel = None
self.checked_supplies = [] self.checked_supplies = []
self.target_items = None self.target_items = None
self.maximum_stacks = 0 self.maximum_stacks = 0
+117
View File
@@ -9,6 +9,7 @@ from copy import copy
from mosfet import utils from mosfet import utils
from mosfet import path from mosfet import path
from mosfet.info import blocks from mosfet.info import blocks
from mosfet.info import items
from mosfet.info import mobs from mosfet.info import mobs
class World: class World:
@@ -76,6 +77,122 @@ class World:
result.sort(key=lambda sign: utils.phyp(center, sign['position'])) result.sort(key=lambda sign: utils.phyp(center, sign['position']))
return result return result
def _double_chest_positions(self, chest):
state_id = self.block_at(*chest)
properties = blocks.PROPS.get(state_id, {})
chest_type = properties.get('type')
if chest_type not in ('left', 'right'):
return (tuple(chest),)
other_type = 'right' if chest_type == 'left' else 'left'
for offset in ((1, 0, 0), (-1, 0, 0), (0, 0, 1), (0, 0, -1)):
partner = utils.padd(chest, offset)
other = blocks.PROPS.get(self.block_at(*partner), {})
if (other.get('type') == other_type and
other.get('facing') == properties.get('facing')):
return tuple(sorted((tuple(chest), partner)))
return (tuple(chest),)
def find_labeled_chests(self, center, distance=80):
"""Find single/double chests assigned item categories by nearby signs."""
grouped = {}
sign_offsets = (
(0, 0, 0), (0, -1, 0),
(1, 0, 0), (-1, 0, 0), (0, 0, 1), (0, 0, -1),
)
for sign in self.find_signs(center, distance):
categories = set()
labels = set()
for line in sign['lines']:
item_ids = items.storage_item_ids_for_label(line)
if item_ids:
categories.update(item_ids)
labels.add(line.strip())
if not categories:
continue
sign_pos = sign['position']
adjacent = {
utils.padd(sign_pos, offset)
for offset in sign_offsets
if self.block_at(*utils.padd(sign_pos, offset)) in blocks.CHEST_IDS
}
if not adjacent:
nearby = self.find_blocks_indexed(
sign_pos, blocks.CHEST_IDS, 2)
if nearby:
nearest = min(utils.phyp(sign_pos, chest)
for chest in nearby)
adjacent = {chest for chest in nearby
if utils.phyp(sign_pos, chest) == nearest}
for chest in adjacent:
positions = self._double_chest_positions(chest)
record = grouped.setdefault(positions, {
'position': tuple(chest),
'positions': positions,
'item_ids': set(),
'labels': set(),
'sign_positions': set(),
})
record['item_ids'].update(categories)
record['labels'].update(labels)
record['sign_positions'].add(sign_pos)
result = list(grouped.values())
result.sort(key=lambda chest: utils.phyp(center, chest['position']))
return result
def find_supply_chests(self, center, bot_name, distance=80):
"""Find chests whose sign is addressed to this bot's supplies."""
bot_name = str(bot_name).casefold()
group_name = (bot_name[:-1] if len(bot_name) > 1 and
bot_name[-1] in '123' else None)
addresses = {bot_name}
if group_name:
addresses.add(group_name)
grouped = {}
sign_offsets = (
(0, 0, 0), (0, -1, 0),
(1, 0, 0), (-1, 0, 0), (0, 0, 1), (0, 0, -1),
)
for sign in self.find_signs(center, distance):
text = ' '.join(line for line in sign['lines'] if line.strip())
tokens = re.findall(r'[a-z0-9_]+', text.casefold())
if (len(tokens) < 2 or tokens[0] not in addresses or
'supplies' not in tokens[1:]):
continue
sign_pos = sign['position']
candidates = {
utils.padd(sign_pos, offset)
for offset in sign_offsets
if self.block_at(*utils.padd(sign_pos, offset)) in blocks.CHEST_IDS
}
if not candidates:
nearby = self.find_blocks_indexed(
sign_pos, blocks.CHEST_IDS, 2)
if nearby:
nearest = min(utils.phyp(sign_pos, chest) for chest in nearby)
candidates = {chest for chest in nearby
if utils.phyp(sign_pos, chest) == nearest}
for chest in candidates:
positions = self._double_chest_positions(chest)
record = grouped.setdefault(positions, {
'position': tuple(chest),
'positions': positions,
'labels': set(),
'sign_positions': set(),
})
record['labels'].update(tokens[1:])
record['sign_positions'].add(sign_pos)
result = list(grouped.values())
result.sort(key=lambda chest: utils.phyp(center, chest['position']))
return result
def sign_policies(self, bot_name, distance=64): def sign_policies(self, bot_name, distance=64):
"""Parse sign policies addressed to this bot or its numbered group.""" """Parse sign policies addressed to this bot or its numbered group."""
bot_name = str(bot_name).casefold() bot_name = str(bot_name).casefold()
+1 -1
View File
@@ -147,7 +147,7 @@ class GatherWoodPortTests(unittest.TestCase):
self.assertTrue(items.AXE_IDS.issubset(items.NEEDED_ITEMS)) self.assertTrue(items.AXE_IDS.issubset(items.NEEDED_ITEMS))
self.assertTrue(items.SAPLING_IDS.issubset(items.WANTED_ITEMS)) self.assertTrue(items.SAPLING_IDS.issubset(items.WANTED_ITEMS))
job.grab_supplies_states.state = job.grab_supplies_states.find_barrels job.grab_supplies_states.state = job.grab_supplies_states.find_supply_chests
job.clear_leaves_states.state = job.clear_leaves_states.find_leaves job.clear_leaves_states.state = job.clear_leaves_states.find_leaves
job.plant_tree_states.state = job.plant_tree_states.check_feet job.plant_tree_states.state = job.plant_tree_states.check_feet
job.grab_sapling_states.state = job.grab_sapling_states.find_saplings job.grab_sapling_states.state = job.grab_sapling_states.find_saplings
+45 -6
View File
@@ -8,6 +8,14 @@ from mosfet.jobs.grab_supplies import GrabSuppliesStates
class GroundSupplyTests(unittest.TestCase): class GroundSupplyTests(unittest.TestCase):
def test_default_needed_supplies_are_configured_by_constructor(self): def test_default_needed_supplies_are_configured_by_constructor(self):
state = GrabSuppliesStates(Munch()) state = GrabSuppliesStates(Munch())
self.assertEqual(items.FOOD, [
'cooked_porkchop', 'cooked_beef', 'bread', 'cooked_chicken',
'cooked_cod', 'cooked_salmon',
])
self.assertEqual(items.FOOD_IDS, items.get_set({
'cooked_porkchop', 'cooked_beef', 'bread', 'cooked_chicken',
'cooked_cod', 'cooked_salmon',
}))
self.assertEqual(state.supplies, { self.assertEqual(state.supplies, {
tuple(items.BED_IDS): (1, 1), tuple(items.BED_IDS): (1, 1),
(items.CHEST_ID,): (1, 1), (items.CHEST_ID,): (1, 1),
@@ -44,19 +52,50 @@ class GroundSupplyTests(unittest.TestCase):
self.assertEqual(state.target_items, (items.CHEST_ID,)) self.assertEqual(state.target_items, (items.CHEST_ID,))
self.assertIs(state.state.__func__, state.find_ground_items.__func__) self.assertIs(state.state.__func__, state.find_ground_items.__func__)
def test_unreachable_barrel_ends_supply_task_without_trying_more(self): def test_supply_selection_uses_only_addressed_supply_chests(self):
position = (4, 64, 0)
chest_record = {
'position': position,
'positions': (position,),
'labels': {'mosfet supplies'},
'sign_positions': {(4, 64, 1)},
}
class FakeWorld: class FakeWorld:
@staticmethod @staticmethod
def path_to_place_faked(_start, _barrel): def find_supply_chests(_center, _bot_name, _distance):
return None return [chest_record]
@staticmethod
def find_blocks_indexed(*_args):
raise AssertionError('grab_supplies must not search barrels')
state = GrabSuppliesStates(Munch(
name='mosfet1', pos=(0.5, 64.0, 0.5), world=FakeWorld()))
state.find_supply_chests()
state.choose_supply_chest()
self.assertEqual(state.chest, position)
self.assertIs(state.state.__func__, state.path_to_supply_chest.__func__)
def test_unreachable_supply_chest_ends_task_without_searching_barrels(self):
class FakeWorld:
@staticmethod
def path_to_place_faked(_start, _chest):
return None
position = (10, 64, 10)
state = GrabSuppliesStates(Munch( state = GrabSuppliesStates(Munch(
pos=(0.5, 64.0, 0.5), world=FakeWorld(), chunks=Munch())) pos=(0.5, 64.0, 0.5), world=FakeWorld(), chunks=Munch()))
state.barrel = (10, 64, 10) state.supply_chests = [{
state.path_to_barrel() 'position': position, 'positions': (position,),
'labels': {'mosfet supplies'},
}]
state.choose_supply_chest()
state.path_to_supply_chest()
state.choose_supply_chest()
self.assertIs(state.state.__func__, state.cleanup.__func__) self.assertIs(state.state.__func__, state.cleanup.__func__)
self.assertEqual(state.bad_barrels, [(10, 64, 10)])
def test_job_walks_to_and_picks_up_dropped_supply_before_barrels(self): def test_job_walks_to_and_picks_up_dropped_supply_before_barrels(self):
axe_id = next(iter(items.AXE_IDS)) axe_id = next(iter(items.AXE_IDS))
+77
View File
@@ -0,0 +1,77 @@
import unittest
from munch import Munch
from mosfet.info import blocks, items
from mosfet.jobs.cache_items import CacheItemsStates
from mosfet.world import World
class LabeledStorageTests(unittest.TestCase):
def test_labels_resolve_to_item_categories(self):
seeds = items.storage_item_ids_for_label('Seeds')
wheat = items.storage_item_ids_for_label('Wheat')
self.assertEqual(seeds, items.get_set({
'beetroot', 'beetroot_seeds', 'carrot', 'melon_seeds',
'nether_wart', 'pitcher_pod', 'potato', 'pumpkin_seeds',
'torchflower_seeds', 'wheat_seeds',
}))
self.assertEqual(items.storage_item_ids_for_label('Dirt'), set())
self.assertEqual(items.storage_item_ids_for_label('Stone'), set())
self.assertIn(items.WHEAT_SEEDS_ID, seeds)
self.assertEqual(wheat, {items.WHEAT_ID})
self.assertNotIn(items.WHEAT_SEEDS_ID, wheat)
def test_sign_label_associates_with_adjacent_double_chest(self):
sign_pos = (0, 64, 0)
left_chest = (0, 64, 1)
right_chest = (1, 64, 1)
blocks_by_pos = {left_chest: 5552, right_chest: 5560}
chunks = Munch(
block_entities={sign_pos: {
'type': 7,
'data': {'front_text': {'messages': [
'Seeds', '', '', '',
]}},
}},
index={},
get_block_at=lambda *pos: blocks_by_pos.get(tuple(pos)),
)
chests = World(Munch(chunks=chunks)).find_labeled_chests(
sign_pos, 10)
self.assertEqual(len(chests), 1)
self.assertEqual(chests[0]['positions'], (left_chest, right_chest))
self.assertIn(items.WHEAT_SEEDS_ID, chests[0]['item_ids'])
self.assertEqual(chests[0]['labels'], {'Seeds'})
def test_wheat_chest_does_not_cache_seeds(self):
wheat = Munch(present=True, item_id=items.WHEAT_ID, item_count=64)
seeds = Munch(present=True, item_id=items.WHEAT_SEEDS_ID, item_count=64)
clicks = []
class FakeGame:
@staticmethod
def count_window_slots():
return 0
@staticmethod
def click_window(*args):
clicks.append(args)
cache = CacheItemsStates(Munch(
inv={9: wheat, 10: seeds}, item_lock=False,
window=Munch(container_slots=27, contents={
**dict.fromkeys(range(27)), 27: wheat, 28: seeds,
}),
game=FakeGame(),
))
cache.is_labeled_container = True
cache.container_item_ids = items.storage_item_ids_for_label('Wheat')
cache.move_items()
self.assertEqual(clicks, [(27, 0, 1, wheat)])
if __name__ == '__main__':
unittest.main()