feat: working block implementation

This commit is contained in:
Andrew 2026-04-06 02:03:46 +07:00
parent 23981fb9ca
commit 802bbb6c7b
31 changed files with 1580 additions and 51 deletions

2
.gitignore vendored
View file

@ -19,3 +19,5 @@ hs_err_pid*
# Common working directory
run
/src/generated/resources/.cache/
/repo/

View file

@ -0,0 +1,71 @@
kotlin version: 2.0.0
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 1. The daemon process output:
... (53 more lines)
54. at java.base/sun.nio.ch.Net.bind0(Native Method)
55. at java.base/sun.nio.ch.Net.bind(Net.java:555)
56. at java.base/sun.nio.ch.Net.bind(Net.java:544)
57. at java.base/sun.nio.ch.NioSocketImpl.bind(NioSocketImpl.java:640)
58. at java.base/java.net.ServerSocket.bind(ServerSocket.java:392)
59. at java.base/java.net.ServerSocket.<init>(ServerSocket.java:274)
60. at org.jetbrains.kotlin.daemon.common.LoopbackNetworkInterface$ServerLoopbackSocketFactory.createServerSocket(NetworkUtils.kt:69)
61. at java.rmi/sun.rmi.transport.tcp.TCPEndpoint.newServerSocket(TCPEndpoint.java:673)
62. at java.rmi/sun.rmi.transport.tcp.TCPTransport.listen(TCPTransport.java:344)
63. ... 14 more
error message: The daemon has terminated unexpectedly on startup attempt #2 with error code: 1. The daemon process output:
... (53 more lines)
54. at java.base/sun.nio.ch.Net.bind0(Native Method)
55. at java.base/sun.nio.ch.Net.bind(Net.java:555)
56. at java.base/sun.nio.ch.Net.bind(Net.java:544)
57. at java.base/sun.nio.ch.NioSocketImpl.bind(NioSocketImpl.java:640)
58. at java.base/java.net.ServerSocket.bind(ServerSocket.java:392)
59. at java.base/java.net.ServerSocket.<init>(ServerSocket.java:274)
60. at org.jetbrains.kotlin.daemon.common.LoopbackNetworkInterface$ServerLoopbackSocketFactory.createServerSocket(NetworkUtils.kt:69)
61. at java.rmi/sun.rmi.transport.tcp.TCPEndpoint.newServerSocket(TCPEndpoint.java:673)
62. at java.rmi/sun.rmi.transport.tcp.TCPTransport.listen(TCPTransport.java:344)
63. ... 14 more
error message: Failed connecting to the daemon in 3 retries
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:187)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:120)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:162)
at org.gradle.internal.Factories$1.create(Factories.java:31)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:264)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:128)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:133)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:157)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:126)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833)

View file

@ -0,0 +1,71 @@
kotlin version: 2.0.0
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 1. The daemon process output:
... (53 more lines)
54. at java.base/sun.nio.ch.Net.bind0(Native Method)
55. at java.base/sun.nio.ch.Net.bind(Net.java:565)
56. at java.base/sun.nio.ch.Net.bind(Net.java:554)
57. at java.base/sun.nio.ch.NioSocketImpl.bind(NioSocketImpl.java:636)
58. at java.base/java.net.ServerSocket.bind(ServerSocket.java:391)
59. at java.base/java.net.ServerSocket.<init>(ServerSocket.java:278)
60. at org.jetbrains.kotlin.daemon.common.LoopbackNetworkInterface$ServerLoopbackSocketFactory.createServerSocket(NetworkUtils.kt:69)
61. at java.rmi/sun.rmi.transport.tcp.TCPEndpoint.newServerSocket(TCPEndpoint.java:672)
62. at java.rmi/sun.rmi.transport.tcp.TCPTransport.listen(TCPTransport.java:344)
63. ... 14 more
error message: The daemon has terminated unexpectedly on startup attempt #2 with error code: 1. The daemon process output:
... (53 more lines)
54. at java.base/sun.nio.ch.Net.bind0(Native Method)
55. at java.base/sun.nio.ch.Net.bind(Net.java:565)
56. at java.base/sun.nio.ch.Net.bind(Net.java:554)
57. at java.base/sun.nio.ch.NioSocketImpl.bind(NioSocketImpl.java:636)
58. at java.base/java.net.ServerSocket.bind(ServerSocket.java:391)
59. at java.base/java.net.ServerSocket.<init>(ServerSocket.java:278)
60. at org.jetbrains.kotlin.daemon.common.LoopbackNetworkInterface$ServerLoopbackSocketFactory.createServerSocket(NetworkUtils.kt:69)
61. at java.rmi/sun.rmi.transport.tcp.TCPEndpoint.newServerSocket(TCPEndpoint.java:672)
62. at java.rmi/sun.rmi.transport.tcp.TCPTransport.listen(TCPTransport.java:344)
63. ... 14 more
error message: Failed connecting to the daemon in 3 retries
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:187)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:120)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:162)
at org.gradle.internal.Factories$1.create(Factories.java:31)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:264)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:128)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:133)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:157)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:126)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)

View file

@ -0,0 +1,71 @@
kotlin version: 2.0.0
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 1. The daemon process output:
... (53 more lines)
54. at java.base/sun.nio.ch.Net.bind0(Native Method)
55. at java.base/sun.nio.ch.Net.bind(Net.java:565)
56. at java.base/sun.nio.ch.Net.bind(Net.java:554)
57. at java.base/sun.nio.ch.NioSocketImpl.bind(NioSocketImpl.java:636)
58. at java.base/java.net.ServerSocket.bind(ServerSocket.java:391)
59. at java.base/java.net.ServerSocket.<init>(ServerSocket.java:278)
60. at org.jetbrains.kotlin.daemon.common.LoopbackNetworkInterface$ServerLoopbackSocketFactory.createServerSocket(NetworkUtils.kt:69)
61. at java.rmi/sun.rmi.transport.tcp.TCPEndpoint.newServerSocket(TCPEndpoint.java:672)
62. at java.rmi/sun.rmi.transport.tcp.TCPTransport.listen(TCPTransport.java:344)
63. ... 14 more
error message: The daemon has terminated unexpectedly on startup attempt #2 with error code: 1. The daemon process output:
... (53 more lines)
54. at java.base/sun.nio.ch.Net.bind0(Native Method)
55. at java.base/sun.nio.ch.Net.bind(Net.java:565)
56. at java.base/sun.nio.ch.Net.bind(Net.java:554)
57. at java.base/sun.nio.ch.NioSocketImpl.bind(NioSocketImpl.java:636)
58. at java.base/java.net.ServerSocket.bind(ServerSocket.java:391)
59. at java.base/java.net.ServerSocket.<init>(ServerSocket.java:278)
60. at org.jetbrains.kotlin.daemon.common.LoopbackNetworkInterface$ServerLoopbackSocketFactory.createServerSocket(NetworkUtils.kt:69)
61. at java.rmi/sun.rmi.transport.tcp.TCPEndpoint.newServerSocket(TCPEndpoint.java:672)
62. at java.rmi/sun.rmi.transport.tcp.TCPTransport.listen(TCPTransport.java:344)
63. ... 14 more
error message: Failed connecting to the daemon in 3 retries
error message: Daemon compilation failed: Could not connect to Kotlin compile daemon
java.lang.RuntimeException: Could not connect to Kotlin compile daemon
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:214)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:209)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:204)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:66)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:166)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:59)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:53)
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:187)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:120)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:162)
at org.gradle.internal.Factories$1.create(Factories.java:31)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:264)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:128)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:133)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:157)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:126)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:317)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:47)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
at java.base/java.lang.Thread.run(Thread.java:1583)

104
EnchantmentTransfer.ipr Normal file
View file

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<option name="DEFAULT_COMPILER" value="Javac"/>
<resourceExtensions>
<entry name=".+\.(properties|xml|html|dtd|tld)"/>
<entry name=".+\.(gif|png|jpeg|jpg)"/>
</resourceExtensions>
<wildcardResourcePatterns>
<entry name="!?*.class"/>
<entry name="!?*.scala"/>
<entry name="!?*.groovy"/>
<entry name="!?*.java"/>
</wildcardResourcePatterns>
<annotationProcessing enabled="false" useClasspath="true"/>
<bytecodeTargetLevel target="21"/>
</component>
<component name="CopyrightManager" default="">
<module2copyright/>
</component>
<component name="DependencyValidationManager">
<option name="SKIP_IMPORT_STATEMENTS" value="false"/>
</component>
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false"/>
<component name="GradleUISettings">
<setting name="root"/>
</component>
<component name="GradleUISettings2">
<setting name="root"/>
</component>
<component name="IdProvider" IDEtalkID="11DA1DB66DD62DDA1ED602B7079FE97C"/>
<component name="JavadocGenerationManager">
<option name="OUTPUT_DIRECTORY"/>
<option name="OPTION_SCOPE" value="protected"/>
<option name="OPTION_HIERARCHY" value="true"/>
<option name="OPTION_NAVIGATOR" value="true"/>
<option name="OPTION_INDEX" value="true"/>
<option name="OPTION_SEPARATE_INDEX" value="true"/>
<option name="OPTION_DOCUMENT_TAG_USE" value="false"/>
<option name="OPTION_DOCUMENT_TAG_AUTHOR" value="false"/>
<option name="OPTION_DOCUMENT_TAG_VERSION" value="false"/>
<option name="OPTION_DOCUMENT_TAG_DEPRECATED" value="true"/>
<option name="OPTION_DEPRECATED_LIST" value="true"/>
<option name="OTHER_OPTIONS" value=""/>
<option name="HEAP_SIZE"/>
<option name="LOCALE"/>
<option name="OPEN_IN_BROWSER" value="true"/>
</component>
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/EnchantmentTransfer.iml" filepath="$PROJECT_DIR$/EnchantmentTransfer.iml"/>
</modules>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" assert-keyword="true" jdk-15="true" project-jdk-type="JavaSDK" assert-jdk-15="true" project-jdk-name="21">
<output url="file://$PROJECT_DIR$/out"/>
</component>
<component name="SvnBranchConfigurationManager">
<option name="mySupportsUserInfoFilter" value="true"/>
</component>
<component name="VcsDirectoryMappings">
<mapping directory="" vcs=""/>
</component>
<component name="masterDetails">
<states>
<state key="ArtifactsStructureConfigurable.UI">
<UIState>
<splitter-proportions>
<SplitterProportionsDataImpl/>
</splitter-proportions>
<settings/>
</UIState>
</state>
<state key="Copyright.UI">
<UIState>
<splitter-proportions>
<SplitterProportionsDataImpl/>
</splitter-proportions>
</UIState>
</state>
<state key="ProjectJDKs.UI">
<UIState>
<splitter-proportions>
<SplitterProportionsDataImpl>
<option name="proportions">
<list>
<option value="0.2"/>
</list>
</option>
</SplitterProportionsDataImpl>
</splitter-proportions>
<last-edited>1.6</last-edited>
</UIState>
</state>
<state key="ScopeChooserConfigurable.UI">
<UIState>
<splitter-proportions>
<SplitterProportionsDataImpl/>
</splitter-proportions>
<settings/>
</UIState>
</state>
</states>
</component>
</project>

207
EnchantmentTransfer.iws Normal file
View file

@ -0,0 +1,207 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<option name="TRACKING_ENABLED" value="true"/>
<option name="SHOW_DIALOG" value="false"/>
<option name="HIGHLIGHT_CONFLICTS" value="true"/>
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false"/>
<option name="LAST_RESOLUTION" value="IGNORE"/>
</component>
<component name="ChangesViewManager" flattened_view="true" show_ignored="false"/>
<component name="CreatePatchCommitExecutor">
<option name="PATCH_PATH" value=""/>
<option name="REVERSE_PATCH" value="false"/>
</component>
<component name="DaemonCodeAnalyzer">
<disable_hints/>
</component>
<component name="DebuggerManager">
<breakpoint_any>
<breakpoint>
<option name="NOTIFY_CAUGHT" value="true"/>
<option name="NOTIFY_UNCAUGHT" value="true"/>
<option name="ENABLED" value="false"/>
<option name="LOG_ENABLED" value="false"/>
<option name="LOG_EXPRESSION_ENABLED" value="false"/>
<option name="SUSPEND_POLICY" value="SuspendAll"/>
<option name="COUNT_FILTER_ENABLED" value="false"/>
<option name="COUNT_FILTER" value="0"/>
<option name="CONDITION_ENABLED" value="false"/>
<option name="CLASS_FILTERS_ENABLED" value="false"/>
<option name="INSTANCE_FILTERS_ENABLED" value="false"/>
<option name="CONDITION" value=""/>
<option name="LOG_MESSAGE" value=""/>
</breakpoint>
<breakpoint>
<option name="NOTIFY_CAUGHT" value="true"/>
<option name="NOTIFY_UNCAUGHT" value="true"/>
<option name="ENABLED" value="false"/>
<option name="LOG_ENABLED" value="false"/>
<option name="LOG_EXPRESSION_ENABLED" value="false"/>
<option name="SUSPEND_POLICY" value="SuspendAll"/>
<option name="COUNT_FILTER_ENABLED" value="false"/>
<option name="COUNT_FILTER" value="0"/>
<option name="CONDITION_ENABLED" value="false"/>
<option name="CLASS_FILTERS_ENABLED" value="false"/>
<option name="INSTANCE_FILTERS_ENABLED" value="false"/>
<option name="CONDITION" value=""/>
<option name="LOG_MESSAGE" value=""/>
</breakpoint>
</breakpoint_any>
<breakpoint_rules/>
<ui_properties/>
</component>
<component name="ModuleEditorState">
<option name="LAST_EDITED_MODULE_NAME"/>
<option name="LAST_EDITED_TAB_NAME"/>
</component>
<component name="ProjectInspectionProfilesVisibleTreeState">
<entry key="Project Default">
<profile-state/>
</entry>
</component>
<component name="ProjectLevelVcsManager">
<OptionsSetting value="true" id="Add"/>
<OptionsSetting value="true" id="Remove"/>
<OptionsSetting value="true" id="Checkout"/>
<OptionsSetting value="true" id="Update"/>
<OptionsSetting value="true" id="Status"/>
<OptionsSetting value="true" id="Edit"/>
<ConfirmationsSetting value="0" id="Add"/>
<ConfirmationsSetting value="0" id="Remove"/>
</component>
<component name="ProjectReloadState">
<option name="STATE" value="0"/>
</component>
<component name="PropertiesComponent">
<property name="GoToFile.includeJavaFiles" value="false"/>
<property name="GoToClass.toSaveIncludeLibraries" value="false"/>
<property name="MemberChooser.sorted" value="false"/>
<property name="MemberChooser.showClasses" value="true"/>
<property name="GoToClass.includeLibraries" value="false"/>
<property name="MemberChooser.copyJavadoc" value="false"/>
</component>
<component name="RunManager">
<configuration default="true" type="Remote" factoryName="Remote">
<option name="USE_SOCKET_TRANSPORT" value="true"/>
<option name="SERVER_MODE" value="false"/>
<option name="SHMEM_ADDRESS" value="javadebug"/>
<option name="HOST" value="localhost"/>
<option name="PORT" value="5005"/>
<method>
<option name="BuildArtifacts" enabled="false"/>
</method>
</configuration>
<configuration default="true" type="Applet" factoryName="Applet">
<module name=""/>
<option name="MAIN_CLASS_NAME"/>
<option name="HTML_FILE_NAME"/>
<option name="HTML_USED" value="false"/>
<option name="WIDTH" value="400"/>
<option name="HEIGHT" value="300"/>
<option name="POLICY_FILE" value="$APPLICATION_HOME_DIR$/bin/appletviewer.policy"/>
<option name="VM_PARAMETERS"/>
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false"/>
<option name="ALTERNATIVE_JRE_PATH"/>
<method>
<option name="BuildArtifacts" enabled="false"/>
<option name="Make" enabled="true"/>
</method>
</configuration>
<configuration default="true" type="Application" factoryName="Application">
<extension name="coverage" enabled="false" merge="false"/>
<option name="MAIN_CLASS_NAME"/>
<option name="VM_PARAMETERS"/>
<option name="PROGRAM_PARAMETERS"/>
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$"/>
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false"/>
<option name="ALTERNATIVE_JRE_PATH"/>
<option name="ENABLE_SWING_INSPECTOR" value="false"/>
<option name="ENV_VARIABLES"/>
<option name="PASS_PARENT_ENVS" value="true"/>
<module name=""/>
<envs/>
<method>
<option name="BuildArtifacts" enabled="false"/>
<option name="Make" enabled="true"/>
</method>
</configuration>
<configuration default="true" type="JUnit" factoryName="JUnit">
<extension name="coverage" enabled="false" merge="false"/>
<module name=""/>
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false"/>
<option name="ALTERNATIVE_JRE_PATH"/>
<option name="PACKAGE_NAME"/>
<option name="MAIN_CLASS_NAME"/>
<option name="METHOD_NAME"/>
<option name="TEST_OBJECT" value="class"/>
<option name="VM_PARAMETERS"/>
<option name="PARAMETERS"/>
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$"/>
<option name="ENV_VARIABLES"/>
<option name="PASS_PARENT_ENVS" value="true"/>
<option name="TEST_SEARCH_SCOPE">
<value defaultName="moduleWithDependencies"/>
</option>
<envs/>
<method>
<option name="BuildArtifacts" enabled="false"/>
<option name="Make" enabled="true"/>
</method>
</configuration>
<list size="0"/>
<configuration name="&lt;template&gt;" type="WebApp" default="true" selected="false">
<Host>localhost</Host>
<Port>5050</Port>
</configuration>
</component>
<component name="ShelveChangesManager" show_recycled="false"/>
<component name="SvnConfiguration" maxAnnotateRevisions="500">
<option name="USER" value=""/>
<option name="PASSWORD" value=""/>
<option name="LAST_MERGED_REVISION"/>
<option name="UPDATE_RUN_STATUS" value="false"/>
<option name="MERGE_DRY_RUN" value="false"/>
<option name="MERGE_DIFF_USE_ANCESTRY" value="true"/>
<option name="UPDATE_LOCK_ON_DEMAND" value="false"/>
<option name="IGNORE_SPACES_IN_MERGE" value="false"/>
<option name="DETECT_NESTED_COPIES" value="true"/>
<option name="IGNORE_SPACES_IN_ANNOTATE" value="true"/>
<option name="SHOW_MERGE_SOURCES_IN_ANNOTATE" value="true"/>
<myIsUseDefaultProxy>false</myIsUseDefaultProxy>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task"/>
<servers/>
</component>
<component name="VcsManagerConfiguration">
<option name="OFFER_MOVE_TO_ANOTHER_CHANGELIST_ON_PARTIAL_COMMIT" value="true"/>
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="true"/>
<option name="PERFORM_UPDATE_IN_BACKGROUND" value="true"/>
<option name="PERFORM_COMMIT_IN_BACKGROUND" value="true"/>
<option name="PERFORM_EDIT_IN_BACKGROUND" value="true"/>
<option name="PERFORM_CHECKOUT_IN_BACKGROUND" value="true"/>
<option name="PERFORM_ADD_REMOVE_IN_BACKGROUND" value="true"/>
<option name="PERFORM_ROLLBACK_IN_BACKGROUND" value="false"/>
<option name="CHECK_LOCALLY_CHANGED_CONFLICTS_IN_BACKGROUND" value="false"/>
<option name="ENABLE_BACKGROUND_PROCESSES" value="false"/>
<option name="CHANGED_ON_SERVER_INTERVAL" value="60"/>
<option name="FORCE_NON_EMPTY_COMMENT" value="false"/>
<option name="LAST_COMMIT_MESSAGE"/>
<option name="MAKE_NEW_CHANGELIST_ACTIVE" value="true"/>
<option name="OPTIMIZE_IMPORTS_BEFORE_PROJECT_COMMIT" value="false"/>
<option name="CHECK_FILES_UP_TO_DATE_BEFORE_COMMIT" value="false"/>
<option name="REFORMAT_BEFORE_PROJECT_COMMIT" value="false"/>
<option name="REFORMAT_BEFORE_FILE_COMMIT" value="false"/>
<option name="FILE_HISTORY_DIALOG_COMMENTS_SPLITTER_PROPORTION" value="0.8"/>
<option name="FILE_HISTORY_DIALOG_SPLITTER_PROPORTION" value="0.5"/>
<option name="ACTIVE_VCS_NAME"/>
<option name="UPDATE_GROUP_BY_PACKAGES" value="false"/>
<option name="UPDATE_GROUP_BY_CHANGELIST" value="false"/>
<option name="SHOW_FILE_HISTORY_AS_TREE" value="false"/>
<option name="FILE_HISTORY_SPLITTER_PROPORTION" value="0.6"/>
</component>
<component name="XDebuggerManager">
<breakpoint-manager/>
</component>
</project>

51
README.en.md Normal file
View file

@ -0,0 +1,51 @@
[Русская версия](README.md)
# Enchantment Transfer Mod
A mod for Minecraft 1.21.1 that adds a machine for transferring enchantments between items.
## Features
- **Enchantment Transfer**: Transfers enchantments from one item to another (as well as to books)
- **Charge System**: Machine requires energy in the form of items
- **Redstone Control**: Only works with redstone signal
- **Processing Progress**: Processing time depends on amount and level of enchantments
## Accepted Fuel Sources
| Item | Charge |
|-----------------|-------|
| Redstone | 16 |
| Copper Ingot | 1 |
| Iron Ingot | 4 |
| Gold Ingot | 2 |
| Diamond | 32 |
| Netherite Ingot | 64 |
Maximum charge capacity: 1 000 000 units
## How to Use
1. Place the **Enchantment Transferrer** block in the world
2. Put an enchanted item, target item and, optionally, a **book**
3. Put **fuel items** in the fuel slot
4. Provide a **redstone signal** to activate
5. Wait for the process to complete
## Installation
1. Install NeoForge 21.1.219 for Minecraft 1.21.1
2. Place the `.jar` mod file in the `mods` folder
3. Launch the game
## Building
```bash
gradlew.bat build
```
The built file will be located in `build/libs/`
## 3rd-party Assets
UI and block textures were taken from [Ycarx/artoftecharium](https://github.com/Ycarx/artoftecharium) and altered to suite my needs.

51
README.md Normal file
View file

@ -0,0 +1,51 @@
[English version](README.en.md)
# Мод Enchantment Transfer
Мод для Minecraft 1.21.1, добавляющий машину для переноса зачарований.
## Возможности
- **Перенос зачарований**: Переносит зачарования с одного предмета на другой (в т.ч. - на книги)
- **Система заряда**: Машина требует энергию в виде предметов
- **Редстоун контроль**: Работает только при наличии редстоун сигнала
- **Прогресс обработки**: Время обработки зависит от количества и уровня зачарований
## Принимаемые источники энергии
| Предмет | Заряд |
|----------|-------|
| Редстоун | 16 |
| Медь | 1 |
| Железо | 4 |
| Золото | 2 |
| Алмаз | 32 |
| Незерит | 64 |
Максимальная вместимость заряда: 1 000 000 единиц
## Как использовать
1. Поместите **Enchantment Transferrer** в мир
2. Поместите зачарованный предмет, целевой предмет в **средний слот**, опционально - книгу
3. Поместите **топливо** в слот
4. Подайте **редстоун сигнал** для активации
5. Дождитесь завершения процесса
## Установка
1. Установите NeoForge 21.1.219 для Minecraft 1.21.1
2. Поместите `.jar` файл мода в папку `mods`
3. Запустите игру
## Сборка
```bash
gradlew.bat build
```
Собранный файл будет находиться в `build/libs/`
## Чужие ассеты
Текстуры для UI и блоков были взяты из [Ycarx/artoftecharium](https://github.com/Ycarx/artoftecharium) из изменены по потребности

View file

@ -3,7 +3,7 @@ plugins {
id 'maven-publish'
id 'idea'
id 'net.neoforged.moddev' version '2.0.141'
id 'org.jetbrains.kotlin.jvm' version '2.0.0'
id 'org.jetbrains.kotlin.jvm' version '2.3.0'
}
version = mod_version
@ -103,7 +103,7 @@ sourceSets.main.resources { srcDir 'src/generated/resources' }
dependencies {
implementation 'thedarkcolour:kotlinforforge-neoforge:5.3.0'
implementation 'thedarkcolour:kotlinforforge-neoforge:5.11.0'
// Example mod dependency with JEI
// The JEI API is declared for compile time use, while the full JEI artifact is used at runtime

18
generate_textures.py Normal file
View file

@ -0,0 +1,18 @@
from PIL import Image
import os
textures_dir = "src/main/resources/assets/enct/textures/block"
os.makedirs(textures_dir, exist_ok=True)
colors = {
"enchantment_transferrer_top": (120, 80, 200),
"enchantment_transferrer_bottom": (80, 80, 80),
"enchantment_transferrer_side": (100, 100, 120),
"enchantment_transferrer_front": (140, 100, 220),
}
for name, color in colors.items():
img = Image.new('RGBA', (16, 16), color + (255,))
img.save(f"{textures_dir}/{name}.png", 'PNG')
print("Textures generated successfully!")

View file

@ -4,6 +4,7 @@ org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=true
kotlin.compiler.execution.strategy=in-process
## Environment Properties
# You can find the latest versions here: https://projects.neoforged.net/neoforged/neoforge
# The Minecraft version must agree with the Neo version to get a valid artifact
@ -35,6 +36,6 @@ mod_version=1.0.0
# See https://maven.apache.org/guides/mini/guide-naming-conventions.html
mod_group_id=xyz.nuark.mcmod
# The authors of the mod. This is a simple text string that is used for display purposes in the mod list.
mod_authors=
mod_authors=nuark
# The description of the mod. This is a simple multiline text string that is used for display purposes in the mod list.
mod_description=
mod_description=Ever been in a situation, when you have new toy, but now you need to move enchantments from previous one? Say no more!

View file

@ -0,0 +1,32 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_ench_table": {
"conditions": {
"items": [
{
"items": "minecraft:enchanting_table"
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "enct:enchantment_transferrer"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_the_recipe",
"has_ench_table"
]
],
"rewards": {
"recipes": [
"enct:enchantment_transferrer"
]
}
}

View file

@ -0,0 +1,24 @@
{
"type": "minecraft:crafting_shaped",
"category": "redstone",
"key": {
"B": {
"item": "minecraft:blaze_rod"
},
"E": {
"item": "minecraft:enchanting_table"
},
"I": {
"item": "minecraft:iron_block"
}
},
"pattern": [
"III",
"BEB",
"III"
],
"result": {
"count": 1,
"id": "enct:enchantment_transferrer"
}
}

View file

@ -1,63 +1,83 @@
package xyz.nuark.mcmod.enct
import xyz.nuark.mcmod.enct.block.ModBlocks
import net.minecraft.client.Minecraft
import net.minecraft.core.registries.Registries
import net.minecraft.world.flag.FeatureFlags
import net.minecraft.world.inventory.MenuType
import net.minecraft.world.item.CreativeModeTabs
import net.neoforged.bus.api.SubscribeEvent
import net.neoforged.fml.common.EventBusSubscriber
import net.neoforged.fml.common.Mod
import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent
import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent
import net.neoforged.fml.event.lifecycle.FMLDedicatedServerSetupEvent
import net.neoforged.neoforge.capabilities.Capabilities
import net.neoforged.neoforge.client.event.RegisterMenuScreensEvent
import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent
import net.neoforged.neoforge.network.IContainerFactory
import net.neoforged.neoforge.registries.DeferredHolder
import net.neoforged.neoforge.registries.DeferredRegister
import org.apache.logging.log4j.Level
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import thedarkcolour.kotlinforforge.neoforge.forge.MOD_BUS
import thedarkcolour.kotlinforforge.neoforge.forge.runForDist
import xyz.nuark.mcmod.enct.block.ModBlocks
import xyz.nuark.mcmod.enct.block.entity.EnchantmentTransferrerBlockEntity
import xyz.nuark.mcmod.enct.menu.EnchantmentTransferrerMenu
import xyz.nuark.mcmod.enct.screen.EnchantmentTransferrerScreen
import java.util.function.Supplier
/**
* Main mod class.
*
* An example for blocks is in the `blocks` package of this mod.
*/
@Mod(Enchantmenttransfer.ID)
@EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD)
@EventBusSubscriber
object Enchantmenttransfer {
const val ID = "enct"
// the logger for our mod
val LOGGER: Logger = LogManager.getLogger(ID)
val LOGGER = LogManager.getLogger(ID)
val MENU_TYPES: DeferredRegister<MenuType<*>> = DeferredRegister.create(Registries.MENU, ID)
@Suppress("UNCHECKED_CAST")
val ENCHANTMENT_TRANSFERRER_MENU_TYPE: DeferredHolder<MenuType<*>, MenuType<EnchantmentTransferrerMenu>> = MENU_TYPES.register("enchantment_transferrer", Supplier {
MenuType<EnchantmentTransferrerMenu>(
IContainerFactory { containerId, playerInventory, buf ->
val pos = buf.readBlockPos()
val level = playerInventory.player.level()
val blockEntity = level.getBlockEntity(pos) as? EnchantmentTransferrerBlockEntity
if (blockEntity != null) {
EnchantmentTransferrerMenu(containerId, playerInventory, blockEntity, blockEntity.dataAccess)
} else {
throw IllegalStateException("BlockEntity not found at $pos")
}
},
FeatureFlags.VANILLA_SET
)
})
init {
LOGGER.log(Level.INFO, "Hello world!")
ModBlocks.register(MOD_BUS)
MENU_TYPES.register(MOD_BUS)
// Register the KDeferredRegister to the mod-specific event bus
ModBlocks.REGISTRY.register(MOD_BUS)
val obj = runForDist(clientTarget = {
MOD_BUS.addListener(::onClientSetup)
Minecraft.getInstance()
}, serverTarget = {
MOD_BUS.addListener(::onServerSetup)
"test"
})
println(obj)
MOD_BUS.addListener<net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent> { event ->
event.registerBlockEntity(
Capabilities.ItemHandler.BLOCK,
ModBlocks.ENCHANTMENT_TRANSFERRER_BE.value(),
{ be, side -> be.getHandlerForSide(side) }
)
}
}
/**
* This is used for initializing client specific
* things such as renderers and keymaps
* Fired on the mod specific event bus.
*/
private fun onClientSetup(event: FMLClientSetupEvent) {
@SubscribeEvent
fun onBuildCreativeTabContents(event: BuildCreativeModeTabContentsEvent) {
if (event.tabKey == CreativeModeTabs.REDSTONE_BLOCKS) {
event.accept(ModBlocks.ENCHANTMENT_TRANSFERRER_ITEM.get())
}
}
@SubscribeEvent
fun onClientSetup(event: FMLClientSetupEvent) {
LOGGER.log(Level.INFO, "Initializing client...")
}
/**
* Fired on the global Forge bus.
*/
private fun onServerSetup(event: FMLDedicatedServerSetupEvent) {
LOGGER.log(Level.INFO, "Server starting...")
@SubscribeEvent
fun onRegisterMenuScreens(event: RegisterMenuScreensEvent) {
event.register(ENCHANTMENT_TRANSFERRER_MENU_TYPE.value(), ::EnchantmentTransferrerScreen)
}
@SubscribeEvent

View file

@ -0,0 +1,108 @@
package xyz.nuark.mcmod.enct.block
import com.mojang.serialization.MapCodec
import xyz.nuark.mcmod.enct.block.entity.EnchantmentTransferrerBlockEntity
import net.minecraft.core.BlockPos
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.BaseEntityBlock
import net.minecraft.world.level.block.RenderShape
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityTicker
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.phys.BlockHitResult
import net.neoforged.neoforge.capabilities.Capabilities
import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent
import org.jetbrains.annotations.Nullable
class EnchantmentTransferrerBlock(properties: Properties) : BaseEntityBlock(properties) {
companion object {
val CODEC: MapCodec<EnchantmentTransferrerBlock> = MapCodec.unit { EnchantmentTransferrerBlock(Properties.of()) }
fun registerCapabilities(event: RegisterCapabilitiesEvent) {
event.registerBlockEntity(
Capabilities.ItemHandler.BLOCK,
ModBlocks.ENCHANTMENT_TRANSFERRER_BE.value(),
) { be, side ->
be.getHandlerForSide(side)
}
}
}
override fun codec(): MapCodec<out EnchantmentTransferrerBlock> = CODEC
@Nullable
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity? {
return EnchantmentTransferrerBlockEntity(pos, state)
}
override fun useWithoutItem(
state: BlockState,
level: Level,
pos: BlockPos,
player: Player,
hitResult: BlockHitResult
): InteractionResult {
if (level.isClientSide) {
return InteractionResult.SUCCESS
}
val blockEntity = level.getBlockEntity(pos)
if (blockEntity is EnchantmentTransferrerBlockEntity) {
player.openMenu(blockEntity) { buf ->
buf.writeBlockPos(pos)
}
}
return InteractionResult.CONSUME
}
override fun setPlacedBy(level: Level, pos: BlockPos, state: BlockState, @Nullable placer: LivingEntity?, stack: ItemStack) {
super.setPlacedBy(level, pos, state, placer, stack)
if (level.isClientSide) return
val blockEntity = level.getBlockEntity(pos)
// TODO: Maybe link machine to player? XP usage instead of fuel :thinking:
}
override fun onRemove(state: BlockState, level: Level, pos: BlockPos, newState: BlockState, isMoving: Boolean) {
if (state.block !== newState.block) {
val blockEntity = level.getBlockEntity(pos)
if (blockEntity is EnchantmentTransferrerBlockEntity) {
for (i in 0 until EnchantmentTransferrerBlockEntity.TOTAL_SLOTS) {
val stack = blockEntity.getItemHandler().getStackInSlot(i)
if (!stack.isEmpty) {
popResource(level, pos, stack)
}
}
}
super.onRemove(state, level, pos, newState, isMoving)
}
}
override fun getRenderShape(state: BlockState): RenderShape {
return RenderShape.MODEL
}
@Nullable
override fun <T : BlockEntity> getTicker(
level: Level,
state: BlockState,
type: BlockEntityType<T>
): BlockEntityTicker<T>? {
if (level.isClientSide) return null
val blockEntityType = ModBlocks.ENCHANTMENT_TRANSFERRER_BE.value()
if (type !== blockEntityType) return null
@Suppress("UNCHECKED_CAST")
return BlockEntityTicker<T> { l, pos, s, be ->
EnchantmentTransferrerBlockEntity.serverTick(l, pos, s, be as EnchantmentTransferrerBlockEntity)
}
}
}

View file

@ -1,18 +1,41 @@
package xyz.nuark.mcmod.enct.block
import xyz.nuark.mcmod.enct.Enchantmenttransfer
import xyz.nuark.mcmod.enct.block.entity.EnchantmentTransferrerBlockEntity
import net.minecraft.core.registries.Registries
import net.minecraft.world.item.BlockItem
import net.minecraft.world.item.Item
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.entity.BlockEntityType
import net.minecraft.world.level.block.state.BlockBehaviour
import net.neoforged.bus.api.IEventBus
import net.neoforged.neoforge.registries.DeferredRegister
// THIS LINE IS REQUIRED FOR USING PROPERTY DELEGATES
import thedarkcolour.kotlinforforge.neoforge.forge.getValue
import net.neoforged.neoforge.registries.DeferredHolder
import java.util.function.Supplier
object ModBlocks {
val REGISTRY = DeferredRegister.createBlocks(Enchantmenttransfer.ID)
val BLOCKS = DeferredRegister.create(Registries.BLOCK, Enchantmenttransfer.ID)
val ITEMS = DeferredRegister.create(Registries.ITEM, Enchantmenttransfer.ID)
val BLOCK_ENTITIES = DeferredRegister.create(Registries.BLOCK_ENTITY_TYPE, Enchantmenttransfer.ID)
// If you get an "overload resolution ambiguity" error, include the arrow at the start of the closure.
val EXAMPLE_BLOCK by REGISTRY.register("example_block") { ->
Block(BlockBehaviour.Properties.of().lightLevel { 15 }.strength(3.0f))
val ENCHANTMENT_TRANSFERRER: DeferredHolder<Block, Block> = BLOCKS.register("enchantment_transferrer", Supplier {
EnchantmentTransferrerBlock(BlockBehaviour.Properties.of().strength(3.0f).requiresCorrectToolForDrops())
})
val ENCHANTMENT_TRANSFERRER_ITEM: DeferredHolder<Item, Item> = ITEMS.register("enchantment_transferrer", Supplier {
BlockItem(ENCHANTMENT_TRANSFERRER.get(), Item.Properties())
})
val ENCHANTMENT_TRANSFERRER_BE: DeferredHolder<BlockEntityType<*>, BlockEntityType<EnchantmentTransferrerBlockEntity>> = BLOCK_ENTITIES.register("enchantment_transferrer", Supplier {
BlockEntityType.Builder.of(
{ pos, state -> EnchantmentTransferrerBlockEntity(pos, state) },
ENCHANTMENT_TRANSFERRER.get()
).build(null)
})
fun register(eventBus: IEventBus) {
BLOCKS.register(eventBus)
ITEMS.register(eventBus)
BLOCK_ENTITIES.register(eventBus)
}
}

View file

@ -0,0 +1,339 @@
package xyz.nuark.mcmod.enct.block.entity
import xyz.nuark.mcmod.enct.Enchantmenttransfer
import xyz.nuark.mcmod.enct.block.ModBlocks
import xyz.nuark.mcmod.enct.menu.EnchantmentTransferrerMenu
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.HolderLookup
import net.minecraft.core.component.DataComponents
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.chat.Component
import net.minecraft.network.protocol.Packet
import net.minecraft.network.protocol.game.ClientGamePacketListener
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket
import net.minecraft.world.MenuProvider
import net.minecraft.world.Nameable
import net.minecraft.world.WorldlyContainer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.inventory.ContainerData
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.enchantment.EnchantmentHelper
import net.minecraft.world.item.enchantment.ItemEnchantments
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.neoforged.neoforge.items.IItemHandler
import net.neoforged.neoforge.items.ItemStackHandler
import net.neoforged.neoforge.items.wrapper.SidedInvWrapper
import org.jetbrains.annotations.Nullable
class EnchantmentTransferrerBlockEntity(
pos: BlockPos,
state: BlockState
) : BlockEntity(ModBlocks.ENCHANTMENT_TRANSFERRER_BE.value(), pos, state), MenuProvider, Nameable, WorldlyContainer {
companion object {
const val SLOT_MAIN = 0
const val SLOT_BOOK = 1
const val SLOT_TARGET = 2
const val SLOT_FUEL = 3
const val TOTAL_SLOTS = 4
const val MAX_CHARGE = 1_000_000
// Charge values for different items
val CHARGE_VALUES = mapOf(
Items.COPPER_INGOT to 1,
Items.IRON_INGOT to 4,
Items.GOLD_INGOT to 2,
Items.DIAMOND to 32,
Items.NETHERITE_INGOT to 64,
Items.REDSTONE to 16
)
// Base time per enchantment (in ticks)
const val BASE_TIME_PER_ENCHANTMENT = 100
@JvmStatic
fun serverTick(level: Level, pos: BlockPos, state: BlockState, be: EnchantmentTransferrerBlockEntity) {
be.tick()
}
}
private val itemHandler = object : ItemStackHandler(TOTAL_SLOTS) {
override fun isItemValid(slot: Int, stack: ItemStack): Boolean {
return when (slot) {
SLOT_MAIN -> EnchantmentHelper.hasAnyEnchantments(stack)
SLOT_TARGET -> !EnchantmentHelper.hasAnyEnchantments(stack) && EnchantmentHelper.canStoreEnchantments(stack)
SLOT_BOOK -> stack.item == Items.BOOK || stack.item == Items.ENCHANTED_BOOK
SLOT_FUEL -> CHARGE_VALUES.containsKey(stack.item)
else -> false
}
}
}
override fun getSlotsForFace(side: Direction): IntArray {
return when (side) {
Direction.UP -> intArrayOf(SLOT_BOOK)
Direction.DOWN -> intArrayOf()
else -> intArrayOf(SLOT_FUEL)
}
}
override fun canPlaceItemThroughFace(index: Int, itemStack: ItemStack, @Nullable direction: Direction?): Boolean {
return itemHandler.isItemValid(index, itemStack)
}
override fun canTakeItemThroughFace(index: Int, itemStack: ItemStack, direction: Direction): Boolean {
return false
}
override fun getContainerSize(): Int = TOTAL_SLOTS
override fun isEmpty(): Boolean = (0 until TOTAL_SLOTS).all { itemHandler.getStackInSlot(it).isEmpty }
override fun getItem(slot: Int): ItemStack = itemHandler.getStackInSlot(slot)
override fun removeItem(slot: Int, amount: Int): ItemStack {
return itemHandler.extractItem(slot, amount, false)
}
override fun removeItemNoUpdate(slot: Int): ItemStack {
val stack = itemHandler.getStackInSlot(slot)
itemHandler.setStackInSlot(slot, ItemStack.EMPTY)
return stack
}
override fun setItem(slot: Int, stack: ItemStack) {
itemHandler.setStackInSlot(slot, stack)
}
override fun stillValid(player: Player): Boolean = true
override fun clearContent() {
for (i in 0 until TOTAL_SLOTS) {
itemHandler.setStackInSlot(i, ItemStack.EMPTY)
}
}
private val handlerUp = SidedInvWrapper(this, Direction.UP)
private val handlerSides = SidedInvWrapper(this, Direction.NORTH)
private var charge: Int = 0
private var processTime: Int = 0
private var totalTime: Int = 0
private var isProcessing: Boolean = false
private var wasPowered: Boolean = false
// ContainerData for GUI sync
val dataAccess = object : ContainerData {
override fun get(index: Int): Int = when (index) {
0 -> this@EnchantmentTransferrerBlockEntity.charge
1 -> this@EnchantmentTransferrerBlockEntity.processTime
2 -> this@EnchantmentTransferrerBlockEntity.totalTime
3 -> if (this@EnchantmentTransferrerBlockEntity.isProcessing) 1 else 0
else -> 0
}
override fun set(index: Int, value: Int) {
when (index) {
0 -> this@EnchantmentTransferrerBlockEntity.charge = value
1 -> this@EnchantmentTransferrerBlockEntity.processTime = value
2 -> this@EnchantmentTransferrerBlockEntity.totalTime = value
3 -> this@EnchantmentTransferrerBlockEntity.isProcessing = value == 1
}
}
override fun getCount(): Int = 4
}
fun getItemHandler(): IItemHandler = itemHandler
fun getHandlerForSide(side: Direction?): IItemHandler? {
return when (side) {
Direction.UP -> handlerUp
Direction.DOWN -> null
else -> handlerSides
}
}
override fun getName(): Component = Component.translatable("block.${Enchantmenttransfer.ID}.enchantment_transferrer")
override fun getDisplayName(): Component = name
override fun createMenu(containerId: Int, playerInventory: Inventory, player: Player): AbstractContainerMenu {
return EnchantmentTransferrerMenu(containerId, playerInventory, this, dataAccess)
}
override fun saveAdditional(tag: CompoundTag, registries: net.minecraft.core.HolderLookup.Provider) {
super.saveAdditional(tag, registries)
tag.put("items", itemHandler.serializeNBT(registries))
tag.putInt("charge", charge)
tag.putInt("processTime", processTime)
tag.putInt("totalTime", totalTime)
tag.putBoolean("isProcessing", isProcessing)
}
override fun loadAdditional(tag: CompoundTag, registries: HolderLookup.Provider) {
super.loadAdditional(tag, registries)
itemHandler.deserializeNBT(registries, tag.getCompound("items"))
charge = tag.getInt("charge")
processTime = tag.getInt("processTime")
totalTime = tag.getInt("totalTime")
isProcessing = tag.getBoolean("isProcessing")
setChanged()
}
override fun getUpdateTag(registries: HolderLookup.Provider): CompoundTag {
val tag = CompoundTag()
saveAdditional(tag, registries)
return tag
}
override fun getUpdatePacket(): Packet<ClientGamePacketListener>? {
return ClientboundBlockEntityDataPacket.create(this)
}
private fun tick() {
if (level?.isClientSide == true) return
val isPowered = level?.hasNeighborSignal(worldPosition) == true
if (charge < MAX_CHARGE) {
consumeFuel()
}
if (!isPowered) {
if (wasPowered) {
setChanged()
}
wasPowered = false
return
}
wasPowered = true
if (!isProcessing) {
if (canProcess()) {
startProcessing()
}
return
}
if (charge > 0) {
if (!canProcess()) {
processTime = 0
setChanged()
return
}
processTime++
charge--
if (processTime >= totalTime) {
finishProcessing()
}
setChanged()
level?.sendBlockUpdated(worldPosition, blockState, blockState, 3)
}
}
private fun consumeFuel() {
val stack = getItem(SLOT_FUEL)
if (!stack.isEmpty) {
val chargeValue = CHARGE_VALUES[stack.item] ?: 0
if (chargeValue > 0 && charge + chargeValue <= MAX_CHARGE) {
stack.shrink(1)
setItem(SLOT_FUEL, stack)
charge += chargeValue
setChanged()
return
}
}
}
private fun canProcess(): Boolean {
val mainStack = getItem(SLOT_MAIN)
if (mainStack.isEmpty || !EnchantmentHelper.hasAnyEnchantments(mainStack)) return false
val targetStack = getItem(SLOT_TARGET)
val mainEnchantments = EnchantmentHelper.getEnchantmentsForCrafting(mainStack)
if (mainEnchantments.isEmpty) return false
val targetEnchantments = EnchantmentHelper.getEnchantmentsForCrafting(targetStack)
if (!targetEnchantments.isEmpty) return false
val hasValidTarget = !targetStack.isEmpty
return hasValidTarget
}
private fun startProcessing() {
val mainStack = getItem(SLOT_MAIN)
val mainEnchantments = EnchantmentHelper.getEnchantmentsForCrafting(mainStack).entrySet()
var totalLevels = 0
mainEnchantments.forEach { entry ->
totalLevels += entry.intValue
}
val enchantmentCount = mainEnchantments.size
val avgLevel = if (enchantmentCount > 0) totalLevels.toFloat() / enchantmentCount else 1f
totalTime = (enchantmentCount * avgLevel * BASE_TIME_PER_ENCHANTMENT).toInt().coerceAtLeast(100)
processTime = 0
isProcessing = true
setChanged()
}
private fun finishProcessing() {
val mainStack = getItem(SLOT_MAIN)
val targetStack = getItem(SLOT_TARGET)
val bookStack = getItem(SLOT_BOOK)
val originalEnchantments = EnchantmentHelper.getEnchantmentsForCrafting(mainStack).entrySet()
val (possible, impossible) = originalEnchantments.partition { targetStack.supportsEnchantment(it.key) }
val possibleEnchantments = ItemEnchantments.Mutable(ItemEnchantments.EMPTY).apply {
possible.forEach{
set(it.key, it.intValue)
}
}.toImmutable()
val impossibleEnchantments = ItemEnchantments.Mutable(ItemEnchantments.EMPTY).apply {
impossible.forEach{
set(it.key, it.intValue)
}
}.toImmutable()
if (possible.isNotEmpty()) {
EnchantmentHelper.setEnchantments(targetStack, possibleEnchantments)
}
if (bookStack.isEmpty) {
setItem(SLOT_MAIN, mainStack.apply {
set(DataComponents.ENCHANTMENTS, impossibleEnchantments)
})
} else {
setItem(SLOT_MAIN, mainStack.apply {
set(DataComponents.ENCHANTMENTS, ItemEnchantments.EMPTY)
})
setItem(SLOT_BOOK, ItemStack(Items.ENCHANTED_BOOK).apply {
EnchantmentHelper.setEnchantments(this, impossibleEnchantments)
})
}
resetProcessing()
}
private fun resetProcessing() {
processTime = 0
totalTime = 0
isProcessing = false
setChanged()
}
}

View file

@ -0,0 +1,24 @@
package xyz.nuark.mcmod.enct.datagen
import net.minecraft.core.HolderLookup
import net.minecraft.data.DataGenerator
import net.minecraft.data.PackOutput
import net.neoforged.bus.api.SubscribeEvent
import net.neoforged.fml.common.EventBusSubscriber
import net.neoforged.neoforge.data.event.GatherDataEvent
import java.util.concurrent.CompletableFuture
@EventBusSubscriber
object DataGenerators {
@SubscribeEvent
fun gatherData(event: GatherDataEvent) {
val generator: DataGenerator = event.generator
val packOutput: PackOutput = generator.packOutput
val registries: CompletableFuture<HolderLookup.Provider> = event.lookupProvider
if (event.includeServer()) {
generator.addProvider(true, ModRecipeProvider(packOutput, registries))
}
}
}

View file

@ -0,0 +1,29 @@
package xyz.nuark.mcmod.enct.datagen
import xyz.nuark.mcmod.enct.block.ModBlocks
import net.minecraft.core.HolderLookup
import net.minecraft.data.PackOutput
import net.minecraft.data.recipes.RecipeCategory
import net.minecraft.data.recipes.RecipeOutput
import net.minecraft.data.recipes.RecipeProvider
import net.minecraft.data.recipes.ShapedRecipeBuilder
import net.minecraft.world.item.Items
import java.util.concurrent.CompletableFuture
class ModRecipeProvider(
output: PackOutput,
registries: CompletableFuture<HolderLookup.Provider>
) : RecipeProvider(output, registries) {
override fun buildRecipes(recipeOutput: RecipeOutput) {
ShapedRecipeBuilder.shaped(RecipeCategory.REDSTONE, ModBlocks.ENCHANTMENT_TRANSFERRER_ITEM.get())
.define('I', Items.IRON_BLOCK)
.define('B', Items.BLAZE_ROD)
.define('E', Items.ENCHANTING_TABLE)
.pattern("III")
.pattern("BEB")
.pattern("III")
.unlockedBy("has_ench_table", has(Items.ENCHANTING_TABLE))
.save(recipeOutput)
}
}

View file

@ -0,0 +1,108 @@
package xyz.nuark.mcmod.enct.menu
import xyz.nuark.mcmod.enct.Enchantmenttransfer
import xyz.nuark.mcmod.enct.block.entity.EnchantmentTransferrerBlockEntity
import net.minecraft.world.Container
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.inventory.ContainerData
import net.minecraft.world.inventory.SimpleContainerData
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.item.enchantment.EnchantmentHelper
import xyz.nuark.mcmod.enct.block.entity.EnchantmentTransferrerBlockEntity.Companion.CHARGE_VALUES
class EnchantmentTransferrerMenu(
containerId: Int,
private val playerInventory: Inventory,
private val container: Container,
private val data: ContainerData = SimpleContainerData(4)
) : AbstractContainerMenu(Enchantmenttransfer.ENCHANTMENT_TRANSFERRER_MENU_TYPE.value(), containerId) {
init {
checkContainerSize(container, EnchantmentTransferrerBlockEntity.TOTAL_SLOTS)
checkContainerDataCount(data, 4)
// Block inventory slots
addSlot(object : Slot(container, EnchantmentTransferrerBlockEntity.SLOT_MAIN, 35, 23) {
override fun mayPlace(stack: ItemStack): Boolean = EnchantmentHelper.hasAnyEnchantments(stack)
})
addSlot(object : Slot(container, EnchantmentTransferrerBlockEntity.SLOT_BOOK, 65, 23) {
override fun mayPlace(stack: ItemStack): Boolean = stack.item == Items.BOOK || stack.item == Items.ENCHANTED_BOOK
override fun getMaxStackSize(): Int = 1
})
addSlot(object : Slot(container, EnchantmentTransferrerBlockEntity.SLOT_TARGET, 124, 23) {
override fun mayPlace(stack: ItemStack): Boolean = !EnchantmentHelper.hasAnyEnchantments(stack) && EnchantmentHelper.canStoreEnchantments(stack)
})
addSlot(object : Slot(container, EnchantmentTransferrerBlockEntity.SLOT_FUEL, 3, 23) {
override fun mayPlace(stack: ItemStack): Boolean = CHARGE_VALUES.containsKey(stack.item)
})
// Player inventory slots
for (row in 0 until 3) {
for (col in 0 until 9) {
addSlot(Slot(playerInventory, col + row * 9 + 9, 8 + col * 18, 72 + row * 18))
}
}
// Player hotbar
for (col in 0 until 9) {
addSlot(Slot(playerInventory, col, 8 + col * 18, 130))
}
addDataSlots(data)
}
override fun stillValid(player: Player): Boolean {
return container.stillValid(player)
}
override fun quickMoveStack(player: Player, index: Int): ItemStack {
var movedStack = ItemStack.EMPTY
val slot = this.slots[index]
if (slot.hasItem()) {
val slotStack = slot.item
movedStack = slotStack.copy()
if (index < EnchantmentTransferrerBlockEntity.TOTAL_SLOTS) {
if (!this.moveItemStackTo(slotStack, EnchantmentTransferrerBlockEntity.TOTAL_SLOTS, this.slots.size, true)) {
return ItemStack.EMPTY
}
} else {
val item = slotStack.item
if (CHARGE_VALUES.containsKey(item)) {
if (!this.moveItemStackTo(slotStack, EnchantmentTransferrerBlockEntity.SLOT_FUEL, EnchantmentTransferrerBlockEntity.SLOT_FUEL+1, false)) {
return ItemStack.EMPTY
}
} else {
if (!this.moveItemStackTo(slotStack, 0, EnchantmentTransferrerBlockEntity.SLOT_FUEL, false)) {
return ItemStack.EMPTY
}
}
}
if (slotStack.isEmpty) {
slot.setByPlayer(ItemStack.EMPTY)
} else {
slot.setChanged()
}
if (slotStack.count == movedStack.count) {
return ItemStack.EMPTY
}
slot.onTake(player, slotStack)
}
return movedStack
}
fun getCharge(): Int = data.get(0)
fun getProcessTime(): Int = data.get(1)
fun getTotalTime(): Int = data.get(2)
fun isProcessing(): Boolean = data.get(3) == 1
}

View file

@ -0,0 +1,134 @@
package xyz.nuark.mcmod.enct.screen
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.player.Inventory
import xyz.nuark.mcmod.enct.Enchantmenttransfer
import xyz.nuark.mcmod.enct.block.entity.EnchantmentTransferrerBlockEntity
import xyz.nuark.mcmod.enct.menu.EnchantmentTransferrerMenu
class EnchantmentTransferrerScreen(
menu: EnchantmentTransferrerMenu,
playerInventory: Inventory,
title: Component
) : AbstractContainerScreen<EnchantmentTransferrerMenu>(menu, playerInventory, title) {
companion object {
val BACKGROUND_LOCATION: ResourceLocation = ResourceLocation.fromNamespaceAndPath(Enchantmenttransfer.ID, "textures/gui/enchantment_transferrer.png")
val PROGRESS_GAUGE: ResourceLocation = ResourceLocation.fromNamespaceAndPath(Enchantmenttransfer.ID, "textures/gui/ets_gauge.png")
const val GAUGE_WIDTH = 117
const val GAUGE_HEIGHT = 7
}
override fun init() {
super.init()
imageWidth = 176
imageHeight = 154
}
override fun renderBg(guiGraphics: GuiGraphics, partialTick: Float, mouseX: Int, mouseY: Int) {
guiGraphics.blit(BACKGROUND_LOCATION, leftPos, topPos, 0, 0, imageWidth, imageHeight)
// Progress bar
if (menu.isProcessing()) {
val progress = menu.getProcessTime()
val total = menu.getTotalTime()
if (total > 0) {
val progressWidth = ((progress.toDouble() / total) * GAUGE_WIDTH).toInt()
guiGraphics.blit(PROGRESS_GAUGE, leftPos+33, topPos+5, 0f, 0f, progressWidth, GAUGE_HEIGHT, GAUGE_WIDTH, GAUGE_HEIGHT)
}
}
// Charge bar
val charge = menu.getCharge()
val maxCharge = EnchantmentTransferrerBlockEntity.MAX_CHARGE
if (charge > 0) {
val chargeWidth = ((charge.toDouble() / maxCharge) * GAUGE_WIDTH).toInt()
guiGraphics.blit(PROGRESS_GAUGE, leftPos+33, topPos+50, 0f, 0f, chargeWidth, GAUGE_HEIGHT, GAUGE_WIDTH, GAUGE_HEIGHT)
}
}
override fun renderLabels(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int) {
// No labels for me, thanks
}
override fun renderTooltip(guiGraphics: GuiGraphics, x: Int, y: Int) {
val xRange = (leftPos + 33)..(leftPos + 33 + GAUGE_WIDTH)
when (y) {
in (topPos + 5)..(topPos + 5 + GAUGE_HEIGHT) if x in xRange -> { // Over progress bar
val progress = menu.getProcessTime()
val total = menu.getTotalTime()
val processing = menu.isProcessing()
val status = if (total > 0 && processing) {
val perc = ((progress.toDouble() / total) * 100).toInt()
"$perc%"
} else if (total > 0) {
Component.translatable("container.enct.processing.waiting").withColor(0xFFFF00)
} else {
Component.translatable("container.enct.processing.idle").withColor(0x00FFFF)
}
guiGraphics.renderTooltip(
this.font,
Component.translatable("container.enct.processing", status),
x,
y
)
}
in (topPos + 50)..(topPos + 50 + GAUGE_HEIGHT) if x in xRange -> { // Over charge bar
val charge = menu.getCharge()
guiGraphics.renderTooltip(
this.font,
Component.translatable("container.enct.charge", charge.toString()),
x,
y
)
}
else -> {
val hoveredSlot = this.hoveredSlot
if (hoveredSlot != null && !hoveredSlot.hasItem()) { // Showing tooltips for empty slots
when (hoveredSlot.slotIndex) {
EnchantmentTransferrerBlockEntity.SLOT_MAIN -> guiGraphics.renderTooltip(
this.font,
Component.translatable("container.enct.info.main"),
x,
y
)
EnchantmentTransferrerBlockEntity.SLOT_TARGET -> guiGraphics.renderTooltip(
this.font,
Component.translatable("container.enct.info.target"),
x,
y
)
EnchantmentTransferrerBlockEntity.SLOT_BOOK -> guiGraphics.renderTooltip(
this.font,
Component.translatable("container.enct.info.book"),
x,
y
)
EnchantmentTransferrerBlockEntity.SLOT_FUEL -> guiGraphics.renderTooltip(
this.font,
Component.translatable("container.enct.info.fuel"),
x,
y
)
}
} else {
super.renderTooltip(guiGraphics, x, y)
}
}
}
}
override fun render(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
renderBackground(guiGraphics, mouseX, mouseY, partialTick)
super.render(guiGraphics, mouseX, mouseY, partialTick)
renderTooltip(guiGraphics, mouseX, mouseY)
}
}

View file

@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "enct:block/enchantment_transferrer"
}
}
}

View file

@ -1,5 +1,12 @@
{
"itemGroup.enct": "Example Mod Tab",
"block.enct.example_block": "Example Block",
"item.enct.example_item": "Example Item"
"block.enct.enchantment_transferrer": "Enchantment Transferrer",
"container.enct.enchantment_transferrer": "Enchantment Transferrer",
"container.enct.charge": "Charge: %s",
"container.enct.processing": "Process: %s",
"container.enct.processing.waiting": "waiting",
"container.enct.processing.idle": "idle",
"container.enct.info.main": "Item with enchantments",
"container.enct.info.target": "Item to transfer enchantments to",
"container.enct.info.book": "Optional book",
"container.enct.info.fuel": "\"Charge\" fuel: copper, iron, gold, diamonds, redstone or netherite"
}

View file

@ -0,0 +1,12 @@
{
"block.enct.enchantment_transferrer": "Переносчик зачарований",
"container.enct.enchantment_transferrer": "Переносчик зачарований",
"container.enct.charge": "Заряд: %s",
"container.enct.processing": "Процесс: %s",
"container.enct.processing.waiting": "ждём",
"container.enct.processing.idle": "спим",
"container.enct.info.main": "Зачарованный предмет",
"container.enct.info.target": "Цель для переноса",
"container.enct.info.book": "Опциональная книга",
"container.enct.info.fuel": "Топливо для \"заряда\": медь, железо, золото, алмазы, редстоун or незерит"
}

View file

@ -0,0 +1,12 @@
{
"parent": "block/cube",
"textures": {
"down": "enct:block/enchantment_transferrer_bottom",
"up": "enct:block/enchantment_transferrer_top",
"north": "enct:block/enchantment_transferrer_side",
"east": "enct:block/enchantment_transferrer_side",
"south": "enct:block/enchantment_transferrer_side",
"west": "enct:block/enchantment_transferrer_side",
"particle": "enct:block/enchantment_transferrer_top"
}
}

View file

@ -0,0 +1,3 @@
{
"parent": "enct:block/enchantment_transferrer"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 221 B