first commit
@@ -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
|
||||
}
|
||||
}
|
||||
|
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
|
||||
}
|
||||
|
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"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 212 B |
|
After Width: | Height: | Size: 208 B |
|
After Width: | Height: | Size: 201 B |
|
After Width: | Height: | Size: 195 B |
|
After Width: | Height: | Size: 202 B |
|
After Width: | Height: | Size: 205 B |
|
After Width: | Height: | Size: 1.3 KiB |
@@ -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
|
||||
@@ -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"
|
||||
]
|
||||
}
|
||||