mirror of
https://github.com/GTNewHorizons/ForgeMultipart.git
synced 2026-11-13 17:04:25 +00:00
[ci skip] spotlessApply with the new settings
This commit is contained in:
1 parent
f02b6bae19
commit
6b18eca613
89 files changed
+8520
-7190
No files matched your search
@@ -1,13 +1,5 @@
|
||||
package codechicken.core.launch;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import cpw.mods.fml.common.versioning.ComparableVersion;
|
||||
import cpw.mods.fml.relauncher.FMLInjectionData;
|
||||
import cpw.mods.fml.relauncher.FMLLaunchHandler;
|
||||
import cpw.mods.fml.relauncher.IFMLCallHook;
|
||||
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||
import java.awt.*;
|
||||
import java.awt.Dialog.ModalityType;
|
||||
import java.awt.event.WindowAdapter;
|
||||
@@ -28,23 +20,37 @@ import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
|
||||
import net.minecraft.launchwrapper.LaunchClassLoader;
|
||||
|
||||
import sun.misc.URLClassPath;
|
||||
import sun.net.util.URLUtil;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
|
||||
import cpw.mods.fml.common.versioning.ComparableVersion;
|
||||
import cpw.mods.fml.relauncher.FMLInjectionData;
|
||||
import cpw.mods.fml.relauncher.FMLLaunchHandler;
|
||||
import cpw.mods.fml.relauncher.IFMLCallHook;
|
||||
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
|
||||
|
||||
/**
|
||||
* For autodownloading stuff.
|
||||
* This is really unoriginal, mostly ripped off FML, credits to cpw.
|
||||
* For autodownloading stuff. This is really unoriginal, mostly ripped off FML, credits to cpw.
|
||||
*/
|
||||
public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
|
||||
|
||||
private static ByteBuffer downloadBuffer = ByteBuffer.allocateDirect(1 << 23);
|
||||
private static final String owner = "CB's DepLoader";
|
||||
private static DepLoadInst inst;
|
||||
|
||||
public interface IDownloadDisplay {
|
||||
|
||||
void resetProgress(int sizeGuess);
|
||||
|
||||
void setPokeThread(Thread currentThread);
|
||||
@@ -62,6 +68,7 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public static class Downloader extends JOptionPane implements IDownloadDisplay {
|
||||
|
||||
private JDialog container;
|
||||
private JLabel currentActivity;
|
||||
private JProgressBar progress;
|
||||
@@ -71,12 +78,13 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
|
||||
private Box makeProgressPanel() {
|
||||
Box box = Box.createVerticalBox();
|
||||
box.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
JLabel welcomeLabel = new JLabel("<html><b><font size='+1'>" + owner
|
||||
+ " is setting up your minecraft environment</font></b></html>");
|
||||
JLabel welcomeLabel = new JLabel(
|
||||
"<html><b><font size='+1'>" + owner
|
||||
+ " is setting up your minecraft environment</font></b></html>");
|
||||
box.add(welcomeLabel);
|
||||
welcomeLabel.setAlignmentY(LEFT_ALIGNMENT);
|
||||
welcomeLabel =
|
||||
new JLabel("<html>Please wait, " + owner + " has some tasks to do before you can play</html>");
|
||||
welcomeLabel = new JLabel(
|
||||
"<html>Please wait, " + owner + " has some tasks to do before you can play</html>");
|
||||
welcomeLabel.setAlignmentY(LEFT_ALIGNMENT);
|
||||
box.add(welcomeLabel);
|
||||
box.add(Box.createRigidArea(new Dimension(0, 10)));
|
||||
@@ -96,8 +104,9 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
|
||||
|
||||
setMessageType(JOptionPane.INFORMATION_MESSAGE);
|
||||
setMessage(makeProgressPanel());
|
||||
setOptions(new Object[] {"Stop"});
|
||||
setOptions(new Object[] { "Stop" });
|
||||
addPropertyChangeListener(new PropertyChangeListener() {
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (evt.getSource() == Downloader.this && evt.getPropertyName() == VALUE_PROPERTY) {
|
||||
@@ -115,6 +124,7 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
|
||||
container.setVisible(true);
|
||||
container.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
|
||||
container.addWindowListener(new WindowAdapter() {
|
||||
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
requestClose(
|
||||
@@ -168,21 +178,26 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
|
||||
JEditorPane ep = new JEditorPane(
|
||||
"text/html",
|
||||
"<html>" + owner
|
||||
+ " was unable to download required library " + name
|
||||
+ " was unable to download required library "
|
||||
+ name
|
||||
+ "<br>Check your internet connection and try restarting or download it manually from"
|
||||
+ "<br><a href=\""
|
||||
+ url + "\">" + url + "</a> and put it in your mods folder" + "</html>");
|
||||
+ url
|
||||
+ "\">"
|
||||
+ url
|
||||
+ "</a> and put it in your mods folder"
|
||||
+ "</html>");
|
||||
|
||||
ep.setEditable(false);
|
||||
ep.setOpaque(false);
|
||||
ep.addHyperlinkListener(new HyperlinkListener() {
|
||||
|
||||
@Override
|
||||
public void hyperlinkUpdate(HyperlinkEvent event) {
|
||||
try {
|
||||
if (event.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED))
|
||||
Desktop.getDesktop().browse(event.getURL().toURI());
|
||||
} catch (Exception e) {
|
||||
}
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -191,6 +206,7 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
|
||||
}
|
||||
|
||||
public static class DummyDownloader implements IDownloadDisplay {
|
||||
|
||||
@Override
|
||||
public void resetProgress(int sizeGuess) {}
|
||||
|
||||
@@ -218,6 +234,7 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
|
||||
}
|
||||
|
||||
public static class VersionedFile {
|
||||
|
||||
public final Pattern pattern;
|
||||
public final String filename;
|
||||
public final ComparableVersion version;
|
||||
@@ -242,6 +259,7 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
|
||||
}
|
||||
|
||||
public static class Dependency {
|
||||
|
||||
public String url;
|
||||
public VersionedFile file;
|
||||
|
||||
@@ -259,6 +277,7 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
|
||||
}
|
||||
|
||||
public static class DepLoadInst {
|
||||
|
||||
private File modsDir;
|
||||
private File v_modsDir;
|
||||
private IDownloadDisplay downloadMonitor;
|
||||
@@ -310,7 +329,8 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
|
||||
|
||||
if (!mod.delete()) {
|
||||
mod.deleteOnExit();
|
||||
String msg = owner + " was unable to delete file " + mod.getPath()
|
||||
String msg = owner + " was unable to delete file "
|
||||
+ mod.getPath()
|
||||
+ " the game will now try to delete it on exit. If this dialog appears again, delete it manually.";
|
||||
System.err.println(msg);
|
||||
if (!GraphicsEnvironment.isHeadless())
|
||||
@@ -350,10 +370,10 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
|
||||
}
|
||||
|
||||
private void download(InputStream is, int sizeGuess, File target) throws Exception {
|
||||
if (sizeGuess > downloadBuffer.capacity())
|
||||
throw new Exception(String.format(
|
||||
"The file %s is too large to be downloaded by " + owner + " - the download is invalid",
|
||||
target.getName()));
|
||||
if (sizeGuess > downloadBuffer.capacity()) throw new Exception(
|
||||
String.format(
|
||||
"The file %s is too large to be downloaded by " + owner + " - the download is invalid",
|
||||
target.getName()));
|
||||
|
||||
downloadBuffer.clear();
|
||||
|
||||
@@ -384,20 +404,20 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
|
||||
}
|
||||
|
||||
try {
|
||||
/*String cksum = generateChecksum(downloadBuffer);
|
||||
if (cksum.equals(validationHash))
|
||||
{*/
|
||||
/*
|
||||
* String cksum = generateChecksum(downloadBuffer); if (cksum.equals(validationHash)) {
|
||||
*/
|
||||
if (!target.exists()) target.createNewFile();
|
||||
|
||||
downloadBuffer.position(0);
|
||||
FileOutputStream fos = new FileOutputStream(target);
|
||||
fos.getChannel().write(downloadBuffer);
|
||||
fos.close();
|
||||
/*}
|
||||
else
|
||||
{
|
||||
throw new RuntimeException(String.format("The downloaded file %s has an invalid checksum %s (expecting %s). The download did not succeed correctly and the file has been deleted. Please try launching again.", target.getName(), cksum, validationHash));
|
||||
}*/
|
||||
/*
|
||||
* } else { throw new RuntimeException(String.
|
||||
* format("The downloaded file %s has an invalid checksum %s (expecting %s). The download did not succeed correctly and the file has been deleted. Please try launching again."
|
||||
* , target.getName(), cksum, validationHash)); }
|
||||
*/
|
||||
} catch (Exception e) {
|
||||
throw e;
|
||||
}
|
||||
@@ -424,8 +444,12 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
|
||||
return null;
|
||||
}
|
||||
if (cmp > 0) {
|
||||
System.err.println("Warning: version of " + dep.file.name + ", " + vfile.version
|
||||
+ " is newer than request " + dep.file.version);
|
||||
System.err.println(
|
||||
"Warning: version of " + dep.file.name
|
||||
+ ", "
|
||||
+ vfile.version
|
||||
+ " is newer than request "
|
||||
+ dep.file.version);
|
||||
return f.getName();
|
||||
}
|
||||
return f.getName(); // found dependency
|
||||
@@ -512,9 +536,8 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
|
||||
}
|
||||
|
||||
private void loadJson(JsonObject node) throws IOException {
|
||||
boolean obfuscated =
|
||||
((LaunchClassLoader) DepLoader.class.getClassLoader()).getClassBytes("net.minecraft.world.World")
|
||||
== null;
|
||||
boolean obfuscated = ((LaunchClassLoader) DepLoader.class.getClassLoader())
|
||||
.getClassBytes("net.minecraft.world.World") == null;
|
||||
|
||||
String testClass = node.get("class").getAsString();
|
||||
if (DepLoader.class.getResource("/" + testClass.replace('.', '/') + ".class") != null) return;
|
||||
@@ -527,8 +550,7 @@ public class DepLoader implements IFMLLoadingPlugin, IFMLCallHook {
|
||||
|
||||
Pattern pattern = null;
|
||||
try {
|
||||
if (node.has("pattern"))
|
||||
pattern = Pattern.compile(node.get("pattern").getAsString());
|
||||
if (node.has("pattern")) pattern = Pattern.compile(node.get("pattern").getAsString());
|
||||
} catch (PatternSyntaxException e) {
|
||||
System.err.println("Invalid filename pattern: " + node.get("pattern"));
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -8,132 +8,168 @@ import cpw.mods.fml.relauncher.Side
|
||||
import net.minecraft.item.ItemStack
|
||||
import codechicken.lib.vec.{Cuboid6, Vector3}
|
||||
import net.minecraft.entity.player.EntityPlayer
|
||||
import codechicken.lib.render.{CCRenderPipeline, ColourMultiplier, CCRenderState}
|
||||
import codechicken.lib.render.{
|
||||
CCRenderPipeline,
|
||||
ColourMultiplier,
|
||||
CCRenderState
|
||||
}
|
||||
import codechicken.microblock.handler.MicroblockProxy
|
||||
import net.minecraft.entity.Entity
|
||||
import codechicken.lib.render.uv.{UVTransformation, MultiIconTransformation}
|
||||
import cpw.mods.fml.common.registry.{GameRegistry, GameData}
|
||||
|
||||
object MaterialRenderHelper
|
||||
{
|
||||
private var pass = 0
|
||||
private var builder:CCRenderPipeline#PipelineBuilder = _
|
||||
object MaterialRenderHelper {
|
||||
private var pass = 0
|
||||
private var builder: CCRenderPipeline#PipelineBuilder = _
|
||||
|
||||
def start(pos:Vector3, pass:Int, uvt:UVTransformation) = {
|
||||
this.pass = pass
|
||||
builder = CCRenderState.pipeline.builder()
|
||||
builder.add(pos.translation()).add(uvt)
|
||||
this
|
||||
}
|
||||
def start(pos: Vector3, pass: Int, uvt: UVTransformation) = {
|
||||
this.pass = pass
|
||||
builder = CCRenderState.pipeline.builder()
|
||||
builder.add(pos.translation()).add(uvt)
|
||||
this
|
||||
}
|
||||
|
||||
def blockColour(colour:Int) = {
|
||||
builder.add(ColourMultiplier.instance(colour))
|
||||
this
|
||||
}
|
||||
def blockColour(colour: Int) = {
|
||||
builder.add(ColourMultiplier.instance(colour))
|
||||
this
|
||||
}
|
||||
|
||||
def lighting() = {
|
||||
if(pass != -1)
|
||||
builder.add(CCRenderState.lightMatrix)
|
||||
this
|
||||
}
|
||||
def lighting() = {
|
||||
if (pass != -1)
|
||||
builder.add(CCRenderState.lightMatrix)
|
||||
this
|
||||
}
|
||||
|
||||
def render() = builder.render()
|
||||
def render() = builder.render()
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard micro material class suitable for most blocks.
|
||||
*/
|
||||
class BlockMicroMaterial(val block:Block, val meta:Int = 0) extends IMicroMaterial
|
||||
{
|
||||
val blockKey = Block.blockRegistry.getNameForObject(block)
|
||||
/** Standard micro material class suitable for most blocks.
|
||||
*/
|
||||
class BlockMicroMaterial(val block: Block, val meta: Int = 0)
|
||||
extends IMicroMaterial {
|
||||
val blockKey = Block.blockRegistry.getNameForObject(block)
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
var icont:MultiIconTransformation = _
|
||||
@SideOnly(Side.CLIENT)
|
||||
var icont: MultiIconTransformation = _
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def loadIcons() {
|
||||
def safeIcon(block:Block, side:Int):IIcon = {
|
||||
try {
|
||||
return MicroblockProxy.renderBlocks.getIconSafe(block.getIcon(side, meta))
|
||||
}
|
||||
catch {
|
||||
case e:Exception =>
|
||||
}
|
||||
return MicroblockProxy.renderBlocks.getIconSafe(null)
|
||||
}
|
||||
|
||||
val iblock = Block.getBlockFromName(blockKey)//reacquire block instance incase a mod replcaed it
|
||||
icont = new MultiIconTransformation(Array.tabulate(6)(side => safeIcon(iblock, side)):_*)
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def loadIcons() {
|
||||
def safeIcon(block: Block, side: Int): IIcon = {
|
||||
try {
|
||||
return MicroblockProxy.renderBlocks.getIconSafe(
|
||||
block.getIcon(side, meta)
|
||||
)
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
}
|
||||
return MicroblockProxy.renderBlocks.getIconSafe(null)
|
||||
}
|
||||
|
||||
def renderMicroFace(pos:Vector3, pass:Int, bounds:Cuboid6) {
|
||||
MaterialRenderHelper.start(pos, pass, icont).blockColour(getColour(pass)).lighting().render()
|
||||
}
|
||||
val iblock = Block.getBlockFromName(
|
||||
blockKey
|
||||
) // reacquire block instance incase a mod replcaed it
|
||||
icont = new MultiIconTransformation(
|
||||
Array.tabulate(6)(side => safeIcon(iblock, side)): _*
|
||||
)
|
||||
}
|
||||
|
||||
def getColour(pass:Int) = {
|
||||
if(pass == -1)
|
||||
block.getBlockColor<<8|0xFF
|
||||
else {
|
||||
val pos = CCRenderState.lightMatrix.pos
|
||||
block.colorMultiplier(CCRenderState.lightMatrix.access, pos.x, pos.y, pos.z)<<8|0xFF
|
||||
}
|
||||
}
|
||||
def renderMicroFace(pos: Vector3, pass: Int, bounds: Cuboid6) {
|
||||
MaterialRenderHelper
|
||||
.start(pos, pass, icont)
|
||||
.blockColour(getColour(pass))
|
||||
.lighting()
|
||||
.render()
|
||||
}
|
||||
|
||||
override def canRenderInPass(pass:Int) = block.canRenderInPass(pass)
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
def getBreakingIcon(side:Int) = block.getIcon(side, meta)
|
||||
|
||||
def getItem = new ItemStack(block, 1, meta)
|
||||
|
||||
def getLocalizedName = getItem.getDisplayName
|
||||
|
||||
def getStrength(player:EntityPlayer) = {
|
||||
var hardness = 30F
|
||||
try {
|
||||
hardness = block.getBlockHardness(null, 0, 0, 0)
|
||||
} catch {
|
||||
case e:Exception =>
|
||||
}
|
||||
player.getBreakSpeed(block, false, meta%16, 0, -1, 0) / hardness
|
||||
def getColour(pass: Int) = {
|
||||
if (pass == -1)
|
||||
block.getBlockColor << 8 | 0xff
|
||||
else {
|
||||
val pos = CCRenderState.lightMatrix.pos
|
||||
block.colorMultiplier(
|
||||
CCRenderState.lightMatrix.access,
|
||||
pos.x,
|
||||
pos.y,
|
||||
pos.z
|
||||
) << 8 | 0xff
|
||||
}
|
||||
|
||||
def isTransparent = !block.isOpaqueCube
|
||||
|
||||
def getLightValue = block.getLightValue
|
||||
|
||||
def toolClasses = Seq("axe", "pickaxe", "shovel")
|
||||
|
||||
// meta > 15 will throw ArrayIndexOutOfBoundsException
|
||||
def getCutterStrength = block.getHarvestLevel(meta % 16)
|
||||
|
||||
def getSound = block.stepSound
|
||||
}
|
||||
|
||||
def explosionResistance(entity:Entity):Float = block.getExplosionResistance(entity)
|
||||
override def canRenderInPass(pass: Int) = block.canRenderInPass(pass)
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
def getBreakingIcon(side: Int) = block.getIcon(side, meta)
|
||||
|
||||
def getItem = new ItemStack(block, 1, meta)
|
||||
|
||||
def getLocalizedName = getItem.getDisplayName
|
||||
|
||||
def getStrength(player: EntityPlayer) = {
|
||||
var hardness = 30f
|
||||
try {
|
||||
hardness = block.getBlockHardness(null, 0, 0, 0)
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
}
|
||||
player.getBreakSpeed(block, false, meta % 16, 0, -1, 0) / hardness
|
||||
}
|
||||
|
||||
def isTransparent = !block.isOpaqueCube
|
||||
|
||||
def getLightValue = block.getLightValue
|
||||
|
||||
def toolClasses = Seq("axe", "pickaxe", "shovel")
|
||||
|
||||
// meta > 15 will throw ArrayIndexOutOfBoundsException
|
||||
def getCutterStrength = block.getHarvestLevel(meta % 16)
|
||||
|
||||
def getSound = block.stepSound
|
||||
|
||||
def explosionResistance(entity: Entity): Float =
|
||||
block.getExplosionResistance(entity)
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility functions for cleaner registry code
|
||||
*/
|
||||
object BlockMicroMaterial
|
||||
{
|
||||
def oldKey(block:Block) = block.getUnlocalizedName
|
||||
def materialKey(block:Block) = Block.blockRegistry.getNameForObject(block)
|
||||
def materialKey(name:String, meta:Int) = name+(if(meta > 0) "_"+meta else "")
|
||||
def materialKey(block:Block, meta:Int):String = materialKey(materialKey(block), meta)
|
||||
/** Utility functions for cleaner registry code
|
||||
*/
|
||||
object BlockMicroMaterial {
|
||||
def oldKey(block: Block) = block.getUnlocalizedName
|
||||
def materialKey(block: Block) = Block.blockRegistry.getNameForObject(block)
|
||||
def materialKey(name: String, meta: Int) =
|
||||
name + (if (meta > 0) "_" + meta else "")
|
||||
def materialKey(block: Block, meta: Int): String =
|
||||
materialKey(materialKey(block), meta)
|
||||
|
||||
def createAndRegister(block:Block, meta:Int, name:String) =
|
||||
MicroMaterialRegistry.registerMaterial(new BlockMicroMaterial(block, meta), materialKey(name, meta))
|
||||
def createAndRegister(block: Block, meta: Int, name: String) =
|
||||
MicroMaterialRegistry.registerMaterial(
|
||||
new BlockMicroMaterial(block, meta),
|
||||
materialKey(name, meta)
|
||||
)
|
||||
|
||||
def createAndRegister(block:Block, meta:Int, name:String, oldName:String) {
|
||||
MicroMaterialRegistry.remapName(materialKey(oldName, meta), materialKey(name, meta))
|
||||
createAndRegister(block, meta, name)
|
||||
}
|
||||
def createAndRegister(
|
||||
block: Block,
|
||||
meta: Int,
|
||||
name: String,
|
||||
oldName: String
|
||||
) {
|
||||
MicroMaterialRegistry.remapName(
|
||||
materialKey(oldName, meta),
|
||||
materialKey(name, meta)
|
||||
)
|
||||
createAndRegister(block, meta, name)
|
||||
}
|
||||
|
||||
def createAndRegister(block:Block, meta:Int = 0):Unit = createAndRegister(block, Seq(meta))
|
||||
def createAndRegister(block:Block, meta:Seq[Int]):Unit = createAndRegister(block, meta, materialKey(block), oldKey(block))
|
||||
def createAndRegister(block:Block, meta:Seq[Int], oldName:String):Unit = createAndRegister(block, 0, materialKey(block), oldName)
|
||||
def createAndRegister(block: Block, meta: Int = 0): Unit =
|
||||
createAndRegister(block, Seq(meta))
|
||||
def createAndRegister(block: Block, meta: Seq[Int]): Unit =
|
||||
createAndRegister(block, meta, materialKey(block), oldKey(block))
|
||||
def createAndRegister(block: Block, meta: Seq[Int], oldName: String): Unit =
|
||||
createAndRegister(block, 0, materialKey(block), oldName)
|
||||
|
||||
def createAndRegister(block:Block, meta:Seq[Int], name:String, oldName:String):Unit =
|
||||
meta.foreach(m => createAndRegister(block, m, name, oldName))
|
||||
def createAndRegister(
|
||||
block: Block,
|
||||
meta: Seq[Int],
|
||||
name: String,
|
||||
oldName: String
|
||||
): Unit =
|
||||
meta.foreach(m => createAndRegister(block, m, name, oldName))
|
||||
}
|
||||
@@ -16,133 +16,158 @@ import BlockMicroMaterial.materialKey
|
||||
import java.lang.{Iterable => JIterable}
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
object ConfigContent
|
||||
{
|
||||
private val nameMap = MMap[String, Seq[Int]]()
|
||||
object ConfigContent {
|
||||
private val nameMap = MMap[String, Seq[Int]]()
|
||||
|
||||
def parse(cfgDir: File) {
|
||||
val cfgFile = new File(cfgDir, "microblocks.cfg")
|
||||
def parse(cfgDir: File) {
|
||||
val cfgFile = new File(cfgDir, "microblocks.cfg")
|
||||
try {
|
||||
if (!cfgFile.exists())
|
||||
generateDefault(cfgFile)
|
||||
else
|
||||
loadLines(cfgFile)
|
||||
} catch {
|
||||
case e: IOException => logger.error("Error parsing config", e)
|
||||
}
|
||||
}
|
||||
|
||||
def generateDefault(cfgFile: File) {
|
||||
val writer = new PrintWriter(cfgFile)
|
||||
writer.println(
|
||||
"#Configuration file for adding microblock materials for aesthetic blocks added by mods"
|
||||
)
|
||||
writer.println("#Each line needs to be of the form <name>:<meta>")
|
||||
writer.println(
|
||||
"#<name> is the unlocalised name or registry key of the block/item enclosed in quotes. NEI can help you find these"
|
||||
)
|
||||
writer.println(
|
||||
"#<meta> may be ommitted, in which case it defaults to 0, otherwise it can be a number, a comma separated list of numbers, or a dash separated range"
|
||||
)
|
||||
writer.println(
|
||||
"#Ex. \"dirt\" \"minecraft:planks\":3 \"iron_ore\":1,2,3,5 \"ThermalFoundation:Storage\":0-15"
|
||||
)
|
||||
writer.close()
|
||||
}
|
||||
|
||||
def loadLine(line: String) {
|
||||
if (line.startsWith("#") || line.length < 3)
|
||||
return
|
||||
|
||||
if (line.charAt(0) != '\"')
|
||||
throw new IllegalArgumentException("Line must begin with a quote")
|
||||
val q2 = line.indexOf('\"', 1)
|
||||
if (q2 < 0)
|
||||
throw new IllegalArgumentException("Unmatched quotes")
|
||||
|
||||
var name = line.substring(1, q2)
|
||||
if (!name.contains('.') && !name.contains(':'))
|
||||
name = "minecraft:" + name
|
||||
|
||||
var meta = Seq(0)
|
||||
if (line.length > q2 + 1) {
|
||||
if (line.charAt(q2 + 1) != ':')
|
||||
throw new IllegalArgumentException(
|
||||
"Name must be followed by a colon separator"
|
||||
)
|
||||
|
||||
meta = line.substring(q2 + 2).split(",").flatMap { s =>
|
||||
if (s.contains("-")) {
|
||||
val split2 = s.split("-")
|
||||
if (split2.length != 2)
|
||||
throw new IllegalArgumentException("Invalid - separated range")
|
||||
split2(0).toInt to split2(1).toInt
|
||||
} else {
|
||||
Seq(s.toInt)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nameMap.put(name, meta)
|
||||
}
|
||||
|
||||
def loadLines(cfgFile: File) {
|
||||
val reader = new BufferedReader(new FileReader(cfgFile))
|
||||
var s: String = null
|
||||
do {
|
||||
s = reader.readLine
|
||||
if (s != null) {
|
||||
try {
|
||||
if (!cfgFile.exists())
|
||||
generateDefault(cfgFile)
|
||||
else
|
||||
loadLines(cfgFile)
|
||||
loadLine(s)
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
logger.error("Invalid line in microblocks.cfg: " + s)
|
||||
logger.error(e.getMessage)
|
||||
}
|
||||
catch {
|
||||
case e: IOException => logger.error("Error parsing config", e)
|
||||
}
|
||||
} while (s != null)
|
||||
reader.close()
|
||||
}
|
||||
|
||||
def load() {
|
||||
for (block <- Block.blockRegistry.asInstanceOf[JIterable[Block]]) {
|
||||
val metas = Seq(
|
||||
block.getUnlocalizedName,
|
||||
Block.blockRegistry.getNameForObject(block)
|
||||
).flatMap(nameMap.remove).flatten
|
||||
metas.foreach { m =>
|
||||
try {
|
||||
createAndRegister(block, m)
|
||||
} catch {
|
||||
case e: IllegalStateException =>
|
||||
logger.error(
|
||||
"Unable to register micro material: " +
|
||||
materialKey(block, m) + "\n\t" + e.getMessage
|
||||
)
|
||||
case e: Exception =>
|
||||
logger.error(
|
||||
"Unable to register micro material: " + materialKey(block, m),
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def generateDefault(cfgFile: File) {
|
||||
val writer = new PrintWriter(cfgFile)
|
||||
writer.println("#Configuration file for adding microblock materials for aesthetic blocks added by mods")
|
||||
writer.println("#Each line needs to be of the form <name>:<meta>")
|
||||
writer.println("#<name> is the unlocalised name or registry key of the block/item enclosed in quotes. NEI can help you find these")
|
||||
writer.println("#<meta> may be ommitted, in which case it defaults to 0, otherwise it can be a number, a comma separated list of numbers, or a dash separated range")
|
||||
writer.println("#Ex. \"dirt\" \"minecraft:planks\":3 \"iron_ore\":1,2,3,5 \"ThermalFoundation:Storage\":0-15")
|
||||
writer.close()
|
||||
}
|
||||
nameMap.foreach(e =>
|
||||
logger.warn(
|
||||
"Unable to add micro material for block with unlocalised name " + e._1 + " as it doesn't exist"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def loadLine(line: String) {
|
||||
if (line.startsWith("#") || line.length < 3)
|
||||
return
|
||||
def handleIMC(messages: Seq[IMCMessage]) {
|
||||
messages.filter(_.key == "microMaterial").foreach { msg =>
|
||||
def error(s: String) {
|
||||
logger.error(
|
||||
"Invalid microblock IMC message from " + msg.getSender + ": " + s
|
||||
)
|
||||
}
|
||||
|
||||
if(line.charAt(0) != '\"')
|
||||
throw new IllegalArgumentException("Line must begin with a quote")
|
||||
val q2 = line.indexOf('\"', 1)
|
||||
if(q2 < 0)
|
||||
throw new IllegalArgumentException("Unmatched quotes")
|
||||
|
||||
var name = line.substring(1, q2)
|
||||
if(!name.contains('.') && !name.contains(':'))
|
||||
name = "minecraft:"+name
|
||||
|
||||
var meta = Seq(0)
|
||||
if(line.length > q2+1) {
|
||||
if(line.charAt(q2+1) != ':')
|
||||
throw new IllegalArgumentException("Name must be followed by a colon separator")
|
||||
|
||||
meta = line.substring(q2+2).split(",").flatMap{s =>
|
||||
if (s.contains("-")) {
|
||||
val split2 = s.split("-")
|
||||
if (split2.length != 2)
|
||||
throw new IllegalArgumentException("Invalid - separated range")
|
||||
split2(0).toInt to split2(1).toInt
|
||||
}
|
||||
else {
|
||||
Seq(s.toInt)
|
||||
}
|
||||
}
|
||||
if (msg.getMessageType != classOf[ItemStack])
|
||||
error("value is not an instanceof ItemStack")
|
||||
else {
|
||||
val stack = msg.getItemStackValue
|
||||
if (!Block.blockRegistry.containsId(Item.getIdFromItem(stack.getItem)))
|
||||
error("Invalid Block: " + stack.getItem)
|
||||
else if (stack.getItemDamage < 0 || stack.getItemDamage >= 16)
|
||||
error("Invalid metadata: " + stack.getItemDamage)
|
||||
else {
|
||||
try {
|
||||
createAndRegister(
|
||||
Block.getBlockFromItem(stack.getItem),
|
||||
stack.getItemDamage
|
||||
)
|
||||
} catch {
|
||||
case e: IllegalStateException =>
|
||||
error(
|
||||
"Unable to register micro material: " +
|
||||
materialKey(
|
||||
Block.getBlockFromItem(stack.getItem),
|
||||
stack.getItemDamage
|
||||
) + "\n\t" + e.getMessage
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
nameMap.put(name, meta)
|
||||
}
|
||||
}
|
||||
|
||||
def loadLines(cfgFile: File) {
|
||||
val reader = new BufferedReader(new FileReader(cfgFile))
|
||||
var s: String = null
|
||||
do {
|
||||
s = reader.readLine
|
||||
if (s != null) {
|
||||
try {
|
||||
loadLine(s)
|
||||
}
|
||||
catch {
|
||||
case e: Exception =>
|
||||
logger.error("Invalid line in microblocks.cfg: " + s)
|
||||
logger.error(e.getMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
while (s != null)
|
||||
reader.close()
|
||||
}
|
||||
|
||||
def load() {
|
||||
for(block <- Block.blockRegistry.asInstanceOf[JIterable[Block]]) {
|
||||
val metas = Seq(block.getUnlocalizedName, Block.blockRegistry.getNameForObject(block)).flatMap(nameMap.remove).flatten
|
||||
metas.foreach{m =>
|
||||
|
||||
try {
|
||||
createAndRegister(block, m)
|
||||
}
|
||||
catch {
|
||||
case e: IllegalStateException => logger.error("Unable to register micro material: " +
|
||||
materialKey(block, m) + "\n\t" + e.getMessage)
|
||||
case e: Exception =>
|
||||
logger.error("Unable to register micro material: " + materialKey(block, m), e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nameMap.foreach(e => logger.warn("Unable to add micro material for block with unlocalised name " + e._1 + " as it doesn't exist"))
|
||||
}
|
||||
|
||||
def handleIMC(messages: Seq[IMCMessage]) {
|
||||
messages.filter(_.key == "microMaterial").foreach{msg =>
|
||||
|
||||
def error(s: String) {
|
||||
logger.error("Invalid microblock IMC message from " + msg.getSender + ": " + s)
|
||||
}
|
||||
|
||||
if (msg.getMessageType != classOf[ItemStack])
|
||||
error("value is not an instanceof ItemStack")
|
||||
else {
|
||||
val stack = msg.getItemStackValue
|
||||
if (!Block.blockRegistry.containsId(Item.getIdFromItem(stack.getItem)))
|
||||
error("Invalid Block: " + stack.getItem)
|
||||
else if (stack.getItemDamage < 0 || stack.getItemDamage >= 16)
|
||||
error("Invalid metadata: " + stack.getItemDamage)
|
||||
else {
|
||||
try {
|
||||
createAndRegister(Block.getBlockFromItem(stack.getItem), stack.getItemDamage)
|
||||
}
|
||||
catch {
|
||||
case e: IllegalStateException => error("Unable to register micro material: " +
|
||||
materialKey(Block.getBlockFromItem(stack.getItem), stack.getItemDamage) + "\n\t" + e.getMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,52 +5,48 @@ import codechicken.lib.vec.Scale
|
||||
import codechicken.lib.vec.Vector3
|
||||
import Vector3._
|
||||
|
||||
object CornerPlacement extends PlacementProperties
|
||||
{
|
||||
def microClass = CornerMicroClass
|
||||
|
||||
def placementGrid = CornerPlacementGrid
|
||||
|
||||
def opposite(slot:Int, side:Int) = ((slot-7)^(1<<(side>>1)))+7
|
||||
object CornerPlacement extends PlacementProperties {
|
||||
def microClass = CornerMicroClass
|
||||
|
||||
def placementGrid = CornerPlacementGrid
|
||||
|
||||
def opposite(slot: Int, side: Int) = ((slot - 7) ^ (1 << (side >> 1))) + 7
|
||||
}
|
||||
|
||||
object CornerMicroClass extends CommonMicroClass
|
||||
{
|
||||
var aBounds:Array[Cuboid6] = new Array(256)
|
||||
|
||||
for(s <- 0 until 8)
|
||||
{
|
||||
val rx = if((s&4) != 0) -1 else 1
|
||||
val ry = if((s&1) != 0) -1 else 1
|
||||
val rz = if((s&2) != 0) -1 else 1
|
||||
val transform = new Scale(new Vector3(rx, ry, rz)).at(center)
|
||||
|
||||
for(t <- 1 until 8)
|
||||
{
|
||||
val d = t/8D
|
||||
aBounds(t<<4|s) = new Cuboid6(0, 0, 0, d, d, d).apply(transform)
|
||||
}
|
||||
object CornerMicroClass extends CommonMicroClass {
|
||||
var aBounds: Array[Cuboid6] = new Array(256)
|
||||
|
||||
for (s <- 0 until 8) {
|
||||
val rx = if ((s & 4) != 0) -1 else 1
|
||||
val ry = if ((s & 1) != 0) -1 else 1
|
||||
val rz = if ((s & 2) != 0) -1 else 1
|
||||
val transform = new Scale(new Vector3(rx, ry, rz)).at(center)
|
||||
|
||||
for (t <- 1 until 8) {
|
||||
val d = t / 8d
|
||||
aBounds(t << 4 | s) = new Cuboid6(0, 0, 0, d, d, d).apply(transform)
|
||||
}
|
||||
|
||||
def getName = "mcr_cnr"
|
||||
}
|
||||
|
||||
def baseTrait = classOf[CornerMicroblock]
|
||||
def clientTrait = classOf[CommonMicroblockClient]
|
||||
|
||||
override def itemSlot = 7
|
||||
|
||||
def placementProperties = CornerPlacement
|
||||
def getName = "mcr_cnr"
|
||||
|
||||
def getResistanceFactor = 1
|
||||
def baseTrait = classOf[CornerMicroblock]
|
||||
def clientTrait = classOf[CommonMicroblockClient]
|
||||
|
||||
override def itemSlot = 7
|
||||
|
||||
def placementProperties = CornerPlacement
|
||||
|
||||
def getResistanceFactor = 1
|
||||
}
|
||||
|
||||
trait CornerMicroblock extends CommonMicroblock
|
||||
{
|
||||
override def setShape(size: Int, slot: Int) = shape = (size<<4|(slot-7)).toByte
|
||||
|
||||
def microClass = CornerMicroClass
|
||||
|
||||
def getBounds = CornerMicroClass.aBounds(shape)
|
||||
|
||||
override def getSlot = getShape+7
|
||||
}
|
||||
trait CornerMicroblock extends CommonMicroblock {
|
||||
override def setShape(size: Int, slot: Int) = shape =
|
||||
(size << 4 | (slot - 7)).toByte
|
||||
|
||||
def microClass = CornerMicroClass
|
||||
|
||||
def getBounds = CornerMicroClass.aBounds(shape)
|
||||
|
||||
override def getSlot = getShape + 7
|
||||
}
|
||||
@@ -4,57 +4,55 @@ import net.minecraft.init.Blocks._
|
||||
import BlockMicroMaterial._
|
||||
import MicroMaterialRegistry._
|
||||
|
||||
object DefaultContent
|
||||
{
|
||||
def load()
|
||||
{
|
||||
FaceMicroClass.register(0)
|
||||
HollowMicroClass.register(1)
|
||||
CornerMicroClass.register(2)
|
||||
EdgeMicroClass.register(3)
|
||||
PostMicroClass.register()
|
||||
|
||||
createAndRegister(stone)
|
||||
createAndRegister(dirt, 0 to 2)
|
||||
createAndRegister(cobblestone)
|
||||
createAndRegister(planks, 0 to 5)
|
||||
createAndRegister(log, 0 to 3)
|
||||
createAndRegister(log2, 0 to 1, "tile.log2")
|
||||
createAndRegister(leaves, 0 to 3)
|
||||
createAndRegister(leaves2, 0 to 1, "tile.leaves2")
|
||||
createAndRegister(sponge)
|
||||
createAndRegister(glass)
|
||||
createAndRegister(lapis_block)
|
||||
createAndRegister(sandstone, 0 to 2)
|
||||
createAndRegister(wool, 0 to 15)
|
||||
createAndRegister(gold_block)
|
||||
createAndRegister(iron_block)
|
||||
createAndRegister(brick_block)
|
||||
createAndRegister(bookshelf)
|
||||
createAndRegister(mossy_cobblestone)
|
||||
createAndRegister(obsidian)
|
||||
createAndRegister(diamond_block)
|
||||
createAndRegister(ice)
|
||||
createAndRegister(snow)
|
||||
createAndRegister(clay)
|
||||
createAndRegister(netherrack)
|
||||
createAndRegister(soul_sand)
|
||||
createAndRegister(glowstone)
|
||||
createAndRegister(stonebrick, 0 to 3)
|
||||
createAndRegister(nether_brick)
|
||||
createAndRegister(end_stone)
|
||||
createAndRegister(emerald_block)
|
||||
createAndRegister(redstone_block)
|
||||
createAndRegister(quartz_block)
|
||||
createAndRegister(stained_hardened_clay, 0 to 15)
|
||||
createAndRegister(hardened_clay)
|
||||
createAndRegister(coal_block)
|
||||
createAndRegister(packed_ice)
|
||||
createAndRegister(stained_glass, 0 to 15)
|
||||
object DefaultContent {
|
||||
def load() {
|
||||
FaceMicroClass.register(0)
|
||||
HollowMicroClass.register(1)
|
||||
CornerMicroClass.register(2)
|
||||
EdgeMicroClass.register(3)
|
||||
PostMicroClass.register()
|
||||
|
||||
MicroMaterialRegistry.remapName(oldKey(grass), materialKey(grass))
|
||||
registerMaterial(new GrassMicroMaterial, materialKey(grass))
|
||||
MicroMaterialRegistry.remapName(oldKey(mycelium), materialKey(mycelium))
|
||||
registerMaterial(new TopMicroMaterial(mycelium), materialKey(mycelium))
|
||||
}
|
||||
createAndRegister(stone)
|
||||
createAndRegister(dirt, 0 to 2)
|
||||
createAndRegister(cobblestone)
|
||||
createAndRegister(planks, 0 to 5)
|
||||
createAndRegister(log, 0 to 3)
|
||||
createAndRegister(log2, 0 to 1, "tile.log2")
|
||||
createAndRegister(leaves, 0 to 3)
|
||||
createAndRegister(leaves2, 0 to 1, "tile.leaves2")
|
||||
createAndRegister(sponge)
|
||||
createAndRegister(glass)
|
||||
createAndRegister(lapis_block)
|
||||
createAndRegister(sandstone, 0 to 2)
|
||||
createAndRegister(wool, 0 to 15)
|
||||
createAndRegister(gold_block)
|
||||
createAndRegister(iron_block)
|
||||
createAndRegister(brick_block)
|
||||
createAndRegister(bookshelf)
|
||||
createAndRegister(mossy_cobblestone)
|
||||
createAndRegister(obsidian)
|
||||
createAndRegister(diamond_block)
|
||||
createAndRegister(ice)
|
||||
createAndRegister(snow)
|
||||
createAndRegister(clay)
|
||||
createAndRegister(netherrack)
|
||||
createAndRegister(soul_sand)
|
||||
createAndRegister(glowstone)
|
||||
createAndRegister(stonebrick, 0 to 3)
|
||||
createAndRegister(nether_brick)
|
||||
createAndRegister(end_stone)
|
||||
createAndRegister(emerald_block)
|
||||
createAndRegister(redstone_block)
|
||||
createAndRegister(quartz_block)
|
||||
createAndRegister(stained_hardened_clay, 0 to 15)
|
||||
createAndRegister(hardened_clay)
|
||||
createAndRegister(coal_block)
|
||||
createAndRegister(packed_ice)
|
||||
createAndRegister(stained_glass, 0 to 15)
|
||||
|
||||
MicroMaterialRegistry.remapName(oldKey(grass), materialKey(grass))
|
||||
registerMaterial(new GrassMicroMaterial, materialKey(grass))
|
||||
MicroMaterialRegistry.remapName(oldKey(mycelium), materialKey(mycelium))
|
||||
registerMaterial(new TopMicroMaterial(mycelium), materialKey(mycelium))
|
||||
}
|
||||
}
|
||||
@@ -20,217 +20,205 @@ import scala.collection.JavaConversions._
|
||||
import codechicken.lib.data.MCDataInput
|
||||
import codechicken.lib.lighting.LightMatrix
|
||||
|
||||
object EdgePlacement extends PlacementProperties
|
||||
{
|
||||
import codechicken.multipart.PartMap._
|
||||
|
||||
def microClass = EdgeMicroClass
|
||||
|
||||
def placementGrid = EdgePlacementGrid
|
||||
|
||||
def opposite(slot:Int, side:Int):Int =
|
||||
{
|
||||
if(slot < 0)//custom placement
|
||||
return slot
|
||||
val e = slot-15
|
||||
return 15+ packEdgeBits(e, unpackEdgeBits(e)^(1<<(side>>1)))
|
||||
}
|
||||
|
||||
override def customPlacement(pmt:MicroblockPlacement):ExecutablePlacement =
|
||||
{
|
||||
if(pmt.size%2 == 1) return null
|
||||
|
||||
val part = PostMicroClass.create(pmt.world.isRemote, pmt.material)
|
||||
part.setShape(pmt.size, pmt.hit.sideHit>>1)
|
||||
if(pmt.doExpand)
|
||||
{
|
||||
val hpart = pmt.htile.partList(ExtendedMOP.getData[(Int, _)](pmt.hit)._1)
|
||||
if(hpart.getType == PostMicroClass.getName)
|
||||
{
|
||||
val mpart = hpart.asInstanceOf[Microblock]
|
||||
if(mpart.material == pmt.material && mpart.getSize + pmt.size < 8)
|
||||
{
|
||||
part.shape = ((mpart.getSize+pmt.size)<<4|mpart.getShape).toByte
|
||||
return pmt.expand(mpart, part)
|
||||
}
|
||||
}
|
||||
object EdgePlacement extends PlacementProperties {
|
||||
import codechicken.multipart.PartMap._
|
||||
|
||||
def microClass = EdgeMicroClass
|
||||
|
||||
def placementGrid = EdgePlacementGrid
|
||||
|
||||
def opposite(slot: Int, side: Int): Int = {
|
||||
if (slot < 0) // custom placement
|
||||
return slot
|
||||
val e = slot - 15
|
||||
return 15 + packEdgeBits(e, unpackEdgeBits(e) ^ (1 << (side >> 1)))
|
||||
}
|
||||
|
||||
override def customPlacement(
|
||||
pmt: MicroblockPlacement
|
||||
): ExecutablePlacement = {
|
||||
if (pmt.size % 2 == 1) return null
|
||||
|
||||
val part = PostMicroClass.create(pmt.world.isRemote, pmt.material)
|
||||
part.setShape(pmt.size, pmt.hit.sideHit >> 1)
|
||||
if (pmt.doExpand) {
|
||||
val hpart = pmt.htile.partList(ExtendedMOP.getData[(Int, _)](pmt.hit)._1)
|
||||
if (hpart.getType == PostMicroClass.getName) {
|
||||
val mpart = hpart.asInstanceOf[Microblock]
|
||||
if (mpart.material == pmt.material && mpart.getSize + pmt.size < 8) {
|
||||
part.shape = ((mpart.getSize + pmt.size) << 4 | mpart.getShape).toByte
|
||||
return pmt.expand(mpart, part)
|
||||
}
|
||||
|
||||
if(pmt.slot >= 0)
|
||||
return null
|
||||
|
||||
if(pmt.internal && !pmt.oppMod)
|
||||
return pmt.internalPlacement(pmt.htile.asInstanceOf[TileMultipart], part)
|
||||
|
||||
return pmt.externalPlacement(part)
|
||||
}
|
||||
}
|
||||
|
||||
if (pmt.slot >= 0)
|
||||
return null
|
||||
|
||||
if (pmt.internal && !pmt.oppMod)
|
||||
return pmt.internalPlacement(pmt.htile.asInstanceOf[TileMultipart], part)
|
||||
|
||||
return pmt.externalPlacement(part)
|
||||
}
|
||||
}
|
||||
|
||||
object EdgeMicroClass extends CommonMicroClass
|
||||
{
|
||||
var aBounds:Array[Cuboid6] = new Array(256)
|
||||
|
||||
for(s <- 0 until 12)
|
||||
{
|
||||
val rx = if((s&2) != 0) -1 else 1
|
||||
val rz = if((s&1) != 0) -1 else 1
|
||||
val transform = new TransformationList(new Scale(new Vector3(rx, 1, rz)), AxisCycle.cycles(s>>2)).at(center)
|
||||
|
||||
for(t <- 1 until 8)
|
||||
{
|
||||
val d = t/8D
|
||||
aBounds(t<<4|s) = new Cuboid6(0, 0, 0, d, 1, d).apply(transform)
|
||||
}
|
||||
object EdgeMicroClass extends CommonMicroClass {
|
||||
var aBounds: Array[Cuboid6] = new Array(256)
|
||||
|
||||
for (s <- 0 until 12) {
|
||||
val rx = if ((s & 2) != 0) -1 else 1
|
||||
val rz = if ((s & 1) != 0) -1 else 1
|
||||
val transform = new TransformationList(
|
||||
new Scale(new Vector3(rx, 1, rz)),
|
||||
AxisCycle.cycles(s >> 2)
|
||||
).at(center)
|
||||
|
||||
for (t <- 1 until 8) {
|
||||
val d = t / 8d
|
||||
aBounds(t << 4 | s) = new Cuboid6(0, 0, 0, d, 1, d).apply(transform)
|
||||
}
|
||||
|
||||
override def itemSlot = 15
|
||||
|
||||
def getName = "mcr_edge"
|
||||
}
|
||||
|
||||
def baseTrait = classOf[EdgeMicroblock]
|
||||
def clientTrait = classOf[CommonMicroblockClient]
|
||||
override def itemSlot = 15
|
||||
|
||||
def placementProperties = EdgePlacement
|
||||
def getName = "mcr_edge"
|
||||
|
||||
def getResistanceFactor = 0.5F
|
||||
def baseTrait = classOf[EdgeMicroblock]
|
||||
def clientTrait = classOf[CommonMicroblockClient]
|
||||
|
||||
def placementProperties = EdgePlacement
|
||||
|
||||
def getResistanceFactor = 0.5f
|
||||
}
|
||||
|
||||
trait EdgeMicroblock extends CommonMicroblock with TEdgePart
|
||||
{
|
||||
override def setShape(size: Int, slot: Int) = shape = (size<<4|(slot-15)).toByte
|
||||
trait EdgeMicroblock extends CommonMicroblock with TEdgePart {
|
||||
override def setShape(size: Int, slot: Int) = shape =
|
||||
(size << 4 | (slot - 15)).toByte
|
||||
|
||||
def microClass = EdgeMicroClass
|
||||
|
||||
def getBounds = EdgeMicroClass.aBounds(shape)
|
||||
|
||||
override def getSlot = getShape+15
|
||||
def microClass = EdgeMicroClass
|
||||
|
||||
def getBounds = EdgeMicroClass.aBounds(shape)
|
||||
|
||||
override def getSlot = getShape + 15
|
||||
}
|
||||
|
||||
object PostMicroClass extends MicroblockClass
|
||||
{
|
||||
var aBounds:Array[Cuboid6] = new Array(256)
|
||||
|
||||
for(s <- 0 until 3)
|
||||
{
|
||||
val transform = sideRotations(s<<1).at(center)
|
||||
for(t <- 2 until 8 by 2)
|
||||
{
|
||||
val d1 = 0.5-t/16D
|
||||
val d2 = 0.5+t/16D
|
||||
aBounds(t<<4|s) = new Cuboid6(d1, 0, d1, d2, 1, d2).apply(transform)
|
||||
}
|
||||
object PostMicroClass extends MicroblockClass {
|
||||
var aBounds: Array[Cuboid6] = new Array(256)
|
||||
|
||||
for (s <- 0 until 3) {
|
||||
val transform = sideRotations(s << 1).at(center)
|
||||
for (t <- 2 until 8 by 2) {
|
||||
val d1 = 0.5 - t / 16d
|
||||
val d2 = 0.5 + t / 16d
|
||||
aBounds(t << 4 | s) = new Cuboid6(d1, 0, d1, d2, 1, d2).apply(transform)
|
||||
}
|
||||
|
||||
def getName = "mcr_post"
|
||||
}
|
||||
|
||||
def baseTrait = classOf[PostMicroblock]
|
||||
def clientTrait = classOf[PostMicroblockClient]
|
||||
def getName = "mcr_post"
|
||||
|
||||
def getResistanceFactor = 0.5F
|
||||
def baseTrait = classOf[PostMicroblock]
|
||||
def clientTrait = classOf[PostMicroblockClient]
|
||||
|
||||
def getResistanceFactor = 0.5f
|
||||
}
|
||||
|
||||
trait PostMicroblockClient extends PostMicroblock with MicroblockClient
|
||||
{
|
||||
var renderBounds1:Cuboid6 = _
|
||||
var renderBounds2:Cuboid6 = _
|
||||
|
||||
override def render(pos:Vector3, pass:Int)
|
||||
{
|
||||
val mat = getIMaterial
|
||||
if(pass == -1)
|
||||
MicroblockRender.renderCuboid(pos, mat, pass, getBounds, 0)
|
||||
else {
|
||||
MicroblockRender.renderCuboid(pos, mat, pass, renderBounds1, 0)
|
||||
if (renderBounds2 != null)
|
||||
MicroblockRender.renderCuboid(pos, mat, pass, renderBounds2, 0)
|
||||
}
|
||||
}
|
||||
|
||||
override def onPartChanged(part:TMultiPart)
|
||||
{
|
||||
recalcBounds()
|
||||
}
|
||||
|
||||
override def onAdded()
|
||||
{
|
||||
super.onAdded()
|
||||
recalcBounds()
|
||||
}
|
||||
trait PostMicroblockClient extends PostMicroblock with MicroblockClient {
|
||||
var renderBounds1: Cuboid6 = _
|
||||
var renderBounds2: Cuboid6 = _
|
||||
|
||||
override def read(packet:MCDataInput)
|
||||
{
|
||||
super.read(packet)
|
||||
recalcBounds()
|
||||
override def render(pos: Vector3, pass: Int) {
|
||||
val mat = getIMaterial
|
||||
if (pass == -1)
|
||||
MicroblockRender.renderCuboid(pos, mat, pass, getBounds, 0)
|
||||
else {
|
||||
MicroblockRender.renderCuboid(pos, mat, pass, renderBounds1, 0)
|
||||
if (renderBounds2 != null)
|
||||
MicroblockRender.renderCuboid(pos, mat, pass, renderBounds2, 0)
|
||||
}
|
||||
|
||||
def recalcBounds()
|
||||
{
|
||||
renderBounds1 = getBounds.copy
|
||||
renderBounds2 = null
|
||||
|
||||
shrinkFace(getShape<<1)
|
||||
shrinkFace(getShape<<1|1)
|
||||
|
||||
tile.partList.foreach(p =>
|
||||
if(p.isInstanceOf[PostMicroblock] && p != this)
|
||||
shrinkPost(p.asInstanceOf[PostMicroblock])
|
||||
)
|
||||
}
|
||||
|
||||
def shrinkFace(fside:Int)
|
||||
{
|
||||
val part = tile.partMap(fside)
|
||||
if(part != null && part.getType.equals("mcr_face"))
|
||||
MicroOcclusion.shrink(renderBounds1, part.asInstanceOf[CommonMicroblock].getBounds, fside)
|
||||
}
|
||||
|
||||
def shrinkPost(post:PostMicroblock)
|
||||
{
|
||||
if(post == this)
|
||||
return
|
||||
|
||||
if(thisShrinks(post))
|
||||
{
|
||||
if(renderBounds2 == null)
|
||||
renderBounds2 = getBounds.copy
|
||||
MicroOcclusion.shrink(renderBounds1, post.getBounds, getShape<<1|1)
|
||||
MicroOcclusion.shrink(renderBounds2, post.getBounds, getShape<<1)
|
||||
}
|
||||
}
|
||||
|
||||
def thisShrinks(other:PostMicroblock):Boolean =
|
||||
{
|
||||
if(getSize != other.getSize) return getSize < other.getSize
|
||||
if(isTransparent != other.isTransparent) return isTransparent
|
||||
return getShape > other.getShape
|
||||
}
|
||||
|
||||
override def onPartChanged(part: TMultiPart) {
|
||||
recalcBounds()
|
||||
}
|
||||
|
||||
override def onAdded() {
|
||||
super.onAdded()
|
||||
recalcBounds()
|
||||
}
|
||||
|
||||
override def read(packet: MCDataInput) {
|
||||
super.read(packet)
|
||||
recalcBounds()
|
||||
}
|
||||
|
||||
def recalcBounds() {
|
||||
renderBounds1 = getBounds.copy
|
||||
renderBounds2 = null
|
||||
|
||||
shrinkFace(getShape << 1)
|
||||
shrinkFace(getShape << 1 | 1)
|
||||
|
||||
tile.partList.foreach(p =>
|
||||
if (p.isInstanceOf[PostMicroblock] && p != this)
|
||||
shrinkPost(p.asInstanceOf[PostMicroblock])
|
||||
)
|
||||
}
|
||||
|
||||
def shrinkFace(fside: Int) {
|
||||
val part = tile.partMap(fside)
|
||||
if (part != null && part.getType.equals("mcr_face"))
|
||||
MicroOcclusion.shrink(
|
||||
renderBounds1,
|
||||
part.asInstanceOf[CommonMicroblock].getBounds,
|
||||
fside
|
||||
)
|
||||
}
|
||||
|
||||
def shrinkPost(post: PostMicroblock) {
|
||||
if (post == this)
|
||||
return
|
||||
|
||||
if (thisShrinks(post)) {
|
||||
if (renderBounds2 == null)
|
||||
renderBounds2 = getBounds.copy
|
||||
MicroOcclusion.shrink(renderBounds1, post.getBounds, getShape << 1 | 1)
|
||||
MicroOcclusion.shrink(renderBounds2, post.getBounds, getShape << 1)
|
||||
}
|
||||
}
|
||||
|
||||
def thisShrinks(other: PostMicroblock): Boolean = {
|
||||
if (getSize != other.getSize) return getSize < other.getSize
|
||||
if (isTransparent != other.isTransparent) return isTransparent
|
||||
return getShape > other.getShape
|
||||
}
|
||||
}
|
||||
|
||||
trait PostMicroblock extends Microblock with JPartialOcclusion with TNormalOcclusion
|
||||
{
|
||||
def microClass = PostMicroClass
|
||||
trait PostMicroblock
|
||||
extends Microblock
|
||||
with JPartialOcclusion
|
||||
with TNormalOcclusion {
|
||||
def microClass = PostMicroClass
|
||||
|
||||
def getBounds = PostMicroClass.aBounds(shape)
|
||||
|
||||
def getOcclusionBoxes = Seq(getBounds)
|
||||
|
||||
def getPartialOcclusionBoxes = getOcclusionBoxes
|
||||
|
||||
override def itemClassID = EdgeMicroClass.getClassId
|
||||
|
||||
override def occlusionTest(npart:TMultiPart): Boolean =
|
||||
{
|
||||
if(npart.isInstanceOf[PostMicroblock])
|
||||
return npart.asInstanceOf[PostMicroblock].getShape != getShape
|
||||
|
||||
if(npart.getType.equals("mcr_face"))
|
||||
if(npart.asInstanceOf[CommonMicroblock].getSlot>>1 == getShape)
|
||||
return true
|
||||
def getBounds = PostMicroClass.aBounds(shape)
|
||||
|
||||
return super.occlusionTest(npart)
|
||||
}
|
||||
def getOcclusionBoxes = Seq(getBounds)
|
||||
|
||||
def getResistanceFactor = PostMicroClass.getResistanceFactor
|
||||
def getPartialOcclusionBoxes = getOcclusionBoxes
|
||||
|
||||
override def canPlaceTorchOnTop = getShape == 0
|
||||
}
|
||||
override def itemClassID = EdgeMicroClass.getClassId
|
||||
|
||||
override def occlusionTest(npart: TMultiPart): Boolean = {
|
||||
if (npart.isInstanceOf[PostMicroblock])
|
||||
return npart.asInstanceOf[PostMicroblock].getShape != getShape
|
||||
|
||||
if (npart.getType.equals("mcr_face"))
|
||||
if (npart.asInstanceOf[CommonMicroblock].getSlot >> 1 == getShape)
|
||||
return true
|
||||
|
||||
return super.occlusionTest(npart)
|
||||
}
|
||||
|
||||
def getResistanceFactor = PostMicroClass.getResistanceFactor
|
||||
|
||||
override def canPlaceTorchOnTop = getShape == 0
|
||||
}
|
||||
@@ -7,67 +7,79 @@ import codechicken.lib.vec.Rotation
|
||||
import Rotation._
|
||||
import Vector3._
|
||||
|
||||
object FacePlacement extends PlacementProperties
|
||||
{
|
||||
def microClass = FaceMicroClass
|
||||
|
||||
def placementGrid = FacePlacementGrid
|
||||
|
||||
def opposite(slot:Int, side:Int) = slot^1
|
||||
|
||||
override def expand(slot:Int, side:Int) = sneakOpposite(slot, side)
|
||||
|
||||
override def sneakOpposite(slot:Int, side:Int) = slot == (side^1)
|
||||
object FacePlacement extends PlacementProperties {
|
||||
def microClass = FaceMicroClass
|
||||
|
||||
def placementGrid = FacePlacementGrid
|
||||
|
||||
def opposite(slot: Int, side: Int) = slot ^ 1
|
||||
|
||||
override def expand(slot: Int, side: Int) = sneakOpposite(slot, side)
|
||||
|
||||
override def sneakOpposite(slot: Int, side: Int) = slot == (side ^ 1)
|
||||
}
|
||||
|
||||
class FaceMicroClass
|
||||
|
||||
object FaceMicroClass extends CommonMicroClass
|
||||
{
|
||||
var aBounds:Array[Cuboid6] = new Array(256)
|
||||
|
||||
for(s <- 0 until 6)
|
||||
{
|
||||
val transform = sideRotations(s).at(center)
|
||||
for(t <- 1 until 8)
|
||||
{
|
||||
val d = t/8D
|
||||
aBounds(t<<4|s) = new Cuboid6(0, 0, 0, 1, d, 1).apply(transform)
|
||||
}
|
||||
object FaceMicroClass extends CommonMicroClass {
|
||||
var aBounds: Array[Cuboid6] = new Array(256)
|
||||
|
||||
for (s <- 0 until 6) {
|
||||
val transform = sideRotations(s).at(center)
|
||||
for (t <- 1 until 8) {
|
||||
val d = t / 8d
|
||||
aBounds(t << 4 | s) = new Cuboid6(0, 0, 0, 1, d, 1).apply(transform)
|
||||
}
|
||||
|
||||
def getName = "mcr_face"
|
||||
}
|
||||
|
||||
def itemSlot = 3
|
||||
def getName = "mcr_face"
|
||||
|
||||
def baseTrait = classOf[FaceMicroblock]
|
||||
def clientTrait = classOf[FaceMicroblockClient]
|
||||
|
||||
def placementProperties = FacePlacement
|
||||
def itemSlot = 3
|
||||
|
||||
def getResistanceFactor = 1
|
||||
def baseTrait = classOf[FaceMicroblock]
|
||||
def clientTrait = classOf[FaceMicroblockClient]
|
||||
|
||||
def placementProperties = FacePlacement
|
||||
|
||||
def getResistanceFactor = 1
|
||||
}
|
||||
|
||||
trait FaceMicroblockClient extends CommonMicroblockClient
|
||||
{
|
||||
override def render(pos:Vector3, pass:Int) {
|
||||
if(pass < 0)
|
||||
MicroblockRender.renderCuboid(pos, getIMaterial, pass, getBounds, 0)
|
||||
else if(isTransparent)
|
||||
MicroblockRender.renderCuboid(pos, getIMaterial, pass, renderBounds, renderMask)
|
||||
else {
|
||||
val mat = getIMaterial
|
||||
MicroblockRender.renderCuboid(pos, mat, pass, renderBounds, renderMask | 1<<getSlot)
|
||||
MicroblockRender.renderCuboid(pos, mat, pass, Cuboid6.full, ~(1<<getSlot))
|
||||
}
|
||||
trait FaceMicroblockClient extends CommonMicroblockClient {
|
||||
override def render(pos: Vector3, pass: Int) {
|
||||
if (pass < 0)
|
||||
MicroblockRender.renderCuboid(pos, getIMaterial, pass, getBounds, 0)
|
||||
else if (isTransparent)
|
||||
MicroblockRender.renderCuboid(
|
||||
pos,
|
||||
getIMaterial,
|
||||
pass,
|
||||
renderBounds,
|
||||
renderMask
|
||||
)
|
||||
else {
|
||||
val mat = getIMaterial
|
||||
MicroblockRender.renderCuboid(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
renderBounds,
|
||||
renderMask | 1 << getSlot
|
||||
)
|
||||
MicroblockRender.renderCuboid(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
Cuboid6.full,
|
||||
~(1 << getSlot)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait FaceMicroblock extends CommonMicroblock with TFacePart
|
||||
{
|
||||
def microClass = FaceMicroClass
|
||||
|
||||
def getBounds = FaceMicroClass.aBounds(shape)
|
||||
|
||||
override def solid(side:Int) = getIMaterial.isSolid
|
||||
trait FaceMicroblock extends CommonMicroblock with TFacePart {
|
||||
def microClass = FaceMicroClass
|
||||
|
||||
def getBounds = FaceMicroClass.aBounds(shape)
|
||||
|
||||
override def solid(side: Int) = getIMaterial.isSolid
|
||||
}
|
||||
@@ -8,41 +8,52 @@ import net.minecraft.init.Blocks
|
||||
import codechicken.lib.render.uv.{UVTranslation, IconTransformation}
|
||||
import codechicken.lib.render.BlockRenderer.BlockFace
|
||||
|
||||
class GrassMicroMaterial extends BlockMicroMaterial(Blocks.grass, 0)
|
||||
{
|
||||
var sideIconT:IconTransformation = _
|
||||
|
||||
override def loadIcons()
|
||||
{
|
||||
super.loadIcons()
|
||||
sideIconT = new IconTransformation(BlockGrass.getIconSideOverlay)
|
||||
}
|
||||
class GrassMicroMaterial extends BlockMicroMaterial(Blocks.grass, 0) {
|
||||
var sideIconT: IconTransformation = _
|
||||
|
||||
override def renderMicroFace(pos:Vector3, pass:Int, bounds:Cuboid6) {
|
||||
val face = CCRenderState.model.asInstanceOf[BlockFace]
|
||||
if(pass != -1)
|
||||
face.computeLightCoords()
|
||||
override def loadIcons() {
|
||||
super.loadIcons()
|
||||
sideIconT = new IconTransformation(BlockGrass.getIconSideOverlay)
|
||||
}
|
||||
|
||||
if(face.side == 1)
|
||||
MaterialRenderHelper.start(pos, pass, icont).blockColour(getColour(pass)).lighting().render()
|
||||
else
|
||||
MaterialRenderHelper.start(pos, pass, icont).lighting().render()
|
||||
override def renderMicroFace(pos: Vector3, pass: Int, bounds: Cuboid6) {
|
||||
val face = CCRenderState.model.asInstanceOf[BlockFace]
|
||||
if (pass != -1)
|
||||
face.computeLightCoords()
|
||||
|
||||
if(face.side > 1)
|
||||
MaterialRenderHelper.start(pos, pass, new UVTranslation(0, bounds.max.y-1) ++ sideIconT)
|
||||
.blockColour(getColour(pass)).lighting().render()
|
||||
}
|
||||
if (face.side == 1)
|
||||
MaterialRenderHelper
|
||||
.start(pos, pass, icont)
|
||||
.blockColour(getColour(pass))
|
||||
.lighting()
|
||||
.render()
|
||||
else
|
||||
MaterialRenderHelper.start(pos, pass, icont).lighting().render()
|
||||
|
||||
if (face.side > 1)
|
||||
MaterialRenderHelper
|
||||
.start(pos, pass, new UVTranslation(0, bounds.max.y - 1) ++ sideIconT)
|
||||
.blockColour(getColour(pass))
|
||||
.lighting()
|
||||
.render()
|
||||
}
|
||||
}
|
||||
|
||||
class TopMicroMaterial($block:Block, $meta:Int = 0) extends BlockMicroMaterial($block, $meta)
|
||||
{
|
||||
override def renderMicroFace(pos:Vector3, pass:Int, bounds:Cuboid6)
|
||||
{
|
||||
val face = CCRenderState.model.asInstanceOf[BlockFace]
|
||||
if(face.side <= 1)
|
||||
MaterialRenderHelper.start(pos, pass, icont).blockColour(getColour(pass)).lighting().render()
|
||||
else
|
||||
MaterialRenderHelper.start(pos, pass, new UVTranslation(0, bounds.max.y-1) ++ icont)
|
||||
.blockColour(getColour(pass)).lighting().render()
|
||||
}
|
||||
}
|
||||
class TopMicroMaterial($block: Block, $meta: Int = 0)
|
||||
extends BlockMicroMaterial($block, $meta) {
|
||||
override def renderMicroFace(pos: Vector3, pass: Int, bounds: Cuboid6) {
|
||||
val face = CCRenderState.model.asInstanceOf[BlockFace]
|
||||
if (face.side <= 1)
|
||||
MaterialRenderHelper
|
||||
.start(pos, pass, icont)
|
||||
.blockColour(getColour(pass))
|
||||
.lighting()
|
||||
.render()
|
||||
else
|
||||
MaterialRenderHelper
|
||||
.start(pos, pass, new UVTranslation(0, bounds.max.y - 1) ++ icont)
|
||||
.blockColour(getColour(pass))
|
||||
.lighting()
|
||||
.render()
|
||||
}
|
||||
}
|
||||
@@ -16,226 +16,401 @@ import org.lwjgl.opengl.GL11
|
||||
import codechicken.lib.render.uv.IconTransformation
|
||||
import codechicken.lib.lighting.LightMatrix
|
||||
|
||||
object HollowPlacement extends PlacementProperties
|
||||
{
|
||||
object HollowPlacementGrid extends FaceEdgeGrid(3 / 8D)
|
||||
object HollowPlacement extends PlacementProperties {
|
||||
object HollowPlacementGrid extends FaceEdgeGrid(3 / 8d)
|
||||
|
||||
def microClass = HollowMicroClass
|
||||
def microClass = HollowMicroClass
|
||||
|
||||
def placementGrid = HollowPlacementGrid
|
||||
def placementGrid = HollowPlacementGrid
|
||||
|
||||
def opposite(slot: Int, side: Int) = slot ^ 1
|
||||
def opposite(slot: Int, side: Int) = slot ^ 1
|
||||
|
||||
override def expand(slot: Int, side: Int) = sneakOpposite(slot, side)
|
||||
override def expand(slot: Int, side: Int) = sneakOpposite(slot, side)
|
||||
|
||||
override def sneakOpposite(slot: Int, side: Int) = slot == (side ^ 1)
|
||||
override def sneakOpposite(slot: Int, side: Int) = slot == (side ^ 1)
|
||||
}
|
||||
|
||||
object HollowMicroClass extends CommonMicroClass
|
||||
{
|
||||
var pBoxes: Array[Seq[Cuboid6]] = new Array(256)
|
||||
var occBounds: Array[Cuboid6] = new Array(256)
|
||||
for (s <- 0 until 6) {
|
||||
val transform = sideRotations(s).at(center)
|
||||
for (t <- 1 until 8) {
|
||||
val d = t / 8D
|
||||
val w1 = 1 / 8D
|
||||
val w2 = 3 / 16D
|
||||
pBoxes(t << 4 | s) = Seq(
|
||||
new Cuboid6(0, 0, 0, w1, d, 1),
|
||||
new Cuboid6(1 - w1, 0, 0, 1, d, 1),
|
||||
new Cuboid6(w1, 0, 0, 1 - w1, d, w1),
|
||||
new Cuboid6(w1, 0, 1 - w1, 1 - w1, d, 1))
|
||||
.map(_.apply(transform))
|
||||
occBounds(t << 4 | s) = new Cuboid6(1 / 8D, 0, 1 / 8D, 7 / 8D, d, 7 / 8D).apply(transform)
|
||||
}
|
||||
object HollowMicroClass extends CommonMicroClass {
|
||||
var pBoxes: Array[Seq[Cuboid6]] = new Array(256)
|
||||
var occBounds: Array[Cuboid6] = new Array(256)
|
||||
for (s <- 0 until 6) {
|
||||
val transform = sideRotations(s).at(center)
|
||||
for (t <- 1 until 8) {
|
||||
val d = t / 8d
|
||||
val w1 = 1 / 8d
|
||||
val w2 = 3 / 16d
|
||||
pBoxes(t << 4 | s) = Seq(
|
||||
new Cuboid6(0, 0, 0, w1, d, 1),
|
||||
new Cuboid6(1 - w1, 0, 0, 1, d, 1),
|
||||
new Cuboid6(w1, 0, 0, 1 - w1, d, w1),
|
||||
new Cuboid6(w1, 0, 1 - w1, 1 - w1, d, 1)
|
||||
)
|
||||
.map(_.apply(transform))
|
||||
occBounds(t << 4 | s) =
|
||||
new Cuboid6(1 / 8d, 0, 1 / 8d, 7 / 8d, d, 7 / 8d).apply(transform)
|
||||
}
|
||||
}
|
||||
|
||||
def getName = "mcr_hllw"
|
||||
def getName = "mcr_hllw"
|
||||
|
||||
def baseTrait = classOf[HollowMicroblock]
|
||||
def clientTrait = classOf[HollowMicroblockClient]
|
||||
def baseTrait = classOf[HollowMicroblock]
|
||||
def clientTrait = classOf[HollowMicroblockClient]
|
||||
|
||||
def itemSlot = 3
|
||||
def itemSlot = 3
|
||||
|
||||
def placementProperties = HollowPlacement
|
||||
def placementProperties = HollowPlacement
|
||||
|
||||
def getResistanceFactor = 1
|
||||
def getResistanceFactor = 1
|
||||
}
|
||||
|
||||
trait HollowMicroblockClient extends HollowMicroblock with CommonMicroblockClient
|
||||
{
|
||||
renderMask |= 8 << 8
|
||||
trait HollowMicroblockClient
|
||||
extends HollowMicroblock
|
||||
with CommonMicroblockClient {
|
||||
renderMask |= 8 << 8
|
||||
|
||||
override def recalcBounds() {
|
||||
super.recalcBounds()
|
||||
renderMask = renderMask & 0xFF | getHollowSize << 8
|
||||
override def recalcBounds() {
|
||||
super.recalcBounds()
|
||||
renderMask = renderMask & 0xff | getHollowSize << 8
|
||||
}
|
||||
|
||||
override def drawBreaking(renderBlocks: RenderBlocks) {
|
||||
CCRenderState.reset()
|
||||
CCRenderState.setPipeline(
|
||||
new Translation(x, y, z),
|
||||
new IconTransformation(renderBlocks.overrideBlockTexture)
|
||||
)
|
||||
renderHollow(
|
||||
null,
|
||||
0,
|
||||
getBounds,
|
||||
0,
|
||||
false,
|
||||
(
|
||||
pos: Vector3,
|
||||
mat: IMicroMaterial,
|
||||
pass: Int,
|
||||
c: Cuboid6,
|
||||
sideMask: Int
|
||||
) => BlockRenderer.renderCuboid(c, sideMask)
|
||||
)
|
||||
}
|
||||
|
||||
override def render(pos: Vector3, pass: Int) {
|
||||
if (pass == -1)
|
||||
renderHollow(
|
||||
pos,
|
||||
pass,
|
||||
getBounds,
|
||||
0,
|
||||
false,
|
||||
MicroblockRender.renderCuboid
|
||||
)
|
||||
else if (isTransparent)
|
||||
renderHollow(
|
||||
pos,
|
||||
pass,
|
||||
renderBounds,
|
||||
renderMask,
|
||||
false,
|
||||
MicroblockRender.renderCuboid
|
||||
)
|
||||
else {
|
||||
renderHollow(
|
||||
pos,
|
||||
pass,
|
||||
renderBounds,
|
||||
renderMask | 1 << getSlot,
|
||||
false,
|
||||
MicroblockRender.renderCuboid
|
||||
)
|
||||
renderHollow(
|
||||
pos,
|
||||
pass,
|
||||
Cuboid6.full,
|
||||
~(1 << getSlot),
|
||||
true,
|
||||
MicroblockRender.renderCuboid
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override def drawBreaking(renderBlocks: RenderBlocks) {
|
||||
CCRenderState.reset()
|
||||
CCRenderState.setPipeline(new Translation(x, y, z), new IconTransformation(renderBlocks.overrideBlockTexture))
|
||||
renderHollow(null, 0, getBounds, 0, false,
|
||||
(pos: Vector3, mat: IMicroMaterial, pass: Int, c: Cuboid6, sideMask: Int) =>
|
||||
BlockRenderer.renderCuboid(c, sideMask))
|
||||
def renderHollow(
|
||||
pos: Vector3,
|
||||
pass: Int,
|
||||
c: Cuboid6,
|
||||
sideMask: Int,
|
||||
face: Boolean,
|
||||
f: (Vector3, IMicroMaterial, Int, Cuboid6, Int) => Unit
|
||||
) {
|
||||
val mat = getIMaterial
|
||||
val size = renderMask >> 8
|
||||
val d1 = 0.5 - size / 32d
|
||||
val d2 = 0.5 + size / 32d
|
||||
val x1 = c.min.x
|
||||
val x2 = c.max.x
|
||||
val y1 = c.min.y
|
||||
val y2 = c.max.y
|
||||
val z1 = c.min.z
|
||||
val z2 = c.max.z
|
||||
|
||||
var iMask = 0
|
||||
getSlot match {
|
||||
case 0 | 1 =>
|
||||
if (face)
|
||||
iMask = 0x3c
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(d1, y1, d2, d2, y2, z2),
|
||||
0x3b | iMask
|
||||
) // -z internal
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(d1, y1, z1, d2, y2, d1),
|
||||
0x37 | iMask
|
||||
) // +z internal
|
||||
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(d2, y1, d1, x2, y2, d2),
|
||||
sideMask & 0x23 | 0xc | iMask
|
||||
) // -x internal -y+y+x external
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(x1, y1, d1, d1, y2, d2),
|
||||
sideMask & 0x13 | 0xc | iMask
|
||||
) // +x internal -y+y-x external
|
||||
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(x1, y1, d2, x2, y2, z2),
|
||||
sideMask & 0x3b | 4 | iMask
|
||||
) // -y+y+z-x+x external
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(x1, y1, z1, x2, y2, d1),
|
||||
sideMask & 0x37 | 8 | iMask
|
||||
) // -y+y-z-x+x external
|
||||
case 2 | 3 =>
|
||||
if (face)
|
||||
iMask = 0x33
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(d2, d1, z1, x2, d2, z2),
|
||||
0x2f | iMask
|
||||
) // -x internal
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(x1, d1, z1, d1, d2, z2),
|
||||
0x1f | iMask
|
||||
) // +x internal
|
||||
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(d1, d2, z1, d2, y2, z2),
|
||||
sideMask & 0xe | 0x30 | iMask
|
||||
) // -y internal -z+z+y external
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(d1, y1, z1, d2, d1, z2),
|
||||
sideMask & 0xd | 0x30 | iMask
|
||||
) // +y internal -z+z-y external
|
||||
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(d2, y1, z1, x2, y2, z2),
|
||||
sideMask & 0x2f | 0x10 | iMask
|
||||
) // -z+z+x-y+y external
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(x1, y1, z1, d1, y2, z2),
|
||||
sideMask & 0x1f | 0x20 | iMask
|
||||
) // -z+z-x-y+y external
|
||||
case 4 | 5 =>
|
||||
if (face)
|
||||
iMask = 0xf
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(x1, d2, d1, x2, y2, d2),
|
||||
0x3e | iMask
|
||||
) // -y internal
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(x1, y1, d1, x2, d1, d2),
|
||||
0x3d | iMask
|
||||
) // +y internal
|
||||
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(x1, d1, d2, x2, d2, z2),
|
||||
sideMask & 0x38 | 3 | iMask
|
||||
) // -z internal -x+x+z external
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(x1, d1, z1, x2, d2, d1),
|
||||
sideMask & 0x34 | 3 | iMask
|
||||
) // +z internal -x+x-z external
|
||||
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(x1, d2, z1, x2, y2, z2),
|
||||
sideMask & 0x3e | 1 | iMask
|
||||
) // -x+x+y-z+z external
|
||||
f(
|
||||
pos,
|
||||
mat,
|
||||
pass,
|
||||
new Cuboid6(x1, y1, z1, x2, d1, z2),
|
||||
sideMask & 0x3d | 2 | iMask
|
||||
) // -x+x-y-z+z external
|
||||
}
|
||||
}
|
||||
|
||||
override def render(pos: Vector3, pass: Int) {
|
||||
if (pass == -1)
|
||||
renderHollow(pos, pass, getBounds, 0, false, MicroblockRender.renderCuboid)
|
||||
else if (isTransparent)
|
||||
renderHollow(pos, pass, renderBounds, renderMask, false, MicroblockRender.renderCuboid)
|
||||
else {
|
||||
renderHollow(pos, pass, renderBounds, renderMask | 1 << getSlot, false, MicroblockRender.renderCuboid)
|
||||
renderHollow(pos, pass, Cuboid6.full, ~(1 << getSlot), true, MicroblockRender.renderCuboid)
|
||||
}
|
||||
}
|
||||
override def drawHighlight(
|
||||
hit: MovingObjectPosition,
|
||||
player: EntityPlayer,
|
||||
frame: Float
|
||||
): Boolean = {
|
||||
val size = getHollowSize
|
||||
val d1 = 0.5 - size / 32d
|
||||
val d2 = 0.5 + size / 32d
|
||||
val t = (shape >> 4) / 8d
|
||||
|
||||
def renderHollow(pos: Vector3, pass: Int, c: Cuboid6, sideMask: Int, face: Boolean,
|
||||
f: (Vector3, IMicroMaterial, Int, Cuboid6, Int) => Unit) {
|
||||
val mat = getIMaterial
|
||||
val size = renderMask >> 8
|
||||
val d1 = 0.5 - size / 32D
|
||||
val d2 = 0.5 + size / 32D
|
||||
val x1 = c.min.x
|
||||
val x2 = c.max.x
|
||||
val y1 = c.min.y
|
||||
val y2 = c.max.y
|
||||
val z1 = c.min.z
|
||||
val z2 = c.max.z
|
||||
import GL11._
|
||||
glEnable(GL_BLEND)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
glColor4f(0, 0, 0, 0.4f)
|
||||
glLineWidth(2)
|
||||
glDepthMask(false)
|
||||
glPushMatrix()
|
||||
RenderUtils.translateToWorldCoords(player, frame)
|
||||
glTranslated(x, y, z)
|
||||
sideRotations(shape & 0xf).at(center).glApply()
|
||||
|
||||
var iMask = 0
|
||||
getSlot match {
|
||||
case 0 | 1 =>
|
||||
if (face)
|
||||
iMask = 0x3C
|
||||
f(pos, mat, pass, new Cuboid6(d1, y1, d2, d2, y2, z2), 0x3B | iMask) //-z internal
|
||||
f(pos, mat, pass, new Cuboid6(d1, y1, z1, d2, y2, d1), 0x37 | iMask) //+z internal
|
||||
|
||||
f(pos, mat, pass, new Cuboid6(d2, y1, d1, x2, y2, d2), sideMask & 0x23 | 0xC | iMask) //-x internal -y+y+x external
|
||||
f(pos, mat, pass, new Cuboid6(x1, y1, d1, d1, y2, d2), sideMask & 0x13 | 0xC | iMask) //+x internal -y+y-x external
|
||||
|
||||
f(pos, mat, pass, new Cuboid6(x1, y1, d2, x2, y2, z2), sideMask & 0x3B | 4 | iMask) //-y+y+z-x+x external
|
||||
f(pos, mat, pass, new Cuboid6(x1, y1, z1, x2, y2, d1), sideMask & 0x37 | 8 | iMask) //-y+y-z-x+x external
|
||||
case 2 | 3 =>
|
||||
if (face)
|
||||
iMask = 0x33
|
||||
f(pos, mat, pass, new Cuboid6(d2, d1, z1, x2, d2, z2), 0x2F | iMask) //-x internal
|
||||
f(pos, mat, pass, new Cuboid6(x1, d1, z1, d1, d2, z2), 0x1F | iMask) //+x internal
|
||||
|
||||
f(pos, mat, pass, new Cuboid6(d1, d2, z1, d2, y2, z2), sideMask & 0xE | 0x30 | iMask) //-y internal -z+z+y external
|
||||
f(pos, mat, pass, new Cuboid6(d1, y1, z1, d2, d1, z2), sideMask & 0xD | 0x30 | iMask) //+y internal -z+z-y external
|
||||
|
||||
f(pos, mat, pass, new Cuboid6(d2, y1, z1, x2, y2, z2), sideMask & 0x2F | 0x10 | iMask) //-z+z+x-y+y external
|
||||
f(pos, mat, pass, new Cuboid6(x1, y1, z1, d1, y2, z2), sideMask & 0x1F | 0x20 | iMask) //-z+z-x-y+y external
|
||||
case 4 | 5 =>
|
||||
if (face)
|
||||
iMask = 0xF
|
||||
f(pos, mat, pass, new Cuboid6(x1, d2, d1, x2, y2, d2), 0x3E | iMask) //-y internal
|
||||
f(pos, mat, pass, new Cuboid6(x1, y1, d1, x2, d1, d2), 0x3D | iMask) //+y internal
|
||||
|
||||
f(pos, mat, pass, new Cuboid6(x1, d1, d2, x2, d2, z2), sideMask & 0x38 | 3 | iMask) //-z internal -x+x+z external
|
||||
f(pos, mat, pass, new Cuboid6(x1, d1, z1, x2, d2, d1), sideMask & 0x34 | 3 | iMask) //+z internal -x+x-z external
|
||||
|
||||
f(pos, mat, pass, new Cuboid6(x1, d2, z1, x2, y2, z2), sideMask & 0x3E | 1 | iMask) //-x+x+y-z+z external
|
||||
f(pos, mat, pass, new Cuboid6(x1, y1, z1, x2, d1, z2), sideMask & 0x3D | 2 | iMask) //-x+x-y-z+z external
|
||||
}
|
||||
}
|
||||
|
||||
override def drawHighlight(hit: MovingObjectPosition, player: EntityPlayer, frame: Float): Boolean = {
|
||||
val size = getHollowSize
|
||||
val d1 = 0.5 - size / 32D
|
||||
val d2 = 0.5 + size / 32D
|
||||
val t = (shape >> 4) / 8D
|
||||
|
||||
import GL11._
|
||||
glEnable(GL_BLEND)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
glDisable(GL_TEXTURE_2D)
|
||||
glColor4f(0, 0, 0, 0.4F)
|
||||
glLineWidth(2)
|
||||
glDepthMask(false)
|
||||
glPushMatrix()
|
||||
RenderUtils.translateToWorldCoords(player, frame)
|
||||
glTranslated(x, y, z)
|
||||
sideRotations(shape & 0xF).at(center).glApply()
|
||||
|
||||
RenderUtils.drawCuboidOutline(new Cuboid6(0, 0, 0, 1, t, 1).expand(0.001))
|
||||
RenderUtils.drawCuboidOutline(new Cuboid6(d1, 0, d1, d2, t, d2).expand(-0.001))
|
||||
glPopMatrix()
|
||||
glDepthMask(true)
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
glDisable(GL_BLEND)
|
||||
return true
|
||||
}
|
||||
RenderUtils.drawCuboidOutline(new Cuboid6(0, 0, 0, 1, t, 1).expand(0.001))
|
||||
RenderUtils.drawCuboidOutline(
|
||||
new Cuboid6(d1, 0, d1, d2, t, d2).expand(-0.001)
|
||||
)
|
||||
glPopMatrix()
|
||||
glDepthMask(true)
|
||||
glEnable(GL_TEXTURE_2D)
|
||||
glDisable(GL_BLEND)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
trait HollowMicroblock extends CommonMicroblock with TFacePart with TNormalOcclusion
|
||||
{
|
||||
def microClass = HollowMicroClass
|
||||
trait HollowMicroblock
|
||||
extends CommonMicroblock
|
||||
with TFacePart
|
||||
with TNormalOcclusion {
|
||||
def microClass = HollowMicroClass
|
||||
|
||||
def getBounds: Cuboid6 = FaceMicroClass.aBounds(shape)
|
||||
def getBounds: Cuboid6 = FaceMicroClass.aBounds(shape)
|
||||
|
||||
override def getPartialOcclusionBoxes = HollowMicroClass.pBoxes(shape)
|
||||
override def getPartialOcclusionBoxes = HollowMicroClass.pBoxes(shape)
|
||||
|
||||
def getHollowSize = tile match {
|
||||
case null => 8
|
||||
case _ => tile.partMap(6) match {
|
||||
case part: ISidedHollowConnect => part.getHollowSize(getSlot)
|
||||
case _ => 8
|
||||
}
|
||||
def getHollowSize = tile match {
|
||||
case null => 8
|
||||
case _ =>
|
||||
tile.partMap(6) match {
|
||||
case part: ISidedHollowConnect => part.getHollowSize(getSlot)
|
||||
case _ => 8
|
||||
}
|
||||
}
|
||||
|
||||
def getOcclusionBoxes = {
|
||||
val size = getHollowSize
|
||||
val c = HollowMicroClass.occBounds(shape)
|
||||
val d1 = 0.5 - size / 32d
|
||||
val d2 = 0.5 + size / 32d
|
||||
val x1 = c.min.x
|
||||
val x2 = c.max.x
|
||||
val y1 = c.min.y
|
||||
val y2 = c.max.y
|
||||
val z1 = c.min.z
|
||||
val z2 = c.max.z
|
||||
|
||||
getSlot match {
|
||||
case 0 | 1 =>
|
||||
Seq(
|
||||
new Cuboid6(d2, y1, d1, x2, y2, d2),
|
||||
new Cuboid6(x1, y1, d1, d1, y2, d2),
|
||||
new Cuboid6(x1, y1, d2, x2, y2, z2),
|
||||
new Cuboid6(x1, y1, z1, x2, y2, d1)
|
||||
)
|
||||
case 2 | 3 =>
|
||||
Seq(
|
||||
new Cuboid6(d1, d2, z1, d2, y2, z2),
|
||||
new Cuboid6(d1, y1, z1, d2, d1, z2),
|
||||
new Cuboid6(d2, y1, z1, x2, y2, z2),
|
||||
new Cuboid6(x1, y1, z1, d1, y2, z2)
|
||||
)
|
||||
case 4 | 5 =>
|
||||
Seq(
|
||||
new Cuboid6(x1, d1, d2, x2, d2, z2),
|
||||
new Cuboid6(x1, d1, z1, x2, d2, d1),
|
||||
new Cuboid6(x1, d2, z1, x2, y2, z2),
|
||||
new Cuboid6(x1, y1, z1, x2, d1, z2)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def getOcclusionBoxes = {
|
||||
val size = getHollowSize
|
||||
val c = HollowMicroClass.occBounds(shape)
|
||||
val d1 = 0.5 - size / 32D
|
||||
val d2 = 0.5 + size / 32D
|
||||
val x1 = c.min.x
|
||||
val x2 = c.max.x
|
||||
val y1 = c.min.y
|
||||
val y2 = c.max.y
|
||||
val z1 = c.min.z
|
||||
val z2 = c.max.z
|
||||
override def getCollisionBoxes = {
|
||||
val size = getHollowSize
|
||||
val d1 = 0.5 - size / 32d
|
||||
val d2 = 0.5 + size / 32d
|
||||
val t = (shape >> 4) / 8d
|
||||
|
||||
getSlot match {
|
||||
case 0 | 1 =>
|
||||
Seq(new Cuboid6(d2, y1, d1, x2, y2, d2),
|
||||
new Cuboid6(x1, y1, d1, d1, y2, d2),
|
||||
new Cuboid6(x1, y1, d2, x2, y2, z2),
|
||||
new Cuboid6(x1, y1, z1, x2, y2, d1))
|
||||
case 2 | 3 =>
|
||||
Seq(new Cuboid6(d1, d2, z1, d2, y2, z2),
|
||||
new Cuboid6(d1, y1, z1, d2, d1, z2),
|
||||
new Cuboid6(d2, y1, z1, x2, y2, z2),
|
||||
new Cuboid6(x1, y1, z1, d1, y2, z2))
|
||||
case 4 | 5 =>
|
||||
Seq(new Cuboid6(x1, d1, d2, x2, d2, z2),
|
||||
new Cuboid6(x1, d1, z1, x2, d2, d1),
|
||||
new Cuboid6(x1, d2, z1, x2, y2, z2),
|
||||
new Cuboid6(x1, y1, z1, x2, d1, z2))
|
||||
}
|
||||
}
|
||||
val tr = sideRotations(shape & 0xf).at(center)
|
||||
Seq(
|
||||
new Cuboid6(0, 0, 0, 1, t, d1),
|
||||
new Cuboid6(0, 0, d2, 1, t, 1),
|
||||
new Cuboid6(0, 0, d1, d1, t, d2),
|
||||
new Cuboid6(d2, 0, d1, 1, t, d2)
|
||||
)
|
||||
.map(c => c.apply(tr))
|
||||
}
|
||||
|
||||
override def getCollisionBoxes = {
|
||||
val size = getHollowSize
|
||||
val d1 = 0.5 - size / 32D
|
||||
val d2 = 0.5 + size / 32D
|
||||
val t = (shape >> 4) / 8D
|
||||
override def getSubParts =
|
||||
getCollisionBoxes.map(c => new IndexedCuboid6(0, c))
|
||||
|
||||
val tr = sideRotations(shape & 0xF).at(center)
|
||||
Seq(new Cuboid6(0, 0, 0, 1, t, d1),
|
||||
new Cuboid6(0, 0, d2, 1, t, 1),
|
||||
new Cuboid6(0, 0, d1, d1, t, d2),
|
||||
new Cuboid6(d2, 0, d1, 1, t, d2))
|
||||
.map(c => c.apply(tr))
|
||||
}
|
||||
override def allowCompleteOcclusion = true
|
||||
|
||||
override def getSubParts = getCollisionBoxes.map(c => new IndexedCuboid6(0, c))
|
||||
override def solid(side: Int) = false
|
||||
|
||||
override def allowCompleteOcclusion = true
|
||||
|
||||
override def solid(side: Int) = false
|
||||
|
||||
override def redstoneConductionMap = 0x10
|
||||
}
|
||||
override def redstoneConductionMap = 0x10
|
||||
}
|
||||
@@ -3,20 +3,19 @@ package codechicken.microblock
|
||||
import codechicken.lib.vec.Cuboid6
|
||||
import net.minecraft.world.World
|
||||
|
||||
/**
|
||||
* Wrapper class blocks/tiles/parts wanting to use the micro material system to render part of their models.
|
||||
*/
|
||||
/** Wrapper class blocks/tiles/parts wanting to use the micro material system to
|
||||
* render part of their models.
|
||||
*/
|
||||
trait IMicroMaterialRender {
|
||||
/**
|
||||
* May be null for inventory rendering.
|
||||
*/
|
||||
def world:World
|
||||
def x:Int
|
||||
def y:Int
|
||||
def z:Int
|
||||
|
||||
/**
|
||||
* Return the bounds of the part for texture mapping side decals like grass
|
||||
*/
|
||||
def getRenderBounds:Cuboid6
|
||||
}
|
||||
|
||||
/** May be null for inventory rendering.
|
||||
*/
|
||||
def world: World
|
||||
def x: Int
|
||||
def y: Int
|
||||
def z: Int
|
||||
|
||||
/** Return the bounds of the part for texture mapping side decals like grass
|
||||
*/
|
||||
def getRenderBounds: Cuboid6
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
package codechicken.microblock
|
||||
|
||||
/**
|
||||
* Implement this on center parts that connect through hollow covers to have the covers render their hollows to fit the connection size
|
||||
*/
|
||||
trait ISidedHollowConnect
|
||||
{
|
||||
/**
|
||||
* @param side The side of the block on which the cover resides
|
||||
* @return The size (width and height) of the connection in pixels. Must be be less than 12 and more than 0
|
||||
*/
|
||||
def getHollowSize(side:Int):Int
|
||||
}
|
||||
/** Implement this on center parts that connect through hollow covers to have
|
||||
* the covers render their hollows to fit the connection size
|
||||
*/
|
||||
trait ISidedHollowConnect {
|
||||
|
||||
/** @param side
|
||||
* The side of the block on which the cover resides
|
||||
* @return
|
||||
* The size (width and height) of the connection in pixels. Must be be less
|
||||
* than 12 and more than 0
|
||||
*/
|
||||
def getHollowSize(side: Int): Int
|
||||
}
|
||||
@@ -23,148 +23,184 @@ import codechicken.lib.render.TextureUtils
|
||||
import net.minecraft.util.MovingObjectPosition.MovingObjectType
|
||||
import CommonMicroClass._
|
||||
|
||||
class ItemMicroPart extends Item
|
||||
{
|
||||
setUnlocalizedName("microblock")
|
||||
setHasSubtypes(true)
|
||||
|
||||
override def getItemStackDisplayName(stack:ItemStack):String =
|
||||
{
|
||||
val material = getMaterial(stack)
|
||||
val mcrClass = getMicroClass(stack.getItemDamage)
|
||||
val size = stack.getItemDamage&0xFF
|
||||
if(material == null || mcrClass == null)
|
||||
return "Unnamed"
|
||||
|
||||
return StatCollector.translateToLocalFormatted(mcrClass.getName+"."+size+".name", material.getLocalizedName)
|
||||
class ItemMicroPart extends Item {
|
||||
setUnlocalizedName("microblock")
|
||||
setHasSubtypes(true)
|
||||
|
||||
override def getItemStackDisplayName(stack: ItemStack): String = {
|
||||
val material = getMaterial(stack)
|
||||
val mcrClass = getMicroClass(stack.getItemDamage)
|
||||
val size = stack.getItemDamage & 0xff
|
||||
if (material == null || mcrClass == null)
|
||||
return "Unnamed"
|
||||
|
||||
return StatCollector.translateToLocalFormatted(
|
||||
mcrClass.getName + "." + size + ".name",
|
||||
material.getLocalizedName
|
||||
)
|
||||
}
|
||||
|
||||
override def getSubItems(item: Item, tab: CreativeTabs, list$ : List[_]) {
|
||||
val list = list$.asInstanceOf[List[ItemStack]]
|
||||
for (classId <- 0 until classes.length) {
|
||||
val mcrClass = classes(classId)
|
||||
if (mcrClass != null)
|
||||
for (size <- Seq(1, 2, 4))
|
||||
MicroMaterialRegistry.getIdMap.foreach(e =>
|
||||
list.add(create(classId << 8 | size, e._1))
|
||||
)
|
||||
}
|
||||
|
||||
override def getSubItems(item:Item, tab:CreativeTabs, list$:List[_])
|
||||
{
|
||||
val list = list$.asInstanceOf[List[ItemStack]]
|
||||
for(classId <- 0 until classes.length)
|
||||
{
|
||||
val mcrClass = classes(classId)
|
||||
if(mcrClass != null)
|
||||
for(size <- Seq(1, 2, 4))
|
||||
MicroMaterialRegistry.getIdMap.foreach(e => list.add(create(classId<<8|size, e._1)))
|
||||
}
|
||||
}
|
||||
|
||||
override def registerIcons(register:IIconRegister){}
|
||||
|
||||
override def onItemUse(item:ItemStack, player:EntityPlayer, world:World, x:Int, y:Int, z:Int, s:Int, hitX:Float, hitY:Float, hitZ:Float):Boolean =
|
||||
{
|
||||
val material = getMaterialID(item)
|
||||
val mcrClass = getMicroClass(item.getItemDamage)
|
||||
val size = item.getItemDamage&0xFF
|
||||
if(material < 0 || mcrClass == null)
|
||||
return false
|
||||
|
||||
val hit = RayTracer.retraceBlock(world, player, x, y, z)
|
||||
if(hit != null && hit.typeOfHit == MovingObjectType.BLOCK)
|
||||
{
|
||||
val placement = MicroblockPlacement(player, hit, size, material, !player.capabilities.isCreativeMode, mcrClass.placementProperties)
|
||||
if(placement == null)
|
||||
return false
|
||||
|
||||
if(!world.isRemote)
|
||||
{
|
||||
placement.place(world, player, item)
|
||||
if(!player.capabilities.isCreativeMode)
|
||||
placement.consume(world, player, item)
|
||||
|
||||
val sound = MicroMaterialRegistry.getMaterial(material).getSound
|
||||
if(sound != null)
|
||||
world.playSoundEffect(placement.pos.x + 0.5D, placement.pos.y + 0.5D, placement.pos.z + 0.5D, sound.func_150496_b, (sound.getVolume + 1.0F) / 2.0F, sound.getPitch * 0.8F)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override def registerIcons(register: IIconRegister) {}
|
||||
|
||||
override def onItemUse(
|
||||
item: ItemStack,
|
||||
player: EntityPlayer,
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
s: Int,
|
||||
hitX: Float,
|
||||
hitY: Float,
|
||||
hitZ: Float
|
||||
): Boolean = {
|
||||
val material = getMaterialID(item)
|
||||
val mcrClass = getMicroClass(item.getItemDamage)
|
||||
val size = item.getItemDamage & 0xff
|
||||
if (material < 0 || mcrClass == null)
|
||||
return false
|
||||
|
||||
val hit = RayTracer.retraceBlock(world, player, x, y, z)
|
||||
if (hit != null && hit.typeOfHit == MovingObjectType.BLOCK) {
|
||||
val placement = MicroblockPlacement(
|
||||
player,
|
||||
hit,
|
||||
size,
|
||||
material,
|
||||
!player.capabilities.isCreativeMode,
|
||||
mcrClass.placementProperties
|
||||
)
|
||||
if (placement == null)
|
||||
return false
|
||||
|
||||
if (!world.isRemote) {
|
||||
placement.place(world, player, item)
|
||||
if (!player.capabilities.isCreativeMode)
|
||||
placement.consume(world, player, item)
|
||||
|
||||
val sound = MicroMaterialRegistry.getMaterial(material).getSound
|
||||
if (sound != null)
|
||||
world.playSoundEffect(
|
||||
placement.pos.x + 0.5d,
|
||||
placement.pos.y + 0.5d,
|
||||
placement.pos.z + 0.5d,
|
||||
sound.func_150496_b,
|
||||
(sound.getVolume + 1.0f) / 2.0f,
|
||||
sound.getPitch * 0.8f
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
object ItemMicroPart
|
||||
{
|
||||
def checkTagCompound(stack:ItemStack)
|
||||
{
|
||||
if(!stack.hasTagCompound)
|
||||
stack.setTagCompound(new NBTTagCompound())
|
||||
}
|
||||
|
||||
def create(damage:Int, material:Int):ItemStack = create(damage, MicroMaterialRegistry.materialName(material))
|
||||
|
||||
def create(damage:Int, material:String):ItemStack = create(1, damage, material)
|
||||
|
||||
def create(amount:Int, damage:Int, material:String):ItemStack =
|
||||
{
|
||||
val stack = new ItemStack(MicroblockProxy.itemMicro, amount, damage)
|
||||
checkTagCompound(stack)
|
||||
stack.getTagCompound.setString("mat", material)
|
||||
return stack
|
||||
}
|
||||
|
||||
def getMaterial(stack:ItemStack):IMicroMaterial =
|
||||
{
|
||||
checkTagCompound(stack)
|
||||
if(!stack.getTagCompound.hasKey("mat"))
|
||||
return null
|
||||
object ItemMicroPart {
|
||||
def checkTagCompound(stack: ItemStack) {
|
||||
if (!stack.hasTagCompound)
|
||||
stack.setTagCompound(new NBTTagCompound())
|
||||
}
|
||||
|
||||
return MicroMaterialRegistry.getMaterial(stack.getTagCompound.getString("mat"))
|
||||
}
|
||||
|
||||
def getMaterialID(stack:ItemStack):Int =
|
||||
{
|
||||
checkTagCompound(stack)
|
||||
if(!stack.getTagCompound.hasKey("mat"))
|
||||
return 0
|
||||
def create(damage: Int, material: Int): ItemStack =
|
||||
create(damage, MicroMaterialRegistry.materialName(material))
|
||||
|
||||
return MicroMaterialRegistry.materialID(stack.getTagCompound.getString("mat"))
|
||||
}
|
||||
def create(damage: Int, material: String): ItemStack =
|
||||
create(1, damage, material)
|
||||
|
||||
def create(amount: Int, damage: Int, material: String): ItemStack = {
|
||||
val stack = new ItemStack(MicroblockProxy.itemMicro, amount, damage)
|
||||
checkTagCompound(stack)
|
||||
stack.getTagCompound.setString("mat", material)
|
||||
return stack
|
||||
}
|
||||
|
||||
def getMaterial(stack: ItemStack): IMicroMaterial = {
|
||||
checkTagCompound(stack)
|
||||
if (!stack.getTagCompound.hasKey("mat"))
|
||||
return null
|
||||
|
||||
return MicroMaterialRegistry.getMaterial(
|
||||
stack.getTagCompound.getString("mat")
|
||||
)
|
||||
}
|
||||
|
||||
def getMaterialID(stack: ItemStack): Int = {
|
||||
checkTagCompound(stack)
|
||||
if (!stack.getTagCompound.hasKey("mat"))
|
||||
return 0
|
||||
|
||||
return MicroMaterialRegistry.materialID(
|
||||
stack.getTagCompound.getString("mat")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object ItemMicroPartRenderer extends IItemRenderer
|
||||
{
|
||||
def handleRenderType(item:ItemStack, t:ItemRenderType) = true
|
||||
|
||||
def shouldUseRenderHelper(t:ItemRenderType, item:ItemStack, helper:ItemRendererHelper) = true
|
||||
|
||||
def renderItem(t:ItemRenderType, item:ItemStack, data:Object*)
|
||||
{
|
||||
val material = getMaterial(item)
|
||||
val mcrClass = getMicroClass(item.getItemDamage)
|
||||
val size = item.getItemDamage&0xFF
|
||||
if(material == null || mcrClass == null)
|
||||
return
|
||||
|
||||
GL11.glPushMatrix()
|
||||
if(t == ItemRenderType.ENTITY)
|
||||
GL11.glScaled(0.5, 0.5, 0.5)
|
||||
if(t == ItemRenderType.INVENTORY || t == ItemRenderType.ENTITY)
|
||||
GL11.glTranslatef(-0.5F, -0.5F, -0.5F)
|
||||
object ItemMicroPartRenderer extends IItemRenderer {
|
||||
def handleRenderType(item: ItemStack, t: ItemRenderType) = true
|
||||
|
||||
TextureUtils.bindAtlas(0)
|
||||
CCRenderState.reset()
|
||||
CCRenderState.useNormals = true
|
||||
CCRenderState.pullLightmap()
|
||||
CCRenderState.startDrawing()
|
||||
val part = mcrClass.create(true, getMaterialID(item)).asInstanceOf[MicroblockClient]
|
||||
part.setShape(size, mcrClass.itemSlot)
|
||||
part.render(new Vector3(0.5, 0.5, 0.5).subtract(part.getBounds.center), -1)
|
||||
CCRenderState.draw()
|
||||
GL11.glPopMatrix()
|
||||
}
|
||||
|
||||
def renderHighlight(player:EntityPlayer, stack:ItemStack, hit:MovingObjectPosition):Boolean =
|
||||
{
|
||||
val material = getMaterialID(stack)
|
||||
val mcrClass = getMicroClass(stack.getItemDamage)
|
||||
val size = stack.getItemDamage&0xFF
|
||||
if(material < 0 || mcrClass == null)
|
||||
return false
|
||||
|
||||
return MicroMaterialRegistry.renderHighlight(player, hit, mcrClass, size, material)
|
||||
}
|
||||
}
|
||||
def shouldUseRenderHelper(
|
||||
t: ItemRenderType,
|
||||
item: ItemStack,
|
||||
helper: ItemRendererHelper
|
||||
) = true
|
||||
|
||||
def renderItem(t: ItemRenderType, item: ItemStack, data: Object*) {
|
||||
val material = getMaterial(item)
|
||||
val mcrClass = getMicroClass(item.getItemDamage)
|
||||
val size = item.getItemDamage & 0xff
|
||||
if (material == null || mcrClass == null)
|
||||
return
|
||||
|
||||
GL11.glPushMatrix()
|
||||
if (t == ItemRenderType.ENTITY)
|
||||
GL11.glScaled(0.5, 0.5, 0.5)
|
||||
if (t == ItemRenderType.INVENTORY || t == ItemRenderType.ENTITY)
|
||||
GL11.glTranslatef(-0.5f, -0.5f, -0.5f)
|
||||
|
||||
TextureUtils.bindAtlas(0)
|
||||
CCRenderState.reset()
|
||||
CCRenderState.useNormals = true
|
||||
CCRenderState.pullLightmap()
|
||||
CCRenderState.startDrawing()
|
||||
val part =
|
||||
mcrClass.create(true, getMaterialID(item)).asInstanceOf[MicroblockClient]
|
||||
part.setShape(size, mcrClass.itemSlot)
|
||||
part.render(new Vector3(0.5, 0.5, 0.5).subtract(part.getBounds.center), -1)
|
||||
CCRenderState.draw()
|
||||
GL11.glPopMatrix()
|
||||
}
|
||||
|
||||
def renderHighlight(
|
||||
player: EntityPlayer,
|
||||
stack: ItemStack,
|
||||
hit: MovingObjectPosition
|
||||
): Boolean = {
|
||||
val material = getMaterialID(stack)
|
||||
val mcrClass = getMicroClass(stack.getItemDamage)
|
||||
val size = stack.getItemDamage & 0xff
|
||||
if (material < 0 || mcrClass == null)
|
||||
return false
|
||||
|
||||
return MicroMaterialRegistry.renderHighlight(
|
||||
player,
|
||||
hit,
|
||||
mcrClass,
|
||||
size,
|
||||
material
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -21,79 +21,116 @@ import net.minecraft.client.renderer.texture.{TextureMap, IIconRegister}
|
||||
import codechicken.lib.render.uv.UVTranslation
|
||||
import codechicken.microblock.handler.MicroblockProxy
|
||||
|
||||
/**
|
||||
* Interface for items that are 'saws'
|
||||
*/
|
||||
trait Saw extends Item
|
||||
{
|
||||
/**
|
||||
* The maximum harvest level that some version of this saw is capable of cutting
|
||||
*/
|
||||
def getMaxCuttingStrength:Int = getCuttingStrength(new ItemStack(this))
|
||||
/**
|
||||
* The harvest level this saw is capable of cutting
|
||||
*/
|
||||
def getCuttingStrength(item:ItemStack):Int
|
||||
/** Interface for items that are 'saws'
|
||||
*/
|
||||
trait Saw extends Item {
|
||||
|
||||
/** The maximum harvest level that some version of this saw is capable of
|
||||
* cutting
|
||||
*/
|
||||
def getMaxCuttingStrength: Int = getCuttingStrength(new ItemStack(this))
|
||||
|
||||
/** The harvest level this saw is capable of cutting
|
||||
*/
|
||||
def getCuttingStrength(item: ItemStack): Int
|
||||
}
|
||||
|
||||
class ItemSaw(sawTag:ConfigTag, val harvestLevel:Int) extends Item with Saw
|
||||
{
|
||||
{
|
||||
val maxDamage = sawTag.getTag("durability").getIntValue(1<<harvestLevel+8)
|
||||
if(maxDamage > 0)
|
||||
setMaxDamage(maxDamage)
|
||||
setNoRepair()
|
||||
setMaxStackSize(1)
|
||||
setCreativeTab(net.minecraft.creativetab.CreativeTabs.tabTools)
|
||||
class ItemSaw(sawTag: ConfigTag, val harvestLevel: Int) extends Item with Saw {
|
||||
{
|
||||
val maxDamage =
|
||||
sawTag.getTag("durability").getIntValue(1 << harvestLevel + 8)
|
||||
if (maxDamage > 0)
|
||||
setMaxDamage(maxDamage)
|
||||
setNoRepair()
|
||||
setMaxStackSize(1)
|
||||
setCreativeTab(net.minecraft.creativetab.CreativeTabs.tabTools)
|
||||
}
|
||||
|
||||
override def hasContainerItem = true
|
||||
|
||||
override def getContainerItem(stack: ItemStack) =
|
||||
if (isDamageable)
|
||||
new ItemStack(stack.getItem, 1, stack.getItemDamage + 1)
|
||||
else
|
||||
stack
|
||||
|
||||
override def doesContainerItemLeaveCraftingGrid(stack: ItemStack) = false
|
||||
|
||||
def getCuttingStrength(item: ItemStack) = harvestLevel
|
||||
}
|
||||
|
||||
object ItemSawRenderer extends IItemRenderer {
|
||||
val models = CCModel.parseObjModels(
|
||||
new ResourceLocation("microblock", "models/saw.obj"),
|
||||
7,
|
||||
new SwapYZ()
|
||||
)
|
||||
val handle = models.get("Handle")
|
||||
val holder = models.get("BladeSupport")
|
||||
val blade = models.get("Blade")
|
||||
|
||||
def handleRenderType(item: ItemStack, renderType: ItemRenderType) =
|
||||
!MicroblockProxy.useSawIcons || TextureUtils.isMissing(
|
||||
item.getIconIndex,
|
||||
TextureMap.locationItemsTexture
|
||||
)
|
||||
|
||||
def shouldUseRenderHelper(
|
||||
renderType: ItemRenderType,
|
||||
item: ItemStack,
|
||||
helper: ItemRendererHelper
|
||||
) = true
|
||||
|
||||
def renderItem(renderType: ItemRenderType, item: ItemStack, data: Object*) {
|
||||
val t = renderType match {
|
||||
case INVENTORY =>
|
||||
new TransformationList(
|
||||
new Scale(1.8),
|
||||
new Translation(0, 0, -0.6),
|
||||
new Rotation(-pi / 4, 1, 0, 0),
|
||||
new Rotation(pi * 3 / 4, 0, 1, 0)
|
||||
)
|
||||
case ENTITY =>
|
||||
new TransformationList(
|
||||
new Scale(1),
|
||||
new Translation(0, 0, -0.25),
|
||||
new Rotation(-pi / 4, 1, 0, 0)
|
||||
)
|
||||
case EQUIPPED_FIRST_PERSON =>
|
||||
new TransformationList(
|
||||
new Scale(1.5),
|
||||
new Rotation(-pi / 3, 1, 0, 0),
|
||||
new Rotation(pi * 3 / 4, 0, 1, 0),
|
||||
new Translation(0.5, 0.5, 0.5)
|
||||
)
|
||||
case EQUIPPED =>
|
||||
new TransformationList(
|
||||
new Scale(1.5),
|
||||
new Rotation(-pi / 5, 1, 0, 0),
|
||||
new Rotation(-pi * 3 / 4, 0, 1, 0),
|
||||
new Translation(0.75, 0.5, 0.75)
|
||||
)
|
||||
case _ => return
|
||||
}
|
||||
|
||||
override def hasContainerItem = true
|
||||
|
||||
override def getContainerItem(stack:ItemStack) =
|
||||
if(isDamageable)
|
||||
new ItemStack(stack.getItem, 1, stack.getItemDamage+1)
|
||||
else
|
||||
stack
|
||||
|
||||
override def doesContainerItemLeaveCraftingGrid(stack:ItemStack) = false
|
||||
|
||||
def getCuttingStrength(item:ItemStack) = harvestLevel
|
||||
}
|
||||
|
||||
object ItemSawRenderer extends IItemRenderer
|
||||
{
|
||||
val models = CCModel.parseObjModels(new ResourceLocation("microblock", "models/saw.obj"), 7, new SwapYZ())
|
||||
val handle = models.get("Handle")
|
||||
val holder = models.get("BladeSupport")
|
||||
val blade = models.get("Blade")
|
||||
|
||||
def handleRenderType(item:ItemStack, renderType:ItemRenderType) =
|
||||
!MicroblockProxy.useSawIcons || TextureUtils.isMissing(item.getIconIndex, TextureMap.locationItemsTexture)
|
||||
|
||||
def shouldUseRenderHelper(renderType:ItemRenderType, item:ItemStack, helper:ItemRendererHelper) = true
|
||||
|
||||
def renderItem(renderType:ItemRenderType, item:ItemStack, data:Object*)
|
||||
{
|
||||
val t = renderType match {
|
||||
case INVENTORY => new TransformationList(new Scale(1.8), new Translation(0, 0, -0.6), new Rotation(-pi/4, 1, 0, 0), new Rotation(pi*3/4, 0, 1, 0))
|
||||
case ENTITY => new TransformationList(new Scale(1), new Translation(0, 0, -0.25), new Rotation(-pi/4, 1, 0, 0))
|
||||
case EQUIPPED_FIRST_PERSON => new TransformationList(new Scale(1.5), new Rotation(-pi/3, 1, 0, 0), new Rotation(pi*3/4, 0, 1, 0), new Translation(0.5, 0.5, 0.5))
|
||||
case EQUIPPED => new TransformationList(new Scale(1.5), new Rotation(-pi/5, 1, 0, 0), new Rotation(-pi*3/4, 0, 1, 0), new Translation(0.75, 0.5, 0.75))
|
||||
case _ => return
|
||||
}
|
||||
|
||||
CCRenderState.reset()
|
||||
CCRenderState.useNormals = true
|
||||
CCRenderState.pullLightmap()
|
||||
CCRenderState.changeTexture("microblock:textures/items/saw.png")
|
||||
CCRenderState.startDrawing()
|
||||
handle.render(t)
|
||||
holder.render(t)
|
||||
CCRenderState.draw()
|
||||
GL11.glDisable(GL11.GL_CULL_FACE)
|
||||
CCRenderState.startDrawing()
|
||||
blade.render(t, new UVTranslation(0, (item.getItem.asInstanceOf[Saw].getCuttingStrength(item)-1)*4/64D))
|
||||
CCRenderState.draw()
|
||||
GL11.glEnable(GL11.GL_CULL_FACE)
|
||||
}
|
||||
CCRenderState.reset()
|
||||
CCRenderState.useNormals = true
|
||||
CCRenderState.pullLightmap()
|
||||
CCRenderState.changeTexture("microblock:textures/items/saw.png")
|
||||
CCRenderState.startDrawing()
|
||||
handle.render(t)
|
||||
holder.render(t)
|
||||
CCRenderState.draw()
|
||||
GL11.glDisable(GL11.GL_CULL_FACE)
|
||||
CCRenderState.startDrawing()
|
||||
blade.render(
|
||||
t,
|
||||
new UVTranslation(
|
||||
0,
|
||||
(item.getItem.asInstanceOf[Saw].getCuttingStrength(item) - 1) * 4 / 64d
|
||||
)
|
||||
)
|
||||
CCRenderState.draw()
|
||||
GL11.glEnable(GL11.GL_CULL_FACE)
|
||||
}
|
||||
}
|
||||
@@ -19,220 +19,227 @@ import scala.collection.JavaConversions._
|
||||
import java.util.{Iterator => JIterator}
|
||||
import net.minecraft.block.Block.SoundType
|
||||
|
||||
object MicroMaterialRegistry
|
||||
{
|
||||
/**
|
||||
* Interface for defining a micro material
|
||||
*/
|
||||
trait IMicroMaterial
|
||||
{
|
||||
/**
|
||||
* The icon to be used for breaking particles on side
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def getBreakingIcon(side: Int): IIcon
|
||||
object MicroMaterialRegistry {
|
||||
|
||||
/**
|
||||
* Callback to load icons from the underlying block/etc
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def loadIcons() {}
|
||||
/** Interface for defining a micro material
|
||||
*/
|
||||
trait IMicroMaterial {
|
||||
|
||||
/**
|
||||
* This function must configure and use the CCRenderState pipeline to draw from the current vertex source.
|
||||
* CCRenderState.lightMatrix will be set.
|
||||
* @param pass The current render pass, -1 for inventory rendering
|
||||
* @param bounds The cuboid bounds of the face being rendered
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def renderMicroFace(pos: Vector3, pass: Int, bounds: Cuboid6)
|
||||
/** The icon to be used for breaking particles on side
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def getBreakingIcon(side: Int): IIcon
|
||||
|
||||
/**
|
||||
* Get the render pass for which this material renders in.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def canRenderInPass(pass: Int) = pass == 0
|
||||
/** Callback to load icons from the underlying block/etc
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def loadIcons() {}
|
||||
|
||||
/**
|
||||
* Return true if this material is not opaque (glass, ice).
|
||||
*/
|
||||
def isTransparent: Boolean
|
||||
/** This function must configure and use the CCRenderState pipeline to draw
|
||||
* from the current vertex source. CCRenderState.lightMatrix will be set.
|
||||
* @param pass
|
||||
* The current render pass, -1 for inventory rendering
|
||||
* @param bounds
|
||||
* The cuboid bounds of the face being rendered
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def renderMicroFace(pos: Vector3, pass: Int, bounds: Cuboid6)
|
||||
|
||||
/**
|
||||
* Return the light level emitted by this material (glowstone)
|
||||
*/
|
||||
def getLightValue: Int
|
||||
/** Get the render pass for which this material renders in.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def canRenderInPass(pass: Int) = pass == 0
|
||||
|
||||
/**
|
||||
* Return the strength of this material
|
||||
*/
|
||||
def getStrength(player: EntityPlayer): Float
|
||||
/** Return true if this material is not opaque (glass, ice).
|
||||
*/
|
||||
def isTransparent: Boolean
|
||||
|
||||
/**
|
||||
* Return the localised name of this material (normally the block name)
|
||||
*/
|
||||
def getLocalizedName: String
|
||||
/** Return the light level emitted by this material (glowstone)
|
||||
*/
|
||||
def getLightValue: Int
|
||||
|
||||
/**
|
||||
* Get the item that this material is cut from (full block -> slabs)
|
||||
*/
|
||||
def getItem: ItemStack
|
||||
/** Return the strength of this material
|
||||
*/
|
||||
def getStrength(player: EntityPlayer): Float
|
||||
|
||||
/**
|
||||
* Get the strength of saw requried to cut this material
|
||||
*/
|
||||
def getCutterStrength: Int
|
||||
/** Return the localised name of this material (normally the block name)
|
||||
*/
|
||||
def getLocalizedName: String
|
||||
|
||||
/**
|
||||
* Get the breaking/walking sound
|
||||
*/
|
||||
def getSound: SoundType
|
||||
/** Get the item that this material is cut from (full block -> slabs)
|
||||
*/
|
||||
def getItem: ItemStack
|
||||
|
||||
/**
|
||||
* Return true if this material is solid and opaque (can run wires on etc)
|
||||
*/
|
||||
def isSolid = !isTransparent
|
||||
/** Get the strength of saw requried to cut this material
|
||||
*/
|
||||
def getCutterStrength: Int
|
||||
|
||||
/**
|
||||
* Get the explosion resistance of this part to an explosion caused by entity
|
||||
*/
|
||||
def explosionResistance(entity: Entity): Float
|
||||
/** Get the breaking/walking sound
|
||||
*/
|
||||
def getSound: SoundType
|
||||
|
||||
/** Return true if this material is solid and opaque (can run wires on etc)
|
||||
*/
|
||||
def isSolid = !isTransparent
|
||||
|
||||
/** Get the explosion resistance of this part to an explosion caused by
|
||||
* entity
|
||||
*/
|
||||
def explosionResistance(entity: Entity): Float
|
||||
}
|
||||
|
||||
/** Interface for overriding the default micro placement highlight handler to
|
||||
* show the effect of placement on a certain block/part
|
||||
*/
|
||||
trait IMicroHighlightRenderer {
|
||||
|
||||
/** Return true if a custom highlight was rendered and the default should be
|
||||
* skipped
|
||||
*/
|
||||
def renderHighlight(
|
||||
player: EntityPlayer,
|
||||
hit: MovingObjectPosition,
|
||||
mcrClass: CommonMicroClass,
|
||||
size: Int,
|
||||
material: Int
|
||||
): Boolean
|
||||
}
|
||||
|
||||
private val typeMap = HashMap[String, IMicroMaterial]()
|
||||
private val nameMap = HashMap[String, Int]()
|
||||
private var idMap: Array[(String, IMicroMaterial)] = _
|
||||
private val idWriter = new IDWriter
|
||||
|
||||
private val highlightRenderers = ListBuffer[IMicroHighlightRenderer]()
|
||||
private var maxCuttingStrength: Int = _
|
||||
|
||||
private val remap = HashMap[String, String]()
|
||||
|
||||
/** Register a micro material with unique identifier name
|
||||
*/
|
||||
def registerMaterial(material: IMicroMaterial, name: String) {
|
||||
if (MultiPartRegistry.loaded)
|
||||
throw new IllegalStateException(
|
||||
"You must register your materials in the init methods."
|
||||
)
|
||||
|
||||
if (typeMap.contains(name)) {
|
||||
logger.error("Material with id " + name + " is already registered.")
|
||||
return
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for overriding the default micro placement highlight handler to show the effect of placement on a certain block/part
|
||||
*/
|
||||
trait IMicroHighlightRenderer
|
||||
{
|
||||
/**
|
||||
* Return true if a custom highlight was rendered and the default should be skipped
|
||||
*/
|
||||
def renderHighlight(player: EntityPlayer, hit: MovingObjectPosition, mcrClass: CommonMicroClass, size: Int, material: Int): Boolean
|
||||
typeMap.put(name, material)
|
||||
}
|
||||
|
||||
/** Replace a micro material with unique identifier name
|
||||
*/
|
||||
def replaceMaterial(material: IMicroMaterial, name: String) {
|
||||
if (MultiPartRegistry.loaded)
|
||||
throw new IllegalStateException(
|
||||
"You must register your materials in the init methods."
|
||||
)
|
||||
|
||||
if (typeMap.remove(name).isEmpty)
|
||||
logger.error("Material with id " + name + " was not registered.")
|
||||
|
||||
logger.debug("Replaced micro material: " + name)
|
||||
|
||||
typeMap.put(name, material)
|
||||
}
|
||||
|
||||
/** Registers a highlight renderer
|
||||
*/
|
||||
def registerHighlightRenderer(handler: IMicroHighlightRenderer) {
|
||||
highlightRenderers += handler
|
||||
}
|
||||
|
||||
def remapName(oldName: String, newName: String): Unit =
|
||||
remap.put(oldName, newName)
|
||||
|
||||
private[microblock] def setupIDMap() {
|
||||
idMap = typeMap.toList.sortBy(_._1).toArray
|
||||
idWriter.setMax(idMap.length)
|
||||
nameMap.clear()
|
||||
for (i <- 0 until idMap.length)
|
||||
nameMap.put(idMap(i)._1, i)
|
||||
}
|
||||
|
||||
private[microblock] def calcMaxCuttingStrength() {
|
||||
val it = Item.itemRegistry.iterator.asInstanceOf[JIterator[Item]]
|
||||
maxCuttingStrength = it.flatMap {
|
||||
case saw: Saw => Some(saw.getMaxCuttingStrength)
|
||||
case _ => None
|
||||
}.max
|
||||
}
|
||||
|
||||
private[microblock] def loadIcons() {
|
||||
if (idMap != null)
|
||||
idMap.foreach(e => e._2.loadIcons())
|
||||
}
|
||||
|
||||
def getMaxCuttingStrength = maxCuttingStrength
|
||||
|
||||
def writeIDMap(packet: PacketCustom) {
|
||||
packet.writeInt(idMap.size)
|
||||
idMap.foreach(e => packet.writeString(e._1))
|
||||
}
|
||||
|
||||
def readIDMap(packet: PacketCustom): Seq[String] = {
|
||||
val k = packet.readInt()
|
||||
idWriter.setMax(k)
|
||||
idMap = new Array(k)
|
||||
nameMap.clear()
|
||||
val missing = ListBuffer[String]()
|
||||
for (i <- 0 until k) {
|
||||
val s = packet.readString()
|
||||
val v = typeMap.get(s)
|
||||
if (v.isEmpty)
|
||||
missing += s
|
||||
else {
|
||||
idMap(i) = (s, v.get)
|
||||
nameMap.put(s, i)
|
||||
}
|
||||
}
|
||||
return missing
|
||||
}
|
||||
|
||||
def writeMaterialID(data: MCDataOutput, id: Int) {
|
||||
idWriter.write(data, id)
|
||||
}
|
||||
|
||||
def readMaterialID(data: MCDataInput) = idWriter.read(data)
|
||||
|
||||
def materialName(id: Int) = idMap(id)._1
|
||||
|
||||
def materialID(name: String) =
|
||||
nameMap.get(remap.getOrElse(name, name)) match {
|
||||
case Some(v) => v
|
||||
case None =>
|
||||
logger.error("Missing mapping for part with ID: " + name)
|
||||
0
|
||||
}
|
||||
|
||||
private val typeMap = HashMap[String, IMicroMaterial]()
|
||||
private val nameMap = HashMap[String, Int]()
|
||||
private var idMap: Array[(String, IMicroMaterial)] = _
|
||||
private val idWriter = new IDWriter
|
||||
def getMaterial(name: String) =
|
||||
typeMap.getOrElse(remap.getOrElse(name, name), null)
|
||||
|
||||
private val highlightRenderers = ListBuffer[IMicroHighlightRenderer]()
|
||||
private var maxCuttingStrength: Int = _
|
||||
def getMaterial(id: Int) = idMap(id)._2
|
||||
|
||||
private val remap = HashMap[String, String]()
|
||||
def getIdMap = idMap
|
||||
|
||||
/**
|
||||
* Register a micro material with unique identifier name
|
||||
*/
|
||||
def registerMaterial(material: IMicroMaterial, name: String) {
|
||||
if (MultiPartRegistry.loaded)
|
||||
throw new IllegalStateException("You must register your materials in the init methods.")
|
||||
def renderHighlight(
|
||||
player: EntityPlayer,
|
||||
hit: MovingObjectPosition,
|
||||
mcrClass: CommonMicroClass,
|
||||
size: Int,
|
||||
material: Int
|
||||
): Boolean = {
|
||||
val overridden = highlightRenderers.find(
|
||||
_.renderHighlight(player, hit, mcrClass, size, material)
|
||||
)
|
||||
if (overridden.isDefined)
|
||||
return true
|
||||
|
||||
if (typeMap.contains(name)) {
|
||||
logger.error("Material with id " + name + " is already registered.")
|
||||
return
|
||||
}
|
||||
|
||||
typeMap.put(name, material)
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace a micro material with unique identifier name
|
||||
*/
|
||||
def replaceMaterial(material: IMicroMaterial, name: String) {
|
||||
if (MultiPartRegistry.loaded)
|
||||
throw new IllegalStateException("You must register your materials in the init methods.")
|
||||
|
||||
if (typeMap.remove(name).isEmpty)
|
||||
logger.error("Material with id " + name + " was not registered.")
|
||||
|
||||
logger.debug("Replaced micro material: " + name)
|
||||
|
||||
typeMap.put(name, material)
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a highlight renderer
|
||||
*/
|
||||
def registerHighlightRenderer(handler: IMicroHighlightRenderer) {
|
||||
highlightRenderers += handler
|
||||
}
|
||||
|
||||
def remapName(oldName:String, newName:String):Unit = remap.put(oldName, newName)
|
||||
|
||||
private[microblock] def setupIDMap() {
|
||||
idMap = typeMap.toList.sortBy(_._1).toArray
|
||||
idWriter.setMax(idMap.length)
|
||||
nameMap.clear()
|
||||
for (i <- 0 until idMap.length)
|
||||
nameMap.put(idMap(i)._1, i)
|
||||
}
|
||||
|
||||
private[microblock] def calcMaxCuttingStrength() {
|
||||
val it = Item.itemRegistry.iterator.asInstanceOf[JIterator[Item]]
|
||||
maxCuttingStrength = it.flatMap {
|
||||
case saw: Saw => Some(saw.getMaxCuttingStrength)
|
||||
case _ => None
|
||||
}.max
|
||||
}
|
||||
|
||||
private[microblock] def loadIcons() {
|
||||
if(idMap != null)
|
||||
idMap.foreach(e => e._2.loadIcons())
|
||||
}
|
||||
|
||||
def getMaxCuttingStrength = maxCuttingStrength
|
||||
|
||||
def writeIDMap(packet: PacketCustom) {
|
||||
packet.writeInt(idMap.size)
|
||||
idMap.foreach(e => packet.writeString(e._1))
|
||||
}
|
||||
|
||||
def readIDMap(packet: PacketCustom): Seq[String] = {
|
||||
val k = packet.readInt()
|
||||
idWriter.setMax(k)
|
||||
idMap = new Array(k)
|
||||
nameMap.clear()
|
||||
val missing = ListBuffer[String]()
|
||||
for (i <- 0 until k) {
|
||||
val s = packet.readString()
|
||||
val v = typeMap.get(s)
|
||||
if (v.isEmpty)
|
||||
missing += s
|
||||
else {
|
||||
idMap(i) = (s, v.get)
|
||||
nameMap.put(s, i)
|
||||
}
|
||||
}
|
||||
return missing
|
||||
}
|
||||
|
||||
def writeMaterialID(data: MCDataOutput, id: Int) {
|
||||
idWriter.write(data, id)
|
||||
}
|
||||
|
||||
def readMaterialID(data: MCDataInput) = idWriter.read(data)
|
||||
|
||||
def materialName(id: Int) = idMap(id)._1
|
||||
|
||||
def materialID(name: String) = nameMap.get(remap.getOrElse(name, name)) match {
|
||||
case Some(v) => v
|
||||
case None =>
|
||||
logger.error("Missing mapping for part with ID: " + name)
|
||||
0
|
||||
}
|
||||
|
||||
def getMaterial(name: String) = typeMap.getOrElse(remap.getOrElse(name, name), null)
|
||||
|
||||
def getMaterial(id: Int) = idMap(id)._2
|
||||
|
||||
def getIdMap = idMap
|
||||
|
||||
def renderHighlight(player: EntityPlayer, hit: MovingObjectPosition, mcrClass: CommonMicroClass, size: Int, material: Int): Boolean = {
|
||||
val overridden = highlightRenderers.find(_.renderHighlight(player, hit, mcrClass, size, material))
|
||||
if (overridden.isDefined)
|
||||
return true
|
||||
|
||||
MicroblockRender.renderHighlight(player, hit, mcrClass, size, material)
|
||||
return true
|
||||
}
|
||||
}
|
||||
MicroblockRender.renderHighlight(player, hit, mcrClass, size, material)
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -7,221 +7,241 @@ import net.minecraft.item.ItemStack
|
||||
import codechicken.microblock.handler.MicroblockProxy._
|
||||
import net.minecraftforge.oredict.RecipeSorter
|
||||
|
||||
object MicroRecipe extends IRecipe
|
||||
{
|
||||
RecipeSorter.register("fmp:micro", getClass, RecipeSorter.Category.SHAPED, "after:forge:shapelessore")
|
||||
object MicroRecipe extends IRecipe {
|
||||
RecipeSorter.register(
|
||||
"fmp:micro",
|
||||
getClass,
|
||||
RecipeSorter.Category.SHAPED,
|
||||
"after:forge:shapelessore"
|
||||
)
|
||||
|
||||
def getRecipeOutput = ItemMicroPart.create(1, 1, "tile.stone")
|
||||
|
||||
def getRecipeSize = 9
|
||||
|
||||
def matches(icraft:InventoryCrafting, world:World) = getCraftingResult(icraft) != null
|
||||
|
||||
def getCraftingResult(icraft:InventoryCrafting):ItemStack =
|
||||
{
|
||||
var res = getHollowResult(icraft)
|
||||
if(res != null) return res
|
||||
res = getGluingResult(icraft)
|
||||
if(res != null) return res
|
||||
res = getThinningResult(icraft)
|
||||
if(res != null) return res
|
||||
res = getSplittingResult(icraft)
|
||||
if(res != null) return res
|
||||
res = getHollowFillResult(icraft)
|
||||
return res
|
||||
def getRecipeOutput = ItemMicroPart.create(1, 1, "tile.stone")
|
||||
|
||||
def getRecipeSize = 9
|
||||
|
||||
def matches(icraft: InventoryCrafting, world: World) =
|
||||
getCraftingResult(icraft) != null
|
||||
|
||||
def getCraftingResult(icraft: InventoryCrafting): ItemStack = {
|
||||
var res = getHollowResult(icraft)
|
||||
if (res != null) return res
|
||||
res = getGluingResult(icraft)
|
||||
if (res != null) return res
|
||||
res = getThinningResult(icraft)
|
||||
if (res != null) return res
|
||||
res = getSplittingResult(icraft)
|
||||
if (res != null) return res
|
||||
res = getHollowFillResult(icraft)
|
||||
return res
|
||||
}
|
||||
|
||||
def create(
|
||||
amount: Int,
|
||||
mcrClass: Int,
|
||||
size: Int,
|
||||
material: Int
|
||||
): ItemStack = {
|
||||
if (size == 8) {
|
||||
val item = MicroMaterialRegistry.getMaterial(material).getItem.copy
|
||||
item.stackSize = amount
|
||||
return item
|
||||
}
|
||||
|
||||
def create(amount:Int, mcrClass:Int, size:Int, material:Int):ItemStack =
|
||||
{
|
||||
if(size == 8)
|
||||
{
|
||||
val item = MicroMaterialRegistry.getMaterial(material).getItem.copy
|
||||
item.stackSize = amount
|
||||
return item
|
||||
return ItemMicroPart.create(
|
||||
amount,
|
||||
mcrClass << 8 | size,
|
||||
MicroMaterialRegistry.materialName(material)
|
||||
)
|
||||
}
|
||||
|
||||
def microMaterial(item: ItemStack) =
|
||||
if (item.getItem == itemMicro)
|
||||
ItemMicroPart.getMaterialID(item)
|
||||
else
|
||||
findMaterial(item)
|
||||
|
||||
def microClass(item: ItemStack) =
|
||||
if (item.getItem == itemMicro)
|
||||
item.getItemDamage >> 8
|
||||
else
|
||||
0
|
||||
|
||||
def microSize(item: ItemStack) =
|
||||
if (item.getItem == itemMicro)
|
||||
item.getItemDamage & 0xff
|
||||
else
|
||||
8
|
||||
|
||||
def getHollowResult(icraft: InventoryCrafting): ItemStack = {
|
||||
if (icraft.getStackInRowAndColumn(1, 1) != null) return null
|
||||
|
||||
val first = icraft.getStackInRowAndColumn(0, 0)
|
||||
if (first == null || first.getItem != itemMicro || microClass(first) != 0)
|
||||
return null
|
||||
val size = microSize(first)
|
||||
val material = microMaterial(first)
|
||||
|
||||
for (i <- 1 to 8 if i != 4) {
|
||||
val item = icraft.getStackInSlot(i)
|
||||
if (
|
||||
item == null || item.getItem != itemMicro ||
|
||||
microMaterial(
|
||||
item
|
||||
) != material || item.getItemDamage != first.getItemDamage
|
||||
)
|
||||
return null
|
||||
}
|
||||
return create(8, 1, size, material)
|
||||
}
|
||||
|
||||
def getGluingResult(icraft: InventoryCrafting): ItemStack = {
|
||||
var size = 0
|
||||
var count = 0
|
||||
var smallest = 0
|
||||
var mcrClass = 0
|
||||
var material = 0
|
||||
for (i <- 0 until 9) {
|
||||
val item = icraft.getStackInSlot(i)
|
||||
if (item != null) {
|
||||
if (item.getItem != itemMicro) return null
|
||||
if (count == 0) {
|
||||
size = microSize(item)
|
||||
mcrClass = microClass(item)
|
||||
material = microMaterial(item)
|
||||
count = 1
|
||||
smallest = size
|
||||
} else if (
|
||||
microClass(item) != mcrClass || microMaterial(item) != material
|
||||
) return null
|
||||
else if (mcrClass >= 2 && microSize(item) != smallest) return null
|
||||
else {
|
||||
smallest = Math.min(smallest, microSize(item))
|
||||
count += 1
|
||||
size += microSize(item)
|
||||
}
|
||||
return ItemMicroPart.create(amount, mcrClass<<8|size, MicroMaterialRegistry.materialName(material))
|
||||
}
|
||||
}
|
||||
|
||||
def microMaterial(item:ItemStack) =
|
||||
if(item.getItem == itemMicro)
|
||||
ItemMicroPart.getMaterialID(item)
|
||||
|
||||
if (count <= 1) return null
|
||||
|
||||
mcrClass match {
|
||||
case 3 =>
|
||||
count match {
|
||||
case 2 => create(1, 0, smallest, material)
|
||||
case _ => null
|
||||
}
|
||||
case 2 =>
|
||||
count match {
|
||||
case 2 => create(1, 3, smallest, material)
|
||||
case 4 => create(1, 0, smallest, material)
|
||||
case _ => null
|
||||
}
|
||||
case 1 | 0 =>
|
||||
val base = Seq(1, 2, 4).find(s => (s & size) != 0)
|
||||
if (base.isEmpty)
|
||||
create(size / 8, 0, 8, material)
|
||||
else if (base.get <= smallest)
|
||||
null
|
||||
else
|
||||
findMaterial(item)
|
||||
|
||||
def microClass(item:ItemStack) =
|
||||
if(item.getItem == itemMicro)
|
||||
item.getItemDamage >> 8
|
||||
else
|
||||
0
|
||||
|
||||
def microSize(item:ItemStack) =
|
||||
if(item.getItem == itemMicro)
|
||||
item.getItemDamage & 0xFF
|
||||
else
|
||||
8
|
||||
|
||||
def getHollowResult(icraft:InventoryCrafting):ItemStack =
|
||||
{
|
||||
if(icraft.getStackInRowAndColumn(1, 1) != null) return null
|
||||
|
||||
val first = icraft.getStackInRowAndColumn(0, 0)
|
||||
if(first == null || first.getItem != itemMicro || microClass(first) != 0) return null
|
||||
val size = microSize(first)
|
||||
val material = microMaterial(first)
|
||||
|
||||
for(i <- 1 to 8 if i != 4)
|
||||
{
|
||||
val item = icraft.getStackInSlot(i)
|
||||
if(item == null || item.getItem != itemMicro ||
|
||||
microMaterial(item) != material || item.getItemDamage != first.getItemDamage)
|
||||
return null
|
||||
}
|
||||
return create(8, 1, size, material)
|
||||
create(size / base.get, mcrClass, base.get, material)
|
||||
case _ => null
|
||||
}
|
||||
|
||||
def getGluingResult(icraft:InventoryCrafting):ItemStack =
|
||||
{
|
||||
var size = 0
|
||||
var count = 0
|
||||
var smallest = 0
|
||||
var mcrClass = 0
|
||||
var material = 0
|
||||
for(i <- 0 until 9)
|
||||
{
|
||||
val item = icraft.getStackInSlot(i)
|
||||
if(item != null)
|
||||
{
|
||||
if(item.getItem != itemMicro) return null
|
||||
if(count == 0)
|
||||
{
|
||||
size = microSize(item)
|
||||
mcrClass = microClass(item)
|
||||
material = microMaterial(item)
|
||||
count = 1
|
||||
smallest = size
|
||||
}
|
||||
else if(microClass(item) != mcrClass || microMaterial(item) != material) return null
|
||||
else if(mcrClass >= 2 && microSize(item) != smallest) return null
|
||||
else
|
||||
{
|
||||
smallest= Math.min(smallest, microSize(item))
|
||||
count+=1
|
||||
size+=microSize(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(count <= 1) return null
|
||||
|
||||
mcrClass match {
|
||||
case 3 => count match {
|
||||
case 2 => create(1, 0, smallest, material)
|
||||
case _ => null
|
||||
}
|
||||
case 2 => count match {
|
||||
case 2 => create(1, 3, smallest, material)
|
||||
case 4 => create(1, 0, smallest, material)
|
||||
case _ => null
|
||||
}
|
||||
case 1|0 =>
|
||||
val base = Seq(1, 2, 4).find(s => (s&size) != 0)
|
||||
if(base.isEmpty)
|
||||
create(size/8, 0, 8, material)
|
||||
else if(base.get <= smallest)
|
||||
null
|
||||
else
|
||||
create(size/base.get, mcrClass, base.get, material)
|
||||
case _ => null
|
||||
}
|
||||
}
|
||||
|
||||
def getSaw(icraft: InventoryCrafting): (Saw, Int, Int) = {
|
||||
for (r <- 0 until 3)
|
||||
for (c <- 0 until 3) {
|
||||
val item = icraft.getStackInRowAndColumn(c, r)
|
||||
if (item != null && item.getItem.isInstanceOf[Saw])
|
||||
return (item.getItem.asInstanceOf[Saw], r, c)
|
||||
}
|
||||
return (null, 0, 0)
|
||||
}
|
||||
|
||||
def canCut(saw: Saw, sawItem: ItemStack, material: Int): Boolean = {
|
||||
val sawStrength = saw.getCuttingStrength(sawItem)
|
||||
val matStrength =
|
||||
MicroMaterialRegistry.getMaterial(material).getCutterStrength
|
||||
return sawStrength >= matStrength || sawStrength == MicroMaterialRegistry.getMaxCuttingStrength
|
||||
}
|
||||
|
||||
def getThinningResult(icraft: InventoryCrafting): ItemStack = {
|
||||
val (saw, row, col) = getSaw(icraft)
|
||||
if (saw == null)
|
||||
return null
|
||||
|
||||
val item = icraft.getStackInRowAndColumn(col, row + 1)
|
||||
if (item == null)
|
||||
return null
|
||||
|
||||
val size = microSize(item)
|
||||
val material = microMaterial(item)
|
||||
val mcrClass = microClass(item)
|
||||
if (
|
||||
size == 1 || material < 0 || !canCut(
|
||||
saw,
|
||||
icraft.getStackInRowAndColumn(col, row),
|
||||
material
|
||||
)
|
||||
)
|
||||
return null
|
||||
|
||||
for (r <- 0 until 3)
|
||||
for (c <- 0 until 3)
|
||||
if (
|
||||
(c != col || r != row && r != row + 1) &&
|
||||
icraft.getStackInRowAndColumn(c, r) != null
|
||||
)
|
||||
return null
|
||||
|
||||
return create(2, mcrClass, size / 2, material)
|
||||
}
|
||||
|
||||
def findMaterial(item: ItemStack): Int =
|
||||
MicroMaterialRegistry.getIdMap.find { m =>
|
||||
val mitem = m._2.getItem
|
||||
item.getItem == mitem.getItem &&
|
||||
item.getItemDamage == mitem.getItemDamage &&
|
||||
ItemStack.areItemStackTagsEqual(item, mitem)
|
||||
} match {
|
||||
case None => -1
|
||||
case Some((name, m)) => MicroMaterialRegistry.materialID(name)
|
||||
}
|
||||
|
||||
def getSaw(icraft:InventoryCrafting):(Saw, Int, Int) =
|
||||
{
|
||||
for(r <- 0 until 3)
|
||||
for(c <- 0 until 3)
|
||||
{
|
||||
val item = icraft.getStackInRowAndColumn(c, r)
|
||||
if(item != null && item.getItem.isInstanceOf[Saw])
|
||||
return (item.getItem.asInstanceOf[Saw], r, c)
|
||||
}
|
||||
return (null, 0, 0)
|
||||
|
||||
val splitMap = Map(0 -> 3, 1 -> 3, 3 -> 2)
|
||||
def getSplittingResult(icraft: InventoryCrafting): ItemStack = {
|
||||
val (saw, row, col) = getSaw(icraft)
|
||||
if (saw == null) return null
|
||||
val item = icraft.getStackInRowAndColumn(col + 1, row)
|
||||
if (item == null || item.getItem != itemMicro) return null
|
||||
val mcrClass = microClass(item)
|
||||
val material = microMaterial(item)
|
||||
if (!canCut(saw, icraft.getStackInRowAndColumn(col, row), material))
|
||||
return null
|
||||
val split = splitMap.get(mcrClass)
|
||||
if (split.isEmpty) return null
|
||||
|
||||
for (r <- 0 until 3)
|
||||
for (c <- 0 until 3)
|
||||
if (
|
||||
(r != row || c != col && c != col + 1) &&
|
||||
icraft.getStackInRowAndColumn(c, r) != null
|
||||
)
|
||||
return null
|
||||
|
||||
return create(2, split.get, microSize(item), material)
|
||||
}
|
||||
|
||||
def getHollowFillResult(icraft: InventoryCrafting): ItemStack = {
|
||||
var cover: ItemStack = null
|
||||
for (i <- 0 until 9) {
|
||||
val item = icraft.getStackInSlot(i)
|
||||
if (item != null) {
|
||||
if (item.getItem != itemMicro || cover != null || microClass(item) != 1)
|
||||
return null
|
||||
else cover = item
|
||||
}
|
||||
}
|
||||
|
||||
def canCut(saw:Saw, sawItem:ItemStack, material:Int):Boolean = {
|
||||
val sawStrength = saw.getCuttingStrength(sawItem)
|
||||
val matStrength = MicroMaterialRegistry.getMaterial(material).getCutterStrength
|
||||
return sawStrength >= matStrength || sawStrength == MicroMaterialRegistry.getMaxCuttingStrength
|
||||
}
|
||||
|
||||
def getThinningResult(icraft:InventoryCrafting):ItemStack =
|
||||
{
|
||||
val (saw, row, col) = getSaw(icraft)
|
||||
if(saw == null)
|
||||
return null
|
||||
|
||||
val item = icraft.getStackInRowAndColumn(col, row+1)
|
||||
if(item == null)
|
||||
return null
|
||||
|
||||
val size = microSize(item)
|
||||
val material = microMaterial(item)
|
||||
val mcrClass = microClass(item)
|
||||
if(size == 1 || material < 0 || !canCut(saw, icraft.getStackInRowAndColumn(col, row), material))
|
||||
return null
|
||||
|
||||
for(r <- 0 until 3)
|
||||
for(c <- 0 until 3)
|
||||
if((c != col || r != row && r != row+1) &&
|
||||
icraft.getStackInRowAndColumn(c, r) != null)
|
||||
return null
|
||||
|
||||
return create(2, mcrClass, size/2, material)
|
||||
}
|
||||
|
||||
def findMaterial(item:ItemStack):Int =
|
||||
MicroMaterialRegistry.getIdMap.find{m => val mitem = m._2.getItem
|
||||
item.getItem == mitem.getItem &&
|
||||
item.getItemDamage == mitem.getItemDamage &&
|
||||
ItemStack.areItemStackTagsEqual(item, mitem)} match {
|
||||
case None => -1
|
||||
case Some((name, m)) => MicroMaterialRegistry.materialID(name)
|
||||
}
|
||||
|
||||
val splitMap = Map(0 -> 3, 1 -> 3, 3 -> 2)
|
||||
def getSplittingResult(icraft:InventoryCrafting):ItemStack =
|
||||
{
|
||||
val (saw, row, col) = getSaw(icraft)
|
||||
if(saw == null) return null
|
||||
val item = icraft.getStackInRowAndColumn(col+1, row)
|
||||
if(item == null || item.getItem != itemMicro) return null
|
||||
val mcrClass = microClass(item)
|
||||
val material = microMaterial(item)
|
||||
if(!canCut(saw, icraft.getStackInRowAndColumn(col, row), material)) return null
|
||||
val split = splitMap.get(mcrClass)
|
||||
if(split.isEmpty)return null
|
||||
|
||||
for(r <- 0 until 3)
|
||||
for(c <- 0 until 3)
|
||||
if((r != row || c != col && c != col+1) &&
|
||||
icraft.getStackInRowAndColumn(c, r) != null)
|
||||
return null
|
||||
|
||||
return create(2, split.get, microSize(item), material)
|
||||
}
|
||||
|
||||
def getHollowFillResult(icraft:InventoryCrafting):ItemStack =
|
||||
{
|
||||
var cover:ItemStack = null
|
||||
for(i <- 0 until 9)
|
||||
{
|
||||
val item = icraft.getStackInSlot(i)
|
||||
if(item != null)
|
||||
{
|
||||
if(item.getItem != itemMicro || cover != null || microClass(item) != 1) return null
|
||||
else cover = item
|
||||
}
|
||||
}
|
||||
if(cover == null) return null
|
||||
return create(1, 0, microSize(cover), microMaterial(cover))
|
||||
}
|
||||
}
|
||||
if (cover == null) return null
|
||||
return create(1, 0, microSize(cover), microMaterial(cover))
|
||||
}
|
||||
}
|
||||
@@ -31,128 +31,147 @@ import net.minecraft.init.Blocks
|
||||
import cpw.mods.fml.relauncher.{Side, SideOnly}
|
||||
import codechicken.lib.render.BlockRenderer.BlockFace
|
||||
|
||||
abstract class Microblock(var material:Int = 0) extends TCuboidPart
|
||||
{
|
||||
var shape:Byte = 0
|
||||
abstract class Microblock(var material: Int = 0) extends TCuboidPart {
|
||||
var shape: Byte = 0
|
||||
|
||||
def microClass:MicroblockClass
|
||||
def microClass: MicroblockClass
|
||||
|
||||
def getType = microClass.getName
|
||||
def getType = microClass.getName
|
||||
|
||||
override def getStrength(hit:MovingObjectPosition, player:EntityPlayer) = getIMaterial match {
|
||||
case null => super.getStrength(hit, player)
|
||||
case mat => mat.getStrength(player)
|
||||
override def getStrength(hit: MovingObjectPosition, player: EntityPlayer) =
|
||||
getIMaterial match {
|
||||
case null => super.getStrength(hit, player)
|
||||
case mat => mat.getStrength(player)
|
||||
}
|
||||
|
||||
override def doesTick = false
|
||||
|
||||
def getSize = shape>>4
|
||||
|
||||
def getShape = shape&0xF
|
||||
override def doesTick = false
|
||||
|
||||
def setShape(size:Int, slot:Int) = shape = (size<<4|slot).toByte
|
||||
def getSize = shape >> 4
|
||||
|
||||
def getMaterial = material
|
||||
|
||||
def getIMaterial = MicroMaterialRegistry.getMaterial(material)
|
||||
|
||||
def itemClassID:Int
|
||||
|
||||
override def getDrops = {
|
||||
var size = getSize
|
||||
val items = ListBuffer[ItemStack]()
|
||||
for(s <- Seq(4, 2, 1))
|
||||
{
|
||||
val m = size/s
|
||||
size-=m*s
|
||||
if(m > 0)
|
||||
items+=ItemMicroPart.create(m, s|itemClassID<<8, MicroMaterialRegistry.materialName(material))
|
||||
}
|
||||
items
|
||||
}
|
||||
|
||||
override def pickItem(hit:MovingObjectPosition):ItemStack = {
|
||||
val size = getSize
|
||||
for(s <- Seq(4, 2, 1))
|
||||
if(size%s == 0 && size/s >= 1)
|
||||
return ItemMicroPart.create(s|itemClassID<<8, MicroMaterialRegistry.materialName(material))
|
||||
def getShape = shape & 0xf
|
||||
|
||||
return null//unreachable
|
||||
}
|
||||
|
||||
override def writeDesc(packet:MCDataOutput) {
|
||||
writeMaterialID(packet, material)
|
||||
packet.writeByte(shape)
|
||||
}
|
||||
|
||||
override def readDesc(packet:MCDataInput) {
|
||||
shape = packet.readByte
|
||||
}
|
||||
def setShape(size: Int, slot: Int) = shape = (size << 4 | slot).toByte
|
||||
|
||||
def sendShapeUpdate() {
|
||||
getWriteStream.writeByte(shape)
|
||||
}
|
||||
|
||||
override def read(packet:MCDataInput) {
|
||||
super.read(packet)
|
||||
tile.notifyPartChange(this)
|
||||
}
|
||||
|
||||
override def save(tag:NBTTagCompound) {
|
||||
tag.setByte("shape", shape)
|
||||
tag.setString("material", materialName(material))
|
||||
}
|
||||
|
||||
override def load(tag:NBTTagCompound) {
|
||||
shape = tag.getByte("shape")
|
||||
material = materialID(tag.getString("material"))
|
||||
}
|
||||
|
||||
def isTransparent = getIMaterial.isTransparent
|
||||
|
||||
override def getLightValue = getIMaterial.getLightValue
|
||||
def getMaterial = material
|
||||
|
||||
override def explosionResistance(entity:Entity) = getIMaterial.explosionResistance(entity) * microClass.getResistanceFactor
|
||||
def getIMaterial = MicroMaterialRegistry.getMaterial(material)
|
||||
|
||||
def itemClassID: Int
|
||||
|
||||
override def getDrops = {
|
||||
var size = getSize
|
||||
val items = ListBuffer[ItemStack]()
|
||||
for (s <- Seq(4, 2, 1)) {
|
||||
val m = size / s
|
||||
size -= m * s
|
||||
if (m > 0)
|
||||
items += ItemMicroPart.create(
|
||||
m,
|
||||
s | itemClassID << 8,
|
||||
MicroMaterialRegistry.materialName(material)
|
||||
)
|
||||
}
|
||||
items
|
||||
}
|
||||
|
||||
override def pickItem(hit: MovingObjectPosition): ItemStack = {
|
||||
val size = getSize
|
||||
for (s <- Seq(4, 2, 1))
|
||||
if (size % s == 0 && size / s >= 1)
|
||||
return ItemMicroPart.create(
|
||||
s | itemClassID << 8,
|
||||
MicroMaterialRegistry.materialName(material)
|
||||
)
|
||||
|
||||
return null // unreachable
|
||||
}
|
||||
|
||||
override def writeDesc(packet: MCDataOutput) {
|
||||
writeMaterialID(packet, material)
|
||||
packet.writeByte(shape)
|
||||
}
|
||||
|
||||
override def readDesc(packet: MCDataInput) {
|
||||
shape = packet.readByte
|
||||
}
|
||||
|
||||
def sendShapeUpdate() {
|
||||
getWriteStream.writeByte(shape)
|
||||
}
|
||||
|
||||
override def read(packet: MCDataInput) {
|
||||
super.read(packet)
|
||||
tile.notifyPartChange(this)
|
||||
}
|
||||
|
||||
override def save(tag: NBTTagCompound) {
|
||||
tag.setByte("shape", shape)
|
||||
tag.setString("material", materialName(material))
|
||||
}
|
||||
|
||||
override def load(tag: NBTTagCompound) {
|
||||
shape = tag.getByte("shape")
|
||||
material = materialID(tag.getString("material"))
|
||||
}
|
||||
|
||||
def isTransparent = getIMaterial.isTransparent
|
||||
|
||||
override def getLightValue = getIMaterial.getLightValue
|
||||
|
||||
override def explosionResistance(entity: Entity) =
|
||||
getIMaterial.explosionResistance(entity) * microClass.getResistanceFactor
|
||||
}
|
||||
|
||||
trait MicroblockClient extends Microblock with TIconHitEffects with IMicroMaterialRender
|
||||
{
|
||||
def getBrokenIcon(side:Int) = getIMaterial match {
|
||||
case null => Blocks.stone.getIcon(0, 0)
|
||||
case mat => mat.getBreakingIcon(side)
|
||||
}
|
||||
trait MicroblockClient
|
||||
extends Microblock
|
||||
with TIconHitEffects
|
||||
with IMicroMaterialRender {
|
||||
def getBrokenIcon(side: Int) = getIMaterial match {
|
||||
case null => Blocks.stone.getIcon(0, 0)
|
||||
case mat => mat.getBreakingIcon(side)
|
||||
}
|
||||
|
||||
override def renderStatic(pos:Vector3, pass:Int) = {
|
||||
if(getIMaterial.canRenderInPass(pass)) {
|
||||
render(pos, pass)
|
||||
true
|
||||
}
|
||||
else
|
||||
false
|
||||
}
|
||||
override def renderStatic(pos: Vector3, pass: Int) = {
|
||||
if (getIMaterial.canRenderInPass(pass)) {
|
||||
render(pos, pass)
|
||||
true
|
||||
} else
|
||||
false
|
||||
}
|
||||
|
||||
def render(pos:Vector3, pass:Int)
|
||||
def render(pos: Vector3, pass: Int)
|
||||
|
||||
override def getRenderBounds = getBounds
|
||||
override def getRenderBounds = getBounds
|
||||
}
|
||||
|
||||
trait CommonMicroblockClient extends CommonMicroblock with MicroblockClient with TMicroOcclusionClient
|
||||
{
|
||||
def render(pos:Vector3, pass:Int) {
|
||||
if(pass < 0)
|
||||
MicroblockRender.renderCuboid(pos, getIMaterial, pass, getBounds, 0)
|
||||
else
|
||||
MicroblockRender.renderCuboid(pos, getIMaterial, pass, renderBounds, renderMask)
|
||||
}
|
||||
trait CommonMicroblockClient
|
||||
extends CommonMicroblock
|
||||
with MicroblockClient
|
||||
with TMicroOcclusionClient {
|
||||
def render(pos: Vector3, pass: Int) {
|
||||
if (pass < 0)
|
||||
MicroblockRender.renderCuboid(pos, getIMaterial, pass, getBounds, 0)
|
||||
else
|
||||
MicroblockRender.renderCuboid(
|
||||
pos,
|
||||
getIMaterial,
|
||||
pass,
|
||||
renderBounds,
|
||||
renderMask
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
trait CommonMicroblock extends Microblock with JPartialOcclusion with TMicroOcclusion with TSlottedPart
|
||||
{
|
||||
def microClass:CommonMicroClass
|
||||
trait CommonMicroblock
|
||||
extends Microblock
|
||||
with JPartialOcclusion
|
||||
with TMicroOcclusion
|
||||
with TSlottedPart {
|
||||
def microClass: CommonMicroClass
|
||||
|
||||
def getSlot = getShape
|
||||
def getSlotMask = 1<<getSlot
|
||||
def getPartialOcclusionBoxes = Seq(getBounds)
|
||||
|
||||
override def itemClassID = microClass.getClassId
|
||||
}
|
||||
def getSlot = getShape
|
||||
def getSlotMask = 1 << getSlot
|
||||
def getPartialOcclusionBoxes = Seq(getBounds)
|
||||
|
||||
override def itemClassID = microClass.getClassId
|
||||
}
|
||||
@@ -6,55 +6,58 @@ import net.minecraft.nbt.NBTTagCompound
|
||||
import codechicken.lib.data.MCDataInput
|
||||
import cpw.mods.fml.relauncher.{Side, SideOnly}
|
||||
|
||||
abstract class MicroblockClass extends IPartFactory2
|
||||
{
|
||||
def getName:String
|
||||
def baseTrait:Class[_ <: Microblock]
|
||||
@SideOnly(Side.CLIENT)
|
||||
def clientTrait:Class[_ <: MicroblockClient]
|
||||
def getResistanceFactor:Float
|
||||
abstract class MicroblockClass extends IPartFactory2 {
|
||||
def getName: String
|
||||
def baseTrait: Class[_ <: Microblock]
|
||||
@SideOnly(Side.CLIENT)
|
||||
def clientTrait: Class[_ <: MicroblockClient]
|
||||
def getResistanceFactor: Float
|
||||
|
||||
val baseTraitId = MicroblockGenerator.registerTrait(baseTrait)
|
||||
@SideOnly(Side.CLIENT)
|
||||
lazy val clientTraitId = MicroblockGenerator.registerTrait(clientTrait)
|
||||
val baseTraitId = MicroblockGenerator.registerTrait(baseTrait)
|
||||
@SideOnly(Side.CLIENT)
|
||||
lazy val clientTraitId = MicroblockGenerator.registerTrait(clientTrait)
|
||||
|
||||
def register() {
|
||||
MultiPartRegistry.registerParts(this, getName)
|
||||
}
|
||||
def register() {
|
||||
MultiPartRegistry.registerParts(this, getName)
|
||||
}
|
||||
|
||||
def create(client:Boolean, material:Int) = MicroblockGenerator.create(this, material, client)
|
||||
override def createPart(name: String, packet: MCDataInput) = create(true, MicroMaterialRegistry.readMaterialID(packet))
|
||||
override def createPart(name: String, nbt: NBTTagCompound) = create(false, MicroMaterialRegistry.materialID(nbt.getString("material")))
|
||||
def create(client: Boolean, material: Int) =
|
||||
MicroblockGenerator.create(this, material, client)
|
||||
override def createPart(name: String, packet: MCDataInput) =
|
||||
create(true, MicroMaterialRegistry.readMaterialID(packet))
|
||||
override def createPart(name: String, nbt: NBTTagCompound) =
|
||||
create(false, MicroMaterialRegistry.materialID(nbt.getString("material")))
|
||||
}
|
||||
|
||||
/**
|
||||
* Microblocks with corresponding items
|
||||
*/
|
||||
abstract class CommonMicroClass extends MicroblockClass
|
||||
{
|
||||
private var classId:Int = _
|
||||
/** Microblocks with corresponding items
|
||||
*/
|
||||
abstract class CommonMicroClass extends MicroblockClass {
|
||||
private var classId: Int = _
|
||||
|
||||
def getClassId = classId
|
||||
def itemSlot:Int
|
||||
def placementProperties:PlacementProperties
|
||||
def getClassId = classId
|
||||
def itemSlot: Int
|
||||
def placementProperties: PlacementProperties
|
||||
|
||||
def register(id:Int) {
|
||||
register()
|
||||
classId = id
|
||||
CommonMicroClass.registerMicroClass(this, id)
|
||||
}
|
||||
def register(id: Int) {
|
||||
register()
|
||||
classId = id
|
||||
CommonMicroClass.registerMicroClass(this, id)
|
||||
}
|
||||
}
|
||||
|
||||
object CommonMicroClass
|
||||
{
|
||||
val classes = new Array[CommonMicroClass](256)
|
||||
object CommonMicroClass {
|
||||
val classes = new Array[CommonMicroClass](256)
|
||||
|
||||
def getMicroClass(modelId:Int) = classes(modelId>>8)
|
||||
def getMicroClass(modelId: Int) = classes(modelId >> 8)
|
||||
|
||||
def registerMicroClass(mcrClass:CommonMicroClass, id:Int) {
|
||||
if(classes(id) != null)
|
||||
throw new IllegalArgumentException("Microblock class id "+id+" is already taken by "+classes(id).getName+" when adding "+mcrClass.getName)
|
||||
def registerMicroClass(mcrClass: CommonMicroClass, id: Int) {
|
||||
if (classes(id) != null)
|
||||
throw new IllegalArgumentException(
|
||||
"Microblock class id " + id + " is already taken by " + classes(
|
||||
id
|
||||
).getName + " when adding " + mcrClass.getName
|
||||
)
|
||||
|
||||
classes(id) = mcrClass
|
||||
}
|
||||
}
|
||||
classes(id) = mcrClass
|
||||
}
|
||||
}
|
||||
@@ -3,23 +3,24 @@ package codechicken.microblock
|
||||
import codechicken.multipart.asm.{ScratchBitSet, ASMMixinFactory}
|
||||
import java.util.BitSet
|
||||
|
||||
object MicroblockGenerator extends ASMMixinFactory(classOf[Microblock], classOf[Int]) with ScratchBitSet
|
||||
{
|
||||
trait IGeneratedMaterial
|
||||
{
|
||||
def addTraits(traits:BitSet, mcrClass:MicroblockClass, client:Boolean)
|
||||
object MicroblockGenerator
|
||||
extends ASMMixinFactory(classOf[Microblock], classOf[Int])
|
||||
with ScratchBitSet {
|
||||
trait IGeneratedMaterial {
|
||||
def addTraits(traits: BitSet, mcrClass: MicroblockClass, client: Boolean)
|
||||
}
|
||||
|
||||
def create(mcrClass: MicroblockClass, material: Int, client: Boolean) = {
|
||||
val bitset = freshBitSet
|
||||
bitset.set(mcrClass.baseTraitId)
|
||||
if (client) bitset.set(mcrClass.clientTraitId)
|
||||
|
||||
MicroMaterialRegistry.getMaterial(material) match {
|
||||
case genMat: IGeneratedMaterial =>
|
||||
genMat.addTraits(bitset, mcrClass, client)
|
||||
case _ =>
|
||||
}
|
||||
|
||||
def create(mcrClass: MicroblockClass, material: Int, client: Boolean) = {
|
||||
val bitset = freshBitSet
|
||||
bitset.set(mcrClass.baseTraitId)
|
||||
if(client) bitset.set(mcrClass.clientTraitId)
|
||||
|
||||
MicroMaterialRegistry.getMaterial(material) match {
|
||||
case genMat:IGeneratedMaterial => genMat.addTraits(bitset, mcrClass, client)
|
||||
case _ =>
|
||||
}
|
||||
|
||||
construct(bitset, material:Integer)
|
||||
}
|
||||
construct(bitset, material: Integer)
|
||||
}
|
||||
}
|
||||
@@ -11,157 +11,170 @@ import net.minecraft.item.ItemStack
|
||||
import codechicken.multipart.TileMultipart
|
||||
import codechicken.lib.raytracer.ExtendedMOP
|
||||
|
||||
abstract class ExecutablePlacement(val pos:BlockCoord, val part:Microblock)
|
||||
{
|
||||
def place(world:World, player:EntityPlayer, item:ItemStack)
|
||||
def consume(world:World, player:EntityPlayer, item:ItemStack)
|
||||
abstract class ExecutablePlacement(val pos: BlockCoord, val part: Microblock) {
|
||||
def place(world: World, player: EntityPlayer, item: ItemStack)
|
||||
def consume(world: World, player: EntityPlayer, item: ItemStack)
|
||||
}
|
||||
|
||||
class AdditionPlacement($pos:BlockCoord, $part:Microblock) extends ExecutablePlacement($pos, $part)
|
||||
{
|
||||
def place(world:World, player:EntityPlayer, item:ItemStack)
|
||||
{
|
||||
TileMultipart.addPart(world, pos, part)
|
||||
class AdditionPlacement($pos: BlockCoord, $part: Microblock)
|
||||
extends ExecutablePlacement($pos, $part) {
|
||||
def place(world: World, player: EntityPlayer, item: ItemStack) {
|
||||
TileMultipart.addPart(world, pos, part)
|
||||
}
|
||||
|
||||
def consume(world: World, player: EntityPlayer, item: ItemStack) {
|
||||
item.stackSize -= 1
|
||||
}
|
||||
}
|
||||
|
||||
class ExpandingPlacement($pos: BlockCoord, $part: Microblock, opart: Microblock)
|
||||
extends ExecutablePlacement($pos, $part) {
|
||||
def place(world: World, player: EntityPlayer, item: ItemStack) {
|
||||
opart.shape = part.shape
|
||||
opart.tile.notifyPartChange(opart)
|
||||
opart.sendShapeUpdate()
|
||||
}
|
||||
|
||||
def consume(world: World, player: EntityPlayer, item: ItemStack) {
|
||||
item.stackSize -= 1
|
||||
}
|
||||
}
|
||||
|
||||
abstract class PlacementProperties {
|
||||
def opposite(slot: Int, side: Int): Int
|
||||
|
||||
def sneakOpposite(slot: Int, side: Int) = true
|
||||
|
||||
def expand(slot: Int, side: Int) = true
|
||||
|
||||
def microClass: MicroblockClass
|
||||
|
||||
def placementGrid: PlacementGrid
|
||||
|
||||
def customPlacement(pmt: MicroblockPlacement): ExecutablePlacement = null
|
||||
}
|
||||
|
||||
object MicroblockPlacement {
|
||||
def apply(
|
||||
player: EntityPlayer,
|
||||
hit: MovingObjectPosition,
|
||||
size: Int,
|
||||
material: Int,
|
||||
checkMaterial: Boolean,
|
||||
pp: PlacementProperties
|
||||
): ExecutablePlacement =
|
||||
new MicroblockPlacement(player, hit, size, material, checkMaterial, pp)()
|
||||
}
|
||||
|
||||
class MicroblockPlacement(
|
||||
val player: EntityPlayer,
|
||||
val hit: MovingObjectPosition,
|
||||
val size: Int,
|
||||
val material: Int,
|
||||
val checkMaterial: Boolean,
|
||||
val pp: PlacementProperties
|
||||
) {
|
||||
val world = player.worldObj
|
||||
val mcrClass = pp.microClass
|
||||
val pos = new BlockCoord(hit.blockX, hit.blockY, hit.blockZ)
|
||||
val vhit = new Vector3(hit.hitVec).add(-pos.x, -pos.y, -pos.z)
|
||||
val gtile = TileMultipart.getOrConvertTile2(world, pos)
|
||||
val htile = gtile._1
|
||||
val slot = pp.placementGrid.getHitSlot(vhit, hit.sideHit)
|
||||
val oslot = pp.opposite(slot, hit.sideHit)
|
||||
|
||||
val d = getHitDepth(vhit, hit.sideHit)
|
||||
val useOppMod = pp.sneakOpposite(slot, hit.sideHit)
|
||||
val oppMod = player.isControlDown
|
||||
val internal = d < 1 && htile != null
|
||||
val doExpand =
|
||||
internal && !gtile._2 && !player.isSneaking && !(oppMod && useOppMod) && pp
|
||||
.expand(slot, hit.sideHit)
|
||||
val side = hit.sideHit
|
||||
|
||||
def apply(): ExecutablePlacement = {
|
||||
val customPlacement = pp.customPlacement(this)
|
||||
if (customPlacement != null)
|
||||
return customPlacement
|
||||
|
||||
if (slot < 0)
|
||||
return null
|
||||
|
||||
if (doExpand) {
|
||||
val hpart = htile.partList(ExtendedMOP.getData[(Int, _)](hit)._1)
|
||||
if (hpart.getType == mcrClass.getName) {
|
||||
val mpart = hpart.asInstanceOf[CommonMicroblock]
|
||||
if (mpart.material == material && mpart.getSize + size < 8)
|
||||
return expand(mpart)
|
||||
}
|
||||
}
|
||||
|
||||
def consume(world:World, player:EntityPlayer, item:ItemStack)
|
||||
{
|
||||
item.stackSize-=1
|
||||
}
|
||||
}
|
||||
|
||||
class ExpandingPlacement($pos:BlockCoord, $part:Microblock, opart:Microblock) extends ExecutablePlacement($pos, $part)
|
||||
{
|
||||
def place(world:World, player:EntityPlayer, item:ItemStack)
|
||||
{
|
||||
opart.shape = part.shape
|
||||
opart.tile.notifyPartChange(opart)
|
||||
opart.sendShapeUpdate()
|
||||
}
|
||||
|
||||
def consume(world:World, player:EntityPlayer, item:ItemStack)
|
||||
{
|
||||
item.stackSize-=1
|
||||
}
|
||||
}
|
||||
|
||||
abstract class PlacementProperties
|
||||
{
|
||||
def opposite(slot:Int, side:Int):Int
|
||||
|
||||
def sneakOpposite(slot:Int, side:Int) = true
|
||||
|
||||
def expand(slot:Int, side:Int) = true
|
||||
|
||||
def microClass:MicroblockClass
|
||||
|
||||
def placementGrid:PlacementGrid
|
||||
|
||||
def customPlacement(pmt:MicroblockPlacement):ExecutablePlacement = null
|
||||
}
|
||||
|
||||
object MicroblockPlacement
|
||||
{
|
||||
def apply(player:EntityPlayer, hit:MovingObjectPosition, size:Int, material:Int, checkMaterial:Boolean, pp:PlacementProperties):ExecutablePlacement =
|
||||
new MicroblockPlacement(player, hit, size, material, checkMaterial, pp)()
|
||||
}
|
||||
|
||||
class MicroblockPlacement(val player:EntityPlayer, val hit:MovingObjectPosition, val size:Int, val material:Int, val checkMaterial:Boolean, val pp:PlacementProperties)
|
||||
{
|
||||
val world = player.worldObj
|
||||
val mcrClass = pp.microClass
|
||||
val pos = new BlockCoord(hit.blockX, hit.blockY, hit.blockZ)
|
||||
val vhit = new Vector3(hit.hitVec).add(-pos.x, -pos.y, -pos.z)
|
||||
val gtile = TileMultipart.getOrConvertTile2(world, pos)
|
||||
val htile = gtile._1
|
||||
val slot = pp.placementGrid.getHitSlot(vhit, hit.sideHit)
|
||||
val oslot = pp.opposite(slot, hit.sideHit)
|
||||
|
||||
val d = getHitDepth(vhit, hit.sideHit)
|
||||
val useOppMod = pp.sneakOpposite(slot, hit.sideHit)
|
||||
val oppMod = player.isControlDown
|
||||
val internal = d < 1 && htile != null
|
||||
val doExpand = internal && !gtile._2 && !player.isSneaking && !(oppMod && useOppMod) && pp.expand(slot, hit.sideHit)
|
||||
val side = hit.sideHit
|
||||
|
||||
def apply():ExecutablePlacement =
|
||||
{
|
||||
val customPlacement = pp.customPlacement(this)
|
||||
if(customPlacement != null)
|
||||
return customPlacement
|
||||
|
||||
if(slot < 0)
|
||||
return null
|
||||
|
||||
if(doExpand)
|
||||
{
|
||||
val hpart = htile.partList(ExtendedMOP.getData[(Int, _)](hit)._1)
|
||||
if(hpart.getType == mcrClass.getName)
|
||||
{
|
||||
val mpart = hpart.asInstanceOf[CommonMicroblock]
|
||||
if(mpart.material == material && mpart.getSize + size < 8)
|
||||
return expand(mpart)
|
||||
}
|
||||
}
|
||||
|
||||
if(internal)
|
||||
{
|
||||
if(d < 0.5 || !useOppMod)
|
||||
{
|
||||
val ret = internalPlacement(htile, slot)
|
||||
if(ret != null) {
|
||||
if(!useOppMod || !oppMod) return ret
|
||||
else return internalPlacement(htile, oslot)
|
||||
}
|
||||
}
|
||||
if(useOppMod && !oppMod)
|
||||
return internalPlacement(htile, oslot)
|
||||
else
|
||||
return externalPlacement(slot)
|
||||
if (internal) {
|
||||
if (d < 0.5 || !useOppMod) {
|
||||
val ret = internalPlacement(htile, slot)
|
||||
if (ret != null) {
|
||||
if (!useOppMod || !oppMod) return ret
|
||||
else return internalPlacement(htile, oslot)
|
||||
}
|
||||
}
|
||||
if (useOppMod && !oppMod)
|
||||
return internalPlacement(htile, oslot)
|
||||
else
|
||||
return externalPlacement(slot)
|
||||
}
|
||||
|
||||
if(!useOppMod || !oppMod)
|
||||
return externalPlacement(slot)
|
||||
else
|
||||
return externalPlacement(oslot)
|
||||
}
|
||||
|
||||
def expand(mpart:CommonMicroblock):ExecutablePlacement = expand(mpart, create(mpart.getSize+size, mpart.getSlot, mpart.material))
|
||||
|
||||
def expand(mpart:Microblock, npart:Microblock):ExecutablePlacement =
|
||||
{
|
||||
val pos = new BlockCoord(mpart.tile)
|
||||
if(TileMultipart.checkNoEntityCollision(world, pos, npart) && mpart.tile.canReplacePart(mpart, npart))
|
||||
return new ExpandingPlacement(pos, npart, mpart)
|
||||
return null
|
||||
}
|
||||
|
||||
def internalPlacement(htile:TileMultipart, slot:Int):ExecutablePlacement = internalPlacement(htile, create(size, slot, material))
|
||||
|
||||
def internalPlacement(htile:TileMultipart, npart:Microblock):ExecutablePlacement =
|
||||
{
|
||||
val pos = new BlockCoord(htile)
|
||||
if(TileMultipart.checkNoEntityCollision(world, pos, npart) && htile.canAddPart(npart))
|
||||
return new AdditionPlacement(pos, npart)
|
||||
return null
|
||||
}
|
||||
|
||||
def externalPlacement(slot:Int):ExecutablePlacement = externalPlacement(create(size, slot, material))
|
||||
|
||||
def externalPlacement(npart:Microblock):ExecutablePlacement =
|
||||
{
|
||||
val pos = this.pos.copy().offset(side)
|
||||
if(TileMultipart.canPlacePart(world, pos, npart))
|
||||
return new AdditionPlacement(pos, npart)
|
||||
return null
|
||||
}
|
||||
|
||||
def getHitDepth(vhit:Vector3, side:Int):Double =
|
||||
vhit.copy.scalarProject(Rotation.axes(side)) + (side%2^1)
|
||||
|
||||
def create(size:Int, slot:Int, material:Int) = {
|
||||
val part = mcrClass.create(world.isRemote, material)
|
||||
part.setShape(size, slot)
|
||||
part
|
||||
}
|
||||
}
|
||||
if (!useOppMod || !oppMod)
|
||||
return externalPlacement(slot)
|
||||
else
|
||||
return externalPlacement(oslot)
|
||||
}
|
||||
|
||||
def expand(mpart: CommonMicroblock): ExecutablePlacement =
|
||||
expand(mpart, create(mpart.getSize + size, mpart.getSlot, mpart.material))
|
||||
|
||||
def expand(mpart: Microblock, npart: Microblock): ExecutablePlacement = {
|
||||
val pos = new BlockCoord(mpart.tile)
|
||||
if (
|
||||
TileMultipart.checkNoEntityCollision(world, pos, npart) && mpart.tile
|
||||
.canReplacePart(mpart, npart)
|
||||
)
|
||||
return new ExpandingPlacement(pos, npart, mpart)
|
||||
return null
|
||||
}
|
||||
|
||||
def internalPlacement(htile: TileMultipart, slot: Int): ExecutablePlacement =
|
||||
internalPlacement(htile, create(size, slot, material))
|
||||
|
||||
def internalPlacement(
|
||||
htile: TileMultipart,
|
||||
npart: Microblock
|
||||
): ExecutablePlacement = {
|
||||
val pos = new BlockCoord(htile)
|
||||
if (
|
||||
TileMultipart.checkNoEntityCollision(world, pos, npart) && htile
|
||||
.canAddPart(npart)
|
||||
)
|
||||
return new AdditionPlacement(pos, npart)
|
||||
return null
|
||||
}
|
||||
|
||||
def externalPlacement(slot: Int): ExecutablePlacement = externalPlacement(
|
||||
create(size, slot, material)
|
||||
)
|
||||
|
||||
def externalPlacement(npart: Microblock): ExecutablePlacement = {
|
||||
val pos = this.pos.copy().offset(side)
|
||||
if (TileMultipart.canPlacePart(world, pos, npart))
|
||||
return new AdditionPlacement(pos, npart)
|
||||
return null
|
||||
}
|
||||
|
||||
def getHitDepth(vhit: Vector3, side: Int): Double =
|
||||
vhit.copy.scalarProject(Rotation.axes(side)) + (side % 2 ^ 1)
|
||||
|
||||
def create(size: Int, slot: Int, material: Int) = {
|
||||
val part = mcrClass.create(world.isRemote, material)
|
||||
part.setShape(size, slot)
|
||||
part
|
||||
}
|
||||
}
|
||||
@@ -8,48 +8,64 @@ import codechicken.lib.render.{CCRenderState, TextureUtils}
|
||||
import codechicken.lib.render.BlockRenderer.BlockFace
|
||||
import codechicken.microblock.MicroMaterialRegistry.IMicroMaterial
|
||||
|
||||
object MicroblockRender
|
||||
{
|
||||
def renderHighlight(player:EntityPlayer, hit:MovingObjectPosition, mcrClass:CommonMicroClass, size:Int, material:Int)
|
||||
{
|
||||
mcrClass.placementProperties.placementGrid.render(new Vector3(hit.hitVec), hit.sideHit)
|
||||
object MicroblockRender {
|
||||
def renderHighlight(
|
||||
player: EntityPlayer,
|
||||
hit: MovingObjectPosition,
|
||||
mcrClass: CommonMicroClass,
|
||||
size: Int,
|
||||
material: Int
|
||||
) {
|
||||
mcrClass.placementProperties.placementGrid
|
||||
.render(new Vector3(hit.hitVec), hit.sideHit)
|
||||
|
||||
val placement = MicroblockPlacement(player, hit, size, material, !player.capabilities.isCreativeMode, mcrClass.placementProperties)
|
||||
if(placement == null)
|
||||
return
|
||||
val pos = placement.pos
|
||||
val part = placement.part.asInstanceOf[MicroblockClient]
|
||||
val placement = MicroblockPlacement(
|
||||
player,
|
||||
hit,
|
||||
size,
|
||||
material,
|
||||
!player.capabilities.isCreativeMode,
|
||||
mcrClass.placementProperties
|
||||
)
|
||||
if (placement == null)
|
||||
return
|
||||
val pos = placement.pos
|
||||
val part = placement.part.asInstanceOf[MicroblockClient]
|
||||
|
||||
glPushMatrix()
|
||||
glTranslated(pos.x+0.5, pos.y+0.5, pos.z+0.5)
|
||||
glScaled(1.002, 1.002, 1.002)
|
||||
glTranslated(-0.5, -0.5, -0.5)
|
||||
glPushMatrix()
|
||||
glTranslated(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5)
|
||||
glScaled(1.002, 1.002, 1.002)
|
||||
glTranslated(-0.5, -0.5, -0.5)
|
||||
|
||||
glEnable(GL_BLEND)
|
||||
glDepthMask(false)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
glEnable(GL_BLEND)
|
||||
glDepthMask(false)
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
|
||||
|
||||
TextureUtils.bindAtlas(0)
|
||||
CCRenderState.reset()
|
||||
CCRenderState.alphaOverride = 80
|
||||
CCRenderState.useNormals = true
|
||||
CCRenderState.startDrawing()
|
||||
part.render(Vector3.zero, -1)
|
||||
CCRenderState.draw()
|
||||
TextureUtils.bindAtlas(0)
|
||||
CCRenderState.reset()
|
||||
CCRenderState.alphaOverride = 80
|
||||
CCRenderState.useNormals = true
|
||||
CCRenderState.startDrawing()
|
||||
part.render(Vector3.zero, -1)
|
||||
CCRenderState.draw()
|
||||
|
||||
glDisable(GL_BLEND)
|
||||
glDepthMask(true)
|
||||
glPopMatrix()
|
||||
}
|
||||
|
||||
val face = new BlockFace()
|
||||
def renderCuboid(pos:Vector3, mat:IMicroMaterial, pass:Int, c:Cuboid6, faces:Int) {
|
||||
CCRenderState.setModel(face)
|
||||
for(s <- 0 until 6 if (faces & 1<<s) == 0) {
|
||||
face.loadCuboidFace(c, s)
|
||||
mat.renderMicroFace(pos, pass, c)
|
||||
}
|
||||
glDisable(GL_BLEND)
|
||||
glDepthMask(true)
|
||||
glPopMatrix()
|
||||
}
|
||||
|
||||
val face = new BlockFace()
|
||||
def renderCuboid(
|
||||
pos: Vector3,
|
||||
mat: IMicroMaterial,
|
||||
pass: Int,
|
||||
c: Cuboid6,
|
||||
faces: Int
|
||||
) {
|
||||
CCRenderState.setModel(face)
|
||||
for (s <- 0 until 6 if (faces & 1 << s) == 0) {
|
||||
face.loadCuboidFace(c, s)
|
||||
mat.renderMicroFace(pos, pass, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,173 +7,174 @@ import Rotation._
|
||||
import org.lwjgl.opengl.GL11._
|
||||
import codechicken.multipart.PartMap
|
||||
|
||||
trait PlacementGrid
|
||||
{
|
||||
def getHitSlot(vhit: Vector3, side: Int): Int
|
||||
trait PlacementGrid {
|
||||
def getHitSlot(vhit: Vector3, side: Int): Int
|
||||
|
||||
def render(hit: Vector3, side: Int) {
|
||||
glTransformFace(hit, side)
|
||||
glLineWidth(2)
|
||||
glColor4f(0, 0, 0, 1)
|
||||
glBegin(GL_LINES)
|
||||
drawLines()
|
||||
glEnd()
|
||||
glPopMatrix()
|
||||
}
|
||||
def render(hit: Vector3, side: Int) {
|
||||
glTransformFace(hit, side)
|
||||
glLineWidth(2)
|
||||
glColor4f(0, 0, 0, 1)
|
||||
glBegin(GL_LINES)
|
||||
drawLines()
|
||||
glEnd()
|
||||
glPopMatrix()
|
||||
}
|
||||
|
||||
def drawLines() {}
|
||||
def drawLines() {}
|
||||
|
||||
def glTransformFace(hit: Vector3, side: Int) {
|
||||
val pos = new BlockCoord(hit)
|
||||
glPushMatrix()
|
||||
glTranslated(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5)
|
||||
sideRotations(side).glApply()
|
||||
val rhit = new Vector3(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5).subtract(hit).apply(sideRotations(side ^ 1).inverse)
|
||||
glTranslated(0, rhit.y - 0.002, 0)
|
||||
}
|
||||
def glTransformFace(hit: Vector3, side: Int) {
|
||||
val pos = new BlockCoord(hit)
|
||||
glPushMatrix()
|
||||
glTranslated(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5)
|
||||
sideRotations(side).glApply()
|
||||
val rhit = new Vector3(pos.x + 0.5, pos.y + 0.5, pos.z + 0.5)
|
||||
.subtract(hit)
|
||||
.apply(sideRotations(side ^ 1).inverse)
|
||||
glTranslated(0, rhit.y - 0.002, 0)
|
||||
}
|
||||
}
|
||||
|
||||
class FaceEdgeGrid(size: Double) extends PlacementGrid
|
||||
{
|
||||
override def drawLines() {
|
||||
glVertex3d(-0.5, 0, -0.5)
|
||||
glVertex3d(-0.5, 0, 0.5)
|
||||
class FaceEdgeGrid(size: Double) extends PlacementGrid {
|
||||
override def drawLines() {
|
||||
glVertex3d(-0.5, 0, -0.5)
|
||||
glVertex3d(-0.5, 0, 0.5)
|
||||
|
||||
glVertex3d(-0.5, 0, 0.5)
|
||||
glVertex3d(0.5, 0, 0.5)
|
||||
glVertex3d(-0.5, 0, 0.5)
|
||||
glVertex3d(0.5, 0, 0.5)
|
||||
|
||||
glVertex3d(0.5, 0, 0.5)
|
||||
glVertex3d(0.5, 0, -0.5)
|
||||
glVertex3d(0.5, 0, 0.5)
|
||||
glVertex3d(0.5, 0, -0.5)
|
||||
|
||||
glVertex3d(0.5, 0, -0.5)
|
||||
glVertex3d(-0.5, 0, -0.5)
|
||||
glVertex3d(0.5, 0, -0.5)
|
||||
glVertex3d(-0.5, 0, -0.5)
|
||||
|
||||
glVertex3d(0.5, 0, 0.5)
|
||||
glVertex3d(size, 0, size)
|
||||
glVertex3d(0.5, 0, 0.5)
|
||||
glVertex3d(size, 0, size)
|
||||
|
||||
glVertex3d(-0.5, 0, 0.5)
|
||||
glVertex3d(-size, 0, size)
|
||||
glVertex3d(-0.5, 0, 0.5)
|
||||
glVertex3d(-size, 0, size)
|
||||
|
||||
glVertex3d(0.5, 0, -0.5)
|
||||
glVertex3d(size, 0, -size)
|
||||
glVertex3d(0.5, 0, -0.5)
|
||||
glVertex3d(size, 0, -size)
|
||||
|
||||
glVertex3d(-0.5, 0, -0.5)
|
||||
glVertex3d(-size, 0, -size)
|
||||
glVertex3d(-0.5, 0, -0.5)
|
||||
glVertex3d(-size, 0, -size)
|
||||
|
||||
glVertex3d(-size, 0, -size)
|
||||
glVertex3d(-size, 0, size)
|
||||
glVertex3d(-size, 0, -size)
|
||||
glVertex3d(-size, 0, size)
|
||||
|
||||
glVertex3d(-size, 0, size)
|
||||
glVertex3d(size, 0, size)
|
||||
glVertex3d(-size, 0, size)
|
||||
glVertex3d(size, 0, size)
|
||||
|
||||
glVertex3d(size, 0, size)
|
||||
glVertex3d(size, 0, -size)
|
||||
glVertex3d(size, 0, size)
|
||||
glVertex3d(size, 0, -size)
|
||||
|
||||
glVertex3d(size, 0, -size)
|
||||
glVertex3d(-size, 0, -size)
|
||||
}
|
||||
glVertex3d(size, 0, -size)
|
||||
glVertex3d(-size, 0, -size)
|
||||
}
|
||||
|
||||
def getHitSlot(vhit: Vector3, side: Int) = {
|
||||
val s1 = (side + 2) % 6
|
||||
val s2 = (side + 4) % 6
|
||||
val u = vhit.copy.add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s1))
|
||||
val v = vhit.copy.add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s2))
|
||||
def getHitSlot(vhit: Vector3, side: Int) = {
|
||||
val s1 = (side + 2) % 6
|
||||
val s2 = (side + 4) % 6
|
||||
val u = vhit.copy.add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s1))
|
||||
val v = vhit.copy.add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s2))
|
||||
|
||||
if (Math.abs(u) < size && Math.abs(v) < size)
|
||||
side ^ 1
|
||||
else if (Math.abs(u) > Math.abs(v))
|
||||
if (u > 0) s1 else s1 ^ 1
|
||||
else
|
||||
if (v > 0) s2 else s2 ^ 1
|
||||
}
|
||||
if (Math.abs(u) < size && Math.abs(v) < size)
|
||||
side ^ 1
|
||||
else if (Math.abs(u) > Math.abs(v))
|
||||
if (u > 0) s1 else s1 ^ 1
|
||||
else if (v > 0) s2
|
||||
else s2 ^ 1
|
||||
}
|
||||
}
|
||||
|
||||
object FacePlacementGrid extends FaceEdgeGrid(1 / 4D)
|
||||
object FacePlacementGrid extends FaceEdgeGrid(1 / 4d)
|
||||
|
||||
object CornerPlacementGrid extends PlacementGrid
|
||||
{
|
||||
override def drawLines() {
|
||||
glVertex3d(-0.5, 0, -0.5)
|
||||
glVertex3d(-0.5, 0, 0.5)
|
||||
object CornerPlacementGrid extends PlacementGrid {
|
||||
override def drawLines() {
|
||||
glVertex3d(-0.5, 0, -0.5)
|
||||
glVertex3d(-0.5, 0, 0.5)
|
||||
|
||||
glVertex3d(-0.5, 0, 0.5)
|
||||
glVertex3d(0.5, 0, 0.5)
|
||||
glVertex3d(-0.5, 0, 0.5)
|
||||
glVertex3d(0.5, 0, 0.5)
|
||||
|
||||
glVertex3d(0.5, 0, 0.5)
|
||||
glVertex3d(0.5, 0, -0.5)
|
||||
glVertex3d(0.5, 0, 0.5)
|
||||
glVertex3d(0.5, 0, -0.5)
|
||||
|
||||
glVertex3d(0.5, 0, -0.5)
|
||||
glVertex3d(-0.5, 0, -0.5)
|
||||
glVertex3d(0.5, 0, -0.5)
|
||||
glVertex3d(-0.5, 0, -0.5)
|
||||
|
||||
glVertex3d(0, 0, -0.5)
|
||||
glVertex3d(0, 0, 0.5)
|
||||
glVertex3d(0, 0, -0.5)
|
||||
glVertex3d(0, 0, 0.5)
|
||||
|
||||
glVertex3d(-0.5, 0, 0)
|
||||
glVertex3d(0.5, 0, 0)
|
||||
}
|
||||
glVertex3d(-0.5, 0, 0)
|
||||
glVertex3d(0.5, 0, 0)
|
||||
}
|
||||
|
||||
def getHitSlot(vhit: Vector3, side: Int): Int = {
|
||||
val s1 = ((side & 6) + 3) % 6
|
||||
val s2 = ((side & 6) + 5) % 6
|
||||
val u = vhit.copy().add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s1))
|
||||
val v = vhit.copy().add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s2))
|
||||
def getHitSlot(vhit: Vector3, side: Int): Int = {
|
||||
val s1 = ((side & 6) + 3) % 6
|
||||
val s2 = ((side & 6) + 5) % 6
|
||||
val u = vhit.copy().add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s1))
|
||||
val v = vhit.copy().add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s2))
|
||||
|
||||
val bu = if (u >= 0) 1 else 0
|
||||
val bv = if (v >= 0) 1 else 0
|
||||
val bw = (side & 1) ^ 1
|
||||
val bu = if (u >= 0) 1 else 0
|
||||
val bv = if (v >= 0) 1 else 0
|
||||
val bw = (side & 1) ^ 1
|
||||
|
||||
return 7 + (
|
||||
bw << (side >> 1) |
|
||||
bu << (s1 >> 1) |
|
||||
bv << (s2 >> 1))
|
||||
}
|
||||
return 7 + (bw << (side >> 1) |
|
||||
bu << (s1 >> 1) |
|
||||
bv << (s2 >> 1))
|
||||
}
|
||||
}
|
||||
|
||||
object EdgePlacementGrid extends PlacementGrid
|
||||
{
|
||||
override def drawLines() {
|
||||
glVertex3d(-0.5, 0, -0.5)
|
||||
glVertex3d(-0.5, 0, 0.5)
|
||||
object EdgePlacementGrid extends PlacementGrid {
|
||||
override def drawLines() {
|
||||
glVertex3d(-0.5, 0, -0.5)
|
||||
glVertex3d(-0.5, 0, 0.5)
|
||||
|
||||
glVertex3d(-0.5, 0, 0.5)
|
||||
glVertex3d(0.5, 0, 0.5)
|
||||
glVertex3d(-0.5, 0, 0.5)
|
||||
glVertex3d(0.5, 0, 0.5)
|
||||
|
||||
glVertex3d(0.5, 0, 0.5)
|
||||
glVertex3d(0.5, 0, -0.5)
|
||||
glVertex3d(0.5, 0, 0.5)
|
||||
glVertex3d(0.5, 0, -0.5)
|
||||
|
||||
glVertex3d(0.5, 0, -0.5)
|
||||
glVertex3d(-0.5, 0, -0.5)
|
||||
glVertex3d(0.5, 0, -0.5)
|
||||
glVertex3d(-0.5, 0, -0.5)
|
||||
|
||||
glVertex3d(0.25, 0, -0.5)
|
||||
glVertex3d(0.25, 0, 0.5)
|
||||
glVertex3d(0.25, 0, -0.5)
|
||||
glVertex3d(0.25, 0, 0.5)
|
||||
|
||||
glVertex3d(-0.25, 0, -0.5)
|
||||
glVertex3d(-0.25, 0, 0.5)
|
||||
glVertex3d(-0.25, 0, -0.5)
|
||||
glVertex3d(-0.25, 0, 0.5)
|
||||
|
||||
glVertex3d(-0.5, 0, 0.25)
|
||||
glVertex3d(0.5, 0, 0.25)
|
||||
glVertex3d(-0.5, 0, 0.25)
|
||||
glVertex3d(0.5, 0, 0.25)
|
||||
|
||||
glVertex3d(-0.5, 0, -0.25)
|
||||
glVertex3d(0.5, 0, -0.25)
|
||||
}
|
||||
glVertex3d(-0.5, 0, -0.25)
|
||||
glVertex3d(0.5, 0, -0.25)
|
||||
}
|
||||
|
||||
override def getHitSlot(vhit: Vector3, side: Int): Int = {
|
||||
val s1 = (side + 2) % 6
|
||||
val s2 = (side + 4) % 6
|
||||
val u = vhit.copy.add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s1))
|
||||
val v = vhit.copy.add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s2))
|
||||
override def getHitSlot(vhit: Vector3, side: Int): Int = {
|
||||
val s1 = (side + 2) % 6
|
||||
val s2 = (side + 4) % 6
|
||||
val u = vhit.copy.add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s1))
|
||||
val v = vhit.copy.add(-0.5, -0.5, -0.5).scalarProject(Rotation.axes(s2))
|
||||
|
||||
if (Math.abs(u) < 4 / 16D && Math.abs(v) < 4 / 16D)
|
||||
return -1
|
||||
if (Math.abs(u) < 4 / 16d && Math.abs(v) < 4 / 16d)
|
||||
return -1
|
||||
|
||||
if(Math.abs(u) > 4 / 16D && Math.abs(v) > 4 / 16D)
|
||||
return PartMap.edgeBetween(if(u > 0) s1 else s1^1, if(v > 0) s2 else s2^1)
|
||||
if (Math.abs(u) > 4 / 16d && Math.abs(v) > 4 / 16d)
|
||||
return PartMap.edgeBetween(
|
||||
if (u > 0) s1 else s1 ^ 1,
|
||||
if (v > 0) s2 else s2 ^ 1
|
||||
)
|
||||
|
||||
val s = if(Math.abs(u) > Math.abs(v))
|
||||
if(u > 0) s1 else s1^1
|
||||
else
|
||||
if(v > 0) s2 else s2^1
|
||||
val s =
|
||||
if (Math.abs(u) > Math.abs(v))
|
||||
if (u > 0) s1 else s1 ^ 1
|
||||
else if (v > 0) s2
|
||||
else s2 ^ 1
|
||||
|
||||
return PartMap.edgeBetween(side^1, s)
|
||||
}
|
||||
}
|
||||
return PartMap.edgeBetween(side ^ 1, s)
|
||||
}
|
||||
}
|
||||
@@ -5,257 +5,249 @@ import codechicken.lib.vec.Cuboid6
|
||||
import codechicken.multipart.PartMap._
|
||||
import codechicken.lib.data.MCDataInput
|
||||
|
||||
/**
|
||||
* Suite of classes for performing microblock style occlusion and shrink rendering. Use at your own peril.
|
||||
*/
|
||||
object MicroOcclusion
|
||||
{
|
||||
def shrink(renderBounds:Cuboid6, b:Cuboid6, side:Int) = side match
|
||||
/** Suite of classes for performing microblock style occlusion and shrink
|
||||
* rendering. Use at your own peril.
|
||||
*/
|
||||
object MicroOcclusion {
|
||||
def shrink(renderBounds: Cuboid6, b: Cuboid6, side: Int) = side match {
|
||||
case -1 =>
|
||||
case 0 => if (renderBounds.min.y < b.max.y) renderBounds.min.y = b.max.y
|
||||
case 1 => if (renderBounds.max.y > b.min.y) renderBounds.max.y = b.min.y
|
||||
case 2 => if (renderBounds.min.z < b.max.z) renderBounds.min.z = b.max.z
|
||||
case 3 => if (renderBounds.max.z > b.min.z) renderBounds.max.z = b.min.z
|
||||
case 4 => if (renderBounds.min.x < b.max.x) renderBounds.min.x = b.max.x
|
||||
case 5 => if (renderBounds.max.x > b.min.x) renderBounds.max.x = b.min.x
|
||||
}
|
||||
|
||||
def shrinkFrom(
|
||||
p: JMicroShrinkRender,
|
||||
other: JMicroShrinkRender,
|
||||
renderBounds: Cuboid6
|
||||
): Int = {
|
||||
if (shrinkTest(p, other)) {
|
||||
shrink(
|
||||
renderBounds,
|
||||
other.getBounds,
|
||||
shrinkSide(p.getSlot, other.getSlot)
|
||||
)
|
||||
} else if (
|
||||
other.getSlot < 6 && !other.isTransparent
|
||||
) // other gets full face, we didn't shrink, flag rendermask
|
||||
{
|
||||
if (
|
||||
other.getSlot match {
|
||||
case 0 => renderBounds.min.y <= 0
|
||||
case 1 => renderBounds.max.y >= 1
|
||||
case 2 => renderBounds.min.z <= 0
|
||||
case 3 => renderBounds.max.z >= 1
|
||||
case 4 => renderBounds.min.x <= 0
|
||||
case 5 => renderBounds.max.x >= 1
|
||||
}
|
||||
)
|
||||
return 1 << other.getSlot
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
def shrink(p: JMicroShrinkRender, renderBounds: Cuboid6, m: Int): Int = {
|
||||
var renderMask = 0
|
||||
val part = p.asInstanceOf[TMultiPart]
|
||||
val tile = part.tile
|
||||
for (i <- 0 until m)
|
||||
if (i != p.getSlot)
|
||||
tile.partMap(i) match {
|
||||
case other: JMicroShrinkRender =>
|
||||
renderMask |= shrinkFrom(p, other, renderBounds)
|
||||
case _ =>
|
||||
}
|
||||
return renderMask
|
||||
}
|
||||
|
||||
def shrinkSide(s1: Int, s2: Int): Int = {
|
||||
if (s2 < 6) // other is a cover
|
||||
return s2
|
||||
if (s1 < 15) // both corners
|
||||
{
|
||||
val c1 = s1 - 7
|
||||
val c2 = s2 - 7
|
||||
return c1 ^ c2 match // different axis bits will become 1, same 0
|
||||
{
|
||||
case 1 => c2 & 1
|
||||
case 2 => 2 | (c2 & 2) >> 1
|
||||
case 4 => 4 | (c2 & 4) >> 2
|
||||
case _ => -1
|
||||
}
|
||||
}
|
||||
if (s2 < 15) // edge, other corner
|
||||
{
|
||||
val e1 = s1 - 15
|
||||
val c2 = s2 - 7
|
||||
val ebits = unpackEdgeBits(e1)
|
||||
if ((c2 & edgeAxisMask(e1)) != ebits)
|
||||
return -1
|
||||
|
||||
return (e1 & 0xc) >> 1 | (c2 & (~ebits)) >> (e1 >> 2)
|
||||
}
|
||||
// both edges
|
||||
{
|
||||
case -1 =>
|
||||
case 0 => if(renderBounds.min.y < b.max.y)renderBounds.min.y = b.max.y
|
||||
case 1 => if(renderBounds.max.y > b.min.y)renderBounds.max.y = b.min.y
|
||||
case 2 => if(renderBounds.min.z < b.max.z)renderBounds.min.z = b.max.z
|
||||
case 3 => if(renderBounds.max.z > b.min.z)renderBounds.max.z = b.min.z
|
||||
case 4 => if(renderBounds.min.x < b.max.x)renderBounds.min.x = b.max.x
|
||||
case 5 => if(renderBounds.max.x > b.min.x)renderBounds.max.x = b.min.x
|
||||
val e1 = s1 - 15
|
||||
val e2 = s2 - 15
|
||||
val e1bits = unpackEdgeBits(e1)
|
||||
val e2bits = unpackEdgeBits(e2)
|
||||
if ((e1 & 0xc) == (e2 & 0xc)) // same axis
|
||||
{
|
||||
return e1bits ^ e2bits match {
|
||||
case 1 => if ((e2bits & 1) == 0) 0 else 1
|
||||
case 2 => if ((e2bits & 2) == 0) 2 else 3
|
||||
case 4 => if ((e2bits & 4) == 0) 4 else 5
|
||||
case _ => -1
|
||||
}
|
||||
} else {
|
||||
val mask = edgeAxisMask(e1) & edgeAxisMask(e2)
|
||||
if ((e1bits & mask) != (e2bits & mask))
|
||||
return -1
|
||||
|
||||
return e1 >> 2 match {
|
||||
case 0 => if ((e2bits & 1) == 0) 0 else 1
|
||||
case 1 => if ((e2bits & 2) == 0) 2 else 3
|
||||
case 2 => if ((e2bits & 4) == 0) 4 else 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def shrinkFrom(p:JMicroShrinkRender, other:JMicroShrinkRender, renderBounds:Cuboid6):Int =
|
||||
{
|
||||
if(shrinkTest(p, other))
|
||||
{
|
||||
shrink(renderBounds, other.getBounds, shrinkSide(p.getSlot, other.getSlot))
|
||||
}
|
||||
else if(other.getSlot < 6 && !other.isTransparent)//other gets full face, we didn't shrink, flag rendermask
|
||||
{
|
||||
if(other.getSlot match {
|
||||
case 0 => renderBounds.min.y <= 0
|
||||
case 1 => renderBounds.max.y >= 1
|
||||
case 2 => renderBounds.min.z <= 0
|
||||
case 3 => renderBounds.max.z >= 1
|
||||
case 4 => renderBounds.min.x <= 0
|
||||
case 5 => renderBounds.max.x >= 1
|
||||
})
|
||||
return 1<<other.getSlot
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
def shrink(p:JMicroShrinkRender, renderBounds:Cuboid6, m:Int):Int =
|
||||
{
|
||||
var renderMask = 0
|
||||
val part = p.asInstanceOf[TMultiPart]
|
||||
val tile = part.tile
|
||||
for(i <- 0 until m)
|
||||
if(i != p.getSlot)
|
||||
tile.partMap(i) match {
|
||||
case other:JMicroShrinkRender =>
|
||||
renderMask |= shrinkFrom(p, other, renderBounds)
|
||||
case _ =>
|
||||
}
|
||||
return renderMask
|
||||
}
|
||||
|
||||
def shrinkSide(s1:Int, s2:Int):Int = {
|
||||
if(s2 < 6)//other is a cover
|
||||
return s2
|
||||
if(s1 < 15)//both corners
|
||||
{
|
||||
val c1 = s1-7
|
||||
val c2 = s2-7
|
||||
return c1^c2 match//different axis bits will become 1, same 0
|
||||
{
|
||||
case 1 => c2&1
|
||||
case 2 => 2|(c2&2)>>1
|
||||
case 4 => 4|(c2&4)>>2
|
||||
case _ => -1
|
||||
}
|
||||
}
|
||||
if(s2 < 15)//edge, other corner
|
||||
{
|
||||
val e1 = s1-15
|
||||
val c2 = s2-7
|
||||
val ebits = unpackEdgeBits(e1)
|
||||
if((c2&edgeAxisMask(e1)) != ebits)
|
||||
return -1
|
||||
|
||||
return (e1&0xC)>>1|(c2&(~ebits))>>(e1>>2)
|
||||
}
|
||||
//both edges
|
||||
{
|
||||
val e1 = s1-15
|
||||
val e2 = s2-15
|
||||
val e1bits = unpackEdgeBits(e1)
|
||||
val e2bits = unpackEdgeBits(e2)
|
||||
if((e1&0xC) == (e2&0xC))//same axis
|
||||
{
|
||||
return e1bits^e2bits match
|
||||
{
|
||||
case 1 => if((e2bits&1) == 0) 0 else 1
|
||||
case 2 => if((e2bits&2) == 0) 2 else 3
|
||||
case 4 => if((e2bits&4) == 0) 4 else 5
|
||||
case _ => -1
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
val mask = edgeAxisMask(e1)&edgeAxisMask(e2)
|
||||
if((e1bits&mask) != (e2bits&mask))
|
||||
return -1
|
||||
|
||||
return e1>>2 match
|
||||
{
|
||||
case 0 => if((e2bits&1) == 0) 0 else 1
|
||||
case 1 => if((e2bits&2) == 0) 2 else 3
|
||||
case 2 => if((e2bits&4) == 0) 4 else 5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException("Switch Falloff")
|
||||
}
|
||||
|
||||
def recalcBounds(p:JMicroShrinkRender, renderBounds:Cuboid6) =
|
||||
{
|
||||
if(p.getSlot < 6)
|
||||
shrink(p, renderBounds, 6)
|
||||
else if(p.getSlot < 15)
|
||||
shrink(p, renderBounds, 15)
|
||||
else
|
||||
shrink(p, renderBounds, 27)
|
||||
}
|
||||
|
||||
def shapePriority(slot:Int):Int =
|
||||
if(slot < 6)
|
||||
return 2
|
||||
else if(slot < 15)
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
|
||||
def shrinkTest(a:JMicroShrinkRender, b:JMicroShrinkRender):Boolean =
|
||||
{
|
||||
if(a.getPriorityClass != b.getPriorityClass) return a.getPriorityClass < b.getPriorityClass
|
||||
|
||||
val shape1 = shapePriority(a.getSlot)
|
||||
val shape2 = shapePriority(b.getSlot)
|
||||
|
||||
if(shape1 != shape2) return shape1 < shape2
|
||||
if(a.getSlot < 6)//transparency takes precedence for covers
|
||||
{
|
||||
if(a.isTransparent != b.isTransparent) return a.isTransparent
|
||||
if(a.getSize != b.getSize) return a.getSize < b.getSize
|
||||
}
|
||||
else
|
||||
{
|
||||
if(a.getSize != b.getSize) return a.getSize < b.getSize
|
||||
if(a.isTransparent != b.isTransparent) return a.isTransparent
|
||||
}
|
||||
return a.getSlot < b.getSlot
|
||||
|
||||
throw new IllegalArgumentException("Switch Falloff")
|
||||
}
|
||||
|
||||
def recalcBounds(p: JMicroShrinkRender, renderBounds: Cuboid6) = {
|
||||
if (p.getSlot < 6)
|
||||
shrink(p, renderBounds, 6)
|
||||
else if (p.getSlot < 15)
|
||||
shrink(p, renderBounds, 15)
|
||||
else
|
||||
shrink(p, renderBounds, 27)
|
||||
}
|
||||
|
||||
def shapePriority(slot: Int): Int =
|
||||
if (slot < 6)
|
||||
return 2
|
||||
else if (slot < 15)
|
||||
return 1
|
||||
else
|
||||
return 0
|
||||
|
||||
def shrinkTest(a: JMicroShrinkRender, b: JMicroShrinkRender): Boolean = {
|
||||
if (a.getPriorityClass != b.getPriorityClass)
|
||||
return a.getPriorityClass < b.getPriorityClass
|
||||
|
||||
val shape1 = shapePriority(a.getSlot)
|
||||
val shape2 = shapePriority(b.getSlot)
|
||||
|
||||
if (shape1 != shape2) return shape1 < shape2
|
||||
if (a.getSlot < 6) // transparency takes precedence for covers
|
||||
{
|
||||
if (a.isTransparent != b.isTransparent) return a.isTransparent
|
||||
if (a.getSize != b.getSize) return a.getSize < b.getSize
|
||||
} else {
|
||||
if (a.getSize != b.getSize) return a.getSize < b.getSize
|
||||
if (a.isTransparent != b.isTransparent) return a.isTransparent
|
||||
}
|
||||
return a.getSlot < b.getSlot
|
||||
}
|
||||
}
|
||||
|
||||
trait JMicroShrinkRender
|
||||
{
|
||||
def getPriorityClass:Int
|
||||
def getSlot:Int
|
||||
def getSize:Int
|
||||
def isTransparent:Boolean
|
||||
def getBounds:Cuboid6
|
||||
trait JMicroShrinkRender {
|
||||
def getPriorityClass: Int
|
||||
def getSlot: Int
|
||||
def getSize: Int
|
||||
def isTransparent: Boolean
|
||||
def getBounds: Cuboid6
|
||||
}
|
||||
|
||||
trait TMicroOcclusionClient extends TMicroOcclusion with JMicroShrinkRender
|
||||
{
|
||||
var renderBounds:Cuboid6 = _
|
||||
var renderMask:Int = _
|
||||
|
||||
override def onPartChanged(part:TMultiPart)
|
||||
{
|
||||
super.onPartChanged(part)
|
||||
recalcBounds()
|
||||
}
|
||||
|
||||
override def onAdded()
|
||||
{
|
||||
super.onAdded()
|
||||
recalcBounds()
|
||||
}
|
||||
trait TMicroOcclusionClient extends TMicroOcclusion with JMicroShrinkRender {
|
||||
var renderBounds: Cuboid6 = _
|
||||
var renderMask: Int = _
|
||||
|
||||
override def read(packet:MCDataInput)
|
||||
{
|
||||
super.read(packet)
|
||||
recalcBounds()
|
||||
}
|
||||
|
||||
def recalcBounds()
|
||||
{
|
||||
renderBounds = getBounds.copy
|
||||
renderMask = MicroOcclusion.recalcBounds(this, renderBounds)
|
||||
}
|
||||
|
||||
override def getPriorityClass = 0
|
||||
override def onPartChanged(part: TMultiPart) {
|
||||
super.onPartChanged(part)
|
||||
recalcBounds()
|
||||
}
|
||||
|
||||
override def onAdded() {
|
||||
super.onAdded()
|
||||
recalcBounds()
|
||||
}
|
||||
|
||||
override def read(packet: MCDataInput) {
|
||||
super.read(packet)
|
||||
recalcBounds()
|
||||
}
|
||||
|
||||
def recalcBounds() {
|
||||
renderBounds = getBounds.copy
|
||||
renderMask = MicroOcclusion.recalcBounds(this, renderBounds)
|
||||
}
|
||||
|
||||
override def getPriorityClass = 0
|
||||
}
|
||||
|
||||
trait TMicroOcclusion extends TMultiPart
|
||||
{
|
||||
import MicroOcclusion._
|
||||
|
||||
def getSlot:Int
|
||||
def getSize:Int
|
||||
def getMaterial:Int
|
||||
def getBounds:Cuboid6
|
||||
|
||||
override def occlusionTest(npart:TMultiPart):Boolean =
|
||||
{
|
||||
if(!super.occlusionTest(npart))
|
||||
trait TMicroOcclusion extends TMultiPart {
|
||||
import MicroOcclusion._
|
||||
|
||||
def getSlot: Int
|
||||
def getSize: Int
|
||||
def getMaterial: Int
|
||||
def getBounds: Cuboid6
|
||||
|
||||
override def occlusionTest(npart: TMultiPart): Boolean = {
|
||||
if (!super.occlusionTest(npart))
|
||||
return false
|
||||
|
||||
if (!npart.isInstanceOf[TMicroOcclusion])
|
||||
return true
|
||||
|
||||
val mpart = npart.asInstanceOf[TMicroOcclusion]
|
||||
val shape1 = shapePriority(getSlot)
|
||||
val shape2 = shapePriority(mpart.getSlot)
|
||||
|
||||
if (mpart.getSize + getSize > 8) // intersecting if opposite
|
||||
{
|
||||
if (shape1 == 2 && shape2 == 2)
|
||||
if (mpart.getSlot == (getSlot ^ 1))
|
||||
return false
|
||||
|
||||
if(!npart.isInstanceOf[TMicroOcclusion])
|
||||
return true
|
||||
|
||||
val mpart = npart.asInstanceOf[TMicroOcclusion]
|
||||
val shape1 = shapePriority(getSlot)
|
||||
val shape2 = shapePriority(mpart.getSlot)
|
||||
|
||||
if(mpart.getSize + getSize > 8)//intersecting if opposite
|
||||
{
|
||||
if(shape1 == 2 && shape2 == 2)
|
||||
if(mpart.getSlot == (getSlot^1))
|
||||
return false
|
||||
|
||||
if(mpart.getMaterial != getMaterial)
|
||||
{
|
||||
if(shape1 == 1 && shape2 == 1)
|
||||
{
|
||||
val axisMask = (getSlot-7)^(mpart.getSlot-7)
|
||||
if(axisMask == 3 || axisMask == 5 || axisMask == 6)
|
||||
return false
|
||||
}
|
||||
|
||||
if(shape1 == 0 && shape2 == 1)
|
||||
if(!edgeCornerOcclusionTest(this, mpart))
|
||||
return false
|
||||
|
||||
if(shape1 == 1 && shape2 == 0)
|
||||
if(!edgeCornerOcclusionTest(mpart, this))
|
||||
return false
|
||||
|
||||
if(shape1 == 0 && shape2 == 0)
|
||||
{
|
||||
val e1 = getSlot-15
|
||||
val e2 = mpart.getSlot-15
|
||||
if((e1&0xC) == (e2&0xC) && ((e1&3)^(e2&3)) == 3)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (mpart.getMaterial != getMaterial) {
|
||||
if (shape1 == 1 && shape2 == 1) {
|
||||
val axisMask = (getSlot - 7) ^ (mpart.getSlot - 7)
|
||||
if (axisMask == 3 || axisMask == 5 || axisMask == 6)
|
||||
return false
|
||||
}
|
||||
|
||||
if (shape1 == 0 && shape2 == 1)
|
||||
if (!edgeCornerOcclusionTest(this, mpart))
|
||||
return false
|
||||
|
||||
if (shape1 == 1 && shape2 == 0)
|
||||
if (!edgeCornerOcclusionTest(mpart, this))
|
||||
return false
|
||||
|
||||
if (shape1 == 0 && shape2 == 0) {
|
||||
val e1 = getSlot - 15
|
||||
val e2 = mpart.getSlot - 15
|
||||
if ((e1 & 0xc) == (e2 & 0xc) && ((e1 & 3) ^ (e2 & 3)) == 3)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
def edgeCornerOcclusionTest(edge:TMicroOcclusion, corner:TMicroOcclusion):Boolean =
|
||||
{
|
||||
((corner.getSlot-7)&edgeAxisMask(edge.getSlot-15)) == unpackEdgeBits(edge.getSlot-15)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
def edgeCornerOcclusionTest(
|
||||
edge: TMicroOcclusion,
|
||||
corner: TMicroOcclusion
|
||||
): Boolean = {
|
||||
((corner.getSlot - 7) & edgeAxisMask(edge.getSlot - 15)) == unpackEdgeBits(
|
||||
edge.getSlot - 15
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -13,28 +13,32 @@ import cpw.mods.fml.common.Loader
|
||||
import cpw.mods.fml.common.LoaderState
|
||||
import net.minecraft.util.MovingObjectPosition.MovingObjectType
|
||||
|
||||
object MicroblockEventHandler
|
||||
{
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
def postTextureStitch(event:TextureStitchEvent.Post)
|
||||
{
|
||||
if(event.map.getTextureType == 0)
|
||||
MicroMaterialRegistry.loadIcons()
|
||||
}
|
||||
object MicroblockEventHandler {
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
def postTextureStitch(event: TextureStitchEvent.Post) {
|
||||
if (event.map.getTextureType == 0)
|
||||
MicroMaterialRegistry.loadIcons()
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
def drawBlockHighlight(event:DrawBlockHighlightEvent)
|
||||
{
|
||||
if(event.currentItem != null && event.currentItem.getItem == MicroblockProxy.itemMicro &&
|
||||
event.target != null && event.target.typeOfHit == MovingObjectType.BLOCK)
|
||||
{
|
||||
GL11.glPushMatrix()
|
||||
RenderUtils.translateToWorldCoords(event.player, event.partialTicks)
|
||||
if(ItemMicroPartRenderer.renderHighlight(event.player, event.currentItem, event.target))
|
||||
event.setCanceled(true)
|
||||
GL11.glPopMatrix()
|
||||
}
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
def drawBlockHighlight(event: DrawBlockHighlightEvent) {
|
||||
if (
|
||||
event.currentItem != null && event.currentItem.getItem == MicroblockProxy.itemMicro &&
|
||||
event.target != null && event.target.typeOfHit == MovingObjectType.BLOCK
|
||||
) {
|
||||
GL11.glPushMatrix()
|
||||
RenderUtils.translateToWorldCoords(event.player, event.partialTicks)
|
||||
if (
|
||||
ItemMicroPartRenderer.renderHighlight(
|
||||
event.player,
|
||||
event.currentItem,
|
||||
event.target
|
||||
)
|
||||
)
|
||||
event.setCanceled(true)
|
||||
GL11.glPopMatrix()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,35 +12,39 @@ import codechicken.microblock.ConfigContent
|
||||
import cpw.mods.fml.common.event.FMLInterModComms.IMCEvent
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
@Mod(modid = "ForgeMicroblock", acceptedMinecraftVersions = "[1.7.10]", dependencies = "required-after:ForgeMultipart", modLanguage = "scala")
|
||||
object MicroblockMod
|
||||
{
|
||||
@EventHandler
|
||||
def preInit(event: FMLPreInitializationEvent) {
|
||||
MicroblockProxy.preInit(event.getModLog)
|
||||
DefaultContent.load()
|
||||
ConfigContent.parse(event.getModConfigurationDirectory)
|
||||
}
|
||||
@Mod(
|
||||
modid = "ForgeMicroblock",
|
||||
acceptedMinecraftVersions = "[1.7.10]",
|
||||
dependencies = "required-after:ForgeMultipart",
|
||||
modLanguage = "scala"
|
||||
)
|
||||
object MicroblockMod {
|
||||
@EventHandler
|
||||
def preInit(event: FMLPreInitializationEvent) {
|
||||
MicroblockProxy.preInit(event.getModLog)
|
||||
DefaultContent.load()
|
||||
ConfigContent.parse(event.getModConfigurationDirectory)
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
def init(event: FMLInitializationEvent) {
|
||||
MicroblockProxy.init()
|
||||
ConfigContent.load()
|
||||
}
|
||||
@EventHandler
|
||||
def init(event: FMLInitializationEvent) {
|
||||
MicroblockProxy.init()
|
||||
ConfigContent.load()
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
def postInit(event: FMLPostInitializationEvent) {
|
||||
MicroMaterialRegistry.setupIDMap()
|
||||
MicroblockProxy.postInit()
|
||||
}
|
||||
@EventHandler
|
||||
def postInit(event: FMLPostInitializationEvent) {
|
||||
MicroMaterialRegistry.setupIDMap()
|
||||
MicroblockProxy.postInit()
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
def beforeServerStart(event: FMLServerAboutToStartEvent) {
|
||||
MicroMaterialRegistry.setupIDMap()
|
||||
}
|
||||
@EventHandler
|
||||
def beforeServerStart(event: FMLServerAboutToStartEvent) {
|
||||
MicroMaterialRegistry.setupIDMap()
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
def handleIMC(event: IMCEvent) {
|
||||
ConfigContent.handleIMC(event.getMessages)
|
||||
}
|
||||
@EventHandler
|
||||
def handleIMC(event: IMCEvent) {
|
||||
ConfigContent.handleIMC(event.getMessages)
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
package codechicken.microblock.handler
|
||||
|
||||
import codechicken.lib.packet.PacketCustom.{IHandshakeHandler, IClientPacketHandler, IServerPacketHandler}
|
||||
import codechicken.lib.packet.PacketCustom.{
|
||||
IHandshakeHandler,
|
||||
IClientPacketHandler,
|
||||
IServerPacketHandler
|
||||
}
|
||||
import codechicken.lib.packet.PacketCustom
|
||||
import net.minecraft.client.Minecraft
|
||||
import codechicken.microblock.MicroMaterialRegistry
|
||||
@@ -10,33 +14,52 @@ import net.minecraft.network.play.server.S40PacketDisconnect
|
||||
import net.minecraft.util.ChatComponentTranslation
|
||||
import net.minecraft.network.NetHandlerPlayServer
|
||||
|
||||
class MicroblockPH
|
||||
{
|
||||
val registryChannel = "ForgeMicroblock" //Must use the 250 system for ID registry as the NetworkMod idMap hasn't been properly initialized from the server yet.
|
||||
class MicroblockPH {
|
||||
val registryChannel =
|
||||
"ForgeMicroblock" // Must use the 250 system for ID registry as the NetworkMod idMap hasn't been properly initialized from the server yet.
|
||||
}
|
||||
|
||||
object MicroblockCPH extends MicroblockPH with IClientPacketHandler
|
||||
{
|
||||
def handlePacket(packet: PacketCustom, mc: Minecraft, netHandler:INetHandlerPlayClient) {
|
||||
packet.getType match {
|
||||
case 1 => handleMaterialRegistration(packet, netHandler)
|
||||
}
|
||||
object MicroblockCPH extends MicroblockPH with IClientPacketHandler {
|
||||
def handlePacket(
|
||||
packet: PacketCustom,
|
||||
mc: Minecraft,
|
||||
netHandler: INetHandlerPlayClient
|
||||
) {
|
||||
packet.getType match {
|
||||
case 1 => handleMaterialRegistration(packet, netHandler)
|
||||
}
|
||||
}
|
||||
|
||||
def handleMaterialRegistration(packet: PacketCustom, netHandler: INetHandlerPlayClient) {
|
||||
val missing = MicroMaterialRegistry.readIDMap(packet)
|
||||
if (!missing.isEmpty)
|
||||
netHandler.handleDisconnect(new S40PacketDisconnect(new ChatComponentTranslation("microblock.missing", missing.mkString(", "))))
|
||||
}
|
||||
def handleMaterialRegistration(
|
||||
packet: PacketCustom,
|
||||
netHandler: INetHandlerPlayClient
|
||||
) {
|
||||
val missing = MicroMaterialRegistry.readIDMap(packet)
|
||||
if (!missing.isEmpty)
|
||||
netHandler.handleDisconnect(
|
||||
new S40PacketDisconnect(
|
||||
new ChatComponentTranslation(
|
||||
"microblock.missing",
|
||||
missing.mkString(", ")
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
object MicroblockSPH extends MicroblockPH with IServerPacketHandler with IHandshakeHandler
|
||||
{
|
||||
def handlePacket(packet: PacketCustom, sender: EntityPlayerMP, netHandler:INetHandlerPlayServer) {}
|
||||
object MicroblockSPH
|
||||
extends MicroblockPH
|
||||
with IServerPacketHandler
|
||||
with IHandshakeHandler {
|
||||
def handlePacket(
|
||||
packet: PacketCustom,
|
||||
sender: EntityPlayerMP,
|
||||
netHandler: INetHandlerPlayServer
|
||||
) {}
|
||||
|
||||
def handshakeRecieved(netHandler: NetHandlerPlayServer) {
|
||||
val packet = new PacketCustom(registryChannel, 1)
|
||||
MicroMaterialRegistry.writeIDMap(packet)
|
||||
netHandler.sendPacket(packet.toPacket)
|
||||
}
|
||||
}
|
||||
def handshakeRecieved(netHandler: NetHandlerPlayServer) {
|
||||
val packet = new PacketCustom(registryChannel, 1)
|
||||
MicroMaterialRegistry.writeIDMap(packet)
|
||||
netHandler.sendPacket(packet.toPacket)
|
||||
}
|
||||
}
|
||||
@@ -24,89 +24,112 @@ import org.apache.logging.log4j.Logger
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
class MicroblockProxy_serverImpl
|
||||
{
|
||||
var logger: Logger = _
|
||||
class MicroblockProxy_serverImpl {
|
||||
var logger: Logger = _
|
||||
|
||||
var itemMicro: ItemMicroPart = _
|
||||
var sawStone: Item = _
|
||||
var sawIron: Item = _
|
||||
var sawDiamond: Item = _
|
||||
var stoneRod: Item = _
|
||||
var itemMicro: ItemMicroPart = _
|
||||
var sawStone: Item = _
|
||||
var sawIron: Item = _
|
||||
var sawDiamond: Item = _
|
||||
var stoneRod: Item = _
|
||||
|
||||
var useSawIcons: Boolean = _
|
||||
var useSawIcons: Boolean = _
|
||||
|
||||
def preInit(logger: Logger) {
|
||||
this.logger = logger
|
||||
itemMicro = new ItemMicroPart
|
||||
GameRegistry.registerItem(itemMicro, "microblock")
|
||||
sawStone = createSaw(config, "sawStone", 1)
|
||||
sawIron = createSaw(config, "sawIron", 2)
|
||||
sawDiamond = createSaw(config, "sawDiamond", 3)
|
||||
stoneRod = new Item().setUnlocalizedName("microblock:stoneRod").setTextureName("microblock:stoneRod")
|
||||
GameRegistry.registerItem(stoneRod, "stoneRod")
|
||||
def preInit(logger: Logger) {
|
||||
this.logger = logger
|
||||
itemMicro = new ItemMicroPart
|
||||
GameRegistry.registerItem(itemMicro, "microblock")
|
||||
sawStone = createSaw(config, "sawStone", 1)
|
||||
sawIron = createSaw(config, "sawIron", 2)
|
||||
sawDiamond = createSaw(config, "sawDiamond", 3)
|
||||
stoneRod = new Item()
|
||||
.setUnlocalizedName("microblock:stoneRod")
|
||||
.setTextureName("microblock:stoneRod")
|
||||
GameRegistry.registerItem(stoneRod, "stoneRod")
|
||||
|
||||
OreDictionary.registerOre("rodStone", stoneRod)
|
||||
MinecraftForge.EVENT_BUS.register(MicroblockEventHandler)
|
||||
OreDictionary.registerOre("rodStone", stoneRod)
|
||||
MinecraftForge.EVENT_BUS.register(MicroblockEventHandler)
|
||||
|
||||
useSawIcons = config.getTag("useSawIcons").setComment("Set to true to use mc style icons for the saw instead of the 3D model").getBooleanValue(false)
|
||||
useSawIcons = config
|
||||
.getTag("useSawIcons")
|
||||
.setComment(
|
||||
"Set to true to use mc style icons for the saw instead of the 3D model"
|
||||
)
|
||||
.getBooleanValue(false)
|
||||
}
|
||||
|
||||
protected var saws = mutable.MutableList[Item]()
|
||||
def createSaw(config: ConfigFile, name: String, strength: Int) = {
|
||||
val saw = new ItemSaw(config.getTag(name).useBraces(), strength)
|
||||
.setUnlocalizedName("microblock:" + name)
|
||||
.setTextureName("microblock:" + name)
|
||||
GameRegistry.registerItem(saw, name)
|
||||
saws += saw
|
||||
saw
|
||||
}
|
||||
|
||||
def addSawRecipe(saw: Item, blade: Item) {
|
||||
CraftingManager.getInstance.getRecipeList
|
||||
.asInstanceOf[JList[IRecipe]]
|
||||
.add(
|
||||
new ShapedOreRecipe(
|
||||
new ItemStack(saw),
|
||||
"srr",
|
||||
"sbr",
|
||||
's': Character,
|
||||
"stickWood",
|
||||
'r': Character,
|
||||
"rodStone",
|
||||
'b': Character,
|
||||
blade
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def init() {
|
||||
CraftingManager.getInstance.getRecipeList
|
||||
.asInstanceOf[JList[IRecipe]]
|
||||
.add(MicroRecipe)
|
||||
if (!Loader.isModLoaded("dreamcraft")) {
|
||||
CraftingManager.getInstance.addRecipe(
|
||||
new ItemStack(stoneRod, 4),
|
||||
"s",
|
||||
"s",
|
||||
's': Character,
|
||||
Blocks.stone
|
||||
)
|
||||
addSawRecipe(sawStone, Items.flint)
|
||||
addSawRecipe(sawIron, Items.iron_ingot)
|
||||
addSawRecipe(sawDiamond, Items.diamond)
|
||||
}
|
||||
}
|
||||
|
||||
protected var saws = mutable.MutableList[Item]()
|
||||
def createSaw(config: ConfigFile, name: String, strength: Int) = {
|
||||
val saw = new ItemSaw(config.getTag(name).useBraces(), strength)
|
||||
.setUnlocalizedName("microblock:" + name).setTextureName("microblock:" + name)
|
||||
GameRegistry.registerItem(saw, name)
|
||||
saws+=saw
|
||||
saw
|
||||
}
|
||||
|
||||
def addSawRecipe(saw: Item, blade: Item) {
|
||||
CraftingManager.getInstance.getRecipeList.asInstanceOf[JList[IRecipe]].add(
|
||||
new ShapedOreRecipe(new ItemStack(saw),
|
||||
"srr",
|
||||
"sbr",
|
||||
's': Character, "stickWood",
|
||||
'r': Character, "rodStone",
|
||||
'b': Character, blade))
|
||||
}
|
||||
|
||||
def init() {
|
||||
CraftingManager.getInstance.getRecipeList.asInstanceOf[JList[IRecipe]].add(MicroRecipe)
|
||||
if (!Loader.isModLoaded("dreamcraft")) {
|
||||
CraftingManager.getInstance.addRecipe(new ItemStack(stoneRod, 4), "s", "s", 's': Character, Blocks.stone)
|
||||
addSawRecipe(sawStone, Items.flint)
|
||||
addSawRecipe(sawIron, Items.iron_ingot)
|
||||
addSawRecipe(sawDiamond, Items.diamond)
|
||||
}
|
||||
}
|
||||
|
||||
def postInit() {
|
||||
MicroMaterialRegistry.calcMaxCuttingStrength()
|
||||
PacketCustom.assignHandshakeHandler(MicroblockSPH.registryChannel, MicroblockSPH)
|
||||
}
|
||||
def postInit() {
|
||||
MicroMaterialRegistry.calcMaxCuttingStrength()
|
||||
PacketCustom.assignHandshakeHandler(
|
||||
MicroblockSPH.registryChannel,
|
||||
MicroblockSPH
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class MicroblockProxy_clientImpl extends MicroblockProxy_serverImpl
|
||||
{
|
||||
@SideOnly(Side.CLIENT)
|
||||
lazy val renderBlocks = new RenderBlocks
|
||||
class MicroblockProxy_clientImpl extends MicroblockProxy_serverImpl {
|
||||
@SideOnly(Side.CLIENT)
|
||||
lazy val renderBlocks = new RenderBlocks
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def postInit() {
|
||||
super.postInit()
|
||||
MicroMaterialRegistry.loadIcons()
|
||||
MinecraftForgeClient.registerItemRenderer(itemMicro, ItemMicroPartRenderer)
|
||||
PacketCustom.assignHandler(MicroblockCPH.registryChannel, MicroblockCPH)
|
||||
}
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def postInit() {
|
||||
super.postInit()
|
||||
MicroMaterialRegistry.loadIcons()
|
||||
MinecraftForgeClient.registerItemRenderer(itemMicro, ItemMicroPartRenderer)
|
||||
PacketCustom.assignHandler(MicroblockCPH.registryChannel, MicroblockCPH)
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def init() {
|
||||
super.init()
|
||||
saws.foreach(MinecraftForgeClient.registerItemRenderer(_, ItemSawRenderer))
|
||||
}
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def init() {
|
||||
super.init()
|
||||
saws.foreach(MinecraftForgeClient.registerItemRenderer(_, ItemSawRenderer))
|
||||
}
|
||||
}
|
||||
|
||||
object MicroblockProxy extends MicroblockProxy_clientImpl
|
||||
{
|
||||
}
|
||||
object MicroblockProxy extends MicroblockProxy_clientImpl {}
|
||||
@@ -2,7 +2,6 @@ package codechicken
|
||||
|
||||
import codechicken.microblock.handler.MicroblockProxy
|
||||
|
||||
package object microblock
|
||||
{
|
||||
def logger = MicroblockProxy.logger
|
||||
package object microblock {
|
||||
def logger = MicroblockProxy.logger
|
||||
}
|
||||
@@ -23,282 +23,429 @@ import net.minecraft.world.IBlockAccess
|
||||
import codechicken.lib.raytracer.ExtendedMOP
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
object BlockMultipart
|
||||
{
|
||||
def getTile(world:IBlockAccess, x:Int, y:Int, z:Int) = world.getTileEntity(x, y, z) match {
|
||||
case t:TileMultipart if !t.partList.isEmpty => t
|
||||
case _ => null
|
||||
object BlockMultipart {
|
||||
def getTile(world: IBlockAccess, x: Int, y: Int, z: Int) =
|
||||
world.getTileEntity(x, y, z) match {
|
||||
case t: TileMultipart if !t.partList.isEmpty => t
|
||||
case _ => null
|
||||
}
|
||||
|
||||
def getClientTile(world:IBlockAccess, x:Int, y:Int, z:Int) = world.getTileEntity(x, y, z) match {
|
||||
case t:TileMultipartClient if !t.partList.isEmpty => t
|
||||
case _ => null
|
||||
def getClientTile(world: IBlockAccess, x: Int, y: Int, z: Int) =
|
||||
world.getTileEntity(x, y, z) match {
|
||||
case t: TileMultipartClient if !t.partList.isEmpty => t
|
||||
case _ => null
|
||||
}
|
||||
|
||||
def reduceMOP(hit:MovingObjectPosition):(Int, ExtendedMOP) = {
|
||||
val ehit = hit.asInstanceOf[ExtendedMOP]
|
||||
val data:(Int, _) = ExtendedMOP.getData(hit)
|
||||
return (data._1, new ExtendedMOP(ehit, data._2, ehit.dist))
|
||||
}
|
||||
|
||||
def drawHighlight(world:World, player:EntityPlayer, hit:MovingObjectPosition, frame:Float):Boolean =
|
||||
{
|
||||
val tile = getTile(world, hit.blockX, hit.blockY, hit.blockZ)
|
||||
if(tile == null)
|
||||
return false
|
||||
|
||||
val (index, mop) = reduceMOP(hit)
|
||||
if(tile.partList(index).drawHighlight(mop, player, frame))
|
||||
return true
|
||||
|
||||
tile.partList(index).collisionRayTrace(RayTracer.getStartVec(player), RayTracer.getEndVec(player))
|
||||
return false
|
||||
}
|
||||
def reduceMOP(hit: MovingObjectPosition): (Int, ExtendedMOP) = {
|
||||
val ehit = hit.asInstanceOf[ExtendedMOP]
|
||||
val data: (Int, _) = ExtendedMOP.getData(hit)
|
||||
return (data._1, new ExtendedMOP(ehit, data._2, ehit.dist))
|
||||
}
|
||||
|
||||
def drawHighlight(
|
||||
world: World,
|
||||
player: EntityPlayer,
|
||||
hit: MovingObjectPosition,
|
||||
frame: Float
|
||||
): Boolean = {
|
||||
val tile = getTile(world, hit.blockX, hit.blockY, hit.blockZ)
|
||||
if (tile == null)
|
||||
return false
|
||||
|
||||
val (index, mop) = reduceMOP(hit)
|
||||
if (tile.partList(index).drawHighlight(mop, player, frame))
|
||||
return true
|
||||
|
||||
tile
|
||||
.partList(index)
|
||||
.collisionRayTrace(
|
||||
RayTracer.getStartVec(player),
|
||||
RayTracer.getEndVec(player)
|
||||
)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Block class for all multiparts, should be internal use only.
|
||||
*/
|
||||
class BlockMultipart extends Block(Material.rock)
|
||||
{
|
||||
import BlockMultipart._
|
||||
|
||||
override def hasTileEntity(meta:Int = 0) = true
|
||||
|
||||
override def isBlockSolid(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int):Boolean =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => false
|
||||
case tile => tile.isSolid(side)
|
||||
}
|
||||
|
||||
override def isSideSolid(world:IBlockAccess, x:Int, y:Int, z:Int, side:ForgeDirection):Boolean =
|
||||
isBlockSolid(world, x, y, z, side.ordinal())
|
||||
/** Block class for all multiparts, should be internal use only.
|
||||
*/
|
||||
class BlockMultipart extends Block(Material.rock) {
|
||||
import BlockMultipart._
|
||||
|
||||
override def canPlaceTorchOnTop(world:World, x:Int, y:Int, z:Int) =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => false
|
||||
case tile => tile.canPlaceTorchOnTop
|
||||
}
|
||||
override def hasTileEntity(meta: Int = 0) = true
|
||||
|
||||
override def onNeighborBlockChange(world:World, x:Int, y:Int, z:Int, block:Block) {
|
||||
val tile = getTile(world, x, y, z)
|
||||
if(tile != null)
|
||||
tile.onNeighborBlockChange()
|
||||
override def isBlockSolid(
|
||||
world: IBlockAccess,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
side: Int
|
||||
): Boolean =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => false
|
||||
case tile => tile.isSolid(side)
|
||||
}
|
||||
|
||||
override def collisionRayTrace(world:World, x:Int, y:Int, z:Int, start:Vec3, end:Vec3):ExtendedMOP =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => null
|
||||
case tile => tile.collisionRayTrace(start, end)
|
||||
}
|
||||
|
||||
def rayTraceAll(world:World, x:Int, y:Int, z:Int, start:Vec3, end:Vec3):Iterable[ExtendedMOP] =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => Seq()
|
||||
case tile => tile.rayTraceAll(start, end)
|
||||
}
|
||||
override def isSideSolid(
|
||||
world: IBlockAccess,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
side: ForgeDirection
|
||||
): Boolean =
|
||||
isBlockSolid(world, x, y, z, side.ordinal())
|
||||
|
||||
override def removedByPlayer(world:World, player:EntityPlayer, x:Int, y:Int, z:Int):Boolean = {
|
||||
val hit = RayTracer.retraceBlock(world, player, x, y, z)
|
||||
val tile = getTile(world, x, y, z)
|
||||
|
||||
if(hit == null || tile == null)
|
||||
{
|
||||
dropAndDestroy(world, x, y, z)
|
||||
return true
|
||||
}
|
||||
|
||||
val (index, mop) = reduceMOP(hit)
|
||||
if(world.isRemote)
|
||||
{
|
||||
tile.partList(index).addDestroyEffects(mop, Minecraft.getMinecraft.effectRenderer)
|
||||
return true
|
||||
}
|
||||
|
||||
tile.harvestPart(index, mop, player)
|
||||
return world.getTileEntity(x, y, z) == null
|
||||
override def canPlaceTorchOnTop(world: World, x: Int, y: Int, z: Int) =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => false
|
||||
case tile => tile.canPlaceTorchOnTop
|
||||
}
|
||||
|
||||
def dropAndDestroy(world:World, x:Int, y:Int, z:Int) {
|
||||
val tile = getTile(world, x, y, z)
|
||||
if(tile != null && !world.isRemote)
|
||||
tile.dropItems(getDrops(world, x, y, z, 0, 0))
|
||||
|
||||
world.setBlockToAir(x, y, z)
|
||||
}
|
||||
|
||||
override def quantityDropped(meta:Int, fortune:Int, random:Random) = 0
|
||||
|
||||
override def getDrops(world:World, x:Int, y:Int, z:Int, meta:Int, fortune:Int):ArrayList[ItemStack] = {
|
||||
val ai = new ArrayList[ItemStack]()
|
||||
if(world.isRemote)
|
||||
return ai
|
||||
|
||||
val tile = getTile(world, x, y, z)
|
||||
if(tile != null)
|
||||
tile.partList.foreach(part => part.getDrops.foreach(item => ai.add(item)))
|
||||
|
||||
return ai
|
||||
}
|
||||
|
||||
override def addCollisionBoxesToList(world:World, x:Int, y:Int, z:Int, ebb:AxisAlignedBB, list$:List[_], entity:Entity) {
|
||||
val list = list$.asInstanceOf[List[AxisAlignedBB]]
|
||||
val tile = getTile(world, x, y, z)
|
||||
if(tile != null)
|
||||
tile.partList.foreach(part =>
|
||||
part.getCollisionBoxes.foreach{c =>
|
||||
val aabb = c.toAABB.offset(x, y, z)
|
||||
if(aabb.intersectsWith(ebb))
|
||||
list.add(aabb)
|
||||
})
|
||||
}
|
||||
|
||||
override def addHitEffects(world:World, hit:MovingObjectPosition, effectRenderer:EffectRenderer):Boolean = {
|
||||
val tile = getClientTile(world, hit.blockX, hit.blockY, hit.blockZ)
|
||||
if(tile != null) {
|
||||
val (index, mop) = reduceMOP(hit)
|
||||
if(index < tile.partList.size)
|
||||
tile.partList(index).addHitEffects(mop, effectRenderer)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override def addDestroyEffects(world:World, x:Int, y:Int, z:Int, s:Int, effectRenderer:EffectRenderer) = true
|
||||
|
||||
override def renderAsNormalBlock() = false
|
||||
|
||||
override def isOpaqueCube = false
|
||||
|
||||
override def getRenderType = TileMultipart.renderID
|
||||
|
||||
override def isAir(world:IBlockAccess, x:Int, y:Int, z:Int):Boolean =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => true
|
||||
case tile => tile.partList.isEmpty
|
||||
}
|
||||
|
||||
override def isReplaceable(world:IBlockAccess, x:Int, y:Int, z:Int) = isAir(world, x, y, z)
|
||||
|
||||
override def getRenderBlockPass = 1
|
||||
|
||||
override def canRenderInPass(pass:Int):Boolean = {
|
||||
MultipartRenderer.pass = pass
|
||||
return true
|
||||
override def onNeighborBlockChange(
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
block: Block
|
||||
) {
|
||||
val tile = getTile(world, x, y, z)
|
||||
if (tile != null)
|
||||
tile.onNeighborBlockChange()
|
||||
}
|
||||
|
||||
override def collisionRayTrace(
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
start: Vec3,
|
||||
end: Vec3
|
||||
): ExtendedMOP =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => null
|
||||
case tile => tile.collisionRayTrace(start, end)
|
||||
}
|
||||
|
||||
override def getPickBlock(hit:MovingObjectPosition, world:World, x:Int, y:Int, z:Int):ItemStack = {
|
||||
val tile = getTile(world, x, y, z)
|
||||
if(tile != null)
|
||||
{
|
||||
if (!hit.isInstanceOf[ExtendedMOP]) {
|
||||
return null
|
||||
}
|
||||
val (index, mop) = reduceMOP(hit)
|
||||
return tile.partList(index).pickItem(mop)
|
||||
|
||||
def rayTraceAll(
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
start: Vec3,
|
||||
end: Vec3
|
||||
): Iterable[ExtendedMOP] =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => Seq()
|
||||
case tile => tile.rayTraceAll(start, end)
|
||||
}
|
||||
|
||||
override def removedByPlayer(
|
||||
world: World,
|
||||
player: EntityPlayer,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int
|
||||
): Boolean = {
|
||||
val hit = RayTracer.retraceBlock(world, player, x, y, z)
|
||||
val tile = getTile(world, x, y, z)
|
||||
|
||||
if (hit == null || tile == null) {
|
||||
dropAndDestroy(world, x, y, z)
|
||||
return true
|
||||
}
|
||||
|
||||
val (index, mop) = reduceMOP(hit)
|
||||
if (world.isRemote) {
|
||||
tile
|
||||
.partList(index)
|
||||
.addDestroyEffects(mop, Minecraft.getMinecraft.effectRenderer)
|
||||
return true
|
||||
}
|
||||
|
||||
tile.harvestPart(index, mop, player)
|
||||
return world.getTileEntity(x, y, z) == null
|
||||
}
|
||||
|
||||
def dropAndDestroy(world: World, x: Int, y: Int, z: Int) {
|
||||
val tile = getTile(world, x, y, z)
|
||||
if (tile != null && !world.isRemote)
|
||||
tile.dropItems(getDrops(world, x, y, z, 0, 0))
|
||||
|
||||
world.setBlockToAir(x, y, z)
|
||||
}
|
||||
|
||||
override def quantityDropped(meta: Int, fortune: Int, random: Random) = 0
|
||||
|
||||
override def getDrops(
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
meta: Int,
|
||||
fortune: Int
|
||||
): ArrayList[ItemStack] = {
|
||||
val ai = new ArrayList[ItemStack]()
|
||||
if (world.isRemote)
|
||||
return ai
|
||||
|
||||
val tile = getTile(world, x, y, z)
|
||||
if (tile != null)
|
||||
tile.partList.foreach(part => part.getDrops.foreach(item => ai.add(item)))
|
||||
|
||||
return ai
|
||||
}
|
||||
|
||||
override def addCollisionBoxesToList(
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
ebb: AxisAlignedBB,
|
||||
list$ : List[_],
|
||||
entity: Entity
|
||||
) {
|
||||
val list = list$.asInstanceOf[List[AxisAlignedBB]]
|
||||
val tile = getTile(world, x, y, z)
|
||||
if (tile != null)
|
||||
tile.partList.foreach(part =>
|
||||
part.getCollisionBoxes.foreach { c =>
|
||||
val aabb = c.toAABB.offset(x, y, z)
|
||||
if (aabb.intersectsWith(ebb))
|
||||
list.add(aabb)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override def addHitEffects(
|
||||
world: World,
|
||||
hit: MovingObjectPosition,
|
||||
effectRenderer: EffectRenderer
|
||||
): Boolean = {
|
||||
val tile = getClientTile(world, hit.blockX, hit.blockY, hit.blockZ)
|
||||
if (tile != null) {
|
||||
val (index, mop) = reduceMOP(hit)
|
||||
if (index < tile.partList.size)
|
||||
tile.partList(index).addHitEffects(mop, effectRenderer)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override def addDestroyEffects(
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
s: Int,
|
||||
effectRenderer: EffectRenderer
|
||||
) = true
|
||||
|
||||
override def renderAsNormalBlock() = false
|
||||
|
||||
override def isOpaqueCube = false
|
||||
|
||||
override def getRenderType = TileMultipart.renderID
|
||||
|
||||
override def isAir(world: IBlockAccess, x: Int, y: Int, z: Int): Boolean =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => true
|
||||
case tile => tile.partList.isEmpty
|
||||
}
|
||||
|
||||
override def isReplaceable(world: IBlockAccess, x: Int, y: Int, z: Int) =
|
||||
isAir(world, x, y, z)
|
||||
|
||||
override def getRenderBlockPass = 1
|
||||
|
||||
override def canRenderInPass(pass: Int): Boolean = {
|
||||
MultipartRenderer.pass = pass
|
||||
return true
|
||||
}
|
||||
|
||||
override def getPickBlock(
|
||||
hit: MovingObjectPosition,
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int
|
||||
): ItemStack = {
|
||||
val tile = getTile(world, x, y, z)
|
||||
if (tile != null) {
|
||||
if (!hit.isInstanceOf[ExtendedMOP]) {
|
||||
return null
|
||||
}
|
||||
val (index, mop) = reduceMOP(hit)
|
||||
return tile.partList(index).pickItem(mop)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override def getPlayerRelativeBlockHardness(
|
||||
player: EntityPlayer,
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int
|
||||
): Float = {
|
||||
val hit = RayTracer.retraceBlock(world, player, x, y, z)
|
||||
val tile = getTile(world, x, y, z)
|
||||
if (hit != null && tile != null) {
|
||||
val (index, mop) = reduceMOP(hit)
|
||||
return tile.partList(index).getStrength(mop, player) / 30f
|
||||
}
|
||||
|
||||
override def getPlayerRelativeBlockHardness(player:EntityPlayer, world:World, x:Int, y:Int, z:Int):Float = {
|
||||
val hit = RayTracer.retraceBlock(world, player, x, y, z)
|
||||
val tile = getTile(world, x, y, z)
|
||||
if(hit != null && tile != null) {
|
||||
val (index, mop) = reduceMOP(hit)
|
||||
return tile.partList(index).getStrength(mop, player)/30F
|
||||
}
|
||||
|
||||
return 1/100F
|
||||
}
|
||||
|
||||
/**
|
||||
* Kludge to set PROTECTED blockIcon to a blank icon
|
||||
*/
|
||||
override def registerBlockIcons(register:IIconRegister) {
|
||||
val icon = TextureUtils.getBlankIcon(16, register)
|
||||
setBlockTextureName(icon.getIconName)
|
||||
super.registerBlockIcons(register)
|
||||
}
|
||||
|
||||
override def getLightValue(world:IBlockAccess, x:Int, y:Int, z:Int):Int =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => 0
|
||||
case tile => tile.getLightValue
|
||||
}
|
||||
return 1 / 100f
|
||||
}
|
||||
|
||||
override def randomDisplayTick(world:World, x:Int, y:Int, z:Int, random:Random) {
|
||||
val tile = getClientTile(world, x, y, z)
|
||||
if(tile != null)
|
||||
tile.randomDisplayTick(random)
|
||||
}
|
||||
|
||||
override def onBlockActivated(world:World, x:Int, y:Int, z:Int, player:EntityPlayer, side:Int, hitX:Float, hitY:Float, hitZ:Float):Boolean = {
|
||||
val hit = RayTracer.retraceBlock(world, player, x, y, z)
|
||||
if(hit == null)
|
||||
return false
|
||||
|
||||
val tile = getTile(world, x, y, z)
|
||||
if(tile == null)
|
||||
return false
|
||||
|
||||
val (index, mop) = reduceMOP(hit)
|
||||
return tile.partList(index).activate(player, mop, player.getHeldItem)
|
||||
}
|
||||
|
||||
override def onBlockClicked(world:World, x:Int, y:Int, z:Int, player:EntityPlayer) {
|
||||
val hit = RayTracer.retraceBlock(world, player, x, y, z)
|
||||
if(hit == null)
|
||||
return
|
||||
|
||||
val tile = getTile(world, x, y, z)
|
||||
if(tile == null)
|
||||
return
|
||||
|
||||
val (index, mop) = reduceMOP(hit)
|
||||
tile.partList(index).click(player, mop, player.getHeldItem)
|
||||
}
|
||||
|
||||
override def isProvidingStrongPower(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int):Int =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => 0
|
||||
case tile => tile.strongPowerLevel(side^1)
|
||||
}
|
||||
/** Kludge to set PROTECTED blockIcon to a blank icon
|
||||
*/
|
||||
override def registerBlockIcons(register: IIconRegister) {
|
||||
val icon = TextureUtils.getBlankIcon(16, register)
|
||||
setBlockTextureName(icon.getIconName)
|
||||
super.registerBlockIcons(register)
|
||||
}
|
||||
|
||||
override def isProvidingWeakPower(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int):Int =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => 0
|
||||
case tile => tile.weakPowerLevel(side^1)
|
||||
}
|
||||
|
||||
override def canConnectRedstone(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int):Boolean =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => false
|
||||
case tile => tile.canConnectRedstone(side)
|
||||
}
|
||||
|
||||
override def onEntityCollidedWithBlock(world:World, x:Int, y:Int, z:Int, entity:Entity) {
|
||||
val tile = getTile(world, x, y, z)
|
||||
if(tile != null)
|
||||
tile.onEntityCollision(entity)
|
||||
override def getLightValue(world: IBlockAccess, x: Int, y: Int, z: Int): Int =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => 0
|
||||
case tile => tile.getLightValue
|
||||
}
|
||||
|
||||
override def onNeighborChange(world:IBlockAccess, x:Int, y:Int, z:Int, tileX:Int, tileY:Int, tileZ:Int) {
|
||||
val tile = getTile(world, x, y, z)
|
||||
if(tile != null)
|
||||
tile.onNeighborTileChange(tileX, tileY, tileZ)
|
||||
override def randomDisplayTick(
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
random: Random
|
||||
) {
|
||||
val tile = getClientTile(world, x, y, z)
|
||||
if (tile != null)
|
||||
tile.randomDisplayTick(random)
|
||||
}
|
||||
|
||||
override def onBlockActivated(
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
player: EntityPlayer,
|
||||
side: Int,
|
||||
hitX: Float,
|
||||
hitY: Float,
|
||||
hitZ: Float
|
||||
): Boolean = {
|
||||
val hit = RayTracer.retraceBlock(world, player, x, y, z)
|
||||
if (hit == null)
|
||||
return false
|
||||
|
||||
val tile = getTile(world, x, y, z)
|
||||
if (tile == null)
|
||||
return false
|
||||
|
||||
val (index, mop) = reduceMOP(hit)
|
||||
return tile.partList(index).activate(player, mop, player.getHeldItem)
|
||||
}
|
||||
|
||||
override def onBlockClicked(
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
player: EntityPlayer
|
||||
) {
|
||||
val hit = RayTracer.retraceBlock(world, player, x, y, z)
|
||||
if (hit == null)
|
||||
return
|
||||
|
||||
val tile = getTile(world, x, y, z)
|
||||
if (tile == null)
|
||||
return
|
||||
|
||||
val (index, mop) = reduceMOP(hit)
|
||||
tile.partList(index).click(player, mop, player.getHeldItem)
|
||||
}
|
||||
|
||||
override def isProvidingStrongPower(
|
||||
world: IBlockAccess,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
side: Int
|
||||
): Int =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => 0
|
||||
case tile => tile.strongPowerLevel(side ^ 1)
|
||||
}
|
||||
|
||||
override def getExplosionResistance(entity:Entity, world:World, x:Int, y:Int, z:Int, explosionX:Double, explosionY:Double, explosionZ:Double):Float =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => 0
|
||||
case tile => tile.getExplosionResistance(entity)
|
||||
}
|
||||
override def isProvidingWeakPower(
|
||||
world: IBlockAccess,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
side: Int
|
||||
): Int =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => 0
|
||||
case tile => tile.weakPowerLevel(side ^ 1)
|
||||
}
|
||||
|
||||
override def getWeakChanges(world:IBlockAccess, x:Int, y:Int, z:Int) =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => false
|
||||
case tile => tile.getWeakChanges
|
||||
}
|
||||
override def canConnectRedstone(
|
||||
world: IBlockAccess,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
side: Int
|
||||
): Boolean =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => false
|
||||
case tile => tile.canConnectRedstone(side)
|
||||
}
|
||||
|
||||
override def canProvidePower = true
|
||||
override def onEntityCollidedWithBlock(
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
entity: Entity
|
||||
) {
|
||||
val tile = getTile(world, x, y, z)
|
||||
if (tile != null)
|
||||
tile.onEntityCollision(entity)
|
||||
}
|
||||
|
||||
override def onNeighborChange(
|
||||
world: IBlockAccess,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
tileX: Int,
|
||||
tileY: Int,
|
||||
tileZ: Int
|
||||
) {
|
||||
val tile = getTile(world, x, y, z)
|
||||
if (tile != null)
|
||||
tile.onNeighborTileChange(tileX, tileY, tileZ)
|
||||
}
|
||||
|
||||
override def getExplosionResistance(
|
||||
entity: Entity,
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
explosionX: Double,
|
||||
explosionY: Double,
|
||||
explosionZ: Double
|
||||
): Float =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => 0
|
||||
case tile => tile.getExplosionResistance(entity)
|
||||
}
|
||||
|
||||
override def getWeakChanges(world: IBlockAccess, x: Int, y: Int, z: Int) =
|
||||
getTile(world, x, y, z) match {
|
||||
case null => false
|
||||
case tile => tile.getWeakChanges
|
||||
}
|
||||
|
||||
override def canProvidePower = true
|
||||
}
|
||||
@@ -11,47 +11,46 @@ import cpw.mods.fml.common.eventhandler.SubscribeEvent
|
||||
import cpw.mods.fml.relauncher.{Side, SideOnly}
|
||||
import cpw.mods.fml.common.gameevent.TickEvent.ClientTickEvent
|
||||
|
||||
/**
|
||||
* A class that maintains a map server<->client of which players are holding the control (or placement modifier key) much like sneaking.
|
||||
*/
|
||||
object ControlKeyModifer
|
||||
{
|
||||
implicit def playerControlValue(p:EntityPlayer) = new ControlKeyValue(p)
|
||||
|
||||
class ControlKeyValue(p:EntityPlayer)
|
||||
{
|
||||
def isControlDown = map(p)
|
||||
}
|
||||
|
||||
val map = HashMap[EntityPlayer, Boolean]().withDefaultValue(false)
|
||||
|
||||
/**
|
||||
* Implicit static for Java users.
|
||||
*/
|
||||
def isControlDown(p:EntityPlayer) = p.isControlDown
|
||||
/** A class that maintains a map server<->client of which players are holding
|
||||
* the control (or placement modifier key) much like sneaking.
|
||||
*/
|
||||
object ControlKeyModifer {
|
||||
implicit def playerControlValue(p: EntityPlayer) = new ControlKeyValue(p)
|
||||
|
||||
class ControlKeyValue(p: EntityPlayer) {
|
||||
def isControlDown = map(p)
|
||||
}
|
||||
|
||||
val map = HashMap[EntityPlayer, Boolean]().withDefaultValue(false)
|
||||
|
||||
/** Implicit static for Java users.
|
||||
*/
|
||||
def isControlDown(p: EntityPlayer) = p.isControlDown
|
||||
}
|
||||
|
||||
/**
|
||||
* Key Handler implementation
|
||||
*/
|
||||
object ControlKeyHandler extends KeyBinding("key.control", Keyboard.KEY_LCONTROL, "key.categories.gameplay")
|
||||
{
|
||||
import ControlKeyModifer._
|
||||
var wasPressed = false
|
||||
/** Key Handler implementation
|
||||
*/
|
||||
object ControlKeyHandler
|
||||
extends KeyBinding(
|
||||
"key.control",
|
||||
Keyboard.KEY_LCONTROL,
|
||||
"key.categories.gameplay"
|
||||
) {
|
||||
import ControlKeyModifer._
|
||||
var wasPressed = false
|
||||
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
def tick(event:ClientTickEvent) {
|
||||
val pressed = getIsKeyPressed
|
||||
if(pressed != wasPressed) {
|
||||
wasPressed = pressed
|
||||
if(Minecraft.getMinecraft.getNetHandler != null)
|
||||
{
|
||||
map.put(Minecraft.getMinecraft.thePlayer, pressed)
|
||||
val packet = new PacketCustom(MultipartCPH.channel, 1)
|
||||
packet.writeBoolean(pressed)
|
||||
packet.sendToServer()
|
||||
}
|
||||
}
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
def tick(event: ClientTickEvent) {
|
||||
val pressed = getIsKeyPressed
|
||||
if (pressed != wasPressed) {
|
||||
wasPressed = pressed
|
||||
if (Minecraft.getMinecraft.getNetHandler != null) {
|
||||
map.put(Minecraft.getMinecraft.thePlayer, pressed)
|
||||
val packet = new PacketCustom(MultipartCPH.channel, 1)
|
||||
packet.writeBoolean(pressed)
|
||||
packet.sendToServer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,31 +3,24 @@ package codechicken.multipart
|
||||
import codechicken.lib.data.MCDataOutput
|
||||
import codechicken.lib.data.MCDataInput
|
||||
|
||||
/**
|
||||
* Class for reading and writing ids, widening the carrier data type as necessary
|
||||
*/
|
||||
class IDWriter
|
||||
{
|
||||
var write:(MCDataOutput, Int)=>Unit = _
|
||||
var read:(MCDataInput)=>Int = _
|
||||
|
||||
def setMax(i:Int)
|
||||
{
|
||||
val l = i.toLong & 0xFFFFFFFF
|
||||
if(l > 0xFFFF)
|
||||
{
|
||||
write = (data, i) => data.writeInt(i)
|
||||
read = (data) => data.readInt()
|
||||
}
|
||||
else if(l > 0xFF)
|
||||
{
|
||||
write = (data, i) => data.writeShort(i)
|
||||
read = (data) => data.readUShort()
|
||||
}
|
||||
else
|
||||
{
|
||||
write = (data, i) => data.writeByte(i)
|
||||
read = (data) => data.readUByte()
|
||||
}
|
||||
/** Class for reading and writing ids, widening the carrier data type as
|
||||
* necessary
|
||||
*/
|
||||
class IDWriter {
|
||||
var write: (MCDataOutput, Int) => Unit = _
|
||||
var read: (MCDataInput) => Int = _
|
||||
|
||||
def setMax(i: Int) {
|
||||
val l = i.toLong & 0xffffffff
|
||||
if (l > 0xffff) {
|
||||
write = (data, i) => data.writeInt(i)
|
||||
read = (data) => data.readInt()
|
||||
} else if (l > 0xff) {
|
||||
write = (data, i) => data.writeShort(i)
|
||||
read = (data) => data.readUShort()
|
||||
} else {
|
||||
write = (data, i) => data.writeByte(i)
|
||||
read = (data) => data.readUByte()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
package codechicken.multipart
|
||||
|
||||
/**
|
||||
* Mixin interface for parts that want to be notified of neighbor tile change events (comparators or inventory maintainers)
|
||||
*/
|
||||
/** Mixin interface for parts that want to be notified of neighbor tile change
|
||||
* events (comparators or inventory maintainers)
|
||||
*/
|
||||
trait INeighborTileChange {
|
||||
/**
|
||||
* Returns whether this part needs calls for tile changes through one solid block
|
||||
*/
|
||||
def weakTileChanges():Boolean
|
||||
/**
|
||||
* Callback for neighbor tile changes, from same function in Block
|
||||
*/
|
||||
def onNeighborTileChanged(side:Int, weak:Boolean)
|
||||
}
|
||||
|
||||
/** Returns whether this part needs calls for tile changes through one solid
|
||||
* block
|
||||
*/
|
||||
def weakTileChanges(): Boolean
|
||||
|
||||
/** Callback for neighbor tile changes, from same function in Block
|
||||
*/
|
||||
def onNeighborTileChanged(side: Int, weak: Boolean)
|
||||
}
|
||||
@@ -2,15 +2,13 @@ package codechicken.multipart
|
||||
|
||||
import java.util.Random
|
||||
|
||||
/**
|
||||
* Interface for parts that need random display ticks (torches)
|
||||
*
|
||||
* Marker interface for TRandomDisplayTickTile
|
||||
*/
|
||||
trait IRandomDisplayTick
|
||||
{
|
||||
/**
|
||||
* Called on a random display tick.
|
||||
*/
|
||||
def randomDisplayTick(random:Random)
|
||||
}
|
||||
/** Interface for parts that need random display ticks (torches)
|
||||
*
|
||||
* Marker interface for TRandomDisplayTickTile
|
||||
*/
|
||||
trait IRandomDisplayTick {
|
||||
|
||||
/** Called on a random display tick.
|
||||
*/
|
||||
def randomDisplayTick(random: Random)
|
||||
}
|
||||
@@ -7,237 +7,307 @@ import net.minecraft.util.Direction
|
||||
import codechicken.lib.vec.Rotation._
|
||||
import net.minecraft.init.Blocks
|
||||
|
||||
/**
|
||||
* Interface for parts with redstone interaction
|
||||
*
|
||||
* Marker interface for TRedstoneTile. This means that if a part is an instance of IRedstonePart, the container tile may be cast to TRedstoneTile
|
||||
*/
|
||||
trait IRedstonePart
|
||||
{
|
||||
/**
|
||||
* Returns the strong (indirect, through blocks) signal being emitted by this part to the specified side
|
||||
*/
|
||||
def strongPowerLevel(side:Int):Int
|
||||
/**
|
||||
* Returns the weak (direct) signal being emitted by this part to the specified side
|
||||
*/
|
||||
def weakPowerLevel(side:Int):Int
|
||||
/**
|
||||
* Returns true if this part can connect to redstone on the specified side. Blocking parts like covers will be handled by RedstoneInteractions
|
||||
*/
|
||||
def canConnectRedstone(side:Int):Boolean
|
||||
/** Interface for parts with redstone interaction
|
||||
*
|
||||
* Marker interface for TRedstoneTile. This means that if a part is an instance
|
||||
* of IRedstonePart, the container tile may be cast to TRedstoneTile
|
||||
*/
|
||||
trait IRedstonePart {
|
||||
|
||||
/** Returns the strong (indirect, through blocks) signal being emitted by this
|
||||
* part to the specified side
|
||||
*/
|
||||
def strongPowerLevel(side: Int): Int
|
||||
|
||||
/** Returns the weak (direct) signal being emitted by this part to the
|
||||
* specified side
|
||||
*/
|
||||
def weakPowerLevel(side: Int): Int
|
||||
|
||||
/** Returns true if this part can connect to redstone on the specified side.
|
||||
* Blocking parts like covers will be handled by RedstoneInteractions
|
||||
*/
|
||||
def canConnectRedstone(side: Int): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* For parts like wires that adhere to a specific face, reduces redstone connections to the specific edge between two faces.
|
||||
* Should be implemented on parts implementing TFacePart
|
||||
*/
|
||||
trait IFaceRedstonePart extends IRedstonePart
|
||||
{
|
||||
/**
|
||||
* Return the face to which this redstone part is attached
|
||||
*/
|
||||
def getFace:Int
|
||||
/** For parts like wires that adhere to a specific face, reduces redstone
|
||||
* connections to the specific edge between two faces. Should be implemented on
|
||||
* parts implementing TFacePart
|
||||
*/
|
||||
trait IFaceRedstonePart extends IRedstonePart {
|
||||
|
||||
/** Return the face to which this redstone part is attached
|
||||
*/
|
||||
def getFace: Int
|
||||
}
|
||||
|
||||
/**
|
||||
* For parts that want to define their own connection masks (like center-center parts)
|
||||
*/
|
||||
trait IMaskedRedstonePart extends IRedstonePart
|
||||
{
|
||||
/**
|
||||
* Returns the redstone connection mask for this part on side. See IRedstoneConnector for mask definition
|
||||
*/
|
||||
def getConnectionMask(side:Int):Int
|
||||
/** For parts that want to define their own connection masks (like center-center
|
||||
* parts)
|
||||
*/
|
||||
trait IMaskedRedstonePart extends IRedstonePart {
|
||||
|
||||
/** Returns the redstone connection mask for this part on side. See
|
||||
* IRedstoneConnector for mask definition
|
||||
*/
|
||||
def getConnectionMask(side: Int): Int
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for tile entities which split their redstone connections into a mask for each side (edges and center)
|
||||
*
|
||||
* All connection masks are a 5 bit map.
|
||||
* The lowest 4 bits correspond to the connection toward the face specified Rotation.rotateSide(side&6, b) where b is the bit index from lowest to highest.
|
||||
* Bit 5 corresponds to a connection opposite side.
|
||||
*/
|
||||
trait IRedstoneConnector
|
||||
{
|
||||
/**
|
||||
* Returns the redstone connection mask for this tile on side.
|
||||
*/
|
||||
def getConnectionMask(side:Int):Int
|
||||
/**
|
||||
* Returns the weak power level provided by this tile on side through mask
|
||||
*/
|
||||
def weakPowerLevel(side:Int, mask:Int):Int
|
||||
/** Interface for tile entities which split their redstone connections into a
|
||||
* mask for each side (edges and center)
|
||||
*
|
||||
* All connection masks are a 5 bit map. The lowest 4 bits correspond to the
|
||||
* connection toward the face specified Rotation.rotateSide(side&6, b) where b
|
||||
* is the bit index from lowest to highest. Bit 5 corresponds to a connection
|
||||
* opposite side.
|
||||
*/
|
||||
trait IRedstoneConnector {
|
||||
|
||||
/** Returns the redstone connection mask for this tile on side.
|
||||
*/
|
||||
def getConnectionMask(side: Int): Int
|
||||
|
||||
/** Returns the weak power level provided by this tile on side through mask
|
||||
*/
|
||||
def weakPowerLevel(side: Int, mask: Int): Int
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal interface for TileMultipart instances hosting IRedstonePart
|
||||
*/
|
||||
trait IRedstoneTile extends IRedstoneConnector
|
||||
{
|
||||
/**
|
||||
* Returns a mask of spaces through which a wire could connect on side
|
||||
*/
|
||||
def openConnections(side:Int):Int
|
||||
/** Internal interface for TileMultipart instances hosting IRedstonePart
|
||||
*/
|
||||
trait IRedstoneTile extends IRedstoneConnector {
|
||||
|
||||
/** Returns a mask of spaces through which a wire could connect on side
|
||||
*/
|
||||
def openConnections(side: Int): Int
|
||||
}
|
||||
|
||||
/**
|
||||
* Block version of IRedstoneConnector
|
||||
* Due to the inadequate Block.canConnectRedstone not handling the bottom side (nor the top particularly well)
|
||||
*/
|
||||
trait IRedstoneConnectorBlock
|
||||
{
|
||||
def getConnectionMask(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int):Int
|
||||
def weakPowerLevel(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int, mask:Int):Int
|
||||
/** Block version of IRedstoneConnector Due to the inadequate
|
||||
* Block.canConnectRedstone not handling the bottom side (nor the top
|
||||
* particularly well)
|
||||
*/
|
||||
trait IRedstoneConnectorBlock {
|
||||
def getConnectionMask(
|
||||
world: IBlockAccess,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
side: Int
|
||||
): Int
|
||||
def weakPowerLevel(
|
||||
world: IBlockAccess,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
side: Int,
|
||||
mask: Int
|
||||
): Int
|
||||
}
|
||||
|
||||
/**
|
||||
* static helper class for calculating various things about redstone.
|
||||
* Indirect power (also known as strong power) is not handled here, just use world.getIndirectPowerTo
|
||||
* Masks are defined in IRedstoneConnector
|
||||
*/
|
||||
object RedstoneInteractions
|
||||
{
|
||||
import net.minecraft.util.Facing._
|
||||
|
||||
val vanillaSideMap = Array(-2, -1, 0, 2, 3, 1)
|
||||
val sideVanillaMap = Array(1, 2, 5, 3, 4)
|
||||
/** static helper class for calculating various things about redstone. Indirect
|
||||
* power (also known as strong power) is not handled here, just use
|
||||
* world.getIndirectPowerTo Masks are defined in IRedstoneConnector
|
||||
*/
|
||||
object RedstoneInteractions {
|
||||
import net.minecraft.util.Facing._
|
||||
|
||||
/**
|
||||
* Hardcoded vanilla overrides for Block.canConnectRedstone (see @IRedstoneConnectorBlock)
|
||||
*/
|
||||
val fullVanillaBlocks = Set(
|
||||
Blocks.redstone_torch,
|
||||
Blocks.unlit_redstone_torch,
|
||||
Blocks.lever,
|
||||
Blocks.stone_button,
|
||||
Blocks.wooden_button,
|
||||
Blocks.redstone_block)
|
||||
|
||||
/**
|
||||
* Get the direct power to p on side
|
||||
*/
|
||||
def getPowerTo(p:TMultiPart, side:Int):Int =
|
||||
{
|
||||
val tile = p.tile
|
||||
return getPowerTo(tile.getWorldObj, tile.xCoord, tile.yCoord, tile.zCoord, side,
|
||||
tile.asInstanceOf[IRedstoneTile].openConnections(side)&connectionMask(p, side))
|
||||
val vanillaSideMap = Array(-2, -1, 0, 2, 3, 1)
|
||||
val sideVanillaMap = Array(1, 2, 5, 3, 4)
|
||||
|
||||
/** Hardcoded vanilla overrides for Block.canConnectRedstone (see
|
||||
* \@IRedstoneConnectorBlock)
|
||||
*/
|
||||
val fullVanillaBlocks = Set(
|
||||
Blocks.redstone_torch,
|
||||
Blocks.unlit_redstone_torch,
|
||||
Blocks.lever,
|
||||
Blocks.stone_button,
|
||||
Blocks.wooden_button,
|
||||
Blocks.redstone_block
|
||||
)
|
||||
|
||||
/** Get the direct power to p on side
|
||||
*/
|
||||
def getPowerTo(p: TMultiPart, side: Int): Int = {
|
||||
val tile = p.tile
|
||||
return getPowerTo(
|
||||
tile.getWorldObj,
|
||||
tile.xCoord,
|
||||
tile.yCoord,
|
||||
tile.zCoord,
|
||||
side,
|
||||
tile.asInstanceOf[IRedstoneTile].openConnections(side) & connectionMask(
|
||||
p,
|
||||
side
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
/** Get the direct power level to space (x, y, z) on side with mask
|
||||
*/
|
||||
def getPowerTo(
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
side: Int,
|
||||
mask: Int
|
||||
): Int =
|
||||
getPower(
|
||||
world,
|
||||
x + offsetsXForSide(side),
|
||||
y + offsetsYForSide(side),
|
||||
z + offsetsZForSide(side),
|
||||
side ^ 1,
|
||||
mask
|
||||
)
|
||||
|
||||
/** Get the direct power level provided by space (x, y, z) on side with mask
|
||||
*/
|
||||
def getPower(
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
side: Int,
|
||||
mask: Int
|
||||
): Int = {
|
||||
val tile = world.getTileEntity(x, y, z)
|
||||
if (tile.isInstanceOf[IRedstoneConnector])
|
||||
return tile.asInstanceOf[IRedstoneConnector].weakPowerLevel(side, mask)
|
||||
|
||||
val block = world.getBlock(x, y, z)
|
||||
if (block.isInstanceOf[IRedstoneConnectorBlock])
|
||||
return block
|
||||
.asInstanceOf[IRedstoneConnectorBlock]
|
||||
.weakPowerLevel(world, x, y, z, side, mask)
|
||||
|
||||
val vmask = vanillaConnectionMask(block, world, x, y, z, side, true)
|
||||
if ((vmask & mask) > 0) {
|
||||
var m = world.getIndirectPowerLevelTo(x, y, z, side ^ 1)
|
||||
if (m < 15 && block == Blocks.redstone_wire)
|
||||
m =
|
||||
Math.max(m, world.getBlockMetadata(x, y, z)) // painful vanilla kludge
|
||||
return m
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the direct power level to space (x, y, z) on side with mask
|
||||
*/
|
||||
def getPowerTo(world:World, x:Int, y:Int, z:Int, side:Int, mask:Int):Int =
|
||||
getPower(world, x+offsetsXForSide(side), y+offsetsYForSide(side), z+offsetsZForSide(side), side^1, mask)
|
||||
|
||||
/**
|
||||
* Get the direct power level provided by space (x, y, z) on side with mask
|
||||
*/
|
||||
def getPower(world:World, x:Int, y:Int, z:Int, side:Int, mask:Int):Int =
|
||||
{
|
||||
val tile = world.getTileEntity(x, y, z)
|
||||
if(tile.isInstanceOf[IRedstoneConnector])
|
||||
return tile.asInstanceOf[IRedstoneConnector].weakPowerLevel(side, mask)
|
||||
|
||||
val block = world.getBlock(x, y, z)
|
||||
if(block.isInstanceOf[IRedstoneConnectorBlock])
|
||||
return block.asInstanceOf[IRedstoneConnectorBlock].weakPowerLevel(world, x, y, z, side, mask)
|
||||
|
||||
val vmask = vanillaConnectionMask(block, world, x, y, z, side, true)
|
||||
if((vmask&mask) > 0)
|
||||
{
|
||||
var m = world.getIndirectPowerLevelTo(x, y, z, side^1)
|
||||
if(m < 15 && block == Blocks.redstone_wire)
|
||||
m = Math.max(m, world.getBlockMetadata(x, y, z))//painful vanilla kludge
|
||||
return m
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
def vanillaToSide(vside: Int) = sideVanillaMap(vside + 1)
|
||||
|
||||
/** Get the connection mask of the block on side of (x, y, z).
|
||||
* @param power,
|
||||
* whether the connection mask is for signal transfer or visual connection.
|
||||
* (some blocks accept power without visual connection)
|
||||
*/
|
||||
def otherConnectionMask(
|
||||
world: IBlockAccess,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
side: Int,
|
||||
power: Boolean
|
||||
): Int =
|
||||
getConnectionMask(
|
||||
world,
|
||||
x + offsetsXForSide(side),
|
||||
y + offsetsYForSide(side),
|
||||
z + offsetsZForSide(side),
|
||||
side ^ 1,
|
||||
power
|
||||
)
|
||||
|
||||
/** Get the connection mask of part on side
|
||||
*/
|
||||
def connectionMask(p: TMultiPart, side: Int): Int = {
|
||||
if (
|
||||
p.isInstanceOf[IRedstonePart] && p
|
||||
.asInstanceOf[IRedstonePart]
|
||||
.canConnectRedstone(side)
|
||||
) {
|
||||
if (p.isInstanceOf[IFaceRedstonePart]) {
|
||||
val fside = p.asInstanceOf[IFaceRedstonePart].getFace
|
||||
if ((side & 6) == (fside & 6))
|
||||
return 0x10
|
||||
|
||||
return 1 << rotationTo(side & 6, fside)
|
||||
} else if (p.isInstanceOf[IMaskedRedstonePart]) {
|
||||
return p.asInstanceOf[IMaskedRedstonePart].getConnectionMask(side)
|
||||
}
|
||||
return 0x1f
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
/** @param power
|
||||
* If true, don't test canConnectRedstone on blocks, just get a power
|
||||
* transmission mask rather than a visual connection
|
||||
*/
|
||||
def getConnectionMask(
|
||||
world: IBlockAccess,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
side: Int,
|
||||
power: Boolean
|
||||
): Int = {
|
||||
val tile = world.getTileEntity(x, y, z)
|
||||
if (tile.isInstanceOf[IRedstoneConnector])
|
||||
return tile.asInstanceOf[IRedstoneConnector].getConnectionMask(side)
|
||||
|
||||
val block = world.getBlock(x, y, z)
|
||||
if (block.isInstanceOf[IRedstoneConnectorBlock])
|
||||
return block
|
||||
.asInstanceOf[IRedstoneConnectorBlock]
|
||||
.getConnectionMask(world, x, y, z, side)
|
||||
|
||||
return vanillaConnectionMask(block, world, x, y, z, side, power)
|
||||
}
|
||||
|
||||
/** Returns the connection mask for a vanilla block
|
||||
*/
|
||||
def vanillaConnectionMask(
|
||||
block: Block,
|
||||
world: IBlockAccess,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
side: Int,
|
||||
power: Boolean
|
||||
): Int = {
|
||||
if (fullVanillaBlocks(block))
|
||||
return 0x1f
|
||||
|
||||
if (side == 0) // vanilla doesn't handle side 0
|
||||
return if (power) 0x1f else 0
|
||||
|
||||
/** so that these can be conducted to from face parts on the other side of
|
||||
* the block. Due to vanilla's in adequecy with redstone/logic on walls
|
||||
*/
|
||||
if (
|
||||
block == Blocks.redstone_wire || block == Blocks.powered_comparator || block == Blocks.unpowered_comparator
|
||||
) {
|
||||
if (side != 0)
|
||||
return if (power) 0x1f else 4
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
val vside = vanillaSideMap(side)
|
||||
if (
|
||||
block == Blocks.powered_repeater || block == Blocks.unpowered_repeater
|
||||
) // stupid minecraft hardcodes
|
||||
{
|
||||
val meta = world.getBlockMetadata(x, y, z)
|
||||
if (vside == (meta & 3) || vside == Direction.rotateOpposite(meta & 3))
|
||||
return if (power) 0x1f else 4
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
def vanillaToSide(vside:Int) = sideVanillaMap(vside+1)
|
||||
|
||||
/**
|
||||
* Get the connection mask of the block on side of (x, y, z).
|
||||
* @param power, whether the connection mask is for signal transfer or visual connection. (some blocks accept power without visual connection)
|
||||
*/
|
||||
def otherConnectionMask(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int, power:Boolean):Int =
|
||||
getConnectionMask(world, x+offsetsXForSide(side), y+offsetsYForSide(side), z+offsetsZForSide(side), side^1, power)
|
||||
|
||||
/**
|
||||
* Get the connection mask of part on side
|
||||
*/
|
||||
def connectionMask(p:TMultiPart, side:Int):Int =
|
||||
{
|
||||
if(p.isInstanceOf[IRedstonePart] && p.asInstanceOf[IRedstonePart].canConnectRedstone(side))
|
||||
{
|
||||
if(p.isInstanceOf[IFaceRedstonePart])
|
||||
{
|
||||
val fside = p.asInstanceOf[IFaceRedstonePart].getFace
|
||||
if((side&6) == (fside&6))
|
||||
return 0x10
|
||||
|
||||
return 1<<rotationTo(side&6, fside)
|
||||
}
|
||||
else if(p.isInstanceOf[IMaskedRedstonePart])
|
||||
{
|
||||
return p.asInstanceOf[IMaskedRedstonePart].getConnectionMask(side)
|
||||
}
|
||||
return 0x1F
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* @param power If true, don't test canConnectRedstone on blocks, just get a power transmission mask rather than a visual connection
|
||||
*/
|
||||
def getConnectionMask(world:IBlockAccess, x:Int, y:Int, z:Int, side:Int, power:Boolean):Int =
|
||||
{
|
||||
val tile = world.getTileEntity(x, y, z)
|
||||
if(tile.isInstanceOf[IRedstoneConnector])
|
||||
return tile.asInstanceOf[IRedstoneConnector].getConnectionMask(side)
|
||||
|
||||
val block = world.getBlock(x, y, z)
|
||||
if(block.isInstanceOf[IRedstoneConnectorBlock])
|
||||
return block.asInstanceOf[IRedstoneConnectorBlock].getConnectionMask(world, x, y, z, side)
|
||||
|
||||
return vanillaConnectionMask(block, world, x, y, z, side, power)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the connection mask for a vanilla block
|
||||
*/
|
||||
def vanillaConnectionMask(block:Block, world:IBlockAccess, x:Int, y:Int, z:Int, side:Int, power:Boolean):Int =
|
||||
{
|
||||
if(fullVanillaBlocks(block))
|
||||
return 0x1F
|
||||
|
||||
if(side == 0)//vanilla doesn't handle side 0
|
||||
return if(power) 0x1F else 0
|
||||
}
|
||||
|
||||
/**
|
||||
* so that these can be conducted to from face parts on the other side of the block.
|
||||
* Due to vanilla's in adequecy with redstone/logic on walls
|
||||
*/
|
||||
if(block == Blocks.redstone_wire || block == Blocks.powered_comparator || block == Blocks.unpowered_comparator) {
|
||||
if(side != 0)
|
||||
return if(power) 0x1F else 4
|
||||
if (
|
||||
power || block.canConnectRedstone(world, x, y, z, vside)
|
||||
) // some blocks accept power without visualising connections
|
||||
return 0x1f
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
val vside = vanillaSideMap(side)
|
||||
if(block == Blocks.powered_repeater || block == Blocks.unpowered_repeater)//stupid minecraft hardcodes
|
||||
{
|
||||
val meta = world.getBlockMetadata(x, y, z)
|
||||
if(vside == (meta & 3) || vside == Direction.rotateOpposite(meta & 3))
|
||||
return if(power) 0x1F else 4
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
if(power || block.canConnectRedstone(world, x, y, z, vside))//some blocks accept power without visualising connections
|
||||
return 0x1F
|
||||
|
||||
return 0
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
}
|
||||
@@ -8,47 +8,61 @@ import net.minecraft.entity.player.EntityPlayer
|
||||
import net.minecraft.world.World
|
||||
import codechicken.lib.vec.BlockCoord
|
||||
|
||||
/**
|
||||
* Java class implementation
|
||||
*/
|
||||
/** Java class implementation
|
||||
*/
|
||||
abstract class JItemMultiPart extends Item with TItemMultiPart
|
||||
|
||||
/**
|
||||
* Simple multipart item class for easy placement. Simply override the newPart function and it the part will be added to the block space if it passes the occlusion tests.
|
||||
*/
|
||||
trait TItemMultiPart extends Item
|
||||
{
|
||||
def getHitDepth(vhit:Vector3, side:Int):Double =
|
||||
vhit.copy.scalarProject(Rotation.axes(side)) + (side%2^1)
|
||||
|
||||
override def onItemUse(item:ItemStack, player:EntityPlayer, world:World, x:Int, y:Int, z:Int, side:Int, hitX:Float, hitY:Float, hitZ:Float):Boolean =
|
||||
{
|
||||
val pos = new BlockCoord(x, y, z)
|
||||
val vhit = new Vector3(hitX, hitY, hitZ)
|
||||
val d = getHitDepth(vhit, side)
|
||||
|
||||
def place():Boolean =
|
||||
{
|
||||
val part = newPart(item, player, world, pos, side, vhit)
|
||||
if(part == null || !TileMultipart.canPlacePart(world, pos, part))
|
||||
return false
|
||||
|
||||
if(!world.isRemote)
|
||||
TileMultipart.addPart(world, pos, part)
|
||||
if(!player.capabilities.isCreativeMode)
|
||||
item.stackSize-=1
|
||||
return true
|
||||
}
|
||||
|
||||
if(d < 1 && place())
|
||||
return true
|
||||
|
||||
pos.offset(side)
|
||||
return place()
|
||||
/** Simple multipart item class for easy placement. Simply override the newPart
|
||||
* function and it the part will be added to the block space if it passes the
|
||||
* occlusion tests.
|
||||
*/
|
||||
trait TItemMultiPart extends Item {
|
||||
def getHitDepth(vhit: Vector3, side: Int): Double =
|
||||
vhit.copy.scalarProject(Rotation.axes(side)) + (side % 2 ^ 1)
|
||||
|
||||
override def onItemUse(
|
||||
item: ItemStack,
|
||||
player: EntityPlayer,
|
||||
world: World,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
side: Int,
|
||||
hitX: Float,
|
||||
hitY: Float,
|
||||
hitZ: Float
|
||||
): Boolean = {
|
||||
val pos = new BlockCoord(x, y, z)
|
||||
val vhit = new Vector3(hitX, hitY, hitZ)
|
||||
val d = getHitDepth(vhit, side)
|
||||
|
||||
def place(): Boolean = {
|
||||
val part = newPart(item, player, world, pos, side, vhit)
|
||||
if (part == null || !TileMultipart.canPlacePart(world, pos, part))
|
||||
return false
|
||||
|
||||
if (!world.isRemote)
|
||||
TileMultipart.addPart(world, pos, part)
|
||||
if (!player.capabilities.isCreativeMode)
|
||||
item.stackSize -= 1
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new part based on the placement information parameters.
|
||||
*/
|
||||
def newPart(item:ItemStack, player:EntityPlayer, world:World, pos:BlockCoord, side:Int, vhit:Vector3):TMultiPart
|
||||
}
|
||||
|
||||
if (d < 1 && place())
|
||||
return true
|
||||
|
||||
pos.offset(side)
|
||||
return place()
|
||||
}
|
||||
|
||||
/** Create a new part based on the placement information parameters.
|
||||
*/
|
||||
def newPart(
|
||||
item: ItemStack,
|
||||
player: EntityPlayer,
|
||||
world: World,
|
||||
pos: BlockCoord,
|
||||
side: Int,
|
||||
vhit: Vector3
|
||||
): TMultiPart
|
||||
}
|
||||
@@ -15,223 +15,233 @@ import com.google.common.collect.ArrayListMultimap
|
||||
import scala.collection.JavaConversions._
|
||||
import net.minecraft.nbt.NBTTagCompound
|
||||
|
||||
/**
|
||||
* This class handles the registration and internal ID mapping of all multipart classes.
|
||||
*/
|
||||
object MultiPartRegistry
|
||||
{
|
||||
/**
|
||||
* Interface to be registered for constructing parts.
|
||||
* Every instance of every multipart is constructed from an implementor of this.
|
||||
*/
|
||||
@Deprecated
|
||||
trait IPartFactory
|
||||
{
|
||||
/**
|
||||
* Create a new instance of the part with the specified type name identifier
|
||||
* @param client If the part instance is for the client or the server
|
||||
*/
|
||||
def createPart(name: String, client: Boolean): TMultiPart
|
||||
/** This class handles the registration and internal ID mapping of all multipart
|
||||
* classes.
|
||||
*/
|
||||
object MultiPartRegistry {
|
||||
|
||||
/** Interface to be registered for constructing parts. Every instance of every
|
||||
* multipart is constructed from an implementor of this.
|
||||
*/
|
||||
@Deprecated
|
||||
trait IPartFactory {
|
||||
|
||||
/** Create a new instance of the part with the specified type name
|
||||
* identifier
|
||||
* @param client
|
||||
* If the part instance is for the client or the server
|
||||
*/
|
||||
def createPart(name: String, client: Boolean): TMultiPart
|
||||
}
|
||||
|
||||
/** Will replace IPartFactory in 1.8
|
||||
*/
|
||||
trait IPartFactory2 {
|
||||
|
||||
/** Create a new server instance of the part with the specified type name
|
||||
* identifier
|
||||
* @param nbt
|
||||
* The tag compound that will be passed to part.load, can be used to
|
||||
* change the class of part returned
|
||||
*/
|
||||
def createPart(name: String, nbt: NBTTagCompound): TMultiPart
|
||||
|
||||
/** Create a new client instance of the part with the specified type name
|
||||
* identifier
|
||||
* @param packet
|
||||
* The packet that will be passed to part.readDesc, can be used to change
|
||||
* the class of part returned
|
||||
*/
|
||||
def createPart(name: String, packet: MCDataInput): TMultiPart
|
||||
}
|
||||
|
||||
/** An interface for converting existing blocks/tile entities to multipart
|
||||
* versions.
|
||||
*/
|
||||
trait IPartConverter {
|
||||
|
||||
/** Return true if this converter can handle the specific blockID (may or
|
||||
* may not actually convert the block)
|
||||
*/
|
||||
def blockTypes: Iterable[Block]
|
||||
|
||||
/** Return a multipart version of the block at pos in world. Return null if
|
||||
* no conversion is possible.
|
||||
*/
|
||||
def convert(world: World, pos: BlockCoord): TMultiPart
|
||||
}
|
||||
|
||||
private val typeMap = MMap[String, IPartFactory2]()
|
||||
private val nameMap = MMap[String, Int]()
|
||||
private var idMap: Array[(String, IPartFactory2)] = _
|
||||
private val idWriter = new IDWriter
|
||||
private val converters = ArrayListMultimap.create[Block, IPartConverter]()
|
||||
private val containers = MMap[String, ModContainer]()
|
||||
|
||||
/** The state of the registry. 0 = no parts, 1 = registering, 2 = registered
|
||||
*/
|
||||
private var state: Int = 0
|
||||
|
||||
/** Register a part factory with an array of types it is capable of
|
||||
* instantiating. Must be called before postInit
|
||||
* @deprecated
|
||||
* Use IPartFactory2
|
||||
*/
|
||||
@Deprecated
|
||||
def registerParts(partFactory: IPartFactory, types: Array[String]) {
|
||||
registerParts(partFactory.createPart _, types: _*)
|
||||
}
|
||||
|
||||
/** Scala function version of registerParts
|
||||
* @deprecated
|
||||
* Use IPartFactory2
|
||||
*/
|
||||
@Deprecated
|
||||
def registerParts(
|
||||
partFactory: (String, Boolean) => TMultiPart,
|
||||
types: String*
|
||||
) {
|
||||
registerParts(
|
||||
new IPartFactory2 {
|
||||
override def createPart(name: String, packet: MCDataInput) =
|
||||
partFactory(name, true)
|
||||
override def createPart(name: String, nbt: NBTTagCompound) =
|
||||
partFactory(name, false)
|
||||
},
|
||||
types: _*
|
||||
)
|
||||
}
|
||||
|
||||
/** Register a part factory with an array of types it is capable of
|
||||
* instantiating. Must be called before postInit
|
||||
*/
|
||||
def registerParts(partFactory: IPartFactory2, types: Array[String]) {
|
||||
registerParts(partFactory, types: _*)
|
||||
}
|
||||
|
||||
/** Scala va-args version of registerParts
|
||||
*/
|
||||
def registerParts(partFactory: IPartFactory2, types: String*) {
|
||||
if (loaded)
|
||||
throw new IllegalStateException(
|
||||
"Parts must be registered in the init methods."
|
||||
)
|
||||
state = 1
|
||||
|
||||
val container = Loader.instance.activeModContainer
|
||||
if (container == null)
|
||||
throw new IllegalStateException(
|
||||
"Parts must be registered during the initialization phase of a mod container"
|
||||
)
|
||||
|
||||
types.foreach { s =>
|
||||
if (typeMap.contains(s))
|
||||
throw new IllegalStateException(
|
||||
"Part with id " + s + " is already registered."
|
||||
)
|
||||
|
||||
typeMap.put(s, partFactory)
|
||||
containers.put(s, container)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Will replace IPartFactory in 1.8
|
||||
*/
|
||||
trait IPartFactory2
|
||||
{
|
||||
/**
|
||||
* Create a new server instance of the part with the specified type name identifier
|
||||
* @param nbt The tag compound that will be passed to part.load, can be used to change the class of part returned
|
||||
*/
|
||||
def createPart(name: String, nbt: NBTTagCompound): TMultiPart
|
||||
/** Register a part converter instance
|
||||
*/
|
||||
def registerConverter(c: IPartConverter) {
|
||||
c.blockTypes.foreach(converters.put(_, c))
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new client instance of the part with the specified type name identifier
|
||||
* @param packet The packet that will be passed to part.readDesc, can be used to change the class of part returned
|
||||
*/
|
||||
def createPart(name: String, packet: MCDataInput): TMultiPart
|
||||
private[multipart] def beforeServerStart() {
|
||||
idMap = typeMap.toList.sortBy(_._1).toArray
|
||||
idWriter.setMax(idMap.length)
|
||||
nameMap.clear()
|
||||
for (i <- 0 until idMap.length)
|
||||
nameMap.put(idMap(i)._1, i)
|
||||
}
|
||||
|
||||
private[multipart] def writeIDMap(packet: PacketCustom) {
|
||||
packet.writeInt(idMap.length)
|
||||
idMap.foreach(e => packet.writeString(e._1))
|
||||
}
|
||||
|
||||
private[multipart] def readIDMap(packet: PacketCustom): Seq[String] = {
|
||||
val k = packet.readInt()
|
||||
idWriter.setMax(k)
|
||||
idMap = new Array(k)
|
||||
nameMap.clear()
|
||||
val missing = ListBuffer[String]()
|
||||
for (i <- 0 until k) {
|
||||
val s = packet.readString()
|
||||
val v = typeMap.get(s)
|
||||
if (v.isEmpty)
|
||||
missing += s
|
||||
else {
|
||||
idMap(i) = (s, v.get)
|
||||
nameMap.put(s, i)
|
||||
}
|
||||
}
|
||||
return missing
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for converting existing blocks/tile entities to multipart versions.
|
||||
*/
|
||||
trait IPartConverter
|
||||
{
|
||||
/**
|
||||
* Return true if this converter can handle the specific blockID (may or may not actually convert the block)
|
||||
*/
|
||||
def blockTypes: Iterable[Block]
|
||||
/** Return true if any multiparts have been registered
|
||||
*/
|
||||
private[multipart] def required = state > 0
|
||||
|
||||
/**
|
||||
* Return a multipart version of the block at pos in world. Return null if no conversion is possible.
|
||||
*/
|
||||
def convert(world: World, pos: BlockCoord): TMultiPart
|
||||
/** Return true if no more parts can be registered
|
||||
*/
|
||||
def loaded = state == 2
|
||||
|
||||
private[multipart] def postInit() {
|
||||
state = 2
|
||||
}
|
||||
|
||||
/** Writes the id of part to data
|
||||
*/
|
||||
def writePartID(data: MCDataOutput, part: TMultiPart) {
|
||||
idWriter.write(data, nameMap.get(part.getType).get)
|
||||
}
|
||||
|
||||
/** Uses instantiators to creat a new part from the id read from data
|
||||
*/
|
||||
def readPart(data: MCDataInput) = {
|
||||
val e = idMap(idWriter.read(data))
|
||||
e._2.createPart(e._1, data)
|
||||
}
|
||||
|
||||
/** Uses instantiators to creat a new part from the a tag compound
|
||||
*/
|
||||
def loadPart(name: String, nbt: NBTTagCompound) = typeMap.get(name) match {
|
||||
case Some(factory) => factory.createPart(name, nbt)
|
||||
case None =>
|
||||
logger.error("Missing mapping for part with ID: " + name)
|
||||
null
|
||||
}
|
||||
|
||||
/** Uses instantiators to create a new part with specified identifier on side
|
||||
* @deprecated
|
||||
* currently calls the nbt/packet version with a null parameter, use
|
||||
* readPart or loadPart instead
|
||||
*/
|
||||
@Deprecated
|
||||
def createPart(name: String, client: Boolean) = typeMap.get(name) match {
|
||||
case Some(factory) =>
|
||||
if (client) factory.createPart(name, null: MCDataInput)
|
||||
else factory.createPart(name, null: NBTTagCompound)
|
||||
case None =>
|
||||
logger.error("Missing mapping for part with ID: " + name)
|
||||
null
|
||||
}
|
||||
|
||||
/** Calls converters to create a multipart version of the block at pos
|
||||
*/
|
||||
def convertBlock(world: World, pos: BlockCoord, block: Block): TMultiPart = {
|
||||
for (c <- converters.get(block)) {
|
||||
val ret = c.convert(world, pos)
|
||||
if (ret != null)
|
||||
return ret
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private val typeMap = MMap[String, IPartFactory2]()
|
||||
private val nameMap = MMap[String, Int]()
|
||||
private var idMap: Array[(String, IPartFactory2)] = _
|
||||
private val idWriter = new IDWriter
|
||||
private val converters = ArrayListMultimap.create[Block, IPartConverter]()
|
||||
private val containers = MMap[String, ModContainer]()
|
||||
|
||||
/**
|
||||
* The state of the registry. 0 = no parts, 1 = registering, 2 = registered
|
||||
*/
|
||||
private var state: Int = 0
|
||||
|
||||
/**
|
||||
* Register a part factory with an array of types it is capable of instantiating. Must be called before postInit
|
||||
* @deprecated Use IPartFactory2
|
||||
*/
|
||||
@Deprecated
|
||||
def registerParts(partFactory: IPartFactory, types: Array[String]) {
|
||||
registerParts(partFactory.createPart _, types: _*)
|
||||
}
|
||||
|
||||
/**
|
||||
* Scala function version of registerParts
|
||||
* @deprecated Use IPartFactory2
|
||||
*/
|
||||
@Deprecated
|
||||
def registerParts(partFactory: (String, Boolean) => TMultiPart, types: String*) {
|
||||
registerParts(new IPartFactory2 {
|
||||
override def createPart(name: String, packet: MCDataInput) = partFactory(name, true)
|
||||
override def createPart(name: String, nbt: NBTTagCompound) = partFactory(name, false)
|
||||
}, types:_*)
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a part factory with an array of types it is capable of instantiating. Must be called before postInit
|
||||
*/
|
||||
def registerParts(partFactory: IPartFactory2, types: Array[String]) {
|
||||
registerParts(partFactory, types:_*)
|
||||
}
|
||||
|
||||
/**
|
||||
* Scala va-args version of registerParts
|
||||
*/
|
||||
def registerParts(partFactory: IPartFactory2, types: String*) {
|
||||
if (loaded)
|
||||
throw new IllegalStateException("Parts must be registered in the init methods.")
|
||||
state = 1
|
||||
|
||||
val container = Loader.instance.activeModContainer
|
||||
if (container == null)
|
||||
throw new IllegalStateException("Parts must be registered during the initialization phase of a mod container")
|
||||
|
||||
types.foreach { s =>
|
||||
if (typeMap.contains(s))
|
||||
throw new IllegalStateException("Part with id " + s + " is already registered.")
|
||||
|
||||
typeMap.put(s, partFactory)
|
||||
containers.put(s, container)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a part converter instance
|
||||
*/
|
||||
def registerConverter(c: IPartConverter) {
|
||||
c.blockTypes.foreach(converters.put(_, c))
|
||||
}
|
||||
|
||||
private[multipart] def beforeServerStart() {
|
||||
idMap = typeMap.toList.sortBy(_._1).toArray
|
||||
idWriter.setMax(idMap.length)
|
||||
nameMap.clear()
|
||||
for (i <- 0 until idMap.length)
|
||||
nameMap.put(idMap(i)._1, i)
|
||||
}
|
||||
|
||||
private[multipart] def writeIDMap(packet: PacketCustom) {
|
||||
packet.writeInt(idMap.length)
|
||||
idMap.foreach(e => packet.writeString(e._1))
|
||||
}
|
||||
|
||||
private[multipart] def readIDMap(packet: PacketCustom): Seq[String] = {
|
||||
val k = packet.readInt()
|
||||
idWriter.setMax(k)
|
||||
idMap = new Array(k)
|
||||
nameMap.clear()
|
||||
val missing = ListBuffer[String]()
|
||||
for (i <- 0 until k) {
|
||||
val s = packet.readString()
|
||||
val v = typeMap.get(s)
|
||||
if (v.isEmpty)
|
||||
missing += s
|
||||
else {
|
||||
idMap(i) = (s, v.get)
|
||||
nameMap.put(s, i)
|
||||
}
|
||||
}
|
||||
return missing
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if any multiparts have been registered
|
||||
*/
|
||||
private[multipart] def required = state > 0
|
||||
|
||||
/**
|
||||
* Return true if no more parts can be registered
|
||||
*/
|
||||
def loaded = state == 2
|
||||
|
||||
private[multipart] def postInit() {
|
||||
state = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the id of part to data
|
||||
*/
|
||||
def writePartID(data: MCDataOutput, part: TMultiPart) {
|
||||
idWriter.write(data, nameMap.get(part.getType).get)
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses instantiators to creat a new part from the id read from data
|
||||
*/
|
||||
def readPart(data: MCDataInput) = {
|
||||
val e = idMap(idWriter.read(data))
|
||||
e._2.createPart(e._1, data)
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses instantiators to creat a new part from the a tag compound
|
||||
*/
|
||||
def loadPart(name: String, nbt:NBTTagCompound) = typeMap.get(name) match {
|
||||
case Some(factory) => factory.createPart(name, nbt)
|
||||
case None =>
|
||||
logger.error("Missing mapping for part with ID: " + name)
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses instantiators to create a new part with specified identifier on side
|
||||
* @deprecated currently calls the nbt/packet version with a null parameter, use readPart or loadPart instead
|
||||
*/
|
||||
@Deprecated
|
||||
def createPart(name: String, client: Boolean) = typeMap.get(name) match {
|
||||
case Some(factory) =>
|
||||
if(client) factory.createPart(name, null: MCDataInput)
|
||||
else factory.createPart(name, null: NBTTagCompound)
|
||||
case None =>
|
||||
logger.error("Missing mapping for part with ID: " + name)
|
||||
null
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls converters to create a multipart version of the block at pos
|
||||
*/
|
||||
def convertBlock(world: World, pos: BlockCoord, block: Block): TMultiPart = {
|
||||
for (c <- converters.get(block)) {
|
||||
val ret = c.convert(world, pos)
|
||||
if (ret != null)
|
||||
return ret
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
def getModContainer(name: String) = containers(name)
|
||||
def getModContainer(name: String) = containers(name)
|
||||
}
|
||||
@@ -11,176 +11,224 @@ import scala.collection.mutable.Map
|
||||
import codechicken.multipart.asm.{ScratchBitSet, MultipartMixinFactory}
|
||||
import codechicken.multipart.asm.ASMImplicits._
|
||||
|
||||
/**
|
||||
* This class manages the dynamic construction and allocation of container TileMultipart instances.
|
||||
*
|
||||
* Classes that extend TileMultipart, adding tile centric logic, optimisations or interfaces, can be registered to a marker interface on a part instance.
|
||||
* When a part is added to the tile that implements the certain marker interface, the container tile will be replaced with a class that includes the functionality from the corresponding mixin class.
|
||||
*
|
||||
* Classes are generated in a similar fashion to the way scala traits are compiled. To see the output, simply enable the config option and look in the asm/multipart folder of you .minecraft directory.
|
||||
*
|
||||
* There are several mixin traits that come with the API included in the scalatraits package. TPartialOcclusionTile is defined as class instead of trait to give an example for Java programmers.
|
||||
*/
|
||||
object MultipartGenerator extends ScratchBitSet
|
||||
{
|
||||
private val tileTraitMap = Map[Class[_], BitSet]()
|
||||
private val interfaceTraitMap_c = Map[String, String]()
|
||||
private val interfaceTraitMap_s = Map[String, String]()
|
||||
private val partTraitMap_c = Map[Class[_], BitSet]()
|
||||
private val partTraitMap_s = Map[Class[_], BitSet]()
|
||||
private val clientTraitId = MultipartMixinFactory.registerTrait(classOf[TileMultipartClient])
|
||||
|
||||
private def partTraitMap(client:Boolean) = if(client) partTraitMap_c else partTraitMap_s
|
||||
private def interfaceTraitMap(client:Boolean) = if(client) interfaceTraitMap_c else interfaceTraitMap_s
|
||||
/** This class manages the dynamic construction and allocation of container
|
||||
* TileMultipart instances.
|
||||
*
|
||||
* Classes that extend TileMultipart, adding tile centric logic, optimisations
|
||||
* or interfaces, can be registered to a marker interface on a part instance.
|
||||
* When a part is added to the tile that implements the certain marker
|
||||
* interface, the container tile will be replaced with a class that includes
|
||||
* the functionality from the corresponding mixin class.
|
||||
*
|
||||
* Classes are generated in a similar fashion to the way scala traits are
|
||||
* compiled. To see the output, simply enable the config option and look in the
|
||||
* asm/multipart folder of you .minecraft directory.
|
||||
*
|
||||
* There are several mixin traits that come with the API included in the
|
||||
* scalatraits package. TPartialOcclusionTile is defined as class instead of
|
||||
* trait to give an example for Java programmers.
|
||||
*/
|
||||
object MultipartGenerator extends ScratchBitSet {
|
||||
private val tileTraitMap = Map[Class[_], BitSet]()
|
||||
private val interfaceTraitMap_c = Map[String, String]()
|
||||
private val interfaceTraitMap_s = Map[String, String]()
|
||||
private val partTraitMap_c = Map[Class[_], BitSet]()
|
||||
private val partTraitMap_s = Map[Class[_], BitSet]()
|
||||
private val clientTraitId =
|
||||
MultipartMixinFactory.registerTrait(classOf[TileMultipartClient])
|
||||
|
||||
private def traitsForPart(part:TMultiPart, client:Boolean) =
|
||||
partTraitMap(client).getOrElseUpdate(part.getClass, {
|
||||
def heirachy(clazz:Class[_]):Seq[Class[_]] =
|
||||
{
|
||||
var superClasses:Seq[Class[_]] = clazz.getInterfaces.flatMap(c => heirachy(c)):+clazz
|
||||
if(clazz.getSuperclass != null)
|
||||
superClasses = superClasses++heirachy(clazz.getSuperclass)
|
||||
return superClasses
|
||||
}
|
||||
private def partTraitMap(client: Boolean) =
|
||||
if (client) partTraitMap_c else partTraitMap_s
|
||||
private def interfaceTraitMap(client: Boolean) =
|
||||
if (client) interfaceTraitMap_c else interfaceTraitMap_s
|
||||
|
||||
val map = interfaceTraitMap(client)
|
||||
val traits = heirachy(part.getClass).flatMap(c => map.get(c.nodeName)).distinct
|
||||
private def traitsForPart(part: TMultiPart, client: Boolean) =
|
||||
partTraitMap(client).getOrElseUpdate(
|
||||
part.getClass, {
|
||||
def heirachy(clazz: Class[_]): Seq[Class[_]] = {
|
||||
var superClasses: Seq[Class[_]] =
|
||||
clazz.getInterfaces.flatMap(c => heirachy(c)) :+ clazz
|
||||
if (clazz.getSuperclass != null)
|
||||
superClasses = superClasses ++ heirachy(clazz.getSuperclass)
|
||||
return superClasses
|
||||
}
|
||||
|
||||
val bitset = new BitSet
|
||||
traits.map(MultipartMixinFactory.getId).foreach(bitset.set)
|
||||
bitset
|
||||
})
|
||||
val map = interfaceTraitMap(client)
|
||||
val traits =
|
||||
heirachy(part.getClass).flatMap(c => map.get(c.nodeName)).distinct
|
||||
|
||||
private def setTraits(part:TMultiPart, client:Boolean):BitSet = setTraits(Seq(part), client)
|
||||
|
||||
private def setTraits(parts:Iterable[TMultiPart], client:Boolean) = {
|
||||
val bitset = freshBitSet
|
||||
parts.foreach(p => bitset.or(traitsForPart(p, client)))
|
||||
if(client) bitset.set(clientTraitId)
|
||||
val bitset = new BitSet
|
||||
traits.map(MultipartMixinFactory.getId).foreach(bitset.set)
|
||||
bitset
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Check if part adds any new interfaces to tile, if so, replace tile with a new copy and call tile.addPart(part)
|
||||
* returns true if tile was replaced
|
||||
*/
|
||||
private[multipart] def addPart(world:World, pos:BlockCoord, part:TMultiPart):TileMultipart =
|
||||
{
|
||||
val (tile, converted) = TileMultipart.getOrConvertTile2(world, pos)
|
||||
val bitset = setTraits(part, world.isRemote)
|
||||
private def setTraits(part: TMultiPart, client: Boolean): BitSet =
|
||||
setTraits(Seq(part), client)
|
||||
|
||||
var ntile = tile
|
||||
if(ntile != null) {
|
||||
if(converted) {//perform client conversion
|
||||
ntile.partList(0).invalidateConvertedTile()
|
||||
world.setBlock(pos.x, pos.y, pos.z, MultipartProxy.block, 0, 0)
|
||||
silentAddTile(world, pos, ntile)
|
||||
PacketCustom.sendToChunk(new S23PacketBlockChange(pos.x, pos.y, pos.z, world), world, pos.x>>4, pos.z>>4)
|
||||
ntile.partList(0).onConverted()
|
||||
ntile.writeAddPart(ntile.partList(0))
|
||||
}
|
||||
private def setTraits(parts: Iterable[TMultiPart], client: Boolean) = {
|
||||
val bitset = freshBitSet
|
||||
parts.foreach(p => bitset.or(traitsForPart(p, client)))
|
||||
if (client) bitset.set(clientTraitId)
|
||||
bitset
|
||||
}
|
||||
|
||||
val tileTraits = tileTraitMap(tile.getClass)
|
||||
bitset.andNot(tileTraits)
|
||||
if(!bitset.isEmpty) {
|
||||
bitset.or(tileTraits)
|
||||
ntile = MultipartMixinFactory.construct(bitset)
|
||||
tile.setValid(false)
|
||||
silentAddTile(world, pos, ntile)
|
||||
ntile.from(tile)
|
||||
}
|
||||
}
|
||||
else {
|
||||
world.setBlock(pos.x, pos.y, pos.z, MultipartProxy.block, 0, 0)
|
||||
ntile = MultipartMixinFactory.construct(bitset)
|
||||
silentAddTile(world, pos, ntile)
|
||||
}
|
||||
ntile.addPart_impl(part)
|
||||
return ntile
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a tile entity to the world without notifying neighbor blocks or adding it to the tick list
|
||||
*/
|
||||
def silentAddTile(world:World, pos:BlockCoord, tile:TileEntity) {
|
||||
val chunk = world.getChunkFromBlockCoords(pos.x, pos.z)
|
||||
if(chunk != null)
|
||||
chunk.func_150812_a(pos.x & 15, pos.y, pos.z & 15, tile)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if tile satisfies all the interfaces required by parts. If not, return a new generated copy of tile
|
||||
*/
|
||||
private[multipart] def generateCompositeTile(tile:TileEntity, parts:Iterable[TMultiPart], client:Boolean) = {
|
||||
val bitset = setTraits(parts, client)
|
||||
if(tile != null && tile.isInstanceOf[TileMultipart] && bitset == tileTraitMap(tile.getClass))
|
||||
tile.asInstanceOf[TileMultipart]
|
||||
else
|
||||
MultipartMixinFactory.construct(bitset)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are any redundant interfaces on tile, if so, replace tile with new copy
|
||||
*/
|
||||
private[multipart] def partRemoved(tile:TileMultipart) = {
|
||||
val ntile = generateCompositeTile(tile, tile.partList, tile.getWorldObj.isRemote)
|
||||
if(ntile != tile) {
|
||||
tile.setValid(false)
|
||||
silentAddTile(tile.getWorldObj, new BlockCoord(tile), ntile)
|
||||
ntile.from(tile)
|
||||
ntile.notifyTileChange()
|
||||
}
|
||||
ntile
|
||||
}
|
||||
|
||||
/**
|
||||
* register s_trait to be applied to tiles containing parts implementing s_interface
|
||||
*/
|
||||
def registerTrait(s_interface:String, s_trait:String):Unit = registerTrait(s_interface, s_trait, s_trait)
|
||||
|
||||
/**
|
||||
* register traits to be applied to tiles containing parts implementing s_interface
|
||||
* c_trait for client worlds (may be null)
|
||||
* s_trait for server worlds (may be null)
|
||||
*/
|
||||
def registerTrait(s_interface$:String, c_trait$:String, s_trait$:String) {
|
||||
val s_interface = nodeName(s_interface$)
|
||||
val c_trait = nodeName(c_trait$)
|
||||
val s_trait = nodeName(s_trait$)
|
||||
/** Check if part adds any new interfaces to tile, if so, replace tile with a
|
||||
* new copy and call tile.addPart(part) returns true if tile was replaced
|
||||
*/
|
||||
private[multipart] def addPart(
|
||||
world: World,
|
||||
pos: BlockCoord,
|
||||
part: TMultiPart
|
||||
): TileMultipart = {
|
||||
val (tile, converted) = TileMultipart.getOrConvertTile2(world, pos)
|
||||
val bitset = setTraits(part, world.isRemote)
|
||||
|
||||
def registerSide(map:Map[String, String], traitName:String) = if(traitName != null) {
|
||||
if(map.contains(s_interface))
|
||||
logger.error("Trait already registered for "+s_interface)
|
||||
else {
|
||||
map.put(s_interface, traitName)
|
||||
MultipartMixinFactory.registerTrait(traitName)
|
||||
}
|
||||
}
|
||||
registerSide(interfaceTraitMap_c, c_trait)
|
||||
registerSide(interfaceTraitMap_s, s_trait)
|
||||
}
|
||||
|
||||
def registerPassThroughInterface(s_interface:String):Unit = registerPassThroughInterface(s_interface, true, true)
|
||||
|
||||
/**
|
||||
* A passthrough interface, is an interface to be implemented on the container tile instance, for which all calls are passed directly to the single implementing part.
|
||||
* Registering a passthrough interface is equivalent to defining a mixin class as follows.
|
||||
* 1. field 'impl' which contains the reference to the corresponding part
|
||||
* 2. occlusionTest is overriden to prevent more than one part with s_interface existing in the block space
|
||||
* 3. implementing s_interface and passing all calls directly to the part instance.
|
||||
*
|
||||
* This allows compatibility with APIs that expect interfaces on the tile entity.
|
||||
*/
|
||||
def registerPassThroughInterface(s_interface:String, client:Boolean, server:Boolean) {
|
||||
val tType = MultipartMixinFactory.generatePassThroughTrait(s_interface)
|
||||
if(tType == null)
|
||||
return
|
||||
var ntile = tile
|
||||
if (ntile != null) {
|
||||
if (converted) { // perform client conversion
|
||||
ntile.partList(0).invalidateConvertedTile()
|
||||
world.setBlock(pos.x, pos.y, pos.z, MultipartProxy.block, 0, 0)
|
||||
silentAddTile(world, pos, ntile)
|
||||
PacketCustom.sendToChunk(
|
||||
new S23PacketBlockChange(pos.x, pos.y, pos.z, world),
|
||||
world,
|
||||
pos.x >> 4,
|
||||
pos.z >> 4
|
||||
)
|
||||
ntile.partList(0).onConverted()
|
||||
ntile.writeAddPart(ntile.partList(0))
|
||||
}
|
||||
|
||||
registerTrait(s_interface, if(client) tType else null, if(server) tType else null)
|
||||
val tileTraits = tileTraitMap(tile.getClass)
|
||||
bitset.andNot(tileTraits)
|
||||
if (!bitset.isEmpty) {
|
||||
bitset.or(tileTraits)
|
||||
ntile = MultipartMixinFactory.construct(bitset)
|
||||
tile.setValid(false)
|
||||
silentAddTile(world, pos, ntile)
|
||||
ntile.from(tile)
|
||||
}
|
||||
} else {
|
||||
world.setBlock(pos.x, pos.y, pos.z, MultipartProxy.block, 0, 0)
|
||||
ntile = MultipartMixinFactory.construct(bitset)
|
||||
silentAddTile(world, pos, ntile)
|
||||
}
|
||||
|
||||
private[multipart] def registerTileClass(clazz:Class[_ <: TileMultipart], traits:BitSet) {
|
||||
tileTraitMap.put(clazz, traits.copy)
|
||||
MultipartProxy.onTileClassBuilt(clazz)
|
||||
ntile.addPart_impl(part)
|
||||
return ntile
|
||||
}
|
||||
|
||||
/** Adds a tile entity to the world without notifying neighbor blocks or
|
||||
* adding it to the tick list
|
||||
*/
|
||||
def silentAddTile(world: World, pos: BlockCoord, tile: TileEntity) {
|
||||
val chunk = world.getChunkFromBlockCoords(pos.x, pos.z)
|
||||
if (chunk != null)
|
||||
chunk.func_150812_a(pos.x & 15, pos.y, pos.z & 15, tile)
|
||||
}
|
||||
|
||||
/** Check if tile satisfies all the interfaces required by parts. If not,
|
||||
* return a new generated copy of tile
|
||||
*/
|
||||
private[multipart] def generateCompositeTile(
|
||||
tile: TileEntity,
|
||||
parts: Iterable[TMultiPart],
|
||||
client: Boolean
|
||||
) = {
|
||||
val bitset = setTraits(parts, client)
|
||||
if (
|
||||
tile != null && tile
|
||||
.isInstanceOf[TileMultipart] && bitset == tileTraitMap(tile.getClass)
|
||||
)
|
||||
tile.asInstanceOf[TileMultipart]
|
||||
else
|
||||
MultipartMixinFactory.construct(bitset)
|
||||
}
|
||||
|
||||
/** Check if there are any redundant interfaces on tile, if so, replace tile
|
||||
* with new copy
|
||||
*/
|
||||
private[multipart] def partRemoved(tile: TileMultipart) = {
|
||||
val ntile =
|
||||
generateCompositeTile(tile, tile.partList, tile.getWorldObj.isRemote)
|
||||
if (ntile != tile) {
|
||||
tile.setValid(false)
|
||||
silentAddTile(tile.getWorldObj, new BlockCoord(tile), ntile)
|
||||
ntile.from(tile)
|
||||
ntile.notifyTileChange()
|
||||
}
|
||||
}
|
||||
ntile
|
||||
}
|
||||
|
||||
/** register s_trait to be applied to tiles containing parts implementing
|
||||
* s_interface
|
||||
*/
|
||||
def registerTrait(s_interface: String, s_trait: String): Unit =
|
||||
registerTrait(s_interface, s_trait, s_trait)
|
||||
|
||||
/** register traits to be applied to tiles containing parts implementing
|
||||
* s_interface c_trait for client worlds (may be null) s_trait for server
|
||||
* worlds (may be null)
|
||||
*/
|
||||
def registerTrait(
|
||||
s_interface$ : String,
|
||||
c_trait$ : String,
|
||||
s_trait$ : String
|
||||
) {
|
||||
val s_interface = nodeName(s_interface$)
|
||||
val c_trait = nodeName(c_trait$)
|
||||
val s_trait = nodeName(s_trait$)
|
||||
|
||||
def registerSide(map: Map[String, String], traitName: String) = if (
|
||||
traitName != null
|
||||
) {
|
||||
if (map.contains(s_interface))
|
||||
logger.error("Trait already registered for " + s_interface)
|
||||
else {
|
||||
map.put(s_interface, traitName)
|
||||
MultipartMixinFactory.registerTrait(traitName)
|
||||
}
|
||||
}
|
||||
registerSide(interfaceTraitMap_c, c_trait)
|
||||
registerSide(interfaceTraitMap_s, s_trait)
|
||||
}
|
||||
|
||||
def registerPassThroughInterface(s_interface: String): Unit =
|
||||
registerPassThroughInterface(s_interface, true, true)
|
||||
|
||||
/** A passthrough interface, is an interface to be implemented on the
|
||||
* container tile instance, for which all calls are passed directly to the
|
||||
* single implementing part. Registering a passthrough interface is
|
||||
* equivalent to defining a mixin class as follows.
|
||||
* 1. field 'impl' which contains the reference to the corresponding part
|
||||
* 2. occlusionTest is overriden to prevent more than one part with
|
||||
* s_interface existing in the block space 3. implementing s_interface
|
||||
* and passing all calls directly to the part instance.
|
||||
*
|
||||
* This allows compatibility with APIs that expect interfaces on the tile
|
||||
* entity.
|
||||
*/
|
||||
def registerPassThroughInterface(
|
||||
s_interface: String,
|
||||
client: Boolean,
|
||||
server: Boolean
|
||||
) {
|
||||
val tType = MultipartMixinFactory.generatePassThroughTrait(s_interface)
|
||||
if (tType == null)
|
||||
return
|
||||
|
||||
registerTrait(
|
||||
s_interface,
|
||||
if (client) tType else null,
|
||||
if (server) tType else null
|
||||
)
|
||||
}
|
||||
|
||||
private[multipart] def registerTileClass(
|
||||
clazz: Class[_ <: TileMultipart],
|
||||
traits: BitSet
|
||||
) {
|
||||
tileTraitMap.put(clazz, traits.copy)
|
||||
MultipartProxy.onTileClassBuilt(clazz)
|
||||
}
|
||||
}
|
||||
@@ -12,53 +12,56 @@ import net.minecraft.world.WorldServer
|
||||
import java.util.Arrays
|
||||
import java.lang.Iterable
|
||||
|
||||
/**
|
||||
* Static helper class for handling the unusual way that multipart tile entities load from nbt and send description packets
|
||||
* <br>
|
||||
* Multipart tile entities will all save themselves with the id "savedMultipart" which if normally loaded by minecraft,
|
||||
* will create a dummy tile entity which just holds the NBT it was read from. These dummies are then converted to actual container tiles on the ChunkLoad event.
|
||||
* The createTileFromNBT function should be used to construct a multipart tile from NBT without the ChunkLoad event.
|
||||
* <br>
|
||||
* Multipart tile entities do not send description packets via the conventional means of one packet per tile when PlayerInstance calls for it, to do so would be terribly inefficient.
|
||||
* Instead, the ChunkWatch event is used to batch all the describing data for a chunk into one packet which is compressed using relative positions.
|
||||
* The sendDescPacket(s) functions should be used to send the description packet of a tile or tiles without a ChunkWatch event.
|
||||
* <br>
|
||||
* An example of using this class to move blocks/tile entites around can be found at www.chickenbones.craftsaddle.org/Files/Other/ItemDevTool2.java
|
||||
*/
|
||||
object MultipartHelper
|
||||
{
|
||||
/*val f_playersInChunk = classOf[PlayerInstance].getDeclaredField(
|
||||
/** Static helper class for handling the unusual way that multipart tile
|
||||
* entities load from nbt and send description packets <br> Multipart tile
|
||||
* entities will all save themselves with the id "savedMultipart" which if
|
||||
* normally loaded by minecraft, will create a dummy tile entity which just
|
||||
* holds the NBT it was read from. These dummies are then converted to actual
|
||||
* container tiles on the ChunkLoad event. The createTileFromNBT function
|
||||
* should be used to construct a multipart tile from NBT without the ChunkLoad
|
||||
* event. <br> Multipart tile entities do not send description packets via the
|
||||
* conventional means of one packet per tile when PlayerInstance calls for it,
|
||||
* to do so would be terribly inefficient. Instead, the ChunkWatch event is
|
||||
* used to batch all the describing data for a chunk into one packet which is
|
||||
* compressed using relative positions. The sendDescPacket(s) functions should
|
||||
* be used to send the description packet of a tile or tiles without a
|
||||
* ChunkWatch event. <br> An example of using this class to move blocks/tile
|
||||
* entites around can be found at
|
||||
* www.chickenbones.craftsaddle.org/Files/Other/ItemDevTool2.java
|
||||
*/
|
||||
object MultipartHelper {
|
||||
/*val f_playersInChunk = classOf[PlayerInstance].getDeclaredField(
|
||||
new ObfMapping("net/minecraft/server/management/PlayerInstance", "playersInChunk", "Ljava/util/List;")
|
||||
.toRuntime.s_name)
|
||||
f_playersInChunk.setAccessible(true)
|
||||
|
||||
|
||||
def playersInChunk(inst:PlayerInstance) = f_playersInChunk.get(inst).asInstanceOf[List[_]]*/
|
||||
|
||||
def createTileFromNBT(world:World, tag:NBTTagCompound):TileEntity = {
|
||||
if(!tag.getString("id").equals("savedMultipart"))
|
||||
return null
|
||||
|
||||
MultipartSaveLoad.loadingWorld = world
|
||||
return TileMultipart.createFromNBT(tag)
|
||||
}
|
||||
|
||||
/**
|
||||
* Note. This method should only be used to send tiles that have been created on the server mid-game via an NBT load to clients.
|
||||
*/
|
||||
def sendDescPacket(world:World, tile:TileEntity) {
|
||||
val c = world.getChunkFromBlockCoords(tile.xCoord, tile.zCoord)
|
||||
val pkt = MultipartSPH.getDescPacket(c, Arrays.asList(tile).iterator)
|
||||
if(pkt != null)
|
||||
pkt.sendToChunk(world, c.xPosition, c.zPosition)
|
||||
}
|
||||
|
||||
/* Removed for compilation until PlayerInstance access transformer is pulled in forge
|
||||
|
||||
def createTileFromNBT(world: World, tag: NBTTagCompound): TileEntity = {
|
||||
if (!tag.getString("id").equals("savedMultipart"))
|
||||
return null
|
||||
|
||||
MultipartSaveLoad.loadingWorld = world
|
||||
return TileMultipart.createFromNBT(tag)
|
||||
}
|
||||
|
||||
/** Note. This method should only be used to send tiles that have been created
|
||||
* on the server mid-game via an NBT load to clients.
|
||||
*/
|
||||
def sendDescPacket(world: World, tile: TileEntity) {
|
||||
val c = world.getChunkFromBlockCoords(tile.xCoord, tile.zCoord)
|
||||
val pkt = MultipartSPH.getDescPacket(c, Arrays.asList(tile).iterator)
|
||||
if (pkt != null)
|
||||
pkt.sendToChunk(world, c.xPosition, c.zPosition)
|
||||
}
|
||||
|
||||
/* Removed for compilation until PlayerInstance access transformer is pulled in forge
|
||||
def sendDescPackets(world:World, tiles:Iterable[TileEntity]) {
|
||||
val map = LinkedListMultimap.create[Long, TileEntity]()
|
||||
tiles.filter(_.isInstanceOf[TileMultipart]).foreach(t => map.put(t.xCoord.toLong<<32|t.zCoord, t))
|
||||
|
||||
|
||||
val mgr = world.asInstanceOf[WorldServer].getPlayerManager
|
||||
map.asMap.entrySet.foreach{e =>
|
||||
map.asMap.entrySet.foreach{e =>
|
||||
val coord = e.getKey
|
||||
val c = world.getChunkFromBlockCoords((coord>>32).toInt, coord.toInt)
|
||||
lazy val pkt = MultipartSPH.getDescPacket(c, e.getValue.iterator)
|
||||
@@ -68,40 +71,42 @@ object MultipartHelper
|
||||
}
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Helper class for converting tile entities to multiparts on chunk load
|
||||
*/
|
||||
abstract class IPartTileConverter[T <: TileEntity](val clazz:Class[T])
|
||||
{
|
||||
/**
|
||||
* @return true if this tile can be converted by this converter. If canConvert returns true, but convertMulti returns no parts, the tile will be deleted
|
||||
*/
|
||||
def canConvert(tile:TileEntity) = clazz.isInstance(tile)
|
||||
/** Helper class for converting tile entities to multiparts on chunk load
|
||||
*/
|
||||
abstract class IPartTileConverter[T <: TileEntity](val clazz: Class[T]) {
|
||||
|
||||
def convert(tile:TileEntity) = convertMulti(tile.asInstanceOf[T])
|
||||
/** @return
|
||||
* true if this tile can be converted by this converter. If canConvert
|
||||
* returns true, but convertMulti returns no parts, the tile will be
|
||||
* deleted
|
||||
*/
|
||||
def canConvert(tile: TileEntity) = clazz.isInstance(tile)
|
||||
|
||||
/**
|
||||
* Convert the TileEntity into multiple parts
|
||||
* @return An iterable list of parts or an empty list to delete the tile
|
||||
*/
|
||||
def convertMulti(tile:T):Iterable[TMultiPart] = convertOne(tile) match {
|
||||
case null => Seq()
|
||||
case p => Seq(p)
|
||||
}
|
||||
/**
|
||||
* Convert the TileEntity into a part
|
||||
* @return The converted part, or null to delete the tile
|
||||
*/
|
||||
def convertOne(tile:T):TMultiPart
|
||||
def convert(tile: TileEntity) = convertMulti(tile.asInstanceOf[T])
|
||||
|
||||
/** Convert the TileEntity into multiple parts
|
||||
* @return
|
||||
* An iterable list of parts or an empty list to delete the tile
|
||||
*/
|
||||
def convertMulti(tile: T): Iterable[TMultiPart] = convertOne(tile) match {
|
||||
case null => Seq()
|
||||
case p => Seq(p)
|
||||
}
|
||||
|
||||
def registerTileConverter(converter:IPartTileConverter[_]) {
|
||||
MultipartSaveLoad.converters += converter
|
||||
}
|
||||
/** Convert the TileEntity into a part
|
||||
* @return
|
||||
* The converted part, or null to delete the tile
|
||||
*/
|
||||
def convertOne(tile: T): TMultiPart
|
||||
}
|
||||
|
||||
def createTileFromParts(parts:Iterable[TMultiPart]) = {
|
||||
val tile = MultipartGenerator.generateCompositeTile(null, parts, false)
|
||||
tile.loadParts(parts)
|
||||
tile
|
||||
}
|
||||
}
|
||||
def registerTileConverter(converter: IPartTileConverter[_]) {
|
||||
MultipartSaveLoad.converters += converter
|
||||
}
|
||||
|
||||
def createTileFromParts(parts: Iterable[TMultiPart]) = {
|
||||
val tile = MultipartGenerator.generateCompositeTile(null, parts, false)
|
||||
tile.loadParts(parts)
|
||||
tile
|
||||
}
|
||||
}
|
||||
@@ -15,60 +15,83 @@ import cpw.mods.fml.relauncher.Side
|
||||
import codechicken.lib.raytracer.ExtendedMOP
|
||||
import codechicken.lib.lighting.LightMatrix
|
||||
|
||||
/**
|
||||
* Internal class for rendering callbacks. Should be moved to the handler package
|
||||
*/
|
||||
/** Internal class for rendering callbacks. Should be moved to the handler
|
||||
* package
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
object MultipartRenderer extends TileEntitySpecialRenderer with ISimpleBlockRenderingHandler
|
||||
{
|
||||
TileMultipart.renderID = RenderingRegistry.getNextAvailableRenderId
|
||||
var pass:Int = 0
|
||||
|
||||
override def renderTileEntityAt(t:TileEntity, x:Double, y:Double, z:Double, f:Float)
|
||||
{
|
||||
val tmpart = t.asInstanceOf[TileMultipartClient]
|
||||
if(tmpart.partList.isEmpty)
|
||||
return
|
||||
object MultipartRenderer
|
||||
extends TileEntitySpecialRenderer
|
||||
with ISimpleBlockRenderingHandler {
|
||||
TileMultipart.renderID = RenderingRegistry.getNextAvailableRenderId
|
||||
var pass: Int = 0
|
||||
|
||||
CCRenderState.reset()
|
||||
CCRenderState.pullLightmap()
|
||||
CCRenderState.useNormals = true
|
||||
|
||||
val pos = new Vector3(x, y, z)
|
||||
tmpart.renderDynamic(pos, f, pass)
|
||||
override def renderTileEntityAt(
|
||||
t: TileEntity,
|
||||
x: Double,
|
||||
y: Double,
|
||||
z: Double,
|
||||
f: Float
|
||||
) {
|
||||
val tmpart = t.asInstanceOf[TileMultipartClient]
|
||||
if (tmpart.partList.isEmpty)
|
||||
return
|
||||
|
||||
CCRenderState.reset()
|
||||
CCRenderState.pullLightmap()
|
||||
CCRenderState.useNormals = true
|
||||
|
||||
val pos = new Vector3(x, y, z)
|
||||
tmpart.renderDynamic(pos, f, pass)
|
||||
}
|
||||
|
||||
override def getRenderId = TileMultipart.renderID
|
||||
|
||||
override def renderWorldBlock(
|
||||
world: IBlockAccess,
|
||||
x: Int,
|
||||
y: Int,
|
||||
z: Int,
|
||||
block: Block,
|
||||
modelId: Int,
|
||||
renderer: RenderBlocks
|
||||
): Boolean = {
|
||||
val t = world.getTileEntity(x, y, z)
|
||||
if (!t.isInstanceOf[TileMultipartClient])
|
||||
return false
|
||||
|
||||
val tmpart = t.asInstanceOf[TileMultipartClient]
|
||||
if (tmpart.partList.isEmpty)
|
||||
return false
|
||||
|
||||
if (renderer.hasOverrideBlockTexture) {
|
||||
val hit = Minecraft.getMinecraft.objectMouseOver
|
||||
if (
|
||||
hit != null && hit.blockX == x && hit.blockY == y && hit.blockZ == z && ExtendedMOP
|
||||
.getData(hit)
|
||||
.isInstanceOf[(_, _)]
|
||||
) {
|
||||
val hitInfo: (Int, _) = ExtendedMOP.getData(hit)
|
||||
if (
|
||||
hitInfo._1.isInstanceOf[
|
||||
Int
|
||||
] && hitInfo._1 >= 0 && hitInfo._1 < tmpart.partList.size
|
||||
)
|
||||
tmpart.partList(hitInfo._1).drawBreaking(renderer)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override def getRenderId = TileMultipart.renderID
|
||||
|
||||
override def renderWorldBlock(world:IBlockAccess, x:Int, y:Int, z:Int, block:Block, modelId:Int, renderer:RenderBlocks):Boolean =
|
||||
{
|
||||
val t = world.getTileEntity(x, y, z)
|
||||
if(!t.isInstanceOf[TileMultipartClient])
|
||||
return false
|
||||
|
||||
val tmpart = t.asInstanceOf[TileMultipartClient]
|
||||
if(tmpart.partList.isEmpty)
|
||||
return false
|
||||
|
||||
if(renderer.hasOverrideBlockTexture)
|
||||
{
|
||||
val hit = Minecraft.getMinecraft.objectMouseOver
|
||||
if(hit != null && hit.blockX == x && hit.blockY == y && hit.blockZ == z && ExtendedMOP.getData(hit).isInstanceOf[(_, _)])
|
||||
{
|
||||
val hitInfo:(Int, _) = ExtendedMOP.getData(hit)
|
||||
if(hitInfo._1.isInstanceOf[Int] && hitInfo._1 >= 0 && hitInfo._1 < tmpart.partList.size)
|
||||
tmpart.partList(hitInfo._1).drawBreaking(renderer)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
CCRenderState.reset()
|
||||
CCRenderState.lightMatrix.locate(world, x, y, z)
|
||||
return tmpart.renderStatic(new Vector3(x, y, z), pass)
|
||||
}
|
||||
|
||||
override def renderInventoryBlock(block:Block, meta:Int, modelId:Int, renderer:RenderBlocks) {}
|
||||
CCRenderState.reset()
|
||||
CCRenderState.lightMatrix.locate(world, x, y, z)
|
||||
return tmpart.renderStatic(new Vector3(x, y, z), pass)
|
||||
}
|
||||
|
||||
override def renderInventoryBlock(
|
||||
block: Block,
|
||||
meta: Int,
|
||||
modelId: Int,
|
||||
renderer: RenderBlocks
|
||||
) {}
|
||||
|
||||
def shouldRender3DInInventory(modelId:Int) = false
|
||||
def shouldRender3DInInventory(modelId: Int) = false
|
||||
}
|
||||
@@ -4,79 +4,77 @@ import scala.collection.mutable.{Map => MMap}
|
||||
import codechicken.lib.data.MCDataOutput
|
||||
import codechicken.lib.data.MCDataInput
|
||||
|
||||
/**
|
||||
* Static class for packing update data.
|
||||
* When a specific property of a part changes and needs sending to the client, a bit can be set in the mask.
|
||||
* This bit can then be checked in the writeScheduled callback.
|
||||
* This prevents sending multiple packets if the same property updates more than once per tick.
|
||||
*/
|
||||
object PacketScheduler
|
||||
{
|
||||
private val map = MMap[TMultiPart, Long]()
|
||||
|
||||
/**
|
||||
* Add bits to the current update mask for part. (binary OR)
|
||||
*/
|
||||
def schedulePacket(part:TMultiPart, mask:Long) {
|
||||
if(part.world.isRemote)
|
||||
throw new IllegalArgumentException("Cannot use PacketScheduler on a client world")
|
||||
/** Static class for packing update data. When a specific property of a part
|
||||
* changes and needs sending to the client, a bit can be set in the mask. This
|
||||
* bit can then be checked in the writeScheduled callback. This prevents
|
||||
* sending multiple packets if the same property updates more than once per
|
||||
* tick.
|
||||
*/
|
||||
object PacketScheduler {
|
||||
private val map = MMap[TMultiPart, Long]()
|
||||
|
||||
map.put(part, map.getOrElse(part, 0L)|mask)
|
||||
}
|
||||
/** Add bits to the current update mask for part. (binary OR)
|
||||
*/
|
||||
def schedulePacket(part: TMultiPart, mask: Long) {
|
||||
if (part.world.isRemote)
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot use PacketScheduler on a client world"
|
||||
)
|
||||
|
||||
private[multipart] def sendScheduled() {
|
||||
map.foreach{ e =>
|
||||
val (part, mask) = e
|
||||
if(part.tile != null) {
|
||||
val ipart = part.asInstanceOf[IScheduledPacketPart]
|
||||
val w = part.getWriteStream
|
||||
ipart.maskWidth match {
|
||||
case 1 => w.writeByte(mask.toInt)
|
||||
case 2 => w.writeShort(mask.toInt)
|
||||
case 4 => w.writeInt(mask.toInt)
|
||||
case 8 => w.writeLong(mask)
|
||||
}
|
||||
|
||||
ipart.writeScheduled(mask, w)
|
||||
}
|
||||
map.put(part, map.getOrElse(part, 0L) | mask)
|
||||
}
|
||||
|
||||
private[multipart] def sendScheduled() {
|
||||
map.foreach { e =>
|
||||
val (part, mask) = e
|
||||
if (part.tile != null) {
|
||||
val ipart = part.asInstanceOf[IScheduledPacketPart]
|
||||
val w = part.getWriteStream
|
||||
ipart.maskWidth match {
|
||||
case 1 => w.writeByte(mask.toInt)
|
||||
case 2 => w.writeShort(mask.toInt)
|
||||
case 4 => w.writeInt(mask.toInt)
|
||||
case 8 => w.writeLong(mask)
|
||||
}
|
||||
map.clear()
|
||||
|
||||
ipart.writeScheduled(mask, w)
|
||||
}
|
||||
}
|
||||
map.clear()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback interface for PacketScheduler
|
||||
*/
|
||||
trait IScheduledPacketPart
|
||||
{
|
||||
/**
|
||||
* Write scheduled data to the packet, mask is the cumulative mask from calls to schedulePacket
|
||||
*/
|
||||
def writeScheduled(mask:Long, packet:MCDataOutput)
|
||||
|
||||
/**
|
||||
* Returns the width (in bytes) of the data type required to hold all valid mask bits. Valid values are 1, 2, 4 and 8
|
||||
*/
|
||||
def maskWidth:Int
|
||||
|
||||
/**
|
||||
* Read data matching mask. Estiablishes a method for subclasses to override. This should be called from read
|
||||
*/
|
||||
def readScheduled(mask:Long, packet:MCDataInput)
|
||||
/** Callback interface for PacketScheduler
|
||||
*/
|
||||
trait IScheduledPacketPart {
|
||||
|
||||
/** Write scheduled data to the packet, mask is the cumulative mask from calls
|
||||
* to schedulePacket
|
||||
*/
|
||||
def writeScheduled(mask: Long, packet: MCDataOutput)
|
||||
|
||||
/** Returns the width (in bytes) of the data type required to hold all valid
|
||||
* mask bits. Valid values are 1, 2, 4 and 8
|
||||
*/
|
||||
def maskWidth: Int
|
||||
|
||||
/** Read data matching mask. Estiablishes a method for subclasses to override.
|
||||
* This should be called from read
|
||||
*/
|
||||
def readScheduled(mask: Long, packet: MCDataInput)
|
||||
}
|
||||
|
||||
trait TScheduledPacketPart extends TMultiPart with IScheduledPacketPart
|
||||
{
|
||||
final override def read(packet:MCDataInput) {
|
||||
val mask = maskWidth match {
|
||||
case 1 => packet.readUByte
|
||||
case 2 => packet.readUShort
|
||||
case 4 => packet.readInt
|
||||
case 8 => packet.readLong
|
||||
}
|
||||
readScheduled(mask, packet)
|
||||
trait TScheduledPacketPart extends TMultiPart with IScheduledPacketPart {
|
||||
final override def read(packet: MCDataInput) {
|
||||
val mask = maskWidth match {
|
||||
case 1 => packet.readUByte
|
||||
case 2 => packet.readUShort
|
||||
case 4 => packet.readInt
|
||||
case 8 => packet.readLong
|
||||
}
|
||||
|
||||
def writeScheduled(mask:Long, packet:MCDataOutput){}
|
||||
def readScheduled(mask:Long, packet:MCDataInput){}
|
||||
}
|
||||
readScheduled(mask, packet)
|
||||
}
|
||||
|
||||
def writeScheduled(mask: Long, packet: MCDataOutput) {}
|
||||
def readScheduled(mask: Long, packet: MCDataInput) {}
|
||||
}
|
||||
@@ -1,10 +1,11 @@
|
||||
package codechicken.multipart;
|
||||
|
||||
/**
|
||||
* Defines what each slot in a multipart tile corresponds to and provides some utility functions.
|
||||
* For performance reasons, it is recommended that integer constants be useds as opposed to this enum
|
||||
* Defines what each slot in a multipart tile corresponds to and provides some utility functions. For performance
|
||||
* reasons, it is recommended that integer constants be useds as opposed to this enum
|
||||
*/
|
||||
public enum PartMap {
|
||||
|
||||
BOTTOM(0),
|
||||
TOP(1),
|
||||
NORTH(2),
|
||||
@@ -63,9 +64,9 @@ public enum PartMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a 3 bit mask of the axis xzy that are variable in this edge.
|
||||
* For example, the first 4 edges (15-18) are along the Y axis, variable in x and z, so the mask is 110b. Note axis y is 1<<0, z is 1<<1 and x is 1<<2
|
||||
* Note the parameter e is relative to the first edge slot and can range from 0-11
|
||||
* Returns a 3 bit mask of the axis xzy that are variable in this edge. For example, the first 4 edges (15-18) are
|
||||
* along the Y axis, variable in x and z, so the mask is 110b. Note axis y is 1<<0, z is 1<<1 and x is 1<<2 Note the
|
||||
* parameter e is relative to the first edge slot and can range from 0-11
|
||||
*/
|
||||
public static int edgeAxisMask(int e) {
|
||||
switch (e >> 2) {
|
||||
@@ -80,9 +81,9 @@ public enum PartMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks an edge index, to a mask where high values indicate positive positions in that axis.
|
||||
* Note the parameter e is relative to the first edge slot and can range from 0-11
|
||||
* For example, edge 1 (slot 16), is EDGE_NYP so the mask returned would be 010 as z is positive.
|
||||
* Unpacks an edge index, to a mask where high values indicate positive positions in that axis. Note the parameter e
|
||||
* is relative to the first edge slot and can range from 0-11 For example, edge 1 (slot 16), is EDGE_NYP so the mask
|
||||
* returned would be 010 as z is positive.
|
||||
*/
|
||||
public static int unpackEdgeBits(int e) {
|
||||
switch (e >> 2) {
|
||||
@@ -97,8 +98,8 @@ public enum PartMap {
|
||||
}
|
||||
|
||||
/**
|
||||
* Repacks a mask of axis bits indicating positive positions, into an edge in along the same axis as e.
|
||||
* Note the parameter e is relative to the first edge slot and can range from 0-11
|
||||
* Repacks a mask of axis bits indicating positive positions, into an edge in along the same axis as e. Note the
|
||||
* parameter e is relative to the first edge slot and can range from 0-11
|
||||
*/
|
||||
public static int packEdgeBits(int e, int bits) {
|
||||
switch (e >> 2) {
|
||||
@@ -112,12 +113,8 @@ public enum PartMap {
|
||||
throw new IllegalArgumentException("Switch Falloff");
|
||||
}
|
||||
|
||||
private static int[] edgeBetweenMap = new int[] {
|
||||
-1, -1, 8, 10, 4, 5,
|
||||
-1, -1, 9, 11, 6, 7,
|
||||
-1, -1, -1, -1, 0, 2,
|
||||
-1, -1, -1, -1, 1, 3
|
||||
};
|
||||
private static int[] edgeBetweenMap = new int[] { -1, -1, 8, 10, 4, 5, -1, -1, 9, 11, 6, 7, -1, -1, -1, -1, 0, 2,
|
||||
-1, -1, -1, -1, 1, 3 };
|
||||
|
||||
/**
|
||||
* Returns the slot of the edge between 2 sides.
|
||||
|
||||
@@ -10,30 +10,32 @@ import codechicken.lib.vec.Translation
|
||||
import codechicken.lib.render.uv.IconTransformation
|
||||
import cpw.mods.fml.relauncher.{Side, SideOnly}
|
||||
|
||||
/**
|
||||
* Java class implementation
|
||||
*/
|
||||
/** Java class implementation
|
||||
*/
|
||||
abstract class JCuboidPart extends TCuboidPart
|
||||
|
||||
/**
|
||||
* Trait for parts that are simply a cuboid, having one bounding box. Overrides multipart functions to this effect.
|
||||
*/
|
||||
trait TCuboidPart extends TMultiPart
|
||||
{
|
||||
/**
|
||||
* Return the bounding Cuboid6 for this part.
|
||||
*/
|
||||
def getBounds:Cuboid6
|
||||
|
||||
override def getSubParts:Iterable[IndexedCuboid6] = Seq(new IndexedCuboid6(0, getBounds))
|
||||
|
||||
override def getCollisionBoxes:Iterable[Cuboid6] = Seq(getBounds)
|
||||
/** Trait for parts that are simply a cuboid, having one bounding box. Overrides
|
||||
* multipart functions to this effect.
|
||||
*/
|
||||
trait TCuboidPart extends TMultiPart {
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def drawBreaking(renderBlocks:RenderBlocks)
|
||||
{
|
||||
CCRenderState.reset()
|
||||
CCRenderState.setPipeline(new Translation(x, y, z), new IconTransformation(renderBlocks.overrideBlockTexture))
|
||||
BlockRenderer.renderCuboid(getBounds, 0)
|
||||
}
|
||||
}
|
||||
/** Return the bounding Cuboid6 for this part.
|
||||
*/
|
||||
def getBounds: Cuboid6
|
||||
|
||||
override def getSubParts: Iterable[IndexedCuboid6] = Seq(
|
||||
new IndexedCuboid6(0, getBounds)
|
||||
)
|
||||
|
||||
override def getCollisionBoxes: Iterable[Cuboid6] = Seq(getBounds)
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def drawBreaking(renderBlocks: RenderBlocks) {
|
||||
CCRenderState.reset()
|
||||
CCRenderState.setPipeline(
|
||||
new Translation(x, y, z),
|
||||
new IconTransformation(renderBlocks.overrideBlockTexture)
|
||||
)
|
||||
BlockRenderer.renderCuboid(getBounds, 0)
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,11 @@
|
||||
package codechicken.multipart
|
||||
|
||||
/**
|
||||
* Interface which must be implemented by parts that go in an edge slot.
|
||||
*/
|
||||
trait TEdgePart extends TSlottedPart
|
||||
{
|
||||
/**
|
||||
* Return true if this part can conduct redstone signal or let redstone signal pass through it.
|
||||
*/
|
||||
def conductsRedstone = false
|
||||
}
|
||||
/** Interface which must be implemented by parts that go in an edge slot.
|
||||
*/
|
||||
trait TEdgePart extends TSlottedPart {
|
||||
|
||||
/** Return true if this part can conduct redstone signal or let redstone
|
||||
* signal pass through it.
|
||||
*/
|
||||
def conductsRedstone = false
|
||||
}
|
||||
@@ -1,16 +1,17 @@
|
||||
package codechicken.multipart
|
||||
|
||||
/**
|
||||
* Inteface which must be implemented by parts that go in a face part.
|
||||
*/
|
||||
trait TFacePart extends TSlottedPart
|
||||
{
|
||||
/**
|
||||
* Passed down from Block.isSideSolid. Return true if this part is solid and opaque on the specified side
|
||||
*/
|
||||
def solid(side:Int):Boolean = true
|
||||
/**
|
||||
* Return the redstone conduction map for which signal can pass through this part on the face. Eg, hollow covers return 0x10 as signal can pass through the center hole.
|
||||
*/
|
||||
def redstoneConductionMap = 0
|
||||
}
|
||||
/** Inteface which must be implemented by parts that go in a face part.
|
||||
*/
|
||||
trait TFacePart extends TSlottedPart {
|
||||
|
||||
/** Passed down from Block.isSideSolid. Return true if this part is solid and
|
||||
* opaque on the specified side
|
||||
*/
|
||||
def solid(side: Int): Boolean = true
|
||||
|
||||
/** Return the redstone conduction map for which signal can pass through this
|
||||
* part on the face. Eg, hollow covers return 0x10 as signal can pass through
|
||||
* the center hole.
|
||||
*/
|
||||
def redstoneConductionMap = 0
|
||||
}
|
||||
@@ -10,72 +10,83 @@ import codechicken.lib.vec.Vector3
|
||||
import codechicken.lib.raytracer.ExtendedMOP
|
||||
import codechicken.lib.render.EntityDigIconFX
|
||||
|
||||
/**
|
||||
* This suite of 3 classes provides simple functions for standard minecraft style hit and break particles.
|
||||
*
|
||||
* Scala|Java composition setup.
|
||||
* Due to the lack of mixin inheritance in Java, the classes are structured to suit both languages as follows.
|
||||
* IconHitEffects contains static implementations of the functions that would be overriden in TMultiPart
|
||||
* JIconHitEffects is the interface that should be implemented by a Java class,
|
||||
* which can then override the functions in TMultipart and call the static methods in IconHitEffects with 'this' as the first parameter
|
||||
* TIconHitEffects is a trait for scala implementors that does includes the overrides/static calls that Java programmers need to include themselves.
|
||||
*/
|
||||
object IconHitEffects
|
||||
{
|
||||
def addHitEffects(part:JIconHitEffects, hit:MovingObjectPosition, effectRenderer:EffectRenderer)
|
||||
{
|
||||
EntityDigIconFX.addBlockHitEffects(part.tile.getWorldObj,
|
||||
part.getBounds.copy.add(Vector3.fromTileEntity(part.tile)), hit.sideHit,
|
||||
part.getBreakingIcon(ExtendedMOP.getData(hit), hit.sideHit), effectRenderer)
|
||||
}
|
||||
|
||||
def addDestroyEffects(part:JIconHitEffects, effectRenderer:EffectRenderer)
|
||||
{
|
||||
addDestroyEffects(part, effectRenderer, true)
|
||||
}
|
||||
|
||||
def addDestroyEffects(part:JIconHitEffects, effectRenderer:EffectRenderer, scaleDensity:Boolean)
|
||||
{
|
||||
val icons = new Array[IIcon](6)
|
||||
for(i <- 0 until 6)
|
||||
icons(i) = part.getBrokenIcon(i)
|
||||
val bounds =
|
||||
if(scaleDensity) part.getBounds.copy
|
||||
else Cuboid6.full.copy
|
||||
EntityDigIconFX.addBlockDestroyEffects(part.tile.getWorldObj,
|
||||
bounds.add(Vector3.fromTileEntity(part.tile)), icons, effectRenderer)
|
||||
}
|
||||
/** This suite of 3 classes provides simple functions for standard minecraft
|
||||
* style hit and break particles.
|
||||
*
|
||||
* Scala|Java composition setup. Due to the lack of mixin inheritance in Java,
|
||||
* the classes are structured to suit both languages as follows. IconHitEffects
|
||||
* contains static implementations of the functions that would be overriden in
|
||||
* TMultiPart JIconHitEffects is the interface that should be implemented by a
|
||||
* Java class, which can then override the functions in TMultipart and call the
|
||||
* static methods in IconHitEffects with 'this' as the first parameter
|
||||
* TIconHitEffects is a trait for scala implementors that does includes the
|
||||
* overrides/static calls that Java programmers need to include themselves.
|
||||
*/
|
||||
object IconHitEffects {
|
||||
def addHitEffects(
|
||||
part: JIconHitEffects,
|
||||
hit: MovingObjectPosition,
|
||||
effectRenderer: EffectRenderer
|
||||
) {
|
||||
EntityDigIconFX.addBlockHitEffects(
|
||||
part.tile.getWorldObj,
|
||||
part.getBounds.copy.add(Vector3.fromTileEntity(part.tile)),
|
||||
hit.sideHit,
|
||||
part.getBreakingIcon(ExtendedMOP.getData(hit), hit.sideHit),
|
||||
effectRenderer
|
||||
)
|
||||
}
|
||||
|
||||
def addDestroyEffects(part: JIconHitEffects, effectRenderer: EffectRenderer) {
|
||||
addDestroyEffects(part, effectRenderer, true)
|
||||
}
|
||||
|
||||
def addDestroyEffects(
|
||||
part: JIconHitEffects,
|
||||
effectRenderer: EffectRenderer,
|
||||
scaleDensity: Boolean
|
||||
) {
|
||||
val icons = new Array[IIcon](6)
|
||||
for (i <- 0 until 6)
|
||||
icons(i) = part.getBrokenIcon(i)
|
||||
val bounds =
|
||||
if (scaleDensity) part.getBounds.copy
|
||||
else Cuboid6.full.copy
|
||||
EntityDigIconFX.addBlockDestroyEffects(
|
||||
part.tile.getWorldObj,
|
||||
bounds.add(Vector3.fromTileEntity(part.tile)),
|
||||
icons,
|
||||
effectRenderer
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Java interface containing callbacks for particle rendering.
|
||||
* Make sure to override addHitEffects and addDestroyEffects as in TIconHitEffects
|
||||
*/
|
||||
trait JIconHitEffects extends TMultiPart
|
||||
{
|
||||
def getBounds:Cuboid6
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
def getBreakingIcon(subPart:Any, side:Int):IIcon = getBrokenIcon(side)
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
def getBrokenIcon(side:Int):IIcon
|
||||
/** Java interface containing callbacks for particle rendering. Make sure to
|
||||
* override addHitEffects and addDestroyEffects as in TIconHitEffects
|
||||
*/
|
||||
trait JIconHitEffects extends TMultiPart {
|
||||
def getBounds: Cuboid6
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
def getBreakingIcon(subPart: Any, side: Int): IIcon = getBrokenIcon(side)
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
def getBrokenIcon(side: Int): IIcon
|
||||
}
|
||||
|
||||
/**
|
||||
* Trait for scala programmers
|
||||
*/
|
||||
trait TIconHitEffects extends JIconHitEffects
|
||||
{
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def addHitEffects(hit:MovingObjectPosition, effectRenderer:EffectRenderer)
|
||||
{
|
||||
IconHitEffects.addHitEffects(this, hit, effectRenderer)
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def addDestroyEffects(effectRenderer:EffectRenderer)
|
||||
{
|
||||
IconHitEffects.addDestroyEffects(this, effectRenderer)
|
||||
}
|
||||
}
|
||||
/** Trait for scala programmers
|
||||
*/
|
||||
trait TIconHitEffects extends JIconHitEffects {
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def addHitEffects(
|
||||
hit: MovingObjectPosition,
|
||||
effectRenderer: EffectRenderer
|
||||
) {
|
||||
IconHitEffects.addHitEffects(this, hit, effectRenderer)
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def addDestroyEffects(effectRenderer: EffectRenderer) {
|
||||
IconHitEffects.addDestroyEffects(this, effectRenderer)
|
||||
}
|
||||
}
|
||||
@@ -23,282 +23,338 @@ import net.minecraft.util.Vec3
|
||||
import codechicken.lib.raytracer.ExtendedMOP
|
||||
import codechicken.lib.raytracer.RayTracer
|
||||
|
||||
abstract class TMultiPart
|
||||
{
|
||||
/**
|
||||
* Reference to the container TileMultipart instance
|
||||
*/
|
||||
var tile:TileMultipart = _
|
||||
|
||||
/**
|
||||
* Legacy helper function for getting the tile entity (from when TileMultipart was a trait). Use tile() now.
|
||||
*/
|
||||
@Deprecated
|
||||
def getTile:TileEntity = tile
|
||||
/**
|
||||
* Getter for tile.worldObj
|
||||
*/
|
||||
def world = if(tile == null) null else tile.getWorldObj
|
||||
/**
|
||||
* Short getter for xCoord
|
||||
*/
|
||||
def x = tile.xCoord
|
||||
/**
|
||||
* Short getter for yCoord
|
||||
*/
|
||||
def y = tile.yCoord
|
||||
/**
|
||||
* Short getter for zCoord
|
||||
*/
|
||||
def z = tile.zCoord
|
||||
|
||||
/**
|
||||
* The unique string identifier for this class of multipart.
|
||||
*/
|
||||
def getType:String
|
||||
/**
|
||||
* Called when the container tile instance is changed to update reference
|
||||
*/
|
||||
def bind(t:TileMultipart)
|
||||
{
|
||||
tile = t
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform an occlusion test to determine whether this and npart can 'fit' in this block space.
|
||||
*/
|
||||
def occlusionTest(npart:TMultiPart):Boolean = true
|
||||
/**
|
||||
* Return a list of entity collision boxes.
|
||||
* Note all Cuboid6's returned by methods in TMultiPart should be within (0,0,0)->(1,1,1)
|
||||
*/
|
||||
def getCollisionBoxes:Iterable[Cuboid6] = Seq()
|
||||
/**
|
||||
* Perform a raytrace of this part. The default implementation does a Cuboid6 ray trace on bounding boxes returned from getSubParts.
|
||||
* This should only be overridden if you need special ray-tracing capabilities such as triangular faces.
|
||||
* The returned ExtendedMOP will be passed to methods such as 'activate' so it is recommended to use the data field to indicate information about the hit area.
|
||||
*/
|
||||
def collisionRayTrace(start: Vec3, end: Vec3): ExtendedMOP = {
|
||||
val offset = new Vector3(x, y, z)
|
||||
val boxes = getSubParts.map(c => new IndexedCuboid6(c.data, c.copy.add(offset)))
|
||||
return RayTracer.instance.rayTraceCuboids(new Vector3(start), new Vector3(end), boxes.toList,
|
||||
new BlockCoord(x, y, z), tile.getBlockType).asInstanceOf[ExtendedMOP]
|
||||
}
|
||||
/**
|
||||
* For the default collisionRayTrace implementation, returns a list of indexed bounding boxes. The data field of ExtendedMOP will be set to the index of the cuboid the raytrace hit.
|
||||
*/
|
||||
def getSubParts:Iterable[IndexedCuboid6] = Seq()
|
||||
|
||||
/**
|
||||
* Return a list of items that should be dropped when this part is destroyed.
|
||||
*/
|
||||
def getDrops:Iterable[ItemStack] = Seq()
|
||||
/**
|
||||
* Return a value indicating how hard this part is to break
|
||||
* @param hit An instance of ExtendedMOP from collisionRayTrace
|
||||
*/
|
||||
def getStrength(hit:MovingObjectPosition, player:EntityPlayer):Float = 1
|
||||
/**
|
||||
* Harvest this part, removing it from the container tile and dropping items if necessary.
|
||||
* @param hit An instance of ExtendedMOP from collisionRayTrace
|
||||
* @param player The player harvesting the part
|
||||
*/
|
||||
def harvest(hit:MovingObjectPosition, player:EntityPlayer)
|
||||
{
|
||||
if(!player.capabilities.isCreativeMode)
|
||||
tile.dropItems(getDrops)
|
||||
tile.remPart(this)
|
||||
}
|
||||
/**
|
||||
* The light level emitted by this part
|
||||
*/
|
||||
def getLightValue = 0
|
||||
abstract class TMultiPart {
|
||||
|
||||
/**
|
||||
* If any part returns true for this, torches can be placed. Vanilla hacks...
|
||||
*/
|
||||
def canPlaceTorchOnTop = false
|
||||
/** Reference to the container TileMultipart instance
|
||||
*/
|
||||
var tile: TileMultipart = _
|
||||
|
||||
/**
|
||||
* Explosion resistance of the host tile is the maximum explosion resistance of the contained parts
|
||||
* @param entity The entity responsible for this explosion
|
||||
* @return The resistance of this part the the explosion
|
||||
*/
|
||||
def explosionResistance(entity:Entity) = 0F
|
||||
/** Legacy helper function for getting the tile entity (from when
|
||||
* TileMultipart was a trait). Use tile() now.
|
||||
*/
|
||||
@Deprecated
|
||||
def getTile: TileEntity = tile
|
||||
|
||||
/**
|
||||
* Add particles and other effects when a player is mining this part
|
||||
* @param hit An instance of ExtendedMOP from collisionRayTrace
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def addHitEffects(hit:MovingObjectPosition, effectRenderer:EffectRenderer){}
|
||||
/**
|
||||
* Add particles and other effects when a player broke this part
|
||||
* @param hit An instance of ExtendedMOP from collisionRayTrace
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def addDestroyEffects(hit:MovingObjectPosition, effectRenderer:EffectRenderer) {addDestroyEffects(effectRenderer)}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Deprecated
|
||||
def addDestroyEffects(effectRenderer:EffectRenderer){}
|
||||
/**
|
||||
* Render the static, unmoving faces of this part into the world renderer.
|
||||
* The Tessellator is already drawing.
|
||||
* @param pass The render pass, 1 or 0
|
||||
* @return true if vertices were added to the tessellator
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def renderStatic(pos:Vector3, pass:Int) = false
|
||||
/**
|
||||
* Render the dynamic, changing faces of this part and other gfx as in a TESR.
|
||||
* The Tessellator will need to be started if it is to be used.
|
||||
* @param pos The position of this block space relative to the renderer, same as x, y, z passed to TESR.
|
||||
* @param frame The partial interpolation frame value for animations between ticks
|
||||
* @param pass The render pass, 1 or 0
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def renderDynamic(pos:Vector3, frame:Float, pass:Int){}
|
||||
/**
|
||||
* Draw the breaking overlay for this part. The overrideIcon in RenderBlocks will be set to the fracture icon.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def drawBreaking(renderBlocks:RenderBlocks){}
|
||||
/**
|
||||
* Override the drawing of the selection box around this part.
|
||||
* @param hit An instance of ExtendedMOP from collisionRayTrace
|
||||
* @return true if highlight rendering was overridden.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def drawHighlight(hit:MovingObjectPosition, player:EntityPlayer, frame:Float):Boolean = false
|
||||
/**
|
||||
* @return A Cuboid6 bounding the render of this part for frustum culling. The bounds are relative to the tile coordinates.
|
||||
*/
|
||||
def getRenderBounds = Cuboid6.full
|
||||
/** Getter for tile.worldObj
|
||||
*/
|
||||
def world = if (tile == null) null else tile.getWorldObj
|
||||
|
||||
/**
|
||||
* Write all the data required to describe a client version of this part to the packet.
|
||||
* Called serverside, when a client loads this part for the first time.
|
||||
*/
|
||||
def writeDesc(packet:MCDataOutput){}
|
||||
/**
|
||||
* Fill out this part with the description information contained in packet. Will be exactly as written from writeDesc.
|
||||
* Called clientside when a client loads this part for the first time.
|
||||
*/
|
||||
def readDesc(packet:MCDataInput){}
|
||||
/**
|
||||
* Save part to NBT (only called serverside)
|
||||
*/
|
||||
def save(tag:NBTTagCompound){}
|
||||
/**
|
||||
* Load part from NBT (only called serverside)
|
||||
*/
|
||||
def load(tag:NBTTagCompound){}
|
||||
/**
|
||||
* Gets a MCDataOutput instance for writing update data to clients with this part loaded.
|
||||
* The write stream functions as a buffer which is flushed in a compressed databurst packet at the end of the tick.
|
||||
*/
|
||||
def getWriteStream:MCDataOutput = tile.getWriteStream(this)
|
||||
/**
|
||||
* Read and operate on data written to getWriteStream. Ensure all data this part wrote is read even if it's not going to be used.
|
||||
* The default implementation assumes a call to sendDescUpdate as the only use of getWriteStream.
|
||||
*/
|
||||
def read(packet:MCDataInput)
|
||||
{
|
||||
readDesc(packet)
|
||||
tile.markRender()
|
||||
}
|
||||
/**
|
||||
* Quick and easy method to re-describe the whole part on the client. This will call read on the client which calls readDesc unless overriden.
|
||||
* Incremental changes should be sent rather than the whole description packet if possible.
|
||||
*/
|
||||
def sendDescUpdate() = writeDesc(getWriteStream)
|
||||
|
||||
/**
|
||||
* Called when a part is added or removed from this block space.
|
||||
* The part parameter may be null if several things have changed.
|
||||
*/
|
||||
def onPartChanged(part:TMultiPart){}
|
||||
/**
|
||||
* Called when a neighbor block changed
|
||||
*/
|
||||
def onNeighborChanged(){}
|
||||
/**
|
||||
* Called when this part is added to the block space
|
||||
*/
|
||||
def onAdded() = onWorldJoin()
|
||||
/**
|
||||
* Called when this part is removed from the block space
|
||||
*/
|
||||
def onRemoved() = onWorldSeparate()
|
||||
/**
|
||||
* Called when the containing chunk is loaded on the server.
|
||||
*/
|
||||
def onChunkLoad() = onWorldJoin()
|
||||
/**
|
||||
* Called when the containing chunk is unloaded on the server.
|
||||
*/
|
||||
def onChunkUnload() = onWorldSeparate()
|
||||
/**
|
||||
* Called when this part separates from the world (due to removal, chunk unload or other). Use this to sync with external data structures. Called on both client and server
|
||||
*/
|
||||
def onWorldSeparate(){}
|
||||
/**
|
||||
* Called when this part joins the world (due to placement, chunkload or frame move etc). Use this to sync with external data structures. Called on both client and server
|
||||
*/
|
||||
def onWorldJoin(){}
|
||||
/**
|
||||
* Called when this part is converted from a normal block/tile (only applicable if a converter has been registered)
|
||||
*/
|
||||
def onConverted() = onAdded()
|
||||
/**
|
||||
* Called when this part is converted from a normal block/tile (only applicable if a converter has been registered) before the original tile has been replaced
|
||||
* Use this to clear out things like inventory from the old tile.
|
||||
*/
|
||||
def invalidateConvertedTile(){}
|
||||
/**
|
||||
* Called when this part has been moved without a save/load.
|
||||
*/
|
||||
def onMoved() = onWorldJoin()
|
||||
/**
|
||||
* Called just before this part is actually removed from the container tile
|
||||
*/
|
||||
def preRemove(){}
|
||||
|
||||
/**
|
||||
* Return whether this part needs update ticks. This will only be called on addition/removal so it should be a constant for this instance.
|
||||
*/
|
||||
def doesTick = true
|
||||
/**
|
||||
* Called once per world tick. This will be called even if doesTick returns false if another part in the space needs ticks.
|
||||
*/
|
||||
def update(){}
|
||||
/**
|
||||
* Called when a scheduled tick is executed.
|
||||
*/
|
||||
def scheduledTick(){}
|
||||
/**
|
||||
* Sets a scheduledTick callback for this part ticks in the future. This is a world time value, so if the chunk is unloaded and reloaded some time later, the tick may fire immediately.
|
||||
*/
|
||||
def scheduleTick(ticks:Int) = TickScheduler.scheduleTick(this, ticks)
|
||||
|
||||
/**
|
||||
* Return the itemstack for the middle click pick-block function.
|
||||
*/
|
||||
def pickItem(hit:MovingObjectPosition):ItemStack = null
|
||||
/**
|
||||
* Called on block right click. item is the player's held item.
|
||||
* This should not modify the part client side. If the client call returns false, the server will not call this function.
|
||||
* @param hit An instance of ExtendedMOP from collisionRayTrace
|
||||
*/
|
||||
def activate(player:EntityPlayer, hit:MovingObjectPosition, item:ItemStack) = false
|
||||
/**
|
||||
* Called on block left click. item is the player's held item.
|
||||
* @param hit An instance of ExtendedMOP from collisionRayTrace
|
||||
*/
|
||||
def click(player:EntityPlayer, hit:MovingObjectPosition, item:ItemStack){}
|
||||
/**
|
||||
* Called when an entity is within this block space. May not actually collide with this part.
|
||||
*/
|
||||
def onEntityCollision(entity:Entity){}
|
||||
}
|
||||
/** Short getter for xCoord
|
||||
*/
|
||||
def x = tile.xCoord
|
||||
|
||||
/** Short getter for yCoord
|
||||
*/
|
||||
def y = tile.yCoord
|
||||
|
||||
/** Short getter for zCoord
|
||||
*/
|
||||
def z = tile.zCoord
|
||||
|
||||
/** The unique string identifier for this class of multipart.
|
||||
*/
|
||||
def getType: String
|
||||
|
||||
/** Called when the container tile instance is changed to update reference
|
||||
*/
|
||||
def bind(t: TileMultipart) {
|
||||
tile = t
|
||||
}
|
||||
|
||||
/** Perform an occlusion test to determine whether this and npart can 'fit' in
|
||||
* this block space.
|
||||
*/
|
||||
def occlusionTest(npart: TMultiPart): Boolean = true
|
||||
|
||||
/** Return a list of entity collision boxes. Note all Cuboid6's returned by
|
||||
* methods in TMultiPart should be within (0,0,0)->(1,1,1)
|
||||
*/
|
||||
def getCollisionBoxes: Iterable[Cuboid6] = Seq()
|
||||
|
||||
/** Perform a raytrace of this part. The default implementation does a Cuboid6
|
||||
* ray trace on bounding boxes returned from getSubParts. This should only be
|
||||
* overridden if you need special ray-tracing capabilities such as triangular
|
||||
* faces. The returned ExtendedMOP will be passed to methods such as
|
||||
* 'activate' so it is recommended to use the data field to indicate
|
||||
* information about the hit area.
|
||||
*/
|
||||
def collisionRayTrace(start: Vec3, end: Vec3): ExtendedMOP = {
|
||||
val offset = new Vector3(x, y, z)
|
||||
val boxes =
|
||||
getSubParts.map(c => new IndexedCuboid6(c.data, c.copy.add(offset)))
|
||||
return RayTracer.instance
|
||||
.rayTraceCuboids(
|
||||
new Vector3(start),
|
||||
new Vector3(end),
|
||||
boxes.toList,
|
||||
new BlockCoord(x, y, z),
|
||||
tile.getBlockType
|
||||
)
|
||||
.asInstanceOf[ExtendedMOP]
|
||||
}
|
||||
|
||||
/** For the default collisionRayTrace implementation, returns a list of
|
||||
* indexed bounding boxes. The data field of ExtendedMOP will be set to the
|
||||
* index of the cuboid the raytrace hit.
|
||||
*/
|
||||
def getSubParts: Iterable[IndexedCuboid6] = Seq()
|
||||
|
||||
/** Return a list of items that should be dropped when this part is destroyed.
|
||||
*/
|
||||
def getDrops: Iterable[ItemStack] = Seq()
|
||||
|
||||
/** Return a value indicating how hard this part is to break
|
||||
* @param hit
|
||||
* An instance of ExtendedMOP from collisionRayTrace
|
||||
*/
|
||||
def getStrength(hit: MovingObjectPosition, player: EntityPlayer): Float = 1
|
||||
|
||||
/** Harvest this part, removing it from the container tile and dropping items
|
||||
* if necessary.
|
||||
* @param hit
|
||||
* An instance of ExtendedMOP from collisionRayTrace
|
||||
* @param player
|
||||
* The player harvesting the part
|
||||
*/
|
||||
def harvest(hit: MovingObjectPosition, player: EntityPlayer) {
|
||||
if (!player.capabilities.isCreativeMode)
|
||||
tile.dropItems(getDrops)
|
||||
tile.remPart(this)
|
||||
}
|
||||
|
||||
/** The light level emitted by this part
|
||||
*/
|
||||
def getLightValue = 0
|
||||
|
||||
/** If any part returns true for this, torches can be placed. Vanilla hacks...
|
||||
*/
|
||||
def canPlaceTorchOnTop = false
|
||||
|
||||
/** Explosion resistance of the host tile is the maximum explosion resistance
|
||||
* of the contained parts
|
||||
* @param entity
|
||||
* The entity responsible for this explosion
|
||||
* @return
|
||||
* The resistance of this part the the explosion
|
||||
*/
|
||||
def explosionResistance(entity: Entity) = 0f
|
||||
|
||||
/** Add particles and other effects when a player is mining this part
|
||||
* @param hit
|
||||
* An instance of ExtendedMOP from collisionRayTrace
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def addHitEffects(
|
||||
hit: MovingObjectPosition,
|
||||
effectRenderer: EffectRenderer
|
||||
) {}
|
||||
|
||||
/** Add particles and other effects when a player broke this part
|
||||
* @param hit
|
||||
* An instance of ExtendedMOP from collisionRayTrace
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def addDestroyEffects(
|
||||
hit: MovingObjectPosition,
|
||||
effectRenderer: EffectRenderer
|
||||
) { addDestroyEffects(effectRenderer) }
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Deprecated
|
||||
def addDestroyEffects(effectRenderer: EffectRenderer) {}
|
||||
|
||||
/** Render the static, unmoving faces of this part into the world renderer.
|
||||
* The Tessellator is already drawing.
|
||||
* @param pass
|
||||
* The render pass, 1 or 0
|
||||
* @return
|
||||
* true if vertices were added to the tessellator
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def renderStatic(pos: Vector3, pass: Int) = false
|
||||
|
||||
/** Render the dynamic, changing faces of this part and other gfx as in a
|
||||
* TESR. The Tessellator will need to be started if it is to be used.
|
||||
* @param pos
|
||||
* The position of this block space relative to the renderer, same as x, y,
|
||||
* z passed to TESR.
|
||||
* @param frame
|
||||
* The partial interpolation frame value for animations between ticks
|
||||
* @param pass
|
||||
* The render pass, 1 or 0
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def renderDynamic(pos: Vector3, frame: Float, pass: Int) {}
|
||||
|
||||
/** Draw the breaking overlay for this part. The overrideIcon in RenderBlocks
|
||||
* will be set to the fracture icon.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def drawBreaking(renderBlocks: RenderBlocks) {}
|
||||
|
||||
/** Override the drawing of the selection box around this part.
|
||||
* @param hit
|
||||
* An instance of ExtendedMOP from collisionRayTrace
|
||||
* @return
|
||||
* true if highlight rendering was overridden.
|
||||
*/
|
||||
@SideOnly(Side.CLIENT)
|
||||
def drawHighlight(
|
||||
hit: MovingObjectPosition,
|
||||
player: EntityPlayer,
|
||||
frame: Float
|
||||
): Boolean = false
|
||||
|
||||
/** @return
|
||||
* A Cuboid6 bounding the render of this part for frustum culling. The
|
||||
* bounds are relative to the tile coordinates.
|
||||
*/
|
||||
def getRenderBounds = Cuboid6.full
|
||||
|
||||
/** Write all the data required to describe a client version of this part to
|
||||
* the packet. Called serverside, when a client loads this part for the first
|
||||
* time.
|
||||
*/
|
||||
def writeDesc(packet: MCDataOutput) {}
|
||||
|
||||
/** Fill out this part with the description information contained in packet.
|
||||
* Will be exactly as written from writeDesc. Called clientside when a client
|
||||
* loads this part for the first time.
|
||||
*/
|
||||
def readDesc(packet: MCDataInput) {}
|
||||
|
||||
/** Save part to NBT (only called serverside)
|
||||
*/
|
||||
def save(tag: NBTTagCompound) {}
|
||||
|
||||
/** Load part from NBT (only called serverside)
|
||||
*/
|
||||
def load(tag: NBTTagCompound) {}
|
||||
|
||||
/** Gets a MCDataOutput instance for writing update data to clients with this
|
||||
* part loaded. The write stream functions as a buffer which is flushed in a
|
||||
* compressed databurst packet at the end of the tick.
|
||||
*/
|
||||
def getWriteStream: MCDataOutput = tile.getWriteStream(this)
|
||||
|
||||
/** Read and operate on data written to getWriteStream. Ensure all data this
|
||||
* part wrote is read even if it's not going to be used. The default
|
||||
* implementation assumes a call to sendDescUpdate as the only use of
|
||||
* getWriteStream.
|
||||
*/
|
||||
def read(packet: MCDataInput) {
|
||||
readDesc(packet)
|
||||
tile.markRender()
|
||||
}
|
||||
|
||||
/** Quick and easy method to re-describe the whole part on the client. This
|
||||
* will call read on the client which calls readDesc unless overriden.
|
||||
* Incremental changes should be sent rather than the whole description
|
||||
* packet if possible.
|
||||
*/
|
||||
def sendDescUpdate() = writeDesc(getWriteStream)
|
||||
|
||||
/** Called when a part is added or removed from this block space. The part
|
||||
* parameter may be null if several things have changed.
|
||||
*/
|
||||
def onPartChanged(part: TMultiPart) {}
|
||||
|
||||
/** Called when a neighbor block changed
|
||||
*/
|
||||
def onNeighborChanged() {}
|
||||
|
||||
/** Called when this part is added to the block space
|
||||
*/
|
||||
def onAdded() = onWorldJoin()
|
||||
|
||||
/** Called when this part is removed from the block space
|
||||
*/
|
||||
def onRemoved() = onWorldSeparate()
|
||||
|
||||
/** Called when the containing chunk is loaded on the server.
|
||||
*/
|
||||
def onChunkLoad() = onWorldJoin()
|
||||
|
||||
/** Called when the containing chunk is unloaded on the server.
|
||||
*/
|
||||
def onChunkUnload() = onWorldSeparate()
|
||||
|
||||
/** Called when this part separates from the world (due to removal, chunk
|
||||
* unload or other). Use this to sync with external data structures. Called
|
||||
* on both client and server
|
||||
*/
|
||||
def onWorldSeparate() {}
|
||||
|
||||
/** Called when this part joins the world (due to placement, chunkload or
|
||||
* frame move etc). Use this to sync with external data structures. Called on
|
||||
* both client and server
|
||||
*/
|
||||
def onWorldJoin() {}
|
||||
|
||||
/** Called when this part is converted from a normal block/tile (only
|
||||
* applicable if a converter has been registered)
|
||||
*/
|
||||
def onConverted() = onAdded()
|
||||
|
||||
/** Called when this part is converted from a normal block/tile (only
|
||||
* applicable if a converter has been registered) before the original tile
|
||||
* has been replaced Use this to clear out things like inventory from the old
|
||||
* tile.
|
||||
*/
|
||||
def invalidateConvertedTile() {}
|
||||
|
||||
/** Called when this part has been moved without a save/load.
|
||||
*/
|
||||
def onMoved() = onWorldJoin()
|
||||
|
||||
/** Called just before this part is actually removed from the container tile
|
||||
*/
|
||||
def preRemove() {}
|
||||
|
||||
/** Return whether this part needs update ticks. This will only be called on
|
||||
* addition/removal so it should be a constant for this instance.
|
||||
*/
|
||||
def doesTick = true
|
||||
|
||||
/** Called once per world tick. This will be called even if doesTick returns
|
||||
* false if another part in the space needs ticks.
|
||||
*/
|
||||
def update() {}
|
||||
|
||||
/** Called when a scheduled tick is executed.
|
||||
*/
|
||||
def scheduledTick() {}
|
||||
|
||||
/** Sets a scheduledTick callback for this part ticks in the future. This is a
|
||||
* world time value, so if the chunk is unloaded and reloaded some time
|
||||
* later, the tick may fire immediately.
|
||||
*/
|
||||
def scheduleTick(ticks: Int) = TickScheduler.scheduleTick(this, ticks)
|
||||
|
||||
/** Return the itemstack for the middle click pick-block function.
|
||||
*/
|
||||
def pickItem(hit: MovingObjectPosition): ItemStack = null
|
||||
|
||||
/** Called on block right click. item is the player's held item. This should
|
||||
* not modify the part client side. If the client call returns false, the
|
||||
* server will not call this function.
|
||||
* @param hit
|
||||
* An instance of ExtendedMOP from collisionRayTrace
|
||||
*/
|
||||
def activate(
|
||||
player: EntityPlayer,
|
||||
hit: MovingObjectPosition,
|
||||
item: ItemStack
|
||||
) = false
|
||||
|
||||
/** Called on block left click. item is the player's held item.
|
||||
* @param hit
|
||||
* An instance of ExtendedMOP from collisionRayTrace
|
||||
*/
|
||||
def click(player: EntityPlayer, hit: MovingObjectPosition, item: ItemStack) {}
|
||||
|
||||
/** Called when an entity is within this block space. May not actually collide
|
||||
* with this part.
|
||||
*/
|
||||
def onEntityCollision(entity: Entity) {}
|
||||
}
|
||||
@@ -4,65 +4,62 @@ import codechicken.lib.vec.Cuboid6
|
||||
import scala.collection.JavaConversions._
|
||||
import java.lang.Iterable
|
||||
|
||||
/**
|
||||
* This suite of 3 classes provides simple functions for standard bounding box based occlusion testing.
|
||||
* If any two parts have overlapping bounding boxes, the test fails
|
||||
*
|
||||
* See TIconHitEffects for notes on the Scala|Java composition setup.
|
||||
*/
|
||||
object NormalOcclusionTest
|
||||
{
|
||||
/**
|
||||
* Performs the test, returns true if the test fails
|
||||
*/
|
||||
def apply(boxes1:Traversable[Cuboid6], boxes2:Traversable[Cuboid6]):Boolean =
|
||||
boxes1.forall(v1 => boxes2.forall(v2 => !v1.intersects(v2)))
|
||||
|
||||
/**
|
||||
* Performs the test, returns true if the test fails
|
||||
*/
|
||||
def apply(part1:JNormalOcclusion, part2:TMultiPart):Boolean =
|
||||
{
|
||||
var boxes = Seq[Cuboid6]()
|
||||
if(part2.isInstanceOf[JNormalOcclusion])
|
||||
boxes = boxes++part2.asInstanceOf[JNormalOcclusion].getOcclusionBoxes
|
||||
|
||||
if(part2.isInstanceOf[JPartialOcclusion])
|
||||
boxes = boxes++part2.asInstanceOf[JPartialOcclusion].getPartialOcclusionBoxes
|
||||
/** This suite of 3 classes provides simple functions for standard bounding box
|
||||
* based occlusion testing. If any two parts have overlapping bounding boxes,
|
||||
* the test fails
|
||||
*
|
||||
* See TIconHitEffects for notes on the Scala|Java composition setup.
|
||||
*/
|
||||
object NormalOcclusionTest {
|
||||
|
||||
return NormalOcclusionTest(boxes, part1.getOcclusionBoxes)
|
||||
}
|
||||
/** Performs the test, returns true if the test fails
|
||||
*/
|
||||
def apply(
|
||||
boxes1: Traversable[Cuboid6],
|
||||
boxes2: Traversable[Cuboid6]
|
||||
): Boolean =
|
||||
boxes1.forall(v1 => boxes2.forall(v2 => !v1.intersects(v2)))
|
||||
|
||||
/** Performs the test, returns true if the test fails
|
||||
*/
|
||||
def apply(part1: JNormalOcclusion, part2: TMultiPart): Boolean = {
|
||||
var boxes = Seq[Cuboid6]()
|
||||
if (part2.isInstanceOf[JNormalOcclusion])
|
||||
boxes = boxes ++ part2.asInstanceOf[JNormalOcclusion].getOcclusionBoxes
|
||||
|
||||
if (part2.isInstanceOf[JPartialOcclusion])
|
||||
boxes =
|
||||
boxes ++ part2.asInstanceOf[JPartialOcclusion].getPartialOcclusionBoxes
|
||||
|
||||
return NormalOcclusionTest(boxes, part1.getOcclusionBoxes)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Java interface containing callbacks for normal occlusion testing.
|
||||
* Make sure to override occlusionTest as in TNormalOcclusion
|
||||
*/
|
||||
trait JNormalOcclusion
|
||||
{
|
||||
/**
|
||||
* Return a list of normal occlusion boxes
|
||||
*/
|
||||
def getOcclusionBoxes:Iterable[Cuboid6]
|
||||
/** Java interface containing callbacks for normal occlusion testing. Make sure
|
||||
* to override occlusionTest as in TNormalOcclusion
|
||||
*/
|
||||
trait JNormalOcclusion {
|
||||
|
||||
/** Return a list of normal occlusion boxes
|
||||
*/
|
||||
def getOcclusionBoxes: Iterable[Cuboid6]
|
||||
}
|
||||
|
||||
/**
|
||||
* Trait for scala programmers
|
||||
*/
|
||||
trait TNormalOcclusion extends TMultiPart with JNormalOcclusion
|
||||
{
|
||||
override def occlusionTest(npart:TMultiPart):Boolean =
|
||||
NormalOcclusionTest(this, npart) && super.occlusionTest(npart)
|
||||
/** Trait for scala programmers
|
||||
*/
|
||||
trait TNormalOcclusion extends TMultiPart with JNormalOcclusion {
|
||||
override def occlusionTest(npart: TMultiPart): Boolean =
|
||||
NormalOcclusionTest(this, npart) && super.occlusionTest(npart)
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility part class for performing 3rd party occlusion tests
|
||||
*/
|
||||
class NormallyOccludedPart(bounds:Iterable[Cuboid6]) extends TMultiPart with TNormalOcclusion
|
||||
{
|
||||
def this(bound:Cuboid6) = this(Seq(bound))
|
||||
|
||||
def getType = null
|
||||
|
||||
def getOcclusionBoxes = bounds
|
||||
}
|
||||
/** Utility part class for performing 3rd party occlusion tests
|
||||
*/
|
||||
class NormallyOccludedPart(bounds: Iterable[Cuboid6])
|
||||
extends TMultiPart
|
||||
with TNormalOcclusion {
|
||||
def this(bound: Cuboid6) = this(Seq(bound))
|
||||
|
||||
def getType = null
|
||||
|
||||
def getOcclusionBoxes = bounds
|
||||
}
|
||||
@@ -4,73 +4,70 @@ import codechicken.lib.vec.Cuboid6
|
||||
import scala.collection.JavaConversions._
|
||||
import java.lang.Iterable
|
||||
|
||||
/**
|
||||
* This class provides a special type of occlusion model used by microblocks.
|
||||
* The partial occlusion test defines bounding boxes that may intersect, so long as no part is completely obscured by a combination of the others.
|
||||
* Partial bounding boxes may not intersect with normal bounding boxes from NormalOcclusionTest
|
||||
*
|
||||
* This test is actually managed by the mixin trait TPartialOcclusionTile which is generated when the marker interface JPartialOcclusion is found
|
||||
*/
|
||||
class PartialOcclusionTest(size:Int)
|
||||
{
|
||||
/**
|
||||
* The resolution of the test, set to 1/8th of a block
|
||||
*/
|
||||
val res = 8
|
||||
val bits = new Array[Byte](res*res*res)
|
||||
val partial = new Array[Boolean](size)
|
||||
|
||||
def fill(i:Int, part:JPartialOcclusion)
|
||||
{
|
||||
fill(i, part.getPartialOcclusionBoxes, part.allowCompleteOcclusion)
|
||||
}
|
||||
|
||||
def fill(i:Int, boxes:Iterable[Cuboid6], complete:Boolean)
|
||||
{
|
||||
partial(i) = !complete
|
||||
boxes.foreach(box => fill(i+1, box))
|
||||
}
|
||||
|
||||
def fill(v:Int, box:Cuboid6)
|
||||
{
|
||||
for(x <- (box.min.x*res+0.5).toInt until (box.max.x*res+0.5).toInt)
|
||||
for(y <- (box.min.y*res+0.5).toInt until (box.max.y*res+0.5).toInt)
|
||||
for(z <- (box.min.z*res+0.5).toInt until (box.max.z*res+0.5).toInt)
|
||||
{
|
||||
val i = (x*res+y)*res+z
|
||||
if(bits(i) == 0)
|
||||
bits(i) = v.toByte
|
||||
else
|
||||
bits(i) = -1
|
||||
}
|
||||
}
|
||||
|
||||
def apply():Boolean =
|
||||
{
|
||||
val visible = new Array[Boolean](size)
|
||||
bits.foreach(n => if(n > 0) visible(n-1) = true)
|
||||
|
||||
var i = 0
|
||||
while(i < partial.length)
|
||||
{
|
||||
if(partial(i) && !visible(i))
|
||||
return false
|
||||
i+=1
|
||||
/** This class provides a special type of occlusion model used by microblocks.
|
||||
* The partial occlusion test defines bounding boxes that may intersect, so
|
||||
* long as no part is completely obscured by a combination of the others.
|
||||
* Partial bounding boxes may not intersect with normal bounding boxes from
|
||||
* NormalOcclusionTest
|
||||
*
|
||||
* This test is actually managed by the mixin trait TPartialOcclusionTile which
|
||||
* is generated when the marker interface JPartialOcclusion is found
|
||||
*/
|
||||
class PartialOcclusionTest(size: Int) {
|
||||
|
||||
/** The resolution of the test, set to 1/8th of a block
|
||||
*/
|
||||
val res = 8
|
||||
val bits = new Array[Byte](res * res * res)
|
||||
val partial = new Array[Boolean](size)
|
||||
|
||||
def fill(i: Int, part: JPartialOcclusion) {
|
||||
fill(i, part.getPartialOcclusionBoxes, part.allowCompleteOcclusion)
|
||||
}
|
||||
|
||||
def fill(i: Int, boxes: Iterable[Cuboid6], complete: Boolean) {
|
||||
partial(i) = !complete
|
||||
boxes.foreach(box => fill(i + 1, box))
|
||||
}
|
||||
|
||||
def fill(v: Int, box: Cuboid6) {
|
||||
for (x <- (box.min.x * res + 0.5).toInt until (box.max.x * res + 0.5).toInt)
|
||||
for (
|
||||
y <- (box.min.y * res + 0.5).toInt until (box.max.y * res + 0.5).toInt
|
||||
)
|
||||
for (
|
||||
z <- (box.min.z * res + 0.5).toInt until (box.max.z * res + 0.5).toInt
|
||||
) {
|
||||
val i = (x * res + y) * res + z
|
||||
if (bits(i) == 0)
|
||||
bits(i) = v.toByte
|
||||
else
|
||||
bits(i) = -1
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
def apply(): Boolean = {
|
||||
val visible = new Array[Boolean](size)
|
||||
bits.foreach(n => if (n > 0) visible(n - 1) = true)
|
||||
|
||||
var i = 0
|
||||
while (i < partial.length) {
|
||||
if (partial(i) && !visible(i))
|
||||
return false
|
||||
i += 1
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
trait JPartialOcclusion
|
||||
{
|
||||
/**
|
||||
* Return a list of partial occlusion boxes
|
||||
*/
|
||||
def getPartialOcclusionBoxes:Iterable[Cuboid6]
|
||||
|
||||
/**
|
||||
* Return true if this part may be completely obscured
|
||||
*/
|
||||
def allowCompleteOcclusion = false
|
||||
}
|
||||
trait JPartialOcclusion {
|
||||
|
||||
/** Return a list of partial occlusion boxes
|
||||
*/
|
||||
def getPartialOcclusionBoxes: Iterable[Cuboid6]
|
||||
|
||||
/** Return true if this part may be completely obscured
|
||||
*/
|
||||
def allowCompleteOcclusion = false
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
package codechicken.multipart
|
||||
|
||||
/**
|
||||
* Interface for parts with random update ticks. Used in conjuction with TickScheduler
|
||||
*/
|
||||
trait TRandomUpdateTick extends TMultiPart
|
||||
{
|
||||
/**
|
||||
* Called on random update. Random ticks are between 800 and 1600 ticks from their last scheduled/random tick
|
||||
*/
|
||||
def randomUpdate()
|
||||
/** Interface for parts with random update ticks. Used in conjuction with
|
||||
* TickScheduler
|
||||
*/
|
||||
trait TRandomUpdateTick extends TMultiPart {
|
||||
|
||||
/**
|
||||
* If implementing interface in java, be sure to implement this method yourself
|
||||
*/
|
||||
override def onWorldJoin() {
|
||||
TickScheduler.loadRandomTick(this)
|
||||
}
|
||||
}
|
||||
/** Called on random update. Random ticks are between 800 and 1600 ticks from
|
||||
* their last scheduled/random tick
|
||||
*/
|
||||
def randomUpdate()
|
||||
|
||||
/** If implementing interface in java, be sure to implement this method
|
||||
* yourself
|
||||
*/
|
||||
override def onWorldJoin() {
|
||||
TickScheduler.loadRandomTick(this)
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
package codechicken.multipart
|
||||
|
||||
/**
|
||||
* Interface for parts that fill a slot based configuration as defined in PartMap.
|
||||
* If this is implemented, calling partMap(slot) on the host tile will return this part if the corresponding bit in the slotMask is set
|
||||
*
|
||||
* Marker interface for TSlottedTile
|
||||
*/
|
||||
trait TSlottedPart extends TMultiPart
|
||||
{
|
||||
/**
|
||||
* a bitmask of slots that this part fills. slot x is 1<<x
|
||||
*/
|
||||
def getSlotMask:Int
|
||||
}
|
||||
/** Interface for parts that fill a slot based configuration as defined in
|
||||
* PartMap. If this is implemented, calling partMap(slot) on the host tile will
|
||||
* return this part if the corresponding bit in the slotMask is set
|
||||
*
|
||||
* Marker interface for TSlottedTile
|
||||
*/
|
||||
trait TSlottedPart extends TMultiPart {
|
||||
|
||||
/** a bitmask of slots that this part fills. slot x is 1<<x
|
||||
*/
|
||||
def getSlotMask: Int
|
||||
}
|
||||
@@ -18,243 +18,242 @@ import java.io.FileOutputStream
|
||||
import java.io.FileInputStream
|
||||
import net.minecraft.world.storage.SaveHandler
|
||||
|
||||
/**
|
||||
* Used for scheduling delayed callbacks to parts.
|
||||
* Do not use this for redstone applications that require precise timing.
|
||||
* If 2 parts are both scheduled for an update on the same tick, there is no guarantee which one will update first.
|
||||
* These parts should not depend on a state of another part that may have changed before/after them.
|
||||
*/
|
||||
object TickScheduler extends WorldExtensionInstantiator
|
||||
{
|
||||
class PartTickEntry(val part:TMultiPart, var time:Long, var random:Boolean)
|
||||
{
|
||||
def this(part:TMultiPart, ticks:Int) = this(part, ticks, false)
|
||||
}
|
||||
|
||||
private class WorldTickScheduler(world$:World) extends WorldExtension(world$)
|
||||
{
|
||||
var schedTime = 0L
|
||||
var tickChunks = HashSet[ChunkTickScheduler]()
|
||||
private var processing = false
|
||||
private val pending = ListBuffer[PartTickEntry]()
|
||||
|
||||
def scheduleTick(part:TMultiPart, ticks:Int, random:Boolean)
|
||||
{
|
||||
if(processing)
|
||||
pending+=new PartTickEntry(part, schedTime+ticks, random)
|
||||
else
|
||||
_scheduleTick(part, schedTime+ticks, random)
|
||||
}
|
||||
|
||||
def _scheduleTick(part:TMultiPart, time:Long, random:Boolean)
|
||||
{
|
||||
if(part.tile != null)
|
||||
getChunkExtension(part.tile.xCoord>>4, part.tile.zCoord>>4)
|
||||
.asInstanceOf[ChunkTickScheduler].scheduleTick(part, time, random)
|
||||
}
|
||||
|
||||
def loadRandom(part:TRandomUpdateTick) = scheduleTick(part, nextRandomTick, true)
|
||||
|
||||
override def preTick()
|
||||
{
|
||||
processing = true
|
||||
}
|
||||
|
||||
override def postTick()
|
||||
{
|
||||
if(!tickChunks.isEmpty)
|
||||
tickChunks = tickChunks.filter(_.processTicks())
|
||||
|
||||
processing = false
|
||||
pending.foreach(e => _scheduleTick(e.part, e.time, e.random))
|
||||
pending.clear()
|
||||
|
||||
schedTime+=1
|
||||
}
|
||||
|
||||
def saveDir:File =
|
||||
{
|
||||
if(world.provider.dimensionId == 0)//Calling DimensionManager.getCurrentSaveRootDirectory too early breaks game saves, we have a world reference, use it
|
||||
return world.getSaveHandler.asInstanceOf[SaveHandler].getWorldDirectory
|
||||
|
||||
return new File(DimensionManager.getCurrentSaveRootDirectory, world.provider.getSaveFolder)
|
||||
}
|
||||
|
||||
def saveFile:File = new File(saveDir, "multipart.dat")
|
||||
|
||||
override def load()
|
||||
{
|
||||
try
|
||||
{
|
||||
val in = new FileInputStream(saveFile)
|
||||
loadTag(CompressedStreamTools.readCompressed(in))
|
||||
in.close()
|
||||
}
|
||||
catch
|
||||
{
|
||||
case e:Exception =>
|
||||
}
|
||||
|
||||
loadTag(new NBTTagCompound)
|
||||
}
|
||||
|
||||
def loadTag(tag:NBTTagCompound)
|
||||
{
|
||||
if(tag.hasKey("schedTime"))
|
||||
schedTime = tag.getLong("schedTime")
|
||||
else
|
||||
schedTime = world.getTotalWorldTime
|
||||
}
|
||||
|
||||
def saveTag:NBTTagCompound =
|
||||
{
|
||||
val tag = new NBTTagCompound
|
||||
tag.setLong("schedTime", schedTime)
|
||||
return tag
|
||||
}
|
||||
|
||||
override def save() {
|
||||
val file = saveFile
|
||||
if(!file.getParentFile.exists)
|
||||
file.getParentFile.mkdirs()
|
||||
if(!file.exists)
|
||||
file.createNewFile()
|
||||
|
||||
val dout = new DataOutputStream(new FileOutputStream(file))
|
||||
CompressedStreamTools.writeCompressed(saveTag, dout)
|
||||
dout.close()
|
||||
}
|
||||
|
||||
def nextRandomTick = world.rand.nextInt(800)+800
|
||||
}
|
||||
|
||||
def createWorldExtension(world:World):WorldExtension = new WorldTickScheduler(world)
|
||||
|
||||
private class ChunkTickScheduler(chunk$:Chunk, world:WorldTickScheduler) extends ChunkExtension(chunk$, world)
|
||||
{
|
||||
import codechicken.multipart.handler.MultipartProxy._
|
||||
|
||||
var tickList = ListBuffer[PartTickEntry]()
|
||||
|
||||
def schedTime = world.schedTime
|
||||
|
||||
def scheduleTick(part:TMultiPart, time:Long, random:Boolean)
|
||||
{
|
||||
val it = tickList.iterator
|
||||
while(it.hasNext)
|
||||
{
|
||||
val e = it.next()
|
||||
if(e.part == part)
|
||||
{
|
||||
if(e.random && !random)//only override an existing tick if we're going from random->scheduled
|
||||
{
|
||||
e.time = time
|
||||
e.random = random
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
tickList+=new PartTickEntry(part, time, random)
|
||||
if(tickList.size == 1)
|
||||
world.tickChunks+=this
|
||||
}
|
||||
|
||||
def nextRandomTick = world.nextRandomTick
|
||||
|
||||
def processTicks():Boolean =
|
||||
{
|
||||
tickList = tickList.filter(processTick)
|
||||
return !tickList.isEmpty
|
||||
}
|
||||
|
||||
def processTick(e:PartTickEntry):Boolean =
|
||||
{
|
||||
if(e.time <= schedTime)
|
||||
{
|
||||
if(e.part.tile != null)
|
||||
{
|
||||
if(e.random && e.part.isInstanceOf[TRandomUpdateTick])
|
||||
e.part.asInstanceOf[TRandomUpdateTick].randomUpdate()
|
||||
else
|
||||
e.part.scheduledTick()
|
||||
|
||||
if(e.part.isInstanceOf[TRandomUpdateTick])
|
||||
{
|
||||
e.time = schedTime+nextRandomTick
|
||||
e.random = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override def saveData(data:NBTTagCompound)
|
||||
{
|
||||
val tagList = new NBTTagList
|
||||
tickList.foreach{e =>
|
||||
val part = e.part
|
||||
if(part.tile != null && !e.random)
|
||||
{
|
||||
val tag = new NBTTagCompound
|
||||
tag.setShort("pos", indexInChunk(new BlockCoord(part.tile)).toShort)
|
||||
tag.setByte("i", part.tile.partList.indexOf(part).toByte)
|
||||
tag.setLong("time", e.time)
|
||||
tagList.appendTag(tag)
|
||||
}
|
||||
}
|
||||
if(tagList.tagCount > 0)
|
||||
data.setTag("multipartTicks", tagList)
|
||||
}
|
||||
|
||||
override def loadData(data:NBTTagCompound)
|
||||
{
|
||||
tickList.clear()
|
||||
if(!data.hasKey("multipartTicks"))
|
||||
return
|
||||
|
||||
val tagList = data.getTagList("multipartTicks", 10)
|
||||
val cc = new ChunkCoordIntPair(0, 0)
|
||||
for(i <- 0 until tagList.tagCount)
|
||||
{
|
||||
val tag = tagList.getCompoundTagAt(i)
|
||||
val pos = indexInChunk(cc, tag.getShort("pos"))
|
||||
val tile = chunk.chunkTileEntityMap.get(new ChunkPosition(pos.x, pos.y, pos.z))
|
||||
if(tile.isInstanceOf[TileMultipart])
|
||||
tickList+=new PartTickEntry(tile.asInstanceOf[TileMultipart].partList(tag.getByte("i")), tag.getLong("time"), false)
|
||||
}
|
||||
}
|
||||
|
||||
override def unload()
|
||||
{
|
||||
if(!tickList.isEmpty)
|
||||
world.tickChunks-=this
|
||||
}
|
||||
}
|
||||
|
||||
def createChunkExtension(chunk:Chunk, world:WorldExtension):ChunkExtension = new ChunkTickScheduler(chunk, world.asInstanceOf[WorldTickScheduler])
|
||||
/** Used for scheduling delayed callbacks to parts. Do not use this for redstone
|
||||
* applications that require precise timing. If 2 parts are both scheduled for
|
||||
* an update on the same tick, there is no guarantee which one will update
|
||||
* first. These parts should not depend on a state of another part that may
|
||||
* have changed before/after them.
|
||||
*/
|
||||
object TickScheduler extends WorldExtensionInstantiator {
|
||||
class PartTickEntry(
|
||||
val part: TMultiPart,
|
||||
var time: Long,
|
||||
var random: Boolean
|
||||
) {
|
||||
def this(part: TMultiPart, ticks: Int) = this(part, ticks, false)
|
||||
}
|
||||
|
||||
/**
|
||||
* Start random ticking for a part. Should be called from TMultiPart.onWorldJoin
|
||||
*/
|
||||
def loadRandomTick(part:TRandomUpdateTick)
|
||||
{
|
||||
getExtension(part.tile.getWorldObj).asInstanceOf[WorldTickScheduler].loadRandom(part)
|
||||
private class WorldTickScheduler(world$ : World)
|
||||
extends WorldExtension(world$) {
|
||||
var schedTime = 0L
|
||||
var tickChunks = HashSet[ChunkTickScheduler]()
|
||||
private var processing = false
|
||||
private val pending = ListBuffer[PartTickEntry]()
|
||||
|
||||
def scheduleTick(part: TMultiPart, ticks: Int, random: Boolean) {
|
||||
if (processing)
|
||||
pending += new PartTickEntry(part, schedTime + ticks, random)
|
||||
else
|
||||
_scheduleTick(part, schedTime + ticks, random)
|
||||
}
|
||||
|
||||
/**
|
||||
* Schedule a tick for part relative to the current time.
|
||||
*/
|
||||
def scheduleTick(part:TMultiPart, ticks:Int)
|
||||
{
|
||||
getExtension(part.tile.getWorldObj).asInstanceOf[WorldTickScheduler].scheduleTick(part, ticks, false)
|
||||
|
||||
def _scheduleTick(part: TMultiPart, time: Long, random: Boolean) {
|
||||
if (part.tile != null)
|
||||
getChunkExtension(part.tile.xCoord >> 4, part.tile.zCoord >> 4)
|
||||
.asInstanceOf[ChunkTickScheduler]
|
||||
.scheduleTick(part, time, random)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current scheduler time. Like the world time, but unaffected by the time set command and other things changing time of day.
|
||||
* Deprecated in favor of world.getTotalWorldTime
|
||||
*/
|
||||
@Deprecated
|
||||
def getSchedulerTime(world:World):Long = getExtension(world).asInstanceOf[WorldTickScheduler].schedTime
|
||||
}
|
||||
|
||||
def loadRandom(part: TRandomUpdateTick) =
|
||||
scheduleTick(part, nextRandomTick, true)
|
||||
|
||||
override def preTick() {
|
||||
processing = true
|
||||
}
|
||||
|
||||
override def postTick() {
|
||||
if (!tickChunks.isEmpty)
|
||||
tickChunks = tickChunks.filter(_.processTicks())
|
||||
|
||||
processing = false
|
||||
pending.foreach(e => _scheduleTick(e.part, e.time, e.random))
|
||||
pending.clear()
|
||||
|
||||
schedTime += 1
|
||||
}
|
||||
|
||||
def saveDir: File = {
|
||||
if (
|
||||
world.provider.dimensionId == 0
|
||||
) // Calling DimensionManager.getCurrentSaveRootDirectory too early breaks game saves, we have a world reference, use it
|
||||
return world.getSaveHandler.asInstanceOf[SaveHandler].getWorldDirectory
|
||||
|
||||
return new File(
|
||||
DimensionManager.getCurrentSaveRootDirectory,
|
||||
world.provider.getSaveFolder
|
||||
)
|
||||
}
|
||||
|
||||
def saveFile: File = new File(saveDir, "multipart.dat")
|
||||
|
||||
override def load() {
|
||||
try {
|
||||
val in = new FileInputStream(saveFile)
|
||||
loadTag(CompressedStreamTools.readCompressed(in))
|
||||
in.close()
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
}
|
||||
|
||||
loadTag(new NBTTagCompound)
|
||||
}
|
||||
|
||||
def loadTag(tag: NBTTagCompound) {
|
||||
if (tag.hasKey("schedTime"))
|
||||
schedTime = tag.getLong("schedTime")
|
||||
else
|
||||
schedTime = world.getTotalWorldTime
|
||||
}
|
||||
|
||||
def saveTag: NBTTagCompound = {
|
||||
val tag = new NBTTagCompound
|
||||
tag.setLong("schedTime", schedTime)
|
||||
return tag
|
||||
}
|
||||
|
||||
override def save() {
|
||||
val file = saveFile
|
||||
if (!file.getParentFile.exists)
|
||||
file.getParentFile.mkdirs()
|
||||
if (!file.exists)
|
||||
file.createNewFile()
|
||||
|
||||
val dout = new DataOutputStream(new FileOutputStream(file))
|
||||
CompressedStreamTools.writeCompressed(saveTag, dout)
|
||||
dout.close()
|
||||
}
|
||||
|
||||
def nextRandomTick = world.rand.nextInt(800) + 800
|
||||
}
|
||||
|
||||
def createWorldExtension(world: World): WorldExtension =
|
||||
new WorldTickScheduler(world)
|
||||
|
||||
private class ChunkTickScheduler(chunk$ : Chunk, world: WorldTickScheduler)
|
||||
extends ChunkExtension(chunk$, world) {
|
||||
import codechicken.multipart.handler.MultipartProxy._
|
||||
|
||||
var tickList = ListBuffer[PartTickEntry]()
|
||||
|
||||
def schedTime = world.schedTime
|
||||
|
||||
def scheduleTick(part: TMultiPart, time: Long, random: Boolean) {
|
||||
val it = tickList.iterator
|
||||
while (it.hasNext) {
|
||||
val e = it.next()
|
||||
if (e.part == part) {
|
||||
if (
|
||||
e.random && !random
|
||||
) // only override an existing tick if we're going from random->scheduled
|
||||
{
|
||||
e.time = time
|
||||
e.random = random
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
tickList += new PartTickEntry(part, time, random)
|
||||
if (tickList.size == 1)
|
||||
world.tickChunks += this
|
||||
}
|
||||
|
||||
def nextRandomTick = world.nextRandomTick
|
||||
|
||||
def processTicks(): Boolean = {
|
||||
tickList = tickList.filter(processTick)
|
||||
return !tickList.isEmpty
|
||||
}
|
||||
|
||||
def processTick(e: PartTickEntry): Boolean = {
|
||||
if (e.time <= schedTime) {
|
||||
if (e.part.tile != null) {
|
||||
if (e.random && e.part.isInstanceOf[TRandomUpdateTick])
|
||||
e.part.asInstanceOf[TRandomUpdateTick].randomUpdate()
|
||||
else
|
||||
e.part.scheduledTick()
|
||||
|
||||
if (e.part.isInstanceOf[TRandomUpdateTick]) {
|
||||
e.time = schedTime + nextRandomTick
|
||||
e.random = true
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override def saveData(data: NBTTagCompound) {
|
||||
val tagList = new NBTTagList
|
||||
tickList.foreach { e =>
|
||||
val part = e.part
|
||||
if (part.tile != null && !e.random) {
|
||||
val tag = new NBTTagCompound
|
||||
tag.setShort("pos", indexInChunk(new BlockCoord(part.tile)).toShort)
|
||||
tag.setByte("i", part.tile.partList.indexOf(part).toByte)
|
||||
tag.setLong("time", e.time)
|
||||
tagList.appendTag(tag)
|
||||
}
|
||||
}
|
||||
if (tagList.tagCount > 0)
|
||||
data.setTag("multipartTicks", tagList)
|
||||
}
|
||||
|
||||
override def loadData(data: NBTTagCompound) {
|
||||
tickList.clear()
|
||||
if (!data.hasKey("multipartTicks"))
|
||||
return
|
||||
|
||||
val tagList = data.getTagList("multipartTicks", 10)
|
||||
val cc = new ChunkCoordIntPair(0, 0)
|
||||
for (i <- 0 until tagList.tagCount) {
|
||||
val tag = tagList.getCompoundTagAt(i)
|
||||
val pos = indexInChunk(cc, tag.getShort("pos"))
|
||||
val tile =
|
||||
chunk.chunkTileEntityMap.get(new ChunkPosition(pos.x, pos.y, pos.z))
|
||||
if (tile.isInstanceOf[TileMultipart])
|
||||
tickList += new PartTickEntry(
|
||||
tile.asInstanceOf[TileMultipart].partList(tag.getByte("i")),
|
||||
tag.getLong("time"),
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override def unload() {
|
||||
if (!tickList.isEmpty)
|
||||
world.tickChunks -= this
|
||||
}
|
||||
}
|
||||
|
||||
def createChunkExtension(
|
||||
chunk: Chunk,
|
||||
world: WorldExtension
|
||||
): ChunkExtension =
|
||||
new ChunkTickScheduler(chunk, world.asInstanceOf[WorldTickScheduler])
|
||||
|
||||
/** Start random ticking for a part. Should be called from
|
||||
* TMultiPart.onWorldJoin
|
||||
*/
|
||||
def loadRandomTick(part: TRandomUpdateTick) {
|
||||
getExtension(part.tile.getWorldObj)
|
||||
.asInstanceOf[WorldTickScheduler]
|
||||
.loadRandom(part)
|
||||
}
|
||||
|
||||
/** Schedule a tick for part relative to the current time.
|
||||
*/
|
||||
def scheduleTick(part: TMultiPart, ticks: Int) {
|
||||
getExtension(part.tile.getWorldObj)
|
||||
.asInstanceOf[WorldTickScheduler]
|
||||
.scheduleTick(part, ticks, false)
|
||||
}
|
||||
|
||||
/** Returns the current scheduler time. Like the world time, but unaffected by
|
||||
* the time set command and other things changing time of day. Deprecated in
|
||||
* favor of world.getTotalWorldTime
|
||||
*/
|
||||
@Deprecated
|
||||
def getSchedulerTime(world: World): Long =
|
||||
getExtension(world).asInstanceOf[WorldTickScheduler].schedTime
|
||||
}
|
||||
@@ -5,34 +5,42 @@ import scala.collection.mutable.{Map => MMap}
|
||||
import net.minecraft.world.World
|
||||
import codechicken.multipart.handler.MultipartProxy
|
||||
|
||||
/**
|
||||
* In order to maintain tight client/server synchronisation without bandwidth overhead, all data written must be read.
|
||||
* Sometimes the client tile is removed from the chunk before the packet arrives. This class maintains a reference to all multipart tiles.
|
||||
* The original design placed tiles that were invalidated into a map that was cleared upon arrival of the update packet.
|
||||
* Due to other factors that have eluded identification, references to all tiles held until they are replaced or the world is reloaded.
|
||||
*/
|
||||
object TileCache
|
||||
{
|
||||
case class FlaggedTile(t:TileMultipart, removed:Boolean)
|
||||
/** In order to maintain tight client/server synchronisation without bandwidth
|
||||
* overhead, all data written must be read. Sometimes the client tile is
|
||||
* removed from the chunk before the packet arrives. This class maintains a
|
||||
* reference to all multipart tiles. The original design placed tiles that were
|
||||
* invalidated into a map that was cleared upon arrival of the update packet.
|
||||
* Due to other factors that have eluded identification, references to all
|
||||
* tiles held until they are replaced or the world is reloaded.
|
||||
*/
|
||||
object TileCache {
|
||||
case class FlaggedTile(t: TileMultipart, removed: Boolean)
|
||||
|
||||
val map = MMap[BlockCoord, FlaggedTile]()
|
||||
val map = MMap[BlockCoord, FlaggedTile]()
|
||||
|
||||
def add(t:TileMultipart) = map.put(new BlockCoord(t), FlaggedTile(t, false))
|
||||
def remove(t:TileMultipart) = map.put(new BlockCoord(t), FlaggedTile(t, true))
|
||||
def apply(c:BlockCoord) = map.get(c)
|
||||
def clear() = map.clear()
|
||||
def add(t: TileMultipart) = map.put(new BlockCoord(t), FlaggedTile(t, false))
|
||||
def remove(t: TileMultipart) =
|
||||
map.put(new BlockCoord(t), FlaggedTile(t, true))
|
||||
def apply(c: BlockCoord) = map.get(c)
|
||||
def clear() = map.clear()
|
||||
|
||||
def findTile(world:World, c:BlockCoord) = BlockMultipart.getTile(world, c.x, c.y, c.z) match {
|
||||
case null => apply(c) match {
|
||||
case Some(FlaggedTile(t, rem)) =>
|
||||
if(!rem)
|
||||
MultipartProxy.logger.warn("Client multipart @"+c+" vanished from world but was recovered. If possible causes can be identified, please report to the github issue tracker.")
|
||||
t
|
||||
case None =>
|
||||
throw new RuntimeException("DC: Client multipart @"+c+" not found")
|
||||
case _ =>
|
||||
null
|
||||
def findTile(world: World, c: BlockCoord) =
|
||||
BlockMultipart.getTile(world, c.x, c.y, c.z) match {
|
||||
case null =>
|
||||
apply(c) match {
|
||||
case Some(FlaggedTile(t, rem)) =>
|
||||
if (!rem)
|
||||
MultipartProxy.logger.warn(
|
||||
"Client multipart @" + c + " vanished from world but was recovered. If possible causes can be identified, please report to the github issue tracker."
|
||||
)
|
||||
t
|
||||
case None =>
|
||||
throw new RuntimeException(
|
||||
"DC: Client multipart @" + c + " not found"
|
||||
)
|
||||
case _ =>
|
||||
null
|
||||
}
|
||||
case t => t
|
||||
case t => t
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -2,23 +2,21 @@ package codechicken.multipart.asm
|
||||
|
||||
import java.util.BitSet
|
||||
|
||||
object ASMImplicits
|
||||
{
|
||||
implicit class ExtBitSet(val bitset:BitSet) extends AnyVal
|
||||
{
|
||||
def set(b:BitSet) = {
|
||||
bitset.clear()
|
||||
bitset.or(b)
|
||||
bitset
|
||||
}
|
||||
|
||||
def copy = new BitSet().set(bitset)
|
||||
object ASMImplicits {
|
||||
implicit class ExtBitSet(val bitset: BitSet) extends AnyVal {
|
||||
def set(b: BitSet) = {
|
||||
bitset.clear()
|
||||
bitset.or(b)
|
||||
bitset
|
||||
}
|
||||
|
||||
def nodeName(name:String) = if(name == null) null else name.replace('.', '/')
|
||||
def copy = new BitSet().set(bitset)
|
||||
}
|
||||
|
||||
implicit class ExtClass(val clazz:Class[_]) extends AnyVal
|
||||
{
|
||||
def nodeName = ASMImplicits.nodeName(clazz.getName)
|
||||
}
|
||||
def nodeName(name: String) =
|
||||
if (name == null) null else name.replace('.', '/')
|
||||
|
||||
implicit class ExtClass(val clazz: Class[_]) extends AnyVal {
|
||||
def nodeName = ASMImplicits.nodeName(clazz.getName)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -8,80 +8,88 @@ import scala.collection.mutable.ArrayBuffer
|
||||
import ASMMixinCompiler._
|
||||
import ASMImplicits._
|
||||
|
||||
class ASMMixinFactory[T](val baseType:Class[T], private val paramTypes:Class[_]*)
|
||||
{
|
||||
private val traitMap = mutable.Map[String, Int]()
|
||||
private val traits = ArrayBuffer[String]()
|
||||
private val classMap = mutable.Map[BitSet, Constructor[_ <: T]]()
|
||||
class ASMMixinFactory[T](
|
||||
val baseType: Class[T],
|
||||
private val paramTypes: Class[_]*
|
||||
) {
|
||||
private val traitMap = mutable.Map[String, Int]()
|
||||
private val traits = ArrayBuffer[String]()
|
||||
private val classMap = mutable.Map[BitSet, Constructor[_ <: T]]()
|
||||
|
||||
private var ugenid = 0
|
||||
private var ugenid = 0
|
||||
|
||||
private def nextName() = {
|
||||
val ret = baseType.getSimpleName+"_cmp$$"+ugenid
|
||||
ugenid += 1
|
||||
ret
|
||||
private def nextName() = {
|
||||
val ret = baseType.getSimpleName + "_cmp$$" + ugenid
|
||||
ugenid += 1
|
||||
ret
|
||||
}
|
||||
|
||||
private def compile(traitSet: BitSet) = {
|
||||
val seq = Seq.newBuilder[String]
|
||||
var i = -1
|
||||
while ({ i = traitSet.nextSetBit(i + 1); i >= 0 })
|
||||
seq += traits(i)
|
||||
|
||||
val c = ASMMixinCompiler
|
||||
.mixinClasses(nextName(), baseType.nodeName, seq.result())
|
||||
.asInstanceOf[Class[_ <: T]]
|
||||
onCompiled(c, traitSet)
|
||||
c.getDeclaredConstructor(paramTypes: _*)
|
||||
}
|
||||
|
||||
protected def onCompiled(clazz: Class[_ <: T], traitSet: BitSet) {}
|
||||
protected def autoCompleteJavaTrait(cnode: ClassNode) {}
|
||||
|
||||
def construct(traitSet: BitSet, args: Object*) = synchronized {
|
||||
(classMap.get(traitSet) match {
|
||||
case Some(c) => c
|
||||
case None =>
|
||||
val c = compile(traitSet)
|
||||
classMap.put(traitSet.copy, c)
|
||||
c
|
||||
}).newInstance(args: _*)
|
||||
}
|
||||
|
||||
def getId(s_trait: String) = traitMap(s_trait)
|
||||
|
||||
def registerTrait(traitClass: Class[_]): Int = registerTrait(
|
||||
traitClass.nodeName
|
||||
)
|
||||
|
||||
def registerTrait(s_trait: String): Int = {
|
||||
val cnode = classNode(s_trait)
|
||||
if (cnode == null)
|
||||
throw new ClassNotFoundException(s_trait)
|
||||
|
||||
traitMap.get(cnode.name) match {
|
||||
case Some(id) => return id
|
||||
case None =>
|
||||
}
|
||||
|
||||
private def compile(traitSet:BitSet) = {
|
||||
val seq = Seq.newBuilder[String]
|
||||
var i = -1
|
||||
while({i = traitSet.nextSetBit(i+1); i >= 0})
|
||||
seq += traits(i)
|
||||
val info = getClassInfo(cnode)
|
||||
def concreteParent(info: ClassInfo): ClassInfo = info.superClass.map {
|
||||
case i if i.isTrait => concreteParent(i)
|
||||
case i => i
|
||||
}.get
|
||||
|
||||
val c = ASMMixinCompiler.mixinClasses(nextName(), baseType.nodeName, seq.result()).asInstanceOf[Class[_ <: T]]
|
||||
onCompiled(c, traitSet)
|
||||
c.getDeclaredConstructor(paramTypes:_*)
|
||||
val parentName = concreteParent(info).name
|
||||
def checkParent(info: ClassInfo): Boolean =
|
||||
info.name == parentName || info.superClass.exists(checkParent)
|
||||
if (!checkParent(getClassInfo(baseType)))
|
||||
throw new IllegalArgumentException(
|
||||
baseType.nodeName + " does not extend parent " + parentName + " of mixin trait " + s_trait
|
||||
)
|
||||
|
||||
if (info.isTrait) {
|
||||
registerScalaTrait(cnode)
|
||||
} else {
|
||||
autoCompleteJavaTrait(cnode)
|
||||
registerJavaTrait(cnode)
|
||||
}
|
||||
|
||||
protected def onCompiled(clazz:Class[_ <: T], traitSet:BitSet){}
|
||||
protected def autoCompleteJavaTrait(cnode:ClassNode){}
|
||||
|
||||
def construct(traitSet:BitSet, args:Object*) = synchronized {
|
||||
(classMap.get(traitSet) match {
|
||||
case Some(c) => c
|
||||
case None =>
|
||||
val c = compile(traitSet)
|
||||
classMap.put(traitSet.copy, c)
|
||||
c
|
||||
}).newInstance(args: _*)
|
||||
}
|
||||
|
||||
def getId(s_trait:String) = traitMap(s_trait)
|
||||
|
||||
def registerTrait(traitClass:Class[_]):Int = registerTrait(traitClass.nodeName)
|
||||
|
||||
def registerTrait(s_trait:String):Int = {
|
||||
val cnode = classNode(s_trait)
|
||||
if(cnode == null)
|
||||
throw new ClassNotFoundException(s_trait)
|
||||
|
||||
traitMap.get(cnode.name) match {
|
||||
case Some(id) => return id
|
||||
case None =>
|
||||
}
|
||||
|
||||
val info = getClassInfo(cnode)
|
||||
def concreteParent(info:ClassInfo):ClassInfo = info.superClass.map {
|
||||
case i if i.isTrait => concreteParent(i)
|
||||
case i => i
|
||||
}.get
|
||||
|
||||
val parentName = concreteParent(info).name
|
||||
def checkParent(info:ClassInfo):Boolean = info.name == parentName || info.superClass.exists(checkParent)
|
||||
if(!checkParent(getClassInfo(baseType)))
|
||||
throw new IllegalArgumentException(baseType.nodeName+" does not extend parent "+parentName+" of mixin trait "+s_trait)
|
||||
|
||||
if(info.isTrait) {
|
||||
registerScalaTrait(cnode)
|
||||
}
|
||||
else {
|
||||
autoCompleteJavaTrait(cnode)
|
||||
registerJavaTrait(cnode)
|
||||
}
|
||||
|
||||
val id = traits.size
|
||||
traits += cnode.name
|
||||
traitMap.put(cnode.name, id)
|
||||
id
|
||||
}
|
||||
val id = traits.size
|
||||
traits += cnode.name
|
||||
traitMap.put(cnode.name, id)
|
||||
id
|
||||
}
|
||||
}
|
||||
@@ -190,24 +190,22 @@ object ByteCodecs {
|
||||
|
||||
def encode(xs: Array[Byte]): Array[Byte] = avoidZero(encode8to7(xs))
|
||||
|
||||
/**
|
||||
* Destructively decodes array xs and returns the length of the decoded array.
|
||||
*
|
||||
* Sometimes returns (length+1) of the decoded array. Example:
|
||||
*
|
||||
* scala> val enc = scala.reflect.generic.ByteCodecs.encode(Array(1,2,3))
|
||||
* enc: Array[Byte] = Array(2, 5, 13, 1)
|
||||
*
|
||||
* scala> scala.reflect.generic.ByteCodecs.decode(enc)
|
||||
* res43: Int = 4
|
||||
*
|
||||
* scala> enc
|
||||
* res44: Array[Byte] = Array(1, 2, 3, 0)
|
||||
*
|
||||
* However, this does not always happen.
|
||||
*/
|
||||
/** Destructively decodes array xs and returns the length of the decoded
|
||||
* array.
|
||||
*
|
||||
* Sometimes returns (length+1) of the decoded array. Example:
|
||||
*
|
||||
* scala> val enc = scala.reflect.generic.ByteCodecs.encode(Array(1,2,3))
|
||||
* enc: Array[Byte] = Array(2, 5, 13, 1)
|
||||
*
|
||||
* scala> scala.reflect.generic.ByteCodecs.decode(enc) res43: Int = 4
|
||||
*
|
||||
* scala> enc res44: Array[Byte] = Array(1, 2, 3, 0)
|
||||
*
|
||||
* However, this does not always happen.
|
||||
*/
|
||||
def decode(xs: Array[Byte]): Int = {
|
||||
val len = regenerateZero(xs)
|
||||
decode7to8(xs, len)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,166 +12,237 @@ import ASMImplicits._
|
||||
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
object MultipartMixinFactory extends ASMMixinFactory(classOf[TileMultipart])
|
||||
{
|
||||
override protected def autoCompleteJavaTrait(cnode: ClassNode)
|
||||
object MultipartMixinFactory extends ASMMixinFactory(classOf[TileMultipart]) {
|
||||
override protected def autoCompleteJavaTrait(cnode: ClassNode) {
|
||||
if (
|
||||
!cnode.fields.isEmpty && findMethod(
|
||||
new ObfMapping(
|
||||
cnode.name,
|
||||
"copyFrom",
|
||||
"(Lcodechicken/multipart/TileMultipart;)V"
|
||||
),
|
||||
cnode
|
||||
) == null
|
||||
) {
|
||||
val mv = cnode.visitMethod(
|
||||
ACC_PUBLIC,
|
||||
"copyFrom",
|
||||
"(Lcodechicken/multipart/TileMultipart;)V",
|
||||
null,
|
||||
null
|
||||
)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitMethodInsn(
|
||||
INVOKESPECIAL,
|
||||
"codechicken/multipart/TileMultipart",
|
||||
"copyFrom",
|
||||
"(Lcodechicken/multipart/TileMultipart;)V"
|
||||
)
|
||||
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitTypeInsn(INSTANCEOF, cnode.name)
|
||||
val end = new Label()
|
||||
mv.visitJumpInsn(IFEQ, end)
|
||||
|
||||
cnode.fields.foreach { f =>
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitFieldInsn(GETFIELD, cnode.name, f.name, f.desc)
|
||||
mv.visitFieldInsn(PUTFIELD, cnode.name, f.name, f.desc)
|
||||
}
|
||||
|
||||
mv.visitLabel(end)
|
||||
mv.visitInsn(RETURN)
|
||||
mv.visitMaxs(2, 2)
|
||||
}
|
||||
}
|
||||
|
||||
def generatePassThroughTrait(s_interface: String): String = {
|
||||
val iname = nodeName(s_interface)
|
||||
val tname = {
|
||||
var simpleName = iname.substring(iname.lastIndexOf('/') + 1)
|
||||
if (simpleName.startsWith("I")) simpleName = simpleName.substring(1)
|
||||
"T" + simpleName + "$$PassThrough"
|
||||
}
|
||||
val vname = "impl"
|
||||
val idesc = "L" + iname + ";"
|
||||
|
||||
val inode = classNode(s_interface)
|
||||
if (inode == null) {
|
||||
logger.error(
|
||||
"Unable to generate pass through trait for: " + s_interface + " class not found."
|
||||
)
|
||||
return null
|
||||
}
|
||||
if ((inode.access & ACC_INTERFACE) == 0)
|
||||
throw new IllegalArgumentException(s_interface + " is not an interface.")
|
||||
|
||||
val cw = new CC_ClassWriter(0)
|
||||
var mv: MethodVisitor = null
|
||||
var fv: FieldVisitor = null
|
||||
|
||||
cw.visit(
|
||||
V1_6,
|
||||
ACC_PUBLIC | ACC_SUPER,
|
||||
tname,
|
||||
null,
|
||||
"codechicken/multipart/TileMultipart",
|
||||
Array(iname)
|
||||
)
|
||||
|
||||
{
|
||||
if(!cnode.fields.isEmpty && findMethod(new ObfMapping(cnode.name, "copyFrom", "(Lcodechicken/multipart/TileMultipart;)V"), cnode) == null)
|
||||
{
|
||||
val mv = cnode.visitMethod(ACC_PUBLIC, "copyFrom", "(Lcodechicken/multipart/TileMultipart;)V", null, null)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "copyFrom", "(Lcodechicken/multipart/TileMultipart;)V")
|
||||
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitTypeInsn(INSTANCEOF, cnode.name)
|
||||
val end = new Label()
|
||||
mv.visitJumpInsn(IFEQ, end)
|
||||
|
||||
cnode.fields.foreach{ f =>
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitFieldInsn(GETFIELD, cnode.name, f.name, f.desc)
|
||||
mv.visitFieldInsn(PUTFIELD, cnode.name, f.name, f.desc)
|
||||
}
|
||||
|
||||
mv.visitLabel(end)
|
||||
mv.visitInsn(RETURN)
|
||||
mv.visitMaxs(2, 2)
|
||||
}
|
||||
fv = cw.visitField(ACC_PRIVATE, vname, idesc, null, null)
|
||||
fv.visitEnd()
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null)
|
||||
mv.visitCode()
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitMethodInsn(
|
||||
INVOKESPECIAL,
|
||||
"codechicken/multipart/TileMultipart",
|
||||
"<init>",
|
||||
"()V"
|
||||
)
|
||||
mv.visitInsn(RETURN)
|
||||
mv.visitMaxs(1, 1)
|
||||
mv.visitEnd()
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(
|
||||
ACC_PUBLIC,
|
||||
"bindPart",
|
||||
"(Lcodechicken/multipart/TMultiPart;)V",
|
||||
null,
|
||||
null
|
||||
)
|
||||
mv.visitCode()
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitMethodInsn(
|
||||
INVOKESPECIAL,
|
||||
"codechicken/multipart/TileMultipart",
|
||||
"bindPart",
|
||||
"(Lcodechicken/multipart/TMultiPart;)V"
|
||||
)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitTypeInsn(INSTANCEOF, iname)
|
||||
val l2 = new Label()
|
||||
mv.visitJumpInsn(IFEQ, l2)
|
||||
val l3 = new Label()
|
||||
mv.visitLabel(l3)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitTypeInsn(CHECKCAST, iname)
|
||||
mv.visitFieldInsn(PUTFIELD, tname, vname, idesc)
|
||||
mv.visitLabel(l2)
|
||||
mv.visitFrame(F_SAME, 0, null, 0, null)
|
||||
mv.visitInsn(RETURN)
|
||||
mv.visitMaxs(2, 2)
|
||||
mv.visitEnd()
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(
|
||||
ACC_PUBLIC,
|
||||
"partRemoved",
|
||||
"(Lcodechicken/multipart/TMultiPart;I)V",
|
||||
null,
|
||||
null
|
||||
)
|
||||
mv.visitCode()
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitVarInsn(ILOAD, 2)
|
||||
mv.visitMethodInsn(
|
||||
INVOKESPECIAL,
|
||||
"codechicken/multipart/TileMultipart",
|
||||
"partRemoved",
|
||||
"(Lcodechicken/multipart/TMultiPart;I)V"
|
||||
)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
|
||||
val l2 = new Label()
|
||||
mv.visitJumpInsn(IF_ACMPNE, l2)
|
||||
val l3 = new Label()
|
||||
mv.visitLabel(l3)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitInsn(ACONST_NULL)
|
||||
mv.visitFieldInsn(PUTFIELD, tname, vname, idesc)
|
||||
mv.visitLabel(l2)
|
||||
mv.visitFrame(F_SAME, 0, null, 0, null)
|
||||
mv.visitInsn(RETURN)
|
||||
mv.visitMaxs(3, 3)
|
||||
mv.visitEnd()
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(
|
||||
ACC_PUBLIC,
|
||||
"canAddPart",
|
||||
"(Lcodechicken/multipart/TMultiPart;)Z",
|
||||
null,
|
||||
null
|
||||
)
|
||||
mv.visitCode()
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
|
||||
val l1 = new Label()
|
||||
mv.visitJumpInsn(IFNULL, l1)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitTypeInsn(INSTANCEOF, iname)
|
||||
mv.visitJumpInsn(IFEQ, l1)
|
||||
val l2 = new Label()
|
||||
mv.visitLabel(l2)
|
||||
mv.visitInsn(ICONST_0)
|
||||
mv.visitInsn(IRETURN)
|
||||
mv.visitLabel(l1)
|
||||
mv.visitFrame(F_SAME, 0, null, 0, null)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitMethodInsn(
|
||||
INVOKESPECIAL,
|
||||
"codechicken/multipart/TileMultipart",
|
||||
"canAddPart",
|
||||
"(Lcodechicken/multipart/TMultiPart;)Z"
|
||||
)
|
||||
mv.visitInsn(IRETURN)
|
||||
mv.visitMaxs(2, 2)
|
||||
mv.visitEnd()
|
||||
}
|
||||
|
||||
def generatePassThroughTrait(s_interface:String):String =
|
||||
{
|
||||
val iname = nodeName(s_interface)
|
||||
val tname = {
|
||||
var simpleName = iname.substring(iname.lastIndexOf('/')+1)
|
||||
if(simpleName.startsWith("I")) simpleName = simpleName.substring(1)
|
||||
"T" + simpleName + "$$PassThrough"
|
||||
}
|
||||
val vname = "impl"
|
||||
val idesc = "L"+iname+";"
|
||||
|
||||
val inode = classNode(s_interface)
|
||||
if(inode == null) {
|
||||
logger.error("Unable to generate pass through trait for: "+s_interface+" class not found.")
|
||||
return null
|
||||
}
|
||||
if((inode.access&ACC_INTERFACE) == 0) throw new IllegalArgumentException(s_interface+" is not an interface.")
|
||||
|
||||
val cw = new CC_ClassWriter(0)
|
||||
var mv:MethodVisitor = null
|
||||
var fv:FieldVisitor = null
|
||||
|
||||
cw.visit(V1_6, ACC_PUBLIC|ACC_SUPER, tname, null, "codechicken/multipart/TileMultipart", Array(iname))
|
||||
|
||||
{
|
||||
fv = cw.visitField(ACC_PRIVATE, vname, idesc, null, null)
|
||||
fv.visitEnd()
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null)
|
||||
mv.visitCode()
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "<init>", "()V")
|
||||
mv.visitInsn(RETURN)
|
||||
mv.visitMaxs(1, 1)
|
||||
mv.visitEnd()
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "bindPart", "(Lcodechicken/multipart/TMultiPart;)V", null, null)
|
||||
mv.visitCode()
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "bindPart", "(Lcodechicken/multipart/TMultiPart;)V")
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitTypeInsn(INSTANCEOF, iname)
|
||||
val l2 = new Label()
|
||||
mv.visitJumpInsn(IFEQ, l2)
|
||||
val l3 = new Label()
|
||||
mv.visitLabel(l3)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitTypeInsn(CHECKCAST, iname)
|
||||
mv.visitFieldInsn(PUTFIELD, tname, vname, idesc)
|
||||
mv.visitLabel(l2)
|
||||
mv.visitFrame(F_SAME, 0, null, 0, null)
|
||||
mv.visitInsn(RETURN)
|
||||
mv.visitMaxs(2, 2)
|
||||
mv.visitEnd()
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "partRemoved", "(Lcodechicken/multipart/TMultiPart;I)V", null, null)
|
||||
mv.visitCode()
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitVarInsn(ILOAD, 2)
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "partRemoved", "(Lcodechicken/multipart/TMultiPart;I)V")
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
|
||||
val l2 = new Label()
|
||||
mv.visitJumpInsn(IF_ACMPNE, l2)
|
||||
val l3 = new Label()
|
||||
mv.visitLabel(l3)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitInsn(ACONST_NULL)
|
||||
mv.visitFieldInsn(PUTFIELD, tname, vname, idesc)
|
||||
mv.visitLabel(l2)
|
||||
mv.visitFrame(F_SAME, 0, null, 0, null)
|
||||
mv.visitInsn(RETURN)
|
||||
mv.visitMaxs(3, 3)
|
||||
mv.visitEnd()
|
||||
}
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, "canAddPart", "(Lcodechicken/multipart/TMultiPart;)Z", null, null)
|
||||
mv.visitCode()
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
|
||||
val l1 = new Label()
|
||||
mv.visitJumpInsn(IFNULL, l1)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitTypeInsn(INSTANCEOF, iname)
|
||||
mv.visitJumpInsn(IFEQ, l1)
|
||||
val l2 = new Label()
|
||||
mv.visitLabel(l2)
|
||||
mv.visitInsn(ICONST_0)
|
||||
mv.visitInsn(IRETURN)
|
||||
mv.visitLabel(l1)
|
||||
mv.visitFrame(F_SAME, 0, null, 0, null)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitVarInsn(ALOAD, 1)
|
||||
mv.visitMethodInsn(INVOKESPECIAL, "codechicken/multipart/TileMultipart", "canAddPart", "(Lcodechicken/multipart/TMultiPart;)Z")
|
||||
mv.visitInsn(IRETURN)
|
||||
mv.visitMaxs(2, 2)
|
||||
mv.visitEnd()
|
||||
}
|
||||
|
||||
def methods(cnode:ClassNode):Map[String, MethodNode] =
|
||||
{
|
||||
val m = cnode.methods.map(m => (m.name+m.desc, m)).toMap
|
||||
if(cnode.interfaces != null)
|
||||
m++cnode.interfaces.flatMap(i => methods(classNode(i)))
|
||||
else
|
||||
m
|
||||
}
|
||||
|
||||
def generatePassThroughMethod(m:MethodNode)
|
||||
{
|
||||
mv = cw.visitMethod(ACC_PUBLIC, m.name, m.desc, m.signature, Array(m.exceptions:_*))
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
|
||||
finishBridgeCall(mv, m.desc, INVOKEINTERFACE, iname, m.name, m.desc)
|
||||
}
|
||||
|
||||
methods(inode).values.foreach(generatePassThroughMethod)
|
||||
|
||||
cw.visitEnd()
|
||||
internalDefine(tname, cw.toByteArray)
|
||||
registerTrait(tname)
|
||||
return tname
|
||||
def methods(cnode: ClassNode): Map[String, MethodNode] = {
|
||||
val m = cnode.methods.map(m => (m.name + m.desc, m)).toMap
|
||||
if (cnode.interfaces != null)
|
||||
m ++ cnode.interfaces.flatMap(i => methods(classNode(i)))
|
||||
else
|
||||
m
|
||||
}
|
||||
|
||||
override protected def onCompiled(clazz: Class[_ <: TileMultipart], traitSet:BitSet) =
|
||||
MultipartGenerator.registerTileClass(clazz, traitSet)
|
||||
def generatePassThroughMethod(m: MethodNode) {
|
||||
mv = cw.visitMethod(
|
||||
ACC_PUBLIC,
|
||||
m.name,
|
||||
m.desc,
|
||||
m.signature,
|
||||
Array(m.exceptions: _*)
|
||||
)
|
||||
mv.visitVarInsn(ALOAD, 0)
|
||||
mv.visitFieldInsn(GETFIELD, tname, vname, idesc)
|
||||
finishBridgeCall(mv, m.desc, INVOKEINTERFACE, iname, m.name, m.desc)
|
||||
}
|
||||
|
||||
methods(inode).values.foreach(generatePassThroughMethod)
|
||||
|
||||
cw.visitEnd()
|
||||
internalDefine(tname, cw.toByteArray)
|
||||
registerTrait(tname)
|
||||
return tname
|
||||
}
|
||||
|
||||
override protected def onCompiled(
|
||||
clazz: Class[_ <: TileMultipart],
|
||||
traitSet: BitSet
|
||||
) =
|
||||
MultipartGenerator.registerTileClass(clazz, traitSet)
|
||||
}
|
||||
@@ -10,344 +10,360 @@ import scala.collection.JavaConversions._
|
||||
|
||||
import ScalaSignature._
|
||||
|
||||
object ScalaSignature
|
||||
{
|
||||
object Bytes
|
||||
{
|
||||
def apply(arr: Array[Byte]):Bytes = Bytes(arr, 0, arr.length)
|
||||
}
|
||||
object ScalaSignature {
|
||||
object Bytes {
|
||||
def apply(arr: Array[Byte]): Bytes = Bytes(arr, 0, arr.length)
|
||||
}
|
||||
|
||||
case class Bytes(arr: Array[Byte], pos: Int, len: Int)
|
||||
{
|
||||
def reader = new ByteCodeReader(this)
|
||||
def section = arr.drop(pos).take(len)
|
||||
}
|
||||
case class Bytes(arr: Array[Byte], pos: Int, len: Int) {
|
||||
def reader = new ByteCodeReader(this)
|
||||
def section = arr.drop(pos).take(len)
|
||||
}
|
||||
|
||||
trait Flags
|
||||
{
|
||||
def hasFlag(flag: Int): Boolean
|
||||
trait Flags {
|
||||
def hasFlag(flag: Int): Boolean
|
||||
|
||||
def isPrivate = hasFlag(0x00000004)
|
||||
def isProtected = hasFlag(0x00000008)
|
||||
def isAbstract = hasFlag(0x00000080)
|
||||
def isDeferred = hasFlag(0x00000100)
|
||||
//abstract for methods
|
||||
def isMethod = hasFlag(0x00000200)
|
||||
def isModule = hasFlag(0x00000400)
|
||||
//object module class
|
||||
def isInterface = hasFlag(0x00000800)
|
||||
def isParam = hasFlag(0x00002000)
|
||||
def isStatic = hasFlag(0x00800000)
|
||||
def isTrait = hasFlag(0x02000000)
|
||||
def isAccessor = hasFlag(0x08000000)
|
||||
}
|
||||
def isPrivate = hasFlag(0x00000004)
|
||||
def isProtected = hasFlag(0x00000008)
|
||||
def isAbstract = hasFlag(0x00000080)
|
||||
def isDeferred = hasFlag(0x00000100)
|
||||
// abstract for methods
|
||||
def isMethod = hasFlag(0x00000200)
|
||||
def isModule = hasFlag(0x00000400)
|
||||
// object module class
|
||||
def isInterface = hasFlag(0x00000800)
|
||||
def isParam = hasFlag(0x00002000)
|
||||
def isStatic = hasFlag(0x00800000)
|
||||
def isTrait = hasFlag(0x02000000)
|
||||
def isAccessor = hasFlag(0x08000000)
|
||||
}
|
||||
}
|
||||
|
||||
class ScalaSignature(val bytes: Bytes)
|
||||
{
|
||||
val major = bytes.arr(0).toInt
|
||||
val minor = bytes.arr(1).toInt
|
||||
val table = {
|
||||
val bcr = bytes.reader
|
||||
bcr.pos = 2
|
||||
Array.tabulate(bcr.readNat){ i =>
|
||||
val start = bcr.pos
|
||||
val tpe = bcr.readByte
|
||||
val len = bcr.readNat
|
||||
bcr.advance(len)(new SigEntry(i, start, Bytes(bytes.arr, bcr.pos, len)))
|
||||
}
|
||||
class ScalaSignature(val bytes: Bytes) {
|
||||
val major = bytes.arr(0).toInt
|
||||
val minor = bytes.arr(1).toInt
|
||||
val table = {
|
||||
val bcr = bytes.reader
|
||||
bcr.pos = 2
|
||||
Array.tabulate(bcr.readNat) { i =>
|
||||
val start = bcr.pos
|
||||
val tpe = bcr.readByte
|
||||
val len = bcr.readNat
|
||||
bcr.advance(len)(new SigEntry(i, start, Bytes(bytes.arr, bcr.pos, len)))
|
||||
}
|
||||
}
|
||||
|
||||
trait SymbolRef extends Flags {
|
||||
def full: String
|
||||
def flags: Int
|
||||
def hasFlag(flag: Int) = (flags & flag) != 0
|
||||
}
|
||||
|
||||
trait ClassSymbolRef extends SymbolRef {
|
||||
def name: String
|
||||
def owner: SymbolRef
|
||||
def flags: Int
|
||||
def infoId: Int
|
||||
|
||||
def full = owner.full + "." + name
|
||||
override def toString = getClass.getName.replaceAll(".+\\$", "") +
|
||||
"(" + name + "," + owner + "," + flags.toHexString + "," + infoId + ")"
|
||||
|
||||
def isObject = false
|
||||
def info: ClassType = evalT(infoId)
|
||||
def jParent = info.parent.jName
|
||||
def jInterfaces = info.interfaces.map(_.jName)
|
||||
}
|
||||
|
||||
case class ClassSymbol(
|
||||
name: String,
|
||||
owner: SymbolRef,
|
||||
flags: Int,
|
||||
infoId: Int
|
||||
) extends ClassSymbolRef
|
||||
|
||||
case class ObjectSymbol(
|
||||
name: String,
|
||||
owner: SymbolRef,
|
||||
flags: Int,
|
||||
infoId: Int
|
||||
) extends ClassSymbolRef {
|
||||
override def isObject = true
|
||||
}
|
||||
|
||||
case class MethodSymbol(
|
||||
name: String,
|
||||
owner: SymbolRef,
|
||||
flags: Int,
|
||||
infoId: Int
|
||||
) extends SymbolRef {
|
||||
override def toString =
|
||||
"MethodSymbol(" + name + "," + owner + "," + flags.toHexString + "," + infoId + ")"
|
||||
def full = owner.full + "." + name
|
||||
|
||||
def info: TMethodType = evalT(infoId)
|
||||
def jDesc = info.jDesc
|
||||
}
|
||||
|
||||
case class ExternalSymbol(name: String) extends SymbolRef {
|
||||
override def toString = name
|
||||
def full = name
|
||||
def flags = 0
|
||||
}
|
||||
|
||||
case object NoSymbol extends SymbolRef {
|
||||
def full = "<no symbol>"
|
||||
def flags = 0
|
||||
}
|
||||
|
||||
trait TMethodType {
|
||||
def jDesc = "(" + params
|
||||
.map(m => m.info.returnType.jDesc)
|
||||
.mkString + ")" + returnType.jDesc
|
||||
def returnType: TypeRef
|
||||
def params: List[MethodSymbol]
|
||||
}
|
||||
|
||||
case class ClassType(owner: SymbolRef, parents: List[TypeRef]) {
|
||||
def parent = parents.head
|
||||
def interfaces = parents.drop(1)
|
||||
}
|
||||
|
||||
case class MethodType(returnType: TypeRef, params: List[MethodSymbol])
|
||||
extends TMethodType
|
||||
|
||||
case class ParameterlessType(returnType: TypeRef) extends TMethodType {
|
||||
def params = List()
|
||||
}
|
||||
|
||||
trait TypeRef {
|
||||
def sym: SymbolRef
|
||||
def name = sym.full
|
||||
|
||||
def jName = name.replace('.', '/') match {
|
||||
case "scala/AnyRef" | "scala/Any" => "java/lang/Object"
|
||||
case s => s
|
||||
}
|
||||
|
||||
trait SymbolRef extends Flags
|
||||
{
|
||||
def full: String
|
||||
def flags: Int
|
||||
def hasFlag(flag: Int) = (flags & flag) != 0
|
||||
def jDesc = name match {
|
||||
case "scala.Array" => null
|
||||
case "scala.Long" => "J"
|
||||
case "scala.Int" => "I"
|
||||
case "scala.Short" => "S"
|
||||
case "scala.Byte" => "B"
|
||||
case "scala.Double" => "D"
|
||||
case "scala.Float" => "F"
|
||||
case "scala.Boolean" => "Z"
|
||||
case "scala.Unit" => "V"
|
||||
case _ => "L" + jName + ";"
|
||||
}
|
||||
}
|
||||
|
||||
trait ClassSymbolRef extends SymbolRef
|
||||
{
|
||||
def name: String
|
||||
def owner: SymbolRef
|
||||
def flags: Int
|
||||
def infoId: Int
|
||||
case class TypeRefType(owner: TypeRef, sym: SymbolRef, typArgs: List[TypeRef])
|
||||
extends TMethodType
|
||||
with TypeRef {
|
||||
def params = List()
|
||||
|
||||
def full = owner.full + "." + name
|
||||
override def toString = getClass.getName.replaceAll(".+\\$", "") +
|
||||
"(" + name + "," + owner + "," + flags.toHexString + "," + infoId + ")"
|
||||
def returnType = this
|
||||
|
||||
def isObject = false
|
||||
def info: ClassType = evalT(infoId)
|
||||
def jParent = info.parent.jName
|
||||
def jInterfaces = info.interfaces.map(_.jName)
|
||||
override def jDesc = name match {
|
||||
case "scala.Array" => "[" + typArgs(0).jDesc
|
||||
case _ => super.jDesc
|
||||
}
|
||||
}
|
||||
|
||||
case class ClassSymbol(name: String, owner: SymbolRef, flags: Int, infoId: Int) extends ClassSymbolRef
|
||||
case class ThisType(sym: SymbolRef) extends TypeRef
|
||||
|
||||
case class ObjectSymbol(name: String, owner: SymbolRef, flags: Int, infoId: Int) extends ClassSymbolRef
|
||||
{
|
||||
override def isObject = true
|
||||
case class SingleType(owner: TypeRef, sym: SymbolRef) extends TypeRef {
|
||||
override def jName = super.jName + "$"
|
||||
}
|
||||
|
||||
case object NoType extends TypeRef {
|
||||
def sym = null
|
||||
override def name = "<no type>"
|
||||
}
|
||||
|
||||
case class SigEntry(index: Int, start: Int, bytes: Bytes) {
|
||||
def id = bytes.arr(start)
|
||||
def delete() = bytes.arr(start) = 3
|
||||
override def toString =
|
||||
"SigEntry(" + index + "," + id + "," + bytes.len + " bytes)"
|
||||
}
|
||||
|
||||
trait Literal {
|
||||
def value: Any
|
||||
}
|
||||
case class BooleanLiteral(value: Boolean) extends Literal
|
||||
case class ByteLiteral(value: Byte) extends Literal
|
||||
case class ShortLiteral(value: Short) extends Literal
|
||||
case class CharLiteral(value: Char) extends Literal
|
||||
case class IntLiteral(value: Int) extends Literal
|
||||
case class LongLiteral(value: Long) extends Literal
|
||||
case class FloatLiteral(value: Float) extends Literal
|
||||
case class DoubleLiteral(value: Double) extends Literal
|
||||
case object NullLiteral extends Literal {
|
||||
override def value = null
|
||||
}
|
||||
case class StringLiteral(value: String) extends Literal
|
||||
case class TypeLiteral(value: TypeRef) extends Literal
|
||||
case class EnumLiteral(value: ExternalSymbol) extends Literal
|
||||
case class ArrayLiteral(value: List[_]) extends Literal
|
||||
|
||||
case class AnnotationInfo(
|
||||
owner: SymbolRef,
|
||||
annType: TypeRef,
|
||||
values: Map[String, Literal]
|
||||
) {
|
||||
def getValue[T](name: String) = values(name).asInstanceOf[T]
|
||||
}
|
||||
|
||||
def evalS(i: Int): String = {
|
||||
val e = table(i)
|
||||
val bc = e.bytes
|
||||
val bcr = bc.reader
|
||||
e.id match {
|
||||
case 1 | 2 => bcr.readString(bc.len)
|
||||
case 3 => NoSymbol.full
|
||||
case 9 | 10 =>
|
||||
var s = evalS(bcr.readNat)
|
||||
if (bc.pos + bc.len > bcr.pos)
|
||||
s = evalS(bcr.readNat) + "." + s
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
case class MethodSymbol(name: String, owner: SymbolRef, flags: Int, infoId: Int) extends SymbolRef
|
||||
{
|
||||
override def toString = "MethodSymbol(" + name + "," + owner + "," + flags.toHexString + "," + infoId + ")"
|
||||
def full = owner.full + "." + name
|
||||
def evalT[T](i: Int) = eval(i).asInstanceOf[T]
|
||||
|
||||
def info: TMethodType = evalT(infoId)
|
||||
def jDesc = info.jDesc
|
||||
def evalList[T](bcr: ByteCodeReader) = {
|
||||
val l = List.newBuilder
|
||||
while (bcr.more)
|
||||
l += evalT(bcr.readNat)
|
||||
l.result()
|
||||
}
|
||||
|
||||
def eval(i: Int): Any = {
|
||||
// only parse the ones that matter for this project
|
||||
val e = table(i)
|
||||
val bcr = e.bytes.reader
|
||||
|
||||
def nat = bcr.readNat
|
||||
def evalS = this.evalS(nat)
|
||||
def evalT[T] = this.evalT[T](nat)
|
||||
def evalList[T] = this.evalList[T](bcr)
|
||||
|
||||
e.id match {
|
||||
case 1 | 2 => this.evalS(i)
|
||||
case 3 => NoSymbol
|
||||
case 6 => ClassSymbol(evalS, evalT, nat, nat)
|
||||
case 7 => ObjectSymbol(evalS, evalT, nat, nat)
|
||||
case 8 => MethodSymbol(evalS, evalT, nat, nat)
|
||||
case 9 | 10 => ExternalSymbol(this.evalS(i))
|
||||
case 11 | 12 => NoType // 12 is actually NoPrefixType (no lower bound)
|
||||
case 13 => ThisType(evalT)
|
||||
case 14 => SingleType(evalT, evalT)
|
||||
case 16 => TypeRefType(evalT, evalT, evalList)
|
||||
case 19 => ClassType(evalT, evalList)
|
||||
case 20 => MethodType(evalT, evalList)
|
||||
case 21 | 48 =>
|
||||
ParameterlessType(
|
||||
evalT
|
||||
) // 48 is actually a bounded super type, but it should work fine for this project
|
||||
case 25 => BooleanLiteral(bcr.readLong != 0)
|
||||
case 26 => ByteLiteral(bcr.readLong.toByte)
|
||||
case 27 => ShortLiteral(bcr.readLong.toShort)
|
||||
case 28 => CharLiteral(bcr.readLong.toChar)
|
||||
case 29 => IntLiteral(bcr.readLong.toInt)
|
||||
case 30 => LongLiteral(bcr.readLong)
|
||||
case 31 =>
|
||||
FloatLiteral(java.lang.Float.intBitsToFloat(bcr.readLong.toInt))
|
||||
case 32 => DoubleLiteral(java.lang.Double.longBitsToDouble(bcr.readLong))
|
||||
case 33 => StringLiteral(evalS)
|
||||
case 34 => NullLiteral
|
||||
case 35 => TypeLiteral(evalT)
|
||||
case 36 => EnumLiteral(evalT)
|
||||
case 40 =>
|
||||
AnnotationInfo(
|
||||
evalT,
|
||||
evalT,
|
||||
evalList.grouped(2).map(g => (g(0), g(1))).toMap
|
||||
)
|
||||
case 44 => ArrayLiteral(evalList)
|
||||
case _ => e
|
||||
}
|
||||
}
|
||||
|
||||
case class ExternalSymbol(name: String) extends SymbolRef
|
||||
{
|
||||
override def toString = name
|
||||
def full = name
|
||||
def flags = 0
|
||||
}
|
||||
def collect[T](id: Int) = (0 until table.length).collect {
|
||||
case i if table(i).id == id => evalT(i): T
|
||||
}
|
||||
|
||||
case object NoSymbol extends SymbolRef
|
||||
{
|
||||
def full = "<no symbol>"
|
||||
def flags = 0
|
||||
}
|
||||
|
||||
trait TMethodType
|
||||
{
|
||||
def jDesc = "(" + params.map(m => m.info.returnType.jDesc).mkString + ")" + returnType.jDesc
|
||||
def returnType: TypeRef
|
||||
def params: List[MethodSymbol]
|
||||
}
|
||||
|
||||
case class ClassType(owner: SymbolRef, parents: List[TypeRef])
|
||||
{
|
||||
def parent = parents.head
|
||||
def interfaces = parents.drop(1)
|
||||
}
|
||||
|
||||
case class MethodType(returnType: TypeRef, params: List[MethodSymbol]) extends TMethodType
|
||||
|
||||
case class ParameterlessType(returnType: TypeRef) extends TMethodType
|
||||
{
|
||||
def params = List()
|
||||
}
|
||||
|
||||
trait TypeRef
|
||||
{
|
||||
def sym: SymbolRef
|
||||
def name = sym.full
|
||||
|
||||
def jName = name.replace('.', '/') match {
|
||||
case "scala/AnyRef" | "scala/Any" => "java/lang/Object"
|
||||
case s => s
|
||||
}
|
||||
|
||||
def jDesc = name match {
|
||||
case "scala.Array" => null
|
||||
case "scala.Long" => "J"
|
||||
case "scala.Int" => "I"
|
||||
case "scala.Short" => "S"
|
||||
case "scala.Byte" => "B"
|
||||
case "scala.Double" => "D"
|
||||
case "scala.Float" => "F"
|
||||
case "scala.Boolean" => "Z"
|
||||
case "scala.Unit" => "V"
|
||||
case _ => "L" + jName + ";"
|
||||
}
|
||||
}
|
||||
|
||||
case class TypeRefType(owner: TypeRef, sym: SymbolRef, typArgs: List[TypeRef]) extends TMethodType with TypeRef
|
||||
{
|
||||
def params = List()
|
||||
|
||||
def returnType = this
|
||||
|
||||
override def jDesc = name match {
|
||||
case "scala.Array" => "[" + typArgs(0).jDesc
|
||||
case _ => super.jDesc
|
||||
}
|
||||
}
|
||||
|
||||
case class ThisType(sym: SymbolRef) extends TypeRef
|
||||
|
||||
case class SingleType(owner: TypeRef, sym: SymbolRef) extends TypeRef
|
||||
{
|
||||
override def jName = super.jName + "$"
|
||||
}
|
||||
|
||||
case object NoType extends TypeRef
|
||||
{
|
||||
def sym = null
|
||||
override def name = "<no type>"
|
||||
}
|
||||
|
||||
case class SigEntry(index:Int, start: Int, bytes: Bytes)
|
||||
{
|
||||
def id = bytes.arr(start)
|
||||
def delete() = bytes.arr(start) = 3
|
||||
override def toString = "SigEntry("+index+","+id+","+bytes.len+" bytes)"
|
||||
}
|
||||
|
||||
trait Literal
|
||||
{
|
||||
def value:Any
|
||||
}
|
||||
case class BooleanLiteral(value:Boolean) extends Literal
|
||||
case class ByteLiteral(value:Byte) extends Literal
|
||||
case class ShortLiteral(value:Short) extends Literal
|
||||
case class CharLiteral(value:Char) extends Literal
|
||||
case class IntLiteral(value:Int) extends Literal
|
||||
case class LongLiteral(value:Long) extends Literal
|
||||
case class FloatLiteral(value:Float) extends Literal
|
||||
case class DoubleLiteral(value:Double) extends Literal
|
||||
case object NullLiteral extends Literal
|
||||
{
|
||||
override def value = null
|
||||
}
|
||||
case class StringLiteral(value:String) extends Literal
|
||||
case class TypeLiteral(value:TypeRef) extends Literal
|
||||
case class EnumLiteral(value:ExternalSymbol) extends Literal
|
||||
case class ArrayLiteral(value:List[_]) extends Literal
|
||||
|
||||
case class AnnotationInfo(owner:SymbolRef, annType:TypeRef, values:Map[String, Literal])
|
||||
{
|
||||
def getValue[T](name:String) = values(name).asInstanceOf[T]
|
||||
}
|
||||
|
||||
def evalS(i: Int): String = {
|
||||
val e = table(i)
|
||||
val bc = e.bytes
|
||||
val bcr = bc.reader
|
||||
e.id match {
|
||||
case 1 | 2 => bcr.readString(bc.len)
|
||||
case 3 => NoSymbol.full
|
||||
case 9 | 10 =>
|
||||
var s = evalS(bcr.readNat)
|
||||
if (bc.pos + bc.len > bcr.pos)
|
||||
s = evalS(bcr.readNat) + "." + s
|
||||
s
|
||||
}
|
||||
}
|
||||
|
||||
def evalT[T](i: Int) = eval(i).asInstanceOf[T]
|
||||
|
||||
def evalList[T](bcr: ByteCodeReader) = {
|
||||
val l = List.newBuilder
|
||||
while (bcr.more)
|
||||
l += evalT(bcr.readNat)
|
||||
l.result()
|
||||
}
|
||||
|
||||
def eval(i: Int): Any = {
|
||||
//only parse the ones that matter for this project
|
||||
val e = table(i)
|
||||
val bcr = e.bytes.reader
|
||||
|
||||
def nat = bcr.readNat
|
||||
def evalS = this.evalS(nat)
|
||||
def evalT[T] = this.evalT[T](nat)
|
||||
def evalList[T] = this.evalList[T](bcr)
|
||||
|
||||
e.id match {
|
||||
case 1 | 2 => this.evalS(i)
|
||||
case 3 => NoSymbol
|
||||
case 6 => ClassSymbol(evalS, evalT, nat, nat)
|
||||
case 7 => ObjectSymbol(evalS, evalT, nat, nat)
|
||||
case 8 => MethodSymbol(evalS, evalT, nat, nat)
|
||||
case 9 | 10 => ExternalSymbol(this.evalS(i))
|
||||
case 11 | 12 => NoType //12 is actually NoPrefixType (no lower bound)
|
||||
case 13 => ThisType(evalT)
|
||||
case 14 => SingleType(evalT, evalT)
|
||||
case 16 => TypeRefType(evalT, evalT, evalList)
|
||||
case 19 => ClassType(evalT, evalList)
|
||||
case 20 => MethodType(evalT, evalList)
|
||||
case 21 | 48 => ParameterlessType(evalT) //48 is actually a bounded super type, but it should work fine for this project
|
||||
case 25 => BooleanLiteral(bcr.readLong != 0)
|
||||
case 26 => ByteLiteral(bcr.readLong.toByte)
|
||||
case 27 => ShortLiteral(bcr.readLong.toShort)
|
||||
case 28 => CharLiteral(bcr.readLong.toChar)
|
||||
case 29 => IntLiteral(bcr.readLong.toInt)
|
||||
case 30 => LongLiteral(bcr.readLong)
|
||||
case 31 => FloatLiteral(java.lang.Float.intBitsToFloat(bcr.readLong.toInt))
|
||||
case 32 => DoubleLiteral(java.lang.Double.longBitsToDouble(bcr.readLong))
|
||||
case 33 => StringLiteral(evalS)
|
||||
case 34 => NullLiteral
|
||||
case 35 => TypeLiteral(evalT)
|
||||
case 36 => EnumLiteral(evalT)
|
||||
case 40 => AnnotationInfo(evalT, evalT, evalList.grouped(2).map(g => (g(0), g(1))).toMap)
|
||||
case 44 => ArrayLiteral(evalList)
|
||||
case _ => e
|
||||
}
|
||||
}
|
||||
|
||||
def collect[T](id: Int) = (0 until table.length).collect {
|
||||
case i if table(i).id == id => evalT(i): T
|
||||
}
|
||||
|
||||
def findObject(name: String) = collect[ObjectSymbol](7).find(_.full == name)
|
||||
def findClass(name: String) = collect[ClassSymbol](6).find(c => !c.isModule && c.full == name)
|
||||
def findObject(name: String) = collect[ObjectSymbol](7).find(_.full == name)
|
||||
def findClass(name: String) =
|
||||
collect[ClassSymbol](6).find(c => !c.isModule && c.full == name)
|
||||
}
|
||||
|
||||
class ByteCodeReader(val bc: Bytes)
|
||||
{
|
||||
var pos = bc.pos
|
||||
class ByteCodeReader(val bc: Bytes) {
|
||||
var pos = bc.pos
|
||||
|
||||
def more = pos < bc.pos + bc.len
|
||||
def more = pos < bc.pos + bc.len
|
||||
|
||||
def readString(len: Int) = advance(len)(new String(bc.arr.drop(pos).take(len)))
|
||||
def readString(len: Int) =
|
||||
advance(len)(new String(bc.arr.drop(pos).take(len)))
|
||||
|
||||
def readByte = advance(1)(bc.arr(pos))
|
||||
def readByte = advance(1)(bc.arr(pos))
|
||||
|
||||
def readNat = {
|
||||
var r = 0
|
||||
var b = 0
|
||||
do {
|
||||
b = readByte
|
||||
r = r << 7 | b & 0x7F
|
||||
}
|
||||
while ((b & 0x80) != 0)
|
||||
r
|
||||
def readNat = {
|
||||
var r = 0
|
||||
var b = 0
|
||||
do {
|
||||
b = readByte
|
||||
r = r << 7 | b & 0x7f
|
||||
} while ((b & 0x80) != 0)
|
||||
r
|
||||
}
|
||||
|
||||
def readLong = {
|
||||
var l = 0L
|
||||
while (more) {
|
||||
l <<= 8
|
||||
l |= readByte & 0xff
|
||||
}
|
||||
l
|
||||
}
|
||||
|
||||
def readLong = {
|
||||
var l = 0L
|
||||
while(more) {
|
||||
l <<= 8
|
||||
l |= readByte & 0xFF
|
||||
}
|
||||
l
|
||||
}
|
||||
|
||||
def advance[A](len: Int)(r: A) = {
|
||||
if (pos + len > bc.pos + bc.len)
|
||||
throw new IllegalArgumentException("Ran off the end of bytecode")
|
||||
pos += len
|
||||
r
|
||||
}
|
||||
def advance[A](len: Int)(r: A) = {
|
||||
if (pos + len > bc.pos + bc.len)
|
||||
throw new IllegalArgumentException("Ran off the end of bytecode")
|
||||
pos += len
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
object ScalaSigReader
|
||||
{
|
||||
def decode(s: String) = {
|
||||
val bytes = s.getBytes
|
||||
bytes take ByteCodecs.decode(bytes)
|
||||
object ScalaSigReader {
|
||||
def decode(s: String) = {
|
||||
val bytes = s.getBytes
|
||||
bytes take ByteCodecs.decode(bytes)
|
||||
}
|
||||
|
||||
def encode(b: Array[Byte]) = {
|
||||
val bytes = ByteCodecs.encode8to7(b)
|
||||
var i = 0
|
||||
while (i < bytes.length) {
|
||||
bytes(i) = ((bytes(i) + 1) & 0x7f).toByte
|
||||
i += 1
|
||||
}
|
||||
new String(bytes.take(bytes.length - 1), "UTF-8")
|
||||
}
|
||||
|
||||
def encode(b: Array[Byte]) = {
|
||||
val bytes = ByteCodecs.encode8to7(b)
|
||||
var i = 0
|
||||
while (i < bytes.length) {
|
||||
bytes(i) = ((bytes(i) + 1) & 0x7F).toByte
|
||||
i += 1
|
||||
}
|
||||
new String(bytes.take(bytes.length - 1), "UTF-8")
|
||||
def read(ann: AnnotationNode): ScalaSignature = new ScalaSignature(
|
||||
Bytes(decode(ann.values.get(1).asInstanceOf[String]))
|
||||
)
|
||||
|
||||
def write(sig: ScalaSignature, ann: AnnotationNode) =
|
||||
ann.values.set(1, encode(sig.bytes.arr))
|
||||
|
||||
def ann(cnode: ClassNode): Option[AnnotationNode] =
|
||||
cnode.visibleAnnotations match {
|
||||
case null => None
|
||||
case a => a.find(ann => ann.desc.equals("Lscala/reflect/ScalaSignature;"))
|
||||
}
|
||||
|
||||
def read(ann: AnnotationNode): ScalaSignature = new ScalaSignature(Bytes(decode(ann.values.get(1).asInstanceOf[String])))
|
||||
|
||||
def write(sig: ScalaSignature, ann: AnnotationNode) = ann.values.set(1, encode(sig.bytes.arr))
|
||||
|
||||
def ann(cnode: ClassNode): Option[AnnotationNode] = cnode.visibleAnnotations match {
|
||||
case null => None
|
||||
case a => a.find(ann => ann.desc.equals("Lscala/reflect/ScalaSignature;"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
package codechicken.multipart.asm
|
||||
import java.util.BitSet
|
||||
|
||||
trait ScratchBitSet
|
||||
{
|
||||
private val bitSets = new ThreadLocal[BitSet]
|
||||
trait ScratchBitSet {
|
||||
private val bitSets = new ThreadLocal[BitSet]
|
||||
|
||||
def getBitSet = bitSets.get match {
|
||||
case null =>
|
||||
val bitset = new BitSet
|
||||
bitSets.set(bitset)
|
||||
bitset
|
||||
case bitset => bitset
|
||||
}
|
||||
def getBitSet = bitSets.get match {
|
||||
case null =>
|
||||
val bitset = new BitSet
|
||||
bitSets.set(bitset)
|
||||
bitset
|
||||
case bitset => bitset
|
||||
}
|
||||
|
||||
def freshBitSet = {
|
||||
val bitset = getBitSet
|
||||
bitset.clear()
|
||||
bitset
|
||||
}
|
||||
def freshBitSet = {
|
||||
val bitset = getBitSet
|
||||
bitset.clear()
|
||||
bitset
|
||||
}
|
||||
}
|
||||
@@ -7,294 +7,300 @@ import org.objectweb.asm.Type
|
||||
import org.objectweb.asm.Type._
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
object StackAnalyser
|
||||
{
|
||||
def width(t:Type):Int = t.getSize
|
||||
def width(s:String):Int = width(Type.getType(s))
|
||||
def width(it:Iterable[Type]):Int = it.foldLeft(0)(_+width(_))
|
||||
|
||||
abstract class StackEntry(implicit val insn:AbstractInsnNode)
|
||||
{
|
||||
def getType:Type
|
||||
}
|
||||
abstract class LocalEntry
|
||||
{
|
||||
def getType:Type
|
||||
}
|
||||
object StackAnalyser {
|
||||
def width(t: Type): Int = t.getSize
|
||||
def width(s: String): Int = width(Type.getType(s))
|
||||
def width(it: Iterable[Type]): Int = it.foldLeft(0)(_ + width(_))
|
||||
|
||||
case class This(owner:Type) extends LocalEntry
|
||||
{
|
||||
def getType = owner
|
||||
}
|
||||
case class Param(i:Int, t:Type) extends LocalEntry
|
||||
{
|
||||
def getType = t
|
||||
}
|
||||
case class Store(e:StackEntry)(implicit val insn:AbstractInsnNode) extends LocalEntry
|
||||
{
|
||||
def getType = e.getType
|
||||
}
|
||||
abstract class StackEntry(implicit val insn: AbstractInsnNode) {
|
||||
def getType: Type
|
||||
}
|
||||
abstract class LocalEntry {
|
||||
def getType: Type
|
||||
}
|
||||
|
||||
case class Const(c:Any)(implicit insn:AbstractInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = c match {
|
||||
case o:Byte => BYTE_TYPE
|
||||
case o:Short => SHORT_TYPE
|
||||
case o:Int => INT_TYPE
|
||||
case o:Long => LONG_TYPE
|
||||
case o:Float => FLOAT_TYPE
|
||||
case o:Double => DOUBLE_TYPE
|
||||
case o:Char => CHAR_TYPE
|
||||
case o:Boolean => BOOLEAN_TYPE
|
||||
case o:String => getObjectType("java/lang/String")
|
||||
case null => getObjectType("java/lang/Object")
|
||||
case _ => throw new IllegalArgumentException("Unknown const "+c)
|
||||
}
|
||||
}
|
||||
case class Load(e:LocalEntry)(implicit insn:AbstractInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = e.getType
|
||||
}
|
||||
case class UnaryOp(op:Int, e:StackEntry)(implicit insn:AbstractInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = e.getType
|
||||
}
|
||||
case class BinaryOp(op:Int, e2:StackEntry, e1:StackEntry)(implicit insn:AbstractInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = e1.getType
|
||||
}
|
||||
case class PrimitiveCast(e:StackEntry, t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = t
|
||||
}
|
||||
case class ReturnAddress()(implicit insn:AbstractInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = INT_TYPE
|
||||
}
|
||||
case class GetField(obj:StackEntry, field:FieldInsnNode)(implicit insn:AbstractInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = Type.getType(field.desc)
|
||||
}
|
||||
case class Invoke(op:Int, params:Array[StackEntry], obj:StackEntry, method:MethodInsnNode)(implicit insn:AbstractInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = Type.getMethodType(method.desc).getReturnType
|
||||
}
|
||||
case class New(t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = t
|
||||
}
|
||||
case class NewArray(len:StackEntry, t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = t
|
||||
}
|
||||
case class ArrayLength(array:StackEntry)(implicit insn:AbstractInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = INT_TYPE
|
||||
}
|
||||
case class ArrayLoad(index:StackEntry, e:StackEntry)(implicit insn:AbstractInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = e.getType.getElementType
|
||||
}
|
||||
case class Cast(obj:StackEntry, t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = t
|
||||
}
|
||||
case class NewMultiArray(sizes:Array[StackEntry], t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = t
|
||||
}
|
||||
case class CaughtException(t:Type)(implicit insn:AbstractInsnNode) extends StackEntry
|
||||
{
|
||||
def getType = t
|
||||
case class This(owner: Type) extends LocalEntry {
|
||||
def getType = owner
|
||||
}
|
||||
case class Param(i: Int, t: Type) extends LocalEntry {
|
||||
def getType = t
|
||||
}
|
||||
case class Store(e: StackEntry)(implicit val insn: AbstractInsnNode)
|
||||
extends LocalEntry {
|
||||
def getType = e.getType
|
||||
}
|
||||
|
||||
case class Const(c: Any)(implicit insn: AbstractInsnNode) extends StackEntry {
|
||||
def getType = c match {
|
||||
case o: Byte => BYTE_TYPE
|
||||
case o: Short => SHORT_TYPE
|
||||
case o: Int => INT_TYPE
|
||||
case o: Long => LONG_TYPE
|
||||
case o: Float => FLOAT_TYPE
|
||||
case o: Double => DOUBLE_TYPE
|
||||
case o: Char => CHAR_TYPE
|
||||
case o: Boolean => BOOLEAN_TYPE
|
||||
case o: String => getObjectType("java/lang/String")
|
||||
case null => getObjectType("java/lang/Object")
|
||||
case _ => throw new IllegalArgumentException("Unknown const " + c)
|
||||
}
|
||||
}
|
||||
case class Load(e: LocalEntry)(implicit insn: AbstractInsnNode)
|
||||
extends StackEntry {
|
||||
def getType = e.getType
|
||||
}
|
||||
case class UnaryOp(op: Int, e: StackEntry)(implicit insn: AbstractInsnNode)
|
||||
extends StackEntry {
|
||||
def getType = e.getType
|
||||
}
|
||||
case class BinaryOp(op: Int, e2: StackEntry, e1: StackEntry)(implicit
|
||||
insn: AbstractInsnNode
|
||||
) extends StackEntry {
|
||||
def getType = e1.getType
|
||||
}
|
||||
case class PrimitiveCast(e: StackEntry, t: Type)(implicit
|
||||
insn: AbstractInsnNode
|
||||
) extends StackEntry {
|
||||
def getType = t
|
||||
}
|
||||
case class ReturnAddress()(implicit insn: AbstractInsnNode)
|
||||
extends StackEntry {
|
||||
def getType = INT_TYPE
|
||||
}
|
||||
case class GetField(obj: StackEntry, field: FieldInsnNode)(implicit
|
||||
insn: AbstractInsnNode
|
||||
) extends StackEntry {
|
||||
def getType = Type.getType(field.desc)
|
||||
}
|
||||
case class Invoke(
|
||||
op: Int,
|
||||
params: Array[StackEntry],
|
||||
obj: StackEntry,
|
||||
method: MethodInsnNode
|
||||
)(implicit insn: AbstractInsnNode)
|
||||
extends StackEntry {
|
||||
def getType = Type.getMethodType(method.desc).getReturnType
|
||||
}
|
||||
case class New(t: Type)(implicit insn: AbstractInsnNode) extends StackEntry {
|
||||
def getType = t
|
||||
}
|
||||
case class NewArray(len: StackEntry, t: Type)(implicit insn: AbstractInsnNode)
|
||||
extends StackEntry {
|
||||
def getType = t
|
||||
}
|
||||
case class ArrayLength(array: StackEntry)(implicit insn: AbstractInsnNode)
|
||||
extends StackEntry {
|
||||
def getType = INT_TYPE
|
||||
}
|
||||
case class ArrayLoad(index: StackEntry, e: StackEntry)(implicit
|
||||
insn: AbstractInsnNode
|
||||
) extends StackEntry {
|
||||
def getType = e.getType.getElementType
|
||||
}
|
||||
case class Cast(obj: StackEntry, t: Type)(implicit insn: AbstractInsnNode)
|
||||
extends StackEntry {
|
||||
def getType = t
|
||||
}
|
||||
case class NewMultiArray(sizes: Array[StackEntry], t: Type)(implicit
|
||||
insn: AbstractInsnNode
|
||||
) extends StackEntry {
|
||||
def getType = t
|
||||
}
|
||||
case class CaughtException(t: Type)(implicit insn: AbstractInsnNode)
|
||||
extends StackEntry {
|
||||
def getType = t
|
||||
}
|
||||
}
|
||||
|
||||
class StackAnalyser(val owner:Type, val m:MethodNode)
|
||||
{
|
||||
import StackAnalyser._
|
||||
|
||||
val stack = MList[StackEntry]()
|
||||
val locals = MList[LocalEntry]()
|
||||
private val catchHandlers = MMap[LabelNode, TryCatchBlockNode]()
|
||||
|
||||
{
|
||||
if((m.access & ACC_STATIC) == 0)
|
||||
pushL(This(owner))
|
||||
|
||||
val ptypes = getArgumentTypes(m.desc)
|
||||
for(i <- 0 until ptypes.length)
|
||||
pushL(Param(i, ptypes(i)))
|
||||
|
||||
m.tryCatchBlocks.foreach(b => catchHandlers.put(b.handler, b))
|
||||
class StackAnalyser(val owner: Type, val m: MethodNode) {
|
||||
import StackAnalyser._
|
||||
|
||||
val stack = MList[StackEntry]()
|
||||
val locals = MList[LocalEntry]()
|
||||
private val catchHandlers = MMap[LabelNode, TryCatchBlockNode]()
|
||||
|
||||
{
|
||||
if ((m.access & ACC_STATIC) == 0)
|
||||
pushL(This(owner))
|
||||
|
||||
val ptypes = getArgumentTypes(m.desc)
|
||||
for (i <- 0 until ptypes.length)
|
||||
pushL(Param(i, ptypes(i)))
|
||||
|
||||
m.tryCatchBlocks.foreach(b => catchHandlers.put(b.handler, b))
|
||||
}
|
||||
|
||||
def pushL(entry: LocalEntry) = setL(locals.size, entry)
|
||||
|
||||
def setL(i: Int, entry: LocalEntry) = {
|
||||
while (i + entry.getType.getSize > locals.size) locals += null
|
||||
locals(i) = entry
|
||||
if (entry.getType.getSize == 2)
|
||||
locals(i + 1) = entry
|
||||
}
|
||||
|
||||
def push(entry: StackEntry) = insert(0, entry)
|
||||
|
||||
def _pop(i: Int = 0) = stack.remove(stack.size - i - 1)
|
||||
|
||||
def pop(i: Int = 0) = {
|
||||
val e = _pop(i)
|
||||
if (e.getType.getSize == 2) {
|
||||
if (peek(i) != e)
|
||||
throw new IllegalStateException(
|
||||
"Wide stack entry elems don't match (" + e + "," + peek(i)
|
||||
)
|
||||
_pop(i)
|
||||
}
|
||||
|
||||
def pushL(entry:LocalEntry) = setL(locals.size, entry)
|
||||
e
|
||||
}
|
||||
|
||||
def setL(i:Int, entry:LocalEntry) =
|
||||
{
|
||||
while(i+entry.getType.getSize > locals.size) locals += null
|
||||
locals(i) = entry
|
||||
if(entry.getType.getSize == 2)
|
||||
locals(i+1) = entry
|
||||
}
|
||||
|
||||
def push(entry:StackEntry) = insert(0, entry)
|
||||
def peek(i: Int = 0) = stack(stack.size - i - 1)
|
||||
|
||||
def _pop(i:Int = 0) = stack.remove(stack.size-i-1)
|
||||
def insert(i: Int, entry: StackEntry) {
|
||||
if (entry.getType.getSize == 0)
|
||||
return
|
||||
stack.insert(stack.size - i, entry)
|
||||
if (entry.getType.getSize == 2)
|
||||
stack.insert(stack.size - i, entry)
|
||||
}
|
||||
|
||||
def popArgs(desc: String) = {
|
||||
val t = getType(desc)
|
||||
val args = new Array[StackEntry](t.getArgumentTypes.length)
|
||||
for (i <- 0 until args.length)
|
||||
args(args.length - i - 1) = pop()
|
||||
args
|
||||
}
|
||||
|
||||
def visitInsn(ainsn: AbstractInsnNode) {
|
||||
implicit val thisInsn = ainsn // passes to any StackEntry we create
|
||||
ainsn match {
|
||||
case insn: InsnNode =>
|
||||
ainsn.getOpcode match {
|
||||
case ACONST_NULL => push(Const(null))
|
||||
case ICONST_M1 => push(Const(-1))
|
||||
case ICONST_0 => push(Const(0))
|
||||
case ICONST_1 => push(Const(1))
|
||||
case ICONST_2 => push(Const(2))
|
||||
case ICONST_3 => push(Const(3))
|
||||
case ICONST_4 => push(Const(4))
|
||||
case ICONST_5 => push(Const(5))
|
||||
case LCONST_0 => push(Const(0L))
|
||||
case LCONST_1 => push(Const(1L))
|
||||
case FCONST_0 => push(Const(0f))
|
||||
case FCONST_1 => push(Const(1f))
|
||||
case FCONST_2 => push(Const(2f))
|
||||
case DCONST_0 => push(Const(0d))
|
||||
case DCONST_1 => push(Const(1d))
|
||||
|
||||
case i if i >= IALOAD && i <= SALOAD =>
|
||||
push(ArrayLoad(pop(), pop()))
|
||||
case i if i >= IASTORE && i <= SASTORE =>
|
||||
pop(); pop(); pop()
|
||||
|
||||
case POP => _pop()
|
||||
case POP2 => _pop(); _pop()
|
||||
case DUP => push(peek())
|
||||
case DUP_X1 => insert(2, peek())
|
||||
case DUP_X2 => insert(3, peek())
|
||||
case DUP2 => push(peek(1)); push(peek(1))
|
||||
case DUP2_X1 => insert(3, peek(1)); insert(3, peek())
|
||||
case DUP2_X2 => insert(4, peek(1)); insert(4, peek())
|
||||
case SWAP => push(pop(1))
|
||||
|
||||
case i if i >= IADD && i <= DREM =>
|
||||
push(BinaryOp(i, pop(), pop()))
|
||||
case i if i >= INEG && i <= DNEG =>
|
||||
push(UnaryOp(i, pop()))
|
||||
case i if i >= ISHL && i <= LXOR =>
|
||||
push(BinaryOp(i, pop(), pop()))
|
||||
|
||||
case L2I | F2I | D2I => push(PrimitiveCast(pop(), DOUBLE_TYPE))
|
||||
case I2L | F2L | D2L => push(PrimitiveCast(pop(), LONG_TYPE))
|
||||
case I2F | L2F | D2F => push(PrimitiveCast(pop(), FLOAT_TYPE))
|
||||
case I2D | L2D | F2D => push(PrimitiveCast(pop(), DOUBLE_TYPE))
|
||||
case I2B => push(PrimitiveCast(pop(), BYTE_TYPE))
|
||||
case I2C => push(PrimitiveCast(pop(), CHAR_TYPE))
|
||||
case I2S => push(PrimitiveCast(pop(), SHORT_TYPE))
|
||||
|
||||
case i if i >= LCMP && i <= DCMPG =>
|
||||
push(BinaryOp(i, pop(), pop()))
|
||||
|
||||
case i if i >= IRETURN && i <= ARETURN => pop()
|
||||
|
||||
case ARRAYLENGTH => push(ArrayLength(pop()))
|
||||
case ATHROW => pop()
|
||||
case MONITORENTER | MONITOREXIT => pop()
|
||||
|
||||
case _ =>
|
||||
|
||||
def pop(i:Int = 0) =
|
||||
{
|
||||
val e = _pop(i)
|
||||
if(e.getType.getSize == 2)
|
||||
{
|
||||
if(peek(i) != e) throw new IllegalStateException("Wide stack entry elems don't match ("+e+","+peek(i))
|
||||
_pop(i)
|
||||
}
|
||||
e
|
||||
}
|
||||
|
||||
def peek(i:Int = 0) = stack(stack.size-i-1)
|
||||
|
||||
def insert(i:Int, entry:StackEntry)
|
||||
{
|
||||
if(entry.getType.getSize == 0)
|
||||
return
|
||||
stack.insert(stack.size-i, entry)
|
||||
if(entry.getType.getSize == 2)
|
||||
stack.insert(stack.size-i, entry)
|
||||
}
|
||||
|
||||
def popArgs(desc:String) =
|
||||
{
|
||||
val t = getType(desc)
|
||||
val args = new Array[StackEntry](t.getArgumentTypes.length)
|
||||
for(i <- 0 until args.length)
|
||||
args(args.length-i-1) = pop()
|
||||
args
|
||||
}
|
||||
|
||||
def visitInsn(ainsn:AbstractInsnNode)
|
||||
{
|
||||
implicit val thisInsn = ainsn//passes to any StackEntry we create
|
||||
ainsn match {
|
||||
case insn:InsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case ACONST_NULL => push(Const(null))
|
||||
case ICONST_M1 => push(Const(-1))
|
||||
case ICONST_0 => push(Const(0))
|
||||
case ICONST_1 => push(Const(1))
|
||||
case ICONST_2 => push(Const(2))
|
||||
case ICONST_3 => push(Const(3))
|
||||
case ICONST_4 => push(Const(4))
|
||||
case ICONST_5 => push(Const(5))
|
||||
case LCONST_0 => push(Const(0L))
|
||||
case LCONST_1 => push(Const(1L))
|
||||
case FCONST_0 => push(Const(0F))
|
||||
case FCONST_1 => push(Const(1F))
|
||||
case FCONST_2 => push(Const(2F))
|
||||
case DCONST_0 => push(Const(0D))
|
||||
case DCONST_1 => push(Const(1D))
|
||||
|
||||
case i if i >= IALOAD && i <= SALOAD =>
|
||||
push(ArrayLoad(pop(), pop()))
|
||||
case i if i >= IASTORE && i <= SASTORE =>
|
||||
pop(); pop(); pop()
|
||||
|
||||
case POP => _pop()
|
||||
case POP2 => _pop(); _pop()
|
||||
case DUP => push(peek())
|
||||
case DUP_X1 => insert(2, peek())
|
||||
case DUP_X2 => insert(3, peek())
|
||||
case DUP2 => push(peek(1)); push(peek(1))
|
||||
case DUP2_X1 => insert(3, peek(1)); insert(3, peek())
|
||||
case DUP2_X2 => insert(4, peek(1)); insert(4, peek())
|
||||
case SWAP => push(pop(1))
|
||||
|
||||
case i if i >= IADD && i <= DREM =>
|
||||
push(BinaryOp(i, pop(), pop()))
|
||||
case i if i >= INEG && i <= DNEG =>
|
||||
push(UnaryOp(i, pop()))
|
||||
case i if i >= ISHL && i <= LXOR =>
|
||||
push(BinaryOp(i, pop(), pop()))
|
||||
|
||||
case L2I|F2I|D2I => push(PrimitiveCast(pop(), DOUBLE_TYPE))
|
||||
case I2L|F2L|D2L => push(PrimitiveCast(pop(), LONG_TYPE))
|
||||
case I2F|L2F|D2F => push(PrimitiveCast(pop(), FLOAT_TYPE))
|
||||
case I2D|L2D|F2D => push(PrimitiveCast(pop(), DOUBLE_TYPE))
|
||||
case I2B => push(PrimitiveCast(pop(), BYTE_TYPE))
|
||||
case I2C => push(PrimitiveCast(pop(), CHAR_TYPE))
|
||||
case I2S => push(PrimitiveCast(pop(), SHORT_TYPE))
|
||||
|
||||
case i if i >= LCMP && i <= DCMPG =>
|
||||
push(BinaryOp(i, pop(), pop()))
|
||||
|
||||
case i if i >= IRETURN && i <= ARETURN => pop()
|
||||
|
||||
case ARRAYLENGTH => push(ArrayLength(pop()))
|
||||
case ATHROW => pop()
|
||||
case MONITORENTER|MONITOREXIT => pop()
|
||||
|
||||
case _ =>
|
||||
|
||||
}
|
||||
case iinsn:IntInsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case BIPUSH => push(Const(iinsn.operand.toByte))
|
||||
case SIPUSH => push(Const(iinsn.operand.toShort))
|
||||
}
|
||||
case ldcinsn:LdcInsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case LDC => push(Const(ldcinsn.cst))
|
||||
}
|
||||
case vinsn:VarInsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case i if i >= ILOAD && i <= ALOAD =>
|
||||
push(Load(locals(vinsn.`var`)))
|
||||
case i if i >= ISTORE && i <= ASTORE =>
|
||||
setL(vinsn.`var`, Store(pop()))
|
||||
}
|
||||
case incinsn:IincInsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case IINC => setL(incinsn.`var`, Store(BinaryOp(IINC, Const(incinsn.incr), Load(locals(incinsn.`var`)))))
|
||||
}
|
||||
case jinsn:JumpInsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case i if i >= IFEQ && i <= IFLE => pop()
|
||||
case i if i >= IF_ICMPEQ && i <= IF_ACMPNE => pop(); pop()
|
||||
case JSR => push(ReturnAddress())
|
||||
case IFNULL|IFNONNULL => pop()
|
||||
case GOTO =>
|
||||
}
|
||||
case sinsn:TableSwitchInsnNode => pop()
|
||||
case linsn:LookupSwitchInsnNode => pop()
|
||||
case finsn:FieldInsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case GETSTATIC => push(GetField(null, finsn))
|
||||
case PUTSTATIC => pop()
|
||||
case GETFIELD => push(GetField(pop(), finsn))
|
||||
case PUTFIELD => pop(); pop()
|
||||
}
|
||||
case minsn:MethodInsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case INVOKEVIRTUAL|INVOKESPECIAL|INVOKEINTERFACE =>
|
||||
push(Invoke(ainsn.getOpcode, popArgs(minsn.desc), pop(), minsn))
|
||||
case INVOKESTATIC =>
|
||||
push(Invoke(ainsn.getOpcode, popArgs(minsn.desc), null, minsn))
|
||||
}
|
||||
case tinsn:TypeInsnNode => ainsn.getOpcode match
|
||||
{
|
||||
case NEW => push(New(getObjectType(tinsn.desc)))
|
||||
case NEWARRAY => push(NewArray(pop(), getObjectType(tinsn.desc)))
|
||||
case ANEWARRAY => push(NewArray(pop(), getObjectType("["+tinsn.desc)))
|
||||
case CHECKCAST => push(Cast(pop(), getObjectType(tinsn.desc)))
|
||||
case INSTANCEOF => push(UnaryOp(INSTANCEOF, pop()))
|
||||
}
|
||||
case mainsn:MultiANewArrayInsnNode =>
|
||||
val sizes = new Array[StackEntry](mainsn.dims)
|
||||
for(i <- 0 until sizes.length)
|
||||
sizes(i) = pop()
|
||||
push(NewMultiArray(sizes, getType(mainsn.desc)))
|
||||
/*case fnode:FrameNode => fnode.`type` match
|
||||
case iinsn: IntInsnNode =>
|
||||
ainsn.getOpcode match {
|
||||
case BIPUSH => push(Const(iinsn.operand.toByte))
|
||||
case SIPUSH => push(Const(iinsn.operand.toShort))
|
||||
}
|
||||
case ldcinsn: LdcInsnNode =>
|
||||
ainsn.getOpcode match {
|
||||
case LDC => push(Const(ldcinsn.cst))
|
||||
}
|
||||
case vinsn: VarInsnNode =>
|
||||
ainsn.getOpcode match {
|
||||
case i if i >= ILOAD && i <= ALOAD =>
|
||||
push(Load(locals(vinsn.`var`)))
|
||||
case i if i >= ISTORE && i <= ASTORE =>
|
||||
setL(vinsn.`var`, Store(pop()))
|
||||
}
|
||||
case incinsn: IincInsnNode =>
|
||||
ainsn.getOpcode match {
|
||||
case IINC =>
|
||||
setL(
|
||||
incinsn.`var`,
|
||||
Store(
|
||||
BinaryOp(IINC, Const(incinsn.incr), Load(locals(incinsn.`var`)))
|
||||
)
|
||||
)
|
||||
}
|
||||
case jinsn: JumpInsnNode =>
|
||||
ainsn.getOpcode match {
|
||||
case i if i >= IFEQ && i <= IFLE => pop()
|
||||
case i if i >= IF_ICMPEQ && i <= IF_ACMPNE => pop(); pop()
|
||||
case JSR => push(ReturnAddress())
|
||||
case IFNULL | IFNONNULL => pop()
|
||||
case GOTO =>
|
||||
}
|
||||
case sinsn: TableSwitchInsnNode => pop()
|
||||
case linsn: LookupSwitchInsnNode => pop()
|
||||
case finsn: FieldInsnNode =>
|
||||
ainsn.getOpcode match {
|
||||
case GETSTATIC => push(GetField(null, finsn))
|
||||
case PUTSTATIC => pop()
|
||||
case GETFIELD => push(GetField(pop(), finsn))
|
||||
case PUTFIELD => pop(); pop()
|
||||
}
|
||||
case minsn: MethodInsnNode =>
|
||||
ainsn.getOpcode match {
|
||||
case INVOKEVIRTUAL | INVOKESPECIAL | INVOKEINTERFACE =>
|
||||
push(Invoke(ainsn.getOpcode, popArgs(minsn.desc), pop(), minsn))
|
||||
case INVOKESTATIC =>
|
||||
push(Invoke(ainsn.getOpcode, popArgs(minsn.desc), null, minsn))
|
||||
}
|
||||
case tinsn: TypeInsnNode =>
|
||||
ainsn.getOpcode match {
|
||||
case NEW => push(New(getObjectType(tinsn.desc)))
|
||||
case NEWARRAY => push(NewArray(pop(), getObjectType(tinsn.desc)))
|
||||
case ANEWARRAY =>
|
||||
push(NewArray(pop(), getObjectType("[" + tinsn.desc)))
|
||||
case CHECKCAST => push(Cast(pop(), getObjectType(tinsn.desc)))
|
||||
case INSTANCEOF => push(UnaryOp(INSTANCEOF, pop()))
|
||||
}
|
||||
case mainsn: MultiANewArrayInsnNode =>
|
||||
val sizes = new Array[StackEntry](mainsn.dims)
|
||||
for (i <- 0 until sizes.length)
|
||||
sizes(i) = pop()
|
||||
push(NewMultiArray(sizes, getType(mainsn.desc)))
|
||||
/*case fnode:FrameNode => fnode.`type` match
|
||||
{
|
||||
case F_NEW|F_FULL => println("reset stacks/locals")
|
||||
case F_APPEND => println("add local")
|
||||
@@ -302,13 +308,13 @@ class StackAnalyser(val owner:Type, val m:MethodNode)
|
||||
case F_SAME => println("reset")
|
||||
case F_SAME1 => println("reset locals and all but bottom stack")
|
||||
}*/
|
||||
case lnode:LabelNode =>
|
||||
catchHandlers.get(lnode) match
|
||||
{
|
||||
case Some(tcblock) => push(CaughtException(Type.getType(tcblock.`type`)))
|
||||
case None =>
|
||||
}
|
||||
case _ =>
|
||||
case lnode: LabelNode =>
|
||||
catchHandlers.get(lnode) match {
|
||||
case Some(tcblock) =>
|
||||
push(CaughtException(Type.getType(tcblock.`type`)))
|
||||
case None =>
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,28 +4,44 @@ import net.minecraft.world.World
|
||||
import cpw.mods.fml.common.FMLCommonHandler
|
||||
import net.minecraft.block.Block
|
||||
|
||||
object MultipartCompatiblity
|
||||
{
|
||||
var canAddPart = (world:World, x:Int, y:Int, z:Int) => true
|
||||
object MultipartCompatiblity {
|
||||
var canAddPart = (world: World, x: Int, y: Int, z: Int) => true
|
||||
|
||||
def load() {
|
||||
if(FMLCommonHandler.instance().getModName.contains("mcpc"))
|
||||
MCPCCompatModule.load()
|
||||
}
|
||||
def load() {
|
||||
if (FMLCommonHandler.instance().getModName.contains("mcpc"))
|
||||
MCPCCompatModule.load()
|
||||
}
|
||||
}
|
||||
|
||||
object MCPCCompatModule
|
||||
{
|
||||
def load() {
|
||||
try {
|
||||
val m_canPlacePart = classOf[World].getDeclaredMethod("canPlaceMultipart", classOf[Block], classOf[Int], classOf[Int], classOf[Int])
|
||||
MultipartCompatiblity.canAddPart = (world:World, x:Int, y:Int, z:Int) => {
|
||||
m_canPlacePart.invoke(world, MultipartProxy.block, x:Integer, y:Integer, z:Integer).asInstanceOf[Boolean].booleanValue()
|
||||
}
|
||||
} catch {
|
||||
case e:Exception => MultipartProxy.logger.error("Failed to integrate MCPC placement hooks", e)
|
||||
object MCPCCompatModule {
|
||||
def load() {
|
||||
try {
|
||||
val m_canPlacePart = classOf[World].getDeclaredMethod(
|
||||
"canPlaceMultipart",
|
||||
classOf[Block],
|
||||
classOf[Int],
|
||||
classOf[Int],
|
||||
classOf[Int]
|
||||
)
|
||||
MultipartCompatiblity.canAddPart =
|
||||
(world: World, x: Int, y: Int, z: Int) => {
|
||||
m_canPlacePart
|
||||
.invoke(
|
||||
world,
|
||||
MultipartProxy.block,
|
||||
x: Integer,
|
||||
y: Integer,
|
||||
z: Integer
|
||||
)
|
||||
.asInstanceOf[Boolean]
|
||||
.booleanValue()
|
||||
}
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
MultipartProxy.logger.error(
|
||||
"Failed to integrate MCPC placement hooks",
|
||||
e
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
package codechicken.multipart.handler
|
||||
|
||||
import codechicken.multipart.{TileCache, TileMultipart, MultiPartRegistry, BlockMultipart}
|
||||
import codechicken.multipart.{
|
||||
TileCache,
|
||||
TileMultipart,
|
||||
MultiPartRegistry,
|
||||
BlockMultipart
|
||||
}
|
||||
import cpw.mods.fml.common.eventhandler.{EventPriority, SubscribeEvent}
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import codechicken.lib.packet.PacketCustom
|
||||
@@ -16,52 +21,61 @@ import net.minecraft.util.MovingObjectPosition
|
||||
import net.minecraft.util.MovingObjectPosition.MovingObjectType
|
||||
import cpw.mods.fml.common.gameevent.TickEvent
|
||||
|
||||
object MultipartEventHandler
|
||||
{
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
def tileEntityLoad(event:ChunkDataEvent.Load)
|
||||
{
|
||||
MultipartSaveLoad.loadTiles(event.getChunk)
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
def worldUnLoad(event:WorldEvent.Unload)
|
||||
{
|
||||
MultipartSPH.onWorldUnload(event.world)
|
||||
if(event.world.isRemote)
|
||||
TileCache.clear()
|
||||
}
|
||||
object MultipartEventHandler {
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
||||
def tileEntityLoad(event: ChunkDataEvent.Load) {
|
||||
MultipartSaveLoad.loadTiles(event.getChunk)
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
def chunkWatch(event:ChunkWatchEvent.Watch)
|
||||
{
|
||||
MultipartSPH.onChunkWatch(event.player, event.chunk)
|
||||
}
|
||||
@SubscribeEvent
|
||||
def worldUnLoad(event: WorldEvent.Unload) {
|
||||
MultipartSPH.onWorldUnload(event.world)
|
||||
if (event.world.isRemote)
|
||||
TileCache.clear()
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
def chunkUnWatch(event:ChunkWatchEvent.UnWatch)
|
||||
{
|
||||
MultipartSPH.onChunkUnWatch(event.player, event.chunk)
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
def drawBlockHighlight(event:DrawBlockHighlightEvent)
|
||||
{
|
||||
if(event.target != null && event.target.typeOfHit == MovingObjectType.BLOCK &&
|
||||
event.player.worldObj.getTileEntity(event.target.blockX, event.target.blockY, event.target.blockZ).isInstanceOf[TileMultipart])
|
||||
{
|
||||
if(BlockMultipart.drawHighlight(event.player.worldObj, event.player, event.target, event.partialTicks))
|
||||
event.setCanceled(true)
|
||||
}
|
||||
}
|
||||
@SubscribeEvent
|
||||
def chunkWatch(event: ChunkWatchEvent.Watch) {
|
||||
MultipartSPH.onChunkWatch(event.player, event.chunk)
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
def serverTick(event:TickEvent.ServerTickEvent)
|
||||
{
|
||||
if(event.phase == TickEvent.Phase.END)
|
||||
MultipartSPH.onTickEnd(
|
||||
MinecraftServer.getServer.getConfigurationManager.playerEntityList
|
||||
.asInstanceOf[List[EntityPlayerMP]].asScala)
|
||||
@SubscribeEvent
|
||||
def chunkUnWatch(event: ChunkWatchEvent.UnWatch) {
|
||||
MultipartSPH.onChunkUnWatch(event.player, event.chunk)
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@SideOnly(Side.CLIENT)
|
||||
def drawBlockHighlight(event: DrawBlockHighlightEvent) {
|
||||
if (
|
||||
event.target != null && event.target.typeOfHit == MovingObjectType.BLOCK &&
|
||||
event.player.worldObj
|
||||
.getTileEntity(
|
||||
event.target.blockX,
|
||||
event.target.blockY,
|
||||
event.target.blockZ
|
||||
)
|
||||
.isInstanceOf[TileMultipart]
|
||||
) {
|
||||
if (
|
||||
BlockMultipart.drawHighlight(
|
||||
event.player.worldObj,
|
||||
event.player,
|
||||
event.target,
|
||||
event.partialTicks
|
||||
)
|
||||
)
|
||||
event.setCanceled(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
def serverTick(event: TickEvent.ServerTickEvent) {
|
||||
if (event.phase == TickEvent.Phase.END)
|
||||
MultipartSPH.onTickEnd(
|
||||
MinecraftServer.getServer.getConfigurationManager.playerEntityList
|
||||
.asInstanceOf[List[EntityPlayerMP]]
|
||||
.asScala
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -8,29 +8,32 @@ import cpw.mods.fml.common.Mod.EventHandler
|
||||
import cpw.mods.fml.common.event.FMLServerAboutToStartEvent
|
||||
import codechicken.multipart.MultiPartRegistry
|
||||
|
||||
@Mod(modid = "ForgeMultipart", acceptedMinecraftVersions = "[1.7.10]", modLanguage = "scala")
|
||||
object MultipartMod
|
||||
{
|
||||
@EventHandler
|
||||
def preInit(event: FMLPreInitializationEvent) {
|
||||
MultipartProxy.preInit(event.getModConfigurationDirectory, event.getModLog)
|
||||
}
|
||||
@Mod(
|
||||
modid = "ForgeMultipart",
|
||||
acceptedMinecraftVersions = "[1.7.10]",
|
||||
modLanguage = "scala"
|
||||
)
|
||||
object MultipartMod {
|
||||
@EventHandler
|
||||
def preInit(event: FMLPreInitializationEvent) {
|
||||
MultipartProxy.preInit(event.getModConfigurationDirectory, event.getModLog)
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
def init(event: FMLInitializationEvent) {
|
||||
MultipartProxy.init()
|
||||
}
|
||||
@EventHandler
|
||||
def init(event: FMLInitializationEvent) {
|
||||
MultipartProxy.init()
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
def postInit(event: FMLPostInitializationEvent) {
|
||||
if (MultiPartRegistry.required) {
|
||||
MultiPartRegistry.postInit()
|
||||
MultipartProxy.postInit()
|
||||
}
|
||||
@EventHandler
|
||||
def postInit(event: FMLPostInitializationEvent) {
|
||||
if (MultiPartRegistry.required) {
|
||||
MultiPartRegistry.postInit()
|
||||
MultipartProxy.postInit()
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
def beforeServerStart(event: FMLServerAboutToStartEvent) {
|
||||
MultiPartRegistry.beforeServerStart()
|
||||
}
|
||||
}
|
||||
@EventHandler
|
||||
def beforeServerStart(event: FMLServerAboutToStartEvent) {
|
||||
MultiPartRegistry.beforeServerStart()
|
||||
}
|
||||
}
|
||||
@@ -12,78 +12,88 @@ import scala.collection.mutable
|
||||
import codechicken.multipart.MultipartHelper.IPartTileConverter
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
/**
|
||||
* Hack due to lack of TileEntityLoadEvent in forge
|
||||
*/
|
||||
object MultipartSaveLoad
|
||||
{
|
||||
val converters = mutable.MutableList[IPartTileConverter[_]]()
|
||||
var loadingWorld: World = _
|
||||
/** Hack due to lack of TileEntityLoadEvent in forge
|
||||
*/
|
||||
object MultipartSaveLoad {
|
||||
val converters = mutable.MutableList[IPartTileConverter[_]]()
|
||||
var loadingWorld: World = _
|
||||
|
||||
class TileNBTContainer extends TileEntity
|
||||
{
|
||||
var tag: NBTTagCompound = _
|
||||
class TileNBTContainer extends TileEntity {
|
||||
var tag: NBTTagCompound = _
|
||||
|
||||
override def readFromNBT(t: NBTTagCompound) {
|
||||
super.readFromNBT(t)
|
||||
tag = t
|
||||
}
|
||||
override def readFromNBT(t: NBTTagCompound) {
|
||||
super.readFromNBT(t)
|
||||
tag = t
|
||||
}
|
||||
}
|
||||
|
||||
def hookLoader() {
|
||||
val field = classOf[TileEntity].getDeclaredField(
|
||||
new ObfMapping("net/minecraft/tileentity/TileEntity", "field_145855_i", "Ljava/util/Map;")
|
||||
.toRuntime.s_name)
|
||||
field.setAccessible(true)
|
||||
val map = field.get(null).asInstanceOf[Map[String, Class[_ <: TileEntity]]]
|
||||
map.put("savedMultipart", classOf[TileNBTContainer])
|
||||
def hookLoader() {
|
||||
val field = classOf[TileEntity].getDeclaredField(
|
||||
new ObfMapping(
|
||||
"net/minecraft/tileentity/TileEntity",
|
||||
"field_145855_i",
|
||||
"Ljava/util/Map;"
|
||||
).toRuntime.s_name
|
||||
)
|
||||
field.setAccessible(true)
|
||||
val map = field.get(null).asInstanceOf[Map[String, Class[_ <: TileEntity]]]
|
||||
map.put("savedMultipart", classOf[TileNBTContainer])
|
||||
}
|
||||
|
||||
private val classToNameMap = getClassToNameMap
|
||||
|
||||
def registerTileClass(t: Class[_ <: TileEntity]) {
|
||||
classToNameMap.put(t, "savedMultipart")
|
||||
}
|
||||
|
||||
def getClassToNameMap = {
|
||||
val field = classOf[TileEntity].getDeclaredField(
|
||||
new ObfMapping(
|
||||
"net/minecraft/tileentity/TileEntity",
|
||||
"field_145853_j",
|
||||
"Ljava/util/Map;"
|
||||
).toRuntime.s_name
|
||||
)
|
||||
field.setAccessible(true)
|
||||
field.get(null).asInstanceOf[Map[Class[_ <: TileEntity], String]]
|
||||
}
|
||||
|
||||
def loadTiles(chunk: Chunk) {
|
||||
loadingWorld = chunk.worldObj
|
||||
val iterator = chunk.chunkTileEntityMap
|
||||
.asInstanceOf[Map[ChunkPosition, TileEntity]]
|
||||
.entrySet
|
||||
.iterator
|
||||
while (iterator.hasNext) {
|
||||
val e = iterator.next
|
||||
var next = false
|
||||
val t = e.getValue match {
|
||||
case t: TileNBTContainer if t.tag.getString("id") == "savedMultipart" =>
|
||||
TileMultipart.createFromNBT(
|
||||
e.getValue.asInstanceOf[TileNBTContainer].tag
|
||||
)
|
||||
case t =>
|
||||
converters.find(_.canConvert(t)) match {
|
||||
case Some(c) =>
|
||||
val parts = c.convert(t)
|
||||
if (!parts.isEmpty)
|
||||
MultipartHelper.createTileFromParts(parts)
|
||||
else
|
||||
null
|
||||
case _ =>
|
||||
next = true
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
if (!next) {
|
||||
if (t != null) {
|
||||
t.setWorldObj(e.getValue.getWorldObj)
|
||||
t.validate()
|
||||
e.setValue(t)
|
||||
} else
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
|
||||
private val classToNameMap = getClassToNameMap
|
||||
|
||||
def registerTileClass(t: Class[_ <: TileEntity]) {
|
||||
classToNameMap.put(t, "savedMultipart")
|
||||
}
|
||||
|
||||
def getClassToNameMap = {
|
||||
val field = classOf[TileEntity].getDeclaredField(
|
||||
new ObfMapping("net/minecraft/tileentity/TileEntity", "field_145853_j", "Ljava/util/Map;")
|
||||
.toRuntime.s_name)
|
||||
field.setAccessible(true)
|
||||
field.get(null).asInstanceOf[Map[Class[_ <: TileEntity], String]]
|
||||
}
|
||||
|
||||
def loadTiles(chunk: Chunk) {
|
||||
loadingWorld = chunk.worldObj
|
||||
val iterator = chunk.chunkTileEntityMap.asInstanceOf[Map[ChunkPosition, TileEntity]].entrySet.iterator
|
||||
while (iterator.hasNext) {
|
||||
val e = iterator.next
|
||||
var next = false
|
||||
val t = e.getValue match {
|
||||
case t:TileNBTContainer if t.tag.getString("id") == "savedMultipart" =>
|
||||
TileMultipart.createFromNBT(e.getValue.asInstanceOf[TileNBTContainer].tag)
|
||||
case t => converters.find(_.canConvert(t)) match {
|
||||
case Some(c) =>
|
||||
val parts = c.convert(t)
|
||||
if(!parts.isEmpty)
|
||||
MultipartHelper.createTileFromParts(parts)
|
||||
else
|
||||
null
|
||||
case _ =>
|
||||
next = true
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
if(!next) {
|
||||
if (t != null) {
|
||||
t.setWorldObj(e.getValue.getWorldObj)
|
||||
t.validate()
|
||||
e.setValue(t)
|
||||
}
|
||||
else
|
||||
iterator.remove()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
package codechicken.multipart.handler
|
||||
|
||||
import codechicken.lib.packet.PacketCustom.{IHandshakeHandler, IClientPacketHandler, IServerPacketHandler}
|
||||
import codechicken.lib.packet.PacketCustom.{
|
||||
IHandshakeHandler,
|
||||
IClientPacketHandler,
|
||||
IServerPacketHandler
|
||||
}
|
||||
import codechicken.lib.packet.PacketCustom
|
||||
import net.minecraft.client.Minecraft
|
||||
import codechicken.multipart.MultiPartRegistry
|
||||
@@ -28,160 +32,207 @@ import net.minecraft.network.play.server.S40PacketDisconnect
|
||||
import net.minecraft.util.{ChatComponentText, ChatComponentTranslation}
|
||||
import net.minecraft.network.NetHandlerPlayServer
|
||||
|
||||
class MultipartPH
|
||||
{
|
||||
val channel = MultipartMod
|
||||
val registryChannel = "ForgeMultipart"
|
||||
class MultipartPH {
|
||||
val channel = MultipartMod
|
||||
val registryChannel = "ForgeMultipart"
|
||||
}
|
||||
|
||||
object MultipartCPH extends MultipartPH with IClientPacketHandler
|
||||
{
|
||||
def handlePacket(packet: PacketCustom, mc: Minecraft, netHandler:INetHandlerPlayClient) {
|
||||
try {
|
||||
packet.getType match {
|
||||
case 1 => handlePartRegistration(packet, netHandler)
|
||||
case 2 => handleCompressedTileDesc(packet, mc.theWorld)
|
||||
case 3 => handleCompressedTileData(packet, mc.theWorld)
|
||||
}
|
||||
}
|
||||
catch {
|
||||
case e:RuntimeException if e.getMessage != null && e.getMessage.startsWith("DC: ") =>
|
||||
netHandler.handleDisconnect(new S40PacketDisconnect(new ChatComponentText(e.getMessage.substring(4))))
|
||||
}
|
||||
object MultipartCPH extends MultipartPH with IClientPacketHandler {
|
||||
def handlePacket(
|
||||
packet: PacketCustom,
|
||||
mc: Minecraft,
|
||||
netHandler: INetHandlerPlayClient
|
||||
) {
|
||||
try {
|
||||
packet.getType match {
|
||||
case 1 => handlePartRegistration(packet, netHandler)
|
||||
case 2 => handleCompressedTileDesc(packet, mc.theWorld)
|
||||
case 3 => handleCompressedTileData(packet, mc.theWorld)
|
||||
}
|
||||
} catch {
|
||||
case e: RuntimeException
|
||||
if e.getMessage != null && e.getMessage.startsWith("DC: ") =>
|
||||
netHandler.handleDisconnect(
|
||||
new S40PacketDisconnect(
|
||||
new ChatComponentText(e.getMessage.substring(4))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
def handlePartRegistration(packet: PacketCustom, netHandler: INetHandlerPlayClient) {
|
||||
val missing = MultiPartRegistry.readIDMap(packet)
|
||||
if (!missing.isEmpty)
|
||||
netHandler.handleDisconnect(new S40PacketDisconnect(new ChatComponentTranslation("multipart.missing", missing.mkString(", "))))
|
||||
}
|
||||
def handlePartRegistration(
|
||||
packet: PacketCustom,
|
||||
netHandler: INetHandlerPlayClient
|
||||
) {
|
||||
val missing = MultiPartRegistry.readIDMap(packet)
|
||||
if (!missing.isEmpty)
|
||||
netHandler.handleDisconnect(
|
||||
new S40PacketDisconnect(
|
||||
new ChatComponentTranslation(
|
||||
"multipart.missing",
|
||||
missing.mkString(", ")
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
def handleCompressedTileDesc(packet: PacketCustom, world: World) {
|
||||
val cc = new ChunkCoordIntPair(packet.readInt, packet.readInt)
|
||||
val num = packet.readUShort
|
||||
for (i <- 0 until num)
|
||||
TileMultipart.handleDescPacket(world, indexInChunk(cc, packet.readShort), packet)
|
||||
}
|
||||
def handleCompressedTileDesc(packet: PacketCustom, world: World) {
|
||||
val cc = new ChunkCoordIntPair(packet.readInt, packet.readInt)
|
||||
val num = packet.readUShort
|
||||
for (i <- 0 until num)
|
||||
TileMultipart.handleDescPacket(
|
||||
world,
|
||||
indexInChunk(cc, packet.readShort),
|
||||
packet
|
||||
)
|
||||
}
|
||||
|
||||
def handleCompressedTileData(packet: PacketCustom, world: World) {
|
||||
var x = packet.readInt
|
||||
while (x != Int.MaxValue) {
|
||||
val pos = new BlockCoord(x, packet.readInt, packet.readInt)
|
||||
var i = packet.readUByte
|
||||
while (i < 255) {
|
||||
TileMultipart.handlePacket(pos, world, i, packet)
|
||||
i = packet.readUByte
|
||||
}
|
||||
x = packet.readInt
|
||||
}
|
||||
def handleCompressedTileData(packet: PacketCustom, world: World) {
|
||||
var x = packet.readInt
|
||||
while (x != Int.MaxValue) {
|
||||
val pos = new BlockCoord(x, packet.readInt, packet.readInt)
|
||||
var i = packet.readUByte
|
||||
while (i < 255) {
|
||||
TileMultipart.handlePacket(pos, world, i, packet)
|
||||
i = packet.readUByte
|
||||
}
|
||||
x = packet.readInt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object MultipartSPH extends MultipartPH with IServerPacketHandler with IHandshakeHandler
|
||||
{
|
||||
class MCByteStream(bout: ByteArrayOutputStream) extends MCDataOutputWrapper(new DataOutputStream(bout))
|
||||
{
|
||||
def getBytes = bout.toByteArray
|
||||
object MultipartSPH
|
||||
extends MultipartPH
|
||||
with IServerPacketHandler
|
||||
with IHandshakeHandler {
|
||||
class MCByteStream(bout: ByteArrayOutputStream)
|
||||
extends MCDataOutputWrapper(new DataOutputStream(bout)) {
|
||||
def getBytes = bout.toByteArray
|
||||
}
|
||||
|
||||
private val updateMap = Map[World, Map[BlockCoord, MCByteStream]]()
|
||||
|
||||
/** These maps are keyed by entityID so that new player instances with the
|
||||
* same entity id don't conflict world references
|
||||
*/
|
||||
private val chunkWatchers = new HashMap[Int, Set[ChunkCoordIntPair]]
|
||||
with MultiMap[Int, ChunkCoordIntPair]
|
||||
private val newWatchers = Map[Int, LinkedList[ChunkCoordIntPair]]()
|
||||
|
||||
def handlePacket(
|
||||
packet: PacketCustom,
|
||||
sender: EntityPlayerMP,
|
||||
netHandler: INetHandlerPlayServer
|
||||
) {
|
||||
packet.getType match {
|
||||
case 1 => ControlKeyModifer.map.put(sender, packet.readBoolean)
|
||||
}
|
||||
}
|
||||
|
||||
private val updateMap = Map[World, Map[BlockCoord, MCByteStream]]()
|
||||
/**
|
||||
* These maps are keyed by entityID so that new player instances with the same entity id don't conflict world references
|
||||
*/
|
||||
private val chunkWatchers = new HashMap[Int, Set[ChunkCoordIntPair]] with MultiMap[Int, ChunkCoordIntPair]
|
||||
private val newWatchers = Map[Int, LinkedList[ChunkCoordIntPair]]()
|
||||
def handshakeRecieved(netHandler: NetHandlerPlayServer) {
|
||||
val packet = new PacketCustom(registryChannel, 1)
|
||||
MultiPartRegistry.writeIDMap(packet)
|
||||
netHandler.sendPacket(packet.toPacket)
|
||||
}
|
||||
|
||||
def handlePacket(packet: PacketCustom, sender: EntityPlayerMP, netHandler:INetHandlerPlayServer) {
|
||||
packet.getType match {
|
||||
case 1 => ControlKeyModifer.map.put(sender, packet.readBoolean)
|
||||
def onWorldUnload(world: World) {
|
||||
if (!world.isRemote)
|
||||
updateMap.remove(world)
|
||||
}
|
||||
|
||||
def getTileStream(world: World, pos: BlockCoord) =
|
||||
updateMap
|
||||
.getOrElseUpdate(
|
||||
world, {
|
||||
if (world.isRemote)
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot use MultipartSPH on a client world"
|
||||
)
|
||||
Map()
|
||||
}
|
||||
}
|
||||
|
||||
def handshakeRecieved(netHandler: NetHandlerPlayServer) {
|
||||
val packet = new PacketCustom(registryChannel, 1)
|
||||
MultiPartRegistry.writeIDMap(packet)
|
||||
netHandler.sendPacket(packet.toPacket)
|
||||
}
|
||||
|
||||
def onWorldUnload(world: World) {
|
||||
if (!world.isRemote)
|
||||
updateMap.remove(world)
|
||||
}
|
||||
|
||||
def getTileStream(world: World, pos: BlockCoord) =
|
||||
updateMap.getOrElseUpdate(world, {
|
||||
if (world.isRemote)
|
||||
throw new IllegalArgumentException("Cannot use MultipartSPH on a client world")
|
||||
Map()
|
||||
}).getOrElseUpdate(pos, {
|
||||
val s = new MCByteStream(new ByteArrayOutputStream)
|
||||
s.writeCoord(pos)
|
||||
s
|
||||
})
|
||||
|
||||
def onTickEnd(players: Seq[EntityPlayerMP]) {
|
||||
PacketScheduler.sendScheduled()
|
||||
|
||||
for (p <- players if chunkWatchers.containsKey(p.getEntityId)) {
|
||||
updateMap.get(p.worldObj) match {
|
||||
case Some(m) if !m.isEmpty =>
|
||||
val chunks = chunkWatchers(p.getEntityId)
|
||||
val packet = new PacketCustom(channel, 3).compress()
|
||||
var send = false
|
||||
for ((pos, stream) <- m if chunks(new ChunkCoordIntPair(pos.x >> 4, pos.z >> 4))) {
|
||||
send = true
|
||||
packet.writeByteArray(stream.getBytes)
|
||||
packet.writeByte(255) //terminator
|
||||
}
|
||||
if (send) {
|
||||
packet.writeInt(Int.MaxValue) //terminator
|
||||
packet.sendToPlayer(p)
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
)
|
||||
.getOrElseUpdate(
|
||||
pos, {
|
||||
val s = new MCByteStream(new ByteArrayOutputStream)
|
||||
s.writeCoord(pos)
|
||||
s
|
||||
}
|
||||
updateMap.foreach(_._2.clear())
|
||||
for (p <- players if newWatchers.containsKey(p.getEntityId)) {
|
||||
for (c <- newWatchers(p.getEntityId)) {
|
||||
val chunk = p.worldObj.getChunkFromChunkCoords(c.chunkXPos, c.chunkZPos)
|
||||
val pkt = getDescPacket(chunk, chunk.chunkTileEntityMap.asInstanceOf[JMap[_, TileEntity]].values.iterator)
|
||||
if (pkt != null) pkt.sendToPlayer(p)
|
||||
chunkWatchers.addBinding(p.getEntityId, c)
|
||||
}
|
||||
}
|
||||
newWatchers.clear()
|
||||
}
|
||||
)
|
||||
|
||||
def onChunkWatch(p: EntityPlayer, c: ChunkCoordIntPair) {
|
||||
newWatchers.getOrElseUpdate(p.getEntityId, new LinkedList).add(c)
|
||||
}
|
||||
def onTickEnd(players: Seq[EntityPlayerMP]) {
|
||||
PacketScheduler.sendScheduled()
|
||||
|
||||
def onChunkUnWatch(p: EntityPlayer, c: ChunkCoordIntPair) {
|
||||
newWatchers.get(p.getEntityId) match {
|
||||
case Some(chunks) => chunks.remove(c)
|
||||
case _ =>
|
||||
}
|
||||
chunkWatchers.removeBinding(p.getEntityId, c)
|
||||
for (p <- players if chunkWatchers.containsKey(p.getEntityId)) {
|
||||
updateMap.get(p.worldObj) match {
|
||||
case Some(m) if !m.isEmpty =>
|
||||
val chunks = chunkWatchers(p.getEntityId)
|
||||
val packet = new PacketCustom(channel, 3).compress()
|
||||
var send = false
|
||||
for (
|
||||
(pos, stream) <- m
|
||||
if chunks(new ChunkCoordIntPair(pos.x >> 4, pos.z >> 4))
|
||||
) {
|
||||
send = true
|
||||
packet.writeByteArray(stream.getBytes)
|
||||
packet.writeByte(255) // terminator
|
||||
}
|
||||
if (send) {
|
||||
packet.writeInt(Int.MaxValue) // terminator
|
||||
packet.sendToPlayer(p)
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
||||
def getDescPacket(chunk: Chunk, it: Iterator[TileEntity]): PacketCustom = {
|
||||
val s = new MCByteStream(new ByteArrayOutputStream)
|
||||
|
||||
var num = 0
|
||||
while (it.hasNext) {
|
||||
val tile = it.next
|
||||
if (tile.isInstanceOf[TileMultipart]) {
|
||||
s.writeShort(indexInChunk(new BlockCoord(tile)))
|
||||
tile.asInstanceOf[TileMultipart].writeDesc(s)
|
||||
num += 1
|
||||
}
|
||||
}
|
||||
if (num != 0) {
|
||||
return new PacketCustom(channel, 2).compress()
|
||||
.writeInt(chunk.xPosition).writeInt(chunk.zPosition)
|
||||
.writeShort(num)
|
||||
.writeByteArray(s.getBytes)
|
||||
}
|
||||
return null
|
||||
updateMap.foreach(_._2.clear())
|
||||
for (p <- players if newWatchers.containsKey(p.getEntityId)) {
|
||||
for (c <- newWatchers(p.getEntityId)) {
|
||||
val chunk = p.worldObj.getChunkFromChunkCoords(c.chunkXPos, c.chunkZPos)
|
||||
val pkt = getDescPacket(
|
||||
chunk,
|
||||
chunk.chunkTileEntityMap
|
||||
.asInstanceOf[JMap[_, TileEntity]]
|
||||
.values
|
||||
.iterator
|
||||
)
|
||||
if (pkt != null) pkt.sendToPlayer(p)
|
||||
chunkWatchers.addBinding(p.getEntityId, c)
|
||||
}
|
||||
}
|
||||
newWatchers.clear()
|
||||
}
|
||||
|
||||
def onChunkWatch(p: EntityPlayer, c: ChunkCoordIntPair) {
|
||||
newWatchers.getOrElseUpdate(p.getEntityId, new LinkedList).add(c)
|
||||
}
|
||||
|
||||
def onChunkUnWatch(p: EntityPlayer, c: ChunkCoordIntPair) {
|
||||
newWatchers.get(p.getEntityId) match {
|
||||
case Some(chunks) => chunks.remove(c)
|
||||
case _ =>
|
||||
}
|
||||
chunkWatchers.removeBinding(p.getEntityId, c)
|
||||
}
|
||||
|
||||
def getDescPacket(chunk: Chunk, it: Iterator[TileEntity]): PacketCustom = {
|
||||
val s = new MCByteStream(new ByteArrayOutputStream)
|
||||
|
||||
var num = 0
|
||||
while (it.hasNext) {
|
||||
val tile = it.next
|
||||
if (tile.isInstanceOf[TileMultipart]) {
|
||||
s.writeShort(indexInChunk(new BlockCoord(tile)))
|
||||
tile.asInstanceOf[TileMultipart].writeDesc(s)
|
||||
num += 1
|
||||
}
|
||||
}
|
||||
if (num != 0) {
|
||||
return new PacketCustom(channel, 2)
|
||||
.compress()
|
||||
.writeInt(chunk.xPosition)
|
||||
.writeInt(chunk.zPosition)
|
||||
.writeShort(num)
|
||||
.writeByteArray(s.getBytes)
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -18,73 +18,110 @@ import cpw.mods.fml.common.registry.GameRegistry
|
||||
import net.minecraft.block.Block
|
||||
import org.apache.logging.log4j.Logger
|
||||
|
||||
class MultipartProxy_serverImpl
|
||||
{
|
||||
var block:BlockMultipart = _
|
||||
var config:ConfigFile = _
|
||||
var logger:Logger = _
|
||||
class MultipartProxy_serverImpl {
|
||||
var block: BlockMultipart = _
|
||||
var config: ConfigFile = _
|
||||
var logger: Logger = _
|
||||
|
||||
def preInit(cfgdir: File, logger:Logger) {
|
||||
this.logger = logger
|
||||
config = new ConfigFile(new File(cfgdir, "multipart.cfg"))
|
||||
.setComment("Multipart API config file")
|
||||
def preInit(cfgdir: File, logger: Logger) {
|
||||
this.logger = logger
|
||||
config = new ConfigFile(new File(cfgdir, "multipart.cfg"))
|
||||
.setComment("Multipart API config file")
|
||||
|
||||
GameRegistry.registerBlock(new BlockMultipart().setBlockName("multipart"), null, "block")
|
||||
block = Block.blockRegistry.getObject("ForgeMultipart:block").asInstanceOf[BlockMultipart]
|
||||
GameRegistry.registerBlock(
|
||||
new BlockMultipart().setBlockName("multipart"),
|
||||
null,
|
||||
"block"
|
||||
)
|
||||
block = Block.blockRegistry
|
||||
.getObject("ForgeMultipart:block")
|
||||
.asInstanceOf[BlockMultipart]
|
||||
|
||||
MultipartGenerator.registerTrait("codechicken.multipart.TSlottedPart", "codechicken.multipart.scalatraits.TSlottedTile")
|
||||
MultipartGenerator.registerTrait("net.minecraftforge.fluids.IFluidHandler", "codechicken.multipart.scalatraits.TFluidHandlerTile")
|
||||
MultipartGenerator.registerTrait("net.minecraft.inventory.IInventory", "codechicken.multipart.scalatraits.JInventoryTile")
|
||||
MultipartGenerator.registerTrait("net.minecraft.inventory.ISidedInventory", "codechicken.multipart.scalatraits.JInventoryTile")
|
||||
MultipartGenerator.registerTrait("codechicken.multipart.JPartialOcclusion", "codechicken.multipart.scalatraits.TPartialOcclusionTile")
|
||||
MultipartGenerator.registerTrait("codechicken.multipart.IRedstonePart", "codechicken.multipart.scalatraits.TRedstoneTile")
|
||||
MultipartGenerator.registerTrait("codechicken.multipart.IRandomDisplayTick", "codechicken.multipart.scalatraits.TRandomDisplayTickTile", null)
|
||||
MultipartGenerator.registerTrait("codechicken.multipart.INeighborTileChange", null, "codechicken.multipart.scalatraits.TTileChangeTile")
|
||||
MultipartGenerator.registerTrait(
|
||||
"codechicken.multipart.TSlottedPart",
|
||||
"codechicken.multipart.scalatraits.TSlottedTile"
|
||||
)
|
||||
MultipartGenerator.registerTrait(
|
||||
"net.minecraftforge.fluids.IFluidHandler",
|
||||
"codechicken.multipart.scalatraits.TFluidHandlerTile"
|
||||
)
|
||||
MultipartGenerator.registerTrait(
|
||||
"net.minecraft.inventory.IInventory",
|
||||
"codechicken.multipart.scalatraits.JInventoryTile"
|
||||
)
|
||||
MultipartGenerator.registerTrait(
|
||||
"net.minecraft.inventory.ISidedInventory",
|
||||
"codechicken.multipart.scalatraits.JInventoryTile"
|
||||
)
|
||||
MultipartGenerator.registerTrait(
|
||||
"codechicken.multipart.JPartialOcclusion",
|
||||
"codechicken.multipart.scalatraits.TPartialOcclusionTile"
|
||||
)
|
||||
MultipartGenerator.registerTrait(
|
||||
"codechicken.multipart.IRedstonePart",
|
||||
"codechicken.multipart.scalatraits.TRedstoneTile"
|
||||
)
|
||||
MultipartGenerator.registerTrait(
|
||||
"codechicken.multipart.IRandomDisplayTick",
|
||||
"codechicken.multipart.scalatraits.TRandomDisplayTickTile",
|
||||
null
|
||||
)
|
||||
MultipartGenerator.registerTrait(
|
||||
"codechicken.multipart.INeighborTileChange",
|
||||
null,
|
||||
"codechicken.multipart.scalatraits.TTileChangeTile"
|
||||
)
|
||||
|
||||
MultipartSaveLoad.hookLoader()
|
||||
}
|
||||
MultipartSaveLoad.hookLoader()
|
||||
}
|
||||
|
||||
def init() {}
|
||||
def init() {}
|
||||
|
||||
def postInit() {
|
||||
FMLCommonHandler.instance().bus().register(MultipartEventHandler)
|
||||
MinecraftForge.EVENT_BUS.register(MultipartEventHandler)
|
||||
PacketCustom.assignHandler(MultipartSPH.channel, MultipartSPH)
|
||||
PacketCustom.assignHandshakeHandler(MultipartSPH.registryChannel, MultipartSPH)
|
||||
def postInit() {
|
||||
FMLCommonHandler.instance().bus().register(MultipartEventHandler)
|
||||
MinecraftForge.EVENT_BUS.register(MultipartEventHandler)
|
||||
PacketCustom.assignHandler(MultipartSPH.channel, MultipartSPH)
|
||||
PacketCustom.assignHandshakeHandler(
|
||||
MultipartSPH.registryChannel,
|
||||
MultipartSPH
|
||||
)
|
||||
|
||||
WorldExtensionManager.registerWorldExtension(TickScheduler)
|
||||
TileChunkLoadHook.init()
|
||||
WorldExtensionManager.registerWorldExtension(TickScheduler)
|
||||
TileChunkLoadHook.init()
|
||||
|
||||
MultipartCompatiblity.load()
|
||||
}
|
||||
MultipartCompatiblity.load()
|
||||
}
|
||||
|
||||
def onTileClassBuilt(t: Class[_ <: TileEntity]) {
|
||||
MultipartSaveLoad.registerTileClass(t)
|
||||
}
|
||||
def onTileClassBuilt(t: Class[_ <: TileEntity]) {
|
||||
MultipartSaveLoad.registerTileClass(t)
|
||||
}
|
||||
}
|
||||
|
||||
class MultipartProxy_clientImpl extends MultipartProxy_serverImpl
|
||||
{
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def postInit() {
|
||||
super.postInit()
|
||||
RenderingRegistry.registerBlockHandler(MultipartRenderer)
|
||||
PacketCustom.assignHandler(MultipartCPH.channel, MultipartCPH)
|
||||
PacketCustom.assignHandler(MultipartCPH.registryChannel, MultipartCPH)
|
||||
class MultipartProxy_clientImpl extends MultipartProxy_serverImpl {
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def postInit() {
|
||||
super.postInit()
|
||||
RenderingRegistry.registerBlockHandler(MultipartRenderer)
|
||||
PacketCustom.assignHandler(MultipartCPH.channel, MultipartCPH)
|
||||
PacketCustom.assignHandler(MultipartCPH.registryChannel, MultipartCPH)
|
||||
|
||||
FMLCommonHandler.instance().bus().register(ControlKeyHandler)
|
||||
ClientRegistry.registerKeyBinding(ControlKeyHandler)
|
||||
}
|
||||
FMLCommonHandler.instance().bus().register(ControlKeyHandler)
|
||||
ClientRegistry.registerKeyBinding(ControlKeyHandler)
|
||||
}
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def onTileClassBuilt(t: Class[_ <: TileEntity]) {
|
||||
super.onTileClassBuilt(t)
|
||||
ClientRegistry.bindTileEntitySpecialRenderer(t, MultipartRenderer)
|
||||
}
|
||||
@SideOnly(Side.CLIENT)
|
||||
override def onTileClassBuilt(t: Class[_ <: TileEntity]) {
|
||||
super.onTileClassBuilt(t)
|
||||
ClientRegistry.bindTileEntitySpecialRenderer(t, MultipartRenderer)
|
||||
}
|
||||
}
|
||||
|
||||
object MultipartProxy extends MultipartProxy_clientImpl
|
||||
{
|
||||
def indexInChunk(cc: ChunkCoordIntPair, i: Int) = new BlockCoord(cc.chunkXPos << 4 | i & 0xF, (i >> 8) & 0xFF, cc.chunkZPos << 4 | (i & 0xF0) >> 4)
|
||||
def indexInChunk(pos: BlockCoord) = pos.x & 0xF | pos.y << 8 | (pos.z & 0xF) << 4
|
||||
object MultipartProxy extends MultipartProxy_clientImpl {
|
||||
def indexInChunk(cc: ChunkCoordIntPair, i: Int) = new BlockCoord(
|
||||
cc.chunkXPos << 4 | i & 0xf,
|
||||
(i >> 8) & 0xff,
|
||||
cc.chunkZPos << 4 | (i & 0xf0) >> 4
|
||||
)
|
||||
def indexInChunk(pos: BlockCoord) =
|
||||
pos.x & 0xf | pos.y << 8 | (pos.z & 0xf) << 4
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
package codechicken.multipart.minecraft;
|
||||
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import codechicken.lib.vec.Cuboid6;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
import codechicken.multipart.IFaceRedstonePart;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockButton;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -15,11 +11,17 @@ import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import codechicken.lib.vec.Cuboid6;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
import codechicken.multipart.IFaceRedstonePart;
|
||||
|
||||
public class ButtonPart extends McSidedMetaPart implements IFaceRedstonePart {
|
||||
|
||||
public static BlockButton stoneButton = (BlockButton) Blocks.stone_button;
|
||||
public static BlockButton woodenButton = (BlockButton) Blocks.wooden_button;
|
||||
public static int[] metaSideMap = new int[] {-1, 4, 5, 2, 3};
|
||||
public static int[] sideMetaMap = new int[] {-1, -1, 3, 4, 1, 2};
|
||||
public static int[] metaSideMap = new int[] { -1, 4, 5, 2, 3 };
|
||||
public static int[] sideMetaMap = new int[] { -1, -1, 3, 4, 1, 2 };
|
||||
|
||||
public static BlockButton getButton(int meta) {
|
||||
return (meta & 0x10) > 0 ? woodenButton : stoneButton;
|
||||
@@ -112,11 +114,9 @@ public class ButtonPart extends McSidedMetaPart implements IFaceRedstonePart {
|
||||
}
|
||||
|
||||
private void updateState() {
|
||||
boolean arrows = sensitive()
|
||||
&& !world().getEntitiesWithinAABB(
|
||||
EntityArrow.class,
|
||||
getBounds().add(Vector3.fromTileEntity(tile())).toAABB())
|
||||
.isEmpty();
|
||||
boolean arrows = sensitive() && !world()
|
||||
.getEntitiesWithinAABB(EntityArrow.class, getBounds().add(Vector3.fromTileEntity(tile())).toAABB())
|
||||
.isEmpty();
|
||||
boolean pressed = pressed();
|
||||
|
||||
if (arrows != pressed) toggle();
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
package codechicken.multipart.minecraft;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import codechicken.multipart.MultiPartRegistry;
|
||||
import codechicken.multipart.MultiPartRegistry.IPartConverter;
|
||||
import codechicken.multipart.MultiPartRegistry.IPartFactory;
|
||||
import codechicken.multipart.TMultiPart;
|
||||
import java.util.Arrays;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Content implements IPartFactory, IPartConverter {
|
||||
|
||||
@Override
|
||||
public TMultiPart createPart(String name, boolean client) {
|
||||
if (name.equals("mc_torch")) return new TorchPart();
|
||||
@@ -23,7 +26,7 @@ public class Content implements IPartFactory, IPartConverter {
|
||||
|
||||
public void init() {
|
||||
MultiPartRegistry.registerConverter(this);
|
||||
MultiPartRegistry.registerParts(this, new String[] {"mc_torch", "mc_lever", "mc_button", "mc_redtorch"});
|
||||
MultiPartRegistry.registerParts(this, new String[] { "mc_torch", "mc_lever", "mc_button", "mc_redtorch" });
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,12 +1,5 @@
|
||||
package codechicken.multipart.minecraft;
|
||||
|
||||
import codechicken.lib.packet.PacketCustom;
|
||||
import codechicken.lib.raytracer.RayTracer;
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
import codechicken.multipart.TileMultipart;
|
||||
import cpw.mods.fml.common.eventhandler.EventPriority;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockFence;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
@@ -20,7 +13,16 @@ import net.minecraftforge.event.entity.player.PlayerDestroyItemEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent.Action;
|
||||
|
||||
import codechicken.lib.packet.PacketCustom;
|
||||
import codechicken.lib.raytracer.RayTracer;
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
import codechicken.multipart.TileMultipart;
|
||||
import cpw.mods.fml.common.eventhandler.EventPriority;
|
||||
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
public class EventHandler {
|
||||
|
||||
private ThreadLocal<Object> placing = new ThreadLocal<Object>();
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOW)
|
||||
@@ -53,33 +55,32 @@ public class EventHandler {
|
||||
|
||||
if (part == null) return false;
|
||||
|
||||
if (world.isRemote
|
||||
&& !player
|
||||
.isSneaking()) // attempt to use block activated like normal and tell the server the right stuff
|
||||
if (world.isRemote && !player.isSneaking()) // attempt to use block activated like normal and tell the server
|
||||
// the right stuff
|
||||
{
|
||||
Vector3 f = new Vector3(hit.hitVec).add(-hit.blockX, -hit.blockY, -hit.blockZ);
|
||||
Block block = world.getBlock(hit.blockX, hit.blockY, hit.blockZ);
|
||||
if (!ignoreActivate(block)
|
||||
&& block.onBlockActivated(
|
||||
world,
|
||||
hit.blockX,
|
||||
hit.blockY,
|
||||
hit.blockZ,
|
||||
player,
|
||||
hit.sideHit,
|
||||
(float) f.x,
|
||||
(float) f.y,
|
||||
(float) f.z)) {
|
||||
if (!ignoreActivate(block) && block.onBlockActivated(
|
||||
world,
|
||||
hit.blockX,
|
||||
hit.blockY,
|
||||
hit.blockZ,
|
||||
player,
|
||||
hit.sideHit,
|
||||
(float) f.x,
|
||||
(float) f.y,
|
||||
(float) f.z)) {
|
||||
player.swingItem();
|
||||
PacketCustom.sendToServer(new C08PacketPlayerBlockPlacement(
|
||||
hit.blockX,
|
||||
hit.blockY,
|
||||
hit.blockZ,
|
||||
hit.sideHit,
|
||||
player.inventory.getCurrentItem(),
|
||||
(float) f.x,
|
||||
(float) f.y,
|
||||
(float) f.z));
|
||||
PacketCustom.sendToServer(
|
||||
new C08PacketPlayerBlockPlacement(
|
||||
hit.blockX,
|
||||
hit.blockY,
|
||||
hit.blockZ,
|
||||
hit.sideHit,
|
||||
player.inventory.getCurrentItem(),
|
||||
(float) f.x,
|
||||
(float) f.y,
|
||||
(float) f.z));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
package codechicken.multipart.minecraft;
|
||||
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
|
||||
public interface IPartMeta {
|
||||
|
||||
public int getMetadata();
|
||||
|
||||
public World getWorld();
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
package codechicken.multipart.minecraft;
|
||||
|
||||
import codechicken.lib.math.MathHelper;
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import codechicken.lib.vec.Cuboid6;
|
||||
import codechicken.multipart.IFaceRedstonePart;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockLever;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
@@ -15,11 +11,17 @@ import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import codechicken.lib.math.MathHelper;
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import codechicken.lib.vec.Cuboid6;
|
||||
import codechicken.multipart.IFaceRedstonePart;
|
||||
|
||||
public class LeverPart extends McSidedMetaPart implements IFaceRedstonePart {
|
||||
|
||||
public static BlockLever lever = (BlockLever) Blocks.lever;
|
||||
public static int[] metaSideMap = new int[] {1, 4, 5, 2, 3, 0, 0, 1};
|
||||
public static int[] sideMetaMap = new int[] {6, 0, 3, 4, 1, 2};
|
||||
public static int[] metaSwapMap = new int[] {5, 7};
|
||||
public static int[] metaSideMap = new int[] { 1, 4, 5, 2, 3, 0, 0, 1 };
|
||||
public static int[] sideMetaMap = new int[] { 6, 0, 3, 4, 1, 2 };
|
||||
public static int[] metaSwapMap = new int[] { 5, 7 };
|
||||
|
||||
public LeverPart() {}
|
||||
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
package codechicken.multipart.minecraft;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.particle.EffectRenderer;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
|
||||
import codechicken.lib.vec.Cuboid6;
|
||||
import codechicken.multipart.IconHitEffects;
|
||||
import codechicken.multipart.JCuboidPart;
|
||||
@@ -9,16 +19,9 @@ import codechicken.multipart.NormalOcclusionTest;
|
||||
import codechicken.multipart.TMultiPart;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.client.particle.EffectRenderer;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
|
||||
public abstract class McBlockPart extends JCuboidPart implements JNormalOcclusion, JIconHitEffects {
|
||||
|
||||
public abstract Block getBlock();
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package codechicken.multipart.minecraft;
|
||||
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import codechicken.lib.data.MCDataInput;
|
||||
import codechicken.lib.data.MCDataOutput;
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
import net.minecraft.client.renderer.RenderBlocks;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public abstract class McMetaPart extends McBlockPart implements IPartMeta {
|
||||
|
||||
public byte meta;
|
||||
|
||||
public McMetaPart() {}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
package codechicken.multipart.minecraft;
|
||||
|
||||
import codechicken.lib.packet.PacketCustom;
|
||||
import codechicken.lib.packet.PacketCustom.IClientPacketHandler;
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.WorldClient;
|
||||
import net.minecraft.network.play.INetHandlerPlayClient;
|
||||
|
||||
import codechicken.lib.packet.PacketCustom;
|
||||
import codechicken.lib.packet.PacketCustom.IClientPacketHandler;
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
|
||||
public class McMultipartCPH implements IClientPacketHandler {
|
||||
|
||||
public static Object channel = MinecraftMultipartMod.instance;
|
||||
|
||||
@Override
|
||||
@@ -20,14 +22,13 @@ public class McMultipartCPH implements IClientPacketHandler {
|
||||
}
|
||||
|
||||
private void spawnBurnoutSmoke(WorldClient world, BlockCoord pos) {
|
||||
for (int l = 0; l < 5; l++)
|
||||
world.spawnParticle(
|
||||
"smoke",
|
||||
pos.x + world.rand.nextDouble() * 0.6 + 0.2,
|
||||
pos.y + world.rand.nextDouble() * 0.6 + 0.2,
|
||||
pos.z + world.rand.nextDouble() * 0.6 + 0.2,
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
for (int l = 0; l < 5; l++) world.spawnParticle(
|
||||
"smoke",
|
||||
pos.x + world.rand.nextDouble() * 0.6 + 0.2,
|
||||
pos.y + world.rand.nextDouble() * 0.6 + 0.2,
|
||||
pos.z + world.rand.nextDouble() * 0.6 + 0.2,
|
||||
0,
|
||||
0,
|
||||
0);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,14 @@
|
||||
package codechicken.multipart.minecraft;
|
||||
|
||||
import codechicken.lib.packet.PacketCustom;
|
||||
import codechicken.lib.packet.PacketCustom.IServerPacketHandler;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.network.play.INetHandlerPlayServer;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import codechicken.lib.packet.PacketCustom;
|
||||
import codechicken.lib.packet.PacketCustom.IServerPacketHandler;
|
||||
|
||||
public class McMultipartSPH implements IServerPacketHandler {
|
||||
|
||||
public static Object channel = MinecraftMultipartMod.instance;
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
package codechicken.multipart.minecraft;
|
||||
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
import codechicken.multipart.TFacePart;
|
||||
import codechicken.multipart.TileMultipart;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
public abstract class McSidedMetaPart extends McMetaPart implements TFacePart {
|
||||
|
||||
public McSidedMetaPart() {}
|
||||
|
||||
public McSidedMetaPart(int meta) {
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package codechicken.multipart.minecraft;
|
||||
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
import codechicken.lib.packet.PacketCustom;
|
||||
import cpw.mods.fml.common.FMLCommonHandler;
|
||||
import cpw.mods.fml.common.Mod;
|
||||
import cpw.mods.fml.common.Mod.Instance;
|
||||
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
||||
@Mod(modid = "McMultipart", acceptedMinecraftVersions = "[1.7.10]")
|
||||
public class MinecraftMultipartMod {
|
||||
|
||||
@Instance("McMultipart")
|
||||
public static MinecraftMultipartMod instance;
|
||||
|
||||
|
||||
@@ -1,15 +1,17 @@
|
||||
package codechicken.multipart.minecraft;
|
||||
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.world.IBlockAccess;
|
||||
import net.minecraft.world.biome.BiomeGenBase;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import cpw.mods.fml.relauncher.Side;
|
||||
import cpw.mods.fml.relauncher.SideOnly;
|
||||
|
||||
public class PartMetaAccess implements IBlockAccess {
|
||||
|
||||
public IPartMeta part;
|
||||
private BlockCoord pos;
|
||||
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
package codechicken.multipart.minecraft;
|
||||
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import codechicken.lib.vec.Cuboid6;
|
||||
import codechicken.multipart.IFaceRedstonePart;
|
||||
import codechicken.multipart.RedstoneInteractions;
|
||||
import codechicken.multipart.TRandomUpdateTick;
|
||||
import codechicken.multipart.TickScheduler;
|
||||
import java.util.Random;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockRedstoneTorch;
|
||||
import net.minecraft.init.Blocks;
|
||||
@@ -15,11 +10,20 @@ import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import codechicken.lib.vec.Cuboid6;
|
||||
import codechicken.multipart.IFaceRedstonePart;
|
||||
import codechicken.multipart.RedstoneInteractions;
|
||||
import codechicken.multipart.TRandomUpdateTick;
|
||||
import codechicken.multipart.TickScheduler;
|
||||
|
||||
public class RedstoneTorchPart extends TorchPart implements IFaceRedstonePart, TRandomUpdateTick {
|
||||
|
||||
public static BlockRedstoneTorch torchActive = (BlockRedstoneTorch) Blocks.redstone_torch;
|
||||
public static BlockRedstoneTorch torchIdle = (BlockRedstoneTorch) Blocks.unlit_redstone_torch;
|
||||
|
||||
public class BurnoutEntry {
|
||||
|
||||
public BurnoutEntry(long l) {
|
||||
timeout = l;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
package codechicken.multipart.minecraft;
|
||||
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import codechicken.lib.vec.Cuboid6;
|
||||
import codechicken.multipart.IRandomDisplayTick;
|
||||
import java.util.Random;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockTorch;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.ForgeDirection;
|
||||
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import codechicken.lib.vec.Cuboid6;
|
||||
import codechicken.multipart.IRandomDisplayTick;
|
||||
|
||||
public class TorchPart extends McSidedMetaPart implements IRandomDisplayTick {
|
||||
|
||||
public static BlockTorch torch = (BlockTorch) Blocks.torch;
|
||||
public static int[] metaSideMap = new int[] {-1, 4, 5, 2, 3, 0};
|
||||
public static int[] sideMetaMap = new int[] {5, 0, 3, 4, 1, 2};
|
||||
public static int[] metaSideMap = new int[] { -1, 4, 5, 2, 3, 0 };
|
||||
public static int[] sideMetaMap = new int[] { 5, 0, 3, 4, 1, 2 };
|
||||
|
||||
public TorchPart() {}
|
||||
|
||||
@@ -55,7 +58,7 @@ public class TorchPart extends McSidedMetaPart implements IRandomDisplayTick {
|
||||
@Override
|
||||
public boolean canStay() {
|
||||
return sideForMeta(meta) == 0
|
||||
&& world().getBlock(x(), y() - 1, z()).canPlaceTorchOnTop(world(), x(), y() - 1, z())
|
||||
&& world().getBlock(x(), y() - 1, z()).canPlaceTorchOnTop(world(), x(), y() - 1, z())
|
||||
|| super.canStay();
|
||||
}
|
||||
|
||||
@@ -64,7 +67,8 @@ public class TorchPart extends McSidedMetaPart implements IRandomDisplayTick {
|
||||
pos = pos.copy().offset(side ^ 1);
|
||||
Block block = world.getBlock(pos.x, pos.y, pos.z);
|
||||
if (!block.isSideSolid(world, pos.x, pos.y, pos.z, ForgeDirection.getOrientation(side))
|
||||
&& (side != 1 || block.canPlaceTorchOnTop(world, pos.x, pos.y, pos.z))) return null;
|
||||
&& (side != 1 || block.canPlaceTorchOnTop(world, pos.x, pos.y, pos.z)))
|
||||
return null;
|
||||
|
||||
return new TorchPart(sideMetaMap[side ^ 1]);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package codechicken.multipart.nei;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import net.minecraft.util.StatCollector;
|
||||
|
||||
import codechicken.lib.inventory.InventoryUtils;
|
||||
import codechicken.microblock.CommonMicroClass;
|
||||
import codechicken.microblock.MicroblockClass;
|
||||
@@ -7,10 +11,9 @@ import codechicken.microblock.handler.MicroblockProxy;
|
||||
import codechicken.nei.ItemStackMap;
|
||||
import codechicken.nei.api.API;
|
||||
import codechicken.nei.api.IConfigureNEI;
|
||||
import java.util.Arrays;
|
||||
import net.minecraft.util.StatCollector;
|
||||
|
||||
public class NEI_MicroblockConfig implements IConfigureNEI {
|
||||
|
||||
@Override
|
||||
public void loadConfig() {
|
||||
MicroblockClass[] microClasses = CommonMicroClass.classes();
|
||||
|
||||
@@ -2,7 +2,6 @@ package codechicken
|
||||
|
||||
import codechicken.multipart.handler.MultipartProxy
|
||||
|
||||
package object multipart
|
||||
{
|
||||
def logger = MultipartProxy.logger
|
||||
package object multipart {
|
||||
def logger = MultipartProxy.logger
|
||||
}
|
||||
@@ -10,121 +10,130 @@ import net.minecraftforge.fluids.Fluid
|
||||
import java.util.LinkedList
|
||||
import scala.collection.JavaConversions._
|
||||
|
||||
/**
|
||||
* Mixin trait implementation for parts implementing IFluidHandler.
|
||||
* Distributes fluid manipulation among fluid handling parts.
|
||||
*/
|
||||
trait TFluidHandlerTile extends TileMultipart with IFluidHandler
|
||||
{
|
||||
var tankList = new LinkedList[IFluidHandler]()
|
||||
|
||||
override def copyFrom(that:TileMultipart)
|
||||
{
|
||||
super.copyFrom(that)
|
||||
if(that.isInstanceOf[TFluidHandlerTile])
|
||||
tankList = that.asInstanceOf[TFluidHandlerTile].tankList
|
||||
}
|
||||
|
||||
override def bindPart(part:TMultiPart)
|
||||
{
|
||||
super.bindPart(part)
|
||||
if(part.isInstanceOf[IFluidHandler])
|
||||
tankList+=part.asInstanceOf[IFluidHandler]
|
||||
}
|
||||
|
||||
override def partRemoved(part:TMultiPart, p:Int)
|
||||
{
|
||||
super.partRemoved(part, p)
|
||||
if(part.isInstanceOf[IFluidHandler])
|
||||
tankList-=part.asInstanceOf[IFluidHandler]
|
||||
}
|
||||
|
||||
override def clearParts()
|
||||
{
|
||||
super.clearParts()
|
||||
tankList.clear()
|
||||
}
|
||||
|
||||
override def getTankInfo(dir:ForgeDirection):Array[FluidTankInfo] =
|
||||
{
|
||||
var tankCount:Int = 0
|
||||
tankList.foreach(t => tankCount += t.getTankInfo(dir).length)
|
||||
val tanks = new Array[FluidTankInfo](tankCount)
|
||||
var i = 0
|
||||
tankList.foreach(p => p.getTankInfo(dir).foreach{t =>
|
||||
tanks(i) = t
|
||||
i+=1
|
||||
})
|
||||
return tanks
|
||||
}
|
||||
|
||||
override def fill(dir:ForgeDirection, liquid:FluidStack, doFill:Boolean):Int =
|
||||
{
|
||||
var filled = 0
|
||||
val initial = liquid.amount
|
||||
tankList.foreach(p =>
|
||||
filled+=p.fill(dir, copy(liquid, initial-filled), doFill)
|
||||
)
|
||||
return filled
|
||||
}
|
||||
|
||||
override def canFill(dir:ForgeDirection, liquid:Fluid) = tankList.find(_.canFill(dir, liquid)).isDefined
|
||||
|
||||
override def canDrain(dir:ForgeDirection, liquid:Fluid) = tankList.find(_.canDrain(dir, liquid)).isDefined
|
||||
|
||||
private def copy(liquid:FluidStack, quantity:Int):FluidStack =
|
||||
{
|
||||
val copy = liquid.copy
|
||||
copy.amount = quantity
|
||||
return copy
|
||||
}
|
||||
/** Mixin trait implementation for parts implementing IFluidHandler. Distributes
|
||||
* fluid manipulation among fluid handling parts.
|
||||
*/
|
||||
trait TFluidHandlerTile extends TileMultipart with IFluidHandler {
|
||||
var tankList = new LinkedList[IFluidHandler]()
|
||||
|
||||
override def drain(dir:ForgeDirection, amount:Int, doDrain:Boolean):FluidStack =
|
||||
{
|
||||
var drained:FluidStack = null
|
||||
var d_amount = 0
|
||||
tankList.foreach{p =>
|
||||
val drain = amount-d_amount
|
||||
val ret = p.drain(dir, drain, false)
|
||||
if(ret != null && ret.amount > 0 && (drained == null || drained.isFluidEqual(ret)))
|
||||
{
|
||||
if(doDrain)
|
||||
p.drain(dir, drain, true)
|
||||
override def copyFrom(that: TileMultipart) {
|
||||
super.copyFrom(that)
|
||||
if (that.isInstanceOf[TFluidHandlerTile])
|
||||
tankList = that.asInstanceOf[TFluidHandlerTile].tankList
|
||||
}
|
||||
|
||||
if(drained == null)
|
||||
drained = ret
|
||||
override def bindPart(part: TMultiPart) {
|
||||
super.bindPart(part)
|
||||
if (part.isInstanceOf[IFluidHandler])
|
||||
tankList += part.asInstanceOf[IFluidHandler]
|
||||
}
|
||||
|
||||
d_amount+=ret.amount
|
||||
}
|
||||
}
|
||||
if(drained != null)
|
||||
drained.amount = d_amount
|
||||
override def partRemoved(part: TMultiPart, p: Int) {
|
||||
super.partRemoved(part, p)
|
||||
if (part.isInstanceOf[IFluidHandler])
|
||||
tankList -= part.asInstanceOf[IFluidHandler]
|
||||
}
|
||||
|
||||
return drained
|
||||
override def clearParts() {
|
||||
super.clearParts()
|
||||
tankList.clear()
|
||||
}
|
||||
|
||||
override def getTankInfo(dir: ForgeDirection): Array[FluidTankInfo] = {
|
||||
var tankCount: Int = 0
|
||||
tankList.foreach(t => tankCount += t.getTankInfo(dir).length)
|
||||
val tanks = new Array[FluidTankInfo](tankCount)
|
||||
var i = 0
|
||||
tankList.foreach(p =>
|
||||
p.getTankInfo(dir).foreach { t =>
|
||||
tanks(i) = t
|
||||
i += 1
|
||||
}
|
||||
)
|
||||
return tanks
|
||||
}
|
||||
|
||||
override def fill(
|
||||
dir: ForgeDirection,
|
||||
liquid: FluidStack,
|
||||
doFill: Boolean
|
||||
): Int = {
|
||||
var filled = 0
|
||||
val initial = liquid.amount
|
||||
tankList.foreach(p =>
|
||||
filled += p.fill(dir, copy(liquid, initial - filled), doFill)
|
||||
)
|
||||
return filled
|
||||
}
|
||||
|
||||
override def canFill(dir: ForgeDirection, liquid: Fluid) =
|
||||
tankList.find(_.canFill(dir, liquid)).isDefined
|
||||
|
||||
override def canDrain(dir: ForgeDirection, liquid: Fluid) =
|
||||
tankList.find(_.canDrain(dir, liquid)).isDefined
|
||||
|
||||
private def copy(liquid: FluidStack, quantity: Int): FluidStack = {
|
||||
val copy = liquid.copy
|
||||
copy.amount = quantity
|
||||
return copy
|
||||
}
|
||||
|
||||
override def drain(
|
||||
dir: ForgeDirection,
|
||||
amount: Int,
|
||||
doDrain: Boolean
|
||||
): FluidStack = {
|
||||
var drained: FluidStack = null
|
||||
var d_amount = 0
|
||||
tankList.foreach { p =>
|
||||
val drain = amount - d_amount
|
||||
val ret = p.drain(dir, drain, false)
|
||||
if (
|
||||
ret != null && ret.amount > 0 && (drained == null || drained
|
||||
.isFluidEqual(ret))
|
||||
) {
|
||||
if (doDrain)
|
||||
p.drain(dir, drain, true)
|
||||
|
||||
if (drained == null)
|
||||
drained = ret
|
||||
|
||||
d_amount += ret.amount
|
||||
}
|
||||
}
|
||||
if (drained != null)
|
||||
drained.amount = d_amount
|
||||
|
||||
override def drain(dir:ForgeDirection, fluid:FluidStack, doDrain:Boolean):FluidStack =
|
||||
{
|
||||
val amount = fluid.amount
|
||||
var drained:FluidStack = null
|
||||
var d_amount = 0
|
||||
tankList.foreach{p =>
|
||||
val drain = copy(fluid, amount-d_amount)
|
||||
val ret = p.drain(dir, drain, false)
|
||||
if(ret != null && ret.amount > 0 && (drained == null || drained.isFluidEqual(ret)))
|
||||
{
|
||||
if(doDrain)
|
||||
p.drain(dir, drain, true)
|
||||
return drained
|
||||
}
|
||||
|
||||
if(drained == null)
|
||||
drained = ret
|
||||
override def drain(
|
||||
dir: ForgeDirection,
|
||||
fluid: FluidStack,
|
||||
doDrain: Boolean
|
||||
): FluidStack = {
|
||||
val amount = fluid.amount
|
||||
var drained: FluidStack = null
|
||||
var d_amount = 0
|
||||
tankList.foreach { p =>
|
||||
val drain = copy(fluid, amount - d_amount)
|
||||
val ret = p.drain(dir, drain, false)
|
||||
if (
|
||||
ret != null && ret.amount > 0 && (drained == null || drained
|
||||
.isFluidEqual(ret))
|
||||
) {
|
||||
if (doDrain)
|
||||
p.drain(dir, drain, true)
|
||||
|
||||
d_amount+=ret.amount
|
||||
}
|
||||
}
|
||||
if(drained != null)
|
||||
drained.amount = d_amount
|
||||
if (drained == null)
|
||||
drained = ret
|
||||
|
||||
return drained
|
||||
d_amount += ret.amount
|
||||
}
|
||||
}
|
||||
}
|
||||
if (drained != null)
|
||||
drained.amount = d_amount
|
||||
|
||||
return drained
|
||||
}
|
||||
}
|
||||
@@ -8,126 +8,119 @@ import net.minecraft.entity.player.EntityPlayer
|
||||
import scala.collection.JavaConversions._
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
trait TIInventoryTile extends TileMultipart with ISidedInventory
|
||||
{
|
||||
var invList = new LinkedList[IInventory]()
|
||||
var slotMap = Array[(IInventory, Int)]()
|
||||
trait TIInventoryTile extends TileMultipart with ISidedInventory {
|
||||
var invList = new LinkedList[IInventory]()
|
||||
var slotMap = Array[(IInventory, Int)]()
|
||||
|
||||
override def copyFrom(that:TileMultipart)
|
||||
{
|
||||
super.copyFrom(that)
|
||||
if(that.isInstanceOf[TIInventoryTile]) {
|
||||
invList = that.asInstanceOf[TIInventoryTile].invList
|
||||
rebuildSlotMap()
|
||||
}
|
||||
override def copyFrom(that: TileMultipart) {
|
||||
super.copyFrom(that)
|
||||
if (that.isInstanceOf[TIInventoryTile]) {
|
||||
invList = that.asInstanceOf[TIInventoryTile].invList
|
||||
rebuildSlotMap()
|
||||
}
|
||||
}
|
||||
|
||||
override def bindPart(part:TMultiPart)
|
||||
{
|
||||
super.bindPart(part)
|
||||
if(part.isInstanceOf[IInventory]) {
|
||||
invList+=part.asInstanceOf[IInventory]
|
||||
rebuildSlotMap()
|
||||
}
|
||||
override def bindPart(part: TMultiPart) {
|
||||
super.bindPart(part)
|
||||
if (part.isInstanceOf[IInventory]) {
|
||||
invList += part.asInstanceOf[IInventory]
|
||||
rebuildSlotMap()
|
||||
}
|
||||
}
|
||||
|
||||
override def partRemoved(part:TMultiPart, p:Int)
|
||||
{
|
||||
super.partRemoved(part, p)
|
||||
if(part.isInstanceOf[IInventory]) {
|
||||
invList-=part.asInstanceOf[IInventory]
|
||||
rebuildSlotMap()
|
||||
}
|
||||
override def partRemoved(part: TMultiPart, p: Int) {
|
||||
super.partRemoved(part, p)
|
||||
if (part.isInstanceOf[IInventory]) {
|
||||
invList -= part.asInstanceOf[IInventory]
|
||||
rebuildSlotMap()
|
||||
}
|
||||
}
|
||||
|
||||
override def clearParts()
|
||||
{
|
||||
super.clearParts()
|
||||
invList.clear()
|
||||
slotMap = Array()
|
||||
override def clearParts() {
|
||||
super.clearParts()
|
||||
invList.clear()
|
||||
slotMap = Array()
|
||||
}
|
||||
|
||||
def rebuildSlotMap() {
|
||||
slotMap = Array.ofDim(invList.map(_.getSizeInventory).sum)
|
||||
var i = 0
|
||||
for (inv <- invList; s <- 0 until inv.getSizeInventory) {
|
||||
slotMap(i) = (inv, s)
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
|
||||
def rebuildSlotMap() {
|
||||
slotMap = Array.ofDim(invList.map(_.getSizeInventory).sum)
|
||||
var i = 0
|
||||
for(inv <- invList; s <- 0 until inv.getSizeInventory) {
|
||||
slotMap(i) = (inv, s)
|
||||
i+=1
|
||||
}
|
||||
def getSizeInventory: Int = slotMap.length
|
||||
|
||||
def getStackInSlot(i: Int) = {
|
||||
val (inv, slot) = slotMap(i)
|
||||
inv.getStackInSlot(slot)
|
||||
}
|
||||
|
||||
def decrStackSize(i: Int, j: Int) = {
|
||||
val (inv, slot) = slotMap(i)
|
||||
inv.decrStackSize(slot, j)
|
||||
}
|
||||
|
||||
def getStackInSlotOnClosing(i: Int) = {
|
||||
val (inv, slot) = slotMap(i)
|
||||
inv.getStackInSlotOnClosing(slot)
|
||||
}
|
||||
|
||||
def setInventorySlotContents(i: Int, itemstack: ItemStack) = {
|
||||
val (inv, slot) = slotMap(i)
|
||||
inv.setInventorySlotContents(slot, itemstack)
|
||||
}
|
||||
|
||||
def getInventoryName = "Multipart Inventory"
|
||||
|
||||
def hasCustomInventoryName = false
|
||||
|
||||
def getInventoryStackLimit = 64
|
||||
|
||||
def isUseableByPlayer(entityplayer: EntityPlayer) = true
|
||||
|
||||
def openInventory() {}
|
||||
|
||||
def closeInventory() {}
|
||||
|
||||
def isItemValidForSlot(i: Int, itemstack: ItemStack) = {
|
||||
val (inv, slot) = slotMap(i)
|
||||
inv.isItemValidForSlot(slot, itemstack)
|
||||
}
|
||||
|
||||
def getAccessibleSlotsFromSide(side: Int) = {
|
||||
val buf = new ArrayBuffer[Int]()
|
||||
var base = 0
|
||||
for (inv <- invList) {
|
||||
inv match {
|
||||
case isided: ISidedInventory =>
|
||||
buf ++= isided.getAccessibleSlotsFromSide(side).map(_ + base)
|
||||
case _ =>
|
||||
}
|
||||
base += inv.getSizeInventory
|
||||
}
|
||||
buf.toArray
|
||||
}
|
||||
|
||||
def getSizeInventory:Int = slotMap.length
|
||||
|
||||
def getStackInSlot(i:Int) = {
|
||||
val (inv, slot) = slotMap(i)
|
||||
inv.getStackInSlot(slot)
|
||||
def canInsertItem(i: Int, itemstack: ItemStack, j: Int) = {
|
||||
val (inv, slot) = slotMap(i)
|
||||
inv match {
|
||||
case isided: ISidedInventory => isided.canInsertItem(slot, itemstack, j)
|
||||
case _ => true
|
||||
}
|
||||
}
|
||||
|
||||
def decrStackSize(i:Int, j:Int) = {
|
||||
val (inv, slot) = slotMap(i)
|
||||
inv.decrStackSize(slot, j)
|
||||
}
|
||||
|
||||
def getStackInSlotOnClosing(i:Int) = {
|
||||
val (inv, slot) = slotMap(i)
|
||||
inv.getStackInSlotOnClosing(slot)
|
||||
}
|
||||
|
||||
def setInventorySlotContents(i:Int, itemstack:ItemStack) = {
|
||||
val (inv, slot) = slotMap(i)
|
||||
inv.setInventorySlotContents(slot, itemstack)
|
||||
}
|
||||
|
||||
def getInventoryName = "Multipart Inventory"
|
||||
|
||||
def hasCustomInventoryName = false
|
||||
|
||||
def getInventoryStackLimit = 64
|
||||
|
||||
def isUseableByPlayer(entityplayer:EntityPlayer) = true
|
||||
|
||||
def openInventory(){}
|
||||
|
||||
def closeInventory(){}
|
||||
|
||||
def isItemValidForSlot(i:Int, itemstack:ItemStack) = {
|
||||
val (inv, slot) = slotMap(i)
|
||||
inv.isItemValidForSlot(slot, itemstack)
|
||||
}
|
||||
|
||||
def getAccessibleSlotsFromSide(side:Int) = {
|
||||
val buf = new ArrayBuffer[Int]()
|
||||
var base = 0
|
||||
for(inv <- invList) {
|
||||
inv match {
|
||||
case isided:ISidedInventory => buf++=isided.getAccessibleSlotsFromSide(side).map(_ + base)
|
||||
case _ =>
|
||||
}
|
||||
base+=inv.getSizeInventory
|
||||
}
|
||||
buf.toArray
|
||||
}
|
||||
|
||||
def canInsertItem(i:Int, itemstack:ItemStack, j:Int) = {
|
||||
val (inv, slot) = slotMap(i)
|
||||
inv match {
|
||||
case isided:ISidedInventory => isided.canInsertItem(slot, itemstack, j)
|
||||
case _ => true
|
||||
}
|
||||
}
|
||||
|
||||
def canExtractItem(i:Int, itemstack:ItemStack, j:Int) = {
|
||||
val (inv, slot) = slotMap(i)
|
||||
inv match {
|
||||
case isided:ISidedInventory => isided.canExtractItem(slot, itemstack, j)
|
||||
case _ => true
|
||||
}
|
||||
def canExtractItem(i: Int, itemstack: ItemStack, j: Int) = {
|
||||
val (inv, slot) = slotMap(i)
|
||||
inv match {
|
||||
case isided: ISidedInventory => isided.canExtractItem(slot, itemstack, j)
|
||||
case _ => true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To handle obfuscation issues, this is registered as a java trait.
|
||||
*/
|
||||
class JInventoryTile extends TileMultipart with TIInventoryTile
|
||||
{
|
||||
}
|
||||
/** To handle obfuscation issues, this is registered as a java trait.
|
||||
*/
|
||||
class JInventoryTile extends TileMultipart with TIInventoryTile {}
|
||||
@@ -5,30 +5,32 @@ import codechicken.multipart.TMultiPart
|
||||
import codechicken.multipart.JPartialOcclusion
|
||||
import codechicken.multipart.PartialOcclusionTest
|
||||
|
||||
/**
|
||||
* Implementation for the partial occlusion test.
|
||||
*/
|
||||
class TPartialOcclusionTile extends TileMultipart
|
||||
{
|
||||
override def occlusionTest(parts:Seq[TMultiPart], npart:TMultiPart):Boolean =
|
||||
{
|
||||
if(npart.isInstanceOf[JPartialOcclusion] && !partialOcclusionTest(parts:+npart))
|
||||
return false
|
||||
|
||||
return super.occlusionTest(parts, npart)
|
||||
/** Implementation for the partial occlusion test.
|
||||
*/
|
||||
class TPartialOcclusionTile extends TileMultipart {
|
||||
override def occlusionTest(
|
||||
parts: Seq[TMultiPart],
|
||||
npart: TMultiPart
|
||||
): Boolean = {
|
||||
if (
|
||||
npart.isInstanceOf[JPartialOcclusion] && !partialOcclusionTest(
|
||||
parts :+ npart
|
||||
)
|
||||
)
|
||||
return false
|
||||
|
||||
return super.occlusionTest(parts, npart)
|
||||
}
|
||||
|
||||
def partialOcclusionTest(parts: Seq[TMultiPart]): Boolean = {
|
||||
val test = new PartialOcclusionTest(parts.length)
|
||||
var i = 0
|
||||
while (i < parts.length) {
|
||||
val part = parts(i)
|
||||
if (part.isInstanceOf[JPartialOcclusion])
|
||||
test.fill(i, part.asInstanceOf[JPartialOcclusion])
|
||||
i += 1
|
||||
}
|
||||
|
||||
def partialOcclusionTest(parts:Seq[TMultiPart]):Boolean =
|
||||
{
|
||||
val test = new PartialOcclusionTest(parts.length)
|
||||
var i = 0
|
||||
while(i < parts.length)
|
||||
{
|
||||
val part = parts(i)
|
||||
if(part.isInstanceOf[JPartialOcclusion])
|
||||
test.fill(i, part.asInstanceOf[JPartialOcclusion])
|
||||
i+=1
|
||||
}
|
||||
return test()
|
||||
}
|
||||
}
|
||||
return test()
|
||||
}
|
||||
}
|
||||
@@ -4,14 +4,11 @@ import codechicken.multipart.TileMultipartClient
|
||||
import codechicken.multipart.IRandomDisplayTick
|
||||
import java.util.Random
|
||||
|
||||
/**
|
||||
* Saves processor time looping on tiles that don't need it
|
||||
*/
|
||||
trait TRandomDisplayTickTile extends TileMultipartClient
|
||||
{
|
||||
override def randomDisplayTick(random:Random)
|
||||
{
|
||||
for(p@(_p: IRandomDisplayTick) <- partList.iterator)
|
||||
p.randomDisplayTick(random)
|
||||
}
|
||||
}
|
||||
/** Saves processor time looping on tiles that don't need it
|
||||
*/
|
||||
trait TRandomDisplayTickTile extends TileMultipartClient {
|
||||
override def randomDisplayTick(random: Random) {
|
||||
for (p @ (_p: IRandomDisplayTick) <- partList.iterator)
|
||||
p.randomDisplayTick(random)
|
||||
}
|
||||
}
|
||||
@@ -9,79 +9,78 @@ import codechicken.multipart.IRedstoneTile
|
||||
import codechicken.multipart.TFacePart
|
||||
import codechicken.multipart.TEdgePart
|
||||
|
||||
/**
|
||||
* Mixin trait implementation for IRedstonePart
|
||||
* Provides and overrides various redstone functions.
|
||||
*/
|
||||
trait TRedstoneTile extends TileMultipart with IRedstoneTile
|
||||
{
|
||||
/**
|
||||
* Returns the strong (indirect) power being emitted fr
|
||||
*/
|
||||
override def strongPowerLevel(side:Int):Int =
|
||||
{
|
||||
var max = 0
|
||||
for(p@(_p: IRedstonePart) <- partList.iterator)
|
||||
{
|
||||
val l = p.strongPowerLevel(side)
|
||||
if(l > max) max = l
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
def openConnections(side:Int):Int =
|
||||
{
|
||||
var m = 0x10
|
||||
var i = 0
|
||||
while(i < 4)
|
||||
{
|
||||
if(redstoneConductionE(edgeBetween(side, rotateSide(side&6, i))))
|
||||
m|=1<<i
|
||||
i+=1
|
||||
}
|
||||
m&=redstoneConductionF(side)
|
||||
return m
|
||||
}
|
||||
|
||||
def redstoneConductionF(i:Int) = partMap(i) match {
|
||||
case null => 0x1F
|
||||
case p => p.asInstanceOf[TFacePart].redstoneConductionMap
|
||||
}
|
||||
|
||||
def redstoneConductionE(i:Int) = partMap(i) match {
|
||||
case null => true
|
||||
case p => p.asInstanceOf[TEdgePart].conductsRedstone
|
||||
}
|
||||
|
||||
override def weakPowerLevel(side:Int):Int =
|
||||
weakPowerLevel(side, otherConnectionMask(getWorldObj, xCoord, yCoord, zCoord, side, true))
|
||||
|
||||
override def canConnectRedstone(side:Int):Boolean =
|
||||
{
|
||||
val vside = vanillaToSide(side)
|
||||
return (getConnectionMask(vside) & otherConnectionMask(getWorldObj, xCoord, yCoord, zCoord, vside, false)) > 0
|
||||
}
|
||||
|
||||
def getConnectionMask(side:Int):Int =
|
||||
{
|
||||
val mask = openConnections(side)
|
||||
var res = 0
|
||||
partList.foreach(p =>
|
||||
res|=connectionMask(p, side)&mask)
|
||||
return res
|
||||
}
|
||||
|
||||
def weakPowerLevel(side:Int, mask:Int):Int =
|
||||
{
|
||||
val tmask = openConnections(side)&mask
|
||||
var max = 0
|
||||
partList.foreach(p =>
|
||||
if((connectionMask(p, side)&tmask) > 0)
|
||||
{
|
||||
val l = p.asInstanceOf[IRedstonePart].weakPowerLevel(side)
|
||||
if(l > max) max = l
|
||||
})
|
||||
return max
|
||||
}
|
||||
}
|
||||
/** Mixin trait implementation for IRedstonePart Provides and overrides various
|
||||
* redstone functions.
|
||||
*/
|
||||
trait TRedstoneTile extends TileMultipart with IRedstoneTile {
|
||||
|
||||
/** Returns the strong (indirect) power being emitted fr
|
||||
*/
|
||||
override def strongPowerLevel(side: Int): Int = {
|
||||
var max = 0
|
||||
for (p @ (_p: IRedstonePart) <- partList.iterator) {
|
||||
val l = p.strongPowerLevel(side)
|
||||
if (l > max) max = l
|
||||
}
|
||||
return max
|
||||
}
|
||||
|
||||
def openConnections(side: Int): Int = {
|
||||
var m = 0x10
|
||||
var i = 0
|
||||
while (i < 4) {
|
||||
if (redstoneConductionE(edgeBetween(side, rotateSide(side & 6, i))))
|
||||
m |= 1 << i
|
||||
i += 1
|
||||
}
|
||||
m &= redstoneConductionF(side)
|
||||
return m
|
||||
}
|
||||
|
||||
def redstoneConductionF(i: Int) = partMap(i) match {
|
||||
case null => 0x1f
|
||||
case p => p.asInstanceOf[TFacePart].redstoneConductionMap
|
||||
}
|
||||
|
||||
def redstoneConductionE(i: Int) = partMap(i) match {
|
||||
case null => true
|
||||
case p => p.asInstanceOf[TEdgePart].conductsRedstone
|
||||
}
|
||||
|
||||
override def weakPowerLevel(side: Int): Int =
|
||||
weakPowerLevel(
|
||||
side,
|
||||
otherConnectionMask(getWorldObj, xCoord, yCoord, zCoord, side, true)
|
||||
)
|
||||
|
||||
override def canConnectRedstone(side: Int): Boolean = {
|
||||
val vside = vanillaToSide(side)
|
||||
return (getConnectionMask(vside) & otherConnectionMask(
|
||||
getWorldObj,
|
||||
xCoord,
|
||||
yCoord,
|
||||
zCoord,
|
||||
vside,
|
||||
false
|
||||
)) > 0
|
||||
}
|
||||
|
||||
def getConnectionMask(side: Int): Int = {
|
||||
val mask = openConnections(side)
|
||||
var res = 0
|
||||
partList.foreach(p => res |= connectionMask(p, side) & mask)
|
||||
return res
|
||||
}
|
||||
|
||||
def weakPowerLevel(side: Int, mask: Int): Int = {
|
||||
val tmask = openConnections(side) & mask
|
||||
var max = 0
|
||||
partList.foreach(p =>
|
||||
if ((connectionMask(p, side) & tmask) > 0) {
|
||||
val l = p.asInstanceOf[IRedstonePart].weakPowerLevel(side)
|
||||
if (l > max) max = l
|
||||
}
|
||||
)
|
||||
return max
|
||||
}
|
||||
}
|
||||
@@ -4,61 +4,52 @@ import codechicken.multipart.TileMultipart
|
||||
import codechicken.multipart.TMultiPart
|
||||
import codechicken.multipart.TSlottedPart
|
||||
|
||||
/**
|
||||
* Mixin implementation for TSlottedPart.
|
||||
* Puts parts into a slot array for quick access at the cost of memory consumption
|
||||
*/
|
||||
trait TSlottedTile extends TileMultipart
|
||||
{
|
||||
var v_partMap = new Array[TMultiPart](27)
|
||||
|
||||
override def copyFrom(that:TileMultipart)
|
||||
{
|
||||
super.copyFrom(that)
|
||||
if(that.isInstanceOf[TSlottedTile])
|
||||
v_partMap = that.asInstanceOf[TSlottedTile].v_partMap
|
||||
/** Mixin implementation for TSlottedPart. Puts parts into a slot array for
|
||||
* quick access at the cost of memory consumption
|
||||
*/
|
||||
trait TSlottedTile extends TileMultipart {
|
||||
var v_partMap = new Array[TMultiPart](27)
|
||||
|
||||
override def copyFrom(that: TileMultipart) {
|
||||
super.copyFrom(that)
|
||||
if (that.isInstanceOf[TSlottedTile])
|
||||
v_partMap = that.asInstanceOf[TSlottedTile].v_partMap
|
||||
}
|
||||
|
||||
override def partMap(slot: Int) = v_partMap(slot)
|
||||
|
||||
override def clearParts() {
|
||||
super.clearParts()
|
||||
for (i <- 0 until v_partMap.length)
|
||||
v_partMap(i) = null
|
||||
}
|
||||
|
||||
override def partRemoved(part: TMultiPart, p: Int) {
|
||||
super.partRemoved(part, p)
|
||||
if (part.isInstanceOf[TSlottedPart])
|
||||
for (i <- 0 until 27)
|
||||
if (partMap(i) == part)
|
||||
v_partMap(i) = null
|
||||
}
|
||||
|
||||
override def canAddPart(part: TMultiPart): Boolean = {
|
||||
if (part.isInstanceOf[TSlottedPart]) {
|
||||
val slotMask = part.asInstanceOf[TSlottedPart].getSlotMask
|
||||
for (i <- 0 until v_partMap.length)
|
||||
if ((slotMask & 1 << i) != 0 && partMap(i) != null)
|
||||
return false
|
||||
}
|
||||
|
||||
override def partMap(slot:Int) = v_partMap(slot)
|
||||
|
||||
override def clearParts()
|
||||
{
|
||||
super.clearParts()
|
||||
for(i <- 0 until v_partMap.length)
|
||||
v_partMap(i) = null
|
||||
|
||||
return super.canAddPart(part)
|
||||
}
|
||||
|
||||
override def bindPart(part: TMultiPart) {
|
||||
super.bindPart(part)
|
||||
if (part.isInstanceOf[TSlottedPart]) {
|
||||
val mask = part.asInstanceOf[TSlottedPart].getSlotMask
|
||||
for (i <- 0 until 27)
|
||||
if ((mask & 1 << i) > 0)
|
||||
v_partMap(i) = part
|
||||
}
|
||||
|
||||
override def partRemoved(part:TMultiPart, p:Int)
|
||||
{
|
||||
super.partRemoved(part, p)
|
||||
if(part.isInstanceOf[TSlottedPart])
|
||||
for(i <- 0 until 27)
|
||||
if(partMap(i) == part)
|
||||
v_partMap(i) = null
|
||||
}
|
||||
|
||||
override def canAddPart(part:TMultiPart):Boolean =
|
||||
{
|
||||
if(part.isInstanceOf[TSlottedPart])
|
||||
{
|
||||
val slotMask = part.asInstanceOf[TSlottedPart].getSlotMask
|
||||
for(i <- 0 until v_partMap.length)
|
||||
if((slotMask&1<<i) != 0 && partMap(i) != null)
|
||||
return false
|
||||
}
|
||||
|
||||
return super.canAddPart(part)
|
||||
}
|
||||
|
||||
override def bindPart(part:TMultiPart)
|
||||
{
|
||||
super.bindPart(part)
|
||||
if(part.isInstanceOf[TSlottedPart])
|
||||
{
|
||||
val mask = part.asInstanceOf[TSlottedPart].getSlotMask
|
||||
for(i <- 0 until 27)
|
||||
if ((mask&1<<i) > 0)
|
||||
v_partMap(i) = part
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,55 +5,54 @@ import codechicken.multipart.INeighborTileChange
|
||||
import codechicken.multipart.TileMultipart
|
||||
import codechicken.lib.vec.BlockCoord
|
||||
|
||||
/**
|
||||
* Mixin implementation for INeighborTileChange
|
||||
*
|
||||
* Reduces unnecessary computation
|
||||
*/
|
||||
/** Mixin implementation for INeighborTileChange
|
||||
*
|
||||
* Reduces unnecessary computation
|
||||
*/
|
||||
trait TTileChangeTile extends TileMultipart {
|
||||
var weakTileChanges = false
|
||||
|
||||
override def copyFrom(that:TileMultipart)
|
||||
{
|
||||
super.copyFrom(that)
|
||||
if(that.isInstanceOf[TTileChangeTile])
|
||||
weakTileChanges = that.asInstanceOf[TTileChangeTile].weakTileChanges
|
||||
}
|
||||
|
||||
override def bindPart(part:TMultiPart)
|
||||
{
|
||||
super.bindPart(part)
|
||||
if(part.isInstanceOf[INeighborTileChange])
|
||||
weakTileChanges|=part.asInstanceOf[INeighborTileChange].weakTileChanges
|
||||
}
|
||||
|
||||
override def clearParts()
|
||||
{
|
||||
super.clearParts()
|
||||
weakTileChanges = false
|
||||
}
|
||||
|
||||
override def partRemoved(part:TMultiPart, p:Int) {
|
||||
super.partRemoved(part, p)
|
||||
weakTileChanges = partList.exists(p => p.isInstanceOf[INeighborTileChange] && p.asInstanceOf[INeighborTileChange].weakTileChanges)
|
||||
}
|
||||
|
||||
override def onNeighborTileChange(tileX:Int, tileY:Int, tileZ:Int)
|
||||
{
|
||||
super.onNeighborTileChange(tileX, tileY, tileZ)
|
||||
val offset = new BlockCoord(tileX, tileY, tileZ).sub(xCoord, yCoord, zCoord)
|
||||
val diff = offset.absSum
|
||||
val side = offset.toSide
|
||||
|
||||
if(side < 0 || diff <= 0 || diff > 2)
|
||||
return
|
||||
|
||||
val weak = diff == 2
|
||||
operate{ p =>
|
||||
if(p.isInstanceOf[INeighborTileChange])
|
||||
p.asInstanceOf[INeighborTileChange].onNeighborTileChanged(side, weak)
|
||||
}
|
||||
}
|
||||
var weakTileChanges = false
|
||||
|
||||
override def getWeakChanges() = weakTileChanges
|
||||
}
|
||||
override def copyFrom(that: TileMultipart) {
|
||||
super.copyFrom(that)
|
||||
if (that.isInstanceOf[TTileChangeTile])
|
||||
weakTileChanges = that.asInstanceOf[TTileChangeTile].weakTileChanges
|
||||
}
|
||||
|
||||
override def bindPart(part: TMultiPart) {
|
||||
super.bindPart(part)
|
||||
if (part.isInstanceOf[INeighborTileChange])
|
||||
weakTileChanges |= part.asInstanceOf[INeighborTileChange].weakTileChanges
|
||||
}
|
||||
|
||||
override def clearParts() {
|
||||
super.clearParts()
|
||||
weakTileChanges = false
|
||||
}
|
||||
|
||||
override def partRemoved(part: TMultiPart, p: Int) {
|
||||
super.partRemoved(part, p)
|
||||
weakTileChanges = partList.exists(p =>
|
||||
p.isInstanceOf[INeighborTileChange] && p
|
||||
.asInstanceOf[INeighborTileChange]
|
||||
.weakTileChanges
|
||||
)
|
||||
}
|
||||
|
||||
override def onNeighborTileChange(tileX: Int, tileY: Int, tileZ: Int) {
|
||||
super.onNeighborTileChange(tileX, tileY, tileZ)
|
||||
val offset = new BlockCoord(tileX, tileY, tileZ).sub(xCoord, yCoord, zCoord)
|
||||
val diff = offset.absSum
|
||||
val side = offset.toSide
|
||||
|
||||
if (side < 0 || diff <= 0 || diff > 2)
|
||||
return
|
||||
|
||||
val weak = diff == 2
|
||||
operate { p =>
|
||||
if (p.isInstanceOf[INeighborTileChange])
|
||||
p.asInstanceOf[INeighborTileChange].onNeighborTileChanged(side, weak)
|
||||
}
|
||||
}
|
||||
|
||||
override def getWeakChanges() = weakTileChanges
|
||||
}
|
||||
Reference in new issue
Block a user