diff --git a/build.properties b/build.properties index b9816b189..a8683e892 100644 --- a/build.properties +++ b/build.properties @@ -1,7 +1,7 @@ minecraft.version=1.8 forge.version=11.14.3.1450 -oc.version=1.5.17 +oc.version=1.5.18 oc.subversion=dev ae2.version=rv2-beta-26 diff --git a/src/main/scala/li/cil/oc/client/PacketHandler.scala b/src/main/scala/li/cil/oc/client/PacketHandler.scala index 9a2ea387d..a240f4f01 100644 --- a/src/main/scala/li/cil/oc/client/PacketHandler.scala +++ b/src/main/scala/li/cil/oc/client/PacketHandler.scala @@ -54,6 +54,8 @@ object PacketHandler extends CommonPacketHandler { case PacketType.HologramClear => onHologramClear(p) case PacketType.HologramColor => onHologramColor(p) case PacketType.HologramPowerChange => onHologramPowerChange(p) + case PacketType.HologramRotation => onHologramRotation(p) + case PacketType.HologramRotationSpeed => onHologramRotationSpeed(p) case PacketType.HologramScale => onHologramScale(p) case PacketType.HologramTranslation => onHologramPositionOffsetY(p) case PacketType.HologramValues => onHologramValues(p) @@ -254,6 +256,26 @@ object PacketHandler extends CommonPacketHandler { case _ => // Invalid packet. } + def onHologramRotation(p: PacketParser) = + p.readTileEntity[Hologram]() match { + case Some(t) => + t.rotationAngle = p.readFloat() + t.rotationX = p.readFloat() + t.rotationY = p.readFloat() + t.rotationZ = p.readFloat() + case _ => // Invalid packet. + } + + def onHologramRotationSpeed(p: PacketParser) = + p.readTileEntity[Hologram]() match { + case Some(t) => + t.rotationSpeed = p.readFloat() + t.rotationSpeedX = p.readFloat() + t.rotationSpeedY = p.readFloat() + t.rotationSpeedZ = p.readFloat() + case _ => // Invalid packet. + } + def onLootDisk(p: PacketParser) = { val stack = p.readItemStack() if (stack != null) { diff --git a/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala index ff60bcec8..0e46675b7 100644 --- a/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/font/DynamicFontRenderer.scala @@ -2,7 +2,7 @@ package li.cil.oc.client.renderer.font import li.cil.oc.Settings import li.cil.oc.client.renderer.font.DynamicFontRenderer.CharTexture -import li.cil.oc.util.FontUtil +import li.cil.oc.util.FontUtils import li.cil.oc.util.RenderState import net.minecraft.client.Minecraft import net.minecraft.client.renderer.GlStateManager @@ -76,7 +76,7 @@ class DynamicFontRenderer extends TextureFontRenderer with IResourceManagerReloa } private def createCharIcon(char: Char): DynamicFontRenderer.CharIcon = { - if (FontUtil.wcwidth(char) < 1 || glyphProvider.getGlyph(char) == null) { + if (FontUtils.wcwidth(char) < 1 || glyphProvider.getGlyph(char) == null) { if (char == '?') null else charMap.getOrElseUpdate('?', createCharIcon('?')) } @@ -123,10 +123,10 @@ object DynamicFontRenderer { RenderState.bindTexture(id) } - def isFull(char: Char) = chars + FontUtil.wcwidth(char) > capacity + def isFull(char: Char) = chars + FontUtils.wcwidth(char) > capacity def add(char: Char) = { - val glyphWidth = FontUtil.wcwidth(char) + val glyphWidth = FontUtils.wcwidth(char) val w = owner.charWidth * glyphWidth val h = owner.charHeight // Force line break if we have a char that's wider than what space remains in this row. diff --git a/src/main/scala/li/cil/oc/client/renderer/font/FontParserUnifont.java b/src/main/scala/li/cil/oc/client/renderer/font/FontParserUnifont.java index 9d1310b96..000ccb713 100644 --- a/src/main/scala/li/cil/oc/client/renderer/font/FontParserUnifont.java +++ b/src/main/scala/li/cil/oc/client/renderer/font/FontParserUnifont.java @@ -2,7 +2,7 @@ package li.cil.oc.client.renderer.font; import li.cil.oc.OpenComputers; import li.cil.oc.Settings; -import li.cil.oc.util.FontUtil; +import li.cil.oc.util.FontUtils; import net.minecraft.client.Minecraft; import net.minecraft.util.ResourceLocation; import org.lwjgl.BufferUtils; @@ -35,7 +35,7 @@ public class FontParserUnifont implements IGlyphProvider { while ((line = input.readLine()) != null) { final String[] info = line.split(":"); final int charCode = Integer.parseInt(info[0], 16); - final int expectedWidth = FontUtil.wcwidth(charCode); + final int expectedWidth = FontUtils.wcwidth(charCode); if (expectedWidth < 1) continue; // Skip control characters. final byte[] glyph = new byte[info[1].length() >> 1]; final int glyphWidth = glyph.length / getGlyphHeight(); diff --git a/src/main/scala/li/cil/oc/client/renderer/font/IGlyphProvider.java b/src/main/scala/li/cil/oc/client/renderer/font/IGlyphProvider.java index fabd3c45a..cba6960c4 100644 --- a/src/main/scala/li/cil/oc/client/renderer/font/IGlyphProvider.java +++ b/src/main/scala/li/cil/oc/client/renderer/font/IGlyphProvider.java @@ -39,7 +39,7 @@ public interface IGlyphProvider { *

* Each glyph provided is expected to have the same width multiplier; i.e. * a glyphs actual width (in pixels) is expected to be this value times - * {@link li.cil.oc.util.FontUtil#wcwidth(int)} (for a specific char). + * {@link li.cil.oc.util.FontUtils#wcwidth(int)} (for a specific char). */ public int getGlyphWidth(); diff --git a/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala b/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala index b9e980698..426201045 100644 --- a/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala +++ b/src/main/scala/li/cil/oc/client/renderer/tileentity/HologramRenderer.scala @@ -99,6 +99,9 @@ object HologramRenderer extends TileEntitySpecialRenderer with Callable[Int] wit case _ => // No pitch. } + GL11.glRotatef(hologram.rotationAngle, hologram.rotationX, hologram.rotationY, hologram.rotationZ) + GL11.glRotatef(hologram.rotationSpeed * (hologram.getWorld.getTotalWorldTime % (360 * 20 - 1) + f) / 20f, hologram.rotationSpeedX, hologram.rotationSpeedY, hologram.rotationSpeedZ) + GL11.glScaled(1.001, 1.001, 1.001) // Avoid z-fighting with other blocks. GL11.glTranslated( (hologram.translation.xCoord * hologram.width / 16 - 1.5) * hologram.scale, diff --git a/src/main/scala/li/cil/oc/common/EventHandler.scala b/src/main/scala/li/cil/oc/common/EventHandler.scala index ae137a245..65b51bcb2 100644 --- a/src/main/scala/li/cil/oc/common/EventHandler.scala +++ b/src/main/scala/li/cil/oc/common/EventHandler.scala @@ -26,7 +26,6 @@ import li.cil.oc.util._ import net.minecraft.entity.player.EntityPlayer import net.minecraft.entity.player.EntityPlayerMP import net.minecraft.item.ItemStack -import net.minecraft.nbt.NBTTagCompound import net.minecraft.server.MinecraftServer import net.minecraft.tileentity.TileEntity import net.minecraftforge.common.util.FakePlayer @@ -199,13 +198,9 @@ object EventHandler { def onEntityJoinWorld(e: EntityJoinWorldEvent): Unit = { if (Settings.get.giveManualToNewPlayers && !e.world.isRemote) e.entity match { case player: EntityPlayer if !player.isInstanceOf[FakePlayer] => - val nbt = player.getEntityData - if (!nbt.hasKey(EntityPlayer.PERSISTED_NBT_TAG)) { - nbt.setTag(EntityPlayer.PERSISTED_NBT_TAG, new NBTTagCompound()) - } - val ocData = nbt.getCompoundTag(EntityPlayer.PERSISTED_NBT_TAG) - if (!ocData.getBoolean(Settings.namespace + "receivedManual")) { - ocData.setBoolean(Settings.namespace + "receivedManual", true) + val persistedData = PlayerUtils.persistedData(player) + if (!persistedData.getBoolean(Settings.namespace + "receivedManual")) { + persistedData.setBoolean(Settings.namespace + "receivedManual", true) player.inventory.addItemStackToInventory(api.Items.get(Constants.ItemName.Manual).createItemStack(1)) } case _ => diff --git a/src/main/scala/li/cil/oc/common/PacketType.scala b/src/main/scala/li/cil/oc/common/PacketType.scala index 325612a99..4ad59cad7 100644 --- a/src/main/scala/li/cil/oc/common/PacketType.scala +++ b/src/main/scala/li/cil/oc/common/PacketType.scala @@ -16,6 +16,8 @@ object PacketType extends Enumeration { HologramClear, HologramColor, HologramPowerChange, + HologramRotation, + HologramRotationSpeed, HologramScale, HologramTranslation, HologramValues, diff --git a/src/main/scala/li/cil/oc/common/inventory/Inventory.scala b/src/main/scala/li/cil/oc/common/inventory/Inventory.scala index b1c42f3a3..2d022efe9 100644 --- a/src/main/scala/li/cil/oc/common/inventory/Inventory.scala +++ b/src/main/scala/li/cil/oc/common/inventory/Inventory.scala @@ -1,15 +1,12 @@ package li.cil.oc.common.inventory -import li.cil.oc.Localization import li.cil.oc.Settings import li.cil.oc.util.ExtendedNBT._ -import net.minecraft.entity.player.EntityPlayer -import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraftforge.common.util.Constants.NBT -trait Inventory extends IInventory { +trait Inventory extends SimpleInventory { def items: Array[Option[ItemStack]] def updateItems(slot: Int, stack: ItemStack) = items(slot) = Option(stack) @@ -20,24 +17,6 @@ trait Inventory extends IInventory { if (slot >= 0 && slot < getSizeInventory) items(slot).orNull else null - override def decrStackSize(slot: Int, amount: Int) = - if (slot >= 0 && slot < getSizeInventory) { - (items(slot) match { - case Some(stack) if stack.stackSize - amount < getInventoryStackRequired => - setInventorySlotContents(slot, null) - stack - case Some(stack) => - val result = stack.splitStack(amount) - markDirty() - result - case _ => null - }) match { - case stack: ItemStack if stack.stackSize > 0 => stack - case _ => null - } - } - else null - override def setInventorySlotContents(slot: Int, stack: ItemStack): Unit = { if (slot >= 0 && slot < getSizeInventory) { if (stack == null && items(slot).isEmpty) { @@ -67,30 +46,10 @@ trait Inventory extends IInventory { } } - def getInventoryStackRequired = 1 - - override def getStackInSlotOnClosing(slot: Int) = null - - override def openInventory(player: EntityPlayer) {} - - override def closeInventory(player: EntityPlayer) {} - - override def clear() {} // TODO implement? - override def getName = Settings.namespace + "container." + inventoryName - override def hasCustomName = false - - override def getDisplayName = Localization.localizeLater(getName) - protected def inventoryName = getClass.getSimpleName - override def getField(id: Int) = 0 - - override def setField(id: Int, value: Int) {} - - override def getFieldCount = 0 - // ----------------------------------------------------------------------- // def load(nbt: NBTTagCompound) { diff --git a/src/main/scala/li/cil/oc/common/inventory/InventoryProxy.scala b/src/main/scala/li/cil/oc/common/inventory/InventoryProxy.scala new file mode 100644 index 000000000..120a4c4f3 --- /dev/null +++ b/src/main/scala/li/cil/oc/common/inventory/InventoryProxy.scala @@ -0,0 +1,67 @@ +package li.cil.oc.common.inventory + +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.inventory.IInventory +import net.minecraft.item.ItemStack + +trait InventoryProxy extends IInventory { + def inventory: IInventory + + def offset = 0 + + override def getSizeInventory = inventory.getSizeInventory + + override def getInventoryStackLimit = inventory.getInventoryStackLimit + + override def getName = inventory.getName + + override def getDisplayName = inventory.getDisplayName + + override def hasCustomName = inventory.hasCustomName + + override def isUseableByPlayer(player: EntityPlayer) = inventory.isUseableByPlayer(player) + + override def isItemValidForSlot(slot: Int, stack: ItemStack) = { + val offsetSlot = slot + offset + isValidSlot(offsetSlot) && inventory.isItemValidForSlot(offsetSlot, stack) + } + + override def getStackInSlot(slot: Int) = { + val offsetSlot = slot + offset + if (isValidSlot(offsetSlot)) inventory.getStackInSlot(offsetSlot) + else null + } + + override def decrStackSize(slot: Int, amount: Int) = { + val offsetSlot = slot + offset + if (isValidSlot(offsetSlot)) inventory.decrStackSize(offsetSlot, amount) + else null + } + + override def getStackInSlotOnClosing(slot: Int) = { + val offsetSlot = slot + offset + if (isValidSlot(offsetSlot)) inventory.getStackInSlotOnClosing(offsetSlot) + else null + } + + override def setInventorySlotContents(slot: Int, stack: ItemStack) = { + val offsetSlot = slot + offset + if (isValidSlot(offsetSlot)) inventory.setInventorySlotContents(offsetSlot, stack) + } + + override def markDirty() = inventory.markDirty() + + override def openInventory(player: EntityPlayer) = inventory.openInventory(player) + + override def closeInventory(player: EntityPlayer) = inventory.closeInventory(player) + + override def setField(id: Int, value: Int) = inventory.setField(id, value) + + override def clear() = inventory.clear() + + override def getFieldCount = inventory.getFieldCount + + override def getField(id: Int) = inventory.getField(id) + + private def isValidSlot(slot: Int) = slot >= 0 && slot < getSizeInventory +} diff --git a/src/main/scala/li/cil/oc/common/inventory/SimpleInventory.scala b/src/main/scala/li/cil/oc/common/inventory/SimpleInventory.scala new file mode 100644 index 000000000..984d3e50b --- /dev/null +++ b/src/main/scala/li/cil/oc/common/inventory/SimpleInventory.scala @@ -0,0 +1,61 @@ +package li.cil.oc.common.inventory + +import li.cil.oc.Localization +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.inventory.IInventory +import net.minecraft.item.ItemStack + +trait SimpleInventory extends IInventory { + override def hasCustomName = false + + override def getDisplayName = Localization.localizeLater(getName) + + override def getInventoryStackLimit = 64 + + // Items required in a slot before it's set to null (for ghost stacks). + def getInventoryStackRequired = 1 + + override def openInventory(player: EntityPlayer): Unit = {} + + override def closeInventory(player: EntityPlayer): Unit = {} + + override def decrStackSize(slot: Int, amount: Int): ItemStack = { + if (slot >= 0 && slot < getSizeInventory) { + (getStackInSlot(slot) match { + case stack: ItemStack if stack.stackSize - amount < getInventoryStackRequired => + setInventorySlotContents(slot, null) + stack + case stack: ItemStack => + val result = stack.splitStack(amount) + markDirty() + result + case _ => null + }) match { + case stack: ItemStack if stack.stackSize > 0 => stack + case _ => null + } + } + else null + } + + override def getStackInSlotOnClosing(slot: Int) = { + if (slot >= 0 && slot < getSizeInventory) { + val stack = getStackInSlot(slot) + setInventorySlotContents(slot, null) + stack + } + else null + } + + override def clear(): Unit = { + for (slot <- 0 until getSizeInventory) { + setInventorySlotContents(slot, null) + } + } + + override def getField(id: Int) = 0 + + override def setField(id: Int, value: Int) {} + + override def getFieldCount = 0 +} diff --git a/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala b/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala index 151354086..6f8c2470e 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Disassembler.scala @@ -155,8 +155,6 @@ class Disassembler extends traits.Environment with traits.PowerAcceptor with tra override def getSizeInventory = 1 - override def getInventoryStackLimit = 64 - override def isItemValidForSlot(i: Int, stack: ItemStack) = allowDisassembling(stack) && (((Settings.get.disassembleAllTheThings || api.Items.get(stack) != null) && ItemUtils.getIngredients(stack).nonEmpty) || diff --git a/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala b/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala index cb8a169ca..10666c101 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Hologram.scala @@ -63,6 +63,16 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w var hasPower = true + // Rotation base state. Current rotation is based on world time. See HologramRenderer. + var rotationAngle = 0f + var rotationX = 0f + var rotationY = 0f + var rotationZ = 0f + var rotationSpeed = 0f + var rotationSpeedX = 0f + var rotationSpeedY = 0f + var rotationSpeedZ = 0f + final val colorsByTier = Array(Array(0x00FF00), Array(0x0000FF, 0x00FF00, 0xFF0000)) // 0xBBGGRR for rendering convenience // This is a def and not a val for loading (where the tier comes from the nbt and is always 0 here). @@ -293,6 +303,44 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w result(oldValue) } + @Callback(doc = """function(angle:number, x:number, y:number, z:number):boolean -- Set the base rotation of the displayed hologram.""") + def setRotation(context: Context, args: Arguments): Array[AnyRef] = { + if (tier > 0) { + val r = args.checkDouble(0) % 360 + val x = args.checkDouble(1) + val y = args.checkDouble(2) + val z = args.checkDouble(3) + + rotationAngle = r.toFloat + rotationX = x.toFloat + rotationY = y.toFloat + rotationZ = z.toFloat + ServerPacketSender.sendHologramRotation(this) + + result(true) + } + else result(null, "not supported") + } + + @Callback(doc = """function(speed:number, x:number, y:number, z:number):boolean -- Set the rotation speed of the displayed hologram.""") + def setRotationSpeed(context: Context, args: Arguments): Array[AnyRef] = { + if (tier > 0) { + val v = args.checkDouble(0) max -360 * 4 min 360 * 4 + val x = args.checkDouble(1) + val y = args.checkDouble(2) + val z = args.checkDouble(3) + + rotationSpeed = v.toFloat + rotationSpeedX = x.toFloat + rotationSpeedY = y.toFloat + rotationSpeedZ = z.toFloat + ServerPacketSender.sendHologramRotationSpeed(this) + + result(true) + } + else result(null, "not supported") + } + private def checkCoordinates(args: Arguments, idxX: Int = 0, idxY: Int = 1, idxZ: Int = 2) = { val x = if (idxX >= 0) args.checkInteger(idxX) - 1 else 0 if (x < 0 || x >= width) throw new ArrayIndexOutOfBoundsException("x") @@ -370,12 +418,14 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w def getFadeStartDistanceSquared = scale / Settings.get.hologramMaxScaleByTier.max * Settings.get.hologramFadeStartDistance * Settings.get.hologramFadeStartDistance + private final val Sqrt2 = Math.sqrt(2) + override def getRenderBoundingBox = { val cx = x + 0.5 val cy = y + 0.5 val cz = z + 0.5 - val sh = width / 16 * scale - val sv = height / 16 * scale + val sh = width / 16 * scale * Sqrt2 // overscale to take into account 45 degree rotation + val sv = height / 16 * scale * Sqrt2 AxisAlignedBB.fromBounds( cx + (-0.5 + translation.xCoord) * sh, cy + translation.yCoord * sv, @@ -398,6 +448,14 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w val ty = nbt.getDouble(Settings.namespace + "offsetY") val tz = nbt.getDouble(Settings.namespace + "offsetZ") translation = new Vec3(tx, ty, tz) + rotationAngle = nbt.getFloat(Settings.namespace + "rotationAngle") + rotationX = nbt.getFloat(Settings.namespace + "rotationX") + rotationY = nbt.getFloat(Settings.namespace + "rotationY") + rotationZ = nbt.getFloat(Settings.namespace + "rotationZ") + rotationSpeed = nbt.getFloat(Settings.namespace + "rotationSpeed") + rotationSpeedX = nbt.getFloat(Settings.namespace + "rotationSpeedX") + rotationSpeedY = nbt.getFloat(Settings.namespace + "rotationSpeedY") + rotationSpeedZ = nbt.getFloat(Settings.namespace + "rotationSpeedZ") } override def writeToNBTForServer(nbt: NBTTagCompound) = this.synchronized { @@ -413,6 +471,14 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w nbt.setDouble(Settings.namespace + "offsetX", translation.xCoord) nbt.setDouble(Settings.namespace + "offsetY", translation.yCoord) nbt.setDouble(Settings.namespace + "offsetZ", translation.zCoord) + nbt.setFloat(Settings.namespace + "rotationAngle", rotationAngle) + nbt.setFloat(Settings.namespace + "rotationX", rotationX) + nbt.setFloat(Settings.namespace + "rotationY", rotationY) + nbt.setFloat(Settings.namespace + "rotationZ", rotationZ) + nbt.setFloat(Settings.namespace + "rotationSpeed", rotationSpeed) + nbt.setFloat(Settings.namespace + "rotationSpeedX", rotationSpeedX) + nbt.setFloat(Settings.namespace + "rotationSpeedY", rotationSpeedY) + nbt.setFloat(Settings.namespace + "rotationSpeedZ", rotationSpeedZ) } @SideOnly(Side.CLIENT) @@ -426,6 +492,14 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w val ty = nbt.getDouble("offsetY") val tz = nbt.getDouble("offsetZ") translation = new Vec3(tx, ty, tz) + rotationAngle = nbt.getFloat("rotationAngle") + rotationX = nbt.getFloat("rotationX") + rotationY = nbt.getFloat("rotationY") + rotationZ = nbt.getFloat("rotationZ") + rotationSpeed = nbt.getFloat("rotationSpeed") + rotationSpeedX = nbt.getFloat("rotationSpeedX") + rotationSpeedY = nbt.getFloat("rotationSpeedY") + rotationSpeedZ = nbt.getFloat("rotationSpeedZ") } override def writeToNBTForClient(nbt: NBTTagCompound) { @@ -437,5 +511,13 @@ class Hologram(var tier: Int) extends traits.Environment with SidedEnvironment w nbt.setDouble("offsetX", translation.xCoord) nbt.setDouble("offsetY", translation.yCoord) nbt.setDouble("offsetZ", translation.zCoord) + nbt.setFloat("rotationAngle", rotationAngle) + nbt.setFloat("rotationX", rotationX) + nbt.setFloat("rotationY", rotationY) + nbt.setFloat("rotationZ", rotationZ) + nbt.setFloat("rotationSpeed", rotationSpeed) + nbt.setFloat("rotationSpeedX", rotationSpeedX) + nbt.setFloat("rotationSpeedY", rotationSpeedY) + nbt.setFloat("rotationSpeedZ", rotationSpeedZ) } } diff --git a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala index 32398b457..ffe21918f 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Microcontroller.scala @@ -235,6 +235,9 @@ class Microcontroller extends traits.PowerAcceptor with traits.Hub with traits.C // Nope. override def decrStackSize(slot: Int, amount: Int) = null + // Nope. + override def getStackInSlotOnClosing(slot: Int) = null + // For hotswapping EEPROMs. def changeEEPROM(newEeprom: ItemStack) = { val oldEepromIndex = info.components.indexWhere(api.Items.get(_) == api.Items.get(Constants.ItemName.EEPROM)) diff --git a/src/main/scala/li/cil/oc/common/tileentity/Printer.scala b/src/main/scala/li/cil/oc/common/tileentity/Printer.scala index 74c7fdea2..34918b455 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Printer.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Printer.scala @@ -325,8 +325,6 @@ class Printer extends traits.Environment with traits.Inventory with traits.Rotat override def getSizeInventory = 3 - override def getInventoryStackLimit = 64 - override def isItemValidForSlot(slot: Int, stack: ItemStack) = if (slot == slotMaterial) PrintData.materialValue(stack) > 0 diff --git a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala index 217887ba8..6b86994ff 100644 --- a/src/main/scala/li/cil/oc/common/tileentity/Robot.scala +++ b/src/main/scala/li/cil/oc/common/tileentity/Robot.scala @@ -14,6 +14,7 @@ import li.cil.oc.client.gui import li.cil.oc.common.EventHandler import li.cil.oc.common.Slot import li.cil.oc.common.Tier +import li.cil.oc.common.inventory.InventoryProxy import li.cil.oc.common.inventory.InventorySelection import li.cil.oc.common.inventory.TankSelection import li.cil.oc.common.item.data.RobotData @@ -32,7 +33,6 @@ import net.minecraft.block.BlockLiquid import net.minecraft.client.Minecraft import net.minecraft.entity.player.EntityPlayer import net.minecraft.init.Blocks -import net.minecraft.inventory.IInventory import net.minecraft.item.ItemStack import net.minecraft.nbt.NBTTagCompound import net.minecraft.util.AxisAlignedBB @@ -68,87 +68,19 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot def isCreative = tier == Tier.Four - val equipmentInventory = new IInventory { + val equipmentInventory = new InventoryProxy { + override def inventory = Robot.this + override def getSizeInventory = 4 - - override def getInventoryStackLimit = Robot.this.getInventoryStackLimit - - override def markDirty() = Robot.this.markDirty() - - override def isItemValidForSlot(slot: Int, stack: ItemStack) = - slot >= 0 && slot < getSizeInventory && Robot.this.isItemValidForSlot(slot, stack) - - override def getStackInSlot(slot: Int) = - if (slot >= 0 && slot < getSizeInventory) Robot.this.getStackInSlot(slot) - else null - - override def setInventorySlotContents(slot: Int, stack: ItemStack) = - if (slot >= 0 && slot < getSizeInventory) Robot.this.setInventorySlotContents(slot, stack) - - override def decrStackSize(slot: Int, amount: Int) = - if (slot >= 0 && slot < getSizeInventory) Robot.this.decrStackSize(slot, amount) - else null - - override def getName = Robot.this.getName - - override def hasCustomName = Robot.this.hasCustomName - - override def openInventory(player: EntityPlayer): Unit = {} - - override def closeInventory(player: EntityPlayer): Unit = {} - - override def getStackInSlotOnClosing(slot: Int): ItemStack = null - - override def isUseableByPlayer(player: EntityPlayer) = Robot.this.isUseableByPlayer(player) - - override def getField(id: Int) = Robot.this.getField(id) - - override def setField(id: Int, value: Int) = Robot.this.setField(id, value) - - override def getFieldCount = Robot.this.getFieldCount - - override def clear() = Robot.this.clear() - - override def getDisplayName = Robot.this.getDisplayName } // Wrapper for the part of the inventory that is mutable. - val mainInventory = new IInventory { + val mainInventory = new InventoryProxy { + override def inventory = Robot.this + override def getSizeInventory = Robot.this.inventorySize - override def getInventoryStackLimit = Robot.this.getInventoryStackLimit - - override def markDirty() = Robot.this.markDirty() - - override def isItemValidForSlot(slot: Int, stack: ItemStack) = Robot.this.isItemValidForSlot(equipmentInventory.getSizeInventory + slot, stack) - - override def getStackInSlot(slot: Int) = Robot.this.getStackInSlot(equipmentInventory.getSizeInventory + slot) - - override def setInventorySlotContents(slot: Int, stack: ItemStack) = Robot.this.setInventorySlotContents(equipmentInventory.getSizeInventory + slot, stack) - - override def decrStackSize(slot: Int, amount: Int) = Robot.this.decrStackSize(equipmentInventory.getSizeInventory + slot, amount) - - override def getName = Robot.this.getName - - override def hasCustomName = Robot.this.hasCustomName - - override def openInventory(player: EntityPlayer): Unit = {} - - override def closeInventory(player: EntityPlayer): Unit = {} - - override def getStackInSlotOnClosing(slot: Int): ItemStack = null - - override def isUseableByPlayer(player: EntityPlayer) = Robot.this.isUseableByPlayer(player) - - override def getField(id: Int) = Robot.this.getField(id) - - override def setField(id: Int, value: Int) = Robot.this.setField(id, value) - - override def getFieldCount = Robot.this.getFieldCount - - override def clear() = Robot.this.clear() - - override def getDisplayName = Robot.this.getDisplayName + override def offset = equipmentInventory.getSizeInventory } val actualInventorySize = 100 @@ -760,8 +692,6 @@ class Robot extends traits.Computer with traits.PowerInformation with traits.Rot var getSizeInventory = actualInventorySize - override def getInventoryStackLimit = 64 - override def getStackInSlot(slot: Int) = { if (slot >= getSizeInventory) null // Required to always show 16 inventory slots in GUI. else if (slot >= getSizeInventory - componentCount) { diff --git a/src/main/scala/li/cil/oc/server/PacketSender.scala b/src/main/scala/li/cil/oc/server/PacketSender.scala index 9975e0deb..ea400d2b3 100644 --- a/src/main/scala/li/cil/oc/server/PacketSender.scala +++ b/src/main/scala/li/cil/oc/server/PacketSender.scala @@ -215,6 +215,30 @@ object PacketSender { pb.sendToPlayersNearTileEntity(t) } + def sendHologramRotation(t: tileentity.Hologram) { + val pb = new SimplePacketBuilder(PacketType.HologramRotation) + + pb.writeTileEntity(t) + pb.writeFloat(t.rotationAngle) + pb.writeFloat(t.rotationX) + pb.writeFloat(t.rotationY) + pb.writeFloat(t.rotationZ) + + pb.sendToPlayersNearTileEntity(t) + } + + def sendHologramRotationSpeed(t: tileentity.Hologram) { + val pb = new SimplePacketBuilder(PacketType.HologramRotationSpeed) + + pb.writeTileEntity(t) + pb.writeFloat(t.rotationSpeed) + pb.writeFloat(t.rotationSpeedX) + pb.writeFloat(t.rotationSpeedY) + pb.writeFloat(t.rotationSpeedZ) + + pb.sendToPlayersNearTileEntity(t) + } + def sendLootDisks(p: EntityPlayerMP): Unit = { // Sending as separate packets, because CompressedStreamTools hiccups otherwise... val stacks = Loot.worldDisks.values.map(_._1) diff --git a/src/main/scala/li/cil/oc/server/agent/Player.scala b/src/main/scala/li/cil/oc/server/agent/Player.scala index 6e4ca65ac..6aa423645 100644 --- a/src/main/scala/li/cil/oc/server/agent/Player.scala +++ b/src/main/scala/li/cil/oc/server/agent/Player.scala @@ -527,7 +527,12 @@ class Player(val agent: internal.Agent) extends FakePlayer(agent.world.asInstanc override def onItemPickup(entity: Entity, count: Int) {} - override def setCurrentItemOrArmor(slot: Int, stack: ItemStack) {} + override def setCurrentItemOrArmor(slot: Int, stack: ItemStack): Unit = { + if (slot == 0 && agent.equipmentInventory.getSizeInventory > 0) { + agent.equipmentInventory.setInventorySlotContents(slot, stack) + } + // else: armor slots, which are unsupported in agents. + } override def setRevengeTarget(entity: EntityLivingBase) {} diff --git a/src/main/scala/li/cil/oc/server/machine/luac/OSAPI.scala b/src/main/scala/li/cil/oc/server/machine/luac/OSAPI.scala index 6ae6d04bf..50d9cba2f 100644 --- a/src/main/scala/li/cil/oc/server/machine/luac/OSAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/luac/OSAPI.scala @@ -24,8 +24,8 @@ class OSAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) { if (lua.getTop > 0 && lua.isString(1)) lua.toString(1) else "%d/%m/%y %H:%M:%S" val time = - if (lua.getTop > 1 && lua.isNumber(2)) lua.toNumber(2) * 1000 / 60 / 60 - else machine.worldTime + 6000 + if (lua.getTop > 1 && lua.isNumber(2)) lua.toNumber(2) + else (machine.worldTime + 6000) * 60 * 60 / 1000 val dt = GameTimeFormatter.parse(time) def fmt(format: String) { diff --git a/src/main/scala/li/cil/oc/server/machine/luac/UnicodeAPI.scala b/src/main/scala/li/cil/oc/server/machine/luac/UnicodeAPI.scala index 7c6854376..0be3f1ead 100644 --- a/src/main/scala/li/cil/oc/server/machine/luac/UnicodeAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/luac/UnicodeAPI.scala @@ -1,7 +1,7 @@ package li.cil.oc.server.machine.luac import li.cil.oc.util.ExtendedLuaState.extendLuaState -import li.cil.oc.util.FontUtil +import li.cil.oc.util.FontUtils class UnicodeAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) { override def initialize() { @@ -57,20 +57,20 @@ class UnicodeAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) { lua.setField(-2, "upper") lua.pushScalaFunction(lua => { - lua.pushBoolean(FontUtil.wcwidth(lua.checkString(1).codePointAt(0)) > 1) + lua.pushBoolean(FontUtils.wcwidth(lua.checkString(1).codePointAt(0)) > 1) 1 }) lua.setField(-2, "isWide") lua.pushScalaFunction(lua => { - lua.pushInteger(FontUtil.wcwidth(lua.checkString(1).codePointAt(0))) + lua.pushInteger(FontUtils.wcwidth(lua.checkString(1).codePointAt(0))) 1 }) lua.setField(-2, "charWidth") lua.pushScalaFunction(lua => { val value = lua.checkString(1) - lua.pushInteger(value.toCharArray.map(ch => math.max(1, FontUtil.wcwidth(ch))).sum) + lua.pushInteger(value.toCharArray.map(ch => math.max(1, FontUtils.wcwidth(ch))).sum) 1 }) lua.setField(-2, "wlen") @@ -81,7 +81,7 @@ class UnicodeAPI(owner: NativeLuaArchitecture) extends NativeLuaAPI(owner) { var width = 0 var end = 0 while (width < count) { - width += FontUtil.wcwidth(value(end)) + width += FontUtils.wcwidth(value(end)) end += 1 } if (end > 1) lua.pushString(value.substring(0, end - 1)) diff --git a/src/main/scala/li/cil/oc/server/machine/luaj/OSAPI.scala b/src/main/scala/li/cil/oc/server/machine/luaj/OSAPI.scala index b0050abc7..06e721194 100644 --- a/src/main/scala/li/cil/oc/server/machine/luaj/OSAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/luaj/OSAPI.scala @@ -17,8 +17,8 @@ class OSAPI(owner: LuaJLuaArchitecture) extends LuaJAPI(owner) { if (args.narg > 0 && args.isstring(1)) args.tojstring(1) else "%d/%m/%y %H:%M:%S" val time = - if (args.narg > 1 && args.isnumber(2)) args.todouble(2) * 1000 / 60 / 60 - else machine.worldTime + 6000 + if (args.narg > 1 && args.isnumber(2)) args.todouble(2) + else (machine.worldTime + 6000) * 60 * 60 / 1000 val dt = GameTimeFormatter.parse(time) def fmt(format: String) = { diff --git a/src/main/scala/li/cil/oc/server/machine/luaj/UnicodeAPI.scala b/src/main/scala/li/cil/oc/server/machine/luaj/UnicodeAPI.scala index fc876c1d8..b791210e1 100644 --- a/src/main/scala/li/cil/oc/server/machine/luaj/UnicodeAPI.scala +++ b/src/main/scala/li/cil/oc/server/machine/luaj/UnicodeAPI.scala @@ -1,6 +1,6 @@ package li.cil.oc.server.machine.luaj -import li.cil.oc.util.FontUtil +import li.cil.oc.util.FontUtils import li.cil.oc.util.ScalaClosure._ import li.cil.repack.org.luaj.vm2.LuaValue import li.cil.repack.org.luaj.vm2.Varargs @@ -37,14 +37,14 @@ class UnicodeAPI(owner: LuaJLuaArchitecture) extends LuaJAPI(owner) { }) unicode.set("isWide", (args: Varargs) => - LuaValue.valueOf(FontUtil.wcwidth(args.checkjstring(1).codePointAt(0)) > 1)) + LuaValue.valueOf(FontUtils.wcwidth(args.checkjstring(1).codePointAt(0)) > 1)) unicode.set("charWidth", (args: Varargs) => - LuaValue.valueOf(FontUtil.wcwidth(args.checkjstring(1).codePointAt(0)))) + LuaValue.valueOf(FontUtils.wcwidth(args.checkjstring(1).codePointAt(0)))) unicode.set("wlen", (args: Varargs) => { val value = args.checkjstring(1) - LuaValue.valueOf(value.toCharArray.map(ch => math.max(1, FontUtil.wcwidth(ch))).sum) + LuaValue.valueOf(value.toCharArray.map(ch => math.max(1, FontUtils.wcwidth(ch))).sum) }) unicode.set("wtrunc", (args: Varargs) => { @@ -53,7 +53,7 @@ class UnicodeAPI(owner: LuaJLuaArchitecture) extends LuaJAPI(owner) { var width = 0 var end = 0 while (width < count) { - width += FontUtil.wcwidth(value(end)) + width += FontUtils.wcwidth(value(end)) end += 1 } if (end > 1) LuaValue.valueOf(value.substring(0, end - 1)) diff --git a/src/main/scala/li/cil/oc/util/FontUtil.scala b/src/main/scala/li/cil/oc/util/FontUtils.scala similarity index 87% rename from src/main/scala/li/cil/oc/util/FontUtil.scala rename to src/main/scala/li/cil/oc/util/FontUtils.scala index d1e7c4e47..dcc614c84 100644 --- a/src/main/scala/li/cil/oc/util/FontUtil.scala +++ b/src/main/scala/li/cil/oc/util/FontUtils.scala @@ -4,14 +4,14 @@ import java.io.IOException import li.cil.oc.OpenComputers -object FontUtil { +object FontUtils { // Note: we load the widths from a file (one byte per width) because the Scala // compiler craps its pants when we try to have it as an array in the source // file... seems having an array with 0x10000 entries leads to stack overflows, // who would have known! private val widths = { val ba = Array.fill[Byte](0x10000)(-1) - val is = FontUtil.getClass.getResourceAsStream("/assets/opencomputers/wcwidth.bin") + val is = FontUtils.getClass.getResourceAsStream("/assets/opencomputers/wcwidth.bin") if (is != null) { try { is.read(ba) diff --git a/src/main/scala/li/cil/oc/util/GameTimeFormatter.scala b/src/main/scala/li/cil/oc/util/GameTimeFormatter.scala index fe57294e3..d3e4834ca 100644 --- a/src/main/scala/li/cil/oc/util/GameTimeFormatter.scala +++ b/src/main/scala/li/cil/oc/util/GameTimeFormatter.scala @@ -1,5 +1,8 @@ package li.cil.oc.util +import java.util.Calendar +import java.util.GregorianCalendar + import scala.collection.mutable object GameTimeFormatter { @@ -56,34 +59,19 @@ object GameTimeFormatter { '%' -> (t => "%") ) - private val monthLengths = Array( - Array(31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31), - Array(31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)) - - private def monthLengthsForYear(year: Int) = { - if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) monthLengths(0) else monthLengths(1) - } - def parse(time: Double) = { - var day = (time / 24000).toLong - val weekDay = ((4 + day) % 7).toInt - val year = 1970 + (day / 364.2425).toInt - val yearDay = (day % 364.2425).toInt - day = yearDay - val monthLengths = monthLengthsForYear(year) - var month = 0 - while (day >= monthLengths(month)) { - day = day - monthLengths(month) - month = month + 1 - } + val calendar = new GregorianCalendar() + calendar.setTimeInMillis((time * 1000).toLong) - var seconds = ((time % 24000) * 60 * 60 / 1000).toInt - var minutes = seconds / 60 - seconds = seconds % 60 - val hours = (minutes / 60) % 24 - minutes = minutes % 60 - - new DateTime(year, month + 1, day.toInt + 1, weekDay + 1, yearDay + 1, hours, minutes, seconds) + new DateTime( + calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH) + 1, + calendar.get(Calendar.DAY_OF_MONTH), + calendar.get(Calendar.DAY_OF_WEEK), + calendar.get(Calendar.DAY_OF_YEAR), + calendar.get(Calendar.HOUR_OF_DAY), + calendar.get(Calendar.MINUTE), + calendar.get(Calendar.SECOND)) } def format(format: String, time: DateTime) = { @@ -103,10 +91,14 @@ object GameTimeFormatter { } def mktime(year: Int, mon: Int, mday: Int, hour: Int, min: Int, sec: Int): Option[Int] = { - if (year < 1970 || mon < 1 || mon > 12) return None - val monthLengths = monthLengthsForYear(year) - val days = ((year - 1970) * 365.2425).ceil.toInt + (0 until mon - 1).foldLeft(0)((d, m) => d + monthLengths(m)) + mday - 1 - val secs = sec + (min + (hour - 1 + days * 24) * 60) * 60 - Option(secs) + val calendar = new GregorianCalendar() + calendar.set(Calendar.YEAR, year) + calendar.set(Calendar.MONTH, mon - 1) + calendar.set(Calendar.DAY_OF_MONTH, mday) + calendar.set(Calendar.HOUR_OF_DAY, hour) + calendar.set(Calendar.MINUTE, min) + calendar.set(Calendar.SECOND, sec) + + Option((calendar.getTimeInMillis / 1000).toInt) } } diff --git a/src/main/scala/li/cil/oc/util/PlayerUtils.scala b/src/main/scala/li/cil/oc/util/PlayerUtils.scala new file mode 100644 index 000000000..5a80710b1 --- /dev/null +++ b/src/main/scala/li/cil/oc/util/PlayerUtils.scala @@ -0,0 +1,14 @@ +package li.cil.oc.util + +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.nbt.NBTTagCompound + +object PlayerUtils { + def persistedData(player: EntityPlayer): NBTTagCompound = { + val nbt = player.getEntityData + if (!nbt.hasKey(EntityPlayer.PERSISTED_NBT_TAG)) { + nbt.setTag(EntityPlayer.PERSISTED_NBT_TAG, new NBTTagCompound()) + } + nbt.getCompoundTag(EntityPlayer.PERSISTED_NBT_TAG) + } +} diff --git a/src/main/scala/li/cil/oc/util/TextBuffer.scala b/src/main/scala/li/cil/oc/util/TextBuffer.scala index bcf4c0b07..f0f7f6713 100644 --- a/src/main/scala/li/cil/oc/util/TextBuffer.scala +++ b/src/main/scala/li/cil/oc/util/TextBuffer.scala @@ -129,7 +129,7 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col val c = s(x - col) changed = changed || (line(bx) != c) || (lineColor(bx) != packed) setChar(line, lineColor, bx, c) - bx += math.max(1, FontUtil.wcwidth(c)) + bx += math.max(1, FontUtils.wcwidth(c)) } changed } @@ -148,7 +148,7 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col for (x <- bx until math.min(col + w, width) if bx < line.length) { changed = changed || (line(bx) != c) || (lineColor(bx) != packed) setChar(line, lineColor, bx, c) - bx += math.max(1, FontUtil.wcwidth(c)) + bx += math.max(1, FontUtils.wcwidth(c)) } } changed @@ -185,7 +185,7 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col changed = changed || (nl(nx) != ol(ox)) || (nc(nx) != oc(ox)) nl(nx) = ol(ox) nc(nx) = oc(ox) - for (offset <- 1 until FontUtil.wcwidth(nl(nx))) { + for (offset <- 1 until FontUtils.wcwidth(nl(nx))) { nl(nx + offset) = ol(' ') nc(nx + offset) = oc(nx) } @@ -198,17 +198,17 @@ class TextBuffer(var width: Int, var height: Int, initialFormat: PackedColor.Col } private def setChar(line: Array[Char], lineColor: Array[Short], x: Int, c: Char) { - if (FontUtil.wcwidth(c) > 1 && x >= line.length - 1) { + if (FontUtils.wcwidth(c) > 1 && x >= line.length - 1) { // Don't allow setting wide chars in right-most col. return } - if (x > 0 && line(x) == ' ' && FontUtil.wcwidth(line(x - 1)) > 1) { + if (x > 0 && line(x) == ' ' && FontUtils.wcwidth(line(x - 1)) > 1) { // Don't allow setting the cell following a wide char. return } line(x) = c lineColor(x) = packed - for (x1 <- x + 1 until x + FontUtil.wcwidth(c)) { + for (x1 <- x + 1 until x + FontUtils.wcwidth(c)) { line(x1) = ' ' lineColor(x1) = packed }