commit
6052e00429
47 changed files with 2207 additions and 0 deletions
@ -0,0 +1,25 @@
|
||||
# gradle |
||||
|
||||
.gradle/ |
||||
build/ |
||||
out/ |
||||
classes/ |
||||
|
||||
# idea |
||||
|
||||
.idea/ |
||||
*.iml |
||||
*.ipr |
||||
*.iws |
||||
|
||||
# vscode |
||||
|
||||
.settings/ |
||||
.vscode/ |
||||
bin/ |
||||
.classpath |
||||
.project |
||||
|
||||
# fabric |
||||
|
||||
run/ |
@ -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 |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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. |
@ -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. |
@ -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() |
||||
} |
||||
} |
@ -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 |
Binary file not shown.
@ -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 |
@ -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" "$@" |
@ -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 |
@ -0,0 +1,10 @@
|
||||
pluginManagement { |
||||
repositories { |
||||
jcenter() |
||||
maven { |
||||
name = 'Fabric' |
||||
url = 'https://maven.fabricmc.net/' |
||||
} |
||||
gradlePluginPortal() |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
@ -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(); |
||||
Map<String, Integer> itemSpells = SpellTag.getSpells(player.inventory.getInvStack(slot)); |
||||
|
||||
boolean changed = false; |
||||
|
||||
for (Map.Entry<String, Integer> entry : itemSpells.entrySet()) { |
||||
Spell spell = SpellRegistry.getSpell(entry.getKey(), entry.getValue()); |
||||
if (spell != null) { |
||||
if (spell.getLevel() >= spell.getMaxLevel()) { |
||||
spell = SpellRegistry.getSpell(entry.getKey(), spell.getMaxLevel() - 1); |
||||