Refactored a few things out of the trading upgrade into InventoryUtils, making things a little more consistent, added manual entry for trade upgrade, tweaked the texture for the trade upgrade a bit.

This commit is contained in:
Florian Nücke 2016-01-04 18:41:40 +01:00
parent 7af4b41b1e
commit 58de9015c3
14 changed files with 229 additions and 273 deletions

Binary file not shown.

View File

@ -50,6 +50,7 @@ Keep in mind that some of these may not be available, depending on the recipe se
* [Tank Controller](tankControllerUpgrade.md)
* [Tank Upgrade](tankUpgrade.md)
* [Tractor Beam Upgrade](tractorBeamUpgrade.md)
* [Trading Upgrade](tradingUpgrade.md)
* [Upgrade Container](upgradeContainer1.md)
### Other

View File

@ -0,0 +1,5 @@
# Trading Upgrade
![Equivalent Exchange](oredict:oc:tradingUpgrade)
The trading upgrade enables automatic trading with merchants, such as villagers. It can be used in agents such as [robots](../block/robot.md) and [drones](drone.md) and provides capabilities to identify available trades from nearby merchants, retrieve information on individual trade offers, and finally for performing trades.

View File

@ -335,6 +335,11 @@ tractorBeamUpgrade {
[ingotIron, "oc:capacitor", ingotIron]
[ingotGold, "oc:circuitChip3", ingotGold]]
}
tradingUpgrade {
input: [[ingotGold, chest, ingotGold]
[emerald, "oc:circuitChip2", emerald]
[dropper, "oc:materialCircuitBoardPrinted", craftingPiston]]
}
cardContainer1 {
input: [[ingotIron, "oc:circuitChip1", ingotIron]
@ -367,12 +372,6 @@ upgradeContainer3 {
[ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]]
}
upgradeTrading {
input: [[ingotGold, chest, ingotGold]
[emerald, "oc:circuitChip2", emerald]
[dropper, "oc:materialCircuitBoardPrinted", craftingPiston]]
}
# Note: iron ingot and nugget recipes are *only* registered if no other mod
# already provides the same functionality.
nuggetIron {

View File

@ -181,8 +181,7 @@ solarGeneratorUpgrade {
input: [[blockGlass, blockGlass, blockGlass]
["oc:circuitChip3", "oc:generatorUpgrade", "oc:circuitChip3"]]
}
upgradeTrading {
tradingUpgrade {
input: [["oc:circuitChip2", chest, "oc:circuitChip2"]
[emerald, "oc:circuitChip2", emerald]
[dropper, "oc:materialCircuitBoardPrinted", craftingPiston]]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 739 B

View File

@ -154,7 +154,7 @@ object Constants {
final val TerminalServer = "terminalServer"
final val TexturePicker = "texturePicker"
final val TractorBeamUpgrade = "tractorBeamUpgrade"
final val TradingUpgrade = "upgradeTrading"
final val TradingUpgrade = "tradingUpgrade"
final val Transistor = "transistor"
final val UpgradeContainerTier1 = "upgradeContainer1"
final val UpgradeContainerTier2 = "upgradeContainer2"

View File

@ -532,8 +532,7 @@ object Items extends ItemAPI {
// 1.6.0
Recipes.addSubItem(new item.TerminalServer(multi), Constants.ItemName.TerminalServer, "oc:terminalServer")
Recipes.addSubItem(new item.DiskDriveMountable(multi), Constants.ItemName.DiskDriveMountable, "oc:diskDriveMountable")
Recipes.addSubItem(new item.UpgradeTrading(multi), Constants.ItemName.TradingUpgrade, "oc:upgradeTrading")
Recipes.addSubItem(new item.UpgradeTrading(multi), Constants.ItemName.TradingUpgrade, "oc:tradingUpgrade")
// Register aliases.
for ((k, v) <- aliases) {

View File

@ -1,16 +1,15 @@
package li.cil.oc.integration.opencomputers
import li.cil.oc.api
import li.cil.oc.Constants
import li.cil.oc.api
import li.cil.oc.api.driver.EnvironmentProvider
import li.cil.oc.api.network.EnvironmentHost
import li.cil.oc.api.driver.item.HostAware
import li.cil.oc.common.{Tier, Slot}
import li.cil.oc.api.network.EnvironmentHost
import li.cil.oc.common.Slot
import li.cil.oc.common.Tier
import li.cil.oc.server.component
import li.cil.oc.server.component.UpgradeTrading
import net.minecraft.item.ItemStack
import li.cil.oc.common.entity.Drone
import li.cil.oc.common.tileentity.Robot
import li.cil.oc.server.component
object DriverUpgradeTrading extends Item with HostAware {
override def worksWith(stack: ItemStack) = isOneOf(stack,
@ -18,11 +17,7 @@ object DriverUpgradeTrading extends Item with HostAware {
override def createEnvironment(stack: ItemStack, host: EnvironmentHost) =
if (host.world.isRemote) null
else host match {
case host: EnvironmentHost with Robot => new UpgradeTrading(host)
case host: EnvironmentHost with Drone => new UpgradeTrading(host)
case _ => null
}
else new UpgradeTrading(host)
override def slot(stack: ItemStack) = Slot.Upgrade
@ -34,4 +29,5 @@ object DriverUpgradeTrading extends Item with HostAware {
classOf[component.UpgradeTrading]
else null
}
}

View File

@ -4,253 +4,192 @@ import java.util.UUID
import li.cil.oc.Settings
import li.cil.oc.api.machine._
import li.cil.oc.api.network.EnvironmentHost
import li.cil.oc.api.prefab.AbstractValue
import li.cil.oc.common.EventHandler
import li.cil.oc.util.InventoryUtils
import net.minecraft.entity.passive.EntityVillager
import net.minecraft.entity.Entity
import net.minecraft.entity.IMerchant
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
import net.minecraft.tileentity.TileEntity
import net.minecraft.village.MerchantRecipe
import net.minecraftforge.common.DimensionManager
import net.minecraftforge.common.util.ForgeDirection
import ref.WeakReference
import li.cil.oc.api.network.EnvironmentHost
import scala.collection.JavaConverters._
class Trade(info: TradeInfo) extends AbstractValue {
import scala.collection.convert.WrapAsScala._
import scala.ref.WeakReference
class Trade(val info: TradeInfo) extends AbstractValue {
def this() = this(new TradeInfo())
def this(upgr: UpgradeTrading, villager: EntityVillager, recipeID: Int) = {
this(new TradeInfo(upgr.host, villager, recipeID))
}
def this(upgrade: UpgradeTrading, merchant: IMerchant, recipeID: Int) =
this(new TradeInfo(upgrade.host, merchant, recipeID))
override def load(nbt: NBTTagCompound) = {
EventHandler.scheduleServer(() => {
//Tell the info to load from NBT, behind EventHandler because when load is called we can't access the world yet
//and we need to access it to get the Robot/Drone TileEntity/Entity
info.load(nbt)
})
}
def maxRange = Settings.get.tradingRange
override def save(nbt: NBTTagCompound) = {
//Tell info to save to nbt
info.save(nbt)
}
def inventory = info.inventory
@Callback(doc="function():table, table -- returns the items the villager wants for this trade")
def getInput(context: Context, arguments: Arguments): Array[AnyRef] = {
Array(info.recipe.getItemToBuy.copy(),
info.recipe.hasSecondItemToBuy match {
case true => info.recipe.getSecondItemToBuy.copy()
case false => null
})
}
@Callback(doc = "function():table -- returns the item the villager offers for this trade")
def getOutput(context: Context, arguments: Arguments): Array[AnyRef] = {
Array(info.recipe.getItemToSell.copy())
}
@Callback(doc="function():boolean -- returns whether the villager currently wants to trade this")
def isEnabled(context: Context, arguments: Arguments): Array[AnyRef] = {
Array(info.villager.exists((villager: EntityVillager) => // Make sure villager is neither dead/gone nor the recipe
!info.recipe.isRecipeDisabled // has been disabled
).asInstanceOf[AnyRef])
}
val maxRange = Settings.get.tradingRange
def inRange = info.villager.isDefined && distance.exists((distance: Double) => distance < maxRange)
def distance = info.villager match {
case Some(villager: EntityVillager) =>
info.host match {
case Some(h: EnvironmentHost) => Some(Math.sqrt(Math.pow(villager.posX - h.xPosition, 2) + Math.pow(villager.posY - h.yPosition, 2) + Math.pow(villager.posZ - h.zPosition, 2)))
case _ => None
}
case None => None
}
@Callback(doc="function():boolean, string -- returns true when trade succeeds and nil, error when not")
def trade(context: Context, arguments: Arguments): Array[AnyRef] = {
//Make sure we can access an inventory
val inventory = info.inventory match {
case Some(i) => i
case None => return result(false, "trading requires an inventory upgrade to be installed")
}
//Make sure villager hasn't died, it somehow gone or moved out of range
if (info.villager.isEmpty)
return result(false, "trade has become invalid")
else if (!info.villager.get.isEntityAlive)
return result(false, "trader died")
if (!inRange) {
return result(false, "out of range")
}
//Make sure villager wants to trade this
if (info.recipe.isRecipeDisabled)
return result(false, "recipe is disabled")
//Now we'll check if we have enough items to perform the trade, caching first
val firstItem = info.recipe.getItemToBuy
val secondItem = info.recipe.hasSecondItemToBuy match {
case true => Some(info.recipe.getSecondItemToBuy)
case false => None
}
//Check if we have enough of the first item
var extracting: Int = firstItem.stackSize
for (slot <- 0 until inventory.getSizeInventory) {
val stack = inventory.getStackInSlot(slot)
if (stack != null && stack.isItemEqual(firstItem) && extracting > 0)
//Takes the stack in the slot, extracts up to limit and calls the function in the first argument
//We don't actually consume anything, we just count that we have extracted as much as we need
InventoryUtils.extractFromInventorySlot((stack: ItemStack) => extracting -= stack.stackSize, inventory, ForgeDirection.UNKNOWN, slot, extracting)
}
//If we had enough, the left-over amount will be 0
if (extracting != 0)
return result(false, "not enough items to trade")
//Do the same with the second item if there is one
if (secondItem.isDefined) {
extracting = secondItem.orNull.stackSize
for (slot <- 0 until inventory.getSizeInventory) {
val stack = inventory.getStackInSlot(slot)
if (stack != null && stack.isItemEqual(secondItem.orNull) && extracting > 0)
InventoryUtils.extractFromInventorySlot((stack: ItemStack) => extracting -= stack.stackSize, inventory, ForgeDirection.UNKNOWN, slot, extracting)
}
if (extracting != 0)
return result(false, "not enough items to trade")
}
//Now we need to check if we have enough inventory space to accept the item we get for the trade
val outputItemSim = info.recipe.getItemToSell.copy()
InventoryUtils.insertIntoInventory(outputItemSim, inventory, None, 64, simulate = true)
if (outputItemSim.stackSize != 0)
return result(false, "not enough inventory space to trade")
//We established that out inventory allows to perform the trade, now actaully do the trade
extracting = firstItem.stackSize
for (slot <- 0 until inventory.getSizeInventory) {
if (extracting != 0) {
val stack = inventory.getStackInSlot(slot)
if (stack != null && stack.isItemEqual(firstItem))
//Pretty much the same as earlier (but counting down, and not up now)
//but this time we actually consume the stack we get
InventoryUtils.extractFromInventorySlot((stack: ItemStack) => {
extracting -= stack.stackSize
stack.stackSize = 0
}, inventory, ForgeDirection.UNKNOWN, slot, extracting)
}
}
//Do the same for the second item
if (secondItem.isDefined) {
extracting = secondItem.orNull.stackSize
for (slot <- 0 until inventory.getSizeInventory) {
if (extracting != 0) {
val stack = inventory.getStackInSlot(slot)
if (stack != null && stack.isItemEqual(secondItem.orNull))
InventoryUtils.extractFromInventorySlot((stack: ItemStack) => {
extracting -= stack.stackSize
stack.stackSize = 0
}, inventory, ForgeDirection.UNKNOWN, slot, extracting)
}
}
}
//Now put our output item into the inventory
val outputItem = info.recipe.getItemToSell.copy()
while (outputItem.stackSize != 0)
InventoryUtils.insertIntoInventory(outputItem, inventory, None, outputItem.stackSize)
//Tell the villager we used the recipe, so MC can disable it and/or enable more recipes
info.villager.orNull.useRecipe(info.recipe)
result(true)
}
}
class TradeInfo() {
def this(host: EnvironmentHost, villager: EntityVillager, recipeID: Int) = {
this()
_vilRef = new WeakReference[EntityVillager](villager)
_recipeID = recipeID
this.host = host
}
def getEntityByUUID(dimID: Int, uuid: UUID) = DimensionManager.getProvider(dimID).worldObj.getLoadedEntityList.asScala.find {
case entAny: net.minecraft.entity.Entity if entAny.getPersistentID == uuid => true
def isInRange = (info.merchant.get, info.host) match {
case (Some(merchant: Entity), Some(host)) => merchant.getDistanceSq(host.xPosition, host.yPosition, host.zPosition) < maxRange * maxRange
case _ => false
}
def getTileEntity(dimID: Int, posX: Int, posY: Int, posZ: Int) = Option(DimensionManager.getProvider(dimID).worldObj.getTileEntity(posX, posY, posZ) match {
case robot : li.cil.oc.common.tileentity.Robot => robot
case robotProxy : li.cil.oc.common.tileentity.RobotProxy => robotProxy.robot
case null => None
})
// Queue the load because when load is called we can't access the world yet
// and we need to access it to get the Robot's TileEntity / Drone's Entity.
override def load(nbt: NBTTagCompound) = EventHandler.scheduleServer(() => info.load(nbt))
override def save(nbt: NBTTagCompound) = info.save(nbt)
@Callback(doc = "function():table, table -- Returns the items the merchant wants for this trade.")
def getInput(context: Context, arguments: Arguments): Array[AnyRef] =
result(info.recipe.map(_.getItemToBuy.copy()).orNull,
if (info.recipe.exists(_.hasSecondItemToBuy)) info.recipe.map(_.getSecondItemToBuy.copy()).orNull else null)
@Callback(doc = "function():table -- Returns the item the merchant offers for this trade.")
def getOutput(context: Context, arguments: Arguments): Array[AnyRef] =
result(info.recipe.map(_.getItemToSell.copy()).orNull)
@Callback(doc = "function():boolean -- Returns whether the merchant currently wants to trade this.")
def isEnabled(context: Context, arguments: Arguments): Array[AnyRef] =
result(info.merchant.get.exists(merchant => !info.recipe.exists(_.isRecipeDisabled))) // Make sure merchant is neither dead/gone nor the recipe has been disabled.
@Callback(doc = "function():boolean, string -- Returns true when trade succeeds and nil, error when not.")
def trade(context: Context, arguments: Arguments): Array[AnyRef] = {
// Make sure we can access an inventory.
info.inventory match {
case Some(inventory) =>
// Make sure merchant hasn't died, it somehow gone or moved out of range and still wants to trade this.
info.merchant.get match {
case Some(merchant: Entity) if merchant.isEntityAlive && isInRange =>
if (!merchant.isEntityAlive) {
result(false, "trader died")
} else if (!isInRange) {
result(false, "out of range")
} else {
info.recipe match {
case Some(recipe) =>
if (recipe.isRecipeDisabled) {
result(false, "trade is disabled")
} else {
// Now we'll check if we have enough items to perform the trade, caching first
val firstInputStack = recipe.getItemToBuy
val secondInputStack = if (recipe.hasSecondItemToBuy) Option(recipe.getSecondItemToBuy) else None
def containsAccumulativeItemStack(stack: ItemStack) =
InventoryUtils.extractFromInventory(stack, inventory, ForgeDirection.UNKNOWN, simulate = true).stackSize == 0
def hasRoomForItemStack(stack: ItemStack) = {
val remainder = stack.copy()
InventoryUtils.insertIntoInventory(remainder, inventory, None, remainder.stackSize, simulate = true)
remainder.stackSize == 0
}
// Check if we have enough to perform the trade.
if (containsAccumulativeItemStack(firstInputStack) && secondInputStack.forall(containsAccumulativeItemStack)) {
// Now we need to check if we have enough inventory space to accept the item we get for the trade.
val outputStack = recipe.getItemToSell.copy()
if (hasRoomForItemStack(outputStack)) {
// We established that out inventory allows to perform the trade, now actually do the trade.
InventoryUtils.extractFromInventory(firstInputStack, inventory, ForgeDirection.UNKNOWN)
secondInputStack.map(InventoryUtils.extractFromInventory(_, inventory, ForgeDirection.UNKNOWN))
InventoryUtils.insertIntoInventory(outputStack, inventory, None, outputStack.stackSize)
// Tell the merchant we used the recipe, so MC can disable it and/or enable more recipes.
info.merchant.get.orNull.useRecipe(recipe)
result(true)
} else {
result(false, "not enough inventory space to trade")
}
} else {
result(false, "not enough items to trade")
}
}
case _ => result(false, "trade has become invalid")
}
}
case _ => result(false, "trade has become invalid")
}
case _ => result(false, "trading requires an inventory upgrade to be installed")
}
}
}
class TradeInfo(var host: Option[EnvironmentHost], var merchant: WeakReference[IMerchant], var recipeID: Int) {
def this() = this(None, new WeakReference[IMerchant](null), -1)
def this(host: EnvironmentHost, merchant: IMerchant, recipeID: Int) =
this(Option(host), new WeakReference[IMerchant](merchant), recipeID)
def recipe = merchant.get.map(_.getRecipes(null).get(recipeID).asInstanceOf[MerchantRecipe])
def inventory = host match {
case Some(agent: li.cil.oc.api.internal.Agent) => Option(agent.mainInventory())
case _ => None
}
def load(nbt: NBTTagCompound): Unit = {
val dimID = nbt.getInteger("dimensionID")
val _hostIsDrone = nbt.getBoolean("hostIsDrone")
//If drone we find it again by its UUID, if Robot we know the X/Y/Z of the TileEntity
host = Option(_hostIsDrone match {
case true => getEntityByUUID(dimID, UUID.fromString(nbt.getString("hostUUID"))).orNull.asInstanceOf[EnvironmentHost]
case false => getTileEntity(
dimID,
nbt.getInteger("hostX"),
nbt.getInteger("hostY"),
nbt.getInteger("hostZ")
).orNull.asInstanceOf[EnvironmentHost]
val isEntity = nbt.getBoolean("hostIsEntity")
// If drone we find it again by its UUID, if Robot we know the X/Y/Z of the TileEntity.
host = if (isEntity) loadHostEntity(nbt) else loadHostTileEntity(nbt)
merchant = new WeakReference[IMerchant](loadEntity(nbt, new UUID(nbt.getLong("merchantUUIDMost"), nbt.getLong("merchantUUIDLeast"))) match {
case Some(merchant: IMerchant) => merchant
case _ => null
})
_recipeID = nbt.getInteger("recipeID")
_vilRef = new WeakReference[EntityVillager](getEntityByUUID(dimID, UUID.fromString(nbt.getString("villagerUUID"))).orNull.asInstanceOf[EntityVillager])
recipeID = nbt.getInteger("recipeID")
}
def save(nbt: NBTTagCompound): Unit = {
host match {
case Some(h) =>
nbt.setInteger("dimensionID", h.world.provider.dimensionId)
hostIsDrone match {
case true => nbt.setString("hostUUID", h.asInstanceOf[li.cil.oc.common.entity.Drone].getPersistentID.toString)
case false =>
nbt.setInteger("hostX", h.xPosition.floor.toInt)
nbt.setInteger("hostY", h.yPosition.floor.toInt)
nbt.setInteger("hostZ", h.zPosition.floor.toInt)
}
case None =>
case Some(entity: Entity) =>
nbt.setBoolean("hostIsEntity", true)
nbt.setInteger("dimensionID", entity.world.provider.dimensionId)
nbt.setLong("hostUUIDLeast", entity.getPersistentID.getLeastSignificantBits)
nbt.setLong("hostUUIDMost", entity.getPersistentID.getMostSignificantBits)
case Some(tileEntity: TileEntity) =>
nbt.setBoolean("hostIsEntity", false)
nbt.setInteger("dimensionID", tileEntity.getWorldObj.provider.dimensionId)
nbt.setInteger("hostX", tileEntity.xCoord)
nbt.setInteger("hostY", tileEntity.yCoord)
nbt.setInteger("hostZ", tileEntity.zCoord)
case _ => // Welp!
}
villager match {
case Some(v) => nbt.setString("villagerUUID", v.getPersistentID.toString)
case None =>
merchant.get match {
case Some(entity: Entity) =>
nbt.setLong("merchantUUIDLeast", entity.getPersistentID.getLeastSignificantBits)
nbt.setLong("merchantUUIDMost", entity.getPersistentID.getMostSignificantBits)
case _ =>
}
nbt.setBoolean("hostIsDrone", hostIsDrone)
nbt.setInteger("recipeID", _recipeID)
nbt.setInteger("recipeID", recipeID)
}
def hostIsDrone = host.orNull match {
case h : li.cil.oc.common.tileentity.Robot => false
case h : li.cil.oc.common.entity.Drone => true
case _ => false
private def loadEntity(nbt: NBTTagCompound, uuid: UUID): Option[Entity] = {
val dimension = nbt.getInteger("dimensionID")
val world = DimensionManager.getProvider(dimension).worldObj
world.loadedEntityList.find {
case entity: Entity if entity.getPersistentID == uuid => true
case _ => false
}.map(_.asInstanceOf[Entity])
}
private var _host: Option[EnvironmentHost] = None
private var _recipeID: Int = _
private var _vilRef: WeakReference[EntityVillager] = new WeakReference[EntityVillager](null)
def host = _host
private def host_= (value: EnvironmentHost): Unit = _host = Option(value)
private def host_= (value: Option[EnvironmentHost]): Unit = _host = value
def villager = _vilRef.get
def recipeID = _recipeID
def recipe : MerchantRecipe = villager.orNull.getRecipes(null).get(recipeID).asInstanceOf[MerchantRecipe]
def inventory = host match {
case Some(h) => hostIsDrone match {
case true => Some(h.asInstanceOf[li.cil.oc.common.entity.Drone].mainInventory)
case false => Some(h.asInstanceOf[li.cil.oc.common.tileentity.Robot].mainInventory)
private def loadHostEntity(nbt: NBTTagCompound): Option[EnvironmentHost] = {
loadEntity(nbt, new UUID(nbt.getLong("hostUUIDMost"), nbt.getLong("hostUUIDLeast"))) match {
case Some(entity: Entity with li.cil.oc.api.internal.Agent) => Option(entity: EnvironmentHost)
case _ => None
}
case None => None
}
}
private def loadHostTileEntity(nbt: NBTTagCompound): Option[EnvironmentHost] = {
val dimension = nbt.getInteger("dimensionID")
val world = DimensionManager.getProvider(dimension).worldObj
val x = nbt.getInteger("hostX")
val y = nbt.getInteger("hostY")
val z = nbt.getInteger("hostZ")
world.getTileEntity(x, y, z) match {
case robotProxy: li.cil.oc.common.tileentity.RobotProxy => Option(robotProxy.robot)
case agent: li.cil.oc.api.internal.Agent => Option(agent)
case null => None
}
}
}

View File

@ -1,16 +1,19 @@
package li.cil.oc.server.component
import li.cil.oc.Settings
import li.cil.oc.api.network.EnvironmentHost
import li.cil.oc.api.prefab
import li.cil.oc.api.Network
import li.cil.oc.api.machine.Arguments
import li.cil.oc.api.machine.Callback
import li.cil.oc.api.machine.Context
import li.cil.oc.api.network.EnvironmentHost
import li.cil.oc.api.network.Visibility
import li.cil.oc.api.prefab
import li.cil.oc.util.BlockPosition
import li.cil.oc.api.machine.{Callback, Arguments, Context}
import net.minecraft.entity.passive.EntityVillager
import net.minecraft.entity.Entity
import net.minecraft.entity.IMerchant
import net.minecraft.util.Vec3
import scala.collection.mutable.ArrayBuffer
import scala.collection.convert.WrapAsScala._
class UpgradeTrading(val host: EnvironmentHost) extends prefab.ManagedEnvironment with traits.WorldAware {
override val node = Network.newNode(this, Visibility.Network).
@ -19,29 +22,15 @@ class UpgradeTrading(val host: EnvironmentHost) extends prefab.ManagedEnvironmen
override def position = BlockPosition(host)
var maxRange = Settings.get.tradingRange
def maxRange = Settings.get.tradingRange
def inRange(vil: EntityVillager): Boolean = {
Vec3.createVectorHelper(vil.posX, vil.posY, vil.posZ).distanceTo(position.toVec3) <= maxRange
}
def isInRange(entity: Entity) = Vec3.createVectorHelper(entity.posX, entity.posY, entity.posZ).distanceTo(position.toVec3) <= maxRange
@Callback(doc = "function():table -- Returns a table of trades in range as userdata objects")
@Callback(doc = "function():table -- Returns a table of trades in range as userdata objects.")
def getTrades(context: Context, args: Arguments): Array[AnyRef] = {
val boundsLow = position.bounds.offset(-maxRange, -maxRange, -maxRange)
val boundsHigh = position.bounds.offset(maxRange, maxRange, maxRange)
val bounds = boundsLow.func_111270_a(boundsHigh)
val trades = ArrayBuffer[Trade]()
entitiesInBounds[EntityVillager](bounds).foreach((vil: EntityVillager) => {
if (inRange(vil)) {
val merchantRecipes = vil.getRecipes(null)
for (i <- 0 to merchantRecipes.size() - 1) {
val trade = new Trade(this, vil, i)
trades += trade
}
}
})
trades.toArray[Object]
result(entitiesInBounds[Entity](position.bounds.expand(maxRange, maxRange, maxRange)).
filter(isInRange).
collect { case merchant: IMerchant => merchant }.
flatMap(merchant => merchant.getRecipes(null).indices.map(new Trade(this, merchant, _))))
}
}

View File

@ -73,7 +73,7 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest
val blockPos = position.offset(facing)
if (InventoryUtils.inventoryAt(blockPos).exists(inventory => {
inventory.isUseableByPlayer(fakePlayer) && mayInteract(blockPos, facing.getOpposite) && InventoryUtils.extractFromInventory(InventoryUtils.insertIntoInventory(_, this.inventory, slots = Option(insertionSlots)), inventory, facing.getOpposite, count)
inventory.isUseableByPlayer(fakePlayer) && mayInteract(blockPos, facing.getOpposite) && InventoryUtils.extractAnyFromInventory(InventoryUtils.insertIntoInventory(_, this.inventory, slots = Option(insertionSlots)), inventory, facing.getOpposite, count)
})) {
context.pause(Settings.get.suckDelay)
result(true)

View File

@ -21,7 +21,7 @@ trait ItemInventoryControl extends InventoryAware {
def dropIntoItemInventory(context: Context, args: Arguments): Array[AnyRef] = {
withItemInventory(args.checkSlot(inventory, 0), itemInventory => {
val count = args.optItemCount(1)
result(InventoryUtils.extractFromInventory(InventoryUtils.insertIntoInventory(_, itemInventory), inventory, ForgeDirection.UNKNOWN, count))
result(InventoryUtils.extractAnyFromInventory(InventoryUtils.insertIntoInventory(_, itemInventory), inventory, ForgeDirection.UNKNOWN, count))
})
}
@ -29,7 +29,7 @@ trait ItemInventoryControl extends InventoryAware {
def suckFromItemInventory(context: Context, args: Arguments): Array[AnyRef] = {
withItemInventory(args.checkSlot(inventory, 0), itemInventory => {
val count = args.optItemCount(1)
result(InventoryUtils.extractFromInventory(InventoryUtils.insertIntoInventory(_, inventory, slots = Option(insertionSlots)), itemInventory, ForgeDirection.UNKNOWN, count))
result(InventoryUtils.extractAnyFromInventory(InventoryUtils.insertIntoInventory(_, inventory, slots = Option(insertionSlots)), itemInventory, ForgeDirection.UNKNOWN, count))
})
}

View File

@ -209,7 +209,7 @@ object InventoryUtils {
/**
* Extracts a slot from an inventory.
* </p>
* <p/>
* This will try to extract a stack from any inventory slot. It will iterate
* all slots until an item can be extracted from a slot.
* <p/>
@ -218,7 +218,7 @@ object InventoryUtils {
* <p/>
* This returns <tt>true</tt> if at least one item was extracted.
*/
def extractFromInventory(consumer: (ItemStack) => Unit, inventory: IInventory, side: ForgeDirection, limit: Int = 64) = {
def extractAnyFromInventory(consumer: (ItemStack) => Unit, inventory: IInventory, side: ForgeDirection, limit: Int = 64) = {
val range = inventory match {
case sided: ISidedInventory => sided.getAccessibleSlotsFromSide(side.ordinal).toIterable
case _ => 0 until inventory.getSizeInventory
@ -226,6 +226,35 @@ object InventoryUtils {
range.exists(slot => extractFromInventorySlot(consumer, inventory, side, slot, limit))
}
/**
* Extracts an item stack from an inventory.
* <p/>
* This will try to remove items of the same type as the specified item stack
* up to the number of the stack's size for all slots in the specified inventory.
* <p/>
* This uses the <tt>extractFromInventorySlot</tt> method, and therefore
* handles special cases such as sided inventories and stack size limits.
*/
def extractFromInventory(stack: ItemStack, inventory: IInventory, side: ForgeDirection, simulate: Boolean = false) = {
val range = inventory match {
case sided: ISidedInventory => sided.getAccessibleSlotsFromSide(side.ordinal).toIterable
case _ => 0 until inventory.getSizeInventory
}
val remaining = stack.copy()
for (slot <- range if remaining.stackSize > 0) {
extractFromInventorySlot(stack => {
if (haveSameItemType(remaining, stack)) {
val transferred = stack.stackSize min remaining.stackSize
remaining.stackSize -= transferred
if (!simulate) {
stack.stackSize -= transferred
}
}
}, inventory, side, slot, remaining.stackSize)
}
remaining
}
/**
* Utility method for calling <tt>insertIntoInventory</tt> on an inventory
* in the world.
@ -238,7 +267,7 @@ object InventoryUtils {
* in the world.
*/
def extractFromInventoryAt(consumer: (ItemStack) => Unit, position: BlockPosition, side: ForgeDirection, limit: Int = 64) =
inventoryAt(position).exists(extractFromInventory(consumer, _, side, limit))
inventoryAt(position).exists(extractAnyFromInventory(consumer, _, side, limit))
/**
* Transfers some items between two inventories.
@ -254,7 +283,7 @@ object InventoryUtils {
* This returns <tt>true</tt> if at least one item was transferred.
*/
def transferBetweenInventories(source: IInventory, sourceSide: ForgeDirection, sink: IInventory, sinkSide: Option[ForgeDirection], limit: Int = 64) =
extractFromInventory(
extractAnyFromInventory(
insertIntoInventory(_, sink, sinkSide, limit), source, sourceSide, limit)
/**