diff --git a/README.md b/README.md
index fb9841e..9cc8930 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
## Scott's Tweaks
-**LATEST OFFICIAL VERSION**: [Scott's Tweaks 1.1.0 for MC 1.7.10][latest] ([changelog][changelog.md]) ([all releases][releases])
-*DEPENDS ON*: [Kore Sample 1.2.2 (or later) for MC 1.7.10][koresample]
+**LATEST OFFICIAL VERSION**: [Scott's Tweaks 1.2.0 for MC 1.7.10][latest] ([changelog][changelog.md]) ([all releases][releases])
+*DEPENDS ON*: [Kore Sample 1.2.5 (or later) for MC 1.7.10][koresample]
[latest]: https://github.com/MinecraftModArchive/ScottsTweaks/releases/latest
[releases]: https://github.com/MinecraftModArchive/ScottsTweaks/releases
[changelog.md]: https://github.com/MinecraftModArchive/ScottsTweaks/blob/develop/src/main/resources/CHANGELOG.md
@@ -13,6 +13,7 @@ Scott's Tweaks adds several of my favorite gameplay tweaks to the game.
- Live chickens drop feathers as they go about their lives.
- Growables (saplings, etc.) plant themselves if possible.
- Clay generates in the overworld (not just in lake bottoms).
+- Squid and bat spawning can be reduced or disabled.
[Contributing](#contributing)
diff --git a/api/KoreSample/.gitignore b/api/KoreSample/.gitignore
new file mode 100644
index 0000000..a91a1cd
--- /dev/null
+++ b/api/KoreSample/.gitignore
@@ -0,0 +1,135 @@
+# Adapted from http://www.gitignote.io
+
+### Windows ###
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Windows Installer files
+*.cab
+*.msi
+*.msm
+*.msp
+
+
+### OSX ###
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+
+
+### Linux ###
+*~
+
+# KDE directory preferences
+.directory
+
+
+### Java ###
+*.class
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+
+### Eclipse ###
+*.pydevproject
+.metadata
+.gradle
+bin/
+tmp/
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.settings/
+.loadpath
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# CDT-specific
+.cproject
+
+# PDT-specific
+.buildpath
+
+# sbteclipse plugin
+.target
+
+# TeXlipse plugin
+.texlipse
+
+
+### Intellij ###
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
+
+## Directory-based project format
+.idea/
+/*.iml
+
+## File-based project format
+*.ipr
+*.iws
+
+## Additional for IntelliJ
+out/
+
+# generated by mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# generated by JIRA plugin
+atlassian-ide-plugin.xml
+
+# generated by Crashlytics plugin (for Android Studio and Intellij)
+com_crashlytics_export_strings.xml
+
+
+### Project ###
+.classpath
+.project
+run/
+
+### Gradle ###
+.gradle
+build/
+
+# Ignore Gradle GUI config
+gradle-app.setting
diff --git a/api/KoreSample/CONTRIBUTING.md b/api/KoreSample/CONTRIBUTING.md
new file mode 100644
index 0000000..2bbb036
--- /dev/null
+++ b/api/KoreSample/CONTRIBUTING.md
@@ -0,0 +1,43 @@
+#Contributing
+
+We love pull requests. Here is a quick guide:
+
+To get started, [sign the Contributor License Agreement][cla]. This project is in the public domain and we need you to agree that your submissions will also be in the public domain. See [unlicense.org](http://unlicense.org/).
+
+[cla]: https://www.clahub.com/agreements/MinecraftModArchive/KoreSample
+
+Fork, then clone the repo:
+
+ git clone git@github.com:your-username/KoreSample.git
+
+Check out the development branch:
+
+ git checkout develop
+
+Set up your development environment:
+
+ gradlew setupDecompWorkspace
+
+Setup your integrated development environment:
+
+ gradlew idea
+
+-or-
+
+ gradlew eclipse
+
+Make your changes.
+
+Push to your fork and [submit a pull request][pr].
+
+[pr]: https://github.com/MinecraftModArchive/KoreSample/compare/
+
+At this point, things are waiting on us. We may merge your code as is. We may suggest some changes or improvements or alternatives.
+
+###Some things that will increase the chance thate your pull request is accepted:
+
+* Write logical, thought out code.
+* Reuse your code.
+* Write a [good commit message][commit].
+
+[commit]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
diff --git a/api/KoreSample/README.md b/api/KoreSample/README.md
new file mode 100644
index 0000000..4560e9b
--- /dev/null
+++ b/api/KoreSample/README.md
@@ -0,0 +1,67 @@
+## Kore Sample
+**LATEST OFFICIAL VERSION**: [Kore Sample 1.2.5 for MC 1.7.10][latest] ([changelog][changelog.md]) ([all releases][releases])
+[latest]: https://github.com/MinecraftModArchive/KoreSample/releases/latest
+[releases]: https://github.com/MinecraftModArchive/KoreSample/releases
+[changelog.md]: https://github.com/MinecraftModArchive/KoreSample/blob/develop/src/main/resources/CHANGELOG.md
+
+![KoreSample](https://raw.githubusercontent.com/MinecraftModArchive/KoreSample/develop/src/main/resources/assets/logo.png)
+
+Kore Sample provides a set of tools and base classes for other Minecraft mods that depend on it.
+
+[Contributing](#contributing)
+
+[Support](#support)
+
+[Mod Packs](#mod-packs)
+
+[Licensing](#licensing)
+
+### Contributing
+
+Please see [CONTRIBUTING.md](CONTRIBUTING.md).
+
+### Support
+Something not quite right? Have a suggestion? Found a bug? Create an issue now!
+
+1. Make sure your issue hasn't already been answered or fixed. Also think about whether your issue is a valid one
+before submitting it.
+2. Go to [the issues page][issues].
+3. Click [`New Issue`][new] right below `Star` and `Fork`.
+4. Enter your Issue's title (something that summarizes your issue), and then create a detailed description ("Hey, could
+you add/change xxx?" or "Hey, I found an exploit.", etc.).
+5. Click `Submit new issue`, and wait for feedback!
+
+[issues]: /MinecraftModArchive/KoreSample/issues
+[new]: /MinecraftModArchive/KoreSample/issues/new
+
+* * *
+
+#### Mod Packs
+
+This mod is released to the public domain. (*See [below](#licensing).*) The authors have disclaimed all rights and
+anyone can use this software and source code as they wish. This means that anyone has permission to distribute this
+compiled mod in any form, including bundled with other mods in a "mod pack." In fact, this mod was written with that
+purpose in mind.
+
+* * *
+
+#### Licensing
+
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or
+as a compiled binary, for any purpose, commercial or non-commercial, and by any means.
+
+In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright
+interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the
+detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of
+all present and future rights to this software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT
+OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to [unlicense.org](http://unlicense.org/).
+
+![Public Domain](https://raw.githubusercontent.com/MinecraftModArchive/assets/master/pd-icon.png)
diff --git a/api/KoreSample/UNLICENSE b/api/KoreSample/UNLICENSE
new file mode 100644
index 0000000..a84c395
--- /dev/null
+++ b/api/KoreSample/UNLICENSE
@@ -0,0 +1,25 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+
diff --git a/api/KoreSample/build.gradle b/api/KoreSample/build.gradle
new file mode 100644
index 0000000..f642d64
--- /dev/null
+++ b/api/KoreSample/build.gradle
@@ -0,0 +1,110 @@
+evaluationDependsOn(':version')
+
+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'
+apply plugin: 'maven'
+
+group = "com.scottkillen.mod"
+archivesBaseName = "KoreSample"
+
+minecraft {
+ version = project.mcversion + "-" + project.forgeversion
+ runDir = "run"
+
+ replace '${mod_version}', project.version
+}
+
+processResources
+{
+ // this will ensure that this task is redone when the versions change.
+ inputs.property "version", project.version
+ inputs.property "mcversion", project.minecraft.version
+ inputs.property "forgeversion", project.ext.forgeversion
+
+ // replace stuff in the files we want.
+ from(sourceSets.main.resources.srcDirs) {
+ include '**/*.info'
+ include '**/version.properties'
+
+ expand 'mod_version': project.version, 'minecraft_version': project.minecraft.version, 'forge_version': project
+ .ext.forgeversion
+ }
+
+ // copy everything else, that we didn't do before
+ from(sourceSets.main.resources.srcDirs) {
+ exclude '**/*.info'
+ exclude '**/version.properties'
+ }
+}
+
+jar {
+ includeEmptyDirs = false
+}
+
+task sourcesJar(type: Jar, dependsOn: classes) {
+ from(sourceSets.main.output) {
+ include '**/*.info'
+ include '**/version.properties'
+
+ expand 'mod_version': project.version, 'minecraft_version': project.minecraft.version, 'forge_version': project
+ .ext.forgeversion
+ }
+
+ from(sourceSets.main.allSource) {
+ include '**/TheMod.java'
+
+ expand 'mod_version': project.version
+ }
+
+ from(sourceSets.main.allSource) {
+ exclude '**/*.info'
+ exclude '**/version.properties'
+ exclude '**/TheMod.java'
+ }
+
+ classifier = 'sources'
+}
+
+task deobfJar(type: Jar) {
+ from(sourceSets.main.output) {
+ include '**/*.info'
+ include '**/version.properties'
+
+ expand 'mod_version': project.version, 'minecraft_version': project.minecraft.version, 'forge_version': project
+ .ext.forgeversion
+ }
+
+ from(sourceSets.main.output) {
+ exclude '**/*.info'
+ exclude '**/version.properties'
+ }
+
+ classifier = 'deobf'
+}
+
+artifacts {
+ archives sourcesJar
+ archives deobfJar
+}
+
+uploadArchives {
+ repositories.mavenDeployer {
+ repository (url:"file://" + projectDir + "/build/maven")
+ }
+}
diff --git a/api/KoreSample/gradle/wrapper/gradle-wrapper.jar b/api/KoreSample/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..b761216
Binary files /dev/null and b/api/KoreSample/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/api/KoreSample/gradle/wrapper/gradle-wrapper.properties b/api/KoreSample/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..dc4c2ff
--- /dev/null
+++ b/api/KoreSample/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue Oct 07 22:55:33 EDT 2014
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.0-all.zip
diff --git a/api/KoreSample/gradlew b/api/KoreSample/gradlew
new file mode 100644
index 0000000..91a7e26
--- /dev/null
+++ b/api/KoreSample/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# 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
+ ;;
+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"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+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.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+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
+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
+
+# 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\""
+ fi
+ i=$((i+1))
+ 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 "$@"
diff --git a/api/KoreSample/gradlew.bat b/api/KoreSample/gradlew.bat
new file mode 100644
index 0000000..8a0b282
--- /dev/null
+++ b/api/KoreSample/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@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=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@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
+
+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.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+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.
+
+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%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="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
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/api/KoreSample/settings.gradle b/api/KoreSample/settings.gradle
new file mode 100644
index 0000000..d6855b7
--- /dev/null
+++ b/api/KoreSample/settings.gradle
@@ -0,0 +1 @@
+include 'version'
\ No newline at end of file
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/TheMod.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/TheMod.java
new file mode 100644
index 0000000..6cf81da
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/TheMod.java
@@ -0,0 +1,45 @@
+package com.scottkillen.mod.koresample;
+
+import com.scottkillen.mod.koresample.compat.versionchecker.Versioned;
+import cpw.mods.fml.common.Mod;
+import cpw.mods.fml.common.Mod.EventHandler;
+import cpw.mods.fml.common.Mod.Instance;
+import cpw.mods.fml.common.event.FMLInterModComms;
+import cpw.mods.fml.common.event.FMLPreInitializationEvent;
+
+@SuppressWarnings({
+ "StaticNonFinalField",
+ "WeakerAccess",
+ "StaticVariableMayNotBeInitialized",
+ "NonConstantFieldWithUpperCaseName",
+ "MethodMayBeStatic"
+})
+@Mod(modid = TheMod.MOD_ID, name = TheMod.MOD_NAME, version = TheMod.MOD_VERSION, useMetadata = true)
+public final class TheMod implements Versioned
+{
+ static final String MOD_ID = "koresample";
+ static final String MOD_NAME = "Kore Sample";
+ static final String MOD_VERSION = "${mod_version}";
+ private static final String RESOURCE_PREFIX = MOD_ID.toLowerCase() + ':';
+ private static final String VERSIONCHECK_URL =
+ "https://raw.githubusercontent.com/ScottKillen/glowing-ninja/master/KoreSample.json";
+ @Instance(MOD_ID)
+ public static TheMod INSTANCE;
+
+ public static String resourcePrefix() { return RESOURCE_PREFIX; }
+
+ @EventHandler
+ public void onFMLPreInitialization(FMLPreInitializationEvent event) { addVersionCheck(this); }
+
+ public void addVersionCheck(Versioned versionCheck)
+ {
+ FMLInterModComms.sendRuntimeMessage(versionCheck.modID(), "VersionChecker", "addVersionCheck",
+ versionCheck.versionInfoURL());
+ }
+
+ @Override
+ public String modID() { return MOD_ID; }
+
+ @Override
+ public String versionInfoURL() { return VERSIONCHECK_URL; }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/block/SlabBlock.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/block/SlabBlock.java
new file mode 100644
index 0000000..7853a93
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/block/SlabBlock.java
@@ -0,0 +1,129 @@
+package com.scottkillen.mod.koresample.common.block;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.scottkillen.mod.koresample.common.util.slab.TheSingleSlabRegistry;
+import com.scottkillen.mod.koresample.tree.DefinesSlab;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockSlab;
+import net.minecraft.block.material.Material;
+import net.minecraft.client.renderer.texture.IIconRegister;
+import net.minecraft.creativetab.CreativeTabs;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.IIcon;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+import static com.google.common.base.Preconditions.*;
+
+@SuppressWarnings("AbstractClassNeverImplemented")
+public abstract class SlabBlock extends BlockSlab
+{
+ public static final int CAPACITY = 8;
+ private static final int METADATA_MASK = CAPACITY - 1;
+ private static final TheSingleSlabRegistry slabRegistry = TheSingleSlabRegistry.REFERENCE;
+ private final ImmutableList subBlocks;
+
+ protected SlabBlock(boolean isDouble, Collection extends DefinesSlab> subBlocks)
+ {
+ super(isDouble, Material.wood);
+
+ checkArgument(!subBlocks.isEmpty());
+ checkArgument(subBlocks.size() <= CAPACITY);
+ this.subBlocks = ImmutableList.copyOf(subBlocks);
+ setBlockName("slab");
+ }
+
+ private static int mask(int metadata) {return metadata & METADATA_MASK;}
+
+ public static boolean isSingleSlab(Item item)
+ {
+ return slabRegistry.isSingleSlab(item);
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ protected static String getUnwrappedUnlocalizedName(String unlocalizedName)
+ {
+ return unlocalizedName.substring(unlocalizedName.indexOf('.') + 1);
+ }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public final IIcon getIcon(int side, int metadata)
+ {
+ final DefinesSlab subBlock = subBlocks.get(mask(metadata));
+ final Block modelBlock = subBlock.slabModelBlock();
+ final int modelBlockMetadata = subBlock.slabModelSubBlockIndex();
+ return modelBlock.getIcon(side, modelBlockMetadata);
+ }
+
+ @Override
+ public final Item getItemDropped(int metadata, Random unused, int unused2)
+ {
+ final DefinesSlab subBlock = subBlocks.get(mask(metadata));
+ return Item.getItemFromBlock(subBlock.singleSlabBlock());
+ }
+
+ @Override
+ public boolean getUseNeighborBrightness()
+ {
+ // Fix lighting bugs
+ return true;
+ }
+
+ @Override
+ protected final ItemStack createStackedBlock(int metadata)
+ {
+ final DefinesSlab subBlock = subBlocks.get(mask(metadata));
+ return new ItemStack(Item.getItemFromBlock(subBlock.singleSlabBlock()), 2, subBlock.slabSubBlockIndex());
+ }
+
+ @Override
+ public final String getUnlocalizedName()
+ {
+ return String.format("tile.%s%s", resourcePrefix(), getUnwrappedUnlocalizedName(super.getUnlocalizedName()));
+ }
+
+ @SuppressWarnings("unchecked")
+ @SideOnly(Side.CLIENT)
+ @Override
+ public final void getSubBlocks(Item item, CreativeTabs unused, List subblocks)
+ {
+ if (isSingleSlab(item))
+ {
+ for (int i = 0; i < subBlocks.size(); ++i)
+ {
+ //noinspection ObjectAllocationInLoop
+ subblocks.add(new ItemStack(item, 1, i));
+ }
+ }
+ }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public final void registerBlockIcons(IIconRegister unused) {}
+
+ @Override
+ public final String func_150002_b(int metadata)
+ {
+ int metadata1 = metadata;
+ if (metadata1 < 0 || metadata1 >= subBlocks.size())
+ {
+ metadata1 = 0;
+ }
+
+ return getUnlocalizedName() + '.' + subBlocks.get(metadata1).slabName();
+ }
+
+ protected abstract String resourcePrefix();
+
+ protected final List subBlocks() { return Collections.unmodifiableList(subBlocks); }
+
+ @Override
+ public String toString() { return Objects.toStringHelper(this).add("subBlocks", subBlocks).toString(); }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/block/StairsBlock.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/block/StairsBlock.java
new file mode 100644
index 0000000..0601ef6
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/block/StairsBlock.java
@@ -0,0 +1,35 @@
+package com.scottkillen.mod.koresample.common.block;
+
+import com.scottkillen.mod.koresample.tree.DefinesStairs;
+import net.minecraft.block.BlockStairs;
+
+@SuppressWarnings("AbstractClassNeverImplemented")
+public abstract class StairsBlock extends BlockStairs
+{
+ protected StairsBlock(DefinesStairs model)
+ {
+ super(model.stairsModelBlock(), model.stairsModelSubBlockIndex());
+ }
+
+ @Override
+ public boolean getUseNeighborBrightness()
+ {
+ // Fix lighting bugs
+ return true;
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ protected static String getUnwrappedUnlocalizedName(String unlocalizedName)
+ {
+ return unlocalizedName.substring(unlocalizedName.indexOf('.') + 1);
+ }
+
+ @Override
+ public final String getUnlocalizedName()
+ {
+ //noinspection StringConcatenationMissingWhitespace
+ return "tile." + resourcePrefix() + getUnwrappedUnlocalizedName(super.getUnlocalizedName());
+ }
+
+ protected abstract String resourcePrefix();
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/util/event/ForgeEventListener.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/util/event/ForgeEventListener.java
new file mode 100644
index 0000000..c955a8e
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/util/event/ForgeEventListener.java
@@ -0,0 +1,12 @@
+package com.scottkillen.mod.koresample.common.util.event;
+
+import cpw.mods.fml.common.eventhandler.EventBus;
+
+@SuppressWarnings({ "AbstractClassNeverImplemented", "WeakerAccess" })
+public abstract class ForgeEventListener
+{
+ public void listen(EventBus eventBus)
+ {
+ eventBus.register(this);
+ }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/util/log/Logger.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/util/log/Logger.java
new file mode 100644
index 0000000..d3d4900
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/util/log/Logger.java
@@ -0,0 +1,42 @@
+package com.scottkillen.mod.koresample.common.util.log;
+
+import com.google.common.base.Objects;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+
+public final class Logger
+{
+ private final org.apache.logging.log4j.Logger logger;
+
+ private Logger(String modID)
+ {
+ logger = LogManager.getLogger(modID);
+ }
+
+ public static Logger forMod(String modID)
+ {
+ return new Logger(modID);
+ }
+
+ public void info(final String format, final Object... args) { log(Level.INFO, format, args); }
+
+ public void log(final Level level, final Throwable exception, final String format, final Object... args)
+ {
+ logger.log(level, String.format(format, args), exception);
+ }
+
+ private void log(final Level level, final String format, final Object... data)
+ {
+ logger.log(level, String.format(format, data));
+ }
+
+ public void severe(final String format, final Object... args) { log(Level.ERROR, format, args); }
+
+ public void warning(final String format, final Object... args) { log(Level.WARN, format, args); }
+
+ @Override
+ public String toString()
+ {
+ return Objects.toStringHelper(this).add("logger", logger).toString();
+ }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/util/slab/SingleDoubleSlab.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/util/slab/SingleDoubleSlab.java
new file mode 100644
index 0000000..b01b860
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/util/slab/SingleDoubleSlab.java
@@ -0,0 +1,31 @@
+package com.scottkillen.mod.koresample.common.util.slab;
+
+import com.google.common.base.Objects;
+import com.scottkillen.mod.koresample.common.block.SlabBlock;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+
+public final class SingleDoubleSlab
+{
+ private final ImmutablePair pair;
+
+ public SingleDoubleSlab(SlabBlock singleSlab, SlabBlock doubleSlab)
+ {
+ pair = ImmutablePair.of(singleSlab, doubleSlab);
+ }
+
+ public SlabBlock singleSlab()
+ {
+ return pair.left;
+ }
+
+ public SlabBlock doubleSlab()
+ {
+ return pair.right;
+ }
+
+ @Override
+ public String toString()
+ {
+ return Objects.toStringHelper(this).add("pair", pair).toString();
+ }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/util/slab/TheSingleSlabRegistry.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/util/slab/TheSingleSlabRegistry.java
new file mode 100644
index 0000000..863a7d3
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/common/util/slab/TheSingleSlabRegistry.java
@@ -0,0 +1,30 @@
+package com.scottkillen.mod.koresample.common.util.slab;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Sets;
+import com.scottkillen.mod.koresample.common.block.SlabBlock;
+import net.minecraft.block.Block;
+import net.minecraft.item.Item;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.*;
+
+@SuppressWarnings({ "NonSerializableFieldInSerializableClass", "WeakerAccess" })
+public enum TheSingleSlabRegistry
+{
+ REFERENCE;
+
+ private final Set slabBlocks = Sets.newHashSet();
+
+ public void add(SlabBlock slabBlock) { slabBlocks.add(checkNotNull(slabBlock)); }
+
+ public boolean isSingleSlab(Block block) { return slabBlocks.contains(block); }
+
+ public boolean isSingleSlab(Item item) { return isSingleSlab(Block.getBlockFromItem(item)); }
+
+ @Override
+ public String toString()
+ {
+ return Objects.toStringHelper(this).add("slabBlocks", slabBlocks).toString();
+ }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/compat/Integrates.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/compat/Integrates.java
new file mode 100644
index 0000000..e56523b
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/compat/Integrates.java
@@ -0,0 +1,8 @@
+package com.scottkillen.mod.koresample.compat;
+
+import cpw.mods.fml.common.LoaderState.ModState;
+
+public interface Integrates
+{
+ void integrate(ModState modState);
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/compat/Integrator.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/compat/Integrator.java
new file mode 100644
index 0000000..f26ead6
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/compat/Integrator.java
@@ -0,0 +1,24 @@
+package com.scottkillen.mod.koresample.compat;
+
+import com.scottkillen.mod.koresample.common.util.log.Logger;
+import cpw.mods.fml.common.Loader;
+import cpw.mods.fml.common.LoaderState.ModState;
+
+public abstract class Integrator implements Integrates
+{
+
+ protected abstract void doIntegration(ModState modState);
+
+ @Override
+ public final void integrate(ModState modState)
+ {
+ if (Loader.isModLoaded(modID()))
+ {
+ doIntegration(modState);
+ } else Logger.forMod(modID()).info("%s not present. %s state integration skipped.", modName(), modState);
+ }
+
+ protected abstract String modID();
+
+ protected abstract String modName();
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/compat/versionchecker/Versioned.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/compat/versionchecker/Versioned.java
new file mode 100644
index 0000000..bbbe5e5
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/compat/versionchecker/Versioned.java
@@ -0,0 +1,8 @@
+package com.scottkillen.mod.koresample.compat.versionchecker;
+
+public interface Versioned
+{
+ String modID();
+
+ String versionInfoURL();
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/config/ConfigEventHandler.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/config/ConfigEventHandler.java
new file mode 100644
index 0000000..f03f728
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/config/ConfigEventHandler.java
@@ -0,0 +1,153 @@
+package com.scottkillen.mod.koresample.config;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Optional;
+import com.scottkillen.mod.koresample.common.util.log.Logger;
+import cpw.mods.fml.client.event.ConfigChangedEvent.OnConfigChangedEvent;
+import cpw.mods.fml.common.FMLCommonHandler;
+import cpw.mods.fml.common.Loader;
+import cpw.mods.fml.common.ModContainer;
+import cpw.mods.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.common.config.Configuration;
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Map;
+import java.util.Set;
+
+public final class ConfigEventHandler
+{
+ private final File configFile;
+ private final ConfigSyncable sync;
+ private final Configuration configuration;
+ private final String configVersion;
+ private final String modID;
+ private final Logger logger;
+
+ public ConfigEventHandler(String modID, File configFile, ConfigSyncable sync, String configVersion)
+ {
+ this.modID = modID;
+ logger = Logger.forMod(modID);
+ this.configFile = configFile;
+ this.sync = sync;
+
+ final Configuration localConfiguration = new Configuration(configFile, configVersion);
+ final Optional oldConfig;
+ if (isConfigVersionMismatch(localConfiguration))
+ {
+ backup(configFile);
+ oldConfig = Optional.of(localConfiguration);
+ configuration = new Configuration(configFile, configVersion);
+ } else
+ {
+ oldConfig = Optional.absent();
+ configuration = localConfiguration;
+ }
+
+ this.configVersion = configVersion;
+
+ syncConfig(false, oldConfig);
+ }
+
+ private static String getModName(String modID)
+ {
+ final Map mods = Loader.instance().getIndexedModList();
+ final ModContainer mod = mods.get(modID);
+ return mod == null ? "Unknown" : mod.getName();
+ }
+
+ private static boolean isConfigVersionMismatch(Configuration configuration)
+ {
+ return !configuration.getLoadedConfigVersion().equals(configuration.getDefinedConfigVersion());
+ }
+
+ public void activate()
+ {
+ FMLCommonHandler.instance().bus().register(this);
+ }
+
+ private void backup(File fileRef)
+ {
+ final File fileBak = new File(
+ fileRef.getAbsolutePath() + '_' + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + ".old");
+ logger.warning(
+ "Your %s config file is out of date and could cause issues. The existing file will be renamed to %s and a new one will be generated.",
+ getModName(modID), fileBak.getName());
+ logger.warning(
+ "%s will attempt to copy your old settings.",
+ getModName(modID));
+
+ final boolean success = fileRef.renameTo(fileBak);
+ logger.warning("Rename %s successful.", success ? "was" : "was not");
+ }
+
+ public Configuration configuration() { return configuration; }
+
+ private void loadConfig()
+ {
+ try
+ {
+ configuration.load();
+ } catch (final RuntimeException e)
+ {
+ final File fileBak = new File(
+ configFile.getAbsolutePath() + '_' + new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) +
+ ".errored");
+ logger.severe(
+ "An exception occurred while loading your config file. This file will be renamed to %s and a new config file will be generated.",
+ fileBak.getName());
+ logger.severe("Exception encountered: %s", e.getLocalizedMessage());
+
+ final boolean success = configFile.renameTo(fileBak);
+ logger.warning("Rename %s successful.", success ? "was" : "was not");
+
+ final Configuration newConfig = new Configuration(configFile, configVersion);
+ final Set categoryNames = configuration.getCategoryNames();
+ newConfig.copyCategoryProps(configuration, categoryNames.toArray(new String[categoryNames.size()]));
+ sync.syncConfig(newConfig);
+ newConfig.save();
+ }
+ }
+
+ @SubscribeEvent
+ public void onConfigChanged(OnConfigChangedEvent event)
+ {
+ if (event.modID.equalsIgnoreCase(modID))
+ {
+ saveConfig();
+ syncConfig();
+ }
+ }
+
+ private void saveConfig()
+ {
+ if (configuration.hasChanged())
+ {
+ configuration.save();
+ }
+ }
+
+ void syncConfig()
+ {
+ syncConfig(true, Optional.absent());
+ }
+
+ private void syncConfig(boolean doLoad, Optional oldConfig)
+ {
+ if (doLoad) loadConfig();
+
+ sync.syncConfig(configuration);
+
+ if (oldConfig.isPresent()) sync.convertOldConfig(oldConfig.get());
+
+ saveConfig();
+ }
+
+ @Override
+ public String toString()
+ {
+ return Objects.toStringHelper(this).add("configFile", configFile).add("sync", sync)
+ .add("configuration", configuration).add("configVersion", configVersion).add("modID", modID)
+ .add("logger", logger).toString();
+ }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/config/ConfigSyncable.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/config/ConfigSyncable.java
new file mode 100644
index 0000000..3485a26
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/config/ConfigSyncable.java
@@ -0,0 +1,10 @@
+package com.scottkillen.mod.koresample.config;
+
+import net.minecraftforge.common.config.Configuration;
+
+public interface ConfigSyncable
+{
+ void convertOldConfig(Configuration oldConfig);
+
+ void syncConfig(Configuration config);
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesLeaves.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesLeaves.java
new file mode 100644
index 0000000..47a236f
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesLeaves.java
@@ -0,0 +1,18 @@
+package com.scottkillen.mod.koresample.tree;
+
+import com.scottkillen.mod.koresample.tree.block.LeavesBlock;
+
+public interface DefinesLeaves extends ProvidesColor
+{
+ void assignLeavesBlock(LeavesBlock block);
+
+ void assignLeavesSubBlockIndex(int index);
+
+ LeavesBlock leavesBlock();
+
+ int leavesSubBlockIndex();
+
+ DefinesSapling saplingDefinition();
+
+ String speciesName();
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesLog.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesLog.java
new file mode 100644
index 0000000..661fb81
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesLog.java
@@ -0,0 +1,21 @@
+package com.scottkillen.mod.koresample.tree;
+
+import com.scottkillen.mod.koresample.tree.block.LogBlock;
+import net.minecraft.block.Block;
+
+public interface DefinesLog
+{
+ void assignLogBlock(LogBlock block);
+
+ void assignLogSubBlockIndex(int index);
+
+ Block logBlock();
+
+ int logSubBlockIndex();
+
+ String speciesName();
+
+ Block woodBlock();
+
+ int woodSubBlockIndex();
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesSapling.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesSapling.java
new file mode 100644
index 0000000..46e5201
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesSapling.java
@@ -0,0 +1,23 @@
+package com.scottkillen.mod.koresample.tree;
+
+import com.scottkillen.mod.koresample.tree.block.SaplingBlock;
+import net.minecraft.world.gen.feature.WorldGenerator;
+
+@SuppressWarnings("InterfaceNeverImplemented")
+public interface DefinesSapling
+{
+ void assignSaplingBlock(SaplingBlock block);
+
+ void assignSaplingSubBlockIndex(int index);
+
+ SaplingBlock saplingBlock();
+
+ int saplingSubBlockIndex();
+
+ String speciesName();
+
+ @Deprecated
+ WorldGenerator treeGenerator();
+
+ WorldGenerator saplingTreeGenerator();
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesSlab.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesSlab.java
new file mode 100644
index 0000000..185444d
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesSlab.java
@@ -0,0 +1,26 @@
+package com.scottkillen.mod.koresample.tree;
+
+import com.scottkillen.mod.koresample.common.block.SlabBlock;
+import net.minecraft.block.Block;
+
+public interface DefinesSlab
+{
+
+ void assignDoubleSlabBlock(SlabBlock doubleSlabBlock);
+
+ void assignSingleSlabBlock(SlabBlock block);
+
+ void assignSlabSubBlockIndex(int index);
+
+ SlabBlock doubleSlabBlock();
+
+ SlabBlock singleSlabBlock();
+
+ int slabSubBlockIndex();
+
+ Block slabModelBlock();
+
+ int slabModelSubBlockIndex();
+
+ String slabName();
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesStairs.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesStairs.java
new file mode 100644
index 0000000..ed4af15
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesStairs.java
@@ -0,0 +1,17 @@
+package com.scottkillen.mod.koresample.tree;
+
+import com.scottkillen.mod.koresample.common.block.StairsBlock;
+import net.minecraft.block.Block;
+
+public interface DefinesStairs
+{
+ void assignStairsBlock(StairsBlock block);
+
+ StairsBlock stairsBlock();
+
+ Block stairsModelBlock();
+
+ int stairsModelSubBlockIndex();
+
+ String stairsName();
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesTree.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesTree.java
new file mode 100644
index 0000000..66f77f7
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesTree.java
@@ -0,0 +1,26 @@
+package com.scottkillen.mod.koresample.tree;
+
+import com.scottkillen.mod.koresample.tree.block.LeavesBlock;
+import com.scottkillen.mod.koresample.tree.block.LogBlock;
+import com.scottkillen.mod.koresample.tree.block.SaplingBlock;
+import net.minecraft.world.gen.feature.WorldGenerator;
+
+@SuppressWarnings("InterfaceNeverImplemented")
+public interface DefinesTree
+{
+ LeavesBlock leavesBlock();
+
+ int leavesSubBlockIndex();
+
+ LogBlock logBlock();
+
+ int logSubBlockIndex();
+
+ SaplingBlock saplingBlock();
+
+ int saplingSubBlockIndex();
+
+ WorldGenerator saplingTreeGenerator();
+
+ WorldGenerator worldTreeGenerator();
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesWood.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesWood.java
new file mode 100644
index 0000000..f99740f
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/DefinesWood.java
@@ -0,0 +1,16 @@
+package com.scottkillen.mod.koresample.tree;
+
+import com.scottkillen.mod.koresample.tree.block.WoodBlock;
+
+public interface DefinesWood
+{
+ void assignWoodBlock(WoodBlock block);
+
+ void assignWoodSubBlockIndex(int index);
+
+ WoodBlock woodBlock();
+
+ int woodSubBlockIndex();
+
+ String speciesName();
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/ProvidesColor.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/ProvidesColor.java
new file mode 100644
index 0000000..bb849b1
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/ProvidesColor.java
@@ -0,0 +1,14 @@
+package com.scottkillen.mod.koresample.tree;
+
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import net.minecraft.world.IBlockAccess;
+
+public interface ProvidesColor
+{
+ @SideOnly(Side.CLIENT)
+ int getLeavesInventoryColor();
+
+ @SideOnly(Side.CLIENT)
+ int getLeavesColor(IBlockAccess blockAccess, int x, int y, int z);
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/TreeBlockFactory.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/TreeBlockFactory.java
new file mode 100644
index 0000000..288bd1f
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/TreeBlockFactory.java
@@ -0,0 +1,23 @@
+package com.scottkillen.mod.koresample.tree;
+
+import com.scottkillen.mod.koresample.common.util.slab.SingleDoubleSlab;
+import com.scottkillen.mod.koresample.tree.block.LeavesBlock;
+import com.scottkillen.mod.koresample.tree.block.LogBlock;
+import com.scottkillen.mod.koresample.tree.block.SaplingBlock;
+import com.scottkillen.mod.koresample.common.block.StairsBlock;
+import com.scottkillen.mod.koresample.tree.block.WoodBlock;
+
+public interface TreeBlockFactory
+{
+ LeavesBlock createLeavesBlock(Iterable subBlocks);
+
+ LogBlock createLogBlock(Iterable subBlocks);
+
+ SaplingBlock createSaplingBlock(Iterable subBlocks);
+
+ SingleDoubleSlab createSlabBlocks(Iterable subBlocks);
+
+ StairsBlock createStairsBlock(DefinesStairs definition);
+
+ WoodBlock createWoodBlock(Iterable subBlocks);
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/TreeTaxonomy.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/TreeTaxonomy.java
new file mode 100644
index 0000000..54e1875
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/TreeTaxonomy.java
@@ -0,0 +1,18 @@
+package com.scottkillen.mod.koresample.tree;
+
+import com.scottkillen.mod.koresample.tree.DefinesLeaves;
+
+public interface TreeTaxonomy
+{
+ Iterable extends DefinesLeaves> leavesDefinitions();
+
+ Iterable extends DefinesLog> logDefinitions();
+
+ Iterable extends DefinesSapling> saplingDefinitions();
+
+ Iterable extends DefinesSlab> slabDefinitions();
+
+ Iterable extends DefinesStairs> stairsDefinitions();
+
+ Iterable extends DefinesWood> woodDefinitions();
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/LeavesBlock.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/LeavesBlock.java
new file mode 100644
index 0000000..7946f3e
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/LeavesBlock.java
@@ -0,0 +1,151 @@
+package com.scottkillen.mod.koresample.tree.block;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.scottkillen.mod.koresample.tree.DefinesLeaves;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockLeaves;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.texture.IIconRegister;
+import net.minecraft.creativetab.CreativeTabs;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.IIcon;
+import net.minecraft.world.IBlockAccess;
+import net.minecraft.world.World;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+import static com.google.common.base.Preconditions.*;
+
+public abstract class LeavesBlock extends BlockLeaves
+{
+ public static final int CAPACITY = 4;
+ private static final int METADATA_MASK = CAPACITY - 1;
+ private final ImmutableList subBlocks;
+
+ protected LeavesBlock(Collection extends DefinesLeaves> subBlocks)
+ {
+ checkArgument(!subBlocks.isEmpty());
+ checkArgument(subBlocks.size() <= CAPACITY);
+ this.subBlocks = ImmutableList.copyOf(subBlocks);
+ setBlockName("leaves");
+ }
+
+ private static int mask(int metadata) {return metadata & METADATA_MASK;}
+
+ @SuppressWarnings("WeakerAccess")
+ protected static String getUnwrappedUnlocalizedName(String unlocalizedName)
+ {
+ return unlocalizedName.substring(unlocalizedName.indexOf('.') + 1);
+ }
+
+ @SideOnly(Side.CLIENT)
+ private static boolean isFancyGraphics() {return Minecraft.getMinecraft().gameSettings.fancyGraphics;}
+
+ protected final List subBlocks() { return Collections.unmodifiableList(subBlocks); }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public final int getRenderColor(int metadata) { return subBlocks.get(mask(metadata)).getLeavesInventoryColor(); }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public final int colorMultiplier(IBlockAccess blockAccess, int x, int y, int z)
+ {
+ final int metadata = mask(blockAccess.getBlockMetadata(x, y, z));
+ return subBlocks.get(metadata).getLeavesColor(blockAccess, x, y, z);
+ }
+
+ @Override
+ public final Item getItemDropped(int metadata, Random unused, int unused2)
+ {
+ return Item.getItemFromBlock(subBlocks.get(mask(metadata)).saplingDefinition().saplingBlock());
+ }
+
+ @Override
+ public final int damageDropped(int metadata)
+ {
+ return subBlocks.get(mask(metadata)).saplingDefinition().saplingSubBlockIndex();
+ }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public final boolean isOpaqueCube() { return !isFancyGraphics(); }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public final IIcon getIcon(int unused, int metadata)
+ {
+ return field_150129_M[isFancyGraphics() ? 0 : 1][mask(metadata)];
+ }
+
+ @Override
+ public final String[] func_150125_e()
+ {
+ final List names = Lists.newArrayList();
+ for (final DefinesLeaves subBlock : subBlocks)
+ names.add(subBlock.speciesName());
+ return names.toArray(new String[names.size()]);
+ }
+
+ @Override
+ public final String getUnlocalizedName()
+ {
+ return String.format("tile.%s%s", resourcePrefix(), getUnwrappedUnlocalizedName(super.getUnlocalizedName()));
+ }
+
+ @Override
+ public final int getDamageValue(World world, int x, int y, int z) { return world.getBlockMetadata(x, y, z) & 3; }
+
+ @SuppressWarnings("unchecked")
+ @SideOnly(Side.CLIENT)
+ @Override
+ public final void getSubBlocks(Item item, CreativeTabs unused, List subBlocks)
+ {
+ for (int i = 0; i < this.subBlocks.size(); i++)
+ //noinspection ObjectAllocationInLoop
+ subBlocks.add(new ItemStack(item, 1, i));
+ }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public final void registerBlockIcons(IIconRegister iconRegister)
+ {
+ field_150129_M[0] = new IIcon[subBlocks.size()];
+ field_150129_M[1] = new IIcon[subBlocks.size()];
+
+ for (int i = 0; i < subBlocks.size(); i++)
+ {
+ final String iconName =
+ String.format("%sleaves_%s", resourcePrefix(), subBlocks.get(i).speciesName().replace('.', '_'));
+ field_150129_M[0][i] = iconRegister.registerIcon(iconName);
+ field_150129_M[1][i] = iconRegister.registerIcon(iconName + "_opaque");
+ }
+ }
+
+ protected abstract String resourcePrefix();
+
+ @SuppressWarnings("OverlyComplexBooleanExpression")
+ @SideOnly(Side.CLIENT)
+ @Override
+ public final boolean shouldSideBeRendered(IBlockAccess blockAccess, int x, int y, int z, int side)
+ {
+ final Block block = blockAccess.getBlock(x, y, z);
+ return !(!isFancyGraphics() && block.equals(this)) &&
+ (side == 0 && minY > 0.0D || side == 1 && maxY < 1.0D || side == 2 && minZ > 0.0D ||
+ side == 3 && maxZ < 1.0D || side == 4 && minX > 0.0D || side == 5 && maxX < 1.0D ||
+ !blockAccess.getBlock(x, y, z).isOpaqueCube());
+ }
+
+ @Override
+ public String toString()
+ {
+ return Objects.toStringHelper(this).add("subBlocks", subBlocks).toString();
+ }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/LogBlock.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/LogBlock.java
new file mode 100644
index 0000000..8fcfb6c
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/LogBlock.java
@@ -0,0 +1,88 @@
+package com.scottkillen.mod.koresample.tree.block;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.scottkillen.mod.koresample.tree.DefinesLog;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import net.minecraft.block.BlockLog;
+import net.minecraft.client.renderer.texture.IIconRegister;
+import net.minecraft.creativetab.CreativeTabs;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.IIcon;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import static com.google.common.base.Preconditions.*;
+
+public abstract class LogBlock extends BlockLog
+{
+ public static final int CAPACITY = 4;
+ private final ImmutableList subBlocks;
+
+ protected LogBlock(Collection extends DefinesLog> subBlocks)
+ {
+ checkArgument(!subBlocks.isEmpty());
+ checkArgument(subBlocks.size() <= CAPACITY);
+ this.subBlocks = ImmutableList.copyOf(subBlocks);
+ setBlockName("log");
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ protected static String getUnwrappedUnlocalizedName(String unlocalizedName)
+ {
+ return unlocalizedName.substring(unlocalizedName.indexOf('.') + 1);
+ }
+
+ protected final List subBlocks() { return Collections.unmodifiableList(subBlocks); }
+
+ public final ImmutableList getSubBlockNames()
+ {
+ final List names = Lists.newArrayList();
+ for (final DefinesLog subBlock : subBlocks)
+ names.add(subBlock.speciesName());
+ return ImmutableList.copyOf(names);
+ }
+
+ @Override
+ public final String getUnlocalizedName()
+ {
+ return String.format("tile.%s%s", resourcePrefix(), getUnwrappedUnlocalizedName(super.getUnlocalizedName()));
+ }
+
+ @SuppressWarnings("unchecked")
+ @SideOnly(Side.CLIENT)
+ @Override
+ public final void getSubBlocks(Item item, CreativeTabs unused, List subblocks)
+ {
+ for (int i = 0; i < subBlocks.size(); i++)
+ //noinspection ObjectAllocationInLoop
+ subblocks.add(new ItemStack(item, 1, i));
+ }
+
+ @Override
+ @SideOnly(Side.CLIENT)
+ public final void registerBlockIcons(IIconRegister iconRegister)
+ {
+ field_150167_a = new IIcon[subBlocks.size()];
+ field_150166_b = new IIcon[subBlocks.size()];
+
+ for (int i = 0; i < subBlocks.size(); i++)
+ {
+ final String iconName = String.format("%slog_%s", resourcePrefix(), subBlocks.get(i).speciesName());
+ field_150167_a[i] = iconRegister.registerIcon(iconName);
+ field_150166_b[i] = iconRegister.registerIcon(iconName + "_top");
+ }
+ }
+
+ protected abstract String resourcePrefix();
+
+ @Override
+ public String toString()
+ {
+ return Objects.toStringHelper(this).add("subBlocks", subBlocks).toString();
+ }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/SaplingBlock.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/SaplingBlock.java
new file mode 100644
index 0000000..4a587e7
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/SaplingBlock.java
@@ -0,0 +1,119 @@
+package com.scottkillen.mod.koresample.tree.block;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.scottkillen.mod.koresample.tree.DefinesSapling;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import net.minecraft.block.BlockSapling;
+import net.minecraft.client.renderer.texture.IIconRegister;
+import net.minecraft.creativetab.CreativeTabs;
+import net.minecraft.init.Blocks;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.IIcon;
+import net.minecraft.world.World;
+import net.minecraft.world.gen.feature.WorldGenerator;
+import net.minecraftforge.event.terraingen.TerrainGen;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+
+import static com.google.common.base.Preconditions.*;
+
+@SuppressWarnings("AbstractClassNeverImplemented")
+public abstract class SaplingBlock extends BlockSapling
+{
+ public static final int CAPACITY = 8;
+ private static final int METADATA_MASK = CAPACITY - 1;
+ private final ImmutableList subBlocks;
+ private final List subblockIcons;
+
+ protected SaplingBlock(Collection extends DefinesSapling> subBlocks)
+ {
+ checkArgument(!subBlocks.isEmpty());
+ checkArgument(subBlocks.size() <= CAPACITY);
+ this.subBlocks = ImmutableList.copyOf(subBlocks);
+
+ subblockIcons = Lists.newArrayListWithCapacity(subBlocks.size());
+
+ setBlockName("sapling");
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ protected static String getUnwrappedUnlocalizedName(String unlocalizedName)
+ {
+ return unlocalizedName.substring(unlocalizedName.indexOf('.') + 1);
+ }
+
+ private static int mask(int metadata) { return metadata & METADATA_MASK; }
+
+ protected final List subBlocks() { return Collections.unmodifiableList(subBlocks); }
+
+ public final List subBlockNames()
+ {
+ final List names = Lists.newArrayListWithCapacity(subBlocks.size());
+ for (final DefinesSapling subBlock : subBlocks)
+ names.add(subBlock.speciesName());
+ return ImmutableList.copyOf(names);
+ }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public final IIcon getIcon(int unused, int metadata) { return subblockIcons.get(mask(metadata)); }
+
+ @Override
+ public final void func_149878_d(World world, int x, int y, int z, Random rand)
+ {
+ if (!TerrainGen.saplingGrowTree(world, rand, x, y, z)) return;
+
+ final int metadata = mask(world.getBlockMetadata(x, y, z));
+ final WorldGenerator treeGen = subBlocks.get(metadata).saplingTreeGenerator();
+ world.setBlock(x, y, z, Blocks.air, 0, 4);
+ if (!treeGen.generate(world, rand, x, y, z)) world.setBlock(x, y, z, this, metadata, 4);
+ }
+
+ @Override
+ public final int damageDropped(int metadata)
+ {
+ return mask(metadata);
+ }
+
+ @SuppressWarnings("unchecked")
+ @SideOnly(Side.CLIENT)
+ @Override
+ public final void getSubBlocks(Item item, CreativeTabs unused, List subBlocks)
+ {
+ for (int i = 0; i < this.subBlocks.size(); i++)
+ //noinspection ObjectAllocationInLoop
+ subBlocks.add(new ItemStack(item, 1, i));
+ }
+
+ @Override
+ public final void registerBlockIcons(IIconRegister iconRegister)
+ {
+ subblockIcons.clear();
+
+ for (int i = 0; i < subBlocks.size(); i++)
+ {
+ final String iconName = String.format("%ssapling_%s", resourcePrefix(), subBlocks.get(i).speciesName());
+ subblockIcons.add(i, iconRegister.registerIcon(iconName));
+ }
+ }
+
+ @Override
+ public final String getUnlocalizedName()
+ {
+ return String.format("tile.%s%s", resourcePrefix(), getUnwrappedUnlocalizedName(super.getUnlocalizedName()));
+ }
+
+ protected abstract String resourcePrefix();
+
+ @Override
+ public String toString()
+ {
+ return Objects.toStringHelper(this).add("subBlocks", subBlocks).add("subblockIcons", subblockIcons).toString();
+ }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/SlabBlock.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/SlabBlock.java
new file mode 100644
index 0000000..4fad2ff
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/SlabBlock.java
@@ -0,0 +1,15 @@
+package com.scottkillen.mod.koresample.tree.block;
+
+import com.scottkillen.mod.koresample.tree.DefinesSlab;
+import java.util.Collection;
+
+// Use com.scottkillen.mod.koresample.common.block.SlabBlock instead
+// This is left to honor the api contract
+@Deprecated
+public abstract class SlabBlock extends com.scottkillen.mod.koresample.common.block.SlabBlock
+{
+ protected SlabBlock(boolean isDouble, Collection extends DefinesSlab> subBlocks)
+ {
+ super(isDouble, subBlocks);
+ }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/StairsBlock.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/StairsBlock.java
new file mode 100644
index 0000000..b49192f
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/StairsBlock.java
@@ -0,0 +1,14 @@
+package com.scottkillen.mod.koresample.tree.block;
+
+import com.scottkillen.mod.koresample.tree.DefinesStairs;
+
+// Use com.scottkillen.mod.koresample.common.block.StairsBlock instead
+// This is left to honor the api contract
+@Deprecated
+public abstract class StairsBlock extends com.scottkillen.mod.koresample.common.block.StairsBlock
+{
+ protected StairsBlock(DefinesStairs model)
+ {
+ super(model);
+ }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/WoodBlock.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/WoodBlock.java
new file mode 100644
index 0000000..09a5bab
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/block/WoodBlock.java
@@ -0,0 +1,93 @@
+package com.scottkillen.mod.koresample.tree.block;
+
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.scottkillen.mod.koresample.tree.DefinesWood;
+import cpw.mods.fml.relauncher.Side;
+import cpw.mods.fml.relauncher.SideOnly;
+import net.minecraft.block.BlockWood;
+import net.minecraft.client.renderer.texture.IIconRegister;
+import net.minecraft.creativetab.CreativeTabs;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.IIcon;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+public abstract class WoodBlock extends BlockWood
+{
+ public static final int CAPACITY = 16;
+ private final ImmutableList subBlocks;
+ private final List icons = Lists.newArrayList();
+
+ protected WoodBlock(Collection extends DefinesWood> subBlocks)
+ {
+ Preconditions.checkArgument(!subBlocks.isEmpty());
+ Preconditions.checkArgument(subBlocks.size() <= CAPACITY);
+ this.subBlocks = ImmutableList.copyOf(subBlocks);
+ setBlockName("wood");
+ }
+
+ @SuppressWarnings("WeakerAccess")
+ protected static String getUnwrappedUnlocalizedName(String unlocalizedName)
+ {
+ return unlocalizedName.substring(unlocalizedName.indexOf('.') + 1);
+ }
+
+ protected final List subBlocks() { return Collections.unmodifiableList(subBlocks); }
+
+ public final ImmutableList getSubBlockNames()
+ {
+ final List names = Lists.newArrayList();
+ for (final DefinesWood subBlock : subBlocks)
+ names.add(subBlock.speciesName());
+ return ImmutableList.copyOf(names);
+ }
+
+ @Override
+ public final String getUnlocalizedName()
+ {
+ return String.format("tile.%s%s", resourcePrefix(), getUnwrappedUnlocalizedName(super.getUnlocalizedName()));
+ }
+
+ @SideOnly(Side.CLIENT)
+ @Override
+ public final IIcon getIcon(int unused, int meta)
+ {
+ final int meta1 = meta < 0 || meta >= icons.size() ? 0 : meta;
+ return icons.get(meta1);
+ }
+
+ @SuppressWarnings("unchecked")
+ @SideOnly(Side.CLIENT)
+ @Override
+ public final void getSubBlocks(Item item, CreativeTabs unused, List subblocks)
+ {
+ for (int i = 0; i < subBlocks.size(); i++)
+ //noinspection ObjectAllocationInLoop
+ subblocks.add(new ItemStack(item, 1, i));
+ }
+
+ @Override
+ public final void registerBlockIcons(IIconRegister iconRegister)
+ {
+ icons.clear();
+
+ for (int i = 0; i < subBlocks.size(); i++)
+ {
+ final String iconName = String.format("%splanks_%s", resourcePrefix(), subBlocks.get(i).speciesName());
+ icons.add(i, iconRegister.registerIcon(iconName));
+ }
+ }
+
+ protected abstract String resourcePrefix();
+
+ @Override
+ public String toString()
+ {
+ return Objects.toStringHelper(this).add("subBlocks", subBlocks).add("icons", icons).toString();
+ }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/item/LeavesItem.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/item/LeavesItem.java
new file mode 100644
index 0000000..3ad3620
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/item/LeavesItem.java
@@ -0,0 +1,10 @@
+package com.scottkillen.mod.koresample.tree.item;
+
+import net.minecraft.block.Block;
+import net.minecraft.block.BlockLeaves;
+import net.minecraft.item.ItemLeaves;
+
+public final class LeavesItem extends ItemLeaves
+{
+ public LeavesItem(Block block) { super((BlockLeaves) block); }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/item/LogItem.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/item/LogItem.java
new file mode 100644
index 0000000..fbd6d53
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/item/LogItem.java
@@ -0,0 +1,13 @@
+package com.scottkillen.mod.koresample.tree.item;
+
+import com.scottkillen.mod.koresample.tree.block.LogBlock;
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemMultiTexture;
+
+public abstract class LogItem extends ItemMultiTexture
+{
+ // This provides a reminder that you must extend this class and change the constructor to accept your extension of
+ // LogBlock in the second parameter
+
+ protected LogItem(Block block, LogBlock log, String[] names) { super(block, log, names); }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/item/SaplingItem.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/item/SaplingItem.java
new file mode 100644
index 0000000..8d02419
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/item/SaplingItem.java
@@ -0,0 +1,13 @@
+package com.scottkillen.mod.koresample.tree.item;
+
+import com.scottkillen.mod.koresample.tree.block.SaplingBlock;
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemMultiTexture;
+
+public abstract class SaplingItem extends ItemMultiTexture
+{
+ // This provides a reminder that you must extend this class and change the constructor to accept your extension of
+ // SaplingBlock in the second parameter
+
+ protected SaplingItem(Block block, SaplingBlock sapling, String[] names) { super(block, sapling, names); }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/item/SlabItem.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/item/SlabItem.java
new file mode 100644
index 0000000..992ddc0
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/item/SlabItem.java
@@ -0,0 +1,15 @@
+package com.scottkillen.mod.koresample.tree.item;
+
+import com.scottkillen.mod.koresample.common.block.SlabBlock;
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemSlab;
+
+public abstract class SlabItem extends ItemSlab
+{
+ // This provides a reminder that you must extend this class and change the constructor to accept your extension of
+ // SlabBlock in the second and third parameters
+ protected SlabItem(Block block, SlabBlock singleSlab, SlabBlock doubleSlab, Boolean isDouble)
+ {
+ super(block, singleSlab, doubleSlab, isDouble);
+ }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/item/WoodItem.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/item/WoodItem.java
new file mode 100644
index 0000000..4f94b3a
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/item/WoodItem.java
@@ -0,0 +1,13 @@
+package com.scottkillen.mod.koresample.tree.item;
+
+import com.scottkillen.mod.koresample.tree.block.WoodBlock;
+import net.minecraft.block.Block;
+import net.minecraft.item.ItemMultiTexture;
+
+public abstract class WoodItem extends ItemMultiTexture
+{
+ // This provides a reminder that you must extend this class and change the constructor to accept your extension of
+ // WoodBlock in the second parameter
+
+ protected WoodItem(Block block, WoodBlock log, String[] names) { super(block, log, names); }
+}
diff --git a/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/loader/TreeSpeciesLoader.java b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/loader/TreeSpeciesLoader.java
new file mode 100644
index 0000000..84d4093
--- /dev/null
+++ b/api/KoreSample/src/main/java/com/scottkillen/mod/koresample/tree/loader/TreeSpeciesLoader.java
@@ -0,0 +1,146 @@
+package com.scottkillen.mod.koresample.tree.loader;
+
+import com.google.common.base.Objects;
+import com.google.common.collect.Lists;
+import com.scottkillen.mod.koresample.common.util.slab.SingleDoubleSlab;
+import com.scottkillen.mod.koresample.common.util.slab.TheSingleSlabRegistry;
+import com.scottkillen.mod.koresample.tree.DefinesLeaves;
+import com.scottkillen.mod.koresample.tree.DefinesLog;
+import com.scottkillen.mod.koresample.tree.DefinesSapling;
+import com.scottkillen.mod.koresample.tree.DefinesSlab;
+import com.scottkillen.mod.koresample.tree.DefinesStairs;
+import com.scottkillen.mod.koresample.tree.DefinesWood;
+import com.scottkillen.mod.koresample.tree.TreeBlockFactory;
+import com.scottkillen.mod.koresample.tree.TreeTaxonomy;
+import com.scottkillen.mod.koresample.tree.block.LeavesBlock;
+import com.scottkillen.mod.koresample.tree.block.LogBlock;
+import com.scottkillen.mod.koresample.tree.block.SaplingBlock;
+import com.scottkillen.mod.koresample.common.block.SlabBlock;
+import com.scottkillen.mod.koresample.tree.block.WoodBlock;
+import java.util.List;
+
+public class TreeSpeciesLoader
+{
+ private final TreeTaxonomy taxonomy;
+ private final TheSingleSlabRegistry slabRegistry = TheSingleSlabRegistry.REFERENCE;
+
+ public TreeSpeciesLoader(TreeTaxonomy taxonomy)
+ {
+ this.taxonomy = taxonomy;
+ }
+
+ public void load(TreeBlockFactory factory)
+ {
+ loadLogBlocks(factory);
+ loadLeavesBlocks(factory);
+ loadWoodBlocks(factory);
+ loadSaplingBlocks(factory);
+ loadSlabBlocks(factory);
+ loadStairsBlocks(factory);
+ }
+
+ private void loadLeavesBlocks(TreeBlockFactory factory)
+ {
+ final List subBlocks = Lists.newArrayListWithCapacity(LeavesBlock.CAPACITY);
+ for (final DefinesLeaves definition : taxonomy.leavesDefinitions())
+ {
+ definition.assignLeavesSubBlockIndex(subBlocks.size());
+
+ subBlocks.add(definition);
+ if (subBlocks.size() == LeavesBlock.CAPACITY)
+ {
+ factory.createLeavesBlock(subBlocks);
+ subBlocks.clear();
+ }
+ }
+ if (!subBlocks.isEmpty()) factory.createLeavesBlock(subBlocks);
+ }
+
+ private void loadLogBlocks(TreeBlockFactory factory)
+ {
+ final List subBlocks = Lists.newArrayListWithCapacity(LogBlock.CAPACITY);
+ for (final DefinesLog definition : taxonomy.logDefinitions())
+ {
+ definition.assignLogSubBlockIndex(subBlocks.size());
+
+ subBlocks.add(definition);
+ if (subBlocks.size() == LogBlock.CAPACITY)
+ {
+ factory.createLogBlock(subBlocks);
+ subBlocks.clear();
+ }
+ }
+ if (!subBlocks.isEmpty()) factory.createLogBlock(subBlocks);
+ }
+
+ private void loadSaplingBlocks(TreeBlockFactory factory)
+ {
+ final List subBlocks = Lists.newArrayListWithCapacity(SaplingBlock.CAPACITY);
+ for (final DefinesSapling definition : taxonomy.saplingDefinitions())
+ {
+ definition.assignSaplingSubBlockIndex(subBlocks.size());
+
+ subBlocks.add(definition);
+ if (subBlocks.size() == SaplingBlock.CAPACITY)
+ {
+ factory.createSaplingBlock(subBlocks);
+ subBlocks.clear();
+ }
+ }
+ if (!subBlocks.isEmpty()) factory.createSaplingBlock(subBlocks);
+ }
+
+ private void loadSlabBlocks(TreeBlockFactory factory)
+ {
+ final List subBlocks = Lists.newArrayList();
+ for (final DefinesSlab definition : taxonomy.slabDefinitions())
+ {
+ definition.assignSlabSubBlockIndex(subBlocks.size());
+
+ subBlocks.add(definition);
+ if (subBlocks.size() == SlabBlock.CAPACITY)
+ {
+ final SingleDoubleSlab slabs = factory.createSlabBlocks(subBlocks);
+ slabRegistry.add(slabs.singleSlab());
+
+ subBlocks.clear();
+ }
+ }
+ if (!subBlocks.isEmpty())
+ {
+ final SingleDoubleSlab slabs = factory.createSlabBlocks(subBlocks);
+ slabRegistry.add(slabs.singleSlab());
+ }
+ }
+
+ private void loadStairsBlocks(TreeBlockFactory factory)
+ {
+ for (final DefinesStairs definition : taxonomy.stairsDefinitions())
+ {
+ factory.createStairsBlock(definition);
+ }
+ }
+
+ private void loadWoodBlocks(TreeBlockFactory factory)
+ {
+ final List subBlocks = Lists.newArrayListWithCapacity(WoodBlock.CAPACITY);
+ for (final DefinesWood definition : taxonomy.woodDefinitions())
+ {
+ definition.assignWoodSubBlockIndex(subBlocks.size());
+
+ subBlocks.add(definition);
+ if (subBlocks.size() == WoodBlock.CAPACITY)
+ {
+ factory.createWoodBlock(subBlocks);
+ subBlocks.clear();
+ }
+ }
+ if (!subBlocks.isEmpty()) factory.createWoodBlock(subBlocks);
+ }
+
+ @Override
+ public String toString()
+ {
+ return Objects.toStringHelper(this).add("taxonomy", taxonomy).add("slabRegistry", slabRegistry).toString();
+ }
+}
diff --git a/api/KoreSample/src/main/resources/CHANGELOG.md b/api/KoreSample/src/main/resources/CHANGELOG.md
new file mode 100644
index 0000000..690ae51
--- /dev/null
+++ b/api/KoreSample/src/main/resources/CHANGELOG.md
@@ -0,0 +1,31 @@
+# Kore Sample Changelog
+
+## 1.2.5
+- Fixed lighting bug for slabs and stairs.
+- Fixed slabs showing wrong textures.
+
+## 1.2.4
+- Corrected incorrect log message.
+
+## 1.2.3
+- Fixed failure to detect config version mismatch.
+- Update build environment to Minecraft Forge 10.13.2.1291
+
+## 1.2.2
+- Refactored Version Checker integration to be reused by client mods.
+
+## 1.2.1
+- Add support for [Version Checker][vc_url].
+[vc_url]: http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-mods/2091981-version-checker-auto-update-mods-and-clean
+
+## 1.2.0
+- Added boilerplate for forge event listeners
+
+## 1.1.0
+- Added logo made by [CyanideX][cyanidex_twitter]
+- Expand tree definition to include worldGen spawns
+- Enhance epic awesomeness
+[cyanidex_twitter]: https://twitter.com/electrodynamix
+
+## 1.0.0
+- Initial release
diff --git a/api/KoreSample/src/main/resources/UNLICENSE b/api/KoreSample/src/main/resources/UNLICENSE
new file mode 100644
index 0000000..a84c395
--- /dev/null
+++ b/api/KoreSample/src/main/resources/UNLICENSE
@@ -0,0 +1,25 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
+
diff --git a/api/KoreSample/src/main/resources/assets/logo.png b/api/KoreSample/src/main/resources/assets/logo.png
new file mode 100644
index 0000000..e90b1cf
Binary files /dev/null and b/api/KoreSample/src/main/resources/assets/logo.png differ
diff --git a/api/KoreSample/src/main/resources/mcmod.info b/api/KoreSample/src/main/resources/mcmod.info
new file mode 100644
index 0000000..f34d532
--- /dev/null
+++ b/api/KoreSample/src/main/resources/mcmod.info
@@ -0,0 +1,21 @@
+[
+{
+ "modid": "koresample",
+ "name": "Kore Sample",
+ "description": "Kore Sample provides a set of tools and base classes for other Minecraft mods that depend on it.",
+ "version": "${mod_version}",
+ "mcversion": "${minecraft_version}",
+ "url": "",
+ "updateUrl": "",
+ "authorList": [
+ "ScottKillen"
+ ],
+ "credits": "",
+ "logoFile": "assets/logo.png",
+ "screenshots": [
+ ],
+ "parent":"",
+ "dependencies": [
+ ]
+}
+]
diff --git a/api/KoreSample/src/main/resources/version.properties b/api/KoreSample/src/main/resources/version.properties
new file mode 100644
index 0000000..c204bf4
--- /dev/null
+++ b/api/KoreSample/src/main/resources/version.properties
@@ -0,0 +1,3 @@
+version.mod=${mod_version}
+version.forge=${forge_version}
+version.minecraft=${minecraft_version}
diff --git a/api/KoreSample/version/.gitignore b/api/KoreSample/version/.gitignore
new file mode 100644
index 0000000..89654b7
--- /dev/null
+++ b/api/KoreSample/version/.gitignore
@@ -0,0 +1 @@
+version.iml
diff --git a/api/KoreSample/version/build.gradle b/api/KoreSample/version/build.gradle
new file mode 100644
index 0000000..9977553
--- /dev/null
+++ b/api/KoreSample/version/build.gradle
@@ -0,0 +1,42 @@
+rootProject.version = "$mcversion-$version_major.$version_series.$version_revision"
+rootProject.ext.mcversion = mcversion
+rootProject.ext.forgeversion = forgeversion
+
+task echoVersion << {
+ printNextVersion()
+}
+
+def saveVersion() {
+ ant.propertyfile(
+ file: "gradle.properties") {
+ entry( key:"version_major", type:"int", value: version_major)
+ entry( key:"version_series", type:"int", value: version_series)
+ entry( key:"version_revision", type:"int", value: version_revision)
+ }
+ rootProject.version = "$mcversion-$version_major.$version_series.$version_revision"
+ printNextVersion()
+}
+
+def printNextVersion(){
+ println '*************************************************************'
+ println 'The next build will be version: ' + rootProject.version
+ println '*************************************************************'
+}
+
+task bumpMajor << {
+ project.version_major = Integer.parseInt(project.version_major) + 1
+ project.version_series = "0"
+ project.version_revision = "0"
+ saveVersion()
+}
+
+task bumpSeries << {
+ project.version_series = Integer.parseInt(project.version_series) + 1
+ project.version_revision = "0"
+ saveVersion()
+}
+
+task bumpRevision << {
+ project.version_revision = Integer.parseInt(project.version_revision) + 1
+ saveVersion()
+}
diff --git a/api/KoreSample/version/gradle.properties b/api/KoreSample/version/gradle.properties
new file mode 100644
index 0000000..b8fce11
--- /dev/null
+++ b/api/KoreSample/version/gradle.properties
@@ -0,0 +1,6 @@
+#Mon, 16 Feb 2015 00:21:40 -0500
+mcversion=1.7.10
+forgeversion=10.13.2.1291
+version_major=1
+version_series=2
+version_revision=5
diff --git a/build.gradle b/build.gradle
index 6700e31..0bd8761 100644
--- a/build.gradle
+++ b/build.gradle
@@ -23,6 +23,8 @@ apply plugin: 'maven'
group = "com.scottkillen.mod"
archivesBaseName = "ScottsTweaks"
+ext.apiKore = "./api/KoreSample/src/main/java"
+
minecraft {
version = project.mcversion + "-" + project.forgeversion
runDir = "run"
@@ -30,17 +32,14 @@ minecraft {
replace '${mod_version}', project.version
}
-repositories {
- mavenLocal()
- maven {
- url = 'http://maven.scottkillen.com/'
+sourceSets {
+ api {
+ java {
+ srcDir project.apiKore
+ }
}
}
-dependencies {
- compile "com.scottkillen.mod:KoreSample:${project.ext.koreversion}:deobf"
-}
-
processResources
{
// this will ensure that this task is redone when the versions change.
diff --git a/src/main/java/com/scottkillen/mod/scottstweaks/TheMod.java b/src/main/java/com/scottkillen/mod/scottstweaks/TheMod.java
index 23151e1..a3961cf 100644
--- a/src/main/java/com/scottkillen/mod/scottstweaks/TheMod.java
+++ b/src/main/java/com/scottkillen/mod/scottstweaks/TheMod.java
@@ -7,9 +7,10 @@
import com.scottkillen.mod.koresample.compat.versionchecker.Versioned;
import com.scottkillen.mod.koresample.config.ConfigEventHandler;
import com.scottkillen.mod.scottstweaks.config.Settings;
-import com.scottkillen.mod.scottstweaks.tweaks.ClayGenerator;
import com.scottkillen.mod.scottstweaks.tweaks.chicken.ChickenPlucker;
import com.scottkillen.mod.scottstweaks.tweaks.planting.Planter;
+import com.scottkillen.mod.scottstweaks.tweaks.spawncontrol.SpawnGovernor;
+import com.scottkillen.mod.scottstweaks.tweaks.worldgen.ClayGenerator;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.Mod.Instance;
@@ -57,6 +58,7 @@ public void onFMLInitialization(FMLInitializationEvent unused)
new ChickenPlucker().listen(MinecraftForge.EVENT_BUS);
new Planter().listen(MinecraftForge.EVENT_BUS);
new ClayGenerator().install();
+ new SpawnGovernor().listen(MinecraftForge.EVENT_BUS);
}
@Override
diff --git a/src/main/java/com/scottkillen/mod/scottstweaks/config/Settings.java b/src/main/java/com/scottkillen/mod/scottstweaks/config/Settings.java
index ba788c2..de9cbaf 100644
--- a/src/main/java/com/scottkillen/mod/scottstweaks/config/Settings.java
+++ b/src/main/java/com/scottkillen/mod/scottstweaks/config/Settings.java
@@ -16,6 +16,8 @@ public enum Settings implements ConfigSyncable
String.format("%s%sfeather_drop", Configuration.CATEGORY_GENERAL, Configuration.CATEGORY_SPLITTER);
private static final String CATEGORY_PLANTING =
String.format("%s%splanting", Configuration.CATEGORY_GENERAL, Configuration.CATEGORY_SPLITTER);
+ private static final String CATEGORY_SPAWN_CONTROL =
+ String.format("%s%sspawn_control", Configuration.CATEGORY_GENERAL, Configuration.CATEGORY_SPLITTER);
private int chickFeatherQuantity = 1;
@@ -27,6 +29,8 @@ public enum Settings implements ConfigSyncable
private int clayVeinSizeMin = 12;
private int claySpawnYMin = 50;
private int claySpawnYMax = 60;
+ private int batSpawnPercent = 100;
+ private int squidSpawnPercent = 100;
private boolean doChicksDropFeathers = true;
private boolean doHensDropFeathers = true;
private boolean doPlantGrowable = true;
@@ -75,6 +79,9 @@ public void syncConfig(Configuration config)
clayVeinSizeMin = get(config, "clayVeinSizeMin", CATEGORY_CLAY_SPAWN, clayVeinSizeMin, 0, clayVeinSizeMax);
claySpawnYMax = get(config, "claySpawnYMax", CATEGORY_CLAY_SPAWN, claySpawnYMax, 1, 255);
claySpawnYMin = get(config, "claySpawnYMin", CATEGORY_CLAY_SPAWN, claySpawnYMin, 1, claySpawnYMax);
+
+ batSpawnPercent = get(config, "batSpawnPercent", CATEGORY_SPAWN_CONTROL, batSpawnPercent, 0, 100);
+ squidSpawnPercent = get(config, "squidSpawnPercent", CATEGORY_SPAWN_CONTROL, squidSpawnPercent, 0, 100);
}
public int chickFeatherQuantity() { return chickFeatherQuantity; }
@@ -101,6 +108,10 @@ public void syncConfig(Configuration config)
public int henFeatherRarity() { return henFeatherRarity; }
+ public int batSpawnPercent() { return batSpawnPercent; }
+
+ public int squidSpawnPercent() { return squidSpawnPercent; }
+
@Override
public String toString()
{
@@ -109,6 +120,7 @@ public String toString()
.add("henFeatherRarity", henFeatherRarity).add("clayVeinQuantity", clayVeinQuantity)
.add("clayVeinSizeMax", clayVeinSizeMax).add("clayVeinSizeMin", clayVeinSizeMin)
.add("claySpawnYMin", claySpawnYMin).add("claySpawnYMax", claySpawnYMax)
+ .add("batSpawnPercent", batSpawnPercent).add("squidSpawnPercent", squidSpawnPercent)
.add("doChicksDropFeathers", doChicksDropFeathers).add("doHensDropFeathers", doHensDropFeathers)
.add("doPlantGrowable", doPlantGrowable).toString();
}
diff --git a/src/main/java/com/scottkillen/mod/scottstweaks/tweaks/spawncontrol/SpawnGovernor.java b/src/main/java/com/scottkillen/mod/scottstweaks/tweaks/spawncontrol/SpawnGovernor.java
new file mode 100644
index 0000000..a0c0760
--- /dev/null
+++ b/src/main/java/com/scottkillen/mod/scottstweaks/tweaks/spawncontrol/SpawnGovernor.java
@@ -0,0 +1,36 @@
+package com.scottkillen.mod.scottstweaks.tweaks.spawncontrol;
+
+import com.scottkillen.mod.koresample.common.util.event.ForgeEventListener;
+import com.scottkillen.mod.scottstweaks.config.Settings;
+import cpw.mods.fml.common.eventhandler.Event.Result;
+import cpw.mods.fml.common.eventhandler.SubscribeEvent;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.passive.EntityBat;
+import net.minecraft.entity.passive.EntitySquid;
+import net.minecraftforge.event.entity.living.LivingSpawnEvent.CheckSpawn;
+
+public final class SpawnGovernor extends ForgeEventListener
+{
+ @SuppressWarnings("MethodMayBeStatic")
+ @SubscribeEvent
+ public void onCheckSpawn(CheckSpawn event)
+ {
+ if (event.entity.worldObj.isRemote) return;
+
+ if (event.entity instanceof EntityBat || event.entity instanceof EntitySquid)
+ {
+ event.setResult(limitSpawn(event.entity));
+ }
+ }
+
+ private Result limitSpawn(Entity entity)
+ {
+ final Settings settings = Settings.INSTANCE;
+ final int successChance =
+ entity instanceof EntityBat ? settings.batSpawnPercent() : settings.squidSpawnPercent();
+ if (successChance == 0) return Result.DENY;
+ if (successChance == 100) return Result.DEFAULT;
+
+ return entity.worldObj.rand.nextInt(100) < successChance ? Result.DEFAULT : Result.DENY;
+ }
+}
diff --git a/src/main/java/com/scottkillen/mod/scottstweaks/tweaks/ClayGenerator.java b/src/main/java/com/scottkillen/mod/scottstweaks/tweaks/worldgen/ClayGenerator.java
similarity index 96%
rename from src/main/java/com/scottkillen/mod/scottstweaks/tweaks/ClayGenerator.java
rename to src/main/java/com/scottkillen/mod/scottstweaks/tweaks/worldgen/ClayGenerator.java
index 53e8c63..41f9e96 100644
--- a/src/main/java/com/scottkillen/mod/scottstweaks/tweaks/ClayGenerator.java
+++ b/src/main/java/com/scottkillen/mod/scottstweaks/tweaks/worldgen/ClayGenerator.java
@@ -1,4 +1,4 @@
-package com.scottkillen.mod.scottstweaks.tweaks;
+package com.scottkillen.mod.scottstweaks.tweaks.worldgen;
import com.scottkillen.mod.scottstweaks.config.Settings;
import cpw.mods.fml.common.IWorldGenerator;
diff --git a/src/main/resources/CHANGELOG.md b/src/main/resources/CHANGELOG.md
index 83750a7..b9f83c7 100644
--- a/src/main/resources/CHANGELOG.md
+++ b/src/main/resources/CHANGELOG.md
@@ -1,3 +1,9 @@
+# Scott's Tweaks Changelog
+
+## 1.2.0
+- Update build environment to minecraft Forge 10.13.2.1291
+- Added bat and squid spawn control.
+
## 1.1.0
- Added clay generation to overworld.
diff --git a/src/main/resources/assets/scottstweaks/lang/en_US.lang b/src/main/resources/assets/scottstweaks/lang/en_US.lang
index e626703..be074d5 100644
--- a/src/main/resources/assets/scottstweaks/lang/en_US.lang
+++ b/src/main/resources/assets/scottstweaks/lang/en_US.lang
@@ -12,3 +12,5 @@ config.scottstweaks:doChicksDropFeathers='True' enables feather drops from live
config.scottstweaks:doHensDropFeathers='True' enables feather drops from live adult chickens.
config.scottstweaks:henFeatherQuantity=The number of feathers dropped by live adult chickens.
config.scottstweaks:henFeatherRarity=The rarity of an adult chicken dropping feathers. Higher values are more rare.
+config.scottstweaks:batSpawnPercent=Limit bat spawning to this percentage of vanilla rate. (0 disables spawns; 100 preserves vanilla rate)
+config.scottstweaks:squidSpawnPercent=Limit squid spawning to this percentage of vanilla rate. (0 disables spawns; 100 preserves vanilla rate)
diff --git a/version/gradle.properties b/version/gradle.properties
index d3bf69a..ad1d6f7 100644
--- a/version/gradle.properties
+++ b/version/gradle.properties
@@ -1,7 +1,7 @@
-#Sun, 08 Feb 2015 23:28:27 -0500
+#Sat, 21 Feb 2015 01:35:30 -0500
mcversion=1.7.10
-forgeversion=10.13.2.1235
-koreversion=1.2.2
+forgeversion=10.13.2.1291
+koreversion=1.2.5
version_major=1
-version_series=1
+version_series=2
version_revision=0