first commit
This commit is contained in:
@@ -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/katara.accesswidener"))
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package gg.norisk.heroes.katara.mixin;
|
||||
|
||||
import gg.norisk.heroes.katara.client.render.HealingWaterFeatureRenderer;
|
||||
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
|
||||
import net.minecraft.client.render.BufferBuilderStorage;
|
||||
import net.minecraft.client.render.RenderLayer;
|
||||
import net.minecraft.client.util.BufferAllocator;
|
||||
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(BufferBuilderStorage.class)
|
||||
public abstract class BufferBuilderStorageMixin {
|
||||
@Shadow
|
||||
private static void assignBufferBuilder(Object2ObjectLinkedOpenHashMap<RenderLayer, BufferAllocator> object2ObjectLinkedOpenHashMap, RenderLayer renderLayer) {
|
||||
}
|
||||
|
||||
@Inject(method = "method_54639", at = @At("TAIL"))
|
||||
private void injected(Object2ObjectLinkedOpenHashMap object2ObjectLinkedOpenHashMap, CallbackInfo ci) {
|
||||
assignBufferBuilder(object2ObjectLinkedOpenHashMap, HealingWaterFeatureRenderer.Companion.getLAYER());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package gg.norisk.heroes.katara.mixin;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import gg.norisk.heroes.katara.entity.IKataraEntity;
|
||||
import gg.norisk.heroes.katara.ability.WaterCircleAbilityV2;
|
||||
import gg.norisk.heroes.katara.utils.EntityCircleTracker;
|
||||
import gg.norisk.heroes.katara.utils.EntitySpinTracker;
|
||||
import kotlinx.coroutines.Job;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.ProjectileDeflection;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(Entity.class)
|
||||
public abstract class EntityMixin implements IKataraEntity {
|
||||
@Unique
|
||||
private Job waterHealingJob;
|
||||
@Unique
|
||||
private EntitySpinTracker entitySpinTracker = new EntitySpinTracker();
|
||||
@Unique
|
||||
private EntityCircleTracker entityCircleTracker = new EntityCircleTracker();
|
||||
|
||||
@ModifyReturnValue(
|
||||
method = "getProjectileDeflection",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private ProjectileDeflection katara$projectileReflection(ProjectileDeflection original) {
|
||||
if ((Object) this instanceof PlayerEntity player) {
|
||||
if (WaterCircleAbilityV2.INSTANCE.breakWaterCirclePiece(player)) {
|
||||
return ProjectileDeflection.REDIRECTED;
|
||||
}
|
||||
}
|
||||
return original;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Job getKatara_waterHealingJob() {
|
||||
return waterHealingJob;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKatara_waterHealingJob(@Nullable Job job) {
|
||||
waterHealingJob = job;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull EntitySpinTracker getKatara_entitySpinTracker() {
|
||||
return entitySpinTracker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull EntityCircleTracker getKatara_entityCircleTracker() {
|
||||
return entityCircleTracker;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package gg.norisk.heroes.katara.mixin;
|
||||
|
||||
import gg.norisk.heroes.katara.event.FluidEvents;
|
||||
import net.minecraft.fluid.FlowableFluid;
|
||||
import net.minecraft.fluid.Fluid;
|
||||
import net.minecraft.fluid.FluidState;
|
||||
import net.minecraft.state.StateManager;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
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;
|
||||
|
||||
@Mixin(FlowableFluid.class)
|
||||
public abstract class FlowableFluidMixin {
|
||||
@Inject(method = "onScheduledTick", at = @At("HEAD"), cancellable = true)
|
||||
private void onScheduledTickInjection(World world, BlockPos blockPos, FluidState fluidState, CallbackInfo ci) {
|
||||
var event = new FluidEvents.FluidEvent(world, blockPos, fluidState);
|
||||
FluidEvents.INSTANCE.getFluidTickEvent().invoke(event);
|
||||
if (event.isCancelled().get()) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "appendProperties", at = @At("TAIL"))
|
||||
private void appendStillProperties(StateManager.Builder<Fluid, FluidState> builder, CallbackInfo ci) {
|
||||
//builder.add(FluidEvents.INSTANCE.getStatic());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
package gg.norisk.heroes.katara.mixin;
|
||||
|
||||
import gg.norisk.heroes.katara.client.render.IFluidRendererExt;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.LeavesBlock;
|
||||
import net.minecraft.block.TranslucentBlock;
|
||||
import net.minecraft.client.color.world.BiomeColors;
|
||||
import net.minecraft.client.render.VertexConsumer;
|
||||
import net.minecraft.client.render.block.FluidRenderer;
|
||||
import net.minecraft.client.texture.Sprite;
|
||||
import net.minecraft.client.util.math.MatrixStack;
|
||||
import net.minecraft.fluid.Fluid;
|
||||
import net.minecraft.fluid.FluidState;
|
||||
import net.minecraft.registry.tag.FluidTags;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.BlockRenderView;
|
||||
import net.minecraft.world.BlockView;
|
||||
import org.joml.Matrix4f;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Shadow;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
@Mixin(FluidRenderer.class)
|
||||
public abstract class FluidRendererMixin implements IFluidRendererExt {
|
||||
@Shadow
|
||||
@Final
|
||||
private Sprite[] lavaSprites;
|
||||
|
||||
@Shadow
|
||||
@Final
|
||||
private Sprite[] waterSprites;
|
||||
|
||||
@Shadow
|
||||
protected static boolean isSameFluid(FluidState fluidState, FluidState fluidState2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Shadow
|
||||
public static boolean shouldRenderSide(BlockRenderView blockRenderView, BlockPos blockPos, FluidState fluidState, BlockState blockState, Direction direction, FluidState fluidState2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Shadow
|
||||
protected static boolean isSideCovered(BlockView blockView, BlockPos blockPos, Direction direction, float f, BlockState blockState) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Shadow
|
||||
protected abstract float getFluidHeight(BlockRenderView blockRenderView, Fluid fluid, BlockPos blockPos);
|
||||
|
||||
@Shadow
|
||||
protected abstract float calculateFluidHeight(BlockRenderView blockRenderView, Fluid fluid, float f, float g, float h, BlockPos blockPos);
|
||||
|
||||
@Shadow
|
||||
protected abstract float getFluidHeight(BlockRenderView blockRenderView, Fluid fluid, BlockPos blockPos, BlockState blockState, FluidState fluidState);
|
||||
|
||||
@Shadow
|
||||
protected abstract int getLight(BlockRenderView blockRenderView, BlockPos blockPos);
|
||||
|
||||
@Shadow
|
||||
protected abstract void vertex(VertexConsumer vertexConsumer, float f, float g, float h, float i, float j, float k, float l, float m, int n);
|
||||
|
||||
private void vertex2(Matrix4f matrix4f, VertexConsumer vertexConsumer, float f, float g, float h, float i, float j, float k, float l, float m, int n) {
|
||||
vertexConsumer.vertex(matrix4f, f, g, h).color(i, j, k, 1.0F).texture(l, m).light(n).normal(0.0F, 1.0F, 0.0F);
|
||||
}
|
||||
|
||||
@Shadow
|
||||
private Sprite waterOverlaySprite;
|
||||
|
||||
public void katara_renderFluid(MatrixStack matrixStack, BlockRenderView blockRenderView, Vec3d pos, VertexConsumer vertexConsumer, BlockState blockState, FluidState fluidState, Color waterColor) {
|
||||
Matrix4f matrix = matrixStack.peek().getPositionMatrix();
|
||||
|
||||
// Beispiel für Vertex-Anwendung:
|
||||
// float x = (float) pos.x - 0.5f;
|
||||
// float y = (float) pos.y - 0.5f;
|
||||
// float z = (float) pos.z - 0.5f;
|
||||
|
||||
boolean bl = fluidState.isIn(FluidTags.LAVA);
|
||||
Sprite[] sprites = bl ? this.lavaSprites : this.waterSprites;
|
||||
int i;
|
||||
if (waterColor != null) {
|
||||
i = waterColor.getRGB();
|
||||
} else {
|
||||
i = bl ? 16777215 : BiomeColors.getWaterColor(blockRenderView, new BlockPos((int) pos.x, (int) pos.y, (int) pos.z));
|
||||
}
|
||||
float f = (float) (i >> 16 & 0xFF) / 255.0F;
|
||||
float g = (float) (i >> 8 & 0xFF) / 255.0F;
|
||||
float h = (float) (i & 0xFF) / 255.0F;
|
||||
var blockPos = new BlockPos((int) pos.x, (int) pos.y, (int) pos.z);
|
||||
/*BlockState blockState2 = blockRenderView.getBlockState(blockPos.offset(Direction.DOWN));
|
||||
FluidState fluidState2 = blockState2.getFluidState();
|
||||
BlockState blockState3 = blockRenderView.getBlockState(blockPos.offset(Direction.UP));
|
||||
FluidState fluidState3 = blockState3.getFluidState();
|
||||
BlockState blockState4 = blockRenderView.getBlockState(blockPos.offset(Direction.NORTH));
|
||||
FluidState fluidState4 = blockState4.getFluidState();
|
||||
BlockState blockState5 = blockRenderView.getBlockState(blockPos.offset(Direction.SOUTH));
|
||||
FluidState fluidState5 = blockState5.getFluidState();
|
||||
BlockState blockState6 = blockRenderView.getBlockState(blockPos.offset(Direction.WEST));
|
||||
FluidState fluidState6 = blockState6.getFluidState();
|
||||
BlockState blockState7 = blockRenderView.getBlockState(blockPos.offset(Direction.EAST));
|
||||
FluidState fluidState7 = blockState7.getFluidState();*/
|
||||
boolean bl2 = true; //!isSameFluid(fluidState, fluidState3);
|
||||
boolean bl3 = true;//shouldRenderSide(blockRenderView, blockPos, fluidState, blockState, Direction.DOWN, fluidState2) && !isSideCovered(blockRenderView, blockPos, Direction.DOWN, 0.8888889F, blockState2);
|
||||
boolean bl4 = true;//shouldRenderSide(blockRenderView, blockPos, fluidState, blockState, Direction.NORTH, fluidState4);
|
||||
boolean bl5 = true;//shouldRenderSide(blockRenderView, blockPos, fluidState, blockState, Direction.SOUTH, fluidState5);
|
||||
boolean bl6 = true;//shouldRenderSide(blockRenderView, blockPos, fluidState, blockState, Direction.WEST, fluidState6);
|
||||
boolean bl7 = true;//shouldRenderSide(blockRenderView, blockPos, fluidState, blockState, Direction.EAST, fluidState7);
|
||||
if (bl2 || bl3 || bl7 || bl6 || bl4 || bl5) {
|
||||
float j = blockRenderView.getBrightness(Direction.DOWN, true);
|
||||
float k = blockRenderView.getBrightness(Direction.UP, true);
|
||||
float l = blockRenderView.getBrightness(Direction.NORTH, true);
|
||||
float m = blockRenderView.getBrightness(Direction.WEST, true);
|
||||
Fluid fluid = fluidState.getFluid();
|
||||
float n = 1.0f;//this.getFluidHeight(blockRenderView, fluid, blockPos, blockState, fluidState);
|
||||
float o = 1.0F;
|
||||
float p = 1.0F;
|
||||
float q = 1.0F;
|
||||
float r = 1.0F;
|
||||
if (n >= 1.0F) {
|
||||
o = 1.0F;
|
||||
p = 1.0F;
|
||||
q = 1.0F;
|
||||
r = 1.0F;
|
||||
} else {
|
||||
/*float s = this.getFluidHeight(blockRenderView, fluid, blockPos.north(), blockState4, fluidState4);
|
||||
float t = this.getFluidHeight(blockRenderView, fluid, blockPos.south(), blockState5, fluidState5);
|
||||
float u = this.getFluidHeight(blockRenderView, fluid, blockPos.east(), blockState7, fluidState7);
|
||||
float v = this.getFluidHeight(blockRenderView, fluid, blockPos.west(), blockState6, fluidState6);
|
||||
o = this.calculateFluidHeight(blockRenderView, fluid, n, s, u, blockPos.offset(Direction.NORTH).offset(Direction.EAST));
|
||||
p = this.calculateFluidHeight(blockRenderView, fluid, n, s, v, blockPos.offset(Direction.NORTH).offset(Direction.WEST));
|
||||
q = this.calculateFluidHeight(blockRenderView, fluid, n, t, u, blockPos.offset(Direction.SOUTH).offset(Direction.EAST));
|
||||
r = this.calculateFluidHeight(blockRenderView, fluid, n, t, v, blockPos.offset(Direction.SOUTH).offset(Direction.WEST));*/
|
||||
}
|
||||
|
||||
|
||||
// float s = (float) ((float) pos.getX() - camera.getPos().getX()) - 0.5f;//(float) (blockPos.getX() & 15);
|
||||
// float t = (float) ((float) pos.getY() - camera.getPos().getY()) - 0.5f;//(float) (blockPos.getY() & 15);
|
||||
// float u = (float) ((float) pos.getZ() - camera.getPos().getZ()) - 0.5f;//(float) (blockPos.getZ() & 15);
|
||||
float s = 0f;//(float) (blockPos.getX() & 15);
|
||||
float t = 0f;//(float) (blockPos.getY() & 15);
|
||||
float u = 0f;//(float) (blockPos.getZ() & 15);
|
||||
float v = 0.001F;
|
||||
float w = bl3 ? 0.001F : 0.0F;
|
||||
if (bl2 /*&& !isSideCovered(blockRenderView, blockPos, Direction.UP, Math.min(Math.min(p, r), Math.min(q, o)), blockState3)*/) {
|
||||
p -= 0.001F;
|
||||
r -= 0.001F;
|
||||
q -= 0.001F;
|
||||
o -= 0.001F;
|
||||
Vec3d vec3d = new Vec3d(1f, 1f, 1f); //fluidState.getVelocity(blockRenderView, blockPos);
|
||||
float x;
|
||||
float z;
|
||||
float ab;
|
||||
float ad;
|
||||
float y;
|
||||
float aa;
|
||||
float ac;
|
||||
float ae;
|
||||
if (vec3d.x == 0.0 && vec3d.z == 0.0) {
|
||||
Sprite sprite = sprites[0];
|
||||
x = sprite.getFrameU(0.0F);
|
||||
y = sprite.getFrameV(0.0F);
|
||||
z = x;
|
||||
aa = sprite.getFrameV(1.0F);
|
||||
ab = sprite.getFrameU(1.0F);
|
||||
ac = aa;
|
||||
ad = ab;
|
||||
ae = y;
|
||||
} else {
|
||||
Sprite sprite = sprites[1];
|
||||
float af = (float) MathHelper.atan2(vec3d.z, vec3d.x) - (float) (Math.PI / 2);
|
||||
float ag = MathHelper.sin(af) * 0.25F;
|
||||
float ah = MathHelper.cos(af) * 0.25F;
|
||||
float ai = 0.5F;
|
||||
x = sprite.getFrameU(0.5F + (-ah - ag));
|
||||
y = sprite.getFrameV(0.5F + -ah + ag);
|
||||
z = sprite.getFrameU(0.5F + -ah + ag);
|
||||
aa = sprite.getFrameV(0.5F + ah + ag);
|
||||
ab = sprite.getFrameU(0.5F + ah + ag);
|
||||
ac = sprite.getFrameV(0.5F + (ah - ag));
|
||||
ad = sprite.getFrameU(0.5F + (ah - ag));
|
||||
ae = sprite.getFrameV(0.5F + (-ah - ag));
|
||||
}
|
||||
|
||||
float aj = (x + z + ab + ad) / 4.0F;
|
||||
float af = (y + aa + ac + ae) / 4.0F;
|
||||
float ag = sprites[0].getAnimationFrameDelta();
|
||||
x = MathHelper.lerp(ag, x, aj);
|
||||
z = MathHelper.lerp(ag, z, aj);
|
||||
ab = MathHelper.lerp(ag, ab, aj);
|
||||
ad = MathHelper.lerp(ag, ad, aj);
|
||||
y = MathHelper.lerp(ag, y, af);
|
||||
aa = MathHelper.lerp(ag, aa, af);
|
||||
ac = MathHelper.lerp(ag, ac, af);
|
||||
ae = MathHelper.lerp(ag, ae, af);
|
||||
int ak = this.getLight(blockRenderView, blockPos);
|
||||
float ai = k * f;
|
||||
float al = k * g;
|
||||
float am = k * h;
|
||||
this.vertex2(matrix, vertexConsumer, s + 0.0F, t + p, u + 0.0F, ai, al, am, x, y, ak);
|
||||
this.vertex2(matrix, vertexConsumer, s + 0.0F, t + r, u + 1.0F, ai, al, am, z, aa, ak);
|
||||
this.vertex2(matrix, vertexConsumer, s + 1.0F, t + q, u + 1.0F, ai, al, am, ab, ac, ak);
|
||||
this.vertex2(matrix, vertexConsumer, s + 1.0F, t + o, u + 0.0F, ai, al, am, ad, ae, ak);
|
||||
if (fluidState.canFlowTo(blockRenderView, blockPos.up())) {
|
||||
this.vertex2(matrix, vertexConsumer, s + 0.0F, t + p, u + 0.0F, ai, al, am, x, y, ak);
|
||||
this.vertex2(matrix, vertexConsumer, s + 1.0F, t + o, u + 0.0F, ai, al, am, ad, ae, ak);
|
||||
this.vertex2(matrix, vertexConsumer, s + 1.0F, t + q, u + 1.0F, ai, al, am, ab, ac, ak);
|
||||
this.vertex2(matrix, vertexConsumer, s + 0.0F, t + r, u + 1.0F, ai, al, am, z, aa, ak);
|
||||
}
|
||||
}
|
||||
|
||||
if (bl3) {
|
||||
float xx = sprites[0].getMinU();
|
||||
float zx = sprites[0].getMaxU();
|
||||
float abx = sprites[0].getMinV();
|
||||
float adx = sprites[0].getMaxV();
|
||||
int an = this.getLight(blockRenderView, blockPos.down());
|
||||
float aax = j * f;
|
||||
float acx = j * g;
|
||||
float aex = j * h;
|
||||
this.vertex2(matrix, vertexConsumer, s, t + w, u + 1.0F, aax, acx, aex, xx, adx, an);
|
||||
this.vertex2(matrix, vertexConsumer, s, t + w, u, aax, acx, aex, xx, abx, an);
|
||||
this.vertex2(matrix, vertexConsumer, s + 1.0F, t + w, u, aax, acx, aex, zx, abx, an);
|
||||
this.vertex2(matrix, vertexConsumer, s + 1.0F, t + w, u + 1.0F, aax, acx, aex, zx, adx, an);
|
||||
}
|
||||
|
||||
int ao = this.getLight(blockRenderView, blockPos);
|
||||
|
||||
for (Direction direction : Direction.Type.HORIZONTAL) {
|
||||
float adx;
|
||||
float yx;
|
||||
float aax;
|
||||
float acx;
|
||||
float aex;
|
||||
float ap;
|
||||
boolean bl8;
|
||||
switch (direction) {
|
||||
case NORTH:
|
||||
adx = p;
|
||||
yx = o;
|
||||
aax = s;
|
||||
aex = s + 1.0F;
|
||||
acx = u + 0.001F;
|
||||
ap = u + 0.001F;
|
||||
bl8 = bl4;
|
||||
break;
|
||||
case SOUTH:
|
||||
adx = q;
|
||||
yx = r;
|
||||
aax = s + 1.0F;
|
||||
aex = s;
|
||||
acx = u + 1.0F - 0.001F;
|
||||
ap = u + 1.0F - 0.001F;
|
||||
bl8 = bl5;
|
||||
break;
|
||||
case WEST:
|
||||
adx = r;
|
||||
yx = p;
|
||||
aax = s + 0.001F;
|
||||
aex = s + 0.001F;
|
||||
acx = u + 1.0F;
|
||||
ap = u;
|
||||
bl8 = bl6;
|
||||
break;
|
||||
default:
|
||||
adx = o;
|
||||
yx = q;
|
||||
aax = s + 1.0F - 0.001F;
|
||||
aex = s + 1.0F - 0.001F;
|
||||
acx = u;
|
||||
ap = u + 1.0F;
|
||||
bl8 = bl7;
|
||||
}
|
||||
|
||||
if (bl8 && !isSideCovered(blockRenderView, blockPos, direction, Math.max(adx, yx), blockRenderView.getBlockState(blockPos.offset(direction)))) {
|
||||
BlockPos blockPos2 = blockPos.offset(direction);
|
||||
Sprite sprite2 = sprites[1];
|
||||
if (!bl) {
|
||||
Block block = blockRenderView.getBlockState(blockPos2).getBlock();
|
||||
if (block instanceof TranslucentBlock || block instanceof LeavesBlock) {
|
||||
sprite2 = this.waterOverlaySprite;
|
||||
}
|
||||
}
|
||||
|
||||
float ah = sprite2.getFrameU(0.0F);
|
||||
float ai = sprite2.getFrameU(0.5F);
|
||||
float al = sprite2.getFrameV((1.0F - adx) * 0.5F);
|
||||
float am = sprite2.getFrameV((1.0F - yx) * 0.5F);
|
||||
float aq = sprite2.getFrameV(0.5F);
|
||||
float ar = direction.getAxis() == Direction.Axis.Z ? l : m;
|
||||
float as = k * ar * f;
|
||||
float at = k * ar * g;
|
||||
float au = k * ar * h;
|
||||
this.vertex2(matrix, vertexConsumer, aax, t + adx, acx, as, at, au, ah, al, ao);
|
||||
this.vertex2(matrix, vertexConsumer, aex, t + yx, ap, as, at, au, ai, am, ao);
|
||||
this.vertex2(matrix, vertexConsumer, aex, t + w, ap, as, at, au, ai, aq, ao);
|
||||
this.vertex2(matrix, vertexConsumer, aax, t + w, acx, as, at, au, ah, aq, ao);
|
||||
if (sprite2 != this.waterOverlaySprite) {
|
||||
this.vertex2(matrix, vertexConsumer, aax, t + w, acx, as, at, au, ah, aq, ao);
|
||||
this.vertex2(matrix, vertexConsumer, aex, t + w, ap, as, at, au, ai, aq, ao);
|
||||
this.vertex2(matrix, vertexConsumer, aex, t + yx, ap, as, at, au, ai, am, ao);
|
||||
this.vertex2(matrix, vertexConsumer, aax, t + adx, acx, as, at, au, ah, al, ao);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package gg.norisk.heroes.katara.mixin;
|
||||
|
||||
import gg.norisk.heroes.katara.entity.IWaterBendingPlayer;
|
||||
import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@Mixin(PlayerEntity.class)
|
||||
public abstract class PlayerEntityMixin implements IWaterBendingPlayer {
|
||||
@Unique
|
||||
private final Set<BlockPos> waterPillarBlocks = new HashSet<>();
|
||||
@Unique
|
||||
private BlockPos waterPillarPos;
|
||||
|
||||
@Override
|
||||
public @NotNull Set<BlockPos> getKatara_waterPillarBlocks() {
|
||||
return waterPillarBlocks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable BlockPos getKatara_waterPillarOrigin() {
|
||||
return waterPillarPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKatara_waterPillarOrigin(@Nullable BlockPos blockPos) {
|
||||
waterPillarPos = blockPos;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package gg.norisk.heroes.katara.mixin;
|
||||
|
||||
import com.llamalad7.mixinextras.injector.ModifyReturnValue;
|
||||
import gg.norisk.heroes.katara.ability.WaterBendingAbility;
|
||||
import net.minecraft.client.network.AbstractClientPlayerEntity;
|
||||
import net.minecraft.client.render.entity.PlayerEntityRenderer;
|
||||
import net.minecraft.client.render.entity.model.BipedEntityModel;
|
||||
import net.minecraft.util.Hand;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
|
||||
@Mixin(PlayerEntityRenderer.class)
|
||||
public abstract class PlayerEntityRendererMixin {
|
||||
@ModifyReturnValue(
|
||||
method = "getArmPose",
|
||||
at = @At("RETURN")
|
||||
)
|
||||
private static BipedEntityModel.ArmPose getArmPoseInjection(BipedEntityModel.ArmPose original, AbstractClientPlayerEntity abstractClientPlayerEntity, Hand hand) {
|
||||
BipedEntityModel.ArmPose waterBendingPose = WaterBendingAbility.INSTANCE.getWaterBendingPose(abstractClientPlayerEntity, hand);
|
||||
if (waterBendingPose != null) {
|
||||
return waterBendingPose;
|
||||
}
|
||||
return original;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package gg.norisk.heroes.katara
|
||||
|
||||
import gg.norisk.heroes.common.hero.Hero
|
||||
import gg.norisk.heroes.common.hero.HeroManager.registerHero
|
||||
import gg.norisk.heroes.katara.ability.*
|
||||
import gg.norisk.heroes.katara.registry.EntityRegistry
|
||||
import gg.norisk.heroes.katara.registry.EntityRendererRegistry
|
||||
import gg.norisk.heroes.katara.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.minecraft.util.Identifier
|
||||
import org.apache.logging.log4j.LogManager
|
||||
|
||||
object KataraManager : ModInitializer, ClientModInitializer, DedicatedServerModInitializer {
|
||||
const val MOD_ID = "katara"
|
||||
val logger = LogManager.getLogger(MOD_ID)
|
||||
fun String.toId() = Identifier.of(MOD_ID, this)
|
||||
fun String.toEmote(): Identifier {
|
||||
return "emotes/$this.animation.json".toId()
|
||||
}
|
||||
|
||||
val waterBenderOverlay = "waterbender_overlay.png".toId()
|
||||
|
||||
override fun onInitialize() {
|
||||
logger.info("Starting $MOD_ID Hero...")
|
||||
SoundRegistry.init()
|
||||
WaterPillarAbility.initServer()
|
||||
HealingAbility.initServer()
|
||||
WaterFormingAbility.initServer()
|
||||
WaterBendingAbility.initServer()
|
||||
WaterCircleAbilityV2.initServer()
|
||||
EntityRegistry.init()
|
||||
//WaterBubbleAbility.initServer()
|
||||
}
|
||||
|
||||
override fun onInitializeClient() {
|
||||
ClientLifecycleEvents.CLIENT_STARTED.register {
|
||||
registerHeroes()
|
||||
}
|
||||
EntityRendererRegistry.init()
|
||||
WaterPillarAbility.initClient()
|
||||
HealingAbility.initClient()
|
||||
WaterFormingAbility.initClient()
|
||||
WaterCircleAbilityV2.initClient()
|
||||
WaterBendingAbility.initClient()
|
||||
}
|
||||
|
||||
override fun onInitializeServer() {
|
||||
registerHero(Katara)
|
||||
}
|
||||
|
||||
private fun registerHeroes() {
|
||||
registerHero(Katara)
|
||||
}
|
||||
|
||||
val Katara: Hero by Hero("Katara") {
|
||||
ability(WaterBendingAbility.ability)
|
||||
ability(WaterCircleAbilityV2.ability)
|
||||
ability(WaterPillarAbility.ability)
|
||||
ability(WaterFormingAbility.ability)
|
||||
ability(IceShardAbility.ability)
|
||||
ability(HealingAbility.ability)
|
||||
color = 0x416bdf
|
||||
overlaySkin = waterBenderOverlay
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
package gg.norisk.heroes.katara.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
|
||||
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.utils.sound
|
||||
import gg.norisk.heroes.katara.ability.WaterBendingAbility.getCurrentBendingEntity
|
||||
import gg.norisk.heroes.katara.ability.WaterBendingAbility.waterBendingDistance
|
||||
import gg.norisk.heroes.katara.client.render.HealingWaterFeatureRenderer
|
||||
import gg.norisk.heroes.katara.client.sound.WaterHealingSoundInstance
|
||||
import gg.norisk.heroes.katara.entity.IKataraEntity
|
||||
import gg.norisk.heroes.katara.entity.WaterBendingEntity
|
||||
import gg.norisk.utils.OldAnimation
|
||||
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.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.LivingEntity
|
||||
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.potion.Potions
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.sound.SoundEvents
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.silkmc.silk.core.entity.directionVector
|
||||
import net.silkmc.silk.core.item.itemStack
|
||||
import net.silkmc.silk.core.item.setPotion
|
||||
import net.silkmc.silk.core.task.mcCoroutineTask
|
||||
import net.silkmc.silk.core.text.literalText
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
object HealingAbility {
|
||||
fun initServer() {
|
||||
syncedValueChangeEvent.listen {
|
||||
if (it.key != WATER_HEALING_EFFECT) return@listen
|
||||
if (!it.entity.world.isClient) {
|
||||
|
||||
} else {
|
||||
if (it.entity.isReceivingWaterHealing) {
|
||||
MinecraftClient.getInstance().soundManager.play(WaterHealingSoundInstance(it.entity) { entity ->
|
||||
entity.isReceivingWaterHealing
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
/*ServerTickEvents.END_SERVER_TICK.register(ServerTickEvents.EndTick {
|
||||
for (player in it.players) {
|
||||
val currentEntity = player.getCurrentBendingEntity()
|
||||
if (currentEntity != null) {
|
||||
(player as IKataraEntity).katara_entityCircleTracker.update(player)
|
||||
if (player.katara_entityCircleTracker.isDrawingCircle) {
|
||||
(player as IKataraEntity).katara_entityCircleTracker.clear()
|
||||
//addToCircle(currentEntity, player)
|
||||
player.sendMessage("CIRCLE: ".literal)
|
||||
}
|
||||
} else {
|
||||
(player as IKataraEntity).katara_entityCircleTracker.clear()
|
||||
}
|
||||
}
|
||||
})*/
|
||||
}
|
||||
|
||||
data class WaterRender(
|
||||
val pos: Vec3d, var animation: OldAnimation, val startTime: Int = 0
|
||||
)
|
||||
|
||||
var counter = 0
|
||||
|
||||
fun PlayerEntity.getWaterBendingPos(distance: Double = this.waterBendingDistance): Vec3d {
|
||||
return this.getLeashPos(
|
||||
if (world.isClient) {
|
||||
MinecraftClient.getInstance().renderTickCounter.getTickDelta(false)
|
||||
} else 1f
|
||||
).add(this.directionVector.normalize().multiply(distance).add(0.0, 1.0, 0.0))
|
||||
}
|
||||
|
||||
fun initClient() {
|
||||
LivingEntityFeatureRendererRegistrationCallback.EVENT.register(LivingEntityFeatureRendererRegistrationCallback { entityType, entityRenderer, registrationHelper, context ->
|
||||
registrationHelper.register(HealingWaterFeatureRenderer(entityRenderer))
|
||||
})
|
||||
WorldRenderEvents.BEFORE_DEBUG_RENDER.register {
|
||||
val player = MinecraftClient.getInstance().player ?: return@register
|
||||
|
||||
val tickDelta = it.tickCounter().getTickDelta(false)
|
||||
for (entity in it.world().entities) {
|
||||
if (entity is WaterBendingEntity) {
|
||||
entity.tickTrail(entity.getOwner(), tickDelta)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val WATER_HEALING = "WaterBending:Healing"
|
||||
private const val WATER_HEALING_EFFECT = "WaterBending:HealingEffect"
|
||||
|
||||
private var PlayerEntity.isHealing: Boolean
|
||||
get() = this.getSyncedData<Boolean>(WATER_HEALING) ?: false
|
||||
set(value) = this.setSyncedData(WATER_HEALING, value)
|
||||
|
||||
var Entity.isReceivingWaterHealing: Boolean
|
||||
get() = this.getSyncedData<Boolean>(WATER_HEALING_EFFECT) ?: false
|
||||
set(value) = this.setSyncedData(WATER_HEALING_EFFECT, value)
|
||||
|
||||
fun Entity.handleWaterHealing(owner: PlayerEntity) {
|
||||
(this as IKataraEntity).katara_waterHealingJob?.cancel()
|
||||
isReceivingWaterHealing = true
|
||||
val duration = waterHealingMaxDuration.getValue(owner.uuid).seconds
|
||||
if (this is LivingEntity) {
|
||||
addStatusEffect(
|
||||
StatusEffectInstance(
|
||||
StatusEffects.REGENERATION,
|
||||
duration.inWholeMilliseconds.toInt() / 50,
|
||||
waterHealingRegeneration.getValue(owner.uuid).toInt(),
|
||||
false,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
(this as IKataraEntity).katara_waterHealingJob = mcCoroutineTask(sync = true, delay = duration) {
|
||||
isReceivingWaterHealing = false
|
||||
// sendMessage("Stopped WaterHealing".literal)
|
||||
}
|
||||
}
|
||||
|
||||
val waterHealingRegeneration = NumberProperty(
|
||||
0.0, 3,
|
||||
"Regeneration",
|
||||
AddValueTotal(1.0, 1.0, 1.0)
|
||||
).apply {
|
||||
icon = {
|
||||
Components.item(itemStack(Items.POTION) {
|
||||
setPotion(
|
||||
MinecraftClient.getInstance().world!!.registryManager.get(RegistryKeys.POTION)
|
||||
.getEntry(Potions.REGENERATION.value())
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
val waterHealingMaxDuration = NumberProperty(
|
||||
5.0, 4,
|
||||
"Max Duration Lasts",
|
||||
AddValueTotal(1.0, 1.0, 1.0, 1.0)
|
||||
).apply {
|
||||
icon = {
|
||||
Components.item(itemStack(Items.CLOCK) {})
|
||||
}
|
||||
}
|
||||
|
||||
val ability = object : PressAbility("Healing") {
|
||||
init {
|
||||
HeroesManager.client {
|
||||
this.keyBind = HeroKeyBindings.thirdKeyBind
|
||||
}
|
||||
this.condition = {
|
||||
it.getCurrentBendingEntity() != null
|
||||
}
|
||||
//this.usageProperty = buildMultipleUses(1.0, 3, AddValueTotal(1.0, 1.0, 1.0))
|
||||
this.cooldownProperty = buildCooldown(50.0, 4, AddValueTotal(-10.0, -10.0, -10.0, -10.0))
|
||||
|
||||
this.properties = listOf(waterHealingRegeneration, waterHealingMaxDuration)
|
||||
}
|
||||
|
||||
override fun getIconComponent(): Component {
|
||||
return Components.item(itemStack(Items.POTION) {
|
||||
setPotion(
|
||||
MinecraftClient.getInstance().world!!.registryManager.get(RegistryKeys.POTION)
|
||||
.getEntry(Potions.REGENERATION.value())
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
override fun hasUnlocked(player: PlayerEntity): Boolean {
|
||||
return WaterBendingAbility.ability.cooldownProperty.isMaxed(player.uuid) || player.isCreative
|
||||
}
|
||||
|
||||
override fun getUnlockCondition(): Text {
|
||||
return literalText {
|
||||
text(Text.translatable("heroes.ability.$internalKey.unlock_condition"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getBackgroundTexture(): Identifier {
|
||||
return Identifier.of("textures/block/packed_ice.png")
|
||||
}
|
||||
|
||||
override fun onStart(player: PlayerEntity, abilityScope: AbilityScope) {
|
||||
if (player is ServerPlayerEntity) {
|
||||
val entity = player.getCurrentBendingEntity()
|
||||
if (player.isSneaking) {
|
||||
entity?.apply {
|
||||
player.sound(SoundEvents.ENTITY_PLAYER_HURT_FREEZE, 1f, 2f)
|
||||
freeze()
|
||||
}
|
||||
} else {
|
||||
player.isHealing = true
|
||||
entity?.apply {
|
||||
isHealing = !isHealing
|
||||
if (isHealing) {
|
||||
entity.sound(SoundEvents.BLOCK_AMETHYST_BLOCK_HIT, 1f, 2f)
|
||||
} else {
|
||||
entity.sound(SoundEvents.BLOCK_AMETHYST_BLOCK_HIT, 1f, 0.3)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package gg.norisk.heroes.katara.ability
|
||||
|
||||
import gg.norisk.datatracker.entity.getSyncedData
|
||||
import gg.norisk.datatracker.entity.setSyncedData
|
||||
import gg.norisk.heroes.client.option.HeroKeyBindings
|
||||
import gg.norisk.heroes.common.HeroesManager
|
||||
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.katara.KataraManager.toId
|
||||
import gg.norisk.heroes.katara.ability.WaterBendingAbility.getCurrentBendingEntity
|
||||
import gg.norisk.heroes.katara.entity.IceShardEntity
|
||||
import gg.norisk.heroes.katara.registry.EntityRegistry
|
||||
import gg.norisk.heroes.katara.registry.SoundRegistry
|
||||
import io.wispforest.owo.ui.component.Components
|
||||
import io.wispforest.owo.ui.core.Component
|
||||
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.sound.SoundCategory
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.world.World
|
||||
import net.silkmc.silk.core.entity.directionVector
|
||||
import kotlin.random.Random
|
||||
|
||||
object IceShardAbility {
|
||||
|
||||
private const val ICE_SHARDING = "WaterBending:ICE_SHARDING"
|
||||
var PlayerEntity.isIceShooting: Boolean
|
||||
get() = this.getSyncedData<Boolean>(ICE_SHARDING) ?: false
|
||||
set(value) = this.setSyncedData(ICE_SHARDING, value)
|
||||
|
||||
val ICE_SHARD_SLOW_BOOST = EntityAttributeModifier(
|
||||
"ice_shard".toId(),
|
||||
-0.3,
|
||||
EntityAttributeModifier.Operation.ADD_MULTIPLIED_TOTAL
|
||||
)
|
||||
|
||||
val ability = object : HoldAbility("Ice Shards") {
|
||||
|
||||
init {
|
||||
HeroesManager.client {
|
||||
this.keyBind = HeroKeyBindings.fifthKeyBind
|
||||
}
|
||||
this.condition = {
|
||||
it.getCurrentBendingEntity() != null
|
||||
}
|
||||
|
||||
this.cooldownProperty =
|
||||
buildCooldown(26.0, 4, AddValueTotal(-2.0, -2.0, -2.0, -2.0))
|
||||
this.maxDurationProperty =
|
||||
buildMaxDuration(0.5, 5, AddValueTotal(0.25, 0.25, 0.25, 0.25, 0.25))
|
||||
}
|
||||
|
||||
override fun getIconComponent(): Component {
|
||||
return Components.item(Items.ARROW.defaultStack)
|
||||
}
|
||||
|
||||
override fun getBackgroundTexture(): Identifier {
|
||||
return Identifier.of("textures/block/packed_ice.png")
|
||||
}
|
||||
|
||||
override fun onStart(player: PlayerEntity, abilityScope: AbilityScope) {
|
||||
super.onStart(player, abilityScope)
|
||||
if (player is ServerPlayerEntity) {
|
||||
player.isIceShooting = true
|
||||
player.getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED)
|
||||
?.addTemporaryModifier(ICE_SHARD_SLOW_BOOST)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onTick(player: PlayerEntity) {
|
||||
super.onTick(player)
|
||||
if (player.isIceShooting) {
|
||||
launchProjectiles(player.world, player)
|
||||
}
|
||||
}
|
||||
|
||||
fun cleanUp(player: PlayerEntity) {
|
||||
if (player is ServerPlayerEntity) {
|
||||
player.isIceShooting = false
|
||||
player.getAttributeInstance(EntityAttributes.GENERIC_MOVEMENT_SPEED)
|
||||
?.removeModifier(ICE_SHARD_SLOW_BOOST.id)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDisable(player: PlayerEntity) {
|
||||
super.onDisable(player)
|
||||
cleanUp(player)
|
||||
}
|
||||
|
||||
override fun onEnd(player: PlayerEntity, abilityEndInformation: AbilityEndInformation) {
|
||||
super.onEnd(player, abilityEndInformation)
|
||||
cleanUp(player)
|
||||
}
|
||||
|
||||
private fun launchProjectiles(world: World, user: PlayerEntity) {
|
||||
if (!world.isClient) {
|
||||
val entity = user.getCurrentBendingEntity()
|
||||
entity?.discard()
|
||||
val iceShard = IceShardEntity(EntityRegistry.ICE_SHARD, world)
|
||||
iceShard.damage = 1.0
|
||||
val pos = user.eyePos.add(0.0, -0.1, 0.0).add(user.directionVector.normalize().multiply(1.0))
|
||||
val offset = 1.0
|
||||
val randomX = Random.nextDouble(-offset, offset)
|
||||
val randomY = Random.nextDouble(-offset, offset)
|
||||
val randomZ = Random.nextDouble(-offset, offset)
|
||||
val spawnPos = Vec3d(pos.x + randomX, pos.y + randomY, pos.z + randomZ)
|
||||
iceShard.setPosition(spawnPos.x, spawnPos.y, spawnPos.z)
|
||||
//snowballEntity.setItem(Items.SNOWBALL.defaultStack)
|
||||
iceShard.setVelocity(user, user.pitch, user.yaw, 0.0f, 5f, 0.0f)
|
||||
world.playSound(
|
||||
null,
|
||||
spawnPos.getX(),
|
||||
spawnPos.getY(),
|
||||
spawnPos.getZ(),
|
||||
SoundRegistry.ICE_PLACE,
|
||||
SoundCategory.PLAYERS,
|
||||
0.75f,
|
||||
Random.nextDouble(1.5, 2.0).toFloat()
|
||||
)
|
||||
world.spawnEntity(iceShard)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,296 @@
|
||||
package gg.norisk.heroes.katara.ability
|
||||
|
||||
import gg.norisk.datatracker.entity.*
|
||||
import gg.norisk.datatracker.serialization.BlockPosSerializer
|
||||
import gg.norisk.heroes.client.option.HeroKeyBindings
|
||||
import gg.norisk.heroes.client.renderer.BlockOutlineRenderer
|
||||
import gg.norisk.heroes.common.HeroesManager
|
||||
import gg.norisk.heroes.common.ability.CooldownProperty
|
||||
import gg.norisk.heroes.common.ability.operation.AddValueTotal
|
||||
import gg.norisk.heroes.common.command.DebugCommand.sendDebugMessage
|
||||
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.Networking.mousePacket
|
||||
import gg.norisk.heroes.common.networking.Networking.mouseScrollPacket
|
||||
import gg.norisk.heroes.common.utils.sound
|
||||
import gg.norisk.heroes.katara.KataraManager.Katara
|
||||
import gg.norisk.heroes.katara.KataraManager.MOD_ID
|
||||
import gg.norisk.heroes.katara.KataraManager.toId
|
||||
import gg.norisk.heroes.katara.ability.HealingAbility.getWaterBendingPos
|
||||
import gg.norisk.heroes.katara.ability.IceShardAbility.isIceShooting
|
||||
import gg.norisk.heroes.katara.ability.WaterCircleAbilityV2.waterCircleAmount
|
||||
import gg.norisk.heroes.katara.ability.WaterFormingAbility.isWaterForming
|
||||
import gg.norisk.heroes.katara.client.sound.WaterSelectingSoundInstance
|
||||
import gg.norisk.heroes.katara.entity.WaterBendingEntity
|
||||
import gg.norisk.heroes.katara.registry.EntityRegistry
|
||||
import gg.norisk.heroes.katara.registry.SoundRegistry
|
||||
import io.wispforest.owo.ui.component.Components
|
||||
import io.wispforest.owo.ui.core.Component
|
||||
import kotlinx.serialization.Serializable
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.network.AbstractClientPlayerEntity
|
||||
import net.minecraft.client.render.entity.model.BipedEntityModel.ArmPose
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.Fluids
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.registry.tag.BlockTags
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.silkmc.silk.core.server.players
|
||||
import net.silkmc.silk.core.task.mcCoroutineTask
|
||||
import net.silkmc.silk.core.text.literal
|
||||
import net.silkmc.silk.network.packet.s2cPacket
|
||||
import kotlin.random.Random
|
||||
|
||||
object WaterBendingAbility {
|
||||
val ability = object : HoldAbility("Water Bending") {
|
||||
init {
|
||||
HeroesManager.client {
|
||||
this.keyBind = HeroKeyBindings.firstKeyBind
|
||||
}
|
||||
|
||||
mouseScrollPacket.receiveOnServer { packet, context ->
|
||||
mcCoroutineTask(sync = true, client = false) {
|
||||
context.player.scaleBendingDistance(packet)
|
||||
}
|
||||
}
|
||||
|
||||
this.cooldownProperty =
|
||||
buildCooldown(10.0, 5, AddValueTotal(-1.0, -1.0, -1.0, -1.0, -1.0))
|
||||
this.maxDurationProperty =
|
||||
buildMaxDuration(10.0, 5, AddValueTotal(2.0, 1.0, 0.5, 0.5, 1.0))
|
||||
}
|
||||
|
||||
override fun onStart(player: PlayerEntity, abilityScope: AbilityScope) {
|
||||
if (!player.world.isClient) {
|
||||
player.isWaterSelecting = true
|
||||
}
|
||||
}
|
||||
|
||||
override fun getIconComponent(): Component {
|
||||
return Components.item(Items.WATER_BUCKET.defaultStack)
|
||||
}
|
||||
|
||||
|
||||
override fun getBackgroundTexture(): Identifier {
|
||||
return Identifier.of("textures/block/packed_ice.png")
|
||||
}
|
||||
|
||||
override fun onEnd(player: PlayerEntity, abilityEndInformation: AbilityEndInformation) {
|
||||
if (player is ServerPlayerEntity) {
|
||||
player.isWaterSelecting = false
|
||||
// player.sendMessage("END".literal)
|
||||
var lastPos: BlockPos? = null
|
||||
for (block in player.selectedWaterBlocks.blocks) {
|
||||
val state = player.world.getBlockState(block)
|
||||
if (state.isWaterSource
|
||||
) {
|
||||
lastPos = block
|
||||
player.world.breakBlock(block, false, player)
|
||||
if (state.isOf(Blocks.WATER)) {
|
||||
player.serverWorld.setBlockState(
|
||||
block,
|
||||
Fluids.FLOWING_WATER.getFlowing(1, false).blockState
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lastPos != null) {
|
||||
val water = EntityRegistry.WATER_BENDING.create(player.serverWorld) ?: return
|
||||
/*player.serverWorld.setBlockState(
|
||||
clipWithDistance.blockPos,
|
||||
Fluids.FLOWING_WATER.getFlowing(1, false).blockState
|
||||
)*/
|
||||
water.setPosition(lastPos.toCenterPos())
|
||||
//water.setPosition(player.getWaterBendingPos())
|
||||
water.ownerId = player.id
|
||||
water.isInitial = true
|
||||
player.serverWorld.spawnEntity(water)
|
||||
} else {
|
||||
// player.sendMessage("Jetzt".literal)
|
||||
if (player.waterCircleAmount > 0) {
|
||||
player.waterCircleAmount -= 1
|
||||
player.sound(SoundRegistry.WATER_CIRCLE_ADD, 0.4f, Random.nextDouble(1.5, 2.0))
|
||||
val water = EntityRegistry.WATER_BENDING.create(player.serverWorld) ?: return
|
||||
water.setPosition(player.getWaterBendingPos())
|
||||
water.ownerId = player.id
|
||||
water.isInitial = false
|
||||
player.serverWorld.spawnEntity(water)
|
||||
} else {
|
||||
abilityEndInformation.applyCooldown = false
|
||||
}
|
||||
}
|
||||
player.selectedWaterBlocks = SelectedWaterBlocks(mutableListOf())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ServerPlayerEntity.scaleBendingDistance(packet: Boolean) {
|
||||
if (!isHero(Katara)) return
|
||||
if (getCurrentBendingEntity() == null) return
|
||||
val world = this.serverWorld
|
||||
|
||||
val scale = 0.5
|
||||
val forceStrength = if (packet) scale else -scale
|
||||
|
||||
|
||||
waterBendingDistance += forceStrength
|
||||
if (waterBendingDistance < 3.0) {
|
||||
waterBendingDistance = 3.0
|
||||
}
|
||||
if (waterBendingDistance > 15) {
|
||||
waterBendingDistance = 15.0
|
||||
}
|
||||
}
|
||||
|
||||
val BlockState.isWaterSource
|
||||
get() = isOf(Blocks.WATER) || isIn(BlockTags.SNOW) || isIn(BlockTags.ICE) || isIn(
|
||||
BlockTags.LEAVES
|
||||
) || isIn(BlockTags.FLOWERS) || isIn(BlockTags.REPLACEABLE_BY_TREES) || isIn(BlockTags.LOGS) || isOf(Blocks.BAMBOO)
|
||||
|
||||
fun initServer() {
|
||||
(registeredTypes as MutableMap<Any, Any>).put(
|
||||
SelectedWaterBlocks::class,
|
||||
SelectedWaterBlocks.serializer()
|
||||
)
|
||||
|
||||
ServerTickEvents.END_SERVER_TICK.register {
|
||||
for (player in it.players) {
|
||||
if (!player.isHero(Katara)) continue
|
||||
if (!player.isWaterSelecting) continue
|
||||
/*val target = findCrosshairTarget(player,5.0,5.0,1f)
|
||||
player.sendMessage("Target: ${target.pos} ${target.type} ${player.world.getBlockState(target.pos.toBlockPos())}".literal)
|
||||
player.firstWaterFormingPos = target.pos.toBlockPos()*/
|
||||
val pos = (player.raycast(45.0, 0.0f, true) as? BlockHitResult?)?.blockPos?.toImmutable() ?: continue
|
||||
val state = player.world.getBlockState(pos)
|
||||
if (state.isWaterSource) {
|
||||
if (!player.selectedWaterBlocks.blocks.contains(pos)) {
|
||||
player.selectedWaterBlocks.blocks.add(pos)
|
||||
if (player.selectedWaterBlocks.blocks.size >= 15) {
|
||||
player.selectedWaterBlocks.blocks.removeFirstOrNull()
|
||||
}
|
||||
|
||||
player.selectedWaterBlocks = SelectedWaterBlocks(buildList {
|
||||
addAll(player.selectedWaterBlocks.blocks)
|
||||
}.toMutableList())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mousePacket.receiveOnServer { packet, context ->
|
||||
mcCoroutineTask(sync = true, client = false) {
|
||||
//TODO automatisieren
|
||||
if (!context.player.isHero(Katara)) return@mcCoroutineTask
|
||||
if (packet.isClicked() && packet.isRight()) {
|
||||
for (entity in context.player.serverWorld.iterateEntities()) {
|
||||
if (entity is WaterBendingEntity && entity.ownerId == context.player.id) {
|
||||
entity.drop(context.player)
|
||||
}
|
||||
}
|
||||
} else if (packet.isLeft() && packet.isClicked()) {
|
||||
for (entity in context.player.serverWorld.iterateEntities()) {
|
||||
if (entity is WaterBendingEntity && entity.ownerId == context.player.id) {
|
||||
entity.launch(context.player)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
syncedValueChangeEvent.listen { event ->
|
||||
if (event.key != IS_WATER_SELECTING) return@listen
|
||||
val player = event.entity as? PlayerEntity? ?: return@listen
|
||||
if (player.world.isClient) {
|
||||
if (player.isWaterSelecting) {
|
||||
MinecraftClient.getInstance().soundManager.play(WaterSelectingSoundInstance(player) { entity ->
|
||||
((entity as? PlayerEntity?)?.isWaterSelecting == true)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun initClient() {
|
||||
WorldRenderEvents.AFTER_TRANSLUCENT.register {
|
||||
val player = MinecraftClient.getInstance().player ?: return@register
|
||||
val matrices = it.matrixStack() ?: return@register
|
||||
if (!player.isWaterSelecting) return@register
|
||||
for (block in player.selectedWaterBlocks.blocks) {
|
||||
BlockOutlineRenderer.drawBlockBox(
|
||||
matrices,
|
||||
it.consumers() ?: return@register,
|
||||
block,
|
||||
1.0f,
|
||||
1.0f,
|
||||
1.0f,
|
||||
0.6f
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class SelectedWaterBlocks(
|
||||
val blocks: MutableList<@Serializable(with = BlockPosSerializer::class) BlockPos>
|
||||
)
|
||||
|
||||
private const val IS_WATER_SELECTING = "WaterBending:IS_WATER_SELECTING"
|
||||
private const val WATER_BENDING_DISTANCE = "WaterBending:WATER_BENDING_DISTANCE"
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
fun getWaterBendingPose(player: AbstractClientPlayerEntity, hand: Hand): ArmPose? {
|
||||
if (player.isIceShooting) {
|
||||
return ArmPose.BOW_AND_ARROW
|
||||
}
|
||||
if (player.isWaterSelecting) {
|
||||
return ArmPose.BOW_AND_ARROW
|
||||
}
|
||||
if (player.isWaterForming) {
|
||||
return ArmPose.BOW_AND_ARROW
|
||||
}
|
||||
val currentEntity = player.getCurrentBendingEntity()
|
||||
if (currentEntity != null && !currentEntity.wasLaunched) {
|
||||
return ArmPose.BOW_AND_ARROW
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
var Entity.selectedWaterBlocks: SelectedWaterBlocks
|
||||
get() = this.getSyncedData<SelectedWaterBlocks>("$MOD_ID:SelectedWaterBlocks")
|
||||
?: SelectedWaterBlocks(mutableListOf())
|
||||
set(value) {
|
||||
this.setSyncedData("$MOD_ID:SelectedWaterBlocks", value)
|
||||
}
|
||||
|
||||
var PlayerEntity.waterBendingDistance: Double
|
||||
get() = this.getSyncedData<Double>(WATER_BENDING_DISTANCE) ?: 3.0
|
||||
set(value) = this.setSyncedData(WATER_BENDING_DISTANCE, value)
|
||||
|
||||
var PlayerEntity.isWaterSelecting: Boolean
|
||||
get() = this.getSyncedData<Boolean>(IS_WATER_SELECTING) ?: false
|
||||
set(value) = this.setSyncedData(IS_WATER_SELECTING, value)
|
||||
|
||||
fun PlayerEntity.getCurrentBendingEntity(): WaterBendingEntity? {
|
||||
val entities = if (!world.isClient) {
|
||||
(world as ServerWorld).iterateEntities()
|
||||
} else {
|
||||
(world as ClientWorld).entities
|
||||
}
|
||||
|
||||
return entities.filterIsInstance<WaterBendingEntity>().find { it.ownerId == id }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
package gg.norisk.heroes.katara.ability
|
||||
|
||||
import gg.norisk.datatracker.entity.getSyncedData
|
||||
import gg.norisk.datatracker.entity.setSyncedData
|
||||
import gg.norisk.datatracker.entity.syncedValueChangeEvent
|
||||
import gg.norisk.heroes.common.ability.NumberProperty
|
||||
import gg.norisk.heroes.common.ability.operation.AddValueTotal
|
||||
import gg.norisk.heroes.common.events.EntityEvents
|
||||
import gg.norisk.heroes.common.hero.ability.AbstractAbility
|
||||
import gg.norisk.heroes.common.utils.sound
|
||||
import gg.norisk.heroes.katara.ability.WaterBendingAbility.getCurrentBendingEntity
|
||||
import gg.norisk.heroes.katara.client.render.IFluidRendererExt
|
||||
import gg.norisk.heroes.katara.client.sound.WaterCircleSoundInstance
|
||||
import gg.norisk.heroes.katara.entity.IKataraEntity
|
||||
import gg.norisk.heroes.katara.entity.WaterBendingEntity
|
||||
import gg.norisk.heroes.katara.registry.SoundRegistry
|
||||
import gg.norisk.utils.OldAnimation
|
||||
import io.wispforest.owo.ui.component.Components
|
||||
import io.wispforest.owo.ui.core.Component
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents
|
||||
import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents
|
||||
import net.fabricmc.fabric.api.entity.event.v1.ServerLivingEntityEvents.AllowDamage
|
||||
import net.fabricmc.loader.api.FabricLoader
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.RenderLayers
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.damage.DamageTypes
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.Fluids
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.sound.SoundEvents
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Colors
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.silkmc.silk.commands.command
|
||||
import net.silkmc.silk.core.text.literalText
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.*
|
||||
import kotlin.random.Random
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.toJavaDuration
|
||||
|
||||
object WaterCircleAbilityV2 {
|
||||
val waterCircleMaxBalls = NumberProperty(1.0, 5, "Water Circle Sphere", AddValueTotal(1.0, 1.0, 1.0, 1.0, 1.0, 3.0)).apply {
|
||||
icon = {
|
||||
Components.item(Items.HEART_OF_THE_SEA.defaultStack)
|
||||
}
|
||||
}
|
||||
val waterCircleMaxFallDamage = NumberProperty(10.0, 3, "Water Circle Fall Distance", AddValueTotal(20.0, 30.0, 40.0)).apply {
|
||||
icon = {
|
||||
Components.item(Items.WATER_BUCKET.defaultStack)
|
||||
}
|
||||
}
|
||||
|
||||
val ability = object : AbstractAbility<Any>("Water Circle") {
|
||||
init {
|
||||
this.cooldownProperty = buildNoCooldown()
|
||||
this.properties = listOf(
|
||||
waterCircleMaxBalls,
|
||||
waterCircleMaxFallDamage
|
||||
)
|
||||
}
|
||||
|
||||
override fun onTick(player: PlayerEntity) {
|
||||
super.onTick(player)
|
||||
if (player is ServerPlayerEntity) {
|
||||
val currentEntity = player.getCurrentBendingEntity()
|
||||
if (currentEntity != null) {
|
||||
(player as IKataraEntity).katara_entitySpinTracker.update(player)
|
||||
if (player.katara_entitySpinTracker.hasSpunWildly()) {
|
||||
if (hasUnlocked(player)) {
|
||||
(player as IKataraEntity).katara_entitySpinTracker.clear()
|
||||
addToCircle(currentEntity, player)
|
||||
} else {
|
||||
player.katara_entitySpinTracker.clear()
|
||||
player.sendMessage(Text.translatable("heroes.ability.locked").withColor(Colors.RED))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
(player as IKataraEntity).katara_entitySpinTracker.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEnable(player: PlayerEntity) {
|
||||
super.onEnable(player)
|
||||
if (player is ServerPlayerEntity) {
|
||||
player.waterCircleAmount = 0
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDisable(player: PlayerEntity) {
|
||||
super.onDisable(player)
|
||||
if (player is ServerPlayerEntity) {
|
||||
player.waterCircleAmount = 0
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasUnlocked(player: PlayerEntity): Boolean {
|
||||
return WaterBendingAbility.ability.cooldownProperty.isMaxed(player.uuid) || player.isCreative
|
||||
}
|
||||
|
||||
override fun getUnlockCondition(): Text {
|
||||
return literalText {
|
||||
text(Text.translatable("heroes.ability.$internalKey.unlock_condition"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getIconComponent(): Component {
|
||||
return Components.item(Items.HEART_OF_THE_SEA.defaultStack)
|
||||
}
|
||||
|
||||
override fun getBackgroundTexture(): Identifier {
|
||||
return Identifier.of("textures/block/packed_ice.png")
|
||||
}
|
||||
}
|
||||
|
||||
fun initServer() {
|
||||
if (FabricLoader.getInstance().isDevelopmentEnvironment) {
|
||||
command("waterbending") {
|
||||
literal("watercircle") {
|
||||
argument<Int>("amount") { amount ->
|
||||
runs {
|
||||
this.source.playerOrThrow.waterCircleAmount = amount()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
syncedValueChangeEvent.listen { event ->
|
||||
if (event.key != WATER_CIRCLE_AMOUNT) return@listen
|
||||
val player = event.entity as? PlayerEntity? ?: return@listen
|
||||
if (player.world.isClient) {
|
||||
if (player.waterCircleAmount == 1) {
|
||||
MinecraftClient.getInstance().soundManager.play(WaterCircleSoundInstance(player) { entity ->
|
||||
((entity as? PlayerEntity?)?.waterCircleAmount ?: 1) > 0
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EntityEvents.computeFallDamageEvent.listen { event ->
|
||||
if (event.livingEntity is ServerPlayerEntity) {
|
||||
if (event.originalFallDamage > 0 && event.fallDistance <= waterCircleMaxFallDamage.getValue(event.livingEntity.uuid)) {
|
||||
if (event.livingEntity.breakWaterCirclePiece()) {
|
||||
event.fallDamage = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ServerLivingEntityEvents.ALLOW_DAMAGE.register(AllowDamage { entity, source, amount ->
|
||||
if (entity is PlayerEntity) {
|
||||
if ((source.isOf(DamageTypes.ON_FIRE)) && entity.breakWaterCirclePiece()) {
|
||||
entity.extinguishWithSound()
|
||||
return@AllowDamage false
|
||||
} else if ((source.isOf(DamageTypes.IN_FIRE) || source.isOf(DamageTypes.LAVA)) && entity.breakWaterCirclePiece()) {
|
||||
entity.world.setBlockState(entity.blockPos, Fluids.WATER.getFlowing(1, true).blockState)
|
||||
return@AllowDamage false
|
||||
}
|
||||
}
|
||||
return@AllowDamage true
|
||||
})
|
||||
}
|
||||
|
||||
private fun addToCircle(entity: WaterBendingEntity?, player: PlayerEntity) {
|
||||
if (player.waterCircleAmount < waterCircleMaxBalls.getValue(player.uuid)) {
|
||||
player.sound(SoundRegistry.WATER_CIRCLE_ADD, 0.7f, Random.nextDouble(1.2, 1.5))
|
||||
entity?.discard()
|
||||
player.waterCircleAmount += 1
|
||||
}
|
||||
}
|
||||
|
||||
fun LivingEntity.breakWaterCirclePiece(): Boolean {
|
||||
if (waterCircleAmount > 0) {
|
||||
waterCircleAmount -= 1
|
||||
sound(SoundEvents.ENTITY_GENERIC_SPLASH, 1f, 1f)
|
||||
repeat(40) {
|
||||
(world as? ServerWorld?)?.spawnParticles(
|
||||
ParticleTypes.SPLASH,
|
||||
getParticleX(0.5) + Random.nextDouble(-1.0, 1.0),
|
||||
randomBodyY + Random.nextDouble(-1.0, 1.0),
|
||||
getParticleZ(0.5) + Random.nextDouble(-1.0, 1.0),
|
||||
20,
|
||||
0.001,
|
||||
0.001,
|
||||
0.001,
|
||||
0.0
|
||||
)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun initClient() {
|
||||
WorldRenderEvents.BEFORE_DEBUG_RENDER.register {
|
||||
val clientPlayer = MinecraftClient.getInstance().player ?: return@register
|
||||
val tickDelta = it.tickCounter().getTickDelta(false)
|
||||
val matrixStack = it.matrixStack() ?: return@register
|
||||
for (player in it.world().players) {
|
||||
val amount = player.waterCircleAmount
|
||||
if (amount > 0) {
|
||||
repeat(amount) { index ->
|
||||
renderWaterCircle(
|
||||
player,
|
||||
matrixStack,
|
||||
player.getLerpedPos(tickDelta),
|
||||
Fluids.FLOWING_WATER.defaultState.blockState,
|
||||
OldAnimation(0.5f, 0.5f, 1.seconds.toJavaDuration()),
|
||||
index,
|
||||
waterCircleMaxBalls.getMaxValue().toInt(), // Pass the total amount for even distribution
|
||||
System.currentTimeMillis() // Use current time in milliseconds for rotation
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
fun renderWaterCircle(
|
||||
player: PlayerEntity,
|
||||
matrixStack: MatrixStack,
|
||||
centerPos: Vec3d,
|
||||
state: BlockState,
|
||||
animation: OldAnimation,
|
||||
index: Int,
|
||||
totalAmount: Int,
|
||||
currentTimeMillis: Long
|
||||
) {
|
||||
val camera = MinecraftClient.getInstance().gameRenderer.camera
|
||||
val renderer = MinecraftClient.getInstance().blockRenderManager
|
||||
val world = MinecraftClient.getInstance().world ?: return
|
||||
val vertexConsumer = MinecraftClient.getInstance().bufferBuilders.entityVertexConsumers.getBuffer(
|
||||
RenderLayers.getFluidLayer(state.fluidState)
|
||||
)
|
||||
|
||||
// Kreisposition berechnen
|
||||
val radius = 0.8 // Radius des Kreises
|
||||
val rotationSpeed = 0.002 // Geschwindigkeit der Rotation (Bogenmaß pro Millisekunde)
|
||||
val baseAngle = (index.toDouble() / totalAmount) * 2 * PI // Gleichmäßige Winkelverteilung entlang des Kreises
|
||||
val rotationOffset = (currentTimeMillis * rotationSpeed) % (2 * PI) // Rotationsversatz basierend auf der Zeit
|
||||
val angle = baseAngle + rotationOffset
|
||||
|
||||
val timeFactor = (System.currentTimeMillis() % 10000L) / 1000.0 // Zeit in Sekunden (loop alle 10 Sekunden)
|
||||
val sineOffset = Math.sin((timeFactor + index * 0.5) * Math.PI) * 0.1 // Wellenbewegung mit tickDelta animiert
|
||||
|
||||
|
||||
val offsetX = radius * cos(angle)
|
||||
val offsetZ = radius * sin(angle)
|
||||
|
||||
val blockPos = centerPos.add(offsetX, 0.0, offsetZ)
|
||||
|
||||
matrixStack.push()
|
||||
matrixStack.translate(
|
||||
blockPos.x - camera.pos.x,
|
||||
blockPos.y - camera.pos.y + sineOffset,
|
||||
blockPos.z - camera.pos.z
|
||||
)
|
||||
matrixStack.translate(0.0, (player.height / 2.0), 0.0)
|
||||
matrixStack.scale(animation.get(), animation.get(), animation.get())
|
||||
matrixStack.multiply(rotateTowards(blockPos, centerPos, Quaternionf()))
|
||||
matrixStack.translate(-0.5, -0.5, -0.5)
|
||||
|
||||
(renderer.fluidRenderer as IFluidRendererExt).katara_renderFluid(
|
||||
matrixStack,
|
||||
world,
|
||||
blockPos,
|
||||
vertexConsumer,
|
||||
state,
|
||||
state.fluidState,
|
||||
null
|
||||
)
|
||||
|
||||
matrixStack.pop()
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
fun rotateTowards(from: Vec3d?, to: Vec3d, original: Quaternionf): Quaternionf {
|
||||
val direction = to.subtract(from).normalize()
|
||||
val forward = Vector3f(0f, 0f, -1f)
|
||||
|
||||
val dir = Vector3f(direction.x.toFloat(), direction.y.toFloat(), direction.z.toFloat())
|
||||
val axis = Vector3f()
|
||||
forward.cross(dir, axis).normalize()
|
||||
|
||||
val dot = forward.dot(dir)
|
||||
val angle = acos(max(-1.0, min(1.0, dot.toDouble()))).toFloat()
|
||||
|
||||
val rotationQuat = Quaternionf().fromAxisAngleRad(axis, angle)
|
||||
return rotationQuat.mul(original)
|
||||
}
|
||||
|
||||
private const val WATER_CIRCLE_AMOUNT = "WaterBending:WaterCircleAmount"
|
||||
|
||||
var LivingEntity.waterCircleAmount: Int
|
||||
get() = this.getSyncedData<Int>(WATER_CIRCLE_AMOUNT) ?: 0
|
||||
set(value) = this.setSyncedData(WATER_CIRCLE_AMOUNT, value)
|
||||
}
|
||||
@@ -0,0 +1,376 @@
|
||||
package gg.norisk.heroes.katara.ability
|
||||
|
||||
import gg.norisk.datatracker.entity.getSyncedData
|
||||
import gg.norisk.datatracker.entity.setSyncedData
|
||||
import gg.norisk.heroes.client.option.HeroKeyBindings
|
||||
import gg.norisk.heroes.client.renderer.RenderUtils
|
||||
import gg.norisk.heroes.common.HeroesManager
|
||||
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.utils.sound
|
||||
import gg.norisk.heroes.common.utils.toBlockPos
|
||||
import gg.norisk.heroes.katara.KataraManager
|
||||
import gg.norisk.heroes.katara.ability.IceShardAbility.isIceShooting
|
||||
import gg.norisk.heroes.katara.ability.WaterBendingAbility.getCurrentBendingEntity
|
||||
import gg.norisk.heroes.katara.client.render.IFluidRendererExt
|
||||
import gg.norisk.heroes.katara.registry.SoundRegistry
|
||||
import io.wispforest.owo.ui.component.Components
|
||||
import io.wispforest.owo.ui.core.Component
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.RenderLayers
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.item.Items
|
||||
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.util.Identifier
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.util.math.random.Random
|
||||
import net.minecraft.world.World
|
||||
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.abs
|
||||
import kotlin.math.sign
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
object WaterFormingAbility {
|
||||
|
||||
val waterFormingMaxDistance = NumberProperty(
|
||||
10.0, 3,
|
||||
"Water Forming Max Blocks",
|
||||
AddValueTotal(5.0, 5.0, 5.0)
|
||||
).apply {
|
||||
icon = {
|
||||
Components.item(itemStack(Items.ICE) {})
|
||||
}
|
||||
}
|
||||
|
||||
val ability = object : HoldAbility("Water Forming") {
|
||||
init {
|
||||
HeroesManager.client {
|
||||
this.keyBind = HeroKeyBindings.secondKeyBind
|
||||
}
|
||||
this.condition = {
|
||||
val pos = (it.raycast(20.0, 0.0f, false) as? BlockHitResult?)?.blockPos?.toImmutable()
|
||||
checkForEnoughWater(pos, it.world) || it.getCurrentBendingEntity() != null
|
||||
}
|
||||
|
||||
this.cooldownProperty =
|
||||
buildCooldown(10.0, 5, AddValueTotal(-0.1, -0.4, -0.2, -0.8, -1.5, -1.0))
|
||||
this.maxDurationProperty =
|
||||
buildMaxDuration(10.0, 5, AddValueTotal(0.1, 0.4, 0.2, 0.8, 1.5, 1.0))
|
||||
|
||||
this.properties = listOf(waterFormingMaxDistance)
|
||||
}
|
||||
|
||||
override fun onStart(player: PlayerEntity, abilityScope: AbilityScope) {
|
||||
super.onStart(player, abilityScope)
|
||||
if (player is ServerPlayerEntity) {
|
||||
val pos = (player.raycast(20.0, 0.0f, false) as? BlockHitResult?)?.blockPos?.toImmutable()
|
||||
player.isWaterForming = true
|
||||
player.firstWaterFormingPos = pos
|
||||
}
|
||||
}
|
||||
|
||||
override fun getIconComponent(): Component {
|
||||
return Components.item(Items.BLUE_ICE.defaultStack)
|
||||
}
|
||||
|
||||
override fun getBackgroundTexture(): Identifier {
|
||||
return Identifier.of("textures/block/packed_ice.png")
|
||||
}
|
||||
|
||||
override fun onDisable(player: PlayerEntity) {
|
||||
super.onDisable(player)
|
||||
cleanUp(player)
|
||||
}
|
||||
|
||||
private fun cleanUp(player: PlayerEntity) {
|
||||
if (player is ServerPlayerEntity) {
|
||||
player.isWaterForming = false
|
||||
player.firstWaterFormingPos = null
|
||||
player.secondWaterFormingPos = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEnd(player: PlayerEntity, abilityEndInformation: AbilityEndInformation) {
|
||||
super.onEnd(player, abilityEndInformation)
|
||||
if (player is ServerPlayerEntity) {
|
||||
placeIceSelection(
|
||||
player.serverWorld,
|
||||
player,
|
||||
player.firstWaterFormingPos,
|
||||
player.secondWaterFormingPos
|
||||
)
|
||||
cleanUp(player)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var startTime: Long? = null
|
||||
private var endTime: Long? = null // Endzeit speichern
|
||||
private var lastAlpha: Float = 0f
|
||||
|
||||
fun initServer() {
|
||||
ServerTickEvents.END_SERVER_TICK.register {
|
||||
for (player in it.players) {
|
||||
if (!player.isHero(KataraManager.Katara)) continue
|
||||
/*val target = findCrosshairTarget(player,5.0,5.0,1f)
|
||||
player.sendMessage("Target: ${target.pos} ${target.type} ${player.world.getBlockState(target.pos.toBlockPos())}".literal)
|
||||
player.firstWaterFormingPos = target.pos.toBlockPos()*/
|
||||
if (player.firstWaterFormingPos != null) {
|
||||
val pos = (player.raycast(20.0, 0.0f, false) as? BlockHitResult?)?.blockPos?.toImmutable()
|
||||
if (pos != player.firstWaterFormingPos && pos?.isWithinDistance(
|
||||
player.firstWaterFormingPos,
|
||||
waterFormingMaxDistance.getValue(player.uuid)
|
||||
) == true
|
||||
) {
|
||||
player.secondWaterFormingPos = pos
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun initClient() {
|
||||
WorldRenderEvents.BEFORE_DEBUG_RENDER.register {
|
||||
val player = MinecraftClient.getInstance().player ?: return@register
|
||||
val tickDelta = it.tickCounter().getTickDelta(false)
|
||||
val matrixStack = it.matrixStack() ?: return@register
|
||||
/*renderBlock(
|
||||
matrixStack,
|
||||
Vec3d.of(player.firstWaterFormingPos ?: return@register),
|
||||
Blocks.ICE.defaultState,
|
||||
false
|
||||
)*/
|
||||
for (blockPos in createStaircasePath(
|
||||
player.firstWaterFormingPos ?: return@register,
|
||||
player.secondWaterFormingPos ?: player.firstWaterFormingPos ?: return@register
|
||||
)) {
|
||||
renderBlock(
|
||||
matrixStack,
|
||||
Vec3d.of(blockPos),
|
||||
Blocks.ICE.defaultState,
|
||||
false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val overlay = Identifier.ofVanilla("textures/misc/powder_snow_outline.png")
|
||||
HudRenderCallback.EVENT.register(HudRenderCallback { drawContext, tickCounter ->
|
||||
val player = MinecraftClient.getInstance().player
|
||||
|
||||
if (player?.isWaterForming == true || player?.isIceShooting == true) {
|
||||
if (startTime == null) {
|
||||
startTime = System.currentTimeMillis() // Startzeit initialisieren
|
||||
}
|
||||
endTime = null // Endzeit zurücksetzen, da der Effekt aktiv ist
|
||||
|
||||
val currentTime = System.currentTimeMillis()
|
||||
val elapsedTime = currentTime - startTime!! // Verstrichene Zeit in Millisekunden
|
||||
|
||||
val fadeInDuration = 1000.0 // Fade-In-Dauer in Millisekunden (1 Sekunde)
|
||||
lastAlpha = (elapsedTime / fadeInDuration).coerceAtMost(1.0).toFloat() // Begrenze Alpha auf maximal 1
|
||||
|
||||
RenderUtils.renderOverlay(drawContext, overlay, lastAlpha)
|
||||
} else if (endTime == null && startTime != null) {
|
||||
// Effekt ist beendet, Endzeit setzen
|
||||
endTime = System.currentTimeMillis()
|
||||
} else if (endTime != null) {
|
||||
// Ausfade-Phase
|
||||
val currentTime = System.currentTimeMillis()
|
||||
val fadeOutDuration = 200.0 // Ausfade-Dauer in Millisekunden (1 Sekunde)
|
||||
val elapsedFadeOutTime = currentTime - endTime!! // Verstrichene Zeit seit Ende
|
||||
|
||||
if (elapsedFadeOutTime < fadeOutDuration) {
|
||||
// Linearer Fade-Out von 1 bis 0
|
||||
val alpha = (lastAlpha - (elapsedFadeOutTime / fadeOutDuration)).toFloat()
|
||||
RenderUtils.renderOverlay(drawContext, overlay, alpha)
|
||||
} else {
|
||||
// Vollständig ausgeblendet, alles zurücksetzen
|
||||
startTime = null
|
||||
endTime = null
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
fun renderBlock(
|
||||
matrixStack: MatrixStack,
|
||||
pos: Vec3d,
|
||||
state: BlockState,
|
||||
fluid: Boolean = false,
|
||||
) {
|
||||
val blockPos = pos.toBlockPos()
|
||||
//println("Pos: $blockPos")
|
||||
val camera = MinecraftClient.getInstance().gameRenderer.camera
|
||||
val renderer = MinecraftClient.getInstance().blockRenderManager
|
||||
val world = MinecraftClient.getInstance().world ?: return
|
||||
val blockState = world.getBlockState(blockPos)
|
||||
if (!(blockState.isLiquid || blockState.isAir || !blockState.isSolid)) {
|
||||
return
|
||||
}
|
||||
val vertexConsumer = if (fluid) {
|
||||
MinecraftClient.getInstance().bufferBuilders.entityVertexConsumers.getBuffer(
|
||||
RenderLayers.getFluidLayer(state.fluidState)
|
||||
)
|
||||
} else {
|
||||
MinecraftClient.getInstance().bufferBuilders.entityVertexConsumers.getBuffer(
|
||||
RenderLayers.getBlockLayer(state)
|
||||
)
|
||||
}
|
||||
|
||||
matrixStack.push()
|
||||
matrixStack.translate(
|
||||
blockPos.x - camera.pos.x,
|
||||
blockPos.y - camera.pos.y,
|
||||
blockPos.z - camera.pos.z
|
||||
)
|
||||
|
||||
val scale = 1f
|
||||
matrixStack.scale(scale, scale, scale)
|
||||
|
||||
|
||||
//matrixStack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(angle))
|
||||
|
||||
// Rotation um die Y-Achse
|
||||
|
||||
//matrixStack.translate(-0.5, 0.5, -0.5)
|
||||
|
||||
if (fluid) {
|
||||
(renderer.fluidRenderer as IFluidRendererExt).katara_renderFluid(
|
||||
matrixStack,
|
||||
world,
|
||||
blockPos.toCenterPos(),
|
||||
vertexConsumer,
|
||||
state,
|
||||
state.fluidState,
|
||||
null
|
||||
)
|
||||
} else {
|
||||
renderer.renderBlock(state, blockPos, world, matrixStack, vertexConsumer, true, Random.create())
|
||||
}
|
||||
|
||||
matrixStack.pop()
|
||||
}
|
||||
|
||||
|
||||
fun createStaircasePath(start: BlockPos, end: BlockPos): List<BlockPos> {
|
||||
val path = mutableSetOf<BlockPos>()
|
||||
path.add(start)
|
||||
path.add(end)
|
||||
|
||||
var currentX = start.x
|
||||
var currentY = start.y
|
||||
var currentZ = start.z
|
||||
|
||||
val deltaX = end.x - start.x
|
||||
val deltaY = end.y - start.y
|
||||
val deltaZ = end.z - start.z
|
||||
|
||||
val steps = maxOf(abs(deltaX), abs(deltaY), abs(deltaZ))
|
||||
|
||||
val stepX = deltaX.sign
|
||||
val stepY = deltaY.sign
|
||||
val stepZ = deltaZ.sign
|
||||
|
||||
var accumulatedY = 0.0
|
||||
val yStepIncrement = abs(deltaY.toDouble() / steps)
|
||||
|
||||
for (i in 0..steps) {
|
||||
path.add(BlockPos(currentX, currentY, currentZ))
|
||||
|
||||
if (currentX != end.x) currentX += stepX
|
||||
if (currentZ != end.z) currentZ += stepZ
|
||||
|
||||
accumulatedY += yStepIncrement
|
||||
if (accumulatedY >= 1) {
|
||||
currentY += stepY
|
||||
accumulatedY -= 1
|
||||
}
|
||||
}
|
||||
|
||||
return path.toList()
|
||||
}
|
||||
|
||||
private fun placeIceSelection(
|
||||
world: ServerWorld,
|
||||
player: ServerPlayerEntity,
|
||||
firstPos: BlockPos?,
|
||||
secondPos: BlockPos?
|
||||
) {
|
||||
if (firstPos == null) return
|
||||
if (secondPos == null) return
|
||||
|
||||
player.sound(SoundRegistry.ICE_PLACE, pitch = kotlin.random.Random.nextDouble(1.0, 1.5))
|
||||
//player.sound(SoundEvents.ENTITY_PLAYER_HURT_FREEZE, volume = 0.5f, pitch = kotlin.random.Random.nextDouble(1.0, 1.5))
|
||||
player.getCurrentBendingEntity()?.discard()
|
||||
|
||||
for (blockPos in createStaircasePath(
|
||||
firstPos,
|
||||
secondPos
|
||||
)) {
|
||||
val currentState = world.getBlockState(blockPos)
|
||||
if (currentState.isLiquid || currentState.isAir || !currentState.isSolid) {
|
||||
world.setBlockState(blockPos, Blocks.ICE.defaultState)
|
||||
val pos = blockPos.toCenterPos()
|
||||
world.spawnParticles(
|
||||
ParticleTypes.CLOUD,
|
||||
pos.x + kotlin.random.Random.nextDouble(-1.0, 1.0),
|
||||
pos.y + kotlin.random.Random.nextDouble(-1.0, 1.0),
|
||||
pos.z + kotlin.random.Random.nextDouble(-1.0, 1.0),
|
||||
1,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkForEnoughWater(pos: BlockPos?, world: World): Boolean {
|
||||
if (pos == null) return false
|
||||
for (blockPos in pos.filledSpherePositionSet(3)) {
|
||||
val state = world.getBlockState(blockPos)
|
||||
if (world.getBlockState(blockPos)
|
||||
.isOf(Blocks.WATER) || state.isIn(BlockTags.SNOW) || state.isIn(BlockTags.ICE)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private const val IS_WATER_FORMING = "WaterBending:IS_WATER_FORMING"
|
||||
private const val WATER_FORMING_FIRST = "WaterBending:FirstWaterFormingPos"
|
||||
private const val WATER_FORMING_SECOND = "WaterBending:SecondWaterFormingPos"
|
||||
|
||||
var PlayerEntity.isWaterForming: Boolean
|
||||
get() = this.getSyncedData<Boolean>(IS_WATER_FORMING) ?: false
|
||||
set(value) = this.setSyncedData(IS_WATER_FORMING, value)
|
||||
|
||||
var PlayerEntity.firstWaterFormingPos: BlockPos?
|
||||
get() = this.getSyncedData<BlockPos>(WATER_FORMING_FIRST)
|
||||
set(value) = this.setSyncedData(WATER_FORMING_FIRST, value)
|
||||
|
||||
var PlayerEntity.secondWaterFormingPos: BlockPos?
|
||||
get() = this.getSyncedData<BlockPos>(WATER_FORMING_SECOND)
|
||||
set(value) = this.setSyncedData(WATER_FORMING_SECOND, value)
|
||||
}
|
||||
@@ -0,0 +1,430 @@
|
||||
package gg.norisk.heroes.katara.ability
|
||||
|
||||
import gg.norisk.datatracker.entity.getSyncedData
|
||||
import gg.norisk.datatracker.entity.setSyncedData
|
||||
import gg.norisk.datatracker.entity.syncedValueChangeEvent
|
||||
import gg.norisk.emote.ext.playEmote
|
||||
import gg.norisk.emote.ext.stopEmote
|
||||
import gg.norisk.emote.network.EmoteNetworking.playEmote
|
||||
import gg.norisk.heroes.client.option.HeroKeyBindings
|
||||
import gg.norisk.heroes.common.HeroesManager
|
||||
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.ability.operation.MultiplyBase
|
||||
import gg.norisk.heroes.common.command.DebugCommand.sendDebugMessage
|
||||
import gg.norisk.heroes.common.hero.ability.AbilityScope
|
||||
import gg.norisk.heroes.common.hero.ability.implementation.ToggleAbility
|
||||
import gg.norisk.heroes.common.utils.sound
|
||||
import gg.norisk.heroes.common.utils.toBlockPos
|
||||
import gg.norisk.heroes.katara.KataraManager.toEmote
|
||||
import gg.norisk.heroes.katara.client.render.IFluidRendererExt
|
||||
import gg.norisk.heroes.katara.client.sound.VelocityBasedFlyingSoundInstance
|
||||
import gg.norisk.heroes.katara.entity.IWaterBendingPlayer
|
||||
import gg.norisk.heroes.katara.event.FluidEvents
|
||||
import gg.norisk.utils.OldAnimation
|
||||
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.block.Blocks
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.network.AbstractClientPlayerEntity
|
||||
import net.minecraft.client.render.RenderLayers
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.Fluids
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvents
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.RotationAxis
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.util.math.random.Random
|
||||
import net.silkmc.silk.core.entity.modifyVelocity
|
||||
import net.silkmc.silk.core.math.geometry.filledSpherePositionSet
|
||||
import net.silkmc.silk.core.task.mcCoroutineTask
|
||||
import net.silkmc.silk.core.text.literal
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import kotlin.math.*
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.toJavaDuration
|
||||
|
||||
object WaterPillarAbility {
|
||||
private val animation by lazy { OldAnimation(0f, 360f, 0.6.seconds.toJavaDuration()) }
|
||||
|
||||
fun initClient() {
|
||||
WorldRenderEvents.BEFORE_DEBUG_RENDER.register {
|
||||
val tickDelta = it.tickCounter().getTickDelta(false)
|
||||
for (player in it.world().players) {/*renderBlock(it.matrixStack() ?: return@register, player.getLerpedPos(tickDelta), Blocks.DIRT.defaultState)
|
||||
renderBlock(
|
||||
it.matrixStack() ?: return@register,
|
||||
player.getLerpedPos(tickDelta).add(1.0, 0.0, 0.0),
|
||||
Fluids.WATER.defaultState.blockState,
|
||||
true
|
||||
)*/
|
||||
|
||||
val dummy = player as IWaterBendingPlayer
|
||||
val origin = player.waterPillarOrigin ?: continue
|
||||
val currentPos = player.getLerpedPos(tickDelta).add(0.0, 0.2, 0.0)
|
||||
val positions = calculatePositionsBetween(origin, currentPos, 10)
|
||||
val world = it.world()
|
||||
|
||||
for ((index, position) in positions.withIndex()) {
|
||||
val pos = position
|
||||
renderBlock(
|
||||
it.matrixStack() ?: return@register,
|
||||
pos,
|
||||
origin,
|
||||
currentPos,
|
||||
Fluids.WATER.defaultState.blockState,
|
||||
true,
|
||||
index = index
|
||||
)
|
||||
if (kotlin.random.Random.nextInt(1, 11) > 9) {
|
||||
val offset = 0.5
|
||||
val randomXOffset = kotlin.random.Random.nextDouble(-offset, offset)
|
||||
val randomYOffset = kotlin.random.Random.nextDouble(-offset, offset)
|
||||
val randomZOffset = kotlin.random.Random.nextDouble(-offset, offset)
|
||||
//val particle = listOf(ParticleTypes.BUBBLE_POP, ParticleTypes.SPLASH)
|
||||
world.addParticle(
|
||||
ParticleTypes.SPLASH,
|
||||
pos.x + randomXOffset,
|
||||
pos.y + randomYOffset,
|
||||
pos.z + randomZOffset,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun renderBlock(
|
||||
matrixStack: MatrixStack,
|
||||
pos: Vec3d,
|
||||
origin: BlockPos,
|
||||
playerPos: Vec3d,
|
||||
state: BlockState,
|
||||
fluid: Boolean = false,
|
||||
index: Int = 0, // Neuer Parameter für den Index
|
||||
) {
|
||||
val camera = MinecraftClient.getInstance().gameRenderer.camera
|
||||
val renderer = MinecraftClient.getInstance().blockRenderManager
|
||||
val world = MinecraftClient.getInstance().world ?: return
|
||||
val vertexConsumer = if (fluid) {
|
||||
MinecraftClient.getInstance().bufferBuilders.entityVertexConsumers.getBuffer(
|
||||
RenderLayers.getFluidLayer(state.fluidState)
|
||||
)
|
||||
} else {
|
||||
MinecraftClient.getInstance().bufferBuilders.entityVertexConsumers.getBuffer(
|
||||
RenderLayers.getBlockLayer(state)
|
||||
)
|
||||
}
|
||||
|
||||
matrixStack.push()
|
||||
matrixStack.translate(
|
||||
pos.x - camera.pos.x, pos.y - camera.pos.y, pos.z - camera.pos.z
|
||||
)
|
||||
|
||||
val scale = 2f / (Math.pow(1.1, index.toDouble())).toFloat()
|
||||
matrixStack.scale(scale, scale, scale)
|
||||
|
||||
val dx = origin.x - playerPos.x
|
||||
val dz = origin.z - playerPos.z
|
||||
val angle = Math.toDegrees(Math.atan2(dz, dx)).toFloat() // Berechne den Winkel
|
||||
|
||||
matrixStack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(angle))
|
||||
//matrixStack.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(angle))
|
||||
|
||||
// Rotation um die Y-Achse
|
||||
matrixStack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(animation.get()))
|
||||
|
||||
|
||||
matrixStack.translate(-0.5, -0.5, -0.5)
|
||||
|
||||
if (fluid) {
|
||||
(renderer.fluidRenderer as IFluidRendererExt).katara_renderFluid(
|
||||
matrixStack, world, pos, vertexConsumer, state, state.fluidState, null
|
||||
)
|
||||
} else {
|
||||
renderer.renderBlock(state, pos.toBlockPos(), world, matrixStack, vertexConsumer, true, Random.create())
|
||||
}
|
||||
|
||||
matrixStack.pop()
|
||||
|
||||
if (animation.isDone) {
|
||||
animation.reset()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun rotateTowards(from: Vec3d?, to: Vec3d, original: Quaternionf): Quaternionf {
|
||||
val direction = to.subtract(from).normalize()
|
||||
val forward = Vector3f(0f, 0f, -1f)
|
||||
|
||||
val dir = Vector3f(direction.x.toFloat(), direction.y.toFloat(), direction.z.toFloat())
|
||||
val axis = Vector3f()
|
||||
forward.cross(dir, axis).normalize()
|
||||
|
||||
val dot = forward.dot(dir)
|
||||
val angle = acos(max(-1.0, min(1.0, dot.toDouble()))).toFloat()
|
||||
|
||||
/*if (dot < -0.9999f) {
|
||||
axis[1f, 0f] = 0f
|
||||
if (abs(forward.x.toDouble()) > 0.999f) axis[0f, 1f] = 0f
|
||||
}*/
|
||||
|
||||
val rotationQuat = Quaternionf().fromAxisAngleRad(axis, angle)
|
||||
return rotationQuat.mul(original)
|
||||
}
|
||||
|
||||
|
||||
fun initServer() {
|
||||
syncedValueChangeEvent.listen {
|
||||
if (it.key != WATER_PILLAR) return@listen
|
||||
val player = it.entity as? PlayerEntity ?: return@listen
|
||||
if (!player.world.isClient) {
|
||||
if (player.isWaterPillar) {/*player.modifyVelocity(Vec3d(0.0, 1.3, 0.0))
|
||||
player.sound(SoundEvents.BLOCK_BUBBLE_COLUMN_UPWARDS_INSIDE, 0.3, 1f)
|
||||
mcCoroutineTask(sync = true, client = false, delay = 0.5.seconds) {
|
||||
//player.sendMessage("JETZT".literal)
|
||||
player.abilities.allowFlying = true
|
||||
player.abilities.flying = true
|
||||
player.sendAbilitiesUpdate()
|
||||
}*/
|
||||
} else {
|
||||
(player as ServerPlayerEntity).cleanUpWaterBendingBlocks()
|
||||
}
|
||||
} else {
|
||||
if (player.isWaterPillar) {
|
||||
(player as AbstractClientPlayerEntity).playEmote("waterpillar".toEmote())
|
||||
MinecraftClient.getInstance().soundManager.play(VelocityBasedFlyingSoundInstance(player) { entity ->
|
||||
(entity as? PlayerEntity?)?.isWaterPillar == true
|
||||
})
|
||||
} else {
|
||||
(player as AbstractClientPlayerEntity).stopEmote("waterpillar".toEmote())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FluidEvents.fluidTickEvent.listen { event ->
|
||||
for (player in event.world.players.filter { it.isWaterPillar }) {
|
||||
val dummy = player as IWaterBendingPlayer
|
||||
if (player.katara_waterPillarBlocks.contains(event.blockPos)) {
|
||||
event.isCancelled.set(true)
|
||||
return@listen
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private const val WATER_PILLAR = "WaterBending:WaterPillar"
|
||||
private const val WATER_PILLAR_ORIGIN = "WaterBending:WaterPillarOrigin"
|
||||
|
||||
private var PlayerEntity.isWaterPillar: Boolean
|
||||
get() = this.getSyncedData<Boolean>(WATER_PILLAR) ?: false
|
||||
set(value) = this.setSyncedData(WATER_PILLAR, value)
|
||||
|
||||
private var PlayerEntity.waterPillarOrigin: BlockPos?
|
||||
get() = this.getSyncedData<BlockPos>(WATER_PILLAR_ORIGIN)
|
||||
set(value) = this.setSyncedData(WATER_PILLAR_ORIGIN, value)
|
||||
|
||||
private fun ServerPlayerEntity.cleanUpWaterBendingBlocks() {
|
||||
val dummy = this as IWaterBendingPlayer
|
||||
for (pos in this.katara_waterPillarBlocks) {
|
||||
if (world.getBlockState(pos).isOf(Blocks.WATER)) {
|
||||
world.setBlockState(pos, Blocks.AIR.defaultState)
|
||||
}
|
||||
}
|
||||
sound(SoundEvents.ENTITY_GENERIC_SPLASH, 1f, 1f)
|
||||
katara_waterPillarBlocks.clear()
|
||||
|
||||
if (interactionManager.gameMode.isSurvivalLike) {
|
||||
abilities.allowFlying = false
|
||||
abilities.flying = false
|
||||
sendAbilitiesUpdate()
|
||||
}
|
||||
(this as IWaterBendingPlayer).katara_waterPillarOrigin = null
|
||||
}
|
||||
|
||||
private fun calculatePositionsBetween(start: BlockPos, end: Vec3d, steps: Int = 10): List<Vec3d> {
|
||||
val positions = mutableSetOf<Vec3d>()
|
||||
|
||||
val controlPoint = Vec3d(
|
||||
(start.x + end.x) / 2.0,
|
||||
(start.y + end.y) / 2.0 + 5, // Raise the control point to create a curve
|
||||
(start.z + end.z) / 2.0
|
||||
)
|
||||
|
||||
for (i in 0..steps) {
|
||||
val t = i / steps.toDouble()
|
||||
val oneMinusT = 1 - t
|
||||
|
||||
val x = (oneMinusT.pow(2) * start.x) + (2 * oneMinusT * t * controlPoint.x) + (t.pow(2) * end.x)
|
||||
val y = (oneMinusT.pow(2) * start.y) + (2 * oneMinusT * t * controlPoint.y) + (t.pow(2) * end.y)
|
||||
val z = (oneMinusT.pow(2) * start.z) + (2 * oneMinusT * t * controlPoint.z) + (t.pow(2) * end.z)
|
||||
|
||||
positions.add(Vec3d(x, y, z))
|
||||
}
|
||||
|
||||
return positions.toList()
|
||||
}
|
||||
|
||||
val waterPillarDistance = NumberProperty(15.0, 5, "Water Pillar Distance", AddValueTotal(3.0,3.0,3.0,3.0,3.0)).apply {
|
||||
icon = {
|
||||
Components.item(Items.SPYGLASS.defaultStack)
|
||||
}
|
||||
}
|
||||
val waterPillarVelocityBoost = NumberProperty(1.0, 5, "Water Pillar Start Boost", MultiplyBase(1.0, 1.2, 1.4, 1.5, 1.8, 1.9)).apply {
|
||||
icon = {
|
||||
Components.item(Items.FIREWORK_ROCKET.defaultStack)
|
||||
}
|
||||
}
|
||||
|
||||
val ability = object : ToggleAbility("Water Pillar") {
|
||||
init {
|
||||
HeroesManager.client {
|
||||
this.keyBind = HeroKeyBindings.fourthKeyBinding
|
||||
}
|
||||
this.condition = {
|
||||
it.isTouchingWater
|
||||
}
|
||||
this.properties = listOf(
|
||||
waterPillarDistance,
|
||||
waterPillarVelocityBoost,
|
||||
)
|
||||
this.cooldownProperty =
|
||||
CooldownProperty(20.0, 4, "Cooldown", AddValueTotal(-5.0, -5.0, -2.0, -3.0))
|
||||
}
|
||||
|
||||
override fun getBackgroundTexture(): Identifier {
|
||||
return Identifier.of("textures/block/packed_ice.png")
|
||||
}
|
||||
|
||||
override fun getIconComponent(): Component {
|
||||
return Components.item(Items.WATER_BUCKET.defaultStack)
|
||||
}
|
||||
|
||||
override fun onStart(player: PlayerEntity, abilityScope: AbilityScope) {
|
||||
if (player is ServerPlayerEntity) {
|
||||
player.playEmote("waterpillar_start".toEmote())
|
||||
mcCoroutineTask(sync = true, client = false, delay = 0.5.seconds) {
|
||||
player.waterPillarOrigin = player.blockPos
|
||||
(player as IWaterBendingPlayer).katara_waterPillarOrigin = player.blockPos
|
||||
player.isWaterPillar = true
|
||||
player.abilities.allowFlying = true
|
||||
player.abilities.flying = true
|
||||
player.modifyVelocity(Vec3d(0.0, waterPillarVelocityBoost.getValue(player.uuid), 0.0))
|
||||
player.sound(SoundEvents.BLOCK_BUBBLE_COLUMN_UPWARDS_INSIDE, 0.3, 1f)
|
||||
mcCoroutineTask(sync = true, client = false, delay = 0.5.seconds) {
|
||||
player.sendAbilitiesUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDisable(player: PlayerEntity) {
|
||||
super.onDisable(player)
|
||||
cleanUp(player)
|
||||
}
|
||||
|
||||
fun cleanUp(player: PlayerEntity) {
|
||||
if (player is ServerPlayerEntity) {
|
||||
if (player.isWaterPillar) {
|
||||
player.waterPillarOrigin = null
|
||||
player.isWaterPillar = false
|
||||
player.cleanUpWaterBendingBlocks()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEnd(player: PlayerEntity, abilityEndInformation: AbilityEndInformation) {
|
||||
cleanUp(player)
|
||||
}
|
||||
|
||||
override fun onTick(player: PlayerEntity) {
|
||||
if (player is ServerPlayerEntity && player.isWaterPillar) {
|
||||
val dummy = player as IWaterBendingPlayer
|
||||
val otherPos = mutableSetOf<BlockPos>()
|
||||
val origin = player.waterPillarOrigin ?: return
|
||||
for (vec3d in calculatePositionsBetween(origin, player.pos)) {
|
||||
for (blockPos in vec3d.toBlockPos().filledSpherePositionSet(3)) {
|
||||
otherPos.add(blockPos)
|
||||
}
|
||||
}
|
||||
val distance = sqrt(origin.getSquaredDistance(player.pos))
|
||||
val maxDistance = waterPillarDistance.getValue(player.uuid)
|
||||
|
||||
if (distance >= maxDistance) {
|
||||
player.sendMessage(Text.translatable("heroes.katara.ability.water_pillar.too_far_away"))
|
||||
player.sendDebugMessage("Max Distance: ${maxDistance}".literal)
|
||||
player.isWaterPillar = false
|
||||
player.waterPillarOrigin = null
|
||||
addCooldown(player)
|
||||
return
|
||||
}
|
||||
|
||||
val world = player.world
|
||||
for (freePos in otherPos) {
|
||||
if (kotlin.random.Random.nextInt(1, 1000) > 960) {
|
||||
if (world.getBlockState(freePos)
|
||||
.isOf(Blocks.AIR) && world.getBlockState(freePos.down()).isSolid
|
||||
) {
|
||||
world.setBlockState(freePos, Fluids.WATER.getFlowing(1, true).blockState)
|
||||
world.playSound(
|
||||
null,
|
||||
freePos,
|
||||
SoundEvents.ENTITY_GENERIC_SPLASH,
|
||||
SoundCategory.BLOCKS,
|
||||
0.3f,
|
||||
kotlin.random.Random.nextDouble(0.8, 1.3).toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val dummy = player as IWaterBendingPlayer
|
||||
val origin = player.waterPillarOrigin ?: return
|
||||
|
||||
repeat(1) {
|
||||
player.world.addParticle(
|
||||
ParticleTypes.BUBBLE_POP,
|
||||
player.getParticleX(0.5),
|
||||
player.randomBodyY,
|
||||
player.getParticleZ(0.5),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
)
|
||||
}
|
||||
|
||||
if (player.age.mod(10) == 0) {
|
||||
for (vec3d in calculatePositionsBetween(
|
||||
origin,
|
||||
player.getLerpedPos(MinecraftClient.getInstance().renderTickCounter.getTickDelta(false)),
|
||||
10
|
||||
)) {
|
||||
player.world.playSound(
|
||||
MinecraftClient.getInstance().player,
|
||||
vec3d.x,
|
||||
vec3d.y,
|
||||
vec3d.z,
|
||||
SoundEvents.BLOCK_WATER_AMBIENT,
|
||||
SoundCategory.BLOCKS,
|
||||
0.5f,
|
||||
kotlin.random.Random.nextDouble(0.5, 1.1).toFloat()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
package gg.norisk.heroes.katara.client.render
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import gg.norisk.heroes.katara.KataraManager.toId
|
||||
import gg.norisk.heroes.katara.ability.HealingAbility.isReceivingWaterHealing
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.*
|
||||
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.util.math.MatrixStack
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.Util
|
||||
import org.joml.Matrix4f
|
||||
|
||||
@Environment(value = EnvType.CLIENT)
|
||||
class HealingWaterFeatureRenderer<T : Entity, M : EntityModel<T>>(featureRendererContext: FeatureRendererContext<T, M>) :
|
||||
FeatureRenderer<T, M>(featureRendererContext) {
|
||||
companion object {
|
||||
val healingWaterTexture = "textures/overlay/healing_water.png".toId()
|
||||
|
||||
val LAYER: RenderLayer = RenderLayer.of(
|
||||
"katara_healing_overlay",
|
||||
VertexFormats.POSITION_TEXTURE,
|
||||
VertexFormat.DrawMode.QUADS,
|
||||
1536,
|
||||
RenderLayer.MultiPhaseParameters.builder()
|
||||
.program(RenderPhase.ENTITY_GLINT_PROGRAM)
|
||||
.texture(
|
||||
RenderPhase.Texture(
|
||||
Identifier.ofVanilla("textures/entity/creeper/creeper_armor.png"),
|
||||
true,
|
||||
false
|
||||
)
|
||||
)
|
||||
.writeMaskState(RenderPhase.COLOR_MASK)
|
||||
.cull(RenderPhase.DISABLE_CULLING)
|
||||
.depthTest(RenderPhase.EQUAL_DEPTH_TEST)
|
||||
.transparency(RenderPhase.GLINT_TRANSPARENCY)
|
||||
.target(RenderPhase.ITEM_ENTITY_TARGET)
|
||||
.texturing(RenderPhase.ENTITY_GLINT_TEXTURING)
|
||||
.build(false)
|
||||
)
|
||||
|
||||
private fun setupOverlayTexture() {
|
||||
val l = (Util.getMeasuringTimeMs()
|
||||
.toDouble() * MinecraftClient.getInstance().options.glintSpeed.value * 8.0).toLong()
|
||||
val f = (l % 110000L).toFloat() / 110000.0f
|
||||
val g = (l % 30000L).toFloat() / 30000.0f
|
||||
RenderSystem.setTextureMatrix(Matrix4f().translation(f, g, 0.0f))
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(
|
||||
matrixStack: MatrixStack,
|
||||
vertexConsumerProvider: VertexConsumerProvider,
|
||||
i: Int,
|
||||
entity: T,
|
||||
f: Float,
|
||||
g: Float,
|
||||
h: Float,
|
||||
j: Float,
|
||||
k: Float,
|
||||
l: Float
|
||||
) {
|
||||
val livingEntity = entity as? LivingEntity? ?: return
|
||||
if (livingEntity.isReceivingWaterHealing) {
|
||||
val m = (entity as Entity).age.toFloat() + h
|
||||
val entityModel: EntityModel<T> = this.contextModel
|
||||
entityModel.animateModel(entity, f, g, h)
|
||||
this.contextModel.copyStateTo(entityModel)
|
||||
val vertexConsumer = vertexConsumerProvider.getBuffer(LAYER)
|
||||
entityModel.setAngles(entity, f, g, j, k, l)
|
||||
entityModel.render(matrixStack, vertexConsumer, i, OverlayTexture.DEFAULT_UV)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package gg.norisk.heroes.katara.client.render
|
||||
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.render.VertexConsumer
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.fluid.FluidState
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.world.BlockRenderView
|
||||
import java.awt.Color
|
||||
|
||||
interface IFluidRendererExt {
|
||||
fun katara_renderFluid(
|
||||
matrixStack: MatrixStack,
|
||||
blockRenderView: BlockRenderView,
|
||||
post: Vec3d,
|
||||
vertexConsumer: VertexConsumer,
|
||||
blockState: BlockState,
|
||||
fluidState: FluidState,
|
||||
waterColor: Color?
|
||||
)
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package gg.norisk.heroes.katara.client.render
|
||||
|
||||
import gg.norisk.heroes.katara.KataraManager.toId
|
||||
import gg.norisk.heroes.katara.entity.IceShardEntity
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.minecraft.client.render.entity.EntityRendererFactory
|
||||
import net.minecraft.client.render.entity.ProjectileEntityRenderer
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
class IceShardEntityRenderer(context: EntityRendererFactory.Context) :
|
||||
ProjectileEntityRenderer<IceShardEntity>(context) {
|
||||
override fun getTexture(entity: IceShardEntity): Identifier {
|
||||
return TEXTURE
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TEXTURE: Identifier = "textures/entity/projectiles/ice_shard.png".toId()
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package gg.norisk.heroes.katara.client.render
|
||||
|
||||
import gg.norisk.heroes.katara.entity.WaterBendingEntity
|
||||
import net.minecraft.client.model.ModelData
|
||||
import net.minecraft.client.model.ModelPart
|
||||
import net.minecraft.client.model.TexturedModelData
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.entity.model.SinglePartEntityModel
|
||||
|
||||
class WaterBendingEntityModel(val modelPart: ModelPart) :
|
||||
SinglePartEntityModel<WaterBendingEntity>(RenderLayer::getEntityTranslucent) {
|
||||
override fun setAngles(entity: WaterBendingEntity?, f: Float, g: Float, h: Float, i: Float, j: Float) {
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getTexturedModelData(): TexturedModelData {
|
||||
val modelData = ModelData()
|
||||
return TexturedModelData.of(modelData, 64, 32)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPart(): ModelPart {
|
||||
return modelPart
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
package gg.norisk.heroes.katara.client.render
|
||||
|
||||
import gg.norisk.heroes.katara.ability.WaterPillarAbility
|
||||
import gg.norisk.heroes.katara.entity.WaterBendingEntity
|
||||
import gg.norisk.utils.OldAnimation
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.RenderLayers
|
||||
import net.minecraft.client.render.VertexConsumerProvider
|
||||
import net.minecraft.client.render.entity.EntityRendererFactory
|
||||
import net.minecraft.client.render.entity.LivingEntityRenderer
|
||||
import net.minecraft.client.render.entity.model.EntityModelLayers
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.Fluids
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import org.joml.Quaternionf
|
||||
import java.awt.Color
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.toJavaDuration
|
||||
|
||||
class WaterBendingEntityRenderer(context: EntityRendererFactory.Context) :
|
||||
LivingEntityRenderer<WaterBendingEntity, WaterBendingEntityModel>(
|
||||
context,
|
||||
//doesnt matter
|
||||
WaterBendingEntityModel(context.getPart(EntityModelLayers.PIG)),
|
||||
0f
|
||||
) {
|
||||
|
||||
override fun render(
|
||||
livingEntity: WaterBendingEntity,
|
||||
f: Float,
|
||||
g: Float,
|
||||
matrixStack: MatrixStack,
|
||||
vertexConsumerProvider: VertexConsumerProvider,
|
||||
i: Int
|
||||
) {
|
||||
val owner = livingEntity.getOwner()
|
||||
renderWater(
|
||||
matrixStack,
|
||||
livingEntity.pos,
|
||||
Fluids.FLOWING_WATER.defaultState.blockState,
|
||||
OldAnimation(0.5f, 1f, 1.seconds.toJavaDuration()),
|
||||
0,
|
||||
owner,
|
||||
f,
|
||||
livingEntity
|
||||
)
|
||||
for (position in livingEntity.positions) {
|
||||
matrixStack.push()
|
||||
val difference = position.pos.subtract(livingEntity.getLerpedPos(g))
|
||||
matrixStack.translate(difference.x, difference.y, difference.z)
|
||||
renderWater(
|
||||
matrixStack,
|
||||
position.pos,
|
||||
Fluids.FLOWING_WATER.defaultState.blockState,
|
||||
position.animation,
|
||||
position.startTime,
|
||||
owner, f, livingEntity
|
||||
)
|
||||
matrixStack.pop()
|
||||
}
|
||||
// super.render(livingEntity, f, g, matrixStack, vertexConsumerProvider, i)
|
||||
}
|
||||
|
||||
fun renderWater(
|
||||
matrixStack: MatrixStack,
|
||||
pos: Vec3d,
|
||||
state: BlockState,
|
||||
animation: OldAnimation,
|
||||
index: Int,
|
||||
owner: PlayerEntity?,
|
||||
tickDelta: Float,
|
||||
waterBendingEntity: WaterBendingEntity,
|
||||
) {
|
||||
val renderer = MinecraftClient.getInstance().blockRenderManager
|
||||
val world = MinecraftClient.getInstance().world ?: return
|
||||
val vertexConsumer = MinecraftClient.getInstance().bufferBuilders.entityVertexConsumers.getBuffer(
|
||||
RenderLayers.getFluidLayer(state.fluidState)
|
||||
)
|
||||
|
||||
matrixStack.push()
|
||||
// Berechne den animierten Sinus-Offset basierend auf tickDelta und index
|
||||
// Berechne den animierten Sinus-Offset basierend auf tickDelta und index
|
||||
val timeFactor = (System.currentTimeMillis() % 10000L) / 1000.0 // Zeit in Sekunden (loop alle 10 Sekunden)
|
||||
val sineOffset = Math.sin((timeFactor + index * 0.5) * Math.PI) * 0.05 // Wellenbewegung mit tickDelta animiert
|
||||
|
||||
var scale = 0.5f
|
||||
matrixStack.scale(scale, scale, scale)
|
||||
matrixStack.scale(animation.get(), animation.get(), animation.get())
|
||||
owner?.apply {
|
||||
matrixStack.multiply(
|
||||
WaterPillarAbility.rotateTowards(
|
||||
pos, this.getLerpedPos(tickDelta),
|
||||
Quaternionf()
|
||||
)
|
||||
)
|
||||
}
|
||||
//matrixStack.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90f))
|
||||
matrixStack.translate(-0.5, -0.5 + sineOffset, -0.5)
|
||||
|
||||
(renderer.fluidRenderer as IFluidRendererExt).katara_renderFluid(
|
||||
matrixStack,
|
||||
world,
|
||||
pos,
|
||||
vertexConsumer,
|
||||
state,
|
||||
state.fluidState,
|
||||
if (waterBendingEntity.isHealing) {
|
||||
Color.decode("#8aefff")
|
||||
} else {
|
||||
null
|
||||
}
|
||||
)
|
||||
|
||||
matrixStack.pop()
|
||||
}
|
||||
|
||||
override fun getTexture(entity: WaterBendingEntity): Identifier {
|
||||
return MinecraftClient.getInstance().player!!.skinTextures.texture
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package gg.norisk.heroes.katara.client.sound
|
||||
|
||||
import gg.norisk.heroes.common.registry.SoundRegistry
|
||||
import net.minecraft.client.sound.MovingSoundInstance
|
||||
import net.minecraft.client.sound.SoundInstance
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.sound.SoundCategory
|
||||
|
||||
class VelocityBasedFlyingSoundInstance(private val entity: Entity, val condition: (Entity) -> Boolean) :
|
||||
MovingSoundInstance(SoundRegistry.FLYING, SoundCategory.NEUTRAL, SoundInstance.createRandom()) {
|
||||
|
||||
init {
|
||||
this.repeat = true
|
||||
this.repeatDelay = 0
|
||||
this.volume = 0.3f
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
if (!entity.isRemoved && condition.invoke(entity)) {
|
||||
this.x = entity.x.toFloat().toDouble()
|
||||
this.y = entity.y.toFloat().toDouble()
|
||||
this.z = entity.z.toFloat().toDouble()
|
||||
val f: Float = Math.min(0.3f, Math.max(0.01f, this.entity.velocity.lengthSquared().toFloat()))
|
||||
this.volume = f
|
||||
this.pitch = 1f + this.volume
|
||||
} else {
|
||||
this.setDone()
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package gg.norisk.heroes.katara.client.sound
|
||||
|
||||
import net.minecraft.client.sound.MovingSoundInstance
|
||||
import net.minecraft.client.sound.SoundInstance
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvents
|
||||
|
||||
class WaterCircleSoundInstance(private val entity: Entity, val condition: (Entity) -> Boolean) :
|
||||
MovingSoundInstance(SoundEvents.BLOCK_WATER_AMBIENT, SoundCategory.NEUTRAL, SoundInstance.createRandom()) {
|
||||
var fadeTime = 20
|
||||
var isFading = false
|
||||
|
||||
init {
|
||||
this.repeat = true
|
||||
this.repeatDelay = 0
|
||||
this.volume = 0.01f
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
if (isFading) {
|
||||
--fadeTime
|
||||
this.volume *= 0.9f
|
||||
if (fadeTime < 0) {
|
||||
this.setDone()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.x = entity.x.toFloat().toDouble()
|
||||
this.y = entity.y.toFloat().toDouble()
|
||||
this.z = entity.z.toFloat().toDouble()
|
||||
|
||||
if (!entity.isRemoved && condition.invoke(entity)) {
|
||||
this.volume = 0.7f
|
||||
this.pitch = 0.5f
|
||||
} else {
|
||||
isFading = true
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package gg.norisk.heroes.katara.client.sound
|
||||
|
||||
import net.minecraft.client.sound.MovingSoundInstance
|
||||
import net.minecraft.client.sound.SoundInstance
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvents
|
||||
|
||||
class WaterHealingSoundInstance(private val entity: Entity, val condition: (Entity) -> Boolean) :
|
||||
MovingSoundInstance(SoundEvents.BLOCK_BEACON_ACTIVATE, SoundCategory.NEUTRAL, SoundInstance.createRandom()) {
|
||||
var fadeTime = 20
|
||||
var isFading = false
|
||||
|
||||
init {
|
||||
this.repeat = true
|
||||
this.repeatDelay = 0
|
||||
this.volume = 0.01f
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
if (isFading) {
|
||||
--fadeTime
|
||||
this.volume *= 0.9f
|
||||
if (fadeTime < 0) {
|
||||
this.setDone()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.x = entity.x.toFloat().toDouble()
|
||||
this.y = entity.y.toFloat().toDouble()
|
||||
this.z = entity.z.toFloat().toDouble()
|
||||
|
||||
if (!entity.isRemoved && condition.invoke(entity)) {
|
||||
this.volume = 0.4f
|
||||
this.pitch = 0.5f
|
||||
} else {
|
||||
isFading = true
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
package gg.norisk.heroes.katara.client.sound
|
||||
|
||||
import net.minecraft.client.sound.MovingSoundInstance
|
||||
import net.minecraft.client.sound.SoundInstance
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvents
|
||||
|
||||
class WaterSelectingSoundInstance(private val entity: Entity, val condition: (Entity) -> Boolean) :
|
||||
MovingSoundInstance(SoundEvents.BLOCK_WATER_AMBIENT, SoundCategory.NEUTRAL, SoundInstance.createRandom()) {
|
||||
var fadeTime = 20
|
||||
var isFading = false
|
||||
|
||||
init {
|
||||
this.repeat = true
|
||||
this.repeatDelay = 0
|
||||
this.volume = 0.01f
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
if (isFading) {
|
||||
--fadeTime
|
||||
this.volume *= 0.9f
|
||||
if (fadeTime < 0) {
|
||||
this.setDone()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.x = entity.x.toFloat().toDouble()
|
||||
this.y = entity.y.toFloat().toDouble()
|
||||
this.z = entity.z.toFloat().toDouble()
|
||||
|
||||
if (!entity.isRemoved && condition.invoke(entity)) {
|
||||
this.volume = 0.7f
|
||||
this.pitch = 0.5f
|
||||
} else {
|
||||
isFading = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package gg.norisk.heroes.katara.entity
|
||||
|
||||
import gg.norisk.heroes.katara.utils.EntityCircleTracker
|
||||
import gg.norisk.heroes.katara.utils.EntitySpinTracker
|
||||
import kotlinx.coroutines.Job
|
||||
|
||||
interface IKataraEntity {
|
||||
var katara_waterHealingJob: Job?
|
||||
val katara_entitySpinTracker: EntitySpinTracker
|
||||
val katara_entityCircleTracker: EntityCircleTracker
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package gg.norisk.heroes.katara.entity
|
||||
|
||||
import net.minecraft.util.math.BlockPos
|
||||
|
||||
interface IWaterBendingPlayer {
|
||||
val katara_waterPillarBlocks: MutableSet<BlockPos>
|
||||
var katara_waterPillarOrigin: BlockPos?
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package gg.norisk.heroes.katara.entity
|
||||
|
||||
import gg.norisk.heroes.katara.registry.SoundRegistry
|
||||
import net.minecraft.entity.EntityType
|
||||
import net.minecraft.entity.projectile.PersistentProjectileEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.sound.SoundEvent
|
||||
import net.minecraft.world.World
|
||||
import kotlin.random.Random
|
||||
|
||||
class IceShardEntity(entityType: EntityType<out PersistentProjectileEntity>, world: World) :
|
||||
PersistentProjectileEntity(entityType, world) {
|
||||
|
||||
override fun getDefaultItemStack(): ItemStack {
|
||||
return ItemStack(Items.ICE);
|
||||
}
|
||||
|
||||
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
if (!inGround) {
|
||||
if (world.isClient) {
|
||||
if (Random.nextBoolean() && Random.nextBoolean()) {
|
||||
world.addParticle(ParticleTypes.SNOWFLAKE, this.x, this.y, this.z, 0.0, 0.0, 0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getHitSound(): SoundEvent {
|
||||
return SoundRegistry.ICE_PLACE
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
package gg.norisk.heroes.katara.entity
|
||||
|
||||
import gg.norisk.datatracker.entity.getSyncedData
|
||||
import gg.norisk.datatracker.entity.setSyncedData
|
||||
import gg.norisk.heroes.common.utils.sound
|
||||
import gg.norisk.heroes.common.utils.toBlockPos
|
||||
import gg.norisk.heroes.common.utils.toVec
|
||||
import gg.norisk.heroes.katara.ability.HealingAbility.WaterRender
|
||||
import gg.norisk.heroes.katara.ability.HealingAbility.counter
|
||||
import gg.norisk.heroes.katara.ability.HealingAbility.getWaterBendingPos
|
||||
import gg.norisk.heroes.katara.ability.HealingAbility.handleWaterHealing
|
||||
import gg.norisk.heroes.katara.ability.WaterBendingAbility.waterBendingDistance
|
||||
import gg.norisk.heroes.katara.ability.WaterFormingAbility.firstWaterFormingPos
|
||||
import gg.norisk.heroes.katara.ability.WaterFormingAbility.secondWaterFormingPos
|
||||
import gg.norisk.utils.Easing
|
||||
import gg.norisk.utils.OldAnimation
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.EntityType
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.damage.DamageSource
|
||||
import net.minecraft.entity.damage.DamageTypes
|
||||
import net.minecraft.entity.mob.PathAwareEntity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.Fluids
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvents
|
||||
import net.minecraft.util.math.MathHelper
|
||||
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.task.mcCoroutineTask
|
||||
import kotlin.math.sqrt
|
||||
import kotlin.random.Random
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.toJavaDuration
|
||||
|
||||
class WaterBendingEntity(entityType: EntityType<out PathAwareEntity>, world: World) :
|
||||
PathAwareEntity(entityType, world) {
|
||||
init {
|
||||
this.ignoreCameraFrustum = true
|
||||
//this.getAttributeInstance(EntityAttributes.GENERIC_GRAVITY)?.baseValue = 0.02
|
||||
}
|
||||
|
||||
var positions: MutableList<WaterRender> = mutableListOf<WaterRender>()
|
||||
var wasLaunched: Boolean
|
||||
get() = this.getSyncedData<Boolean>("WaterBendingEntityWasLaunched") ?: false
|
||||
set(value) {
|
||||
this.setSyncedData("WaterBendingEntityWasLaunched", value)
|
||||
}
|
||||
var isGettingRemoved = false
|
||||
var wasDropped: Boolean
|
||||
get() = this.getSyncedData<Boolean>("WaterBendingEntityWasDropped") ?: false
|
||||
set(value) {
|
||||
this.setSyncedData("WaterBendingEntityWasDropped", value)
|
||||
}
|
||||
|
||||
var isInitial: Boolean
|
||||
get() = this.getSyncedData<Boolean>("WaterBendingIsInitial") ?: false
|
||||
set(value) {
|
||||
this.setSyncedData("WaterBendingIsInitial", value)
|
||||
}
|
||||
var initialCounter = 0;
|
||||
|
||||
var isHealing: Boolean
|
||||
get() = this.getSyncedData<Boolean>("WaterBendingIsHealing") ?: false
|
||||
set(value) {
|
||||
this.setSyncedData("WaterBendingIsHealing", value)
|
||||
}
|
||||
|
||||
var ownerId: Int
|
||||
get() = this.getSyncedData<Int>("WaterBendingEntityOwnerId") ?: -1
|
||||
set(value) {
|
||||
this.setSyncedData("WaterBendingEntityOwnerId", value)
|
||||
}
|
||||
|
||||
fun getOwner(): PlayerEntity? {
|
||||
val id = if (ownerId != -1) ownerId else return null
|
||||
return world.getEntityById(id) as? PlayerEntity?
|
||||
}
|
||||
|
||||
override fun tick() {
|
||||
super.tick()
|
||||
val owner = getOwner()
|
||||
|
||||
|
||||
if (!world.isClient) {
|
||||
if ((wasLaunched || wasDropped) && (horizontalCollision || verticalCollision || isTouchingWater || world.getOtherEntities(
|
||||
this,
|
||||
this.boundingBox
|
||||
) { it.isAlive && !it.isSpectator }.isNotEmpty()) && !isGettingRemoved
|
||||
) {
|
||||
isGettingRemoved = true
|
||||
|
||||
if (isHealing) {
|
||||
for (otherEntity in world.getOtherEntities(
|
||||
this,
|
||||
boundingBox.expand(2.0)
|
||||
) { it.isAlive && !it.isSpectator }) {
|
||||
if (owner != null) {
|
||||
otherEntity.handleWaterHealing(owner)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (otherEntity in world.getOtherEntities(
|
||||
this,
|
||||
boundingBox.expand(2.89)
|
||||
) { it.isAlive && !it.isSpectator && it.canHit() && it != owner && it !is WaterBendingEntity }) {
|
||||
otherEntity.damage(this.damageSources.playerAttack(owner), 4f)
|
||||
(otherEntity as? LivingEntity?)?.takeKnockback(1.1, Random.nextDouble(), Random.nextDouble())
|
||||
}
|
||||
}
|
||||
|
||||
if (wasDropped) {
|
||||
val pos = blockPos
|
||||
world.setBlockState(pos, Fluids.WATER.getStill(false).blockState)
|
||||
mcCoroutineTask(sync = true, delay = 6.ticks) {
|
||||
if (world.getBlockState(pos).isOf(Blocks.WATER)) {
|
||||
world.setBlockState(pos, Blocks.AIR.defaultState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
repeat(20) {
|
||||
(world as ServerWorld).spawnParticles(
|
||||
ParticleTypes.SPLASH,
|
||||
getParticleX(0.5) + Random.nextDouble(-1.0, 1.0),
|
||||
randomBodyY + Random.nextDouble(-1.0, 1.0),
|
||||
getParticleZ(0.5) + Random.nextDouble(-1.0, 1.0),
|
||||
50,
|
||||
0.001,
|
||||
0.001,
|
||||
0.001,
|
||||
0.0
|
||||
)
|
||||
}
|
||||
world.playSound(
|
||||
null,
|
||||
pos.x,
|
||||
pos.y,
|
||||
pos.z,
|
||||
SoundEvents.ENTITY_GENERIC_SPLASH,
|
||||
SoundCategory.BLOCKS,
|
||||
1f, Random.nextDouble(1.5, 2.0).toFloat(),
|
||||
)
|
||||
mcCoroutineTask(delay = 2.ticks, sync = true) {
|
||||
discard()
|
||||
}
|
||||
}
|
||||
sound(
|
||||
SoundEvents.ENTITY_BOAT_PADDLE_WATER,
|
||||
0.1f,
|
||||
Random.nextDouble(1.9, 2.0).toFloat(),
|
||||
)
|
||||
}
|
||||
|
||||
if (isInitial && owner != null) {
|
||||
val distanceToPlayer = this.squaredDistanceTo(owner)
|
||||
val distance = owner.waterBendingDistance
|
||||
|
||||
if (distanceToPlayer >= (distance * distance) + sqrt(distance) * 2) {
|
||||
modifyVelocity(owner.getWaterBendingPos(distance).subtract(this.pos).normalize().multiply(1.0))
|
||||
} else {
|
||||
initialCounter++
|
||||
if (initialCounter >= 5) {
|
||||
isInitial = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(wasLaunched || wasDropped) && !isInitial) {
|
||||
owner?.apply {
|
||||
val waterFormingPos = owner.secondWaterFormingPos ?: owner.firstWaterFormingPos
|
||||
if (waterFormingPos != null) {
|
||||
this@WaterBendingEntity.modifyVelocity(
|
||||
waterFormingPos.toVec().subtract(this@WaterBendingEntity.pos).normalize().multiply(1.0)
|
||||
)
|
||||
} else {
|
||||
this@WaterBendingEntity.setPosition(this.getWaterBendingPos())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tickTrail(owner)
|
||||
}
|
||||
|
||||
fun freeze() {
|
||||
for (position in positions.takeLast(10)) {
|
||||
val pos = position.pos.toBlockPos()
|
||||
val currentState = world.getBlockState(pos)
|
||||
if (world.getBlockState(pos).isOf(Blocks.WATER) || world.getBlockState(pos).isAir) {
|
||||
world.setBlockState(position.pos.toBlockPos(), Blocks.ICE.defaultState)
|
||||
(world as? ServerWorld?)?.spawnParticles(
|
||||
ParticleTypes.CLOUD,
|
||||
pos.x + kotlin.random.Random.nextDouble(-1.0, 1.0),
|
||||
pos.y + kotlin.random.Random.nextDouble(-1.0, 1.0),
|
||||
pos.z + kotlin.random.Random.nextDouble(-1.0, 1.0),
|
||||
1,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
)
|
||||
}
|
||||
}
|
||||
discard()
|
||||
}
|
||||
|
||||
fun tickTrail(owner: PlayerEntity?, tickDelta: Float = 1f) {
|
||||
if (owner != null || wasLaunched || wasDropped) {
|
||||
//println("Was Launched $wasLaunched")
|
||||
if (positions.size > 30) {
|
||||
positions.removeFirstOrNull()
|
||||
/* val first = positions.getOrNull(0)
|
||||
if (first?.animation?.end != 0f) {
|
||||
first?.animation = OldAnimation(1f, 0.0f, 0.1.seconds.toJavaDuration())
|
||||
}
|
||||
if (first?.animation?.isDone == true) {
|
||||
} */
|
||||
}
|
||||
|
||||
val lastPos = positions.lastOrNull()
|
||||
|
||||
val pos = if (wasLaunched || wasDropped || isInitial || (owner?.secondWaterFormingPos
|
||||
?: owner?.firstWaterFormingPos) != null
|
||||
) getLerpedPos(
|
||||
tickDelta
|
||||
) else owner!!.getWaterBendingPos()
|
||||
|
||||
//TODO der check könnte für probleme sorgen
|
||||
for (otherEntity in world.getOtherEntities(owner, this.boundingBox.expand(1.5)) {
|
||||
it !is PlayerEntity && it !is WaterBendingEntity
|
||||
}) {
|
||||
val distance = otherEntity.distanceTo(this)
|
||||
val speed = if (distance <= 0.43) {
|
||||
0.05
|
||||
} else {
|
||||
0.2
|
||||
}
|
||||
otherEntity.modifyVelocity(this.pos.subtract(otherEntity.pos).normalize().multiply(speed))
|
||||
|
||||
/*otherEntity.teleport(
|
||||
otherEntity.world as ServerWorld,
|
||||
this.x,
|
||||
this.y,
|
||||
this.z,
|
||||
PositionFlag.VALUES,
|
||||
otherEntity.yaw,
|
||||
otherEntity.pitch
|
||||
)*/
|
||||
}
|
||||
|
||||
//println("${lastPos?.distanceTo(pos)}")
|
||||
if ((lastPos?.pos?.distanceTo(pos) ?: 10000.0) >= 0.2) {
|
||||
positions += WaterRender(
|
||||
pos,
|
||||
OldAnimation(0.7f, 1f, 0.1.seconds.toJavaDuration(), Easing.LINEAR),
|
||||
counter++
|
||||
)
|
||||
if (world.isClient) {
|
||||
world.addParticle(
|
||||
ParticleTypes.SPLASH,
|
||||
pos.x,
|
||||
pos.y,
|
||||
pos.z,
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
)
|
||||
} else {
|
||||
world.playSound(
|
||||
null,
|
||||
pos.x,
|
||||
pos.y,
|
||||
pos.z,
|
||||
SoundEvents.ENTITY_BOAT_PADDLE_WATER,
|
||||
SoundCategory.BLOCKS,
|
||||
0.3f, Random.nextDouble(1.5, 2.0).toFloat(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply player-controlled movement
|
||||
override fun travel(pos: Vec3d) {
|
||||
if (!wasDropped) {
|
||||
this.setNoDrag(true)
|
||||
this.setNoGravity(true)
|
||||
}
|
||||
super.travel(pos)
|
||||
}
|
||||
|
||||
fun launch(owner: LivingEntity) {
|
||||
if (!wasLaunched && !isInitial && !wasDropped) {
|
||||
wasLaunched = true
|
||||
owner.sound(SoundEvents.ENTITY_GENERIC_SPLASH, 0.6f, Random.nextDouble(1.5, 2.0))
|
||||
sound(SoundEvents.ENTITY_GENERIC_SPLASH, 1f, Random.nextDouble(1.5, 2.0))
|
||||
setVelocity(owner, owner.pitch, owner.yaw, 0.0f, 2f, 1.0f)
|
||||
}
|
||||
}
|
||||
|
||||
fun drop(owner: LivingEntity) {
|
||||
if (!wasDropped && !isInitial) {
|
||||
wasDropped = true
|
||||
owner.sound(SoundEvents.ENTITY_GENERIC_SPLASH, 0.6f, Random.nextDouble(1.5, 2.0))
|
||||
sound(SoundEvents.ENTITY_GENERIC_SPLASH, 1f, Random.nextDouble(1.5, 2.0))
|
||||
modifyVelocity(Vec3d(0.0, -0.6, 0.0))
|
||||
}
|
||||
}
|
||||
|
||||
override fun damage(damageSource: DamageSource, f: Float): Boolean {
|
||||
if (damageSource.isOf(DamageTypes.GENERIC_KILL)) {
|
||||
return super.damage(damageSource, f)
|
||||
}
|
||||
val attacker = damageSource.attacker as? LivingEntity ?: return false
|
||||
if (attacker.id == ownerId) {
|
||||
launch(attacker)
|
||||
return false
|
||||
} else {
|
||||
this.discard()
|
||||
//TODO
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun calculateVelocity(d: Double, e: Double, f: Double, g: Float, h: Float): Vec3d {
|
||||
return Vec3d(d, e, f)
|
||||
.normalize()
|
||||
.add(
|
||||
random.nextTriangular(0.0, 0.0172275 * h.toDouble()),
|
||||
random.nextTriangular(0.0, 0.0172275 * h.toDouble()),
|
||||
random.nextTriangular(0.0, 0.0172275 * h.toDouble())
|
||||
)
|
||||
.multiply(g.toDouble())
|
||||
}
|
||||
|
||||
fun setVelocity(entity: Entity, f: Float, g: Float, h: Float, i: Float, j: Float) {
|
||||
val k = -MathHelper.sin(g * (Math.PI / 180.0).toFloat()) * MathHelper.cos(f * (Math.PI / 180.0).toFloat())
|
||||
val l = -MathHelper.sin((f + h) * (Math.PI / 180.0).toFloat())
|
||||
val m = MathHelper.cos(g * (Math.PI / 180.0).toFloat()) * MathHelper.cos(f * (Math.PI / 180.0).toFloat())
|
||||
this.setVelocity(k.toDouble(), l.toDouble(), m.toDouble(), i, j)
|
||||
val vec3d = entity.movement
|
||||
this.velocity = velocity.add(vec3d.x, if (entity.isOnGround) 0.0 else vec3d.y, vec3d.z)
|
||||
}
|
||||
|
||||
fun setVelocity(d: Double, e: Double, f: Double, g: Float, h: Float) {
|
||||
val vec3d: Vec3d = this.calculateVelocity(d, e, f, g, h)
|
||||
this.velocity = vec3d
|
||||
this.velocityDirty = true
|
||||
val i = vec3d.horizontalLength()
|
||||
this.yaw = (MathHelper.atan2(vec3d.x, vec3d.z) * 180.0f / Math.PI.toFloat()).toFloat()
|
||||
this.pitch = (MathHelper.atan2(vec3d.y, i) * 180.0f / Math.PI.toFloat()).toFloat()
|
||||
this.prevYaw = this.yaw
|
||||
this.prevPitch = this.pitch
|
||||
}
|
||||
|
||||
override fun handleFallDamage(f: Float, g: Float, damageSource: DamageSource?): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package gg.norisk.heroes.katara.event
|
||||
|
||||
import net.minecraft.fluid.FluidState
|
||||
import net.minecraft.state.property.BooleanProperty
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.World
|
||||
import net.silkmc.silk.core.event.Cancellable
|
||||
import net.silkmc.silk.core.event.Event
|
||||
import net.silkmc.silk.core.event.EventScopeProperty
|
||||
|
||||
object FluidEvents {
|
||||
val static: BooleanProperty = BooleanProperty.of("static")
|
||||
|
||||
|
||||
class FluidEvent(val world: World, val blockPos: BlockPos, val fluidState: FluidState) : Cancellable {
|
||||
override val isCancelled = EventScopeProperty(false)
|
||||
}
|
||||
|
||||
val fluidTickEvent = Event.onlySync<FluidEvent>()
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package gg.norisk.heroes.katara.registry
|
||||
|
||||
import gg.norisk.heroes.common.HeroesManager
|
||||
import gg.norisk.heroes.katara.KataraManager.toId
|
||||
import gg.norisk.heroes.katara.entity.IceShardEntity
|
||||
import gg.norisk.heroes.katara.entity.WaterBendingEntity
|
||||
import net.fabricmc.fabric.api.`object`.builder.v1.entity.FabricDefaultAttributeRegistry
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.EntityDimensions
|
||||
import net.minecraft.entity.EntityType
|
||||
import net.minecraft.entity.SpawnGroup
|
||||
import net.minecraft.entity.attribute.DefaultAttributeContainer
|
||||
import net.minecraft.entity.attribute.EntityAttributes
|
||||
import net.minecraft.entity.mob.PathAwareEntity
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.registry.Registry
|
||||
|
||||
object EntityRegistry {
|
||||
val WATER_BENDING: EntityType<WaterBendingEntity> = register("water_bending", { entityType, world ->
|
||||
WaterBendingEntity(entityType, world)
|
||||
}, 0.3125f, 0.3125f)
|
||||
val ICE_SHARD: EntityType<IceShardEntity> = register("ice_shard", { entityType, world ->
|
||||
IceShardEntity(entityType, world)
|
||||
}, 0.3125f, 0.3125f)
|
||||
|
||||
fun init() {
|
||||
registerEntityAttributes()
|
||||
}
|
||||
|
||||
private fun registerEntityAttributes() {
|
||||
FabricDefaultAttributeRegistry.register(WATER_BENDING, createGenericEntityAttributes())
|
||||
}
|
||||
|
||||
fun createGenericEntityAttributes(): DefaultAttributeContainer.Builder {
|
||||
return PathAwareEntity.createLivingAttributes()
|
||||
.add(EntityAttributes.GENERIC_MOVEMENT_SPEED, 0.80000000298023224)
|
||||
.add(EntityAttributes.GENERIC_FOLLOW_RANGE, 16.0).add(EntityAttributes.GENERIC_MAX_HEALTH, 10.0)
|
||||
.add(EntityAttributes.GENERIC_ATTACK_DAMAGE, 5.0).add(EntityAttributes.GENERIC_ATTACK_KNOCKBACK, 0.1)
|
||||
}
|
||||
|
||||
private fun <T : Entity> register(
|
||||
name: String, entity: EntityType.EntityFactory<T>, width: Float, height: Float
|
||||
): EntityType<T> {
|
||||
val dimension = EntityDimensions.changing(width, height).withEyeHeight(0f)
|
||||
val builder = EntityType.Builder.create(entity, SpawnGroup.CREATURE)
|
||||
return Registry.register(
|
||||
Registries.ENTITY_TYPE,
|
||||
name.toId(),
|
||||
builder.eyeHeight(0f)
|
||||
.dimensions(dimension.width, dimension.height)
|
||||
.apply {
|
||||
requires(HeroesManager.heroesFlag)
|
||||
}
|
||||
.build(null)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package gg.norisk.heroes.katara.registry
|
||||
|
||||
import gg.norisk.heroes.katara.client.render.IceShardEntityRenderer
|
||||
import gg.norisk.heroes.katara.client.render.WaterBendingEntityRenderer
|
||||
import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry
|
||||
|
||||
|
||||
object EntityRendererRegistry {
|
||||
fun init() {
|
||||
EntityRendererRegistry.register(EntityRegistry.WATER_BENDING, ::WaterBendingEntityRenderer)
|
||||
EntityRendererRegistry.register(EntityRegistry.ICE_SHARD, ::IceShardEntityRenderer)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package gg.norisk.heroes.katara.registry
|
||||
|
||||
import gg.norisk.heroes.katara.KataraManager.toId
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.sound.SoundEvent
|
||||
|
||||
object SoundRegistry {
|
||||
var ICE_PLACE = "ice_place".register()
|
||||
var WATER_CIRCLE_ADD = "water_circle_add".register()
|
||||
|
||||
fun init() {
|
||||
}
|
||||
|
||||
private fun String.register() = Registry.register(Registries.SOUND_EVENT, this.toId(), SoundEvent.of(this.toId()))
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package gg.norisk.heroes.katara.utils
|
||||
|
||||
import net.minecraft.entity.Entity
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
|
||||
class EntityCircleTracker {
|
||||
private val rotationHistory: Deque<Vec2f> = ArrayDeque()
|
||||
private val maxHistorySize = 40 // Über 2 Sekunden (20 Ticks pro Sekunde)
|
||||
private val minCircleThreshold = 360.0f // Mindestens 360° Yaw+Pitch-Änderung
|
||||
|
||||
fun update(entity: Entity) {
|
||||
val yaw = normalizeAngle(entity.yaw)
|
||||
val pitch = normalizeAngle(entity.pitch)
|
||||
|
||||
// Aktuelle Werte speichern
|
||||
if (rotationHistory.size >= maxHistorySize) {
|
||||
rotationHistory.pollFirst()
|
||||
}
|
||||
rotationHistory.addLast(Vec2f(yaw, pitch))
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
rotationHistory.clear()
|
||||
}
|
||||
|
||||
val isDrawingCircle: Boolean
|
||||
get() {
|
||||
if (rotationHistory.size < 2) {
|
||||
return false // Nicht genug Daten
|
||||
}
|
||||
|
||||
var yawChange = 0.0f
|
||||
var pitchChange = 0.0f
|
||||
var previous: Vec2f? = null
|
||||
|
||||
for (current in rotationHistory) {
|
||||
if (previous != null) {
|
||||
yawChange += calculateAngleDifference(previous.x, current.x)
|
||||
pitchChange += calculateAngleDifference(previous.y, current.y)
|
||||
}
|
||||
previous = current
|
||||
}
|
||||
|
||||
// Prüfen, ob Yaw und Pitch zusammen mindestens 360°-Bewegung ergeben
|
||||
return (yawChange + pitchChange) >= minCircleThreshold
|
||||
}
|
||||
|
||||
private fun calculateAngleDifference(previous: Float, current: Float): Float {
|
||||
var diff = current - previous
|
||||
while (diff < -180.0f) {
|
||||
diff += 360.0f
|
||||
}
|
||||
while (diff > 180.0f) {
|
||||
diff -= 360.0f
|
||||
}
|
||||
return abs(diff.toDouble()).toFloat()
|
||||
}
|
||||
|
||||
private fun normalizeAngle(angle: Float): Float {
|
||||
return (angle % 360.0f + 360.0f) % 360.0f
|
||||
}
|
||||
|
||||
// Hilfsklasse für 2D-Werte
|
||||
private data class Vec2f(// Yaw
|
||||
val x: Float, // Pitch
|
||||
val y: Float
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package gg.norisk.heroes.katara.utils
|
||||
|
||||
import net.minecraft.entity.Entity
|
||||
import java.util.*
|
||||
import kotlin.math.abs
|
||||
|
||||
class EntitySpinTracker {
|
||||
private val yawHistory: Deque<Float> = ArrayDeque()
|
||||
private val maxHistorySize = 20 // Anzahl der Ticks, die wir überwachen (z. B. 1 Sekunde bei 20 Ticks pro Sekunde)
|
||||
private val spinThreshold = 720.0f // Mindestens 720° Änderung für einen "wilden Spin" (z. B. 2 volle Umdrehungen)
|
||||
|
||||
fun update(entity: Entity) {
|
||||
// Aktuelle Yaw-Rotation der Entity holen
|
||||
val currentYaw = normalizeYaw(entity.yaw)
|
||||
|
||||
// Letzten Wert speichern
|
||||
if (yawHistory.size >= maxHistorySize) {
|
||||
yawHistory.pollFirst()
|
||||
}
|
||||
yawHistory.addLast(currentYaw)
|
||||
|
||||
// Optional: Debug-Log für Rotation
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
yawHistory.clear()
|
||||
}
|
||||
|
||||
fun hasSpunWildly(): Boolean {
|
||||
if (yawHistory.size < 2) {
|
||||
return false // Nicht genug Daten
|
||||
}
|
||||
|
||||
var totalChange = 0.0f
|
||||
var previousYaw: Float? = null
|
||||
|
||||
for (yaw in yawHistory) {
|
||||
if (previousYaw != null) {
|
||||
val delta = calculateYawDifference(previousYaw, yaw)
|
||||
totalChange += delta
|
||||
}
|
||||
previousYaw = yaw
|
||||
}
|
||||
|
||||
// Wenn die gesamte Änderung den Schwellenwert überschreitet
|
||||
return totalChange >= spinThreshold
|
||||
}
|
||||
|
||||
private fun calculateYawDifference(previous: Float, current: Float): Float {
|
||||
var diff = current - previous
|
||||
while (diff < -180.0f) {
|
||||
diff += 360.0f
|
||||
}
|
||||
while (diff > 180.0f) {
|
||||
diff -= 360.0f
|
||||
}
|
||||
return abs(diff.toDouble()).toFloat()
|
||||
}
|
||||
|
||||
private fun normalizeYaw(yaw: Float): Float {
|
||||
// Yaw auf den Bereich [0, 360) normalisieren
|
||||
return (yaw % 360.0f + 360.0f) % 360.0f
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package gg.norisk.heroes.katara.utils
|
||||
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.RenderLayers
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import java.util.function.Consumer
|
||||
|
||||
object RenderUtils {
|
||||
fun renderBlock(
|
||||
matrixStack: MatrixStack,
|
||||
pos: Vec3d,
|
||||
state: BlockState,
|
||||
blockPos: BlockPos,
|
||||
consumer: Consumer<MatrixStack> = Consumer { }
|
||||
) {
|
||||
val camera = MinecraftClient.getInstance().gameRenderer.camera
|
||||
val renderer = MinecraftClient.getInstance().blockRenderManager
|
||||
val world = MinecraftClient.getInstance().world ?: return
|
||||
val vertexConsumer = MinecraftClient.getInstance().bufferBuilders.entityVertexConsumers.getBuffer(
|
||||
RenderLayers.getBlockLayer(state)
|
||||
)
|
||||
|
||||
matrixStack.push()
|
||||
matrixStack.translate(
|
||||
pos.x - camera.getPos().x + 0.5,
|
||||
pos.y - camera.getPos().y + 0.5,
|
||||
pos.z - camera.getPos().z + 0.5
|
||||
)
|
||||
consumer.accept(matrixStack)
|
||||
// Verschiebe den Block um 0.5 in alle Richtungen und führe dann die Skalierung aus
|
||||
// matrixStack.scale(0.5f, 0.5f, 0.5f)
|
||||
|
||||
// Rückverschiebung nach der Skalierung, um wieder zum tatsächlichen Mittelpunkt des Blocks zu gelangen
|
||||
matrixStack.translate(-0.5, -0.5, -0.5)
|
||||
renderer.renderBlock(
|
||||
state,
|
||||
blockPos,
|
||||
world,
|
||||
matrixStack,
|
||||
vertexConsumer,
|
||||
true,
|
||||
net.minecraft.util.math.random.Random.create()
|
||||
)
|
||||
matrixStack.pop()
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 100 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 98 KiB |
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"format_version": "1.8.0",
|
||||
"animations": {
|
||||
"tpose": {
|
||||
"loop": "hold_on_last_frame",
|
||||
"animation_length": 0.2917,
|
||||
"lockVanillaBones": {
|
||||
"bipedRig": false,
|
||||
"bipedHead": false,
|
||||
"bipedBody": false,
|
||||
"bipedLeftArm": true,
|
||||
"bipedRightArm": true,
|
||||
"bipedLeftLeg": true,
|
||||
"bipedRightLeg": true
|
||||
},
|
||||
"bones": {
|
||||
"bipedRightArm": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.2917": {
|
||||
"vector": [0, 0, "90 + Math.cos(query.anim_time * 100 +25) * 3"],
|
||||
"easing": "easeOutCubic"
|
||||
}
|
||||
},
|
||||
"position": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.2917": {
|
||||
"vector": [-1, -1, 0],
|
||||
"easing": "easeOutCubic"
|
||||
}
|
||||
}
|
||||
},
|
||||
"bipedLeftArm": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.2917": {
|
||||
"vector": [0, 0, "-90 - Math.cos(query.anim_time * 100 +25) * 3"],
|
||||
"easing": "easeOutCubic"
|
||||
}
|
||||
},
|
||||
"position": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.2917": {
|
||||
"vector": [1, -1, 0],
|
||||
"easing": "easeOutCubic"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"geckolib_format_version": 2
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"format_version": "1.8.0",
|
||||
"animations": {
|
||||
"water_pillar_start": {
|
||||
"animation_length": 0.52,
|
||||
"bones": {
|
||||
"bipedRig": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.48": {
|
||||
"vector": [0, 360, 0],
|
||||
"easing": "easeInExpo"
|
||||
},
|
||||
"0.52": {
|
||||
"vector": [0, 0, 0]
|
||||
}
|
||||
}
|
||||
},
|
||||
"bipedRightArm": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [-78.99034, 24.59477, 4.62934]
|
||||
},
|
||||
"0.12": {
|
||||
"vector": [73.84641, 51.3798, 167.24859]
|
||||
},
|
||||
"0.24": {
|
||||
"vector": [31.34641, 51.3798, 167.24859]
|
||||
},
|
||||
"0.48": {
|
||||
"vector": [-81.56011, -42.11662, 4.58533]
|
||||
}
|
||||
},
|
||||
"position": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.24": {
|
||||
"vector": [0, 3, 0]
|
||||
},
|
||||
"0.48": {
|
||||
"vector": [0, -1, -3]
|
||||
}
|
||||
}
|
||||
},
|
||||
"bipedLeftArm": {
|
||||
"rotation": {
|
||||
"0.0": {
|
||||
"vector": [86.44785, -33.16347, -168.10969]
|
||||
},
|
||||
"0.12": {
|
||||
"vector": [-82.49399, -66.60691, 3.05019]
|
||||
},
|
||||
"0.24": {
|
||||
"vector": [-42.49399, -66.60691, 3.05019]
|
||||
},
|
||||
"0.48": {
|
||||
"vector": [-72.50228, 13.17432, 52.2119]
|
||||
}
|
||||
},
|
||||
"position": {
|
||||
"0.0": {
|
||||
"vector": [0, 0, 0]
|
||||
},
|
||||
"0.24": {
|
||||
"vector": [0, 0, -2]
|
||||
},
|
||||
"0.48": {
|
||||
"vector": [-1, 0, -2]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"geckolib_format_version": 2
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ice_place": {
|
||||
"sounds": [
|
||||
"katara:ice_place"
|
||||
]
|
||||
},
|
||||
"water_circle_add": {
|
||||
"sounds": [
|
||||
"katara:water_circle_add"
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 508 B |
Binary file not shown.
|
After Width: | Height: | Size: 385 B |
Binary file not shown.
|
After Width: | Height: | Size: 897 B |
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"name": "Katara",
|
||||
"id": "katara",
|
||||
"version": "${version}",
|
||||
"description": "Katara",
|
||||
"authors": [
|
||||
"NoRiskk"
|
||||
],
|
||||
"icon": "assets/katara/icon.png",
|
||||
"license": "ARR",
|
||||
"environment": "*",
|
||||
"entrypoints": {
|
||||
"main": [
|
||||
{
|
||||
"adapter": "kotlin",
|
||||
"value": "gg.norisk.heroes.katara.KataraManager"
|
||||
}
|
||||
],
|
||||
"client": [
|
||||
{
|
||||
"adapter": "kotlin",
|
||||
"value": "gg.norisk.heroes.katara.KataraManager"
|
||||
}
|
||||
],
|
||||
"server": [
|
||||
{
|
||||
"adapter": "kotlin",
|
||||
"value": "gg.norisk.heroes.katara.KataraManager"
|
||||
}
|
||||
]
|
||||
},
|
||||
"mixins": [
|
||||
"katara.mixins.json"
|
||||
],
|
||||
"accessWidener": "katara.accesswidener",
|
||||
"depends": {
|
||||
},
|
||||
"custom": {
|
||||
"modmenu": {
|
||||
"badges": [
|
||||
"library"
|
||||
],
|
||||
"parent": {
|
||||
"id": "hero-api"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
accessWidener v2 named
|
||||
accessible field net/minecraft/entity/FallingBlockEntity block Lnet/minecraft/block/BlockState;
|
||||
accessible field net/minecraft/client/render/block/BlockRenderManager fluidRenderer Lnet/minecraft/client/render/block/FluidRenderer;
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"required": true,
|
||||
"minVersion": "0.8",
|
||||
"package": "gg.norisk.heroes.katara.mixin",
|
||||
"compatibilityLevel": "JAVA_21",
|
||||
"injectors": {
|
||||
"defaultRequire": 1
|
||||
},
|
||||
"mixins": [
|
||||
"EntityMixin",
|
||||
"FlowableFluidMixin",
|
||||
"PlayerEntityMixin"
|
||||
],
|
||||
"client": [
|
||||
"BufferBuilderStorageMixin",
|
||||
"FluidRendererMixin",
|
||||
"PlayerEntityRendererMixin"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user