diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..fd2792b6
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,44 @@
+* text eol=lf
+
+*.[jJ][aA][rR] binary
+
+*.[pP][nN][gG] binary
+*.[jJ][pP][gG] binary
+*.[jJ][pP][eE][gG] binary
+*.[gG][iI][fF] binary
+*.[tT][iI][fF] binary
+*.[tT][iI][fF][fF] binary
+*.[iI][cC][oO] binary
+*.[sS][vV][gG] text
+*.[eE][pP][sS] binary
+*.[xX][cC][fF] binary
+
+*.[kK][aA][rR] binary
+*.[mM]4[aA] binary
+*.[mM][iI][dD] binary
+*.[mM][iI][dD][iI] binary
+*.[mM][pP]3 binary
+*.[oO][gG][gG] binary
+*.[rR][aA] binary
+
+*.7[zZ] binary
+*.[gG][zZ] binary
+*.[tT][aA][rR] binary
+*.[tT][gG][zZ] binary
+*.[zZ][iI][pP] binary
+
+*.[tT][cC][nN] binary
+*.[sS][oO] binary
+*.[dD][lL][lL] binary
+*.[dD][yY][lL][iI][bB] binary
+*.[pP][sS][dD] binary
+*.[tT][tT][fF] binary
+*.[oO][tT][fF] binary
+
+*.[pP][aA][tT][cC][hH] -text
+
+*.[bB][aA][tT] text eol=crlf
+*.[cC][mM][dD] text eol=crlf
+*.[pP][sS]1 text eol=crlf
+
+*[aA][uU][tT][oO][gG][eE][nN][eE][rR][aA][tT][eE][dD]* binary
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
new file mode 100644
index 00000000..b4e100c4
--- /dev/null
+++ b/.github/workflows/build-and-test.yml
@@ -0,0 +1,17 @@
+name: Build and test
+
+on:
+ pull_request:
+ branches:
+ - master
+ push:
+ branches:
+ - master
+
+jobs:
+ build-and-test:
+ uses: FalsePattern/fpgradle-workflows/.github/workflows/build-and-test.yml@master
+ with:
+ timeout: 90
+ workspace: setupCIWorkspace
+ client-only: false
diff --git a/.github/workflows/release-tags.yml b/.github/workflows/release-tags.yml
new file mode 100644
index 00000000..8275da25
--- /dev/null
+++ b/.github/workflows/release-tags.yml
@@ -0,0 +1,21 @@
+name: Release Tags
+
+on:
+ push:
+ tags:
+ - '*'
+
+permissions:
+ contents: write
+
+jobs:
+ release-tags:
+ uses: FalsePattern/fpgradle-workflows/.github/workflows/release-tags.yml@master
+ with:
+ workspace: "setupCIWorkspace"
+ secrets:
+ MAVEN_DEPLOY_USER: ${{ secrets.MAVEN_DEPLOY_USER }}
+ MAVEN_DEPLOY_PASSWORD: ${{ secrets.MAVEN_DEPLOY_PASSWORD }}
+ MODRINTH_TOKEN: ${{ secrets.MODRINTH_TOKEN }}
+ CURSEFORGE_TOKEN: ${{ secrets.CURSEFORGE_TOKEN }}
+
diff --git a/.gitignore b/.gitignore
index b3e6a285..a57763bf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,185 +1,166 @@
-#################
-## Eclipse
-#################
+# Created by https://www.toptal.com/developers/gitignore/api/gradle,forgegradle,kotlin,java,scala,intellij+all
+# Edit at https://www.toptal.com/developers/gitignore?templates=gradle,forgegradle,kotlin,java,scala,intellij+all
-*.pydevproject
-.gradle/
+### ForgeGradle ###
+# Minecraft client/server files
+run/
+
+### Intellij+all ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# AWS User-specific
+.idea/**/aws.xml
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# SonarLint plugin
+.idea/sonarlint/
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### Intellij+all Patch ###
+# Ignore everything but code style settings and run configurations
+# that are supposed to be shared within teams.
+
+.idea/*
+
+!.idea/codeStyles
+!.idea/runConfigurations
+
+### Java ###
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+replay_pid*
+
+### Kotlin ###
+# Compiled class file
+
+# Log file
+
+# BlueJ files
+
+# Mobile Tools for Java (J2ME)
+
+# Package Files #
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+
+### Scala ###
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+
+### Gradle ###
+.gradle
+**/build/
+!src/**/build/
+
+# Ignore Gradle GUI config
+gradle-app.setting
+
+# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
+!gradle-wrapper.jar
+
+# Avoid ignore Gradle wrappper properties
+!gradle-wrapper.properties
+
+# Cache of project
+.gradletasknamecache
+
+# Eclipse Gradle plugin generated files
+# Eclipse Core
.project
-.metadata
-bin/
-eclipse/
-tmp/
-*.tmp
-*.bak
-*.swp
-*~.nib
-local.properties
+# JDT-specific (Eclipse Java Development Tools)
.classpath
-.settings/
-.loadpath
-crash-reports/
-logs/
-mods/
-resourcepacks/
-saves/
-/options.txt
-
-# External tool builders
-.externalToolBuilders/
-
-# Locally stored "Eclipse launch configurations"
-*.launch
-
-# CDT-specific
-.cproject
-
-# PDT-specific
-.buildpath
-
-
-#################
-## Visual Studio
-#################
-
-## Ignore Visual Studio temporary files, build results, and
-## files generated by popular Visual Studio add-ons.
-
-# User-specific files
-*.suo
-*.user
-*.sln.docstates
-
-# Build results
-[Dd]ebug/
-[Rr]elease/
-*_i.c
-*_p.c
-*.ilk
-*.meta
-*.pch
-*.pdb
-*.pgc
-*.pgd
-*.rsp
-*.sbr
-*.tlb
-*.tli
-*.tlh
-*.tmp
-*.vspscc
-.builds
-*.dotCover
-
-## TODO: If you have NuGet Package Restore enabled, uncomment this
-#packages/
-
-# Visual C++ cache files
-ipch/
-*.aps
-*.ncb
-*.opensdf
-*.sdf
-
-# Visual Studio profiler
-*.psess
-*.vsp
-
-# ReSharper is a .NET coding add-in
-_ReSharper*
-
-# Installshield output folder
-[Ee]xpress
-
-# DocProject is a documentation generator add-in
-DocProject/buildhelp/
-DocProject/Help/*.HxT
-DocProject/Help/*.HxC
-DocProject/Help/*.hhc
-DocProject/Help/*.hhk
-DocProject/Help/*.hhp
-DocProject/Help/Html2
-DocProject/Help/html
-
-# Click-Once directory
-publish
-
-# Others
-[Bb]in
-[Oo]bj
-sql
-TestResults
-*.Cache
-ClientBin
-stylecop.*
-~$*
-*.dbmdl
-Generated_Code #added for RIA/Silverlight projects
-
-# Backup & report files from converting an old project file to a newer
-# Visual Studio version. Backup files are not needed, because we have git ;-)
-_UpgradeReport_Files/
-Backup*/
-UpgradeLog*.XML
-
-
-
-############
-## Windows
-############
-
-# Windows image file caches
-Thumbs.db
-
-# Folder config file
-Desktop.ini
-
-
-#############
-## Python
-#############
-
-*.py[co]
-
-# Packages
-*.egg
-*.egg-info
-dist
-build
-eggs
-parts
-bin
-var
-sdist
-develop-eggs
-.installed.cfg
-
-# Installer logs
-pip-log.txt
-
-# Unit test / coverage reports
-.coverage
-.tox
-
-#Translations
-*.mo
-
-#Mr Developer
-.mr.developer.cfg
-
-# Mac crap
-.DS_Store
-
-###########
-
-downloads
-screenshots
-techne
-
-#############
-## denoflions
-#############
-
-MFR-Forestry
-nbproject
-*.iml
-*.eml
+
+### Gradle Patch ###
+# Java heap dump
+*.hprof
+
+# End of https://www.toptal.com/developers/gitignore/api/gradle,forgegradle,kotlin,java,scala,intellij+all
diff --git a/LICENSE - CoFHLib.txt b/LICENSE - CoFHLib.txt
new file mode 100644
index 00000000..65c5ca88
--- /dev/null
+++ b/LICENSE - CoFHLib.txt
@@ -0,0 +1,165 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index 52bb4cec..00000000
--- a/build.gradle
+++ /dev/null
@@ -1,47 +0,0 @@
-buildscript {
- repositories {
- mavenCentral()
- maven {
- name = "forge"
- url = "http://files.minecraftforge.net/maven"
- }
- maven {
- name = "sonatype"
- url = "https://oss.sonatype.org/content/repositories/snapshots/"
- }
- }
- dependencies {
- classpath 'net.minecraftforge.gradle:ForgeGradle:1.2-SNAPSHOT'
- }
-}
-
-apply plugin: 'forge'
-
-version = "1.7.10R3.0.4"
-group = "cofh"
-archivesBaseName = "CoFHCore"
-
-minecraft {
- version = "1.7.10-10.13.4.1448-1.7.10"
- runDir = "eclipse/assets"
-}
-
-processResources
-{
- // this will ensure that this task is redone when the versions change.
- inputs.property "version", project.version
- inputs.property "mcversion", project.minecraft.version
-
- // replace stuff in mcmod.info, nothing else
- from(sourceSets.main.resources.srcDirs) {
- include 'mcmod.info'
-
- // replace version and mcversion
- expand 'version':project.version, 'mcversion':project.minecraft.version
- }
-
- // copy everything else, thats not the mcmod.info
- from(sourceSets.main.resources.srcDirs) {
- exclude 'mcmod.info'
- }
-}
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 00000000..3bd4e072
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,39 @@
+plugins {
+ id("fpgradle-minecraft") version ("0.6.1")
+}
+
+group = "cofh"
+
+minecraft_fp {
+ mod {
+ modid = "CoFHCore"
+ name = "CoFH Core"
+ rootPkg = "$group"
+ }
+
+ api {
+ packages = listOf("api", "lib")
+ }
+
+ core {
+ coreModClass = "asm.LoadingPlugin"
+ accessTransformerFile = "CoFH_at.cfg"
+ }
+
+ tokens {
+ tokenClass = "Tags"
+ }
+}
+
+repositories {
+ mega {
+ content {
+ includeGroup("codechicken")
+ }
+ }
+}
+
+dependencies {
+ implementation("codechicken:codechickencore-mc1.7.10:1.4.0-mega:dev")
+ implementation("codechicken:notenoughitems-mc1.7.10:2.3.1-mega:dev")
+}
\ No newline at end of file
diff --git a/build.xml b/build.xml
deleted file mode 100644
index ef1f5372..00000000
--- a/build.xml
+++ /dev/null
@@ -1,550 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ${build.full}${build.number}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 3c7abdf1..e6441136 100644
Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 244a668f..a4413138 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,7 @@
-#Wed Mar 26 13:33:58 CDT 2014
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-bin.zip
diff --git a/gradlew b/gradlew
index 91a7e269..b740cf13 100755
--- a/gradlew
+++ b/gradlew
@@ -1,79 +1,127 @@
-#!/usr/bin/env bash
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
##############################################################################
-##
-## Gradle start up script for UN*X
-##
+#
+# Gradle start up script for POSIX generated by Gradle.
+#
+# Important for running:
+#
+# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+# noncompliant, but you have some other compliant shell such as ksh or
+# bash, then to run this script, type that shell name before the whole
+# command line, like:
+#
+# ksh Gradle
+#
+# Busybox and similar reduced shells will NOT work, because this script
+# requires all of these POSIX shell features:
+# * functions;
+# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+# * compound commands having a testable exit status, especially «case»;
+# * various built-in commands including «command», «set», and «ulimit».
+#
+# Important for patching:
+#
+# (2) This script targets any POSIX shell, so it avoids extensions provided
+# by Bash, Ksh, etc; in particular arrays are avoided.
+#
+# The "traditional" practice of packing multiple parameters into a
+# space-separated string is a well documented source of bugs and security
+# problems, so this is (mostly) avoided, by progressively accumulating
+# options in "$@", and eventually passing that to Java.
+#
+# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+# see the in-line comments for details.
+#
+# There are tweaks for specific operating systems such as AIX, CygWin,
+# Darwin, MinGW, and NonStop.
+#
+# (3) This script is generated from the Groovy template
+# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+# within the Gradle project.
+#
+# You can find Gradle at https://github.com/gradle/gradle/.
+#
##############################################################################
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS=""
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+ APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
+ [ -h "$app_path" ]
+do
+ ls=$( ls -ld "$app_path" )
+ link=${ls#*' -> '}
+ case $link in #(
+ /*) app_path=$link ;; #(
+ *) app_path=$APP_HOME$link ;;
+ esac
+done
-APP_NAME="Gradle"
-APP_BASE_NAME=`basename "$0"`
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD="maximum"
+MAX_FD=maximum
-warn ( ) {
+warn () {
echo "$*"
-}
+} >&2
-die ( ) {
+die () {
echo
echo "$*"
echo
exit 1
-}
+} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
-case "`uname`" in
- CYGWIN* )
- cygwin=true
- ;;
- Darwin* )
- darwin=true
- ;;
- MINGW* )
- msys=true
- ;;
+nonstop=false
+case "$( uname )" in #(
+ CYGWIN* ) cygwin=true ;; #(
+ Darwin* ) darwin=true ;; #(
+ MSYS* | MINGW* ) msys=true ;; #(
+ NONSTOP* ) nonstop=true ;;
esac
-# For Cygwin, ensure paths are in UNIX format before anything is touched.
-if $cygwin ; then
- [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
-fi
-
-# Attempt to set APP_HOME
-# Resolve links: $0 may be a link
-PRG="$0"
-# Need this for relative symlinks.
-while [ -h "$PRG" ] ; do
- ls=`ls -ld "$PRG"`
- link=`expr "$ls" : '.*-> \(.*\)$'`
- if expr "$link" : '/.*' > /dev/null; then
- PRG="$link"
- else
- PRG=`dirname "$PRG"`"/$link"
- fi
-done
-SAVED="`pwd`"
-cd "`dirname \"$PRG\"`/" >&-
-APP_HOME="`pwd -P`"
-cd "$SAVED" >&-
-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
- JAVACMD="$JAVA_HOME/jre/sh/java"
+ JAVACMD=$JAVA_HOME/jre/sh/java
else
- JAVACMD="$JAVA_HOME/bin/java"
+ JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -82,83 +130,120 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
- JAVACMD="java"
- which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+ JAVACMD=java
+ if ! command -v java >/dev/null 2>&1
+ then
+ die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
+ fi
fi
# Increase the maximum file descriptors if we can.
-if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
- MAX_FD_LIMIT=`ulimit -H -n`
- if [ $? -eq 0 ] ; then
- if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
- MAX_FD="$MAX_FD_LIMIT"
- fi
- ulimit -n $MAX_FD
- if [ $? -ne 0 ] ; then
- warn "Could not set maximum file descriptor limit: $MAX_FD"
- fi
- else
- warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
- fi
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+ case $MAX_FD in #(
+ max*)
+ # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ MAX_FD=$( ulimit -H -n ) ||
+ warn "Could not query maximum file descriptor limit"
+ esac
+ case $MAX_FD in #(
+ '' | soft) :;; #(
+ *)
+ # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+ # shellcheck disable=SC2039,SC3045
+ ulimit -n "$MAX_FD" ||
+ warn "Could not set maximum file descriptor limit to $MAX_FD"
+ esac
fi
-# For Darwin, add options to specify how the application appears in the dock
-if $darwin; then
- GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
-fi
+# Collect all arguments for the java command, stacking in reverse order:
+# * args from the command line
+# * the main class name
+# * -classpath
+# * -D...appname settings
+# * --module-path (only if needed)
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+ APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+ CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+ JAVACMD=$( cygpath --unix "$JAVACMD" )
-# For Cygwin, switch paths to Windows format before running java
-if $cygwin ; then
- APP_HOME=`cygpath --path --mixed "$APP_HOME"`
- CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
-
- # We build the pattern for arguments to be converted via cygpath
- ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
- SEP=""
- for dir in $ROOTDIRSRAW ; do
- ROOTDIRS="$ROOTDIRS$SEP$dir"
- SEP="|"
- done
- OURCYGPATTERN="(^($ROOTDIRS))"
- # Add a user-defined pattern to the cygpath arguments
- if [ "$GRADLE_CYGPATTERN" != "" ] ; then
- OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
- fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
- i=0
- for arg in "$@" ; do
- CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
- CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
-
- if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
- eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
- else
- eval `echo args$i`="\"$arg\""
+ for arg do
+ if
+ case $arg in #(
+ -*) false ;; # don't mess with options #(
+ /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
+ [ -e "$t" ] ;; #(
+ *) false ;;
+ esac
+ then
+ arg=$( cygpath --path --ignore --mixed "$arg" )
fi
- i=$((i+1))
+ # Roll the args list around exactly as many times as the number of
+ # args, so each arg winds up back in the position where it started, but
+ # possibly modified.
+ #
+ # NB: a `for` loop captures its iteration list before it begins, so
+ # changing the positional parameters here affects neither the number of
+ # iterations, nor the values presented in `arg`.
+ shift # remove old arg
+ set -- "$@" "$arg" # push replacement arg
done
- case $i in
- (0) set -- ;;
- (1) set -- "$args0" ;;
- (2) set -- "$args0" "$args1" ;;
- (3) set -- "$args0" "$args1" "$args2" ;;
- (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
- (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
- (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
- (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
- (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
- (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
- esac
fi
-# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
-function splitJvmOpts() {
- JVM_OPTS=("$@")
-}
-eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
-JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
-exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+# and any embedded shellness will be escaped.
+# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+# treated as '${Hostname}' itself on the command line.
+
+set -- \
+ "-Dorg.gradle.appname=$APP_BASE_NAME" \
+ -classpath "$CLASSPATH" \
+ org.gradle.wrapper.GradleWrapperMain \
+ "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+ die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+# set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+ printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+ xargs -n1 |
+ sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+ tr '\n' ' '
+ )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
index 8a0b282a..25da30db 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -1,4 +1,20 @@
-@if "%DEBUG%" == "" @echo off
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -8,26 +24,30 @@
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS=
-
set DIRNAME=%~dp0
-if "%DIRNAME%" == "" set DIRNAME=.
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
+if %ERRORLEVEL% equ 0 goto execute
-echo.
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
@@ -35,54 +55,36 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
-echo.
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
-echo.
-echo Please set the JAVA_HOME variable in your environment to match the
-echo location of your Java installation.
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
goto fail
-:init
-@rem Get command-line arguments, handling Windowz variants
-
-if not "%OS%" == "Windows_NT" goto win9xME_args
-if "%@eval[2+2]" == "4" goto 4NT_args
-
-:win9xME_args
-@rem Slurp the command line arguments.
-set CMD_LINE_ARGS=
-set _SKIP=2
-
-:win9xME_args_slurp
-if "x%~1" == "x" goto execute
-
-set CMD_LINE_ARGS=%*
-goto execute
-
-:4NT_args
-@rem Get arguments from the 4NT Shell from JP Software
-set CMD_LINE_ARGS=%$
-
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
-if "%ERRORLEVEL%"=="0" goto mainEnd
+if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
-if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
-exit /b 1
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 00000000..6882347c
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,31 @@
+pluginManagement {
+ repositories {
+ maven {
+ url = uri("https://mvn.falsepattern.com/fpgradle/")
+ name = "fpgradle"
+ content {
+ includeModule("com.gtnewhorizons", "retrofuturagradle")
+ includeModule("com.falsepattern", "fpgradle-plugin")
+ includeModule("fpgradle-minecraft", "fpgradle-minecraft.gradle.plugin")
+ }
+ }
+ maven {
+ url = uri("https://mvn.falsepattern.com/releases/")
+ name = "mavenpattern"
+ content {
+ includeGroup("com.falsepattern")
+ }
+ }
+ maven {
+ url = uri("https://mvn.falsepattern.com/jitpack/")
+ name = "jitpack"
+ content {
+ includeModule("io.github.LegacyModdingMC.MappingGenerator", "MappingGenerator")
+ }
+ }
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+rootProject.name = "CoFHCore"
diff --git a/src/main/java/cofh/CoFHCore.java b/src/main/java/cofh/CoFHCore.java
index a443accb..eb43ff50 100644
--- a/src/main/java/cofh/CoFHCore.java
+++ b/src/main/java/cofh/CoFHCore.java
@@ -63,7 +63,7 @@ public class CoFHCore extends BaseMod {
public static final String modId = "CoFHCore";
public static final String modName = "CoFH Core";
- public static final String version = "1.7.10R3.1.4";
+ public static final String version = "1.7.10R" + Tags.MOD_VERSION;
public static final String version_max = "1.7.10R3.2.0";
public static final String dependencies = CoFHProps.FORGE_DEP;
public static final String modGuiFactory = "cofh.core.gui.GuiConfigCoreFactory";
diff --git a/src/main/java/cofh/api/CoFHAPIProps.java b/src/main/java/cofh/api/CoFHAPIProps.java
new file mode 100644
index 00000000..5d21f636
--- /dev/null
+++ b/src/main/java/cofh/api/CoFHAPIProps.java
@@ -0,0 +1,11 @@
+package cofh.api;
+
+public class CoFHAPIProps {
+
+ private CoFHAPIProps() {
+
+ }
+
+ public static final String VERSION = "1.7.10R1.3.1";
+
+}
diff --git a/src/main/java/cofh/api/block/IBlockAppearance.java b/src/main/java/cofh/api/block/IBlockAppearance.java
new file mode 100644
index 00000000..70b08515
--- /dev/null
+++ b/src/main/java/cofh/api/block/IBlockAppearance.java
@@ -0,0 +1,53 @@
+package cofh.api.block;
+
+import net.minecraft.block.Block;
+import net.minecraft.world.IBlockAccess;
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this interface on blocks that can mimic the appearance of other blocks. Note that this is meant to be available server-side, so ensure the code is
+ * server-safe and doesn't use client-side code.
+ *
+ */
+public interface IBlockAppearance {
+
+ /**
+ * This function returns the block that is being shown on a given side.
+ *
+ * Do not return null from this method.
+ *
+ * @param world
+ * Reference to the world.
+ * @param x
+ * X coordinate of the block.
+ * @param y
+ * Y coordinate of the block.
+ * @param z
+ * Z coordinate of the block.
+ * @param side
+ * The side of the block.
+ */
+ public Block getVisualBlock(IBlockAccess world, int x, int y, int z, ForgeDirection side);
+
+ /**
+ * This function returns metadata of the block that is being shown on a given side.
+ *
+ * @param world
+ * Reference to the world.
+ * @param x
+ * X coordinate of the block.
+ * @param y
+ * Y coordinate of the block.
+ * @param z
+ * Z coordinate of the block.
+ * @param side
+ * The side of the block.
+ */
+ public int getVisualMeta(IBlockAccess world, int x, int y, int z, ForgeDirection side);
+
+ /**
+ * This function returns whether the block's renderer will visually connect to other blocks implementing IBlockAppearance.
+ */
+ public boolean supportsVisualConnections();
+
+}
diff --git a/src/main/java/cofh/api/block/IBlockConfigGui.java b/src/main/java/cofh/api/block/IBlockConfigGui.java
new file mode 100644
index 00000000..9e00c87d
--- /dev/null
+++ b/src/main/java/cofh/api/block/IBlockConfigGui.java
@@ -0,0 +1,31 @@
+package cofh.api.block;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.world.IBlockAccess;
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this interface on blocks which have a GUI that needs a tool (e.g., multimeter) to open.
+ *
+ */
+public interface IBlockConfigGui {
+
+ /**
+ * This function will open a GUI if the player has permission.
+ *
+ * @param world
+ * Reference to the world.
+ * @param x
+ * X coordinate of the block.
+ * @param y
+ * Y coordinate of the block.
+ * @param z
+ * Z coordinate of the block.
+ * @param side
+ * The side of the block.
+ * @param player
+ * Player doing the configuring.
+ * @return True if the GUI was opened.
+ */
+ public boolean openConfigGui(IBlockAccess world, int x, int y, int z, ForgeDirection side, EntityPlayer player);
+}
diff --git a/src/main/java/cofh/api/block/IBlockDebug.java b/src/main/java/cofh/api/block/IBlockDebug.java
new file mode 100644
index 00000000..dc02f7f7
--- /dev/null
+++ b/src/main/java/cofh/api/block/IBlockDebug.java
@@ -0,0 +1,33 @@
+package cofh.api.block;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.world.IBlockAccess;
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this interface on blocks which have some debug method which can be activated via a tool or other means.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IBlockDebug {
+
+ /**
+ * This function debugs a block.
+ *
+ * @param world
+ * Reference to the world.
+ * @param x
+ * X coordinate of the block.
+ * @param y
+ * Y coordinate of the block.
+ * @param z
+ * Z coordinate of the block.
+ * @param side
+ * The side of the block.
+ * @param player
+ * Player doing the debugging.
+ */
+ void debugBlock(IBlockAccess world, int x, int y, int z, ForgeDirection side, EntityPlayer player);
+
+}
diff --git a/src/main/java/cofh/api/block/IBlockInfo.java b/src/main/java/cofh/api/block/IBlockInfo.java
new file mode 100644
index 00000000..8494243c
--- /dev/null
+++ b/src/main/java/cofh/api/block/IBlockInfo.java
@@ -0,0 +1,43 @@
+package cofh.api.block;
+
+import cofh.api.tileentity.ITileInfo;
+
+import java.util.List;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.util.IChatComponent;
+import net.minecraft.world.IBlockAccess;
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this interface on blocks which can provide information about themselves. If the block contains Tile Entities, then it is recommended that this
+ * function serve as a passthrough for {@link ITileInfo}.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IBlockInfo {
+
+ /**
+ * This function appends information to a list provided to it.
+ *
+ * @param world
+ * Reference to the world.
+ * @param x
+ * X coordinate of the block.
+ * @param y
+ * Y coordinate of the block.
+ * @param z
+ * Z coordinate of the block.
+ * @param side
+ * The side of the block that is being queried.
+ * @param player
+ * Player doing the querying - this can be NULL.
+ * @param info
+ * The list that the information should be appended to.
+ * @param debug
+ * If true, the block should return "debug" information.
+ */
+ void getBlockInfo(IBlockAccess world, int x, int y, int z, ForgeDirection side, EntityPlayer player, List info, boolean debug);
+
+}
diff --git a/src/main/java/cofh/api/block/IDismantleable.java b/src/main/java/cofh/api/block/IDismantleable.java
new file mode 100644
index 00000000..12c09e3a
--- /dev/null
+++ b/src/main/java/cofh/api/block/IDismantleable.java
@@ -0,0 +1,27 @@
+package cofh.api.block;
+
+import java.util.ArrayList;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.world.World;
+
+/**
+ * Implemented on Blocks which have some method of being instantly dismantled.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IDismantleable {
+
+ /**
+ * Dismantles the block. If returnDrops is true, the drop(s) should be placed into the player's inventory.
+ */
+ ArrayList dismantleBlock(EntityPlayer player, World world, int x, int y, int z, boolean returnDrops);
+
+ /**
+ * Return true if the block can be dismantled. The criteria for this is entirely up to the block.
+ */
+ boolean canDismantle(EntityPlayer player, World world, int x, int y, int z);
+
+}
diff --git a/src/main/java/cofh/api/block/package-info.java b/src/main/java/cofh/api/block/package-info.java
new file mode 100644
index 00000000..36f61194
--- /dev/null
+++ b/src/main/java/cofh/api/block/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHAPIProps.VERSION, owner = "CoFHAPI", provides = "CoFHAPI|block")
+package cofh.api.block;
+
+import cofh.api.CoFHAPIProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/api/core/ICustomInventory.java b/src/main/java/cofh/api/core/ICustomInventory.java
new file mode 100644
index 00000000..02395fbe
--- /dev/null
+++ b/src/main/java/cofh/api/core/ICustomInventory.java
@@ -0,0 +1,19 @@
+package cofh.api.core;
+
+import net.minecraft.item.ItemStack;
+
+/**
+ * Interface to allow a Container to interact with a secondary inventory.
+ *
+ * @author King Lemming
+ *
+ */
+public interface ICustomInventory {
+
+ ItemStack[] getInventorySlots(int inventoryIndex);
+
+ int getSlotStackLimit(int slotIndex);
+
+ void onSlotUpdate();
+
+}
diff --git a/src/main/java/cofh/api/core/IInitializer.java b/src/main/java/cofh/api/core/IInitializer.java
new file mode 100644
index 00000000..190859a9
--- /dev/null
+++ b/src/main/java/cofh/api/core/IInitializer.java
@@ -0,0 +1,17 @@
+package cofh.api.core;
+
+/**
+ * Interface which can be put on just about anything to allow for iteration during initialization.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IInitializer {
+
+ boolean preInit();
+
+ boolean initialize();
+
+ boolean postInit();
+
+}
diff --git a/src/main/java/cofh/api/core/package-info.java b/src/main/java/cofh/api/core/package-info.java
new file mode 100644
index 00000000..6ad8913c
--- /dev/null
+++ b/src/main/java/cofh/api/core/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHAPIProps.VERSION, owner = "CoFHAPI", provides = "CoFHAPI|core")
+package cofh.api.core;
+
+import cofh.api.CoFHAPIProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/api/energy/EnergyStorage.java b/src/main/java/cofh/api/energy/EnergyStorage.java
new file mode 100644
index 00000000..25e0126a
--- /dev/null
+++ b/src/main/java/cofh/api/energy/EnergyStorage.java
@@ -0,0 +1,158 @@
+package cofh.api.energy;
+
+import net.minecraft.nbt.NBTTagCompound;
+
+/**
+ * Reference implementation of {@link IEnergyStorage}. Use/extend this or implement your own.
+ *
+ * @author King Lemming
+ *
+ */
+public class EnergyStorage implements IEnergyStorage {
+
+ protected int energy;
+ protected int capacity;
+ protected int maxReceive;
+ protected int maxExtract;
+
+ public EnergyStorage(int capacity) {
+
+ this(capacity, capacity, capacity);
+ }
+
+ public EnergyStorage(int capacity, int maxTransfer) {
+
+ this(capacity, maxTransfer, maxTransfer);
+ }
+
+ public EnergyStorage(int capacity, int maxReceive, int maxExtract) {
+
+ this.capacity = capacity;
+ this.maxReceive = maxReceive;
+ this.maxExtract = maxExtract;
+ }
+
+ public EnergyStorage readFromNBT(NBTTagCompound nbt) {
+
+ this.energy = nbt.getInteger("Energy");
+
+ if (energy > capacity) {
+ energy = capacity;
+ }
+ return this;
+ }
+
+ public NBTTagCompound writeToNBT(NBTTagCompound nbt) {
+
+ if (energy < 0) {
+ energy = 0;
+ }
+ nbt.setInteger("Energy", energy);
+ return nbt;
+ }
+
+ public void setCapacity(int capacity) {
+
+ this.capacity = capacity;
+
+ if (energy > capacity) {
+ energy = capacity;
+ }
+ }
+
+ public void setMaxTransfer(int maxTransfer) {
+
+ setMaxReceive(maxTransfer);
+ setMaxExtract(maxTransfer);
+ }
+
+ public void setMaxReceive(int maxReceive) {
+
+ this.maxReceive = maxReceive;
+ }
+
+ public void setMaxExtract(int maxExtract) {
+
+ this.maxExtract = maxExtract;
+ }
+
+ public int getMaxReceive() {
+
+ return maxReceive;
+ }
+
+ public int getMaxExtract() {
+
+ return maxExtract;
+ }
+
+ /**
+ * This function is included to allow for server -> client sync. Do not call this externally to the containing Tile Entity, as not all IEnergyHandlers
+ * are guaranteed to have it.
+ *
+ * @param energy
+ */
+ public void setEnergyStored(int energy) {
+
+ this.energy = energy;
+
+ if (this.energy > capacity) {
+ this.energy = capacity;
+ } else if (this.energy < 0) {
+ this.energy = 0;
+ }
+ }
+
+ /**
+ * This function is included to allow the containing tile to directly and efficiently modify the energy contained in the EnergyStorage. Do not rely on this
+ * externally, as not all IEnergyHandlers are guaranteed to have it.
+ *
+ * @param energy
+ */
+ public void modifyEnergyStored(int energy) {
+
+ this.energy += energy;
+
+ if (this.energy > capacity) {
+ this.energy = capacity;
+ } else if (this.energy < 0) {
+ this.energy = 0;
+ }
+ }
+
+ /* IEnergyStorage */
+ @Override
+ public int receiveEnergy(int maxReceive, boolean simulate) {
+
+ int energyReceived = Math.min(capacity - energy, Math.min(this.maxReceive, maxReceive));
+
+ if (!simulate) {
+ energy += energyReceived;
+ }
+ return energyReceived;
+ }
+
+ @Override
+ public int extractEnergy(int maxExtract, boolean simulate) {
+
+ int energyExtracted = Math.min(energy, Math.min(this.maxExtract, maxExtract));
+
+ if (!simulate) {
+ energy -= energyExtracted;
+ }
+ return energyExtracted;
+ }
+
+ @Override
+ public int getEnergyStored() {
+
+ return energy;
+ }
+
+ @Override
+ public int getMaxEnergyStored() {
+
+ return capacity;
+ }
+
+}
diff --git a/src/main/java/cofh/api/energy/IEnergyConnection.java b/src/main/java/cofh/api/energy/IEnergyConnection.java
new file mode 100644
index 00000000..301271e5
--- /dev/null
+++ b/src/main/java/cofh/api/energy/IEnergyConnection.java
@@ -0,0 +1,21 @@
+package cofh.api.energy;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this interface on TileEntities which should connect to energy transportation blocks. This is intended for blocks which generate energy but do not
+ * accept it; otherwise just use IEnergyHandler.
+ *
+ * Note that {@link IEnergyHandler} is an extension of this.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IEnergyConnection {
+
+ /**
+ * Returns TRUE if the TileEntity can connect on a given side.
+ */
+ boolean canConnectEnergy(ForgeDirection from);
+
+}
diff --git a/src/main/java/cofh/api/energy/IEnergyContainerItem.java b/src/main/java/cofh/api/energy/IEnergyContainerItem.java
new file mode 100644
index 00000000..3ef72576
--- /dev/null
+++ b/src/main/java/cofh/api/energy/IEnergyContainerItem.java
@@ -0,0 +1,52 @@
+package cofh.api.energy;
+
+import net.minecraft.item.ItemStack;
+
+/**
+ * Implement this interface on Item classes that support external manipulation of their internal energy storages.
+ *
+ * A reference implementation is provided {@link ItemEnergyContainer}.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IEnergyContainerItem {
+
+ /**
+ * Adds energy to a container item. Returns the quantity of energy that was accepted. This should always return 0 if the item cannot be externally charged.
+ *
+ * @param container
+ * ItemStack to be charged.
+ * @param maxReceive
+ * Maximum amount of energy to be sent into the item.
+ * @param simulate
+ * If TRUE, the charge will only be simulated.
+ * @return Amount of energy that was (or would have been, if simulated) received by the item.
+ */
+ int receiveEnergy(ItemStack container, int maxReceive, boolean simulate);
+
+ /**
+ * Removes energy from a container item. Returns the quantity of energy that was removed. This should always return 0 if the item cannot be externally
+ * discharged.
+ *
+ * @param container
+ * ItemStack to be discharged.
+ * @param maxExtract
+ * Maximum amount of energy to be extracted from the item.
+ * @param simulate
+ * If TRUE, the discharge will only be simulated.
+ * @return Amount of energy that was (or would have been, if simulated) extracted from the item.
+ */
+ int extractEnergy(ItemStack container, int maxExtract, boolean simulate);
+
+ /**
+ * Get the amount of energy currently stored in the container item.
+ */
+ int getEnergyStored(ItemStack container);
+
+ /**
+ * Get the max amount of energy that can be stored in the container item.
+ */
+ int getMaxEnergyStored(ItemStack container);
+
+}
diff --git a/src/main/java/cofh/api/energy/IEnergyHandler.java b/src/main/java/cofh/api/energy/IEnergyHandler.java
new file mode 100644
index 00000000..6a4600a4
--- /dev/null
+++ b/src/main/java/cofh/api/energy/IEnergyHandler.java
@@ -0,0 +1,57 @@
+package cofh.api.energy;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this interface on Tile Entities which should handle energy, generally storing it in one or more internal {@link IEnergyStorage} objects.
+ *
+ * A reference implementation is provided {@link TileEnergyHandler}.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IEnergyHandler extends IEnergyProvider, IEnergyReceiver {
+
+ // merely a convenience interface (remove these methods in 1.8; provided here for back-compat via compiler doing things)
+
+ /**
+ * Add energy to an IEnergyReceiver, internal distribution is left entirely to the IEnergyReceiver.
+ *
+ * @param from
+ * Orientation the energy is received from.
+ * @param maxReceive
+ * Maximum amount of energy to receive.
+ * @param simulate
+ * If TRUE, the charge will only be simulated.
+ * @return Amount of energy that was (or would have been, if simulated) received.
+ */
+ @Override
+ int receiveEnergy(ForgeDirection from, int maxReceive, boolean simulate);
+
+ /**
+ * Remove energy from an IEnergyProvider, internal distribution is left entirely to the IEnergyProvider.
+ *
+ * @param from
+ * Orientation the energy is extracted from.
+ * @param maxExtract
+ * Maximum amount of energy to extract.
+ * @param simulate
+ * If TRUE, the extraction will only be simulated.
+ * @return Amount of energy that was (or would have been, if simulated) extracted.
+ */
+ @Override
+ int extractEnergy(ForgeDirection from, int maxExtract, boolean simulate);
+
+ /**
+ * Returns the amount of energy currently stored.
+ */
+ @Override
+ int getEnergyStored(ForgeDirection from);
+
+ /**
+ * Returns the maximum amount of energy that can be stored.
+ */
+ @Override
+ int getMaxEnergyStored(ForgeDirection from);
+
+}
diff --git a/src/main/java/cofh/api/energy/IEnergyProvider.java b/src/main/java/cofh/api/energy/IEnergyProvider.java
new file mode 100644
index 00000000..05287b35
--- /dev/null
+++ b/src/main/java/cofh/api/energy/IEnergyProvider.java
@@ -0,0 +1,38 @@
+package cofh.api.energy;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this interface on Tile Entities which should provide energy, generally storing it in one or more internal {@link IEnergyStorage} objects.
+ *
+ * A reference implementation is provided {@link TileEnergyHandler}.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IEnergyProvider extends IEnergyConnection {
+
+ /**
+ * Remove energy from an IEnergyProvider, internal distribution is left entirely to the IEnergyProvider.
+ *
+ * @param from
+ * Orientation the energy is extracted from.
+ * @param maxExtract
+ * Maximum amount of energy to extract.
+ * @param simulate
+ * If TRUE, the extraction will only be simulated.
+ * @return Amount of energy that was (or would have been, if simulated) extracted.
+ */
+ int extractEnergy(ForgeDirection from, int maxExtract, boolean simulate);
+
+ /**
+ * Returns the amount of energy currently stored.
+ */
+ int getEnergyStored(ForgeDirection from);
+
+ /**
+ * Returns the maximum amount of energy that can be stored.
+ */
+ int getMaxEnergyStored(ForgeDirection from);
+
+}
diff --git a/src/main/java/cofh/api/energy/IEnergyReceiver.java b/src/main/java/cofh/api/energy/IEnergyReceiver.java
new file mode 100644
index 00000000..c726e09e
--- /dev/null
+++ b/src/main/java/cofh/api/energy/IEnergyReceiver.java
@@ -0,0 +1,38 @@
+package cofh.api.energy;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this interface on Tile Entities which should receive energy, generally storing it in one or more internal {@link IEnergyStorage} objects.
+ *
+ * A reference implementation is provided {@link TileEnergyHandler}.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IEnergyReceiver extends IEnergyConnection {
+
+ /**
+ * Add energy to an IEnergyReceiver, internal distribution is left entirely to the IEnergyReceiver.
+ *
+ * @param from
+ * Orientation the energy is received from.
+ * @param maxReceive
+ * Maximum amount of energy to receive.
+ * @param simulate
+ * If TRUE, the charge will only be simulated.
+ * @return Amount of energy that was (or would have been, if simulated) received.
+ */
+ int receiveEnergy(ForgeDirection from, int maxReceive, boolean simulate);
+
+ /**
+ * Returns the amount of energy currently stored.
+ */
+ int getEnergyStored(ForgeDirection from);
+
+ /**
+ * Returns the maximum amount of energy that can be stored.
+ */
+ int getMaxEnergyStored(ForgeDirection from);
+
+}
diff --git a/src/main/java/cofh/api/energy/IEnergyStorage.java b/src/main/java/cofh/api/energy/IEnergyStorage.java
new file mode 100644
index 00000000..414b2656
--- /dev/null
+++ b/src/main/java/cofh/api/energy/IEnergyStorage.java
@@ -0,0 +1,46 @@
+package cofh.api.energy;
+
+/**
+ * An energy storage is the unit of interaction with Energy inventories.
+ * This is not to be implemented on TileEntities. This is for internal use only.
+ *
+ * A reference implementation can be found at {@link EnergyStorage}.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IEnergyStorage {
+
+ /**
+ * Adds energy to the storage. Returns quantity of energy that was accepted.
+ *
+ * @param maxReceive
+ * Maximum amount of energy to be inserted.
+ * @param simulate
+ * If TRUE, the insertion will only be simulated.
+ * @return Amount of energy that was (or would have been, if simulated) accepted by the storage.
+ */
+ int receiveEnergy(int maxReceive, boolean simulate);
+
+ /**
+ * Removes energy from the storage. Returns quantity of energy that was removed.
+ *
+ * @param maxExtract
+ * Maximum amount of energy to be extracted.
+ * @param simulate
+ * If TRUE, the extraction will only be simulated.
+ * @return Amount of energy that was (or would have been, if simulated) extracted from the storage.
+ */
+ int extractEnergy(int maxExtract, boolean simulate);
+
+ /**
+ * Returns the amount of energy currently stored.
+ */
+ int getEnergyStored();
+
+ /**
+ * Returns the maximum amount of energy that can be stored.
+ */
+ int getMaxEnergyStored();
+
+}
diff --git a/src/main/java/cofh/api/energy/IEnergyTransport.java b/src/main/java/cofh/api/energy/IEnergyTransport.java
new file mode 100644
index 00000000..ab45e04f
--- /dev/null
+++ b/src/main/java/cofh/api/energy/IEnergyTransport.java
@@ -0,0 +1,109 @@
+package cofh.api.energy;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this interface on Tile Entities which transport energy.
+ *
+ * This is used to "negotiate" connection types between two separate IEnergyTransports, allowing users to set flow direction and allowing for networks Of
+ * IEnergyTransports to intelligently transfer energy to other networks.
+ */
+public interface IEnergyTransport extends IEnergyProvider, IEnergyReceiver {
+
+ /**
+ * The type of interface for a given side of a {@link IEnergyTransport}.
+ *
+ * Values are:
+ * {@link SEND} for sending only
+ * {@link RECEIVE} for receiving only
+ * {@link BALANCE} for sending and receiving, and the default state
+ */
+ public enum InterfaceType {
+ /**
+ * Indicates that this {@link IEnergyTransport} is only sending power on this side.
+ */
+ SEND,
+ /**
+ * Indicates that this {@link IEnergyTransport} is only receiving power on this side.
+ */
+ RECEIVE,
+ /**
+ * Indicates that this {@link IEnergyTransport} wants to balance power between itself and the
+ * senders/receivers on this side. This is the default state.
+ * To block any connection, use {@link IEnergyConnection#canConnectEnergy}
+ *
+ * IEnergyTransport based senders should check that the total power in the destination IEnergyTransport is less than the power in themselves before sending.
+ *
+ * Active IEnergyTransport receivers (i.e., those that call {@link IEnergyProvider#extractEnergy}) should check that they contain less power than the
+ * source IEnergyTransport.
+ */
+ BALANCE;
+
+ /**
+ * Returns the opposite state to this InterfaceType.
+ *
+ * {@link #BALANCE} is considered its own opposite.
+ * {@link #SEND} is the opposite of {@link #RECEIVE} and visa versa.
+ */
+ public InterfaceType getOpposite() {
+
+ return this == BALANCE ? BALANCE : this == SEND ? RECEIVE : SEND;
+ }
+
+ /**
+ * Returns the next InterfaceType as described in {@link IEnergyTransport#getTransportState}
+ */
+ public InterfaceType rotate() {
+
+ return rotate(true);
+ }
+
+ /**
+ * Returns the next InterfaceType as described in {@link IEnergyTransport#getTransportState}
+ *
+ * @param forward
+ * Whether to step in the order specified by {@link IEnergyTransport#getTransportState} (true ) or to step in the opposite direction
+ */
+ public InterfaceType rotate(boolean forward) {
+
+ if (forward) {
+ return this == BALANCE ? RECEIVE : this == RECEIVE ? SEND : BALANCE;
+ } else {
+ return this == BALANCE ? SEND : this == SEND ? RECEIVE : BALANCE;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ * This method cannot be a no-op for IEnergyTransport.
+ */
+ @Override
+ int getEnergyStored(ForgeDirection from);
+
+ /**
+ * Indicates to other IEnergyTransports the state of the given side. See {@link #InterfaceType} for details.
+ *
+ * For clarity of state tracking, on a tile update from another IEnergyTransport, if its mode has changed from the opposite of your own mode on that side, you
+ * should change your mode to the opposite of its mode.
+ *
+ * When the user alters your mode and your state is:
+ * BALANCE, your mode should change to {@link InterFaceType#RECEIVE}.
+ * RECEIVE, your mode should change to {@link InterFaceType#SEND}.
+ * SEND, your mode should change to {@link InterFaceType#BALANCE}.
+ * This is not required, but will be easier for users.
+ *
+ * @return The type of connection to establish on this side. null is NOT a valid value
+ */
+ InterfaceType getTransportState(ForgeDirection from);
+
+ /**
+ * This method is provided primarily for the purposes of automation tools, and should not need to be called by another IEnergyTransport.
+ *
+ * Calls to this method may fail if this IEnergyTransport has been secured by a user.
+ *
+ * @return Whether or not state was successfully altered.
+ */
+ boolean setTransportState(InterfaceType state, ForgeDirection from);
+
+}
diff --git a/src/main/java/cofh/api/energy/ItemEnergyContainer.java b/src/main/java/cofh/api/energy/ItemEnergyContainer.java
new file mode 100644
index 00000000..2d3659cb
--- /dev/null
+++ b/src/main/java/cofh/api/energy/ItemEnergyContainer.java
@@ -0,0 +1,110 @@
+package cofh.api.energy;
+
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+
+/**
+ * Reference implementation of {@link IEnergyContainerItem}. Use/extend this or implement your own.
+ *
+ * @author King Lemming
+ *
+ */
+public class ItemEnergyContainer extends Item implements IEnergyContainerItem {
+
+ protected int capacity;
+ protected int maxReceive;
+ protected int maxExtract;
+
+ public ItemEnergyContainer() {
+
+ }
+
+ public ItemEnergyContainer(int capacity) {
+
+ this(capacity, capacity, capacity);
+ }
+
+ public ItemEnergyContainer(int capacity, int maxTransfer) {
+
+ this(capacity, maxTransfer, maxTransfer);
+ }
+
+ public ItemEnergyContainer(int capacity, int maxReceive, int maxExtract) {
+
+ this.capacity = capacity;
+ this.maxReceive = maxReceive;
+ this.maxExtract = maxExtract;
+ }
+
+ public ItemEnergyContainer setCapacity(int capacity) {
+
+ this.capacity = capacity;
+ return this;
+ }
+
+ public void setMaxTransfer(int maxTransfer) {
+
+ setMaxReceive(maxTransfer);
+ setMaxExtract(maxTransfer);
+ }
+
+ public void setMaxReceive(int maxReceive) {
+
+ this.maxReceive = maxReceive;
+ }
+
+ public void setMaxExtract(int maxExtract) {
+
+ this.maxExtract = maxExtract;
+ }
+
+ /* IEnergyContainerItem */
+ @Override
+ public int receiveEnergy(ItemStack container, int maxReceive, boolean simulate) {
+
+ if (container.stackTagCompound == null) {
+ container.stackTagCompound = new NBTTagCompound();
+ }
+ int energy = container.stackTagCompound.getInteger("Energy");
+ int energyReceived = Math.min(capacity - energy, Math.min(this.maxReceive, maxReceive));
+
+ if (!simulate) {
+ energy += energyReceived;
+ container.stackTagCompound.setInteger("Energy", energy);
+ }
+ return energyReceived;
+ }
+
+ @Override
+ public int extractEnergy(ItemStack container, int maxExtract, boolean simulate) {
+
+ if (container.stackTagCompound == null || !container.stackTagCompound.hasKey("Energy")) {
+ return 0;
+ }
+ int energy = container.stackTagCompound.getInteger("Energy");
+ int energyExtracted = Math.min(energy, Math.min(this.maxExtract, maxExtract));
+
+ if (!simulate) {
+ energy -= energyExtracted;
+ container.stackTagCompound.setInteger("Energy", energy);
+ }
+ return energyExtracted;
+ }
+
+ @Override
+ public int getEnergyStored(ItemStack container) {
+
+ if (container.stackTagCompound == null || !container.stackTagCompound.hasKey("Energy")) {
+ return 0;
+ }
+ return container.stackTagCompound.getInteger("Energy");
+ }
+
+ @Override
+ public int getMaxEnergyStored(ItemStack container) {
+
+ return capacity;
+ }
+
+}
diff --git a/src/main/java/cofh/api/energy/TileEnergyHandler.java b/src/main/java/cofh/api/energy/TileEnergyHandler.java
new file mode 100644
index 00000000..7cc655e9
--- /dev/null
+++ b/src/main/java/cofh/api/energy/TileEnergyHandler.java
@@ -0,0 +1,65 @@
+package cofh.api.energy;
+
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Reference implementation of {@link IEnergyHandler}. Use/extend this or implement your own.
+ *
+ * @author King Lemming
+ *
+ */
+public class TileEnergyHandler extends TileEntity implements IEnergyHandler {
+
+ protected EnergyStorage storage = new EnergyStorage(32000);
+
+ @Override
+ public void readFromNBT(NBTTagCompound nbt) {
+
+ super.readFromNBT(nbt);
+ storage.readFromNBT(nbt);
+ }
+
+ @Override
+ public void writeToNBT(NBTTagCompound nbt) {
+
+ super.writeToNBT(nbt);
+ storage.writeToNBT(nbt);
+ }
+
+ /* IEnergyConnection */
+ @Override
+ public boolean canConnectEnergy(ForgeDirection from) {
+
+ return true;
+ }
+
+ /* IEnergyReceiver */
+ @Override
+ public int receiveEnergy(ForgeDirection from, int maxReceive, boolean simulate) {
+
+ return storage.receiveEnergy(maxReceive, simulate);
+ }
+
+ /* IEnergyProvider */
+ @Override
+ public int extractEnergy(ForgeDirection from, int maxExtract, boolean simulate) {
+
+ return storage.extractEnergy(maxExtract, simulate);
+ }
+
+ /* IEnergyReceiver and IEnergyProvider */
+ @Override
+ public int getEnergyStored(ForgeDirection from) {
+
+ return storage.getEnergyStored();
+ }
+
+ @Override
+ public int getMaxEnergyStored(ForgeDirection from) {
+
+ return storage.getMaxEnergyStored();
+ }
+
+}
diff --git a/src/main/java/cofh/api/energy/package-info.java b/src/main/java/cofh/api/energy/package-info.java
new file mode 100644
index 00000000..7379702b
--- /dev/null
+++ b/src/main/java/cofh/api/energy/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHAPIProps.VERSION, owner = "CoFHAPI", provides = "CoFHAPI|energy")
+package cofh.api.energy;
+
+import cofh.api.CoFHAPIProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/api/fluid/ITankContainerBucketable.java b/src/main/java/cofh/api/fluid/ITankContainerBucketable.java
new file mode 100644
index 00000000..8a85b8cd
--- /dev/null
+++ b/src/main/java/cofh/api/fluid/ITankContainerBucketable.java
@@ -0,0 +1,32 @@
+package cofh.api.fluid;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fluids.IFluidHandler;
+
+/**
+ * Extends the IFluidHandler interface to allow manual draining/filling via buckets.
+ *
+ * @author Emy
+ *
+ */
+public interface ITankContainerBucketable extends IFluidHandler {
+
+ /**
+ * Called to determine if the {@link IFluidHandler} should be filled by buckets.
+ *
+ * @param stack
+ * The {@link ItemStack} being used to fill the IFluidHandler
+ * @return True if the IFluidHandler is allowed to be filled with stack
+ */
+ public boolean allowBucketFill(ItemStack stack);
+
+ /**
+ * Called to determine if the {@link IFluidHandler} should be drained by buckets.
+ *
+ * @param stack
+ * The {@link ItemStack} being used to drain the IFluidHandler
+ * @return True if the IFluidHandler is allowed to be drained with stack
+ */
+ public boolean allowBucketDrain(ItemStack stack);
+
+}
diff --git a/src/main/java/cofh/api/fluid/package-info.java b/src/main/java/cofh/api/fluid/package-info.java
new file mode 100644
index 00000000..3a589b01
--- /dev/null
+++ b/src/main/java/cofh/api/fluid/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHAPIProps.VERSION, owner = "CoFHAPI", provides = "CoFHAPI|fluid")
+package cofh.api.fluid;
+
+import cofh.api.CoFHAPIProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/api/inventory/IInventoryConnection.java b/src/main/java/cofh/api/inventory/IInventoryConnection.java
new file mode 100644
index 00000000..99ea70bf
--- /dev/null
+++ b/src/main/java/cofh/api/inventory/IInventoryConnection.java
@@ -0,0 +1,24 @@
+package cofh.api.inventory;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this interface on TileEntities which should connect to item transportation blocks.
+ */
+public interface IInventoryConnection {
+
+ /**
+ * @param from
+ * Side to which a connector would connect
+ * @return DEFAULT if the connector should decide how to connect; FORCE if the connector should always connect; DENY if the connector should never connect.
+ */
+ public ConnectionType canConnectInventory(ForgeDirection from);
+
+ public static enum ConnectionType {
+ DEFAULT, FORCE, DENY;
+
+ public final boolean canConnect = ordinal() != 2;
+ public final boolean forceConnect = ordinal() == 1;
+ }
+
+}
diff --git a/src/main/java/cofh/api/inventory/IInventoryHandler.java b/src/main/java/cofh/api/inventory/IInventoryHandler.java
new file mode 100644
index 00000000..344e8153
--- /dev/null
+++ b/src/main/java/cofh/api/inventory/IInventoryHandler.java
@@ -0,0 +1,82 @@
+package cofh.api.inventory;
+
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this interface on TileEntities which should handle items.
+ *
+ * A reference implementation is provided {@link TileInventoryHandler}.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IInventoryHandler extends IInventoryConnection {
+
+ /**
+ * Insert an ItemStack into the IInventoryHandler, internal distribution is left entirely to the IInventoryHandler. This returns what is remaining of the
+ * original stack - a null return means that the entire stack was accepted!
+ *
+ * @param from
+ * Orientation the item is inserted from.
+ * @param item
+ * ItemStack to be inserted. The size of this stack corresponds to the maximum amount to insert.
+ * @param simulate
+ * If TRUE, the insertion will only be simulated.
+ * @return An ItemStack representing how much is remaining after the item was inserted (or would have been, if simulated) into the container inventory.
+ */
+ ItemStack insertItem(ForgeDirection from, ItemStack item, boolean simulate);
+
+ /**
+ * Extract an ItemStack from an IInventoryHandler, internal distribution is left entirely to the IInventoryHandler. This returns the resulting stack - a
+ * null return means that nothing was extracted!
+ *
+ * @param from
+ * Orientation the item is extracted from.
+ * @param item
+ * ItemStack to be extracted. The size of this stack corresponds to the maximum amount to extract. If this is null, then a null ItemStack should
+ * immediately be returned.
+ * @param simulate
+ * If TRUE, the extraction will only be simulated.
+ * @return An ItemStack representing how much was extracted (or would have been, if simulated) from the container inventory.
+ */
+ ItemStack extractItem(ForgeDirection from, ItemStack item, boolean simulate);
+
+ /**
+ * Extract an ItemStack from an IInventoryHandler, internal distribution is left entirely to the IInventoryHandler. This returns the resulting stack - a
+ * null return means that nothing was extracted!
+ *
+ * @param from
+ * Orientation the item is extracted from.
+ * @param maxExtract
+ * Maximum number of items to extract. (The returned ItemStack should have a stackSize no higher than this.)
+ * @param simulate
+ * If TRUE, the extraction will only be simulated.
+ * @return An ItemStack representing how much was extracted (or would have been, if simulated) from the container inventory.
+ */
+ ItemStack extractItem(ForgeDirection from, int maxExtract, boolean simulate);
+
+ /**
+ * Get the contents of the IInventoryHandler's inventory. This returns a COPY. This should only return non-null ItemStacks, and an empty List if the
+ * inventory has nothing.
+ */
+ List getInventoryContents(ForgeDirection from);
+
+ /**
+ * Get the size (number of internal slots) of the IInventoryHandler's inventory.
+ */
+ int getSizeInventory(ForgeDirection from);
+
+ /**
+ * Returns whether or not the IInventoryHandler's inventory is empty (for a given side).
+ */
+ boolean isEmpty(ForgeDirection from);
+
+ /**
+ * Returns whether or not the IInventoryHandler's inventory is full (for a given side).
+ */
+ boolean isFull(ForgeDirection from);
+
+}
diff --git a/src/main/java/cofh/api/inventory/IInventoryRetainer.java b/src/main/java/cofh/api/inventory/IInventoryRetainer.java
new file mode 100644
index 00000000..df79681e
--- /dev/null
+++ b/src/main/java/cofh/api/inventory/IInventoryRetainer.java
@@ -0,0 +1,12 @@
+package cofh.api.inventory;
+
+/**
+ * Marks a block which will retain its inventory when broken.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IInventoryRetainer {
+
+ // There's nothing else to go here at the moment. Potentially a conditional at a later date.
+}
diff --git a/src/main/java/cofh/api/inventory/package-info.java b/src/main/java/cofh/api/inventory/package-info.java
new file mode 100644
index 00000000..a11a175c
--- /dev/null
+++ b/src/main/java/cofh/api/inventory/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHAPIProps.VERSION, owner = "CoFHAPI", provides = "CoFHAPI|inventory")
+package cofh.api.inventory;
+
+import cofh.api.CoFHAPIProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/api/item/IAugmentItem.java b/src/main/java/cofh/api/item/IAugmentItem.java
new file mode 100644
index 00000000..e17fa8de
--- /dev/null
+++ b/src/main/java/cofh/api/item/IAugmentItem.java
@@ -0,0 +1,29 @@
+package cofh.api.item;
+
+import java.util.Set;
+
+import net.minecraft.item.ItemStack;
+
+public interface IAugmentItem {
+
+ /**
+ * Get the augmentation level for a given Augment and Augment Type.
+ *
+ * @param stack
+ * ItemStack representing the Augment.
+ * @param type
+ * String containing the Augment type name.
+ * @return The Augment level of the stack for the requested type - 0 if it does not affect that attribute.
+ */
+ int getAugmentLevel(ItemStack stack, String type);
+
+ /**
+ * Get the Augment Types for a given Augment. Set ensure that there are no duplicates.
+ *
+ * @param stack
+ * ItemStack representing the Augment.
+ * @return Set of the Augmentation Types. Should return an empty set if there are none (but this would be really stupid to make). DO NOT RETURN NULL.
+ */
+ Set getAugmentTypes(ItemStack stack);
+
+}
diff --git a/src/main/java/cofh/api/item/IEmpowerableItem.java b/src/main/java/cofh/api/item/IEmpowerableItem.java
new file mode 100644
index 00000000..42877110
--- /dev/null
+++ b/src/main/java/cofh/api/item/IEmpowerableItem.java
@@ -0,0 +1,41 @@
+package cofh.api.item;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Implement this interface on Item classes which may be "Empowered" - what that means is completely up to you. This just provides a uniform way of dealing with
+ * them.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IEmpowerableItem {
+
+ /**
+ * Check whether or not a given item is currently in an empowered state.
+ */
+ boolean isEmpowered(ItemStack stack);
+
+ /**
+ * Attempt to set the empowered state of the item.
+ *
+ * @param stack
+ * ItemStack to be empowered/disempowered.
+ * @param state
+ * Desired state.
+ * @return TRUE if the operation was successful, FALSE if it was not.
+ */
+ boolean setEmpoweredState(ItemStack stack, boolean state);
+
+ /**
+ * Callback method for reacting to a state change. Useful in KeyBinding handlers.
+ *
+ * @param player
+ * Player holding the item, if applicable.
+ * @param stack
+ * The item being held.
+ */
+ void onStateChange(EntityPlayer player, ItemStack stack);
+
+}
diff --git a/src/main/java/cofh/api/item/IInventoryContainerItem.java b/src/main/java/cofh/api/item/IInventoryContainerItem.java
new file mode 100644
index 00000000..adadf10a
--- /dev/null
+++ b/src/main/java/cofh/api/item/IInventoryContainerItem.java
@@ -0,0 +1,20 @@
+package cofh.api.item;
+
+import net.minecraft.item.ItemStack;
+
+/**
+ * Implement this interface on Item classes that are themselves inventories.
+ *
+ * A reference implementation is provided {@link ItemInventoryContainer}.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IInventoryContainerItem {
+
+ /**
+ * Get the size of this inventory of this container item.
+ */
+ int getSizeInventory(ItemStack container);
+
+}
diff --git a/src/main/java/cofh/api/item/IMultiModeItem.java b/src/main/java/cofh/api/item/IMultiModeItem.java
new file mode 100644
index 00000000..c236bbfb
--- /dev/null
+++ b/src/main/java/cofh/api/item/IMultiModeItem.java
@@ -0,0 +1,56 @@
+package cofh.api.item;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Implement this interface on Item classes which may be "Empowered" - what that means is completely up to you. This just provides a uniform way of dealing with
+ * them.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IMultiModeItem {
+
+ /**
+ * Get the current mode of an item.
+ */
+ int getMode(ItemStack stack);
+
+ /**
+ * Attempt to set the empowered state of the item.
+ *
+ * @param stack
+ * ItemStack to set the mode on.
+ * @param mode
+ * Desired mode.
+ * @return TRUE if the operation was successful, FALSE if it was not.
+ */
+ boolean setMode(ItemStack stack, int mode);
+
+ /**
+ * Increment the current mode of an item.
+ */
+ boolean incrMode(ItemStack stack);
+
+ /**
+ * Decrement the current mode of an item.
+ */
+ boolean decrMode(ItemStack stack);
+
+ /**
+ * Returns the number of possible modes.
+ */
+ int getNumModes(ItemStack stack);
+
+ /**
+ * Callback method for reacting to a state change. Useful in KeyBinding handlers.
+ *
+ * @param player
+ * Player holding the item, if applicable.
+ * @param stack
+ * The item being held.
+ */
+ void onModeChange(EntityPlayer player, ItemStack stack);
+
+}
diff --git a/src/main/java/cofh/api/item/ISpecialFilterFluid.java b/src/main/java/cofh/api/item/ISpecialFilterFluid.java
new file mode 100644
index 00000000..7e8d3d02
--- /dev/null
+++ b/src/main/java/cofh/api/item/ISpecialFilterFluid.java
@@ -0,0 +1,24 @@
+package cofh.api.item;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.fluids.FluidStack;
+
+/**
+ * Implement this interface on subclasses of Item to change how the item works in Thermal Dynamics Fluiducts filter slots.
+ *
+ * This can be used to create customizable Items which are determined to be "equal" for the purposes of filtering.
+ */
+public interface ISpecialFilterFluid {
+
+ /**
+ * This method is called to find out if the given FluidStack should be matched by the given Filter ItemStack.
+ *
+ * @param filter
+ * ItemStack representing the filter.
+ * @param fluid
+ * FluidStack representing the queried fluid.
+ * @return True if the filter should match the FluidStack. False if the default matching should be used.
+ */
+ public boolean matchesFluid(ItemStack filter, FluidStack fluid);
+
+}
diff --git a/src/main/java/cofh/api/item/ISpecialFilterItem.java b/src/main/java/cofh/api/item/ISpecialFilterItem.java
new file mode 100644
index 00000000..ef9f953d
--- /dev/null
+++ b/src/main/java/cofh/api/item/ISpecialFilterItem.java
@@ -0,0 +1,23 @@
+package cofh.api.item;
+
+import net.minecraft.item.ItemStack;
+
+/**
+ * Implement this interface on subclasses of Item to change how the item works in Thermal Dynamics Itemducts filter slots.
+ *
+ * This can be used to create customizable Items which are determined to be "equal" for the purposes of filtering.
+ */
+public interface ISpecialFilterItem {
+
+ /**
+ * This method is called to find out if the given ItemStack should be matched by the given Filter ItemStack.
+ *
+ * @param filter
+ * ItemStack representing the filter.
+ * @param item
+ * ItemStack representing the queried item.
+ * @return True if the filter should match. False if the default matching should be used.
+ */
+ public boolean matchesItem(ItemStack filter, ItemStack item);
+
+}
diff --git a/src/main/java/cofh/api/item/IToolHammer.java b/src/main/java/cofh/api/item/IToolHammer.java
new file mode 100644
index 00000000..f1b4bb9b
--- /dev/null
+++ b/src/main/java/cofh/api/item/IToolHammer.java
@@ -0,0 +1,44 @@
+package cofh.api.item;
+
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Implement this interface on subclasses of Item to have that item work as a tool for CoFH mods.
+ */
+public interface IToolHammer {
+
+ /**
+ * Called to ensure that the tool can be used.
+ *
+ * @param item
+ * The itemstack for the tool. Not required to match equipped item (e.g., multi-tools that contain other tools)
+ * @param user
+ * The entity using the tool
+ * @param x
+ * X location of the block/tile
+ * @param y
+ * Y location of the block/tile
+ * @param z
+ * Z location of the block/tile
+ * @return True if this tool can be used
+ */
+ boolean isUsable(ItemStack item, EntityLivingBase user, int x, int y, int z);
+
+ /**
+ * Callback for when the tool has been used reactively.
+ *
+ * @param item
+ * The ItemStack for the tool. Not required to match equipped item (e.g., multi-tools that contain other tools)
+ * @param user
+ * The entity using the tool
+ * @param x
+ * X location of the block/tile
+ * @param y
+ * Y location of the block/tile
+ * @param z
+ * Z location of the block/tile
+ */
+ void toolUsed(ItemStack item, EntityLivingBase user, int x, int y, int z);
+
+}
diff --git a/src/main/java/cofh/api/item/package-info.java b/src/main/java/cofh/api/item/package-info.java
new file mode 100644
index 00000000..49608fd1
--- /dev/null
+++ b/src/main/java/cofh/api/item/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHAPIProps.VERSION, owner = "CoFHAPI", provides = "CoFHAPI|item")
+package cofh.api.item;
+
+import cofh.api.CoFHAPIProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/api/modhelpers/EE3Helper.java b/src/main/java/cofh/api/modhelpers/EE3Helper.java
new file mode 100644
index 00000000..30909f25
--- /dev/null
+++ b/src/main/java/cofh/api/modhelpers/EE3Helper.java
@@ -0,0 +1,164 @@
+package cofh.api.modhelpers;
+
+import cofh.lib.util.helpers.ItemHelper;
+import cpw.mods.fml.common.Loader;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+
+public class EE3Helper {
+
+ public static final boolean EE3_PRESENT;
+
+ private static Method addRecipe;
+ private static Method setAsNotLearnable;
+ private static Method setAsNotRecoverable;
+ private static Method addPreAssignedEnergyValue;
+
+ static {
+ boolean found = false;
+ if (Loader.isModLoaded("EE3")) {
+ try {
+ Class> aClass = Class.forName("com.pahimar.ee3.api.exchange.RecipeRegistryProxy");
+ addRecipe = aClass.getDeclaredMethod("addRecipe", Object.class, List.class);
+
+ aClass = Class.forName("com.pahimar.ee3.api.knowledge.AbilityRegistryProxy");
+ setAsNotLearnable = aClass.getDeclaredMethod("setAsNotLearnable", Object.class);
+ setAsNotRecoverable = aClass.getDeclaredMethod("setAsNotRecoverable", Object.class);
+
+ aClass = Class.forName("com.pahimar.ee3.api.exchange.EnergyValueRegistryProxy");
+ addPreAssignedEnergyValue = aClass.getDeclaredMethod("addPreAssignedEnergyValue", Object.class, float.class);
+
+ found = true;
+ } catch (Exception e) {
+ found = false;
+ }
+ }
+ EE3_PRESENT = found;
+ }
+
+ private EE3Helper() {
+
+ }
+
+ public static void addPreAssignedEnergyValue(Object object, float val) throws Throwable {
+
+ if (EE3_PRESENT) {
+ addPreAssignedEnergyValue.invoke(null, object, val);
+ }
+ }
+
+ public static void setAsNotLearnable(Object o) throws Throwable {
+
+ if (EE3_PRESENT) {
+ setAsNotLearnable.invoke(null, o);
+ }
+ }
+
+ public static void setAsNotRecoverable(Object o) throws Throwable {
+
+ if (EE3_PRESENT) {
+ setAsNotRecoverable.invoke(null, o);
+ }
+ }
+
+ private static int gcd(int a, int b) {
+
+ int temp;
+ if (a == 0) {
+ return b;
+ }
+
+ if (b > a) {
+ b = b % a;
+ }
+
+ while (b > 0) {
+ temp = b;
+ b = a % b;
+ a = temp;
+ }
+ return a;
+ }
+
+ private static int[] getRatio(double prob, int i) {
+
+ if (prob == (int) prob) {
+ return new int[] { (int) prob, 1 };
+ }
+ int[] a = reduceRatio(new int[] { (int) Math.ceil(prob * i), i });
+ int[] b = reduceRatio(new int[] { (int) Math.floor(prob * i), i });
+ return a[1] < b[1] ? a : b; // choose smaller denominator
+ }
+
+ private static int[] reduceRatio(int[] v) {
+
+ int k = gcd(v[0], v[1]);
+ v[0] /= k;
+ v[1] /= k;
+ return v;
+ }
+
+ public static void addProbabilisticRecipe(ItemStack output, double prob, ItemStack... input) throws Throwable {
+
+ if (!EE3_PRESENT || output == null || prob == 0) {
+ return;
+ }
+ if (prob == 1.0) {
+ addRecipe(output, (Object[]) input);
+ } else {
+ int[] ratio = getRatio(output.stackSize * prob, 16);
+
+ ArrayList multInput = new ArrayList();
+ for (int i = 0; i < ratio[1]; i++) {
+ for (ItemStack stack : input) {
+ multInput.add(stack.copy());
+ }
+ }
+
+ addRecipe(ItemHelper.cloneStack(output, ratio[0]), multInput.toArray());
+ }
+ }
+
+ public static void addRecipe(Object output, Object... inputs) throws Throwable {
+
+ if (!EE3_PRESENT || output == null) {
+ return;
+ }
+ ArrayList items = new ArrayList(inputs.length);
+
+ for (Object a : inputs) {
+ if (a == null) {
+ continue;
+ }
+ if (a instanceof ItemStack) {
+ ItemStack input = (ItemStack) a;
+
+ int k = input.stackSize;
+ input = input.copy();
+ input.stackSize = 1;
+ for (int i = 0; i < k; i++) {
+ items.add(input.copy());
+ }
+ } else {
+ items.add(a);
+ }
+
+ }
+ if (items.isEmpty()) {
+ return;
+ }
+ addRecipe_do(output, items);
+ }
+
+ private static void addRecipe_do(Object itemStack, List items) throws Throwable {
+
+ if (EE3_PRESENT) {
+ addRecipe.invoke(null, itemStack, items);
+ }
+ }
+
+}
diff --git a/src/main/java/cofh/api/modhelpers/ThaumcraftHelper.java b/src/main/java/cofh/api/modhelpers/ThaumcraftHelper.java
new file mode 100644
index 00000000..e38c3f14
--- /dev/null
+++ b/src/main/java/cofh/api/modhelpers/ThaumcraftHelper.java
@@ -0,0 +1,313 @@
+package cofh.api.modhelpers;
+
+import cpw.mods.fml.common.FMLLog;
+import cpw.mods.fml.common.ModAPIManager;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.oredict.OreDictionary;
+
+public class ThaumcraftHelper {
+
+ // public static final boolean THAUMCRAFT_PRESENT;
+
+ private static Map aspects = null;
+ private static Method registerItem = null;
+ private static Method registerEntity = null;
+ private static Class> AspectList = null;
+ private static Constructor> newAspectList = null;
+ private static Method addAspect = null;
+ private static boolean works = false;
+
+ static {
+ l: try {
+ if (!ModAPIManager.INSTANCE.hasAPI("Thaumcraft|API")) {
+ break l;
+ }
+ Class> Aspect = Class.forName("thaumcraft.api.aspects.Aspect");
+ aspects = (Map) Aspect.getDeclaredField("aspects").get(null);
+ Class> ThaumcraftApi = Class.forName("thaumcraft.api.ThaumcraftApi");
+ AspectList = Class.forName("thaumcraft.api.aspects.AspectList");
+ registerItem = ThaumcraftApi.getDeclaredMethod("registerObjectTag", ItemStack.class, AspectList);
+ Class> EntityTagsNBT = Class.forName("[Lthaumcraft.api.ThaumcraftApi$EntityTagsNBT;");
+ registerEntity = ThaumcraftApi.getDeclaredMethod("registerEntityTag", String.class, AspectList, EntityTagsNBT);
+ addAspect = AspectList.getDeclaredMethod("add", Aspect, int.class);
+ newAspectList = AspectList.getDeclaredConstructor(ItemStack.class);
+ works = true;
+ } catch (Throwable x) {
+ x.printStackTrace();
+ }
+ }
+
+ private ThaumcraftHelper() {
+
+ }
+
+ private static void parseAspects(Object aspectList, String toadd) throws Throwable {
+
+ if (!works) {
+ return;
+ }
+ toadd = toadd.trim();
+ if (!toadd.isEmpty()) {
+ String[] list = toadd.split(",");
+ for (int i = 0, e = list.length; i < e; ++i) {
+ String[] temp = list[i].trim().split(" ", 2);
+ if (temp.length != 2) {
+ FMLLog.bigWarning("[CoFH Thaumcraft Helper] Invalid aspect entry '%s'", list[i]);
+ continue;
+ }
+ String aspect = temp[1].trim();
+ if (aspects.containsKey(aspect)) {
+ addAspect.invoke(aspectList, aspects.get(aspect), Integer.valueOf(temp[0], 10));
+ } else {
+ FMLLog.fine("[CoFH Thaumcraft Helper] %s aspect missing.", temp[1]);
+ }
+ }
+ }
+ }
+
+ /**
+ * @param entity
+ * The string ID of the entity
+ * @param toadd
+ * A list of comma-separated aspects for scanning the entity
+ *
+ * entry format : '\d+ .+
'
+ *
+ * @throws Throwable
+ * Any errors caused by reflection
+ */
+ public static void parseAspects(String entity, String toadd) throws Throwable {
+
+ if (!works) {
+ return;
+ }
+ Object aspectList = AspectList.newInstance();
+
+ parseAspects(aspectList, toadd);
+
+ registerEntity.invoke(null, entity, aspectList, null);
+ }
+
+ /**
+ * @param item
+ * The {@link ItemStack} to add aspects to.
+ * @param toadd
+ * A list of comma-separated aspects for scanning the ItemStack
+ *
+ * entry format : '\d+ .+
'
+ *
+ * @param craftedAspects
+ * True if the item should inherit aspects from its crafting ingredients
+ * @throws Throwable
+ * Any errors caused by reflection
+ */
+ public static void parseAspects(ItemStack item, String toadd, boolean craftedAspects) throws Throwable {
+
+ if (!works) {
+ return;
+ }
+ Object aspectList;
+ if (craftedAspects) {
+ aspectList = newAspectList.newInstance(item);
+ } else {
+ aspectList = AspectList.newInstance();
+ }
+
+ parseAspects(aspectList, toadd);
+
+ registerItem.invoke(null, item, aspectList);
+ }
+
+ /**
+ * @param item
+ * The {@link ItemStack} to add aspects to.
+ *
+ * Will inherit aspects .
+ *
+ * @param toadd
+ * A list of comma-separated aspects for scanning the ItemStack
+ *
+ * entry format : '\d+ .+
'
+ *
+ * @throws Throwable
+ * Any errors caused by reflection
+ */
+ public static void parseAspects(ItemStack item, String toadd) throws Throwable {
+
+ parseAspects(item, toadd, true);
+ }
+
+ /* ITEMS */
+ /**
+ * @param item
+ * The {@link Item} to add aspects to.
+ * @param meta
+ * The metadata of the Item
+ * @param toadd
+ * A list of comma-separated aspects for scanning the Item
+ *
+ * entry format : '\d+ .+
'
+ *
+ * @param craftedAspects
+ * True if the item should inherit aspects from its crafting ingredients
+ * @throws Throwable
+ * Any errors caused by reflection
+ */
+ public static void parseAspects(Item item, int meta, String toadd, boolean craftedAspects) throws Throwable {
+
+ parseAspects(new ItemStack(item, 1, meta), toadd, craftedAspects);
+ }
+
+ /**
+ * @param item
+ * The {@link Item} to add aspects to.
+ *
+ * Will apply to all metadatas .
+ *
+ * @param toadd
+ * A list of comma-separated aspects for scanning the Item
+ *
+ * entry format : '\d+ .+
'
+ *
+ * @param craftedAspects
+ * True if the item should inherit aspects from its crafting ingredients
+ * @throws Throwable
+ * Any errors caused by reflection
+ */
+ public static void parseAspects(Item item, String toadd, boolean craftedAspects) throws Throwable {
+
+ parseAspects(item, OreDictionary.WILDCARD_VALUE, toadd, craftedAspects);
+ }
+
+ /**
+ * @param item
+ * The {@link Item} to add aspects to.
+ *
+ * Will inherit aspects .
+ *
+ * @param meta
+ * The metadata of the Item
+ * @param toadd
+ * A list of comma-separated aspects for scanning the Item
+ *
+ * entry format : '\d+ .+
'
+ *
+ * @throws Throwable
+ * Any errors caused by reflection
+ */
+ public static void parseAspects(Item item, int meta, String toadd) throws Throwable {
+
+ parseAspects(item, meta, toadd, true);
+ }
+
+ /**
+ * @param item
+ * The {@link Item} to add aspects to.
+ *
+ * Will apply to all metadatas .
+ * Will inherit aspects .
+ *
+ * @param toadd
+ * A list of comma-separated aspects for scanning the Item
+ *
+ * entry format : '\d+ .+
'
+ *
+ * @throws Throwable
+ * Any errors caused by reflection
+ */
+ public static void parseAspects(Item item, String toadd) throws Throwable {
+
+ parseAspects(item, OreDictionary.WILDCARD_VALUE, toadd, true);
+ }
+
+ /* BLOCKS */
+ /**
+ * @param item
+ * The {@link Block} to add aspects to.
+ * @param meta
+ * The metadata of the Block
+ * @param toadd
+ * A list of comma-separated aspects for scanning the Block
+ *
+ * entry format : '\d+ .+
'
+ *
+ * @param craftedAspects
+ * True if the item should inherit aspects from its crafting ingredients
+ * @throws Throwable
+ * Any errors caused by reflection
+ */
+ public static void parseAspects(Block item, int meta, String toadd, boolean craftedAspects) throws Throwable {
+
+ parseAspects(new ItemStack(item, 1, meta), toadd, craftedAspects);
+ }
+
+ /**
+ * @param item
+ * The {@link Block} to add aspects to.
+ *
+ * Will apply to all metadatas .
+ *
+ * @param toadd
+ * A list of comma-separated aspects for scanning the Block
+ *
+ * entry format : '\d+ .+
'
+ *
+ * @param craftedAspects
+ * True if the item should inherit aspects from its crafting ingredients
+ * @throws Throwable
+ * Any errors caused by reflection
+ */
+ public static void parseAspects(Block item, String toadd, boolean craftedAspects) throws Throwable {
+
+ parseAspects(item, OreDictionary.WILDCARD_VALUE, toadd, craftedAspects);
+ }
+
+ /**
+ * @param item
+ * The {@link Block} to add aspects to.
+ *
+ * Will inherit aspects .
+ *
+ * @param meta
+ * The metadata of the Block
+ * @param toadd
+ * A list of comma-separated aspects for scanning the Block
+ *
+ * entry format : '\d+ .+
'
+ *
+ * @throws Throwable
+ * Any errors caused by reflection
+ */
+ public static void parseAspects(Block item, int meta, String toadd) throws Throwable {
+
+ parseAspects(item, meta, toadd, true);
+ }
+
+ /**
+ * @param item
+ * The {@link Block} to add aspects to.
+ *
+ * Will apply to all metadatas .
+ * Will inherit aspects .
+ *
+ * @param toadd
+ * A list of comma-separated aspects for scanning the Block
+ *
+ * entry format : '\d+ .+
'
+ *
+ * @throws Throwable
+ * Any errors caused by reflection
+ */
+ public static void parseAspects(Block item, String toadd) throws Throwable {
+
+ parseAspects(item, OreDictionary.WILDCARD_VALUE, toadd, true);
+ }
+
+}
diff --git a/src/main/java/cofh/api/modhelpers/ThermalExpansionHelper.java b/src/main/java/cofh/api/modhelpers/ThermalExpansionHelper.java
new file mode 100644
index 00000000..4fc7518c
--- /dev/null
+++ b/src/main/java/cofh/api/modhelpers/ThermalExpansionHelper.java
@@ -0,0 +1,457 @@
+package cofh.api.modhelpers;
+
+import cpw.mods.fml.common.event.FMLInterModComms;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraftforge.fluids.FluidStack;
+
+/**
+ * The purpose of this class is to show how to use and provide an interface for Thermal Expansion's IMC Recipe manipulation.
+ *
+ * It is not the only way to add recipes to TE, but it is BY FAR the safest. Please use it.
+ *
+ * @author King Lemming
+ *
+ */
+public class ThermalExpansionHelper {
+
+ private ThermalExpansionHelper() {
+
+ }
+
+ /** MACHINES */
+
+ /* Furnace */
+ public static void addFurnaceRecipe(int energy, ItemStack input, ItemStack output) {
+
+ if (input == null || output == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setInteger("energy", energy);
+ toSend.setTag("input", new NBTTagCompound());
+ toSend.setTag("output", new NBTTagCompound());
+
+ input.writeToNBT(toSend.getCompoundTag("input"));
+ output.writeToNBT(toSend.getCompoundTag("output"));
+ FMLInterModComms.sendMessage("ThermalExpansion", "FurnaceRecipe", toSend);
+ }
+
+ public static void removeFurnaceRecipe(ItemStack input) {
+
+ if (input == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setTag("input", new NBTTagCompound());
+
+ input.writeToNBT(toSend.getCompoundTag("input"));
+ FMLInterModComms.sendMessage("ThermalExpansion", "RemoveFurnaceRecipe", toSend);
+ }
+
+ /* Pulverizer */
+ public static void addPulverizerRecipe(int energy, ItemStack input, ItemStack primaryOutput) {
+
+ addPulverizerRecipe(energy, input, primaryOutput, null, 0);
+ }
+
+ public static void addPulverizerRecipe(int energy, ItemStack input, ItemStack primaryOutput, ItemStack secondaryOutput) {
+
+ addPulverizerRecipe(energy, input, primaryOutput, secondaryOutput, 100);
+ }
+
+ public static void addPulverizerRecipe(int energy, ItemStack input, ItemStack primaryOutput, ItemStack secondaryOutput, int secondaryChance) {
+
+ if (input == null || primaryOutput == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setInteger("energy", energy);
+ toSend.setTag("input", new NBTTagCompound());
+ toSend.setTag("primaryOutput", new NBTTagCompound());
+
+ if (secondaryOutput != null) {
+ toSend.setTag("secondaryOutput", new NBTTagCompound());
+ }
+
+ input.writeToNBT(toSend.getCompoundTag("input"));
+ primaryOutput.writeToNBT(toSend.getCompoundTag("primaryOutput"));
+
+ if (secondaryOutput != null) {
+ secondaryOutput.writeToNBT(toSend.getCompoundTag("secondaryOutput"));
+ toSend.setInteger("secondaryChance", secondaryChance);
+ }
+
+ FMLInterModComms.sendMessage("ThermalExpansion", "PulverizerRecipe", toSend);
+ }
+
+ public static void removePulverizerRecipe(ItemStack input) {
+
+ if (input == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setTag("input", new NBTTagCompound());
+
+ input.writeToNBT(toSend.getCompoundTag("input"));
+ FMLInterModComms.sendMessage("ThermalExpansion", "RemovePulverizerRecipe", toSend);
+ }
+
+ /* Sawmill */
+ public static void addSawmillRecipe(int energy, ItemStack input, ItemStack primaryOutput) {
+
+ addSawmillRecipe(energy, input, primaryOutput, null, 0);
+ }
+
+ public static void addSawmillRecipe(int energy, ItemStack input, ItemStack primaryOutput, ItemStack secondaryOutput) {
+
+ addSawmillRecipe(energy, input, primaryOutput, secondaryOutput, 100);
+ }
+
+ public static void addSawmillRecipe(int energy, ItemStack input, ItemStack primaryOutput, ItemStack secondaryOutput, int secondaryChance) {
+
+ if (input == null || primaryOutput == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setInteger("energy", energy);
+ toSend.setTag("input", new NBTTagCompound());
+ toSend.setTag("primaryOutput", new NBTTagCompound());
+
+ if (secondaryOutput != null) {
+ toSend.setTag("secondaryOutput", new NBTTagCompound());
+ }
+
+ input.writeToNBT(toSend.getCompoundTag("input"));
+ primaryOutput.writeToNBT(toSend.getCompoundTag("primaryOutput"));
+
+ if (secondaryOutput != null) {
+ secondaryOutput.writeToNBT(toSend.getCompoundTag("secondaryOutput"));
+ toSend.setInteger("secondaryChance", secondaryChance);
+ }
+
+ FMLInterModComms.sendMessage("ThermalExpansion", "SawmillRecipe", toSend);
+ }
+
+ public static void removeSawmillRecipe(ItemStack input) {
+
+ if (input == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setTag("input", new NBTTagCompound());
+
+ input.writeToNBT(toSend.getCompoundTag("input"));
+ FMLInterModComms.sendMessage("ThermalExpansion", "RemoveSawmillRecipe", toSend);
+ }
+
+ /* Smelter */
+ public static void addSmelterRecipe(int energy, ItemStack primaryInput, ItemStack secondaryInput, ItemStack primaryOutput) {
+
+ addSmelterRecipe(energy, primaryInput, secondaryInput, primaryOutput, null, 0);
+ }
+
+ public static void addSmelterRecipe(int energy, ItemStack primaryInput, ItemStack secondaryInput, ItemStack primaryOutput, ItemStack secondaryOutput) {
+
+ addSmelterRecipe(energy, primaryInput, secondaryInput, primaryOutput, secondaryOutput, 100);
+ }
+
+ public static void addSmelterRecipe(int energy, ItemStack primaryInput, ItemStack secondaryInput, ItemStack primaryOutput, ItemStack secondaryOutput,
+ int secondaryChance) {
+
+ if (primaryInput == null || secondaryInput == null || primaryOutput == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setInteger("energy", energy);
+ toSend.setTag("primaryInput", new NBTTagCompound());
+ toSend.setTag("secondaryInput", new NBTTagCompound());
+ toSend.setTag("primaryOutput", new NBTTagCompound());
+
+ if (secondaryOutput != null) {
+ toSend.setTag("secondaryOutput", new NBTTagCompound());
+ }
+
+ primaryInput.writeToNBT(toSend.getCompoundTag("primaryInput"));
+ secondaryInput.writeToNBT(toSend.getCompoundTag("secondaryInput"));
+ primaryOutput.writeToNBT(toSend.getCompoundTag("primaryOutput"));
+
+ if (secondaryOutput != null) {
+ secondaryOutput.writeToNBT(toSend.getCompoundTag("secondaryOutput"));
+ toSend.setInteger("secondaryChance", secondaryChance);
+ }
+ FMLInterModComms.sendMessage("ThermalExpansion", "SmelterRecipe", toSend);
+ }
+
+ public static void removeSmelterRecipe(ItemStack primaryInput, ItemStack secondaryInput) {
+
+ if (primaryInput == null || secondaryInput == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setTag("primaryInput", new NBTTagCompound());
+ toSend.setTag("secondaryInput", new NBTTagCompound());
+
+ primaryInput.writeToNBT(toSend.getCompoundTag("primaryInput"));
+ secondaryInput.writeToNBT(toSend.getCompoundTag("secondaryInput"));
+ FMLInterModComms.sendMessage("ThermalExpansion", "RemoveSmelterRecipe", toSend);
+ }
+
+ /**
+ * Use this to register an Ore TYPE as a "Blast" recipe - it will require Pyrotheum Dust to smelt. Do not add the prefix. This is an opt-in for ores which
+ * do NOT have vanilla furnace recipes.
+ *
+ * Ex: "Steel" or "ElectrumFlux", not "dustSteel" or "dustElectrumFlux"
+ *
+ * @param oreType
+ */
+ public static void addSmelterBlastOre(String oreType) {
+
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setString("oreType", oreType);
+
+ FMLInterModComms.sendMessage("ThermalExpansion", "SmelterBlastOreType", toSend);
+ }
+
+ /* Crucible */
+ public static void addCrucibleRecipe(int energy, ItemStack input, FluidStack output) {
+
+ if (input == null || output == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setInteger("energy", energy);
+ toSend.setTag("input", new NBTTagCompound());
+ toSend.setTag("output", new NBTTagCompound());
+
+ input.writeToNBT(toSend.getCompoundTag("input"));
+ output.writeToNBT(toSend.getCompoundTag("output"));
+
+ FMLInterModComms.sendMessage("ThermalExpansion", "CrucibleRecipe", toSend);
+ }
+
+ public static void removeCrucibleRecipe(ItemStack input) {
+
+ if (input == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setTag("input", new NBTTagCompound());
+
+ input.writeToNBT(toSend.getCompoundTag("input"));
+ FMLInterModComms.sendMessage("ThermalExpansion", "RemoveCrucibleRecipe", toSend);
+ }
+
+ /* Transposer */
+ public static void addTransposerFill(int energy, ItemStack input, ItemStack output, FluidStack fluid, boolean reversible) {
+
+ if (input == null || output == null || fluid == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setInteger("energy", energy);
+ toSend.setTag("input", new NBTTagCompound());
+ toSend.setTag("output", new NBTTagCompound());
+ toSend.setTag("fluid", new NBTTagCompound());
+
+ input.writeToNBT(toSend.getCompoundTag("input"));
+ output.writeToNBT(toSend.getCompoundTag("output"));
+ toSend.setBoolean("reversible", reversible);
+ fluid.writeToNBT(toSend.getCompoundTag("fluid"));
+
+ FMLInterModComms.sendMessage("ThermalExpansion", "TransposerFillRecipe", toSend);
+ }
+
+ public static void addTransposerExtract(int energy, ItemStack input, ItemStack output, FluidStack fluid, int chance, boolean reversible) {
+
+ if (input == null || output == null || fluid == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setInteger("energy", energy);
+ toSend.setTag("input", new NBTTagCompound());
+ toSend.setTag("output", new NBTTagCompound());
+ toSend.setTag("fluid", new NBTTagCompound());
+
+ input.writeToNBT(toSend.getCompoundTag("input"));
+ output.writeToNBT(toSend.getCompoundTag("output"));
+ toSend.setBoolean("reversible", reversible);
+ toSend.setInteger("chance", chance);
+ fluid.writeToNBT(toSend.getCompoundTag("fluid"));
+
+ FMLInterModComms.sendMessage("ThermalExpansion", "TransposerExtractRecipe", toSend);
+ }
+
+ public static void removeTransposerFill(ItemStack input, FluidStack fluid) {
+
+ if (input == null || fluid == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setTag("input", new NBTTagCompound());
+ toSend.setTag("fluid", new NBTTagCompound());
+
+ input.writeToNBT(toSend.getCompoundTag("input"));
+ fluid.writeToNBT(toSend.getCompoundTag("fluid"));
+ FMLInterModComms.sendMessage("ThermalExpansion", "RemoveTransposerFillRecipe", toSend);
+ }
+
+ public static void removeTransposerExtract(ItemStack input) {
+
+ if (input == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setTag("input", new NBTTagCompound());
+
+ input.writeToNBT(toSend.getCompoundTag("input"));
+ FMLInterModComms.sendMessage("ThermalExpansion", "RemoveTransposerExtractRecipe", toSend);
+ }
+
+ /* Charger */
+ public static void addChargerRecipe(int energy, ItemStack input, ItemStack output) {
+
+ if (input == null || output == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setInteger("energy", energy);
+ toSend.setTag("input", new NBTTagCompound());
+ toSend.setTag("output", new NBTTagCompound());
+
+ input.writeToNBT(toSend.getCompoundTag("input"));
+ output.writeToNBT(toSend.getCompoundTag("output"));
+ FMLInterModComms.sendMessage("ThermalExpansion", "ChargerRecipe", toSend);
+ }
+
+ public static void removeChargerRecipe(ItemStack input) {
+
+ if (input == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setTag("input", new NBTTagCompound());
+
+ input.writeToNBT(toSend.getCompoundTag("input"));
+ FMLInterModComms.sendMessage("ThermalExpansion", "RemoveChargerRecipe", toSend);
+ }
+
+ /* Insolator */
+ public static void addInsolatorRecipe(int energy, ItemStack primaryInput, ItemStack secondaryInput, ItemStack primaryOutput) {
+
+ addInsolatorRecipe(energy, primaryInput, secondaryInput, primaryOutput, null, 0);
+ }
+
+ public static void addInsolatorRecipe(int energy, ItemStack primaryInput, ItemStack secondaryInput, ItemStack primaryOutput, ItemStack secondaryOutput) {
+
+ addInsolatorRecipe(energy, primaryInput, secondaryInput, primaryOutput, secondaryOutput, 100);
+ }
+
+ public static void addInsolatorRecipe(int energy, ItemStack primaryInput, ItemStack secondaryInput, ItemStack primaryOutput, ItemStack secondaryOutput,
+ int secondaryChance) {
+
+ if (primaryInput == null || secondaryInput == null || primaryOutput == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setInteger("energy", energy);
+ toSend.setTag("primaryInput", new NBTTagCompound());
+ toSend.setTag("secondaryInput", new NBTTagCompound());
+ toSend.setTag("primaryOutput", new NBTTagCompound());
+
+ if (secondaryOutput != null) {
+ toSend.setTag("secondaryOutput", new NBTTagCompound());
+ }
+
+ primaryInput.writeToNBT(toSend.getCompoundTag("primaryInput"));
+ secondaryInput.writeToNBT(toSend.getCompoundTag("secondaryInput"));
+ primaryOutput.writeToNBT(toSend.getCompoundTag("primaryOutput"));
+
+ if (secondaryOutput != null) {
+ secondaryOutput.writeToNBT(toSend.getCompoundTag("secondaryOutput"));
+ toSend.setInteger("secondaryChance", secondaryChance);
+ }
+ FMLInterModComms.sendMessage("ThermalExpansion", "InsolatorRecipe", toSend);
+ }
+
+ public static void removeInsolatorRecipe(ItemStack primaryInput, ItemStack secondaryInput) {
+
+ if (primaryInput == null || secondaryInput == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setTag("primaryInput", new NBTTagCompound());
+ toSend.setTag("secondaryInput", new NBTTagCompound());
+
+ primaryInput.writeToNBT(toSend.getCompoundTag("primaryInput"));
+ secondaryInput.writeToNBT(toSend.getCompoundTag("secondaryInput"));
+ FMLInterModComms.sendMessage("ThermalExpansion", "RemoveInsolatorRecipe", toSend);
+ }
+
+ /** DYNAMOS */
+
+ /* Magmatic */
+ public static void addMagmaticFuel(String fluidName, int energy) {
+
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setString("fluidName", fluidName);
+ toSend.setInteger("energy", energy);
+
+ FMLInterModComms.sendMessage("ThermalExpansion", "MagmaticFuel", toSend);
+ }
+
+ /* Compression */
+ public static void addCompressionFuel(String fluidName, int energy) {
+
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setString("fluidName", fluidName);
+ toSend.setInteger("energy", energy);
+
+ FMLInterModComms.sendMessage("ThermalExpansion", "CompressionFuel", toSend);
+ }
+
+ /* Reactant */
+ public static void addReactantFuel(String fluidName, int energy) {
+
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setString("fluidName", fluidName);
+ toSend.setInteger("energy", energy);
+
+ FMLInterModComms.sendMessage("ThermalExpansion", "ReactantFuel", toSend);
+ }
+
+ /* Coolants */
+ public static void addCoolant(String fluidName, int energy) {
+
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setString("fluidName", fluidName);
+ toSend.setInteger("energy", energy);
+
+ FMLInterModComms.sendMessage("ThermalExpansion", "Coolant", toSend);
+ }
+
+}
diff --git a/src/main/java/cofh/api/modhelpers/ThermalFoundationHelper.java b/src/main/java/cofh/api/modhelpers/ThermalFoundationHelper.java
new file mode 100644
index 00000000..05ba4791
--- /dev/null
+++ b/src/main/java/cofh/api/modhelpers/ThermalFoundationHelper.java
@@ -0,0 +1,49 @@
+package cofh.api.modhelpers;
+
+import cpw.mods.fml.common.event.FMLInterModComms;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+
+/**
+ * The purpose of this class is to show how to use and provide an interface for Thermal Foundation's IMC Lexicon Blacklist manipulation.
+ *
+ * This is really the only safe way to do this. Please do not attempt any direct Lexicon manipulation.
+ *
+ * @author King Lemming
+ *
+ */
+public class ThermalFoundationHelper {
+
+ private ThermalFoundationHelper() {
+
+ }
+
+ /* Lexicon */
+ public static void addBlacklistEntry(ItemStack entry) {
+
+ if (entry == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setTag("entry", new NBTTagCompound());
+
+ entry.writeToNBT(toSend.getCompoundTag("entry"));
+ FMLInterModComms.sendMessage("ThermalFoundation", "AddLexiconBlacklistEntry", toSend);
+ }
+
+ public static void removeBlacklistEntry(ItemStack entry) {
+
+ if (entry == null) {
+ return;
+ }
+ NBTTagCompound toSend = new NBTTagCompound();
+
+ toSend.setTag("entry", new NBTTagCompound());
+
+ entry.writeToNBT(toSend.getCompoundTag("entry"));
+ FMLInterModComms.sendMessage("ThermalFoundation", "RemoveLexiconBlacklistEntry", toSend);
+ }
+
+}
diff --git a/src/main/java/cofh/api/modhelpers/package-info.java b/src/main/java/cofh/api/modhelpers/package-info.java
new file mode 100644
index 00000000..10329a71
--- /dev/null
+++ b/src/main/java/cofh/api/modhelpers/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHAPIProps.VERSION, owner = "CoFHAPI", provides = "CoFHAPI|modhelpers")
+package cofh.api.modhelpers;
+
+import cofh.api.CoFHAPIProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/api/package-info.java b/src/main/java/cofh/api/package-info.java
new file mode 100644
index 00000000..08ff5fcb
--- /dev/null
+++ b/src/main/java/cofh/api/package-info.java
@@ -0,0 +1,9 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHAPIProps.VERSION, owner = "CoFHLib", provides = "CoFHAPI")
+package cofh.api;
+
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/api/tileentity/IAugmentable.java b/src/main/java/cofh/api/tileentity/IAugmentable.java
new file mode 100644
index 00000000..5a99c598
--- /dev/null
+++ b/src/main/java/cofh/api/tileentity/IAugmentable.java
@@ -0,0 +1,28 @@
+package cofh.api.tileentity;
+
+import net.minecraft.item.ItemStack;
+
+/**
+ * Implemented on objects which support Augments.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IAugmentable {
+
+ /**
+ * Attempt to reconfigure the tile based on the Augmentations present.
+ */
+ void installAugments();
+
+ /**
+ * Returns an array of the Augment slots for this Tile Entity.
+ */
+ ItemStack[] getAugmentSlots();
+
+ /**
+ * Returns a status array for the Augmentations installed in the Tile Entity.
+ */
+ boolean[] getAugmentStatus();
+
+}
diff --git a/src/main/java/cofh/api/tileentity/IEnergyInfo.java b/src/main/java/cofh/api/tileentity/IEnergyInfo.java
new file mode 100644
index 00000000..6277a34c
--- /dev/null
+++ b/src/main/java/cofh/api/tileentity/IEnergyInfo.java
@@ -0,0 +1,33 @@
+package cofh.api.tileentity;
+
+/**
+ * Implement this interface on objects which can report information about their energy usage.
+ *
+ * This is used for reporting purposes - Energy transactions are handled through IEnergyHandler!
+ *
+ * @author King Lemming
+ *
+ */
+public interface IEnergyInfo {
+
+ /**
+ * Returns energy usage/generation per tick (RF/t).
+ */
+ int getInfoEnergyPerTick();
+
+ /**
+ * Returns maximum energy usage/generation per tick (RF/t).
+ */
+ int getInfoMaxEnergyPerTick();
+
+ /**
+ * Returns energy stored (RF).
+ */
+ int getInfoEnergyStored();
+
+ /**
+ * Returns maximum energy stored (RF).
+ */
+ int getInfoMaxEnergyStored();
+
+}
diff --git a/src/main/java/cofh/api/tileentity/IPortableData.java b/src/main/java/cofh/api/tileentity/IPortableData.java
new file mode 100644
index 00000000..81d42964
--- /dev/null
+++ b/src/main/java/cofh/api/tileentity/IPortableData.java
@@ -0,0 +1,33 @@
+package cofh.api.tileentity;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.nbt.NBTTagCompound;
+
+/**
+ * Implement this interface on Tile Entities which can write a limited amount of data about themselves.
+ *
+ * This is typically for the purposes of being transferred to a similar tile entity.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IPortableData {
+
+ /**
+ * Data identifier of the Tile Entity/Block. Used for display as well as verification purposes. Tiles with completely interchangeable data should return the
+ * same type.
+ *
+ * @return
+ */
+ String getDataType();
+
+ /**
+ * Read the data from a tag. The player object exists because this should always be called via player interaction!
+ */
+ void readPortableData(EntityPlayer player, NBTTagCompound tag);
+
+ /**
+ * Write the data to a tag. The player object exists because this should always be called via player interaction!
+ */
+ void writePortableData(EntityPlayer player, NBTTagCompound tag);
+}
diff --git a/src/main/java/cofh/api/tileentity/IReconfigurableFacing.java b/src/main/java/cofh/api/tileentity/IReconfigurableFacing.java
new file mode 100644
index 00000000..e6972091
--- /dev/null
+++ b/src/main/java/cofh/api/tileentity/IReconfigurableFacing.java
@@ -0,0 +1,39 @@
+package cofh.api.tileentity;
+
+/**
+ * Implement this interface on Tile Entities which allow for reconfiguration of their facing.
+ *
+ * Coordination with the containing block is required.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IReconfigurableFacing {
+
+ /**
+ * Returns the current facing of the block.
+ */
+ int getFacing();
+
+ /**
+ * Returns whether or not the block's face can be aligned with the Y Axis.
+ */
+ boolean allowYAxisFacing();
+
+ /**
+ * Attempt to rotate the block. Arbitrary based on implementation.
+ *
+ * @return True if rotation was successful, false otherwise.
+ */
+ boolean rotateBlock();
+
+ /**
+ * Set the facing of the block.
+ *
+ * @param side
+ * The side to set the facing to.
+ * @return True if the facing was set, false otherwise.
+ */
+ boolean setFacing(int side);
+
+}
diff --git a/src/main/java/cofh/api/tileentity/IReconfigurableSides.java b/src/main/java/cofh/api/tileentity/IReconfigurableSides.java
new file mode 100644
index 00000000..e3a708dd
--- /dev/null
+++ b/src/main/java/cofh/api/tileentity/IReconfigurableSides.java
@@ -0,0 +1,54 @@
+package cofh.api.tileentity;
+
+/**
+ * Implement this interface on Tile Entities which allow for reconfiguration of their sides.
+ *
+ * Coordination with the containing block is required.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IReconfigurableSides {
+
+ /**
+ * Decrement the config for a given side.
+ *
+ * @param side
+ * The side to decrement.
+ * @return True if config was changed, false otherwise.
+ */
+ boolean decrSide(int side);
+
+ /**
+ * Increment the config for a given side.
+ *
+ * @param side
+ * The side to decrement.
+ * @return True if config was changed, false otherwise.
+ */
+ boolean incrSide(int side);
+
+ /**
+ * Set the config for a given side.
+ *
+ * @param side
+ * The side to set.
+ * @param config
+ * The config value to use.
+ * @return True of config was set, false otherwise.
+ */
+ boolean setSide(int side, int config);
+
+ /**
+ * Reset configs on all sides to their base values.
+ *
+ * @return True if reset was successful, false otherwise.
+ */
+ boolean resetSides();
+
+ /**
+ * Returns the number of possible config settings for a given side.
+ */
+ int getNumConfig(int side);
+
+}
diff --git a/src/main/java/cofh/api/tileentity/IRedstoneCache.java b/src/main/java/cofh/api/tileentity/IRedstoneCache.java
new file mode 100644
index 00000000..1d873e53
--- /dev/null
+++ b/src/main/java/cofh/api/tileentity/IRedstoneCache.java
@@ -0,0 +1,17 @@
+package cofh.api.tileentity;
+
+/**
+ * Implement this interface on Tile Entities which cache their redstone status.
+ *
+ * Note that {@link IRedstoneControl} is an extension of this.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IRedstoneCache {
+
+ void setPowered(boolean isPowered);
+
+ boolean isPowered();
+
+}
diff --git a/src/main/java/cofh/api/tileentity/IRedstoneControl.java b/src/main/java/cofh/api/tileentity/IRedstoneControl.java
new file mode 100644
index 00000000..f868affb
--- /dev/null
+++ b/src/main/java/cofh/api/tileentity/IRedstoneControl.java
@@ -0,0 +1,57 @@
+package cofh.api.tileentity;
+
+/**
+ * Implement this interface on Tile Entities which have Redstone Control functionality. This means that a tile can be set to ignore redstone entirely, or
+ * respond to a low or high redstone state.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IRedstoneControl extends IRedstoneCache {
+
+ public static enum ControlMode {
+ DISABLED(true), LOW(false), HIGH(true);
+
+ private final boolean state;
+
+ private ControlMode(boolean state) {
+
+ this.state = state;
+ }
+
+ public boolean isDisabled() {
+
+ return this == DISABLED;
+ }
+
+ public boolean isLow() {
+
+ return this == LOW;
+ }
+
+ public boolean isHigh() {
+
+ return this == HIGH;
+ }
+
+ public boolean getState() {
+
+ return state;
+ }
+
+ public static ControlMode stepForward(ControlMode curControl) {
+
+ return curControl == DISABLED ? LOW : curControl == HIGH ? DISABLED : HIGH;
+ }
+
+ public static ControlMode stepBackward(ControlMode curControl) {
+
+ return curControl == DISABLED ? HIGH : curControl == HIGH ? LOW : DISABLED;
+ }
+ }
+
+ void setControl(ControlMode control);
+
+ ControlMode getControl();
+
+}
diff --git a/src/main/java/cofh/api/tileentity/ISecurable.java b/src/main/java/cofh/api/tileentity/ISecurable.java
new file mode 100644
index 00000000..fd29f1e6
--- /dev/null
+++ b/src/main/java/cofh/api/tileentity/ISecurable.java
@@ -0,0 +1,64 @@
+package cofh.api.tileentity;
+
+import com.mojang.authlib.GameProfile;
+
+import net.minecraft.entity.player.EntityPlayer;
+
+/**
+ * Implement this interface on Tile Entities which can have access restrictions.
+ *
+ * @author King Lemming
+ *
+ */
+public interface ISecurable {
+
+ /**
+ * Enum for Access Modes - Restricted is Friends Only, Private is Owner only.
+ *
+ * @author King Lemming
+ *
+ */
+ public static enum AccessMode {
+ PUBLIC, RESTRICTED, PRIVATE;
+
+ public boolean isPublic() {
+
+ return this == PUBLIC;
+ }
+
+ public boolean isRestricted() {
+
+ return this == RESTRICTED;
+ }
+
+ public boolean isPrivate() {
+
+ return this == PRIVATE;
+ }
+
+ public static AccessMode stepForward(AccessMode curAccess) {
+
+ return curAccess == PUBLIC ? RESTRICTED : curAccess == PRIVATE ? PUBLIC : PRIVATE;
+ }
+
+ public static AccessMode stepBackward(AccessMode curAccess) {
+
+ return curAccess == PUBLIC ? PRIVATE : curAccess == PRIVATE ? RESTRICTED : PUBLIC;
+ }
+ }
+
+ boolean setAccess(AccessMode access);
+
+ boolean setOwnerName(String name);
+
+ boolean setOwner(GameProfile name);
+
+ AccessMode getAccess();
+
+ String getOwnerName();
+
+ GameProfile getOwner();
+
+ boolean canPlayerAccess(EntityPlayer player);
+
+}
diff --git a/src/main/java/cofh/api/tileentity/ISidedTexture.java b/src/main/java/cofh/api/tileentity/ISidedTexture.java
new file mode 100644
index 00000000..20be6dd6
--- /dev/null
+++ b/src/main/java/cofh/api/tileentity/ISidedTexture.java
@@ -0,0 +1,25 @@
+package cofh.api.tileentity;
+
+import net.minecraft.util.IIcon;
+
+/**
+ * Implement this interface on Tile Entities which can change their block's texture based on the current render pass. The block must defer the call to its Tile
+ * Entity.
+ *
+ * @author Zeldo Kavira
+ *
+ */
+public interface ISidedTexture {
+
+ /**
+ * Returns the icon to use for a given side and render pass.
+ *
+ * @param side
+ * Block side to get the texture for.
+ * @param pass
+ * Render pass.
+ * @return The icon to use.
+ */
+ IIcon getTexture(int side, int pass);
+
+}
diff --git a/src/main/java/cofh/api/tileentity/ITileInfo.java b/src/main/java/cofh/api/tileentity/ITileInfo.java
new file mode 100644
index 00000000..87e4ce8f
--- /dev/null
+++ b/src/main/java/cofh/api/tileentity/ITileInfo.java
@@ -0,0 +1,31 @@
+package cofh.api.tileentity;
+
+import java.util.List;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.util.IChatComponent;
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Implement this interface on Tile Entities which can provide information about themselves.
+ *
+ * @author King Lemming
+ *
+ */
+public interface ITileInfo {
+
+ /**
+ * This function appends information to a list provided to it.
+ *
+ * @param info
+ * The list that the information should be appended to.
+ * @param side
+ * The side of the block that is being queried.
+ * @param player
+ * Player doing the querying - this can be NULL.
+ * @param debug
+ * If true, the tile should return "debug" information.
+ */
+ void getTileInfo(List info, ForgeDirection side, EntityPlayer player, boolean debug);
+
+}
diff --git a/src/main/java/cofh/api/tileentity/package-info.java b/src/main/java/cofh/api/tileentity/package-info.java
new file mode 100644
index 00000000..eae5967b
--- /dev/null
+++ b/src/main/java/cofh/api/tileentity/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHAPIProps.VERSION, owner = "CoFHAPI", provides = "CoFHAPI|tileentity")
+package cofh.api.tileentity;
+
+import cofh.api.CoFHAPIProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/api/transport/IEnderAttuned.java b/src/main/java/cofh/api/transport/IEnderAttuned.java
new file mode 100644
index 00000000..df02f910
--- /dev/null
+++ b/src/main/java/cofh/api/transport/IEnderAttuned.java
@@ -0,0 +1,45 @@
+package cofh.api.transport;
+
+/**
+ * Internal Only.
+ * Not to be implemented directly.
+ */
+public interface IEnderAttuned {
+
+ /**
+ * Returns the channel this IEnderAttuned is operating on.
+ * Typically, this is _public_
or the player's profile (not display) name but can be anything.
+ * It is used to separate frequency spectrums.
+ *
+ * Before changing, the IEnderAttuned must be removed from all registries it has been added to.
+ *
+ * @return The channel this IEnderAttuned is operating on.
+ */
+ public String getChannelString();
+
+ /**
+ * Returns the frequency this IEnderAttuned is operating on.
+ * Nominally, this value is positive and user-controlled.
+ *
+ * Before changing, the IEnderAttuned must be removed from all registries it has been added to.
+ *
+ * @return The frequency this IEnderAttuned is operating on.
+ */
+ public int getFrequency();
+
+ /**
+ * Changes the value returned by getFrequency()
+ *
+ * @return True if the frequency was successfully modified.
+ */
+ public boolean setFrequency(int frequency);
+
+ /**
+ * Removes this IEnderAttuned from all registries.
+ * It is recommended that this not be called from setFrequency()
.
+ *
+ * @return True if the frequency was successfully modified.
+ */
+ public boolean clearFrequency();
+
+}
diff --git a/src/main/java/cofh/api/transport/IEnderDestination.java b/src/main/java/cofh/api/transport/IEnderDestination.java
new file mode 100644
index 00000000..f3042a76
--- /dev/null
+++ b/src/main/java/cofh/api/transport/IEnderDestination.java
@@ -0,0 +1,21 @@
+package cofh.api.transport;
+
+public interface IEnderDestination extends IEnderAttuned {
+
+ public boolean isNotValid();
+
+ public int x();
+
+ public int y();
+
+ public int z();
+
+ public int dimension();
+
+ public int getDestination();
+
+ public boolean setDestination(int frequency);
+
+ public boolean clearDestination();
+
+}
diff --git a/src/main/java/cofh/api/transport/IEnderEnergyHandler.java b/src/main/java/cofh/api/transport/IEnderEnergyHandler.java
new file mode 100644
index 00000000..892a5c66
--- /dev/null
+++ b/src/main/java/cofh/api/transport/IEnderEnergyHandler.java
@@ -0,0 +1,35 @@
+package cofh.api.transport;
+
+/**
+ * This interface is implemented on Ender Attuned objects which can receive Energy (Redstone Flux).
+ *
+ * @author King Lemming
+ *
+ */
+public interface IEnderEnergyHandler extends IEnderAttuned {
+
+ /**
+ * Return whether or not the Ender Attuned object can currently send energy (Redstone Flux).
+ */
+ boolean canSendEnergy();
+
+ /**
+ * This should be checked to see if the Ender Attuned object can currently receive energy (Redstone Flux).
+ *
+ * Note: In practice, this can (and should) be used to ensure that something does not send to itself.
+ */
+ boolean canReceiveEnergy();
+
+ /**
+ * This tells the Ender Attuned object to receive energy. This returns the amount remaining, *not* the amount received - a return of 0 means that all energy
+ * was received!
+ *
+ * @param energy
+ * Amount of energy to be received.
+ * @param simulate
+ * If TRUE, the result will only be simulated.
+ * @return Amount of energy that is remaining (or would be remaining, if simulated).
+ */
+ int receiveEnergy(int energy, boolean simulate);
+
+}
diff --git a/src/main/java/cofh/api/transport/IEnderFluidHandler.java b/src/main/java/cofh/api/transport/IEnderFluidHandler.java
new file mode 100644
index 00000000..2f509e81
--- /dev/null
+++ b/src/main/java/cofh/api/transport/IEnderFluidHandler.java
@@ -0,0 +1,37 @@
+package cofh.api.transport;
+
+import net.minecraftforge.fluids.FluidStack;
+
+/**
+ * This interface is implemented on Ender Attuned objects which can receive Fluid.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IEnderFluidHandler extends IEnderAttuned {
+
+ /**
+ * Return whether or not the Ender Attuned object can currently send FluidStacks.
+ */
+ boolean canSendFluid();
+
+ /**
+ * This should be checked to see if the Ender Attuned object can currently receive a FluidStack.
+ *
+ * Note: In practice, this can (and should) be used to ensure that something does not send to itself.
+ */
+ boolean canReceiveFluid();
+
+ /**
+ * This tells the Ender Attuned object to receive a FluidStack. This returns what remains of the original stack, *not* the amount received - a null return
+ * means that the entire stack was received!
+ *
+ * @param fluid
+ * FluidStack to be received.
+ * @param simulate
+ * If TRUE, the result will only be simulated.
+ * @return FluidStack representing how much fluid is remaining (or would be remaining, if simulated).
+ */
+ FluidStack receiveFluid(FluidStack fluid, boolean simulate);
+
+}
diff --git a/src/main/java/cofh/api/transport/IEnderItemHandler.java b/src/main/java/cofh/api/transport/IEnderItemHandler.java
new file mode 100644
index 00000000..d5dad8f3
--- /dev/null
+++ b/src/main/java/cofh/api/transport/IEnderItemHandler.java
@@ -0,0 +1,38 @@
+package cofh.api.transport;
+
+import net.minecraft.item.ItemStack;
+
+/**
+ * This interface is implemented on Ender Attuned objects which can receive Items.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IEnderItemHandler extends IEnderAttuned {
+
+ /**
+ * Return whether or not the Ender Attuned object can currently send ItemStacks.
+ */
+ boolean canSendItems();
+
+ /**
+ * This should be checked to see if the Ender Attuned object can currently receive an ItemStack.
+ *
+ * Note: In practice, this can (and should) be used to ensure that something does not send to itself.
+ */
+ boolean canReceiveItems();
+
+ /**
+ * This tells the Ender Attuned object to receive an ItemStack. This returns what remains of the original stack, *not* the amount received - a null return
+ * means that the entire stack was received!
+ *
+ * This function does not support simulation because Inventory manipulation in Minecraft is an absolute mess and it would be a computational liability to do
+ * so.
+ *
+ * @param item
+ * ItemStack to be received.
+ * @return An ItemStack representing how much is remaining.
+ */
+ ItemStack receiveItem(ItemStack item);
+
+}
diff --git a/src/main/java/cofh/api/transport/IItemDuct.java b/src/main/java/cofh/api/transport/IItemDuct.java
new file mode 100644
index 00000000..67338346
--- /dev/null
+++ b/src/main/java/cofh/api/transport/IItemDuct.java
@@ -0,0 +1,26 @@
+package cofh.api.transport;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * This interface is implemented on ItemDucts. Use it to attempt to eject items into an entry point.
+ *
+ * @author Zeldo Kavira, King Lemming
+ *
+ */
+public interface IItemDuct {
+
+ /**
+ * Insert an ItemStack into the IItemDuct. Will only accept items if there is a valid destination. This returns what is remaining of the original stack - a
+ * null return means that the entire stack was accepted/routed!
+ *
+ * @param from
+ * Orientation the item is inserted from.
+ * @param item
+ * ItemStack to be inserted. The size of this stack corresponds to the maximum amount to insert.
+ * @return An ItemStack representing how much is remaining after the item was inserted into the Duct.
+ */
+ public ItemStack insertItem(ForgeDirection from, ItemStack item);
+
+}
diff --git a/src/main/java/cofh/api/transport/package-info.java b/src/main/java/cofh/api/transport/package-info.java
new file mode 100644
index 00000000..fae0150f
--- /dev/null
+++ b/src/main/java/cofh/api/transport/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHAPIProps.VERSION, owner = "CoFHAPI", provides = "CoFHAPI|transport")
+package cofh.api.transport;
+
+import cofh.api.CoFHAPIProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/api/world/IFeatureGenerator.java b/src/main/java/cofh/api/world/IFeatureGenerator.java
new file mode 100644
index 00000000..05d845e3
--- /dev/null
+++ b/src/main/java/cofh/api/world/IFeatureGenerator.java
@@ -0,0 +1,39 @@
+package cofh.api.world;
+
+import java.util.Random;
+
+import net.minecraft.world.World;
+import net.minecraft.world.gen.feature.WorldGenerator;
+
+/**
+ * This interface should be implemented on classes which define a world feature to be generated in a {@link IFeatureHandler}. It is essentially a more robust
+ * version of {@link WorldGenerator}, and may include one or more WorldGenerators should you wish.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IFeatureGenerator {
+
+ /**
+ * Returns the name of the feature, used for unique identification in configs and retrogen.
+ */
+ public String getFeatureName();
+
+ /**
+ * Generates the world feature.
+ *
+ * @param random
+ * Random derived from the world seed.
+ * @param chunkX
+ * Minimum X chunk-coordinate of the chunk. (x16 for block coordinate)
+ * @param chunkZ
+ * Minimum Z chunk-coordinate of the chunk. (x16 for block coordinate)
+ * @param world
+ * The world to generate in.
+ * @param newGen
+ * True on initial generation, false on retrogen.
+ * @return True if generation happened, false otherwise.
+ */
+ public boolean generateFeature(Random random, int chunkX, int chunkZ, World world, boolean newGen);
+
+}
diff --git a/src/main/java/cofh/api/world/IFeatureHandler.java b/src/main/java/cofh/api/world/IFeatureHandler.java
new file mode 100644
index 00000000..0c1939b1
--- /dev/null
+++ b/src/main/java/cofh/api/world/IFeatureHandler.java
@@ -0,0 +1,22 @@
+package cofh.api.world;
+
+/**
+ * Provides an interface to allow for the addition of Features to world generation.
+ *
+ * See {@link IFeatureGenerator}.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IFeatureHandler {
+
+ /**
+ * Register a feature with an IFeatureHandler.
+ *
+ * @param feature
+ * The feature to register.
+ * @return True if the registration was successful, false if a feature with that name existed.
+ */
+ public boolean registerFeature(IFeatureGenerator feature);
+
+}
diff --git a/src/main/java/cofh/api/world/IFeatureParser.java b/src/main/java/cofh/api/world/IFeatureParser.java
new file mode 100644
index 00000000..ff58c7dc
--- /dev/null
+++ b/src/main/java/cofh/api/world/IFeatureParser.java
@@ -0,0 +1,22 @@
+package cofh.api.world;
+
+import com.google.gson.JsonObject;
+
+import org.apache.logging.log4j.Logger;
+
+public interface IFeatureParser {
+
+ /**
+ * Parse a {@link JsonObject} for registration with an with an {@link IFeatureHandler}.
+ *
+ * @param featureName
+ * The name of the feature to register.
+ * @param genObject
+ * The JsonObject to parse.
+ * @param log
+ * The {@link Logger} to log debug/error/etc. messages to.
+ * @return The {@link IFeatureGenerator} to be registered with an IFeatureHandler
+ */
+ public IFeatureGenerator parseFeature(String featureName, JsonObject genObject, Logger log);
+
+}
diff --git a/src/main/java/cofh/api/world/IGeneratorParser.java b/src/main/java/cofh/api/world/IGeneratorParser.java
new file mode 100644
index 00000000..2d608b55
--- /dev/null
+++ b/src/main/java/cofh/api/world/IGeneratorParser.java
@@ -0,0 +1,34 @@
+package cofh.api.world;
+
+import cofh.lib.util.WeightedRandomBlock;
+import com.google.gson.JsonObject;
+
+import java.util.List;
+
+import net.minecraft.world.gen.feature.WorldGenerator;
+
+import org.apache.logging.log4j.Logger;
+
+public interface IGeneratorParser {
+
+ /**
+ * Parse a {@link JsonObject} for registration with an with an {@link IFeatureGenerator}.
+ *
+ * @param generatorName
+ * The name of the generator to register.
+ * @param genObject
+ * The JsonObject to parse.
+ * @param log
+ * The {@link Logger} to log debug/error/etc. messages to.
+ * @param resList
+ * The processed list of resources to generate
+ * @param clusterSize
+ * The processed size of the cluster
+ * @param matList
+ * The processed list of materials to generate in
+ * @return The {@link WorldGenerator} to be registered with an IFeatureGenerator
+ */
+ public WorldGenerator parseGenerator(String generatorName, JsonObject genObject, Logger log, List resList, int clusterSize,
+ List matList);
+
+}
diff --git a/src/main/java/cofh/api/world/package-info.java b/src/main/java/cofh/api/world/package-info.java
new file mode 100644
index 00000000..3e77dc95
--- /dev/null
+++ b/src/main/java/cofh/api/world/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHAPIProps.VERSION, owner = "CoFHAPI", provides = "CoFHAPI|world")
+package cofh.api.world;
+
+import cofh.api.CoFHAPIProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/asm/CoFHAccessTransformer.java b/src/main/java/cofh/asm/CoFHAccessTransformer.java
index a89c86a0..7b838014 100644
--- a/src/main/java/cofh/asm/CoFHAccessTransformer.java
+++ b/src/main/java/cofh/asm/CoFHAccessTransformer.java
@@ -48,8 +48,6 @@ public CoFHAccessTransformer() throws IOException {
superClasses.put(null, null);
- // file names are case sensitive. do not alter.
- mapFileList.add("CoFH_at.cfg");
// CoFH_at.cfg must also contain all entries from cofhlib_at.cfg
for (String file : mapFileList) {
diff --git a/src/main/java/cofh/core/render/ItemRenderRegistry.java b/src/main/java/cofh/core/render/ItemRenderRegistry.java
index 93ac27c1..4eddcde3 100644
--- a/src/main/java/cofh/core/render/ItemRenderRegistry.java
+++ b/src/main/java/cofh/core/render/ItemRenderRegistry.java
@@ -33,7 +33,7 @@ public static boolean validItem(ItemStack stack) {
return itemRenders.containsKey(ItemWrapper.fromItemStack(stack));
}
- public static void refreshMap() {
+ public static synchronized void refreshMap() {
Map tempMap = new THashMap(itemRenders.size());
diff --git a/src/main/java/cofh/core/util/FMLEventHandler.java b/src/main/java/cofh/core/util/FMLEventHandler.java
index 2b849a12..5d137e76 100644
--- a/src/main/java/cofh/core/util/FMLEventHandler.java
+++ b/src/main/java/cofh/core/util/FMLEventHandler.java
@@ -39,7 +39,7 @@ public void onPlayerLogin(PlayerLoggedInEvent event) {
}
@EventHandler
- public void handleIdMappingEvent(FMLModIdMappingEvent event) {
+ public synchronized void handleIdMappingEvent(FMLModIdMappingEvent event) {
BucketHandler.refreshMap();
FurnaceFuelHandler.refreshMap();
diff --git a/src/main/java/cofh/core/util/energy/FurnaceFuelHandler.java b/src/main/java/cofh/core/util/energy/FurnaceFuelHandler.java
index 47ff4a4b..1b394541 100644
--- a/src/main/java/cofh/core/util/energy/FurnaceFuelHandler.java
+++ b/src/main/java/cofh/core/util/energy/FurnaceFuelHandler.java
@@ -46,7 +46,7 @@ public static boolean registerFuel(ItemStack fuel, int burnTime) {
return true;
}
- public static void refreshMap() {
+ public static synchronized void refreshMap() {
THashMap tempMap = new THashMap();
diff --git a/src/main/java/cofh/core/util/fluid/BucketHandler.java b/src/main/java/cofh/core/util/fluid/BucketHandler.java
index b5d6760e..b843886f 100644
--- a/src/main/java/cofh/core/util/fluid/BucketHandler.java
+++ b/src/main/java/cofh/core/util/fluid/BucketHandler.java
@@ -183,7 +183,7 @@ public static boolean emptyBucket(World world, int x, int y, int z, ItemStack bu
return r;
}
- public static void refreshMap() {
+ public static synchronized void refreshMap() {
BiMap tempMap = HashBiMap.create(buckets.size());
diff --git a/src/main/java/cofh/core/util/oredict/OreDictionaryArbiter.java b/src/main/java/cofh/core/util/oredict/OreDictionaryArbiter.java
index 8bbd795d..2efd7a4e 100644
--- a/src/main/java/cofh/core/util/oredict/OreDictionaryArbiter.java
+++ b/src/main/java/cofh/core/util/oredict/OreDictionaryArbiter.java
@@ -8,8 +8,10 @@
import gnu.trove.map.TMap;
import gnu.trove.map.hash.THashMap;
+import lombok.val;
import java.util.ArrayList;
+import java.util.HashSet;
import net.minecraft.item.ItemStack;
import net.minecraftforge.oredict.OreDictionary;
@@ -40,7 +42,7 @@ public class OreDictionaryArbiter {
/**
* Initializes all of the entries. Called on server start to make sure everything is in sync.
*/
- public static void initialize() {
+ public static synchronized void initialize() {
oreIDs = HashBiMap.create(oreIDs == null ? 32 : oreIDs.size());
oreStacks = new THashMap>(oreStacks == null ? 32 : oreStacks.size());
@@ -57,15 +59,23 @@ public static void initialize() {
registerOreDictionaryEntry(ores.get(j), oreNames[i]);
}
}
- for (ItemWrapper wrapper : stackIDs.keySet()) {
- if (wrapper.metadata != WILDCARD_VALUE) {
- ItemWrapper wildItem = new ItemWrapper(wrapper.item, WILDCARD_VALUE);
- if (stackIDs.containsKey(wildItem)) {
- stackIDs.get(wrapper).addAll(stackIDs.get(wildItem));
- stackNames.get(wrapper).addAll(stackNames.get(wildItem));
+ val stackKeys = new HashSet();
+ do {
+ val currentKeys = new HashSet<>(stackIDs.keySet());
+ currentKeys.removeAll(stackKeys);
+ if (currentKeys.isEmpty())
+ break;
+ for (ItemWrapper wrapper : currentKeys) {
+ if (wrapper.metadata != WILDCARD_VALUE) {
+ ItemWrapper wildItem = new ItemWrapper(wrapper.item, WILDCARD_VALUE);
+ if (stackIDs.containsKey(wildItem)) {
+ stackIDs.get(wrapper).addAll(stackIDs.get(wildItem));
+ stackNames.get(wrapper).addAll(stackNames.get(wildItem));
+ }
}
}
- }
+ stackKeys.addAll(currentKeys);
+ } while(true);
ItemHelper.oreProxy = new OreDictionaryArbiterProxy();
}
diff --git a/src/main/java/cofh/lib/CoFHLibProps.java b/src/main/java/cofh/lib/CoFHLibProps.java
new file mode 100644
index 00000000..4af7db0e
--- /dev/null
+++ b/src/main/java/cofh/lib/CoFHLibProps.java
@@ -0,0 +1,11 @@
+package cofh.lib;
+
+public class CoFHLibProps {
+
+ private CoFHLibProps() {
+
+ }
+
+ public static final String VERSION = "1.7.10R1.2.1";
+
+}
diff --git a/src/main/java/cofh/lib/audio/ISoundSource.java b/src/main/java/cofh/lib/audio/ISoundSource.java
new file mode 100644
index 00000000..21f1e437
--- /dev/null
+++ b/src/main/java/cofh/lib/audio/ISoundSource.java
@@ -0,0 +1,13 @@
+package cofh.lib.audio;
+
+import net.minecraft.client.audio.ISound;
+
+public interface ISoundSource {
+
+ /**
+ * Should actually return an ISound. The object return prevents server crashes.
+ */
+ ISound getSound();
+
+ boolean shouldPlaySound();
+}
diff --git a/src/main/java/cofh/lib/audio/SoundBase.java b/src/main/java/cofh/lib/audio/SoundBase.java
new file mode 100644
index 00000000..8404b7a5
--- /dev/null
+++ b/src/main/java/cofh/lib/audio/SoundBase.java
@@ -0,0 +1,175 @@
+package cofh.lib.audio;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+import net.minecraft.client.audio.ISound;
+import net.minecraft.util.ResourceLocation;
+
+/**
+ * Generic ISound class with lots of constructor functionality. Required because - of course - Mojang has no generic that lets you specify *any* arguments for
+ * this.
+ *
+ * @author skyboy
+ *
+ */
+@SideOnly(Side.CLIENT)
+public class SoundBase implements ISound {
+
+ protected AttenuationType attenuation;
+ protected final ResourceLocation sound;
+ protected float volume;
+ protected float pitch;
+ protected float x;
+ protected float y;
+ protected float z;
+ protected boolean repeat;
+ protected int repeatDelay;
+
+ public SoundBase(String sound) {
+
+ this(sound, 0);
+ }
+
+ public SoundBase(String sound, float volume) {
+
+ this(sound, volume, 0);
+ }
+
+ public SoundBase(String sound, float volume, float pitch) {
+
+ this(sound, volume, pitch, false, 0);
+ }
+
+ public SoundBase(String sound, float volume, float pitch, boolean repeat, int repeatDelay) {
+
+ this(sound, volume, pitch, repeat, repeatDelay, 0, 0, 0, AttenuationType.NONE);
+ }
+
+ public SoundBase(String sound, float volume, float pitch, double x, double y, double z) {
+
+ this(sound, volume, pitch, false, 0, x, y, z);
+ }
+
+ public SoundBase(String sound, float volume, float pitch, boolean repeat, int repeatDelay, double x, double y, double z) {
+
+ this(sound, volume, pitch, repeat, repeatDelay, x, y, z, AttenuationType.LINEAR);
+ }
+
+ public SoundBase(String sound, float volume, float pitch, boolean repeat, int repeatDelay, double x, double y, double z, AttenuationType attenuation) {
+
+ this(new ResourceLocation(sound), volume, pitch, repeat, repeatDelay, x, y, z, attenuation);
+ }
+
+ public SoundBase(ResourceLocation sound) {
+
+ this(sound, 0);
+ }
+
+ public SoundBase(ResourceLocation sound, float volume) {
+
+ this(sound, volume, 0);
+ }
+
+ public SoundBase(ResourceLocation sound, float volume, float pitch) {
+
+ this(sound, volume, pitch, false, 0);
+ }
+
+ public SoundBase(ResourceLocation sound, float volume, float pitch, boolean repeat, int repeatDelay) {
+
+ this(sound, volume, pitch, repeat, repeatDelay, 0, 0, 0, AttenuationType.NONE);
+ }
+
+ public SoundBase(ResourceLocation sound, float volume, float pitch, double x, double y, double z) {
+
+ this(sound, volume, pitch, false, 0, x, y, z);
+ }
+
+ public SoundBase(ResourceLocation sound, float volume, float pitch, boolean repeat, int repeatDelay, double x, double y, double z) {
+
+ this(sound, volume, pitch, repeat, repeatDelay, x, y, z, AttenuationType.LINEAR);
+ }
+
+ public SoundBase(ResourceLocation sound, float volume, float pitch, boolean repeat, int repeatDelay, double x, double y, double z,
+ AttenuationType attenuation) {
+
+ this.attenuation = attenuation;
+ this.sound = sound;
+ this.volume = volume;
+ this.pitch = pitch;
+ this.x = (float) x;
+ this.y = (float) y;
+ this.z = (float) z;
+ this.repeat = repeat;
+ this.repeatDelay = repeatDelay;
+ }
+
+ public SoundBase(SoundBase other) {
+
+ this.attenuation = other.attenuation;
+ this.sound = other.sound;
+ this.volume = other.volume;
+ this.pitch = other.pitch;
+ this.x = other.x;
+ this.y = other.y;
+ this.z = other.z;
+ this.repeat = other.repeat;
+ this.repeatDelay = other.repeatDelay;
+ }
+
+ @Override
+ public AttenuationType getAttenuationType() {
+
+ return attenuation;
+ }
+
+ @Override
+ public ResourceLocation getPositionedSoundLocation() {
+
+ return sound;
+ }
+
+ @Override
+ public float getVolume() {
+
+ return volume;
+ }
+
+ @Override
+ public float getPitch() {
+
+ return pitch;
+ }
+
+ @Override
+ public float getXPosF() {
+
+ return x;
+ }
+
+ @Override
+ public float getYPosF() {
+
+ return y;
+ }
+
+ @Override
+ public float getZPosF() {
+
+ return z;
+ }
+
+ @Override
+ public boolean canRepeat() {
+
+ return repeat;
+ }
+
+ @Override
+ public int getRepeatDelay() {
+
+ return repeatDelay;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/audio/SoundTile.java b/src/main/java/cofh/lib/audio/SoundTile.java
new file mode 100644
index 00000000..37878c3f
--- /dev/null
+++ b/src/main/java/cofh/lib/audio/SoundTile.java
@@ -0,0 +1,91 @@
+package cofh.lib.audio;
+
+import net.minecraft.client.audio.ITickableSound;
+import net.minecraft.util.ResourceLocation;
+
+public class SoundTile extends SoundBase implements ITickableSound {
+
+ ISoundSource source;
+ boolean beginFadeOut;
+ boolean donePlaying;
+ int ticks = 0;
+ int fadeIn = 50;
+ int fadeOut = 50;
+ float baseVolume = 1.0F;
+
+ public SoundTile(ISoundSource source, String sound, float volume, float pitch, boolean repeat, int repeatDelay, double x, double y, double z) {
+
+ this(source, sound, volume, pitch, repeat, repeatDelay, x, y, z, AttenuationType.LINEAR);
+ }
+
+ public SoundTile(ISoundSource source, String sound, float volume, float pitch, boolean repeat, int repeatDelay, double x, double y, double z,
+ AttenuationType attenuation) {
+
+ this(source, new ResourceLocation(sound), volume, pitch, repeat, repeatDelay, x, y, z, attenuation);
+ }
+
+ public SoundTile(ISoundSource source, ResourceLocation sound, float volume, float pitch, boolean repeat, int repeatDelay, double x, double y, double z) {
+
+ this(source, sound, volume, pitch, repeat, repeatDelay, x, y, z, AttenuationType.LINEAR);
+ }
+
+ public SoundTile(ISoundSource source, ResourceLocation sound, float volume, float pitch, boolean repeat, int repeatDelay, double x, double y, double z,
+ AttenuationType attenuation) {
+
+ super(sound, volume, pitch, repeat, repeatDelay, x, y, z, attenuation);
+ this.source = source;
+ this.baseVolume = volume;
+ }
+
+ public SoundTile setFadeIn(int fadeIn) {
+
+ this.fadeIn = Math.min(0, fadeIn);
+ return this;
+ }
+
+ public SoundTile setFadeOut(int fadeOut) {
+
+ this.fadeOut = Math.min(0, fadeOut);
+ return this;
+ }
+
+ public float getFadeInMultiplier() {
+
+ return ticks >= fadeIn ? 1 : (float) (ticks / (float) fadeIn);
+ }
+
+ public float getFadeOutMultiplier() {
+
+ return ticks >= fadeOut ? 0 : (float) ((fadeOut - ticks) / (float) fadeOut);
+ }
+
+ /* ITickableSound */
+ @Override
+ public void update() {
+
+ if (!beginFadeOut) {
+ if (ticks < fadeIn) {
+ ticks++;
+ }
+ if (!source.shouldPlaySound()) {
+ beginFadeOut = true;
+ ticks = 0;
+ }
+ } else {
+ ticks++;
+ }
+ float multiplier = beginFadeOut ? getFadeOutMultiplier() : getFadeInMultiplier();
+ volume = baseVolume * multiplier;
+
+ if (multiplier <= 0) {
+ donePlaying = true;
+ }
+ }
+
+ @Override
+ public boolean isDonePlaying() {
+
+ return donePlaying;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/audio/package-info.java b/src/main/java/cofh/lib/audio/package-info.java
new file mode 100644
index 00000000..50b9ad03
--- /dev/null
+++ b/src/main/java/cofh/lib/audio/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHLibProps.VERSION, owner = "CoFHLib", provides = "CoFHLib|audio")
+package cofh.lib.audio;
+
+import cofh.lib.CoFHLibProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/lib/gui/GuiBase.java b/src/main/java/cofh/lib/gui/GuiBase.java
new file mode 100644
index 00000000..8fd26c55
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/GuiBase.java
@@ -0,0 +1,803 @@
+package cofh.lib.gui;
+
+import cofh.lib.audio.SoundBase;
+import cofh.lib.gui.element.ElementBase;
+import cofh.lib.gui.element.TabBase;
+import cofh.lib.gui.slot.SlotFalseCopy;
+import cofh.lib.render.RenderHelper;
+import cofh.lib.util.helpers.StringHelper;
+import cpw.mods.fml.client.FMLClientHandler;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import net.minecraft.client.audio.SoundHandler;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.inventory.GuiContainer;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.inventory.Container;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.IIcon;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL12;
+
+/**
+ * Base class for a modular GUIs. Works with Elements {@link ElementBase} and Tabs {@link TabBase} which are both modular elements.
+ *
+ * @author King Lemming
+ */
+public abstract class GuiBase extends GuiContainer {
+
+ public static final SoundHandler guiSoundManager = FMLClientHandler.instance().getClient().getSoundHandler();
+
+ protected boolean drawTitle = true;
+ protected boolean drawInventory = true;
+ protected int mouseX = 0;
+ protected int mouseY = 0;
+
+ protected int lastIndex = -1;
+
+ protected String name;
+ protected ResourceLocation texture;
+
+ public ArrayList tabs = new ArrayList();
+ protected ArrayList elements = new ArrayList();
+
+ protected List tooltip = new LinkedList();
+ protected boolean tooltips = true;
+
+ public static void playSound(String name, float volume, float pitch) {
+
+ guiSoundManager.playSound(new SoundBase(name, volume, pitch));
+ }
+
+ public GuiBase(Container container) {
+
+ super(container);
+ }
+
+ public GuiBase(Container container, ResourceLocation texture) {
+
+ super(container);
+ this.texture = texture;
+ }
+
+ @Override
+ public void initGui() {
+
+ super.initGui();
+ tabs.clear();
+ elements.clear();
+ }
+
+ @Override
+ public void drawScreen(int x, int y, float partialTick) {
+
+ updateElementInformation();
+
+ super.drawScreen(x, y, partialTick);
+
+ if (tooltips && mc.thePlayer.inventory.getItemStack() == null) {
+ addTooltips(tooltip);
+ drawTooltip(tooltip);
+ }
+ mouseX = x - guiLeft;
+ mouseY = y - guiTop;
+
+ updateElements();
+ }
+
+ @Override
+ protected void drawGuiContainerForegroundLayer(int x, int y) {
+
+ if (drawTitle & name != null) {
+ fontRendererObj.drawString(StringHelper.localize(name), getCenteredOffset(StringHelper.localize(name)), 6, 0x404040);
+ }
+ if (drawInventory) {
+ fontRendererObj.drawString(StatCollector.translateToLocal("container.inventory"), 8, ySize - 96 + 3, 0x404040);
+ }
+ drawElements(0, true);
+ drawTabs(0, true);
+ }
+
+ @Override
+ protected void drawGuiContainerBackgroundLayer(float partialTick, int x, int y) {
+
+ GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
+ bindTexture(texture);
+ drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize);
+
+ mouseX = x - guiLeft;
+ mouseY = y - guiTop;
+
+ GL11.glPushMatrix();
+ GL11.glTranslatef(guiLeft, guiTop, 0.0F);
+ drawElements(partialTick, false);
+ drawTabs(partialTick, false);
+ GL11.glPopMatrix();
+ }
+
+ @Override
+ protected void keyTyped(char characterTyped, int keyPressed) {
+
+ for (int i = elements.size(); i-- > 0;) {
+ ElementBase c = elements.get(i);
+ if (!c.isVisible() || !c.isEnabled()) {
+ continue;
+ }
+ if (c.onKeyTyped(characterTyped, keyPressed)) {
+ return;
+ }
+ }
+ super.keyTyped(characterTyped, keyPressed);
+ }
+
+ @Override
+ public void handleMouseInput() {
+
+ int x = Mouse.getEventX() * width / mc.displayWidth;
+ int y = height - Mouse.getEventY() * height / mc.displayHeight - 1;
+
+ mouseX = x - guiLeft;
+ mouseY = y - guiTop;
+
+ int wheelMovement = Mouse.getEventDWheel();
+
+ if (wheelMovement != 0) {
+ for (int i = elements.size(); i-- > 0;) {
+ ElementBase c = elements.get(i);
+ if (!c.isVisible() || !c.isEnabled() || !c.intersectsWith(mouseX, mouseY)) {
+ continue;
+ }
+ if (c.onMouseWheel(mouseX, mouseY, wheelMovement)) {
+ return;
+ }
+ }
+ TabBase tab = getTabAtPosition(mouseX, mouseY);
+
+ if (tab != null && tab.onMouseWheel(mouseX, mouseY, wheelMovement)) {
+ return;
+ }
+
+ if (onMouseWheel(mouseX, mouseY, wheelMovement)) {
+ return;
+ }
+ }
+ super.handleMouseInput();
+ }
+
+ protected boolean onMouseWheel(int mouseX, int mouseY, int wheelMovement) {
+
+ return false;
+ }
+
+ @Override
+ protected void mouseClicked(int mX, int mY, int mouseButton) {
+
+ mX -= guiLeft;
+ mY -= guiTop;
+
+ for (int i = elements.size(); i-- > 0;) {
+ ElementBase c = elements.get(i);
+ if (!c.isVisible() || !c.isEnabled() || !c.intersectsWith(mX, mY)) {
+ continue;
+ }
+ if (c.onMousePressed(mX, mY, mouseButton)) {
+ return;
+ }
+ }
+
+ TabBase tab = getTabAtPosition(mX, mY);
+ if (tab != null) {
+ int tMx = mX;
+
+ if (!tab.onMousePressed(tMx, mY, mouseButton)) {
+ for (int i = tabs.size(); i-- > 0;) {
+ TabBase other = tabs.get(i);
+ if (other != tab && other.open && other.side == tab.side) {
+ other.toggleOpen();
+ }
+ }
+ tab.toggleOpen();
+ return;
+ }
+ }
+
+ mX += guiLeft;
+ mY += guiTop;
+
+ if (tab != null) {
+ switch (tab.side) {
+ case TabBase.LEFT:
+ // guiLeft -= tab.currentWidth;
+ break;
+ case TabBase.RIGHT:
+ xSize += tab.currentWidth;
+ break;
+ }
+ }
+ super.mouseClicked(mX, mY, mouseButton);
+ if (tab != null) {
+ switch (tab.side) {
+ case TabBase.LEFT:
+ // guiLeft += tab.currentWidth;
+ break;
+ case TabBase.RIGHT:
+ xSize -= tab.currentWidth;
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void mouseMovedOrUp(int mX, int mY, int mouseButton) {
+
+ mX -= guiLeft;
+ mY -= guiTop;
+
+ if (mouseButton >= 0 && mouseButton <= 2) { // 0:left, 1:right, 2: middle
+ for (int i = elements.size(); i-- > 0;) {
+ ElementBase c = elements.get(i);
+ if (!c.isVisible() || !c.isEnabled()) { // no bounds checking on mouseUp events
+ continue;
+ }
+ c.onMouseReleased(mX, mY);
+ }
+ }
+ mX += guiLeft;
+ mY += guiTop;
+
+ super.mouseMovedOrUp(mX, mY, mouseButton);
+ }
+
+ @Override
+ protected void mouseClickMove(int mX, int mY, int lastClick, long timeSinceClick) {
+
+ Slot slot = getSlotAtPosition(mX, mY);
+ ItemStack itemstack = this.mc.thePlayer.inventory.getItemStack();
+
+ if (this.field_147007_t && slot != null && itemstack != null && slot instanceof SlotFalseCopy) {
+ if (lastIndex != slot.slotNumber) {
+ lastIndex = slot.slotNumber;
+ this.handleMouseClick(slot, slot.slotNumber, 0, 0);
+ }
+ } else {
+ lastIndex = -1;
+ super.mouseClickMove(mX, mY, lastClick, timeSinceClick);
+ }
+ }
+
+ public Slot getSlotAtPosition(int xCoord, int yCoord) {
+
+ for (int k = 0; k < this.inventorySlots.inventorySlots.size(); ++k) {
+ Slot slot = (Slot) this.inventorySlots.inventorySlots.get(k);
+
+ if (this.isMouseOverSlot(slot, xCoord, yCoord)) {
+ return slot;
+ }
+ }
+ return null;
+ }
+
+ public boolean isMouseOverSlot(Slot theSlot, int xCoord, int yCoord) {
+
+ return this.func_146978_c(theSlot.xDisplayPosition, theSlot.yDisplayPosition, 16, 16, xCoord, yCoord);
+ }
+
+ /**
+ * Draws the elements for this GUI.
+ */
+ protected void drawElements(float partialTick, boolean foreground) {
+
+ if (foreground) {
+ for (int i = 0; i < elements.size(); i++) {
+ ElementBase element = elements.get(i);
+ if (element.isVisible()) {
+ element.drawForeground(mouseX, mouseY);
+ }
+ }
+ } else {
+ for (int i = 0; i < elements.size(); i++) {
+ ElementBase element = elements.get(i);
+ if (element.isVisible()) {
+ element.drawBackground(mouseX, mouseY, partialTick);
+ }
+ }
+ }
+ }
+
+ /**
+ * Draws the tabs for this GUI. Handles Tab open/close animation.
+ */
+ protected void drawTabs(float partialTick, boolean foreground) {
+
+ int yPosRight = 4;
+ int yPosLeft = 4;
+
+ if (foreground) {
+ for (int i = 0; i < tabs.size(); i++) {
+ TabBase tab = tabs.get(i);
+ tab.update();
+ if (!tab.isVisible()) {
+ continue;
+ }
+ if (tab.side == TabBase.LEFT) {
+ tab.drawForeground(mouseX, mouseY);
+ yPosLeft += tab.currentHeight;
+ } else {
+ tab.drawForeground(mouseX, mouseY);
+ yPosRight += tab.currentHeight;
+ }
+ }
+ } else {
+ for (int i = 0; i < tabs.size(); i++) {
+ TabBase tab = tabs.get(i);
+ tab.update();
+ if (!tab.isVisible()) {
+ continue;
+ }
+ if (tab.side == TabBase.LEFT) {
+ tab.setPosition(0, yPosLeft);
+ tab.drawBackground(mouseX, mouseY, partialTick);
+ yPosLeft += tab.currentHeight;
+ } else {
+ tab.setPosition(xSize, yPosRight);
+ tab.drawBackground(mouseX, mouseY, partialTick);
+ yPosRight += tab.currentHeight;
+ }
+ }
+ }
+ }
+
+ /**
+ * Called by NEI if installed
+ */
+ // @Override
+ public List handleTooltip(int mousex, int mousey, List tooltip) {
+
+ if (mc.thePlayer.inventory.getItemStack() == null) {
+ addTooltips(tooltip);
+ }
+ return tooltip;
+ }
+
+ public void addTooltips(List tooltip) {
+
+ TabBase tab = getTabAtPosition(mouseX, mouseY);
+
+ if (tab != null) {
+ tab.addTooltip(tooltip);
+ }
+ ElementBase element = getElementAtPosition(mouseX, mouseY);
+
+ if (element != null && element.isVisible()) {
+ element.addTooltip(tooltip);
+ }
+ }
+
+ /* ELEMENTS */
+ public ElementBase addElement(ElementBase element) {
+
+ elements.add(element);
+ return element;
+ }
+
+ public TabBase addTab(TabBase tab) {
+
+ int yOffset = 4;
+ for (int i = 0; i < tabs.size(); i++) {
+ if (tabs.get(i).side == tab.side && tabs.get(i).isVisible()) {
+ yOffset += tabs.get(i).currentHeight;
+ }
+ }
+ tab.setPosition(tab.side == TabBase.LEFT ? 0 : xSize, yOffset);
+ tabs.add(tab);
+
+ if (TabTracker.getOpenedLeftTab() != null && tab.getClass().equals(TabTracker.getOpenedLeftTab())) {
+ tab.setFullyOpen();
+ } else if (TabTracker.getOpenedRightTab() != null && tab.getClass().equals(TabTracker.getOpenedRightTab())) {
+ tab.setFullyOpen();
+ }
+ return tab;
+ }
+
+ protected ElementBase getElementAtPosition(int mX, int mY) {
+
+ for (int i = elements.size(); i-- > 0;) {
+ ElementBase element = elements.get(i);
+ if (element.intersectsWith(mX, mY)) {
+ return element;
+ }
+ }
+ return null;
+ }
+
+ protected TabBase getTabAtPosition(int mX, int mY) {
+
+ int xShift = 0;
+ int yShift = 4;
+
+ for (int i = 0; i < tabs.size(); i++) {
+ TabBase tab = tabs.get(i);
+ if (!tab.isVisible() || tab.side == TabBase.RIGHT) {
+ continue;
+ }
+ tab.setCurrentShift(xShift, yShift);
+ if (tab.intersectsWith(mX, mY, xShift, yShift)) {
+ return tab;
+ }
+ yShift += tab.currentHeight;
+ }
+
+ xShift = xSize;
+ yShift = 4;
+
+ for (int i = 0; i < tabs.size(); i++) {
+ TabBase tab = tabs.get(i);
+ if (!tab.isVisible() || tab.side == TabBase.LEFT) {
+ continue;
+ }
+ tab.setCurrentShift(xShift, yShift);
+ if (tab.intersectsWith(mX, mY, xShift, yShift)) {
+ return tab;
+ }
+ yShift += tab.currentHeight;
+ }
+ return null;
+ }
+
+ protected final void updateElements() {
+
+ for (int i = elements.size(); i-- > 0;) {
+ ElementBase c = elements.get(i);
+ if (c.isVisible() && c.isEnabled()) {
+ c.update(mouseX, mouseY);
+ }
+ }
+ }
+
+ protected void updateElementInformation() {
+
+ }
+
+ public void handleElementButtonClick(String buttonName, int mouseButton) {
+
+ }
+
+ /* HELPERS */
+ public void bindTexture(ResourceLocation texture) {
+
+ mc.renderEngine.bindTexture(texture);
+ }
+
+ /**
+ * Abstract method to retrieve icons by name from a registry. You must override this if you use any of the String methods below.
+ */
+ public IIcon getIcon(String name) {
+
+ return null;
+ }
+
+ /**
+ * Essentially a placeholder method for tabs to use should they need to draw a button.
+ */
+ public void drawButton(IIcon icon, int x, int y, int spriteSheet, int mode) {
+
+ drawIcon(icon, x, y, spriteSheet);
+ }
+
+ public void drawButton(String iconName, int x, int y, int spriteSheet, int mode) {
+
+ drawButton(getIcon(iconName), x, y, spriteSheet, mode);
+ }
+
+ public void drawItemStack(ItemStack stack, int x, int y, boolean drawOverlay, String overlayTxt) {
+
+ GL11.glEnable(GL11.GL_DEPTH_TEST);
+ GL11.glEnable(GL11.GL_LIGHTING);
+ GL11.glPushMatrix();
+ GL11.glTranslatef(0.0F, 0.0F, 32.0F);
+ this.zLevel = 200.0F;
+ itemRender.zLevel = 200.0F;
+
+ FontRenderer font = null;
+ if (stack != null) {
+ font = stack.getItem().getFontRenderer(stack);
+ }
+ if (font == null) {
+ font = fontRendererObj;
+ }
+
+ itemRender.renderItemAndEffectIntoGUI(font, this.mc.getTextureManager(), stack, x, y);
+
+ if (drawOverlay) {
+ itemRender.renderItemOverlayIntoGUI(font, this.mc.getTextureManager(), stack, x, y - (this.draggedStack == null ? 0 : 8), overlayTxt);
+ }
+
+ this.zLevel = 0.0F;
+ itemRender.zLevel = 0.0F;
+ GL11.glPopMatrix();
+ GL11.glDisable(GL11.GL_LIGHTING);
+ }
+
+ /**
+ * Simple method used to draw a fluid of arbitrary size.
+ */
+ public void drawFluid(int x, int y, FluidStack fluid, int width, int height) {
+
+ if (fluid == null || fluid.getFluid() == null) {
+ return;
+ }
+ RenderHelper.setBlockTextureSheet();
+ RenderHelper.setColor3ub(fluid.getFluid().getColor(fluid));
+
+ drawTiledTexture(x, y, fluid.getFluid().getIcon(fluid), width, height);
+ }
+
+ public void drawTiledTexture(int x, int y, IIcon icon, int width, int height) {
+
+ int i = 0;
+ int j = 0;
+
+ int drawHeight = 0;
+ int drawWidth = 0;
+
+ for (i = 0; i < width; i += 16) {
+ for (j = 0; j < height; j += 16) {
+ drawWidth = Math.min(width - i, 16);
+ drawHeight = Math.min(height - j, 16);
+ drawScaledTexturedModelRectFromIcon(x + i, y + j, icon, drawWidth, drawHeight);
+ }
+ }
+ GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0F);
+ }
+
+ public void drawIcon(IIcon icon, int x, int y, int spriteSheet) {
+
+ if (spriteSheet == 0) {
+ RenderHelper.setBlockTextureSheet();
+ } else {
+ RenderHelper.setItemTextureSheet();
+ }
+ GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0F);
+ drawTexturedModelRectFromIcon(x, y, icon, 16, 16);
+ }
+
+ public void drawColorIcon(IIcon icon, int x, int y, int spriteSheet) {
+
+ if (spriteSheet == 0) {
+ RenderHelper.setBlockTextureSheet();
+ } else {
+ RenderHelper.setItemTextureSheet();
+ }
+ drawTexturedModelRectFromIcon(x, y, icon, 16, 16);
+ }
+
+ public void drawIcon(String iconName, int x, int y, int spriteSheet) {
+
+ drawIcon(getIcon(iconName), x, y, spriteSheet);
+ }
+
+ public void drawSizedModalRect(int x1, int y1, int x2, int y2, int color) {
+
+ int temp;
+
+ if (x1 < x2) {
+ temp = x1;
+ x1 = x2;
+ x2 = temp;
+ }
+ if (y1 < y2) {
+ temp = y1;
+ y1 = y2;
+ y2 = temp;
+ }
+
+ float a = (color >> 24 & 255) / 255.0F;
+ float r = (color >> 16 & 255) / 255.0F;
+ float g = (color >> 8 & 255) / 255.0F;
+ float b = (color & 255) / 255.0F;
+ Tessellator tessellator = Tessellator.instance;
+ GL11.glEnable(GL11.GL_BLEND);
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+ GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
+ GL11.glColor4f(r, g, b, a);
+ tessellator.startDrawingQuads();
+ tessellator.addVertex(x1, y2, this.zLevel);
+ tessellator.addVertex(x2, y2, this.zLevel);
+ tessellator.addVertex(x2, y1, this.zLevel);
+ tessellator.addVertex(x1, y1, this.zLevel);
+ tessellator.draw();
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+ GL11.glDisable(GL11.GL_BLEND);
+ }
+
+ public void drawSizedRect(int x1, int y1, int x2, int y2, int color) {
+
+ int temp;
+
+ if (x1 < x2) {
+ temp = x1;
+ x1 = x2;
+ x2 = temp;
+ }
+ if (y1 < y2) {
+ temp = y1;
+ y1 = y2;
+ y2 = temp;
+ }
+
+ float a = (color >> 24 & 255) / 255.0F;
+ float r = (color >> 16 & 255) / 255.0F;
+ float g = (color >> 8 & 255) / 255.0F;
+ float b = (color & 255) / 255.0F;
+ Tessellator tessellator = Tessellator.instance;
+ GL11.glDisable(GL11.GL_TEXTURE_2D);
+ GL11.glColor4f(r, g, b, a);
+ tessellator.startDrawingQuads();
+ tessellator.addVertex(x1, y2, this.zLevel);
+ tessellator.addVertex(x2, y2, this.zLevel);
+ tessellator.addVertex(x2, y1, this.zLevel);
+ tessellator.addVertex(x1, y1, this.zLevel);
+ tessellator.draw();
+ GL11.glEnable(GL11.GL_TEXTURE_2D);
+ }
+
+ public void drawSizedTexturedModalRect(int x, int y, int u, int v, int width, int height, float texW, float texH) {
+
+ float texU = 1 / texW;
+ float texV = 1 / texH;
+ Tessellator tessellator = Tessellator.instance;
+ tessellator.startDrawingQuads();
+ tessellator.addVertexWithUV(x + 0, y + height, this.zLevel, (u + 0) * texU, (v + height) * texV);
+ tessellator.addVertexWithUV(x + width, y + height, this.zLevel, (u + width) * texU, (v + height) * texV);
+ tessellator.addVertexWithUV(x + width, y + 0, this.zLevel, (u + width) * texU, (v + 0) * texV);
+ tessellator.addVertexWithUV(x + 0, y + 0, this.zLevel, (u + 0) * texU, (v + 0) * texV);
+ tessellator.draw();
+ }
+
+ public void drawScaledTexturedModelRectFromIcon(int x, int y, IIcon icon, int width, int height) {
+
+ if (icon == null) {
+ return;
+ }
+ double minU = icon.getMinU();
+ double maxU = icon.getMaxU();
+ double minV = icon.getMinV();
+ double maxV = icon.getMaxV();
+
+ Tessellator tessellator = Tessellator.instance;
+ tessellator.startDrawingQuads();
+ tessellator.addVertexWithUV(x + 0, y + height, this.zLevel, minU, minV + (maxV - minV) * height / 16F);
+ tessellator.addVertexWithUV(x + width, y + height, this.zLevel, minU + (maxU - minU) * width / 16F, minV + (maxV - minV) * height / 16F);
+ tessellator.addVertexWithUV(x + width, y + 0, this.zLevel, minU + (maxU - minU) * width / 16F, minV);
+ tessellator.addVertexWithUV(x + 0, y + 0, this.zLevel, minU, minV);
+ tessellator.draw();
+ }
+
+ public void drawTooltip(List list) {
+
+ drawTooltipHoveringText(list, mouseX + guiLeft, mouseY + guiTop, fontRendererObj);
+ tooltip.clear();
+ }
+
+ @SuppressWarnings("rawtypes")
+ protected void drawTooltipHoveringText(List list, int x, int y, FontRenderer font) {
+
+ if (list == null || list.isEmpty()) {
+ return;
+ }
+ GL11.glDisable(GL12.GL_RESCALE_NORMAL);
+ GL11.glDisable(GL11.GL_LIGHTING);
+ GL11.glDisable(GL11.GL_DEPTH_TEST);
+ int k = 0;
+ Iterator iterator = list.iterator();
+
+ while (iterator.hasNext()) {
+ String s = (String) iterator.next();
+ int l = font.getStringWidth(s);
+
+ if (l > k) {
+ k = l;
+ }
+ }
+ int i1 = x + 12;
+ int j1 = y - 12;
+ int k1 = 8;
+
+ if (list.size() > 1) {
+ k1 += 2 + (list.size() - 1) * 10;
+ }
+ if (i1 + k > this.width) {
+ i1 -= 28 + k;
+ }
+ if (j1 + k1 + 6 > this.height) {
+ j1 = this.height - k1 - 6;
+ }
+ this.zLevel = 300.0F;
+ itemRender.zLevel = 300.0F;
+ int l1 = -267386864;
+ this.drawGradientRect(i1 - 3, j1 - 4, i1 + k + 3, j1 - 3, l1, l1);
+ this.drawGradientRect(i1 - 3, j1 + k1 + 3, i1 + k + 3, j1 + k1 + 4, l1, l1);
+ this.drawGradientRect(i1 - 3, j1 - 3, i1 + k + 3, j1 + k1 + 3, l1, l1);
+ this.drawGradientRect(i1 - 4, j1 - 3, i1 - 3, j1 + k1 + 3, l1, l1);
+ this.drawGradientRect(i1 + k + 3, j1 - 3, i1 + k + 4, j1 + k1 + 3, l1, l1);
+ int i2 = 1347420415;
+ int j2 = (i2 & 16711422) >> 1 | i2 & -16777216;
+ this.drawGradientRect(i1 - 3, j1 - 3 + 1, i1 - 3 + 1, j1 + k1 + 3 - 1, i2, j2);
+ this.drawGradientRect(i1 + k + 2, j1 - 3 + 1, i1 + k + 3, j1 + k1 + 3 - 1, i2, j2);
+ this.drawGradientRect(i1 - 3, j1 - 3, i1 + k + 3, j1 - 3 + 1, i2, i2);
+ this.drawGradientRect(i1 - 3, j1 + k1 + 2, i1 + k + 3, j1 + k1 + 3, j2, j2);
+
+ for (int k2 = 0; k2 < list.size(); ++k2) {
+ String s1 = (String) list.get(k2);
+ font.drawStringWithShadow(s1, i1, j1, -1);
+
+ if (k2 == 0) {
+ j1 += 2;
+ }
+ j1 += 10;
+ }
+ this.zLevel = 0.0F;
+ itemRender.zLevel = 0.0F;
+ GL11.glEnable(GL11.GL_LIGHTING);
+ GL11.glEnable(GL11.GL_DEPTH_TEST);
+ GL11.glEnable(GL12.GL_RESCALE_NORMAL);
+ }
+
+ /**
+ * Passthrough method for tab use.
+ */
+ public void mouseClicked(int mouseButton) {
+
+ super.mouseClicked(guiLeft + mouseX, guiTop + mouseY, mouseButton);
+ }
+
+ public FontRenderer getFontRenderer() {
+
+ return fontRendererObj;
+ }
+
+ protected int getCenteredOffset(String string) {
+
+ return this.getCenteredOffset(string, xSize);
+ }
+
+ protected int getCenteredOffset(String string, int xWidth) {
+
+ return (xWidth - fontRendererObj.getStringWidth(string)) / 2;
+ }
+
+ public int getGuiLeft() {
+
+ return guiLeft;
+ }
+
+ public int getGuiTop() {
+
+ return guiTop;
+ }
+
+ public int getMouseX() {
+
+ return mouseX;
+ }
+
+ public int getMouseY() {
+
+ return mouseY;
+ }
+
+ public void overlayRecipe() {
+
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/GuiBook.java b/src/main/java/cofh/lib/gui/GuiBook.java
new file mode 100644
index 00000000..29020e48
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/GuiBook.java
@@ -0,0 +1,10 @@
+package cofh.lib.gui;
+
+import net.minecraft.client.gui.GuiScreen;
+
+public class GuiBook extends GuiScreen {
+
+ protected int mouseX = 0;
+ protected int mouseY = 0;
+
+}
diff --git a/src/main/java/cofh/lib/gui/GuiColor.java b/src/main/java/cofh/lib/gui/GuiColor.java
new file mode 100644
index 00000000..0473984a
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/GuiColor.java
@@ -0,0 +1,142 @@
+package cofh.lib.gui;
+
+public class GuiColor extends Number {
+
+ private static final long serialVersionUID = 7024827242888861187L;
+ private final int _color;
+
+ public GuiColor(int argb) {
+
+ _color = argb;
+ }
+
+ public GuiColor(int rgba, Void dummy) {
+
+ this(rgba >>> 24, rgba >> 16, rgba >> 8, rgba);
+ }
+
+ public GuiColor(byte alpha, int argb) {
+
+ this(argb >> 16, argb >> 8, argb, alpha);
+ }
+
+ public GuiColor(int rgba, byte alpha) {
+
+ this(rgba >>> 24, rgba >> 16, rgba >> 8, alpha);
+ }
+
+ public GuiColor(int r, int g, int b) {
+
+ this(r, g, b, 255);
+ }
+
+ public GuiColor(int r, int g, int b, int a) {
+
+ _color = (b & 0xFF) | (g & 0xFF) << 8 | (r & 0xFF) << 16 | (a & 0xFF) << 24;
+ }
+
+ public int getColor() {
+
+ return _color;
+ }
+
+ public int getIntR() {
+
+ return (_color >> 16) & 0xFF;
+ }
+
+ public int getIntG() {
+
+ return (_color >> 8) & 0xFF;
+ }
+
+ public int getIntB() {
+
+ return (_color >> 0) & 0xFF;
+ }
+
+ public int getIntA() {
+
+ return (_color >> 24) & 0xFF;
+ }
+
+ public float getFloatR() {
+
+ return getIntR() / 255f;
+ }
+
+ public float getFloatG() {
+
+ return getIntG() / 255f;
+ }
+
+ public float getFloatB() {
+
+ return getIntB() / 255f;
+ }
+
+ public float getFloatA() {
+
+ return getIntA() / 255f;
+ }
+
+ // ///////////////////////////////////////////////////////// Math Methods ////////////////////////////////////////////
+ public GuiColor multiply(float amount) {
+
+ return multiply(amount, amount, amount, amount);
+ }
+
+ public GuiColor multiply(float rgb, float a) {
+
+ return multiply(rgb, rgb, rgb, a);
+ }
+
+ public GuiColor multiply(float r, float g, float b) {
+
+ return multiply(r, g, b, 1);
+ }
+
+ public GuiColor multiply(float r, float g, float b, float a) {
+
+ return new GuiColor(Math.min((int) (getIntR() * r), 255), Math.min((int) (getIntG() * g), 255), Math.min((int) (getIntB() * b), 255));
+ }
+
+ public GuiColor add(int amount) {
+
+ return new GuiColor(Math.max(Math.min(getIntR() + amount, 255), 0), Math.max(Math.min(getIntG() + amount, 255), 0), Math.max(
+ Math.min(getIntB() + amount, 255), 0), Math.max(Math.min(getIntA() + amount, 255), 0));
+ }
+
+ public GuiColor add(GuiColor color) {
+
+ return new GuiColor(Math.max(Math.min(getIntR() + color.getIntR(), 255), 0), Math.max(Math.min(getIntG() + color.getIntG(), 255), 0), Math.max(
+ Math.min(getIntB() + color.getIntB(), 255), 0), Math.max(Math.min(getIntA() + color.getIntA(), 255), 0));
+ }
+
+ // ///////////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ public int intValue() {
+
+ return getColor();
+ }
+
+ @Override
+ public long longValue() {
+
+ return getColor();
+ }
+
+ @Override
+ public float floatValue() {
+
+ return getColor();
+ }
+
+ @Override
+ public double doubleValue() {
+
+ return getColor();
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/GuiProps.java b/src/main/java/cofh/lib/gui/GuiProps.java
new file mode 100644
index 00000000..1b66ef13
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/GuiProps.java
@@ -0,0 +1,17 @@
+package cofh.lib.gui;
+
+public class GuiProps {
+
+ /* GUI */
+ public static final String PATH_GFX = "cofh:textures/";
+ public static final String PATH_ARMOR = PATH_GFX + "armor/";
+ public static final String PATH_GUI = PATH_GFX + "gui/";
+ public static final String PATH_RENDER = PATH_GFX + "blocks/";
+ public static final String PATH_ELEMENTS = PATH_GUI + "elements/";
+ public static final String PATH_ICON = PATH_GUI + "icons/";
+
+ private GuiProps() {
+
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/TabTracker.java b/src/main/java/cofh/lib/gui/TabTracker.java
new file mode 100644
index 00000000..1f120e9a
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/TabTracker.java
@@ -0,0 +1,40 @@
+package cofh.lib.gui;
+
+import cofh.lib.gui.element.TabBase;
+
+/**
+ * Keeps track of which tabs should be open by default when a player opens a GUI.
+ *
+ * @author King Lemming
+ *
+ */
+public class TabTracker {
+
+ private static Class extends TabBase> openedLeftTab;
+ private static Class extends TabBase> openedRightTab;
+
+ private TabTracker() {
+
+ }
+
+ public static Class extends TabBase> getOpenedLeftTab() {
+
+ return openedLeftTab;
+ }
+
+ public static Class extends TabBase> getOpenedRightTab() {
+
+ return openedRightTab;
+ }
+
+ public static void setOpenedLeftTab(Class extends TabBase> tabClass) {
+
+ openedLeftTab = tabClass;
+ }
+
+ public static void setOpenedRightTab(Class extends TabBase> tabClass) {
+
+ openedRightTab = tabClass;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/container/ContainerBase.java b/src/main/java/cofh/lib/gui/container/ContainerBase.java
new file mode 100644
index 00000000..560a78ca
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/container/ContainerBase.java
@@ -0,0 +1,152 @@
+package cofh.lib.gui.container;
+
+import cofh.lib.gui.slot.SlotFalseCopy;
+import cofh.lib.util.helpers.InventoryHelper;
+import cofh.lib.util.helpers.MathHelper;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.inventory.Container;
+import net.minecraft.inventory.ICrafting;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+
+public abstract class ContainerBase extends Container {
+
+ public ContainerBase() {
+
+ }
+
+ protected abstract int getPlayerInventoryVerticalOffset();
+
+ protected int getPlayerInventoryHorizontalOffset() {
+
+ return 8;
+ }
+
+ protected void bindPlayerInventory(InventoryPlayer inventoryPlayer) {
+
+ int yOff = getPlayerInventoryVerticalOffset();
+ int xOff = getPlayerInventoryHorizontalOffset();
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 9; j++) {
+ addSlotToContainer(new Slot(inventoryPlayer, j + i * 9 + 9, xOff + j * 18, yOff + i * 18));
+ }
+ }
+
+ for (int i = 0; i < 9; i++) {
+ addSlotToContainer(new Slot(inventoryPlayer, i, xOff + i * 18, yOff + 58));
+ }
+ }
+
+ protected abstract int getSizeInventory();
+
+ protected boolean supportsShiftClick(EntityPlayer player, int slotIndex) {
+
+ return supportsShiftClick(slotIndex);
+ }
+
+ protected boolean supportsShiftClick(int slotIndex) {
+
+ return true;
+ }
+
+ protected boolean performMerge(EntityPlayer player, int slotIndex, ItemStack stack) {
+
+ return performMerge(slotIndex, stack);
+ }
+
+ protected boolean performMerge(int slotIndex, ItemStack stack) {
+
+ int invBase = getSizeInventory();
+ int invFull = inventorySlots.size();
+
+ if (slotIndex < invBase) {
+ return mergeItemStack(stack, invBase, invFull, true);
+ }
+ return mergeItemStack(stack, 0, invBase, false);
+ }
+
+ @Override
+ public ItemStack transferStackInSlot(EntityPlayer player, int slotIndex) {
+
+ if (!supportsShiftClick(player, slotIndex)) {
+ return null;
+ }
+
+ ItemStack stack = null;
+ Slot slot = (Slot) inventorySlots.get(slotIndex);
+
+ if (slot != null && slot.getHasStack()) {
+ ItemStack stackInSlot = slot.getStack();
+ stack = stackInSlot.copy();
+
+ if (!performMerge(player, slotIndex, stackInSlot)) {
+ return null;
+ }
+
+ slot.onSlotChange(stackInSlot, stack);
+
+ if (stackInSlot.stackSize <= 0) {
+ slot.putStack((ItemStack) null);
+ } else {
+ slot.putStack(stackInSlot);
+ }
+
+ if (stackInSlot.stackSize == stack.stackSize) {
+ return null;
+ }
+ slot.onPickupFromSlot(player, stackInSlot);
+ }
+ return stack;
+ }
+
+ protected void sendSlots(int start, int end) {
+
+ start = MathHelper.clamp(start, 0, inventorySlots.size());
+ end = MathHelper.clamp(end, 0, inventorySlots.size());
+ for (; start < end; ++start) {
+ ItemStack itemstack = ((Slot) inventorySlots.get(start)).getStack();
+
+ ItemStack itemstack1 = itemstack == null ? null : itemstack.copy();
+ inventoryItemStacks.set(start, itemstack1);
+
+ for (int j = 0; j < this.crafters.size(); ++j) {
+ ((ICrafting) this.crafters.get(j)).sendSlotContents(this, start, itemstack1);
+ }
+ }
+ }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public void putStacksInSlots(ItemStack[] stacks) {
+
+ for (int i = 0; i < stacks.length; ++i) {
+ putStackInSlot(i, stacks[i]);
+ }
+ }
+
+ @Override
+ public ItemStack slotClick(int slotId, int mouseButton, int modifier, EntityPlayer player) {
+
+ Slot slot = slotId < 0 ? null : (Slot) this.inventorySlots.get(slotId);
+ if (slot instanceof SlotFalseCopy) {
+ if (mouseButton == 2) {
+ slot.putStack(null);
+ } else {
+ slot.putStack(player.inventory.getItemStack() == null ? null : player.inventory.getItemStack().copy());
+ }
+ return player.inventory.getItemStack();
+ }
+ return super.slotClick(slotId, mouseButton, modifier, player);
+ }
+
+ @Override
+ protected boolean mergeItemStack(ItemStack stack, int slotMin, int slotMax, boolean ascending) {
+
+ return InventoryHelper.mergeItemStack(this.inventorySlots, stack, slotMin, slotMax, ascending);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/container/ContainerFalse.java b/src/main/java/cofh/lib/gui/container/ContainerFalse.java
new file mode 100644
index 00000000..ca36a497
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/container/ContainerFalse.java
@@ -0,0 +1,20 @@
+package cofh.lib.gui.container;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.Container;
+
+/**
+ * Basic false container class. You'll know if you need one.
+ *
+ * @author King Lemming
+ *
+ */
+public final class ContainerFalse extends Container {
+
+ @Override
+ public boolean canInteractWith(EntityPlayer player) {
+
+ return false;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/container/ContainerInventoryItem.java b/src/main/java/cofh/lib/gui/container/ContainerInventoryItem.java
new file mode 100644
index 00000000..51fd413d
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/container/ContainerInventoryItem.java
@@ -0,0 +1,354 @@
+package cofh.lib.gui.container;
+
+import java.util.Iterator;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+
+public abstract class ContainerInventoryItem extends ContainerBase {
+
+ protected final InventoryContainerItemWrapper containerWrapper;
+ protected final EntityPlayer player;
+ protected final int containerIndex;
+ protected boolean valid = true;
+
+ public ContainerInventoryItem(ItemStack stack, InventoryPlayer inventory) {
+
+ player = inventory.player;
+ containerIndex = inventory.currentItem;
+ containerWrapper = new InventoryContainerItemWrapper(stack);
+ }
+
+ @Override
+ protected int getSizeInventory() {
+
+ return containerWrapper.getSizeInventory();
+ }
+
+ public ItemStack getContainerStack() {
+
+ return containerWrapper.getContainerStack();
+ }
+
+ public String getInventoryName() {
+
+ return containerWrapper.getInventoryName();
+ }
+
+ @Override
+ public void detectAndSendChanges() {
+
+ ItemStack item = player.inventory.mainInventory[containerIndex];
+ if (item == null || item.getItem() != containerWrapper.getContainerItem()) {
+ valid = false;
+ return;
+ }
+ super.detectAndSendChanges();
+ }
+
+ public void onSlotChanged() {
+
+ ItemStack item = player.inventory.mainInventory[containerIndex];
+ if (valid && item != null && item.getItem() == containerWrapper.getContainerItem()) {
+ player.inventory.mainInventory[containerIndex] = containerWrapper.getContainerStack();
+ }
+ }
+
+ @Override
+ public boolean canInteractWith(EntityPlayer player) {
+
+ onSlotChanged();
+ if (containerWrapper.getDirty() && !valid)
+ player.inventory.setItemStack(null);
+ return valid;
+ }
+
+ @Override
+ protected boolean performMerge(int slotIndex, ItemStack stack) {
+
+ int invPlayer = 27;
+ int invFull = invPlayer + 9;
+ int invTile = invFull + getSizeInventory();
+
+ if (slotIndex < invFull) {
+ return mergeItemStack(stack, invFull, invTile, false);
+ }
+ return mergeItemStack(stack, 0, invFull, true);
+ }
+
+ @Override
+ public ItemStack slotClick(int slotIndex, int mouseButton, int modifier, EntityPlayer player) {
+
+ if (modifier == 2 && mouseButton == containerIndex) {
+ return null;
+ }
+ ItemStack stack = null;
+ InventoryPlayer inventoryPlayer = player.inventory;
+ int i1;
+ ItemStack itemstack3;
+
+ if (modifier == 5) {
+ int l = this.field_94536_g;
+ this.field_94536_g = func_94532_c(mouseButton);
+
+ if ((l != 1 || this.field_94536_g != 2) && l != this.field_94536_g) {
+ this.func_94533_d();
+ } else if (inventoryPlayer.getItemStack() == null) {
+ this.func_94533_d();
+ } else if (this.field_94536_g == 0) {
+ this.field_94535_f = func_94529_b(mouseButton);
+
+ if (func_94528_d(this.field_94535_f)) {
+ this.field_94536_g = 1;
+ this.field_94537_h.clear();
+ } else {
+ this.func_94533_d();
+ }
+ } else if (this.field_94536_g == 1) {
+ Slot slot = (Slot) this.inventorySlots.get(slotIndex);
+
+ if (slot != null && func_94527_a(slot, inventoryPlayer.getItemStack(), true) && slot.isItemValid(inventoryPlayer.getItemStack())
+ && inventoryPlayer.getItemStack().stackSize > this.field_94537_h.size() && this.canDragIntoSlot(slot)) {
+ this.field_94537_h.add(slot);
+ }
+ } else if (this.field_94536_g == 2) {
+ if (!this.field_94537_h.isEmpty()) {
+ itemstack3 = inventoryPlayer.getItemStack().copy();
+ i1 = inventoryPlayer.getItemStack().stackSize;
+ Iterator iterator = this.field_94537_h.iterator();
+
+ while (iterator.hasNext()) {
+ Slot slot1 = iterator.next();
+
+ if (slot1 != null && func_94527_a(slot1, inventoryPlayer.getItemStack(), true) && slot1.isItemValid(inventoryPlayer.getItemStack())
+ && inventoryPlayer.getItemStack().stackSize >= this.field_94537_h.size() && this.canDragIntoSlot(slot1)) {
+ ItemStack itemstack1 = itemstack3.copy();
+ int j1 = slot1.getHasStack() ? slot1.getStack().stackSize : 0;
+ func_94525_a(this.field_94537_h, this.field_94535_f, itemstack1, j1);
+
+ if (itemstack1.stackSize > itemstack1.getMaxStackSize()) {
+ itemstack1.stackSize = itemstack1.getMaxStackSize();
+ }
+ if (itemstack1.stackSize > slot1.getSlotStackLimit()) {
+ itemstack1.stackSize = slot1.getSlotStackLimit();
+ }
+ i1 -= itemstack1.stackSize - j1;
+ slot1.putStack(itemstack1);
+ }
+ }
+
+ itemstack3.stackSize = i1;
+
+ if (itemstack3.stackSize <= 0) {
+ itemstack3 = null;
+ }
+ inventoryPlayer.setItemStack(itemstack3);
+ }
+ this.func_94533_d();
+ } else {
+ this.func_94533_d();
+ }
+ } else if (this.field_94536_g != 0) {
+ this.func_94533_d();
+ } else {
+ Slot slot2;
+ int l1;
+ ItemStack itemstack5;
+
+ if ((modifier == 0 || modifier == 1) && (mouseButton == 0 || mouseButton == 1)) {
+ if (slotIndex == -999) {
+ if (inventoryPlayer.getItemStack() != null && slotIndex == -999) {
+ if (mouseButton == 0) {
+ player.dropPlayerItemWithRandomChoice(inventoryPlayer.getItemStack(), true);
+ inventoryPlayer.setItemStack((ItemStack) null);
+ }
+
+ if (mouseButton == 1) {
+ player.dropPlayerItemWithRandomChoice(inventoryPlayer.getItemStack().splitStack(1), true);
+
+ if (inventoryPlayer.getItemStack().stackSize == 0) {
+ inventoryPlayer.setItemStack((ItemStack) null);
+ }
+ }
+ }
+ } else if (modifier == 1) {
+ if (slotIndex < 0) {
+ return null;
+ }
+ slot2 = (Slot) this.inventorySlots.get(slotIndex);
+
+ if (slot2 != null && slot2.canTakeStack(player)) {
+ itemstack3 = this.transferStackInSlot(player, slotIndex);
+
+ if (itemstack3 != null) {
+ Item item = itemstack3.getItem();
+ stack = itemstack3.copy();
+
+ if (slot2.getStack() != null && slot2.getStack().getItem() == item) {
+ this.retrySlotClick(slotIndex, mouseButton, true, player);
+ }
+ }
+ }
+ } else {
+ if (slotIndex < 0) {
+ return null;
+ }
+ slot2 = (Slot) this.inventorySlots.get(slotIndex);
+
+ if (slot2 != null) {
+ itemstack3 = slot2.getStack();
+ ItemStack itemstack4 = inventoryPlayer.getItemStack();
+
+ if (itemstack3 != null) {
+ stack = itemstack3.copy();
+ }
+ if (itemstack3 == null) {
+ if (itemstack4 != null && slot2.isItemValid(itemstack4)) {
+ l1 = mouseButton == 0 ? itemstack4.stackSize : 1;
+
+ if (l1 > slot2.getSlotStackLimit()) {
+ l1 = slot2.getSlotStackLimit();
+ }
+ if (itemstack4.stackSize >= l1) {
+ slot2.putStack(itemstack4.splitStack(l1));
+ }
+ if (itemstack4.stackSize == 0) {
+ inventoryPlayer.setItemStack((ItemStack) null);
+ }
+ }
+ } else if (slot2.canTakeStack(player)) {
+ if (itemstack4 == null) {
+ l1 = mouseButton == 0 ? itemstack3.stackSize : (itemstack3.stackSize + 1) / 2;
+ itemstack5 = slot2.decrStackSize(l1);
+ inventoryPlayer.setItemStack(itemstack5);
+
+ if (itemstack3.stackSize == 0) {
+ slot2.putStack((ItemStack) null);
+ }
+ slot2.onPickupFromSlot(player, inventoryPlayer.getItemStack());
+ } else if (slot2.isItemValid(itemstack4)) {
+ if (itemstack3.getItem() == itemstack4.getItem() && itemstack3.getItemDamage() == itemstack4.getItemDamage()
+ && ItemStack.areItemStackTagsEqual(itemstack3, itemstack4)) {
+ l1 = mouseButton == 0 ? itemstack4.stackSize : 1;
+
+ if (l1 > slot2.getSlotStackLimit() - itemstack3.stackSize) {
+ l1 = slot2.getSlotStackLimit() - itemstack3.stackSize;
+ }
+ if (l1 > itemstack4.getMaxStackSize() - itemstack3.stackSize) {
+ l1 = itemstack4.getMaxStackSize() - itemstack3.stackSize;
+ }
+ itemstack4.splitStack(l1);
+
+ if (itemstack4.stackSize == 0) {
+ inventoryPlayer.setItemStack((ItemStack) null);
+ }
+ itemstack3.stackSize += l1;
+ slot2.putStack(itemstack3);
+ } else if (itemstack4.stackSize <= slot2.getSlotStackLimit()) {
+ slot2.putStack(itemstack4);
+ inventoryPlayer.setItemStack(itemstack3);
+ }
+ } else if (itemstack3.getItem() == itemstack4.getItem() && itemstack4.getMaxStackSize() > 1
+ && (!itemstack3.getHasSubtypes() || itemstack3.getItemDamage() == itemstack4.getItemDamage())
+ && ItemStack.areItemStackTagsEqual(itemstack3, itemstack4)) {
+ l1 = itemstack3.stackSize;
+
+ if (l1 > 0 && l1 + itemstack4.stackSize <= itemstack4.getMaxStackSize()) {
+ itemstack4.stackSize += l1;
+ itemstack3 = slot2.decrStackSize(l1);
+
+ if (itemstack3.stackSize == 0) {
+ slot2.putStack((ItemStack) null);
+ }
+ slot2.onPickupFromSlot(player, inventoryPlayer.getItemStack());
+ }
+ }
+ }
+ slot2.onSlotChanged();
+ }
+ }
+ } else if (modifier == 2 && mouseButton >= 0 && mouseButton < 9) {
+ slot2 = (Slot) this.inventorySlots.get(slotIndex);
+
+ if (slot2.canTakeStack(player)) {
+ itemstack3 = inventoryPlayer.getStackInSlot(mouseButton);
+ boolean flag = itemstack3 == null || slot2.inventory == inventoryPlayer && slot2.isItemValid(itemstack3);
+ l1 = -1;
+
+ if (!flag) {
+ l1 = inventoryPlayer.getFirstEmptyStack();
+ flag |= l1 > -1;
+ }
+ if (slot2.getHasStack() && flag) {
+ itemstack5 = slot2.getStack();
+ inventoryPlayer.setInventorySlotContents(mouseButton, itemstack5.copy());
+
+ if ((slot2.inventory != inventoryPlayer || !slot2.isItemValid(itemstack3)) && itemstack3 != null) {
+ if (l1 > -1) {
+ inventoryPlayer.addItemStackToInventory(itemstack3);
+ slot2.decrStackSize(itemstack5.stackSize);
+ slot2.putStack((ItemStack) null);
+ slot2.onPickupFromSlot(player, itemstack5);
+ }
+ } else {
+ slot2.decrStackSize(itemstack5.stackSize);
+ slot2.putStack(itemstack3);
+ slot2.onPickupFromSlot(player, itemstack5);
+ }
+ } else if (!slot2.getHasStack() && itemstack3 != null && slot2.isItemValid(itemstack3)) {
+ inventoryPlayer.setInventorySlotContents(mouseButton, (ItemStack) null);
+ slot2.putStack(itemstack3);
+ }
+ }
+ } else if (modifier == 3 && player.capabilities.isCreativeMode && inventoryPlayer.getItemStack() == null && slotIndex >= 0) {
+ slot2 = (Slot) this.inventorySlots.get(slotIndex);
+
+ if (slot2 != null && slot2.getHasStack()) {
+ itemstack3 = slot2.getStack().copy();
+ itemstack3.stackSize = itemstack3.getMaxStackSize();
+ inventoryPlayer.setItemStack(itemstack3);
+ }
+ } else if (modifier == 4 && inventoryPlayer.getItemStack() == null && slotIndex >= 0) {
+ slot2 = (Slot) this.inventorySlots.get(slotIndex);
+
+ if (slot2 != null && slot2.getHasStack() && slot2.canTakeStack(player)) {
+ itemstack3 = slot2.decrStackSize(mouseButton == 0 ? 1 : slot2.getStack().stackSize);
+ slot2.onPickupFromSlot(player, itemstack3);
+ player.dropPlayerItemWithRandomChoice(itemstack3, true);
+ }
+ } else if (modifier == 6 && slotIndex >= 0) {
+ slot2 = (Slot) this.inventorySlots.get(slotIndex);
+ itemstack3 = inventoryPlayer.getItemStack();
+
+ if (itemstack3 != null && (slot2 == null || !slot2.getHasStack() || !slot2.canTakeStack(player))) {
+ i1 = mouseButton == 0 ? 0 : this.inventorySlots.size() - 1;
+ l1 = mouseButton == 0 ? 1 : -1;
+
+ for (int i2 = 0; i2 < 2; ++i2) {
+ for (int j2 = i1; j2 >= 0 && j2 < this.inventorySlots.size() && itemstack3.stackSize < itemstack3.getMaxStackSize(); j2 += l1) {
+ Slot slot3 = (Slot) this.inventorySlots.get(j2);
+
+ if (slot3.getHasStack() && func_94527_a(slot3, itemstack3, true) && slot3.canTakeStack(player)
+ && this.func_94530_a(itemstack3, slot3) && (i2 != 0 || slot3.getStack().stackSize != slot3.getStack().getMaxStackSize())) {
+ int k1 = Math.min(itemstack3.getMaxStackSize() - itemstack3.stackSize, slot3.getStack().stackSize);
+ ItemStack itemstack2 = slot3.decrStackSize(k1);
+ itemstack3.stackSize += k1;
+
+ if (itemstack2.stackSize <= 0) {
+ slot3.putStack((ItemStack) null);
+ }
+ slot3.onPickupFromSlot(player, itemstack2);
+ }
+ }
+ }
+ }
+ this.detectAndSendChanges();
+ }
+ }
+ return stack;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/container/CustomInventoryWrapper.java b/src/main/java/cofh/lib/gui/container/CustomInventoryWrapper.java
new file mode 100644
index 00000000..b1f843a2
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/container/CustomInventoryWrapper.java
@@ -0,0 +1,113 @@
+package cofh.lib.gui.container;
+
+import cofh.api.core.ICustomInventory;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.item.ItemStack;
+
+public class CustomInventoryWrapper implements IInventory {
+
+ private final ItemStack[] inventory;
+
+ public CustomInventoryWrapper(ICustomInventory customInv, int inventoryIndex) {
+
+ inventory = customInv.getInventorySlots(0);
+ }
+
+ @Override
+ public int getSizeInventory() {
+
+ return inventory.length;
+ }
+
+ @Override
+ public ItemStack getStackInSlot(int slot) {
+
+ return inventory[slot];
+ }
+
+ @Override
+ public ItemStack decrStackSize(int slot, int amount) {
+
+ if (inventory[slot] == null) {
+ return null;
+ }
+ if (inventory[slot].stackSize <= amount) {
+ amount = inventory[slot].stackSize;
+ }
+ ItemStack stack = inventory[slot].splitStack(amount);
+
+ if (inventory[slot].stackSize <= 0) {
+ inventory[slot] = null;
+ }
+ return stack;
+ }
+
+ @Override
+ public ItemStack getStackInSlotOnClosing(int slot) {
+
+ if (inventory[slot] == null) {
+ return null;
+ }
+ ItemStack stack = inventory[slot];
+ inventory[slot] = null;
+ return stack;
+ }
+
+ @Override
+ public void setInventorySlotContents(int slot, ItemStack stack) {
+
+ inventory[slot] = stack;
+
+ if (stack != null && stack.stackSize > getInventoryStackLimit()) {
+ stack.stackSize = getInventoryStackLimit();
+ }
+ }
+
+ @Override
+ public String getInventoryName() {
+
+ return "container.crafting";
+ }
+
+ @Override
+ public boolean hasCustomInventoryName() {
+
+ return false;
+ }
+
+ @Override
+ public int getInventoryStackLimit() {
+
+ return 64;
+ }
+
+ @Override
+ public void markDirty() {
+
+ }
+
+ @Override
+ public boolean isUseableByPlayer(EntityPlayer player) {
+
+ return true;
+ }
+
+ @Override
+ public void openInventory() {
+
+ }
+
+ @Override
+ public void closeInventory() {
+
+ }
+
+ @Override
+ public boolean isItemValidForSlot(int slot, ItemStack stack) {
+
+ return true;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/container/IAugmentableContainer.java b/src/main/java/cofh/lib/gui/container/IAugmentableContainer.java
new file mode 100644
index 00000000..a0f4d5d4
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/container/IAugmentableContainer.java
@@ -0,0 +1,23 @@
+package cofh.lib.gui.container;
+
+import net.minecraft.inventory.Slot;
+
+/**
+ * Implement this interface on Container objects (the backend of a GUI). These are basically passthrough functions which should call back to the Tile Entity.
+ *
+ * @author King Lemming
+ *
+ */
+public interface IAugmentableContainer {
+
+ /**
+ * Used by a tab to set lock status so that new Augments cannot be added (there may be many reasons for this).
+ */
+ void setAugmentLock(boolean lock);
+
+ /**
+ * Returns the Augment slots.
+ */
+ Slot[] getAugmentSlots();
+
+}
diff --git a/src/main/java/cofh/lib/gui/container/InventoryContainerItemWrapper.java b/src/main/java/cofh/lib/gui/container/InventoryContainerItemWrapper.java
new file mode 100644
index 00000000..fbfb5993
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/container/InventoryContainerItemWrapper.java
@@ -0,0 +1,191 @@
+package cofh.lib.gui.container;
+
+import cofh.api.item.IInventoryContainerItem;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+
+public class InventoryContainerItemWrapper implements IInventory {
+
+ protected final IInventoryContainerItem inventoryItem;
+ protected final ItemStack stack;
+ protected NBTTagCompound tag;
+ protected ItemStack[] inventory;
+ protected boolean dirty = false;
+
+ @Deprecated
+ public InventoryContainerItemWrapper(ContainerInventoryItem gui, ItemStack stack) {
+
+ this(stack);
+ }
+
+ public InventoryContainerItemWrapper(ItemStack itemstack) {
+
+ stack = itemstack;
+ inventoryItem = (IInventoryContainerItem) stack.getItem();
+ inventory = new ItemStack[getSizeInventory()];
+
+ loadInventory();
+ markDirty();
+ }
+
+ protected void loadInventory() {
+
+ boolean loaded = false;
+ if (stack.stackTagCompound == null || !stack.stackTagCompound.hasKey("Inventory")) {
+ loaded = stack.hasTagCompound();
+ if (loaded) {
+ if (stack.stackTagCompound.hasKey("inventory")) {
+ tag = stack.stackTagCompound.getCompoundTag("inventory");
+ stack.stackTagCompound.removeTag("inventory");
+ } else {
+ tag = stack.stackTagCompound;
+ }
+ loadStacks();
+ tag = new NBTTagCompound();
+ saveStacks();
+ } else {
+ stack.setTagInfo("Inventory", new NBTTagCompound());
+ }
+ }
+ tag = stack.stackTagCompound.getCompoundTag("Inventory");
+ loadStacks();
+ }
+
+ protected void loadStacks() {
+
+ for (int i = inventory.length; i-- > 0;) {
+ if (tag.hasKey("Slot" + i)) {
+ inventory[i] = ItemStack.loadItemStackFromNBT(tag.getCompoundTag("Slot" + i));
+ } else if (tag.hasKey("slot" + i)) {
+ inventory[i] = ItemStack.loadItemStackFromNBT(tag.getCompoundTag("slot" + i));
+ } else {
+ inventory[i] = null;
+ }
+ }
+ }
+
+ protected void saveStacks() {
+
+ for (int i = inventory.length; i-- > 0;) {
+ if (inventory[i] == null) {
+ tag.removeTag("Slot" + i);
+ } else {
+ tag.setTag("Slot" + i, inventory[i].writeToNBT(new NBTTagCompound()));
+ }
+ }
+ stack.setTagInfo("Inventory", tag);
+ }
+
+ @Override
+ public void markDirty() {
+
+ saveStacks();
+ dirty = true;
+ }
+
+ public boolean getDirty() {
+
+ boolean r = dirty;
+ dirty = false;
+ return r;
+ }
+
+ public Item getContainerItem() {
+
+ return stack.getItem();
+ }
+
+ public ItemStack getContainerStack() {
+
+ saveStacks();
+ return stack;
+ }
+
+ @Override
+ public int getSizeInventory() {
+
+ return inventoryItem.getSizeInventory(stack);
+ }
+
+ @Override
+ public ItemStack getStackInSlot(int i) {
+
+ return inventory[i];
+ }
+
+ @Override
+ public ItemStack decrStackSize(int i, int j) {
+
+ ItemStack s = inventory[i];
+ if (s == null) {
+ return null;
+ }
+ ItemStack r = s.splitStack(j);
+ if (s.stackSize <= 0) {
+ inventory[i] = null;
+ r.stackSize += s.stackSize;
+ }
+ return r;
+ }
+
+ @Override
+ public void setInventorySlotContents(int i, ItemStack itemstack) {
+
+ inventory[i] = itemstack;
+ }
+
+ @Override
+ public ItemStack getStackInSlotOnClosing(int slot) {
+
+ return null;
+ }
+
+ @Override
+ public boolean isItemValidForSlot(int slot, ItemStack stack) {
+
+ if (stack != null && stack.getItem() instanceof IInventoryContainerItem) {
+ return ((IInventoryContainerItem) stack.getItem()).getSizeInventory(stack) <= 0;
+ }
+ return true;
+ }
+
+ @Override
+ public String getInventoryName() {
+
+ return stack.getDisplayName();
+ }
+
+ @Override
+ public boolean hasCustomInventoryName() {
+
+ return true;
+ }
+
+ @Override
+ public int getInventoryStackLimit() {
+
+ return 64;
+ }
+
+ @Override
+ public boolean isUseableByPlayer(EntityPlayer player) {
+
+ return true;
+ }
+
+ @Override
+ public void openInventory() {
+
+ }
+
+ @Override
+ public void closeInventory() {
+
+ markDirty();
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/container/package-info.java b/src/main/java/cofh/lib/gui/container/package-info.java
new file mode 100644
index 00000000..87f8c213
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/container/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHLibProps.VERSION, owner = "CoFHLib", provides = "CoFHLib|gui|container")
+package cofh.lib.gui.container;
+
+import cofh.lib.CoFHLibProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/lib/gui/element/ElementBase.java b/src/main/java/cofh/lib/gui/element/ElementBase.java
new file mode 100644
index 00000000..965d2664
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementBase.java
@@ -0,0 +1,237 @@
+package cofh.lib.gui.element;
+
+import static org.lwjgl.opengl.GL11.*;
+
+import cofh.lib.gui.GuiBase;
+
+import java.util.List;
+
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.util.ResourceLocation;
+
+/**
+ * Base class for a modular GUI element. Has self-contained rendering methods and a link back to the {@link GuiBase} it is a part of.
+ *
+ * @author King Lemming
+ *
+ */
+public abstract class ElementBase {
+
+ protected GuiBase gui;
+ protected ResourceLocation texture;
+ private FontRenderer fontRenderer;
+
+ protected int posX;
+ protected int posY;
+
+ protected int sizeX;
+ protected int sizeY;
+
+ protected int texW = 256;
+ protected int texH = 256;
+
+ protected String name;
+
+ private boolean visible = true;
+ private boolean enabled = true;
+
+ public ElementBase(GuiBase gui, int posX, int posY) {
+
+ this.gui = gui;
+ this.posX = posX;
+ this.posY = posY;
+ }
+
+ public ElementBase(GuiBase gui, int posX, int posY, int width, int height) {
+
+ this.gui = gui;
+ this.posX = posX;
+ this.posY = posY;
+ this.sizeX = width;
+ this.sizeY = height;
+ }
+
+ public ElementBase setName(String name) {
+
+ this.name = name;
+ return this;
+ }
+
+ public ElementBase setPosition(int posX, int posY) {
+
+ this.posX = posX;
+ this.posY = posY;
+ return this;
+ }
+
+ public ElementBase setSize(int sizeX, int sizeY) {
+
+ this.sizeX = sizeX;
+ this.sizeY = sizeY;
+ return this;
+ }
+
+ public ElementBase setTexture(String texture, int texW, int texH) {
+
+ this.texture = new ResourceLocation(texture);
+ this.texW = texW;
+ this.texH = texH;
+ return this;
+ }
+
+ public final ElementBase setVisible(boolean visible) {
+
+ this.visible = visible;
+ return this;
+ }
+
+ public boolean isVisible() {
+
+ return visible;
+ }
+
+ public final ElementBase setEnabled(boolean enabled) {
+
+ this.enabled = enabled;
+ return this;
+ }
+
+ public boolean isEnabled() {
+
+ return enabled;
+ }
+
+ public void update(int mouseX, int mouseY) {
+
+ update();
+ }
+
+ public void update() {
+
+ }
+
+ public abstract void drawBackground(int mouseX, int mouseY, float gameTicks);
+
+ public abstract void drawForeground(int mouseX, int mouseY);
+
+ public void addTooltip(List list) {
+
+ }
+
+ public void drawModalRect(int x, int y, int width, int height, int color) {
+
+ gui.drawSizedModalRect(x, y, width, height, color);
+ }
+
+ public void drawStencil(int xStart, int yStart, int xEnd, int yEnd, int flag) {
+
+ glDisable(GL_TEXTURE_2D);
+ glStencilFunc(GL_ALWAYS, flag, flag);
+ glStencilOp(GL_ZERO, GL_ZERO, GL_REPLACE);
+ glStencilMask(flag);
+ glColorMask(false, false, false, false);
+ glDepthMask(false);
+ glClearStencil(0);
+ glClear(GL_STENCIL_BUFFER_BIT);
+
+ Tessellator.instance.startDrawingQuads();
+ Tessellator.instance.addVertex(xStart, yEnd, 0);
+ Tessellator.instance.addVertex(xEnd, yEnd, 0);
+ Tessellator.instance.addVertex(xEnd, yStart, 0);
+ Tessellator.instance.addVertex(xStart, yStart, 0);
+ Tessellator.instance.draw();
+
+ glEnable(GL_TEXTURE_2D);
+ glStencilFunc(GL_EQUAL, flag, flag);
+ glStencilMask(0);
+ glColorMask(true, true, true, true);
+ glDepthMask(true);
+ }
+
+ public void drawTexturedModalRect(int x, int y, int u, int v, int width, int height) {
+
+ gui.drawSizedTexturedModalRect(x, y, u, v, width, height, texW, texH);
+ }
+
+ public void drawCenteredString(FontRenderer fontRenderer, String text, int x, int y, int color) {
+
+ fontRenderer.drawStringWithShadow(text, x - fontRenderer.getStringWidth(text) / 2, y, color);
+ }
+
+ public boolean onMousePressed(int mouseX, int mouseY, int mouseButton) {
+
+ return false;
+ }
+
+ public void onMouseReleased(int mouseX, int mouseY) {
+
+ return;
+ }
+
+ public boolean onMouseWheel(int mouseX, int mouseY, int movement) {
+
+ return false;
+ }
+
+ public boolean onKeyTyped(char characterTyped, int keyPressed) {
+
+ return false;
+ }
+
+ public boolean intersectsWith(int mouseX, int mouseY) {
+
+ if (mouseX >= this.posX && mouseX <= this.posX + this.sizeX && mouseY >= this.posY && mouseY <= this.posY + this.sizeY) {
+ return true;
+ }
+ return false;
+ }
+
+ public FontRenderer getFontRenderer() {
+
+ return fontRenderer == null ? gui.getFontRenderer() : fontRenderer;
+ }
+
+ public ElementBase setFontRenderer(FontRenderer renderer) {
+
+ fontRenderer = renderer;
+ return this;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final GuiBase getContainerScreen() {
+
+ return gui;
+ }
+
+ /**
+ * This method is relative to the GUI's y coordinate
+ */
+ public final int getPosY() {
+
+ return posY;
+ }
+
+ /**
+ * This method is relative to the GUI's x coordinate
+ */
+ public final int getPosX() {
+
+ return posX;
+ }
+
+ public final int getHeight() {
+
+ return sizeY;
+ }
+
+ public final int getWidth() {
+
+ return sizeX;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementButton.java b/src/main/java/cofh/lib/gui/element/ElementButton.java
new file mode 100644
index 00000000..94ee41f6
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementButton.java
@@ -0,0 +1,185 @@
+package cofh.lib.gui.element;
+
+import cofh.lib.gui.GuiBase;
+import cofh.lib.render.RenderHelper;
+import cofh.lib.util.helpers.StringHelper;
+
+import java.util.List;
+
+import org.lwjgl.opengl.GL11;
+
+public class ElementButton extends ElementButtonBase {
+
+ int sheetX;
+ int sheetY;
+ int hoverX;
+ int hoverY;
+ int disabledX = 0;
+ int disabledY = 0;
+ boolean tooltipLocalized = false;
+ boolean managedClicks;
+ String tooltip;
+
+ public ElementButton(GuiBase gui, int posX, int posY, int sizeX, int sizeY, int sheetX, int sheetY, int hoverX, int hoverY, String texture) {
+
+ super(gui, posX, posY, sizeX, sizeY);
+ setGuiManagedClicks(false);
+ setTexture(texture, texW, texH);
+ this.sheetX = sheetX;
+ this.sheetY = sheetY;
+ this.hoverX = hoverX;
+ this.hoverY = hoverY;
+ }
+
+ public ElementButton(GuiBase gui, int posX, int posY, int sizeX, int sizeY, int sheetX, int sheetY, int hoverX, int hoverY, int disabledX, int disabledY,
+ String texture) {
+
+ this(gui, posX, posY, sizeX, sizeY, sheetX, sheetY, hoverX, hoverY, texture);
+ this.disabledX = disabledX;
+ this.disabledY = disabledY;
+ }
+
+ public ElementButton(GuiBase gui, int posX, int posY, String name, int sheetX, int sheetY, int hoverX, int hoverY, int sizeX, int sizeY, String texture) {
+
+ super(gui, posX, posY, sizeX, sizeY);
+ setGuiManagedClicks(true);
+ setName(name);
+ setTexture(texture, texW, texH);
+ this.sheetX = sheetX;
+ this.sheetY = sheetY;
+ this.hoverX = hoverX;
+ this.hoverY = hoverY;
+ }
+
+ public ElementButton(GuiBase gui, int posX, int posY, String name, int sheetX, int sheetY, int hoverX, int hoverY, int disabledX, int disabledY, int sizeX,
+ int sizeY, String texture) {
+
+ this(gui, posX, posY, name, sheetX, sheetY, hoverX, hoverY, sizeX, sizeY, texture);
+ this.disabledX = disabledX;
+ this.disabledY = disabledY;
+ }
+
+ public ElementButton setGuiManagedClicks(boolean managed) {
+
+ this.managedClicks = managed;
+ return this;
+ }
+
+ public ElementButton clearToolTip() {
+
+ this.tooltip = null;
+ return this;
+ }
+
+ public ElementButton setToolTip(String tooltip) {
+
+ this.tooltip = tooltip;
+ return this;
+ }
+
+ public ElementButton setToolTipLocalized(boolean localized) {
+
+ this.tooltipLocalized = localized;
+ return this;
+ }
+
+ public ElementButton setToolTipLocalized(String tooltip) {
+
+ return setToolTip(tooltip).setToolTipLocalized(true);
+ }
+
+ @Override
+ public void drawBackground(int mouseX, int mouseY, float gameTicks) {
+
+ GL11.glColor4f(1, 1, 1, 1);
+ RenderHelper.bindTexture(texture);
+ if (isEnabled()) {
+ if (intersectsWith(mouseX, mouseY)) {
+
+ drawTexturedModalRect(posX, posY, hoverX, hoverY, sizeX, sizeY);
+ } else {
+ drawTexturedModalRect(posX, posY, sheetX, sheetY, sizeX, sizeY);
+ }
+ } else {
+ drawTexturedModalRect(posX, posY, disabledX, disabledY, sizeX, sizeY);
+ }
+ }
+
+ @Override
+ public void drawForeground(int mouseX, int mouseY) {
+
+ }
+
+ @Override
+ public void addTooltip(List list) {
+
+ if (tooltip != null) {
+ if (tooltipLocalized) {
+ list.add(tooltip);
+ } else {
+ list.add(StringHelper.localize(tooltip));
+ }
+ }
+ }
+
+ @Override
+ public void onClick() {
+
+ }
+
+ @Override
+ public boolean onMousePressed(int x, int y, int mouseButton) {
+
+ if (!managedClicks) {
+ return super.onMousePressed(x, y, mouseButton);
+ }
+ if (isEnabled()) {
+ gui.handleElementButtonClick(getName(), mouseButton);
+ return true;
+ }
+ return false;
+ }
+
+ public void setSheetX(int pos) {
+
+ sheetX = pos;
+ }
+
+ public void setSheetY(int pos) {
+
+ sheetY = pos;
+ }
+
+ public void setHoverX(int pos) {
+
+ hoverX = pos;
+ }
+
+ public void setHoverY(int pos) {
+
+ hoverY = pos;
+ }
+
+ public ElementButton setDisabledX(int pos) {
+
+ disabledX = pos;
+ return this;
+ }
+
+ public ElementButton setDisabledY(int pos) {
+
+ disabledY = pos;
+ return this;
+ }
+
+ public void setActive() {
+
+ setEnabled(true);
+ }
+
+ public void setDisabled() {
+
+ setEnabled(false);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementButtonBase.java b/src/main/java/cofh/lib/gui/element/ElementButtonBase.java
new file mode 100644
index 00000000..695a2821
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementButtonBase.java
@@ -0,0 +1,55 @@
+package cofh.lib.gui.element;
+
+import cofh.lib.gui.GuiBase;
+import cofh.lib.gui.GuiProps;
+
+import net.minecraft.util.ResourceLocation;
+
+public abstract class ElementButtonBase extends ElementBase {
+
+ public static final ResourceLocation HOVER = new ResourceLocation(GuiProps.PATH_ELEMENTS + "Button_Hover.png");
+ public static final ResourceLocation ENABLED = new ResourceLocation(GuiProps.PATH_ELEMENTS + "Button_Enabled.png");
+ public static final ResourceLocation DISABLED = new ResourceLocation(GuiProps.PATH_ELEMENTS + "Button_Disabled.png");
+
+ public ElementButtonBase(GuiBase containerScreen, int posX, int posY, int sizeX, int sizeY) {
+
+ super(containerScreen, posX, posY, sizeX, sizeY);
+ }
+
+ @Override
+ public boolean onMousePressed(int mouseX, int mouseY, int mouseButton) {
+
+ playSound(mouseButton);
+ switch (mouseButton) {
+ case 0:
+ onClick();
+ break;
+ case 1:
+ onRightClick();
+ break;
+ case 2:
+ onMiddleClick();
+ break;
+ }
+ return true;
+ }
+
+ protected void playSound(int button) {
+
+ if (button == 0) {
+ GuiBase.playSound("random.click", 1.0F, 1.0F);
+ }
+ }
+
+ public void onClick() {
+
+ }
+
+ public void onRightClick() {
+
+ }
+
+ public void onMiddleClick() {
+
+ }
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementButtonManaged.java b/src/main/java/cofh/lib/gui/element/ElementButtonManaged.java
new file mode 100644
index 00000000..09204a49
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementButtonManaged.java
@@ -0,0 +1,72 @@
+package cofh.lib.gui.element;
+
+import cofh.lib.gui.GuiBase;
+
+import org.lwjgl.opengl.GL11;
+
+public abstract class ElementButtonManaged extends ElementButtonBase {
+
+ private String _text;
+
+ public ElementButtonManaged(GuiBase containerScreen, int posX, int posY, int sizeX, int sizeY, String text) {
+
+ super(containerScreen, posX, posY, sizeX, sizeY);
+ _text = text;
+ }
+
+ public void setText(String text) {
+
+ _text = text;
+ }
+
+ public String getText() {
+
+ return _text;
+ }
+
+ protected void bindTexture(int mouseX, int mouseY) {
+
+ if (!isEnabled()) {
+ gui.bindTexture(DISABLED);
+ } else if (intersectsWith(mouseX, mouseY)) {
+ gui.bindTexture(HOVER);
+ } else {
+ gui.bindTexture(ENABLED);
+ }
+ GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
+
+ }
+
+ @Override
+ public void drawBackground(int mouseX, int mouseY, float gameTicks) {
+
+ bindTexture(mouseX, mouseY);
+
+ drawTexturedModalRect(posX, posY, 0, 0, sizeX / 2, sizeY / 2);
+ drawTexturedModalRect(posX, posY + sizeY / 2, 0, 256 - sizeY / 2, sizeX / 2, sizeY / 2);
+ drawTexturedModalRect(posX + sizeX / 2, posY, 256 - sizeX / 2, 0, sizeX / 2, sizeY / 2);
+ drawTexturedModalRect(posX + sizeX / 2, posY + sizeY / 2, 256 - sizeX / 2, 256 - sizeY / 2, sizeX / 2, sizeY / 2);
+ }
+
+ @Override
+ public void drawForeground(int mouseX, int mouseY) {
+
+ String text = getFontRenderer().trimStringToWidth(_text, sizeX - 4);
+ drawCenteredString(getFontRenderer(), text, posX + sizeX / 2, posY + (sizeY - 8) / 2, getTextColor(mouseX, mouseY));
+ }
+
+ protected int getTextColor(int mouseX, int mouseY) {
+
+ if (!isEnabled()) {
+ return -6250336;
+ } else if (intersectsWith(mouseX, mouseY)) {
+ return 16777120;
+ } else {
+ return 14737632;
+ }
+ }
+
+ @Override
+ public abstract void onClick();
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementButtonOption.java b/src/main/java/cofh/lib/gui/element/ElementButtonOption.java
new file mode 100644
index 00000000..e01707b9
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementButtonOption.java
@@ -0,0 +1,73 @@
+package cofh.lib.gui.element;
+
+import cofh.lib.gui.GuiBase;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public abstract class ElementButtonOption extends ElementButtonManaged {
+
+ private final Map _values = new HashMap();
+ private int _currentValue = 0;
+ private int _maxValue;
+
+ public ElementButtonOption(GuiBase containerScreen, int x, int y, int width, int height) {
+
+ super(containerScreen, x, y, width, height, "");
+ }
+
+ public void setValue(int value, String label) {
+
+ _values.put(value, label);
+ if (value > _maxValue) {
+ _maxValue = value;
+ }
+ }
+
+ @Override
+ public void onClick() {
+
+ int nextValue = _currentValue;
+ do {
+ nextValue++;
+ if (nextValue > _maxValue) {
+ nextValue = 0;
+ }
+ } while (_values.get(nextValue) == null);
+ setSelectedIndex(nextValue);
+ }
+
+ @Override
+ public void onRightClick() {
+
+ int nextValue = _currentValue;
+
+ do {
+ nextValue--;
+ if (nextValue < 0) {
+ nextValue = _maxValue;
+ }
+ } while (_values.get(nextValue) == null);
+ setSelectedIndex(nextValue);
+ }
+
+ public int getSelectedIndex() {
+
+ return _currentValue;
+ }
+
+ public void setSelectedIndex(int index) {
+
+ _currentValue = index;
+ setText(_values.get(_currentValue));
+ onValueChanged(_currentValue, _values.get(_currentValue));
+ }
+
+ public String getValue() {
+
+ return _values.get(_currentValue);
+ }
+
+ public abstract void onValueChanged(int value, String label);
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementDualScaled.java b/src/main/java/cofh/lib/gui/element/ElementDualScaled.java
new file mode 100644
index 00000000..4c26edf0
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementDualScaled.java
@@ -0,0 +1,64 @@
+package cofh.lib.gui.element;
+
+import cofh.lib.gui.GuiBase;
+import cofh.lib.render.RenderHelper;
+
+public class ElementDualScaled extends ElementBase {
+
+ public int quantity;
+ public int mode;
+ public boolean background = true;
+
+ public ElementDualScaled(GuiBase gui, int posX, int posY) {
+
+ super(gui, posX, posY);
+ }
+
+ public ElementDualScaled setBackground(boolean background) {
+
+ this.background = background;
+ return this;
+ }
+
+ public ElementDualScaled setMode(int mode) {
+
+ this.mode = mode;
+ return this;
+ }
+
+ public ElementDualScaled setQuantity(int quantity) {
+
+ this.quantity = quantity;
+ return this;
+ }
+
+ @Override
+ public void drawBackground(int mouseX, int mouseY, float gameTicks) {
+
+ RenderHelper.bindTexture(texture);
+
+ if (background) {
+ drawTexturedModalRect(posX, posY, 0, 0, sizeX, sizeY);
+ }
+ switch (mode) {
+ case 0:
+ // vertical bottom -> top
+ drawTexturedModalRect(posX, posY + sizeY - quantity, sizeX, sizeY - quantity, sizeX, quantity);
+ return;
+ case 1:
+ // horizontal left -> right
+ drawTexturedModalRect(posX, posY, sizeX, 0, quantity, sizeY);
+ return;
+ case 2:
+ // horizontal right -> left
+ drawTexturedModalRect(posX + sizeX - quantity, posY, sizeX + sizeX - quantity, 0, quantity, sizeY);
+ return;
+ }
+ }
+
+ @Override
+ public void drawForeground(int mouseX, int mouseY) {
+
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementEnergyStored.java b/src/main/java/cofh/lib/gui/element/ElementEnergyStored.java
new file mode 100644
index 00000000..d22862ac
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementEnergyStored.java
@@ -0,0 +1,77 @@
+package cofh.lib.gui.element;
+
+import cofh.api.energy.IEnergyStorage;
+import cofh.lib.gui.GuiBase;
+import cofh.lib.gui.GuiProps;
+import cofh.lib.render.RenderHelper;
+import cofh.lib.util.helpers.MathHelper;
+
+import java.util.List;
+
+import net.minecraft.util.ResourceLocation;
+
+public class ElementEnergyStored extends ElementBase {
+
+ public static final ResourceLocation DEFAULT_TEXTURE = new ResourceLocation(GuiProps.PATH_ELEMENTS + "Energy.png");
+ public static final int DEFAULT_SCALE = 42;
+
+ protected IEnergyStorage storage;
+
+ // If this is enabled, 1 pixel of energy will always show in the bar as long as it is non-zero.
+ protected boolean alwaysShowMinimum = false;
+
+ public ElementEnergyStored(GuiBase gui, int posX, int posY, IEnergyStorage storage) {
+
+ super(gui, posX, posY);
+ this.storage = storage;
+
+ this.texture = DEFAULT_TEXTURE;
+ this.sizeX = 16;
+ this.sizeY = DEFAULT_SCALE;
+
+ this.texW = 32;
+ this.texH = 64;
+ }
+
+ public ElementEnergyStored setAlwaysShow(boolean show) {
+
+ alwaysShowMinimum = show;
+ return this;
+ }
+
+ @Override
+ public void drawBackground(int mouseX, int mouseY, float gameTicks) {
+
+ int amount = getScaled();
+
+ RenderHelper.bindTexture(texture);
+ drawTexturedModalRect(posX, posY, 0, 0, sizeX, sizeY);
+ drawTexturedModalRect(posX, posY + DEFAULT_SCALE - amount, 16, DEFAULT_SCALE - amount, sizeX, amount);
+ }
+
+ @Override
+ public void drawForeground(int mouseX, int mouseY) {
+
+ }
+
+ @Override
+ public void addTooltip(List list) {
+
+ if (storage.getMaxEnergyStored() < 0) {
+ list.add("Infinite RF");
+ } else {
+ list.add(storage.getEnergyStored() + " / " + storage.getMaxEnergyStored() + " RF");
+ }
+ }
+
+ protected int getScaled() {
+
+ if (storage.getMaxEnergyStored() <= 0) {
+ return sizeY;
+ }
+ long fraction = (long) storage.getEnergyStored() * sizeY / storage.getMaxEnergyStored();
+
+ return alwaysShowMinimum && storage.getEnergyStored() > 0 ? Math.max(1, MathHelper.round(fraction)) : MathHelper.round(fraction);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementFluid.java b/src/main/java/cofh/lib/gui/element/ElementFluid.java
new file mode 100644
index 00000000..fdf0dae4
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementFluid.java
@@ -0,0 +1,41 @@
+package cofh.lib.gui.element;
+
+import cofh.lib.gui.GuiBase;
+import cofh.lib.util.helpers.FluidHelper;
+
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+
+public class ElementFluid extends ElementBase {
+
+ public FluidStack fluid;
+
+ public ElementFluid(GuiBase gui, int posX, int posY) {
+
+ super(gui, posX, posY);
+ }
+
+ public ElementFluid setFluid(FluidStack stack) {
+
+ this.fluid = stack;
+ return this;
+ }
+
+ public ElementFluid setFluid(Fluid fluid) {
+
+ this.fluid = new FluidStack(fluid, FluidHelper.BUCKET_VOLUME);
+ return this;
+ }
+
+ @Override
+ public void drawBackground(int mouseX, int mouseY, float gameTicks) {
+
+ gui.drawFluid(posX, posY, fluid, sizeX, sizeY);
+ }
+
+ @Override
+ public void drawForeground(int mouseX, int mouseY) {
+
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementFluidTank.java b/src/main/java/cofh/lib/gui/element/ElementFluidTank.java
new file mode 100644
index 00000000..827a10bd
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementFluidTank.java
@@ -0,0 +1,101 @@
+package cofh.lib.gui.element;
+
+import cofh.lib.gui.GuiBase;
+import cofh.lib.gui.GuiProps;
+import cofh.lib.render.RenderHelper;
+import cofh.lib.util.helpers.MathHelper;
+import cofh.lib.util.helpers.StringHelper;
+
+import java.util.List;
+
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.fluids.IFluidTank;
+
+public class ElementFluidTank extends ElementBase {
+
+ public static final ResourceLocation DEFAULT_TEXTURE = new ResourceLocation(GuiProps.PATH_ELEMENTS + "FluidTank.png");
+ public static final int DEFAULT_SCALE = 60;
+
+ protected IFluidTank tank;
+ protected int gaugeType;
+
+ // If this is enabled, 1 pixel of fluid will always show in the tank as long as fluid is present.
+ protected boolean alwaysShowMinimum = false;
+
+ public ElementFluidTank(GuiBase gui, int posX, int posY, IFluidTank tank) {
+
+ super(gui, posX, posY);
+ this.tank = tank;
+
+ this.texture = DEFAULT_TEXTURE;
+ this.texW = 64;
+ this.texH = 64;
+
+ this.sizeX = 16;
+ this.sizeY = DEFAULT_SCALE;
+ }
+
+ public ElementFluidTank(GuiBase gui, int posX, int posY, IFluidTank tank, String texture) {
+
+ super(gui, posX, posY);
+ this.tank = tank;
+
+ this.texture = new ResourceLocation(texture);
+ this.texW = 64;
+ this.texH = 64;
+
+ this.sizeX = 16;
+ this.sizeY = DEFAULT_SCALE;
+ }
+
+ public ElementFluidTank setGauge(int gaugeType) {
+
+ this.gaugeType = gaugeType;
+ return this;
+ }
+
+ public ElementFluidTank setAlwaysShow(boolean show) {
+
+ alwaysShowMinimum = show;
+ return this;
+ }
+
+ @Override
+ public void drawBackground(int mouseX, int mouseY, float gameTicks) {
+
+ int amount = getScaled();
+
+ gui.drawFluid(posX, posY + sizeY - amount, tank.getFluid(), sizeX, amount);
+ RenderHelper.bindTexture(texture);
+ drawTexturedModalRect(posX, posY, 32 + gaugeType * 16, 1, sizeX, sizeY);
+ }
+
+ @Override
+ public void drawForeground(int mouseX, int mouseY) {
+
+ }
+
+ @Override
+ public void addTooltip(List list) {
+
+ if (tank.getFluid() != null && tank.getFluidAmount() > 0) {
+ list.add(StringHelper.getFluidName(tank.getFluid()));
+ }
+ if (tank.getCapacity() < 0) {
+ list.add("Infinite Fluid");
+ } else {
+ list.add("" + tank.getFluidAmount() + " / " + tank.getCapacity() + " mB");
+ }
+ }
+
+ protected int getScaled() {
+
+ if (tank.getCapacity() < 0) {
+ return sizeY;
+ }
+ long fraction = (long) tank.getFluidAmount() * sizeY / tank.getCapacity();
+
+ return alwaysShowMinimum && tank.getFluidAmount() > 0 ? Math.max(1, MathHelper.round(fraction)) : MathHelper.round(fraction);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementIcon.java b/src/main/java/cofh/lib/gui/element/ElementIcon.java
new file mode 100644
index 00000000..e374071e
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementIcon.java
@@ -0,0 +1,67 @@
+package cofh.lib.gui.element;
+
+import cofh.lib.gui.GuiBase;
+import cofh.lib.gui.GuiColor;
+
+import net.minecraft.util.IIcon;
+
+import org.lwjgl.opengl.GL11;
+
+public class ElementIcon extends ElementBase {
+
+ protected IIcon icon;
+ protected int spriteSheet;
+ protected GuiColor color = new GuiColor(-1);
+
+ public ElementIcon(GuiBase gui, int posX, int posY, IIcon icon) {
+
+ this(gui, posX, posY, icon, 0);
+ }
+
+ public ElementIcon(GuiBase gui, int posX, int posY, IIcon icon, int spriteSheet) {
+
+ super(gui, posX, posY);
+ this.icon = icon;
+ this.spriteSheet = spriteSheet;
+ }
+
+ public ElementIcon setColor(Number color) {
+
+ this.color = new GuiColor(color.intValue());
+ return this;
+ }
+
+ public ElementIcon setIcon(IIcon icon) {
+
+ this.icon = icon;
+ return this;
+ }
+
+ public ElementIcon setSpriteSheet(int spriteSheet) {
+
+ this.spriteSheet = spriteSheet;
+ return this;
+ }
+
+ public int getColor() {
+
+ return color.getColor();
+ }
+
+ @Override
+ public void drawBackground(int mouseX, int mouseY, float gameTicks) {
+
+ if (icon != null) {
+ GL11.glColor4f(color.getFloatR(), color.getFloatG(), color.getFloatB(), color.getFloatA());
+ gui.drawColorIcon(icon, posX, posY, spriteSheet);
+ GL11.glColor4f(1.0f, 1.0f, 1.0f, 1.0F);
+ }
+ }
+
+ @Override
+ public void drawForeground(int mouseX, int mouseY) {
+
+ return;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementListBox.java b/src/main/java/cofh/lib/gui/element/ElementListBox.java
new file mode 100644
index 00000000..be2e9fcb
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementListBox.java
@@ -0,0 +1,363 @@
+package cofh.lib.gui.element;
+
+import static org.lwjgl.opengl.GL11.*;
+
+import cofh.lib.gui.GuiBase;
+import cofh.lib.gui.GuiColor;
+import cofh.lib.gui.element.listbox.IListBoxElement;
+import cofh.lib.util.helpers.StringHelper;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+public class ElementListBox extends ElementBase {
+
+ public int borderColor = new GuiColor(120, 120, 120, 255).getColor();
+ public int backgroundColor = new GuiColor(0, 0, 0, 255).getColor();
+ public int selectedLineColor = new GuiColor(0, 0, 0, 255).getColor();
+ public int textColor = new GuiColor(150, 150, 150, 255).getColor();
+ public int selectedTextColor = new GuiColor(255, 255, 255, 255).getColor();
+
+ protected int _marginTop = 2;
+ protected int _marginLeft = 2;
+ protected int _marginRight = 2;
+ protected int _marginBottom = 2;
+
+ protected final List _elements = new LinkedList();
+
+ protected int _firstIndexDisplayed;
+ protected int _selectedIndex;
+ protected int scrollHoriz;
+
+ public ElementListBox(GuiBase containerScreen, int x, int y, int width, int height) {
+
+ super(containerScreen, x, y, width, height);
+ }
+
+ public void add(IListBoxElement element) {
+
+ _elements.add(element);
+ }
+
+ public void add(Collection extends IListBoxElement> elements) {
+
+ _elements.addAll(elements);
+ }
+
+ public void remove(IListBoxElement element) {
+
+ int e = _elements.indexOf(element);
+ if (_elements.remove(element)) {
+ if (e < _firstIndexDisplayed) {
+ --_firstIndexDisplayed;
+ }
+ if (e < _selectedIndex) {
+ --_selectedIndex;
+ }
+ }
+ }
+
+ public void removeAt(int index) {
+
+ _firstIndexDisplayed = scrollHoriz = 0;
+ _selectedIndex = -1;
+ _elements.remove(index);
+ }
+
+ public void removeAll() {
+
+ _elements.clear();
+ }
+
+ public int getInternalWidth() {
+
+ int width = 0;
+ for (int i = 0; i < _elements.size(); i++) {
+ width = Math.max(_elements.get(i).getWidth(), width);
+ }
+ return width;
+ }
+
+ public int getInternalHeight() {
+
+ int height = 0;
+ for (int i = 0; i < _elements.size(); i++) {
+ height += _elements.get(i).getHeight();
+ }
+ return height;
+ }
+
+ public int getContentWidth() {
+
+ return sizeX - _marginLeft - _marginRight;
+ }
+
+ public int getContentHeight() {
+
+ return sizeY - _marginTop - _marginBottom;
+ }
+
+ public int getContentTop() {
+
+ return posY + _marginTop;
+ }
+
+ public int getContentLeft() {
+
+ return posX + _marginLeft;
+ }
+
+ public final int getContentBottom() {
+
+ return getContentTop() + getContentHeight();
+ }
+
+ public final int getContentRight() {
+
+ return getContentLeft() + getContentWidth();
+ }
+
+ public ElementListBox setTextColor(Number textColor, Number selectedTextColor) {
+
+ if (textColor != null) {
+ this.textColor = textColor.intValue();
+ }
+ if (selectedTextColor != null) {
+ this.selectedTextColor = selectedTextColor.intValue();
+ }
+ return this;
+ }
+
+ public ElementListBox setSelectionColor(Number selectedLineColor) {
+
+ if (selectedLineColor != null) {
+ this.selectedLineColor = selectedLineColor.intValue();
+ }
+ return this;
+ }
+
+ public ElementListBox setBackgroundColor(Number backgroundColor, Number borderColor) {
+
+ if (backgroundColor != null) {
+ this.backgroundColor = backgroundColor.intValue();
+ }
+ if (borderColor != null) {
+ this.borderColor = borderColor.intValue();
+ }
+ return this;
+ }
+
+ @Override
+ public void drawBackground(int mouseX, int mouseY, float gameTicks) {
+
+ drawModalRect(posX - 1, posY - 1, posX + sizeX + 1, posY + sizeY + 1, borderColor);
+ drawModalRect(posX, posY, posX + sizeX, posY + sizeY, backgroundColor);
+ }
+
+ @Override
+ public void drawForeground(int mouseX, int mouseY) {
+
+ int heightDrawn = 0;
+ int nextElement = _firstIndexDisplayed;
+
+ glPushMatrix();
+ glDisable(GL_LIGHTING);
+
+ glEnable(GL_STENCIL_TEST);
+ drawStencil(getContentLeft(), getContentTop(), getContentRight(), getContentBottom(), 1);
+
+ glPushMatrix();
+ glTranslated(-scrollHoriz, 0, 0);
+
+ int e = _elements.size();
+ while (nextElement < e && heightDrawn <= getContentHeight()) {
+ heightDrawn += drawElement(nextElement, getContentLeft(), getContentTop() + heightDrawn);
+ nextElement++;
+ }
+ glPopMatrix();
+ glDisable(GL_STENCIL_TEST);
+ glPopMatrix();
+ }
+
+ protected int drawElement(int elementIndex, int x, int y) {
+
+ IListBoxElement element = _elements.get(elementIndex);
+ if (elementIndex == _selectedIndex) {
+ element.draw(this, x, y, selectedLineColor, selectedTextColor);
+ } else {
+ element.draw(this, x, y, backgroundColor, textColor);
+ }
+
+ return element.getHeight();
+ }
+
+ @Override
+ public boolean onMousePressed(int mouseX, int mouseY, int mouseButton) {
+
+ int heightChecked = 0;
+ for (int i = _firstIndexDisplayed; i < _elements.size(); i++) {
+ if (heightChecked > getContentHeight()) {
+ break;
+ }
+ int elementHeight = _elements.get(i).getHeight();
+ if (getContentTop() + heightChecked <= mouseY && getContentTop() + heightChecked + elementHeight >= mouseY) {
+ setSelectedIndex(i);
+ onElementClicked(_elements.get(i));
+ break;
+ }
+ heightChecked += elementHeight;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onMouseWheel(int mouseX, int mouseY, int movement) {
+
+ if (StringHelper.isControlKeyDown()) {
+ if (movement > 0) {
+ scrollLeft();
+ } else if (movement < 0) {
+ scrollRight();
+ }
+ } else {
+ if (movement > 0) {
+ scrollUp();
+ } else if (movement < 0) {
+ scrollDown();
+ }
+ }
+ return true;
+ }
+
+ public void scrollDown() {
+
+ int heightDisplayed = 0;
+ int elementsDisplayed = 0;
+ for (int i = _firstIndexDisplayed; i < _elements.size(); i++) {
+ if (heightDisplayed + _elements.get(i).getHeight() > sizeY) {
+ break;
+ }
+ heightDisplayed += _elements.get(i).getHeight();
+ elementsDisplayed++;
+ }
+ if (_firstIndexDisplayed + elementsDisplayed < _elements.size()) {
+ _firstIndexDisplayed++;
+ }
+ onScrollV(_firstIndexDisplayed);
+ }
+
+ public void scrollUp() {
+
+ if (_firstIndexDisplayed > 0) {
+ _firstIndexDisplayed--;
+ }
+ onScrollV(_firstIndexDisplayed);
+ }
+
+ public void scrollLeft() {
+
+ scrollHoriz = Math.max(scrollHoriz - 15, 0);
+ onScrollH(scrollHoriz);
+ }
+
+ public void scrollRight() {
+
+ scrollHoriz = Math.min(scrollHoriz + 15, getLastScrollPositionH());
+ onScrollH(scrollHoriz);
+ }
+
+ public int getLastScrollPosition() {
+
+ int position = _elements.size() - 1;
+ if (position < 0) {
+ return 0;
+ }
+ int heightUsed = 0;
+
+ while (position >= 0 && heightUsed < sizeY) {
+ heightUsed += _elements.get(position--).getHeight();
+ }
+ if (heightUsed > sizeY) {
+ ++position;
+ }
+ return position + 1;
+ }
+
+ public int getLastScrollPositionH() {
+
+ return Math.max(getInternalWidth() - getContentWidth(), 0);
+ }
+
+ public int getSelectedIndex() {
+
+ return _selectedIndex;
+ }
+
+ public int getIndexOf(Object value) {
+
+ for (int i = 0; i < _elements.size(); i++) {
+ if (_elements.get(i).getValue().equals(value)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ public IListBoxElement getSelectedElement() {
+
+ if (_selectedIndex == -1 || _selectedIndex >= _elements.size()) {
+ return null;
+ }
+ return _elements.get(_selectedIndex);
+ }
+
+ public void setSelectedIndex(int index) {
+
+ if (index >= -1 && index != _selectedIndex && index < _elements.size()) {
+ _selectedIndex = index;
+ onSelectionChanged(_selectedIndex, getSelectedElement());
+ }
+ }
+
+ public IListBoxElement getElement(int index) {
+
+ return _elements.get(index);
+ }
+
+ public int getElementCount() {
+
+ return _elements.size();
+ }
+
+ public void scrollToV(int index) {
+
+ if (index >= 0 && index < _elements.size()) {
+ _firstIndexDisplayed = index;
+ }
+ }
+
+ public void scrollToH(int index) {
+
+ if (index >= 0 && index <= getLastScrollPositionH()) {
+ scrollHoriz = index;
+ }
+ }
+
+ protected void onElementClicked(IListBoxElement element) {
+
+ }
+
+ protected void onScrollV(int newStartIndex) {
+
+ }
+
+ protected void onScrollH(int newStartIndex) {
+
+ }
+
+ protected void onSelectionChanged(int newIndex, IListBoxElement newElement) {
+
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementSimple.java b/src/main/java/cofh/lib/gui/element/ElementSimple.java
new file mode 100644
index 00000000..46af0418
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementSimple.java
@@ -0,0 +1,42 @@
+package cofh.lib.gui.element;
+
+import cofh.lib.gui.GuiBase;
+import cofh.lib.render.RenderHelper;
+
+/**
+ * Basic element which can render an arbitrary texture.
+ *
+ * @author King Lemming
+ *
+ */
+public class ElementSimple extends ElementBase {
+
+ int texU = 0;
+ int texV = 0;
+
+ public ElementSimple(GuiBase gui, int posX, int posY) {
+
+ super(gui, posX, posY);
+ }
+
+ public ElementSimple setTextureOffsets(int u, int v) {
+
+ texU = u;
+ texV = v;
+ return this;
+ }
+
+ @Override
+ public void drawBackground(int mouseX, int mouseY, float gameTicks) {
+
+ RenderHelper.bindTexture(texture);
+ drawTexturedModalRect(posX, posY, texU, texV, sizeX, sizeY);
+ }
+
+ @Override
+ public void drawForeground(int mouseX, int mouseY) {
+
+ return;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementSimpleBox.java b/src/main/java/cofh/lib/gui/element/ElementSimpleBox.java
new file mode 100644
index 00000000..89ac54e8
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementSimpleBox.java
@@ -0,0 +1,38 @@
+package cofh.lib.gui.element;
+
+import cofh.lib.gui.GuiBase;
+
+public class ElementSimpleBox extends ElementBase {
+
+ protected int color;
+
+ public ElementSimpleBox(GuiBase gui, int posX, int posY, Number color) {
+
+ this(gui, posX, posY, 16, 16, color);
+ }
+
+ public ElementSimpleBox(GuiBase gui, int posX, int posY, int width, int height, Number color) {
+
+ super(gui, posX, posY, width, height);
+ setColor(color);
+ }
+
+ public ElementSimpleBox setColor(Number color) {
+
+ this.color = color.intValue();
+ return this;
+ }
+
+ @Override
+ public void drawBackground(int mouseX, int mouseY, float gameTicks) {
+
+ drawModalRect(posX, posY, posX + sizeX, posY + sizeY, color);
+ }
+
+ @Override
+ public void drawForeground(int mouseX, int mouseY) {
+
+ return;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementSimpleToolTip.java b/src/main/java/cofh/lib/gui/element/ElementSimpleToolTip.java
new file mode 100644
index 00000000..36b6226b
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementSimpleToolTip.java
@@ -0,0 +1,77 @@
+package cofh.lib.gui.element;
+
+import cofh.lib.gui.GuiBase;
+import cofh.lib.render.RenderHelper;
+import cofh.lib.util.helpers.StringHelper;
+
+import java.util.List;
+
+/**
+ * Basic element which can render an arbitrary texture and may have a tooltip.
+ *
+ * @author King Lemming
+ *
+ */
+public class ElementSimpleToolTip extends ElementBase {
+
+ int texU = 0;
+ int texV = 0;
+ boolean tooltipLocalized = false;
+ String tooltip;
+
+ public ElementSimpleToolTip(GuiBase gui, int posX, int posY) {
+
+ super(gui, posX, posY);
+ }
+
+ public ElementSimpleToolTip setTextureOffsets(int u, int v) {
+
+ texU = u;
+ texV = v;
+ return this;
+ }
+
+ public ElementSimpleToolTip clearToolTip() {
+
+ this.tooltip = null;
+ return this;
+ }
+
+ public ElementSimpleToolTip setToolTip(String tooltip) {
+
+ this.tooltip = tooltip;
+ return this;
+ }
+
+ public ElementSimpleToolTip setToolTipLocalized(boolean localized) {
+
+ this.tooltipLocalized = localized;
+ return this;
+ }
+
+ @Override
+ public void drawBackground(int mouseX, int mouseY, float gameTicks) {
+
+ RenderHelper.bindTexture(texture);
+ drawTexturedModalRect(posX, posY, texU, texV, sizeX, sizeY);
+ }
+
+ @Override
+ public void drawForeground(int mouseX, int mouseY) {
+
+ return;
+ }
+
+ @Override
+ public void addTooltip(List list) {
+
+ if (tooltip != null) {
+ if (tooltipLocalized) {
+ list.add(tooltip);
+ } else {
+ list.add(StringHelper.localize(tooltip));
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementSlider.java b/src/main/java/cofh/lib/gui/element/ElementSlider.java
new file mode 100644
index 00000000..a59daa5a
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementSlider.java
@@ -0,0 +1,174 @@
+package cofh.lib.gui.element;
+
+import static cofh.lib.gui.element.ElementButtonBase.*;
+
+import cofh.lib.gui.GuiBase;
+import cofh.lib.gui.GuiColor;
+
+import org.lwjgl.opengl.GL11;
+
+public abstract class ElementSlider extends ElementBase {
+
+ protected int _value;
+ protected int _valueMin;
+ protected int _valueMax;
+ protected int _sliderWidth;
+ protected int _sliderHeight;
+
+ protected boolean _isDragging;
+
+ public int borderColor = new GuiColor(120, 120, 120, 255).getColor();
+ public int backgroundColor = new GuiColor(0, 0, 0, 255).getColor();
+
+ protected ElementSlider(GuiBase containerScreen, int x, int y, int width, int height, int maxValue) {
+
+ this(containerScreen, x, y, width, height, maxValue, 0);
+ }
+
+ protected ElementSlider(GuiBase containerScreen, int x, int y, int width, int height, int maxValue, int minValue) {
+
+ super(containerScreen, x, y, width, height);
+ _valueMax = maxValue;
+ _valueMin = minValue;
+ }
+
+ public ElementSlider setColor(int backgroundColor, int borderColor) {
+
+ this.borderColor = borderColor;
+ this.backgroundColor = backgroundColor;
+ return this;
+ }
+
+ public ElementSlider setSliderSize(int width, int height) {
+
+ _sliderWidth = width;
+ _sliderHeight = height;
+ return this;
+ }
+
+ public ElementSlider setValue(int value) {
+
+ value = Math.max(_valueMin, Math.min(_valueMax, value));
+ if (value != _value) {
+ _value = value;
+ onValueChanged(_value);
+ }
+ return this;
+ }
+
+ public ElementSlider setLimits(int min, int max) {
+
+ _valueMin = min;
+ _valueMax = max;
+ setValue(_value);
+ return this;
+ }
+
+ @Override
+ public void drawBackground(int mouseX, int mouseY, float gameTicks) {
+
+ drawModalRect(posX - 1, posY - 1, posX + sizeX + 1, posY + sizeY + 1, borderColor);
+ drawModalRect(posX, posY, posX + sizeX, posY + sizeY, backgroundColor);
+ GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
+ }
+
+ protected void drawSlider(int mouseX, int mouseY, int sliderX, int sliderY) {
+
+ int sliderMidX = _sliderWidth / 2;
+ int sliderMidY = _sliderHeight / 2;
+ int sliderEndX = _sliderWidth - sliderMidX;
+ int sliderEndY = _sliderHeight - sliderMidY;
+
+ if (!isEnabled()) {
+ gui.bindTexture(DISABLED);
+ } else if (isHovering(mouseX, mouseY)) {
+ gui.bindTexture(HOVER);
+ } else {
+ gui.bindTexture(ENABLED);
+ }
+ GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
+ drawTexturedModalRect(sliderX, sliderY, 0, 0, sliderMidX, sliderMidY);
+ drawTexturedModalRect(sliderX, sliderY + sliderMidY, 0, 256 - sliderEndY, sliderMidX, sliderEndY);
+ drawTexturedModalRect(sliderX + sliderMidX, sliderY, 256 - sliderEndX, 0, sliderEndX, sliderMidY);
+ drawTexturedModalRect(sliderX + sliderMidX, sliderY + sliderMidY, 256 - sliderEndX, 256 - sliderEndY, sliderEndX, sliderEndY);
+ }
+
+ @Override
+ public void drawForeground(int mouseX, int mouseY) {
+
+ int sliderX = posX + getSliderX();
+ int sliderY = posY + getSliderY();
+
+ drawSlider(mouseX, mouseY, sliderX, sliderY);
+ GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
+ }
+
+ protected boolean isHovering(int x, int y) {
+
+ return intersectsWith(x, y);
+ }
+
+ public int getSliderX() {
+
+ return 0;
+ }
+
+ public int getSliderY() {
+
+ return 0;
+ }
+
+ @Override
+ public boolean onMousePressed(int mouseX, int mouseY, int mouseButton) {
+
+ _isDragging = mouseButton == 0;
+ update(mouseX, mouseY);
+ return true;
+ }
+
+ @Override
+ public void onMouseReleased(int mouseX, int mouseY) {
+
+ if (_isDragging) {
+ onStopDragging();
+ }
+ _isDragging = false;
+ }
+
+ @Override
+ public void update(int mouseX, int mouseY) {
+
+ if (_isDragging) {
+ dragSlider(mouseX - posX, mouseY - posY);
+ }
+ }
+
+ protected abstract void dragSlider(int x, int y);
+
+ @Override
+ public boolean onMouseWheel(int mouseX, int mouseY, int movement) {
+
+ if (movement > 0) {
+ setValue(_value - 1);
+ } else if (movement < 0) {
+ setValue(_value + 1);
+ }
+ return true;
+ }
+
+ public void onValueChanged(int value) {
+
+ return;
+ }
+
+ public void onStopDragging() {
+
+ return;
+ }
+
+ public int getValue() {
+
+ return _value;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementTextField.java b/src/main/java/cofh/lib/gui/element/ElementTextField.java
new file mode 100644
index 00000000..e1109aed
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementTextField.java
@@ -0,0 +1,1003 @@
+package cofh.lib.gui.element;
+
+import static org.lwjgl.opengl.GL11.*;
+
+import cofh.lib.gui.GuiBase;
+import cofh.lib.gui.GuiColor;
+import cofh.lib.util.helpers.MathHelper;
+import cofh.lib.util.helpers.StringHelper;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.client.gui.GuiScreen;
+import net.minecraft.util.ChatAllowedCharacters;
+import net.minecraftforge.client.MinecraftForgeClient;
+
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.opengl.GL11;
+
+public class ElementTextField extends ElementBase {
+
+ public int borderColor = new GuiColor(55, 55, 55).getColor();
+ public int backgroundColor = new GuiColor(139, 139, 139).getColor();
+ public int disabledColor = new GuiColor(198, 198, 198).getColor();
+ public int selectedLineColor = new GuiColor(160, 160, 224).getColor();
+ public int textColor = new GuiColor(224, 224, 224).getColor();
+ public int selectedTextColor = new GuiColor(224, 224, 224).getColor();
+ public int defaultCaretColor = new GuiColor(255, 255, 255).getColor();
+
+ @Deprecated
+ // dummy variable to avoid crashes with older implementation
+ protected int renderStart;
+
+ protected char[] text;
+ protected int textLength;
+ protected int selectionStart, selectionEnd;
+ protected int renderStartX, renderStartY;
+ protected int caret, prevCaret, caretX;
+
+ private boolean isFocused;
+ private boolean canFocusChange = true;
+
+ private boolean selecting, pressed;
+
+ private byte caretCounter, counterOffset;
+ protected boolean caretInsert;
+ protected boolean smartCaret = true;
+ protected boolean smartCaretCase = true;
+
+ protected boolean multiline = false;
+
+ protected boolean enableStencil = true;
+
+ public ElementTextField(GuiBase gui, int posX, int posY, int width, int height) {
+
+ this(gui, posX, posY, width, height, (short) 32);
+ }
+
+ public ElementTextField(GuiBase gui, int posX, int posY, int width, int height, short limit) {
+
+ super(gui, posX, posY, width, height);
+ setMaxLength(limit);
+ }
+
+ public ElementTextField setTextColor(Number textColor, Number selectedTextColor) {
+
+ if (textColor != null) {
+ this.textColor = textColor.intValue();
+ }
+ if (selectedTextColor != null) {
+ this.selectedTextColor = selectedTextColor.intValue();
+ }
+ return this;
+ }
+
+ public ElementTextField setSelectionColor(Number selectedLineColor, Number defaultCaretColor) {
+
+ if (selectedLineColor != null) {
+ this.selectedLineColor = selectedLineColor.intValue();
+ }
+ if (defaultCaretColor != null) {
+ this.defaultCaretColor = defaultCaretColor.intValue();
+ }
+ return this;
+ }
+
+ public ElementTextField setBackgroundColor(Number backgroundColor, Number disabledColor, Number borderColor) {
+
+ if (backgroundColor != null) {
+ this.backgroundColor = backgroundColor.intValue();
+ }
+ if (disabledColor != null) {
+ this.disabledColor = disabledColor.intValue();
+ }
+ if (borderColor != null) {
+ this.borderColor = borderColor.intValue();
+ }
+ return this;
+ }
+
+ public ElementTextField setMultiline(boolean multi) {
+
+ multiline = multi;
+ return this;
+ }
+
+ public ElementTextField setFocusable(boolean focusable) {
+
+ canFocusChange = focusable;
+ return this;
+ }
+
+ public ElementTextField setFocused(boolean focused) {
+
+ if (isFocusable()) {
+ isFocused = focused;
+ resetCaretFlash();
+ }
+ return this;
+ }
+
+ public ElementTextField setText(String text) {
+
+ selectionStart = 0;
+ selectionEnd = textLength;
+ writeText(text);
+ return this;
+ }
+
+ public ElementTextField setMaxLength(short limit) {
+
+ char[] oldText = text;
+ text = new char[limit];
+ textLength = Math.min(limit, textLength);
+ if (oldText != null) {
+ System.arraycopy(oldText, 0, text, 0, textLength);
+ }
+ findRenderStart();
+ return this;
+ }
+
+ public int getContentLength() {
+
+ return textLength;
+ }
+
+ public int getMaxLength() {
+
+ return text.length;
+ }
+
+ /**
+ * @deprecated Use getMaxLength
+ */
+ @Deprecated
+ public int getMaxStringLength() {
+
+ return text.length;
+ }
+
+ public boolean isFocused() {
+
+ return isEnabled() && isFocused;
+ }
+
+ public boolean isFocusable() {
+
+ return canFocusChange;
+ }
+
+ public int getContentHeight() {
+
+ FontRenderer font = getFontRenderer();
+ int height = font.FONT_HEIGHT;
+ if (multiline) {
+ for (int i = 0; i < textLength; ++i) {
+ if (text[i] == '\n') {
+ height += font.FONT_HEIGHT;
+ }
+ }
+ }
+ return height;
+ }
+
+ public int getVisibleHeight() {
+
+ FontRenderer font = getFontRenderer();
+ int height = font.FONT_HEIGHT;
+ if (multiline) {
+ for (int i = 0; i < textLength; ++i) {
+ if (text[i] == '\n') {
+ height += font.FONT_HEIGHT;
+ }
+ }
+ }
+ return Math.min(height - renderStartY, sizeY);
+ }
+
+ public int getContentWidth() {
+
+ FontRenderer font = getFontRenderer();
+ int width = 0;
+ for (int i = 0; i < textLength; ++i) {
+ width += font.getCharWidth(text[i]);
+ }
+ return width;
+ }
+
+ public int getVisibleWidth() {
+
+ FontRenderer font = getFontRenderer();
+ int width = 0, endX = sizeX - 1, maxWidth = 0;
+ if (multiline) {
+ for (int i = 0; i < textLength; ++i) {
+ char c = text[i];
+ int charW = font.getCharWidth(c);
+ if (c == '\n') {
+ maxWidth = Math.max(maxWidth, width);
+ width = 0;
+ } else {
+ width += charW;
+ }
+ if ((width - renderStartX) >= endX) {
+ maxWidth = endX + renderStartX;
+ break;
+ }
+ }
+ maxWidth -= renderStartX;
+ } else {
+ for (int i = renderStartX; i < textLength; ++i) {
+ char c = text[i];
+ int charW = font.getCharWidth(c);
+ maxWidth += charW;
+ if (maxWidth >= endX) {
+ maxWidth = endX;
+ break;
+ }
+ }
+ }
+ return maxWidth;
+ }
+
+ public String getText() {
+
+ return new String(text, 0, textLength);
+ }
+
+ public String getSelectedText() {
+
+ if (selectionStart != selectionEnd) {
+ return new String(text, selectionStart, selectionEnd);
+ }
+ return getText();
+ }
+
+ public void writeText(String text) {
+
+ int i = 0;
+ for (int e = text.length(); i < e; ++i) {
+ if (!insertCharacter(text.charAt(i))) {
+ break;
+ }
+ }
+ clearSelection();
+ findRenderStart();
+ onCharacterEntered(i > 0);
+ }
+
+ public boolean isAllowedCharacter(char charTyped) {
+
+ return (multiline && charTyped == '\n') || ChatAllowedCharacters.isAllowedCharacter(charTyped);
+ }
+
+ protected boolean onEnter() {
+
+ if (multiline) {
+ boolean typed;
+ if (caretInsert && selectionStart == selectionEnd) {
+ caretInsert = false;
+ typed = insertCharacter('\n');
+ caretInsert = true;
+ } else {
+ typed = insertCharacter('\n');
+ }
+ clearSelection();
+ findRenderStart();
+ onCharacterEntered(typed);
+
+ resetCaretFlash();
+ return true;
+ }
+ return false;
+ }
+
+ protected void onFocusLost() {
+
+ }
+
+ protected void onCharacterEntered(boolean success) {
+
+ }
+
+ protected void resetCaretFlash() {
+
+ int v = Minecraft.getMinecraft().ingameGUI.getUpdateCounter();
+ counterOffset = (byte) ((v - counterOffset) & 63);
+ counterOffset += (byte) ((v - counterOffset) & 63);
+ caretCounter = 0;
+ }
+
+ protected boolean insertCharacter(char charTyped) {
+
+ if (isAllowedCharacter(charTyped)) {
+
+ if (selectionStart != selectionEnd) {
+ if (caret == selectionStart) {
+ ++caret;
+ }
+ text[selectionStart++] = charTyped;
+ return true;
+ }
+
+ int len = getMaxLength();
+ if ((caretInsert && caret == len) || textLength == len) {
+ return false;
+ }
+
+ if (!caretInsert || (multiline && text[caret] == '\n')) {
+ if (caret < textLength) {
+ System.arraycopy(text, caret, text, caret + 1, textLength - caret);
+ }
+ ++textLength;
+ }
+ text[caret++] = charTyped;
+ return true;
+ } else {
+ return true;
+ }
+ }
+
+ protected void findRenderStart() {
+
+ caret = MathHelper.clamp(caret, 0, textLength);
+ if (selectionStart == selectionEnd) {
+ selectionStart = selectionEnd = caret;
+ }
+
+ if (multiline) {
+ findRenderStartML();
+ return;
+ }
+
+ renderStartY = 0;
+
+ if (caret < renderStartX) {
+ renderStartX = caret;
+ return;
+ }
+
+ FontRenderer font = getFontRenderer();
+ int endX = sizeX - 2;
+
+ for (int i = renderStartX, width = 0; i < caret; ++i) {
+ width += font.getCharWidth(text[i]);
+ while (width >= endX) {
+ width -= font.getCharWidth(text[renderStartX++]);
+ if (renderStartX >= textLength) {
+ return;
+ }
+ }
+ }
+ }
+
+ protected void findRenderStartML() {
+
+ if (caret == textLength && textLength == 0) {
+ renderStartX = renderStartY = 0;
+ return;
+ }
+ FontRenderer font = getFontRenderer();
+ int widthLeft = 0;
+ int breaksAbove = 0;
+ for (int i = caret; i-- > 0;) {
+ char c = text[i];
+ if (c == '\n') {
+ for (; i > 0; --i) {
+ c = text[i];
+ if (c == '\n') {
+ breaksAbove += font.FONT_HEIGHT;
+ }
+ }
+ break;
+ }
+ widthLeft += font.getCharWidth(c);
+ }
+ caretX = widthLeft;
+
+ int pos = Math.max(0, (sizeY - 2) / font.FONT_HEIGHT) * font.FONT_HEIGHT;
+ if (caret > 0 && text[caret - 1] == '\n') {
+ renderStartX = 0;
+ if (caret == textLength) {
+ renderStartY -= pos;
+ renderStartY &= ~renderStartY >> 31;
+ }
+ }
+
+ while ((breaksAbove - renderStartY) < 0) {
+ renderStartY -= font.FONT_HEIGHT;
+ }
+ while ((breaksAbove - renderStartY) >= pos) {
+ renderStartY += font.FONT_HEIGHT;
+ }
+
+ int dir = prevCaret > caret ? 1 : -1;
+ for (int i = 0; (widthLeft - renderStartX) < 0; i += dir) {
+ char c = text[caret + i];
+ if (c == '\n') {
+ break;
+ }
+ renderStartX -= font.getCharWidth(c);
+ }
+ renderStartX &= ~renderStartX >> 31;
+ pos = sizeX - 2 - 3;
+ for (int i = 0; (widthLeft - renderStartX) >= pos; ++i) {
+ renderStartX += font.getCharWidth(text[caret - i]);
+ }
+ prevCaret = caret;
+ }
+
+ protected void clearSelection() {
+
+ if (selectionStart != selectionEnd) {
+ if (selectionEnd < textLength) {
+ System.arraycopy(text, selectionEnd, text, selectionStart, textLength - selectionEnd);
+ }
+ textLength -= selectionEnd - selectionStart;
+
+ selectionEnd = caret = selectionStart;
+ findRenderStart();
+ onCharacterEntered(true);
+ }
+ }
+
+ protected final int seekNextCaretLocation(int pos) {
+
+ return seekNextCaretLocation(pos, true);
+ }
+
+ protected int seekNextCaretLocation(int pos, boolean forward) {
+
+ int dir = forward ? 1 : -1;
+ int e = forward ? textLength : 0;
+ if (pos != e) {
+ pos += dir;
+ }
+ char prevChar = pos == textLength ? 0 : text[pos];
+ if (!forward) {
+ if (pos != e && Character.isSpaceChar(prevChar)) {
+ pos += !Character.isSpaceChar(text[pos + dir]) ? dir : 0;
+ }
+ } else if (pos != e && Character.isSpaceChar(prevChar)) {
+ pos -= !Character.isSpaceChar(text[pos - dir]) ? dir : 0;
+ }
+ prevChar = text[pos];
+ int i = pos;
+
+ if (smartCaret) {
+ boolean originalCase = Character.isUpperCase(prevChar);
+ for (; i != e; i += dir) {
+ char curChar = text[i];
+ boolean dig = Character.isLetterOrDigit(curChar) != Character.isLetterOrDigit(prevChar);
+ boolean caze = !dig && Character.isUpperCase(curChar) != Character.isUpperCase(prevChar);
+ boolean space = Character.isWhitespace(prevChar) != Character.isWhitespace(curChar);
+ if (dig || (caze & smartCaretCase) || space) {
+ int o = 0;
+ if (smartCaretCase && caze) {
+ o = originalCase && Character.isUpperCase(prevChar) ? -dir : 0;
+ } else {
+ if (space) {
+ if (forward) {
+ if (i != e && !Character.isWhitespace(text[i + dir])) {
+ o = Character.isWhitespace(curChar) ? 1 : 0;
+ }
+ } else {
+ o = 1;
+ }
+ }
+ }
+ return i + o;
+ }
+ prevChar = curChar;
+ }
+ } else {
+ for (; i != e; i += dir) {
+ char curChar = text[i];
+ if (Character.isSpaceChar(curChar) != Character.isSpaceChar(prevChar)) {
+ return i;
+ }
+ }
+ }
+ return forward ? textLength : 0;
+ }
+
+ @Override
+ public boolean onKeyTyped(char charTyped, int keyTyped) {
+
+ if (!isFocused()) {
+ return false;
+ }
+
+ switch (charTyped) {
+ case 1: // ^A
+ selectionEnd = caret = textLength;
+ selectionStart = 0;
+ findRenderStart();
+ return true;
+ case 3: // ^C
+ if (selectionStart != selectionEnd) {
+ GuiScreen.setClipboardString(getSelectedText());
+ }
+ return true;
+ case 24: // ^X
+ if (selectionStart != selectionEnd) {
+ GuiScreen.setClipboardString(getSelectedText());
+ clearSelection();
+
+ resetCaretFlash();
+ }
+
+ return true;
+ case 22: // ^V
+ writeText(GuiScreen.getClipboardString());
+
+ resetCaretFlash();
+
+ return true;
+ default:
+ switch (keyTyped) {
+ case Keyboard.KEY_ESCAPE:
+ setFocused(false);
+ return !isFocused();
+ case Keyboard.KEY_RETURN:
+ case Keyboard.KEY_NUMPADENTER:
+ return onEnter();
+ case Keyboard.KEY_INSERT:
+ if (GuiScreen.isShiftKeyDown()) {
+ writeText(GuiScreen.getClipboardString());
+
+ resetCaretFlash();
+ } else {
+ caretInsert = !caretInsert;
+ }
+
+ return true;
+ case Keyboard.KEY_CLEAR: // mac only (clear selection)
+ clearSelection();
+
+ resetCaretFlash();
+
+ return true;
+ case Keyboard.KEY_DELETE: // delete
+ boolean changed = false;
+ if (!GuiScreen.isShiftKeyDown()) {
+ if (selectionStart != selectionEnd) {
+ clearSelection();
+ } else if (GuiScreen.isCtrlKeyDown()) {
+ int size = seekNextCaretLocation(caret, true) - caret;
+ selectionStart = caret;
+ selectionEnd = caret + size;
+ clearSelection();
+ } else {
+ if (caret < textLength && textLength > 0) {
+ --textLength;
+ System.arraycopy(text, caret + 1, text, caret, textLength - caret);
+ changed = true;
+ }
+ findRenderStart();
+
+ onCharacterEntered(changed);
+ }
+
+ resetCaretFlash();
+
+ return true;
+ }
+ // continue.. (shift+delete = backspace)
+ case Keyboard.KEY_BACK: // backspace
+ changed = false;
+ boolean calledEntered = true,
+ onBreak = false;
+ if (selectionStart != selectionEnd) {
+ clearSelection();
+ } else if (GuiScreen.isCtrlKeyDown()) {
+ int size = seekNextCaretLocation(caret, false) - caret;
+ selectionStart = caret + size;
+ selectionEnd = caret;
+ clearSelection();
+ } else {
+ calledEntered = false;
+ if (caret > 0 && textLength > 0) {
+ if (caret != textLength) {
+ System.arraycopy(text, caret, text, caret - 1, textLength - caret);
+ }
+ onBreak = text[--caret] == '\n';
+ --textLength;
+ changed = true;
+ }
+ }
+ int old = caret;
+ if (!onBreak) {
+ for (int i = 3; i-- > 0 && caret > 1 && text[caret - 1] != '\n'; --caret) {
+ ;
+ }
+ }
+ findRenderStart();
+ caret = old;
+
+ if (!calledEntered) {
+ onCharacterEntered(changed);
+ }
+
+ resetCaretFlash();
+
+ return true;
+ case Keyboard.KEY_HOME: // home
+ int begin = 0;
+ if (!GuiScreen.isCtrlKeyDown()) {
+ for (int i = caret - 1; i > 0; --i) {
+ if (text[i] == '\n') {
+ begin = Math.min(i + 1, textLength);
+ break;
+ }
+ }
+ }
+
+ if (GuiScreen.isShiftKeyDown()) {
+ if (caret >= selectionEnd) {
+ selectionEnd = selectionStart;
+ }
+ selectionStart = begin;
+ } else {
+ selectionStart = selectionEnd = begin;
+ }
+ caret = begin;
+ findRenderStart();
+
+ resetCaretFlash();
+
+ return true;
+ case Keyboard.KEY_END: // end
+ int end = textLength;
+ if (!GuiScreen.isCtrlKeyDown()) {
+ for (int i = caret; i < textLength; ++i) {
+ if (text[i] == '\n') {
+ end = i;
+ break;
+ }
+ }
+ }
+
+ if (GuiScreen.isShiftKeyDown()) {
+ if (caret <= selectionStart) {
+ selectionStart = selectionEnd;
+ }
+ selectionEnd = end;
+ } else {
+ selectionStart = selectionEnd = end;
+ }
+ caret = end;
+ findRenderStart();
+
+ resetCaretFlash();
+
+ return true;
+ case Keyboard.KEY_LEFT: // left arrow
+ case Keyboard.KEY_RIGHT: // right arrow
+ int size = keyTyped == 203 ? -1 : 1;
+ boolean shiftCaret = false;
+ if (GuiScreen.isCtrlKeyDown()) {
+ size = seekNextCaretLocation(caret, keyTyped == 205) - caret;
+ } else if (StringHelper.isAltKeyDown() && GuiScreen.isShiftKeyDown()) {
+ caret = seekNextCaretLocation(caret, keyTyped == 205);
+ selectionStart = selectionEnd = caret;
+ size = seekNextCaretLocation(caret, keyTyped != 205) - caret;
+ shiftCaret = true;
+ }
+
+ if (!GuiScreen.isShiftKeyDown()) {
+ selectionStart = selectionEnd = caret;
+ }
+
+ {
+ int t = caret;
+ caret = MathHelper.clamp(caret + size, 0, textLength);
+ size = caret - t;
+ }
+
+ if (GuiScreen.isShiftKeyDown()) {
+ if (caret == selectionStart + size) {
+ selectionStart = caret;
+ } else if (caret == selectionEnd + size) {
+ selectionEnd = caret;
+ }
+
+ if (selectionStart > selectionEnd) {
+ int t = selectionStart;
+ selectionStart = selectionEnd;
+ selectionEnd = t;
+ }
+ }
+
+ if (shiftCaret) {
+ caret = caret - size;
+ }
+ findRenderStart();
+
+ resetCaretFlash();
+
+ return true;
+ case Keyboard.KEY_UP:
+ case Keyboard.KEY_DOWN:
+ if (!multiline) {
+ return false;
+ }
+
+ if (!GuiScreen.isShiftKeyDown()) {
+ selectionStart = selectionEnd = caret;
+ }
+ int dir = keyTyped == Keyboard.KEY_UP ? -1 : 1;
+ end = dir == -1 ? 0 : textLength;
+ int i = caret,
+ pos = caretX;
+ old = i;
+ for (; i != end; i += dir) {
+ if ((dir == -1 ? i != caret : true) && text[i] == '\n') {
+ if (i != end) {
+ i += dir;
+ } else {
+ return true;
+ }
+ break;
+ }
+ }
+ l: if (dir == -1) {
+ for (; i > 0 && text[i] != '\n'; --i) {
+ ;
+ }
+ if (i == 0) {
+ if (text[0] == '\n') {
+ caret = 0;
+ findRenderStart();
+ caretX = pos;
+ }
+ break l;
+ }
+ ++i;
+ }
+ FontRenderer font = getFontRenderer();
+ for (int width = 0; i <= textLength; ++i) {
+ char c = i < textLength ? text[i] : 0;
+ if (i == textLength || c == '\n' || width >= pos) {
+ caret = i;
+ findRenderStart();
+ caretX = pos;
+ break;
+ } else {
+ width += font.getCharWidth(c);
+ }
+ }
+
+ size = caret - old;
+
+ if (GuiScreen.isShiftKeyDown()) {
+ if (selectionStart == selectionEnd) {
+ selectionStart = selectionEnd = old;
+ }
+ if (caret == selectionStart + size) {
+ selectionStart = caret;
+ } else if (caret == selectionEnd + size) {
+ selectionEnd = caret;
+ }
+
+ if (selectionStart > selectionEnd) {
+ int t = selectionStart;
+ selectionStart = selectionEnd;
+ selectionEnd = t;
+ }
+ }
+
+ resetCaretFlash();
+
+ return true;
+ default:
+ if (isAllowedCharacter(charTyped)) {
+ boolean typed = insertCharacter(charTyped);
+ clearSelection();
+ findRenderStart();
+ onCharacterEntered(typed);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean onMousePressed(int mouseX, int mouseY, int mouseButton) {
+
+ pressed = mouseButton == 0;
+ selecting = mouseButton == 0 && isFocused();
+ l: if (selecting) {
+ if (textLength == 0) {
+ selectionStart = selectionEnd = caret = 0;
+ break l;
+ }
+ FontRenderer font = getFontRenderer();
+ int posX = mouseX - this.posX - 1, posY = mouseY - this.posY - 1;
+ s: if (!multiline) {
+ for (int i = renderStartX, width = 0;;) {
+ int charW = font.getCharWidth(text[i]);
+ if ((width += charW) > posX || ++i >= textLength) {
+ selectionStart = selectionEnd = caret = i;
+ break;
+ }
+ }
+ } else {
+ posX += renderStartX;
+ posY += renderStartY;
+ int maxX = 0;
+ boolean found = false;
+ for (int i = 0, width = 0, height = font.FONT_HEIGHT; i < textLength;) {
+ char c = text[i];
+ int charW = 0;
+ if (c == '\n') {
+ if (height > posY) {
+ maxX = i;
+ break;
+ }
+ found = false;
+ width = 0;
+ height += font.FONT_HEIGHT;
+ } else {
+ charW = font.getCharWidth(c);
+ }
+ if (!found) {
+ maxX = i;
+ }
+ if ((width += charW) > posX || ++i >= textLength) {
+ if (posY < height || i >= textLength) {
+ selectionStart = selectionEnd = caret = i;
+ break s;
+ } else {
+ ++i;
+ found = true;
+ }
+ }
+ }
+ selectionStart = selectionEnd = caret = maxX;
+ }
+ findRenderStart();
+ }
+
+ setFocused(true);
+ return true;
+ }
+
+ @Override
+ public void update(int mouseX, int mouseY) {
+
+ caretCounter = (byte) ((Minecraft.getMinecraft().ingameGUI.getUpdateCounter() - counterOffset) & 63);
+ // if (selecting) {
+ // FontRenderer font = getFontRenderer();
+ // int pos = mouseX - posX - 1;
+ // for (int i = renderStart, width = 0; i < textLength; ++i) {
+ // }
+ // }
+ }
+
+ @Override
+ public void onMouseReleased(int mouseX, int mouseY) {
+
+ if (!pressed) {
+ boolean focus = isFocused();
+ setFocused(false);
+ if (focus && !isFocused()) {
+ onFocusLost();
+ }
+ }
+ pressed = selecting = false;
+ }
+
+ @Override
+ public void drawBackground(int mouseX, int mouseY, float gameTicks) {
+
+ drawModalRect(posX - 1, posY - 1, posX + sizeX + 1, posY + sizeY + 1, borderColor);
+ drawModalRect(posX, posY, posX + sizeX, posY + sizeY, isEnabled() ? backgroundColor : disabledColor);
+ }
+
+ @Override
+ public void drawForeground(int mouseX, int mouseY) {
+
+ boolean enableStencil = this.enableStencil;
+ int bit = -1;
+ l: if (enableStencil) {
+ bit = MinecraftForgeClient.reserveStencilBit();
+ if (bit == -1) {
+ enableStencil = false;
+ break l;
+ }
+ glEnable(GL_STENCIL_TEST);
+ drawStencil(posX + 1, posY + 1, posX + sizeX - 1, posY + sizeY - 1, 1 << bit);
+ }
+
+ FontRenderer font = getFontRenderer();
+ char[] text = this.text;
+ int startX = posX + 1 - (multiline ? renderStartX : 0), endX = sizeX - 1;
+ int startY = posY + 1 - renderStartY, endY = startY + font.FONT_HEIGHT;
+ int drawY = renderStartY + Math.max(0, (sizeY - 2) / font.FONT_HEIGHT) * font.FONT_HEIGHT;
+ if (enableStencil && sizeY - (drawY - renderStartY) > 2) {
+ drawY += font.FONT_HEIGHT;
+ }
+ int drawX = endX + (multiline ? renderStartX : 0);
+ for (int i = multiline ? 0 : renderStartX, width = 0, height = 0; i <= textLength; ++i) {
+ boolean end = i == textLength, draw = height >= renderStartY && width < drawX && height < drawY;
+ int charW = 2;
+ char c = 0;
+ if (!end) {
+ c = text[i];
+ if (draw) {
+ charW = multiline && c == '\n' ? 2 : font.getCharWidth(c);
+ }
+ int tWidth = width + charW;
+ if (multiline) {
+ if (!enableStencil) {
+ draw &= width >= renderStartX;
+ }
+ draw &= tWidth > renderStartX;
+ }
+ l: if (!enableStencil && tWidth > endX) {
+ draw = false;
+ if (multiline) {
+ if (c == '\n') {
+ break l;
+ }
+ continue;
+ }
+ break;
+ }
+ }
+
+ boolean drawCaret = draw && i == caret && (caretCounter & 31) < 16 && isFocused();
+ if (drawCaret) {
+ int caretEnd = width + 2;
+ if (caretInsert) {
+ caretEnd = width + charW;
+ }
+ drawModalRect(startX + width, startY - 1 + height, startX + caretEnd, endY + height, (0xFF000000 & defaultCaretColor)
+ | (~defaultCaretColor & 0xFFFFFF));
+ }
+
+ if (draw && !end) {
+ boolean selected = i >= selectionStart & i < selectionEnd;
+ if (selected) {
+ drawModalRect(startX + width, startY + height, startX + width + charW, endY + height, selectedLineColor);
+ }
+ if (c != '\n') {
+ font.drawString(String.valueOf(c), startX + width, startY + height, selected ? selectedTextColor : textColor);
+ }
+ }
+
+ if (drawCaret) {
+ int caretEnd = width + 2;
+ if (caretInsert) {
+ caretEnd = width + charW;
+ }
+
+ GL11.glEnable(GL11.GL_BLEND);
+ GL11.glBlendFunc(GL11.GL_ONE_MINUS_DST_COLOR, GL11.GL_ZERO);
+ gui.drawSizedRect(startX + width, startY - 1 + height, startX + caretEnd, endY + height, -1);
+ GL11.glDisable(GL11.GL_BLEND);
+ }
+
+ if (c == '\n') {
+ height += font.FONT_HEIGHT;
+ charW = width = 0;
+ if (height > drawY) {
+ break;
+ }
+ }
+
+ width += charW;
+ if (!multiline && width > endX) {
+ break;
+ }
+ }
+
+ if (enableStencil) {
+ glDisable(GL_STENCIL_TEST);
+ MinecraftForgeClient.releaseStencilBit(bit);
+ }
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementTextFieldFiltered.java b/src/main/java/cofh/lib/gui/element/ElementTextFieldFiltered.java
new file mode 100644
index 00000000..bf5384cd
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementTextFieldFiltered.java
@@ -0,0 +1,47 @@
+package cofh.lib.gui.element;
+
+import cofh.lib.gui.GuiBase;
+import cofh.lib.util.CharacterSingleton;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class ElementTextFieldFiltered extends ElementTextField {
+
+ protected boolean includeVanilla = true;
+ protected CharacterSingleton seq = new CharacterSingleton();
+ protected Matcher filter;
+
+ public ElementTextFieldFiltered(GuiBase gui, int posX, int posY, int width, int height) {
+
+ super(gui, posX, posY, width, height);
+ }
+
+ public ElementTextFieldFiltered(GuiBase gui, int posX, int posY, int width, int height, short limit) {
+
+ super(gui, posX, posY, width, height, limit);
+ }
+
+ /**
+ *
+ * @param pattern
+ * Regex limit what characters can be typed
+ * @param includeVanilla
+ * Include vanilla disallowed characters
+ * @return this
+ */
+ public ElementTextFieldFiltered setFilter(Pattern pattern, boolean includeVanilla) {
+
+ filter = pattern.matcher(seq);
+ this.includeVanilla = includeVanilla;
+ return this;
+ }
+
+ @Override
+ public boolean isAllowedCharacter(char charTyped) {
+
+ seq.character = charTyped;
+ return (!includeVanilla || super.isAllowedCharacter(charTyped)) && (filter == null || filter.reset().matches());
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/ElementTextFieldLimited.java b/src/main/java/cofh/lib/gui/element/ElementTextFieldLimited.java
new file mode 100644
index 00000000..cf3d1b0c
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/ElementTextFieldLimited.java
@@ -0,0 +1,41 @@
+package cofh.lib.gui.element;
+
+import cofh.lib.gui.GuiBase;
+
+public class ElementTextFieldLimited extends ElementTextField {
+
+ protected boolean includeVanilla = true;
+ protected String filter;
+
+ public ElementTextFieldLimited(GuiBase gui, int posX, int posY, int width, int height) {
+
+ super(gui, posX, posY, width, height);
+ }
+
+ public ElementTextFieldLimited(GuiBase gui, int posX, int posY, int width, int height, short limit) {
+
+ super(gui, posX, posY, width, height, limit);
+ }
+
+ /**
+ *
+ * @param pattern
+ * String containing all characters permitted
+ * @param includeVanilla
+ * Include vanilla disallowed characters
+ * @return this
+ */
+ public ElementTextFieldLimited setFilter(String pattern, boolean includeVanilla) {
+
+ filter = pattern;
+ this.includeVanilla = includeVanilla;
+ return this;
+ }
+
+ @Override
+ public boolean isAllowedCharacter(char charTyped) {
+
+ return (!includeVanilla || super.isAllowedCharacter(charTyped)) && (filter == null || filter.indexOf(charTyped) >= 0);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/TabBase.java b/src/main/java/cofh/lib/gui/element/TabBase.java
new file mode 100644
index 00000000..cbe4c304
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/TabBase.java
@@ -0,0 +1,426 @@
+package cofh.lib.gui.element;
+
+import cofh.lib.gui.GuiBase;
+import cofh.lib.gui.GuiProps;
+import cofh.lib.gui.TabTracker;
+import cofh.lib.render.RenderHelper;
+import cofh.lib.util.Rectangle4i;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.minecraft.util.ResourceLocation;
+
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.GL11;
+
+/**
+ * Base class for a tab element. Has self-contained rendering methods and a link back to the {@link GuiBase} it is a part of.
+ *
+ * @author King Lemming
+ *
+ */
+public abstract class TabBase extends ElementBase {
+
+ public static int tabExpandSpeed = 8;
+
+ public static final int LEFT = 0;
+ public static final int RIGHT = 1;
+
+ protected int offsetX = 0;
+ protected int offsetY = 0;
+
+ public boolean open;
+ public boolean fullyOpen;
+ public int side = RIGHT;
+
+ public int headerColor = 0xe1c92f;
+ public int subheaderColor = 0xaaafb8;
+ public int textColor = 0x000000;
+ public int backgroundColor = 0xffffff;
+
+ protected int currentShiftX = 0;
+ protected int currentShiftY = 0;
+
+ public int minWidth = 22;
+ public int maxWidth = 124;
+ public int currentWidth = minWidth;
+
+ public int minHeight = 22;
+ public int maxHeight = 22;
+ public int currentHeight = minHeight;
+
+ protected ArrayList elements = new ArrayList();
+
+ public static final ResourceLocation DEFAULT_TEXTURE_LEFT = new ResourceLocation(GuiProps.PATH_ELEMENTS + "Tab_Left.png");
+ public static final ResourceLocation DEFAULT_TEXTURE_RIGHT = new ResourceLocation(GuiProps.PATH_ELEMENTS + "Tab_Right.png");
+
+ public TabBase(GuiBase gui) {
+
+ super(gui, 0, 0);
+ texture = DEFAULT_TEXTURE_RIGHT;
+ }
+
+ public TabBase(GuiBase gui, int side) {
+
+ super(gui, 0, 0);
+ this.side = side;
+
+ if (side == LEFT) {
+ texture = DEFAULT_TEXTURE_LEFT;
+ } else {
+ texture = DEFAULT_TEXTURE_RIGHT;
+ }
+ }
+
+ public TabBase setOffsets(int x, int y) {
+
+ posX -= offsetX;
+ posY -= offsetY;
+ offsetX = x;
+ offsetY = y;
+ posX += offsetX;
+ posY += offsetY;
+
+ return this;
+ }
+
+ @Override
+ public TabBase setPosition(int posX, int posY) {
+
+ this.posX = posX + offsetX;
+ this.posY = posY + offsetY;
+ return this;
+ }
+
+ protected void drawForeground() {
+
+ // TODO: this and drawBackground() need to be called after the matrix translation (not for back compat)
+ }
+
+ protected void drawBackground() {
+
+ float colorR = (backgroundColor >> 16 & 255) / 255.0F;
+ float colorG = (backgroundColor >> 8 & 255) / 255.0F;
+ float colorB = (backgroundColor & 255) / 255.0F;
+
+ GL11.glColor4f(colorR, colorG, colorB, 1.0F);
+
+ RenderHelper.bindTexture(texture);
+
+ int xPosition = posX();
+
+ gui.drawTexturedModalRect(xPosition, posY + 4, 0, 256 - currentHeight + 4, 4, currentHeight - 4);
+ gui.drawTexturedModalRect(xPosition + 4, posY, 256 - currentWidth + 4, 0, currentWidth - 4, 4);
+ gui.drawTexturedModalRect(xPosition, posY, 0, 0, 4, 4);
+ gui.drawTexturedModalRect(xPosition + 4, posY + 4, 256 - currentWidth + 4, 256 - currentHeight + 4, currentWidth - 4, currentHeight - 4);
+
+ GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F);
+ }
+
+ @Override
+ public void drawBackground(int mouseX, int mouseY, float gameTicks) {
+
+ mouseX -= this.posX();
+ mouseY -= this.posY;
+
+ GL11.glPushMatrix();
+
+ drawBackground();
+
+ GL11.glTranslatef(this.posX(), this.posY, 0.0F);
+
+ for (int i = 0; i < elements.size(); i++) {
+ ElementBase element = elements.get(i);
+ if (element.isVisible()) {
+ element.drawBackground(mouseX, mouseY, gameTicks);
+ }
+ }
+ GL11.glPopMatrix();
+ }
+
+ @Override
+ public void drawForeground(int mouseX, int mouseY) {
+
+ mouseX -= this.posX();
+ mouseY -= this.posY;
+
+ GL11.glPushMatrix();
+
+ drawForeground();
+
+ GL11.glTranslatef(this.posX(), this.posY, 0.0F);
+
+ for (int i = 0; i < elements.size(); i++) {
+ ElementBase element = elements.get(i);
+ if (element.isVisible()) {
+ element.drawForeground(mouseX, mouseY);
+ }
+ }
+ GL11.glPopMatrix();
+ }
+
+ @Override
+ public void update(int mouseX, int mouseY) {
+
+ super.update(mouseX, mouseY);
+
+ mouseX -= this.posX();
+ mouseY -= this.posY;
+
+ for (int i = elements.size(); i-- > 0;) {
+ ElementBase c = elements.get(i);
+ if (c.isVisible() && c.isEnabled()) {
+ c.update(mouseX, mouseY);
+ }
+ }
+ }
+
+ @Override
+ public void update() {
+
+ if (open && currentWidth < maxWidth) {
+ currentWidth += tabExpandSpeed;
+ } else if (!open && currentWidth > minWidth) {
+ currentWidth -= tabExpandSpeed;
+ }
+ if (currentWidth > maxWidth) {
+ currentWidth = maxWidth;
+ } else if (currentWidth < minWidth) {
+ currentWidth = minWidth;
+ }
+ if (open && currentHeight < maxHeight) {
+ currentHeight += tabExpandSpeed;
+ } else if (!open && currentHeight > minHeight) {
+ currentHeight -= tabExpandSpeed;
+ }
+ if (currentHeight > maxHeight) {
+ currentHeight = maxHeight;
+ } else if (currentHeight < minHeight) {
+ currentHeight = minHeight;
+ }
+ if (!fullyOpen && open && currentWidth == maxWidth && currentHeight == maxHeight) {
+ setFullyOpen();
+ }
+ }
+
+ protected void drawTabIcon(String iconName) {
+
+ gui.drawIcon(iconName, posXOffset(), posY + 3, 1);
+ }
+
+ /**
+ * Shortcut to correct for the proper X position.
+ */
+ protected int posX() {
+
+ if (side == LEFT) {
+ return posX - currentWidth;
+ }
+ return posX;
+ }
+
+ /**
+ * Corrects for shadowing differences in tabs to ensure that they always look nice - used in font rendering, typically.
+ */
+ protected int posXOffset() {
+
+ return posX() + sideOffset();
+ }
+
+ protected int sideOffset() {
+
+ return (side == LEFT ? 4 : 2);
+ }
+
+ public boolean intersectsWith(int mouseX, int mouseY, int shiftX, int shiftY) {
+
+ shiftX += offsetX;
+ shiftY += offsetY;
+
+ if (side == LEFT) {
+ if (mouseX <= shiftX && mouseX >= shiftX - currentWidth && mouseY >= shiftY && mouseY <= shiftY + currentHeight) {
+ return true;
+ }
+ } else if (mouseX >= shiftX && mouseX <= shiftX + currentWidth && mouseY >= shiftY && mouseY <= shiftY + currentHeight) {
+ return true;
+ }
+ return false;
+ }
+
+ public boolean isFullyOpened() {
+
+ return fullyOpen;
+ }
+
+ public void setCurrentShift(int x, int y) {
+
+ updateElements();
+
+ currentShiftX = x + offsetX;
+ currentShiftY = y + offsetY;
+ }
+
+ public void setFullyOpen() {
+
+ open = true;
+ currentWidth = maxWidth;
+ currentHeight = maxHeight;
+ fullyOpen = true;
+
+ updateElements();
+ }
+
+ public void toggleOpen() {
+
+ if (open) {
+ open = false;
+ if (side == LEFT) {
+ TabTracker.setOpenedLeftTab(null);
+ } else {
+ TabTracker.setOpenedRightTab(null);
+ }
+ fullyOpen = false;
+ } else {
+ open = true;
+ if (side == LEFT) {
+ TabTracker.setOpenedLeftTab(this.getClass());
+ } else {
+ TabTracker.setOpenedRightTab(this.getClass());
+ }
+ }
+
+ updateElements();
+ }
+
+ public Rectangle4i getBounds() {
+
+ if (isVisible()) {
+ return new Rectangle4i(posX() + gui.getGuiLeft(), posY + gui.getGuiTop(), currentWidth, currentHeight);
+ } else {
+ return new Rectangle4i(posX() + gui.getGuiLeft(), posY + gui.getGuiTop(), 0, 0);
+ }
+
+ }
+
+ /* Elements */
+ public ElementBase addElement(ElementBase element) {
+
+ elements.add(element);
+ return element;
+ }
+
+ protected ElementBase getElementAtPosition(int mX, int mY) {
+
+ for (int i = elements.size(); i-- > 0;) {
+ ElementBase element = elements.get(i);
+ if (element.intersectsWith(mX, mY)) {
+ return element;
+ }
+ }
+ return null;
+ }
+
+ /* Redirects to Elements */
+
+ @Override
+ public boolean onMouseWheel(int mouseX, int mouseY, int movement) {
+
+ int wheelMovement = Mouse.getEventDWheel();
+
+ mouseX -= this.posX();
+ mouseY -= this.posY;
+
+ if (wheelMovement != 0) {
+ for (int i = elements.size(); i-- > 0;) {
+ ElementBase c = elements.get(i);
+ if (!c.isVisible() || !c.isEnabled() || !c.intersectsWith(mouseX, mouseY)) {
+ continue;
+ }
+ if (c.onMouseWheel(mouseX, mouseY, wheelMovement)) {
+ return true;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public void addTooltip(List list) {
+
+ for (int i = 0; i < this.elements.size(); i++) {
+ ElementBase c = elements.get(i);
+
+ if (!c.isVisible() || !c.isEnabled() || !c.intersectsWith(gui.getMouseX(), gui.getMouseY())) {
+ continue;
+ }
+ c.addTooltip(list);
+ }
+ }
+
+ @Override
+ public boolean onKeyTyped(char characterTyped, int keyPressed) {
+
+ for (int i = elements.size(); i-- > 0;) {
+ ElementBase c = elements.get(i);
+ if (!c.isVisible() || !c.isEnabled()) {
+ continue;
+ }
+ if (c.onKeyTyped(characterTyped, keyPressed)) {
+ return true;
+ }
+ }
+ return super.onKeyTyped(characterTyped, keyPressed);
+ }
+
+ @Override
+ /**
+ * @return Whether the tab should stay open or not.
+ */
+ public boolean onMousePressed(int mouseX, int mouseY, int mouseButton) {
+
+ mouseX -= this.posX();
+ mouseY -= this.posY;
+
+ boolean shouldStayOpen = false;
+
+ for (int i = 0; i < this.elements.size(); i++) {
+ ElementBase c = elements.get(i);
+ if (!c.isVisible() || !c.isEnabled() || !c.intersectsWith(mouseX, mouseY)) {
+ continue;
+ }
+
+ shouldStayOpen = true;
+
+ if (c.onMousePressed(mouseX, mouseY, mouseButton)) {
+ return true;
+ }
+ }
+
+ return shouldStayOpen;
+ }
+
+ @Override
+ public void onMouseReleased(int mouseX, int mouseY) {
+
+ mouseX -= this.posX();
+ mouseY -= this.posY;
+
+ for (int i = elements.size(); i-- > 0;) {
+ ElementBase c = elements.get(i);
+ if (!c.isVisible() || !c.isEnabled()) { // no bounds checking on mouseUp events
+ continue;
+ }
+ c.onMouseReleased(mouseX, mouseY);
+ }
+ }
+
+ private void updateElements() {
+
+ for (ElementBase element : elements) {
+ element.setVisible(this.isFullyOpened());
+ }
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/listbox/IListBoxElement.java b/src/main/java/cofh/lib/gui/element/listbox/IListBoxElement.java
new file mode 100644
index 00000000..55da1394
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/listbox/IListBoxElement.java
@@ -0,0 +1,14 @@
+package cofh.lib.gui.element.listbox;
+
+import cofh.lib.gui.element.ElementListBox;
+
+public interface IListBoxElement {
+
+ public int getHeight();
+
+ public int getWidth();
+
+ public Object getValue();
+
+ public void draw(ElementListBox listBox, int x, int y, int backColor, int textColor);
+}
diff --git a/src/main/java/cofh/lib/gui/element/listbox/ListBoxElementText.java b/src/main/java/cofh/lib/gui/element/listbox/ListBoxElementText.java
new file mode 100644
index 00000000..a4c259fc
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/listbox/ListBoxElementText.java
@@ -0,0 +1,40 @@
+package cofh.lib.gui.element.listbox;
+
+import cofh.lib.gui.element.ElementListBox;
+
+import net.minecraft.client.Minecraft;
+
+public class ListBoxElementText implements IListBoxElement {
+
+ private final String _text;
+
+ public ListBoxElementText(String text) {
+
+ _text = text;
+ }
+
+ @Override
+ public Object getValue() {
+
+ return _text;
+ }
+
+ @Override
+ public int getHeight() {
+
+ return 10;
+ }
+
+ @Override
+ public int getWidth() {
+
+ return Minecraft.getMinecraft().fontRenderer.getStringWidth(_text);
+ }
+
+ @Override
+ public void draw(ElementListBox listBox, int x, int y, int backColor, int textColor) {
+
+ listBox.getFontRenderer().drawStringWithShadow(_text, x, y, textColor);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/element/listbox/SliderHorizontal.java b/src/main/java/cofh/lib/gui/element/listbox/SliderHorizontal.java
new file mode 100644
index 00000000..f3e1b917
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/listbox/SliderHorizontal.java
@@ -0,0 +1,42 @@
+package cofh.lib.gui.element.listbox;
+
+import cofh.lib.gui.GuiBase;
+import cofh.lib.gui.element.ElementSlider;
+
+public class SliderHorizontal extends ElementSlider {
+
+ public SliderHorizontal(GuiBase containerScreen, int x, int y, int width, int height, int maxValue) {
+
+ this(containerScreen, x, y, width, height, maxValue, 0);
+ }
+
+ public SliderHorizontal(GuiBase containerScreen, int x, int y, int width, int height, int maxValue, int minValue) {
+
+ super(containerScreen, x, y, width, height, maxValue, minValue);
+ int dist = maxValue - minValue;
+ setSliderSize(dist <= 0 ? width : Math.max(width / ++dist, 9), height);
+ }
+
+ @Override
+ public ElementSlider setLimits(int min, int max) {
+
+ int dist = max - min;
+ setSliderSize(dist <= 0 ? getWidth() : Math.max(getWidth() / ++dist, 9), getHeight());
+ return super.setLimits(min, max);
+ }
+
+ @Override
+ public int getSliderX() {
+
+ int dist = _valueMax - _valueMin;
+ int maxPos = sizeX - _sliderWidth;
+ return Math.min(dist == 0 ? 0 : maxPos * (_value - _valueMin) / dist, maxPos);
+ }
+
+ @Override
+ public void dragSlider(int v, int y) {
+
+ v += Math.round(_sliderWidth * (v / (float) sizeX) + (_sliderWidth * 0.25f));
+ setValue(_valueMin + ((_valueMax - _valueMin) * v / sizeX));
+ }
+}
diff --git a/src/main/java/cofh/lib/gui/element/listbox/SliderVertical.java b/src/main/java/cofh/lib/gui/element/listbox/SliderVertical.java
new file mode 100644
index 00000000..d7d680b9
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/listbox/SliderVertical.java
@@ -0,0 +1,42 @@
+package cofh.lib.gui.element.listbox;
+
+import cofh.lib.gui.GuiBase;
+import cofh.lib.gui.element.ElementSlider;
+
+public class SliderVertical extends ElementSlider {
+
+ public SliderVertical(GuiBase containerScreen, int x, int y, int width, int height, int maxValue) {
+
+ this(containerScreen, x, y, width, height, maxValue, 0);
+ }
+
+ public SliderVertical(GuiBase containerScreen, int x, int y, int width, int height, int maxValue, int minValue) {
+
+ super(containerScreen, x, y, width, height, maxValue, minValue);
+ int dist = maxValue - minValue;
+ setSliderSize(width, dist <= 0 ? height : Math.max(height / ++dist, 9));
+ }
+
+ @Override
+ public ElementSlider setLimits(int min, int max) {
+
+ int dist = max - min;
+ setSliderSize(getWidth(), dist <= 0 ? getHeight() : Math.max(getHeight() / ++dist, 9));
+ return super.setLimits(min, max);
+ }
+
+ @Override
+ public int getSliderY() {
+
+ int dist = _valueMax - _valueMin;
+ int maxPos = sizeY - _sliderHeight;
+ return Math.min(dist == 0 ? 0 : maxPos * (_value - _valueMin) / dist, maxPos);
+ }
+
+ @Override
+ public void dragSlider(int x, int v) {
+
+ v += Math.round(_sliderHeight * (v / (float) sizeY) + (_sliderHeight * 0.25f));
+ setValue(_valueMin + ((_valueMax - _valueMin) * v / sizeY));
+ }
+}
diff --git a/src/main/java/cofh/lib/gui/element/listbox/package-info.java b/src/main/java/cofh/lib/gui/element/listbox/package-info.java
new file mode 100644
index 00000000..6c847922
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/listbox/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHLibProps.VERSION, owner = "CoFHLib", provides = "CoFHLib|gui|element|listbox")
+package cofh.lib.gui.element.listbox;
+
+import cofh.lib.CoFHLibProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/lib/gui/element/package-info.java b/src/main/java/cofh/lib/gui/element/package-info.java
new file mode 100644
index 00000000..2d1222e1
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/element/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHLibProps.VERSION, owner = "CoFHLib", provides = "CoFHLib|gui|element")
+package cofh.lib.gui.element;
+
+import cofh.lib.CoFHLibProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/lib/gui/package-info.java b/src/main/java/cofh/lib/gui/package-info.java
new file mode 100644
index 00000000..cab0eb02
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHLibProps.VERSION, owner = "CoFHLib", provides = "CoFHLib|gui")
+package cofh.lib.gui;
+
+import cofh.lib.CoFHLibProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/lib/gui/slot/ISlotValidator.java b/src/main/java/cofh/lib/gui/slot/ISlotValidator.java
new file mode 100644
index 00000000..a321e89e
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/ISlotValidator.java
@@ -0,0 +1,18 @@
+package cofh.lib.gui.slot;
+
+import net.minecraft.item.ItemStack;
+
+/**
+ * Interface used in conjunction with {@link SlotValidated}.
+ *
+ * @author King Lemming
+ *
+ */
+public interface ISlotValidator {
+
+ /**
+ * Essentially a passthrough so an arbitrary criterion can be checked against.
+ */
+ boolean isItemValid(ItemStack stack);
+
+}
diff --git a/src/main/java/cofh/lib/gui/slot/SlotAcceptAssignable.java b/src/main/java/cofh/lib/gui/slot/SlotAcceptAssignable.java
new file mode 100644
index 00000000..fe1cb471
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/SlotAcceptAssignable.java
@@ -0,0 +1,27 @@
+package cofh.lib.gui.slot;
+
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Slot that will only accept ItemStacks whose items are a subclass of the given class.
+ */
+public class SlotAcceptAssignable extends Slot {
+
+ protected Class extends Item> clazz;
+
+ public SlotAcceptAssignable(IInventory inventory, int index, int x, int y, Class extends Item> c) {
+
+ super(inventory, index, x, y);
+ clazz = c;
+ }
+
+ @Override
+ public boolean isItemValid(ItemStack stack) {
+
+ return stack != null && clazz.isInstance(stack.getItem());
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/slot/SlotAcceptInsertable.java b/src/main/java/cofh/lib/gui/slot/SlotAcceptInsertable.java
new file mode 100644
index 00000000..c96ae465
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/SlotAcceptInsertable.java
@@ -0,0 +1,35 @@
+package cofh.lib.gui.slot;
+
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.ISidedInventory;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Slot that will only accept ItemStacks when the IInventory returns true from isItemValidForSlot.
+ *
+ * If an ISidedInventory, canInsertItem (from side 6 (UNKNOWN)) must also return true.
+ */
+public class SlotAcceptInsertable extends SlotAcceptValid {
+
+ protected ISidedInventory sidedInv;
+
+ public SlotAcceptInsertable(IInventory inventory, int index, int x, int y) {
+
+ super(inventory, index, x, y);
+
+ if (inventory instanceof ISidedInventory) {
+ sidedInv = (ISidedInventory) inventory;
+ } else {
+ sidedInv = null;
+ }
+ }
+
+ @Override
+ public boolean isItemValid(ItemStack stack) {
+
+ boolean valid = super.isItemValid(stack);
+
+ return valid && sidedInv != null ? sidedInv.canInsertItem(slotNumber, stack, 6) : valid;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/slot/SlotAcceptValid.java b/src/main/java/cofh/lib/gui/slot/SlotAcceptValid.java
new file mode 100644
index 00000000..087bca93
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/SlotAcceptValid.java
@@ -0,0 +1,23 @@
+package cofh.lib.gui.slot;
+
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Slot that will only accept ItemStacks when the IInventory returns true from isItemValidForSlot.
+ */
+public class SlotAcceptValid extends Slot {
+
+ public SlotAcceptValid(IInventory inventory, int index, int x, int y) {
+
+ super(inventory, index, x, y);
+ }
+
+ @Override
+ public boolean isItemValid(ItemStack stack) {
+
+ return stack != null && this.inventory.isItemValidForSlot(this.slotNumber, stack);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/slot/SlotCraftingLocked.java b/src/main/java/cofh/lib/gui/slot/SlotCraftingLocked.java
new file mode 100644
index 00000000..d6c08046
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/SlotCraftingLocked.java
@@ -0,0 +1,33 @@
+package cofh.lib.gui.slot;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.SlotCrafting;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Crafting result slot where the result cannot be removed.
+ *
+ * @author King Lemming
+ *
+ */
+public class SlotCraftingLocked extends SlotCrafting {
+
+ public SlotCraftingLocked(EntityPlayer player, IInventory craftMatrix, IInventory inventory, int index, int x, int y) {
+
+ super(player, craftMatrix, inventory, index, x, y);
+ }
+
+ @Override
+ public boolean isItemValid(ItemStack stack) {
+
+ return false;
+ }
+
+ @Override
+ public boolean canTakeStack(EntityPlayer player) {
+
+ return false;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/slot/SlotCustomInventory.java b/src/main/java/cofh/lib/gui/slot/SlotCustomInventory.java
new file mode 100644
index 00000000..cbd157dc
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/SlotCustomInventory.java
@@ -0,0 +1,78 @@
+package cofh.lib.gui.slot;
+
+import cofh.api.core.ICustomInventory;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+
+public class SlotCustomInventory extends Slot {
+
+ ICustomInventory customInv;
+ int inventoryIndex = 0;
+ boolean canTake = true;
+
+ public SlotCustomInventory(ICustomInventory tile, int invIndex, IInventory inventory, int slotIndex, int x, int y, boolean lootable) {
+
+ super(inventory, slotIndex, x, y);
+ customInv = tile;
+ inventoryIndex = invIndex;
+ canTake = lootable;
+ }
+
+ @Override
+ public ItemStack getStack() {
+
+ return customInv.getInventorySlots(inventoryIndex)[getSlotIndex()];
+ }
+
+ @Override
+ public void putStack(ItemStack stack) {
+
+ customInv.getInventorySlots(inventoryIndex)[getSlotIndex()] = stack;
+ onSlotChanged();
+ }
+
+ @Override
+ public void onSlotChanged() {
+
+ customInv.onSlotUpdate();
+ }
+
+ @Override
+ public int getSlotStackLimit() {
+
+ return customInv.getSlotStackLimit(getSlotIndex());
+ }
+
+ @Override
+ public ItemStack decrStackSize(int amount) {
+
+ if (customInv.getInventorySlots(inventoryIndex)[getSlotIndex()] == null) {
+ return null;
+ }
+ if (customInv.getInventorySlots(inventoryIndex)[getSlotIndex()].stackSize <= amount) {
+ amount = customInv.getInventorySlots(inventoryIndex)[getSlotIndex()].stackSize;
+ }
+ ItemStack stack = customInv.getInventorySlots(inventoryIndex)[getSlotIndex()].splitStack(amount);
+
+ if (customInv.getInventorySlots(inventoryIndex)[getSlotIndex()].stackSize <= 0) {
+ customInv.getInventorySlots(inventoryIndex)[getSlotIndex()] = null;
+ }
+ return stack;
+ }
+
+ @Override
+ public boolean isSlotInInventory(IInventory inventory, int slot) {
+
+ return false;
+ }
+
+ @Override
+ public boolean canTakeStack(EntityPlayer par1EntityPlayer) {
+
+ return canTake;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/slot/SlotEnergy.java b/src/main/java/cofh/lib/gui/slot/SlotEnergy.java
new file mode 100644
index 00000000..55a93153
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/SlotEnergy.java
@@ -0,0 +1,28 @@
+package cofh.lib.gui.slot;
+
+import cofh.lib.util.helpers.EnergyHelper;
+
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Slot which only accepts Energy (Redstone Flux) Containers as valid.
+ *
+ * @author King Lemming
+ *
+ */
+public class SlotEnergy extends Slot {
+
+ public SlotEnergy(IInventory inventory, int index, int x, int y) {
+
+ super(inventory, index, x, y);
+ }
+
+ @Override
+ public boolean isItemValid(ItemStack stack) {
+
+ return EnergyHelper.isEnergyContainerItem(stack);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/slot/SlotFalseCopy.java b/src/main/java/cofh/lib/gui/slot/SlotFalseCopy.java
new file mode 100644
index 00000000..f9479732
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/SlotFalseCopy.java
@@ -0,0 +1,46 @@
+package cofh.lib.gui.slot;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Slot which copies an ItemStack when clicked on, does not decrement the ItemStack on the cursor.
+ *
+ * @author King Lemming
+ *
+ */
+public class SlotFalseCopy extends Slot {
+
+ public int slotIndex = 0;
+
+ public SlotFalseCopy(IInventory inventory, int index, int x, int y) {
+
+ super(inventory, index, x, y);
+ slotIndex = index;
+ }
+
+ @Override
+ public boolean canTakeStack(EntityPlayer player) {
+
+ return false;
+ }
+
+ @Override
+ public boolean isItemValid(ItemStack stack) {
+
+ return true;
+ }
+
+ @Override
+ public void putStack(ItemStack stack) {
+
+ if (stack != null) {
+ stack.stackSize = 1;
+ }
+ this.inventory.setInventorySlotContents(this.slotIndex, stack);
+ this.onSlotChanged();
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/slot/SlotInvisible.java b/src/main/java/cofh/lib/gui/slot/SlotInvisible.java
new file mode 100644
index 00000000..b1c04f2a
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/SlotInvisible.java
@@ -0,0 +1,58 @@
+package cofh.lib.gui.slot;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Slot that will redirect inserts to another inventory slot (other than index), but not be visible.
+ *
+ * Used primarily for containers that have a larger internal inventory than external (e.g., DeepStorageUnit)
+ */
+public class SlotInvisible extends Slot {
+
+ protected final int slotIndex;
+
+ public SlotInvisible(IInventory inventory, int index, int x, int y, int slot) {
+
+ super(inventory, index, x, y);
+ slotIndex = slot;
+ }
+
+ @Override
+ public void putStack(ItemStack stack) {
+
+ this.inventory.setInventorySlotContents(slotIndex, stack);
+ this.onSlotChanged();
+ }
+
+ @Override
+ public ItemStack getStack() {
+
+ return null;
+ }
+
+ @Override
+ public ItemStack decrStackSize(int par1) {
+
+ return null;
+ }
+
+ @Override
+ public boolean canTakeStack(EntityPlayer p) {
+
+ return false;
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public boolean func_111238_b() {
+
+ return false;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/slot/SlotLocked.java b/src/main/java/cofh/lib/gui/slot/SlotLocked.java
new file mode 100644
index 00000000..fae9aebb
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/SlotLocked.java
@@ -0,0 +1,48 @@
+package cofh.lib.gui.slot;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+
+/**
+ * A slot that can only be used to display an item, not edited. Can optionally not highlight when moused over.
+ */
+public class SlotLocked extends Slot {
+
+ protected boolean showHighlight;
+
+ public SlotLocked(IInventory inventory, int index, int x, int y) {
+
+ this(inventory, index, x, y, false);
+ }
+
+ public SlotLocked(IInventory inventory, int index, int x, int y, boolean highlight) {
+
+ super(inventory, index, x, y);
+ showHighlight = highlight;
+ }
+
+ @Override
+ public boolean canTakeStack(EntityPlayer player) {
+
+ return false;
+ }
+
+ @Override
+ public boolean isItemValid(ItemStack stack) {
+
+ return false;
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public boolean func_111238_b() {
+
+ return showHighlight;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/slot/SlotPotion.java b/src/main/java/cofh/lib/gui/slot/SlotPotion.java
new file mode 100644
index 00000000..49b5d970
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/SlotPotion.java
@@ -0,0 +1,24 @@
+package cofh.lib.gui.slot;
+
+import net.minecraft.init.Items;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Slot that will only accept Potions.
+ */
+public class SlotPotion extends Slot {
+
+ public SlotPotion(IInventory inventory, int index, int x, int y) {
+
+ super(inventory, index, x, y);
+ }
+
+ @Override
+ public boolean isItemValid(ItemStack stack) {
+
+ return stack != null && stack.getItem().equals(Items.potionitem);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/slot/SlotPotionIngredient.java b/src/main/java/cofh/lib/gui/slot/SlotPotionIngredient.java
new file mode 100644
index 00000000..907e0182
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/SlotPotionIngredient.java
@@ -0,0 +1,23 @@
+package cofh.lib.gui.slot;
+
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Slot that will only accept Potion Ingredients.
+ */
+public class SlotPotionIngredient extends Slot {
+
+ public SlotPotionIngredient(IInventory inventory, int index, int x, int y) {
+
+ super(inventory, index, x, y);
+ }
+
+ @Override
+ public boolean isItemValid(ItemStack stack) {
+
+ return stack != null && stack.getItem().isPotionIngredient(stack);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/slot/SlotRemoveOnly.java b/src/main/java/cofh/lib/gui/slot/SlotRemoveOnly.java
new file mode 100644
index 00000000..ade85096
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/SlotRemoveOnly.java
@@ -0,0 +1,23 @@
+package cofh.lib.gui.slot;
+
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Slot which players can only remove items from.
+ */
+public class SlotRemoveOnly extends Slot {
+
+ public SlotRemoveOnly(IInventory inventory, int index, int x, int y) {
+
+ super(inventory, index, x, y);
+ }
+
+ @Override
+ public boolean isItemValid(ItemStack stack) {
+
+ return false;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/slot/SlotSpecificItem.java b/src/main/java/cofh/lib/gui/slot/SlotSpecificItem.java
new file mode 100644
index 00000000..d0b9f29d
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/SlotSpecificItem.java
@@ -0,0 +1,47 @@
+package cofh.lib.gui.slot;
+
+import cofh.lib.inventory.ComparableItemStack;
+
+import net.minecraft.init.Blocks;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Slot which is restricted to a specific item and maximum amount.
+ *
+ * @author King Lemming
+ *
+ */
+public class SlotSpecificItem extends Slot {
+
+ protected final ComparableItemStack stack;
+ protected ComparableItemStack query = new ComparableItemStack(new ItemStack(Blocks.stone));
+ protected int slotStackLimit = -1;
+
+ public SlotSpecificItem(IInventory inventory, int index, int x, int y, ItemStack stack) {
+
+ super(inventory, index, x, y);
+
+ this.stack = new ComparableItemStack(stack);
+ }
+
+ @Override
+ public boolean isItemValid(ItemStack stack) {
+
+ return this.stack.isItemEqual(query.set(stack));
+ }
+
+ public SlotSpecificItem setSlotStackLimit(int slotStackLimit) {
+
+ this.slotStackLimit = slotStackLimit;
+ return this;
+ }
+
+ @Override
+ public int getSlotStackLimit() {
+
+ return slotStackLimit <= 0 ? inventory.getInventoryStackLimit() : slotStackLimit;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/slot/SlotValidated.java b/src/main/java/cofh/lib/gui/slot/SlotValidated.java
new file mode 100644
index 00000000..a88f77fe
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/SlotValidated.java
@@ -0,0 +1,29 @@
+package cofh.lib.gui.slot;
+
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+
+/**
+ * A slot where the input can be validated based on any arbitrary criteria by using a passthrough method to an {@link ISlotValidator}.
+ *
+ * @author King Lemming
+ *
+ */
+public class SlotValidated extends Slot {
+
+ ISlotValidator validator;
+
+ public SlotValidated(ISlotValidator validator, IInventory inventory, int index, int x, int y) {
+
+ super(inventory, index, x, y);
+ this.validator = validator;
+ }
+
+ @Override
+ public boolean isItemValid(ItemStack stack) {
+
+ return validator.isItemValid(stack);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/slot/SlotViewOnly.java b/src/main/java/cofh/lib/gui/slot/SlotViewOnly.java
new file mode 100644
index 00000000..b68c3b7e
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/SlotViewOnly.java
@@ -0,0 +1,59 @@
+package cofh.lib.gui.slot;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+
+/**
+ * A slot that can only be used to display an item, not edited. Can optionally not highlight when moused over.
+ */
+public class SlotViewOnly extends Slot {
+
+ protected boolean showHighlight;
+
+ public SlotViewOnly(IInventory inventory, int index, int x, int y) {
+
+ this(inventory, index, x, y, false);
+ }
+
+ public SlotViewOnly(IInventory inventory, int index, int x, int y, boolean highlight) {
+
+ super(inventory, index, x, y);
+ showHighlight = highlight;
+ }
+
+ @Override
+ public void putStack(ItemStack stack) {
+
+ }
+
+ @Override
+ public ItemStack decrStackSize(int i) {
+
+ return null;
+ }
+
+ @Override
+ public boolean canTakeStack(EntityPlayer player) {
+
+ return false;
+ }
+
+ @Override
+ public boolean isItemValid(ItemStack stack) {
+
+ return false;
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public boolean func_111238_b() {
+
+ return showHighlight;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/gui/slot/package-info.java b/src/main/java/cofh/lib/gui/slot/package-info.java
new file mode 100644
index 00000000..86d359ba
--- /dev/null
+++ b/src/main/java/cofh/lib/gui/slot/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHLibProps.VERSION, owner = "CoFHLib", provides = "CoFHLib|gui|slot")
+package cofh.lib.gui.slot;
+
+import cofh.lib.CoFHLibProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/lib/inventory/ComparableItemStack.java b/src/main/java/cofh/lib/inventory/ComparableItemStack.java
new file mode 100644
index 00000000..f581a72d
--- /dev/null
+++ b/src/main/java/cofh/lib/inventory/ComparableItemStack.java
@@ -0,0 +1,137 @@
+package cofh.lib.inventory;
+
+import cofh.lib.util.ComparableItem;
+import cofh.lib.util.helpers.ItemHelper;
+
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+
+/**
+ * This class allows for OreDictionary-compatible ItemStack comparisons and Integer-based Hashes without collisions.
+ *
+ * The intended purpose of this is for things such as Recipe Handlers or HashMaps of ItemStacks.
+ *
+ * @author King Lemming
+ *
+ */
+public class ComparableItemStack extends ComparableItem {
+
+ public static ComparableItemStack fromItemStack(ItemStack stack) {
+
+ return new ComparableItemStack(stack);
+ }
+
+ public int stackSize = -1;
+ public int oreID = -1;
+
+ protected static ItemStack getOre(String oreName) {
+
+ if (ItemHelper.oreNameExists(oreName)) {
+ return ItemHelper.oreProxy.getOre(oreName);
+ }
+ return null;
+ }
+
+ public ComparableItemStack(String oreName) {
+
+ this(getOre(oreName));
+ }
+
+ public ComparableItemStack(ItemStack stack) {
+
+ super(stack);
+ if (stack != null) {
+ stackSize = stack.stackSize;
+ oreID = ItemHelper.oreProxy.getOreID(stack);
+ }
+ }
+
+ public ComparableItemStack(Item item, int damage, int stackSize) {
+
+ super(item, damage);
+ this.stackSize = stackSize;
+ this.oreID = ItemHelper.oreProxy.getOreID(this.toItemStack());
+ }
+
+ public ComparableItemStack(ComparableItemStack stack) {
+
+ super(stack.item, stack.metadata);
+ this.stackSize = stack.stackSize;
+ this.oreID = stack.oreID;
+ }
+
+ @Override
+ public ComparableItemStack set(ItemStack stack) {
+
+ if (stack != null) {
+ item = stack.getItem();
+ metadata = ItemHelper.getItemDamage(stack);
+ stackSize = stack.stackSize;
+ oreID = ItemHelper.oreProxy.getOreID(stack);
+ } else {
+ item = null;
+ metadata = -1;
+ stackSize = -1;
+ oreID = -1;
+ }
+ return this;
+ }
+
+ public ComparableItemStack set(ComparableItemStack stack) {
+
+ if (stack != null) {
+ item = stack.item;
+ metadata = stack.metadata;
+ stackSize = stack.stackSize;
+ oreID = stack.oreID;
+ } else {
+ item = null;
+ metadata = -1;
+ stackSize = -1;
+ oreID = -1;
+ }
+ return this;
+ }
+
+ public boolean isItemEqual(ComparableItemStack other) {
+
+ return other != null && (oreID != -1 && oreID == other.oreID || isEqual(other));
+ }
+
+ public boolean isStackEqual(ComparableItemStack other) {
+
+ return isItemEqual(other) && stackSize == other.stackSize;
+ }
+
+ public boolean isStackValid() {
+
+ return item != null;
+ }
+
+ public ItemStack toItemStack() {
+
+ return item != null ? new ItemStack(item, stackSize, metadata) : null;
+ }
+
+ @Override
+ public ComparableItemStack clone() {
+
+ return new ComparableItemStack(this);
+ }
+
+ @Override
+ public int hashCode() {
+
+ return oreID != -1 ? oreID : super.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+
+ if (!(o instanceof ComparableItemStack)) {
+ return false;
+ }
+ return isItemEqual((ComparableItemStack) o);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/inventory/ComparableItemStackNBT.java b/src/main/java/cofh/lib/inventory/ComparableItemStackNBT.java
new file mode 100644
index 00000000..56f6bf05
--- /dev/null
+++ b/src/main/java/cofh/lib/inventory/ComparableItemStackNBT.java
@@ -0,0 +1,48 @@
+package cofh.lib.inventory;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+
+/**
+ * Extension of {@link ComparableItemStack} except NBT sensitive.
+ *
+ * It is expected that this will have limited use, so this is a child class for overhead performance reasons.
+ *
+ * @author King Lemming
+ *
+ */
+public class ComparableItemStackNBT extends ComparableItemStack {
+
+ public NBTTagCompound tag;
+
+ public ComparableItemStackNBT(ItemStack stack) {
+
+ super(stack);
+
+ if (stack != null && stack.stackTagCompound != null) {
+ tag = (NBTTagCompound) stack.stackTagCompound.copy();
+ }
+ }
+
+ @Override
+ public boolean isStackEqual(ComparableItemStack other) {
+
+ return super.isStackEqual(other) && isStackTagEqual((ComparableItemStackNBT) other);
+ }
+
+ private boolean isStackTagEqual(ComparableItemStackNBT other) {
+
+ return tag == null ? other.tag == null : other.tag == null ? false : tag.equals(other.tag);
+ }
+
+ @Override
+ public ItemStack toItemStack() {
+
+ ItemStack ret = super.toItemStack();
+ if (ret != null) {
+ ret.stackTagCompound = (NBTTagCompound) tag.copy();
+ }
+ return ret;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/inventory/ComparableItemStackSafe.java b/src/main/java/cofh/lib/inventory/ComparableItemStackSafe.java
new file mode 100644
index 00000000..717176a5
--- /dev/null
+++ b/src/main/java/cofh/lib/inventory/ComparableItemStackSafe.java
@@ -0,0 +1,66 @@
+package cofh.lib.inventory;
+
+import cofh.lib.util.helpers.ItemHelper;
+
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+
+/**
+ * This is basically a default "safe" implementation of a ComparableItemStack - the OreID will only be used for the 5 "basic" conventions.
+ *
+ * @author King Lemming
+ *
+ */
+public class ComparableItemStackSafe extends ComparableItemStack {
+
+ static final String BLOCK = "block";
+ static final String ORE = "ore";
+ static final String DUST = "dust";
+ static final String INGOT = "ingot";
+ static final String NUGGET = "nugget";
+
+ public static boolean safeOreType(String oreName) {
+
+ return oreName.startsWith(BLOCK) || oreName.startsWith(ORE) || oreName.startsWith(DUST) || oreName.startsWith(INGOT) || oreName.startsWith(NUGGET);
+ }
+
+ public static int getOreID(ItemStack stack) {
+
+ int id = ItemHelper.oreProxy.getOreID(stack);
+
+ if (!safeOreType(ItemHelper.oreProxy.getOreName(id))) {
+ return -1;
+ }
+ return id;
+ }
+
+ public static int getOreID(String oreName) {
+
+ if (!safeOreType(oreName)) {
+ return -1;
+ }
+ return ItemHelper.oreProxy.getOreID(oreName);
+ }
+
+ public ComparableItemStackSafe(ItemStack stack) {
+
+ super(stack);
+ oreID = getOreID(stack);
+ }
+
+ public ComparableItemStackSafe(Item item, int damage, int stackSize) {
+
+ super(item, damage, stackSize);
+ this.oreID = getOreID(this.toItemStack());
+ }
+
+ @Override
+ public ComparableItemStackSafe set(ItemStack stack) {
+
+ super.set(stack);
+ oreID = getOreID(stack);
+
+ return this;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/inventory/IInventoryManager.java b/src/main/java/cofh/lib/inventory/IInventoryManager.java
new file mode 100644
index 00000000..018203e4
--- /dev/null
+++ b/src/main/java/cofh/lib/inventory/IInventoryManager.java
@@ -0,0 +1,29 @@
+package cofh.lib.inventory;
+
+import java.util.Map;
+
+import net.minecraft.item.ItemStack;
+
+public interface IInventoryManager {
+
+ public boolean canAddItem(ItemStack stack, int slot);
+
+ public boolean canRemoveItem(ItemStack stack, int slot);
+
+ public ItemStack addItem(ItemStack stack);
+
+ public ItemStack removeItem(int maxRemove);
+
+ public ItemStack removeItem(int maxRemove, ItemStack type);
+
+ public ItemStack getSlotContents(int slot);
+
+ public int hasItem(ItemStack type);
+
+ public int findItem(ItemStack type);
+
+ public int[] getSlots();
+
+ public Map getContents();
+
+}
diff --git a/src/main/java/cofh/lib/inventory/InventoryCraftingCustom.java b/src/main/java/cofh/lib/inventory/InventoryCraftingCustom.java
new file mode 100644
index 00000000..7e73af47
--- /dev/null
+++ b/src/main/java/cofh/lib/inventory/InventoryCraftingCustom.java
@@ -0,0 +1,129 @@
+package cofh.lib.inventory;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.Container;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.InventoryCrafting;
+import net.minecraft.item.ItemStack;
+
+public class InventoryCraftingCustom extends InventoryCrafting {
+
+ public IInventory masterInv;
+ public int invOffset = 0;
+ public int invSize = 0;
+ /** the width of the crafting inventory */
+ public final int inventoryWidth;
+
+ /**
+ * Class containing the callbacks for the events onGUIClosed and onCraftMatrixChanged.
+ */
+ public final Container eventHandler;
+
+ public InventoryCraftingCustom(Container container, int rows, int columns, IInventory master, int startingInventoryIndex) {
+
+ super(container, rows, columns);
+ invSize = rows * columns;
+ this.eventHandler = container;
+ this.inventoryWidth = rows;
+ invOffset = startingInventoryIndex;
+ masterInv = master;
+ }
+
+ @Override
+ public int getSizeInventory() {
+
+ return invSize;
+ }
+
+ @Override
+ public ItemStack getStackInSlot(int slot) {
+
+ return slot >= this.getSizeInventory() ? null : masterInv.getStackInSlot(invOffset + slot);
+ }
+
+ @Override
+ public ItemStack getStackInRowAndColumn(int row, int column) {
+
+ if (row >= 0 && row < this.inventoryWidth) {
+ int k = row + column * this.inventoryWidth;
+ return this.getStackInSlot(k);
+ }
+ return null;
+ }
+
+ @Override
+ public ItemStack getStackInSlotOnClosing(int slot) {
+
+ if (masterInv.getStackInSlot(invOffset + slot) != null) {
+ ItemStack stack = masterInv.getStackInSlot(invOffset + slot);
+ masterInv.setInventorySlotContents(invOffset + slot, null);
+ return stack;
+ }
+ return null;
+ }
+
+ @Override
+ public ItemStack decrStackSize(int slot, int amount) {
+
+ if (masterInv.getStackInSlot(invOffset + slot) != null) {
+ ItemStack stack;
+
+ if (masterInv.getStackInSlot(invOffset + slot).stackSize <= amount) {
+ stack = masterInv.getStackInSlot(invOffset + slot);
+ masterInv.setInventorySlotContents(invOffset + slot, null);
+ this.eventHandler.onCraftMatrixChanged(this);
+ return stack;
+ } else {
+ stack = masterInv.getStackInSlot(invOffset + slot).splitStack(amount);
+
+ if (masterInv.getStackInSlot(invOffset + slot).stackSize <= 0) {
+ masterInv.setInventorySlotContents(invOffset + slot, null);
+ }
+ this.eventHandler.onCraftMatrixChanged(this);
+ return stack;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void setInventorySlotContents(int slot, ItemStack stack) {
+
+ masterInv.setInventorySlotContents(invOffset + slot, stack);
+ this.eventHandler.onCraftMatrixChanged(this);
+ }
+
+ @Override
+ public int getInventoryStackLimit() {
+
+ return 64;
+ }
+
+ @Override
+ public void markDirty() {
+
+ }
+
+ @Override
+ public boolean isUseableByPlayer(EntityPlayer player) {
+
+ return true;
+ }
+
+ @Override
+ public void openInventory() {
+
+ }
+
+ @Override
+ public void closeInventory() {
+
+ }
+
+ @Override
+ public boolean isItemValidForSlot(int slot, ItemStack stack) {
+
+ return true;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/inventory/InventoryCraftingFalse.java b/src/main/java/cofh/lib/inventory/InventoryCraftingFalse.java
new file mode 100644
index 00000000..10b055bb
--- /dev/null
+++ b/src/main/java/cofh/lib/inventory/InventoryCraftingFalse.java
@@ -0,0 +1,39 @@
+package cofh.lib.inventory;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.Container;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.InventoryCrafting;
+
+/**
+ * This class is used to get recipes (IRecipe requires it...) with a Container.
+ *
+ * @author King Lemming
+ *
+ */
+public final class InventoryCraftingFalse extends InventoryCrafting {
+
+ private static final NullContainer nullContainer = new NullContainer();
+
+ /* NULL INNER CLASS */
+ public static class NullContainer extends Container {
+
+ @Override
+ public void onCraftMatrixChanged(IInventory inventory) {
+
+ }
+
+ @Override
+ public boolean canInteractWith(EntityPlayer player) {
+
+ return false;
+ }
+
+ }
+
+ public InventoryCraftingFalse(int width, int height) {
+
+ super(nullContainer, width, height);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/inventory/InventoryManager.java b/src/main/java/cofh/lib/inventory/InventoryManager.java
new file mode 100644
index 00000000..3b998a74
--- /dev/null
+++ b/src/main/java/cofh/lib/inventory/InventoryManager.java
@@ -0,0 +1,24 @@
+package cofh.lib.inventory;
+
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.ISidedInventory;
+import net.minecraftforge.common.util.ForgeDirection;
+
+public class InventoryManager {
+
+ private InventoryManager() {
+
+ }
+
+ public static IInventoryManager create(Object inventory, ForgeDirection targetSide) {
+
+ if (inventory instanceof ISidedInventory) {
+ return new InventoryManagerSided((ISidedInventory) inventory, targetSide);
+ } else if (inventory instanceof IInventory) {
+ return new InventoryManagerStandard((IInventory) inventory, targetSide);
+ } else {
+ return null;
+ }
+ }
+
+}
diff --git a/src/main/java/cofh/lib/inventory/InventoryManagerSided.java b/src/main/java/cofh/lib/inventory/InventoryManagerSided.java
new file mode 100644
index 00000000..3bac3155
--- /dev/null
+++ b/src/main/java/cofh/lib/inventory/InventoryManagerSided.java
@@ -0,0 +1,35 @@
+package cofh.lib.inventory;
+
+import net.minecraft.inventory.ISidedInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+public class InventoryManagerSided extends InventoryManagerStandard {
+
+ private final ISidedInventory _sidedInv;
+
+ public InventoryManagerSided(ISidedInventory inventory, ForgeDirection targetSide) {
+
+ super(inventory, targetSide);
+ _sidedInv = inventory;
+ }
+
+ @Override
+ public boolean canAddItem(ItemStack stack, int slot) {
+
+ return super.canAddItem(stack, slot) && _sidedInv.canInsertItem(slot, stack, _targetSide.ordinal());
+ }
+
+ @Override
+ public boolean canRemoveItem(ItemStack stack, int slot) {
+
+ return _sidedInv.canExtractItem(slot, stack, _targetSide.ordinal());
+ }
+
+ @Override
+ public int[] getSlots() {
+
+ return _sidedInv.getAccessibleSlotsFromSide(_targetSide.ordinal());
+ }
+
+}
diff --git a/src/main/java/cofh/lib/inventory/InventoryManagerStandard.java b/src/main/java/cofh/lib/inventory/InventoryManagerStandard.java
new file mode 100644
index 00000000..c3955f10
--- /dev/null
+++ b/src/main/java/cofh/lib/inventory/InventoryManagerStandard.java
@@ -0,0 +1,206 @@
+package cofh.lib.inventory;
+
+import cofh.lib.util.helpers.ItemHelper;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import net.minecraft.inventory.IInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.common.util.ForgeDirection;
+
+public class InventoryManagerStandard implements IInventoryManager {
+
+ private final IInventory _inv;
+ protected ForgeDirection _targetSide;
+ protected int _cachedSize;
+ protected int[] _cachedSlots = new int[] {};
+
+ public InventoryManagerStandard(IInventory inventory, ForgeDirection targetSide) {
+
+ _inv = inventory;
+ _targetSide = targetSide;
+ }
+
+ @Override
+ public boolean canAddItem(ItemStack stack, int slot) {
+
+ return _inv.isItemValidForSlot(slot, stack);
+ }
+
+ @Override
+ public boolean canRemoveItem(ItemStack stack, int slot) {
+
+ return true;
+ }
+
+ @Override
+ public ItemStack addItem(ItemStack stack) {
+
+ if (stack == null) {
+ return null;
+ }
+
+ int quantitytoadd = stack.stackSize;
+ ItemStack remaining = stack.copy();
+ int[] slots = getSlots();
+ if (slots == null) {
+ return remaining;
+ }
+
+ for (int i : slots) {
+ int maxStackSize = Math.min(_inv.getInventoryStackLimit(), stack.getMaxStackSize());
+ ItemStack s = getSlotContents(i);
+ if (s == null) {
+ ItemStack add = stack.copy();
+ add.stackSize = Math.min(quantitytoadd, maxStackSize);
+
+ if (canAddItem(add, i)) {
+ quantitytoadd -= add.stackSize;
+ _inv.setInventorySlotContents(i, add);
+ _inv.markDirty();
+ }
+ } else if (ItemHelper.itemsEqualWithMetadata(s, stack, true)) {
+ ItemStack add = stack.copy();
+ add.stackSize = Math.min(quantitytoadd, maxStackSize - s.stackSize);
+
+ if (add.stackSize > 0 && canAddItem(add, i)) {
+ s.stackSize += add.stackSize;
+ quantitytoadd -= add.stackSize;
+ _inv.setInventorySlotContents(i, s);
+ _inv.markDirty();
+ }
+ }
+ if (quantitytoadd == 0) {
+ break;
+ }
+ }
+
+ remaining.stackSize = quantitytoadd;
+ if (remaining.stackSize == 0) {
+ return null;
+ } else {
+ return remaining;
+ }
+ }
+
+ @Override
+ public ItemStack removeItem(int maxRemove) {
+
+ if (maxRemove <= 0) {
+ return null;
+ }
+
+ int[] slots = getSlots();
+ if (slots == null) {
+ return null;
+ }
+
+ for (int i : slots) {
+ ItemStack s = getSlotContents(i);
+ if (s != null && canRemoveItem(s, i)) {
+ int toRemove = Math.min(s.stackSize, maxRemove);
+ s.stackSize -= toRemove;
+ ItemStack removed = s.copy();
+ removed.stackSize = toRemove;
+ if (s.stackSize > 0) {
+ _inv.setInventorySlotContents(i, s);
+ } else {
+ _inv.setInventorySlotContents(i, null);
+ }
+ _inv.markDirty();
+ return removed;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public ItemStack removeItem(int maxRemove, ItemStack type) {
+
+ if (maxRemove <= 0) {
+ return null;
+ }
+
+ int[] slots = getSlots();
+ if (slots == null) {
+ return null;
+ }
+
+ for (int i : slots) {
+ ItemStack s = getSlotContents(i);
+ if (ItemHelper.itemsEqualWithMetadata(s, type, true) && canRemoveItem(s, i)) {
+ int toRemove = Math.min(s.stackSize, maxRemove);
+ s.stackSize -= toRemove;
+ ItemStack removed = s.copy();
+ removed.stackSize = toRemove;
+ if (s.stackSize > 0) {
+ _inv.setInventorySlotContents(i, s);
+ } else {
+ _inv.setInventorySlotContents(i, null);
+ }
+ return removed;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public ItemStack getSlotContents(int slot) {
+
+ return _inv.getStackInSlot(slot);
+ }
+
+ @Override
+ public int hasItem(ItemStack type) {
+
+ int quantity = 0;
+ for (ItemStack s : getContents().values()) {
+ if (ItemHelper.itemsEqualWithMetadata(s, type, true)) {
+ quantity += s.stackSize;
+ }
+ }
+ return quantity;
+ }
+
+ @Override
+ public int findItem(ItemStack type) {
+
+ int[] slots = getSlots();
+ if (slots == null) {
+ return -1;
+ }
+
+ for (int i : slots) {
+ ItemStack s = _inv.getStackInSlot(i);
+ if (ItemHelper.itemsEqualWithMetadata(s, type, true)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ @Override
+ public int[] getSlots() {
+
+ if (_inv.getSizeInventory() != _cachedSize) {
+ _cachedSize = _inv.getSizeInventory();
+ _cachedSlots = new int[_cachedSize];
+ for (int i = 0; i < _cachedSize; i++) {
+ _cachedSlots[i] = i;
+ }
+ }
+ return _cachedSlots;
+ }
+
+ @Override
+ public Map getContents() {
+
+ Map contents = new HashMap();
+ for (int i : getSlots()) {
+ contents.put(i, _inv.getStackInSlot(i));
+ }
+ return contents;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/inventory/package-info.java b/src/main/java/cofh/lib/inventory/package-info.java
new file mode 100644
index 00000000..149e2a15
--- /dev/null
+++ b/src/main/java/cofh/lib/inventory/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHLibProps.VERSION, owner = "CoFHLib", provides = "CoFHLib|inventory")
+package cofh.lib.inventory;
+
+import cofh.lib.CoFHLibProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/lib/network/ByteBufHelper.java b/src/main/java/cofh/lib/network/ByteBufHelper.java
new file mode 100644
index 00000000..6edce000
--- /dev/null
+++ b/src/main/java/cofh/lib/network/ByteBufHelper.java
@@ -0,0 +1,183 @@
+package cofh.lib.network;
+
+import io.netty.buffer.ByteBuf;
+
+public final class ByteBufHelper {
+
+ private ByteBufHelper() {
+
+ }
+
+ public static int readVarInt(ByteBuf data) {
+
+ int v = data.readByte(), r = v & 0x3F;
+ boolean n = (v & 0x40) != 0;
+ for (int i = 6; (v & 0x80) != 0;) {
+ v = data.readByte();
+ r |= (v & 0x7F) << i;
+ i += 7;
+ if (i > 34) { // 7 * 4 + 6
+ throw new RuntimeException("VarInt too big");
+ }
+ }
+ return n ? ~r : r;
+ }
+
+ public static void writeVarInt(int in, ByteBuf out) {
+
+ /*
+ * Custom format: pseudo-ones-compliment utf-7
+ * zyxx xxxx | zxxx xxxx | zxxx xxxx | zxxx xxxx | 0000 xxxx
+ * z = `continue` bit, if not set following bytes do not exist
+ * y = `negate` bit, if set the final value should be twos-compliment bitwise negated
+ * x = value bit. encoded in little-endian
+ */
+ int v = 0x00;
+ if (in < 0) {
+ v |= 0x40;
+ in = ~in;
+ }
+ if ((in & ~0x3F) != 0) {
+ v |= 0x80;
+ }
+ out.writeByte(v | (in & 0x3F));
+ in >>>= 6;
+ while (in != 0) {
+ out.writeByte((in & 0x7F) | ((in & ~0x7F) != 0 ? 0x80 : 0));
+ in >>>= 7;
+ }
+ }
+
+ public static String readString(ByteBuf data) {
+
+ int utflen = readVarInt(data);
+ if (utflen == -1) {
+ return null;
+ }
+ byte[] bytearr = null;
+ char[] chararr = null;
+ bytearr = new byte[utflen];
+ chararr = new char[utflen];
+
+ int c, char2, char3;
+ int count = 0;
+ int chararr_count = 0;
+
+ data.readBytes(bytearr, 0, utflen);
+
+ while (count < utflen) {
+ c = bytearr[count] & 0xff;
+ if (c > 127) {
+ break;
+ }
+ ++count;
+ chararr[chararr_count++] = (char) c;
+ }
+
+ while (count < utflen) {
+ c = bytearr[count] & 0xff;
+ switch (c >> 4) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ /* 0xxxxxxx */
+ ++count;
+ chararr[chararr_count++] = (char) c;
+ break;
+ case 12:
+ case 13:
+ /* 110x xxxx 10xx xxxx */
+ count += 2;
+ if (count > utflen) {
+ throw new IllegalArgumentException("malformed input: partial character at end");
+ }
+ char2 = bytearr[count - 1];
+ if ((char2 & 0xC0) != 0x80) {
+ throw new IllegalArgumentException("malformed input around byte " + count);
+ }
+ chararr[chararr_count++] = (char) (((c & 0x1F) << 6) | (char2 & 0x3F));
+ break;
+ case 14:
+ /* 1110 xxxx 10xx xxxx 10xx xxxx */
+ count += 3;
+ if (count > utflen) {
+ throw new IllegalArgumentException("malformed input: partial character at end");
+ }
+ char2 = bytearr[count - 2];
+ char3 = bytearr[count - 1];
+ if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) {
+ throw new IllegalArgumentException("malformed input around byte " + (count - 1));
+ }
+ chararr[chararr_count++] = (char) (((c & 0x0F) << 12) | ((char2 & 0x3F) << 6) | ((char3 & 0x3F) << 0));
+ break;
+ default:
+ /* 10xx xxxx, 1111 xxxx */
+ throw new IllegalArgumentException("malformed input around byte " + count);
+ }
+ }
+ // The number of chars produced may be less than utflen
+ return new String(chararr, 0, chararr_count);
+ }
+
+ public static void writeString(String str, ByteBuf out) {
+
+ if (str == null) {
+ writeVarInt(-1, out);
+ return;
+ }
+
+ int strlen = str.length();
+ long utflen = 0;
+ int c, count = 0;
+
+ /* use charAt instead of copying String to char array */
+ for (int i = 0; i < strlen; ++i) {
+ c = str.charAt(i);
+ if ((c >= 0x0001) & (c <= 0x007F)) {
+ utflen++;
+ } else if (c < 0x0800) {
+ utflen += 2;
+ } else {
+ utflen += 3;
+ }
+ }
+
+ if (utflen < 0 || utflen > Integer.MAX_VALUE) {
+ throw new IllegalArgumentException("encoded string too long: " + utflen + " bytes");
+ }
+
+ byte[] bytearr = new byte[(int) utflen];
+
+ writeVarInt((int) utflen, out);
+
+ int i = 0;
+
+ if (utflen == strlen) {
+ for (; i < strlen; ++i) {
+ bytearr[count++] = (byte) str.charAt(i);
+ }
+ }
+
+ for (; i < strlen; ++i) {
+ c = str.charAt(i);
+ if ((c >= 0x0001) & (c <= 0x007F)) {
+ bytearr[count++] = (byte) c;
+
+ } else if (c < 0x0800) {
+ bytearr[count++] = (byte) (0xC0 | ((c >> 6) & 0x1F));
+ bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
+ } else {
+ bytearr[count++] = (byte) (0xE0 | ((c >> 12) & 0x0F));
+ bytearr[count++] = (byte) (0x80 | ((c >> 6) & 0x3F));
+ bytearr[count++] = (byte) (0x80 | ((c >> 0) & 0x3F));
+ }
+ }
+ out.writeBytes(bytearr);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/package-info.java b/src/main/java/cofh/lib/package-info.java
new file mode 100644
index 00000000..ab0dd8a2
--- /dev/null
+++ b/src/main/java/cofh/lib/package-info.java
@@ -0,0 +1,9 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHLibProps.VERSION, owner = "CoFHCore", provides = "CoFHLib")
+package cofh.lib;
+
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/lib/render/IFluidOverlayItem.java b/src/main/java/cofh/lib/render/IFluidOverlayItem.java
new file mode 100644
index 00000000..868070c0
--- /dev/null
+++ b/src/main/java/cofh/lib/render/IFluidOverlayItem.java
@@ -0,0 +1,11 @@
+package cofh.lib.render;
+
+/**
+ * Simple fluid containers that implement this can be assigned the FactoryFluidOverlayItem. The mask for the fluid is assumed to be for render pass 1, with the
+ * base icon render pass 0. {@link getRenderPasses(int)} is called to see if the item needs an overlay (return 2)
+ *
+ * @author skyboy
+ */
+public interface IFluidOverlayItem {
+
+}
diff --git a/src/main/java/cofh/lib/render/IconOverlay.java b/src/main/java/cofh/lib/render/IconOverlay.java
new file mode 100644
index 00000000..f001920c
--- /dev/null
+++ b/src/main/java/cofh/lib/render/IconOverlay.java
@@ -0,0 +1,160 @@
+package cofh.lib.render;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+import net.minecraft.util.IIcon;
+
+public class IconOverlay implements IIcon {
+
+ private IIcon overlayIcon;
+ private float xSegments, ySegments;
+ private float selectedSegmentX, selectedSegmentY;
+
+ public IconOverlay(IIcon overlayIcon, int subX, int subY, int selectedX, int selectedY) {
+ this.overlayIcon = overlayIcon;
+ xSegments = subX;
+ ySegments = subY;
+ selectedSegmentX = selectedX;
+ selectedSegmentY = selectedY;
+ }
+
+ public IconOverlay(IIcon overlayIcon, int subX, int subY, int index) {
+ this.overlayIcon = overlayIcon;
+ xSegments = subX;
+ ySegments = subY;
+ selectedSegmentX = index % subX;
+ selectedSegmentY = index / subX;
+ }
+
+ public IconOverlay(IIcon overlayIcon, int subX, int subY, boolean ...sides) {
+ this.overlayIcon = overlayIcon;
+ xSegments = subX;
+ ySegments = subY;
+ int parts = toInt(sides) & 255;
+ int value = (parts & 15);
+ parts = parts >> 4;
+ int w;
+ switch (value) {
+ case 3: // bottom right connection
+ value ^= ((parts & 1) << 4); // bithack: add 16 if connection
+ break;
+ case 5: // top right connection
+ value ^= ((parts & 8) << 1); // bithack: add 16 if connection
+ break;
+ case 7: // left empty
+ w = parts & 9;
+ value ^= ((w & (w << 3)) << 1); // bithack: add 16 if both connections
+ if ((w == 1) | w == 8) // bottom right, top right
+ value = 32 | (w >> 3);
+ break;
+ case 10: // bottom left connection
+ value ^= ((parts & 2) << 3); // bithack: add 16 if connection
+ break;
+ case 11: // top empty
+ w = parts & 3;
+ value ^= ((w & (w << 1)) << 3); // bithack: add 16 if both connections
+ if ((w == 1) | w == 2) // bottom right, bottom left
+ value = 34 | (w >> 1);
+ break;
+ case 12: // top left connection
+ value ^= ((parts & 4) << 2); // bithack: add 16 if connection
+ break;
+ case 13: // bottom empty
+ w = parts & 12;
+ value ^= ((w & (w << 1)) << 1); // bithack: add 16 if both connections
+ if ((w == 4) | w == 8) // top left, top right
+ value = 36 | (w >> 3);
+ break;
+ case 14: // right empty
+ w = parts & 6;
+ value ^= ((w & (w << 1)) << 2); // bithack: add 16 if both connections
+ if ((w == 2) | w == 4) // bottom left, top left
+ value = 38 | (w >> 2);
+ break;
+ case 15: // all sides
+ value = 40 + parts;
+ default:
+ }
+ selectedSegmentX = value % subX;
+ selectedSegmentY = value / subX;
+ }
+
+ private static int toInt(boolean ...flags) {
+ int ret = 0;
+ for (int i = flags.length; i --> 0;)
+ ret |= (flags[i] ? 1 : 0) << i;
+ return ret;
+ }
+
+ /*@Override
+ @SideOnly(Side.CLIENT)
+ public int getOriginX() {
+ return (int)(this.getMinU() * overlayIcon.getSheetWidth());
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public int getOriginY() {
+ return (int)(this.getMinV() * overlayIcon.getSheetHeight());
+ }//*/
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public float getMinU() {
+ return overlayIcon.getInterpolatedU(((selectedSegmentX + 0.001f) / xSegments) * 16f);
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public float getMaxU() {
+ return overlayIcon.getInterpolatedU(((selectedSegmentX + 0.999f) / xSegments) * 16f);
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public float getInterpolatedU(double d0) {
+ float minU = this.getMinU();
+ float f = this.getMaxU() - minU;
+ return minU + f * ((float)d0 / 16.0F);
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public float getMinV() {
+ return overlayIcon.getInterpolatedV(((selectedSegmentY + 0.001f) / ySegments) * 16f);
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public float getMaxV() {
+ return overlayIcon.getInterpolatedV(((selectedSegmentY + 0.999f) / ySegments) * 16f);
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public float getInterpolatedV(double d0) {
+ float minV = this.getMinV();
+ float f = this.getMaxV() - minV;
+ return minV + f * ((float)d0 / 16.0F);
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public String getIconName() {
+ return overlayIcon.getIconName();
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public int getIconWidth() {
+ return (int)(overlayIcon.getIconWidth() / xSegments);
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public int getIconHeight() {
+ return (int)(overlayIcon.getIconHeight() / ySegments);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/render/RenderFluidOverlayItem.java b/src/main/java/cofh/lib/render/RenderFluidOverlayItem.java
new file mode 100644
index 00000000..15fde5f2
--- /dev/null
+++ b/src/main/java/cofh/lib/render/RenderFluidOverlayItem.java
@@ -0,0 +1,224 @@
+package cofh.lib.render;
+
+import cofh.lib.util.helpers.ItemHelper;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+import net.minecraft.client.renderer.ItemRenderer;
+import net.minecraft.client.renderer.OpenGlHelper;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.client.renderer.texture.TextureManager;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.IIcon;
+import net.minecraftforge.client.IItemRenderer;
+import net.minecraftforge.fluids.FluidContainerRegistry;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.IFluidContainerItem;
+
+import org.lwjgl.opengl.GL11;
+import org.lwjgl.opengl.GL12;
+
+@SideOnly(Side.CLIENT)
+public class RenderFluidOverlayItem implements IItemRenderer {
+
+ private final boolean canFlip;
+
+ public RenderFluidOverlayItem() {
+
+ this(true);
+ }
+
+ public RenderFluidOverlayItem(boolean canFlip) {
+
+ this.canFlip = canFlip;
+ }
+
+ @Override
+ public boolean handleRenderType(ItemStack item, ItemRenderType type) {
+
+ return (type.ordinal() < ItemRenderType.FIRST_PERSON_MAP.ordinal());
+ }
+
+ @Override
+ public boolean shouldUseRenderHelper(ItemRenderType type, ItemStack item, ItemRendererHelper helper) {
+
+ return handleRenderType(item, type) & helper.ordinal() < ItemRendererHelper.EQUIPPED_BLOCK.ordinal();
+ }
+
+ @Override
+ public void renderItem(ItemRenderType type, ItemStack stack, Object... data) {
+
+ Item item = stack.getItem();
+ FluidStack fluid = null;
+ if (item instanceof IFluidContainerItem) {
+ IFluidContainerItem fluidItem = (IFluidContainerItem) item;
+ fluid = fluidItem.getFluid(stack);
+ } else if (item instanceof IFluidOverlayItem
+ && item.getRenderPasses(ItemHelper.getItemDamage(stack)) == 2) {
+ fluid = FluidContainerRegistry.getFluidForFilledItem(stack);
+ }
+ doRenderItem(type, stack, item, fluid);
+ }
+
+ protected void doRenderItem(ItemRenderType type, ItemStack item, Item iconItem, FluidStack fluid) {
+
+ IIcon icon = iconItem.getIcon(item, 0);
+ IIcon mask = iconItem.getIcon(item, 1);
+ boolean hasFluid = fluid != null;
+
+ IIcon fluidIcon = hasFluid ? fluid.getFluid().getIcon(fluid) : mask;
+ int fluidSheet = hasFluid ? fluid.getFluid().getSpriteNumber() : 0;
+ int colorMult = hasFluid ? fluid.getFluid().getColor(fluid) : 0xFFFFFF;
+ boolean isFloaty = hasFluid ? fluid.getFluid().getDensity(fluid) < 0 : false;
+
+ if (fluidIcon == null) {
+ fluidIcon = Blocks.flowing_lava.getIcon(2, 0);
+ fluidSheet = 0;
+ colorMult = 0x3F3F3F;
+ }
+ GL11.glPushMatrix();
+
+ Tessellator tessellator = Tessellator.instance;
+
+ float iconMinX = icon.getMinU();
+ float iconMaxX = icon.getMaxU();
+ float iconMinY = icon.getMinV();
+ float iconMaxY = icon.getMaxV();
+
+ float maskMinX = mask.getMinU();
+ float maskMaxX = mask.getMaxU();
+ float maskMinY = mask.getMinV();
+ float maskMaxY = mask.getMaxV();
+
+ float fluidMinX = fluidIcon.getMinU();
+ float fluidMaxX = fluidIcon.getMaxU();
+ float fluidMinY = fluidIcon.getMinV();
+ float fluidMaxY = fluidIcon.getMaxV();
+
+ if (isFloaty && canFlip) {
+ iconMaxY = icon.getMinV();
+ iconMinY = icon.getMaxV();
+
+ maskMaxY = mask.getMinV();
+ maskMinY = mask.getMaxV();
+
+ fluidMaxY = fluidIcon.getMinV();
+ fluidMinY = fluidIcon.getMaxV();
+ }
+ GL11.glEnable(GL11.GL_BLEND);
+ GL11.glEnable(GL11.GL_ALPHA_TEST);
+ OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
+ int texture = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D);
+
+ if (type == ItemRenderType.INVENTORY) {
+ GL11.glDisable(GL11.GL_LIGHTING);
+
+ tessellator.startDrawingQuads();
+ tessellator.addVertexWithUV(0, 16, 0, iconMinX, iconMaxY);
+ tessellator.addVertexWithUV(16, 16, 0, iconMaxX, iconMaxY);
+ tessellator.addVertexWithUV(16, 0, 0, iconMaxX, iconMinY);
+ tessellator.addVertexWithUV(0, 0, 0, iconMinX, iconMinY);
+ tessellator.draw();
+
+ if (hasFluid) {
+ tessellator.startDrawingQuads();
+ tessellator.addVertexWithUV(0, 16, 0.001, maskMinX, maskMaxY);
+ tessellator.addVertexWithUV(16, 16, 0.001, maskMaxX, maskMaxY);
+ tessellator.addVertexWithUV(16, 0, 0.001, maskMaxX, maskMinY);
+ tessellator.addVertexWithUV(0, 0, 0.001, maskMinX, maskMinY);
+ tessellator.draw();
+
+ GL11.glEnable(GL11.GL_CULL_FACE);
+ GL11.glDepthFunc(GL11.GL_EQUAL);
+ GL11.glDepthMask(false);
+ GL11.glMatrixMode(GL11.GL_TEXTURE);
+ bindTexture(RenderHelper.engine(), fluidSheet);
+ OpenGlHelper.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
+
+ tessellator.startDrawingQuads();
+ tessellator.setColorOpaque_I(colorMult);
+ tessellator.addVertexWithUV(0, 16, 0.001, fluidMinX, fluidMaxY);
+ tessellator.addVertexWithUV(16, 16, 0.001, fluidMaxX, fluidMaxY);
+ tessellator.addVertexWithUV(16, 0, 0.001, fluidMaxX, fluidMinY);
+ tessellator.addVertexWithUV(0, 0, 0.001, fluidMinX, fluidMinY);
+ tessellator.draw();
+
+ GL11.glMatrixMode(GL11.GL_MODELVIEW);
+ GL11.glDepthMask(true);
+ GL11.glDepthFunc(GL11.GL_LEQUAL);
+ }
+
+ GL11.glEnable(GL11.GL_LIGHTING);
+ } else {
+ GL11.glEnable(GL12.GL_RESCALE_NORMAL);
+
+ if (type == ItemRenderType.ENTITY) {
+ GL11.glTranslatef(0.5f, 4 / -16f, 0);
+ GL11.glRotatef(180, 0, 1, 0);
+ }
+ ItemRenderer.renderItemIn2D(tessellator, iconMaxX, iconMinY, iconMinX, iconMaxY, icon.getIconWidth(), icon.getIconHeight(), 0.0625F);
+
+ if (hasFluid) {
+ tessellator.startDrawingQuads();
+ tessellator.setNormal(0, 0, 1);
+ tessellator.addVertexWithUV(0, 0, 0.001, maskMaxX, maskMaxY);
+ tessellator.addVertexWithUV(1, 0, 0.001, maskMinX, maskMaxY);
+ tessellator.addVertexWithUV(1, 1, 0.001, maskMinX, maskMinY);
+ tessellator.addVertexWithUV(0, 1, 0.001, maskMaxX, maskMinY);
+ tessellator.draw();
+ tessellator.startDrawingQuads();
+ tessellator.setNormal(0, 0, -1);
+ tessellator.addVertexWithUV(0, 1, -0.0635, maskMinX, maskMinY);
+ tessellator.addVertexWithUV(1, 1, -0.0635, maskMaxX, maskMinY);
+ tessellator.addVertexWithUV(1, 0, -0.0635, maskMaxX, maskMaxY);
+ tessellator.addVertexWithUV(0, 0, -0.0635, maskMinX, maskMaxY);
+ tessellator.draw();
+
+ GL11.glEnable(GL11.GL_CULL_FACE);
+ GL11.glDepthFunc(GL11.GL_EQUAL);
+ GL11.glDepthMask(false);
+ bindTexture(RenderHelper.engine(), fluidSheet);
+ OpenGlHelper.glBlendFunc(GL11.GL_ONE, GL11.GL_ZERO, GL11.GL_ONE, GL11.GL_ZERO);
+
+ tessellator.startDrawingQuads();
+ tessellator.setNormal(0, 0, 1);
+ tessellator.setColorOpaque_I(colorMult);
+ tessellator.addVertexWithUV(0, 0, 0.001, fluidMaxX, fluidMaxY);
+ tessellator.addVertexWithUV(1, 0, 0.001, fluidMinX, fluidMaxY);
+ tessellator.addVertexWithUV(1, 1, 0.001, fluidMinX, fluidMinY);
+ tessellator.addVertexWithUV(0, 1, 0.001, fluidMaxX, fluidMinY);
+ tessellator.draw();
+
+ tessellator.startDrawingQuads();
+ tessellator.setNormal(0, 0, -1);
+ tessellator.setColorOpaque_I(colorMult);
+ tessellator.addVertexWithUV(0, 1, -0.0635, fluidMinX, fluidMinY);
+ tessellator.addVertexWithUV(1, 1, -0.0635, fluidMaxX, fluidMinY);
+ tessellator.addVertexWithUV(1, 0, -0.0635, fluidMaxX, fluidMaxY);
+ tessellator.addVertexWithUV(0, 0, -0.0635, fluidMinX, fluidMaxY);
+ tessellator.draw();
+
+ GL11.glDepthMask(true);
+ GL11.glDepthFunc(GL11.GL_LEQUAL);
+ }
+ GL11.glDisable(GL12.GL_RESCALE_NORMAL);
+ }
+
+ GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture);
+ OpenGlHelper.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL11.GL_ONE, GL11.GL_ZERO);
+ GL11.glDisable(GL11.GL_ALPHA_TEST);
+ GL11.glPopMatrix();
+ }
+
+ protected void bindTexture(TextureManager renderEngine, int spriteNumber) {
+
+ if (spriteNumber == 0) {
+ renderEngine.bindTexture(RenderHelper.MC_BLOCK_SHEET);
+ } else {
+ GL11.glBindTexture(GL11.GL_TEXTURE_2D, spriteNumber);
+ }
+ }
+
+}
diff --git a/src/main/java/cofh/lib/render/RenderHelper.java b/src/main/java/cofh/lib/render/RenderHelper.java
new file mode 100644
index 00000000..84b56899
--- /dev/null
+++ b/src/main/java/cofh/lib/render/RenderHelper.java
@@ -0,0 +1,225 @@
+package cofh.lib.render;
+
+import net.minecraft.block.Block;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.ItemRenderer;
+import net.minecraft.client.renderer.RenderBlocks;
+import net.minecraft.client.renderer.Tessellator;
+import net.minecraft.client.renderer.texture.TextureManager;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.IIcon;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidRegistry;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.lwjgl.opengl.GL11;
+
+/**
+ * Contains various helper functions to assist with rendering.
+ *
+ * @author King Lemming
+ *
+ */
+public final class RenderHelper {
+
+ public static final double RENDER_OFFSET = 1.0D / 1024.0D;
+ public static final ResourceLocation MC_BLOCK_SHEET = new ResourceLocation("textures/atlas/blocks.png");
+ public static final ResourceLocation MC_ITEM_SHEET = new ResourceLocation("textures/atlas/items.png");
+ public static final ResourceLocation MC_FONT_DEFAULT = new ResourceLocation("textures/font/ascii.png");
+ public static final ResourceLocation MC_FONT_ALTERNATE = new ResourceLocation("textures/font/ascii_sga.png");
+ public static final ResourceLocation MC_ITEM_GLINT = new ResourceLocation("textures/misc/enchanted_item_glint.png");
+
+ private RenderHelper() {
+
+ }
+
+ public static final TextureManager engine() {
+
+ return Minecraft.getMinecraft().renderEngine;
+ }
+
+ public static final Tessellator tessellator() {
+
+ return Tessellator.instance;
+ }
+
+ public static void setColor3ub(int color) {
+
+ GL11.glColor3ub((byte) (color >> 16 & 0xFF), (byte) (color >> 8 & 0xFF), (byte) (color & 0xFF));
+ }
+
+ public static void setColor4ub(int color) {
+
+ GL11.glColor4ub((byte) (color >> 24 & 0xFF), (byte) (color >> 16 & 0xFF), (byte) (color >> 8 & 0xFF), (byte) (color & 0xFF));
+ }
+
+ public static void resetColor() {
+
+ GL11.glColor4f(1F, 1F, 1F, 1F);
+ }
+
+ public static void renderItemAsBlock(RenderBlocks renderer, ItemStack item, double translateX, double translateY, double translateZ) {
+
+ renderTextureAsBlock(renderer, item.getIconIndex(), translateX, translateY, translateZ);
+ }
+
+ public static void renderTextureAsBlock(RenderBlocks renderer, IIcon texture, double translateX, double translateY, double translateZ) {
+
+ Tessellator tessellator = Tessellator.instance;
+ Block block = Blocks.stone;
+
+ if (texture == null) {
+ return;
+ }
+ renderer.setRenderBoundsFromBlock(block);
+ GL11.glTranslated(translateX, translateY, translateZ);
+ tessellator.startDrawingQuads();
+
+ tessellator.setNormal(0.0F, -1.0F, 0.0F);
+ renderer.renderFaceYNeg(block, 0.0D, 0.0D, 0.0D, texture);
+
+ tessellator.setNormal(0.0F, 1.0F, 0.0F);
+ renderer.renderFaceYPos(block, 0.0D, 0.0D, 0.0D, texture);
+
+ tessellator.setNormal(0.0F, 0.0F, -1.0F);
+ renderer.renderFaceZNeg(block, 0.0D, 0.0D, 0.0D, texture);
+
+ tessellator.setNormal(0.0F, 0.0F, 1.0F);
+ renderer.renderFaceZPos(block, 0.0D, 0.0D, 0.0D, texture);
+
+ tessellator.setNormal(-1.0F, 0.0F, 0.0F);
+ renderer.renderFaceXNeg(block, 0.0D, 0.0D, 0.0D, texture);
+
+ tessellator.setNormal(1.0F, 0.0F, 0.0F);
+ renderer.renderFaceXPos(block, 0.0D, 0.0D, 0.0D, texture);
+
+ tessellator.draw();
+ }
+
+ public static void renderBlockFace(RenderBlocks renderer, IIcon texture, int face, double translateX, double translateY, double translateZ) {
+
+ Tessellator tessellator = Tessellator.instance;
+ Block block = Blocks.stone;
+
+ if (texture == null || face < 0 || face > 5) {
+ return;
+ }
+ renderer.setRenderBoundsFromBlock(block);
+ GL11.glTranslated(translateX, translateY, translateZ);
+ tessellator.startDrawingQuads();
+
+ switch (face) {
+ case 0:
+ tessellator.setNormal(0.0F, -1.0F, 0.0F);
+ renderer.renderFaceYNeg(block, 0.0D, 0.0D, 0.0D, texture);
+ break;
+ case 1:
+ tessellator.setNormal(0.0F, 1.0F, 0.0F);
+ renderer.renderFaceYPos(block, 0.0D, 0.0D, 0.0D, texture);
+ break;
+ case 2:
+ tessellator.setNormal(0.0F, 0.0F, -1.0F);
+ renderer.renderFaceZNeg(block, 0.0D, 0.0D, 0.0D, texture);
+ break;
+ case 3:
+ tessellator.setNormal(0.0F, 0.0F, 1.0F);
+ renderer.renderFaceZPos(block, 0.0D, 0.0D, 0.0D, texture);
+ break;
+ case 4:
+ tessellator.setNormal(-1.0F, 0.0F, 0.0F);
+ renderer.renderFaceXNeg(block, 0.0D, 0.0D, 0.0D, texture);
+ break;
+ case 5:
+ tessellator.setNormal(1.0F, 0.0F, 0.0F);
+ renderer.renderFaceXPos(block, 0.0D, 0.0D, 0.0D, texture);
+ break;
+ }
+ tessellator.draw();
+ }
+
+ public static void renderItemIn2D(IIcon icon) {
+
+ ItemRenderer.renderItemIn2D(Tessellator.instance, icon.getMaxU(), icon.getMinV(), icon.getMinU(), icon.getMaxV(), icon.getIconWidth(),
+ icon.getIconHeight(), 0.0625F);
+ }
+
+ public static void renderIcon(IIcon icon, double z) {
+
+ Tessellator.instance.startDrawingQuads();
+ Tessellator.instance.addVertexWithUV(0, 16, z, icon.getMinU(), icon.getMaxV());
+ Tessellator.instance.addVertexWithUV(16, 16, z, icon.getMaxU(), icon.getMaxV());
+ Tessellator.instance.addVertexWithUV(16, 0, z, icon.getMaxU(), icon.getMinV());
+ Tessellator.instance.addVertexWithUV(0, 0, z, icon.getMinU(), icon.getMinV());
+ Tessellator.instance.draw();
+ }
+
+ public static void renderIcon(double x, double y, double z, IIcon icon, int width, int height) {
+
+ Tessellator tessellator = Tessellator.instance;
+ tessellator.startDrawingQuads();
+ tessellator.addVertexWithUV(x, y + height, z, icon.getMinU(), icon.getMaxV());
+ tessellator.addVertexWithUV(x + width, y + height, z, icon.getMaxU(), icon.getMaxV());
+ tessellator.addVertexWithUV(x + width, y, z, icon.getMaxU(), icon.getMinV());
+ tessellator.addVertexWithUV(x, y, z, icon.getMinU(), icon.getMinV());
+ tessellator.draw();
+ }
+
+ public static final IIcon getFluidTexture(Fluid fluid) {
+
+ if (fluid == null) {
+ return FluidRegistry.LAVA.getIcon();
+ }
+ return fluid.getIcon();
+ }
+
+ public static final IIcon getFluidTexture(FluidStack fluid) {
+
+ if (fluid == null || fluid.getFluid() == null || fluid.getFluid().getIcon(fluid) == null) {
+ return FluidRegistry.LAVA.getIcon();
+ }
+ return fluid.getFluid().getIcon(fluid);
+ }
+
+ public static final void bindItemTexture(ItemStack stack) {
+
+ engine().bindTexture(stack.getItemSpriteNumber() == 0 ? MC_BLOCK_SHEET : MC_ITEM_SHEET);
+ }
+
+ public static final void bindTexture(ResourceLocation texture) {
+
+ engine().bindTexture(texture);
+ }
+
+ public static final void setBlockTextureSheet() {
+
+ bindTexture(MC_BLOCK_SHEET);
+ }
+
+ public static final void setItemTextureSheet() {
+
+ bindTexture(MC_ITEM_SHEET);
+ }
+
+ public static final void setDefaultFontTextureSheet() {
+
+ bindTexture(MC_FONT_DEFAULT);
+ }
+
+ public static final void setSGAFontTextureSheet() {
+
+ bindTexture(MC_FONT_ALTERNATE);
+ }
+
+ public static final void enableGUIStandardItemLighting() {
+
+ net.minecraft.client.renderer.RenderHelper.enableGUIStandardItemLighting();
+ }
+
+ public static void enableStandardItemLighting() {
+
+ net.minecraft.client.renderer.RenderHelper.enableStandardItemLighting();
+ }
+
+}
diff --git a/src/main/java/cofh/lib/render/RenderItemAsBlock.java b/src/main/java/cofh/lib/render/RenderItemAsBlock.java
new file mode 100644
index 00000000..ff0ea5c7
--- /dev/null
+++ b/src/main/java/cofh/lib/render/RenderItemAsBlock.java
@@ -0,0 +1,43 @@
+package cofh.lib.render;
+
+import net.minecraft.client.renderer.RenderBlocks;
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.client.IItemRenderer;
+
+import org.lwjgl.opengl.GL11;
+
+/**
+ * Easy way of rendering an item which should look like a block.
+ *
+ * @author King Lemming
+ *
+ */
+public class RenderItemAsBlock implements IItemRenderer {
+
+ public static RenderItemAsBlock instance = new RenderItemAsBlock();
+
+ @Override
+ public boolean handleRenderType(ItemStack item, ItemRenderType type) {
+
+ return true;
+ }
+
+ @Override
+ public boolean shouldUseRenderHelper(ItemRenderType type, ItemStack item, ItemRendererHelper helper) {
+
+ return true;
+ }
+
+ @Override
+ public void renderItem(ItemRenderType type, ItemStack item, Object... data) {
+
+ double offset = -0.5;
+ if (type == ItemRenderType.EQUIPPED || type == ItemRenderType.EQUIPPED_FIRST_PERSON) {
+ offset = 0;
+ } else if (type == ItemRenderType.ENTITY) {
+ GL11.glScalef(0.5F, 0.5F, 0.5F);
+ }
+ RenderHelper.renderItemAsBlock((RenderBlocks) data[0], item, offset, offset, offset);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/render/package-info.java b/src/main/java/cofh/lib/render/package-info.java
new file mode 100644
index 00000000..bf6187bf
--- /dev/null
+++ b/src/main/java/cofh/lib/render/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHLibProps.VERSION, owner = "CoFHLib", provides = "CoFHLib|render")
+package cofh.lib.render;
+
+import cofh.lib.CoFHLibProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/lib/render/particle/EntityDropParticleFX.java b/src/main/java/cofh/lib/render/particle/EntityDropParticleFX.java
new file mode 100644
index 00000000..e0fff43a
--- /dev/null
+++ b/src/main/java/cofh/lib/render/particle/EntityDropParticleFX.java
@@ -0,0 +1,96 @@
+package cofh.lib.render.particle;
+
+import cofh.lib.util.helpers.MathHelper;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+import net.minecraft.block.BlockLiquid;
+import net.minecraft.block.material.Material;
+import net.minecraft.client.particle.EntityFX;
+import net.minecraft.world.World;
+
+@SideOnly(Side.CLIENT)
+public class EntityDropParticleFX extends EntityFX {
+
+ private int bobTimer;
+
+ public EntityDropParticleFX(World world, double x, double y, double z, float particleRed, float particleGreen, float particleBlue) {
+
+ this(world, x, y, z, particleRed, particleGreen, particleBlue, -1);
+ }
+
+ public EntityDropParticleFX(World world, double x, double y, double z, float particleRed, float particleGreen, float particleBlue, int gravityMod) {
+
+ super(world, x, y, z, 0.0D, 0.0D, 0.0D);
+ this.motionX = this.motionY = this.motionZ = 0.0D;
+
+ this.particleRed = particleRed;
+ this.particleGreen = particleGreen;
+ this.particleBlue = particleBlue;
+
+ this.setParticleTextureIndex(113);
+ this.setSize(0.01F, 0.01F);
+ this.particleGravity = -0.06F * gravityMod;
+ this.bobTimer = 40;
+ this.particleMaxAge = (int) (48.0D / (Math.random() * 0.8D + 0.2D));
+ this.motionX = this.motionY = this.motionZ = 0.0D;
+ }
+
+ @Override
+ public void onUpdate() {
+
+ this.prevPosX = this.posX;
+ this.prevPosY = this.posY;
+ this.prevPosZ = this.posZ;
+
+ this.motionY -= this.particleGravity;
+
+ if (this.bobTimer-- > 0) {
+ this.motionX *= 0.02D;
+ this.motionY *= 0.02D;
+ this.motionZ *= 0.02D;
+ this.setParticleTextureIndex(113);
+ } else {
+ this.setParticleTextureIndex(112);
+ }
+ this.moveEntity(this.motionX, this.motionY, this.motionZ);
+ this.motionX *= 0.9800000190734863D;
+ this.motionY *= 0.9800000190734863D;
+ this.motionZ *= 0.9800000190734863D;
+
+ if (this.particleMaxAge-- <= 0) {
+ this.setDead();
+ }
+ if (this.onGround) {
+ this.setParticleTextureIndex(114);
+ this.motionX *= 0.699999988079071D;
+ this.motionZ *= 0.699999988079071D;
+ }
+ if (this.particleGravity > 0) {
+ Material material = this.worldObj.getBlock(MathHelper.floor(this.posX), MathHelper.floor(this.posY), MathHelper.floor(this.posZ)).getMaterial();
+
+ if (material.isLiquid() || material.isSolid()) {
+ double d0 = MathHelper.floor(this.posY)
+ + 1
+ - BlockLiquid.getLiquidHeightPercent(this.worldObj.getBlockMetadata(MathHelper.floor(this.posX), MathHelper.floor(this.posY),
+ MathHelper.floor(this.posZ)));
+ if (this.posY < d0) {
+ this.setDead();
+ }
+ }
+ } else {
+ Material material = this.worldObj.getBlock(MathHelper.ceil(this.posX), MathHelper.ceil(this.posY), MathHelper.ceil(this.posZ)).getMaterial();
+
+ if (material.isLiquid() || material.isSolid()) {
+ double d0 = MathHelper.ceil(this.posY)
+ + 1
+ - BlockLiquid.getLiquidHeightPercent(this.worldObj.getBlockMetadata(MathHelper.ceil(this.posX), MathHelper.ceil(this.posY),
+ MathHelper.ceil(this.posZ)));
+ if (this.posY > d0) {
+ this.setDead();
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/cofh/lib/render/particle/package-info.java b/src/main/java/cofh/lib/render/particle/package-info.java
new file mode 100644
index 00000000..5c532427
--- /dev/null
+++ b/src/main/java/cofh/lib/render/particle/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHLibProps.VERSION, owner = "CoFHLib", provides = "CoFHLib|render|particle")
+package cofh.lib.render.particle;
+
+import cofh.lib.CoFHLibProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/lib/transport/ClientEnderChannelRegistry.java b/src/main/java/cofh/lib/transport/ClientEnderChannelRegistry.java
new file mode 100644
index 00000000..af7263e1
--- /dev/null
+++ b/src/main/java/cofh/lib/transport/ClientEnderChannelRegistry.java
@@ -0,0 +1,86 @@
+package cofh.lib.transport;
+
+import cofh.lib.network.ByteBufHelper;
+
+import gnu.trove.map.hash.TIntObjectHashMap;
+
+import io.netty.buffer.ByteBuf;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class ClientEnderChannelRegistry implements IEnderChannelRegistry {
+
+ private TIntObjectHashMap channel = new TIntObjectHashMap();
+ private ArrayList list = new ArrayList();
+ private int modCount;
+ protected String hostedChannel = "";
+
+ public ClientEnderChannelRegistry() {
+
+ }
+
+ public void readFrequencyData(ByteBuf data) {
+
+ ++modCount;
+ channel.clear();
+ list.clear();
+ int size = ByteBufHelper.readVarInt(data);
+ hostedChannel = ByteBufHelper.readString(data);
+ for (int i = 0; i < size; ++i) {
+ int freq = ByteBufHelper.readVarInt(data);
+ String name = ByteBufHelper.readString(data);
+ channel.put(freq, name);
+ list.add(new Frequency(freq, name));
+ }
+ Collections.sort(list);
+ }
+
+ public String getChannelName() {
+
+ return hostedChannel;
+ }
+
+ @Override
+ public List getFrequencyList(String _) {
+
+ return list;
+ }
+
+ @Override
+ public String getFrequency(String _, int freq) {
+
+ return channel.get(freq);
+ }
+
+ @Override
+ public String setFrequency(String _, int freq, String name) {
+
+ ++modCount;
+ Frequency f = new Frequency(freq, name);
+ int i = list.indexOf(f);
+ if (i < 0) {
+ list.add(f);
+ Collections.sort(list);
+ } else {
+ list.set(i, f);
+ }
+ return channel.put(freq, name);
+ }
+
+ @Override
+ public String removeFrequency(String _, int freq) {
+
+ ++modCount;
+ list.remove(new Frequency(freq, ""));
+ return channel.remove(freq);
+ }
+
+ @Override
+ public int updated() {
+
+ return modCount;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/transport/EnderRegistry.java b/src/main/java/cofh/lib/transport/EnderRegistry.java
new file mode 100644
index 00000000..e535d17d
--- /dev/null
+++ b/src/main/java/cofh/lib/transport/EnderRegistry.java
@@ -0,0 +1,488 @@
+package cofh.lib.transport;
+
+import cofh.api.transport.IEnderAttuned;
+import cofh.api.transport.IEnderDestination;
+import cofh.api.transport.IEnderEnergyHandler;
+import cofh.api.transport.IEnderFluidHandler;
+import cofh.api.transport.IEnderItemHandler;
+import cofh.lib.util.ArrayHashList;
+
+import gnu.trove.map.hash.TIntObjectHashMap;
+
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.List;
+
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.WorldServer;
+import net.minecraftforge.common.DimensionManager;
+import net.minecraftforge.common.config.ConfigCategory;
+import net.minecraftforge.common.config.Configuration;
+import net.minecraftforge.common.config.Property;
+
+public final class EnderRegistry {
+
+ private HashMap>> inputItem;
+ private HashMap>> inputFluid;
+ private HashMap>> inputEnergy;
+
+ private HashMap>> outputItem;
+ private HashMap>> outputFluid;
+ private HashMap>> outputEnergy;
+
+ private HashMap> outputTeleport;
+ private HashMap usedTeleports;
+
+ private Configuration linkConf;
+
+ public EnderRegistry(Configuration config) {
+
+ inputItem = new HashMap>>();
+ inputFluid = new HashMap>>();
+ inputEnergy = new HashMap>>();
+
+ outputItem = new HashMap>>();
+ outputFluid = new HashMap>>();
+ outputEnergy = new HashMap>>();
+
+ outputTeleport = new HashMap>();
+ usedTeleports = new HashMap();
+
+ linkConf = config;
+ load();
+ }
+
+ private void load() {
+
+ for (String channel : linkConf.getCategoryNames()) {
+ ConfigCategory category = linkConf.getCategory(channel);
+ TIntObjectHashMap map = outputTeleport.get(channel);
+ BitSet set = usedTeleports.get(channel);
+ if (map == null) {
+ outputTeleport.put(channel, map = new TIntObjectHashMap());
+ usedTeleports.put(channel, set = new BitSet());
+ }
+ for (Property prop : category.values()) {
+ try {
+ int freq = Integer.parseInt(prop.getName());
+ String[] data = prop.getString().split("@");
+ if (data.length != 2) {
+ continue;
+ }
+ int dimension = Integer.parseInt(data[0]);
+ data = data[1].split(",");
+ if (data.length != 3) {
+ continue;
+ }
+ int x, y, z;
+ x = Integer.parseInt(data[0]);
+ y = Integer.parseInt(data[1]);
+ z = Integer.parseInt(data[2]);
+ EnderDestination dest = new EnderDestination(x, y, z, dimension);
+ map.put(freq, dest);
+ set.set(freq);
+ } catch (Throwable p) {
+ }
+ }
+ }
+ }
+
+ public void save() {
+
+ if (linkConf.hasChanged()) {
+ linkConf.save();
+ }
+ }
+
+ private T getElement(HashMap> map, IEnderAttuned theAttuned) {
+
+ TIntObjectHashMap list = map.get(theAttuned.getChannelString());
+ if (list != null) {
+ return list.get(theAttuned.getFrequency());
+ }
+ return null;
+ }
+
+ public List getLinkedItemInputs(IEnderItemHandler theAttuned) {
+
+ return getElement(inputItem, theAttuned);
+ }
+
+ public List getLinkedItemOutputs(IEnderItemHandler theAttuned) {
+
+ return getElement(outputItem, theAttuned);
+ }
+
+ public List getLinkedFluidInputs(IEnderFluidHandler theAttuned) {
+
+ return getElement(inputFluid, theAttuned);
+ }
+
+ public List getLinkedFluidOutputs(IEnderFluidHandler theAttuned) {
+
+ return getElement(outputFluid, theAttuned);
+ }
+
+ public List getLinkedEnergyInputs(IEnderEnergyHandler theAttuned) {
+
+ return getElement(inputEnergy, theAttuned);
+ }
+
+ public List getLinkedEnergyOutputs(IEnderEnergyHandler theAttuned) {
+
+ return getElement(outputEnergy, theAttuned);
+ }
+
+ public int findFreeFrequency(String channel) {
+
+ BitSet set = usedTeleports.get(channel);
+ if (set == null) {
+ return 0;
+ }
+ return set.nextClearBit(0);
+ }
+
+ public boolean hasDestination(IEnderDestination theAttuned) {
+
+ return hasDestination(theAttuned, true);
+ }
+
+ public boolean hasDestination(IEnderDestination theAttuned, boolean to) {
+
+ String channel = theAttuned.getChannelString();
+ TIntObjectHashMap map = outputTeleport.get(channel);
+ if (map == null) {
+ return false;
+ }
+ int freq = to ? theAttuned.getDestination() : theAttuned.getFrequency();
+ EnderDestination dest = map.get(freq);
+ boolean r = dest == null ? false : dest.hasOutput();
+ usedTeleports.get(channel).set(freq, r);
+ return r;
+ }
+
+ public IEnderDestination getDestination(IEnderDestination theAttuned) {
+
+ return getDestination(theAttuned, true);
+ }
+
+ public IEnderDestination getDestination(IEnderDestination theAttuned, boolean requireLoaded) {
+
+ final String channel = theAttuned.getChannelString();
+ TIntObjectHashMap map = outputTeleport.get(channel);
+ if (map == null) {
+ return null;
+ }
+ final int frequency = theAttuned.getDestination();
+ final EnderDestination dest = map.get(frequency);
+ if (dest == null) {
+ return null;
+ }
+ IEnderDestination out = dest.getOutput(requireLoaded);
+ if (requireLoaded && out == null && !dest.isInvalid) {
+ return new IEnderDestination() {
+
+ @Override
+ public String getChannelString() {
+
+ return channel;
+ }
+
+ @Override
+ public int getFrequency() {
+
+ return frequency;
+ }
+
+ @Override
+ public boolean setFrequency(int frequency) {
+
+ return false;
+ }
+
+ @Override
+ public boolean clearFrequency() {
+
+ return false;
+ }
+
+ @Override
+ public boolean isNotValid() {
+
+ return true;
+ }
+
+ @Override
+ public int x() {
+
+ return dest.x;
+ }
+
+ @Override
+ public int y() {
+
+ return dest.y;
+ }
+
+ @Override
+ public int z() {
+
+ return dest.z;
+ }
+
+ @Override
+ public int dimension() {
+
+ return dest.dimension;
+ }
+
+ @Override
+ public int getDestination() {
+
+ return -1;
+ }
+
+ @Override
+ public boolean setDestination(int frequency) {
+
+ return false;
+ }
+
+ @Override
+ public boolean clearDestination() {
+
+ return false;
+ }
+
+ };
+ }
+ return out;
+ }
+
+ /* HELPER FUNCTIONS */
+ private boolean addHandler(HashMap>> map, T theAttuned) {
+
+ String channel = theAttuned.getChannelString();
+ TIntObjectHashMap> list = map.get(channel);
+ if (list == null) {
+ map.put(channel, list = new TIntObjectHashMap>());
+ }
+ int freq = theAttuned.getFrequency();
+ ArrayHashList array = list.get(freq);
+ if (array == null) {
+ list.put(freq, array = new ArrayHashList());
+ }
+ return array.add(theAttuned);
+ }
+
+ public void addItemHandler(IEnderItemHandler theAttuned) {
+
+ if (theAttuned.canSendItems()) {
+ addHandler(inputItem, theAttuned);
+ }
+ if (theAttuned.canReceiveItems()) {
+ addHandler(outputItem, theAttuned);
+ }
+ }
+
+ public void addFluidHandler(IEnderFluidHandler theAttuned) {
+
+ if (theAttuned.canSendFluid()) {
+ addHandler(inputFluid, theAttuned);
+ }
+ if (theAttuned.canReceiveFluid()) {
+ addHandler(outputFluid, theAttuned);
+ }
+ }
+
+ public void addEnergyHandler(IEnderEnergyHandler theAttuned) {
+
+ if (theAttuned.canSendEnergy()) {
+ addHandler(inputEnergy, theAttuned);
+ }
+ if (theAttuned.canReceiveEnergy()) {
+ addHandler(outputEnergy, theAttuned);
+ }
+ }
+
+ public void addDestination(IEnderDestination theAttuned) {
+
+ if (!hasDestination(theAttuned, false)) {
+ String channel = theAttuned.getChannelString();
+ TIntObjectHashMap map = outputTeleport.get(channel);
+ BitSet set = usedTeleports.get(channel);
+ if (map == null) {
+ outputTeleport.put(channel, map = new TIntObjectHashMap());
+ usedTeleports.put(channel, set = new BitSet());
+ }
+ int freq = theAttuned.getFrequency();
+ EnderDestination dest = new EnderDestination(theAttuned);
+ map.put(freq, dest);
+ set.set(freq);
+ linkConf.get(channel, String.valueOf(freq), "").set(dest.toString());
+ }
+ }
+
+ private boolean removeHandler(HashMap>> map, IEnderAttuned theAttuned) {
+
+ TIntObjectHashMap> list = map.get(theAttuned.getChannelString());
+ if (list == null) {
+ return false;
+ }
+ ArrayHashList array = list.get(theAttuned.getFrequency());
+ if (array == null) {
+ return false;
+ }
+ return array.remove(theAttuned);
+ }
+
+ public void removeItemHandler(IEnderItemHandler theAttuned) {
+
+ removeHandler(inputItem, theAttuned);
+ removeHandler(outputItem, theAttuned);
+ }
+
+ public void removeFluidHandler(IEnderFluidHandler theAttuned) {
+
+ removeHandler(inputFluid, theAttuned);
+ removeHandler(outputFluid, theAttuned);
+ }
+
+ public void removeEnergyHandler(IEnderEnergyHandler theAttuned) {
+
+ removeHandler(inputEnergy, theAttuned);
+ removeHandler(outputEnergy, theAttuned);
+ }
+
+ public void removeDestination(IEnderDestination theAttuned) {
+
+ String channel = theAttuned.getChannelString();
+ TIntObjectHashMap map = outputTeleport.get(channel);
+ if (map == null) {
+ return;
+ }
+ int freq = theAttuned.getFrequency();
+ EnderDestination dest = map.get(freq);
+ if (dest == null) {
+ return;
+ }
+ if (dest.dimension == theAttuned.dimension()
+ && dest.x == theAttuned.x() && dest.y == theAttuned.y() && dest.z == theAttuned.z()) {
+ map.remove(freq);
+ usedTeleports.get(channel).set(freq, false);
+ linkConf.getCategory(channel).remove(String.valueOf(freq));
+ }
+ }
+
+ public void add(IEnderAttuned theAttuned) {
+
+ if (theAttuned instanceof IEnderItemHandler) {
+ addItemHandler((IEnderItemHandler) theAttuned);
+ }
+ if (theAttuned instanceof IEnderFluidHandler) {
+ addFluidHandler((IEnderFluidHandler) theAttuned);
+ }
+ if (theAttuned instanceof IEnderEnergyHandler) {
+ addEnergyHandler((IEnderEnergyHandler) theAttuned);
+ }
+ if (theAttuned instanceof IEnderDestination) {
+ addDestination((IEnderDestination) theAttuned);
+ }
+ }
+
+ public void remove(IEnderAttuned theAttuned) {
+
+ if (theAttuned instanceof IEnderItemHandler) {
+ removeItemHandler((IEnderItemHandler) theAttuned);
+ }
+ if (theAttuned instanceof IEnderFluidHandler) {
+ removeFluidHandler((IEnderFluidHandler) theAttuned);
+ }
+ if (theAttuned instanceof IEnderEnergyHandler) {
+ removeEnergyHandler((IEnderEnergyHandler) theAttuned);
+ }
+ if (theAttuned instanceof IEnderDestination) {
+ removeDestination((IEnderDestination) theAttuned);
+ }
+ }
+
+ private static class EnderDestination {
+
+ private final int dimension;
+ private final int x, y, z;
+ private IEnderDestination output;
+ private boolean isInvalid;
+
+ public EnderDestination(IEnderDestination output) {
+
+ x = output.x();
+ y = output.y();
+ z = output.z();
+ dimension = output.dimension();
+ this.output = output;
+ }
+
+ private EnderDestination(int x, int y, int z, int dimension) {
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.dimension = dimension;
+ }
+
+ public boolean hasOutput() {
+
+ return !isInvalid && DimensionManager.isDimensionRegistered(dimension);
+ }
+
+ public IEnderDestination getOutput(boolean onlyLoaded) {
+
+ if (output == null || output.isNotValid()) {
+ output = null;
+ if (!DimensionManager.isDimensionRegistered(dimension)) {
+ return null;
+ }
+ WorldServer world = DimensionManager.getWorld(dimension);
+ if (world == null && !onlyLoaded) {
+ DimensionManager.initDimension(dimension);
+ world = DimensionManager.getWorld(dimension);
+ } else {
+ return null;
+ }
+ if (world.blockExists(x, y, z)) {
+ TileEntity te = world.getTileEntity(x, y, z);
+ if (te instanceof IEnderDestination) {
+ output = (IEnderDestination) te;
+ } else {
+ isInvalid = true;
+ }
+ }
+ }
+ return output;
+ }
+
+ @Override
+ public String toString() {
+
+ return String.format("%s@%s,%s,%s", dimension, x, y, z);
+ }
+
+ @Override
+ public int hashCode() {
+
+ return dimension ^ (y + x * (z * 100));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+
+ if (o == null || o.getClass() != EnderDestination.class) {
+ return false;
+ }
+ EnderDestination other = (EnderDestination) o;
+ return other.x == x && other.y == y && other.z == z && other.dimension == dimension;
+ }
+
+ }
+
+}
diff --git a/src/main/java/cofh/lib/transport/IEnderChannelRegistry.java b/src/main/java/cofh/lib/transport/IEnderChannelRegistry.java
new file mode 100644
index 00000000..45c09113
--- /dev/null
+++ b/src/main/java/cofh/lib/transport/IEnderChannelRegistry.java
@@ -0,0 +1,54 @@
+package cofh.lib.transport;
+
+import com.google.common.primitives.Ints;
+
+import java.util.List;
+
+public interface IEnderChannelRegistry {
+
+ public List getFrequencyList(String channel);
+
+ public String getFrequency(String channel, int freq);
+
+ public String setFrequency(String channel, int freq, String name);
+
+ public String removeFrequency(String channel, int freq);
+
+ public int updated();
+
+ public static class Frequency implements Comparable {
+
+ public final int freq;
+ public final String name;
+
+ public Frequency(int freq, String name) {
+
+ this.freq = freq;
+ this.name = name;
+ }
+
+ @Override
+ public int compareTo(Frequency o) {
+
+ if (o == null) {
+ return 1;
+ }
+ return Ints.compare(freq, o.freq);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+
+ if (o instanceof Frequency) {
+ return ((Frequency) o).freq == freq;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+
+ return freq;
+ }
+ }
+}
diff --git a/src/main/java/cofh/lib/transport/ServerEnderChannelRegistry.java b/src/main/java/cofh/lib/transport/ServerEnderChannelRegistry.java
new file mode 100644
index 00000000..a24ce226
--- /dev/null
+++ b/src/main/java/cofh/lib/transport/ServerEnderChannelRegistry.java
@@ -0,0 +1,148 @@
+package cofh.lib.transport;
+
+import cofh.lib.network.ByteBufHelper;
+
+import gnu.trove.iterator.TIntObjectIterator;
+import gnu.trove.map.hash.TIntObjectHashMap;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+
+import net.minecraftforge.common.config.ConfigCategory;
+import net.minecraftforge.common.config.Configuration;
+import net.minecraftforge.common.config.Property;
+
+public class ServerEnderChannelRegistry implements IEnderChannelRegistry {
+
+ protected Configuration linkConf;
+ protected HashMap> channels;
+ private int modCount;
+
+ public ServerEnderChannelRegistry(Configuration config) {
+
+ channels = new HashMap>();
+
+ linkConf = config;
+ load();
+ }
+
+ protected void load() {
+
+ ++modCount;
+ for (String channel : linkConf.getCategoryNames()) {
+ ConfigCategory category = linkConf.getCategory(channel);
+ TIntObjectHashMap map = channels.get(channel);
+ if (map == null) {
+ channels.put(channel, map = new TIntObjectHashMap());
+ }
+ for (Property prop : category.values()) {
+ try {
+ int freq = Integer.parseInt(prop.getName());
+ map.put(freq, prop.getString());
+ } catch (Throwable p) {
+ }
+ }
+ }
+ }
+
+ public void save() {
+
+ if (linkConf.hasChanged()) {
+ linkConf.save();
+ }
+ }
+
+ /**
+ * Returns nulls or a ByteBuf of the frequency<->name mappings for channel
+ * Format:
+ *
+ * VarInt entries : VarInt frequency ; VarInt length ; byte[] UTF8
+ *
+ *
+ * @param channel
+ * The channel to get frequency data for
+ * @return A ByteBuf of the frequency data for channel
or null if the channel does not exist.
+ */
+ public ByteBuf getFrequencyData(String channel) {
+
+ TIntObjectHashMap map = channels.get(channel);
+ ByteBuf ret = Unpooled.buffer();
+ if (map != null) {
+ TIntObjectIterator iter = map.iterator(); // allocate before size() so a comod throws correctly
+ ByteBufHelper.writeVarInt(map.size(), ret);
+ ByteBufHelper.writeString(channel, ret);
+ for (; iter.hasNext();) {
+ iter.advance();
+ ByteBufHelper.writeVarInt(iter.key(), ret);
+ ByteBufHelper.writeString(iter.value(), ret);
+ }
+ } else {
+ ByteBufHelper.writeVarInt(0, ret);
+ ByteBufHelper.writeString(channel, ret);
+ }
+ return ret;
+ }
+
+ @Override
+ public List getFrequencyList(String channel) {
+
+ LinkedList ret = new LinkedList();
+ TIntObjectHashMap map = channels.get(channel);
+ if (map != null) {
+ for (TIntObjectIterator iter = map.iterator(); iter.hasNext();) {
+ iter.advance();
+ ret.add(new Frequency(iter.key(), iter.value()));
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public String getFrequency(String channel, int freq) {
+
+ TIntObjectHashMap map = channels.get(channel);
+ if (map != null) {
+ return map.get(freq);
+ }
+ return null;
+ }
+
+ @Override
+ public String setFrequency(String channel, int freq, String name) {
+
+ TIntObjectHashMap map = channels.get(channel);
+ if (map == null) {
+ channels.put(channel, map = new TIntObjectHashMap());
+ }
+ String old = map.put(freq, name);
+ ++modCount;
+ linkConf.get(channel, String.valueOf(freq), "").set(name);
+ return old;
+ }
+
+ @Override
+ public String removeFrequency(String channel, int freq) {
+
+ TIntObjectHashMap map = channels.get(channel);
+ if (map == null) {
+ return null;
+ }
+ String old = map.remove(freq);
+ if (old != null) {
+ ++modCount;
+ linkConf.getCategory(channel).remove(String.valueOf(freq));
+ }
+ return old;
+ }
+
+ @Override
+ public int updated() {
+
+ return modCount;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/ArrayHashList.java b/src/main/java/cofh/lib/util/ArrayHashList.java
new file mode 100644
index 00000000..c0d903c3
--- /dev/null
+++ b/src/main/java/cofh/lib/util/ArrayHashList.java
@@ -0,0 +1,626 @@
+package cofh.lib.util;
+
+import com.google.common.base.Objects;
+import com.google.common.primitives.Ints;
+
+import java.util.AbstractCollection;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+
+@SuppressWarnings("unchecked")
+public class ArrayHashList extends AbstractCollection implements List, Cloneable, java.io.Serializable {
+
+ private static final long serialVersionUID = 3230581060536180693L;
+
+ protected static final class Entry {
+
+ final Object key;
+ final int hash;
+ Entry nextInBucket;
+
+ protected Entry(Object key, int keyHash) {
+
+ this.key = key;
+ this.hash = keyHash;
+ }
+ }
+
+ private static int roundUpToPowerOf2(int number) {
+
+ return number >= Ints.MAX_POWER_OF_TWO ? Ints.MAX_POWER_OF_TWO : (number > 2) ? Integer.highestOneBit((number - 1) << 1) : 2;
+ }
+
+ private transient Object[] elementData;
+ protected transient int size;
+ protected transient int mask;
+ protected transient Entry[] hashTable;
+ protected transient int modCount;
+
+ public ArrayHashList() {
+
+ elementData = new Object[10];
+ hashTable = new Entry[8];
+ mask = 7;
+ }
+
+ public ArrayHashList(int size) {
+
+ elementData = new Object[size];
+ size = roundUpToPowerOf2(size) >> 1;
+ hashTable = new Entry[size];
+ mask = size - 1;
+ }
+
+ public ArrayHashList(Collection col) {
+
+ int size = col.size();
+ elementData = new Object[size];
+ size = roundUpToPowerOf2(size) >> 1;
+ hashTable = new Entry[size];
+ mask = size - 1;
+ addAll(col);
+ }
+
+ protected int hash(Object n) {
+
+ int h = n == null ? 0 : n.hashCode();
+ h ^= (h >>> 20) ^ (h >>> 12);
+ return h ^ (h >>> 7) ^ (h >>> 4);
+ }
+
+ @Override
+ public int size() {
+
+ return size;
+ }
+
+ protected void add(E obj, int hash) {
+
+ ensureCapacityInternal(size + 1);
+ elementData[size++] = obj;
+ insert(new Entry(obj, hash));
+ rehashIfNecessary();
+ }
+
+ @Override
+ public boolean add(E obj) {
+
+ int hash = hash(obj);
+ if (seek(obj, hash) != null) {
+ return false;
+ }
+
+ add(obj, hash);
+
+ return true;
+ }
+
+ @Override
+ public E set(int index, E obj) {
+
+ checkElementIndex(index);
+
+ int hash = hash(obj);
+ if (seek(obj, hash) != null) {
+ // return null;
+ throw new IllegalArgumentException("Duplicate entries not allowed");
+ }
+
+ ++modCount;
+ Entry e = seek(elementData[index], hash(elementData[index]));
+ delete(e);
+ elementData[index] = obj;
+ insert(new Entry(obj, hash));
+
+ return (E) e.key;
+ }
+
+ @Override
+ public void add(int index, E obj) {
+
+ checkPositionIndex(index);
+
+ int hash = hash(obj);
+ if (seek(obj, hash) != null) {
+ throw new IllegalArgumentException("Duplicate entries not allowed");
+ }
+
+ if (index == size) {
+ add(obj, hash);
+ return;
+ }
+
+ ensureCapacityInternal(++size);
+ System.arraycopy(elementData, index, elementData, index + 1, size - index - 1);
+ elementData[index] = obj;
+ insert(new Entry(obj, hash));
+ rehashIfNecessary();
+ }
+
+ @Override
+ public boolean addAll(int index, Collection extends E> c) {
+
+ if (c.size() == 0) {
+ return false;
+ }
+
+ for (E e : c) {
+ add(index++, e);
+ }
+
+ return true;
+ }
+
+ @Override
+ public E get(int index) {
+
+ checkElementIndex(index);
+ return index(index);
+ }
+
+ @Override
+ public int indexOf(Object obj) {
+
+ Entry e = seek(obj, hash(obj));
+ if (e == null) {
+ return -1;
+ }
+
+ Object o = e.key;
+ Object[] data = elementData;
+ int i = size;
+ while (i-- > 0) {
+ if (data[i] == o) {
+ break;
+ }
+ }
+ return i;
+ }
+
+ @Override
+ public int lastIndexOf(Object o) {
+
+ return indexOf(o);
+ }
+
+ @Override
+ public boolean contains(Object obj) {
+
+ return seek(obj, hash(obj)) != null;
+ }
+
+ @Override
+ public E remove(int index) {
+
+ checkElementIndex(index);
+
+ E oldValue = index(index);
+ delete(seek(oldValue, hash(oldValue)));
+ fastRemove(index);
+
+ return oldValue;
+ }
+
+ @Override
+ public boolean remove(Object obj) {
+
+ Entry e = seek(obj, hash(obj));
+ if (e == null) {
+ return false;
+ }
+
+ Object o = e.key;
+ Object[] data = elementData;
+ for (int i = size; i-- > 0;) {
+ if (data[i] == o) {
+ fastRemove(i);
+ break;
+ }
+ }
+ delete(e);
+ return true;
+ }
+
+ private void fastRemove(int index) {
+
+ modCount++;
+ int numMoved = size - index - 1;
+ if (numMoved > 0) {
+ System.arraycopy(elementData, index + 1, elementData, index, numMoved);
+ }
+ elementData[--size] = null; // clear to let GC do its work
+ }
+
+ // { following methods (until the next }) copied mostly verbatim from ArrayList
+ @Override
+ public void clear() {
+
+ modCount++;
+
+ // clear to let GC do its work
+ for (int i = 0; i < size; i++) {
+ elementData[i] = null;
+ }
+
+ for (int i = hashTable.length; i-- > 0;) {
+ hashTable[i] = null;
+ }
+
+ size = 0;
+ }
+
+ /**
+ * Trims the capacity of this ArrayHashList instance to be the list's current size. An application can use this operation to minimize the storage
+ * of an ArrayHashList instance.
+ */
+ public void trimToSize() {
+
+ ++modCount;
+ if (size < elementData.length) {
+ elementData = Arrays.copyOf(elementData, size);
+ }
+ }
+
+ /**
+ * Increases the capacity of this ArrayHashList instance, if necessary, to ensure that it can hold at least the number of elements specified by the
+ * minimum capacity argument.
+ *
+ * @param minCapacity
+ * the desired minimum capacity
+ */
+ public void ensureCapacity(int minCapacity) {
+
+ if (minCapacity > 0) {
+ ensureCapacityInternal(minCapacity);
+ }
+ }
+
+ private void ensureCapacityInternal(int minCapacity) {
+
+ ++modCount;
+ // overflow-conscious code
+ if (minCapacity - elementData.length > 0) {
+ grow(minCapacity);
+ }
+ }
+
+ /**
+ * The maximum size of array to allocate. Some VMs reserve some header words in an array. Attempts to allocate larger arrays may result in OutOfMemoryError:
+ * Requested array size exceeds VM limit
+ */
+ private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
+
+ /**
+ * Increases the capacity to ensure that it can hold at least the number of elements specified by the minimum capacity argument.
+ *
+ * @param minCapacity
+ * the desired minimum capacity
+ */
+ private void grow(int minCapacity) {
+
+ // overflow-conscious code
+ int oldCapacity = elementData.length;
+ int newCapacity = oldCapacity + (oldCapacity >> 1);
+ if (newCapacity - minCapacity < 0) {
+ newCapacity = minCapacity;
+ }
+ if (newCapacity - MAX_ARRAY_SIZE > 0) {
+ newCapacity = hugeCapacity(minCapacity);
+ }
+ // minCapacity is usually close to size, so this is a win:
+ elementData = Arrays.copyOf(elementData, newCapacity);
+ }
+
+ private static int hugeCapacity(int minCapacity) {
+
+ if (minCapacity < 0) {
+ throw new OutOfMemoryError();
+ }
+ return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
+ }
+
+ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
+
+ // Write out element count, and any hidden stuff
+ int expectedModCount = modCount;
+ s.defaultWriteObject();
+
+ // Write out size as capacity for behavioural compatibility with clone()
+ s.writeInt(size);
+
+ // Write out all elements in the proper order.
+ for (int i = 0; i < size; i++) {
+ s.writeObject(elementData[i]);
+ }
+
+ if (modCount != expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
+
+ elementData = new Object[10];
+ hashTable = new Entry[8];
+ mask = 7;
+ size = 0;
+
+ // Read in size, and any hidden stuff
+ s.defaultReadObject();
+
+ // Read in capacity
+ int size = s.readInt();
+
+ if (size > 0) {
+ // be like clone(), allocate array based upon size not capacity
+ ensureCapacityInternal(size);
+
+ // Read in all elements in the proper order.
+ for (int i = 0; i < size; i++) {
+ add((E) s.readObject());
+ }
+ }
+ }
+
+ // }
+
+ E index(int index) {
+
+ return (E) elementData[index];
+ }
+
+ protected Entry seek(Object obj, int hash) {
+
+ for (Entry entry = hashTable[hash & mask]; entry != null; entry = entry.nextInBucket) {
+ if (hash == entry.hash && Objects.equal(obj, entry.key)) {
+ return entry;
+ }
+ }
+
+ return null;
+ }
+
+ protected void insert(Entry entry) {
+
+ int bucket = entry.hash & mask;
+ entry.nextInBucket = hashTable[bucket];
+ hashTable[bucket] = entry;
+ }
+
+ protected void delete(Entry entry) {
+
+ l: synchronized (hashTable) {
+ int bucket = entry.hash & mask;
+ Entry prev = null, cur = hashTable[bucket];
+ if (cur == entry) {
+ hashTable[bucket] = cur.nextInBucket;
+ break l;
+ }
+ for (; true; cur = cur.nextInBucket) {
+ if (cur == entry) {
+ prev.nextInBucket = entry.nextInBucket;
+ break l;
+ }
+ prev = cur;
+ }
+ }
+ }
+
+ protected void rehashIfNecessary() {
+
+ Entry[] old = hashTable, newTable;
+ if (size > old.length * 2 && old.length < Ints.MAX_POWER_OF_TWO) {
+ synchronized (hashTable) {
+ int newTableSize = old.length * 2, newMask = newTableSize - 1;
+ newTable = new Entry[newTableSize];
+
+ for (int bucket = old.length; bucket-- > 0;) {
+ Entry entry = old[bucket];
+ while (entry != null) {
+ Entry nextEntry = entry.nextInBucket;
+ int keyBucket = entry.hash & newMask;
+ entry.nextInBucket = newTable[keyBucket];
+ newTable[keyBucket] = entry;
+ entry = nextEntry;
+ }
+ }
+ hashTable = newTable;
+ mask = newMask;
+ }
+ }
+ }
+
+ @Override
+ public ArrayHashList clone() {
+
+ return new ArrayHashList(this);
+ }
+
+ @Override
+ public List subList(int fromIndex, int toIndex) {
+
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Iterator iterator() {
+
+ return new Itr();
+ }
+
+ @Override
+ public ListIterator listIterator() {
+
+ return listIterator(0);
+ }
+
+ @Override
+ public ListIterator listIterator(int index) {
+
+ return new ListItr(index);
+ }
+
+ protected boolean isElementIndex(int index) {
+
+ return index >= 0 && index < size;
+ }
+
+ protected boolean isPositionIndex(int index) {
+
+ return index >= 0 && index <= size;
+ }
+
+ protected String outOfBoundsMsg(int index) {
+
+ return "Index: " + index + ", Size: " + size;
+ }
+
+ protected void checkElementIndex(int index) {
+
+ if (!isElementIndex(index)) {
+ throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
+ }
+ }
+
+ protected void checkPositionIndex(int index) {
+
+ if (!isPositionIndex(index)) {
+ throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
+ }
+ }
+
+ private class Itr implements Iterator {
+
+ int cursor; // index of next element to return
+ int lastRet = -1; // index of last element returned; -1 if no such
+ int expectedModCount = modCount;
+
+ @Override
+ public boolean hasNext() {
+
+ return cursor != size;
+ }
+
+ @Override
+ public E next() {
+
+ checkForComodification();
+ int i = cursor;
+ if (i >= size) {
+ throw new NoSuchElementException();
+ }
+ Object[] elementData = ArrayHashList.this.elementData;
+ if (i >= elementData.length) {
+ throw new ConcurrentModificationException();
+ }
+ cursor = i + 1;
+ return (E) elementData[lastRet = i];
+ }
+
+ @Override
+ public void remove() {
+
+ if (lastRet < 0) {
+ throw new IllegalStateException();
+ }
+ checkForComodification();
+
+ try {
+ ArrayHashList.this.remove(lastRet);
+ cursor = lastRet;
+ lastRet = -1;
+ expectedModCount = modCount;
+ } catch (IndexOutOfBoundsException ex) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ final void checkForComodification() {
+
+ if (modCount != expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+
+ private class ListItr extends Itr implements ListIterator {
+
+ ListItr(int index) {
+
+ super();
+ cursor = index;
+ }
+
+ @Override
+ public boolean hasPrevious() {
+
+ return cursor != 0;
+ }
+
+ @Override
+ public int nextIndex() {
+
+ return cursor;
+ }
+
+ @Override
+ public int previousIndex() {
+
+ return cursor - 1;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public E previous() {
+
+ checkForComodification();
+ int i = cursor - 1;
+ if (i < 0) {
+ throw new NoSuchElementException();
+ }
+ Object[] elementData = ArrayHashList.this.elementData;
+ if (i >= elementData.length) {
+ throw new ConcurrentModificationException();
+ }
+ cursor = i;
+ return (E) elementData[lastRet = i];
+ }
+
+ @Override
+ public void set(E e) {
+
+ if (lastRet < 0) {
+ throw new IllegalStateException();
+ }
+ checkForComodification();
+
+ try {
+ ArrayHashList.this.set(lastRet, e);
+ } catch (IndexOutOfBoundsException ex) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ @Override
+ public void add(E e) {
+
+ checkForComodification();
+
+ try {
+ int i = cursor;
+ ArrayHashList.this.add(i, e);
+ cursor = i + 1;
+ lastRet = -1;
+ expectedModCount = modCount;
+ } catch (IndexOutOfBoundsException ex) {
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/BlockWrapper.java b/src/main/java/cofh/lib/util/BlockWrapper.java
new file mode 100644
index 00000000..ffb66291
--- /dev/null
+++ b/src/main/java/cofh/lib/util/BlockWrapper.java
@@ -0,0 +1,81 @@
+package cofh.lib.util;
+
+import net.minecraft.block.Block;
+
+/**
+ * Wrapper for a Block/Metadata combination post 1.7. Quick and dirty, allows for Integer-based Hashes without collisions.
+ *
+ * @author King Lemming
+ *
+ */
+public final class BlockWrapper {
+
+ public Block block;
+ public int metadata;
+
+ public BlockWrapper(Block block, int metadata) {
+
+ this.block = block;
+ this.metadata = metadata;
+ }
+
+ public BlockWrapper set(Block block, int metadata) {
+
+ if (block != null) {
+ this.block = block;
+ this.metadata = metadata;
+ } else {
+ this.block = null;
+ this.metadata = 0;
+ }
+ return this;
+ }
+
+ public boolean isEqual(BlockWrapper other) {
+
+ if (other == null) {
+ return false;
+ }
+ if (metadata == other.metadata) {
+ if (block == other.block) {
+ return true;
+ }
+ if (block != null && other.block != null) {
+ return block.delegate.get() == other.block.delegate.get();
+ }
+ }
+ return false;
+ }
+
+ final int getId() {
+
+ return Block.getIdFromBlock(block);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+
+ if (!(o instanceof BlockWrapper)) {
+ return false;
+ }
+ return isEqual((BlockWrapper) o);
+ }
+
+ @Override
+ public int hashCode() {
+
+ return metadata | getId() << 16;
+ }
+
+ @Override
+ public String toString() {
+
+ StringBuilder b = new StringBuilder(getClass().getName());
+ b.append('@').append(System.identityHashCode(this)).append('{');
+ b.append("m:").append(metadata).append(", i:").append(block == null ? null : block.getClass().getName());
+ b.append('@').append(System.identityHashCode(block)).append(", v:");
+ b.append(getId()).append('}');
+ return b.toString();
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/CharacterSingleton.java b/src/main/java/cofh/lib/util/CharacterSingleton.java
new file mode 100644
index 00000000..19bf9d63
--- /dev/null
+++ b/src/main/java/cofh/lib/util/CharacterSingleton.java
@@ -0,0 +1,28 @@
+package cofh.lib.util;
+
+public class CharacterSingleton implements CharSequence {
+
+ public char character;
+
+ @Override
+ public int length() {
+
+ return 1;
+ }
+
+ @Override
+ public char charAt(int index) {
+
+ return character;
+ }
+
+ @Override
+ public CharSequence subSequence(int start, int end) {
+
+ if (start == end) {
+ return "";
+ }
+ return this;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/ComparableItem.java b/src/main/java/cofh/lib/util/ComparableItem.java
new file mode 100644
index 00000000..cdc04de6
--- /dev/null
+++ b/src/main/java/cofh/lib/util/ComparableItem.java
@@ -0,0 +1,120 @@
+package cofh.lib.util;
+
+import cofh.lib.util.helpers.ItemHelper;
+
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+
+import java.util.Objects;
+
+/**
+ * Wrapper for an Item/Metadata combination post 1.7. Quick and dirty, allows for Integer-based Hashes without collisions.
+ *
+ * @author King Lemming
+ *
+ */
+public class ComparableItem {
+
+ public Item item;
+ public int metadata;
+
+ public static ComparableItem fromItemStack(ItemStack stack) {
+
+ return new ComparableItem(stack);
+ }
+
+ protected ComparableItem() {
+
+ item = null;
+ metadata = 0;
+ }
+
+ public ComparableItem(Item item, int metadata) {
+
+ this.item = item;
+ this.metadata = metadata;
+ }
+
+ public ComparableItem(ItemStack stack) {
+
+ if (stack != null) {
+ this.item = stack.getItem();
+ this.metadata = ItemHelper.getItemDamage(stack);
+ } else {
+ this.item = null;
+ this.metadata = 0;
+ }
+ }
+
+ public ComparableItem(ComparableItem stack) {
+
+ this.item = stack.item;
+ this.metadata = stack.metadata;
+ }
+
+ public ComparableItem set(ItemStack stack) {
+
+ if (stack != null) {
+ this.item = stack.getItem();
+ this.metadata = ItemHelper.getItemDamage(stack);
+ } else {
+ this.item = null;
+ this.metadata = 0;
+ }
+ return this;
+ }
+
+ // '0' is null. '-1' is an unmapped item (missing in this World)
+ protected final int getId() {
+
+ return Item.getIdFromItem(item);
+ }
+
+ public boolean isEqual(ComparableItem other) {
+
+ if (other == null) {
+ return false;
+ }
+ if (metadata == other.metadata) {
+ if (item == other.item) {
+ return true;
+ }
+ if (item != null && other.item != null) {
+ return item.delegate.get() == other.item.delegate.get();
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public ComparableItem clone() {
+
+ return new ComparableItem(this);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+
+ if (!(o instanceof ComparableItem)) {
+ return false;
+ }
+ return isEqual((ComparableItem) o);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(item == null ? null : item.delegate.get(), metadata);
+ }
+
+ @Override
+ public String toString() {
+
+ StringBuilder b = new StringBuilder(getClass().getName());
+ b.append('@').append(System.identityHashCode(this)).append('{');
+ b.append("m:").append(metadata).append(", i:").append(item == null ? null : item.getClass().getName());
+ b.append('@').append(System.identityHashCode(item)).append(", v:");
+ b.append(getId()).append('}');
+ return b.toString();
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/IdentityLinkedHashList.java b/src/main/java/cofh/lib/util/IdentityLinkedHashList.java
new file mode 100644
index 00000000..1c06a29d
--- /dev/null
+++ b/src/main/java/cofh/lib/util/IdentityLinkedHashList.java
@@ -0,0 +1,48 @@
+package cofh.lib.util;
+
+import java.util.Collection;
+
+@SuppressWarnings("unchecked")
+public class IdentityLinkedHashList extends LinkedHashList {
+
+ private static final long serialVersionUID = 4893829808146776641L;
+
+ public IdentityLinkedHashList() {
+
+ super();
+ }
+
+ public IdentityLinkedHashList(int size) {
+
+ super(size);
+ }
+
+ public IdentityLinkedHashList(Collection col) {
+
+ super(col);
+ }
+
+ @Override
+ protected int hash(Object o) {
+
+ return System.identityHashCode(o);
+ }
+
+ @Override
+ protected Entry seek(Object obj, int hash) {
+
+ for (Entry entry = hashTable[hash & mask]; entry != null; entry = entry.nextInBucket) {
+ if (obj == entry.key) {
+ return entry;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public IdentityLinkedHashList clone() {
+
+ return new IdentityLinkedHashList(this);
+ }
+}
diff --git a/src/main/java/cofh/lib/util/ItemWrapper.java b/src/main/java/cofh/lib/util/ItemWrapper.java
new file mode 100644
index 00000000..8d234ace
--- /dev/null
+++ b/src/main/java/cofh/lib/util/ItemWrapper.java
@@ -0,0 +1,49 @@
+package cofh.lib.util;
+
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+
+/**
+ * Wrapper for an Item/Metadata combination post 1.7. Quick and dirty, allows for Integer-based Hashes without collisions.
+ *
+ * @author King Lemming
+ *
+ */
+public final class ItemWrapper extends ComparableItem {
+
+ public static ItemWrapper fromItemStack(ItemStack stack) {
+
+ return new ItemWrapper(stack);
+ }
+
+ public ItemWrapper(Item item, int metadata) {
+
+ super(item, metadata);
+ }
+
+ public ItemWrapper(ItemStack stack) {
+
+ super(stack);
+ }
+
+ public ItemWrapper(ItemWrapper stack) {
+
+ super(stack);
+ }
+
+ @Override
+ public ItemWrapper clone() {
+
+ return new ItemWrapper(this);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+
+ if (!(o instanceof ItemWrapper)) {
+ return false;
+ }
+ return isEqual((ItemWrapper) o);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/LinkedHashList.java b/src/main/java/cofh/lib/util/LinkedHashList.java
new file mode 100644
index 00000000..51d2f924
--- /dev/null
+++ b/src/main/java/cofh/lib/util/LinkedHashList.java
@@ -0,0 +1,648 @@
+package cofh.lib.util;
+
+import com.google.common.base.Objects;
+import com.google.common.primitives.Ints;
+
+import java.util.AbstractCollection;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+
+@SuppressWarnings("unchecked")
+public class LinkedHashList extends AbstractCollection implements List, Cloneable, java.io.Serializable {
+
+ private static final long serialVersionUID = -642033533165934945L;
+
+ protected static final class Entry {
+
+ Entry next;
+ Entry prev;
+ final Object key;
+ final int hash;
+ Entry nextInBucket;
+
+ protected Entry(Object key, int keyHash) {
+
+ this.key = key;
+ this.hash = keyHash;
+ }
+ }
+
+ protected static int roundUpToPowerOf2(int number) {
+
+ return number >= Ints.MAX_POWER_OF_TWO ? Ints.MAX_POWER_OF_TWO : (number > 2) ? Integer.highestOneBit((number - 1) << 1) : 2;
+ }
+
+ protected transient Entry head;
+ protected transient Entry tail;
+ protected transient int size;
+ protected transient int mask;
+ protected transient Entry[] hashTable;
+ protected transient int modCount;
+
+ public LinkedHashList() {
+
+ hashTable = new Entry[8];
+ mask = 7;
+ }
+
+ public LinkedHashList(int size) {
+
+ size = roundUpToPowerOf2(size);
+ hashTable = new Entry[size];
+ mask = size - 1;
+ }
+
+ public LinkedHashList(Collection col) {
+
+ int size = roundUpToPowerOf2(col.size());
+ hashTable = new Entry[size];
+ mask = size - 1;
+ addAll(col);
+ }
+
+ protected int hash(Object n) {
+
+ int h = n == null ? 0 : n.hashCode();
+ h ^= (h >>> 20) ^ (h >>> 12);
+ return h ^ (h >>> 7) ^ (h >>> 4);
+ }
+
+ @Override
+ public int size() {
+
+ return size;
+ }
+
+ protected synchronized boolean add(E obj, int hash) {
+
+ if (seek(obj, hash) != null) {
+ return false;
+ }
+
+ Entry e;
+ ++modCount;
+ insert(e = new Entry(obj, hash));
+ rehashIfNecessary();
+ e.prev = tail;
+ e.next = null;
+ if (tail != null) {
+ tail.next = e;
+ } else {
+ head = e;
+ }
+ tail = e;
+ return true;
+ }
+
+ @Override
+ public boolean add(E obj) {
+
+ int hash = hash(obj);
+ return add(obj, hash);
+ }
+
+ @Override
+ public E set(int index, E obj) {
+
+ checkElementIndex(index);
+
+ int hash = hash(obj);
+ if (seek(obj, hash) != null) {
+ // return null;
+ throw new IllegalArgumentException("Duplicate entries not allowed");
+ }
+
+ ++modCount;
+ Entry e = index(index);
+ delete(e);
+ insert(new Entry(obj, hash));
+
+ return (E) e.key;
+ }
+
+ @Override
+ public synchronized void add(int index, E obj) {
+
+ checkPositionIndex(index);
+
+ int hash = hash(obj);
+ if (seek(obj, hash) != null) {
+ throw new IllegalArgumentException("Duplicate entries not allowed");
+ }
+
+ if (index == size) {
+ add(obj, hash);
+ return;
+ }
+
+ ++modCount;
+ Entry e = index(index);
+ Entry n = new Entry(obj, hash);
+ n.next = e.next;
+ n.prev = e;
+ e.next = n;
+ if (n.next != null) {
+ n.next.prev = n;
+ }
+ insert(n);
+ rehashIfNecessary();
+ }
+
+ @Override
+ public boolean addAll(int index, Collection extends E> c) {
+
+ if (c.size() == 0) {
+ return false;
+ }
+
+ for (E e : c) {
+ add(index++, e);
+ }
+
+ return true;
+ }
+
+ @Override
+ public E get(int index) {
+
+ checkElementIndex(index);
+ return (E) index(index).key;
+ }
+
+ @Override
+ public int indexOf(Object o) {
+
+ Entry v = seek(o, hash(o));
+ if (v == null) {
+ return -1;
+ }
+ Entry n = head;
+ for (int i = 0; n != tail; ++i) {
+ if (v == n) {
+ return i;
+ }
+ n = n.next;
+ }
+ return size;
+ }
+
+ @Override
+ public int lastIndexOf(Object o) {
+
+ return indexOf(o);
+ }
+
+ public boolean push(E obj) {
+
+ int hash = hash(obj);
+ return add(obj, hash);
+ }
+
+ public E pop() {
+
+ Entry e = tail;
+ if (e != null) {
+ return unlink(e);
+ }
+ return null;
+ }
+
+ public E peek() {
+
+ return tail != null ? (E) tail.key : null;
+ }
+
+ public E poke() {
+
+ return head != null ? (E) head.key : null;
+ }
+
+ public boolean unshift(E obj) {
+
+ return linkBefore(obj, head);
+ }
+
+ public E shift() {
+
+ Entry e = head;
+ if (e != null) {
+ return unlink(e);
+ }
+ return null;
+ }
+
+ @Override
+ public boolean contains(Object obj) {
+
+ return seek(obj, hash(obj)) != null;
+ }
+
+ @Override
+ public boolean remove(Object obj) {
+
+ Entry e = seek(obj, hash(obj));
+ if (e == null) {
+ return false;
+ }
+
+ unlink(e);
+ return true;
+ }
+
+ @Override
+ public E remove(int index) {
+
+ checkElementIndex(index);
+
+ Entry oldValue = index(index);
+
+ return unlink(oldValue);
+ }
+
+ protected Entry index(int index) {
+
+ Entry x;
+ if (index < (size >> 1)) {
+ x = head;
+ for (int i = index; i-- > 0;) {
+ x = x.next;
+ }
+ } else {
+ x = tail;
+ for (int i = size - 1; i-- > index;) {
+ x = x.prev;
+ }
+ }
+ return x;
+ }
+
+ protected Entry seek(Object obj, int hash) {
+
+ for (Entry entry = hashTable[hash & mask]; entry != null; entry = entry.nextInBucket) {
+ if (hash == entry.hash && Objects.equal(obj, entry.key)) {
+ return entry;
+ }
+ }
+
+ return null;
+ }
+
+ protected void insert(Entry entry) {
+
+ synchronized (hashTable) {
+ int bucket = entry.hash & mask;
+ entry.nextInBucket = hashTable[bucket];
+ hashTable[bucket] = entry;
+ }
+ ++size;
+ }
+
+ protected synchronized boolean linkBefore(E obj, Entry succ) {
+
+ int hash = hash(obj);
+ if (seek(obj, hash) != null) {
+ return false;
+ }
+
+ final Entry pred = succ.prev;
+ final Entry newNode = new Entry(obj, hash);
+ modCount++;
+ insert(newNode);
+ rehashIfNecessary();
+ newNode.next = succ;
+ newNode.prev = pred;
+ succ.prev = newNode;
+ if (pred == null) {
+ head = newNode;
+ } else {
+ pred.next = newNode;
+ }
+ return true;
+ }
+
+ protected void delete(Entry entry) {
+
+ l: synchronized (hashTable) {
+ int bucket = entry.hash & mask;
+ Entry prev = null, cur = hashTable[bucket];
+ if (cur == entry) {
+ hashTable[bucket] = cur.nextInBucket;
+ break l;
+ }
+ for (; true; cur = cur.nextInBucket) {
+ if (cur == entry) {
+ prev.nextInBucket = entry.nextInBucket;
+ break l;
+ }
+ prev = cur;
+ }
+ }
+ --size;
+ }
+
+ protected synchronized E unlink(Entry x) {
+
+ modCount++;
+ final E element = (E) x.key;
+ final Entry next = x.next;
+ final Entry prev = x.prev;
+
+ if (prev == null) {
+ head = next;
+ } else {
+ prev.next = next;
+ x.prev = null;
+ }
+
+ if (next == null) {
+ tail = prev;
+ } else {
+ next.prev = prev;
+ x.next = null;
+ }
+
+ delete(x);
+ return element;
+ }
+
+ protected void rehashIfNecessary() {
+
+ Entry[] old = hashTable, newTable;
+ if (size > old.length * 2 && old.length < Ints.MAX_POWER_OF_TWO) {
+ synchronized (hashTable) {
+ int newTableSize = old.length * 2, newMask = newTableSize - 1;
+ newTable = new Entry[newTableSize];
+
+ for (int bucket = old.length; bucket-- > 0;) {
+ Entry entry = old[bucket];
+ while (entry != null) {
+ Entry nextEntry = entry.nextInBucket;
+ int keyBucket = entry.hash & newMask;
+ entry.nextInBucket = newTable[keyBucket];
+ newTable[keyBucket] = entry;
+ entry = nextEntry;
+ }
+ }
+ hashTable = newTable;
+ mask = newMask;
+ }
+ }
+ }
+
+ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
+
+ // Write out element count, and any hidden stuff
+ int expectedModCount = modCount;
+ s.defaultWriteObject();
+
+ // Write out size as capacity for behavioural compatibility with clone()
+ s.writeInt(size);
+
+ // Write out all elements in the proper order.
+ Entry n = head;
+ for (int i = 0; i < size; i++) {
+ s.writeObject(n.key);
+ n = n.next;
+ }
+
+ if (modCount != expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException {
+
+ head = tail = null;
+ hashTable = new Entry[8];
+ mask = 7;
+ size = 0;
+
+ // Read in size, and any hidden stuff
+ s.defaultReadObject();
+
+ // Read in capacity
+ int size = s.readInt();
+
+ if (size > 0) {
+
+ // Read in all elements in the proper order.
+ for (int i = 0; i < size; i++) {
+ add((E) s.readObject());
+ }
+ }
+ }
+
+ @Override
+ public LinkedHashList clone() {
+
+ return new LinkedHashList(this);
+ }
+
+ @Override
+ public List subList(int fromIndex, int toIndex) {
+
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Iterator iterator() {
+
+ return listIterator();
+ }
+
+ @Override
+ public ListIterator listIterator() {
+
+ return listIterator(0);
+ }
+
+ @Override
+ public ListIterator listIterator(int index) {
+
+ checkPositionIndex(index);
+ return new ListItr(index);
+ }
+
+ public Iterator descendingIterator() {
+
+ return new DescendingIterator();
+ }
+
+ protected boolean isElementIndex(int index) {
+
+ return index >= 0 && index < size;
+ }
+
+ protected boolean isPositionIndex(int index) {
+
+ return index >= 0 && index <= size;
+ }
+
+ protected String outOfBoundsMsg(int index) {
+
+ return "Index: " + index + ", Size: " + size;
+ }
+
+ protected void checkElementIndex(int index) {
+
+ if (!isElementIndex(index)) {
+ throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
+ }
+ }
+
+ protected void checkPositionIndex(int index) {
+
+ if (!isPositionIndex(index)) {
+ throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
+ }
+ }
+
+ protected class ListItr implements ListIterator {
+
+ protected Entry lastReturned = null;
+ protected Entry next;
+ protected int nextIndex;
+ protected int expectedModCount = modCount;
+
+ protected ListItr(int index) {
+
+ next = (index == size) ? null : index(index);
+ nextIndex = index;
+ }
+
+ @Override
+ public boolean hasNext() {
+
+ return nextIndex < size;
+ }
+
+ @Override
+ public E next() {
+
+ checkForComodification();
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ lastReturned = next;
+ next = next.next;
+ nextIndex++;
+ return (E) lastReturned.key;
+ }
+
+ @Override
+ public boolean hasPrevious() {
+
+ return nextIndex > 0;
+ }
+
+ @Override
+ public E previous() {
+
+ checkForComodification();
+ if (!hasPrevious()) {
+ throw new NoSuchElementException();
+ }
+
+ lastReturned = next = (next == null) ? tail : next.prev;
+ nextIndex--;
+ return (E) lastReturned.key;
+ }
+
+ @Override
+ public int nextIndex() {
+
+ return nextIndex;
+ }
+
+ @Override
+ public int previousIndex() {
+
+ return nextIndex - 1;
+ }
+
+ @Override
+ public void remove() {
+
+ checkForComodification();
+ if (lastReturned == null) {
+ throw new IllegalStateException();
+ }
+
+ Entry lastNext = lastReturned.next;
+ unlink(lastReturned);
+ if (next == lastReturned) {
+ next = lastNext;
+ } else {
+ nextIndex--;
+ }
+ lastReturned = null;
+ expectedModCount++;
+ }
+
+ @Override
+ public void set(E e) {
+
+ checkForComodification();
+ if (lastReturned == null) {
+ throw new IllegalStateException();
+ }
+
+ linkBefore(e, lastReturned);
+ unlink(lastReturned);
+ lastReturned = (next == null) ? tail : next.prev;
+ expectedModCount += 2;
+ }
+
+ @Override
+ public void add(E e) {
+
+ checkForComodification();
+ lastReturned = null;
+ if (next == null) {
+ push(e);
+ } else {
+ linkBefore(e, next);
+ }
+ nextIndex++;
+ expectedModCount++;
+ }
+
+ protected final void checkForComodification() {
+
+ if (modCount != expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
+
+ }
+
+ protected class DescendingIterator implements Iterator {
+
+ protected final ListItr itr = new ListItr(size());
+
+ @Override
+ public boolean hasNext() {
+
+ return itr.hasPrevious();
+ }
+
+ @Override
+ public E next() {
+
+ return itr.previous();
+ }
+
+ @Override
+ public void remove() {
+
+ itr.remove();
+ }
+
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/OreDictionaryProxy.java b/src/main/java/cofh/lib/util/OreDictionaryProxy.java
new file mode 100644
index 00000000..497fbaca
--- /dev/null
+++ b/src/main/java/cofh/lib/util/OreDictionaryProxy.java
@@ -0,0 +1,60 @@
+package cofh.lib.util;
+
+import cofh.lib.util.helpers.ItemHelper;
+
+import net.minecraft.item.ItemStack;
+import net.minecraftforge.oredict.OreDictionary;
+
+/**
+ * Don't instantiate this or call these methods in any way. Use the methods in {@link ItemHelper}.
+ *
+ * @author King Lemming
+ *
+ */
+@SuppressWarnings("deprecation")
+public class OreDictionaryProxy {
+
+ public ItemStack getOre(String oreName) {
+
+ if (!oreNameExists(oreName)) {
+ return null;
+ }
+ return ItemHelper.cloneStack(OreDictionary.getOres(oreName).get(0), 1);
+ }
+
+ public int getOreID(ItemStack stack) {
+
+ return OreDictionary.getOreID(stack);
+ }
+
+ public int getOreID(String oreName) {
+
+ return OreDictionary.getOreID(oreName);
+ }
+
+ public String getOreName(ItemStack stack) {
+
+ return OreDictionary.getOreName(OreDictionary.getOreID(stack));
+ }
+
+ public String getOreName(int oreID) {
+
+ return OreDictionary.getOreName(oreID);
+ }
+
+ public boolean isOreIDEqual(ItemStack stack, int oreID) {
+
+ return OreDictionary.getOreID(stack) == oreID;
+ }
+
+ public boolean isOreNameEqual(ItemStack stack, String oreName) {
+
+ return OreDictionary.getOreName(OreDictionary.getOreID(stack)).equals(oreName);
+ }
+
+ public boolean oreNameExists(String oreName) {
+
+ return OreDictionary.doesOreNameExist(oreName);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/Rectangle4i.java b/src/main/java/cofh/lib/util/Rectangle4i.java
new file mode 100644
index 00000000..4a039479
--- /dev/null
+++ b/src/main/java/cofh/lib/util/Rectangle4i.java
@@ -0,0 +1,118 @@
+package cofh.lib.util;
+
+/**
+ * Generic rectangle class.
+ *
+ * @author Chicken Bones
+ *
+ */
+public class Rectangle4i {
+
+ public int x;
+ public int y;
+ public int w;
+ public int h;
+
+ public Rectangle4i() {
+
+ }
+
+ public Rectangle4i(int x, int y, int w, int h) {
+
+ this.x = x;
+ this.y = y;
+ this.w = w;
+ this.h = h;
+ }
+
+ public int x1() {
+
+ return x;
+ }
+
+ public int y1() {
+
+ return y;
+ }
+
+ public int x2() {
+
+ return x + w - 1;
+ }
+
+ public int y2() {
+
+ return y + h - 1;
+ }
+
+ public void set(int x, int y, int w, int h) {
+
+ this.x = x;
+ this.y = y;
+ this.w = w;
+ this.h = h;
+ }
+
+ public Rectangle4i offset(int dx, int dy) {
+
+ x += dx;
+ y += dy;
+ return this;
+ }
+
+ public Rectangle4i include(int px, int py) {
+
+ if (px < x) {
+ expand(px - x, 0);
+ }
+ if (px >= x + w) {
+ expand(px - x - w + 1, 0);
+ }
+ if (py < y) {
+ expand(0, py - y);
+ }
+ if (py >= y + h) {
+ expand(0, py - y - h + 1);
+ }
+ return this;
+ }
+
+ public Rectangle4i include(Rectangle4i r) {
+
+ include(r.x, r.y);
+ return include(r.x2(), r.y2());
+ }
+
+ public Rectangle4i expand(int px, int py) {
+
+ if (px > 0) {
+ w += px;
+ } else {
+ x += px;
+ w -= px;
+ }
+ if (py > 0) {
+ h += py;
+ } else {
+ y += py;
+ h -= py;
+ }
+ return this;
+ }
+
+ public boolean contains(int px, int py) {
+
+ return x <= px && px < x + w && y <= py && py < y + h;
+ }
+
+ public boolean intersects(Rectangle4i r) {
+
+ return r.x + r.w > x && r.x < x + w && r.y + r.h > y && r.y < y + h;
+ }
+
+ public int area() {
+
+ return w * h;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/RegistryUtils.java b/src/main/java/cofh/lib/util/RegistryUtils.java
new file mode 100644
index 00000000..8d819a1f
--- /dev/null
+++ b/src/main/java/cofh/lib/util/RegistryUtils.java
@@ -0,0 +1,231 @@
+package cofh.lib.util;
+
+import com.google.common.base.Throwables;
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.Multimap;
+import cpw.mods.fml.common.eventhandler.EventPriority;
+import cpw.mods.fml.common.eventhandler.SubscribeEvent;
+import cpw.mods.fml.common.registry.RegistryDelegate;
+import cpw.mods.fml.relauncher.ReflectionHelper;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+
+import java.awt.image.BufferedImage;
+import java.util.IdentityHashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import javax.imageio.ImageIO;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.item.Item;
+import net.minecraft.util.RegistryNamespaced;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.common.MinecraftForge;
+import net.minecraftforge.event.world.WorldEvent;
+
+public class RegistryUtils {
+
+ private RegistryUtils() {
+
+ }
+
+ private static class Repl {
+
+ private static IdentityHashMap> replacements;
+ private static Class> DelegateClass;
+
+ @SuppressWarnings({ "rawtypes", "unchecked" })
+ private static void overwrite_do(RegistryNamespaced registry, String name, Object object, Object oldThing) {
+
+ int id = registry.getIDForObject(oldThing);
+ BiMap map = ((BiMap) registry.registryObjects);
+ registry.underlyingIntegerMap.func_148746_a(object, id);
+ map.remove(name);
+ map.forcePut(name, object);
+ }
+
+ private static void alterDelegateChain(RegistryNamespaced registry, String id, Object object) {
+
+ Multimap map = replacements.get(registry);
+ List c = (List) map.get(id);
+ int i = 0, e = c.size() - 1;
+ Object end = c.get(e);
+ for (; i <= e; ++i) {
+ Object t = c.get(i);
+ Repl.alterDelegate(t, end);
+ }
+ }
+
+ private static void alterDelegate(Object obj, Object repl) {
+
+ if (obj instanceof Item) {
+ RegistryDelegate- delegate = ((Item) obj).delegate;
+ ReflectionHelper.setPrivateValue(DelegateClass, delegate, repl, "referant");
+ ReflectionHelper.setPrivateValue(DelegateClass, ((Item) repl).delegate, delegate.name(), "name");
+ }
+ }
+
+ static {
+
+ replacements = new IdentityHashMap
>(2);
+ MinecraftForge.EVENT_BUS.register(new RegistryUtils());
+ try {
+ DelegateClass = (Class>) Class.forName("cpw.mods.fml.common.registry.RegistryDelegate$Delegate");
+ } catch (Throwable e) {
+ Throwables.propagate(e);
+ }
+ }
+ }
+
+ @SubscribeEvent(priority = EventPriority.HIGHEST)
+ public void _(WorldEvent.Load event) {
+
+ if (Repl.replacements.size() < 1) {
+ return;
+ }
+ for (Map.Entry> entry : Repl.replacements.entrySet()) {
+ RegistryNamespaced reg = entry.getKey();
+ Multimap map = entry.getValue();
+ Iterator v = map.keySet().iterator();
+ while (v.hasNext()) {
+ String id = v.next();
+ List c = (List) map.get(id);
+ int i = 0, e = c.size() - 1;
+ Object end = c.get(e);
+ if (reg.getIDForObject(c.get(0)) != reg.getIDForObject(end)) {
+ for (; i <= e; ++i) {
+ Object t = c.get(i);
+ Object oldThing = reg.getObject(id);
+ Repl.overwrite_do(reg, id, t, oldThing);
+ Repl.alterDelegate(oldThing, end);
+ }
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ public static void overwriteEntry(RegistryNamespaced registry, String name, Object object) {
+
+ Object oldThing = registry.getObject(name);
+ Repl.overwrite_do(registry, name, object, oldThing);
+ Multimap reg = Repl.replacements.get(registry);
+ if (reg == null) {
+ Repl.replacements.put(registry, reg = ArrayListMultimap.create());
+ }
+ if (!reg.containsKey(name)) {
+ reg.put(name, oldThing);
+ }
+ reg.put(name, object);
+ Repl.alterDelegateChain(registry, name, object);
+ }
+
+ @SideOnly(Side.CLIENT)
+ public static boolean textureExists(ResourceLocation texture) {
+
+ try {
+ Minecraft.getMinecraft().getResourceManager().getAllResources(texture);
+ return true;
+ } catch (Throwable t) { // pokemon!
+ return false;
+ }
+ }
+
+ @SideOnly(Side.CLIENT)
+ public static boolean textureExists(String texture) {
+
+ return textureExists(new ResourceLocation(texture));
+ }
+
+ @SideOnly(Side.CLIENT)
+ public static boolean blockTextureExists(String texture) {
+
+ int i = texture.indexOf(':');
+
+ if (i > 0) {
+ texture = texture.substring(0, i) + ":textures/blocks/" + texture.substring(i + 1, texture.length());
+ } else {
+ texture = "textures/blocks/" + texture;
+ }
+ return textureExists(texture + ".png");
+ }
+
+ @SideOnly(Side.CLIENT)
+ public static boolean itemTextureExists(String texture) {
+
+ int i = texture.indexOf(':');
+
+ if (i > 0) {
+ texture = texture.substring(0, i) + ":textures/items/" + texture.substring(i + 1, texture.length());
+ } else {
+ texture = "textures/items/" + texture;
+ }
+ return textureExists(texture + ".png");
+ }
+
+ @SideOnly(Side.CLIENT)
+ public static int getTextureColor(ResourceLocation texture) {
+
+ try {
+ BufferedImage image = ImageIO.read(Minecraft.getMinecraft().getResourceManager().getResource(texture).getInputStream());
+
+ int[] a = new int[image.getWidth() * image.getHeight()];
+ image.getRGB(0, 0, image.getWidth(), image.getHeight(), a, 0, image.getWidth());
+
+ int r = a[0];
+ for (int i = a.length; i-- > 1;) {
+ int t = a[i], v;
+ v = (((r >> 24) & 255) + ((t >> 24) & 255)) / 2;
+ r &= 0x00FFFFFF;
+ r |= v << 24;
+ v = (((r >> 16) & 255) + ((t >> 16) & 255)) / 2;
+ r &= 0xFF00FFFF;
+ r |= v << 16;
+ v = (((r >> 8) & 255) + ((t >> 8) & 255)) / 2;
+ r &= 0xFFFF00FF;
+ r |= v << 8;
+ v = (((r >> 0) & 255) + ((t >> 0) & 255)) / 2;
+ r &= 0xFFFFFF00;
+ r |= v << 0;
+ }
+ return r;
+ } catch (Throwable t) { // pokemon!
+ return 0xFFFFFF;
+ }
+ }
+
+ @SideOnly(Side.CLIENT)
+ public static int getTextureColor(String texture) {
+
+ return getTextureColor(new ResourceLocation(texture));
+ }
+
+ @SideOnly(Side.CLIENT)
+ public static int getBlockTextureColor(String texture) {
+
+ int i = texture.indexOf(':');
+
+ if (i > 0) {
+ texture = texture.substring(0, i) + ":textures/blocks/" + texture.substring(i + 1, texture.length());
+ } else {
+ texture = "textures/blocks/" + texture;
+ }
+ return getTextureColor(texture + ".png");
+ }
+
+ @SideOnly(Side.CLIENT)
+ public static int getItemTextureColor(String texture) {
+
+ int i = texture.indexOf(':');
+
+ if (i > 0) {
+ texture = texture.substring(0, i) + ":textures/items/" + texture.substring(i + 1, texture.length());
+ } else {
+ texture = "textures/items/" + texture;
+ }
+ return getTextureColor(texture + ".png");
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/TimeTracker.java b/src/main/java/cofh/lib/util/TimeTracker.java
new file mode 100644
index 00000000..2fd03fae
--- /dev/null
+++ b/src/main/java/cofh/lib/util/TimeTracker.java
@@ -0,0 +1,34 @@
+package cofh.lib.util;
+
+import net.minecraft.world.World;
+
+/**
+ * A basic time tracker class. Nothing surprising here.
+ *
+ * @author King Lemming
+ *
+ */
+public class TimeTracker {
+
+ private long lastMark = Long.MIN_VALUE;
+
+ public boolean hasDelayPassed(World world, int delay) {
+
+ long currentTime = world.getTotalWorldTime();
+
+ if (currentTime < lastMark) {
+ lastMark = currentTime;
+ return false;
+ } else if (lastMark + delay <= currentTime) {
+ lastMark = currentTime;
+ return true;
+ }
+ return false;
+ }
+
+ public void markTime(World world) {
+
+ lastMark = world.getTotalWorldTime();
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/UtilLiquidMover.java b/src/main/java/cofh/lib/util/UtilLiquidMover.java
new file mode 100644
index 00000000..218bbc17
--- /dev/null
+++ b/src/main/java/cofh/lib/util/UtilLiquidMover.java
@@ -0,0 +1,180 @@
+package cofh.lib.util;
+
+import cofh.api.fluid.ITankContainerBucketable;
+import cofh.lib.util.helpers.ItemHelper;
+import cofh.lib.util.position.BlockPosition;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.FluidContainerRegistry;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTankInfo;
+import net.minecraftforge.fluids.IFluidContainerItem;
+import net.minecraftforge.fluids.IFluidHandler;
+import net.minecraftforge.fluids.IFluidTank;
+
+/**
+ *
+ * @deprecated Use {@link cofh.lib.util.helpers.FluidHelper}
+ */
+@Deprecated
+public class UtilLiquidMover {
+
+ /**
+ * Attempts to fill tank with the player's current item.
+ *
+ * @param itcb
+ * the tank the liquid is going into
+ * @param player
+ * the player trying to fill the tank
+ * @return True if liquid was transferred to the tank.
+ */
+ public static boolean manuallyFillTank(ITankContainerBucketable itcb, EntityPlayer player) {
+
+ ItemStack ci = player.inventory.getCurrentItem();
+ FluidStack liquid = FluidContainerRegistry.getFluidForFilledItem(ci);
+ if (liquid != null) {
+ Item item = ci.getItem();
+ if (itcb.fill(ForgeDirection.UNKNOWN, liquid, false) == liquid.amount) {
+ itcb.fill(ForgeDirection.UNKNOWN, liquid, true);
+
+ if (!player.capabilities.isCreativeMode) {
+ if (item.hasContainerItem(ci)) {
+ ItemStack drop = item.getContainerItem(ci);
+ if (drop != null && drop.isItemStackDamageable() && drop.getItemDamage() > drop.getMaxDamage()) {
+ drop = null;
+ }
+ ItemHelper.disposePlayerItem(ci, drop, player, true);
+ } else {
+ player.inventory.setInventorySlotContents(player.inventory.currentItem, ItemHelper.consumeItem(ci, player));
+ }
+
+ if (!player.worldObj.isRemote) {
+ player.openContainer.detectAndSendChanges();
+ ((EntityPlayerMP) player).sendContainerAndContentsToPlayer(player.openContainer, player.openContainer.getInventory());
+ }
+ }
+ return true;
+ }
+ } else if (ci != null && ci.getItem() instanceof IFluidContainerItem) {
+ Item item = ci.getItem();
+ IFluidContainerItem fluidContainer = (IFluidContainerItem) item;
+ liquid = fluidContainer.getFluid(ci);
+ if (itcb.fill(ForgeDirection.UNKNOWN, liquid, false) > 0) {
+ int amount = itcb.fill(ForgeDirection.UNKNOWN, liquid, true);
+ ItemStack drop = ci.splitStack(1);
+ ci.stackSize++;
+ fluidContainer.drain(drop, amount, true);
+
+ if (!player.capabilities.isCreativeMode) {
+ if (item.hasContainerItem(drop)) {
+ drop = item.getContainerItem(drop);
+ if (drop != null && drop.isItemStackDamageable() && drop.getItemDamage() > drop.getMaxDamage()) {
+ drop = null;
+ }
+ }
+ ItemHelper.disposePlayerItem(ci, drop, player, true);
+
+ if (!player.worldObj.isRemote) {
+ player.openContainer.detectAndSendChanges();
+ ((EntityPlayerMP) player).sendContainerAndContentsToPlayer(player.openContainer, player.openContainer.getInventory());
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Attempts to drain tank into the player's current item.
+ *
+ * @param itcb
+ * the tank the liquid is coming from
+ * @param player
+ * the player trying to take liquid from the tank
+ * @return True if liquid was transferred from the tank.
+ */
+ public static boolean manuallyDrainTank(ITankContainerBucketable itcb, EntityPlayer player) {
+
+ ItemStack ci = player.inventory.getCurrentItem();
+ boolean isSmartContainer = false;
+ IFluidContainerItem fluidContainer;
+ if (ci != null && (FluidContainerRegistry.isEmptyContainer(ci) || (isSmartContainer = ci.getItem() instanceof IFluidContainerItem))) {
+ for (FluidTankInfo tank : itcb.getTankInfo(ForgeDirection.UNKNOWN)) {
+ FluidStack tankLiquid = tank.fluid;
+ if (tankLiquid == null || tankLiquid.amount == 0) {
+ continue;
+ }
+ ItemStack filledBucket = null;
+ FluidStack bucketLiquid = null;
+ if (isSmartContainer) {
+ fluidContainer = (IFluidContainerItem) ci.getItem();
+ filledBucket = ci.copy();
+ filledBucket.stackSize = 1;
+ if (fluidContainer.fill(filledBucket, tankLiquid, false) > 0) {
+ int amount = fluidContainer.fill(filledBucket, tankLiquid, true);
+ bucketLiquid = new FluidStack(tankLiquid, amount);
+ FluidStack l = itcb.drain(ForgeDirection.UNKNOWN, bucketLiquid, false);
+ if (l == null || l.amount < amount) {
+ filledBucket = null;
+ }
+ } else {
+ filledBucket = null;
+ }
+ } else {
+ filledBucket = FluidContainerRegistry.fillFluidContainer(tankLiquid, ci);
+ if (FluidContainerRegistry.isFilledContainer(filledBucket)) {
+ bucketLiquid = FluidContainerRegistry.getFluidForFilledItem(filledBucket);
+ FluidStack l = itcb.drain(ForgeDirection.UNKNOWN, bucketLiquid, false);
+ if (l == null || l.amount < bucketLiquid.amount) {
+ filledBucket = null;
+ }
+ } else {
+ filledBucket = null;
+ }
+ }
+ if (filledBucket != null
+ && ItemHelper.disposePlayerItem(ci, filledBucket, player, true)) {
+ if (!player.worldObj.isRemote) {
+ player.openContainer.detectAndSendChanges();
+ ((EntityPlayerMP) player).sendContainerAndContentsToPlayer(player.openContainer, player.openContainer.getInventory());
+ }
+ itcb.drain(ForgeDirection.UNKNOWN, bucketLiquid, true);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public static void pumpLiquid(IFluidTank iFluidTank, TileEntity from) {
+
+ if (iFluidTank != null && iFluidTank.getFluidAmount() > 0) {
+ FluidStack l = iFluidTank.getFluid().copy();
+ int amount = Math.min(l.amount, FluidContainerRegistry.BUCKET_VOLUME);
+ l.amount = amount;
+ for (BlockPosition adj : new BlockPosition(from).getAdjacent(true)) {
+
+ IFluidHandler tile = adj.getTileEntity(from.getWorldObj(), IFluidHandler.class);
+ if (tile != null) {
+ if (!tile.canFill(adj.orientation.getOpposite(), l.getFluid())) {
+ continue;
+ }
+
+ int filled = tile.fill(adj.orientation.getOpposite(), l, true);
+ iFluidTank.drain(filled, true);
+ amount -= filled;
+ if (amount <= 0) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/WeightedRandomBlock.java b/src/main/java/cofh/lib/util/WeightedRandomBlock.java
new file mode 100644
index 00000000..23a530f1
--- /dev/null
+++ b/src/main/java/cofh/lib/util/WeightedRandomBlock.java
@@ -0,0 +1,67 @@
+package cofh.lib.util;
+
+import java.util.Collection;
+
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.WeightedRandom;
+
+/**
+ * This class essentially allows for ores to be generated in clusters, with Features randomly choosing one or more blocks from a weighted list.
+ *
+ * @author King Lemming
+ *
+ */
+public final class WeightedRandomBlock extends WeightedRandom.Item {
+
+ public final Block block;
+ public final int metadata;
+
+ public WeightedRandomBlock(ItemStack ore) {
+
+ this(ore, 100);
+ }
+
+ public WeightedRandomBlock(ItemStack ore, int weight) {
+
+ this(Block.getBlockFromItem(ore.getItem()), ore.getItemDamage(), weight);
+ }
+
+ public WeightedRandomBlock(Block ore) {
+
+ this(ore, 0, 100); // some blocks do not have associated items
+ }
+
+ public WeightedRandomBlock(Block ore, int metadata) {
+
+ this(ore, metadata, 100);
+ }
+
+ public WeightedRandomBlock(Block ore, int metadata, int weight) {
+
+ super(weight);
+ this.block = ore;
+ this.metadata = metadata;
+ }
+
+ public static boolean isBlockContained(Block block, int metadata, Collection list) {
+
+ for (WeightedRandomBlock rb : list) {
+ if (block.equals(rb.block) && (metadata == -1 || rb.metadata == -1 || rb.metadata == metadata)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean isBlockContained(Block block, int metadata, WeightedRandomBlock[] list) {
+
+ for (WeightedRandomBlock rb : list) {
+ if (block.equals(rb.block) && (metadata == -1 || rb.metadata == -1 || rb.metadata == metadata)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/WeightedRandomItemStack.java b/src/main/java/cofh/lib/util/WeightedRandomItemStack.java
new file mode 100644
index 00000000..3fc069ab
--- /dev/null
+++ b/src/main/java/cofh/lib/util/WeightedRandomItemStack.java
@@ -0,0 +1,29 @@
+package cofh.lib.util;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.WeightedRandom;
+
+public class WeightedRandomItemStack extends WeightedRandom.Item {
+
+ private final ItemStack stack;
+
+ public WeightedRandomItemStack(ItemStack stack) {
+
+ this(stack, 100);
+ }
+
+ public WeightedRandomItemStack(ItemStack stack, int weight) {
+
+ super(weight);
+ this.stack = stack;
+ }
+
+ public ItemStack getStack() {
+
+ if (stack == null) {
+ return null;
+ }
+ return stack.copy();
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/WeightedRandomNBTTag.java b/src/main/java/cofh/lib/util/WeightedRandomNBTTag.java
new file mode 100644
index 00000000..34f2bb8f
--- /dev/null
+++ b/src/main/java/cofh/lib/util/WeightedRandomNBTTag.java
@@ -0,0 +1,16 @@
+package cofh.lib.util;
+
+import net.minecraft.nbt.NBTBase;
+import net.minecraft.util.WeightedRandom;
+
+public class WeightedRandomNBTTag extends WeightedRandom.Item {
+
+ public final NBTBase tag;
+
+ public WeightedRandomNBTTag(int weight, NBTBase tag) {
+
+ super(weight);
+ this.tag = tag;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/WeightedRandomWorldGenerator.java b/src/main/java/cofh/lib/util/WeightedRandomWorldGenerator.java
new file mode 100644
index 00000000..a92969af
--- /dev/null
+++ b/src/main/java/cofh/lib/util/WeightedRandomWorldGenerator.java
@@ -0,0 +1,21 @@
+package cofh.lib.util;
+
+import net.minecraft.util.WeightedRandom;
+import net.minecraft.world.gen.feature.WorldGenerator;
+
+public final class WeightedRandomWorldGenerator extends WeightedRandom.Item {
+
+ public final WorldGenerator generator;
+
+ public WeightedRandomWorldGenerator(WorldGenerator worldgen) {
+
+ this(worldgen, 100);
+ }
+
+ public WeightedRandomWorldGenerator(WorldGenerator worldgen, int weight) {
+
+ super(weight);
+ generator = worldgen;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/AugmentHelper.java b/src/main/java/cofh/lib/util/helpers/AugmentHelper.java
new file mode 100644
index 00000000..c0c9a012
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/AugmentHelper.java
@@ -0,0 +1,59 @@
+package cofh.lib.util.helpers;
+
+import cofh.api.item.IAugmentItem;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+
+public class AugmentHelper {
+
+ private AugmentHelper() {
+
+ }
+
+ /* NBT TAG HELPERS */
+ public static void writeAugmentsToNBT(NBTTagCompound nbt, ItemStack[] augments) {
+
+ if (augments.length <= 0) {
+ return;
+ }
+ NBTTagList list = new NBTTagList();
+ for (int i = 0; i < augments.length; i++) {
+ if (augments[i] != null) {
+ NBTTagCompound tag = new NBTTagCompound();
+ tag.setInteger("Slot", i);
+ augments[i].writeToNBT(tag);
+ list.appendTag(tag);
+ }
+ }
+ nbt.setTag("Augments", list);
+ }
+
+ /* ITEM HELPERS */
+ public static void writeAugments(ItemStack stack, ItemStack[] augments) {
+
+ if (augments.length <= 0) {
+ return;
+ }
+ if (stack.stackTagCompound == null) {
+ stack.setTagCompound(new NBTTagCompound());
+ }
+ NBTTagList list = new NBTTagList();
+ for (int i = 0; i < augments.length; i++) {
+ if (augments[i] != null) {
+ NBTTagCompound tag = new NBTTagCompound();
+ tag.setInteger("Slot", i);
+ augments[i].writeToNBT(tag);
+ list.appendTag(tag);
+ }
+ }
+ stack.stackTagCompound.setTag("Augments", list);
+ }
+
+ public static boolean isAugmentItem(ItemStack stack) {
+
+ return stack != null && stack.getItem() instanceof IAugmentItem;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/BlockHelper.java b/src/main/java/cofh/lib/util/helpers/BlockHelper.java
new file mode 100644
index 00000000..868d5b53
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/BlockHelper.java
@@ -0,0 +1,571 @@
+package cofh.lib.util.helpers;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.item.EntityItem;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.AxisAlignedBB;
+import net.minecraft.util.MathHelper;
+import net.minecraft.util.MovingObjectPosition;
+import net.minecraft.util.Vec3;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * Contains various helper functions to assist with {@link Block} and Block-related manipulation and interaction.
+ *
+ * @author King Lemming
+ *
+ */
+public final class BlockHelper {
+
+ private BlockHelper() {
+
+ }
+
+ public static int MAX_ID = 1024;
+ public static byte[] rotateType = new byte[MAX_ID];
+ public static final int[][] SIDE_COORD_MOD = { { 0, -1, 0 }, { 0, 1, 0 }, { 0, 0, -1 }, { 0, 0, 1 }, { -1, 0, 0 }, { 1, 0, 0 } };
+ public static float[][] SIDE_COORD_AABB = { { 1, -2, 1 }, { 1, 2, 1 }, { 1, 1, 1 }, { 1, 1, 2 }, { 1, 1, 1 }, { 2, 1, 1 } };
+ public static final byte[] SIDE_LEFT = { 4, 5, 5, 4, 2, 3 };
+ public static final byte[] SIDE_RIGHT = { 5, 4, 4, 5, 3, 2 };
+ public static final byte[] SIDE_OPPOSITE = { 1, 0, 3, 2, 5, 4 };
+ public static final byte[] SIDE_ABOVE = { 3, 2, 1, 1, 1, 1 };
+ public static final byte[] SIDE_BELOW = { 2, 3, 0, 0, 0, 0 };
+
+ // These assume facing is towards negative - looking AT side 1, 3, or 5.
+ public static final byte[] ROTATE_CLOCK_Y = { 0, 1, 4, 5, 3, 2 };
+ public static final byte[] ROTATE_CLOCK_Z = { 5, 4, 2, 3, 0, 1 };
+ public static final byte[] ROTATE_CLOCK_X = { 2, 3, 1, 0, 4, 5 };
+
+ public static final byte[] ROTATE_COUNTER_Y = { 0, 1, 5, 4, 2, 3 };
+ public static final byte[] ROTATE_COUNTER_Z = { 4, 5, 2, 3, 1, 0 };
+ public static final byte[] ROTATE_COUNTER_X = { 3, 2, 0, 1, 4, 5 };
+
+ public static final byte[] INVERT_AROUND_Y = { 0, 1, 3, 2, 5, 4 };
+ public static final byte[] INVERT_AROUND_Z = { 1, 0, 2, 3, 5, 4 };
+ public static final byte[] INVERT_AROUND_X = { 1, 0, 3, 2, 4, 5 };
+
+ // Map which gives relative Icon to use on a block which can be placed on any side.
+ public static final byte[][] ICON_ROTATION_MAP = new byte[6][];
+
+ static {
+ ICON_ROTATION_MAP[0] = new byte[] { 0, 1, 2, 3, 4, 5 };
+ ICON_ROTATION_MAP[1] = new byte[] { 1, 0, 2, 3, 4, 5 };
+ ICON_ROTATION_MAP[2] = new byte[] { 3, 2, 0, 1, 4, 5 };
+ ICON_ROTATION_MAP[3] = new byte[] { 3, 2, 1, 0, 5, 4 };
+ ICON_ROTATION_MAP[4] = new byte[] { 3, 2, 5, 4, 0, 1 };
+ ICON_ROTATION_MAP[5] = new byte[] { 3, 2, 4, 5, 1, 0 };
+ }
+
+ public static final class RotationType {
+
+ public static final int PREVENT = -1;
+ public static final int FOUR_WAY = 1;
+ public static final int SIX_WAY = 2;
+ public static final int RAIL = 3;
+ public static final int PUMPKIN = 4;
+ public static final int STAIRS = 5;
+ public static final int REDSTONE = 6;
+ public static final int LOG = 7;
+ public static final int SLAB = 8;
+ public static final int CHEST = 9;
+ public static final int LEVER = 10;
+ public static final int SIGN = 11;
+
+ private RotationType() {
+
+ }
+ }
+
+ static { // TODO: review which of these can be removed in favor of the vanilla handler
+ rotateType[Block.getIdFromBlock(Blocks.bed)] = RotationType.PREVENT;
+
+ rotateType[Block.getIdFromBlock(Blocks.stone_slab)] = RotationType.SLAB;
+ rotateType[Block.getIdFromBlock(Blocks.wooden_slab)] = RotationType.SLAB;
+
+ rotateType[Block.getIdFromBlock(Blocks.rail)] = RotationType.RAIL;
+ rotateType[Block.getIdFromBlock(Blocks.golden_rail)] = RotationType.RAIL;
+ rotateType[Block.getIdFromBlock(Blocks.detector_rail)] = RotationType.RAIL;
+ rotateType[Block.getIdFromBlock(Blocks.activator_rail)] = RotationType.RAIL;
+
+ rotateType[Block.getIdFromBlock(Blocks.pumpkin)] = RotationType.PUMPKIN;
+ rotateType[Block.getIdFromBlock(Blocks.lit_pumpkin)] = RotationType.PUMPKIN;
+
+ rotateType[Block.getIdFromBlock(Blocks.furnace)] = RotationType.FOUR_WAY;
+ rotateType[Block.getIdFromBlock(Blocks.lit_furnace)] = RotationType.FOUR_WAY;
+ rotateType[Block.getIdFromBlock(Blocks.ender_chest)] = RotationType.FOUR_WAY;
+
+ rotateType[Block.getIdFromBlock(Blocks.trapped_chest)] = RotationType.CHEST;
+ rotateType[Block.getIdFromBlock(Blocks.chest)] = RotationType.CHEST;
+
+ rotateType[Block.getIdFromBlock(Blocks.dispenser)] = RotationType.SIX_WAY;
+ rotateType[Block.getIdFromBlock(Blocks.sticky_piston)] = RotationType.SIX_WAY;
+ rotateType[Block.getIdFromBlock(Blocks.piston)] = RotationType.SIX_WAY;
+ rotateType[Block.getIdFromBlock(Blocks.hopper)] = RotationType.SIX_WAY;
+ rotateType[Block.getIdFromBlock(Blocks.dropper)] = RotationType.SIX_WAY;
+
+ rotateType[Block.getIdFromBlock(Blocks.unpowered_repeater)] = RotationType.REDSTONE;
+ rotateType[Block.getIdFromBlock(Blocks.unpowered_comparator)] = RotationType.REDSTONE;
+ rotateType[Block.getIdFromBlock(Blocks.powered_repeater)] = RotationType.REDSTONE;
+ rotateType[Block.getIdFromBlock(Blocks.powered_comparator)] = RotationType.REDSTONE;
+
+ rotateType[Block.getIdFromBlock(Blocks.lever)] = RotationType.LEVER;
+
+ rotateType[Block.getIdFromBlock(Blocks.standing_sign)] = RotationType.SIGN;
+
+ rotateType[Block.getIdFromBlock(Blocks.oak_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.stone_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.brick_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.stone_brick_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.nether_brick_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.sandstone_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.spruce_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.birch_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.jungle_stairs)] = RotationType.STAIRS;
+ rotateType[Block.getIdFromBlock(Blocks.quartz_stairs)] = RotationType.STAIRS;
+ }
+
+ public static int getMicroBlockAngle(int side, float hitX, float hitY, float hitZ) {
+
+ int direction = side ^ 1;
+ float degreeCenter = 0.32f / 2;
+
+ float x = 0, y = 0;
+ switch (side >> 1) {
+ case 0:
+ x = hitX;
+ y = hitZ;
+ break;
+ case 1:
+ x = hitX;
+ y = hitY;
+ break;
+ case 2:
+ x = hitY;
+ y = hitZ;
+ break;
+ }
+ x -= .5f;
+ y -= .5f;
+
+ if (x * x + y * y > degreeCenter * degreeCenter) {
+
+ int a = (int) ((Math.atan2(x, y) + Math.PI) * 4 / Math.PI);
+ a = ++a & 7;
+ switch (a >> 1) {
+ case 0:
+ case 4:
+ direction = 2;
+ break;
+ case 1:
+ direction = 4;
+ break;
+ case 2:
+ direction = 3;
+ break;
+ case 3:
+ direction = 5;
+ break;
+ }
+ }
+ return direction;
+ }
+
+ public static ForgeDirection getMicroBlockAngle(ForgeDirection side, float hitX, float hitY, float hitZ) {
+
+ return ForgeDirection.VALID_DIRECTIONS[getMicroBlockAngle(side.ordinal(), hitX, hitY, hitZ)];
+ }
+
+ public static int getHighestY(World world, int x, int z) {
+
+ return world.getChunkFromBlockCoords(x, z).getTopFilledSegment() + 16;
+ }
+
+ public static int getSurfaceBlockY(World world, int x, int z) {
+
+ int y = world.getChunkFromBlockCoords(x, z).getTopFilledSegment() + 16;
+
+ Block block;
+ do {
+ if (--y < 0) {
+ break;
+ }
+ block = world.getBlock(x, y, z);
+ } while (block.isAir(world, x, y, z) || block.isReplaceable(world, x, y, z) || block.isLeaves(world, x, y, z) || block.isFoliage(world, x, y, z)
+ || block.canBeReplacedByLeaves(world, x, y, z));
+ return y;
+ }
+
+ public static int getTopBlockY(World world, int x, int z) {
+
+ int y = world.getChunkFromBlockCoords(x, z).getTopFilledSegment() + 16;
+
+ Block block;
+ do {
+ if (--y < 0) {
+ break;
+ }
+ block = world.getBlock(x, y, z);
+ } while (block.isAir(world, x, y, z));
+ return y;
+ }
+
+ public static MovingObjectPosition getCurrentMovingObjectPosition(EntityPlayer player, double distance, boolean fluid) {
+
+ Vec3 posVec = Vec3.createVectorHelper(player.posX, player.posY, player.posZ);
+ Vec3 lookVec = player.getLook(1);
+ posVec.yCoord += player.getEyeHeight();
+ lookVec = posVec.addVector(lookVec.xCoord * distance, lookVec.yCoord * distance, lookVec.zCoord * distance);
+ return player.worldObj.rayTraceBlocks(posVec, lookVec, fluid);
+ }
+
+ public static MovingObjectPosition getCurrentMovingObjectPosition(EntityPlayer player, double distance) {
+
+ return getCurrentMovingObjectPosition(player, distance, false);
+ }
+
+ public static MovingObjectPosition getCurrentMovingObjectPosition(EntityPlayer player, boolean fluid) {
+
+ return getCurrentMovingObjectPosition(player, player.capabilities.isCreativeMode ? 5.0F : 4.5F, fluid);
+ }
+
+ public static MovingObjectPosition getCurrentMovingObjectPosition(EntityPlayer player) {
+
+ return getCurrentMovingObjectPosition(player, player.capabilities.isCreativeMode ? 5.0F : 4.5F, false);
+ }
+
+ public static int getCurrentMousedOverSide(EntityPlayer player) {
+
+ MovingObjectPosition mouseOver = getCurrentMovingObjectPosition(player);
+ return mouseOver == null ? 0 : mouseOver.sideHit;
+ }
+
+ public static int determineXZPlaceFacing(EntityLivingBase living) {
+
+ int quadrant = MathHelper.floor_double(living.rotationYaw * 4.0F / 360.0F + 0.5D) & 3;
+
+ switch (quadrant) {
+ case 0:
+ return 2;
+ case 1:
+ return 5;
+ case 2:
+ return 3;
+ case 3:
+ return 4;
+ }
+ return 3;
+ }
+
+ public static boolean isEqual(Block blockA, Block blockB) {
+
+ if (blockA == blockB) {
+ return true;
+ }
+ if (blockA == null | blockB == null) {
+ return false;
+ }
+ return blockA.equals(blockB) || blockA.isAssociatedBlock(blockB);
+ }
+
+ /* UNSAFE Tile Entity Retrieval */
+ // public static TileEntity getAdjacentTileEntityUnsafe(World world, int x, int y, int z, ForgeDirection dir) {
+ //
+ // if (world == null) {
+ // return null;
+ // }
+ // Chunk chunk = world.getChunkFromBlockCoords(x + dir.offsetX, z + dir.offsetZ);
+ // return chunk == null ? null : chunk.getChunkBlockTileEntityUnsafe((x + dir.offsetX) & 0xF, y + dir.offsetY, (z + dir.offsetZ) & 0xF);
+ // }
+ //
+ // public static TileEntity getAdjacentTileEntityUnsafe(World world, int x, int y, int z, int side) {
+ //
+ // return world == null ? null : getAdjacentTileEntityUnsafe(world, x, y, z, ForgeDirection.values()[side]);
+ // }
+ //
+ // public static TileEntity getAdjacentTileEntityUnsafe(TileEntity refTile, ForgeDirection dir) {
+ //
+ // return refTile == null ? null : getAdjacentTileEntityUnsafe(refTile.worldObj, refTile.xCoord, refTile.yCoord, refTile.zCoord, dir);
+ // }
+ //
+ // public static TileEntity getAdjacentTileEntityUnsafe(TileEntity refTile, int side) {
+ //
+ // return refTile == null ? null : getAdjacentTileEntityUnsafe(refTile.worldObj, refTile.xCoord, refTile.yCoord, refTile.zCoord,
+ // ForgeDirection.values()[side]);
+ // }
+
+ public static Block getAdjacentBlock(World world, int x, int y, int z, ForgeDirection dir) {
+
+ x += dir.offsetX;
+ y += dir.offsetY;
+ z += dir.offsetZ;
+ return world == null || !world.blockExists(x, y, z) ? Blocks.air : world.getBlock(x, y, z);
+ }
+
+ public static Block getAdjacentBlock(World world, int x, int y, int z, int side) {
+
+ return world == null ? Blocks.air : getAdjacentBlock(world, x, y, z, ForgeDirection.getOrientation(side));
+ }
+
+ /* Safe Tile Entity Retrieval */
+ public static TileEntity getAdjacentTileEntity(World world, int x, int y, int z, ForgeDirection dir) {
+
+ x += dir.offsetX;
+ y += dir.offsetY;
+ z += dir.offsetZ;
+ return world == null || !world.blockExists(x, y, z) ? null : world.getTileEntity(x, y, z);
+ }
+
+ public static TileEntity getAdjacentTileEntity(World world, int x, int y, int z, int side) {
+
+ return world == null ? null : getAdjacentTileEntity(world, x, y, z, ForgeDirection.getOrientation(side));
+ }
+
+ public static TileEntity getAdjacentTileEntity(TileEntity refTile, ForgeDirection dir) {
+
+ return refTile == null ? null : getAdjacentTileEntity(refTile.getWorldObj(), refTile.xCoord, refTile.yCoord, refTile.zCoord, dir);
+ }
+
+ public static TileEntity getAdjacentTileEntity(TileEntity refTile, int side) {
+
+ return refTile == null ? null : getAdjacentTileEntity(refTile.getWorldObj(), refTile.xCoord, refTile.yCoord, refTile.zCoord,
+ ForgeDirection.values()[side]);
+ }
+
+ public static int determineAdjacentSide(TileEntity refTile, int x, int y, int z) {
+
+ return y > refTile.yCoord ? 1 : y < refTile.yCoord ? 0 : z > refTile.zCoord ? 3 : z < refTile.zCoord ? 2 : x > refTile.xCoord ? 5 : 4;
+ }
+
+ /* COORDINATE TRANSFORM */
+ public static int[] getAdjacentCoordinatesForSide(MovingObjectPosition pos) {
+
+ return getAdjacentCoordinatesForSide(pos.blockX, pos.blockY, pos.blockZ, pos.sideHit);
+ }
+
+ public static int[] getAdjacentCoordinatesForSide(int x, int y, int z, int side) {
+
+ return new int[] { x + SIDE_COORD_MOD[side][0], y + SIDE_COORD_MOD[side][1], z + SIDE_COORD_MOD[side][2] };
+ }
+
+ public static AxisAlignedBB getAdjacentAABBForSide(MovingObjectPosition pos) {
+
+ return getAdjacentAABBForSide(pos.blockX, pos.blockY, pos.blockZ, pos.sideHit);
+ }
+
+ public static AxisAlignedBB getAdjacentAABBForSide(int x, int y, int z, int side) {
+
+ return AxisAlignedBB.getBoundingBox(x + SIDE_COORD_MOD[side][0], y + SIDE_COORD_MOD[side][1], z + SIDE_COORD_MOD[side][2],
+ x + SIDE_COORD_AABB[side][0], y + SIDE_COORD_AABB[side][1], z + SIDE_COORD_AABB[side][2]);
+ }
+
+ public static int getLeftSide(int side) {
+
+ return SIDE_LEFT[side];
+ }
+
+ public static int getRightSide(int side) {
+
+ return SIDE_RIGHT[side];
+ }
+
+ public static int getOppositeSide(int side) {
+
+ return SIDE_OPPOSITE[side];
+ }
+
+ public static int getAboveSide(int side) {
+
+ return SIDE_ABOVE[side];
+ }
+
+ public static int getBelowSide(int side) {
+
+ return SIDE_BELOW[side];
+ }
+
+ /* BLOCK ROTATION */
+ public static boolean canRotate(Block block) {
+
+ int bId = Block.getIdFromBlock(block);
+ return bId < MAX_ID ? rotateType[Block.getIdFromBlock(block)] != 0 : false;
+ }
+
+ public static int rotateVanillaBlock(World world, Block block, int x, int y, int z) {
+
+ int bId = Block.getIdFromBlock(block), bMeta = world.getBlockMetadata(x, y, z);
+ switch (rotateType[bId]) {
+ case RotationType.FOUR_WAY:
+ return SIDE_LEFT[bMeta];
+ case RotationType.SIX_WAY:
+ if (bMeta < 6) {
+ return ++bMeta % 6;
+ }
+ return bMeta;
+ case RotationType.RAIL:
+ if (bMeta < 2) {
+ return ++bMeta % 2;
+ }
+ return bMeta;
+ case RotationType.PUMPKIN:
+ return ++bMeta % 4;
+ case RotationType.STAIRS:
+ return ++bMeta % 8;
+ case RotationType.REDSTONE:
+ int upper = bMeta & 0xC;
+ int lower = bMeta & 0x3;
+ return upper + ++lower % 4;
+ case RotationType.LOG:
+ return (bMeta + 4) % 12;
+ case RotationType.SLAB:
+ return (bMeta + 8) % 16;
+ case RotationType.CHEST:
+ int coords[] = new int[3];
+ for (int i = 2; i < 6; i++) {
+ coords = getAdjacentCoordinatesForSide(x, y, z, i);
+ if (isEqual(world.getBlock(coords[0], coords[1], coords[2]), block)) {
+ world.setBlockMetadataWithNotify(coords[0], coords[1], coords[2], SIDE_OPPOSITE[bMeta], 1);
+ return SIDE_OPPOSITE[bMeta];
+ }
+ }
+ return SIDE_LEFT[bMeta];
+ case RotationType.LEVER:
+ int shift = 0;
+ if (bMeta > 7) {
+ bMeta -= 8;
+ shift = 8;
+ }
+ if (bMeta == 5) {
+ return 6 + shift;
+ } else if (bMeta == 6) {
+ return 5 + shift;
+ } else if (bMeta == 7) {
+ return 0 + shift;
+ } else if (bMeta == 0) {
+ return 7 + shift;
+ }
+ return bMeta + shift;
+ case RotationType.SIGN:
+ return ++bMeta % 16;
+ case RotationType.PREVENT:
+ default:
+ return bMeta;
+ }
+ }
+
+ public static int rotateVanillaBlockAlt(World world, Block block, int x, int y, int z) {
+
+ int bId = Block.getIdFromBlock(block), bMeta = world.getBlockMetadata(x, y, z);
+ switch (rotateType[bId]) {
+ case RotationType.FOUR_WAY:
+ return SIDE_RIGHT[bMeta];
+ case RotationType.SIX_WAY:
+ if (bMeta < 6) {
+ return (bMeta + 5) % 6;
+ }
+ return bMeta;
+ case RotationType.RAIL:
+ if (bMeta < 2) {
+ return ++bMeta % 2;
+ }
+ return bMeta;
+ case RotationType.PUMPKIN:
+ return (bMeta + 3) % 4;
+ case RotationType.STAIRS:
+ return (bMeta + 7) % 8;
+ case RotationType.REDSTONE:
+ int upper = bMeta & 0xC;
+ int lower = bMeta & 0x3;
+ return upper + (lower + 3) % 4;
+ case RotationType.LOG:
+ return (bMeta + 8) % 12;
+ case RotationType.SLAB:
+ return (bMeta + 8) % 16;
+ case RotationType.CHEST:
+ int coords[] = new int[3];
+ for (int i = 2; i < 6; i++) {
+ coords = getAdjacentCoordinatesForSide(x, y, z, i);
+ if (isEqual(world.getBlock(coords[0], coords[1], coords[2]), block)) {
+ world.setBlockMetadataWithNotify(coords[0], coords[1], coords[2], SIDE_OPPOSITE[bMeta], 1);
+ return SIDE_OPPOSITE[bMeta];
+ }
+ }
+ return SIDE_RIGHT[bMeta];
+ case RotationType.LEVER:
+ int shift = 0;
+ if (bMeta > 7) {
+ bMeta -= 8;
+ shift = 8;
+ }
+ if (bMeta == 5) {
+ return 6 + shift;
+ } else if (bMeta == 6) {
+ return 5 + shift;
+ } else if (bMeta == 7) {
+ return 0 + shift;
+ } else if (bMeta == 0) {
+ return 7 + shift;
+ }
+ case RotationType.SIGN:
+ return ++bMeta % 16;
+ case RotationType.PREVENT:
+ default:
+ return bMeta;
+ }
+ }
+
+ public static List breakBlock(World worldObj, int x, int y, int z, Block block, int fortune, boolean doBreak, boolean silkTouch) {
+
+ return breakBlock(worldObj, null, x, y, z, block, fortune, doBreak, silkTouch);
+ }
+
+ public static List breakBlock(World worldObj, EntityPlayer player, int x, int y, int z, Block block, int fortune, boolean doBreak,
+ boolean silkTouch) {
+
+ if (block.getBlockHardness(worldObj, x, y, z) == -1) {
+ return new LinkedList();
+ }
+ int meta = worldObj.getBlockMetadata(x, y, z);
+ List stacks = null;
+ if (silkTouch && block.canSilkHarvest(worldObj, player, x, y, z, meta)) {
+ stacks = new LinkedList();
+ stacks.add(createStackedBlock(block, meta));
+ } else {
+ stacks = block.getDrops(worldObj, x, y, z, meta, fortune);
+ }
+ if (!doBreak) {
+ return stacks;
+ }
+ worldObj.playAuxSFXAtEntity(player, 2001, x, y, z, Block.getIdFromBlock(block) + (meta << 12));
+ worldObj.setBlockToAir(x, y, z);
+
+ List result = worldObj.getEntitiesWithinAABB(EntityItem.class, AxisAlignedBB.getBoundingBox(x - 2, y - 2, z - 2, x + 3, y + 3, z + 3));
+ for (int i = 0; i < result.size(); i++) {
+ EntityItem entity = result.get(i);
+ if (entity.isDead || entity.getEntityItem().stackSize <= 0) {
+ continue;
+ }
+ stacks.add(entity.getEntityItem());
+ entity.worldObj.removeEntity(entity);
+ }
+ return stacks;
+ }
+
+ public static ItemStack createStackedBlock(Block block, int bMeta) {
+
+ Item item = Item.getItemFromBlock(block); // not all blocks that can be silked have an item. other method?
+ if (item != null && item.getHasSubtypes()) {
+ return new ItemStack(item, 1, bMeta);
+ }
+ return new ItemStack(item, 1, 0);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/ColorHelper.java b/src/main/java/cofh/lib/util/helpers/ColorHelper.java
new file mode 100644
index 00000000..6c0dec41
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/ColorHelper.java
@@ -0,0 +1,45 @@
+package cofh.lib.util.helpers;
+
+/**
+ * Contains various helper functions to assist with colors.
+ *
+ * @author King Lemming
+ *
+ */
+public final class ColorHelper {
+
+ private ColorHelper() {
+
+ }
+
+ public static final int DYE_BLACK = 0x191919;
+ public static final int DYE_RED = 0xCC4C4C;
+ public static final int DYE_GREEN = 0x667F33;
+ public static final int DYE_BROWN = 0x7F664C;
+ public static final int DYE_BLUE = 0x3366CC;
+ public static final int DYE_PURPLE = 0xB266E5;
+ public static final int DYE_CYAN = 0x4C99B2;
+ public static final int DYE_LIGHT_GRAY = 0x999999;
+ public static final int DYE_GRAY = 0x4C4C4C;
+ public static final int DYE_PINK = 0xF2B2CC;
+ public static final int DYE_LIME = 0x7FCC19;
+ public static final int DYE_YELLOW = 0xE5E533;
+ public static final int DYE_LIGHT_BLUE = 0x99B2F2;
+ public static final int DYE_MAGENTA = 0xE57FD8;
+ public static final int DYE_ORANGE = 0xF2B233;
+ public static final int DYE_WHITE = 0xFFFFFF;
+
+ public static final int[] DYE_COLORS = { DYE_BLACK, DYE_RED, DYE_GREEN, DYE_BROWN, DYE_BLUE, DYE_PURPLE, DYE_CYAN, DYE_LIGHT_GRAY, DYE_GRAY, DYE_PINK,
+ DYE_LIME, DYE_YELLOW, DYE_LIGHT_BLUE, DYE_MAGENTA, DYE_ORANGE, DYE_WHITE };
+
+ // Yes, this list is pre-localized to en_US and has no spaces. There are times when this is useful, such as in a config file. Localization there is messy
+ // and not strictly required.
+ public static final String[] woolColorConfig = { "White", "Orange", "Magenta", "LightBlue", "Yellow", "Lime", "Pink", "Gray", "LightGray", "Cyan",
+ "Purple", "Blue", "Brown", "Green", "Red", "Black" };
+
+ public static int getDyeColor(int color) {
+
+ return color < 0 || color > 15 ? 0xFFFFFF : DYE_COLORS[color];
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/DamageHelper.java b/src/main/java/cofh/lib/util/helpers/DamageHelper.java
new file mode 100644
index 00000000..1b541beb
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/DamageHelper.java
@@ -0,0 +1,91 @@
+package cofh.lib.util.helpers;
+
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.util.DamageSource;
+import net.minecraft.util.EntityDamageSource;
+
+/**
+ * This class contains helper functions related to Damage types that CoFH mods add.
+ *
+ * @author King Lemming
+ *
+ */
+public class DamageHelper {
+
+ private DamageHelper() {
+
+ }
+
+ /* DAMAGE SOURCES */
+ public static final DamageSourcePyrotheum pyrotheum = new DamageSourcePyrotheum();
+ public static final DamageSourceCryotheum cryotheum = new DamageSourceCryotheum();
+ public static final DamageSourcePetrotheum petrotheum = new DamageSourcePetrotheum();
+ public static final DamageSourceMana mana = new DamageSourceMana();
+ public static final DamageSourceFlux flux = new DamageSourceFlux();
+
+ /* DAMAGE SOURCE CLASSES */
+ public static class DamageSourcePyrotheum extends DamageSource {
+
+ protected DamageSourcePyrotheum() {
+
+ super("pyrotheum");
+ this.setDamageBypassesArmor();
+ this.isFireDamage();
+ }
+ }
+
+ public static class DamageSourceCryotheum extends DamageSource {
+
+ protected DamageSourceCryotheum() {
+
+ super("cryotheum");
+ this.setDamageBypassesArmor();
+ }
+ }
+
+ public static class DamageSourcePetrotheum extends DamageSource {
+
+ protected DamageSourcePetrotheum() {
+
+ super("petrotheum");
+ this.setDamageBypassesArmor();
+ }
+ }
+
+ public static class DamageSourceMana extends DamageSource {
+
+ protected DamageSourceMana() {
+
+ super("mana");
+ this.setDamageBypassesArmor();
+ this.isMagicDamage();
+ }
+ }
+
+ public static class DamageSourceFlux extends DamageSource {
+
+ protected DamageSourceFlux() {
+
+ super("flux");
+ this.setDamageBypassesArmor();
+ }
+ }
+
+ /* ENTITY DAMAGE SOURCES */
+ public static class EntityDamageSourceFlux extends EntityDamageSource {
+
+ public EntityDamageSourceFlux(String type, Entity entity) {
+
+ super(type, entity);
+ this.setDamageBypassesArmor();
+ }
+ }
+
+ /* HELPERS */
+ public static DamageSource causePlayerFluxDamage(EntityPlayer entityPlayer) {
+
+ return new EntityDamageSourceFlux("player", entityPlayer);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/EnergyHelper.java b/src/main/java/cofh/lib/util/helpers/EnergyHelper.java
new file mode 100644
index 00000000..d83d4bbd
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/EnergyHelper.java
@@ -0,0 +1,167 @@
+package cofh.lib.util.helpers;
+
+import cofh.api.energy.IEnergyConnection;
+import cofh.api.energy.IEnergyContainerItem;
+import cofh.api.energy.IEnergyHandler;
+import cofh.api.energy.IEnergyProvider;
+import cofh.api.energy.IEnergyReceiver;
+
+import java.util.List;
+
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * This class contains helper functions related to Redstone Flux, the basis of the CoFH Energy System.
+ *
+ * @author King Lemming
+ *
+ */
+public class EnergyHelper {
+
+ public static final int RF_PER_MJ = 10; // Official Ratio of RF to MJ (BuildCraft)
+ public static final int RF_PER_EU = 4; // Official Ratio of RF to EU (IndustrialCraft)
+
+ private EnergyHelper() {
+
+ }
+
+ /* NBT TAG HELPER */
+ public static void addEnergyInformation(ItemStack stack, List list) {
+
+ if (stack.getItem() instanceof IEnergyContainerItem) {
+ list.add(StringHelper.localize("info.cofh.charge") + ": " + StringHelper.getScaledNumber(stack.stackTagCompound.getInteger("Energy")) + " / "
+ + StringHelper.getScaledNumber(((IEnergyContainerItem) stack.getItem()).getMaxEnergyStored(stack)) + " RF");
+ }
+ }
+
+ /* IEnergyContainer Interaction */
+ public static int extractEnergyFromContainer(ItemStack container, int maxExtract, boolean simulate) {
+
+ return isEnergyContainerItem(container) ? ((IEnergyContainerItem) container.getItem()).extractEnergy(container, maxExtract, simulate) : 0;
+ }
+
+ public static int insertEnergyIntoContainer(ItemStack container, int maxReceive, boolean simulate) {
+
+ return isEnergyContainerItem(container) ? ((IEnergyContainerItem) container.getItem()).receiveEnergy(container, maxReceive, simulate) : 0;
+ }
+
+ public static int extractEnergyFromHeldContainer(EntityPlayer player, int maxExtract, boolean simulate) {
+
+ ItemStack container = player.getCurrentEquippedItem();
+
+ return isEnergyContainerItem(container) ? ((IEnergyContainerItem) container.getItem()).extractEnergy(container, maxExtract, simulate) : 0;
+ }
+
+ public static int insertEnergyIntoHeldContainer(EntityPlayer player, int maxReceive, boolean simulate) {
+
+ ItemStack container = player.getCurrentEquippedItem();
+
+ return isEnergyContainerItem(container) ? ((IEnergyContainerItem) container.getItem()).receiveEnergy(container, maxReceive, simulate) : 0;
+ }
+
+ public static boolean isPlayerHoldingEnergyContainerItem(EntityPlayer player) {
+
+ return isEnergyContainerItem(player.getCurrentEquippedItem());
+ }
+
+ public static boolean isEnergyContainerItem(ItemStack container) {
+
+ return container != null && container.getItem() instanceof IEnergyContainerItem;
+ }
+
+ public static ItemStack setDefaultEnergyTag(ItemStack container, int energy) {
+
+ if (container.stackTagCompound == null) {
+ container.setTagCompound(new NBTTagCompound());
+ }
+ container.stackTagCompound.setInteger("Energy", energy);
+
+ return container;
+ }
+
+ /* IEnergyHandler Interaction */
+ @Deprecated
+ public static int extractEnergyFromAdjacentEnergyHandler(TileEntity tile, int side, int energy, boolean simulate) {
+
+ TileEntity handler = BlockHelper.getAdjacentTileEntity(tile, side);
+
+ return handler instanceof IEnergyHandler ? ((IEnergyHandler) handler).extractEnergy(ForgeDirection.VALID_DIRECTIONS[side ^ 1], energy, simulate) : 0;
+ }
+
+ @Deprecated
+ public static int insertEnergyIntoAdjacentEnergyHandler(TileEntity tile, int side, int energy, boolean simulate) {
+
+ TileEntity handler = BlockHelper.getAdjacentTileEntity(tile, side);
+
+ return handler instanceof IEnergyHandler ? ((IEnergyHandler) handler).receiveEnergy(ForgeDirection.VALID_DIRECTIONS[side ^ 1], energy, simulate) : 0;
+ }
+
+ public static int extractEnergyFromAdjacentEnergyProvider(TileEntity tile, int side, int energy, boolean simulate) {
+
+ TileEntity handler = BlockHelper.getAdjacentTileEntity(tile, side);
+
+ return handler instanceof IEnergyProvider ? ((IEnergyProvider) handler).extractEnergy(ForgeDirection.VALID_DIRECTIONS[side ^ 1], energy, simulate) : 0;
+ }
+
+ public static int insertEnergyIntoAdjacentEnergyReceiver(TileEntity tile, int side, int energy, boolean simulate) {
+
+ TileEntity handler = BlockHelper.getAdjacentTileEntity(tile, side);
+
+ return handler instanceof IEnergyReceiver ? ((IEnergyReceiver) handler).receiveEnergy(ForgeDirection.VALID_DIRECTIONS[side ^ 1], energy, simulate) : 0;
+ }
+
+ @Deprecated
+ public static boolean isAdjacentEnergyHandlerFromSide(TileEntity tile, int side) {
+
+ TileEntity handler = BlockHelper.getAdjacentTileEntity(tile, side);
+
+ return isEnergyHandlerFromSide(handler, ForgeDirection.VALID_DIRECTIONS[side ^ 1]);
+ }
+
+ @Deprecated
+ public static boolean isEnergyHandlerFromSide(TileEntity tile, ForgeDirection from) {
+
+ return tile instanceof IEnergyHandler ? ((IEnergyHandler) tile).canConnectEnergy(from) : false;
+ }
+
+ public static boolean isAdjacentEnergyConnectableFromSide(TileEntity tile, int side) {
+
+ TileEntity handler = BlockHelper.getAdjacentTileEntity(tile, side);
+
+ return isEnergyConnectableFromSide(handler, ForgeDirection.VALID_DIRECTIONS[side ^ 1]);
+ }
+
+ public static boolean isEnergyConnectableFromSide(TileEntity tile, ForgeDirection from) {
+
+ return tile instanceof IEnergyConnection ? ((IEnergyConnection) tile).canConnectEnergy(from) : false;
+ }
+
+ public static boolean isAdjacentEnergyReceiverFromSide(TileEntity tile, int side) {
+
+ TileEntity handler = BlockHelper.getAdjacentTileEntity(tile, side);
+
+ return isEnergyReceiverFromSide(handler, ForgeDirection.VALID_DIRECTIONS[side ^ 1]);
+ }
+
+ public static boolean isEnergyReceiverFromSide(TileEntity tile, ForgeDirection from) {
+
+ return tile instanceof IEnergyReceiver ? ((IEnergyReceiver) tile).canConnectEnergy(from) : false;
+ }
+
+ public static boolean isAdjacentEnergyProviderFromSide(TileEntity tile, int side) {
+
+ TileEntity handler = BlockHelper.getAdjacentTileEntity(tile, side);
+
+ return isEnergyProviderFromSide(handler, ForgeDirection.VALID_DIRECTIONS[side ^ 1]);
+ }
+
+ public static boolean isEnergyProviderFromSide(TileEntity tile, ForgeDirection from) {
+
+ return tile instanceof IEnergyProvider ? ((IEnergyProvider) tile).canConnectEnergy(from) : false;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/EntityHelper.java b/src/main/java/cofh/lib/util/helpers/EntityHelper.java
new file mode 100644
index 00000000..47d3f175
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/EntityHelper.java
@@ -0,0 +1,198 @@
+package cofh.lib.util.helpers;
+
+import cpw.mods.fml.common.FMLCommonHandler;
+
+import java.util.Iterator;
+
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.network.play.server.S07PacketRespawn;
+import net.minecraft.network.play.server.S1DPacketEntityEffect;
+import net.minecraft.potion.PotionEffect;
+import net.minecraft.server.management.ServerConfigurationManager;
+import net.minecraft.util.MathHelper;
+import net.minecraft.world.WorldProvider;
+import net.minecraft.world.WorldServer;
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * This class contains various helper functions related to Entities.
+ *
+ * @author King Lemming
+ *
+ */
+public class EntityHelper {
+
+ private EntityHelper() {
+
+ }
+
+ public static int getEntityFacingCardinal(EntityLivingBase living) {
+
+ int quadrant = cofh.lib.util.helpers.MathHelper.floor(living.rotationYaw * 4.0F / 360.0F + 0.5D) & 3;
+
+ switch (quadrant) {
+ case 0:
+ return 2;
+ case 1:
+ return 5;
+ case 2:
+ return 3;
+ default:
+ return 4;
+ }
+ }
+
+ public static ForgeDirection getEntityFacingForgeDirection(EntityLivingBase living) {
+
+ return ForgeDirection.VALID_DIRECTIONS[getEntityFacingCardinal(living)];
+ }
+
+ public static void transferEntityToDimension(Entity entity, int dimension, ServerConfigurationManager manager) {
+
+ if (entity instanceof EntityPlayerMP) {
+ transferPlayerToDimension((EntityPlayerMP) entity, dimension, manager);
+ return;
+ }
+ WorldServer worldserver = manager.getServerInstance().worldServerForDimension(entity.dimension);
+ entity.dimension = dimension;
+ WorldServer worldserver1 = manager.getServerInstance().worldServerForDimension(entity.dimension);
+ worldserver.removePlayerEntityDangerously(entity);
+ if (entity.riddenByEntity != null) {
+ entity.riddenByEntity.mountEntity(null);
+ }
+ if (entity.ridingEntity != null) {
+ entity.mountEntity(null);
+ }
+ entity.isDead = false;
+ transferEntityToWorld(entity, worldserver, worldserver1);
+ }
+
+ public static void transferEntityToWorld(Entity entity, WorldServer oldWorld, WorldServer newWorld) {
+
+ WorldProvider pOld = oldWorld.provider;
+ WorldProvider pNew = newWorld.provider;
+ double moveFactor = pOld.getMovementFactor() / pNew.getMovementFactor();
+ double x = entity.posX * moveFactor;
+ double z = entity.posZ * moveFactor;
+
+ oldWorld.theProfiler.startSection("placing");
+ x = MathHelper.clamp_double(x, -29999872, 29999872);
+ z = MathHelper.clamp_double(z, -29999872, 29999872);
+
+ if (entity.isEntityAlive()) {
+ entity.setLocationAndAngles(x, entity.posY, z, entity.rotationYaw, entity.rotationPitch);
+ entity.forceSpawn = true;
+ newWorld.spawnEntityInWorld(entity);
+ entity.forceSpawn = false;
+ newWorld.updateEntityWithOptionalForce(entity, false);
+ }
+
+ oldWorld.theProfiler.endSection();
+
+ entity.setWorld(newWorld);
+ }
+
+ public static void transferPlayerToDimension(EntityPlayerMP player, int dimension, ServerConfigurationManager manager) {
+
+ int oldDim = player.dimension;
+ WorldServer worldserver = manager.getServerInstance().worldServerForDimension(player.dimension);
+ player.dimension = dimension;
+ WorldServer worldserver1 = manager.getServerInstance().worldServerForDimension(player.dimension);
+ player.playerNetServerHandler.sendPacket(new S07PacketRespawn(player.dimension, player.worldObj.difficultySetting, player.worldObj.getWorldInfo()
+ .getTerrainType(), player.theItemInWorldManager.getGameType()));
+ worldserver.removePlayerEntityDangerously(player);
+ if (player.riddenByEntity != null) {
+ player.riddenByEntity.mountEntity(null);
+ }
+ if (player.ridingEntity != null) {
+ player.mountEntity(null);
+ }
+ player.isDead = false;
+ transferEntityToWorld(player, worldserver, worldserver1);
+ manager.func_72375_a(player, worldserver);
+ player.playerNetServerHandler.setPlayerLocation(player.posX, player.posY, player.posZ, player.rotationYaw, player.rotationPitch);
+ player.theItemInWorldManager.setWorld(worldserver1);
+ manager.updateTimeAndWeatherForPlayer(player, worldserver1);
+ manager.syncPlayerInventory(player);
+ Iterator iterator = player.getActivePotionEffects().iterator();
+
+ while (iterator.hasNext()) {
+ PotionEffect potioneffect = iterator.next();
+ player.playerNetServerHandler.sendPacket(new S1DPacketEntityEffect(player.getEntityId(), potioneffect));
+ }
+ FMLCommonHandler.instance().firePlayerChangedDimensionEvent(player, oldDim, dimension);
+ }
+
+ public static void transferEntityToDimension(Entity entity, double x, double y, double z, int dimension, ServerConfigurationManager manager) {
+
+ if (entity instanceof EntityPlayerMP) {
+ transferPlayerToDimension((EntityPlayerMP) entity, x, y, z, dimension, manager);
+ return;
+ }
+ if (entity.riddenByEntity != null) {
+ entity.riddenByEntity.mountEntity(null);
+ }
+ if (entity.ridingEntity != null) {
+ entity.mountEntity(null);
+ }
+ WorldServer worldserver = manager.getServerInstance().worldServerForDimension(entity.dimension);
+ entity.dimension = dimension;
+ WorldServer worldserver1 = manager.getServerInstance().worldServerForDimension(entity.dimension);
+ worldserver.removePlayerEntityDangerously(entity);
+ entity.isDead = false;
+ transferEntityToWorld(entity, x, y, z, worldserver, worldserver1);
+ }
+
+ public static void transferEntityToWorld(Entity entity, double x, double y, double z, WorldServer oldWorld, WorldServer newWorld) {
+
+ oldWorld.theProfiler.startSection("placing");
+ x = MathHelper.clamp_double(x, -29999872, 29999872);
+ z = MathHelper.clamp_double(z, -29999872, 29999872);
+
+ if (entity.isEntityAlive()) {
+ entity.setLocationAndAngles(x, y, z, entity.rotationYaw, entity.rotationPitch);
+ entity.forceSpawn = true;
+ newWorld.spawnEntityInWorld(entity);
+ entity.forceSpawn = false;
+ newWorld.updateEntityWithOptionalForce(entity, false);
+ }
+
+ oldWorld.theProfiler.endSection();
+
+ entity.setWorld(newWorld);
+ }
+
+ public static void transferPlayerToDimension(EntityPlayerMP player, double x, double y, double z, int dimension, ServerConfigurationManager manager) {
+
+ int oldDim = player.dimension;
+ WorldServer worldserver = manager.getServerInstance().worldServerForDimension(player.dimension);
+ player.dimension = dimension;
+ WorldServer worldserver1 = manager.getServerInstance().worldServerForDimension(player.dimension);
+ player.playerNetServerHandler.sendPacket(new S07PacketRespawn(player.dimension, player.worldObj.difficultySetting, player.worldObj.getWorldInfo()
+ .getTerrainType(), player.theItemInWorldManager.getGameType()));
+ worldserver.removePlayerEntityDangerously(player);
+ if (player.riddenByEntity != null) {
+ player.riddenByEntity.mountEntity(null);
+ }
+ if (player.ridingEntity != null) {
+ player.mountEntity(null);
+ }
+ player.isDead = false;
+ transferEntityToWorld(player, x, y, z, worldserver, worldserver1);
+ manager.func_72375_a(player, worldserver);
+ player.playerNetServerHandler.setPlayerLocation(x, y, z, player.rotationYaw, player.rotationPitch);
+ player.theItemInWorldManager.setWorld(worldserver1);
+ manager.updateTimeAndWeatherForPlayer(player, worldserver1);
+ manager.syncPlayerInventory(player);
+ Iterator iterator = player.getActivePotionEffects().iterator();
+
+ while (iterator.hasNext()) {
+ PotionEffect potioneffect = iterator.next();
+ player.playerNetServerHandler.sendPacket(new S1DPacketEntityEffect(player.getEntityId(), potioneffect));
+ }
+ FMLCommonHandler.instance().firePlayerChangedDimensionEvent(player, oldDim, dimension);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/FireworksHelper.java b/src/main/java/cofh/lib/util/helpers/FireworksHelper.java
new file mode 100644
index 00000000..351097c5
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/FireworksHelper.java
@@ -0,0 +1,288 @@
+package cofh.lib.util.helpers;
+
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.minecraft.init.Items;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+
+/**
+ * Contains helper functions to assist with working with fireworks.
+ *
+ * @author Tonius
+ */
+public final class FireworksHelper {
+
+ private FireworksHelper() {
+
+ }
+
+ /**
+ * Represents a single explosion that a firework rocket can contain.
+ *
+ * @author Tonius
+ */
+ public static final class Explosion {
+
+ /**
+ * The different shapes that an explosion can have.
+ *
+ * @author Tonius
+ */
+ public static enum Type {
+ /** Small ball (default) */
+ BALL,
+ /** Large ball (made with Fire Charge) */
+ LARGE_BALL,
+ /** Star-shaped (made with Gold Nugget) */
+ STAR,
+ /** Creeper face (made with any Head) */
+ CREEPER,
+ /** Burst (made with Feather) */
+ BURST;
+ }
+
+ /**
+ * Generates a randomized Explosion instance.
+ *
+ * @param primaryColors
+ * The amount of different primary colors that the Explosion will have.
+ * @param fadeColors
+ * The amount of different fade colors that the Explosion will have.
+ *
+ * @return A random Explosion instance
+ */
+ public static Explosion getRandom(int primaryColors, int fadeColors) {
+
+ primaryColors = MathHelper.clamp(primaryColors, 1, Integer.MAX_VALUE);
+ fadeColors = MathHelper.clamp(fadeColors, 1, Integer.MAX_VALUE);
+
+ Explosion e = new Explosion();
+
+ int v;
+ switch (v = MathHelper.RANDOM.nextInt(4)) {
+ case 2:
+ case 0:
+ e.setTwinkle(true);
+ if (v == 0) {
+ break;
+ }
+ case 1:
+ e.setTrail(true);
+ }
+
+ e.setType(MathHelper.RANDOM.nextInt(5));
+
+ for (int i = 0; i < primaryColors; i++) {
+ Color color = new Color(Color.HSBtoRGB(MathHelper.RANDOM.nextFloat() * 360, MathHelper.RANDOM.nextFloat() * 0.15F + 0.8F, 0.85F));
+ e.addPrimaryColor(color.getRed(), color.getGreen(), color.getBlue());
+ }
+
+ for (int i = 0; i < fadeColors; i++) {
+ Color color = new Color(Color.HSBtoRGB(MathHelper.RANDOM.nextFloat() * 360, MathHelper.RANDOM.nextFloat() * 0.15F + 0.8F, 0.85F));
+ e.addFadeColor(color.getRed(), color.getGreen(), color.getBlue());
+ }
+
+ return e;
+ }
+
+ private boolean twinkle = false;
+ private boolean trail = false;
+ private List primaryColors = new ArrayList();
+ private List fadeColors = new ArrayList();
+ private Type type = Type.BALL;
+
+ /**
+ * Sets whether the explosion should have the 'twinkle' effect (made with Glowstone Dust).
+ *
+ * @param twinkle
+ * Whether to have the 'twinkle' effect.
+ * @return The current Explosion instance.
+ */
+ public Explosion setTwinkle(boolean twinkle) {
+
+ this.twinkle = twinkle;
+ return this;
+ }
+
+ /**
+ * Sets whether the explosion should have the 'trail' effect (made with Diamond).
+ *
+ * @param trail
+ * Whether to have the 'trail' effect.
+ * @return The current Explosion instance.
+ */
+ public Explosion setTrail(boolean trail) {
+
+ this.trail = trail;
+ return this;
+ }
+
+ /**
+ * Sets the explosion type using the {@link Type} enum.
+ *
+ * @param type
+ * The explosion type to set.
+ * @return The current Explosion instance.
+ */
+ public Explosion setType(Type type) {
+
+ this.type = type;
+ return this;
+ }
+
+ /**
+ * Sets the explosion type using an integer.
+ *
+ * @param type
+ * The explosion type as an integer.
+ * @return The current Explosion instance.
+ */
+ public Explosion setType(int type) {
+
+ this.setType(Type.values()[MathHelper.clamp(type, 0, Type.values().length - 1)]);
+ return this;
+ }
+
+ /**
+ * Adds a primary color to the explosion.
+ *
+ * @param red
+ * The RGB red value of the color to add (0 - 255).
+ * @param green
+ * The RGB green value of the color to add (0 - 255).
+ * @param blue
+ * The RGB blue value of the color to add (0 - 255).
+ * @return The current Explosion instance.
+ */
+ public Explosion addPrimaryColor(int red, int green, int blue) {
+
+ this.primaryColors.add((red << 16) + (green << 8) + blue);
+ return this;
+ }
+
+ /**
+ * Adds a fade color to the explosion.
+ *
+ * @param red
+ * The RGB red value of the color to add (0 - 255).
+ * @param green
+ * The RGB green value of the color to add (0 - 255).
+ * @param blue
+ * The RGB blue value of the color to add (0 - 255).
+ * @return The current Explosion instance.
+ */
+ public Explosion addFadeColor(int red, int green, int blue) {
+
+ this.fadeColors.add((red << 16) + (green << 8) + blue);
+ return this;
+ }
+
+ /**
+ * Converts the Explosion to an {@link NBTTagCompound} for use in creating fireworks {@link ItemStack}s.
+ *
+ * @return An NBTTagCompound representing the Explosion.
+ */
+ public NBTTagCompound getTagCompound() {
+
+ NBTTagCompound tag = new NBTTagCompound();
+
+ tag.setBoolean("Flicker", this.twinkle);
+ tag.setBoolean("Trail", this.trail);
+
+ tag.setByte("Type", (byte) this.type.ordinal());
+
+ int[] colorArray = new int[this.primaryColors.size()];
+ for (int i = 0; i < this.primaryColors.size(); i++) {
+ colorArray[i] = this.primaryColors.get(i);
+ }
+ tag.setIntArray("Colors", colorArray);
+
+ colorArray = new int[this.fadeColors.size()];
+ for (int i = 0; i < this.fadeColors.size(); i++) {
+ colorArray[i] = this.fadeColors.get(i);
+ }
+ tag.setIntArray("FadeColors", colorArray);
+
+ return tag;
+ }
+
+ /**
+ * Converts the Explosion to a Firework Star {@link ItemStack}.
+ *
+ * @return A Firework Star ItemStack representing the Explosion.
+ */
+ public ItemStack getFireworkStarStack() {
+
+ NBTTagCompound tags = new NBTTagCompound();
+ NBTTagCompound explosionTag = this.getTagCompound();
+ tags.setTag("Explosion", explosionTag);
+
+ ItemStack stack = new ItemStack(Items.firework_charge);
+ stack.setTagCompound(tags);
+ return stack;
+ }
+
+ }
+
+ /**
+ * Creates a fireworks {@link ItemStack}, with the resulting fireworks having a certain flight duration and a variety of {@link Explosion}s.
+ *
+ * @param flightDuration
+ * The flight duration of the fireworks. Possible range is between 0 (inclusive) and 3 (inclusive).
+ * @param explosions
+ * The explosions that will occur when the fireworks detonate.
+ * @return A fireworks ItemStack.
+ */
+ public static ItemStack getFireworksStack(int flightDuration, Explosion... explosions) {
+
+ NBTTagCompound tags = new NBTTagCompound();
+
+ NBTTagCompound fireworksTag = new NBTTagCompound();
+ NBTTagList explosionsList = new NBTTagList();
+ if (explosions != null) {
+ for (Explosion e : explosions) {
+ if (e == null) {
+ continue;
+ }
+ explosionsList.appendTag(e.getTagCompound());
+ }
+ }
+
+ fireworksTag.setByte("Flight", (byte) MathHelper.clamp(flightDuration, 0, 3));
+ fireworksTag.setTag("Explosions", explosionsList);
+ tags.setTag("Fireworks", fireworksTag);
+
+ ItemStack stack = new ItemStack(Items.fireworks);
+ stack.setTagCompound(tags);
+ return stack;
+ }
+
+ /**
+ * Creates a randomized fireworks {@link ItemStack}.
+ *
+ * @param flightDuration
+ * The flight duration of the fireworks. Possible range is between 0 (inclusive) and 3 (inclusive).
+ * @param explosions
+ * The amount of {@link Explosion}s that will occur when the fireworks detonate.
+ * @param primaryColors
+ * The amount of different primary colors that each Explosion will have.
+ * @param fadeColors
+ * The amount of different fade colors that each Explosion will have.
+ * @return A randomized fireworks ItemStack.
+ */
+ public static ItemStack getRandomFireworks(int flightDuration, int explosions, int primaryColors, int fadeColors) {
+
+ explosions = MathHelper.clamp(explosions, 0, Integer.MAX_VALUE);
+ Explosion[] explosionsArray = new Explosion[explosions];
+ for (int i = 0; i < explosions; i++) {
+ explosionsArray[i] = Explosion.getRandom(primaryColors, fadeColors);
+ }
+
+ return getFireworksStack(flightDuration, explosionsArray);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/FluidHelper.java b/src/main/java/cofh/lib/util/helpers/FluidHelper.java
new file mode 100644
index 00000000..e3d067ce
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/FluidHelper.java
@@ -0,0 +1,307 @@
+package cofh.lib.util.helpers;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.CompressedStreamTools;
+import net.minecraft.nbt.NBTSizeTracker;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidContainerRegistry;
+import net.minecraftforge.fluids.FluidRegistry;
+import net.minecraftforge.fluids.FluidStack;
+import net.minecraftforge.fluids.FluidTankInfo;
+import net.minecraftforge.fluids.IFluidBlock;
+import net.minecraftforge.fluids.IFluidContainerItem;
+import net.minecraftforge.fluids.IFluidHandler;
+
+/**
+ * Contains various helper functions to assist with {@link Fluid} and Fluid-related manipulation and interaction.
+ *
+ * @author King Lemming
+ *
+ */
+public class FluidHelper {
+
+ public static final int BUCKET_VOLUME = FluidContainerRegistry.BUCKET_VOLUME;
+
+ public static final Fluid WATER_FLUID = FluidRegistry.WATER;
+ public static final Fluid LAVA_FLUID = FluidRegistry.LAVA;
+
+ public static final FluidStack WATER = new FluidStack(WATER_FLUID, BUCKET_VOLUME);
+ public static final FluidStack LAVA = new FluidStack(LAVA_FLUID, BUCKET_VOLUME);
+
+ public static final FluidTankInfo[] NULL_TANK_INFO = new FluidTankInfo[] {};
+
+ private FluidHelper() {
+
+ }
+
+ /* IFluidContainer Interaction */
+ public static int fillFluidContainerItem(ItemStack container, FluidStack resource, boolean doFill) {
+
+ return isFluidContainerItem(container) && container.stackSize == 1 ? ((IFluidContainerItem) container.getItem()).fill(container, resource, doFill) : 0;
+ }
+
+ public static FluidStack drainFluidContainerItem(ItemStack container, int maxDrain, boolean doDrain) {
+
+ return isFluidContainerItem(container) && container.stackSize == 1 ? ((IFluidContainerItem) container.getItem()).drain(container, maxDrain, doDrain)
+ : null;
+ }
+
+ public static FluidStack extractFluidFromHeldContainer(EntityPlayer player, int maxDrain, boolean doDrain) {
+
+ ItemStack container = player.getCurrentEquippedItem();
+
+ return isFluidContainerItem(container) && container.stackSize == 1 ? ((IFluidContainerItem) container.getItem()).drain(container, maxDrain, doDrain)
+ : null;
+ }
+
+ public static int insertFluidIntoHeldContainer(EntityPlayer player, FluidStack resource, boolean doFill) {
+
+ ItemStack container = player.getCurrentEquippedItem();
+
+ return isFluidContainerItem(container) && container.stackSize == 1 ? ((IFluidContainerItem) container.getItem()).fill(container, resource, doFill) : 0;
+ }
+
+ public static boolean isPlayerHoldingFluidContainerItem(EntityPlayer player) {
+
+ return isFluidContainerItem(player.getCurrentEquippedItem());
+ }
+
+ public static boolean isFluidContainerItem(ItemStack container) {
+
+ return container != null && container.getItem() instanceof IFluidContainerItem;
+ }
+
+ public static FluidStack getFluidStackFromContainerItem(ItemStack container) {
+
+ return ((IFluidContainerItem) container.getItem()).getFluid(container);
+ }
+
+ public static ItemStack setDefaultFluidTag(ItemStack container, FluidStack resource) {
+
+ container.setTagCompound(new NBTTagCompound());
+ NBTTagCompound fluidTag = resource.writeToNBT(new NBTTagCompound());
+ container.stackTagCompound.setTag("Fluid", fluidTag);
+
+ return container;
+ }
+
+ /* IFluidHandler Interaction */
+ public static FluidStack extractFluidFromAdjacentFluidHandler(TileEntity tile, int side, int maxDrain, boolean doDrain) {
+
+ TileEntity handler = BlockHelper.getAdjacentTileEntity(tile, side);
+
+ return handler instanceof IFluidHandler ? ((IFluidHandler) handler).drain(ForgeDirection.VALID_DIRECTIONS[side ^ 1], maxDrain, doDrain) : null;
+ }
+
+ public static int insertFluidIntoAdjacentFluidHandler(TileEntity tile, int side, FluidStack fluid, boolean doFill) {
+
+ TileEntity handler = BlockHelper.getAdjacentTileEntity(tile, side);
+
+ return handler instanceof IFluidHandler ? ((IFluidHandler) handler).fill(ForgeDirection.VALID_DIRECTIONS[side ^ 1], fluid, doFill) : 0;
+ }
+
+ // TODO: Replace with sided version post-1.8 Fluid revamp
+ public static boolean isAdjacentFluidHandler(TileEntity tile, int side) {
+
+ return BlockHelper.getAdjacentTileEntity(tile, side) instanceof IFluidHandler;
+ }
+
+ // TODO: Replace with sided version post-1.8 Fluid revamp
+ public static boolean isFluidHandler(TileEntity tile) {
+
+ return tile instanceof IFluidHandler;
+ }
+
+ /* Fluid Container Registry Interaction */
+ public static boolean fillContainerFromHandler(World world, IFluidHandler handler, EntityPlayer player, FluidStack tankFluid) {
+
+ ItemStack container = player.getCurrentEquippedItem();
+
+ if (FluidContainerRegistry.isEmptyContainer(container)) {
+ ItemStack returnStack = FluidContainerRegistry.fillFluidContainer(tankFluid, container);
+ FluidStack fluid = FluidContainerRegistry.getFluidForFilledItem(returnStack);
+
+ if (fluid == null || returnStack == null) {
+ return false;
+ }
+ if (ServerHelper.isClientWorld(world)) {
+ return true;
+ }
+ if (!player.capabilities.isCreativeMode) {
+ if (container.stackSize == 1) {
+ player.inventory.setInventorySlotContents(player.inventory.currentItem, returnStack);
+ container.stackSize--;
+ if (container.stackSize <= 0) {
+ container = null;
+ }
+ } else {
+ if (ItemHelper.disposePlayerItem(player.getCurrentEquippedItem(), returnStack, player, true)) {
+ player.openContainer.detectAndSendChanges();
+ ((EntityPlayerMP) player).sendContainerAndContentsToPlayer(player.openContainer, player.openContainer.getInventory());
+ }
+ }
+ }
+ handler.drain(ForgeDirection.UNKNOWN, fluid.amount, true);
+ return true;
+ }
+ return false;
+ }
+
+ public static boolean fillHandlerWithContainer(World world, IFluidHandler handler, EntityPlayer player) {
+
+ ItemStack container = player.getCurrentEquippedItem();
+ FluidStack fluid = FluidContainerRegistry.getFluidForFilledItem(container);
+
+ if (fluid != null) {
+ if (handler.fill(ForgeDirection.UNKNOWN, fluid, false) == fluid.amount || player.capabilities.isCreativeMode) {
+ ItemStack returnStack = FluidContainerRegistry.drainFluidContainer(container);
+ if (ServerHelper.isClientWorld(world)) {
+ return true;
+ }
+ if (!player.capabilities.isCreativeMode) {
+ if (ItemHelper.disposePlayerItem(player.getCurrentEquippedItem(), returnStack, player, true)) {
+ if (ServerHelper.isServerWorld(world)) {
+ player.openContainer.detectAndSendChanges();
+ ((EntityPlayerMP) player).sendContainerAndContentsToPlayer(player.openContainer, player.openContainer.getInventory());
+ }
+ }
+ }
+ handler.fill(ForgeDirection.UNKNOWN, fluid, true);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /* PACKETS */
+ public static void writeFluidStackToPacket(FluidStack fluid, DataOutput data) throws IOException {
+
+ if (!isValidFluidStack(fluid)) {
+ data.writeShort(-1);
+ } else {
+ byte[] abyte = CompressedStreamTools.compress(fluid.writeToNBT(new NBTTagCompound()));
+ data.writeShort((short) abyte.length);
+ data.write(abyte);
+ }
+ }
+
+ public static FluidStack readFluidStackFromPacket(DataInput data) throws IOException {
+
+ short length = data.readShort();
+
+ if (length < 0) {
+ return null;
+ } else {
+ byte[] abyte = new byte[length];
+ data.readFully(abyte);
+ return FluidStack.loadFluidStackFromNBT(CompressedStreamTools.func_152457_a(abyte, new NBTSizeTracker(2097152L)));
+ }
+ }
+
+ /* HELPERS */
+ public static boolean isValidFluidStack(FluidStack fluid) {
+
+ return fluid == null ? false : FluidRegistry.getFluidName(fluid) != null;
+ }
+
+ public static int getFluidLuminosity(FluidStack fluid) {
+
+ return fluid == null ? 0 : getFluidLuminosity(fluid.getFluid());
+ }
+
+ public static int getFluidLuminosity(Fluid fluid) {
+
+ return fluid == null ? 0 : fluid.getLuminosity();
+ }
+
+ public static FluidStack getFluidFromWorld(World world, int x, int y, int z, boolean doDrain) {
+
+ Block bId = world.getBlock(x, y, z);
+ int bMeta = world.getBlockMetadata(x, y, z);
+
+ if (Block.isEqualTo(bId, Blocks.water)) {
+ if (bMeta == 0) {
+ return WATER.copy();
+ } else {
+ return null;
+ }
+ } else if (Block.isEqualTo(bId, Blocks.lava) || Block.isEqualTo(bId, Blocks.flowing_lava)) {
+ if (bMeta == 0) {
+ return LAVA.copy();
+ } else {
+ return null;
+ }
+ } else if (bId instanceof IFluidBlock) {
+ IFluidBlock block = (IFluidBlock) bId;
+ return block.drain(world, x, y, z, doDrain);
+ }
+ return null;
+ }
+
+ public static FluidStack getFluidFromWorld(World world, int x, int y, int z) {
+
+ return getFluidFromWorld(world, x, y, z, false);
+ }
+
+ public static Fluid lookupFluidForBlock(Block block) {
+
+ if (block == Blocks.flowing_water) {
+ return WATER_FLUID;
+ }
+ if (block == Blocks.flowing_lava) {
+ return LAVA_FLUID;
+ }
+ return FluidRegistry.lookupFluidForBlock(block);
+ }
+
+ public static FluidStack getFluidForFilledItem(ItemStack container) {
+
+ if (container != null && container.getItem() instanceof IFluidContainerItem) {
+ return ((IFluidContainerItem) container.getItem()).getFluid(container);
+ }
+ return FluidContainerRegistry.getFluidForFilledItem(container);
+ }
+
+ public static boolean isFluidEqualOrNull(FluidStack resourceA, FluidStack resourceB) {
+
+ return resourceA == null || resourceB == null || resourceA.isFluidEqual(resourceB);
+ }
+
+ public static boolean isFluidEqualOrNull(Fluid fluidA, FluidStack resourceB) {
+
+ return fluidA == null || resourceB == null || fluidA == resourceB.getFluid();
+ }
+
+ public static boolean isFluidEqualOrNull(Fluid fluidA, Fluid fluidB) {
+
+ return fluidA == null || fluidB == null || fluidA == fluidB;
+ }
+
+ public static boolean isFluidEqual(FluidStack resourceA, FluidStack resourceB) {
+
+ return resourceA != null && resourceA.isFluidEqual(resourceB);
+ }
+
+ public static boolean isFluidEqual(Fluid fluidA, FluidStack resourceB) {
+
+ return fluidA != null && resourceB != null && fluidA == resourceB.getFluid();
+ }
+
+ public static boolean isFluidEqual(Fluid fluidA, Fluid fluidB) {
+
+ return fluidA != null && fluidB != null && fluidA.equals(fluidB);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/HolidayHelper.java b/src/main/java/cofh/lib/util/helpers/HolidayHelper.java
new file mode 100644
index 00000000..22568507
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/HolidayHelper.java
@@ -0,0 +1,206 @@
+package cofh.lib.util.helpers;
+
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+/**
+ * The class contains helper functions related to Holidays! The holidays intentionally begin a day before the actual holiday and end one day after it.
+ *
+ * Yes, they are US-centric. Feel free to suggest others!
+ *
+ * @author King Lemming
+ *
+ */
+public class HolidayHelper {
+
+ private HolidayHelper() {
+
+ }
+
+ static Calendar curTime = Calendar.getInstance();
+
+ static Calendar holidayStart = Calendar.getInstance();
+ static Calendar holidayEnd = Calendar.getInstance();
+
+ public static boolean isNewYear() {
+
+ setDate(holidayStart, Calendar.DECEMBER, 31, false);
+ setDate(holidayEnd, Calendar.JANUARY, 2, true);
+ holidayEnd.set(Calendar.YEAR, Calendar.YEAR + 1);
+
+ return dateCheck();
+ }
+
+ public static boolean isValentinesDay() {
+
+ setDate(holidayStart, Calendar.FEBRUARY, 13, false);
+ setDate(holidayEnd, Calendar.FEBRUARY, 15, true);
+
+ return dateCheck();
+ }
+
+ public static boolean isStPatricksDay() {
+
+ setDate(holidayStart, Calendar.MARCH, 16, false);
+ setDate(holidayEnd, Calendar.MARCH, 18, true);
+
+ return dateCheck();
+ }
+
+ public static boolean isAprilFools() {
+
+ setDate(holidayStart, Calendar.MARCH, 31, false);
+ setDate(holidayEnd, Calendar.APRIL, 2, true);
+
+ return dateCheck();
+ }
+
+ public static boolean isEarthDay() {
+
+ setDate(holidayStart, Calendar.APRIL, 21, false);
+ setDate(holidayEnd, Calendar.APRIL, 23, true);
+
+ return dateCheck();
+ }
+
+ public static boolean isEaster() {
+
+ /**
+ * Compute the day of the year that Easter falls on. Step names E1 E2 etc., are direct references to Knuth, Vol 1, p 155.
+ *
+ * http://en.wikipedia.org/wiki/Computus#Meeus.2FJones.2FButcher_Gregorian_algorithm
+ */
+ Calendar easterSunCal;
+
+ int year = Calendar.getInstance().get(Calendar.YEAR);
+
+ if (year <= 1582) {
+ return false; // The calculation is based on Gregorian calendar and it's incorrect before 1582
+ }
+ int golden, century, x, z, d, epact, n;
+
+ golden = (year % 19) + 1; // metonic cycle
+ century = (year / 100) + 1; // Centuries are shifted by one e.g. 1984 was in 20th C
+ x = (3 * century / 4) - 12; // leap year correction
+ z = ((8 * century + 5) / 25) - 5; // syncing with moon's orbit
+
+ d = (5 * year / 4) - x - 10;
+ epact = (11 * golden + 20 + z - x) % 30; /* epact */
+
+ if ((epact == 25 && golden > 11) || epact == 24) {
+ epact++;
+ }
+ n = 44 - epact;
+ n += 30 * (n < 21 ? 1 : 0);
+ n += 7 - ((d + n) % 7);
+
+ if (n > 31) {
+ easterSunCal = new GregorianCalendar(year, 4 - 1, n - 31); // if April
+ } else {
+ easterSunCal = new GregorianCalendar(year, 3 - 1, n); // if March
+ }
+ setDate(holidayStart, easterSunCal.get(Calendar.MONTH), easterSunCal.get(Calendar.DAY_OF_MONTH) - 1, false);
+ setDate(holidayEnd, easterSunCal.get(Calendar.MONTH), easterSunCal.get(Calendar.DAY_OF_MONTH) + 1, true);
+
+ return dateCheck();
+ }
+
+ public static boolean isUSIndependenceDay() {
+
+ setDate(holidayStart, Calendar.JULY, 3, false);
+ setDate(holidayEnd, Calendar.JULY, 5, true);
+
+ return dateCheck();
+ }
+
+ public static boolean isHalloween() {
+
+ setDate(holidayStart, Calendar.OCTOBER, 30, false);
+ setDate(holidayEnd, Calendar.NOVEMBER, 1, true);
+
+ return dateCheck();
+ }
+
+ public static boolean isVeteransDay() {
+
+ setDate(holidayStart, Calendar.NOVEMBER, 10, false);
+ setDate(holidayEnd, Calendar.NOVEMBER, 12, true);
+
+ return dateCheck();
+ }
+
+ public static boolean isCAThanksgiving() {
+
+ Calendar thanksgivingCal = Calendar.getInstance();
+ thanksgivingCal.clear();
+ thanksgivingCal.set(Calendar.YEAR, Calendar.getInstance().get(Calendar.YEAR));
+ thanksgivingCal.set(Calendar.MONTH, Calendar.OCTOBER);
+ thanksgivingCal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
+ thanksgivingCal.set(Calendar.DAY_OF_WEEK_IN_MONTH, 2);
+
+ setDate(holidayStart, Calendar.OCTOBER, thanksgivingCal.get(Calendar.DAY_OF_MONTH) - 1, false);
+ setDate(holidayEnd, Calendar.OCTOBER, thanksgivingCal.get(Calendar.DAY_OF_MONTH) + 1, true);
+
+ return dateCheck();
+ }
+
+ public static boolean isUSThanksgiving() {
+
+ Calendar thanksgivingCal = Calendar.getInstance();
+ thanksgivingCal.clear();
+ thanksgivingCal.set(Calendar.YEAR, Calendar.getInstance().get(Calendar.YEAR));
+ thanksgivingCal.set(Calendar.MONTH, Calendar.NOVEMBER);
+ thanksgivingCal.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY);
+ thanksgivingCal.set(Calendar.DAY_OF_WEEK_IN_MONTH, 4);
+
+ setDate(holidayStart, Calendar.NOVEMBER, thanksgivingCal.get(Calendar.DAY_OF_MONTH) - 1, false);
+ setDate(holidayEnd, Calendar.NOVEMBER, thanksgivingCal.get(Calendar.DAY_OF_MONTH) + 1, true);
+
+ return dateCheck();
+ }
+
+ public static boolean isChristmas() {
+
+ setDate(holidayStart, Calendar.DECEMBER, 24, false);
+ setDate(holidayEnd, Calendar.DECEMBER, 26, true);
+
+ return dateCheck();
+ }
+
+ public static boolean isBoxingDay() {
+
+ setDate(holidayStart, Calendar.DECEMBER, 25, false);
+ setDate(holidayEnd, Calendar.DECEMBER, 27, true);
+
+ return dateCheck();
+ }
+
+ /* HELPER FUNCTIONS */
+ static void setDate(Calendar cal, int month, int date, boolean endOfDay) {
+
+ cal.clear();
+
+ cal.set(Calendar.YEAR, Calendar.getInstance().get(Calendar.YEAR));
+ cal.set(Calendar.MONTH, month);
+ cal.set(Calendar.DATE, date);
+
+ if (endOfDay) {
+ cal.set(Calendar.HOUR_OF_DAY, 23);
+ cal.set(Calendar.MINUTE, 59);
+ cal.set(Calendar.SECOND, 59);
+ cal.set(Calendar.MILLISECOND, 999);
+ } else {
+ cal.set(Calendar.HOUR_OF_DAY, 0);
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ cal.set(Calendar.MILLISECOND, 0);
+ }
+ }
+
+ static boolean dateCheck() {
+
+ curTime = Calendar.getInstance();
+ return curTime.after(holidayStart) && curTime.before(holidayEnd);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/InventoryHelper.java b/src/main/java/cofh/lib/util/helpers/InventoryHelper.java
new file mode 100644
index 00000000..7b457ae2
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/InventoryHelper.java
@@ -0,0 +1,396 @@
+package cofh.lib.util.helpers;
+
+import cofh.api.transport.IItemDuct;
+
+import java.util.List;
+
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.ISidedInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraftforge.common.util.ForgeDirection;
+
+/**
+ * This class contains helper functions related to Inventories and Inventory manipulation.
+ *
+ * @author King Lemming
+ *
+ */
+public class InventoryHelper {
+
+ private InventoryHelper() {
+
+ }
+
+ /**
+ * Copy an entire inventory. Best to avoid doing this often.
+ */
+ public static ItemStack[] cloneInventory(ItemStack[] inventory) {
+
+ ItemStack[] inventoryCopy = new ItemStack[inventory.length];
+ for (int i = 0; i < inventory.length; i++) {
+ inventoryCopy[i] = inventory[i] == null ? null : inventory[i].copy();
+ }
+ return inventoryCopy;
+ }
+
+ /**
+ * Add an ItemStack to an inventory. Return true if the entire stack was added.
+ *
+ * @param inventory
+ * The inventory.
+ * @param stack
+ * ItemStack to add.
+ * @param startIndex
+ * First slot to attempt to add into. Does not loop around fully.
+ * @param endIndex
+ * Final slot to attempt to add into. Should be at most length - 1
+ */
+ public static boolean addItemStackToInventory(ItemStack[] inventory, ItemStack stack, int startIndex, int endIndex) {
+
+ if (stack == null) {
+ return true;
+ }
+ int openSlot = -1;
+ for (int i = startIndex; i <= endIndex; i++) {
+ if (ItemHelper.itemsEqualForCrafting(stack, inventory[i]) && inventory[i].getMaxStackSize() > inventory[i].stackSize) {
+ int hold = inventory[i].getMaxStackSize() - inventory[i].stackSize;
+ if (hold >= stack.stackSize) {
+ inventory[i].stackSize += stack.stackSize;
+ stack = null;
+ return true;
+ } else {
+ stack.stackSize -= hold;
+ inventory[i].stackSize += hold;
+ }
+ } else if (inventory[i] == null && openSlot == -1) {
+ openSlot = i;
+ }
+ }
+ if (openSlot > -1) {
+ inventory[openSlot] = stack;
+ } else {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Shortcut method for above, assumes ending slot is length - 1
+ */
+ public static boolean addItemStackToInventory(ItemStack[] inventory, ItemStack stack, int startIndex) {
+
+ return addItemStackToInventory(inventory, stack, startIndex, inventory.length - 1);
+ }
+
+ /**
+ * Shortcut method for above, assumes starting slot is 0.
+ */
+ public static boolean addItemStackToInventory(ItemStack[] inventory, ItemStack stack) {
+
+ return addItemStackToInventory(inventory, stack, 0);
+ }
+
+ /* IInventoryHandler Interaction */
+
+ // IInventoryHandler is not currently implemented or used. Possibly in the future.
+
+ /* IInventory Interaction */
+ public static ItemStack extractItemStackFromInventory(IInventory inventory, int side) {
+
+ if (inventory == null) {
+ return null;
+ }
+ ItemStack retStack = null;
+
+ if (inventory instanceof ISidedInventory) {
+ ISidedInventory sidedInv = (ISidedInventory) inventory;
+ int slots[] = sidedInv.getAccessibleSlotsFromSide(side);
+ for (int i = 0; i < slots.length && retStack == null; i++) {
+ if (sidedInv.getStackInSlot(i) != null && sidedInv.canExtractItem(i, sidedInv.getStackInSlot(i), side)) {
+ retStack = sidedInv.getStackInSlot(i).copy();
+ sidedInv.setInventorySlotContents(i, null);
+ }
+ }
+ } else {
+ for (int i = 0; i < inventory.getSizeInventory() && retStack == null; i++) {
+ if (inventory.getStackInSlot(i) != null) {
+ retStack = inventory.getStackInSlot(i).copy();
+ inventory.setInventorySlotContents(i, null);
+ }
+ }
+ }
+ if (retStack != null) {
+ inventory.markDirty();
+ }
+ return retStack;
+ }
+
+ public static ItemStack insertItemStackIntoInventory(IInventory inventory, ItemStack stack, int side) {
+
+ if (stack == null || inventory == null) {
+ return null;
+ }
+ int stackSize = stack.stackSize;
+
+ if (inventory instanceof ISidedInventory) {
+ ISidedInventory sidedInv = (ISidedInventory) inventory;
+ int slots[] = sidedInv.getAccessibleSlotsFromSide(side);
+
+ if (slots == null) {
+ return stack;
+ }
+ for (int i = 0; i < slots.length && stack != null; i++) {
+ if (sidedInv.canInsertItem(slots[i], stack, side)) {
+ ItemStack existingStack = inventory.getStackInSlot(slots[i]);
+ if (ItemHelper.itemsEqualWithMetadata(stack, existingStack, true)) {
+ stack = addToOccupiedInventorySlot(sidedInv, slots[i], stack, existingStack);
+ }
+ }
+ }
+ for (int i = 0; i < slots.length && stack != null; i++) {
+ if (inventory.getStackInSlot(slots[i]) == null && sidedInv.canInsertItem(slots[i], stack, side)) {
+ stack = addToEmptyInventorySlot(sidedInv, slots[i], stack);
+ }
+ }
+ } else {
+ int invSize = inventory.getSizeInventory();
+ for (int i = 0; i < invSize && stack != null; i++) {
+ ItemStack existingStack = inventory.getStackInSlot(i);
+ if (ItemHelper.itemsEqualWithMetadata(stack, existingStack, true)) {
+ stack = addToOccupiedInventorySlot(inventory, i, stack, existingStack);
+ }
+ }
+ for (int i = 0; i < invSize && stack != null; i++) {
+ if (inventory.getStackInSlot(i) == null) {
+ stack = addToEmptyInventorySlot(inventory, i, stack);
+ }
+ }
+ }
+ if (stack == null || stack.stackSize != stackSize) {
+ inventory.markDirty();
+ }
+ return stack;
+ }
+
+ public static ItemStack simulateInsertItemStackIntoInventory(IInventory inventory, ItemStack stack, int side) {
+
+ if (stack == null || inventory == null) {
+ return null;
+ }
+ if (inventory instanceof ISidedInventory) {
+ ISidedInventory sidedInv = (ISidedInventory) inventory;
+ int slots[] = sidedInv.getAccessibleSlotsFromSide(side);
+
+ if (slots == null) {
+ return stack;
+ }
+ for (int i = 0; i < slots.length && stack != null; i++) {
+ if (sidedInv.canInsertItem(slots[i], stack, side)) {
+ ItemStack existingStack = inventory.getStackInSlot(slots[i]);
+ if (ItemHelper.itemsEqualWithMetadata(stack, existingStack, true)) {
+ stack = simulateAddToOccupiedInventorySlot(sidedInv, slots[i], stack, existingStack);
+ }
+ }
+ }
+ for (int i = 0; i < slots.length && stack != null; i++) {
+ if (inventory.getStackInSlot(slots[i]) == null && sidedInv.canInsertItem(slots[i], stack, side)) {
+ stack = simulateAddToEmptyInventorySlot(sidedInv, slots[i], stack);
+ }
+ }
+ } else {
+ int invSize = inventory.getSizeInventory();
+ for (int i = 0; i < invSize && stack != null; i++) {
+ ItemStack existingStack = inventory.getStackInSlot(i);
+ if (ItemHelper.itemsEqualWithMetadata(stack, existingStack, true)) {
+ stack = simulateAddToOccupiedInventorySlot(inventory, i, stack, existingStack);
+ }
+ }
+ for (int i = 0; i < invSize && stack != null; i++) {
+ if (inventory.getStackInSlot(i) == null) {
+ stack = simulateAddToEmptyInventorySlot(inventory, i, stack);
+ }
+ }
+ }
+ return stack;
+ }
+
+ /* Slot Interaction */
+ public static ItemStack addToEmptyInventorySlot(IInventory inventory, int slot, ItemStack stack) {
+
+ if (!inventory.isItemValidForSlot(slot, stack)) {
+ return stack;
+ }
+ int stackLimit = inventory.getInventoryStackLimit();
+ inventory.setInventorySlotContents(slot, ItemHelper.cloneStack(stack, Math.min(stack.stackSize, stackLimit)));
+ return stackLimit >= stack.stackSize ? null : stack.splitStack(stack.stackSize - stackLimit);
+ }
+
+ public static ItemStack addToOccupiedInventorySlot(IInventory inventory, int slot, ItemStack stack) {
+
+ int stackLimit = Math.min(inventory.getInventoryStackLimit(), stack.getMaxStackSize());
+ ItemStack stackInSlot = inventory.getStackInSlot(slot);
+
+ if (stack.stackSize + stackInSlot.stackSize > stackLimit) {
+ int stackDiff = stackLimit - stackInSlot.stackSize;
+ stackInSlot.stackSize = stackLimit;
+ stack.stackSize -= stackDiff;
+ inventory.setInventorySlotContents(slot, stackInSlot);
+ return stack;
+ }
+ stackInSlot.stackSize += Math.min(stack.stackSize, stackLimit);
+ inventory.setInventorySlotContents(slot, stackInSlot);
+ return stackLimit >= stack.stackSize ? null : stack.splitStack(stack.stackSize - stackLimit);
+ }
+
+ public static ItemStack addToOccupiedInventorySlot(IInventory inventory, int slot, ItemStack stack, ItemStack existingStack) {
+
+ int stackLimit = Math.min(inventory.getInventoryStackLimit(), stack.getMaxStackSize());
+
+ if (existingStack.stackSize >= stackLimit) {
+ return stack;
+ }
+ if (stack.stackSize + existingStack.stackSize > stackLimit) {
+ int stackDiff = stackLimit - existingStack.stackSize;
+ existingStack.stackSize = stackLimit;
+ stack.stackSize -= stackDiff;
+ inventory.setInventorySlotContents(slot, existingStack);
+ return stack;
+ }
+ existingStack.stackSize += stack.stackSize;
+ inventory.setInventorySlotContents(slot, existingStack);
+ return stackLimit >= stack.stackSize ? null : stack.splitStack(stack.stackSize - stackLimit);
+ }
+
+ public static ItemStack simulateAddToEmptyInventorySlot(IInventory inventory, int slot, ItemStack stack) {
+
+ if (!inventory.isItemValidForSlot(slot, stack)) {
+ return stack;
+ }
+ int stackLimit = Math.min(inventory.getInventoryStackLimit(), stack.getMaxStackSize());
+ return stackLimit >= stack.stackSize ? null : stack.splitStack(stack.stackSize - stackLimit);
+ }
+
+ public static ItemStack simulateAddToOccupiedInventorySlot(IInventory inventory, int slot, ItemStack stack) {
+
+ int stackLimit = Math.min(inventory.getInventoryStackLimit(), stack.getMaxStackSize());
+ ItemStack stackInSlot = inventory.getStackInSlot(slot);
+
+ if (stack.stackSize + stackInSlot.stackSize > stackLimit) {
+ stack.stackSize -= stackLimit - stackInSlot.stackSize;
+ return stack;
+ }
+ return stackLimit >= stack.stackSize ? null : stack.splitStack(stack.stackSize - stackLimit);
+ }
+
+ public static ItemStack simulateAddToOccupiedInventorySlot(IInventory inventory, int slot, ItemStack stack, ItemStack existingStack) {
+
+ int stackLimit = Math.min(inventory.getInventoryStackLimit(), stack.getMaxStackSize());
+
+ if (existingStack.stackSize >= stackLimit) {
+ return stack;
+ }
+ if (stack.stackSize + existingStack.stackSize > stackLimit) {
+ stack.stackSize -= stackLimit - existingStack.stackSize;
+ return stack;
+ }
+ return stackLimit >= stack.stackSize ? null : stack.splitStack(stack.stackSize - stackLimit);
+ }
+
+ public static boolean mergeItemStack(List slots, ItemStack stack, int start, int length, boolean reverse) {
+
+ return mergeItemStack(slots, stack, start, length, reverse, true);
+ }
+
+ public static boolean mergeItemStack(List slots, ItemStack stack, int start, int length, boolean r, boolean limit) {
+
+ boolean successful = false;
+ int i = !r ? start : length - 1;
+ int iterOrder = !r ? 1 : -1;
+
+ Slot slot;
+ ItemStack existingStack;
+
+ if (stack.isStackable()) {
+ while (stack.stackSize > 0 && (!r && i < length || r && i >= start)) {
+ slot = slots.get(i);
+ existingStack = slot.getStack();
+
+ if (existingStack != null) {
+ int maxStack = Math.min(stack.getMaxStackSize(), slot.getSlotStackLimit());
+ int rmv = Math.min(maxStack, stack.stackSize);
+
+ if (slot.isItemValid(ItemHelper.cloneStack(stack, rmv)) && existingStack.getItem().equals(stack.getItem())
+ && (!stack.getHasSubtypes() || stack.getItemDamage() == existingStack.getItemDamage())
+ && ItemStack.areItemStackTagsEqual(stack, existingStack)) {
+ int existingSize = existingStack.stackSize + stack.stackSize;
+
+ if (existingSize <= maxStack) {
+ stack.stackSize = 0;
+ existingStack.stackSize = existingSize;
+ slot.putStack(existingStack);
+ successful = true;
+ } else if (existingStack.stackSize < maxStack) {
+ stack.stackSize -= maxStack - existingStack.stackSize;
+ existingStack.stackSize = maxStack;
+ slot.putStack(existingStack);
+ successful = true;
+ }
+ }
+ }
+
+ i += iterOrder;
+ }
+ }
+
+ if (stack.stackSize > 0) {
+ i = !r ? start : length - 1;
+
+ while (stack.stackSize > 0 && (!r && i < length || r && i >= start)) {
+ slot = slots.get(i);
+ existingStack = slot.getStack();
+
+ if (existingStack == null) {
+ int maxStack = Math.min(stack.getMaxStackSize(), slot.getSlotStackLimit());
+ int rmv = Math.min(maxStack, stack.stackSize);
+
+ if (slot.isItemValid(ItemHelper.cloneStack(stack, rmv))) {
+ existingStack = stack.splitStack(rmv);
+ slot.putStack(existingStack);
+ successful = true;
+ }
+ }
+
+ i += iterOrder;
+ }
+ }
+
+ return successful;
+ }
+
+ /* HELPERS */
+ public static ItemStack addToInsertion(Object tile, int side, ItemStack stack) {
+
+ if (stack == null) {
+ return null;
+ }
+ if (tile instanceof IInventory) {
+ stack = insertItemStackIntoInventory((IInventory) tile, stack, BlockHelper.SIDE_OPPOSITE[side]);
+ } else {
+ stack = ((IItemDuct) tile).insertItem(ForgeDirection.VALID_DIRECTIONS[side ^ 1], stack);
+ }
+ return stack;
+ }
+
+ public static boolean isInventory(TileEntity tile) {
+
+ return tile instanceof IInventory;
+ }
+
+ public static boolean isInsertion(Object tile) {
+
+ return tile instanceof IInventory || tile instanceof IItemDuct;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/ItemHelper.java b/src/main/java/cofh/lib/util/helpers/ItemHelper.java
new file mode 100644
index 00000000..61fcac5f
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/ItemHelper.java
@@ -0,0 +1,1265 @@
+package cofh.lib.util.helpers;
+
+import static net.minecraftforge.oredict.OreDictionary.WILDCARD_VALUE;
+
+import cofh.api.item.IEmpowerableItem;
+import cofh.api.item.IInventoryContainerItem;
+import cofh.api.item.IMultiModeItem;
+import cofh.lib.util.OreDictionaryProxy;
+import com.google.common.base.Strings;
+import cpw.mods.fml.common.event.FMLInterModComms;
+import cpw.mods.fml.common.registry.GameRegistry;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.Blocks;
+import net.minecraft.init.Items;
+import net.minecraft.inventory.InventoryCrafting;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.item.crafting.CraftingManager;
+import net.minecraft.item.crafting.FurnaceRecipes;
+import net.minecraft.item.crafting.IRecipe;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagList;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.Constants;
+import net.minecraftforge.fluids.FluidContainerRegistry;
+import net.minecraftforge.oredict.OreDictionary;
+import net.minecraftforge.oredict.ShapedOreRecipe;
+import net.minecraftforge.oredict.ShapelessOreRecipe;
+
+/**
+ * Contains various helper functions to assist with {@link Item} and {@link ItemStack} manipulation and interaction.
+ *
+ * @author King Lemming
+ *
+ */
+public final class ItemHelper {
+
+ public static final String BLOCK = "block";
+ public static final String ORE = "ore";
+ public static final String DUST = "dust";
+ public static final String INGOT = "ingot";
+ public static final String NUGGET = "nugget";
+ public static final String LOG = "log";
+
+ public static OreDictionaryProxy oreProxy = new OreDictionaryProxy();
+
+ private ItemHelper() {
+
+ }
+
+ public static ItemStack cloneStack(Item item, int stackSize) {
+
+ if (item == null) {
+ return null;
+ }
+ ItemStack stack = new ItemStack(item, stackSize);
+
+ return stack;
+ }
+
+ public static ItemStack cloneStack(Block item, int stackSize) {
+
+ if (item == null) {
+ return null;
+ }
+ ItemStack stack = new ItemStack(item, stackSize);
+
+ return stack;
+ }
+
+ public static ItemStack cloneStack(ItemStack stack, int stackSize) {
+
+ if (stack == null) {
+ return null;
+ }
+ ItemStack retStack = stack.copy();
+ retStack.stackSize = stackSize;
+
+ return retStack;
+ }
+
+ public static ItemStack cloneStack(ItemStack stack) {
+
+ if (stack == null) {
+ return null;
+ }
+ ItemStack retStack = stack.copy();
+
+ return retStack;
+ }
+
+ public static ItemStack copyTag(ItemStack container, ItemStack other) {
+
+ if (other != null && other.stackTagCompound != null) {
+ container.stackTagCompound = (NBTTagCompound) other.stackTagCompound.copy();
+ }
+ return container;
+ }
+
+ public static NBTTagCompound setItemStackTagName(NBTTagCompound tag, String name) {
+
+ if (Strings.isNullOrEmpty(name)) {
+ return null;
+ }
+ if (tag == null) {
+ tag = new NBTTagCompound();
+ }
+ if (!tag.hasKey("display")) {
+ tag.setTag("display", new NBTTagCompound());
+ }
+ tag.getCompoundTag("display").setString("Name", name);
+
+ return tag;
+ }
+
+ public static ItemStack readItemStackFromNBT(NBTTagCompound nbt) {
+
+ ItemStack stack = new ItemStack(Item.getItemById(nbt.getShort("id")));
+ stack.stackSize = nbt.getInteger("Count");
+ stack.setItemDamage(Math.max(0, nbt.getShort("Damage")));
+
+ if (nbt.hasKey("tag", 10)) {
+ stack.stackTagCompound = nbt.getCompoundTag("tag");
+ }
+ return stack;
+ }
+
+ public static NBTTagCompound writeItemStackToNBT(ItemStack stack, NBTTagCompound nbt) {
+
+ nbt.setShort("id", (short) Item.getIdFromItem(stack.getItem()));
+ nbt.setInteger("Count", stack.stackSize);
+ nbt.setShort("Damage", (short) getItemDamage(stack));
+
+ if (stack.stackTagCompound != null) {
+ nbt.setTag("tag", stack.stackTagCompound);
+ }
+ return nbt;
+ }
+
+ public static NBTTagCompound writeItemStackToNBT(ItemStack stack, int amount, NBTTagCompound nbt) {
+
+ nbt.setShort("id", (short) Item.getIdFromItem(stack.getItem()));
+ nbt.setInteger("Count", amount);
+ nbt.setShort("Damage", (short) getItemDamage(stack));
+
+ if (stack.stackTagCompound != null) {
+ nbt.setTag("tag", stack.stackTagCompound);
+ }
+ return nbt;
+ }
+
+ public static String getNameFromItemStack(ItemStack stack) {
+
+ if (stack == null || stack.stackTagCompound == null || !stack.stackTagCompound.hasKey("display")) {
+ return "";
+ }
+ return stack.stackTagCompound.getCompoundTag("display").getString("Name");
+ }
+
+ public static ItemStack damageItem(ItemStack stack, int amt, Random rand) {
+
+ if (stack != null && stack.isItemStackDamageable() && stack.attemptDamageItem(amt, rand)) {
+ if (--stack.stackSize <= 0) {
+ stack = null;
+ } else {
+ stack.setItemDamage(0);
+ }
+ }
+
+ return stack;
+ }
+
+ public static ItemStack consumeItem(ItemStack stack) {
+
+ if (stack == null) {
+ return null;
+ }
+
+ Item item = stack.getItem();
+ boolean largerStack = stack.stackSize > 1;
+ // vanilla only alters the stack passed to hasContainerItem/etc. when the size is >1
+
+ if (largerStack) {
+ stack.stackSize -= 1;
+ }
+ if (item.hasContainerItem(stack)) {
+ ItemStack ret = item.getContainerItem(stack);
+
+ if (ret == null) {
+ return null;
+ }
+ if (ret.isItemStackDamageable() && ret.getItemDamage() > ret.getMaxDamage()) {
+ ret = null;
+ }
+ return ret;
+ }
+
+ return largerStack ? stack : null;
+ }
+
+ public static ItemStack consumeItem(ItemStack stack, EntityPlayer player) {
+
+ if (stack == null) {
+ return null;
+ }
+
+ Item item = stack.getItem();
+ boolean largerStack = stack.stackSize > 1;
+ // vanilla only alters the stack passed to hasContainerItem/etc. when the size is >1
+
+ if (largerStack) {
+ stack.stackSize -= 1;
+ }
+ if (item.hasContainerItem(stack)) {
+ ItemStack ret = item.getContainerItem(stack);
+
+ if (ret == null || (ret.isItemStackDamageable() && ret.getItemDamage() > ret.getMaxDamage())) {
+ ret = null;
+ }
+ if (stack.stackSize < 1) {
+ return ret;
+ }
+ if (ret != null && !player.inventory.addItemStackToInventory(ret)) {
+ player.func_146097_a(ret, false, true);
+ }
+ }
+
+ return largerStack ? stack : null;
+ }
+
+ public static boolean disposePlayerItem(ItemStack stack, ItemStack dropStack, EntityPlayer entityplayer, boolean allowDrop) {
+
+ return disposePlayerItem(stack, dropStack, entityplayer, allowDrop, true);
+ }
+
+ public static boolean disposePlayerItem(ItemStack stack, ItemStack dropStack, EntityPlayer entityplayer, boolean allowDrop, boolean allowReplace) {
+
+ if (entityplayer == null || entityplayer.capabilities.isCreativeMode) {
+ return true;
+ }
+ if (allowReplace && stack.stackSize <= 1) {
+ entityplayer.inventory.setInventorySlotContents(entityplayer.inventory.currentItem, null);
+ entityplayer.inventory.addItemStackToInventory(dropStack);
+ return true;
+ } else if (allowDrop) {
+ stack.stackSize -= 1;
+ if (dropStack != null && !entityplayer.inventory.addItemStackToInventory(dropStack)) {
+ entityplayer.func_146097_a(dropStack, false, true);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This prevents an overridden getDamage() call from messing up metadata acquisition.
+ */
+ public static int getItemDamage(ItemStack stack) {
+
+ return Items.diamond.getDamage(stack);
+ }
+
+ /**
+ * Gets a vanilla CraftingManager result.
+ */
+ public static ItemStack findMatchingRecipe(InventoryCrafting inv, World world) {
+
+ ItemStack[] dmgItems = new ItemStack[2];
+ for (int i = 0; i < inv.getSizeInventory(); i++) {
+ if (inv.getStackInSlot(i) != null) {
+ if (dmgItems[0] == null) {
+ dmgItems[0] = inv.getStackInSlot(i);
+ } else {
+ dmgItems[1] = inv.getStackInSlot(i);
+ break;
+ }
+ }
+ }
+ if (dmgItems[0] == null || dmgItems[0].getItem() == null) {
+ return null;
+ } else if (dmgItems[1] != null && dmgItems[0].getItem() == dmgItems[1].getItem() && dmgItems[0].stackSize == 1 && dmgItems[1].stackSize == 1
+ && dmgItems[0].getItem().isRepairable()) {
+ Item theItem = dmgItems[0].getItem();
+ int var13 = theItem.getMaxDamage() - dmgItems[0].getItemDamageForDisplay();
+ int var8 = theItem.getMaxDamage() - dmgItems[1].getItemDamageForDisplay();
+ int var9 = var13 + var8 + theItem.getMaxDamage() * 5 / 100;
+ int var10 = Math.max(0, theItem.getMaxDamage() - var9);
+
+ return new ItemStack(dmgItems[0].getItem(), 1, var10);
+ } else {
+ IRecipe recipe;
+ for (int i = 0; i < CraftingManager.getInstance().getRecipeList().size(); i++) {
+ recipe = (IRecipe) CraftingManager.getInstance().getRecipeList().get(i);
+
+ if (recipe.matches(inv, world)) {
+ return recipe.getCraftingResult(inv);
+ }
+ }
+ return null;
+ }
+ }
+
+ /* ORE DICTIONARY FUNCTIONS */
+ public static ItemStack getOre(String oreName) {
+
+ return oreProxy.getOre(oreName);
+ }
+
+ public static String getOreName(ItemStack stack) {
+
+ return oreProxy.getOreName(stack);
+ }
+
+ public static boolean isOreIDEqual(ItemStack stack, int oreID) {
+
+ return oreProxy.isOreIDEqual(stack, oreID);
+ }
+
+ public static boolean isOreNameEqual(ItemStack stack, String oreName) {
+
+ return oreProxy.isOreNameEqual(stack, oreName);
+ }
+
+ public static boolean oreNameExists(String oreName) {
+
+ return oreProxy.oreNameExists(oreName);
+ }
+
+ public static boolean hasOreName(ItemStack stack) {
+
+ return !getOreName(stack).equals("Unknown");
+ }
+
+ public static boolean isBlock(ItemStack stack) {
+
+ return getOreName(stack).startsWith(BLOCK);
+ }
+
+ public static boolean isOre(ItemStack stack) {
+
+ return getOreName(stack).startsWith(ORE);
+ }
+
+ public static boolean isDust(ItemStack stack) {
+
+ return getOreName(stack).startsWith(DUST);
+ }
+
+ public static boolean isIngot(ItemStack stack) {
+
+ return getOreName(stack).startsWith(INGOT);
+ }
+
+ public static boolean isNugget(ItemStack stack) {
+
+ return getOreName(stack).startsWith(NUGGET);
+ }
+
+ public static boolean isLog(ItemStack stack) {
+
+ return getOreName(stack).startsWith(LOG);
+ }
+
+ /* CREATING ItemStacks */
+ public static final ItemStack stack(Item t) {
+
+ return new ItemStack(t);
+ }
+
+ public static final ItemStack stack(Item t, int s) {
+
+ return new ItemStack(t, s);
+ }
+
+ public static final ItemStack stack(Item t, int s, int m) {
+
+ return new ItemStack(t, s, m);
+ }
+
+ public static final ItemStack stack(Block t) {
+
+ return new ItemStack(t);
+ }
+
+ public static final ItemStack stack(Block t, int s) {
+
+ return new ItemStack(t, s);
+ }
+
+ public static final ItemStack stack(Block t, int s, int m) {
+
+ return new ItemStack(t, s, m);
+ }
+
+ public static final ItemStack stack2(Item t) {
+
+ return new ItemStack(t, 1, WILDCARD_VALUE);
+ }
+
+ public static final ItemStack stack2(Item t, int s) {
+
+ return new ItemStack(t, s, WILDCARD_VALUE);
+ }
+
+ public static final ItemStack stack2(Block t) {
+
+ return new ItemStack(t, 1, WILDCARD_VALUE);
+ }
+
+ public static final ItemStack stack2(Block t, int s) {
+
+ return new ItemStack(t, s, WILDCARD_VALUE);
+ }
+
+ /* CREATING OreRecipes */
+ public static final IRecipe ShapedRecipe(Block result, Object... recipe) {
+
+ return new ShapedOreRecipe(result, recipe);
+ }
+
+ public static final IRecipe ShapedRecipe(Item result, Object... recipe) {
+
+ return new ShapedOreRecipe(result, recipe);
+ }
+
+ public static final IRecipe ShapedRecipe(ItemStack result, Object... recipe) {
+
+ return new ShapedOreRecipe(result, recipe);
+ }
+
+ public static final IRecipe ShapedRecipe(Block result, int s, Object... recipe) {
+
+ return new ShapedOreRecipe(stack(result, s), recipe);
+ }
+
+ public static final IRecipe ShapedRecipe(Item result, int s, Object... recipe) {
+
+ return new ShapedOreRecipe(stack(result, s), recipe);
+ }
+
+ public static final IRecipe ShapedRecipe(ItemStack result, int s, Object... recipe) {
+
+ return new ShapedOreRecipe(cloneStack(result, s), recipe);
+ }
+
+ public static final IRecipe ShapelessRecipe(Block result, Object... recipe) {
+
+ return new ShapelessOreRecipe(result, recipe);
+ }
+
+ public static final IRecipe ShapelessRecipe(Item result, Object... recipe) {
+
+ return new ShapelessOreRecipe(result, recipe);
+ }
+
+ public static final IRecipe ShapelessRecipe(ItemStack result, Object... recipe) {
+
+ return new ShapelessOreRecipe(result, recipe);
+ }
+
+ public static final IRecipe ShapelessRecipe(Block result, int s, Object... recipe) {
+
+ return new ShapelessOreRecipe(stack(result, s), recipe);
+ }
+
+ public static final IRecipe ShapelessRecipe(Item result, int s, Object... recipe) {
+
+ return new ShapelessOreRecipe(stack(result, s), recipe);
+ }
+
+ public static final IRecipe ShapelessRecipe(ItemStack result, int s, Object... recipe) {
+
+ return new ShapelessOreRecipe(cloneStack(result, s), recipe);
+ }
+
+ /* CRAFTING HELPER FUNCTIONS */
+ // GEARS{
+ public static boolean addGearRecipe(ItemStack gear, String ingot) {
+
+ if (gear == null || !oreNameExists(ingot)) {
+ return false;
+ }
+ GameRegistry.addRecipe(ShapedRecipe(gear, " X ", "XIX", " X ", 'X', ingot, 'I', "ingotIron"));
+ return true;
+ }
+
+ public static boolean addGearRecipe(ItemStack gear, String ingot, String center) {
+
+ if (gear == null || !oreNameExists(ingot) || !oreNameExists(center)) {
+ return false;
+ }
+ GameRegistry.addRecipe(ShapedRecipe(gear, " X ", "XIX", " X ", 'X', ingot, 'I', center));
+ return true;
+ }
+
+ public static boolean addGearRecipe(ItemStack gear, String ingot, ItemStack center) {
+
+ if (gear == null | center == null || !oreNameExists(ingot)) {
+ return false;
+ }
+ GameRegistry.addRecipe(ShapedRecipe(gear, " X ", "XIX", " X ", 'X', ingot, 'I', center));
+ return true;
+ }
+
+ public static boolean addGearRecipe(ItemStack gear, ItemStack ingot, String center) {
+
+ if (gear == null | ingot == null || !oreNameExists(center)) {
+ return false;
+ }
+ GameRegistry.addRecipe(ShapedRecipe(gear, " X ", "XIX", " X ", 'X', ingot, 'I', center));
+ return true;
+ }
+
+ public static boolean addGearRecipe(ItemStack gear, ItemStack ingot, ItemStack center) {
+
+ if (gear == null | ingot == null | center == null) {
+ return false;
+ }
+ GameRegistry.addRecipe(cloneStack(gear), " X ", "XIX", " X ", 'X', cloneStack(ingot, 1), 'I', cloneStack(center, 1));
+ return true;
+ }
+
+ // rotated
+ public static boolean addRotatedGearRecipe(ItemStack gear, String ingot, String center) {
+
+ if (gear == null || !oreNameExists(ingot) || !oreNameExists(center)) {
+ return false;
+ }
+ GameRegistry.addRecipe(ShapedRecipe(gear, "X X", " I ", "X X", 'X', ingot, 'I', center));
+ return true;
+ }
+
+ public static boolean addRotatedGearRecipe(ItemStack gear, String ingot, ItemStack center) {
+
+ if (gear == null | center == null || !oreNameExists(ingot)) {
+ return false;
+ }
+ GameRegistry.addRecipe(ShapedRecipe(gear, "X X", " I ", "X X", 'X', ingot, 'I', center));
+ return true;
+ }
+
+ public static boolean addRotatedGearRecipe(ItemStack gear, ItemStack ingot, String center) {
+
+ if (gear == null | ingot == null || !oreNameExists(center)) {
+ return false;
+ }
+ GameRegistry.addRecipe(ShapedRecipe(gear, "X X", " I ", "X X", 'X', ingot, 'I', center));
+ return true;
+ }
+
+ public static boolean addRotatedGearRecipe(ItemStack gear, ItemStack ingot, ItemStack center) {
+
+ if (gear == null | ingot == null | center == null) {
+ return false;
+ }
+ GameRegistry.addRecipe(cloneStack(gear), "X X", " I ", "X X", 'X', cloneStack(ingot, 1), 'I', cloneStack(center, 1));
+ return true;
+ }
+
+ // }
+
+ // SURROUND{
+ public static boolean addSurroundRecipe(ItemStack out, ItemStack one, ItemStack eight) {
+
+ if (out == null | one == null | eight == null) {
+ return false;
+ }
+ GameRegistry.addRecipe(cloneStack(out), "XXX", "XIX", "XXX", 'X', cloneStack(eight, 1), 'I', cloneStack(one, 1));
+ return true;
+ }
+
+ public static boolean addSurroundRecipe(ItemStack out, String one, ItemStack eight) {
+
+ if (out == null | eight == null || !oreNameExists(one)) {
+ return false;
+ }
+ GameRegistry.addRecipe(ShapedRecipe(out, "XXX", "XIX", "XXX", 'X', eight, 'I', one));
+ return true;
+ }
+
+ public static boolean addSurroundRecipe(ItemStack out, ItemStack one, String eight) {
+
+ if (out == null | one == null || !oreNameExists(eight)) {
+ return false;
+ }
+ GameRegistry.addRecipe(ShapedRecipe(out, "XXX", "XIX", "XXX", 'X', eight, 'I', one));
+ return true;
+ }
+
+ public static boolean addSurroundRecipe(ItemStack out, String one, String eight) {
+
+ if (out == null || !oreNameExists(one) || !oreNameExists(eight)) {
+ return false;
+ }
+ GameRegistry.addRecipe(ShapedRecipe(out, "XXX", "XIX", "XXX", 'X', eight, 'I', one));
+ return true;
+ }
+
+ // }
+
+ // FENCES{
+ public static boolean addFenceRecipe(ItemStack out, ItemStack in) {
+
+ if (out == null | in == null) {
+ return false;
+ }
+ GameRegistry.addRecipe(cloneStack(out), "XXX", "XXX", 'X', cloneStack(in, 1));
+ return true;
+ }
+
+ public static boolean addFenceRecipe(ItemStack out, String in) {
+
+ if (out == null || !oreNameExists(in)) {
+ return false;
+ }
+ GameRegistry.addRecipe(ShapedRecipe(out, "XXX", "XXX", 'X', in));
+ return true;
+ }
+
+ // }
+
+ // REVERSE STORAGE{
+ public static boolean addReverseStorageRecipe(ItemStack nine, String one) {
+
+ if (nine == null || !oreNameExists(one)) {
+ return false;
+ }
+ GameRegistry.addRecipe(ShapelessRecipe(cloneStack(nine, 9), one));
+ return true;
+ }
+
+ public static boolean addReverseStorageRecipe(ItemStack nine, ItemStack one) {
+
+ if (nine == null | one == null) {
+ return false;
+ }
+ GameRegistry.addShapelessRecipe(cloneStack(nine, 9), cloneStack(one, 1));
+ return true;
+ }
+
+ public static boolean addSmallReverseStorageRecipe(ItemStack four, String one) {
+
+ if (four == null || !oreNameExists(one)) {
+ return false;
+ }
+ GameRegistry.addRecipe(ShapelessRecipe(cloneStack(four, 4), one));
+ return true;
+ }
+
+ public static boolean addSmallReverseStorageRecipe(ItemStack four, ItemStack one) {
+
+ if (four == null | one == null) {
+ return false;
+ }
+ GameRegistry.addShapelessRecipe(cloneStack(four, 4), cloneStack(one, 1));
+ return true;
+ }
+
+ // }
+
+ // STORAGE{
+ public static boolean addStorageRecipe(ItemStack one, String nine) {
+
+ if (one == null || !oreNameExists(nine)) {
+ return false;
+ }
+ GameRegistry.addRecipe(ShapelessRecipe(one, nine, nine, nine, nine, nine, nine, nine, nine, nine));
+ return true;
+ }
+
+ public static boolean addStorageRecipe(ItemStack one, ItemStack nine) {
+
+ if (one == null | nine == null) {
+ return false;
+ }
+ nine = cloneStack(nine, 1);
+ GameRegistry.addShapelessRecipe(one, nine, nine, nine, nine, nine, nine, nine, nine, nine);
+ return true;
+ }
+
+ public static boolean addSmallStorageRecipe(ItemStack one, String four) {
+
+ if (one == null || !oreNameExists(four)) {
+ return false;
+ }
+ GameRegistry.addRecipe(ShapedRecipe(one, "XX", "XX", 'X', four));
+ return true;
+ }
+
+ public static boolean addSmallStorageRecipe(ItemStack one, ItemStack four) {
+
+ if (one == null | four == null) {
+ return false;
+ }
+ GameRegistry.addRecipe(cloneStack(one), "XX", "XX", 'X', cloneStack(four, 1));
+ return true;
+ }
+
+ public static boolean addTwoWayStorageRecipe(ItemStack one, ItemStack nine) {
+
+ return addStorageRecipe(one, nine) && addReverseStorageRecipe(nine, one);
+ }
+
+ public static boolean addTwoWayStorageRecipe(ItemStack one, String one_ore, ItemStack nine, String nine_ore) {
+
+ return addStorageRecipe(one, nine_ore) && addReverseStorageRecipe(nine, one_ore);
+ }
+
+ public static boolean addSmallTwoWayStorageRecipe(ItemStack one, ItemStack four) {
+
+ return addSmallStorageRecipe(one, four) && addSmallReverseStorageRecipe(four, one);
+ }
+
+ public static boolean addSmallTwoWayStorageRecipe(ItemStack one, String one_ore, ItemStack four, String four_ore) {
+
+ return addSmallStorageRecipe(one, four_ore) && addSmallReverseStorageRecipe(four, one_ore);
+ }
+
+ // }
+
+ // SMELTING{
+ public static boolean addSmelting(ItemStack out, Item in) {
+
+ if (out == null | in == null) {
+ return false;
+ }
+ FurnaceRecipes.smelting().func_151394_a(cloneStack(in, 1), cloneStack(out), 0);
+ return true;
+ }
+
+ public static boolean addSmelting(ItemStack out, Block in) {
+
+ if (out == null | in == null) {
+ return false;
+ }
+ FurnaceRecipes.smelting().func_151394_a(cloneStack(in, 1), cloneStack(out), 0);
+ return true;
+ }
+
+ public static boolean addSmelting(ItemStack out, ItemStack in) {
+
+ if (out == null | in == null) {
+ return false;
+ }
+ FurnaceRecipes.smelting().func_151394_a(cloneStack(in, 1), cloneStack(out), 0);
+ return true;
+ }
+
+ public static boolean addSmelting(ItemStack out, Item in, float XP) {
+
+ if (out == null | in == null) {
+ return false;
+ }
+ FurnaceRecipes.smelting().func_151394_a(cloneStack(in, 1), cloneStack(out), XP);
+ return true;
+ }
+
+ public static boolean addSmelting(ItemStack out, Block in, float XP) {
+
+ if (out == null | in == null) {
+ return false;
+ }
+ FurnaceRecipes.smelting().func_151394_a(cloneStack(in, 1), cloneStack(out), XP);
+ return true;
+ }
+
+ public static boolean addSmelting(ItemStack out, ItemStack in, float XP) {
+
+ if (out == null | in == null) {
+ return false;
+ }
+ FurnaceRecipes.smelting().func_151394_a(cloneStack(in, 1), cloneStack(out), XP);
+ return true;
+ }
+
+ public static boolean addWeakSmelting(ItemStack out, Item in) {
+
+ if (out == null | in == null) {
+ return false;
+ }
+ FurnaceRecipes.smelting().func_151394_a(cloneStack(in, 1), cloneStack(out), 0.1f);
+ return true;
+ }
+
+ public static boolean addWeakSmelting(ItemStack out, Block in) {
+
+ if (out == null | in == null) {
+ return false;
+ }
+ FurnaceRecipes.smelting().func_151394_a(cloneStack(in, 1), cloneStack(out), 0.1f);
+ return true;
+ }
+
+ public static boolean addWeakSmelting(ItemStack out, ItemStack in) {
+
+ if (out == null | in == null) {
+ return false;
+ }
+ FurnaceRecipes.smelting().func_151394_a(cloneStack(in, 1), cloneStack(out), 0.1f);
+ return true;
+ }
+
+ // }
+
+ public static boolean addTwoWayConversionRecipe(ItemStack a, ItemStack b) {
+
+ if (a == null | b == null) {
+ return false;
+ }
+ GameRegistry.addShapelessRecipe(cloneStack(a, 1), cloneStack(b, 1));
+ GameRegistry.addShapelessRecipe(cloneStack(b, 1), cloneStack(a, 1));
+ return true;
+ }
+
+ public static void registerWithHandlers(String oreName, ItemStack stack) {
+
+ OreDictionary.registerOre(oreName, stack);
+ GameRegistry.registerCustomItemStack(oreName, stack);
+ FMLInterModComms.sendMessage("ForgeMicroblock", "microMaterial", stack);
+ }
+
+ // RECIPE{
+
+ public static void addRecipe(IRecipe recipe) {
+
+ GameRegistry.addRecipe(recipe);
+ }
+
+ public static void addRecipe(ItemStack out, Object... recipe) {
+
+ GameRegistry.addRecipe(out, recipe);
+ }
+
+ public static void addShapedRecipe(ItemStack out, Object... recipe) {
+
+ GameRegistry.addRecipe(out, recipe);
+ }
+
+ public static void addShapedRecipe(Item out, Object... recipe) {
+
+ addRecipe(new ItemStack(out), recipe);
+ }
+
+ public static void addShapedRecipe(Block out, Object... recipe) {
+
+ addRecipe(new ItemStack(out), recipe);
+ }
+
+ public static void addShapelessRecipe(ItemStack out, Object... recipe) {
+
+ GameRegistry.addShapelessRecipe(out, recipe);
+ }
+
+ public static void addShapelessRecipe(Item out, Object... recipe) {
+
+ addShapelessRecipe(new ItemStack(out), recipe);
+ }
+
+ public static void addShapelessRecipe(Block out, Object... recipe) {
+
+ addShapelessRecipe(new ItemStack(out), recipe);
+ }
+
+ public static void addShapedOreRecipe(ItemStack out, Object... recipe) {
+
+ GameRegistry.addRecipe(ShapedRecipe(out, recipe));
+ }
+
+ public static void addShapedOreRecipe(Item out, Object... recipe) {
+
+ GameRegistry.addRecipe(ShapedRecipe(out, recipe));
+ }
+
+ public static void addShapedOreRecipe(Block out, Object... recipe) {
+
+ GameRegistry.addRecipe(ShapedRecipe(out, recipe));
+ }
+
+ public static void addShapelessOreRecipe(ItemStack out, Object... recipe) {
+
+ GameRegistry.addRecipe(ShapelessRecipe(out, recipe));
+ }
+
+ public static void addShapelessOreRecipe(Item out, Object... recipe) {
+
+ GameRegistry.addRecipe(ShapelessRecipe(out, recipe));
+ }
+
+ public static void addShapelessOreRecipe(Block out, Object... recipe) {
+
+ GameRegistry.addRecipe(ShapelessRecipe(out, recipe));
+ }
+
+ // }
+
+ /* EMPOWERED ITEM HELPERS */
+ public static boolean isPlayerHoldingEmpowerableItem(EntityPlayer player) {
+
+ Item equipped = player.getCurrentEquippedItem() != null ? player.getCurrentEquippedItem().getItem() : null;
+ return equipped instanceof IEmpowerableItem;
+ }
+
+ public static boolean isPlayerHoldingEmpoweredItem(EntityPlayer player) {
+
+ Item equipped = player.getCurrentEquippedItem() != null ? player.getCurrentEquippedItem().getItem() : null;
+ return equipped instanceof IEmpowerableItem && ((IEmpowerableItem) equipped).isEmpowered(player.getCurrentEquippedItem());
+ }
+
+ public static boolean toggleHeldEmpowerableItemState(EntityPlayer player) {
+
+ ItemStack equipped = player.getCurrentEquippedItem();
+ IEmpowerableItem empowerableItem = (IEmpowerableItem) equipped.getItem();
+
+ return empowerableItem.setEmpoweredState(equipped, !empowerableItem.isEmpowered(equipped));
+ }
+
+ /* MULTIMODE ITEM HELPERS */
+ public static boolean isPlayerHoldingMultiModeItem(EntityPlayer player) {
+
+ Item equipped = player.getCurrentEquippedItem() != null ? player.getCurrentEquippedItem().getItem() : null;
+ return equipped instanceof IMultiModeItem;
+ }
+
+ public static boolean incrHeldMultiModeItemState(EntityPlayer player) {
+
+ ItemStack equipped = player.getCurrentEquippedItem();
+ IMultiModeItem multiModeItem = (IMultiModeItem) equipped.getItem();
+
+ return multiModeItem.incrMode(equipped);
+ }
+
+ public static boolean decrHeldMultiModeItemState(EntityPlayer player) {
+
+ ItemStack equipped = player.getCurrentEquippedItem();
+ IMultiModeItem multiModeItem = (IMultiModeItem) equipped.getItem();
+
+ return multiModeItem.incrMode(equipped);
+ }
+
+ public static boolean setHeldMultiModeItemState(EntityPlayer player, int mode) {
+
+ ItemStack equipped = player.getCurrentEquippedItem();
+ IMultiModeItem multiModeItem = (IMultiModeItem) equipped.getItem();
+
+ return multiModeItem.setMode(equipped, mode);
+ }
+
+ /**
+ * Determine if a player is holding a registered Fluid Container.
+ */
+ public static final boolean isPlayerHoldingFluidContainer(EntityPlayer player) {
+
+ return FluidContainerRegistry.isContainer(player.getCurrentEquippedItem());
+ }
+
+ public static final boolean isPlayerHoldingFluidContainerItem(EntityPlayer player) {
+
+ return FluidHelper.isPlayerHoldingFluidContainerItem(player);
+ }
+
+ public static final boolean isPlayerHoldingEnergyContainerItem(EntityPlayer player) {
+
+ return EnergyHelper.isPlayerHoldingEnergyContainerItem(player);
+ }
+
+ public static final boolean isPlayerHoldingNothing(EntityPlayer player) {
+
+ return player.getCurrentEquippedItem() == null;
+ }
+
+ public static Item getItemFromStack(ItemStack theStack) {
+
+ return theStack == null ? null : theStack.getItem();
+ }
+
+ public static boolean areItemsEqual(Item itemA, Item itemB) {
+
+ if (itemA == null | itemB == null) {
+ return false;
+ }
+ return itemA == itemB || itemA.equals(itemB);
+ }
+
+ public static final boolean isPlayerHoldingItem(Class> item, EntityPlayer player) {
+
+ return item.isInstance(getItemFromStack(player.getCurrentEquippedItem()));
+ }
+
+ /**
+ * Determine if a player is holding an ItemStack of a specific Item type.
+ */
+ public static final boolean isPlayerHoldingItem(Item item, EntityPlayer player) {
+
+ return areItemsEqual(item, getItemFromStack(player.getCurrentEquippedItem()));
+ }
+
+ /**
+ * Determine if a player is holding an ItemStack with a specific Item ID, Metadata, and NBT.
+ */
+ public static final boolean isPlayerHoldingItemStack(ItemStack stack, EntityPlayer player) {
+
+ return itemsEqualWithMetadata(stack, player.getCurrentEquippedItem());
+ }
+
+ /**
+ * Determine if the damage of two ItemStacks is equal. Assumes both itemstacks are of type A.
+ */
+ public static boolean itemsDamageEqual(ItemStack stackA, ItemStack stackB) {
+
+ return (!stackA.getHasSubtypes() && stackA.getMaxDamage() == 0) || (getItemDamage(stackA) == getItemDamage(stackB));
+ }
+
+ /**
+ * Determine if two ItemStacks have the same Item.
+ */
+ public static boolean itemsEqualWithoutMetadata(ItemStack stackA, ItemStack stackB) {
+
+ if (stackA == null || stackB == null) {
+ return false;
+ }
+ return areItemsEqual(stackA.getItem(), stackB.getItem());
+ }
+
+ /**
+ * Determine if two ItemStacks have the same Item and NBT.
+ */
+ public static boolean itemsEqualWithoutMetadata(ItemStack stackA, ItemStack stackB, boolean checkNBT) {
+
+ return itemsEqualWithoutMetadata(stackA, stackB) && (!checkNBT || doNBTsMatch(stackA.stackTagCompound, stackB.stackTagCompound));
+ }
+
+ /**
+ * Determine if two ItemStacks have the same Item and damage.
+ */
+ public static boolean itemsEqualWithMetadata(ItemStack stackA, ItemStack stackB) {
+
+ return itemsEqualWithoutMetadata(stackA, stackB) && itemsDamageEqual(stackA, stackB);
+ }
+
+ /**
+ * Determine if two ItemStacks have the same Item, damage, and NBT.
+ */
+ public static boolean itemsEqualWithMetadata(ItemStack stackA, ItemStack stackB, boolean checkNBT) {
+
+ return itemsEqualWithMetadata(stackA, stackB) && (!checkNBT || doNBTsMatch(stackA.stackTagCompound, stackB.stackTagCompound));
+ }
+
+ /**
+ * Determine if two ItemStacks have the same Item, identical damage, and NBT.
+ */
+ public static boolean itemsIdentical(ItemStack stackA, ItemStack stackB) {
+
+ return itemsEqualWithoutMetadata(stackA, stackB) && getItemDamage(stackA) == getItemDamage(stackB)
+ && doNBTsMatch(stackA.stackTagCompound, stackB.stackTagCompound);
+ }
+
+ /**
+ * Determine if two NBTTagCompounds are equal.
+ */
+ public static boolean doNBTsMatch(NBTTagCompound nbtA, NBTTagCompound nbtB) {
+
+ if (nbtA == null & nbtB == null) {
+ return true;
+ }
+ if (nbtA != null & nbtB != null) {
+ return nbtA.equals(nbtB);
+ }
+ return false;
+ }
+
+ public static boolean itemsEqualForCrafting(ItemStack stackA, ItemStack stackB) {
+
+ return itemsEqualWithoutMetadata(stackA, stackB)
+ && (!stackA.getHasSubtypes() || ((getItemDamage(stackA) == OreDictionary.WILDCARD_VALUE || getItemDamage(stackB) == OreDictionary.WILDCARD_VALUE) || getItemDamage(stackB) == getItemDamage(stackA)));
+ }
+
+ public static boolean craftingEquivalent(ItemStack checked, ItemStack source, String oreDict, ItemStack output) {
+
+ if (itemsEqualForCrafting(checked, source)) {
+ return true;
+ } else if (output != null && isBlacklist(output)) {
+ return false;
+ } else if (oreDict == null || oreDict.equals("Unknown")) {
+ return false;
+ } else {
+ return getOreName(checked).equalsIgnoreCase(oreDict);
+ }
+ }
+
+ public static boolean doOreIDsMatch(ItemStack stackA, ItemStack stackB) {
+
+ int id = oreProxy.getOreID(stackA);
+ return id >= 0 && id == oreProxy.getOreID(stackB);
+ }
+
+ public static boolean isBlacklist(ItemStack output) {
+
+ Item item = output.getItem();
+ return Item.getItemFromBlock(Blocks.birch_stairs) == item || Item.getItemFromBlock(Blocks.jungle_stairs) == item
+ || Item.getItemFromBlock(Blocks.oak_stairs) == item || Item.getItemFromBlock(Blocks.spruce_stairs) == item
+ || Item.getItemFromBlock(Blocks.planks) == item || Item.getItemFromBlock(Blocks.wooden_slab) == item;
+ }
+
+ public static String getItemNBTString(ItemStack theItem, String nbtKey, String invalidReturn) {
+
+ return theItem.stackTagCompound != null && theItem.stackTagCompound.hasKey(nbtKey) ? theItem.stackTagCompound.getString(nbtKey) : invalidReturn;
+ }
+
+ /**
+ * Adds Inventory information to ItemStacks which themselves hold things. Called in addInformation().
+ */
+ public static void addInventoryInformation(ItemStack stack, List list) {
+
+ addInventoryInformation(stack, list, 0, Integer.MAX_VALUE);
+ }
+
+ public static void addInventoryInformation(ItemStack stack, List list, int minSlot, int maxSlot) {
+
+ if (stack.stackTagCompound == null) {
+ list.add(StringHelper.localize("info.cofh.empty"));
+ return;
+ }
+ if (stack.getItem() instanceof IInventoryContainerItem && stack.stackTagCompound.hasKey("Accessible")) {
+ addAccessibleInventoryInformation(stack, list, minSlot, maxSlot);
+ return;
+ }
+ if (!stack.stackTagCompound.hasKey("Inventory", Constants.NBT.TAG_LIST)
+ || stack.stackTagCompound.getTagList("Inventory", stack.stackTagCompound.getId()).tagCount() <= 0) {
+ list.add(StringHelper.localize("info.cofh.empty"));
+ return;
+ }
+ NBTTagList nbtList = stack.stackTagCompound.getTagList("Inventory", stack.stackTagCompound.getId());
+ ItemStack curStack;
+ ItemStack curStack2;
+
+ ArrayList containedItems = new ArrayList();
+
+ boolean[] visited = new boolean[nbtList.tagCount()];
+
+ for (int i = 0; i < nbtList.tagCount(); i++) {
+ NBTTagCompound tag = nbtList.getCompoundTagAt(i);
+ int slot = tag.getInteger("Slot");
+
+ if (visited[i] || slot < minSlot || slot > maxSlot) {
+ continue;
+ }
+ visited[i] = true;
+ curStack = ItemStack.loadItemStackFromNBT(tag);
+
+ if (curStack == null) {
+ continue;
+ }
+ containedItems.add(curStack);
+ for (int j = 0; j < nbtList.tagCount(); j++) {
+ NBTTagCompound tag2 = nbtList.getCompoundTagAt(j);
+ int slot2 = tag.getInteger("Slot");
+
+ if (visited[j] || slot2 < minSlot || slot2 > maxSlot) {
+ continue;
+ }
+ curStack2 = ItemStack.loadItemStackFromNBT(tag2);
+
+ if (curStack2 == null) {
+ continue;
+ }
+ if (itemsIdentical(curStack, curStack2)) {
+ curStack.stackSize += curStack2.stackSize;
+ visited[j] = true;
+ }
+ }
+ }
+ if (containedItems.size() > 0) {
+ list.add(StringHelper.localize("info.cofh.contents") + ":");
+ }
+ for (ItemStack item : containedItems) {
+ int maxStackSize = item.getMaxStackSize();
+
+ if (!StringHelper.displayStackCount || item.stackSize < maxStackSize || maxStackSize == 1) {
+ list.add(" " + StringHelper.BRIGHT_GREEN + item.stackSize + " " + StringHelper.getItemName(item));
+ } else {
+ if (item.stackSize % maxStackSize != 0) {
+ list.add(" " + StringHelper.BRIGHT_GREEN + maxStackSize + "x" + item.stackSize / maxStackSize + "+" + item.stackSize % maxStackSize
+ + " " + StringHelper.getItemName(item));
+ } else {
+ list.add(" " + StringHelper.BRIGHT_GREEN + maxStackSize + "x" + item.stackSize / maxStackSize + " " + StringHelper.getItemName(item));
+ }
+ }
+ }
+ }
+
+ public static void addAccessibleInventoryInformation(ItemStack stack, List list, int minSlot, int maxSlot) {
+
+ int invSize = ((IInventoryContainerItem) stack.getItem()).getSizeInventory(stack);
+ ItemStack curStack;
+ ItemStack curStack2;
+
+ ArrayList containedItems = new ArrayList();
+
+ boolean[] visited = new boolean[invSize];
+
+ NBTTagCompound tag = stack.stackTagCompound;
+ if (tag.hasKey("Inventory")) {
+ tag = tag.getCompoundTag("Inventory");
+ }
+
+ for (int i = minSlot; i < Math.min(invSize, maxSlot); i++) {
+ if (visited[i]) {
+ continue;
+ }
+ if (!tag.hasKey("Slot" + i)) {
+ continue;
+ }
+ curStack = ItemStack.loadItemStackFromNBT(tag.getCompoundTag("Slot" + i));
+ visited[i] = true;
+
+ if (curStack == null) {
+ continue;
+ }
+ containedItems.add(curStack);
+ for (int j = minSlot; j < Math.min(invSize, maxSlot); j++) {
+ if (visited[j]) {
+ continue;
+ }
+ if (!tag.hasKey("Slot" + j)) {
+ continue;
+ }
+ curStack2 = ItemStack.loadItemStackFromNBT(tag.getCompoundTag("Slot" + j));
+
+ if (curStack2 == null) {
+ continue;
+ }
+ if (itemsIdentical(curStack, curStack2)) {
+ curStack.stackSize += curStack2.stackSize;
+ visited[j] = true;
+ }
+ }
+ }
+ if (containedItems.size() > 0) {
+ list.add(StringHelper.localize("info.cofh.contents") + ":");
+ } else {
+ list.add(StringHelper.localize("info.cofh.empty"));
+ }
+ for (ItemStack item : containedItems) {
+ int maxStackSize = item.getMaxStackSize();
+
+ if (!StringHelper.displayStackCount || item.stackSize < maxStackSize || maxStackSize == 1) {
+ list.add(" " + StringHelper.BRIGHT_GREEN + item.stackSize + " " + StringHelper.getItemName(item));
+ } else {
+ if (item.stackSize % maxStackSize != 0) {
+ list.add(" " + StringHelper.BRIGHT_GREEN + maxStackSize + "x" + item.stackSize / maxStackSize + "+" + item.stackSize % maxStackSize
+ + " " + StringHelper.getItemName(item));
+ } else {
+ list.add(" " + StringHelper.BRIGHT_GREEN + maxStackSize + "x" + item.stackSize / maxStackSize + " " + StringHelper.getItemName(item));
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/MathHelper.java b/src/main/java/cofh/lib/util/helpers/MathHelper.java
new file mode 100644
index 00000000..ab2a2667
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/MathHelper.java
@@ -0,0 +1,229 @@
+package cofh.lib.util.helpers;
+
+import java.util.Random;
+
+/**
+ * Contains various math-related helper functions. Often faster than conventional implementations.
+ *
+ * @author King Lemming
+ *
+ */
+public final class MathHelper {
+
+ private MathHelper() {
+
+ }
+
+ public static final Random RANDOM = new Random();
+ public static final double PHI = 1.618034;
+ public static final double[] SIN_TABLE = new double[65536];
+
+ static {
+ for (int i = 0; i < 65536; i++) {
+ SIN_TABLE[i] = Math.sin(i / 65536D * 2 * Math.PI);
+ }
+ SIN_TABLE[0] = 0;
+ SIN_TABLE[16384] = 1;
+ SIN_TABLE[32768] = 0;
+ SIN_TABLE[49152] = -1;
+ }
+
+ public static double sin(double d) {
+
+ return SIN_TABLE[(int) ((float) d * 10430.378F) & 65535];
+ }
+
+ public static double cos(double d) {
+
+ return SIN_TABLE[(int) ((float) d * 10430.378F + 16384.0F) & 65535];
+ }
+
+ /**
+ * @deprecated use {@link clamp} instead
+ */
+ @Deprecated
+ public static int clampI(int a, int min, int max) {
+
+ return a < min ? min : (a > max ? max : a);
+ }
+
+ /**
+ * @deprecated use {@link clamp} instead
+ */
+ @Deprecated
+ public static float clampF(float a, float min, float max) {
+
+ return a < min ? min : (a > max ? max : a);
+ }
+
+ public static int clamp(int a, int min, int max) {
+
+ return a < min ? min : (a > max ? max : a);
+ }
+
+ public static float clamp(float a, float min, float max) {
+
+ return a < min ? min : (a > max ? max : a);
+ }
+
+ public static double clamp(double a, double min, double max) {
+
+ return a < min ? min : (a > max ? max : a);
+ }
+
+ public static float approachLinear(float a, float b, float max) {
+
+ return a > b ? a - b < max ? b : a - max : b - a < max ? b : a + max;
+ }
+
+ public static double approachLinear(double a, double b, double max) {
+
+ return a > b ? a - b < max ? b : a - max : b - a < max ? b : a + max;
+ }
+
+ public static float interpolate(float a, float b, float d) {
+
+ return a + (b - a) * d;
+ }
+
+ public static double interpolate(double a, double b, double d) {
+
+ return a + (b - a) * d;
+ }
+
+ public static double approachExp(double a, double b, double ratio) {
+
+ return a + (b - a) * ratio;
+ }
+
+ public static double approachExp(double a, double b, double ratio, double cap) {
+
+ double d = (b - a) * ratio;
+
+ if (Math.abs(d) > cap) {
+ d = Math.signum(d) * cap;
+ }
+ return a + d;
+ }
+
+ public static double retreatExp(double a, double b, double c, double ratio, double kick) {
+
+ double d = (Math.abs(c - a) + kick) * ratio;
+
+ if (d > Math.abs(b - a)) {
+ return b;
+ }
+ return a + Math.signum(b - a) * d;
+ }
+
+ public static double clip(double value, double min, double max) {
+
+ if (value > max) {
+ value = max;
+ } else if (value < min) {
+ value = min;
+ }
+ return value;
+ }
+
+ public static boolean between(double a, double x, double b) {
+
+ return a <= x && x <= b;
+ }
+
+ public static int approachExpI(int a, int b, double ratio) {
+
+ int r = (int) Math.round(approachExp(a, b, ratio));
+ return r == a ? b : r;
+ }
+
+ public static int retreatExpI(int a, int b, int c, double ratio, int kick) {
+
+ int r = (int) Math.round(retreatExp(a, b, c, ratio, kick));
+ return r == a ? b : r;
+ }
+
+ /**
+ * Unchecked implementation to round a number. Parameter should be known to be valid in advance.
+ */
+ public static int round(double d) {
+
+ return (int) (d + 0.5D);
+ }
+
+ /**
+ * Unchecked implementation to round a number up. Parameter should be known to be valid in advance.
+ */
+ public static int ceil(double d) {
+
+ return (int) (d + 0.9999D);
+ }
+
+ /**
+ * Unchecked implementation to round a number down. Parameter should be known to be valid in advance.
+ */
+ public static int floor(double d) {
+
+ int i = (int) d;
+ return d < i ? i - 1 : i;
+ }
+
+ /**
+ * Unchecked implementation to determine the smaller of two Floats. Parameters should be known to be valid in advance.
+ */
+ public static float minF(float a, float b) {
+
+ return a < b ? a : b;
+ }
+
+ public static float minF(int a, float b) {
+
+ return a < b ? a : b;
+ }
+
+ public static float minF(float a, int b) {
+
+ return a < b ? a : b;
+ }
+
+ /**
+ * Unchecked implementation to determine the larger of two Floats. Parameters should be known to be valid in advance.
+ */
+ public static float maxF(float a, float b) {
+
+ return a > b ? a : b;
+ }
+
+ public static float maxF(int a, float b) {
+
+ return a > b ? a : b;
+ }
+
+ public static float maxF(float a, int b) {
+
+ return a > b ? a : b;
+ }
+
+ public static double maxAbs(double a, double b) {
+
+ if (a < 0.0D) {
+ a = -a;
+ }
+ if (b < 0.0D) {
+ b = -b;
+ }
+ return a > b ? a : b;
+ }
+
+ public static int setBit(int mask, int bit, boolean value) {
+
+ mask |= (value ? 1 : 0) << bit;
+ return mask;
+ }
+
+ public static boolean isBitSet(int mask, int bit) {
+
+ return (mask & 1 << bit) != 0;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/NBTHelper.java b/src/main/java/cofh/lib/util/helpers/NBTHelper.java
new file mode 100755
index 00000000..9dcb5ef9
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/NBTHelper.java
@@ -0,0 +1,194 @@
+package cofh.lib.util.helpers;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTBase;
+import net.minecraft.nbt.NBTTagCompound;
+
+/**
+ * Contains helper functions for the {@link NBTTagCompound} on an {@link ItemStack}
+ *
+ * @author Jotato
+ *
+ */
+public final class NBTHelper {
+
+ private NBTHelper() {
+
+ }
+
+ public static NBTTagCompound getTagCompound(ItemStack stack) {
+
+ if (stack == null) {
+ return null;
+ }
+ if (stack.getTagCompound() == null) {
+ stack.setTagCompound(new NBTTagCompound());
+ }
+ return stack.getTagCompound();
+ }
+
+ public static boolean keyExists(ItemStack stack, String key) {
+
+ if (stack == null) {
+ return false;
+ }
+ return getTagCompound(stack).hasKey(key);
+ }
+
+ public static int getInt(ItemStack stack, String key, int defaultValue) {
+
+ if (!keyExists(stack, key)) {
+ return defaultValue;
+ }
+ return getTagCompound(stack).getInteger(key);
+ }
+
+ public static void setInt(ItemStack stack, String key, int value) {
+
+ getTagCompound(stack).setInteger(key, value);
+ }
+
+ public static long getLong(ItemStack stack, String key, long defaultValue) {
+
+ if (!keyExists(stack, key)) {
+ return defaultValue;
+ }
+ return getTagCompound(stack).getLong(key);
+ }
+
+ public static void setLong(ItemStack stack, String key, Long value) {
+
+ getTagCompound(stack).setLong(key, value);
+ }
+
+ public static boolean getBoolean(ItemStack stack, String key, boolean defaultValue) {
+
+ if (!keyExists(stack, key)) {
+ return defaultValue;
+ }
+ return getTagCompound(stack).getBoolean(key);
+ }
+
+ public static void setBoolean(ItemStack stack, String key, boolean value) {
+
+ getTagCompound(stack).setBoolean(key, value);
+ }
+
+ public static byte getByte(ItemStack stack, String key, byte defaultValue) {
+
+ if (!keyExists(stack, key)) {
+ return defaultValue;
+ }
+ return getTagCompound(stack).getByte(key);
+ }
+
+ public static void setByte(ItemStack stack, String key, byte value) {
+
+ getTagCompound(stack).setByte(key, value);
+ }
+
+ public static byte[] getByteArray(ItemStack stack, String key, byte[] defaultValue) {
+
+ if (!keyExists(stack, key)) {
+ return defaultValue;
+ }
+ return getTagCompound(stack).getByteArray(key);
+ }
+
+ public static void setByteArray(ItemStack stack, String key, byte[] value) {
+
+ getTagCompound(stack).setByteArray(key, value);
+ }
+
+ public static double getDouble(ItemStack stack, String key, double defaultValue) {
+
+ if (!keyExists(stack, key)) {
+ return defaultValue;
+ }
+ return getTagCompound(stack).getDouble(key);
+ }
+
+ public static void setDouble(ItemStack stack, String key, double value) {
+
+ getTagCompound(stack).setDouble(key, value);
+ }
+
+ public static float getFloat(ItemStack stack, String key, float defaultValue) {
+
+ if (!keyExists(stack, key)) {
+ return defaultValue;
+ }
+ return getTagCompound(stack).getFloat(key);
+ };
+
+ public static void setFloat(ItemStack stack, String key, float value) {
+
+ getTagCompound(stack).setFloat(key, value);
+ }
+
+ public static int[] getIntArray(ItemStack stack, String key, int[] defaultValue) {
+
+ if (!keyExists(stack, key)) {
+ return defaultValue;
+ }
+ return getTagCompound(stack).getIntArray(key);
+ }
+
+ public static void setIntArray(ItemStack stack, String key, int[] value) {
+
+ getTagCompound(stack).setIntArray(key, value);
+ }
+
+ public static short getShort(ItemStack stack, String key, short defaultValue) {
+
+ if (!keyExists(stack, key)) {
+ return defaultValue;
+ }
+ return getTagCompound(stack).getShort(key);
+ }
+
+ public static void setShort(ItemStack stack, String key, short value) {
+
+ getTagCompound(stack).setShort(key, value);
+ }
+
+ public static String getString(ItemStack stack, String key, String defaultValue) {
+
+ if (!keyExists(stack, key)) {
+ return defaultValue;
+ }
+ return getTagCompound(stack).getString(key);
+ }
+
+ public static void setString(ItemStack stack, String key, String value) {
+
+ getTagCompound(stack).setString(key, value);
+ }
+
+ public static NBTBase getTag(ItemStack stack, String key) {
+
+ if (!keyExists(stack, key)) {
+ return null;
+ }
+ return getTagCompound(stack).getTag(key);
+ }
+
+ public static void setTag(ItemStack stack, String key, NBTBase value) {
+
+ getTagCompound(stack).setTag(key, value);
+ }
+
+ public static NBTTagCompound getCompoundTag(ItemStack stack, String key) {
+
+ if (!keyExists(stack, key)) {
+ return null;
+ }
+ return getTagCompound(stack).getCompoundTag(key);
+ }
+
+ public static void removeTag(ItemStack stack, String key) {
+
+ getTagCompound(stack).removeTag(key);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/RedstoneControlHelper.java b/src/main/java/cofh/lib/util/helpers/RedstoneControlHelper.java
new file mode 100644
index 00000000..c571f2b3
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/RedstoneControlHelper.java
@@ -0,0 +1,77 @@
+package cofh.lib.util.helpers;
+
+import cofh.api.tileentity.IRedstoneControl;
+import cofh.api.tileentity.IRedstoneControl.ControlMode;
+
+import java.util.List;
+
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+
+public class RedstoneControlHelper {
+
+ private RedstoneControlHelper() {
+
+ }
+
+ /* NBT TAG HELPERS */
+ public static NBTTagCompound setItemStackTagRS(NBTTagCompound tag, IRedstoneControl tile) {
+
+ if (tile == null) {
+ return null;
+ }
+ if (tag == null) {
+ tag = new NBTTagCompound();
+ }
+ tag.setByte("RSControl", (byte) tile.getControl().ordinal());
+ return tag;
+ }
+
+ public static ControlMode getControlFromNBT(NBTTagCompound tag) {
+
+ return tag == null ? ControlMode.DISABLED : ControlMode.values()[tag.getByte("RSControl")];
+ }
+
+ /**
+ * Adds Redstone Control information to ItemStacks.
+ */
+ public static void addRSControlInformation(ItemStack stack, List list) {
+
+ if (hasRSControl(stack)) {
+ switch (stack.stackTagCompound.getByte("RSControl")) {
+ case 0:
+ list.add(StringHelper.localize("info.cofh.signal") + ": " + StringHelper.RED + StringHelper.localize("info.cofh.disabled") + StringHelper.END);
+ return;
+ case 1:
+ list.add(StringHelper.localize("info.cofh.signal") + ": " + StringHelper.BRIGHT_GREEN + StringHelper.localize("info.cofh.enabled")
+ + StringHelper.LIGHT_GRAY + ", " + StringHelper.localize("info.cofh.low") + StringHelper.END);
+ return;
+ case 2:
+ list.add(StringHelper.localize("info.cofh.signal") + ": " + StringHelper.BRIGHT_GREEN + StringHelper.localize("info.cofh.enabled")
+ + StringHelper.LIGHT_GRAY + ", " + StringHelper.localize("info.cofh.high") + StringHelper.END);
+ return;
+ }
+ }
+ }
+
+ /* ITEM HELPERS */
+ public static boolean hasRSControl(ItemStack stack) {
+
+ return stack.stackTagCompound == null ? false : stack.stackTagCompound.hasKey("RSControl");
+ }
+
+ public static boolean setControl(ItemStack stack, ControlMode control) {
+
+ if (stack.stackTagCompound == null) {
+ stack.setTagCompound(new NBTTagCompound());
+ }
+ stack.stackTagCompound.setByte("RSControl", (byte) control.ordinal());
+ return true;
+ }
+
+ public static ControlMode getControl(ItemStack stack) {
+
+ return stack.stackTagCompound == null ? ControlMode.DISABLED : ControlMode.values()[stack.stackTagCompound.getByte("RSControl")];
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/SecurityHelper.java b/src/main/java/cofh/lib/util/helpers/SecurityHelper.java
new file mode 100644
index 00000000..62e42570
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/SecurityHelper.java
@@ -0,0 +1,285 @@
+package cofh.lib.util.helpers;
+
+import cofh.api.tileentity.ISecurable;
+import cofh.api.tileentity.ISecurable.AccessMode;
+import com.google.common.base.Strings;
+import com.mojang.authlib.GameProfile;
+import cpw.mods.fml.common.FMLCommonHandler;
+import cpw.mods.fml.common.eventhandler.SubscribeEvent;
+import cpw.mods.fml.common.gameevent.PlayerEvent.PlayerLoggedInEvent;
+import cpw.mods.fml.relauncher.ReflectionHelper;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.nbt.NBTTagString;
+import net.minecraft.network.EnumConnectionState;
+import net.minecraft.network.INetHandler;
+import net.minecraft.network.Packet;
+import net.minecraft.network.PacketBuffer;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.management.PreYggdrasilConverter;
+
+public class SecurityHelper {
+
+ public static final GameProfile UNKNOWN_GAME_PROFILE = new GameProfile(UUID.fromString("1ef1a6f0-87bc-4e78-0a0b-c6824eb787ea"), "[None]");
+ private static boolean setup = false;
+
+ public static void setup() {
+
+ if (setup) {
+ return;
+ }
+ EnumConnectionState.PLAY.func_150755_b().put(-26, Login.S__PacketSendUUID.class);
+ Map, EnumConnectionState> data;
+ data = ReflectionHelper.getPrivateValue(EnumConnectionState.class, null, "field_150761_f");
+ data.put(Login.S__PacketSendUUID.class, EnumConnectionState.PLAY);
+ FMLCommonHandler.instance().bus().register(new Login.S__PacketSendUUID());
+ setup = true;
+ }
+
+ static {
+ setup();
+ }
+
+ private SecurityHelper() {
+
+ }
+
+ public static boolean isDefaultUUID(UUID uuid) {
+
+ return uuid == null || (uuid.version() == 4 && uuid.variant() == 0);
+ }
+
+ public static UUID getID(EntityPlayer player) {
+
+ if (MinecraftServer.getServer() != null && MinecraftServer.getServer().isServerRunning()) {
+ return player.getGameProfile().getId();
+ }
+ return getClientId(player);
+ }
+
+ private static UUID cachedId;
+
+ private static UUID getClientId(EntityPlayer player) {
+
+ if (player != Minecraft.getMinecraft().thePlayer) {
+ return player.getGameProfile().getId();
+ }
+ if (cachedId == null) {
+ cachedId = Minecraft.getMinecraft().thePlayer.getGameProfile().getId();
+ }
+ return cachedId;
+ }
+
+ /* NBT TAG HELPER */
+ public static NBTTagCompound setItemStackTagSecure(NBTTagCompound tag, ISecurable tile) {
+
+ if (tile == null) {
+ return null;
+ }
+ if (tag == null) {
+ tag = new NBTTagCompound();
+ }
+ tag.setBoolean("Secure", true);
+ tag.setByte("Access", (byte) tile.getAccess().ordinal());
+ tag.setString("OwnerUUID", tile.getOwner().getId().toString());
+ tag.setString("Owner", tile.getOwner().getName());
+ return tag;
+ }
+
+ /**
+ * Adds Security information to ItemStacks.
+ */
+ public static void addOwnerInformation(ItemStack stack, List list) {
+
+ if (SecurityHelper.isSecure(stack)) {
+ boolean hasUUID = stack.stackTagCompound.hasKey("OwnerUUID");
+ if (!stack.stackTagCompound.hasKey("Owner") && !hasUUID) {
+ list.add(StringHelper.localize("info.cofh.owner") + ": " + StringHelper.localize("info.cofh.none"));
+ } else {
+ if (hasUUID && stack.stackTagCompound.hasKey("Owner")) {
+ list.add(StringHelper.localize("info.cofh.owner") + ": " + stack.stackTagCompound.getString("Owner") + " \u0378");
+ } else {
+ list.add(StringHelper.localize("info.cofh.owner") + ": " + StringHelper.localize("info.cofh.anotherplayer"));
+ }
+ }
+ }
+ }
+
+ public static void addAccessInformation(ItemStack stack, List list) {
+
+ if (SecurityHelper.isSecure(stack)) {
+ String accessString = "";
+ switch (stack.stackTagCompound.getByte("Access")) {
+ case 0:
+ accessString = StringHelper.localize("info.cofh.accessPublic");
+ break;
+ case 1:
+ accessString = StringHelper.localize("info.cofh.accessRestricted");
+ break;
+ case 2:
+ accessString = StringHelper.localize("info.cofh.accessPrivate");
+ break;
+ }
+ list.add(StringHelper.localize("info.cofh.access") + ": " + accessString);
+ }
+ }
+
+ /* ITEM HELPERS */
+ public static boolean isSecure(ItemStack stack) {
+
+ return stack.stackTagCompound == null ? false : stack.stackTagCompound.hasKey("Secure");
+ }
+
+ public static ItemStack setSecure(ItemStack stack) {
+
+ if (isSecure(stack)) {
+ return stack;
+ }
+ if (stack.stackTagCompound == null) {
+ stack.setTagCompound(new NBTTagCompound());
+ }
+ stack.stackTagCompound.setBoolean("Secure", true);
+ stack.stackTagCompound.setByte("Access", (byte) 0);
+ return stack;
+ }
+
+ public static ItemStack removeSecure(ItemStack stack) {
+
+ if (!isSecure(stack)) {
+ return stack;
+ }
+ stack.stackTagCompound.removeTag("Secure");
+ stack.stackTagCompound.removeTag("Access");
+ stack.stackTagCompound.removeTag("OwnerUUID");
+ stack.stackTagCompound.removeTag("Owner");
+
+ if (stack.stackTagCompound.hasNoTags()) {
+ stack.stackTagCompound = null;
+ }
+ return stack;
+ }
+
+ public static boolean setAccess(ItemStack stack, AccessMode access) {
+
+ if (!isSecure(stack)) {
+ return false;
+ }
+ stack.stackTagCompound.setByte("Access", (byte) access.ordinal());
+ return true;
+ }
+
+ public static AccessMode getAccess(ItemStack stack) {
+
+ return stack.stackTagCompound == null ? AccessMode.PUBLIC : AccessMode.values()[stack.stackTagCompound.getByte("Access")];
+ }
+
+ public static boolean setOwner(ItemStack stack, GameProfile name) {
+
+ if (!isSecure(stack)) {
+ return false;
+ }
+ stack.setTagInfo("OwnerUUID", new NBTTagString(name.getId().toString()));
+ stack.setTagInfo("Owner", new NBTTagString(name.getName()));
+ return true;
+ }
+
+ public static GameProfile getOwner(ItemStack stack) {
+
+ if (stack.stackTagCompound != null) {
+ NBTTagCompound nbt = stack.stackTagCompound;
+
+ String uuid = nbt.getString("OwnerUUID");
+ String name = nbt.getString("Owner");
+ if (!Strings.isNullOrEmpty(uuid)) {
+ return new GameProfile(UUID.fromString(uuid), name);
+ } else if (!Strings.isNullOrEmpty(name)) {
+ return new GameProfile(UUID.fromString(PreYggdrasilConverter.func_152719_a(name)), name);
+ }
+ }
+ return UNKNOWN_GAME_PROFILE;
+ }
+
+ public static GameProfile getProfile(UUID uuid, String name) {
+
+ GameProfile owner = MinecraftServer.getServer().func_152358_ax().func_152652_a(uuid);
+ if (owner == null) {
+ GameProfile temp = new GameProfile(uuid, name);
+ owner = MinecraftServer.getServer().func_147130_as().fillProfileProperties(temp, true);
+ if (owner != temp) {
+ MinecraftServer.getServer().func_152358_ax().func_152649_a(owner);
+ }
+ }
+ return owner;
+ }
+
+ public static String getOwnerName(ItemStack stack) {
+
+ NBTTagCompound nbt = stack.stackTagCompound;
+ boolean hasUUID;
+ if (nbt == null || (!(hasUUID = nbt.hasKey("OwnerUUID")) && !nbt.hasKey("Owner"))) {
+ return "[None]";
+ }
+ return hasUUID ? stack.stackTagCompound.getString("Owner") : StringHelper.localize("info.cofh.anotherplayer");
+ }
+
+ // this class is to avoid an illegal access error from FML's event handler
+ private static class Login {
+
+ public static class S__PacketSendUUID extends Packet {
+
+ @SubscribeEvent
+ public void login(PlayerLoggedInEvent evt) {
+
+ ((EntityPlayerMP) evt.player).playerNetServerHandler.sendPacket(new S__PacketSendUUID(evt.player));
+ }
+
+ private UUID id;
+
+ public S__PacketSendUUID() {
+
+ }
+
+ public S__PacketSendUUID(EntityPlayer player) {
+
+ id = player.getGameProfile().getId();
+ }
+
+ @Override
+ public void readPacketData(PacketBuffer buffer) throws IOException {
+
+ id = new UUID(buffer.readLong(), buffer.readLong());
+ }
+
+ @Override
+ public void writePacketData(PacketBuffer buffer) throws IOException {
+
+ buffer.writeLong(id.getMostSignificantBits());
+ buffer.writeLong(id.getLeastSignificantBits());
+ }
+
+ @Override
+ public boolean hasPriority() {
+
+ return true;
+ }
+
+ @Override
+ public void processPacket(INetHandler p_148833_1_) {
+
+ cachedId = id;
+ }
+
+ }
+
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/ServerHelper.java b/src/main/java/cofh/lib/util/helpers/ServerHelper.java
new file mode 100644
index 00000000..db1d408b
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/ServerHelper.java
@@ -0,0 +1,57 @@
+package cofh.lib.util.helpers;
+
+import cpw.mods.fml.client.FMLClientHandler;
+import cpw.mods.fml.common.FMLCommonHandler;
+
+import net.minecraft.client.network.NetHandlerPlayClient;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.play.client.C08PacketPlayerBlockPlacement;
+import net.minecraft.world.World;
+
+/**
+ * Contains various helper functions to assist with determining Server/Client status.
+ *
+ * @author King Lemming
+ *
+ */
+public final class ServerHelper {
+
+ private ServerHelper() {
+
+ }
+
+ public static final boolean isClientWorld(World world) {
+
+ return world.isRemote;
+ }
+
+ public static final boolean isServerWorld(World world) {
+
+ return !world.isRemote;
+ }
+
+ public static final boolean isSinglePlayerServer() {
+
+ return FMLCommonHandler.instance().getMinecraftServerInstance() != null;
+ }
+
+ public static final boolean isMultiPlayerServer() {
+
+ return FMLCommonHandler.instance().getMinecraftServerInstance() == null;
+ }
+
+ /**
+ * This function circumvents a miserable failing.
+ */
+ public static final void sendItemUsePacket(ItemStack stack, EntityPlayer player, World world, int x, int y, int z, int hitSide, float hitX, float hitY,
+ float hitZ) {
+
+ if (isServerWorld(world)) {
+ return;
+ }
+ NetHandlerPlayClient netClientHandler = (NetHandlerPlayClient) FMLClientHandler.instance().getClientPlayHandler();
+ netClientHandler.addToSendQueue(new C08PacketPlayerBlockPlacement(x, y, z, hitSide, player.inventory.getCurrentItem(), hitX, hitY, hitZ));
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/SoundHelper.java b/src/main/java/cofh/lib/util/helpers/SoundHelper.java
new file mode 100644
index 00000000..b0ef33d7
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/SoundHelper.java
@@ -0,0 +1,43 @@
+package cofh.lib.util.helpers;
+
+import cofh.lib.audio.SoundBase;
+import cpw.mods.fml.client.FMLClientHandler;
+
+import net.minecraft.client.audio.ISound;
+import net.minecraft.client.audio.SoundHandler;
+
+/**
+ * Contains various helper functions to assist with Sound manipulation.
+ *
+ * @author King Lemming
+ *
+ */
+public class SoundHelper {
+
+ public static final SoundHandler soundManager = FMLClientHandler.instance().getClient().getSoundHandler();
+
+ private SoundHelper() {
+
+ }
+
+ /**
+ * This allows you to have some tricky functionality with Tile Entities. Just be sure you aren't dumb.
+ */
+ public static void playSound(Object sound) {
+
+ if (sound instanceof ISound) {
+ soundManager.playSound((ISound) sound);
+ }
+ }
+
+ public static void playSound(ISound sound) {
+
+ soundManager.playSound(sound);
+ }
+
+ public static void playSound(String soundName, float x, float y, float z, float volume, float pitch) {
+
+ soundManager.playSound(new SoundBase(soundName, volume, pitch, x, y, z));
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/StringHelper.java b/src/main/java/cofh/lib/util/helpers/StringHelper.java
new file mode 100644
index 00000000..45d84e3e
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/StringHelper.java
@@ -0,0 +1,268 @@
+package cofh.lib.util.helpers;
+
+import java.util.List;
+import java.util.Locale;
+
+import net.minecraft.client.gui.FontRenderer;
+import net.minecraft.item.EnumRarity;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.StatCollector;
+import net.minecraftforge.fluids.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+
+import org.lwjgl.input.Keyboard;
+
+/**
+ * Contains various helper functions to assist with String manipulation.
+ *
+ * @author King Lemming
+ *
+ */
+public final class StringHelper {
+
+ private StringHelper() {
+
+ }
+
+ /* KEY HELPERS */
+ public static boolean isAltKeyDown() {
+
+ return Keyboard.isKeyDown(Keyboard.KEY_LMENU) || Keyboard.isKeyDown(Keyboard.KEY_RMENU);
+ }
+
+ public static boolean isControlKeyDown() {
+
+ return Keyboard.isKeyDown(Keyboard.KEY_LCONTROL) || Keyboard.isKeyDown(Keyboard.KEY_RCONTROL);
+ }
+
+ public static boolean isShiftKeyDown() {
+
+ return Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT);
+ }
+
+ /* FORMAT HELPERS */
+ public static int getSplitStringHeight(FontRenderer fontRenderer, String input, int width) {
+
+ @SuppressWarnings("rawtypes")
+ List stringRows = fontRenderer.listFormattedStringToWidth(input, width);
+ return stringRows.size() * fontRenderer.FONT_HEIGHT;
+ }
+
+ public static String camelCase(String input) {
+
+ return input.substring(0, 1).toLowerCase(Locale.US) + input.substring(1);
+ }
+
+ public static String titleCase(String input) {
+
+ return input.substring(0, 1).toUpperCase(Locale.US) + input.substring(1);
+ }
+
+ public static String localize(String key) {
+
+ return StatCollector.translateToLocal(key);
+ }
+
+ public static String getKeyName(int key) {
+
+ return key < 0 ? StatCollector.translateToLocalFormatted("key.mouseButton", key + 101) : Keyboard.getKeyName(key);
+ }
+
+ public static String getFluidName(FluidStack stack) {
+
+ Fluid fluid = stack.getFluid();
+
+ String name = "" + END;
+ if (fluid.getRarity() == EnumRarity.uncommon) {
+ name += YELLOW;
+ } else if (fluid.getRarity() == EnumRarity.rare) {
+ name += BRIGHT_BLUE;
+ } else if (fluid.getRarity() == EnumRarity.epic) {
+ name += PINK;
+ }
+ name += fluid.getLocalizedName(stack) + END;
+
+ return name;
+ }
+
+ public static String getFluidName(FluidStack stack, String defaultName) {
+
+ if (stack == null) {
+ return defaultName;
+ }
+ return getFluidName(stack);
+ }
+
+ public static String getItemName(ItemStack stack) {
+
+ String name = "" + END;
+ if (stack.getRarity() == EnumRarity.uncommon) {
+ name += YELLOW;
+ } else if (stack.getRarity() == EnumRarity.rare) {
+ name += BRIGHT_BLUE;
+ } else if (stack.getRarity() == EnumRarity.epic) {
+ name += PINK;
+ }
+ name += stack.getDisplayName() + END;
+
+ return name;
+ }
+
+ public static String getScaledNumber(long number) {
+
+ if (number >= 1000000000) {
+ return number / 1000000000 + "." + (number % 1000000000 / 10000000) + "G";
+ } else if (number >= 1000000) {
+ return number / 1000000 + "." + (number % 1000000 / 10000) + "M";
+ } else if (number >= 1000) {
+ return number / 1000 + "." + (number % 1000 / 10) + "k";
+ } else {
+ return String.valueOf(number);
+ }
+ }
+
+ public static String toNumerals(short v) {
+
+ String s = "potion.potency." + v;
+ if (StatCollector.canTranslate(s))
+ return StatCollector.translateToLocal(s);
+ StringBuilder r = new StringBuilder();
+ int i = v;
+ if (i < 0) {
+ i = -i;
+ r.append('-');
+ }
+ for (Numeral k : Numeral.values) {
+ for (int j = i / k.value; j-- > 0; r.append(k.name));
+ i %= k.value;
+ }
+ return r.toString();
+ }
+
+ @Deprecated
+ public static String getScaledNumber(long number, int minDigits) {
+
+ return getScaledNumber(number);
+ }
+
+ /* ITEM TEXT HELPERS */
+ public static String getActivationText(String key) {
+
+ return BRIGHT_BLUE + localize(key) + END;
+ }
+
+ public static String getDeactivationText(String key) {
+
+ return YELLOW + localize(key) + END;
+ }
+
+ public static String getInfoText(String key) {
+
+ return BRIGHT_GREEN + localize(key) + END;
+ }
+
+ public static String getNoticeText(String key) {
+
+ return ORANGE + localize(key) + END;
+ }
+
+ public static String getFlavorText(String key) {
+
+ return LIGHT_GRAY + localize(key) + END;
+ }
+
+ public static String getRarity(int level) {
+
+ switch (level) {
+ case 2:
+ return StringHelper.YELLOW;
+ case 3:
+ return StringHelper.BRIGHT_BLUE;
+ default:
+ return StringHelper.LIGHT_GRAY;
+ }
+ }
+
+ public static String shiftForDetails() {
+
+ return LIGHT_GRAY + localize("info.cofh.hold") + " " + YELLOW + ITALIC + localize("info.cofh.shift") + " " + END + LIGHT_GRAY
+ + localize("info.cofh.forDetails") + END;
+ }
+
+ /* TUTORIAL TAB HELPERS */
+ public static String tutorialTabAugment() {
+
+ return localize("info.cofh.tutorial.tabAugment");
+ }
+
+ public static String tutorialTabConfiguration() {
+
+ return localize("info.cofh.tutorial.tabConfiguration.0");
+ }
+
+ public static String tutorialTabConfigurationEnergy() {
+
+ return localize("info.cofh.tutorial.tabConfiguration.1");
+ }
+
+ public static String tutorialTabConfigurationOperation() {
+
+ return localize("info.cofh.tutorial.tabConfiguration.2");
+ }
+
+ public static String tutorialTabRedstone() {
+
+ return localize("info.cofh.tutorial.tabRedstone");
+ }
+
+ public static String tutorialTabSecurity() {
+
+ return localize("info.cofh.tutorial.tabSecurity");
+ }
+
+ public static String tutorialTabFluxRequired() {
+
+ return localize("info.cofh.tutorial.fluxRequired");
+ }
+
+ public static final String[] ROMAN_NUMERAL = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X" };
+
+ private static enum Numeral {
+ M(1000), CM(900), D(500), CD(400), C(100), XC(90), L(50), XL(40), X(10), IX(9), V(5), IV(4), I(1);
+ public final String name = name();
+ public final int value;
+ private Numeral(int val) {
+ value = val;
+ }
+ private static final Numeral[] values = values();
+ }
+
+ /** When formatting a string, always apply color before font modification. */
+ public static final String BLACK = (char) 167 + "0";
+ public static final String BLUE = (char) 167 + "1";
+ public static final String GREEN = (char) 167 + "2";
+ public static final String TEAL = (char) 167 + "3";
+ public static final String RED = (char) 167 + "4";
+ public static final String PURPLE = (char) 167 + "5";
+ public static final String ORANGE = (char) 167 + "6";
+ public static final String LIGHT_GRAY = (char) 167 + "7";
+ public static final String GRAY = (char) 167 + "8";
+ public static final String LIGHT_BLUE = (char) 167 + "9";
+ public static final String BRIGHT_GREEN = (char) 167 + "a";
+ public static final String BRIGHT_BLUE = (char) 167 + "b";
+ public static final String LIGHT_RED = (char) 167 + "c";
+ public static final String PINK = (char) 167 + "d";
+ public static final String YELLOW = (char) 167 + "e";
+ public static final String WHITE = (char) 167 + "f";
+
+ public static final String OBFUSCATED = (char) 167 + "k";
+ public static final String BOLD = (char) 167 + "l";
+ public static final String STRIKETHROUGH = (char) 167 + "m";
+ public static final String UNDERLINE = (char) 167 + "n";
+ public static final String ITALIC = (char) 167 + "o";
+ public static final String END = (char) 167 + "r";
+
+ public static boolean displayShiftForDetail = true;
+ public static boolean displayStackCount = false;
+
+}
diff --git a/src/main/java/cofh/lib/util/helpers/package-info.java b/src/main/java/cofh/lib/util/helpers/package-info.java
new file mode 100644
index 00000000..cb78a5ac
--- /dev/null
+++ b/src/main/java/cofh/lib/util/helpers/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHLibProps.VERSION, owner = "CoFHLib", provides = "CoFHLib|util|helpers")
+package cofh.lib.util.helpers;
+
+import cofh.lib.CoFHLibProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/lib/util/package-info.java b/src/main/java/cofh/lib/util/package-info.java
new file mode 100644
index 00000000..8bc1c3db
--- /dev/null
+++ b/src/main/java/cofh/lib/util/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHLibProps.VERSION, owner = "CoFHLib", provides = "CoFHLib|util")
+package cofh.lib.util;
+
+import cofh.lib.CoFHLibProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/lib/util/position/Area.java b/src/main/java/cofh/lib/util/position/Area.java
new file mode 100644
index 00000000..5f752dfa
--- /dev/null
+++ b/src/main/java/cofh/lib/util/position/Area.java
@@ -0,0 +1,92 @@
+package cofh.lib.util.position;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import net.minecraft.util.AxisAlignedBB;
+
+public class Area {
+
+ public int xMin;
+ public int xMax;
+ public int yMin;
+ public int yMax;
+ public int zMin;
+ public int zMax;
+
+ private BlockPosition origin;
+
+ public Area(int xMin, int xMax, int yMin, int yMax, int zMin, int zMax) {
+
+ this.xMin = xMin;
+ this.xMax = xMax;
+ this.yMin = yMin;
+ this.yMax = yMax;
+ this.zMin = zMin;
+ this.zMax = zMax;
+ }
+
+ public Area(BlockPosition center, int radius, int yNegOffset, int yPosOffset) {
+
+ xMin = center.x - radius;
+ xMax = center.x + radius;
+ yMin = center.y - yNegOffset;
+ yMax = center.y + yPosOffset;
+ zMin = center.z - radius;
+ zMax = center.z + radius;
+
+ origin = center;
+ }
+
+ public BlockPosition getMin() {
+
+ return new BlockPosition(xMin, yMin, zMin);
+ }
+
+ public BlockPosition getMax() {
+
+ return new BlockPosition(xMax, yMax, zMax);
+ }
+
+ public boolean contains(BlockPosition pos) {
+
+ return pos.x >= xMin & pos.x <= xMax & pos.y >= yMin & pos.y <= yMax & pos.z >= zMin & pos.z <= zMax;
+ }
+
+ public List getPositionsTopFirst() {
+
+ ArrayList positions = new ArrayList();
+ for (int y = yMax; y >= yMin; y--) {
+ for (int x = xMin; x <= xMax; x++) {
+ for (int z = zMin; z <= zMax; z++) {
+ positions.add(new BlockPosition(x, y, z));
+ }
+ }
+ }
+ return positions;
+ }
+
+ public List getPositionsBottomFirst() {
+
+ ArrayList positions = new ArrayList();
+ for (int y = yMin; y <= yMax; y++) {
+ for (int x = xMin; x <= xMax; x++) {
+ for (int z = zMin; z <= zMax; z++) {
+ positions.add(new BlockPosition(x, y, z));
+ }
+ }
+ }
+ return positions;
+ }
+
+ public BlockPosition getOrigin() {
+
+ return origin.copy();
+ }
+
+ public AxisAlignedBB toAxisAlignedBB() {
+
+ return AxisAlignedBB.getBoundingBox(xMin, yMin, zMin, xMax + 1, yMax + 1, zMax + 1);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/position/BlockPosition.java b/src/main/java/cofh/lib/util/position/BlockPosition.java
new file mode 100644
index 00000000..1c5e5c84
--- /dev/null
+++ b/src/main/java/cofh/lib/util/position/BlockPosition.java
@@ -0,0 +1,391 @@
+package cofh.lib.util.position;
+
+import cofh.lib.util.helpers.BlockHelper;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.minecraft.block.Block;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.world.ChunkPosition;
+import net.minecraft.world.World;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraftforge.common.util.ForgeDirection;
+
+public class BlockPosition implements Comparable, Serializable {
+
+ private static final long serialVersionUID = 8671402745765780610L;
+
+ public int x;
+ public int y;
+ public int z;
+ public ForgeDirection orientation;
+
+ public BlockPosition(int x, int y, int z) {
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ orientation = ForgeDirection.UNKNOWN;
+ }
+
+ public BlockPosition(int x, int y, int z, ForgeDirection orientation) {
+
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.orientation = orientation;
+ }
+
+ public BlockPosition(BlockPosition p) {
+
+ x = p.x;
+ y = p.y;
+ z = p.z;
+ orientation = p.orientation;
+ }
+
+ public BlockPosition(NBTTagCompound tag) {
+
+ x = tag.getInteger("bp_i");
+ y = tag.getInteger("bp_j");
+ z = tag.getInteger("bp_k");
+
+ if (!tag.hasKey("bp_dir")) {
+ orientation = ForgeDirection.UNKNOWN;
+ } else {
+ orientation = ForgeDirection.getOrientation(tag.getByte("bp_dir"));
+ }
+ }
+
+ public BlockPosition(TileEntity tile) {
+
+ x = tile.xCoord;
+ y = tile.yCoord;
+ z = tile.zCoord;
+ if (tile instanceof IRotateableTile) {
+ orientation = ((IRotateableTile) tile).getDirectionFacing();
+ } else {
+ orientation = ForgeDirection.UNKNOWN;
+ }
+ }
+
+ public static BlockPosition fromRotateableTile(T te) {
+
+ return new BlockPosition(te);
+ }
+
+ public BlockPosition copy() {
+
+ return new BlockPosition(x, y, z, orientation);
+ }
+
+ public BlockPosition copy(ForgeDirection orientation) {
+
+ return new BlockPosition(x, y, z, orientation);
+ }
+
+ public BlockPosition setOrientation(ForgeDirection o) {
+
+ orientation = o;
+ return this;
+ }
+
+ public BlockPosition step(int dir) {
+
+ int[] d = BlockHelper.SIDE_COORD_MOD[dir];
+ x += d[0];
+ y += d[1];
+ z += d[2];
+ return this;
+ }
+
+ public BlockPosition step(int dir, int dist) {
+
+ int[] d = BlockHelper.SIDE_COORD_MOD[dir];
+ x += d[0] * dist;
+ y += d[1] * dist;
+ z += d[2] * dist;
+ return this;
+ }
+
+ public BlockPosition step(ForgeDirection dir) {
+
+ x += dir.offsetX;
+ y += dir.offsetY;
+ z += dir.offsetZ;
+ return this;
+ }
+
+ public BlockPosition step(ForgeDirection dir, int dist) {
+
+ x += dir.offsetX * dist;
+ y += dir.offsetY * dist;
+ z += dir.offsetZ * dist;
+ return this;
+ }
+
+ public BlockPosition moveForwards(int step) {
+
+ switch (orientation) {
+ case UP:
+ y = y + step;
+ break;
+ case DOWN:
+ y = y - step;
+ break;
+ case SOUTH:
+ z = z + step;
+ break;
+ case NORTH:
+ z = z - step;
+ break;
+ case EAST:
+ x = x + step;
+ break;
+ case WEST:
+ x = x - step;
+ break;
+ default:
+ }
+ return this;
+ }
+
+ public BlockPosition moveBackwards(int step) {
+
+ return moveForwards(-step);
+ }
+
+ public BlockPosition moveRight(int step) {
+
+ switch (orientation) {
+ case UP:
+ case SOUTH:
+ x = x - step;
+ break;
+ case DOWN:
+ case NORTH:
+ x = x + step;
+ break;
+ case EAST:
+ z = z + step;
+ break;
+ case WEST:
+ z = z - step;
+ break;
+ default:
+ break;
+ }
+ return this;
+ }
+
+ public BlockPosition moveLeft(int step) {
+
+ return moveRight(-step);
+ }
+
+ public BlockPosition moveUp(int step) {
+
+ switch (orientation) {
+ case EAST:
+ case WEST:
+ case NORTH:
+ case SOUTH:
+ y = y + step;
+ break;
+ case UP:
+ z = z - step;
+ break;
+ case DOWN:
+ z = z + step;
+ default:
+ break;
+ }
+ return this;
+ }
+
+ public BlockPosition moveDown(int step) {
+
+ return moveUp(-step);
+ }
+
+ public void writeToNBT(NBTTagCompound tag) {
+
+ tag.setInteger("bp_i", x);
+ tag.setInteger("bp_j", y);
+ tag.setInteger("bp_k", z);
+ tag.setByte("bp_dir", (byte) orientation.ordinal());
+ }
+
+ @Override
+ public String toString() {
+
+ if (orientation == null) {
+ return "{" + x + ", " + y + ", " + z + "}";
+ }
+ return "{" + x + ", " + y + ", " + z + ";" + orientation.toString() + "}";
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+
+ if (!(obj instanceof BlockPosition)) {
+ return false;
+ }
+ BlockPosition bp = (BlockPosition) obj;
+ return bp.x == x & bp.y == y & bp.z == z & bp.orientation == orientation;
+ }
+
+ // so compiler will optimize
+ public boolean equals(BlockPosition bp) {
+
+ return bp != null && bp.x == x & bp.y == y & bp.z == z & bp.orientation == orientation;
+ }
+
+ @Override
+ public int hashCode() {
+
+ return (x & 0xFFF) | (y & 0xFF << 8) | (z & 0xFFF << 12);
+ }
+
+ public BlockPosition min(BlockPosition p) {
+
+ return new BlockPosition(p.x > x ? x : p.x, p.y > y ? y : p.y, p.z > z ? z : p.z);
+ }
+
+ public BlockPosition max(BlockPosition p) {
+
+ return new BlockPosition(p.x < x ? x : p.x, p.y < y ? y : p.y, p.z < z ? z : p.z);
+ }
+
+ public List getAdjacent(boolean includeVertical) {
+
+ List a = new ArrayList(4 + (includeVertical ? 2 : 0));
+ a.add(copy(ForgeDirection.EAST).moveForwards(1));
+ a.add(copy(ForgeDirection.WEST).moveForwards(1));
+ a.add(copy(ForgeDirection.SOUTH).moveForwards(1));
+ a.add(copy(ForgeDirection.NORTH).moveForwards(1));
+ if (includeVertical) {
+ a.add(copy(ForgeDirection.UP).moveForwards(1));
+ a.add(copy(ForgeDirection.DOWN).moveForwards(1));
+ }
+ return a;
+ }
+
+ public boolean blockExists(World world) {
+
+ return world.blockExists(x, y, z);
+ }
+
+ public TileEntity getTileEntity(World world) {
+
+ return world.getTileEntity(x, y, z);
+ }
+
+ public Block getBlock(World world) {
+
+ return world.getBlock(x, y, z);
+ }
+
+ @SuppressWarnings("unchecked")
+ public T getTileEntity(World world, Class targetClass) {
+
+ TileEntity te = world.getTileEntity(x, y, z);
+ if (targetClass.isInstance(te)) {
+ return (T) te;
+ } else {
+ return null;
+ }
+ }
+
+ public static ForgeDirection getDirection(int xS, int yS, int zS, int x, int y, int z) {
+
+ int dir = 0;
+ if (y < yS) {
+ dir |= 1;
+ } else if (y != yS) {
+ dir |= 2;
+ }
+ if (z < zS) {
+ dir |= 4;
+ } else if (z != zS) {
+ dir |= 8;
+ }
+ if (x < xS) {
+ dir |= 16;
+ } else if (x != xS) {
+ dir |= 32;
+ }
+ switch (dir) {
+ case 2:
+ return ForgeDirection.UP;
+ case 1:
+ return ForgeDirection.DOWN;
+ case 4:
+ return ForgeDirection.WEST;
+ case 8:
+ return ForgeDirection.EAST;
+ case 16:
+ return ForgeDirection.NORTH;
+ case 32:
+ return ForgeDirection.SOUTH;
+ default:
+ return ForgeDirection.UNKNOWN;
+ }
+ }
+
+ public static TileEntity getTileEntityRaw(World world, int x, int y, int z) {
+
+ if (!world.blockExists(x, y, z)) {
+ return null;
+ }
+ ChunkPosition chunkposition = new ChunkPosition(x & 15, y, z & 15);
+ Chunk chunk = world.getChunkFromBlockCoords(x, z);
+ TileEntity tileentity = (TileEntity)chunk.chunkTileEntityMap.get(chunkposition);
+ return tileentity == null || tileentity.isInvalid() ? null : tileentity;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static T getTileEntityRaw(World world, int x, int y, int z, Class targetClass) {
+
+ TileEntity te = getTileEntityRaw(world, x, y, z);
+ if (targetClass.isInstance(te)) {
+ return (T) te;
+ } else {
+ return null;
+ }
+ }
+
+ public static boolean blockExists(TileEntity start, ForgeDirection dir) {
+
+ final int x = start.xCoord + dir.offsetX, y = start.yCoord + dir.offsetY, z = start.zCoord + dir.offsetZ;
+ return start.getWorldObj().blockExists(x, y, z);
+ }
+
+ public static TileEntity getAdjacentTileEntity(TileEntity start, ForgeDirection dir) {
+
+ final int x = start.xCoord + dir.offsetX, y = start.yCoord + dir.offsetY, z = start.zCoord + dir.offsetZ;
+ return getTileEntityRaw(start.getWorldObj(), x, y, z);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static T getAdjacentTileEntity(TileEntity start, ForgeDirection direction, Class targetClass) {
+
+ TileEntity te = getAdjacentTileEntity(start, direction);
+ if (targetClass.isInstance(te)) {
+ return (T) te;
+ } else {
+ return null;
+ }
+ }
+
+ /* Comparable */
+ @Override
+ public int compareTo(BlockPosition other) {
+
+ return this.x == other.x ? this.y == other.y ? this.z - other.z : this.y - other.y : this.x - other.x;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/position/ChunkCoord.java b/src/main/java/cofh/lib/util/position/ChunkCoord.java
new file mode 100644
index 00000000..018d3fa5
--- /dev/null
+++ b/src/main/java/cofh/lib/util/position/ChunkCoord.java
@@ -0,0 +1,110 @@
+package cofh.lib.util.position;
+
+import cofh.lib.util.helpers.BlockHelper;
+
+import java.io.Serializable;
+
+import net.minecraft.world.chunk.Chunk;
+
+/**
+ * Standardized implementation for representing and manipulating Chunk Coordinates. Provides standard Java Collection interaction.
+ *
+ * @author King Lemming
+ *
+ */
+public final class ChunkCoord implements Comparable, Serializable {
+
+ private static final long serialVersionUID = -9154178151445196959L;
+
+ public int chunkX;
+ public int chunkZ;
+
+ public ChunkCoord(Chunk chunk) {
+
+ this.chunkX = chunk.xPosition;
+ this.chunkZ = chunk.zPosition;
+ }
+
+ public ChunkCoord(BlockPosition c) {
+
+ this(c.x >> 4, c.z >> 4);
+ }
+
+ public ChunkCoord(int x, int z) {
+
+ this.chunkX = x;
+ this.chunkZ = z;
+ }
+
+ public int getCenterX() {
+
+ return (this.chunkX << 4) + 8;
+ }
+
+ public int getCenterZ() {
+
+ return (this.chunkZ << 4) + 8;
+ }
+
+ public void step(int dir) {
+
+ chunkX += BlockHelper.SIDE_COORD_MOD[dir][0];
+ chunkZ += BlockHelper.SIDE_COORD_MOD[dir][2];
+ }
+
+ public void step(int dir, int dist) {
+
+ switch (dir) {
+ case 2:
+ this.chunkZ -= dist;
+ break;
+ case 3:
+ this.chunkZ += dist;
+ break;
+ case 4:
+ this.chunkX -= dist;
+ break;
+ case 5:
+ this.chunkX += dist;
+ break;
+ default:
+ }
+ }
+
+ public ChunkCoord copy() {
+
+ return new ChunkCoord(chunkX, chunkZ);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+
+ if (!(obj instanceof ChunkCoord)) {
+ return false;
+ }
+ ChunkCoord other = (ChunkCoord) obj;
+ return this.chunkX == other.chunkX && this.chunkZ == other.chunkZ;
+ }
+
+ @Override
+ public int hashCode() {
+
+ int hash = chunkX;
+ hash *= 31 + this.chunkZ;
+ return hash;
+ }
+
+ @Override
+ public String toString() {
+
+ return "[" + this.chunkX + ", " + this.chunkZ + "]";
+ }
+
+ /* Comparable */
+ @Override
+ public int compareTo(ChunkCoord other) {
+
+ return this.chunkX == other.chunkX ? this.chunkZ - other.chunkZ : this.chunkX - other.chunkX;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/util/position/IRotateableTile.java b/src/main/java/cofh/lib/util/position/IRotateableTile.java
new file mode 100644
index 00000000..24661426
--- /dev/null
+++ b/src/main/java/cofh/lib/util/position/IRotateableTile.java
@@ -0,0 +1,17 @@
+package cofh.lib.util.position;
+
+import net.minecraftforge.common.util.ForgeDirection;
+
+public interface IRotateableTile {
+
+ public boolean canRotate();
+
+ public boolean canRotate(ForgeDirection axis);
+
+ public void rotate(ForgeDirection axis);
+
+ public void rotateDirectlyTo(int facing);
+
+ public ForgeDirection getDirectionFacing();
+
+}
diff --git a/src/main/java/cofh/lib/util/position/package-info.java b/src/main/java/cofh/lib/util/position/package-info.java
new file mode 100644
index 00000000..c7daa4bd
--- /dev/null
+++ b/src/main/java/cofh/lib/util/position/package-info.java
@@ -0,0 +1,10 @@
+/**
+ * (C) 2014 Team CoFH / CoFH / Cult of the Full Hub
+ * http://www.teamcofh.com
+ */
+@API(apiVersion = CoFHLibProps.VERSION, owner = "CoFHLib", provides = "CoFHLib|util|position")
+package cofh.lib.util.position;
+
+import cofh.lib.CoFHLibProps;
+import cpw.mods.fml.common.API;
+
diff --git a/src/main/java/cofh/lib/world/WorldGenAdvLakes.java b/src/main/java/cofh/lib/world/WorldGenAdvLakes.java
new file mode 100644
index 00000000..6aecceb4
--- /dev/null
+++ b/src/main/java/cofh/lib/world/WorldGenAdvLakes.java
@@ -0,0 +1,162 @@
+package cofh.lib.world;
+
+import static cofh.lib.world.WorldGenMinableCluster.*;
+
+import cofh.lib.util.WeightedRandomBlock;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+import net.minecraft.block.material.Material;
+import net.minecraft.init.Blocks;
+import net.minecraft.world.EnumSkyBlock;
+import net.minecraft.world.World;
+import net.minecraft.world.biome.BiomeGenBase;
+import net.minecraft.world.gen.feature.WorldGenerator;
+
+public class WorldGenAdvLakes extends WorldGenerator {
+
+ private static final List GAP_BLOCK = Arrays.asList(new WeightedRandomBlock(Blocks.air, 0));
+ private final List cluster;
+ private final WeightedRandomBlock[] genBlock;
+ public List outlineBlock = null;
+ public List gapBlock = GAP_BLOCK;
+ public boolean solidOutline = false;
+ public boolean totalOutline = false;
+ public int width = 16;
+ public int height = 8;
+
+ public WorldGenAdvLakes(List resource, List block) {
+
+ cluster = resource;
+ if (block == null) {
+ genBlock = null;
+ } else {
+ genBlock = block.toArray(new WeightedRandomBlock[block.size()]);
+ }
+ }
+
+ @Override
+ public boolean generate(World world, Random rand, int xStart, int yStart, int zStart) {
+
+ int widthOff = width / 2;
+ xStart -= widthOff;
+ zStart -= widthOff;
+
+ int heightOff = height / 2 + 1;
+
+ while (yStart > heightOff && world.isAirBlock(xStart, yStart, zStart)) {
+ --yStart;
+ }
+ --heightOff;
+ if (yStart <= heightOff) {
+ return false;
+ }
+
+ yStart -= heightOff;
+ boolean[] spawnBlock = new boolean[width * width * height];
+
+ int W = width - 1, H = height - 1;
+
+ for (int i = 0, e = rand.nextInt(4) + 4; i < e; ++i) {
+ double xSize = rand.nextDouble() * 6.0D + 3.0D;
+ double ySize = rand.nextDouble() * 4.0D + 2.0D;
+ double zSize = rand.nextDouble() * 6.0D + 3.0D;
+ double xCenter = rand.nextDouble() * (width - xSize - 2.0D) + 1.0D + xSize / 2.0D;
+ double yCenter = rand.nextDouble() * (height - ySize - 4.0D) + 2.0D + ySize / 2.0D;
+ double zCenter = rand.nextDouble() * (width - zSize - 2.0D) + 1.0D + zSize / 2.0D;
+
+ for (int x = 1; x < W; ++x) {
+ for (int z = 1; z < W; ++z) {
+ for (int y = 1; y < H; ++y) {
+ double xDist = (x - xCenter) / (xSize / 2.0D);
+ double yDist = (y - yCenter) / (ySize / 2.0D);
+ double zDist = (z - zCenter) / (zSize / 2.0D);
+ double dist = xDist * xDist + yDist * yDist + zDist * zDist;
+
+ if (dist < 1.0D) {
+ spawnBlock[(x * width + z) * height + y] = true;
+ }
+ }
+ }
+ }
+ }
+
+ int x;
+ int y;
+ int z;
+
+ for (x = 0; x < width; ++x) {
+ for (z = 0; z < width; ++z) {
+ for (y = 0; y < height; ++y) {
+ boolean flag = spawnBlock[(x * width + z) * height + y]
+ || ((x < W && spawnBlock[((x + 1) * width + z) * height + y]) || (x > 0 && spawnBlock[((x - 1) * width + z) * height + y])
+ || (z < W && spawnBlock[(x * width + (z + 1)) * height + y]) || (z > 0 && spawnBlock[(x * width + (z - 1)) * height + y])
+ || (y < H && spawnBlock[(x * width + z) * height + (y + 1)]) || (y > 0 && spawnBlock[(x * width + z) * height + (y - 1)]));
+
+ if (flag) {
+ if (y >= heightOff) {
+ Material material = world.getBlock(xStart + x, yStart + y, zStart + z).getMaterial();
+ if (material.isLiquid()) {
+ return false;
+ }
+ } else {
+ if (!canGenerateInBlock(world, xStart + x, yStart + y, zStart + z, genBlock)) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ for (x = 0; x < width; ++x) {
+ for (z = 0; z < width; ++z) {
+ for (y = 0; y < height; ++y) {
+ if (spawnBlock[(x * width + z) * height + y]) {
+ if (y < heightOff) {
+ generateBlock(world, xStart + x, yStart + y, zStart + z, genBlock, cluster);
+ } else if (canGenerateInBlock(world, xStart + x, yStart + y, zStart + z, genBlock)) {
+ generateBlock(world, xStart + x, yStart + y, zStart + z, gapBlock);
+ }
+ }
+ }
+ }
+ }
+
+ for (x = 0; x < width; ++x) {
+ for (z = 0; z < width; ++z) {
+ for (y = 0; y < height; ++y) {
+ if (spawnBlock[(x * width + z) * height + y] && world.getBlock(xStart + x, yStart + y - 1, zStart + z).equals(Blocks.dirt)
+ && world.getSavedLightValue(EnumSkyBlock.Sky, xStart + x, yStart + y, zStart + z) > 0) {
+ BiomeGenBase bgb = world.getBiomeGenForCoords(xStart + x, zStart + z);
+ world.setBlock(xStart + x, yStart + y - 1, zStart + z, bgb.topBlock, bgb.field_150604_aj, 2);
+ }
+ }
+ }
+ }
+
+ if (outlineBlock != null) {
+ for (x = 0; x < width; ++x) {
+ for (z = 0; z < width; ++z) {
+ for (y = 0; y < height; ++y) {
+ boolean flag = !spawnBlock[(x * width + z) * height + y]
+ && ((x < W && spawnBlock[((x + 1) * width + z) * height + y]) || (x > 0 && spawnBlock[((x - 1) * width + z) * height + y])
+ || (z < W && spawnBlock[(x * width + (z + 1)) * height + y])
+ || (z > 0 && spawnBlock[(x * width + (z - 1)) * height + y])
+ || (y < H && spawnBlock[(x * width + z) * height + (y + 1)]) || (y > 0 && spawnBlock[(x * width + z) * height + (y - 1)]));
+
+ if (flag && (solidOutline | y < heightOff || rand.nextInt(2) != 0)
+ && (totalOutline || world.getBlock(xStart + x, yStart + y, zStart + z).getMaterial().isSolid())) {
+ generateBlock(world, xStart + x, yStart + y, zStart + z, outlineBlock);
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+
+ }
+}
diff --git a/src/main/java/cofh/lib/world/WorldGenBoulder.java b/src/main/java/cofh/lib/world/WorldGenBoulder.java
new file mode 100644
index 00000000..e9909291
--- /dev/null
+++ b/src/main/java/cofh/lib/world/WorldGenBoulder.java
@@ -0,0 +1,85 @@
+package cofh.lib.world;
+
+import static cofh.lib.world.WorldGenMinableCluster.*;
+
+import cofh.lib.util.WeightedRandomBlock;
+
+import java.util.List;
+import java.util.Random;
+
+import net.minecraft.world.World;
+import net.minecraft.world.gen.feature.WorldGenerator;
+
+public class WorldGenBoulder extends WorldGenerator {
+
+ private final List cluster;
+ private final WeightedRandomBlock[] genBlock;
+ private final int size;
+ public int sizeVariance = 2;
+ public int clusters = 3;
+ public int clusterVariance = 0;
+ public boolean hollow = false;
+ public float hollowAmt = 0.1665f;
+ public float hollowVar = 0;
+
+ public WorldGenBoulder(List resource, int minSize, List block) {
+
+ cluster = resource;
+ size = minSize;
+ genBlock = block.toArray(new WeightedRandomBlock[block.size()]);
+ }
+
+ @Override
+ public boolean generate(World world, Random rand, int xCenter, int yCenter, int zCenter) {
+
+ final int minSize = size, var = sizeVariance;
+ boolean r = false;
+ int i = clusterVariance > 0 ? clusters + rand.nextInt(clusterVariance + 1) : clusters;
+ while (i-- > 0) {
+
+ while (yCenter > minSize && world.isAirBlock(xCenter, yCenter - 1, zCenter)) {
+ --yCenter;
+ }
+ if (yCenter <= (minSize + var + 1)) {
+ return false;
+ }
+
+ if (canGenerateInBlock(world, xCenter, yCenter - 1, zCenter, genBlock)) {
+
+ int xWidth = minSize + (var > 1 ? rand.nextInt(var) : 0);
+ int yWidth = minSize + (var > 1 ? rand.nextInt(var) : 0);
+ int zWidth = minSize + (var > 1 ? rand.nextInt(var) : 0);
+ float maxDist = (xWidth + yWidth + zWidth) * 0.333F + 0.5F;
+ maxDist *= maxDist;
+ float minDist = hollow ? (xWidth + yWidth + zWidth) * (hollowAmt * (1 - rand.nextFloat() * hollowVar)) : 0;
+ minDist *= minDist;
+
+ for (int x = -xWidth; x <= xWidth; ++x) {
+ final int xDist = x * x;
+
+ for (int z = -zWidth; z <= zWidth; ++z) {
+ final int xzDist = xDist + z * z;
+
+ for (int y = -yWidth; y <= yWidth; ++y) {
+ final int dist = xzDist + y * y;
+
+ if (dist <= maxDist) {
+ if (dist >= minDist) {
+ r |= generateBlock(world, xCenter + x, yCenter + y, zCenter + z, cluster);
+ } else {
+ r |= world.setBlockToAir(xCenter + x, yCenter + y, zCenter + z);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ xCenter += rand.nextInt(var + minSize * 2) - (minSize + var / 2);
+ zCenter += rand.nextInt(var + minSize * 2) - (minSize + var / 2);
+ yCenter += rand.nextInt((var + 1) * 3) - (var + 1);
+ }
+
+ return r;
+ }
+}
diff --git a/src/main/java/cofh/lib/world/WorldGenDecoration.java b/src/main/java/cofh/lib/world/WorldGenDecoration.java
new file mode 100644
index 00000000..ac5230d4
--- /dev/null
+++ b/src/main/java/cofh/lib/world/WorldGenDecoration.java
@@ -0,0 +1,69 @@
+package cofh.lib.world;
+
+import static cofh.lib.world.WorldGenMinableCluster.*;
+
+import cofh.lib.util.WeightedRandomBlock;
+
+import java.util.List;
+import java.util.Random;
+
+import net.minecraft.world.World;
+import net.minecraft.world.gen.feature.WorldGenerator;
+
+public class WorldGenDecoration extends WorldGenerator {
+
+ private final List cluster;
+ private final WeightedRandomBlock[] genBlock;
+ private final WeightedRandomBlock[] onBlock;
+ private final int clusterSize;
+ public boolean seeSky = true;
+ public boolean checkStay = true;
+ public int stackHeight = 1;
+ public int xVar = 8;
+ public int yVar = 4;
+ public int zVar = 8;
+
+ public WorldGenDecoration(List blocks, int count, List material, List on) {
+
+ cluster = blocks;
+ clusterSize = count;
+ genBlock = material == null ? null : material.toArray(new WeightedRandomBlock[material.size()]);
+ onBlock = on == null ? null : on.toArray(new WeightedRandomBlock[on.size()]);
+ }
+
+ @Override
+ public boolean generate(World world, Random rand, int xStart, int yStart, int zStart) {
+
+ boolean r = false;
+ for (int l = clusterSize; l-- > 0;) {
+ int x = xStart + rand.nextInt(xVar) - rand.nextInt(xVar);
+ int y = yStart + (yVar > 1 ? rand.nextInt(yVar) - rand.nextInt(yVar) : 0);
+ int z = zStart + rand.nextInt(zVar) - rand.nextInt(zVar);
+
+ if (!world.blockExists(x, y, z)) {
+ ++l;
+ continue;
+ }
+
+ if ((!seeSky || world.canBlockSeeTheSky(x, y, z)) && canGenerateInBlock(world, x, y - 1, z, onBlock)
+ && canGenerateInBlock(world, x, y, z, genBlock)) {
+
+ WeightedRandomBlock block = selectBlock(world, cluster);
+ int stack = stackHeight > 1 ? rand.nextInt(stackHeight) : 0;
+ do {
+ if (!checkStay || block.block.canBlockStay(world, x, y, z)) {
+ r |= world.setBlock(x, y, z, block.block, block.metadata, 2);
+ } else {
+ break;
+ }
+ ++y;
+ if (!canGenerateInBlock(world, x, y, z, genBlock)) {
+ break;
+ }
+ } while (stack-- > 0);
+ }
+ }
+ return r;
+ }
+
+}
diff --git a/src/main/java/cofh/lib/world/WorldGenDungeon.java b/src/main/java/cofh/lib/world/WorldGenDungeon.java
new file mode 100644
index 00000000..6c2dd4c5
--- /dev/null
+++ b/src/main/java/cofh/lib/world/WorldGenDungeon.java
@@ -0,0 +1,176 @@
+package cofh.lib.world;
+
+import static cofh.lib.world.WorldGenMinableCluster.*;
+import static java.lang.Math.abs;
+
+import cofh.lib.util.WeightedRandomBlock;
+import cofh.lib.util.WeightedRandomNBTTag;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+import net.minecraft.init.Blocks;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntityChest;
+import net.minecraft.tileentity.TileEntityMobSpawner;
+import net.minecraft.util.WeightedRandom;
+import net.minecraft.util.WeightedRandomChestContent;
+import net.minecraft.world.World;
+import net.minecraft.world.gen.feature.WorldGenerator;
+import net.minecraftforge.common.ChestGenHooks;
+import net.minecraftforge.common.DungeonHooks.DungeonMob;
+
+public class WorldGenDungeon extends WorldGenerator {
+
+ private final List walls;
+ private final WeightedRandomBlock[] genBlock;
+ private final WeightedRandomNBTTag[] spawners;
+ public int minWidthX = 2, maxWidthX = 3;
+ public int minWidthZ = 2, maxWidthZ = 3;
+ public int minHeight = 3, maxHeight = 3;
+ public int minHoles = 1, maxHoles = 5;
+ public int maxChests = 2, maxChestTries = 3;
+ public List lootTables = Arrays.asList(new DungeonMob(100, ChestGenHooks.DUNGEON_CHEST));
+ public List floor;
+
+ public WorldGenDungeon(List blocks, List material, List mobs) {
+
+ walls = blocks;
+ floor = walls;
+ spawners = mobs.toArray(new WeightedRandomNBTTag[mobs.size()]);
+ genBlock = material.toArray(new WeightedRandomBlock[material.size()]);
+ }
+
+ @Override
+ public boolean generate(World world, Random rand, int xStart, int yStart, int zStart) {
+
+ if (yStart <= 2) {
+ return false;
+ }
+
+ int height = nextInt(rand, maxHeight - minHeight + 1) + minHeight;
+ int xWidth = nextInt(rand, maxWidthX - minWidthX + 1) + minWidthX;
+ int zWidth = nextInt(rand, maxWidthZ - minWidthZ + 1) + minWidthZ;
+ int holes = 0;
+ int x, y, z;
+
+ int floor = yStart - 1, ceiling = yStart + height + 1;
+
+ for (x = xStart - xWidth - 1; x <= xStart + xWidth + 1; ++x) {
+ for (z = zStart - zWidth - 1; z <= zStart + zWidth + 1; ++z) {
+ for (y = floor; y <= ceiling; ++y) {
+
+ if (y == floor && !canGenerateInBlock(world, x, y, z, genBlock)) {
+ return false;
+ }
+
+ if (y == ceiling && !canGenerateInBlock(world, x, y, z, genBlock)) {
+ return false;
+ }
+
+ if ((abs(x - xStart) == xWidth + 1 || abs(z - zStart) == zWidth + 1) && y == yStart && world.isAirBlock(x, y, z)
+ && world.isAirBlock(x, y + 1, z)) {
+ ++holes;
+ }
+ }
+ }
+ }
+
+ if (holes < minHoles || holes > maxHoles) {
+ return false;
+ }
+
+ NBTTagCompound tag = (NBTTagCompound) ((WeightedRandomNBTTag) WeightedRandom.getRandomItem(rand, spawners)).tag;
+ ChestGenHooks table = ChestGenHooks.getInfo(((DungeonMob) WeightedRandom.getRandomItem(rand, lootTables)).type);
+
+ for (x = xStart - xWidth - 1; x <= xStart + xWidth + 1; ++x) {
+ for (z = zStart - zWidth - 1; z <= zStart + zWidth + 1; ++z) {
+ for (y = yStart + height; y >= floor; --y) {
+
+ l: if (y != floor) {
+ if ((abs(x - xStart) != xWidth + 1 && abs(z - zStart) != zWidth + 1)) {
+ world.setBlockToAir(x, y, z);
+ } else if (y >= 0 && !canGenerateInBlock(world, x, y - 1, z, genBlock)) {
+ world.setBlockToAir(x, y, z);
+ } else {
+ break l;
+ }
+ continue;
+ }
+ if (canGenerateInBlock(world, x, y, z, genBlock)) {
+ if (y == floor) {
+ generateBlock(world, x, y, z, this.floor);
+ } else {
+ generateBlock(world, x, y, z, walls);
+ }
+ }
+ }
+ }
+ }
+
+ for (int i = maxChests; i-- > 0;) {
+ for (int j = maxChestTries; j-- > 0;) {
+ x = xStart + nextInt(rand, xWidth * 2 + 1) - xWidth;
+ z = zStart + nextInt(rand, zWidth * 2 + 1) - zWidth;
+
+ if (world.isAirBlock(x, yStart, z)) {
+ int walls = 0;
+
+ if (isWall(world, x - 1, yStart, z)) {
+ ++walls;
+ }
+
+ if (isWall(world, x + 1, yStart, z)) {
+ ++walls;
+ }
+
+ if (isWall(world, x, yStart, z - 1)) {
+ ++walls;
+ }
+
+ if (isWall(world, x, yStart, z + 1)) {
+ ++walls;
+ }
+
+ if (walls >= 1 && walls <= 2) {
+ world.setBlock(x, yStart, z, Blocks.chest, 0, 2);
+ TileEntityChest chest = (TileEntityChest) world.getTileEntity(x, yStart, z);
+
+ if (chest != null) {
+ WeightedRandomChestContent.generateChestContents(rand, table.getItems(rand), chest, table.getCount(rand));
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ world.setBlock(xStart, yStart, zStart, Blocks.mob_spawner, 0, 2);
+ TileEntityMobSpawner spawner = (TileEntityMobSpawner) world.getTileEntity(xStart, yStart, zStart);
+
+ if (spawner != null) {
+ spawner.func_145881_a().readFromNBT(tag);
+ } else {
+ System.err.println("Failed to fetch mob spawner entity at (" + xStart + ", " + yStart + ", " + zStart + ")");
+ }
+
+ return true;
+ }
+
+ private static int nextInt(Random rand, int v) {
+
+ if (v <= 1) {
+ return 0;
+ }
+ return rand.nextInt(v);
+ }
+
+ private boolean isWall(World world, int x, int y, int z) {
+
+ int metadata = world.getBlockMetadata(x, y, z);
+ return WeightedRandomBlock.isBlockContained(world.getBlock(x, y, z), metadata, walls);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/world/WorldGenGeode.java b/src/main/java/cofh/lib/world/WorldGenGeode.java
new file mode 100644
index 00000000..a5b50b74
--- /dev/null
+++ b/src/main/java/cofh/lib/world/WorldGenGeode.java
@@ -0,0 +1,133 @@
+package cofh.lib.world;
+
+import static cofh.lib.world.WorldGenMinableCluster.*;
+
+import cofh.lib.util.WeightedRandomBlock;
+
+import java.util.List;
+import java.util.Random;
+
+import net.minecraft.world.World;
+import net.minecraft.world.gen.feature.WorldGenerator;
+
+public class WorldGenGeode extends WorldGenerator {
+
+ private final List cluster;
+ private final List outline;
+ private final WeightedRandomBlock[] genBlock;
+ public List fillBlock = null;
+ public boolean hollow = false;
+ public int width = 16;
+ public int height = 8;
+
+ public WorldGenGeode(List resource, List material, List cover) {
+
+ cluster = resource;
+ genBlock = material.toArray(new WeightedRandomBlock[material.size()]);
+ outline = cover;
+ }
+
+ @Override
+ public boolean generate(World world, Random rand, int xStart, int yStart, int zStart) {
+
+ int heightOff = height / 2;
+ int widthOff = width / 2;
+ xStart -= widthOff;
+ zStart -= widthOff;
+
+ if (yStart <= heightOff) {
+ return false;
+ }
+
+ yStart -= heightOff;
+ boolean[] spawnBlock = new boolean[width * width * height];
+ boolean[] hollowBlock = new boolean[width * width * height];
+
+ int W = width - 1, H = height - 1;
+
+ for (int i = 0, e = rand.nextInt(4) + 4; i < e; ++i) {
+ double xSize = rand.nextDouble() * 6.0D + 3.0D;
+ double ySize = rand.nextDouble() * 4.0D + 2.0D;
+ double zSize = rand.nextDouble() * 6.0D + 3.0D;
+ double xCenter = rand.nextDouble() * (width - xSize - 2.0D) + 1.0D + xSize / 2.0D;
+ double yCenter = rand.nextDouble() * (height - ySize - 4.0D) + 2.0D + ySize / 2.0D;
+ double zCenter = rand.nextDouble() * (width - zSize - 2.0D) + 1.0D + zSize / 2.0D;
+ double minDist = hollow ? rand.nextGaussian() * 0.15 + 0.4 : 0;
+
+ for (int x = 1; x < W; ++x) {
+ for (int z = 1; z < W; ++z) {
+ for (int y = 1; y < H; ++y) {
+ double xDist = (x - xCenter) / (xSize / 2.0D);
+ double yDist = (y - yCenter) / (ySize / 2.0D);
+ double zDist = (z - zCenter) / (zSize / 2.0D);
+ double dist = xDist * xDist + yDist * yDist + zDist * zDist;
+
+ if (dist < 1.0D) {
+ spawnBlock[(x * width + z) * height + y] = hollow ? dist > minDist : true;
+ }
+ if (hollow) {
+ hollowBlock[(x * width + z) * height + y] = dist <= minDist;
+ }
+ }
+ }
+ }
+ }
+
+ int x;
+ int y;
+ int z;
+
+ for (x = 0; x < width; ++x) {
+ for (z = 0; z < width; ++z) {
+ for (y = 0; y < height; ++y) {
+ boolean flag = (fillBlock != null && hollowBlock[(x * width + z) * height + y])
+ || spawnBlock[(x * width + z) * height + y]
+ || ((x < W && spawnBlock[((x + 1) * width + z) * height + y]) || (x > 0 && spawnBlock[((x - 1) * width + z) * height + y])
+ || (z < W && spawnBlock[(x * width + (z + 1)) * height + y]) || (z > 0 && spawnBlock[(x * width + (z - 1)) * height + y])
+ || (y < H && spawnBlock[(x * width + z) * height + (y + 1)]) || (y > 0 && spawnBlock[(x * width + z) * height + (y - 1)]));
+
+ if (flag && !canGenerateInBlock(world, xStart + x, yStart + y, zStart + z, genBlock)) {
+ return false;
+ }
+ }
+ }
+ }
+
+ boolean r = false;
+ for (x = 0; x < width; ++x) {
+ for (z = 0; z < width; ++z) {
+ for (y = 0; y < height; ++y) {
+ if (spawnBlock[(x * width + z) * height + y]) {
+ boolean t = generateBlock(world, xStart + x, yStart + y, zStart + z, cluster);
+ r |= t;
+ if (!t) {
+ spawnBlock[(x * width + z) * height + y] = false;
+ }
+ }
+ }
+ }
+ }
+
+ for (x = 0; x < width; ++x) {
+ for (z = 0; z < width; ++z) {
+ for (y = 0; y < height; ++y) {
+ if (fillBlock != null && hollowBlock[(x * width + z) * height + y]) {
+ r |= generateBlock(world, xStart + x, yStart + y, zStart + z, fillBlock);
+ } else {
+ boolean flag = !spawnBlock[(x * width + z) * height + y]
+ && ((x < W && spawnBlock[((x + 1) * width + z) * height + y]) || (x > 0 && spawnBlock[((x - 1) * width + z) * height + y])
+ || (z < W && spawnBlock[(x * width + (z + 1)) * height + y])
+ || (z > 0 && spawnBlock[(x * width + (z - 1)) * height + y])
+ || (y < H && spawnBlock[(x * width + z) * height + (y + 1)]) || (y > 0 && spawnBlock[(x * width + z) * height + (y - 1)]));
+
+ if (flag) {
+ r |= generateBlock(world, xStart + x, yStart + y, zStart + z, outline);
+ }
+ }
+ }
+ }
+ }
+
+ return r;
+ }
+}
diff --git a/src/main/java/cofh/lib/world/WorldGenMassiveTree.java b/src/main/java/cofh/lib/world/WorldGenMassiveTree.java
new file mode 100644
index 00000000..cc7b6629
--- /dev/null
+++ b/src/main/java/cofh/lib/world/WorldGenMassiveTree.java
@@ -0,0 +1,655 @@
+package cofh.lib.world;
+
+import static cofh.lib.world.WorldGenMinableCluster.*;
+
+import cofh.lib.util.WeightedRandomBlock;
+
+import gnu.trove.iterator.TLongObjectIterator;
+import gnu.trove.map.hash.TLongObjectHashMap;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockSapling;
+import net.minecraft.network.play.server.S21PacketChunkData;
+import net.minecraft.server.management.PlayerManager;
+import net.minecraft.server.management.PlayerManager.PlayerInstance;
+import net.minecraft.util.MathHelper;
+import net.minecraft.world.World;
+import net.minecraft.world.WorldServer;
+import net.minecraft.world.chunk.Chunk;
+import net.minecraft.world.chunk.NibbleArray;
+import net.minecraft.world.chunk.storage.ExtendedBlockStorage;
+import net.minecraft.world.gen.feature.WorldGenerator;
+
+public class WorldGenMassiveTree extends WorldGenerator {
+
+ /**
+ * Contains three sets of two values that provide complimentary indices for a given 'major' index - 1 and 2 for 0, 0 and 2 for 1, and 0 and 1 for 2.
+ */
+ private static final byte[] otherCoordPairs = new byte[] { (byte) 2, (byte) 0, (byte) 0, (byte) 1, (byte) 2, (byte) 1 };
+ private static final float PI = (float) Math.PI;
+
+ /* Running variables */
+ private Random rand = new Random();
+
+ private World worldObj;
+ /** Contains a list of a points at which to generate groups of leaves. */
+ private int[][] leafNodes;
+ private int[] basePos = new int[] { 0, 0, 0 };
+ private int heightLimit = 0;
+ private int height;
+ private int leafBases;
+ private int leafNodesLength;
+ private int density;
+
+ /* Setup variables */
+ private final List leaves;
+ private final List trunk;
+ private final WeightedRandomBlock[] genBlock;
+ private boolean generated = false;
+
+ public boolean smoothLogs = false;
+ public boolean slopeTrunk = false;
+ public boolean safeGrowth = true;
+ public boolean treeChecks = true;
+ public boolean fastPlacement = false;
+ public boolean relightBlocks = true;
+ public WeightedRandomBlock[] genSurface = null;
+
+ public int minHeight = -1;
+ public int leafDistanceLimit = 4;
+ public int heightLimitLimit = 250;
+
+ public float heightAttenuation = 0.45f;
+ public float branchDensity = 1.0f;
+ public float branchSlope = 0.381f;
+ public float scaleWidth = 1.0f;
+ public int trunkSize = 11;
+
+ public WorldGenMassiveTree(List resource, List leaf, List block) {
+
+ super(false);
+ trunk = resource;
+ leaves = leaf;
+ genBlock = block.toArray(new WeightedRandomBlock[block.size()]);
+ }
+
+ private void setup() {
+
+ leafBases = MathHelper.ceiling_float_int(heightLimit * heightAttenuation);
+ density = Math.max(1, (int) (1.382D + Math.pow(branchDensity * heightLimit / 13.0D, 2.0D)));
+ chunkMap = new TLongObjectHashMap((int) (scaleWidth * heightLimit));
+ }
+
+ private float layerSize(int par1) {
+
+ if (par1 < leafBases) {
+ return -1.618F;
+ } else {
+ float var2 = heightLimit * .5F;
+ float var3 = heightLimit * .5F - par1;
+ float var4;
+
+ if (var3 == 0.0F) {
+ var4 = var2;
+ } else if (Math.abs(var3) >= var2) {
+ return 0.0F;
+ } else {
+ var4 = (float) Math.sqrt(var2 * var2 - var3 * var3);
+ }
+
+ var4 *= 0.5F;
+ return var4;
+ }
+ }
+
+ /**
+ * Generates a list of leaf nodes for the tree, to be populated by generateLeaves.
+ */
+ private void generateLeafNodeList() {
+
+ int var1 = density;
+
+ int[] basePos = this.basePos;
+ int[][] var2 = new int[var1 * heightLimit][4];
+ int var3 = basePos[1] + heightLimit - leafDistanceLimit;
+ int var4 = 1;
+ int var5 = basePos[1] + height;
+ int var6 = var3 - basePos[1];
+ var2[0][0] = basePos[0];
+ var2[0][1] = var3;
+ var2[0][2] = basePos[2];
+ var2[0][3] = var5;
+ --var3;
+
+ while (var6 >= 0) {
+ int var7 = 0;
+ float var8 = this.layerSize(var6);
+
+ if (var8 > 0.0F) {
+ for (float var9 = 0.5f; var7 < var1; ++var7) {
+ float var11 = scaleWidth * var8 * (rand.nextFloat() + 0.328f);
+ float var13 = rand.nextFloat() * 2.0f * PI;
+ int var15 = MathHelper.floor_double(var11 * Math.sin(var13) + basePos[0] + var9);
+ int var16 = MathHelper.floor_double(var11 * Math.cos(var13) + basePos[2] + var9);
+ int[] var17 = new int[] { var15, var3, var16 };
+ int[] var18 = new int[] { var15, var3 + leafDistanceLimit, var16 };
+
+ if (this.checkBlockLine(var17, var18) == -1) {
+ int t;
+ double var20 = Math.sqrt((t = basePos[0] - var17[0]) * t + (t = basePos[2] - var17[2]) * t);
+ int var22 = (int) (var20 * branchSlope);
+
+ int[] var19 = new int[] { basePos[0], Math.min(var17[1] - var22, var5), basePos[2] };
+
+ if (this.checkBlockLine(var19, var17) == -1) {
+ var2[var4][0] = var15;
+ var2[var4][1] = var3;
+ var2[var4][2] = var16;
+ var2[var4][3] = var19[1];
+ ++var4;
+ }
+ }
+ }
+ }
+ --var3;
+ --var6;
+ }
+
+ leafNodes = var2;
+ leafNodesLength = var4;
+ }
+
+ private void genLeafLayer(int x, int y, int z, final int size) {
+
+ int t;
+ final int X = x;
+ final int Z = z;
+ final float maxDistSq = size * size;
+
+ for (int xMod = -size; xMod <= size; ++xMod) {
+ x = X + xMod;
+ final int xDistSq = xMod * xMod + (((t = xMod >> 31) ^ xMod) - t);
+
+ for (int zMod = 0; zMod <= size;) { // negative values handled below
+ final float distSq = xDistSq + zMod * zMod + zMod + 0.5f;
+
+ if (distSq > maxDistSq) {
+ break;
+ } else {
+ for (t = -1; t <= 1; t += 2) {
+ z = Z + zMod * t;
+ Block block = worldObj.getBlock(x, y, z);
+
+ if (safeGrowth ? canGenerateInBlock(worldObj, x, y, z, genBlock)
+ && (!treeChecks || (block.isAir(worldObj, x, y, z) || block.isLeaves(worldObj, x, y, z) || block.canBeReplacedByLeaves(
+ worldObj, x, y, z))) : block.getBlockHardness(worldObj, x, y, z) >= 0) {
+ WeightedRandomBlock b = selectBlock(worldObj, leaves);
+ this.setBlockAndNotifyAdequately(worldObj, x, y, z, b.block, b.metadata);
+ }
+ }
+ ++zMod;
+ }
+ }
+ }
+ }
+
+ /**
+ * Generates the leaf portion of the tree as specified by the leafNodes list.
+ */
+ private void generateLeaves() {
+
+ int[][] leafNodes = this.leafNodes;
+
+ for (int i = 0, e = leafNodesLength; i < e; ++i) {
+ int[] n = leafNodes[i];
+ int x = n[0], yO = n[1], z = n[2];
+ int y = 0;
+
+ for (int var5 = y + leafDistanceLimit; y < var5; ++y) {
+ int size = (y != 0) & y != leafDistanceLimit - 1 ? 3 : 2;
+ genLeafLayer(x, yO++, z, size);
+ }
+ }
+ }
+
+ private int[] placeScratch = new int[3];
+
+ private void placeBlockLine(int[] par1, int[] par2, List block, int meta) {
+
+ int t;
+ int[] var4 = placeScratch;
+ byte var6 = 0;
+
+ for (byte i = 0; i < 3; ++i) {
+ int a = par2[i] - par1[i], b = ((t = a >> 31) ^ a) - t;
+ var4[i] = a;
+ if (b > ((a = var4[var6]) ^ (t = a >> 31)) - t) {
+ var6 = i;
+ }
+ }
+
+ if (var4[var6] != 0) {
+ byte var7 = otherCoordPairs[var6];
+ byte var8 = otherCoordPairs[var6 + 3];
+ byte var9;
+
+ if (var4[var6] > 0) {
+ var9 = 1;
+ } else {
+ var9 = -1;
+ }
+
+ float var10 = (float) var4[var7] / (float) var4[var6];
+ float var12 = (float) var4[var8] / (float) var4[var6];
+ int var16 = var4[var6] + var9;
+
+ int[] var14 = var4;
+
+ for (int var15 = 0; var15 != var16; var15 += var9) {
+ var14[var6] = MathHelper.floor_float(par1[var6] + var15 + 0.5F);
+ var14[var7] = MathHelper.floor_float(par1[var7] + var15 * var10 + 0.5F);
+ var14[var8] = MathHelper.floor_float(par1[var8] + var15 * var12 + 0.5F);
+ int var18 = var14[0] - par1[0];
+ var18 = ((t = var18 >> 31) ^ var18) - t;
+ int var19 = var14[2] - par1[2];
+ var19 = ((t = var19 >> 31) ^ var19) - t;
+ int var20 = Math.max(var18, var19);
+
+ int var17 = meta;
+ if (smoothLogs & var20 > 0) {
+ if (var18 == var20) {
+ var17 |= 4;
+ } else if (var19 == var20) {
+ var17 |= 8;
+ }
+ }
+
+ WeightedRandomBlock par3 = selectBlock(worldObj, trunk);
+ this.setBlockAndNotifyAdequately(worldObj, var14[0], var14[1], var14[2], par3.block, par3.metadata | var17);
+ }
+ }
+ }
+
+ /**
+ * Places the trunk for the big tree that is being generated. Able to generate double-sized trunks by changing a field that is always 1 to 2.
+ */
+ private void generateTrunk() {
+
+ int var1 = basePos[0];
+ int var2 = basePos[1];
+ int var3 = basePos[1] + height;
+ int var4 = basePos[2];
+
+ int[] var5 = new int[] { var1, var2, var4 };
+ int[] var6 = new int[] { var1, var3, var4 };
+
+ double lim = 400f / trunkSize;
+
+ for (int i = -trunkSize; i <= trunkSize; i++) {
+ var5[0] = var1 + i;
+ var6[0] = var1 + i;
+
+ for (int j = -trunkSize; j <= trunkSize; j++) {
+ if ((j * j + i * i) * 4 < trunkSize * trunkSize * 5) {
+ var5[2] = var4 + j;
+ var6[2] = var4 + j;
+
+ if (slopeTrunk) {
+ var6[1] = var2 + sinc2(lim * i, lim * j, height) - (rand.nextInt(3) - 1);
+ }
+
+ this.placeBlockLine(var5, var6, trunk, 0);
+ if (smoothLogs) {
+ this.setBlockAndNotifyAdequately(worldObj, var6[0], var6[1], var6[2], null, 12);
+ }
+ worldObj.getBlock(var5[0], var5[1] - 1, var5[2]).onPlantGrow(worldObj, var5[0], var5[1] - 1, var5[2], var1, var2, var4);
+ }
+ }
+ }
+ }
+
+ private static final int sinc2(final double x, final double z, final int y) {
+
+ final double pi = Math.PI, pi2 = pi / 1.5;
+ double r;
+ r = Math.sqrt((r = (x / pi)) * r + (r = (z / pi)) * r) * pi / 180;
+ if (r == 0) {
+ return y;
+ }
+ return (int) Math.round(y * (((Math.sin(r) / r) + (Math.sin(r * pi2) / (r * pi2))) / 2));
+ }
+
+ /**
+ * Generates additional wood blocks to fill out the bases of different leaf nodes that would otherwise degrade.
+ */
+ void generateLeafNodeBases() {
+
+ int[] start = new int[] { basePos[0], basePos[1], basePos[2] };
+ int[][] leafNodes = this.leafNodes;
+
+ int heightLimit = (int) (this.heightLimit * 0.2f);
+ int meta = smoothLogs ? 12 : 0;
+ for (int i = 0, e = leafNodesLength; i < e; ++i) {
+ int[] end = leafNodes[i];
+ start[1] = end[3];
+ int height = start[1] - basePos[1];
+
+ if (height >= heightLimit) {
+ this.placeBlockLine(start, end, trunk, meta);
+ }
+ }
+ }
+
+ private int[] checkScratch = new int[3];
+
+ /**
+ * Checks a line of blocks in the world from the first coordinate to triplet to the second, returning the distance (in blocks) before a non-air, non-leaf
+ * block is encountered and/or the end is encountered.
+ */
+ private int checkBlockLine(int[] par1, int[] par2) {
+
+ int t;
+ int[] var3 = checkScratch;
+ byte var5 = 0;
+
+ for (byte i = 0; i < 3; ++i) {
+ int a = par2[i] - par1[i], b = ((t = a >> 31) ^ a) - t;
+ var3[i] = a;
+ if (b > ((a = var3[var5]) ^ (t = a >> 31)) - t) {
+ var5 = i;
+ }
+ }
+
+ if (var3[var5] == 0) {
+ return -1;
+ } else {
+ byte var6 = otherCoordPairs[var5];
+ byte var7 = otherCoordPairs[var5 + 3];
+ byte var8;
+
+ if (var3[var5] > 0) {
+ var8 = 1;
+ } else {
+ var8 = -1;
+ }
+
+ float var9 = (float) var3[var6] / (float) var3[var5];
+ float var11 = (float) var3[var7] / (float) var3[var5];
+ int var14 = 0;
+ int var15 = var3[var5] + var8;
+
+ int[] var13 = var3;
+
+ for (; var14 != var15; var14 += var8) {
+ var13[var5] = par1[var5] + var14;
+ var13[var6] = MathHelper.floor_float(par1[var6] + var14 * var9);
+ var13[var7] = MathHelper.floor_float(par1[var7] + var14 * var11);
+ int x = var13[0], y = var13[1], z = var13[2];
+ Block var16 = worldObj.getBlock(x, y, z);
+
+ if (safeGrowth ? canGenerateInBlock(worldObj, x, y, z, genBlock)
+ && (!treeChecks || !(var16.isAir(worldObj, x, y, z) || var16.isReplaceable(worldObj, x, y, z)
+ || var16.canBeReplacedByLeaves(worldObj, x, y, z) || var16.isLeaves(worldObj, x, y, z) || var16.isWood(worldObj, x, y, z) || var16 instanceof BlockSapling))
+ : var16.getBlockHardness(worldObj, x, y, z) >= 0) {
+ break;
+ }
+ }
+
+ return var14 == var15 ? -1 : ((t = var14 >> 31) ^ var14) - t;
+ }
+ }
+
+ /**
+ * Returns a boolean indicating whether or not the current location for the tree, spanning basePos to to the height limit, is valid.
+ */
+ private boolean validTreeLocation() {
+
+ int newHeight = Math.min(heightLimit + basePos[1], 255) - basePos[1];
+ if (newHeight < minHeight) {
+ return false;
+ }
+ heightLimit = newHeight;
+
+ if (!canGenerateInBlock(worldObj, basePos[0], basePos[1] - 1, basePos[2], genSurface)) {
+ return false;
+ } else {
+ int[] var5 = new int[] { basePos[0], basePos[1], basePos[2] };
+ int[] var6 = new int[] { basePos[0], basePos[1] + heightLimit - 1, basePos[2] };
+
+ newHeight = this.checkBlockLine(var5, var6);
+
+ if (newHeight == -1) {
+ newHeight = heightLimit;
+ }
+ if (newHeight < minHeight) {
+ return false;
+ }
+
+ heightLimit = Math.min(newHeight, heightLimitLimit);
+ height = (int) (heightLimit * heightAttenuation);
+ if (height >= heightLimit) {
+ height = heightLimit - 1;
+ }
+ height += rand.nextInt(heightLimit - height);
+
+ if (safeGrowth) {
+ int var1 = basePos[0];
+ int var2 = basePos[1];
+ int var3 = basePos[1] + height;
+ int var4 = basePos[2];
+
+ var5 = new int[] { var1, var2, var4 };
+ var6 = new int[] { var1, var3, var4 };
+
+ double lim = 400f / trunkSize;
+
+ for (int i = -trunkSize; i <= trunkSize; i++) {
+ var5[0] = var1 + i;
+ var6[0] = var1 + i;
+
+ for (int j = -trunkSize; j <= trunkSize; j++) {
+ if ((j * j + i * i) * 4 < trunkSize * trunkSize * 5) {
+ var5[2] = var4 + j;
+ var6[2] = var4 + j;
+
+ if (slopeTrunk) {
+ var6[1] = var2 + sinc2(lim * i, lim * j, height);
+ }
+
+ int t = checkBlockLine(var5, var6);
+ if (t != -1) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+ }
+
+ /**
+ * Rescales the generator settings, only used in WorldGenBigTree
+ */
+ @Override
+ public void setScale(double par1, double par3, double par5) {
+
+ setTreeScale((float) par1, (float) par3, (float) par5);
+ }
+
+ public WorldGenMassiveTree setTreeScale(float height, float width, float leaves) {
+
+ heightLimitLimit = (int) (height * 12.0D);
+ minHeight = heightLimitLimit / 2;
+ trunkSize = (int) Math.round((height / 2D));
+
+ if (minHeight > 30) {
+ leafDistanceLimit = 5;
+ } else {
+ leafDistanceLimit = minHeight / 8;
+ }
+
+ scaleWidth = width;
+ branchDensity = leaves;
+ return this;
+ }
+
+ public WorldGenMassiveTree setMinTrunkSize(int radius) {
+
+ trunkSize = Math.max(radius, trunkSize);
+ return this;
+ }
+
+ public WorldGenMassiveTree setLeafAttenuation(float a) {
+
+ heightAttenuation = a;
+ return this;
+ }
+
+ public WorldGenMassiveTree setSloped(boolean s) {
+
+ slopeTrunk = s;
+ return this;
+ }
+
+ public WorldGenMassiveTree setSafe(boolean s) {
+
+ safeGrowth = s;
+ return this;
+ }
+
+ @Override
+ public synchronized boolean generate(World world, Random par2Random, int x, int y, int z) {
+
+ // long time = System.nanoTime();
+ worldObj = world;
+ long var6 = par2Random.nextLong();
+ rand.setSeed(var6);
+ basePos[0] = x;
+ basePos[1] = y;
+ basePos[2] = z;
+ if (heightLimit == 0) {
+ heightLimit = heightLimitLimit;
+ }
+ if (minHeight < 0) {
+ minHeight = 80;
+ }
+
+ if (!this.validTreeLocation()) {
+ worldObj = null;
+ return false;
+ } else {
+ generated = false;
+ this.setup();
+ // time = System.nanoTime() - time;
+ // logger.info("Verified spawn position of massive tree in: " + time + "ns");
+ // long time2 = time = System.nanoTime();
+ this.generateLeafNodeList();
+ // long nodes = System.nanoTime();
+ this.generateLeaves();
+ // long leaves = System.nanoTime();
+ this.generateLeafNodeBases();
+ // long bases = System.nanoTime();
+ this.generateTrunk();
+ // long trunk = System.nanoTime();
+ // time = System.nanoTime() - time;
+ // logger.info("Generated massive tree in: " + time + "ns");
+ // trunk -= bases; bases -= leaves; leaves -= nodes; nodes -= time2;
+ // logger.info(String.format("%s for trunk, %s for leaf nodes, %s for leaves, %s for branches", trunk, nodes, leaves, bases));
+ // logger.info("\tTree contains " + blocksAdded + " Blocks");
+ // time = System.nanoTime();
+ if (fastPlacement) {
+ for (TLongObjectIterator iter = chunkMap.iterator(); iter.hasNext();) {
+ iter.advance();
+ Chunk chunk = iter.value();
+ chunk.generateSkylightMap();
+ ExtendedBlockStorage[] storage = chunk.getBlockStorageArray();
+ if (!relightBlocks) {
+ for (int i = storage.length; i-- > 0;) {
+ if (storage[i] != null) {
+ // { force data array to exist if optimizations to not exist are in place
+ NibbleArray a = storage[i].getSkylightArray();
+ a.set(0, 0, 0, 0);
+ a.set(0, 0, 0, 15);
+ // }
+ Arrays.fill(a.data, (byte) 0);
+ }
+ }
+ chunk.resetRelightChecks();
+ }
+ chunk.isModified = true;
+ if (world instanceof WorldServer) {
+ PlayerManager manager = ((WorldServer) world).getPlayerManager();
+ if (manager == null) {
+ continue;
+ }
+ PlayerInstance watcher = manager.getOrCreateChunkWatcher(chunk.xPosition, chunk.zPosition, false);
+ if (watcher != null) {
+ watcher.sendToAllPlayersWatchingChunk(new S21PacketChunkData(chunk, false, -1));
+ }
+ }
+ }
+ }
+ // time = System.nanoTime() - time;
+ // logger.info("Lit massive tree in: " + time + "ns");
+ worldObj = null;
+ return generated;
+ }
+ }
+
+ // private static final org.apache.logging.log4j.Logger logger = org.apache.logging.log4j.LogManager.getLogger("Tree logger");
+ // private int blocksAdded = 0;
+ private TLongObjectHashMap chunkMap;
+
+ @Override
+ public void setBlockAndNotifyAdequately(World world, int x, int y, int z, Block block, int meta) {
+
+ if ((y < 0) | y > 255) {
+ return;
+ }
+ generated = true;
+ if (!fastPlacement) {
+ if (block != null) {
+ super.setBlockAndNotifyAdequately(world, x, y, z, block, meta);
+ } else {
+ world.setBlockMetadataWithNotify(x, y, z, world.getBlockMetadata(x, y, z) | meta, 2);
+ }
+ return;
+ }
+ // ++blocksAdded;
+ long pos = ((x & 0xFFFFFFF0L) << 32) | (z & 0xFFFFFFF0L);
+
+ Chunk chunk = chunkMap.get(pos);
+ if (chunk == null) {
+ chunk = world.getChunkFromBlockCoords(x, z);
+ chunkMap.put(pos, chunk);
+ }
+
+ ExtendedBlockStorage[] storage = chunk.getBlockStorageArray();
+ ExtendedBlockStorage subChunk = storage[y >> 4];
+ if (subChunk == null) {
+ storage[y >> 4] = subChunk = new ExtendedBlockStorage(y & ~15, !world.provider.hasNoSky);
+ }
+
+ x &= 15;
+ z &= 15;
+ if (block != null && subChunk.getBlockByExtId(x, y & 15, z).hasTileEntity(subChunk.getExtBlockMetadata(x, y & 15, z))) {
+ chunk.removeTileEntity(x, y, z);
+ }
+ y &= 15;
+
+ if (block != null) {
+ subChunk.func_150818_a(x, y, z, block);
+ subChunk.setExtBlockMetadata(x, y, z, meta);
+ } else {
+ subChunk.setExtBlockMetadata(x, y, z, subChunk.getExtBlockMetadata(x, y, z) | meta);
+ }
+ subChunk.setExtBlocklightValue(x, y, z, 0);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/world/WorldGenMinableCluster.java b/src/main/java/cofh/lib/world/WorldGenMinableCluster.java
new file mode 100644
index 00000000..ea301800
--- /dev/null
+++ b/src/main/java/cofh/lib/world/WorldGenMinableCluster.java
@@ -0,0 +1,211 @@
+package cofh.lib.world;
+
+import cofh.lib.util.WeightedRandomBlock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+import net.minecraft.block.Block;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.MathHelper;
+import net.minecraft.util.WeightedRandom;
+import net.minecraft.world.World;
+import net.minecraft.world.gen.feature.WorldGenerator;
+
+public class WorldGenMinableCluster extends WorldGenerator {
+
+ public static final List fabricateList(WeightedRandomBlock resource) {
+
+ List list = new ArrayList();
+ list.add(resource);
+ return list;
+ }
+
+ public static final List fabricateList(Block resource) {
+
+ List list = new ArrayList();
+ list.add(new WeightedRandomBlock(new ItemStack(resource, 1, 0)));
+ return list;
+ }
+
+ private final List cluster;
+ private final int genClusterSize;
+ private final WeightedRandomBlock[] genBlock;
+
+ public WorldGenMinableCluster(ItemStack ore, int clusterSize) {
+
+ this(new WeightedRandomBlock(ore), clusterSize);
+ }
+
+ public WorldGenMinableCluster(WeightedRandomBlock resource, int clusterSize) {
+
+ this(fabricateList(resource), clusterSize);
+ }
+
+ public WorldGenMinableCluster(List resource, int clusterSize) {
+
+ this(resource, clusterSize, Blocks.stone);
+ }
+
+ public WorldGenMinableCluster(ItemStack ore, int clusterSize, Block block) {
+
+ this(new WeightedRandomBlock(ore, 1), clusterSize, block);
+ }
+
+ public WorldGenMinableCluster(WeightedRandomBlock resource, int clusterSize, Block block) {
+
+ this(fabricateList(resource), clusterSize, block);
+ }
+
+ public WorldGenMinableCluster(List resource, int clusterSize, Block block) {
+
+ this(resource, clusterSize, fabricateList(block));
+ }
+
+ public WorldGenMinableCluster(List resource, int clusterSize, List block) {
+
+ cluster = resource;
+ genClusterSize = clusterSize > 32 ? 32 : clusterSize;
+ genBlock = block.toArray(new WeightedRandomBlock[block.size()]);
+ }
+
+ @Override
+ public boolean generate(World world, Random rand, int x, int y, int z) {
+
+ int blocks = genClusterSize;
+ if (blocks < 4) { // HACK: at 1 and 2 no ores are ever generated. at 3 only 1/3 veins generate
+ return generateTiny(world, rand, x, y, z);
+ }
+ float f = rand.nextFloat() * (float) Math.PI;
+ // despite naming, these are not exactly min/max. more like direction
+ float xMin = x + 8 + (MathHelper.sin(f) * blocks) / 8F;
+ float xMax = x + 8 - (MathHelper.sin(f) * blocks) / 8F;
+ float zMin = z + 8 + (MathHelper.cos(f) * blocks) / 8F;
+ float zMax = z + 8 - (MathHelper.cos(f) * blocks) / 8F;
+ float yMin = (y + rand.nextInt(3)) - 2;
+ float yMax = (y + rand.nextInt(3)) - 2;
+
+ // optimization so this subtraction doesn't occur every time in the loop
+ xMax -= xMin;
+ yMax -= yMin;
+ zMax -= zMin;
+
+ boolean r = false;
+ for (int i = 0; i <= blocks; i++) {
+
+ float xCenter = xMin + (xMax * i) / blocks;
+ float yCenter = yMin + (yMax * i) / blocks;
+ float zCenter = zMin + (zMax * i) / blocks;
+
+ // preserved as nextDouble to ensure the rand gets ticked the same amount
+ float size = ((float) rand.nextDouble() * blocks) / 16f;
+
+ float hMod = ((MathHelper.sin((i * (float) Math.PI) / blocks) + 1f) * size + 1f) * .5f;
+ float vMod = ((MathHelper.sin((i * (float) Math.PI) / blocks) + 1f) * size + 1f) * .5f;
+
+ int xStart = MathHelper.floor_float(xCenter - hMod);
+ int yStart = MathHelper.floor_float(yCenter - vMod);
+ int zStart = MathHelper.floor_float(zCenter - hMod);
+
+ int xStop = MathHelper.floor_float(xCenter + hMod);
+ int yStop = MathHelper.floor_float(yCenter + vMod);
+ int zStop = MathHelper.floor_float(zCenter + hMod);
+
+ for (int blockX = xStart; blockX <= xStop; blockX++) {
+ float xDistSq = ((blockX + .5f) - xCenter) / hMod;
+ xDistSq *= xDistSq;
+ if (xDistSq >= 1f) {
+ continue;
+ }
+
+ for (int blockY = yStart; blockY <= yStop; blockY++) {
+ float yDistSq = ((blockY + .5f) - yCenter) / vMod;
+ yDistSq *= yDistSq;
+ float xyDistSq = yDistSq + xDistSq;
+ if (xyDistSq >= 1f) {
+ continue;
+ }
+
+ for (int blockZ = zStart; blockZ <= zStop; blockZ++) {
+ float zDistSq = ((blockZ + .5f) - zCenter) / hMod;
+ zDistSq *= zDistSq;
+ if (zDistSq + xyDistSq >= 1f) {
+ continue;
+ }
+
+ r |= generateBlock(world, blockX, blockY, blockZ, genBlock, cluster);
+ }
+ }
+ }
+ }
+
+ return r;
+ }
+
+ public boolean generateTiny(World world, Random random, int x, int y, int z) {
+
+ boolean r = false;
+ // not <=; generating up to clusterSize blocks
+ for (int i = 0; i < genClusterSize; i++) {
+ int d0 = x + random.nextInt(2);
+ int d1 = y + random.nextInt(2);
+ int d2 = z + random.nextInt(2);
+
+ r |= generateBlock(world, d0, d1, d2, genBlock, cluster);
+ }
+ return r;
+ }
+
+ public static boolean canGenerateInBlock(World world, int x, int y, int z, WeightedRandomBlock[] mat) {
+
+ if (mat == null || mat.length == 0) {
+ return true;
+ }
+
+ Block block = world.getBlock(x, y, z);
+ for (int j = 0, e = mat.length; j < e; ++j) {
+ WeightedRandomBlock genBlock = mat[j];
+ if ((-1 == genBlock.metadata || genBlock.metadata == world.getBlockMetadata(x, y, z))
+ && (block.isReplaceableOreGen(world, x, y, z, genBlock.block) || block.isAssociatedBlock(genBlock.block))) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static boolean generateBlock(World world, int x, int y, int z, WeightedRandomBlock[] mat, List o) {
+
+ if (mat == null || mat.length == 0) {
+ return generateBlock(world, x, y, z, o);
+ }
+
+ if (canGenerateInBlock(world, x, y, z, mat)) {
+ return generateBlock(world, x, y, z, o);
+ }
+ return false;
+ }
+
+ public static boolean generateBlock(World world, int x, int y, int z, List o) {
+
+ WeightedRandomBlock ore = selectBlock(world, o);
+ if (ore == null) {
+ return false;
+ }
+ return world.setBlock(x, y, z, ore.block, ore.metadata, 2);
+ }
+
+ public static WeightedRandomBlock selectBlock(World world, List o) {
+
+ int size = o.size();
+ if (size == 0) {
+ return null;
+ }
+ if (size > 1) {
+ return (WeightedRandomBlock) WeightedRandom.getRandomItem(world.rand, o);
+ }
+ return o.get(0);
+ }
+
+}
diff --git a/src/main/java/cofh/lib/world/WorldGenMinableLargeVein.java b/src/main/java/cofh/lib/world/WorldGenMinableLargeVein.java
new file mode 100644
index 00000000..294eb120
--- /dev/null
+++ b/src/main/java/cofh/lib/world/WorldGenMinableLargeVein.java
@@ -0,0 +1,152 @@
+package cofh.lib.world;
+
+import static cofh.lib.world.WorldGenMinableCluster.*;
+
+import cofh.lib.util.WeightedRandomBlock;
+
+import java.util.List;
+import java.util.Random;
+
+import net.minecraft.block.Block;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.ItemStack;
+import net.minecraft.world.World;
+import net.minecraft.world.gen.feature.WorldGenerator;
+
+public class WorldGenMinableLargeVein extends WorldGenerator {
+
+ private final List cluster;
+ private final WeightedRandomBlock[] genBlock;
+ private final int genVeinSize;
+ private final boolean sparse;
+
+ public WorldGenMinableLargeVein(ItemStack ore, int clusterSize) {
+
+ this(new WeightedRandomBlock(ore), clusterSize);
+ }
+
+ public WorldGenMinableLargeVein(WeightedRandomBlock resource, int clusterSize) {
+
+ this(fabricateList(resource), clusterSize);
+ }
+
+ public WorldGenMinableLargeVein(List