first commit

This commit is contained in:
Patrick
2026-05-01 19:42:33 +02:00
commit e448a542dc
402 changed files with 23697 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
dependencies {
implementation(project(":hero-api", configuration = "namedElements"))
implementation(project(":datatracker", configuration = "namedElements"))
modApi(libs.bundles.fabric)
modApi(libs.bundles.silk)
modApi(libs.bundles.performance)
modApi(libs.owolib)
modApi(libs.geckolib)
modApi(libs.emoteLib)
}
loom {
accessWidenerPath.set(file("src/main/resources/toph.accesswidener"))
}
@@ -0,0 +1,17 @@
package gg.norisk.heroes.toph.mixin;
import gg.norisk.heroes.toph.ability.SeismicSenseAbilityKt;
import net.minecraft.client.MinecraftClient;
import net.minecraft.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(MinecraftClient.class)
public abstract class MinecraftClientMixin {
@Inject(method = "hasOutline", at = @At("RETURN"), cancellable = true)
private void injected(Entity entity, CallbackInfoReturnable<Boolean> cir) {
SeismicSenseAbilityKt.handleSeismicSenseOutline(entity, cir);
}
}
@@ -0,0 +1,23 @@
package gg.norisk.heroes.toph.mixin;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
import org.spongepowered.asm.mixin.gen.Invoker;
import java.util.List;
import java.util.Map;
@Mixin(ModelPart.class)
public interface ModelPartAccessor {
@Accessor("children")
Map<String, ModelPart> getChildren();
@Accessor("cuboids")
List<ModelPart.Cuboid> getCuboids();
@Invoker("renderCuboids")
void invokeRenderCuboids(MatrixStack.Entry entry, VertexConsumer vertexConsumer, int i, int j, int k);
}
@@ -0,0 +1,28 @@
package gg.norisk.heroes.toph.mixin.entity;
import gg.norisk.heroes.common.networking.dto.AnimationInterpolator;
import gg.norisk.heroes.toph.entity.ITrappedEntity;
import net.minecraft.entity.Entity;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
@Mixin(Entity.class)
public abstract class EntityMixin implements ITrappedEntity {
@Nullable
@Unique
private AnimationInterpolator earthRotationAnimation;
@Unique
@Nullable
@Override
public AnimationInterpolator getEarthRotationAnimation() {
return earthRotationAnimation;
}
@Unique
@Override
public void setEarthRotationAnimation(@Nullable AnimationInterpolator animationInterpolator) {
earthRotationAnimation = animationInterpolator;
}
}
@@ -0,0 +1,39 @@
package gg.norisk.heroes.toph.mixin.entity;
import gg.norisk.heroes.toph.ability.EarthArmorAbilityKt;
import gg.norisk.heroes.toph.entity.IBendingItemEntity;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.player.PlayerEntity;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.UUID;
@Mixin(ItemEntity.class)
public abstract class ItemEntityMixin implements IBendingItemEntity {
private UUID bender;
@Inject(method = "tick", at = @At("TAIL"))
private void injected(CallbackInfo ci) {
EarthArmorAbilityKt.moveToBender((ItemEntity) (Object) this);
}
@Inject(method = "onPlayerCollision", at = @At("HEAD"), cancellable = true)
private void onPlayerCollisionInjection(PlayerEntity playerEntity, CallbackInfo ci) {
EarthArmorAbilityKt.handlePlayerCollision((ItemEntity) (Object) this, playerEntity, ci);
}
@Override
public void setBender(UUID bender) {
this.bender = bender;
}
@Nullable
@Override
public UUID getBender() {
return bender;
}
}
@@ -0,0 +1,40 @@
package gg.norisk.heroes.toph.mixin.entity;
import gg.norisk.heroes.toph.entity.ITophPlayer;
import io.netty.util.internal.ConcurrentSet;
import kotlin.Pair;
import kotlinx.coroutines.Job;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.BlockPos;
import org.jetbrains.annotations.NotNull;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@Mixin(PlayerEntity.class)
public abstract class PlayerEntityMixin implements ITophPlayer {
private final Set<Pair<Long, BlockPos>> seismicBlocks = ConcurrentHashMap.newKeySet();
private final Set<Pair<Long, UUID>> seismicEntities = ConcurrentHashMap.newKeySet();
@Unique
private final List<Job> seismicTasks = new ArrayList<>();
@NotNull
@Override
public Set<Pair<Long, BlockPos>> getSeismicBlocks() {
return seismicBlocks;
}
@NotNull
@Override
public Set<Pair<Long, UUID>> getSeismicEntities() {
return seismicEntities;
}
@Override
public @NotNull List<Job> getToph_seismicTasks() {
return seismicTasks;
}
}
@@ -0,0 +1,12 @@
package gg.norisk.heroes.toph.mixin.render;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.entity.model.AnimalModel;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(AnimalModel.class)
public interface AnimalModelAccessor {
@Invoker("getBodyParts")
Iterable<ModelPart> invokeGetBodyParts();
}
@@ -0,0 +1,38 @@
package gg.norisk.heroes.toph.mixin.render;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
import gg.norisk.heroes.toph.ability.SeismicSenseAbilityKt;
import net.minecraft.client.render.BackgroundRenderer;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.player.PlayerEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Constant;
import org.spongepowered.asm.mixin.injection.ModifyConstant;
@Mixin(BackgroundRenderer.DarknessFogModifier.class)
public abstract class DarknessFogModifierMixin {
/* @ModifyConstant(method = "applyStartEndModifier", constant = @Constant(floatValue = 15.0F))
private float injected(float constant, BackgroundRenderer.FogData fogData, LivingEntity livingEntity, StatusEffectInstance statusEffectInstance, float f, float g) {
if (livingEntity instanceof PlayerEntity player) {
var value = SeismicSenseAbilityKt.handleSeismicSenseDarkness(player);
System.out.println("Returning " + (value == -1f ? constant : value));
return value == -1f ? constant : value;
}
return constant;
} */
@ModifyExpressionValue(
method = "applyStartEndModifier",
at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/MathHelper;lerp(FFF)F")
)
private float halveSpeed(float original, BackgroundRenderer.FogData fogData, LivingEntity livingEntity, StatusEffectInstance statusEffectInstance, float f, float g) {
if (livingEntity instanceof PlayerEntity player) {
var value = SeismicSenseAbilityKt.handleSeismicSenseDarkness(player, original);
return value;
}
return original;
}
}
@@ -0,0 +1,12 @@
package gg.norisk.heroes.toph.mixin.render;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(GameRenderer.class)
public interface GameRendererAccessor {
@Invoker("loadPostProcessor")
void invokeLoadPostProcessor(Identifier id);
}
@@ -0,0 +1,31 @@
package gg.norisk.heroes.toph.mixin.render;
import gg.norisk.heroes.toph.ability.SeismicSenseAbilityKt;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.render.GameRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.util.Identifier;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(GameRenderer.class)
public abstract class GameRendererMixin {
@Shadow
abstract void loadPostProcessor(Identifier identifier);
@Inject(at = @At("HEAD"), method = "togglePostProcessorEnabled", cancellable = true)
private void togglePostProcessorEnabledInjection(CallbackInfo ci) {
SeismicSenseAbilityKt.handleSeismicSenseShader(ci);
}
@Inject(at = @At("TAIL"), method = "onCameraEntitySet", cancellable = true)
private void onCameraEntitySet(Entity entity, CallbackInfo ci) {
var player = MinecraftClient.getInstance().player;
if (player != null && SeismicSenseAbilityKt.getHasSeismicSense(player)) {
this.loadPostProcessor(SeismicSenseAbilityKt.getSeismicSenseShader());
}
}
}
@@ -0,0 +1,16 @@
package gg.norisk.heroes.toph.mixin.render;
import net.minecraft.block.BlockState;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.WorldRenderer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.BlockPos;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(WorldRenderer.class)
public interface WorldRendererAccessor {
@Invoker("drawBlockOutline")
void invokeDrawBlockOutline(MatrixStack matrixStack, VertexConsumer vertexConsumer, Entity entity, double d, double e, double f, BlockPos blockPos, BlockState blockState);
}
@@ -0,0 +1,62 @@
package gg.norisk.heroes.toph
import gg.norisk.heroes.common.hero.Hero
import gg.norisk.heroes.common.hero.HeroManager.registerHero
import gg.norisk.heroes.toph.ability.*
import gg.norisk.heroes.toph.ability.EarthArmorAttributeModifiers.EarthArmorAbility
import gg.norisk.heroes.toph.particle.EarthDustParticle
import gg.norisk.heroes.toph.registry.ParticleRegistry
import gg.norisk.heroes.toph.registry.SoundRegistry
import net.fabricmc.api.ClientModInitializer
import net.fabricmc.api.DedicatedServerModInitializer
import net.fabricmc.api.ModInitializer
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents
import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry
import net.minecraft.block.BlockState
import net.minecraft.particle.ParticleEffect
import net.minecraft.particle.ParticleType
import net.minecraft.registry.tag.BlockTags
import net.minecraft.util.Identifier
import org.apache.logging.log4j.LogManager
import java.awt.Color
object TophManager : ModInitializer, ClientModInitializer, DedicatedServerModInitializer {
private const val MOD_ID = "toph"
val logger = LogManager.getLogger(MOD_ID)
fun String.toId() = Identifier.of(MOD_ID, this)
fun String.toEmote(): Identifier {
return "emotes/$this.animation.json".toId()
}
override fun onInitialize() {
ParticleRegistry.init()
SoundRegistry.init()
logger.info("Starting $MOD_ID Hero...")
}
override fun onInitializeClient() {
ParticleFactoryRegistry.getInstance()
.register(ParticleRegistry.EARTH_DUST as ParticleType<ParticleEffect>, EarthDustParticle::CosySmokeFactory)
ClientLifecycleEvents.CLIENT_STARTED.register {
registerHero(Toph)
}
}
override fun onInitializeServer() {
registerHero(Toph)
}
val Toph by Hero("Toph") {
ability(EarthArmorAbility)
ability(SeismicSenseAbility)
ability(EarthColumnInstantAbility)
ability(EarthSurfAbility)
ability(EarthPushAbility)
ability(EarthTrapAbility)
color = Color.GREEN.rgb
overlaySkin = "textures/toph_overlay.png".toId()
}
val BlockState.isEarthBlock
get() = isIn(BlockTags.PICKAXE_MINEABLE) || isIn(BlockTags.SHOVEL_MINEABLE)
}
@@ -0,0 +1,271 @@
package gg.norisk.heroes.toph.ability
import gg.norisk.emote.network.EmoteNetworking.playEmote
import gg.norisk.heroes.client.option.HeroKeyBindings
import gg.norisk.heroes.common.HeroesManager.client
import gg.norisk.heroes.common.ability.NumberProperty
import gg.norisk.heroes.common.ability.operation.AddValueTotal
import gg.norisk.heroes.common.hero.ability.AbilityScope
import gg.norisk.heroes.common.hero.ability.implementation.PressAbility
import gg.norisk.heroes.common.networking.BoomShake
import gg.norisk.heroes.common.networking.cameraShakePacket
import gg.norisk.heroes.common.utils.pos3i
import gg.norisk.heroes.toph.TophManager.toEmote
import gg.norisk.heroes.toph.TophManager.toId
import gg.norisk.heroes.toph.entity.IBendingItemEntity
import gg.norisk.heroes.toph.registry.SoundRegistry
import gg.norisk.heroes.toph.render.ChestItemFeatureRenderer
import io.wispforest.owo.ui.component.Components
import io.wispforest.owo.ui.core.Component
import net.fabricmc.fabric.api.client.rendering.v1.LivingEntityFeatureRendererRegistrationCallback
import net.minecraft.client.network.AbstractClientPlayerEntity
import net.minecraft.client.render.entity.feature.FeatureRendererContext
import net.minecraft.client.render.entity.model.PlayerEntityModel
import net.minecraft.component.DataComponentTypes
import net.minecraft.component.type.AttributeModifierSlot
import net.minecraft.component.type.AttributeModifiersComponent
import net.minecraft.entity.EntityType
import net.minecraft.entity.EquipmentSlot
import net.minecraft.entity.ItemEntity
import net.minecraft.entity.attribute.EntityAttributeModifier
import net.minecraft.entity.attribute.EntityAttributes
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.BlockItem
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import net.minecraft.particle.BlockStateParticleEffect
import net.minecraft.particle.ParticleTypes
import net.minecraft.registry.tag.BlockTags
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.sound.SoundCategory
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.math.Direction
import net.silkmc.silk.core.entity.modifyVelocity
import net.silkmc.silk.core.entity.touchedBlockNoAir
import net.silkmc.silk.core.kotlin.ticks
import net.silkmc.silk.core.math.geometry.filledSpherePositionSet
import net.silkmc.silk.core.task.mcCoroutineTask
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo
import kotlin.random.Random
object EarthArmorAttributeModifiers {
val earthArmorArmorProperty = NumberProperty(
2.0, 3,
"Armor",
AddValueTotal(1.0, 1.0, 1.0),
).apply {
icon = {
Components.item(Items.IRON_CHESTPLATE.defaultStack)
}
}
val earthArmorKnockbackProperty = NumberProperty(
0.05, 3,
"Knockback",
AddValueTotal(-0.01, -0.02, -0.03)
).apply {
icon = {
Components.item(Items.ANVIL.defaultStack)
}
}
val earthArmorSpeedProperty = NumberProperty(
-0.005, 3,
"Speed",
AddValueTotal(0.002, 0.002, 0.002),
).apply {
icon = {
Components.item(Items.SUGAR.defaultStack)
}
}
fun addTo(stack: ItemStack, player: PlayerEntity) {
val ARMOR_ENTRY = AttributeModifiersComponent.Entry(
EntityAttributes.GENERIC_ARMOR,
EntityAttributeModifier(
"armor_modifier".toId(),
earthArmorArmorProperty.getValue(player.uuid),
EntityAttributeModifier.Operation.ADD_VALUE
),
AttributeModifierSlot.ARMOR
)
val KNOCKBACK_ENTRY = AttributeModifiersComponent.Entry(
EntityAttributes.GENERIC_ATTACK_KNOCKBACK,
EntityAttributeModifier(
"knockback_modifier".toId(),
earthArmorKnockbackProperty.getValue(player.uuid),
EntityAttributeModifier.Operation.ADD_VALUE
),
AttributeModifierSlot.ARMOR
)
val SPEED_ENTRY = AttributeModifiersComponent.Entry(
EntityAttributes.GENERIC_MOVEMENT_SPEED,
EntityAttributeModifier(
"speed_modifier".toId(),
earthArmorSpeedProperty.getValue(player.uuid),
EntityAttributeModifier.Operation.ADD_MULTIPLIED_TOTAL
),
AttributeModifierSlot.ARMOR
)
val componentFromStack =
stack.get(DataComponentTypes.ATTRIBUTE_MODIFIERS) ?: AttributeModifiersComponent.DEFAULT
val newList: MutableList<AttributeModifiersComponent.Entry> = ArrayList()
if (componentFromStack != null) {
newList.addAll(componentFromStack.modifiers)
newList.add(ARMOR_ENTRY)
newList.add(KNOCKBACK_ENTRY)
newList.add(SPEED_ENTRY)
}
stack.set(
DataComponentTypes.ATTRIBUTE_MODIFIERS,
AttributeModifiersComponent(newList, true)
)
}
val EarthArmorAbility = object : PressAbility("Earth Armor") {
init {
client {
this.keyBind = HeroKeyBindings.firstKeyBind
LivingEntityFeatureRendererRegistrationCallback.EVENT.register { entityType, entityRenderer, registrationHelper, context ->
if (entityType !== EntityType.PLAYER) return@register
registrationHelper.register(
ChestItemFeatureRenderer(
entityRenderer as FeatureRendererContext<AbstractClientPlayerEntity, PlayerEntityModel<AbstractClientPlayerEntity>>,
context.heldItemRenderer
)
)
}
}
this.properties = listOf(earthArmorArmorProperty, earthArmorSpeedProperty, earthArmorKnockbackProperty)
this.cooldownProperty =
buildCooldown(10.0, 5, AddValueTotal(-0.1, -0.4, -0.2, -0.8, -1.5, -1.0))
}
override fun getIconComponent(): Component {
return Components.item(Items.IRON_CHESTPLATE.defaultStack)
}
override fun getBackgroundTexture(): Identifier {
return Identifier.of("textures/block/packed_mud.png")
}
override fun onStart(player: PlayerEntity, abilityScope: AbilityScope) {
super.onStart(player, abilityScope)
if (player is ServerPlayerEntity) {
player.swingHand(Hand.MAIN_HAND, true)
val world = player.world as ServerWorld
player.playEmote("earth-armor".toEmote())
(player as ServerPlayerEntity).spawnEarthArmorParticle()
world.playSoundFromEntity(
null,
player,
SoundRegistry.EARTH_ARMOR,
SoundCategory.PLAYERS,
1f,
1f
)
cameraShakePacket.send(BoomShake(0.1, 0.2, 0.4), player)
player.pos3i.filledSpherePositionSet(4)
.filter {
world.getBlockState(it).isIn(BlockTags.PICKAXE_MINEABLE) || world.getBlockState(it)
.isIn(BlockTags.SHOVEL_MINEABLE)
}
.filter { pos -> Direction.values().any { world.getBlockState(pos.offset(it)).isAir } }
.shuffled()
.take(4)
.forEachIndexed { index, blockPos ->
mcCoroutineTask(client = false, sync = true, delay = index.ticks) {
val center = blockPos.toCenterPos()
val state = world.getBlockState(blockPos)
val itemStack = state.block.asItem().defaultStack
val itemEntity =
ItemEntity(world, center.x, center.y, center.z, itemStack)
(itemEntity as IBendingItemEntity).bender = player.uuid
world.spawnEntity(itemEntity)
world.breakBlock(blockPos, false, player)
}
}
}
}
}
}
fun ItemEntity.handlePlayerCollision(player: PlayerEntity, ci: CallbackInfo) {
val dummy = this as IBendingItemEntity
if (world.isClient) return
if (bender == player.uuid) {
ci.cancel()
discard()
val blockItem = this.stack.item as? BlockItem ?: return
world.playSoundFromEntity(
null,
player,
blockItem.block.defaultState.soundGroup.placeSound,
SoundCategory.PLAYERS,
0.7f,
1f
)
val slot = EquipmentSlot.entries
.filter { it.isArmorSlot }
.firstOrNull { player.getEquippedStack(it).isEmpty }
?: return
EarthArmorAttributeModifiers.addTo(stack, player)
player.equipStack(slot, this.stack)
}
}
fun ServerPlayerEntity.spawnEarthArmorParticle() {
val block = this.touchedBlockNoAir ?: return
repeat(20) {
(world as ServerWorld).spawnParticles(
BlockStateParticleEffect(ParticleTypes.BLOCK, block.block.defaultState),
this.x,
getBodyY(Random.nextDouble(0.1, 0.5)),
this.z,
7,
(this.width / 4.0f).toDouble(),
(this.height / 4.0f).toDouble(),
(this.width / 4.0f).toDouble(),
0.05
)
}
}
fun ItemEntity.moveToBender() {
val dummy = this as IBendingItemEntity
if (world.isClient) return
val block = this.stack.item as? BlockItem ?: return
if (bender != null) {
val player = world.getPlayerByUuid(bender)
if (player == null) {
discard()
return
}
val direction = player.eyePos.subtract(this.pos).normalize().multiply(0.5)
modifyVelocity(direction)
(world as ServerWorld).spawnParticles(
BlockStateParticleEffect(ParticleTypes.BLOCK, block.block.defaultState),
this.x,
getBodyY(0.6666666666666666),
this.z,
7,
(this.width / 4.0f).toDouble(),
(this.height / 4.0f).toDouble(),
(this.width / 4.0f).toDouble(),
0.05
)
}
}
@@ -0,0 +1,339 @@
package gg.norisk.heroes.toph.ability
import gg.norisk.datatracker.entity.getSyncedData
import gg.norisk.datatracker.entity.setSyncedData
import gg.norisk.emote.network.EmoteNetworking.playEmote
import gg.norisk.emote.network.EmoteNetworking.stopEmote
import gg.norisk.heroes.client.events.ClientEvents
import gg.norisk.heroes.client.option.HeroKeyBindings
import gg.norisk.heroes.client.renderer.BlockOutlineRenderer
import gg.norisk.heroes.common.HeroesManager.client
import gg.norisk.heroes.common.ability.NumberProperty
import gg.norisk.heroes.common.ability.operation.AddValueTotal
import gg.norisk.heroes.common.hero.ability.AbilityScope
import gg.norisk.heroes.common.hero.ability.implementation.HoldAbility
import gg.norisk.heroes.common.hero.isHero
import gg.norisk.heroes.common.networking.BoomShake
import gg.norisk.heroes.common.networking.Networking.mouseScrollPacket
import gg.norisk.heroes.common.networking.cameraShakePacket
import gg.norisk.heroes.common.networking.dto.BlockInfoSmall
import gg.norisk.heroes.common.serialization.BlockPosSerializer
import gg.norisk.heroes.common.utils.random
import gg.norisk.heroes.common.utils.sound
import gg.norisk.heroes.toph.TophManager
import gg.norisk.heroes.toph.TophManager.toEmote
import gg.norisk.heroes.toph.TophManager.toId
import gg.norisk.heroes.toph.network.earthColumnBlockInfos
import gg.norisk.heroes.toph.registry.ParticleRegistry
import gg.norisk.heroes.toph.registry.SoundRegistry
import io.wispforest.owo.ui.component.Components
import io.wispforest.owo.ui.core.Component
import kotlinx.serialization.Serializable
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents
import net.minecraft.block.BlockState
import net.minecraft.client.MinecraftClient
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.attribute.EntityAttributeModifier
import net.minecraft.entity.attribute.EntityAttributes
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.Items
import net.minecraft.network.packet.s2c.play.PositionFlag
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.sound.SoundCategory
import net.minecraft.util.Identifier
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.hit.HitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
import net.minecraft.util.math.Vec3d
import net.minecraft.world.World
import net.silkmc.silk.core.entity.modifyVelocity
import net.silkmc.silk.core.kotlin.ticks
import net.silkmc.silk.core.math.geometry.produceCirclePositions
import net.silkmc.silk.core.math.geometry.produceFilledSpherePositions
import net.silkmc.silk.core.task.mcCoroutineTask
val EarthColumnKey = "isEarthColumn"
val EarthColumnRadiusKey = "earth_column_radius"
//TODO enrico das ist ein fall für dich und deine descriptions oder?
@Serializable
data class EarthColumnDescription(
val blocks: List<BlockInfoSmall>,
val radius: Int,
val center: @Serializable(with = BlockPosSerializer::class) BlockPos,
)
val earthColumnRadius = NumberProperty(2.0, 3, "Radius", AddValueTotal(1.0, 1.0, 1.0)).apply {
icon = {
Components.item(Items.STONE_SHOVEL.defaultStack)
}
}
val earthColumnBoost = NumberProperty(1.0, 2, "Earth Column Boost", AddValueTotal(1.0, 1.0)).apply {
icon = {
Components.item(Items.FIREWORK_ROCKET.defaultStack)
}
}
val EarthColumnInstantAbility: HoldAbility = object : HoldAbility(
"Earth Column"
) {
val earthColumnHeight = NumberProperty(3.0, 5, "Height", AddValueTotal(1.0, 1.0, 1.0, 3.0, 2.0)).apply {
icon = {
Components.item(Items.STONE.defaultStack)
}
}
init {
client {
this.keyBind = HeroKeyBindings.thirdKeyBind
WorldRenderEvents.AFTER_TRANSLUCENT.register {
val world = it.world()
val player = MinecraftClient.getInstance().player ?: return@register
val matrices = it.matrixStack() ?: return@register
if (!player.isEarthColumn()) return@register
val tickCounter = it.tickCounter()
val radius = player.getSyncedData<Int>(EarthColumnRadiusKey) ?: 1
val hitResult = player.raycast(maxDistance, tickCounter.getTickDelta(false), false)
if (hitResult != null && hitResult.type == HitResult.Type.BLOCK) {
((hitResult as BlockHitResult).blockPos).produceFilledSpherePositions(radius) { pos ->
val blockState: BlockState = world.getBlockState(pos)
if (world.canBeBended(pos, blockState) && blockState.isSolid) {
BlockOutlineRenderer.drawBlockBox(
matrices,
it.consumers() ?: return@produceFilledSpherePositions,
pos,
1.0f,
1.0f,
1.0f,
0.5f
)
}
}
}
}
ClientEvents.preHotbarScrollEvent.listen { event ->
val player = MinecraftClient.getInstance().player ?: return@listen
if (player.isEarthColumn()) {
event.isCancelled.set(true)
}
}
}
this.properties = listOf(
earthColumnRadius,
earthColumnHeight,
earthColumnBoost
)
this.cooldownProperty =
buildCooldown(110.0, 4, AddValueTotal(-10.0, -10.0, -10.0, -10.0))
this.maxDurationProperty =
buildMaxDuration(5.0, 5, AddValueTotal(0.1, 0.4, 0.2, 0.8, 1.5, 1.0))
mouseScrollPacket.receiveOnServer { packet, context ->
mcCoroutineTask(sync = true, client = false) {
val player = context.player
if (!player.isHero(TophManager.Toph)) return@mcCoroutineTask
if (!player.isEarthColumn()) return@mcCoroutineTask
var radius = player.getSyncedData<Int>(EarthColumnRadiusKey) ?: 1
radius += if (!packet) -1 else 1
if (radius <= 0) {
radius = 1
}
if (radius >= earthColumnRadius.getValue(player.uuid)) {
radius = earthColumnRadius.getValue(player.uuid).toInt()
}
player.setSyncedData(EarthColumnRadiusKey, radius)
}
}
earthColumnBlockInfos.receiveOnServer { earthColumn, context ->
val world = context.player.serverWorld
val player = context.player
if (!player.isHero(TophManager.Toph)) return@receiveOnServer
mcCoroutineTask(
sync = true,
client = false,
howOften = earthColumnHeight.getValue(player.uuid).toLong(),
period = 0.ticks
) {
earthColumn.move(world, it.round.toInt(), it.counterDownToZero == 0L, player)
}
}
}
override fun getIconComponent(): Component {
return Components.item(Items.STONE.defaultStack)
}
override fun getBackgroundTexture(): Identifier {
return Identifier.of("textures/block/packed_mud.png")
}
val maxDistance = 45.0
val EARTH_COLUMN_SLOW_BOOST = EntityAttributeModifier(
"earth_column".toId(),
-0.7,
EntityAttributeModifier.Operation.ADD_MULTIPLIED_TOTAL
)
override fun onStart(player: PlayerEntity, abilityScope: AbilityScope) {
super.onStart(player, abilityScope)
if (player is ServerPlayerEntity) {
player.setSyncedData(EarthColumnKey, true)
player.playEmote("earth-column-start".toEmote())
player.world.playSoundFromEntity(
null,
player,
SoundRegistry.ARM_WHOOSH,
SoundCategory.PLAYERS,
1f,
1f
)
runCatching {
player.getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED)
?.addTemporaryModifier(EARTH_COLUMN_SLOW_BOOST)
}
}
}
override fun onDisable(player: PlayerEntity) {
super.onDisable(player)
cleanUp(player)
}
private fun cleanUp(player: PlayerEntity) {
if (player is ServerPlayerEntity) {
player.stopEmote("earth-column-start".toEmote())
player.setSyncedData(EarthColumnKey, false)
player.getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED)
?.removeModifier(EARTH_COLUMN_SLOW_BOOST.id)
}
}
override fun onEnd(player: PlayerEntity, abilityEndInformation: AbilityEndInformation) {
super.onEnd(player, abilityEndInformation)
if (player is ServerPlayerEntity) {
val startTime = System.currentTimeMillis()
val world = player.world as ServerWorld
player.stopEmote("earth-column-start".toEmote())
player.playEmote("earth-column-end".toEmote())
player.world.playSoundFromEntity(
null,
player,
SoundRegistry.EARTH_ARMOR,
SoundCategory.PLAYERS,
0.6f,
1.4f
)
cameraShakePacket.send(BoomShake(0.1, 0.2, 0.4), player as ServerPlayerEntity)
player.sound(SoundRegistry.STONE_SMASH)
player.setSyncedData(EarthColumnKey, false)
player.getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED)
?.removeModifier(EARTH_COLUMN_SLOW_BOOST.id)
} else if (player == MinecraftClient.getInstance().player) {
val world = player.world
val tickDelta = MinecraftClient.getInstance().renderTickCounter.getTickDelta(false)
val hitResult = player.raycast(maxDistance, tickDelta, false)
if (hitResult != null && hitResult.type == HitResult.Type.BLOCK && player.isEarthColumn()) {
val blockInfos = mutableSetOf<BlockInfoSmall>()
val radius = player.getSyncedData<Int>(EarthColumnRadiusKey) ?: 1
((hitResult as BlockHitResult).blockPos).produceFilledSpherePositions(radius) { pos ->
val blockState: BlockState = world.getBlockState(pos)
if (world.canBeBended(pos, blockState)) {
repeat(32) {
val newPos = pos.up(it)
val newBlockState = world.getBlockState(newPos)
if (it == 0 && newBlockState.isSolid) {
blockInfos.add(BlockInfoSmall(world.getBlockState(pos.down(1)), pos.down(1)))
}
blockInfos.add(BlockInfoSmall(newBlockState, newPos))
}
}
}
earthColumnBlockInfos.send(
EarthColumnDescription(
blockInfos.toList(), radius, hitResult.blockPos
)
)
}
}
}
}
private fun EarthColumnDescription.move(
world: ServerWorld,
height: Int,
isFinished: Boolean,
player: ServerPlayerEntity
) {
blocks.forEach { (state, pos) ->
val newPos = pos.up(height)
if (world.getBlockState(newPos) == state) return@forEach
world.setBlockState(newPos, state)
world.getOtherEntities(null, Box.from(newPos.toCenterPos())).filterIsInstance<LivingEntity>()
.forEach { entity ->
entity.teleport(
world,
entity.x,
entity.y + 1,
entity.z,
PositionFlag.VALUES,
entity.yaw,
entity.pitch
)
}
}
if (isFinished) {
mcCoroutineTask(sync = true, client = false, delay = 0.ticks) {
blocks.forEach { (state, pos) ->
if (state.isAir) return@forEach
val newPos = pos.up(it.round.toInt()).up().toCenterPos()
world.getOtherEntities(null, Box.from(newPos).expand(earthColumnRadius.getValue(player.uuid)))
.filterIsInstance<LivingEntity>()
.forEach { entity ->
entity.damage(entity.damageSources.playerAttack(player), 0.001f)
if (!entity.isSneaking) {
entity.modifyVelocity(Vec3d(0.0, earthColumnBoost.getValue(player.uuid), 0.0))
}
}
}
}
}
center.up(height).apply {
world.playSound(null, this, SoundRegistry.EARTH_COLUMN_1, SoundCategory.BLOCKS, 1f, 1f)
}
center.up().produceCirclePositions(radius) { pos ->
val particlePos = pos.toCenterPos()
world.spawnParticles(
ParticleRegistry.EARTH_DUST,
particlePos.x,
particlePos.y - 0.3,
particlePos.z,
if (radius == 1) 1 else 5,
(0.1..0.4).random(),
(0.1..0.4).random(),
(0.1..0.4).random(),
(0.01..0.04).random()
)
}
}
fun World.canBeBended(blockPos: BlockPos, blockState: BlockState): Boolean {
return true
}
fun PlayerEntity.isEarthColumn() = getSyncedData<Boolean>(EarthColumnKey) == true
@@ -0,0 +1,124 @@
package gg.norisk.heroes.toph.ability
import gg.norisk.emote.network.EmoteNetworking.playEmote
import gg.norisk.heroes.client.option.HeroKeyBindings
import gg.norisk.heroes.common.HeroesManager.client
import gg.norisk.heroes.common.ability.NumberProperty
import gg.norisk.heroes.common.ability.operation.AddValueTotal
import gg.norisk.heroes.common.hero.ability.AbilityScope
import gg.norisk.heroes.common.hero.ability.implementation.PressAbility
import gg.norisk.heroes.common.networking.BoomShake
import gg.norisk.heroes.common.networking.Networking.mousePacket
import gg.norisk.heroes.common.networking.cameraShakePacket
import gg.norisk.heroes.common.utils.RaycastUtils
import gg.norisk.heroes.common.utils.random
import gg.norisk.heroes.common.utils.sound
import gg.norisk.heroes.toph.TophManager.isEarthBlock
import gg.norisk.heroes.toph.TophManager.toEmote
import gg.norisk.heroes.toph.entity.BendingBlockEntity
import gg.norisk.heroes.toph.entity.BendingBlockEntity.Companion.owner
import gg.norisk.heroes.toph.registry.ParticleRegistry
import gg.norisk.heroes.toph.registry.SoundRegistry
import io.wispforest.owo.ui.component.Components
import io.wispforest.owo.ui.core.Component
import net.minecraft.entity.FallingBlockEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.Items
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.sound.SoundCategory
import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec3d
import net.silkmc.silk.core.entity.modifyVelocity
import kotlin.random.Random
val earthPushDamage = NumberProperty(2.0, 3, "Damage", AddValueTotal(1.25, 1.25, 2.0)).apply {
icon = {
Components.item(Items.STONE_SWORD.defaultStack)
}
}
val EarthPushAbility = object : PressAbility("Earth Push") {
init {
client {
this.keyBind = HeroKeyBindings.pickItemKeyBinding
}
this.properties = listOf(earthPushDamage)
this.cooldownProperty =
buildCooldown(10.0, 5, AddValueTotal(-0.1, -0.4, -0.2, -0.8, -1.5, -1.0))
mousePacket.receiveOnServer { packet, context ->
val player = context.player
val world = player.serverWorld
if (packet.isLeft() && packet.isClicked()) {
val entity = player.getForcedBlocks().filterIsInstance<BendingBlockEntity>().filter {
it.distanceTo(player) < 6.0
}.randomOrNull()
?: return@receiveOnServer
cameraShakePacket.send(BoomShake(0.1, 0.2, 0.4), player as ServerPlayerEntity)
player.sound(SoundRegistry.STONE_SMASH)
player.sound(SoundRegistry.EARTH_COLUMN_1)
entity.forcePush(player)
player.playEmote("earth-kick".toEmote())
}
}
}
override fun getIconComponent(): Component {
return Components.item(Items.DIRT.defaultStack)
}
override fun getBackgroundTexture(): Identifier {
return Identifier.of("textures/block/packed_mud.png")
}
override fun onStart(player: PlayerEntity, abilityScope: AbilityScope) {
super.onStart(player, abilityScope)
if (player is ServerPlayerEntity) {
val world = player.world as ServerWorld
val clipWithDistance = RaycastUtils.clipWithDistance(player, player.world, 4.5) ?: return
val pos = clipWithDistance.blockPos.toCenterPos()
val state = world.getBlockState(clipWithDistance.blockPos)
abilityScope.cancelCooldown()
if (!state.isEarthBlock) return
abilityScope.applyCooldown()
val entity = BendingBlockEntity(world, pos.x, pos.y, pos.z, state)
cameraShakePacket.send(BoomShake(0.1, 0.2, 0.4), player as ServerPlayerEntity)
entity.canAttack = true
entity.owner = player.uuid
world.spawnEntity(entity)
world.playSound(
null,
clipWithDistance.blockPos,
state.soundGroup.breakSound,
SoundCategory.BLOCKS,
0.3f,
Random.nextDouble().toFloat() * 0.8f
)
player.sound(SoundRegistry.EARTH_COLUMN_1)
world.breakBlock(clipWithDistance.blockPos, false, player)
entity.modifyVelocity(Vec3d(0.0, 0.5, 0.0))
(world as? ServerWorld?)?.spawnParticles(
ParticleRegistry.EARTH_DUST,
pos.x,
pos.y + 1,
pos.z,
7,
(0.01..0.04).random(),
(0.01..0.04).random(),
(0.01..0.04).random(),
(0.01..0.04).random()
)
}
}
}
fun ServerPlayerEntity.getForcedBlocks(): List<FallingBlockEntity> {
return serverWorld.iterateEntities()
.filterIsInstance<BendingBlockEntity>()
.filter { it.owner == this.uuid }
.toList()
}
@@ -0,0 +1,246 @@
package gg.norisk.heroes.toph.ability
import gg.norisk.datatracker.entity.getSyncedData
import gg.norisk.datatracker.entity.setSyncedData
import gg.norisk.datatracker.entity.syncedValueChangeEvent
import gg.norisk.emote.network.EmoteNetworking.playEmote
import gg.norisk.emote.network.EmoteNetworking.stopEmote
import gg.norisk.heroes.client.events.ClientEvents
import gg.norisk.heroes.client.option.HeroKeyBindings
import gg.norisk.heroes.client.renderer.Speedlines.showSpeedlines
import gg.norisk.heroes.common.HeroesManager.client
import gg.norisk.heroes.common.ability.NumberProperty
import gg.norisk.heroes.common.ability.operation.AddValueTotal
import gg.norisk.heroes.common.hero.ability.AbilityScope
import gg.norisk.heroes.common.hero.ability.implementation.ToggleAbility
import gg.norisk.heroes.common.networking.BoomShake
import gg.norisk.heroes.common.networking.cameraShakePacket
import gg.norisk.heroes.common.utils.calculateProbability
import gg.norisk.heroes.common.utils.random
import gg.norisk.heroes.common.utils.sound
import gg.norisk.heroes.common.utils.toBlockPos
import gg.norisk.heroes.toph.TophManager.toEmote
import gg.norisk.heroes.toph.TophManager.toId
import gg.norisk.heroes.toph.registry.ParticleRegistry
import gg.norisk.heroes.toph.registry.SoundRegistry
import gg.norisk.heroes.toph.sound.StoneSlideSoundInstance
import io.wispforest.owo.ui.component.Components
import io.wispforest.owo.ui.core.Component
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents
import net.minecraft.block.Blocks
import net.minecraft.client.MinecraftClient
import net.minecraft.entity.Entity
import net.minecraft.entity.FallingBlockEntity
import net.minecraft.entity.attribute.EntityAttributeModifier
import net.minecraft.entity.attribute.EntityAttributes
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.Items
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.sound.SoundCategory
import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec3d
import net.silkmc.silk.core.annotations.ExperimentalSilkApi
import net.silkmc.silk.core.entity.modifyVelocity
import net.silkmc.silk.core.item.itemStack
import net.silkmc.silk.core.math.geometry.filledSpherePositionSet
import net.silkmc.silk.core.server.players
import kotlin.math.cos
import kotlin.math.sin
val EarthSurfKey = "isEarthSurfing"
val earthSurfRadius = NumberProperty(1.0, 3, "Radius", AddValueTotal(1.0, 1.0, 1.0), 20).apply {
icon = {
Components.item(Items.STONE_SHOVEL.defaultStack)
}
}
@OptIn(ExperimentalSilkApi::class)
val EarthSurfAbility = object : ToggleAbility("Earth Surf") {
val earthSurfStepHeight = NumberProperty(3.0, 3, "Step Height", AddValueTotal(1.0, 1.0, 1.0)).apply {
icon = {
Components.item(Items.MUD_BRICK_STAIRS.defaultStack)
}
}
val earthSurfSpeedBoost = NumberProperty(1.1, 4, "Speed", AddValueTotal(0.1, 0.1, 0.1, 0.3)).apply {
icon = {
Components.item(Items.SUGAR.defaultStack)
}
}
init {
client {
this.keyBind = HeroKeyBindings.secondKeyBind
ClientEvents.cameraClipToSpaceEvent.listen { event ->
val player = MinecraftClient.getInstance().player ?: return@listen
if (player.isEarthSurfing()) {
event.value = 8.0
}
}
}
this.properties = listOf(earthSurfStepHeight, earthSurfSpeedBoost, earthSurfRadius)
this.cooldownProperty =
buildCooldown(60.0, 5, AddValueTotal(-5.0, -5.0, -5.0, -5.0,-5.0))
this.maxDurationProperty =
buildMaxDuration(5.0, 5, AddValueTotal(0.1, 0.4, 0.2, 0.8, 1.5, 1.0))
syncedValueChangeEvent.listen {
val player = it.entity as? PlayerEntity ?: return@listen
if (it.key == EarthSurfKey) {
if (player.isEarthSurfing()) {
player.attributes.getCustomInstance(EntityAttributes.GENERIC_STEP_HEIGHT)?.baseValue =
earthSurfStepHeight.getValue(player.uuid)
if (player.world.isClient) {
MinecraftClient.getInstance().soundManager.play(StoneSlideSoundInstance(player))
}
} else {
player.attributes.getCustomInstance(EntityAttributes.GENERIC_STEP_HEIGHT)?.baseValue = 0.6
}
}
}
ServerTickEvents.START_SERVER_TICK.register { server ->
for (player in server.players.filter { it.isEarthSurfing() }) {
player.spawnEarthCircle()
}
}
}
override fun getIconComponent(): Component {
return Components.item(itemStack(Items.IRON_BOOTS) {})
}
override fun getBackgroundTexture(): Identifier {
return Identifier.of("textures/block/packed_mud.png")
}
val EARTH_SURF_SPEED_BOOST = EntityAttributeModifier(
"earth_surf".toId(),
1.3,
EntityAttributeModifier.Operation.ADD_MULTIPLIED_TOTAL
)
override fun onDisable(player: PlayerEntity) {
super.onDisable(player)
cleanUp(player)
}
private fun cleanUp(player: PlayerEntity) {
if (player is ServerPlayerEntity) {
player.stopEmote("earth-surfing".toEmote())
player.setSyncedData(EarthSurfKey, false)
player.getAttributeInstance(EntityAttributes.GENERIC_STEP_HEIGHT)?.baseValue = 0.6
player.getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED)?.removeModifier(EARTH_SURF_SPEED_BOOST.id)
} else if (MinecraftClient.getInstance().player == player) {
player.showSpeedlines = false
}
}
override fun onStart(player: PlayerEntity, abilityScope: AbilityScope) {
super.onStart(player, abilityScope)
if (player is ServerPlayerEntity) {
player.playEmote("earth-surfing".toEmote())
player.setSyncedData(EarthSurfKey, true)
runCatching {
player.getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED)
?.addTemporaryModifier(
EntityAttributeModifier(
"earth_surf".toId(),
earthSurfSpeedBoost.getValue(player.uuid),
EntityAttributeModifier.Operation.ADD_MULTIPLIED_TOTAL
)
)
}
player.sound(SoundRegistry.EARTH_ARMOR)
cameraShakePacket.send(BoomShake(0.1, 0.2, 0.4), player as ServerPlayerEntity)
} else if (player == MinecraftClient.getInstance().player) {
player.showSpeedlines = true
}
}
override fun onEnd(player: PlayerEntity, abilityEndInformation: AbilityEndInformation) {
super.onEnd(player, abilityEndInformation)
cleanUp(player)
}
}
fun PlayerEntity.isEarthSurfing() = getSyncedData<Boolean>(EarthSurfKey) == true
val Entity.bodyDirectionVector: Vec3d
get() {
val rotY = Math.toRadians(yaw.toDouble())
val rotX = Math.toRadians(0.0)
val xz = cos(rotX)
return Vec3d(-xz * sin(rotY), -sin(rotX), xz * cos(rotY))
}
fun PlayerEntity.spawnEarthCircle() {
val radius = earthSurfRadius.getValue(uuid).toInt()
this.pos.add(0.0, 0.0, 0.0).add(
this.bodyDirectionVector.normalize().multiply(-(radius.toDouble() + 2))
).toBlockPos()
.filledSpherePositionSet(radius)
.forEach {
val blockState = world.getBlockState(it)
if (!blockState.isAir) {
if (calculateProbability(5.0)) {
world.playSound(
null,
it,
SoundRegistry.STONE_SMASH,
SoundCategory.BLOCKS,
1f,
(0.9..1.4).random().toFloat()
)
}
if (calculateProbability(25.0)) {
(this as? ServerPlayerEntity?)?.apply {
cameraShakePacket.send(BoomShake(0.1, 0.2, 0.4), this)
}
}
val blockPos = it
val fallingBlock = FallingBlockEntity.spawnFromBlock(world, blockPos, blockState)
fallingBlock.modifyVelocity(Vec3d(0.0, 0.5, 0.0))
fallingBlock.dropItem = false
world.setBlockState(it, Blocks.AIR.defaultState)
val particlePos = it.toCenterPos()
(world as? ServerWorld?)?.spawnParticles(
ParticleRegistry.EARTH_DUST,
particlePos.x,
particlePos.y + 1,
particlePos.z,
7,
(0.1..0.4).random(),
(0.1..0.4).random(),
(0.1..0.4).random(),
(0.01..0.04).random()
)
}
}
/* this.pos.add(0.0, 1.0, 0.0).add(
this.bodyDirectionVector.normalize().multiply(-(radius.toDouble() + 1))
).toBlockPos()
.circlePositionSet(radius)
.forEach {
val particlePos = it.toCenterPos()
(world as? ServerWorld?)?.spawnParticles(
ParticleRegistry.EARTH_DUST,
particlePos.x.toDouble(),
particlePos.y - 0.3,
particlePos.z.toDouble(),
if (radius == 1) 1 else 5,
(0.1..0.4).random(),
(0.1..0.4).random(),
(0.1..0.4).random(),
(0.01..0.04).random()
)
}*/
}
@@ -0,0 +1,197 @@
package gg.norisk.heroes.toph.ability
import gg.norisk.datatracker.entity.getSyncedData
import gg.norisk.datatracker.entity.setSyncedData
import gg.norisk.datatracker.entity.syncedValueChangeEvent
import gg.norisk.heroes.client.option.HeroKeyBindings
import gg.norisk.heroes.common.HeroesManager.client
import gg.norisk.heroes.common.ability.CooldownProperty
import gg.norisk.heroes.common.ability.NumberProperty
import gg.norisk.heroes.common.ability.operation.AddValueTotal
import gg.norisk.heroes.common.hero.ability.AbilityScope
import gg.norisk.heroes.common.hero.ability.implementation.PressAbility
import gg.norisk.heroes.common.networking.BoomShake
import gg.norisk.heroes.common.networking.cameraShakePacket
import gg.norisk.heroes.common.networking.dto.AnimationInterpolator
import gg.norisk.heroes.common.utils.RaycastUtils
import gg.norisk.heroes.common.utils.random
import gg.norisk.heroes.common.utils.sound
import gg.norisk.heroes.toph.TophManager.isEarthBlock
import gg.norisk.heroes.toph.entity.ITrappedEntity
import gg.norisk.heroes.toph.registry.ParticleRegistry
import gg.norisk.heroes.toph.registry.SoundRegistry
import gg.norisk.heroes.toph.render.BlockTrapFeatureRenderer
import io.wispforest.owo.ui.component.Components
import io.wispforest.owo.ui.core.Component
import net.fabricmc.fabric.api.client.rendering.v1.LivingEntityFeatureRendererRegistrationCallback
import net.minecraft.client.render.entity.feature.FeatureRendererContext
import net.minecraft.client.render.entity.model.EntityModel
import net.minecraft.client.render.entity.model.EntityModelLayers
import net.minecraft.entity.Entity
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.attribute.EntityAttributeModifier
import net.minecraft.entity.attribute.EntityAttributes
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.Items
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.util.math.Box
import net.silkmc.silk.core.entity.posUnder
import net.silkmc.silk.core.task.mcCoroutineTask
import net.silkmc.silk.core.text.literalText
import kotlin.time.Duration.Companion.seconds
import kotlin.time.toJavaDuration
val EarthTrappedKey = "isEarthTrapped"
val EarthTrapAbility = object : PressAbility("Earth Trap") {
//TODO player based
val distance = 64.0
val earthTrapMaxDurationProperty = CooldownProperty(
2.0, 3,
"Max Duration",
AddValueTotal(1.0, 1.0, 3.0)
)
val earthTrapSlownessProperty = NumberProperty(
-0.7, 3,
"Slowness",
AddValueTotal(-0.1, -0.2, -0.3),
).apply {
icon = {
Components.item(Items.COBWEB.defaultStack)
}
}
init {
client {
this.keyBind = HeroKeyBindings.pickItemKeyBinding
//TODO condition sneaking
LivingEntityFeatureRendererRegistrationCallback.EVENT.register { entityType, entityRenderer, registrationHelper, context ->
val modelPart = EntityModelLayers.getLayers().toList()
.firstOrNull { it.id.path == entityType.untranslatedName.lowercase() }
if (modelPart != null) {
val model = context.getPart(modelPart)
registrationHelper.register(
BlockTrapFeatureRenderer(
entityRenderer as FeatureRendererContext<LivingEntity, EntityModel<LivingEntity>>,
context.heldItemRenderer,
model
)
)
}
}
}
this.properties = listOf(earthTrapMaxDurationProperty, earthTrapSlownessProperty)
this.condition = {
it.isSneaking && it.world.getBlockState(it.posUnder).isEarthBlock
}
this.cooldownProperty =
buildCooldown(10.0, 5, AddValueTotal(-0.1, -0.4, -0.2, -0.8, -1.5, -1.0))
syncedValueChangeEvent.listen {
if (it.key == EarthTrappedKey) {
if (it.entity.isEarthTrapped()) {
(it.entity as ITrappedEntity).earthRotationAnimation = AnimationInterpolator(
0f,
360f,
0.8.seconds.toJavaDuration(),
AnimationInterpolator.Easing.CUBIC_IN
)
} else {
(it.entity as ITrappedEntity).earthRotationAnimation = AnimationInterpolator(
360f,
0f,
0.8.seconds.toJavaDuration(),
AnimationInterpolator.Easing.CUBIC_IN
)
}
}
}
}
override fun getIconComponent(): Component {
return Components.item(Items.SAND.defaultStack)
}
override fun getUnlockCondition(): Text {
return literalText {
text(Text.translatable("heroes.ability.$internalKey.unlock_condition"))
}
}
override fun hasUnlocked(player: PlayerEntity): Boolean {
return player.isCreative || (EarthPushAbility.cooldownProperty.isMaxed(player.uuid))
}
override fun getBackgroundTexture(): Identifier {
return Identifier.of("textures/block/packed_mud.png")
}
val EARTH_TRAP_SLOW_BOOST = EntityAttributeModifier(
Identifier.of("earth_trap"),
-0.7,
EntityAttributeModifier.Operation.ADD_MULTIPLIED_TOTAL
)
override fun onStart(player: PlayerEntity, abilityScope: AbilityScope) {
if (player is ServerPlayerEntity) {
if (player.isSneaking && player.world.getBlockState(player.posUnder).isEarthBlock) {
val world = player.world as ServerWorld
val clipWithDistance = RaycastUtils.clipWithDistance(player, player.world, distance) ?: return
cameraShakePacket.send(BoomShake(0.1, 0.2, 0.4), player as ServerPlayerEntity)
player.sound(SoundRegistry.STONE_SMASH)
world.getOtherEntities(player, Box.from(clipWithDistance.pos).expand(5.0)).forEach {
val entity = it
if (!entity.isEarthTrapped() && world.getBlockState(entity.posUnder).isEarthBlock) {
entity.sound(SoundRegistry.EARTH_COLUMN_1)
entity.setSyncedData(EarthTrappedKey, true)
(entity as? LivingEntity?)?.apply {
getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED)?.addTemporaryModifier(
EntityAttributeModifier(
Identifier.of("earth_trap"),
earthTrapSlownessProperty.getValue(player.uuid),
EntityAttributeModifier.Operation.ADD_MULTIPLIED_TOTAL
)
)
}
//TODO player based duration
repeat(5) { _ ->
(world as? ServerWorld?)?.spawnParticles(
ParticleRegistry.EARTH_DUST,
it.x,
it.y,
it.z,
7,
(0.01..0.04).random(),
(0.01..0.04).random(),
(0.01..0.04).random(),
(0.01..0.04).random()
)
}
mcCoroutineTask(
sync = true,
client = false,
delay = earthTrapMaxDurationProperty.getValue(player.uuid).seconds
) {
entity.setSyncedData(EarthTrappedKey, false)
(entity as? LivingEntity?)?.apply {
getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED)?.removeModifier(
EARTH_TRAP_SLOW_BOOST.id
)
}
}
}
}
}
}
}
}
fun Entity.isEarthTrapped() = getSyncedData<Boolean>(EarthTrappedKey) == true
@@ -0,0 +1,252 @@
package gg.norisk.heroes.toph.ability
import gg.norisk.datatracker.entity.getSyncedData
import gg.norisk.datatracker.entity.setSyncedData
import gg.norisk.datatracker.entity.syncedValueChangeEvent
import gg.norisk.emote.network.EmoteNetworking.playEmote
import gg.norisk.heroes.client.option.HeroKeyBindings
import gg.norisk.heroes.client.renderer.BlockOutlineRenderer
import gg.norisk.heroes.common.HeroesManager.client
import gg.norisk.heroes.common.ability.operation.AddValueTotal
import gg.norisk.heroes.common.hero.ability.AbilityScope
import gg.norisk.heroes.common.hero.ability.implementation.PressAbility
import gg.norisk.heroes.common.networking.BoomShake
import gg.norisk.heroes.common.networking.cameraShakePacket
import gg.norisk.heroes.common.utils.SphereUtils
import gg.norisk.heroes.common.utils.toVec
import gg.norisk.heroes.toph.TophManager.isEarthBlock
import gg.norisk.heroes.toph.TophManager.toEmote
import gg.norisk.heroes.toph.TophManager.toId
import gg.norisk.heroes.toph.entity.toph
import gg.norisk.heroes.toph.mixin.render.GameRendererAccessor
import gg.norisk.heroes.toph.registry.SoundRegistry
import io.wispforest.owo.ui.component.Components
import io.wispforest.owo.ui.core.Component
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents
import net.minecraft.block.BlockState
import net.minecraft.client.MinecraftClient
import net.minecraft.client.world.ClientWorld
import net.minecraft.entity.Entity
import net.minecraft.entity.effect.StatusEffectInstance
import net.minecraft.entity.effect.StatusEffects
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.Items
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.sound.SoundCategory
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
import net.minecraft.util.math.Direction
import net.minecraft.world.World
import net.silkmc.silk.core.entity.posUnder
import net.silkmc.silk.core.kotlin.ticks
import net.silkmc.silk.core.task.mcCoroutineTask
import net.silkmc.silk.core.text.literalText
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable
import kotlin.time.Duration.Companion.seconds
val SeismicSenseShader = "shaders/post/seismic_sense.json".toId()
val SeismicSenseKey = "hasSeismicSense"
val SeismicSenseAbility = object : PressAbility("Seismic Sense") {
init {
client {
this.keyBind = HeroKeyBindings.fourthKeyBinding
WorldRenderEvents.AFTER_TRANSLUCENT.register { context ->
val world = context.world()
val player = MinecraftClient.getInstance().player ?: return@register
val matrices = context.matrixStack() ?: return@register
player.toph.seismicBlocks.removeIf { (timestamp, _) -> timestamp < System.currentTimeMillis() }
player.toph.seismicBlocks.forEach { (_, pos) ->
val blockState: BlockState = world.getBlockState(pos)
if (!blockState.isAir && world.worldBorder.contains(pos) && blockState.isSolid) {
BlockOutlineRenderer.drawBlockBox(
matrices,
context.consumers() ?: return@forEach,
pos,
1.0f,
1.0f,
1.0f,
0.4f
)
}
}
}
}
syncedValueChangeEvent.listen { event ->
if (event.key != SeismicSenseKey) return@listen
val player = event.entity as? PlayerEntity? ?: return@listen
if (event.entity.world is ServerWorld) {
} else if (event.entity == MinecraftClient.getInstance().player) {
if (player.hasSeismicSense) {
player.toph.toph_seismicTasks += mcCoroutineTask(sync = true, client = true, delay = 0.32.seconds) {
val gameRenderer = MinecraftClient.getInstance().gameRenderer as GameRendererAccessor
gameRenderer.invokeLoadPostProcessor(SeismicSenseShader)
player.spawnSeismicSenseGlowCircle()
player.toph.toph_seismicTasks += mcCoroutineTask(sync = true, delay = 90.ticks, client = true) {
MinecraftClient.getInstance().gameRenderer.disablePostProcessor()
}
}
} else {
cleanUp(player)
}
}
}
this.cooldownProperty =
buildCooldown(10.0, 5, AddValueTotal(-0.1, -0.4, -0.2, -0.8, -1.5, -1.0))
}
override fun hasUnlocked(player: PlayerEntity): Boolean {
return player.isCreative || (EarthPushAbility.cooldownProperty.isMaxed(player.uuid))
}
override fun getUnlockCondition(): Text {
return literalText {
text(Text.translatable("heroes.ability.$internalKey.unlock_condition"))
}
}
override fun getIconComponent(): Component {
return Components.item(Items.ENDER_EYE.defaultStack)
}
override fun getBackgroundTexture(): Identifier {
return Identifier.of("textures/block/packed_mud.png")
}
override fun onDisable(player: PlayerEntity) {
super.onDisable(player)
cleanUp(player)
}
private fun cleanUp(player: PlayerEntity) {
player.toph.toph_seismicTasks.forEach { it.cancel() }
player.removeStatusEffect(StatusEffects.DARKNESS)
player.removeStatusEffect(StatusEffects.SLOWNESS)
player.toph.seismicBlocks.clear()
player.toph.seismicEntities.clear()
if (player is ServerPlayerEntity) {
player.hasSeismicSense = false
} else {
MinecraftClient.getInstance().gameRenderer.disablePostProcessor()
}
}
override fun onStart(player: PlayerEntity, abilityScope: AbilityScope) {
super.onStart(player, abilityScope)
if (player is ServerPlayerEntity) {
if (player.hasSeismicSense) {
cleanUp(player)
} else {
abilityScope.cancelCooldown()
val world = player.world as ServerWorld
player.playEmote("seismic-sense".toEmote())
player.addStatusEffect(StatusEffectInstance(StatusEffects.SLOWNESS, 140, 2, false, false))
player.toph.toph_seismicTasks += mcCoroutineTask(sync = true, client = false, delay = 0.32.seconds) {
player.addStatusEffect(StatusEffectInstance(StatusEffects.DARKNESS, 140, 1, false, false))
player.setSyncedData(SeismicSenseKey, true)
cameraShakePacket.send(BoomShake(0.1, 0.2, 0.4), player)
world.playSoundFromEntity(
null,
player,
SoundRegistry.SEISMIC_SENSE_START,
SoundCategory.PLAYERS,
2f,
1f
)
world.playSoundFromEntity(
null,
player,
SoundRegistry.EARTH_ARMOR,
SoundCategory.PLAYERS,
0.4f,
2f
)
player.toph.toph_seismicTasks += mcCoroutineTask(sync = true, delay = 90.ticks, client = false) {
player.removeStatusEffect(StatusEffects.DARKNESS)
player.removeStatusEffect(StatusEffects.SLOWNESS)
player.hasSeismicSense = false
addCooldown(player)
}
}
}
}
}
}
fun Entity.handleSeismicSenseOutline(cir: CallbackInfoReturnable<Boolean>) {
val player = MinecraftClient.getInstance().player ?: return
player.toph.seismicEntities.removeIf { (timestamp, _) -> timestamp < System.currentTimeMillis() }
if (player.toph.seismicEntities.any { (_, uuid) -> uuid == this.uuid } && player.hasSeismicSense) {
cir.returnValue = true
}
}
fun World.isVisibleVonAtleastEinerSeite(blockPos: BlockPos): Boolean {
return Direction.values().any { direction -> getBlockState(blockPos.offset(direction)).isAir }
}
fun PlayerEntity.spawnSeismicSenseGlowCircle() {
toph.seismicBlocks.clear()
val player = this
val world = this.world as ClientWorld
val maxRadius = 50
player.toph.toph_seismicTasks += mcCoroutineTask(sync = true, client = true, howOften = 4, period = 10.ticks) {
world.playSoundFromEntity(
player,
player,
SoundRegistry.SEISMIC_SENSE_WAVE,
SoundCategory.PLAYERS,
0.5f,
1f
)
repeat(maxRadius) { radius ->
player.toph.toph_seismicTasks += mcCoroutineTask(delay = radius.ticks, client = true, sync = true) {
if (world.getBlockState(player.posUnder).isEarthBlock && hasSeismicSense) {
SphereUtils.generateSphere(player.posUnder, radius, true).forEach {
val blockState = world.getBlockState(it)
/*if (blockState.isAir) {
getNextBottomBlock(world, it)?.apply {
seismicBlocks.add(Pair(System.currentTimeMillis() + 50L, this))
}
}*/
if (!blockState.isAir) {
if (world.isVisibleVonAtleastEinerSeite(it)) {
player.toph.seismicEntities.addAll(
world.getOtherEntities(
player,
Box.from(it.toVec()).expand(2.0)
).map { entity -> entity.uuid }.map { Pair(System.currentTimeMillis() + 1000L, it) }
)
player.toph.seismicBlocks.add(Pair(System.currentTimeMillis() + 50L, it))
}
}
}
}
}
}
}
}
var PlayerEntity.hasSeismicSense: Boolean
get() = this.getSyncedData<Boolean>(SeismicSenseKey) ?: false
set(value) = this.setSyncedData(SeismicSenseKey, value)
fun handleSeismicSenseShader(ci: CallbackInfo) {
val player = MinecraftClient.getInstance().player ?: return
if (player.hasSeismicSense) {
ci.cancel()
}
}
fun PlayerEntity.handleSeismicSenseDarkness(original: Float): Float {
return original * 4
}
@@ -0,0 +1,164 @@
package gg.norisk.heroes.toph.entity
import gg.norisk.datatracker.entity.getSyncedData
import gg.norisk.datatracker.entity.setSyncedData
import gg.norisk.heroes.common.utils.random
import gg.norisk.heroes.common.utils.toVector
import gg.norisk.heroes.toph.ability.earthPushDamage
import gg.norisk.heroes.toph.registry.ParticleRegistry
import gg.norisk.heroes.toph.registry.SoundRegistry
import net.minecraft.block.BlockState
import net.minecraft.entity.EntityType
import net.minecraft.entity.FallingBlockEntity
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.MovementType
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.particle.BlockStateParticleEffect
import net.minecraft.particle.ParticleTypes
import net.minecraft.server.world.ServerWorld
import net.minecraft.sound.SoundCategory
import net.minecraft.util.math.Vec3d
import net.minecraft.world.World
import net.minecraft.world.explosion.Explosion
import net.silkmc.silk.core.entity.modifyVelocity
import org.joml.Vector3f
import java.util.*
class BendingBlockEntity(world: World, x: Double, y: Double, z: Double, blockState: BlockState) :
FallingBlockEntity(EntityType.FALLING_BLOCK, world) {
var canAttack: Boolean = false
companion object {
val NULL = Vector3f()
private val OWNER_KEY = "BendingBlock:Owner"
private val TARGET_KEY = "BendingBlock:TargetPos"
var FallingBlockEntity.owner: UUID?
get() = this.getSyncedData<UUID>(OWNER_KEY)
set(value) = this.setSyncedData(OWNER_KEY, value)
var FallingBlockEntity.targetPos: Vector3f
get() = this.getSyncedData<Vector3f>(TARGET_KEY) ?: NULL
set(value) = this.setSyncedData(TARGET_KEY, value)
}
init {
block = blockState
intersectionChecked = true
setPosition(x, y, z)
velocity = Vec3d.ZERO
prevX = x
prevY = y
prevZ = z
}
override fun isImmuneToExplosion(explosion: Explosion): Boolean {
return true
}
override fun isCollidable(): Boolean {
return true
}
fun forcePush(player: PlayerEntity) {
val target = player.raycast(64.0, 0f, false)
targetPos = target.pos.toVector3f()
canAttack = true
var direction = targetPos.toVector().subtract(pos)
direction = direction.normalize().multiply(2.0)
modifyVelocity(direction)
}
override fun tick() {
if (!world.isClient) {
if (block.isAir) {
discard()
return
}
val block = block.block
++timeFalling
if (!hasNoGravity()) {
velocity = velocity.add(0.0, -0.04, 0.0)
}
move(MovementType.SELF, velocity)
velocity = velocity.multiply(0.98)
if (pos.distanceTo(targetPos.toVector()) <= 1) {
targetPos = NULL
velocity = velocity.multiply(0.01)
}
if (velocity.y <= 0) {
setNoGravity(true)
}
if (world is ServerWorld && (velocity.lengthSquared() > 0.1)) {
(world as ServerWorld).spawnParticles(
BlockStateParticleEffect(ParticleTypes.BLOCK, blockState),
this.x,
this.y,
this.z,
if (canAttack) 7 else 2,
(this.width / 4.0f).toDouble(),
(this.height / 4.0f).toDouble(),
(this.width / 4.0f).toDouble(),
0.05
)
}
if (canAttack) {
explode(horizontalCollision)
}
}
}
private fun explode(force: Boolean = false) {
var flag = force
val owner = world.getPlayerByUuid(owner ?: return) ?: return
for (enemy in this.world.getEntitiesByClass(
LivingEntity::class.java,
this.boundingBox.expand(1.1)
) {
it.isAlive && it.uuid != this.owner
}) {
flag = true
enemy.damage(this.damageSources.playerAttack(owner), earthPushDamage.getValue(owner.uuid).toFloat())
}
if (flag) {
world.playSound(null, blockPos, SoundRegistry.EARTH_ARMOR, SoundCategory.BLOCKS, 1f, 1f)
this.discard()
repeat(10) {
(world as ServerWorld).spawnParticles(
BlockStateParticleEffect(ParticleTypes.BLOCK, blockState),
this.x,
this.y,
this.z,
if (canAttack) 7 else 2,
(this.width / 4.0f).toDouble(),
(this.height / 4.0f).toDouble(),
(this.width / 4.0f).toDouble(),
0.05
)
}
(world as? ServerWorld?)?.spawnParticles(
ParticleRegistry.EARTH_DUST,
this.x,
this.y,
this.z,
7,
(this.width / 4.0f).toDouble(),
(this.height / 4.0f).toDouble(),
(this.width / 4.0f).toDouble(),
(0.01..0.04).random()
)
}
}
}
@@ -0,0 +1,7 @@
package gg.norisk.heroes.toph.entity
import java.util.*
interface IBendingItemEntity {
var bender: UUID?
}
@@ -0,0 +1,14 @@
package gg.norisk.heroes.toph.entity
import kotlinx.coroutines.Job
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.util.math.BlockPos
import java.util.*
interface ITophPlayer {
val seismicBlocks: MutableSet<Pair<Long, BlockPos>>
val seismicEntities: MutableSet<Pair<Long, UUID>>
val toph_seismicTasks: MutableList<Job>
}
val PlayerEntity.toph get() = this as ITophPlayer
@@ -0,0 +1,7 @@
package gg.norisk.heroes.toph.entity
import gg.norisk.heroes.common.networking.dto.AnimationInterpolator
interface ITrappedEntity {
var earthRotationAnimation: AnimationInterpolator?
}
@@ -0,0 +1,7 @@
package gg.norisk.heroes.toph.network
import gg.norisk.heroes.toph.TophManager.toId
import gg.norisk.heroes.toph.ability.EarthColumnDescription
import net.silkmc.silk.network.packet.c2sPacket
val earthColumnBlockInfos = c2sPacket<EarthColumnDescription>("earth_column_block_infos".toId())
@@ -0,0 +1,71 @@
package gg.norisk.heroes.toph.particle
import net.fabricmc.api.EnvType
import net.fabricmc.api.Environment
import net.minecraft.client.particle.*
import net.minecraft.client.world.ClientWorld
import net.minecraft.particle.ParticleEffect
import net.fabricmc.fabric.api.client.particle.v1.ParticleFactoryRegistry
import net.minecraft.particle.ParticleType
@Environment(EnvType.CLIENT)
class EarthDustParticle internal constructor(
clientWorld: ClientWorld,
d: Double,
e: Double,
f: Double,
g: Double,
h: Double,
i: Double,
bl: Boolean
) : SpriteBillboardParticle(clientWorld, d, e, f) {
init {
this.scale(3.0f)
this.setBoundingBoxSpacing(0.25f, 0.25f)
this.maxAge = random.nextInt(50)
this.gravityStrength = 3.0E-6f
this.velocityX = g
this.velocityY = h
this.velocityZ = i
}
override fun tick() {
this.prevPosX = this.x
this.prevPosY = this.y
this.prevPosZ = this.z
if (age++ < this.maxAge && !(this.alpha <= 0.0f)) {
this.velocityX += (random.nextFloat() / 5000.0f * (if (random.nextBoolean()) 1 else -1).toFloat()).toDouble()
this.velocityZ += (random.nextFloat() / 5000.0f * (if (random.nextBoolean()) 1 else -1).toFloat()).toDouble()
this.velocityY -= gravityStrength.toDouble()
this.move(this.velocityX, this.velocityY, this.velocityZ)
if (this.age >= this.maxAge - 60 && this.alpha > 0.01f) {
this.alpha -= 0.015f
}
} else {
this.markDead()
}
}
override fun getType(): ParticleTextureSheet {
return ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT
}
@Environment(EnvType.CLIENT)
class CosySmokeFactory(private val spriteProvider: SpriteProvider) : ParticleFactory<ParticleEffect> {
override fun createParticle(
defaultParticleType: ParticleEffect,
clientWorld: ClientWorld,
d: Double,
e: Double,
f: Double,
g: Double,
h: Double,
i: Double
): Particle {
val campfireSmokeParticle = EarthDustParticle(clientWorld, d, e, f, g, h, i, false)
campfireSmokeParticle.setAlpha(0.9f)
campfireSmokeParticle.setSprite(this.spriteProvider)
return campfireSmokeParticle
}
}
}
@@ -0,0 +1,20 @@
package gg.norisk.heroes.toph.registry
import gg.norisk.heroes.toph.TophManager.toId
import net.fabricmc.fabric.api.particle.v1.FabricParticleTypes
import net.minecraft.particle.ParticleEffect
import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
object ParticleRegistry {
val EARTH_DUST = register("earth_dust")
fun init() {
}
private fun register(
name: String
): ParticleEffect {
return Registry.register(Registries.PARTICLE_TYPE, name.toId(), FabricParticleTypes.simple())
}
}
@@ -0,0 +1,21 @@
package gg.norisk.heroes.toph.registry
import gg.norisk.heroes.toph.TophManager.toId
import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import net.minecraft.sound.SoundEvent
object SoundRegistry {
var EARTH_ARMOR = "earth_armor".register()
var SEISMIC_SENSE_START = "seismic_sense_start".register()
var SEISMIC_SENSE_WAVE = "seismic_sense_wave".register()
var EARTH_COLUMN_1 = "earth_column_1".register()
var ARM_WHOOSH = "arm_whoosh".register()
var STONE_SMASH = "stone_smash".register()
var STONE_SLIDE = "stone_slide".register()
fun init() {
}
private fun String.register() = Registry.register(Registries.SOUND_EVENT, this.toId(), SoundEvent.of(this.toId()))
}
@@ -0,0 +1,135 @@
package gg.norisk.heroes.toph.render
import gg.norisk.heroes.toph.ability.isEarthTrapped
import gg.norisk.heroes.toph.entity.ITrappedEntity
import gg.norisk.heroes.toph.mixin.ModelPartAccessor
import net.minecraft.block.Blocks
import net.minecraft.client.model.ModelPart
import net.minecraft.client.render.VertexConsumerProvider
import net.minecraft.client.render.entity.feature.FeatureRenderer
import net.minecraft.client.render.entity.feature.FeatureRendererContext
import net.minecraft.client.render.entity.model.EntityModel
import net.minecraft.client.render.item.HeldItemRenderer
import net.minecraft.client.render.model.json.ModelTransformationMode
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.entity.LivingEntity
import net.minecraft.util.math.RotationAxis
import net.minecraft.util.math.random.Random
//Credits an https://github.com/chyzman/wearThat/blob/master/src/main/java/com/chyzman/wearthat/client/WearThatClient.java
class BlockTrapFeatureRenderer<T : LivingEntity, M : EntityModel<T>>(
context: FeatureRendererContext<T, M>,
private val heldItemRenderer: HeldItemRenderer,
val root: ModelPart,
) : FeatureRenderer<T, M>(context) {
val legs = (root as ModelPartAccessor).children.filter {
it.key.contains("leg") || it.key.contains("tentacle") || it.key.contains("rod")
}.map { it.value }
override fun render(
matrices: MatrixStack,
vertexConsumers: VertexConsumerProvider,
light: Int,
entity: T,
limbAngle: Float,
limbDistance: Float,
tickDelta: Float,
animationProgress: Float,
headYaw: Float,
headPitch: Float
) {
val trapped = entity as? ITrappedEntity? ?: return
if (!entity.isEarthTrapped()) {
if (trapped.earthRotationAnimation == null || trapped.earthRotationAnimation?.isDone == true) {
return
}
}
var current: Class<*> = this.contextModel::class.java
while (current.superclass != null) { // we don't want to process Object.class
current.declaredFields.forEach { field ->
runCatching {
field.isAccessible = true
field.get(this.contextModel) as ModelPart
}.onSuccess {
val random = Random.create()
if (it.isEmpty) return@onSuccess
if (legs.any { leg -> compareCuboids(leg.getRandomCuboid(random), it.getRandomCuboid(random)) }) {
it.renderBlock(matrices, entity, vertexConsumers, light)
}
}
runCatching {
field.isAccessible = true
field.get(this.contextModel) as Array<ModelPart>
}.onSuccess { modelParts ->
for (it in modelParts) {
if (it.isEmpty) return@onSuccess
val random = Random.create()
if (legs.any { leg ->
compareCuboids(
leg.getRandomCuboid(random),
it.getRandomCuboid(random)
)
}) {
it.renderBlock(matrices, entity, vertexConsumers, light)
}
}
}
}
current = current.superclass
}
}
fun compareCuboids(cuboid1: ModelPart.Cuboid, cuboid2: ModelPart.Cuboid): Boolean {
return cuboid1.minX == cuboid2.minX &&
cuboid1.minY == cuboid2.minY &&
cuboid1.minZ == cuboid2.minZ &&
cuboid1.maxX == cuboid2.maxX &&
cuboid1.maxY == cuboid2.maxY &&
cuboid1.maxZ == cuboid2.maxZ
}
private fun ModelPart.renderBlock(
matrices: MatrixStack,
entity: T,
vertexConsumers: VertexConsumerProvider,
light: Int
) {
val trapped = entity as? ITrappedEntity ?: return
val earthRotation = trapped.earthRotationAnimation ?: return
matrices.push()
rotate(matrices)
var size = 0.0
forEachCuboid(matrices) { entry, string, i, cuboid ->
size = cuboid.maxY.toDouble()
}
matrices.translate(0.0, size / 18, 0.0) // Position anpassen
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(earthRotation.get()))
//val legHeight = it.cuboids.firstOrNull()?.dimensions?.y?.toDouble() ?: 0.0
val progress = if (entity.isEarthTrapped()) {
earthRotation.get() / earthRotation.end
} else {
earthRotation.get() / earthRotation.start
}
val scale = progress
matrices.scale(scale, scale, scale)
heldItemRenderer.renderItem(
entity as LivingEntity,
Blocks.DIRT.asItem().defaultStack,
ModelTransformationMode.FIXED,
false,
matrices,
vertexConsumers,
light
)
matrices.pop()
}
}
@@ -0,0 +1,270 @@
package gg.norisk.heroes.toph.render
import net.minecraft.client.render.VertexConsumerProvider
import net.minecraft.client.render.entity.feature.FeatureRenderer
import net.minecraft.client.render.entity.feature.FeatureRendererContext
import net.minecraft.client.render.entity.model.EntityModel
import net.minecraft.client.render.entity.model.PlayerEntityModel
import net.minecraft.client.render.item.HeldItemRenderer
import net.minecraft.client.render.model.json.ModelTransformationMode
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.entity.EquipmentSlot
import net.minecraft.entity.LivingEntity
import net.minecraft.item.Equipment
import net.minecraft.util.math.RotationAxis
//Credits an https://github.com/chyzman/wearThat/blob/master/src/main/java/com/chyzman/wearthat/client/WearThatClient.java
class ChestItemFeatureRenderer<T : LivingEntity, M : EntityModel<T>>(
context: FeatureRendererContext<T, M>,
private val heldItemRenderer: HeldItemRenderer
) : FeatureRenderer<T, M>(context) {
override fun render(
matrices: MatrixStack,
vertexConsumers: VertexConsumerProvider,
light: Int,
entity: T,
limbAngle: Float,
limbDistance: Float,
tickDelta: Float,
animationProgress: Float,
headYaw: Float,
headPitch: Float
) {
val mode = ModelTransformationMode.FIXED
val chestStack = (entity as LivingEntity).getEquippedStack(EquipmentSlot.CHEST)
if (!chestStack.isEmpty) {
if (Equipment.fromStack(chestStack)?.slotType != EquipmentSlot.CHEST) {
matrices.push()
(this.contextModel as PlayerEntityModel<*>).body.rotate(matrices)
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180f))
matrices.translate(0f, -1 / 4f, 0f)
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180f))
heldItemRenderer.renderItem(
entity as LivingEntity,
chestStack,
mode,
false,
matrices,
vertexConsumers,
light
)
matrices.scale(1.01f, 1.01f, 1.01f)
matrices.translate(0f, -1 / 4f, 0f)
heldItemRenderer.renderItem(
entity as LivingEntity,
chestStack,
mode,
false,
matrices,
vertexConsumers,
light
)
matrices.pop()
matrices.push()
(this.contextModel as PlayerEntityModel<*>).rightArm.rotate(matrices)
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180f))
matrices.scale(2 / 3f, 2 / 3f, 2 / 3f)
matrices.translate(-1 / 12f, 0f, 0f)
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180f))
heldItemRenderer.renderItem(
entity as LivingEntity,
chestStack,
mode,
false,
matrices,
vertexConsumers,
light
)
matrices.scale(0.99f, 0.99f, 0.99f)
matrices.translate(0f, -1 / 2f, 0f)
heldItemRenderer.renderItem(
entity as LivingEntity,
chestStack,
mode,
false,
matrices,
vertexConsumers,
light
)
matrices.pop()
matrices.push()
(this.contextModel as PlayerEntityModel<*>).rightArm.rotate(matrices)
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180f))
matrices.scale(2 / 3f, 2 / 3f, 2 / 3f)
matrices.translate(-1 / 12f, 0f, 0f)
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180f))
heldItemRenderer.renderItem(
entity as LivingEntity,
chestStack,
mode,
false,
matrices,
vertexConsumers,
light
)
matrices.scale(1.25f, 1.25f, 1.25f)
matrices.translate(0f, -0.75f, 0f)
heldItemRenderer.renderItem(
entity as LivingEntity,
chestStack,
mode,
false,
matrices,
vertexConsumers,
light
)
matrices.pop()
matrices.push()
(this.contextModel as PlayerEntityModel<*>).leftArm.rotate(matrices)
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180f))
matrices.scale(2 / 3f, 2 / 3f, 2 / 3f)
matrices.translate(1 / 12f, 0f, 0f)
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180f))
heldItemRenderer.renderItem(
entity as LivingEntity,
chestStack,
mode,
false,
matrices,
vertexConsumers,
light
)
matrices.scale(0.99f, 0.99f, 0.99f)
matrices.translate(0f, -1 / 2f, 0f)
heldItemRenderer.renderItem(
entity as LivingEntity,
chestStack,
mode,
false,
matrices,
vertexConsumers,
light
)
matrices.pop()
matrices.push()
(this.contextModel as PlayerEntityModel<*>).leftArm.rotate(matrices)
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180f))
matrices.scale(2 / 3f, 2 / 3f, 2 / 3f)
matrices.translate(1 / 12f, 0f, 0f)
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180f))
heldItemRenderer.renderItem(
entity as LivingEntity,
chestStack,
mode,
false,
matrices,
vertexConsumers,
light
)
matrices.scale(1.2f, 1.2f, 1.2f)
matrices.translate(0f, -0.75f, 0f)
heldItemRenderer.renderItem(
entity as LivingEntity,
chestStack,
mode,
false,
matrices,
vertexConsumers,
light
)
matrices.pop()
}
}
val legsStack = (entity as LivingEntity).getEquippedStack(EquipmentSlot.LEGS)
if (!legsStack.isEmpty) {
if (Equipment.fromStack(legsStack)?.slotType != EquipmentSlot.LEGS) {
matrices.push()
(this.contextModel as PlayerEntityModel<*>).rightLeg.rotate(matrices)
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180f))
matrices.scale(2 / 3f, 2 / 3f, 2 / 3f)
matrices.translate(0f, -1 / 6f, 0f)
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180f))
heldItemRenderer.renderItem(
entity as LivingEntity,
legsStack,
mode,
false,
matrices,
vertexConsumers,
light
)
matrices.scale(1.01f, 1.01f, 1.01f)
matrices.translate(0f, -1 / 3f, 0f)
heldItemRenderer.renderItem(
entity as LivingEntity,
legsStack,
mode,
false,
matrices,
vertexConsumers,
light
)
matrices.pop()
matrices.push()
(this.contextModel as PlayerEntityModel<*>).leftLeg.rotate(matrices)
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180f))
matrices.scale(2 / 3f, 2 / 3f, 2 / 3f)
matrices.translate(0f, -1 / 6f, 0f)
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180f))
heldItemRenderer.renderItem(
entity as LivingEntity,
legsStack,
mode,
false,
matrices,
vertexConsumers,
light
)
matrices.scale(1.01f, 1.01f, 1.01f)
matrices.translate(0f, -1 / 3f, 0f)
heldItemRenderer.renderItem(
entity as LivingEntity,
legsStack,
mode,
false,
matrices,
vertexConsumers,
light
)
matrices.pop()
}
}
val feetStack = (entity as LivingEntity).getEquippedStack(EquipmentSlot.FEET)
if (!feetStack.isEmpty) {
if (Equipment.fromStack(feetStack)?.slotType != EquipmentSlot.FEET) {
matrices.push()
(this.contextModel as PlayerEntityModel<*>).rightLeg.rotate(matrices)
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180f))
matrices.scale(0.75f, 0.75f, 0.75f)
matrices.translate(0f, -0.8f, 0f)
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180f))
heldItemRenderer.renderItem(
entity as LivingEntity,
feetStack,
mode,
false,
matrices,
vertexConsumers,
light
)
matrices.pop()
matrices.push()
(this.contextModel as PlayerEntityModel<*>).leftLeg.rotate(matrices)
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180f))
matrices.scale(0.75f, 0.75f, 0.75f)
matrices.translate(0f, -0.8f, 0f)
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180f))
heldItemRenderer.renderItem(
entity as LivingEntity,
feetStack,
mode,
false,
matrices,
vertexConsumers,
light
)
matrices.pop()
}
}
}
}
@@ -0,0 +1,42 @@
package gg.norisk.heroes.toph.sound
import gg.norisk.heroes.toph.ability.isEarthSurfing
import gg.norisk.heroes.toph.registry.SoundRegistry
import net.minecraft.client.sound.MovingSoundInstance
import net.minecraft.client.sound.SoundInstance
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.sound.SoundCategory
class StoneSlideSoundInstance(private val player: PlayerEntity) :
MovingSoundInstance(SoundRegistry.STONE_SLIDE, SoundCategory.PLAYERS, SoundInstance.createRandom()) {
init {
repeat = true
repeatDelay = 0
volume = 1f
}
override fun tick() {
if (!player.isAlive || player.isRemoved) {
setDone()
}
x = player.x.toFloat().toDouble()
y = player.y.toFloat().toDouble()
z = player.z.toFloat().toDouble()
if (player.isEarthSurfing()) {
volume = 0.7f
} else {
volume -= 0.1f
}
if (volume <= 0) {
setDone()
}
}
override fun shouldAlwaysPlay(): Boolean {
return true
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

@@ -0,0 +1,31 @@
{
"format_version": "1.8.0",
"animations": {
"earth-armor": {
"animation_length": 0.5417,
"bones": {
"bipedRightArm": {
"rotation": {
"0.0": {
"vector": [0, 0, 0]
},
"0.125": {
"vector": [0, 0, 82.5]
}
}
},
"bipedLeftArm": {
"rotation": {
"0.0": {
"vector": [0, 0, 0]
},
"0.125": {
"vector": [0, 0, -82.5]
}
}
}
}
}
},
"geckolib_format_version": 2
}
@@ -0,0 +1,55 @@
{
"format_version": "1.8.0",
"animations": {
"earth-column-end": {
"animation_length": 0.2917,
"bones": {
"bipedRightArm": {
"rotation": {
"vector": [-15, 0, -27.5]
},
"position": {
"0.0": {
"vector": [-1, 1, -3]
},
"0.125": {
"vector": [-1, -6, -5]
}
}
},
"bipedLeftArm": {
"rotation": {
"vector": [-15, 0, 27.5]
},
"position": {
"0.0": {
"vector": [1, 1, -3]
},
"0.125": {
"vector": [1, -6, -5]
}
}
},
"bipedRightLeg": {
"rotation": {
"0.0": {
"vector": [-22.5, 0, 0]
},
"0.125": {
"vector": [-22.5, 0, 0]
}
},
"position": {
"0.0": {
"vector": [0, 2, -7]
},
"0.125": {
"vector": [0, -3, -10]
}
}
}
}
}
},
"geckolib_format_version": 2
}
@@ -0,0 +1,66 @@
{
"format_version": "1.8.0",
"animations": {
"earth-column-start": {
"loop": "hold_on_last_frame",
"animation_length": 0.125,
"bones": {
"bipedRightArm": {
"rotation": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [-15, 0, -27.5]
}
},
"position": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [-1, 1, -3]
}
}
},
"bipedLeftArm": {
"rotation": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [-15, 0, 27.5]
}
},
"position": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [1, 1, -3]
}
}
},
"bipedRightLeg": {
"rotation": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [-22.5, 0, 0]
}
},
"position": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [0, 2, -7]
}
}
}
}
}
},
"geckolib_format_version": 2
}
@@ -0,0 +1,75 @@
{
"format_version": "1.8.0",
"animations": {
"earth-kick": {
"animation_length": 0.25,
"bones": {
"bipedRig": {
"rotation": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [0, 65, 0]
}
}
},
"bipedRightArm": {
"rotation": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [0, 0, 107.5]
}
},
"position": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [-4, 0, 0]
}
}
},
"bipedLeftArm": {
"rotation": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [0, 0, -117.5]
}
},
"position": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [5, 0, 0]
}
}
},
"bipedLeftLeg": {
"rotation": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [0, 0, -125]
}
},
"position": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [3, 0, 0]
}
}
}
}
}
},
"geckolib_format_version": 2
}
@@ -0,0 +1,17 @@
{
"format_version": "1.8.0",
"animations": {
"earth-surfing": {
"loop": "hold_on_last_frame",
"animation_length": 0.0833,
"bones": {
"bipedRig": {
"rotation": {
"vector": [20, 0, 0]
}
}
}
}
},
"geckolib_format_version": 2
}
@@ -0,0 +1,95 @@
{
"format_version": "1.8.0",
"animations": {
"seismice-sense": {
"animation_length": 0.5,
"bones": {
"bipedRightArm": {
"rotation": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [0, 0, 20]
},
"0.2917": {
"vector": [0, 0, 20]
},
"0.4167": {
"vector": [0, 0, 20]
}
},
"position": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [-2, 4, 0]
},
"0.2917": {
"vector": [-2, 4, 0]
},
"0.4167": {
"vector": [0, -2, 0]
}
}
},
"bipedLeftArm": {
"rotation": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [0, 0, -20]
},
"0.2917": {
"vector": [0, 0, -20]
},
"0.4167": {
"vector": [0, 0, -20]
}
},
"position": {
"0.0": {
"vector": [0, 0, 0]
},
"0.0833": {
"vector": [2, 4, 0]
},
"0.2917": {
"vector": [2, 4, 0]
},
"0.4167": {
"vector": [0, -2, 0]
}
}
},
"bipedRightLeg": {
"rotation": {
"0.0": {
"vector": [0, 0, 0]
},
"0.125": {
"vector": [-22.5, 0, 0]
}
},
"position": {
"0.0": {
"vector": [0, 0, 0]
},
"0.125": {
"vector": [0, 5, -4]
},
"0.2917": {
"vector": [0, 5, -4]
},
"0.4167": {
"vector": [0, 0, -4]
}
}
}
}
}
},
"geckolib_format_version": 2
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

@@ -0,0 +1,8 @@
{
"text.hero.toph.ability.earth_surf.description": "Earth Surf beschreibung",
"text.hero.toph.ability.earth_column.description": "Earth Column beschreibung",
"text.hero.toph.ability.earth_push.description": "Earth Push beschreibung",
"text.hero.toph.ability.earth_armor.description": "Earth Armor beschreibung",
"text.hero.toph.ability.earth_trap.description": "Earth Trap beschreibung",
"text.hero.toph.ability.seismic_sense.description": "Seismic Sense beschreibung"
}
@@ -0,0 +1,9 @@
{
"text.hero.toph.description": "Toph description",
"text.hero.toph.ability.earth_surf.description": "Earth Surf description",
"text.hero.toph.ability.earth_column.description": "Earth Column description",
"text.hero.toph.ability.earth_push.description": "Earth Push description",
"text.hero.toph.ability.earth_armor.description": "Earth Armor description",
"text.hero.toph.ability.earth_trap.description": "Earth Trap description",
"text.hero.toph.ability.seismic_sense.description": "Seismic Sense description"
}
@@ -0,0 +1,16 @@
{
"textures": [
"minecraft:big_smoke_0",
"minecraft:big_smoke_1",
"minecraft:big_smoke_2",
"minecraft:big_smoke_3",
"minecraft:big_smoke_4",
"minecraft:big_smoke_5",
"minecraft:big_smoke_6",
"minecraft:big_smoke_7",
"minecraft:big_smoke_8",
"minecraft:big_smoke_9",
"minecraft:big_smoke_10",
"minecraft:big_smoke_11"
]
}
@@ -0,0 +1,57 @@
{
"targets": [
"swap"
],
"passes": [
{
"name": "color_convolve",
"intarget": "minecraft:main",
"outtarget": "swap",
"uniforms": [
{
"name": "RedMatrix",
"values": [
0.15,
0.15,
0.15
]
},
{
"name": "GreenMatrix",
"values": [
0.15,
0.15,
0.15
]
},
{
"name": "BlueMatrix",
"values": [
0.15,
0.15,
0.15
]
}
]
},
{
"name": "bits",
"intarget": "swap",
"outtarget": "minecraft:main",
"uniforms": [
{
"name": "Resolution",
"values": [
16.0
]
},
{
"name": "MosaicSize",
"values": [
2.0
]
}
]
}
]
}
@@ -0,0 +1,37 @@
{
"earth_armor": {
"sounds": [
"toph:earth_armor"
]
},
"seismic_sense_start": {
"sounds": [
"toph:seismic_sense_start"
]
},
"seismic_sense_wave": {
"sounds": [
"toph:seismic_sense_wave"
]
},
"earth_column_1": {
"sounds": [
"toph:earth_column_1"
]
},
"arm_whoosh": {
"sounds": [
"toph:arm_whoosh"
]
},
"stone_smash": {
"sounds": [
"toph:stone_smash"
]
},
"stone_slide": {
"sounds": [
"toph:stone_slide"
]
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 212 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

+53
View File
@@ -0,0 +1,53 @@
{
"schemaVersion": 1,
"name": "Toph",
"id": "toph",
"version": "${version}",
"description": "Toph",
"authors": [
"NoRiskk"
],
"icon": "assets/toph/icon.png",
"license": "ARR",
"environment": "*",
"entrypoints": {
"main": [
{
"adapter": "kotlin",
"value": "gg.norisk.heroes.toph.TophManager"
}
],
"client": [
{
"adapter": "kotlin",
"value": "gg.norisk.heroes.toph.TophManager"
}
],
"server": [
{
"adapter": "kotlin",
"value": "gg.norisk.heroes.toph.TophManager"
}
]
},
"mixins": [
"toph.mixins.json"
],
"accessWidener": "toph.accesswidener",
"depends": {
"java": ">=17",
"minecraft": "*",
"fabric": "*",
"fabric-language-kotlin": "*"
},
"custom": {
"modmenu": {
"badges": [
"library"
],
"parent": {
"id": "hero-api"
}
}
}
}
@@ -0,0 +1,5 @@
accessWidener v2 named
accessible class net/minecraft/client/gui/hud/InGameHud$HeartType
accessible field net/minecraft/entity/FallingBlockEntity block Lnet/minecraft/block/BlockState;
accessible class net/minecraft/client/render/BackgroundRenderer$DarknessFogModifier
accessible class net/minecraft/client/render/BackgroundRenderer$FogData
+23
View File
@@ -0,0 +1,23 @@
{
"required": true,
"minVersion": "0.8",
"package": "gg.norisk.heroes.toph.mixin",
"compatibilityLevel": "JAVA_17",
"injectors": {
"defaultRequire": 1
},
"mixins": [
"ModelPartAccessor",
"entity.EntityMixin",
"entity.ItemEntityMixin",
"entity.PlayerEntityMixin"
],
"client": [
"MinecraftClientMixin",
"render.AnimalModelAccessor",
"render.DarknessFogModifierMixin",
"render.GameRendererAccessor",
"render.GameRendererMixin",
"render.WorldRendererAccessor"
]
}