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 Controller](tankControllerUpgrade.md)
* [Tank Upgrade](tankUpgrade.md) * [Tank Upgrade](tankUpgrade.md)
* [Tractor Beam Upgrade](tractorBeamUpgrade.md) * [Tractor Beam Upgrade](tractorBeamUpgrade.md)
* [Trading Upgrade](tradingUpgrade.md)
* [Upgrade Container](upgradeContainer1.md) * [Upgrade Container](upgradeContainer1.md)
### Other ### 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] [ingotIron, "oc:capacitor", ingotIron]
[ingotGold, "oc:circuitChip3", ingotGold]] [ingotGold, "oc:circuitChip3", ingotGold]]
} }
tradingUpgrade {
input: [[ingotGold, chest, ingotGold]
[emerald, "oc:circuitChip2", emerald]
[dropper, "oc:materialCircuitBoardPrinted", craftingPiston]]
}
cardContainer1 { cardContainer1 {
input: [[ingotIron, "oc:circuitChip1", ingotIron] input: [[ingotIron, "oc:circuitChip1", ingotIron]
@ -367,12 +372,6 @@ upgradeContainer3 {
[ingotGold, "oc:materialCircuitBoardPrinted", ingotGold]] [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 # Note: iron ingot and nugget recipes are *only* registered if no other mod
# already provides the same functionality. # already provides the same functionality.
nuggetIron { nuggetIron {

View File

@ -181,8 +181,7 @@ solarGeneratorUpgrade {
input: [[blockGlass, blockGlass, blockGlass] input: [[blockGlass, blockGlass, blockGlass]
["oc:circuitChip3", "oc:generatorUpgrade", "oc:circuitChip3"]] ["oc:circuitChip3", "oc:generatorUpgrade", "oc:circuitChip3"]]
} }
tradingUpgrade {
upgradeTrading {
input: [["oc:circuitChip2", chest, "oc:circuitChip2"] input: [["oc:circuitChip2", chest, "oc:circuitChip2"]
[emerald, "oc:circuitChip2", emerald] [emerald, "oc:circuitChip2", emerald]
[dropper, "oc:materialCircuitBoardPrinted", craftingPiston]] [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 TerminalServer = "terminalServer"
final val TexturePicker = "texturePicker" final val TexturePicker = "texturePicker"
final val TractorBeamUpgrade = "tractorBeamUpgrade" final val TractorBeamUpgrade = "tractorBeamUpgrade"
final val TradingUpgrade = "upgradeTrading" final val TradingUpgrade = "tradingUpgrade"
final val Transistor = "transistor" final val Transistor = "transistor"
final val UpgradeContainerTier1 = "upgradeContainer1" final val UpgradeContainerTier1 = "upgradeContainer1"
final val UpgradeContainerTier2 = "upgradeContainer2" final val UpgradeContainerTier2 = "upgradeContainer2"

View File

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

View File

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

View File

@ -4,253 +4,192 @@ import java.util.UUID
import li.cil.oc.Settings import li.cil.oc.Settings
import li.cil.oc.api.machine._ import li.cil.oc.api.machine._
import li.cil.oc.api.network.EnvironmentHost
import li.cil.oc.api.prefab.AbstractValue import li.cil.oc.api.prefab.AbstractValue
import li.cil.oc.common.EventHandler import li.cil.oc.common.EventHandler
import li.cil.oc.util.InventoryUtils 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.item.ItemStack
import net.minecraft.nbt.NBTTagCompound import net.minecraft.nbt.NBTTagCompound
import net.minecraft.tileentity.TileEntity
import net.minecraft.village.MerchantRecipe import net.minecraft.village.MerchantRecipe
import net.minecraftforge.common.DimensionManager import net.minecraftforge.common.DimensionManager
import net.minecraftforge.common.util.ForgeDirection 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() = 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) = { def maxRange = Settings.get.tradingRange
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)
})
}
override def save(nbt: NBTTagCompound) = { def isInRange = (info.merchant.get, info.host) match {
//Tell info to save to nbt case (Some(merchant: Entity), Some(host)) => merchant.getDistanceSq(host.xPosition, host.yPosition, host.zPosition) < maxRange * maxRange
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
case _ => false case _ => false
} }
def getTileEntity(dimID: Int, posX: Int, posY: Int, posZ: Int) = Option(DimensionManager.getProvider(dimID).worldObj.getTileEntity(posX, posY, posZ) match { // Queue the load because when load is called we can't access the world yet
case robot : li.cil.oc.common.tileentity.Robot => robot // and we need to access it to get the Robot's TileEntity / Drone's Entity.
case robotProxy : li.cil.oc.common.tileentity.RobotProxy => robotProxy.robot override def load(nbt: NBTTagCompound) = EventHandler.scheduleServer(() => info.load(nbt))
case null => None
}) 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 = { def load(nbt: NBTTagCompound): Unit = {
val dimID = nbt.getInteger("dimensionID") val isEntity = nbt.getBoolean("hostIsEntity")
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.
//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)
host = Option(_hostIsDrone match { merchant = new WeakReference[IMerchant](loadEntity(nbt, new UUID(nbt.getLong("merchantUUIDMost"), nbt.getLong("merchantUUIDLeast"))) match {
case true => getEntityByUUID(dimID, UUID.fromString(nbt.getString("hostUUID"))).orNull.asInstanceOf[EnvironmentHost] case Some(merchant: IMerchant) => merchant
case false => getTileEntity( case _ => null
dimID,
nbt.getInteger("hostX"),
nbt.getInteger("hostY"),
nbt.getInteger("hostZ")
).orNull.asInstanceOf[EnvironmentHost]
}) })
_recipeID = nbt.getInteger("recipeID") recipeID = nbt.getInteger("recipeID")
_vilRef = new WeakReference[EntityVillager](getEntityByUUID(dimID, UUID.fromString(nbt.getString("villagerUUID"))).orNull.asInstanceOf[EntityVillager])
} }
def save(nbt: NBTTagCompound): Unit = { def save(nbt: NBTTagCompound): Unit = {
host match { host match {
case Some(h) => case Some(entity: Entity) =>
nbt.setInteger("dimensionID", h.world.provider.dimensionId) nbt.setBoolean("hostIsEntity", true)
hostIsDrone match { nbt.setInteger("dimensionID", entity.world.provider.dimensionId)
case true => nbt.setString("hostUUID", h.asInstanceOf[li.cil.oc.common.entity.Drone].getPersistentID.toString) nbt.setLong("hostUUIDLeast", entity.getPersistentID.getLeastSignificantBits)
case false => nbt.setLong("hostUUIDMost", entity.getPersistentID.getMostSignificantBits)
nbt.setInteger("hostX", h.xPosition.floor.toInt) case Some(tileEntity: TileEntity) =>
nbt.setInteger("hostY", h.yPosition.floor.toInt) nbt.setBoolean("hostIsEntity", false)
nbt.setInteger("hostZ", h.zPosition.floor.toInt) nbt.setInteger("dimensionID", tileEntity.getWorldObj.provider.dimensionId)
nbt.setInteger("hostX", tileEntity.xCoord)
nbt.setInteger("hostY", tileEntity.yCoord)
nbt.setInteger("hostZ", tileEntity.zCoord)
case _ => // Welp!
} }
case None => merchant.get match {
case Some(entity: Entity) =>
nbt.setLong("merchantUUIDLeast", entity.getPersistentID.getLeastSignificantBits)
nbt.setLong("merchantUUIDMost", entity.getPersistentID.getMostSignificantBits)
case _ =>
} }
villager match { nbt.setInteger("recipeID", recipeID)
case Some(v) => nbt.setString("villagerUUID", v.getPersistentID.toString)
case None =>
}
nbt.setBoolean("hostIsDrone", hostIsDrone)
nbt.setInteger("recipeID", _recipeID)
} }
def hostIsDrone = host.orNull match { private def loadEntity(nbt: NBTTagCompound, uuid: UUID): Option[Entity] = {
case h : li.cil.oc.common.tileentity.Robot => false val dimension = nbt.getInteger("dimensionID")
case h : li.cil.oc.common.entity.Drone => true val world = DimensionManager.getProvider(dimension).worldObj
world.loadedEntityList.find {
case entity: Entity if entity.getPersistentID == uuid => true
case _ => false 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 loadHostEntity(nbt: NBTTagCompound): Option[EnvironmentHost] = {
private def host_= (value: EnvironmentHost): Unit = _host = Option(value) loadEntity(nbt, new UUID(nbt.getLong("hostUUIDMost"), nbt.getLong("hostUUIDLeast"))) match {
private def host_= (value: Option[EnvironmentHost]): Unit = _host = value case Some(entity: Entity with li.cil.oc.api.internal.Agent) => Option(entity: EnvironmentHost)
case _ => None
}
}
def villager = _vilRef.get private def loadHostTileEntity(nbt: NBTTagCompound): Option[EnvironmentHost] = {
def recipeID = _recipeID val dimension = nbt.getInteger("dimensionID")
def recipe : MerchantRecipe = villager.orNull.getRecipes(null).get(recipeID).asInstanceOf[MerchantRecipe] val world = DimensionManager.getProvider(dimension).worldObj
def inventory = host match { val x = nbt.getInteger("hostX")
case Some(h) => hostIsDrone match { val y = nbt.getInteger("hostY")
case true => Some(h.asInstanceOf[li.cil.oc.common.entity.Drone].mainInventory) val z = nbt.getInteger("hostZ")
case false => Some(h.asInstanceOf[li.cil.oc.common.tileentity.Robot].mainInventory)
} world.getTileEntity(x, y, z) match {
case None => None 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 package li.cil.oc.server.component
import li.cil.oc.Settings 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.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.network.Visibility
import li.cil.oc.api.prefab
import li.cil.oc.util.BlockPosition import li.cil.oc.util.BlockPosition
import li.cil.oc.api.machine.{Callback, Arguments, Context} import net.minecraft.entity.Entity
import net.minecraft.entity.passive.EntityVillager import net.minecraft.entity.IMerchant
import net.minecraft.util.Vec3 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 { class UpgradeTrading(val host: EnvironmentHost) extends prefab.ManagedEnvironment with traits.WorldAware {
override val node = Network.newNode(this, Visibility.Network). 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) override def position = BlockPosition(host)
var maxRange = Settings.get.tradingRange def maxRange = Settings.get.tradingRange
def inRange(vil: EntityVillager): Boolean = { def isInRange(entity: Entity) = Vec3.createVectorHelper(entity.posX, entity.posY, entity.posZ).distanceTo(position.toVec3) <= maxRange
Vec3.createVectorHelper(vil.posX, vil.posY, vil.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] = { def getTrades(context: Context, args: Arguments): Array[AnyRef] = {
result(entitiesInBounds[Entity](position.bounds.expand(maxRange, maxRange, maxRange)).
val boundsLow = position.bounds.offset(-maxRange, -maxRange, -maxRange) filter(isInRange).
val boundsHigh = position.bounds.offset(maxRange, maxRange, maxRange) collect { case merchant: IMerchant => merchant }.
val bounds = boundsLow.func_111270_a(boundsHigh) flatMap(merchant => merchant.getRecipes(null).indices.map(new Trade(this, merchant, _))))
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]
} }
} }

View File

@ -73,7 +73,7 @@ trait InventoryWorldControl extends InventoryAware with WorldAware with SideRest
val blockPos = position.offset(facing) val blockPos = position.offset(facing)
if (InventoryUtils.inventoryAt(blockPos).exists(inventory => { 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) context.pause(Settings.get.suckDelay)
result(true) result(true)

View File

@ -21,7 +21,7 @@ trait ItemInventoryControl extends InventoryAware {
def dropIntoItemInventory(context: Context, args: Arguments): Array[AnyRef] = { def dropIntoItemInventory(context: Context, args: Arguments): Array[AnyRef] = {
withItemInventory(args.checkSlot(inventory, 0), itemInventory => { withItemInventory(args.checkSlot(inventory, 0), itemInventory => {
val count = args.optItemCount(1) 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] = { def suckFromItemInventory(context: Context, args: Arguments): Array[AnyRef] = {
withItemInventory(args.checkSlot(inventory, 0), itemInventory => { withItemInventory(args.checkSlot(inventory, 0), itemInventory => {
val count = args.optItemCount(1) 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. * Extracts a slot from an inventory.
* </p> * <p/>
* This will try to extract a stack from any inventory slot. It will iterate * 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. * all slots until an item can be extracted from a slot.
* <p/> * <p/>
@ -218,7 +218,7 @@ object InventoryUtils {
* <p/> * <p/>
* This returns <tt>true</tt> if at least one item was extracted. * 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 { val range = inventory match {
case sided: ISidedInventory => sided.getAccessibleSlotsFromSide(side.ordinal).toIterable case sided: ISidedInventory => sided.getAccessibleSlotsFromSide(side.ordinal).toIterable
case _ => 0 until inventory.getSizeInventory case _ => 0 until inventory.getSizeInventory
@ -226,6 +226,35 @@ object InventoryUtils {
range.exists(slot => extractFromInventorySlot(consumer, inventory, side, slot, limit)) 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 * Utility method for calling <tt>insertIntoInventory</tt> on an inventory
* in the world. * in the world.
@ -238,7 +267,7 @@ object InventoryUtils {
* in the world. * in the world.
*/ */
def extractFromInventoryAt(consumer: (ItemStack) => Unit, position: BlockPosition, side: ForgeDirection, limit: Int = 64) = 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. * Transfers some items between two inventories.
@ -254,7 +283,7 @@ object InventoryUtils {
* This returns <tt>true</tt> if at least one item was transferred. * 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) = def transferBetweenInventories(source: IInventory, sourceSide: ForgeDirection, sink: IInventory, sinkSide: Option[ForgeDirection], limit: Int = 64) =
extractFromInventory( extractAnyFromInventory(
insertIntoInventory(_, sink, sinkSide, limit), source, sourceSide, limit) insertIntoInventory(_, sink, sinkSide, limit), source, sourceSide, limit)
/** /**