Add ItemStackRef class and supporting elements to PC[Tile]EntityRef
EntityRefs now try to know their blockTypes. This mainly allows 1.7 worlds to return an item's internalName for its ID when accessed. With the caveat that an item created outside of a world and assigned a 1.7 ID will return a 1.7 ID until it is parented to a world. I can live with that.
This commit is contained in:
parent
66d68529ce
commit
62f4855b10
@ -4,13 +4,17 @@
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
import logging
|
||||
from mceditlib import nbt
|
||||
from mceditlib.blocktypes.itemtypes import ItemType
|
||||
from mceditlib.geometry import Vector
|
||||
from mceditlib import nbtattr
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
def PCEntityRef(rootTag, chunk=None):
|
||||
# xxx dispatch on rootTag["id"]
|
||||
return PCEntityRefBase(rootTag, chunk)
|
||||
|
||||
class PCEntityRef(object):
|
||||
class PCEntityRefBase(object):
|
||||
def __init__(self, rootTag, chunk=None):
|
||||
self.rootTag = rootTag
|
||||
self.chunk = chunk
|
||||
@ -34,12 +38,22 @@ class PCEntityRef(object):
|
||||
entity = newEntityClass(tag)
|
||||
entity.Position = self.Position + copyOffset
|
||||
|
||||
return PCEntityRef(tag, None)
|
||||
return self.__class__(tag)
|
||||
|
||||
def dirty(self):
|
||||
self.chunk.dirty = True
|
||||
|
||||
class PCTileEntityRef(object):
|
||||
@property
|
||||
def blockTypes(self):
|
||||
return self.chunk.blocktypes
|
||||
|
||||
def PCTileEntityRef(rootTag, chunk=None):
|
||||
id = rootTag["id"].value
|
||||
cls = _tileEntityClasses.get(id, PCTileEntityRefBase)
|
||||
return cls(rootTag, chunk)
|
||||
|
||||
|
||||
class PCTileEntityRefBase(object):
|
||||
def __init__(self, rootTag, chunk=None):
|
||||
self.rootTag = rootTag
|
||||
self.chunk = chunk
|
||||
@ -73,8 +87,209 @@ class PCTileEntityRef(object):
|
||||
tag["TileY"].value += copyOffset[1]
|
||||
tag["TileZ"].value += copyOffset[2]
|
||||
|
||||
return PCTileEntityRef(tag)
|
||||
|
||||
return self.__class__(tag)
|
||||
|
||||
def dirty(self):
|
||||
self.chunk.dirty = True
|
||||
|
||||
@property
|
||||
def blockTypes(self):
|
||||
return self.chunk.blocktypes
|
||||
|
||||
class ItemStackRef(nbtattr.NBTCompoundRef):
|
||||
def __init__(self, rootTag=None, parent=None):
|
||||
if rootTag is None:
|
||||
rootTag = nbt.TAG_Compound()
|
||||
nbtattr.SetNBTDefaults(self)
|
||||
super(ItemStackRef, self).__init__(rootTag, parent)
|
||||
|
||||
Damage = nbtattr.NBTAttr("Damage", nbt.TAG_Short, 0)
|
||||
Count = nbtattr.NBTAttr("Count", nbt.TAG_Byte, 1)
|
||||
Slot = nbtattr.NBTAttr("Slot", nbt.TAG_Byte)
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
if self.rootTag["id"].tagID == nbt.TAG_Short:
|
||||
if self.blockTypes is None:
|
||||
log.warn("No blocktypes available, returning id")
|
||||
return self.rootTag["id"].value
|
||||
try:
|
||||
itemType = self.blockTypes.itemTypes[self.rootTag["id"].value]
|
||||
return itemType.internalName
|
||||
except KeyError:
|
||||
log.warn("No ItemType defined for %s, returning id" % self.rootTag["id"].value)
|
||||
return self.rootTag["id"].value
|
||||
|
||||
return self.rootTag["id"].value
|
||||
|
||||
@id.setter
|
||||
def id(self, value):
|
||||
if isinstance(value, ItemType):
|
||||
self.rootTag["id"] = nbt.TAG_String(value.internalName)
|
||||
if value.meta is not None:
|
||||
self.Damage = value.meta
|
||||
elif isinstance(value, int):
|
||||
self.rootTag["id"] = nbt.TAG_Short(value)
|
||||
elif isinstance(value, basestring):
|
||||
self.rootTag["id"] = nbt.TAG_String(value)
|
||||
else:
|
||||
raise TypeError("Invalid type for ItemStackRef.id: %r", type(value))
|
||||
|
||||
self.dirty = True
|
||||
|
||||
@property
|
||||
def itemType(self):
|
||||
if self.blockTypes is None:
|
||||
raise ValueError("Cannot get itemType for this item. BlockTypes not set. ")
|
||||
try:
|
||||
itemType = self.blockTypes.itemTypes[self.rootTag["id"].value, self.Damage]
|
||||
return itemType
|
||||
except KeyError:
|
||||
raise ValueError("Cannot get itemType for this item. BlockTypes has no item for %s." % self.rootTag["id"].value)
|
||||
|
||||
@itemType.setter
|
||||
def itemType(self, value):
|
||||
if not isinstance(value, ItemType):
|
||||
raise TypeError("Expected ItemType, got %r", type(value))
|
||||
self.id = value
|
||||
|
||||
|
||||
@property
|
||||
def raw_id(self):
|
||||
return self.rootTag["id"].value
|
||||
|
||||
@raw_id.setter
|
||||
def raw_id(self, value):
|
||||
if isinstance(value, int):
|
||||
self.rootTag["id"] = nbt.TAG_Short(value)
|
||||
elif isinstance(value, basestring):
|
||||
self.rootTag["id"] = nbt.TAG_String(value)
|
||||
else:
|
||||
raise TypeError("Invalid type for ItemStack.id: %r", type(value))
|
||||
|
||||
self.dirty = True
|
||||
|
||||
@staticmethod
|
||||
def tagIsItemStack(tag):
|
||||
if tag.tagID != nbt.ID_COMPOUND:
|
||||
return False
|
||||
return "id" in tag and "Damage" in tag and "Count" in tag
|
||||
|
||||
|
||||
class SlotsListProxy(nbtattr.NBTListProxy):
|
||||
def putItemInSlot(self, item, slot):
|
||||
existing = [stack for stack in self if stack.Slot == slot]
|
||||
for stack in existing:
|
||||
self.remove(stack)
|
||||
|
||||
item.Slot = slot
|
||||
self.append(item)
|
||||
|
||||
def getItemInSlot(self, slot):
|
||||
for stack in self:
|
||||
if stack.Slot == slot:
|
||||
return stack
|
||||
|
||||
|
||||
class SlottedInventoryAttr(nbtattr.NBTCompoundListAttr):
|
||||
def __init__(self, name):
|
||||
super(SlottedInventoryAttr, self).__init__(name, ItemStackRef)
|
||||
self.listProxyClass = SlotsListProxy
|
||||
|
||||
|
||||
class PCTileEntityChestRef(PCTileEntityRefBase):
|
||||
Items = SlottedInventoryAttr("Items")
|
||||
|
||||
def __init__(self, rootTag, chunk=None):
|
||||
super(PCTileEntityChestRef, self).__init__(rootTag, chunk)
|
||||
|
||||
def putItemInSlot(self, item, slot):
|
||||
self.Items.putItemInSlot(item, slot)
|
||||
|
||||
def getItemInSlot(self, slot):
|
||||
return self.Items.getItemInSlot(slot)
|
||||
|
||||
_tileEntityClasses = {
|
||||
"Chest": PCTileEntityChestRef
|
||||
}
|
||||
"""
|
||||
|
||||
ItemStack usage:
|
||||
|
||||
Manipulating items in MCEdit is made easier by the ItemStack and ItemType classes.
|
||||
|
||||
An ItemStack is a reference to a single stack of items. It can either be located in a world, such as in a chest or as
|
||||
a dropped item entity or in the player's inventory, or it can be unconnected to any world. For unconnected items,
|
||||
which are always those created directly rather than loaded from a world, the `itemType` pseudo-attribute is
|
||||
unavailable and the item ID may be returned numerically in some cases.
|
||||
|
||||
An ItemStack has four attributes: `ID`, `Damage`, `Count` and `tag`.
|
||||
|
||||
`ID`
|
||||
|
||||
`ID` accepts either strings, numeric values, or ItemType objects; when the world data is saved, the `ID` is converted
|
||||
to the correct format. If the ItemType's damage value forms part of the item's identity, the ItemStack's Damage is
|
||||
also set. If the item is unconnected and the item is in 1.7 format or has previously been given a numeric ID, its
|
||||
numeric ID will be returned. To ensure that `ID` always returns a string, either connect the world by adding it to
|
||||
a container that is itself connected to the world, or always assign the item ID as a string.
|
||||
|
||||
`Damage`
|
||||
|
||||
For some item types such as wool, dye, and stained clay, the `Damage` value forms part of its identity. For other items
|
||||
such as tools and fishing rods, `Damage` is the actual amount of damage the item has sustained - an undamaged item has
|
||||
a value of zero.
|
||||
|
||||
`Count`
|
||||
|
||||
The number of items in this stack. For each item type, Minecraft has a maximum number of items that can be stacked
|
||||
(usually 64, 16, or 1). There is no protection against creating stacks larger than Minecraft's limit, and large stacks
|
||||
are actually usable in-game; you simply cannot re-stack them that large while playing.
|
||||
|
||||
Another pseudo-attribute `itemType` is also provided. Accessing this attribute always returns an ItemType object from
|
||||
the ItemStack's parent world's ItemTypeSet. If no ItemType is found or the ItemStack is not parented to a world,
|
||||
ValueError is raised.
|
||||
|
||||
An ItemStack sometimes has an attribute `Slot` which is only used when the item is contained by an ordered inventory,
|
||||
such as a chest or a player's inventory. The `Slot` value is generally managed by the object containing the
|
||||
ItemStacks; assigning the same `Slot` value to two ItemStacks in the same container has undefined behavior.
|
||||
|
||||
|
||||
A newly created ItemStack has Damage=0 and Count=1.
|
||||
|
||||
An ItemType is a value type that corresponds one-to-one with a numeric item ID (for Minecraft 1.7)
|
||||
and a textual internalName (for Forge 1.7 and Minecraft 1.8). Each ItemType uniquely identifies an item
|
||||
and can be used to find the item's icon, localized name, maximum stack size, and whether the item's
|
||||
damage value is treated as an ID number (such as for colored wool and clay) or is treated as an actual
|
||||
amount of damage the item has suffered. Since the id<->internalName mapping is defined in the world file for
|
||||
Forge 1.7, ItemTypes are specific to a world.
|
||||
|
||||
For items which use the damage value as an ID number, ItemTypes for each unique damage value are present
|
||||
in the world's ItemTypeSet. You can use the set to look up an ItemType and get its display name and other attributes.
|
||||
|
||||
Examples:
|
||||
|
||||
Creating a new chest, then creating a new item stack and adding it to the chest:
|
||||
|
||||
world = load_my_world() # defined elsewhere
|
||||
|
||||
chest = PCTileEntityChestRef()
|
||||
|
||||
diamondAxe = ItemStackRef()
|
||||
diamondAxe.id = "diamond_axe"
|
||||
|
||||
chest.putItemInSlot(diamondAxe, 0)
|
||||
|
||||
chest.Position = (10, 64, 0)
|
||||
|
||||
world.setBlock(10, 64, 0, "chest[facing=north]")
|
||||
world.addTileEntity(chest)
|
||||
|
||||
Searching a chest for iron tools and upgrading them to diamond:
|
||||
|
||||
chest = find_my_chest() # defined elsewhere
|
||||
|
||||
for stack in chest.Items:
|
||||
if stack.id in ("iron_pickaxe", "iron_axe", "iron_sword", "iron_shovel", "iron_hoe"):
|
||||
stack.id = stack.id.replace("iron", "diamond")
|
||||
|
||||
"""
|
||||
|
Reference in New Issue
Block a user