from bravo.beta.packets import make_packet
from bravo.beta.structures import Slot
from bravo.inventory import SerializableSlots
from bravo.policy.recipes.ingredients import all_ingredients
from bravo.policy.recipes.blueprints import all_blueprints
all_recipes = all_ingredients + all_blueprints
# XXX I am completely undocumented and untested; is this any way to go through
# life? Test and document me!
class comblist(object):
def __init__(self, a, b):
self.l = a, b
self.offset = len(a)
self.length = sum(map(len,self.l))
def __len__(self):
return self.length
def __getitem__(self, key):
if key < self.offset:
return self.l[0][key]
key -= self.offset
if key < self.length:
return self.l[1][key]
raise IndexError
def __setitem__(self, key, value):
if key < 0:
raise IndexError
if key < self.offset:
self.l[0][key] = value
return
key -= self.offset
if key < self.length:
self.l[1][key] = value
return
raise IndexError
[docs]class SlotsSet(SerializableSlots):
'''
Base calss for different slot configurations except player's inventory
'''
crafting = 0 # crafting slots (inventory, workbench, furnace)
fuel = 0 # furnace
storage = 0 # chest
crafting_stride = 0
def __init__(self):
if self.crafting:
self.crafting = [None] * self.crafting
self.crafted = [None]
else:
self.crafting = self.crafted = []
if self.fuel:
self.fuel = [None]
else:
self.fuel = []
if self.storage:
self.storage = [None] * self.storage
else:
self.storage = []
self.dummy = [None] * 36 # represents gap in serialized structure:
# storage (27) + holdables(9) from player's
# inventory (notchian)
@property
def metalist(self):
return [self.crafted, self.crafting, self.fuel, self.storage, self.dummy]
def update_crafted(self):
# override me
pass
def close(self, wid):
# override me, see description in Crafting
return [], ""
[docs]class Crafting(SlotsSet):
'''
Base crafting class. Never shall be instantiated directly.
'''
crafting = 4
crafting_stride = 2
def __init__(self):
SlotsSet.__init__(self)
self.recipe = None
def update_crafted(self):
self.check_recipes()
if self.recipe is None:
self.crafted[0] = None
else:
provides = self.recipe.provides
self.crafted[0] = Slot(provides[0][0], provides[0][1], provides[1])
[docs] def select_crafted(self, index, alternate, shift, selected = None):
"""
Handle a slot selection on a crafted output.
:param index: index of the selection
:param alternate: whether this was an alternate selection
:param shift: whether this was a shifted selection
:param selected: the current selection
:returns: a tuple of a bool indicating whether the selection was
valid, and the newly selected slot
"""
if self.recipe and self.crafted[0]:
if selected is None:
selected = self.crafted[0]
self.crafted[0] = None
else:
sslot = selected
if sslot.holds(self.recipe.provides[0]):
selected = sslot.increment(self.recipe.provides[1])
else:
# Mismatch; don't allow it.
return (False, selected)
self.reduce_recipe()
self.update_crafted()
return (True, selected)
else:
# Forbid placing things in the crafted slot.
return (False, selected)
[docs] def check_recipes(self):
"""
See if the crafting table matches any recipes.
:returns: None
"""
self.recipe = None
for recipe in all_recipes:
if recipe.matches(self.crafting, self.crafting_stride):
self.recipe = recipe
[docs] def reduce_recipe(self):
"""
Reduce a crafting table according to a recipe.
This function returns None; the crafting table is modified in-place.
This function assumes that the recipe already fits the crafting table
and will not do additional checks to verify this assumption.
"""
self.recipe.reduce(self.crafting, self.crafting_stride)
[docs] def close(self, wid):
'''
Clear crafting areas and return items to drop and packets to send to client
'''
items = []
packets = ""
# process crafting area
for i, itm in enumerate(self.crafting):
if itm is not None:
items.append(itm)
self.crafting[i] = None
packets += make_packet("window-slot", wid=wid,
slot=i+1, primary=-1)
self.crafted[0] = None
return items, packets
class Workbench(Crafting):
crafting = 9
crafting_stride = 3
title = "Workbench"
identifier = "workbench"
slots_num = 9
class ChestStorage(SlotsSet):
storage = 27
identifier = "chest"
title = "Chest"
slots_num = 27
def __init__(self):
SlotsSet.__init__(self)
self.title = "Chest"
[docs]class LargeChestStorage(SlotsSet):
"""
LargeChest is a wrapper around 2 ChestStorages
"""
identifier = "chest"
title = "LargeChest"
slots_num = 54
def __init__(self, chest1, chest2):
SlotsSet.__init__(self)
# NOTE: chest1 and chest2 are ChestStorage.storages
self.storage = comblist(chest1, chest2)
@property
def metalist(self):
return [self.storage]
class FurnaceStorage(SlotsSet):
#TODO: Make sure notchian furnace have following slots order:
# 0 - crafted, 1 - crafting, 2 - fuel
# Override SlotsSet.metalist() property if not.
crafting = 1
fuel = 1
title = "Furnace"
identifier = "furnace"
slots_num = 3
def select_crafted(self, index, alternate, shift, selected = None):
"""
Handle a slot selection on a crafted output.
Returns: ( True/False, new selection )
"""
if self.crafted[0]:
if selected is None:
selected = self.crafted[0]
self.crafted[0] = None
else:
sslot = selected
if sslot.holds(self.crafted[0]):
selected = sslot.increment(self.crafted[0].quantity)
self.crafted[0] = None
else:
# Mismatch; don't allow it.
return (False, selected)
return (True, selected)
else:
# Forbid placing things in the crafted slot.
return (False, selected)
#@property
#def metalist(self):
# return [self.crafting, self.fuel, self.crafted]