commit e5bdff972186c9cfd45c9b0976b0ca60b9487c81 Author: yourfriendoss Date: Sun Oct 5 13:52:42 2025 +0300 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d5f737e --- /dev/null +++ b/.gitignore @@ -0,0 +1,119 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +.gradle +build/ + +# Ignore Gradle GUI config +gradle-app.setting + +# Cache of project +.gradletasknamecache + +**/build/ + +# Common working directory +run/ +runs/ + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..28d47f8 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,406 @@ +# PolyForm Shield License 1.0.0 + + + +## Acceptance + +In order to get any license under these terms, you must agree +to them as both strict obligations and conditions to all +your licenses. + +## Copyright License + +The licensor grants you a copyright license for the +software to do everything you might do with the software +that would otherwise infringe the licensor's copyright +in it for any permitted purpose. However, you may +only distribute the software according to [Distribution +License](#distribution-license) and make changes or new works +based on the software according to [Changes and New Works +License](#changes-and-new-works-license). + +## Distribution License + +The licensor grants you an additional copyright license +to distribute copies of the software. Your license +to distribute covers distributing the software with +changes and new works permitted by [Changes and New Works +License](#changes-and-new-works-license). + +## Notices + +You must ensure that anyone who gets a copy of any part of +the software from you also gets a copy of these terms or the +URL for them above, as well as copies of any plain-text lines +beginning with `Required Notice:` that the licensor provided +with the software. For example: + +> Required Notice: Copyright Yoyodyne, Inc. (http://example.com) + +## Changes and New Works License + +The licensor grants you an additional copyright license to +make changes and new works based on the software for any +permitted purpose. + +## Patent License + +The licensor grants you a patent license for the software that +covers patent claims the licensor can license, or becomes able +to license, that you would infringe by using the software. + +## Noncompete + +Any purpose is a permitted purpose, except for providing any +product that competes with the software or any product the +licensor or any of its affiliates provides using the software. + +## Competition + +Goods and services compete even when they provide functionality +through different kinds of interfaces or for different technical +platforms. Applications can compete with services, libraries +with plugins, frameworks with development tools, and so on, +even if they're written in different programming languages +or for different computer architectures. Goods and services +compete even when provided free of charge. If you market a +product as a practical substitute for the software or another +product, it definitely competes. + +## New Products + +If you are using the software to provide a product that does +not compete, but the licensor or any of its affiliates brings +your product into competition by providing a new version of +the software or another product using the software, you may +continue using versions of the software available under these +terms beforehand to provide your competing product, but not +any later versions. + +## Discontinued Products + +You may begin using the software to compete with a product +or service that the licensor or any of its affiliates has +stopped providing, unless the licensor includes a plain-text +line beginning with `Licensor Line of Business:` with the +software that mentions that line of business. For example: + +> Licensor Line of Business: YoyodyneCMS Content Management +System (http://example.com/cms) + +## Sales of Business + +If the licensor or any of its affiliates sells a line of +business developing the software or using the software +to provide a product, the buyer can also enforce +[Noncompete](#noncompete) for that product. + +## Fair Use + +You may have "fair use" rights for the software under the +law. These terms do not limit them. + +## No Other Rights + +These terms do not allow you to sublicense or transfer any of +your licenses to anyone else, or prevent the licensor from +granting licenses to anyone else. These terms do not imply +any other licenses. + +## Patent Defense + +If you make any written claim that the software infringes or +contributes to infringement of any patent, your patent license +for the software granted under these terms ends immediately. If +your company makes such a claim, your patent license ends +immediately for work on behalf of your company. + +## Violations + +The first time you are notified in writing that you have +violated any of these terms, or done anything with the software +not covered by your licenses, your licenses can nonetheless +continue if you come into full compliance with these terms, +and take practical steps to correct past violations, within +32 days of receiving notice. Otherwise, all your licenses +end immediately. + +## No Liability + +***As far as the law allows, the software comes as is, without +any warranty or condition, and the licensor will not be liable +to you for any damages arising out of these terms or the use +or nature of the software, under any kind of legal claim.*** + +## Definitions + +The **licensor** is the individual or entity offering these +terms, and the **software** is the software the licensor makes +available under these terms. + +A **product** can be a good or service, or a combination +of them. + +**You** refers to the individual or entity agreeing to these +terms. + +**Your company** is any legal entity, sole proprietorship, +or other kind of organization that you work for, plus all +its affiliates. + +**Affiliates** means the other organizations than an +organization has control over, is under the control of, or is +under common control with. + +**Control** means ownership of substantially all the assets of +an entity, or the power to direct its management and policies +by vote, contract, or otherwise. Control can be direct or +indirect. + +**Your licenses** are all the licenses granted to you for the +software under these terms. + +**Use** means anything you do with the software requiring one +of your licenses. + +--- + +# PolyForm Countdown License Grant + +Version 1.0.0 + + + +## Start Date + +2026-12-01 (ISO 8601-1:2019) + +## License + +Each contributor licenses this release to you on the new +license terms below, starting at 12:00 noon UTC on the +start date above. + +## Scope + +This license grant applies only to this release, not to +any other releases. Other releases may come with their +own license grants. + +## Reliability + +No contributor can revoke the new license before it starts. +If the new license terms allow a contributor to revoke, +they can do so only after the new license starts. + +## Legalities + +Legally, this is a present grant of a license on the date of +release, not a contract promise to grant the license later. + +## New License Terms + +``` + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2025 sad.ovh + +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 + + http://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. +``` + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..80c9db8 --- /dev/null +++ b/build.gradle @@ -0,0 +1,104 @@ +plugins { + id 'fabric-loom' version '1.11-SNAPSHOT' + id 'maven-publish' +} + +version = project.mod_version +group = project.maven_group + +base { + archivesName = project.archives_base_name +} + +loom { + splitEnvironmentSourceSets() + accessWidenerPath = file("src/main/resources/growglobe.accesswidener") + mods { + "growglobe" { + sourceSet sourceSets.main + sourceSet sourceSets.client + } + } +} + + +repositories { + // Add repositories to retrieve artifacts from in here. + // You should only use this when depending on other mods because + // Loom adds the essential maven repositories to download Minecraft and libraries from automatically. + // See https://docs.gradle.org/current/userguide/declaring_repositories.html + // for more information about repositories. +} + +dependencies { + // To change the versions see the gradle.properties file + minecraft "com.mojang:minecraft:${project.minecraft_version}" + mappings "net.fabricmc:yarn:${project.yarn_mappings}:v2" + modImplementation "net.fabricmc:fabric-loader:${project.loader_version}" + + modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" + + include modApi('teamreborn:energy:4.2.0') { + exclude(group: "net.fabricmc.fabric-api") + } +} + +processResources { + inputs.property "version", project.version + inputs.property "minecraft_version", project.minecraft_version + inputs.property "loader_version", project.loader_version + filteringCharset "UTF-8" + + filesMatching("fabric.mod.json") { + expand "version": project.version, + "minecraft_version": project.minecraft_version, + "loader_version": project.loader_version + } +} + +def targetJavaVersion = 21 +tasks.withType(JavaCompile).configureEach { + // 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 + // If Javadoc is generated, this must be specified in that task too. + it.options.encoding = "UTF-8" + if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { + it.options.release.set(targetJavaVersion) + } +} + +java { + def javaVersion = JavaVersion.toVersion(targetJavaVersion) + if (JavaVersion.current() < javaVersion) { + toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) + } + // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task + // if it is present. + // If you remove this line, sources will not be generated. + withSourcesJar() +} + +jar { + from("LICENSE") { + rename { "${it}_${project.archivesBaseName}" } + } +} + +// configure the maven publication +publishing { + publications { + create("mavenJava", MavenPublication) { + artifactId = project.archives_base_name + from components.java + } + } + + // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. + repositories { + // Add repositories to publish to here. + // Notice: This block does NOT have the same function as the block in the top level. + // The repositories here will be used for publishing your artifact, not for + // retrieving dependencies. + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..27265e2 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,14 @@ +# Done to increase the memory available to gradle. +org.gradle.jvmargs=-Xmx1G +# Fabric Properties +# check these on https://modmuss50.me/fabric.html +minecraft_version=1.21.8 +yarn_mappings=1.21.8+build.1 +loader_version=0.17.2 +# Mod Properties +mod_version=0.1 +maven_group=ovh.sad +archives_base_name=growglobe +# Dependencies +# check this on https://modmuss50.me/fabric.html +fabric_version=0.134.0+1.21.8 diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..1b33c55 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..002b867 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..23d15a9 --- /dev/null +++ b/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# 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 ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# 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 + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..5eed7ee --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,94 @@ +@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 +@rem SPDX-License-Identifier: Apache-2.0 +@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=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..f91a4fe --- /dev/null +++ b/settings.gradle @@ -0,0 +1,9 @@ +pluginManagement { + repositories { + maven { + name = 'Fabric' + url = 'https://maven.fabricmc.net/' + } + gradlePluginPortal() + } +} diff --git a/src/client/java/ovh/sad/growglobe/client/GrowGlobeBlockEntityRenderer.java b/src/client/java/ovh/sad/growglobe/client/GrowGlobeBlockEntityRenderer.java new file mode 100644 index 0000000..b7e9271 --- /dev/null +++ b/src/client/java/ovh/sad/growglobe/client/GrowGlobeBlockEntityRenderer.java @@ -0,0 +1,41 @@ +package ovh.sad.growglobe.client; + +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.client.render.block.entity.BlockEntityRenderer; +import net.minecraft.client.render.block.entity.BlockEntityRendererFactory; +import net.minecraft.client.render.item.ItemRenderer; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.item.ItemDisplayContext; +import net.minecraft.item.ItemStack; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.RotationAxis; +import ovh.sad.growglobe.GrowGlobeBlockEntity; + +public class GrowGlobeBlockEntityRenderer implements BlockEntityRenderer { + private final ItemRenderer itemRenderer; + + public GrowGlobeBlockEntityRenderer(BlockEntityRendererFactory.Context ctx) { + this.itemRenderer = ctx.getItemRenderer(); + } + + @Override + public void render(GrowGlobeBlockEntity entity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay, Vec3d cameraPos) { + ItemStack stack = entity.getInventory().getStack(9); + if (stack.isEmpty()) return; + + matrices.push(); + + matrices.translate(0.5, 0.5, 0.5); + long time = System.currentTimeMillis(); + float rotation = (time % 4000L) / 4000.0f * 360f; + matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(rotation)); + + float bob = MathHelper.sin((time % 2000L) / 2000.0f * (float)Math.PI * 2) * 0.05f; + matrices.translate(0, bob, 0); + + itemRenderer.renderItem(stack, ItemDisplayContext.GROUND, 0xF000F0, overlay, matrices, vertexConsumers, entity.getWorld(), 0); + + matrices.pop(); + } +} diff --git a/src/client/java/ovh/sad/growglobe/client/GrowGlobeScreen.java b/src/client/java/ovh/sad/growglobe/client/GrowGlobeScreen.java new file mode 100644 index 0000000..4fd1e86 --- /dev/null +++ b/src/client/java/ovh/sad/growglobe/client/GrowGlobeScreen.java @@ -0,0 +1,66 @@ +package ovh.sad.growglobe.client; + +import net.minecraft.client.gl.RenderPipelines; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.screen.ingame.FurnaceScreen; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.MathHelper; +import ovh.sad.growglobe.GrowGlobeScreenHandler; +import ovh.sad.growglobe.Growglobe; + +public class GrowGlobeScreen extends HandledScreen { + private static final Identifier GUI_TEXTURE = Identifier.of(Growglobe.MOD_ID, "textures/gui/grow_globe.png"); + private static final Identifier POWER_TEXTURE = Identifier.of(Growglobe.MOD_ID, "textures/gui/power_bar.png"); + private static final Identifier PROGRESS_TEXTURE = Identifier.ofVanilla("container/furnace/burn_progress"); // vanilla arrow + + public GrowGlobeScreen(GrowGlobeScreenHandler handler, PlayerInventory inventory, Text title) { + super(handler, inventory, title); + backgroundWidth = 176; + backgroundHeight = 166; + } + + @Override + protected void drawBackground(DrawContext context, float delta, int mouseX, int mouseY) { + int x = (width - backgroundWidth) / 2; + int y = (height - backgroundHeight) / 2; + context.drawTexture(RenderPipelines.GUI_TEXTURED, GUI_TEXTURE, x, y, 0, 0, backgroundWidth, backgroundHeight, backgroundWidth, backgroundHeight); + + int energyHeight = (int)(50.0f * ((float)handler.getEnergy() / handler.getMaxEnergy())); + context.drawTexture( + RenderPipelines.GUI_TEXTURED, + POWER_TEXTURE, + x + 10, y + 70 - energyHeight, + 176, 0, + 8, energyHeight, + 8, energyHeight + ); + + int progressWidth = MathHelper.ceil((handler.getProgress() / (float)handler.getMaxProgress()) * 24); // arrow is 24px wide + if (progressWidth > 0) { + context.drawGuiTexture( + RenderPipelines.GUI_TEXTURED, + PROGRESS_TEXTURE, + 24, 16, // full arrow size (from furnace) + 0, 0, // u, v + x + 56, y + 34, // position inside GUI + progressWidth, 16 // drawn portion + ); + } + } + + @Override + protected void drawForeground(DrawContext context, int mouseX, int mouseY) { + context.drawText(textRenderer, title, 8, 6, 0x404040, false); + context.drawText(textRenderer, handler.getEnergy() + " / " + handler.getMaxEnergy() + " FE", 90, 6, 0x404040, false); + } + + @Override + public void render(DrawContext context, int mouseX, int mouseY, float delta) { + renderBackground(context, mouseX, mouseY, delta); + super.render(context, mouseX, mouseY, delta); + drawMouseoverTooltip(context, mouseX, mouseY); + } +} diff --git a/src/client/java/ovh/sad/growglobe/client/GrowglobeClient.java b/src/client/java/ovh/sad/growglobe/client/GrowglobeClient.java new file mode 100644 index 0000000..4e81f59 --- /dev/null +++ b/src/client/java/ovh/sad/growglobe/client/GrowglobeClient.java @@ -0,0 +1,19 @@ +package ovh.sad.growglobe.client; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.rendering.v1.BlockRenderLayerMap; +import net.minecraft.client.gui.screen.ingame.HandledScreens; +import net.minecraft.client.render.BlockRenderLayer; +import net.minecraft.client.render.block.entity.BlockEntityRendererFactories; +import ovh.sad.growglobe.*; + +public class GrowglobeClient implements ClientModInitializer { + + @Override + public void onInitializeClient() { + BlockRenderLayerMap.putBlock(GrowGlobeBlocks.GROW_GLOBE_BLOCK, BlockRenderLayer.CUTOUT); + GrowGlobeScreenHandlers.register(); + HandledScreens.register(GrowGlobeScreenHandlers.GROW_GLOBE, GrowGlobeScreen::new); + BlockEntityRendererFactories.register(GrowGlobeBlockEntities.GROW_GLOBE, GrowGlobeBlockEntityRenderer::new); + } +} diff --git a/src/client/resources/growglobe.client.mixins.json b/src/client/resources/growglobe.client.mixins.json new file mode 100644 index 0000000..a8c8e0f --- /dev/null +++ b/src/client/resources/growglobe.client.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "ovh.sad.growglobe.mixin.client", + "compatibilityLevel": "JAVA_21", + "client": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "overwrites": { + "requireAnnotations": true + } +} diff --git a/src/main/java/ovh/sad/growglobe/GrowGlobeBlock.java b/src/main/java/ovh/sad/growglobe/GrowGlobeBlock.java new file mode 100644 index 0000000..b8d2210 --- /dev/null +++ b/src/main/java/ovh/sad/growglobe/GrowGlobeBlock.java @@ -0,0 +1,67 @@ +package ovh.sad.growglobe; + +import com.mojang.serialization.MapCodec; +import net.minecraft.block.Block; +import net.minecraft.block.BlockEntityProvider; +import net.minecraft.block.BlockState; +import net.minecraft.block.BlockWithEntity; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityTicker; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.entity.ItemEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.NamedScreenHandlerFactory; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.Text; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +public class GrowGlobeBlock extends BlockWithEntity { + public GrowGlobeBlock(Settings settings) { + super(settings); + } + + public static final MapCodec CODEC = createCodec(GrowGlobeBlock::new); + + @Override + protected ActionResult onUse(BlockState state, World world, BlockPos pos, PlayerEntity player, BlockHitResult hit) { + if (!world.isClient) { + BlockEntity blockEntity = world.getBlockEntity(pos); + System.out.println("one"); + + if (blockEntity instanceof GrowGlobeBlockEntity screenHandlerFactory) { + System.out.println("two"); + if (player instanceof ServerPlayerEntity serverPlayer) { + System.out.println("three"); + serverPlayer.openHandledScreen(screenHandlerFactory); + } + } + } + return ActionResult.SUCCESS; + } + + @Override + protected MapCodec getCodec() { + return CODEC; + } + + @Override + public BlockEntity createBlockEntity(BlockPos pos, BlockState state) { + return new GrowGlobeBlockEntity(pos, state); + } + + @Override + public BlockEntityTicker getTicker(World world, BlockState state, BlockEntityType type) { + return world.isClient ? null : + (type == GrowGlobeBlockEntities.GROW_GLOBE + ? (world1, pos, state1, be) -> GrowGlobeBlockEntity.tick(world1, pos, state1, (GrowGlobeBlockEntity)be) + : null); + } +} \ No newline at end of file diff --git a/src/main/java/ovh/sad/growglobe/GrowGlobeBlockEntities.java b/src/main/java/ovh/sad/growglobe/GrowGlobeBlockEntities.java new file mode 100644 index 0000000..9b94a39 --- /dev/null +++ b/src/main/java/ovh/sad/growglobe/GrowGlobeBlockEntities.java @@ -0,0 +1,22 @@ +package ovh.sad.growglobe; + +import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder; +import net.minecraft.block.entity.BlockEntityType; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.util.Identifier; +import team.reborn.energy.api.EnergyStorage; + +public class GrowGlobeBlockEntities { + public static BlockEntityType GROW_GLOBE; + + public static void register() { + GROW_GLOBE = Registry.register( + Registries.BLOCK_ENTITY_TYPE, + Identifier.of(Growglobe.MOD_ID, "growglobe"), + FabricBlockEntityTypeBuilder.create(GrowGlobeBlockEntity::new, GrowGlobeBlocks.GROW_GLOBE_BLOCK).build() + ); + + EnergyStorage.SIDED.registerForBlockEntity((be, dir) -> be.energyStorage, GROW_GLOBE); + } +} diff --git a/src/main/java/ovh/sad/growglobe/GrowGlobeBlockEntity.java b/src/main/java/ovh/sad/growglobe/GrowGlobeBlockEntity.java new file mode 100644 index 0000000..b944e18 --- /dev/null +++ b/src/main/java/ovh/sad/growglobe/GrowGlobeBlockEntity.java @@ -0,0 +1,235 @@ +package ovh.sad.growglobe; + +import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; +import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction; +import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.inventory.Inventories; +import net.minecraft.inventory.SidedInventory; +import net.minecraft.inventory.SimpleInventory; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.screen.ArrayPropertyDelegate; +import net.minecraft.screen.PropertyDelegate; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.storage.ReadView; +import net.minecraft.storage.WriteView; +import net.minecraft.text.Text; +import net.minecraft.util.ItemScatterer; +import net.minecraft.util.collection.DefaultedList; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Direction; +import net.minecraft.world.World; +import org.jetbrains.annotations.Nullable; +import team.reborn.energy.api.EnergyStorage; +import team.reborn.energy.api.base.SimpleEnergyStorage; + +import java.util.List; + +import static ovh.sad.growglobe.HarvestUtil.harvestBlock; + +public class GrowGlobeBlockEntity extends BlockEntity implements ExtendedScreenHandlerFactory, SidedInventory { + public static final long CAPACITY = 10000; + public static final long MAX_INSERT = 200; + public static final long MAX_EXTRACT = 0; + + public final SimpleEnergyStorage energyStorage = new SimpleEnergyStorage(CAPACITY, MAX_INSERT, MAX_EXTRACT) { + @Override + protected void onFinalCommit() { + markDirty(); + } + }; + + // Inventory: slot 9 = input (sapling), slots 0–8 = outputs + private final SimpleInventory inventory = new SimpleInventory(10); + + // [0]=currentEnergy, [1]=maxEnergy, [2]=progress, [3]=maxProgress + private final PropertyDelegate propertyDelegate; + + private int progress = 0; + private static final int MAX_PROGRESS = 80; + private static final int ENERGY_PER_TICK = 65; + + public GrowGlobeBlockEntity(BlockPos pos, BlockState state) { + super(GrowGlobeBlockEntities.GROW_GLOBE, pos, state); + this.propertyDelegate = new ArrayPropertyDelegate(4); + } + + @Override + public void onBlockReplaced(BlockPos pos, BlockState oldState) { + if (this.world != null) { + ItemScatterer.spawn(this.world, pos, this.inventory); + } + } + private static final int[] INPUT_SLOTS = {9}; + private static final int[] OUTPUT_SLOTS = {0,1,2,3,4,5,6,7,8}; + + @Override + public int[] getAvailableSlots(Direction side) { + if (side == Direction.UP) return INPUT_SLOTS; // hopper can insert sapling + if (side == Direction.DOWN) return OUTPUT_SLOTS; // hopper can extract outputs + return new int[0]; // sides cannot access + } + + public static void tick(World world, BlockPos pos, BlockState state, GrowGlobeBlockEntity blockEntity) { + if (world.isClient) return; + + for (Direction dir : Direction.values()) { + @Nullable EnergyStorage adjacent = EnergyStorage.SIDED.find(world, pos.offset(dir), dir.getOpposite()); + if (adjacent == null) continue; + try (Transaction tx = Transaction.openOuter()) { + long moved = adjacent.extract(50, tx); + long inserted = blockEntity.energyStorage.insert(moved, tx); + if (inserted > 0) tx.commit(); + } + } + + blockEntity.propertyDelegate.set(0, (int) Math.min(Integer.MAX_VALUE, blockEntity.energyStorage.amount)); + blockEntity.propertyDelegate.set(1, (int) Math.min(Integer.MAX_VALUE, blockEntity.energyStorage.capacity)); + blockEntity.propertyDelegate.set(2, blockEntity.progress); + blockEntity.propertyDelegate.set(3, MAX_PROGRESS); + + ItemStack input = blockEntity.inventory.getStack(9); + if (input.isEmpty()) { + blockEntity.progress = 0; + return; + } + + if (blockEntity.energyStorage.amount >= ENERGY_PER_TICK) { + blockEntity.energyStorage.amount -= ENERGY_PER_TICK; + blockEntity.progress++; + + if (blockEntity.progress >= MAX_PROGRESS) { + blockEntity.progress = 0; + + Item toolItem = input.getItem(); + List drops = harvestBlock((ServerWorld) world, toolItem, new BlockPos(0, 200, 0)); + + outer: + for (ItemStack drop : drops) { + for (int i = 0; i <= 8; i++) { + ItemStack slot = blockEntity.inventory.getStack(i); + if (slot.isEmpty()) { + blockEntity.inventory.setStack(i, drop); + continue outer; + } else if (canCombine(slot, drop)) { + int combined = Math.min(slot.getMaxCount(), slot.getCount() + drop.getCount()); + drop.setCount(drop.getCount() - (combined - slot.getCount())); + slot.setCount(combined); + blockEntity.inventory.setStack(i, slot); + if (drop.isEmpty()) continue outer; + } + } + } + blockEntity.markDirty(); + } + + } + } + + private static boolean canCombine(ItemStack a, ItemStack b) { + if (a.getItem() != b.getItem()) return false; + + if (a.hasEnchantments() != b.hasEnchantments()) return false; + if (a.hasEnchantments() && !a.getEnchantments().equals(b.getEnchantments())) return false; + + return a.getCount() < a.getMaxCount(); + } + + public PropertyDelegate getPropertyDelegate() { + return propertyDelegate; + } + + @Override + public Text getDisplayName() { + return Text.literal("Grow Globe"); + } + @Override + public boolean canInsert(int slot, ItemStack stack, @Nullable Direction dir) { + return dir == Direction.UP && slot == 9; // only top can insert into slot 9 + } + + @Override + public boolean canExtract(int slot, ItemStack stack, Direction dir) { + return dir == Direction.DOWN && slot >= 0 && slot <= 8; // only bottom can extract outputs + } + @Override + public ScreenHandler createMenu(int syncId, PlayerInventory inv, PlayerEntity player) { + return new GrowGlobeScreenHandler(syncId, inv, inventory, propertyDelegate); + } + + public SimpleInventory getInventory() { + return inventory; + } + + @Override + protected void readData(ReadView view) { + energyStorage.amount = view.getLong("Energy", 0L); + progress = view.getInt("Progress", 0); + DefaultedList list = this.inventory.getHeldStacks(); + Inventories.readData(view, list); + for (int i = 0; i < list.size(); i++) { + inventory.setStack(i, list.get(i)); + } + } + + @Override + protected void writeData(WriteView view) { + view.putLong("Energy", energyStorage.amount); + view.putInt("Progress", progress); + Inventories.writeData(view, this.inventory.getHeldStacks()); + } + + @Override + public Object getScreenOpeningData(ServerPlayerEntity player) { + return this.getPos(); + } + + + + // --- Inventory delegation --- + @Override + public int size() { + return inventory.size(); + } + + @Override + public boolean isEmpty() { + return inventory.isEmpty(); + } + + @Override + public ItemStack getStack(int slot) { + return inventory.getStack(slot); + } + + @Override + public ItemStack removeStack(int slot, int amount) { + return inventory.removeStack(slot, amount); + } + + @Override + public ItemStack removeStack(int slot) { + return inventory.removeStack(slot); + } + + @Override + public void setStack(int slot, ItemStack stack) { + inventory.setStack(slot, stack); + } + + @Override + public boolean canPlayerUse(PlayerEntity player) { + return true; + } + + @Override + public void clear() { + inventory.clear(); + } +} diff --git a/src/main/java/ovh/sad/growglobe/GrowGlobeBlocks.java b/src/main/java/ovh/sad/growglobe/GrowGlobeBlocks.java new file mode 100644 index 0000000..ca21577 --- /dev/null +++ b/src/main/java/ovh/sad/growglobe/GrowGlobeBlocks.java @@ -0,0 +1,58 @@ +package ovh.sad.growglobe; + +import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents; +import net.minecraft.block.AbstractBlock; +import net.minecraft.block.Block; +import net.minecraft.item.BlockItem; +import net.minecraft.item.Item; +import net.minecraft.item.ItemGroups; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.sound.BlockSoundGroup; +import net.minecraft.util.Identifier; + +import java.util.function.Function; + +public class GrowGlobeBlocks { + private static Block register(String name, Function blockFactory, AbstractBlock.Settings settings, boolean shouldRegisterItem) { + // Create a registry key for the block + RegistryKey blockKey = keyOfBlock(name); + // Create the block instance + Block block = blockFactory.apply(settings.registryKey(blockKey)); + + // Sometimes, you may not want to register an item for the block. + // Eg: if it's a technical block like `minecraft:moving_piston` or `minecraft:end_gateway` + if (shouldRegisterItem) { + // Items need to be registered with a different type of registry key, but the ID + // can be the same. + RegistryKey itemKey = keyOfItem(name); + + BlockItem blockItem = new BlockItem(block, new Item.Settings().registryKey(itemKey).useBlockPrefixedTranslationKey()); + Registry.register(Registries.ITEM, itemKey, blockItem); + } + + return Registry.register(Registries.BLOCK, blockKey, block); + } + + private static RegistryKey keyOfBlock(String name) { + return RegistryKey.of(RegistryKeys.BLOCK, Identifier.of(Growglobe.MOD_ID, name)); + } + + private static RegistryKey keyOfItem(String name) { + return RegistryKey.of(RegistryKeys.ITEM, Identifier.of(Growglobe.MOD_ID, name)); + } + public static final Block GROW_GLOBE_BLOCK = register( + "growglobe", + GrowGlobeBlock::new, + AbstractBlock.Settings.create().sounds(BlockSoundGroup.GLASS).nonOpaque(), + true + ); + + public static void initialize() { + ItemGroupEvents.modifyEntriesEvent(ItemGroups.FUNCTIONAL).register((itemGroup) -> { + itemGroup.add(GrowGlobeBlocks.GROW_GLOBE_BLOCK.asItem()); + }); + } +} diff --git a/src/main/java/ovh/sad/growglobe/GrowGlobeScreenHandler.java b/src/main/java/ovh/sad/growglobe/GrowGlobeScreenHandler.java new file mode 100644 index 0000000..f4e2eba --- /dev/null +++ b/src/main/java/ovh/sad/growglobe/GrowGlobeScreenHandler.java @@ -0,0 +1,119 @@ +package ovh.sad.growglobe; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.PropertyDelegate; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.screen.slot.Slot; + +public class GrowGlobeScreenHandler extends ScreenHandler { + private final Inventory inventory; + private final PropertyDelegate energyProperty; + + public GrowGlobeScreenHandler(int syncId, PlayerInventory playerInv, Inventory inventory, PropertyDelegate propertyDelegate) { + super(GrowGlobeScreenHandlers.GROW_GLOBE, syncId); + this.inventory = inventory; + this.energyProperty = propertyDelegate; + addProperties(propertyDelegate); + + // Custom 3x3 machine grid slots (indices 0-8) + int startX = 81; + int startY = 15; + int[][] machinePositions = { + {startX, startY}, {startX + 18, startY}, {startX + 36, startY}, // row 1 + {startX, startY + 18}, {startX + 18, startY + 18}, {startX + 36, startY + 18}, // row 2 + {startX, startY + 36}, {startX + 18, startY + 36}, {startX + 36, startY + 36} // row 3 + }; + for (int i = 0; i < 9; i++) { + this.addSlot(new Slot(inventory, i, machinePositions[i][0], machinePositions[i][1])); + } + + // Extra slot to the right (index 9) + this.addSlot(new Slot(inventory, 9, 38, 33)); + + // Player inventory slots + int playerStartY = startY + 65; // space below machine slots + for (int row = 0; row < 3; row++) { + for (int col = 0; col < 9; col++) { + this.addSlot(new Slot(playerInv, col + row * 9 + 9, 8 + col * 18, playerStartY + row * 18)); + } + } + // Player hotbar + for (int col = 0; col < 9; col++) { + this.addSlot(new Slot(playerInv, col, 8 + col * 18, playerStartY + 58)); + } + } + + @Override + public ItemStack quickMove(PlayerEntity player, int index) { + ItemStack newStack = ItemStack.EMPTY; + Slot slot = this.slots.get(index); + if (slot != null && slot.hasStack()) { + ItemStack original = slot.getStack(); + newStack = original.copy(); + + // Number of machine slots (must match block entity SimpleInventory size) + int machineSlotCount = 10; // slots 0..9 (0=input, 1..9 outputs) + + // Slot indexes in this.slots: + int playerInvStart = machineSlotCount; // start of player inventory in the slots list + int playerMainEnd = playerInvStart + 27; // end (exclusive) of main inventory + int hotbarStart = playerMainEnd; // start of hotbar + int hotbarEnd = hotbarStart + 9; // end (exclusive) of hotbar + + if (index < machineSlotCount) { + // from machine -> try to move into player (first main then hotbar) + if (!this.insertItem(original, playerInvStart, hotbarEnd, true)) { + return ItemStack.EMPTY; + } + } else if (index >= playerInvStart && index < playerMainEnd) { + // from player main inventory -> try machine input first, then hotbar + if (!this.insertItem(original, 0, 1, false)) { + if (!this.insertItem(original, playerMainEnd, hotbarEnd, false)) { + return ItemStack.EMPTY; + } + } + } else if (index >= hotbarStart && index < hotbarEnd) { + // from hotbar -> try machine input first, then main inventory + if (!this.insertItem(original, 0, 1, false)) { + if (!this.insertItem(original, playerInvStart, playerMainEnd, false)) { + return ItemStack.EMPTY; + } + } + } else { + return ItemStack.EMPTY; + } + + if (original.isEmpty()) { + slot.setStack(ItemStack.EMPTY); + } else { + slot.markDirty(); + } + } + return newStack; + } + + + + @Override + public boolean canUse(PlayerEntity player) { + return inventory.canPlayerUse(player); + } + + public int getEnergy() { + return energyProperty.get(0); + } + + public int getMaxEnergy() { + return energyProperty.get(1); + } + public int getProgress() { + return energyProperty.get(2); + } + + public int getMaxProgress() { + return energyProperty.get(3); + } +} diff --git a/src/main/java/ovh/sad/growglobe/GrowGlobeScreenHandlers.java b/src/main/java/ovh/sad/growglobe/GrowGlobeScreenHandlers.java new file mode 100644 index 0000000..37c4ada --- /dev/null +++ b/src/main/java/ovh/sad/growglobe/GrowGlobeScreenHandlers.java @@ -0,0 +1,36 @@ +package ovh.sad.growglobe; + +import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.screen.ScreenHandlerType; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; + +// ModScreenHandlers.java +public class GrowGlobeScreenHandlers { + public static ScreenHandlerType GROW_GLOBE; + + public static void register() { + GROW_GLOBE = Registry.register( + Registries.SCREEN_HANDLER, + Identifier.of(Growglobe.MOD_ID, "growglobe"), + new ExtendedScreenHandlerType<>( + // Decoder: called on client + (syncId, playerInv, buf) -> { + BlockPos pos = buf; + + var be = playerInv.player.getWorld().getBlockEntity(pos); + if (be instanceof GrowGlobeBlockEntity blockEntity) { + return new GrowGlobeScreenHandler(syncId, playerInv, blockEntity.getInventory(), blockEntity.getPropertyDelegate()); + } + // Fallback if BE missing + return new GrowGlobeScreenHandler(syncId, playerInv, new net.minecraft.inventory.SimpleInventory(7), new net.minecraft.screen.ArrayPropertyDelegate(2)); + }, + // PacketCodec to sync BlockPos + BlockPos.PACKET_CODEC + ) + ); + } +} + diff --git a/src/main/java/ovh/sad/growglobe/Growglobe.java b/src/main/java/ovh/sad/growglobe/Growglobe.java new file mode 100644 index 0000000..2cf2eaf --- /dev/null +++ b/src/main/java/ovh/sad/growglobe/Growglobe.java @@ -0,0 +1,14 @@ +package ovh.sad.growglobe; + +import net.fabricmc.api.ModInitializer; +import net.minecraft.world.World; + +public class Growglobe implements ModInitializer { + public static String MOD_ID = "growglobe"; + + @Override + public void onInitialize() { + GrowGlobeBlocks.initialize(); + GrowGlobeBlockEntities.register(); + } +} diff --git a/src/main/java/ovh/sad/growglobe/HarvestUtil.java b/src/main/java/ovh/sad/growglobe/HarvestUtil.java new file mode 100644 index 0000000..209bff9 --- /dev/null +++ b/src/main/java/ovh/sad/growglobe/HarvestUtil.java @@ -0,0 +1,92 @@ +package ovh.sad.growglobe; + +import net.minecraft.block.*; +import net.minecraft.item.BlockItem; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.registry.Registries; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.registry.entry.RegistryEntry; +import net.minecraft.registry.tag.BlockTags; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import net.minecraft.world.gen.feature.ConfiguredFeature; +import net.minecraft.world.gen.feature.TreeFeatureConfig; +import net.minecraft.world.gen.foliage.SpruceFoliagePlacer; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class HarvestUtil { + + /** + * Dynamically harvests any block or item and returns the resulting drops. + * Works with vanilla and modded blocks. + * + * @param world the world + * @param item the Item (usually BlockItem) to harvest + * @param pos the position (optional for context; can be BlockPos.ORIGIN) + * @return List of ItemStacks that would drop + */ + public static List harvestBlock(ServerWorld world, Item item, BlockPos pos) { + List drops = new ArrayList<>(); + + if (!(item instanceof BlockItem blockItem)) return drops; + + Block block = blockItem.getBlock(); + BlockState state = block.getDefaultState(); + + BlockEntityProvider blockEntityProvider = block instanceof BlockEntityProvider bep ? bep : null; + + // CropBlock: drop crop + seeds + if (block instanceof CropBlock crop) { + state = crop.withAge(crop.getMaxAge()); + } + // Stackable plants like sugar cane or bamboo + else if (block instanceof SugarCaneBlock) { + int height = 3; // or dynamically detect in world + for (int i = 0; i < height; i++) drops.add(new ItemStack(block)); + return drops; + } + else if (state.isIn(BlockTags.SAPLINGS)) { + SaplingBlock sapling = (SaplingBlock) block; + SaplingGenerator generator = sapling.generator; + + RegistryKey> featureKey = + generator.getSmallTreeFeature(world.random, true); + if (featureKey == null) + featureKey = generator.getMegaTreeFeature(world.random); + + if (featureKey != null) { + var featureRegistry = world.getRegistryManager().getOrThrow(RegistryKeys.CONFIGURED_FEATURE); + + var configured = featureRegistry.getOrThrow(featureKey); + + if (configured.value().config() instanceof TreeFeatureConfig treeConfig) { + BlockState logState = treeConfig.trunkProvider.get(world.random, pos); + BlockState leafState = treeConfig.foliageProvider.get(world.random, pos); + + int logCount = 5 + world.random.nextInt(3); + for (int i = 0; i < logCount; i++) + drops.addAll(Block.getDroppedStacks(logState, world, pos, null)); + + int leafCount = 10 + world.random.nextInt(10); + for (int i = 0; i < leafCount; i++) + drops.addAll(Block.getDroppedStacks(leafState, world, pos, null)); + + drops.add(new ItemStack(sapling)); + return drops; + } + } + + drops.add(new ItemStack(sapling)); + return drops; + } + + drops.addAll(Block.getDroppedStacks(state, world, pos, blockEntityProvider != null ? blockEntityProvider.createBlockEntity(pos, state) : null)); + return drops; + } +} \ No newline at end of file diff --git a/src/main/resources/assets/growglobe/blockstates/growglobe.json b/src/main/resources/assets/growglobe/blockstates/growglobe.json new file mode 100644 index 0000000..f367a19 --- /dev/null +++ b/src/main/resources/assets/growglobe/blockstates/growglobe.json @@ -0,0 +1,7 @@ +{ + "variants": { + "": { + "model": "growglobe:block/growglobe" + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/growglobe/items/growglobe.json b/src/main/resources/assets/growglobe/items/growglobe.json new file mode 100644 index 0000000..a16eb66 --- /dev/null +++ b/src/main/resources/assets/growglobe/items/growglobe.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "growglobe:block/growglobe" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/growglobe/lang/en_us.json b/src/main/resources/assets/growglobe/lang/en_us.json new file mode 100644 index 0000000..65dd83b --- /dev/null +++ b/src/main/resources/assets/growglobe/lang/en_us.json @@ -0,0 +1,3 @@ +{ + "block.growglobe.growglobe": "Grow Globe" +} \ No newline at end of file diff --git a/src/main/resources/assets/growglobe/models/block/growglobe.json b/src/main/resources/assets/growglobe/models/block/growglobe.json new file mode 100644 index 0000000..f3cef4e --- /dev/null +++ b/src/main/resources/assets/growglobe/models/block/growglobe.json @@ -0,0 +1,227 @@ +{ + "credit": "Made with Blockbench", + "textures": { + "0": "block/black_concrete", + "1": "block/black_concrete_powder", + "2": "block/dirt", + "3": "block/rooted_dirt", + "4": "block/glass", + "particle": "block/black_concrete" + }, + "elements": [ + { + "from": [0, 0, 0], + "to": [16, 2, 2], + "faces": { + "north": {"uv": [0, 0, 16, 2], "texture": "#0"}, + "east": {"uv": [0, 0, 2, 2], "texture": "#0"}, + "south": {"uv": [0, 0, 16, 2], "texture": "#0"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#0"}, + "up": {"uv": [0, 0, 16, 2], "texture": "#0"}, + "down": {"uv": [0, 0, 16, 2], "texture": "#0"} + } + }, + { + "from": [0, 0, 14], + "to": [16, 2, 16], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 14]}, + "faces": { + "north": {"uv": [0, 0, 16, 2], "texture": "#0"}, + "east": {"uv": [0, 0, 2, 2], "texture": "#0"}, + "south": {"uv": [0, 0, 16, 2], "texture": "#0"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#0"}, + "up": {"uv": [0, 0, 16, 2], "texture": "#0"}, + "down": {"uv": [0, 0, 16, 2], "texture": "#0"} + } + }, + { + "from": [14, 0, 2], + "to": [16, 2, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 2]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "texture": "#0"}, + "east": {"uv": [0, 0, 12, 2], "texture": "#0"}, + "south": {"uv": [0, 0, 2, 2], "texture": "#0"}, + "west": {"uv": [0, 0, 12, 2], "texture": "#0"}, + "up": {"uv": [0, 0, 2, 12], "texture": "#0"}, + "down": {"uv": [0, 0, 2, 12], "texture": "#0"} + } + }, + { + "from": [0, 0, 2], + "to": [2, 2, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [-14, 0, 2]}, + "faces": { + "north": {"uv": [0, 0, 2, 2], "texture": "#0"}, + "east": {"uv": [0, 0, 12, 2], "texture": "#0"}, + "south": {"uv": [0, 0, 2, 2], "texture": "#0"}, + "west": {"uv": [0, 0, 12, 2], "texture": "#0"}, + "up": {"uv": [0, 0, 2, 12], "texture": "#0"}, + "down": {"uv": [0, 0, 2, 12], "texture": "#0"} + } + }, + { + "from": [2, 0, 2], + "to": [14, 2, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [2, 0, 2]}, + "faces": { + "north": {"uv": [0, 0, 12, 2], "texture": "#1"}, + "east": {"uv": [0, 0, 12, 2], "texture": "#1"}, + "south": {"uv": [0, 0, 12, 2], "texture": "#1"}, + "west": {"uv": [0, 0, 12, 2], "texture": "#1"}, + "up": {"uv": [0, 0, 12, 12], "texture": "#1"}, + "down": {"uv": [0, 0, 12, 12], "texture": "#1"} + } + }, + { + "from": [8, 2, 8], + "to": [13, 4, 13], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 2, 8]}, + "faces": { + "north": {"uv": [0, 0, 5, 2], "texture": "#2"}, + "east": {"uv": [0, 0, 5, 2], "texture": "#2"}, + "south": {"uv": [0, 0, 5, 2], "texture": "#2"}, + "west": {"uv": [0, 0, 5, 2], "texture": "#2"}, + "up": {"uv": [0, 0, 5, 5], "texture": "#2"}, + "down": {"uv": [0, 0, 5, 5], "texture": "#2"} + } + }, + { + "from": [4, 2, 6], + "to": [8, 4, 10], + "rotation": {"angle": 0, "axis": "y", "origin": [4, 2, 5]}, + "faces": { + "north": {"uv": [0, 0, 4, 2], "texture": "#2"}, + "east": {"uv": [0, 0, 4, 2], "texture": "#2"}, + "south": {"uv": [0, 0, 4, 2], "texture": "#2"}, + "west": {"uv": [0, 0, 4, 2], "texture": "#2"}, + "up": {"uv": [0, 0, 4, 4], "texture": "#2"}, + "down": {"uv": [0, 0, 4, 4], "texture": "#2"} + } + }, + { + "from": [8, 2, 3], + "to": [13, 4, 8], + "rotation": {"angle": 0, "axis": "y", "origin": [10, 2, 4]}, + "faces": { + "north": {"uv": [0, 0, 5, 2], "texture": "#3"}, + "east": {"uv": [0, 0, 5, 2], "texture": "#3"}, + "south": {"uv": [0, 0, 5, 2], "texture": "#3"}, + "west": {"uv": [0, 0, 5, 2], "texture": "#3"}, + "up": {"uv": [0, 0, 5, 5], "texture": "#3"}, + "down": {"uv": [0, 0, 5, 5], "texture": "#3"} + } + }, + { + "from": [4, 2, 4], + "to": [8, 4, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [6, 2, 4]}, + "faces": { + "north": {"uv": [0, 0, 4, 2], "texture": "#3"}, + "east": {"uv": [0, 0, 2, 2], "texture": "#3"}, + "south": {"uv": [0, 0, 4, 2], "texture": "#3"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#3"}, + "up": {"uv": [0, 0, 4, 2], "texture": "#3"}, + "down": {"uv": [0, 0, 4, 2], "texture": "#3"} + } + }, + { + "from": [5, 2, 10], + "to": [8, 4, 12], + "rotation": {"angle": 0, "axis": "y", "origin": [6, 2, 10]}, + "faces": { + "north": {"uv": [0, 0, 3, 2], "texture": "#3"}, + "east": {"uv": [0, 0, 2, 2], "texture": "#3"}, + "south": {"uv": [0, 0, 3, 2], "texture": "#3"}, + "west": {"uv": [0, 0, 2, 2], "texture": "#3"}, + "up": {"uv": [0, 0, 3, 2], "texture": "#3"}, + "down": {"uv": [0, 0, 3, 2], "texture": "#3"} + } + }, + { + "from": [2, 15, 2], + "to": [14, 16, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [2, 15, 2]}, + "faces": { + "north": {"uv": [0, 0, 12, 1], "texture": "#4"}, + "east": {"uv": [0, 0, 12, 1], "texture": "#4"}, + "south": {"uv": [0, 0, 12, 1], "texture": "#4"}, + "west": {"uv": [0, 0, 12, 1], "texture": "#4"}, + "up": {"uv": [0, 0, 12, 12], "texture": "#4"}, + "down": {"uv": [0, 0, 12, 12], "texture": "#4"} + } + }, + { + "from": [1, 2, 1], + "to": [15, 15, 2], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 1]}, + "faces": { + "north": {"uv": [0, 0, 14, 13], "texture": "#4"}, + "east": {"uv": [0, 0, 1, 13], "texture": "#4"}, + "south": {"uv": [0, 0, 14, 13], "texture": "#4"}, + "west": {"uv": [0, 0, 1, 13], "texture": "#4"}, + "up": {"uv": [0, 0, 14, 1], "texture": "#4"}, + "down": {"uv": [0, 0, 14, 1], "texture": "#4"} + } + }, + { + "from": [1, 2, 14], + "to": [15, 15, 15], + "rotation": {"angle": 0, "axis": "y", "origin": [0, 0, 14]}, + "faces": { + "north": {"uv": [0, 0, 14, 13], "texture": "#4"}, + "east": {"uv": [0, 0, 1, 13], "texture": "#4"}, + "south": {"uv": [0, 0, 14, 13], "texture": "#4"}, + "west": {"uv": [0, 0, 1, 13], "texture": "#4"}, + "up": {"uv": [0, 0, 14, 1], "texture": "#4"}, + "down": {"uv": [0, 0, 14, 1], "texture": "#4"} + } + }, + { + "from": [14, 2, 2], + "to": [15, 15, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [15, 0, 1]}, + "faces": { + "north": {"uv": [0, 0, 1, 13], "texture": "#4"}, + "east": {"uv": [0, 0, 12, 13], "texture": "#4"}, + "south": {"uv": [0, 0, 1, 13], "texture": "#4"}, + "west": {"uv": [0, 0, 12, 13], "texture": "#4"}, + "up": {"uv": [0, 0, 12, 1], "rotation": 90, "texture": "#4"}, + "down": {"uv": [0, 0, 12, 1], "rotation": 270, "texture": "#4"} + } + }, + { + "from": [1, 2, 2], + "to": [2, 15, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [2, 0, 1]}, + "faces": { + "north": {"uv": [0, 0, 1, 13], "texture": "#4"}, + "east": {"uv": [0, 0, 12, 13], "texture": "#4"}, + "south": {"uv": [0, 0, 1, 13], "texture": "#4"}, + "west": {"uv": [0, 0, 12, 13], "texture": "#4"}, + "up": {"uv": [0, 0, 12, 1], "rotation": 90, "texture": "#4"}, + "down": {"uv": [0, 0, 12, 1], "rotation": 270, "texture": "#4"} + } + } + ], + "groups": [ + { + "name": "base", + "origin": [8, 8, 8], + "color": 0, + "children": [0, 1, 2, 3, 4] + }, + { + "name": "inside", + "origin": [8, 8, 8], + "color": 0, + "children": [5, 6, 7, 8, 9] + }, + { + "name": "globe", + "origin": [7, 4, 7], + "color": 0, + "children": [10, 11, 12, 13, 14] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/growglobe/textures/gui/grow_globe.png b/src/main/resources/assets/growglobe/textures/gui/grow_globe.png new file mode 100644 index 0000000..390121f Binary files /dev/null and b/src/main/resources/assets/growglobe/textures/gui/grow_globe.png differ diff --git a/src/main/resources/assets/growglobe/textures/gui/power_bar.png b/src/main/resources/assets/growglobe/textures/gui/power_bar.png new file mode 100644 index 0000000..90ba44b Binary files /dev/null and b/src/main/resources/assets/growglobe/textures/gui/power_bar.png differ diff --git a/src/main/resources/data/growglobe/loot_table/blocks/growglobe.json b/src/main/resources/data/growglobe/loot_table/blocks/growglobe.json new file mode 100644 index 0000000..7a3f0ad --- /dev/null +++ b/src/main/resources/data/growglobe/loot_table/blocks/growglobe.json @@ -0,0 +1,14 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "growglobe:growglobe" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/data/minecraft/tags/block/mineable/pickaxe.json b/src/main/resources/data/minecraft/tags/block/mineable/pickaxe.json new file mode 100644 index 0000000..5ef56e1 --- /dev/null +++ b/src/main/resources/data/minecraft/tags/block/mineable/pickaxe.json @@ -0,0 +1,4 @@ +{ + "replace": false, + "values": ["growglobe:growglobe"] +} \ No newline at end of file diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json new file mode 100644 index 0000000..1f3dd63 --- /dev/null +++ b/src/main/resources/fabric.mod.json @@ -0,0 +1,33 @@ +{ + "schemaVersion": 1, + "id": "growglobe", + "version": "${version}", + "name": "Grow Globe", + "description": "A plant-in-jar like mod for Fabric.", + "authors": [], + "contact": {}, + "license": "PolyForm-Shield-License-1.0.0", + "icon": "assets/growglobe/icon.png", + "environment": "*", + "accessWidener" : "growglobe.accesswidener", + "entrypoints": { + "client": [ + "ovh.sad.growglobe.client.GrowglobeClient" + ], + "main": [ + "ovh.sad.growglobe.Growglobe" + ] + }, + "mixins": [ + "growglobe.mixins.json", + { + "config": "growglobe.client.mixins.json", + "environment": "client" + } + ], + "depends": { + "fabricloader": ">=${loader_version}", + "fabric": "*", + "minecraft": "${minecraft_version}" + } +} diff --git a/src/main/resources/growglobe.accesswidener b/src/main/resources/growglobe.accesswidener new file mode 100644 index 0000000..3a9b27c --- /dev/null +++ b/src/main/resources/growglobe.accesswidener @@ -0,0 +1,5 @@ +accessWidener v2 named + +accessible field net/minecraft/block/SaplingBlock generator Lnet/minecraft/block/SaplingGenerator; +accessible method net/minecraft/block/SaplingGenerator getSmallTreeFeature (Lnet/minecraft/util/math/random/Random;Z)Lnet/minecraft/registry/RegistryKey; +accessible method net/minecraft/block/SaplingGenerator getMegaTreeFeature (Lnet/minecraft/util/math/random/Random;)Lnet/minecraft/registry/RegistryKey; \ No newline at end of file diff --git a/src/main/resources/growglobe.mixins.json b/src/main/resources/growglobe.mixins.json new file mode 100644 index 0000000..65711ef --- /dev/null +++ b/src/main/resources/growglobe.mixins.json @@ -0,0 +1,14 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "ovh.sad.growglobe.mixin", + "compatibilityLevel": "JAVA_21", + "mixins": [ + ], + "injectors": { + "defaultRequire": 1 + }, + "overwrites": { + "requireAnnotations": true + } +}