Merge branch 'UpgradeTrading' of https://github.com/Inari-Whitebear/OpenComputers-TradingUpgrade into master-MC1.7.10

This commit is contained in:
Florian Nücke 2016-01-04 15:03:47 +01:00
commit 7af4b41b1e
14 changed files with 372 additions and 3 deletions

View File

@ -1317,6 +1317,10 @@ opencomputers {
# such as the redstone card and redstone I/O block. Lowering this can
# have very negative impact on server TPS, so beware.
redstoneDelay: 0.1
# The maximum range between the drone/robot and a villager for a trade to
# be performed by the trading upgrade
tradingRange: 8.0
}
# Settings for mod integration (the mod previously known as OpenComponents).

View File

@ -145,6 +145,7 @@ item.oc.UpgradeSolarGenerator.name=Solargenerator-Upgrade
item.oc.UpgradeTank.name=Tank-Upgrade
item.oc.UpgradeTankController.name=Tankbedienungs-Upgrade
item.oc.UpgradeTractorBeam.name=Traktorstrahl-Upgrade
item.oc.UpgradeTrading.name=Handels-Upgrade
item.oc.WirelessNetworkCard.name=Drahtlosnetzwerkkarte
item.oc.WorldSensorCard.name=Weltsensorkarte
item.oc.wrench.name=Schraubenziehschlüssel

View File

@ -150,6 +150,7 @@ item.oc.UpgradeSolarGenerator.name=Solar Generator Upgrade
item.oc.UpgradeTank.name=Tank Upgrade
item.oc.UpgradeTankController.name=Tank Controller Upgrade
item.oc.UpgradeTractorBeam.name=Tractor Beam Upgrade
item.oc.UpgradeTrading.name=Trading Upgrade
item.oc.WirelessNetworkCard.name=Wireless Network Card
item.oc.WorldSensorCard.name=World Sensor Card
item.oc.wrench.name=Scrench

View File

@ -367,6 +367,12 @@ 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

@ -182,6 +182,12 @@ solarGeneratorUpgrade {
["oc:circuitChip3", "oc:generatorUpgrade", "oc:circuitChip3"]]
}
upgradeTrading {
input: [["oc:circuitChip2", chest, "oc:circuitChip2"]
[emerald, "oc:circuitChip2", emerald]
[dropper, "oc:materialCircuitBoardPrinted", craftingPiston]]
}
nuggetIron {
type: shapeless
input: ingotIron

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

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

View File

@ -342,6 +342,7 @@ class Settings(val config: Config) {
val dataCardTimeout = config.getDouble("misc.dataCardTimeout") max 0
val serverRackSwitchTier = (config.getInt("misc.serverRackSwitchTier") - 1) max Tier.None min Tier.Three
val redstoneDelay = config.getDouble("misc.redstoneDelay") max 0
val tradingRange = config.getDouble("misc.tradingRange") max 0
// ----------------------------------------------------------------------- //
// nanomachines

View File

@ -533,6 +533,8 @@ object Items extends ItemAPI {
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")
// Register aliases.
for ((k, v) <- aliases) {
descriptors.getOrElseUpdate(k, descriptors(v))

View File

@ -0,0 +1,3 @@
package li.cil.oc.common.item
class UpgradeTrading(val parent: Delegator) extends traits.Delegate with traits.ItemTier

View File

@ -0,0 +1,37 @@
package li.cil.oc.integration.opencomputers
import li.cil.oc.api
import li.cil.oc.Constants
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.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,
api.Items.get(Constants.ItemName.TradingUpgrade))
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
}
override def slot(stack: ItemStack) = Slot.Upgrade
override def tier(stack: ItemStack) = Tier.Two
object Provider extends EnvironmentProvider {
override def getEnvironment(stack: ItemStack): Class[_] =
if (worksWith(stack))
classOf[component.UpgradeTrading]
else null
}
}

View File

@ -146,6 +146,7 @@ object ModOpenComputers extends ModProxy {
api.Driver.add(DriverUpgradeTank)
api.Driver.add(DriverUpgradeTankController)
api.Driver.add(DriverUpgradeTractorBeam)
api.Driver.add(DriverUpgradeTrading)
api.Driver.add(DriverAPU.Provider)
api.Driver.add(DriverDataCard.Provider)
@ -200,7 +201,8 @@ object ModOpenComputers extends ModProxy {
Constants.ItemName.SolarGeneratorUpgrade,
Constants.ItemName.TankUpgrade,
Constants.ItemName.TractorBeamUpgrade,
Constants.ItemName.LeashUpgrade)
Constants.ItemName.LeashUpgrade,
Constants.ItemName.TradingUpgrade)
blacklistHost(classOf[internal.Drone],
Constants.BlockName.Keyboard,
Constants.BlockName.ScreenTier1,
@ -240,7 +242,8 @@ object ModOpenComputers extends ModProxy {
Constants.ItemName.TankUpgrade,
Constants.ItemName.TankControllerUpgrade,
Constants.ItemName.TractorBeamUpgrade,
Constants.ItemName.LeashUpgrade)
Constants.ItemName.LeashUpgrade,
Constants.ItemName.TradingUpgrade)
blacklistHost(classOf[internal.Robot],
Constants.BlockName.Transposer,
Constants.ItemName.LeashUpgrade)
@ -263,7 +266,8 @@ object ModOpenComputers extends ModProxy {
Constants.ItemName.InventoryControllerUpgrade,
Constants.ItemName.TankUpgrade,
Constants.ItemName.TankControllerUpgrade,
Constants.ItemName.LeashUpgrade)
Constants.ItemName.LeashUpgrade,
Constants.ItemName.TradingUpgrade)
if (!WirelessRedstone.isAvailable) {
blacklistHost(classOf[internal.Drone], Constants.ItemName.RedstoneCardTier2)

View File

@ -0,0 +1,256 @@
package li.cil.oc.server.component
import java.util.UUID
import li.cil.oc.Settings
import li.cil.oc.api.machine._
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.item.ItemStack
import net.minecraft.nbt.NBTTagCompound
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 {
def this() = this(new TradeInfo())
def this(upgr: UpgradeTrading, villager: EntityVillager, recipeID: Int) = {
this(new TradeInfo(upgr.host, villager, 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)
})
}
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
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
})
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]
})
_recipeID = nbt.getInteger("recipeID")
_vilRef = new WeakReference[EntityVillager](getEntityByUUID(dimID, UUID.fromString(nbt.getString("villagerUUID"))).orNull.asInstanceOf[EntityVillager])
}
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 =>
}
villager match {
case Some(v) => nbt.setString("villagerUUID", v.getPersistentID.toString)
case None =>
}
nbt.setBoolean("hostIsDrone", hostIsDrone)
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 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)
}
case None => None
}
}

View File

@ -0,0 +1,47 @@
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.network.Visibility
import li.cil.oc.util.BlockPosition
import li.cil.oc.api.machine.{Callback, Arguments, Context}
import net.minecraft.entity.passive.EntityVillager
import net.minecraft.util.Vec3
import scala.collection.mutable.ArrayBuffer
class UpgradeTrading(val host: EnvironmentHost) extends prefab.ManagedEnvironment with traits.WorldAware {
override val node = Network.newNode(this, Visibility.Network).
withComponent("trading").
create()
override def position = BlockPosition(host)
var maxRange = Settings.get.tradingRange
def inRange(vil: EntityVillager): Boolean = {
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")
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]
}
}