This commit is contained in:
commit
6052e00429
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# gradle
|
||||
|
||||
.gradle/
|
||||
build/
|
||||
out/
|
||||
classes/
|
||||
|
||||
# idea
|
||||
|
||||
.idea/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
# vscode
|
||||
|
||||
.settings/
|
||||
.vscode/
|
||||
bin/
|
||||
.classpath
|
||||
.project
|
||||
|
||||
# fabric
|
||||
|
||||
run/
|
19
Jenkinsfile
vendored
Normal file
19
Jenkinsfile
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
pipeline {
|
||||
agent {
|
||||
docker {
|
||||
image 'openjdk:8-jdk'
|
||||
}
|
||||
}
|
||||
stages {
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh './gradlew jar'
|
||||
}
|
||||
post {
|
||||
success {
|
||||
archiveArtifacts artifacts: 'build/libs/*', fingerprint: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 TheBrokenRail
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
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 OR COPYRIGHT HOLDERS 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.
|
17
README.md
Normal file
17
README.md
Normal file
@ -0,0 +1,17 @@
|
||||
# SorceryCraft
|
||||
Cast Spells in Minecraft!
|
||||
|
||||
This mod supports the Minecraft 1.16 snapshots.
|
||||
|
||||
## Spells
|
||||
Spells are found throughout the world in chests. When you pick up a Spell you will learn it, one you learn a Spell you can apply it to a blank or existing Spell in the Casting Table. You can cast Spells by right-clicking.
|
||||
|
||||
## Casting Table
|
||||
You can apply Spells to blank or existing Spells in the Casting Table. Doing so will require a certain amount of levels, and sometimes may require an item.
|
||||
|
||||
## Crafting
|
||||
#### Blank Spell
|
||||
Craft a blank Spell by placing a Paper in the center of a Crafting Table, and surround it with 4 Lapis Lazuli in a diamond shape.
|
||||
|
||||
#### Casting table
|
||||
Craft a Casting Table by placing a Diamond in the center of a Crafting Table, place 4 Lapis Lazuli in the corners, and fill in the rest with 4 Cobblestone.
|
75
build.gradle
Normal file
75
build.gradle
Normal file
@ -0,0 +1,75 @@
|
||||
plugins {
|
||||
id 'fabric-loom' version '0.2.6-SNAPSHOT'
|
||||
id 'maven-publish'
|
||||
}
|
||||
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
|
||||
archivesBaseName = project.archives_base_name
|
||||
version = project.mod_version
|
||||
group = project.maven_group
|
||||
|
||||
minecraft {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
minecraft "com.mojang:minecraft:${project.minecraft_version}"
|
||||
mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2"
|
||||
modCompile "net.fabricmc:fabric-loader:${project.loader_version}"
|
||||
|
||||
modCompile "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"
|
||||
}
|
||||
|
||||
processResources {
|
||||
inputs.property "version", project.version
|
||||
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
include "fabric.mod.json"
|
||||
expand "version": project.version
|
||||
}
|
||||
|
||||
from(sourceSets.main.resources.srcDirs) {
|
||||
exclude "fabric.mod.json"
|
||||
}
|
||||
}
|
||||
|
||||
// ensure that the encoding is set to UTF-8, no matter what the system default is
|
||||
// this fixes some edge cases with special characters not displaying correctly
|
||||
// see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html
|
||||
tasks.withType(JavaCompile) {
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
|
||||
// Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task
|
||||
// if it is present.
|
||||
// If you remove this task, sources will not be generated.
|
||||
task sourcesJar(type: Jar, dependsOn: classes) {
|
||||
classifier "sources"
|
||||
from sourceSets.main.allSource
|
||||
}
|
||||
|
||||
jar {
|
||||
from "LICENSE"
|
||||
}
|
||||
|
||||
// configure the maven publication
|
||||
publishing {
|
||||
publications {
|
||||
mavenJava(MavenPublication) {
|
||||
// add all the jars that should be included when publishing to maven
|
||||
artifact(remapJar) {
|
||||
builtBy remapJar
|
||||
}
|
||||
artifact(sourcesJar) {
|
||||
builtBy remapSourcesJar
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// select the repositories you want to publish to
|
||||
repositories {
|
||||
// uncomment to publish to the local maven
|
||||
// mavenLocal()
|
||||
}
|
||||
}
|
17
gradle.properties
Normal file
17
gradle.properties
Normal file
@ -0,0 +1,17 @@
|
||||
# Done to increase the memory available to gradle.
|
||||
org.gradle.jvmargs = -Xmx1G
|
||||
|
||||
# Fabric Properties
|
||||
# check these on https://fabricmc.net/use
|
||||
minecraft_version = 20w09a
|
||||
yarn_mappings = 20w09a+build.6
|
||||
loader_version = 0.7.8+build.184
|
||||
|
||||
# Mod Properties
|
||||
mod_version = 1.0.0
|
||||
maven_group = com.thebrokenrail
|
||||
archives_base_name = sorcerycraft
|
||||
|
||||
# Dependencies
|
||||
# currently not on the main fabric site, check on the maven: https://maven.fabricmc.net/net/fabricmc/fabric-api/fabric-api
|
||||
fabric_version = 0.4.33+build.301-1.16
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
#Sat Feb 29 21:58:32 EST 2020
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.5.1-all.zip
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
188
gradlew
vendored
Executable file
188
gradlew
vendored
Executable file
@ -0,0 +1,188 @@
|
||||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or 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
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# 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\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# 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"'
|
||||
|
||||
# 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
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
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" -a "$nonstop" = "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"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# 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
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=$(save "$@")
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
|
||||
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
|
||||
cd "$(dirname "$0")"
|
||||
fi
|
||||
|
||||
exec "$JAVACMD" "$@"
|
100
gradlew.bat
vendored
Normal file
100
gradlew.bat
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
@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
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@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
|
||||
|
||||
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 Windows variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_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=%*
|
||||
|
||||
: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
|
10
settings.gradle
Normal file
10
settings.gradle
Normal file
@ -0,0 +1,10 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
jcenter()
|
||||
maven {
|
||||
name = 'Fabric'
|
||||
url = 'https://maven.fabricmc.net/'
|
||||
}
|
||||
gradlePluginPortal()
|
||||
}
|
||||
}
|
122
src/main/java/com/thebrokenrail/sorcerycraft/SorceryCraft.java
Normal file
122
src/main/java/com/thebrokenrail/sorcerycraft/SorceryCraft.java
Normal file
@ -0,0 +1,122 @@
|
||||
package com.thebrokenrail.sorcerycraft;
|
||||
|
||||
import com.thebrokenrail.sorcerycraft.block.CastingTableBlock;
|
||||
import com.thebrokenrail.sorcerycraft.block.CastingTableContainer;
|
||||
import com.thebrokenrail.sorcerycraft.client.block.CastingTableScreen;
|
||||
import com.thebrokenrail.sorcerycraft.client.entity.SpellEntityRenderer;
|
||||
import com.thebrokenrail.sorcerycraft.command.SpellCommand;
|
||||
import com.thebrokenrail.sorcerycraft.entity.SpellEntity;
|
||||
import com.thebrokenrail.sorcerycraft.item.SpellItem;
|
||||
import com.thebrokenrail.sorcerycraft.packet.LearnedNewSpellS2CPacket;
|
||||
import com.thebrokenrail.sorcerycraft.packet.SelectSpellC2SPacket;
|
||||
import com.thebrokenrail.sorcerycraft.packet.UpdateKnownSpellsS2CPacket;
|
||||
import com.thebrokenrail.sorcerycraft.spell.RandomSpellLootTableFunction;
|
||||
import com.thebrokenrail.sorcerycraft.spell.SpellRegistry;
|
||||
import net.fabricmc.api.ClientModInitializer;
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder;
|
||||
import net.fabricmc.fabric.api.client.rendereregistry.v1.EntityRendererRegistry;
|
||||
import net.fabricmc.fabric.api.client.screen.ScreenProviderRegistry;
|
||||
import net.fabricmc.fabric.api.container.ContainerProviderRegistry;
|
||||
import net.fabricmc.fabric.api.entity.FabricEntityTypeBuilder;
|
||||
import net.fabricmc.fabric.api.loot.v1.FabricLootPoolBuilder;
|
||||
import net.fabricmc.fabric.api.loot.v1.event.LootTableLoadingCallback;
|
||||
import net.fabricmc.fabric.api.registry.CommandRegistry;
|
||||
import net.fabricmc.fabric.impl.networking.ClientSidePacketRegistryImpl;
|
||||
import net.fabricmc.fabric.impl.networking.ServerSidePacketRegistryImpl;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
import net.minecraft.entity.EntityCategory;
|
||||
import net.minecraft.entity.EntityDimensions;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.item.*;
|
||||
import net.minecraft.loot.BinomialLootTableRange;
|
||||
import net.minecraft.loot.LootTables;
|
||||
import net.minecraft.loot.entry.ItemEntry;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class SorceryCraft implements ModInitializer, ClientModInitializer {
|
||||
public static final String NAMESPACE = "sorcerycraft";
|
||||
public static SpellItem SPELL_ITEM;
|
||||
public static CastingTableBlock CASTING_TABLE_BLOCK;
|
||||
public static BlockItem CASTING_TABLE_BLOCK_ITEM;
|
||||
public static ItemGroup ITEM_GROUP;
|
||||
public static EntityType<SpellEntity> SPELL_ENTITY;
|
||||
public static final Identifier[] LOOT_TABLES = new Identifier[] {
|
||||
LootTables.SIMPLE_DUNGEON_CHEST,
|
||||
LootTables.END_CITY_TREASURE_CHEST,
|
||||
LootTables.NETHER_BRIDGE_CHEST,
|
||||
LootTables.ABANDONED_MINESHAFT_CHEST,
|
||||
LootTables.SHIPWRECK_TREASURE_CHEST,
|
||||
LootTables.DESERT_PYRAMID_CHEST,
|
||||
LootTables.JUNGLE_TEMPLE_CHEST,
|
||||
LootTables.STRONGHOLD_LIBRARY_CHEST
|
||||
};
|
||||
|
||||
private boolean isSelectedLootTable(Identifier lootTable) {
|
||||
for (Identifier id : LOOT_TABLES) {
|
||||
if (id.equals(lootTable)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitialize() {
|
||||
ITEM_GROUP = FabricItemGroupBuilder.create(
|
||||
new Identifier(NAMESPACE, "spells"))
|
||||
.icon(() -> new ItemStack(SPELL_ITEM))
|
||||
.build();
|
||||
|
||||
CASTING_TABLE_BLOCK = new CastingTableBlock();
|
||||
CASTING_TABLE_BLOCK_ITEM = new BlockItem(CASTING_TABLE_BLOCK, new Item.Settings().group(ITEM_GROUP));
|
||||
SPELL_ITEM = new SpellItem();
|
||||
|
||||
SPELL_ENTITY = FabricEntityTypeBuilder.<SpellEntity>create(EntityCategory.MISC, SpellEntity::new).size(EntityDimensions.fixed(0.25f, 0.25f)).build();
|
||||
|
||||
Registry.register(Registry.ITEM, new Identifier(NAMESPACE, "spell"), SPELL_ITEM);
|
||||
Registry.register(Registry.BLOCK, new Identifier(NAMESPACE, "casting_table"), CASTING_TABLE_BLOCK);
|
||||
Registry.register(Registry.ITEM, new Identifier(NAMESPACE, "casting_table"), CASTING_TABLE_BLOCK_ITEM);
|
||||
Registry.register(Registry.ENTITY_TYPE, new Identifier(NAMESPACE, "spell"), SPELL_ENTITY);
|
||||
|
||||
SpellRegistry.init();
|
||||
|
||||
ContainerProviderRegistry.INSTANCE.registerFactory(new Identifier(NAMESPACE, "casting_table"), (syncId, identifier, player, buf) -> {
|
||||
final World world = player.world;
|
||||
final BlockPos pos = buf.readBlockPos();
|
||||
return Objects.requireNonNull(world.getBlockState(pos).createContainerFactory(player.world, pos)).createMenu(syncId, player.inventory, player);
|
||||
});
|
||||
|
||||
CommandRegistry.INSTANCE.register(false, SpellCommand::register);
|
||||
|
||||
ServerSidePacketRegistryImpl.INSTANCE.register(new Identifier(NAMESPACE, "select_spell"), SelectSpellC2SPacket::handle);
|
||||
ClientSidePacketRegistryImpl.INSTANCE.register(new Identifier(NAMESPACE, "update_known_spells"), UpdateKnownSpellsS2CPacket::handle);
|
||||
ClientSidePacketRegistryImpl.INSTANCE.register(new Identifier(NAMESPACE, "learned_new_spell"), LearnedNewSpellS2CPacket::handle);
|
||||
|
||||
LootTableLoadingCallback.EVENT.register((resourceManager, lootManager, id, supplier, setter) -> {
|
||||
if (isSelectedLootTable(id)) {
|
||||
FabricLootPoolBuilder poolBuilder = FabricLootPoolBuilder.builder()
|
||||
.withRolls(new BinomialLootTableRange(2, 0.5f))
|
||||
.withEntry(ItemEntry.builder(SPELL_ITEM))
|
||||
.withFunction(new RandomSpellLootTableFunction.Builder());
|
||||
|
||||
supplier.withPool(poolBuilder);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeClient() {
|
||||
EntityRendererRegistry.INSTANCE.register(SPELL_ENTITY, (entityRenderDispatcher, context) -> new SpellEntityRenderer(entityRenderDispatcher));
|
||||
ScreenProviderRegistry.INSTANCE.<CastingTableContainer>registerFactory(new Identifier(NAMESPACE, "casting_table"), (container) -> {
|
||||
assert MinecraftClient.getInstance().player != null;
|
||||
return new CastingTableScreen(container, MinecraftClient.getInstance().player.inventory, new TranslatableText("block.sorcerycraft.casting_table"));
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
package com.thebrokenrail.sorcerycraft.block;
|
||||
|
||||
import com.thebrokenrail.sorcerycraft.SorceryCraft;
|
||||
import net.fabricmc.fabric.api.block.FabricBlockSettings;
|
||||
import net.fabricmc.fabric.api.container.ContainerProviderRegistry;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Material;
|
||||
import net.minecraft.container.BlockContext;
|
||||
import net.minecraft.container.NameableContainerFactory;
|
||||
import net.minecraft.container.SimpleNamedContainerFactory;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class CastingTableBlock extends Block {
|
||||
public CastingTableBlock() {
|
||||
super(FabricBlockSettings.of(Material.STONE).strength(2.0F, 6.0F).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockHitResult hit) {
|
||||
if (!world.isClient()) {
|
||||
ContainerProviderRegistry.INSTANCE.openContainer(new Identifier(SorceryCraft.NAMESPACE, "casting_table"), player, (buf) -> buf.writeBlockPos(pos));
|
||||
}
|
||||
return ActionResult.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NameableContainerFactory createContainerFactory(BlockState state, World world, BlockPos pos) {
|
||||
return new SimpleNamedContainerFactory((i, playerInventory, playerEntity) -> new CastingTableContainer(i, playerInventory, BlockContext.create(world, pos)), new TranslatableText("container.sorcerycraft.casting_table"));
|
||||
}
|
||||
}
|
@ -0,0 +1,190 @@
|
||||
package com.thebrokenrail.sorcerycraft.block;
|
||||
|
||||
import com.thebrokenrail.sorcerycraft.SorceryCraft;
|
||||
import com.thebrokenrail.sorcerycraft.spell.Spell;
|
||||
import com.thebrokenrail.sorcerycraft.spell.SpellPlayerEntity;
|
||||
import com.thebrokenrail.sorcerycraft.spell.SpellRegistry;
|
||||
import com.thebrokenrail.sorcerycraft.spell.SpellTag;
|
||||
import net.minecraft.container.*;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.inventory.BasicInventory;
|
||||
import net.minecraft.inventory.CraftingResultInventory;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class CastingTableContainer extends Container {
|
||||
private final Inventory inventory;
|
||||
private final Inventory result;
|
||||
private final Spell[] spells;
|
||||
private final BlockContext context;
|
||||
private int index = 0;
|
||||
|
||||
public CastingTableContainer(int syncId, PlayerInventory playerInventory, BlockContext blockContext) {
|
||||
super(ContainerType.STONECUTTER, syncId);
|
||||
|
||||
inventory = new BasicInventory(2) {
|
||||
public void markDirty() {
|
||||
super.markDirty();
|
||||
CastingTableContainer.this.onContentChanged(this);
|
||||
}
|
||||
};
|
||||
context = blockContext;
|
||||
result = new CraftingResultInventory();
|
||||
|
||||
if (playerInventory.player.isCreative()) {
|
||||
spells = SpellRegistry.getSpells();
|
||||
} else {
|
||||
SpellPlayerEntity spellPlayer = (SpellPlayerEntity) playerInventory.player;
|
||||
Map<String, Integer> spellsMap = spellPlayer.getSpells();
|
||||
List<Spell> spellsArray = new ArrayList<>();
|
||||
|
||||
Spell[] allSpells = SpellRegistry.getSpells();
|
||||
for (Spell spell : allSpells) {
|
||||
if (spellsMap.containsKey(spell.getID()) && spellsMap.get(spell.getID()) >= spell.getLevel()) {
|
||||
spellsArray.add(spell);
|
||||
}
|
||||
}
|
||||
spells = spellsArray.toArray(new Spell[0]);
|
||||
}
|
||||
|
||||
addSlot(new Slot(inventory, 0, 136, 37) {
|
||||
@Override
|
||||
public boolean canInsert(ItemStack stack) {
|
||||
return stack.getItem().equals(SorceryCraft.SPELL_ITEM);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxStackAmount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStackChanged(ItemStack originalItem, ItemStack itemStack) {
|
||||
super.onStackChanged(originalItem, itemStack);
|
||||
markDirty();
|
||||
}
|
||||
});
|
||||
addSlot(new Slot(inventory, 1, 162, 37) {
|
||||
@Override
|
||||
public void onStackChanged(ItemStack originalItem, ItemStack itemStack) {
|
||||
super.onStackChanged(originalItem, itemStack);
|
||||
markDirty();
|
||||
}
|
||||
});
|
||||
addSlot(new Slot(result, 2, 220, 37) {
|
||||
public boolean canInsert(ItemStack stack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean canTakeItems(PlayerEntity playerEntity) {
|
||||
return canTakeResult(playerEntity) && hasStack();
|
||||
}
|
||||
|
||||
public ItemStack onTakeItem(PlayerEntity player, ItemStack stack) {
|
||||
if (!player.isCreative()) {
|
||||
player.addExperienceLevels(-spells[index].getXPCost());
|
||||
}
|
||||
|
||||
player.playSound(SoundEvents.BLOCK_ENCHANTMENT_TABLE_USE, 1.0f, 1.0f);
|
||||
|
||||
CastingTableContainer.this.inventory.setInvStack(0, ItemStack.EMPTY);
|
||||
CastingTableContainer.this.inventory.takeInvStack(1, spells[index].getItemCost().getCount());
|
||||
return stack;
|
||||
}
|
||||
});
|
||||
|
||||
int k;
|
||||
for (k = 0; k < 3; ++k) {
|
||||
for(int j = 0; j < 9; ++j) {
|
||||
addSlot(new Slot(playerInventory, j + k * 9 + 9, 108 + j * 18, 84 + k * 18));
|
||||
}
|
||||
}
|
||||
|
||||
for (k = 0; k < 9; ++k) {
|
||||
addSlot(new Slot(playerInventory, k, 108 + k * 18, 142));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canTakeResult(PlayerEntity playerEntity) {
|
||||
return (playerEntity.isCreative() || playerEntity.experienceLevel >= spells[index].getXPCost()) && spells[index].getXPCost() > 0;
|
||||
}
|
||||
|
||||
public void setIndex(int index) {
|
||||
this.index = index;
|
||||
onContentChanged(inventory);
|
||||
|
||||
if (inventory.getInvStack(0).isEmpty() && inventory.getInvStack(1).isEmpty()) {
|
||||
ItemStack spellItem = new ItemStack(SorceryCraft.SPELL_ITEM);
|
||||
autoFill(0, spellItem, true);
|
||||
ItemStack paymentItem = getRecipes()[index].getItemCost();
|
||||
autoFill(1, paymentItem, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(PlayerEntity player) {
|
||||
super.close(player);
|
||||
context.run((world, blockPos) -> {
|
||||
dropInventory(player, world, inventory);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onContentChanged(Inventory inventory) {
|
||||
super.onContentChanged(inventory);
|
||||
ItemStack item = inventory.getInvStack(0);
|
||||
ItemStack cost = inventory.getInvStack(1);
|
||||
if (inventory == this.inventory) {
|
||||
if (spells.length > 0 &&
|
||||
!item.isEmpty() &&
|
||||
cost.getItem() == spells[index].getItemCost().getItem() &&
|
||||
cost.getCount() >= spells[index].getItemCost().getCount()) {
|
||||
ItemStack resultItem = item.copy();
|
||||
Map<String, Integer> resultSpells = SpellTag.getSpells(resultItem);
|
||||
if (!resultSpells.containsKey(spells[index].getID()) || resultSpells.get(spells[index].getID()) <= spells[index].getLevel()) {
|
||||
resultSpells.put(spells[index].getID(), spells[index].getLevel());
|
||||
}
|
||||
SpellTag.setSpells(resultItem, resultSpells);
|
||||
result.setInvStack(2, resultItem);
|
||||
} else {
|
||||
result.setInvStack(2, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void autoFill(int slot, ItemStack stack, boolean onlyOne) {
|
||||
if (!stack.isEmpty()) {
|
||||
for (int i = 3; i < 39; ++i) {
|
||||
ItemStack itemStack = slots.get(i).getStack();
|
||||
if (!itemStack.isEmpty() && stack.getItem() == itemStack.getItem()) {
|
||||
ItemStack invSlot = inventory.getInvStack(slot);
|
||||
int count = invSlot.isEmpty() ? 0 : invSlot.getCount();
|
||||
int requiredCount = Math.min((onlyOne ? 1 : stack.getMaxCount()) - count, itemStack.getCount());
|
||||
ItemStack modifiedItem = itemStack.copy();
|
||||
int totalCount = count + requiredCount;
|
||||
itemStack.decrement(requiredCount);
|
||||
modifiedItem.setCount(totalCount);
|
||||
inventory.setInvStack(slot, modifiedItem);
|
||||
if (totalCount >= stack.getMaxCount() || onlyOne) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse(PlayerEntity player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Spell[] getRecipes() {
|
||||
return spells;
|
||||
}
|
||||
}
|
@ -0,0 +1,235 @@
|
||||
package com.thebrokenrail.sorcerycraft.client.block;
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import com.thebrokenrail.sorcerycraft.block.CastingTableContainer;
|
||||
import com.thebrokenrail.sorcerycraft.packet.SelectSpellC2SPacket;
|
||||
import com.thebrokenrail.sorcerycraft.spell.Spell;
|
||||
import com.thebrokenrail.sorcerycraft.spell.SpellTag;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.font.TextRenderer;
|
||||
import net.minecraft.client.gui.screen.ingame.ContainerScreen;
|
||||
import net.minecraft.client.gui.widget.ButtonWidget;
|
||||
import net.minecraft.entity.player.PlayerInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class CastingTableScreen extends ContainerScreen<CastingTableContainer> {
|
||||
private static final Identifier TEXTURE = new Identifier("textures/gui/container/villager2.png");
|
||||
private int selectedIndex;
|
||||
private int indexStartOffset;
|
||||
private boolean scrolling;
|
||||
|
||||
public CastingTableScreen(CastingTableContainer container, PlayerInventory inventory, Text title) {
|
||||
super(container, inventory, title);
|
||||
containerWidth = 276;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawForeground(int mouseX, int mouseY) {
|
||||
int j = containerHeight - 94;
|
||||
font.draw(title.asFormattedString(), (float) (49 + this.containerWidth / 2 - font.getStringWidth(title.asFormattedString()) / 2), 6.0F, 4210752);
|
||||
font.draw(playerInventory.getDisplayName().asFormattedString(), 107.0F, (float) j, 4210752);
|
||||
String spells = new TranslatableText("container.sorcerycraft.spells").getString();
|
||||
font.draw(spells, (float) (5 - font.getStringWidth(spells) / 2 + 48), 6.0F, 4210752);
|
||||
renderXPCost();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawBackground(float delta, int mouseX, int mouseY) {
|
||||
RenderSystem.color4f(1.0F, 1.0F, 1.0F, 1.0F);
|
||||
assert minecraft != null;
|
||||
minecraft.getTextureManager().bindTexture(TEXTURE);
|
||||
int i = (width - containerWidth) / 2;
|
||||
int j = (height - containerHeight) / 2;
|
||||
blit(i, j, this.getBlitOffset(), 0.0F, 0.0F, containerWidth, containerHeight, 256, 512);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(int mouseX, int mouseY, float delta) {
|
||||
renderBackground();
|
||||
super.render(mouseX, mouseY, delta);
|
||||
drawMouseoverTooltip(mouseX, mouseY);
|
||||
|
||||
renderScrollbar();
|
||||
renderItems();
|
||||
|
||||
for (WidgetButtonPage button : buttons) {
|
||||
if (button.isHovered()) {
|
||||
button.renderToolTip(mouseX, mouseY);
|
||||
}
|
||||
button.visible = button.index < container.getRecipes().length;
|
||||
if (button.visible) {
|
||||
Spell spell = container.getRecipes()[button.getIndex() + indexStartOffset];
|
||||
button.setMessage(SpellTag.getTranslatedSpell(spell.getID(), spell.getLevel()).getString());
|
||||
button.setFocused((button.getIndex() + indexStartOffset) == selectedIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void renderScrollbar() {
|
||||
Spell[] spells = container.getRecipes();
|
||||
assert minecraft != null;
|
||||
minecraft.getTextureManager().bindTexture(TEXTURE);
|
||||
int i = (width - containerWidth) / 2;
|
||||
int j = (height - containerHeight) / 2;
|
||||
int k = spells.length + 1 - 7;
|
||||
|
||||
if (k > 1) {
|
||||
int l = 139 - (27 + (k - 1) * 139 / k);
|
||||
int m = 1 + l / k + 139 / k;
|
||||
int o = Math.min(113, indexStartOffset * m);
|
||||
if (this.indexStartOffset == k - 1) {
|
||||
o = 113;
|
||||
}
|
||||
|
||||
blit(i + 94, j + 18 + o, getBlitOffset(), 0.0F, 199.0F, 6, 27, 256, 512);
|
||||
} else {
|
||||
blit(i + 94, j + 18, getBlitOffset(), 6.0F, 199.0F, 6, 27, 256, 512);
|
||||
}
|
||||
}
|
||||
|
||||
private void renderItems() {
|
||||
int i = (width - containerWidth) / 2;
|
||||
int j = (height - containerHeight) / 2;
|
||||
int k = j + 16 + 1;
|
||||
|
||||
Spell[] spells = container.getRecipes();
|
||||
for (int x = 0; x < spells.length; x++) {
|
||||
if (!canScroll(spells.length) || (x >= indexStartOffset && x < 7 + indexStartOffset)) {
|
||||
ItemStack itemStack = spells[x].getItemCost();
|
||||
itemRenderer.zOffset = 100.0F;
|
||||
int n = k + 2;
|
||||
|
||||
itemRenderer.renderGuiItem(itemStack, i + 5 + 68, n);
|
||||
itemRenderer.renderGuiItemOverlay(font, itemStack, i + 5 + 68, n);
|
||||
itemRenderer.zOffset = 0.0F;
|
||||
k += 20;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void renderXPCost() {
|
||||
if (container.getRecipes().length > 0) {
|
||||
int i = container.getRecipes()[selectedIndex].getXPCost();
|
||||
int j = 8453920;
|
||||
assert minecraft != null;
|
||||
assert minecraft.player != null;
|
||||
String string = new TranslatableText("container.repair.cost", i).getString();
|
||||
if (!container.canTakeResult(playerInventory.player)) {
|
||||
j = 16736352;
|
||||
}
|
||||
|
||||
int x2 = containerWidth - 8;
|
||||
int x1 = x2 - font.getStringWidth(string);
|
||||
fill(x1, 65, x2, 77, 1325400064);
|
||||
font.drawWithShadow(string, (float) x1, 67.0F, j);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean canScroll(int listSize) {
|
||||
return listSize > 7;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseScrolled(double d, double e, double amount) {
|
||||
int i = container.getRecipes().length;
|
||||
if (this.canScroll(i)) {
|
||||
int j = i - 7;
|
||||
indexStartOffset = (int) ((double) indexStartOffset - amount);
|
||||
indexStartOffset = MathHelper.clamp(indexStartOffset, 0, j);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseDragged(double mouseX, double mouseY, int button, double deltaX, double deltaY) {
|
||||
int i = container.getRecipes().length;
|
||||
if (scrolling) {
|
||||
int j = y + 18;
|
||||
int k = j + 139;
|
||||
int l = i - 7;
|
||||
float f = ((float) mouseY - (float) j - 13.5F) / ((float) (k - j) - 27.0F);
|
||||
f = f * (float)l + 0.5F;
|
||||
indexStartOffset = MathHelper.clamp((int) f, 0, l);
|
||||
return true;
|
||||
} else {
|
||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mouseClicked(double mouseX, double mouseY, int button) {
|
||||
scrolling = false;
|
||||
int i = (width - containerWidth) / 2;
|
||||
int j = (height - containerHeight) / 2;
|
||||
if (this.canScroll(container.getRecipes().length) && mouseX > (double)(i + 94) && mouseX < (double) (i + 94 + 6) && mouseY > (double) (j + 18) && mouseY <= (double) (j + 18 + 139 + 1)) {
|
||||
scrolling = true;
|
||||
}
|
||||
|
||||
return super.mouseClicked(mouseX, mouseY, button);
|
||||
}
|
||||
|
||||
private void syncRecipeIndex() {
|
||||
container.setIndex(selectedIndex);
|
||||
assert minecraft != null;
|
||||
SelectSpellC2SPacket.send(minecraft, selectedIndex);
|
||||
}
|
||||
|
||||
private final WidgetButtonPage[] buttons = new WidgetButtonPage[7];
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
int i = (width - containerWidth) / 2;
|
||||
int j = (height - containerHeight) / 2;
|
||||
int k = j + 16 + 2;
|
||||
|
||||
for (int l = 0; l < 7; ++l) {
|
||||
buttons[l] = addButton(new WidgetButtonPage(i + 5, k, l, (buttonWidget) -> {
|
||||
selectedIndex = ((WidgetButtonPage) buttonWidget).getIndex() + indexStartOffset;
|
||||
syncRecipeIndex();
|
||||
}));
|
||||
k += 20;
|
||||
}
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
private class WidgetButtonPage extends ButtonWidget {
|
||||
final int index;
|
||||
|
||||
public WidgetButtonPage(int i, int j, int k, PressAction pressAction) {
|
||||
super(i, j, 89, 20, "", pressAction);
|
||||
index = k;
|
||||
visible = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFocused(boolean state) {
|
||||
super.setFocused(state);
|
||||
}
|
||||
|
||||
public int getIndex() {
|
||||
return this.index;
|
||||
}
|
||||
|
||||
public void renderToolTip(int mouseX, int mouseY) {
|
||||
if (isHovered && container.getRecipes().length > index + indexStartOffset && mouseX > this.x + 65) {
|
||||
ItemStack itemStack = container.getRecipes()[index + indexStartOffset].getItemCost();
|
||||
if (!itemStack.isEmpty()) {
|
||||
renderTooltip(itemStack, mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawCenteredString(TextRenderer textRenderer, String str, int ignored, int y, int color) {
|
||||
drawString(textRenderer, str, x + 5, y, color);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package com.thebrokenrail.sorcerycraft.client.entity;
|
||||
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.client.render.entity.EntityRenderDispatcher;
|
||||
import net.minecraft.client.render.entity.EntityRenderer;
|
||||
import net.minecraft.container.PlayerContainer;
|
||||
import net.minecraft.entity.AreaEffectCloudEntity;
|
||||
import net.minecraft.util.Identifier;
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
public class SpellEntityRenderer extends EntityRenderer<AreaEffectCloudEntity> {
|
||||
public SpellEntityRenderer(EntityRenderDispatcher entityRenderDispatcher) {
|
||||
super(entityRenderDispatcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getTexture(AreaEffectCloudEntity areaEffectCloudEntity) {
|
||||
return PlayerContainer.BLOCK_ATLAS_TEXTURE;
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package com.thebrokenrail.sorcerycraft.command;
|
||||
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.suggestion.SuggestionProvider;
|
||||
import com.mojang.brigadier.suggestion.Suggestions;
|
||||
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||
import com.thebrokenrail.sorcerycraft.spell.SpellPlayerEntity;
|
||||
import com.thebrokenrail.sorcerycraft.spell.SpellTag;
|
||||
import net.minecraft.command.arguments.EntityArgumentType;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.server.command.CommandManager;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import net.minecraft.util.Formatting;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class SpellCommand {
|
||||
public static SuggestionProvider<ServerCommandSource> getOptions() {
|
||||
return (ctx, builder) -> getSuggestionsBuilder(builder, new String[]{"clear", "list"});
|
||||
}
|
||||
|
||||
private static CompletableFuture<Suggestions> getSuggestionsBuilder(SuggestionsBuilder builder, String[] list) {
|
||||
String remaining = builder.getRemaining().toLowerCase(Locale.ROOT);
|
||||
|
||||
if (list.length < 1) {
|
||||
return Suggestions.empty();
|
||||
}
|
||||
|
||||
for (String str : list) {
|
||||
if (str.toLowerCase(Locale.ROOT).startsWith(remaining)) {
|
||||
builder.suggest(str);
|
||||
}
|
||||
}
|
||||
return builder.buildFuture();
|
||||
}
|
||||
|
||||
public static void register(CommandDispatcher<ServerCommandSource> dispatcher) {
|
||||
dispatcher.register(CommandManager.literal("spell")
|
||||
.requires(source -> source.hasPermissionLevel(4))
|
||||
.then(CommandManager.argument("action", StringArgumentType.word())
|
||||
.suggests(getOptions())
|
||||
.then(CommandManager.argument("player", EntityArgumentType.player())
|
||||
.executes(ctx -> {
|
||||
String action = StringArgumentType.getString(ctx, "action");
|
||||
PlayerEntity player = EntityArgumentType.getPlayer(ctx, "player");
|
||||
switch (action) {
|
||||
case "clear": {
|
||||
SpellPlayerEntity spellPlayer = (SpellPlayerEntity) player;
|
||||
spellPlayer.setSpells(new HashMap<>());
|
||||
ctx.getSource().sendFeedback(new TranslatableText("command.sorcerycraft.spell.cleared_spells", player.getDisplayName()), true);
|
||||
return 1;
|
||||
}
|
||||
case "list": {
|
||||
ctx.getSource().sendFeedback(new TranslatableText("command.sorcerycraft.spell.listing_spells", player.getDisplayName()), false);
|
||||
SpellPlayerEntity spellPlayer = (SpellPlayerEntity) player;
|
||||
Map<String, Integer> spells = spellPlayer.getSpells();
|
||||
for (Map.Entry<String, Integer> entry : spells.entrySet()) {
|
||||
ctx.getSource().sendFeedback(SpellTag.getTranslatedSpell(entry.getKey(), entry.getValue()).formatted(Formatting.YELLOW), false);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
default: {
|
||||
ctx.getSource().sendFeedback(new TranslatableText("command.sorcerycraft.spell.invalid_action", action).formatted(Formatting.RED), true);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,91 @@
|
||||
package com.thebrokenrail.sorcerycraft.entity;
|
||||
|
||||
import com.thebrokenrail.sorcerycraft.SorceryCraft;
|
||||
import com.thebrokenrail.sorcerycraft.spell.Spell;
|
||||
import com.thebrokenrail.sorcerycraft.spell.SpellRegistry;
|
||||
import com.thebrokenrail.sorcerycraft.spell.SpellTag;
|
||||
import net.fabricmc.api.EnvType;
|
||||
import net.fabricmc.api.Environment;
|
||||
import net.minecraft.entity.*;
|
||||
import net.minecraft.entity.thrown.ThrownItemEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.network.Packet;
|
||||
import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket;
|
||||
import net.minecraft.particle.ParticleTypes;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.util.hit.EntityHitResult;
|
||||
import net.minecraft.util.hit.HitResult;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class SpellEntity extends ThrownItemEntity {
|
||||
public SpellEntity(EntityType<? extends SpellEntity> entityType, World world) {
|
||||
super(entityType, world);
|
||||
}
|
||||
|
||||
public SpellEntity(World world, LivingEntity owner) {
|
||||
super(SorceryCraft.SPELL_ENTITY, owner, world);
|
||||
}
|
||||
|
||||
public SpellEntity(World world, double x, double y, double z) {
|
||||
super(SorceryCraft.SPELL_ENTITY, x, y, z, world);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCollision(HitResult hitResult) {
|
||||
boolean remove = false;
|
||||
if (hitResult.getType() == HitResult.Type.BLOCK) {
|
||||
remove = true;
|
||||
} else if (hitResult.getType() == HitResult.Type.ENTITY) {
|
||||
Entity entity = ((EntityHitResult) hitResult).getEntity();
|
||||
|
||||
Map<String, Integer> spells = SpellTag.getSpells(getItem());
|
||||
|
||||
for (Map.Entry<String, Integer> entry : spells.entrySet()) {
|
||||
Spell spell = SpellRegistry.getSpell(entry.getKey(), entry.getValue());
|
||||
if (spell != null) {
|
||||
if (Math.random() > 0.1 || spells.containsKey("steadfast_spell")) {
|
||||
spell.execute(entity, this, getOwner());
|
||||
} else if (getOwner() != null) {
|
||||
getOwner().playSound(SoundEvents.ENCHANT_THORNS_HIT, 1.0f, 1.0f);
|
||||
spell.execute(getOwner(), this, getOwner());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
remove = true;
|
||||
}
|
||||
if (remove && !getEntityWorld().isClient()) {
|
||||
remove();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Environment(EnvType.CLIENT)
|
||||
public void handleStatus(byte status) {
|
||||
if (status == 0) {
|
||||
for (int i = 0; i < 12; i++) {
|
||||
getEntityWorld().addParticle(ParticleTypes.WITCH, getX(), getY(), getZ(), 0.0d, 0.0d, 0.0d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
if (!getEntityWorld().isClient()) {
|
||||
getEntityWorld().sendEntityStatus(this, (byte) 0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Item getDefaultItem() {
|
||||
return SorceryCraft.SPELL_ITEM;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Packet<?> createSpawnPacket() {
|
||||
return new EntitySpawnS2CPacket(this);
|
||||
}
|
||||
}
|
126
src/main/java/com/thebrokenrail/sorcerycraft/item/SpellItem.java
Normal file
126
src/main/java/com/thebrokenrail/sorcerycraft/item/SpellItem.java
Normal file
@ -0,0 +1,126 @@
|
||||
package com.thebrokenrail.sorcerycraft.item;
|
||||
|
||||
import com.thebrokenrail.sorcerycraft.SorceryCraft;
|
||||
import com.thebrokenrail.sorcerycraft.entity.SpellEntity;
|
||||
import com.thebrokenrail.sorcerycraft.packet.LearnedNewSpellS2CPacket;
|
||||
import com.thebrokenrail.sorcerycraft.spell.*;
|
||||
import net.minecraft.client.item.TooltipContext;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemGroup;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import net.minecraft.util.*;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class SpellItem extends Item {
|
||||
public SpellItem() {
|
||||
super(new Item.Settings().group(SorceryCraft.ITEM_GROUP).rarity(Rarity.UNCOMMON).maxCount(16));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypedActionResult<ItemStack> use(World world, PlayerEntity playerEntity, Hand hand) {
|
||||
ItemStack itemStack = playerEntity.getStackInHand(hand);
|
||||
|
||||
Map<String, Integer> spells = SpellTag.getSpells(itemStack);
|
||||
|
||||
if (spells.size() > 0) {
|
||||
playerEntity.playSound(SoundEvents.BLOCK_ENCHANTMENT_TABLE_USE, 1.0f, 1.0f);
|
||||
|
||||
if (!world.isClient()) {
|
||||
SpellEntity entity = new SpellEntity(world, playerEntity);
|
||||
entity.setItem(itemStack);
|
||||
entity.setProperties(playerEntity, playerEntity.pitch, playerEntity.yaw, 0.0f, 1.5f, 1.0f);
|
||||
world.spawnEntity(entity);
|
||||
}
|
||||
|
||||
if (!playerEntity.isCreative()) {
|
||||
itemStack.decrement(1);
|
||||
}
|
||||
|
||||
return new TypedActionResult<>(ActionResult.SUCCESS, itemStack);
|
||||
} else {
|
||||
return new TypedActionResult<>(ActionResult.FAIL, itemStack);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEnchantmentGlint(ItemStack stack) {
|
||||
Map<String, Integer> spells = SpellTag.getSpells(stack);
|
||||
return spells.size() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendTooltip(ItemStack itemStack, World world, List<Text> tooltip, TooltipContext tooltipContext) {
|
||||
Map<String, Integer> spells = SpellTag.getSpells(itemStack);
|
||||
for (Map.Entry<String, Integer> entry : spells.entrySet()) {
|
||||
tooltip.add(SpellTag.getTranslatedSpell(entry.getKey(), entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendStacks(ItemGroup group, DefaultedList<ItemStack> stacks) {
|
||||
if (isIn(group)) {
|
||||
stacks.add(new ItemStack(this));
|
||||
Spell[] spells = SpellRegistry.getSpells();
|
||||
for (Spell value : spells) {
|
||||
ItemStack item = new ItemStack(this);
|
||||
Map<String, Integer> spell = new HashMap<>();
|
||||
spell.put(value.getID(), value.getLevel());
|
||||
SpellTag.setSpells(item, spell);
|
||||
stacks.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnchantable(ItemStack stack) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inventoryTick(ItemStack stack, World world, Entity entity, int slot, boolean selected) {
|
||||
super.inventoryTick(stack, world, entity, slot, selected);
|
||||
if (!world.isClient() && entity instanceof PlayerEntity) {
|
||||
PlayerEntity player = (PlayerEntity) entity;
|
||||
SpellPlayerEntity spellPlayer = (SpellPlayerEntity) player;
|
||||
|
||||
Map<String, Integer> playerSpells = spellPlayer.getSpells(); |