Compare commits

...

53 Commits

Author SHA1 Message Date
c2a88e33c3 Merge pull request 'Release 1.21.1-v3.0.0' (#8) from 1.21.1/dev into 1.21.1/main
Reviewed-on: #8
2026-01-18 02:01:30 +00:00
067ac953ec Merge pull request '1.21.1/feat/6-update' (#7) from 1.21.1/feat/6-update into 1.21.1/dev
Reviewed-on: #7
2026-01-18 01:59:33 +00:00
9f797a9112 Adjusted default config value. 2026-01-18 02:52:54 +01:00
05192b2601 Fixed charge going below 0 when reviving more expensive targets with dynamic cost. Fixing tooltip cost appearing in red when the target's cost is more than the max charge capacity but the totem can revive more expensive targets. 2026-01-18 02:48:22 +01:00
c21ba74432 Using config for getting totem max durability for tooltip. 2026-01-18 02:36:43 +01:00
ed3dcc3d1e Checking config for unbreakable status to make tooltips more accurate in menus such as the creative menu. 2026-01-18 02:21:02 +01:00
07184045e4 Rebalanced default configs. Displaying static cost in tooltip. 2026-01-18 02:18:15 +01:00
6bb74b3635 Updated totem tooltip to display N/A for cost when the target is also N/A. 2026-01-18 00:52:36 +01:00
8b296c6750 Changed diamond totem default config to allow reviving across dimensions. 2026-01-18 00:49:04 +01:00
85b67a1a8d Switched rarities of Iron and Straw totems to be consistent in regard to progression. 2026-01-18 00:46:24 +01:00
8dbeb36d13 Updated item constructors to simplify usage. 2026-01-18 00:41:24 +01:00
ef50e0084c Updated totem tooltip to display general durability information. 2026-01-18 00:35:15 +01:00
27634a9bdf Updated config comments. 2026-01-18 00:18:48 +01:00
7afc1956bb Reworked durability configs to be dynamically set during inventory tick update whenever a config change is detected. Added unbreakable text to totem tooltip. Fixed server world crashing when reviving player due to closing level (try-with-resources). 2026-01-18 00:00:36 +01:00
5a9fd203d3 Bumped version number. 2026-01-14 02:23:06 +01:00
b4d6a9dba1 Ensuring target costs at least 1 charge when using dynamic cost. 2026-01-14 01:52:09 +01:00
9afca9c3b9 Extracted general methods from LangAsset into ComponentHelper class. 2026-01-14 01:07:20 +01:00
b0c5e9ba3d Converted charge item tooltip to use lang file. Improved tooltip to display corresponding totem's name. 2026-01-14 01:04:50 +01:00
859775d7b5 Inverted colors to improve readability of messages. 2026-01-14 00:44:46 +01:00
984f5ca8d6 Improved look of tooltips. Changed colors to improve readability. Implemented helper method for adding indent to components. 2026-01-14 00:43:46 +01:00
11e6aee70e Reworked tooltip for totems to look better and be translatable via lang files. Implemented helper method for creating text components displaying keybind keys. 2026-01-14 00:10:47 +01:00
8bc56f76de Removed hard coded lang string reference. 2026-01-12 22:20:55 +01:00
684cb23f74 Ported cycle totem message to lang file. 2026-01-12 22:19:00 +01:00
24d21126b0 Implemented LangAsset enum to declare translatable keys for strings. Moved message strings for reviving functionality into lang file. 2026-01-12 22:15:41 +01:00
925f244cc0 Removed old networking code. 2026-01-12 04:37:21 +01:00
dbabba477a CycleTarget method now returns message component. Implemented totem functionality without any extra networking. 2026-01-12 04:36:44 +01:00
5b2771c1f7 Fixed advanced tooltip key press check. Removed old broken mod key mappings. 2026-01-12 03:57:26 +01:00
b03aa3e0ed Fixed totem data not saving or synchronizing on a dedicated server. 2026-01-12 03:40:41 +01:00
4bb8e9dca0 Removed the need for template files. Replaced logo file with png as jpg seems to not work with NeoForge. 2026-01-12 01:22:46 +01:00
06ef028b6c Updated mods.toml. 2026-01-11 01:19:49 +01:00
6eabe7b3c5 Updated gitignore. Updated mod id across project. Fixed gradle not replacing properties in mods.toml. 2026-01-11 01:18:18 +01:00
8abbf5384e WIP: Temporarily disabling old networking code. 2026-01-11 00:40:15 +01:00
1c45352037 Ported charge items. Implemented property helper. Made netherite charges fire-resistant. 2026-01-11 00:25:55 +01:00
935ccd14bb Implemented new methods for each totem item. Implemented helper for generating item properties, now also adding totem data to the item. 2026-01-11 00:20:54 +01:00
0f35787a53 WIP use method. Fixed remaining methods. 2026-01-11 00:00:30 +01:00
4c6d9b4b14 Ported totem tooltip. Implemented helpers for getting target UUID from totem data. 2026-01-10 19:51:56 +01:00
af097ffdf5 Extracted code into method. 2026-01-10 19:27:17 +01:00
300e0a940a Rewrote TotemItem methods to simplify usage and keep functionality within the same class. 2026-01-10 19:24:37 +01:00
b400f4334a WIP: Logic cleanup. 2026-01-09 07:05:33 +01:00
39f8544e08 WIP: Converting methods to utilize Optionals. 2026-01-09 05:55:38 +01:00
f41bead3d2 Included charge in totem data. 2026-01-09 05:40:01 +01:00
56f274a81b WIP: Included target deaths in data component. 2026-01-09 05:26:42 +01:00
91f3f9dbdd WIP: Included target name in data component. 2026-01-09 05:20:04 +01:00
7421a5a18d WIP: Included target UUID in data component. 2026-01-09 05:14:49 +01:00
f6e47d78e1 WIP: Creating DataComponent for totem items. 2026-01-09 05:01:09 +01:00
2f1a9793a3 Ported datagens. 2026-01-09 01:01:53 +01:00
2bfe6b72e5 Ported registers. 2026-01-09 00:45:40 +01:00
e8b0b1987b Removed old metadata. 2026-01-09 00:38:33 +01:00
2262cc5952 Ported config. 2026-01-09 00:37:25 +01:00
9a0415effd Ported remainder of main class. 2026-01-09 00:08:13 +01:00
681ebeeeb7 Ported side proxy. 2026-01-09 00:02:21 +01:00
aff7f9ae54 Ported gradle and metadata to NeoForge. 2026-01-08 23:36:45 +01:00
407e26a179 Updated gradle and metadata. 2026-01-08 22:17:34 +01:00
91 changed files with 1490 additions and 1233 deletions

3
.gitignore vendored
View File

@ -2,6 +2,9 @@
*.blend?
*~
### Minecraft
/src/generated/resources/.cache/*
### Java template
# Compiled class file
*.class

BIN
assets/images/web/logo.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -1,112 +1,191 @@
buildscript {
repositories {
// These repositories are only for Gradle plugins, put any other repositories in the repository block further below
maven { url = 'https://maven.minecraftforge.net' }
mavenCentral()
}
dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true
}
plugins {
id 'java-library'
id 'maven-publish'
id 'net.neoforged.moddev' version '2.0.137'
id 'idea'
}
apply plugin: 'net.minecraftforge.gradle'
tasks.named('wrapper', Wrapper).configure {
// Define wrapper values here so as to not have to always do so when updating gradlew.properties.
// Switching this to Wrapper.DistributionType.ALL will download the full gradle sources that comes with
// documentation attached on cursor hover of gradle classes and methods. However, this comes with increased
// file size for Gradle. If you do switch this to ALL, run the Gradle wrapper task twice afterwards.
// (Verify by checking gradle/wrapper/gradle-wrapper.properties to see if distributionUrl now points to `-all`)
distributionType = Wrapper.DistributionType.BIN
}
def archiveVersion = "${project.mcVersion}-${project.buildVersion}" as Object
group = mod_group_id
version = mod_version
java.toolchain.languageVersion = JavaLanguageVersion.of(17)
repositories {
// Add here additional repositories if required by some of the dependencies below.
}
minecraft {
mappings channel: 'official', version: mcVersion
base {
archivesName = "${mod_id}-${minecraft_version}"
}
accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
java {
toolchain.languageVersion = JavaLanguageVersion.of(21)
}
neoForge {
// Specify the version of NeoForge to use.
version = project.neo_version
parchment {
mappingsVersion = project.parchment_mappings_version
minecraftVersion = project.parchment_minecraft_version
}
// This line is optional. Access Transformers are automatically detected
// accessTransformers = project.files('src/main/resources/META-INF/accesstransformer.cfg')
// Default run configurations.
// These can be tweaked, removed, or duplicated as needed.
runs {
client {
workingDirectory project.file('run')
client()
property 'forge.logging.markers', 'REGISTRIES'
property 'forge.logging.console.level', 'debug'
mods {
totemofreviving {
source sourceSets.main
}
}
// Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
}
server {
workingDirectory project.file('run')
property 'forge.logging.markers', 'REGISTRIES'
property 'forge.logging.console.level', 'debug'
mods {
totemofreviving {
source sourceSets.main
}
server()
programArgument '--nogui'
systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
}
// This run config launches GameTestServer and runs all registered gametests, then exits.
// By default, the server will crash when no gametests are provided.
// The gametest system is also enabled by default for other run configs under the /test command.
gameTestServer {
type = "gameTestServer"
systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
}
data {
workingDirectory project.file('run')
data()
property 'forge.logging.markers', 'REGISTRIES'
property 'forge.logging.console.level', 'debug'
// example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it
// gameDirectory = project.file('run-data')
args '--mod', 'totemofreviving', '--all',
'--existing', file('src/main/resources').toString(),
'--existing', file('src/generated/resources').toString(),
'--output', file('src/generated/resources/')
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath()
}
// applies to all the run configs above
configureEach {
// Recommended logging data for a userdev environment
// The markers can be added/remove as needed separated by commas.
// "SCAN": For mods scan.
// "REGISTRIES": For firing of registry events.
// "REGISTRYDUMP": For getting the contents of all registries.
systemProperty 'forge.logging.markers', 'REGISTRIES'
// Recommended logging level for the console
// You can set various levels here.
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
logLevel = org.slf4j.event.Level.DEBUG
}
}
mods {
totemofreviving {
source sourceSets.main
}
}
// define mod <-> source bindings
// these are used to tell the game which sources are for which mod
// multi mod projects should define one per mod
"${mod_id}" {
sourceSet(sourceSets.main)
}
}
}
// Include resources generated by data generators.
sourceSets.main.resources {
srcDir 'src/generated/resources'
}
repositories {
maven {
name = "Progwml6 maven"
url = "https://dvs1.progwml6.com/files/maven/"
}
maven {
name = "ModMaven"
url = "https://modmaven.dev"
}
// Sets up a dependency configuration called 'localRuntime'.
// This configuration should be used instead of 'runtimeOnly' to declare
// a dependency that will be present for runtime testing but that is
// "optional", meaning it will not be pulled by dependents of this mod.
configurations {
runtimeClasspath.extendsFrom localRuntime
}
dependencies {
minecraft "net.minecraftforge:forge:${project.mcVersion}-${project.forgeVersion}"
// Example optional mod dependency with JEI
// The JEI API is declared for compile time use, while the full JEI artifact is used at runtime
// compileOnly "mezz.jei:jei-${mc_version}-common-api:${jei_version}"
// compileOnly "mezz.jei:jei-${mc_version}-neoforge-api:${jei_version}"
// We add the full version to localRuntime, not runtimeOnly, so that we do not publish a dependency on it
// localRuntime "mezz.jei:jei-${mc_version}-neoforge:${jei_version}"
// JEI
if (project.hasProperty('jeiVersion')) {
// compile against JEI API but do not include it at runtime
compileOnly fg.deobf("mezz.jei:jei-${project.mcVersion}-forge-api:${project.jeiVersion}")
// at runtime, use full JEI jar
runtimeOnly fg.deobf("mezz.jei:jei-${project.mcVersion}-forge:${project.jeiVersion}")
// Example mod dependency using a mod jar from ./libs with a flat dir repository
// This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar
// The group id is ignored when searching -- in this case, it is "blank"
// implementation "blank:coolmod-${mc_version}:${coolmod_version}"
// Example mod dependency using a file as dependency
// implementation files("libs/coolmod-${mc_version}-${coolmod_version}.jar")
// Example project dependency using a sister or child project:
// implementation project(":myproject")
// For more info:
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
// http://www.gradle.org/docs/current/userguide/dependency_management.html
}
// This block of code expands all declared replace properties in the specified resource targets.
// A missing property will result in an error. Properties are expanded using ${} Groovy notation.
// When "copyIdeResources" is enabled, this will also run before the game launches in IDE environments.
// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html
tasks.withType(ProcessResources).configureEach {
var replaceProperties = [
minecraft_version : minecraft_version,
minecraft_version_range: minecraft_version_range,
neo_version : neo_version,
neo_version_range : neo_version_range,
loader_version_range : loader_version_range,
mod_id : mod_id,
mod_name : mod_name,
mod_license : mod_license,
mod_version : mod_version,
mod_authors : mod_authors,
mod_description : mod_description
]
inputs.properties replaceProperties
filesMatching(['META-INF/neoforge.mods.toml']) {
expand replaceProperties
}
}
jar {
archiveFileName = "${project.archivesBaseName}-${archiveVersion}.jar"
manifest {
attributes([
"Specification-Title" : project.name,
"Specification-Vendor" : project.author,
"Specification-Version" : "1",
"Implementation-Title" : project.name,
"Implementation-Vendor" : project.author,
"Implementation-Version" : archiveVersion,
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
])
// Example configuration to allow publishing using the maven-publish plugin
publishing {
publications {
register('mavenJava', MavenPublication) {
from components.java
}
}
repositories {
maven {
url "file://${project.projectDir}/repo"
}
}
}
jar.finalizedBy('reobfJar')
tasks.withType(JavaCompile).configureEach {
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
}
// IDEA no longer automatically downloads sources/javadoc jars for dependencies, so we need to explicitly enable the behavior.
idea {
module {
downloadSources = true
downloadJavadoc = true
}
}

View File

@ -1,14 +1,23 @@
org.gradle.jvmargs=-Xmx3G
org.gradle.daemon=false
org.gradle.daemon=true
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.configuration-cache=true
group = dev.micle
archivesBaseName = micles-totem-of-reviving-forge
modID = totemofreviving
name = Micle's Totem of Reviving
author = Micle
mod_id=totem_of_reviving
mod_name=Micle's Totem of Reviving
mod_license=All Rights Reserved
mod_version=3.0.0
mod_group_id=dev.micle
mod_authors=Micle
mod_description=Totems to revive your friends in hardcore.
buildVersion = 2.0.0
mcVersion = 1.20.1
forgeVersion = 47.2.20
minecraft_version=1.21.1
minecraft_version_range=[1.21.1,1.22)
#jeiVersion = 11.+
neo_version=21.1.217
neo_version_range=[21.1.217,21.2)
loader_version_range=[1,)
parchment_minecraft_version=1.21.1
parchment_mappings_version=2024.11.17

View File

@ -1,5 +1,7 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -1 +1,16 @@
rootProject.name = 'TotemOfReviving-1.20'
pluginManagement {
repositories {
gradlePluginPortal()
maven {
name = 'MinecraftForge'
url = 'https://maven.minecraftforge.net/'
}
maven { url = 'https://maven.parchmentmc.org' }
}
}
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0'
}
rootProject.name = "${mod_id}-${minecraft_version}"

View File

@ -1,6 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "totemofreviving:item/diamond_charge"
"layer0": "totem_of_reviving:item/diamond_charge"
}
}

View File

@ -1,6 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "totemofreviving:item/diamond_totem"
"layer0": "totem_of_reviving:item/diamond_totem"
}
}

View File

@ -1,6 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "totemofreviving:item/iron_charge"
"layer0": "totem_of_reviving:item/iron_charge"
}
}

View File

@ -1,6 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "totemofreviving:item/iron_totem"
"layer0": "totem_of_reviving:item/iron_totem"
}
}

View File

@ -1,6 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "totemofreviving:item/netherite_charge"
"layer0": "totem_of_reviving:item/netherite_charge"
}
}

View File

@ -1,6 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "totemofreviving:item/netherite_totem"
"layer0": "totem_of_reviving:item/netherite_totem"
}
}

View File

@ -1,6 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "totemofreviving:item/straw_charge"
"layer0": "totem_of_reviving:item/straw_charge"
}
}

View File

@ -1,6 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "totemofreviving:item/straw_totem"
"layer0": "totem_of_reviving:item/straw_totem"
}
}

View File

@ -0,0 +1,32 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_item": {
"conditions": {
"items": [
{
"items": "totem_of_reviving:diamond_totem"
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "totem_of_reviving:diamond_charge"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_the_recipe",
"has_item"
]
],
"rewards": {
"recipes": [
"totem_of_reviving:diamond_charge"
]
}
}

View File

@ -0,0 +1,32 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_item": {
"conditions": {
"items": [
{
"items": "minecraft:totem_of_undying"
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "totem_of_reviving:diamond_totem"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_the_recipe",
"has_item"
]
],
"rewards": {
"recipes": [
"totem_of_reviving:diamond_totem"
]
}
}

View File

@ -5,9 +5,7 @@
"conditions": {
"items": [
{
"items": [
"totemofreviving:iron_totem"
]
"items": "totem_of_reviving:iron_totem"
}
]
},
@ -15,21 +13,20 @@
},
"has_the_recipe": {
"conditions": {
"recipe": "totemofreviving:iron_charge"
"recipe": "totem_of_reviving:iron_charge"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
"has_the_recipe",
"has_item"
]
],
"rewards": {
"recipes": [
"totemofreviving:iron_charge"
"totem_of_reviving:iron_charge"
]
},
"sends_telemetry_event": false
}
}

View File

@ -5,9 +5,7 @@
"conditions": {
"items": [
{
"items": [
"minecraft:emerald"
]
"items": "minecraft:emerald"
}
]
},
@ -15,21 +13,20 @@
},
"has_the_recipe": {
"conditions": {
"recipe": "totemofreviving:iron_totem"
"recipe": "totem_of_reviving:iron_totem"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
"has_the_recipe",
"has_item"
]
],
"rewards": {
"recipes": [
"totemofreviving:iron_totem"
"totem_of_reviving:iron_totem"
]
},
"sends_telemetry_event": false
}
}

View File

@ -0,0 +1,32 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_item": {
"conditions": {
"items": [
{
"items": "totem_of_reviving:netherite_totem"
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "totem_of_reviving:netherite_charge"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_the_recipe",
"has_item"
]
],
"rewards": {
"recipes": [
"totem_of_reviving:netherite_charge"
]
}
}

View File

@ -0,0 +1,32 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_item": {
"conditions": {
"items": [
{
"items": "minecraft:netherite_scrap"
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "totem_of_reviving:netherite_totem"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_the_recipe",
"has_item"
]
],
"rewards": {
"recipes": [
"totem_of_reviving:netherite_totem"
]
}
}

View File

@ -5,9 +5,7 @@
"conditions": {
"items": [
{
"items": [
"totemofreviving:straw_totem"
]
"items": "totem_of_reviving:straw_totem"
}
]
},
@ -15,21 +13,20 @@
},
"has_the_recipe": {
"conditions": {
"recipe": "totemofreviving:straw_charge"
"recipe": "totem_of_reviving:straw_charge"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
"has_the_recipe",
"has_item"
]
],
"rewards": {
"recipes": [
"totemofreviving:straw_charge"
"totem_of_reviving:straw_charge"
]
},
"sends_telemetry_event": false
}
}

View File

@ -5,9 +5,7 @@
"conditions": {
"items": [
{
"items": [
"minecraft:wheat"
]
"items": "minecraft:wheat"
}
]
},
@ -15,21 +13,20 @@
},
"has_the_recipe": {
"conditions": {
"recipe": "totemofreviving:straw_totem"
"recipe": "totem_of_reviving:straw_totem"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
"has_the_recipe",
"has_item"
]
],
"rewards": {
"recipes": [
"totemofreviving:straw_totem"
"totem_of_reviving:straw_totem"
]
},
"sends_telemetry_event": false
}
}

View File

@ -21,7 +21,7 @@
"IDI"
],
"result": {
"item": "totemofreviving:diamond_charge"
},
"show_notification": true
"count": 1,
"id": "totem_of_reviving:diamond_charge"
}
}

View File

@ -18,7 +18,7 @@
"EDE"
],
"result": {
"item": "totemofreviving:diamond_totem"
},
"show_notification": true
"count": 1,
"id": "totem_of_reviving:diamond_totem"
}
}

View File

@ -18,7 +18,7 @@
"NIN"
],
"result": {
"item": "totemofreviving:iron_charge"
},
"show_notification": true
"count": 1,
"id": "totem_of_reviving:iron_charge"
}
}

View File

@ -18,7 +18,7 @@
" I "
],
"result": {
"item": "totemofreviving:iron_totem"
},
"show_notification": true
"count": 1,
"id": "totem_of_reviving:iron_totem"
}
}

View File

@ -3,7 +3,7 @@
"category": "equipment",
"key": {
"C": {
"item": "totemofreviving:diamond_charge"
"item": "totem_of_reviving:diamond_charge"
},
"G": {
"item": "minecraft:gold_ingot"
@ -18,7 +18,7 @@
"GNG"
],
"result": {
"item": "totemofreviving:netherite_charge"
},
"show_notification": true
"count": 1,
"id": "totem_of_reviving:netherite_charge"
}
}

View File

@ -12,7 +12,7 @@
"item": "minecraft:netherite_scrap"
},
"T": {
"item": "totemofreviving:diamond_totem"
"item": "totem_of_reviving:diamond_totem"
}
},
"pattern": [
@ -21,7 +21,7 @@
"BGB"
],
"result": {
"item": "totemofreviving:netherite_totem"
},
"show_notification": true
"count": 1,
"id": "totem_of_reviving:netherite_totem"
}
}

View File

@ -18,7 +18,7 @@
"NWN"
],
"result": {
"item": "totemofreviving:straw_charge"
},
"show_notification": true
"count": 1,
"id": "totem_of_reviving:straw_charge"
}
}

View File

@ -21,7 +21,7 @@
"N|N"
],
"result": {
"item": "totemofreviving:straw_totem"
},
"show_notification": true
"count": 1,
"id": "totem_of_reviving:straw_totem"
}
}

View File

@ -1,35 +0,0 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_item": {
"conditions": {
"items": [
{
"items": [
"totemofreviving:diamond_totem"
]
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "totemofreviving:diamond_charge"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
]
],
"rewards": {
"recipes": [
"totemofreviving:diamond_charge"
]
},
"sends_telemetry_event": false
}

View File

@ -1,35 +0,0 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_item": {
"conditions": {
"items": [
{
"items": [
"minecraft:totem_of_undying"
]
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "totemofreviving:diamond_totem"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
]
],
"rewards": {
"recipes": [
"totemofreviving:diamond_totem"
]
},
"sends_telemetry_event": false
}

View File

@ -1,35 +0,0 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_item": {
"conditions": {
"items": [
{
"items": [
"totemofreviving:netherite_totem"
]
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "totemofreviving:netherite_charge"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
]
],
"rewards": {
"recipes": [
"totemofreviving:netherite_charge"
]
},
"sends_telemetry_event": false
}

View File

@ -1,35 +0,0 @@
{
"parent": "minecraft:recipes/root",
"criteria": {
"has_item": {
"conditions": {
"items": [
{
"items": [
"minecraft:netherite_scrap"
]
}
]
},
"trigger": "minecraft:inventory_changed"
},
"has_the_recipe": {
"conditions": {
"recipe": "totemofreviving:netherite_totem"
},
"trigger": "minecraft:recipe_unlocked"
}
},
"requirements": [
[
"has_item",
"has_the_recipe"
]
],
"rewards": {
"recipes": [
"totemofreviving:netherite_totem"
]
},
"sends_telemetry_event": false
}

View File

@ -0,0 +1,45 @@
package dev.micle.totem_of_reviving;
import dev.micle.totem_of_reviving.proxy.IProxy;
import dev.micle.totem_of_reviving.proxy.Proxy;
import net.minecraft.resources.ResourceLocation;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.ModContainer;
import net.neoforged.fml.ModList;
import net.neoforged.fml.common.Mod;
import net.neoforged.fml.loading.FMLEnvironment;
import java.util.Optional;
@Mod(TotemOfReviving.MOD_ID)
public final class TotemOfReviving {
public static final String MOD_ID = "totem_of_reviving";
private static IProxy proxy;
public TotemOfReviving(IEventBus modEventBus, ModContainer modContainer) {
proxy = FMLEnvironment.dist == Dist.CLIENT
? new Proxy.Client(modEventBus, modContainer)
: new Proxy.Server(modEventBus, modContainer);
}
public static ResourceLocation createResourceLocation(String name) {
if (name.contains(":")) {
throw new IllegalArgumentException("Name contains namespace");
}
return ResourceLocation.fromNamespaceAndPath(MOD_ID, name);
}
public static String getVersion() {
Optional<? extends ModContainer> optional = ModList.get().getModContainerById(MOD_ID);
if (optional.isPresent()) {
return optional.get().getModInfo().getVersion().toString();
}
return "0.0.0";
}
public static IProxy getProxy() {
return proxy;
}
}

View File

@ -0,0 +1,46 @@
package dev.micle.totem_of_reviving.component;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import java.util.Objects;
public class TotemConfigHashData {
public static final Codec<TotemConfigHashData> CODEC = RecordCodecBuilder.create(instance ->
instance.group(
Codec.INT.fieldOf("configHash").forGetter(TotemConfigHashData::getConfigHash)
).apply(instance, TotemConfigHashData::new)
);
public static final StreamCodec<ByteBuf, TotemConfigHashData> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.INT, TotemConfigHashData::getConfigHash,
TotemConfigHashData::new
);
private final int configHash;
public TotemConfigHashData(int configHash) {
this.configHash = configHash;
}
public int getConfigHash() {
return this.configHash;
}
@Override
public int hashCode() {
return Objects.hash(this.configHash);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else {
return obj instanceof TotemConfigHashData tchd
&& this.configHash == tchd.configHash;
}
}
}

View File

@ -0,0 +1,92 @@
package dev.micle.totem_of_reviving.component;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import io.netty.buffer.ByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
public class TotemData {
public static final Codec<TotemData> CODEC = RecordCodecBuilder.create(instance ->
instance.group(
Codec.INT.fieldOf("targetIndex").forGetter(TotemData::getTargetIndex),
Codec.STRING.fieldOf("targetUUID").forGetter(TotemData::getTargetStringUUID),
Codec.STRING.fieldOf("targetName").forGetter(TotemData::getTargetName),
Codec.INT.fieldOf("targetDeaths").forGetter(TotemData::getTargetDeaths),
Codec.INT.fieldOf("charge").forGetter(TotemData::getCharge)
).apply(instance, TotemData::new)
);
public static final StreamCodec<ByteBuf, TotemData> STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.INT, TotemData::getTargetIndex,
ByteBufCodecs.STRING_UTF8, TotemData::getTargetStringUUID,
ByteBufCodecs.STRING_UTF8, TotemData::getTargetName,
ByteBufCodecs.INT, TotemData::getTargetDeaths,
ByteBufCodecs.INT, TotemData::getCharge,
TotemData::new
);
private final int targetIndex;
private final String targetUUID;
private final String targetName;
private final int targetDeaths;
private final int charge;
public TotemData(int targetIndex, String targetUUID, String targetName, int targetDeaths, int charge) {
this.targetIndex = targetIndex;
this.targetUUID = targetUUID;
this.targetName = targetName;
this.targetDeaths = targetDeaths;
this.charge = charge;
}
public int getTargetIndex() {
return targetIndex;
}
public String getTargetStringUUID() {
return targetUUID;
}
public Optional<UUID> getTargetUUID() {
Optional<UUID> targetUUID = Optional.empty();
try {
targetUUID = Optional.of(UUID.fromString(this.targetUUID));
} catch (IllegalArgumentException ignored) {}
return targetUUID;
}
public String getTargetName() {
return targetName;
}
public int getTargetDeaths() {
return targetDeaths;
}
public int getCharge() {
return charge;
}
@Override
public int hashCode() {
return Objects.hash(this.targetIndex, this.targetUUID, this.targetName, this.targetDeaths, this.charge);
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else {
return obj instanceof TotemData td
&& this.targetIndex == td.targetIndex
&& Objects.equals(this.targetUUID, td.targetUUID)
&& Objects.equals(this.targetName, td.targetName)
&& this.targetDeaths == td.targetDeaths
&& this.charge == td.charge;
}
}
}

View File

@ -0,0 +1,21 @@
package dev.micle.totem_of_reviving.data;
import dev.micle.totem_of_reviving.data.client.ModItemModelProvider;
import net.minecraft.data.DataGenerator;
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.common.data.ExistingFileHelper;
import net.neoforged.neoforge.data.event.GatherDataEvent;
@EventBusSubscriber
public class DataGenerators {
@SubscribeEvent
public static void gatherData(GatherDataEvent event) {
DataGenerator generator = event.getGenerator();
ExistingFileHelper existingFileHelper = event.getExistingFileHelper();
generator.addProvider(event.includeServer(), new ModItemModelProvider(generator, existingFileHelper));
generator.addProvider(event.includeServer(), new ModRecipeProvider(generator.getPackOutput(), event.getLookupProvider()));
}
}

View File

@ -1,24 +1,25 @@
package dev.micle.totemofreviving.data;
package dev.micle.totem_of_reviving.data;
import dev.micle.totemofreviving.setup.ModItems;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.recipes.FinishedRecipe;
import dev.micle.totem_of_reviving.setup.ModItems;
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 javax.annotation.ParametersAreNonnullByDefault;
import java.util.function.Consumer;
import java.util.concurrent.CompletableFuture;
public class ModRecipeProvider extends RecipeProvider {
public ModRecipeProvider(DataGenerator generator) {
super(generator.getPackOutput());
public ModRecipeProvider(PackOutput packOutput, CompletableFuture<HolderLookup.Provider> registries) {
super(packOutput, registries);
}
@Override
@ParametersAreNonnullByDefault
protected void buildRecipes(Consumer<FinishedRecipe> consumer) {
@Override
protected void buildRecipes(RecipeOutput consumer) {
ShapedRecipeBuilder.shaped(RecipeCategory.TOOLS, ModItems.STRAW_TOTEM.get())
.define('W', Items.WHEAT)
.define('|', Items.STICK)

View File

@ -1,19 +1,19 @@
package dev.micle.totemofreviving.data.client;
package dev.micle.totem_of_reviving.data.client;
import dev.micle.totemofreviving.TotemOfReviving;
import dev.micle.totemofreviving.item.charge.DiamondChargeItem;
import dev.micle.totemofreviving.item.charge.IronChargeItem;
import dev.micle.totemofreviving.item.charge.NetheriteChargeItem;
import dev.micle.totemofreviving.item.charge.StrawChargeItem;
import dev.micle.totemofreviving.item.totem.DiamondTotemItem;
import dev.micle.totemofreviving.item.totem.IronTotemItem;
import dev.micle.totemofreviving.item.totem.NetheriteTotemItem;
import dev.micle.totemofreviving.item.totem.StrawTotemItem;
import dev.micle.totem_of_reviving.TotemOfReviving;
import dev.micle.totem_of_reviving.item.charge.DiamondChargeItem;
import dev.micle.totem_of_reviving.item.charge.IronChargeItem;
import dev.micle.totem_of_reviving.item.charge.NetheriteChargeItem;
import dev.micle.totem_of_reviving.item.charge.StrawChargeItem;
import dev.micle.totem_of_reviving.item.totem.DiamondTotemItem;
import dev.micle.totem_of_reviving.item.totem.IronTotemItem;
import dev.micle.totem_of_reviving.item.totem.NetheriteTotemItem;
import dev.micle.totem_of_reviving.item.totem.StrawTotemItem;
import net.minecraft.data.DataGenerator;
import net.minecraftforge.client.model.generators.ItemModelBuilder;
import net.minecraftforge.client.model.generators.ItemModelProvider;
import net.minecraftforge.client.model.generators.ModelFile;
import net.minecraftforge.common.data.ExistingFileHelper;
import net.neoforged.neoforge.client.model.generators.ItemModelBuilder;
import net.neoforged.neoforge.client.model.generators.ItemModelProvider;
import net.neoforged.neoforge.client.model.generators.ModelFile;
import net.neoforged.neoforge.common.data.ExistingFileHelper;
public class ModItemModelProvider extends ItemModelProvider {
public ModItemModelProvider(DataGenerator generator, ExistingFileHelper existingFileHelper) {

View File

@ -0,0 +1,37 @@
package dev.micle.totem_of_reviving.item.charge;
import dev.micle.totem_of_reviving.util.LangAsset;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Rarity;
import net.minecraft.world.item.TooltipFlag;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.List;
public abstract class ChargeItem extends Item {
public ChargeItem(Rarity rarity, boolean isFireResistant) {
super(createProperties(rarity, isFireResistant));
}
public abstract Component getTotemDisplayName();
public static Properties createProperties(Rarity rarity, boolean isFireResistant) {
Properties properties = new Properties().stacksTo(64).rarity(rarity);
if (isFireResistant) properties = properties.fireResistant();
return properties;
}
@Override
@ParametersAreNonnullByDefault
public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
tooltipComponents.add(LangAsset.TOOLTIP_CHARGE.getComponent(
getTotemDisplayName()
).withStyle(ChatFormatting.GRAY));
super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
}
}

View File

@ -0,0 +1,20 @@
package dev.micle.totem_of_reviving.item.charge;
import dev.micle.totem_of_reviving.setup.ModItems;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.Rarity;
public class DiamondChargeItem extends ChargeItem {
public DiamondChargeItem() {
super(Rarity.RARE, false);
}
public static String getName() {
return "diamond_charge";
}
@Override
public Component getTotemDisplayName() {
return ModItems.DIAMOND_TOTEM.get().getDefaultInstance().getDisplayName();
}
}

View File

@ -0,0 +1,20 @@
package dev.micle.totem_of_reviving.item.charge;
import dev.micle.totem_of_reviving.setup.ModItems;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.Rarity;
public class IronChargeItem extends ChargeItem {
public IronChargeItem() {
super(Rarity.UNCOMMON, false);
}
public static String getName() {
return "iron_charge";
}
@Override
public Component getTotemDisplayName() {
return ModItems.IRON_TOTEM.get().getDefaultInstance().getDisplayName();
}
}

View File

@ -0,0 +1,20 @@
package dev.micle.totem_of_reviving.item.charge;
import dev.micle.totem_of_reviving.setup.ModItems;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.Rarity;
public class NetheriteChargeItem extends ChargeItem {
public NetheriteChargeItem() {
super(Rarity.EPIC, true);
}
public static String getName() {
return "netherite_charge";
}
@Override
public Component getTotemDisplayName() {
return ModItems.NETHERITE_TOTEM.get().getDefaultInstance().getDisplayName();
}
}

View File

@ -0,0 +1,20 @@
package dev.micle.totem_of_reviving.item.charge;
import dev.micle.totem_of_reviving.setup.ModItems;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.Rarity;
public class StrawChargeItem extends ChargeItem {
public StrawChargeItem() {
super(Rarity.COMMON, false);
}
public static String getName() {
return "straw_charge";
}
@Override
public Component getTotemDisplayName() {
return ModItems.STRAW_TOTEM.get().getDefaultInstance().getDisplayName();
}
}

View File

@ -1,13 +1,13 @@
package dev.micle.totemofreviving.item.totem;
package dev.micle.totem_of_reviving.item.totem;
import dev.micle.totemofreviving.item.charge.DiamondChargeItem;
import dev.micle.totemofreviving.setup.Config;
import dev.micle.totem_of_reviving.item.charge.DiamondChargeItem;
import dev.micle.totem_of_reviving.setup.Config;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Rarity;
public class DiamondTotemItem extends TotemItem {
public DiamondTotemItem() {
super(Rarity.RARE, Config.Server.getDiamondTotemConfig().getDurability());
super(Rarity.RARE, false);
}
public static String getName() {
@ -18,4 +18,9 @@ public class DiamondTotemItem extends TotemItem {
public boolean isCharge(ItemStack stack) {
return (stack.getItem() instanceof DiamondChargeItem);
}
@Override
public Config.TotemConfig getConfig() {
return Config.Server.getDiamondTotemConfig();
}
}

View File

@ -0,0 +1,26 @@
package dev.micle.totem_of_reviving.item.totem;
import dev.micle.totem_of_reviving.item.charge.IronChargeItem;
import dev.micle.totem_of_reviving.setup.Config;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Rarity;
public class IronTotemItem extends TotemItem {
public IronTotemItem() {
super(Rarity.UNCOMMON, false);
}
public static String getName() {
return "iron_totem";
}
@Override
public boolean isCharge(ItemStack stack) {
return (stack.getItem() instanceof IronChargeItem);
}
@Override
public Config.TotemConfig getConfig() {
return Config.Server.getIronTotemConfig();
}
}

View File

@ -1,13 +1,13 @@
package dev.micle.totemofreviving.item.totem;
package dev.micle.totem_of_reviving.item.totem;
import dev.micle.totemofreviving.item.charge.NetheriteChargeItem;
import dev.micle.totemofreviving.setup.Config;
import dev.micle.totem_of_reviving.item.charge.NetheriteChargeItem;
import dev.micle.totem_of_reviving.setup.Config;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Rarity;
public class NetheriteTotemItem extends TotemItem {
public NetheriteTotemItem() {
super(Rarity.EPIC, Config.Server.getNetheriteTotemConfig().getDurability());
super(Rarity.EPIC, true);
}
public static String getName() {
@ -20,7 +20,7 @@ public class NetheriteTotemItem extends TotemItem {
}
@Override
public boolean isFireResistant() {
return true;
public Config.TotemConfig getConfig() {
return Config.Server.getNetheriteTotemConfig();
}
}

View File

@ -1,13 +1,13 @@
package dev.micle.totemofreviving.item.totem;
package dev.micle.totem_of_reviving.item.totem;
import dev.micle.totemofreviving.item.charge.StrawChargeItem;
import dev.micle.totemofreviving.setup.Config;
import dev.micle.totem_of_reviving.item.charge.StrawChargeItem;
import dev.micle.totem_of_reviving.setup.Config;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Rarity;
public class StrawTotemItem extends TotemItem {
public StrawTotemItem() {
super(Rarity.UNCOMMON, Config.Server.getStrawTotemConfig().getDurability());
super(Rarity.COMMON, false);
}
public static String getName() {
@ -18,4 +18,9 @@ public class StrawTotemItem extends TotemItem {
public boolean isCharge(ItemStack stack) {
return (stack.getItem() instanceof StrawChargeItem);
}
@Override
public Config.TotemConfig getConfig() {
return Config.Server.getStrawTotemConfig();
}
}

View File

@ -0,0 +1,321 @@
package dev.micle.totem_of_reviving.item.totem;
import com.mojang.blaze3d.platform.InputConstants;
import dev.micle.totem_of_reviving.component.TotemConfigHashData;
import dev.micle.totem_of_reviving.component.TotemData;
import dev.micle.totem_of_reviving.setup.Config;
import dev.micle.totem_of_reviving.setup.ModDataComponents;
import dev.micle.totem_of_reviving.util.ComponentHelper;
import dev.micle.totem_of_reviving.util.LangAsset;
import net.minecraft.ChatFormatting;
import net.minecraft.client.Minecraft;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.players.PlayerList;
import net.minecraft.stats.Stats;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EquipmentSlot;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Rarity;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.item.component.Unbreakable;
import net.minecraft.world.level.Level;
import org.jetbrains.annotations.NotNull;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
public abstract class TotemItem extends Item {
public TotemItem(Rarity rarity, boolean isFireResistant) {
super(createProperties(rarity, isFireResistant));
}
public static Properties createProperties(Rarity rarity, boolean isFireResistant) {
Properties properties = new Properties().stacksTo(1).rarity(rarity);
if (isFireResistant) properties = properties.fireResistant();
properties = properties.component(ModDataComponents.TOTEM_DATA.get(), new TotemData(
0, "", "", 0, 0
));
return properties;
}
public abstract boolean isCharge(ItemStack stack);
public abstract Config.TotemConfig getConfig();
private boolean isChargeFull(TotemData totemData) {
return totemData.getCharge() >= getMaxCharge();
}
private boolean isChargeCostDynamic() {
return getConfig().getChargeCost() == -1;
}
private boolean isUnbreakable() {
return getConfig().getDurability() == 0;
}
private int getMaxCharge() {
return isChargeCostDynamic()
? getConfig().getChargeCostLimit()
: getConfig().getChargeCost();
}
private int getTargetCost(TotemData totemData) {
return isChargeCostDynamic()
? Math.max(1, (int)(totemData.getTargetDeaths() * getConfig().getChargeCostMultiplier()))
: getConfig().getChargeCost();
}
private boolean canAffordTarget(TotemData totemData) {
return totemData.getCharge() >= getTargetCost(totemData) || (isChargeFull(totemData) && getConfig().getCanReviveMoreExpensiveTargets());
}
private static TotemData getTotemData(ItemStack itemStack) {
return itemStack.getComponents().get(ModDataComponents.TOTEM_DATA.get());
}
private static void setTotemData(ItemStack itemStack, TotemData totemData) {
itemStack.set(ModDataComponents.TOTEM_DATA.get(), totemData);
}
public static boolean isTotem(ItemStack stack) {
return (stack.getItem() instanceof TotemItem);
}
public static Component cycleTarget(ItemStack itemStack, PlayerList playerList) {
if (!isTotem(itemStack)) return Component.empty();
TotemData totemData = getTotemData(itemStack);
int targetIndex = totemData.getTargetIndex() + 1;
targetIndex = targetIndex >= playerList.getPlayerCount() ? 0 : targetIndex;
ServerPlayer target = playerList.getPlayers().get(targetIndex);
totemData = new TotemData(
targetIndex,
target.getStringUUID(),
target.getScoreboardName(),
target.getStats().getValue(Stats.CUSTOM.get(Stats.DEATHS)),
totemData.getCharge()
);
setTotemData(itemStack, totemData);
return LangAsset.MESSAGE_NOW_TARGETING.getComponent(
Component.literal(totemData.getTargetName()).withStyle(ChatFormatting.WHITE)
).withStyle(ChatFormatting.GRAY);
}
public static boolean chargeTotem(ItemStack totemStack, ItemStack chargeStack) {
if (!isTotem(totemStack)) return false;
TotemData totemData = getTotemData(totemStack);
TotemItem totemItem = ((TotemItem) totemStack.getItem());
if (!totemItem.isCharge(chargeStack)) return false;
if (totemItem.isChargeFull(totemData)) return false;
setTotemData(totemStack, new TotemData(
totemData.getTargetIndex(),
totemData.getTargetStringUUID(),
totemData.getTargetName(),
totemData.getTargetDeaths(),
totemData.getCharge() + 1
));
chargeStack.setCount(chargeStack.getCount() - 1);
return true;
}
public static Component reviveTarget(EquipmentSlot slot, ItemStack itemStack, ServerPlayer user, PlayerList playerList) {
if (!isTotem(itemStack)) return Component.empty();
TotemData totemData = getTotemData(itemStack);
TotemItem totemItem = ((TotemItem) itemStack.getItem());
Config.TotemConfig config = totemItem.getConfig();
Optional<UUID> targetUUID = totemData.getTargetUUID();
if (targetUUID.isEmpty()) {
return LangAsset.MESSAGE_UNABLE_TO_FIND_PLAYER.getComponent().withStyle(ChatFormatting.RED);
}
ServerPlayer target = playerList.getPlayer(targetUUID.get());
if (target == null) {
return LangAsset.MESSAGE_UNABLE_TO_FIND_PLAYER.getComponent().withStyle(ChatFormatting.RED);
}
totemData = new TotemData(
totemData.getTargetIndex(),
totemData.getTargetStringUUID(),
totemData.getTargetName(),
target.getStats().getValue(Stats.CUSTOM.get(Stats.DEATHS)),
totemData.getCharge()
);
setTotemData(itemStack, totemData);
if (!target.isSpectator()) {
return LangAsset.MESSAGE_PLAYER_IS_NOT_DEAD.getComponent(
Component.literal(totemData.getTargetName()).withStyle(ChatFormatting.WHITE)
).withStyle(ChatFormatting.GRAY);
}
if (!target.serverLevel().equals(user.serverLevel()) && !config.getCanReviveAcrossDimensions()) {
return LangAsset.MESSAGE_PLAYER_IS_IN_ANOTHER_DIMENSION.getComponent(
Component.literal(totemData.getTargetName()).withStyle(ChatFormatting.WHITE)
).withStyle(ChatFormatting.GRAY);
}
if (!totemItem.canAffordTarget(totemData)) {
return LangAsset.MESSAGE_NOT_ENOUGH_CHARGE.getComponent().withStyle(ChatFormatting.WHITE);
}
target.teleportTo(user.serverLevel(), user.position().x, user.position().y, user.position().z, user.getYRot(), user.getXRot());
target.setGameMode(user.serverLevel().getServer().getDefaultGameType());
totemData = new TotemData(
totemData.getTargetIndex(),
totemData.getTargetStringUUID(),
totemData.getTargetName(),
totemData.getTargetDeaths(),
Math.max(0, totemData.getCharge() - totemItem.getTargetCost(totemData))
);
setTotemData(itemStack, totemData);
itemStack.hurtAndBreak(1, user, slot);
return LangAsset.MESSAGE_SUCCESSFULLY_REVIVED_TARGET.getComponent(
Component.literal(totemData.getTargetName()).withStyle(ChatFormatting.GREEN)
).withStyle(ChatFormatting.DARK_GREEN);
}
@Override
@ParametersAreNonnullByDefault
public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> tooltipComponents, TooltipFlag tooltipFlag) {
Minecraft minecraft = Minecraft.getInstance();
TotemData totemData = getTotemData(stack);
tooltipComponents.add(LangAsset.TOOLTIP_TOTEM_TARGET.getComponent(
totemData.getTargetUUID().isPresent()
? Component.literal(totemData.getTargetName()).withStyle(ChatFormatting.WHITE)
: LangAsset.GENERAL_NA.getComponent().withStyle(ChatFormatting.RED)
).withStyle(ChatFormatting.GRAY));
tooltipComponents.add(LangAsset.TOOLTIP_TOTEM_TARGET_COST.getComponent(
totemData.getTargetUUID().isPresent()
? Component.literal(String.format("%d", getTargetCost(totemData))).withStyle(
getTargetCost(totemData) <= getMaxCharge() || (getConfig().getCanReviveMoreExpensiveTargets())
? ChatFormatting.WHITE
: ChatFormatting.RED
)
: LangAsset.GENERAL_NA.getComponent().withStyle(ChatFormatting.RED)
).withStyle(ChatFormatting.GRAY));
tooltipComponents.add(LangAsset.TOOLTIP_TOTEM_CHARGES.getComponent(
Component.literal(String.format("%d / %d", totemData.getCharge(), getMaxCharge())).withStyle(ChatFormatting.WHITE)
).withStyle(ChatFormatting.GRAY));
tooltipComponents.add(
isUnbreakable()
? LangAsset.GENERAL_UNBREAKABLE.getComponent().withStyle(ChatFormatting.WHITE)
: LangAsset.TOOLTIP_TOTEM_DURABILITY.getComponent(
Component.literal(String.format("%d / %d", getConfig().getDurability() - stack.getDamageValue(), getConfig().getDurability())).withStyle(ChatFormatting.WHITE)
).withStyle(ChatFormatting.GRAY)
);
tooltipComponents.add(Component.empty());
if (isChargeCostDynamic()) {
tooltipComponents.add(LangAsset.TOOLTIP_TOTEM_DYNAMIC_COST.getComponent(
Component.literal(String.format("%.2f", getConfig().getChargeCostMultiplier())).withStyle(ChatFormatting.WHITE)
).withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC));
} else {
tooltipComponents.add(LangAsset.TOOLTIP_TOTEM_STATIC_COST.getComponent(
Component.literal(String.format("%d", getMaxCharge())).withStyle(ChatFormatting.WHITE)
).withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC));
}
if (getConfig().getCanReviveMoreExpensiveTargets()) {
tooltipComponents.add(LangAsset.TOOLTIP_TOTEM_CAN_REVIVE_MORE_EXPENSIVE_TARGETS.getComponent().withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC));
}
if (getConfig().getCanReviveAcrossDimensions()) {
tooltipComponents.add(LangAsset.TOOLTIP_TOTEM_CAN_REVIVE_ACROSS_DIMENSIONS.getComponent().withStyle(ChatFormatting.GRAY, ChatFormatting.ITALIC));
}
tooltipComponents.add(Component.empty());
if (InputConstants.isKeyDown(minecraft.getWindow().getWindow(), minecraft.options.keyShift.getKey().getValue())) {
tooltipComponents.add(LangAsset.TOOLTIP_ADVANCED_TOOLTIP_SHOWN.getComponent().withStyle(ChatFormatting.DARK_GRAY, ChatFormatting.UNDERLINE));
tooltipComponents.add(ComponentHelper.createKeyText(false, new ChatFormatting[]{ChatFormatting.GOLD}, new ChatFormatting[]{ChatFormatting.GOLD, ChatFormatting.ITALIC}, minecraft.options.keyUse));
tooltipComponents.add(ComponentHelper.addIndentToComponent(LangAsset.TOOLTIP_TOTEM_REVIVE_INSTRUCTION.getComponent().withStyle(ChatFormatting.GRAY), 1));
tooltipComponents.add(ComponentHelper.addIndentToComponent(LangAsset.TOOLTIP_TOTEM_CHARGE_INSTRUCTION.getComponent().withStyle(ChatFormatting.GRAY), 1));
tooltipComponents.add(Component.empty());
tooltipComponents.add(ComponentHelper.createKeyText(false, new ChatFormatting[]{ChatFormatting.GOLD}, new ChatFormatting[]{ChatFormatting.GOLD, ChatFormatting.ITALIC}, minecraft.options.keyShift, minecraft.options.keyUse));
tooltipComponents.add(ComponentHelper.addIndentToComponent(LangAsset.TOOLTIP_TOTEM_CHANGE_TARGET_INSTRUCTION.getComponent().withStyle(ChatFormatting.GRAY), 1));
} else {
tooltipComponents.add(LangAsset.TOOLTIP_ADVANCED_TOOLTIP_HIDDEN.getComponent(
ComponentHelper.createKeyText(false, new ChatFormatting[]{ChatFormatting.GOLD}, new ChatFormatting[]{ChatFormatting.GOLD, ChatFormatting.ITALIC}, minecraft.options.keyShift)
).withStyle(ChatFormatting.GRAY));
}
super.appendHoverText(stack, context, tooltipComponents, tooltipFlag);
}
@Override
@ParametersAreNonnullByDefault
public void inventoryTick(ItemStack stack, Level level, Entity entity, int slotId, boolean isSelected) {
if (level.isClientSide) {
super.inventoryTick(stack, level, entity, slotId, isSelected);
return;
}
TotemConfigHashData totemConfigHashData = stack.get(ModDataComponents.TOTEM_CONFIG_HASH_DATA);
if (totemConfigHashData == null || totemConfigHashData.getConfigHash() != getConfig().getConfigHash()) {
if (isUnbreakable()) {
stack.set(DataComponents.UNBREAKABLE, new Unbreakable(false));
stack.remove(DataComponents.MAX_DAMAGE);
stack.remove(DataComponents.DAMAGE);
} else {
stack.remove(DataComponents.UNBREAKABLE);
stack.set(DataComponents.MAX_DAMAGE, getConfig().getDurability());
stack.set(DataComponents.DAMAGE, 0);
}
stack.set(ModDataComponents.TOTEM_CONFIG_HASH_DATA, new TotemConfigHashData(getConfig().getConfigHash()));
}
super.inventoryTick(stack, level, entity, slotId, isSelected);
}
@Override
@ParametersAreNonnullByDefault
public @NotNull InteractionResultHolder<ItemStack> use(Level level, Player player, InteractionHand usedHand) {
if (level.isClientSide) return super.use(level, player, usedHand);
MinecraftServer server = level.getServer();
if (server == null) return super.use(level, player, usedHand);
EquipmentSlot slot = (usedHand.equals(InteractionHand.MAIN_HAND)) ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND;
ItemStack totemStack = (usedHand.equals(InteractionHand.MAIN_HAND)) ? player.getMainHandItem() : player.getOffhandItem();
ItemStack chargeStack = (usedHand.equals(InteractionHand.MAIN_HAND)) ? player.getOffhandItem() : player.getMainHandItem();
if (player.isCrouching()) {
// Change Target
player.sendSystemMessage(cycleTarget(totemStack, server.getPlayerList()));
} else {
if (isCharge(chargeStack)) {
// Charge Totem
chargeTotem(totemStack, chargeStack);
} else {
// Revive Target
player.sendSystemMessage(reviveTarget(slot, totemStack, server.getPlayerList().getPlayer(player.getUUID()), server.getPlayerList()));
}
}
return super.use(level, player, usedHand);
}
}

View File

@ -1,11 +1,18 @@
package dev.micle.totemofreviving.proxy;
package dev.micle.totem_of_reviving.proxy;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import javax.annotation.Nullable;
public interface IProxy {
@Nullable
MinecraftServer getServer();
@Nullable
Player getClientPlayer();
Level getClientWorld();
@Nullable
Level getClientLevel();
}

View File

@ -1,43 +1,40 @@
package dev.micle.totemofreviving.proxy;
package dev.micle.totem_of_reviving.proxy;
import dev.micle.totemofreviving.network.NetworkManager;
import dev.micle.totemofreviving.setup.Config;
import dev.micle.totemofreviving.setup.Registration;
import dev.micle.totem_of_reviving.setup.Config;
import dev.micle.totem_of_reviving.setup.Registration;
import net.minecraft.client.Minecraft;
import net.minecraft.server.MinecraftServer;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.AddReloadListenerEvent;
import net.minecraftforge.event.server.ServerStartedEvent;
import net.minecraftforge.event.server.ServerStoppingEvent;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.event.lifecycle.*;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.neoforged.api.distmarker.Dist;
import net.neoforged.api.distmarker.OnlyIn;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.ModContainer;
import net.neoforged.fml.event.lifecycle.*;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.AddReloadListenerEvent;
import net.neoforged.neoforge.event.server.ServerStartedEvent;
import net.neoforged.neoforge.event.server.ServerStoppingEvent;
public class Proxy implements IProxy {
// Initialize variables
private static MinecraftServer server = null;
// Common setup
public Proxy() {
public Proxy(IEventBus modEventBus, ModContainer modContainer) {
// Initialize setup
Registration.register();
Config.init();
NetworkManager.init();
Registration.register(modEventBus);
Config.init(modContainer);
// Register mod event bus listeners
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
modEventBus.addListener(Proxy::setup);
modEventBus.addListener(Proxy::imcEnqueue);
modEventBus.addListener(Proxy::imcProcess);
// Register event bus listeners
MinecraftForge.EVENT_BUS.addListener(Proxy::onAddReloadListeners);
MinecraftForge.EVENT_BUS.addListener(Proxy::serverStarted);
MinecraftForge.EVENT_BUS.addListener(Proxy::serverStopping);
NeoForge.EVENT_BUS.addListener(Proxy::onAddReloadListeners);
NeoForge.EVENT_BUS.addListener(Proxy::serverStarted);
NeoForge.EVENT_BUS.addListener(Proxy::serverStopping);
}
private static void setup(FMLCommonSetupEvent event) {}
@ -67,15 +64,15 @@ public class Proxy implements IProxy {
}
@Override
public Level getClientWorld() {
public Level getClientLevel() {
return null;
}
// Client setup
public static class Client extends Proxy {
public Client() {
public Client(IEventBus modEventBus, ModContainer modContainer) {
super(modEventBus, modContainer);
// Register mod event bus listeners
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
modEventBus.addListener(Client::setup);
modEventBus.addListener(Client::postSetup);
}
@ -92,16 +89,16 @@ public class Proxy implements IProxy {
@Override
@OnlyIn(Dist.CLIENT)
public Level getClientWorld() {
public Level getClientLevel() {
return Minecraft.getInstance().level;
}
}
// Server setup
public static class Server extends Proxy {
public Server() {
public Server(IEventBus modEventBus, ModContainer modContainer) {
super(modEventBus, modContainer);
// Register mod event bus listeners
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
modEventBus.addListener(Server::setup);
}

View File

@ -0,0 +1,97 @@
package dev.micle.totem_of_reviving.setup;
import dev.micle.totem_of_reviving.item.totem.DiamondTotemItem;
import dev.micle.totem_of_reviving.item.totem.IronTotemItem;
import dev.micle.totem_of_reviving.item.totem.NetheriteTotemItem;
import dev.micle.totem_of_reviving.item.totem.StrawTotemItem;
import net.neoforged.fml.ModContainer;
import net.neoforged.fml.config.ModConfig;
import net.neoforged.neoforge.common.ModConfigSpec;
import java.util.Objects;
public final class Config {
public static void init(ModContainer modContainer) {
modContainer.registerConfig(ModConfig.Type.SERVER, Server.SPEC);
}
public static class Server {
private static final ModConfigSpec SPEC;
private static final TotemConfig STRAW_TOTEM_CONFIG;
private static final TotemConfig IRON_TOTEM_CONFIG;
private static final TotemConfig DIAMOND_TOTEM_CONFIG;
private static final TotemConfig NETHERITE_TOTEM_CONFIG;
static {
ModConfigSpec.Builder builder = new ModConfigSpec.Builder();
STRAW_TOTEM_CONFIG = new TotemConfig(builder, StrawTotemItem.getName(), -1, 3,
1, false, false, 1);
IRON_TOTEM_CONFIG = new TotemConfig(builder, IronTotemItem.getName(), -1, 3,
0.5, false, false, 2);
DIAMOND_TOTEM_CONFIG = new TotemConfig(builder, DiamondTotemItem.getName(), -1, 6,
0.67, true, false, 3);
NETHERITE_TOTEM_CONFIG = new TotemConfig(builder, NetheriteTotemItem.getName(), 1, 1,
1, false, true, 0);
SPEC = builder.build();
}
public static TotemConfig getStrawTotemConfig() { return STRAW_TOTEM_CONFIG; }
public static TotemConfig getIronTotemConfig() { return IRON_TOTEM_CONFIG; }
public static TotemConfig getDiamondTotemConfig() { return DIAMOND_TOTEM_CONFIG; }
public static TotemConfig getNetheriteTotemConfig() { return NETHERITE_TOTEM_CONFIG; }
}
public static class TotemConfig {
private final ModConfigSpec.IntValue CHARGE_COST;
private final ModConfigSpec.IntValue CHARGE_COST_LIMIT;
private final ModConfigSpec.DoubleValue CHARGE_COST_MULTIPLIER;
private final ModConfigSpec.BooleanValue CAN_REVIVE_MORE_EXPENSIVE_TARGETS;
private final ModConfigSpec.BooleanValue CAN_REVIVE_ACROSS_DIMENSIONS;
private final ModConfigSpec.IntValue DURABILITY;
public TotemConfig(ModConfigSpec.Builder builder, String name, int chargeCost,
int chargeCostLimit, double chargeCostMultiplier, boolean canReviveMoreExpensiveTargets,
boolean canReviveAcrossDimensions, int durability) {
builder.push(name);
CHARGE_COST = builder
.comment("""
The amount of charges required to revive a player.
Set to -1 for dynamic cost that scales with a player's deaths.
""")
.defineInRange("chargeCost", chargeCost, -1, Integer.MAX_VALUE);
CHARGE_COST_LIMIT = builder
.comment("DYNAMIC COST REQUIRED. The max amount of charges the totem can hold at once.")
.defineInRange("chargeCostLimit", chargeCostLimit, 1, Integer.MAX_VALUE);
CHARGE_COST_MULTIPLIER = builder
.comment("""
DYNAMIC COST REQUIRED. charge_cost = player_deaths * charge_cost_multiplier.
Note: The resulting cost will always be at least 1.
""")
.defineInRange("chargeCostMultiplier", chargeCostMultiplier, 0.01, 100);
CAN_REVIVE_MORE_EXPENSIVE_TARGETS = builder
.comment("DYNAMIC COST REQUIRED. Allows players with a higher cost to be revived as long as the totem is fully charged.")
.define("canReviveMoreExpensiveTargets", canReviveMoreExpensiveTargets);
CAN_REVIVE_ACROSS_DIMENSIONS = builder
.comment("Allows players in different dimensions to be revived.")
.define("canReviveAcrossDimensions", canReviveAcrossDimensions);
DURABILITY = builder
.comment("""
The durability of the totem.
Set to 0 to make the totem unbreakable.
""")
.defineInRange("durability", durability, 0, Integer.MAX_VALUE);
builder.pop();
}
public int getChargeCost() { return CHARGE_COST.get(); }
public int getChargeCostLimit() { return CHARGE_COST_LIMIT.get(); }
public double getChargeCostMultiplier() { return CHARGE_COST_MULTIPLIER.get(); }
public boolean getCanReviveMoreExpensiveTargets() { return CAN_REVIVE_MORE_EXPENSIVE_TARGETS.get(); }
public boolean getCanReviveAcrossDimensions() { return CAN_REVIVE_ACROSS_DIMENSIONS.get(); }
public int getDurability() { return DURABILITY.get(); }
public int getConfigHash() { return Objects.hash(getDurability()); }
}
}

View File

@ -1,14 +1,13 @@
package dev.micle.totemofreviving.setup;
package dev.micle.totem_of_reviving.setup;
import dev.micle.totemofreviving.TotemOfReviving;
import net.minecraft.network.chat.Component;
import dev.micle.totem_of_reviving.util.LangAsset;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraftforge.registries.RegistryObject;
import net.neoforged.neoforge.registries.DeferredHolder;
public class ModCreativeModeTabs {
public static RegistryObject<CreativeModeTab> MAIN = Registration.CREATIVE_MODE_TABS.register("main",
public static DeferredHolder<CreativeModeTab, CreativeModeTab> MAIN = Registration.CREATIVE_MODE_TABS.register("main",
() -> CreativeModeTab.builder()
.title(Component.translatable(String.format("itemGroup.%s.main", TotemOfReviving.MOD_ID)))
.title(LangAsset.ITEM_GROUP_MAIN.getComponent())
.icon(ModItems.STRAW_TOTEM.get()::getDefaultInstance)
.displayItems((itemDisplayParameters, output) -> {
output.accept(ModItems.STRAW_TOTEM.get());

View File

@ -0,0 +1,24 @@
package dev.micle.totem_of_reviving.setup;
import dev.micle.totem_of_reviving.component.TotemConfigHashData;
import dev.micle.totem_of_reviving.component.TotemData;
import net.minecraft.core.component.DataComponentType;
import net.neoforged.neoforge.registries.DeferredHolder;
public class ModDataComponents {
public static final DeferredHolder<DataComponentType<?>, DataComponentType<TotemData>> TOTEM_DATA = Registration.DATA_COMPONENTS.registerComponentType(
"totem_data",
builder -> builder
.persistent(TotemData.CODEC)
.networkSynchronized(TotemData.STREAM_CODEC)
);
public static final DeferredHolder<DataComponentType<?>, DataComponentType<TotemConfigHashData>> TOTEM_CONFIG_HASH_DATA = Registration.DATA_COMPONENTS.registerComponentType(
"totem_config_hash_data",
builder -> builder
.persistent(TotemConfigHashData.CODEC)
.networkSynchronized(TotemConfigHashData.STREAM_CODEC)
);
public static void register() {}
}

View File

@ -0,0 +1,26 @@
package dev.micle.totem_of_reviving.setup;
import dev.micle.totem_of_reviving.item.charge.DiamondChargeItem;
import dev.micle.totem_of_reviving.item.charge.IronChargeItem;
import dev.micle.totem_of_reviving.item.charge.NetheriteChargeItem;
import dev.micle.totem_of_reviving.item.charge.StrawChargeItem;
import dev.micle.totem_of_reviving.item.totem.DiamondTotemItem;
import dev.micle.totem_of_reviving.item.totem.IronTotemItem;
import dev.micle.totem_of_reviving.item.totem.NetheriteTotemItem;
import dev.micle.totem_of_reviving.item.totem.StrawTotemItem;
import net.minecraft.world.item.Item;
import net.neoforged.neoforge.registries.DeferredItem;
public class ModItems {
public static DeferredItem<Item> STRAW_TOTEM = Registration.ITEMS.register(StrawTotemItem.getName(), StrawTotemItem::new);
public static DeferredItem<Item> IRON_TOTEM = Registration.ITEMS.register(IronTotemItem.getName(), IronTotemItem::new);
public static DeferredItem<Item> DIAMOND_TOTEM = Registration.ITEMS.register(DiamondTotemItem.getName(), DiamondTotemItem::new);
public static DeferredItem<Item> NETHERITE_TOTEM = Registration.ITEMS.register(NetheriteTotemItem.getName(), NetheriteTotemItem::new);
public static DeferredItem<Item> STRAW_CHARGE = Registration.ITEMS.register(StrawChargeItem.getName(), StrawChargeItem::new);
public static DeferredItem<Item> IRON_CHARGE = Registration.ITEMS.register(IronChargeItem.getName(), IronChargeItem::new);
public static DeferredItem<Item> DIAMOND_CHARGE = Registration.ITEMS.register(DiamondChargeItem.getName(), DiamondChargeItem::new);
public static DeferredItem<Item> NETHERITE_CHARGE = Registration.ITEMS.register(NetheriteChargeItem.getName(), NetheriteChargeItem::new);
public static void register() {}
}

View File

@ -0,0 +1,30 @@
package dev.micle.totem_of_reviving.setup;
import dev.micle.totem_of_reviving.TotemOfReviving;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.item.CreativeModeTab;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredRegister;
public class Registration {
// Initialize variables
public static final DeferredRegister.DataComponents DATA_COMPONENTS =
DeferredRegister.createDataComponents(Registries.DATA_COMPONENT_TYPE, TotemOfReviving.MOD_ID);
public static final DeferredRegister.Items ITEMS =
DeferredRegister.createItems(TotemOfReviving.MOD_ID);
public static final DeferredRegister<CreativeModeTab> CREATIVE_MODE_TABS =
DeferredRegister.create(Registries.CREATIVE_MODE_TAB, TotemOfReviving.MOD_ID);
public static void register(IEventBus modEventBus) {
DATA_COMPONENTS.register(modEventBus);
ModDataComponents.register();
ITEMS.register(modEventBus);
ModItems.register();
CREATIVE_MODE_TABS.register(modEventBus);
ModCreativeModeTabs.register();
}
}

View File

@ -0,0 +1,31 @@
package dev.micle.totem_of_reviving.util;
import net.minecraft.ChatFormatting;
import net.minecraft.client.KeyMapping;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentUtils;
import net.minecraft.network.chat.MutableComponent;
import java.util.ArrayList;
import java.util.List;
public final class ComponentHelper {
public static MutableComponent addIndentToComponent(Component component, int indentLevel) {
List<Component> components = new ArrayList<>();
components.add(Component.literal(" ".repeat(indentLevel)));
components.add(component);
return (MutableComponent) ComponentUtils.formatList(components, Component.empty());
}
public static MutableComponent createKeyText(boolean isEnclosed, ChatFormatting[] textFormat, ChatFormatting[] keyFormat, KeyMapping...keys) {
List<Component> textComponents = new ArrayList<>();
textComponents.add(isEnclosed ? Component.literal("[").withStyle(textFormat) : Component.empty());
for (int i = 0; i < keys.length; i++) {
if (i > 0) textComponents.add(Component.literal(" + ").withStyle(textFormat));
textComponents.add(Component.literal(keys[i].getKey().getDisplayName().getString()).withStyle(keyFormat));
}
textComponents.add(isEnclosed ? Component.literal("]").withStyle(textFormat) : Component.empty());
return (MutableComponent) ComponentUtils.formatList(textComponents, Component.empty());
}
}

View File

@ -0,0 +1,58 @@
package dev.micle.totem_of_reviving.util;
import dev.micle.totem_of_reviving.TotemOfReviving;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.MutableComponent;
import net.minecraft.network.chat.contents.TranslatableContents;
public enum LangAsset {
ITEM_GROUP_MAIN("itemGroup", "main"),
ITEM_STRAW_TOTEM("item", "straw_totem"),
ITEM_IRON_TOTEM("item", "iron_totem"),
ITEM_DIAMOND_TOTEM("item", "diamond_totem"),
ITEM_NETHERITE_TOTEM("item", "netherite_totem"),
ITEM_STRAW_CHARGE("item", "straw_charge"),
ITEM_IRON_CHARGE("item", "iron_charge"),
ITEM_DIAMOND_CHARGE("item", "diamond_charge"),
ITEM_NETHERITE_CHARGE("item", "netherite_charge"),
TOOLTIP_TOTEM_TARGET("tooltip", "totem_target"),
TOOLTIP_TOTEM_TARGET_COST("tooltip", "totem_target_cost"),
TOOLTIP_TOTEM_CHARGES("tooltip", "totem_charges"),
TOOLTIP_TOTEM_DURABILITY("tooltip", "totem_durability"),
TOOLTIP_TOTEM_DYNAMIC_COST("tooltip", "totem_dynamic_cost"),
TOOLTIP_TOTEM_STATIC_COST("tooltip", "totem_static_cost"),
TOOLTIP_TOTEM_CAN_REVIVE_MORE_EXPENSIVE_TARGETS("tooltip", "totem_can_revive_more_expensive_targets"),
TOOLTIP_TOTEM_CAN_REVIVE_ACROSS_DIMENSIONS("tooltip", "totem_can_revive_across_dimensions"),
TOOLTIP_TOTEM_REVIVE_INSTRUCTION("tooltip", "totem_revive_instruction"),
TOOLTIP_TOTEM_CHARGE_INSTRUCTION("tooltip", "totem_charge_instruction"),
TOOLTIP_TOTEM_CHANGE_TARGET_INSTRUCTION("tooltip", "totem_change_target_instructions"),
TOOLTIP_CHARGE("tooltip", "charge"),
TOOLTIP_ADVANCED_TOOLTIP_HIDDEN("tooltip", "advanced_tooltip_hidden"),
TOOLTIP_ADVANCED_TOOLTIP_SHOWN("tooltip", "advanced_tooltip_shown"),
GENERAL_NA("general", "na"),
GENERAL_UNBREAKABLE("general", "unbreakable"),
MESSAGE_UNABLE_TO_FIND_PLAYER("message", "unable_to_find_player"),
MESSAGE_PLAYER_IS_NOT_DEAD("message", "player_is_not_dead"),
MESSAGE_PLAYER_IS_IN_ANOTHER_DIMENSION("message", "player_is_in_another_dimension"),
MESSAGE_UNABLE_TO_GET_PLAYER_WORLD("message", "unable_to_get_player_world"),
MESSAGE_NOT_ENOUGH_CHARGE("message", "not_enough_charge"),
MESSAGE_UNABLE_TO_GET_YOUR_WORLD("message", "unable_to_get_your_world"),
MESSAGE_SUCCESSFULLY_REVIVED_TARGET("message", "successfully_revived_target"),
MESSAGE_NOW_TARGETING("message", "now_targeting");
private final String category;
private final String key;
private LangAsset(String category, String key) {
this.category = category;
this.key = key;
}
public MutableComponent getComponent() {
return getComponent(TranslatableContents.NO_ARGS);
}
public MutableComponent getComponent(Object ...args) {
return Component.translatable(String.format("%s.%s.%s", category, TotemOfReviving.MOD_ID, key), args);
}
}

View File

@ -1,44 +0,0 @@
package dev.micle.totemofreviving;
import dev.micle.totemofreviving.proxy.IProxy;
import dev.micle.totemofreviving.proxy.Proxy;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModList;
import net.minecraftforge.fml.common.Mod;
import java.util.Optional;
@Mod(TotemOfReviving.MOD_ID)
public final class TotemOfReviving {
public static final String MOD_ID = "totemofreviving";
private static IProxy proxy;
public TotemOfReviving() {
proxy = DistExecutor.safeRunForDist(
() -> Proxy.Client::new,
() -> Proxy.Server::new
);
}
public static ResourceLocation createResourceLocation(String name) {
if (name.contains(":")) {
throw new IllegalArgumentException("Name contains namespace");
}
return new ResourceLocation(MOD_ID, name);
}
public static String getVersion() {
Optional<? extends ModContainer> optional = ModList.get().getModContainerById(MOD_ID);
if (optional.isPresent()) {
return optional.get().getModInfo().getVersion().toString();
}
return "0.0.0";
}
public static IProxy getProxy() {
return proxy;
}
}

View File

@ -1,22 +0,0 @@
package dev.micle.totemofreviving.data;
import dev.micle.totemofreviving.TotemOfReviving;
import dev.micle.totemofreviving.data.client.ModItemModelProvider;
import net.minecraft.data.DataGenerator;
import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraftforge.data.event.GatherDataEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
@Mod.EventBusSubscriber(modid = TotemOfReviving.MOD_ID, bus = Mod.EventBusSubscriber.Bus.MOD)
public class DataGenerators {
@SubscribeEvent
public static void gatherData(GatherDataEvent event) {
DataGenerator generator = event.getGenerator();
ExistingFileHelper existingFileHelper = event.getExistingFileHelper();
generator.addProvider(event.includeServer(), new ModItemModelProvider(generator, existingFileHelper));
generator.addProvider(event.includeServer(), new ModRecipeProvider(generator));
}
}

View File

@ -1,33 +0,0 @@
package dev.micle.totemofreviving.item.charge;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Rarity;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.List;
public class ChargeItem extends Item {
public ChargeItem() {
this(Rarity.COMMON);
}
public ChargeItem(Rarity rarity) {
super(new Properties().stacksTo(64).rarity(rarity));
}
@Override
@OnlyIn(Dist.CLIENT)
@ParametersAreNonnullByDefault
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> tooltip, TooltipFlag tooltipFlag) {
tooltip.add(Component.literal(ChatFormatting.WHITE + "Used for charging its corresponding totem."));
super.appendHoverText(stack, world, tooltip, tooltipFlag);
}
}

View File

@ -1,13 +0,0 @@
package dev.micle.totemofreviving.item.charge;
import net.minecraft.world.item.Rarity;
public class DiamondChargeItem extends ChargeItem {
public DiamondChargeItem() {
super(Rarity.RARE);
}
public static String getName() {
return "diamond_charge";
}
}

View File

@ -1,9 +0,0 @@
package dev.micle.totemofreviving.item.charge;
public class IronChargeItem extends ChargeItem {
public IronChargeItem() {}
public static String getName() {
return "iron_charge";
}
}

View File

@ -1,13 +0,0 @@
package dev.micle.totemofreviving.item.charge;
import net.minecraft.world.item.Rarity;
public class NetheriteChargeItem extends ChargeItem {
public NetheriteChargeItem() {
super(Rarity.EPIC);
}
public static String getName() {
return "netherite_charge";
}
}

View File

@ -1,13 +0,0 @@
package dev.micle.totemofreviving.item.charge;
import net.minecraft.world.item.Rarity;
public class StrawChargeItem extends ChargeItem {
public StrawChargeItem() {
super(Rarity.UNCOMMON);
}
public static String getName() {
return "straw_charge";
}
}

View File

@ -1,20 +0,0 @@
package dev.micle.totemofreviving.item.totem;
import dev.micle.totemofreviving.item.charge.IronChargeItem;
import dev.micle.totemofreviving.setup.Config;
import net.minecraft.world.item.ItemStack;
public class IronTotemItem extends TotemItem {
public IronTotemItem() {
super(Config.Server.getIronTotemConfig().getDurability());
}
public static String getName() {
return "iron_totem";
}
@Override
public boolean isCharge(ItemStack stack) {
return (stack.getItem() instanceof IronChargeItem);
}
}

View File

@ -1,248 +0,0 @@
package dev.micle.totemofreviving.item.totem;
import dev.micle.totemofreviving.network.NetworkManager;
import dev.micle.totemofreviving.network.client.ChangeTargetPacket;
import dev.micle.totemofreviving.network.client.ChargeTotemPacket;
import dev.micle.totemofreviving.network.client.ReviveTargetPacket;
import dev.micle.totemofreviving.setup.Config;
import dev.micle.totemofreviving.setup.ModKeyMappings;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.stats.Stats;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Rarity;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.List;
import java.util.UUID;
public abstract class TotemItem extends Item {
private static final String TAG_TARGET_INDEX = "target_index";
private static final String TAG_TARGET_UUID = "target_uuid";
private static final String TAG_TARGET_NAME = "target_name";
private static final String TAG_TARGET_DEATHS = "target_deaths";
private static final String TAG_CHARGE = "charge";
public TotemItem(int durability) {
this(Rarity.COMMON, durability);
}
public TotemItem(Rarity rarity, int durability) {
super(new Properties().stacksTo(1).rarity(rarity).defaultDurability(durability));
}
public abstract boolean isCharge(ItemStack stack);
public static int getTargetIndex(ItemStack stack) {
if (!isTotem(stack)) { return -1; }
return stack.getOrCreateTag().getInt(TAG_TARGET_INDEX);
}
public static void setTargetIndex(ItemStack stack, int targetIndex) {
if (!isTotem(stack)) { return; }
stack.getOrCreateTag().putInt(TAG_TARGET_INDEX, targetIndex);
}
public static UUID getTargetUUID(ItemStack stack) {
if (!isTotem(stack)) { return null; }
try {
return UUID.fromString(stack.getOrCreateTag().getString(TAG_TARGET_UUID));
} catch (IllegalArgumentException exception) {
return null;
}
}
public static void setTargetUUID(ItemStack stack, UUID targetUUID) {
if (!isTotem(stack)) { return; }
stack.getOrCreateTag().putString(TAG_TARGET_UUID, targetUUID.toString());
}
public static String getTargetName(ItemStack stack) {
if (!isTotem(stack)) { return null; }
return stack.getOrCreateTag().getString(TAG_TARGET_NAME);
}
public static void setTargetName(ItemStack stack, String targetName) {
if (!isTotem(stack)) { return; }
stack.getOrCreateTag().putString(TAG_TARGET_NAME, targetName);
}
public static int getTargetCost(ItemStack stack) {
if (!isTotem(stack)) { return -1; }
return !isCostDynamic(stack) ? getConfig(stack).getChargeCost() :
(int)(getTargetDeaths(stack) * getConfig(stack).getChargeCostMultiplier());
}
public static int getTargetDeaths(ItemStack stack) {
if (!isTotem(stack)) { return -1; }
return stack.getOrCreateTag().getInt(TAG_TARGET_DEATHS);
}
public static void setTargetDeaths(ItemStack stack, ServerPlayer target) {
if (!isTotem(stack)) { return; }
stack.getOrCreateTag().putInt(TAG_TARGET_DEATHS, target.getStats().getValue(Stats.CUSTOM.get(Stats.DEATHS)));
}
public static boolean isTotemFull(ItemStack stack) {
if (!isTotem(stack)) { return false; }
return getCharge(stack) == getMaxCharge(stack);
}
public static boolean canTotemAffordTarget(ItemStack stack) {
if (!isTotem(stack)) { return false; }
if (getCharge(stack) < getTargetCost(stack)) {
if (!isTotemFull(stack)) {
return false;
}
return isTotemFull(stack) && canReviveMoreExpensiveTargets(stack);
}
return true;
}
public static int getCharge(ItemStack stack) {
if (!isTotem(stack)) { return -1; }
int charge = stack.getOrCreateTag().getInt(TAG_CHARGE);
int maxCharge = getMaxCharge(stack);
if (charge > maxCharge) {
charge = maxCharge;
setCharge(stack, charge);
}
return charge;
}
public static void setCharge(ItemStack stack, int charge) {
if (!isTotem(stack)) { return; }
stack.getOrCreateTag().putInt(TAG_CHARGE, charge);
}
public static int getMaxCharge(ItemStack stack) {
Config.TotemConfig config = getConfig(stack);
if (config == null) { return -1; }
if (config.getChargeCost() == -1) {
return config.getChargeCostLimit();
}
return config.getChargeCost();
}
public static boolean isCostDynamic(ItemStack stack) {
if (!isTotem(stack)) { return false; }
return getConfig(stack).getChargeCost() == -1;
}
public static boolean canReviveAcrossDimensions(ItemStack stack) {
if (!isTotem(stack)) { return false; }
return getConfig(stack).getCanReviveAcrossDimensions();
}
public static boolean canReviveMoreExpensiveTargets(ItemStack stack) {
if (!isTotem(stack)) { return false; }
return getConfig(stack).getCanReviveMoreExpensiveTargets();
}
@Override
@OnlyIn(Dist.CLIENT)
@ParametersAreNonnullByDefault
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> tooltip, TooltipFlag tooltipFlag) {
if (world == null) { return; }
UUID targetUUID = getTargetUUID(stack);
String targetName = getTargetName(stack);
int targetCost = getTargetCost(stack);
int charge = getCharge(stack);
int maxCharge = getMaxCharge(stack);
double multiplier = getConfig(stack).getChargeCostMultiplier();
if (targetUUID == null) {
tooltip.add(Component.literal(ChatFormatting.RED + "Target: " + ChatFormatting.DARK_RED + "NONE"));
} else {
tooltip.add(Component.literal(ChatFormatting.WHITE + "Target: " + ChatFormatting.GRAY + targetName));
}
tooltip.add(Component.literal(""));
if (!canTotemAffordTarget(stack)) {
tooltip.add(Component.literal(ChatFormatting.RED + "Charges: " + ChatFormatting.DARK_RED + "(" + charge + "/" + targetCost + ") " +
ChatFormatting.RED + "[Max: " + ChatFormatting.DARK_RED + maxCharge + ChatFormatting.RED + "] [Multi: " + ChatFormatting.DARK_RED + multiplier + ChatFormatting.RED + "]"));
} else {
tooltip.add(Component.literal(ChatFormatting.WHITE + "Charges: " + ChatFormatting.GRAY + "(" + charge + "/" + targetCost + ") " +
ChatFormatting.WHITE + "[Max: " + ChatFormatting.GRAY + maxCharge + ChatFormatting.WHITE + "] [Multi: " + ChatFormatting.GRAY + multiplier + ChatFormatting.WHITE + "]"));
}
if (canReviveMoreExpensiveTargets(stack)) {
tooltip.add(Component.literal(ChatFormatting.WHITE + "Can revive more expensive targets."));
}
if (canReviveAcrossDimensions(stack)) {
tooltip.add(Component.literal(ChatFormatting.WHITE + "Can revive targets across dimensions."));
}
tooltip.add(Component.literal(""));
if (ModKeyMappings.ADVANCED_TOOLTIP.isDown()) {
tooltip.add(Component.literal(ChatFormatting.GRAY + "Showing advanced tooltip."));
tooltip.add(Component.literal(ChatFormatting.WHITE + "[" + ChatFormatting.GRAY + "R-CLICK" + ChatFormatting.WHITE + "]"));
tooltip.add(Component.literal(ChatFormatting.WHITE + "When second hand is empty: revive target."));
tooltip.add(Component.literal(ChatFormatting.WHITE + "When second hand is holding a reviving charge: charge totem."));
tooltip.add(Component.literal(""));
tooltip.add(Component.literal(ChatFormatting.WHITE + "[" + ChatFormatting.GRAY + "L-SHIFT + R-CLICK" + ChatFormatting.WHITE + "]"));
tooltip.add(Component.literal(ChatFormatting.WHITE + "Change target."));
} else {
tooltip.add(Component.literal(ChatFormatting.GRAY + "Hold [" + ChatFormatting.DARK_GRAY + "L-SHIFT" + ChatFormatting.GRAY + "] for advanced tooltip."));
}
super.appendHoverText(stack, world, tooltip, tooltipFlag);
}
@Override
@OnlyIn(Dist.CLIENT)
@ParametersAreNonnullByDefault
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
if (!world.isClientSide) {
return super.use(world, player, hand);
}
ItemStack chargeItem = (hand.equals(InteractionHand.MAIN_HAND)) ? player.getOffhandItem() : player.getMainHandItem();
if (player.isCrouching()) {
NetworkManager.getChannel().sendToServer(new ChangeTargetPacket(hand));
} else {
if (isCharge(chargeItem)) {
NetworkManager.getChannel().sendToServer(new ChargeTotemPacket(hand));
} else {
NetworkManager.getChannel().sendToServer(new ReviveTargetPacket(hand));
}
}
return super.use(world, player, hand);
}
@Override
public int getMaxDamage(ItemStack stack) {
return getConfig(stack).getDurability();
}
@Override
public boolean isDamageable(ItemStack stack) {
return (getConfig(stack).getDurability() != 0);
}
@Override
public int getDamage(ItemStack stack) {
if (isDamageable(stack) && super.getDamage(stack) >= getMaxDamage(stack)) {
stack.setCount(0);
return getMaxDamage(stack);
}
return (isDamageable(stack)) ? super.getDamage(stack) : 0;
}
private static boolean isTotem(ItemStack stack) {
return (stack.getItem() instanceof TotemItem);
}
private static Config.TotemConfig getConfig(ItemStack stack) {
Item item = stack.getItem();
if (item instanceof StrawTotemItem) { return Config.Server.getStrawTotemConfig(); }
if (item instanceof IronTotemItem) { return Config.Server.getIronTotemConfig(); }
if (item instanceof DiamondTotemItem) { return Config.Server.getDiamondTotemConfig(); }
if (item instanceof NetheriteTotemItem) { return Config.Server.getNetheriteTotemConfig(); }
return null;
}
}

View File

@ -1,44 +0,0 @@
package dev.micle.totemofreviving.network;
import dev.micle.totemofreviving.TotemOfReviving;
import dev.micle.totemofreviving.network.client.ChangeTargetPacket;
import dev.micle.totemofreviving.network.client.ChargeTotemPacket;
import dev.micle.totemofreviving.network.client.ReviveTargetPacket;
import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.simple.SimpleChannel;
public class NetworkManager {
// Initialize variables
private static SimpleChannel channel;
public static void init() {
// Create channel
channel = NetworkRegistry.ChannelBuilder.named(TotemOfReviving.createResourceLocation("network"))
.clientAcceptedVersions(v -> v.equals(TotemOfReviving.getVersion()))
.serverAcceptedVersions(v -> v.equals(TotemOfReviving.getVersion()))
.networkProtocolVersion(TotemOfReviving::getVersion)
.simpleChannel();
// Register packets
int id = 0;
channel.messageBuilder(ChangeTargetPacket.class, id++)
.encoder(ChangeTargetPacket::encode)
.decoder(ChangeTargetPacket::decode)
.consumerNetworkThread(ChangeTargetPacket::handle)
.add();
channel.messageBuilder(ChargeTotemPacket.class, id++)
.encoder(ChargeTotemPacket::encode)
.decoder(ChargeTotemPacket::decode)
.consumerNetworkThread(ChargeTotemPacket::handle)
.add();
channel.messageBuilder(ReviveTargetPacket.class, id++)
.encoder(ReviveTargetPacket::encode)
.decoder(ReviveTargetPacket::decode)
.consumerNetworkThread(ReviveTargetPacket::handle)
.add();
}
public static SimpleChannel getChannel() {
return channel;
}
}

View File

@ -1,53 +0,0 @@
package dev.micle.totemofreviving.network.client;
import dev.micle.totemofreviving.TotemOfReviving;
import dev.micle.totemofreviving.item.totem.TotemItem;
import net.minecraft.ChatFormatting;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.players.PlayerList;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkEvent;
import java.util.function.Supplier;
public class ChangeTargetPacket {
private final InteractionHand hand;
public ChangeTargetPacket(final InteractionHand hand) {
this.hand = hand;
}
public static void encode(final ChangeTargetPacket packet, final FriendlyByteBuf buffer) {
buffer.writeEnum(packet.hand);
}
public static ChangeTargetPacket decode(final FriendlyByteBuf buffer) {
return new ChangeTargetPacket(buffer.readEnum(InteractionHand.class));
}
public static void handle(final ChangeTargetPacket packet, final Supplier<NetworkEvent.Context> contextSupplier) {
final NetworkEvent.Context context = contextSupplier.get();
context.enqueueWork(() -> {
ServerPlayer sender = context.getSender();
if (sender == null) { return; }
ItemStack totemStack = sender.getItemInHand(packet.hand);
PlayerList playerList = TotemOfReviving.getProxy().getServer().getPlayerList();
int targetIndex = TotemItem.getTargetIndex(totemStack) + 1;
if (targetIndex > playerList.getPlayerCount()-1) { targetIndex = 0; }
ServerPlayer target = playerList.getPlayers().get(targetIndex);
TotemItem.setTargetIndex(totemStack, targetIndex);
TotemItem.setTargetUUID(totemStack, target.getUUID());
TotemItem.setTargetName(totemStack, target.getDisplayName().getString());
TotemItem.setTargetDeaths(totemStack, target);
sender.sendSystemMessage(Component.literal(ChatFormatting.WHITE + "Now targetting " + ChatFormatting.GRAY + target.getDisplayName().getString() + "."));
});
context.setPacketHandled(true);
}
}

View File

@ -1,43 +0,0 @@
package dev.micle.totemofreviving.network.client;
import dev.micle.totemofreviving.item.totem.TotemItem;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkEvent;
import java.util.function.Supplier;
public class ChargeTotemPacket {
private final InteractionHand hand;
public ChargeTotemPacket(final InteractionHand hand) {
this.hand = hand;
}
public static void encode(final ChargeTotemPacket packet, final FriendlyByteBuf buffer) {
buffer.writeEnum(packet.hand);
}
public static ChargeTotemPacket decode(final FriendlyByteBuf buffer) {
return new ChargeTotemPacket(buffer.readEnum(InteractionHand.class));
}
public static void handle(final ChargeTotemPacket packet, final Supplier<NetworkEvent.Context> contextSupplier) {
final NetworkEvent.Context context = contextSupplier.get();
context.enqueueWork(() -> {
ServerPlayer sender = context.getSender();
if (sender == null) { return; }
ItemStack totemStack = (packet.hand.equals(InteractionHand.MAIN_HAND)) ? sender.getMainHandItem() : sender.getOffhandItem();
ItemStack chargeStack = (packet.hand.equals(InteractionHand.MAIN_HAND)) ? sender.getOffhandItem() : sender.getMainHandItem();
if (TotemItem.isTotemFull(totemStack)) { return; }
TotemItem.setCharge(totemStack, TotemItem.getCharge(totemStack) + 1);
chargeStack.setCount(chargeStack.getCount() - 1);
});
context.setPacketHandled(true);
}
}

View File

@ -1,71 +0,0 @@
package dev.micle.totemofreviving.network.client;
import dev.micle.totemofreviving.TotemOfReviving;
import dev.micle.totemofreviving.item.totem.TotemItem;
import net.minecraft.ChatFormatting;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.network.NetworkEvent;
import java.util.function.Supplier;
public class ReviveTargetPacket {
private final InteractionHand hand;
public ReviveTargetPacket(final InteractionHand hand) {
this.hand = hand;
}
public static void encode(final ReviveTargetPacket packet, final FriendlyByteBuf buffer) {
buffer.writeEnum(packet.hand);
}
public static ReviveTargetPacket decode(final FriendlyByteBuf buffer) {
return new ReviveTargetPacket(buffer.readEnum(InteractionHand.class));
}
public static void handle(final ReviveTargetPacket packet, final Supplier<NetworkEvent.Context> contextSupplier) {
final NetworkEvent.Context context = contextSupplier.get();
context.enqueueWork(() -> {
ServerPlayer sender = context.getSender();
if (sender == null) { return; }
ItemStack totemStack = sender.getItemInHand(packet.hand);
ServerPlayer target;
try {
target = TotemOfReviving.getProxy().getServer().getPlayerList().getPlayer(TotemItem.getTargetUUID(totemStack));
if (target == null) { throw new NullPointerException("Target is null!"); }
} catch (NullPointerException exception) {
sender.sendSystemMessage(Component.literal(ChatFormatting.WHITE + "Unable to find player!"));
return;
}
if (!target.isSpectator()) {
sender.sendSystemMessage(Component.literal(ChatFormatting.GRAY + target.getDisplayName().getString() + ChatFormatting.WHITE + " is not dead!"));
return;
}
if (!target.serverLevel().equals(sender.serverLevel()) && !TotemItem.canReviveAcrossDimensions(totemStack)) {
sender.sendSystemMessage(Component.literal(ChatFormatting.GRAY + target.getDisplayName().getString() + ChatFormatting.WHITE + " is in a different dimension!"));
return;
}
TotemItem.setTargetDeaths(totemStack, target);
if (!TotemItem.canTotemAffordTarget(totemStack)) {
sender.sendSystemMessage(Component.literal(ChatFormatting.WHITE + "Not enough charge!"));
return;
}
target.teleportTo(sender.serverLevel(), sender.position().x, sender.position().y, sender.position().z, sender.getYRot(), sender.getXRot());
target.setGameMode(sender.serverLevel().getServer().getDefaultGameType());
TotemItem.setCharge(totemStack, TotemItem.getCharge(totemStack) - TotemItem.getTargetCost(totemStack));
totemStack.hurtAndBreak(1, sender, e -> e.broadcastBreakEvent(packet.hand));
sender.sendSystemMessage(Component.literal(ChatFormatting.WHITE + "Successfully revived " + ChatFormatting.GRAY + target.getDisplayName().getString() + "!"));
});
context.setPacketHandled(true);
}
}

View File

@ -1,97 +0,0 @@
package dev.micle.totemofreviving.setup;
import dev.micle.totemofreviving.item.totem.DiamondTotemItem;
import dev.micle.totemofreviving.item.totem.IronTotemItem;
import dev.micle.totemofreviving.item.totem.NetheriteTotemItem;
import dev.micle.totemofreviving.item.totem.StrawTotemItem;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.fml.ModLoadingContext;
import net.minecraftforge.fml.config.ModConfig;
public final class Config {
public static void init() {
ModLoadingContext.get().registerConfig(ModConfig.Type.SERVER, Server.SPEC);
}
public static class Server {
private static final ForgeConfigSpec SPEC;
private static final TotemConfig STRAW_TOTEM_CONFIG;
private static final TotemConfig IRON_TOTEM_CONFIG;
private static final TotemConfig DIAMOND_TOTEM_CONFIG;
private static final TotemConfig NETHERITE_TOTEM_CONFIG;
static {
ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder();
builder.comment("WHEN MAKING CHANGES IT IS RECOMMENDED TO NOT BE IN A WORLD.\n" +
"CHANGES WILL MOST LIKELY REQUIRE A RESTART FOR EVERYTHING TO WORK PROPERLY.");
STRAW_TOTEM_CONFIG = new TotemConfig(builder, StrawTotemItem.getName(), -1, 3,
1, false, false, 1);
IRON_TOTEM_CONFIG = new TotemConfig(builder, IronTotemItem.getName(), -1, 5,
0.75, false, false, 4);
DIAMOND_TOTEM_CONFIG = new TotemConfig(builder, DiamondTotemItem.getName(), -1, 10,
0.5, false, false, 10);
NETHERITE_TOTEM_CONFIG = new TotemConfig(builder, NetheriteTotemItem.getName(), 1, 1,
1, true, true, 0);
SPEC = builder.build();
}
public static TotemConfig getStrawTotemConfig() { return STRAW_TOTEM_CONFIG; }
public static TotemConfig getIronTotemConfig() { return IRON_TOTEM_CONFIG; }
public static TotemConfig getDiamondTotemConfig() { return DIAMOND_TOTEM_CONFIG; }
public static TotemConfig getNetheriteTotemConfig() { return NETHERITE_TOTEM_CONFIG; }
}
public static class TotemConfig {
private final ForgeConfigSpec.IntValue CHARGE_COST;
private final ForgeConfigSpec.IntValue CHARGE_COST_LIMIT;
private final ForgeConfigSpec.DoubleValue CHARGE_COST_MULTIPLIER;
private final ForgeConfigSpec.BooleanValue CAN_REVIVE_MORE_EXPENSIVE_TARGETS;
private final ForgeConfigSpec.BooleanValue CAN_REVIVE_ACROSS_DIMENSIONS;
private final ForgeConfigSpec.IntValue DURABILITY;
public TotemConfig(ForgeConfigSpec.Builder builder, String name, int chargeCost,
int chargeCostLimit, double chargeCostMultiplier, boolean canReviveMoreExpensiveTargets,
boolean canReviveAcrossDimensions, int durability) {
builder.push(name);
CHARGE_COST = builder
.comment("The charge cost to revive a player.\n" +
"-1 means the cost is dynamic (follows the amount of deaths the target has)\n" +
"Higher values set the charge cost to static, meaning that this will be the amount of charges needed to revive someone.")
.defineInRange("chargeCost", chargeCost, -1, Integer.MAX_VALUE);
CHARGE_COST_LIMIT = builder
.comment("The max amount of charge this totem can hold at once. Only works with dynamic cost.")
.defineInRange("chargeCostLimit", chargeCostLimit, 1, Integer.MAX_VALUE);
CHARGE_COST_MULTIPLIER = builder
.comment("Charge cost multiplier. 0.5 means the charge cost will be 50% of the original cost. Only works with dynamic cost.")
.defineInRange("chargeCostMultiplier", chargeCostMultiplier, 0.01, 100);
CAN_REVIVE_MORE_EXPENSIVE_TARGETS = builder
.comment("Is the totem able to revive targets that cost more than the totems max charge?\n" +
"This only applies if the totem is fully charged. (dynamic wont work if limit is 0)")
.define("canReviveMoreExpensiveTargets", canReviveMoreExpensiveTargets);
CAN_REVIVE_ACROSS_DIMENSIONS = builder
.comment("Is the totem able to revive targets across dimensions?")
.define("canReviveAcrossDimensions", canReviveAcrossDimensions);
DURABILITY = builder
.comment("The durability of the totem. 0 means unbreakable.")
.defineInRange("durability", durability, 0, Integer.MAX_VALUE);
builder.pop();
}
public int getChargeCost() { return CHARGE_COST.get(); }
public int getChargeCostLimit() { return CHARGE_COST_LIMIT.get(); }
public double getChargeCostMultiplier() { return CHARGE_COST_MULTIPLIER.get(); }
public boolean getCanReviveMoreExpensiveTargets() { return CAN_REVIVE_MORE_EXPENSIVE_TARGETS.get(); }
public boolean getCanReviveAcrossDimensions() { return CAN_REVIVE_ACROSS_DIMENSIONS.get(); }
public int getDurability() {
try {
return DURABILITY.get();
} catch (IllegalStateException e) {
return 10;
}
}
}
}

View File

@ -1,26 +0,0 @@
package dev.micle.totemofreviving.setup;
import dev.micle.totemofreviving.item.charge.DiamondChargeItem;
import dev.micle.totemofreviving.item.charge.IronChargeItem;
import dev.micle.totemofreviving.item.charge.NetheriteChargeItem;
import dev.micle.totemofreviving.item.charge.StrawChargeItem;
import dev.micle.totemofreviving.item.totem.DiamondTotemItem;
import dev.micle.totemofreviving.item.totem.IronTotemItem;
import dev.micle.totemofreviving.item.totem.NetheriteTotemItem;
import dev.micle.totemofreviving.item.totem.StrawTotemItem;
import net.minecraft.world.item.Item;
import net.minecraftforge.registries.RegistryObject;
public class ModItems {
public static RegistryObject<Item> STRAW_TOTEM = Registration.ITEMS.register(StrawTotemItem.getName(), StrawTotemItem::new);
public static RegistryObject<Item> IRON_TOTEM = Registration.ITEMS.register(IronTotemItem.getName(), IronTotemItem::new);
public static RegistryObject<Item> DIAMOND_TOTEM = Registration.ITEMS.register(DiamondTotemItem.getName(), DiamondTotemItem::new);
public static RegistryObject<Item> NETHERITE_TOTEM = Registration.ITEMS.register(NetheriteTotemItem.getName(), NetheriteTotemItem::new);
public static RegistryObject<Item> STRAW_CHARGE = Registration.ITEMS.register(StrawChargeItem.getName(), StrawChargeItem::new);
public static RegistryObject<Item> IRON_CHARGE = Registration.ITEMS.register(IronChargeItem.getName(), IronChargeItem::new);
public static RegistryObject<Item> DIAMOND_CHARGE = Registration.ITEMS.register(DiamondChargeItem.getName(), DiamondChargeItem::new);
public static RegistryObject<Item> NETHERITE_CHARGE = Registration.ITEMS.register(NetheriteChargeItem.getName(), NetheriteChargeItem::new);
public static void register() {}
}

View File

@ -1,9 +0,0 @@
package dev.micle.totemofreviving.setup;
import com.mojang.blaze3d.platform.InputConstants;
import net.minecraft.client.KeyMapping;
public class ModKeyMappings {
public static final KeyMapping ADVANCED_TOOLTIP =
new KeyMapping("advanced_tooltips", InputConstants.KEY_LSHIFT, KeyMapping.CATEGORY_INVENTORY);
}

View File

@ -1,29 +0,0 @@
package dev.micle.totemofreviving.setup;
import dev.micle.totemofreviving.TotemOfReviving;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
public class Registration {
// Initialize variables
public static final DeferredRegister<Item> ITEMS =
DeferredRegister.create(ForgeRegistries.ITEMS, TotemOfReviving.MOD_ID);
public static final DeferredRegister<CreativeModeTab> CREATIVE_MODE_TABS =
DeferredRegister.create(Registries.CREATIVE_MODE_TAB, TotemOfReviving.MOD_ID);
public static void register() {
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
ITEMS.register(modEventBus);
ModItems.register();
CREATIVE_MODE_TABS.register(modEventBus);
ModCreativeModeTabs.register();
}
}

View File

@ -1,58 +0,0 @@
# This is an example mods.toml file. It contains the data relating to the loading mods.
# There are several mandatory fields (#mandatory), and many more that are optional (#optional).
# The overall format is standard TOML format, v0.5.0.
# Note that there are a couple of TOML lists in this file.
# Find more information on toml format here: https://github.com/toml-lang/toml
# The name of the mod loader type to load - for regular FML @Mod mods it should be javafml
modLoader = "javafml" #mandatory
# A version range to match for said mod loader - for regular FML @Mod it will be the forge version
loaderVersion = "[47,)" #mandatory This is typically bumped every Minecraft version by Forge. See our download page for lists of versions.
# The license for you mod. This is mandatory metadata and allows for easier comprehension of your redistributive properties.
# Review your options at https://choosealicense.com/. All rights reserved is the default copyright stance, and is thus the default here.
license = "All Rights Reserved"
# A URL to refer people to when problems occur with this mod
#issueTrackerURL="http://my.issue.tracker/" #optional
# A list of mods - how many allowed here is determined by the individual mod loader
[[mods]] #mandatory
# The modid of the mod
modId = "totemofreviving" #mandatory
# The version number of the mod - there's a few well known ${} variables useable here or just hardcode it
# ${file.jarVersion} will substitute the value of the Implementation-Version as read from the mod's JAR file metadata
# see the associated build.gradle script for how to populate this completely automatically during a build
version = "${file.jarVersion}" #mandatory
# A display name for the mod
displayName = "Micle's Totem of Reviving" #mandatory
# A URL to query for updates for this mod. See the JSON update specification <here>
#updateJSONURL="http://myurl.me/" #optional
# A URL for the "homepage" for this mod, displayed in the mod UI
#displayURL="http://example.com/" #optional
# A file name (in the root of the mod JAR) containing a logo for display
logoFile="logo.jpg" #optional
# A text field displayed in the mod UI
#credits="Thanks for this example mod goes to Java" #optional
# A text field displayed in the mod UI
authors="Micle" #optional
# The description text for the mod (multi line!) (#mandatory)
description = '''
Mod for reviving players in a hardcore world.
'''
# A dependency - use the . to indicate dependency for a specific modid. Dependencies are optional.
[[dependencies.totemofreviving]] #optional
# the modid of the dependency
modId = "forge" #mandatory
# Does this dependency have to exist - if not, ordering below must be specified
mandatory = true #mandatory
# The version range of the dependency
versionRange = "[47,)" #mandatory
# An ordering relationship for the dependency - BEFORE or AFTER required if the relationship is not mandatory
ordering = "NONE"
# Side this dependency is applied on - BOTH, CLIENT or SERVER
side = "BOTH"
# Here's another dependency
[[dependencies.totemofreviving]]
modId = "minecraft"
mandatory = true
# This version range declares a minimum of the current minecraft version up to but not including the next major version
versionRange = "[1.20.1,1.20.2)"
ordering = "NONE"
side = "BOTH"

View File

@ -0,0 +1,44 @@
modLoader = "javafml"
loaderVersion = "${loader_version_range}"
license = "${mod_license}"
issueTrackerURL = "https://gitea.micle.dev/minecraft-mods/totem_of_reviving/issues"
[[mods]]
modId = "${mod_id}"
version = "${mod_version}"
displayName = "${mod_name}"
#updateJSONURL = "http://myurl.me/"
#displayURL = "http://example.com/"
logoFile = "logo.png"
#credits = ""
authors = "${mod_authors}"
description = '''${mod_description}'''
#[[mixins]]
#config = "${mod_id}.mixins.json"
#[[accessTransformers]]
#file = "META-INF/accesstransformer.cfg"
[[dependencies."${mod_id}"]]
modId = "neoforge"
# The type of the dependency. Can be one of "required", "optional", "incompatible" or "discouraged" (case insensitive).
# 'required' requires the mod to exist, 'optional' does not
# 'incompatible' will prevent the game from loading when the mod exists, and 'discouraged' will show a warning
type = "required"
versionRange = "${neo_version_range}"
ordering = "NONE" # BEFORE, AFTER, NONE
side = "BOTH" # BOTH, CLIENT, SERVER
[[dependencies."${mod_id}"]]
modId = "minecraft"
type = "required"
versionRange = "${minecraft_version_range}"
ordering = "NONE"
side = "BOTH"
# Features are specific properties of the game environment, that you may want to declare you require. This example declares
# that your mod requires GL version 3.2 or higher. Other features will be added. They are side aware so declaring this won't
# stop your mod loading on the server for example.
#[features.${mod_id}]
#openGLVersion = "[3.2,)"

View File

@ -0,0 +1,35 @@
{
"itemGroup.totem_of_reviving.main": "Micle's Totem of Reviving",
"item.totem_of_reviving.straw_totem": "Straw totem of reviving",
"item.totem_of_reviving.iron_totem": "Iron totem of reviving",
"item.totem_of_reviving.diamond_totem": "Diamond totem of reviving",
"item.totem_of_reviving.netherite_totem": "Netherite totem of reviving",
"item.totem_of_reviving.straw_charge": "Straw reviving charge",
"item.totem_of_reviving.iron_charge": "Iron reviving charge",
"item.totem_of_reviving.diamond_charge": "Diamond reviving charge",
"item.totem_of_reviving.netherite_charge": "Netherite reviving charge",
"tooltip.totem_of_reviving.totem_target": "Target: %s",
"tooltip.totem_of_reviving.totem_target_cost": "Cost: %s",
"tooltip.totem_of_reviving.totem_charges": "Charges: %s",
"tooltip.totem_of_reviving.totem_durability": "Durability: %s",
"tooltip.totem_of_reviving.totem_dynamic_cost": "Dynamic cost (Multiplier: %s).",
"tooltip.totem_of_reviving.totem_static_cost": "Static cost (%s).",
"tooltip.totem_of_reviving.totem_can_revive_more_expensive_targets": "Can revive more expensive targets.",
"tooltip.totem_of_reviving.totem_can_revive_across_dimensions": "Can revive across dimensions.",
"tooltip.totem_of_reviving.totem_revive_instruction": "When second hand is empty: revive target.",
"tooltip.totem_of_reviving.totem_charge_instruction": "When second hand has a reviving charge: charge totem.",
"tooltip.totem_of_reviving.totem_change_target_instructions": "Change target.",
"tooltip.totem_of_reviving.charge": "Used for charging the %s",
"tooltip.totem_of_reviving.advanced_tooltip_hidden": "Hold %s for advanced tooltip.",
"tooltip.totem_of_reviving.advanced_tooltip_shown": "Showing advanced tooltip:",
"general.totem_of_reviving.na": "N/A",
"general.totem_of_reviving.unbreakable": "Unbreakable.",
"message.totem_of_reviving.unable_to_find_player": "Unable to find player!",
"message.totem_of_reviving.player_is_not_dead": "%s is not dead!",
"message.totem_of_reviving.player_is_in_another_dimension": "%s is in a different dimension!",
"message.totem_of_reviving.unable_to_get_player_world": "Unable to get %s's world!",
"message.totem_of_reviving.not_enough_charge": "Not enough charge!",
"message.totem_of_reviving.unable_to_get_your_world": "Unable to get your world!",
"message.totem_of_reviving.successfully_revived_target": "Successfully revived %s!",
"message.totem_of_reviving.now_targeting": "Now targeting %s."
}

View File

@ -1,12 +0,0 @@
{
"itemGroup.totemofreviving.main": "Micle's Totem of Reviving",
"item.totemofreviving.straw_totem": "Straw totem of reviving",
"item.totemofreviving.iron_totem": "Iron totem of reviving",
"item.totemofreviving.diamond_totem": "Diamond totem of reviving",
"item.totemofreviving.netherite_totem": "Netherite totem of reviving",
"item.totemofreviving.straw_charge": "Straw reviving charge",
"item.totemofreviving.iron_charge": "Iron reviving charge",
"item.totemofreviving.diamond_charge": "Diamond reviving charge",
"item.totemofreviving.netherite_charge": "Netherite reviving charge",
"advanced_tooltips": "advanced_tooltips"
}

BIN
src/main/resources/logo.jpg (Stored with Git LFS)

Binary file not shown.

BIN
src/main/resources/logo.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -1,6 +1,6 @@
{
"pack": {
"description": "totemofreviving resources",
"description": "totem_of_reviving resources",
"pack_format": 8
}
}