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
This commit is contained in:
2026-01-18 01:59:33 +00:00
91 changed files with 1490 additions and 1233 deletions

3
.gitignore vendored
View File

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

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

Binary file not shown.

View File

@ -1,112 +1,191 @@
buildscript { plugins {
id 'java-library'
id 'maven-publish'
id 'net.neoforged.moddev' version '2.0.137'
id 'idea'
}
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
}
group = mod_group_id
version = mod_version
repositories { repositories {
// These repositories are only for Gradle plugins, put any other repositories in the repository block further below // Add here additional repositories if required by some of the dependencies below.
maven { url = 'https://maven.minecraftforge.net' }
mavenCentral()
}
dependencies {
classpath group: 'net.minecraftforge.gradle', name: 'ForgeGradle', version: '5.1.+', changing: true
}
} }
apply plugin: 'net.minecraftforge.gradle' base {
archivesName = "${mod_id}-${minecraft_version}"
}
def archiveVersion = "${project.mcVersion}-${project.buildVersion}" as Object java {
toolchain.languageVersion = JavaLanguageVersion.of(21)
}
java.toolchain.languageVersion = JavaLanguageVersion.of(17) neoForge {
// Specify the version of NeoForge to use.
version = project.neo_version
minecraft { parchment {
mappings channel: 'official', version: mcVersion mappingsVersion = project.parchment_mappings_version
minecraftVersion = project.parchment_minecraft_version
}
accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') // 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 { runs {
client { client {
workingDirectory project.file('run') client()
property 'forge.logging.markers', 'REGISTRIES' // Comma-separated list of namespaces to load gametests from. Empty = all namespaces.
property 'forge.logging.console.level', 'debug' systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
mods {
totemofreviving {
source sourceSets.main
}
}
} }
server { server {
workingDirectory project.file('run') server()
programArgument '--nogui'
property 'forge.logging.markers', 'REGISTRIES' systemProperty 'neoforge.enabledGameTestNamespaces', project.mod_id
property 'forge.logging.console.level', 'debug'
mods {
totemofreviving {
source sourceSets.main
}
} }
// 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 { data {
workingDirectory project.file('run') data()
property 'forge.logging.markers', 'REGISTRIES' // example of overriding the workingDirectory set in configureEach above, uncomment if you want to use it
property 'forge.logging.console.level', 'debug' // gameDirectory = project.file('run-data')
args '--mod', 'totemofreviving', '--all', // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
'--existing', file('src/main/resources').toString(), programArguments.addAll '--mod', project.mod_id, '--all', '--output', file('src/generated/resources/').getAbsolutePath(), '--existing', file('src/main/resources/').getAbsolutePath()
'--existing', file('src/generated/resources').toString(), }
'--output', file('src/generated/resources/')
// 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 { mods {
totemofreviving { // define mod <-> source bindings
source sourceSets.main // 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 { sourceSets.main.resources {
srcDir 'src/generated/resources' srcDir 'src/generated/resources'
} }
repositories { // Sets up a dependency configuration called 'localRuntime'.
maven { // This configuration should be used instead of 'runtimeOnly' to declare
name = "Progwml6 maven" // a dependency that will be present for runtime testing but that is
url = "https://dvs1.progwml6.com/files/maven/" // "optional", meaning it will not be pulled by dependents of this mod.
} configurations {
maven { runtimeClasspath.extendsFrom localRuntime
name = "ModMaven"
url = "https://modmaven.dev"
}
} }
dependencies { 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 // Example mod dependency using a mod jar from ./libs with a flat dir repository
if (project.hasProperty('jeiVersion')) { // This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar
// compile against JEI API but do not include it at runtime // The group id is ignored when searching -- in this case, it is "blank"
compileOnly fg.deobf("mezz.jei:jei-${project.mcVersion}-forge-api:${project.jeiVersion}") // implementation "blank:coolmod-${mc_version}:${coolmod_version}"
// at runtime, use full JEI jar
runtimeOnly fg.deobf("mezz.jei:jei-${project.mcVersion}-forge:${project.jeiVersion}") // 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 { // Example configuration to allow publishing using the maven-publish plugin
archiveFileName = "${project.archivesBaseName}-${archiveVersion}.jar" publishing {
manifest { publications {
attributes([ register('mavenJava', MavenPublication) {
"Specification-Title" : project.name, from components.java
"Specification-Vendor" : project.author, }
"Specification-Version" : "1", }
"Implementation-Title" : project.name, repositories {
"Implementation-Vendor" : project.author, maven {
"Implementation-Version" : archiveVersion, url "file://${project.projectDir}/repo"
"Implementation-Timestamp": new Date().format("yyyy-MM-dd'T'HH:mm:ssZ") }
])
} }
} }
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.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 mod_id=totem_of_reviving
archivesBaseName = micles-totem-of-reviving-forge mod_name=Micle's Totem of Reviving
modID = totemofreviving mod_license=All Rights Reserved
name = Micle's Totem of Reviving mod_version=3.0.0
author = Micle mod_group_id=dev.micle
mod_authors=Micle
mod_description=Totems to revive your friends in hardcore.
buildVersion = 2.0.0 minecraft_version=1.21.1
mcVersion = 1.20.1 minecraft_version_range=[1.21.1,1.22)
forgeVersion = 47.2.20
#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 distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists 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 zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists 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", "parent": "minecraft:item/generated",
"textures": { "textures": {
"layer0": "totemofreviving:item/diamond_charge" "layer0": "totem_of_reviving:item/diamond_charge"
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,6 @@
{ {
"parent": "minecraft:item/generated", "parent": "minecraft:item/generated",
"textures": { "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": { "conditions": {
"items": [ "items": [
{ {
"items": [ "items": "totem_of_reviving:iron_totem"
"totemofreviving:iron_totem"
]
} }
] ]
}, },
@ -15,21 +13,20 @@
}, },
"has_the_recipe": { "has_the_recipe": {
"conditions": { "conditions": {
"recipe": "totemofreviving:iron_charge" "recipe": "totem_of_reviving:iron_charge"
}, },
"trigger": "minecraft:recipe_unlocked" "trigger": "minecraft:recipe_unlocked"
} }
}, },
"requirements": [ "requirements": [
[ [
"has_item", "has_the_recipe",
"has_the_recipe" "has_item"
] ]
], ],
"rewards": { "rewards": {
"recipes": [ "recipes": [
"totemofreviving:iron_charge" "totem_of_reviving:iron_charge"
] ]
}, }
"sends_telemetry_event": false
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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 dev.micle.totem_of_reviving.setup.ModItems;
import net.minecraft.data.DataGenerator; import net.minecraft.core.HolderLookup;
import net.minecraft.data.recipes.FinishedRecipe; import net.minecraft.data.PackOutput;
import net.minecraft.data.recipes.RecipeCategory; import net.minecraft.data.recipes.RecipeCategory;
import net.minecraft.data.recipes.RecipeOutput;
import net.minecraft.data.recipes.RecipeProvider; import net.minecraft.data.recipes.RecipeProvider;
import net.minecraft.data.recipes.ShapedRecipeBuilder; import net.minecraft.data.recipes.ShapedRecipeBuilder;
import net.minecraft.world.item.Items; import net.minecraft.world.item.Items;
import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.ParametersAreNonnullByDefault;
import java.util.function.Consumer; import java.util.concurrent.CompletableFuture;
public class ModRecipeProvider extends RecipeProvider { public class ModRecipeProvider extends RecipeProvider {
public ModRecipeProvider(DataGenerator generator) { public ModRecipeProvider(PackOutput packOutput, CompletableFuture<HolderLookup.Provider> registries) {
super(generator.getPackOutput()); super(packOutput, registries);
} }
@Override
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
protected void buildRecipes(Consumer<FinishedRecipe> consumer) { @Override
protected void buildRecipes(RecipeOutput consumer) {
ShapedRecipeBuilder.shaped(RecipeCategory.TOOLS, ModItems.STRAW_TOTEM.get()) ShapedRecipeBuilder.shaped(RecipeCategory.TOOLS, ModItems.STRAW_TOTEM.get())
.define('W', Items.WHEAT) .define('W', Items.WHEAT)
.define('|', Items.STICK) .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.totem_of_reviving.TotemOfReviving;
import dev.micle.totemofreviving.item.charge.DiamondChargeItem; import dev.micle.totem_of_reviving.item.charge.DiamondChargeItem;
import dev.micle.totemofreviving.item.charge.IronChargeItem; import dev.micle.totem_of_reviving.item.charge.IronChargeItem;
import dev.micle.totemofreviving.item.charge.NetheriteChargeItem; import dev.micle.totem_of_reviving.item.charge.NetheriteChargeItem;
import dev.micle.totemofreviving.item.charge.StrawChargeItem; import dev.micle.totem_of_reviving.item.charge.StrawChargeItem;
import dev.micle.totemofreviving.item.totem.DiamondTotemItem; import dev.micle.totem_of_reviving.item.totem.DiamondTotemItem;
import dev.micle.totemofreviving.item.totem.IronTotemItem; import dev.micle.totem_of_reviving.item.totem.IronTotemItem;
import dev.micle.totemofreviving.item.totem.NetheriteTotemItem; import dev.micle.totem_of_reviving.item.totem.NetheriteTotemItem;
import dev.micle.totemofreviving.item.totem.StrawTotemItem; import dev.micle.totem_of_reviving.item.totem.StrawTotemItem;
import net.minecraft.data.DataGenerator; import net.minecraft.data.DataGenerator;
import net.minecraftforge.client.model.generators.ItemModelBuilder; import net.neoforged.neoforge.client.model.generators.ItemModelBuilder;
import net.minecraftforge.client.model.generators.ItemModelProvider; import net.neoforged.neoforge.client.model.generators.ItemModelProvider;
import net.minecraftforge.client.model.generators.ModelFile; import net.neoforged.neoforge.client.model.generators.ModelFile;
import net.minecraftforge.common.data.ExistingFileHelper; import net.neoforged.neoforge.common.data.ExistingFileHelper;
public class ModItemModelProvider extends ItemModelProvider { public class ModItemModelProvider extends ItemModelProvider {
public ModItemModelProvider(DataGenerator generator, ExistingFileHelper existingFileHelper) { 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.totem_of_reviving.item.charge.DiamondChargeItem;
import dev.micle.totemofreviving.setup.Config; import dev.micle.totem_of_reviving.setup.Config;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Rarity; import net.minecraft.world.item.Rarity;
public class DiamondTotemItem extends TotemItem { public class DiamondTotemItem extends TotemItem {
public DiamondTotemItem() { public DiamondTotemItem() {
super(Rarity.RARE, Config.Server.getDiamondTotemConfig().getDurability()); super(Rarity.RARE, false);
} }
public static String getName() { public static String getName() {
@ -18,4 +18,9 @@ public class DiamondTotemItem extends TotemItem {
public boolean isCharge(ItemStack stack) { public boolean isCharge(ItemStack stack) {
return (stack.getItem() instanceof DiamondChargeItem); 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.totem_of_reviving.item.charge.NetheriteChargeItem;
import dev.micle.totemofreviving.setup.Config; import dev.micle.totem_of_reviving.setup.Config;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Rarity; import net.minecraft.world.item.Rarity;
public class NetheriteTotemItem extends TotemItem { public class NetheriteTotemItem extends TotemItem {
public NetheriteTotemItem() { public NetheriteTotemItem() {
super(Rarity.EPIC, Config.Server.getNetheriteTotemConfig().getDurability()); super(Rarity.EPIC, true);
} }
public static String getName() { public static String getName() {
@ -20,7 +20,7 @@ public class NetheriteTotemItem extends TotemItem {
} }
@Override @Override
public boolean isFireResistant() { public Config.TotemConfig getConfig() {
return true; 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.totem_of_reviving.item.charge.StrawChargeItem;
import dev.micle.totemofreviving.setup.Config; import dev.micle.totem_of_reviving.setup.Config;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Rarity; import net.minecraft.world.item.Rarity;
public class StrawTotemItem extends TotemItem { public class StrawTotemItem extends TotemItem {
public StrawTotemItem() { public StrawTotemItem() {
super(Rarity.UNCOMMON, Config.Server.getStrawTotemConfig().getDurability()); super(Rarity.COMMON, false);
} }
public static String getName() { public static String getName() {
@ -18,4 +18,9 @@ public class StrawTotemItem extends TotemItem {
public boolean isCharge(ItemStack stack) { public boolean isCharge(ItemStack stack) {
return (stack.getItem() instanceof StrawChargeItem); 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.server.MinecraftServer;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import javax.annotation.Nullable;
public interface IProxy { public interface IProxy {
@Nullable
MinecraftServer getServer(); MinecraftServer getServer();
@Nullable
Player getClientPlayer(); 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.totem_of_reviving.setup.Config;
import dev.micle.totemofreviving.setup.Config; import dev.micle.totem_of_reviving.setup.Registration;
import dev.micle.totemofreviving.setup.Registration;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.server.MinecraftServer; import net.minecraft.server.MinecraftServer;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraftforge.api.distmarker.Dist; import net.neoforged.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.neoforged.api.distmarker.OnlyIn;
import net.minecraftforge.common.MinecraftForge; import net.neoforged.bus.api.IEventBus;
import net.minecraftforge.event.AddReloadListenerEvent; import net.neoforged.fml.ModContainer;
import net.minecraftforge.event.server.ServerStartedEvent; import net.neoforged.fml.event.lifecycle.*;
import net.minecraftforge.event.server.ServerStoppingEvent; import net.neoforged.neoforge.common.NeoForge;
import net.minecraftforge.eventbus.api.IEventBus; import net.neoforged.neoforge.event.AddReloadListenerEvent;
import net.minecraftforge.fml.event.lifecycle.*; import net.neoforged.neoforge.event.server.ServerStartedEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.neoforged.neoforge.event.server.ServerStoppingEvent;
public class Proxy implements IProxy { public class Proxy implements IProxy {
// Initialize variables // Initialize variables
private static MinecraftServer server = null; private static MinecraftServer server = null;
// Common setup // Common setup
public Proxy() { public Proxy(IEventBus modEventBus, ModContainer modContainer) {
// Initialize setup // Initialize setup
Registration.register(); Registration.register(modEventBus);
Config.init(); Config.init(modContainer);
NetworkManager.init();
// Register mod event bus listeners // Register mod event bus listeners
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
modEventBus.addListener(Proxy::setup); modEventBus.addListener(Proxy::setup);
modEventBus.addListener(Proxy::imcEnqueue); modEventBus.addListener(Proxy::imcEnqueue);
modEventBus.addListener(Proxy::imcProcess); modEventBus.addListener(Proxy::imcProcess);
// Register event bus listeners // Register event bus listeners
MinecraftForge.EVENT_BUS.addListener(Proxy::onAddReloadListeners); NeoForge.EVENT_BUS.addListener(Proxy::onAddReloadListeners);
MinecraftForge.EVENT_BUS.addListener(Proxy::serverStarted); NeoForge.EVENT_BUS.addListener(Proxy::serverStarted);
MinecraftForge.EVENT_BUS.addListener(Proxy::serverStopping); NeoForge.EVENT_BUS.addListener(Proxy::serverStopping);
} }
private static void setup(FMLCommonSetupEvent event) {} private static void setup(FMLCommonSetupEvent event) {}
@ -67,15 +64,15 @@ public class Proxy implements IProxy {
} }
@Override @Override
public Level getClientWorld() { public Level getClientLevel() {
return null; return null;
} }
// Client setup // Client setup
public static class Client extends Proxy { public static class Client extends Proxy {
public Client() { public Client(IEventBus modEventBus, ModContainer modContainer) {
super(modEventBus, modContainer);
// Register mod event bus listeners // Register mod event bus listeners
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
modEventBus.addListener(Client::setup); modEventBus.addListener(Client::setup);
modEventBus.addListener(Client::postSetup); modEventBus.addListener(Client::postSetup);
} }
@ -92,16 +89,16 @@ public class Proxy implements IProxy {
@Override @Override
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
public Level getClientWorld() { public Level getClientLevel() {
return Minecraft.getInstance().level; return Minecraft.getInstance().level;
} }
} }
// Server setup // Server setup
public static class Server extends Proxy { public static class Server extends Proxy {
public Server() { public Server(IEventBus modEventBus, ModContainer modContainer) {
super(modEventBus, modContainer);
// Register mod event bus listeners // Register mod event bus listeners
IEventBus modEventBus = FMLJavaModLoadingContext.get().getModEventBus();
modEventBus.addListener(Server::setup); 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 dev.micle.totem_of_reviving.util.LangAsset;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.CreativeModeTab;
import net.minecraftforge.registries.RegistryObject; import net.neoforged.neoforge.registries.DeferredHolder;
public class ModCreativeModeTabs { 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() () -> 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) .icon(ModItems.STRAW_TOTEM.get()::getDefaultInstance)
.displayItems((itemDisplayParameters, output) -> { .displayItems((itemDisplayParameters, output) -> {
output.accept(ModItems.STRAW_TOTEM.get()); 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": { "pack": {
"description": "totemofreviving resources", "description": "totem_of_reviving resources",
"pack_format": 8 "pack_format": 8
} }
} }