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 openedLeftTab; + private static Class openedRightTab; + + private TabTracker() { + + } + + public static Class getOpenedLeftTab() { + + return openedLeftTab; + } + + public static Class getOpenedRightTab() { + + return openedRightTab; + } + + public static void setOpenedLeftTab(Class tabClass) { + + openedLeftTab = tabClass; + } + + public static void setOpenedRightTab(Class 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 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 clazz; + + public SlotAcceptAssignable(IInventory inventory, int index, int x, int y, Class 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 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 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 resource, int clusterSize) { + + this(resource, clusterSize, Blocks.stone); + } + + public WorldGenMinableLargeVein(ItemStack ore, int clusterSize, Block block) { + + this(new WeightedRandomBlock(ore, 1), clusterSize, block); + } + + public WorldGenMinableLargeVein(WeightedRandomBlock resource, int clusterSize, Block block) { + + this(fabricateList(resource), clusterSize, block); + } + + public WorldGenMinableLargeVein(List resource, int clusterSize, Block block) { + + this(resource, clusterSize, fabricateList(block)); + } + + public WorldGenMinableLargeVein(List resource, int clusterSize, List block) { + + this(resource, clusterSize, block, true); + } + + public WorldGenMinableLargeVein(List resource, int clusterSize, List block, boolean sparze) { + + cluster = resource; + genVeinSize = clusterSize; + genBlock = block.toArray(new WeightedRandomBlock[block.size()]); + sparse = sparze; + } + + @Override + public boolean generate(World world, Random rand, int x, int y, int z) { + + final int veinSize = genVeinSize; + final int branchSize = 1 + (veinSize / 30); + final int subBranchSize = 1 + (branchSize / 5); + + boolean r = false; + for (int blocksVein = 0; blocksVein <= veinSize;) { + int posX = x; + int posY = y; + int posZ = z; + + int directionChange = rand.nextInt(6); + + int directionX1 = -rand.nextInt(2); + int directionY1 = -rand.nextInt(2); + int directionZ1 = -rand.nextInt(2); + { // random code block to circumvent eclipse freaking out on auto-indent with unsigned right shift + directionX1 += ~directionX1 >>> 31; + directionY1 += ~directionY1 >>> 31; + directionZ1 += ~directionZ1 >>> 31; + } + + for (int blocksBranch = 0; blocksBranch <= branchSize;) { + if (directionChange != 1) { + posX += rand.nextInt(2) * directionX1; + } + if (directionChange != 2) { + posY += rand.nextInt(2) * directionY1; + } + if (directionChange != 3) { + posZ += rand.nextInt(2) * directionZ1; + } + + if (rand.nextInt(3) == 0) { + int posX2 = posX; + int posY2 = posY; + int posZ2 = posZ; + + int directionChange2 = rand.nextInt(6); + + int directionX2 = -rand.nextInt(2); + int directionY2 = -rand.nextInt(2); + int directionZ2 = -rand.nextInt(2); + { // freaking out does not occur here, for some reason. the number at the end of the variable? + directionX2 += ~directionX2 >>> 31; + directionY2 += ~directionY2 >>> 31; + directionZ2 += ~directionZ2 >>> 31; + } + + for (int blocksSubBranch = 0; blocksSubBranch <= subBranchSize;) { + if (directionChange2 != 0) { + posX2 += rand.nextInt(2) * directionX2; + } + if (directionChange2 != 1) { + posY2 += rand.nextInt(2) * directionY2; + } + if (directionChange2 != 2) { + posZ2 += rand.nextInt(2) * directionZ2; + } + + r |= generateBlock(world, posX2, posY2, posZ2, genBlock, cluster); + + if (sparse) { + blocksVein++; + blocksBranch++; + } + blocksSubBranch++; + } + } + + r |= generateBlock(world, posX, posY, posZ, genBlock, cluster); + + blocksBranch++; + } + + x = x + (rand.nextInt(3) - 1); + y = y + (rand.nextInt(3) - 1); + z = z + (rand.nextInt(3) - 1); + blocksVein++; + } + + return r; + } + +} diff --git a/src/main/java/cofh/lib/world/WorldGenMinablePlate.java b/src/main/java/cofh/lib/world/WorldGenMinablePlate.java new file mode 100644 index 00000000..02fade83 --- /dev/null +++ b/src/main/java/cofh/lib/world/WorldGenMinablePlate.java @@ -0,0 +1,58 @@ +package cofh.lib.world; + +import static cofh.lib.world.WorldGenMinableCluster.generateBlock; + +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 WorldGenMinablePlate extends WorldGenerator { + + private final List cluster; + private final WeightedRandomBlock[] genBlock; + private final int radius; + public byte height = 1; + public byte variation = 2; + public boolean slim = false; + + public WorldGenMinablePlate(List resource, int clusterSize, List block) { + + cluster = resource; + radius = clusterSize; + genBlock = block.toArray(new WeightedRandomBlock[block.size()]); + } + + @Override + public boolean generate(World world, Random rand, int x, int y, int z) { + + ++y; + int size = radius; + if (radius > variation + 1) { + size = rand.nextInt(radius - variation) + variation; + } + final int dist = size * size; + byte height = this.height; + + boolean r = false; + for (int posX = x - size; posX <= x + size; ++posX) { + int xDist = posX - x; + xDist *= xDist; + for (int posZ = z - size; posZ <= z + size; ++posZ) { + int zSize = posZ - z; + + if (zSize * zSize + xDist <= dist) { + for (int posY = y - height; slim ? posY < y + height : posY <= y + height; ++posY) { + r |= generateBlock(world, posX, posY, posZ, genBlock, cluster); + } + } + } + } + + return r; + } + +} diff --git a/src/main/java/cofh/lib/world/WorldGenMulti.java b/src/main/java/cofh/lib/world/WorldGenMulti.java new file mode 100644 index 00000000..e91bd0af --- /dev/null +++ b/src/main/java/cofh/lib/world/WorldGenMulti.java @@ -0,0 +1,28 @@ +package cofh.lib.world; + +import cofh.lib.util.WeightedRandomWorldGenerator; + +import java.util.ArrayList; +import java.util.Random; + +import net.minecraft.util.WeightedRandom; +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.WorldGenerator; + +public class WorldGenMulti extends WorldGenerator { + + private final WeightedRandomWorldGenerator[] generators; + + public WorldGenMulti(ArrayList values) { + + generators = values.toArray(new WeightedRandomWorldGenerator[values.size()]); + } + + @Override + public boolean generate(World world, Random random, int x, int y, int z) { + + WeightedRandomWorldGenerator gen = (WeightedRandomWorldGenerator) WeightedRandom.getRandomItem(random, generators); + return gen.generator.generate(world, random, x, y, z); + } + +} diff --git a/src/main/java/cofh/lib/world/WorldGenSmallTree.java b/src/main/java/cofh/lib/world/WorldGenSmallTree.java new file mode 100644 index 00000000..f7400d2e --- /dev/null +++ b/src/main/java/cofh/lib/world/WorldGenSmallTree.java @@ -0,0 +1,165 @@ +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.world.World; +import net.minecraft.world.gen.feature.WorldGenerator; + +public class WorldGenSmallTree extends WorldGenerator { + + private final List leaves; + private final List trunk; + private final WeightedRandomBlock[] genBlock; + + public WeightedRandomBlock[] genSurface = null; + public int minHeight = 5; + public int heightVariance = 3; + public boolean treeChecks = true; + public boolean leafVariance = true; + public boolean relaxedGrowth = false; + public boolean waterLoving = false; + + public WorldGenSmallTree(List resource, List leaf, List block) { + + trunk = resource; + leaves = leaf; + genBlock = block.toArray(new WeightedRandomBlock[block.size()]); + } + + protected int getLeafRadius(int height, int level, boolean check) { + + if (check) { + if (level >= 1 + height - 2) { + return 2; + } else { + return relaxedGrowth ? 0 : 1; + } + } + + if (level >= 1 + height - 4) { + return 1 - ((level - height) / 2); + } else { + return 0; + } + } + + @Override + public boolean generate(World world, Random rand, int x, int y, int z) { + + int treeHeight = (heightVariance <= 1 ? 0 : rand.nextInt(heightVariance)) + minHeight; + int worldHeight = world.getHeight(); + Block block; + + if (y + treeHeight + 1 <= worldHeight) { + int xOffset; + int yOffset; + int zOffset; + + if (!canGenerateInBlock(world, x, y - 1, z, genSurface)) { + return false; + } + + if (y < worldHeight - treeHeight - 1) { + if (treeChecks) { + for (yOffset = y; yOffset <= y + 1 + treeHeight; ++yOffset) { + + int radius = getLeafRadius(treeHeight, yOffset - y, true); + + if (yOffset >= 0 & yOffset < worldHeight) { + if (radius == 0) { + block = world.getBlock(x, yOffset, z); + if (!(block.isLeaves(world, x, yOffset, z) || block.isAir(world, x, yOffset, z) || block.isReplaceable(world, x, yOffset, z) + || block.canBeReplacedByLeaves(world, x, yOffset, z) || canGenerateInBlock(world, x, yOffset, z, genBlock))) { + return false; + } + + if (!waterLoving && yOffset >= y + 1) { + radius = 1; + for (xOffset = x - radius; xOffset <= x + radius; ++xOffset) { + for (zOffset = z - radius; zOffset <= z + radius; ++zOffset) { + block = world.getBlock(xOffset, yOffset, zOffset); + + if (block.getMaterial().isLiquid()) { + return false; + } + } + } + } + } else { + for (xOffset = x - radius; xOffset <= x + radius; ++xOffset) { + for (zOffset = z - radius; zOffset <= z + radius; ++zOffset) { + block = world.getBlock(xOffset, yOffset, zOffset); + + if (!(block.isLeaves(world, xOffset, yOffset, zOffset) || block.isAir(world, xOffset, yOffset, zOffset) + || block.canBeReplacedByLeaves(world, xOffset, yOffset, zOffset) || canGenerateInBlock(world, xOffset, yOffset, + zOffset, genBlock))) { + return false; + } + } + } + } + } else { + return false; + } + } + + if (genSurface != null && !canGenerateInBlock(world, x, y - 1, z, genSurface)) { + return false; + } + block = world.getBlock(x, y - 1, z); + block.onPlantGrow(world, x, y - 1, z, x, y, z); + } + + boolean r = false; + + for (yOffset = y; yOffset <= y + treeHeight; ++yOffset) { + + int var12 = yOffset - (y + treeHeight); + int radius = getLeafRadius(treeHeight, yOffset - y, false); + if (radius <= 0) { + continue; + } + + for (xOffset = x - radius; xOffset <= x + radius; ++xOffset) { + int xPos = xOffset - x, t; + xPos = (xPos + (t = xPos >> 31)) ^ t; + + for (zOffset = z - radius; zOffset <= z + radius; ++zOffset) { + int zPos = zOffset - z; + zPos = (zPos + (t = zPos >> 31)) ^ t; + + block = world.getBlock(xOffset, yOffset, zOffset); + + if (((xPos != radius | zPos != radius) || (!leafVariance || (rand.nextInt(2) != 0 && var12 != 0))) + && ((treeChecks ? block.isLeaves(world, xOffset, yOffset, zOffset) || block.isAir(world, xOffset, yOffset, zOffset) + || block.canBeReplacedByLeaves(world, xOffset, yOffset, zOffset) : false) || canGenerateInBlock(world, xOffset, + yOffset, zOffset, genBlock))) { + r |= generateBlock(world, xOffset, yOffset, zOffset, leaves); + } + } + } + } + + for (yOffset = 0; yOffset < treeHeight; ++yOffset) { + block = world.getBlock(x, y + yOffset, z); + + if ((treeChecks ? block.isAir(world, x, y + yOffset, z) || block.isLeaves(world, x, y + yOffset, z) + || block.isReplaceable(world, x, y + yOffset, z) : false) + || canGenerateInBlock(world, x, yOffset + y, z, genBlock)) { + r |= generateBlock(world, x, yOffset + y, z, trunk); + } + } + + return r; + } + } + return false; + } + +} diff --git a/src/main/java/cofh/lib/world/WorldGenSparseMinableCluster.java b/src/main/java/cofh/lib/world/WorldGenSparseMinableCluster.java new file mode 100644 index 00000000..3045ffa8 --- /dev/null +++ b/src/main/java/cofh/lib/world/WorldGenSparseMinableCluster.java @@ -0,0 +1,140 @@ +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.util.MathHelper; +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.WorldGenerator; + +public class WorldGenSparseMinableCluster extends WorldGenerator { + + private final List cluster; + private final int genClusterSize; + private final WeightedRandomBlock[] genBlock; + + public WorldGenSparseMinableCluster(ItemStack ore, int clusterSize) { + + this(new WeightedRandomBlock(ore), clusterSize); + } + + public WorldGenSparseMinableCluster(WeightedRandomBlock resource, int clusterSize) { + + this(fabricateList(resource), clusterSize); + } + + public WorldGenSparseMinableCluster(List resource, int clusterSize) { + + this(resource, clusterSize, Blocks.stone); + } + + public WorldGenSparseMinableCluster(ItemStack ore, int clusterSize, Block block) { + + this(new WeightedRandomBlock(ore, 1), clusterSize, block); + } + + public WorldGenSparseMinableCluster(WeightedRandomBlock resource, int clusterSize, Block block) { + + this(fabricateList(resource), clusterSize, block); + } + + public WorldGenSparseMinableCluster(List resource, int clusterSize, Block block) { + + this(resource, clusterSize, fabricateList(block)); + } + + public WorldGenSparseMinableCluster(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; + float f = rand.nextFloat() * (float) Math.PI; + // despite naming, these are not exactly min/max. more like direction + float yMin = (y + rand.nextInt(3)) - 2; + float yMax = (y + rand.nextInt(3)) - 2; + // { HACK: at 1 and 2 no ores are ever generated. by doing it this way, + // 3 = 1/3rd clusters gen, 2 = 1/6, 1 = 1/12 allowing for much finer + // grained rarity than the non-sparse version + if (blocks == 1 && yMin > yMax) { + ++blocks; + } + if (blocks == 2 && f > (float) Math.PI * 0.5f) { + ++blocks; + } + // } + 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; + + // 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; + } + +} diff --git a/src/main/java/cofh/lib/world/WorldGenSpike.java b/src/main/java/cofh/lib/world/WorldGenSpike.java new file mode 100644 index 00000000..448ec8b9 --- /dev/null +++ b/src/main/java/cofh/lib/world/WorldGenSpike.java @@ -0,0 +1,87 @@ +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.util.MathHelper; +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.WorldGenerator; + +public class WorldGenSpike extends WorldGenerator { + + private final List cluster; + private final WeightedRandomBlock[] genBlock; + public boolean largeSpikes = true; + public int largeSpikeChance = 60; + public int minHeight = 7; + public int heightVariance = 4; + public int sizeVariance = 2; + public int positionVariance = 3; + public int minLargeSpikeHeightGain = 10; + public int largeSpikeHeightVariance = 30; + public int largeSpikeFillerSize = 1; + + public WorldGenSpike(List resource, List block) { + + cluster = resource; + genBlock = block.toArray(new WeightedRandomBlock[block.size()]); + } + + @Override + public boolean generate(World world, Random rand, int xStart, int yStart, int zStart) { + + while (world.isAirBlock(xStart, yStart, zStart) && yStart > 2) { + --yStart; + } + + if (!canGenerateInBlock(world, xStart, yStart, zStart, genBlock)) { + return false; + } + + int height = rand.nextInt(heightVariance) + minHeight, originalHeight = height; + int size = height / (minHeight / 2) + rand.nextInt(sizeVariance); + if (size > 1 && positionVariance > 0) { + yStart += rand.nextInt(positionVariance + 1) - 1; + } + + if (largeSpikes && size > 1 && (largeSpikeChance <= 0 || rand.nextInt(largeSpikeChance) == 0)) { + height += minLargeSpikeHeightGain + rand.nextInt(largeSpikeHeightVariance); + } + + int offsetHeight = height - originalHeight; + + for (int y = 0; y < height; ++y) { + float layerSize; + if (y >= offsetHeight) { + layerSize = (1.0F - (float) (y - offsetHeight) / (float) originalHeight) * size; + } else { + layerSize = largeSpikeFillerSize; + } + int width = MathHelper.ceiling_float_int(layerSize); + + for (int x = -width; x <= width; ++x) { + float xDist = MathHelper.abs_int(x) - 0.25F; + + for (int z = -width; z <= width; ++z) { + float zDist = MathHelper.abs_int(z) - 0.25F; + + if ((x == 0 && z == 0 || xDist * xDist + zDist * zDist <= layerSize * layerSize) + && (x != -width && x != width && z != -width && z != width || rand.nextFloat() <= 0.75F)) { + + generateBlock(world, xStart + x, yStart + y, zStart + z, genBlock, cluster); + + if (y != 0 && width > 1) { + generateBlock(world, xStart + x, yStart - y + offsetHeight, zStart + z, genBlock, cluster); + } + } + } + } + } + + return true; + } +} diff --git a/src/main/java/cofh/lib/world/WorldGenStalactite.java b/src/main/java/cofh/lib/world/WorldGenStalactite.java new file mode 100644 index 00000000..96eb4040 --- /dev/null +++ b/src/main/java/cofh/lib/world/WorldGenStalactite.java @@ -0,0 +1,48 @@ +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; + +public class WorldGenStalactite extends WorldGenStalagmite { + + public WorldGenStalactite(List resource, List block, List gblock) { + + super(resource, block, gblock); + } + + @Override + public boolean generate(World world, Random rand, int xStart, int yStart, int zStart) { + + int end = world.getActualHeight(); + while (world.isAirBlock(xStart, yStart, zStart) && yStart < end) { + ++yStart; + } + + if (!canGenerateInBlock(world, xStart, yStart--, zStart, baseBlock)) { + return false; + } + + int maxHeight = rand.nextInt(heightVariance) + minHeight; + + int size = genSize > 0 ? genSize : maxHeight / heightMod + rand.nextInt(sizeVariance); + boolean r = false; + for (int x = -size; x <= size; ++x) { + for (int z = -size; z <= size; ++z) { + if (!canGenerateInBlock(world, xStart + x, yStart + 1, zStart + z, baseBlock)) { + continue; + } + int height = getHeight(x, z, size, rand, maxHeight); + for (int y = 0; y < height; ++y) { + r |= generateBlock(world, xStart + x, yStart - y, zStart + z, genBlock, cluster); + } + } + } + return r; + } +} diff --git a/src/main/java/cofh/lib/world/WorldGenStalagmite.java b/src/main/java/cofh/lib/world/WorldGenStalagmite.java new file mode 100644 index 00000000..94ba71cb --- /dev/null +++ b/src/main/java/cofh/lib/world/WorldGenStalagmite.java @@ -0,0 +1,98 @@ +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 WorldGenStalagmite extends WorldGenerator { + + protected final List cluster; + protected final WeightedRandomBlock[] baseBlock; + protected final WeightedRandomBlock[] genBlock; + public int minHeight = 7; + public int heightVariance = 4; + public int sizeVariance = 2; + public int heightMod = 5; + public int genSize = 0; + public boolean smooth = false; + public boolean fat = true; + public boolean altSinc = false; + + public WorldGenStalagmite(List resource, List block, List gblock) { + + cluster = resource; + baseBlock = block.toArray(new WeightedRandomBlock[block.size()]); + genBlock = gblock.toArray(new WeightedRandomBlock[gblock.size()]); + } + + protected int getHeight(int x, int z, int size, Random rand, int height) { + + if (smooth) { + if ((x * x + z * z) * 4 >= size * size * 5) { + return 0; + } + + final double lim = (altSinc ? 600f : (fat ? 1f : .5f) * 400f) / size; + final double pi = Math.PI; + double r; + r = Math.sqrt((r = ((x * lim) / pi)) * r + (r = ((z * lim) / pi)) * r) * pi / 180; + if (altSinc && r < 1) { + r = Math.sqrt((size * 2 * lim) / pi) * pi / 180; + } + if (r == 0) { + return height; + } + if (!altSinc) { + return (int) Math.round(height * (fat ? Math.sin(r) / r : Math.sin(r = r * pi) / r)); + } + double sinc = (Math.sin(r) / r); + return (int) Math.round(height * (sinc * 2 + (Math.sin(r = r * (pi * 4)) / r)) / 2 + rand.nextGaussian() * .75); + } else { + int absx = x < 0 ? -x : x, absz = (z < 0 ? -z : z); + int dist = fat ? (absx < absz ? absz + absx / 2 : absx + absz / 2) : absx + absz; + if (dist == 0) { + return height; + } + int v = 1 + height / dist; + return v > 1 ? rand.nextInt(v) : 0; + } + } + + @Override + public boolean generate(World world, Random rand, int xStart, int yStart, int zStart) { + + while (world.isAirBlock(xStart, yStart, zStart) && yStart > 0) { + --yStart; + } + + if (!canGenerateInBlock(world, xStart, yStart++, zStart, baseBlock)) { + return false; + } + + int maxHeight = (heightVariance > 0 ? rand.nextInt(heightVariance) : 0) + minHeight; + + int size = (genSize > 0 ? genSize : maxHeight / heightMod); + if (sizeVariance > 0) { + size += rand.nextInt(sizeVariance); + } + boolean r = false; + for (int x = -size; x <= size; ++x) { + for (int z = -size; z <= size; ++z) { + if (!canGenerateInBlock(world, xStart + x, yStart - 1, zStart + z, baseBlock)) { + continue; + } + int height = getHeight(x, z, size, rand, maxHeight); + for (int y = 0; y < height; ++y) { + r |= generateBlock(world, xStart + x, yStart + y, zStart + z, genBlock, cluster); + } + } + } + return r; + } +} diff --git a/src/main/java/cofh/lib/world/biome/BiomeDictionaryArbiter.java b/src/main/java/cofh/lib/world/biome/BiomeDictionaryArbiter.java new file mode 100644 index 00000000..6ab60aa1 --- /dev/null +++ b/src/main/java/cofh/lib/world/biome/BiomeDictionaryArbiter.java @@ -0,0 +1,48 @@ +package cofh.lib.world.biome; + +import cpw.mods.fml.common.Loader; +import cpw.mods.fml.common.LoaderState; + +import java.util.HashMap; + +import net.minecraft.world.biome.BiomeGenBase; +import net.minecraftforge.common.BiomeDictionary; +import net.minecraftforge.common.BiomeDictionary.Type; + +public class BiomeDictionaryArbiter { + + private static HashMap types = new HashMap(); + private static HashMap biomes = new HashMap(); + private static boolean loaded = Loader.instance().isInState(LoaderState.AVAILABLE); + + public static Type[] getTypesForBiome(BiomeGenBase biome) { + + if (loaded) { + Type[] r = types.get(biome); + if (r == null) { + types.put(biome, r = BiomeDictionary.getTypesForBiome(biome)); + } + return r; + } + loaded = Loader.instance().isInState(LoaderState.AVAILABLE); + return BiomeDictionary.getTypesForBiome(biome); + } + + public static BiomeGenBase[] getTypesForBiome(Type type) { + + if (loaded) { + BiomeGenBase[] r = biomes.get(type); + if (r == null) { + biomes.put(type, r = BiomeDictionary.getBiomesForType(type)); + } + return r; + } + loaded = Loader.instance().isInState(LoaderState.AVAILABLE); + return BiomeDictionary.getBiomesForType(type); + } + + private BiomeDictionaryArbiter() { + + throw new IllegalArgumentException(); + } +} diff --git a/src/main/java/cofh/lib/world/biome/BiomeInfo.java b/src/main/java/cofh/lib/world/biome/BiomeInfo.java new file mode 100644 index 00000000..ac8d0f95 --- /dev/null +++ b/src/main/java/cofh/lib/world/biome/BiomeInfo.java @@ -0,0 +1,67 @@ +package cofh.lib.world.biome; + +import java.util.Collection; +import java.util.Random; + +import net.minecraft.world.biome.BiomeGenBase; +import net.minecraft.world.biome.BiomeGenBase.TempCategory; +import net.minecraftforge.common.BiomeDictionary; +import net.minecraftforge.common.BiomeDictionary.Type; + +public class BiomeInfo { + + private final Object data; + private final boolean whitelist; + private final int type; + private final int hash; + + public BiomeInfo(String name) { + + data = name; + hash = name.hashCode(); + whitelist = true; + type = 0; + } + + public BiomeInfo(Object d, int t, boolean wl) { + + data = d; + hash = 0; + whitelist = wl; + type = t; + } + + @SuppressWarnings("unchecked") + public boolean isBiomeEqual(BiomeGenBase biome, Random rand) { + + if (biome != null) { + switch (type) { + default: + break; + case 0: + String name = biome.biomeName; + return name.hashCode() == hash && name.equals(data); + case 1: + return biome.getTempCategory() == data == whitelist; + case 2: + return BiomeDictionary.isBiomeOfType(biome, (Type) data) == whitelist; + case 4: + return ((Collection) data).contains(biome.biomeName); + case 5: + return ((Collection) data).contains(biome.getTempCategory()) == whitelist; + case 6: + Type[] d = (Type[]) data; + int c = 0, + e = d.length; + for (int i = 0; i < e; ++i) { + if (BiomeDictionary.isBiomeOfType(biome, d[i])) { + ++c; + } + } + return c == e == whitelist; + } + } + return !whitelist; + } + +} diff --git a/src/main/java/cofh/lib/world/biome/BiomeInfoRarity.java b/src/main/java/cofh/lib/world/biome/BiomeInfoRarity.java new file mode 100644 index 00000000..d68e68ae --- /dev/null +++ b/src/main/java/cofh/lib/world/biome/BiomeInfoRarity.java @@ -0,0 +1,32 @@ +package cofh.lib.world.biome; + +import java.util.Random; + +import net.minecraft.world.biome.BiomeGenBase; + +public class BiomeInfoRarity extends BiomeInfo { + + private final int rarity; + + public BiomeInfoRarity(String name, int r) { + + super(name); + rarity = r; + } + + public BiomeInfoRarity(Object d, int t, boolean wl, int r) { + + super(d, t, wl); + rarity = r; + } + + @Override + public boolean isBiomeEqual(BiomeGenBase biome, Random rand) { + + boolean r = super.isBiomeEqual(biome, rand); + if (rand != null) { + return r ? rarity <= 1 || rand.nextInt(rarity) == 0 : false; + } + return r; + } +} diff --git a/src/main/java/cofh/lib/world/biome/BiomeInfoSet.java b/src/main/java/cofh/lib/world/biome/BiomeInfoSet.java new file mode 100644 index 00000000..1587c8b8 --- /dev/null +++ b/src/main/java/cofh/lib/world/biome/BiomeInfoSet.java @@ -0,0 +1,269 @@ +package cofh.lib.world.biome; + +import java.util.Arrays; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Random; +import java.util.Set; + +import net.minecraft.world.biome.BiomeGenBase; + +public class BiomeInfoSet implements Set { + + protected BiomeInfo[] elementData; + protected int size, modCount; + + public BiomeInfoSet() { + + this(10); + } + + public BiomeInfoSet(int size) { + + elementData = new BiomeInfo[size]; + } + + public BiomeInfoSet(Collection c) { + + elementData = c.toArray(new BiomeInfo[c.size()]); + } + + public void ensureCapacity(int minCapacity) { + + modCount++; + int oldCapacity = elementData.length; + if (minCapacity > oldCapacity) { + int newCapacity = (oldCapacity * 3) / 2 + 1; + if (newCapacity < minCapacity) { + newCapacity = minCapacity; + } + elementData = Arrays.copyOf(elementData, newCapacity); + } + } + + @Override + public int size() { + + return size; + } + + @Override + public boolean isEmpty() { + + return size == 0; + } + + @Override + public boolean contains(Object o) { + + BiomeInfo[] oldData = elementData; + if (o instanceof BiomeGenBase) { + BiomeGenBase bgb = (BiomeGenBase) o; + for (int i = 0, e = size; i < e; ++i) { + if (oldData[i] != null && oldData[i].isBiomeEqual(bgb, null)) { + return true; + } + } + return false; + } + for (int i = 0, e = size; i < e; ++i) { + if (oldData[i] == o || (oldData[i] != null && o != null && oldData[i].equals(o))) { + return true; + } + } + return false; + } + + public boolean contains(BiomeGenBase bgb, Random rand) { + + BiomeInfo[] oldData = elementData; + for (int i = 0, e = size; i < e; ++i) { + if (oldData[i] != null && oldData[i].isBiomeEqual(bgb, rand)) { + return true; + } + } + return false; + } + + public BiomeInfo get(int i) { + + if ((i < 0) | i >= size) { + throw new IndexOutOfBoundsException(); + } + return elementData[i]; + } + + @Override + public Iterator iterator() { + + return new Itr(); + } + + @Override + public Object[] toArray() { + + return Arrays.copyOf(elementData, size, Object[].class); + } + + @Override + public T[] toArray(T[] a) { + + if (a.length < size) { + return (T[]) Arrays.copyOf(elementData, size, a.getClass()); + } + System.arraycopy(elementData, 0, a, 0, size); + if (a.length > size) { + a[size] = null; + } + return a; + } + + @Override + public boolean add(BiomeInfo e) { + + int i = size; + ensureCapacity(size = i + 1); + elementData[i] = e; + return true; + } + + @Override + public boolean remove(Object o) { + + for (int i = 0, e = size; i < e; ++i) { + if (elementData[i] == o) { + fastRemove(i); + return true; + } + } + return false; + } + + protected void fastRemove(int index) { + + modCount++; + int numMoved = size - index - 1; + if (numMoved > 0) { + System.arraycopy(elementData, index + 1, elementData, index, numMoved); + } + elementData[--size] = null; + } + + @Override + public boolean containsAll(Collection c) { + + boolean r = true; + for (Object i : c) { + r |= contains(i); + } + return r; + } + + @Override + public boolean addAll(Collection c) { + + int oSize = c.size(); + if (oSize == 0) { + return false; + } + BiomeInfo[] a = c.toArray(new BiomeInfo[oSize]); + ensureCapacity(size + oSize); + System.arraycopy(a, 0, elementData, size, oSize); + size += oSize; + return true; + } + + @Override + public boolean retainAll(Collection c) { + + BiomeInfo[] oldData = elementData; + int e = oldData.length; + elementData = new BiomeInfo[c.size() / 2]; + size = 0; + boolean r = true; + for (Object o : c) { + for (int i = 0; i < e; ++i) { + if (oldData[i] == o || (oldData[i] != null && o != null && oldData[i].equals(o))) { + r |= add(oldData[i]); + } + } + } + return r; + } + + @Override + public boolean removeAll(Collection c) { + + boolean r = true; + for (Object i : c) { + r |= remove(i); + } + return r; + } + + @Override + public void clear() { + + modCount++; + + for (int i = 0; i < size; i++) { + elementData[i] = null; + } + + size = 0; + } + + protected class Itr implements Iterator { + + protected int cursor = 0; + protected int expectedModCount = BiomeInfoSet.this.modCount; + protected boolean lastRet = false; + + @Override + public boolean hasNext() { + + return cursor != BiomeInfoSet.this.size; + } + + @Override + public BiomeInfo next() { + + checkForComodification(); + try { + if (cursor < size) { + lastRet = true; + return BiomeInfoSet.this.elementData[cursor++]; + } + } catch (IndexOutOfBoundsException e) { + checkForComodification(); + } + throw new NoSuchElementException(); + } + + @Override + public void remove() { + + if (!lastRet) { + throw new IllegalStateException(); + } + checkForComodification(); + + try { + lastRet = false; + BiomeInfoSet.this.remove(--cursor); + expectedModCount = modCount; + } catch (IndexOutOfBoundsException e) { + throw new ConcurrentModificationException(); + } + } + + protected final void checkForComodification() { + + if (BiomeInfoSet.this.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + } +} diff --git a/src/main/java/cofh/lib/world/feature/FeatureBase.java b/src/main/java/cofh/lib/world/feature/FeatureBase.java new file mode 100644 index 00000000..d474eb55 --- /dev/null +++ b/src/main/java/cofh/lib/world/feature/FeatureBase.java @@ -0,0 +1,130 @@ +package cofh.lib.world.feature; + +import cofh.api.world.IFeatureGenerator; +import cofh.lib.world.biome.BiomeInfo; +import cofh.lib.world.biome.BiomeInfoSet; + +import gnu.trove.set.hash.THashSet; + +import java.util.Random; +import java.util.Set; + +import net.minecraft.world.World; +import net.minecraft.world.biome.BiomeGenBase; + +public abstract class FeatureBase implements IFeatureGenerator { + + public static enum GenRestriction { + NONE, BLACKLIST, WHITELIST; + + public static GenRestriction get(String restriction) { + + if (restriction.equalsIgnoreCase("blacklist")) { + return BLACKLIST; + } + if (restriction.equalsIgnoreCase("whitelist")) { + return WHITELIST; + } + return NONE; + } + } + + public final String name; + public final GenRestriction biomeRestriction; + public final GenRestriction dimensionRestriction; + public final boolean regen; + protected int rarity; + protected final BiomeInfoSet biomes = new BiomeInfoSet(1); + protected final Set dimensions = new THashSet(); + + /** + * Shortcut to add a Feature with no biome or dimension restriction. + */ + public FeatureBase(String name, boolean regen) { + + this(name, GenRestriction.NONE, regen, GenRestriction.NONE); + } + + /** + * Shortcut to add a Feature with a dimension restriction but no biome restriction. + */ + public FeatureBase(String name, boolean regen, GenRestriction dimRes) { + + this(name, GenRestriction.NONE, regen, dimRes); + } + + /** + * Shortcut to add a Feature with a biome restriction but no dimension restriction. + */ + public FeatureBase(String name, GenRestriction biomeRes, boolean regen) { + + this(name, biomeRes, regen, GenRestriction.NONE); + } + + public FeatureBase(String name, GenRestriction biomeRes, boolean regen, GenRestriction dimRes) { + + this.name = name; + this.biomeRestriction = biomeRes; + this.dimensionRestriction = dimRes; + this.regen = regen; + } + + public void setRarity(int rarity) { + + this.rarity = rarity; + } + + public FeatureBase addBiome(BiomeInfo biome) { + + biomes.add(biome); + return this; + } + + public FeatureBase addBiomes(BiomeInfoSet biomes) { + + this.biomes.addAll(biomes); + return this; + } + + public FeatureBase addDimension(int dimID) { + + dimensions.add(dimID); + return this; + } + + /* IFeatureGenerator */ + @Override + public final String getFeatureName() { + + return name; + } + + @Override + public boolean generateFeature(Random random, int chunkX, int chunkZ, World world, boolean newGen) { + + if (!newGen && !regen) { + return false; + } + if (dimensionRestriction != GenRestriction.NONE + && dimensionRestriction == GenRestriction.BLACKLIST == dimensions.contains(world.provider.dimensionId)) { + return false; + } + if (rarity > 1 && random.nextInt(rarity) != 0) { + return false; + } + + return generateFeature(random, chunkX, chunkZ, world); + } + + protected abstract boolean generateFeature(Random random, int chunkX, int chunkZ, World world); + + protected boolean canGenerateInBiome(World world, int x, int z, Random rand) { + + if (biomeRestriction != GenRestriction.NONE) { + BiomeGenBase biome = world.getBiomeGenForCoords(x, z); + return !(biomeRestriction == GenRestriction.BLACKLIST == biomes.contains(biome, rand)); + } + return true; + } + +} diff --git a/src/main/java/cofh/lib/world/feature/FeatureGenCave.java b/src/main/java/cofh/lib/world/feature/FeatureGenCave.java new file mode 100644 index 00000000..35fa1d42 --- /dev/null +++ b/src/main/java/cofh/lib/world/feature/FeatureGenCave.java @@ -0,0 +1,87 @@ +package cofh.lib.world.feature; + +import java.util.Random; + +import net.minecraft.block.Block; +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.WorldGenerator; + +public class FeatureGenCave extends FeatureBase { + + final WorldGenerator worldGen; + final int count; + final boolean ceiling; + + public FeatureGenCave(String name, WorldGenerator worldGen, boolean ceiling, int count, GenRestriction biomeRes, boolean regen, GenRestriction dimRes) { + + super(name, biomeRes, regen, dimRes); + this.worldGen = worldGen; + this.count = count; + this.ceiling = ceiling; + } + + @Override + protected boolean generateFeature(Random random, int chunkX, int chunkZ, World world) { + + int averageSeaLevel = world.provider.getAverageGroundLevel() + 1; + int blockX = chunkX * 16; + int blockZ = chunkZ * 16; + + boolean generated = false; + for (int i = 0; i < count; i++) { + int x = blockX + random.nextInt(16); + int z = blockZ + random.nextInt(16); + if (!canGenerateInBiome(world, x, z, random)) { + continue; + } + int seaLevel = averageSeaLevel; + if (seaLevel < 20) { + seaLevel = world.getHeightValue(x, z); + } + + int stopY = random.nextInt(1 + seaLevel / 2); + int y = stopY; + Block block; + do { + block = world.getBlock(x, y, z); + } while (!block.isAir(world, x, y, z) && ++y < seaLevel); + + if (y == seaLevel) { + y = 0; + do { + block = world.getBlock(x, y, z); + } while (!block.isAir(world, x, y, z) && ++y < stopY); + if (y == stopY) { + continue; + } + } + + if (ceiling) { + if (y < stopY) { + seaLevel = stopY + 1; + } + do { + ++y; + block = world.getBlock(x, y, z); + } while (y < seaLevel && block.isAir(world, x, y, z)); + if (y == seaLevel) { + continue; + } + --y; + } else if (block.isAir(world, x, y - 1, z)) { + --y; + do { + block = world.getBlock(x, y, z); + } while (block.isAir(world, x, y, z) && y-- > 0); + if (y == -1) { + continue; + } + ++y; + } + + generated |= worldGen.generate(world, random, x, y, z); + } + return generated; + } + +} diff --git a/src/main/java/cofh/lib/world/feature/FeatureGenLargeVein.java b/src/main/java/cofh/lib/world/feature/FeatureGenLargeVein.java new file mode 100644 index 00000000..8436996c --- /dev/null +++ b/src/main/java/cofh/lib/world/feature/FeatureGenLargeVein.java @@ -0,0 +1,68 @@ +package cofh.lib.world.feature; + +import java.util.Random; + +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.WorldGenerator; + +public class FeatureGenLargeVein extends FeatureBase { + + final WorldGenerator worldGen; + final int count; + final int minY; + private int veinHeight, veinDiameter; + private int verticalDensity; + private int horizontalDensity; + + public FeatureGenLargeVein(String name, WorldGenerator worldGen, int count, int minY, GenRestriction biomeRes, boolean regen, GenRestriction dimRes, + int height, int diameter, int vDensity, int hDensity) { + + super(name, biomeRes, regen, dimRes); + this.worldGen = worldGen; + this.count = count; + this.minY = minY; + this.veinHeight = height; + this.veinDiameter = diameter; + this.verticalDensity = vDensity; + this.horizontalDensity = hDensity; + } + + public int getDensity(Random rand, int oreDistance, float oreDensity) { + + oreDensity = (oreDensity * 0.01f * (oreDistance >> 1)) + 1f; + int i = (int) oreDensity; + int rnd = oreDistance / i; + int r = 0; + for (; i > 0; --i) { + r += rand.nextInt(rnd); + } + return r; + } + + @Override + public boolean generateFeature(Random random, int chunkX, int chunkZ, World world) { + + int blockX = chunkX * 16; + int blockY = minY; + int blockZ = chunkZ * 16; + + Random dRand = new Random(world.getSeed()); + long l = (dRand.nextLong() / 2L) * 2L + 1L; + long l1 = (dRand.nextLong() / 2L) * 2L + 1L; + dRand.setSeed(chunkX * l + chunkZ * l1 ^ world.getSeed()); + + boolean generated = false; + for (int i = count; i-- > 0;) { + int x = blockX + getDensity(dRand, veinDiameter, horizontalDensity); + int y = blockY + getDensity(dRand, veinHeight, verticalDensity); + int z = blockZ + getDensity(dRand, veinDiameter, horizontalDensity); + if (!canGenerateInBiome(world, x, z, random)) { + continue; + } + + generated |= worldGen.generate(world, random, x, y, z); + } + return generated; + } + +} diff --git a/src/main/java/cofh/lib/world/feature/FeatureGenNormal.java b/src/main/java/cofh/lib/world/feature/FeatureGenNormal.java new file mode 100644 index 00000000..35dac819 --- /dev/null +++ b/src/main/java/cofh/lib/world/feature/FeatureGenNormal.java @@ -0,0 +1,45 @@ +package cofh.lib.world.feature; + +import java.util.Random; + +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.WorldGenerator; + +public class FeatureGenNormal extends FeatureBase { + + final WorldGenerator worldGen; + final int count; + final int meanY; + final int maxVar; + + public FeatureGenNormal(String name, WorldGenerator worldGen, int count, int meanY, int maxVar, GenRestriction biomeRes, boolean regen, + GenRestriction dimRes) { + + super(name, biomeRes, regen, dimRes); + this.worldGen = worldGen; + this.count = count; + this.meanY = meanY; + this.maxVar = maxVar; + } + + @Override + public boolean generateFeature(Random random, int chunkX, int chunkZ, World world) { + + int blockX = chunkX * 16; + int blockZ = chunkZ * 16; + + boolean generated = false; + for (int i = 0; i < count; i++) { + int x = blockX + random.nextInt(16); + int y = maxVar <= 1 ? meanY : (random.nextInt(maxVar) + random.nextInt(maxVar) + meanY - maxVar); + int z = blockZ + random.nextInt(16); + if (!canGenerateInBiome(world, x, z, random)) { + continue; + } + + generated |= worldGen.generate(world, random, x, y, z); + } + return generated; + } + +} diff --git a/src/main/java/cofh/lib/world/feature/FeatureGenSurface.java b/src/main/java/cofh/lib/world/feature/FeatureGenSurface.java new file mode 100644 index 00000000..65de7100 --- /dev/null +++ b/src/main/java/cofh/lib/world/feature/FeatureGenSurface.java @@ -0,0 +1,58 @@ +package cofh.lib.world.feature; + +import static cofh.lib.world.WorldGenMinableCluster.canGenerateInBlock; + +import cofh.lib.util.WeightedRandomBlock; +import cofh.lib.util.helpers.BlockHelper; + +import java.util.List; +import java.util.Random; + +import net.minecraft.block.Block; +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.WorldGenerator; + +public class FeatureGenSurface extends FeatureBase { + + final WorldGenerator worldGen; + final int count; + final WeightedRandomBlock[] matList; + + public FeatureGenSurface(String name, WorldGenerator worldGen, List matList, int count, GenRestriction biomeRes, boolean regen, + GenRestriction dimRes) { + + super(name, biomeRes, regen, dimRes); + this.worldGen = worldGen; + this.count = count; + this.matList = matList.toArray(new WeightedRandomBlock[matList.size()]); + } + + @Override + public boolean generateFeature(Random random, int chunkX, int chunkZ, World world) { + + int blockX = chunkX * 16; + int blockZ = chunkZ * 16; + + boolean generated = false; + for (int i = 0; i < count; i++) { + int x = blockX + random.nextInt(16); + int z = blockZ + random.nextInt(16); + if (!canGenerateInBiome(world, x, z, random)) { + continue; + } + + int y = BlockHelper.getSurfaceBlockY(world, x, z); + l: { + Block block = world.getBlock(x, y, z); + if (!block.isAir(world, x, y, z) && canGenerateInBlock(world, x, y, z, matList)) { + break l; + } + continue; + } + + generated |= worldGen.generate(world, random, x, y + 1, z); + } + return generated; + } + +} diff --git a/src/main/java/cofh/lib/world/feature/FeatureGenTopBlock.java b/src/main/java/cofh/lib/world/feature/FeatureGenTopBlock.java new file mode 100644 index 00000000..d4618c7f --- /dev/null +++ b/src/main/java/cofh/lib/world/feature/FeatureGenTopBlock.java @@ -0,0 +1,58 @@ +package cofh.lib.world.feature; + +import static cofh.lib.world.WorldGenMinableCluster.canGenerateInBlock; + +import cofh.lib.util.WeightedRandomBlock; +import cofh.lib.util.helpers.BlockHelper; + +import java.util.List; +import java.util.Random; + +import net.minecraft.block.Block; +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.WorldGenerator; + +public class FeatureGenTopBlock extends FeatureBase { + + final WorldGenerator worldGen; + final int count; + final WeightedRandomBlock[] matList; + + public FeatureGenTopBlock(String name, WorldGenerator worldGen, List matList, int count, GenRestriction biomeRes, boolean regen, + GenRestriction dimRes) { + + super(name, biomeRes, regen, dimRes); + this.worldGen = worldGen; + this.count = count; + this.matList = matList.toArray(new WeightedRandomBlock[matList.size()]); + } + + @Override + public boolean generateFeature(Random random, int chunkX, int chunkZ, World world) { + + int blockX = chunkX * 16; + int blockZ = chunkZ * 16; + + boolean generated = false; + for (int i = 0; i < count; i++) { + int x = blockX + random.nextInt(16); + int z = blockZ + random.nextInt(16); + if (!canGenerateInBiome(world, x, z, random)) { + continue; + } + + int y = BlockHelper.getTopBlockY(world, x, z); + l: { + Block block = world.getBlock(x, y, z); + if (!block.isAir(world, x, y, z) && canGenerateInBlock(world, x, y, z, matList)) { + break l; + } + continue; + } + + generated |= worldGen.generate(world, random, x, y + 1, z); + } + return generated; + } + +} diff --git a/src/main/java/cofh/lib/world/feature/FeatureGenUnderfluid.java b/src/main/java/cofh/lib/world/feature/FeatureGenUnderfluid.java new file mode 100644 index 00000000..b49181f3 --- /dev/null +++ b/src/main/java/cofh/lib/world/feature/FeatureGenUnderfluid.java @@ -0,0 +1,97 @@ +package cofh.lib.world.feature; + +import cofh.lib.util.WeightedRandomBlock; +import cofh.lib.util.helpers.BlockHelper; +import cofh.lib.util.helpers.FluidHelper; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +import net.minecraft.block.Block; +import net.minecraft.block.material.Material; +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.WorldGenerator; +import net.minecraftforge.fluids.Fluid; + +public class FeatureGenUnderfluid extends FeatureBase { + + final boolean water; + final WorldGenerator worldGen; + final int count; + final List matList; + final int[] fluidList; + + public FeatureGenUnderfluid(String name, WorldGenerator worldGen, List matList, int count, GenRestriction biomeRes, boolean regen, + GenRestriction dimRes) { + + super(name, biomeRes, regen, dimRes); + this.worldGen = worldGen; + this.count = count; + this.matList = matList; + water = true; + fluidList = null; + } + + public FeatureGenUnderfluid(String name, WorldGenerator worldGen, List matList, int[] fluidList, int count, GenRestriction biomeRes, + boolean regen, GenRestriction dimRes) { + + super(name, biomeRes, regen, dimRes); + this.worldGen = worldGen; + this.count = count; + this.matList = matList; + water = false; + Arrays.sort(fluidList); + this.fluidList = fluidList; + } + + @Override + public boolean generateFeature(Random random, int chunkX, int chunkZ, World world) { + + int blockX = chunkX * 16; + int blockZ = chunkZ * 16; + + boolean generated = false; + for (int i = 0; i < count; i++) { + int x = blockX + random.nextInt(16); + int z = blockZ + random.nextInt(16); + if (!canGenerateInBiome(world, x, z, random)) { + continue; + } + + int y = BlockHelper.getSurfaceBlockY(world, x, z); + l: do { + Block block = world.getBlock(x, y, z); + if (water) { + if (block.getMaterial() == Material.water) { + continue; + } + if (world.getBlock(x, y + 1, z).getMaterial() != Material.water) { + continue; + } + } else { + Fluid fluid = FluidHelper.lookupFluidForBlock(block); + if (fluid != null && Arrays.binarySearch(fluidList, fluid.getID()) >= 0) { + continue; + } + + fluid = FluidHelper.lookupFluidForBlock(world.getBlock(x, y + 1, z)); + if (fluid == null || Arrays.binarySearch(fluidList, fluid.getID()) < 0) { + continue; + } + } + for (WeightedRandomBlock mat : matList) { + if (block.isReplaceableOreGen(world, x, y, z, mat.block)) { + break l; + } + } + } while (y-- > 1); + + if (y > 0) { + generated |= worldGen.generate(world, random, x, y, z); + } + } + return generated; + } + +} diff --git a/src/main/java/cofh/lib/world/feature/FeatureGenUniform.java b/src/main/java/cofh/lib/world/feature/FeatureGenUniform.java new file mode 100644 index 00000000..5bf35722 --- /dev/null +++ b/src/main/java/cofh/lib/world/feature/FeatureGenUniform.java @@ -0,0 +1,44 @@ +package cofh.lib.world.feature; + +import java.util.Random; + +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.WorldGenerator; + +public class FeatureGenUniform extends FeatureBase { + + final WorldGenerator worldGen; + final int count; + final int minY; + final int maxY; + + public FeatureGenUniform(String name, WorldGenerator worldGen, int count, int minY, int maxY, GenRestriction biomeRes, boolean regen, GenRestriction dimRes) { + + super(name, biomeRes, regen, dimRes); + this.worldGen = worldGen; + this.count = count; + this.minY = minY; + this.maxY = maxY; + } + + @Override + public boolean generateFeature(Random random, int chunkX, int chunkZ, World world) { + + int blockX = chunkX * 16; + int blockZ = chunkZ * 16; + + boolean generated = false; + for (int i = 0; i < count; i++) { + int x = blockX + random.nextInt(16); + int y = minY + random.nextInt(maxY - minY); + int z = blockZ + random.nextInt(16); + if (!canGenerateInBiome(world, x, z, random)) { + continue; + } + + generated |= worldGen.generate(world, random, x, y, z); + } + return generated; + } + +} diff --git a/src/main/java/cofh/lib/world/feature/package-info.java b/src/main/java/cofh/lib/world/feature/package-info.java new file mode 100644 index 00000000..5b8ce4fd --- /dev/null +++ b/src/main/java/cofh/lib/world/feature/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|world|feature") +package cofh.lib.world.feature; + +import cofh.lib.CoFHLibProps; +import cpw.mods.fml.common.API; + diff --git a/src/main/java/cofh/lib/world/package-info.java b/src/main/java/cofh/lib/world/package-info.java new file mode 100644 index 00000000..e5e6c88b --- /dev/null +++ b/src/main/java/cofh/lib/world/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|world") +package cofh.lib.world; + +import cofh.lib.CoFHLibProps; +import cpw.mods.fml.common.API; + diff --git a/src/main/resources/CoFH_at.cfg b/src/main/resources/META-INF/CoFH_at.cfg similarity index 84% rename from src/main/resources/CoFH_at.cfg rename to src/main/resources/META-INF/CoFH_at.cfg index b1cf76fc..55aa65e8 100644 --- a/src/main/resources/CoFH_at.cfg +++ b/src/main/resources/META-INF/CoFH_at.cfg @@ -48,6 +48,7 @@ public net.minecraft.world.WorldServer field_73068_P # allPlayersSleeping protected net.minecraft.inventory.Container * # senseless to have private stuff here protected net.minecraft.client.gui.inventory.GuiContainer * # senseless to have private stuff here protected net.minecraft.client.gui.FontRenderer *()V # all of it +protected net.minecraft.client.gui.FontRenderer func_78280_d(Ljava/lang/String;I)Ljava/lang/String; # wrapFormattedStringToWidth protected net.minecraft.client.gui.FontRenderer * # all of it public net.minecraft.entity.projectile.EntityFishHook * # (nearly) literally everything is private @@ -64,3 +65,13 @@ public net.minecraft.world.World field_72998_d # collidingBoundingBoxes # WorldProxy public-f net.minecraft.world.World *()V # Doesn't matter what it is, we need it public public-f net.minecraft.world.WorldServer *()V # Doesn't matter what it is, we need it public + +# CoFHLib + +protected net.minecraft.inventory.Container * # senseless to have private stuff here +protected net.minecraft.client.gui.inventory.GuiContainer * # senseless to have private stuff here +public net.minecraft.util.RegistrySimple field_82596_a # registryObjects +public-f net.minecraft.util.RegistryNamespaced field_148759_a # underlyingIntegerMap + +public net.minecraft.server.management.PlayerManager func_72690_a(IIZ)Lnet/minecraft/server/management/PlayerManager$PlayerInstance; # getOrCreateChunkWatcher +public net.minecraft.server.management.PlayerManager$PlayerInstance diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index 2f57474b..15f71bb1 100644 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -1,12 +1,12 @@ [ { - "modid": "CoFHCore", - "name": "CoFH Core", + "modid": "${modId}", + "name": "${modName}", "description": "Required for all CoFH Mods. Also provides some customization options for Minecraft.", - "version": "${version}", + "version": "${modVersion}", "credits": "Team CoFH", "logoFile": "assets/cofh/textures/logo.png", - "mcversion": "${mcversion}", + "mcversion": "${minecraftVersion}", "url": "http://www.teamcofh.com", "updateUrl": "", "authorList": [ "Team CoFH" ],