Finish executing the villager trade

This commit is contained in:
Tanner Collin 2021-02-24 01:02:00 +00:00
parent 1d8d473e26
commit 28ca442a46
5 changed files with 120 additions and 10 deletions

19
game.py
View File

@ -20,7 +20,8 @@ from protocol.packets import (
ClickWindowPacket, CloseWindowPacket, ServerWindowConfirmationPacket, ClickWindowPacket, CloseWindowPacket, ServerWindowConfirmationPacket,
ClientWindowConfirmationPacket, EntityMetadataPacket, ClientWindowConfirmationPacket, EntityMetadataPacket,
SpawnLivingEntityPacket, EntityPositionRotationPacket, DestroyEntitiesPacket, SpawnLivingEntityPacket, EntityPositionRotationPacket, DestroyEntitiesPacket,
EntityActionPacket, EntityTeleport, InteractEntityPacket, TradeListPacket EntityActionPacket, EntityTeleport, InteractEntityPacket, TradeListPacket,
SelectTradePacket,
) )
from protocol.types import Slot from protocol.types import Slot
@ -789,6 +790,7 @@ class Game:
if command == 'close': if command == 'close':
if self.g.window: if self.g.window:
self.close_window() self.close_window()
reply = 'ok'
else: else:
reply = 'nothing open' reply = 'nothing open'
@ -930,6 +932,16 @@ class Game:
count += item.item_count count += item.item_count
return count return count
def get_window_slot(self, item_id):
# get the first slot that matches item of a window
window_items = list(self.g.window.contents.items())
for slot, item in window_items:
if not item.present: continue
if item.item_id == item_id:
return slot, item
else: #for
return False, False
def select_item(self, items): def select_item(self, items):
# select the first match from items of inv # select the first match from items of inv
# uses smallest stack of that match # uses smallest stack of that match
@ -1186,6 +1198,11 @@ class Game:
print(packet) print(packet)
self.g.trades = packet.trades self.g.trades = packet.trades
def select_trade(self, num):
packet = SelectTradePacket()
packet.selected_slot = num
self.g.connection.write_packet(packet)
def tick(self): def tick(self):
if self.g.breaking: if self.g.breaking:
self.animate() self.animate()

View File

@ -108,5 +108,7 @@ POTATO_ID = get_id('potato')
WHEAT_SEEDS_ID = get_id('wheat_seeds') WHEAT_SEEDS_ID = get_id('wheat_seeds')
BEETROOT_SEEDS_ID = get_id('beetroot_seeds') BEETROOT_SEEDS_ID = get_id('beetroot_seeds')
EMERALD_ID = get_id('emerald')
NEEDED_ITEMS = BED_IDS | SHOVEL_IDS | AXE_IDS | FOOD_IDS | set([CHEST_ID]) NEEDED_ITEMS = BED_IDS | SHOVEL_IDS | AXE_IDS | FOOD_IDS | set([CHEST_ID])
WANTED_ITEMS = SAPLING_IDS | set([NETHERWART_ID, CARROT_ID, POTATO_ID, WHEAT_SEEDS_ID, BEETROOT_SEEDS_ID]) WANTED_ITEMS = SAPLING_IDS | set([NETHERWART_ID, CARROT_ID, POTATO_ID, WHEAT_SEEDS_ID, BEETROOT_SEEDS_ID])

91
jobs.py
View File

@ -1777,6 +1777,7 @@ class SellToVillagerStates:
return None return None
def init(self): def init(self):
self.trade = None
self.state = self.find_villager self.state = self.find_villager
def find_villager(self): def find_villager(self):
@ -1784,17 +1785,18 @@ class SellToVillagerStates:
w = self.g.world w = self.g.world
p = utils.pint(self.g.pos) p = utils.pint(self.g.pos)
for villager in w.find_villagers(p, 100): for v in w.find_villagers(p, 100):
print('Found villager:', villager) print('Found villager:', v)
if villager not in self.bad_villagers: if v not in self.bad_villagers and v not in self.spent_villagers:
break break
else: # for else: # for
print('No good villagers left, aborting.') print('No good villagers left, aborting.')
self.spent_villagers = []
self.state = self.cleanup self.state = self.cleanup
return return
self.villager = villager self.villager = v
self.villager_pos = utils.pint((villager.x, villager.y, villager.z)) self.villager_pos = utils.pint((v.x, v.y, v.z))
self.state = self.find_openings self.state = self.find_openings
def find_openings(self): def find_openings(self):
@ -1841,14 +1843,83 @@ class SellToVillagerStates:
print('Interacting with villager') print('Interacting with villager')
self.g.game.interact(self.villager.entity_id) self.g.game.interact(self.villager.entity_id)
self.g.game.animate() self.g.game.animate()
self.state = self.select_trade self.state = self.choose_trade
def choose_trade(self):
g = self.g.game
def select_trade(self):
if not self.g.window or not self.g.trades: if not self.g.window or not self.g.trades:
return return
print('Got trades window') for trade_num, trade in enumerate(self.g.trades):
self.state = self.freeze in_id = trade.input_item_1.item_id
in_num = trade.input_item_1.item_count
out_id = trade.output_item.item_id
if g.count_items([in_id]) < in_num:
continue
if trade.trade_disabled:
continue
if out_id != items.EMERALD_ID:
continue
print('Found trade: #', trade_num, trade)
self.trade = trade
self.trade_num = trade_num
self.state = self.click_trade
break
else:
print('Villager has been spent, aborting')
self.spent_villagers.append(self.villager)
self.state = self.cleanup
return
def click_trade(self):
self.g.item_lock = True
self.g.game.select_trade(self.trade_num)
self.state = self.execute_trade
def execute_trade(self):
if self.g.item_lock:
return
w = self.g.window
w_info = mcdata.WINDOWS[w.data.window_type]
slot_num = w_info.output
slot = w.contents[slot_num]
self.g.item_lock = True
self.g.game.click_window(slot_num, 0, 1, slot)
self.g.game.close_window()
self.state = self.end_trade
def end_trade(self):
if self.g.item_lock:
return
print('Trade completed.')
self.state = self.interact_villager
#def grab_stack(self):
# g = self.g.game
# in_id = self.trade.input_item_1.item_id
# slot_num, slot = g.get_window_slot(in_id)
# if not slot:
# print('Item not found, aborting')
# self.state = self.cleanup
# return
# print('Grabbing:', slot)
# self.g.item_lock = True
# # double click stack to grab max
# self.g.game.click_window(slot_num, 0, 6, slot)
# self.state = self.freeze
def freeze(self): def freeze(self):
return return
@ -1874,7 +1945,9 @@ class SellToVillagerStates:
self.villager_pos = None self.villager_pos = None
self.bad_villagers = [] self.bad_villagers = []
self.good_villagers = [] self.good_villagers = []
self.spent_villagers = []
self.openings = [] self.openings = []
self.trade = None
self.wait_time = 0 self.wait_time = 0
def run(self): def run(self):

View File

@ -10,6 +10,7 @@ with open('minecraft_data/registries.json') as f:
SINGLE_CHEST = 2 SINGLE_CHEST = 2
DOUBLE_CHEST = 5 DOUBLE_CHEST = 5
VILLAGER_TRADE = 18
WINDOWS = { WINDOWS = {
SINGLE_CHEST: Munch( SINGLE_CHEST: Munch(
@ -22,4 +23,10 @@ WINDOWS = {
inventory=list(range(54, 90)), inventory=list(range(54, 90)),
slot_diff=45, slot_diff=45,
), ),
VILLAGER_TRADE: Munch(
input1=0,
input2=1,
output=2,
inventory=list(range(3, 38)),
),
} }

View File

@ -436,3 +436,14 @@ class TradeListPacket(Packet):
self.experience = VarInt.read(file_object) self.experience = VarInt.read(file_object)
self.is_regular_villager = Boolean.read(file_object) self.is_regular_villager = Boolean.read(file_object)
self.can_restock = Boolean.read(file_object) self.can_restock = Boolean.read(file_object)
class SelectTradePacket(Packet):
# Sent when a player selects a specific trade offered by a villager
# https://wiki.vg/Protocol#Select_Trade
id = 0x23
packet_name = 'select trade entity'
definition = [
{'selected_slot': VarInt},
]